aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/acpi/acpi_memhotplug.c4
-rw-r--r--drivers/acpi/acpica/Makefile2
-rw-r--r--drivers/acpi/acpica/acmacros.h3
-rw-r--r--drivers/acpi/acpica/acnamesp.h16
-rw-r--r--drivers/acpi/acpica/acutils.h7
-rw-r--r--drivers/acpi/acpica/dsinit.c2
-rw-r--r--drivers/acpi/acpica/dsmthdat.c4
-rw-r--r--drivers/acpi/acpica/dsobject.c8
-rw-r--r--drivers/acpi/acpica/dswload.c50
-rw-r--r--drivers/acpi/acpica/evgpeblk.c8
-rw-r--r--drivers/acpi/acpica/evregion.c81
-rw-r--r--drivers/acpi/acpica/exconfig.c4
-rw-r--r--drivers/acpi/acpica/exconvrt.c3
-rw-r--r--drivers/acpi/acpica/exfield.c3
-rw-r--r--drivers/acpi/acpica/exoparg1.c38
-rw-r--r--drivers/acpi/acpica/exoparg6.c7
-rw-r--r--drivers/acpi/acpica/nsdump.c4
-rw-r--r--drivers/acpi/acpica/nsdumpdv.c3
-rw-r--r--drivers/acpi/acpica/nseval.c50
-rw-r--r--drivers/acpi/acpica/nsinit.c8
-rw-r--r--drivers/acpi/acpica/nspredef.c49
-rw-r--r--drivers/acpi/acpica/nsrepair.c114
-rw-r--r--drivers/acpi/acpica/nsrepair2.c540
-rw-r--r--drivers/acpi/acpica/nswalk.c200
-rw-r--r--drivers/acpi/acpica/nsxfeval.c32
-rw-r--r--drivers/acpi/acpica/psloop.c32
-rw-r--r--drivers/acpi/acpica/psparse.c8
-rw-r--r--drivers/acpi/acpica/psxface.c4
-rw-r--r--drivers/acpi/acpica/utmisc.c42
-rw-r--r--drivers/acpi/acpica/utobject.c29
-rw-r--r--drivers/acpi/container.c4
-rw-r--r--drivers/acpi/dock.c8
-rw-r--r--drivers/acpi/ec.c2
-rw-r--r--drivers/acpi/glue.c2
-rw-r--r--drivers/acpi/numa.c10
-rw-r--r--drivers/acpi/pci_slot.c8
-rw-r--r--drivers/acpi/processor_core.c4
-rw-r--r--drivers/acpi/scan.c2
-rw-r--r--drivers/acpi/video_detect.c16
-rw-r--r--drivers/ata/Kconfig41
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/ahci.c28
-rw-r--r--drivers/ata/ata_generic.c5
-rw-r--r--drivers/ata/ata_piix.c8
-rw-r--r--drivers/ata/libata-acpi.c15
-rw-r--r--drivers/ata/libata-core.c7
-rw-r--r--drivers/ata/libata-eh.c94
-rw-r--r--drivers/ata/libata-scsi.c114
-rw-r--r--drivers/ata/libata-sff.c8
-rw-r--r--drivers/ata/pata_ali.c4
-rw-r--r--drivers/ata/pata_cmd64x.c125
-rw-r--r--drivers/ata/pata_cs5520.c39
-rw-r--r--drivers/ata/pata_cs5535.c2
-rw-r--r--drivers/ata/pata_cs5536.c2
-rw-r--r--drivers/ata/pata_efar.c9
-rw-r--r--drivers/ata/pata_hpt366.c38
-rw-r--r--drivers/ata/pata_hpt37x.c137
-rw-r--r--drivers/ata/pata_hpt3x2n.c40
-rw-r--r--drivers/ata/pata_hpt3x3.c11
-rw-r--r--drivers/ata/pata_it8213.c27
-rw-r--r--drivers/ata/pata_it821x.c2
-rw-r--r--drivers/ata/pata_legacy.c14
-rw-r--r--drivers/ata/pata_marvell.c2
-rw-r--r--drivers/ata/pata_ns87415.c32
-rw-r--r--drivers/ata/pata_oldpiix.c2
-rw-r--r--drivers/ata/pata_pcmcia.c17
-rw-r--r--drivers/ata/pata_piccolo.c140
-rw-r--r--drivers/ata/pata_radisys.c4
-rw-r--r--drivers/ata/pata_rdc.c2
-rw-r--r--drivers/ata/pata_rz1000.c11
-rw-r--r--drivers/ata/pata_sil680.c6
-rw-r--r--drivers/ata/pata_sis.c21
-rw-r--r--drivers/ata/pata_via.c56
-rw-r--r--drivers/ata/sata_fsl.c24
-rw-r--r--drivers/ata/sata_mv.c2
-rw-r--r--drivers/ata/sata_nv.c2
-rw-r--r--drivers/ata/sata_sil24.c9
-rw-r--r--drivers/atm/ambassador.c1
-rw-r--r--drivers/atm/fore200e.c4
-rw-r--r--drivers/atm/he.c2
-rw-r--r--drivers/atm/iphase.c2
-rw-r--r--drivers/atm/solos-pci.c32
-rw-r--r--drivers/base/dd.c2
-rw-r--r--drivers/base/power/runtime.c23
-rw-r--r--drivers/block/Kconfig2
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/ataflop.c3
-rw-r--r--drivers/block/cciss.c544
-rw-r--r--drivers/block/cciss.h18
-rw-r--r--drivers/block/cciss_cmd.h9
-rw-r--r--drivers/block/cciss_scsi.c4
-rw-r--r--drivers/block/drbd/Kconfig71
-rw-r--r--drivers/block/drbd/Makefile5
-rw-r--r--drivers/block/drbd/drbd_actlog.c1424
-rw-r--r--drivers/block/drbd/drbd_bitmap.c1327
-rw-r--r--drivers/block/drbd/drbd_int.h2252
-rw-r--r--drivers/block/drbd/drbd_main.c3699
-rw-r--r--drivers/block/drbd/drbd_nl.c2364
-rw-r--r--drivers/block/drbd/drbd_proc.c265
-rw-r--r--drivers/block/drbd/drbd_receiver.c4426
-rw-r--r--drivers/block/drbd/drbd_req.c1125
-rw-r--r--drivers/block/drbd/drbd_req.h326
-rw-r--r--drivers/block/drbd/drbd_strings.c113
-rw-r--r--drivers/block/drbd/drbd_vli.h351
-rw-r--r--drivers/block/drbd/drbd_worker.c1512
-rw-r--r--drivers/block/drbd/drbd_wrappers.h91
-rw-r--r--drivers/block/ps3vram.c10
-rw-r--r--drivers/bluetooth/bluecard_cs.c16
-rw-r--r--drivers/bluetooth/bt3c_cs.c13
-rw-r--r--drivers/bluetooth/btmrvl_debugfs.c1
-rw-r--r--drivers/bluetooth/btmrvl_drv.h1
-rw-r--r--drivers/bluetooth/btmrvl_main.c55
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c6
-rw-r--r--drivers/bluetooth/btuart_cs.c13
-rw-r--r--drivers/bluetooth/dtl1_cs.c12
-rw-r--r--drivers/bluetooth/hci_ldisc.c2
-rw-r--r--drivers/bluetooth/hci_vhci.c20
-rw-r--r--drivers/cdrom/cdrom.c20
-rw-r--r--drivers/cdrom/gdrom.c10
-rw-r--r--drivers/char/agp/Kconfig3
-rw-r--r--drivers/char/agp/frontend.c28
-rw-r--r--drivers/char/cs5535_gpio.c3
-rw-r--r--drivers/char/efirtc.c12
-rw-r--r--drivers/char/generic_nvram.c9
-rw-r--r--drivers/char/hpet.c11
-rw-r--r--drivers/char/hw_random/core.c109
-rw-r--r--drivers/char/hw_random/virtio-rng.c78
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c11
-rw-r--r--drivers/char/keyboard.c213
-rw-r--r--drivers/char/mem.c19
-rw-r--r--drivers/char/misc.c5
-rw-r--r--drivers/char/mspec.c2
-rw-r--r--drivers/char/n_r3964.c2
-rw-r--r--drivers/char/nvram.c5
-rw-r--r--drivers/char/pc8736x_gpio.c2
-rw-r--r--drivers/char/pcmcia/cm4000_cs.c73
-rw-r--r--drivers/char/pcmcia/cm4040_cs.c52
-rw-r--r--drivers/char/pcmcia/ipwireless/hardware.c8
-rw-r--r--drivers/char/pcmcia/ipwireless/main.c296
-rw-r--r--drivers/char/pcmcia/synclink_cs.c80
-rw-r--r--drivers/char/pty.c14
-rw-r--r--drivers/char/random.c54
-rw-r--r--drivers/char/rio/route.h2
-rw-r--r--drivers/char/rtc.c11
-rw-r--r--drivers/char/scx200_gpio.c2
-rw-r--r--drivers/char/tb0219.c6
-rw-r--r--drivers/char/tpm/tpm.c2
-rw-r--r--drivers/char/tpm/tpm_tis.c11
-rw-r--r--drivers/char/tty_ioctl.c2
-rw-r--r--drivers/char/vt.c11
-rw-r--r--drivers/cpuidle/governor.c4
-rw-r--r--drivers/crypto/hifn_795x.c2
-rw-r--r--drivers/dio/dio-driver.c1
-rw-r--r--drivers/dma/Kconfig2
-rw-r--r--drivers/dma/at_hdmac.c2
-rw-r--r--drivers/edac/amd64_edac.c1251
-rw-r--r--drivers/edac/amd64_edac.h62
-rw-r--r--drivers/edac/edac_core.h1
-rw-r--r--drivers/edac/edac_mc.c24
-rw-r--r--drivers/edac/edac_mce_amd.c23
-rw-r--r--drivers/firewire/core-card.c75
-rw-r--r--drivers/firewire/core-cdev.c113
-rw-r--r--drivers/firewire/core-topology.c19
-rw-r--r--drivers/firewire/core-transaction.c19
-rw-r--r--drivers/firewire/core.h9
-rw-r--r--drivers/firewire/ohci.c39
-rw-r--r--drivers/firewire/sbp2.c9
-rw-r--r--drivers/firmware/dmi_scan.c15
-rw-r--r--drivers/gpu/drm/drm_crtc.c4
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c2
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c2
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c2
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c2
-rw-r--r--drivers/gpu/drm/radeon/atombios.h16
-rw-r--r--drivers/gpu/drm/radeon/r600.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_state.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c2
-rw-r--r--drivers/gpu/drm/radeon/rv770.c4
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c2
-rw-r--r--drivers/hid/hid-core.c9
-rw-r--r--drivers/hid/hid-cypress.c2
-rw-r--r--drivers/hid/hid-ids.h7
-rw-r--r--drivers/hid/hid-lg.c33
-rw-r--r--drivers/hid/usbhid/hid-core.c12
-rw-r--r--drivers/hid/usbhid/hid-pidff.c5
-rw-r--r--drivers/hid/usbhid/hid-quirks.c4
-rw-r--r--drivers/hid/usbhid/hiddev.c5
-rw-r--r--drivers/hid/usbhid/usbkbd.c2
-rw-r--r--drivers/hwmon/Kconfig35
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/adm1021.c76
-rw-r--r--drivers/hwmon/adm1025.c73
-rw-r--r--drivers/hwmon/adm1026.c47
-rw-r--r--drivers/hwmon/adm1029.c57
-rw-r--r--drivers/hwmon/adm1031.c26
-rw-r--r--drivers/hwmon/adm9240.c61
-rw-r--r--drivers/hwmon/ads7828.c22
-rw-r--r--drivers/hwmon/adt7462.c25
-rw-r--r--drivers/hwmon/adt7470.c25
-rw-r--r--drivers/hwmon/adt7473.c28
-rw-r--r--drivers/hwmon/adt7475.c538
-rw-r--r--drivers/hwmon/asb100.c55
-rw-r--r--drivers/hwmon/dme1737.c32
-rw-r--r--drivers/hwmon/ds1621.c22
-rw-r--r--drivers/hwmon/f71882fg.c350
-rw-r--r--drivers/hwmon/f75375s.c32
-rw-r--r--drivers/hwmon/fschmd.c53
-rw-r--r--drivers/hwmon/gl518sm.c29
-rw-r--r--drivers/hwmon/gl520sm.c12
-rw-r--r--drivers/hwmon/it87.c47
-rw-r--r--drivers/hwmon/lm63.c54
-rw-r--r--drivers/hwmon/lm73.c205
-rw-r--r--drivers/hwmon/lm75.c56
-rw-r--r--drivers/hwmon/lm77.c80
-rw-r--r--drivers/hwmon/lm78.c58
-rw-r--r--drivers/hwmon/lm83.c81
-rw-r--r--drivers/hwmon/lm85.c157
-rw-r--r--drivers/hwmon/lm87.c41
-rw-r--r--drivers/hwmon/lm90.c228
-rw-r--r--drivers/hwmon/lm92.c31
-rw-r--r--drivers/hwmon/lm93.c34
-rw-r--r--drivers/hwmon/lm95241.c48
-rw-r--r--drivers/hwmon/max1619.c64
-rw-r--r--drivers/hwmon/max6650.c20
-rw-r--r--drivers/hwmon/mc13783-adc.c236
-rw-r--r--drivers/hwmon/s3c-hwmon.c4
-rw-r--r--drivers/hwmon/smsc47m192.c28
-rw-r--r--drivers/hwmon/thmc50.c37
-rw-r--r--drivers/hwmon/tmp401.c55
-rw-r--r--drivers/hwmon/tmp421.c45
-rw-r--r--drivers/hwmon/w83627hf.c96
-rw-r--r--drivers/hwmon/w83781d.c131
-rw-r--r--drivers/hwmon/w83791d.c60
-rw-r--r--drivers/hwmon/w83792d.c61
-rw-r--r--drivers/hwmon/w83793.c53
-rw-r--r--drivers/hwmon/w83l785ts.c70
-rw-r--r--drivers/hwmon/w83l786ng.c56
-rw-r--r--drivers/i2c/Kconfig1
-rw-r--r--drivers/i2c/busses/Kconfig16
-rw-r--r--drivers/i2c/busses/Makefile3
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c2
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c2
-rw-r--r--drivers/i2c/busses/i2c-designware.c488
-rw-r--r--drivers/i2c/busses/i2c-i801.c3
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c6
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c3
-rw-r--r--drivers/i2c/busses/i2c-omap.c64
-rw-r--r--drivers/i2c/busses/i2c-pnx.c3
-rw-r--r--drivers/i2c/busses/i2c-powermac.c139
-rw-r--r--drivers/i2c/busses/i2c-scmi.c2
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c2
-rw-r--r--drivers/i2c/busses/i2c-sis630.c2
-rw-r--r--drivers/i2c/busses/i2c-stub.c34
-rw-r--r--drivers/i2c/busses/i2c-voodoo3.c248
-rw-r--r--drivers/i2c/chips/Kconfig10
-rw-r--r--drivers/i2c/chips/Makefile1
-rw-r--r--drivers/i2c/i2c-core.c169
-rw-r--r--drivers/i2c/i2c-dev.c22
-rw-r--r--drivers/ide/Kconfig45
-rw-r--r--drivers/ide/alim15x3.c12
-rw-r--r--drivers/ide/au1xxx-ide.c7
-rw-r--r--drivers/ide/cmd64x.c17
-rw-r--r--drivers/ide/cs5535.c1
-rw-r--r--drivers/ide/cy82c693.c27
-rw-r--r--drivers/ide/hpt366.c65
-rw-r--r--drivers/ide/ide-cs.c33
-rw-r--r--drivers/ide/ide-pci-generic.c3
-rw-r--r--drivers/ide/ide-tape.c21
-rw-r--r--drivers/ide/ide_platform.c8
-rw-r--r--drivers/ide/pdc202xx_old.c15
-rw-r--r--drivers/ide/sis5513.c9
-rw-r--r--drivers/ide/sl82c105.c24
-rw-r--r--drivers/ide/slc90e66.c3
-rw-r--r--drivers/ide/tx4938ide.c2
-rw-r--r--drivers/ieee1394/dv1394.c2
-rw-r--r--drivers/ieee1394/ohci1394.c8
-rw-r--r--drivers/ieee802154/fakehard.c59
-rw-r--r--drivers/infiniband/hw/cxgb3/cxio_hal.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6110.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_sd7220.c4
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c2
-rw-r--r--drivers/infiniband/hw/nes/nes_nic.c12
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c1
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c2
-rw-r--r--drivers/input/Kconfig29
-rw-r--r--drivers/input/Makefile1
-rw-r--r--drivers/input/ff-core.c3
-rw-r--r--drivers/input/input-polldev.c137
-rw-r--r--drivers/input/input.c37
-rw-r--r--drivers/input/joystick/xpad.c3
-rw-r--r--drivers/input/keyboard/Kconfig10
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/atkbd.c26
-rw-r--r--drivers/input/keyboard/davinci_keyscan.c337
-rw-r--r--drivers/input/keyboard/gpio_keys.c134
-rw-r--r--drivers/input/keyboard/lkkbd.c496
-rw-r--r--drivers/input/keyboard/matrix_keypad.c17
-rw-r--r--drivers/input/keyboard/omap-keypad.c6
-rw-r--r--drivers/input/keyboard/sh_keysc.c2
-rw-r--r--drivers/input/misc/Kconfig2
-rw-r--r--drivers/input/misc/ati_remote.c2
-rw-r--r--drivers/input/misc/dm355evm_keys.c150
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c2
-rw-r--r--drivers/input/misc/powermate.c2
-rw-r--r--drivers/input/misc/wistron_btns.c256
-rw-r--r--drivers/input/mouse/alps.c100
-rw-r--r--drivers/input/mouse/elantech.c1
-rw-r--r--drivers/input/mouse/hgpk.c13
-rw-r--r--drivers/input/mouse/lifebook.c55
-rw-r--r--drivers/input/mouse/lifebook.h4
-rw-r--r--drivers/input/mouse/logips2pp.c4
-rw-r--r--drivers/input/mouse/psmouse-base.c41
-rw-r--r--drivers/input/mouse/sentelic.c1
-rw-r--r--drivers/input/mouse/synaptics.c31
-rw-r--r--drivers/input/mouse/synaptics.h1
-rw-r--r--drivers/input/mouse/synaptics_i2c.c6
-rw-r--r--drivers/input/mouse/touchkit_ps2.c3
-rw-r--r--drivers/input/mouse/trackpoint.c13
-rw-r--r--drivers/input/mouse/vsxxxaa.c374
-rw-r--r--drivers/input/serio/Kconfig8
-rw-r--r--drivers/input/serio/Makefile1
-rw-r--r--drivers/input/serio/altera_ps2.c200
-rw-r--r--drivers/input/serio/hp_sdc.c2
-rw-r--r--drivers/input/serio/hp_sdc_mlc.c2
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h143
-rw-r--r--drivers/input/sparse-keymap.c250
-rw-r--r--drivers/input/touchscreen/Kconfig37
-rw-r--r--drivers/input/touchscreen/Makefile4
-rw-r--r--drivers/input/touchscreen/ads7846.c25
-rw-r--r--drivers/input/touchscreen/atmel-wm97xx.c2
-rw-r--r--drivers/input/touchscreen/atmel_tsadcc.c47
-rw-r--r--drivers/input/touchscreen/dynapro.c206
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c4
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c457
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c11
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c104
-rw-r--r--drivers/input/touchscreen/zylonite-wm97xx.c2
-rw-r--r--drivers/isdn/act2000/Kconfig4
-rw-r--r--drivers/isdn/capi/Kconfig4
-rw-r--r--drivers/isdn/capi/capidrv.c2
-rw-r--r--drivers/isdn/gigaset/Kconfig25
-rw-r--r--drivers/isdn/gigaset/Makefile5
-rw-r--r--drivers/isdn/gigaset/asyncdata.c662
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c76
-rw-r--r--drivers/isdn/gigaset/capi.c2292
-rw-r--r--drivers/isdn/gigaset/common.c139
-rw-r--r--drivers/isdn/gigaset/dummyll.c68
-rw-r--r--drivers/isdn/gigaset/ev-layer.c578
-rw-r--r--drivers/isdn/gigaset/gigaset.h176
-rw-r--r--drivers/isdn/gigaset/i4l.c563
-rw-r--r--drivers/isdn/gigaset/interface.c41
-rw-r--r--drivers/isdn/gigaset/isocdata.c186
-rw-r--r--drivers/isdn/gigaset/proc.c2
-rw-r--r--drivers/isdn/gigaset/ser-gigaset.c56
-rw-r--r--drivers/isdn/gigaset/usb-gigaset.c71
-rw-r--r--drivers/isdn/hardware/avm/avm_cs.c4
-rw-r--r--drivers/isdn/hardware/eicon/di.c2
-rw-r--r--drivers/isdn/hardware/eicon/maintidi.c4
-rw-r--r--drivers/isdn/hardware/mISDN/hfcsusb.c2
-rw-r--r--drivers/isdn/hardware/mISDN/hfcsusb.h2
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNisar.c2
-rw-r--r--drivers/isdn/hardware/mISDN/speedfax.c1
-rw-r--r--drivers/isdn/hisax/avma1_cs.c28
-rw-r--r--drivers/isdn/hisax/elsa_cs.c46
-rw-r--r--drivers/isdn/hisax/hfc_usb.c2
-rw-r--r--drivers/isdn/hisax/sedlbauer_cs.c64
-rw-r--r--drivers/isdn/hisax/teles_cs.c38
-rw-r--r--drivers/isdn/hysdn/Kconfig4
-rw-r--r--drivers/isdn/i4l/isdn_net.c2
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c6
-rw-r--r--drivers/isdn/i4l/isdn_ttyfax.c2
-rw-r--r--drivers/isdn/icn/Kconfig4
-rw-r--r--drivers/isdn/mISDN/dsp_core.c2
-rw-r--r--drivers/isdn/mISDN/socket.c5
-rw-r--r--drivers/isdn/mISDN/tei.c2
-rw-r--r--drivers/isdn/pcbit/Kconfig4
-rw-r--r--drivers/isdn/sc/Kconfig4
-rw-r--r--drivers/leds/leds-ams-delta.c2
-rw-r--r--drivers/leds/leds-locomo.c2
-rw-r--r--drivers/macintosh/ans-lcd.c45
-rw-r--r--drivers/macintosh/mac_hid.c17
-rw-r--r--drivers/macintosh/therm_windtunnel.c2
-rw-r--r--drivers/md/dm-crypt.c2
-rw-r--r--drivers/md/dm-mpath.c8
-rw-r--r--drivers/md/md.c14
-rw-r--r--drivers/media/common/Makefile2
-rw-r--r--drivers/media/common/ir-functions.c71
-rw-r--r--drivers/media/common/ir-keymaps.c167
-rw-r--r--drivers/media/common/ir-keytable.c429
-rw-r--r--drivers/media/common/saa7146_i2c.c2
-rw-r--r--drivers/media/common/saa7146_video.c16
-rw-r--r--drivers/media/common/tuners/Kconfig7
-rw-r--r--drivers/media/common/tuners/Makefile1
-rw-r--r--drivers/media/common/tuners/max2165.c442
-rw-r--r--drivers/media/common/tuners/max2165.h48
-rw-r--r--drivers/media/common/tuners/max2165_priv.h60
-rw-r--r--drivers/media/common/tuners/mxl5005s.c5
-rw-r--r--drivers/media/common/tuners/mxl5005s.h4
-rw-r--r--drivers/media/common/tuners/mxl5007t.c2
-rw-r--r--drivers/media/common/tuners/tda18271-common.c16
-rw-r--r--drivers/media/common/tuners/tda18271-fe.c114
-rw-r--r--drivers/media/common/tuners/tda18271-maps.c1
-rw-r--r--drivers/media/common/tuners/tda18271-priv.h47
-rw-r--r--drivers/media/common/tuners/tda18271.h12
-rw-r--r--drivers/media/common/tuners/tda8290.c1
-rw-r--r--drivers/media/common/tuners/tda9887.c2
-rw-r--r--drivers/media/common/tuners/xc5000.c97
-rw-r--r--drivers/media/common/tuners/xc5000.h6
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.c2
-rw-r--r--drivers/media/dvb/dm1105/dm1105.c12
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.c39
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.h5
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c179
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h2
-rw-r--r--drivers/media/dvb/dvb-core/dvb_net.c2
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h28
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig8
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c33
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h215
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c2
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c117
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c182
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mb.c2
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-dvb.c1
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h11
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-remote.c2
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb.h3
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.c440
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.h73
-rw-r--r--drivers/media/dvb/dvb-usb/friio-fe.c59
-rw-r--r--drivers/media/dvb/dvb-usb/usb-urb.c4
-rw-r--r--drivers/media/dvb/firewire/Kconfig7
-rw-r--r--drivers/media/dvb/firewire/Makefile1
-rw-r--r--drivers/media/dvb/firewire/firedtv-1394.c41
-rw-r--r--drivers/media/dvb/firewire/firedtv-avc.c54
-rw-r--r--drivers/media/dvb/firewire/firedtv-dvb.c15
-rw-r--r--drivers/media/dvb/firewire/firedtv-fw.c376
-rw-r--r--drivers/media/dvb/firewire/firedtv-rc.c2
-rw-r--r--drivers/media/dvb/firewire/firedtv.h19
-rw-r--r--drivers/media/dvb/frontends/Kconfig21
-rw-r--r--drivers/media/dvb/frontends/Makefile3
-rw-r--r--drivers/media/dvb/frontends/atbm8830.c495
-rw-r--r--drivers/media/dvb/frontends/atbm8830.h76
-rw-r--r--drivers/media/dvb/frontends/atbm8830_priv.h75
-rw-r--r--drivers/media/dvb/frontends/au8522_decoder.c24
-rw-r--r--drivers/media/dvb/frontends/au8522_priv.h2
-rw-r--r--drivers/media/dvb/frontends/cx24110.c4
-rw-r--r--drivers/media/dvb/frontends/cx24113.c2
-rw-r--r--drivers/media/dvb/frontends/dib3000mb.c2
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c33
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h13
-rw-r--r--drivers/media/dvb/frontends/dib8000.c23
-rw-r--r--drivers/media/dvb/frontends/dib8000.h14
-rw-r--r--drivers/media/dvb/frontends/drx397xD.c2
-rw-r--r--drivers/media/dvb/frontends/ds3000.c1367
-rw-r--r--drivers/media/dvb/frontends/ds3000.h45
-rw-r--r--drivers/media/dvb/frontends/ec100.c335
-rw-r--r--drivers/media/dvb/frontends/ec100.h46
-rw-r--r--drivers/media/dvb/frontends/ec100_priv.h39
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.c4
-rw-r--r--drivers/media/dvb/frontends/s5h1409.c149
-rw-r--r--drivers/media/dvb/frontends/s5h1409.h7
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c2
-rw-r--r--drivers/media/dvb/frontends/stb6100_proc.h138
-rw-r--r--drivers/media/dvb/frontends/stv0900.h3
-rw-r--r--drivers/media/dvb/frontends/stv0900_core.c1560
-rw-r--r--drivers/media/dvb/frontends/stv0900_init.h257
-rw-r--r--drivers/media/dvb/frontends/stv0900_priv.h81
-rw-r--r--drivers/media/dvb/frontends/stv0900_reg.h5000
-rw-r--r--drivers/media/dvb/frontends/stv0900_sw.c3171
-rw-r--r--drivers/media/dvb/frontends/stv090x.c242
-rw-r--r--drivers/media/dvb/frontends/stv090x_priv.h3
-rw-r--r--drivers/media/dvb/frontends/stv090x_reg.h70
-rw-r--r--drivers/media/dvb/frontends/stv6110.c13
-rw-r--r--drivers/media/dvb/frontends/stv6110.h1
-rw-r--r--drivers/media/dvb/frontends/stv6110x.c6
-rw-r--r--drivers/media/dvb/pt1/pt1.c1
-rw-r--r--drivers/media/dvb/pt1/va1j5jf8007s.c57
-rw-r--r--drivers/media/dvb/pt1/va1j5jf8007t.c49
-rw-r--r--drivers/media/dvb/siano/sms-cards.c4
-rw-r--r--drivers/media/dvb/siano/sms-cards.h2
-rw-r--r--drivers/media/dvb/siano/smscoreapi.c2
-rw-r--r--drivers/media/dvb/siano/smssdio.c2
-rw-r--r--drivers/media/dvb/ttpci/av7110.c4
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c2
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c18
-rw-r--r--drivers/media/dvb/ttpci/budget-patch.c2
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c2
-rw-r--r--drivers/media/radio/Kconfig31
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/radio-miropcm20.c270
-rw-r--r--drivers/media/radio/radio-mr800.c360
-rw-r--r--drivers/media/radio/tef6862.c232
-rw-r--r--drivers/media/video/Kconfig33
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/adv7180.c323
-rw-r--r--drivers/media/video/au0828/au0828-video.c2
-rw-r--r--drivers/media/video/bt819.c2
-rw-r--r--drivers/media/video/bt8xx/bttv-input.c15
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c14
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h2
-rw-r--r--drivers/media/video/cx18/cx18-cards.h3
-rw-r--r--drivers/media/video/cx18/cx18-driver.c64
-rw-r--r--drivers/media/video/cx18/cx18-driver.h62
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c9
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c132
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c25
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c3
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c62
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h8
-rw-r--r--drivers/media/video/cx18/cx18-queue.c346
-rw-r--r--drivers/media/video/cx18/cx18-queue.h41
-rw-r--r--drivers/media/video/cx18/cx18-scb.h4
-rw-r--r--drivers/media/video/cx18/cx18-streams.c92
-rw-r--r--drivers/media/video/cx18/cx18-streams.h10
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c35
-rw-r--r--drivers/media/video/cx18/cx18-vbi.h2
-rw-r--r--drivers/media/video/cx18/cx18-version.h2
-rw-r--r--drivers/media/video/cx18/cx23418.h2
-rw-r--r--drivers/media/video/cx231xx/cx231xx-avcore.c8
-rw-r--r--drivers/media/video/cx231xx/cx231xx-input.c11
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c2
-rw-r--r--drivers/media/video/cx23885/Kconfig2
-rw-r--r--drivers/media/video/cx23885/Makefile3
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c10
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c155
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c119
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c160
-rw-r--r--drivers/media/video/cx23885/cx23885-f300.c177
-rw-r--r--drivers/media/video/cx23885/cx23885-f300.h2
-rw-r--r--drivers/media/video/cx23885/cx23885-input.c427
-rw-r--r--drivers/media/video/cx23885/cx23885-input.h30
-rw-r--r--drivers/media/video/cx23885/cx23885-ioctl.c208
-rw-r--r--drivers/media/video/cx23885/cx23885-ioctl.h39
-rw-r--r--drivers/media/video/cx23885/cx23885-ir.c101
-rw-r--r--drivers/media/video/cx23885/cx23885-ir.h31
-rw-r--r--drivers/media/video/cx23885/cx23885-reg.h5
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c44
-rw-r--r--drivers/media/video/cx23885/cx23885.h31
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.c1239
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.h28
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c463
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c324
-rw-r--r--drivers/media/video/cx25840/cx25840-core.h22
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c10
-rw-r--r--drivers/media/video/cx88/Kconfig2
-rw-r--r--drivers/media/video/cx88/cx88-cards.c19
-rw-r--r--drivers/media/video/cx88/cx88-core.c2
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c58
-rw-r--r--drivers/media/video/cx88/cx88-input.c47
-rw-r--r--drivers/media/video/cx88/cx88-video.c2
-rw-r--r--drivers/media/video/cx88/cx88.h1
-rw-r--r--drivers/media/video/davinci/dm355_ccdc.c6
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc.c4
-rw-r--r--drivers/media/video/davinci/vpfe_capture.c47
-rw-r--r--drivers/media/video/davinci/vpss.c2
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c4
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c50
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c22
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c24
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c27
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h1
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c8
-rw-r--r--drivers/media/video/em28xx/em28xx.h7
-rw-r--r--drivers/media/video/gspca/Kconfig23
-rw-r--r--drivers/media/video/gspca/Makefile4
-rw-r--r--drivers/media/video/gspca/conex.c12
-rw-r--r--drivers/media/video/gspca/etoms.c10
-rw-r--r--drivers/media/video/gspca/finepix.c23
-rw-r--r--drivers/media/video/gspca/gl860/gl860-mi1320.c55
-rw-r--r--drivers/media/video/gspca/gl860/gl860-mi2020.c69
-rw-r--r--drivers/media/video/gspca/gl860/gl860-ov2640.c140
-rw-r--r--drivers/media/video/gspca/gl860/gl860-ov9655.c43
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c61
-rw-r--r--drivers/media/video/gspca/gl860/gl860.h7
-rw-r--r--drivers/media/video/gspca/gspca.c137
-rw-r--r--drivers/media/video/gspca/gspca.h15
-rw-r--r--drivers/media/video/gspca/jeilinj.c34
-rw-r--r--drivers/media/video/gspca/m5602/m5602_core.c25
-rw-r--r--drivers/media/video/gspca/mars.c11
-rw-r--r--drivers/media/video/gspca/mr97310a.c602
-rw-r--r--drivers/media/video/gspca/ov519.c1495
-rw-r--r--drivers/media/video/gspca/ov534.c1537
-rw-r--r--drivers/media/video/gspca/pac207.c13
-rw-r--r--drivers/media/video/gspca/pac7302.c1272
-rw-r--r--drivers/media/video/gspca/pac7311.c716
-rw-r--r--drivers/media/video/gspca/pac_common.h91
-rw-r--r--drivers/media/video/gspca/sn9c20x.c11
-rw-r--r--drivers/media/video/gspca/sonixb.c23
-rw-r--r--drivers/media/video/gspca/sonixj.c660
-rw-r--r--drivers/media/video/gspca/spca500.c13
-rw-r--r--drivers/media/video/gspca/spca501.c20
-rw-r--r--drivers/media/video/gspca/spca505.c10
-rw-r--r--drivers/media/video/gspca/spca506.c12
-rw-r--r--drivers/media/video/gspca/spca508.c10
-rw-r--r--drivers/media/video/gspca/spca561.c14
-rw-r--r--drivers/media/video/gspca/sq905.c75
-rw-r--r--drivers/media/video/gspca/sq905c.c31
-rw-r--r--drivers/media/video/gspca/stk014.c11
-rw-r--r--drivers/media/video/gspca/stv0680.c364
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c11
-rw-r--r--drivers/media/video/gspca/sunplus.c13
-rw-r--r--drivers/media/video/gspca/t613.c7
-rw-r--r--drivers/media/video/gspca/tv8532.c7
-rw-r--r--drivers/media/video/gspca/vc032x.c23
-rw-r--r--drivers/media/video/gspca/w996Xcf.c609
-rw-r--r--drivers/media/video/gspca/zc3xx.c1472
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c8
-rw-r--r--drivers/media/video/hexium_gemini.c2
-rw-r--r--drivers/media/video/hexium_orion.c2
-rw-r--r--drivers/media/video/ir-kbd-i2c.c19
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c16
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h51
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c27
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h6
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c153
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.h1
-rw-r--r--drivers/media/video/mxb.c2
-rw-r--r--drivers/media/video/ov772x.c2
-rw-r--r--drivers/media/video/ov9640.c801
-rw-r--r--drivers/media/video/ov9640.h209
-rw-r--r--drivers/media/video/pms.c1425
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.c17
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c13
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c5
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h3
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c63
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c15
-rw-r--r--drivers/media/video/pwc/pwc-if.c23
-rw-r--r--drivers/media/video/rj54n1cb0c.c1219
-rw-r--r--drivers/media/video/s2255drv.c4
-rw-r--r--drivers/media/video/saa7110.c2
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c55
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c6
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c66
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c55
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2
-rw-r--r--drivers/media/video/saa7134/saa7134.h3
-rw-r--r--drivers/media/video/saa7164/saa7164-core.c4
-rw-r--r--drivers/media/video/saa7164/saa7164-dvb.c1
-rw-r--r--drivers/media/video/saa717x.c4
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c83
-rw-r--r--drivers/media/video/tuner-core.c20
-rw-r--r--drivers/media/video/tvaudio.c2
-rw-r--r--drivers/media/video/tvp514x.c2
-rw-r--r--drivers/media/video/usbvideo/konicawc.c2
-rw-r--r--drivers/media/video/usbvideo/quickcam_messenger.c2
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c2
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c36
-rw-r--r--drivers/media/video/uvc/uvc_driver.c428
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c60
-rw-r--r--drivers/media/video/uvc/uvc_video.c26
-rw-r--r--drivers/media/video/uvc/uvcvideo.h23
-rw-r--r--drivers/media/video/v4l2-common.c9
-rw-r--r--drivers/media/video/videobuf-core.c12
-rw-r--r--drivers/media/video/videobuf-dma-contig.c2
-rw-r--r--drivers/media/video/videobuf-dma-sg.c6
-rw-r--r--drivers/media/video/videobuf-dvb.c11
-rw-r--r--drivers/media/video/videobuf-vmalloc.c4
-rw-r--r--drivers/media/video/vpx3220.c2
-rw-r--r--drivers/media/video/zoran/zoran.h2
-rw-r--r--drivers/media/video/zoran/zoran_driver.c2
-rw-r--r--drivers/media/video/zr364xx.c1
-rw-r--r--drivers/message/fusion/mptbase.h4
-rw-r--r--drivers/message/fusion/mptctl.c5
-rw-r--r--drivers/message/fusion/mptscsih.c95
-rw-r--r--drivers/message/fusion/mptscsih.h3
-rw-r--r--drivers/message/i2o/i2o_block.c2
-rw-r--r--drivers/message/i2o/iop.c4
-rw-r--r--drivers/mfd/Kconfig14
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/mcp-core.c2
-rw-r--r--drivers/mfd/mcp-sa11x0.c3
-rw-r--r--drivers/mfd/mcp.h66
-rw-r--r--drivers/mfd/menelaus.c2
-rw-r--r--drivers/mfd/sh_mobile_sdhi.c156
-rw-r--r--drivers/mfd/twl4030-codec.c276
-rw-r--r--drivers/mfd/twl4030-core.c18
-rw-r--r--drivers/mfd/ucb1400_core.c7
-rw-r--r--drivers/mfd/ucb1x00-assabet.c2
-rw-r--r--drivers/mfd/ucb1x00-core.c89
-rw-r--r--drivers/mfd/ucb1x00-ts.c2
-rw-r--r--drivers/mfd/ucb1x00.h255
-rw-r--r--drivers/misc/Kconfig11
-rw-r--r--drivers/misc/Makefile2
-rw-r--r--drivers/misc/ds1682.c (renamed from drivers/i2c/chips/ds1682.c)0
-rw-r--r--drivers/misc/ics932s401.c37
-rw-r--r--drivers/misc/iwmc3200top/Kconfig20
-rw-r--r--drivers/misc/iwmc3200top/Makefile29
-rw-r--r--drivers/misc/iwmc3200top/debugfs.c133
-rw-r--r--drivers/misc/iwmc3200top/debugfs.h58
-rw-r--r--drivers/misc/iwmc3200top/fw-download.c355
-rw-r--r--drivers/misc/iwmc3200top/fw-msg.h113
-rw-r--r--drivers/misc/iwmc3200top/iwmc3200top.h209
-rw-r--r--drivers/misc/iwmc3200top/log.c347
-rw-r--r--drivers/misc/iwmc3200top/log.h158
-rw-r--r--drivers/misc/iwmc3200top/main.c678
-rw-r--r--drivers/misc/sgi-gru/grufile.c2
-rw-r--r--drivers/misc/sgi-xp/xpc_main.c14
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c5
-rw-r--r--drivers/mmc/card/sdio_uart.c40
-rw-r--r--drivers/mmc/host/Kconfig2
-rw-r--r--drivers/mmc/host/mmci.c2
-rw-r--r--drivers/mmc/host/omap.c10
-rw-r--r--drivers/mmc/host/omap_hsmmc.c8
-rw-r--r--drivers/mmc/host/pxamci.c10
-rw-r--r--drivers/mmc/host/s3cmci.c2
-rw-r--r--drivers/mtd/chips/Kconfig3
-rw-r--r--drivers/mtd/devices/Kconfig3
-rw-r--r--drivers/mtd/devices/slram.c2
-rw-r--r--drivers/mtd/lpddr/Kconfig3
-rw-r--r--drivers/mtd/maps/Kconfig8
-rw-r--r--drivers/mtd/maps/omap_nor.c2
-rw-r--r--drivers/mtd/maps/pcmciamtd.c197
-rw-r--r--drivers/mtd/mtd_blkdevs.c2
-rw-r--r--drivers/mtd/nand/Kconfig4
-rw-r--r--drivers/mtd/nand/ams-delta.c2
-rw-r--r--drivers/mtd/nand/diskonchip.c2
-rw-r--r--drivers/mtd/nand/nand_ecc.c2
-rw-r--r--drivers/mtd/nand/omap2.c6
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c82
-rw-r--r--drivers/mtd/nand/s3c2410.c2
-rw-r--r--drivers/mtd/onenand/Kconfig4
-rw-r--r--drivers/mtd/onenand/omap2.c8
-rw-r--r--drivers/mtd/ubi/Kconfig2
-rw-r--r--drivers/mtd/ubi/kapi.c40
-rw-r--r--drivers/mtd/ubi/upd.c20
-rw-r--r--drivers/net/3c501.c10
-rw-r--r--drivers/net/3c503.c15
-rw-r--r--drivers/net/3c505.c2
-rw-r--r--drivers/net/3c507.c6
-rw-r--r--drivers/net/3c509.c12
-rw-r--r--drivers/net/3c515.c15
-rw-r--r--drivers/net/3c523.c2
-rw-r--r--drivers/net/3c527.c6
-rw-r--r--drivers/net/3c59x.c7
-rw-r--r--drivers/net/8139cp.c12
-rw-r--r--drivers/net/8139too.c15
-rw-r--r--drivers/net/82596.c2
-rw-r--r--drivers/net/Kconfig4
-rw-r--r--drivers/net/amd8111e.c7
-rw-r--r--drivers/net/appletalk/cops.c4
-rw-r--r--drivers/net/appletalk/ipddp.c12
-rw-r--r--drivers/net/appletalk/ltpc.c2
-rw-r--r--drivers/net/arcnet/arc-rimi.c8
-rw-r--r--drivers/net/arcnet/arcnet.c14
-rw-r--r--drivers/net/arcnet/com20020.c2
-rw-r--r--drivers/net/arcnet/com90io.c2
-rw-r--r--drivers/net/arcnet/com90xx.c2
-rw-r--r--drivers/net/ariadne.h2
-rw-r--r--drivers/net/arm/ks8695net.c131
-rw-r--r--drivers/net/arm/w90p910_ether.c4
-rw-r--r--drivers/net/at1700.c18
-rw-r--r--drivers/net/atarilance.c4
-rw-r--r--drivers/net/atl1c/atl1c.h22
-rw-r--r--drivers/net/atl1c/atl1c_main.c90
-rw-r--r--drivers/net/atl1e/atl1e_ethtool.c17
-rw-r--r--drivers/net/atl1e/atl1e_main.c42
-rw-r--r--drivers/net/atlx/atl1.c21
-rw-r--r--drivers/net/atlx/atl2.c5
-rw-r--r--drivers/net/atp.c6
-rw-r--r--drivers/net/au1000_eth.c2
-rw-r--r--drivers/net/bcm63xx_enet.c5
-rw-r--r--drivers/net/benet/be.h24
-rw-r--r--drivers/net/benet/be_cmds.c496
-rw-r--r--drivers/net/benet/be_cmds.h154
-rw-r--r--drivers/net/benet/be_ethtool.c204
-rw-r--r--drivers/net/benet/be_hw.h9
-rw-r--r--drivers/net/benet/be_main.c259
-rw-r--r--drivers/net/bfin_mac.c12
-rw-r--r--drivers/net/bmac.c4
-rw-r--r--drivers/net/bnx2.c217
-rw-r--r--drivers/net/bnx2.h3
-rw-r--r--drivers/net/bnx2x.h95
-rw-r--r--drivers/net/bnx2x_hsi.h21
-rw-r--r--drivers/net/bnx2x_link.c321
-rw-r--r--drivers/net/bnx2x_link.h3
-rw-r--r--drivers/net/bnx2x_main.c1443
-rw-r--r--drivers/net/bnx2x_reg.h25
-rw-r--r--drivers/net/bonding/bond_3ad.c123
-rw-r--r--drivers/net/bonding/bond_alb.c5
-rw-r--r--drivers/net/bonding/bond_ipv6.c7
-rw-r--r--drivers/net/bonding/bond_main.c316
-rw-r--r--drivers/net/bonding/bond_sysfs.c92
-rw-r--r--drivers/net/bonding/bonding.h35
-rw-r--r--drivers/net/can/Kconfig15
-rw-r--r--drivers/net/can/Makefile3
-rw-r--r--drivers/net/can/at91_can.c34
-rw-r--r--drivers/net/can/dev.c76
-rw-r--r--drivers/net/can/mcp251x.c1166
-rw-r--r--drivers/net/can/mscan/Kconfig23
-rw-r--r--drivers/net/can/mscan/Makefile5
-rw-r--r--drivers/net/can/mscan/mpc5xxx_can.c259
-rw-r--r--drivers/net/can/mscan/mscan.c668
-rw-r--r--drivers/net/can/mscan/mscan.h296
-rw-r--r--drivers/net/can/sja1000/sja1000.c19
-rw-r--r--drivers/net/can/sja1000/sja1000.h2
-rw-r--r--drivers/net/can/ti_hecc.c993
-rw-r--r--drivers/net/can/usb/ems_usb.c28
-rw-r--r--drivers/net/cnic.c1875
-rw-r--r--drivers/net/cnic.h64
-rw-r--r--drivers/net/cnic_defs.h1917
-rw-r--r--drivers/net/cnic_if.h14
-rw-r--r--drivers/net/cpmac.c6
-rw-r--r--drivers/net/cs89x0.c2
-rw-r--r--drivers/net/cxgb3/adapter.h16
-rw-r--r--drivers/net/cxgb3/common.h8
-rw-r--r--drivers/net/cxgb3/cxgb3_main.c73
-rw-r--r--drivers/net/cxgb3/sge.c33
-rw-r--r--drivers/net/davinci_emac.c30
-rw-r--r--drivers/net/declance.c4
-rw-r--r--drivers/net/depca.c2
-rw-r--r--drivers/net/dl2k.c24
-rw-r--r--drivers/net/dm9000.c149
-rw-r--r--drivers/net/dm9000.h7
-rw-r--r--drivers/net/e100.c25
-rw-r--r--drivers/net/e1000/e1000.h2
-rw-r--r--drivers/net/e1000/e1000_ethtool.c39
-rw-r--r--drivers/net/e1000/e1000_main.c130
-rw-r--r--drivers/net/e1000e/82571.c323
-rw-r--r--drivers/net/e1000e/defines.h2
-rw-r--r--drivers/net/e1000e/e1000.h47
-rw-r--r--drivers/net/e1000e/es2lan.c213
-rw-r--r--drivers/net/e1000e/ethtool.c81
-rw-r--r--drivers/net/e1000e/hw.h52
-rw-r--r--drivers/net/e1000e/ich8lan.c530
-rw-r--r--drivers/net/e1000e/lib.c261
-rw-r--r--drivers/net/e1000e/netdev.c470
-rw-r--r--drivers/net/e1000e/param.c2
-rw-r--r--drivers/net/e1000e/phy.c516
-rw-r--r--drivers/net/e2100.c6
-rw-r--r--drivers/net/eepro.c2
-rw-r--r--drivers/net/eexpress.c2
-rw-r--r--drivers/net/ehea/ehea_ethtool.c4
-rw-r--r--drivers/net/ehea/ehea_main.c29
-rw-r--r--drivers/net/ehea/ehea_qmr.c4
-rw-r--r--drivers/net/enic/enic_main.c15
-rw-r--r--drivers/net/epic100.c10
-rw-r--r--drivers/net/ethoc.c6
-rw-r--r--drivers/net/fealnx.c14
-rw-r--r--drivers/net/fec_mpc52xx.c6
-rw-r--r--drivers/net/forcedeth.c6
-rw-r--r--drivers/net/fsl_pq_mdio.c67
-rw-r--r--drivers/net/fsl_pq_mdio.h11
-rw-r--r--drivers/net/gianfar.c1826
-rw-r--r--drivers/net/gianfar.h412
-rw-r--r--drivers/net/gianfar_ethtool.c376
-rw-r--r--drivers/net/gianfar_sysfs.c81
-rw-r--r--drivers/net/hamachi.c24
-rw-r--r--drivers/net/hamradio/6pack.c21
-rw-r--r--drivers/net/hamradio/baycom_epp.c6
-rw-r--r--drivers/net/hamradio/baycom_ser_fdx.c2
-rw-r--r--drivers/net/hamradio/bpqether.c9
-rw-r--r--drivers/net/hamradio/dmascc.c8
-rw-r--r--drivers/net/hamradio/mkiss.c21
-rw-r--r--drivers/net/hp-plus.c4
-rw-r--r--drivers/net/hp100.c4
-rw-r--r--drivers/net/ibm_newemac/core.c34
-rw-r--r--drivers/net/ibmveth.c2
-rw-r--r--drivers/net/ifb.c10
-rw-r--r--drivers/net/igb/e1000_82575.c572
-rw-r--r--drivers/net/igb/e1000_82575.h32
-rw-r--r--drivers/net/igb/e1000_defines.h50
-rw-r--r--drivers/net/igb/e1000_hw.h22
-rw-r--r--drivers/net/igb/e1000_mac.c100
-rw-r--r--drivers/net/igb/e1000_mbx.c82
-rw-r--r--drivers/net/igb/e1000_mbx.h10
-rw-r--r--drivers/net/igb/e1000_nvm.c36
-rw-r--r--drivers/net/igb/e1000_phy.c453
-rw-r--r--drivers/net/igb/e1000_phy.h37
-rw-r--r--drivers/net/igb/e1000_regs.h80
-rw-r--r--drivers/net/igb/igb.h149
-rw-r--r--drivers/net/igb/igb_ethtool.c747
-rw-r--r--drivers/net/igb/igb_main.c3450
-rw-r--r--drivers/net/igbvf/ethtool.c25
-rw-r--r--drivers/net/igbvf/igbvf.h1
-rw-r--r--drivers/net/igbvf/netdev.c83
-rw-r--r--drivers/net/ipg.c9
-rw-r--r--drivers/net/irda/au1k_ir.c4
-rw-r--r--drivers/net/irda/irda-usb.c10
-rw-r--r--drivers/net/irda/smsc-ircc2.c2
-rw-r--r--drivers/net/irda/stir4200.c12
-rw-r--r--drivers/net/irda/via-ircc.c16
-rw-r--r--drivers/net/irda/vlsi_ir.c16
-rw-r--r--drivers/net/isa-skeleton.c10
-rw-r--r--drivers/net/iseries_veth.c44
-rw-r--r--drivers/net/ixgb/ixgb.h2
-rw-r--r--drivers/net/ixgb/ixgb_ethtool.c69
-rw-r--r--drivers/net/ixgb/ixgb_main.c118
-rw-r--r--drivers/net/ixgbe/ixgbe.h33
-rw-r--r--drivers/net/ixgbe/ixgbe_82599.c178
-rw-r--r--drivers/net/ixgbe/ixgbe_common.c37
-rw-r--r--drivers/net/ixgbe/ixgbe_dcb_nl.c2
-rw-r--r--drivers/net/ixgbe/ixgbe_ethtool.c140
-rw-r--r--drivers/net/ixgbe/ixgbe_fcoe.c76
-rw-r--r--drivers/net/ixgbe/ixgbe_fcoe.h3
-rw-r--r--drivers/net/ixgbe/ixgbe_main.c251
-rw-r--r--drivers/net/ixgbe/ixgbe_type.h26
-rw-r--r--drivers/net/ixp2000/ixpdev.c3
-rw-r--r--drivers/net/jazzsonic.c6
-rw-r--r--drivers/net/jme.c22
-rw-r--r--drivers/net/korina.c13
-rw-r--r--drivers/net/ks8842.c5
-rw-r--r--drivers/net/ks8851.c4
-rw-r--r--drivers/net/lance.c14
-rw-r--r--drivers/net/lasi_82596.c2
-rw-r--r--drivers/net/lib82596.c15
-rw-r--r--drivers/net/lib8390.c12
-rw-r--r--drivers/net/ll_temac_main.c4
-rw-r--r--drivers/net/loopback.c10
-rw-r--r--drivers/net/lp486e.c2
-rw-r--r--drivers/net/mac89x0.c6
-rw-r--r--drivers/net/mace.c4
-rw-r--r--drivers/net/macsonic.c4
-rw-r--r--drivers/net/macvlan.c278
-rw-r--r--drivers/net/mdio.c12
-rw-r--r--drivers/net/mipsnet.c2
-rw-r--r--drivers/net/mlx4/en_rx.c2
-rw-r--r--drivers/net/mlx4/en_tx.c2
-rw-r--r--drivers/net/mlx4/mlx4_en.h2
-rw-r--r--drivers/net/mv643xx_eth.c2
-rw-r--r--drivers/net/myri10ge/myri10ge.c25
-rw-r--r--drivers/net/myri_sbus.c2
-rw-r--r--drivers/net/natsemi.c30
-rw-r--r--drivers/net/netx-eth.c5
-rw-r--r--drivers/net/netxen/netxen_nic.h78
-rw-r--r--drivers/net/netxen/netxen_nic_ethtool.c38
-rw-r--r--drivers/net/netxen/netxen_nic_hdr.h76
-rw-r--r--drivers/net/netxen/netxen_nic_hw.c995
-rw-r--r--drivers/net/netxen/netxen_nic_init.c361
-rw-r--r--drivers/net/netxen/netxen_nic_main.c334
-rw-r--r--drivers/net/ni5010.c2
-rw-r--r--drivers/net/ni52.c6
-rw-r--r--drivers/net/ni65.c2
-rw-r--r--drivers/net/niu.c15
-rw-r--r--drivers/net/ns83820.c24
-rw-r--r--drivers/net/pasemi_mac.c4
-rw-r--r--drivers/net/pasemi_mac_ethtool.c14
-rw-r--r--drivers/net/pci-skeleton.c12
-rw-r--r--drivers/net/pcmcia/3c574_cs.c90
-rw-r--r--drivers/net/pcmcia/3c589_cs.c102
-rw-r--r--drivers/net/pcmcia/axnet_cs.c60
-rw-r--r--drivers/net/pcmcia/com20020_cs.c63
-rw-r--r--drivers/net/pcmcia/fmvj18x_cs.c211
-rw-r--r--drivers/net/pcmcia/ibmtr_cs.c71
-rw-r--r--drivers/net/pcmcia/nmclan_cs.c175
-rw-r--r--drivers/net/pcmcia/pcnet_cs.c91
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c348
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c265
-rw-r--r--drivers/net/pcnet32.c22
-rw-r--r--drivers/net/phy/broadcom.c208
-rw-r--r--drivers/net/phy/phy.c18
-rw-r--r--drivers/net/plip.c13
-rw-r--r--drivers/net/ppp_async.c19
-rw-r--r--drivers/net/ppp_deflate.c44
-rw-r--r--drivers/net/ppp_generic.c98
-rw-r--r--drivers/net/ppp_mppe.c8
-rw-r--r--drivers/net/ppp_synctty.c9
-rw-r--r--drivers/net/pppoe.c63
-rw-r--r--drivers/net/pppol2tp.c62
-rw-r--r--drivers/net/pppox.c5
-rw-r--r--drivers/net/ps3_gelic_net.c100
-rw-r--r--drivers/net/ps3_gelic_net.h21
-rw-r--r--drivers/net/ps3_gelic_wireless.h2
-rw-r--r--drivers/net/qla3xxx.c10
-rw-r--r--drivers/net/qlge/qlge.h229
-rw-r--r--drivers/net/qlge/qlge_dbg.c180
-rw-r--r--drivers/net/qlge/qlge_ethtool.c290
-rw-r--r--drivers/net/qlge/qlge_main.c490
-rw-r--r--drivers/net/qlge/qlge_mpi.c210
-rw-r--r--drivers/net/r6040.c5
-rw-r--r--drivers/net/r8169.c71
-rw-r--r--drivers/net/s2io.c2
-rw-r--r--drivers/net/s6gmac.c8
-rw-r--r--drivers/net/sb1000.c2
-rw-r--r--drivers/net/sb1250-mac.c3
-rw-r--r--drivers/net/sc92031.c18
-rw-r--r--drivers/net/seeq8005.c4
-rw-r--r--drivers/net/sfc/Kconfig13
-rw-r--r--drivers/net/sfc/Makefile7
-rw-r--r--drivers/net/sfc/bitfield.h17
-rw-r--r--drivers/net/sfc/boards.c328
-rw-r--r--drivers/net/sfc/boards.h28
-rw-r--r--drivers/net/sfc/efx.c793
-rw-r--r--drivers/net/sfc/efx.h65
-rw-r--r--drivers/net/sfc/enum.h116
-rw-r--r--drivers/net/sfc/ethtool.c222
-rw-r--r--drivers/net/sfc/ethtool.h27
-rw-r--r--drivers/net/sfc/falcon.c2829
-rw-r--r--drivers/net/sfc/falcon.h145
-rw-r--r--drivers/net/sfc/falcon_boards.c752
-rw-r--r--drivers/net/sfc/falcon_gmac.c123
-rw-r--r--drivers/net/sfc/falcon_hwdefs.h1333
-rw-r--r--drivers/net/sfc/falcon_io.h258
-rw-r--r--drivers/net/sfc/falcon_xmac.c278
-rw-r--r--drivers/net/sfc/gmii.h60
-rw-r--r--drivers/net/sfc/io.h256
-rw-r--r--drivers/net/sfc/mac.h6
-rw-r--r--drivers/net/sfc/mcdi.c1112
-rw-r--r--drivers/net/sfc/mcdi.h130
-rw-r--r--drivers/net/sfc/mcdi_mac.c152
-rw-r--r--drivers/net/sfc/mcdi_pcol.h1578
-rw-r--r--drivers/net/sfc/mcdi_phy.c597
-rw-r--r--drivers/net/sfc/mdio_10g.c144
-rw-r--r--drivers/net/sfc/mdio_10g.h6
-rw-r--r--drivers/net/sfc/mtd.c559
-rw-r--r--drivers/net/sfc/net_driver.h302
-rw-r--r--drivers/net/sfc/nic.c1583
-rw-r--r--drivers/net/sfc/nic.h261
-rw-r--r--drivers/net/sfc/phy.h27
-rw-r--r--drivers/net/sfc/qt202x_phy.c (renamed from drivers/net/sfc/xfp_phy.c)132
-rw-r--r--drivers/net/sfc/regs.h3168
-rw-r--r--drivers/net/sfc/rx.c82
-rw-r--r--drivers/net/sfc/rx.h26
-rw-r--r--drivers/net/sfc/selftest.c146
-rw-r--r--drivers/net/sfc/sfe4001.c435
-rw-r--r--drivers/net/sfc/siena.c604
-rw-r--r--drivers/net/sfc/spi.h18
-rw-r--r--drivers/net/sfc/tenxpress.c223
-rw-r--r--drivers/net/sfc/tx.c184
-rw-r--r--drivers/net/sfc/tx.h25
-rw-r--r--drivers/net/sfc/workarounds.h20
-rw-r--r--drivers/net/sgiseeq.c7
-rw-r--r--drivers/net/sh_eth.c56
-rw-r--r--drivers/net/sh_eth.h1
-rw-r--r--drivers/net/sis190.c3
-rw-r--r--drivers/net/sis900.c6
-rw-r--r--drivers/net/skfp/h/smc.h8
-rw-r--r--drivers/net/skfp/skfddi.c2
-rw-r--r--drivers/net/skge.c32
-rw-r--r--drivers/net/sky2.c159
-rw-r--r--drivers/net/sky2.h185
-rw-r--r--drivers/net/slip.c33
-rw-r--r--drivers/net/smc-mca.c6
-rw-r--r--drivers/net/smc911x.c2
-rw-r--r--drivers/net/smc9194.c2
-rw-r--r--drivers/net/smc91x.c20
-rw-r--r--drivers/net/smc91x.h10
-rw-r--r--drivers/net/smsc911x.c9
-rw-r--r--drivers/net/smsc911x.h2
-rw-r--r--drivers/net/smsc9420.c2
-rw-r--r--drivers/net/spider_net.c3
-rw-r--r--drivers/net/starfire.c10
-rw-r--r--drivers/net/stmmac/gmac.c2
-rw-r--r--drivers/net/stmmac/gmac.h4
-rw-r--r--drivers/net/stmmac/stmmac_main.c5
-rw-r--r--drivers/net/sun3_82586.c2
-rw-r--r--drivers/net/sunbmac.c2
-rw-r--r--drivers/net/sundance.c18
-rw-r--r--drivers/net/sungem.c4
-rw-r--r--drivers/net/sungem.h4
-rw-r--r--drivers/net/sunhme.c27
-rw-r--r--drivers/net/sunlance.c2
-rw-r--r--drivers/net/sunqe.c2
-rw-r--r--drivers/net/tc35815.c292
-rw-r--r--drivers/net/tehuti.c45
-rw-r--r--drivers/net/tehuti.h2
-rw-r--r--drivers/net/tg3.c1254
-rw-r--r--drivers/net/tg3.h99
-rw-r--r--drivers/net/tlan.c11
-rw-r--r--drivers/net/tokenring/3c359.c3
-rw-r--r--drivers/net/tokenring/ibmtr.c4
-rw-r--r--drivers/net/tokenring/lanstreamer.c10
-rw-r--r--drivers/net/tokenring/olympic.c4
-rw-r--r--drivers/net/tokenring/smctr.c88
-rw-r--r--drivers/net/tokenring/tms380tr.c26
-rw-r--r--drivers/net/tsi108_eth.c10
-rw-r--r--drivers/net/tulip/21142.c8
-rw-r--r--drivers/net/tulip/de2104x.c6
-rw-r--r--drivers/net/tulip/dmfe.c2
-rw-r--r--drivers/net/tulip/eeprom.c8
-rw-r--r--drivers/net/tulip/interrupt.c16
-rw-r--r--drivers/net/tulip/media.c4
-rw-r--r--drivers/net/tulip/pnic2.c6
-rw-r--r--drivers/net/tulip/tulip_core.c26
-rw-r--r--drivers/net/tulip/uli526x.c2
-rw-r--r--drivers/net/tulip/winbond-840.c10
-rw-r--r--drivers/net/tulip/xircom_cb.c2
-rw-r--r--drivers/net/tun.c58
-rw-r--r--drivers/net/typhoon.c6
-rw-r--r--drivers/net/ucc_geth.c26
-rw-r--r--drivers/net/ucc_geth.h20
-rw-r--r--drivers/net/usb/asix.c24
-rw-r--r--drivers/net/usb/catc.c4
-rw-r--r--drivers/net/usb/cdc-phonet.c8
-rw-r--r--drivers/net/usb/cdc_eem.c4
-rw-r--r--drivers/net/usb/cdc_ether.c79
-rw-r--r--drivers/net/usb/dm9601.c2
-rw-r--r--drivers/net/usb/hso.c34
-rw-r--r--drivers/net/usb/kaweth.c11
-rw-r--r--drivers/net/usb/mcs7830.c4
-rw-r--r--drivers/net/usb/rndis_host.c10
-rw-r--r--drivers/net/usb/smsc95xx.c2
-rw-r--r--drivers/net/usb/usbnet.c218
-rw-r--r--drivers/net/usb/zaurus.c4
-rw-r--r--drivers/net/veth.c38
-rw-r--r--drivers/net/via-rhine.c20
-rw-r--r--drivers/net/via-velocity.c405
-rw-r--r--drivers/net/via-velocity.h15
-rw-r--r--drivers/net/virtio_net.c17
-rw-r--r--drivers/net/vmxnet3/vmxnet3_defs.h246
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c363
-rw-r--r--drivers/net/vmxnet3/vmxnet3_ethtool.c10
-rw-r--r--drivers/net/vmxnet3/vmxnet3_int.h17
-rw-r--r--drivers/net/vxge/vxge-config.c300
-rw-r--r--drivers/net/vxge/vxge-config.h2
-rw-r--r--drivers/net/vxge/vxge-main.c115
-rw-r--r--drivers/net/vxge/vxge-main.h1
-rw-r--r--drivers/net/vxge/vxge-reg.h4
-rw-r--r--drivers/net/vxge/vxge-traffic.c24
-rw-r--r--drivers/net/vxge/vxge-traffic.h2
-rw-r--r--drivers/net/vxge/vxge-version.h4
-rw-r--r--drivers/net/wan/cosa.c20
-rw-r--r--drivers/net/wan/dlci.c14
-rw-r--r--drivers/net/wan/dscc4.c2
-rw-r--r--drivers/net/wan/farsync.c12
-rw-r--r--drivers/net/wan/hdlc.c4
-rw-r--r--drivers/net/wan/hdlc_fr.c8
-rw-r--r--drivers/net/wan/hostess_sv11.c2
-rw-r--r--drivers/net/wan/lmc/lmc_main.c4
-rw-r--r--drivers/net/wan/n2.c2
-rw-r--r--drivers/net/wan/pc300_drv.c17
-rw-r--r--drivers/net/wan/sbni.c28
-rw-r--r--drivers/net/wan/sdla.c2
-rw-r--r--drivers/net/wan/sealevel.c13
-rw-r--r--drivers/net/wan/x25_asy.c23
-rw-r--r--drivers/net/wimax/i2400m/Kconfig8
-rw-r--r--drivers/net/wimax/i2400m/control.c16
-rw-r--r--drivers/net/wimax/i2400m/debugfs.c2
-rw-r--r--drivers/net/wimax/i2400m/driver.c500
-rw-r--r--drivers/net/wimax/i2400m/fw.c886
-rw-r--r--drivers/net/wimax/i2400m/i2400m-sdio.h16
-rw-r--r--drivers/net/wimax/i2400m/i2400m-usb.h16
-rw-r--r--drivers/net/wimax/i2400m/i2400m.h209
-rw-r--r--drivers/net/wimax/i2400m/netdev.c127
-rw-r--r--drivers/net/wimax/i2400m/rx.c172
-rw-r--r--drivers/net/wimax/i2400m/sdio-fw.c11
-rw-r--r--drivers/net/wimax/i2400m/sdio-rx.c42
-rw-r--r--drivers/net/wimax/i2400m/sdio-tx.c5
-rw-r--r--drivers/net/wimax/i2400m/sdio.c205
-rw-r--r--drivers/net/wimax/i2400m/tx.c20
-rw-r--r--drivers/net/wimax/i2400m/usb-fw.c37
-rw-r--r--drivers/net/wimax/i2400m/usb-notif.c35
-rw-r--r--drivers/net/wimax/i2400m/usb-rx.c60
-rw-r--r--drivers/net/wimax/i2400m/usb-tx.c61
-rw-r--r--drivers/net/wimax/i2400m/usb.c189
-rw-r--r--drivers/net/wireless/Kconfig212
-rw-r--r--drivers/net/wireless/Makefile10
-rw-r--r--drivers/net/wireless/adm8211.c2
-rw-r--r--drivers/net/wireless/airo.c5
-rw-r--r--drivers/net/wireless/airo_cs.c55
-rw-r--r--drivers/net/wireless/at76c50x-usb.c55
-rw-r--r--drivers/net/wireless/ath/Kconfig9
-rw-r--r--drivers/net/wireless/ath/Makefile9
-rw-r--r--drivers/net/wireless/ath/ar9170/Kconfig2
-rw-r--r--drivers/net/wireless/ath/ar9170/ar9170.h6
-rw-r--r--drivers/net/wireless/ath/ar9170/cmd.c3
-rw-r--r--drivers/net/wireless/ath/ar9170/cmd.h1
-rw-r--r--drivers/net/wireless/ath/ar9170/hw.h6
-rw-r--r--drivers/net/wireless/ath/ar9170/mac.c15
-rw-r--r--drivers/net/wireless/ath/ar9170/main.c50
-rw-r--r--drivers/net/wireless/ath/ar9170/phy.c99
-rw-r--r--drivers/net/wireless/ath/ar9170/usb.c16
-rw-r--r--drivers/net/wireless/ath/ar9170/usb.h2
-rw-r--r--drivers/net/wireless/ath/ath.h69
-rw-r--r--drivers/net/wireless/ath/ath5k/Kconfig2
-rw-r--r--drivers/net/wireless/ath/ath5k/ath5k.h53
-rw-r--r--drivers/net/wireless/ath/ath5k/attach.c33
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c140
-rw-r--r--drivers/net/wireless/ath/ath5k/base.h18
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/initvals.c4
-rw-r--r--drivers/net/wireless/ath/ath5k/led.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/pcu.c193
-rw-r--r--drivers/net/wireless/ath/ath5k/phy.c197
-rw-r--r--drivers/net/wireless/ath/ath5k/reg.h19
-rw-r--r--drivers/net/wireless/ath/ath5k/reset.c33
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig18
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile32
-rw-r--r--drivers/net/wireless/ath/ath9k/ahb.c29
-rw-r--r--drivers/net/wireless/ath/ath9k/ani.c141
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h215
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c136
-rw-r--r--drivers/net/wireless/ath/ath9k/btcoex.c383
-rw-r--r--drivers/net/wireless/ath/ath9k/btcoex.h64
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.c421
-rw-r--r--drivers/net/wireless/ath/ath9k/calib.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/common.c299
-rw-r--r--drivers/net/wireless/ath/ath9k/common.h127
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c72
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h47
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.h9
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_4k.c94
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_9287.c97
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_def.c183
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c1344
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h98
-rw-r--r--drivers/net/wireless/ath/ath9k/initvals.h101
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c200
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h26
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c1413
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c47
-rw-r--r--drivers/net/wireless/ath/ath9k/phy.c1034
-rw-r--r--drivers/net/wireless/ath/ath9k/phy.h42
-rw-r--r--drivers/net/wireless/ath/ath9k/rc.c537
-rw-r--r--drivers/net/wireless/ath/ath9k/rc.h25
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c366
-rw-r--r--drivers/net/wireless/ath/ath9k/reg.h27
-rw-r--r--drivers/net/wireless/ath/ath9k/virtual.c110
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c354
-rw-r--r--drivers/net/wireless/ath/debug.c32
-rw-r--r--drivers/net/wireless/ath/debug.h77
-rw-r--r--drivers/net/wireless/ath/hw.c126
-rw-r--r--drivers/net/wireless/ath/reg.h27
-rw-r--r--drivers/net/wireless/ath/regd.c5
-rw-r--r--drivers/net/wireless/ath/regd.h8
-rw-r--r--drivers/net/wireless/ath/regd_common.h32
-rw-r--r--drivers/net/wireless/atmel.c16
-rw-r--r--drivers/net/wireless/atmel_cs.c51
-rw-r--r--drivers/net/wireless/b43/Kconfig2
-rw-r--r--drivers/net/wireless/b43/b43.h18
-rw-r--r--drivers/net/wireless/b43/dma.c312
-rw-r--r--drivers/net/wireless/b43/dma.h13
-rw-r--r--drivers/net/wireless/b43/leds.c1
-rw-r--r--drivers/net/wireless/b43/main.c8
-rw-r--r--drivers/net/wireless/b43/pcmcia.c26
-rw-r--r--drivers/net/wireless/b43/phy_lp.c777
-rw-r--r--drivers/net/wireless/b43/phy_lp.h11
-rw-r--r--drivers/net/wireless/b43/pio.c85
-rw-r--r--drivers/net/wireless/b43/rfkill.c10
-rw-r--r--drivers/net/wireless/b43/xmit.c8
-rw-r--r--drivers/net/wireless/b43/xmit.h19
-rw-r--r--drivers/net/wireless/b43legacy/Kconfig2
-rw-r--r--drivers/net/wireless/b43legacy/b43legacy.h2
-rw-r--r--drivers/net/wireless/b43legacy/dma.c17
-rw-r--r--drivers/net/wireless/b43legacy/main.c8
-rw-r--r--drivers/net/wireless/b43legacy/rfkill.c7
-rw-r--r--drivers/net/wireless/b43legacy/xmit.c1
-rw-r--r--drivers/net/wireless/hostap/Kconfig3
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c51
-rw-r--r--drivers/net/wireless/i82593.h229
-rw-r--r--drivers/net/wireless/ipw2x00/Kconfig11
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c153
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2200.c167
-rw-r--r--drivers/net/wireless/ipw2x00/libipw.h8
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_module.c73
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig30
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c51
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-hw.h12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-led.c371
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-led.h22
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-rs.c102
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.c287
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.h56
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965-hw.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c255
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c415
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c305
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-led.c85
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-led.h32
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.c584
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rs.h101
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c776
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-calib.c78
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-commands.h297
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c691
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h86
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h189
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debugfs.c860
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h150
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace.c14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace.h197
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom.c133
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom.h26
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-hcmd.c29
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.h56
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.c324
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-led.h46
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.c232
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-power.h3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h19
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rx.c218
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c40
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-spectrum.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.c160
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-sta.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c155
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c444
-rw-r--r--drivers/net/wireless/iwmc3200wifi/Kconfig3
-rw-r--r--drivers/net/wireless/iwmc3200wifi/cfg80211.c82
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.c108
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.h93
-rw-r--r--drivers/net/wireless/iwmc3200wifi/debugfs.c23
-rw-r--r--drivers/net/wireless/iwmc3200wifi/eeprom.c50
-rw-r--r--drivers/net/wireless/iwmc3200wifi/eeprom.h29
-rw-r--r--drivers/net/wireless/iwmc3200wifi/fw.c9
-rw-r--r--drivers/net/wireless/iwmc3200wifi/hal.c2
-rw-r--r--drivers/net/wireless/iwmc3200wifi/iwm.h17
-rw-r--r--drivers/net/wireless/iwmc3200wifi/lmac.h8
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c92
-rw-r--r--drivers/net/wireless/iwmc3200wifi/netdev.c9
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.c152
-rw-r--r--drivers/net/wireless/iwmc3200wifi/sdio.c13
-rw-r--r--drivers/net/wireless/iwmc3200wifi/tx.c66
-rw-r--r--drivers/net/wireless/iwmc3200wifi/umac.h41
-rw-r--r--drivers/net/wireless/libertas/11d.c696
-rw-r--r--drivers/net/wireless/libertas/11d.h105
-rw-r--r--drivers/net/wireless/libertas/Kconfig39
-rw-r--r--drivers/net/wireless/libertas/Makefile14
-rw-r--r--drivers/net/wireless/libertas/README26
-rw-r--r--drivers/net/wireless/libertas/assoc.c445
-rw-r--r--drivers/net/wireless/libertas/assoc.h141
-rw-r--r--drivers/net/wireless/libertas/cfg.c198
-rw-r--r--drivers/net/wireless/libertas/cfg.h16
-rw-r--r--drivers/net/wireless/libertas/cmd.c695
-rw-r--r--drivers/net/wireless/libertas/cmd.h127
-rw-r--r--drivers/net/wireless/libertas/cmdresp.c116
-rw-r--r--drivers/net/wireless/libertas/debugfs.c27
-rw-r--r--drivers/net/wireless/libertas/decl.h65
-rw-r--r--drivers/net/wireless/libertas/defs.h3
-rw-r--r--drivers/net/wireless/libertas/dev.h431
-rw-r--r--drivers/net/wireless/libertas/ethtool.c84
-rw-r--r--drivers/net/wireless/libertas/host.h959
-rw-r--r--drivers/net/wireless/libertas/hostcmd.h800
-rw-r--r--drivers/net/wireless/libertas/if_cs.c76
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c64
-rw-r--r--drivers/net/wireless/libertas/if_sdio.h3
-rw-r--r--drivers/net/wireless/libertas/if_spi.c143
-rw-r--r--drivers/net/wireless/libertas/if_usb.c5
-rw-r--r--drivers/net/wireless/libertas/main.c720
-rw-r--r--drivers/net/wireless/libertas/mesh.c1141
-rw-r--r--drivers/net/wireless/libertas/mesh.h78
-rw-r--r--drivers/net/wireless/libertas/persistcfg.c453
-rw-r--r--drivers/net/wireless/libertas/rx.c13
-rw-r--r--drivers/net/wireless/libertas/scan.c250
-rw-r--r--drivers/net/wireless/libertas/scan.h30
-rw-r--r--drivers/net/wireless/libertas/tx.c9
-rw-r--r--drivers/net/wireless/libertas/types.h4
-rw-r--r--drivers/net/wireless/libertas/wext.c196
-rw-r--r--drivers/net/wireless/libertas/wext.h9
-rw-r--r--drivers/net/wireless/libertas_tf/if_usb.c2
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c85
-rw-r--r--drivers/net/wireless/mwl8k.c1265
-rw-r--r--drivers/net/wireless/orinoco/Kconfig6
-rw-r--r--drivers/net/wireless/orinoco/fw.c6
-rw-r--r--drivers/net/wireless/orinoco/hermes_dld.c4
-rw-r--r--drivers/net/wireless/orinoco/hw.c33
-rw-r--r--drivers/net/wireless/orinoco/hw.h3
-rw-r--r--drivers/net/wireless/orinoco/main.c34
-rw-r--r--drivers/net/wireless/orinoco/orinoco.h1
-rw-r--r--drivers/net/wireless/orinoco/orinoco_cs.c33
-rw-r--r--drivers/net/wireless/orinoco/spectrum_cs.c60
-rw-r--r--drivers/net/wireless/p54/Kconfig2
-rw-r--r--drivers/net/wireless/p54/eeprom.c31
-rw-r--r--drivers/net/wireless/p54/main.c2
-rw-r--r--drivers/net/wireless/p54/p54pci.c2
-rw-r--r--drivers/net/wireless/prism54/isl_ioctl.c4
-rw-r--r--drivers/net/wireless/prism54/islpci_dev.c3
-rw-r--r--drivers/net/wireless/prism54/islpci_hotplug.c2
-rw-r--r--drivers/net/wireless/ray_cs.c393
-rw-r--r--drivers/net/wireless/rndis_wlan.c13
-rw-r--r--drivers/net/wireless/rt2x00/Kconfig46
-rw-r--r--drivers/net/wireless/rt2x00/Makefile3
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.c31
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.h4
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.c32
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.h4
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c166
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.h4
-rw-r--r--drivers/net/wireless/rt2x00/rt2800.h1852
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c2284
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.h151
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c1322
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.h159
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c2286
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.h1864
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h73
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00config.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00crypto.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c19
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dump.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00firmware.c3
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00ht.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00leds.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00leds.h6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00lib.h20
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00link.c90
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c10
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.c4
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.h26
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c15
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h7
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00reg.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00soc.c165
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00soc.h52
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.c6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.h21
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c65
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.h4
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c29
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.h4
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180_dev.c2
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187.h1
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_dev.c13
-rw-r--r--drivers/net/wireless/wl12xx/Kconfig3
-rw-r--r--drivers/net/wireless/wl12xx/wl1251.h5
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_acx.c68
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_acx.h36
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_boot.c55
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_event.c15
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_init.c8
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_main.c171
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_netlink.h30
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_ps.c44
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_ps.h1
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_reg.h6
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_rx.c6
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_spi.c4
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h95
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.c369
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.h586
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_boot.c218
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_boot.h22
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_cmd.c503
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_cmd.h190
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_conf.h919
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.c121
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.h37
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_init.c141
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_init.h83
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c979
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_ps.c68
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_ps.h2
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_reg.h47
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_rx.c88
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_rx.h4
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_spi.c311
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_spi.h65
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.c76
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.h18
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx_80211.h4
-rw-r--r--drivers/net/wireless/wl3501_cs.c76
-rw-r--r--drivers/net/wireless/zd1201.c3
-rw-r--r--drivers/net/wireless/zd1211rw/Kconfig2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.c4
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.h18
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c204
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.h25
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c11
-rw-r--r--drivers/net/xilinx_emaclite.c2
-rw-r--r--drivers/net/xtsonic.c2
-rw-r--r--drivers/net/yellowfin.c10
-rw-r--r--drivers/net/znet.c9
-rw-r--r--drivers/of/base.c26
-rw-r--r--drivers/parisc/ccio-dma.c2
-rw-r--r--drivers/parisc/eisa_eeprom.c10
-rw-r--r--drivers/parisc/led.c7
-rw-r--r--drivers/parport/parport_cs.c38
-rw-r--r--drivers/parport/parport_mfc3.c2
-rw-r--r--drivers/parport/procfs.c39
-rw-r--r--drivers/pci/dmar.c7
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c2
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c22
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c2
-rw-r--r--drivers/pci/intel-iommu.c6
-rw-r--r--drivers/pci/intr_remapping.c89
-rw-r--r--drivers/pci/intr_remapping.h7
-rw-r--r--drivers/pcmcia/Kconfig42
-rw-r--r--drivers/pcmcia/Makefile10
-rw-r--r--drivers/pcmcia/cardbus.c4
-rw-r--r--drivers/pcmcia/cirrus.h10
-rw-r--r--drivers/pcmcia/cistpl.c71
-rw-r--r--drivers/pcmcia/cs.c67
-rw-r--r--drivers/pcmcia/cs_internal.h42
-rw-r--r--drivers/pcmcia/ds.c188
-rw-r--r--drivers/pcmcia/i82365.c37
-rw-r--r--drivers/pcmcia/m32r_cfc.c105
-rw-r--r--drivers/pcmcia/m32r_pcc.c51
-rw-r--r--drivers/pcmcia/m8xx_pcmcia.c40
-rw-r--r--drivers/pcmcia/o2micro.h22
-rw-r--r--drivers/pcmcia/omap_cf.c4
-rw-r--r--drivers/pcmcia/pcmcia_ioctl.c92
-rw-r--r--drivers/pcmcia/pcmcia_resource.c482
-rw-r--r--drivers/pcmcia/pd6729.c70
-rw-r--r--drivers/pcmcia/pd6729.h7
-rw-r--r--drivers/pcmcia/pxa2xx_base.c94
-rw-r--r--drivers/pcmcia/pxa2xx_base.h3
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x255.c2
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x270.c2
-rw-r--r--drivers/pcmcia/pxa2xx_e740.c2
-rw-r--r--drivers/pcmcia/pxa2xx_lubbock.c14
-rw-r--r--drivers/pcmcia/pxa2xx_mainstone.c2
-rw-r--r--drivers/pcmcia/pxa2xx_palmld.c2
-rw-r--r--drivers/pcmcia/pxa2xx_palmtx.c2
-rw-r--r--drivers/pcmcia/pxa2xx_sharpsl.c2
-rw-r--r--drivers/pcmcia/pxa2xx_trizeps4.c4
-rw-r--r--drivers/pcmcia/pxa2xx_viper.c2
-rw-r--r--drivers/pcmcia/rsrc_mgr.c1
-rw-r--r--drivers/pcmcia/sa1100_assabet.c2
-rw-r--r--drivers/pcmcia/sa1100_badge4.c11
-rw-r--r--drivers/pcmcia/sa1100_cerf.c2
-rw-r--r--drivers/pcmcia/sa1100_generic.c13
-rw-r--r--drivers/pcmcia/sa1100_h3600.c140
-rw-r--r--drivers/pcmcia/sa1100_jornada720.c42
-rw-r--r--drivers/pcmcia/sa1100_neponset.c13
-rw-r--r--drivers/pcmcia/sa1100_shannon.c2
-rw-r--r--drivers/pcmcia/sa1100_simpad.c2
-rw-r--r--drivers/pcmcia/sa1111_generic.c65
-rw-r--r--drivers/pcmcia/sa1111_generic.h17
-rw-r--r--drivers/pcmcia/sa11xx_base.c99
-rw-r--r--drivers/pcmcia/sa11xx_base.h2
-rw-r--r--drivers/pcmcia/soc_common.c225
-rw-r--r--drivers/pcmcia/soc_common.h10
-rw-r--r--drivers/pcmcia/tcic.c29
-rw-r--r--drivers/pcmcia/topic.h15
-rw-r--r--drivers/platform/Kconfig2
-rw-r--r--drivers/platform/x86/intel_menlow.c2
-rw-r--r--drivers/platform/x86/sony-laptop.c2
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c4
-rw-r--r--drivers/pnp/pnpbios/rsparser.c8
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/collie_battery.c418
-rw-r--r--drivers/ps3/ps3-sys-manager.c2
-rw-r--r--drivers/rtc/Kconfig21
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/rtc-ds1302.c2
-rw-r--r--drivers/rtc/rtc-ds1511.c4
-rw-r--r--drivers/rtc/rtc-msm6242.c269
-rw-r--r--drivers/rtc/rtc-rp5c01.c222
-rw-r--r--drivers/rtc/rtc-stk17ta8.c2
-rw-r--r--drivers/rtc/rtc-v3020.c2
-rw-r--r--drivers/s390/block/dasd.c237
-rw-r--r--drivers/s390/block/dasd_3990_erp.c47
-rw-r--r--drivers/s390/block/dasd_alias.c77
-rw-r--r--drivers/s390/block/dasd_diag.c20
-rw-r--r--drivers/s390/block/dasd_eckd.c170
-rw-r--r--drivers/s390/block/dasd_eckd.h4
-rw-r--r--drivers/s390/block/dasd_eer.c5
-rw-r--r--drivers/s390/block/dasd_fba.c11
-rw-r--r--drivers/s390/block/dasd_int.h13
-rw-r--r--drivers/s390/block/dasd_ioctl.c4
-rw-r--r--drivers/s390/block/dasd_proc.c2
-rw-r--r--drivers/s390/char/con3215.c1
-rw-r--r--drivers/s390/char/con3270.c1
-rw-r--r--drivers/s390/char/fs3270.c12
-rw-r--r--drivers/s390/char/monreader.c8
-rw-r--r--drivers/s390/char/monwriter.c7
-rw-r--r--drivers/s390/char/sclp_async.c5
-rw-r--r--drivers/s390/char/sclp_cmd.c1
-rw-r--r--drivers/s390/char/tape.h9
-rw-r--r--drivers/s390/char/tape_34xx.c8
-rw-r--r--drivers/s390/char/tape_3590.c2
-rw-r--r--drivers/s390/char/tape_block.c17
-rw-r--r--drivers/s390/char/tape_char.c54
-rw-r--r--drivers/s390/char/tape_core.c65
-rw-r--r--drivers/s390/char/tape_proc.c2
-rw-r--r--drivers/s390/char/tty3270.c20
-rw-r--r--drivers/s390/char/vmlogrdr.c8
-rw-r--r--drivers/s390/char/vmur.c3
-rw-r--r--drivers/s390/char/vmwatchdog.c29
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/ccwreq.c328
-rw-r--r--drivers/s390/cio/chp.c2
-rw-r--r--drivers/s390/cio/cio.c1
-rw-r--r--drivers/s390/cio/cio.h8
-rw-r--r--drivers/s390/cio/cmf.c2
-rw-r--r--drivers/s390/cio/css.c57
-rw-r--r--drivers/s390/cio/css.h3
-rw-r--r--drivers/s390/cio/device.c1006
-rw-r--r--drivers/s390/cio/device.h25
-rw-r--r--drivers/s390/cio/device_fsm.c411
-rw-r--r--drivers/s390/cio/device_id.c375
-rw-r--r--drivers/s390/cio/device_ops.c142
-rw-r--r--drivers/s390/cio/device_pgid.c963
-rw-r--r--drivers/s390/cio/device_status.c3
-rw-r--r--drivers/s390/cio/io_sch.h73
-rw-r--r--drivers/s390/crypto/ap_bus.c31
-rw-r--r--drivers/s390/crypto/ap_bus.h18
-rw-r--r--drivers/s390/crypto/zcrypt_api.c11
-rw-r--r--drivers/s390/crypto/zcrypt_api.h2
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c75
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c2
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c2
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c38
-rw-r--r--drivers/s390/net/Makefile6
-rw-r--r--drivers/s390/net/claw.c82
-rw-r--r--drivers/s390/net/claw.h12
-rw-r--r--drivers/s390/net/ctcm_fsms.c1
-rw-r--r--drivers/s390/net/ctcm_fsms.h1
-rw-r--r--drivers/s390/net/ctcm_main.c168
-rw-r--r--drivers/s390/net/ctcm_main.h20
-rw-r--r--drivers/s390/net/ctcm_mpc.c1
-rw-r--r--drivers/s390/net/ctcm_sysfs.c11
-rw-r--r--drivers/s390/net/cu3088.c148
-rw-r--r--drivers/s390/net/cu3088.h41
-rw-r--r--drivers/s390/net/fsm.c1
-rw-r--r--drivers/s390/net/fsm.h2
-rw-r--r--drivers/s390/net/lcs.c115
-rw-r--r--drivers/s390/net/lcs.h18
-rw-r--r--drivers/s390/net/netiucv.c4
-rw-r--r--drivers/s390/net/qeth_core.h8
-rw-r--r--drivers/s390/net/qeth_core_main.c225
-rw-r--r--drivers/s390/net/qeth_core_mpc.h47
-rw-r--r--drivers/s390/net/qeth_core_sys.c83
-rw-r--r--drivers/s390/net/qeth_l2_main.c33
-rw-r--r--drivers/s390/net/qeth_l3.h2
-rw-r--r--drivers/s390/net/qeth_l3_main.c144
-rw-r--r--drivers/s390/net/qeth_l3_sys.c67
-rw-r--r--drivers/s390/scsi/zfcp_aux.c388
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c179
-rw-r--r--drivers/s390/scsi/zfcp_cfdc.c26
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c134
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h10
-rw-r--r--drivers/s390/scsi/zfcp_def.h323
-rw-r--r--drivers/s390/scsi/zfcp_erp.c138
-rw-r--r--drivers/s390/scsi/zfcp_ext.h38
-rw-r--r--drivers/s390/scsi/zfcp_fc.c677
-rw-r--r--drivers/s390/scsi/zfcp_fc.h260
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c367
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h53
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c156
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c250
-rw-r--r--drivers/sbus/char/envctrl.c4
-rw-r--r--drivers/scsi/3w-9xxx.c8
-rw-r--r--drivers/scsi/3w-sas.c1924
-rw-r--r--drivers/scsi/3w-sas.h396
-rw-r--r--drivers/scsi/3w-xxxx.c8
-rw-r--r--drivers/scsi/53c700.c9
-rw-r--r--drivers/scsi/Kconfig29
-rw-r--r--drivers/scsi/Makefile3
-rw-r--r--drivers/scsi/aacraid/aacraid.h6
-rw-r--r--drivers/scsi/aacraid/comminit.c2
-rw-r--r--drivers/scsi/aacraid/linit.c6
-rw-r--r--drivers/scsi/advansys.c2
-rw-r--r--drivers/scsi/aic7xxx/aic79xx.seq4
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c2
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c2
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx.seq2
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_core.c2
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_reg_def.h2
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c5
-rw-r--r--drivers/scsi/be2iscsi/be.h24
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.c263
-rw-r--r--drivers/scsi/be2iscsi/be_cmds.h37
-rw-r--r--drivers/scsi/be2iscsi/be_iscsi.c23
-rw-r--r--drivers/scsi/be2iscsi/be_main.c1019
-rw-r--r--drivers/scsi/be2iscsi/be_main.h49
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.c69
-rw-r--r--drivers/scsi/be2iscsi/be_mgmt.h8
-rw-r--r--drivers/scsi/bfa/bfa_cb_ioim_macros.h10
-rw-r--r--drivers/scsi/bfa/bfa_cee.c2
-rw-r--r--drivers/scsi/bfa/bfa_csdebug.c4
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.c4
-rw-r--r--drivers/scsi/bfa/bfa_fcpim_priv.h4
-rw-r--r--drivers/scsi/bfa/bfa_fcport.c59
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c11
-rw-r--r--drivers/scsi/bfa/bfa_fcxp.c8
-rw-r--r--drivers/scsi/bfa/bfa_intr.c2
-rw-r--r--drivers/scsi/bfa/bfa_intr_priv.h18
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c10
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h12
-rw-r--r--drivers/scsi/bfa/bfa_iocfc.c8
-rw-r--r--drivers/scsi/bfa/bfa_iocfc.h8
-rw-r--r--drivers/scsi/bfa/bfa_ioim.c4
-rw-r--r--drivers/scsi/bfa/bfa_itnim.c6
-rw-r--r--drivers/scsi/bfa/bfa_log.c4
-rw-r--r--drivers/scsi/bfa/bfa_port_priv.h4
-rw-r--r--drivers/scsi/bfa/bfa_rport.c6
-rw-r--r--drivers/scsi/bfa/bfa_tskim.c9
-rw-r--r--drivers/scsi/bfa/bfa_uf.c2
-rw-r--r--drivers/scsi/bfa/bfad.c6
-rw-r--r--drivers/scsi/bfa/bfad_fwimg.c8
-rw-r--r--drivers/scsi/bfa/bfad_im.c2
-rw-r--r--drivers/scsi/bfa/bfad_im_compat.h2
-rw-r--r--drivers/scsi/bfa/bfad_intr.c3
-rw-r--r--drivers/scsi/bfa/fabric.c18
-rw-r--r--drivers/scsi/bfa/fcbuild.c190
-rw-r--r--drivers/scsi/bfa/fcbuild.h6
-rw-r--r--drivers/scsi/bfa/fcpim.c7
-rw-r--r--drivers/scsi/bfa/fcs.h2
-rw-r--r--drivers/scsi/bfa/fdmi.c8
-rw-r--r--drivers/scsi/bfa/include/aen/bfa_aen.h2
-rw-r--r--drivers/scsi/bfa/include/bfa.h10
-rw-r--r--drivers/scsi/bfa/include/bfa_svc.h8
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi.h12
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi_ioc.h2
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi_lps.h4
-rw-r--r--drivers/scsi/bfa/include/bfi/bfi_rport.h8
-rw-r--r--drivers/scsi/bfa/include/cs/bfa_checksum.h6
-rw-r--r--drivers/scsi/bfa/include/cs/bfa_sm.h6
-rw-r--r--drivers/scsi/bfa/include/cs/bfa_trc.h2
-rw-r--r--drivers/scsi/bfa/include/defs/bfa_defs_pport.h2
-rw-r--r--drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h2
-rw-r--r--drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h2
-rw-r--r--drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h17
-rw-r--r--drivers/scsi/bfa/include/protocol/ct.h14
-rw-r--r--drivers/scsi/bfa/include/protocol/fc.h22
-rw-r--r--drivers/scsi/bfa/loop.c233
-rw-r--r--drivers/scsi/bfa/lport_api.c15
-rw-r--r--drivers/scsi/bfa/ns.c5
-rw-r--r--drivers/scsi/bfa/plog.c2
-rw-r--r--drivers/scsi/bfa/rport_ftrs.c28
-rw-r--r--drivers/scsi/bfa/vfapi.c2
-rw-r--r--drivers/scsi/bfa/vport.c20
-rw-r--r--drivers/scsi/bnx2i/bnx2i.h1
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c12
-rw-r--r--drivers/scsi/constants.c1
-rw-r--r--drivers/scsi/cxgb3i/cxgb3i_iscsi.c2
-rw-r--r--drivers/scsi/dc395x.c2
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c28
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c139
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c7
-rw-r--r--drivers/scsi/device_handler/scsi_dh_hp_sw.c94
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c115
-rw-r--r--drivers/scsi/dmx3191d.c2
-rw-r--r--drivers/scsi/fcoe/fcoe.c1376
-rw-r--r--drivers/scsi/fcoe/fcoe.h82
-rw-r--r--drivers/scsi/fcoe/libfcoe.c433
-rw-r--r--drivers/scsi/fnic/fnic.h25
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c499
-rw-r--r--drivers/scsi/fnic/fnic_isr.c18
-rw-r--r--drivers/scsi/fnic/fnic_main.c104
-rw-r--r--drivers/scsi/fnic/fnic_res.c5
-rw-r--r--drivers/scsi/fnic/fnic_res.h54
-rw-r--r--drivers/scsi/fnic/fnic_scsi.c96
-rw-r--r--drivers/scsi/fnic/vnic_scsi.h1
-rw-r--r--drivers/scsi/hosts.c13
-rw-r--r--drivers/scsi/hptiop.c7
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c335
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h12
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c7
-rw-r--r--drivers/scsi/ipr.c7
-rw-r--r--drivers/scsi/iscsi_tcp.c2
-rw-r--r--drivers/scsi/libfc/Makefile4
-rw-r--r--drivers/scsi/libfc/fc_disc.c85
-rw-r--r--drivers/scsi/libfc/fc_elsct.c79
-rw-r--r--drivers/scsi/libfc/fc_exch.c932
-rw-r--r--drivers/scsi/libfc/fc_fcp.c1044
-rw-r--r--drivers/scsi/libfc/fc_frame.c13
-rw-r--r--drivers/scsi/libfc/fc_libfc.c134
-rw-r--r--drivers/scsi/libfc/fc_libfc.h112
-rw-r--r--drivers/scsi/libfc/fc_lport.c855
-rw-r--r--drivers/scsi/libfc/fc_npiv.c161
-rw-r--r--drivers/scsi/libfc/fc_rport.c409
-rw-r--r--drivers/scsi/libiscsi.c389
-rw-r--r--drivers/scsi/libiscsi_tcp.c2
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c6
-rw-r--r--drivers/scsi/lpfc/lpfc.h17
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c266
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c5
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h14
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c53
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c10
-rw-r--r--drivers/scsi/lpfc/lpfc_disc.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c80
-rwxr-xr-x[-rw-r--r--]drivers/scsi/lpfc/lpfc_hbadisc.c129
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h34
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h131
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c577
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c35
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c7
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c394
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c1250
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h27
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h34
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c4
-rw-r--r--drivers/scsi/megaraid.h2
-rw-r--r--drivers/scsi/megaraid/mbox_defs.h2
-rw-r--r--drivers/scsi/megaraid/megaraid_mbox.c9
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.c738
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h149
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2.h5
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h274
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_ioc.h18
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_raid.h14
-rw-r--r--drivers/scsi/mpt2sas/mpi/mpi2_tool.h16
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c152
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h37
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c197
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.h4
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c821
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c86
-rw-r--r--drivers/scsi/ncr53c8xx.c2
-rw-r--r--drivers/scsi/nsp32.c2
-rw-r--r--drivers/scsi/osd/osd_initiator.c121
-rw-r--r--drivers/scsi/osd/osd_uld.c260
-rw-r--r--drivers/scsi/pcmcia/aha152x_stub.c42
-rw-r--r--drivers/scsi/pcmcia/fdomain_stub.c44
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c8
-rw-r--r--drivers/scsi/pcmcia/qlogic_stub.c45
-rw-r--r--drivers/scsi/pcmcia/sym53c500_cs.c42
-rw-r--r--drivers/scsi/pm8001/Makefile12
-rw-r--r--drivers/scsi/pm8001/pm8001_chips.h89
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.c573
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.h67
-rw-r--r--drivers/scsi/pm8001/pm8001_defs.h112
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c4458
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.h1030
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c891
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c1103
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.h481
-rw-r--r--drivers/scsi/pmcraid.c13
-rw-r--r--drivers/scsi/pmcraid.h6
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c78
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h9
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c12
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c144
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c50
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c67
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/qlogicpti.h2
-rw-r--r--drivers/scsi/scsi.c10
-rw-r--r--drivers/scsi/scsi_debug.c351
-rw-r--r--drivers/scsi/scsi_devinfo.c13
-rw-r--r--drivers/scsi/scsi_error.c65
-rw-r--r--drivers/scsi/scsi_ioctl.c3
-rw-r--r--drivers/scsi/scsi_lib.c11
-rw-r--r--drivers/scsi/scsi_lib_dma.c4
-rw-r--r--drivers/scsi/scsi_netlink.c2
-rw-r--r--drivers/scsi/scsi_scan.c3
-rw-r--r--drivers/scsi/scsi_sysctl.c11
-rw-r--r--drivers/scsi/scsi_sysfs.c44
-rw-r--r--drivers/scsi/scsi_transport_fc.c99
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c13
-rw-r--r--drivers/scsi/scsi_transport_sas.c6
-rw-r--r--drivers/scsi/st.c3
-rw-r--r--drivers/scsi/stex.c282
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c2
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.c2
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_hipd.h2
-rw-r--r--drivers/scsi/vmw_pvscsi.c1407
-rw-r--r--drivers/scsi/vmw_pvscsi.h397
-rw-r--r--drivers/scsi/wd7000.c2
-rw-r--r--drivers/serial/8250_pnp.c4
-rw-r--r--drivers/serial/Kconfig15
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/apbuart.c710
-rw-r--r--drivers/serial/apbuart.h64
-rw-r--r--drivers/serial/mcf.c2
-rw-r--r--drivers/serial/pmac_zilog.h2
-rw-r--r--drivers/serial/s3c2410.c2
-rw-r--r--drivers/serial/s3c2412.c2
-rw-r--r--drivers/serial/s3c2440.c2
-rw-r--r--drivers/serial/s3c24a0.c2
-rw-r--r--drivers/serial/samsung.c2
-rw-r--r--drivers/serial/samsung.h2
-rw-r--r--drivers/serial/serial_cs.c143
-rw-r--r--drivers/serial/sh-sci.c59
-rw-r--r--drivers/serial/sh-sci.h2
-rw-r--r--drivers/serial/ucc_uart.c2
-rw-r--r--drivers/sh/Makefile1
-rw-r--r--drivers/sh/intc.c123
-rw-r--r--drivers/sh/maple/maple.c4
-rw-r--r--drivers/sh/pfc.c577
-rw-r--r--drivers/spi/omap2_mcspi.c4
-rw-r--r--drivers/spi/omap_uwire.c10
-rw-r--r--drivers/spi/spidev.c5
-rw-r--r--drivers/ssb/driver_pcicore.c4
-rw-r--r--drivers/ssb/main.c126
-rw-r--r--drivers/ssb/pcmcia.c232
-rw-r--r--drivers/ssb/scan.c2
-rw-r--r--drivers/ssb/sprom.c30
-rw-r--r--drivers/ssb/ssb_private.h12
-rw-r--r--drivers/staging/Kconfig8
-rw-r--r--drivers/staging/Makefile5
-rw-r--r--drivers/staging/arlan/Kconfig15
-rw-r--r--drivers/staging/arlan/Makefile3
-rw-r--r--drivers/staging/arlan/TODO7
-rw-r--r--drivers/staging/arlan/arlan-main.c (renamed from drivers/net/wireless/arlan-main.c)0
-rw-r--r--drivers/staging/arlan/arlan-proc.c (renamed from drivers/net/wireless/arlan-proc.c)245
-rw-r--r--drivers/staging/arlan/arlan.h (renamed from drivers/net/wireless/arlan.h)0
-rw-r--r--drivers/staging/comedi/drivers/cb_das16_cs.c221
-rw-r--r--drivers/staging/comedi/drivers/das08_cs.c203
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_700.c237
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_dio24.c236
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_cs.c225
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_cs.c127
-rw-r--r--drivers/staging/comedi/drivers/quatech_daqp_cs.c219
-rw-r--r--drivers/staging/go7007/Makefile4
-rw-r--r--drivers/staging/go7007/go7007-driver.c30
-rw-r--r--drivers/staging/go7007/go7007-priv.h8
-rw-r--r--drivers/staging/go7007/go7007-usb.c6
-rw-r--r--drivers/staging/go7007/go7007-v4l2.c109
-rw-r--r--drivers/staging/go7007/s2250-board.c558
-rw-r--r--drivers/staging/go7007/s2250-loader.c12
-rw-r--r--drivers/staging/netwave/Kconfig11
-rw-r--r--drivers/staging/netwave/Makefile1
-rw-r--r--drivers/staging/netwave/TODO7
-rw-r--r--drivers/staging/netwave/netwave_cs.c (renamed from drivers/net/wireless/netwave_cs.c)95
-rw-r--r--drivers/staging/otus/80211core/pub_zfi.h2
-rw-r--r--drivers/staging/otus/zdcompat.h2
-rw-r--r--drivers/staging/pohmelfs/inode.c10
-rw-r--r--drivers/staging/rtl8187se/Kconfig3
-rw-r--r--drivers/staging/rtl8187se/r8180.h2
-rw-r--r--drivers/staging/rtl8192e/Kconfig3
-rw-r--r--drivers/staging/rtl8192su/r8192S_phyreg.h2
-rw-r--r--drivers/staging/strip/Kconfig22
-rw-r--r--drivers/staging/strip/Makefile1
-rw-r--r--drivers/staging/strip/TODO7
-rw-r--r--drivers/staging/strip/strip.c (renamed from drivers/net/wireless/strip.c)17
-rw-r--r--drivers/staging/vt6655/Kconfig4
-rw-r--r--drivers/staging/vt6656/Kconfig4
-rw-r--r--drivers/staging/wavelan/Kconfig38
-rw-r--r--drivers/staging/wavelan/Makefile2
-rw-r--r--drivers/staging/wavelan/TODO7
-rw-r--r--drivers/staging/wavelan/i82586.h (renamed from drivers/net/wireless/i82586.h)0
-rw-r--r--drivers/staging/wavelan/wavelan.c (renamed from drivers/net/wireless/wavelan.c)0
-rw-r--r--drivers/staging/wavelan/wavelan.h (renamed from drivers/net/wireless/wavelan.h)0
-rw-r--r--drivers/staging/wavelan/wavelan.p.h (renamed from drivers/net/wireless/wavelan.p.h)0
-rw-r--r--drivers/staging/wavelan/wavelan_cs.c (renamed from drivers/net/wireless/wavelan_cs.c)37
-rw-r--r--drivers/staging/wavelan/wavelan_cs.h (renamed from drivers/net/wireless/wavelan_cs.h)0
-rw-r--r--drivers/staging/wavelan/wavelan_cs.p.h (renamed from drivers/net/wireless/wavelan_cs.p.h)2
-rw-r--r--drivers/telephony/ixj.c4
-rw-r--r--drivers/telephony/ixj_pcmcia.c36
-rw-r--r--drivers/usb/atm/ueagle-atm.c2
-rw-r--r--drivers/usb/class/usbtmc.c2
-rw-r--r--drivers/usb/core/message.c2
-rw-r--r--drivers/usb/gadget/f_acm.c2
-rw-r--r--drivers/usb/gadget/omap_udc.c25
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/fhci-sched.c2
-rw-r--r--drivers/usb/host/ohci-omap.c6
-rw-r--r--drivers/usb/host/sl811_cs.c49
-rw-r--r--drivers/usb/musb/omap2430.c2
-rw-r--r--drivers/usb/musb/omap2430.h2
-rw-r--r--drivers/usb/musb/tusb6010_omap.c4
-rw-r--r--drivers/usb/otg/isp1301_omap.c4
-rw-r--r--drivers/usb/wusbcore/crypto.c2
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c4
-rw-r--r--drivers/uwb/i1480/dfu/usb.c2
-rw-r--r--drivers/uwb/neh.c2
-rw-r--r--drivers/uwb/wlp/txrx.c2
-rw-r--r--drivers/video/Kconfig7
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/atafb.c3
-rw-r--r--drivers/video/atmel_lcdfb.c2
-rw-r--r--drivers/video/aty/atyfb_base.c4
-rw-r--r--drivers/video/backlight/atmel-pwm-bl.c2
-rw-r--r--drivers/video/backlight/da903x_bl.c7
-rw-r--r--drivers/video/backlight/omap1_bl.c4
-rw-r--r--drivers/video/backlight/tdo24m.c1
-rw-r--r--drivers/video/backlight/tosa_lcd.c2
-rw-r--r--drivers/video/broadsheetfb.c2
-rw-r--r--drivers/video/console/sticore.c2
-rw-r--r--drivers/video/console/vgacon.c5
-rw-r--r--drivers/video/fb_defio.c6
-rw-r--r--drivers/video/gbefb.c2
-rw-r--r--drivers/video/hecubafb.c2
-rw-r--r--drivers/video/metronomefb.c2
-rw-r--r--drivers/video/omap/Kconfig5
-rw-r--r--drivers/video/omap/Makefile1
-rw-r--r--drivers/video/omap/blizzard.c6
-rw-r--r--drivers/video/omap/dispc.c45
-rw-r--r--drivers/video/omap/hwa742.c7
-rw-r--r--drivers/video/omap/lcd_2430sdp.c5
-rw-r--r--drivers/video/omap/lcd_ams_delta.c9
-rw-r--r--drivers/video/omap/lcd_apollon.c5
-rw-r--r--drivers/video/omap/lcd_h3.c2
-rw-r--r--drivers/video/omap/lcd_h4.c2
-rw-r--r--drivers/video/omap/lcd_htcherald.c130
-rw-r--r--drivers/video/omap/lcd_inn1510.c4
-rw-r--r--drivers/video/omap/lcd_inn1610.c2
-rw-r--r--drivers/video/omap/lcd_ldp.c5
-rw-r--r--drivers/video/omap/lcd_mipid.c9
-rw-r--r--drivers/video/omap/lcd_omap2evm.c5
-rw-r--r--drivers/video/omap/lcd_omap3beagle.c6
-rw-r--r--drivers/video/omap/lcd_omap3evm.c5
-rw-r--r--drivers/video/omap/lcd_osk.c4
-rw-r--r--drivers/video/omap/lcd_overo.c5
-rw-r--r--drivers/video/omap/lcd_palmte.c4
-rw-r--r--drivers/video/omap/lcd_palmtt.c2
-rw-r--r--drivers/video/omap/lcd_palmz71.c2
-rw-r--r--drivers/video/omap/lcdc.c5
-rw-r--r--drivers/video/omap/omapfb.h227
-rw-r--r--drivers/video/omap/omapfb_main.c4
-rw-r--r--drivers/video/omap/rfbi.c3
-rw-r--r--drivers/video/omap/sossi.c5
-rw-r--r--drivers/video/omap2/Kconfig9
-rw-r--r--drivers/video/omap2/Makefile6
-rw-r--r--drivers/video/omap2/displays/Kconfig22
-rw-r--r--drivers/video/omap2/displays/Makefile4
-rw-r--r--drivers/video/omap2/displays/panel-generic.c104
-rw-r--r--drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c153
-rw-r--r--drivers/video/omap2/displays/panel-taal.c1003
-rw-r--r--drivers/video/omap2/dss/Kconfig89
-rw-r--r--drivers/video/omap2/dss/Makefile6
-rw-r--r--drivers/video/omap2/dss/core.c919
-rw-r--r--drivers/video/omap2/dss/dispc.c3091
-rw-r--r--drivers/video/omap2/dss/display.c671
-rw-r--r--drivers/video/omap2/dss/dpi.c399
-rw-r--r--drivers/video/omap2/dss/dsi.c3710
-rw-r--r--drivers/video/omap2/dss/dss.c596
-rw-r--r--drivers/video/omap2/dss/dss.h370
-rw-r--r--drivers/video/omap2/dss/manager.c1487
-rw-r--r--drivers/video/omap2/dss/overlay.c680
-rw-r--r--drivers/video/omap2/dss/rfbi.c1309
-rw-r--r--drivers/video/omap2/dss/sdi.c277
-rw-r--r--drivers/video/omap2/dss/venc.c797
-rw-r--r--drivers/video/omap2/omapfb/Kconfig37
-rw-r--r--drivers/video/omap2/omapfb/Makefile2
-rw-r--r--drivers/video/omap2/omapfb/omapfb-ioctl.c755
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c2261
-rw-r--r--drivers/video/omap2/omapfb/omapfb-sysfs.c507
-rw-r--r--drivers/video/omap2/omapfb/omapfb.h146
-rw-r--r--drivers/video/omap2/vram.c655
-rw-r--r--drivers/video/omap2/vrfb.c315
-rw-r--r--drivers/video/pxa168fb.c1
-rw-r--r--drivers/video/pxafb.c23
-rw-r--r--drivers/video/sgivwfb.c4
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c32
-rw-r--r--drivers/video/stifb.c4
-rw-r--r--drivers/video/tdfxfb.c2
-rw-r--r--drivers/video/via/dvi.c4
-rw-r--r--drivers/video/vt8623fb.c2
-rw-r--r--drivers/video/xen-fbfront.c2
-rw-r--r--drivers/watchdog/coh901327_wdt.c2
-rw-r--r--drivers/watchdog/iTCO_wdt.c68
-rw-r--r--drivers/watchdog/machzwd.c2
-rw-r--r--drivers/watchdog/omap_wdt.c2
-rw-r--r--drivers/watchdog/riowd.c6
-rw-r--r--drivers/watchdog/s3c2410_wdt.c89
-rw-r--r--drivers/watchdog/sb_wdog.c4
-rw-r--r--drivers/watchdog/wdrtas.c6
-rw-r--r--drivers/xen/balloon.c38
-rw-r--r--drivers/xen/cpu_hotplug.c2
-rw-r--r--drivers/xen/events.c3
-rw-r--r--drivers/xen/manage.c37
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c48
-rw-r--r--drivers/zorro/zorro-driver.c1
2048 files changed, 186987 insertions, 68701 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 48bbdbe43e6..26e434ad373 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -1,5 +1,3 @@
-# drivers/Kconfig
-
menu "Device Drivers"
source "drivers/base/Kconfig"
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index 28ccdbc05ac..3597d73f28f 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -537,7 +537,7 @@ static int __init acpi_memory_device_init(void)
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- acpi_memory_register_notify_handler,
+ acpi_memory_register_notify_handler, NULL,
NULL, NULL);
if (ACPI_FAILURE(status)) {
@@ -561,7 +561,7 @@ static void __exit acpi_memory_device_exit(void)
*/
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- acpi_memory_deregister_notify_handler,
+ acpi_memory_deregister_notify_handler, NULL,
NULL, NULL);
if (ACPI_FAILURE(status))
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
index e7973bc1684..7423052ece5 100644
--- a/drivers/acpi/acpica/Makefile
+++ b/drivers/acpi/acpica/Makefile
@@ -28,7 +28,7 @@ acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o
acpi-y += nsaccess.o nsload.o nssearch.o nsxfeval.o \
nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \
nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \
- nsparse.o nspredef.o nsrepair.o
+ nsparse.o nspredef.o nsrepair.o nsrepair2.o
acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index 3acd9c6760e..7d9ba6e5755 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -341,6 +341,7 @@
#define ACPI_ERROR_NAMESPACE(s, e) acpi_ns_report_error (AE_INFO, s, e);
#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ns_report_method_error (AE_INFO, s, n, p, e);
#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist
+#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist
#else
@@ -349,6 +350,8 @@
#define ACPI_ERROR_NAMESPACE(s, e)
#define ACPI_ERROR_METHOD(s, n, p, e)
#define ACPI_WARN_PREDEFINED(plist)
+#define ACPI_INFO_PREDEFINED(plist)
+
#endif /* ACPI_NO_ERROR_MESSAGES */
/*
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 09a2764c734..ab83919dda6 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -104,7 +104,8 @@ acpi_ns_walk_namespace(acpi_object_type type,
acpi_handle start_object,
u32 max_depth,
u32 flags,
- acpi_walk_callback user_function,
+ acpi_walk_callback pre_order_visit,
+ acpi_walk_callback post_order_visit,
void *context, void **return_value);
struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
@@ -272,7 +273,8 @@ acpi_ns_get_attached_data(struct acpi_namespace_node *node,
acpi_object_handler handler, void **data);
/*
- * nsrepair - return object repair for predefined methods/objects
+ * nsrepair - General return object repair for all
+ * predefined methods/objects
*/
acpi_status
acpi_ns_repair_object(struct acpi_predefined_data *data,
@@ -285,6 +287,16 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
union acpi_operand_object **obj_desc_ptr);
/*
+ * nsrepair2 - Return object repair for specific
+ * predefined methods/objects
+ */
+acpi_status
+acpi_ns_complex_repairs(struct acpi_predefined_data *data,
+ struct acpi_namespace_node *node,
+ acpi_status validate_status,
+ union acpi_operand_object **return_object_ptr);
+
+/*
* nssearch - Namespace searching and entry
*/
acpi_status
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 863a264b829..3a451a21a3f 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -386,6 +386,8 @@ u8 acpi_ut_valid_internal_object(void *object);
union acpi_operand_object *acpi_ut_create_package_object(u32 count);
+union acpi_operand_object *acpi_ut_create_integer_object(u64 value);
+
union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size);
union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size);
@@ -481,6 +483,11 @@ acpi_ut_predefined_warning(const char *module_name,
char *pathname,
u8 node_flags, const char *format, ...);
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+ u32 line_number,
+ char *pathname, u8 node_flags, const char *format, ...);
+
/* Values for Base above (16=Hex, 10=Decimal) */
#define ACPI_ANY_BASE 0
diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c
index 3aae13f30c5..f23fa0be6fc 100644
--- a/drivers/acpi/acpica/dsinit.c
+++ b/drivers/acpi/acpica/dsinit.c
@@ -192,7 +192,7 @@ acpi_ds_initialize_objects(u32 table_index,
status =
acpi_ns_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX,
ACPI_NS_WALK_UNLOCK, acpi_ds_init_one_object,
- &info, NULL);
+ NULL, &info, NULL);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
}
diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c
index 7d077bb2f52..0ba19f84ad8 100644
--- a/drivers/acpi/acpica/dsmthdat.c
+++ b/drivers/acpi/acpica/dsmthdat.c
@@ -409,13 +409,11 @@ acpi_ds_method_data_get_value(u8 type,
/* If slack enabled, init the local_x/arg_x to an Integer of value zero */
if (acpi_gbl_enable_interpreter_slack) {
- object =
- acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ object = acpi_ut_create_integer_object((u64) 0);
if (!object) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
- object->integer.value = 0;
node->object = object;
}
diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c
index 507e1f0bbdf..9bc1ba07634 100644
--- a/drivers/acpi/acpica/dsobject.c
+++ b/drivers/acpi/acpica/dsobject.c
@@ -486,7 +486,7 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
*
* Note: technically, this is an error, from ACPI spec: "It is an error
* for NumElements to be less than the number of elements in the
- * PackageList". However, we just print an error message and
+ * PackageList". However, we just print a message and
* no exception is returned. This provides Windows compatibility. Some
* BIOSs will alter the num_elements on the fly, creating this type
* of ill-formed package object.
@@ -510,9 +510,9 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
arg = arg->common.next;
}
- ACPI_WARNING((AE_INFO,
- "Package List length (0x%X) larger than NumElements count (0x%X), truncated\n",
- i, element_count));
+ ACPI_INFO((AE_INFO,
+ "Actual Package length (0x%X) is larger than NumElements field (0x%X), truncated\n",
+ i, element_count));
} else if (i < element_count) {
/*
* Arg list (elements) was exhausted, but we did not reach num_elements count.
diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c
index 6de3a99d4cd..10fc7851784 100644
--- a/drivers/acpi/acpica/dswload.c
+++ b/drivers/acpi/acpica/dswload.c
@@ -639,26 +639,42 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
break;
case AML_SCOPE_OP:
- /*
- * The Path is an object reference to an existing object.
- * Don't enter the name into the namespace, but look it up
- * for use later.
- */
- status =
- acpi_ns_lookup(walk_state->scope_info, buffer_ptr,
- object_type, ACPI_IMODE_EXECUTE,
- ACPI_NS_SEARCH_PARENT, walk_state, &(node));
- if (ACPI_FAILURE(status)) {
-#ifdef ACPI_ASL_COMPILER
- if (status == AE_NOT_FOUND) {
- status = AE_OK;
- } else {
- ACPI_ERROR_NAMESPACE(buffer_ptr, status);
+
+ /* Special case for Scope(\) -> refers to the Root node */
+
+ if (op && (op->named.node == acpi_gbl_root_node)) {
+ node = op->named.node;
+
+ status =
+ acpi_ds_scope_stack_push(node, object_type,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
}
+ } else {
+ /*
+ * The Path is an object reference to an existing object.
+ * Don't enter the name into the namespace, but look it up
+ * for use later.
+ */
+ status =
+ acpi_ns_lookup(walk_state->scope_info, buffer_ptr,
+ object_type, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT, walk_state,
+ &(node));
+ if (ACPI_FAILURE(status)) {
+#ifdef ACPI_ASL_COMPILER
+ if (status == AE_NOT_FOUND) {
+ status = AE_OK;
+ } else {
+ ACPI_ERROR_NAMESPACE(buffer_ptr,
+ status);
+ }
#else
- ACPI_ERROR_NAMESPACE(buffer_ptr, status);
+ ACPI_ERROR_NAMESPACE(buffer_ptr, status);
#endif
- return_ACPI_STATUS(status);
+ return_ACPI_STATUS(status);
+ }
}
/*
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
index a60aaa7635f..24792090018 100644
--- a/drivers/acpi/acpica/evgpeblk.c
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -945,8 +945,8 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device,
ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
- acpi_ev_save_method_info, gpe_block,
- NULL);
+ acpi_ev_save_method_info, NULL,
+ gpe_block, NULL);
/* Return the new block */
@@ -1022,8 +1022,8 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
status =
acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
- acpi_ev_match_prw_and_gpe, &gpe_info,
- NULL);
+ acpi_ev_match_prw_and_gpe, NULL,
+ &gpe_info, NULL);
}
/*
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 98c7f9c6265..0bc807c33a5 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -51,6 +51,10 @@
ACPI_MODULE_NAME("evregion")
/* Local prototypes */
+static u8
+acpi_ev_has_default_handler(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id);
+
static acpi_status
acpi_ev_reg_run(acpi_handle obj_handle,
u32 level, void *context, void **return_value);
@@ -142,6 +146,50 @@ acpi_status acpi_ev_install_region_handlers(void)
/*******************************************************************************
*
+ * FUNCTION: acpi_ev_has_default_handler
+ *
+ * PARAMETERS: Node - Namespace node for the device
+ * space_id - The address space ID
+ *
+ * RETURN: TRUE if default handler is installed, FALSE otherwise
+ *
+ * DESCRIPTION: Check if the default handler is installed for the requested
+ * space ID.
+ *
+ ******************************************************************************/
+
+static u8
+acpi_ev_has_default_handler(struct acpi_namespace_node *node,
+ acpi_adr_space_type space_id)
+{
+ union acpi_operand_object *obj_desc;
+ union acpi_operand_object *handler_obj;
+
+ /* Must have an existing internal object */
+
+ obj_desc = acpi_ns_get_attached_object(node);
+ if (obj_desc) {
+ handler_obj = obj_desc->device.handler;
+
+ /* Walk the linked list of handlers for this object */
+
+ while (handler_obj) {
+ if (handler_obj->address_space.space_id == space_id) {
+ if (handler_obj->address_space.handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) {
+ return (TRUE);
+ }
+ }
+
+ handler_obj = handler_obj->address_space.next;
+ }
+ }
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ev_initialize_op_regions
*
* PARAMETERS: None
@@ -169,12 +217,18 @@ acpi_status acpi_ev_initialize_op_regions(void)
for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) {
/*
- * TBD: Make sure handler is the DEFAULT handler, otherwise
- * _REG will have already been run.
+ * Make sure the installed handler is the DEFAULT handler. If not the
+ * default, the _REG methods will have already been run (when the
+ * handler was installed)
*/
- status = acpi_ev_execute_reg_methods(acpi_gbl_root_node,
- acpi_gbl_default_address_spaces
- [i]);
+ if (acpi_ev_has_default_handler(acpi_gbl_root_node,
+ acpi_gbl_default_address_spaces
+ [i])) {
+ status =
+ acpi_ev_execute_reg_methods(acpi_gbl_root_node,
+ acpi_gbl_default_address_spaces
+ [i]);
+ }
}
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
@@ -235,23 +289,20 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)
* connection status 1 for connecting the handler, 0 for disconnecting
* the handler (Passed as a parameter)
*/
- args[0] = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ args[0] =
+ acpi_ut_create_integer_object((u64) region_obj->region.space_id);
if (!args[0]) {
status = AE_NO_MEMORY;
goto cleanup1;
}
- args[1] = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ args[1] = acpi_ut_create_integer_object((u64) function);
if (!args[1]) {
status = AE_NO_MEMORY;
goto cleanup2;
}
- /* Setup the parameter objects */
-
- args[0]->integer.value = region_obj->region.space_id;
- args[1]->integer.value = function;
- args[2] = NULL;
+ args[2] = NULL; /* Terminate list */
/* Execute the method, no return value */
@@ -971,8 +1022,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
*/
status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
ACPI_NS_WALK_UNLOCK,
- acpi_ev_install_handler, handler_obj,
- NULL);
+ acpi_ev_install_handler, NULL,
+ handler_obj, NULL);
unlock_and_exit:
return_ACPI_STATUS(status);
@@ -1008,7 +1059,7 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
*/
status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run,
- &space_id, NULL);
+ NULL, &space_id, NULL);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c
index 24afef81af3..46adfa541cb 100644
--- a/drivers/acpi/acpica/exconfig.c
+++ b/drivers/acpi/acpica/exconfig.c
@@ -170,14 +170,12 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
/* Table not found, return an Integer=0 and AE_OK */
- ddb_handle = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ ddb_handle = acpi_ut_create_integer_object((u64) 0);
if (!ddb_handle) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
- ddb_handle->integer.value = 0;
*return_desc = ddb_handle;
-
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c
index 37d0d39e60a..51d5f224f9f 100644
--- a/drivers/acpi/acpica/exconvrt.c
+++ b/drivers/acpi/acpica/exconvrt.c
@@ -167,7 +167,7 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
/* Create a new integer */
- return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ return_desc = acpi_ut_create_integer_object(result);
if (!return_desc) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
@@ -177,7 +177,6 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
/* Save the Result */
- return_desc->integer.value = result;
acpi_ex_truncate_for32bit_table(return_desc);
*result_desc = return_desc;
return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
index 0b33d6c887b..1588a2d660e 100644
--- a/drivers/acpi/acpica/exfield.c
+++ b/drivers/acpi/acpica/exfield.c
@@ -162,13 +162,12 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
} else {
/* Field will fit within an Integer (normal case) */
- buffer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ buffer_desc = acpi_ut_create_integer_object((u64) 0);
if (!buffer_desc) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
length = acpi_gbl_integer_byte_width;
- buffer_desc->integer.value = 0;
buffer = &buffer_desc->integer.value;
}
diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c
index 9635d21e568..752fe48b2d2 100644
--- a/drivers/acpi/acpica/exoparg1.c
+++ b/drivers/acpi/acpica/exoparg1.c
@@ -100,12 +100,12 @@ acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state)
/* Create a return object of type Integer */
- return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ return_desc =
+ acpi_ut_create_integer_object(acpi_os_get_timer());
if (!return_desc) {
status = AE_NO_MEMORY;
goto cleanup;
}
- return_desc->integer.value = acpi_os_get_timer();
break;
default: /* Unknown opcode */
@@ -599,7 +599,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
switch (walk_state->opcode) {
case AML_LNOT_OP: /* LNot (Operand) */
- return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ return_desc = acpi_ut_create_integer_object((u64) 0);
if (!return_desc) {
status = AE_NO_MEMORY;
goto cleanup;
@@ -702,13 +702,11 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
/* Allocate a descriptor to hold the type. */
- return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ return_desc = acpi_ut_create_integer_object((u64) type);
if (!return_desc) {
status = AE_NO_MEMORY;
goto cleanup;
}
-
- return_desc->integer.value = type;
break;
case AML_SIZE_OF_OP: /* size_of (source_object) */
@@ -777,13 +775,11 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
* Now that we have the size of the object, create a result
* object to hold the value
*/
- return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ return_desc = acpi_ut_create_integer_object(value);
if (!return_desc) {
status = AE_NO_MEMORY;
goto cleanup;
}
-
- return_desc->integer.value = value;
break;
case AML_REF_OF_OP: /* ref_of (source_object) */
@@ -946,24 +942,24 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
* NOTE: index into a buffer is NOT a pointer to a
* sub-buffer of the main buffer, it is only a pointer to a
* single element (byte) of the buffer!
+ *
+ * Since we are returning the value of the buffer at the
+ * indexed location, we don't need to add an additional
+ * reference to the buffer itself.
*/
return_desc =
- acpi_ut_create_internal_object
- (ACPI_TYPE_INTEGER);
+ acpi_ut_create_integer_object((u64)
+ temp_desc->
+ buffer.
+ pointer
+ [operand
+ [0]->
+ reference.
+ value]);
if (!return_desc) {
status = AE_NO_MEMORY;
goto cleanup;
}
-
- /*
- * Since we are returning the value of the buffer at the
- * indexed location, we don't need to add an additional
- * reference to the buffer itself.
- */
- return_desc->integer.value =
- temp_desc->buffer.
- pointer[operand[0]->reference.
- value];
break;
case ACPI_TYPE_PACKAGE:
diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c
index ae43f7670a6..295542e6bd5 100644
--- a/drivers/acpi/acpica/exoparg6.c
+++ b/drivers/acpi/acpica/exoparg6.c
@@ -253,18 +253,15 @@ acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state * walk_state)
}
/* Create an integer for the return value */
+ /* Default return value is ACPI_INTEGER_MAX if no match found */
- return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ return_desc = acpi_ut_create_integer_object(ACPI_INTEGER_MAX);
if (!return_desc) {
status = AE_NO_MEMORY;
goto cleanup;
}
- /* Default return value if no match found */
-
- return_desc->integer.value = ACPI_INTEGER_MAX;
-
/*
* Examine each element until a match is found. Both match conditions
* must be satisfied for a match to occur. Within the loop,
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index 2bad613db73..2deb986861c 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -634,8 +634,8 @@ acpi_ns_dump_objects(acpi_object_type type,
(void)acpi_ns_walk_namespace(type, start_handle, max_depth,
ACPI_NS_WALK_NO_UNLOCK |
ACPI_NS_WALK_TEMP_NODES,
- acpi_ns_dump_one_object, (void *)&info,
- NULL);
+ acpi_ns_dump_one_object, NULL,
+ (void *)&info, NULL);
}
#endif /* ACPI_FUTURE_USAGE */
diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c
index 0fe87f1aef1..36be7f0e97e 100644
--- a/drivers/acpi/acpica/nsdumpdv.c
+++ b/drivers/acpi/acpica/nsdumpdv.c
@@ -131,7 +131,8 @@ void acpi_ns_dump_root_devices(void)
status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, sys_bus_handle,
ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
- acpi_ns_dump_one_device, NULL, NULL);
+ acpi_ns_dump_one_device, NULL, NULL,
+ NULL);
}
#endif
diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c
index 846d1132feb..f771e978c40 100644
--- a/drivers/acpi/acpica/nseval.c
+++ b/drivers/acpi/acpica/nseval.c
@@ -366,33 +366,49 @@ static void
acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
struct acpi_evaluate_info *info)
{
- union acpi_operand_object *root_obj;
+ union acpi_operand_object *parent_obj;
+ struct acpi_namespace_node *parent_node;
+ acpi_object_type type;
acpi_status status;
ACPI_FUNCTION_TRACE(ns_exec_module_code);
+ /*
+ * Get the parent node. We cheat by using the next_object field
+ * of the method object descriptor.
+ */
+ parent_node = ACPI_CAST_PTR(struct acpi_namespace_node,
+ method_obj->method.next_object);
+ type = acpi_ns_get_type(parent_node);
+
+ /* Must clear next_object (acpi_ns_attach_object needs the field) */
+
+ method_obj->method.next_object = NULL;
+
/* Initialize the evaluation information block */
ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info));
- info->prefix_node = acpi_gbl_root_node;
+ info->prefix_node = parent_node;
/*
- * Get the currently attached root object. Add a reference, because the
+ * Get the currently attached parent object. Add a reference, because the
* ref count will be decreased when the method object is installed to
- * the root node.
+ * the parent node.
*/
- root_obj = acpi_ns_get_attached_object(acpi_gbl_root_node);
- acpi_ut_add_reference(root_obj);
+ parent_obj = acpi_ns_get_attached_object(parent_node);
+ if (parent_obj) {
+ acpi_ut_add_reference(parent_obj);
+ }
- /* Install the method (module-level code) in the root node */
+ /* Install the method (module-level code) in the parent node */
- status = acpi_ns_attach_object(acpi_gbl_root_node, method_obj,
+ status = acpi_ns_attach_object(parent_node, method_obj,
ACPI_TYPE_METHOD);
if (ACPI_FAILURE(status)) {
goto exit;
}
- /* Execute the root node as a control method */
+ /* Execute the parent node as a control method */
status = acpi_ns_evaluate(info);
@@ -401,15 +417,19 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
/* Detach the temporary method object */
- acpi_ns_detach_object(acpi_gbl_root_node);
+ acpi_ns_detach_object(parent_node);
- /* Restore the original root object */
+ /* Restore the original parent object */
- status =
- acpi_ns_attach_object(acpi_gbl_root_node, root_obj,
- ACPI_TYPE_DEVICE);
+ if (parent_obj) {
+ status = acpi_ns_attach_object(parent_node, parent_obj, type);
+ } else {
+ parent_node->type = (u8)type;
+ }
exit:
- acpi_ut_remove_reference(root_obj);
+ if (parent_obj) {
+ acpi_ut_remove_reference(parent_obj);
+ }
return_VOID;
}
diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c
index 1d5b360eb25..4f8abac231d 100644
--- a/drivers/acpi/acpica/nsinit.c
+++ b/drivers/acpi/acpica/nsinit.c
@@ -96,7 +96,7 @@ acpi_status acpi_ns_initialize_objects(void)
/* Walk entire namespace from the supplied root */
status = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, acpi_ns_init_one_object,
+ ACPI_UINT32_MAX, acpi_ns_init_one_object, NULL,
&info, NULL);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
@@ -156,7 +156,8 @@ acpi_status acpi_ns_initialize_devices(void)
status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, FALSE,
- acpi_ns_find_ini_methods, &info, NULL);
+ acpi_ns_find_ini_methods, NULL, &info,
+ NULL);
if (ACPI_FAILURE(status)) {
goto error_exit;
}
@@ -189,7 +190,8 @@ acpi_status acpi_ns_initialize_devices(void)
status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, FALSE,
- acpi_ns_init_one_device, &info, NULL);
+ acpi_ns_init_one_device, NULL, &info,
+ NULL);
ACPI_FREE(info.evaluate_info);
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index f8427afeebd..b05f42903c8 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -232,6 +232,12 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
status = acpi_ns_check_package(data, return_object_ptr);
}
+ /*
+ * Perform additional, more complicated repairs on a per-name
+ * basis.
+ */
+ status = acpi_ns_complex_repairs(data, node, status, return_object_ptr);
+
check_validation_status:
/*
* If the object validation failed or if we successfully repaired one
@@ -601,7 +607,8 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
* there is only one entry). We may be able to repair this by
* wrapping the returned Package with a new outer Package.
*/
- if ((*elements)->common.type != ACPI_TYPE_PACKAGE) {
+ if (*elements
+ && ((*elements)->common.type != ACPI_TYPE_PACKAGE)) {
/* Create the new outer package and populate it */
@@ -673,6 +680,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data,
union acpi_operand_object *sub_package;
union acpi_operand_object **sub_elements;
acpi_status status;
+ u8 non_trailing_null = FALSE;
u32 expected_count;
u32 i;
u32 j;
@@ -680,6 +688,45 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data,
/* Validate each sub-Package in the parent Package */
for (i = 0; i < count; i++) {
+ /*
+ * Handling for NULL package elements. For now, we will simply allow
+ * a parent package with trailing NULL elements. This can happen if
+ * the package was defined to be longer than the initializer list.
+ * This is legal as per the ACPI specification. It is often used
+ * to allow for dynamic initialization of a Package.
+ *
+ * A future enhancement may be to simply truncate the package to
+ * remove the trailing NULL elements.
+ */
+ if (!(*elements)) {
+ if (!non_trailing_null) {
+
+ /* Ensure the remaining elements are all NULL */
+
+ for (j = 1; j < (count - i + 1); j++) {
+ if (elements[j]) {
+ non_trailing_null = TRUE;
+ }
+ }
+
+ if (!non_trailing_null) {
+
+ /* Ignore the trailing NULL elements */
+
+ return (AE_OK);
+ }
+ }
+
+ /* There are trailing non-null elements, issue warning */
+
+ ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
+ data->node_flags,
+ "Found NULL element at package index %u",
+ i));
+ elements++;
+ continue;
+ }
+
sub_package = *elements;
sub_elements = sub_package->package.elements;
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
index db2b2a99c3a..d563f1a564a 100644
--- a/drivers/acpi/acpica/nsrepair.c
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -44,6 +44,7 @@
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
+#include "acinterp.h"
#include "acpredef.h"
#define _COMPONENT ACPI_NAMESPACE
@@ -76,7 +77,13 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
union acpi_operand_object *return_object = *return_object_ptr;
union acpi_operand_object *new_object;
acpi_size length;
+ acpi_status status;
+ /*
+ * At this point, we know that the type of the returned object was not
+ * one of the expected types for this predefined name. Attempt to
+ * repair the object. Only a limited number of repairs are possible.
+ */
switch (return_object->common.type) {
case ACPI_TYPE_BUFFER:
@@ -111,43 +118,94 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
*/
ACPI_MEMCPY(new_object->string.pointer,
return_object->buffer.pointer, length);
+ break;
- /*
- * If the original object is a package element, we need to:
- * 1. Set the reference count of the new object to match the
- * reference count of the old object.
- * 2. Decrement the reference count of the original object.
- */
- if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
- new_object->common.reference_count =
- return_object->common.reference_count;
+ case ACPI_TYPE_INTEGER:
+
+ /* 1) Does the method/object legally return a buffer? */
+
+ if (expected_btypes & ACPI_RTYPE_BUFFER) {
+ /*
+ * Convert the Integer to a packed-byte buffer. _MAT needs
+ * this sometimes, if a read has been performed on a Field
+ * object that is less than or equal to the global integer
+ * size (32 or 64 bits).
+ */
+ status =
+ acpi_ex_convert_to_buffer(return_object,
+ &new_object);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
- if (return_object->common.reference_count > 1) {
- return_object->common.reference_count--;
+ /* 2) Does the method/object legally return a string? */
+
+ else if (expected_btypes & ACPI_RTYPE_STRING) {
+ /*
+ * The only supported Integer-to-String conversion is to convert
+ * an integer of value 0 to a NULL string. The last element of
+ * _BIF and _BIX packages occasionally need this fix.
+ */
+ if (return_object->integer.value != 0) {
+ return (AE_AML_OPERAND_TYPE);
}
- ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
- data->node_flags,
- "Converted Buffer to expected String at index %u",
- package_index));
+ /* Allocate a new NULL string object */
+
+ new_object = acpi_ut_create_string_object(0);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
} else {
- ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
- data->node_flags,
- "Converted Buffer to expected String"));
+ return (AE_AML_OPERAND_TYPE);
}
+ break;
- /* Delete old object, install the new return object */
+ default:
- acpi_ut_remove_reference(return_object);
- *return_object_ptr = new_object;
- data->flags |= ACPI_OBJECT_REPAIRED;
- return (AE_OK);
+ /* We cannot repair this object */
- default:
- break;
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /* Object was successfully repaired */
+
+ /*
+ * If the original object is a package element, we need to:
+ * 1. Set the reference count of the new object to match the
+ * reference count of the old object.
+ * 2. Decrement the reference count of the original object.
+ */
+ if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
+ new_object->common.reference_count =
+ return_object->common.reference_count;
+
+ if (return_object->common.reference_count > 1) {
+ return_object->common.reference_count--;
+ }
+
+ ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+ "Converted %s to expected %s at index %u",
+ acpi_ut_get_object_type_name
+ (return_object),
+ acpi_ut_get_object_type_name(new_object),
+ package_index));
+ } else {
+ ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+ "Converted %s to expected %s",
+ acpi_ut_get_object_type_name
+ (return_object),
+ acpi_ut_get_object_type_name
+ (new_object)));
}
- return (AE_AML_OPERAND_TYPE);
+ /* Delete old object, install the new return object */
+
+ acpi_ut_remove_reference(return_object);
+ *return_object_ptr = new_object;
+ data->flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
}
/*******************************************************************************
@@ -196,8 +254,8 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
*obj_desc_ptr = pkg_obj_desc;
data->flags |= ACPI_OBJECT_REPAIRED;
- ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
- "Incorrectly formed Package, attempting repair"));
+ ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+ "Repaired Incorrectly formed Package"));
return (AE_OK);
}
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
new file mode 100644
index 00000000000..d07b6861381
--- /dev/null
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -0,0 +1,540 @@
+/******************************************************************************
+ *
+ * Module Name: nsrepair2 - Repair for objects returned by specific
+ * predefined methods
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2009, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsrepair2")
+
+/*
+ * Information structure and handler for ACPI predefined names that can
+ * be repaired on a per-name basis.
+ */
+typedef
+acpi_status(*acpi_repair_function) (struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr);
+
+typedef struct acpi_repair_info {
+ char name[ACPI_NAME_SIZE];
+ acpi_repair_function repair_function;
+
+} acpi_repair_info;
+
+/* Local prototypes */
+
+static const struct acpi_repair_info *acpi_ns_match_repairable_name(struct
+ acpi_namespace_node
+ *node);
+
+static acpi_status
+acpi_ns_repair_ALR(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_PSS(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_TSS(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
+ union acpi_operand_object *return_object,
+ u32 expected_count,
+ u32 sort_index,
+ u8 sort_direction, char *sort_key_name);
+
+static acpi_status
+acpi_ns_remove_null_elements(union acpi_operand_object *package);
+
+static acpi_status
+acpi_ns_sort_list(union acpi_operand_object **elements,
+ u32 count, u32 index, u8 sort_direction);
+
+/* Values for sort_direction above */
+
+#define ACPI_SORT_ASCENDING 0
+#define ACPI_SORT_DESCENDING 1
+
+/*
+ * This table contains the names of the predefined methods for which we can
+ * perform more complex repairs.
+ *
+ * _ALR: Sort the list ascending by ambient_illuminance if necessary
+ * _PSS: Sort the list descending by Power if necessary
+ * _TSS: Sort the list descending by Power if necessary
+ */
+static const struct acpi_repair_info acpi_ns_repairable_names[] = {
+ {"_ALR", acpi_ns_repair_ALR},
+ {"_PSS", acpi_ns_repair_PSS},
+ {"_TSS", acpi_ns_repair_TSS},
+ {{0, 0, 0, 0}, NULL} /* Table terminator */
+};
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_complex_repairs
+ *
+ * PARAMETERS: Data - Pointer to validation data structure
+ * Node - Namespace node for the method/object
+ * validate_status - Original status of earlier validation
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if repair was successful. If name is not
+ * matched, validate_status is returned.
+ *
+ * DESCRIPTION: Attempt to repair/convert a return object of a type that was
+ * not expected.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_ns_complex_repairs(struct acpi_predefined_data *data,
+ struct acpi_namespace_node *node,
+ acpi_status validate_status,
+ union acpi_operand_object **return_object_ptr)
+{
+ const struct acpi_repair_info *predefined;
+ acpi_status status;
+
+ /* Check if this name is in the list of repairable names */
+
+ predefined = acpi_ns_match_repairable_name(node);
+ if (!predefined) {
+ return (validate_status);
+ }
+
+ status = predefined->repair_function(data, return_object_ptr);
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_match_repairable_name
+ *
+ * PARAMETERS: Node - Namespace node for the method/object
+ *
+ * RETURN: Pointer to entry in repair table. NULL indicates not found.
+ *
+ * DESCRIPTION: Check an object name against the repairable object list.
+ *
+ *****************************************************************************/
+
+static const struct acpi_repair_info *acpi_ns_match_repairable_name(struct
+ acpi_namespace_node
+ *node)
+{
+ const struct acpi_repair_info *this_name;
+
+ /* Search info table for a repairable predefined method/object name */
+
+ this_name = acpi_ns_repairable_names;
+ while (this_name->repair_function) {
+ if (ACPI_COMPARE_NAME(node->name.ascii, this_name->name)) {
+ return (this_name);
+ }
+ this_name++;
+ }
+
+ return (NULL); /* Not found */
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_ALR
+ *
+ * PARAMETERS: Data - Pointer to validation data structure
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _ALR object. If necessary, sort the object list
+ * ascending by the ambient illuminance values.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_ALR(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ acpi_status status;
+
+ status = acpi_ns_check_sorted_list(data, return_object, 2, 1,
+ ACPI_SORT_ASCENDING,
+ "AmbientIlluminance");
+
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_TSS
+ *
+ * PARAMETERS: Data - Pointer to validation data structure
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list
+ * descending by the power dissipation values.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_TSS(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ acpi_status status;
+
+ status = acpi_ns_check_sorted_list(data, return_object, 5, 1,
+ ACPI_SORT_DESCENDING,
+ "PowerDissipation");
+
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_repair_PSS
+ *
+ * PARAMETERS: Data - Pointer to validation data structure
+ * return_object_ptr - Pointer to the object returned from the
+ * evaluation of a method or object
+ *
+ * RETURN: Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _PSS object. If necessary, sort the object list
+ * by the CPU frequencies. Check that the power dissipation values
+ * are all proportional to CPU frequency (i.e., sorting by
+ * frequency should be the same as sorting by power.)
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_PSS(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr)
+{
+ union acpi_operand_object *return_object = *return_object_ptr;
+ union acpi_operand_object **outer_elements;
+ u32 outer_element_count;
+ union acpi_operand_object **elements;
+ union acpi_operand_object *obj_desc;
+ u32 previous_value;
+ acpi_status status;
+ u32 i;
+
+ /*
+ * Entries (sub-packages) in the _PSS Package must be sorted by power
+ * dissipation, in descending order. If it appears that the list is
+ * incorrectly sorted, sort it. We sort by cpu_frequency, since this
+ * should be proportional to the power.
+ */
+ status = acpi_ns_check_sorted_list(data, return_object, 6, 0,
+ ACPI_SORT_DESCENDING,
+ "CpuFrequency");
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ /*
+ * We now know the list is correctly sorted by CPU frequency. Check if
+ * the power dissipation values are proportional.
+ */
+ previous_value = ACPI_UINT32_MAX;
+ outer_elements = return_object->package.elements;
+ outer_element_count = return_object->package.count;
+
+ for (i = 0; i < outer_element_count; i++) {
+ elements = (*outer_elements)->package.elements;
+ obj_desc = elements[1]; /* Index1 = power_dissipation */
+
+ if ((u32) obj_desc->integer.value > previous_value) {
+ ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
+ data->node_flags,
+ "SubPackage[%u,%u] - suspicious power dissipation values",
+ i - 1, i));
+ }
+
+ previous_value = (u32) obj_desc->integer.value;
+ outer_elements++;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_check_sorted_list
+ *
+ * PARAMETERS: Data - Pointer to validation data structure
+ * return_object - Pointer to the top-level returned object
+ * expected_count - Minimum length of each sub-package
+ * sort_index - Sub-package entry to sort on
+ * sort_direction - Ascending or descending
+ * sort_key_name - Name of the sort_index field
+ *
+ * RETURN: Status. AE_OK if the list is valid and is sorted correctly or
+ * has been repaired by sorting the list.
+ *
+ * DESCRIPTION: Check if the package list is valid and sorted correctly by the
+ * sort_index. If not, then sort the list.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
+ union acpi_operand_object *return_object,
+ u32 expected_count,
+ u32 sort_index,
+ u8 sort_direction, char *sort_key_name)
+{
+ u32 outer_element_count;
+ union acpi_operand_object **outer_elements;
+ union acpi_operand_object **elements;
+ union acpi_operand_object *obj_desc;
+ u32 i;
+ u32 previous_value;
+ acpi_status status;
+
+ /* The top-level object must be a package */
+
+ if (return_object->common.type != ACPI_TYPE_PACKAGE) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * Detect any NULL package elements and remove them from the
+ * package.
+ *
+ * TBD: We may want to do this for all predefined names that
+ * return a variable-length package of packages.
+ */
+ status = acpi_ns_remove_null_elements(return_object);
+ if (status == AE_NULL_ENTRY) {
+ ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+ "NULL elements removed from package"));
+
+ /* Exit if package is now zero length */
+
+ if (!return_object->package.count) {
+ return (AE_NULL_ENTRY);
+ }
+ }
+
+ outer_elements = return_object->package.elements;
+ outer_element_count = return_object->package.count;
+ if (!outer_element_count) {
+ return (AE_AML_PACKAGE_LIMIT);
+ }
+
+ previous_value = 0;
+ if (sort_direction == ACPI_SORT_DESCENDING) {
+ previous_value = ACPI_UINT32_MAX;
+ }
+
+ /* Examine each subpackage */
+
+ for (i = 0; i < outer_element_count; i++) {
+
+ /* Each element of the top-level package must also be a package */
+
+ if ((*outer_elements)->common.type != ACPI_TYPE_PACKAGE) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /* Each sub-package must have the minimum length */
+
+ if ((*outer_elements)->package.count < expected_count) {
+ return (AE_AML_PACKAGE_LIMIT);
+ }
+
+ elements = (*outer_elements)->package.elements;
+ obj_desc = elements[sort_index];
+
+ if (obj_desc->common.type != ACPI_TYPE_INTEGER) {
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ /*
+ * The list must be sorted in the specified order. If we detect a
+ * discrepancy, issue a warning and sort the entire list
+ */
+ if (((sort_direction == ACPI_SORT_ASCENDING) &&
+ (obj_desc->integer.value < previous_value)) ||
+ ((sort_direction == ACPI_SORT_DESCENDING) &&
+ (obj_desc->integer.value > previous_value))) {
+ status =
+ acpi_ns_sort_list(return_object->package.elements,
+ outer_element_count, sort_index,
+ sort_direction);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ data->flags |= ACPI_OBJECT_REPAIRED;
+
+ ACPI_INFO_PREDEFINED((AE_INFO, data->pathname,
+ data->node_flags,
+ "Repaired unsorted list - now sorted by %s",
+ sort_key_name));
+ return (AE_OK);
+ }
+
+ previous_value = (u32) obj_desc->integer.value;
+ outer_elements++;
+ }
+
+ return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_remove_null_elements
+ *
+ * PARAMETERS: obj_desc - A Package object
+ *
+ * RETURN: Status. AE_NULL_ENTRY means that one or more elements were
+ * removed.
+ *
+ * DESCRIPTION: Remove all NULL package elements and update the package count.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
+{
+ union acpi_operand_object **source;
+ union acpi_operand_object **dest;
+ acpi_status status = AE_OK;
+ u32 count;
+ u32 new_count;
+ u32 i;
+
+ count = obj_desc->package.count;
+ new_count = count;
+
+ source = obj_desc->package.elements;
+ dest = source;
+
+ /* Examine all elements of the package object */
+
+ for (i = 0; i < count; i++) {
+ if (!*source) {
+ status = AE_NULL_ENTRY;
+ new_count--;
+ } else {
+ *dest = *source;
+ dest++;
+ }
+ source++;
+ }
+
+ if (status == AE_NULL_ENTRY) {
+
+ /* NULL terminate list and update the package count */
+
+ *dest = NULL;
+ obj_desc->package.count = new_count;
+ }
+
+ return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION: acpi_ns_sort_list
+ *
+ * PARAMETERS: Elements - Package object element list
+ * Count - Element count for above
+ * Index - Sort by which package element
+ * sort_direction - Ascending or Descending sort
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Sort the objects that are in a package element list.
+ *
+ * NOTE: Assumes that all NULL elements have been removed from the package.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_sort_list(union acpi_operand_object **elements,
+ u32 count, u32 index, u8 sort_direction)
+{
+ union acpi_operand_object *obj_desc1;
+ union acpi_operand_object *obj_desc2;
+ union acpi_operand_object *temp_obj;
+ u32 i;
+ u32 j;
+
+ /* Simple bubble sort */
+
+ for (i = 1; i < count; i++) {
+ for (j = (count - 1); j >= i; j--) {
+ obj_desc1 = elements[j - 1]->package.elements[index];
+ obj_desc2 = elements[j]->package.elements[index];
+
+ if (((sort_direction == ACPI_SORT_ASCENDING) &&
+ (obj_desc1->integer.value >
+ obj_desc2->integer.value))
+ || ((sort_direction == ACPI_SORT_DESCENDING)
+ && (obj_desc1->integer.value <
+ obj_desc2->integer.value))) {
+ temp_obj = elements[j - 1];
+ elements[j - 1] = elements[j];
+ elements[j] = temp_obj;
+ }
+ }
+ }
+
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c
index 35539df5c75..d7e6b52b448 100644
--- a/drivers/acpi/acpica/nswalk.c
+++ b/drivers/acpi/acpica/nswalk.c
@@ -165,24 +165,27 @@ struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
* max_depth - Depth to which search is to reach
* Flags - Whether to unlock the NS before invoking
* the callback routine
- * user_function - Called when an object of "Type" is found
- * Context - Passed to user function
- * return_value - from the user_function if terminated early.
- * Otherwise, returns NULL.
+ * pre_order_visit - Called during tree pre-order visit
+ * when an object of "Type" is found
+ * post_order_visit - Called during tree post-order visit
+ * when an object of "Type" is found
+ * Context - Passed to user function(s) above
+ * return_value - from the user_function if terminated
+ * early. Otherwise, returns NULL.
* RETURNS: Status
*
* DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
* starting (and ending) at the node specified by start_handle.
- * The user_function is called whenever a node that matches
- * the type parameter is found. If the user function returns
+ * The callback function is called whenever a node that matches
+ * the type parameter is found. If the callback function returns
* a non-zero value, the search is terminated immediately and
* this value is returned to the caller.
*
* The point of this procedure is to provide a generic namespace
* walk routine that can be called from multiple places to
- * provide multiple services; the User Function can be tailored
- * to each task, whether it is a print function, a compare
- * function, etc.
+ * provide multiple services; the callback function(s) can be
+ * tailored to each task, whether it is a print function,
+ * a compare function, etc.
*
******************************************************************************/
@@ -191,7 +194,8 @@ acpi_ns_walk_namespace(acpi_object_type type,
acpi_handle start_node,
u32 max_depth,
u32 flags,
- acpi_walk_callback user_function,
+ acpi_walk_callback pre_order_visit,
+ acpi_walk_callback post_order_visit,
void *context, void **return_value)
{
acpi_status status;
@@ -200,6 +204,7 @@ acpi_ns_walk_namespace(acpi_object_type type,
struct acpi_namespace_node *parent_node;
acpi_object_type child_type;
u32 level;
+ u8 node_previously_visited = FALSE;
ACPI_FUNCTION_TRACE(ns_walk_namespace);
@@ -212,7 +217,7 @@ acpi_ns_walk_namespace(acpi_object_type type,
/* Null child means "get first node" */
parent_node = start_node;
- child_node = NULL;
+ child_node = acpi_ns_get_next_node(parent_node, NULL);
child_type = ACPI_TYPE_ANY;
level = 1;
@@ -221,102 +226,129 @@ acpi_ns_walk_namespace(acpi_object_type type,
* started. When Level is zero, the loop is done because we have
* bubbled up to (and passed) the original parent handle (start_entry)
*/
- while (level > 0) {
+ while (level > 0 && child_node) {
+ status = AE_OK;
- /* Get the next node in this scope. Null if not found */
+ /* Found next child, get the type if we are not searching for ANY */
- status = AE_OK;
- child_node = acpi_ns_get_next_node(parent_node, child_node);
- if (child_node) {
+ if (type != ACPI_TYPE_ANY) {
+ child_type = child_node->type;
+ }
- /* Found next child, get the type if we are not searching for ANY */
+ /*
+ * Ignore all temporary namespace nodes (created during control
+ * method execution) unless told otherwise. These temporary nodes
+ * can cause a race condition because they can be deleted during
+ * the execution of the user function (if the namespace is
+ * unlocked before invocation of the user function.) Only the
+ * debugger namespace dump will examine the temporary nodes.
+ */
+ if ((child_node->flags & ANOBJ_TEMPORARY) &&
+ !(flags & ACPI_NS_WALK_TEMP_NODES)) {
+ status = AE_CTRL_DEPTH;
+ }
- if (type != ACPI_TYPE_ANY) {
- child_type = child_node->type;
- }
+ /* Type must match requested type */
+ else if (child_type == type) {
/*
- * Ignore all temporary namespace nodes (created during control
- * method execution) unless told otherwise. These temporary nodes
- * can cause a race condition because they can be deleted during
- * the execution of the user function (if the namespace is
- * unlocked before invocation of the user function.) Only the
- * debugger namespace dump will examine the temporary nodes.
+ * Found a matching node, invoke the user callback function.
+ * Unlock the namespace if flag is set.
*/
- if ((child_node->flags & ANOBJ_TEMPORARY) &&
- !(flags & ACPI_NS_WALK_TEMP_NODES)) {
- status = AE_CTRL_DEPTH;
+ if (flags & ACPI_NS_WALK_UNLOCK) {
+ mutex_status =
+ acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(mutex_status)) {
+ return_ACPI_STATUS(mutex_status);
+ }
}
- /* Type must match requested type */
-
- else if (child_type == type) {
- /*
- * Found a matching node, invoke the user callback function.
- * Unlock the namespace if flag is set.
- */
- if (flags & ACPI_NS_WALK_UNLOCK) {
- mutex_status =
- acpi_ut_release_mutex
- (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(mutex_status)) {
- return_ACPI_STATUS
- (mutex_status);
- }
+ /*
+ * Invoke the user function, either pre-order or post-order
+ * or both.
+ */
+ if (!node_previously_visited) {
+ if (pre_order_visit) {
+ status =
+ pre_order_visit(child_node, level,
+ context,
+ return_value);
}
+ } else {
+ if (post_order_visit) {
+ status =
+ post_order_visit(child_node, level,
+ context,
+ return_value);
+ }
+ }
- status =
- user_function(child_node, level, context,
- return_value);
-
- if (flags & ACPI_NS_WALK_UNLOCK) {
- mutex_status =
- acpi_ut_acquire_mutex
- (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(mutex_status)) {
- return_ACPI_STATUS
- (mutex_status);
- }
+ if (flags & ACPI_NS_WALK_UNLOCK) {
+ mutex_status =
+ acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(mutex_status)) {
+ return_ACPI_STATUS(mutex_status);
}
+ }
- switch (status) {
- case AE_OK:
- case AE_CTRL_DEPTH:
+ switch (status) {
+ case AE_OK:
+ case AE_CTRL_DEPTH:
- /* Just keep going */
- break;
+ /* Just keep going */
+ break;
- case AE_CTRL_TERMINATE:
+ case AE_CTRL_TERMINATE:
- /* Exit now, with OK status */
+ /* Exit now, with OK status */
- return_ACPI_STATUS(AE_OK);
+ return_ACPI_STATUS(AE_OK);
- default:
+ default:
- /* All others are valid exceptions */
+ /* All others are valid exceptions */
- return_ACPI_STATUS(status);
- }
+ return_ACPI_STATUS(status);
+ }
+ }
+
+ /*
+ * Depth first search: Attempt to go down another level in the
+ * namespace if we are allowed to. Don't go any further if we have
+ * reached the caller specified maximum depth or if the user
+ * function has specified that the maximum depth has been reached.
+ */
+ if (!node_previously_visited &&
+ (level < max_depth) && (status != AE_CTRL_DEPTH)) {
+ if (child_node->child) {
+
+ /* There is at least one child of this node, visit it */
+
+ level++;
+ parent_node = child_node;
+ child_node =
+ acpi_ns_get_next_node(parent_node, NULL);
+ continue;
}
+ }
- /*
- * Depth first search: Attempt to go down another level in the
- * namespace if we are allowed to. Don't go any further if we have
- * reached the caller specified maximum depth or if the user
- * function has specified that the maximum depth has been reached.
- */
- if ((level < max_depth) && (status != AE_CTRL_DEPTH)) {
- if (child_node->child) {
+ /* No more children, re-visit this node */
- /* There is at least one child of this node, visit it */
+ if (!node_previously_visited) {
+ node_previously_visited = TRUE;
+ continue;
+ }
- level++;
- parent_node = child_node;
- child_node = NULL;
- }
- }
- } else {
+ /* No more children, visit peers */
+
+ child_node = acpi_ns_get_next_node(parent_node, child_node);
+ if (child_node) {
+ node_previously_visited = FALSE;
+ }
+
+ /* No peers, re-visit parent */
+
+ else {
/*
* No more children of this node (acpi_ns_get_next_node failed), go
* back upwards in the namespace tree to the node's parent.
@@ -324,6 +356,8 @@ acpi_ns_walk_namespace(acpi_object_type type,
level--;
child_node = parent_node;
parent_node = acpi_ns_get_parent_node(parent_node);
+
+ node_previously_visited = TRUE;
}
}
diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c
index 4929dbdbc8f..f2bd1da7700 100644
--- a/drivers/acpi/acpica/nsxfeval.c
+++ b/drivers/acpi/acpica/nsxfeval.c
@@ -433,8 +433,11 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info)
* PARAMETERS: Type - acpi_object_type to search for
* start_object - Handle in namespace where search begins
* max_depth - Depth to which search is to reach
- * user_function - Called when an object of "Type" is found
- * Context - Passed to user function
+ * pre_order_visit - Called during tree pre-order visit
+ * when an object of "Type" is found
+ * post_order_visit - Called during tree post-order visit
+ * when an object of "Type" is found
+ * Context - Passed to user function(s) above
* return_value - Location where return value of
* user_function is put if terminated early
*
@@ -443,16 +446,16 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info)
*
* DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
* starting (and ending) at the object specified by start_handle.
- * The user_function is called whenever an object that matches
- * the type parameter is found. If the user function returns
+ * The callback function is called whenever an object that matches
+ * the type parameter is found. If the callback function returns
* a non-zero value, the search is terminated immediately and this
* value is returned to the caller.
*
* The point of this procedure is to provide a generic namespace
* walk routine that can be called from multiple places to
- * provide multiple services; the User Function can be tailored
- * to each task, whether it is a print function, a compare
- * function, etc.
+ * provide multiple services; the callback function(s) can be
+ * tailored to each task, whether it is a print function,
+ * a compare function, etc.
*
******************************************************************************/
@@ -460,7 +463,8 @@ acpi_status
acpi_walk_namespace(acpi_object_type type,
acpi_handle start_object,
u32 max_depth,
- acpi_walk_callback user_function,
+ acpi_walk_callback pre_order_visit,
+ acpi_walk_callback post_order_visit,
void *context, void **return_value)
{
acpi_status status;
@@ -469,7 +473,8 @@ acpi_walk_namespace(acpi_object_type type,
/* Parameter validation */
- if ((type > ACPI_TYPE_LOCAL_MAX) || (!max_depth) || (!user_function)) {
+ if ((type > ACPI_TYPE_LOCAL_MAX) ||
+ (!max_depth) || (!pre_order_visit && !post_order_visit)) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
@@ -501,8 +506,9 @@ acpi_walk_namespace(acpi_object_type type,
}
status = acpi_ns_walk_namespace(type, start_object, max_depth,
- ACPI_NS_WALK_UNLOCK, user_function,
- context, return_value);
+ ACPI_NS_WALK_UNLOCK, pre_order_visit,
+ post_order_visit, context,
+ return_value);
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
@@ -681,8 +687,8 @@ acpi_get_devices(const char *HID,
status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
- acpi_ns_get_device_callback, &info,
- return_value);
+ acpi_ns_get_device_callback, NULL,
+ &info, return_value);
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c
index cd7995b3aed..0988e4a8901 100644
--- a/drivers/acpi/acpica/psloop.c
+++ b/drivers/acpi/acpica/psloop.c
@@ -87,7 +87,8 @@ acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
union acpi_parse_object *op, acpi_status status);
static void
-acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id);
+acpi_ps_link_module_code(union acpi_parse_object *parent_op,
+ u8 *aml_start, u32 aml_length, acpi_owner_id owner_id);
/*******************************************************************************
*
@@ -479,11 +480,14 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
*/
if (walk_state->pass_number ==
ACPI_IMODE_LOAD_PASS1) {
- acpi_ps_link_module_code(aml_op_start,
- walk_state->
+ acpi_ps_link_module_code(op->common.
+ parent,
+ aml_op_start,
+ (u32)
+ (walk_state->
parser_state.
pkg_end -
- aml_op_start,
+ aml_op_start),
walk_state->
owner_id);
}
@@ -598,7 +602,8 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
*
* FUNCTION: acpi_ps_link_module_code
*
- * PARAMETERS: aml_start - Pointer to the AML
+ * PARAMETERS: parent_op - Parent parser op
+ * aml_start - Pointer to the AML
* aml_length - Length of executable AML
* owner_id - owner_id of module level code
*
@@ -611,11 +616,13 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
******************************************************************************/
static void
-acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id)
+acpi_ps_link_module_code(union acpi_parse_object *parent_op,
+ u8 *aml_start, u32 aml_length, acpi_owner_id owner_id)
{
union acpi_operand_object *prev;
union acpi_operand_object *next;
union acpi_operand_object *method_obj;
+ struct acpi_namespace_node *parent_node;
/* Get the tail of the list */
@@ -639,11 +646,24 @@ acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id)
return;
}
+ if (parent_op->common.node) {
+ parent_node = parent_op->common.node;
+ } else {
+ parent_node = acpi_gbl_root_node;
+ }
+
method_obj->method.aml_start = aml_start;
method_obj->method.aml_length = aml_length;
method_obj->method.owner_id = owner_id;
method_obj->method.flags |= AOPOBJ_MODULE_LEVEL;
+ /*
+ * Save the parent node in next_object. This is cheating, but we
+ * don't want to expand the method object.
+ */
+ method_obj->method.next_object =
+ ACPI_CAST_PTR(union acpi_operand_object, parent_node);
+
if (!prev) {
acpi_gbl_module_code_list = method_obj;
} else {
diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c
index 70838e9b608..4df8f139026 100644
--- a/drivers/acpi/acpica/psparse.c
+++ b/drivers/acpi/acpica/psparse.c
@@ -610,17 +610,13 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
implicit_return_obj) {
previous_walk_state->
implicit_return_obj =
- acpi_ut_create_internal_object
- (ACPI_TYPE_INTEGER);
+ acpi_ut_create_integer_object
+ ((u64) 0);
if (!previous_walk_state->
implicit_return_obj) {
return_ACPI_STATUS
(AE_NO_MEMORY);
}
-
- previous_walk_state->
- implicit_return_obj->
- integer.value = 0;
}
/* Restart the calling control method */
diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c
index dd9731c29a7..12934ad6da8 100644
--- a/drivers/acpi/acpica/psxface.c
+++ b/drivers/acpi/acpica/psxface.c
@@ -306,14 +306,12 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info)
*/
if (acpi_gbl_enable_interpreter_slack) {
walk_state->implicit_return_obj =
- acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ acpi_ut_create_integer_object((u64) 0);
if (!walk_state->implicit_return_obj) {
status = AE_NO_MEMORY;
acpi_ds_delete_walk_state(walk_state);
goto cleanup;
}
-
- walk_state->implicit_return_obj->integer.value = 0;
}
/* Parse the AML */
diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c
index 61f6315fce9..6c6a5137b72 100644
--- a/drivers/acpi/acpica/utmisc.c
+++ b/drivers/acpi/acpica/utmisc.c
@@ -1161,3 +1161,45 @@ acpi_ut_predefined_warning(const char *module_name,
ACPI_COMMON_MSG_SUFFIX;
va_end(args);
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_predefined_info
+ *
+ * PARAMETERS: module_name - Caller's module name (for error output)
+ * line_number - Caller's line number (for error output)
+ * Pathname - Full pathname to the node
+ * node_flags - From Namespace node for the method/object
+ * Format - Printf format string + additional args
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Info messages for the predefined validation module. Messages
+ * are only emitted the first time a problem with a particular
+ * method/object is detected. This prevents a flood of
+ * messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+ u32 line_number,
+ char *pathname, u8 node_flags, const char *format, ...)
+{
+ va_list args;
+
+ /*
+ * Warning messages for this method/object will be disabled after the
+ * first time a validation fails or an object is successfully repaired.
+ */
+ if (node_flags & ANOBJ_EVALUATED) {
+ return;
+ }
+
+ acpi_os_printf("ACPI Info for %s: ", pathname);
+
+ va_start(args, format);
+ acpi_os_vprintf(format, args);
+ ACPI_COMMON_MSG_SUFFIX;
+ va_end(args);
+}
diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c
index 0207b625274..42e658b543f 100644
--- a/drivers/acpi/acpica/utobject.c
+++ b/drivers/acpi/acpica/utobject.c
@@ -190,6 +190,35 @@ union acpi_operand_object *acpi_ut_create_package_object(u32 count)
/*******************************************************************************
*
+ * FUNCTION: acpi_ut_create_integer_object
+ *
+ * PARAMETERS: initial_value - Initial value for the integer
+ *
+ * RETURN: Pointer to a new Integer object, null on failure
+ *
+ * DESCRIPTION: Create an initialized integer object
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ut_create_integer_object(u64 initial_value)
+{
+ union acpi_operand_object *integer_desc;
+
+ ACPI_FUNCTION_TRACE(ut_create_integer_object);
+
+ /* Create and initialize a new integer object */
+
+ integer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+ if (!integer_desc) {
+ return_PTR(NULL);
+ }
+
+ integer_desc->integer.value = initial_value;
+ return_PTR(integer_desc);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ut_create_buffer_object
*
* PARAMETERS: buffer_size - Size of buffer to be created
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
index 642bb305cb6..5faf6c21257 100644
--- a/drivers/acpi/container.c
+++ b/drivers/acpi/container.c
@@ -258,7 +258,7 @@ static int __init acpi_container_init(void)
acpi_walk_namespace(ACPI_TYPE_DEVICE,
ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- container_walk_namespace_cb, &action, NULL);
+ container_walk_namespace_cb, NULL, &action, NULL);
return (0);
}
@@ -271,7 +271,7 @@ static void __exit acpi_container_exit(void)
acpi_walk_namespace(ACPI_TYPE_DEVICE,
ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- container_walk_namespace_cb, &action, NULL);
+ container_walk_namespace_cb, NULL, &action, NULL);
acpi_bus_unregister_driver(&acpi_container_driver);
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 7338b6a3e04..30be3c148f7 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -1030,8 +1030,8 @@ static int dock_add(acpi_handle handle)
/* Find dependent devices */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, find_dock_devices, dock_station,
- NULL);
+ ACPI_UINT32_MAX, find_dock_devices, NULL,
+ dock_station, NULL);
/* add the dock station as a device dependent on itself */
dd = alloc_dock_dependent_device(handle);
@@ -1127,11 +1127,11 @@ static int __init dock_init(void)
/* look for a dock station */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, find_dock, NULL, NULL);
+ ACPI_UINT32_MAX, find_dock, NULL, NULL, NULL);
/* look for bay */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, find_bay, NULL, NULL);
+ ACPI_UINT32_MAX, find_bay, NULL, NULL, NULL);
if (!dock_station_count) {
printk(KERN_INFO PREFIX "No dock devices found.\n");
return 0;
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index baef28c1e63..75b147f5c8f 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -820,7 +820,7 @@ static int acpi_ec_add(struct acpi_device *device)
/* Find and register all query methods */
acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
- acpi_ec_register_query_methods, ec, NULL);
+ acpi_ec_register_query_methods, NULL, ec, NULL);
if (!first_ec)
first_ec = ec;
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index c6645f26224..4c8fcff662c 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -113,7 +113,7 @@ acpi_handle acpi_get_child(acpi_handle parent, acpi_integer address)
if (!parent)
return NULL;
acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
- 1, do_acpi_find_child, &find, NULL);
+ 1, do_acpi_find_child, NULL, &find, NULL);
return find.handle;
}
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index 202dd0c976a..2be2fb66204 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -283,22 +283,24 @@ acpi_table_parse_srat(enum acpi_srat_type id,
int __init acpi_numa_init(void)
{
+ int ret = 0;
+
/* SRAT: Static Resource Affinity Table */
if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
acpi_table_parse_srat(ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY,
acpi_parse_x2apic_affinity, NR_CPUS);
acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY,
acpi_parse_processor_affinity, NR_CPUS);
- acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
- acpi_parse_memory_affinity,
- NR_NODE_MEMBLKS);
+ ret = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
+ acpi_parse_memory_affinity,
+ NR_NODE_MEMBLKS);
}
/* SLIT: System Locality Information Table */
acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);
acpi_numa_arch_fixup();
- return 0;
+ return ret;
}
int acpi_get_pxm(acpi_handle h)
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index 45da2bae36c..11f21974320 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -219,12 +219,12 @@ walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number);
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- user_function, &child_context, NULL);
+ user_function, NULL, &child_context, NULL);
if (ACPI_FAILURE(status))
goto out;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- walk_p2p_bridge, &child_context, NULL);
+ walk_p2p_bridge, NULL, &child_context, NULL);
out:
pci_dev_put(dev);
return AE_OK;
@@ -277,12 +277,12 @@ walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
dbg("root bridge walk, pci_bus = %x\n", pci_bus->number);
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- user_function, &context, NULL);
+ user_function, NULL, &context, NULL);
if (ACPI_FAILURE(status))
return status;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- walk_p2p_bridge, &context, NULL);
+ walk_p2p_bridge, NULL, &context, NULL);
if (ACPI_FAILURE(status))
err("%s: walk_p2p_bridge failure - %d\n", __func__, status);
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index ec742a4e563..cb4283f5a79 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -1102,7 +1102,7 @@ void acpi_processor_install_hotplug_notify(void)
acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- processor_walk_namespace_cb, &action, NULL);
+ processor_walk_namespace_cb, NULL, &action, NULL);
#endif
register_hotcpu_notifier(&acpi_cpu_notifier);
}
@@ -1115,7 +1115,7 @@ void acpi_processor_uninstall_hotplug_notify(void)
acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- processor_walk_namespace_cb, &action, NULL);
+ processor_walk_namespace_cb, NULL, &action, NULL);
#endif
unregister_hotcpu_notifier(&acpi_cpu_notifier);
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 14a7481c97d..ff9f6226085 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1332,7 +1332,7 @@ static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
status = acpi_bus_check_add(handle, 0, ops, &device);
if (ACPI_SUCCESS(status))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
- acpi_bus_check_add, ops, &device);
+ acpi_bus_check_add, NULL, ops, &device);
if (child)
*child = device;
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 575593a8b4e..fc2f26b9b40 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -7,7 +7,7 @@
* video_detect.c:
* Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c
* There a Linux specific (Spec does not provide a HID for video devices) is
- * assinged
+ * assigned
*
* After PCI devices are glued with ACPI devices
* acpi_get_pci_dev() can be called to identify ACPI graphics
@@ -83,16 +83,16 @@ long acpi_is_video_device(struct acpi_device *device)
if (!device)
return 0;
- /* Does this device able to support video switching ? */
+ /* Is this device able to support video switching ? */
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) ||
ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy)))
video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
- /* Does this device able to retrieve a video ROM ? */
+ /* Is this device able to retrieve a video ROM ? */
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy)))
video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
- /* Does this device able to configure which video head to be POSTed ? */
+ /* Is this device able to configure which video head to be POSTed ? */
if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) &&
ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy)))
@@ -101,7 +101,7 @@ long acpi_is_video_device(struct acpi_device *device)
/* Only check for backlight functionality if one of the above hit. */
if (video_caps)
acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle,
- ACPI_UINT32_MAX, acpi_backlight_cap_match,
+ ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
&video_caps, NULL);
return video_caps;
@@ -137,7 +137,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
*
* if NULL is passed as argument all ACPI devices are enumerated and
* all graphics capabilities of physically present devices are
- * summerized and returned. This is cached and done only once.
+ * summarized and returned. This is cached and done only once.
*/
long acpi_video_get_capabilities(acpi_handle graphics_handle)
{
@@ -151,7 +151,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
if (!graphics_handle) {
/* Only do the global walk through all graphics devices once */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, find_video,
+ ACPI_UINT32_MAX, find_video, NULL,
&caps, NULL);
/* There might be boot param flags set already... */
acpi_video_support |= caps;
@@ -173,7 +173,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
return 0;
}
acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle,
- ACPI_UINT32_MAX, find_video,
+ ACPI_UINT32_MAX, find_video, NULL,
&caps, NULL);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n",
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index f2df6e2a224..676f08b004b 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -3,7 +3,7 @@
#
menuconfig ATA
- tristate "Serial ATA (prod) and Parallel ATA (experimental) drivers"
+ tristate "Serial ATA and Parallel ATA drivers"
depends on HAS_IOMEM
depends on BLOCK
depends on !(M32R || M68K) || BROKEN
@@ -374,8 +374,8 @@ config PATA_HPT366
If unsure, say N.
config PATA_HPT37X
- tristate "HPT 370/370A/371/372/374/302 PATA support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ tristate "HPT 370/370A/371/372/374/302 PATA support"
+ depends on PCI
help
This option enables support for the majority of the later HPT
PATA controllers via the new ATA layer.
@@ -383,8 +383,8 @@ config PATA_HPT37X
If unsure, say N.
config PATA_HPT3X2N
- tristate "HPT 372N/302N PATA support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ tristate "HPT 372N/302N PATA support"
+ depends on PCI
help
This option enables support for the N variant HPT PATA
controllers via the new ATA layer
@@ -401,7 +401,7 @@ config PATA_HPT3X3
If unsure, say N.
config PATA_HPT3X3_DMA
- bool "HPT 343/363 DMA support (Experimental)"
+ bool "HPT 343/363 DMA support"
depends on PATA_HPT3X3
help
This option enables DMA support for the HPT343/363
@@ -510,8 +510,8 @@ config PATA_NETCELL
If unsure, say N.
config PATA_NINJA32
- tristate "Ninja32/Delkin Cardbus ATA support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ tristate "Ninja32/Delkin Cardbus ATA support"
+ depends on PCI
help
This option enables support for the Ninja32, Delkin and
possibly other brands of Cardbus ATA adapter
@@ -573,6 +573,14 @@ config PATA_PCMCIA
If unsure, say N.
+config PATA_PDC2027X
+ tristate "Promise PATA 2027x support"
+ depends on PCI
+ help
+ This option enables support for Promise PATA pdc20268 to pdc20277 host adapters.
+
+ If unsure, say N.
+
config PATA_PDC_OLD
tristate "Older Promise PATA controller support"
depends on PCI
@@ -643,14 +651,6 @@ config PATA_SERVERWORKS
If unsure, say N.
-config PATA_PDC2027X
- tristate "Promise PATA 2027x support"
- depends on PCI
- help
- This option enables support for Promise PATA pdc20268 to pdc20277 host adapters.
-
- If unsure, say N.
-
config PATA_SIL680
tristate "CMD / Silicon Image 680 PATA support"
depends on PCI
@@ -667,6 +667,15 @@ config PATA_SIS
If unsure, say N.
+config PATA_TOSHIBA
+ tristate "Toshiba Piccolo support (Experimental)"
+ depends on PCI && EXPERIMENTAL
+ help
+ Support for the Toshiba Piccolo controllers. Currently only the
+ primary channel is supported by this driver.
+
+ If unsure, say N.
+
config PATA_VIA
tristate "VIA PATA support"
depends on PCI
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 01e126f343b..d909435e9d8 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
obj-$(CONFIG_PATA_SC1200) += pata_sc1200.o
obj-$(CONFIG_PATA_SERVERWORKS) += pata_serverworks.o
obj-$(CONFIG_PATA_SIL680) += pata_sil680.o
+obj-$(CONFIG_PATA_TOSHIBA) += pata_piccolo.o
obj-$(CONFIG_PATA_VIA) += pata_via.o
obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o
obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index a3241a1a710..b8bea100a16 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -113,6 +113,7 @@ enum {
board_ahci_mcp65 = 6,
board_ahci_nopmp = 7,
board_ahci_yesncq = 8,
+ board_ahci_nosntf = 9,
/* global controller registers */
HOST_CAP = 0x00, /* host capabilities */
@@ -235,6 +236,7 @@ enum {
AHCI_HFLAG_NO_SUSPEND = (1 << 10), /* don't suspend */
AHCI_HFLAG_SRST_TOUT_IS_OFFLINE = (1 << 11), /* treat SRST timeout as
link offline */
+ AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */
/* ap->flags bits */
@@ -508,7 +510,7 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
- /* board_ahci_yesncq */
+ [board_ahci_yesncq] =
{
AHCI_HFLAGS (AHCI_HFLAG_YES_NCQ),
.flags = AHCI_FLAG_COMMON,
@@ -516,6 +518,14 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
+ [board_ahci_nosntf] =
+ {
+ AHCI_HFLAGS (AHCI_HFLAG_NO_SNTF),
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ops,
+ },
};
static const struct pci_device_id ahci_pci_tbl[] = {
@@ -531,7 +541,7 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x2683), board_ahci }, /* ESB2 */
{ PCI_VDEVICE(INTEL, 0x27c6), board_ahci }, /* ICH7-M DH */
{ PCI_VDEVICE(INTEL, 0x2821), board_ahci }, /* ICH8 */
- { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* ICH8 */
+ { PCI_VDEVICE(INTEL, 0x2822), board_ahci_nosntf }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2824), board_ahci }, /* ICH8 */
{ PCI_VDEVICE(INTEL, 0x2829), board_ahci }, /* ICH8M */
{ PCI_VDEVICE(INTEL, 0x282a), board_ahci }, /* ICH8M */
@@ -849,6 +859,12 @@ static void ahci_save_initial_config(struct pci_dev *pdev,
cap &= ~HOST_CAP_PMP;
}
+ if ((cap & HOST_CAP_SNTF) && (hpriv->flags & AHCI_HFLAG_NO_SNTF)) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "controller can't do SNTF, turning off CAP_SNTF\n");
+ cap &= ~HOST_CAP_SNTF;
+ }
+
if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361 &&
port_map != 1) {
dev_printk(KERN_INFO, &pdev->dev,
@@ -2988,6 +3004,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (pdev->vendor == PCI_VENDOR_ID_MARVELL && !marvell_enable)
return -ENODEV;
+ /* Promise's PDC42819 is a SAS/SATA controller that has an AHCI mode.
+ * At the moment, we can only use the AHCI mode. Let the users know
+ * that for SAS drives they're out of luck.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_PROMISE)
+ dev_printk(KERN_INFO, &pdev->dev, "PDC42819 "
+ "can only drive SATA devices with this driver\n");
+
/* acquire resources */
rc = pcim_enable_device(pdev);
if (rc)
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index ecfd22b4f1c..12e26c3c68e 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -168,9 +168,12 @@ static struct pci_device_id ata_generic[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561), },
{ PCI_DEVICE(PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558), },
{ PCI_DEVICE(PCI_VENDOR_ID_CENATEK,PCI_DEVICE_ID_CENATEK_IDE), },
- { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO), },
+#if !defined(CONFIG_PATA_TOSHIBA) && !defined(CONFIG_PATA_TOSHIBA_MODULE)
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), },
{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), },
+ { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), },
+ { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_5), },
+#endif
/* Must come last. If you add entries adjust this table appropriately */
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xFFFFFF00UL, 1},
{ 0, },
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 9ac4e378992..19136a7e106 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -599,7 +599,7 @@ static const struct ich_laptop ich_laptop[] = {
{ 0x27DF, 0x1028, 0x02b0 }, /* ICH7 on unknown Dell */
{ 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */
{ 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */
- { 0x27DF, 0x103C, 0x361a }, /* ICH7 on unkown HP */
+ { 0x27DF, 0x103C, 0x361a }, /* ICH7 on unknown HP */
{ 0x27DF, 0x1071, 0xD221 }, /* ICH7 on Hercules EC-900 */
{ 0x27DF, 0x152D, 0x0778 }, /* ICH7 on unknown Intel */
{ 0x24CA, 0x1025, 0x0061 }, /* ICH4 on ACER Aspire 2023WLMi */
@@ -869,10 +869,10 @@ static void do_pata_set_dmamode(struct ata_port *ap, struct ata_device *adev, in
(timings[pio][1] << 8);
}
- if (ap->udma_mask) {
+ if (ap->udma_mask)
udma_enable &= ~(1 << devid);
- pci_write_config_word(dev, master_port, master_data);
- }
+
+ pci_write_config_word(dev, master_port, master_data);
}
/* Don't scribble on 0x48 if the controller does not support UDMA */
if (ap->udma_mask)
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index b0882cddfd4..1245838ac13 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -807,12 +807,11 @@ static int ata_acpi_exec_tfs(struct ata_device *dev, int *nr_executed)
* EH context.
*
* RETURNS:
- * 0 on success, -errno on failure.
+ * 0 on success, -ENOENT if _SDD doesn't exist, -errno on failure.
*/
static int ata_acpi_push_id(struct ata_device *dev)
{
struct ata_port *ap = dev->link->ap;
- int err;
acpi_status status;
struct acpi_object_list input;
union acpi_object in_params[1];
@@ -835,12 +834,16 @@ static int ata_acpi_push_id(struct ata_device *dev)
status = acpi_evaluate_object(dev->acpi_handle, "_SDD", &input, NULL);
swap_buf_le16(dev->id, ATA_ID_WORDS);
- err = ACPI_FAILURE(status) ? -EIO : 0;
- if (err < 0)
+ if (status == AE_NOT_FOUND)
+ return -ENOENT;
+
+ if (ACPI_FAILURE(status)) {
ata_dev_printk(dev, KERN_WARNING,
"ACPI _SDD failed (AE 0x%x)\n", status);
+ return -EIO;
+ }
- return err;
+ return 0;
}
/**
@@ -971,7 +974,7 @@ int ata_acpi_on_devcfg(struct ata_device *dev)
/* do _SDD if SATA */
if (acpi_sata) {
rc = ata_acpi_push_id(dev);
- if (rc)
+ if (rc && rc != -ENOENT)
goto acpi_err;
}
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index dc72690ed5d..22ff51bdbc8 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6616,6 +6616,13 @@ static int __init ata_init(void)
{
ata_parse_force_param();
+ /*
+ * FIXME: In UP case, there is only one workqueue thread and if you
+ * have more than one PIO device, latency is bloody awful, with
+ * occasional multi-second "hiccups" as one PIO device waits for
+ * another. It's an ugly wart that users DO occasionally complain
+ * about; luckily most users have at most one PIO polled device.
+ */
ata_wq = create_workqueue("ata");
if (!ata_wq)
goto free_force_tbl;
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index bba2ae5df1c..0ea97c942ce 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -110,6 +110,13 @@ static const unsigned long ata_eh_identify_timeouts[] = {
ULONG_MAX,
};
+static const unsigned long ata_eh_flush_timeouts[] = {
+ 15000, /* be generous with flush */
+ 15000, /* ditto */
+ 30000, /* and even more generous */
+ ULONG_MAX,
+};
+
static const unsigned long ata_eh_other_timeouts[] = {
5000, /* same rationale as identify timeout */
10000, /* ditto */
@@ -147,6 +154,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = {
.timeouts = ata_eh_other_timeouts, },
{ .commands = CMDS(ATA_CMD_INIT_DEV_PARAMS),
.timeouts = ata_eh_other_timeouts, },
+ { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT),
+ .timeouts = ata_eh_flush_timeouts },
};
#undef CMDS
@@ -3112,6 +3121,82 @@ static int atapi_eh_clear_ua(struct ata_device *dev)
return 0;
}
+/**
+ * ata_eh_maybe_retry_flush - Retry FLUSH if necessary
+ * @dev: ATA device which may need FLUSH retry
+ *
+ * If @dev failed FLUSH, it needs to be reported upper layer
+ * immediately as it means that @dev failed to remap and already
+ * lost at least a sector and further FLUSH retrials won't make
+ * any difference to the lost sector. However, if FLUSH failed
+ * for other reasons, for example transmission error, FLUSH needs
+ * to be retried.
+ *
+ * This function determines whether FLUSH failure retry is
+ * necessary and performs it if so.
+ *
+ * RETURNS:
+ * 0 if EH can continue, -errno if EH needs to be repeated.
+ */
+static int ata_eh_maybe_retry_flush(struct ata_device *dev)
+{
+ struct ata_link *link = dev->link;
+ struct ata_port *ap = link->ap;
+ struct ata_queued_cmd *qc;
+ struct ata_taskfile tf;
+ unsigned int err_mask;
+ int rc = 0;
+
+ /* did flush fail for this device? */
+ if (!ata_tag_valid(link->active_tag))
+ return 0;
+
+ qc = __ata_qc_from_tag(ap, link->active_tag);
+ if (qc->dev != dev || (qc->tf.command != ATA_CMD_FLUSH_EXT &&
+ qc->tf.command != ATA_CMD_FLUSH))
+ return 0;
+
+ /* if the device failed it, it should be reported to upper layers */
+ if (qc->err_mask & AC_ERR_DEV)
+ return 0;
+
+ /* flush failed for some other reason, give it another shot */
+ ata_tf_init(dev, &tf);
+
+ tf.command = qc->tf.command;
+ tf.flags |= ATA_TFLAG_DEVICE;
+ tf.protocol = ATA_PROT_NODATA;
+
+ ata_dev_printk(dev, KERN_WARNING, "retrying FLUSH 0x%x Emask 0x%x\n",
+ tf.command, qc->err_mask);
+
+ err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
+ if (!err_mask) {
+ /*
+ * FLUSH is complete but there's no way to
+ * successfully complete a failed command from EH.
+ * Making sure retry is allowed at least once and
+ * retrying it should do the trick - whatever was in
+ * the cache is already on the platter and this won't
+ * cause infinite loop.
+ */
+ qc->scsicmd->allowed = max(qc->scsicmd->allowed, 1);
+ } else {
+ ata_dev_printk(dev, KERN_WARNING, "FLUSH failed Emask 0x%x\n",
+ err_mask);
+ rc = -EIO;
+
+ /* if device failed it, report it to upper layers */
+ if (err_mask & AC_ERR_DEV) {
+ qc->err_mask |= AC_ERR_DEV;
+ qc->result_tf = tf;
+ if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
static int ata_link_nr_enabled(struct ata_link *link)
{
struct ata_device *dev;
@@ -3455,6 +3540,15 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
}
}
+ /* retry flush if necessary */
+ ata_for_each_dev(dev, link, ALL) {
+ if (dev->class != ATA_DEV_ATA)
+ continue;
+ rc = ata_eh_maybe_retry_flush(dev);
+ if (rc)
+ goto dev_fail;
+ }
+
/* configure link power saving */
if (ehc->i.action & ATA_EH_LPM)
ata_for_each_dev(dev, link, ALL)
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index b4ee28dec52..1683ebda900 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -47,6 +47,7 @@
#include <linux/hdreg.h>
#include <linux/uaccess.h>
#include <linux/suspend.h>
+#include <asm/unaligned.h>
#include "libata.h"
@@ -154,8 +155,7 @@ static ssize_t ata_scsi_lpm_put(struct device *dev,
*/
for (i = 1; i < ARRAY_SIZE(link_pm_policy); i++) {
const int len = strlen(link_pm_policy[i].name);
- if (strncmp(link_pm_policy[i].name, buf, len) == 0 &&
- buf[len] == '\n') {
+ if (strncmp(link_pm_policy[i].name, buf, len) == 0) {
policy = link_pm_policy[i].value;
break;
}
@@ -1208,6 +1208,7 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
* ata_scsi_change_queue_depth - SCSI callback for queue depth config
* @sdev: SCSI device to configure queue depth for
* @queue_depth: new queue depth
+ * @reason: calling context
*
* This is libata standard hostt->change_queue_depth callback.
* SCSI will call into this callback when user tries to set queue
@@ -1219,12 +1220,16 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
* RETURNS:
* Newly configured queue depth.
*/
-int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
struct ata_device *dev;
unsigned long flags;
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth < 1 || queue_depth == sdev->queue_depth)
return sdev->queue_depth;
@@ -1964,6 +1969,7 @@ static unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf)
0x80, /* page 0x80, unit serial no page */
0x83, /* page 0x83, device ident page */
0x89, /* page 0x89, ata info page */
+ 0xb0, /* page 0xb0, block limits page */
0xb1, /* page 0xb1, block device characteristics page */
};
@@ -2085,6 +2091,43 @@ static unsigned int ata_scsiop_inq_89(struct ata_scsi_args *args, u8 *rbuf)
return 0;
}
+static unsigned int ata_scsiop_inq_b0(struct ata_scsi_args *args, u8 *rbuf)
+{
+ u32 min_io_sectors;
+
+ rbuf[1] = 0xb0;
+ rbuf[3] = 0x3c; /* required VPD size with unmap support */
+
+ /*
+ * Optimal transfer length granularity.
+ *
+ * This is always one physical block, but for disks with a smaller
+ * logical than physical sector size we need to figure out what the
+ * latter is.
+ */
+ if (ata_id_has_large_logical_sectors(args->id))
+ min_io_sectors = ata_id_logical_per_physical_sectors(args->id);
+ else
+ min_io_sectors = 1;
+ put_unaligned_be16(min_io_sectors, &rbuf[6]);
+
+ /*
+ * Optimal unmap granularity.
+ *
+ * The ATA spec doesn't even know about a granularity or alignment
+ * for the TRIM command. We can leave away most of the unmap related
+ * VPD page entries, but we have specifify a granularity to signal
+ * that we support some form of unmap - in thise case via WRITE SAME
+ * with the unmap bit set.
+ */
+ if (ata_id_has_trim(args->id)) {
+ put_unaligned_be32(65535 * 512 / 8, &rbuf[20]);
+ put_unaligned_be32(1, &rbuf[28]);
+ }
+
+ return 0;
+}
+
static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf)
{
int form_factor = ata_id_form_factor(args->id);
@@ -2374,6 +2417,13 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
rbuf[13] = log_per_phys;
rbuf[14] = (lowest_aligned >> 8) & 0x3f;
rbuf[15] = lowest_aligned;
+
+ if (ata_id_has_trim(args->id)) {
+ rbuf[14] |= 0x80; /* TPE */
+
+ if (ata_id_has_zero_after_trim(args->id))
+ rbuf[14] |= 0x40; /* TPRZ */
+ }
}
return 0;
@@ -2896,6 +2946,58 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
return 1;
}
+static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
+{
+ struct ata_taskfile *tf = &qc->tf;
+ struct scsi_cmnd *scmd = qc->scsicmd;
+ struct ata_device *dev = qc->dev;
+ const u8 *cdb = scmd->cmnd;
+ u64 block;
+ u32 n_block;
+ u32 size;
+ void *buf;
+
+ /* we may not issue DMA commands if no DMA mode is set */
+ if (unlikely(!dev->dma_mode))
+ goto invalid_fld;
+
+ if (unlikely(scmd->cmd_len < 16))
+ goto invalid_fld;
+ scsi_16_lba_len(cdb, &block, &n_block);
+
+ /* for now we only support WRITE SAME with the unmap bit set */
+ if (unlikely(!(cdb[1] & 0x8)))
+ goto invalid_fld;
+
+ /*
+ * WRITE SAME always has a sector sized buffer as payload, this
+ * should never be a multiple entry S/G list.
+ */
+ if (!scsi_sg_count(scmd))
+ goto invalid_fld;
+
+ buf = page_address(sg_page(scsi_sglist(scmd)));
+ size = ata_set_lba_range_entries(buf, 512, block, n_block);
+
+ tf->protocol = ATA_PROT_DMA;
+ tf->hob_feature = 0;
+ tf->feature = ATA_DSM_TRIM;
+ tf->hob_nsect = (size / 512) >> 8;
+ tf->nsect = size / 512;
+ tf->command = ATA_CMD_DSM;
+ tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 |
+ ATA_TFLAG_WRITE;
+
+ ata_qc_set_pc_nbytes(qc);
+
+ return 0;
+
+ invalid_fld:
+ ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x00);
+ /* "Invalid field in cdb" */
+ return 1;
+}
+
/**
* ata_get_xlat_func - check if SCSI to ATA translation is possible
* @dev: ATA device
@@ -2920,6 +3022,9 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
case WRITE_16:
return ata_scsi_rw_xlat;
+ case 0x93 /*WRITE_SAME_16*/:
+ return ata_scsi_write_same_xlat;
+
case SYNCHRONIZE_CACHE:
if (ata_try_flush_cache(dev))
return ata_scsi_flush_xlat;
@@ -3109,6 +3214,9 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
case 0x89:
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_89);
break;
+ case 0xb0:
+ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b0);
+ break;
case 0xb1:
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_b1);
break;
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index bbbb1fab175..efa8773bef5 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -736,7 +736,7 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf,
/*
* Use io*16_rep() accessors here as well to avoid pointlessly
- * swapping bytes to and fro on the big endian machines...
+ * swapping bytes to and from on the big endian machines...
*/
if (rw == READ) {
ioread16_rep(data_addr, pad, 1);
@@ -776,7 +776,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf,
void __iomem *data_addr = ap->ioaddr.data_addr;
unsigned int words = buflen >> 2;
int slop = buflen & 3;
-
+
if (!(ap->pflags & ATA_PFLAG_PIO32))
return ata_sff_data_xfer(dev, buf, buflen, rw);
@@ -795,7 +795,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf,
/*
* Use io*_rep() accessors here as well to avoid pointlessly
- * swapping bytes to and fro on the big endian machines...
+ * swapping bytes to and from on the big endian machines...
*/
if (rw == READ) {
if (slop < 3)
@@ -2384,7 +2384,7 @@ void ata_sff_post_internal_cmd(struct ata_queued_cmd *qc)
ap->hsm_task_state = HSM_ST_IDLE;
if (ap->ioaddr.bmdma_addr)
- ata_bmdma_stop(qc);
+ ap->ops->bmdma_stop(qc);
spin_unlock_irqrestore(ap->lock, flags);
}
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 1432dc9d0ab..9434114b2ca 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -453,7 +453,9 @@ static void ali_init_chipset(struct pci_dev *pdev)
/* Clear CD-ROM DMA write bit */
tmp &= 0x7F;
/* Cable and UDMA */
- pci_write_config_byte(pdev, 0x4B, tmp | 0x09);
+ if (pdev->revision >= 0xc2)
+ tmp |= 0x01;
+ pci_write_config_byte(pdev, 0x4B, tmp | 0x08);
/*
* CD_ROM DMA on (0x53 bit 0). Enable this even if we want
* to use PIO. 0x53 bit 1 (rev 20 only) - enable FIFO control
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index f98dffedf4b..dadfc358ba1 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -31,7 +31,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_cmd64x"
-#define DRV_VERSION "0.2.5"
+#define DRV_VERSION "0.3.1"
/*
* CMD64x specific registers definition.
@@ -254,17 +254,109 @@ static void cmd648_bmdma_stop(struct ata_queued_cmd *qc)
}
/**
- * cmd646r1_dma_stop - DMA stop callback
+ * cmd64x_bmdma_stop - DMA stop callback
* @qc: Command in progress
*
- * Stub for now while investigating the r1 quirk in the old driver.
+ * Track the completion of live DMA commands and clear the
+ * host->private_data DMA tracking flag as we do.
*/
-static void cmd646r1_bmdma_stop(struct ata_queued_cmd *qc)
+static void cmd64x_bmdma_stop(struct ata_queued_cmd *qc)
{
+ struct ata_port *ap = qc->ap;
ata_bmdma_stop(qc);
+ WARN_ON(ap->host->private_data != ap);
+ ap->host->private_data = NULL;
}
+/**
+ * cmd64x_qc_defer - Defer logic for chip limits
+ * @qc: queued command
+ *
+ * Decide whether we can issue the command. Called under the host lock.
+ */
+
+static int cmd64x_qc_defer(struct ata_queued_cmd *qc)
+{
+ struct ata_host *host = qc->ap->host;
+ struct ata_port *alt = host->ports[1 ^ qc->ap->port_no];
+ int rc;
+ int dma = 0;
+
+ /* Apply the ATA rules first */
+ rc = ata_std_qc_defer(qc);
+ if (rc)
+ return rc;
+
+ if (qc->tf.protocol == ATAPI_PROT_DMA ||
+ qc->tf.protocol == ATA_PROT_DMA)
+ dma = 1;
+
+ /* If the other port is not live then issue the command */
+ if (alt == NULL || !alt->qc_active) {
+ if (dma)
+ host->private_data = qc->ap;
+ return 0;
+ }
+ /* If there is a live DMA command then wait */
+ if (host->private_data != NULL)
+ return ATA_DEFER_PORT;
+ if (dma)
+ /* Cannot overlap our DMA command */
+ return ATA_DEFER_PORT;
+ return 0;
+}
+
+/**
+ * cmd64x_interrupt - ATA host interrupt handler
+ * @irq: irq line (unused)
+ * @dev_instance: pointer to our ata_host information structure
+ *
+ * Our interrupt handler for PCI IDE devices. Calls
+ * ata_sff_host_intr() for each port that is flagging an IRQ. We cannot
+ * use the defaults as we need to avoid touching status/altstatus during
+ * a DMA.
+ *
+ * LOCKING:
+ * Obtains host lock during operation.
+ *
+ * RETURNS:
+ * IRQ_NONE or IRQ_HANDLED.
+ */
+irqreturn_t cmd64x_interrupt(int irq, void *dev_instance)
+{
+ struct ata_host *host = dev_instance;
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ unsigned int i;
+ unsigned int handled = 0;
+ unsigned long flags;
+ static const u8 irq_reg[2] = { CFR, ARTTIM23 };
+ static const u8 irq_mask[2] = { 1 << 2, 1 << 4 };
+
+ /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */
+ spin_lock_irqsave(&host->lock, flags);
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap;
+ u8 reg;
+
+ pci_read_config_byte(pdev, irq_reg[i], &reg);
+ ap = host->ports[i];
+ if (ap && (reg & irq_mask[i]) &&
+ !(ap->flags & ATA_FLAG_DISABLED)) {
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->link.active_tag);
+ if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) &&
+ (qc->flags & ATA_QCFLAG_ACTIVE))
+ handled |= ata_sff_host_intr(ap, qc);
+ }
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
static struct scsi_host_template cmd64x_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
@@ -273,6 +365,8 @@ static const struct ata_port_operations cmd64x_base_ops = {
.inherits = &ata_bmdma_port_ops,
.set_piomode = cmd64x_set_piomode,
.set_dmamode = cmd64x_set_dmamode,
+ .bmdma_stop = cmd64x_bmdma_stop,
+ .qc_defer = cmd64x_qc_defer,
};
static struct ata_port_operations cmd64x_port_ops = {
@@ -282,7 +376,6 @@ static struct ata_port_operations cmd64x_port_ops = {
static struct ata_port_operations cmd646r1_port_ops = {
.inherits = &cmd64x_base_ops,
- .bmdma_stop = cmd646r1_bmdma_stop,
.cable_detect = ata_cable_40wire,
};
@@ -290,12 +383,11 @@ static struct ata_port_operations cmd648_port_ops = {
.inherits = &cmd64x_base_ops,
.bmdma_stop = cmd648_bmdma_stop,
.cable_detect = cmd648_cable_detect,
+ .qc_defer = ata_std_qc_defer
};
static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
- u32 class_rev;
-
static const struct ata_port_info cmd_info[6] = {
{ /* CMD 643 - no UDMA */
.flags = ATA_FLAG_SLAVE_POSS,
@@ -340,40 +432,43 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
const struct ata_port_info *ppi[] = { &cmd_info[id->driver_data], NULL };
u8 mrdmode;
int rc;
+ struct ata_host *host;
rc = pcim_enable_device(pdev);
if (rc)
return rc;
- pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev);
- class_rev &= 0xFF;
-
if (id->driver_data == 0) /* 643 */
ata_pci_bmdma_clear_simplex(pdev);
if (pdev->device == PCI_DEVICE_ID_CMD_646) {
/* Does UDMA work ? */
- if (class_rev > 4)
+ if (pdev->revision > 4)
ppi[0] = &cmd_info[2];
/* Early rev with other problems ? */
- else if (class_rev == 1)
+ else if (pdev->revision == 1)
ppi[0] = &cmd_info[3];
}
+
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
pci_read_config_byte(pdev, MRDMODE, &mrdmode);
mrdmode &= ~ 0x30; /* IRQ set up */
mrdmode |= 0x02; /* Memory read line enable */
pci_write_config_byte(pdev, MRDMODE, mrdmode);
- /* Force PIO 0 here.. */
-
/* PPC specific fixup copied from old driver */
#ifdef CONFIG_PPC
pci_write_config_byte(pdev, UDIDETCR0, 0xF0);
#endif
+ rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+ if (rc)
+ return rc;
+ /* We use this pointer to track the AP which has DMA running */
+ host->private_data = NULL;
- return ata_pci_sff_init_one(pdev, ppi, &cmd64x_sht, NULL);
+ pci_set_master(pdev);
+ return ata_pci_sff_activate_host(host, cmd64x_interrupt, &cmd64x_sht);
}
#ifdef CONFIG_PM
diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
index 0df83cf7423..95ebdac517f 100644
--- a/drivers/ata/pata_cs5520.c
+++ b/drivers/ata/pata_cs5520.c
@@ -90,48 +90,12 @@ static void cs5520_set_timings(struct ata_port *ap, struct ata_device *adev, int
}
/**
- * cs5520_enable_dma - turn on DMA bits
- *
- * Turn on the DMA bits for this disk. Needed because the BIOS probably
- * has not done the work for us. Belongs in the core SATA code.
- */
-
-static void cs5520_enable_dma(struct ata_port *ap, struct ata_device *adev)
-{
- /* Set the DMA enable/disable flag */
- u8 reg = ioread8(ap->ioaddr.bmdma_addr + 0x02);
- reg |= 1<<(adev->devno + 5);
- iowrite8(reg, ap->ioaddr.bmdma_addr + 0x02);
-}
-
-/**
- * cs5520_set_dmamode - program DMA timings
- * @ap: ATA port
- * @adev: ATA device
- *
- * Program the DMA mode timings for the controller according to the pio
- * clocking table. Note that this device sets the DMA timings to PIO
- * mode values. This may seem bizarre but the 5520 architecture talks
- * PIO mode to the disk and DMA mode to the controller so the underlying
- * transfers are PIO timed.
- */
-
-static void cs5520_set_dmamode(struct ata_port *ap, struct ata_device *adev)
-{
- static const int dma_xlate[3] = { XFER_PIO_0, XFER_PIO_3, XFER_PIO_4 };
- cs5520_set_timings(ap, adev, dma_xlate[adev->dma_mode]);
- cs5520_enable_dma(ap, adev);
-}
-
-/**
* cs5520_set_piomode - program PIO timings
* @ap: ATA port
* @adev: ATA device
*
* Program the PIO mode timings for the controller according to the pio
- * clocking table. We know pio_mode will equal dma_mode because of the
- * CS5520 architecture. At least once we turned DMA on and wrote a
- * mode setter.
+ * clocking table.
*/
static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev)
@@ -149,7 +113,6 @@ static struct ata_port_operations cs5520_port_ops = {
.qc_prep = ata_sff_dumb_qc_prep,
.cable_detect = ata_cable_40wire,
.set_piomode = cs5520_set_piomode,
- .set_dmamode = cs5520_set_dmamode,
};
static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
index 403f56165ce..71cef9a962d 100644
--- a/drivers/ata/pata_cs5535.c
+++ b/drivers/ata/pata_cs5535.c
@@ -4,7 +4,7 @@
* Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* based upon cs5535.c from AMD <Jens.Altmann@amd.com> as cleaned up and
- * made readable and Linux style by Wolfgang Zuleger <wolfgang.zuleger@gmx.de
+ * made readable and Linux style by Wolfgang Zuleger <wolfgang.zuleger@gmx.de>
* and Alexander Kiausch <alex.kiausch@t-online.de>
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
index 6da4cb486c8..ffee3978ec8 100644
--- a/drivers/ata/pata_cs5536.c
+++ b/drivers/ata/pata_cs5536.c
@@ -224,7 +224,7 @@ static struct scsi_host_template cs5536_sht = {
};
static struct ata_port_operations cs5536_port_ops = {
- .inherits = &ata_bmdma_port_ops,
+ .inherits = &ata_bmdma32_port_ops,
.cable_detect = cs5536_cable_detect,
.set_piomode = cs5536_set_piomode,
.set_dmamode = cs5536_set_dmamode,
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index 2a6412f5d11..b2e71e6473e 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -2,6 +2,7 @@
* pata_efar.c - EFAR PIIX clone controller driver
*
* (C) 2005 Red Hat
+ * (C) 2009 Bartlomiej Zolnierkiewicz
*
* Some parts based on ata_piix.c by Jeff Garzik and others.
*
@@ -118,12 +119,12 @@ static void efar_set_piomode (struct ata_port *ap, struct ata_device *adev)
int shift = 4 * ap->port_no;
u8 slave_data;
- idetm_data &= 0xCC0F;
+ idetm_data &= 0xFF0F;
idetm_data |= (control << 4);
/* Slave timing in separate register */
pci_read_config_byte(dev, 0x44, &slave_data);
- slave_data &= 0x0F << shift;
+ slave_data &= ap->port_no ? 0x0F : 0xF0;
slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << shift;
pci_write_config_byte(dev, 0x44, slave_data);
}
@@ -200,7 +201,7 @@ static void efar_set_dmamode (struct ata_port *ap, struct ata_device *adev)
master_data &= 0xFF4F; /* Mask out IORDY|TIME1|DMAONLY */
master_data |= control << 4;
pci_read_config_byte(dev, 0x44, &slave_data);
- slave_data &= (0x0F + 0xE1 * ap->port_no);
+ slave_data &= ap->port_no ? 0x0F : 0xF0;
/* Load the matching timing */
slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->port_no ? 4 : 0);
pci_write_config_byte(dev, 0x44, slave_data);
@@ -251,7 +252,7 @@ static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
+ .mwdma_mask = ATA_MWDMA12_ONLY,
.udma_mask = ATA_UDMA4,
.port_ops = &efar_ops,
};
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index d7f2da127d1..0bd48e8f21b 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -27,7 +27,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_hpt366"
-#define DRV_VERSION "0.6.2"
+#define DRV_VERSION "0.6.7"
struct hpt_clock {
u8 xfer_mode;
@@ -36,24 +36,22 @@ struct hpt_clock {
/* key for bus clock timings
* bit
- * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW
- * DMA. cycles = value + 1
- * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW
- * DMA. cycles = value + 1
- * 9:12 cmd_high_time. inactive time of DIOW_/DIOR_ during task file
+ * 0:3 data_high_time. Inactive time of DIOW_/DIOR_ for PIO and MW DMA.
+ * cycles = value + 1
+ * 4:7 data_low_time. Active time of DIOW_/DIOR_ for PIO and MW DMA.
+ * cycles = value + 1
+ * 8:11 cmd_high_time. Inactive time of DIOW_/DIOR_ during task file
* register access.
- * 13:17 cmd_low_time. active time of DIOW_/DIOR_ during task file
+ * 12:15 cmd_low_time. Active time of DIOW_/DIOR_ during task file
* register access.
- * 18:21 udma_cycle_time. clock freq and clock cycles for UDMA xfer.
- * during task file register access.
- * 22:24 pre_high_time. time to initialize 1st cycle for PIO and MW DMA
- * xfer.
- * 25:27 cmd_pre_high_time. time to initialize 1st PIO cycle for task
+ * 16:18 udma_cycle_time. Clock cycles for UDMA xfer?
+ * 19:21 pre_high_time. Time to initialize 1st cycle for PIO and MW DMA xfer.
+ * 22:24 cmd_pre_high_time. Time to initialize 1st PIO cycle for task file
* register access.
- * 28 UDMA enable
- * 29 DMA enable
- * 30 PIO_MST enable. if set, the chip is in bus master mode during
- * PIO.
+ * 28 UDMA enable.
+ * 29 DMA enable.
+ * 30 PIO_MST enable. If set, the chip is in bus master mode during
+ * PIO xfer.
* 31 FIFO enable.
*/
@@ -344,7 +342,6 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
const struct ata_port_info *ppi[] = { &info_hpt366, NULL };
void *hpriv = NULL;
- u32 class_rev;
u32 reg1;
int rc;
@@ -352,13 +349,10 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
if (rc)
return rc;
- pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
- class_rev &= 0xFF;
-
/* May be a later chip in disguise. Check */
/* Newer chips are not in the HPT36x driver. Ignore them */
- if (class_rev > 2)
- return -ENODEV;
+ if (dev->revision > 2)
+ return -ENODEV;
hpt36x_init_chipset(dev);
diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c
index d0a7df2e5ca..4224cfccede 100644
--- a/drivers/ata/pata_hpt37x.c
+++ b/drivers/ata/pata_hpt37x.c
@@ -24,7 +24,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_hpt37x"
-#define DRV_VERSION "0.6.12"
+#define DRV_VERSION "0.6.14"
struct hpt_clock {
u8 xfer_speed;
@@ -303,72 +303,79 @@ static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask)
}
/**
- * hpt37x_pre_reset - reset the hpt37x bus
- * @link: ATA link to reset
- * @deadline: deadline jiffies for the operation
+ * hpt37x_cable_detect - Detect the cable type
+ * @ap: ATA port to detect on
*
- * Perform the initial reset handling for the 370/372 and 374 func 0
+ * Return the cable type attached to this port
*/
-static int hpt37x_pre_reset(struct ata_link *link, unsigned long deadline)
+static int hpt37x_cable_detect(struct ata_port *ap)
{
- u8 scr2, ata66;
- struct ata_port *ap = link->ap;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- static const struct pci_bits hpt37x_enable_bits[] = {
- { 0x50, 1, 0x04, 0x04 },
- { 0x54, 1, 0x04, 0x04 }
- };
- if (!pci_test_config_bits(pdev, &hpt37x_enable_bits[ap->port_no]))
- return -ENOENT;
+ u8 scr2, ata66;
pci_read_config_byte(pdev, 0x5B, &scr2);
pci_write_config_byte(pdev, 0x5B, scr2 & ~0x01);
+
+ udelay(10); /* debounce */
+
/* Cable register now active */
pci_read_config_byte(pdev, 0x5A, &ata66);
/* Restore state */
pci_write_config_byte(pdev, 0x5B, scr2);
if (ata66 & (2 >> ap->port_no))
- ap->cbl = ATA_CBL_PATA40;
+ return ATA_CBL_PATA40;
else
- ap->cbl = ATA_CBL_PATA80;
-
- /* Reset the state machine */
- pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
- udelay(100);
-
- return ata_sff_prereset(link, deadline);
+ return ATA_CBL_PATA80;
}
-static int hpt374_fn1_pre_reset(struct ata_link *link, unsigned long deadline)
+/**
+ * hpt374_fn1_cable_detect - Detect the cable type
+ * @ap: ATA port to detect on
+ *
+ * Return the cable type attached to this port
+ */
+
+static int hpt374_fn1_cable_detect(struct ata_port *ap)
{
- static const struct pci_bits hpt37x_enable_bits[] = {
- { 0x50, 1, 0x04, 0x04 },
- { 0x54, 1, 0x04, 0x04 }
- };
- u16 mcr3;
- u8 ata66;
- struct ata_port *ap = link->ap;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
unsigned int mcrbase = 0x50 + 4 * ap->port_no;
-
- if (!pci_test_config_bits(pdev, &hpt37x_enable_bits[ap->port_no]))
- return -ENOENT;
+ u16 mcr3;
+ u8 ata66;
/* Do the extra channel work */
pci_read_config_word(pdev, mcrbase + 2, &mcr3);
- /* Set bit 15 of 0x52 to enable TCBLID as input
- */
+ /* Set bit 15 of 0x52 to enable TCBLID as input */
pci_write_config_word(pdev, mcrbase + 2, mcr3 | 0x8000);
pci_read_config_byte(pdev, 0x5A, &ata66);
/* Reset TCBLID/FCBLID to output */
pci_write_config_word(pdev, mcrbase + 2, mcr3);
if (ata66 & (2 >> ap->port_no))
- ap->cbl = ATA_CBL_PATA40;
+ return ATA_CBL_PATA40;
else
- ap->cbl = ATA_CBL_PATA80;
+ return ATA_CBL_PATA80;
+}
+
+/**
+ * hpt37x_pre_reset - reset the hpt37x bus
+ * @link: ATA link to reset
+ * @deadline: deadline jiffies for the operation
+ *
+ * Perform the initial reset handling for the HPT37x.
+ */
+
+static int hpt37x_pre_reset(struct ata_link *link, unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ static const struct pci_bits hpt37x_enable_bits[] = {
+ { 0x50, 1, 0x04, 0x04 },
+ { 0x54, 1, 0x04, 0x04 }
+ };
+ if (!pci_test_config_bits(pdev, &hpt37x_enable_bits[ap->port_no]))
+ return -ENOENT;
/* Reset the state machine */
pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37);
@@ -404,9 +411,8 @@ static void hpt370_set_piomode(struct ata_port *ap, struct ata_device *adev)
pci_read_config_dword(pdev, addr1, &reg);
mode = hpt37x_find_mode(ap, adev->pio_mode);
- mode &= ~0x8000000; /* No FIFO in PIO */
- mode &= ~0x30070000; /* Leave config bits alone */
- reg &= 0x30070000; /* Strip timing bits */
+ mode &= 0xCFC3FFFF; /* Leave DMA bits alone */
+ reg &= ~0xCFC3FFFF; /* Strip timing bits */
pci_write_config_dword(pdev, addr1, reg | mode);
}
@@ -423,8 +429,7 @@ static void hpt370_set_dmamode(struct ata_port *ap, struct ata_device *adev)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u32 addr1, addr2;
- u32 reg;
- u32 mode;
+ u32 reg, mode, mask;
u8 fast;
addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no);
@@ -436,11 +441,12 @@ static void hpt370_set_dmamode(struct ata_port *ap, struct ata_device *adev)
fast |= 0x01;
pci_write_config_byte(pdev, addr2, fast);
+ mask = adev->dma_mode < XFER_UDMA_0 ? 0x31C001FF : 0x303C0000;
+
pci_read_config_dword(pdev, addr1, &reg);
mode = hpt37x_find_mode(ap, adev->dma_mode);
- mode |= 0x8000000; /* FIFO in MWDMA or UDMA */
- mode &= ~0xC0000000; /* Leave config bits alone */
- reg &= 0xC0000000; /* Strip timing bits */
+ mode &= mask;
+ reg &= ~mask;
pci_write_config_dword(pdev, addr1, reg | mode);
}
@@ -508,9 +514,8 @@ static void hpt372_set_piomode(struct ata_port *ap, struct ata_device *adev)
mode = hpt37x_find_mode(ap, adev->pio_mode);
printk("Find mode for %d reports %X\n", adev->pio_mode, mode);
- mode &= ~0x80000000; /* No FIFO in PIO */
- mode &= ~0x30070000; /* Leave config bits alone */
- reg &= 0x30070000; /* Strip timing bits */
+ mode &= 0xCFC3FFFF; /* Leave DMA bits alone */
+ reg &= ~0xCFC3FFFF; /* Strip timing bits */
pci_write_config_dword(pdev, addr1, reg | mode);
}
@@ -527,8 +532,7 @@ static void hpt372_set_dmamode(struct ata_port *ap, struct ata_device *adev)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u32 addr1, addr2;
- u32 reg;
- u32 mode;
+ u32 reg, mode, mask;
u8 fast;
addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no);
@@ -539,12 +543,13 @@ static void hpt372_set_dmamode(struct ata_port *ap, struct ata_device *adev)
fast &= ~0x07;
pci_write_config_byte(pdev, addr2, fast);
+ mask = adev->dma_mode < XFER_UDMA_0 ? 0x31C001FF : 0x303C0000;
+
pci_read_config_dword(pdev, addr1, &reg);
mode = hpt37x_find_mode(ap, adev->dma_mode);
printk("Find mode for DMA %d reports %X\n", adev->dma_mode, mode);
- mode &= ~0xC0000000; /* Leave config bits alone */
- mode |= 0x80000000; /* FIFO in MWDMA or UDMA */
- reg &= 0xC0000000; /* Strip timing bits */
+ mode &= mask;
+ reg &= ~mask;
pci_write_config_dword(pdev, addr1, reg | mode);
}
@@ -584,6 +589,7 @@ static struct ata_port_operations hpt370_port_ops = {
.bmdma_stop = hpt370_bmdma_stop,
.mode_filter = hpt370_filter,
+ .cable_detect = hpt37x_cable_detect,
.set_piomode = hpt370_set_piomode,
.set_dmamode = hpt370_set_dmamode,
.prereset = hpt37x_pre_reset,
@@ -608,6 +614,7 @@ static struct ata_port_operations hpt372_port_ops = {
.bmdma_stop = hpt37x_bmdma_stop,
+ .cable_detect = hpt37x_cable_detect,
.set_piomode = hpt372_set_piomode,
.set_dmamode = hpt372_set_dmamode,
.prereset = hpt37x_pre_reset,
@@ -620,7 +627,8 @@ static struct ata_port_operations hpt372_port_ops = {
static struct ata_port_operations hpt374_fn1_port_ops = {
.inherits = &hpt372_port_ops,
- .prereset = hpt374_fn1_pre_reset,
+ .cable_detect = hpt374_fn1_cable_detect,
+ .prereset = hpt37x_pre_reset,
};
/**
@@ -791,9 +799,8 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
static const int MHz[4] = { 33, 40, 50, 66 };
void *private_data = NULL;
const struct ata_port_info *ppi[] = { NULL, NULL };
-
+ u8 rev = dev->revision;
u8 irqmask;
- u32 class_rev;
u8 mcr1;
u32 freq;
int prefer_dpll = 1;
@@ -808,19 +815,16 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
if (rc)
return rc;
- pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
- class_rev &= 0xFF;
-
if (dev->device == PCI_DEVICE_ID_TTI_HPT366) {
/* May be a later chip in disguise. Check */
/* Older chips are in the HPT366 driver. Ignore them */
- if (class_rev < 3)
+ if (rev < 3)
return -ENODEV;
/* N series chips have their own driver. Ignore */
- if (class_rev == 6)
+ if (rev == 6)
return -ENODEV;
- switch(class_rev) {
+ switch(rev) {
case 3:
ppi[0] = &info_hpt370;
chip_table = &hpt370;
@@ -836,28 +840,29 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
chip_table = &hpt372;
break;
default:
- printk(KERN_ERR "pata_hpt37x: Unknown HPT366 subtype please report (%d).\n", class_rev);
+ printk(KERN_ERR "pata_hpt37x: Unknown HPT366 "
+ "subtype, please report (%d).\n", rev);
return -ENODEV;
}
} else {
switch(dev->device) {
case PCI_DEVICE_ID_TTI_HPT372:
/* 372N if rev >= 2*/
- if (class_rev >= 2)
+ if (rev >= 2)
return -ENODEV;
ppi[0] = &info_hpt372;
chip_table = &hpt372a;
break;
case PCI_DEVICE_ID_TTI_HPT302:
/* 302N if rev > 1 */
- if (class_rev > 1)
+ if (rev > 1)
return -ENODEV;
ppi[0] = &info_hpt372;
/* Check this */
chip_table = &hpt302;
break;
case PCI_DEVICE_ID_TTI_HPT371:
- if (class_rev > 1)
+ if (rev > 1)
return -ENODEV;
ppi[0] = &info_hpt372;
chip_table = &hpt371;
diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c
index 3d59fe0a408..9a09a1b11ca 100644
--- a/drivers/ata/pata_hpt3x2n.c
+++ b/drivers/ata/pata_hpt3x2n.c
@@ -25,7 +25,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_hpt3x2n"
-#define DRV_VERSION "0.3.4"
+#define DRV_VERSION "0.3.7"
enum {
HPT_PCI_FAST = (1 << 31),
@@ -80,14 +80,13 @@ static struct hpt_clock hpt3x2n_clocks[] = {
{ XFER_MW_DMA_2, 0x2c829c62 },
{ XFER_MW_DMA_1, 0x2c829c66 },
- { XFER_MW_DMA_0, 0x2c829d2c },
+ { XFER_MW_DMA_0, 0x2c829d2e },
{ XFER_PIO_4, 0x0c829c62 },
{ XFER_PIO_3, 0x0c829c84 },
{ XFER_PIO_2, 0x0c829ca6 },
{ XFER_PIO_1, 0x0d029d26 },
{ XFER_PIO_0, 0x0d029d5e },
- { 0, 0x0d029d5e }
};
/**
@@ -128,12 +127,15 @@ static int hpt3x2n_cable_detect(struct ata_port *ap)
pci_read_config_byte(pdev, 0x5B, &scr2);
pci_write_config_byte(pdev, 0x5B, scr2 & ~0x01);
+
+ udelay(10); /* debounce */
+
/* Cable register now active */
pci_read_config_byte(pdev, 0x5A, &ata66);
/* Restore state */
pci_write_config_byte(pdev, 0x5B, scr2);
- if (ata66 & (1 << ap->port_no))
+ if (ata66 & (2 >> ap->port_no))
return ATA_CBL_PATA40;
else
return ATA_CBL_PATA80;
@@ -185,9 +187,8 @@ static void hpt3x2n_set_piomode(struct ata_port *ap, struct ata_device *adev)
pci_read_config_dword(pdev, addr1, &reg);
mode = hpt3x2n_find_mode(ap, adev->pio_mode);
- mode &= ~0x8000000; /* No FIFO in PIO */
- mode &= ~0x30070000; /* Leave config bits alone */
- reg &= 0x30070000; /* Strip timing bits */
+ mode &= 0xCFC3FFFF; /* Leave DMA bits alone */
+ reg &= ~0xCFC3FFFF; /* Strip timing bits */
pci_write_config_dword(pdev, addr1, reg | mode);
}
@@ -204,8 +205,7 @@ static void hpt3x2n_set_dmamode(struct ata_port *ap, struct ata_device *adev)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u32 addr1, addr2;
- u32 reg;
- u32 mode;
+ u32 reg, mode, mask;
u8 fast;
addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no);
@@ -216,11 +216,12 @@ static void hpt3x2n_set_dmamode(struct ata_port *ap, struct ata_device *adev)
fast &= ~0x07;
pci_write_config_byte(pdev, addr2, fast);
+ mask = adev->dma_mode < XFER_UDMA_0 ? 0x31C001FF : 0x303C0000;
+
pci_read_config_dword(pdev, addr1, &reg);
mode = hpt3x2n_find_mode(ap, adev->dma_mode);
- mode |= 0x8000000; /* FIFO in MWDMA or UDMA */
- mode &= ~0xC0000000; /* Leave config bits alone */
- reg &= 0xC0000000; /* Strip timing bits */
+ mode &= mask;
+ reg &= ~mask;
pci_write_config_dword(pdev, addr1, reg | mode);
}
@@ -447,10 +448,8 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
.port_ops = &hpt3x2n_port_ops
};
const struct ata_port_info *ppi[] = { &info, NULL };
-
+ u8 rev = dev->revision;
u8 irqmask;
- u32 class_rev;
-
unsigned int pci_mhz;
unsigned int f_low, f_high;
int adjust;
@@ -462,26 +461,23 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
if (rc)
return rc;
- pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
- class_rev &= 0xFF;
-
switch(dev->device) {
case PCI_DEVICE_ID_TTI_HPT366:
- if (class_rev < 6)
+ if (rev < 6)
return -ENODEV;
break;
case PCI_DEVICE_ID_TTI_HPT371:
- if (class_rev < 2)
+ if (rev < 2)
return -ENODEV;
/* 371N if rev > 1 */
break;
case PCI_DEVICE_ID_TTI_HPT372:
/* 372N if rev >= 2*/
- if (class_rev < 2)
+ if (rev < 2)
return -ENODEV;
break;
case PCI_DEVICE_ID_TTI_HPT302:
- if (class_rev < 2)
+ if (rev < 2)
return -ENODEV;
break;
case PCI_DEVICE_ID_TTI_HPT372N:
diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
index 7e310253b36..c86c71639a9 100644
--- a/drivers/ata/pata_hpt3x3.c
+++ b/drivers/ata/pata_hpt3x3.c
@@ -255,8 +255,17 @@ static int hpt3x3_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
#ifdef CONFIG_PM
static int hpt3x3_reinit_one(struct pci_dev *dev)
{
+ struct ata_host *host = dev_get_drvdata(&dev->dev);
+ int rc;
+
+ rc = ata_pci_device_do_resume(dev);
+ if (rc)
+ return rc;
+
hpt3x3_init_chipset(dev);
- return ata_pci_device_resume(dev);
+
+ ata_host_resume(host);
+ return 0;
}
#endif
diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c
index f156da8076f..8f3325adceb 100644
--- a/drivers/ata/pata_it8213.c
+++ b/drivers/ata/pata_it8213.c
@@ -22,7 +22,7 @@
#define DRV_VERSION "0.0.3"
/**
- * it8213_pre_reset - check for 40/80 pin
+ * it8213_pre_reset - probe begin
* @link: link
* @deadline: deadline jiffies for the operation
*
@@ -92,18 +92,17 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev)
{ 2, 1 },
{ 2, 3 }, };
- if (pio > 2)
- control |= 1; /* TIME1 enable */
+ if (pio > 1)
+ control |= 1; /* TIME */
if (ata_pio_need_iordy(adev)) /* PIO 3/4 require IORDY */
- control |= 2; /* IORDY enable */
+ control |= 2; /* IE */
/* Bit 2 is set for ATAPI on the IT8213 - reverse of ICH/PIIX */
if (adev->class != ATA_DEV_ATA)
- control |= 4;
+ control |= 4; /* PPE */
pci_read_config_word(dev, idetm_port, &idetm_data);
- /* Enable PPE, IE and TIME as appropriate */
-
+ /* Set PPE, IE, and TIME as appropriate */
if (adev->devno == 0) {
idetm_data &= 0xCCF0;
idetm_data |= control;
@@ -112,17 +111,17 @@ static void it8213_set_piomode (struct ata_port *ap, struct ata_device *adev)
} else {
u8 slave_data;
- idetm_data &= 0xCC0F;
+ idetm_data &= 0xFF0F;
idetm_data |= (control << 4);
/* Slave timing in separate register */
pci_read_config_byte(dev, 0x44, &slave_data);
slave_data &= 0xF0;
- slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << 4;
+ slave_data |= (timings[pio][0] << 2) | timings[pio][1];
pci_write_config_byte(dev, 0x44, slave_data);
}
- idetm_data |= 0x4000; /* Ensure SITRE is enabled */
+ idetm_data |= 0x4000; /* Ensure SITRE is set */
pci_write_config_word(dev, idetm_port, idetm_data);
}
@@ -173,10 +172,10 @@ static void it8213_set_dmamode (struct ata_port *ap, struct ata_device *adev)
udma_enable |= (1 << devid);
- /* Load the UDMA mode number */
+ /* Load the UDMA cycle time */
pci_read_config_word(dev, 0x4A, &udma_timing);
udma_timing &= ~(3 << (4 * devid));
- udma_timing |= (udma & 3) << (4 * devid);
+ udma_timing |= u_speed << (4 * devid);
pci_write_config_word(dev, 0x4A, udma_timing);
/* Load the clock selection */
@@ -211,7 +210,7 @@ static void it8213_set_dmamode (struct ata_port *ap, struct ata_device *adev)
master_data &= 0xFF4F; /* Mask out IORDY|TIME1|DMAONLY */
master_data |= control << 4;
pci_read_config_byte(dev, 0x44, &slave_data);
- slave_data &= (0x0F + 0xE1 * ap->port_no);
+ slave_data &= 0xF0;
/* Load the matching timing */
slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << (ap->port_no ? 4 : 0);
pci_write_config_byte(dev, 0x44, slave_data);
@@ -263,7 +262,7 @@ static int it8213_init_one (struct pci_dev *pdev, const struct pci_device_id *en
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
+ .mwdma_mask = ATA_MWDMA12_ONLY,
.udma_mask = ATA_UDMA4, /* FIXME: want UDMA 100? */
.port_ops = &it8213_ops,
};
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 188bc2fcd22..edc5c1fed15 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -955,7 +955,7 @@ static int it821x_reinit_one(struct pci_dev *pdev)
static const struct pci_device_id it821x[] = {
{ PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), },
{ PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8212), },
- { PCI_VDEVICE(RDC, 0x1010), },
+ { PCI_VDEVICE(RDC, PCI_DEVICE_ID_RDC_D1010), },
{ },
};
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 6932e56d179..9df1ff7e1ea 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -25,6 +25,13 @@
* http://www.ryston.cz/petr/vlb/pdc20230b.html
* http://www.ryston.cz/petr/vlb/pdc20230c.html
* http://www.ryston.cz/petr/vlb/pdc20630.html
+ * QDI65x0:
+ * http://www.ryston.cz/petr/vlb/qd6500.html
+ * http://www.ryston.cz/petr/vlb/qd6580.html
+ *
+ * QDI65x0 probe code based on drivers/ide/legacy/qd65xx.c
+ * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by
+ * Samuel Thibault <samuel.thibault@ens-lyon.org>
*
* Unsupported but docs exist:
* Appian/Adaptec AIC25VL01/Cirrus Logic PD7220
@@ -35,7 +42,7 @@
* the MPIIX where the tuning is PCI side but the IDE is "ISA side".
*
* Specific support is included for the ht6560a/ht6560b/opti82c611a/
- * opti82c465mv/promise 20230c/20630/winbond83759A
+ * opti82c465mv/promise 20230c/20630/qdi65x0/winbond83759A
*
* Use the autospeed and pio_mask options with:
* Appian ADI/2 aka CLPD7220 or AIC25VL01.
@@ -672,7 +679,7 @@ static void qdi6580dp_set_piomode(struct ata_port *ap, struct ata_device *adev)
outb(timing, ld_qdi->timing + 2 * ap->port_no);
/* Clear the FIFO */
if (adev->class != ATA_DEV_ATA)
- outb(0x5F, ld_qdi->timing + 3);
+ outb(0x5F, (ld_qdi->timing & 0xFFF0) + 3);
}
/**
@@ -707,7 +714,7 @@ static void qdi6580_set_piomode(struct ata_port *ap, struct ata_device *adev)
outb(timing, ld_qdi->timing + 2 * adev->devno);
/* Clear the FIFO */
if (adev->class != ATA_DEV_ATA)
- outb(0x5F, ld_qdi->timing + 3);
+ outb(0x5F, (ld_qdi->timing & 0xFFF0) + 3);
}
/**
@@ -787,6 +794,7 @@ static struct ata_port_operations qdi6580_port_ops = {
static struct ata_port_operations qdi6580dp_port_ops = {
.inherits = &legacy_base_port_ops,
.set_piomode = qdi6580dp_set_piomode,
+ .qc_issue = qdi_qc_issue,
.sff_data_xfer = vlb32_data_xfer,
};
diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c
index 2096fb737f8..950da39cae3 100644
--- a/drivers/ata/pata_marvell.c
+++ b/drivers/ata/pata_marvell.c
@@ -58,7 +58,7 @@ static int marvell_pata_active(struct pci_dev *pdev)
}
/**
- * marvell_pre_reset - check for 40/80 pin
+ * marvell_pre_reset - probe begin
* @link: link
* @deadline: deadline jiffies for the operation
*
diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c
index 773b1590b49..061aa1c41a4 100644
--- a/drivers/ata/pata_ns87415.c
+++ b/drivers/ata/pata_ns87415.c
@@ -325,6 +325,13 @@ static struct scsi_host_template ns87415_sht = {
ATA_BMDMA_SHT(DRV_NAME),
};
+static void ns87415_fixup(struct pci_dev *pdev)
+{
+ /* Select 512 byte sectors */
+ pci_write_config_byte(pdev, 0x55, 0xEE);
+ /* Select PIO0 8bit clocking */
+ pci_write_config_byte(pdev, 0x54, 0xB7);
+}
/**
* ns87415_init_one - Register 87415 ATA PCI device with kernel services
@@ -371,10 +378,8 @@ static int ns87415_init_one (struct pci_dev *pdev, const struct pci_device_id *e
if (rc)
return rc;
- /* Select 512 byte sectors */
- pci_write_config_byte(pdev, 0x55, 0xEE);
- /* Select PIO0 8bit clocking */
- pci_write_config_byte(pdev, 0x54, 0xB7);
+ ns87415_fixup(pdev);
+
return ata_pci_sff_init_one(pdev, ppi, &ns87415_sht, NULL);
}
@@ -384,6 +389,23 @@ static const struct pci_device_id ns87415_pci_tbl[] = {
{ } /* terminate list */
};
+#ifdef CONFIG_PM
+static int ns87415_reinit_one(struct pci_dev *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ rc = ata_pci_device_do_resume(pdev);
+ if (rc)
+ return rc;
+
+ ns87415_fixup(pdev);
+
+ ata_host_resume(host);
+ return 0;
+}
+#endif
+
static struct pci_driver ns87415_pci_driver = {
.name = DRV_NAME,
.id_table = ns87415_pci_tbl,
@@ -391,7 +413,7 @@ static struct pci_driver ns87415_pci_driver = {
.remove = ata_pci_remove_one,
#ifdef CONFIG_PM
.suspend = ata_pci_device_suspend,
- .resume = ata_pci_device_resume,
+ .resume = ns87415_reinit_one,
#endif
};
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index 84ac5033ac8..9a8687db6b2 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -239,7 +239,7 @@ static int oldpiix_init_one (struct pci_dev *pdev, const struct pci_device_id *e
static const struct ata_port_info info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
+ .mwdma_mask = ATA_MWDMA12_ONLY,
.port_ops = &oldpiix_pata_ops,
};
const struct ata_port_info *ppi[] = { &info, NULL };
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index dc99e26f8e5..1b392c9e853 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -177,9 +177,6 @@ static struct ata_port_operations pcmcia_8bit_port_ops = {
.drain_fifo = pcmcia_8bit_drain_fifo,
};
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
struct pcmcia_config_check {
unsigned long ctl_base;
@@ -252,7 +249,7 @@ static int pcmcia_init_one(struct pcmcia_device *pdev)
struct ata_port *ap;
struct ata_pcmcia_info *info;
struct pcmcia_config_check *stk = NULL;
- int last_ret = 0, last_fn = 0, is_kme = 0, ret = -ENOMEM, p;
+ int is_kme = 0, ret = -ENOMEM, p;
unsigned long io_base, ctl_base;
void __iomem *io_addr, *ctl_addr;
int n_ports = 1;
@@ -271,7 +268,6 @@ static int pcmcia_init_one(struct pcmcia_device *pdev)
pdev->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
pdev->io.IOAddrLines = 3;
pdev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- pdev->irq.IRQInfo1 = IRQ_LEVEL_ID;
pdev->conf.Attributes = CONF_ENABLE_IRQ;
pdev->conf.IntType = INT_MEMORY_AND_IO;
@@ -296,8 +292,13 @@ static int pcmcia_init_one(struct pcmcia_device *pdev)
}
io_base = pdev->io.BasePort1;
ctl_base = stk->ctl_base;
- CS_CHECK(RequestIRQ, pcmcia_request_irq(pdev, &pdev->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(pdev, &pdev->conf));
+ ret = pcmcia_request_irq(pdev, &pdev->irq);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_request_configuration(pdev, &pdev->conf);
+ if (ret)
+ goto failed;
/* iomap */
ret = -ENOMEM;
@@ -351,8 +352,6 @@ static int pcmcia_init_one(struct pcmcia_device *pdev)
kfree(stk);
return 0;
-cs_failed:
- cs_error(pdev, last_fn, last_ret);
failed:
kfree(stk);
info->ndev = 0;
diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c
new file mode 100644
index 00000000000..bfe0180f3ef
--- /dev/null
+++ b/drivers/ata/pata_piccolo.c
@@ -0,0 +1,140 @@
+/*
+ * pata_piccolo.c - Toshiba Piccolo PATA/SATA controller driver.
+ *
+ * This is basically an update to ata_generic.c to add Toshiba Piccolo support
+ * then split out to keep ata_generic "clean".
+ *
+ * Copyright 2005 Red Hat Inc, all rights reserved.
+ *
+ * Elements from ide/pci/generic.c
+ * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org>
+ * Portions (C) Copyright 2002 Red Hat Inc <alan@redhat.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public License
+ *
+ * The timing data tables/programming info are courtesy of the NetBSD driver
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME "pata_piccolo"
+#define DRV_VERSION "0.0.1"
+
+
+
+static void tosh_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ static const u16 pio[6] = { /* For reg 0x50 low word & E088 */
+ 0x0566, 0x0433, 0x0311, 0x0201, 0x0200, 0x0100
+ };
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u16 conf;
+ pci_read_config_word(pdev, 0x50, &conf);
+ conf &= 0xE088;
+ conf |= pio[adev->pio_mode - XFER_PIO_0];
+ pci_write_config_word(pdev, 0x50, conf);
+}
+
+static void tosh_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ u32 conf;
+ pci_read_config_dword(pdev, 0x5C, &conf);
+ conf &= 0x78FFE088; /* Keep the other bits */
+ if (adev->dma_mode >= XFER_UDMA_0) {
+ int udma = adev->dma_mode - XFER_UDMA_0;
+ conf |= 0x80000000;
+ conf |= (udma + 2) << 28;
+ conf |= (2 - udma) * 0x111; /* spread into three nibbles */
+ } else {
+ static const u32 mwdma[4] = {
+ 0x0655, 0x0200, 0x0200, 0x0100
+ };
+ conf |= mwdma[adev->dma_mode - XFER_MW_DMA_0];
+ }
+ pci_write_config_dword(pdev, 0x5C, conf);
+}
+
+
+static struct scsi_host_template tosh_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations tosh_port_ops = {
+ .inherits = &ata_bmdma_port_ops,
+ .cable_detect = ata_cable_unknown,
+ .set_piomode = tosh_set_piomode,
+ .set_dmamode = tosh_set_dmamode
+};
+
+/**
+ * ata_tosh_init - attach generic IDE
+ * @dev: PCI device found
+ * @id: match entry
+ *
+ * Called each time a matching IDE interface is found. We check if the
+ * interface is one we wish to claim and if so we perform any chip
+ * specific hacks then let the ATA layer do the heavy lifting.
+ */
+
+static int ata_tosh_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ static const struct ata_port_info info = {
+ .flags = ATA_FLAG_SLAVE_POSS,
+ .pio_mask = ATA_PIO5,
+ .mwdma_mask = ATA_MWDMA2,
+ .udma_mask = ATA_UDMA2,
+ .port_ops = &tosh_port_ops
+ };
+ const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
+ /* Just one port for the moment */
+ return ata_pci_sff_init_one(dev, ppi, &tosh_sht, NULL);
+}
+
+static struct pci_device_id ata_tosh[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), },
+ { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), },
+ { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), },
+ { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_5), },
+ { 0, },
+};
+
+static struct pci_driver ata_tosh_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ata_tosh,
+ .probe = ata_tosh_init_one,
+ .remove = ata_pci_remove_one,
+#ifdef CONFIG_PM
+ .suspend = ata_pci_device_suspend,
+ .resume = ata_pci_device_resume,
+#endif
+};
+
+static int __init ata_tosh_init(void)
+{
+ return pci_register_driver(&ata_tosh_pci_driver);
+}
+
+
+static void __exit ata_tosh_exit(void)
+{
+ pci_unregister_driver(&ata_tosh_pci_driver);
+}
+
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("Low level driver for Toshiba Piccolo ATA");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, ata_tosh);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(ata_tosh_init);
+module_exit(ata_tosh_exit);
+
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 4401b332eaa..4fd25e737d9 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -139,9 +139,9 @@ static void radisys_set_dmamode (struct ata_port *ap, struct ata_device *adev)
pci_read_config_byte(dev, 0x4A, &udma_mode);
if (adev->xfer_mode == XFER_UDMA_2)
- udma_mode &= ~ (1 << adev->devno);
+ udma_mode &= ~(2 << (adev->devno * 4));
else /* UDMA 4 */
- udma_mode |= (1 << adev->devno);
+ udma_mode |= (2 << (adev->devno * 4));
pci_write_config_byte(dev, 0x4A, udma_mode);
diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c
index c843a1e07c4..237a24d41a2 100644
--- a/drivers/ata/pata_rdc.c
+++ b/drivers/ata/pata_rdc.c
@@ -284,7 +284,7 @@ static struct ata_port_info rdc_port_info = {
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
+ .mwdma_mask = ATA_MWDMA12_ONLY,
.udma_mask = ATA_UDMA5,
.port_ops = &rdc_pata_ops,
};
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index a5e4dfe60b4..2932998fc4c 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -105,11 +105,20 @@ static int rz1000_init_one (struct pci_dev *pdev, const struct pci_device_id *en
#ifdef CONFIG_PM
static int rz1000_reinit_one(struct pci_dev *pdev)
{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ rc = ata_pci_device_do_resume(pdev);
+ if (rc)
+ return rc;
+
/* If this fails on resume (which is a "cant happen" case), we
must stop as any progress risks data loss */
if (rz1000_fifo_disable(pdev))
panic("rz1000 fifo");
- return ata_pci_device_resume(pdev);
+
+ ata_host_resume(host);
+ return 0;
}
#endif
diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
index 4cb649d8d38..a2ace48a461 100644
--- a/drivers/ata/pata_sil680.c
+++ b/drivers/ata/pata_sil680.c
@@ -212,13 +212,11 @@ static struct ata_port_operations sil680_port_ops = {
static u8 sil680_init_chip(struct pci_dev *pdev, int *try_mmio)
{
- u32 class_rev = 0;
u8 tmpbyte = 0;
- pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class_rev);
- class_rev &= 0xff;
/* FIXME: double check */
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, (class_rev) ? 1 : 255);
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
+ pdev->revision ? 1 : 255);
pci_write_config_byte(pdev, 0x80, 0x00);
pci_write_config_byte(pdev, 0x84, 0x00);
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index 488e77bcd22..5c30d56dec8 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -2,7 +2,7 @@
* pata_sis.c - SiS ATA driver
*
* (C) 2005 Red Hat
- * (C) 2007 Bartlomiej Zolnierkiewicz
+ * (C) 2007,2009 Bartlomiej Zolnierkiewicz
*
* Based upon linux/drivers/ide/pci/sis5513.c
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
@@ -829,6 +829,23 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
return ata_pci_sff_init_one(pdev, ppi, &sis_sht, chipset);
}
+#ifdef CONFIG_PM
+static int sis_reinit_one(struct pci_dev *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ int rc;
+
+ rc = ata_pci_device_do_resume(pdev);
+ if (rc)
+ return rc;
+
+ sis_fixup(pdev, host->private_data);
+
+ ata_host_resume(host);
+ return 0;
+}
+#endif
+
static const struct pci_device_id sis_pci_tbl[] = {
{ PCI_VDEVICE(SI, 0x5513), }, /* SiS 5513 */
{ PCI_VDEVICE(SI, 0x5518), }, /* SiS 5518 */
@@ -844,7 +861,7 @@ static struct pci_driver sis_pci_driver = {
.remove = ata_pci_remove_one,
#ifdef CONFIG_PM
.suspend = ata_pci_device_suspend,
- .resume = ata_pci_device_resume,
+ .resume = sis_reinit_one,
#endif
};
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index 88984b803d6..0d97890af68 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -303,14 +303,21 @@ static void via_do_set_mode(struct ata_port *ap, struct ata_device *adev, int mo
}
/* Set UDMA unless device is not UDMA capable */
- if (udma_type && t.udma) {
- u8 cable80_status;
+ if (udma_type) {
+ u8 udma_etc;
- /* Get 80-wire cable detection bit */
- pci_read_config_byte(pdev, 0x50 + offset, &cable80_status);
- cable80_status &= 0x10;
+ pci_read_config_byte(pdev, 0x50 + offset, &udma_etc);
- pci_write_config_byte(pdev, 0x50 + offset, ut | cable80_status);
+ /* clear transfer mode bit */
+ udma_etc &= ~0x20;
+
+ if (t.udma) {
+ /* preserve 80-wire cable detection bit */
+ udma_etc &= 0x10;
+ udma_etc |= ut;
+ }
+
+ pci_write_config_byte(pdev, 0x50 + offset, udma_etc);
}
}
@@ -337,6 +344,32 @@ static void via_set_dmamode(struct ata_port *ap, struct ata_device *adev)
}
/**
+ * via_mode_filter - filter buggy device/mode pairs
+ * @dev: ATA device
+ * @mask: Mode bitmask
+ *
+ * We need to apply some minimal filtering for old controllers and at least
+ * one breed of Transcend SSD. Return the updated mask.
+ */
+
+static unsigned long via_mode_filter(struct ata_device *dev, unsigned long mask)
+{
+ struct ata_host *host = dev->link->ap->host;
+ const struct via_isa_bridge *config = host->private_data;
+ unsigned char model_num[ATA_ID_PROD_LEN + 1];
+
+ if (config->id == PCI_DEVICE_ID_VIA_82C586_0) {
+ ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
+ if (strcmp(model_num, "TS64GSSD25-M") == 0) {
+ ata_dev_printk(dev, KERN_WARNING,
+ "disabling UDMA mode due to reported lockups with this device.\n");
+ mask &= ~ ATA_MASK_UDMA;
+ }
+ }
+ return ata_bmdma_mode_filter(dev, mask);
+}
+
+/**
* via_tf_load - send taskfile registers to host controller
* @ap: Port to which output is sent
* @tf: ATA taskfile register set
@@ -427,6 +460,7 @@ static struct ata_port_operations via_port_ops = {
.prereset = via_pre_reset,
.sff_tf_load = via_tf_load,
.port_start = via_port_start,
+ .mode_filter = via_mode_filter,
};
static struct ata_port_operations via_port_ops_noirq = {
@@ -526,7 +560,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
.port_ops = &via_port_ops
};
const struct ata_port_info *ppi[] = { NULL, NULL };
- struct pci_dev *isa = NULL;
+ struct pci_dev *isa;
const struct via_isa_bridge *config;
static int printed_version;
u8 enable;
@@ -551,15 +585,13 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
if ((isa = pci_get_device(PCI_VENDOR_ID_VIA +
!!(config->flags & VIA_BAD_ID),
config->id, NULL))) {
+ u8 rev = isa->revision;
+ pci_dev_put(isa);
- if (isa->revision >= config->rev_min &&
- isa->revision <= config->rev_max)
+ if (rev >= config->rev_min && rev <= config->rev_max)
break;
- pci_dev_put(isa);
}
- pci_dev_put(isa);
-
if (!(config->flags & VIA_NO_ENABLES)) {
/* 0x40 low bits indicate enabled channels */
pci_read_config_byte(pdev, 0x40 , &enable);
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 172b57e6543..ce4136eea08 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -34,7 +34,7 @@ enum {
SATA_FSL_HOST_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
- ATA_FLAG_PMP | ATA_FLAG_NCQ),
+ ATA_FLAG_PMP | ATA_FLAG_NCQ | ATA_FLAG_AN),
SATA_FSL_MAX_CMDS = SATA_FSL_QUEUE_DEPTH,
SATA_FSL_CMD_HDR_SIZE = 16, /* 4 DWORDS */
@@ -43,9 +43,9 @@ enum {
/*
* SATA-FSL host controller supports a max. of (15+1) direct PRDEs, and
* chained indirect PRDEs upto a max count of 63.
- * We are allocating an array of 63 PRDEs contigiously, but PRDE#15 will
+ * We are allocating an array of 63 PRDEs contiguously, but PRDE#15 will
* be setup as an indirect descriptor, pointing to it's next
- * (contigious) PRDE. Though chained indirect PRDE arrays are
+ * (contiguous) PRDE. Though chained indirect PRDE arrays are
* supported,it will be more efficient to use a direct PRDT and
* a single chain/link to indirect PRDE array/PRDT.
*/
@@ -132,7 +132,7 @@ enum {
INT_ON_SINGL_DEVICE_ERR = (1 << 1),
INT_ON_CMD_COMPLETE = 1,
- INT_ON_ERROR = INT_ON_FATAL_ERR |
+ INT_ON_ERROR = INT_ON_FATAL_ERR | INT_ON_SNOTIFY_UPDATE |
INT_ON_PHYRDY_CHG | INT_ON_SINGL_DEVICE_ERR,
/*
@@ -153,7 +153,7 @@ enum {
IE_ON_CMD_COMPLETE = 1,
DEFAULT_PORT_IRQ_ENABLE_MASK = IE_ON_FATAL_ERR | IE_ON_PHYRDY_CHG |
- IE_ON_SIGNATURE_UPDATE |
+ IE_ON_SIGNATURE_UPDATE | IE_ON_SNOTIFY_UPDATE |
IE_ON_SINGL_DEVICE_ERR | IE_ON_CMD_COMPLETE,
EXT_INDIRECT_SEG_PRD_FLAG = (1 << 31),
@@ -314,7 +314,7 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
u32 ttl_dwords = 0;
/*
- * NOTE : direct & indirect prdt's are contigiously allocated
+ * NOTE : direct & indirect prdt's are contiguously allocated
*/
struct prde *prd = (struct prde *)&((struct command_desc *)
cmd_desc)->prdt;
@@ -992,9 +992,8 @@ static void sata_fsl_error_intr(struct ata_port *ap)
*/
sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError);
- if (unlikely(SError & 0xFFFF0000)) {
+ if (unlikely(SError & 0xFFFF0000))
sata_fsl_scr_write(&ap->link, SCR_ERROR, SError);
- }
DPRINTK("error_intr,hStat=0x%x,CE=0x%x,DE =0x%x,SErr=0x%x\n",
hstatus, cereg, ioread32(hcr_base + DE), SError);
@@ -1007,6 +1006,10 @@ static void sata_fsl_error_intr(struct ata_port *ap)
freeze = 1;
}
+ /* Handle SDB FIS receive & notify update */
+ if (hstatus & INT_ON_SNOTIFY_UPDATE)
+ sata_async_notification(ap);
+
/* Handle PHYRDY change notification */
if (hstatus & INT_ON_PHYRDY_CHG) {
DPRINTK("SATA FSL: PHYRDY change indication\n");
@@ -1070,9 +1073,9 @@ static void sata_fsl_error_intr(struct ata_port *ap)
}
/* record error info */
- if (qc) {
+ if (qc)
qc->err_mask |= err_mask;
- } else
+ else
ehi->err_mask |= err_mask;
ehi->action |= action;
@@ -1103,7 +1106,6 @@ static void sata_fsl_host_intr(struct ata_port *ap)
if (unlikely(SError & 0xFFFF0000)) {
DPRINTK("serror @host_intr : 0x%x\n", SError);
sata_fsl_error_intr(ap);
-
}
if (unlikely(hstatus & INT_ON_ERROR)) {
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 6f5093b7c8c..a8a7be0d06f 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -2217,7 +2217,7 @@ static unsigned int mv_qc_issue_fis(struct ata_queued_cmd *qc)
int err = 0;
ata_tf_to_fis(&qc->tf, link->pmp, 1, (void *)fis);
- err = mv_send_fis(ap, fis, sizeof(fis) / sizeof(fis[0]));
+ err = mv_send_fis(ap, fis, ARRAY_SIZE(fis));
if (err)
return err;
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 1eb4e020eb5..0c82d335c55 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -1975,7 +1975,7 @@ static int nv_swncq_slave_config(struct scsi_device *sdev)
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
if (strncmp(model_num, "Maxtor", 6) == 0) {
- ata_scsi_change_queue_depth(sdev, 1);
+ ata_scsi_change_queue_depth(sdev, 1, SCSI_QDEPTH_DEFAULT);
ata_dev_printk(dev, KERN_NOTICE,
"Disabling SWNCQ mode (depth %x)\n", sdev->queue_depth);
}
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index e6946fc527d..1370df6c420 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -417,6 +417,10 @@ static struct ata_port_operations sil24_ops = {
#endif
};
+static int sata_sil24_msi; /* Disable MSI */
+module_param_named(msi, sata_sil24_msi, bool, S_IRUGO);
+MODULE_PARM_DESC(msi, "Enable MSI (Default: false)");
+
/*
* Use bits 30-31 of port_flags to encode available port numbers.
* Current maxium is 4.
@@ -1340,6 +1344,11 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
sil24_init_controller(host);
+ if (sata_sil24_msi && !pci_enable_msi(pdev)) {
+ dev_printk(KERN_INFO, &pdev->dev, "Using MSI\n");
+ pci_intx(pdev, 0);
+ }
+
pci_set_master(pdev);
return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED,
&sil24_sht);
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
index 66e181345b3..8af23411743 100644
--- a/drivers/atm/ambassador.c
+++ b/drivers/atm/ambassador.c
@@ -2351,6 +2351,7 @@ static void __init amb_check_args (void) {
MODULE_AUTHOR(maintainer_string);
MODULE_DESCRIPTION(description_string);
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("atmsar11.fw");
module_param(debug, ushort, 0644);
module_param(cmds, uint, 0);
module_param(txs, uint, 0);
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index f766cc46b4c..bc53fed89b1 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -2906,8 +2906,8 @@ fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page)
u32 media_index = FORE200E_MEDIA_INDEX(fore200e->bus->read(&fore200e->cp_queues->media_type));
u32 oc3_index;
- if ((media_index < 0) || (media_index > 4))
- media_index = 5;
+ if (media_index > 4)
+ media_index = 5;
switch (fore200e->loop_mode) {
case ATM_LM_NONE: oc3_index = 0;
diff --git a/drivers/atm/he.c b/drivers/atm/he.c
index 70667033a56..e90665876c4 100644
--- a/drivers/atm/he.c
+++ b/drivers/atm/he.c
@@ -2739,7 +2739,7 @@ he_ioctl(struct atm_dev *atm_dev, unsigned int cmd, void __user *arg)
spin_lock_irqsave(&he_dev->global_lock, flags);
switch (reg.type) {
case HE_REGTYPE_PCI:
- if (reg.addr < 0 || reg.addr >= HE_REGMAP_SIZE) {
+ if (reg.addr >= HE_REGMAP_SIZE) {
err = -EINVAL;
break;
}
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index b2c1b37ab2e..f734b345ac7 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -1132,7 +1132,7 @@ static int rx_pkt(struct atm_dev *dev)
IF_ERR(printk(" cause: packet time out\n");)
}
else {
- IF_ERR(printk(" cause: buffer over flow\n");)
+ IF_ERR(printk(" cause: buffer overflow\n");)
}
goto out_free_desc;
}
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index c5f5186d62a..51eed679a05 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -142,6 +142,9 @@ MODULE_AUTHOR("Traverse Technologies <support@traverse.com.au>");
MODULE_DESCRIPTION("Solos PCI driver");
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("solos-FPGA.bin");
+MODULE_FIRMWARE("solos-Firmware.bin");
+MODULE_FIRMWARE("solos-db-FPGA.bin");
MODULE_PARM_DESC(reset, "Reset Solos chips on startup");
MODULE_PARM_DESC(atmdebug, "Print ATM data");
MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade");
@@ -528,34 +531,37 @@ static int flash_upgrade(struct solos_card *card, int chip)
int numblocks = 0;
int offset;
- if (chip == 0) {
+ switch (chip) {
+ case 0:
fw_name = "solos-FPGA.bin";
blocksize = FPGA_BLOCK;
- }
-
- if (chip == 1) {
+ break;
+ case 1:
fw_name = "solos-Firmware.bin";
blocksize = SOLOS_BLOCK;
- }
-
- if (chip == 2){
+ break;
+ case 2:
if (card->fpga_version > LEGACY_BUFFERS){
fw_name = "solos-db-FPGA.bin";
blocksize = FPGA_BLOCK;
} else {
- dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n");
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
return -EPERM;
}
- }
-
- if (chip == 3){
+ break;
+ case 3:
if (card->fpga_version > LEGACY_BUFFERS){
fw_name = "solos-Firmware.bin";
blocksize = SOLOS_BLOCK;
} else {
- dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n");
- return -EPERM;
+ dev_info(&card->dev->dev, "FPGA version doesn't support"
+ " daughter board upgrades\n");
+ return -EPERM;
}
+ break;
+ default:
+ return -ENODEV;
}
if (request_firmware(&fw, fw_name, &card->dev->dev))
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 979d159b5cd..ee95c76bfd3 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -188,7 +188,7 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe);
* @dev: device to try to bind to the driver
*
* This function returns -ENODEV if the device is not registered,
- * 1 if the device is bound sucessfully and 0 otherwise.
+ * 1 if the device is bound successfully and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 846d89e3d12..5a01ecef4af 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -185,6 +185,7 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
}
dev->power.runtime_status = RPM_SUSPENDING;
+ dev->power.deferred_resume = false;
if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) {
spin_unlock_irq(&dev->power.lock);
@@ -200,7 +201,6 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
if (retval) {
dev->power.runtime_status = RPM_ACTIVE;
pm_runtime_cancel_pending(dev);
- dev->power.deferred_resume = false;
if (retval == -EAGAIN || retval == -EBUSY) {
notify = true;
@@ -217,7 +217,6 @@ int __pm_runtime_suspend(struct device *dev, bool from_wq)
wake_up_all(&dev->power.wait_queue);
if (dev->power.deferred_resume) {
- dev->power.deferred_resume = false;
__pm_runtime_resume(dev, false);
retval = -EAGAIN;
goto out;
@@ -626,6 +625,8 @@ int pm_schedule_suspend(struct device *dev, unsigned int delay)
goto out;
dev->power.timer_expires = jiffies + msecs_to_jiffies(delay);
+ if (!dev->power.timer_expires)
+ dev->power.timer_expires = 1;
mod_timer(&dev->power.suspend_timer, dev->power.timer_expires);
out:
@@ -659,13 +660,17 @@ static int __pm_request_resume(struct device *dev)
pm_runtime_deactivate_timer(dev);
+ if (dev->power.runtime_status == RPM_SUSPENDING) {
+ dev->power.deferred_resume = true;
+ return retval;
+ }
if (dev->power.request_pending) {
/* If non-resume request is pending, we can overtake it. */
dev->power.request = retval ? RPM_REQ_NONE : RPM_REQ_RESUME;
return retval;
- } else if (retval) {
- return retval;
}
+ if (retval)
+ return retval;
dev->power.request = RPM_REQ_RESUME;
dev->power.request_pending = true;
@@ -777,7 +782,7 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
}
if (parent) {
- spin_lock(&parent->power.lock);
+ spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING);
/*
* It is invalid to put an active child under a parent that is
@@ -786,12 +791,10 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
*/
if (!parent->power.disable_depth
&& !parent->power.ignore_children
- && parent->power.runtime_status != RPM_ACTIVE) {
+ && parent->power.runtime_status != RPM_ACTIVE)
error = -EBUSY;
- } else {
- if (dev->power.runtime_status == RPM_SUSPENDED)
- atomic_inc(&parent->power.child_count);
- }
+ else if (dev->power.runtime_status == RPM_SUSPENDED)
+ atomic_inc(&parent->power.child_count);
spin_unlock(&parent->power.lock);
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 1d886e079c5..77bfce52e9c 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -271,6 +271,8 @@ config BLK_DEV_CRYPTOLOOP
instead, which can be configured to be on-disk compatible with the
cryptoloop device.
+source "drivers/block/drbd/Kconfig"
+
config BLK_DEV_NBD
tristate "Network block device support"
depends on NET
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index cdaa3f8fddf..aff5ac925c3 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -36,5 +36,6 @@ obj-$(CONFIG_BLK_DEV_UB) += ub.o
obj-$(CONFIG_BLK_DEV_HD) += hd.o
obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o
+obj-$(CONFIG_BLK_DEV_DRBD) += drbd/
swim_mod-objs := swim.o swim_asm.o
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index 847a9e57570..a5af1d6dda8 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -1478,10 +1478,7 @@ void do_fd_request(struct request_queue * q)
stdma_lock(floppy_irq, NULL);
atari_disable_irq( IRQ_MFP_FDC );
- local_save_flags(flags); /* The request function is called with ints
- local_irq_disable(); * disabled... so must save the IPL for later */
redo_fd_request();
- local_irq_restore(flags);
atari_enable_irq( IRQ_MFP_FDC );
}
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 92b126394fa..873e594860d 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -179,19 +179,17 @@ static int rebuild_lun_table(ctlr_info_t *h, int first_time, int via_ioctl);
static int deregister_disk(ctlr_info_t *h, int drv_index,
int clear_all, int via_ioctl);
-static void cciss_read_capacity(int ctlr, int logvol, int withirq,
+static void cciss_read_capacity(int ctlr, int logvol,
sector_t *total_size, unsigned int *block_size);
-static void cciss_read_capacity_16(int ctlr, int logvol, int withirq,
+static void cciss_read_capacity_16(int ctlr, int logvol,
sector_t *total_size, unsigned int *block_size);
static void cciss_geometry_inquiry(int ctlr, int logvol,
- int withirq, sector_t total_size,
+ sector_t total_size,
unsigned int block_size, InquiryData_struct *inq_buff,
drive_info_struct *drv);
static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *,
__u32);
static void start_io(ctlr_info_t *h);
-static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size,
- __u8 page_code, unsigned char *scsi3addr, int cmd_type);
static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
__u8 page_code, unsigned char scsi3addr[],
int cmd_type);
@@ -424,12 +422,9 @@ cciss_proc_write(struct file *file, const char __user *buf,
if (strncmp(ENGAGE_SCSI, buffer, sizeof ENGAGE_SCSI - 1) == 0) {
struct seq_file *seq = file->private_data;
ctlr_info_t *h = seq->private;
- int rc;
- rc = cciss_engage_scsi(h->ctlr);
- if (rc != 0)
- err = -rc;
- else
+ err = cciss_engage_scsi(h->ctlr);
+ if (err == 0)
err = length;
} else
#endif /* CONFIG_CISS_SCSI_TAPE */
@@ -1657,9 +1652,11 @@ static void cciss_softirq_done(struct request *rq)
{
CommandList_struct *cmd = rq->completion_data;
ctlr_info_t *h = hba[cmd->ctlr];
+ SGDescriptor_struct *curr_sg = cmd->SG;
unsigned long flags;
u64bit temp64;
int i, ddir;
+ int sg_index = 0;
if (cmd->Request.Type.Direction == XFER_READ)
ddir = PCI_DMA_FROMDEVICE;
@@ -1669,9 +1666,22 @@ static void cciss_softirq_done(struct request *rq)
/* command did not need to be retried */
/* unmap the DMA mapping for all the scatter gather elements */
for (i = 0; i < cmd->Header.SGList; i++) {
- temp64.val32.lower = cmd->SG[i].Addr.lower;
- temp64.val32.upper = cmd->SG[i].Addr.upper;
- pci_unmap_page(h->pdev, temp64.val, cmd->SG[i].Len, ddir);
+ if (curr_sg[sg_index].Ext == CCISS_SG_CHAIN) {
+ temp64.val32.lower = cmd->SG[i].Addr.lower;
+ temp64.val32.upper = cmd->SG[i].Addr.upper;
+ pci_dma_sync_single_for_cpu(h->pdev, temp64.val,
+ cmd->SG[i].Len, ddir);
+ pci_unmap_single(h->pdev, temp64.val,
+ cmd->SG[i].Len, ddir);
+ /* Point to the next block */
+ curr_sg = h->cmd_sg_list[cmd->cmdindex]->sgchain;
+ sg_index = 0;
+ }
+ temp64.val32.lower = curr_sg[sg_index].Addr.lower;
+ temp64.val32.upper = curr_sg[sg_index].Addr.upper;
+ pci_unmap_page(h->pdev, temp64.val, curr_sg[sg_index].Len,
+ ddir);
+ ++sg_index;
}
#ifdef CCISS_DEBUG
@@ -1701,7 +1711,7 @@ static inline void log_unit_to_scsi3addr(ctlr_info_t *h,
* via the inquiry page 0. Model, vendor, and rev are set to empty strings if
* they cannot be read.
*/
-static void cciss_get_device_descr(int ctlr, int logvol, int withirq,
+static void cciss_get_device_descr(int ctlr, int logvol,
char *vendor, char *model, char *rev)
{
int rc;
@@ -1717,14 +1727,8 @@ static void cciss_get_device_descr(int ctlr, int logvol, int withirq,
return;
log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- if (withirq)
- rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf,
- sizeof(InquiryData_struct), 0,
- scsi3addr, TYPE_CMD);
- else
- rc = sendcmd(CISS_INQUIRY, ctlr, inq_buf,
- sizeof(InquiryData_struct), 0,
- scsi3addr, TYPE_CMD);
+ rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf, sizeof(*inq_buf), 0,
+ scsi3addr, TYPE_CMD);
if (rc == IO_OK) {
memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN);
vendor[VENDOR_LEN] = '\0';
@@ -1743,7 +1747,7 @@ static void cciss_get_device_descr(int ctlr, int logvol, int withirq,
* number cannot be had, for whatever reason, 16 bytes of 0xff
* are returned instead.
*/
-static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
+static void cciss_get_serial_no(int ctlr, int logvol,
unsigned char *serial_no, int buflen)
{
#define PAGE_83_INQ_BYTES 64
@@ -1759,12 +1763,8 @@ static void cciss_get_serial_no(int ctlr, int logvol, int withirq,
return;
memset(serial_no, 0, buflen);
log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- if (withirq)
- rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
- PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
- else
- rc = sendcmd(CISS_INQUIRY, ctlr, buf,
- PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
+ rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
+ PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
if (rc == IO_OK)
memcpy(serial_no, &buf[8], buflen);
kfree(buf);
@@ -1793,10 +1793,10 @@ static int cciss_add_disk(ctlr_info_t *h, struct gendisk *disk,
blk_queue_bounce_limit(disk->queue, h->pdev->dma_mask);
/* This is a hardware imposed limit. */
- blk_queue_max_hw_segments(disk->queue, MAXSGENTRIES);
+ blk_queue_max_hw_segments(disk->queue, h->maxsgentries);
/* This is a limit in the driver and could be eliminated. */
- blk_queue_max_phys_segments(disk->queue, MAXSGENTRIES);
+ blk_queue_max_phys_segments(disk->queue, h->maxsgentries);
blk_queue_max_sectors(disk->queue, h->cciss_max_sectors);
@@ -1852,18 +1852,16 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
/* testing to see if 16-byte CDBs are already being used */
if (h->cciss_read == CCISS_READ_16) {
- cciss_read_capacity_16(h->ctlr, drv_index, 1,
+ cciss_read_capacity_16(h->ctlr, drv_index,
&total_size, &block_size);
} else {
- cciss_read_capacity(ctlr, drv_index, 1,
- &total_size, &block_size);
-
+ cciss_read_capacity(ctlr, drv_index, &total_size, &block_size);
/* if read_capacity returns all F's this volume is >2TB */
/* in size so we switch to 16-byte CDB's for all */
/* read/write ops */
if (total_size == 0xFFFFFFFFULL) {
- cciss_read_capacity_16(ctlr, drv_index, 1,
+ cciss_read_capacity_16(ctlr, drv_index,
&total_size, &block_size);
h->cciss_read = CCISS_READ_16;
h->cciss_write = CCISS_WRITE_16;
@@ -1873,14 +1871,14 @@ static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
}
}
- cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
+ cciss_geometry_inquiry(ctlr, drv_index, total_size, block_size,
inq_buff, drvinfo);
drvinfo->block_size = block_size;
drvinfo->nr_blocks = total_size + 1;
- cciss_get_device_descr(ctlr, drv_index, 1, drvinfo->vendor,
+ cciss_get_device_descr(ctlr, drv_index, drvinfo->vendor,
drvinfo->model, drvinfo->rev);
- cciss_get_serial_no(ctlr, drv_index, 1, drvinfo->serial_no,
+ cciss_get_serial_no(ctlr, drv_index, drvinfo->serial_no,
sizeof(drvinfo->serial_no));
/* Save the lunid in case we deregister the disk, below. */
memcpy(drvinfo->LunID, h->drv[drv_index]->LunID,
@@ -2531,6 +2529,8 @@ static int check_target_status(ctlr_info_t *h, CommandList_struct *c)
case 0: return IO_OK; /* no sense */
case 1: return IO_OK; /* recovered error */
default:
+ if (check_for_unit_attention(h, c))
+ return IO_NEEDS_RETRY;
printk(KERN_WARNING "cciss%d: cmd 0x%02x "
"check condition, sense key = 0x%02x\n",
h->ctlr, c->Request.CDB[0],
@@ -2672,7 +2672,7 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
}
static void cciss_geometry_inquiry(int ctlr, int logvol,
- int withirq, sector_t total_size,
+ sector_t total_size,
unsigned int block_size,
InquiryData_struct *inq_buff,
drive_info_struct *drv)
@@ -2683,14 +2683,8 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
memset(inq_buff, 0, sizeof(InquiryData_struct));
log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- if (withirq)
- return_code = sendcmd_withirq(CISS_INQUIRY, ctlr,
- inq_buff, sizeof(*inq_buff),
- 0xC1, scsi3addr, TYPE_CMD);
- else
- return_code = sendcmd(CISS_INQUIRY, ctlr, inq_buff,
- sizeof(*inq_buff), 0xC1, scsi3addr,
- TYPE_CMD);
+ return_code = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buff,
+ sizeof(*inq_buff), 0xC1, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
if (inq_buff->data_byte[8] == 0xFF) {
printk(KERN_WARNING
@@ -2723,7 +2717,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
}
static void
-cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
+cciss_read_capacity(int ctlr, int logvol, sector_t *total_size,
unsigned int *block_size)
{
ReadCapdata_struct *buf;
@@ -2737,14 +2731,8 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
}
log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- if (withirq)
- return_code = sendcmd_withirq(CCISS_READ_CAPACITY,
- ctlr, buf, sizeof(ReadCapdata_struct),
- 0, scsi3addr, TYPE_CMD);
- else
- return_code = sendcmd(CCISS_READ_CAPACITY,
- ctlr, buf, sizeof(ReadCapdata_struct),
- 0, scsi3addr, TYPE_CMD);
+ return_code = sendcmd_withirq(CCISS_READ_CAPACITY, ctlr, buf,
+ sizeof(ReadCapdata_struct), 0, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be32_to_cpu(*(__be32 *) buf->total_size);
*block_size = be32_to_cpu(*(__be32 *) buf->block_size);
@@ -2756,8 +2744,8 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
kfree(buf);
}
-static void
-cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size, unsigned int *block_size)
+static void cciss_read_capacity_16(int ctlr, int logvol,
+ sector_t *total_size, unsigned int *block_size)
{
ReadCapdata_struct_16 *buf;
int return_code;
@@ -2770,16 +2758,9 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size,
}
log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- if (withirq) {
- return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
- ctlr, buf, sizeof(ReadCapdata_struct_16),
- 0, scsi3addr, TYPE_CMD);
- }
- else {
- return_code = sendcmd(CCISS_READ_CAPACITY_16,
- ctlr, buf, sizeof(ReadCapdata_struct_16),
- 0, scsi3addr, TYPE_CMD);
- }
+ return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
+ ctlr, buf, sizeof(ReadCapdata_struct_16),
+ 0, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be64_to_cpu(*(__be64 *) buf->total_size);
*block_size = be32_to_cpu(*(__be32 *) buf->block_size);
@@ -2820,13 +2801,13 @@ static int cciss_revalidate(struct gendisk *disk)
return 1;
}
if (h->cciss_read == CCISS_READ_10) {
- cciss_read_capacity(h->ctlr, logvol, 1,
+ cciss_read_capacity(h->ctlr, logvol,
&total_size, &block_size);
} else {
- cciss_read_capacity_16(h->ctlr, logvol, 1,
+ cciss_read_capacity_16(h->ctlr, logvol,
&total_size, &block_size);
}
- cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size,
+ cciss_geometry_inquiry(h->ctlr, logvol, total_size, block_size,
inq_buff, drv);
blk_queue_logical_block_size(drv->queue, drv->block_size);
@@ -2837,167 +2818,6 @@ static int cciss_revalidate(struct gendisk *disk)
}
/*
- * Wait polling for a command to complete.
- * The memory mapped FIFO is polled for the completion.
- * Used only at init time, interrupts from the HBA are disabled.
- */
-static unsigned long pollcomplete(int ctlr)
-{
- unsigned long done;
- int i;
-
- /* Wait (up to 20 seconds) for a command to complete */
-
- for (i = 20 * HZ; i > 0; i--) {
- done = hba[ctlr]->access.command_completed(hba[ctlr]);
- if (done == FIFO_EMPTY)
- schedule_timeout_uninterruptible(1);
- else
- return done;
- }
- /* Invalid address to tell caller we ran out of time */
- return 1;
-}
-
-/* Send command c to controller h and poll for it to complete.
- * Turns interrupts off on the board. Used at driver init time
- * and during SCSI error recovery.
- */
-static int sendcmd_core(ctlr_info_t *h, CommandList_struct *c)
-{
- int i;
- unsigned long complete;
- int status = IO_ERROR;
- u64bit buff_dma_handle;
-
-resend_cmd1:
-
- /* Disable interrupt on the board. */
- h->access.set_intr_mask(h, CCISS_INTR_OFF);
-
- /* Make sure there is room in the command FIFO */
- /* Actually it should be completely empty at this time */
- /* unless we are in here doing error handling for the scsi */
- /* tape side of the driver. */
- for (i = 200000; i > 0; i--) {
- /* if fifo isn't full go */
- if (!(h->access.fifo_full(h)))
- break;
- udelay(10);
- printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full,"
- " waiting!\n", h->ctlr);
- }
- h->access.submit_command(h, c); /* Send the cmd */
- do {
- complete = pollcomplete(h->ctlr);
-
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: command completed\n");
-#endif /* CCISS_DEBUG */
-
- if (complete == 1) {
- printk(KERN_WARNING
- "cciss cciss%d: SendCmd Timeout out, "
- "No command list address returned!\n", h->ctlr);
- status = IO_ERROR;
- break;
- }
-
- /* Make sure it's the command we're expecting. */
- if ((complete & ~CISS_ERROR_BIT) != c->busaddr) {
- printk(KERN_WARNING "cciss%d: Unexpected command "
- "completion.\n", h->ctlr);
- continue;
- }
-
- /* It is our command. If no error, we're done. */
- if (!(complete & CISS_ERROR_BIT)) {
- status = IO_OK;
- break;
- }
-
- /* There is an error... */
-
- /* if data overrun or underun on Report command ignore it */
- if (((c->Request.CDB[0] == CISS_REPORT_LOG) ||
- (c->Request.CDB[0] == CISS_REPORT_PHYS) ||
- (c->Request.CDB[0] == CISS_INQUIRY)) &&
- ((c->err_info->CommandStatus == CMD_DATA_OVERRUN) ||
- (c->err_info->CommandStatus == CMD_DATA_UNDERRUN))) {
- complete = c->busaddr;
- status = IO_OK;
- break;
- }
-
- if (c->err_info->CommandStatus == CMD_UNSOLICITED_ABORT) {
- printk(KERN_WARNING "cciss%d: unsolicited abort %p\n",
- h->ctlr, c);
- if (c->retry_count < MAX_CMD_RETRIES) {
- printk(KERN_WARNING "cciss%d: retrying %p\n",
- h->ctlr, c);
- c->retry_count++;
- /* erase the old error information */
- memset(c->err_info, 0, sizeof(c->err_info));
- goto resend_cmd1;
- }
- printk(KERN_WARNING "cciss%d: retried %p too many "
- "times\n", h->ctlr, c);
- status = IO_ERROR;
- break;
- }
-
- if (c->err_info->CommandStatus == CMD_UNABORTABLE) {
- printk(KERN_WARNING "cciss%d: command could not be "
- "aborted.\n", h->ctlr);
- status = IO_ERROR;
- break;
- }
-
- if (c->err_info->CommandStatus == CMD_TARGET_STATUS) {
- status = check_target_status(h, c);
- break;
- }
-
- printk(KERN_WARNING "cciss%d: sendcmd error\n", h->ctlr);
- printk(KERN_WARNING "cmd = 0x%02x, CommandStatus = 0x%02x\n",
- c->Request.CDB[0], c->err_info->CommandStatus);
- status = IO_ERROR;
- break;
-
- } while (1);
-
- /* unlock the data buffer from DMA */
- buff_dma_handle.val32.lower = c->SG[0].Addr.lower;
- buff_dma_handle.val32.upper = c->SG[0].Addr.upper;
- pci_unmap_single(h->pdev, (dma_addr_t) buff_dma_handle.val,
- c->SG[0].Len, PCI_DMA_BIDIRECTIONAL);
- return status;
-}
-
-/*
- * Send a command to the controller, and wait for it to complete.
- * Used at init time, and during SCSI error recovery.
- */
-static int sendcmd(__u8 cmd, int ctlr, void *buff, size_t size,
- __u8 page_code, unsigned char *scsi3addr, int cmd_type)
-{
- CommandList_struct *c;
- int status;
-
- c = cmd_alloc(hba[ctlr], 1);
- if (!c) {
- printk(KERN_WARNING "cciss: unable to get memory");
- return IO_ERROR;
- }
- status = fill_cmd(c, cmd, ctlr, buff, size, page_code,
- scsi3addr, cmd_type);
- if (status == IO_OK)
- status = sendcmd_core(hba[ctlr], c);
- cmd_free(hba[ctlr], c, 1);
- return status;
-}
-
-/*
* Map (physical) PCI mem into (virtual) kernel space
*/
static void __iomem *remap_pci_mem(ulong base, ulong size)
@@ -3255,9 +3075,13 @@ static void do_cciss_request(struct request_queue *q)
int seg;
struct request *creq;
u64bit temp64;
- struct scatterlist tmp_sg[MAXSGENTRIES];
+ struct scatterlist *tmp_sg;
+ SGDescriptor_struct *curr_sg;
drive_info_struct *drv;
int i, dir;
+ int nseg = 0;
+ int sg_index = 0;
+ int chained = 0;
/* We call start_io here in case there is a command waiting on the
* queue that has not been sent.
@@ -3270,13 +3094,14 @@ static void do_cciss_request(struct request_queue *q)
if (!creq)
goto startio;
- BUG_ON(creq->nr_phys_segments > MAXSGENTRIES);
+ BUG_ON(creq->nr_phys_segments > h->maxsgentries);
if ((c = cmd_alloc(h, 1)) == NULL)
goto full;
blk_start_request(creq);
+ tmp_sg = h->scatter_list[c->cmdindex];
spin_unlock_irq(q->queue_lock);
c->cmd_type = CMD_RWREQ;
@@ -3305,7 +3130,7 @@ static void do_cciss_request(struct request_queue *q)
(int)blk_rq_pos(creq), (int)blk_rq_sectors(creq));
#endif /* CCISS_DEBUG */
- sg_init_table(tmp_sg, MAXSGENTRIES);
+ sg_init_table(tmp_sg, h->maxsgentries);
seg = blk_rq_map_sg(q, creq, tmp_sg);
/* get the DMA records for the setup */
@@ -3314,25 +3139,70 @@ static void do_cciss_request(struct request_queue *q)
else
dir = PCI_DMA_TODEVICE;
+ curr_sg = c->SG;
+ sg_index = 0;
+ chained = 0;
+
for (i = 0; i < seg; i++) {
- c->SG[i].Len = tmp_sg[i].length;
+ if (((sg_index+1) == (h->max_cmd_sgentries)) &&
+ !chained && ((seg - i) > 1)) {
+ nseg = seg - i;
+ curr_sg[sg_index].Len = (nseg) *
+ sizeof(SGDescriptor_struct);
+ curr_sg[sg_index].Ext = CCISS_SG_CHAIN;
+
+ /* Point to next chain block. */
+ curr_sg = h->cmd_sg_list[c->cmdindex]->sgchain;
+ sg_index = 0;
+ chained = 1;
+ }
+ curr_sg[sg_index].Len = tmp_sg[i].length;
temp64.val = (__u64) pci_map_page(h->pdev, sg_page(&tmp_sg[i]),
- tmp_sg[i].offset,
- tmp_sg[i].length, dir);
- c->SG[i].Addr.lower = temp64.val32.lower;
- c->SG[i].Addr.upper = temp64.val32.upper;
- c->SG[i].Ext = 0; // we are not chaining
+ tmp_sg[i].offset,
+ tmp_sg[i].length, dir);
+ curr_sg[sg_index].Addr.lower = temp64.val32.lower;
+ curr_sg[sg_index].Addr.upper = temp64.val32.upper;
+ curr_sg[sg_index].Ext = 0; /* we are not chaining */
+
+ ++sg_index;
+ }
+
+ if (chained) {
+ int len;
+ curr_sg = c->SG;
+ sg_index = h->max_cmd_sgentries - 1;
+ len = curr_sg[sg_index].Len;
+ /* Setup pointer to next chain block.
+ * Fill out last element in current chain
+ * block with address of next chain block.
+ */
+ temp64.val = pci_map_single(h->pdev,
+ h->cmd_sg_list[c->cmdindex]->sgchain,
+ len, dir);
+
+ h->cmd_sg_list[c->cmdindex]->sg_chain_dma = temp64.val;
+ curr_sg[sg_index].Addr.lower = temp64.val32.lower;
+ curr_sg[sg_index].Addr.upper = temp64.val32.upper;
+
+ pci_dma_sync_single_for_device(h->pdev,
+ h->cmd_sg_list[c->cmdindex]->sg_chain_dma,
+ len, dir);
}
+
/* track how many SG entries we are using */
if (seg > h->maxSG)
h->maxSG = seg;
#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: Submitting %u sectors in %d segments\n",
- blk_rq_sectors(creq), seg);
+ printk(KERN_DEBUG "cciss: Submitting %ld sectors in %d segments "
+ "chained[%d]\n",
+ blk_rq_sectors(creq), seg, chained);
#endif /* CCISS_DEBUG */
- c->Header.SGList = c->Header.SGTotal = seg;
+ c->Header.SGList = c->Header.SGTotal = seg + chained;
+ if (seg > h->max_cmd_sgentries)
+ c->Header.SGList = h->max_cmd_sgentries;
+
if (likely(blk_fs_request(creq))) {
if(h->cciss_read == CCISS_READ_10) {
c->Request.CDB[1] = 0;
@@ -3513,28 +3383,33 @@ static int add_to_scan_list(struct ctlr_info *h)
* @h: Pointer to the controller.
*
* Removes the controller from the rescan queue if present. Blocks if
- * the controller is currently conducting a rescan.
+ * the controller is currently conducting a rescan. The controller
+ * can be in one of three states:
+ * 1. Doesn't need a scan
+ * 2. On the scan list, but not scanning yet (we remove it)
+ * 3. Busy scanning (and not on the list). In this case we want to wait for
+ * the scan to complete to make sure the scanning thread for this
+ * controller is completely idle.
**/
static void remove_from_scan_list(struct ctlr_info *h)
{
struct ctlr_info *test_h, *tmp_h;
- int scanning = 0;
mutex_lock(&scan_mutex);
list_for_each_entry_safe(test_h, tmp_h, &scan_q, scan_list) {
- if (test_h == h) {
+ if (test_h == h) { /* state 2. */
list_del(&h->scan_list);
complete_all(&h->scan_wait);
mutex_unlock(&scan_mutex);
return;
}
}
- if (&h->busy_scanning)
- scanning = 0;
- mutex_unlock(&scan_mutex);
-
- if (scanning)
+ if (h->busy_scanning) { /* state 3. */
+ mutex_unlock(&scan_mutex);
wait_for_completion(&h->scan_wait);
+ } else { /* state 1, nothing to do. */
+ mutex_unlock(&scan_mutex);
+ }
}
/**
@@ -3573,13 +3448,11 @@ static int scan_thread(void *data)
h->busy_scanning = 1;
mutex_unlock(&scan_mutex);
- if (h) {
- rebuild_lun_table(h, 0, 0);
- complete_all(&h->scan_wait);
- mutex_lock(&scan_mutex);
- h->busy_scanning = 0;
- mutex_unlock(&scan_mutex);
- }
+ rebuild_lun_table(h, 0, 0);
+ complete_all(&h->scan_wait);
+ mutex_lock(&scan_mutex);
+ h->busy_scanning = 0;
+ mutex_unlock(&scan_mutex);
}
}
@@ -3605,8 +3478,22 @@ static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c)
case REPORT_LUNS_CHANGED:
printk(KERN_WARNING "cciss%d: report LUN data "
"changed\n", h->ctlr);
- add_to_scan_list(h);
- wake_up_process(cciss_scan_thread);
+ /*
+ * Here, we could call add_to_scan_list and wake up the scan thread,
+ * except that it's quite likely that we will get more than one
+ * REPORT_LUNS_CHANGED condition in quick succession, which means
+ * that those which occur after the first one will likely happen
+ * *during* the scan_thread's rescan. And the rescan code is not
+ * robust enough to restart in the middle, undoing what it has already
+ * done, and it's not clear that it's even possible to do this, since
+ * part of what it does is notify the block layer, which starts
+ * doing it's own i/o to read partition tables and so on, and the
+ * driver doesn't have visibility to know what might need undoing.
+ * In any event, if possible, it is horribly complicated to get right
+ * so we just don't do it for now.
+ *
+ * Note: this REPORT_LUNS_CHANGED condition only occurs on the MSA2012.
+ */
return 1;
break;
case POWER_OR_RESET:
@@ -3888,6 +3775,23 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
* leave a little room for ioctl calls.
*/
c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
+ c->maxsgentries = readl(&(c->cfgtable->MaxSGElements));
+
+ /*
+ * Limit native command to 32 s/g elements to save dma'able memory.
+ * Howvever spec says if 0, use 31
+ */
+
+ c->max_cmd_sgentries = 31;
+ if (c->maxsgentries > 512) {
+ c->max_cmd_sgentries = 32;
+ c->chainsize = c->maxsgentries - c->max_cmd_sgentries + 1;
+ c->maxsgentries -= 1; /* account for chain pointer */
+ } else {
+ c->maxsgentries = 31; /* Default to traditional value */
+ c->chainsize = 0; /* traditional */
+ }
+
c->product_name = products[prod_index].product_name;
c->access = *(products[prod_index].access);
c->nr_cmds = c->max_commands - 4;
@@ -4214,6 +4118,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
{
int i;
int j = 0;
+ int k = 0;
int rc;
int dac, return_code;
InquiryData_struct *inq_buff;
@@ -4317,6 +4222,53 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
printk(KERN_ERR "cciss: out of memory");
goto clean4;
}
+
+ /* Need space for temp scatter list */
+ hba[i]->scatter_list = kmalloc(hba[i]->max_commands *
+ sizeof(struct scatterlist *),
+ GFP_KERNEL);
+ for (k = 0; k < hba[i]->nr_cmds; k++) {
+ hba[i]->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
+ hba[i]->maxsgentries,
+ GFP_KERNEL);
+ if (hba[i]->scatter_list[k] == NULL) {
+ printk(KERN_ERR "cciss%d: could not allocate "
+ "s/g lists\n", i);
+ goto clean4;
+ }
+ }
+ hba[i]->cmd_sg_list = kmalloc(sizeof(struct Cmd_sg_list *) *
+ hba[i]->nr_cmds,
+ GFP_KERNEL);
+ if (!hba[i]->cmd_sg_list) {
+ printk(KERN_ERR "cciss%d: Cannot get memory for "
+ "s/g chaining.\n", i);
+ goto clean4;
+ }
+ /* Build up chain blocks for each command */
+ if (hba[i]->chainsize > 0) {
+ for (j = 0; j < hba[i]->nr_cmds; j++) {
+ hba[i]->cmd_sg_list[j] =
+ kmalloc(sizeof(struct Cmd_sg_list),
+ GFP_KERNEL);
+ if (!hba[i]->cmd_sg_list[j]) {
+ printk(KERN_ERR "cciss%d: Cannot get memory "
+ "for chain block.\n", i);
+ goto clean4;
+ }
+ /* Need a block of chainsized s/g elements. */
+ hba[i]->cmd_sg_list[j]->sgchain =
+ kmalloc((hba[i]->chainsize *
+ sizeof(SGDescriptor_struct)),
+ GFP_KERNEL);
+ if (!hba[i]->cmd_sg_list[j]->sgchain) {
+ printk(KERN_ERR "cciss%d: Cannot get memory "
+ "for s/g chains\n", i);
+ goto clean4;
+ }
+ }
+ }
+
spin_lock_init(&hba[i]->lock);
/* Initialize the pdev driver private data.
@@ -4362,7 +4314,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
cciss_procinit(i);
- hba[i]->cciss_max_sectors = 2048;
+ hba[i]->cciss_max_sectors = 8192;
rebuild_lun_table(hba[i], 1, 0);
hba[i]->busy_initializing = 0;
@@ -4370,6 +4322,20 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
clean4:
kfree(hba[i]->cmd_pool_bits);
+ /* Free up sg elements */
+ for (k = 0; k < hba[i]->nr_cmds; k++)
+ kfree(hba[i]->scatter_list[k]);
+ kfree(hba[i]->scatter_list);
+ /* Only free up extra s/g lists if controller supports them */
+ if (hba[i]->chainsize > 0) {
+ for (j = 0; j < hba[i]->nr_cmds; j++) {
+ if (hba[i]->cmd_sg_list[j]) {
+ kfree(hba[i]->cmd_sg_list[j]->sgchain);
+ kfree(hba[i]->cmd_sg_list[j]);
+ }
+ }
+ kfree(hba[i]->cmd_sg_list);
+ }
if (hba[i]->cmd_pool)
pci_free_consistent(hba[i]->pdev,
hba[i]->nr_cmds * sizeof(CommandList_struct),
@@ -4400,30 +4366,28 @@ clean_no_release_regions:
static void cciss_shutdown(struct pci_dev *pdev)
{
- ctlr_info_t *tmp_ptr;
- int i;
- char flush_buf[4];
+ ctlr_info_t *h;
+ char *flush_buf;
int return_code;
- tmp_ptr = pci_get_drvdata(pdev);
- if (tmp_ptr == NULL)
- return;
- i = tmp_ptr->ctlr;
- if (hba[i] == NULL)
+ h = pci_get_drvdata(pdev);
+ flush_buf = kzalloc(4, GFP_KERNEL);
+ if (!flush_buf) {
+ printk(KERN_WARNING
+ "cciss:%d cache not flushed, out of memory.\n",
+ h->ctlr);
return;
-
- /* Turn board interrupts off and send the flush cache command */
- /* sendcmd will turn off interrupt, and send the flush...
- * To write all data in the battery backed cache to disks */
- memset(flush_buf, 0, 4);
- return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0,
- CTLR_LUNID, TYPE_CMD);
- if (return_code == IO_OK) {
- printk(KERN_INFO "Completed flushing cache on controller %d\n", i);
- } else {
- printk(KERN_WARNING "Error flushing cache on controller %d\n", i);
}
- free_irq(hba[i]->intr[2], hba[i]);
+ /* write all data in the battery backed cache to disk */
+ memset(flush_buf, 0, 4);
+ return_code = sendcmd_withirq(CCISS_CACHE_FLUSH, h->ctlr, flush_buf,
+ 4, 0, CTLR_LUNID, TYPE_CMD);
+ kfree(flush_buf);
+ if (return_code != IO_OK)
+ printk(KERN_WARNING "cciss%d: Error flushing cache\n",
+ h->ctlr);
+ h->access.set_intr_mask(h, CCISS_INTR_OFF);
+ free_irq(h->intr[2], h);
}
static void __devexit cciss_remove_one(struct pci_dev *pdev)
@@ -4485,6 +4449,20 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev)
pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle);
kfree(hba[i]->cmd_pool_bits);
+ /* Free up sg elements */
+ for (j = 0; j < hba[i]->nr_cmds; j++)
+ kfree(hba[i]->scatter_list[j]);
+ kfree(hba[i]->scatter_list);
+ /* Only free up extra s/g lists if controller supports them */
+ if (hba[i]->chainsize > 0) {
+ for (j = 0; j < hba[i]->nr_cmds; j++) {
+ if (hba[i]->cmd_sg_list[j]) {
+ kfree(hba[i]->cmd_sg_list[j]->sgchain);
+ kfree(hba[i]->cmd_sg_list[j]);
+ }
+ }
+ kfree(hba[i]->cmd_sg_list);
+ }
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 31524cf42c7..1d95db25406 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -55,7 +55,13 @@ typedef struct _drive_info_struct
char device_initialized; /* indicates whether dev is initialized */
} drive_info_struct;
-struct ctlr_info
+struct Cmd_sg_list {
+ SGDescriptor_struct *sgchain;
+ dma_addr_t sg_chain_dma;
+ int chain_block_size;
+};
+
+struct ctlr_info
{
int ctlr;
char devname[8];
@@ -75,6 +81,16 @@ struct ctlr_info
int num_luns;
int highest_lun;
int usage_count; /* number of opens all all minor devices */
+ /* Need space for temp sg list
+ * number of scatter/gathers supported
+ * number of scatter/gathers in chained block
+ */
+ struct scatterlist **scatter_list;
+ int maxsgentries;
+ int chainsize;
+ int max_cmd_sgentries;
+ struct Cmd_sg_list **cmd_sg_list;
+
# define DOORBELL_INT 0
# define PERF_MODE_INT 1
# define SIMPLE_MODE_INT 2
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h
index dbaed1ea0da..6afa700890f 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -5,9 +5,10 @@
//###########################################################################
#define CISS_VERSION "1.00"
-//general boundary defintions
+//general boundary definitions
#define SENSEINFOBYTES 32//note that this value may vary between host implementations
-#define MAXSGENTRIES 31
+#define MAXSGENTRIES 32
+#define CCISS_SG_CHAIN 0x80000000
#define MAXREPLYQS 256
//Command Status value
@@ -319,6 +320,10 @@ typedef struct _CfgTable_struct {
BYTE ServerName[16];
DWORD HeartBeat;
DWORD SCSI_Prefetch;
+ DWORD MaxSGElements;
+ DWORD MaxLogicalUnits;
+ DWORD MaxPhysicalDrives;
+ DWORD MaxPhysicalDrivesPerLogicalUnit;
} CfgTable_struct;
#pragma pack()
#endif // CCISS_CMD_H
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 3315268b4ec..5d0e46dc363 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -755,7 +755,7 @@ complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
cp,
ei->ScsiStatus);
#endif
- cmd->result |= (ei->ScsiStatus < 1);
+ cmd->result |= (ei->ScsiStatus << 1);
}
else { /* scsi status is zero??? How??? */
@@ -1547,7 +1547,7 @@ cciss_engage_scsi(int ctlr)
if (sa->registered) {
printk("cciss%d: SCSI subsystem already engaged.\n", ctlr);
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- return ENXIO;
+ return -ENXIO;
}
sa->registered = 1;
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
diff --git a/drivers/block/drbd/Kconfig b/drivers/block/drbd/Kconfig
new file mode 100644
index 00000000000..f4acd04ebee
--- /dev/null
+++ b/drivers/block/drbd/Kconfig
@@ -0,0 +1,71 @@
+#
+# DRBD device driver configuration
+#
+
+comment "DRBD disabled because PROC_FS, INET or CONNECTOR not selected"
+ depends on !PROC_FS || !INET || !CONNECTOR
+
+config BLK_DEV_DRBD
+ tristate "DRBD Distributed Replicated Block Device support"
+ depends on PROC_FS && INET && CONNECTOR
+ select LRU_CACHE
+ default n
+ help
+
+ NOTE: In order to authenticate connections you have to select
+ CRYPTO_HMAC and a hash function as well.
+
+ DRBD is a shared-nothing, synchronously replicated block device. It
+ is designed to serve as a building block for high availability
+ clusters and in this context, is a "drop-in" replacement for shared
+ storage. Simplistically, you could see it as a network RAID 1.
+
+ Each minor device has a role, which can be 'primary' or 'secondary'.
+ On the node with the primary device the application is supposed to
+ run and to access the device (/dev/drbdX). Every write is sent to
+ the local 'lower level block device' and, across the network, to the
+ node with the device in 'secondary' state. The secondary device
+ simply writes the data to its lower level block device.
+
+ DRBD can also be used in dual-Primary mode (device writable on both
+ nodes), which means it can exhibit shared disk semantics in a
+ shared-nothing cluster. Needless to say, on top of dual-Primary
+ DRBD utilizing a cluster file system is necessary to maintain for
+ cache coherency.
+
+ For automatic failover you need a cluster manager (e.g. heartbeat).
+ See also: http://www.drbd.org/, http://www.linux-ha.org
+
+ If unsure, say N.
+
+config DRBD_FAULT_INJECTION
+ bool "DRBD fault injection"
+ depends on BLK_DEV_DRBD
+ help
+
+ Say Y here if you want to simulate IO errors, in order to test DRBD's
+ behavior.
+
+ The actual simulation of IO errors is done by writing 3 values to
+ /sys/module/drbd/parameters/
+
+ enable_faults: bitmask of...
+ 1 meta data write
+ 2 read
+ 4 resync data write
+ 8 read
+ 16 data write
+ 32 data read
+ 64 read ahead
+ 128 kmalloc of bitmap
+ 256 allocation of EE (epoch_entries)
+
+ fault_devs: bitmask of minor numbers
+ fault_rate: frequency in percent
+
+ Example: Simulate data write errors on /dev/drbd0 with a probability of 5%.
+ echo 16 > /sys/module/drbd/parameters/enable_faults
+ echo 1 > /sys/module/drbd/parameters/fault_devs
+ echo 5 > /sys/module/drbd/parameters/fault_rate
+
+ If unsure, say N.
diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile
new file mode 100644
index 00000000000..0d3f337ff5f
--- /dev/null
+++ b/drivers/block/drbd/Makefile
@@ -0,0 +1,5 @@
+drbd-y := drbd_bitmap.o drbd_proc.o
+drbd-y += drbd_worker.o drbd_receiver.o drbd_req.o drbd_actlog.o
+drbd-y += drbd_main.o drbd_strings.o drbd_nl.o
+
+obj-$(CONFIG_BLK_DEV_DRBD) += drbd.o
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
new file mode 100644
index 00000000000..17956ff6a08
--- /dev/null
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -0,0 +1,1424 @@
+/*
+ drbd_actlog.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2003-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 2003-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2003-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/slab.h>
+#include <linux/drbd.h>
+#include "drbd_int.h"
+#include "drbd_wrappers.h"
+
+/* We maintain a trivial check sum in our on disk activity log.
+ * With that we can ensure correct operation even when the storage
+ * device might do a partial (last) sector write while loosing power.
+ */
+struct __packed al_transaction {
+ u32 magic;
+ u32 tr_number;
+ struct __packed {
+ u32 pos;
+ u32 extent; } updates[1 + AL_EXTENTS_PT];
+ u32 xor_sum;
+};
+
+struct update_odbm_work {
+ struct drbd_work w;
+ unsigned int enr;
+};
+
+struct update_al_work {
+ struct drbd_work w;
+ struct lc_element *al_ext;
+ struct completion event;
+ unsigned int enr;
+ /* if old_enr != LC_FREE, write corresponding bitmap sector, too */
+ unsigned int old_enr;
+};
+
+struct drbd_atodb_wait {
+ atomic_t count;
+ struct completion io_done;
+ struct drbd_conf *mdev;
+ int error;
+};
+
+
+int w_al_write_transaction(struct drbd_conf *, struct drbd_work *, int);
+
+static int _drbd_md_sync_page_io(struct drbd_conf *mdev,
+ struct drbd_backing_dev *bdev,
+ struct page *page, sector_t sector,
+ int rw, int size)
+{
+ struct bio *bio;
+ struct drbd_md_io md_io;
+ int ok;
+
+ md_io.mdev = mdev;
+ init_completion(&md_io.event);
+ md_io.error = 0;
+
+ if ((rw & WRITE) && !test_bit(MD_NO_BARRIER, &mdev->flags))
+ rw |= (1 << BIO_RW_BARRIER);
+ rw |= ((1<<BIO_RW_UNPLUG) | (1<<BIO_RW_SYNCIO));
+
+ retry:
+ bio = bio_alloc(GFP_NOIO, 1);
+ bio->bi_bdev = bdev->md_bdev;
+ bio->bi_sector = sector;
+ ok = (bio_add_page(bio, page, size, 0) == size);
+ if (!ok)
+ goto out;
+ bio->bi_private = &md_io;
+ bio->bi_end_io = drbd_md_io_complete;
+ bio->bi_rw = rw;
+
+ if (FAULT_ACTIVE(mdev, (rw & WRITE) ? DRBD_FAULT_MD_WR : DRBD_FAULT_MD_RD))
+ bio_endio(bio, -EIO);
+ else
+ submit_bio(rw, bio);
+ wait_for_completion(&md_io.event);
+ ok = bio_flagged(bio, BIO_UPTODATE) && md_io.error == 0;
+
+ /* check for unsupported barrier op.
+ * would rather check on EOPNOTSUPP, but that is not reliable.
+ * don't try again for ANY return value != 0 */
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && !ok)) {
+ /* Try again with no barrier */
+ dev_warn(DEV, "Barriers not supported on meta data device - disabling\n");
+ set_bit(MD_NO_BARRIER, &mdev->flags);
+ rw &= ~(1 << BIO_RW_BARRIER);
+ bio_put(bio);
+ goto retry;
+ }
+ out:
+ bio_put(bio);
+ return ok;
+}
+
+int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
+ sector_t sector, int rw)
+{
+ int logical_block_size, mask, ok;
+ int offset = 0;
+ struct page *iop = mdev->md_io_page;
+
+ D_ASSERT(mutex_is_locked(&mdev->md_io_mutex));
+
+ BUG_ON(!bdev->md_bdev);
+
+ logical_block_size = bdev_logical_block_size(bdev->md_bdev);
+ if (logical_block_size == 0)
+ logical_block_size = MD_SECTOR_SIZE;
+
+ /* in case logical_block_size != 512 [ s390 only? ] */
+ if (logical_block_size != MD_SECTOR_SIZE) {
+ mask = (logical_block_size / MD_SECTOR_SIZE) - 1;
+ D_ASSERT(mask == 1 || mask == 3 || mask == 7);
+ D_ASSERT(logical_block_size == (mask+1) * MD_SECTOR_SIZE);
+ offset = sector & mask;
+ sector = sector & ~mask;
+ iop = mdev->md_io_tmpp;
+
+ if (rw & WRITE) {
+ /* these are GFP_KERNEL pages, pre-allocated
+ * on device initialization */
+ void *p = page_address(mdev->md_io_page);
+ void *hp = page_address(mdev->md_io_tmpp);
+
+ ok = _drbd_md_sync_page_io(mdev, bdev, iop, sector,
+ READ, logical_block_size);
+
+ if (unlikely(!ok)) {
+ dev_err(DEV, "drbd_md_sync_page_io(,%llus,"
+ "READ [logical_block_size!=512]) failed!\n",
+ (unsigned long long)sector);
+ return 0;
+ }
+
+ memcpy(hp + offset*MD_SECTOR_SIZE, p, MD_SECTOR_SIZE);
+ }
+ }
+
+ if (sector < drbd_md_first_sector(bdev) ||
+ sector > drbd_md_last_sector(bdev))
+ dev_alert(DEV, "%s [%d]:%s(,%llus,%s) out of range md access!\n",
+ current->comm, current->pid, __func__,
+ (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ");
+
+ ok = _drbd_md_sync_page_io(mdev, bdev, iop, sector, rw, logical_block_size);
+ if (unlikely(!ok)) {
+ dev_err(DEV, "drbd_md_sync_page_io(,%llus,%s) failed!\n",
+ (unsigned long long)sector, (rw & WRITE) ? "WRITE" : "READ");
+ return 0;
+ }
+
+ if (logical_block_size != MD_SECTOR_SIZE && !(rw & WRITE)) {
+ void *p = page_address(mdev->md_io_page);
+ void *hp = page_address(mdev->md_io_tmpp);
+
+ memcpy(p, hp + offset*MD_SECTOR_SIZE, MD_SECTOR_SIZE);
+ }
+
+ return ok;
+}
+
+static struct lc_element *_al_get(struct drbd_conf *mdev, unsigned int enr)
+{
+ struct lc_element *al_ext;
+ struct lc_element *tmp;
+ unsigned long al_flags = 0;
+
+ spin_lock_irq(&mdev->al_lock);
+ tmp = lc_find(mdev->resync, enr/AL_EXT_PER_BM_SECT);
+ if (unlikely(tmp != NULL)) {
+ struct bm_extent *bm_ext = lc_entry(tmp, struct bm_extent, lce);
+ if (test_bit(BME_NO_WRITES, &bm_ext->flags)) {
+ spin_unlock_irq(&mdev->al_lock);
+ return NULL;
+ }
+ }
+ al_ext = lc_get(mdev->act_log, enr);
+ al_flags = mdev->act_log->flags;
+ spin_unlock_irq(&mdev->al_lock);
+
+ /*
+ if (!al_ext) {
+ if (al_flags & LC_STARVING)
+ dev_warn(DEV, "Have to wait for LRU element (AL too small?)\n");
+ if (al_flags & LC_DIRTY)
+ dev_warn(DEV, "Ongoing AL update (AL device too slow?)\n");
+ }
+ */
+
+ return al_ext;
+}
+
+void drbd_al_begin_io(struct drbd_conf *mdev, sector_t sector)
+{
+ unsigned int enr = (sector >> (AL_EXTENT_SHIFT-9));
+ struct lc_element *al_ext;
+ struct update_al_work al_work;
+
+ D_ASSERT(atomic_read(&mdev->local_cnt) > 0);
+
+ wait_event(mdev->al_wait, (al_ext = _al_get(mdev, enr)));
+
+ if (al_ext->lc_number != enr) {
+ /* drbd_al_write_transaction(mdev,al_ext,enr);
+ * recurses into generic_make_request(), which
+ * disallows recursion, bios being serialized on the
+ * current->bio_tail list now.
+ * we have to delegate updates to the activity log
+ * to the worker thread. */
+ init_completion(&al_work.event);
+ al_work.al_ext = al_ext;
+ al_work.enr = enr;
+ al_work.old_enr = al_ext->lc_number;
+ al_work.w.cb = w_al_write_transaction;
+ drbd_queue_work_front(&mdev->data.work, &al_work.w);
+ wait_for_completion(&al_work.event);
+
+ mdev->al_writ_cnt++;
+
+ spin_lock_irq(&mdev->al_lock);
+ lc_changed(mdev->act_log, al_ext);
+ spin_unlock_irq(&mdev->al_lock);
+ wake_up(&mdev->al_wait);
+ }
+}
+
+void drbd_al_complete_io(struct drbd_conf *mdev, sector_t sector)
+{
+ unsigned int enr = (sector >> (AL_EXTENT_SHIFT-9));
+ struct lc_element *extent;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->al_lock, flags);
+
+ extent = lc_find(mdev->act_log, enr);
+
+ if (!extent) {
+ spin_unlock_irqrestore(&mdev->al_lock, flags);
+ dev_err(DEV, "al_complete_io() called on inactive extent %u\n", enr);
+ return;
+ }
+
+ if (lc_put(mdev->act_log, extent) == 0)
+ wake_up(&mdev->al_wait);
+
+ spin_unlock_irqrestore(&mdev->al_lock, flags);
+}
+
+int
+w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+{
+ struct update_al_work *aw = container_of(w, struct update_al_work, w);
+ struct lc_element *updated = aw->al_ext;
+ const unsigned int new_enr = aw->enr;
+ const unsigned int evicted = aw->old_enr;
+ struct al_transaction *buffer;
+ sector_t sector;
+ int i, n, mx;
+ unsigned int extent_nr;
+ u32 xor_sum = 0;
+
+ if (!get_ldev(mdev)) {
+ dev_err(DEV, "get_ldev() failed in w_al_write_transaction\n");
+ complete(&((struct update_al_work *)w)->event);
+ return 1;
+ }
+ /* do we have to do a bitmap write, first?
+ * TODO reduce maximum latency:
+ * submit both bios, then wait for both,
+ * instead of doing two synchronous sector writes. */
+ if (mdev->state.conn < C_CONNECTED && evicted != LC_FREE)
+ drbd_bm_write_sect(mdev, evicted/AL_EXT_PER_BM_SECT);
+
+ mutex_lock(&mdev->md_io_mutex); /* protects md_io_page, al_tr_cycle, ... */
+ buffer = (struct al_transaction *)page_address(mdev->md_io_page);
+
+ buffer->magic = __constant_cpu_to_be32(DRBD_MAGIC);
+ buffer->tr_number = cpu_to_be32(mdev->al_tr_number);
+
+ n = lc_index_of(mdev->act_log, updated);
+
+ buffer->updates[0].pos = cpu_to_be32(n);
+ buffer->updates[0].extent = cpu_to_be32(new_enr);
+
+ xor_sum ^= new_enr;
+
+ mx = min_t(int, AL_EXTENTS_PT,
+ mdev->act_log->nr_elements - mdev->al_tr_cycle);
+ for (i = 0; i < mx; i++) {
+ unsigned idx = mdev->al_tr_cycle + i;
+ extent_nr = lc_element_by_index(mdev->act_log, idx)->lc_number;
+ buffer->updates[i+1].pos = cpu_to_be32(idx);
+ buffer->updates[i+1].extent = cpu_to_be32(extent_nr);
+ xor_sum ^= extent_nr;
+ }
+ for (; i < AL_EXTENTS_PT; i++) {
+ buffer->updates[i+1].pos = __constant_cpu_to_be32(-1);
+ buffer->updates[i+1].extent = __constant_cpu_to_be32(LC_FREE);
+ xor_sum ^= LC_FREE;
+ }
+ mdev->al_tr_cycle += AL_EXTENTS_PT;
+ if (mdev->al_tr_cycle >= mdev->act_log->nr_elements)
+ mdev->al_tr_cycle = 0;
+
+ buffer->xor_sum = cpu_to_be32(xor_sum);
+
+ sector = mdev->ldev->md.md_offset
+ + mdev->ldev->md.al_offset + mdev->al_tr_pos;
+
+ if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE))
+ drbd_chk_io_error(mdev, 1, TRUE);
+
+ if (++mdev->al_tr_pos >
+ div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT))
+ mdev->al_tr_pos = 0;
+
+ D_ASSERT(mdev->al_tr_pos < MD_AL_MAX_SIZE);
+ mdev->al_tr_number++;
+
+ mutex_unlock(&mdev->md_io_mutex);
+
+ complete(&((struct update_al_work *)w)->event);
+ put_ldev(mdev);
+
+ return 1;
+}
+
+/**
+ * drbd_al_read_tr() - Read a single transaction from the on disk activity log
+ * @mdev: DRBD device.
+ * @bdev: Block device to read form.
+ * @b: pointer to an al_transaction.
+ * @index: On disk slot of the transaction to read.
+ *
+ * Returns -1 on IO error, 0 on checksum error and 1 upon success.
+ */
+static int drbd_al_read_tr(struct drbd_conf *mdev,
+ struct drbd_backing_dev *bdev,
+ struct al_transaction *b,
+ int index)
+{
+ sector_t sector;
+ int rv, i;
+ u32 xor_sum = 0;
+
+ sector = bdev->md.md_offset + bdev->md.al_offset + index;
+
+ /* Dont process error normally,
+ * as this is done before disk is attached! */
+ if (!drbd_md_sync_page_io(mdev, bdev, sector, READ))
+ return -1;
+
+ rv = (be32_to_cpu(b->magic) == DRBD_MAGIC);
+
+ for (i = 0; i < AL_EXTENTS_PT + 1; i++)
+ xor_sum ^= be32_to_cpu(b->updates[i].extent);
+ rv &= (xor_sum == be32_to_cpu(b->xor_sum));
+
+ return rv;
+}
+
+/**
+ * drbd_al_read_log() - Restores the activity log from its on disk representation.
+ * @mdev: DRBD device.
+ * @bdev: Block device to read form.
+ *
+ * Returns 1 on success, returns 0 when reading the log failed due to IO errors.
+ */
+int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
+{
+ struct al_transaction *buffer;
+ int i;
+ int rv;
+ int mx;
+ int active_extents = 0;
+ int transactions = 0;
+ int found_valid = 0;
+ int from = 0;
+ int to = 0;
+ u32 from_tnr = 0;
+ u32 to_tnr = 0;
+ u32 cnr;
+
+ mx = div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT);
+
+ /* lock out all other meta data io for now,
+ * and make sure the page is mapped.
+ */
+ mutex_lock(&mdev->md_io_mutex);
+ buffer = page_address(mdev->md_io_page);
+
+ /* Find the valid transaction in the log */
+ for (i = 0; i <= mx; i++) {
+ rv = drbd_al_read_tr(mdev, bdev, buffer, i);
+ if (rv == 0)
+ continue;
+ if (rv == -1) {
+ mutex_unlock(&mdev->md_io_mutex);
+ return 0;
+ }
+ cnr = be32_to_cpu(buffer->tr_number);
+
+ if (++found_valid == 1) {
+ from = i;
+ to = i;
+ from_tnr = cnr;
+ to_tnr = cnr;
+ continue;
+ }
+ if ((int)cnr - (int)from_tnr < 0) {
+ D_ASSERT(from_tnr - cnr + i - from == mx+1);
+ from = i;
+ from_tnr = cnr;
+ }
+ if ((int)cnr - (int)to_tnr > 0) {
+ D_ASSERT(cnr - to_tnr == i - to);
+ to = i;
+ to_tnr = cnr;
+ }
+ }
+
+ if (!found_valid) {
+ dev_warn(DEV, "No usable activity log found.\n");
+ mutex_unlock(&mdev->md_io_mutex);
+ return 1;
+ }
+
+ /* Read the valid transactions.
+ * dev_info(DEV, "Reading from %d to %d.\n",from,to); */
+ i = from;
+ while (1) {
+ int j, pos;
+ unsigned int extent_nr;
+ unsigned int trn;
+
+ rv = drbd_al_read_tr(mdev, bdev, buffer, i);
+ ERR_IF(rv == 0) goto cancel;
+ if (rv == -1) {
+ mutex_unlock(&mdev->md_io_mutex);
+ return 0;
+ }
+
+ trn = be32_to_cpu(buffer->tr_number);
+
+ spin_lock_irq(&mdev->al_lock);
+
+ /* This loop runs backwards because in the cyclic
+ elements there might be an old version of the
+ updated element (in slot 0). So the element in slot 0
+ can overwrite old versions. */
+ for (j = AL_EXTENTS_PT; j >= 0; j--) {
+ pos = be32_to_cpu(buffer->updates[j].pos);
+ extent_nr = be32_to_cpu(buffer->updates[j].extent);
+
+ if (extent_nr == LC_FREE)
+ continue;
+
+ lc_set(mdev->act_log, extent_nr, pos);
+ active_extents++;
+ }
+ spin_unlock_irq(&mdev->al_lock);
+
+ transactions++;
+
+cancel:
+ if (i == to)
+ break;
+ i++;
+ if (i > mx)
+ i = 0;
+ }
+
+ mdev->al_tr_number = to_tnr+1;
+ mdev->al_tr_pos = to;
+ if (++mdev->al_tr_pos >
+ div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT))
+ mdev->al_tr_pos = 0;
+
+ /* ok, we are done with it */
+ mutex_unlock(&mdev->md_io_mutex);
+
+ dev_info(DEV, "Found %d transactions (%d active extents) in activity log.\n",
+ transactions, active_extents);
+
+ return 1;
+}
+
+static void atodb_endio(struct bio *bio, int error)
+{
+ struct drbd_atodb_wait *wc = bio->bi_private;
+ struct drbd_conf *mdev = wc->mdev;
+ struct page *page;
+ int uptodate = bio_flagged(bio, BIO_UPTODATE);
+
+ /* strange behavior of some lower level drivers...
+ * fail the request by clearing the uptodate flag,
+ * but do not return any error?! */
+ if (!error && !uptodate)
+ error = -EIO;
+
+ drbd_chk_io_error(mdev, error, TRUE);
+ if (error && wc->error == 0)
+ wc->error = error;
+
+ if (atomic_dec_and_test(&wc->count))
+ complete(&wc->io_done);
+
+ page = bio->bi_io_vec[0].bv_page;
+ put_page(page);
+ bio_put(bio);
+ mdev->bm_writ_cnt++;
+ put_ldev(mdev);
+}
+
+#define S2W(s) ((s)<<(BM_EXT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL))
+/* activity log to on disk bitmap -- prepare bio unless that sector
+ * is already covered by previously prepared bios */
+static int atodb_prepare_unless_covered(struct drbd_conf *mdev,
+ struct bio **bios,
+ unsigned int enr,
+ struct drbd_atodb_wait *wc) __must_hold(local)
+{
+ struct bio *bio;
+ struct page *page;
+ sector_t on_disk_sector = enr + mdev->ldev->md.md_offset
+ + mdev->ldev->md.bm_offset;
+ unsigned int page_offset = PAGE_SIZE;
+ int offset;
+ int i = 0;
+ int err = -ENOMEM;
+
+ /* Check if that enr is already covered by an already created bio.
+ * Caution, bios[] is not NULL terminated,
+ * but only initialized to all NULL.
+ * For completely scattered activity log,
+ * the last invocation iterates over all bios,
+ * and finds the last NULL entry.
+ */
+ while ((bio = bios[i])) {
+ if (bio->bi_sector == on_disk_sector)
+ return 0;
+ i++;
+ }
+ /* bios[i] == NULL, the next not yet used slot */
+
+ /* GFP_KERNEL, we are not in the write-out path */
+ bio = bio_alloc(GFP_KERNEL, 1);
+ if (bio == NULL)
+ return -ENOMEM;
+
+ if (i > 0) {
+ const struct bio_vec *prev_bv = bios[i-1]->bi_io_vec;
+ page_offset = prev_bv->bv_offset + prev_bv->bv_len;
+ page = prev_bv->bv_page;
+ }
+ if (page_offset == PAGE_SIZE) {
+ page = alloc_page(__GFP_HIGHMEM);
+ if (page == NULL)
+ goto out_bio_put;
+ page_offset = 0;
+ } else {
+ get_page(page);
+ }
+
+ offset = S2W(enr);
+ drbd_bm_get_lel(mdev, offset,
+ min_t(size_t, S2W(1), drbd_bm_words(mdev) - offset),
+ kmap(page) + page_offset);
+ kunmap(page);
+
+ bio->bi_private = wc;
+ bio->bi_end_io = atodb_endio;
+ bio->bi_bdev = mdev->ldev->md_bdev;
+ bio->bi_sector = on_disk_sector;
+
+ if (bio_add_page(bio, page, MD_SECTOR_SIZE, page_offset) != MD_SECTOR_SIZE)
+ goto out_put_page;
+
+ atomic_inc(&wc->count);
+ /* we already know that we may do this...
+ * get_ldev_if_state(mdev,D_ATTACHING);
+ * just get the extra reference, so that the local_cnt reflects
+ * the number of pending IO requests DRBD at its backing device.
+ */
+ atomic_inc(&mdev->local_cnt);
+
+ bios[i] = bio;
+
+ return 0;
+
+out_put_page:
+ err = -EINVAL;
+ put_page(page);
+out_bio_put:
+ bio_put(bio);
+ return err;
+}
+
+/**
+ * drbd_al_to_on_disk_bm() - * Writes bitmap parts covered by active AL extents
+ * @mdev: DRBD device.
+ *
+ * Called when we detach (unconfigure) local storage,
+ * or when we go from R_PRIMARY to R_SECONDARY role.
+ */
+void drbd_al_to_on_disk_bm(struct drbd_conf *mdev)
+{
+ int i, nr_elements;
+ unsigned int enr;
+ struct bio **bios;
+ struct drbd_atodb_wait wc;
+
+ ERR_IF (!get_ldev_if_state(mdev, D_ATTACHING))
+ return; /* sorry, I don't have any act_log etc... */
+
+ wait_event(mdev->al_wait, lc_try_lock(mdev->act_log));
+
+ nr_elements = mdev->act_log->nr_elements;
+
+ /* GFP_KERNEL, we are not in anyone's write-out path */
+ bios = kzalloc(sizeof(struct bio *) * nr_elements, GFP_KERNEL);
+ if (!bios)
+ goto submit_one_by_one;
+
+ atomic_set(&wc.count, 0);
+ init_completion(&wc.io_done);
+ wc.mdev = mdev;
+ wc.error = 0;
+
+ for (i = 0; i < nr_elements; i++) {
+ enr = lc_element_by_index(mdev->act_log, i)->lc_number;
+ if (enr == LC_FREE)
+ continue;
+ /* next statement also does atomic_inc wc.count and local_cnt */
+ if (atodb_prepare_unless_covered(mdev, bios,
+ enr/AL_EXT_PER_BM_SECT,
+ &wc))
+ goto free_bios_submit_one_by_one;
+ }
+
+ /* unnecessary optimization? */
+ lc_unlock(mdev->act_log);
+ wake_up(&mdev->al_wait);
+
+ /* all prepared, submit them */
+ for (i = 0; i < nr_elements; i++) {
+ if (bios[i] == NULL)
+ break;
+ if (FAULT_ACTIVE(mdev, DRBD_FAULT_MD_WR)) {
+ bios[i]->bi_rw = WRITE;
+ bio_endio(bios[i], -EIO);
+ } else {
+ submit_bio(WRITE, bios[i]);
+ }
+ }
+
+ drbd_blk_run_queue(bdev_get_queue(mdev->ldev->md_bdev));
+
+ /* always (try to) flush bitmap to stable storage */
+ drbd_md_flush(mdev);
+
+ /* In case we did not submit a single IO do not wait for
+ * them to complete. ( Because we would wait forever here. )
+ *
+ * In case we had IOs and they are already complete, there
+ * is not point in waiting anyways.
+ * Therefore this if () ... */
+ if (atomic_read(&wc.count))
+ wait_for_completion(&wc.io_done);
+
+ put_ldev(mdev);
+
+ kfree(bios);
+ return;
+
+ free_bios_submit_one_by_one:
+ /* free everything by calling the endio callback directly. */
+ for (i = 0; i < nr_elements && bios[i]; i++)
+ bio_endio(bios[i], 0);
+
+ kfree(bios);
+
+ submit_one_by_one:
+ dev_warn(DEV, "Using the slow drbd_al_to_on_disk_bm()\n");
+
+ for (i = 0; i < mdev->act_log->nr_elements; i++) {
+ enr = lc_element_by_index(mdev->act_log, i)->lc_number;
+ if (enr == LC_FREE)
+ continue;
+ /* Really slow: if we have al-extents 16..19 active,
+ * sector 4 will be written four times! Synchronous! */
+ drbd_bm_write_sect(mdev, enr/AL_EXT_PER_BM_SECT);
+ }
+
+ lc_unlock(mdev->act_log);
+ wake_up(&mdev->al_wait);
+ put_ldev(mdev);
+}
+
+/**
+ * drbd_al_apply_to_bm() - Sets the bitmap to diry(1) where covered ba active AL extents
+ * @mdev: DRBD device.
+ */
+void drbd_al_apply_to_bm(struct drbd_conf *mdev)
+{
+ unsigned int enr;
+ unsigned long add = 0;
+ char ppb[10];
+ int i;
+
+ wait_event(mdev->al_wait, lc_try_lock(mdev->act_log));
+
+ for (i = 0; i < mdev->act_log->nr_elements; i++) {
+ enr = lc_element_by_index(mdev->act_log, i)->lc_number;
+ if (enr == LC_FREE)
+ continue;
+ add += drbd_bm_ALe_set_all(mdev, enr);
+ }
+
+ lc_unlock(mdev->act_log);
+ wake_up(&mdev->al_wait);
+
+ dev_info(DEV, "Marked additional %s as out-of-sync based on AL.\n",
+ ppsize(ppb, Bit2KB(add)));
+}
+
+static int _try_lc_del(struct drbd_conf *mdev, struct lc_element *al_ext)
+{
+ int rv;
+
+ spin_lock_irq(&mdev->al_lock);
+ rv = (al_ext->refcnt == 0);
+ if (likely(rv))
+ lc_del(mdev->act_log, al_ext);
+ spin_unlock_irq(&mdev->al_lock);
+
+ return rv;
+}
+
+/**
+ * drbd_al_shrink() - Removes all active extents form the activity log
+ * @mdev: DRBD device.
+ *
+ * Removes all active extents form the activity log, waiting until
+ * the reference count of each entry dropped to 0 first, of course.
+ *
+ * You need to lock mdev->act_log with lc_try_lock() / lc_unlock()
+ */
+void drbd_al_shrink(struct drbd_conf *mdev)
+{
+ struct lc_element *al_ext;
+ int i;
+
+ D_ASSERT(test_bit(__LC_DIRTY, &mdev->act_log->flags));
+
+ for (i = 0; i < mdev->act_log->nr_elements; i++) {
+ al_ext = lc_element_by_index(mdev->act_log, i);
+ if (al_ext->lc_number == LC_FREE)
+ continue;
+ wait_event(mdev->al_wait, _try_lc_del(mdev, al_ext));
+ }
+
+ wake_up(&mdev->al_wait);
+}
+
+static int w_update_odbm(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+{
+ struct update_odbm_work *udw = container_of(w, struct update_odbm_work, w);
+
+ if (!get_ldev(mdev)) {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_warn(DEV, "Can not update on disk bitmap, local IO disabled.\n");
+ kfree(udw);
+ return 1;
+ }
+
+ drbd_bm_write_sect(mdev, udw->enr);
+ put_ldev(mdev);
+
+ kfree(udw);
+
+ if (drbd_bm_total_weight(mdev) <= mdev->rs_failed) {
+ switch (mdev->state.conn) {
+ case C_SYNC_SOURCE: case C_SYNC_TARGET:
+ case C_PAUSED_SYNC_S: case C_PAUSED_SYNC_T:
+ drbd_resync_finished(mdev);
+ default:
+ /* nothing to do */
+ break;
+ }
+ }
+ drbd_bcast_sync_progress(mdev);
+
+ return 1;
+}
+
+
+/* ATTENTION. The AL's extents are 4MB each, while the extents in the
+ * resync LRU-cache are 16MB each.
+ * The caller of this function has to hold an get_ldev() reference.
+ *
+ * TODO will be obsoleted once we have a caching lru of the on disk bitmap
+ */
+static void drbd_try_clear_on_disk_bm(struct drbd_conf *mdev, sector_t sector,
+ int count, int success)
+{
+ struct lc_element *e;
+ struct update_odbm_work *udw;
+
+ unsigned int enr;
+
+ D_ASSERT(atomic_read(&mdev->local_cnt));
+
+ /* I simply assume that a sector/size pair never crosses
+ * a 16 MB extent border. (Currently this is true...) */
+ enr = BM_SECT_TO_EXT(sector);
+
+ e = lc_get(mdev->resync, enr);
+ if (e) {
+ struct bm_extent *ext = lc_entry(e, struct bm_extent, lce);
+ if (ext->lce.lc_number == enr) {
+ if (success)
+ ext->rs_left -= count;
+ else
+ ext->rs_failed += count;
+ if (ext->rs_left < ext->rs_failed) {
+ dev_err(DEV, "BAD! sector=%llus enr=%u rs_left=%d "
+ "rs_failed=%d count=%d\n",
+ (unsigned long long)sector,
+ ext->lce.lc_number, ext->rs_left,
+ ext->rs_failed, count);
+ dump_stack();
+
+ lc_put(mdev->resync, &ext->lce);
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return;
+ }
+ } else {
+ /* Normally this element should be in the cache,
+ * since drbd_rs_begin_io() pulled it already in.
+ *
+ * But maybe an application write finished, and we set
+ * something outside the resync lru_cache in sync.
+ */
+ int rs_left = drbd_bm_e_weight(mdev, enr);
+ if (ext->flags != 0) {
+ dev_warn(DEV, "changing resync lce: %d[%u;%02lx]"
+ " -> %d[%u;00]\n",
+ ext->lce.lc_number, ext->rs_left,
+ ext->flags, enr, rs_left);
+ ext->flags = 0;
+ }
+ if (ext->rs_failed) {
+ dev_warn(DEV, "Kicking resync_lru element enr=%u "
+ "out with rs_failed=%d\n",
+ ext->lce.lc_number, ext->rs_failed);
+ set_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags);
+ }
+ ext->rs_left = rs_left;
+ ext->rs_failed = success ? 0 : count;
+ lc_changed(mdev->resync, &ext->lce);
+ }
+ lc_put(mdev->resync, &ext->lce);
+ /* no race, we are within the al_lock! */
+
+ if (ext->rs_left == ext->rs_failed) {
+ ext->rs_failed = 0;
+
+ udw = kmalloc(sizeof(*udw), GFP_ATOMIC);
+ if (udw) {
+ udw->enr = ext->lce.lc_number;
+ udw->w.cb = w_update_odbm;
+ drbd_queue_work_front(&mdev->data.work, &udw->w);
+ } else {
+ dev_warn(DEV, "Could not kmalloc an udw\n");
+ set_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags);
+ }
+ }
+ } else {
+ dev_err(DEV, "lc_get() failed! locked=%d/%d flags=%lu\n",
+ mdev->resync_locked,
+ mdev->resync->nr_elements,
+ mdev->resync->flags);
+ }
+}
+
+/* clear the bit corresponding to the piece of storage in question:
+ * size byte of data starting from sector. Only clear a bits of the affected
+ * one ore more _aligned_ BM_BLOCK_SIZE blocks.
+ *
+ * called by worker on C_SYNC_TARGET and receiver on SyncSource.
+ *
+ */
+void __drbd_set_in_sync(struct drbd_conf *mdev, sector_t sector, int size,
+ const char *file, const unsigned int line)
+{
+ /* Is called from worker and receiver context _only_ */
+ unsigned long sbnr, ebnr, lbnr;
+ unsigned long count = 0;
+ sector_t esector, nr_sectors;
+ int wake_up = 0;
+ unsigned long flags;
+
+ if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) {
+ dev_err(DEV, "drbd_set_in_sync: sector=%llus size=%d nonsense!\n",
+ (unsigned long long)sector, size);
+ return;
+ }
+ nr_sectors = drbd_get_capacity(mdev->this_bdev);
+ esector = sector + (size >> 9) - 1;
+
+ ERR_IF(sector >= nr_sectors) return;
+ ERR_IF(esector >= nr_sectors) esector = (nr_sectors-1);
+
+ lbnr = BM_SECT_TO_BIT(nr_sectors-1);
+
+ /* we clear it (in sync).
+ * round up start sector, round down end sector. we make sure we only
+ * clear full, aligned, BM_BLOCK_SIZE (4K) blocks */
+ if (unlikely(esector < BM_SECT_PER_BIT-1))
+ return;
+ if (unlikely(esector == (nr_sectors-1)))
+ ebnr = lbnr;
+ else
+ ebnr = BM_SECT_TO_BIT(esector - (BM_SECT_PER_BIT-1));
+ sbnr = BM_SECT_TO_BIT(sector + BM_SECT_PER_BIT-1);
+
+ if (sbnr > ebnr)
+ return;
+
+ /*
+ * ok, (capacity & 7) != 0 sometimes, but who cares...
+ * we count rs_{total,left} in bits, not sectors.
+ */
+ spin_lock_irqsave(&mdev->al_lock, flags);
+ count = drbd_bm_clear_bits(mdev, sbnr, ebnr);
+ if (count) {
+ /* we need the lock for drbd_try_clear_on_disk_bm */
+ if (jiffies - mdev->rs_mark_time > HZ*10) {
+ /* should be rolling marks,
+ * but we estimate only anyways. */
+ if (mdev->rs_mark_left != drbd_bm_total_weight(mdev) &&
+ mdev->state.conn != C_PAUSED_SYNC_T &&
+ mdev->state.conn != C_PAUSED_SYNC_S) {
+ mdev->rs_mark_time = jiffies;
+ mdev->rs_mark_left = drbd_bm_total_weight(mdev);
+ }
+ }
+ if (get_ldev(mdev)) {
+ drbd_try_clear_on_disk_bm(mdev, sector, count, TRUE);
+ put_ldev(mdev);
+ }
+ /* just wake_up unconditional now, various lc_chaged(),
+ * lc_put() in drbd_try_clear_on_disk_bm(). */
+ wake_up = 1;
+ }
+ spin_unlock_irqrestore(&mdev->al_lock, flags);
+ if (wake_up)
+ wake_up(&mdev->al_wait);
+}
+
+/*
+ * this is intended to set one request worth of data out of sync.
+ * affects at least 1 bit,
+ * and at most 1+DRBD_MAX_SEGMENT_SIZE/BM_BLOCK_SIZE bits.
+ *
+ * called by tl_clear and drbd_send_dblock (==drbd_make_request).
+ * so this can be _any_ process.
+ */
+void __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size,
+ const char *file, const unsigned int line)
+{
+ unsigned long sbnr, ebnr, lbnr, flags;
+ sector_t esector, nr_sectors;
+ unsigned int enr, count;
+ struct lc_element *e;
+
+ if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) {
+ dev_err(DEV, "sector: %llus, size: %d\n",
+ (unsigned long long)sector, size);
+ return;
+ }
+
+ if (!get_ldev(mdev))
+ return; /* no disk, no metadata, no bitmap to set bits in */
+
+ nr_sectors = drbd_get_capacity(mdev->this_bdev);
+ esector = sector + (size >> 9) - 1;
+
+ ERR_IF(sector >= nr_sectors)
+ goto out;
+ ERR_IF(esector >= nr_sectors)
+ esector = (nr_sectors-1);
+
+ lbnr = BM_SECT_TO_BIT(nr_sectors-1);
+
+ /* we set it out of sync,
+ * we do not need to round anything here */
+ sbnr = BM_SECT_TO_BIT(sector);
+ ebnr = BM_SECT_TO_BIT(esector);
+
+ /* ok, (capacity & 7) != 0 sometimes, but who cares...
+ * we count rs_{total,left} in bits, not sectors. */
+ spin_lock_irqsave(&mdev->al_lock, flags);
+ count = drbd_bm_set_bits(mdev, sbnr, ebnr);
+
+ enr = BM_SECT_TO_EXT(sector);
+ e = lc_find(mdev->resync, enr);
+ if (e)
+ lc_entry(e, struct bm_extent, lce)->rs_left += count;
+ spin_unlock_irqrestore(&mdev->al_lock, flags);
+
+out:
+ put_ldev(mdev);
+}
+
+static
+struct bm_extent *_bme_get(struct drbd_conf *mdev, unsigned int enr)
+{
+ struct lc_element *e;
+ struct bm_extent *bm_ext;
+ int wakeup = 0;
+ unsigned long rs_flags;
+
+ spin_lock_irq(&mdev->al_lock);
+ if (mdev->resync_locked > mdev->resync->nr_elements/2) {
+ spin_unlock_irq(&mdev->al_lock);
+ return NULL;
+ }
+ e = lc_get(mdev->resync, enr);
+ bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL;
+ if (bm_ext) {
+ if (bm_ext->lce.lc_number != enr) {
+ bm_ext->rs_left = drbd_bm_e_weight(mdev, enr);
+ bm_ext->rs_failed = 0;
+ lc_changed(mdev->resync, &bm_ext->lce);
+ wakeup = 1;
+ }
+ if (bm_ext->lce.refcnt == 1)
+ mdev->resync_locked++;
+ set_bit(BME_NO_WRITES, &bm_ext->flags);
+ }
+ rs_flags = mdev->resync->flags;
+ spin_unlock_irq(&mdev->al_lock);
+ if (wakeup)
+ wake_up(&mdev->al_wait);
+
+ if (!bm_ext) {
+ if (rs_flags & LC_STARVING)
+ dev_warn(DEV, "Have to wait for element"
+ " (resync LRU too small?)\n");
+ BUG_ON(rs_flags & LC_DIRTY);
+ }
+
+ return bm_ext;
+}
+
+static int _is_in_al(struct drbd_conf *mdev, unsigned int enr)
+{
+ struct lc_element *al_ext;
+ int rv = 0;
+
+ spin_lock_irq(&mdev->al_lock);
+ if (unlikely(enr == mdev->act_log->new_number))
+ rv = 1;
+ else {
+ al_ext = lc_find(mdev->act_log, enr);
+ if (al_ext) {
+ if (al_ext->refcnt)
+ rv = 1;
+ }
+ }
+ spin_unlock_irq(&mdev->al_lock);
+
+ /*
+ if (unlikely(rv)) {
+ dev_info(DEV, "Delaying sync read until app's write is done\n");
+ }
+ */
+ return rv;
+}
+
+/**
+ * drbd_rs_begin_io() - Gets an extent in the resync LRU cache and sets it to BME_LOCKED
+ * @mdev: DRBD device.
+ * @sector: The sector number.
+ *
+ * This functions sleeps on al_wait. Returns 1 on success, 0 if interrupted.
+ */
+int drbd_rs_begin_io(struct drbd_conf *mdev, sector_t sector)
+{
+ unsigned int enr = BM_SECT_TO_EXT(sector);
+ struct bm_extent *bm_ext;
+ int i, sig;
+
+ sig = wait_event_interruptible(mdev->al_wait,
+ (bm_ext = _bme_get(mdev, enr)));
+ if (sig)
+ return 0;
+
+ if (test_bit(BME_LOCKED, &bm_ext->flags))
+ return 1;
+
+ for (i = 0; i < AL_EXT_PER_BM_SECT; i++) {
+ sig = wait_event_interruptible(mdev->al_wait,
+ !_is_in_al(mdev, enr * AL_EXT_PER_BM_SECT + i));
+ if (sig) {
+ spin_lock_irq(&mdev->al_lock);
+ if (lc_put(mdev->resync, &bm_ext->lce) == 0) {
+ clear_bit(BME_NO_WRITES, &bm_ext->flags);
+ mdev->resync_locked--;
+ wake_up(&mdev->al_wait);
+ }
+ spin_unlock_irq(&mdev->al_lock);
+ return 0;
+ }
+ }
+
+ set_bit(BME_LOCKED, &bm_ext->flags);
+
+ return 1;
+}
+
+/**
+ * drbd_try_rs_begin_io() - Gets an extent in the resync LRU cache, does not sleep
+ * @mdev: DRBD device.
+ * @sector: The sector number.
+ *
+ * Gets an extent in the resync LRU cache, sets it to BME_NO_WRITES, then
+ * tries to set it to BME_LOCKED. Returns 0 upon success, and -EAGAIN
+ * if there is still application IO going on in this area.
+ */
+int drbd_try_rs_begin_io(struct drbd_conf *mdev, sector_t sector)
+{
+ unsigned int enr = BM_SECT_TO_EXT(sector);
+ const unsigned int al_enr = enr*AL_EXT_PER_BM_SECT;
+ struct lc_element *e;
+ struct bm_extent *bm_ext;
+ int i;
+
+ spin_lock_irq(&mdev->al_lock);
+ if (mdev->resync_wenr != LC_FREE && mdev->resync_wenr != enr) {
+ /* in case you have very heavy scattered io, it may
+ * stall the syncer undefined if we give up the ref count
+ * when we try again and requeue.
+ *
+ * if we don't give up the refcount, but the next time
+ * we are scheduled this extent has been "synced" by new
+ * application writes, we'd miss the lc_put on the
+ * extent we keep the refcount on.
+ * so we remembered which extent we had to try again, and
+ * if the next requested one is something else, we do
+ * the lc_put here...
+ * we also have to wake_up
+ */
+ e = lc_find(mdev->resync, mdev->resync_wenr);
+ bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL;
+ if (bm_ext) {
+ D_ASSERT(!test_bit(BME_LOCKED, &bm_ext->flags));
+ D_ASSERT(test_bit(BME_NO_WRITES, &bm_ext->flags));
+ clear_bit(BME_NO_WRITES, &bm_ext->flags);
+ mdev->resync_wenr = LC_FREE;
+ if (lc_put(mdev->resync, &bm_ext->lce) == 0)
+ mdev->resync_locked--;
+ wake_up(&mdev->al_wait);
+ } else {
+ dev_alert(DEV, "LOGIC BUG\n");
+ }
+ }
+ /* TRY. */
+ e = lc_try_get(mdev->resync, enr);
+ bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL;
+ if (bm_ext) {
+ if (test_bit(BME_LOCKED, &bm_ext->flags))
+ goto proceed;
+ if (!test_and_set_bit(BME_NO_WRITES, &bm_ext->flags)) {
+ mdev->resync_locked++;
+ } else {
+ /* we did set the BME_NO_WRITES,
+ * but then could not set BME_LOCKED,
+ * so we tried again.
+ * drop the extra reference. */
+ bm_ext->lce.refcnt--;
+ D_ASSERT(bm_ext->lce.refcnt > 0);
+ }
+ goto check_al;
+ } else {
+ /* do we rather want to try later? */
+ if (mdev->resync_locked > mdev->resync->nr_elements-3)
+ goto try_again;
+ /* Do or do not. There is no try. -- Yoda */
+ e = lc_get(mdev->resync, enr);
+ bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL;
+ if (!bm_ext) {
+ const unsigned long rs_flags = mdev->resync->flags;
+ if (rs_flags & LC_STARVING)
+ dev_warn(DEV, "Have to wait for element"
+ " (resync LRU too small?)\n");
+ BUG_ON(rs_flags & LC_DIRTY);
+ goto try_again;
+ }
+ if (bm_ext->lce.lc_number != enr) {
+ bm_ext->rs_left = drbd_bm_e_weight(mdev, enr);
+ bm_ext->rs_failed = 0;
+ lc_changed(mdev->resync, &bm_ext->lce);
+ wake_up(&mdev->al_wait);
+ D_ASSERT(test_bit(BME_LOCKED, &bm_ext->flags) == 0);
+ }
+ set_bit(BME_NO_WRITES, &bm_ext->flags);
+ D_ASSERT(bm_ext->lce.refcnt == 1);
+ mdev->resync_locked++;
+ goto check_al;
+ }
+check_al:
+ for (i = 0; i < AL_EXT_PER_BM_SECT; i++) {
+ if (unlikely(al_enr+i == mdev->act_log->new_number))
+ goto try_again;
+ if (lc_is_used(mdev->act_log, al_enr+i))
+ goto try_again;
+ }
+ set_bit(BME_LOCKED, &bm_ext->flags);
+proceed:
+ mdev->resync_wenr = LC_FREE;
+ spin_unlock_irq(&mdev->al_lock);
+ return 0;
+
+try_again:
+ if (bm_ext)
+ mdev->resync_wenr = enr;
+ spin_unlock_irq(&mdev->al_lock);
+ return -EAGAIN;
+}
+
+void drbd_rs_complete_io(struct drbd_conf *mdev, sector_t sector)
+{
+ unsigned int enr = BM_SECT_TO_EXT(sector);
+ struct lc_element *e;
+ struct bm_extent *bm_ext;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->al_lock, flags);
+ e = lc_find(mdev->resync, enr);
+ bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL;
+ if (!bm_ext) {
+ spin_unlock_irqrestore(&mdev->al_lock, flags);
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "drbd_rs_complete_io() called, but extent not found\n");
+ return;
+ }
+
+ if (bm_ext->lce.refcnt == 0) {
+ spin_unlock_irqrestore(&mdev->al_lock, flags);
+ dev_err(DEV, "drbd_rs_complete_io(,%llu [=%u]) called, "
+ "but refcnt is 0!?\n",
+ (unsigned long long)sector, enr);
+ return;
+ }
+
+ if (lc_put(mdev->resync, &bm_ext->lce) == 0) {
+ clear_bit(BME_LOCKED, &bm_ext->flags);
+ clear_bit(BME_NO_WRITES, &bm_ext->flags);
+ mdev->resync_locked--;
+ wake_up(&mdev->al_wait);
+ }
+
+ spin_unlock_irqrestore(&mdev->al_lock, flags);
+}
+
+/**
+ * drbd_rs_cancel_all() - Removes all extents from the resync LRU (even BME_LOCKED)
+ * @mdev: DRBD device.
+ */
+void drbd_rs_cancel_all(struct drbd_conf *mdev)
+{
+ spin_lock_irq(&mdev->al_lock);
+
+ if (get_ldev_if_state(mdev, D_FAILED)) { /* Makes sure ->resync is there. */
+ lc_reset(mdev->resync);
+ put_ldev(mdev);
+ }
+ mdev->resync_locked = 0;
+ mdev->resync_wenr = LC_FREE;
+ spin_unlock_irq(&mdev->al_lock);
+ wake_up(&mdev->al_wait);
+}
+
+/**
+ * drbd_rs_del_all() - Gracefully remove all extents from the resync LRU
+ * @mdev: DRBD device.
+ *
+ * Returns 0 upon success, -EAGAIN if at least one reference count was
+ * not zero.
+ */
+int drbd_rs_del_all(struct drbd_conf *mdev)
+{
+ struct lc_element *e;
+ struct bm_extent *bm_ext;
+ int i;
+
+ spin_lock_irq(&mdev->al_lock);
+
+ if (get_ldev_if_state(mdev, D_FAILED)) {
+ /* ok, ->resync is there. */
+ for (i = 0; i < mdev->resync->nr_elements; i++) {
+ e = lc_element_by_index(mdev->resync, i);
+ bm_ext = e ? lc_entry(e, struct bm_extent, lce) : NULL;
+ if (bm_ext->lce.lc_number == LC_FREE)
+ continue;
+ if (bm_ext->lce.lc_number == mdev->resync_wenr) {
+ dev_info(DEV, "dropping %u in drbd_rs_del_all, apparently"
+ " got 'synced' by application io\n",
+ mdev->resync_wenr);
+ D_ASSERT(!test_bit(BME_LOCKED, &bm_ext->flags));
+ D_ASSERT(test_bit(BME_NO_WRITES, &bm_ext->flags));
+ clear_bit(BME_NO_WRITES, &bm_ext->flags);
+ mdev->resync_wenr = LC_FREE;
+ lc_put(mdev->resync, &bm_ext->lce);
+ }
+ if (bm_ext->lce.refcnt != 0) {
+ dev_info(DEV, "Retrying drbd_rs_del_all() later. "
+ "refcnt=%d\n", bm_ext->lce.refcnt);
+ put_ldev(mdev);
+ spin_unlock_irq(&mdev->al_lock);
+ return -EAGAIN;
+ }
+ D_ASSERT(!test_bit(BME_LOCKED, &bm_ext->flags));
+ D_ASSERT(!test_bit(BME_NO_WRITES, &bm_ext->flags));
+ lc_del(mdev->resync, &bm_ext->lce);
+ }
+ D_ASSERT(mdev->resync->used == 0);
+ put_ldev(mdev);
+ }
+ spin_unlock_irq(&mdev->al_lock);
+
+ return 0;
+}
+
+/**
+ * drbd_rs_failed_io() - Record information on a failure to resync the specified blocks
+ * @mdev: DRBD device.
+ * @sector: The sector number.
+ * @size: Size of failed IO operation, in byte.
+ */
+void drbd_rs_failed_io(struct drbd_conf *mdev, sector_t sector, int size)
+{
+ /* Is called from worker and receiver context _only_ */
+ unsigned long sbnr, ebnr, lbnr;
+ unsigned long count;
+ sector_t esector, nr_sectors;
+ int wake_up = 0;
+
+ if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) {
+ dev_err(DEV, "drbd_rs_failed_io: sector=%llus size=%d nonsense!\n",
+ (unsigned long long)sector, size);
+ return;
+ }
+ nr_sectors = drbd_get_capacity(mdev->this_bdev);
+ esector = sector + (size >> 9) - 1;
+
+ ERR_IF(sector >= nr_sectors) return;
+ ERR_IF(esector >= nr_sectors) esector = (nr_sectors-1);
+
+ lbnr = BM_SECT_TO_BIT(nr_sectors-1);
+
+ /*
+ * round up start sector, round down end sector. we make sure we only
+ * handle full, aligned, BM_BLOCK_SIZE (4K) blocks */
+ if (unlikely(esector < BM_SECT_PER_BIT-1))
+ return;
+ if (unlikely(esector == (nr_sectors-1)))
+ ebnr = lbnr;
+ else
+ ebnr = BM_SECT_TO_BIT(esector - (BM_SECT_PER_BIT-1));
+ sbnr = BM_SECT_TO_BIT(sector + BM_SECT_PER_BIT-1);
+
+ if (sbnr > ebnr)
+ return;
+
+ /*
+ * ok, (capacity & 7) != 0 sometimes, but who cares...
+ * we count rs_{total,left} in bits, not sectors.
+ */
+ spin_lock_irq(&mdev->al_lock);
+ count = drbd_bm_count_bits(mdev, sbnr, ebnr);
+ if (count) {
+ mdev->rs_failed += count;
+
+ if (get_ldev(mdev)) {
+ drbd_try_clear_on_disk_bm(mdev, sector, count, FALSE);
+ put_ldev(mdev);
+ }
+
+ /* just wake_up unconditional now, various lc_chaged(),
+ * lc_put() in drbd_try_clear_on_disk_bm(). */
+ wake_up = 1;
+ }
+ spin_unlock_irq(&mdev->al_lock);
+ if (wake_up)
+ wake_up(&mdev->al_wait);
+}
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
new file mode 100644
index 00000000000..b61057e7788
--- /dev/null
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -0,0 +1,1327 @@
+/*
+ drbd_bitmap.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2004-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 2004-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2004-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/bitops.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/drbd.h>
+#include <asm/kmap_types.h>
+#include "drbd_int.h"
+
+/* OPAQUE outside this file!
+ * interface defined in drbd_int.h
+
+ * convention:
+ * function name drbd_bm_... => used elsewhere, "public".
+ * function name bm_... => internal to implementation, "private".
+
+ * Note that since find_first_bit returns int, at the current granularity of
+ * the bitmap (4KB per byte), this implementation "only" supports up to
+ * 1<<(32+12) == 16 TB...
+ */
+
+/*
+ * NOTE
+ * Access to the *bm_pages is protected by bm_lock.
+ * It is safe to read the other members within the lock.
+ *
+ * drbd_bm_set_bits is called from bio_endio callbacks,
+ * We may be called with irq already disabled,
+ * so we need spin_lock_irqsave().
+ * And we need the kmap_atomic.
+ */
+struct drbd_bitmap {
+ struct page **bm_pages;
+ spinlock_t bm_lock;
+ /* WARNING unsigned long bm_*:
+ * 32bit number of bit offset is just enough for 512 MB bitmap.
+ * it will blow up if we make the bitmap bigger...
+ * not that it makes much sense to have a bitmap that large,
+ * rather change the granularity to 16k or 64k or something.
+ * (that implies other problems, however...)
+ */
+ unsigned long bm_set; /* nr of set bits; THINK maybe atomic_t? */
+ unsigned long bm_bits;
+ size_t bm_words;
+ size_t bm_number_of_pages;
+ sector_t bm_dev_capacity;
+ struct semaphore bm_change; /* serializes resize operations */
+
+ atomic_t bm_async_io;
+ wait_queue_head_t bm_io_wait;
+
+ unsigned long bm_flags;
+
+ /* debugging aid, in case we are still racy somewhere */
+ char *bm_why;
+ struct task_struct *bm_task;
+};
+
+/* definition of bits in bm_flags */
+#define BM_LOCKED 0
+#define BM_MD_IO_ERROR 1
+#define BM_P_VMALLOCED 2
+
+static int bm_is_locked(struct drbd_bitmap *b)
+{
+ return test_bit(BM_LOCKED, &b->bm_flags);
+}
+
+#define bm_print_lock_info(m) __bm_print_lock_info(m, __func__)
+static void __bm_print_lock_info(struct drbd_conf *mdev, const char *func)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ if (!__ratelimit(&drbd_ratelimit_state))
+ return;
+ dev_err(DEV, "FIXME %s in %s, bitmap locked for '%s' by %s\n",
+ current == mdev->receiver.task ? "receiver" :
+ current == mdev->asender.task ? "asender" :
+ current == mdev->worker.task ? "worker" : current->comm,
+ func, b->bm_why ?: "?",
+ b->bm_task == mdev->receiver.task ? "receiver" :
+ b->bm_task == mdev->asender.task ? "asender" :
+ b->bm_task == mdev->worker.task ? "worker" : "?");
+}
+
+void drbd_bm_lock(struct drbd_conf *mdev, char *why)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ int trylock_failed;
+
+ if (!b) {
+ dev_err(DEV, "FIXME no bitmap in drbd_bm_lock!?\n");
+ return;
+ }
+
+ trylock_failed = down_trylock(&b->bm_change);
+
+ if (trylock_failed) {
+ dev_warn(DEV, "%s going to '%s' but bitmap already locked for '%s' by %s\n",
+ current == mdev->receiver.task ? "receiver" :
+ current == mdev->asender.task ? "asender" :
+ current == mdev->worker.task ? "worker" : current->comm,
+ why, b->bm_why ?: "?",
+ b->bm_task == mdev->receiver.task ? "receiver" :
+ b->bm_task == mdev->asender.task ? "asender" :
+ b->bm_task == mdev->worker.task ? "worker" : "?");
+ down(&b->bm_change);
+ }
+ if (__test_and_set_bit(BM_LOCKED, &b->bm_flags))
+ dev_err(DEV, "FIXME bitmap already locked in bm_lock\n");
+
+ b->bm_why = why;
+ b->bm_task = current;
+}
+
+void drbd_bm_unlock(struct drbd_conf *mdev)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ if (!b) {
+ dev_err(DEV, "FIXME no bitmap in drbd_bm_unlock!?\n");
+ return;
+ }
+
+ if (!__test_and_clear_bit(BM_LOCKED, &mdev->bitmap->bm_flags))
+ dev_err(DEV, "FIXME bitmap not locked in bm_unlock\n");
+
+ b->bm_why = NULL;
+ b->bm_task = NULL;
+ up(&b->bm_change);
+}
+
+/* word offset to long pointer */
+static unsigned long *__bm_map_paddr(struct drbd_bitmap *b, unsigned long offset, const enum km_type km)
+{
+ struct page *page;
+ unsigned long page_nr;
+
+ /* page_nr = (word*sizeof(long)) >> PAGE_SHIFT; */
+ page_nr = offset >> (PAGE_SHIFT - LN2_BPL + 3);
+ BUG_ON(page_nr >= b->bm_number_of_pages);
+ page = b->bm_pages[page_nr];
+
+ return (unsigned long *) kmap_atomic(page, km);
+}
+
+static unsigned long * bm_map_paddr(struct drbd_bitmap *b, unsigned long offset)
+{
+ return __bm_map_paddr(b, offset, KM_IRQ1);
+}
+
+static void __bm_unmap(unsigned long *p_addr, const enum km_type km)
+{
+ kunmap_atomic(p_addr, km);
+};
+
+static void bm_unmap(unsigned long *p_addr)
+{
+ return __bm_unmap(p_addr, KM_IRQ1);
+}
+
+/* long word offset of _bitmap_ sector */
+#define S2W(s) ((s)<<(BM_EXT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL))
+/* word offset from start of bitmap to word number _in_page_
+ * modulo longs per page
+#define MLPP(X) ((X) % (PAGE_SIZE/sizeof(long))
+ hm, well, Philipp thinks gcc might not optimze the % into & (... - 1)
+ so do it explicitly:
+ */
+#define MLPP(X) ((X) & ((PAGE_SIZE/sizeof(long))-1))
+
+/* Long words per page */
+#define LWPP (PAGE_SIZE/sizeof(long))
+
+/*
+ * actually most functions herein should take a struct drbd_bitmap*, not a
+ * struct drbd_conf*, but for the debug macros I like to have the mdev around
+ * to be able to report device specific.
+ */
+
+static void bm_free_pages(struct page **pages, unsigned long number)
+{
+ unsigned long i;
+ if (!pages)
+ return;
+
+ for (i = 0; i < number; i++) {
+ if (!pages[i]) {
+ printk(KERN_ALERT "drbd: bm_free_pages tried to free "
+ "a NULL pointer; i=%lu n=%lu\n",
+ i, number);
+ continue;
+ }
+ __free_page(pages[i]);
+ pages[i] = NULL;
+ }
+}
+
+static void bm_vk_free(void *ptr, int v)
+{
+ if (v)
+ vfree(ptr);
+ else
+ kfree(ptr);
+}
+
+/*
+ * "have" and "want" are NUMBER OF PAGES.
+ */
+static struct page **bm_realloc_pages(struct drbd_bitmap *b, unsigned long want)
+{
+ struct page **old_pages = b->bm_pages;
+ struct page **new_pages, *page;
+ unsigned int i, bytes, vmalloced = 0;
+ unsigned long have = b->bm_number_of_pages;
+
+ BUG_ON(have == 0 && old_pages != NULL);
+ BUG_ON(have != 0 && old_pages == NULL);
+
+ if (have == want)
+ return old_pages;
+
+ /* Trying kmalloc first, falling back to vmalloc.
+ * GFP_KERNEL is ok, as this is done when a lower level disk is
+ * "attached" to the drbd. Context is receiver thread or cqueue
+ * thread. As we have no disk yet, we are not in the IO path,
+ * not even the IO path of the peer. */
+ bytes = sizeof(struct page *)*want;
+ new_pages = kmalloc(bytes, GFP_KERNEL);
+ if (!new_pages) {
+ new_pages = vmalloc(bytes);
+ if (!new_pages)
+ return NULL;
+ vmalloced = 1;
+ }
+
+ memset(new_pages, 0, bytes);
+ if (want >= have) {
+ for (i = 0; i < have; i++)
+ new_pages[i] = old_pages[i];
+ for (; i < want; i++) {
+ page = alloc_page(GFP_HIGHUSER);
+ if (!page) {
+ bm_free_pages(new_pages + have, i - have);
+ bm_vk_free(new_pages, vmalloced);
+ return NULL;
+ }
+ new_pages[i] = page;
+ }
+ } else {
+ for (i = 0; i < want; i++)
+ new_pages[i] = old_pages[i];
+ /* NOT HERE, we are outside the spinlock!
+ bm_free_pages(old_pages + want, have - want);
+ */
+ }
+
+ if (vmalloced)
+ set_bit(BM_P_VMALLOCED, &b->bm_flags);
+ else
+ clear_bit(BM_P_VMALLOCED, &b->bm_flags);
+
+ return new_pages;
+}
+
+/*
+ * called on driver init only. TODO call when a device is created.
+ * allocates the drbd_bitmap, and stores it in mdev->bitmap.
+ */
+int drbd_bm_init(struct drbd_conf *mdev)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ WARN_ON(b != NULL);
+ b = kzalloc(sizeof(struct drbd_bitmap), GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ spin_lock_init(&b->bm_lock);
+ init_MUTEX(&b->bm_change);
+ init_waitqueue_head(&b->bm_io_wait);
+
+ mdev->bitmap = b;
+
+ return 0;
+}
+
+sector_t drbd_bm_capacity(struct drbd_conf *mdev)
+{
+ ERR_IF(!mdev->bitmap) return 0;
+ return mdev->bitmap->bm_dev_capacity;
+}
+
+/* called on driver unload. TODO: call when a device is destroyed.
+ */
+void drbd_bm_cleanup(struct drbd_conf *mdev)
+{
+ ERR_IF (!mdev->bitmap) return;
+ bm_free_pages(mdev->bitmap->bm_pages, mdev->bitmap->bm_number_of_pages);
+ bm_vk_free(mdev->bitmap->bm_pages, test_bit(BM_P_VMALLOCED, &mdev->bitmap->bm_flags));
+ kfree(mdev->bitmap);
+ mdev->bitmap = NULL;
+}
+
+/*
+ * since (b->bm_bits % BITS_PER_LONG) != 0,
+ * this masks out the remaining bits.
+ * Returns the number of bits cleared.
+ */
+static int bm_clear_surplus(struct drbd_bitmap *b)
+{
+ const unsigned long mask = (1UL << (b->bm_bits & (BITS_PER_LONG-1))) - 1;
+ size_t w = b->bm_bits >> LN2_BPL;
+ int cleared = 0;
+ unsigned long *p_addr, *bm;
+
+ p_addr = bm_map_paddr(b, w);
+ bm = p_addr + MLPP(w);
+ if (w < b->bm_words) {
+ cleared = hweight_long(*bm & ~mask);
+ *bm &= mask;
+ w++; bm++;
+ }
+
+ if (w < b->bm_words) {
+ cleared += hweight_long(*bm);
+ *bm = 0;
+ }
+ bm_unmap(p_addr);
+ return cleared;
+}
+
+static void bm_set_surplus(struct drbd_bitmap *b)
+{
+ const unsigned long mask = (1UL << (b->bm_bits & (BITS_PER_LONG-1))) - 1;
+ size_t w = b->bm_bits >> LN2_BPL;
+ unsigned long *p_addr, *bm;
+
+ p_addr = bm_map_paddr(b, w);
+ bm = p_addr + MLPP(w);
+ if (w < b->bm_words) {
+ *bm |= ~mask;
+ bm++; w++;
+ }
+
+ if (w < b->bm_words) {
+ *bm = ~(0UL);
+ }
+ bm_unmap(p_addr);
+}
+
+static unsigned long __bm_count_bits(struct drbd_bitmap *b, const int swap_endian)
+{
+ unsigned long *p_addr, *bm, offset = 0;
+ unsigned long bits = 0;
+ unsigned long i, do_now;
+
+ while (offset < b->bm_words) {
+ i = do_now = min_t(size_t, b->bm_words-offset, LWPP);
+ p_addr = __bm_map_paddr(b, offset, KM_USER0);
+ bm = p_addr + MLPP(offset);
+ while (i--) {
+#ifndef __LITTLE_ENDIAN
+ if (swap_endian)
+ *bm = lel_to_cpu(*bm);
+#endif
+ bits += hweight_long(*bm++);
+ }
+ __bm_unmap(p_addr, KM_USER0);
+ offset += do_now;
+ cond_resched();
+ }
+
+ return bits;
+}
+
+static unsigned long bm_count_bits(struct drbd_bitmap *b)
+{
+ return __bm_count_bits(b, 0);
+}
+
+static unsigned long bm_count_bits_swap_endian(struct drbd_bitmap *b)
+{
+ return __bm_count_bits(b, 1);
+}
+
+/* offset and len in long words.*/
+static void bm_memset(struct drbd_bitmap *b, size_t offset, int c, size_t len)
+{
+ unsigned long *p_addr, *bm;
+ size_t do_now, end;
+
+#define BM_SECTORS_PER_BIT (BM_BLOCK_SIZE/512)
+
+ end = offset + len;
+
+ if (end > b->bm_words) {
+ printk(KERN_ALERT "drbd: bm_memset end > bm_words\n");
+ return;
+ }
+
+ while (offset < end) {
+ do_now = min_t(size_t, ALIGN(offset + 1, LWPP), end) - offset;
+ p_addr = bm_map_paddr(b, offset);
+ bm = p_addr + MLPP(offset);
+ if (bm+do_now > p_addr + LWPP) {
+ printk(KERN_ALERT "drbd: BUG BUG BUG! p_addr:%p bm:%p do_now:%d\n",
+ p_addr, bm, (int)do_now);
+ break; /* breaks to after catch_oob_access_end() only! */
+ }
+ memset(bm, c, do_now * sizeof(long));
+ bm_unmap(p_addr);
+ offset += do_now;
+ }
+}
+
+/*
+ * make sure the bitmap has enough room for the attached storage,
+ * if necessary, resize.
+ * called whenever we may have changed the device size.
+ * returns -ENOMEM if we could not allocate enough memory, 0 on success.
+ * In case this is actually a resize, we copy the old bitmap into the new one.
+ * Otherwise, the bitmap is initialized to all bits set.
+ */
+int drbd_bm_resize(struct drbd_conf *mdev, sector_t capacity)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long bits, words, owords, obits, *p_addr, *bm;
+ unsigned long want, have, onpages; /* number of pages */
+ struct page **npages, **opages = NULL;
+ int err = 0, growing;
+ int opages_vmalloced;
+
+ ERR_IF(!b) return -ENOMEM;
+
+ drbd_bm_lock(mdev, "resize");
+
+ dev_info(DEV, "drbd_bm_resize called with capacity == %llu\n",
+ (unsigned long long)capacity);
+
+ if (capacity == b->bm_dev_capacity)
+ goto out;
+
+ opages_vmalloced = test_bit(BM_P_VMALLOCED, &b->bm_flags);
+
+ if (capacity == 0) {
+ spin_lock_irq(&b->bm_lock);
+ opages = b->bm_pages;
+ onpages = b->bm_number_of_pages;
+ owords = b->bm_words;
+ b->bm_pages = NULL;
+ b->bm_number_of_pages =
+ b->bm_set =
+ b->bm_bits =
+ b->bm_words =
+ b->bm_dev_capacity = 0;
+ spin_unlock_irq(&b->bm_lock);
+ bm_free_pages(opages, onpages);
+ bm_vk_free(opages, opages_vmalloced);
+ goto out;
+ }
+ bits = BM_SECT_TO_BIT(ALIGN(capacity, BM_SECT_PER_BIT));
+
+ /* if we would use
+ words = ALIGN(bits,BITS_PER_LONG) >> LN2_BPL;
+ a 32bit host could present the wrong number of words
+ to a 64bit host.
+ */
+ words = ALIGN(bits, 64) >> LN2_BPL;
+
+ if (get_ldev(mdev)) {
+ D_ASSERT((u64)bits <= (((u64)mdev->ldev->md.md_size_sect-MD_BM_OFFSET) << 12));
+ put_ldev(mdev);
+ }
+
+ /* one extra long to catch off by one errors */
+ want = ALIGN((words+1)*sizeof(long), PAGE_SIZE) >> PAGE_SHIFT;
+ have = b->bm_number_of_pages;
+ if (want == have) {
+ D_ASSERT(b->bm_pages != NULL);
+ npages = b->bm_pages;
+ } else {
+ if (FAULT_ACTIVE(mdev, DRBD_FAULT_BM_ALLOC))
+ npages = NULL;
+ else
+ npages = bm_realloc_pages(b, want);
+ }
+
+ if (!npages) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ spin_lock_irq(&b->bm_lock);
+ opages = b->bm_pages;
+ owords = b->bm_words;
+ obits = b->bm_bits;
+
+ growing = bits > obits;
+ if (opages)
+ bm_set_surplus(b);
+
+ b->bm_pages = npages;
+ b->bm_number_of_pages = want;
+ b->bm_bits = bits;
+ b->bm_words = words;
+ b->bm_dev_capacity = capacity;
+
+ if (growing) {
+ bm_memset(b, owords, 0xff, words-owords);
+ b->bm_set += bits - obits;
+ }
+
+ if (want < have) {
+ /* implicit: (opages != NULL) && (opages != npages) */
+ bm_free_pages(opages + want, have - want);
+ }
+
+ p_addr = bm_map_paddr(b, words);
+ bm = p_addr + MLPP(words);
+ *bm = DRBD_MAGIC;
+ bm_unmap(p_addr);
+
+ (void)bm_clear_surplus(b);
+
+ spin_unlock_irq(&b->bm_lock);
+ if (opages != npages)
+ bm_vk_free(opages, opages_vmalloced);
+ if (!growing)
+ b->bm_set = bm_count_bits(b);
+ dev_info(DEV, "resync bitmap: bits=%lu words=%lu\n", bits, words);
+
+ out:
+ drbd_bm_unlock(mdev);
+ return err;
+}
+
+/* inherently racy:
+ * if not protected by other means, return value may be out of date when
+ * leaving this function...
+ * we still need to lock it, since it is important that this returns
+ * bm_set == 0 precisely.
+ *
+ * maybe bm_set should be atomic_t ?
+ */
+static unsigned long _drbd_bm_total_weight(struct drbd_conf *mdev)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long s;
+ unsigned long flags;
+
+ ERR_IF(!b) return 0;
+ ERR_IF(!b->bm_pages) return 0;
+
+ spin_lock_irqsave(&b->bm_lock, flags);
+ s = b->bm_set;
+ spin_unlock_irqrestore(&b->bm_lock, flags);
+
+ return s;
+}
+
+unsigned long drbd_bm_total_weight(struct drbd_conf *mdev)
+{
+ unsigned long s;
+ /* if I don't have a disk, I don't know about out-of-sync status */
+ if (!get_ldev_if_state(mdev, D_NEGOTIATING))
+ return 0;
+ s = _drbd_bm_total_weight(mdev);
+ put_ldev(mdev);
+ return s;
+}
+
+size_t drbd_bm_words(struct drbd_conf *mdev)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ ERR_IF(!b) return 0;
+ ERR_IF(!b->bm_pages) return 0;
+
+ return b->bm_words;
+}
+
+unsigned long drbd_bm_bits(struct drbd_conf *mdev)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ ERR_IF(!b) return 0;
+
+ return b->bm_bits;
+}
+
+/* merge number words from buffer into the bitmap starting at offset.
+ * buffer[i] is expected to be little endian unsigned long.
+ * bitmap must be locked by drbd_bm_lock.
+ * currently only used from receive_bitmap.
+ */
+void drbd_bm_merge_lel(struct drbd_conf *mdev, size_t offset, size_t number,
+ unsigned long *buffer)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long *p_addr, *bm;
+ unsigned long word, bits;
+ size_t end, do_now;
+
+ end = offset + number;
+
+ ERR_IF(!b) return;
+ ERR_IF(!b->bm_pages) return;
+ if (number == 0)
+ return;
+ WARN_ON(offset >= b->bm_words);
+ WARN_ON(end > b->bm_words);
+
+ spin_lock_irq(&b->bm_lock);
+ while (offset < end) {
+ do_now = min_t(size_t, ALIGN(offset+1, LWPP), end) - offset;
+ p_addr = bm_map_paddr(b, offset);
+ bm = p_addr + MLPP(offset);
+ offset += do_now;
+ while (do_now--) {
+ bits = hweight_long(*bm);
+ word = *bm | lel_to_cpu(*buffer++);
+ *bm++ = word;
+ b->bm_set += hweight_long(word) - bits;
+ }
+ bm_unmap(p_addr);
+ }
+ /* with 32bit <-> 64bit cross-platform connect
+ * this is only correct for current usage,
+ * where we _know_ that we are 64 bit aligned,
+ * and know that this function is used in this way, too...
+ */
+ if (end == b->bm_words)
+ b->bm_set -= bm_clear_surplus(b);
+
+ spin_unlock_irq(&b->bm_lock);
+}
+
+/* copy number words from the bitmap starting at offset into the buffer.
+ * buffer[i] will be little endian unsigned long.
+ */
+void drbd_bm_get_lel(struct drbd_conf *mdev, size_t offset, size_t number,
+ unsigned long *buffer)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long *p_addr, *bm;
+ size_t end, do_now;
+
+ end = offset + number;
+
+ ERR_IF(!b) return;
+ ERR_IF(!b->bm_pages) return;
+
+ spin_lock_irq(&b->bm_lock);
+ if ((offset >= b->bm_words) ||
+ (end > b->bm_words) ||
+ (number <= 0))
+ dev_err(DEV, "offset=%lu number=%lu bm_words=%lu\n",
+ (unsigned long) offset,
+ (unsigned long) number,
+ (unsigned long) b->bm_words);
+ else {
+ while (offset < end) {
+ do_now = min_t(size_t, ALIGN(offset+1, LWPP), end) - offset;
+ p_addr = bm_map_paddr(b, offset);
+ bm = p_addr + MLPP(offset);
+ offset += do_now;
+ while (do_now--)
+ *buffer++ = cpu_to_lel(*bm++);
+ bm_unmap(p_addr);
+ }
+ }
+ spin_unlock_irq(&b->bm_lock);
+}
+
+/* set all bits in the bitmap */
+void drbd_bm_set_all(struct drbd_conf *mdev)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ ERR_IF(!b) return;
+ ERR_IF(!b->bm_pages) return;
+
+ spin_lock_irq(&b->bm_lock);
+ bm_memset(b, 0, 0xff, b->bm_words);
+ (void)bm_clear_surplus(b);
+ b->bm_set = b->bm_bits;
+ spin_unlock_irq(&b->bm_lock);
+}
+
+/* clear all bits in the bitmap */
+void drbd_bm_clear_all(struct drbd_conf *mdev)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ ERR_IF(!b) return;
+ ERR_IF(!b->bm_pages) return;
+
+ spin_lock_irq(&b->bm_lock);
+ bm_memset(b, 0, 0, b->bm_words);
+ b->bm_set = 0;
+ spin_unlock_irq(&b->bm_lock);
+}
+
+static void bm_async_io_complete(struct bio *bio, int error)
+{
+ struct drbd_bitmap *b = bio->bi_private;
+ int uptodate = bio_flagged(bio, BIO_UPTODATE);
+
+
+ /* strange behavior of some lower level drivers...
+ * fail the request by clearing the uptodate flag,
+ * but do not return any error?!
+ * do we want to WARN() on this? */
+ if (!error && !uptodate)
+ error = -EIO;
+
+ if (error) {
+ /* doh. what now?
+ * for now, set all bits, and flag MD_IO_ERROR */
+ __set_bit(BM_MD_IO_ERROR, &b->bm_flags);
+ }
+ if (atomic_dec_and_test(&b->bm_async_io))
+ wake_up(&b->bm_io_wait);
+
+ bio_put(bio);
+}
+
+static void bm_page_io_async(struct drbd_conf *mdev, struct drbd_bitmap *b, int page_nr, int rw) __must_hold(local)
+{
+ /* we are process context. we always get a bio */
+ struct bio *bio = bio_alloc(GFP_KERNEL, 1);
+ unsigned int len;
+ sector_t on_disk_sector =
+ mdev->ldev->md.md_offset + mdev->ldev->md.bm_offset;
+ on_disk_sector += ((sector_t)page_nr) << (PAGE_SHIFT-9);
+
+ /* this might happen with very small
+ * flexible external meta data device */
+ len = min_t(unsigned int, PAGE_SIZE,
+ (drbd_md_last_sector(mdev->ldev) - on_disk_sector + 1)<<9);
+
+ bio->bi_bdev = mdev->ldev->md_bdev;
+ bio->bi_sector = on_disk_sector;
+ bio_add_page(bio, b->bm_pages[page_nr], len, 0);
+ bio->bi_private = b;
+ bio->bi_end_io = bm_async_io_complete;
+
+ if (FAULT_ACTIVE(mdev, (rw & WRITE) ? DRBD_FAULT_MD_WR : DRBD_FAULT_MD_RD)) {
+ bio->bi_rw |= rw;
+ bio_endio(bio, -EIO);
+ } else {
+ submit_bio(rw, bio);
+ }
+}
+
+# if defined(__LITTLE_ENDIAN)
+ /* nothing to do, on disk == in memory */
+# define bm_cpu_to_lel(x) ((void)0)
+# else
+void bm_cpu_to_lel(struct drbd_bitmap *b)
+{
+ /* need to cpu_to_lel all the pages ...
+ * this may be optimized by using
+ * cpu_to_lel(-1) == -1 and cpu_to_lel(0) == 0;
+ * the following is still not optimal, but better than nothing */
+ unsigned int i;
+ unsigned long *p_addr, *bm;
+ if (b->bm_set == 0) {
+ /* no page at all; avoid swap if all is 0 */
+ i = b->bm_number_of_pages;
+ } else if (b->bm_set == b->bm_bits) {
+ /* only the last page */
+ i = b->bm_number_of_pages - 1;
+ } else {
+ /* all pages */
+ i = 0;
+ }
+ for (; i < b->bm_number_of_pages; i++) {
+ p_addr = kmap_atomic(b->bm_pages[i], KM_USER0);
+ for (bm = p_addr; bm < p_addr + PAGE_SIZE/sizeof(long); bm++)
+ *bm = cpu_to_lel(*bm);
+ kunmap_atomic(p_addr, KM_USER0);
+ }
+}
+# endif
+/* lel_to_cpu == cpu_to_lel */
+# define bm_lel_to_cpu(x) bm_cpu_to_lel(x)
+
+/*
+ * bm_rw: read/write the whole bitmap from/to its on disk location.
+ */
+static int bm_rw(struct drbd_conf *mdev, int rw) __must_hold(local)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ /* sector_t sector; */
+ int bm_words, num_pages, i;
+ unsigned long now;
+ char ppb[10];
+ int err = 0;
+
+ WARN_ON(!bm_is_locked(b));
+
+ /* no spinlock here, the drbd_bm_lock should be enough! */
+
+ bm_words = drbd_bm_words(mdev);
+ num_pages = (bm_words*sizeof(long) + PAGE_SIZE-1) >> PAGE_SHIFT;
+
+ /* on disk bitmap is little endian */
+ if (rw == WRITE)
+ bm_cpu_to_lel(b);
+
+ now = jiffies;
+ atomic_set(&b->bm_async_io, num_pages);
+ __clear_bit(BM_MD_IO_ERROR, &b->bm_flags);
+
+ /* let the layers below us try to merge these bios... */
+ for (i = 0; i < num_pages; i++)
+ bm_page_io_async(mdev, b, i, rw);
+
+ drbd_blk_run_queue(bdev_get_queue(mdev->ldev->md_bdev));
+ wait_event(b->bm_io_wait, atomic_read(&b->bm_async_io) == 0);
+
+ if (test_bit(BM_MD_IO_ERROR, &b->bm_flags)) {
+ dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n");
+ drbd_chk_io_error(mdev, 1, TRUE);
+ err = -EIO;
+ }
+
+ now = jiffies;
+ if (rw == WRITE) {
+ /* swap back endianness */
+ bm_lel_to_cpu(b);
+ /* flush bitmap to stable storage */
+ drbd_md_flush(mdev);
+ } else /* rw == READ */ {
+ /* just read, if necessary adjust endianness */
+ b->bm_set = bm_count_bits_swap_endian(b);
+ dev_info(DEV, "recounting of set bits took additional %lu jiffies\n",
+ jiffies - now);
+ }
+ now = b->bm_set;
+
+ dev_info(DEV, "%s (%lu bits) marked out-of-sync by on disk bit-map.\n",
+ ppsize(ppb, now << (BM_BLOCK_SHIFT-10)), now);
+
+ return err;
+}
+
+/**
+ * drbd_bm_read() - Read the whole bitmap from its on disk location.
+ * @mdev: DRBD device.
+ */
+int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local)
+{
+ return bm_rw(mdev, READ);
+}
+
+/**
+ * drbd_bm_write() - Write the whole bitmap to its on disk location.
+ * @mdev: DRBD device.
+ */
+int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local)
+{
+ return bm_rw(mdev, WRITE);
+}
+
+/**
+ * drbd_bm_write_sect: Writes a 512 (MD_SECTOR_SIZE) byte piece of the bitmap
+ * @mdev: DRBD device.
+ * @enr: Extent number in the resync lru (happens to be sector offset)
+ *
+ * The BM_EXT_SIZE is on purpose exactly the amount of the bitmap covered
+ * by a single sector write. Therefore enr == sector offset from the
+ * start of the bitmap.
+ */
+int drbd_bm_write_sect(struct drbd_conf *mdev, unsigned long enr) __must_hold(local)
+{
+ sector_t on_disk_sector = enr + mdev->ldev->md.md_offset
+ + mdev->ldev->md.bm_offset;
+ int bm_words, num_words, offset;
+ int err = 0;
+
+ mutex_lock(&mdev->md_io_mutex);
+ bm_words = drbd_bm_words(mdev);
+ offset = S2W(enr); /* word offset into bitmap */
+ num_words = min(S2W(1), bm_words - offset);
+ if (num_words < S2W(1))
+ memset(page_address(mdev->md_io_page), 0, MD_SECTOR_SIZE);
+ drbd_bm_get_lel(mdev, offset, num_words,
+ page_address(mdev->md_io_page));
+ if (!drbd_md_sync_page_io(mdev, mdev->ldev, on_disk_sector, WRITE)) {
+ int i;
+ err = -EIO;
+ dev_err(DEV, "IO ERROR writing bitmap sector %lu "
+ "(meta-disk sector %llus)\n",
+ enr, (unsigned long long)on_disk_sector);
+ drbd_chk_io_error(mdev, 1, TRUE);
+ for (i = 0; i < AL_EXT_PER_BM_SECT; i++)
+ drbd_bm_ALe_set_all(mdev, enr*AL_EXT_PER_BM_SECT+i);
+ }
+ mdev->bm_writ_cnt++;
+ mutex_unlock(&mdev->md_io_mutex);
+ return err;
+}
+
+/* NOTE
+ * find_first_bit returns int, we return unsigned long.
+ * should not make much difference anyways, but ...
+ *
+ * this returns a bit number, NOT a sector!
+ */
+#define BPP_MASK ((1UL << (PAGE_SHIFT+3)) - 1)
+static unsigned long __bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo,
+ const int find_zero_bit, const enum km_type km)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long i = -1UL;
+ unsigned long *p_addr;
+ unsigned long bit_offset; /* bit offset of the mapped page. */
+
+ if (bm_fo > b->bm_bits) {
+ dev_err(DEV, "bm_fo=%lu bm_bits=%lu\n", bm_fo, b->bm_bits);
+ } else {
+ while (bm_fo < b->bm_bits) {
+ unsigned long offset;
+ bit_offset = bm_fo & ~BPP_MASK; /* bit offset of the page */
+ offset = bit_offset >> LN2_BPL; /* word offset of the page */
+ p_addr = __bm_map_paddr(b, offset, km);
+
+ if (find_zero_bit)
+ i = find_next_zero_bit(p_addr, PAGE_SIZE*8, bm_fo & BPP_MASK);
+ else
+ i = find_next_bit(p_addr, PAGE_SIZE*8, bm_fo & BPP_MASK);
+
+ __bm_unmap(p_addr, km);
+ if (i < PAGE_SIZE*8) {
+ i = bit_offset + i;
+ if (i >= b->bm_bits)
+ break;
+ goto found;
+ }
+ bm_fo = bit_offset + PAGE_SIZE*8;
+ }
+ i = -1UL;
+ }
+ found:
+ return i;
+}
+
+static unsigned long bm_find_next(struct drbd_conf *mdev,
+ unsigned long bm_fo, const int find_zero_bit)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long i = -1UL;
+
+ ERR_IF(!b) return i;
+ ERR_IF(!b->bm_pages) return i;
+
+ spin_lock_irq(&b->bm_lock);
+ if (bm_is_locked(b))
+ bm_print_lock_info(mdev);
+
+ i = __bm_find_next(mdev, bm_fo, find_zero_bit, KM_IRQ1);
+
+ spin_unlock_irq(&b->bm_lock);
+ return i;
+}
+
+unsigned long drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo)
+{
+ return bm_find_next(mdev, bm_fo, 0);
+}
+
+#if 0
+/* not yet needed for anything. */
+unsigned long drbd_bm_find_next_zero(struct drbd_conf *mdev, unsigned long bm_fo)
+{
+ return bm_find_next(mdev, bm_fo, 1);
+}
+#endif
+
+/* does not spin_lock_irqsave.
+ * you must take drbd_bm_lock() first */
+unsigned long _drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo)
+{
+ /* WARN_ON(!bm_is_locked(mdev)); */
+ return __bm_find_next(mdev, bm_fo, 0, KM_USER1);
+}
+
+unsigned long _drbd_bm_find_next_zero(struct drbd_conf *mdev, unsigned long bm_fo)
+{
+ /* WARN_ON(!bm_is_locked(mdev)); */
+ return __bm_find_next(mdev, bm_fo, 1, KM_USER1);
+}
+
+/* returns number of bits actually changed.
+ * for val != 0, we change 0 -> 1, return code positive
+ * for val == 0, we change 1 -> 0, return code negative
+ * wants bitnr, not sector.
+ * expected to be called for only a few bits (e - s about BITS_PER_LONG).
+ * Must hold bitmap lock already. */
+int __bm_change_bits_to(struct drbd_conf *mdev, const unsigned long s,
+ unsigned long e, int val, const enum km_type km)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long *p_addr = NULL;
+ unsigned long bitnr;
+ unsigned long last_page_nr = -1UL;
+ int c = 0;
+
+ if (e >= b->bm_bits) {
+ dev_err(DEV, "ASSERT FAILED: bit_s=%lu bit_e=%lu bm_bits=%lu\n",
+ s, e, b->bm_bits);
+ e = b->bm_bits ? b->bm_bits -1 : 0;
+ }
+ for (bitnr = s; bitnr <= e; bitnr++) {
+ unsigned long offset = bitnr>>LN2_BPL;
+ unsigned long page_nr = offset >> (PAGE_SHIFT - LN2_BPL + 3);
+ if (page_nr != last_page_nr) {
+ if (p_addr)
+ __bm_unmap(p_addr, km);
+ p_addr = __bm_map_paddr(b, offset, km);
+ last_page_nr = page_nr;
+ }
+ if (val)
+ c += (0 == __test_and_set_bit(bitnr & BPP_MASK, p_addr));
+ else
+ c -= (0 != __test_and_clear_bit(bitnr & BPP_MASK, p_addr));
+ }
+ if (p_addr)
+ __bm_unmap(p_addr, km);
+ b->bm_set += c;
+ return c;
+}
+
+/* returns number of bits actually changed.
+ * for val != 0, we change 0 -> 1, return code positive
+ * for val == 0, we change 1 -> 0, return code negative
+ * wants bitnr, not sector */
+int bm_change_bits_to(struct drbd_conf *mdev, const unsigned long s,
+ const unsigned long e, int val)
+{
+ unsigned long flags;
+ struct drbd_bitmap *b = mdev->bitmap;
+ int c = 0;
+
+ ERR_IF(!b) return 1;
+ ERR_IF(!b->bm_pages) return 0;
+
+ spin_lock_irqsave(&b->bm_lock, flags);
+ if (bm_is_locked(b))
+ bm_print_lock_info(mdev);
+
+ c = __bm_change_bits_to(mdev, s, e, val, KM_IRQ1);
+
+ spin_unlock_irqrestore(&b->bm_lock, flags);
+ return c;
+}
+
+/* returns number of bits changed 0 -> 1 */
+int drbd_bm_set_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e)
+{
+ return bm_change_bits_to(mdev, s, e, 1);
+}
+
+/* returns number of bits changed 1 -> 0 */
+int drbd_bm_clear_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e)
+{
+ return -bm_change_bits_to(mdev, s, e, 0);
+}
+
+/* sets all bits in full words,
+ * from first_word up to, but not including, last_word */
+static inline void bm_set_full_words_within_one_page(struct drbd_bitmap *b,
+ int page_nr, int first_word, int last_word)
+{
+ int i;
+ int bits;
+ unsigned long *paddr = kmap_atomic(b->bm_pages[page_nr], KM_USER0);
+ for (i = first_word; i < last_word; i++) {
+ bits = hweight_long(paddr[i]);
+ paddr[i] = ~0UL;
+ b->bm_set += BITS_PER_LONG - bits;
+ }
+ kunmap_atomic(paddr, KM_USER0);
+}
+
+/* Same thing as drbd_bm_set_bits, but without taking the spin_lock_irqsave.
+ * You must first drbd_bm_lock().
+ * Can be called to set the whole bitmap in one go.
+ * Sets bits from s to e _inclusive_. */
+void _drbd_bm_set_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e)
+{
+ /* First set_bit from the first bit (s)
+ * up to the next long boundary (sl),
+ * then assign full words up to the last long boundary (el),
+ * then set_bit up to and including the last bit (e).
+ *
+ * Do not use memset, because we must account for changes,
+ * so we need to loop over the words with hweight() anyways.
+ */
+ unsigned long sl = ALIGN(s,BITS_PER_LONG);
+ unsigned long el = (e+1) & ~((unsigned long)BITS_PER_LONG-1);
+ int first_page;
+ int last_page;
+ int page_nr;
+ int first_word;
+ int last_word;
+
+ if (e - s <= 3*BITS_PER_LONG) {
+ /* don't bother; el and sl may even be wrong. */
+ __bm_change_bits_to(mdev, s, e, 1, KM_USER0);
+ return;
+ }
+
+ /* difference is large enough that we can trust sl and el */
+
+ /* bits filling the current long */
+ if (sl)
+ __bm_change_bits_to(mdev, s, sl-1, 1, KM_USER0);
+
+ first_page = sl >> (3 + PAGE_SHIFT);
+ last_page = el >> (3 + PAGE_SHIFT);
+
+ /* MLPP: modulo longs per page */
+ /* LWPP: long words per page */
+ first_word = MLPP(sl >> LN2_BPL);
+ last_word = LWPP;
+
+ /* first and full pages, unless first page == last page */
+ for (page_nr = first_page; page_nr < last_page; page_nr++) {
+ bm_set_full_words_within_one_page(mdev->bitmap, page_nr, first_word, last_word);
+ cond_resched();
+ first_word = 0;
+ }
+
+ /* last page (respectively only page, for first page == last page) */
+ last_word = MLPP(el >> LN2_BPL);
+ bm_set_full_words_within_one_page(mdev->bitmap, last_page, first_word, last_word);
+
+ /* possibly trailing bits.
+ * example: (e & 63) == 63, el will be e+1.
+ * if that even was the very last bit,
+ * it would trigger an assert in __bm_change_bits_to()
+ */
+ if (el <= e)
+ __bm_change_bits_to(mdev, el, e, 1, KM_USER0);
+}
+
+/* returns bit state
+ * wants bitnr, NOT sector.
+ * inherently racy... area needs to be locked by means of {al,rs}_lru
+ * 1 ... bit set
+ * 0 ... bit not set
+ * -1 ... first out of bounds access, stop testing for bits!
+ */
+int drbd_bm_test_bit(struct drbd_conf *mdev, const unsigned long bitnr)
+{
+ unsigned long flags;
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long *p_addr;
+ int i;
+
+ ERR_IF(!b) return 0;
+ ERR_IF(!b->bm_pages) return 0;
+
+ spin_lock_irqsave(&b->bm_lock, flags);
+ if (bm_is_locked(b))
+ bm_print_lock_info(mdev);
+ if (bitnr < b->bm_bits) {
+ unsigned long offset = bitnr>>LN2_BPL;
+ p_addr = bm_map_paddr(b, offset);
+ i = test_bit(bitnr & BPP_MASK, p_addr) ? 1 : 0;
+ bm_unmap(p_addr);
+ } else if (bitnr == b->bm_bits) {
+ i = -1;
+ } else { /* (bitnr > b->bm_bits) */
+ dev_err(DEV, "bitnr=%lu > bm_bits=%lu\n", bitnr, b->bm_bits);
+ i = 0;
+ }
+
+ spin_unlock_irqrestore(&b->bm_lock, flags);
+ return i;
+}
+
+/* returns number of bits set in the range [s, e] */
+int drbd_bm_count_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e)
+{
+ unsigned long flags;
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long *p_addr = NULL, page_nr = -1;
+ unsigned long bitnr;
+ int c = 0;
+ size_t w;
+
+ /* If this is called without a bitmap, that is a bug. But just to be
+ * robust in case we screwed up elsewhere, in that case pretend there
+ * was one dirty bit in the requested area, so we won't try to do a
+ * local read there (no bitmap probably implies no disk) */
+ ERR_IF(!b) return 1;
+ ERR_IF(!b->bm_pages) return 1;
+
+ spin_lock_irqsave(&b->bm_lock, flags);
+ if (bm_is_locked(b))
+ bm_print_lock_info(mdev);
+ for (bitnr = s; bitnr <= e; bitnr++) {
+ w = bitnr >> LN2_BPL;
+ if (page_nr != w >> (PAGE_SHIFT - LN2_BPL + 3)) {
+ page_nr = w >> (PAGE_SHIFT - LN2_BPL + 3);
+ if (p_addr)
+ bm_unmap(p_addr);
+ p_addr = bm_map_paddr(b, w);
+ }
+ ERR_IF (bitnr >= b->bm_bits) {
+ dev_err(DEV, "bitnr=%lu bm_bits=%lu\n", bitnr, b->bm_bits);
+ } else {
+ c += (0 != test_bit(bitnr - (page_nr << (PAGE_SHIFT+3)), p_addr));
+ }
+ }
+ if (p_addr)
+ bm_unmap(p_addr);
+ spin_unlock_irqrestore(&b->bm_lock, flags);
+ return c;
+}
+
+
+/* inherently racy...
+ * return value may be already out-of-date when this function returns.
+ * but the general usage is that this is only use during a cstate when bits are
+ * only cleared, not set, and typically only care for the case when the return
+ * value is zero, or we already "locked" this "bitmap extent" by other means.
+ *
+ * enr is bm-extent number, since we chose to name one sector (512 bytes)
+ * worth of the bitmap a "bitmap extent".
+ *
+ * TODO
+ * I think since we use it like a reference count, we should use the real
+ * reference count of some bitmap extent element from some lru instead...
+ *
+ */
+int drbd_bm_e_weight(struct drbd_conf *mdev, unsigned long enr)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ int count, s, e;
+ unsigned long flags;
+ unsigned long *p_addr, *bm;
+
+ ERR_IF(!b) return 0;
+ ERR_IF(!b->bm_pages) return 0;
+
+ spin_lock_irqsave(&b->bm_lock, flags);
+ if (bm_is_locked(b))
+ bm_print_lock_info(mdev);
+
+ s = S2W(enr);
+ e = min((size_t)S2W(enr+1), b->bm_words);
+ count = 0;
+ if (s < b->bm_words) {
+ int n = e-s;
+ p_addr = bm_map_paddr(b, s);
+ bm = p_addr + MLPP(s);
+ while (n--)
+ count += hweight_long(*bm++);
+ bm_unmap(p_addr);
+ } else {
+ dev_err(DEV, "start offset (%d) too large in drbd_bm_e_weight\n", s);
+ }
+ spin_unlock_irqrestore(&b->bm_lock, flags);
+ return count;
+}
+
+/* set all bits covered by the AL-extent al_enr */
+unsigned long drbd_bm_ALe_set_all(struct drbd_conf *mdev, unsigned long al_enr)
+{
+ struct drbd_bitmap *b = mdev->bitmap;
+ unsigned long *p_addr, *bm;
+ unsigned long weight;
+ int count, s, e, i, do_now;
+ ERR_IF(!b) return 0;
+ ERR_IF(!b->bm_pages) return 0;
+
+ spin_lock_irq(&b->bm_lock);
+ if (bm_is_locked(b))
+ bm_print_lock_info(mdev);
+ weight = b->bm_set;
+
+ s = al_enr * BM_WORDS_PER_AL_EXT;
+ e = min_t(size_t, s + BM_WORDS_PER_AL_EXT, b->bm_words);
+ /* assert that s and e are on the same page */
+ D_ASSERT((e-1) >> (PAGE_SHIFT - LN2_BPL + 3)
+ == s >> (PAGE_SHIFT - LN2_BPL + 3));
+ count = 0;
+ if (s < b->bm_words) {
+ i = do_now = e-s;
+ p_addr = bm_map_paddr(b, s);
+ bm = p_addr + MLPP(s);
+ while (i--) {
+ count += hweight_long(*bm);
+ *bm = -1UL;
+ bm++;
+ }
+ bm_unmap(p_addr);
+ b->bm_set += do_now*BITS_PER_LONG - count;
+ if (e == b->bm_words)
+ b->bm_set -= bm_clear_surplus(b);
+ } else {
+ dev_err(DEV, "start offset (%d) too large in drbd_bm_ALe_set_all\n", s);
+ }
+ weight = b->bm_set - weight;
+ spin_unlock_irq(&b->bm_lock);
+ return weight;
+}
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
new file mode 100644
index 00000000000..2312d782fe9
--- /dev/null
+++ b/drivers/block/drbd/drbd_int.h
@@ -0,0 +1,2252 @@
+/*
+ drbd_int.h
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _DRBD_INT_H
+#define _DRBD_INT_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/crypto.h>
+#include <linux/ratelimit.h>
+#include <linux/tcp.h>
+#include <linux/mutex.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <net/tcp.h>
+#include <linux/lru_cache.h>
+
+#ifdef __CHECKER__
+# define __protected_by(x) __attribute__((require_context(x,1,999,"rdwr")))
+# define __protected_read_by(x) __attribute__((require_context(x,1,999,"read")))
+# define __protected_write_by(x) __attribute__((require_context(x,1,999,"write")))
+# define __must_hold(x) __attribute__((context(x,1,1), require_context(x,1,999,"call")))
+#else
+# define __protected_by(x)
+# define __protected_read_by(x)
+# define __protected_write_by(x)
+# define __must_hold(x)
+#endif
+
+#define __no_warn(lock, stmt) do { __acquire(lock); stmt; __release(lock); } while (0)
+
+/* module parameter, defined in drbd_main.c */
+extern unsigned int minor_count;
+extern int disable_sendpage;
+extern int allow_oos;
+extern unsigned int cn_idx;
+
+#ifdef CONFIG_DRBD_FAULT_INJECTION
+extern int enable_faults;
+extern int fault_rate;
+extern int fault_devs;
+#endif
+
+extern char usermode_helper[];
+
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* I don't remember why XCPU ...
+ * This is used to wake the asender,
+ * and to interrupt sending the sending task
+ * on disconnect.
+ */
+#define DRBD_SIG SIGXCPU
+
+/* This is used to stop/restart our threads.
+ * Cannot use SIGTERM nor SIGKILL, since these
+ * are sent out by init on runlevel changes
+ * I choose SIGHUP for now.
+ */
+#define DRBD_SIGKILL SIGHUP
+
+/* All EEs on the free list should have ID_VACANT (== 0)
+ * freshly allocated EEs get !ID_VACANT (== 1)
+ * so if it says "cannot dereference null pointer at adress 0x00000001",
+ * it is most likely one of these :( */
+
+#define ID_IN_SYNC (4711ULL)
+#define ID_OUT_OF_SYNC (4712ULL)
+
+#define ID_SYNCER (-1ULL)
+#define ID_VACANT 0
+#define is_syncer_block_id(id) ((id) == ID_SYNCER)
+
+struct drbd_conf;
+
+
+/* to shorten dev_warn(DEV, "msg"); and relatives statements */
+#define DEV (disk_to_dev(mdev->vdisk))
+
+#define D_ASSERT(exp) if (!(exp)) \
+ dev_err(DEV, "ASSERT( " #exp " ) in %s:%d\n", __FILE__, __LINE__)
+
+#define ERR_IF(exp) if (({ \
+ int _b = (exp) != 0; \
+ if (_b) dev_err(DEV, "%s: (%s) in %s:%d\n", \
+ __func__, #exp, __FILE__, __LINE__); \
+ _b; \
+ }))
+
+/* Defines to control fault insertion */
+enum {
+ DRBD_FAULT_MD_WR = 0, /* meta data write */
+ DRBD_FAULT_MD_RD = 1, /* read */
+ DRBD_FAULT_RS_WR = 2, /* resync */
+ DRBD_FAULT_RS_RD = 3,
+ DRBD_FAULT_DT_WR = 4, /* data */
+ DRBD_FAULT_DT_RD = 5,
+ DRBD_FAULT_DT_RA = 6, /* data read ahead */
+ DRBD_FAULT_BM_ALLOC = 7, /* bitmap allocation */
+ DRBD_FAULT_AL_EE = 8, /* alloc ee */
+
+ DRBD_FAULT_MAX,
+};
+
+#ifdef CONFIG_DRBD_FAULT_INJECTION
+extern unsigned int
+_drbd_insert_fault(struct drbd_conf *mdev, unsigned int type);
+static inline int
+drbd_insert_fault(struct drbd_conf *mdev, unsigned int type) {
+ return fault_rate &&
+ (enable_faults & (1<<type)) &&
+ _drbd_insert_fault(mdev, type);
+}
+#define FAULT_ACTIVE(_m, _t) (drbd_insert_fault((_m), (_t)))
+
+#else
+#define FAULT_ACTIVE(_m, _t) (0)
+#endif
+
+/* integer division, round _UP_ to the next integer */
+#define div_ceil(A, B) ((A)/(B) + ((A)%(B) ? 1 : 0))
+/* usual integer division */
+#define div_floor(A, B) ((A)/(B))
+
+/* drbd_meta-data.c (still in drbd_main.c) */
+/* 4th incarnation of the disk layout. */
+#define DRBD_MD_MAGIC (DRBD_MAGIC+4)
+
+extern struct drbd_conf **minor_table;
+extern struct ratelimit_state drbd_ratelimit_state;
+
+/* on the wire */
+enum drbd_packets {
+ /* receiver (data socket) */
+ P_DATA = 0x00,
+ P_DATA_REPLY = 0x01, /* Response to P_DATA_REQUEST */
+ P_RS_DATA_REPLY = 0x02, /* Response to P_RS_DATA_REQUEST */
+ P_BARRIER = 0x03,
+ P_BITMAP = 0x04,
+ P_BECOME_SYNC_TARGET = 0x05,
+ P_BECOME_SYNC_SOURCE = 0x06,
+ P_UNPLUG_REMOTE = 0x07, /* Used at various times to hint the peer */
+ P_DATA_REQUEST = 0x08, /* Used to ask for a data block */
+ P_RS_DATA_REQUEST = 0x09, /* Used to ask for a data block for resync */
+ P_SYNC_PARAM = 0x0a,
+ P_PROTOCOL = 0x0b,
+ P_UUIDS = 0x0c,
+ P_SIZES = 0x0d,
+ P_STATE = 0x0e,
+ P_SYNC_UUID = 0x0f,
+ P_AUTH_CHALLENGE = 0x10,
+ P_AUTH_RESPONSE = 0x11,
+ P_STATE_CHG_REQ = 0x12,
+
+ /* asender (meta socket */
+ P_PING = 0x13,
+ P_PING_ACK = 0x14,
+ P_RECV_ACK = 0x15, /* Used in protocol B */
+ P_WRITE_ACK = 0x16, /* Used in protocol C */
+ P_RS_WRITE_ACK = 0x17, /* Is a P_WRITE_ACK, additionally call set_in_sync(). */
+ P_DISCARD_ACK = 0x18, /* Used in proto C, two-primaries conflict detection */
+ P_NEG_ACK = 0x19, /* Sent if local disk is unusable */
+ P_NEG_DREPLY = 0x1a, /* Local disk is broken... */
+ P_NEG_RS_DREPLY = 0x1b, /* Local disk is broken... */
+ P_BARRIER_ACK = 0x1c,
+ P_STATE_CHG_REPLY = 0x1d,
+
+ /* "new" commands, no longer fitting into the ordering scheme above */
+
+ P_OV_REQUEST = 0x1e, /* data socket */
+ P_OV_REPLY = 0x1f,
+ P_OV_RESULT = 0x20, /* meta socket */
+ P_CSUM_RS_REQUEST = 0x21, /* data socket */
+ P_RS_IS_IN_SYNC = 0x22, /* meta socket */
+ P_SYNC_PARAM89 = 0x23, /* data socket, protocol version 89 replacement for P_SYNC_PARAM */
+ P_COMPRESSED_BITMAP = 0x24, /* compressed or otherwise encoded bitmap transfer */
+
+ P_MAX_CMD = 0x25,
+ P_MAY_IGNORE = 0x100, /* Flag to test if (cmd > P_MAY_IGNORE) ... */
+ P_MAX_OPT_CMD = 0x101,
+
+ /* special command ids for handshake */
+
+ P_HAND_SHAKE_M = 0xfff1, /* First Packet on the MetaSock */
+ P_HAND_SHAKE_S = 0xfff2, /* First Packet on the Socket */
+
+ P_HAND_SHAKE = 0xfffe /* FIXED for the next century! */
+};
+
+static inline const char *cmdname(enum drbd_packets cmd)
+{
+ /* THINK may need to become several global tables
+ * when we want to support more than
+ * one PRO_VERSION */
+ static const char *cmdnames[] = {
+ [P_DATA] = "Data",
+ [P_DATA_REPLY] = "DataReply",
+ [P_RS_DATA_REPLY] = "RSDataReply",
+ [P_BARRIER] = "Barrier",
+ [P_BITMAP] = "ReportBitMap",
+ [P_BECOME_SYNC_TARGET] = "BecomeSyncTarget",
+ [P_BECOME_SYNC_SOURCE] = "BecomeSyncSource",
+ [P_UNPLUG_REMOTE] = "UnplugRemote",
+ [P_DATA_REQUEST] = "DataRequest",
+ [P_RS_DATA_REQUEST] = "RSDataRequest",
+ [P_SYNC_PARAM] = "SyncParam",
+ [P_SYNC_PARAM89] = "SyncParam89",
+ [P_PROTOCOL] = "ReportProtocol",
+ [P_UUIDS] = "ReportUUIDs",
+ [P_SIZES] = "ReportSizes",
+ [P_STATE] = "ReportState",
+ [P_SYNC_UUID] = "ReportSyncUUID",
+ [P_AUTH_CHALLENGE] = "AuthChallenge",
+ [P_AUTH_RESPONSE] = "AuthResponse",
+ [P_PING] = "Ping",
+ [P_PING_ACK] = "PingAck",
+ [P_RECV_ACK] = "RecvAck",
+ [P_WRITE_ACK] = "WriteAck",
+ [P_RS_WRITE_ACK] = "RSWriteAck",
+ [P_DISCARD_ACK] = "DiscardAck",
+ [P_NEG_ACK] = "NegAck",
+ [P_NEG_DREPLY] = "NegDReply",
+ [P_NEG_RS_DREPLY] = "NegRSDReply",
+ [P_BARRIER_ACK] = "BarrierAck",
+ [P_STATE_CHG_REQ] = "StateChgRequest",
+ [P_STATE_CHG_REPLY] = "StateChgReply",
+ [P_OV_REQUEST] = "OVRequest",
+ [P_OV_REPLY] = "OVReply",
+ [P_OV_RESULT] = "OVResult",
+ [P_MAX_CMD] = NULL,
+ };
+
+ if (cmd == P_HAND_SHAKE_M)
+ return "HandShakeM";
+ if (cmd == P_HAND_SHAKE_S)
+ return "HandShakeS";
+ if (cmd == P_HAND_SHAKE)
+ return "HandShake";
+ if (cmd >= P_MAX_CMD)
+ return "Unknown";
+ return cmdnames[cmd];
+}
+
+/* for sending/receiving the bitmap,
+ * possibly in some encoding scheme */
+struct bm_xfer_ctx {
+ /* "const"
+ * stores total bits and long words
+ * of the bitmap, so we don't need to
+ * call the accessor functions over and again. */
+ unsigned long bm_bits;
+ unsigned long bm_words;
+ /* during xfer, current position within the bitmap */
+ unsigned long bit_offset;
+ unsigned long word_offset;
+
+ /* statistics; index: (h->command == P_BITMAP) */
+ unsigned packets[2];
+ unsigned bytes[2];
+};
+
+extern void INFO_bm_xfer_stats(struct drbd_conf *mdev,
+ const char *direction, struct bm_xfer_ctx *c);
+
+static inline void bm_xfer_ctx_bit_to_word_offset(struct bm_xfer_ctx *c)
+{
+ /* word_offset counts "native long words" (32 or 64 bit),
+ * aligned at 64 bit.
+ * Encoded packet may end at an unaligned bit offset.
+ * In case a fallback clear text packet is transmitted in
+ * between, we adjust this offset back to the last 64bit
+ * aligned "native long word", which makes coding and decoding
+ * the plain text bitmap much more convenient. */
+#if BITS_PER_LONG == 64
+ c->word_offset = c->bit_offset >> 6;
+#elif BITS_PER_LONG == 32
+ c->word_offset = c->bit_offset >> 5;
+ c->word_offset &= ~(1UL);
+#else
+# error "unsupported BITS_PER_LONG"
+#endif
+}
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+/* This is the layout for a packet on the wire.
+ * The byteorder is the network byte order.
+ * (except block_id and barrier fields.
+ * these are pointers to local structs
+ * and have no relevance for the partner,
+ * which just echoes them as received.)
+ *
+ * NOTE that the payload starts at a long aligned offset,
+ * regardless of 32 or 64 bit arch!
+ */
+struct p_header {
+ u32 magic;
+ u16 command;
+ u16 length; /* bytes of data after this header */
+ u8 payload[0];
+} __packed;
+/* 8 bytes. packet FIXED for the next century! */
+
+/*
+ * short commands, packets without payload, plain p_header:
+ * P_PING
+ * P_PING_ACK
+ * P_BECOME_SYNC_TARGET
+ * P_BECOME_SYNC_SOURCE
+ * P_UNPLUG_REMOTE
+ */
+
+/*
+ * commands with out-of-struct payload:
+ * P_BITMAP (no additional fields)
+ * P_DATA, P_DATA_REPLY (see p_data)
+ * P_COMPRESSED_BITMAP (see receive_compressed_bitmap)
+ */
+
+/* these defines must not be changed without changing the protocol version */
+#define DP_HARDBARRIER 1
+#define DP_RW_SYNC 2
+#define DP_MAY_SET_IN_SYNC 4
+
+struct p_data {
+ struct p_header head;
+ u64 sector; /* 64 bits sector number */
+ u64 block_id; /* to identify the request in protocol B&C */
+ u32 seq_num;
+ u32 dp_flags;
+} __packed;
+
+/*
+ * commands which share a struct:
+ * p_block_ack:
+ * P_RECV_ACK (proto B), P_WRITE_ACK (proto C),
+ * P_DISCARD_ACK (proto C, two-primaries conflict detection)
+ * p_block_req:
+ * P_DATA_REQUEST, P_RS_DATA_REQUEST
+ */
+struct p_block_ack {
+ struct p_header head;
+ u64 sector;
+ u64 block_id;
+ u32 blksize;
+ u32 seq_num;
+} __packed;
+
+
+struct p_block_req {
+ struct p_header head;
+ u64 sector;
+ u64 block_id;
+ u32 blksize;
+ u32 pad; /* to multiple of 8 Byte */
+} __packed;
+
+/*
+ * commands with their own struct for additional fields:
+ * P_HAND_SHAKE
+ * P_BARRIER
+ * P_BARRIER_ACK
+ * P_SYNC_PARAM
+ * ReportParams
+ */
+
+struct p_handshake {
+ struct p_header head; /* 8 bytes */
+ u32 protocol_min;
+ u32 feature_flags;
+ u32 protocol_max;
+
+ /* should be more than enough for future enhancements
+ * for now, feature_flags and the reserverd array shall be zero.
+ */
+
+ u32 _pad;
+ u64 reserverd[7];
+} __packed;
+/* 80 bytes, FIXED for the next century */
+
+struct p_barrier {
+ struct p_header head;
+ u32 barrier; /* barrier number _handle_ only */
+ u32 pad; /* to multiple of 8 Byte */
+} __packed;
+
+struct p_barrier_ack {
+ struct p_header head;
+ u32 barrier;
+ u32 set_size;
+} __packed;
+
+struct p_rs_param {
+ struct p_header head;
+ u32 rate;
+
+ /* Since protocol version 88 and higher. */
+ char verify_alg[0];
+} __packed;
+
+struct p_rs_param_89 {
+ struct p_header head;
+ u32 rate;
+ /* protocol version 89: */
+ char verify_alg[SHARED_SECRET_MAX];
+ char csums_alg[SHARED_SECRET_MAX];
+} __packed;
+
+struct p_protocol {
+ struct p_header head;
+ u32 protocol;
+ u32 after_sb_0p;
+ u32 after_sb_1p;
+ u32 after_sb_2p;
+ u32 want_lose;
+ u32 two_primaries;
+
+ /* Since protocol version 87 and higher. */
+ char integrity_alg[0];
+
+} __packed;
+
+struct p_uuids {
+ struct p_header head;
+ u64 uuid[UI_EXTENDED_SIZE];
+} __packed;
+
+struct p_rs_uuid {
+ struct p_header head;
+ u64 uuid;
+} __packed;
+
+struct p_sizes {
+ struct p_header head;
+ u64 d_size; /* size of disk */
+ u64 u_size; /* user requested size */
+ u64 c_size; /* current exported size */
+ u32 max_segment_size; /* Maximal size of a BIO */
+ u32 queue_order_type;
+} __packed;
+
+struct p_state {
+ struct p_header head;
+ u32 state;
+} __packed;
+
+struct p_req_state {
+ struct p_header head;
+ u32 mask;
+ u32 val;
+} __packed;
+
+struct p_req_state_reply {
+ struct p_header head;
+ u32 retcode;
+} __packed;
+
+struct p_drbd06_param {
+ u64 size;
+ u32 state;
+ u32 blksize;
+ u32 protocol;
+ u32 version;
+ u32 gen_cnt[5];
+ u32 bit_map_gen[5];
+} __packed;
+
+struct p_discard {
+ struct p_header head;
+ u64 block_id;
+ u32 seq_num;
+ u32 pad;
+} __packed;
+
+/* Valid values for the encoding field.
+ * Bump proto version when changing this. */
+enum drbd_bitmap_code {
+ /* RLE_VLI_Bytes = 0,
+ * and other bit variants had been defined during
+ * algorithm evaluation. */
+ RLE_VLI_Bits = 2,
+};
+
+struct p_compressed_bm {
+ struct p_header head;
+ /* (encoding & 0x0f): actual encoding, see enum drbd_bitmap_code
+ * (encoding & 0x80): polarity (set/unset) of first runlength
+ * ((encoding >> 4) & 0x07): pad_bits, number of trailing zero bits
+ * used to pad up to head.length bytes
+ */
+ u8 encoding;
+
+ u8 code[0];
+} __packed;
+
+/* DCBP: Drbd Compressed Bitmap Packet ... */
+static inline enum drbd_bitmap_code
+DCBP_get_code(struct p_compressed_bm *p)
+{
+ return (enum drbd_bitmap_code)(p->encoding & 0x0f);
+}
+
+static inline void
+DCBP_set_code(struct p_compressed_bm *p, enum drbd_bitmap_code code)
+{
+ BUG_ON(code & ~0xf);
+ p->encoding = (p->encoding & ~0xf) | code;
+}
+
+static inline int
+DCBP_get_start(struct p_compressed_bm *p)
+{
+ return (p->encoding & 0x80) != 0;
+}
+
+static inline void
+DCBP_set_start(struct p_compressed_bm *p, int set)
+{
+ p->encoding = (p->encoding & ~0x80) | (set ? 0x80 : 0);
+}
+
+static inline int
+DCBP_get_pad_bits(struct p_compressed_bm *p)
+{
+ return (p->encoding >> 4) & 0x7;
+}
+
+static inline void
+DCBP_set_pad_bits(struct p_compressed_bm *p, int n)
+{
+ BUG_ON(n & ~0x7);
+ p->encoding = (p->encoding & (~0x7 << 4)) | (n << 4);
+}
+
+/* one bitmap packet, including the p_header,
+ * should fit within one _architecture independend_ page.
+ * so we need to use the fixed size 4KiB page size
+ * most architechtures have used for a long time.
+ */
+#define BM_PACKET_PAYLOAD_BYTES (4096 - sizeof(struct p_header))
+#define BM_PACKET_WORDS (BM_PACKET_PAYLOAD_BYTES/sizeof(long))
+#define BM_PACKET_VLI_BYTES_MAX (4096 - sizeof(struct p_compressed_bm))
+#if (PAGE_SIZE < 4096)
+/* drbd_send_bitmap / receive_bitmap would break horribly */
+#error "PAGE_SIZE too small"
+#endif
+
+union p_polymorph {
+ struct p_header header;
+ struct p_handshake handshake;
+ struct p_data data;
+ struct p_block_ack block_ack;
+ struct p_barrier barrier;
+ struct p_barrier_ack barrier_ack;
+ struct p_rs_param_89 rs_param_89;
+ struct p_protocol protocol;
+ struct p_sizes sizes;
+ struct p_uuids uuids;
+ struct p_state state;
+ struct p_req_state req_state;
+ struct p_req_state_reply req_state_reply;
+ struct p_block_req block_req;
+} __packed;
+
+/**********************************************************************/
+enum drbd_thread_state {
+ None,
+ Running,
+ Exiting,
+ Restarting
+};
+
+struct drbd_thread {
+ spinlock_t t_lock;
+ struct task_struct *task;
+ struct completion stop;
+ enum drbd_thread_state t_state;
+ int (*function) (struct drbd_thread *);
+ struct drbd_conf *mdev;
+ int reset_cpu_mask;
+};
+
+static inline enum drbd_thread_state get_t_state(struct drbd_thread *thi)
+{
+ /* THINK testing the t_state seems to be uncritical in all cases
+ * (but thread_{start,stop}), so we can read it *without* the lock.
+ * --lge */
+
+ smp_rmb();
+ return thi->t_state;
+}
+
+
+/*
+ * Having this as the first member of a struct provides sort of "inheritance".
+ * "derived" structs can be "drbd_queue_work()"ed.
+ * The callback should know and cast back to the descendant struct.
+ * drbd_request and drbd_epoch_entry are descendants of drbd_work.
+ */
+struct drbd_work;
+typedef int (*drbd_work_cb)(struct drbd_conf *, struct drbd_work *, int cancel);
+struct drbd_work {
+ struct list_head list;
+ drbd_work_cb cb;
+};
+
+struct drbd_tl_epoch;
+struct drbd_request {
+ struct drbd_work w;
+ struct drbd_conf *mdev;
+
+ /* if local IO is not allowed, will be NULL.
+ * if local IO _is_ allowed, holds the locally submitted bio clone,
+ * or, after local IO completion, the ERR_PTR(error).
+ * see drbd_endio_pri(). */
+ struct bio *private_bio;
+
+ struct hlist_node colision;
+ sector_t sector;
+ unsigned int size;
+ unsigned int epoch; /* barrier_nr */
+
+ /* barrier_nr: used to check on "completion" whether this req was in
+ * the current epoch, and we therefore have to close it,
+ * starting a new epoch...
+ */
+
+ /* up to here, the struct layout is identical to drbd_epoch_entry;
+ * we might be able to use that to our advantage... */
+
+ struct list_head tl_requests; /* ring list in the transfer log */
+ struct bio *master_bio; /* master bio pointer */
+ unsigned long rq_state; /* see comments above _req_mod() */
+ int seq_num;
+ unsigned long start_time;
+};
+
+struct drbd_tl_epoch {
+ struct drbd_work w;
+ struct list_head requests; /* requests before */
+ struct drbd_tl_epoch *next; /* pointer to the next barrier */
+ unsigned int br_number; /* the barriers identifier. */
+ int n_req; /* number of requests attached before this barrier */
+};
+
+struct drbd_request;
+
+/* These Tl_epoch_entries may be in one of 6 lists:
+ active_ee .. data packet being written
+ sync_ee .. syncer block being written
+ done_ee .. block written, need to send P_WRITE_ACK
+ read_ee .. [RS]P_DATA_REQUEST being read
+*/
+
+struct drbd_epoch {
+ struct list_head list;
+ unsigned int barrier_nr;
+ atomic_t epoch_size; /* increased on every request added. */
+ atomic_t active; /* increased on every req. added, and dec on every finished. */
+ unsigned long flags;
+};
+
+/* drbd_epoch flag bits */
+enum {
+ DE_BARRIER_IN_NEXT_EPOCH_ISSUED,
+ DE_BARRIER_IN_NEXT_EPOCH_DONE,
+ DE_CONTAINS_A_BARRIER,
+ DE_HAVE_BARRIER_NUMBER,
+ DE_IS_FINISHING,
+};
+
+enum epoch_event {
+ EV_PUT,
+ EV_GOT_BARRIER_NR,
+ EV_BARRIER_DONE,
+ EV_BECAME_LAST,
+ EV_CLEANUP = 32, /* used as flag */
+};
+
+struct drbd_epoch_entry {
+ struct drbd_work w;
+ struct drbd_conf *mdev;
+ struct bio *private_bio;
+ struct hlist_node colision;
+ sector_t sector;
+ unsigned int size;
+ struct drbd_epoch *epoch;
+
+ /* up to here, the struct layout is identical to drbd_request;
+ * we might be able to use that to our advantage... */
+
+ unsigned int flags;
+ u64 block_id;
+};
+
+struct drbd_wq_barrier {
+ struct drbd_work w;
+ struct completion done;
+};
+
+struct digest_info {
+ int digest_size;
+ void *digest;
+};
+
+/* ee flag bits */
+enum {
+ __EE_CALL_AL_COMPLETE_IO,
+ __EE_CONFLICT_PENDING,
+ __EE_MAY_SET_IN_SYNC,
+ __EE_IS_BARRIER,
+};
+#define EE_CALL_AL_COMPLETE_IO (1<<__EE_CALL_AL_COMPLETE_IO)
+#define EE_CONFLICT_PENDING (1<<__EE_CONFLICT_PENDING)
+#define EE_MAY_SET_IN_SYNC (1<<__EE_MAY_SET_IN_SYNC)
+#define EE_IS_BARRIER (1<<__EE_IS_BARRIER)
+
+/* global flag bits */
+enum {
+ CREATE_BARRIER, /* next P_DATA is preceeded by a P_BARRIER */
+ SIGNAL_ASENDER, /* whether asender wants to be interrupted */
+ SEND_PING, /* whether asender should send a ping asap */
+
+ STOP_SYNC_TIMER, /* tell timer to cancel itself */
+ UNPLUG_QUEUED, /* only relevant with kernel 2.4 */
+ UNPLUG_REMOTE, /* sending a "UnplugRemote" could help */
+ MD_DIRTY, /* current uuids and flags not yet on disk */
+ DISCARD_CONCURRENT, /* Set on one node, cleared on the peer! */
+ USE_DEGR_WFC_T, /* degr-wfc-timeout instead of wfc-timeout. */
+ CLUSTER_ST_CHANGE, /* Cluster wide state change going on... */
+ CL_ST_CHG_SUCCESS,
+ CL_ST_CHG_FAIL,
+ CRASHED_PRIMARY, /* This node was a crashed primary.
+ * Gets cleared when the state.conn
+ * goes into C_CONNECTED state. */
+ WRITE_BM_AFTER_RESYNC, /* A kmalloc() during resync failed */
+ NO_BARRIER_SUPP, /* underlying block device doesn't implement barriers */
+ CONSIDER_RESYNC,
+
+ MD_NO_BARRIER, /* meta data device does not support barriers,
+ so don't even try */
+ SUSPEND_IO, /* suspend application io */
+ BITMAP_IO, /* suspend application io;
+ once no more io in flight, start bitmap io */
+ BITMAP_IO_QUEUED, /* Started bitmap IO */
+ RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */
+ NET_CONGESTED, /* The data socket is congested */
+
+ CONFIG_PENDING, /* serialization of (re)configuration requests.
+ * if set, also prevents the device from dying */
+ DEVICE_DYING, /* device became unconfigured,
+ * but worker thread is still handling the cleanup.
+ * reconfiguring (nl_disk_conf, nl_net_conf) is dissalowed,
+ * while this is set. */
+ RESIZE_PENDING, /* Size change detected locally, waiting for the response from
+ * the peer, if it changed there as well. */
+};
+
+struct drbd_bitmap; /* opaque for drbd_conf */
+
+/* TODO sort members for performance
+ * MAYBE group them further */
+
+/* THINK maybe we actually want to use the default "event/%s" worker threads
+ * or similar in linux 2.6, which uses per cpu data and threads.
+ *
+ * To be general, this might need a spin_lock member.
+ * For now, please use the mdev->req_lock to protect list_head,
+ * see drbd_queue_work below.
+ */
+struct drbd_work_queue {
+ struct list_head q;
+ struct semaphore s; /* producers up it, worker down()s it */
+ spinlock_t q_lock; /* to protect the list. */
+};
+
+struct drbd_socket {
+ struct drbd_work_queue work;
+ struct mutex mutex;
+ struct socket *socket;
+ /* this way we get our
+ * send/receive buffers off the stack */
+ union p_polymorph sbuf;
+ union p_polymorph rbuf;
+};
+
+struct drbd_md {
+ u64 md_offset; /* sector offset to 'super' block */
+
+ u64 la_size_sect; /* last agreed size, unit sectors */
+ u64 uuid[UI_SIZE];
+ u64 device_uuid;
+ u32 flags;
+ u32 md_size_sect;
+
+ s32 al_offset; /* signed relative sector offset to al area */
+ s32 bm_offset; /* signed relative sector offset to bitmap */
+
+ /* u32 al_nr_extents; important for restoring the AL
+ * is stored into sync_conf.al_extents, which in turn
+ * gets applied to act_log->nr_elements
+ */
+};
+
+/* for sync_conf and other types... */
+#define NL_PACKET(name, number, fields) struct name { fields };
+#define NL_INTEGER(pn,pr,member) int member;
+#define NL_INT64(pn,pr,member) __u64 member;
+#define NL_BIT(pn,pr,member) unsigned member:1;
+#define NL_STRING(pn,pr,member,len) unsigned char member[len]; int member ## _len;
+#include "linux/drbd_nl.h"
+
+struct drbd_backing_dev {
+ struct block_device *backing_bdev;
+ struct block_device *md_bdev;
+ struct file *lo_file;
+ struct file *md_file;
+ struct drbd_md md;
+ struct disk_conf dc; /* The user provided config... */
+ sector_t known_size; /* last known size of that backing device */
+};
+
+struct drbd_md_io {
+ struct drbd_conf *mdev;
+ struct completion event;
+ int error;
+};
+
+struct bm_io_work {
+ struct drbd_work w;
+ char *why;
+ int (*io_fn)(struct drbd_conf *mdev);
+ void (*done)(struct drbd_conf *mdev, int rv);
+};
+
+enum write_ordering_e {
+ WO_none,
+ WO_drain_io,
+ WO_bdev_flush,
+ WO_bio_barrier
+};
+
+struct drbd_conf {
+ /* things that are stored as / read from meta data on disk */
+ unsigned long flags;
+
+ /* configured by drbdsetup */
+ struct net_conf *net_conf; /* protected by get_net_conf() and put_net_conf() */
+ struct syncer_conf sync_conf;
+ struct drbd_backing_dev *ldev __protected_by(local);
+
+ sector_t p_size; /* partner's disk size */
+ struct request_queue *rq_queue;
+ struct block_device *this_bdev;
+ struct gendisk *vdisk;
+
+ struct drbd_socket data; /* data/barrier/cstate/parameter packets */
+ struct drbd_socket meta; /* ping/ack (metadata) packets */
+ int agreed_pro_version; /* actually used protocol version */
+ unsigned long last_received; /* in jiffies, either socket */
+ unsigned int ko_count;
+ struct drbd_work resync_work,
+ unplug_work,
+ md_sync_work;
+ struct timer_list resync_timer;
+ struct timer_list md_sync_timer;
+
+ /* Used after attach while negotiating new disk state. */
+ union drbd_state new_state_tmp;
+
+ union drbd_state state;
+ wait_queue_head_t misc_wait;
+ wait_queue_head_t state_wait; /* upon each state change. */
+ unsigned int send_cnt;
+ unsigned int recv_cnt;
+ unsigned int read_cnt;
+ unsigned int writ_cnt;
+ unsigned int al_writ_cnt;
+ unsigned int bm_writ_cnt;
+ atomic_t ap_bio_cnt; /* Requests we need to complete */
+ atomic_t ap_pending_cnt; /* AP data packets on the wire, ack expected */
+ atomic_t rs_pending_cnt; /* RS request/data packets on the wire */
+ atomic_t unacked_cnt; /* Need to send replys for */
+ atomic_t local_cnt; /* Waiting for local completion */
+ atomic_t net_cnt; /* Users of net_conf */
+ spinlock_t req_lock;
+ struct drbd_tl_epoch *unused_spare_tle; /* for pre-allocation */
+ struct drbd_tl_epoch *newest_tle;
+ struct drbd_tl_epoch *oldest_tle;
+ struct list_head out_of_sequence_requests;
+ struct hlist_head *tl_hash;
+ unsigned int tl_hash_s;
+
+ /* blocks to sync in this run [unit BM_BLOCK_SIZE] */
+ unsigned long rs_total;
+ /* number of sync IOs that failed in this run */
+ unsigned long rs_failed;
+ /* Syncer's start time [unit jiffies] */
+ unsigned long rs_start;
+ /* cumulated time in PausedSyncX state [unit jiffies] */
+ unsigned long rs_paused;
+ /* block not up-to-date at mark [unit BM_BLOCK_SIZE] */
+ unsigned long rs_mark_left;
+ /* marks's time [unit jiffies] */
+ unsigned long rs_mark_time;
+ /* skipped because csum was equeal [unit BM_BLOCK_SIZE] */
+ unsigned long rs_same_csum;
+
+ /* where does the admin want us to start? (sector) */
+ sector_t ov_start_sector;
+ /* where are we now? (sector) */
+ sector_t ov_position;
+ /* Start sector of out of sync range (to merge printk reporting). */
+ sector_t ov_last_oos_start;
+ /* size of out-of-sync range in sectors. */
+ sector_t ov_last_oos_size;
+ unsigned long ov_left; /* in bits */
+ struct crypto_hash *csums_tfm;
+ struct crypto_hash *verify_tfm;
+
+ struct drbd_thread receiver;
+ struct drbd_thread worker;
+ struct drbd_thread asender;
+ struct drbd_bitmap *bitmap;
+ unsigned long bm_resync_fo; /* bit offset for drbd_bm_find_next */
+
+ /* Used to track operations of resync... */
+ struct lru_cache *resync;
+ /* Number of locked elements in resync LRU */
+ unsigned int resync_locked;
+ /* resync extent number waiting for application requests */
+ unsigned int resync_wenr;
+
+ int open_cnt;
+ u64 *p_uuid;
+ struct drbd_epoch *current_epoch;
+ spinlock_t epoch_lock;
+ unsigned int epochs;
+ enum write_ordering_e write_ordering;
+ struct list_head active_ee; /* IO in progress */
+ struct list_head sync_ee; /* IO in progress */
+ struct list_head done_ee; /* send ack */
+ struct list_head read_ee; /* IO in progress */
+ struct list_head net_ee; /* zero-copy network send in progress */
+ struct hlist_head *ee_hash; /* is proteced by req_lock! */
+ unsigned int ee_hash_s;
+
+ /* this one is protected by ee_lock, single thread */
+ struct drbd_epoch_entry *last_write_w_barrier;
+
+ int next_barrier_nr;
+ struct hlist_head *app_reads_hash; /* is proteced by req_lock */
+ struct list_head resync_reads;
+ atomic_t pp_in_use;
+ wait_queue_head_t ee_wait;
+ struct page *md_io_page; /* one page buffer for md_io */
+ struct page *md_io_tmpp; /* for logical_block_size != 512 */
+ struct mutex md_io_mutex; /* protects the md_io_buffer */
+ spinlock_t al_lock;
+ wait_queue_head_t al_wait;
+ struct lru_cache *act_log; /* activity log */
+ unsigned int al_tr_number;
+ int al_tr_cycle;
+ int al_tr_pos; /* position of the next transaction in the journal */
+ struct crypto_hash *cram_hmac_tfm;
+ struct crypto_hash *integrity_w_tfm; /* to be used by the worker thread */
+ struct crypto_hash *integrity_r_tfm; /* to be used by the receiver thread */
+ void *int_dig_out;
+ void *int_dig_in;
+ void *int_dig_vv;
+ wait_queue_head_t seq_wait;
+ atomic_t packet_seq;
+ unsigned int peer_seq;
+ spinlock_t peer_seq_lock;
+ unsigned int minor;
+ unsigned long comm_bm_set; /* communicated number of set bits. */
+ cpumask_var_t cpu_mask;
+ struct bm_io_work bm_io_work;
+ u64 ed_uuid; /* UUID of the exposed data */
+ struct mutex state_mutex;
+ char congestion_reason; /* Why we where congested... */
+};
+
+static inline struct drbd_conf *minor_to_mdev(unsigned int minor)
+{
+ struct drbd_conf *mdev;
+
+ mdev = minor < minor_count ? minor_table[minor] : NULL;
+
+ return mdev;
+}
+
+static inline unsigned int mdev_to_minor(struct drbd_conf *mdev)
+{
+ return mdev->minor;
+}
+
+/* returns 1 if it was successfull,
+ * returns 0 if there was no data socket.
+ * so wherever you are going to use the data.socket, e.g. do
+ * if (!drbd_get_data_sock(mdev))
+ * return 0;
+ * CODE();
+ * drbd_put_data_sock(mdev);
+ */
+static inline int drbd_get_data_sock(struct drbd_conf *mdev)
+{
+ mutex_lock(&mdev->data.mutex);
+ /* drbd_disconnect() could have called drbd_free_sock()
+ * while we were waiting in down()... */
+ if (unlikely(mdev->data.socket == NULL)) {
+ mutex_unlock(&mdev->data.mutex);
+ return 0;
+ }
+ return 1;
+}
+
+static inline void drbd_put_data_sock(struct drbd_conf *mdev)
+{
+ mutex_unlock(&mdev->data.mutex);
+}
+
+/*
+ * function declarations
+ *************************/
+
+/* drbd_main.c */
+
+enum chg_state_flags {
+ CS_HARD = 1,
+ CS_VERBOSE = 2,
+ CS_WAIT_COMPLETE = 4,
+ CS_SERIALIZE = 8,
+ CS_ORDERED = CS_WAIT_COMPLETE + CS_SERIALIZE,
+};
+
+extern void drbd_init_set_defaults(struct drbd_conf *mdev);
+extern int drbd_change_state(struct drbd_conf *mdev, enum chg_state_flags f,
+ union drbd_state mask, union drbd_state val);
+extern void drbd_force_state(struct drbd_conf *, union drbd_state,
+ union drbd_state);
+extern int _drbd_request_state(struct drbd_conf *, union drbd_state,
+ union drbd_state, enum chg_state_flags);
+extern int __drbd_set_state(struct drbd_conf *, union drbd_state,
+ enum chg_state_flags, struct completion *done);
+extern void print_st_err(struct drbd_conf *, union drbd_state,
+ union drbd_state, int);
+extern int drbd_thread_start(struct drbd_thread *thi);
+extern void _drbd_thread_stop(struct drbd_thread *thi, int restart, int wait);
+#ifdef CONFIG_SMP
+extern void drbd_thread_current_set_cpu(struct drbd_conf *mdev);
+extern void drbd_calc_cpu_mask(struct drbd_conf *mdev);
+#else
+#define drbd_thread_current_set_cpu(A) ({})
+#define drbd_calc_cpu_mask(A) ({})
+#endif
+extern void drbd_free_resources(struct drbd_conf *mdev);
+extern void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr,
+ unsigned int set_size);
+extern void tl_clear(struct drbd_conf *mdev);
+extern void _tl_add_barrier(struct drbd_conf *, struct drbd_tl_epoch *);
+extern void drbd_free_sock(struct drbd_conf *mdev);
+extern int drbd_send(struct drbd_conf *mdev, struct socket *sock,
+ void *buf, size_t size, unsigned msg_flags);
+extern int drbd_send_protocol(struct drbd_conf *mdev);
+extern int drbd_send_uuids(struct drbd_conf *mdev);
+extern int drbd_send_uuids_skip_initial_sync(struct drbd_conf *mdev);
+extern int drbd_send_sync_uuid(struct drbd_conf *mdev, u64 val);
+extern int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply);
+extern int _drbd_send_state(struct drbd_conf *mdev);
+extern int drbd_send_state(struct drbd_conf *mdev);
+extern int _drbd_send_cmd(struct drbd_conf *mdev, struct socket *sock,
+ enum drbd_packets cmd, struct p_header *h,
+ size_t size, unsigned msg_flags);
+#define USE_DATA_SOCKET 1
+#define USE_META_SOCKET 0
+extern int drbd_send_cmd(struct drbd_conf *mdev, int use_data_socket,
+ enum drbd_packets cmd, struct p_header *h,
+ size_t size);
+extern int drbd_send_cmd2(struct drbd_conf *mdev, enum drbd_packets cmd,
+ char *data, size_t size);
+extern int drbd_send_sync_param(struct drbd_conf *mdev, struct syncer_conf *sc);
+extern int drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr,
+ u32 set_size);
+extern int drbd_send_ack(struct drbd_conf *mdev, enum drbd_packets cmd,
+ struct drbd_epoch_entry *e);
+extern int drbd_send_ack_rp(struct drbd_conf *mdev, enum drbd_packets cmd,
+ struct p_block_req *rp);
+extern int drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packets cmd,
+ struct p_data *dp);
+extern int drbd_send_ack_ex(struct drbd_conf *mdev, enum drbd_packets cmd,
+ sector_t sector, int blksize, u64 block_id);
+extern int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd,
+ struct drbd_epoch_entry *e);
+extern int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req);
+extern int _drbd_send_barrier(struct drbd_conf *mdev,
+ struct drbd_tl_epoch *barrier);
+extern int drbd_send_drequest(struct drbd_conf *mdev, int cmd,
+ sector_t sector, int size, u64 block_id);
+extern int drbd_send_drequest_csum(struct drbd_conf *mdev,
+ sector_t sector,int size,
+ void *digest, int digest_size,
+ enum drbd_packets cmd);
+extern int drbd_send_ov_request(struct drbd_conf *mdev,sector_t sector,int size);
+
+extern int drbd_send_bitmap(struct drbd_conf *mdev);
+extern int _drbd_send_bitmap(struct drbd_conf *mdev);
+extern int drbd_send_sr_reply(struct drbd_conf *mdev, int retcode);
+extern void drbd_free_bc(struct drbd_backing_dev *ldev);
+extern void drbd_mdev_cleanup(struct drbd_conf *mdev);
+
+/* drbd_meta-data.c (still in drbd_main.c) */
+extern void drbd_md_sync(struct drbd_conf *mdev);
+extern int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev);
+/* maybe define them below as inline? */
+extern void drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local);
+extern void _drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local);
+extern void drbd_uuid_new_current(struct drbd_conf *mdev) __must_hold(local);
+extern void _drbd_uuid_new_current(struct drbd_conf *mdev) __must_hold(local);
+extern void drbd_uuid_set_bm(struct drbd_conf *mdev, u64 val) __must_hold(local);
+extern void drbd_md_set_flag(struct drbd_conf *mdev, int flags) __must_hold(local);
+extern void drbd_md_clear_flag(struct drbd_conf *mdev, int flags)__must_hold(local);
+extern int drbd_md_test_flag(struct drbd_backing_dev *, int);
+extern void drbd_md_mark_dirty(struct drbd_conf *mdev);
+extern void drbd_queue_bitmap_io(struct drbd_conf *mdev,
+ int (*io_fn)(struct drbd_conf *),
+ void (*done)(struct drbd_conf *, int),
+ char *why);
+extern int drbd_bmio_set_n_write(struct drbd_conf *mdev);
+extern int drbd_bmio_clear_n_write(struct drbd_conf *mdev);
+extern int drbd_bitmap_io(struct drbd_conf *mdev, int (*io_fn)(struct drbd_conf *), char *why);
+
+
+/* Meta data layout
+ We reserve a 128MB Block (4k aligned)
+ * either at the end of the backing device
+ * or on a seperate meta data device. */
+
+#define MD_RESERVED_SECT (128LU << 11) /* 128 MB, unit sectors */
+/* The following numbers are sectors */
+#define MD_AL_OFFSET 8 /* 8 Sectors after start of meta area */
+#define MD_AL_MAX_SIZE 64 /* = 32 kb LOG ~ 3776 extents ~ 14 GB Storage */
+/* Allows up to about 3.8TB */
+#define MD_BM_OFFSET (MD_AL_OFFSET + MD_AL_MAX_SIZE)
+
+/* Since the smalles IO unit is usually 512 byte */
+#define MD_SECTOR_SHIFT 9
+#define MD_SECTOR_SIZE (1<<MD_SECTOR_SHIFT)
+
+/* activity log */
+#define AL_EXTENTS_PT ((MD_SECTOR_SIZE-12)/8-1) /* 61 ; Extents per 512B sector */
+#define AL_EXTENT_SHIFT 22 /* One extent represents 4M Storage */
+#define AL_EXTENT_SIZE (1<<AL_EXTENT_SHIFT)
+
+#if BITS_PER_LONG == 32
+#define LN2_BPL 5
+#define cpu_to_lel(A) cpu_to_le32(A)
+#define lel_to_cpu(A) le32_to_cpu(A)
+#elif BITS_PER_LONG == 64
+#define LN2_BPL 6
+#define cpu_to_lel(A) cpu_to_le64(A)
+#define lel_to_cpu(A) le64_to_cpu(A)
+#else
+#error "LN2 of BITS_PER_LONG unknown!"
+#endif
+
+/* resync bitmap */
+/* 16MB sized 'bitmap extent' to track syncer usage */
+struct bm_extent {
+ int rs_left; /* number of bits set (out of sync) in this extent. */
+ int rs_failed; /* number of failed resync requests in this extent. */
+ unsigned long flags;
+ struct lc_element lce;
+};
+
+#define BME_NO_WRITES 0 /* bm_extent.flags: no more requests on this one! */
+#define BME_LOCKED 1 /* bm_extent.flags: syncer active on this one. */
+
+/* drbd_bitmap.c */
+/*
+ * We need to store one bit for a block.
+ * Example: 1GB disk @ 4096 byte blocks ==> we need 32 KB bitmap.
+ * Bit 0 ==> local node thinks this block is binary identical on both nodes
+ * Bit 1 ==> local node thinks this block needs to be synced.
+ */
+
+#define BM_BLOCK_SHIFT 12 /* 4k per bit */
+#define BM_BLOCK_SIZE (1<<BM_BLOCK_SHIFT)
+/* (9+3) : 512 bytes @ 8 bits; representing 16M storage
+ * per sector of on disk bitmap */
+#define BM_EXT_SHIFT (BM_BLOCK_SHIFT + MD_SECTOR_SHIFT + 3) /* = 24 */
+#define BM_EXT_SIZE (1<<BM_EXT_SHIFT)
+
+#if (BM_EXT_SHIFT != 24) || (BM_BLOCK_SHIFT != 12)
+#error "HAVE YOU FIXED drbdmeta AS WELL??"
+#endif
+
+/* thus many _storage_ sectors are described by one bit */
+#define BM_SECT_TO_BIT(x) ((x)>>(BM_BLOCK_SHIFT-9))
+#define BM_BIT_TO_SECT(x) ((sector_t)(x)<<(BM_BLOCK_SHIFT-9))
+#define BM_SECT_PER_BIT BM_BIT_TO_SECT(1)
+
+/* bit to represented kilo byte conversion */
+#define Bit2KB(bits) ((bits)<<(BM_BLOCK_SHIFT-10))
+
+/* in which _bitmap_ extent (resp. sector) the bit for a certain
+ * _storage_ sector is located in */
+#define BM_SECT_TO_EXT(x) ((x)>>(BM_EXT_SHIFT-9))
+
+/* how much _storage_ sectors we have per bitmap sector */
+#define BM_EXT_TO_SECT(x) ((sector_t)(x) << (BM_EXT_SHIFT-9))
+#define BM_SECT_PER_EXT BM_EXT_TO_SECT(1)
+
+/* in one sector of the bitmap, we have this many activity_log extents. */
+#define AL_EXT_PER_BM_SECT (1 << (BM_EXT_SHIFT - AL_EXTENT_SHIFT))
+#define BM_WORDS_PER_AL_EXT (1 << (AL_EXTENT_SHIFT-BM_BLOCK_SHIFT-LN2_BPL))
+
+#define BM_BLOCKS_PER_BM_EXT_B (BM_EXT_SHIFT - BM_BLOCK_SHIFT)
+#define BM_BLOCKS_PER_BM_EXT_MASK ((1<<BM_BLOCKS_PER_BM_EXT_B) - 1)
+
+/* the extent in "PER_EXTENT" below is an activity log extent
+ * we need that many (long words/bytes) to store the bitmap
+ * of one AL_EXTENT_SIZE chunk of storage.
+ * we can store the bitmap for that many AL_EXTENTS within
+ * one sector of the _on_disk_ bitmap:
+ * bit 0 bit 37 bit 38 bit (512*8)-1
+ * ...|........|........|.. // ..|........|
+ * sect. 0 `296 `304 ^(512*8*8)-1
+ *
+#define BM_WORDS_PER_EXT ( (AL_EXT_SIZE/BM_BLOCK_SIZE) / BITS_PER_LONG )
+#define BM_BYTES_PER_EXT ( (AL_EXT_SIZE/BM_BLOCK_SIZE) / 8 ) // 128
+#define BM_EXT_PER_SECT ( 512 / BM_BYTES_PER_EXTENT ) // 4
+ */
+
+#define DRBD_MAX_SECTORS_32 (0xffffffffLU)
+#define DRBD_MAX_SECTORS_BM \
+ ((MD_RESERVED_SECT - MD_BM_OFFSET) * (1LL<<(BM_EXT_SHIFT-9)))
+#if DRBD_MAX_SECTORS_BM < DRBD_MAX_SECTORS_32
+#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_BM
+#define DRBD_MAX_SECTORS_FLEX DRBD_MAX_SECTORS_BM
+#elif !defined(CONFIG_LBD) && BITS_PER_LONG == 32
+#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_32
+#define DRBD_MAX_SECTORS_FLEX DRBD_MAX_SECTORS_32
+#else
+#define DRBD_MAX_SECTORS DRBD_MAX_SECTORS_BM
+/* 16 TB in units of sectors */
+#if BITS_PER_LONG == 32
+/* adjust by one page worth of bitmap,
+ * so we won't wrap around in drbd_bm_find_next_bit.
+ * you should use 64bit OS for that much storage, anyways. */
+#define DRBD_MAX_SECTORS_FLEX BM_BIT_TO_SECT(0xffff7fff)
+#else
+#define DRBD_MAX_SECTORS_FLEX BM_BIT_TO_SECT(0x1LU << 32)
+#endif
+#endif
+
+/* Sector shift value for the "hash" functions of tl_hash and ee_hash tables.
+ * With a value of 6 all IO in one 32K block make it to the same slot of the
+ * hash table. */
+#define HT_SHIFT 6
+#define DRBD_MAX_SEGMENT_SIZE (1U<<(9+HT_SHIFT))
+
+/* Number of elements in the app_reads_hash */
+#define APP_R_HSIZE 15
+
+extern int drbd_bm_init(struct drbd_conf *mdev);
+extern int drbd_bm_resize(struct drbd_conf *mdev, sector_t sectors);
+extern void drbd_bm_cleanup(struct drbd_conf *mdev);
+extern void drbd_bm_set_all(struct drbd_conf *mdev);
+extern void drbd_bm_clear_all(struct drbd_conf *mdev);
+extern int drbd_bm_set_bits(
+ struct drbd_conf *mdev, unsigned long s, unsigned long e);
+extern int drbd_bm_clear_bits(
+ struct drbd_conf *mdev, unsigned long s, unsigned long e);
+/* bm_set_bits variant for use while holding drbd_bm_lock */
+extern void _drbd_bm_set_bits(struct drbd_conf *mdev,
+ const unsigned long s, const unsigned long e);
+extern int drbd_bm_test_bit(struct drbd_conf *mdev, unsigned long bitnr);
+extern int drbd_bm_e_weight(struct drbd_conf *mdev, unsigned long enr);
+extern int drbd_bm_write_sect(struct drbd_conf *mdev, unsigned long enr) __must_hold(local);
+extern int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local);
+extern int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local);
+extern unsigned long drbd_bm_ALe_set_all(struct drbd_conf *mdev,
+ unsigned long al_enr);
+extern size_t drbd_bm_words(struct drbd_conf *mdev);
+extern unsigned long drbd_bm_bits(struct drbd_conf *mdev);
+extern sector_t drbd_bm_capacity(struct drbd_conf *mdev);
+extern unsigned long drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo);
+/* bm_find_next variants for use while you hold drbd_bm_lock() */
+extern unsigned long _drbd_bm_find_next(struct drbd_conf *mdev, unsigned long bm_fo);
+extern unsigned long _drbd_bm_find_next_zero(struct drbd_conf *mdev, unsigned long bm_fo);
+extern unsigned long drbd_bm_total_weight(struct drbd_conf *mdev);
+extern int drbd_bm_rs_done(struct drbd_conf *mdev);
+/* for receive_bitmap */
+extern void drbd_bm_merge_lel(struct drbd_conf *mdev, size_t offset,
+ size_t number, unsigned long *buffer);
+/* for _drbd_send_bitmap and drbd_bm_write_sect */
+extern void drbd_bm_get_lel(struct drbd_conf *mdev, size_t offset,
+ size_t number, unsigned long *buffer);
+
+extern void drbd_bm_lock(struct drbd_conf *mdev, char *why);
+extern void drbd_bm_unlock(struct drbd_conf *mdev);
+
+extern int drbd_bm_count_bits(struct drbd_conf *mdev, const unsigned long s, const unsigned long e);
+/* drbd_main.c */
+
+extern struct kmem_cache *drbd_request_cache;
+extern struct kmem_cache *drbd_ee_cache; /* epoch entries */
+extern struct kmem_cache *drbd_bm_ext_cache; /* bitmap extents */
+extern struct kmem_cache *drbd_al_ext_cache; /* activity log extents */
+extern mempool_t *drbd_request_mempool;
+extern mempool_t *drbd_ee_mempool;
+
+extern struct page *drbd_pp_pool; /* drbd's page pool */
+extern spinlock_t drbd_pp_lock;
+extern int drbd_pp_vacant;
+extern wait_queue_head_t drbd_pp_wait;
+
+extern rwlock_t global_state_lock;
+
+extern struct drbd_conf *drbd_new_device(unsigned int minor);
+extern void drbd_free_mdev(struct drbd_conf *mdev);
+
+extern int proc_details;
+
+/* drbd_req */
+extern int drbd_make_request_26(struct request_queue *q, struct bio *bio);
+extern int drbd_read_remote(struct drbd_conf *mdev, struct drbd_request *req);
+extern int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct bio_vec *bvec);
+extern int is_valid_ar_handle(struct drbd_request *, sector_t);
+
+
+/* drbd_nl.c */
+extern void drbd_suspend_io(struct drbd_conf *mdev);
+extern void drbd_resume_io(struct drbd_conf *mdev);
+extern char *ppsize(char *buf, unsigned long long size);
+extern sector_t drbd_new_dev_size(struct drbd_conf *,
+ struct drbd_backing_dev *);
+enum determine_dev_size { dev_size_error = -1, unchanged = 0, shrunk = 1, grew = 2 };
+extern enum determine_dev_size drbd_determin_dev_size(struct drbd_conf *) __must_hold(local);
+extern void resync_after_online_grow(struct drbd_conf *);
+extern void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int) __must_hold(local);
+extern int drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role,
+ int force);
+enum drbd_disk_state drbd_try_outdate_peer(struct drbd_conf *mdev);
+extern int drbd_khelper(struct drbd_conf *mdev, char *cmd);
+
+/* drbd_worker.c */
+extern int drbd_worker(struct drbd_thread *thi);
+extern int drbd_alter_sa(struct drbd_conf *mdev, int na);
+extern void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side);
+extern void resume_next_sg(struct drbd_conf *mdev);
+extern void suspend_other_sg(struct drbd_conf *mdev);
+extern int drbd_resync_finished(struct drbd_conf *mdev);
+/* maybe rather drbd_main.c ? */
+extern int drbd_md_sync_page_io(struct drbd_conf *mdev,
+ struct drbd_backing_dev *bdev, sector_t sector, int rw);
+extern void drbd_ov_oos_found(struct drbd_conf*, sector_t, int);
+
+static inline void ov_oos_print(struct drbd_conf *mdev)
+{
+ if (mdev->ov_last_oos_size) {
+ dev_err(DEV, "Out of sync: start=%llu, size=%lu (sectors)\n",
+ (unsigned long long)mdev->ov_last_oos_start,
+ (unsigned long)mdev->ov_last_oos_size);
+ }
+ mdev->ov_last_oos_size=0;
+}
+
+
+extern void drbd_csum(struct drbd_conf *, struct crypto_hash *, struct bio *, void *);
+/* worker callbacks */
+extern int w_req_cancel_conflict(struct drbd_conf *, struct drbd_work *, int);
+extern int w_read_retry_remote(struct drbd_conf *, struct drbd_work *, int);
+extern int w_e_end_data_req(struct drbd_conf *, struct drbd_work *, int);
+extern int w_e_end_rsdata_req(struct drbd_conf *, struct drbd_work *, int);
+extern int w_e_end_csum_rs_req(struct drbd_conf *, struct drbd_work *, int);
+extern int w_e_end_ov_reply(struct drbd_conf *, struct drbd_work *, int);
+extern int w_e_end_ov_req(struct drbd_conf *, struct drbd_work *, int);
+extern int w_ov_finished(struct drbd_conf *, struct drbd_work *, int);
+extern int w_resync_inactive(struct drbd_conf *, struct drbd_work *, int);
+extern int w_resume_next_sg(struct drbd_conf *, struct drbd_work *, int);
+extern int w_io_error(struct drbd_conf *, struct drbd_work *, int);
+extern int w_send_write_hint(struct drbd_conf *, struct drbd_work *, int);
+extern int w_make_resync_request(struct drbd_conf *, struct drbd_work *, int);
+extern int w_send_dblock(struct drbd_conf *, struct drbd_work *, int);
+extern int w_send_barrier(struct drbd_conf *, struct drbd_work *, int);
+extern int w_send_read_req(struct drbd_conf *, struct drbd_work *, int);
+extern int w_prev_work_done(struct drbd_conf *, struct drbd_work *, int);
+extern int w_e_reissue(struct drbd_conf *, struct drbd_work *, int);
+
+extern void resync_timer_fn(unsigned long data);
+
+/* drbd_receiver.c */
+extern int drbd_release_ee(struct drbd_conf *mdev, struct list_head *list);
+extern struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
+ u64 id,
+ sector_t sector,
+ unsigned int data_size,
+ gfp_t gfp_mask) __must_hold(local);
+extern void drbd_free_ee(struct drbd_conf *mdev, struct drbd_epoch_entry *e);
+extern void drbd_wait_ee_list_empty(struct drbd_conf *mdev,
+ struct list_head *head);
+extern void _drbd_wait_ee_list_empty(struct drbd_conf *mdev,
+ struct list_head *head);
+extern void drbd_set_recv_tcq(struct drbd_conf *mdev, int tcq_enabled);
+extern void _drbd_clear_done_ee(struct drbd_conf *mdev, struct list_head *to_be_freed);
+extern void drbd_flush_workqueue(struct drbd_conf *mdev);
+
+/* yes, there is kernel_setsockopt, but only since 2.6.18. we don't need to
+ * mess with get_fs/set_fs, we know we are KERNEL_DS always. */
+static inline int drbd_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int optlen)
+{
+ int err;
+ if (level == SOL_SOCKET)
+ err = sock_setsockopt(sock, level, optname, optval, optlen);
+ else
+ err = sock->ops->setsockopt(sock, level, optname, optval,
+ optlen);
+ return err;
+}
+
+static inline void drbd_tcp_cork(struct socket *sock)
+{
+ int __user val = 1;
+ (void) drbd_setsockopt(sock, SOL_TCP, TCP_CORK,
+ (char __user *)&val, sizeof(val));
+}
+
+static inline void drbd_tcp_uncork(struct socket *sock)
+{
+ int __user val = 0;
+ (void) drbd_setsockopt(sock, SOL_TCP, TCP_CORK,
+ (char __user *)&val, sizeof(val));
+}
+
+static inline void drbd_tcp_nodelay(struct socket *sock)
+{
+ int __user val = 1;
+ (void) drbd_setsockopt(sock, SOL_TCP, TCP_NODELAY,
+ (char __user *)&val, sizeof(val));
+}
+
+static inline void drbd_tcp_quickack(struct socket *sock)
+{
+ int __user val = 1;
+ (void) drbd_setsockopt(sock, SOL_TCP, TCP_QUICKACK,
+ (char __user *)&val, sizeof(val));
+}
+
+void drbd_bump_write_ordering(struct drbd_conf *mdev, enum write_ordering_e wo);
+
+/* drbd_proc.c */
+extern struct proc_dir_entry *drbd_proc;
+extern struct file_operations drbd_proc_fops;
+extern const char *drbd_conn_str(enum drbd_conns s);
+extern const char *drbd_role_str(enum drbd_role s);
+
+/* drbd_actlog.c */
+extern void drbd_al_begin_io(struct drbd_conf *mdev, sector_t sector);
+extern void drbd_al_complete_io(struct drbd_conf *mdev, sector_t sector);
+extern void drbd_rs_complete_io(struct drbd_conf *mdev, sector_t sector);
+extern int drbd_rs_begin_io(struct drbd_conf *mdev, sector_t sector);
+extern int drbd_try_rs_begin_io(struct drbd_conf *mdev, sector_t sector);
+extern void drbd_rs_cancel_all(struct drbd_conf *mdev);
+extern int drbd_rs_del_all(struct drbd_conf *mdev);
+extern void drbd_rs_failed_io(struct drbd_conf *mdev,
+ sector_t sector, int size);
+extern int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *);
+extern void __drbd_set_in_sync(struct drbd_conf *mdev, sector_t sector,
+ int size, const char *file, const unsigned int line);
+#define drbd_set_in_sync(mdev, sector, size) \
+ __drbd_set_in_sync(mdev, sector, size, __FILE__, __LINE__)
+extern void __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector,
+ int size, const char *file, const unsigned int line);
+#define drbd_set_out_of_sync(mdev, sector, size) \
+ __drbd_set_out_of_sync(mdev, sector, size, __FILE__, __LINE__)
+extern void drbd_al_apply_to_bm(struct drbd_conf *mdev);
+extern void drbd_al_to_on_disk_bm(struct drbd_conf *mdev);
+extern void drbd_al_shrink(struct drbd_conf *mdev);
+
+
+/* drbd_nl.c */
+
+void drbd_nl_cleanup(void);
+int __init drbd_nl_init(void);
+void drbd_bcast_state(struct drbd_conf *mdev, union drbd_state);
+void drbd_bcast_sync_progress(struct drbd_conf *mdev);
+void drbd_bcast_ee(struct drbd_conf *mdev,
+ const char *reason, const int dgs,
+ const char* seen_hash, const char* calc_hash,
+ const struct drbd_epoch_entry* e);
+
+
+/**
+ * DOC: DRBD State macros
+ *
+ * These macros are used to express state changes in easily readable form.
+ *
+ * The NS macros expand to a mask and a value, that can be bit ored onto the
+ * current state as soon as the spinlock (req_lock) was taken.
+ *
+ * The _NS macros are used for state functions that get called with the
+ * spinlock. These macros expand directly to the new state value.
+ *
+ * Besides the basic forms NS() and _NS() additional _?NS[23] are defined
+ * to express state changes that affect more than one aspect of the state.
+ *
+ * E.g. NS2(conn, C_CONNECTED, peer, R_SECONDARY)
+ * Means that the network connection was established and that the peer
+ * is in secondary role.
+ */
+#define role_MASK R_MASK
+#define peer_MASK R_MASK
+#define disk_MASK D_MASK
+#define pdsk_MASK D_MASK
+#define conn_MASK C_MASK
+#define susp_MASK 1
+#define user_isp_MASK 1
+#define aftr_isp_MASK 1
+
+#define NS(T, S) \
+ ({ union drbd_state mask; mask.i = 0; mask.T = T##_MASK; mask; }), \
+ ({ union drbd_state val; val.i = 0; val.T = (S); val; })
+#define NS2(T1, S1, T2, S2) \
+ ({ union drbd_state mask; mask.i = 0; mask.T1 = T1##_MASK; \
+ mask.T2 = T2##_MASK; mask; }), \
+ ({ union drbd_state val; val.i = 0; val.T1 = (S1); \
+ val.T2 = (S2); val; })
+#define NS3(T1, S1, T2, S2, T3, S3) \
+ ({ union drbd_state mask; mask.i = 0; mask.T1 = T1##_MASK; \
+ mask.T2 = T2##_MASK; mask.T3 = T3##_MASK; mask; }), \
+ ({ union drbd_state val; val.i = 0; val.T1 = (S1); \
+ val.T2 = (S2); val.T3 = (S3); val; })
+
+#define _NS(D, T, S) \
+ D, ({ union drbd_state __ns; __ns.i = D->state.i; __ns.T = (S); __ns; })
+#define _NS2(D, T1, S1, T2, S2) \
+ D, ({ union drbd_state __ns; __ns.i = D->state.i; __ns.T1 = (S1); \
+ __ns.T2 = (S2); __ns; })
+#define _NS3(D, T1, S1, T2, S2, T3, S3) \
+ D, ({ union drbd_state __ns; __ns.i = D->state.i; __ns.T1 = (S1); \
+ __ns.T2 = (S2); __ns.T3 = (S3); __ns; })
+
+/*
+ * inline helper functions
+ *************************/
+
+static inline void drbd_state_lock(struct drbd_conf *mdev)
+{
+ wait_event(mdev->misc_wait,
+ !test_and_set_bit(CLUSTER_ST_CHANGE, &mdev->flags));
+}
+
+static inline void drbd_state_unlock(struct drbd_conf *mdev)
+{
+ clear_bit(CLUSTER_ST_CHANGE, &mdev->flags);
+ wake_up(&mdev->misc_wait);
+}
+
+static inline int _drbd_set_state(struct drbd_conf *mdev,
+ union drbd_state ns, enum chg_state_flags flags,
+ struct completion *done)
+{
+ int rv;
+
+ read_lock(&global_state_lock);
+ rv = __drbd_set_state(mdev, ns, flags, done);
+ read_unlock(&global_state_lock);
+
+ return rv;
+}
+
+/**
+ * drbd_request_state() - Reqest a state change
+ * @mdev: DRBD device.
+ * @mask: mask of state bits to change.
+ * @val: value of new state bits.
+ *
+ * This is the most graceful way of requesting a state change. It is verbose
+ * quite verbose in case the state change is not possible, and all those
+ * state changes are globally serialized.
+ */
+static inline int drbd_request_state(struct drbd_conf *mdev,
+ union drbd_state mask,
+ union drbd_state val)
+{
+ return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED);
+}
+
+#define __drbd_chk_io_error(m,f) __drbd_chk_io_error_(m,f, __func__)
+static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, const char *where)
+{
+ switch (mdev->ldev->dc.on_io_error) {
+ case EP_PASS_ON:
+ if (!forcedetach) {
+ if (printk_ratelimit())
+ dev_err(DEV, "Local IO failed in %s."
+ "Passing error on...\n", where);
+ break;
+ }
+ /* NOTE fall through to detach case if forcedetach set */
+ case EP_DETACH:
+ case EP_CALL_HELPER:
+ if (mdev->state.disk > D_FAILED) {
+ _drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL);
+ dev_err(DEV, "Local IO failed in %s."
+ "Detaching...\n", where);
+ }
+ break;
+ }
+}
+
+/**
+ * drbd_chk_io_error: Handle the on_io_error setting, should be called from all io completion handlers
+ * @mdev: DRBD device.
+ * @error: Error code passed to the IO completion callback
+ * @forcedetach: Force detach. I.e. the error happened while accessing the meta data
+ *
+ * See also drbd_main.c:after_state_ch() if (os.disk > D_FAILED && ns.disk == D_FAILED)
+ */
+#define drbd_chk_io_error(m,e,f) drbd_chk_io_error_(m,e,f, __func__)
+static inline void drbd_chk_io_error_(struct drbd_conf *mdev,
+ int error, int forcedetach, const char *where)
+{
+ if (error) {
+ unsigned long flags;
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ __drbd_chk_io_error_(mdev, forcedetach, where);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+ }
+}
+
+
+/**
+ * drbd_md_first_sector() - Returns the first sector number of the meta data area
+ * @bdev: Meta data block device.
+ *
+ * BTW, for internal meta data, this happens to be the maximum capacity
+ * we could agree upon with our peer node.
+ */
+static inline sector_t drbd_md_first_sector(struct drbd_backing_dev *bdev)
+{
+ switch (bdev->dc.meta_dev_idx) {
+ case DRBD_MD_INDEX_INTERNAL:
+ case DRBD_MD_INDEX_FLEX_INT:
+ return bdev->md.md_offset + bdev->md.bm_offset;
+ case DRBD_MD_INDEX_FLEX_EXT:
+ default:
+ return bdev->md.md_offset;
+ }
+}
+
+/**
+ * drbd_md_last_sector() - Return the last sector number of the meta data area
+ * @bdev: Meta data block device.
+ */
+static inline sector_t drbd_md_last_sector(struct drbd_backing_dev *bdev)
+{
+ switch (bdev->dc.meta_dev_idx) {
+ case DRBD_MD_INDEX_INTERNAL:
+ case DRBD_MD_INDEX_FLEX_INT:
+ return bdev->md.md_offset + MD_AL_OFFSET - 1;
+ case DRBD_MD_INDEX_FLEX_EXT:
+ default:
+ return bdev->md.md_offset + bdev->md.md_size_sect;
+ }
+}
+
+/* Returns the number of 512 byte sectors of the device */
+static inline sector_t drbd_get_capacity(struct block_device *bdev)
+{
+ /* return bdev ? get_capacity(bdev->bd_disk) : 0; */
+ return bdev ? bdev->bd_inode->i_size >> 9 : 0;
+}
+
+/**
+ * drbd_get_max_capacity() - Returns the capacity we announce to out peer
+ * @bdev: Meta data block device.
+ *
+ * returns the capacity we announce to out peer. we clip ourselves at the
+ * various MAX_SECTORS, because if we don't, current implementation will
+ * oops sooner or later
+ */
+static inline sector_t drbd_get_max_capacity(struct drbd_backing_dev *bdev)
+{
+ sector_t s;
+ switch (bdev->dc.meta_dev_idx) {
+ case DRBD_MD_INDEX_INTERNAL:
+ case DRBD_MD_INDEX_FLEX_INT:
+ s = drbd_get_capacity(bdev->backing_bdev)
+ ? min_t(sector_t, DRBD_MAX_SECTORS_FLEX,
+ drbd_md_first_sector(bdev))
+ : 0;
+ break;
+ case DRBD_MD_INDEX_FLEX_EXT:
+ s = min_t(sector_t, DRBD_MAX_SECTORS_FLEX,
+ drbd_get_capacity(bdev->backing_bdev));
+ /* clip at maximum size the meta device can support */
+ s = min_t(sector_t, s,
+ BM_EXT_TO_SECT(bdev->md.md_size_sect
+ - bdev->md.bm_offset));
+ break;
+ default:
+ s = min_t(sector_t, DRBD_MAX_SECTORS,
+ drbd_get_capacity(bdev->backing_bdev));
+ }
+ return s;
+}
+
+/**
+ * drbd_md_ss__() - Return the sector number of our meta data super block
+ * @mdev: DRBD device.
+ * @bdev: Meta data block device.
+ */
+static inline sector_t drbd_md_ss__(struct drbd_conf *mdev,
+ struct drbd_backing_dev *bdev)
+{
+ switch (bdev->dc.meta_dev_idx) {
+ default: /* external, some index */
+ return MD_RESERVED_SECT * bdev->dc.meta_dev_idx;
+ case DRBD_MD_INDEX_INTERNAL:
+ /* with drbd08, internal meta data is always "flexible" */
+ case DRBD_MD_INDEX_FLEX_INT:
+ /* sizeof(struct md_on_disk_07) == 4k
+ * position: last 4k aligned block of 4k size */
+ if (!bdev->backing_bdev) {
+ if (__ratelimit(&drbd_ratelimit_state)) {
+ dev_err(DEV, "bdev->backing_bdev==NULL\n");
+ dump_stack();
+ }
+ return 0;
+ }
+ return (drbd_get_capacity(bdev->backing_bdev) & ~7ULL)
+ - MD_AL_OFFSET;
+ case DRBD_MD_INDEX_FLEX_EXT:
+ return 0;
+ }
+}
+
+static inline void
+_drbd_queue_work(struct drbd_work_queue *q, struct drbd_work *w)
+{
+ list_add_tail(&w->list, &q->q);
+ up(&q->s);
+}
+
+static inline void
+drbd_queue_work_front(struct drbd_work_queue *q, struct drbd_work *w)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&q->q_lock, flags);
+ list_add(&w->list, &q->q);
+ up(&q->s); /* within the spinlock,
+ see comment near end of drbd_worker() */
+ spin_unlock_irqrestore(&q->q_lock, flags);
+}
+
+static inline void
+drbd_queue_work(struct drbd_work_queue *q, struct drbd_work *w)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&q->q_lock, flags);
+ list_add_tail(&w->list, &q->q);
+ up(&q->s); /* within the spinlock,
+ see comment near end of drbd_worker() */
+ spin_unlock_irqrestore(&q->q_lock, flags);
+}
+
+static inline void wake_asender(struct drbd_conf *mdev)
+{
+ if (test_bit(SIGNAL_ASENDER, &mdev->flags))
+ force_sig(DRBD_SIG, mdev->asender.task);
+}
+
+static inline void request_ping(struct drbd_conf *mdev)
+{
+ set_bit(SEND_PING, &mdev->flags);
+ wake_asender(mdev);
+}
+
+static inline int drbd_send_short_cmd(struct drbd_conf *mdev,
+ enum drbd_packets cmd)
+{
+ struct p_header h;
+ return drbd_send_cmd(mdev, USE_DATA_SOCKET, cmd, &h, sizeof(h));
+}
+
+static inline int drbd_send_ping(struct drbd_conf *mdev)
+{
+ struct p_header h;
+ return drbd_send_cmd(mdev, USE_META_SOCKET, P_PING, &h, sizeof(h));
+}
+
+static inline int drbd_send_ping_ack(struct drbd_conf *mdev)
+{
+ struct p_header h;
+ return drbd_send_cmd(mdev, USE_META_SOCKET, P_PING_ACK, &h, sizeof(h));
+}
+
+static inline void drbd_thread_stop(struct drbd_thread *thi)
+{
+ _drbd_thread_stop(thi, FALSE, TRUE);
+}
+
+static inline void drbd_thread_stop_nowait(struct drbd_thread *thi)
+{
+ _drbd_thread_stop(thi, FALSE, FALSE);
+}
+
+static inline void drbd_thread_restart_nowait(struct drbd_thread *thi)
+{
+ _drbd_thread_stop(thi, TRUE, FALSE);
+}
+
+/* counts how many answer packets packets we expect from our peer,
+ * for either explicit application requests,
+ * or implicit barrier packets as necessary.
+ * increased:
+ * w_send_barrier
+ * _req_mod(req, queue_for_net_write or queue_for_net_read);
+ * it is much easier and equally valid to count what we queue for the
+ * worker, even before it actually was queued or send.
+ * (drbd_make_request_common; recovery path on read io-error)
+ * decreased:
+ * got_BarrierAck (respective tl_clear, tl_clear_barrier)
+ * _req_mod(req, data_received)
+ * [from receive_DataReply]
+ * _req_mod(req, write_acked_by_peer or recv_acked_by_peer or neg_acked)
+ * [from got_BlockAck (P_WRITE_ACK, P_RECV_ACK)]
+ * for some reason it is NOT decreased in got_NegAck,
+ * but in the resulting cleanup code from report_params.
+ * we should try to remember the reason for that...
+ * _req_mod(req, send_failed or send_canceled)
+ * _req_mod(req, connection_lost_while_pending)
+ * [from tl_clear_barrier]
+ */
+static inline void inc_ap_pending(struct drbd_conf *mdev)
+{
+ atomic_inc(&mdev->ap_pending_cnt);
+}
+
+#define ERR_IF_CNT_IS_NEGATIVE(which) \
+ if (atomic_read(&mdev->which) < 0) \
+ dev_err(DEV, "in %s:%d: " #which " = %d < 0 !\n", \
+ __func__ , __LINE__ , \
+ atomic_read(&mdev->which))
+
+#define dec_ap_pending(mdev) do { \
+ typecheck(struct drbd_conf *, mdev); \
+ if (atomic_dec_and_test(&mdev->ap_pending_cnt)) \
+ wake_up(&mdev->misc_wait); \
+ ERR_IF_CNT_IS_NEGATIVE(ap_pending_cnt); } while (0)
+
+/* counts how many resync-related answers we still expect from the peer
+ * increase decrease
+ * C_SYNC_TARGET sends P_RS_DATA_REQUEST (and expects P_RS_DATA_REPLY)
+ * C_SYNC_SOURCE sends P_RS_DATA_REPLY (and expects P_WRITE_ACK whith ID_SYNCER)
+ * (or P_NEG_ACK with ID_SYNCER)
+ */
+static inline void inc_rs_pending(struct drbd_conf *mdev)
+{
+ atomic_inc(&mdev->rs_pending_cnt);
+}
+
+#define dec_rs_pending(mdev) do { \
+ typecheck(struct drbd_conf *, mdev); \
+ atomic_dec(&mdev->rs_pending_cnt); \
+ ERR_IF_CNT_IS_NEGATIVE(rs_pending_cnt); } while (0)
+
+/* counts how many answers we still need to send to the peer.
+ * increased on
+ * receive_Data unless protocol A;
+ * we need to send a P_RECV_ACK (proto B)
+ * or P_WRITE_ACK (proto C)
+ * receive_RSDataReply (recv_resync_read) we need to send a P_WRITE_ACK
+ * receive_DataRequest (receive_RSDataRequest) we need to send back P_DATA
+ * receive_Barrier_* we need to send a P_BARRIER_ACK
+ */
+static inline void inc_unacked(struct drbd_conf *mdev)
+{
+ atomic_inc(&mdev->unacked_cnt);
+}
+
+#define dec_unacked(mdev) do { \
+ typecheck(struct drbd_conf *, mdev); \
+ atomic_dec(&mdev->unacked_cnt); \
+ ERR_IF_CNT_IS_NEGATIVE(unacked_cnt); } while (0)
+
+#define sub_unacked(mdev, n) do { \
+ typecheck(struct drbd_conf *, mdev); \
+ atomic_sub(n, &mdev->unacked_cnt); \
+ ERR_IF_CNT_IS_NEGATIVE(unacked_cnt); } while (0)
+
+
+static inline void put_net_conf(struct drbd_conf *mdev)
+{
+ if (atomic_dec_and_test(&mdev->net_cnt))
+ wake_up(&mdev->misc_wait);
+}
+
+/**
+ * get_net_conf() - Increase ref count on mdev->net_conf; Returns 0 if nothing there
+ * @mdev: DRBD device.
+ *
+ * You have to call put_net_conf() when finished working with mdev->net_conf.
+ */
+static inline int get_net_conf(struct drbd_conf *mdev)
+{
+ int have_net_conf;
+
+ atomic_inc(&mdev->net_cnt);
+ have_net_conf = mdev->state.conn >= C_UNCONNECTED;
+ if (!have_net_conf)
+ put_net_conf(mdev);
+ return have_net_conf;
+}
+
+/**
+ * get_ldev() - Increase the ref count on mdev->ldev. Returns 0 if there is no ldev
+ * @M: DRBD device.
+ *
+ * You have to call put_ldev() when finished working with mdev->ldev.
+ */
+#define get_ldev(M) __cond_lock(local, _get_ldev_if_state(M,D_INCONSISTENT))
+#define get_ldev_if_state(M,MINS) __cond_lock(local, _get_ldev_if_state(M,MINS))
+
+static inline void put_ldev(struct drbd_conf *mdev)
+{
+ __release(local);
+ if (atomic_dec_and_test(&mdev->local_cnt))
+ wake_up(&mdev->misc_wait);
+ D_ASSERT(atomic_read(&mdev->local_cnt) >= 0);
+}
+
+#ifndef __CHECKER__
+static inline int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_state mins)
+{
+ int io_allowed;
+
+ atomic_inc(&mdev->local_cnt);
+ io_allowed = (mdev->state.disk >= mins);
+ if (!io_allowed)
+ put_ldev(mdev);
+ return io_allowed;
+}
+#else
+extern int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_state mins);
+#endif
+
+/* you must have an "get_ldev" reference */
+static inline void drbd_get_syncer_progress(struct drbd_conf *mdev,
+ unsigned long *bits_left, unsigned int *per_mil_done)
+{
+ /*
+ * this is to break it at compile time when we change that
+ * (we may feel 4TB maximum storage per drbd is not enough)
+ */
+ typecheck(unsigned long, mdev->rs_total);
+
+ /* note: both rs_total and rs_left are in bits, i.e. in
+ * units of BM_BLOCK_SIZE.
+ * for the percentage, we don't care. */
+
+ *bits_left = drbd_bm_total_weight(mdev) - mdev->rs_failed;
+ /* >> 10 to prevent overflow,
+ * +1 to prevent division by zero */
+ if (*bits_left > mdev->rs_total) {
+ /* doh. maybe a logic bug somewhere.
+ * may also be just a race condition
+ * between this and a disconnect during sync.
+ * for now, just prevent in-kernel buffer overflow.
+ */
+ smp_rmb();
+ dev_warn(DEV, "cs:%s rs_left=%lu > rs_total=%lu (rs_failed %lu)\n",
+ drbd_conn_str(mdev->state.conn),
+ *bits_left, mdev->rs_total, mdev->rs_failed);
+ *per_mil_done = 0;
+ } else {
+ /* make sure the calculation happens in long context */
+ unsigned long tmp = 1000UL -
+ (*bits_left >> 10)*1000UL
+ / ((mdev->rs_total >> 10) + 1UL);
+ *per_mil_done = tmp;
+ }
+}
+
+
+/* this throttles on-the-fly application requests
+ * according to max_buffers settings;
+ * maybe re-implement using semaphores? */
+static inline int drbd_get_max_buffers(struct drbd_conf *mdev)
+{
+ int mxb = 1000000; /* arbitrary limit on open requests */
+ if (get_net_conf(mdev)) {
+ mxb = mdev->net_conf->max_buffers;
+ put_net_conf(mdev);
+ }
+ return mxb;
+}
+
+static inline int drbd_state_is_stable(union drbd_state s)
+{
+
+ /* DO NOT add a default clause, we want the compiler to warn us
+ * for any newly introduced state we may have forgotten to add here */
+
+ switch ((enum drbd_conns)s.conn) {
+ /* new io only accepted when there is no connection, ... */
+ case C_STANDALONE:
+ case C_WF_CONNECTION:
+ /* ... or there is a well established connection. */
+ case C_CONNECTED:
+ case C_SYNC_SOURCE:
+ case C_SYNC_TARGET:
+ case C_VERIFY_S:
+ case C_VERIFY_T:
+ case C_PAUSED_SYNC_S:
+ case C_PAUSED_SYNC_T:
+ /* maybe stable, look at the disk state */
+ break;
+
+ /* no new io accepted during tansitional states
+ * like handshake or teardown */
+ case C_DISCONNECTING:
+ case C_UNCONNECTED:
+ case C_TIMEOUT:
+ case C_BROKEN_PIPE:
+ case C_NETWORK_FAILURE:
+ case C_PROTOCOL_ERROR:
+ case C_TEAR_DOWN:
+ case C_WF_REPORT_PARAMS:
+ case C_STARTING_SYNC_S:
+ case C_STARTING_SYNC_T:
+ case C_WF_BITMAP_S:
+ case C_WF_BITMAP_T:
+ case C_WF_SYNC_UUID:
+ case C_MASK:
+ /* not "stable" */
+ return 0;
+ }
+
+ switch ((enum drbd_disk_state)s.disk) {
+ case D_DISKLESS:
+ case D_INCONSISTENT:
+ case D_OUTDATED:
+ case D_CONSISTENT:
+ case D_UP_TO_DATE:
+ /* disk state is stable as well. */
+ break;
+
+ /* no new io accepted during tansitional states */
+ case D_ATTACHING:
+ case D_FAILED:
+ case D_NEGOTIATING:
+ case D_UNKNOWN:
+ case D_MASK:
+ /* not "stable" */
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int __inc_ap_bio_cond(struct drbd_conf *mdev)
+{
+ int mxb = drbd_get_max_buffers(mdev);
+
+ if (mdev->state.susp)
+ return 0;
+ if (test_bit(SUSPEND_IO, &mdev->flags))
+ return 0;
+
+ /* to avoid potential deadlock or bitmap corruption,
+ * in various places, we only allow new application io
+ * to start during "stable" states. */
+
+ /* no new io accepted when attaching or detaching the disk */
+ if (!drbd_state_is_stable(mdev->state))
+ return 0;
+
+ /* since some older kernels don't have atomic_add_unless,
+ * and we are within the spinlock anyways, we have this workaround. */
+ if (atomic_read(&mdev->ap_bio_cnt) > mxb)
+ return 0;
+ if (test_bit(BITMAP_IO, &mdev->flags))
+ return 0;
+ return 1;
+}
+
+/* I'd like to use wait_event_lock_irq,
+ * but I'm not sure when it got introduced,
+ * and not sure when it has 3 or 4 arguments */
+static inline void inc_ap_bio(struct drbd_conf *mdev, int one_or_two)
+{
+ /* compare with after_state_ch,
+ * os.conn != C_WF_BITMAP_S && ns.conn == C_WF_BITMAP_S */
+ DEFINE_WAIT(wait);
+
+ /* we wait here
+ * as long as the device is suspended
+ * until the bitmap is no longer on the fly during connection
+ * handshake as long as we would exeed the max_buffer limit.
+ *
+ * to avoid races with the reconnect code,
+ * we need to atomic_inc within the spinlock. */
+
+ spin_lock_irq(&mdev->req_lock);
+ while (!__inc_ap_bio_cond(mdev)) {
+ prepare_to_wait(&mdev->misc_wait, &wait, TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&mdev->req_lock);
+ schedule();
+ finish_wait(&mdev->misc_wait, &wait);
+ spin_lock_irq(&mdev->req_lock);
+ }
+ atomic_add(one_or_two, &mdev->ap_bio_cnt);
+ spin_unlock_irq(&mdev->req_lock);
+}
+
+static inline void dec_ap_bio(struct drbd_conf *mdev)
+{
+ int mxb = drbd_get_max_buffers(mdev);
+ int ap_bio = atomic_dec_return(&mdev->ap_bio_cnt);
+
+ D_ASSERT(ap_bio >= 0);
+ /* this currently does wake_up for every dec_ap_bio!
+ * maybe rather introduce some type of hysteresis?
+ * e.g. (ap_bio == mxb/2 || ap_bio == 0) ? */
+ if (ap_bio < mxb)
+ wake_up(&mdev->misc_wait);
+ if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) {
+ if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags))
+ drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
+ }
+}
+
+static inline void drbd_set_ed_uuid(struct drbd_conf *mdev, u64 val)
+{
+ mdev->ed_uuid = val;
+}
+
+static inline int seq_cmp(u32 a, u32 b)
+{
+ /* we assume wrap around at 32bit.
+ * for wrap around at 24bit (old atomic_t),
+ * we'd have to
+ * a <<= 8; b <<= 8;
+ */
+ return (s32)(a) - (s32)(b);
+}
+#define seq_lt(a, b) (seq_cmp((a), (b)) < 0)
+#define seq_gt(a, b) (seq_cmp((a), (b)) > 0)
+#define seq_ge(a, b) (seq_cmp((a), (b)) >= 0)
+#define seq_le(a, b) (seq_cmp((a), (b)) <= 0)
+/* CAUTION: please no side effects in arguments! */
+#define seq_max(a, b) ((u32)(seq_gt((a), (b)) ? (a) : (b)))
+
+static inline void update_peer_seq(struct drbd_conf *mdev, unsigned int new_seq)
+{
+ unsigned int m;
+ spin_lock(&mdev->peer_seq_lock);
+ m = seq_max(mdev->peer_seq, new_seq);
+ mdev->peer_seq = m;
+ spin_unlock(&mdev->peer_seq_lock);
+ if (m == new_seq)
+ wake_up(&mdev->seq_wait);
+}
+
+static inline void drbd_update_congested(struct drbd_conf *mdev)
+{
+ struct sock *sk = mdev->data.socket->sk;
+ if (sk->sk_wmem_queued > sk->sk_sndbuf * 4 / 5)
+ set_bit(NET_CONGESTED, &mdev->flags);
+}
+
+static inline int drbd_queue_order_type(struct drbd_conf *mdev)
+{
+ /* sorry, we currently have no working implementation
+ * of distributed TCQ stuff */
+#ifndef QUEUE_ORDERED_NONE
+#define QUEUE_ORDERED_NONE 0
+#endif
+ return QUEUE_ORDERED_NONE;
+}
+
+static inline void drbd_blk_run_queue(struct request_queue *q)
+{
+ if (q && q->unplug_fn)
+ q->unplug_fn(q);
+}
+
+static inline void drbd_kick_lo(struct drbd_conf *mdev)
+{
+ if (get_ldev(mdev)) {
+ drbd_blk_run_queue(bdev_get_queue(mdev->ldev->backing_bdev));
+ put_ldev(mdev);
+ }
+}
+
+static inline void drbd_md_flush(struct drbd_conf *mdev)
+{
+ int r;
+
+ if (test_bit(MD_NO_BARRIER, &mdev->flags))
+ return;
+
+ r = blkdev_issue_flush(mdev->ldev->md_bdev, NULL);
+ if (r) {
+ set_bit(MD_NO_BARRIER, &mdev->flags);
+ dev_err(DEV, "meta data flush failed with status %d, disabling md-flushes\n", r);
+ }
+}
+
+#endif
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
new file mode 100644
index 00000000000..157d1e4343c
--- /dev/null
+++ b/drivers/block/drbd/drbd_main.c
@@ -0,0 +1,3699 @@
+/*
+ drbd.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ Thanks to Carter Burden, Bart Grantham and Gennadiy Nerubayev
+ from Logicworks, Inc. for making SDP replication support possible.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/drbd.h>
+#include <asm/uaccess.h>
+#include <asm/types.h>
+#include <net/sock.h>
+#include <linux/ctype.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/memcontrol.h>
+#include <linux/mm_inline.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/reboot.h>
+#include <linux/notifier.h>
+#include <linux/kthread.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#include <linux/vmalloc.h>
+
+#include <linux/drbd_limits.h>
+#include "drbd_int.h"
+#include "drbd_req.h" /* only for _req_mod in tl_release and tl_clear */
+
+#include "drbd_vli.h"
+
+struct after_state_chg_work {
+ struct drbd_work w;
+ union drbd_state os;
+ union drbd_state ns;
+ enum chg_state_flags flags;
+ struct completion *done;
+};
+
+int drbdd_init(struct drbd_thread *);
+int drbd_worker(struct drbd_thread *);
+int drbd_asender(struct drbd_thread *);
+
+int drbd_init(void);
+static int drbd_open(struct block_device *bdev, fmode_t mode);
+static int drbd_release(struct gendisk *gd, fmode_t mode);
+static int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused);
+static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
+ union drbd_state ns, enum chg_state_flags flags);
+static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused);
+static void md_sync_timer_fn(unsigned long data);
+static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused);
+
+MODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, "
+ "Lars Ellenberg <lars@linbit.com>");
+MODULE_DESCRIPTION("drbd - Distributed Replicated Block Device v" REL_VERSION);
+MODULE_VERSION(REL_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(minor_count, "Maximum number of drbd devices (1-255)");
+MODULE_ALIAS_BLOCKDEV_MAJOR(DRBD_MAJOR);
+
+#include <linux/moduleparam.h>
+/* allow_open_on_secondary */
+MODULE_PARM_DESC(allow_oos, "DONT USE!");
+/* thanks to these macros, if compiled into the kernel (not-module),
+ * this becomes the boot parameter drbd.minor_count */
+module_param(minor_count, uint, 0444);
+module_param(disable_sendpage, bool, 0644);
+module_param(allow_oos, bool, 0);
+module_param(cn_idx, uint, 0444);
+module_param(proc_details, int, 0644);
+
+#ifdef CONFIG_DRBD_FAULT_INJECTION
+int enable_faults;
+int fault_rate;
+static int fault_count;
+int fault_devs;
+/* bitmap of enabled faults */
+module_param(enable_faults, int, 0664);
+/* fault rate % value - applies to all enabled faults */
+module_param(fault_rate, int, 0664);
+/* count of faults inserted */
+module_param(fault_count, int, 0664);
+/* bitmap of devices to insert faults on */
+module_param(fault_devs, int, 0644);
+#endif
+
+/* module parameter, defined */
+unsigned int minor_count = 32;
+int disable_sendpage;
+int allow_oos;
+unsigned int cn_idx = CN_IDX_DRBD;
+int proc_details; /* Detail level in proc drbd*/
+
+/* Module parameter for setting the user mode helper program
+ * to run. Default is /sbin/drbdadm */
+char usermode_helper[80] = "/sbin/drbdadm";
+
+module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0644);
+
+/* in 2.6.x, our device mapping and config info contains our virtual gendisks
+ * as member "struct gendisk *vdisk;"
+ */
+struct drbd_conf **minor_table;
+
+struct kmem_cache *drbd_request_cache;
+struct kmem_cache *drbd_ee_cache; /* epoch entries */
+struct kmem_cache *drbd_bm_ext_cache; /* bitmap extents */
+struct kmem_cache *drbd_al_ext_cache; /* activity log extents */
+mempool_t *drbd_request_mempool;
+mempool_t *drbd_ee_mempool;
+
+/* I do not use a standard mempool, because:
+ 1) I want to hand out the pre-allocated objects first.
+ 2) I want to be able to interrupt sleeping allocation with a signal.
+ Note: This is a single linked list, the next pointer is the private
+ member of struct page.
+ */
+struct page *drbd_pp_pool;
+spinlock_t drbd_pp_lock;
+int drbd_pp_vacant;
+wait_queue_head_t drbd_pp_wait;
+
+DEFINE_RATELIMIT_STATE(drbd_ratelimit_state, 5 * HZ, 5);
+
+static struct block_device_operations drbd_ops = {
+ .owner = THIS_MODULE,
+ .open = drbd_open,
+ .release = drbd_release,
+};
+
+#define ARRY_SIZE(A) (sizeof(A)/sizeof(A[0]))
+
+#ifdef __CHECKER__
+/* When checking with sparse, and this is an inline function, sparse will
+ give tons of false positives. When this is a real functions sparse works.
+ */
+int _get_ldev_if_state(struct drbd_conf *mdev, enum drbd_disk_state mins)
+{
+ int io_allowed;
+
+ atomic_inc(&mdev->local_cnt);
+ io_allowed = (mdev->state.disk >= mins);
+ if (!io_allowed) {
+ if (atomic_dec_and_test(&mdev->local_cnt))
+ wake_up(&mdev->misc_wait);
+ }
+ return io_allowed;
+}
+
+#endif
+
+/**
+ * DOC: The transfer log
+ *
+ * The transfer log is a single linked list of &struct drbd_tl_epoch objects.
+ * mdev->newest_tle points to the head, mdev->oldest_tle points to the tail
+ * of the list. There is always at least one &struct drbd_tl_epoch object.
+ *
+ * Each &struct drbd_tl_epoch has a circular double linked list of requests
+ * attached.
+ */
+static int tl_init(struct drbd_conf *mdev)
+{
+ struct drbd_tl_epoch *b;
+
+ /* during device minor initialization, we may well use GFP_KERNEL */
+ b = kmalloc(sizeof(struct drbd_tl_epoch), GFP_KERNEL);
+ if (!b)
+ return 0;
+ INIT_LIST_HEAD(&b->requests);
+ INIT_LIST_HEAD(&b->w.list);
+ b->next = NULL;
+ b->br_number = 4711;
+ b->n_req = 0;
+ b->w.cb = NULL; /* if this is != NULL, we need to dec_ap_pending in tl_clear */
+
+ mdev->oldest_tle = b;
+ mdev->newest_tle = b;
+ INIT_LIST_HEAD(&mdev->out_of_sequence_requests);
+
+ mdev->tl_hash = NULL;
+ mdev->tl_hash_s = 0;
+
+ return 1;
+}
+
+static void tl_cleanup(struct drbd_conf *mdev)
+{
+ D_ASSERT(mdev->oldest_tle == mdev->newest_tle);
+ D_ASSERT(list_empty(&mdev->out_of_sequence_requests));
+ kfree(mdev->oldest_tle);
+ mdev->oldest_tle = NULL;
+ kfree(mdev->unused_spare_tle);
+ mdev->unused_spare_tle = NULL;
+ kfree(mdev->tl_hash);
+ mdev->tl_hash = NULL;
+ mdev->tl_hash_s = 0;
+}
+
+/**
+ * _tl_add_barrier() - Adds a barrier to the transfer log
+ * @mdev: DRBD device.
+ * @new: Barrier to be added before the current head of the TL.
+ *
+ * The caller must hold the req_lock.
+ */
+void _tl_add_barrier(struct drbd_conf *mdev, struct drbd_tl_epoch *new)
+{
+ struct drbd_tl_epoch *newest_before;
+
+ INIT_LIST_HEAD(&new->requests);
+ INIT_LIST_HEAD(&new->w.list);
+ new->w.cb = NULL; /* if this is != NULL, we need to dec_ap_pending in tl_clear */
+ new->next = NULL;
+ new->n_req = 0;
+
+ newest_before = mdev->newest_tle;
+ /* never send a barrier number == 0, because that is special-cased
+ * when using TCQ for our write ordering code */
+ new->br_number = (newest_before->br_number+1) ?: 1;
+ if (mdev->newest_tle != new) {
+ mdev->newest_tle->next = new;
+ mdev->newest_tle = new;
+ }
+}
+
+/**
+ * tl_release() - Free or recycle the oldest &struct drbd_tl_epoch object of the TL
+ * @mdev: DRBD device.
+ * @barrier_nr: Expected identifier of the DRBD write barrier packet.
+ * @set_size: Expected number of requests before that barrier.
+ *
+ * In case the passed barrier_nr or set_size does not match the oldest
+ * &struct drbd_tl_epoch objects this function will cause a termination
+ * of the connection.
+ */
+void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr,
+ unsigned int set_size)
+{
+ struct drbd_tl_epoch *b, *nob; /* next old barrier */
+ struct list_head *le, *tle;
+ struct drbd_request *r;
+
+ spin_lock_irq(&mdev->req_lock);
+
+ b = mdev->oldest_tle;
+
+ /* first some paranoia code */
+ if (b == NULL) {
+ dev_err(DEV, "BAD! BarrierAck #%u received, but no epoch in tl!?\n",
+ barrier_nr);
+ goto bail;
+ }
+ if (b->br_number != barrier_nr) {
+ dev_err(DEV, "BAD! BarrierAck #%u received, expected #%u!\n",
+ barrier_nr, b->br_number);
+ goto bail;
+ }
+ if (b->n_req != set_size) {
+ dev_err(DEV, "BAD! BarrierAck #%u received with n_req=%u, expected n_req=%u!\n",
+ barrier_nr, set_size, b->n_req);
+ goto bail;
+ }
+
+ /* Clean up list of requests processed during current epoch */
+ list_for_each_safe(le, tle, &b->requests) {
+ r = list_entry(le, struct drbd_request, tl_requests);
+ _req_mod(r, barrier_acked);
+ }
+ /* There could be requests on the list waiting for completion
+ of the write to the local disk. To avoid corruptions of
+ slab's data structures we have to remove the lists head.
+
+ Also there could have been a barrier ack out of sequence, overtaking
+ the write acks - which would be a bug and violating write ordering.
+ To not deadlock in case we lose connection while such requests are
+ still pending, we need some way to find them for the
+ _req_mode(connection_lost_while_pending).
+
+ These have been list_move'd to the out_of_sequence_requests list in
+ _req_mod(, barrier_acked) above.
+ */
+ list_del_init(&b->requests);
+
+ nob = b->next;
+ if (test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) {
+ _tl_add_barrier(mdev, b);
+ if (nob)
+ mdev->oldest_tle = nob;
+ /* if nob == NULL b was the only barrier, and becomes the new
+ barrier. Therefore mdev->oldest_tle points already to b */
+ } else {
+ D_ASSERT(nob != NULL);
+ mdev->oldest_tle = nob;
+ kfree(b);
+ }
+
+ spin_unlock_irq(&mdev->req_lock);
+ dec_ap_pending(mdev);
+
+ return;
+
+bail:
+ spin_unlock_irq(&mdev->req_lock);
+ drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR));
+}
+
+
+/**
+ * tl_clear() - Clears all requests and &struct drbd_tl_epoch objects out of the TL
+ * @mdev: DRBD device.
+ *
+ * This is called after the connection to the peer was lost. The storage covered
+ * by the requests on the transfer gets marked as our of sync. Called from the
+ * receiver thread and the worker thread.
+ */
+void tl_clear(struct drbd_conf *mdev)
+{
+ struct drbd_tl_epoch *b, *tmp;
+ struct list_head *le, *tle;
+ struct drbd_request *r;
+ int new_initial_bnr = net_random();
+
+ spin_lock_irq(&mdev->req_lock);
+
+ b = mdev->oldest_tle;
+ while (b) {
+ list_for_each_safe(le, tle, &b->requests) {
+ r = list_entry(le, struct drbd_request, tl_requests);
+ /* It would be nice to complete outside of spinlock.
+ * But this is easier for now. */
+ _req_mod(r, connection_lost_while_pending);
+ }
+ tmp = b->next;
+
+ /* there could still be requests on that ring list,
+ * in case local io is still pending */
+ list_del(&b->requests);
+
+ /* dec_ap_pending corresponding to queue_barrier.
+ * the newest barrier may not have been queued yet,
+ * in which case w.cb is still NULL. */
+ if (b->w.cb != NULL)
+ dec_ap_pending(mdev);
+
+ if (b == mdev->newest_tle) {
+ /* recycle, but reinit! */
+ D_ASSERT(tmp == NULL);
+ INIT_LIST_HEAD(&b->requests);
+ INIT_LIST_HEAD(&b->w.list);
+ b->w.cb = NULL;
+ b->br_number = new_initial_bnr;
+ b->n_req = 0;
+
+ mdev->oldest_tle = b;
+ break;
+ }
+ kfree(b);
+ b = tmp;
+ }
+
+ /* we expect this list to be empty. */
+ D_ASSERT(list_empty(&mdev->out_of_sequence_requests));
+
+ /* but just in case, clean it up anyways! */
+ list_for_each_safe(le, tle, &mdev->out_of_sequence_requests) {
+ r = list_entry(le, struct drbd_request, tl_requests);
+ /* It would be nice to complete outside of spinlock.
+ * But this is easier for now. */
+ _req_mod(r, connection_lost_while_pending);
+ }
+
+ /* ensure bit indicating barrier is required is clear */
+ clear_bit(CREATE_BARRIER, &mdev->flags);
+
+ spin_unlock_irq(&mdev->req_lock);
+}
+
+/**
+ * cl_wide_st_chg() - TRUE if the state change is a cluster wide one
+ * @mdev: DRBD device.
+ * @os: old (current) state.
+ * @ns: new (wanted) state.
+ */
+static int cl_wide_st_chg(struct drbd_conf *mdev,
+ union drbd_state os, union drbd_state ns)
+{
+ return (os.conn >= C_CONNECTED && ns.conn >= C_CONNECTED &&
+ ((os.role != R_PRIMARY && ns.role == R_PRIMARY) ||
+ (os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
+ (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S) ||
+ (os.disk != D_DISKLESS && ns.disk == D_DISKLESS))) ||
+ (os.conn >= C_CONNECTED && ns.conn == C_DISCONNECTING) ||
+ (os.conn == C_CONNECTED && ns.conn == C_VERIFY_S);
+}
+
+int drbd_change_state(struct drbd_conf *mdev, enum chg_state_flags f,
+ union drbd_state mask, union drbd_state val)
+{
+ unsigned long flags;
+ union drbd_state os, ns;
+ int rv;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ os = mdev->state;
+ ns.i = (os.i & ~mask.i) | val.i;
+ rv = _drbd_set_state(mdev, ns, f, NULL);
+ ns = mdev->state;
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ return rv;
+}
+
+/**
+ * drbd_force_state() - Impose a change which happens outside our control on our state
+ * @mdev: DRBD device.
+ * @mask: mask of state bits to change.
+ * @val: value of new state bits.
+ */
+void drbd_force_state(struct drbd_conf *mdev,
+ union drbd_state mask, union drbd_state val)
+{
+ drbd_change_state(mdev, CS_HARD, mask, val);
+}
+
+static int is_valid_state(struct drbd_conf *mdev, union drbd_state ns);
+static int is_valid_state_transition(struct drbd_conf *,
+ union drbd_state, union drbd_state);
+static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
+ union drbd_state ns, int *warn_sync_abort);
+int drbd_send_state_req(struct drbd_conf *,
+ union drbd_state, union drbd_state);
+
+static enum drbd_state_ret_codes _req_st_cond(struct drbd_conf *mdev,
+ union drbd_state mask, union drbd_state val)
+{
+ union drbd_state os, ns;
+ unsigned long flags;
+ int rv;
+
+ if (test_and_clear_bit(CL_ST_CHG_SUCCESS, &mdev->flags))
+ return SS_CW_SUCCESS;
+
+ if (test_and_clear_bit(CL_ST_CHG_FAIL, &mdev->flags))
+ return SS_CW_FAILED_BY_PEER;
+
+ rv = 0;
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ os = mdev->state;
+ ns.i = (os.i & ~mask.i) | val.i;
+ ns = sanitize_state(mdev, os, ns, NULL);
+
+ if (!cl_wide_st_chg(mdev, os, ns))
+ rv = SS_CW_NO_NEED;
+ if (!rv) {
+ rv = is_valid_state(mdev, ns);
+ if (rv == SS_SUCCESS) {
+ rv = is_valid_state_transition(mdev, ns, os);
+ if (rv == SS_SUCCESS)
+ rv = 0; /* cont waiting, otherwise fail. */
+ }
+ }
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ return rv;
+}
+
+/**
+ * drbd_req_state() - Perform an eventually cluster wide state change
+ * @mdev: DRBD device.
+ * @mask: mask of state bits to change.
+ * @val: value of new state bits.
+ * @f: flags
+ *
+ * Should not be called directly, use drbd_request_state() or
+ * _drbd_request_state().
+ */
+static int drbd_req_state(struct drbd_conf *mdev,
+ union drbd_state mask, union drbd_state val,
+ enum chg_state_flags f)
+{
+ struct completion done;
+ unsigned long flags;
+ union drbd_state os, ns;
+ int rv;
+
+ init_completion(&done);
+
+ if (f & CS_SERIALIZE)
+ mutex_lock(&mdev->state_mutex);
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ os = mdev->state;
+ ns.i = (os.i & ~mask.i) | val.i;
+ ns = sanitize_state(mdev, os, ns, NULL);
+
+ if (cl_wide_st_chg(mdev, os, ns)) {
+ rv = is_valid_state(mdev, ns);
+ if (rv == SS_SUCCESS)
+ rv = is_valid_state_transition(mdev, ns, os);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ if (rv < SS_SUCCESS) {
+ if (f & CS_VERBOSE)
+ print_st_err(mdev, os, ns, rv);
+ goto abort;
+ }
+
+ drbd_state_lock(mdev);
+ if (!drbd_send_state_req(mdev, mask, val)) {
+ drbd_state_unlock(mdev);
+ rv = SS_CW_FAILED_BY_PEER;
+ if (f & CS_VERBOSE)
+ print_st_err(mdev, os, ns, rv);
+ goto abort;
+ }
+
+ wait_event(mdev->state_wait,
+ (rv = _req_st_cond(mdev, mask, val)));
+
+ if (rv < SS_SUCCESS) {
+ drbd_state_unlock(mdev);
+ if (f & CS_VERBOSE)
+ print_st_err(mdev, os, ns, rv);
+ goto abort;
+ }
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ os = mdev->state;
+ ns.i = (os.i & ~mask.i) | val.i;
+ rv = _drbd_set_state(mdev, ns, f, &done);
+ drbd_state_unlock(mdev);
+ } else {
+ rv = _drbd_set_state(mdev, ns, f, &done);
+ }
+
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ if (f & CS_WAIT_COMPLETE && rv == SS_SUCCESS) {
+ D_ASSERT(current != mdev->worker.task);
+ wait_for_completion(&done);
+ }
+
+abort:
+ if (f & CS_SERIALIZE)
+ mutex_unlock(&mdev->state_mutex);
+
+ return rv;
+}
+
+/**
+ * _drbd_request_state() - Request a state change (with flags)
+ * @mdev: DRBD device.
+ * @mask: mask of state bits to change.
+ * @val: value of new state bits.
+ * @f: flags
+ *
+ * Cousin of drbd_request_state(), useful with the CS_WAIT_COMPLETE
+ * flag, or when logging of failed state change requests is not desired.
+ */
+int _drbd_request_state(struct drbd_conf *mdev, union drbd_state mask,
+ union drbd_state val, enum chg_state_flags f)
+{
+ int rv;
+
+ wait_event(mdev->state_wait,
+ (rv = drbd_req_state(mdev, mask, val, f)) != SS_IN_TRANSIENT_STATE);
+
+ return rv;
+}
+
+static void print_st(struct drbd_conf *mdev, char *name, union drbd_state ns)
+{
+ dev_err(DEV, " %s = { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c }\n",
+ name,
+ drbd_conn_str(ns.conn),
+ drbd_role_str(ns.role),
+ drbd_role_str(ns.peer),
+ drbd_disk_str(ns.disk),
+ drbd_disk_str(ns.pdsk),
+ ns.susp ? 's' : 'r',
+ ns.aftr_isp ? 'a' : '-',
+ ns.peer_isp ? 'p' : '-',
+ ns.user_isp ? 'u' : '-'
+ );
+}
+
+void print_st_err(struct drbd_conf *mdev,
+ union drbd_state os, union drbd_state ns, int err)
+{
+ if (err == SS_IN_TRANSIENT_STATE)
+ return;
+ dev_err(DEV, "State change failed: %s\n", drbd_set_st_err_str(err));
+ print_st(mdev, " state", os);
+ print_st(mdev, "wanted", ns);
+}
+
+
+#define drbd_peer_str drbd_role_str
+#define drbd_pdsk_str drbd_disk_str
+
+#define drbd_susp_str(A) ((A) ? "1" : "0")
+#define drbd_aftr_isp_str(A) ((A) ? "1" : "0")
+#define drbd_peer_isp_str(A) ((A) ? "1" : "0")
+#define drbd_user_isp_str(A) ((A) ? "1" : "0")
+
+#define PSC(A) \
+ ({ if (ns.A != os.A) { \
+ pbp += sprintf(pbp, #A "( %s -> %s ) ", \
+ drbd_##A##_str(os.A), \
+ drbd_##A##_str(ns.A)); \
+ } })
+
+/**
+ * is_valid_state() - Returns an SS_ error code if ns is not valid
+ * @mdev: DRBD device.
+ * @ns: State to consider.
+ */
+static int is_valid_state(struct drbd_conf *mdev, union drbd_state ns)
+{
+ /* See drbd_state_sw_errors in drbd_strings.c */
+
+ enum drbd_fencing_p fp;
+ int rv = SS_SUCCESS;
+
+ fp = FP_DONT_CARE;
+ if (get_ldev(mdev)) {
+ fp = mdev->ldev->dc.fencing;
+ put_ldev(mdev);
+ }
+
+ if (get_net_conf(mdev)) {
+ if (!mdev->net_conf->two_primaries &&
+ ns.role == R_PRIMARY && ns.peer == R_PRIMARY)
+ rv = SS_TWO_PRIMARIES;
+ put_net_conf(mdev);
+ }
+
+ if (rv <= 0)
+ /* already found a reason to abort */;
+ else if (ns.role == R_SECONDARY && mdev->open_cnt)
+ rv = SS_DEVICE_IN_USE;
+
+ else if (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.disk < D_UP_TO_DATE)
+ rv = SS_NO_UP_TO_DATE_DISK;
+
+ else if (fp >= FP_RESOURCE &&
+ ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk >= D_UNKNOWN)
+ rv = SS_PRIMARY_NOP;
+
+ else if (ns.role == R_PRIMARY && ns.disk <= D_INCONSISTENT && ns.pdsk <= D_INCONSISTENT)
+ rv = SS_NO_UP_TO_DATE_DISK;
+
+ else if (ns.conn > C_CONNECTED && ns.disk < D_INCONSISTENT)
+ rv = SS_NO_LOCAL_DISK;
+
+ else if (ns.conn > C_CONNECTED && ns.pdsk < D_INCONSISTENT)
+ rv = SS_NO_REMOTE_DISK;
+
+ else if ((ns.conn == C_CONNECTED ||
+ ns.conn == C_WF_BITMAP_S ||
+ ns.conn == C_SYNC_SOURCE ||
+ ns.conn == C_PAUSED_SYNC_S) &&
+ ns.disk == D_OUTDATED)
+ rv = SS_CONNECTED_OUTDATES;
+
+ else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
+ (mdev->sync_conf.verify_alg[0] == 0))
+ rv = SS_NO_VERIFY_ALG;
+
+ else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
+ mdev->agreed_pro_version < 88)
+ rv = SS_NOT_SUPPORTED;
+
+ return rv;
+}
+
+/**
+ * is_valid_state_transition() - Returns an SS_ error code if the state transition is not possible
+ * @mdev: DRBD device.
+ * @ns: new state.
+ * @os: old state.
+ */
+static int is_valid_state_transition(struct drbd_conf *mdev,
+ union drbd_state ns, union drbd_state os)
+{
+ int rv = SS_SUCCESS;
+
+ if ((ns.conn == C_STARTING_SYNC_T || ns.conn == C_STARTING_SYNC_S) &&
+ os.conn > C_CONNECTED)
+ rv = SS_RESYNC_RUNNING;
+
+ if (ns.conn == C_DISCONNECTING && os.conn == C_STANDALONE)
+ rv = SS_ALREADY_STANDALONE;
+
+ if (ns.disk > D_ATTACHING && os.disk == D_DISKLESS)
+ rv = SS_IS_DISKLESS;
+
+ if (ns.conn == C_WF_CONNECTION && os.conn < C_UNCONNECTED)
+ rv = SS_NO_NET_CONFIG;
+
+ if (ns.disk == D_OUTDATED && os.disk < D_OUTDATED && os.disk != D_ATTACHING)
+ rv = SS_LOWER_THAN_OUTDATED;
+
+ if (ns.conn == C_DISCONNECTING && os.conn == C_UNCONNECTED)
+ rv = SS_IN_TRANSIENT_STATE;
+
+ if (ns.conn == os.conn && ns.conn == C_WF_REPORT_PARAMS)
+ rv = SS_IN_TRANSIENT_STATE;
+
+ if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED)
+ rv = SS_NEED_CONNECTION;
+
+ if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
+ ns.conn != os.conn && os.conn > C_CONNECTED)
+ rv = SS_RESYNC_RUNNING;
+
+ if ((ns.conn == C_STARTING_SYNC_S || ns.conn == C_STARTING_SYNC_T) &&
+ os.conn < C_CONNECTED)
+ rv = SS_NEED_CONNECTION;
+
+ return rv;
+}
+
+/**
+ * sanitize_state() - Resolves implicitly necessary additional changes to a state transition
+ * @mdev: DRBD device.
+ * @os: old state.
+ * @ns: new state.
+ * @warn_sync_abort:
+ *
+ * When we loose connection, we have to set the state of the peers disk (pdsk)
+ * to D_UNKNOWN. This rule and many more along those lines are in this function.
+ */
+static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
+ union drbd_state ns, int *warn_sync_abort)
+{
+ enum drbd_fencing_p fp;
+
+ fp = FP_DONT_CARE;
+ if (get_ldev(mdev)) {
+ fp = mdev->ldev->dc.fencing;
+ put_ldev(mdev);
+ }
+
+ /* Disallow Network errors to configure a device's network part */
+ if ((ns.conn >= C_TIMEOUT && ns.conn <= C_TEAR_DOWN) &&
+ os.conn <= C_DISCONNECTING)
+ ns.conn = os.conn;
+
+ /* After a network error (+C_TEAR_DOWN) only C_UNCONNECTED or C_DISCONNECTING can follow */
+ if (os.conn >= C_TIMEOUT && os.conn <= C_TEAR_DOWN &&
+ ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING)
+ ns.conn = os.conn;
+
+ /* After C_DISCONNECTING only C_STANDALONE may follow */
+ if (os.conn == C_DISCONNECTING && ns.conn != C_STANDALONE)
+ ns.conn = os.conn;
+
+ if (ns.conn < C_CONNECTED) {
+ ns.peer_isp = 0;
+ ns.peer = R_UNKNOWN;
+ if (ns.pdsk > D_UNKNOWN || ns.pdsk < D_INCONSISTENT)
+ ns.pdsk = D_UNKNOWN;
+ }
+
+ /* Clear the aftr_isp when becoming unconfigured */
+ if (ns.conn == C_STANDALONE && ns.disk == D_DISKLESS && ns.role == R_SECONDARY)
+ ns.aftr_isp = 0;
+
+ if (ns.conn <= C_DISCONNECTING && ns.disk == D_DISKLESS)
+ ns.pdsk = D_UNKNOWN;
+
+ /* Abort resync if a disk fails/detaches */
+ if (os.conn > C_CONNECTED && ns.conn > C_CONNECTED &&
+ (ns.disk <= D_FAILED || ns.pdsk <= D_FAILED)) {
+ if (warn_sync_abort)
+ *warn_sync_abort = 1;
+ ns.conn = C_CONNECTED;
+ }
+
+ if (ns.conn >= C_CONNECTED &&
+ ((ns.disk == D_CONSISTENT || ns.disk == D_OUTDATED) ||
+ (ns.disk == D_NEGOTIATING && ns.conn == C_WF_BITMAP_T))) {
+ switch (ns.conn) {
+ case C_WF_BITMAP_T:
+ case C_PAUSED_SYNC_T:
+ ns.disk = D_OUTDATED;
+ break;
+ case C_CONNECTED:
+ case C_WF_BITMAP_S:
+ case C_SYNC_SOURCE:
+ case C_PAUSED_SYNC_S:
+ ns.disk = D_UP_TO_DATE;
+ break;
+ case C_SYNC_TARGET:
+ ns.disk = D_INCONSISTENT;
+ dev_warn(DEV, "Implicitly set disk state Inconsistent!\n");
+ break;
+ }
+ if (os.disk == D_OUTDATED && ns.disk == D_UP_TO_DATE)
+ dev_warn(DEV, "Implicitly set disk from Outdated to UpToDate\n");
+ }
+
+ if (ns.conn >= C_CONNECTED &&
+ (ns.pdsk == D_CONSISTENT || ns.pdsk == D_OUTDATED)) {
+ switch (ns.conn) {
+ case C_CONNECTED:
+ case C_WF_BITMAP_T:
+ case C_PAUSED_SYNC_T:
+ case C_SYNC_TARGET:
+ ns.pdsk = D_UP_TO_DATE;
+ break;
+ case C_WF_BITMAP_S:
+ case C_PAUSED_SYNC_S:
+ ns.pdsk = D_OUTDATED;
+ break;
+ case C_SYNC_SOURCE:
+ ns.pdsk = D_INCONSISTENT;
+ dev_warn(DEV, "Implicitly set pdsk Inconsistent!\n");
+ break;
+ }
+ if (os.pdsk == D_OUTDATED && ns.pdsk == D_UP_TO_DATE)
+ dev_warn(DEV, "Implicitly set pdsk from Outdated to UpToDate\n");
+ }
+
+ /* Connection breaks down before we finished "Negotiating" */
+ if (ns.conn < C_CONNECTED && ns.disk == D_NEGOTIATING &&
+ get_ldev_if_state(mdev, D_NEGOTIATING)) {
+ if (mdev->ed_uuid == mdev->ldev->md.uuid[UI_CURRENT]) {
+ ns.disk = mdev->new_state_tmp.disk;
+ ns.pdsk = mdev->new_state_tmp.pdsk;
+ } else {
+ dev_alert(DEV, "Connection lost while negotiating, no data!\n");
+ ns.disk = D_DISKLESS;
+ ns.pdsk = D_UNKNOWN;
+ }
+ put_ldev(mdev);
+ }
+
+ if (fp == FP_STONITH &&
+ (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk > D_OUTDATED) &&
+ !(os.role == R_PRIMARY && os.conn < C_CONNECTED && os.pdsk > D_OUTDATED))
+ ns.susp = 1;
+
+ if (ns.aftr_isp || ns.peer_isp || ns.user_isp) {
+ if (ns.conn == C_SYNC_SOURCE)
+ ns.conn = C_PAUSED_SYNC_S;
+ if (ns.conn == C_SYNC_TARGET)
+ ns.conn = C_PAUSED_SYNC_T;
+ } else {
+ if (ns.conn == C_PAUSED_SYNC_S)
+ ns.conn = C_SYNC_SOURCE;
+ if (ns.conn == C_PAUSED_SYNC_T)
+ ns.conn = C_SYNC_TARGET;
+ }
+
+ return ns;
+}
+
+/* helper for __drbd_set_state */
+static void set_ov_position(struct drbd_conf *mdev, enum drbd_conns cs)
+{
+ if (cs == C_VERIFY_T) {
+ /* starting online verify from an arbitrary position
+ * does not fit well into the existing protocol.
+ * on C_VERIFY_T, we initialize ov_left and friends
+ * implicitly in receive_DataRequest once the
+ * first P_OV_REQUEST is received */
+ mdev->ov_start_sector = ~(sector_t)0;
+ } else {
+ unsigned long bit = BM_SECT_TO_BIT(mdev->ov_start_sector);
+ if (bit >= mdev->rs_total)
+ mdev->ov_start_sector =
+ BM_BIT_TO_SECT(mdev->rs_total - 1);
+ mdev->ov_position = mdev->ov_start_sector;
+ }
+}
+
+/**
+ * __drbd_set_state() - Set a new DRBD state
+ * @mdev: DRBD device.
+ * @ns: new state.
+ * @flags: Flags
+ * @done: Optional completion, that will get completed after the after_state_ch() finished
+ *
+ * Caller needs to hold req_lock, and global_state_lock. Do not call directly.
+ */
+int __drbd_set_state(struct drbd_conf *mdev,
+ union drbd_state ns, enum chg_state_flags flags,
+ struct completion *done)
+{
+ union drbd_state os;
+ int rv = SS_SUCCESS;
+ int warn_sync_abort = 0;
+ struct after_state_chg_work *ascw;
+
+ os = mdev->state;
+
+ ns = sanitize_state(mdev, os, ns, &warn_sync_abort);
+
+ if (ns.i == os.i)
+ return SS_NOTHING_TO_DO;
+
+ if (!(flags & CS_HARD)) {
+ /* pre-state-change checks ; only look at ns */
+ /* See drbd_state_sw_errors in drbd_strings.c */
+
+ rv = is_valid_state(mdev, ns);
+ if (rv < SS_SUCCESS) {
+ /* If the old state was illegal as well, then let
+ this happen...*/
+
+ if (is_valid_state(mdev, os) == rv) {
+ dev_err(DEV, "Considering state change from bad state. "
+ "Error would be: '%s'\n",
+ drbd_set_st_err_str(rv));
+ print_st(mdev, "old", os);
+ print_st(mdev, "new", ns);
+ rv = is_valid_state_transition(mdev, ns, os);
+ }
+ } else
+ rv = is_valid_state_transition(mdev, ns, os);
+ }
+
+ if (rv < SS_SUCCESS) {
+ if (flags & CS_VERBOSE)
+ print_st_err(mdev, os, ns, rv);
+ return rv;
+ }
+
+ if (warn_sync_abort)
+ dev_warn(DEV, "Resync aborted.\n");
+
+ {
+ char *pbp, pb[300];
+ pbp = pb;
+ *pbp = 0;
+ PSC(role);
+ PSC(peer);
+ PSC(conn);
+ PSC(disk);
+ PSC(pdsk);
+ PSC(susp);
+ PSC(aftr_isp);
+ PSC(peer_isp);
+ PSC(user_isp);
+ dev_info(DEV, "%s\n", pb);
+ }
+
+ /* solve the race between becoming unconfigured,
+ * worker doing the cleanup, and
+ * admin reconfiguring us:
+ * on (re)configure, first set CONFIG_PENDING,
+ * then wait for a potentially exiting worker,
+ * start the worker, and schedule one no_op.
+ * then proceed with configuration.
+ */
+ if (ns.disk == D_DISKLESS &&
+ ns.conn == C_STANDALONE &&
+ ns.role == R_SECONDARY &&
+ !test_and_set_bit(CONFIG_PENDING, &mdev->flags))
+ set_bit(DEVICE_DYING, &mdev->flags);
+
+ mdev->state.i = ns.i;
+ wake_up(&mdev->misc_wait);
+ wake_up(&mdev->state_wait);
+
+ /* post-state-change actions */
+ if (os.conn >= C_SYNC_SOURCE && ns.conn <= C_CONNECTED) {
+ set_bit(STOP_SYNC_TIMER, &mdev->flags);
+ mod_timer(&mdev->resync_timer, jiffies);
+ }
+
+ /* aborted verify run. log the last position */
+ if ((os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) &&
+ ns.conn < C_CONNECTED) {
+ mdev->ov_start_sector =
+ BM_BIT_TO_SECT(mdev->rs_total - mdev->ov_left);
+ dev_info(DEV, "Online Verify reached sector %llu\n",
+ (unsigned long long)mdev->ov_start_sector);
+ }
+
+ if ((os.conn == C_PAUSED_SYNC_T || os.conn == C_PAUSED_SYNC_S) &&
+ (ns.conn == C_SYNC_TARGET || ns.conn == C_SYNC_SOURCE)) {
+ dev_info(DEV, "Syncer continues.\n");
+ mdev->rs_paused += (long)jiffies-(long)mdev->rs_mark_time;
+ if (ns.conn == C_SYNC_TARGET) {
+ if (!test_and_clear_bit(STOP_SYNC_TIMER, &mdev->flags))
+ mod_timer(&mdev->resync_timer, jiffies);
+ /* This if (!test_bit) is only needed for the case
+ that a device that has ceased to used its timer,
+ i.e. it is already in drbd_resync_finished() gets
+ paused and resumed. */
+ }
+ }
+
+ if ((os.conn == C_SYNC_TARGET || os.conn == C_SYNC_SOURCE) &&
+ (ns.conn == C_PAUSED_SYNC_T || ns.conn == C_PAUSED_SYNC_S)) {
+ dev_info(DEV, "Resync suspended\n");
+ mdev->rs_mark_time = jiffies;
+ if (ns.conn == C_PAUSED_SYNC_T)
+ set_bit(STOP_SYNC_TIMER, &mdev->flags);
+ }
+
+ if (os.conn == C_CONNECTED &&
+ (ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T)) {
+ mdev->ov_position = 0;
+ mdev->rs_total =
+ mdev->rs_mark_left = drbd_bm_bits(mdev);
+ if (mdev->agreed_pro_version >= 90)
+ set_ov_position(mdev, ns.conn);
+ else
+ mdev->ov_start_sector = 0;
+ mdev->ov_left = mdev->rs_total
+ - BM_SECT_TO_BIT(mdev->ov_position);
+ mdev->rs_start =
+ mdev->rs_mark_time = jiffies;
+ mdev->ov_last_oos_size = 0;
+ mdev->ov_last_oos_start = 0;
+
+ if (ns.conn == C_VERIFY_S) {
+ dev_info(DEV, "Starting Online Verify from sector %llu\n",
+ (unsigned long long)mdev->ov_position);
+ mod_timer(&mdev->resync_timer, jiffies);
+ }
+ }
+
+ if (get_ldev(mdev)) {
+ u32 mdf = mdev->ldev->md.flags & ~(MDF_CONSISTENT|MDF_PRIMARY_IND|
+ MDF_CONNECTED_IND|MDF_WAS_UP_TO_DATE|
+ MDF_PEER_OUT_DATED|MDF_CRASHED_PRIMARY);
+
+ if (test_bit(CRASHED_PRIMARY, &mdev->flags))
+ mdf |= MDF_CRASHED_PRIMARY;
+ if (mdev->state.role == R_PRIMARY ||
+ (mdev->state.pdsk < D_INCONSISTENT && mdev->state.peer == R_PRIMARY))
+ mdf |= MDF_PRIMARY_IND;
+ if (mdev->state.conn > C_WF_REPORT_PARAMS)
+ mdf |= MDF_CONNECTED_IND;
+ if (mdev->state.disk > D_INCONSISTENT)
+ mdf |= MDF_CONSISTENT;
+ if (mdev->state.disk > D_OUTDATED)
+ mdf |= MDF_WAS_UP_TO_DATE;
+ if (mdev->state.pdsk <= D_OUTDATED && mdev->state.pdsk >= D_INCONSISTENT)
+ mdf |= MDF_PEER_OUT_DATED;
+ if (mdf != mdev->ldev->md.flags) {
+ mdev->ldev->md.flags = mdf;
+ drbd_md_mark_dirty(mdev);
+ }
+ if (os.disk < D_CONSISTENT && ns.disk >= D_CONSISTENT)
+ drbd_set_ed_uuid(mdev, mdev->ldev->md.uuid[UI_CURRENT]);
+ put_ldev(mdev);
+ }
+
+ /* Peer was forced D_UP_TO_DATE & R_PRIMARY, consider to resync */
+ if (os.disk == D_INCONSISTENT && os.pdsk == D_INCONSISTENT &&
+ os.peer == R_SECONDARY && ns.peer == R_PRIMARY)
+ set_bit(CONSIDER_RESYNC, &mdev->flags);
+
+ /* Receiver should clean up itself */
+ if (os.conn != C_DISCONNECTING && ns.conn == C_DISCONNECTING)
+ drbd_thread_stop_nowait(&mdev->receiver);
+
+ /* Now the receiver finished cleaning up itself, it should die */
+ if (os.conn != C_STANDALONE && ns.conn == C_STANDALONE)
+ drbd_thread_stop_nowait(&mdev->receiver);
+
+ /* Upon network failure, we need to restart the receiver. */
+ if (os.conn > C_TEAR_DOWN &&
+ ns.conn <= C_TEAR_DOWN && ns.conn >= C_TIMEOUT)
+ drbd_thread_restart_nowait(&mdev->receiver);
+
+ ascw = kmalloc(sizeof(*ascw), GFP_ATOMIC);
+ if (ascw) {
+ ascw->os = os;
+ ascw->ns = ns;
+ ascw->flags = flags;
+ ascw->w.cb = w_after_state_ch;
+ ascw->done = done;
+ drbd_queue_work(&mdev->data.work, &ascw->w);
+ } else {
+ dev_warn(DEV, "Could not kmalloc an ascw\n");
+ }
+
+ return rv;
+}
+
+static int w_after_state_ch(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+{
+ struct after_state_chg_work *ascw =
+ container_of(w, struct after_state_chg_work, w);
+ after_state_ch(mdev, ascw->os, ascw->ns, ascw->flags);
+ if (ascw->flags & CS_WAIT_COMPLETE) {
+ D_ASSERT(ascw->done != NULL);
+ complete(ascw->done);
+ }
+ kfree(ascw);
+
+ return 1;
+}
+
+static void abw_start_sync(struct drbd_conf *mdev, int rv)
+{
+ if (rv) {
+ dev_err(DEV, "Writing the bitmap failed not starting resync.\n");
+ _drbd_request_state(mdev, NS(conn, C_CONNECTED), CS_VERBOSE);
+ return;
+ }
+
+ switch (mdev->state.conn) {
+ case C_STARTING_SYNC_T:
+ _drbd_request_state(mdev, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE);
+ break;
+ case C_STARTING_SYNC_S:
+ drbd_start_resync(mdev, C_SYNC_SOURCE);
+ break;
+ }
+}
+
+/**
+ * after_state_ch() - Perform after state change actions that may sleep
+ * @mdev: DRBD device.
+ * @os: old state.
+ * @ns: new state.
+ * @flags: Flags
+ */
+static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
+ union drbd_state ns, enum chg_state_flags flags)
+{
+ enum drbd_fencing_p fp;
+
+ if (os.conn != C_CONNECTED && ns.conn == C_CONNECTED) {
+ clear_bit(CRASHED_PRIMARY, &mdev->flags);
+ if (mdev->p_uuid)
+ mdev->p_uuid[UI_FLAGS] &= ~((u64)2);
+ }
+
+ fp = FP_DONT_CARE;
+ if (get_ldev(mdev)) {
+ fp = mdev->ldev->dc.fencing;
+ put_ldev(mdev);
+ }
+
+ /* Inform userspace about the change... */
+ drbd_bcast_state(mdev, ns);
+
+ if (!(os.role == R_PRIMARY && os.disk < D_UP_TO_DATE && os.pdsk < D_UP_TO_DATE) &&
+ (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE))
+ drbd_khelper(mdev, "pri-on-incon-degr");
+
+ /* Here we have the actions that are performed after a
+ state change. This function might sleep */
+
+ if (fp == FP_STONITH && ns.susp) {
+ /* case1: The outdate peer handler is successful:
+ * case2: The connection was established again: */
+ if ((os.pdsk > D_OUTDATED && ns.pdsk <= D_OUTDATED) ||
+ (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED)) {
+ tl_clear(mdev);
+ spin_lock_irq(&mdev->req_lock);
+ _drbd_set_state(_NS(mdev, susp, 0), CS_VERBOSE, NULL);
+ spin_unlock_irq(&mdev->req_lock);
+ }
+ }
+ /* Do not change the order of the if above and the two below... */
+ if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) { /* attach on the peer */
+ drbd_send_uuids(mdev);
+ drbd_send_state(mdev);
+ }
+ if (os.conn != C_WF_BITMAP_S && ns.conn == C_WF_BITMAP_S)
+ drbd_queue_bitmap_io(mdev, &drbd_send_bitmap, NULL, "send_bitmap (WFBitMapS)");
+
+ /* Lost contact to peer's copy of the data */
+ if ((os.pdsk >= D_INCONSISTENT &&
+ os.pdsk != D_UNKNOWN &&
+ os.pdsk != D_OUTDATED)
+ && (ns.pdsk < D_INCONSISTENT ||
+ ns.pdsk == D_UNKNOWN ||
+ ns.pdsk == D_OUTDATED)) {
+ kfree(mdev->p_uuid);
+ mdev->p_uuid = NULL;
+ if (get_ldev(mdev)) {
+ if ((ns.role == R_PRIMARY || ns.peer == R_PRIMARY) &&
+ mdev->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) {
+ drbd_uuid_new_current(mdev);
+ drbd_send_uuids(mdev);
+ }
+ put_ldev(mdev);
+ }
+ }
+
+ if (ns.pdsk < D_INCONSISTENT && get_ldev(mdev)) {
+ if (ns.peer == R_PRIMARY && mdev->ldev->md.uuid[UI_BITMAP] == 0)
+ drbd_uuid_new_current(mdev);
+
+ /* D_DISKLESS Peer becomes secondary */
+ if (os.peer == R_PRIMARY && ns.peer == R_SECONDARY)
+ drbd_al_to_on_disk_bm(mdev);
+ put_ldev(mdev);
+ }
+
+ /* Last part of the attaching process ... */
+ if (ns.conn >= C_CONNECTED &&
+ os.disk == D_ATTACHING && ns.disk == D_NEGOTIATING) {
+ kfree(mdev->p_uuid); /* We expect to receive up-to-date UUIDs soon. */
+ mdev->p_uuid = NULL; /* ...to not use the old ones in the mean time */
+ drbd_send_sizes(mdev, 0); /* to start sync... */
+ drbd_send_uuids(mdev);
+ drbd_send_state(mdev);
+ }
+
+ /* We want to pause/continue resync, tell peer. */
+ if (ns.conn >= C_CONNECTED &&
+ ((os.aftr_isp != ns.aftr_isp) ||
+ (os.user_isp != ns.user_isp)))
+ drbd_send_state(mdev);
+
+ /* In case one of the isp bits got set, suspend other devices. */
+ if ((!os.aftr_isp && !os.peer_isp && !os.user_isp) &&
+ (ns.aftr_isp || ns.peer_isp || ns.user_isp))
+ suspend_other_sg(mdev);
+
+ /* Make sure the peer gets informed about eventual state
+ changes (ISP bits) while we were in WFReportParams. */
+ if (os.conn == C_WF_REPORT_PARAMS && ns.conn >= C_CONNECTED)
+ drbd_send_state(mdev);
+
+ /* We are in the progress to start a full sync... */
+ if ((os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
+ (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S))
+ drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, &abw_start_sync, "set_n_write from StartingSync");
+
+ /* We are invalidating our self... */
+ if (os.conn < C_CONNECTED && ns.conn < C_CONNECTED &&
+ os.disk > D_INCONSISTENT && ns.disk == D_INCONSISTENT)
+ drbd_queue_bitmap_io(mdev, &drbd_bmio_set_n_write, NULL, "set_n_write from invalidate");
+
+ if (os.disk > D_FAILED && ns.disk == D_FAILED) {
+ enum drbd_io_error_p eh;
+
+ eh = EP_PASS_ON;
+ if (get_ldev_if_state(mdev, D_FAILED)) {
+ eh = mdev->ldev->dc.on_io_error;
+ put_ldev(mdev);
+ }
+
+ drbd_rs_cancel_all(mdev);
+ /* since get_ldev() only works as long as disk>=D_INCONSISTENT,
+ and it is D_DISKLESS here, local_cnt can only go down, it can
+ not increase... It will reach zero */
+ wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt));
+ mdev->rs_total = 0;
+ mdev->rs_failed = 0;
+ atomic_set(&mdev->rs_pending_cnt, 0);
+
+ spin_lock_irq(&mdev->req_lock);
+ _drbd_set_state(_NS(mdev, disk, D_DISKLESS), CS_HARD, NULL);
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (eh == EP_CALL_HELPER)
+ drbd_khelper(mdev, "local-io-error");
+ }
+
+ if (os.disk > D_DISKLESS && ns.disk == D_DISKLESS) {
+
+ if (os.disk == D_FAILED) /* && ns.disk == D_DISKLESS*/ {
+ if (drbd_send_state(mdev))
+ dev_warn(DEV, "Notified peer that my disk is broken.\n");
+ else
+ dev_err(DEV, "Sending state in drbd_io_error() failed\n");
+ }
+
+ lc_destroy(mdev->resync);
+ mdev->resync = NULL;
+ lc_destroy(mdev->act_log);
+ mdev->act_log = NULL;
+ __no_warn(local,
+ drbd_free_bc(mdev->ldev);
+ mdev->ldev = NULL;);
+
+ if (mdev->md_io_tmpp)
+ __free_page(mdev->md_io_tmpp);
+ }
+
+ /* Disks got bigger while they were detached */
+ if (ns.disk > D_NEGOTIATING && ns.pdsk > D_NEGOTIATING &&
+ test_and_clear_bit(RESYNC_AFTER_NEG, &mdev->flags)) {
+ if (ns.conn == C_CONNECTED)
+ resync_after_online_grow(mdev);
+ }
+
+ /* A resync finished or aborted, wake paused devices... */
+ if ((os.conn > C_CONNECTED && ns.conn <= C_CONNECTED) ||
+ (os.peer_isp && !ns.peer_isp) ||
+ (os.user_isp && !ns.user_isp))
+ resume_next_sg(mdev);
+
+ /* Upon network connection, we need to start the receiver */
+ if (os.conn == C_STANDALONE && ns.conn == C_UNCONNECTED)
+ drbd_thread_start(&mdev->receiver);
+
+ /* Terminate worker thread if we are unconfigured - it will be
+ restarted as needed... */
+ if (ns.disk == D_DISKLESS &&
+ ns.conn == C_STANDALONE &&
+ ns.role == R_SECONDARY) {
+ if (os.aftr_isp != ns.aftr_isp)
+ resume_next_sg(mdev);
+ /* set in __drbd_set_state, unless CONFIG_PENDING was set */
+ if (test_bit(DEVICE_DYING, &mdev->flags))
+ drbd_thread_stop_nowait(&mdev->worker);
+ }
+
+ drbd_md_sync(mdev);
+}
+
+
+static int drbd_thread_setup(void *arg)
+{
+ struct drbd_thread *thi = (struct drbd_thread *) arg;
+ struct drbd_conf *mdev = thi->mdev;
+ unsigned long flags;
+ int retval;
+
+restart:
+ retval = thi->function(thi);
+
+ spin_lock_irqsave(&thi->t_lock, flags);
+
+ /* if the receiver has been "Exiting", the last thing it did
+ * was set the conn state to "StandAlone",
+ * if now a re-connect request comes in, conn state goes C_UNCONNECTED,
+ * and receiver thread will be "started".
+ * drbd_thread_start needs to set "Restarting" in that case.
+ * t_state check and assignment needs to be within the same spinlock,
+ * so either thread_start sees Exiting, and can remap to Restarting,
+ * or thread_start see None, and can proceed as normal.
+ */
+
+ if (thi->t_state == Restarting) {
+ dev_info(DEV, "Restarting %s\n", current->comm);
+ thi->t_state = Running;
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+ goto restart;
+ }
+
+ thi->task = NULL;
+ thi->t_state = None;
+ smp_mb();
+ complete(&thi->stop);
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+
+ dev_info(DEV, "Terminating %s\n", current->comm);
+
+ /* Release mod reference taken when thread was started */
+ module_put(THIS_MODULE);
+ return retval;
+}
+
+static void drbd_thread_init(struct drbd_conf *mdev, struct drbd_thread *thi,
+ int (*func) (struct drbd_thread *))
+{
+ spin_lock_init(&thi->t_lock);
+ thi->task = NULL;
+ thi->t_state = None;
+ thi->function = func;
+ thi->mdev = mdev;
+}
+
+int drbd_thread_start(struct drbd_thread *thi)
+{
+ struct drbd_conf *mdev = thi->mdev;
+ struct task_struct *nt;
+ unsigned long flags;
+
+ const char *me =
+ thi == &mdev->receiver ? "receiver" :
+ thi == &mdev->asender ? "asender" :
+ thi == &mdev->worker ? "worker" : "NONSENSE";
+
+ /* is used from state engine doing drbd_thread_stop_nowait,
+ * while holding the req lock irqsave */
+ spin_lock_irqsave(&thi->t_lock, flags);
+
+ switch (thi->t_state) {
+ case None:
+ dev_info(DEV, "Starting %s thread (from %s [%d])\n",
+ me, current->comm, current->pid);
+
+ /* Get ref on module for thread - this is released when thread exits */
+ if (!try_module_get(THIS_MODULE)) {
+ dev_err(DEV, "Failed to get module reference in drbd_thread_start\n");
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+ return FALSE;
+ }
+
+ init_completion(&thi->stop);
+ D_ASSERT(thi->task == NULL);
+ thi->reset_cpu_mask = 1;
+ thi->t_state = Running;
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+ flush_signals(current); /* otherw. may get -ERESTARTNOINTR */
+
+ nt = kthread_create(drbd_thread_setup, (void *) thi,
+ "drbd%d_%s", mdev_to_minor(mdev), me);
+
+ if (IS_ERR(nt)) {
+ dev_err(DEV, "Couldn't start thread\n");
+
+ module_put(THIS_MODULE);
+ return FALSE;
+ }
+ spin_lock_irqsave(&thi->t_lock, flags);
+ thi->task = nt;
+ thi->t_state = Running;
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+ wake_up_process(nt);
+ break;
+ case Exiting:
+ thi->t_state = Restarting;
+ dev_info(DEV, "Restarting %s thread (from %s [%d])\n",
+ me, current->comm, current->pid);
+ /* fall through */
+ case Running:
+ case Restarting:
+ default:
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+ break;
+ }
+
+ return TRUE;
+}
+
+
+void _drbd_thread_stop(struct drbd_thread *thi, int restart, int wait)
+{
+ unsigned long flags;
+
+ enum drbd_thread_state ns = restart ? Restarting : Exiting;
+
+ /* may be called from state engine, holding the req lock irqsave */
+ spin_lock_irqsave(&thi->t_lock, flags);
+
+ if (thi->t_state == None) {
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+ if (restart)
+ drbd_thread_start(thi);
+ return;
+ }
+
+ if (thi->t_state != ns) {
+ if (thi->task == NULL) {
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+ return;
+ }
+
+ thi->t_state = ns;
+ smp_mb();
+ init_completion(&thi->stop);
+ if (thi->task != current)
+ force_sig(DRBD_SIGKILL, thi->task);
+
+ }
+
+ spin_unlock_irqrestore(&thi->t_lock, flags);
+
+ if (wait)
+ wait_for_completion(&thi->stop);
+}
+
+#ifdef CONFIG_SMP
+/**
+ * drbd_calc_cpu_mask() - Generate CPU masks, spread over all CPUs
+ * @mdev: DRBD device.
+ *
+ * Forces all threads of a device onto the same CPU. This is beneficial for
+ * DRBD's performance. May be overwritten by user's configuration.
+ */
+void drbd_calc_cpu_mask(struct drbd_conf *mdev)
+{
+ int ord, cpu;
+
+ /* user override. */
+ if (cpumask_weight(mdev->cpu_mask))
+ return;
+
+ ord = mdev_to_minor(mdev) % cpumask_weight(cpu_online_mask);
+ for_each_online_cpu(cpu) {
+ if (ord-- == 0) {
+ cpumask_set_cpu(cpu, mdev->cpu_mask);
+ return;
+ }
+ }
+ /* should not be reached */
+ cpumask_setall(mdev->cpu_mask);
+}
+
+/**
+ * drbd_thread_current_set_cpu() - modifies the cpu mask of the _current_ thread
+ * @mdev: DRBD device.
+ *
+ * call in the "main loop" of _all_ threads, no need for any mutex, current won't die
+ * prematurely.
+ */
+void drbd_thread_current_set_cpu(struct drbd_conf *mdev)
+{
+ struct task_struct *p = current;
+ struct drbd_thread *thi =
+ p == mdev->asender.task ? &mdev->asender :
+ p == mdev->receiver.task ? &mdev->receiver :
+ p == mdev->worker.task ? &mdev->worker :
+ NULL;
+ ERR_IF(thi == NULL)
+ return;
+ if (!thi->reset_cpu_mask)
+ return;
+ thi->reset_cpu_mask = 0;
+ set_cpus_allowed_ptr(p, mdev->cpu_mask);
+}
+#endif
+
+/* the appropriate socket mutex must be held already */
+int _drbd_send_cmd(struct drbd_conf *mdev, struct socket *sock,
+ enum drbd_packets cmd, struct p_header *h,
+ size_t size, unsigned msg_flags)
+{
+ int sent, ok;
+
+ ERR_IF(!h) return FALSE;
+ ERR_IF(!size) return FALSE;
+
+ h->magic = BE_DRBD_MAGIC;
+ h->command = cpu_to_be16(cmd);
+ h->length = cpu_to_be16(size-sizeof(struct p_header));
+
+ sent = drbd_send(mdev, sock, h, size, msg_flags);
+
+ ok = (sent == size);
+ if (!ok)
+ dev_err(DEV, "short sent %s size=%d sent=%d\n",
+ cmdname(cmd), (int)size, sent);
+ return ok;
+}
+
+/* don't pass the socket. we may only look at it
+ * when we hold the appropriate socket mutex.
+ */
+int drbd_send_cmd(struct drbd_conf *mdev, int use_data_socket,
+ enum drbd_packets cmd, struct p_header *h, size_t size)
+{
+ int ok = 0;
+ struct socket *sock;
+
+ if (use_data_socket) {
+ mutex_lock(&mdev->data.mutex);
+ sock = mdev->data.socket;
+ } else {
+ mutex_lock(&mdev->meta.mutex);
+ sock = mdev->meta.socket;
+ }
+
+ /* drbd_disconnect() could have called drbd_free_sock()
+ * while we were waiting in down()... */
+ if (likely(sock != NULL))
+ ok = _drbd_send_cmd(mdev, sock, cmd, h, size, 0);
+
+ if (use_data_socket)
+ mutex_unlock(&mdev->data.mutex);
+ else
+ mutex_unlock(&mdev->meta.mutex);
+ return ok;
+}
+
+int drbd_send_cmd2(struct drbd_conf *mdev, enum drbd_packets cmd, char *data,
+ size_t size)
+{
+ struct p_header h;
+ int ok;
+
+ h.magic = BE_DRBD_MAGIC;
+ h.command = cpu_to_be16(cmd);
+ h.length = cpu_to_be16(size);
+
+ if (!drbd_get_data_sock(mdev))
+ return 0;
+
+ ok = (sizeof(h) ==
+ drbd_send(mdev, mdev->data.socket, &h, sizeof(h), 0));
+ ok = ok && (size ==
+ drbd_send(mdev, mdev->data.socket, data, size, 0));
+
+ drbd_put_data_sock(mdev);
+
+ return ok;
+}
+
+int drbd_send_sync_param(struct drbd_conf *mdev, struct syncer_conf *sc)
+{
+ struct p_rs_param_89 *p;
+ struct socket *sock;
+ int size, rv;
+ const int apv = mdev->agreed_pro_version;
+
+ size = apv <= 87 ? sizeof(struct p_rs_param)
+ : apv == 88 ? sizeof(struct p_rs_param)
+ + strlen(mdev->sync_conf.verify_alg) + 1
+ : /* 89 */ sizeof(struct p_rs_param_89);
+
+ /* used from admin command context and receiver/worker context.
+ * to avoid kmalloc, grab the socket right here,
+ * then use the pre-allocated sbuf there */
+ mutex_lock(&mdev->data.mutex);
+ sock = mdev->data.socket;
+
+ if (likely(sock != NULL)) {
+ enum drbd_packets cmd = apv >= 89 ? P_SYNC_PARAM89 : P_SYNC_PARAM;
+
+ p = &mdev->data.sbuf.rs_param_89;
+
+ /* initialize verify_alg and csums_alg */
+ memset(p->verify_alg, 0, 2 * SHARED_SECRET_MAX);
+
+ p->rate = cpu_to_be32(sc->rate);
+
+ if (apv >= 88)
+ strcpy(p->verify_alg, mdev->sync_conf.verify_alg);
+ if (apv >= 89)
+ strcpy(p->csums_alg, mdev->sync_conf.csums_alg);
+
+ rv = _drbd_send_cmd(mdev, sock, cmd, &p->head, size, 0);
+ } else
+ rv = 0; /* not ok */
+
+ mutex_unlock(&mdev->data.mutex);
+
+ return rv;
+}
+
+int drbd_send_protocol(struct drbd_conf *mdev)
+{
+ struct p_protocol *p;
+ int size, rv;
+
+ size = sizeof(struct p_protocol);
+
+ if (mdev->agreed_pro_version >= 87)
+ size += strlen(mdev->net_conf->integrity_alg) + 1;
+
+ /* we must not recurse into our own queue,
+ * as that is blocked during handshake */
+ p = kmalloc(size, GFP_NOIO);
+ if (p == NULL)
+ return 0;
+
+ p->protocol = cpu_to_be32(mdev->net_conf->wire_protocol);
+ p->after_sb_0p = cpu_to_be32(mdev->net_conf->after_sb_0p);
+ p->after_sb_1p = cpu_to_be32(mdev->net_conf->after_sb_1p);
+ p->after_sb_2p = cpu_to_be32(mdev->net_conf->after_sb_2p);
+ p->want_lose = cpu_to_be32(mdev->net_conf->want_lose);
+ p->two_primaries = cpu_to_be32(mdev->net_conf->two_primaries);
+
+ if (mdev->agreed_pro_version >= 87)
+ strcpy(p->integrity_alg, mdev->net_conf->integrity_alg);
+
+ rv = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_PROTOCOL,
+ (struct p_header *)p, size);
+ kfree(p);
+ return rv;
+}
+
+int _drbd_send_uuids(struct drbd_conf *mdev, u64 uuid_flags)
+{
+ struct p_uuids p;
+ int i;
+
+ if (!get_ldev_if_state(mdev, D_NEGOTIATING))
+ return 1;
+
+ for (i = UI_CURRENT; i < UI_SIZE; i++)
+ p.uuid[i] = mdev->ldev ? cpu_to_be64(mdev->ldev->md.uuid[i]) : 0;
+
+ mdev->comm_bm_set = drbd_bm_total_weight(mdev);
+ p.uuid[UI_SIZE] = cpu_to_be64(mdev->comm_bm_set);
+ uuid_flags |= mdev->net_conf->want_lose ? 1 : 0;
+ uuid_flags |= test_bit(CRASHED_PRIMARY, &mdev->flags) ? 2 : 0;
+ uuid_flags |= mdev->new_state_tmp.disk == D_INCONSISTENT ? 4 : 0;
+ p.uuid[UI_FLAGS] = cpu_to_be64(uuid_flags);
+
+ put_ldev(mdev);
+
+ return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_UUIDS,
+ (struct p_header *)&p, sizeof(p));
+}
+
+int drbd_send_uuids(struct drbd_conf *mdev)
+{
+ return _drbd_send_uuids(mdev, 0);
+}
+
+int drbd_send_uuids_skip_initial_sync(struct drbd_conf *mdev)
+{
+ return _drbd_send_uuids(mdev, 8);
+}
+
+
+int drbd_send_sync_uuid(struct drbd_conf *mdev, u64 val)
+{
+ struct p_rs_uuid p;
+
+ p.uuid = cpu_to_be64(val);
+
+ return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_SYNC_UUID,
+ (struct p_header *)&p, sizeof(p));
+}
+
+int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply)
+{
+ struct p_sizes p;
+ sector_t d_size, u_size;
+ int q_order_type;
+ int ok;
+
+ if (get_ldev_if_state(mdev, D_NEGOTIATING)) {
+ D_ASSERT(mdev->ldev->backing_bdev);
+ d_size = drbd_get_max_capacity(mdev->ldev);
+ u_size = mdev->ldev->dc.disk_size;
+ q_order_type = drbd_queue_order_type(mdev);
+ p.queue_order_type = cpu_to_be32(drbd_queue_order_type(mdev));
+ put_ldev(mdev);
+ } else {
+ d_size = 0;
+ u_size = 0;
+ q_order_type = QUEUE_ORDERED_NONE;
+ }
+
+ p.d_size = cpu_to_be64(d_size);
+ p.u_size = cpu_to_be64(u_size);
+ p.c_size = cpu_to_be64(trigger_reply ? 0 : drbd_get_capacity(mdev->this_bdev));
+ p.max_segment_size = cpu_to_be32(queue_max_segment_size(mdev->rq_queue));
+ p.queue_order_type = cpu_to_be32(q_order_type);
+
+ ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_SIZES,
+ (struct p_header *)&p, sizeof(p));
+ return ok;
+}
+
+/**
+ * drbd_send_state() - Sends the drbd state to the peer
+ * @mdev: DRBD device.
+ */
+int drbd_send_state(struct drbd_conf *mdev)
+{
+ struct socket *sock;
+ struct p_state p;
+ int ok = 0;
+
+ /* Grab state lock so we wont send state if we're in the middle
+ * of a cluster wide state change on another thread */
+ drbd_state_lock(mdev);
+
+ mutex_lock(&mdev->data.mutex);
+
+ p.state = cpu_to_be32(mdev->state.i); /* Within the send mutex */
+ sock = mdev->data.socket;
+
+ if (likely(sock != NULL)) {
+ ok = _drbd_send_cmd(mdev, sock, P_STATE,
+ (struct p_header *)&p, sizeof(p), 0);
+ }
+
+ mutex_unlock(&mdev->data.mutex);
+
+ drbd_state_unlock(mdev);
+ return ok;
+}
+
+int drbd_send_state_req(struct drbd_conf *mdev,
+ union drbd_state mask, union drbd_state val)
+{
+ struct p_req_state p;
+
+ p.mask = cpu_to_be32(mask.i);
+ p.val = cpu_to_be32(val.i);
+
+ return drbd_send_cmd(mdev, USE_DATA_SOCKET, P_STATE_CHG_REQ,
+ (struct p_header *)&p, sizeof(p));
+}
+
+int drbd_send_sr_reply(struct drbd_conf *mdev, int retcode)
+{
+ struct p_req_state_reply p;
+
+ p.retcode = cpu_to_be32(retcode);
+
+ return drbd_send_cmd(mdev, USE_META_SOCKET, P_STATE_CHG_REPLY,
+ (struct p_header *)&p, sizeof(p));
+}
+
+int fill_bitmap_rle_bits(struct drbd_conf *mdev,
+ struct p_compressed_bm *p,
+ struct bm_xfer_ctx *c)
+{
+ struct bitstream bs;
+ unsigned long plain_bits;
+ unsigned long tmp;
+ unsigned long rl;
+ unsigned len;
+ unsigned toggle;
+ int bits;
+
+ /* may we use this feature? */
+ if ((mdev->sync_conf.use_rle == 0) ||
+ (mdev->agreed_pro_version < 90))
+ return 0;
+
+ if (c->bit_offset >= c->bm_bits)
+ return 0; /* nothing to do. */
+
+ /* use at most thus many bytes */
+ bitstream_init(&bs, p->code, BM_PACKET_VLI_BYTES_MAX, 0);
+ memset(p->code, 0, BM_PACKET_VLI_BYTES_MAX);
+ /* plain bits covered in this code string */
+ plain_bits = 0;
+
+ /* p->encoding & 0x80 stores whether the first run length is set.
+ * bit offset is implicit.
+ * start with toggle == 2 to be able to tell the first iteration */
+ toggle = 2;
+
+ /* see how much plain bits we can stuff into one packet
+ * using RLE and VLI. */
+ do {
+ tmp = (toggle == 0) ? _drbd_bm_find_next_zero(mdev, c->bit_offset)
+ : _drbd_bm_find_next(mdev, c->bit_offset);
+ if (tmp == -1UL)
+ tmp = c->bm_bits;
+ rl = tmp - c->bit_offset;
+
+ if (toggle == 2) { /* first iteration */
+ if (rl == 0) {
+ /* the first checked bit was set,
+ * store start value, */
+ DCBP_set_start(p, 1);
+ /* but skip encoding of zero run length */
+ toggle = !toggle;
+ continue;
+ }
+ DCBP_set_start(p, 0);
+ }
+
+ /* paranoia: catch zero runlength.
+ * can only happen if bitmap is modified while we scan it. */
+ if (rl == 0) {
+ dev_err(DEV, "unexpected zero runlength while encoding bitmap "
+ "t:%u bo:%lu\n", toggle, c->bit_offset);
+ return -1;
+ }
+
+ bits = vli_encode_bits(&bs, rl);
+ if (bits == -ENOBUFS) /* buffer full */
+ break;
+ if (bits <= 0) {
+ dev_err(DEV, "error while encoding bitmap: %d\n", bits);
+ return 0;
+ }
+
+ toggle = !toggle;
+ plain_bits += rl;
+ c->bit_offset = tmp;
+ } while (c->bit_offset < c->bm_bits);
+
+ len = bs.cur.b - p->code + !!bs.cur.bit;
+
+ if (plain_bits < (len << 3)) {
+ /* incompressible with this method.
+ * we need to rewind both word and bit position. */
+ c->bit_offset -= plain_bits;
+ bm_xfer_ctx_bit_to_word_offset(c);
+ c->bit_offset = c->word_offset * BITS_PER_LONG;
+ return 0;
+ }
+
+ /* RLE + VLI was able to compress it just fine.
+ * update c->word_offset. */
+ bm_xfer_ctx_bit_to_word_offset(c);
+
+ /* store pad_bits */
+ DCBP_set_pad_bits(p, (8 - bs.cur.bit) & 0x7);
+
+ return len;
+}
+
+enum { OK, FAILED, DONE }
+send_bitmap_rle_or_plain(struct drbd_conf *mdev,
+ struct p_header *h, struct bm_xfer_ctx *c)
+{
+ struct p_compressed_bm *p = (void*)h;
+ unsigned long num_words;
+ int len;
+ int ok;
+
+ len = fill_bitmap_rle_bits(mdev, p, c);
+
+ if (len < 0)
+ return FAILED;
+
+ if (len) {
+ DCBP_set_code(p, RLE_VLI_Bits);
+ ok = _drbd_send_cmd(mdev, mdev->data.socket, P_COMPRESSED_BITMAP, h,
+ sizeof(*p) + len, 0);
+
+ c->packets[0]++;
+ c->bytes[0] += sizeof(*p) + len;
+
+ if (c->bit_offset >= c->bm_bits)
+ len = 0; /* DONE */
+ } else {
+ /* was not compressible.
+ * send a buffer full of plain text bits instead. */
+ num_words = min_t(size_t, BM_PACKET_WORDS, c->bm_words - c->word_offset);
+ len = num_words * sizeof(long);
+ if (len)
+ drbd_bm_get_lel(mdev, c->word_offset, num_words, (unsigned long*)h->payload);
+ ok = _drbd_send_cmd(mdev, mdev->data.socket, P_BITMAP,
+ h, sizeof(struct p_header) + len, 0);
+ c->word_offset += num_words;
+ c->bit_offset = c->word_offset * BITS_PER_LONG;
+
+ c->packets[1]++;
+ c->bytes[1] += sizeof(struct p_header) + len;
+
+ if (c->bit_offset > c->bm_bits)
+ c->bit_offset = c->bm_bits;
+ }
+ ok = ok ? ((len == 0) ? DONE : OK) : FAILED;
+
+ if (ok == DONE)
+ INFO_bm_xfer_stats(mdev, "send", c);
+ return ok;
+}
+
+/* See the comment at receive_bitmap() */
+int _drbd_send_bitmap(struct drbd_conf *mdev)
+{
+ struct bm_xfer_ctx c;
+ struct p_header *p;
+ int ret;
+
+ ERR_IF(!mdev->bitmap) return FALSE;
+
+ /* maybe we should use some per thread scratch page,
+ * and allocate that during initial device creation? */
+ p = (struct p_header *) __get_free_page(GFP_NOIO);
+ if (!p) {
+ dev_err(DEV, "failed to allocate one page buffer in %s\n", __func__);
+ return FALSE;
+ }
+
+ if (get_ldev(mdev)) {
+ if (drbd_md_test_flag(mdev->ldev, MDF_FULL_SYNC)) {
+ dev_info(DEV, "Writing the whole bitmap, MDF_FullSync was set.\n");
+ drbd_bm_set_all(mdev);
+ if (drbd_bm_write(mdev)) {
+ /* write_bm did fail! Leave full sync flag set in Meta P_DATA
+ * but otherwise process as per normal - need to tell other
+ * side that a full resync is required! */
+ dev_err(DEV, "Failed to write bitmap to disk!\n");
+ } else {
+ drbd_md_clear_flag(mdev, MDF_FULL_SYNC);
+ drbd_md_sync(mdev);
+ }
+ }
+ put_ldev(mdev);
+ }
+
+ c = (struct bm_xfer_ctx) {
+ .bm_bits = drbd_bm_bits(mdev),
+ .bm_words = drbd_bm_words(mdev),
+ };
+
+ do {
+ ret = send_bitmap_rle_or_plain(mdev, p, &c);
+ } while (ret == OK);
+
+ free_page((unsigned long) p);
+ return (ret == DONE);
+}
+
+int drbd_send_bitmap(struct drbd_conf *mdev)
+{
+ int err;
+
+ if (!drbd_get_data_sock(mdev))
+ return -1;
+ err = !_drbd_send_bitmap(mdev);
+ drbd_put_data_sock(mdev);
+ return err;
+}
+
+int drbd_send_b_ack(struct drbd_conf *mdev, u32 barrier_nr, u32 set_size)
+{
+ int ok;
+ struct p_barrier_ack p;
+
+ p.barrier = barrier_nr;
+ p.set_size = cpu_to_be32(set_size);
+
+ if (mdev->state.conn < C_CONNECTED)
+ return FALSE;
+ ok = drbd_send_cmd(mdev, USE_META_SOCKET, P_BARRIER_ACK,
+ (struct p_header *)&p, sizeof(p));
+ return ok;
+}
+
+/**
+ * _drbd_send_ack() - Sends an ack packet
+ * @mdev: DRBD device.
+ * @cmd: Packet command code.
+ * @sector: sector, needs to be in big endian byte order
+ * @blksize: size in byte, needs to be in big endian byte order
+ * @block_id: Id, big endian byte order
+ */
+static int _drbd_send_ack(struct drbd_conf *mdev, enum drbd_packets cmd,
+ u64 sector,
+ u32 blksize,
+ u64 block_id)
+{
+ int ok;
+ struct p_block_ack p;
+
+ p.sector = sector;
+ p.block_id = block_id;
+ p.blksize = blksize;
+ p.seq_num = cpu_to_be32(atomic_add_return(1, &mdev->packet_seq));
+
+ if (!mdev->meta.socket || mdev->state.conn < C_CONNECTED)
+ return FALSE;
+ ok = drbd_send_cmd(mdev, USE_META_SOCKET, cmd,
+ (struct p_header *)&p, sizeof(p));
+ return ok;
+}
+
+int drbd_send_ack_dp(struct drbd_conf *mdev, enum drbd_packets cmd,
+ struct p_data *dp)
+{
+ const int header_size = sizeof(struct p_data)
+ - sizeof(struct p_header);
+ int data_size = ((struct p_header *)dp)->length - header_size;
+
+ return _drbd_send_ack(mdev, cmd, dp->sector, cpu_to_be32(data_size),
+ dp->block_id);
+}
+
+int drbd_send_ack_rp(struct drbd_conf *mdev, enum drbd_packets cmd,
+ struct p_block_req *rp)
+{
+ return _drbd_send_ack(mdev, cmd, rp->sector, rp->blksize, rp->block_id);
+}
+
+/**
+ * drbd_send_ack() - Sends an ack packet
+ * @mdev: DRBD device.
+ * @cmd: Packet command code.
+ * @e: Epoch entry.
+ */
+int drbd_send_ack(struct drbd_conf *mdev,
+ enum drbd_packets cmd, struct drbd_epoch_entry *e)
+{
+ return _drbd_send_ack(mdev, cmd,
+ cpu_to_be64(e->sector),
+ cpu_to_be32(e->size),
+ e->block_id);
+}
+
+/* This function misuses the block_id field to signal if the blocks
+ * are is sync or not. */
+int drbd_send_ack_ex(struct drbd_conf *mdev, enum drbd_packets cmd,
+ sector_t sector, int blksize, u64 block_id)
+{
+ return _drbd_send_ack(mdev, cmd,
+ cpu_to_be64(sector),
+ cpu_to_be32(blksize),
+ cpu_to_be64(block_id));
+}
+
+int drbd_send_drequest(struct drbd_conf *mdev, int cmd,
+ sector_t sector, int size, u64 block_id)
+{
+ int ok;
+ struct p_block_req p;
+
+ p.sector = cpu_to_be64(sector);
+ p.block_id = block_id;
+ p.blksize = cpu_to_be32(size);
+
+ ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, cmd,
+ (struct p_header *)&p, sizeof(p));
+ return ok;
+}
+
+int drbd_send_drequest_csum(struct drbd_conf *mdev,
+ sector_t sector, int size,
+ void *digest, int digest_size,
+ enum drbd_packets cmd)
+{
+ int ok;
+ struct p_block_req p;
+
+ p.sector = cpu_to_be64(sector);
+ p.block_id = BE_DRBD_MAGIC + 0xbeef;
+ p.blksize = cpu_to_be32(size);
+
+ p.head.magic = BE_DRBD_MAGIC;
+ p.head.command = cpu_to_be16(cmd);
+ p.head.length = cpu_to_be16(sizeof(p) - sizeof(struct p_header) + digest_size);
+
+ mutex_lock(&mdev->data.mutex);
+
+ ok = (sizeof(p) == drbd_send(mdev, mdev->data.socket, &p, sizeof(p), 0));
+ ok = ok && (digest_size == drbd_send(mdev, mdev->data.socket, digest, digest_size, 0));
+
+ mutex_unlock(&mdev->data.mutex);
+
+ return ok;
+}
+
+int drbd_send_ov_request(struct drbd_conf *mdev, sector_t sector, int size)
+{
+ int ok;
+ struct p_block_req p;
+
+ p.sector = cpu_to_be64(sector);
+ p.block_id = BE_DRBD_MAGIC + 0xbabe;
+ p.blksize = cpu_to_be32(size);
+
+ ok = drbd_send_cmd(mdev, USE_DATA_SOCKET, P_OV_REQUEST,
+ (struct p_header *)&p, sizeof(p));
+ return ok;
+}
+
+/* called on sndtimeo
+ * returns FALSE if we should retry,
+ * TRUE if we think connection is dead
+ */
+static int we_should_drop_the_connection(struct drbd_conf *mdev, struct socket *sock)
+{
+ int drop_it;
+ /* long elapsed = (long)(jiffies - mdev->last_received); */
+
+ drop_it = mdev->meta.socket == sock
+ || !mdev->asender.task
+ || get_t_state(&mdev->asender) != Running
+ || mdev->state.conn < C_CONNECTED;
+
+ if (drop_it)
+ return TRUE;
+
+ drop_it = !--mdev->ko_count;
+ if (!drop_it) {
+ dev_err(DEV, "[%s/%d] sock_sendmsg time expired, ko = %u\n",
+ current->comm, current->pid, mdev->ko_count);
+ request_ping(mdev);
+ }
+
+ return drop_it; /* && (mdev->state == R_PRIMARY) */;
+}
+
+/* The idea of sendpage seems to be to put some kind of reference
+ * to the page into the skb, and to hand it over to the NIC. In
+ * this process get_page() gets called.
+ *
+ * As soon as the page was really sent over the network put_page()
+ * gets called by some part of the network layer. [ NIC driver? ]
+ *
+ * [ get_page() / put_page() increment/decrement the count. If count
+ * reaches 0 the page will be freed. ]
+ *
+ * This works nicely with pages from FSs.
+ * But this means that in protocol A we might signal IO completion too early!
+ *
+ * In order not to corrupt data during a resync we must make sure
+ * that we do not reuse our own buffer pages (EEs) to early, therefore
+ * we have the net_ee list.
+ *
+ * XFS seems to have problems, still, it submits pages with page_count == 0!
+ * As a workaround, we disable sendpage on pages
+ * with page_count == 0 or PageSlab.
+ */
+static int _drbd_no_send_page(struct drbd_conf *mdev, struct page *page,
+ int offset, size_t size)
+{
+ int sent = drbd_send(mdev, mdev->data.socket, kmap(page) + offset, size, 0);
+ kunmap(page);
+ if (sent == size)
+ mdev->send_cnt += size>>9;
+ return sent == size;
+}
+
+static int _drbd_send_page(struct drbd_conf *mdev, struct page *page,
+ int offset, size_t size)
+{
+ mm_segment_t oldfs = get_fs();
+ int sent, ok;
+ int len = size;
+
+ /* e.g. XFS meta- & log-data is in slab pages, which have a
+ * page_count of 0 and/or have PageSlab() set.
+ * we cannot use send_page for those, as that does get_page();
+ * put_page(); and would cause either a VM_BUG directly, or
+ * __page_cache_release a page that would actually still be referenced
+ * by someone, leading to some obscure delayed Oops somewhere else. */
+ if (disable_sendpage || (page_count(page) < 1) || PageSlab(page))
+ return _drbd_no_send_page(mdev, page, offset, size);
+
+ drbd_update_congested(mdev);
+ set_fs(KERNEL_DS);
+ do {
+ sent = mdev->data.socket->ops->sendpage(mdev->data.socket, page,
+ offset, len,
+ MSG_NOSIGNAL);
+ if (sent == -EAGAIN) {
+ if (we_should_drop_the_connection(mdev,
+ mdev->data.socket))
+ break;
+ else
+ continue;
+ }
+ if (sent <= 0) {
+ dev_warn(DEV, "%s: size=%d len=%d sent=%d\n",
+ __func__, (int)size, len, sent);
+ break;
+ }
+ len -= sent;
+ offset += sent;
+ } while (len > 0 /* THINK && mdev->cstate >= C_CONNECTED*/);
+ set_fs(oldfs);
+ clear_bit(NET_CONGESTED, &mdev->flags);
+
+ ok = (len == 0);
+ if (likely(ok))
+ mdev->send_cnt += size>>9;
+ return ok;
+}
+
+static int _drbd_send_bio(struct drbd_conf *mdev, struct bio *bio)
+{
+ struct bio_vec *bvec;
+ int i;
+ __bio_for_each_segment(bvec, bio, i, 0) {
+ if (!_drbd_no_send_page(mdev, bvec->bv_page,
+ bvec->bv_offset, bvec->bv_len))
+ return 0;
+ }
+ return 1;
+}
+
+static int _drbd_send_zc_bio(struct drbd_conf *mdev, struct bio *bio)
+{
+ struct bio_vec *bvec;
+ int i;
+ __bio_for_each_segment(bvec, bio, i, 0) {
+ if (!_drbd_send_page(mdev, bvec->bv_page,
+ bvec->bv_offset, bvec->bv_len))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Used to send write requests
+ * R_PRIMARY -> Peer (P_DATA)
+ */
+int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
+{
+ int ok = 1;
+ struct p_data p;
+ unsigned int dp_flags = 0;
+ void *dgb;
+ int dgs;
+
+ if (!drbd_get_data_sock(mdev))
+ return 0;
+
+ dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_w_tfm) ?
+ crypto_hash_digestsize(mdev->integrity_w_tfm) : 0;
+
+ p.head.magic = BE_DRBD_MAGIC;
+ p.head.command = cpu_to_be16(P_DATA);
+ p.head.length =
+ cpu_to_be16(sizeof(p) - sizeof(struct p_header) + dgs + req->size);
+
+ p.sector = cpu_to_be64(req->sector);
+ p.block_id = (unsigned long)req;
+ p.seq_num = cpu_to_be32(req->seq_num =
+ atomic_add_return(1, &mdev->packet_seq));
+ dp_flags = 0;
+
+ /* NOTE: no need to check if barriers supported here as we would
+ * not pass the test in make_request_common in that case
+ */
+ if (bio_rw_flagged(req->master_bio, BIO_RW_BARRIER)) {
+ dev_err(DEV, "ASSERT FAILED would have set DP_HARDBARRIER\n");
+ /* dp_flags |= DP_HARDBARRIER; */
+ }
+ if (bio_rw_flagged(req->master_bio, BIO_RW_SYNCIO))
+ dp_flags |= DP_RW_SYNC;
+ /* for now handle SYNCIO and UNPLUG
+ * as if they still were one and the same flag */
+ if (bio_rw_flagged(req->master_bio, BIO_RW_UNPLUG))
+ dp_flags |= DP_RW_SYNC;
+ if (mdev->state.conn >= C_SYNC_SOURCE &&
+ mdev->state.conn <= C_PAUSED_SYNC_T)
+ dp_flags |= DP_MAY_SET_IN_SYNC;
+
+ p.dp_flags = cpu_to_be32(dp_flags);
+ set_bit(UNPLUG_REMOTE, &mdev->flags);
+ ok = (sizeof(p) ==
+ drbd_send(mdev, mdev->data.socket, &p, sizeof(p), MSG_MORE));
+ if (ok && dgs) {
+ dgb = mdev->int_dig_out;
+ drbd_csum(mdev, mdev->integrity_w_tfm, req->master_bio, dgb);
+ ok = drbd_send(mdev, mdev->data.socket, dgb, dgs, MSG_MORE);
+ }
+ if (ok) {
+ if (mdev->net_conf->wire_protocol == DRBD_PROT_A)
+ ok = _drbd_send_bio(mdev, req->master_bio);
+ else
+ ok = _drbd_send_zc_bio(mdev, req->master_bio);
+ }
+
+ drbd_put_data_sock(mdev);
+ return ok;
+}
+
+/* answer packet, used to send data back for read requests:
+ * Peer -> (diskless) R_PRIMARY (P_DATA_REPLY)
+ * C_SYNC_SOURCE -> C_SYNC_TARGET (P_RS_DATA_REPLY)
+ */
+int drbd_send_block(struct drbd_conf *mdev, enum drbd_packets cmd,
+ struct drbd_epoch_entry *e)
+{
+ int ok;
+ struct p_data p;
+ void *dgb;
+ int dgs;
+
+ dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_w_tfm) ?
+ crypto_hash_digestsize(mdev->integrity_w_tfm) : 0;
+
+ p.head.magic = BE_DRBD_MAGIC;
+ p.head.command = cpu_to_be16(cmd);
+ p.head.length =
+ cpu_to_be16(sizeof(p) - sizeof(struct p_header) + dgs + e->size);
+
+ p.sector = cpu_to_be64(e->sector);
+ p.block_id = e->block_id;
+ /* p.seq_num = 0; No sequence numbers here.. */
+
+ /* Only called by our kernel thread.
+ * This one may be interrupted by DRBD_SIG and/or DRBD_SIGKILL
+ * in response to admin command or module unload.
+ */
+ if (!drbd_get_data_sock(mdev))
+ return 0;
+
+ ok = sizeof(p) == drbd_send(mdev, mdev->data.socket, &p,
+ sizeof(p), MSG_MORE);
+ if (ok && dgs) {
+ dgb = mdev->int_dig_out;
+ drbd_csum(mdev, mdev->integrity_w_tfm, e->private_bio, dgb);
+ ok = drbd_send(mdev, mdev->data.socket, dgb, dgs, MSG_MORE);
+ }
+ if (ok)
+ ok = _drbd_send_zc_bio(mdev, e->private_bio);
+
+ drbd_put_data_sock(mdev);
+ return ok;
+}
+
+/*
+ drbd_send distinguishes two cases:
+
+ Packets sent via the data socket "sock"
+ and packets sent via the meta data socket "msock"
+
+ sock msock
+ -----------------+-------------------------+------------------------------
+ timeout conf.timeout / 2 conf.timeout / 2
+ timeout action send a ping via msock Abort communication
+ and close all sockets
+*/
+
+/*
+ * you must have down()ed the appropriate [m]sock_mutex elsewhere!
+ */
+int drbd_send(struct drbd_conf *mdev, struct socket *sock,
+ void *buf, size_t size, unsigned msg_flags)
+{
+ struct kvec iov;
+ struct msghdr msg;
+ int rv, sent = 0;
+
+ if (!sock)
+ return -1000;
+
+ /* THINK if (signal_pending) return ... ? */
+
+ iov.iov_base = buf;
+ iov.iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = msg_flags | MSG_NOSIGNAL;
+
+ if (sock == mdev->data.socket) {
+ mdev->ko_count = mdev->net_conf->ko_count;
+ drbd_update_congested(mdev);
+ }
+ do {
+ /* STRANGE
+ * tcp_sendmsg does _not_ use its size parameter at all ?
+ *
+ * -EAGAIN on timeout, -EINTR on signal.
+ */
+/* THINK
+ * do we need to block DRBD_SIG if sock == &meta.socket ??
+ * otherwise wake_asender() might interrupt some send_*Ack !
+ */
+ rv = kernel_sendmsg(sock, &msg, &iov, 1, size);
+ if (rv == -EAGAIN) {
+ if (we_should_drop_the_connection(mdev, sock))
+ break;
+ else
+ continue;
+ }
+ D_ASSERT(rv != 0);
+ if (rv == -EINTR) {
+ flush_signals(current);
+ rv = 0;
+ }
+ if (rv < 0)
+ break;
+ sent += rv;
+ iov.iov_base += rv;
+ iov.iov_len -= rv;
+ } while (sent < size);
+
+ if (sock == mdev->data.socket)
+ clear_bit(NET_CONGESTED, &mdev->flags);
+
+ if (rv <= 0) {
+ if (rv != -EAGAIN) {
+ dev_err(DEV, "%s_sendmsg returned %d\n",
+ sock == mdev->meta.socket ? "msock" : "sock",
+ rv);
+ drbd_force_state(mdev, NS(conn, C_BROKEN_PIPE));
+ } else
+ drbd_force_state(mdev, NS(conn, C_TIMEOUT));
+ }
+
+ return sent;
+}
+
+static int drbd_open(struct block_device *bdev, fmode_t mode)
+{
+ struct drbd_conf *mdev = bdev->bd_disk->private_data;
+ unsigned long flags;
+ int rv = 0;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ /* to have a stable mdev->state.role
+ * and no race with updating open_cnt */
+
+ if (mdev->state.role != R_PRIMARY) {
+ if (mode & FMODE_WRITE)
+ rv = -EROFS;
+ else if (!allow_oos)
+ rv = -EMEDIUMTYPE;
+ }
+
+ if (!rv)
+ mdev->open_cnt++;
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ return rv;
+}
+
+static int drbd_release(struct gendisk *gd, fmode_t mode)
+{
+ struct drbd_conf *mdev = gd->private_data;
+ mdev->open_cnt--;
+ return 0;
+}
+
+static void drbd_unplug_fn(struct request_queue *q)
+{
+ struct drbd_conf *mdev = q->queuedata;
+
+ /* unplug FIRST */
+ spin_lock_irq(q->queue_lock);
+ blk_remove_plug(q);
+ spin_unlock_irq(q->queue_lock);
+
+ /* only if connected */
+ spin_lock_irq(&mdev->req_lock);
+ if (mdev->state.pdsk >= D_INCONSISTENT && mdev->state.conn >= C_CONNECTED) {
+ D_ASSERT(mdev->state.role == R_PRIMARY);
+ if (test_and_clear_bit(UNPLUG_REMOTE, &mdev->flags)) {
+ /* add to the data.work queue,
+ * unless already queued.
+ * XXX this might be a good addition to drbd_queue_work
+ * anyways, to detect "double queuing" ... */
+ if (list_empty(&mdev->unplug_work.list))
+ drbd_queue_work(&mdev->data.work,
+ &mdev->unplug_work);
+ }
+ }
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (mdev->state.disk >= D_INCONSISTENT)
+ drbd_kick_lo(mdev);
+}
+
+static void drbd_set_defaults(struct drbd_conf *mdev)
+{
+ mdev->sync_conf.after = DRBD_AFTER_DEF;
+ mdev->sync_conf.rate = DRBD_RATE_DEF;
+ mdev->sync_conf.al_extents = DRBD_AL_EXTENTS_DEF;
+ mdev->state = (union drbd_state) {
+ { .role = R_SECONDARY,
+ .peer = R_UNKNOWN,
+ .conn = C_STANDALONE,
+ .disk = D_DISKLESS,
+ .pdsk = D_UNKNOWN,
+ .susp = 0
+ } };
+}
+
+void drbd_init_set_defaults(struct drbd_conf *mdev)
+{
+ /* the memset(,0,) did most of this.
+ * note: only assignments, no allocation in here */
+
+ drbd_set_defaults(mdev);
+
+ /* for now, we do NOT yet support it,
+ * even though we start some framework
+ * to eventually support barriers */
+ set_bit(NO_BARRIER_SUPP, &mdev->flags);
+
+ atomic_set(&mdev->ap_bio_cnt, 0);
+ atomic_set(&mdev->ap_pending_cnt, 0);
+ atomic_set(&mdev->rs_pending_cnt, 0);
+ atomic_set(&mdev->unacked_cnt, 0);
+ atomic_set(&mdev->local_cnt, 0);
+ atomic_set(&mdev->net_cnt, 0);
+ atomic_set(&mdev->packet_seq, 0);
+ atomic_set(&mdev->pp_in_use, 0);
+
+ mutex_init(&mdev->md_io_mutex);
+ mutex_init(&mdev->data.mutex);
+ mutex_init(&mdev->meta.mutex);
+ sema_init(&mdev->data.work.s, 0);
+ sema_init(&mdev->meta.work.s, 0);
+ mutex_init(&mdev->state_mutex);
+
+ spin_lock_init(&mdev->data.work.q_lock);
+ spin_lock_init(&mdev->meta.work.q_lock);
+
+ spin_lock_init(&mdev->al_lock);
+ spin_lock_init(&mdev->req_lock);
+ spin_lock_init(&mdev->peer_seq_lock);
+ spin_lock_init(&mdev->epoch_lock);
+
+ INIT_LIST_HEAD(&mdev->active_ee);
+ INIT_LIST_HEAD(&mdev->sync_ee);
+ INIT_LIST_HEAD(&mdev->done_ee);
+ INIT_LIST_HEAD(&mdev->read_ee);
+ INIT_LIST_HEAD(&mdev->net_ee);
+ INIT_LIST_HEAD(&mdev->resync_reads);
+ INIT_LIST_HEAD(&mdev->data.work.q);
+ INIT_LIST_HEAD(&mdev->meta.work.q);
+ INIT_LIST_HEAD(&mdev->resync_work.list);
+ INIT_LIST_HEAD(&mdev->unplug_work.list);
+ INIT_LIST_HEAD(&mdev->md_sync_work.list);
+ INIT_LIST_HEAD(&mdev->bm_io_work.w.list);
+ mdev->resync_work.cb = w_resync_inactive;
+ mdev->unplug_work.cb = w_send_write_hint;
+ mdev->md_sync_work.cb = w_md_sync;
+ mdev->bm_io_work.w.cb = w_bitmap_io;
+ init_timer(&mdev->resync_timer);
+ init_timer(&mdev->md_sync_timer);
+ mdev->resync_timer.function = resync_timer_fn;
+ mdev->resync_timer.data = (unsigned long) mdev;
+ mdev->md_sync_timer.function = md_sync_timer_fn;
+ mdev->md_sync_timer.data = (unsigned long) mdev;
+
+ init_waitqueue_head(&mdev->misc_wait);
+ init_waitqueue_head(&mdev->state_wait);
+ init_waitqueue_head(&mdev->ee_wait);
+ init_waitqueue_head(&mdev->al_wait);
+ init_waitqueue_head(&mdev->seq_wait);
+
+ drbd_thread_init(mdev, &mdev->receiver, drbdd_init);
+ drbd_thread_init(mdev, &mdev->worker, drbd_worker);
+ drbd_thread_init(mdev, &mdev->asender, drbd_asender);
+
+ mdev->agreed_pro_version = PRO_VERSION_MAX;
+ mdev->write_ordering = WO_bio_barrier;
+ mdev->resync_wenr = LC_FREE;
+}
+
+void drbd_mdev_cleanup(struct drbd_conf *mdev)
+{
+ if (mdev->receiver.t_state != None)
+ dev_err(DEV, "ASSERT FAILED: receiver t_state == %d expected 0.\n",
+ mdev->receiver.t_state);
+
+ /* no need to lock it, I'm the only thread alive */
+ if (atomic_read(&mdev->current_epoch->epoch_size) != 0)
+ dev_err(DEV, "epoch_size:%d\n", atomic_read(&mdev->current_epoch->epoch_size));
+ mdev->al_writ_cnt =
+ mdev->bm_writ_cnt =
+ mdev->read_cnt =
+ mdev->recv_cnt =
+ mdev->send_cnt =
+ mdev->writ_cnt =
+ mdev->p_size =
+ mdev->rs_start =
+ mdev->rs_total =
+ mdev->rs_failed =
+ mdev->rs_mark_left =
+ mdev->rs_mark_time = 0;
+ D_ASSERT(mdev->net_conf == NULL);
+
+ drbd_set_my_capacity(mdev, 0);
+ if (mdev->bitmap) {
+ /* maybe never allocated. */
+ drbd_bm_resize(mdev, 0);
+ drbd_bm_cleanup(mdev);
+ }
+
+ drbd_free_resources(mdev);
+
+ /*
+ * currently we drbd_init_ee only on module load, so
+ * we may do drbd_release_ee only on module unload!
+ */
+ D_ASSERT(list_empty(&mdev->active_ee));
+ D_ASSERT(list_empty(&mdev->sync_ee));
+ D_ASSERT(list_empty(&mdev->done_ee));
+ D_ASSERT(list_empty(&mdev->read_ee));
+ D_ASSERT(list_empty(&mdev->net_ee));
+ D_ASSERT(list_empty(&mdev->resync_reads));
+ D_ASSERT(list_empty(&mdev->data.work.q));
+ D_ASSERT(list_empty(&mdev->meta.work.q));
+ D_ASSERT(list_empty(&mdev->resync_work.list));
+ D_ASSERT(list_empty(&mdev->unplug_work.list));
+
+}
+
+
+static void drbd_destroy_mempools(void)
+{
+ struct page *page;
+
+ while (drbd_pp_pool) {
+ page = drbd_pp_pool;
+ drbd_pp_pool = (struct page *)page_private(page);
+ __free_page(page);
+ drbd_pp_vacant--;
+ }
+
+ /* D_ASSERT(atomic_read(&drbd_pp_vacant)==0); */
+
+ if (drbd_ee_mempool)
+ mempool_destroy(drbd_ee_mempool);
+ if (drbd_request_mempool)
+ mempool_destroy(drbd_request_mempool);
+ if (drbd_ee_cache)
+ kmem_cache_destroy(drbd_ee_cache);
+ if (drbd_request_cache)
+ kmem_cache_destroy(drbd_request_cache);
+ if (drbd_bm_ext_cache)
+ kmem_cache_destroy(drbd_bm_ext_cache);
+ if (drbd_al_ext_cache)
+ kmem_cache_destroy(drbd_al_ext_cache);
+
+ drbd_ee_mempool = NULL;
+ drbd_request_mempool = NULL;
+ drbd_ee_cache = NULL;
+ drbd_request_cache = NULL;
+ drbd_bm_ext_cache = NULL;
+ drbd_al_ext_cache = NULL;
+
+ return;
+}
+
+static int drbd_create_mempools(void)
+{
+ struct page *page;
+ const int number = (DRBD_MAX_SEGMENT_SIZE/PAGE_SIZE) * minor_count;
+ int i;
+
+ /* prepare our caches and mempools */
+ drbd_request_mempool = NULL;
+ drbd_ee_cache = NULL;
+ drbd_request_cache = NULL;
+ drbd_bm_ext_cache = NULL;
+ drbd_al_ext_cache = NULL;
+ drbd_pp_pool = NULL;
+
+ /* caches */
+ drbd_request_cache = kmem_cache_create(
+ "drbd_req", sizeof(struct drbd_request), 0, 0, NULL);
+ if (drbd_request_cache == NULL)
+ goto Enomem;
+
+ drbd_ee_cache = kmem_cache_create(
+ "drbd_ee", sizeof(struct drbd_epoch_entry), 0, 0, NULL);
+ if (drbd_ee_cache == NULL)
+ goto Enomem;
+
+ drbd_bm_ext_cache = kmem_cache_create(
+ "drbd_bm", sizeof(struct bm_extent), 0, 0, NULL);
+ if (drbd_bm_ext_cache == NULL)
+ goto Enomem;
+
+ drbd_al_ext_cache = kmem_cache_create(
+ "drbd_al", sizeof(struct lc_element), 0, 0, NULL);
+ if (drbd_al_ext_cache == NULL)
+ goto Enomem;
+
+ /* mempools */
+ drbd_request_mempool = mempool_create(number,
+ mempool_alloc_slab, mempool_free_slab, drbd_request_cache);
+ if (drbd_request_mempool == NULL)
+ goto Enomem;
+
+ drbd_ee_mempool = mempool_create(number,
+ mempool_alloc_slab, mempool_free_slab, drbd_ee_cache);
+ if (drbd_request_mempool == NULL)
+ goto Enomem;
+
+ /* drbd's page pool */
+ spin_lock_init(&drbd_pp_lock);
+
+ for (i = 0; i < number; i++) {
+ page = alloc_page(GFP_HIGHUSER);
+ if (!page)
+ goto Enomem;
+ set_page_private(page, (unsigned long)drbd_pp_pool);
+ drbd_pp_pool = page;
+ }
+ drbd_pp_vacant = number;
+
+ return 0;
+
+Enomem:
+ drbd_destroy_mempools(); /* in case we allocated some */
+ return -ENOMEM;
+}
+
+static int drbd_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ /* just so we have it. you never know what interesting things we
+ * might want to do here some day...
+ */
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block drbd_notifier = {
+ .notifier_call = drbd_notify_sys,
+};
+
+static void drbd_release_ee_lists(struct drbd_conf *mdev)
+{
+ int rr;
+
+ rr = drbd_release_ee(mdev, &mdev->active_ee);
+ if (rr)
+ dev_err(DEV, "%d EEs in active list found!\n", rr);
+
+ rr = drbd_release_ee(mdev, &mdev->sync_ee);
+ if (rr)
+ dev_err(DEV, "%d EEs in sync list found!\n", rr);
+
+ rr = drbd_release_ee(mdev, &mdev->read_ee);
+ if (rr)
+ dev_err(DEV, "%d EEs in read list found!\n", rr);
+
+ rr = drbd_release_ee(mdev, &mdev->done_ee);
+ if (rr)
+ dev_err(DEV, "%d EEs in done list found!\n", rr);
+
+ rr = drbd_release_ee(mdev, &mdev->net_ee);
+ if (rr)
+ dev_err(DEV, "%d EEs in net list found!\n", rr);
+}
+
+/* caution. no locking.
+ * currently only used from module cleanup code. */
+static void drbd_delete_device(unsigned int minor)
+{
+ struct drbd_conf *mdev = minor_to_mdev(minor);
+
+ if (!mdev)
+ return;
+
+ /* paranoia asserts */
+ if (mdev->open_cnt != 0)
+ dev_err(DEV, "open_cnt = %d in %s:%u", mdev->open_cnt,
+ __FILE__ , __LINE__);
+
+ ERR_IF (!list_empty(&mdev->data.work.q)) {
+ struct list_head *lp;
+ list_for_each(lp, &mdev->data.work.q) {
+ dev_err(DEV, "lp = %p\n", lp);
+ }
+ };
+ /* end paranoia asserts */
+
+ del_gendisk(mdev->vdisk);
+
+ /* cleanup stuff that may have been allocated during
+ * device (re-)configuration or state changes */
+
+ if (mdev->this_bdev)
+ bdput(mdev->this_bdev);
+
+ drbd_free_resources(mdev);
+
+ drbd_release_ee_lists(mdev);
+
+ /* should be free'd on disconnect? */
+ kfree(mdev->ee_hash);
+ /*
+ mdev->ee_hash_s = 0;
+ mdev->ee_hash = NULL;
+ */
+
+ lc_destroy(mdev->act_log);
+ lc_destroy(mdev->resync);
+
+ kfree(mdev->p_uuid);
+ /* mdev->p_uuid = NULL; */
+
+ kfree(mdev->int_dig_out);
+ kfree(mdev->int_dig_in);
+ kfree(mdev->int_dig_vv);
+
+ /* cleanup the rest that has been
+ * allocated from drbd_new_device
+ * and actually free the mdev itself */
+ drbd_free_mdev(mdev);
+}
+
+static void drbd_cleanup(void)
+{
+ unsigned int i;
+
+ unregister_reboot_notifier(&drbd_notifier);
+
+ drbd_nl_cleanup();
+
+ if (minor_table) {
+ if (drbd_proc)
+ remove_proc_entry("drbd", NULL);
+ i = minor_count;
+ while (i--)
+ drbd_delete_device(i);
+ drbd_destroy_mempools();
+ }
+
+ kfree(minor_table);
+
+ unregister_blkdev(DRBD_MAJOR, "drbd");
+
+ printk(KERN_INFO "drbd: module cleanup done.\n");
+}
+
+/**
+ * drbd_congested() - Callback for pdflush
+ * @congested_data: User data
+ * @bdi_bits: Bits pdflush is currently interested in
+ *
+ * Returns 1<<BDI_async_congested and/or 1<<BDI_sync_congested if we are congested.
+ */
+static int drbd_congested(void *congested_data, int bdi_bits)
+{
+ struct drbd_conf *mdev = congested_data;
+ struct request_queue *q;
+ char reason = '-';
+ int r = 0;
+
+ if (!__inc_ap_bio_cond(mdev)) {
+ /* DRBD has frozen IO */
+ r = bdi_bits;
+ reason = 'd';
+ goto out;
+ }
+
+ if (get_ldev(mdev)) {
+ q = bdev_get_queue(mdev->ldev->backing_bdev);
+ r = bdi_congested(&q->backing_dev_info, bdi_bits);
+ put_ldev(mdev);
+ if (r)
+ reason = 'b';
+ }
+
+ if (bdi_bits & (1 << BDI_async_congested) && test_bit(NET_CONGESTED, &mdev->flags)) {
+ r |= (1 << BDI_async_congested);
+ reason = reason == 'b' ? 'a' : 'n';
+ }
+
+out:
+ mdev->congestion_reason = reason;
+ return r;
+}
+
+struct drbd_conf *drbd_new_device(unsigned int minor)
+{
+ struct drbd_conf *mdev;
+ struct gendisk *disk;
+ struct request_queue *q;
+
+ /* GFP_KERNEL, we are outside of all write-out paths */
+ mdev = kzalloc(sizeof(struct drbd_conf), GFP_KERNEL);
+ if (!mdev)
+ return NULL;
+ if (!zalloc_cpumask_var(&mdev->cpu_mask, GFP_KERNEL))
+ goto out_no_cpumask;
+
+ mdev->minor = minor;
+
+ drbd_init_set_defaults(mdev);
+
+ q = blk_alloc_queue(GFP_KERNEL);
+ if (!q)
+ goto out_no_q;
+ mdev->rq_queue = q;
+ q->queuedata = mdev;
+ blk_queue_max_segment_size(q, DRBD_MAX_SEGMENT_SIZE);
+
+ disk = alloc_disk(1);
+ if (!disk)
+ goto out_no_disk;
+ mdev->vdisk = disk;
+
+ set_disk_ro(disk, TRUE);
+
+ disk->queue = q;
+ disk->major = DRBD_MAJOR;
+ disk->first_minor = minor;
+ disk->fops = &drbd_ops;
+ sprintf(disk->disk_name, "drbd%d", minor);
+ disk->private_data = mdev;
+
+ mdev->this_bdev = bdget(MKDEV(DRBD_MAJOR, minor));
+ /* we have no partitions. we contain only ourselves. */
+ mdev->this_bdev->bd_contains = mdev->this_bdev;
+
+ q->backing_dev_info.congested_fn = drbd_congested;
+ q->backing_dev_info.congested_data = mdev;
+
+ blk_queue_make_request(q, drbd_make_request_26);
+ blk_queue_bounce_limit(q, BLK_BOUNCE_ANY);
+ blk_queue_merge_bvec(q, drbd_merge_bvec);
+ q->queue_lock = &mdev->req_lock; /* needed since we use */
+ /* plugging on a queue, that actually has no requests! */
+ q->unplug_fn = drbd_unplug_fn;
+
+ mdev->md_io_page = alloc_page(GFP_KERNEL);
+ if (!mdev->md_io_page)
+ goto out_no_io_page;
+
+ if (drbd_bm_init(mdev))
+ goto out_no_bitmap;
+ /* no need to lock access, we are still initializing this minor device. */
+ if (!tl_init(mdev))
+ goto out_no_tl;
+
+ mdev->app_reads_hash = kzalloc(APP_R_HSIZE*sizeof(void *), GFP_KERNEL);
+ if (!mdev->app_reads_hash)
+ goto out_no_app_reads;
+
+ mdev->current_epoch = kzalloc(sizeof(struct drbd_epoch), GFP_KERNEL);
+ if (!mdev->current_epoch)
+ goto out_no_epoch;
+
+ INIT_LIST_HEAD(&mdev->current_epoch->list);
+ mdev->epochs = 1;
+
+ return mdev;
+
+/* out_whatever_else:
+ kfree(mdev->current_epoch); */
+out_no_epoch:
+ kfree(mdev->app_reads_hash);
+out_no_app_reads:
+ tl_cleanup(mdev);
+out_no_tl:
+ drbd_bm_cleanup(mdev);
+out_no_bitmap:
+ __free_page(mdev->md_io_page);
+out_no_io_page:
+ put_disk(disk);
+out_no_disk:
+ blk_cleanup_queue(q);
+out_no_q:
+ free_cpumask_var(mdev->cpu_mask);
+out_no_cpumask:
+ kfree(mdev);
+ return NULL;
+}
+
+/* counterpart of drbd_new_device.
+ * last part of drbd_delete_device. */
+void drbd_free_mdev(struct drbd_conf *mdev)
+{
+ kfree(mdev->current_epoch);
+ kfree(mdev->app_reads_hash);
+ tl_cleanup(mdev);
+ if (mdev->bitmap) /* should no longer be there. */
+ drbd_bm_cleanup(mdev);
+ __free_page(mdev->md_io_page);
+ put_disk(mdev->vdisk);
+ blk_cleanup_queue(mdev->rq_queue);
+ free_cpumask_var(mdev->cpu_mask);
+ kfree(mdev);
+}
+
+
+int __init drbd_init(void)
+{
+ int err;
+
+ if (sizeof(struct p_handshake) != 80) {
+ printk(KERN_ERR
+ "drbd: never change the size or layout "
+ "of the HandShake packet.\n");
+ return -EINVAL;
+ }
+
+ if (1 > minor_count || minor_count > 255) {
+ printk(KERN_ERR
+ "drbd: invalid minor_count (%d)\n", minor_count);
+#ifdef MODULE
+ return -EINVAL;
+#else
+ minor_count = 8;
+#endif
+ }
+
+ err = drbd_nl_init();
+ if (err)
+ return err;
+
+ err = register_blkdev(DRBD_MAJOR, "drbd");
+ if (err) {
+ printk(KERN_ERR
+ "drbd: unable to register block device major %d\n",
+ DRBD_MAJOR);
+ return err;
+ }
+
+ register_reboot_notifier(&drbd_notifier);
+
+ /*
+ * allocate all necessary structs
+ */
+ err = -ENOMEM;
+
+ init_waitqueue_head(&drbd_pp_wait);
+
+ drbd_proc = NULL; /* play safe for drbd_cleanup */
+ minor_table = kzalloc(sizeof(struct drbd_conf *)*minor_count,
+ GFP_KERNEL);
+ if (!minor_table)
+ goto Enomem;
+
+ err = drbd_create_mempools();
+ if (err)
+ goto Enomem;
+
+ drbd_proc = proc_create("drbd", S_IFREG | S_IRUGO , NULL, &drbd_proc_fops);
+ if (!drbd_proc) {
+ printk(KERN_ERR "drbd: unable to register proc file\n");
+ goto Enomem;
+ }
+
+ rwlock_init(&global_state_lock);
+
+ printk(KERN_INFO "drbd: initialized. "
+ "Version: " REL_VERSION " (api:%d/proto:%d-%d)\n",
+ API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX);
+ printk(KERN_INFO "drbd: %s\n", drbd_buildtag());
+ printk(KERN_INFO "drbd: registered as block device major %d\n",
+ DRBD_MAJOR);
+ printk(KERN_INFO "drbd: minor_table @ 0x%p\n", minor_table);
+
+ return 0; /* Success! */
+
+Enomem:
+ drbd_cleanup();
+ if (err == -ENOMEM)
+ /* currently always the case */
+ printk(KERN_ERR "drbd: ran out of memory\n");
+ else
+ printk(KERN_ERR "drbd: initialization failure\n");
+ return err;
+}
+
+void drbd_free_bc(struct drbd_backing_dev *ldev)
+{
+ if (ldev == NULL)
+ return;
+
+ bd_release(ldev->backing_bdev);
+ bd_release(ldev->md_bdev);
+
+ fput(ldev->lo_file);
+ fput(ldev->md_file);
+
+ kfree(ldev);
+}
+
+void drbd_free_sock(struct drbd_conf *mdev)
+{
+ if (mdev->data.socket) {
+ kernel_sock_shutdown(mdev->data.socket, SHUT_RDWR);
+ sock_release(mdev->data.socket);
+ mdev->data.socket = NULL;
+ }
+ if (mdev->meta.socket) {
+ kernel_sock_shutdown(mdev->meta.socket, SHUT_RDWR);
+ sock_release(mdev->meta.socket);
+ mdev->meta.socket = NULL;
+ }
+}
+
+
+void drbd_free_resources(struct drbd_conf *mdev)
+{
+ crypto_free_hash(mdev->csums_tfm);
+ mdev->csums_tfm = NULL;
+ crypto_free_hash(mdev->verify_tfm);
+ mdev->verify_tfm = NULL;
+ crypto_free_hash(mdev->cram_hmac_tfm);
+ mdev->cram_hmac_tfm = NULL;
+ crypto_free_hash(mdev->integrity_w_tfm);
+ mdev->integrity_w_tfm = NULL;
+ crypto_free_hash(mdev->integrity_r_tfm);
+ mdev->integrity_r_tfm = NULL;
+
+ drbd_free_sock(mdev);
+
+ __no_warn(local,
+ drbd_free_bc(mdev->ldev);
+ mdev->ldev = NULL;);
+}
+
+/* meta data management */
+
+struct meta_data_on_disk {
+ u64 la_size; /* last agreed size. */
+ u64 uuid[UI_SIZE]; /* UUIDs. */
+ u64 device_uuid;
+ u64 reserved_u64_1;
+ u32 flags; /* MDF */
+ u32 magic;
+ u32 md_size_sect;
+ u32 al_offset; /* offset to this block */
+ u32 al_nr_extents; /* important for restoring the AL */
+ /* `-- act_log->nr_elements <-- sync_conf.al_extents */
+ u32 bm_offset; /* offset to the bitmap, from here */
+ u32 bm_bytes_per_bit; /* BM_BLOCK_SIZE */
+ u32 reserved_u32[4];
+
+} __packed;
+
+/**
+ * drbd_md_sync() - Writes the meta data super block if the MD_DIRTY flag bit is set
+ * @mdev: DRBD device.
+ */
+void drbd_md_sync(struct drbd_conf *mdev)
+{
+ struct meta_data_on_disk *buffer;
+ sector_t sector;
+ int i;
+
+ if (!test_and_clear_bit(MD_DIRTY, &mdev->flags))
+ return;
+ del_timer(&mdev->md_sync_timer);
+
+ /* We use here D_FAILED and not D_ATTACHING because we try to write
+ * metadata even if we detach due to a disk failure! */
+ if (!get_ldev_if_state(mdev, D_FAILED))
+ return;
+
+ mutex_lock(&mdev->md_io_mutex);
+ buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page);
+ memset(buffer, 0, 512);
+
+ buffer->la_size = cpu_to_be64(drbd_get_capacity(mdev->this_bdev));
+ for (i = UI_CURRENT; i < UI_SIZE; i++)
+ buffer->uuid[i] = cpu_to_be64(mdev->ldev->md.uuid[i]);
+ buffer->flags = cpu_to_be32(mdev->ldev->md.flags);
+ buffer->magic = cpu_to_be32(DRBD_MD_MAGIC);
+
+ buffer->md_size_sect = cpu_to_be32(mdev->ldev->md.md_size_sect);
+ buffer->al_offset = cpu_to_be32(mdev->ldev->md.al_offset);
+ buffer->al_nr_extents = cpu_to_be32(mdev->act_log->nr_elements);
+ buffer->bm_bytes_per_bit = cpu_to_be32(BM_BLOCK_SIZE);
+ buffer->device_uuid = cpu_to_be64(mdev->ldev->md.device_uuid);
+
+ buffer->bm_offset = cpu_to_be32(mdev->ldev->md.bm_offset);
+
+ D_ASSERT(drbd_md_ss__(mdev, mdev->ldev) == mdev->ldev->md.md_offset);
+ sector = mdev->ldev->md.md_offset;
+
+ if (drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
+ clear_bit(MD_DIRTY, &mdev->flags);
+ } else {
+ /* this was a try anyways ... */
+ dev_err(DEV, "meta data update failed!\n");
+
+ drbd_chk_io_error(mdev, 1, TRUE);
+ }
+
+ /* Update mdev->ldev->md.la_size_sect,
+ * since we updated it on metadata. */
+ mdev->ldev->md.la_size_sect = drbd_get_capacity(mdev->this_bdev);
+
+ mutex_unlock(&mdev->md_io_mutex);
+ put_ldev(mdev);
+}
+
+/**
+ * drbd_md_read() - Reads in the meta data super block
+ * @mdev: DRBD device.
+ * @bdev: Device from which the meta data should be read in.
+ *
+ * Return 0 (NO_ERROR) on success, and an enum drbd_ret_codes in case
+ * something goes wrong. Currently only: ERR_IO_MD_DISK, ERR_MD_INVALID.
+ */
+int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
+{
+ struct meta_data_on_disk *buffer;
+ int i, rv = NO_ERROR;
+
+ if (!get_ldev_if_state(mdev, D_ATTACHING))
+ return ERR_IO_MD_DISK;
+
+ mutex_lock(&mdev->md_io_mutex);
+ buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page);
+
+ if (!drbd_md_sync_page_io(mdev, bdev, bdev->md.md_offset, READ)) {
+ /* NOTE: cant do normal error processing here as this is
+ called BEFORE disk is attached */
+ dev_err(DEV, "Error while reading metadata.\n");
+ rv = ERR_IO_MD_DISK;
+ goto err;
+ }
+
+ if (be32_to_cpu(buffer->magic) != DRBD_MD_MAGIC) {
+ dev_err(DEV, "Error while reading metadata, magic not found.\n");
+ rv = ERR_MD_INVALID;
+ goto err;
+ }
+ if (be32_to_cpu(buffer->al_offset) != bdev->md.al_offset) {
+ dev_err(DEV, "unexpected al_offset: %d (expected %d)\n",
+ be32_to_cpu(buffer->al_offset), bdev->md.al_offset);
+ rv = ERR_MD_INVALID;
+ goto err;
+ }
+ if (be32_to_cpu(buffer->bm_offset) != bdev->md.bm_offset) {
+ dev_err(DEV, "unexpected bm_offset: %d (expected %d)\n",
+ be32_to_cpu(buffer->bm_offset), bdev->md.bm_offset);
+ rv = ERR_MD_INVALID;
+ goto err;
+ }
+ if (be32_to_cpu(buffer->md_size_sect) != bdev->md.md_size_sect) {
+ dev_err(DEV, "unexpected md_size: %u (expected %u)\n",
+ be32_to_cpu(buffer->md_size_sect), bdev->md.md_size_sect);
+ rv = ERR_MD_INVALID;
+ goto err;
+ }
+
+ if (be32_to_cpu(buffer->bm_bytes_per_bit) != BM_BLOCK_SIZE) {
+ dev_err(DEV, "unexpected bm_bytes_per_bit: %u (expected %u)\n",
+ be32_to_cpu(buffer->bm_bytes_per_bit), BM_BLOCK_SIZE);
+ rv = ERR_MD_INVALID;
+ goto err;
+ }
+
+ bdev->md.la_size_sect = be64_to_cpu(buffer->la_size);
+ for (i = UI_CURRENT; i < UI_SIZE; i++)
+ bdev->md.uuid[i] = be64_to_cpu(buffer->uuid[i]);
+ bdev->md.flags = be32_to_cpu(buffer->flags);
+ mdev->sync_conf.al_extents = be32_to_cpu(buffer->al_nr_extents);
+ bdev->md.device_uuid = be64_to_cpu(buffer->device_uuid);
+
+ if (mdev->sync_conf.al_extents < 7)
+ mdev->sync_conf.al_extents = 127;
+
+ err:
+ mutex_unlock(&mdev->md_io_mutex);
+ put_ldev(mdev);
+
+ return rv;
+}
+
+/**
+ * drbd_md_mark_dirty() - Mark meta data super block as dirty
+ * @mdev: DRBD device.
+ *
+ * Call this function if you change anything that should be written to
+ * the meta-data super block. This function sets MD_DIRTY, and starts a
+ * timer that ensures that within five seconds you have to call drbd_md_sync().
+ */
+void drbd_md_mark_dirty(struct drbd_conf *mdev)
+{
+ set_bit(MD_DIRTY, &mdev->flags);
+ mod_timer(&mdev->md_sync_timer, jiffies + 5*HZ);
+}
+
+
+static void drbd_uuid_move_history(struct drbd_conf *mdev) __must_hold(local)
+{
+ int i;
+
+ for (i = UI_HISTORY_START; i < UI_HISTORY_END; i++)
+ mdev->ldev->md.uuid[i+1] = mdev->ldev->md.uuid[i];
+}
+
+void _drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local)
+{
+ if (idx == UI_CURRENT) {
+ if (mdev->state.role == R_PRIMARY)
+ val |= 1;
+ else
+ val &= ~((u64)1);
+
+ drbd_set_ed_uuid(mdev, val);
+ }
+
+ mdev->ldev->md.uuid[idx] = val;
+ drbd_md_mark_dirty(mdev);
+}
+
+
+void drbd_uuid_set(struct drbd_conf *mdev, int idx, u64 val) __must_hold(local)
+{
+ if (mdev->ldev->md.uuid[idx]) {
+ drbd_uuid_move_history(mdev);
+ mdev->ldev->md.uuid[UI_HISTORY_START] = mdev->ldev->md.uuid[idx];
+ }
+ _drbd_uuid_set(mdev, idx, val);
+}
+
+/**
+ * drbd_uuid_new_current() - Creates a new current UUID
+ * @mdev: DRBD device.
+ *
+ * Creates a new current UUID, and rotates the old current UUID into
+ * the bitmap slot. Causes an incremental resync upon next connect.
+ */
+void drbd_uuid_new_current(struct drbd_conf *mdev) __must_hold(local)
+{
+ u64 val;
+
+ dev_info(DEV, "Creating new current UUID\n");
+ D_ASSERT(mdev->ldev->md.uuid[UI_BITMAP] == 0);
+ mdev->ldev->md.uuid[UI_BITMAP] = mdev->ldev->md.uuid[UI_CURRENT];
+
+ get_random_bytes(&val, sizeof(u64));
+ _drbd_uuid_set(mdev, UI_CURRENT, val);
+}
+
+void drbd_uuid_set_bm(struct drbd_conf *mdev, u64 val) __must_hold(local)
+{
+ if (mdev->ldev->md.uuid[UI_BITMAP] == 0 && val == 0)
+ return;
+
+ if (val == 0) {
+ drbd_uuid_move_history(mdev);
+ mdev->ldev->md.uuid[UI_HISTORY_START] = mdev->ldev->md.uuid[UI_BITMAP];
+ mdev->ldev->md.uuid[UI_BITMAP] = 0;
+ } else {
+ if (mdev->ldev->md.uuid[UI_BITMAP])
+ dev_warn(DEV, "bm UUID already set");
+
+ mdev->ldev->md.uuid[UI_BITMAP] = val;
+ mdev->ldev->md.uuid[UI_BITMAP] &= ~((u64)1);
+
+ }
+ drbd_md_mark_dirty(mdev);
+}
+
+/**
+ * drbd_bmio_set_n_write() - io_fn for drbd_queue_bitmap_io() or drbd_bitmap_io()
+ * @mdev: DRBD device.
+ *
+ * Sets all bits in the bitmap and writes the whole bitmap to stable storage.
+ */
+int drbd_bmio_set_n_write(struct drbd_conf *mdev)
+{
+ int rv = -EIO;
+
+ if (get_ldev_if_state(mdev, D_ATTACHING)) {
+ drbd_md_set_flag(mdev, MDF_FULL_SYNC);
+ drbd_md_sync(mdev);
+ drbd_bm_set_all(mdev);
+
+ rv = drbd_bm_write(mdev);
+
+ if (!rv) {
+ drbd_md_clear_flag(mdev, MDF_FULL_SYNC);
+ drbd_md_sync(mdev);
+ }
+
+ put_ldev(mdev);
+ }
+
+ return rv;
+}
+
+/**
+ * drbd_bmio_clear_n_write() - io_fn for drbd_queue_bitmap_io() or drbd_bitmap_io()
+ * @mdev: DRBD device.
+ *
+ * Clears all bits in the bitmap and writes the whole bitmap to stable storage.
+ */
+int drbd_bmio_clear_n_write(struct drbd_conf *mdev)
+{
+ int rv = -EIO;
+
+ if (get_ldev_if_state(mdev, D_ATTACHING)) {
+ drbd_bm_clear_all(mdev);
+ rv = drbd_bm_write(mdev);
+ put_ldev(mdev);
+ }
+
+ return rv;
+}
+
+static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+{
+ struct bm_io_work *work = container_of(w, struct bm_io_work, w);
+ int rv;
+
+ D_ASSERT(atomic_read(&mdev->ap_bio_cnt) == 0);
+
+ drbd_bm_lock(mdev, work->why);
+ rv = work->io_fn(mdev);
+ drbd_bm_unlock(mdev);
+
+ clear_bit(BITMAP_IO, &mdev->flags);
+ wake_up(&mdev->misc_wait);
+
+ if (work->done)
+ work->done(mdev, rv);
+
+ clear_bit(BITMAP_IO_QUEUED, &mdev->flags);
+ work->why = NULL;
+
+ return 1;
+}
+
+/**
+ * drbd_queue_bitmap_io() - Queues an IO operation on the whole bitmap
+ * @mdev: DRBD device.
+ * @io_fn: IO callback to be called when bitmap IO is possible
+ * @done: callback to be called after the bitmap IO was performed
+ * @why: Descriptive text of the reason for doing the IO
+ *
+ * While IO on the bitmap happens we freeze application IO thus we ensure
+ * that drbd_set_out_of_sync() can not be called. This function MAY ONLY be
+ * called from worker context. It MUST NOT be used while a previous such
+ * work is still pending!
+ */
+void drbd_queue_bitmap_io(struct drbd_conf *mdev,
+ int (*io_fn)(struct drbd_conf *),
+ void (*done)(struct drbd_conf *, int),
+ char *why)
+{
+ D_ASSERT(current == mdev->worker.task);
+
+ D_ASSERT(!test_bit(BITMAP_IO_QUEUED, &mdev->flags));
+ D_ASSERT(!test_bit(BITMAP_IO, &mdev->flags));
+ D_ASSERT(list_empty(&mdev->bm_io_work.w.list));
+ if (mdev->bm_io_work.why)
+ dev_err(DEV, "FIXME going to queue '%s' but '%s' still pending?\n",
+ why, mdev->bm_io_work.why);
+
+ mdev->bm_io_work.io_fn = io_fn;
+ mdev->bm_io_work.done = done;
+ mdev->bm_io_work.why = why;
+
+ set_bit(BITMAP_IO, &mdev->flags);
+ if (atomic_read(&mdev->ap_bio_cnt) == 0) {
+ if (list_empty(&mdev->bm_io_work.w.list)) {
+ set_bit(BITMAP_IO_QUEUED, &mdev->flags);
+ drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
+ } else
+ dev_err(DEV, "FIXME avoided double queuing bm_io_work\n");
+ }
+}
+
+/**
+ * drbd_bitmap_io() - Does an IO operation on the whole bitmap
+ * @mdev: DRBD device.
+ * @io_fn: IO callback to be called when bitmap IO is possible
+ * @why: Descriptive text of the reason for doing the IO
+ *
+ * freezes application IO while that the actual IO operations runs. This
+ * functions MAY NOT be called from worker context.
+ */
+int drbd_bitmap_io(struct drbd_conf *mdev, int (*io_fn)(struct drbd_conf *), char *why)
+{
+ int rv;
+
+ D_ASSERT(current != mdev->worker.task);
+
+ drbd_suspend_io(mdev);
+
+ drbd_bm_lock(mdev, why);
+ rv = io_fn(mdev);
+ drbd_bm_unlock(mdev);
+
+ drbd_resume_io(mdev);
+
+ return rv;
+}
+
+void drbd_md_set_flag(struct drbd_conf *mdev, int flag) __must_hold(local)
+{
+ if ((mdev->ldev->md.flags & flag) != flag) {
+ drbd_md_mark_dirty(mdev);
+ mdev->ldev->md.flags |= flag;
+ }
+}
+
+void drbd_md_clear_flag(struct drbd_conf *mdev, int flag) __must_hold(local)
+{
+ if ((mdev->ldev->md.flags & flag) != 0) {
+ drbd_md_mark_dirty(mdev);
+ mdev->ldev->md.flags &= ~flag;
+ }
+}
+int drbd_md_test_flag(struct drbd_backing_dev *bdev, int flag)
+{
+ return (bdev->md.flags & flag) != 0;
+}
+
+static void md_sync_timer_fn(unsigned long data)
+{
+ struct drbd_conf *mdev = (struct drbd_conf *) data;
+
+ drbd_queue_work_front(&mdev->data.work, &mdev->md_sync_work);
+}
+
+static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+{
+ dev_warn(DEV, "md_sync_timer expired! Worker calls drbd_md_sync().\n");
+ drbd_md_sync(mdev);
+
+ return 1;
+}
+
+#ifdef CONFIG_DRBD_FAULT_INJECTION
+/* Fault insertion support including random number generator shamelessly
+ * stolen from kernel/rcutorture.c */
+struct fault_random_state {
+ unsigned long state;
+ unsigned long count;
+};
+
+#define FAULT_RANDOM_MULT 39916801 /* prime */
+#define FAULT_RANDOM_ADD 479001701 /* prime */
+#define FAULT_RANDOM_REFRESH 10000
+
+/*
+ * Crude but fast random-number generator. Uses a linear congruential
+ * generator, with occasional help from get_random_bytes().
+ */
+static unsigned long
+_drbd_fault_random(struct fault_random_state *rsp)
+{
+ long refresh;
+
+ if (--rsp->count < 0) {
+ get_random_bytes(&refresh, sizeof(refresh));
+ rsp->state += refresh;
+ rsp->count = FAULT_RANDOM_REFRESH;
+ }
+ rsp->state = rsp->state * FAULT_RANDOM_MULT + FAULT_RANDOM_ADD;
+ return swahw32(rsp->state);
+}
+
+static char *
+_drbd_fault_str(unsigned int type) {
+ static char *_faults[] = {
+ [DRBD_FAULT_MD_WR] = "Meta-data write",
+ [DRBD_FAULT_MD_RD] = "Meta-data read",
+ [DRBD_FAULT_RS_WR] = "Resync write",
+ [DRBD_FAULT_RS_RD] = "Resync read",
+ [DRBD_FAULT_DT_WR] = "Data write",
+ [DRBD_FAULT_DT_RD] = "Data read",
+ [DRBD_FAULT_DT_RA] = "Data read ahead",
+ [DRBD_FAULT_BM_ALLOC] = "BM allocation",
+ [DRBD_FAULT_AL_EE] = "EE allocation"
+ };
+
+ return (type < DRBD_FAULT_MAX) ? _faults[type] : "**Unknown**";
+}
+
+unsigned int
+_drbd_insert_fault(struct drbd_conf *mdev, unsigned int type)
+{
+ static struct fault_random_state rrs = {0, 0};
+
+ unsigned int ret = (
+ (fault_devs == 0 ||
+ ((1 << mdev_to_minor(mdev)) & fault_devs) != 0) &&
+ (((_drbd_fault_random(&rrs) % 100) + 1) <= fault_rate));
+
+ if (ret) {
+ fault_count++;
+
+ if (printk_ratelimit())
+ dev_warn(DEV, "***Simulating %s failure\n",
+ _drbd_fault_str(type));
+ }
+
+ return ret;
+}
+#endif
+
+const char *drbd_buildtag(void)
+{
+ /* DRBD built from external sources has here a reference to the
+ git hash of the source code. */
+
+ static char buildtag[38] = "\0uilt-in";
+
+ if (buildtag[0] == 0) {
+#ifdef CONFIG_MODULES
+ if (THIS_MODULE != NULL)
+ sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
+ else
+#endif
+ buildtag[0] = 'b';
+ }
+
+ return buildtag;
+}
+
+module_init(drbd_init)
+module_exit(drbd_cleanup)
+
+EXPORT_SYMBOL(drbd_conn_str);
+EXPORT_SYMBOL(drbd_role_str);
+EXPORT_SYMBOL(drbd_disk_str);
+EXPORT_SYMBOL(drbd_set_st_err_str);
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
new file mode 100644
index 00000000000..436a090b532
--- /dev/null
+++ b/drivers/block/drbd/drbd_nl.c
@@ -0,0 +1,2364 @@
+/*
+ drbd_nl.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+#include <linux/drbd.h>
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/connector.h>
+#include <linux/blkpg.h>
+#include <linux/cpumask.h>
+#include "drbd_int.h"
+#include "drbd_wrappers.h"
+#include <asm/unaligned.h>
+#include <linux/drbd_tag_magic.h>
+#include <linux/drbd_limits.h>
+
+static unsigned short *tl_add_blob(unsigned short *, enum drbd_tags, const void *, int);
+static unsigned short *tl_add_str(unsigned short *, enum drbd_tags, const char *);
+static unsigned short *tl_add_int(unsigned short *, enum drbd_tags, const void *);
+
+/* see get_sb_bdev and bd_claim */
+static char *drbd_m_holder = "Hands off! this is DRBD's meta data device.";
+
+/* Generate the tag_list to struct functions */
+#define NL_PACKET(name, number, fields) \
+static int name ## _from_tags(struct drbd_conf *mdev, \
+ unsigned short *tags, struct name *arg) __attribute__ ((unused)); \
+static int name ## _from_tags(struct drbd_conf *mdev, \
+ unsigned short *tags, struct name *arg) \
+{ \
+ int tag; \
+ int dlen; \
+ \
+ while ((tag = get_unaligned(tags++)) != TT_END) { \
+ dlen = get_unaligned(tags++); \
+ switch (tag_number(tag)) { \
+ fields \
+ default: \
+ if (tag & T_MANDATORY) { \
+ dev_err(DEV, "Unknown tag: %d\n", tag_number(tag)); \
+ return 0; \
+ } \
+ } \
+ tags = (unsigned short *)((char *)tags + dlen); \
+ } \
+ return 1; \
+}
+#define NL_INTEGER(pn, pr, member) \
+ case pn: /* D_ASSERT( tag_type(tag) == TT_INTEGER ); */ \
+ arg->member = get_unaligned((int *)(tags)); \
+ break;
+#define NL_INT64(pn, pr, member) \
+ case pn: /* D_ASSERT( tag_type(tag) == TT_INT64 ); */ \
+ arg->member = get_unaligned((u64 *)(tags)); \
+ break;
+#define NL_BIT(pn, pr, member) \
+ case pn: /* D_ASSERT( tag_type(tag) == TT_BIT ); */ \
+ arg->member = *(char *)(tags) ? 1 : 0; \
+ break;
+#define NL_STRING(pn, pr, member, len) \
+ case pn: /* D_ASSERT( tag_type(tag) == TT_STRING ); */ \
+ if (dlen > len) { \
+ dev_err(DEV, "arg too long: %s (%u wanted, max len: %u bytes)\n", \
+ #member, dlen, (unsigned int)len); \
+ return 0; \
+ } \
+ arg->member ## _len = dlen; \
+ memcpy(arg->member, tags, min_t(size_t, dlen, len)); \
+ break;
+#include "linux/drbd_nl.h"
+
+/* Generate the struct to tag_list functions */
+#define NL_PACKET(name, number, fields) \
+static unsigned short* \
+name ## _to_tags(struct drbd_conf *mdev, \
+ struct name *arg, unsigned short *tags) __attribute__ ((unused)); \
+static unsigned short* \
+name ## _to_tags(struct drbd_conf *mdev, \
+ struct name *arg, unsigned short *tags) \
+{ \
+ fields \
+ return tags; \
+}
+
+#define NL_INTEGER(pn, pr, member) \
+ put_unaligned(pn | pr | TT_INTEGER, tags++); \
+ put_unaligned(sizeof(int), tags++); \
+ put_unaligned(arg->member, (int *)tags); \
+ tags = (unsigned short *)((char *)tags+sizeof(int));
+#define NL_INT64(pn, pr, member) \
+ put_unaligned(pn | pr | TT_INT64, tags++); \
+ put_unaligned(sizeof(u64), tags++); \
+ put_unaligned(arg->member, (u64 *)tags); \
+ tags = (unsigned short *)((char *)tags+sizeof(u64));
+#define NL_BIT(pn, pr, member) \
+ put_unaligned(pn | pr | TT_BIT, tags++); \
+ put_unaligned(sizeof(char), tags++); \
+ *(char *)tags = arg->member; \
+ tags = (unsigned short *)((char *)tags+sizeof(char));
+#define NL_STRING(pn, pr, member, len) \
+ put_unaligned(pn | pr | TT_STRING, tags++); \
+ put_unaligned(arg->member ## _len, tags++); \
+ memcpy(tags, arg->member, arg->member ## _len); \
+ tags = (unsigned short *)((char *)tags + arg->member ## _len);
+#include "linux/drbd_nl.h"
+
+void drbd_bcast_ev_helper(struct drbd_conf *mdev, char *helper_name);
+void drbd_nl_send_reply(struct cn_msg *, int);
+
+int drbd_khelper(struct drbd_conf *mdev, char *cmd)
+{
+ char *envp[] = { "HOME=/",
+ "TERM=linux",
+ "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
+ NULL, /* Will be set to address family */
+ NULL, /* Will be set to address */
+ NULL };
+
+ char mb[12], af[20], ad[60], *afs;
+ char *argv[] = {usermode_helper, cmd, mb, NULL };
+ int ret;
+
+ snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
+
+ if (get_net_conf(mdev)) {
+ switch (((struct sockaddr *)mdev->net_conf->peer_addr)->sa_family) {
+ case AF_INET6:
+ afs = "ipv6";
+ snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI6",
+ &((struct sockaddr_in6 *)mdev->net_conf->peer_addr)->sin6_addr);
+ break;
+ case AF_INET:
+ afs = "ipv4";
+ snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4",
+ &((struct sockaddr_in *)mdev->net_conf->peer_addr)->sin_addr);
+ break;
+ default:
+ afs = "ssocks";
+ snprintf(ad, 60, "DRBD_PEER_ADDRESS=%pI4",
+ &((struct sockaddr_in *)mdev->net_conf->peer_addr)->sin_addr);
+ }
+ snprintf(af, 20, "DRBD_PEER_AF=%s", afs);
+ envp[3]=af;
+ envp[4]=ad;
+ put_net_conf(mdev);
+ }
+
+ dev_info(DEV, "helper command: %s %s %s\n", usermode_helper, cmd, mb);
+
+ drbd_bcast_ev_helper(mdev, cmd);
+ ret = call_usermodehelper(usermode_helper, argv, envp, 1);
+ if (ret)
+ dev_warn(DEV, "helper command: %s %s %s exit code %u (0x%x)\n",
+ usermode_helper, cmd, mb,
+ (ret >> 8) & 0xff, ret);
+ else
+ dev_info(DEV, "helper command: %s %s %s exit code %u (0x%x)\n",
+ usermode_helper, cmd, mb,
+ (ret >> 8) & 0xff, ret);
+
+ if (ret < 0) /* Ignore any ERRNOs we got. */
+ ret = 0;
+
+ return ret;
+}
+
+enum drbd_disk_state drbd_try_outdate_peer(struct drbd_conf *mdev)
+{
+ char *ex_to_string;
+ int r;
+ enum drbd_disk_state nps;
+ enum drbd_fencing_p fp;
+
+ D_ASSERT(mdev->state.pdsk == D_UNKNOWN);
+
+ if (get_ldev_if_state(mdev, D_CONSISTENT)) {
+ fp = mdev->ldev->dc.fencing;
+ put_ldev(mdev);
+ } else {
+ dev_warn(DEV, "Not fencing peer, I'm not even Consistent myself.\n");
+ return mdev->state.pdsk;
+ }
+
+ if (fp == FP_STONITH)
+ _drbd_request_state(mdev, NS(susp, 1), CS_WAIT_COMPLETE);
+
+ r = drbd_khelper(mdev, "fence-peer");
+
+ switch ((r>>8) & 0xff) {
+ case 3: /* peer is inconsistent */
+ ex_to_string = "peer is inconsistent or worse";
+ nps = D_INCONSISTENT;
+ break;
+ case 4: /* peer got outdated, or was already outdated */
+ ex_to_string = "peer was fenced";
+ nps = D_OUTDATED;
+ break;
+ case 5: /* peer was down */
+ if (mdev->state.disk == D_UP_TO_DATE) {
+ /* we will(have) create(d) a new UUID anyways... */
+ ex_to_string = "peer is unreachable, assumed to be dead";
+ nps = D_OUTDATED;
+ } else {
+ ex_to_string = "peer unreachable, doing nothing since disk != UpToDate";
+ nps = mdev->state.pdsk;
+ }
+ break;
+ case 6: /* Peer is primary, voluntarily outdate myself.
+ * This is useful when an unconnected R_SECONDARY is asked to
+ * become R_PRIMARY, but finds the other peer being active. */
+ ex_to_string = "peer is active";
+ dev_warn(DEV, "Peer is primary, outdating myself.\n");
+ nps = D_UNKNOWN;
+ _drbd_request_state(mdev, NS(disk, D_OUTDATED), CS_WAIT_COMPLETE);
+ break;
+ case 7:
+ if (fp != FP_STONITH)
+ dev_err(DEV, "fence-peer() = 7 && fencing != Stonith !!!\n");
+ ex_to_string = "peer was stonithed";
+ nps = D_OUTDATED;
+ break;
+ default:
+ /* The script is broken ... */
+ nps = D_UNKNOWN;
+ dev_err(DEV, "fence-peer helper broken, returned %d\n", (r>>8)&0xff);
+ return nps;
+ }
+
+ dev_info(DEV, "fence-peer helper returned %d (%s)\n",
+ (r>>8) & 0xff, ex_to_string);
+ return nps;
+}
+
+
+int drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force)
+{
+ const int max_tries = 4;
+ int r = 0;
+ int try = 0;
+ int forced = 0;
+ union drbd_state mask, val;
+ enum drbd_disk_state nps;
+
+ if (new_role == R_PRIMARY)
+ request_ping(mdev); /* Detect a dead peer ASAP */
+
+ mutex_lock(&mdev->state_mutex);
+
+ mask.i = 0; mask.role = R_MASK;
+ val.i = 0; val.role = new_role;
+
+ while (try++ < max_tries) {
+ r = _drbd_request_state(mdev, mask, val, CS_WAIT_COMPLETE);
+
+ /* in case we first succeeded to outdate,
+ * but now suddenly could establish a connection */
+ if (r == SS_CW_FAILED_BY_PEER && mask.pdsk != 0) {
+ val.pdsk = 0;
+ mask.pdsk = 0;
+ continue;
+ }
+
+ if (r == SS_NO_UP_TO_DATE_DISK && force &&
+ (mdev->state.disk == D_INCONSISTENT ||
+ mdev->state.disk == D_OUTDATED)) {
+ mask.disk = D_MASK;
+ val.disk = D_UP_TO_DATE;
+ forced = 1;
+ continue;
+ }
+
+ if (r == SS_NO_UP_TO_DATE_DISK &&
+ mdev->state.disk == D_CONSISTENT && mask.pdsk == 0) {
+ D_ASSERT(mdev->state.pdsk == D_UNKNOWN);
+ nps = drbd_try_outdate_peer(mdev);
+
+ if (nps == D_OUTDATED || nps == D_INCONSISTENT) {
+ val.disk = D_UP_TO_DATE;
+ mask.disk = D_MASK;
+ }
+
+ val.pdsk = nps;
+ mask.pdsk = D_MASK;
+
+ continue;
+ }
+
+ if (r == SS_NOTHING_TO_DO)
+ goto fail;
+ if (r == SS_PRIMARY_NOP && mask.pdsk == 0) {
+ nps = drbd_try_outdate_peer(mdev);
+
+ if (force && nps > D_OUTDATED) {
+ dev_warn(DEV, "Forced into split brain situation!\n");
+ nps = D_OUTDATED;
+ }
+
+ mask.pdsk = D_MASK;
+ val.pdsk = nps;
+
+ continue;
+ }
+ if (r == SS_TWO_PRIMARIES) {
+ /* Maybe the peer is detected as dead very soon...
+ retry at most once more in this case. */
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((mdev->net_conf->ping_timeo+1)*HZ/10);
+ if (try < max_tries)
+ try = max_tries - 1;
+ continue;
+ }
+ if (r < SS_SUCCESS) {
+ r = _drbd_request_state(mdev, mask, val,
+ CS_VERBOSE + CS_WAIT_COMPLETE);
+ if (r < SS_SUCCESS)
+ goto fail;
+ }
+ break;
+ }
+
+ if (r < SS_SUCCESS)
+ goto fail;
+
+ if (forced)
+ dev_warn(DEV, "Forced to consider local data as UpToDate!\n");
+
+ /* Wait until nothing is on the fly :) */
+ wait_event(mdev->misc_wait, atomic_read(&mdev->ap_pending_cnt) == 0);
+
+ if (new_role == R_SECONDARY) {
+ set_disk_ro(mdev->vdisk, TRUE);
+ if (get_ldev(mdev)) {
+ mdev->ldev->md.uuid[UI_CURRENT] &= ~(u64)1;
+ put_ldev(mdev);
+ }
+ } else {
+ if (get_net_conf(mdev)) {
+ mdev->net_conf->want_lose = 0;
+ put_net_conf(mdev);
+ }
+ set_disk_ro(mdev->vdisk, FALSE);
+ if (get_ldev(mdev)) {
+ if (((mdev->state.conn < C_CONNECTED ||
+ mdev->state.pdsk <= D_FAILED)
+ && mdev->ldev->md.uuid[UI_BITMAP] == 0) || forced)
+ drbd_uuid_new_current(mdev);
+
+ mdev->ldev->md.uuid[UI_CURRENT] |= (u64)1;
+ put_ldev(mdev);
+ }
+ }
+
+ if ((new_role == R_SECONDARY) && get_ldev(mdev)) {
+ drbd_al_to_on_disk_bm(mdev);
+ put_ldev(mdev);
+ }
+
+ if (mdev->state.conn >= C_WF_REPORT_PARAMS) {
+ /* if this was forced, we should consider sync */
+ if (forced)
+ drbd_send_uuids(mdev);
+ drbd_send_state(mdev);
+ }
+
+ drbd_md_sync(mdev);
+
+ kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE);
+ fail:
+ mutex_unlock(&mdev->state_mutex);
+ return r;
+}
+
+
+static int drbd_nl_primary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ struct primary primary_args;
+
+ memset(&primary_args, 0, sizeof(struct primary));
+ if (!primary_from_tags(mdev, nlp->tag_list, &primary_args)) {
+ reply->ret_code = ERR_MANDATORY_TAG;
+ return 0;
+ }
+
+ reply->ret_code =
+ drbd_set_role(mdev, R_PRIMARY, primary_args.overwrite_peer);
+
+ return 0;
+}
+
+static int drbd_nl_secondary(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ reply->ret_code = drbd_set_role(mdev, R_SECONDARY, 0);
+
+ return 0;
+}
+
+/* initializes the md.*_offset members, so we are able to find
+ * the on disk meta data */
+static void drbd_md_set_sector_offsets(struct drbd_conf *mdev,
+ struct drbd_backing_dev *bdev)
+{
+ sector_t md_size_sect = 0;
+ switch (bdev->dc.meta_dev_idx) {
+ default:
+ /* v07 style fixed size indexed meta data */
+ bdev->md.md_size_sect = MD_RESERVED_SECT;
+ bdev->md.md_offset = drbd_md_ss__(mdev, bdev);
+ bdev->md.al_offset = MD_AL_OFFSET;
+ bdev->md.bm_offset = MD_BM_OFFSET;
+ break;
+ case DRBD_MD_INDEX_FLEX_EXT:
+ /* just occupy the full device; unit: sectors */
+ bdev->md.md_size_sect = drbd_get_capacity(bdev->md_bdev);
+ bdev->md.md_offset = 0;
+ bdev->md.al_offset = MD_AL_OFFSET;
+ bdev->md.bm_offset = MD_BM_OFFSET;
+ break;
+ case DRBD_MD_INDEX_INTERNAL:
+ case DRBD_MD_INDEX_FLEX_INT:
+ bdev->md.md_offset = drbd_md_ss__(mdev, bdev);
+ /* al size is still fixed */
+ bdev->md.al_offset = -MD_AL_MAX_SIZE;
+ /* we need (slightly less than) ~ this much bitmap sectors: */
+ md_size_sect = drbd_get_capacity(bdev->backing_bdev);
+ md_size_sect = ALIGN(md_size_sect, BM_SECT_PER_EXT);
+ md_size_sect = BM_SECT_TO_EXT(md_size_sect);
+ md_size_sect = ALIGN(md_size_sect, 8);
+
+ /* plus the "drbd meta data super block",
+ * and the activity log; */
+ md_size_sect += MD_BM_OFFSET;
+
+ bdev->md.md_size_sect = md_size_sect;
+ /* bitmap offset is adjusted by 'super' block size */
+ bdev->md.bm_offset = -md_size_sect + MD_AL_OFFSET;
+ break;
+ }
+}
+
+char *ppsize(char *buf, unsigned long long size)
+{
+ /* Needs 9 bytes at max. */
+ static char units[] = { 'K', 'M', 'G', 'T', 'P', 'E' };
+ int base = 0;
+ while (size >= 10000) {
+ /* shift + round */
+ size = (size >> 10) + !!(size & (1<<9));
+ base++;
+ }
+ sprintf(buf, "%lu %cB", (long)size, units[base]);
+
+ return buf;
+}
+
+/* there is still a theoretical deadlock when called from receiver
+ * on an D_INCONSISTENT R_PRIMARY:
+ * remote READ does inc_ap_bio, receiver would need to receive answer
+ * packet from remote to dec_ap_bio again.
+ * receiver receive_sizes(), comes here,
+ * waits for ap_bio_cnt == 0. -> deadlock.
+ * but this cannot happen, actually, because:
+ * R_PRIMARY D_INCONSISTENT, and peer's disk is unreachable
+ * (not connected, or bad/no disk on peer):
+ * see drbd_fail_request_early, ap_bio_cnt is zero.
+ * R_PRIMARY D_INCONSISTENT, and C_SYNC_TARGET:
+ * peer may not initiate a resize.
+ */
+void drbd_suspend_io(struct drbd_conf *mdev)
+{
+ set_bit(SUSPEND_IO, &mdev->flags);
+ wait_event(mdev->misc_wait, !atomic_read(&mdev->ap_bio_cnt));
+}
+
+void drbd_resume_io(struct drbd_conf *mdev)
+{
+ clear_bit(SUSPEND_IO, &mdev->flags);
+ wake_up(&mdev->misc_wait);
+}
+
+/**
+ * drbd_determine_dev_size() - Sets the right device size obeying all constraints
+ * @mdev: DRBD device.
+ *
+ * Returns 0 on success, negative return values indicate errors.
+ * You should call drbd_md_sync() after calling this function.
+ */
+enum determine_dev_size drbd_determin_dev_size(struct drbd_conf *mdev) __must_hold(local)
+{
+ sector_t prev_first_sect, prev_size; /* previous meta location */
+ sector_t la_size;
+ sector_t size;
+ char ppb[10];
+
+ int md_moved, la_size_changed;
+ enum determine_dev_size rv = unchanged;
+
+ /* race:
+ * application request passes inc_ap_bio,
+ * but then cannot get an AL-reference.
+ * this function later may wait on ap_bio_cnt == 0. -> deadlock.
+ *
+ * to avoid that:
+ * Suspend IO right here.
+ * still lock the act_log to not trigger ASSERTs there.
+ */
+ drbd_suspend_io(mdev);
+
+ /* no wait necessary anymore, actually we could assert that */
+ wait_event(mdev->al_wait, lc_try_lock(mdev->act_log));
+
+ prev_first_sect = drbd_md_first_sector(mdev->ldev);
+ prev_size = mdev->ldev->md.md_size_sect;
+ la_size = mdev->ldev->md.la_size_sect;
+
+ /* TODO: should only be some assert here, not (re)init... */
+ drbd_md_set_sector_offsets(mdev, mdev->ldev);
+
+ size = drbd_new_dev_size(mdev, mdev->ldev);
+
+ if (drbd_get_capacity(mdev->this_bdev) != size ||
+ drbd_bm_capacity(mdev) != size) {
+ int err;
+ err = drbd_bm_resize(mdev, size);
+ if (unlikely(err)) {
+ /* currently there is only one error: ENOMEM! */
+ size = drbd_bm_capacity(mdev)>>1;
+ if (size == 0) {
+ dev_err(DEV, "OUT OF MEMORY! "
+ "Could not allocate bitmap!\n");
+ } else {
+ dev_err(DEV, "BM resizing failed. "
+ "Leaving size unchanged at size = %lu KB\n",
+ (unsigned long)size);
+ }
+ rv = dev_size_error;
+ }
+ /* racy, see comments above. */
+ drbd_set_my_capacity(mdev, size);
+ mdev->ldev->md.la_size_sect = size;
+ dev_info(DEV, "size = %s (%llu KB)\n", ppsize(ppb, size>>1),
+ (unsigned long long)size>>1);
+ }
+ if (rv == dev_size_error)
+ goto out;
+
+ la_size_changed = (la_size != mdev->ldev->md.la_size_sect);
+
+ md_moved = prev_first_sect != drbd_md_first_sector(mdev->ldev)
+ || prev_size != mdev->ldev->md.md_size_sect;
+
+ if (la_size_changed || md_moved) {
+ drbd_al_shrink(mdev); /* All extents inactive. */
+ dev_info(DEV, "Writing the whole bitmap, %s\n",
+ la_size_changed && md_moved ? "size changed and md moved" :
+ la_size_changed ? "size changed" : "md moved");
+ rv = drbd_bitmap_io(mdev, &drbd_bm_write, "size changed"); /* does drbd_resume_io() ! */
+ drbd_md_mark_dirty(mdev);
+ }
+
+ if (size > la_size)
+ rv = grew;
+ if (size < la_size)
+ rv = shrunk;
+out:
+ lc_unlock(mdev->act_log);
+ wake_up(&mdev->al_wait);
+ drbd_resume_io(mdev);
+
+ return rv;
+}
+
+sector_t
+drbd_new_dev_size(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
+{
+ sector_t p_size = mdev->p_size; /* partner's disk size. */
+ sector_t la_size = bdev->md.la_size_sect; /* last agreed size. */
+ sector_t m_size; /* my size */
+ sector_t u_size = bdev->dc.disk_size; /* size requested by user. */
+ sector_t size = 0;
+
+ m_size = drbd_get_max_capacity(bdev);
+
+ if (p_size && m_size) {
+ size = min_t(sector_t, p_size, m_size);
+ } else {
+ if (la_size) {
+ size = la_size;
+ if (m_size && m_size < size)
+ size = m_size;
+ if (p_size && p_size < size)
+ size = p_size;
+ } else {
+ if (m_size)
+ size = m_size;
+ if (p_size)
+ size = p_size;
+ }
+ }
+
+ if (size == 0)
+ dev_err(DEV, "Both nodes diskless!\n");
+
+ if (u_size) {
+ if (u_size > size)
+ dev_err(DEV, "Requested disk size is too big (%lu > %lu)\n",
+ (unsigned long)u_size>>1, (unsigned long)size>>1);
+ else
+ size = u_size;
+ }
+
+ return size;
+}
+
+/**
+ * drbd_check_al_size() - Ensures that the AL is of the right size
+ * @mdev: DRBD device.
+ *
+ * Returns -EBUSY if current al lru is still used, -ENOMEM when allocation
+ * failed, and 0 on success. You should call drbd_md_sync() after you called
+ * this function.
+ */
+static int drbd_check_al_size(struct drbd_conf *mdev)
+{
+ struct lru_cache *n, *t;
+ struct lc_element *e;
+ unsigned int in_use;
+ int i;
+
+ ERR_IF(mdev->sync_conf.al_extents < 7)
+ mdev->sync_conf.al_extents = 127;
+
+ if (mdev->act_log &&
+ mdev->act_log->nr_elements == mdev->sync_conf.al_extents)
+ return 0;
+
+ in_use = 0;
+ t = mdev->act_log;
+ n = lc_create("act_log", drbd_al_ext_cache,
+ mdev->sync_conf.al_extents, sizeof(struct lc_element), 0);
+
+ if (n == NULL) {
+ dev_err(DEV, "Cannot allocate act_log lru!\n");
+ return -ENOMEM;
+ }
+ spin_lock_irq(&mdev->al_lock);
+ if (t) {
+ for (i = 0; i < t->nr_elements; i++) {
+ e = lc_element_by_index(t, i);
+ if (e->refcnt)
+ dev_err(DEV, "refcnt(%d)==%d\n",
+ e->lc_number, e->refcnt);
+ in_use += e->refcnt;
+ }
+ }
+ if (!in_use)
+ mdev->act_log = n;
+ spin_unlock_irq(&mdev->al_lock);
+ if (in_use) {
+ dev_err(DEV, "Activity log still in use!\n");
+ lc_destroy(n);
+ return -EBUSY;
+ } else {
+ if (t)
+ lc_destroy(t);
+ }
+ drbd_md_mark_dirty(mdev); /* we changed mdev->act_log->nr_elemens */
+ return 0;
+}
+
+void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_seg_s) __must_hold(local)
+{
+ struct request_queue * const q = mdev->rq_queue;
+ struct request_queue * const b = mdev->ldev->backing_bdev->bd_disk->queue;
+ int max_segments = mdev->ldev->dc.max_bio_bvecs;
+
+ if (b->merge_bvec_fn && !mdev->ldev->dc.use_bmbv)
+ max_seg_s = PAGE_SIZE;
+
+ max_seg_s = min(queue_max_sectors(b) * queue_logical_block_size(b), max_seg_s);
+
+ blk_queue_max_sectors(q, max_seg_s >> 9);
+ blk_queue_max_phys_segments(q, max_segments ? max_segments : MAX_PHYS_SEGMENTS);
+ blk_queue_max_hw_segments(q, max_segments ? max_segments : MAX_HW_SEGMENTS);
+ blk_queue_max_segment_size(q, max_seg_s);
+ blk_queue_logical_block_size(q, 512);
+ blk_queue_segment_boundary(q, PAGE_SIZE-1);
+ blk_stack_limits(&q->limits, &b->limits, 0);
+
+ if (b->merge_bvec_fn)
+ dev_warn(DEV, "Backing device's merge_bvec_fn() = %p\n",
+ b->merge_bvec_fn);
+ dev_info(DEV, "max_segment_size ( = BIO size ) = %u\n", queue_max_segment_size(q));
+
+ if (q->backing_dev_info.ra_pages != b->backing_dev_info.ra_pages) {
+ dev_info(DEV, "Adjusting my ra_pages to backing device's (%lu -> %lu)\n",
+ q->backing_dev_info.ra_pages,
+ b->backing_dev_info.ra_pages);
+ q->backing_dev_info.ra_pages = b->backing_dev_info.ra_pages;
+ }
+}
+
+/* serialize deconfig (worker exiting, doing cleanup)
+ * and reconfig (drbdsetup disk, drbdsetup net)
+ *
+ * wait for a potentially exiting worker, then restart it,
+ * or start a new one.
+ */
+static void drbd_reconfig_start(struct drbd_conf *mdev)
+{
+ wait_event(mdev->state_wait, !test_and_set_bit(CONFIG_PENDING, &mdev->flags));
+ wait_event(mdev->state_wait, !test_bit(DEVICE_DYING, &mdev->flags));
+ drbd_thread_start(&mdev->worker);
+}
+
+/* if still unconfigured, stops worker again.
+ * if configured now, clears CONFIG_PENDING.
+ * wakes potential waiters */
+static void drbd_reconfig_done(struct drbd_conf *mdev)
+{
+ spin_lock_irq(&mdev->req_lock);
+ if (mdev->state.disk == D_DISKLESS &&
+ mdev->state.conn == C_STANDALONE &&
+ mdev->state.role == R_SECONDARY) {
+ set_bit(DEVICE_DYING, &mdev->flags);
+ drbd_thread_stop_nowait(&mdev->worker);
+ } else
+ clear_bit(CONFIG_PENDING, &mdev->flags);
+ spin_unlock_irq(&mdev->req_lock);
+ wake_up(&mdev->state_wait);
+}
+
+/* does always return 0;
+ * interesting return code is in reply->ret_code */
+static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ enum drbd_ret_codes retcode;
+ enum determine_dev_size dd;
+ sector_t max_possible_sectors;
+ sector_t min_md_device_sectors;
+ struct drbd_backing_dev *nbc = NULL; /* new_backing_conf */
+ struct inode *inode, *inode2;
+ struct lru_cache *resync_lru = NULL;
+ union drbd_state ns, os;
+ int rv;
+ int cp_discovered = 0;
+ int logical_block_size;
+
+ drbd_reconfig_start(mdev);
+
+ /* if you want to reconfigure, please tear down first */
+ if (mdev->state.disk > D_DISKLESS) {
+ retcode = ERR_DISK_CONFIGURED;
+ goto fail;
+ }
+
+ /* allocation not in the IO path, cqueue thread context */
+ nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL);
+ if (!nbc) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+
+ nbc->dc.disk_size = DRBD_DISK_SIZE_SECT_DEF;
+ nbc->dc.on_io_error = DRBD_ON_IO_ERROR_DEF;
+ nbc->dc.fencing = DRBD_FENCING_DEF;
+ nbc->dc.max_bio_bvecs = DRBD_MAX_BIO_BVECS_DEF;
+
+ if (!disk_conf_from_tags(mdev, nlp->tag_list, &nbc->dc)) {
+ retcode = ERR_MANDATORY_TAG;
+ goto fail;
+ }
+
+ if (nbc->dc.meta_dev_idx < DRBD_MD_INDEX_FLEX_INT) {
+ retcode = ERR_MD_IDX_INVALID;
+ goto fail;
+ }
+
+ nbc->lo_file = filp_open(nbc->dc.backing_dev, O_RDWR, 0);
+ if (IS_ERR(nbc->lo_file)) {
+ dev_err(DEV, "open(\"%s\") failed with %ld\n", nbc->dc.backing_dev,
+ PTR_ERR(nbc->lo_file));
+ nbc->lo_file = NULL;
+ retcode = ERR_OPEN_DISK;
+ goto fail;
+ }
+
+ inode = nbc->lo_file->f_dentry->d_inode;
+
+ if (!S_ISBLK(inode->i_mode)) {
+ retcode = ERR_DISK_NOT_BDEV;
+ goto fail;
+ }
+
+ nbc->md_file = filp_open(nbc->dc.meta_dev, O_RDWR, 0);
+ if (IS_ERR(nbc->md_file)) {
+ dev_err(DEV, "open(\"%s\") failed with %ld\n", nbc->dc.meta_dev,
+ PTR_ERR(nbc->md_file));
+ nbc->md_file = NULL;
+ retcode = ERR_OPEN_MD_DISK;
+ goto fail;
+ }
+
+ inode2 = nbc->md_file->f_dentry->d_inode;
+
+ if (!S_ISBLK(inode2->i_mode)) {
+ retcode = ERR_MD_NOT_BDEV;
+ goto fail;
+ }
+
+ nbc->backing_bdev = inode->i_bdev;
+ if (bd_claim(nbc->backing_bdev, mdev)) {
+ printk(KERN_ERR "drbd: bd_claim(%p,%p); failed [%p;%p;%u]\n",
+ nbc->backing_bdev, mdev,
+ nbc->backing_bdev->bd_holder,
+ nbc->backing_bdev->bd_contains->bd_holder,
+ nbc->backing_bdev->bd_holders);
+ retcode = ERR_BDCLAIM_DISK;
+ goto fail;
+ }
+
+ resync_lru = lc_create("resync", drbd_bm_ext_cache,
+ 61, sizeof(struct bm_extent),
+ offsetof(struct bm_extent, lce));
+ if (!resync_lru) {
+ retcode = ERR_NOMEM;
+ goto release_bdev_fail;
+ }
+
+ /* meta_dev_idx >= 0: external fixed size,
+ * possibly multiple drbd sharing one meta device.
+ * TODO in that case, paranoia check that [md_bdev, meta_dev_idx] is
+ * not yet used by some other drbd minor!
+ * (if you use drbd.conf + drbdadm,
+ * that should check it for you already; but if you don't, or someone
+ * fooled it, we need to double check here) */
+ nbc->md_bdev = inode2->i_bdev;
+ if (bd_claim(nbc->md_bdev, (nbc->dc.meta_dev_idx < 0) ? (void *)mdev
+ : (void *) drbd_m_holder)) {
+ retcode = ERR_BDCLAIM_MD_DISK;
+ goto release_bdev_fail;
+ }
+
+ if ((nbc->backing_bdev == nbc->md_bdev) !=
+ (nbc->dc.meta_dev_idx == DRBD_MD_INDEX_INTERNAL ||
+ nbc->dc.meta_dev_idx == DRBD_MD_INDEX_FLEX_INT)) {
+ retcode = ERR_MD_IDX_INVALID;
+ goto release_bdev2_fail;
+ }
+
+ /* RT - for drbd_get_max_capacity() DRBD_MD_INDEX_FLEX_INT */
+ drbd_md_set_sector_offsets(mdev, nbc);
+
+ if (drbd_get_max_capacity(nbc) < nbc->dc.disk_size) {
+ dev_err(DEV, "max capacity %llu smaller than disk size %llu\n",
+ (unsigned long long) drbd_get_max_capacity(nbc),
+ (unsigned long long) nbc->dc.disk_size);
+ retcode = ERR_DISK_TO_SMALL;
+ goto release_bdev2_fail;
+ }
+
+ if (nbc->dc.meta_dev_idx < 0) {
+ max_possible_sectors = DRBD_MAX_SECTORS_FLEX;
+ /* at least one MB, otherwise it does not make sense */
+ min_md_device_sectors = (2<<10);
+ } else {
+ max_possible_sectors = DRBD_MAX_SECTORS;
+ min_md_device_sectors = MD_RESERVED_SECT * (nbc->dc.meta_dev_idx + 1);
+ }
+
+ if (drbd_get_capacity(nbc->md_bdev) < min_md_device_sectors) {
+ retcode = ERR_MD_DISK_TO_SMALL;
+ dev_warn(DEV, "refusing attach: md-device too small, "
+ "at least %llu sectors needed for this meta-disk type\n",
+ (unsigned long long) min_md_device_sectors);
+ goto release_bdev2_fail;
+ }
+
+ /* Make sure the new disk is big enough
+ * (we may currently be R_PRIMARY with no local disk...) */
+ if (drbd_get_max_capacity(nbc) <
+ drbd_get_capacity(mdev->this_bdev)) {
+ retcode = ERR_DISK_TO_SMALL;
+ goto release_bdev2_fail;
+ }
+
+ nbc->known_size = drbd_get_capacity(nbc->backing_bdev);
+
+ if (nbc->known_size > max_possible_sectors) {
+ dev_warn(DEV, "==> truncating very big lower level device "
+ "to currently maximum possible %llu sectors <==\n",
+ (unsigned long long) max_possible_sectors);
+ if (nbc->dc.meta_dev_idx >= 0)
+ dev_warn(DEV, "==>> using internal or flexible "
+ "meta data may help <<==\n");
+ }
+
+ drbd_suspend_io(mdev);
+ /* also wait for the last barrier ack. */
+ wait_event(mdev->misc_wait, !atomic_read(&mdev->ap_pending_cnt));
+ /* and for any other previously queued work */
+ drbd_flush_workqueue(mdev);
+
+ retcode = _drbd_request_state(mdev, NS(disk, D_ATTACHING), CS_VERBOSE);
+ drbd_resume_io(mdev);
+ if (retcode < SS_SUCCESS)
+ goto release_bdev2_fail;
+
+ if (!get_ldev_if_state(mdev, D_ATTACHING))
+ goto force_diskless;
+
+ drbd_md_set_sector_offsets(mdev, nbc);
+
+ if (!mdev->bitmap) {
+ if (drbd_bm_init(mdev)) {
+ retcode = ERR_NOMEM;
+ goto force_diskless_dec;
+ }
+ }
+
+ retcode = drbd_md_read(mdev, nbc);
+ if (retcode != NO_ERROR)
+ goto force_diskless_dec;
+
+ if (mdev->state.conn < C_CONNECTED &&
+ mdev->state.role == R_PRIMARY &&
+ (mdev->ed_uuid & ~((u64)1)) != (nbc->md.uuid[UI_CURRENT] & ~((u64)1))) {
+ dev_err(DEV, "Can only attach to data with current UUID=%016llX\n",
+ (unsigned long long)mdev->ed_uuid);
+ retcode = ERR_DATA_NOT_CURRENT;
+ goto force_diskless_dec;
+ }
+
+ /* Since we are diskless, fix the activity log first... */
+ if (drbd_check_al_size(mdev)) {
+ retcode = ERR_NOMEM;
+ goto force_diskless_dec;
+ }
+
+ /* Prevent shrinking of consistent devices ! */
+ if (drbd_md_test_flag(nbc, MDF_CONSISTENT) &&
+ drbd_new_dev_size(mdev, nbc) < nbc->md.la_size_sect) {
+ dev_warn(DEV, "refusing to truncate a consistent device\n");
+ retcode = ERR_DISK_TO_SMALL;
+ goto force_diskless_dec;
+ }
+
+ if (!drbd_al_read_log(mdev, nbc)) {
+ retcode = ERR_IO_MD_DISK;
+ goto force_diskless_dec;
+ }
+
+ /* allocate a second IO page if logical_block_size != 512 */
+ logical_block_size = bdev_logical_block_size(nbc->md_bdev);
+ if (logical_block_size == 0)
+ logical_block_size = MD_SECTOR_SIZE;
+
+ if (logical_block_size != MD_SECTOR_SIZE) {
+ if (!mdev->md_io_tmpp) {
+ struct page *page = alloc_page(GFP_NOIO);
+ if (!page)
+ goto force_diskless_dec;
+
+ dev_warn(DEV, "Meta data's bdev logical_block_size = %d != %d\n",
+ logical_block_size, MD_SECTOR_SIZE);
+ dev_warn(DEV, "Workaround engaged (has performance impact).\n");
+
+ mdev->md_io_tmpp = page;
+ }
+ }
+
+ /* Reset the "barriers don't work" bits here, then force meta data to
+ * be written, to ensure we determine if barriers are supported. */
+ if (nbc->dc.no_md_flush)
+ set_bit(MD_NO_BARRIER, &mdev->flags);
+ else
+ clear_bit(MD_NO_BARRIER, &mdev->flags);
+
+ /* Point of no return reached.
+ * Devices and memory are no longer released by error cleanup below.
+ * now mdev takes over responsibility, and the state engine should
+ * clean it up somewhere. */
+ D_ASSERT(mdev->ldev == NULL);
+ mdev->ldev = nbc;
+ mdev->resync = resync_lru;
+ nbc = NULL;
+ resync_lru = NULL;
+
+ mdev->write_ordering = WO_bio_barrier;
+ drbd_bump_write_ordering(mdev, WO_bio_barrier);
+
+ if (drbd_md_test_flag(mdev->ldev, MDF_CRASHED_PRIMARY))
+ set_bit(CRASHED_PRIMARY, &mdev->flags);
+ else
+ clear_bit(CRASHED_PRIMARY, &mdev->flags);
+
+ if (drbd_md_test_flag(mdev->ldev, MDF_PRIMARY_IND)) {
+ set_bit(CRASHED_PRIMARY, &mdev->flags);
+ cp_discovered = 1;
+ }
+
+ mdev->send_cnt = 0;
+ mdev->recv_cnt = 0;
+ mdev->read_cnt = 0;
+ mdev->writ_cnt = 0;
+
+ drbd_setup_queue_param(mdev, DRBD_MAX_SEGMENT_SIZE);
+
+ /* If I am currently not R_PRIMARY,
+ * but meta data primary indicator is set,
+ * I just now recover from a hard crash,
+ * and have been R_PRIMARY before that crash.
+ *
+ * Now, if I had no connection before that crash
+ * (have been degraded R_PRIMARY), chances are that
+ * I won't find my peer now either.
+ *
+ * In that case, and _only_ in that case,
+ * we use the degr-wfc-timeout instead of the default,
+ * so we can automatically recover from a crash of a
+ * degraded but active "cluster" after a certain timeout.
+ */
+ clear_bit(USE_DEGR_WFC_T, &mdev->flags);
+ if (mdev->state.role != R_PRIMARY &&
+ drbd_md_test_flag(mdev->ldev, MDF_PRIMARY_IND) &&
+ !drbd_md_test_flag(mdev->ldev, MDF_CONNECTED_IND))
+ set_bit(USE_DEGR_WFC_T, &mdev->flags);
+
+ dd = drbd_determin_dev_size(mdev);
+ if (dd == dev_size_error) {
+ retcode = ERR_NOMEM_BITMAP;
+ goto force_diskless_dec;
+ } else if (dd == grew)
+ set_bit(RESYNC_AFTER_NEG, &mdev->flags);
+
+ if (drbd_md_test_flag(mdev->ldev, MDF_FULL_SYNC)) {
+ dev_info(DEV, "Assuming that all blocks are out of sync "
+ "(aka FullSync)\n");
+ if (drbd_bitmap_io(mdev, &drbd_bmio_set_n_write, "set_n_write from attaching")) {
+ retcode = ERR_IO_MD_DISK;
+ goto force_diskless_dec;
+ }
+ } else {
+ if (drbd_bitmap_io(mdev, &drbd_bm_read, "read from attaching") < 0) {
+ retcode = ERR_IO_MD_DISK;
+ goto force_diskless_dec;
+ }
+ }
+
+ if (cp_discovered) {
+ drbd_al_apply_to_bm(mdev);
+ drbd_al_to_on_disk_bm(mdev);
+ }
+
+ spin_lock_irq(&mdev->req_lock);
+ os = mdev->state;
+ ns.i = os.i;
+ /* If MDF_CONSISTENT is not set go into inconsistent state,
+ otherwise investigate MDF_WasUpToDate...
+ If MDF_WAS_UP_TO_DATE is not set go into D_OUTDATED disk state,
+ otherwise into D_CONSISTENT state.
+ */
+ if (drbd_md_test_flag(mdev->ldev, MDF_CONSISTENT)) {
+ if (drbd_md_test_flag(mdev->ldev, MDF_WAS_UP_TO_DATE))
+ ns.disk = D_CONSISTENT;
+ else
+ ns.disk = D_OUTDATED;
+ } else {
+ ns.disk = D_INCONSISTENT;
+ }
+
+ if (drbd_md_test_flag(mdev->ldev, MDF_PEER_OUT_DATED))
+ ns.pdsk = D_OUTDATED;
+
+ if ( ns.disk == D_CONSISTENT &&
+ (ns.pdsk == D_OUTDATED || mdev->ldev->dc.fencing == FP_DONT_CARE))
+ ns.disk = D_UP_TO_DATE;
+
+ /* All tests on MDF_PRIMARY_IND, MDF_CONNECTED_IND,
+ MDF_CONSISTENT and MDF_WAS_UP_TO_DATE must happen before
+ this point, because drbd_request_state() modifies these
+ flags. */
+
+ /* In case we are C_CONNECTED postpone any decision on the new disk
+ state after the negotiation phase. */
+ if (mdev->state.conn == C_CONNECTED) {
+ mdev->new_state_tmp.i = ns.i;
+ ns.i = os.i;
+ ns.disk = D_NEGOTIATING;
+ }
+
+ rv = _drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
+ ns = mdev->state;
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (rv < SS_SUCCESS)
+ goto force_diskless_dec;
+
+ if (mdev->state.role == R_PRIMARY)
+ mdev->ldev->md.uuid[UI_CURRENT] |= (u64)1;
+ else
+ mdev->ldev->md.uuid[UI_CURRENT] &= ~(u64)1;
+
+ drbd_md_mark_dirty(mdev);
+ drbd_md_sync(mdev);
+
+ kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE);
+ put_ldev(mdev);
+ reply->ret_code = retcode;
+ drbd_reconfig_done(mdev);
+ return 0;
+
+ force_diskless_dec:
+ put_ldev(mdev);
+ force_diskless:
+ drbd_force_state(mdev, NS(disk, D_DISKLESS));
+ drbd_md_sync(mdev);
+ release_bdev2_fail:
+ if (nbc)
+ bd_release(nbc->md_bdev);
+ release_bdev_fail:
+ if (nbc)
+ bd_release(nbc->backing_bdev);
+ fail:
+ if (nbc) {
+ if (nbc->lo_file)
+ fput(nbc->lo_file);
+ if (nbc->md_file)
+ fput(nbc->md_file);
+ kfree(nbc);
+ }
+ lc_destroy(resync_lru);
+
+ reply->ret_code = retcode;
+ drbd_reconfig_done(mdev);
+ return 0;
+}
+
+static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ reply->ret_code = drbd_request_state(mdev, NS(disk, D_DISKLESS));
+ return 0;
+}
+
+static int drbd_nl_net_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ int i, ns;
+ enum drbd_ret_codes retcode;
+ struct net_conf *new_conf = NULL;
+ struct crypto_hash *tfm = NULL;
+ struct crypto_hash *integrity_w_tfm = NULL;
+ struct crypto_hash *integrity_r_tfm = NULL;
+ struct hlist_head *new_tl_hash = NULL;
+ struct hlist_head *new_ee_hash = NULL;
+ struct drbd_conf *odev;
+ char hmac_name[CRYPTO_MAX_ALG_NAME];
+ void *int_dig_out = NULL;
+ void *int_dig_in = NULL;
+ void *int_dig_vv = NULL;
+ struct sockaddr *new_my_addr, *new_peer_addr, *taken_addr;
+
+ drbd_reconfig_start(mdev);
+
+ if (mdev->state.conn > C_STANDALONE) {
+ retcode = ERR_NET_CONFIGURED;
+ goto fail;
+ }
+
+ /* allocation not in the IO path, cqueue thread context */
+ new_conf = kmalloc(sizeof(struct net_conf), GFP_KERNEL);
+ if (!new_conf) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+
+ memset(new_conf, 0, sizeof(struct net_conf));
+ new_conf->timeout = DRBD_TIMEOUT_DEF;
+ new_conf->try_connect_int = DRBD_CONNECT_INT_DEF;
+ new_conf->ping_int = DRBD_PING_INT_DEF;
+ new_conf->max_epoch_size = DRBD_MAX_EPOCH_SIZE_DEF;
+ new_conf->max_buffers = DRBD_MAX_BUFFERS_DEF;
+ new_conf->unplug_watermark = DRBD_UNPLUG_WATERMARK_DEF;
+ new_conf->sndbuf_size = DRBD_SNDBUF_SIZE_DEF;
+ new_conf->rcvbuf_size = DRBD_RCVBUF_SIZE_DEF;
+ new_conf->ko_count = DRBD_KO_COUNT_DEF;
+ new_conf->after_sb_0p = DRBD_AFTER_SB_0P_DEF;
+ new_conf->after_sb_1p = DRBD_AFTER_SB_1P_DEF;
+ new_conf->after_sb_2p = DRBD_AFTER_SB_2P_DEF;
+ new_conf->want_lose = 0;
+ new_conf->two_primaries = 0;
+ new_conf->wire_protocol = DRBD_PROT_C;
+ new_conf->ping_timeo = DRBD_PING_TIMEO_DEF;
+ new_conf->rr_conflict = DRBD_RR_CONFLICT_DEF;
+
+ if (!net_conf_from_tags(mdev, nlp->tag_list, new_conf)) {
+ retcode = ERR_MANDATORY_TAG;
+ goto fail;
+ }
+
+ if (new_conf->two_primaries
+ && (new_conf->wire_protocol != DRBD_PROT_C)) {
+ retcode = ERR_NOT_PROTO_C;
+ goto fail;
+ };
+
+ if (mdev->state.role == R_PRIMARY && new_conf->want_lose) {
+ retcode = ERR_DISCARD;
+ goto fail;
+ }
+
+ retcode = NO_ERROR;
+
+ new_my_addr = (struct sockaddr *)&new_conf->my_addr;
+ new_peer_addr = (struct sockaddr *)&new_conf->peer_addr;
+ for (i = 0; i < minor_count; i++) {
+ odev = minor_to_mdev(i);
+ if (!odev || odev == mdev)
+ continue;
+ if (get_net_conf(odev)) {
+ taken_addr = (struct sockaddr *)&odev->net_conf->my_addr;
+ if (new_conf->my_addr_len == odev->net_conf->my_addr_len &&
+ !memcmp(new_my_addr, taken_addr, new_conf->my_addr_len))
+ retcode = ERR_LOCAL_ADDR;
+
+ taken_addr = (struct sockaddr *)&odev->net_conf->peer_addr;
+ if (new_conf->peer_addr_len == odev->net_conf->peer_addr_len &&
+ !memcmp(new_peer_addr, taken_addr, new_conf->peer_addr_len))
+ retcode = ERR_PEER_ADDR;
+
+ put_net_conf(odev);
+ if (retcode != NO_ERROR)
+ goto fail;
+ }
+ }
+
+ if (new_conf->cram_hmac_alg[0] != 0) {
+ snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
+ new_conf->cram_hmac_alg);
+ tfm = crypto_alloc_hash(hmac_name, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ tfm = NULL;
+ retcode = ERR_AUTH_ALG;
+ goto fail;
+ }
+
+ if (crypto_tfm_alg_type(crypto_hash_tfm(tfm))
+ != CRYPTO_ALG_TYPE_HASH) {
+ retcode = ERR_AUTH_ALG_ND;
+ goto fail;
+ }
+ }
+
+ if (new_conf->integrity_alg[0]) {
+ integrity_w_tfm = crypto_alloc_hash(new_conf->integrity_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(integrity_w_tfm)) {
+ integrity_w_tfm = NULL;
+ retcode=ERR_INTEGRITY_ALG;
+ goto fail;
+ }
+
+ if (!drbd_crypto_is_hash(crypto_hash_tfm(integrity_w_tfm))) {
+ retcode=ERR_INTEGRITY_ALG_ND;
+ goto fail;
+ }
+
+ integrity_r_tfm = crypto_alloc_hash(new_conf->integrity_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(integrity_r_tfm)) {
+ integrity_r_tfm = NULL;
+ retcode=ERR_INTEGRITY_ALG;
+ goto fail;
+ }
+ }
+
+ ns = new_conf->max_epoch_size/8;
+ if (mdev->tl_hash_s != ns) {
+ new_tl_hash = kzalloc(ns*sizeof(void *), GFP_KERNEL);
+ if (!new_tl_hash) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ }
+
+ ns = new_conf->max_buffers/8;
+ if (new_conf->two_primaries && (mdev->ee_hash_s != ns)) {
+ new_ee_hash = kzalloc(ns*sizeof(void *), GFP_KERNEL);
+ if (!new_ee_hash) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ }
+
+ ((char *)new_conf->shared_secret)[SHARED_SECRET_MAX-1] = 0;
+
+ if (integrity_w_tfm) {
+ i = crypto_hash_digestsize(integrity_w_tfm);
+ int_dig_out = kmalloc(i, GFP_KERNEL);
+ if (!int_dig_out) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ int_dig_in = kmalloc(i, GFP_KERNEL);
+ if (!int_dig_in) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ int_dig_vv = kmalloc(i, GFP_KERNEL);
+ if (!int_dig_vv) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ }
+
+ if (!mdev->bitmap) {
+ if(drbd_bm_init(mdev)) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ }
+
+ spin_lock_irq(&mdev->req_lock);
+ if (mdev->net_conf != NULL) {
+ retcode = ERR_NET_CONFIGURED;
+ spin_unlock_irq(&mdev->req_lock);
+ goto fail;
+ }
+ mdev->net_conf = new_conf;
+
+ mdev->send_cnt = 0;
+ mdev->recv_cnt = 0;
+
+ if (new_tl_hash) {
+ kfree(mdev->tl_hash);
+ mdev->tl_hash_s = mdev->net_conf->max_epoch_size/8;
+ mdev->tl_hash = new_tl_hash;
+ }
+
+ if (new_ee_hash) {
+ kfree(mdev->ee_hash);
+ mdev->ee_hash_s = mdev->net_conf->max_buffers/8;
+ mdev->ee_hash = new_ee_hash;
+ }
+
+ crypto_free_hash(mdev->cram_hmac_tfm);
+ mdev->cram_hmac_tfm = tfm;
+
+ crypto_free_hash(mdev->integrity_w_tfm);
+ mdev->integrity_w_tfm = integrity_w_tfm;
+
+ crypto_free_hash(mdev->integrity_r_tfm);
+ mdev->integrity_r_tfm = integrity_r_tfm;
+
+ kfree(mdev->int_dig_out);
+ kfree(mdev->int_dig_in);
+ kfree(mdev->int_dig_vv);
+ mdev->int_dig_out=int_dig_out;
+ mdev->int_dig_in=int_dig_in;
+ mdev->int_dig_vv=int_dig_vv;
+ spin_unlock_irq(&mdev->req_lock);
+
+ retcode = _drbd_request_state(mdev, NS(conn, C_UNCONNECTED), CS_VERBOSE);
+
+ kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE);
+ reply->ret_code = retcode;
+ drbd_reconfig_done(mdev);
+ return 0;
+
+fail:
+ kfree(int_dig_out);
+ kfree(int_dig_in);
+ kfree(int_dig_vv);
+ crypto_free_hash(tfm);
+ crypto_free_hash(integrity_w_tfm);
+ crypto_free_hash(integrity_r_tfm);
+ kfree(new_tl_hash);
+ kfree(new_ee_hash);
+ kfree(new_conf);
+
+ reply->ret_code = retcode;
+ drbd_reconfig_done(mdev);
+ return 0;
+}
+
+static int drbd_nl_disconnect(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ int retcode;
+
+ retcode = _drbd_request_state(mdev, NS(conn, C_DISCONNECTING), CS_ORDERED);
+
+ if (retcode == SS_NOTHING_TO_DO)
+ goto done;
+ else if (retcode == SS_ALREADY_STANDALONE)
+ goto done;
+ else if (retcode == SS_PRIMARY_NOP) {
+ /* Our statche checking code wants to see the peer outdated. */
+ retcode = drbd_request_state(mdev, NS2(conn, C_DISCONNECTING,
+ pdsk, D_OUTDATED));
+ } else if (retcode == SS_CW_FAILED_BY_PEER) {
+ /* The peer probably wants to see us outdated. */
+ retcode = _drbd_request_state(mdev, NS2(conn, C_DISCONNECTING,
+ disk, D_OUTDATED),
+ CS_ORDERED);
+ if (retcode == SS_IS_DISKLESS || retcode == SS_LOWER_THAN_OUTDATED) {
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ retcode = SS_SUCCESS;
+ }
+ }
+
+ if (retcode < SS_SUCCESS)
+ goto fail;
+
+ if (wait_event_interruptible(mdev->state_wait,
+ mdev->state.conn != C_DISCONNECTING)) {
+ /* Do not test for mdev->state.conn == C_STANDALONE, since
+ someone else might connect us in the mean time! */
+ retcode = ERR_INTR;
+ goto fail;
+ }
+
+ done:
+ retcode = NO_ERROR;
+ fail:
+ drbd_md_sync(mdev);
+ reply->ret_code = retcode;
+ return 0;
+}
+
+void resync_after_online_grow(struct drbd_conf *mdev)
+{
+ int iass; /* I am sync source */
+
+ dev_info(DEV, "Resync of new storage after online grow\n");
+ if (mdev->state.role != mdev->state.peer)
+ iass = (mdev->state.role == R_PRIMARY);
+ else
+ iass = test_bit(DISCARD_CONCURRENT, &mdev->flags);
+
+ if (iass)
+ drbd_start_resync(mdev, C_SYNC_SOURCE);
+ else
+ _drbd_request_state(mdev, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE + CS_SERIALIZE);
+}
+
+static int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ struct resize rs;
+ int retcode = NO_ERROR;
+ int ldsc = 0; /* local disk size changed */
+ enum determine_dev_size dd;
+
+ memset(&rs, 0, sizeof(struct resize));
+ if (!resize_from_tags(mdev, nlp->tag_list, &rs)) {
+ retcode = ERR_MANDATORY_TAG;
+ goto fail;
+ }
+
+ if (mdev->state.conn > C_CONNECTED) {
+ retcode = ERR_RESIZE_RESYNC;
+ goto fail;
+ }
+
+ if (mdev->state.role == R_SECONDARY &&
+ mdev->state.peer == R_SECONDARY) {
+ retcode = ERR_NO_PRIMARY;
+ goto fail;
+ }
+
+ if (!get_ldev(mdev)) {
+ retcode = ERR_NO_DISK;
+ goto fail;
+ }
+
+ if (mdev->ldev->known_size != drbd_get_capacity(mdev->ldev->backing_bdev)) {
+ mdev->ldev->known_size = drbd_get_capacity(mdev->ldev->backing_bdev);
+ ldsc = 1;
+ }
+
+ mdev->ldev->dc.disk_size = (sector_t)rs.resize_size;
+ dd = drbd_determin_dev_size(mdev);
+ drbd_md_sync(mdev);
+ put_ldev(mdev);
+ if (dd == dev_size_error) {
+ retcode = ERR_NOMEM_BITMAP;
+ goto fail;
+ }
+
+ if (mdev->state.conn == C_CONNECTED && (dd != unchanged || ldsc)) {
+ if (dd == grew)
+ set_bit(RESIZE_PENDING, &mdev->flags);
+
+ drbd_send_uuids(mdev);
+ drbd_send_sizes(mdev, 1);
+ }
+
+ fail:
+ reply->ret_code = retcode;
+ return 0;
+}
+
+static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ int retcode = NO_ERROR;
+ int err;
+ int ovr; /* online verify running */
+ int rsr; /* re-sync running */
+ struct crypto_hash *verify_tfm = NULL;
+ struct crypto_hash *csums_tfm = NULL;
+ struct syncer_conf sc;
+ cpumask_var_t new_cpu_mask;
+
+ if (!zalloc_cpumask_var(&new_cpu_mask, GFP_KERNEL)) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+
+ if (nlp->flags & DRBD_NL_SET_DEFAULTS) {
+ memset(&sc, 0, sizeof(struct syncer_conf));
+ sc.rate = DRBD_RATE_DEF;
+ sc.after = DRBD_AFTER_DEF;
+ sc.al_extents = DRBD_AL_EXTENTS_DEF;
+ } else
+ memcpy(&sc, &mdev->sync_conf, sizeof(struct syncer_conf));
+
+ if (!syncer_conf_from_tags(mdev, nlp->tag_list, &sc)) {
+ retcode = ERR_MANDATORY_TAG;
+ goto fail;
+ }
+
+ /* re-sync running */
+ rsr = ( mdev->state.conn == C_SYNC_SOURCE ||
+ mdev->state.conn == C_SYNC_TARGET ||
+ mdev->state.conn == C_PAUSED_SYNC_S ||
+ mdev->state.conn == C_PAUSED_SYNC_T );
+
+ if (rsr && strcmp(sc.csums_alg, mdev->sync_conf.csums_alg)) {
+ retcode = ERR_CSUMS_RESYNC_RUNNING;
+ goto fail;
+ }
+
+ if (!rsr && sc.csums_alg[0]) {
+ csums_tfm = crypto_alloc_hash(sc.csums_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(csums_tfm)) {
+ csums_tfm = NULL;
+ retcode = ERR_CSUMS_ALG;
+ goto fail;
+ }
+
+ if (!drbd_crypto_is_hash(crypto_hash_tfm(csums_tfm))) {
+ retcode = ERR_CSUMS_ALG_ND;
+ goto fail;
+ }
+ }
+
+ /* online verify running */
+ ovr = (mdev->state.conn == C_VERIFY_S || mdev->state.conn == C_VERIFY_T);
+
+ if (ovr) {
+ if (strcmp(sc.verify_alg, mdev->sync_conf.verify_alg)) {
+ retcode = ERR_VERIFY_RUNNING;
+ goto fail;
+ }
+ }
+
+ if (!ovr && sc.verify_alg[0]) {
+ verify_tfm = crypto_alloc_hash(sc.verify_alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(verify_tfm)) {
+ verify_tfm = NULL;
+ retcode = ERR_VERIFY_ALG;
+ goto fail;
+ }
+
+ if (!drbd_crypto_is_hash(crypto_hash_tfm(verify_tfm))) {
+ retcode = ERR_VERIFY_ALG_ND;
+ goto fail;
+ }
+ }
+
+ /* silently ignore cpu mask on UP kernel */
+ if (nr_cpu_ids > 1 && sc.cpu_mask[0] != 0) {
+ err = __bitmap_parse(sc.cpu_mask, 32, 0,
+ cpumask_bits(new_cpu_mask), nr_cpu_ids);
+ if (err) {
+ dev_warn(DEV, "__bitmap_parse() failed with %d\n", err);
+ retcode = ERR_CPU_MASK_PARSE;
+ goto fail;
+ }
+ }
+
+ ERR_IF (sc.rate < 1) sc.rate = 1;
+ ERR_IF (sc.al_extents < 7) sc.al_extents = 127; /* arbitrary minimum */
+#define AL_MAX ((MD_AL_MAX_SIZE-1) * AL_EXTENTS_PT)
+ if (sc.al_extents > AL_MAX) {
+ dev_err(DEV, "sc.al_extents > %d\n", AL_MAX);
+ sc.al_extents = AL_MAX;
+ }
+#undef AL_MAX
+
+ /* most sanity checks done, try to assign the new sync-after
+ * dependency. need to hold the global lock in there,
+ * to avoid a race in the dependency loop check. */
+ retcode = drbd_alter_sa(mdev, sc.after);
+ if (retcode != NO_ERROR)
+ goto fail;
+
+ /* ok, assign the rest of it as well.
+ * lock against receive_SyncParam() */
+ spin_lock(&mdev->peer_seq_lock);
+ mdev->sync_conf = sc;
+
+ if (!rsr) {
+ crypto_free_hash(mdev->csums_tfm);
+ mdev->csums_tfm = csums_tfm;
+ csums_tfm = NULL;
+ }
+
+ if (!ovr) {
+ crypto_free_hash(mdev->verify_tfm);
+ mdev->verify_tfm = verify_tfm;
+ verify_tfm = NULL;
+ }
+ spin_unlock(&mdev->peer_seq_lock);
+
+ if (get_ldev(mdev)) {
+ wait_event(mdev->al_wait, lc_try_lock(mdev->act_log));
+ drbd_al_shrink(mdev);
+ err = drbd_check_al_size(mdev);
+ lc_unlock(mdev->act_log);
+ wake_up(&mdev->al_wait);
+
+ put_ldev(mdev);
+ drbd_md_sync(mdev);
+
+ if (err) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ }
+
+ if (mdev->state.conn >= C_CONNECTED)
+ drbd_send_sync_param(mdev, &sc);
+
+ if (!cpumask_equal(mdev->cpu_mask, new_cpu_mask)) {
+ cpumask_copy(mdev->cpu_mask, new_cpu_mask);
+ drbd_calc_cpu_mask(mdev);
+ mdev->receiver.reset_cpu_mask = 1;
+ mdev->asender.reset_cpu_mask = 1;
+ mdev->worker.reset_cpu_mask = 1;
+ }
+
+ kobject_uevent(&disk_to_dev(mdev->vdisk)->kobj, KOBJ_CHANGE);
+fail:
+ free_cpumask_var(new_cpu_mask);
+ crypto_free_hash(csums_tfm);
+ crypto_free_hash(verify_tfm);
+ reply->ret_code = retcode;
+ return 0;
+}
+
+static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ int retcode;
+
+ retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
+
+ if (retcode < SS_SUCCESS && retcode != SS_NEED_CONNECTION)
+ retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
+
+ while (retcode == SS_NEED_CONNECTION) {
+ spin_lock_irq(&mdev->req_lock);
+ if (mdev->state.conn < C_CONNECTED)
+ retcode = _drbd_set_state(_NS(mdev, disk, D_INCONSISTENT), CS_VERBOSE, NULL);
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (retcode != SS_NEED_CONNECTION)
+ break;
+
+ retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
+ }
+
+ reply->ret_code = retcode;
+ return 0;
+}
+
+static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+
+ reply->ret_code = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S));
+
+ return 0;
+}
+
+static int drbd_nl_pause_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ int retcode = NO_ERROR;
+
+ if (drbd_request_state(mdev, NS(user_isp, 1)) == SS_NOTHING_TO_DO)
+ retcode = ERR_PAUSE_IS_SET;
+
+ reply->ret_code = retcode;
+ return 0;
+}
+
+static int drbd_nl_resume_sync(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ int retcode = NO_ERROR;
+
+ if (drbd_request_state(mdev, NS(user_isp, 0)) == SS_NOTHING_TO_DO)
+ retcode = ERR_PAUSE_IS_CLEAR;
+
+ reply->ret_code = retcode;
+ return 0;
+}
+
+static int drbd_nl_suspend_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ reply->ret_code = drbd_request_state(mdev, NS(susp, 1));
+
+ return 0;
+}
+
+static int drbd_nl_resume_io(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ reply->ret_code = drbd_request_state(mdev, NS(susp, 0));
+ return 0;
+}
+
+static int drbd_nl_outdate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ reply->ret_code = drbd_request_state(mdev, NS(disk, D_OUTDATED));
+ return 0;
+}
+
+static int drbd_nl_get_config(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ unsigned short *tl;
+
+ tl = reply->tag_list;
+
+ if (get_ldev(mdev)) {
+ tl = disk_conf_to_tags(mdev, &mdev->ldev->dc, tl);
+ put_ldev(mdev);
+ }
+
+ if (get_net_conf(mdev)) {
+ tl = net_conf_to_tags(mdev, mdev->net_conf, tl);
+ put_net_conf(mdev);
+ }
+ tl = syncer_conf_to_tags(mdev, &mdev->sync_conf, tl);
+
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ return (int)((char *)tl - (char *)reply->tag_list);
+}
+
+static int drbd_nl_get_state(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ unsigned short *tl = reply->tag_list;
+ union drbd_state s = mdev->state;
+ unsigned long rs_left;
+ unsigned int res;
+
+ tl = get_state_to_tags(mdev, (struct get_state *)&s, tl);
+
+ /* no local ref, no bitmap, no syncer progress. */
+ if (s.conn >= C_SYNC_SOURCE && s.conn <= C_PAUSED_SYNC_T) {
+ if (get_ldev(mdev)) {
+ drbd_get_syncer_progress(mdev, &rs_left, &res);
+ tl = tl_add_int(tl, T_sync_progress, &res);
+ put_ldev(mdev);
+ }
+ }
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ return (int)((char *)tl - (char *)reply->tag_list);
+}
+
+static int drbd_nl_get_uuids(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ unsigned short *tl;
+
+ tl = reply->tag_list;
+
+ if (get_ldev(mdev)) {
+ tl = tl_add_blob(tl, T_uuids, mdev->ldev->md.uuid, UI_SIZE*sizeof(u64));
+ tl = tl_add_int(tl, T_uuids_flags, &mdev->ldev->md.flags);
+ put_ldev(mdev);
+ }
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ return (int)((char *)tl - (char *)reply->tag_list);
+}
+
+/**
+ * drbd_nl_get_timeout_flag() - Used by drbdsetup to find out which timeout value to use
+ * @mdev: DRBD device.
+ * @nlp: Netlink/connector packet from drbdsetup
+ * @reply: Reply packet for drbdsetup
+ */
+static int drbd_nl_get_timeout_flag(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ unsigned short *tl;
+ char rv;
+
+ tl = reply->tag_list;
+
+ rv = mdev->state.pdsk == D_OUTDATED ? UT_PEER_OUTDATED :
+ test_bit(USE_DEGR_WFC_T, &mdev->flags) ? UT_DEGRADED : UT_DEFAULT;
+
+ tl = tl_add_blob(tl, T_use_degraded, &rv, sizeof(rv));
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ return (int)((char *)tl - (char *)reply->tag_list);
+}
+
+static int drbd_nl_start_ov(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ /* default to resume from last known position, if possible */
+ struct start_ov args =
+ { .start_sector = mdev->ov_start_sector };
+
+ if (!start_ov_from_tags(mdev, nlp->tag_list, &args)) {
+ reply->ret_code = ERR_MANDATORY_TAG;
+ return 0;
+ }
+ /* w_make_ov_request expects position to be aligned */
+ mdev->ov_start_sector = args.start_sector & ~BM_SECT_PER_BIT;
+ reply->ret_code = drbd_request_state(mdev,NS(conn,C_VERIFY_S));
+ return 0;
+}
+
+
+static int drbd_nl_new_c_uuid(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
+ struct drbd_nl_cfg_reply *reply)
+{
+ int retcode = NO_ERROR;
+ int skip_initial_sync = 0;
+ int err;
+
+ struct new_c_uuid args;
+
+ memset(&args, 0, sizeof(struct new_c_uuid));
+ if (!new_c_uuid_from_tags(mdev, nlp->tag_list, &args)) {
+ reply->ret_code = ERR_MANDATORY_TAG;
+ return 0;
+ }
+
+ mutex_lock(&mdev->state_mutex); /* Protects us against serialized state changes. */
+
+ if (!get_ldev(mdev)) {
+ retcode = ERR_NO_DISK;
+ goto out;
+ }
+
+ /* this is "skip initial sync", assume to be clean */
+ if (mdev->state.conn == C_CONNECTED && mdev->agreed_pro_version >= 90 &&
+ mdev->ldev->md.uuid[UI_CURRENT] == UUID_JUST_CREATED && args.clear_bm) {
+ dev_info(DEV, "Preparing to skip initial sync\n");
+ skip_initial_sync = 1;
+ } else if (mdev->state.conn != C_STANDALONE) {
+ retcode = ERR_CONNECTED;
+ goto out_dec;
+ }
+
+ drbd_uuid_set(mdev, UI_BITMAP, 0); /* Rotate UI_BITMAP to History 1, etc... */
+ drbd_uuid_new_current(mdev); /* New current, previous to UI_BITMAP */
+
+ if (args.clear_bm) {
+ err = drbd_bitmap_io(mdev, &drbd_bmio_clear_n_write, "clear_n_write from new_c_uuid");
+ if (err) {
+ dev_err(DEV, "Writing bitmap failed with %d\n",err);
+ retcode = ERR_IO_MD_DISK;
+ }
+ if (skip_initial_sync) {
+ drbd_send_uuids_skip_initial_sync(mdev);
+ _drbd_uuid_set(mdev, UI_BITMAP, 0);
+ spin_lock_irq(&mdev->req_lock);
+ _drbd_set_state(_NS2(mdev, disk, D_UP_TO_DATE, pdsk, D_UP_TO_DATE),
+ CS_VERBOSE, NULL);
+ spin_unlock_irq(&mdev->req_lock);
+ }
+ }
+
+ drbd_md_sync(mdev);
+out_dec:
+ put_ldev(mdev);
+out:
+ mutex_unlock(&mdev->state_mutex);
+
+ reply->ret_code = retcode;
+ return 0;
+}
+
+static struct drbd_conf *ensure_mdev(struct drbd_nl_cfg_req *nlp)
+{
+ struct drbd_conf *mdev;
+
+ if (nlp->drbd_minor >= minor_count)
+ return NULL;
+
+ mdev = minor_to_mdev(nlp->drbd_minor);
+
+ if (!mdev && (nlp->flags & DRBD_NL_CREATE_DEVICE)) {
+ struct gendisk *disk = NULL;
+ mdev = drbd_new_device(nlp->drbd_minor);
+
+ spin_lock_irq(&drbd_pp_lock);
+ if (minor_table[nlp->drbd_minor] == NULL) {
+ minor_table[nlp->drbd_minor] = mdev;
+ disk = mdev->vdisk;
+ mdev = NULL;
+ } /* else: we lost the race */
+ spin_unlock_irq(&drbd_pp_lock);
+
+ if (disk) /* we won the race above */
+ /* in case we ever add a drbd_delete_device(),
+ * don't forget the del_gendisk! */
+ add_disk(disk);
+ else /* we lost the race above */
+ drbd_free_mdev(mdev);
+
+ mdev = minor_to_mdev(nlp->drbd_minor);
+ }
+
+ return mdev;
+}
+
+struct cn_handler_struct {
+ int (*function)(struct drbd_conf *,
+ struct drbd_nl_cfg_req *,
+ struct drbd_nl_cfg_reply *);
+ int reply_body_size;
+};
+
+static struct cn_handler_struct cnd_table[] = {
+ [ P_primary ] = { &drbd_nl_primary, 0 },
+ [ P_secondary ] = { &drbd_nl_secondary, 0 },
+ [ P_disk_conf ] = { &drbd_nl_disk_conf, 0 },
+ [ P_detach ] = { &drbd_nl_detach, 0 },
+ [ P_net_conf ] = { &drbd_nl_net_conf, 0 },
+ [ P_disconnect ] = { &drbd_nl_disconnect, 0 },
+ [ P_resize ] = { &drbd_nl_resize, 0 },
+ [ P_syncer_conf ] = { &drbd_nl_syncer_conf, 0 },
+ [ P_invalidate ] = { &drbd_nl_invalidate, 0 },
+ [ P_invalidate_peer ] = { &drbd_nl_invalidate_peer, 0 },
+ [ P_pause_sync ] = { &drbd_nl_pause_sync, 0 },
+ [ P_resume_sync ] = { &drbd_nl_resume_sync, 0 },
+ [ P_suspend_io ] = { &drbd_nl_suspend_io, 0 },
+ [ P_resume_io ] = { &drbd_nl_resume_io, 0 },
+ [ P_outdate ] = { &drbd_nl_outdate, 0 },
+ [ P_get_config ] = { &drbd_nl_get_config,
+ sizeof(struct syncer_conf_tag_len_struct) +
+ sizeof(struct disk_conf_tag_len_struct) +
+ sizeof(struct net_conf_tag_len_struct) },
+ [ P_get_state ] = { &drbd_nl_get_state,
+ sizeof(struct get_state_tag_len_struct) +
+ sizeof(struct sync_progress_tag_len_struct) },
+ [ P_get_uuids ] = { &drbd_nl_get_uuids,
+ sizeof(struct get_uuids_tag_len_struct) },
+ [ P_get_timeout_flag ] = { &drbd_nl_get_timeout_flag,
+ sizeof(struct get_timeout_flag_tag_len_struct)},
+ [ P_start_ov ] = { &drbd_nl_start_ov, 0 },
+ [ P_new_c_uuid ] = { &drbd_nl_new_c_uuid, 0 },
+};
+
+static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp)
+{
+ struct drbd_nl_cfg_req *nlp = (struct drbd_nl_cfg_req *)req->data;
+ struct cn_handler_struct *cm;
+ struct cn_msg *cn_reply;
+ struct drbd_nl_cfg_reply *reply;
+ struct drbd_conf *mdev;
+ int retcode, rr;
+ int reply_size = sizeof(struct cn_msg)
+ + sizeof(struct drbd_nl_cfg_reply)
+ + sizeof(short int);
+
+ if (!try_module_get(THIS_MODULE)) {
+ printk(KERN_ERR "drbd: try_module_get() failed!\n");
+ return;
+ }
+
+ if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) {
+ retcode = ERR_PERM;
+ goto fail;
+ }
+
+ mdev = ensure_mdev(nlp);
+ if (!mdev) {
+ retcode = ERR_MINOR_INVALID;
+ goto fail;
+ }
+
+ if (nlp->packet_type >= P_nl_after_last_packet) {
+ retcode = ERR_PACKET_NR;
+ goto fail;
+ }
+
+ cm = cnd_table + nlp->packet_type;
+
+ /* This may happen if packet number is 0: */
+ if (cm->function == NULL) {
+ retcode = ERR_PACKET_NR;
+ goto fail;
+ }
+
+ reply_size += cm->reply_body_size;
+
+ /* allocation not in the IO path, cqueue thread context */
+ cn_reply = kmalloc(reply_size, GFP_KERNEL);
+ if (!cn_reply) {
+ retcode = ERR_NOMEM;
+ goto fail;
+ }
+ reply = (struct drbd_nl_cfg_reply *) cn_reply->data;
+
+ reply->packet_type =
+ cm->reply_body_size ? nlp->packet_type : P_nl_after_last_packet;
+ reply->minor = nlp->drbd_minor;
+ reply->ret_code = NO_ERROR; /* Might by modified by cm->function. */
+ /* reply->tag_list; might be modified by cm->function. */
+
+ rr = cm->function(mdev, nlp, reply);
+
+ cn_reply->id = req->id;
+ cn_reply->seq = req->seq;
+ cn_reply->ack = req->ack + 1;
+ cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + rr;
+ cn_reply->flags = 0;
+
+ rr = cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_KERNEL);
+ if (rr && rr != -ESRCH)
+ printk(KERN_INFO "drbd: cn_netlink_send()=%d\n", rr);
+
+ kfree(cn_reply);
+ module_put(THIS_MODULE);
+ return;
+ fail:
+ drbd_nl_send_reply(req, retcode);
+ module_put(THIS_MODULE);
+}
+
+static atomic_t drbd_nl_seq = ATOMIC_INIT(2); /* two. */
+
+static unsigned short *
+__tl_add_blob(unsigned short *tl, enum drbd_tags tag, const void *data,
+ unsigned short len, int nul_terminated)
+{
+ unsigned short l = tag_descriptions[tag_number(tag)].max_len;
+ len = (len < l) ? len : l;
+ put_unaligned(tag, tl++);
+ put_unaligned(len, tl++);
+ memcpy(tl, data, len);
+ tl = (unsigned short*)((char*)tl + len);
+ if (nul_terminated)
+ *((char*)tl - 1) = 0;
+ return tl;
+}
+
+static unsigned short *
+tl_add_blob(unsigned short *tl, enum drbd_tags tag, const void *data, int len)
+{
+ return __tl_add_blob(tl, tag, data, len, 0);
+}
+
+static unsigned short *
+tl_add_str(unsigned short *tl, enum drbd_tags tag, const char *str)
+{
+ return __tl_add_blob(tl, tag, str, strlen(str)+1, 0);
+}
+
+static unsigned short *
+tl_add_int(unsigned short *tl, enum drbd_tags tag, const void *val)
+{
+ put_unaligned(tag, tl++);
+ switch(tag_type(tag)) {
+ case TT_INTEGER:
+ put_unaligned(sizeof(int), tl++);
+ put_unaligned(*(int *)val, (int *)tl);
+ tl = (unsigned short*)((char*)tl+sizeof(int));
+ break;
+ case TT_INT64:
+ put_unaligned(sizeof(u64), tl++);
+ put_unaligned(*(u64 *)val, (u64 *)tl);
+ tl = (unsigned short*)((char*)tl+sizeof(u64));
+ break;
+ default:
+ /* someone did something stupid. */
+ ;
+ }
+ return tl;
+}
+
+void drbd_bcast_state(struct drbd_conf *mdev, union drbd_state state)
+{
+ char buffer[sizeof(struct cn_msg)+
+ sizeof(struct drbd_nl_cfg_reply)+
+ sizeof(struct get_state_tag_len_struct)+
+ sizeof(short int)];
+ struct cn_msg *cn_reply = (struct cn_msg *) buffer;
+ struct drbd_nl_cfg_reply *reply =
+ (struct drbd_nl_cfg_reply *)cn_reply->data;
+ unsigned short *tl = reply->tag_list;
+
+ /* dev_warn(DEV, "drbd_bcast_state() got called\n"); */
+
+ tl = get_state_to_tags(mdev, (struct get_state *)&state, tl);
+
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ cn_reply->id.idx = CN_IDX_DRBD;
+ cn_reply->id.val = CN_VAL_DRBD;
+
+ cn_reply->seq = atomic_add_return(1, &drbd_nl_seq);
+ cn_reply->ack = 0; /* not used here. */
+ cn_reply->len = sizeof(struct drbd_nl_cfg_reply) +
+ (int)((char *)tl - (char *)reply->tag_list);
+ cn_reply->flags = 0;
+
+ reply->packet_type = P_get_state;
+ reply->minor = mdev_to_minor(mdev);
+ reply->ret_code = NO_ERROR;
+
+ cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO);
+}
+
+void drbd_bcast_ev_helper(struct drbd_conf *mdev, char *helper_name)
+{
+ char buffer[sizeof(struct cn_msg)+
+ sizeof(struct drbd_nl_cfg_reply)+
+ sizeof(struct call_helper_tag_len_struct)+
+ sizeof(short int)];
+ struct cn_msg *cn_reply = (struct cn_msg *) buffer;
+ struct drbd_nl_cfg_reply *reply =
+ (struct drbd_nl_cfg_reply *)cn_reply->data;
+ unsigned short *tl = reply->tag_list;
+
+ /* dev_warn(DEV, "drbd_bcast_state() got called\n"); */
+
+ tl = tl_add_str(tl, T_helper, helper_name);
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ cn_reply->id.idx = CN_IDX_DRBD;
+ cn_reply->id.val = CN_VAL_DRBD;
+
+ cn_reply->seq = atomic_add_return(1, &drbd_nl_seq);
+ cn_reply->ack = 0; /* not used here. */
+ cn_reply->len = sizeof(struct drbd_nl_cfg_reply) +
+ (int)((char *)tl - (char *)reply->tag_list);
+ cn_reply->flags = 0;
+
+ reply->packet_type = P_call_helper;
+ reply->minor = mdev_to_minor(mdev);
+ reply->ret_code = NO_ERROR;
+
+ cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO);
+}
+
+void drbd_bcast_ee(struct drbd_conf *mdev,
+ const char *reason, const int dgs,
+ const char* seen_hash, const char* calc_hash,
+ const struct drbd_epoch_entry* e)
+{
+ struct cn_msg *cn_reply;
+ struct drbd_nl_cfg_reply *reply;
+ struct bio_vec *bvec;
+ unsigned short *tl;
+ int i;
+
+ if (!e)
+ return;
+ if (!reason || !reason[0])
+ return;
+
+ /* apparently we have to memcpy twice, first to prepare the data for the
+ * struct cn_msg, then within cn_netlink_send from the cn_msg to the
+ * netlink skb. */
+ /* receiver thread context, which is not in the writeout path (of this node),
+ * but may be in the writeout path of the _other_ node.
+ * GFP_NOIO to avoid potential "distributed deadlock". */
+ cn_reply = kmalloc(
+ sizeof(struct cn_msg)+
+ sizeof(struct drbd_nl_cfg_reply)+
+ sizeof(struct dump_ee_tag_len_struct)+
+ sizeof(short int),
+ GFP_NOIO);
+
+ if (!cn_reply) {
+ dev_err(DEV, "could not kmalloc buffer for drbd_bcast_ee, sector %llu, size %u\n",
+ (unsigned long long)e->sector, e->size);
+ return;
+ }
+
+ reply = (struct drbd_nl_cfg_reply*)cn_reply->data;
+ tl = reply->tag_list;
+
+ tl = tl_add_str(tl, T_dump_ee_reason, reason);
+ tl = tl_add_blob(tl, T_seen_digest, seen_hash, dgs);
+ tl = tl_add_blob(tl, T_calc_digest, calc_hash, dgs);
+ tl = tl_add_int(tl, T_ee_sector, &e->sector);
+ tl = tl_add_int(tl, T_ee_block_id, &e->block_id);
+
+ put_unaligned(T_ee_data, tl++);
+ put_unaligned(e->size, tl++);
+
+ __bio_for_each_segment(bvec, e->private_bio, i, 0) {
+ void *d = kmap(bvec->bv_page);
+ memcpy(tl, d + bvec->bv_offset, bvec->bv_len);
+ kunmap(bvec->bv_page);
+ tl=(unsigned short*)((char*)tl + bvec->bv_len);
+ }
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ cn_reply->id.idx = CN_IDX_DRBD;
+ cn_reply->id.val = CN_VAL_DRBD;
+
+ cn_reply->seq = atomic_add_return(1,&drbd_nl_seq);
+ cn_reply->ack = 0; // not used here.
+ cn_reply->len = sizeof(struct drbd_nl_cfg_reply) +
+ (int)((char*)tl - (char*)reply->tag_list);
+ cn_reply->flags = 0;
+
+ reply->packet_type = P_dump_ee;
+ reply->minor = mdev_to_minor(mdev);
+ reply->ret_code = NO_ERROR;
+
+ cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO);
+ kfree(cn_reply);
+}
+
+void drbd_bcast_sync_progress(struct drbd_conf *mdev)
+{
+ char buffer[sizeof(struct cn_msg)+
+ sizeof(struct drbd_nl_cfg_reply)+
+ sizeof(struct sync_progress_tag_len_struct)+
+ sizeof(short int)];
+ struct cn_msg *cn_reply = (struct cn_msg *) buffer;
+ struct drbd_nl_cfg_reply *reply =
+ (struct drbd_nl_cfg_reply *)cn_reply->data;
+ unsigned short *tl = reply->tag_list;
+ unsigned long rs_left;
+ unsigned int res;
+
+ /* no local ref, no bitmap, no syncer progress, no broadcast. */
+ if (!get_ldev(mdev))
+ return;
+ drbd_get_syncer_progress(mdev, &rs_left, &res);
+ put_ldev(mdev);
+
+ tl = tl_add_int(tl, T_sync_progress, &res);
+ put_unaligned(TT_END, tl++); /* Close the tag list */
+
+ cn_reply->id.idx = CN_IDX_DRBD;
+ cn_reply->id.val = CN_VAL_DRBD;
+
+ cn_reply->seq = atomic_add_return(1, &drbd_nl_seq);
+ cn_reply->ack = 0; /* not used here. */
+ cn_reply->len = sizeof(struct drbd_nl_cfg_reply) +
+ (int)((char *)tl - (char *)reply->tag_list);
+ cn_reply->flags = 0;
+
+ reply->packet_type = P_sync_progress;
+ reply->minor = mdev_to_minor(mdev);
+ reply->ret_code = NO_ERROR;
+
+ cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO);
+}
+
+int __init drbd_nl_init(void)
+{
+ static struct cb_id cn_id_drbd;
+ int err, try=10;
+
+ cn_id_drbd.val = CN_VAL_DRBD;
+ do {
+ cn_id_drbd.idx = cn_idx;
+ err = cn_add_callback(&cn_id_drbd, "cn_drbd", &drbd_connector_callback);
+ if (!err)
+ break;
+ cn_idx = (cn_idx + CN_IDX_STEP);
+ } while (try--);
+
+ if (err) {
+ printk(KERN_ERR "drbd: cn_drbd failed to register\n");
+ return err;
+ }
+
+ return 0;
+}
+
+void drbd_nl_cleanup(void)
+{
+ static struct cb_id cn_id_drbd;
+
+ cn_id_drbd.idx = cn_idx;
+ cn_id_drbd.val = CN_VAL_DRBD;
+
+ cn_del_callback(&cn_id_drbd);
+}
+
+void drbd_nl_send_reply(struct cn_msg *req, int ret_code)
+{
+ char buffer[sizeof(struct cn_msg)+sizeof(struct drbd_nl_cfg_reply)];
+ struct cn_msg *cn_reply = (struct cn_msg *) buffer;
+ struct drbd_nl_cfg_reply *reply =
+ (struct drbd_nl_cfg_reply *)cn_reply->data;
+ int rr;
+
+ cn_reply->id = req->id;
+
+ cn_reply->seq = req->seq;
+ cn_reply->ack = req->ack + 1;
+ cn_reply->len = sizeof(struct drbd_nl_cfg_reply);
+ cn_reply->flags = 0;
+
+ reply->minor = ((struct drbd_nl_cfg_req *)req->data)->drbd_minor;
+ reply->ret_code = ret_code;
+
+ rr = cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_NOIO);
+ if (rr && rr != -ESRCH)
+ printk(KERN_INFO "drbd: cn_netlink_send()=%d\n", rr);
+}
+
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
new file mode 100644
index 00000000000..bdd0b4943b1
--- /dev/null
+++ b/drivers/block/drbd/drbd_proc.c
@@ -0,0 +1,265 @@
+/*
+ drbd_proc.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/drbd.h>
+#include "drbd_int.h"
+
+static int drbd_proc_open(struct inode *inode, struct file *file);
+
+
+struct proc_dir_entry *drbd_proc;
+struct file_operations drbd_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = drbd_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+
+/*lge
+ * progress bars shamelessly adapted from driver/md/md.c
+ * output looks like
+ * [=====>..............] 33.5% (23456/123456)
+ * finish: 2:20:20 speed: 6,345 (6,456) K/sec
+ */
+static void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
+{
+ unsigned long db, dt, dbdt, rt, rs_left;
+ unsigned int res;
+ int i, x, y;
+
+ drbd_get_syncer_progress(mdev, &rs_left, &res);
+
+ x = res/50;
+ y = 20-x;
+ seq_printf(seq, "\t[");
+ for (i = 1; i < x; i++)
+ seq_printf(seq, "=");
+ seq_printf(seq, ">");
+ for (i = 0; i < y; i++)
+ seq_printf(seq, ".");
+ seq_printf(seq, "] ");
+
+ seq_printf(seq, "sync'ed:%3u.%u%% ", res / 10, res % 10);
+ /* if more than 1 GB display in MB */
+ if (mdev->rs_total > 0x100000L)
+ seq_printf(seq, "(%lu/%lu)M\n\t",
+ (unsigned long) Bit2KB(rs_left >> 10),
+ (unsigned long) Bit2KB(mdev->rs_total >> 10));
+ else
+ seq_printf(seq, "(%lu/%lu)K\n\t",
+ (unsigned long) Bit2KB(rs_left),
+ (unsigned long) Bit2KB(mdev->rs_total));
+
+ /* see drivers/md/md.c
+ * We do not want to overflow, so the order of operands and
+ * the * 100 / 100 trick are important. We do a +1 to be
+ * safe against division by zero. We only estimate anyway.
+ *
+ * dt: time from mark until now
+ * db: blocks written from mark until now
+ * rt: remaining time
+ */
+ dt = (jiffies - mdev->rs_mark_time) / HZ;
+
+ if (dt > 20) {
+ /* if we made no update to rs_mark_time for too long,
+ * we are stalled. show that. */
+ seq_printf(seq, "stalled\n");
+ return;
+ }
+
+ if (!dt)
+ dt++;
+ db = mdev->rs_mark_left - rs_left;
+ rt = (dt * (rs_left / (db/100+1)))/100; /* seconds */
+
+ seq_printf(seq, "finish: %lu:%02lu:%02lu",
+ rt / 3600, (rt % 3600) / 60, rt % 60);
+
+ /* current speed average over (SYNC_MARKS * SYNC_MARK_STEP) jiffies */
+ dbdt = Bit2KB(db/dt);
+ if (dbdt > 1000)
+ seq_printf(seq, " speed: %ld,%03ld",
+ dbdt/1000, dbdt % 1000);
+ else
+ seq_printf(seq, " speed: %ld", dbdt);
+
+ /* mean speed since syncer started
+ * we do account for PausedSync periods */
+ dt = (jiffies - mdev->rs_start - mdev->rs_paused) / HZ;
+ if (dt <= 0)
+ dt = 1;
+ db = mdev->rs_total - rs_left;
+ dbdt = Bit2KB(db/dt);
+ if (dbdt > 1000)
+ seq_printf(seq, " (%ld,%03ld)",
+ dbdt/1000, dbdt % 1000);
+ else
+ seq_printf(seq, " (%ld)", dbdt);
+
+ seq_printf(seq, " K/sec\n");
+}
+
+static void resync_dump_detail(struct seq_file *seq, struct lc_element *e)
+{
+ struct bm_extent *bme = lc_entry(e, struct bm_extent, lce);
+
+ seq_printf(seq, "%5d %s %s\n", bme->rs_left,
+ bme->flags & BME_NO_WRITES ? "NO_WRITES" : "---------",
+ bme->flags & BME_LOCKED ? "LOCKED" : "------"
+ );
+}
+
+static int drbd_seq_show(struct seq_file *seq, void *v)
+{
+ int i, hole = 0;
+ const char *sn;
+ struct drbd_conf *mdev;
+
+ static char write_ordering_chars[] = {
+ [WO_none] = 'n',
+ [WO_drain_io] = 'd',
+ [WO_bdev_flush] = 'f',
+ [WO_bio_barrier] = 'b',
+ };
+
+ seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n",
+ API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag());
+
+ /*
+ cs .. connection state
+ ro .. node role (local/remote)
+ ds .. disk state (local/remote)
+ protocol
+ various flags
+ ns .. network send
+ nr .. network receive
+ dw .. disk write
+ dr .. disk read
+ al .. activity log write count
+ bm .. bitmap update write count
+ pe .. pending (waiting for ack or data reply)
+ ua .. unack'd (still need to send ack or data reply)
+ ap .. application requests accepted, but not yet completed
+ ep .. number of epochs currently "on the fly", P_BARRIER_ACK pending
+ wo .. write ordering mode currently in use
+ oos .. known out-of-sync kB
+ */
+
+ for (i = 0; i < minor_count; i++) {
+ mdev = minor_to_mdev(i);
+ if (!mdev) {
+ hole = 1;
+ continue;
+ }
+ if (hole) {
+ hole = 0;
+ seq_printf(seq, "\n");
+ }
+
+ sn = drbd_conn_str(mdev->state.conn);
+
+ if (mdev->state.conn == C_STANDALONE &&
+ mdev->state.disk == D_DISKLESS &&
+ mdev->state.role == R_SECONDARY) {
+ seq_printf(seq, "%2d: cs:Unconfigured\n", i);
+ } else {
+ seq_printf(seq,
+ "%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c\n"
+ " ns:%u nr:%u dw:%u dr:%u al:%u bm:%u "
+ "lo:%d pe:%d ua:%d ap:%d ep:%d wo:%c",
+ i, sn,
+ drbd_role_str(mdev->state.role),
+ drbd_role_str(mdev->state.peer),
+ drbd_disk_str(mdev->state.disk),
+ drbd_disk_str(mdev->state.pdsk),
+ (mdev->net_conf == NULL ? ' ' :
+ (mdev->net_conf->wire_protocol - DRBD_PROT_A+'A')),
+ mdev->state.susp ? 's' : 'r',
+ mdev->state.aftr_isp ? 'a' : '-',
+ mdev->state.peer_isp ? 'p' : '-',
+ mdev->state.user_isp ? 'u' : '-',
+ mdev->congestion_reason ?: '-',
+ mdev->send_cnt/2,
+ mdev->recv_cnt/2,
+ mdev->writ_cnt/2,
+ mdev->read_cnt/2,
+ mdev->al_writ_cnt,
+ mdev->bm_writ_cnt,
+ atomic_read(&mdev->local_cnt),
+ atomic_read(&mdev->ap_pending_cnt) +
+ atomic_read(&mdev->rs_pending_cnt),
+ atomic_read(&mdev->unacked_cnt),
+ atomic_read(&mdev->ap_bio_cnt),
+ mdev->epochs,
+ write_ordering_chars[mdev->write_ordering]
+ );
+ seq_printf(seq, " oos:%lu\n",
+ Bit2KB(drbd_bm_total_weight(mdev)));
+ }
+ if (mdev->state.conn == C_SYNC_SOURCE ||
+ mdev->state.conn == C_SYNC_TARGET)
+ drbd_syncer_progress(mdev, seq);
+
+ if (mdev->state.conn == C_VERIFY_S || mdev->state.conn == C_VERIFY_T)
+ seq_printf(seq, "\t%3d%% %lu/%lu\n",
+ (int)((mdev->rs_total-mdev->ov_left) /
+ (mdev->rs_total/100+1)),
+ mdev->rs_total - mdev->ov_left,
+ mdev->rs_total);
+
+ if (proc_details >= 1 && get_ldev_if_state(mdev, D_FAILED)) {
+ lc_seq_printf_stats(seq, mdev->resync);
+ lc_seq_printf_stats(seq, mdev->act_log);
+ put_ldev(mdev);
+ }
+
+ if (proc_details >= 2) {
+ if (mdev->resync) {
+ lc_seq_dump_details(seq, mdev->resync, "rs_left",
+ resync_dump_detail);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int drbd_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, drbd_seq_show, PDE(inode)->data);
+}
+
+/* PROC FS stuff end */
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
new file mode 100644
index 00000000000..c548f24f54a
--- /dev/null
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -0,0 +1,4426 @@
+/*
+ drbd_receiver.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+#include <net/sock.h>
+
+#include <linux/version.h>
+#include <linux/drbd.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/in.h>
+#include <linux/mm.h>
+#include <linux/memcontrol.h>
+#include <linux/mm_inline.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/pkt_sched.h>
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/scatterlist.h>
+#include "drbd_int.h"
+#include "drbd_req.h"
+
+#include "drbd_vli.h"
+
+struct flush_work {
+ struct drbd_work w;
+ struct drbd_epoch *epoch;
+};
+
+enum finish_epoch {
+ FE_STILL_LIVE,
+ FE_DESTROYED,
+ FE_RECYCLED,
+};
+
+static int drbd_do_handshake(struct drbd_conf *mdev);
+static int drbd_do_auth(struct drbd_conf *mdev);
+
+static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *, struct drbd_epoch *, enum epoch_event);
+static int e_end_block(struct drbd_conf *, struct drbd_work *, int);
+
+static struct drbd_epoch *previous_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch)
+{
+ struct drbd_epoch *prev;
+ spin_lock(&mdev->epoch_lock);
+ prev = list_entry(epoch->list.prev, struct drbd_epoch, list);
+ if (prev == epoch || prev == mdev->current_epoch)
+ prev = NULL;
+ spin_unlock(&mdev->epoch_lock);
+ return prev;
+}
+
+#define GFP_TRY (__GFP_HIGHMEM | __GFP_NOWARN)
+
+static struct page *drbd_pp_first_page_or_try_alloc(struct drbd_conf *mdev)
+{
+ struct page *page = NULL;
+
+ /* Yes, testing drbd_pp_vacant outside the lock is racy.
+ * So what. It saves a spin_lock. */
+ if (drbd_pp_vacant > 0) {
+ spin_lock(&drbd_pp_lock);
+ page = drbd_pp_pool;
+ if (page) {
+ drbd_pp_pool = (struct page *)page_private(page);
+ set_page_private(page, 0); /* just to be polite */
+ drbd_pp_vacant--;
+ }
+ spin_unlock(&drbd_pp_lock);
+ }
+ /* GFP_TRY, because we must not cause arbitrary write-out: in a DRBD
+ * "criss-cross" setup, that might cause write-out on some other DRBD,
+ * which in turn might block on the other node at this very place. */
+ if (!page)
+ page = alloc_page(GFP_TRY);
+ if (page)
+ atomic_inc(&mdev->pp_in_use);
+ return page;
+}
+
+/* kick lower level device, if we have more than (arbitrary number)
+ * reference counts on it, which typically are locally submitted io
+ * requests. don't use unacked_cnt, so we speed up proto A and B, too. */
+static void maybe_kick_lo(struct drbd_conf *mdev)
+{
+ if (atomic_read(&mdev->local_cnt) >= mdev->net_conf->unplug_watermark)
+ drbd_kick_lo(mdev);
+}
+
+static void reclaim_net_ee(struct drbd_conf *mdev, struct list_head *to_be_freed)
+{
+ struct drbd_epoch_entry *e;
+ struct list_head *le, *tle;
+
+ /* The EEs are always appended to the end of the list. Since
+ they are sent in order over the wire, they have to finish
+ in order. As soon as we see the first not finished we can
+ stop to examine the list... */
+
+ list_for_each_safe(le, tle, &mdev->net_ee) {
+ e = list_entry(le, struct drbd_epoch_entry, w.list);
+ if (drbd_bio_has_active_page(e->private_bio))
+ break;
+ list_move(le, to_be_freed);
+ }
+}
+
+static void drbd_kick_lo_and_reclaim_net(struct drbd_conf *mdev)
+{
+ LIST_HEAD(reclaimed);
+ struct drbd_epoch_entry *e, *t;
+
+ maybe_kick_lo(mdev);
+ spin_lock_irq(&mdev->req_lock);
+ reclaim_net_ee(mdev, &reclaimed);
+ spin_unlock_irq(&mdev->req_lock);
+
+ list_for_each_entry_safe(e, t, &reclaimed, w.list)
+ drbd_free_ee(mdev, e);
+}
+
+/**
+ * drbd_pp_alloc() - Returns a page, fails only if a signal comes in
+ * @mdev: DRBD device.
+ * @retry: whether or not to retry allocation forever (or until signalled)
+ *
+ * Tries to allocate a page, first from our own page pool, then from the
+ * kernel, unless this allocation would exceed the max_buffers setting.
+ * If @retry is non-zero, retry until DRBD frees a page somewhere else.
+ */
+static struct page *drbd_pp_alloc(struct drbd_conf *mdev, int retry)
+{
+ struct page *page = NULL;
+ DEFINE_WAIT(wait);
+
+ if (atomic_read(&mdev->pp_in_use) < mdev->net_conf->max_buffers) {
+ page = drbd_pp_first_page_or_try_alloc(mdev);
+ if (page)
+ return page;
+ }
+
+ for (;;) {
+ prepare_to_wait(&drbd_pp_wait, &wait, TASK_INTERRUPTIBLE);
+
+ drbd_kick_lo_and_reclaim_net(mdev);
+
+ if (atomic_read(&mdev->pp_in_use) < mdev->net_conf->max_buffers) {
+ page = drbd_pp_first_page_or_try_alloc(mdev);
+ if (page)
+ break;
+ }
+
+ if (!retry)
+ break;
+
+ if (signal_pending(current)) {
+ dev_warn(DEV, "drbd_pp_alloc interrupted!\n");
+ break;
+ }
+
+ schedule();
+ }
+ finish_wait(&drbd_pp_wait, &wait);
+
+ return page;
+}
+
+/* Must not be used from irq, as that may deadlock: see drbd_pp_alloc.
+ * Is also used from inside an other spin_lock_irq(&mdev->req_lock) */
+static void drbd_pp_free(struct drbd_conf *mdev, struct page *page)
+{
+ int free_it;
+
+ spin_lock(&drbd_pp_lock);
+ if (drbd_pp_vacant > (DRBD_MAX_SEGMENT_SIZE/PAGE_SIZE)*minor_count) {
+ free_it = 1;
+ } else {
+ set_page_private(page, (unsigned long)drbd_pp_pool);
+ drbd_pp_pool = page;
+ drbd_pp_vacant++;
+ free_it = 0;
+ }
+ spin_unlock(&drbd_pp_lock);
+
+ atomic_dec(&mdev->pp_in_use);
+
+ if (free_it)
+ __free_page(page);
+
+ wake_up(&drbd_pp_wait);
+}
+
+static void drbd_pp_free_bio_pages(struct drbd_conf *mdev, struct bio *bio)
+{
+ struct page *p_to_be_freed = NULL;
+ struct page *page;
+ struct bio_vec *bvec;
+ int i;
+
+ spin_lock(&drbd_pp_lock);
+ __bio_for_each_segment(bvec, bio, i, 0) {
+ if (drbd_pp_vacant > (DRBD_MAX_SEGMENT_SIZE/PAGE_SIZE)*minor_count) {
+ set_page_private(bvec->bv_page, (unsigned long)p_to_be_freed);
+ p_to_be_freed = bvec->bv_page;
+ } else {
+ set_page_private(bvec->bv_page, (unsigned long)drbd_pp_pool);
+ drbd_pp_pool = bvec->bv_page;
+ drbd_pp_vacant++;
+ }
+ }
+ spin_unlock(&drbd_pp_lock);
+ atomic_sub(bio->bi_vcnt, &mdev->pp_in_use);
+
+ while (p_to_be_freed) {
+ page = p_to_be_freed;
+ p_to_be_freed = (struct page *)page_private(page);
+ set_page_private(page, 0); /* just to be polite */
+ put_page(page);
+ }
+
+ wake_up(&drbd_pp_wait);
+}
+
+/*
+You need to hold the req_lock:
+ _drbd_wait_ee_list_empty()
+
+You must not have the req_lock:
+ drbd_free_ee()
+ drbd_alloc_ee()
+ drbd_init_ee()
+ drbd_release_ee()
+ drbd_ee_fix_bhs()
+ drbd_process_done_ee()
+ drbd_clear_done_ee()
+ drbd_wait_ee_list_empty()
+*/
+
+struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
+ u64 id,
+ sector_t sector,
+ unsigned int data_size,
+ gfp_t gfp_mask) __must_hold(local)
+{
+ struct request_queue *q;
+ struct drbd_epoch_entry *e;
+ struct page *page;
+ struct bio *bio;
+ unsigned int ds;
+
+ if (FAULT_ACTIVE(mdev, DRBD_FAULT_AL_EE))
+ return NULL;
+
+ e = mempool_alloc(drbd_ee_mempool, gfp_mask & ~__GFP_HIGHMEM);
+ if (!e) {
+ if (!(gfp_mask & __GFP_NOWARN))
+ dev_err(DEV, "alloc_ee: Allocation of an EE failed\n");
+ return NULL;
+ }
+
+ bio = bio_alloc(gfp_mask & ~__GFP_HIGHMEM, div_ceil(data_size, PAGE_SIZE));
+ if (!bio) {
+ if (!(gfp_mask & __GFP_NOWARN))
+ dev_err(DEV, "alloc_ee: Allocation of a bio failed\n");
+ goto fail1;
+ }
+
+ bio->bi_bdev = mdev->ldev->backing_bdev;
+ bio->bi_sector = sector;
+
+ ds = data_size;
+ while (ds) {
+ page = drbd_pp_alloc(mdev, (gfp_mask & __GFP_WAIT));
+ if (!page) {
+ if (!(gfp_mask & __GFP_NOWARN))
+ dev_err(DEV, "alloc_ee: Allocation of a page failed\n");
+ goto fail2;
+ }
+ if (!bio_add_page(bio, page, min_t(int, ds, PAGE_SIZE), 0)) {
+ drbd_pp_free(mdev, page);
+ dev_err(DEV, "alloc_ee: bio_add_page(s=%llu,"
+ "data_size=%u,ds=%u) failed\n",
+ (unsigned long long)sector, data_size, ds);
+
+ q = bdev_get_queue(bio->bi_bdev);
+ if (q->merge_bvec_fn) {
+ struct bvec_merge_data bvm = {
+ .bi_bdev = bio->bi_bdev,
+ .bi_sector = bio->bi_sector,
+ .bi_size = bio->bi_size,
+ .bi_rw = bio->bi_rw,
+ };
+ int l = q->merge_bvec_fn(q, &bvm,
+ &bio->bi_io_vec[bio->bi_vcnt]);
+ dev_err(DEV, "merge_bvec_fn() = %d\n", l);
+ }
+
+ /* dump more of the bio. */
+ dev_err(DEV, "bio->bi_max_vecs = %d\n", bio->bi_max_vecs);
+ dev_err(DEV, "bio->bi_vcnt = %d\n", bio->bi_vcnt);
+ dev_err(DEV, "bio->bi_size = %d\n", bio->bi_size);
+ dev_err(DEV, "bio->bi_phys_segments = %d\n", bio->bi_phys_segments);
+
+ goto fail2;
+ break;
+ }
+ ds -= min_t(int, ds, PAGE_SIZE);
+ }
+
+ D_ASSERT(data_size == bio->bi_size);
+
+ bio->bi_private = e;
+ e->mdev = mdev;
+ e->sector = sector;
+ e->size = bio->bi_size;
+
+ e->private_bio = bio;
+ e->block_id = id;
+ INIT_HLIST_NODE(&e->colision);
+ e->epoch = NULL;
+ e->flags = 0;
+
+ return e;
+
+ fail2:
+ drbd_pp_free_bio_pages(mdev, bio);
+ bio_put(bio);
+ fail1:
+ mempool_free(e, drbd_ee_mempool);
+
+ return NULL;
+}
+
+void drbd_free_ee(struct drbd_conf *mdev, struct drbd_epoch_entry *e)
+{
+ struct bio *bio = e->private_bio;
+ drbd_pp_free_bio_pages(mdev, bio);
+ bio_put(bio);
+ D_ASSERT(hlist_unhashed(&e->colision));
+ mempool_free(e, drbd_ee_mempool);
+}
+
+int drbd_release_ee(struct drbd_conf *mdev, struct list_head *list)
+{
+ LIST_HEAD(work_list);
+ struct drbd_epoch_entry *e, *t;
+ int count = 0;
+
+ spin_lock_irq(&mdev->req_lock);
+ list_splice_init(list, &work_list);
+ spin_unlock_irq(&mdev->req_lock);
+
+ list_for_each_entry_safe(e, t, &work_list, w.list) {
+ drbd_free_ee(mdev, e);
+ count++;
+ }
+ return count;
+}
+
+
+/*
+ * This function is called from _asender only_
+ * but see also comments in _req_mod(,barrier_acked)
+ * and receive_Barrier.
+ *
+ * Move entries from net_ee to done_ee, if ready.
+ * Grab done_ee, call all callbacks, free the entries.
+ * The callbacks typically send out ACKs.
+ */
+static int drbd_process_done_ee(struct drbd_conf *mdev)
+{
+ LIST_HEAD(work_list);
+ LIST_HEAD(reclaimed);
+ struct drbd_epoch_entry *e, *t;
+ int ok = (mdev->state.conn >= C_WF_REPORT_PARAMS);
+
+ spin_lock_irq(&mdev->req_lock);
+ reclaim_net_ee(mdev, &reclaimed);
+ list_splice_init(&mdev->done_ee, &work_list);
+ spin_unlock_irq(&mdev->req_lock);
+
+ list_for_each_entry_safe(e, t, &reclaimed, w.list)
+ drbd_free_ee(mdev, e);
+
+ /* possible callbacks here:
+ * e_end_block, and e_end_resync_block, e_send_discard_ack.
+ * all ignore the last argument.
+ */
+ list_for_each_entry_safe(e, t, &work_list, w.list) {
+ /* list_del not necessary, next/prev members not touched */
+ ok = e->w.cb(mdev, &e->w, !ok) && ok;
+ drbd_free_ee(mdev, e);
+ }
+ wake_up(&mdev->ee_wait);
+
+ return ok;
+}
+
+void _drbd_wait_ee_list_empty(struct drbd_conf *mdev, struct list_head *head)
+{
+ DEFINE_WAIT(wait);
+
+ /* avoids spin_lock/unlock
+ * and calling prepare_to_wait in the fast path */
+ while (!list_empty(head)) {
+ prepare_to_wait(&mdev->ee_wait, &wait, TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&mdev->req_lock);
+ drbd_kick_lo(mdev);
+ schedule();
+ finish_wait(&mdev->ee_wait, &wait);
+ spin_lock_irq(&mdev->req_lock);
+ }
+}
+
+void drbd_wait_ee_list_empty(struct drbd_conf *mdev, struct list_head *head)
+{
+ spin_lock_irq(&mdev->req_lock);
+ _drbd_wait_ee_list_empty(mdev, head);
+ spin_unlock_irq(&mdev->req_lock);
+}
+
+/* see also kernel_accept; which is only present since 2.6.18.
+ * also we want to log which part of it failed, exactly */
+static int drbd_accept(struct drbd_conf *mdev, const char **what,
+ struct socket *sock, struct socket **newsock)
+{
+ struct sock *sk = sock->sk;
+ int err = 0;
+
+ *what = "listen";
+ err = sock->ops->listen(sock, 5);
+ if (err < 0)
+ goto out;
+
+ *what = "sock_create_lite";
+ err = sock_create_lite(sk->sk_family, sk->sk_type, sk->sk_protocol,
+ newsock);
+ if (err < 0)
+ goto out;
+
+ *what = "accept";
+ err = sock->ops->accept(sock, *newsock, 0);
+ if (err < 0) {
+ sock_release(*newsock);
+ *newsock = NULL;
+ goto out;
+ }
+ (*newsock)->ops = sock->ops;
+
+out:
+ return err;
+}
+
+static int drbd_recv_short(struct drbd_conf *mdev, struct socket *sock,
+ void *buf, size_t size, int flags)
+{
+ mm_segment_t oldfs;
+ struct kvec iov = {
+ .iov_base = buf,
+ .iov_len = size,
+ };
+ struct msghdr msg = {
+ .msg_iovlen = 1,
+ .msg_iov = (struct iovec *)&iov,
+ .msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL)
+ };
+ int rv;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rv = sock_recvmsg(sock, &msg, size, msg.msg_flags);
+ set_fs(oldfs);
+
+ return rv;
+}
+
+static int drbd_recv(struct drbd_conf *mdev, void *buf, size_t size)
+{
+ mm_segment_t oldfs;
+ struct kvec iov = {
+ .iov_base = buf,
+ .iov_len = size,
+ };
+ struct msghdr msg = {
+ .msg_iovlen = 1,
+ .msg_iov = (struct iovec *)&iov,
+ .msg_flags = MSG_WAITALL | MSG_NOSIGNAL
+ };
+ int rv;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (;;) {
+ rv = sock_recvmsg(mdev->data.socket, &msg, size, msg.msg_flags);
+ if (rv == size)
+ break;
+
+ /* Note:
+ * ECONNRESET other side closed the connection
+ * ERESTARTSYS (on sock) we got a signal
+ */
+
+ if (rv < 0) {
+ if (rv == -ECONNRESET)
+ dev_info(DEV, "sock was reset by peer\n");
+ else if (rv != -ERESTARTSYS)
+ dev_err(DEV, "sock_recvmsg returned %d\n", rv);
+ break;
+ } else if (rv == 0) {
+ dev_info(DEV, "sock was shut down by peer\n");
+ break;
+ } else {
+ /* signal came in, or peer/link went down,
+ * after we read a partial message
+ */
+ /* D_ASSERT(signal_pending(current)); */
+ break;
+ }
+ };
+
+ set_fs(oldfs);
+
+ if (rv != size)
+ drbd_force_state(mdev, NS(conn, C_BROKEN_PIPE));
+
+ return rv;
+}
+
+static struct socket *drbd_try_connect(struct drbd_conf *mdev)
+{
+ const char *what;
+ struct socket *sock;
+ struct sockaddr_in6 src_in6;
+ int err;
+ int disconnect_on_error = 1;
+
+ if (!get_net_conf(mdev))
+ return NULL;
+
+ what = "sock_create_kern";
+ err = sock_create_kern(((struct sockaddr *)mdev->net_conf->my_addr)->sa_family,
+ SOCK_STREAM, IPPROTO_TCP, &sock);
+ if (err < 0) {
+ sock = NULL;
+ goto out;
+ }
+
+ sock->sk->sk_rcvtimeo =
+ sock->sk->sk_sndtimeo = mdev->net_conf->try_connect_int*HZ;
+
+ /* explicitly bind to the configured IP as source IP
+ * for the outgoing connections.
+ * This is needed for multihomed hosts and to be
+ * able to use lo: interfaces for drbd.
+ * Make sure to use 0 as port number, so linux selects
+ * a free one dynamically.
+ */
+ memcpy(&src_in6, mdev->net_conf->my_addr,
+ min_t(int, mdev->net_conf->my_addr_len, sizeof(src_in6)));
+ if (((struct sockaddr *)mdev->net_conf->my_addr)->sa_family == AF_INET6)
+ src_in6.sin6_port = 0;
+ else
+ ((struct sockaddr_in *)&src_in6)->sin_port = 0; /* AF_INET & AF_SCI */
+
+ what = "bind before connect";
+ err = sock->ops->bind(sock,
+ (struct sockaddr *) &src_in6,
+ mdev->net_conf->my_addr_len);
+ if (err < 0)
+ goto out;
+
+ /* connect may fail, peer not yet available.
+ * stay C_WF_CONNECTION, don't go Disconnecting! */
+ disconnect_on_error = 0;
+ what = "connect";
+ err = sock->ops->connect(sock,
+ (struct sockaddr *)mdev->net_conf->peer_addr,
+ mdev->net_conf->peer_addr_len, 0);
+
+out:
+ if (err < 0) {
+ if (sock) {
+ sock_release(sock);
+ sock = NULL;
+ }
+ switch (-err) {
+ /* timeout, busy, signal pending */
+ case ETIMEDOUT: case EAGAIN: case EINPROGRESS:
+ case EINTR: case ERESTARTSYS:
+ /* peer not (yet) available, network problem */
+ case ECONNREFUSED: case ENETUNREACH:
+ case EHOSTDOWN: case EHOSTUNREACH:
+ disconnect_on_error = 0;
+ break;
+ default:
+ dev_err(DEV, "%s failed, err = %d\n", what, err);
+ }
+ if (disconnect_on_error)
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ }
+ put_net_conf(mdev);
+ return sock;
+}
+
+static struct socket *drbd_wait_for_connect(struct drbd_conf *mdev)
+{
+ int timeo, err;
+ struct socket *s_estab = NULL, *s_listen;
+ const char *what;
+
+ if (!get_net_conf(mdev))
+ return NULL;
+
+ what = "sock_create_kern";
+ err = sock_create_kern(((struct sockaddr *)mdev->net_conf->my_addr)->sa_family,
+ SOCK_STREAM, IPPROTO_TCP, &s_listen);
+ if (err) {
+ s_listen = NULL;
+ goto out;
+ }
+
+ timeo = mdev->net_conf->try_connect_int * HZ;
+ timeo += (random32() & 1) ? timeo / 7 : -timeo / 7; /* 28.5% random jitter */
+
+ s_listen->sk->sk_reuse = 1; /* SO_REUSEADDR */
+ s_listen->sk->sk_rcvtimeo = timeo;
+ s_listen->sk->sk_sndtimeo = timeo;
+
+ what = "bind before listen";
+ err = s_listen->ops->bind(s_listen,
+ (struct sockaddr *) mdev->net_conf->my_addr,
+ mdev->net_conf->my_addr_len);
+ if (err < 0)
+ goto out;
+
+ err = drbd_accept(mdev, &what, s_listen, &s_estab);
+
+out:
+ if (s_listen)
+ sock_release(s_listen);
+ if (err < 0) {
+ if (err != -EAGAIN && err != -EINTR && err != -ERESTARTSYS) {
+ dev_err(DEV, "%s failed, err = %d\n", what, err);
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ }
+ }
+ put_net_conf(mdev);
+
+ return s_estab;
+}
+
+static int drbd_send_fp(struct drbd_conf *mdev,
+ struct socket *sock, enum drbd_packets cmd)
+{
+ struct p_header *h = (struct p_header *) &mdev->data.sbuf.header;
+
+ return _drbd_send_cmd(mdev, sock, cmd, h, sizeof(*h), 0);
+}
+
+static enum drbd_packets drbd_recv_fp(struct drbd_conf *mdev, struct socket *sock)
+{
+ struct p_header *h = (struct p_header *) &mdev->data.sbuf.header;
+ int rr;
+
+ rr = drbd_recv_short(mdev, sock, h, sizeof(*h), 0);
+
+ if (rr == sizeof(*h) && h->magic == BE_DRBD_MAGIC)
+ return be16_to_cpu(h->command);
+
+ return 0xffff;
+}
+
+/**
+ * drbd_socket_okay() - Free the socket if its connection is not okay
+ * @mdev: DRBD device.
+ * @sock: pointer to the pointer to the socket.
+ */
+static int drbd_socket_okay(struct drbd_conf *mdev, struct socket **sock)
+{
+ int rr;
+ char tb[4];
+
+ if (!*sock)
+ return FALSE;
+
+ rr = drbd_recv_short(mdev, *sock, tb, 4, MSG_DONTWAIT | MSG_PEEK);
+
+ if (rr > 0 || rr == -EAGAIN) {
+ return TRUE;
+ } else {
+ sock_release(*sock);
+ *sock = NULL;
+ return FALSE;
+ }
+}
+
+/*
+ * return values:
+ * 1 yes, we have a valid connection
+ * 0 oops, did not work out, please try again
+ * -1 peer talks different language,
+ * no point in trying again, please go standalone.
+ * -2 We do not have a network config...
+ */
+static int drbd_connect(struct drbd_conf *mdev)
+{
+ struct socket *s, *sock, *msock;
+ int try, h, ok;
+
+ D_ASSERT(!mdev->data.socket);
+
+ if (test_and_clear_bit(CREATE_BARRIER, &mdev->flags))
+ dev_err(DEV, "CREATE_BARRIER flag was set in drbd_connect - now cleared!\n");
+
+ if (drbd_request_state(mdev, NS(conn, C_WF_CONNECTION)) < SS_SUCCESS)
+ return -2;
+
+ clear_bit(DISCARD_CONCURRENT, &mdev->flags);
+
+ sock = NULL;
+ msock = NULL;
+
+ do {
+ for (try = 0;;) {
+ /* 3 tries, this should take less than a second! */
+ s = drbd_try_connect(mdev);
+ if (s || ++try >= 3)
+ break;
+ /* give the other side time to call bind() & listen() */
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ }
+
+ if (s) {
+ if (!sock) {
+ drbd_send_fp(mdev, s, P_HAND_SHAKE_S);
+ sock = s;
+ s = NULL;
+ } else if (!msock) {
+ drbd_send_fp(mdev, s, P_HAND_SHAKE_M);
+ msock = s;
+ s = NULL;
+ } else {
+ dev_err(DEV, "Logic error in drbd_connect()\n");
+ goto out_release_sockets;
+ }
+ }
+
+ if (sock && msock) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ ok = drbd_socket_okay(mdev, &sock);
+ ok = drbd_socket_okay(mdev, &msock) && ok;
+ if (ok)
+ break;
+ }
+
+retry:
+ s = drbd_wait_for_connect(mdev);
+ if (s) {
+ try = drbd_recv_fp(mdev, s);
+ drbd_socket_okay(mdev, &sock);
+ drbd_socket_okay(mdev, &msock);
+ switch (try) {
+ case P_HAND_SHAKE_S:
+ if (sock) {
+ dev_warn(DEV, "initial packet S crossed\n");
+ sock_release(sock);
+ }
+ sock = s;
+ break;
+ case P_HAND_SHAKE_M:
+ if (msock) {
+ dev_warn(DEV, "initial packet M crossed\n");
+ sock_release(msock);
+ }
+ msock = s;
+ set_bit(DISCARD_CONCURRENT, &mdev->flags);
+ break;
+ default:
+ dev_warn(DEV, "Error receiving initial packet\n");
+ sock_release(s);
+ if (random32() & 1)
+ goto retry;
+ }
+ }
+
+ if (mdev->state.conn <= C_DISCONNECTING)
+ goto out_release_sockets;
+ if (signal_pending(current)) {
+ flush_signals(current);
+ smp_rmb();
+ if (get_t_state(&mdev->receiver) == Exiting)
+ goto out_release_sockets;
+ }
+
+ if (sock && msock) {
+ ok = drbd_socket_okay(mdev, &sock);
+ ok = drbd_socket_okay(mdev, &msock) && ok;
+ if (ok)
+ break;
+ }
+ } while (1);
+
+ msock->sk->sk_reuse = 1; /* SO_REUSEADDR */
+ sock->sk->sk_reuse = 1; /* SO_REUSEADDR */
+
+ sock->sk->sk_allocation = GFP_NOIO;
+ msock->sk->sk_allocation = GFP_NOIO;
+
+ sock->sk->sk_priority = TC_PRIO_INTERACTIVE_BULK;
+ msock->sk->sk_priority = TC_PRIO_INTERACTIVE;
+
+ if (mdev->net_conf->sndbuf_size) {
+ sock->sk->sk_sndbuf = mdev->net_conf->sndbuf_size;
+ sock->sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
+ }
+
+ if (mdev->net_conf->rcvbuf_size) {
+ sock->sk->sk_rcvbuf = mdev->net_conf->rcvbuf_size;
+ sock->sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
+ }
+
+ /* NOT YET ...
+ * sock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10;
+ * sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
+ * first set it to the P_HAND_SHAKE timeout,
+ * which we set to 4x the configured ping_timeout. */
+ sock->sk->sk_sndtimeo =
+ sock->sk->sk_rcvtimeo = mdev->net_conf->ping_timeo*4*HZ/10;
+
+ msock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10;
+ msock->sk->sk_rcvtimeo = mdev->net_conf->ping_int*HZ;
+
+ /* we don't want delays.
+ * we use TCP_CORK where apropriate, though */
+ drbd_tcp_nodelay(sock);
+ drbd_tcp_nodelay(msock);
+
+ mdev->data.socket = sock;
+ mdev->meta.socket = msock;
+ mdev->last_received = jiffies;
+
+ D_ASSERT(mdev->asender.task == NULL);
+
+ h = drbd_do_handshake(mdev);
+ if (h <= 0)
+ return h;
+
+ if (mdev->cram_hmac_tfm) {
+ /* drbd_request_state(mdev, NS(conn, WFAuth)); */
+ if (!drbd_do_auth(mdev)) {
+ dev_err(DEV, "Authentication of peer failed\n");
+ return -1;
+ }
+ }
+
+ if (drbd_request_state(mdev, NS(conn, C_WF_REPORT_PARAMS)) < SS_SUCCESS)
+ return 0;
+
+ sock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10;
+ sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
+
+ atomic_set(&mdev->packet_seq, 0);
+ mdev->peer_seq = 0;
+
+ drbd_thread_start(&mdev->asender);
+
+ drbd_send_protocol(mdev);
+ drbd_send_sync_param(mdev, &mdev->sync_conf);
+ drbd_send_sizes(mdev, 0);
+ drbd_send_uuids(mdev);
+ drbd_send_state(mdev);
+ clear_bit(USE_DEGR_WFC_T, &mdev->flags);
+ clear_bit(RESIZE_PENDING, &mdev->flags);
+
+ return 1;
+
+out_release_sockets:
+ if (sock)
+ sock_release(sock);
+ if (msock)
+ sock_release(msock);
+ return -1;
+}
+
+static int drbd_recv_header(struct drbd_conf *mdev, struct p_header *h)
+{
+ int r;
+
+ r = drbd_recv(mdev, h, sizeof(*h));
+
+ if (unlikely(r != sizeof(*h))) {
+ dev_err(DEV, "short read expecting header on sock: r=%d\n", r);
+ return FALSE;
+ };
+ h->command = be16_to_cpu(h->command);
+ h->length = be16_to_cpu(h->length);
+ if (unlikely(h->magic != BE_DRBD_MAGIC)) {
+ dev_err(DEV, "magic?? on data m: 0x%lx c: %d l: %d\n",
+ (long)be32_to_cpu(h->magic),
+ h->command, h->length);
+ return FALSE;
+ }
+ mdev->last_received = jiffies;
+
+ return TRUE;
+}
+
+static enum finish_epoch drbd_flush_after_epoch(struct drbd_conf *mdev, struct drbd_epoch *epoch)
+{
+ int rv;
+
+ if (mdev->write_ordering >= WO_bdev_flush && get_ldev(mdev)) {
+ rv = blkdev_issue_flush(mdev->ldev->backing_bdev, NULL);
+ if (rv) {
+ dev_err(DEV, "local disk flush failed with status %d\n", rv);
+ /* would rather check on EOPNOTSUPP, but that is not reliable.
+ * don't try again for ANY return value != 0
+ * if (rv == -EOPNOTSUPP) */
+ drbd_bump_write_ordering(mdev, WO_drain_io);
+ }
+ put_ldev(mdev);
+ }
+
+ return drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE);
+}
+
+static int w_flush(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct flush_work *fw = (struct flush_work *)w;
+ struct drbd_epoch *epoch = fw->epoch;
+
+ kfree(w);
+
+ if (!test_and_set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags))
+ drbd_flush_after_epoch(mdev, epoch);
+
+ drbd_may_finish_epoch(mdev, epoch, EV_PUT |
+ (mdev->state.conn < C_CONNECTED ? EV_CLEANUP : 0));
+
+ return 1;
+}
+
+/**
+ * drbd_may_finish_epoch() - Applies an epoch_event to the epoch's state, eventually finishes it.
+ * @mdev: DRBD device.
+ * @epoch: Epoch object.
+ * @ev: Epoch event.
+ */
+static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev,
+ struct drbd_epoch *epoch,
+ enum epoch_event ev)
+{
+ int finish, epoch_size;
+ struct drbd_epoch *next_epoch;
+ int schedule_flush = 0;
+ enum finish_epoch rv = FE_STILL_LIVE;
+
+ spin_lock(&mdev->epoch_lock);
+ do {
+ next_epoch = NULL;
+ finish = 0;
+
+ epoch_size = atomic_read(&epoch->epoch_size);
+
+ switch (ev & ~EV_CLEANUP) {
+ case EV_PUT:
+ atomic_dec(&epoch->active);
+ break;
+ case EV_GOT_BARRIER_NR:
+ set_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags);
+
+ /* Special case: If we just switched from WO_bio_barrier to
+ WO_bdev_flush we should not finish the current epoch */
+ if (test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags) && epoch_size == 1 &&
+ mdev->write_ordering != WO_bio_barrier &&
+ epoch == mdev->current_epoch)
+ clear_bit(DE_CONTAINS_A_BARRIER, &epoch->flags);
+ break;
+ case EV_BARRIER_DONE:
+ set_bit(DE_BARRIER_IN_NEXT_EPOCH_DONE, &epoch->flags);
+ break;
+ case EV_BECAME_LAST:
+ /* nothing to do*/
+ break;
+ }
+
+ if (epoch_size != 0 &&
+ atomic_read(&epoch->active) == 0 &&
+ test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags) &&
+ epoch->list.prev == &mdev->current_epoch->list &&
+ !test_bit(DE_IS_FINISHING, &epoch->flags)) {
+ /* Nearly all conditions are met to finish that epoch... */
+ if (test_bit(DE_BARRIER_IN_NEXT_EPOCH_DONE, &epoch->flags) ||
+ mdev->write_ordering == WO_none ||
+ (epoch_size == 1 && test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) ||
+ ev & EV_CLEANUP) {
+ finish = 1;
+ set_bit(DE_IS_FINISHING, &epoch->flags);
+ } else if (!test_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags) &&
+ mdev->write_ordering == WO_bio_barrier) {
+ atomic_inc(&epoch->active);
+ schedule_flush = 1;
+ }
+ }
+ if (finish) {
+ if (!(ev & EV_CLEANUP)) {
+ spin_unlock(&mdev->epoch_lock);
+ drbd_send_b_ack(mdev, epoch->barrier_nr, epoch_size);
+ spin_lock(&mdev->epoch_lock);
+ }
+ dec_unacked(mdev);
+
+ if (mdev->current_epoch != epoch) {
+ next_epoch = list_entry(epoch->list.next, struct drbd_epoch, list);
+ list_del(&epoch->list);
+ ev = EV_BECAME_LAST | (ev & EV_CLEANUP);
+ mdev->epochs--;
+ kfree(epoch);
+
+ if (rv == FE_STILL_LIVE)
+ rv = FE_DESTROYED;
+ } else {
+ epoch->flags = 0;
+ atomic_set(&epoch->epoch_size, 0);
+ /* atomic_set(&epoch->active, 0); is alrady zero */
+ if (rv == FE_STILL_LIVE)
+ rv = FE_RECYCLED;
+ }
+ }
+
+ if (!next_epoch)
+ break;
+
+ epoch = next_epoch;
+ } while (1);
+
+ spin_unlock(&mdev->epoch_lock);
+
+ if (schedule_flush) {
+ struct flush_work *fw;
+ fw = kmalloc(sizeof(*fw), GFP_ATOMIC);
+ if (fw) {
+ fw->w.cb = w_flush;
+ fw->epoch = epoch;
+ drbd_queue_work(&mdev->data.work, &fw->w);
+ } else {
+ dev_warn(DEV, "Could not kmalloc a flush_work obj\n");
+ set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags);
+ /* That is not a recursion, only one level */
+ drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE);
+ drbd_may_finish_epoch(mdev, epoch, EV_PUT);
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * drbd_bump_write_ordering() - Fall back to an other write ordering method
+ * @mdev: DRBD device.
+ * @wo: Write ordering method to try.
+ */
+void drbd_bump_write_ordering(struct drbd_conf *mdev, enum write_ordering_e wo) __must_hold(local)
+{
+ enum write_ordering_e pwo;
+ static char *write_ordering_str[] = {
+ [WO_none] = "none",
+ [WO_drain_io] = "drain",
+ [WO_bdev_flush] = "flush",
+ [WO_bio_barrier] = "barrier",
+ };
+
+ pwo = mdev->write_ordering;
+ wo = min(pwo, wo);
+ if (wo == WO_bio_barrier && mdev->ldev->dc.no_disk_barrier)
+ wo = WO_bdev_flush;
+ if (wo == WO_bdev_flush && mdev->ldev->dc.no_disk_flush)
+ wo = WO_drain_io;
+ if (wo == WO_drain_io && mdev->ldev->dc.no_disk_drain)
+ wo = WO_none;
+ mdev->write_ordering = wo;
+ if (pwo != mdev->write_ordering || wo == WO_bio_barrier)
+ dev_info(DEV, "Method to ensure write ordering: %s\n", write_ordering_str[mdev->write_ordering]);
+}
+
+/**
+ * w_e_reissue() - Worker callback; Resubmit a bio, without BIO_RW_BARRIER set
+ * @mdev: DRBD device.
+ * @w: work object.
+ * @cancel: The connection will be closed anyways (unused in this callback)
+ */
+int w_e_reissue(struct drbd_conf *mdev, struct drbd_work *w, int cancel) __releases(local)
+{
+ struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
+ struct bio *bio = e->private_bio;
+
+ /* We leave DE_CONTAINS_A_BARRIER and EE_IS_BARRIER in place,
+ (and DE_BARRIER_IN_NEXT_EPOCH_ISSUED in the previous Epoch)
+ so that we can finish that epoch in drbd_may_finish_epoch().
+ That is necessary if we already have a long chain of Epochs, before
+ we realize that BIO_RW_BARRIER is actually not supported */
+
+ /* As long as the -ENOTSUPP on the barrier is reported immediately
+ that will never trigger. If it is reported late, we will just
+ print that warning and continue correctly for all future requests
+ with WO_bdev_flush */
+ if (previous_epoch(mdev, e->epoch))
+ dev_warn(DEV, "Write ordering was not enforced (one time event)\n");
+
+ /* prepare bio for re-submit,
+ * re-init volatile members */
+ /* we still have a local reference,
+ * get_ldev was done in receive_Data. */
+ bio->bi_bdev = mdev->ldev->backing_bdev;
+ bio->bi_sector = e->sector;
+ bio->bi_size = e->size;
+ bio->bi_idx = 0;
+
+ bio->bi_flags &= ~(BIO_POOL_MASK - 1);
+ bio->bi_flags |= 1 << BIO_UPTODATE;
+
+ /* don't know whether this is necessary: */
+ bio->bi_phys_segments = 0;
+ bio->bi_next = NULL;
+
+ /* these should be unchanged: */
+ /* bio->bi_end_io = drbd_endio_write_sec; */
+ /* bio->bi_vcnt = whatever; */
+
+ e->w.cb = e_end_block;
+
+ /* This is no longer a barrier request. */
+ bio->bi_rw &= ~(1UL << BIO_RW_BARRIER);
+
+ drbd_generic_make_request(mdev, DRBD_FAULT_DT_WR, bio);
+
+ return 1;
+}
+
+static int receive_Barrier(struct drbd_conf *mdev, struct p_header *h)
+{
+ int rv, issue_flush;
+ struct p_barrier *p = (struct p_barrier *)h;
+ struct drbd_epoch *epoch;
+
+ ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE;
+
+ rv = drbd_recv(mdev, h->payload, h->length);
+ ERR_IF(rv != h->length) return FALSE;
+
+ inc_unacked(mdev);
+
+ if (mdev->net_conf->wire_protocol != DRBD_PROT_C)
+ drbd_kick_lo(mdev);
+
+ mdev->current_epoch->barrier_nr = p->barrier;
+ rv = drbd_may_finish_epoch(mdev, mdev->current_epoch, EV_GOT_BARRIER_NR);
+
+ /* P_BARRIER_ACK may imply that the corresponding extent is dropped from
+ * the activity log, which means it would not be resynced in case the
+ * R_PRIMARY crashes now.
+ * Therefore we must send the barrier_ack after the barrier request was
+ * completed. */
+ switch (mdev->write_ordering) {
+ case WO_bio_barrier:
+ case WO_none:
+ if (rv == FE_RECYCLED)
+ return TRUE;
+ break;
+
+ case WO_bdev_flush:
+ case WO_drain_io:
+ D_ASSERT(rv == FE_STILL_LIVE);
+ set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &mdev->current_epoch->flags);
+ drbd_wait_ee_list_empty(mdev, &mdev->active_ee);
+ rv = drbd_flush_after_epoch(mdev, mdev->current_epoch);
+ if (rv == FE_RECYCLED)
+ return TRUE;
+
+ /* The asender will send all the ACKs and barrier ACKs out, since
+ all EEs moved from the active_ee to the done_ee. We need to
+ provide a new epoch object for the EEs that come in soon */
+ break;
+ }
+
+ /* receiver context, in the writeout path of the other node.
+ * avoid potential distributed deadlock */
+ epoch = kmalloc(sizeof(struct drbd_epoch), GFP_NOIO);
+ if (!epoch) {
+ dev_warn(DEV, "Allocation of an epoch failed, slowing down\n");
+ issue_flush = !test_and_set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags);
+ drbd_wait_ee_list_empty(mdev, &mdev->active_ee);
+ if (issue_flush) {
+ rv = drbd_flush_after_epoch(mdev, mdev->current_epoch);
+ if (rv == FE_RECYCLED)
+ return TRUE;
+ }
+
+ drbd_wait_ee_list_empty(mdev, &mdev->done_ee);
+
+ return TRUE;
+ }
+
+ epoch->flags = 0;
+ atomic_set(&epoch->epoch_size, 0);
+ atomic_set(&epoch->active, 0);
+
+ spin_lock(&mdev->epoch_lock);
+ if (atomic_read(&mdev->current_epoch->epoch_size)) {
+ list_add(&epoch->list, &mdev->current_epoch->list);
+ mdev->current_epoch = epoch;
+ mdev->epochs++;
+ } else {
+ /* The current_epoch got recycled while we allocated this one... */
+ kfree(epoch);
+ }
+ spin_unlock(&mdev->epoch_lock);
+
+ return TRUE;
+}
+
+/* used from receive_RSDataReply (recv_resync_read)
+ * and from receive_Data */
+static struct drbd_epoch_entry *
+read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __must_hold(local)
+{
+ struct drbd_epoch_entry *e;
+ struct bio_vec *bvec;
+ struct page *page;
+ struct bio *bio;
+ int dgs, ds, i, rr;
+ void *dig_in = mdev->int_dig_in;
+ void *dig_vv = mdev->int_dig_vv;
+
+ dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_r_tfm) ?
+ crypto_hash_digestsize(mdev->integrity_r_tfm) : 0;
+
+ if (dgs) {
+ rr = drbd_recv(mdev, dig_in, dgs);
+ if (rr != dgs) {
+ dev_warn(DEV, "short read receiving data digest: read %d expected %d\n",
+ rr, dgs);
+ return NULL;
+ }
+ }
+
+ data_size -= dgs;
+
+ ERR_IF(data_size & 0x1ff) return NULL;
+ ERR_IF(data_size > DRBD_MAX_SEGMENT_SIZE) return NULL;
+
+ /* GFP_NOIO, because we must not cause arbitrary write-out: in a DRBD
+ * "criss-cross" setup, that might cause write-out on some other DRBD,
+ * which in turn might block on the other node at this very place. */
+ e = drbd_alloc_ee(mdev, id, sector, data_size, GFP_NOIO);
+ if (!e)
+ return NULL;
+ bio = e->private_bio;
+ ds = data_size;
+ bio_for_each_segment(bvec, bio, i) {
+ page = bvec->bv_page;
+ rr = drbd_recv(mdev, kmap(page), min_t(int, ds, PAGE_SIZE));
+ kunmap(page);
+ if (rr != min_t(int, ds, PAGE_SIZE)) {
+ drbd_free_ee(mdev, e);
+ dev_warn(DEV, "short read receiving data: read %d expected %d\n",
+ rr, min_t(int, ds, PAGE_SIZE));
+ return NULL;
+ }
+ ds -= rr;
+ }
+
+ if (dgs) {
+ drbd_csum(mdev, mdev->integrity_r_tfm, bio, dig_vv);
+ if (memcmp(dig_in, dig_vv, dgs)) {
+ dev_err(DEV, "Digest integrity check FAILED.\n");
+ drbd_bcast_ee(mdev, "digest failed",
+ dgs, dig_in, dig_vv, e);
+ drbd_free_ee(mdev, e);
+ return NULL;
+ }
+ }
+ mdev->recv_cnt += data_size>>9;
+ return e;
+}
+
+/* drbd_drain_block() just takes a data block
+ * out of the socket input buffer, and discards it.
+ */
+static int drbd_drain_block(struct drbd_conf *mdev, int data_size)
+{
+ struct page *page;
+ int rr, rv = 1;
+ void *data;
+
+ page = drbd_pp_alloc(mdev, 1);
+
+ data = kmap(page);
+ while (data_size) {
+ rr = drbd_recv(mdev, data, min_t(int, data_size, PAGE_SIZE));
+ if (rr != min_t(int, data_size, PAGE_SIZE)) {
+ rv = 0;
+ dev_warn(DEV, "short read receiving data: read %d expected %d\n",
+ rr, min_t(int, data_size, PAGE_SIZE));
+ break;
+ }
+ data_size -= rr;
+ }
+ kunmap(page);
+ drbd_pp_free(mdev, page);
+ return rv;
+}
+
+static int recv_dless_read(struct drbd_conf *mdev, struct drbd_request *req,
+ sector_t sector, int data_size)
+{
+ struct bio_vec *bvec;
+ struct bio *bio;
+ int dgs, rr, i, expect;
+ void *dig_in = mdev->int_dig_in;
+ void *dig_vv = mdev->int_dig_vv;
+
+ dgs = (mdev->agreed_pro_version >= 87 && mdev->integrity_r_tfm) ?
+ crypto_hash_digestsize(mdev->integrity_r_tfm) : 0;
+
+ if (dgs) {
+ rr = drbd_recv(mdev, dig_in, dgs);
+ if (rr != dgs) {
+ dev_warn(DEV, "short read receiving data reply digest: read %d expected %d\n",
+ rr, dgs);
+ return 0;
+ }
+ }
+
+ data_size -= dgs;
+
+ /* optimistically update recv_cnt. if receiving fails below,
+ * we disconnect anyways, and counters will be reset. */
+ mdev->recv_cnt += data_size>>9;
+
+ bio = req->master_bio;
+ D_ASSERT(sector == bio->bi_sector);
+
+ bio_for_each_segment(bvec, bio, i) {
+ expect = min_t(int, data_size, bvec->bv_len);
+ rr = drbd_recv(mdev,
+ kmap(bvec->bv_page)+bvec->bv_offset,
+ expect);
+ kunmap(bvec->bv_page);
+ if (rr != expect) {
+ dev_warn(DEV, "short read receiving data reply: "
+ "read %d expected %d\n",
+ rr, expect);
+ return 0;
+ }
+ data_size -= rr;
+ }
+
+ if (dgs) {
+ drbd_csum(mdev, mdev->integrity_r_tfm, bio, dig_vv);
+ if (memcmp(dig_in, dig_vv, dgs)) {
+ dev_err(DEV, "Digest integrity check FAILED. Broken NICs?\n");
+ return 0;
+ }
+ }
+
+ D_ASSERT(data_size == 0);
+ return 1;
+}
+
+/* e_end_resync_block() is called via
+ * drbd_process_done_ee() by asender only */
+static int e_end_resync_block(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+{
+ struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
+ sector_t sector = e->sector;
+ int ok;
+
+ D_ASSERT(hlist_unhashed(&e->colision));
+
+ if (likely(drbd_bio_uptodate(e->private_bio))) {
+ drbd_set_in_sync(mdev, sector, e->size);
+ ok = drbd_send_ack(mdev, P_RS_WRITE_ACK, e);
+ } else {
+ /* Record failure to sync */
+ drbd_rs_failed_io(mdev, sector, e->size);
+
+ ok = drbd_send_ack(mdev, P_NEG_ACK, e);
+ }
+ dec_unacked(mdev);
+
+ return ok;
+}
+
+static int recv_resync_read(struct drbd_conf *mdev, sector_t sector, int data_size) __releases(local)
+{
+ struct drbd_epoch_entry *e;
+
+ e = read_in_block(mdev, ID_SYNCER, sector, data_size);
+ if (!e) {
+ put_ldev(mdev);
+ return FALSE;
+ }
+
+ dec_rs_pending(mdev);
+
+ e->private_bio->bi_end_io = drbd_endio_write_sec;
+ e->private_bio->bi_rw = WRITE;
+ e->w.cb = e_end_resync_block;
+
+ inc_unacked(mdev);
+ /* corresponding dec_unacked() in e_end_resync_block()
+ * respective _drbd_clear_done_ee */
+
+ spin_lock_irq(&mdev->req_lock);
+ list_add(&e->w.list, &mdev->sync_ee);
+ spin_unlock_irq(&mdev->req_lock);
+
+ drbd_generic_make_request(mdev, DRBD_FAULT_RS_WR, e->private_bio);
+ /* accounting done in endio */
+
+ maybe_kick_lo(mdev);
+ return TRUE;
+}
+
+static int receive_DataReply(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct drbd_request *req;
+ sector_t sector;
+ unsigned int header_size, data_size;
+ int ok;
+ struct p_data *p = (struct p_data *)h;
+
+ header_size = sizeof(*p) - sizeof(*h);
+ data_size = h->length - header_size;
+
+ ERR_IF(data_size == 0) return FALSE;
+
+ if (drbd_recv(mdev, h->payload, header_size) != header_size)
+ return FALSE;
+
+ sector = be64_to_cpu(p->sector);
+
+ spin_lock_irq(&mdev->req_lock);
+ req = _ar_id_to_req(mdev, p->block_id, sector);
+ spin_unlock_irq(&mdev->req_lock);
+ if (unlikely(!req)) {
+ dev_err(DEV, "Got a corrupt block_id/sector pair(1).\n");
+ return FALSE;
+ }
+
+ /* hlist_del(&req->colision) is done in _req_may_be_done, to avoid
+ * special casing it there for the various failure cases.
+ * still no race with drbd_fail_pending_reads */
+ ok = recv_dless_read(mdev, req, sector, data_size);
+
+ if (ok)
+ req_mod(req, data_received);
+ /* else: nothing. handled from drbd_disconnect...
+ * I don't think we may complete this just yet
+ * in case we are "on-disconnect: freeze" */
+
+ return ok;
+}
+
+static int receive_RSDataReply(struct drbd_conf *mdev, struct p_header *h)
+{
+ sector_t sector;
+ unsigned int header_size, data_size;
+ int ok;
+ struct p_data *p = (struct p_data *)h;
+
+ header_size = sizeof(*p) - sizeof(*h);
+ data_size = h->length - header_size;
+
+ ERR_IF(data_size == 0) return FALSE;
+
+ if (drbd_recv(mdev, h->payload, header_size) != header_size)
+ return FALSE;
+
+ sector = be64_to_cpu(p->sector);
+ D_ASSERT(p->block_id == ID_SYNCER);
+
+ if (get_ldev(mdev)) {
+ /* data is submitted to disk within recv_resync_read.
+ * corresponding put_ldev done below on error,
+ * or in drbd_endio_write_sec. */
+ ok = recv_resync_read(mdev, sector, data_size);
+ } else {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Can not write resync data to local disk.\n");
+
+ ok = drbd_drain_block(mdev, data_size);
+
+ drbd_send_ack_dp(mdev, P_NEG_ACK, p);
+ }
+
+ return ok;
+}
+
+/* e_end_block() is called via drbd_process_done_ee().
+ * this means this function only runs in the asender thread
+ */
+static int e_end_block(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
+ sector_t sector = e->sector;
+ struct drbd_epoch *epoch;
+ int ok = 1, pcmd;
+
+ if (e->flags & EE_IS_BARRIER) {
+ epoch = previous_epoch(mdev, e->epoch);
+ if (epoch)
+ drbd_may_finish_epoch(mdev, epoch, EV_BARRIER_DONE + (cancel ? EV_CLEANUP : 0));
+ }
+
+ if (mdev->net_conf->wire_protocol == DRBD_PROT_C) {
+ if (likely(drbd_bio_uptodate(e->private_bio))) {
+ pcmd = (mdev->state.conn >= C_SYNC_SOURCE &&
+ mdev->state.conn <= C_PAUSED_SYNC_T &&
+ e->flags & EE_MAY_SET_IN_SYNC) ?
+ P_RS_WRITE_ACK : P_WRITE_ACK;
+ ok &= drbd_send_ack(mdev, pcmd, e);
+ if (pcmd == P_RS_WRITE_ACK)
+ drbd_set_in_sync(mdev, sector, e->size);
+ } else {
+ ok = drbd_send_ack(mdev, P_NEG_ACK, e);
+ /* we expect it to be marked out of sync anyways...
+ * maybe assert this? */
+ }
+ dec_unacked(mdev);
+ }
+ /* we delete from the conflict detection hash _after_ we sent out the
+ * P_WRITE_ACK / P_NEG_ACK, to get the sequence number right. */
+ if (mdev->net_conf->two_primaries) {
+ spin_lock_irq(&mdev->req_lock);
+ D_ASSERT(!hlist_unhashed(&e->colision));
+ hlist_del_init(&e->colision);
+ spin_unlock_irq(&mdev->req_lock);
+ } else {
+ D_ASSERT(hlist_unhashed(&e->colision));
+ }
+
+ drbd_may_finish_epoch(mdev, e->epoch, EV_PUT + (cancel ? EV_CLEANUP : 0));
+
+ return ok;
+}
+
+static int e_send_discard_ack(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+{
+ struct drbd_epoch_entry *e = (struct drbd_epoch_entry *)w;
+ int ok = 1;
+
+ D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C);
+ ok = drbd_send_ack(mdev, P_DISCARD_ACK, e);
+
+ spin_lock_irq(&mdev->req_lock);
+ D_ASSERT(!hlist_unhashed(&e->colision));
+ hlist_del_init(&e->colision);
+ spin_unlock_irq(&mdev->req_lock);
+
+ dec_unacked(mdev);
+
+ return ok;
+}
+
+/* Called from receive_Data.
+ * Synchronize packets on sock with packets on msock.
+ *
+ * This is here so even when a P_DATA packet traveling via sock overtook an Ack
+ * packet traveling on msock, they are still processed in the order they have
+ * been sent.
+ *
+ * Note: we don't care for Ack packets overtaking P_DATA packets.
+ *
+ * In case packet_seq is larger than mdev->peer_seq number, there are
+ * outstanding packets on the msock. We wait for them to arrive.
+ * In case we are the logically next packet, we update mdev->peer_seq
+ * ourselves. Correctly handles 32bit wrap around.
+ *
+ * Assume we have a 10 GBit connection, that is about 1<<30 byte per second,
+ * about 1<<21 sectors per second. So "worst" case, we have 1<<3 == 8 seconds
+ * for the 24bit wrap (historical atomic_t guarantee on some archs), and we have
+ * 1<<9 == 512 seconds aka ages for the 32bit wrap around...
+ *
+ * returns 0 if we may process the packet,
+ * -ERESTARTSYS if we were interrupted (by disconnect signal). */
+static int drbd_wait_peer_seq(struct drbd_conf *mdev, const u32 packet_seq)
+{
+ DEFINE_WAIT(wait);
+ unsigned int p_seq;
+ long timeout;
+ int ret = 0;
+ spin_lock(&mdev->peer_seq_lock);
+ for (;;) {
+ prepare_to_wait(&mdev->seq_wait, &wait, TASK_INTERRUPTIBLE);
+ if (seq_le(packet_seq, mdev->peer_seq+1))
+ break;
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ p_seq = mdev->peer_seq;
+ spin_unlock(&mdev->peer_seq_lock);
+ timeout = schedule_timeout(30*HZ);
+ spin_lock(&mdev->peer_seq_lock);
+ if (timeout == 0 && p_seq == mdev->peer_seq) {
+ ret = -ETIMEDOUT;
+ dev_err(DEV, "ASSERT FAILED waited 30 seconds for sequence update, forcing reconnect\n");
+ break;
+ }
+ }
+ finish_wait(&mdev->seq_wait, &wait);
+ if (mdev->peer_seq+1 == packet_seq)
+ mdev->peer_seq++;
+ spin_unlock(&mdev->peer_seq_lock);
+ return ret;
+}
+
+/* mirrored write */
+static int receive_Data(struct drbd_conf *mdev, struct p_header *h)
+{
+ sector_t sector;
+ struct drbd_epoch_entry *e;
+ struct p_data *p = (struct p_data *)h;
+ int header_size, data_size;
+ int rw = WRITE;
+ u32 dp_flags;
+
+ header_size = sizeof(*p) - sizeof(*h);
+ data_size = h->length - header_size;
+
+ ERR_IF(data_size == 0) return FALSE;
+
+ if (drbd_recv(mdev, h->payload, header_size) != header_size)
+ return FALSE;
+
+ if (!get_ldev(mdev)) {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Can not write mirrored data block "
+ "to local disk.\n");
+ spin_lock(&mdev->peer_seq_lock);
+ if (mdev->peer_seq+1 == be32_to_cpu(p->seq_num))
+ mdev->peer_seq++;
+ spin_unlock(&mdev->peer_seq_lock);
+
+ drbd_send_ack_dp(mdev, P_NEG_ACK, p);
+ atomic_inc(&mdev->current_epoch->epoch_size);
+ return drbd_drain_block(mdev, data_size);
+ }
+
+ /* get_ldev(mdev) successful.
+ * Corresponding put_ldev done either below (on various errors),
+ * or in drbd_endio_write_sec, if we successfully submit the data at
+ * the end of this function. */
+
+ sector = be64_to_cpu(p->sector);
+ e = read_in_block(mdev, p->block_id, sector, data_size);
+ if (!e) {
+ put_ldev(mdev);
+ return FALSE;
+ }
+
+ e->private_bio->bi_end_io = drbd_endio_write_sec;
+ e->w.cb = e_end_block;
+
+ spin_lock(&mdev->epoch_lock);
+ e->epoch = mdev->current_epoch;
+ atomic_inc(&e->epoch->epoch_size);
+ atomic_inc(&e->epoch->active);
+
+ if (mdev->write_ordering == WO_bio_barrier && atomic_read(&e->epoch->epoch_size) == 1) {
+ struct drbd_epoch *epoch;
+ /* Issue a barrier if we start a new epoch, and the previous epoch
+ was not a epoch containing a single request which already was
+ a Barrier. */
+ epoch = list_entry(e->epoch->list.prev, struct drbd_epoch, list);
+ if (epoch == e->epoch) {
+ set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags);
+ rw |= (1<<BIO_RW_BARRIER);
+ e->flags |= EE_IS_BARRIER;
+ } else {
+ if (atomic_read(&epoch->epoch_size) > 1 ||
+ !test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) {
+ set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags);
+ set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags);
+ rw |= (1<<BIO_RW_BARRIER);
+ e->flags |= EE_IS_BARRIER;
+ }
+ }
+ }
+ spin_unlock(&mdev->epoch_lock);
+
+ dp_flags = be32_to_cpu(p->dp_flags);
+ if (dp_flags & DP_HARDBARRIER) {
+ dev_err(DEV, "ASSERT FAILED would have submitted barrier request\n");
+ /* rw |= (1<<BIO_RW_BARRIER); */
+ }
+ if (dp_flags & DP_RW_SYNC)
+ rw |= (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG);
+ if (dp_flags & DP_MAY_SET_IN_SYNC)
+ e->flags |= EE_MAY_SET_IN_SYNC;
+
+ /* I'm the receiver, I do hold a net_cnt reference. */
+ if (!mdev->net_conf->two_primaries) {
+ spin_lock_irq(&mdev->req_lock);
+ } else {
+ /* don't get the req_lock yet,
+ * we may sleep in drbd_wait_peer_seq */
+ const int size = e->size;
+ const int discard = test_bit(DISCARD_CONCURRENT, &mdev->flags);
+ DEFINE_WAIT(wait);
+ struct drbd_request *i;
+ struct hlist_node *n;
+ struct hlist_head *slot;
+ int first;
+
+ D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C);
+ BUG_ON(mdev->ee_hash == NULL);
+ BUG_ON(mdev->tl_hash == NULL);
+
+ /* conflict detection and handling:
+ * 1. wait on the sequence number,
+ * in case this data packet overtook ACK packets.
+ * 2. check our hash tables for conflicting requests.
+ * we only need to walk the tl_hash, since an ee can not
+ * have a conflict with an other ee: on the submitting
+ * node, the corresponding req had already been conflicting,
+ * and a conflicting req is never sent.
+ *
+ * Note: for two_primaries, we are protocol C,
+ * so there cannot be any request that is DONE
+ * but still on the transfer log.
+ *
+ * unconditionally add to the ee_hash.
+ *
+ * if no conflicting request is found:
+ * submit.
+ *
+ * if any conflicting request is found
+ * that has not yet been acked,
+ * AND I have the "discard concurrent writes" flag:
+ * queue (via done_ee) the P_DISCARD_ACK; OUT.
+ *
+ * if any conflicting request is found:
+ * block the receiver, waiting on misc_wait
+ * until no more conflicting requests are there,
+ * or we get interrupted (disconnect).
+ *
+ * we do not just write after local io completion of those
+ * requests, but only after req is done completely, i.e.
+ * we wait for the P_DISCARD_ACK to arrive!
+ *
+ * then proceed normally, i.e. submit.
+ */
+ if (drbd_wait_peer_seq(mdev, be32_to_cpu(p->seq_num)))
+ goto out_interrupted;
+
+ spin_lock_irq(&mdev->req_lock);
+
+ hlist_add_head(&e->colision, ee_hash_slot(mdev, sector));
+
+#define OVERLAPS overlaps(i->sector, i->size, sector, size)
+ slot = tl_hash_slot(mdev, sector);
+ first = 1;
+ for (;;) {
+ int have_unacked = 0;
+ int have_conflict = 0;
+ prepare_to_wait(&mdev->misc_wait, &wait,
+ TASK_INTERRUPTIBLE);
+ hlist_for_each_entry(i, n, slot, colision) {
+ if (OVERLAPS) {
+ /* only ALERT on first iteration,
+ * we may be woken up early... */
+ if (first)
+ dev_alert(DEV, "%s[%u] Concurrent local write detected!"
+ " new: %llus +%u; pending: %llus +%u\n",
+ current->comm, current->pid,
+ (unsigned long long)sector, size,
+ (unsigned long long)i->sector, i->size);
+ if (i->rq_state & RQ_NET_PENDING)
+ ++have_unacked;
+ ++have_conflict;
+ }
+ }
+#undef OVERLAPS
+ if (!have_conflict)
+ break;
+
+ /* Discard Ack only for the _first_ iteration */
+ if (first && discard && have_unacked) {
+ dev_alert(DEV, "Concurrent write! [DISCARD BY FLAG] sec=%llus\n",
+ (unsigned long long)sector);
+ inc_unacked(mdev);
+ e->w.cb = e_send_discard_ack;
+ list_add_tail(&e->w.list, &mdev->done_ee);
+
+ spin_unlock_irq(&mdev->req_lock);
+
+ /* we could probably send that P_DISCARD_ACK ourselves,
+ * but I don't like the receiver using the msock */
+
+ put_ldev(mdev);
+ wake_asender(mdev);
+ finish_wait(&mdev->misc_wait, &wait);
+ return TRUE;
+ }
+
+ if (signal_pending(current)) {
+ hlist_del_init(&e->colision);
+
+ spin_unlock_irq(&mdev->req_lock);
+
+ finish_wait(&mdev->misc_wait, &wait);
+ goto out_interrupted;
+ }
+
+ spin_unlock_irq(&mdev->req_lock);
+ if (first) {
+ first = 0;
+ dev_alert(DEV, "Concurrent write! [W AFTERWARDS] "
+ "sec=%llus\n", (unsigned long long)sector);
+ } else if (discard) {
+ /* we had none on the first iteration.
+ * there must be none now. */
+ D_ASSERT(have_unacked == 0);
+ }
+ schedule();
+ spin_lock_irq(&mdev->req_lock);
+ }
+ finish_wait(&mdev->misc_wait, &wait);
+ }
+
+ list_add(&e->w.list, &mdev->active_ee);
+ spin_unlock_irq(&mdev->req_lock);
+
+ switch (mdev->net_conf->wire_protocol) {
+ case DRBD_PROT_C:
+ inc_unacked(mdev);
+ /* corresponding dec_unacked() in e_end_block()
+ * respective _drbd_clear_done_ee */
+ break;
+ case DRBD_PROT_B:
+ /* I really don't like it that the receiver thread
+ * sends on the msock, but anyways */
+ drbd_send_ack(mdev, P_RECV_ACK, e);
+ break;
+ case DRBD_PROT_A:
+ /* nothing to do */
+ break;
+ }
+
+ if (mdev->state.pdsk == D_DISKLESS) {
+ /* In case we have the only disk of the cluster, */
+ drbd_set_out_of_sync(mdev, e->sector, e->size);
+ e->flags |= EE_CALL_AL_COMPLETE_IO;
+ drbd_al_begin_io(mdev, e->sector);
+ }
+
+ e->private_bio->bi_rw = rw;
+ drbd_generic_make_request(mdev, DRBD_FAULT_DT_WR, e->private_bio);
+ /* accounting done in endio */
+
+ maybe_kick_lo(mdev);
+ return TRUE;
+
+out_interrupted:
+ /* yes, the epoch_size now is imbalanced.
+ * but we drop the connection anyways, so we don't have a chance to
+ * receive a barrier... atomic_inc(&mdev->epoch_size); */
+ put_ldev(mdev);
+ drbd_free_ee(mdev, e);
+ return FALSE;
+}
+
+static int receive_DataRequest(struct drbd_conf *mdev, struct p_header *h)
+{
+ sector_t sector;
+ const sector_t capacity = drbd_get_capacity(mdev->this_bdev);
+ struct drbd_epoch_entry *e;
+ struct digest_info *di = NULL;
+ int size, digest_size;
+ unsigned int fault_type;
+ struct p_block_req *p =
+ (struct p_block_req *)h;
+ const int brps = sizeof(*p)-sizeof(*h);
+
+ if (drbd_recv(mdev, h->payload, brps) != brps)
+ return FALSE;
+
+ sector = be64_to_cpu(p->sector);
+ size = be32_to_cpu(p->blksize);
+
+ if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_SEGMENT_SIZE) {
+ dev_err(DEV, "%s:%d: sector: %llus, size: %u\n", __FILE__, __LINE__,
+ (unsigned long long)sector, size);
+ return FALSE;
+ }
+ if (sector + (size>>9) > capacity) {
+ dev_err(DEV, "%s:%d: sector: %llus, size: %u\n", __FILE__, __LINE__,
+ (unsigned long long)sector, size);
+ return FALSE;
+ }
+
+ if (!get_ldev_if_state(mdev, D_UP_TO_DATE)) {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Can not satisfy peer's read request, "
+ "no local data.\n");
+ drbd_send_ack_rp(mdev, h->command == P_DATA_REQUEST ? P_NEG_DREPLY :
+ P_NEG_RS_DREPLY , p);
+ return TRUE;
+ }
+
+ /* GFP_NOIO, because we must not cause arbitrary write-out: in a DRBD
+ * "criss-cross" setup, that might cause write-out on some other DRBD,
+ * which in turn might block on the other node at this very place. */
+ e = drbd_alloc_ee(mdev, p->block_id, sector, size, GFP_NOIO);
+ if (!e) {
+ put_ldev(mdev);
+ return FALSE;
+ }
+
+ e->private_bio->bi_rw = READ;
+ e->private_bio->bi_end_io = drbd_endio_read_sec;
+
+ switch (h->command) {
+ case P_DATA_REQUEST:
+ e->w.cb = w_e_end_data_req;
+ fault_type = DRBD_FAULT_DT_RD;
+ break;
+ case P_RS_DATA_REQUEST:
+ e->w.cb = w_e_end_rsdata_req;
+ fault_type = DRBD_FAULT_RS_RD;
+ /* Eventually this should become asynchronously. Currently it
+ * blocks the whole receiver just to delay the reading of a
+ * resync data block.
+ * the drbd_work_queue mechanism is made for this...
+ */
+ if (!drbd_rs_begin_io(mdev, sector)) {
+ /* we have been interrupted,
+ * probably connection lost! */
+ D_ASSERT(signal_pending(current));
+ goto out_free_e;
+ }
+ break;
+
+ case P_OV_REPLY:
+ case P_CSUM_RS_REQUEST:
+ fault_type = DRBD_FAULT_RS_RD;
+ digest_size = h->length - brps ;
+ di = kmalloc(sizeof(*di) + digest_size, GFP_NOIO);
+ if (!di)
+ goto out_free_e;
+
+ di->digest_size = digest_size;
+ di->digest = (((char *)di)+sizeof(struct digest_info));
+
+ if (drbd_recv(mdev, di->digest, digest_size) != digest_size)
+ goto out_free_e;
+
+ e->block_id = (u64)(unsigned long)di;
+ if (h->command == P_CSUM_RS_REQUEST) {
+ D_ASSERT(mdev->agreed_pro_version >= 89);
+ e->w.cb = w_e_end_csum_rs_req;
+ } else if (h->command == P_OV_REPLY) {
+ e->w.cb = w_e_end_ov_reply;
+ dec_rs_pending(mdev);
+ break;
+ }
+
+ if (!drbd_rs_begin_io(mdev, sector)) {
+ /* we have been interrupted, probably connection lost! */
+ D_ASSERT(signal_pending(current));
+ goto out_free_e;
+ }
+ break;
+
+ case P_OV_REQUEST:
+ if (mdev->state.conn >= C_CONNECTED &&
+ mdev->state.conn != C_VERIFY_T)
+ dev_warn(DEV, "ASSERT FAILED: got P_OV_REQUEST while being %s\n",
+ drbd_conn_str(mdev->state.conn));
+ if (mdev->ov_start_sector == ~(sector_t)0 &&
+ mdev->agreed_pro_version >= 90) {
+ mdev->ov_start_sector = sector;
+ mdev->ov_position = sector;
+ mdev->ov_left = mdev->rs_total - BM_SECT_TO_BIT(sector);
+ dev_info(DEV, "Online Verify start sector: %llu\n",
+ (unsigned long long)sector);
+ }
+ e->w.cb = w_e_end_ov_req;
+ fault_type = DRBD_FAULT_RS_RD;
+ /* Eventually this should become asynchronous. Currently it
+ * blocks the whole receiver just to delay the reading of a
+ * resync data block.
+ * the drbd_work_queue mechanism is made for this...
+ */
+ if (!drbd_rs_begin_io(mdev, sector)) {
+ /* we have been interrupted,
+ * probably connection lost! */
+ D_ASSERT(signal_pending(current));
+ goto out_free_e;
+ }
+ break;
+
+
+ default:
+ dev_err(DEV, "unexpected command (%s) in receive_DataRequest\n",
+ cmdname(h->command));
+ fault_type = DRBD_FAULT_MAX;
+ }
+
+ spin_lock_irq(&mdev->req_lock);
+ list_add(&e->w.list, &mdev->read_ee);
+ spin_unlock_irq(&mdev->req_lock);
+
+ inc_unacked(mdev);
+
+ drbd_generic_make_request(mdev, fault_type, e->private_bio);
+ maybe_kick_lo(mdev);
+
+ return TRUE;
+
+out_free_e:
+ kfree(di);
+ put_ldev(mdev);
+ drbd_free_ee(mdev, e);
+ return FALSE;
+}
+
+static int drbd_asb_recover_0p(struct drbd_conf *mdev) __must_hold(local)
+{
+ int self, peer, rv = -100;
+ unsigned long ch_self, ch_peer;
+
+ self = mdev->ldev->md.uuid[UI_BITMAP] & 1;
+ peer = mdev->p_uuid[UI_BITMAP] & 1;
+
+ ch_peer = mdev->p_uuid[UI_SIZE];
+ ch_self = mdev->comm_bm_set;
+
+ switch (mdev->net_conf->after_sb_0p) {
+ case ASB_CONSENSUS:
+ case ASB_DISCARD_SECONDARY:
+ case ASB_CALL_HELPER:
+ dev_err(DEV, "Configuration error.\n");
+ break;
+ case ASB_DISCONNECT:
+ break;
+ case ASB_DISCARD_YOUNGER_PRI:
+ if (self == 0 && peer == 1) {
+ rv = -1;
+ break;
+ }
+ if (self == 1 && peer == 0) {
+ rv = 1;
+ break;
+ }
+ /* Else fall through to one of the other strategies... */
+ case ASB_DISCARD_OLDER_PRI:
+ if (self == 0 && peer == 1) {
+ rv = 1;
+ break;
+ }
+ if (self == 1 && peer == 0) {
+ rv = -1;
+ break;
+ }
+ /* Else fall through to one of the other strategies... */
+ dev_warn(DEV, "Discard younger/older primary did not find a decision\n"
+ "Using discard-least-changes instead\n");
+ case ASB_DISCARD_ZERO_CHG:
+ if (ch_peer == 0 && ch_self == 0) {
+ rv = test_bit(DISCARD_CONCURRENT, &mdev->flags)
+ ? -1 : 1;
+ break;
+ } else {
+ if (ch_peer == 0) { rv = 1; break; }
+ if (ch_self == 0) { rv = -1; break; }
+ }
+ if (mdev->net_conf->after_sb_0p == ASB_DISCARD_ZERO_CHG)
+ break;
+ case ASB_DISCARD_LEAST_CHG:
+ if (ch_self < ch_peer)
+ rv = -1;
+ else if (ch_self > ch_peer)
+ rv = 1;
+ else /* ( ch_self == ch_peer ) */
+ /* Well, then use something else. */
+ rv = test_bit(DISCARD_CONCURRENT, &mdev->flags)
+ ? -1 : 1;
+ break;
+ case ASB_DISCARD_LOCAL:
+ rv = -1;
+ break;
+ case ASB_DISCARD_REMOTE:
+ rv = 1;
+ }
+
+ return rv;
+}
+
+static int drbd_asb_recover_1p(struct drbd_conf *mdev) __must_hold(local)
+{
+ int self, peer, hg, rv = -100;
+
+ self = mdev->ldev->md.uuid[UI_BITMAP] & 1;
+ peer = mdev->p_uuid[UI_BITMAP] & 1;
+
+ switch (mdev->net_conf->after_sb_1p) {
+ case ASB_DISCARD_YOUNGER_PRI:
+ case ASB_DISCARD_OLDER_PRI:
+ case ASB_DISCARD_LEAST_CHG:
+ case ASB_DISCARD_LOCAL:
+ case ASB_DISCARD_REMOTE:
+ dev_err(DEV, "Configuration error.\n");
+ break;
+ case ASB_DISCONNECT:
+ break;
+ case ASB_CONSENSUS:
+ hg = drbd_asb_recover_0p(mdev);
+ if (hg == -1 && mdev->state.role == R_SECONDARY)
+ rv = hg;
+ if (hg == 1 && mdev->state.role == R_PRIMARY)
+ rv = hg;
+ break;
+ case ASB_VIOLENTLY:
+ rv = drbd_asb_recover_0p(mdev);
+ break;
+ case ASB_DISCARD_SECONDARY:
+ return mdev->state.role == R_PRIMARY ? 1 : -1;
+ case ASB_CALL_HELPER:
+ hg = drbd_asb_recover_0p(mdev);
+ if (hg == -1 && mdev->state.role == R_PRIMARY) {
+ self = drbd_set_role(mdev, R_SECONDARY, 0);
+ /* drbd_change_state() does not sleep while in SS_IN_TRANSIENT_STATE,
+ * we might be here in C_WF_REPORT_PARAMS which is transient.
+ * we do not need to wait for the after state change work either. */
+ self = drbd_change_state(mdev, CS_VERBOSE, NS(role, R_SECONDARY));
+ if (self != SS_SUCCESS) {
+ drbd_khelper(mdev, "pri-lost-after-sb");
+ } else {
+ dev_warn(DEV, "Successfully gave up primary role.\n");
+ rv = hg;
+ }
+ } else
+ rv = hg;
+ }
+
+ return rv;
+}
+
+static int drbd_asb_recover_2p(struct drbd_conf *mdev) __must_hold(local)
+{
+ int self, peer, hg, rv = -100;
+
+ self = mdev->ldev->md.uuid[UI_BITMAP] & 1;
+ peer = mdev->p_uuid[UI_BITMAP] & 1;
+
+ switch (mdev->net_conf->after_sb_2p) {
+ case ASB_DISCARD_YOUNGER_PRI:
+ case ASB_DISCARD_OLDER_PRI:
+ case ASB_DISCARD_LEAST_CHG:
+ case ASB_DISCARD_LOCAL:
+ case ASB_DISCARD_REMOTE:
+ case ASB_CONSENSUS:
+ case ASB_DISCARD_SECONDARY:
+ dev_err(DEV, "Configuration error.\n");
+ break;
+ case ASB_VIOLENTLY:
+ rv = drbd_asb_recover_0p(mdev);
+ break;
+ case ASB_DISCONNECT:
+ break;
+ case ASB_CALL_HELPER:
+ hg = drbd_asb_recover_0p(mdev);
+ if (hg == -1) {
+ /* drbd_change_state() does not sleep while in SS_IN_TRANSIENT_STATE,
+ * we might be here in C_WF_REPORT_PARAMS which is transient.
+ * we do not need to wait for the after state change work either. */
+ self = drbd_change_state(mdev, CS_VERBOSE, NS(role, R_SECONDARY));
+ if (self != SS_SUCCESS) {
+ drbd_khelper(mdev, "pri-lost-after-sb");
+ } else {
+ dev_warn(DEV, "Successfully gave up primary role.\n");
+ rv = hg;
+ }
+ } else
+ rv = hg;
+ }
+
+ return rv;
+}
+
+static void drbd_uuid_dump(struct drbd_conf *mdev, char *text, u64 *uuid,
+ u64 bits, u64 flags)
+{
+ if (!uuid) {
+ dev_info(DEV, "%s uuid info vanished while I was looking!\n", text);
+ return;
+ }
+ dev_info(DEV, "%s %016llX:%016llX:%016llX:%016llX bits:%llu flags:%llX\n",
+ text,
+ (unsigned long long)uuid[UI_CURRENT],
+ (unsigned long long)uuid[UI_BITMAP],
+ (unsigned long long)uuid[UI_HISTORY_START],
+ (unsigned long long)uuid[UI_HISTORY_END],
+ (unsigned long long)bits,
+ (unsigned long long)flags);
+}
+
+/*
+ 100 after split brain try auto recover
+ 2 C_SYNC_SOURCE set BitMap
+ 1 C_SYNC_SOURCE use BitMap
+ 0 no Sync
+ -1 C_SYNC_TARGET use BitMap
+ -2 C_SYNC_TARGET set BitMap
+ -100 after split brain, disconnect
+-1000 unrelated data
+ */
+static int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(local)
+{
+ u64 self, peer;
+ int i, j;
+
+ self = mdev->ldev->md.uuid[UI_CURRENT] & ~((u64)1);
+ peer = mdev->p_uuid[UI_CURRENT] & ~((u64)1);
+
+ *rule_nr = 10;
+ if (self == UUID_JUST_CREATED && peer == UUID_JUST_CREATED)
+ return 0;
+
+ *rule_nr = 20;
+ if ((self == UUID_JUST_CREATED || self == (u64)0) &&
+ peer != UUID_JUST_CREATED)
+ return -2;
+
+ *rule_nr = 30;
+ if (self != UUID_JUST_CREATED &&
+ (peer == UUID_JUST_CREATED || peer == (u64)0))
+ return 2;
+
+ if (self == peer) {
+ int rct, dc; /* roles at crash time */
+
+ if (mdev->p_uuid[UI_BITMAP] == (u64)0 && mdev->ldev->md.uuid[UI_BITMAP] != (u64)0) {
+
+ if (mdev->agreed_pro_version < 91)
+ return -1001;
+
+ if ((mdev->ldev->md.uuid[UI_BITMAP] & ~((u64)1)) == (mdev->p_uuid[UI_HISTORY_START] & ~((u64)1)) &&
+ (mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1)) == (mdev->p_uuid[UI_HISTORY_START + 1] & ~((u64)1))) {
+ dev_info(DEV, "was SyncSource, missed the resync finished event, corrected myself:\n");
+ drbd_uuid_set_bm(mdev, 0UL);
+
+ drbd_uuid_dump(mdev, "self", mdev->ldev->md.uuid,
+ mdev->state.disk >= D_NEGOTIATING ? drbd_bm_total_weight(mdev) : 0, 0);
+ *rule_nr = 34;
+ } else {
+ dev_info(DEV, "was SyncSource (peer failed to write sync_uuid)\n");
+ *rule_nr = 36;
+ }
+
+ return 1;
+ }
+
+ if (mdev->ldev->md.uuid[UI_BITMAP] == (u64)0 && mdev->p_uuid[UI_BITMAP] != (u64)0) {
+
+ if (mdev->agreed_pro_version < 91)
+ return -1001;
+
+ if ((mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1)) == (mdev->p_uuid[UI_BITMAP] & ~((u64)1)) &&
+ (mdev->ldev->md.uuid[UI_HISTORY_START + 1] & ~((u64)1)) == (mdev->p_uuid[UI_HISTORY_START] & ~((u64)1))) {
+ dev_info(DEV, "was SyncTarget, peer missed the resync finished event, corrected peer:\n");
+
+ mdev->p_uuid[UI_HISTORY_START + 1] = mdev->p_uuid[UI_HISTORY_START];
+ mdev->p_uuid[UI_HISTORY_START] = mdev->p_uuid[UI_BITMAP];
+ mdev->p_uuid[UI_BITMAP] = 0UL;
+
+ drbd_uuid_dump(mdev, "peer", mdev->p_uuid, mdev->p_uuid[UI_SIZE], mdev->p_uuid[UI_FLAGS]);
+ *rule_nr = 35;
+ } else {
+ dev_info(DEV, "was SyncTarget (failed to write sync_uuid)\n");
+ *rule_nr = 37;
+ }
+
+ return -1;
+ }
+
+ /* Common power [off|failure] */
+ rct = (test_bit(CRASHED_PRIMARY, &mdev->flags) ? 1 : 0) +
+ (mdev->p_uuid[UI_FLAGS] & 2);
+ /* lowest bit is set when we were primary,
+ * next bit (weight 2) is set when peer was primary */
+ *rule_nr = 40;
+
+ switch (rct) {
+ case 0: /* !self_pri && !peer_pri */ return 0;
+ case 1: /* self_pri && !peer_pri */ return 1;
+ case 2: /* !self_pri && peer_pri */ return -1;
+ case 3: /* self_pri && peer_pri */
+ dc = test_bit(DISCARD_CONCURRENT, &mdev->flags);
+ return dc ? -1 : 1;
+ }
+ }
+
+ *rule_nr = 50;
+ peer = mdev->p_uuid[UI_BITMAP] & ~((u64)1);
+ if (self == peer)
+ return -1;
+
+ *rule_nr = 51;
+ peer = mdev->p_uuid[UI_HISTORY_START] & ~((u64)1);
+ if (self == peer) {
+ self = mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1);
+ peer = mdev->p_uuid[UI_HISTORY_START + 1] & ~((u64)1);
+ if (self == peer) {
+ /* The last P_SYNC_UUID did not get though. Undo the last start of
+ resync as sync source modifications of the peer's UUIDs. */
+
+ if (mdev->agreed_pro_version < 91)
+ return -1001;
+
+ mdev->p_uuid[UI_BITMAP] = mdev->p_uuid[UI_HISTORY_START];
+ mdev->p_uuid[UI_HISTORY_START] = mdev->p_uuid[UI_HISTORY_START + 1];
+ return -1;
+ }
+ }
+
+ *rule_nr = 60;
+ self = mdev->ldev->md.uuid[UI_CURRENT] & ~((u64)1);
+ for (i = UI_HISTORY_START; i <= UI_HISTORY_END; i++) {
+ peer = mdev->p_uuid[i] & ~((u64)1);
+ if (self == peer)
+ return -2;
+ }
+
+ *rule_nr = 70;
+ self = mdev->ldev->md.uuid[UI_BITMAP] & ~((u64)1);
+ peer = mdev->p_uuid[UI_CURRENT] & ~((u64)1);
+ if (self == peer)
+ return 1;
+
+ *rule_nr = 71;
+ self = mdev->ldev->md.uuid[UI_HISTORY_START] & ~((u64)1);
+ if (self == peer) {
+ self = mdev->ldev->md.uuid[UI_HISTORY_START + 1] & ~((u64)1);
+ peer = mdev->p_uuid[UI_HISTORY_START] & ~((u64)1);
+ if (self == peer) {
+ /* The last P_SYNC_UUID did not get though. Undo the last start of
+ resync as sync source modifications of our UUIDs. */
+
+ if (mdev->agreed_pro_version < 91)
+ return -1001;
+
+ _drbd_uuid_set(mdev, UI_BITMAP, mdev->ldev->md.uuid[UI_HISTORY_START]);
+ _drbd_uuid_set(mdev, UI_HISTORY_START, mdev->ldev->md.uuid[UI_HISTORY_START + 1]);
+
+ dev_info(DEV, "Undid last start of resync:\n");
+
+ drbd_uuid_dump(mdev, "self", mdev->ldev->md.uuid,
+ mdev->state.disk >= D_NEGOTIATING ? drbd_bm_total_weight(mdev) : 0, 0);
+
+ return 1;
+ }
+ }
+
+
+ *rule_nr = 80;
+ peer = mdev->p_uuid[UI_CURRENT] & ~((u64)1);
+ for (i = UI_HISTORY_START; i <= UI_HISTORY_END; i++) {
+ self = mdev->ldev->md.uuid[i] & ~((u64)1);
+ if (self == peer)
+ return 2;
+ }
+
+ *rule_nr = 90;
+ self = mdev->ldev->md.uuid[UI_BITMAP] & ~((u64)1);
+ peer = mdev->p_uuid[UI_BITMAP] & ~((u64)1);
+ if (self == peer && self != ((u64)0))
+ return 100;
+
+ *rule_nr = 100;
+ for (i = UI_HISTORY_START; i <= UI_HISTORY_END; i++) {
+ self = mdev->ldev->md.uuid[i] & ~((u64)1);
+ for (j = UI_HISTORY_START; j <= UI_HISTORY_END; j++) {
+ peer = mdev->p_uuid[j] & ~((u64)1);
+ if (self == peer)
+ return -100;
+ }
+ }
+
+ return -1000;
+}
+
+/* drbd_sync_handshake() returns the new conn state on success, or
+ CONN_MASK (-1) on failure.
+ */
+static enum drbd_conns drbd_sync_handshake(struct drbd_conf *mdev, enum drbd_role peer_role,
+ enum drbd_disk_state peer_disk) __must_hold(local)
+{
+ int hg, rule_nr;
+ enum drbd_conns rv = C_MASK;
+ enum drbd_disk_state mydisk;
+
+ mydisk = mdev->state.disk;
+ if (mydisk == D_NEGOTIATING)
+ mydisk = mdev->new_state_tmp.disk;
+
+ dev_info(DEV, "drbd_sync_handshake:\n");
+ drbd_uuid_dump(mdev, "self", mdev->ldev->md.uuid, mdev->comm_bm_set, 0);
+ drbd_uuid_dump(mdev, "peer", mdev->p_uuid,
+ mdev->p_uuid[UI_SIZE], mdev->p_uuid[UI_FLAGS]);
+
+ hg = drbd_uuid_compare(mdev, &rule_nr);
+
+ dev_info(DEV, "uuid_compare()=%d by rule %d\n", hg, rule_nr);
+
+ if (hg == -1000) {
+ dev_alert(DEV, "Unrelated data, aborting!\n");
+ return C_MASK;
+ }
+ if (hg == -1001) {
+ dev_alert(DEV, "To resolve this both sides have to support at least protocol\n");
+ return C_MASK;
+ }
+
+ if ((mydisk == D_INCONSISTENT && peer_disk > D_INCONSISTENT) ||
+ (peer_disk == D_INCONSISTENT && mydisk > D_INCONSISTENT)) {
+ int f = (hg == -100) || abs(hg) == 2;
+ hg = mydisk > D_INCONSISTENT ? 1 : -1;
+ if (f)
+ hg = hg*2;
+ dev_info(DEV, "Becoming sync %s due to disk states.\n",
+ hg > 0 ? "source" : "target");
+ }
+
+ if (hg == 100 || (hg == -100 && mdev->net_conf->always_asbp)) {
+ int pcount = (mdev->state.role == R_PRIMARY)
+ + (peer_role == R_PRIMARY);
+ int forced = (hg == -100);
+
+ switch (pcount) {
+ case 0:
+ hg = drbd_asb_recover_0p(mdev);
+ break;
+ case 1:
+ hg = drbd_asb_recover_1p(mdev);
+ break;
+ case 2:
+ hg = drbd_asb_recover_2p(mdev);
+ break;
+ }
+ if (abs(hg) < 100) {
+ dev_warn(DEV, "Split-Brain detected, %d primaries, "
+ "automatically solved. Sync from %s node\n",
+ pcount, (hg < 0) ? "peer" : "this");
+ if (forced) {
+ dev_warn(DEV, "Doing a full sync, since"
+ " UUIDs where ambiguous.\n");
+ hg = hg*2;
+ }
+ }
+ }
+
+ if (hg == -100) {
+ if (mdev->net_conf->want_lose && !(mdev->p_uuid[UI_FLAGS]&1))
+ hg = -1;
+ if (!mdev->net_conf->want_lose && (mdev->p_uuid[UI_FLAGS]&1))
+ hg = 1;
+
+ if (abs(hg) < 100)
+ dev_warn(DEV, "Split-Brain detected, manually solved. "
+ "Sync from %s node\n",
+ (hg < 0) ? "peer" : "this");
+ }
+
+ if (hg == -100) {
+ dev_alert(DEV, "Split-Brain detected, dropping connection!\n");
+ drbd_khelper(mdev, "split-brain");
+ return C_MASK;
+ }
+
+ if (hg > 0 && mydisk <= D_INCONSISTENT) {
+ dev_err(DEV, "I shall become SyncSource, but I am inconsistent!\n");
+ return C_MASK;
+ }
+
+ if (hg < 0 && /* by intention we do not use mydisk here. */
+ mdev->state.role == R_PRIMARY && mdev->state.disk >= D_CONSISTENT) {
+ switch (mdev->net_conf->rr_conflict) {
+ case ASB_CALL_HELPER:
+ drbd_khelper(mdev, "pri-lost");
+ /* fall through */
+ case ASB_DISCONNECT:
+ dev_err(DEV, "I shall become SyncTarget, but I am primary!\n");
+ return C_MASK;
+ case ASB_VIOLENTLY:
+ dev_warn(DEV, "Becoming SyncTarget, violating the stable-data"
+ "assumption\n");
+ }
+ }
+
+ if (abs(hg) >= 2) {
+ dev_info(DEV, "Writing the whole bitmap, full sync required after drbd_sync_handshake.\n");
+ if (drbd_bitmap_io(mdev, &drbd_bmio_set_n_write, "set_n_write from sync_handshake"))
+ return C_MASK;
+ }
+
+ if (hg > 0) { /* become sync source. */
+ rv = C_WF_BITMAP_S;
+ } else if (hg < 0) { /* become sync target */
+ rv = C_WF_BITMAP_T;
+ } else {
+ rv = C_CONNECTED;
+ if (drbd_bm_total_weight(mdev)) {
+ dev_info(DEV, "No resync, but %lu bits in bitmap!\n",
+ drbd_bm_total_weight(mdev));
+ }
+ }
+
+ return rv;
+}
+
+/* returns 1 if invalid */
+static int cmp_after_sb(enum drbd_after_sb_p peer, enum drbd_after_sb_p self)
+{
+ /* ASB_DISCARD_REMOTE - ASB_DISCARD_LOCAL is valid */
+ if ((peer == ASB_DISCARD_REMOTE && self == ASB_DISCARD_LOCAL) ||
+ (self == ASB_DISCARD_REMOTE && peer == ASB_DISCARD_LOCAL))
+ return 0;
+
+ /* any other things with ASB_DISCARD_REMOTE or ASB_DISCARD_LOCAL are invalid */
+ if (peer == ASB_DISCARD_REMOTE || peer == ASB_DISCARD_LOCAL ||
+ self == ASB_DISCARD_REMOTE || self == ASB_DISCARD_LOCAL)
+ return 1;
+
+ /* everything else is valid if they are equal on both sides. */
+ if (peer == self)
+ return 0;
+
+ /* everything es is invalid. */
+ return 1;
+}
+
+static int receive_protocol(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_protocol *p = (struct p_protocol *)h;
+ int header_size, data_size;
+ int p_proto, p_after_sb_0p, p_after_sb_1p, p_after_sb_2p;
+ int p_want_lose, p_two_primaries;
+ char p_integrity_alg[SHARED_SECRET_MAX] = "";
+
+ header_size = sizeof(*p) - sizeof(*h);
+ data_size = h->length - header_size;
+
+ if (drbd_recv(mdev, h->payload, header_size) != header_size)
+ return FALSE;
+
+ p_proto = be32_to_cpu(p->protocol);
+ p_after_sb_0p = be32_to_cpu(p->after_sb_0p);
+ p_after_sb_1p = be32_to_cpu(p->after_sb_1p);
+ p_after_sb_2p = be32_to_cpu(p->after_sb_2p);
+ p_want_lose = be32_to_cpu(p->want_lose);
+ p_two_primaries = be32_to_cpu(p->two_primaries);
+
+ if (p_proto != mdev->net_conf->wire_protocol) {
+ dev_err(DEV, "incompatible communication protocols\n");
+ goto disconnect;
+ }
+
+ if (cmp_after_sb(p_after_sb_0p, mdev->net_conf->after_sb_0p)) {
+ dev_err(DEV, "incompatible after-sb-0pri settings\n");
+ goto disconnect;
+ }
+
+ if (cmp_after_sb(p_after_sb_1p, mdev->net_conf->after_sb_1p)) {
+ dev_err(DEV, "incompatible after-sb-1pri settings\n");
+ goto disconnect;
+ }
+
+ if (cmp_after_sb(p_after_sb_2p, mdev->net_conf->after_sb_2p)) {
+ dev_err(DEV, "incompatible after-sb-2pri settings\n");
+ goto disconnect;
+ }
+
+ if (p_want_lose && mdev->net_conf->want_lose) {
+ dev_err(DEV, "both sides have the 'want_lose' flag set\n");
+ goto disconnect;
+ }
+
+ if (p_two_primaries != mdev->net_conf->two_primaries) {
+ dev_err(DEV, "incompatible setting of the two-primaries options\n");
+ goto disconnect;
+ }
+
+ if (mdev->agreed_pro_version >= 87) {
+ unsigned char *my_alg = mdev->net_conf->integrity_alg;
+
+ if (drbd_recv(mdev, p_integrity_alg, data_size) != data_size)
+ return FALSE;
+
+ p_integrity_alg[SHARED_SECRET_MAX-1] = 0;
+ if (strcmp(p_integrity_alg, my_alg)) {
+ dev_err(DEV, "incompatible setting of the data-integrity-alg\n");
+ goto disconnect;
+ }
+ dev_info(DEV, "data-integrity-alg: %s\n",
+ my_alg[0] ? my_alg : (unsigned char *)"<not-used>");
+ }
+
+ return TRUE;
+
+disconnect:
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+}
+
+/* helper function
+ * input: alg name, feature name
+ * return: NULL (alg name was "")
+ * ERR_PTR(error) if something goes wrong
+ * or the crypto hash ptr, if it worked out ok. */
+struct crypto_hash *drbd_crypto_alloc_digest_safe(const struct drbd_conf *mdev,
+ const char *alg, const char *name)
+{
+ struct crypto_hash *tfm;
+
+ if (!alg[0])
+ return NULL;
+
+ tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm)) {
+ dev_err(DEV, "Can not allocate \"%s\" as %s (reason: %ld)\n",
+ alg, name, PTR_ERR(tfm));
+ return tfm;
+ }
+ if (!drbd_crypto_is_hash(crypto_hash_tfm(tfm))) {
+ crypto_free_hash(tfm);
+ dev_err(DEV, "\"%s\" is not a digest (%s)\n", alg, name);
+ return ERR_PTR(-EINVAL);
+ }
+ return tfm;
+}
+
+static int receive_SyncParam(struct drbd_conf *mdev, struct p_header *h)
+{
+ int ok = TRUE;
+ struct p_rs_param_89 *p = (struct p_rs_param_89 *)h;
+ unsigned int header_size, data_size, exp_max_sz;
+ struct crypto_hash *verify_tfm = NULL;
+ struct crypto_hash *csums_tfm = NULL;
+ const int apv = mdev->agreed_pro_version;
+
+ exp_max_sz = apv <= 87 ? sizeof(struct p_rs_param)
+ : apv == 88 ? sizeof(struct p_rs_param)
+ + SHARED_SECRET_MAX
+ : /* 89 */ sizeof(struct p_rs_param_89);
+
+ if (h->length > exp_max_sz) {
+ dev_err(DEV, "SyncParam packet too long: received %u, expected <= %u bytes\n",
+ h->length, exp_max_sz);
+ return FALSE;
+ }
+
+ if (apv <= 88) {
+ header_size = sizeof(struct p_rs_param) - sizeof(*h);
+ data_size = h->length - header_size;
+ } else /* apv >= 89 */ {
+ header_size = sizeof(struct p_rs_param_89) - sizeof(*h);
+ data_size = h->length - header_size;
+ D_ASSERT(data_size == 0);
+ }
+
+ /* initialize verify_alg and csums_alg */
+ memset(p->verify_alg, 0, 2 * SHARED_SECRET_MAX);
+
+ if (drbd_recv(mdev, h->payload, header_size) != header_size)
+ return FALSE;
+
+ mdev->sync_conf.rate = be32_to_cpu(p->rate);
+
+ if (apv >= 88) {
+ if (apv == 88) {
+ if (data_size > SHARED_SECRET_MAX) {
+ dev_err(DEV, "verify-alg too long, "
+ "peer wants %u, accepting only %u byte\n",
+ data_size, SHARED_SECRET_MAX);
+ return FALSE;
+ }
+
+ if (drbd_recv(mdev, p->verify_alg, data_size) != data_size)
+ return FALSE;
+
+ /* we expect NUL terminated string */
+ /* but just in case someone tries to be evil */
+ D_ASSERT(p->verify_alg[data_size-1] == 0);
+ p->verify_alg[data_size-1] = 0;
+
+ } else /* apv >= 89 */ {
+ /* we still expect NUL terminated strings */
+ /* but just in case someone tries to be evil */
+ D_ASSERT(p->verify_alg[SHARED_SECRET_MAX-1] == 0);
+ D_ASSERT(p->csums_alg[SHARED_SECRET_MAX-1] == 0);
+ p->verify_alg[SHARED_SECRET_MAX-1] = 0;
+ p->csums_alg[SHARED_SECRET_MAX-1] = 0;
+ }
+
+ if (strcmp(mdev->sync_conf.verify_alg, p->verify_alg)) {
+ if (mdev->state.conn == C_WF_REPORT_PARAMS) {
+ dev_err(DEV, "Different verify-alg settings. me=\"%s\" peer=\"%s\"\n",
+ mdev->sync_conf.verify_alg, p->verify_alg);
+ goto disconnect;
+ }
+ verify_tfm = drbd_crypto_alloc_digest_safe(mdev,
+ p->verify_alg, "verify-alg");
+ if (IS_ERR(verify_tfm)) {
+ verify_tfm = NULL;
+ goto disconnect;
+ }
+ }
+
+ if (apv >= 89 && strcmp(mdev->sync_conf.csums_alg, p->csums_alg)) {
+ if (mdev->state.conn == C_WF_REPORT_PARAMS) {
+ dev_err(DEV, "Different csums-alg settings. me=\"%s\" peer=\"%s\"\n",
+ mdev->sync_conf.csums_alg, p->csums_alg);
+ goto disconnect;
+ }
+ csums_tfm = drbd_crypto_alloc_digest_safe(mdev,
+ p->csums_alg, "csums-alg");
+ if (IS_ERR(csums_tfm)) {
+ csums_tfm = NULL;
+ goto disconnect;
+ }
+ }
+
+
+ spin_lock(&mdev->peer_seq_lock);
+ /* lock against drbd_nl_syncer_conf() */
+ if (verify_tfm) {
+ strcpy(mdev->sync_conf.verify_alg, p->verify_alg);
+ mdev->sync_conf.verify_alg_len = strlen(p->verify_alg) + 1;
+ crypto_free_hash(mdev->verify_tfm);
+ mdev->verify_tfm = verify_tfm;
+ dev_info(DEV, "using verify-alg: \"%s\"\n", p->verify_alg);
+ }
+ if (csums_tfm) {
+ strcpy(mdev->sync_conf.csums_alg, p->csums_alg);
+ mdev->sync_conf.csums_alg_len = strlen(p->csums_alg) + 1;
+ crypto_free_hash(mdev->csums_tfm);
+ mdev->csums_tfm = csums_tfm;
+ dev_info(DEV, "using csums-alg: \"%s\"\n", p->csums_alg);
+ }
+ spin_unlock(&mdev->peer_seq_lock);
+ }
+
+ return ok;
+disconnect:
+ /* just for completeness: actually not needed,
+ * as this is not reached if csums_tfm was ok. */
+ crypto_free_hash(csums_tfm);
+ /* but free the verify_tfm again, if csums_tfm did not work out */
+ crypto_free_hash(verify_tfm);
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+}
+
+static void drbd_setup_order_type(struct drbd_conf *mdev, int peer)
+{
+ /* sorry, we currently have no working implementation
+ * of distributed TCQ */
+}
+
+/* warn if the arguments differ by more than 12.5% */
+static void warn_if_differ_considerably(struct drbd_conf *mdev,
+ const char *s, sector_t a, sector_t b)
+{
+ sector_t d;
+ if (a == 0 || b == 0)
+ return;
+ d = (a > b) ? (a - b) : (b - a);
+ if (d > (a>>3) || d > (b>>3))
+ dev_warn(DEV, "Considerable difference in %s: %llus vs. %llus\n", s,
+ (unsigned long long)a, (unsigned long long)b);
+}
+
+static int receive_sizes(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_sizes *p = (struct p_sizes *)h;
+ enum determine_dev_size dd = unchanged;
+ unsigned int max_seg_s;
+ sector_t p_size, p_usize, my_usize;
+ int ldsc = 0; /* local disk size changed */
+ enum drbd_conns nconn;
+
+ ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE;
+ if (drbd_recv(mdev, h->payload, h->length) != h->length)
+ return FALSE;
+
+ p_size = be64_to_cpu(p->d_size);
+ p_usize = be64_to_cpu(p->u_size);
+
+ if (p_size == 0 && mdev->state.disk == D_DISKLESS) {
+ dev_err(DEV, "some backing storage is needed\n");
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+ }
+
+ /* just store the peer's disk size for now.
+ * we still need to figure out whether we accept that. */
+ mdev->p_size = p_size;
+
+#define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r))
+ if (get_ldev(mdev)) {
+ warn_if_differ_considerably(mdev, "lower level device sizes",
+ p_size, drbd_get_max_capacity(mdev->ldev));
+ warn_if_differ_considerably(mdev, "user requested size",
+ p_usize, mdev->ldev->dc.disk_size);
+
+ /* if this is the first connect, or an otherwise expected
+ * param exchange, choose the minimum */
+ if (mdev->state.conn == C_WF_REPORT_PARAMS)
+ p_usize = min_not_zero((sector_t)mdev->ldev->dc.disk_size,
+ p_usize);
+
+ my_usize = mdev->ldev->dc.disk_size;
+
+ if (mdev->ldev->dc.disk_size != p_usize) {
+ mdev->ldev->dc.disk_size = p_usize;
+ dev_info(DEV, "Peer sets u_size to %lu sectors\n",
+ (unsigned long)mdev->ldev->dc.disk_size);
+ }
+
+ /* Never shrink a device with usable data during connect.
+ But allow online shrinking if we are connected. */
+ if (drbd_new_dev_size(mdev, mdev->ldev) <
+ drbd_get_capacity(mdev->this_bdev) &&
+ mdev->state.disk >= D_OUTDATED &&
+ mdev->state.conn < C_CONNECTED) {
+ dev_err(DEV, "The peer's disk size is too small!\n");
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ mdev->ldev->dc.disk_size = my_usize;
+ put_ldev(mdev);
+ return FALSE;
+ }
+ put_ldev(mdev);
+ }
+#undef min_not_zero
+
+ if (get_ldev(mdev)) {
+ dd = drbd_determin_dev_size(mdev);
+ put_ldev(mdev);
+ if (dd == dev_size_error)
+ return FALSE;
+ drbd_md_sync(mdev);
+ } else {
+ /* I am diskless, need to accept the peer's size. */
+ drbd_set_my_capacity(mdev, p_size);
+ }
+
+ if (mdev->p_uuid && mdev->state.conn <= C_CONNECTED && get_ldev(mdev)) {
+ nconn = drbd_sync_handshake(mdev,
+ mdev->state.peer, mdev->state.pdsk);
+ put_ldev(mdev);
+
+ if (nconn == C_MASK) {
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+ }
+
+ if (drbd_request_state(mdev, NS(conn, nconn)) < SS_SUCCESS) {
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+ }
+ }
+
+ if (get_ldev(mdev)) {
+ if (mdev->ldev->known_size != drbd_get_capacity(mdev->ldev->backing_bdev)) {
+ mdev->ldev->known_size = drbd_get_capacity(mdev->ldev->backing_bdev);
+ ldsc = 1;
+ }
+
+ max_seg_s = be32_to_cpu(p->max_segment_size);
+ if (max_seg_s != queue_max_segment_size(mdev->rq_queue))
+ drbd_setup_queue_param(mdev, max_seg_s);
+
+ drbd_setup_order_type(mdev, be32_to_cpu(p->queue_order_type));
+ put_ldev(mdev);
+ }
+
+ if (mdev->state.conn > C_WF_REPORT_PARAMS) {
+ if (be64_to_cpu(p->c_size) !=
+ drbd_get_capacity(mdev->this_bdev) || ldsc) {
+ /* we have different sizes, probably peer
+ * needs to know my new size... */
+ drbd_send_sizes(mdev, 0);
+ }
+ if (test_and_clear_bit(RESIZE_PENDING, &mdev->flags) ||
+ (dd == grew && mdev->state.conn == C_CONNECTED)) {
+ if (mdev->state.pdsk >= D_INCONSISTENT &&
+ mdev->state.disk >= D_INCONSISTENT)
+ resync_after_online_grow(mdev);
+ else
+ set_bit(RESYNC_AFTER_NEG, &mdev->flags);
+ }
+ }
+
+ return TRUE;
+}
+
+static int receive_uuids(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_uuids *p = (struct p_uuids *)h;
+ u64 *p_uuid;
+ int i;
+
+ ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE;
+ if (drbd_recv(mdev, h->payload, h->length) != h->length)
+ return FALSE;
+
+ p_uuid = kmalloc(sizeof(u64)*UI_EXTENDED_SIZE, GFP_NOIO);
+
+ for (i = UI_CURRENT; i < UI_EXTENDED_SIZE; i++)
+ p_uuid[i] = be64_to_cpu(p->uuid[i]);
+
+ kfree(mdev->p_uuid);
+ mdev->p_uuid = p_uuid;
+
+ if (mdev->state.conn < C_CONNECTED &&
+ mdev->state.disk < D_INCONSISTENT &&
+ mdev->state.role == R_PRIMARY &&
+ (mdev->ed_uuid & ~((u64)1)) != (p_uuid[UI_CURRENT] & ~((u64)1))) {
+ dev_err(DEV, "Can only connect to data with current UUID=%016llX\n",
+ (unsigned long long)mdev->ed_uuid);
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+ }
+
+ if (get_ldev(mdev)) {
+ int skip_initial_sync =
+ mdev->state.conn == C_CONNECTED &&
+ mdev->agreed_pro_version >= 90 &&
+ mdev->ldev->md.uuid[UI_CURRENT] == UUID_JUST_CREATED &&
+ (p_uuid[UI_FLAGS] & 8);
+ if (skip_initial_sync) {
+ dev_info(DEV, "Accepted new current UUID, preparing to skip initial sync\n");
+ drbd_bitmap_io(mdev, &drbd_bmio_clear_n_write,
+ "clear_n_write from receive_uuids");
+ _drbd_uuid_set(mdev, UI_CURRENT, p_uuid[UI_CURRENT]);
+ _drbd_uuid_set(mdev, UI_BITMAP, 0);
+ _drbd_set_state(_NS2(mdev, disk, D_UP_TO_DATE, pdsk, D_UP_TO_DATE),
+ CS_VERBOSE, NULL);
+ drbd_md_sync(mdev);
+ }
+ put_ldev(mdev);
+ }
+
+ /* Before we test for the disk state, we should wait until an eventually
+ ongoing cluster wide state change is finished. That is important if
+ we are primary and are detaching from our disk. We need to see the
+ new disk state... */
+ wait_event(mdev->misc_wait, !test_bit(CLUSTER_ST_CHANGE, &mdev->flags));
+ if (mdev->state.conn >= C_CONNECTED && mdev->state.disk < D_INCONSISTENT)
+ drbd_set_ed_uuid(mdev, p_uuid[UI_CURRENT]);
+
+ return TRUE;
+}
+
+/**
+ * convert_state() - Converts the peer's view of the cluster state to our point of view
+ * @ps: The state as seen by the peer.
+ */
+static union drbd_state convert_state(union drbd_state ps)
+{
+ union drbd_state ms;
+
+ static enum drbd_conns c_tab[] = {
+ [C_CONNECTED] = C_CONNECTED,
+
+ [C_STARTING_SYNC_S] = C_STARTING_SYNC_T,
+ [C_STARTING_SYNC_T] = C_STARTING_SYNC_S,
+ [C_DISCONNECTING] = C_TEAR_DOWN, /* C_NETWORK_FAILURE, */
+ [C_VERIFY_S] = C_VERIFY_T,
+ [C_MASK] = C_MASK,
+ };
+
+ ms.i = ps.i;
+
+ ms.conn = c_tab[ps.conn];
+ ms.peer = ps.role;
+ ms.role = ps.peer;
+ ms.pdsk = ps.disk;
+ ms.disk = ps.pdsk;
+ ms.peer_isp = (ps.aftr_isp | ps.user_isp);
+
+ return ms;
+}
+
+static int receive_req_state(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_req_state *p = (struct p_req_state *)h;
+ union drbd_state mask, val;
+ int rv;
+
+ ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE;
+ if (drbd_recv(mdev, h->payload, h->length) != h->length)
+ return FALSE;
+
+ mask.i = be32_to_cpu(p->mask);
+ val.i = be32_to_cpu(p->val);
+
+ if (test_bit(DISCARD_CONCURRENT, &mdev->flags) &&
+ test_bit(CLUSTER_ST_CHANGE, &mdev->flags)) {
+ drbd_send_sr_reply(mdev, SS_CONCURRENT_ST_CHG);
+ return TRUE;
+ }
+
+ mask = convert_state(mask);
+ val = convert_state(val);
+
+ rv = drbd_change_state(mdev, CS_VERBOSE, mask, val);
+
+ drbd_send_sr_reply(mdev, rv);
+ drbd_md_sync(mdev);
+
+ return TRUE;
+}
+
+static int receive_state(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_state *p = (struct p_state *)h;
+ enum drbd_conns nconn, oconn;
+ union drbd_state ns, peer_state;
+ enum drbd_disk_state real_peer_disk;
+ int rv;
+
+ ERR_IF(h->length != (sizeof(*p)-sizeof(*h)))
+ return FALSE;
+
+ if (drbd_recv(mdev, h->payload, h->length) != h->length)
+ return FALSE;
+
+ peer_state.i = be32_to_cpu(p->state);
+
+ real_peer_disk = peer_state.disk;
+ if (peer_state.disk == D_NEGOTIATING) {
+ real_peer_disk = mdev->p_uuid[UI_FLAGS] & 4 ? D_INCONSISTENT : D_CONSISTENT;
+ dev_info(DEV, "real peer disk state = %s\n", drbd_disk_str(real_peer_disk));
+ }
+
+ spin_lock_irq(&mdev->req_lock);
+ retry:
+ oconn = nconn = mdev->state.conn;
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (nconn == C_WF_REPORT_PARAMS)
+ nconn = C_CONNECTED;
+
+ if (mdev->p_uuid && peer_state.disk >= D_NEGOTIATING &&
+ get_ldev_if_state(mdev, D_NEGOTIATING)) {
+ int cr; /* consider resync */
+
+ /* if we established a new connection */
+ cr = (oconn < C_CONNECTED);
+ /* if we had an established connection
+ * and one of the nodes newly attaches a disk */
+ cr |= (oconn == C_CONNECTED &&
+ (peer_state.disk == D_NEGOTIATING ||
+ mdev->state.disk == D_NEGOTIATING));
+ /* if we have both been inconsistent, and the peer has been
+ * forced to be UpToDate with --overwrite-data */
+ cr |= test_bit(CONSIDER_RESYNC, &mdev->flags);
+ /* if we had been plain connected, and the admin requested to
+ * start a sync by "invalidate" or "invalidate-remote" */
+ cr |= (oconn == C_CONNECTED &&
+ (peer_state.conn >= C_STARTING_SYNC_S &&
+ peer_state.conn <= C_WF_BITMAP_T));
+
+ if (cr)
+ nconn = drbd_sync_handshake(mdev, peer_state.role, real_peer_disk);
+
+ put_ldev(mdev);
+ if (nconn == C_MASK) {
+ if (mdev->state.disk == D_NEGOTIATING) {
+ drbd_force_state(mdev, NS(disk, D_DISKLESS));
+ nconn = C_CONNECTED;
+ } else if (peer_state.disk == D_NEGOTIATING) {
+ dev_err(DEV, "Disk attach process on the peer node was aborted.\n");
+ peer_state.disk = D_DISKLESS;
+ } else {
+ D_ASSERT(oconn == C_WF_REPORT_PARAMS);
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+ }
+ }
+ }
+
+ spin_lock_irq(&mdev->req_lock);
+ if (mdev->state.conn != oconn)
+ goto retry;
+ clear_bit(CONSIDER_RESYNC, &mdev->flags);
+ ns.i = mdev->state.i;
+ ns.conn = nconn;
+ ns.peer = peer_state.role;
+ ns.pdsk = real_peer_disk;
+ ns.peer_isp = (peer_state.aftr_isp | peer_state.user_isp);
+ if ((nconn == C_CONNECTED || nconn == C_WF_BITMAP_S) && ns.disk == D_NEGOTIATING)
+ ns.disk = mdev->new_state_tmp.disk;
+
+ rv = _drbd_set_state(mdev, ns, CS_VERBOSE | CS_HARD, NULL);
+ ns = mdev->state;
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (rv < SS_SUCCESS) {
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return FALSE;
+ }
+
+ if (oconn > C_WF_REPORT_PARAMS) {
+ if (nconn > C_CONNECTED && peer_state.conn <= C_CONNECTED &&
+ peer_state.disk != D_NEGOTIATING ) {
+ /* we want resync, peer has not yet decided to sync... */
+ /* Nowadays only used when forcing a node into primary role and
+ setting its disk to UpToDate with that */
+ drbd_send_uuids(mdev);
+ drbd_send_state(mdev);
+ }
+ }
+
+ mdev->net_conf->want_lose = 0;
+
+ drbd_md_sync(mdev); /* update connected indicator, la_size, ... */
+
+ return TRUE;
+}
+
+static int receive_sync_uuid(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_rs_uuid *p = (struct p_rs_uuid *)h;
+
+ wait_event(mdev->misc_wait,
+ mdev->state.conn == C_WF_SYNC_UUID ||
+ mdev->state.conn < C_CONNECTED ||
+ mdev->state.disk < D_NEGOTIATING);
+
+ /* D_ASSERT( mdev->state.conn == C_WF_SYNC_UUID ); */
+
+ ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE;
+ if (drbd_recv(mdev, h->payload, h->length) != h->length)
+ return FALSE;
+
+ /* Here the _drbd_uuid_ functions are right, current should
+ _not_ be rotated into the history */
+ if (get_ldev_if_state(mdev, D_NEGOTIATING)) {
+ _drbd_uuid_set(mdev, UI_CURRENT, be64_to_cpu(p->uuid));
+ _drbd_uuid_set(mdev, UI_BITMAP, 0UL);
+
+ drbd_start_resync(mdev, C_SYNC_TARGET);
+
+ put_ldev(mdev);
+ } else
+ dev_err(DEV, "Ignoring SyncUUID packet!\n");
+
+ return TRUE;
+}
+
+enum receive_bitmap_ret { OK, DONE, FAILED };
+
+static enum receive_bitmap_ret
+receive_bitmap_plain(struct drbd_conf *mdev, struct p_header *h,
+ unsigned long *buffer, struct bm_xfer_ctx *c)
+{
+ unsigned num_words = min_t(size_t, BM_PACKET_WORDS, c->bm_words - c->word_offset);
+ unsigned want = num_words * sizeof(long);
+
+ if (want != h->length) {
+ dev_err(DEV, "%s:want (%u) != h->length (%u)\n", __func__, want, h->length);
+ return FAILED;
+ }
+ if (want == 0)
+ return DONE;
+ if (drbd_recv(mdev, buffer, want) != want)
+ return FAILED;
+
+ drbd_bm_merge_lel(mdev, c->word_offset, num_words, buffer);
+
+ c->word_offset += num_words;
+ c->bit_offset = c->word_offset * BITS_PER_LONG;
+ if (c->bit_offset > c->bm_bits)
+ c->bit_offset = c->bm_bits;
+
+ return OK;
+}
+
+static enum receive_bitmap_ret
+recv_bm_rle_bits(struct drbd_conf *mdev,
+ struct p_compressed_bm *p,
+ struct bm_xfer_ctx *c)
+{
+ struct bitstream bs;
+ u64 look_ahead;
+ u64 rl;
+ u64 tmp;
+ unsigned long s = c->bit_offset;
+ unsigned long e;
+ int len = p->head.length - (sizeof(*p) - sizeof(p->head));
+ int toggle = DCBP_get_start(p);
+ int have;
+ int bits;
+
+ bitstream_init(&bs, p->code, len, DCBP_get_pad_bits(p));
+
+ bits = bitstream_get_bits(&bs, &look_ahead, 64);
+ if (bits < 0)
+ return FAILED;
+
+ for (have = bits; have > 0; s += rl, toggle = !toggle) {
+ bits = vli_decode_bits(&rl, look_ahead);
+ if (bits <= 0)
+ return FAILED;
+
+ if (toggle) {
+ e = s + rl -1;
+ if (e >= c->bm_bits) {
+ dev_err(DEV, "bitmap overflow (e:%lu) while decoding bm RLE packet\n", e);
+ return FAILED;
+ }
+ _drbd_bm_set_bits(mdev, s, e);
+ }
+
+ if (have < bits) {
+ dev_err(DEV, "bitmap decoding error: h:%d b:%d la:0x%08llx l:%u/%u\n",
+ have, bits, look_ahead,
+ (unsigned int)(bs.cur.b - p->code),
+ (unsigned int)bs.buf_len);
+ return FAILED;
+ }
+ look_ahead >>= bits;
+ have -= bits;
+
+ bits = bitstream_get_bits(&bs, &tmp, 64 - have);
+ if (bits < 0)
+ return FAILED;
+ look_ahead |= tmp << have;
+ have += bits;
+ }
+
+ c->bit_offset = s;
+ bm_xfer_ctx_bit_to_word_offset(c);
+
+ return (s == c->bm_bits) ? DONE : OK;
+}
+
+static enum receive_bitmap_ret
+decode_bitmap_c(struct drbd_conf *mdev,
+ struct p_compressed_bm *p,
+ struct bm_xfer_ctx *c)
+{
+ if (DCBP_get_code(p) == RLE_VLI_Bits)
+ return recv_bm_rle_bits(mdev, p, c);
+
+ /* other variants had been implemented for evaluation,
+ * but have been dropped as this one turned out to be "best"
+ * during all our tests. */
+
+ dev_err(DEV, "receive_bitmap_c: unknown encoding %u\n", p->encoding);
+ drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR));
+ return FAILED;
+}
+
+void INFO_bm_xfer_stats(struct drbd_conf *mdev,
+ const char *direction, struct bm_xfer_ctx *c)
+{
+ /* what would it take to transfer it "plaintext" */
+ unsigned plain = sizeof(struct p_header) *
+ ((c->bm_words+BM_PACKET_WORDS-1)/BM_PACKET_WORDS+1)
+ + c->bm_words * sizeof(long);
+ unsigned total = c->bytes[0] + c->bytes[1];
+ unsigned r;
+
+ /* total can not be zero. but just in case: */
+ if (total == 0)
+ return;
+
+ /* don't report if not compressed */
+ if (total >= plain)
+ return;
+
+ /* total < plain. check for overflow, still */
+ r = (total > UINT_MAX/1000) ? (total / (plain/1000))
+ : (1000 * total / plain);
+
+ if (r > 1000)
+ r = 1000;
+
+ r = 1000 - r;
+ dev_info(DEV, "%s bitmap stats [Bytes(packets)]: plain %u(%u), RLE %u(%u), "
+ "total %u; compression: %u.%u%%\n",
+ direction,
+ c->bytes[1], c->packets[1],
+ c->bytes[0], c->packets[0],
+ total, r/10, r % 10);
+}
+
+/* Since we are processing the bitfield from lower addresses to higher,
+ it does not matter if the process it in 32 bit chunks or 64 bit
+ chunks as long as it is little endian. (Understand it as byte stream,
+ beginning with the lowest byte...) If we would use big endian
+ we would need to process it from the highest address to the lowest,
+ in order to be agnostic to the 32 vs 64 bits issue.
+
+ returns 0 on failure, 1 if we successfully received it. */
+static int receive_bitmap(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct bm_xfer_ctx c;
+ void *buffer;
+ enum receive_bitmap_ret ret;
+ int ok = FALSE;
+
+ wait_event(mdev->misc_wait, !atomic_read(&mdev->ap_bio_cnt));
+
+ drbd_bm_lock(mdev, "receive bitmap");
+
+ /* maybe we should use some per thread scratch page,
+ * and allocate that during initial device creation? */
+ buffer = (unsigned long *) __get_free_page(GFP_NOIO);
+ if (!buffer) {
+ dev_err(DEV, "failed to allocate one page buffer in %s\n", __func__);
+ goto out;
+ }
+
+ c = (struct bm_xfer_ctx) {
+ .bm_bits = drbd_bm_bits(mdev),
+ .bm_words = drbd_bm_words(mdev),
+ };
+
+ do {
+ if (h->command == P_BITMAP) {
+ ret = receive_bitmap_plain(mdev, h, buffer, &c);
+ } else if (h->command == P_COMPRESSED_BITMAP) {
+ /* MAYBE: sanity check that we speak proto >= 90,
+ * and the feature is enabled! */
+ struct p_compressed_bm *p;
+
+ if (h->length > BM_PACKET_PAYLOAD_BYTES) {
+ dev_err(DEV, "ReportCBitmap packet too large\n");
+ goto out;
+ }
+ /* use the page buff */
+ p = buffer;
+ memcpy(p, h, sizeof(*h));
+ if (drbd_recv(mdev, p->head.payload, h->length) != h->length)
+ goto out;
+ if (p->head.length <= (sizeof(*p) - sizeof(p->head))) {
+ dev_err(DEV, "ReportCBitmap packet too small (l:%u)\n", p->head.length);
+ return FAILED;
+ }
+ ret = decode_bitmap_c(mdev, p, &c);
+ } else {
+ dev_warn(DEV, "receive_bitmap: h->command neither ReportBitMap nor ReportCBitMap (is 0x%x)", h->command);
+ goto out;
+ }
+
+ c.packets[h->command == P_BITMAP]++;
+ c.bytes[h->command == P_BITMAP] += sizeof(struct p_header) + h->length;
+
+ if (ret != OK)
+ break;
+
+ if (!drbd_recv_header(mdev, h))
+ goto out;
+ } while (ret == OK);
+ if (ret == FAILED)
+ goto out;
+
+ INFO_bm_xfer_stats(mdev, "receive", &c);
+
+ if (mdev->state.conn == C_WF_BITMAP_T) {
+ ok = !drbd_send_bitmap(mdev);
+ if (!ok)
+ goto out;
+ /* Omit CS_ORDERED with this state transition to avoid deadlocks. */
+ ok = _drbd_request_state(mdev, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE);
+ D_ASSERT(ok == SS_SUCCESS);
+ } else if (mdev->state.conn != C_WF_BITMAP_S) {
+ /* admin may have requested C_DISCONNECTING,
+ * other threads may have noticed network errors */
+ dev_info(DEV, "unexpected cstate (%s) in receive_bitmap\n",
+ drbd_conn_str(mdev->state.conn));
+ }
+
+ ok = TRUE;
+ out:
+ drbd_bm_unlock(mdev);
+ if (ok && mdev->state.conn == C_WF_BITMAP_S)
+ drbd_start_resync(mdev, C_SYNC_SOURCE);
+ free_page((unsigned long) buffer);
+ return ok;
+}
+
+static int receive_skip(struct drbd_conf *mdev, struct p_header *h)
+{
+ /* TODO zero copy sink :) */
+ static char sink[128];
+ int size, want, r;
+
+ dev_warn(DEV, "skipping unknown optional packet type %d, l: %d!\n",
+ h->command, h->length);
+
+ size = h->length;
+ while (size > 0) {
+ want = min_t(int, size, sizeof(sink));
+ r = drbd_recv(mdev, sink, want);
+ ERR_IF(r <= 0) break;
+ size -= r;
+ }
+ return size == 0;
+}
+
+static int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h)
+{
+ if (mdev->state.disk >= D_INCONSISTENT)
+ drbd_kick_lo(mdev);
+
+ /* Make sure we've acked all the TCP data associated
+ * with the data requests being unplugged */
+ drbd_tcp_quickack(mdev->data.socket);
+
+ return TRUE;
+}
+
+typedef int (*drbd_cmd_handler_f)(struct drbd_conf *, struct p_header *);
+
+static drbd_cmd_handler_f drbd_default_handler[] = {
+ [P_DATA] = receive_Data,
+ [P_DATA_REPLY] = receive_DataReply,
+ [P_RS_DATA_REPLY] = receive_RSDataReply,
+ [P_BARRIER] = receive_Barrier,
+ [P_BITMAP] = receive_bitmap,
+ [P_COMPRESSED_BITMAP] = receive_bitmap,
+ [P_UNPLUG_REMOTE] = receive_UnplugRemote,
+ [P_DATA_REQUEST] = receive_DataRequest,
+ [P_RS_DATA_REQUEST] = receive_DataRequest,
+ [P_SYNC_PARAM] = receive_SyncParam,
+ [P_SYNC_PARAM89] = receive_SyncParam,
+ [P_PROTOCOL] = receive_protocol,
+ [P_UUIDS] = receive_uuids,
+ [P_SIZES] = receive_sizes,
+ [P_STATE] = receive_state,
+ [P_STATE_CHG_REQ] = receive_req_state,
+ [P_SYNC_UUID] = receive_sync_uuid,
+ [P_OV_REQUEST] = receive_DataRequest,
+ [P_OV_REPLY] = receive_DataRequest,
+ [P_CSUM_RS_REQUEST] = receive_DataRequest,
+ /* anything missing from this table is in
+ * the asender_tbl, see get_asender_cmd */
+ [P_MAX_CMD] = NULL,
+};
+
+static drbd_cmd_handler_f *drbd_cmd_handler = drbd_default_handler;
+static drbd_cmd_handler_f *drbd_opt_cmd_handler;
+
+static void drbdd(struct drbd_conf *mdev)
+{
+ drbd_cmd_handler_f handler;
+ struct p_header *header = &mdev->data.rbuf.header;
+
+ while (get_t_state(&mdev->receiver) == Running) {
+ drbd_thread_current_set_cpu(mdev);
+ if (!drbd_recv_header(mdev, header)) {
+ drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR));
+ break;
+ }
+
+ if (header->command < P_MAX_CMD)
+ handler = drbd_cmd_handler[header->command];
+ else if (P_MAY_IGNORE < header->command
+ && header->command < P_MAX_OPT_CMD)
+ handler = drbd_opt_cmd_handler[header->command-P_MAY_IGNORE];
+ else if (header->command > P_MAX_OPT_CMD)
+ handler = receive_skip;
+ else
+ handler = NULL;
+
+ if (unlikely(!handler)) {
+ dev_err(DEV, "unknown packet type %d, l: %d!\n",
+ header->command, header->length);
+ drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR));
+ break;
+ }
+ if (unlikely(!handler(mdev, header))) {
+ dev_err(DEV, "error receiving %s, l: %d!\n",
+ cmdname(header->command), header->length);
+ drbd_force_state(mdev, NS(conn, C_PROTOCOL_ERROR));
+ break;
+ }
+ }
+}
+
+static void drbd_fail_pending_reads(struct drbd_conf *mdev)
+{
+ struct hlist_head *slot;
+ struct hlist_node *pos;
+ struct hlist_node *tmp;
+ struct drbd_request *req;
+ int i;
+
+ /*
+ * Application READ requests
+ */
+ spin_lock_irq(&mdev->req_lock);
+ for (i = 0; i < APP_R_HSIZE; i++) {
+ slot = mdev->app_reads_hash+i;
+ hlist_for_each_entry_safe(req, pos, tmp, slot, colision) {
+ /* it may (but should not any longer!)
+ * be on the work queue; if that assert triggers,
+ * we need to also grab the
+ * spin_lock_irq(&mdev->data.work.q_lock);
+ * and list_del_init here. */
+ D_ASSERT(list_empty(&req->w.list));
+ /* It would be nice to complete outside of spinlock.
+ * But this is easier for now. */
+ _req_mod(req, connection_lost_while_pending);
+ }
+ }
+ for (i = 0; i < APP_R_HSIZE; i++)
+ if (!hlist_empty(mdev->app_reads_hash+i))
+ dev_warn(DEV, "ASSERT FAILED: app_reads_hash[%d].first: "
+ "%p, should be NULL\n", i, mdev->app_reads_hash[i].first);
+
+ memset(mdev->app_reads_hash, 0, APP_R_HSIZE*sizeof(void *));
+ spin_unlock_irq(&mdev->req_lock);
+}
+
+void drbd_flush_workqueue(struct drbd_conf *mdev)
+{
+ struct drbd_wq_barrier barr;
+
+ barr.w.cb = w_prev_work_done;
+ init_completion(&barr.done);
+ drbd_queue_work(&mdev->data.work, &barr.w);
+ wait_for_completion(&barr.done);
+}
+
+static void drbd_disconnect(struct drbd_conf *mdev)
+{
+ enum drbd_fencing_p fp;
+ union drbd_state os, ns;
+ int rv = SS_UNKNOWN_ERROR;
+ unsigned int i;
+
+ if (mdev->state.conn == C_STANDALONE)
+ return;
+ if (mdev->state.conn >= C_WF_CONNECTION)
+ dev_err(DEV, "ASSERT FAILED cstate = %s, expected < WFConnection\n",
+ drbd_conn_str(mdev->state.conn));
+
+ /* asender does not clean up anything. it must not interfere, either */
+ drbd_thread_stop(&mdev->asender);
+
+ mutex_lock(&mdev->data.mutex);
+ drbd_free_sock(mdev);
+ mutex_unlock(&mdev->data.mutex);
+
+ spin_lock_irq(&mdev->req_lock);
+ _drbd_wait_ee_list_empty(mdev, &mdev->active_ee);
+ _drbd_wait_ee_list_empty(mdev, &mdev->sync_ee);
+ _drbd_wait_ee_list_empty(mdev, &mdev->read_ee);
+ spin_unlock_irq(&mdev->req_lock);
+
+ /* We do not have data structures that would allow us to
+ * get the rs_pending_cnt down to 0 again.
+ * * On C_SYNC_TARGET we do not have any data structures describing
+ * the pending RSDataRequest's we have sent.
+ * * On C_SYNC_SOURCE there is no data structure that tracks
+ * the P_RS_DATA_REPLY blocks that we sent to the SyncTarget.
+ * And no, it is not the sum of the reference counts in the
+ * resync_LRU. The resync_LRU tracks the whole operation including
+ * the disk-IO, while the rs_pending_cnt only tracks the blocks
+ * on the fly. */
+ drbd_rs_cancel_all(mdev);
+ mdev->rs_total = 0;
+ mdev->rs_failed = 0;
+ atomic_set(&mdev->rs_pending_cnt, 0);
+ wake_up(&mdev->misc_wait);
+
+ /* make sure syncer is stopped and w_resume_next_sg queued */
+ del_timer_sync(&mdev->resync_timer);
+ set_bit(STOP_SYNC_TIMER, &mdev->flags);
+ resync_timer_fn((unsigned long)mdev);
+
+ /* wait for all w_e_end_data_req, w_e_end_rsdata_req, w_send_barrier,
+ * w_make_resync_request etc. which may still be on the worker queue
+ * to be "canceled" */
+ drbd_flush_workqueue(mdev);
+
+ /* This also does reclaim_net_ee(). If we do this too early, we might
+ * miss some resync ee and pages.*/
+ drbd_process_done_ee(mdev);
+
+ kfree(mdev->p_uuid);
+ mdev->p_uuid = NULL;
+
+ if (!mdev->state.susp)
+ tl_clear(mdev);
+
+ drbd_fail_pending_reads(mdev);
+
+ dev_info(DEV, "Connection closed\n");
+
+ drbd_md_sync(mdev);
+
+ fp = FP_DONT_CARE;
+ if (get_ldev(mdev)) {
+ fp = mdev->ldev->dc.fencing;
+ put_ldev(mdev);
+ }
+
+ if (mdev->state.role == R_PRIMARY) {
+ if (fp >= FP_RESOURCE && mdev->state.pdsk >= D_UNKNOWN) {
+ enum drbd_disk_state nps = drbd_try_outdate_peer(mdev);
+ drbd_request_state(mdev, NS(pdsk, nps));
+ }
+ }
+
+ spin_lock_irq(&mdev->req_lock);
+ os = mdev->state;
+ if (os.conn >= C_UNCONNECTED) {
+ /* Do not restart in case we are C_DISCONNECTING */
+ ns = os;
+ ns.conn = C_UNCONNECTED;
+ rv = _drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
+ }
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (os.conn == C_DISCONNECTING) {
+ struct hlist_head *h;
+ wait_event(mdev->misc_wait, atomic_read(&mdev->net_cnt) == 0);
+
+ /* we must not free the tl_hash
+ * while application io is still on the fly */
+ wait_event(mdev->misc_wait, atomic_read(&mdev->ap_bio_cnt) == 0);
+
+ spin_lock_irq(&mdev->req_lock);
+ /* paranoia code */
+ for (h = mdev->ee_hash; h < mdev->ee_hash + mdev->ee_hash_s; h++)
+ if (h->first)
+ dev_err(DEV, "ASSERT FAILED ee_hash[%u].first == %p, expected NULL\n",
+ (int)(h - mdev->ee_hash), h->first);
+ kfree(mdev->ee_hash);
+ mdev->ee_hash = NULL;
+ mdev->ee_hash_s = 0;
+
+ /* paranoia code */
+ for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++)
+ if (h->first)
+ dev_err(DEV, "ASSERT FAILED tl_hash[%u] == %p, expected NULL\n",
+ (int)(h - mdev->tl_hash), h->first);
+ kfree(mdev->tl_hash);
+ mdev->tl_hash = NULL;
+ mdev->tl_hash_s = 0;
+ spin_unlock_irq(&mdev->req_lock);
+
+ crypto_free_hash(mdev->cram_hmac_tfm);
+ mdev->cram_hmac_tfm = NULL;
+
+ kfree(mdev->net_conf);
+ mdev->net_conf = NULL;
+ drbd_request_state(mdev, NS(conn, C_STANDALONE));
+ }
+
+ /* tcp_close and release of sendpage pages can be deferred. I don't
+ * want to use SO_LINGER, because apparently it can be deferred for
+ * more than 20 seconds (longest time I checked).
+ *
+ * Actually we don't care for exactly when the network stack does its
+ * put_page(), but release our reference on these pages right here.
+ */
+ i = drbd_release_ee(mdev, &mdev->net_ee);
+ if (i)
+ dev_info(DEV, "net_ee not empty, killed %u entries\n", i);
+ i = atomic_read(&mdev->pp_in_use);
+ if (i)
+ dev_info(DEV, "pp_in_use = %u, expected 0\n", i);
+
+ D_ASSERT(list_empty(&mdev->read_ee));
+ D_ASSERT(list_empty(&mdev->active_ee));
+ D_ASSERT(list_empty(&mdev->sync_ee));
+ D_ASSERT(list_empty(&mdev->done_ee));
+
+ /* ok, no more ee's on the fly, it is safe to reset the epoch_size */
+ atomic_set(&mdev->current_epoch->epoch_size, 0);
+ D_ASSERT(list_empty(&mdev->current_epoch->list));
+}
+
+/*
+ * We support PRO_VERSION_MIN to PRO_VERSION_MAX. The protocol version
+ * we can agree on is stored in agreed_pro_version.
+ *
+ * feature flags and the reserved array should be enough room for future
+ * enhancements of the handshake protocol, and possible plugins...
+ *
+ * for now, they are expected to be zero, but ignored.
+ */
+static int drbd_send_handshake(struct drbd_conf *mdev)
+{
+ /* ASSERT current == mdev->receiver ... */
+ struct p_handshake *p = &mdev->data.sbuf.handshake;
+ int ok;
+
+ if (mutex_lock_interruptible(&mdev->data.mutex)) {
+ dev_err(DEV, "interrupted during initial handshake\n");
+ return 0; /* interrupted. not ok. */
+ }
+
+ if (mdev->data.socket == NULL) {
+ mutex_unlock(&mdev->data.mutex);
+ return 0;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->protocol_min = cpu_to_be32(PRO_VERSION_MIN);
+ p->protocol_max = cpu_to_be32(PRO_VERSION_MAX);
+ ok = _drbd_send_cmd( mdev, mdev->data.socket, P_HAND_SHAKE,
+ (struct p_header *)p, sizeof(*p), 0 );
+ mutex_unlock(&mdev->data.mutex);
+ return ok;
+}
+
+/*
+ * return values:
+ * 1 yes, we have a valid connection
+ * 0 oops, did not work out, please try again
+ * -1 peer talks different language,
+ * no point in trying again, please go standalone.
+ */
+static int drbd_do_handshake(struct drbd_conf *mdev)
+{
+ /* ASSERT current == mdev->receiver ... */
+ struct p_handshake *p = &mdev->data.rbuf.handshake;
+ const int expect = sizeof(struct p_handshake)
+ -sizeof(struct p_header);
+ int rv;
+
+ rv = drbd_send_handshake(mdev);
+ if (!rv)
+ return 0;
+
+ rv = drbd_recv_header(mdev, &p->head);
+ if (!rv)
+ return 0;
+
+ if (p->head.command != P_HAND_SHAKE) {
+ dev_err(DEV, "expected HandShake packet, received: %s (0x%04x)\n",
+ cmdname(p->head.command), p->head.command);
+ return -1;
+ }
+
+ if (p->head.length != expect) {
+ dev_err(DEV, "expected HandShake length: %u, received: %u\n",
+ expect, p->head.length);
+ return -1;
+ }
+
+ rv = drbd_recv(mdev, &p->head.payload, expect);
+
+ if (rv != expect) {
+ dev_err(DEV, "short read receiving handshake packet: l=%u\n", rv);
+ return 0;
+ }
+
+ p->protocol_min = be32_to_cpu(p->protocol_min);
+ p->protocol_max = be32_to_cpu(p->protocol_max);
+ if (p->protocol_max == 0)
+ p->protocol_max = p->protocol_min;
+
+ if (PRO_VERSION_MAX < p->protocol_min ||
+ PRO_VERSION_MIN > p->protocol_max)
+ goto incompat;
+
+ mdev->agreed_pro_version = min_t(int, PRO_VERSION_MAX, p->protocol_max);
+
+ dev_info(DEV, "Handshake successful: "
+ "Agreed network protocol version %d\n", mdev->agreed_pro_version);
+
+ return 1;
+
+ incompat:
+ dev_err(DEV, "incompatible DRBD dialects: "
+ "I support %d-%d, peer supports %d-%d\n",
+ PRO_VERSION_MIN, PRO_VERSION_MAX,
+ p->protocol_min, p->protocol_max);
+ return -1;
+}
+
+#if !defined(CONFIG_CRYPTO_HMAC) && !defined(CONFIG_CRYPTO_HMAC_MODULE)
+static int drbd_do_auth(struct drbd_conf *mdev)
+{
+ dev_err(DEV, "This kernel was build without CONFIG_CRYPTO_HMAC.\n");
+ dev_err(DEV, "You need to disable 'cram-hmac-alg' in drbd.conf.\n");
+ return 0;
+}
+#else
+#define CHALLENGE_LEN 64
+static int drbd_do_auth(struct drbd_conf *mdev)
+{
+ char my_challenge[CHALLENGE_LEN]; /* 64 Bytes... */
+ struct scatterlist sg;
+ char *response = NULL;
+ char *right_response = NULL;
+ char *peers_ch = NULL;
+ struct p_header p;
+ unsigned int key_len = strlen(mdev->net_conf->shared_secret);
+ unsigned int resp_size;
+ struct hash_desc desc;
+ int rv;
+
+ desc.tfm = mdev->cram_hmac_tfm;
+ desc.flags = 0;
+
+ rv = crypto_hash_setkey(mdev->cram_hmac_tfm,
+ (u8 *)mdev->net_conf->shared_secret, key_len);
+ if (rv) {
+ dev_err(DEV, "crypto_hash_setkey() failed with %d\n", rv);
+ rv = 0;
+ goto fail;
+ }
+
+ get_random_bytes(my_challenge, CHALLENGE_LEN);
+
+ rv = drbd_send_cmd2(mdev, P_AUTH_CHALLENGE, my_challenge, CHALLENGE_LEN);
+ if (!rv)
+ goto fail;
+
+ rv = drbd_recv_header(mdev, &p);
+ if (!rv)
+ goto fail;
+
+ if (p.command != P_AUTH_CHALLENGE) {
+ dev_err(DEV, "expected AuthChallenge packet, received: %s (0x%04x)\n",
+ cmdname(p.command), p.command);
+ rv = 0;
+ goto fail;
+ }
+
+ if (p.length > CHALLENGE_LEN*2) {
+ dev_err(DEV, "expected AuthChallenge payload too big.\n");
+ rv = 0;
+ goto fail;
+ }
+
+ peers_ch = kmalloc(p.length, GFP_NOIO);
+ if (peers_ch == NULL) {
+ dev_err(DEV, "kmalloc of peers_ch failed\n");
+ rv = 0;
+ goto fail;
+ }
+
+ rv = drbd_recv(mdev, peers_ch, p.length);
+
+ if (rv != p.length) {
+ dev_err(DEV, "short read AuthChallenge: l=%u\n", rv);
+ rv = 0;
+ goto fail;
+ }
+
+ resp_size = crypto_hash_digestsize(mdev->cram_hmac_tfm);
+ response = kmalloc(resp_size, GFP_NOIO);
+ if (response == NULL) {
+ dev_err(DEV, "kmalloc of response failed\n");
+ rv = 0;
+ goto fail;
+ }
+
+ sg_init_table(&sg, 1);
+ sg_set_buf(&sg, peers_ch, p.length);
+
+ rv = crypto_hash_digest(&desc, &sg, sg.length, response);
+ if (rv) {
+ dev_err(DEV, "crypto_hash_digest() failed with %d\n", rv);
+ rv = 0;
+ goto fail;
+ }
+
+ rv = drbd_send_cmd2(mdev, P_AUTH_RESPONSE, response, resp_size);
+ if (!rv)
+ goto fail;
+
+ rv = drbd_recv_header(mdev, &p);
+ if (!rv)
+ goto fail;
+
+ if (p.command != P_AUTH_RESPONSE) {
+ dev_err(DEV, "expected AuthResponse packet, received: %s (0x%04x)\n",
+ cmdname(p.command), p.command);
+ rv = 0;
+ goto fail;
+ }
+
+ if (p.length != resp_size) {
+ dev_err(DEV, "expected AuthResponse payload of wrong size\n");
+ rv = 0;
+ goto fail;
+ }
+
+ rv = drbd_recv(mdev, response , resp_size);
+
+ if (rv != resp_size) {
+ dev_err(DEV, "short read receiving AuthResponse: l=%u\n", rv);
+ rv = 0;
+ goto fail;
+ }
+
+ right_response = kmalloc(resp_size, GFP_NOIO);
+ if (response == NULL) {
+ dev_err(DEV, "kmalloc of right_response failed\n");
+ rv = 0;
+ goto fail;
+ }
+
+ sg_set_buf(&sg, my_challenge, CHALLENGE_LEN);
+
+ rv = crypto_hash_digest(&desc, &sg, sg.length, right_response);
+ if (rv) {
+ dev_err(DEV, "crypto_hash_digest() failed with %d\n", rv);
+ rv = 0;
+ goto fail;
+ }
+
+ rv = !memcmp(response, right_response, resp_size);
+
+ if (rv)
+ dev_info(DEV, "Peer authenticated using %d bytes of '%s' HMAC\n",
+ resp_size, mdev->net_conf->cram_hmac_alg);
+
+ fail:
+ kfree(peers_ch);
+ kfree(response);
+ kfree(right_response);
+
+ return rv;
+}
+#endif
+
+int drbdd_init(struct drbd_thread *thi)
+{
+ struct drbd_conf *mdev = thi->mdev;
+ unsigned int minor = mdev_to_minor(mdev);
+ int h;
+
+ sprintf(current->comm, "drbd%d_receiver", minor);
+
+ dev_info(DEV, "receiver (re)started\n");
+
+ do {
+ h = drbd_connect(mdev);
+ if (h == 0) {
+ drbd_disconnect(mdev);
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ }
+ if (h == -1) {
+ dev_warn(DEV, "Discarding network configuration.\n");
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ }
+ } while (h == 0);
+
+ if (h > 0) {
+ if (get_net_conf(mdev)) {
+ drbdd(mdev);
+ put_net_conf(mdev);
+ }
+ }
+
+ drbd_disconnect(mdev);
+
+ dev_info(DEV, "receiver terminated\n");
+ return 0;
+}
+
+/* ********* acknowledge sender ******** */
+
+static int got_RqSReply(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_req_state_reply *p = (struct p_req_state_reply *)h;
+
+ int retcode = be32_to_cpu(p->retcode);
+
+ if (retcode >= SS_SUCCESS) {
+ set_bit(CL_ST_CHG_SUCCESS, &mdev->flags);
+ } else {
+ set_bit(CL_ST_CHG_FAIL, &mdev->flags);
+ dev_err(DEV, "Requested state change failed by peer: %s (%d)\n",
+ drbd_set_st_err_str(retcode), retcode);
+ }
+ wake_up(&mdev->state_wait);
+
+ return TRUE;
+}
+
+static int got_Ping(struct drbd_conf *mdev, struct p_header *h)
+{
+ return drbd_send_ping_ack(mdev);
+
+}
+
+static int got_PingAck(struct drbd_conf *mdev, struct p_header *h)
+{
+ /* restore idle timeout */
+ mdev->meta.socket->sk->sk_rcvtimeo = mdev->net_conf->ping_int*HZ;
+
+ return TRUE;
+}
+
+static int got_IsInSync(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_block_ack *p = (struct p_block_ack *)h;
+ sector_t sector = be64_to_cpu(p->sector);
+ int blksize = be32_to_cpu(p->blksize);
+
+ D_ASSERT(mdev->agreed_pro_version >= 89);
+
+ update_peer_seq(mdev, be32_to_cpu(p->seq_num));
+
+ drbd_rs_complete_io(mdev, sector);
+ drbd_set_in_sync(mdev, sector, blksize);
+ /* rs_same_csums is supposed to count in units of BM_BLOCK_SIZE */
+ mdev->rs_same_csum += (blksize >> BM_BLOCK_SHIFT);
+ dec_rs_pending(mdev);
+
+ return TRUE;
+}
+
+/* when we receive the ACK for a write request,
+ * verify that we actually know about it */
+static struct drbd_request *_ack_id_to_req(struct drbd_conf *mdev,
+ u64 id, sector_t sector)
+{
+ struct hlist_head *slot = tl_hash_slot(mdev, sector);
+ struct hlist_node *n;
+ struct drbd_request *req;
+
+ hlist_for_each_entry(req, n, slot, colision) {
+ if ((unsigned long)req == (unsigned long)id) {
+ if (req->sector != sector) {
+ dev_err(DEV, "_ack_id_to_req: found req %p but it has "
+ "wrong sector (%llus versus %llus)\n", req,
+ (unsigned long long)req->sector,
+ (unsigned long long)sector);
+ break;
+ }
+ return req;
+ }
+ }
+ dev_err(DEV, "_ack_id_to_req: failed to find req %p, sector %llus in list\n",
+ (void *)(unsigned long)id, (unsigned long long)sector);
+ return NULL;
+}
+
+typedef struct drbd_request *(req_validator_fn)
+ (struct drbd_conf *mdev, u64 id, sector_t sector);
+
+static int validate_req_change_req_state(struct drbd_conf *mdev,
+ u64 id, sector_t sector, req_validator_fn validator,
+ const char *func, enum drbd_req_event what)
+{
+ struct drbd_request *req;
+ struct bio_and_error m;
+
+ spin_lock_irq(&mdev->req_lock);
+ req = validator(mdev, id, sector);
+ if (unlikely(!req)) {
+ spin_unlock_irq(&mdev->req_lock);
+ dev_err(DEV, "%s: got a corrupt block_id/sector pair\n", func);
+ return FALSE;
+ }
+ __req_mod(req, what, &m);
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (m.bio)
+ complete_master_bio(mdev, &m);
+ return TRUE;
+}
+
+static int got_BlockAck(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_block_ack *p = (struct p_block_ack *)h;
+ sector_t sector = be64_to_cpu(p->sector);
+ int blksize = be32_to_cpu(p->blksize);
+ enum drbd_req_event what;
+
+ update_peer_seq(mdev, be32_to_cpu(p->seq_num));
+
+ if (is_syncer_block_id(p->block_id)) {
+ drbd_set_in_sync(mdev, sector, blksize);
+ dec_rs_pending(mdev);
+ return TRUE;
+ }
+ switch (be16_to_cpu(h->command)) {
+ case P_RS_WRITE_ACK:
+ D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C);
+ what = write_acked_by_peer_and_sis;
+ break;
+ case P_WRITE_ACK:
+ D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C);
+ what = write_acked_by_peer;
+ break;
+ case P_RECV_ACK:
+ D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_B);
+ what = recv_acked_by_peer;
+ break;
+ case P_DISCARD_ACK:
+ D_ASSERT(mdev->net_conf->wire_protocol == DRBD_PROT_C);
+ what = conflict_discarded_by_peer;
+ break;
+ default:
+ D_ASSERT(0);
+ return FALSE;
+ }
+
+ return validate_req_change_req_state(mdev, p->block_id, sector,
+ _ack_id_to_req, __func__ , what);
+}
+
+static int got_NegAck(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_block_ack *p = (struct p_block_ack *)h;
+ sector_t sector = be64_to_cpu(p->sector);
+
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_warn(DEV, "Got NegAck packet. Peer is in troubles?\n");
+
+ update_peer_seq(mdev, be32_to_cpu(p->seq_num));
+
+ if (is_syncer_block_id(p->block_id)) {
+ int size = be32_to_cpu(p->blksize);
+ dec_rs_pending(mdev);
+ drbd_rs_failed_io(mdev, sector, size);
+ return TRUE;
+ }
+ return validate_req_change_req_state(mdev, p->block_id, sector,
+ _ack_id_to_req, __func__ , neg_acked);
+}
+
+static int got_NegDReply(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_block_ack *p = (struct p_block_ack *)h;
+ sector_t sector = be64_to_cpu(p->sector);
+
+ update_peer_seq(mdev, be32_to_cpu(p->seq_num));
+ dev_err(DEV, "Got NegDReply; Sector %llus, len %u; Fail original request.\n",
+ (unsigned long long)sector, be32_to_cpu(p->blksize));
+
+ return validate_req_change_req_state(mdev, p->block_id, sector,
+ _ar_id_to_req, __func__ , neg_acked);
+}
+
+static int got_NegRSDReply(struct drbd_conf *mdev, struct p_header *h)
+{
+ sector_t sector;
+ int size;
+ struct p_block_ack *p = (struct p_block_ack *)h;
+
+ sector = be64_to_cpu(p->sector);
+ size = be32_to_cpu(p->blksize);
+ D_ASSERT(p->block_id == ID_SYNCER);
+
+ update_peer_seq(mdev, be32_to_cpu(p->seq_num));
+
+ dec_rs_pending(mdev);
+
+ if (get_ldev_if_state(mdev, D_FAILED)) {
+ drbd_rs_complete_io(mdev, sector);
+ drbd_rs_failed_io(mdev, sector, size);
+ put_ldev(mdev);
+ }
+
+ return TRUE;
+}
+
+static int got_BarrierAck(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_barrier_ack *p = (struct p_barrier_ack *)h;
+
+ tl_release(mdev, p->barrier, be32_to_cpu(p->set_size));
+
+ return TRUE;
+}
+
+static int got_OVResult(struct drbd_conf *mdev, struct p_header *h)
+{
+ struct p_block_ack *p = (struct p_block_ack *)h;
+ struct drbd_work *w;
+ sector_t sector;
+ int size;
+
+ sector = be64_to_cpu(p->sector);
+ size = be32_to_cpu(p->blksize);
+
+ update_peer_seq(mdev, be32_to_cpu(p->seq_num));
+
+ if (be64_to_cpu(p->block_id) == ID_OUT_OF_SYNC)
+ drbd_ov_oos_found(mdev, sector, size);
+ else
+ ov_oos_print(mdev);
+
+ drbd_rs_complete_io(mdev, sector);
+ dec_rs_pending(mdev);
+
+ if (--mdev->ov_left == 0) {
+ w = kmalloc(sizeof(*w), GFP_NOIO);
+ if (w) {
+ w->cb = w_ov_finished;
+ drbd_queue_work_front(&mdev->data.work, w);
+ } else {
+ dev_err(DEV, "kmalloc(w) failed.");
+ ov_oos_print(mdev);
+ drbd_resync_finished(mdev);
+ }
+ }
+ return TRUE;
+}
+
+struct asender_cmd {
+ size_t pkt_size;
+ int (*process)(struct drbd_conf *mdev, struct p_header *h);
+};
+
+static struct asender_cmd *get_asender_cmd(int cmd)
+{
+ static struct asender_cmd asender_tbl[] = {
+ /* anything missing from this table is in
+ * the drbd_cmd_handler (drbd_default_handler) table,
+ * see the beginning of drbdd() */
+ [P_PING] = { sizeof(struct p_header), got_Ping },
+ [P_PING_ACK] = { sizeof(struct p_header), got_PingAck },
+ [P_RECV_ACK] = { sizeof(struct p_block_ack), got_BlockAck },
+ [P_WRITE_ACK] = { sizeof(struct p_block_ack), got_BlockAck },
+ [P_RS_WRITE_ACK] = { sizeof(struct p_block_ack), got_BlockAck },
+ [P_DISCARD_ACK] = { sizeof(struct p_block_ack), got_BlockAck },
+ [P_NEG_ACK] = { sizeof(struct p_block_ack), got_NegAck },
+ [P_NEG_DREPLY] = { sizeof(struct p_block_ack), got_NegDReply },
+ [P_NEG_RS_DREPLY] = { sizeof(struct p_block_ack), got_NegRSDReply},
+ [P_OV_RESULT] = { sizeof(struct p_block_ack), got_OVResult },
+ [P_BARRIER_ACK] = { sizeof(struct p_barrier_ack), got_BarrierAck },
+ [P_STATE_CHG_REPLY] = { sizeof(struct p_req_state_reply), got_RqSReply },
+ [P_RS_IS_IN_SYNC] = { sizeof(struct p_block_ack), got_IsInSync },
+ [P_MAX_CMD] = { 0, NULL },
+ };
+ if (cmd > P_MAX_CMD || asender_tbl[cmd].process == NULL)
+ return NULL;
+ return &asender_tbl[cmd];
+}
+
+int drbd_asender(struct drbd_thread *thi)
+{
+ struct drbd_conf *mdev = thi->mdev;
+ struct p_header *h = &mdev->meta.rbuf.header;
+ struct asender_cmd *cmd = NULL;
+
+ int rv, len;
+ void *buf = h;
+ int received = 0;
+ int expect = sizeof(struct p_header);
+ int empty;
+
+ sprintf(current->comm, "drbd%d_asender", mdev_to_minor(mdev));
+
+ current->policy = SCHED_RR; /* Make this a realtime task! */
+ current->rt_priority = 2; /* more important than all other tasks */
+
+ while (get_t_state(thi) == Running) {
+ drbd_thread_current_set_cpu(mdev);
+ if (test_and_clear_bit(SEND_PING, &mdev->flags)) {
+ ERR_IF(!drbd_send_ping(mdev)) goto reconnect;
+ mdev->meta.socket->sk->sk_rcvtimeo =
+ mdev->net_conf->ping_timeo*HZ/10;
+ }
+
+ /* conditionally cork;
+ * it may hurt latency if we cork without much to send */
+ if (!mdev->net_conf->no_cork &&
+ 3 < atomic_read(&mdev->unacked_cnt))
+ drbd_tcp_cork(mdev->meta.socket);
+ while (1) {
+ clear_bit(SIGNAL_ASENDER, &mdev->flags);
+ flush_signals(current);
+ if (!drbd_process_done_ee(mdev)) {
+ dev_err(DEV, "process_done_ee() = NOT_OK\n");
+ goto reconnect;
+ }
+ /* to avoid race with newly queued ACKs */
+ set_bit(SIGNAL_ASENDER, &mdev->flags);
+ spin_lock_irq(&mdev->req_lock);
+ empty = list_empty(&mdev->done_ee);
+ spin_unlock_irq(&mdev->req_lock);
+ /* new ack may have been queued right here,
+ * but then there is also a signal pending,
+ * and we start over... */
+ if (empty)
+ break;
+ }
+ /* but unconditionally uncork unless disabled */
+ if (!mdev->net_conf->no_cork)
+ drbd_tcp_uncork(mdev->meta.socket);
+
+ /* short circuit, recv_msg would return EINTR anyways. */
+ if (signal_pending(current))
+ continue;
+
+ rv = drbd_recv_short(mdev, mdev->meta.socket,
+ buf, expect-received, 0);
+ clear_bit(SIGNAL_ASENDER, &mdev->flags);
+
+ flush_signals(current);
+
+ /* Note:
+ * -EINTR (on meta) we got a signal
+ * -EAGAIN (on meta) rcvtimeo expired
+ * -ECONNRESET other side closed the connection
+ * -ERESTARTSYS (on data) we got a signal
+ * rv < 0 other than above: unexpected error!
+ * rv == expected: full header or command
+ * rv < expected: "woken" by signal during receive
+ * rv == 0 : "connection shut down by peer"
+ */
+ if (likely(rv > 0)) {
+ received += rv;
+ buf += rv;
+ } else if (rv == 0) {
+ dev_err(DEV, "meta connection shut down by peer.\n");
+ goto reconnect;
+ } else if (rv == -EAGAIN) {
+ if (mdev->meta.socket->sk->sk_rcvtimeo ==
+ mdev->net_conf->ping_timeo*HZ/10) {
+ dev_err(DEV, "PingAck did not arrive in time.\n");
+ goto reconnect;
+ }
+ set_bit(SEND_PING, &mdev->flags);
+ continue;
+ } else if (rv == -EINTR) {
+ continue;
+ } else {
+ dev_err(DEV, "sock_recvmsg returned %d\n", rv);
+ goto reconnect;
+ }
+
+ if (received == expect && cmd == NULL) {
+ if (unlikely(h->magic != BE_DRBD_MAGIC)) {
+ dev_err(DEV, "magic?? on meta m: 0x%lx c: %d l: %d\n",
+ (long)be32_to_cpu(h->magic),
+ h->command, h->length);
+ goto reconnect;
+ }
+ cmd = get_asender_cmd(be16_to_cpu(h->command));
+ len = be16_to_cpu(h->length);
+ if (unlikely(cmd == NULL)) {
+ dev_err(DEV, "unknown command?? on meta m: 0x%lx c: %d l: %d\n",
+ (long)be32_to_cpu(h->magic),
+ h->command, h->length);
+ goto disconnect;
+ }
+ expect = cmd->pkt_size;
+ ERR_IF(len != expect-sizeof(struct p_header))
+ goto reconnect;
+ }
+ if (received == expect) {
+ D_ASSERT(cmd != NULL);
+ if (!cmd->process(mdev, h))
+ goto reconnect;
+
+ buf = h;
+ received = 0;
+ expect = sizeof(struct p_header);
+ cmd = NULL;
+ }
+ }
+
+ if (0) {
+reconnect:
+ drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE));
+ }
+ if (0) {
+disconnect:
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ }
+ clear_bit(SIGNAL_ASENDER, &mdev->flags);
+
+ D_ASSERT(mdev->state.conn < C_CONNECTED);
+ dev_info(DEV, "asender terminated\n");
+
+ return 0;
+}
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
new file mode 100644
index 00000000000..de81ab7b462
--- /dev/null
+++ b/drivers/block/drbd/drbd_req.c
@@ -0,0 +1,1125 @@
+/*
+ drbd_req.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/drbd.h>
+#include "drbd_int.h"
+#include "drbd_req.h"
+
+
+/* Update disk stats at start of I/O request */
+static void _drbd_start_io_acct(struct drbd_conf *mdev, struct drbd_request *req, struct bio *bio)
+{
+ const int rw = bio_data_dir(bio);
+ int cpu;
+ cpu = part_stat_lock();
+ part_stat_inc(cpu, &mdev->vdisk->part0, ios[rw]);
+ part_stat_add(cpu, &mdev->vdisk->part0, sectors[rw], bio_sectors(bio));
+ part_inc_in_flight(&mdev->vdisk->part0, rw);
+ part_stat_unlock();
+}
+
+/* Update disk stats when completing request upwards */
+static void _drbd_end_io_acct(struct drbd_conf *mdev, struct drbd_request *req)
+{
+ int rw = bio_data_dir(req->master_bio);
+ unsigned long duration = jiffies - req->start_time;
+ int cpu;
+ cpu = part_stat_lock();
+ part_stat_add(cpu, &mdev->vdisk->part0, ticks[rw], duration);
+ part_round_stats(cpu, &mdev->vdisk->part0);
+ part_dec_in_flight(&mdev->vdisk->part0, rw);
+ part_stat_unlock();
+}
+
+static void _req_is_done(struct drbd_conf *mdev, struct drbd_request *req, const int rw)
+{
+ const unsigned long s = req->rq_state;
+ /* if it was a write, we may have to set the corresponding
+ * bit(s) out-of-sync first. If it had a local part, we need to
+ * release the reference to the activity log. */
+ if (rw == WRITE) {
+ /* remove it from the transfer log.
+ * well, only if it had been there in the first
+ * place... if it had not (local only or conflicting
+ * and never sent), it should still be "empty" as
+ * initialized in drbd_req_new(), so we can list_del() it
+ * here unconditionally */
+ list_del(&req->tl_requests);
+ /* Set out-of-sync unless both OK flags are set
+ * (local only or remote failed).
+ * Other places where we set out-of-sync:
+ * READ with local io-error */
+ if (!(s & RQ_NET_OK) || !(s & RQ_LOCAL_OK))
+ drbd_set_out_of_sync(mdev, req->sector, req->size);
+
+ if ((s & RQ_NET_OK) && (s & RQ_LOCAL_OK) && (s & RQ_NET_SIS))
+ drbd_set_in_sync(mdev, req->sector, req->size);
+
+ /* one might be tempted to move the drbd_al_complete_io
+ * to the local io completion callback drbd_endio_pri.
+ * but, if this was a mirror write, we may only
+ * drbd_al_complete_io after this is RQ_NET_DONE,
+ * otherwise the extent could be dropped from the al
+ * before it has actually been written on the peer.
+ * if we crash before our peer knows about the request,
+ * but after the extent has been dropped from the al,
+ * we would forget to resync the corresponding extent.
+ */
+ if (s & RQ_LOCAL_MASK) {
+ if (get_ldev_if_state(mdev, D_FAILED)) {
+ drbd_al_complete_io(mdev, req->sector);
+ put_ldev(mdev);
+ } else if (__ratelimit(&drbd_ratelimit_state)) {
+ dev_warn(DEV, "Should have called drbd_al_complete_io(, %llu), "
+ "but my Disk seems to have failed :(\n",
+ (unsigned long long) req->sector);
+ }
+ }
+ }
+
+ /* if it was a local io error, we want to notify our
+ * peer about that, and see if we need to
+ * detach the disk and stuff.
+ * to avoid allocating some special work
+ * struct, reuse the request. */
+
+ /* THINK
+ * why do we do this not when we detect the error,
+ * but delay it until it is "done", i.e. possibly
+ * until the next barrier ack? */
+
+ if (rw == WRITE &&
+ ((s & RQ_LOCAL_MASK) && !(s & RQ_LOCAL_OK))) {
+ if (!(req->w.list.next == LIST_POISON1 ||
+ list_empty(&req->w.list))) {
+ /* DEBUG ASSERT only; if this triggers, we
+ * probably corrupt the worker list here */
+ dev_err(DEV, "req->w.list.next = %p\n", req->w.list.next);
+ dev_err(DEV, "req->w.list.prev = %p\n", req->w.list.prev);
+ }
+ req->w.cb = w_io_error;
+ drbd_queue_work(&mdev->data.work, &req->w);
+ /* drbd_req_free() is done in w_io_error */
+ } else {
+ drbd_req_free(req);
+ }
+}
+
+static void queue_barrier(struct drbd_conf *mdev)
+{
+ struct drbd_tl_epoch *b;
+
+ /* We are within the req_lock. Once we queued the barrier for sending,
+ * we set the CREATE_BARRIER bit. It is cleared as soon as a new
+ * barrier/epoch object is added. This is the only place this bit is
+ * set. It indicates that the barrier for this epoch is already queued,
+ * and no new epoch has been created yet. */
+ if (test_bit(CREATE_BARRIER, &mdev->flags))
+ return;
+
+ b = mdev->newest_tle;
+ b->w.cb = w_send_barrier;
+ /* inc_ap_pending done here, so we won't
+ * get imbalanced on connection loss.
+ * dec_ap_pending will be done in got_BarrierAck
+ * or (on connection loss) in tl_clear. */
+ inc_ap_pending(mdev);
+ drbd_queue_work(&mdev->data.work, &b->w);
+ set_bit(CREATE_BARRIER, &mdev->flags);
+}
+
+static void _about_to_complete_local_write(struct drbd_conf *mdev,
+ struct drbd_request *req)
+{
+ const unsigned long s = req->rq_state;
+ struct drbd_request *i;
+ struct drbd_epoch_entry *e;
+ struct hlist_node *n;
+ struct hlist_head *slot;
+
+ /* before we can signal completion to the upper layers,
+ * we may need to close the current epoch */
+ if (mdev->state.conn >= C_CONNECTED &&
+ req->epoch == mdev->newest_tle->br_number)
+ queue_barrier(mdev);
+
+ /* we need to do the conflict detection stuff,
+ * if we have the ee_hash (two_primaries) and
+ * this has been on the network */
+ if ((s & RQ_NET_DONE) && mdev->ee_hash != NULL) {
+ const sector_t sector = req->sector;
+ const int size = req->size;
+
+ /* ASSERT:
+ * there must be no conflicting requests, since
+ * they must have been failed on the spot */
+#define OVERLAPS overlaps(sector, size, i->sector, i->size)
+ slot = tl_hash_slot(mdev, sector);
+ hlist_for_each_entry(i, n, slot, colision) {
+ if (OVERLAPS) {
+ dev_alert(DEV, "LOGIC BUG: completed: %p %llus +%u; "
+ "other: %p %llus +%u\n",
+ req, (unsigned long long)sector, size,
+ i, (unsigned long long)i->sector, i->size);
+ }
+ }
+
+ /* maybe "wake" those conflicting epoch entries
+ * that wait for this request to finish.
+ *
+ * currently, there can be only _one_ such ee
+ * (well, or some more, which would be pending
+ * P_DISCARD_ACK not yet sent by the asender...),
+ * since we block the receiver thread upon the
+ * first conflict detection, which will wait on
+ * misc_wait. maybe we want to assert that?
+ *
+ * anyways, if we found one,
+ * we just have to do a wake_up. */
+#undef OVERLAPS
+#define OVERLAPS overlaps(sector, size, e->sector, e->size)
+ slot = ee_hash_slot(mdev, req->sector);
+ hlist_for_each_entry(e, n, slot, colision) {
+ if (OVERLAPS) {
+ wake_up(&mdev->misc_wait);
+ break;
+ }
+ }
+ }
+#undef OVERLAPS
+}
+
+void complete_master_bio(struct drbd_conf *mdev,
+ struct bio_and_error *m)
+{
+ bio_endio(m->bio, m->error);
+ dec_ap_bio(mdev);
+}
+
+/* Helper for __req_mod().
+ * Set m->bio to the master bio, if it is fit to be completed,
+ * or leave it alone (it is initialized to NULL in __req_mod),
+ * if it has already been completed, or cannot be completed yet.
+ * If m->bio is set, the error status to be returned is placed in m->error.
+ */
+void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
+{
+ const unsigned long s = req->rq_state;
+ struct drbd_conf *mdev = req->mdev;
+ /* only WRITES may end up here without a master bio (on barrier ack) */
+ int rw = req->master_bio ? bio_data_dir(req->master_bio) : WRITE;
+
+ /* we must not complete the master bio, while it is
+ * still being processed by _drbd_send_zc_bio (drbd_send_dblock)
+ * not yet acknowledged by the peer
+ * not yet completed by the local io subsystem
+ * these flags may get cleared in any order by
+ * the worker,
+ * the receiver,
+ * the bio_endio completion callbacks.
+ */
+ if (s & RQ_NET_QUEUED)
+ return;
+ if (s & RQ_NET_PENDING)
+ return;
+ if (s & RQ_LOCAL_PENDING)
+ return;
+
+ if (req->master_bio) {
+ /* this is data_received (remote read)
+ * or protocol C P_WRITE_ACK
+ * or protocol B P_RECV_ACK
+ * or protocol A "handed_over_to_network" (SendAck)
+ * or canceled or failed,
+ * or killed from the transfer log due to connection loss.
+ */
+
+ /*
+ * figure out whether to report success or failure.
+ *
+ * report success when at least one of the operations succeeded.
+ * or, to put the other way,
+ * only report failure, when both operations failed.
+ *
+ * what to do about the failures is handled elsewhere.
+ * what we need to do here is just: complete the master_bio.
+ *
+ * local completion error, if any, has been stored as ERR_PTR
+ * in private_bio within drbd_endio_pri.
+ */
+ int ok = (s & RQ_LOCAL_OK) || (s & RQ_NET_OK);
+ int error = PTR_ERR(req->private_bio);
+
+ /* remove the request from the conflict detection
+ * respective block_id verification hash */
+ if (!hlist_unhashed(&req->colision))
+ hlist_del(&req->colision);
+ else
+ D_ASSERT((s & RQ_NET_MASK) == 0);
+
+ /* for writes we need to do some extra housekeeping */
+ if (rw == WRITE)
+ _about_to_complete_local_write(mdev, req);
+
+ /* Update disk stats */
+ _drbd_end_io_acct(mdev, req);
+
+ m->error = ok ? 0 : (error ?: -EIO);
+ m->bio = req->master_bio;
+ req->master_bio = NULL;
+ }
+
+ if ((s & RQ_NET_MASK) == 0 || (s & RQ_NET_DONE)) {
+ /* this is disconnected (local only) operation,
+ * or protocol C P_WRITE_ACK,
+ * or protocol A or B P_BARRIER_ACK,
+ * or killed from the transfer log due to connection loss. */
+ _req_is_done(mdev, req, rw);
+ }
+ /* else: network part and not DONE yet. that is
+ * protocol A or B, barrier ack still pending... */
+}
+
+/*
+ * checks whether there was an overlapping request
+ * or ee already registered.
+ *
+ * if so, return 1, in which case this request is completed on the spot,
+ * without ever being submitted or send.
+ *
+ * return 0 if it is ok to submit this request.
+ *
+ * NOTE:
+ * paranoia: assume something above us is broken, and issues different write
+ * requests for the same block simultaneously...
+ *
+ * To ensure these won't be reordered differently on both nodes, resulting in
+ * diverging data sets, we discard the later one(s). Not that this is supposed
+ * to happen, but this is the rationale why we also have to check for
+ * conflicting requests with local origin, and why we have to do so regardless
+ * of whether we allowed multiple primaries.
+ *
+ * BTW, in case we only have one primary, the ee_hash is empty anyways, and the
+ * second hlist_for_each_entry becomes a noop. This is even simpler than to
+ * grab a reference on the net_conf, and check for the two_primaries flag...
+ */
+static int _req_conflicts(struct drbd_request *req)
+{
+ struct drbd_conf *mdev = req->mdev;
+ const sector_t sector = req->sector;
+ const int size = req->size;
+ struct drbd_request *i;
+ struct drbd_epoch_entry *e;
+ struct hlist_node *n;
+ struct hlist_head *slot;
+
+ D_ASSERT(hlist_unhashed(&req->colision));
+
+ if (!get_net_conf(mdev))
+ return 0;
+
+ /* BUG_ON */
+ ERR_IF (mdev->tl_hash_s == 0)
+ goto out_no_conflict;
+ BUG_ON(mdev->tl_hash == NULL);
+
+#define OVERLAPS overlaps(i->sector, i->size, sector, size)
+ slot = tl_hash_slot(mdev, sector);
+ hlist_for_each_entry(i, n, slot, colision) {
+ if (OVERLAPS) {
+ dev_alert(DEV, "%s[%u] Concurrent local write detected! "
+ "[DISCARD L] new: %llus +%u; "
+ "pending: %llus +%u\n",
+ current->comm, current->pid,
+ (unsigned long long)sector, size,
+ (unsigned long long)i->sector, i->size);
+ goto out_conflict;
+ }
+ }
+
+ if (mdev->ee_hash_s) {
+ /* now, check for overlapping requests with remote origin */
+ BUG_ON(mdev->ee_hash == NULL);
+#undef OVERLAPS
+#define OVERLAPS overlaps(e->sector, e->size, sector, size)
+ slot = ee_hash_slot(mdev, sector);
+ hlist_for_each_entry(e, n, slot, colision) {
+ if (OVERLAPS) {
+ dev_alert(DEV, "%s[%u] Concurrent remote write detected!"
+ " [DISCARD L] new: %llus +%u; "
+ "pending: %llus +%u\n",
+ current->comm, current->pid,
+ (unsigned long long)sector, size,
+ (unsigned long long)e->sector, e->size);
+ goto out_conflict;
+ }
+ }
+ }
+#undef OVERLAPS
+
+out_no_conflict:
+ /* this is like it should be, and what we expected.
+ * our users do behave after all... */
+ put_net_conf(mdev);
+ return 0;
+
+out_conflict:
+ put_net_conf(mdev);
+ return 1;
+}
+
+/* obviously this could be coded as many single functions
+ * instead of one huge switch,
+ * or by putting the code directly in the respective locations
+ * (as it has been before).
+ *
+ * but having it this way
+ * enforces that it is all in this one place, where it is easier to audit,
+ * it makes it obvious that whatever "event" "happens" to a request should
+ * happen "atomically" within the req_lock,
+ * and it enforces that we have to think in a very structured manner
+ * about the "events" that may happen to a request during its life time ...
+ */
+void __req_mod(struct drbd_request *req, enum drbd_req_event what,
+ struct bio_and_error *m)
+{
+ struct drbd_conf *mdev = req->mdev;
+ m->bio = NULL;
+
+ switch (what) {
+ default:
+ dev_err(DEV, "LOGIC BUG in %s:%u\n", __FILE__ , __LINE__);
+ break;
+
+ /* does not happen...
+ * initialization done in drbd_req_new
+ case created:
+ break;
+ */
+
+ case to_be_send: /* via network */
+ /* reached via drbd_make_request_common
+ * and from w_read_retry_remote */
+ D_ASSERT(!(req->rq_state & RQ_NET_MASK));
+ req->rq_state |= RQ_NET_PENDING;
+ inc_ap_pending(mdev);
+ break;
+
+ case to_be_submitted: /* locally */
+ /* reached via drbd_make_request_common */
+ D_ASSERT(!(req->rq_state & RQ_LOCAL_MASK));
+ req->rq_state |= RQ_LOCAL_PENDING;
+ break;
+
+ case completed_ok:
+ if (bio_data_dir(req->master_bio) == WRITE)
+ mdev->writ_cnt += req->size>>9;
+ else
+ mdev->read_cnt += req->size>>9;
+
+ req->rq_state |= (RQ_LOCAL_COMPLETED|RQ_LOCAL_OK);
+ req->rq_state &= ~RQ_LOCAL_PENDING;
+
+ _req_may_be_done(req, m);
+ put_ldev(mdev);
+ break;
+
+ case write_completed_with_error:
+ req->rq_state |= RQ_LOCAL_COMPLETED;
+ req->rq_state &= ~RQ_LOCAL_PENDING;
+
+ dev_alert(DEV, "Local WRITE failed sec=%llus size=%u\n",
+ (unsigned long long)req->sector, req->size);
+ /* and now: check how to handle local io error. */
+ __drbd_chk_io_error(mdev, FALSE);
+ _req_may_be_done(req, m);
+ put_ldev(mdev);
+ break;
+
+ case read_ahead_completed_with_error:
+ /* it is legal to fail READA */
+ req->rq_state |= RQ_LOCAL_COMPLETED;
+ req->rq_state &= ~RQ_LOCAL_PENDING;
+ _req_may_be_done(req, m);
+ put_ldev(mdev);
+ break;
+
+ case read_completed_with_error:
+ drbd_set_out_of_sync(mdev, req->sector, req->size);
+
+ req->rq_state |= RQ_LOCAL_COMPLETED;
+ req->rq_state &= ~RQ_LOCAL_PENDING;
+
+ dev_alert(DEV, "Local READ failed sec=%llus size=%u\n",
+ (unsigned long long)req->sector, req->size);
+ /* _req_mod(req,to_be_send); oops, recursion... */
+ D_ASSERT(!(req->rq_state & RQ_NET_MASK));
+ req->rq_state |= RQ_NET_PENDING;
+ inc_ap_pending(mdev);
+
+ __drbd_chk_io_error(mdev, FALSE);
+ put_ldev(mdev);
+ /* NOTE: if we have no connection,
+ * or know the peer has no good data either,
+ * then we don't actually need to "queue_for_net_read",
+ * but we do so anyways, since the drbd_io_error()
+ * and the potential state change to "Diskless"
+ * needs to be done from process context */
+
+ /* fall through: _req_mod(req,queue_for_net_read); */
+
+ case queue_for_net_read:
+ /* READ or READA, and
+ * no local disk,
+ * or target area marked as invalid,
+ * or just got an io-error. */
+ /* from drbd_make_request_common
+ * or from bio_endio during read io-error recovery */
+
+ /* so we can verify the handle in the answer packet
+ * corresponding hlist_del is in _req_may_be_done() */
+ hlist_add_head(&req->colision, ar_hash_slot(mdev, req->sector));
+
+ set_bit(UNPLUG_REMOTE, &mdev->flags);
+
+ D_ASSERT(req->rq_state & RQ_NET_PENDING);
+ req->rq_state |= RQ_NET_QUEUED;
+ req->w.cb = (req->rq_state & RQ_LOCAL_MASK)
+ ? w_read_retry_remote
+ : w_send_read_req;
+ drbd_queue_work(&mdev->data.work, &req->w);
+ break;
+
+ case queue_for_net_write:
+ /* assert something? */
+ /* from drbd_make_request_common only */
+
+ hlist_add_head(&req->colision, tl_hash_slot(mdev, req->sector));
+ /* corresponding hlist_del is in _req_may_be_done() */
+
+ /* NOTE
+ * In case the req ended up on the transfer log before being
+ * queued on the worker, it could lead to this request being
+ * missed during cleanup after connection loss.
+ * So we have to do both operations here,
+ * within the same lock that protects the transfer log.
+ *
+ * _req_add_to_epoch(req); this has to be after the
+ * _maybe_start_new_epoch(req); which happened in
+ * drbd_make_request_common, because we now may set the bit
+ * again ourselves to close the current epoch.
+ *
+ * Add req to the (now) current epoch (barrier). */
+
+ /* otherwise we may lose an unplug, which may cause some remote
+ * io-scheduler timeout to expire, increasing maximum latency,
+ * hurting performance. */
+ set_bit(UNPLUG_REMOTE, &mdev->flags);
+
+ /* see drbd_make_request_common,
+ * just after it grabs the req_lock */
+ D_ASSERT(test_bit(CREATE_BARRIER, &mdev->flags) == 0);
+
+ req->epoch = mdev->newest_tle->br_number;
+ list_add_tail(&req->tl_requests,
+ &mdev->newest_tle->requests);
+
+ /* increment size of current epoch */
+ mdev->newest_tle->n_req++;
+
+ /* queue work item to send data */
+ D_ASSERT(req->rq_state & RQ_NET_PENDING);
+ req->rq_state |= RQ_NET_QUEUED;
+ req->w.cb = w_send_dblock;
+ drbd_queue_work(&mdev->data.work, &req->w);
+
+ /* close the epoch, in case it outgrew the limit */
+ if (mdev->newest_tle->n_req >= mdev->net_conf->max_epoch_size)
+ queue_barrier(mdev);
+
+ break;
+
+ case send_canceled:
+ /* treat it the same */
+ case send_failed:
+ /* real cleanup will be done from tl_clear. just update flags
+ * so it is no longer marked as on the worker queue */
+ req->rq_state &= ~RQ_NET_QUEUED;
+ /* if we did it right, tl_clear should be scheduled only after
+ * this, so this should not be necessary! */
+ _req_may_be_done(req, m);
+ break;
+
+ case handed_over_to_network:
+ /* assert something? */
+ if (bio_data_dir(req->master_bio) == WRITE &&
+ mdev->net_conf->wire_protocol == DRBD_PROT_A) {
+ /* this is what is dangerous about protocol A:
+ * pretend it was successfully written on the peer. */
+ if (req->rq_state & RQ_NET_PENDING) {
+ dec_ap_pending(mdev);
+ req->rq_state &= ~RQ_NET_PENDING;
+ req->rq_state |= RQ_NET_OK;
+ } /* else: neg-ack was faster... */
+ /* it is still not yet RQ_NET_DONE until the
+ * corresponding epoch barrier got acked as well,
+ * so we know what to dirty on connection loss */
+ }
+ req->rq_state &= ~RQ_NET_QUEUED;
+ req->rq_state |= RQ_NET_SENT;
+ /* because _drbd_send_zc_bio could sleep, and may want to
+ * dereference the bio even after the "write_acked_by_peer" and
+ * "completed_ok" events came in, once we return from
+ * _drbd_send_zc_bio (drbd_send_dblock), we have to check
+ * whether it is done already, and end it. */
+ _req_may_be_done(req, m);
+ break;
+
+ case connection_lost_while_pending:
+ /* transfer log cleanup after connection loss */
+ /* assert something? */
+ if (req->rq_state & RQ_NET_PENDING)
+ dec_ap_pending(mdev);
+ req->rq_state &= ~(RQ_NET_OK|RQ_NET_PENDING);
+ req->rq_state |= RQ_NET_DONE;
+ /* if it is still queued, we may not complete it here.
+ * it will be canceled soon. */
+ if (!(req->rq_state & RQ_NET_QUEUED))
+ _req_may_be_done(req, m);
+ break;
+
+ case write_acked_by_peer_and_sis:
+ req->rq_state |= RQ_NET_SIS;
+ case conflict_discarded_by_peer:
+ /* for discarded conflicting writes of multiple primaries,
+ * there is no need to keep anything in the tl, potential
+ * node crashes are covered by the activity log. */
+ if (what == conflict_discarded_by_peer)
+ dev_alert(DEV, "Got DiscardAck packet %llus +%u!"
+ " DRBD is not a random data generator!\n",
+ (unsigned long long)req->sector, req->size);
+ req->rq_state |= RQ_NET_DONE;
+ /* fall through */
+ case write_acked_by_peer:
+ /* protocol C; successfully written on peer.
+ * Nothing to do here.
+ * We want to keep the tl in place for all protocols, to cater
+ * for volatile write-back caches on lower level devices.
+ *
+ * A barrier request is expected to have forced all prior
+ * requests onto stable storage, so completion of a barrier
+ * request could set NET_DONE right here, and not wait for the
+ * P_BARRIER_ACK, but that is an unnecessary optimization. */
+
+ /* this makes it effectively the same as for: */
+ case recv_acked_by_peer:
+ /* protocol B; pretends to be successfully written on peer.
+ * see also notes above in handed_over_to_network about
+ * protocol != C */
+ req->rq_state |= RQ_NET_OK;
+ D_ASSERT(req->rq_state & RQ_NET_PENDING);
+ dec_ap_pending(mdev);
+ req->rq_state &= ~RQ_NET_PENDING;
+ _req_may_be_done(req, m);
+ break;
+
+ case neg_acked:
+ /* assert something? */
+ if (req->rq_state & RQ_NET_PENDING)
+ dec_ap_pending(mdev);
+ req->rq_state &= ~(RQ_NET_OK|RQ_NET_PENDING);
+
+ req->rq_state |= RQ_NET_DONE;
+ _req_may_be_done(req, m);
+ /* else: done by handed_over_to_network */
+ break;
+
+ case barrier_acked:
+ if (req->rq_state & RQ_NET_PENDING) {
+ /* barrier came in before all requests have been acked.
+ * this is bad, because if the connection is lost now,
+ * we won't be able to clean them up... */
+ dev_err(DEV, "FIXME (barrier_acked but pending)\n");
+ list_move(&req->tl_requests, &mdev->out_of_sequence_requests);
+ }
+ D_ASSERT(req->rq_state & RQ_NET_SENT);
+ req->rq_state |= RQ_NET_DONE;
+ _req_may_be_done(req, m);
+ break;
+
+ case data_received:
+ D_ASSERT(req->rq_state & RQ_NET_PENDING);
+ dec_ap_pending(mdev);
+ req->rq_state &= ~RQ_NET_PENDING;
+ req->rq_state |= (RQ_NET_OK|RQ_NET_DONE);
+ _req_may_be_done(req, m);
+ break;
+ };
+}
+
+/* we may do a local read if:
+ * - we are consistent (of course),
+ * - or we are generally inconsistent,
+ * BUT we are still/already IN SYNC for this area.
+ * since size may be bigger than BM_BLOCK_SIZE,
+ * we may need to check several bits.
+ */
+static int drbd_may_do_local_read(struct drbd_conf *mdev, sector_t sector, int size)
+{
+ unsigned long sbnr, ebnr;
+ sector_t esector, nr_sectors;
+
+ if (mdev->state.disk == D_UP_TO_DATE)
+ return 1;
+ if (mdev->state.disk >= D_OUTDATED)
+ return 0;
+ if (mdev->state.disk < D_INCONSISTENT)
+ return 0;
+ /* state.disk == D_INCONSISTENT We will have a look at the BitMap */
+ nr_sectors = drbd_get_capacity(mdev->this_bdev);
+ esector = sector + (size >> 9) - 1;
+
+ D_ASSERT(sector < nr_sectors);
+ D_ASSERT(esector < nr_sectors);
+
+ sbnr = BM_SECT_TO_BIT(sector);
+ ebnr = BM_SECT_TO_BIT(esector);
+
+ return 0 == drbd_bm_count_bits(mdev, sbnr, ebnr);
+}
+
+static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio)
+{
+ const int rw = bio_rw(bio);
+ const int size = bio->bi_size;
+ const sector_t sector = bio->bi_sector;
+ struct drbd_tl_epoch *b = NULL;
+ struct drbd_request *req;
+ int local, remote;
+ int err = -EIO;
+
+ /* allocate outside of all locks; */
+ req = drbd_req_new(mdev, bio);
+ if (!req) {
+ dec_ap_bio(mdev);
+ /* only pass the error to the upper layers.
+ * if user cannot handle io errors, that's not our business. */
+ dev_err(DEV, "could not kmalloc() req\n");
+ bio_endio(bio, -ENOMEM);
+ return 0;
+ }
+
+ local = get_ldev(mdev);
+ if (!local) {
+ bio_put(req->private_bio); /* or we get a bio leak */
+ req->private_bio = NULL;
+ }
+ if (rw == WRITE) {
+ remote = 1;
+ } else {
+ /* READ || READA */
+ if (local) {
+ if (!drbd_may_do_local_read(mdev, sector, size)) {
+ /* we could kick the syncer to
+ * sync this extent asap, wait for
+ * it, then continue locally.
+ * Or just issue the request remotely.
+ */
+ local = 0;
+ bio_put(req->private_bio);
+ req->private_bio = NULL;
+ put_ldev(mdev);
+ }
+ }
+ remote = !local && mdev->state.pdsk >= D_UP_TO_DATE;
+ }
+
+ /* If we have a disk, but a READA request is mapped to remote,
+ * we are R_PRIMARY, D_INCONSISTENT, SyncTarget.
+ * Just fail that READA request right here.
+ *
+ * THINK: maybe fail all READA when not local?
+ * or make this configurable...
+ * if network is slow, READA won't do any good.
+ */
+ if (rw == READA && mdev->state.disk >= D_INCONSISTENT && !local) {
+ err = -EWOULDBLOCK;
+ goto fail_and_free_req;
+ }
+
+ /* For WRITES going to the local disk, grab a reference on the target
+ * extent. This waits for any resync activity in the corresponding
+ * resync extent to finish, and, if necessary, pulls in the target
+ * extent into the activity log, which involves further disk io because
+ * of transactional on-disk meta data updates. */
+ if (rw == WRITE && local)
+ drbd_al_begin_io(mdev, sector);
+
+ remote = remote && (mdev->state.pdsk == D_UP_TO_DATE ||
+ (mdev->state.pdsk == D_INCONSISTENT &&
+ mdev->state.conn >= C_CONNECTED));
+
+ if (!(local || remote)) {
+ dev_err(DEV, "IO ERROR: neither local nor remote disk\n");
+ goto fail_free_complete;
+ }
+
+ /* For WRITE request, we have to make sure that we have an
+ * unused_spare_tle, in case we need to start a new epoch.
+ * I try to be smart and avoid to pre-allocate always "just in case",
+ * but there is a race between testing the bit and pointer outside the
+ * spinlock, and grabbing the spinlock.
+ * if we lost that race, we retry. */
+ if (rw == WRITE && remote &&
+ mdev->unused_spare_tle == NULL &&
+ test_bit(CREATE_BARRIER, &mdev->flags)) {
+allocate_barrier:
+ b = kmalloc(sizeof(struct drbd_tl_epoch), GFP_NOIO);
+ if (!b) {
+ dev_err(DEV, "Failed to alloc barrier.\n");
+ err = -ENOMEM;
+ goto fail_free_complete;
+ }
+ }
+
+ /* GOOD, everything prepared, grab the spin_lock */
+ spin_lock_irq(&mdev->req_lock);
+
+ if (remote) {
+ remote = (mdev->state.pdsk == D_UP_TO_DATE ||
+ (mdev->state.pdsk == D_INCONSISTENT &&
+ mdev->state.conn >= C_CONNECTED));
+ if (!remote)
+ dev_warn(DEV, "lost connection while grabbing the req_lock!\n");
+ if (!(local || remote)) {
+ dev_err(DEV, "IO ERROR: neither local nor remote disk\n");
+ spin_unlock_irq(&mdev->req_lock);
+ goto fail_free_complete;
+ }
+ }
+
+ if (b && mdev->unused_spare_tle == NULL) {
+ mdev->unused_spare_tle = b;
+ b = NULL;
+ }
+ if (rw == WRITE && remote &&
+ mdev->unused_spare_tle == NULL &&
+ test_bit(CREATE_BARRIER, &mdev->flags)) {
+ /* someone closed the current epoch
+ * while we were grabbing the spinlock */
+ spin_unlock_irq(&mdev->req_lock);
+ goto allocate_barrier;
+ }
+
+
+ /* Update disk stats */
+ _drbd_start_io_acct(mdev, req, bio);
+
+ /* _maybe_start_new_epoch(mdev);
+ * If we need to generate a write barrier packet, we have to add the
+ * new epoch (barrier) object, and queue the barrier packet for sending,
+ * and queue the req's data after it _within the same lock_, otherwise
+ * we have race conditions were the reorder domains could be mixed up.
+ *
+ * Even read requests may start a new epoch and queue the corresponding
+ * barrier packet. To get the write ordering right, we only have to
+ * make sure that, if this is a write request and it triggered a
+ * barrier packet, this request is queued within the same spinlock. */
+ if (remote && mdev->unused_spare_tle &&
+ test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) {
+ _tl_add_barrier(mdev, mdev->unused_spare_tle);
+ mdev->unused_spare_tle = NULL;
+ } else {
+ D_ASSERT(!(remote && rw == WRITE &&
+ test_bit(CREATE_BARRIER, &mdev->flags)));
+ }
+
+ /* NOTE
+ * Actually, 'local' may be wrong here already, since we may have failed
+ * to write to the meta data, and may become wrong anytime because of
+ * local io-error for some other request, which would lead to us
+ * "detaching" the local disk.
+ *
+ * 'remote' may become wrong any time because the network could fail.
+ *
+ * This is a harmless race condition, though, since it is handled
+ * correctly at the appropriate places; so it just defers the failure
+ * of the respective operation.
+ */
+
+ /* mark them early for readability.
+ * this just sets some state flags. */
+ if (remote)
+ _req_mod(req, to_be_send);
+ if (local)
+ _req_mod(req, to_be_submitted);
+
+ /* check this request on the collision detection hash tables.
+ * if we have a conflict, just complete it here.
+ * THINK do we want to check reads, too? (I don't think so...) */
+ if (rw == WRITE && _req_conflicts(req)) {
+ /* this is a conflicting request.
+ * even though it may have been only _partially_
+ * overlapping with one of the currently pending requests,
+ * without even submitting or sending it, we will
+ * pretend that it was successfully served right now.
+ */
+ if (local) {
+ bio_put(req->private_bio);
+ req->private_bio = NULL;
+ drbd_al_complete_io(mdev, req->sector);
+ put_ldev(mdev);
+ local = 0;
+ }
+ if (remote)
+ dec_ap_pending(mdev);
+ _drbd_end_io_acct(mdev, req);
+ /* THINK: do we want to fail it (-EIO), or pretend success? */
+ bio_endio(req->master_bio, 0);
+ req->master_bio = NULL;
+ dec_ap_bio(mdev);
+ drbd_req_free(req);
+ remote = 0;
+ }
+
+ /* NOTE remote first: to get the concurrent write detection right,
+ * we must register the request before start of local IO. */
+ if (remote) {
+ /* either WRITE and C_CONNECTED,
+ * or READ, and no local disk,
+ * or READ, but not in sync.
+ */
+ _req_mod(req, (rw == WRITE)
+ ? queue_for_net_write
+ : queue_for_net_read);
+ }
+ spin_unlock_irq(&mdev->req_lock);
+ kfree(b); /* if someone else has beaten us to it... */
+
+ if (local) {
+ req->private_bio->bi_bdev = mdev->ldev->backing_bdev;
+
+ if (FAULT_ACTIVE(mdev, rw == WRITE ? DRBD_FAULT_DT_WR
+ : rw == READ ? DRBD_FAULT_DT_RD
+ : DRBD_FAULT_DT_RA))
+ bio_endio(req->private_bio, -EIO);
+ else
+ generic_make_request(req->private_bio);
+ }
+
+ /* we need to plug ALWAYS since we possibly need to kick lo_dev.
+ * we plug after submit, so we won't miss an unplug event */
+ drbd_plug_device(mdev);
+
+ return 0;
+
+fail_free_complete:
+ if (rw == WRITE && local)
+ drbd_al_complete_io(mdev, sector);
+fail_and_free_req:
+ if (local) {
+ bio_put(req->private_bio);
+ req->private_bio = NULL;
+ put_ldev(mdev);
+ }
+ bio_endio(bio, err);
+ drbd_req_free(req);
+ dec_ap_bio(mdev);
+ kfree(b);
+
+ return 0;
+}
+
+/* helper function for drbd_make_request
+ * if we can determine just by the mdev (state) that this request will fail,
+ * return 1
+ * otherwise return 0
+ */
+static int drbd_fail_request_early(struct drbd_conf *mdev, int is_write)
+{
+ /* Unconfigured */
+ if (mdev->state.conn == C_DISCONNECTING &&
+ mdev->state.disk == D_DISKLESS)
+ return 1;
+
+ if (mdev->state.role != R_PRIMARY &&
+ (!allow_oos || is_write)) {
+ if (__ratelimit(&drbd_ratelimit_state)) {
+ dev_err(DEV, "Process %s[%u] tried to %s; "
+ "since we are not in Primary state, "
+ "we cannot allow this\n",
+ current->comm, current->pid,
+ is_write ? "WRITE" : "READ");
+ }
+ return 1;
+ }
+
+ /*
+ * Paranoia: we might have been primary, but sync target, or
+ * even diskless, then lost the connection.
+ * This should have been handled (panic? suspend?) somewhere
+ * else. But maybe it was not, so check again here.
+ * Caution: as long as we do not have a read/write lock on mdev,
+ * to serialize state changes, this is racy, since we may lose
+ * the connection *after* we test for the cstate.
+ */
+ if (mdev->state.disk < D_UP_TO_DATE && mdev->state.pdsk < D_UP_TO_DATE) {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Sorry, I have no access to good data anymore.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+int drbd_make_request_26(struct request_queue *q, struct bio *bio)
+{
+ unsigned int s_enr, e_enr;
+ struct drbd_conf *mdev = (struct drbd_conf *) q->queuedata;
+
+ if (drbd_fail_request_early(mdev, bio_data_dir(bio) & WRITE)) {
+ bio_endio(bio, -EPERM);
+ return 0;
+ }
+
+ /* Reject barrier requests if we know the underlying device does
+ * not support them.
+ * XXX: Need to get this info from peer as well some how so we
+ * XXX: reject if EITHER side/data/metadata area does not support them.
+ *
+ * because of those XXX, this is not yet enabled,
+ * i.e. in drbd_init_set_defaults we set the NO_BARRIER_SUPP bit.
+ */
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags))) {
+ /* dev_warn(DEV, "Rejecting barrier request as underlying device does not support\n"); */
+ bio_endio(bio, -EOPNOTSUPP);
+ return 0;
+ }
+
+ /*
+ * what we "blindly" assume:
+ */
+ D_ASSERT(bio->bi_size > 0);
+ D_ASSERT((bio->bi_size & 0x1ff) == 0);
+ D_ASSERT(bio->bi_idx == 0);
+
+ /* to make some things easier, force alignment of requests within the
+ * granularity of our hash tables */
+ s_enr = bio->bi_sector >> HT_SHIFT;
+ e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT;
+
+ if (likely(s_enr == e_enr)) {
+ inc_ap_bio(mdev, 1);
+ return drbd_make_request_common(mdev, bio);
+ }
+
+ /* can this bio be split generically?
+ * Maybe add our own split-arbitrary-bios function. */
+ if (bio->bi_vcnt != 1 || bio->bi_idx != 0 || bio->bi_size > DRBD_MAX_SEGMENT_SIZE) {
+ /* rather error out here than BUG in bio_split */
+ dev_err(DEV, "bio would need to, but cannot, be split: "
+ "(vcnt=%u,idx=%u,size=%u,sector=%llu)\n",
+ bio->bi_vcnt, bio->bi_idx, bio->bi_size,
+ (unsigned long long)bio->bi_sector);
+ bio_endio(bio, -EINVAL);
+ } else {
+ /* This bio crosses some boundary, so we have to split it. */
+ struct bio_pair *bp;
+ /* works for the "do not cross hash slot boundaries" case
+ * e.g. sector 262269, size 4096
+ * s_enr = 262269 >> 6 = 4097
+ * e_enr = (262269+8-1) >> 6 = 4098
+ * HT_SHIFT = 6
+ * sps = 64, mask = 63
+ * first_sectors = 64 - (262269 & 63) = 3
+ */
+ const sector_t sect = bio->bi_sector;
+ const int sps = 1 << HT_SHIFT; /* sectors per slot */
+ const int mask = sps - 1;
+ const sector_t first_sectors = sps - (sect & mask);
+ bp = bio_split(bio,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+ bio_split_pool,
+#endif
+ first_sectors);
+
+ /* we need to get a "reference count" (ap_bio_cnt)
+ * to avoid races with the disconnect/reconnect/suspend code.
+ * In case we need to split the bio here, we need to get two references
+ * atomically, otherwise we might deadlock when trying to submit the
+ * second one! */
+ inc_ap_bio(mdev, 2);
+
+ D_ASSERT(e_enr == s_enr + 1);
+
+ drbd_make_request_common(mdev, &bp->bio1);
+ drbd_make_request_common(mdev, &bp->bio2);
+ bio_pair_release(bp);
+ }
+ return 0;
+}
+
+/* This is called by bio_add_page(). With this function we reduce
+ * the number of BIOs that span over multiple DRBD_MAX_SEGMENT_SIZEs
+ * units (was AL_EXTENTs).
+ *
+ * we do the calculation within the lower 32bit of the byte offsets,
+ * since we don't care for actual offset, but only check whether it
+ * would cross "activity log extent" boundaries.
+ *
+ * As long as the BIO is empty we have to allow at least one bvec,
+ * regardless of size and offset. so the resulting bio may still
+ * cross extent boundaries. those are dealt with (bio_split) in
+ * drbd_make_request_26.
+ */
+int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct bio_vec *bvec)
+{
+ struct drbd_conf *mdev = (struct drbd_conf *) q->queuedata;
+ unsigned int bio_offset =
+ (unsigned int)bvm->bi_sector << 9; /* 32 bit */
+ unsigned int bio_size = bvm->bi_size;
+ int limit, backing_limit;
+
+ limit = DRBD_MAX_SEGMENT_SIZE
+ - ((bio_offset & (DRBD_MAX_SEGMENT_SIZE-1)) + bio_size);
+ if (limit < 0)
+ limit = 0;
+ if (bio_size == 0) {
+ if (limit <= bvec->bv_len)
+ limit = bvec->bv_len;
+ } else if (limit && get_ldev(mdev)) {
+ struct request_queue * const b =
+ mdev->ldev->backing_bdev->bd_disk->queue;
+ if (b->merge_bvec_fn && mdev->ldev->dc.use_bmbv) {
+ backing_limit = b->merge_bvec_fn(b, bvm, bvec);
+ limit = min(limit, backing_limit);
+ }
+ put_ldev(mdev);
+ }
+ return limit;
+}
diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h
new file mode 100644
index 00000000000..f22c1bc8ec7
--- /dev/null
+++ b/drivers/block/drbd/drbd_req.h
@@ -0,0 +1,326 @@
+/*
+ drbd_req.h
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2006-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 2006-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+ Copyright (C) 2006-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+
+ DRBD is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ DRBD 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DRBD_REQ_H
+#define _DRBD_REQ_H
+
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/drbd.h>
+#include "drbd_int.h"
+#include "drbd_wrappers.h"
+
+/* The request callbacks will be called in irq context by the IDE drivers,
+ and in Softirqs/Tasklets/BH context by the SCSI drivers,
+ and by the receiver and worker in kernel-thread context.
+ Try to get the locking right :) */
+
+/*
+ * Objects of type struct drbd_request do only exist on a R_PRIMARY node, and are
+ * associated with IO requests originating from the block layer above us.
+ *
+ * There are quite a few things that may happen to a drbd request
+ * during its lifetime.
+ *
+ * It will be created.
+ * It will be marked with the intention to be
+ * submitted to local disk and/or
+ * send via the network.
+ *
+ * It has to be placed on the transfer log and other housekeeping lists,
+ * In case we have a network connection.
+ *
+ * It may be identified as a concurrent (write) request
+ * and be handled accordingly.
+ *
+ * It may me handed over to the local disk subsystem.
+ * It may be completed by the local disk subsystem,
+ * either sucessfully or with io-error.
+ * In case it is a READ request, and it failed locally,
+ * it may be retried remotely.
+ *
+ * It may be queued for sending.
+ * It may be handed over to the network stack,
+ * which may fail.
+ * It may be acknowledged by the "peer" according to the wire_protocol in use.
+ * this may be a negative ack.
+ * It may receive a faked ack when the network connection is lost and the
+ * transfer log is cleaned up.
+ * Sending may be canceled due to network connection loss.
+ * When it finally has outlived its time,
+ * corresponding dirty bits in the resync-bitmap may be cleared or set,
+ * it will be destroyed,
+ * and completion will be signalled to the originator,
+ * with or without "success".
+ */
+
+enum drbd_req_event {
+ created,
+ to_be_send,
+ to_be_submitted,
+
+ /* XXX yes, now I am inconsistent...
+ * these two are not "events" but "actions"
+ * oh, well... */
+ queue_for_net_write,
+ queue_for_net_read,
+
+ send_canceled,
+ send_failed,
+ handed_over_to_network,
+ connection_lost_while_pending,
+ recv_acked_by_peer,
+ write_acked_by_peer,
+ write_acked_by_peer_and_sis, /* and set_in_sync */
+ conflict_discarded_by_peer,
+ neg_acked,
+ barrier_acked, /* in protocol A and B */
+ data_received, /* (remote read) */
+
+ read_completed_with_error,
+ read_ahead_completed_with_error,
+ write_completed_with_error,
+ completed_ok,
+ nothing, /* for tracing only */
+};
+
+/* encoding of request states for now. we don't actually need that many bits.
+ * we don't need to do atomic bit operations either, since most of the time we
+ * need to look at the connection state and/or manipulate some lists at the
+ * same time, so we should hold the request lock anyways.
+ */
+enum drbd_req_state_bits {
+ /* 210
+ * 000: no local possible
+ * 001: to be submitted
+ * UNUSED, we could map: 011: submitted, completion still pending
+ * 110: completed ok
+ * 010: completed with error
+ */
+ __RQ_LOCAL_PENDING,
+ __RQ_LOCAL_COMPLETED,
+ __RQ_LOCAL_OK,
+
+ /* 76543
+ * 00000: no network possible
+ * 00001: to be send
+ * 00011: to be send, on worker queue
+ * 00101: sent, expecting recv_ack (B) or write_ack (C)
+ * 11101: sent,
+ * recv_ack (B) or implicit "ack" (A),
+ * still waiting for the barrier ack.
+ * master_bio may already be completed and invalidated.
+ * 11100: write_acked (C),
+ * data_received (for remote read, any protocol)
+ * or finally the barrier ack has arrived (B,A)...
+ * request can be freed
+ * 01100: neg-acked (write, protocol C)
+ * or neg-d-acked (read, any protocol)
+ * or killed from the transfer log
+ * during cleanup after connection loss
+ * request can be freed
+ * 01000: canceled or send failed...
+ * request can be freed
+ */
+
+ /* if "SENT" is not set, yet, this can still fail or be canceled.
+ * if "SENT" is set already, we still wait for an Ack packet.
+ * when cleared, the master_bio may be completed.
+ * in (B,A) the request object may still linger on the transaction log
+ * until the corresponding barrier ack comes in */
+ __RQ_NET_PENDING,
+
+ /* If it is QUEUED, and it is a WRITE, it is also registered in the
+ * transfer log. Currently we need this flag to avoid conflicts between
+ * worker canceling the request and tl_clear_barrier killing it from
+ * transfer log. We should restructure the code so this conflict does
+ * no longer occur. */
+ __RQ_NET_QUEUED,
+
+ /* well, actually only "handed over to the network stack".
+ *
+ * TODO can potentially be dropped because of the similar meaning
+ * of RQ_NET_SENT and ~RQ_NET_QUEUED.
+ * however it is not exactly the same. before we drop it
+ * we must ensure that we can tell a request with network part
+ * from a request without, regardless of what happens to it. */
+ __RQ_NET_SENT,
+
+ /* when set, the request may be freed (if RQ_NET_QUEUED is clear).
+ * basically this means the corresponding P_BARRIER_ACK was received */
+ __RQ_NET_DONE,
+
+ /* whether or not we know (C) or pretend (B,A) that the write
+ * was successfully written on the peer.
+ */
+ __RQ_NET_OK,
+
+ /* peer called drbd_set_in_sync() for this write */
+ __RQ_NET_SIS,
+
+ /* keep this last, its for the RQ_NET_MASK */
+ __RQ_NET_MAX,
+};
+
+#define RQ_LOCAL_PENDING (1UL << __RQ_LOCAL_PENDING)
+#define RQ_LOCAL_COMPLETED (1UL << __RQ_LOCAL_COMPLETED)
+#define RQ_LOCAL_OK (1UL << __RQ_LOCAL_OK)
+
+#define RQ_LOCAL_MASK ((RQ_LOCAL_OK << 1)-1) /* 0x07 */
+
+#define RQ_NET_PENDING (1UL << __RQ_NET_PENDING)
+#define RQ_NET_QUEUED (1UL << __RQ_NET_QUEUED)
+#define RQ_NET_SENT (1UL << __RQ_NET_SENT)
+#define RQ_NET_DONE (1UL << __RQ_NET_DONE)
+#define RQ_NET_OK (1UL << __RQ_NET_OK)
+#define RQ_NET_SIS (1UL << __RQ_NET_SIS)
+
+/* 0x1f8 */
+#define RQ_NET_MASK (((1UL << __RQ_NET_MAX)-1) & ~RQ_LOCAL_MASK)
+
+/* epoch entries */
+static inline
+struct hlist_head *ee_hash_slot(struct drbd_conf *mdev, sector_t sector)
+{
+ BUG_ON(mdev->ee_hash_s == 0);
+ return mdev->ee_hash +
+ ((unsigned int)(sector>>HT_SHIFT) % mdev->ee_hash_s);
+}
+
+/* transfer log (drbd_request objects) */
+static inline
+struct hlist_head *tl_hash_slot(struct drbd_conf *mdev, sector_t sector)
+{
+ BUG_ON(mdev->tl_hash_s == 0);
+ return mdev->tl_hash +
+ ((unsigned int)(sector>>HT_SHIFT) % mdev->tl_hash_s);
+}
+
+/* application reads (drbd_request objects) */
+static struct hlist_head *ar_hash_slot(struct drbd_conf *mdev, sector_t sector)
+{
+ return mdev->app_reads_hash
+ + ((unsigned int)(sector) % APP_R_HSIZE);
+}
+
+/* when we receive the answer for a read request,
+ * verify that we actually know about it */
+static inline struct drbd_request *_ar_id_to_req(struct drbd_conf *mdev,
+ u64 id, sector_t sector)
+{
+ struct hlist_head *slot = ar_hash_slot(mdev, sector);
+ struct hlist_node *n;
+ struct drbd_request *req;
+
+ hlist_for_each_entry(req, n, slot, colision) {
+ if ((unsigned long)req == (unsigned long)id) {
+ D_ASSERT(req->sector == sector);
+ return req;
+ }
+ }
+ return NULL;
+}
+
+static inline struct drbd_request *drbd_req_new(struct drbd_conf *mdev,
+ struct bio *bio_src)
+{
+ struct bio *bio;
+ struct drbd_request *req =
+ mempool_alloc(drbd_request_mempool, GFP_NOIO);
+ if (likely(req)) {
+ bio = bio_clone(bio_src, GFP_NOIO); /* XXX cannot fail?? */
+
+ req->rq_state = 0;
+ req->mdev = mdev;
+ req->master_bio = bio_src;
+ req->private_bio = bio;
+ req->epoch = 0;
+ req->sector = bio->bi_sector;
+ req->size = bio->bi_size;
+ req->start_time = jiffies;
+ INIT_HLIST_NODE(&req->colision);
+ INIT_LIST_HEAD(&req->tl_requests);
+ INIT_LIST_HEAD(&req->w.list);
+
+ bio->bi_private = req;
+ bio->bi_end_io = drbd_endio_pri;
+ bio->bi_next = NULL;
+ }
+ return req;
+}
+
+static inline void drbd_req_free(struct drbd_request *req)
+{
+ mempool_free(req, drbd_request_mempool);
+}
+
+static inline int overlaps(sector_t s1, int l1, sector_t s2, int l2)
+{
+ return !((s1 + (l1>>9) <= s2) || (s1 >= s2 + (l2>>9)));
+}
+
+/* Short lived temporary struct on the stack.
+ * We could squirrel the error to be returned into
+ * bio->bi_size, or similar. But that would be too ugly. */
+struct bio_and_error {
+ struct bio *bio;
+ int error;
+};
+
+extern void _req_may_be_done(struct drbd_request *req,
+ struct bio_and_error *m);
+extern void __req_mod(struct drbd_request *req, enum drbd_req_event what,
+ struct bio_and_error *m);
+extern void complete_master_bio(struct drbd_conf *mdev,
+ struct bio_and_error *m);
+
+/* use this if you don't want to deal with calling complete_master_bio()
+ * outside the spinlock, e.g. when walking some list on cleanup. */
+static inline void _req_mod(struct drbd_request *req, enum drbd_req_event what)
+{
+ struct drbd_conf *mdev = req->mdev;
+ struct bio_and_error m;
+
+ /* __req_mod possibly frees req, do not touch req after that! */
+ __req_mod(req, what, &m);
+ if (m.bio)
+ complete_master_bio(mdev, &m);
+}
+
+/* completion of master bio is outside of spinlock.
+ * If you need it irqsave, do it your self! */
+static inline void req_mod(struct drbd_request *req,
+ enum drbd_req_event what)
+{
+ struct drbd_conf *mdev = req->mdev;
+ struct bio_and_error m;
+ spin_lock_irq(&mdev->req_lock);
+ __req_mod(req, what, &m);
+ spin_unlock_irq(&mdev->req_lock);
+
+ if (m.bio)
+ complete_master_bio(mdev, &m);
+}
+#endif
diff --git a/drivers/block/drbd/drbd_strings.c b/drivers/block/drbd/drbd_strings.c
new file mode 100644
index 00000000000..76863e3f05b
--- /dev/null
+++ b/drivers/block/drbd/drbd_strings.c
@@ -0,0 +1,113 @@
+/*
+ drbd.h
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2003-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 2003-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2003-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/drbd.h>
+
+static const char *drbd_conn_s_names[] = {
+ [C_STANDALONE] = "StandAlone",
+ [C_DISCONNECTING] = "Disconnecting",
+ [C_UNCONNECTED] = "Unconnected",
+ [C_TIMEOUT] = "Timeout",
+ [C_BROKEN_PIPE] = "BrokenPipe",
+ [C_NETWORK_FAILURE] = "NetworkFailure",
+ [C_PROTOCOL_ERROR] = "ProtocolError",
+ [C_WF_CONNECTION] = "WFConnection",
+ [C_WF_REPORT_PARAMS] = "WFReportParams",
+ [C_TEAR_DOWN] = "TearDown",
+ [C_CONNECTED] = "Connected",
+ [C_STARTING_SYNC_S] = "StartingSyncS",
+ [C_STARTING_SYNC_T] = "StartingSyncT",
+ [C_WF_BITMAP_S] = "WFBitMapS",
+ [C_WF_BITMAP_T] = "WFBitMapT",
+ [C_WF_SYNC_UUID] = "WFSyncUUID",
+ [C_SYNC_SOURCE] = "SyncSource",
+ [C_SYNC_TARGET] = "SyncTarget",
+ [C_PAUSED_SYNC_S] = "PausedSyncS",
+ [C_PAUSED_SYNC_T] = "PausedSyncT",
+ [C_VERIFY_S] = "VerifyS",
+ [C_VERIFY_T] = "VerifyT",
+};
+
+static const char *drbd_role_s_names[] = {
+ [R_PRIMARY] = "Primary",
+ [R_SECONDARY] = "Secondary",
+ [R_UNKNOWN] = "Unknown"
+};
+
+static const char *drbd_disk_s_names[] = {
+ [D_DISKLESS] = "Diskless",
+ [D_ATTACHING] = "Attaching",
+ [D_FAILED] = "Failed",
+ [D_NEGOTIATING] = "Negotiating",
+ [D_INCONSISTENT] = "Inconsistent",
+ [D_OUTDATED] = "Outdated",
+ [D_UNKNOWN] = "DUnknown",
+ [D_CONSISTENT] = "Consistent",
+ [D_UP_TO_DATE] = "UpToDate",
+};
+
+static const char *drbd_state_sw_errors[] = {
+ [-SS_TWO_PRIMARIES] = "Multiple primaries not allowed by config",
+ [-SS_NO_UP_TO_DATE_DISK] = "Refusing to be Primary without at least one UpToDate disk",
+ [-SS_NO_LOCAL_DISK] = "Can not resync without local disk",
+ [-SS_NO_REMOTE_DISK] = "Can not resync without remote disk",
+ [-SS_CONNECTED_OUTDATES] = "Refusing to be Outdated while Connected",
+ [-SS_PRIMARY_NOP] = "Refusing to be Primary while peer is not outdated",
+ [-SS_RESYNC_RUNNING] = "Can not start OV/resync since it is already active",
+ [-SS_ALREADY_STANDALONE] = "Can not disconnect a StandAlone device",
+ [-SS_CW_FAILED_BY_PEER] = "State change was refused by peer node",
+ [-SS_IS_DISKLESS] = "Device is diskless, the requested operation requires a disk",
+ [-SS_DEVICE_IN_USE] = "Device is held open by someone",
+ [-SS_NO_NET_CONFIG] = "Have no net/connection configuration",
+ [-SS_NO_VERIFY_ALG] = "Need a verify algorithm to start online verify",
+ [-SS_NEED_CONNECTION] = "Need a connection to start verify or resync",
+ [-SS_NOT_SUPPORTED] = "Peer does not support protocol",
+ [-SS_LOWER_THAN_OUTDATED] = "Disk state is lower than outdated",
+ [-SS_IN_TRANSIENT_STATE] = "In transient state, retry after next state change",
+ [-SS_CONCURRENT_ST_CHG] = "Concurrent state changes detected and aborted",
+};
+
+const char *drbd_conn_str(enum drbd_conns s)
+{
+ /* enums are unsigned... */
+ return s > C_PAUSED_SYNC_T ? "TOO_LARGE" : drbd_conn_s_names[s];
+}
+
+const char *drbd_role_str(enum drbd_role s)
+{
+ return s > R_SECONDARY ? "TOO_LARGE" : drbd_role_s_names[s];
+}
+
+const char *drbd_disk_str(enum drbd_disk_state s)
+{
+ return s > D_UP_TO_DATE ? "TOO_LARGE" : drbd_disk_s_names[s];
+}
+
+const char *drbd_set_st_err_str(enum drbd_state_ret_codes err)
+{
+ return err <= SS_AFTER_LAST_ERROR ? "TOO_SMALL" :
+ err > SS_TWO_PRIMARIES ? "TOO_LARGE"
+ : drbd_state_sw_errors[-err];
+}
diff --git a/drivers/block/drbd/drbd_vli.h b/drivers/block/drbd/drbd_vli.h
new file mode 100644
index 00000000000..fc824006e72
--- /dev/null
+++ b/drivers/block/drbd/drbd_vli.h
@@ -0,0 +1,351 @@
+/*
+-*- linux-c -*-
+ drbd_receiver.c
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _DRBD_VLI_H
+#define _DRBD_VLI_H
+
+/*
+ * At a granularity of 4KiB storage represented per bit,
+ * and stroage sizes of several TiB,
+ * and possibly small-bandwidth replication,
+ * the bitmap transfer time can take much too long,
+ * if transmitted in plain text.
+ *
+ * We try to reduce the transfered bitmap information
+ * by encoding runlengths of bit polarity.
+ *
+ * We never actually need to encode a "zero" (runlengths are positive).
+ * But then we have to store the value of the first bit.
+ * The first bit of information thus shall encode if the first runlength
+ * gives the number of set or unset bits.
+ *
+ * We assume that large areas are either completely set or unset,
+ * which gives good compression with any runlength method,
+ * even when encoding the runlength as fixed size 32bit/64bit integers.
+ *
+ * Still, there may be areas where the polarity flips every few bits,
+ * and encoding the runlength sequence of those areas with fix size
+ * integers would be much worse than plaintext.
+ *
+ * We want to encode small runlength values with minimum code length,
+ * while still being able to encode a Huge run of all zeros.
+ *
+ * Thus we need a Variable Length Integer encoding, VLI.
+ *
+ * For some cases, we produce more code bits than plaintext input.
+ * We need to send incompressible chunks as plaintext, skip over them
+ * and then see if the next chunk compresses better.
+ *
+ * We don't care too much about "excellent" compression ratio for large
+ * runlengths (all set/all clear): whether we achieve a factor of 100
+ * or 1000 is not that much of an issue.
+ * We do not want to waste too much on short runlengths in the "noisy"
+ * parts of the bitmap, though.
+ *
+ * There are endless variants of VLI, we experimented with:
+ * * simple byte-based
+ * * various bit based with different code word length.
+ *
+ * To avoid yet an other configuration parameter (choice of bitmap compression
+ * algorithm) which was difficult to explain and tune, we just chose the one
+ * variant that turned out best in all test cases.
+ * Based on real world usage patterns, with device sizes ranging from a few GiB
+ * to several TiB, file server/mailserver/webserver/mysql/postgress,
+ * mostly idle to really busy, the all time winner (though sometimes only
+ * marginally better) is:
+ */
+
+/*
+ * encoding is "visualised" as
+ * __little endian__ bitstream, least significant bit first (left most)
+ *
+ * this particular encoding is chosen so that the prefix code
+ * starts as unary encoding the level, then modified so that
+ * 10 levels can be described in 8bit, with minimal overhead
+ * for the smaller levels.
+ *
+ * Number of data bits follow fibonacci sequence, with the exception of the
+ * last level (+1 data bit, so it makes 64bit total). The only worse code when
+ * encoding bit polarity runlength is 1 plain bits => 2 code bits.
+prefix data bits max val Nº data bits
+0 x 0x2 1
+10 x 0x4 1
+110 xx 0x8 2
+1110 xxx 0x10 3
+11110 xxx xx 0x30 5
+111110 xx xxxxxx 0x130 8
+11111100 xxxxxxxx xxxxx 0x2130 13
+11111110 xxxxxxxx xxxxxxxx xxxxx 0x202130 21
+11111101 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xx 0x400202130 34
+11111111 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 56
+ * maximum encodable value: 0x100000400202130 == 2**56 + some */
+
+/* compression "table":
+ transmitted x 0.29
+ as plaintext x ........................
+ x ........................
+ x ........................
+ x 0.59 0.21........................
+ x ........................................................
+ x .. c ...................................................
+ x 0.44.. o ...................................................
+ x .......... d ...................................................
+ x .......... e ...................................................
+ X............. ...................................................
+ x.............. b ...................................................
+2.0x............... i ...................................................
+ #X................ t ...................................................
+ #................. s ........................... plain bits ..........
+-+-----------------------------------------------------------------------
+ 1 16 32 64
+*/
+
+/* LEVEL: (total bits, prefix bits, prefix value),
+ * sorted ascending by number of total bits.
+ * The rest of the code table is calculated at compiletime from this. */
+
+/* fibonacci data 1, 1, ... */
+#define VLI_L_1_1() do { \
+ LEVEL( 2, 1, 0x00); \
+ LEVEL( 3, 2, 0x01); \
+ LEVEL( 5, 3, 0x03); \
+ LEVEL( 7, 4, 0x07); \
+ LEVEL(10, 5, 0x0f); \
+ LEVEL(14, 6, 0x1f); \
+ LEVEL(21, 8, 0x3f); \
+ LEVEL(29, 8, 0x7f); \
+ LEVEL(42, 8, 0xbf); \
+ LEVEL(64, 8, 0xff); \
+ } while (0)
+
+/* finds a suitable level to decode the least significant part of in.
+ * returns number of bits consumed.
+ *
+ * BUG() for bad input, as that would mean a buggy code table. */
+static inline int vli_decode_bits(u64 *out, const u64 in)
+{
+ u64 adj = 1;
+
+#define LEVEL(t,b,v) \
+ do { \
+ if ((in & ((1 << b) -1)) == v) { \
+ *out = ((in & ((~0ULL) >> (64-t))) >> b) + adj; \
+ return t; \
+ } \
+ adj += 1ULL << (t - b); \
+ } while (0)
+
+ VLI_L_1_1();
+
+ /* NOT REACHED, if VLI_LEVELS code table is defined properly */
+ BUG();
+#undef LEVEL
+}
+
+/* return number of code bits needed,
+ * or negative error number */
+static inline int __vli_encode_bits(u64 *out, const u64 in)
+{
+ u64 max = 0;
+ u64 adj = 1;
+
+ if (in == 0)
+ return -EINVAL;
+
+#define LEVEL(t,b,v) do { \
+ max += 1ULL << (t - b); \
+ if (in <= max) { \
+ if (out) \
+ *out = ((in - adj) << b) | v; \
+ return t; \
+ } \
+ adj = max + 1; \
+ } while (0)
+
+ VLI_L_1_1();
+
+ return -EOVERFLOW;
+#undef LEVEL
+}
+
+#undef VLI_L_1_1
+
+/* code from here down is independend of actually used bit code */
+
+/*
+ * Code length is determined by some unique (e.g. unary) prefix.
+ * This encodes arbitrary bit length, not whole bytes: we have a bit-stream,
+ * not a byte stream.
+ */
+
+/* for the bitstream, we need a cursor */
+struct bitstream_cursor {
+ /* the current byte */
+ u8 *b;
+ /* the current bit within *b, nomalized: 0..7 */
+ unsigned int bit;
+};
+
+/* initialize cursor to point to first bit of stream */
+static inline void bitstream_cursor_reset(struct bitstream_cursor *cur, void *s)
+{
+ cur->b = s;
+ cur->bit = 0;
+}
+
+/* advance cursor by that many bits; maximum expected input value: 64,
+ * but depending on VLI implementation, it may be more. */
+static inline void bitstream_cursor_advance(struct bitstream_cursor *cur, unsigned int bits)
+{
+ bits += cur->bit;
+ cur->b = cur->b + (bits >> 3);
+ cur->bit = bits & 7;
+}
+
+/* the bitstream itself knows its length */
+struct bitstream {
+ struct bitstream_cursor cur;
+ unsigned char *buf;
+ size_t buf_len; /* in bytes */
+
+ /* for input stream:
+ * number of trailing 0 bits for padding
+ * total number of valid bits in stream: buf_len * 8 - pad_bits */
+ unsigned int pad_bits;
+};
+
+static inline void bitstream_init(struct bitstream *bs, void *s, size_t len, unsigned int pad_bits)
+{
+ bs->buf = s;
+ bs->buf_len = len;
+ bs->pad_bits = pad_bits;
+ bitstream_cursor_reset(&bs->cur, bs->buf);
+}
+
+static inline void bitstream_rewind(struct bitstream *bs)
+{
+ bitstream_cursor_reset(&bs->cur, bs->buf);
+ memset(bs->buf, 0, bs->buf_len);
+}
+
+/* Put (at most 64) least significant bits of val into bitstream, and advance cursor.
+ * Ignores "pad_bits".
+ * Returns zero if bits == 0 (nothing to do).
+ * Returns number of bits used if successful.
+ *
+ * If there is not enough room left in bitstream,
+ * leaves bitstream unchanged and returns -ENOBUFS.
+ */
+static inline int bitstream_put_bits(struct bitstream *bs, u64 val, const unsigned int bits)
+{
+ unsigned char *b = bs->cur.b;
+ unsigned int tmp;
+
+ if (bits == 0)
+ return 0;
+
+ if ((bs->cur.b + ((bs->cur.bit + bits -1) >> 3)) - bs->buf >= bs->buf_len)
+ return -ENOBUFS;
+
+ /* paranoia: strip off hi bits; they should not be set anyways. */
+ if (bits < 64)
+ val &= ~0ULL >> (64 - bits);
+
+ *b++ |= (val & 0xff) << bs->cur.bit;
+
+ for (tmp = 8 - bs->cur.bit; tmp < bits; tmp += 8)
+ *b++ |= (val >> tmp) & 0xff;
+
+ bitstream_cursor_advance(&bs->cur, bits);
+ return bits;
+}
+
+/* Fetch (at most 64) bits from bitstream into *out, and advance cursor.
+ *
+ * If more than 64 bits are requested, returns -EINVAL and leave *out unchanged.
+ *
+ * If there are less than the requested number of valid bits left in the
+ * bitstream, still fetches all available bits.
+ *
+ * Returns number of actually fetched bits.
+ */
+static inline int bitstream_get_bits(struct bitstream *bs, u64 *out, int bits)
+{
+ u64 val;
+ unsigned int n;
+
+ if (bits > 64)
+ return -EINVAL;
+
+ if (bs->cur.b + ((bs->cur.bit + bs->pad_bits + bits -1) >> 3) - bs->buf >= bs->buf_len)
+ bits = ((bs->buf_len - (bs->cur.b - bs->buf)) << 3)
+ - bs->cur.bit - bs->pad_bits;
+
+ if (bits == 0) {
+ *out = 0;
+ return 0;
+ }
+
+ /* get the high bits */
+ val = 0;
+ n = (bs->cur.bit + bits + 7) >> 3;
+ /* n may be at most 9, if cur.bit + bits > 64 */
+ /* which means this copies at most 8 byte */
+ if (n) {
+ memcpy(&val, bs->cur.b+1, n - 1);
+ val = le64_to_cpu(val) << (8 - bs->cur.bit);
+ }
+
+ /* we still need the low bits */
+ val |= bs->cur.b[0] >> bs->cur.bit;
+
+ /* and mask out bits we don't want */
+ val &= ~0ULL >> (64 - bits);
+
+ bitstream_cursor_advance(&bs->cur, bits);
+ *out = val;
+
+ return bits;
+}
+
+/* encodes @in as vli into @bs;
+
+ * return values
+ * > 0: number of bits successfully stored in bitstream
+ * -ENOBUFS @bs is full
+ * -EINVAL input zero (invalid)
+ * -EOVERFLOW input too large for this vli code (invalid)
+ */
+static inline int vli_encode_bits(struct bitstream *bs, u64 in)
+{
+ u64 code = code;
+ int bits = __vli_encode_bits(&code, in);
+
+ if (bits <= 0)
+ return bits;
+
+ return bitstream_put_bits(bs, code, bits);
+}
+
+#endif
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
new file mode 100644
index 00000000000..ed8796f1112
--- /dev/null
+++ b/drivers/block/drbd/drbd_worker.c
@@ -0,0 +1,1512 @@
+/*
+ drbd_worker.c
+
+ This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+ Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
+ Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
+ Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
+
+ drbd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ drbd 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 drbd; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/drbd.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/memcontrol.h>
+#include <linux/mm_inline.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/scatterlist.h>
+
+#include "drbd_int.h"
+#include "drbd_req.h"
+
+#define SLEEP_TIME (HZ/10)
+
+static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel);
+
+
+
+/* defined here:
+ drbd_md_io_complete
+ drbd_endio_write_sec
+ drbd_endio_read_sec
+ drbd_endio_pri
+
+ * more endio handlers:
+ atodb_endio in drbd_actlog.c
+ drbd_bm_async_io_complete in drbd_bitmap.c
+
+ * For all these callbacks, note the following:
+ * The callbacks will be called in irq context by the IDE drivers,
+ * and in Softirqs/Tasklets/BH context by the SCSI drivers.
+ * Try to get the locking right :)
+ *
+ */
+
+
+/* About the global_state_lock
+ Each state transition on an device holds a read lock. In case we have
+ to evaluate the sync after dependencies, we grab a write lock, because
+ we need stable states on all devices for that. */
+rwlock_t global_state_lock;
+
+/* used for synchronous meta data and bitmap IO
+ * submitted by drbd_md_sync_page_io()
+ */
+void drbd_md_io_complete(struct bio *bio, int error)
+{
+ struct drbd_md_io *md_io;
+
+ md_io = (struct drbd_md_io *)bio->bi_private;
+ md_io->error = error;
+
+ complete(&md_io->event);
+}
+
+/* reads on behalf of the partner,
+ * "submitted" by the receiver
+ */
+void drbd_endio_read_sec(struct bio *bio, int error) __releases(local)
+{
+ unsigned long flags = 0;
+ struct drbd_epoch_entry *e = NULL;
+ struct drbd_conf *mdev;
+ int uptodate = bio_flagged(bio, BIO_UPTODATE);
+
+ e = bio->bi_private;
+ mdev = e->mdev;
+
+ if (error)
+ dev_warn(DEV, "read: error=%d s=%llus\n", error,
+ (unsigned long long)e->sector);
+ if (!error && !uptodate) {
+ dev_warn(DEV, "read: setting error to -EIO s=%llus\n",
+ (unsigned long long)e->sector);
+ /* strange behavior of some lower level drivers...
+ * fail the request by clearing the uptodate flag,
+ * but do not return any error?! */
+ error = -EIO;
+ }
+
+ D_ASSERT(e->block_id != ID_VACANT);
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ mdev->read_cnt += e->size >> 9;
+ list_del(&e->w.list);
+ if (list_empty(&mdev->read_ee))
+ wake_up(&mdev->ee_wait);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ drbd_chk_io_error(mdev, error, FALSE);
+ drbd_queue_work(&mdev->data.work, &e->w);
+ put_ldev(mdev);
+}
+
+/* writes on behalf of the partner, or resync writes,
+ * "submitted" by the receiver.
+ */
+void drbd_endio_write_sec(struct bio *bio, int error) __releases(local)
+{
+ unsigned long flags = 0;
+ struct drbd_epoch_entry *e = NULL;
+ struct drbd_conf *mdev;
+ sector_t e_sector;
+ int do_wake;
+ int is_syncer_req;
+ int do_al_complete_io;
+ int uptodate = bio_flagged(bio, BIO_UPTODATE);
+ int is_barrier = bio_rw_flagged(bio, BIO_RW_BARRIER);
+
+ e = bio->bi_private;
+ mdev = e->mdev;
+
+ if (error)
+ dev_warn(DEV, "write: error=%d s=%llus\n", error,
+ (unsigned long long)e->sector);
+ if (!error && !uptodate) {
+ dev_warn(DEV, "write: setting error to -EIO s=%llus\n",
+ (unsigned long long)e->sector);
+ /* strange behavior of some lower level drivers...
+ * fail the request by clearing the uptodate flag,
+ * but do not return any error?! */
+ error = -EIO;
+ }
+
+ /* error == -ENOTSUPP would be a better test,
+ * alas it is not reliable */
+ if (error && is_barrier && e->flags & EE_IS_BARRIER) {
+ drbd_bump_write_ordering(mdev, WO_bdev_flush);
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ list_del(&e->w.list);
+ e->w.cb = w_e_reissue;
+ /* put_ldev actually happens below, once we come here again. */
+ __release(local);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+ drbd_queue_work(&mdev->data.work, &e->w);
+ return;
+ }
+
+ D_ASSERT(e->block_id != ID_VACANT);
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ mdev->writ_cnt += e->size >> 9;
+ is_syncer_req = is_syncer_block_id(e->block_id);
+
+ /* after we moved e to done_ee,
+ * we may no longer access it,
+ * it may be freed/reused already!
+ * (as soon as we release the req_lock) */
+ e_sector = e->sector;
+ do_al_complete_io = e->flags & EE_CALL_AL_COMPLETE_IO;
+
+ list_del(&e->w.list); /* has been on active_ee or sync_ee */
+ list_add_tail(&e->w.list, &mdev->done_ee);
+
+ /* No hlist_del_init(&e->colision) here, we did not send the Ack yet,
+ * neither did we wake possibly waiting conflicting requests.
+ * done from "drbd_process_done_ee" within the appropriate w.cb
+ * (e_end_block/e_end_resync_block) or from _drbd_clear_done_ee */
+
+ do_wake = is_syncer_req
+ ? list_empty(&mdev->sync_ee)
+ : list_empty(&mdev->active_ee);
+
+ if (error)
+ __drbd_chk_io_error(mdev, FALSE);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ if (is_syncer_req)
+ drbd_rs_complete_io(mdev, e_sector);
+
+ if (do_wake)
+ wake_up(&mdev->ee_wait);
+
+ if (do_al_complete_io)
+ drbd_al_complete_io(mdev, e_sector);
+
+ wake_asender(mdev);
+ put_ldev(mdev);
+
+}
+
+/* read, readA or write requests on R_PRIMARY coming from drbd_make_request
+ */
+void drbd_endio_pri(struct bio *bio, int error)
+{
+ unsigned long flags;
+ struct drbd_request *req = bio->bi_private;
+ struct drbd_conf *mdev = req->mdev;
+ struct bio_and_error m;
+ enum drbd_req_event what;
+ int uptodate = bio_flagged(bio, BIO_UPTODATE);
+
+ if (error)
+ dev_warn(DEV, "p %s: error=%d\n",
+ bio_data_dir(bio) == WRITE ? "write" : "read", error);
+ if (!error && !uptodate) {
+ dev_warn(DEV, "p %s: setting error to -EIO\n",
+ bio_data_dir(bio) == WRITE ? "write" : "read");
+ /* strange behavior of some lower level drivers...
+ * fail the request by clearing the uptodate flag,
+ * but do not return any error?! */
+ error = -EIO;
+ }
+
+ /* to avoid recursion in __req_mod */
+ if (unlikely(error)) {
+ what = (bio_data_dir(bio) == WRITE)
+ ? write_completed_with_error
+ : (bio_rw(bio) == READA)
+ ? read_completed_with_error
+ : read_ahead_completed_with_error;
+ } else
+ what = completed_ok;
+
+ bio_put(req->private_bio);
+ req->private_bio = ERR_PTR(error);
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+ __req_mod(req, what, &m);
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ if (m.bio)
+ complete_master_bio(mdev, &m);
+}
+
+int w_io_error(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_request *req = container_of(w, struct drbd_request, w);
+
+ /* NOTE: mdev->ldev can be NULL by the time we get here! */
+ /* D_ASSERT(mdev->ldev->dc.on_io_error != EP_PASS_ON); */
+
+ /* the only way this callback is scheduled is from _req_may_be_done,
+ * when it is done and had a local write error, see comments there */
+ drbd_req_free(req);
+
+ return TRUE;
+}
+
+int w_read_retry_remote(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_request *req = container_of(w, struct drbd_request, w);
+
+ /* We should not detach for read io-error,
+ * but try to WRITE the P_DATA_REPLY to the failed location,
+ * to give the disk the chance to relocate that block */
+
+ spin_lock_irq(&mdev->req_lock);
+ if (cancel ||
+ mdev->state.conn < C_CONNECTED ||
+ mdev->state.pdsk <= D_INCONSISTENT) {
+ _req_mod(req, send_canceled);
+ spin_unlock_irq(&mdev->req_lock);
+ dev_alert(DEV, "WE ARE LOST. Local IO failure, no peer.\n");
+ return 1;
+ }
+ spin_unlock_irq(&mdev->req_lock);
+
+ return w_send_read_req(mdev, w, 0);
+}
+
+int w_resync_inactive(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ ERR_IF(cancel) return 1;
+ dev_err(DEV, "resync inactive, but callback triggered??\n");
+ return 1; /* Simply ignore this! */
+}
+
+void drbd_csum(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bio *bio, void *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg;
+ struct bio_vec *bvec;
+ int i;
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ sg_init_table(&sg, 1);
+ crypto_hash_init(&desc);
+
+ __bio_for_each_segment(bvec, bio, i, 0) {
+ sg_set_page(&sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset);
+ crypto_hash_update(&desc, &sg, sg.length);
+ }
+ crypto_hash_final(&desc, digest);
+}
+
+static int w_e_send_csum(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+ int digest_size;
+ void *digest;
+ int ok;
+
+ D_ASSERT(e->block_id == DRBD_MAGIC + 0xbeef);
+
+ if (unlikely(cancel)) {
+ drbd_free_ee(mdev, e);
+ return 1;
+ }
+
+ if (likely(drbd_bio_uptodate(e->private_bio))) {
+ digest_size = crypto_hash_digestsize(mdev->csums_tfm);
+ digest = kmalloc(digest_size, GFP_NOIO);
+ if (digest) {
+ drbd_csum(mdev, mdev->csums_tfm, e->private_bio, digest);
+
+ inc_rs_pending(mdev);
+ ok = drbd_send_drequest_csum(mdev,
+ e->sector,
+ e->size,
+ digest,
+ digest_size,
+ P_CSUM_RS_REQUEST);
+ kfree(digest);
+ } else {
+ dev_err(DEV, "kmalloc() of digest failed.\n");
+ ok = 0;
+ }
+ } else
+ ok = 1;
+
+ drbd_free_ee(mdev, e);
+
+ if (unlikely(!ok))
+ dev_err(DEV, "drbd_send_drequest(..., csum) failed\n");
+ return ok;
+}
+
+#define GFP_TRY (__GFP_HIGHMEM | __GFP_NOWARN)
+
+static int read_for_csum(struct drbd_conf *mdev, sector_t sector, int size)
+{
+ struct drbd_epoch_entry *e;
+
+ if (!get_ldev(mdev))
+ return 0;
+
+ /* GFP_TRY, because if there is no memory available right now, this may
+ * be rescheduled for later. It is "only" background resync, after all. */
+ e = drbd_alloc_ee(mdev, DRBD_MAGIC+0xbeef, sector, size, GFP_TRY);
+ if (!e) {
+ put_ldev(mdev);
+ return 2;
+ }
+
+ spin_lock_irq(&mdev->req_lock);
+ list_add(&e->w.list, &mdev->read_ee);
+ spin_unlock_irq(&mdev->req_lock);
+
+ e->private_bio->bi_end_io = drbd_endio_read_sec;
+ e->private_bio->bi_rw = READ;
+ e->w.cb = w_e_send_csum;
+
+ mdev->read_cnt += size >> 9;
+ drbd_generic_make_request(mdev, DRBD_FAULT_RS_RD, e->private_bio);
+
+ return 1;
+}
+
+void resync_timer_fn(unsigned long data)
+{
+ unsigned long flags;
+ struct drbd_conf *mdev = (struct drbd_conf *) data;
+ int queue;
+
+ spin_lock_irqsave(&mdev->req_lock, flags);
+
+ if (likely(!test_and_clear_bit(STOP_SYNC_TIMER, &mdev->flags))) {
+ queue = 1;
+ if (mdev->state.conn == C_VERIFY_S)
+ mdev->resync_work.cb = w_make_ov_request;
+ else
+ mdev->resync_work.cb = w_make_resync_request;
+ } else {
+ queue = 0;
+ mdev->resync_work.cb = w_resync_inactive;
+ }
+
+ spin_unlock_irqrestore(&mdev->req_lock, flags);
+
+ /* harmless race: list_empty outside data.work.q_lock */
+ if (list_empty(&mdev->resync_work.list) && queue)
+ drbd_queue_work(&mdev->data.work, &mdev->resync_work);
+}
+
+int w_make_resync_request(struct drbd_conf *mdev,
+ struct drbd_work *w, int cancel)
+{
+ unsigned long bit;
+ sector_t sector;
+ const sector_t capacity = drbd_get_capacity(mdev->this_bdev);
+ int max_segment_size = queue_max_segment_size(mdev->rq_queue);
+ int number, i, size, pe, mx;
+ int align, queued, sndbuf;
+
+ if (unlikely(cancel))
+ return 1;
+
+ if (unlikely(mdev->state.conn < C_CONNECTED)) {
+ dev_err(DEV, "Confused in w_make_resync_request()! cstate < Connected");
+ return 0;
+ }
+
+ if (mdev->state.conn != C_SYNC_TARGET)
+ dev_err(DEV, "%s in w_make_resync_request\n",
+ drbd_conn_str(mdev->state.conn));
+
+ if (!get_ldev(mdev)) {
+ /* Since we only need to access mdev->rsync a
+ get_ldev_if_state(mdev,D_FAILED) would be sufficient, but
+ to continue resync with a broken disk makes no sense at
+ all */
+ dev_err(DEV, "Disk broke down during resync!\n");
+ mdev->resync_work.cb = w_resync_inactive;
+ return 1;
+ }
+
+ number = SLEEP_TIME * mdev->sync_conf.rate / ((BM_BLOCK_SIZE/1024)*HZ);
+ pe = atomic_read(&mdev->rs_pending_cnt);
+
+ mutex_lock(&mdev->data.mutex);
+ if (mdev->data.socket)
+ mx = mdev->data.socket->sk->sk_rcvbuf / sizeof(struct p_block_req);
+ else
+ mx = 1;
+ mutex_unlock(&mdev->data.mutex);
+
+ /* For resync rates >160MB/sec, allow more pending RS requests */
+ if (number > mx)
+ mx = number;
+
+ /* Limit the number of pending RS requests to no more than the peer's receive buffer */
+ if ((pe + number) > mx) {
+ number = mx - pe;
+ }
+
+ for (i = 0; i < number; i++) {
+ /* Stop generating RS requests, when half of the send buffer is filled */
+ mutex_lock(&mdev->data.mutex);
+ if (mdev->data.socket) {
+ queued = mdev->data.socket->sk->sk_wmem_queued;
+ sndbuf = mdev->data.socket->sk->sk_sndbuf;
+ } else {
+ queued = 1;
+ sndbuf = 0;
+ }
+ mutex_unlock(&mdev->data.mutex);
+ if (queued > sndbuf / 2)
+ goto requeue;
+
+next_sector:
+ size = BM_BLOCK_SIZE;
+ bit = drbd_bm_find_next(mdev, mdev->bm_resync_fo);
+
+ if (bit == -1UL) {
+ mdev->bm_resync_fo = drbd_bm_bits(mdev);
+ mdev->resync_work.cb = w_resync_inactive;
+ put_ldev(mdev);
+ return 1;
+ }
+
+ sector = BM_BIT_TO_SECT(bit);
+
+ if (drbd_try_rs_begin_io(mdev, sector)) {
+ mdev->bm_resync_fo = bit;
+ goto requeue;
+ }
+ mdev->bm_resync_fo = bit + 1;
+
+ if (unlikely(drbd_bm_test_bit(mdev, bit) == 0)) {
+ drbd_rs_complete_io(mdev, sector);
+ goto next_sector;
+ }
+
+#if DRBD_MAX_SEGMENT_SIZE > BM_BLOCK_SIZE
+ /* try to find some adjacent bits.
+ * we stop if we have already the maximum req size.
+ *
+ * Additionally always align bigger requests, in order to
+ * be prepared for all stripe sizes of software RAIDs.
+ *
+ * we _do_ care about the agreed-upon q->max_segment_size
+ * here, as splitting up the requests on the other side is more
+ * difficult. the consequence is, that on lvm and md and other
+ * "indirect" devices, this is dead code, since
+ * q->max_segment_size will be PAGE_SIZE.
+ */
+ align = 1;
+ for (;;) {
+ if (size + BM_BLOCK_SIZE > max_segment_size)
+ break;
+
+ /* Be always aligned */
+ if (sector & ((1<<(align+3))-1))
+ break;
+
+ /* do not cross extent boundaries */
+ if (((bit+1) & BM_BLOCKS_PER_BM_EXT_MASK) == 0)
+ break;
+ /* now, is it actually dirty, after all?
+ * caution, drbd_bm_test_bit is tri-state for some
+ * obscure reason; ( b == 0 ) would get the out-of-band
+ * only accidentally right because of the "oddly sized"
+ * adjustment below */
+ if (drbd_bm_test_bit(mdev, bit+1) != 1)
+ break;
+ bit++;
+ size += BM_BLOCK_SIZE;
+ if ((BM_BLOCK_SIZE << align) <= size)
+ align++;
+ i++;
+ }
+ /* if we merged some,
+ * reset the offset to start the next drbd_bm_find_next from */
+ if (size > BM_BLOCK_SIZE)
+ mdev->bm_resync_fo = bit + 1;
+#endif
+
+ /* adjust very last sectors, in case we are oddly sized */
+ if (sector + (size>>9) > capacity)
+ size = (capacity-sector)<<9;
+ if (mdev->agreed_pro_version >= 89 && mdev->csums_tfm) {
+ switch (read_for_csum(mdev, sector, size)) {
+ case 0: /* Disk failure*/
+ put_ldev(mdev);
+ return 0;
+ case 2: /* Allocation failed */
+ drbd_rs_complete_io(mdev, sector);
+ mdev->bm_resync_fo = BM_SECT_TO_BIT(sector);
+ goto requeue;
+ /* case 1: everything ok */
+ }
+ } else {
+ inc_rs_pending(mdev);
+ if (!drbd_send_drequest(mdev, P_RS_DATA_REQUEST,
+ sector, size, ID_SYNCER)) {
+ dev_err(DEV, "drbd_send_drequest() failed, aborting...\n");
+ dec_rs_pending(mdev);
+ put_ldev(mdev);
+ return 0;
+ }
+ }
+ }
+
+ if (mdev->bm_resync_fo >= drbd_bm_bits(mdev)) {
+ /* last syncer _request_ was sent,
+ * but the P_RS_DATA_REPLY not yet received. sync will end (and
+ * next sync group will resume), as soon as we receive the last
+ * resync data block, and the last bit is cleared.
+ * until then resync "work" is "inactive" ...
+ */
+ mdev->resync_work.cb = w_resync_inactive;
+ put_ldev(mdev);
+ return 1;
+ }
+
+ requeue:
+ mod_timer(&mdev->resync_timer, jiffies + SLEEP_TIME);
+ put_ldev(mdev);
+ return 1;
+}
+
+static int w_make_ov_request(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ int number, i, size;
+ sector_t sector;
+ const sector_t capacity = drbd_get_capacity(mdev->this_bdev);
+
+ if (unlikely(cancel))
+ return 1;
+
+ if (unlikely(mdev->state.conn < C_CONNECTED)) {
+ dev_err(DEV, "Confused in w_make_ov_request()! cstate < Connected");
+ return 0;
+ }
+
+ number = SLEEP_TIME*mdev->sync_conf.rate / ((BM_BLOCK_SIZE/1024)*HZ);
+ if (atomic_read(&mdev->rs_pending_cnt) > number)
+ goto requeue;
+
+ number -= atomic_read(&mdev->rs_pending_cnt);
+
+ sector = mdev->ov_position;
+ for (i = 0; i < number; i++) {
+ if (sector >= capacity) {
+ mdev->resync_work.cb = w_resync_inactive;
+ return 1;
+ }
+
+ size = BM_BLOCK_SIZE;
+
+ if (drbd_try_rs_begin_io(mdev, sector)) {
+ mdev->ov_position = sector;
+ goto requeue;
+ }
+
+ if (sector + (size>>9) > capacity)
+ size = (capacity-sector)<<9;
+
+ inc_rs_pending(mdev);
+ if (!drbd_send_ov_request(mdev, sector, size)) {
+ dec_rs_pending(mdev);
+ return 0;
+ }
+ sector += BM_SECT_PER_BIT;
+ }
+ mdev->ov_position = sector;
+
+ requeue:
+ mod_timer(&mdev->resync_timer, jiffies + SLEEP_TIME);
+ return 1;
+}
+
+
+int w_ov_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ kfree(w);
+ ov_oos_print(mdev);
+ drbd_resync_finished(mdev);
+
+ return 1;
+}
+
+static int w_resync_finished(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ kfree(w);
+
+ drbd_resync_finished(mdev);
+
+ return 1;
+}
+
+int drbd_resync_finished(struct drbd_conf *mdev)
+{
+ unsigned long db, dt, dbdt;
+ unsigned long n_oos;
+ union drbd_state os, ns;
+ struct drbd_work *w;
+ char *khelper_cmd = NULL;
+
+ /* Remove all elements from the resync LRU. Since future actions
+ * might set bits in the (main) bitmap, then the entries in the
+ * resync LRU would be wrong. */
+ if (drbd_rs_del_all(mdev)) {
+ /* In case this is not possible now, most probably because
+ * there are P_RS_DATA_REPLY Packets lingering on the worker's
+ * queue (or even the read operations for those packets
+ * is not finished by now). Retry in 100ms. */
+
+ drbd_kick_lo(mdev);
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ w = kmalloc(sizeof(struct drbd_work), GFP_ATOMIC);
+ if (w) {
+ w->cb = w_resync_finished;
+ drbd_queue_work(&mdev->data.work, w);
+ return 1;
+ }
+ dev_err(DEV, "Warn failed to drbd_rs_del_all() and to kmalloc(w).\n");
+ }
+
+ dt = (jiffies - mdev->rs_start - mdev->rs_paused) / HZ;
+ if (dt <= 0)
+ dt = 1;
+ db = mdev->rs_total;
+ dbdt = Bit2KB(db/dt);
+ mdev->rs_paused /= HZ;
+
+ if (!get_ldev(mdev))
+ goto out;
+
+ spin_lock_irq(&mdev->req_lock);
+ os = mdev->state;
+
+ /* This protects us against multiple calls (that can happen in the presence
+ of application IO), and against connectivity loss just before we arrive here. */
+ if (os.conn <= C_CONNECTED)
+ goto out_unlock;
+
+ ns = os;
+ ns.conn = C_CONNECTED;
+
+ dev_info(DEV, "%s done (total %lu sec; paused %lu sec; %lu K/sec)\n",
+ (os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) ?
+ "Online verify " : "Resync",
+ dt + mdev->rs_paused, mdev->rs_paused, dbdt);
+
+ n_oos = drbd_bm_total_weight(mdev);
+
+ if (os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) {
+ if (n_oos) {
+ dev_alert(DEV, "Online verify found %lu %dk block out of sync!\n",
+ n_oos, Bit2KB(1));
+ khelper_cmd = "out-of-sync";
+ }
+ } else {
+ D_ASSERT((n_oos - mdev->rs_failed) == 0);
+
+ if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T)
+ khelper_cmd = "after-resync-target";
+
+ if (mdev->csums_tfm && mdev->rs_total) {
+ const unsigned long s = mdev->rs_same_csum;
+ const unsigned long t = mdev->rs_total;
+ const int ratio =
+ (t == 0) ? 0 :
+ (t < 100000) ? ((s*100)/t) : (s/(t/100));
+ dev_info(DEV, "%u %% had equal check sums, eliminated: %luK; "
+ "transferred %luK total %luK\n",
+ ratio,
+ Bit2KB(mdev->rs_same_csum),
+ Bit2KB(mdev->rs_total - mdev->rs_same_csum),
+ Bit2KB(mdev->rs_total));
+ }
+ }
+
+ if (mdev->rs_failed) {
+ dev_info(DEV, " %lu failed blocks\n", mdev->rs_failed);
+
+ if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T) {
+ ns.disk = D_INCONSISTENT;
+ ns.pdsk = D_UP_TO_DATE;
+ } else {
+ ns.disk = D_UP_TO_DATE;
+ ns.pdsk = D_INCONSISTENT;
+ }
+ } else {
+ ns.disk = D_UP_TO_DATE;
+ ns.pdsk = D_UP_TO_DATE;
+
+ if (os.conn == C_SYNC_TARGET || os.conn == C_PAUSED_SYNC_T) {
+ if (mdev->p_uuid) {
+ int i;
+ for (i = UI_BITMAP ; i <= UI_HISTORY_END ; i++)
+ _drbd_uuid_set(mdev, i, mdev->p_uuid[i]);
+ drbd_uuid_set(mdev, UI_BITMAP, mdev->ldev->md.uuid[UI_CURRENT]);
+ _drbd_uuid_set(mdev, UI_CURRENT, mdev->p_uuid[UI_CURRENT]);
+ } else {
+ dev_err(DEV, "mdev->p_uuid is NULL! BUG\n");
+ }
+ }
+
+ drbd_uuid_set_bm(mdev, 0UL);
+
+ if (mdev->p_uuid) {
+ /* Now the two UUID sets are equal, update what we
+ * know of the peer. */
+ int i;
+ for (i = UI_CURRENT ; i <= UI_HISTORY_END ; i++)
+ mdev->p_uuid[i] = mdev->ldev->md.uuid[i];
+ }
+ }
+
+ _drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
+out_unlock:
+ spin_unlock_irq(&mdev->req_lock);
+ put_ldev(mdev);
+out:
+ mdev->rs_total = 0;
+ mdev->rs_failed = 0;
+ mdev->rs_paused = 0;
+ mdev->ov_start_sector = 0;
+
+ if (test_and_clear_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags)) {
+ dev_warn(DEV, "Writing the whole bitmap, due to failed kmalloc\n");
+ drbd_queue_bitmap_io(mdev, &drbd_bm_write, NULL, "write from resync_finished");
+ }
+
+ if (khelper_cmd)
+ drbd_khelper(mdev, khelper_cmd);
+
+ return 1;
+}
+
+/* helper */
+static void move_to_net_ee_or_free(struct drbd_conf *mdev, struct drbd_epoch_entry *e)
+{
+ if (drbd_bio_has_active_page(e->private_bio)) {
+ /* This might happen if sendpage() has not finished */
+ spin_lock_irq(&mdev->req_lock);
+ list_add_tail(&e->w.list, &mdev->net_ee);
+ spin_unlock_irq(&mdev->req_lock);
+ } else
+ drbd_free_ee(mdev, e);
+}
+
+/**
+ * w_e_end_data_req() - Worker callback, to send a P_DATA_REPLY packet in response to a P_DATA_REQUEST
+ * @mdev: DRBD device.
+ * @w: work object.
+ * @cancel: The connection will be closed anyways
+ */
+int w_e_end_data_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+ int ok;
+
+ if (unlikely(cancel)) {
+ drbd_free_ee(mdev, e);
+ dec_unacked(mdev);
+ return 1;
+ }
+
+ if (likely(drbd_bio_uptodate(e->private_bio))) {
+ ok = drbd_send_block(mdev, P_DATA_REPLY, e);
+ } else {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Sending NegDReply. sector=%llus.\n",
+ (unsigned long long)e->sector);
+
+ ok = drbd_send_ack(mdev, P_NEG_DREPLY, e);
+ }
+
+ dec_unacked(mdev);
+
+ move_to_net_ee_or_free(mdev, e);
+
+ if (unlikely(!ok))
+ dev_err(DEV, "drbd_send_block() failed\n");
+ return ok;
+}
+
+/**
+ * w_e_end_rsdata_req() - Worker callback to send a P_RS_DATA_REPLY packet in response to a P_RS_DATA_REQUESTRS
+ * @mdev: DRBD device.
+ * @w: work object.
+ * @cancel: The connection will be closed anyways
+ */
+int w_e_end_rsdata_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+ int ok;
+
+ if (unlikely(cancel)) {
+ drbd_free_ee(mdev, e);
+ dec_unacked(mdev);
+ return 1;
+ }
+
+ if (get_ldev_if_state(mdev, D_FAILED)) {
+ drbd_rs_complete_io(mdev, e->sector);
+ put_ldev(mdev);
+ }
+
+ if (likely(drbd_bio_uptodate(e->private_bio))) {
+ if (likely(mdev->state.pdsk >= D_INCONSISTENT)) {
+ inc_rs_pending(mdev);
+ ok = drbd_send_block(mdev, P_RS_DATA_REPLY, e);
+ } else {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Not sending RSDataReply, "
+ "partner DISKLESS!\n");
+ ok = 1;
+ }
+ } else {
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Sending NegRSDReply. sector %llus.\n",
+ (unsigned long long)e->sector);
+
+ ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e);
+
+ /* update resync data with failure */
+ drbd_rs_failed_io(mdev, e->sector, e->size);
+ }
+
+ dec_unacked(mdev);
+
+ move_to_net_ee_or_free(mdev, e);
+
+ if (unlikely(!ok))
+ dev_err(DEV, "drbd_send_block() failed\n");
+ return ok;
+}
+
+int w_e_end_csum_rs_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+ struct digest_info *di;
+ int digest_size;
+ void *digest = NULL;
+ int ok, eq = 0;
+
+ if (unlikely(cancel)) {
+ drbd_free_ee(mdev, e);
+ dec_unacked(mdev);
+ return 1;
+ }
+
+ drbd_rs_complete_io(mdev, e->sector);
+
+ di = (struct digest_info *)(unsigned long)e->block_id;
+
+ if (likely(drbd_bio_uptodate(e->private_bio))) {
+ /* quick hack to try to avoid a race against reconfiguration.
+ * a real fix would be much more involved,
+ * introducing more locking mechanisms */
+ if (mdev->csums_tfm) {
+ digest_size = crypto_hash_digestsize(mdev->csums_tfm);
+ D_ASSERT(digest_size == di->digest_size);
+ digest = kmalloc(digest_size, GFP_NOIO);
+ }
+ if (digest) {
+ drbd_csum(mdev, mdev->csums_tfm, e->private_bio, digest);
+ eq = !memcmp(digest, di->digest, digest_size);
+ kfree(digest);
+ }
+
+ if (eq) {
+ drbd_set_in_sync(mdev, e->sector, e->size);
+ mdev->rs_same_csum++;
+ ok = drbd_send_ack(mdev, P_RS_IS_IN_SYNC, e);
+ } else {
+ inc_rs_pending(mdev);
+ e->block_id = ID_SYNCER;
+ ok = drbd_send_block(mdev, P_RS_DATA_REPLY, e);
+ }
+ } else {
+ ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e);
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Sending NegDReply. I guess it gets messy.\n");
+ }
+
+ dec_unacked(mdev);
+
+ kfree(di);
+
+ move_to_net_ee_or_free(mdev, e);
+
+ if (unlikely(!ok))
+ dev_err(DEV, "drbd_send_block/ack() failed\n");
+ return ok;
+}
+
+int w_e_end_ov_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+ int digest_size;
+ void *digest;
+ int ok = 1;
+
+ if (unlikely(cancel))
+ goto out;
+
+ if (unlikely(!drbd_bio_uptodate(e->private_bio)))
+ goto out;
+
+ digest_size = crypto_hash_digestsize(mdev->verify_tfm);
+ /* FIXME if this allocation fails, online verify will not terminate! */
+ digest = kmalloc(digest_size, GFP_NOIO);
+ if (digest) {
+ drbd_csum(mdev, mdev->verify_tfm, e->private_bio, digest);
+ inc_rs_pending(mdev);
+ ok = drbd_send_drequest_csum(mdev, e->sector, e->size,
+ digest, digest_size, P_OV_REPLY);
+ if (!ok)
+ dec_rs_pending(mdev);
+ kfree(digest);
+ }
+
+out:
+ drbd_free_ee(mdev, e);
+
+ dec_unacked(mdev);
+
+ return ok;
+}
+
+void drbd_ov_oos_found(struct drbd_conf *mdev, sector_t sector, int size)
+{
+ if (mdev->ov_last_oos_start + mdev->ov_last_oos_size == sector) {
+ mdev->ov_last_oos_size += size>>9;
+ } else {
+ mdev->ov_last_oos_start = sector;
+ mdev->ov_last_oos_size = size>>9;
+ }
+ drbd_set_out_of_sync(mdev, sector, size);
+ set_bit(WRITE_BM_AFTER_RESYNC, &mdev->flags);
+}
+
+int w_e_end_ov_reply(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_epoch_entry *e = container_of(w, struct drbd_epoch_entry, w);
+ struct digest_info *di;
+ int digest_size;
+ void *digest;
+ int ok, eq = 0;
+
+ if (unlikely(cancel)) {
+ drbd_free_ee(mdev, e);
+ dec_unacked(mdev);
+ return 1;
+ }
+
+ /* after "cancel", because after drbd_disconnect/drbd_rs_cancel_all
+ * the resync lru has been cleaned up already */
+ drbd_rs_complete_io(mdev, e->sector);
+
+ di = (struct digest_info *)(unsigned long)e->block_id;
+
+ if (likely(drbd_bio_uptodate(e->private_bio))) {
+ digest_size = crypto_hash_digestsize(mdev->verify_tfm);
+ digest = kmalloc(digest_size, GFP_NOIO);
+ if (digest) {
+ drbd_csum(mdev, mdev->verify_tfm, e->private_bio, digest);
+
+ D_ASSERT(digest_size == di->digest_size);
+ eq = !memcmp(digest, di->digest, digest_size);
+ kfree(digest);
+ }
+ } else {
+ ok = drbd_send_ack(mdev, P_NEG_RS_DREPLY, e);
+ if (__ratelimit(&drbd_ratelimit_state))
+ dev_err(DEV, "Sending NegDReply. I guess it gets messy.\n");
+ }
+
+ dec_unacked(mdev);
+
+ kfree(di);
+
+ if (!eq)
+ drbd_ov_oos_found(mdev, e->sector, e->size);
+ else
+ ov_oos_print(mdev);
+
+ ok = drbd_send_ack_ex(mdev, P_OV_RESULT, e->sector, e->size,
+ eq ? ID_IN_SYNC : ID_OUT_OF_SYNC);
+
+ drbd_free_ee(mdev, e);
+
+ if (--mdev->ov_left == 0) {
+ ov_oos_print(mdev);
+ drbd_resync_finished(mdev);
+ }
+
+ return ok;
+}
+
+int w_prev_work_done(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_wq_barrier *b = container_of(w, struct drbd_wq_barrier, w);
+ complete(&b->done);
+ return 1;
+}
+
+int w_send_barrier(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_tl_epoch *b = container_of(w, struct drbd_tl_epoch, w);
+ struct p_barrier *p = &mdev->data.sbuf.barrier;
+ int ok = 1;
+
+ /* really avoid racing with tl_clear. w.cb may have been referenced
+ * just before it was reassigned and re-queued, so double check that.
+ * actually, this race was harmless, since we only try to send the
+ * barrier packet here, and otherwise do nothing with the object.
+ * but compare with the head of w_clear_epoch */
+ spin_lock_irq(&mdev->req_lock);
+ if (w->cb != w_send_barrier || mdev->state.conn < C_CONNECTED)
+ cancel = 1;
+ spin_unlock_irq(&mdev->req_lock);
+ if (cancel)
+ return 1;
+
+ if (!drbd_get_data_sock(mdev))
+ return 0;
+ p->barrier = b->br_number;
+ /* inc_ap_pending was done where this was queued.
+ * dec_ap_pending will be done in got_BarrierAck
+ * or (on connection loss) in w_clear_epoch. */
+ ok = _drbd_send_cmd(mdev, mdev->data.socket, P_BARRIER,
+ (struct p_header *)p, sizeof(*p), 0);
+ drbd_put_data_sock(mdev);
+
+ return ok;
+}
+
+int w_send_write_hint(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ if (cancel)
+ return 1;
+ return drbd_send_short_cmd(mdev, P_UNPLUG_REMOTE);
+}
+
+/**
+ * w_send_dblock() - Worker callback to send a P_DATA packet in order to mirror a write request
+ * @mdev: DRBD device.
+ * @w: work object.
+ * @cancel: The connection will be closed anyways
+ */
+int w_send_dblock(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_request *req = container_of(w, struct drbd_request, w);
+ int ok;
+
+ if (unlikely(cancel)) {
+ req_mod(req, send_canceled);
+ return 1;
+ }
+
+ ok = drbd_send_dblock(mdev, req);
+ req_mod(req, ok ? handed_over_to_network : send_failed);
+
+ return ok;
+}
+
+/**
+ * w_send_read_req() - Worker callback to send a read request (P_DATA_REQUEST) packet
+ * @mdev: DRBD device.
+ * @w: work object.
+ * @cancel: The connection will be closed anyways
+ */
+int w_send_read_req(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
+{
+ struct drbd_request *req = container_of(w, struct drbd_request, w);
+ int ok;
+
+ if (unlikely(cancel)) {
+ req_mod(req, send_canceled);
+ return 1;
+ }
+
+ ok = drbd_send_drequest(mdev, P_DATA_REQUEST, req->sector, req->size,
+ (unsigned long)req);
+
+ if (!ok) {
+ /* ?? we set C_TIMEOUT or C_BROKEN_PIPE in drbd_send();
+ * so this is probably redundant */
+ if (mdev->state.conn >= C_CONNECTED)
+ drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE));
+ }
+ req_mod(req, ok ? handed_over_to_network : send_failed);
+
+ return ok;
+}
+
+static int _drbd_may_sync_now(struct drbd_conf *mdev)
+{
+ struct drbd_conf *odev = mdev;
+
+ while (1) {
+ if (odev->sync_conf.after == -1)
+ return 1;
+ odev = minor_to_mdev(odev->sync_conf.after);
+ ERR_IF(!odev) return 1;
+ if ((odev->state.conn >= C_SYNC_SOURCE &&
+ odev->state.conn <= C_PAUSED_SYNC_T) ||
+ odev->state.aftr_isp || odev->state.peer_isp ||
+ odev->state.user_isp)
+ return 0;
+ }
+}
+
+/**
+ * _drbd_pause_after() - Pause resync on all devices that may not resync now
+ * @mdev: DRBD device.
+ *
+ * Called from process context only (admin command and after_state_ch).
+ */
+static int _drbd_pause_after(struct drbd_conf *mdev)
+{
+ struct drbd_conf *odev;
+ int i, rv = 0;
+
+ for (i = 0; i < minor_count; i++) {
+ odev = minor_to_mdev(i);
+ if (!odev)
+ continue;
+ if (odev->state.conn == C_STANDALONE && odev->state.disk == D_DISKLESS)
+ continue;
+ if (!_drbd_may_sync_now(odev))
+ rv |= (__drbd_set_state(_NS(odev, aftr_isp, 1), CS_HARD, NULL)
+ != SS_NOTHING_TO_DO);
+ }
+
+ return rv;
+}
+
+/**
+ * _drbd_resume_next() - Resume resync on all devices that may resync now
+ * @mdev: DRBD device.
+ *
+ * Called from process context only (admin command and worker).
+ */
+static int _drbd_resume_next(struct drbd_conf *mdev)
+{
+ struct drbd_conf *odev;
+ int i, rv = 0;
+
+ for (i = 0; i < minor_count; i++) {
+ odev = minor_to_mdev(i);
+ if (!odev)
+ continue;
+ if (odev->state.conn == C_STANDALONE && odev->state.disk == D_DISKLESS)
+ continue;
+ if (odev->state.aftr_isp) {
+ if (_drbd_may_sync_now(odev))
+ rv |= (__drbd_set_state(_NS(odev, aftr_isp, 0),
+ CS_HARD, NULL)
+ != SS_NOTHING_TO_DO) ;
+ }
+ }
+ return rv;
+}
+
+void resume_next_sg(struct drbd_conf *mdev)
+{
+ write_lock_irq(&global_state_lock);
+ _drbd_resume_next(mdev);
+ write_unlock_irq(&global_state_lock);
+}
+
+void suspend_other_sg(struct drbd_conf *mdev)
+{
+ write_lock_irq(&global_state_lock);
+ _drbd_pause_after(mdev);
+ write_unlock_irq(&global_state_lock);
+}
+
+static int sync_after_error(struct drbd_conf *mdev, int o_minor)
+{
+ struct drbd_conf *odev;
+
+ if (o_minor == -1)
+ return NO_ERROR;
+ if (o_minor < -1 || minor_to_mdev(o_minor) == NULL)
+ return ERR_SYNC_AFTER;
+
+ /* check for loops */
+ odev = minor_to_mdev(o_minor);
+ while (1) {
+ if (odev == mdev)
+ return ERR_SYNC_AFTER_CYCLE;
+
+ /* dependency chain ends here, no cycles. */
+ if (odev->sync_conf.after == -1)
+ return NO_ERROR;
+
+ /* follow the dependency chain */
+ odev = minor_to_mdev(odev->sync_conf.after);
+ }
+}
+
+int drbd_alter_sa(struct drbd_conf *mdev, int na)
+{
+ int changes;
+ int retcode;
+
+ write_lock_irq(&global_state_lock);
+ retcode = sync_after_error(mdev, na);
+ if (retcode == NO_ERROR) {
+ mdev->sync_conf.after = na;
+ do {
+ changes = _drbd_pause_after(mdev);
+ changes |= _drbd_resume_next(mdev);
+ } while (changes);
+ }
+ write_unlock_irq(&global_state_lock);
+ return retcode;
+}
+
+/**
+ * drbd_start_resync() - Start the resync process
+ * @mdev: DRBD device.
+ * @side: Either C_SYNC_SOURCE or C_SYNC_TARGET
+ *
+ * This function might bring you directly into one of the
+ * C_PAUSED_SYNC_* states.
+ */
+void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
+{
+ union drbd_state ns;
+ int r;
+
+ if (mdev->state.conn >= C_SYNC_SOURCE) {
+ dev_err(DEV, "Resync already running!\n");
+ return;
+ }
+
+ /* In case a previous resync run was aborted by an IO error/detach on the peer. */
+ drbd_rs_cancel_all(mdev);
+
+ if (side == C_SYNC_TARGET) {
+ /* Since application IO was locked out during C_WF_BITMAP_T and
+ C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET
+ we check that we might make the data inconsistent. */
+ r = drbd_khelper(mdev, "before-resync-target");
+ r = (r >> 8) & 0xff;
+ if (r > 0) {
+ dev_info(DEV, "before-resync-target handler returned %d, "
+ "dropping connection.\n", r);
+ drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ return;
+ }
+ }
+
+ drbd_state_lock(mdev);
+
+ if (!get_ldev_if_state(mdev, D_NEGOTIATING)) {
+ drbd_state_unlock(mdev);
+ return;
+ }
+
+ if (side == C_SYNC_TARGET) {
+ mdev->bm_resync_fo = 0;
+ } else /* side == C_SYNC_SOURCE */ {
+ u64 uuid;
+
+ get_random_bytes(&uuid, sizeof(u64));
+ drbd_uuid_set(mdev, UI_BITMAP, uuid);
+ drbd_send_sync_uuid(mdev, uuid);
+
+ D_ASSERT(mdev->state.disk == D_UP_TO_DATE);
+ }
+
+ write_lock_irq(&global_state_lock);
+ ns = mdev->state;
+
+ ns.aftr_isp = !_drbd_may_sync_now(mdev);
+
+ ns.conn = side;
+
+ if (side == C_SYNC_TARGET)
+ ns.disk = D_INCONSISTENT;
+ else /* side == C_SYNC_SOURCE */
+ ns.pdsk = D_INCONSISTENT;
+
+ r = __drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
+ ns = mdev->state;
+
+ if (ns.conn < C_CONNECTED)
+ r = SS_UNKNOWN_ERROR;
+
+ if (r == SS_SUCCESS) {
+ mdev->rs_total =
+ mdev->rs_mark_left = drbd_bm_total_weight(mdev);
+ mdev->rs_failed = 0;
+ mdev->rs_paused = 0;
+ mdev->rs_start =
+ mdev->rs_mark_time = jiffies;
+ mdev->rs_same_csum = 0;
+ _drbd_pause_after(mdev);
+ }
+ write_unlock_irq(&global_state_lock);
+ drbd_state_unlock(mdev);
+ put_ldev(mdev);
+
+ if (r == SS_SUCCESS) {
+ dev_info(DEV, "Began resync as %s (will sync %lu KB [%lu bits set]).\n",
+ drbd_conn_str(ns.conn),
+ (unsigned long) mdev->rs_total << (BM_BLOCK_SHIFT-10),
+ (unsigned long) mdev->rs_total);
+
+ if (mdev->rs_total == 0) {
+ /* Peer still reachable? Beware of failing before-resync-target handlers! */
+ request_ping(mdev);
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(mdev->net_conf->ping_timeo*HZ/9); /* 9 instead 10 */
+ drbd_resync_finished(mdev);
+ return;
+ }
+
+ /* ns.conn may already be != mdev->state.conn,
+ * we may have been paused in between, or become paused until
+ * the timer triggers.
+ * No matter, that is handled in resync_timer_fn() */
+ if (ns.conn == C_SYNC_TARGET)
+ mod_timer(&mdev->resync_timer, jiffies);
+
+ drbd_md_sync(mdev);
+ }
+}
+
+int drbd_worker(struct drbd_thread *thi)
+{
+ struct drbd_conf *mdev = thi->mdev;
+ struct drbd_work *w = NULL;
+ LIST_HEAD(work_list);
+ int intr = 0, i;
+
+ sprintf(current->comm, "drbd%d_worker", mdev_to_minor(mdev));
+
+ while (get_t_state(thi) == Running) {
+ drbd_thread_current_set_cpu(mdev);
+
+ if (down_trylock(&mdev->data.work.s)) {
+ mutex_lock(&mdev->data.mutex);
+ if (mdev->data.socket && !mdev->net_conf->no_cork)
+ drbd_tcp_uncork(mdev->data.socket);
+ mutex_unlock(&mdev->data.mutex);
+
+ intr = down_interruptible(&mdev->data.work.s);
+
+ mutex_lock(&mdev->data.mutex);
+ if (mdev->data.socket && !mdev->net_conf->no_cork)
+ drbd_tcp_cork(mdev->data.socket);
+ mutex_unlock(&mdev->data.mutex);
+ }
+
+ if (intr) {
+ D_ASSERT(intr == -EINTR);
+ flush_signals(current);
+ ERR_IF (get_t_state(thi) == Running)
+ continue;
+ break;
+ }
+
+ if (get_t_state(thi) != Running)
+ break;
+ /* With this break, we have done a down() but not consumed
+ the entry from the list. The cleanup code takes care of
+ this... */
+
+ w = NULL;
+ spin_lock_irq(&mdev->data.work.q_lock);
+ ERR_IF(list_empty(&mdev->data.work.q)) {
+ /* something terribly wrong in our logic.
+ * we were able to down() the semaphore,
+ * but the list is empty... doh.
+ *
+ * what is the best thing to do now?
+ * try again from scratch, restarting the receiver,
+ * asender, whatnot? could break even more ugly,
+ * e.g. when we are primary, but no good local data.
+ *
+ * I'll try to get away just starting over this loop.
+ */
+ spin_unlock_irq(&mdev->data.work.q_lock);
+ continue;
+ }
+ w = list_entry(mdev->data.work.q.next, struct drbd_work, list);
+ list_del_init(&w->list);
+ spin_unlock_irq(&mdev->data.work.q_lock);
+
+ if (!w->cb(mdev, w, mdev->state.conn < C_CONNECTED)) {
+ /* dev_warn(DEV, "worker: a callback failed! \n"); */
+ if (mdev->state.conn >= C_CONNECTED)
+ drbd_force_state(mdev,
+ NS(conn, C_NETWORK_FAILURE));
+ }
+ }
+ D_ASSERT(test_bit(DEVICE_DYING, &mdev->flags));
+ D_ASSERT(test_bit(CONFIG_PENDING, &mdev->flags));
+
+ spin_lock_irq(&mdev->data.work.q_lock);
+ i = 0;
+ while (!list_empty(&mdev->data.work.q)) {
+ list_splice_init(&mdev->data.work.q, &work_list);
+ spin_unlock_irq(&mdev->data.work.q_lock);
+
+ while (!list_empty(&work_list)) {
+ w = list_entry(work_list.next, struct drbd_work, list);
+ list_del_init(&w->list);
+ w->cb(mdev, w, 1);
+ i++; /* dead debugging code */
+ }
+
+ spin_lock_irq(&mdev->data.work.q_lock);
+ }
+ sema_init(&mdev->data.work.s, 0);
+ /* DANGEROUS race: if someone did queue his work within the spinlock,
+ * but up() ed outside the spinlock, we could get an up() on the
+ * semaphore without corresponding list entry.
+ * So don't do that.
+ */
+ spin_unlock_irq(&mdev->data.work.q_lock);
+
+ D_ASSERT(mdev->state.disk == D_DISKLESS && mdev->state.conn == C_STANDALONE);
+ /* _drbd_set_state only uses stop_nowait.
+ * wait here for the Exiting receiver. */
+ drbd_thread_stop(&mdev->receiver);
+ drbd_mdev_cleanup(mdev);
+
+ dev_info(DEV, "worker terminated\n");
+
+ clear_bit(DEVICE_DYING, &mdev->flags);
+ clear_bit(CONFIG_PENDING, &mdev->flags);
+ wake_up(&mdev->state_wait);
+
+ return 0;
+}
diff --git a/drivers/block/drbd/drbd_wrappers.h b/drivers/block/drbd/drbd_wrappers.h
new file mode 100644
index 00000000000..f93fa111ce5
--- /dev/null
+++ b/drivers/block/drbd/drbd_wrappers.h
@@ -0,0 +1,91 @@
+#ifndef _DRBD_WRAPPERS_H
+#define _DRBD_WRAPPERS_H
+
+#include <linux/ctype.h>
+#include <linux/mm.h>
+
+/* see get_sb_bdev and bd_claim */
+extern char *drbd_sec_holder;
+
+/* sets the number of 512 byte sectors of our virtual device */
+static inline void drbd_set_my_capacity(struct drbd_conf *mdev,
+ sector_t size)
+{
+ /* set_capacity(mdev->this_bdev->bd_disk, size); */
+ set_capacity(mdev->vdisk, size);
+ mdev->this_bdev->bd_inode->i_size = (loff_t)size << 9;
+}
+
+#define drbd_bio_uptodate(bio) bio_flagged(bio, BIO_UPTODATE)
+
+static inline int drbd_bio_has_active_page(struct bio *bio)
+{
+ struct bio_vec *bvec;
+ int i;
+
+ __bio_for_each_segment(bvec, bio, i, 0) {
+ if (page_count(bvec->bv_page) > 1)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* bi_end_io handlers */
+extern void drbd_md_io_complete(struct bio *bio, int error);
+extern void drbd_endio_read_sec(struct bio *bio, int error);
+extern void drbd_endio_write_sec(struct bio *bio, int error);
+extern void drbd_endio_pri(struct bio *bio, int error);
+
+/*
+ * used to submit our private bio
+ */
+static inline void drbd_generic_make_request(struct drbd_conf *mdev,
+ int fault_type, struct bio *bio)
+{
+ __release(local);
+ if (!bio->bi_bdev) {
+ printk(KERN_ERR "drbd%d: drbd_generic_make_request: "
+ "bio->bi_bdev == NULL\n",
+ mdev_to_minor(mdev));
+ dump_stack();
+ bio_endio(bio, -ENODEV);
+ return;
+ }
+
+ if (FAULT_ACTIVE(mdev, fault_type))
+ bio_endio(bio, -EIO);
+ else
+ generic_make_request(bio);
+}
+
+static inline void drbd_plug_device(struct drbd_conf *mdev)
+{
+ struct request_queue *q;
+ q = bdev_get_queue(mdev->this_bdev);
+
+ spin_lock_irq(q->queue_lock);
+
+/* XXX the check on !blk_queue_plugged is redundant,
+ * implicitly checked in blk_plug_device */
+
+ if (!blk_queue_plugged(q)) {
+ blk_plug_device(q);
+ del_timer(&q->unplug_timer);
+ /* unplugging should not happen automatically... */
+ }
+ spin_unlock_irq(q->queue_lock);
+}
+
+static inline int drbd_crypto_is_hash(struct crypto_tfm *tfm)
+{
+ return (crypto_tfm_alg_type(tfm) & CRYPTO_ALG_TYPE_HASH_MASK)
+ == CRYPTO_ALG_TYPE_HASH;
+}
+
+#ifndef __CHECKER__
+# undef __cond_lock
+# define __cond_lock(x,c) (c)
+#endif
+
+#endif
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index 3bb7c47c869..1fb6c3135fc 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -123,7 +123,15 @@ static int ps3vram_notifier_wait(struct ps3_system_bus_device *dev,
{
struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev);
u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER);
- unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ unsigned long timeout;
+
+ for (timeout = 20; timeout; timeout--) {
+ if (!notify[3])
+ return 0;
+ udelay(10);
+ }
+
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
if (!notify[3])
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index b0e569ba730..2acdc605cb4 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -867,11 +867,9 @@ static int bluecard_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 8;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = bluecard_interrupt;
- link->irq.Instance = info;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -905,22 +903,16 @@ static int bluecard_config(struct pcmcia_device *link)
break;
}
- if (i != 0) {
- cs_error(link, RequestIO, i);
+ if (i != 0)
goto failed;
- }
i = pcmcia_request_irq(link, &link->irq);
- if (i != 0) {
- cs_error(link, RequestIRQ, i);
+ if (i != 0)
link->irq.AssignedIRQ = 0;
- }
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- cs_error(link, RequestConfiguration, i);
+ if (i != 0)
goto failed;
- }
if (bluecard_open(info) != 0)
goto failed;
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index d58e22b9f06..d814a2755cc 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -659,11 +659,9 @@ static int bt3c_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 8;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = bt3c_interrupt;
- link->irq.Instance = info;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -740,21 +738,16 @@ static int bt3c_config(struct pcmcia_device *link)
goto found_port;
BT_ERR("No usable port range found");
- cs_error(link, RequestIO, -ENODEV);
goto failed;
found_port:
i = pcmcia_request_irq(link, &link->irq);
- if (i != 0) {
- cs_error(link, RequestIRQ, i);
+ if (i != 0)
link->irq.AssignedIRQ = 0;
- }
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- cs_error(link, RequestConfiguration, i);
+ if (i != 0)
goto failed;
- }
if (bt3c_open(info) != 0)
goto failed;
diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c
index 4617bd12f63..d43b5cb864e 100644
--- a/drivers/bluetooth/btmrvl_debugfs.c
+++ b/drivers/bluetooth/btmrvl_debugfs.c
@@ -29,7 +29,6 @@ struct btmrvl_debugfs_data {
struct dentry *root_dir, *config_dir, *status_dir;
/* config */
- struct dentry *drvdbg;
struct dentry *psmode;
struct dentry *pscmd;
struct dentry *hsmode;
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index 411c7a77082..523d197b982 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -131,6 +131,7 @@ void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
+int btmrvl_enable_ps(struct btmrvl_private *priv);
int btmrvl_prepare_command(struct btmrvl_private *priv);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index e605563b4ea..f97771ce432 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -189,6 +189,38 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
}
EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
+int btmrvl_enable_ps(struct btmrvl_private *priv)
+{
+ struct sk_buff *skb;
+ struct btmrvl_cmd *cmd;
+
+ skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
+ if (skb == NULL) {
+ BT_ERR("No free skb");
+ return -ENOMEM;
+ }
+
+ cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
+ cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF,
+ BT_CMD_AUTO_SLEEP_MODE));
+ cmd->length = 1;
+
+ if (priv->btmrvl_dev.psmode)
+ cmd->data[0] = BT_PS_ENABLE;
+ else
+ cmd->data[0] = BT_PS_DISABLE;
+
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+
+ skb->dev = (void *) priv->btmrvl_dev.hcidev;
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+
+ BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
+
static int btmrvl_enable_hs(struct btmrvl_private *priv)
{
struct sk_buff *skb;
@@ -258,28 +290,7 @@ int btmrvl_prepare_command(struct btmrvl_private *priv)
if (priv->btmrvl_dev.pscmd) {
priv->btmrvl_dev.pscmd = 0;
-
- skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
- if (skb == NULL) {
- BT_ERR("No free skb");
- return -ENOMEM;
- }
-
- cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
- cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_AUTO_SLEEP_MODE));
- cmd->length = 1;
-
- if (priv->btmrvl_dev.psmode)
- cmd->data[0] = BT_PS_ENABLE;
- else
- cmd->data[0] = BT_PS_DISABLE;
-
- bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
-
- skb->dev = (void *) priv->btmrvl_dev.hcidev;
- skb_queue_head(&priv->adapter->tx_queue, skb);
-
- BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
+ btmrvl_enable_ps(priv);
}
if (priv->btmrvl_dev.hscmd) {
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 5b33b85790f..f36defa3776 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -535,7 +535,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
break;
default:
- BT_ERR("Unknow packet type:%d", type);
+ BT_ERR("Unknown packet type:%d", type);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload,
blksz * buf_block_len);
@@ -930,6 +930,8 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+ priv->btmrvl_dev.psmode = 1;
+ btmrvl_enable_ps(priv);
return 0;
@@ -1001,3 +1003,5 @@ MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION("Marvell BT-over-SDIO driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE("sd8688_helper.bin");
+MODULE_FIRMWARE("sd8688.bin");
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index efd689a062e..d339464dc15 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -588,11 +588,9 @@ static int btuart_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 8;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = btuart_interrupt;
- link->irq.Instance = info;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -669,21 +667,16 @@ static int btuart_config(struct pcmcia_device *link)
goto found_port;
BT_ERR("No usable port range found");
- cs_error(link, RequestIO, -ENODEV);
goto failed;
found_port:
i = pcmcia_request_irq(link, &link->irq);
- if (i != 0) {
- cs_error(link, RequestIRQ, i);
+ if (i != 0)
link->irq.AssignedIRQ = 0;
- }
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- cs_error(link, RequestConfiguration, i);
+ if (i != 0)
goto failed;
- }
if (btuart_open(info) != 0)
goto failed;
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index b881a9cd874..4f02a6f3c98 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -573,11 +573,9 @@ static int dtl1_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 8;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = dtl1_interrupt;
- link->irq.Instance = info;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -622,16 +620,12 @@ static int dtl1_config(struct pcmcia_device *link)
goto failed;
i = pcmcia_request_irq(link, &link->irq);
- if (i != 0) {
- cs_error(link, RequestIRQ, i);
+ if (i != 0)
link->irq.AssignedIRQ = 0;
- }
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- cs_error(link, RequestConfiguration, i);
+ if (i != 0)
goto failed;
- }
if (dtl1_open(info) != 0)
goto failed;
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 4895f0e0532..aa0919386b8 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -214,7 +214,7 @@ static int hci_uart_send_frame(struct sk_buff *skb)
struct hci_uart *hu;
if (!hdev) {
- BT_ERR("Frame for uknown device (hdev=NULL)");
+ BT_ERR("Frame for unknown device (hdev=NULL)");
return -ENODEV;
}
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index d5cde6d86f8..7595274103f 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -41,8 +41,6 @@
#define VERSION "1.3"
-static int minor = MISC_DYNAMIC_MINOR;
-
struct vhci_data {
struct hci_dev *hdev;
@@ -218,12 +216,6 @@ static unsigned int vhci_poll(struct file *file, poll_table *wait)
return POLLOUT | POLLWRNORM;
}
-static int vhci_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return -EINVAL;
-}
-
static int vhci_open(struct inode *inode, struct file *file)
{
struct vhci_data *data;
@@ -284,10 +276,10 @@ static int vhci_release(struct inode *inode, struct file *file)
}
static const struct file_operations vhci_fops = {
+ .owner = THIS_MODULE,
.read = vhci_read,
.write = vhci_write,
.poll = vhci_poll,
- .ioctl = vhci_ioctl,
.open = vhci_open,
.release = vhci_release,
};
@@ -302,18 +294,12 @@ static int __init vhci_init(void)
{
BT_INFO("Virtual HCI driver ver %s", VERSION);
- if (misc_register(&vhci_miscdev) < 0) {
- BT_ERR("Can't register misc device with minor %d", minor);
- return -EIO;
- }
-
- return 0;
+ return misc_register(&vhci_miscdev);
}
static void __exit vhci_exit(void)
{
- if (misc_deregister(&vhci_miscdev) < 0)
- BT_ERR("Can't unregister misc device with minor %d", minor);
+ misc_deregister(&vhci_miscdev);
}
module_init(vhci_init);
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 614da5b8613..e3749d0ba68 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -3557,67 +3557,65 @@ static ctl_table cdrom_table[] = {
.data = &cdrom_sysctl_settings.info,
.maxlen = CDROM_STR_SIZE,
.mode = 0444,
- .proc_handler = &cdrom_sysctl_info,
+ .proc_handler = cdrom_sysctl_info,
},
{
.procname = "autoclose",
.data = &cdrom_sysctl_settings.autoclose,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &cdrom_sysctl_handler,
+ .proc_handler = cdrom_sysctl_handler,
},
{
.procname = "autoeject",
.data = &cdrom_sysctl_settings.autoeject,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &cdrom_sysctl_handler,
+ .proc_handler = cdrom_sysctl_handler,
},
{
.procname = "debug",
.data = &cdrom_sysctl_settings.debug,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &cdrom_sysctl_handler,
+ .proc_handler = cdrom_sysctl_handler,
},
{
.procname = "lock",
.data = &cdrom_sysctl_settings.lock,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &cdrom_sysctl_handler,
+ .proc_handler = cdrom_sysctl_handler,
},
{
.procname = "check_media",
.data = &cdrom_sysctl_settings.check,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &cdrom_sysctl_handler
+ .proc_handler = cdrom_sysctl_handler
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table cdrom_cdrom_table[] = {
{
- .ctl_name = DEV_CDROM,
.procname = "cdrom",
.maxlen = 0,
.mode = 0555,
.child = cdrom_table,
},
- { .ctl_name = 0 }
+ { }
};
/* Make sure that /proc/sys/dev is there */
static ctl_table cdrom_root_table[] = {
{
- .ctl_name = CTL_DEV,
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = cdrom_cdrom_table,
},
- { .ctl_name = 0 }
+ { }
};
static struct ctl_table_header *cdrom_sysctl_header;
diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c
index a762283d2a2..e789e6c9a42 100644
--- a/drivers/cdrom/gdrom.c
+++ b/drivers/cdrom/gdrom.c
@@ -214,7 +214,7 @@ static void gdrom_spicommand(void *spi_string, int buflen)
gdrom_getsense(NULL);
return;
}
- outsw(PHYSADDR(GDROM_DATA_REG), cmd, 6);
+ outsw(GDROM_DATA_REG, cmd, 6);
}
@@ -298,7 +298,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, int session)
err = -EINVAL;
goto cleanup_readtoc;
}
- insw(PHYSADDR(GDROM_DATA_REG), toc, tocsize/2);
+ insw(GDROM_DATA_REG, toc, tocsize/2);
if (gd.status & 0x01)
err = -EINVAL;
@@ -449,7 +449,7 @@ static int gdrom_getsense(short *bufstring)
GDROM_DEFAULT_TIMEOUT);
if (gd.pending)
goto cleanup_sense;
- insw(PHYSADDR(GDROM_DATA_REG), &sense, sense_command->buflen/2);
+ insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
if (sense[1] & 40) {
printk(KERN_INFO "GDROM: Drive not ready - command aborted\n");
goto cleanup_sense;
@@ -586,7 +586,7 @@ static void gdrom_readdisk_dma(struct work_struct *work)
spin_unlock(&gdrom_lock);
block = blk_rq_pos(req)/GD_TO_BLK + GD_SESSION_OFFSET;
block_cnt = blk_rq_sectors(req)/GD_TO_BLK;
- ctrl_outl(PHYSADDR(req->buffer), GDROM_DMA_STARTADDR_REG);
+ ctrl_outl(virt_to_phys(req->buffer), GDROM_DMA_STARTADDR_REG);
ctrl_outl(block_cnt * GDROM_HARD_SECTOR, GDROM_DMA_LENGTH_REG);
ctrl_outl(1, GDROM_DMA_DIRECTION_REG);
ctrl_outl(1, GDROM_DMA_ENABLE_REG);
@@ -615,7 +615,7 @@ static void gdrom_readdisk_dma(struct work_struct *work)
cpu_relax();
gd.pending = 1;
gd.transfer = 1;
- outsw(PHYSADDR(GDROM_DATA_REG), &read_command->cmd, 6);
+ outsw(GDROM_DATA_REG, &read_command->cmd, 6);
timeout = jiffies + HZ / 2;
/* Wait for any pending DMA to finish */
while (ctrl_inb(GDROM_DMA_STATUS_REG) &&
diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
index ccb1fa89de2..2fb3a480f6b 100644
--- a/drivers/char/agp/Kconfig
+++ b/drivers/char/agp/Kconfig
@@ -56,9 +56,8 @@ config AGP_AMD
X on AMD Irongate, 761, and 762 chipsets.
config AGP_AMD64
- tristate "AMD Opteron/Athlon64 on-CPU GART support" if !GART_IOMMU
+ tristate "AMD Opteron/Athlon64 on-CPU GART support"
depends on AGP && X86
- default y if GART_IOMMU
help
This option gives you AGP support for the GLX component of
X using the on-CPU northbridge of the AMD Athlon64/Opteron CPUs.
diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c
index a96f3197e60..43412c03969 100644
--- a/drivers/char/agp/frontend.c
+++ b/drivers/char/agp/frontend.c
@@ -676,25 +676,25 @@ static int agp_open(struct inode *inode, struct file *file)
int minor = iminor(inode);
struct agp_file_private *priv;
struct agp_client *client;
- int rc = -ENXIO;
-
- lock_kernel();
- mutex_lock(&(agp_fe.agp_mutex));
if (minor != AGPGART_MINOR)
- goto err_out;
+ return -ENXIO;
+
+ mutex_lock(&(agp_fe.agp_mutex));
priv = kzalloc(sizeof(struct agp_file_private), GFP_KERNEL);
- if (priv == NULL)
- goto err_out_nomem;
+ if (priv == NULL) {
+ mutex_unlock(&(agp_fe.agp_mutex));
+ return -ENOMEM;
+ }
set_bit(AGP_FF_ALLOW_CLIENT, &priv->access_flags);
priv->my_pid = current->pid;
- if (capable(CAP_SYS_RAWIO)) {
+ if (capable(CAP_SYS_RAWIO))
/* Root priv, can be controller */
set_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags);
- }
+
client = agp_find_client_by_pid(current->pid);
if (client != NULL) {
@@ -704,16 +704,10 @@ static int agp_open(struct inode *inode, struct file *file)
file->private_data = (void *) priv;
agp_insert_file_private(priv);
DBG("private=%p, client=%p", priv, client);
- mutex_unlock(&(agp_fe.agp_mutex));
- unlock_kernel();
- return 0;
-err_out_nomem:
- rc = -ENOMEM;
-err_out:
mutex_unlock(&(agp_fe.agp_mutex));
- unlock_kernel();
- return rc;
+
+ return 0;
}
diff --git a/drivers/char/cs5535_gpio.c b/drivers/char/cs5535_gpio.c
index 04ba906b488..4d830dc482e 100644
--- a/drivers/char/cs5535_gpio.c
+++ b/drivers/char/cs5535_gpio.c
@@ -17,7 +17,7 @@
#include <linux/cdev.h>
#include <linux/ioport.h>
#include <linux/pci.h>
-#include <linux/smp_lock.h>
+
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -158,7 +158,6 @@ static int cs5535_gpio_open(struct inode *inode, struct file *file)
{
u32 m = iminor(inode);
- cycle_kernel_lock();
/* the mask says which pins are usable by this driver */
if ((mask & (1 << m)) == 0)
return -EINVAL;
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index 34d15d54823..26a47dc88f6 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -27,8 +27,6 @@
* - Add module support
*/
-
-#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
@@ -174,13 +172,12 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
case RTC_RD_TIME:
- lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
status = efi.get_time(&eft, &cap);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
- unlock_kernel();
+
if (status != EFI_SUCCESS) {
/* should never happen */
printk(KERN_ERR "efitime: can't read time\n");
@@ -202,13 +199,11 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
convert_to_efi_time(&wtime, &eft);
- lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
status = efi.set_time(&eft);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
- unlock_kernel();
return status == EFI_SUCCESS ? 0 : -EINVAL;
@@ -224,7 +219,6 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
convert_to_efi_time(&wtime, &eft);
- lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
/*
* XXX Fixme:
@@ -235,19 +229,16 @@ static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
status = efi.set_wakeup_time((efi_bool_t)enabled, &eft);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
- unlock_kernel();
return status == EFI_SUCCESS ? 0 : -EINVAL;
case RTC_WKALM_RD:
- lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
status = efi.get_wakeup_time((efi_bool_t *)&enabled, (efi_bool_t *)&pending, &eft);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
- unlock_kernel();
if (status != EFI_SUCCESS) return -EINVAL;
@@ -277,7 +268,6 @@ static int efi_rtc_open(struct inode *inode, struct file *file)
* We do accept multiple open files at the same time as we
* synchronize on the per call operation.
*/
- cycle_kernel_lock();
return 0;
}
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
index ef31738c2cb..fda4181b5e6 100644
--- a/drivers/char/generic_nvram.c
+++ b/drivers/char/generic_nvram.c
@@ -19,7 +19,6 @@
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/nvram.h>
#ifdef CONFIG_PPC_PMAC
@@ -32,7 +31,6 @@ static ssize_t nvram_len;
static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
{
- lock_kernel();
switch (origin) {
case 1:
offset += file->f_pos;
@@ -41,12 +39,11 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
offset += nvram_len;
break;
}
- if (offset < 0) {
- unlock_kernel();
+ if (offset < 0)
return -EINVAL;
- }
+
file->f_pos = offset;
- unlock_kernel();
+
return file->f_pos;
}
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 70a770ac013..e481c5938ba 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -675,36 +675,33 @@ static int hpet_is_known(struct hpet_data *hdp)
static ctl_table hpet_table[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "max-user-freq",
.data = &hpet_max_freq,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
- {.ctl_name = 0}
+ {}
};
static ctl_table hpet_root[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "hpet",
.maxlen = 0,
.mode = 0555,
.child = hpet_table,
},
- {.ctl_name = 0}
+ {}
};
static ctl_table dev_root[] = {
{
- .ctl_name = CTL_DEV,
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = hpet_root,
},
- {.ctl_name = 0}
+ {}
};
static struct ctl_table_header *sysctl_header;
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 1573aebd54b..e989f67bb61 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -52,7 +52,9 @@
static struct hwrng *current_rng;
static LIST_HEAD(rng_list);
static DEFINE_MUTEX(rng_mutex);
-
+static int data_avail;
+static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES]
+ __cacheline_aligned;
static inline int hwrng_init(struct hwrng *rng)
{
@@ -67,19 +69,6 @@ static inline void hwrng_cleanup(struct hwrng *rng)
rng->cleanup(rng);
}
-static inline int hwrng_data_present(struct hwrng *rng, int wait)
-{
- if (!rng->data_present)
- return 1;
- return rng->data_present(rng, wait);
-}
-
-static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
-{
- return rng->data_read(rng, data);
-}
-
-
static int rng_dev_open(struct inode *inode, struct file *filp)
{
/* enforce read-only access to this chrdev */
@@ -87,58 +76,90 @@ static int rng_dev_open(struct inode *inode, struct file *filp)
return -EINVAL;
if (filp->f_mode & FMODE_WRITE)
return -EINVAL;
- cycle_kernel_lock();
+ return 0;
+}
+
+static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
+ int wait) {
+ int present;
+
+ if (rng->read)
+ return rng->read(rng, (void *)buffer, size, wait);
+
+ if (rng->data_present)
+ present = rng->data_present(rng, wait);
+ else
+ present = 1;
+
+ if (present)
+ return rng->data_read(rng, (u32 *)buffer);
+
return 0;
}
static ssize_t rng_dev_read(struct file *filp, char __user *buf,
size_t size, loff_t *offp)
{
- u32 data;
ssize_t ret = 0;
int err = 0;
- int bytes_read;
+ int bytes_read, len;
while (size) {
- err = -ERESTARTSYS;
- if (mutex_lock_interruptible(&rng_mutex))
+ if (mutex_lock_interruptible(&rng_mutex)) {
+ err = -ERESTARTSYS;
goto out;
+ }
+
if (!current_rng) {
- mutex_unlock(&rng_mutex);
err = -ENODEV;
- goto out;
+ goto out_unlock;
}
- bytes_read = 0;
- if (hwrng_data_present(current_rng,
- !(filp->f_flags & O_NONBLOCK)))
- bytes_read = hwrng_data_read(current_rng, &data);
- mutex_unlock(&rng_mutex);
-
- err = -EAGAIN;
- if (!bytes_read && (filp->f_flags & O_NONBLOCK))
- goto out;
- if (bytes_read < 0) {
- err = bytes_read;
- goto out;
+ if (!data_avail) {
+ bytes_read = rng_get_data(current_rng, rng_buffer,
+ sizeof(rng_buffer),
+ !(filp->f_flags & O_NONBLOCK));
+ if (bytes_read < 0) {
+ err = bytes_read;
+ goto out_unlock;
+ }
+ data_avail = bytes_read;
}
- err = -EFAULT;
- while (bytes_read && size) {
- if (put_user((u8)data, buf++))
- goto out;
- size--;
- ret++;
- bytes_read--;
- data >>= 8;
+ if (!data_avail) {
+ if (filp->f_flags & O_NONBLOCK) {
+ err = -EAGAIN;
+ goto out_unlock;
+ }
+ } else {
+ len = data_avail;
+ if (len > size)
+ len = size;
+
+ data_avail -= len;
+
+ if (copy_to_user(buf + ret, rng_buffer + data_avail,
+ len)) {
+ err = -EFAULT;
+ goto out_unlock;
+ }
+
+ size -= len;
+ ret += len;
}
+ mutex_unlock(&rng_mutex);
+
if (need_resched())
schedule_timeout_interruptible(1);
- err = -ERESTARTSYS;
- if (signal_pending(current))
+
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
goto out;
+ }
}
+out_unlock:
+ mutex_unlock(&rng_mutex);
out:
return ret ? : err;
}
@@ -280,7 +301,7 @@ int hwrng_register(struct hwrng *rng)
struct hwrng *old_rng, *tmp;
if (rng->name == NULL ||
- rng->data_read == NULL)
+ (rng->data_read == NULL && rng->read == NULL))
goto out;
mutex_lock(&rng_mutex);
diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c
index 915157fcff9..bdaef8e9402 100644
--- a/drivers/char/hw_random/virtio-rng.c
+++ b/drivers/char/hw_random/virtio-rng.c
@@ -16,6 +16,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/scatterlist.h>
@@ -23,78 +24,64 @@
#include <linux/virtio.h>
#include <linux/virtio_rng.h>
-/* The host will fill any buffer we give it with sweet, sweet randomness. We
- * give it 64 bytes at a time, and the hwrng framework takes it 4 bytes at a
- * time. */
-#define RANDOM_DATA_SIZE 64
-
static struct virtqueue *vq;
-static u32 *random_data;
-static unsigned int data_left;
+static unsigned int data_avail;
static DECLARE_COMPLETION(have_data);
+static bool busy;
static void random_recv_done(struct virtqueue *vq)
{
- unsigned int len;
-
/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
- if (!vq->vq_ops->get_buf(vq, &len))
+ if (!vq->vq_ops->get_buf(vq, &data_avail))
return;
- data_left += len;
complete(&have_data);
}
-static void register_buffer(void)
+/* The host will fill any buffer we give it with sweet, sweet randomness. */
+static void register_buffer(u8 *buf, size_t size)
{
struct scatterlist sg;
- sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left);
+ sg_init_one(&sg, buf, size);
+
/* There should always be room for one buffer. */
- if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) < 0)
+ if (vq->vq_ops->add_buf(vq, &sg, 0, 1, buf) < 0)
BUG();
+
vq->vq_ops->kick(vq);
}
-/* At least we don't udelay() in a loop like some other drivers. */
-static int virtio_data_present(struct hwrng *rng, int wait)
+static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{
- if (data_left >= sizeof(u32))
- return 1;
-again:
+ if (!busy) {
+ busy = true;
+ init_completion(&have_data);
+ register_buffer(buf, size);
+ }
+
if (!wait)
return 0;
wait_for_completion(&have_data);
- /* Not enough? Re-register. */
- if (unlikely(data_left < sizeof(u32))) {
- register_buffer();
- goto again;
- }
+ busy = false;
- return 1;
+ return data_avail;
}
-/* virtio_data_present() must have succeeded before this is called. */
-static int virtio_data_read(struct hwrng *rng, u32 *data)
+static void virtio_cleanup(struct hwrng *rng)
{
- BUG_ON(data_left < sizeof(u32));
- data_left -= sizeof(u32);
- *data = random_data[data_left / 4];
-
- if (data_left < sizeof(u32)) {
- init_completion(&have_data);
- register_buffer();
- }
- return sizeof(*data);
+ if (busy)
+ wait_for_completion(&have_data);
}
+
static struct hwrng virtio_hwrng = {
- .name = "virtio",
- .data_present = virtio_data_present,
- .data_read = virtio_data_read,
+ .name = "virtio",
+ .cleanup = virtio_cleanup,
+ .read = virtio_read,
};
static int virtrng_probe(struct virtio_device *vdev)
@@ -112,7 +99,6 @@ static int virtrng_probe(struct virtio_device *vdev)
return err;
}
- register_buffer();
return 0;
}
@@ -138,21 +124,11 @@ static struct virtio_driver virtio_rng = {
static int __init init(void)
{
- int err;
-
- random_data = kmalloc(RANDOM_DATA_SIZE, GFP_KERNEL);
- if (!random_data)
- return -ENOMEM;
-
- err = register_virtio_driver(&virtio_rng);
- if (err)
- kfree(random_data);
- return err;
+ return register_virtio_driver(&virtio_rng);
}
static void __exit fini(void)
{
- kfree(random_data);
unregister_virtio_driver(&virtio_rng);
}
module_init(init);
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 2e66b5f773d..0dec5da000e 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -660,26 +660,23 @@ static struct ipmi_smi_watcher smi_watcher = {
#include <linux/sysctl.h>
static ctl_table ipmi_table[] = {
- { .ctl_name = DEV_IPMI_POWEROFF_POWERCYCLE,
- .procname = "poweroff_powercycle",
+ { .procname = "poweroff_powercycle",
.data = &poweroff_powercycle,
.maxlen = sizeof(poweroff_powercycle),
.mode = 0644,
- .proc_handler = &proc_dointvec },
+ .proc_handler = proc_dointvec },
{ }
};
static ctl_table ipmi_dir_table[] = {
- { .ctl_name = DEV_IPMI,
- .procname = "ipmi",
+ { .procname = "ipmi",
.mode = 0555,
.child = ipmi_table },
{ }
};
static ctl_table ipmi_root_table[] = {
- { .ctl_name = CTL_DEV,
- .procname = "dev",
+ { .procname = "dev",
.mode = 0555,
.child = ipmi_dir_table },
{ }
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 950837cf9e9..5619007e7e0 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -46,8 +46,6 @@
extern void ctrl_alt_del(void);
-#define to_handle_h(n) container_of(n, struct input_handle, h_node)
-
/*
* Exported functions/variables
*/
@@ -132,6 +130,7 @@ int shift_state = 0;
*/
static struct input_handler kbd_handler;
+static DEFINE_SPINLOCK(kbd_event_lock);
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
static int dead_key_next;
@@ -190,78 +189,85 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
* etc.). So this means that scancodes for the extra function keys won't
* be valid for the first event device, but will be for the second.
*/
+
+struct getset_keycode_data {
+ unsigned int scancode;
+ unsigned int keycode;
+ int error;
+};
+
+static int getkeycode_helper(struct input_handle *handle, void *data)
+{
+ struct getset_keycode_data *d = data;
+
+ d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode);
+
+ return d->error == 0; /* stop as soon as we successfully get one */
+}
+
int getkeycode(unsigned int scancode)
{
- struct input_handle *handle;
- int keycode;
- int error = -ENODEV;
+ struct getset_keycode_data d = { scancode, 0, -ENODEV };
- list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
- error = input_get_keycode(handle->dev, scancode, &keycode);
- if (!error)
- return keycode;
- }
+ input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
- return error;
+ return d.error ?: d.keycode;
+}
+
+static int setkeycode_helper(struct input_handle *handle, void *data)
+{
+ struct getset_keycode_data *d = data;
+
+ d->error = input_set_keycode(handle->dev, d->scancode, d->keycode);
+
+ return d->error == 0; /* stop as soon as we successfully set one */
}
int setkeycode(unsigned int scancode, unsigned int keycode)
{
- struct input_handle *handle;
- int error = -ENODEV;
+ struct getset_keycode_data d = { scancode, keycode, -ENODEV };
- list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
- error = input_set_keycode(handle->dev, scancode, keycode);
- if (!error)
- break;
- }
+ input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
- return error;
+ return d.error;
}
/*
* Making beeps and bells.
*/
-static void kd_nosound(unsigned long ignored)
+
+static int kd_sound_helper(struct input_handle *handle, void *data)
{
- struct input_handle *handle;
+ unsigned int *hz = data;
+ struct input_dev *dev = handle->dev;
- list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
- if (test_bit(EV_SND, handle->dev->evbit)) {
- if (test_bit(SND_TONE, handle->dev->sndbit))
- input_inject_event(handle, EV_SND, SND_TONE, 0);
- if (test_bit(SND_BELL, handle->dev->sndbit))
- input_inject_event(handle, EV_SND, SND_BELL, 0);
- }
+ if (test_bit(EV_SND, dev->evbit)) {
+ if (test_bit(SND_TONE, dev->sndbit))
+ input_inject_event(handle, EV_SND, SND_TONE, *hz);
+ if (test_bit(SND_BELL, handle->dev->sndbit))
+ input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0);
}
+
+ return 0;
+}
+
+static void kd_nosound(unsigned long ignored)
+{
+ static unsigned int zero;
+
+ input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
}
static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0);
void kd_mksound(unsigned int hz, unsigned int ticks)
{
- struct list_head *node;
+ del_timer_sync(&kd_mksound_timer);
- del_timer(&kd_mksound_timer);
+ input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper);
- if (hz) {
- list_for_each_prev(node, &kbd_handler.h_list) {
- struct input_handle *handle = to_handle_h(node);
- if (test_bit(EV_SND, handle->dev->evbit)) {
- if (test_bit(SND_TONE, handle->dev->sndbit)) {
- input_inject_event(handle, EV_SND, SND_TONE, hz);
- break;
- }
- if (test_bit(SND_BELL, handle->dev->sndbit)) {
- input_inject_event(handle, EV_SND, SND_BELL, 1);
- break;
- }
- }
- }
- if (ticks)
- mod_timer(&kd_mksound_timer, jiffies + ticks);
- } else
- kd_nosound(0);
+ if (hz && ticks)
+ mod_timer(&kd_mksound_timer, jiffies + ticks);
}
EXPORT_SYMBOL(kd_mksound);
@@ -269,27 +275,34 @@ EXPORT_SYMBOL(kd_mksound);
* Setting the keyboard rate.
*/
-int kbd_rate(struct kbd_repeat *rep)
+static int kbd_rate_helper(struct input_handle *handle, void *data)
{
- struct list_head *node;
- unsigned int d = 0;
- unsigned int p = 0;
-
- list_for_each(node, &kbd_handler.h_list) {
- struct input_handle *handle = to_handle_h(node);
- struct input_dev *dev = handle->dev;
-
- if (test_bit(EV_REP, dev->evbit)) {
- if (rep->delay > 0)
- input_inject_event(handle, EV_REP, REP_DELAY, rep->delay);
- if (rep->period > 0)
- input_inject_event(handle, EV_REP, REP_PERIOD, rep->period);
- d = dev->rep[REP_DELAY];
- p = dev->rep[REP_PERIOD];
- }
+ struct input_dev *dev = handle->dev;
+ struct kbd_repeat *rep = data;
+
+ if (test_bit(EV_REP, dev->evbit)) {
+
+ if (rep[0].delay > 0)
+ input_inject_event(handle,
+ EV_REP, REP_DELAY, rep[0].delay);
+ if (rep[0].period > 0)
+ input_inject_event(handle,
+ EV_REP, REP_PERIOD, rep[0].period);
+
+ rep[1].delay = dev->rep[REP_DELAY];
+ rep[1].period = dev->rep[REP_PERIOD];
}
- rep->delay = d;
- rep->period = p;
+
+ return 0;
+}
+
+int kbd_rate(struct kbd_repeat *rep)
+{
+ struct kbd_repeat data[2] = { *rep };
+
+ input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper);
+ *rep = data[1]; /* Copy currently used settings */
+
return 0;
}
@@ -997,36 +1010,36 @@ static inline unsigned char getleds(void)
return leds;
}
+static int kbd_update_leds_helper(struct input_handle *handle, void *data)
+{
+ unsigned char leds = *(unsigned char *)data;
+
+ if (test_bit(EV_LED, handle->dev->evbit)) {
+ input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
+ input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
+ input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
+ input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
+ }
+
+ return 0;
+}
+
/*
- * This routine is the bottom half of the keyboard interrupt
- * routine, and runs with all interrupts enabled. It does
- * console changing, led setting and copy_to_cooked, which can
- * take a reasonably long time.
- *
- * Aside from timing (which isn't really that important for
- * keyboard interrupts as they happen often), using the software
- * interrupt routines for this thing allows us to easily mask
- * this when we don't want any of the above to happen.
- * This allows for easy and efficient race-condition prevention
- * for kbd_start => input_inject_event(dev, EV_LED, ...) => ...
+ * This is the tasklet that updates LED state on all keyboards
+ * attached to the box. The reason we use tasklet is that we
+ * need to handle the scenario when keyboard handler is not
+ * registered yet but we already getting updates form VT to
+ * update led state.
*/
-
static void kbd_bh(unsigned long dummy)
{
- struct list_head *node;
unsigned char leds = getleds();
if (leds != ledstate) {
- list_for_each(node, &kbd_handler.h_list) {
- struct input_handle *handle = to_handle_h(node);
- input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
- input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
- input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
- input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
- }
+ input_handler_for_each_handle(&kbd_handler, &leds,
+ kbd_update_leds_helper);
+ ledstate = leds;
}
-
- ledstate = leds;
}
DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
@@ -1136,7 +1149,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char u
static void kbd_rawcode(unsigned char data)
{
struct vc_data *vc = vc_cons[fg_console].d;
- kbd = kbd_table + fg_console;
+ kbd = kbd_table + vc->vc_num;
if (kbd->kbdmode == VC_RAW)
put_queue(vc, data);
}
@@ -1157,7 +1170,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
tty->driver_data = vc;
}
- kbd = kbd_table + fg_console;
+ kbd = kbd_table + vc->vc_num;
if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT)
sysrq_alt = down ? keycode : 0;
@@ -1296,10 +1309,16 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
static void kbd_event(struct input_handle *handle, unsigned int event_type,
unsigned int event_code, int value)
{
+ /* We are called with interrupts disabled, just take the lock */
+ spin_lock(&kbd_event_lock);
+
if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))
kbd_rawcode(value);
if (event_type == EV_KEY)
kbd_keycode(event_code, value, HW_RAW(handle->dev));
+
+ spin_unlock(&kbd_event_lock);
+
tasklet_schedule(&keyboard_tasklet);
do_poke_blanked_console = 1;
schedule_console_callback();
@@ -1363,15 +1382,11 @@ static void kbd_disconnect(struct input_handle *handle)
*/
static void kbd_start(struct input_handle *handle)
{
- unsigned char leds = ledstate;
-
tasklet_disable(&keyboard_tasklet);
- if (leds != 0xff) {
- input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
- input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
- input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
- input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
- }
+
+ if (ledstate != 0xff)
+ kbd_update_leds_helper(handle, &ledstate);
+
tasklet_enable(&keyboard_tasklet);
}
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index a074fceb67d..30eff80fed6 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -5,7 +5,7 @@
*
* Added devfs support.
* Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
- * Shared /dev/zero mmaping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
+ * Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
*/
#include <linux/mm.h>
@@ -26,7 +26,6 @@
#include <linux/bootmem.h>
#include <linux/splice.h>
#include <linux/pfn.h>
-#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -892,29 +891,23 @@ static int memory_open(struct inode *inode, struct file *filp)
{
int minor;
const struct memdev *dev;
- int ret = -ENXIO;
-
- lock_kernel();
minor = iminor(inode);
if (minor >= ARRAY_SIZE(devlist))
- goto out;
+ return -ENXIO;
dev = &devlist[minor];
if (!dev->fops)
- goto out;
+ return -ENXIO;
filp->f_op = dev->fops;
if (dev->dev_info)
filp->f_mapping->backing_dev_info = dev->dev_info;
if (dev->fops->open)
- ret = dev->fops->open(inode, filp);
- else
- ret = 0;
-out:
- unlock_kernel();
- return ret;
+ return dev->fops->open(inode, filp);
+
+ return 0;
}
static const struct file_operations memory_fops = {
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 07fa612a58d..96f1cd086dd 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -49,7 +49,6 @@
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
-#include <linux/smp_lock.h>
/*
* Head entry for the doubly linked miscdevice list
@@ -118,8 +117,7 @@ static int misc_open(struct inode * inode, struct file * file)
struct miscdevice *c;
int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
-
- lock_kernel();
+
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) {
@@ -157,7 +155,6 @@ static int misc_open(struct inode * inode, struct file * file)
fops_put(old_fops);
fail:
mutex_unlock(&misc_mtx);
- unlock_kernel();
return err;
}
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index 1997270bb6f..ecb89d798e3 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -248,7 +248,7 @@ static const struct vm_operations_struct mspec_vm_ops = {
/*
* mspec_mmap
*
- * Called when mmaping the device. Initializes the vma with a fault handler
+ * Called when mmapping the device. Initializes the vma with a fault handler
* and private data structure necessary to allocate, track, and free the
* underlying pages.
*/
diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c
index 6934025a1ac..c1d8b54c816 100644
--- a/drivers/char/n_r3964.c
+++ b/drivers/char/n_r3964.c
@@ -602,7 +602,7 @@ static void receive_char(struct r3964_info *pInfo, const unsigned char c)
}
break;
case R3964_WAIT_FOR_RX_REPEAT:
- /* FALLTROUGH */
+ /* FALLTHROUGH */
case R3964_IDLE:
if (c == STX) {
/* Prevent rx_queue from overflow: */
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 88cee4099be..4008e2ce73c 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -38,7 +38,6 @@
#define NVRAM_VERSION "1.3"
#include <linux/module.h>
-#include <linux/smp_lock.h>
#include <linux/nvram.h>
#define PC 1
@@ -111,6 +110,7 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/uaccess.h>
+#include <linux/smp_lock.h>
#include <asm/system.h>
@@ -214,7 +214,6 @@ void nvram_set_checksum(void)
static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
{
- lock_kernel();
switch (origin) {
case 0:
/* nothing to do */
@@ -226,7 +225,7 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
offset += NVRAM_BYTES;
break;
}
- unlock_kernel();
+
return (offset >= 0) ? (file->f_pos = offset) : -EINVAL;
}
diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c
index 3f7da8cf3a8..8ecbcc174c1 100644
--- a/drivers/char/pc8736x_gpio.c
+++ b/drivers/char/pc8736x_gpio.c
@@ -20,7 +20,6 @@
#include <linux/mutex.h>
#include <linux/nsc_gpio.h>
#include <linux/platform_device.h>
-#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#define DEVNAME "pc8736x_gpio"
@@ -223,7 +222,6 @@ static int pc8736x_gpio_open(struct inode *inode, struct file *file)
unsigned m = iminor(inode);
file->private_data = &pc8736x_gpio_ops;
- cycle_kernel_lock();
dev_dbg(&pdev->dev, "open %d\n", m);
if (m >= PC8736X_GPIO_CT)
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
index c250a31efa5..2db4c0a29b0 100644
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -23,8 +23,6 @@
* All rights reserved. Licensed under dual BSD/GPL license.
*/
-/* #define PCMCIA_DEBUG 6 */
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -47,18 +45,17 @@
/* #define ATR_CSUM */
-#ifdef PCMCIA_DEBUG
-#define reader_to_dev(x) (&handle_to_dev(x->p_dev))
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0600);
-#define DEBUGP(n, rdr, x, args...) do { \
- if (pc_debug >= (n)) \
- dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \
- __func__ , ## args); \
+#define reader_to_dev(x) (&x->p_dev->dev)
+
+/* n (debug level) is ignored */
+/* additional debug output may be enabled by re-compiling with
+ * CM4000_DEBUG set */
+/* #define CM4000_DEBUG */
+#define DEBUGP(n, rdr, x, args...) do { \
+ dev_dbg(reader_to_dev(rdr), "%s:" x, \
+ __func__ , ## args); \
} while (0)
-#else
-#define DEBUGP(n, rdr, x, args...)
-#endif
+
static char *version = "cm4000_cs.c v2.4.0gm6 - All bugs added by Harald Welte";
#define T_1SEC (HZ)
@@ -174,14 +171,13 @@ static unsigned char fi_di_table[10][14] = {
/* 9 */ {0x09,0x19,0x29,0x39,0x49,0x59,0x69,0x11,0x11,0x99,0xA9,0xB9,0xC9,0xD9}
};
-#ifndef PCMCIA_DEBUG
+#ifndef CM4000_DEBUG
#define xoutb outb
#define xinb inb
#else
static inline void xoutb(unsigned char val, unsigned short port)
{
- if (pc_debug >= 7)
- printk(KERN_DEBUG "outb(val=%.2x,port=%.4x)\n", val, port);
+ pr_debug("outb(val=%.2x,port=%.4x)\n", val, port);
outb(val, port);
}
static inline unsigned char xinb(unsigned short port)
@@ -189,8 +185,7 @@ static inline unsigned char xinb(unsigned short port)
unsigned char val;
val = inb(port);
- if (pc_debug >= 7)
- printk(KERN_DEBUG "%.2x=inb(%.4x)\n", val, port);
+ pr_debug("%.2x=inb(%.4x)\n", val, port);
return val;
}
@@ -514,12 +509,10 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq)
for (i = 0; i < 4; i++) {
xoutb(i, REG_BUF_ADDR(iobase));
xoutb(dev->pts[i], REG_BUF_DATA(iobase)); /* buf data */
-#ifdef PCMCIA_DEBUG
- if (pc_debug >= 5)
- printk("0x%.2x ", dev->pts[i]);
+#ifdef CM4000_DEBUG
+ pr_debug("0x%.2x ", dev->pts[i]);
}
- if (pc_debug >= 5)
- printk("\n");
+ pr_debug("\n");
#else
}
#endif
@@ -579,14 +572,13 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq)
pts_reply[i] = inb(REG_BUF_DATA(iobase));
}
-#ifdef PCMCIA_DEBUG
+#ifdef CM4000_DEBUG
DEBUGP(2, dev, "PTSreply: ");
for (i = 0; i < num_bytes_read; i++) {
- if (pc_debug >= 5)
- printk("0x%.2x ", pts_reply[i]);
+ pr_debug("0x%.2x ", pts_reply[i]);
}
- printk("\n");
-#endif /* PCMCIA_DEBUG */
+ pr_debug("\n");
+#endif /* CM4000_DEBUG */
DEBUGP(5, dev, "Clear Tactive in Flags1\n");
xoutb(0x20, REG_FLAGS1(iobase));
@@ -655,7 +647,7 @@ static void terminate_monitor(struct cm4000_dev *dev)
DEBUGP(5, dev, "Delete timer\n");
del_timer_sync(&dev->timer);
-#ifdef PCMCIA_DEBUG
+#ifdef CM4000_DEBUG
dev->monitor_running = 0;
#endif
@@ -898,7 +890,7 @@ static void monitor_card(unsigned long p)
DEBUGP(4, dev, "ATR checksum (0x%.2x, should "
"be zero) failed\n", dev->atr_csum);
}
-#ifdef PCMCIA_DEBUG
+#ifdef CM4000_DEBUG
else if (test_bit(IS_BAD_LENGTH, &dev->flags)) {
DEBUGP(4, dev, "ATR length error\n");
} else {
@@ -1415,7 +1407,7 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
int size;
int rc;
void __user *argp = (void __user *)arg;
-#ifdef PCMCIA_DEBUG
+#ifdef CM4000_DEBUG
char *ioctl_names[CM_IOC_MAXNR + 1] = {
[_IOC_NR(CM_IOCGSTATUS)] "CM_IOCGSTATUS",
[_IOC_NR(CM_IOCGATR)] "CM_IOCGATR",
@@ -1423,9 +1415,9 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
[_IOC_NR(CM_IOCSPTS)] "CM_IOCSPTS",
[_IOC_NR(CM_IOSDBGLVL)] "CM4000_DBGLVL",
};
-#endif
DEBUGP(3, dev, "cmm_ioctl(device=%d.%d) %s\n", imajor(inode),
iminor(inode), ioctl_names[_IOC_NR(cmd)]);
+#endif
lock_kernel();
rc = -ENODEV;
@@ -1523,7 +1515,7 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
case CM_IOCARDOFF:
-#ifdef PCMCIA_DEBUG
+#ifdef CM4000_DEBUG
DEBUGP(4, dev, "... in CM_IOCARDOFF\n");
if (dev->flags0 & 0x01) {
DEBUGP(4, dev, " Card inserted\n");
@@ -1625,18 +1617,9 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
break;
-#ifdef PCMCIA_DEBUG
- case CM_IOSDBGLVL: /* set debug log level */
- {
- int old_pc_debug = 0;
-
- old_pc_debug = pc_debug;
- if (copy_from_user(&pc_debug, argp, sizeof(int)))
- rc = -EFAULT;
- else if (old_pc_debug != pc_debug)
- DEBUGP(0, dev, "Changed debug log level "
- "to %i\n", pc_debug);
- }
+#ifdef CM4000_DEBUG
+ case CM_IOSDBGLVL:
+ rc = -ENOTTY;
break;
#endif
default:
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
index 4f0723b0797..a6a70e476be 100644
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -17,8 +17,6 @@
* All rights reserved, Dual BSD/GPL Licensed.
*/
-/* #define PCMCIA_DEBUG 6 */
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -41,18 +39,16 @@
#include "cm4040_cs.h"
-#ifdef PCMCIA_DEBUG
-#define reader_to_dev(x) (&handle_to_dev(x->p_dev))
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0600);
-#define DEBUGP(n, rdr, x, args...) do { \
- if (pc_debug >= (n)) \
- dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \
- __func__ , ##args); \
+#define reader_to_dev(x) (&x->p_dev->dev)
+
+/* n (debug level) is ignored */
+/* additional debug output may be enabled by re-compiling with
+ * CM4040_DEBUG set */
+/* #define CM4040_DEBUG */
+#define DEBUGP(n, rdr, x, args...) do { \
+ dev_dbg(reader_to_dev(rdr), "%s:" x, \
+ __func__ , ## args); \
} while (0)
-#else
-#define DEBUGP(n, rdr, x, args...)
-#endif
static char *version =
"OMNIKEY CardMan 4040 v1.1.0gm5 - All bugs added by Harald Welte";
@@ -90,14 +86,13 @@ struct reader_dev {
static struct pcmcia_device *dev_table[CM_MAX_DEV];
-#ifndef PCMCIA_DEBUG
+#ifndef CM4040_DEBUG
#define xoutb outb
#define xinb inb
#else
static inline void xoutb(unsigned char val, unsigned short port)
{
- if (pc_debug >= 7)
- printk(KERN_DEBUG "outb(val=%.2x,port=%.4x)\n", val, port);
+ pr_debug("outb(val=%.2x,port=%.4x)\n", val, port);
outb(val, port);
}
@@ -106,8 +101,7 @@ static inline unsigned char xinb(unsigned short port)
unsigned char val;
val = inb(port);
- if (pc_debug >= 7)
- printk(KERN_DEBUG "%.2x=inb(%.4x)\n", val, port);
+ pr_debug("%.2x=inb(%.4x)\n", val, port);
return val;
}
#endif
@@ -260,23 +254,22 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf,
return -EIO;
}
dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN);
-#ifdef PCMCIA_DEBUG
- if (pc_debug >= 6)
- printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]);
+#ifdef CM4040_DEBUG
+ pr_debug("%lu:%2x ", i, dev->r_buf[i]);
}
- printk("\n");
+ pr_debug("\n");
#else
}
#endif
bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]);
- DEBUGP(6, dev, "BytesToRead=%lu\n", bytes_to_read);
+ DEBUGP(6, dev, "BytesToRead=%zu\n", bytes_to_read);
min_bytes_to_read = min(count, bytes_to_read + 5);
min_bytes_to_read = min_t(size_t, min_bytes_to_read, READ_WRITE_BUFFER_SIZE);
- DEBUGP(6, dev, "Min=%lu\n", min_bytes_to_read);
+ DEBUGP(6, dev, "Min=%zu\n", min_bytes_to_read);
for (i = 0; i < (min_bytes_to_read-5); i++) {
rc = wait_for_bulk_in_ready(dev);
@@ -288,11 +281,10 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf,
return -EIO;
}
dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN);
-#ifdef PCMCIA_DEBUG
- if (pc_debug >= 6)
- printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]);
+#ifdef CM4040_DEBUG
+ pr_debug("%lu:%2x ", i, dev->r_buf[i]);
}
- printk("\n");
+ pr_debug("\n");
#else
}
#endif
@@ -547,7 +539,7 @@ static int cm4040_config_check(struct pcmcia_device *p_dev,
p_dev->io.IOAddrLines = cfg->io.flags & CISTPL_IO_LINES_MASK;
rc = pcmcia_request_io(p_dev, &p_dev->io);
- dev_printk(KERN_INFO, &handle_to_dev(p_dev),
+ dev_printk(KERN_INFO, &p_dev->dev,
"pcmcia_request_io returned 0x%x\n", rc);
return rc;
}
@@ -569,7 +561,7 @@ static int reader_config(struct pcmcia_device *link, int devno)
fail_rc = pcmcia_request_configuration(link, &link->conf);
if (fail_rc != 0) {
- dev_printk(KERN_INFO, &handle_to_dev(link),
+ dev_printk(KERN_INFO, &link->dev,
"pcmcia_request_configuration failed 0x%x\n",
fail_rc);
goto cs_release;
diff --git a/drivers/char/pcmcia/ipwireless/hardware.c b/drivers/char/pcmcia/ipwireless/hardware.c
index 4c1820cad71..99cffdab105 100644
--- a/drivers/char/pcmcia/ipwireless/hardware.c
+++ b/drivers/char/pcmcia/ipwireless/hardware.c
@@ -1213,12 +1213,12 @@ static irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq,
irqreturn_t ipwireless_interrupt(int irq, void *dev_id)
{
- struct ipw_hardware *hw = dev_id;
+ struct ipw_dev *ipw = dev_id;
- if (hw->hw_version == HW_VERSION_1)
- return ipwireless_handle_v1_interrupt(irq, hw);
+ if (ipw->hardware->hw_version == HW_VERSION_1)
+ return ipwireless_handle_v1_interrupt(irq, ipw->hardware);
else
- return ipwireless_handle_v2_v3_interrupt(irq, hw);
+ return ipwireless_handle_v2_v3_interrupt(irq, ipw->hardware);
}
static void flush_packets_to_hw(struct ipw_hardware *hw)
diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c
index 5216fce0c62..dff24dae148 100644
--- a/drivers/char/pcmcia/ipwireless/main.c
+++ b/drivers/char/pcmcia/ipwireless/main.c
@@ -65,10 +65,7 @@ static void signalled_reboot_work(struct work_struct *work_reboot)
struct ipw_dev *ipw = container_of(work_reboot, struct ipw_dev,
work_reboot);
struct pcmcia_device *link = ipw->link;
- int ret = pcmcia_reset_card(link->socket);
-
- if (ret != 0)
- cs_error(link, ResetCard, ret);
+ pcmcia_reset_card(link->socket);
}
static void signalled_reboot_callback(void *callback_data)
@@ -79,208 +76,127 @@ static void signalled_reboot_callback(void *callback_data)
schedule_work(&ipw->work_reboot);
}
-static int config_ipwireless(struct ipw_dev *ipw)
+static int ipwireless_probe(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- struct pcmcia_device *link = ipw->link;
- int ret;
- tuple_t tuple;
- unsigned short buf[64];
- cisparse_t parse;
- unsigned short cor_value;
+ struct ipw_dev *ipw = priv_data;
+ struct resource *io_resource;
memreq_t memreq_attr_memory;
memreq_t memreq_common_memory;
+ int ret;
- ipw->is_v2_card = 0;
-
- tuple.Attributes = 0;
- tuple.TupleData = (cisdata_t *) buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- tuple.DesiredTuple = RETURN_FIRST_TUPLE;
-
- ret = pcmcia_get_first_tuple(link, &tuple);
-
- while (ret == 0) {
- ret = pcmcia_get_tuple_data(link, &tuple);
-
- if (ret != 0) {
- cs_error(link, GetTupleData, ret);
- goto exit0;
- }
- ret = pcmcia_get_next_tuple(link, &tuple);
- }
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-
- ret = pcmcia_get_first_tuple(link, &tuple);
-
- if (ret != 0) {
- cs_error(link, GetFirstTuple, ret);
- goto exit0;
- }
-
- ret = pcmcia_get_tuple_data(link, &tuple);
-
- if (ret != 0) {
- cs_error(link, GetTupleData, ret);
- goto exit0;
- }
-
- ret = pcmcia_parse_tuple(&tuple, &parse);
-
- if (ret != 0) {
- cs_error(link, ParseTuple, ret);
- goto exit0;
- }
-
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
- link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
- link->io.IOAddrLines = 16;
-
- link->irq.IRQInfo1 = parse.cftable_entry.irq.IRQInfo1;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+ p_dev->io.IOAddrLines = 16;
/* 0x40 causes it to generate level mode interrupts. */
/* 0x04 enables IREQ pin. */
- cor_value = parse.cftable_entry.index | 0x44;
- link->conf.ConfigIndex = cor_value;
+ p_dev->conf.ConfigIndex = cfg->index | 0x44;
+ ret = pcmcia_request_io(p_dev, &p_dev->io);
+ if (ret)
+ return ret;
- /* IRQ and I/O settings */
- tuple.DesiredTuple = CISTPL_CONFIG;
+ io_resource = request_region(p_dev->io.BasePort1, p_dev->io.NumPorts1,
+ IPWIRELESS_PCCARD_NAME);
- ret = pcmcia_get_first_tuple(link, &tuple);
+ if (cfg->mem.nwin == 0)
+ return 0;
- if (ret != 0) {
- cs_error(link, GetFirstTuple, ret);
- goto exit0;
- }
+ ipw->request_common_memory.Attributes =
+ WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE;
+ ipw->request_common_memory.Base = cfg->mem.win[0].host_addr;
+ ipw->request_common_memory.Size = cfg->mem.win[0].len;
+ if (ipw->request_common_memory.Size < 0x1000)
+ ipw->request_common_memory.Size = 0x1000;
+ ipw->request_common_memory.AccessSpeed = 0;
- ret = pcmcia_get_tuple_data(link, &tuple);
-
- if (ret != 0) {
- cs_error(link, GetTupleData, ret);
- goto exit0;
- }
+ ret = pcmcia_request_window(p_dev, &ipw->request_common_memory,
+ &ipw->handle_common_memory);
- ret = pcmcia_parse_tuple(&tuple, &parse);
+ if (ret != 0)
+ goto exit1;
- if (ret != 0) {
- cs_error(link, GetTupleData, ret);
- goto exit0;
- }
- link->conf.Attributes = CONF_ENABLE_IRQ;
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
- link->conf.IntType = INT_MEMORY_AND_IO;
+ memreq_common_memory.CardOffset = cfg->mem.win[0].card_addr;
+ memreq_common_memory.Page = 0;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.Handler = ipwireless_interrupt;
- link->irq.Instance = ipw->hardware;
+ ret = pcmcia_map_mem_page(p_dev, ipw->handle_common_memory,
+ &memreq_common_memory);
- ret = pcmcia_request_io(link, &link->io);
+ if (ret != 0)
+ goto exit2;
- if (ret != 0) {
- cs_error(link, RequestIO, ret);
- goto exit0;
- }
+ ipw->is_v2_card = cfg->mem.win[0].len == 0x100;
- request_region(link->io.BasePort1, link->io.NumPorts1,
+ ipw->common_memory = ioremap(ipw->request_common_memory.Base,
+ ipw->request_common_memory.Size);
+ request_mem_region(ipw->request_common_memory.Base,
+ ipw->request_common_memory.Size,
IPWIRELESS_PCCARD_NAME);
- /* memory settings */
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-
- ret = pcmcia_get_first_tuple(link, &tuple);
-
- if (ret != 0) {
- cs_error(link, GetFirstTuple, ret);
- goto exit1;
- }
-
- ret = pcmcia_get_tuple_data(link, &tuple);
+ ipw->request_attr_memory.Attributes =
+ WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | WIN_ENABLE;
+ ipw->request_attr_memory.Base = 0;
+ ipw->request_attr_memory.Size = 0; /* this used to be 0x1000 */
+ ipw->request_attr_memory.AccessSpeed = 0;
- if (ret != 0) {
- cs_error(link, GetTupleData, ret);
- goto exit1;
- }
-
- ret = pcmcia_parse_tuple(&tuple, &parse);
-
- if (ret != 0) {
- cs_error(link, ParseTuple, ret);
- goto exit1;
- }
+ ret = pcmcia_request_window(p_dev, &ipw->request_attr_memory,
+ &ipw->handle_attr_memory);
- if (parse.cftable_entry.mem.nwin > 0) {
- ipw->request_common_memory.Attributes =
- WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE;
- ipw->request_common_memory.Base =
- parse.cftable_entry.mem.win[0].host_addr;
- ipw->request_common_memory.Size = parse.cftable_entry.mem.win[0].len;
- if (ipw->request_common_memory.Size < 0x1000)
- ipw->request_common_memory.Size = 0x1000;
- ipw->request_common_memory.AccessSpeed = 0;
-
- ret = pcmcia_request_window(&link, &ipw->request_common_memory,
- &ipw->handle_common_memory);
+ if (ret != 0)
+ goto exit2;
- if (ret != 0) {
- cs_error(link, RequestWindow, ret);
- goto exit1;
- }
+ memreq_attr_memory.CardOffset = 0;
+ memreq_attr_memory.Page = 0;
- memreq_common_memory.CardOffset =
- parse.cftable_entry.mem.win[0].card_addr;
- memreq_common_memory.Page = 0;
+ ret = pcmcia_map_mem_page(p_dev, ipw->handle_attr_memory,
+ &memreq_attr_memory);
- ret = pcmcia_map_mem_page(ipw->handle_common_memory,
- &memreq_common_memory);
+ if (ret != 0)
+ goto exit3;
- if (ret != 0) {
- cs_error(link, MapMemPage, ret);
- goto exit1;
- }
+ ipw->attr_memory = ioremap(ipw->request_attr_memory.Base,
+ ipw->request_attr_memory.Size);
+ request_mem_region(ipw->request_attr_memory.Base,
+ ipw->request_attr_memory.Size, IPWIRELESS_PCCARD_NAME);
- ipw->is_v2_card =
- parse.cftable_entry.mem.win[0].len == 0x100;
+ return 0;
- ipw->common_memory = ioremap(ipw->request_common_memory.Base,
+exit3:
+ pcmcia_release_window(p_dev, ipw->handle_attr_memory);
+exit2:
+ if (ipw->common_memory) {
+ release_mem_region(ipw->request_common_memory.Base,
ipw->request_common_memory.Size);
- request_mem_region(ipw->request_common_memory.Base,
- ipw->request_common_memory.Size, IPWIRELESS_PCCARD_NAME);
-
- ipw->request_attr_memory.Attributes =
- WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | WIN_ENABLE;
- ipw->request_attr_memory.Base = 0;
- ipw->request_attr_memory.Size = 0; /* this used to be 0x1000 */
- ipw->request_attr_memory.AccessSpeed = 0;
-
- ret = pcmcia_request_window(&link, &ipw->request_attr_memory,
- &ipw->handle_attr_memory);
+ iounmap(ipw->common_memory);
+ pcmcia_release_window(p_dev, ipw->handle_common_memory);
+ } else
+ pcmcia_release_window(p_dev, ipw->handle_common_memory);
+exit1:
+ release_resource(io_resource);
+ pcmcia_disable_device(p_dev);
+ return -1;
+}
- if (ret != 0) {
- cs_error(link, RequestWindow, ret);
- goto exit2;
- }
+static int config_ipwireless(struct ipw_dev *ipw)
+{
+ struct pcmcia_device *link = ipw->link;
+ int ret = 0;
- memreq_attr_memory.CardOffset = 0;
- memreq_attr_memory.Page = 0;
+ ipw->is_v2_card = 0;
- ret = pcmcia_map_mem_page(ipw->handle_attr_memory,
- &memreq_attr_memory);
+ ret = pcmcia_loop_config(link, ipwireless_probe, ipw);
+ if (ret != 0)
+ return ret;
- if (ret != 0) {
- cs_error(link, MapMemPage, ret);
- goto exit2;
- }
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
- ipw->attr_memory = ioremap(ipw->request_attr_memory.Base,
- ipw->request_attr_memory.Size);
- request_mem_region(ipw->request_attr_memory.Base, ipw->request_attr_memory.Size,
- IPWIRELESS_PCCARD_NAME);
- }
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ link->irq.Handler = ipwireless_interrupt;
INIT_WORK(&ipw->work_reboot, signalled_reboot_work);
@@ -291,10 +207,8 @@ static int config_ipwireless(struct ipw_dev *ipw)
ret = pcmcia_request_irq(link, &link->irq);
- if (ret != 0) {
- cs_error(link, RequestIRQ, ret);
- goto exit3;
- }
+ if (ret != 0)
+ goto exit;
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": Card type %s\n",
ipw->is_v2_card ? "V2/V3" : "V1");
@@ -316,12 +230,12 @@ static int config_ipwireless(struct ipw_dev *ipw)
ipw->network = ipwireless_network_create(ipw->hardware);
if (!ipw->network)
- goto exit3;
+ goto exit;
ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network,
ipw->nodes);
if (!ipw->tty)
- goto exit3;
+ goto exit;
ipwireless_init_hardware_v2_v3(ipw->hardware);
@@ -331,35 +245,27 @@ static int config_ipwireless(struct ipw_dev *ipw)
*/
ret = pcmcia_request_configuration(link, &link->conf);
- if (ret != 0) {
- cs_error(link, RequestConfiguration, ret);
- goto exit4;
- }
+ if (ret != 0)
+ goto exit;
link->dev_node = &ipw->nodes[0];
return 0;
-exit4:
- pcmcia_disable_device(link);
-exit3:
+exit:
if (ipw->attr_memory) {
release_mem_region(ipw->request_attr_memory.Base,
ipw->request_attr_memory.Size);
iounmap(ipw->attr_memory);
- pcmcia_release_window(ipw->handle_attr_memory);
- pcmcia_disable_device(link);
+ pcmcia_release_window(link, ipw->handle_attr_memory);
}
-exit2:
if (ipw->common_memory) {
release_mem_region(ipw->request_common_memory.Base,
ipw->request_common_memory.Size);
iounmap(ipw->common_memory);
- pcmcia_release_window(ipw->handle_common_memory);
+ pcmcia_release_window(link, ipw->handle_common_memory);
}
-exit1:
pcmcia_disable_device(link);
-exit0:
return -1;
}
@@ -378,9 +284,9 @@ static void release_ipwireless(struct ipw_dev *ipw)
iounmap(ipw->attr_memory);
}
if (ipw->common_memory)
- pcmcia_release_window(ipw->handle_common_memory);
+ pcmcia_release_window(ipw->link, ipw->handle_common_memory);
if (ipw->attr_memory)
- pcmcia_release_window(ipw->handle_attr_memory);
+ pcmcia_release_window(ipw->link, ipw->handle_attr_memory);
/* Break the link with Card Services */
pcmcia_disable_device(ipw->link);
@@ -406,7 +312,6 @@ static int ipwireless_attach(struct pcmcia_device *link)
ipw->link = link;
link->priv = ipw;
- link->irq.Instance = ipw;
/* Link this device into our device list. */
link->dev_node = &ipw->nodes[0];
@@ -421,7 +326,6 @@ static int ipwireless_attach(struct pcmcia_device *link)
ret = config_ipwireless(ipw);
if (ret != 0) {
- cs_error(link, RegisterClient, ret);
ipwireless_detach(link);
return ret;
}
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index caf6e4d1946..c31a0d913d3 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -554,7 +554,6 @@ static int mgslpc_probe(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->irq.Handler = NULL;
link->conf.Attributes = 0;
@@ -572,69 +571,51 @@ static int mgslpc_probe(struct pcmcia_device *link)
/* Card has been inserted.
*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int mgslpc_ioprobe(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->io.nwin > 0) {
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(cfg->io.flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(cfg->io.flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = cfg->io.flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+ return pcmcia_request_io(p_dev, &p_dev->io);
+ }
+ return -ENODEV;
+}
static int mgslpc_config(struct pcmcia_device *link)
{
MGSLPC_INFO *info = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_fn, last_ret;
- u_char buf[64];
- cistpl_cftable_entry_t dflt = { 0 };
- cistpl_cftable_entry_t *cfg;
+ int ret;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("mgslpc_config(0x%p)\n", link);
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- /* get CIS configuration entry */
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
-
- cfg = &(parse.cftable_entry);
- CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
- CS_CHECK(ParseTuple, pcmcia_parse_tuple(&tuple, &parse));
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
- if (cfg->index == 0)
- goto cs_failed;
-
- link->conf.ConfigIndex = cfg->index;
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
- }
+ ret = pcmcia_loop_config(link, mgslpc_ioprobe, NULL);
+ if (ret != 0)
+ goto failed;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.ConfigIndex = 8;
link->conf.Present = PRESENT_OPTION;
- link->irq.Attributes |= IRQ_HANDLE_PRESENT;
link->irq.Handler = mgslpc_isr;
- link->irq.Instance = info;
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
info->io_base = link->io.BasePort1;
info->irq_level = link->irq.AssignedIRQ;
@@ -654,8 +635,7 @@ static int mgslpc_config(struct pcmcia_device *link)
printk("\n");
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
+failed:
mgslpc_release((u_long)link);
return -ENODEV;
}
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 62f282e6763..d86c0bc05c1 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -431,30 +431,25 @@ static struct cdev ptmx_cdev;
static struct ctl_table pty_table[] = {
{
- .ctl_name = PTY_MAX,
.procname = "max",
.maxlen = sizeof(int),
.mode = 0644,
.data = &pty_limit,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = &pty_limit_min,
.extra2 = &pty_limit_max,
}, {
- .ctl_name = PTY_NR,
.procname = "nr",
.maxlen = sizeof(int),
.mode = 0444,
.data = &pty_count,
- .proc_handler = &proc_dointvec,
- }, {
- .ctl_name = 0
- }
+ .proc_handler = proc_dointvec,
+ },
+ {}
};
static struct ctl_table pty_kern_table[] = {
{
- .ctl_name = KERN_PTY,
.procname = "pty",
.mode = 0555,
.child = pty_table,
@@ -464,7 +459,6 @@ static struct ctl_table pty_kern_table[] = {
static struct ctl_table pty_root_table[] = {
{
- .ctl_name = CTL_KERN,
.procname = "kernel",
.mode = 0555,
.child = pty_kern_table,
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 04b505e5a5e..dcd08635cf1 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1257,94 +1257,54 @@ static int proc_do_uuid(ctl_table *table, int write,
return proc_dostring(&fake_table, write, buffer, lenp, ppos);
}
-static int uuid_strategy(ctl_table *table,
- void __user *oldval, size_t __user *oldlenp,
- void __user *newval, size_t newlen)
-{
- unsigned char tmp_uuid[16], *uuid;
- unsigned int len;
-
- if (!oldval || !oldlenp)
- return 1;
-
- uuid = table->data;
- if (!uuid) {
- uuid = tmp_uuid;
- uuid[8] = 0;
- }
- if (uuid[8] == 0)
- generate_random_uuid(uuid);
-
- if (get_user(len, oldlenp))
- return -EFAULT;
- if (len) {
- if (len > 16)
- len = 16;
- if (copy_to_user(oldval, uuid, len) ||
- put_user(len, oldlenp))
- return -EFAULT;
- }
- return 1;
-}
-
static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
ctl_table random_table[] = {
{
- .ctl_name = RANDOM_POOLSIZE,
.procname = "poolsize",
.data = &sysctl_poolsize,
.maxlen = sizeof(int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
- .ctl_name = RANDOM_ENTROPY_COUNT,
.procname = "entropy_avail",
.maxlen = sizeof(int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
.data = &input_pool.entropy_count,
},
{
- .ctl_name = RANDOM_READ_THRESH,
.procname = "read_wakeup_threshold",
.data = &random_read_wakeup_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = &min_read_thresh,
.extra2 = &max_read_thresh,
},
{
- .ctl_name = RANDOM_WRITE_THRESH,
.procname = "write_wakeup_threshold",
.data = &random_write_wakeup_thresh,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = &min_write_thresh,
.extra2 = &max_write_thresh,
},
{
- .ctl_name = RANDOM_BOOT_ID,
.procname = "boot_id",
.data = &sysctl_bootid,
.maxlen = 16,
.mode = 0444,
- .proc_handler = &proc_do_uuid,
- .strategy = &uuid_strategy,
+ .proc_handler = proc_do_uuid,
},
{
- .ctl_name = RANDOM_UUID,
.procname = "uuid",
.maxlen = 16,
.mode = 0444,
- .proc_handler = &proc_do_uuid,
- .strategy = &uuid_strategy,
+ .proc_handler = proc_do_uuid,
},
- { .ctl_name = 0 }
+ { }
};
#endif /* CONFIG_SYSCTL */
diff --git a/drivers/char/rio/route.h b/drivers/char/rio/route.h
index 20ed73f3fd7..46e963771c3 100644
--- a/drivers/char/rio/route.h
+++ b/drivers/char/rio/route.h
@@ -67,7 +67,7 @@
typedef struct COST_ROUTE COST_ROUTE;
struct COST_ROUTE {
unsigned char cost; /* Cost down this link */
- unsigned char route[NODE_BYTES]; /* Nodes thorough this route */
+ unsigned char route[NODE_BYTES]; /* Nodes through this route */
};
typedef struct ROUTE_STR ROUTE_STR;
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index bc4ab3e5455..95acb8c880f 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -282,34 +282,31 @@ static irqreturn_t rtc_interrupt(int irq, void *dev_id)
*/
static ctl_table rtc_table[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "max-user-freq",
.data = &rtc_max_user_freq,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table rtc_root[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "rtc",
.mode = 0555,
.child = rtc_table,
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table dev_root[] = {
{
- .ctl_name = CTL_DEV,
.procname = "dev",
.mode = 0555,
.child = rtc_root,
},
- { .ctl_name = 0 }
+ { }
};
static struct ctl_table_header *sysctl_header;
diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c
index 1d9100561c8..99e5272e3c5 100644
--- a/drivers/char/scx200_gpio.c
+++ b/drivers/char/scx200_gpio.c
@@ -12,7 +12,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -52,7 +51,6 @@ static int scx200_gpio_open(struct inode *inode, struct file *file)
unsigned m = iminor(inode);
file->private_data = &scx200_gpio_ops;
- cycle_kernel_lock();
if (m >= MAX_PINS)
return -EINVAL;
return nonseekable_open(inode, file);
diff --git a/drivers/char/tb0219.c b/drivers/char/tb0219.c
index b3ec9b10e29..cad4eb65f13 100644
--- a/drivers/char/tb0219.c
+++ b/drivers/char/tb0219.c
@@ -21,7 +21,6 @@
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/smp_lock.h>
#include <asm/io.h>
#include <asm/reboot.h>
@@ -38,7 +37,7 @@ MODULE_PARM_DESC(major, "Major device number");
static void (*old_machine_restart)(char *command);
static void __iomem *tb0219_base;
-static spinlock_t tb0219_lock;
+static DEFINE_SPINLOCK(tb0219_lock);
#define tb0219_read(offset) readw(tb0219_base + (offset))
#define tb0219_write(offset, value) writew((value), tb0219_base + (offset))
@@ -237,7 +236,6 @@ static int tanbac_tb0219_open(struct inode *inode, struct file *file)
{
unsigned int minor;
- cycle_kernel_lock();
minor = iminor(inode);
switch (minor) {
case 0:
@@ -306,8 +304,6 @@ static int __devinit tb0219_probe(struct platform_device *dev)
return retval;
}
- spin_lock_init(&tb0219_lock);
-
old_machine_restart = _machine_restart;
_machine_restart = tb0219_restart;
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 47c2d276345..f06bb37defb 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -31,7 +31,7 @@
enum tpm_const {
TPM_MINOR = 224, /* officially assigned */
- TPM_BUFSIZE = 2048,
+ TPM_BUFSIZE = 4096,
TPM_NUM_DEVICES = 256,
};
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 0b73e4ec1ad..2405f17b29d 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -257,6 +257,10 @@ out:
return size;
}
+static int itpm;
+module_param(itpm, bool, 0444);
+MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
+
/*
* If interrupts are used (signaled by an irq set in the vendor structure)
* tpm.c can skip polling for the data to be available as the interrupt is
@@ -293,7 +297,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
&chip->vendor.int_queue);
status = tpm_tis_status(chip);
- if ((status & TPM_STS_DATA_EXPECT) == 0) {
+ if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
rc = -EIO;
goto out_err;
}
@@ -467,6 +471,10 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
+ if (itpm)
+ dev_info(dev, "Intel iTPM workaround enabled\n");
+
+
/* Figure out the capabilities */
intfcaps =
ioread32(chip->vendor.iobase +
@@ -629,6 +637,7 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
{"", 0}, /* User Specified */
{"", 0} /* Terminator */
};
+MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev)
{
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index 8e67d5c642a..6bd5f8866c7 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -315,7 +315,7 @@ EXPORT_SYMBOL(tty_termios_input_baud_rate);
* For maximal back compatibility with legacy SYS5/POSIX *nix behaviour
* we need to carefully set the bits when the user does not get the
* desired speed. We allow small margins and preserve as much of possible
- * of the input intent to keep compatiblity.
+ * of the input intent to keep compatibility.
*
* Locking: Caller should hold termios lock. This is already held
* when calling this function from the driver termios handler.
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 0c80c68cd04..1e3d728dbf7 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -161,6 +161,8 @@ static void set_palette(struct vc_data *vc);
static int printable; /* Is console ready for printing? */
int default_utf8 = true;
module_param(default_utf8, int, S_IRUGO | S_IWUSR);
+int global_cursor_default = -1;
+module_param(global_cursor_default, int, S_IRUGO | S_IWUSR);
/*
* ignore_poke: don't unblank the screen when things are typed. This is
@@ -775,6 +777,12 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
vc_cons[currcons].d = NULL;
return -ENOMEM;
}
+
+ /* If no drivers have overridden us and the user didn't pass a
+ boot option, default to displaying the cursor */
+ if (global_cursor_default == -1)
+ global_cursor_default = 1;
+
vc_init(vc, vc->vc_rows, vc->vc_cols, 1);
vcs_make_sysfs(currcons);
atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param);
@@ -1616,7 +1624,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
vc->vc_decscnm = 0;
vc->vc_decom = 0;
vc->vc_decawm = 1;
- vc->vc_deccm = 1;
+ vc->vc_deccm = global_cursor_default;
vc->vc_decim = 0;
set_kbd(vc, decarm);
@@ -4078,6 +4086,7 @@ EXPORT_SYMBOL(fg_console);
EXPORT_SYMBOL(console_blank_hook);
EXPORT_SYMBOL(console_blanked);
EXPORT_SYMBOL(vc_cons);
+EXPORT_SYMBOL(global_cursor_default);
#ifndef VT_SINGLE_DRIVER
EXPORT_SYMBOL(take_over_console);
EXPORT_SYMBOL(give_up_console);
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 70b59642a70..724c164d31c 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -21,7 +21,7 @@ struct cpuidle_governor *cpuidle_curr_governor;
* __cpuidle_find_governor - finds a governor of the specified name
* @str: the name
*
- * Must be called with cpuidle_lock aquired.
+ * Must be called with cpuidle_lock acquired.
*/
static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
{
@@ -39,7 +39,7 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
* @gov: the new target governor
*
* NOTE: "gov" can be NULL to specify disabled
- * Must be called with cpuidle_lock aquired.
+ * Must be called with cpuidle_lock acquired.
*/
int cpuidle_switch_governor(struct cpuidle_governor *gov)
{
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index 5f753fc0873..09ad9154d86 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -863,7 +863,7 @@ static int hifn_init_pubrng(struct hifn_device *dev)
dev->dmareg |= HIFN_DMAIER_PUBDONE;
hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
- dprintk("Chip %s: Public key engine has been sucessfully "
+ dprintk("Chip %s: Public key engine has been successfully "
"initialised.\n", dev->name);
}
diff --git a/drivers/dio/dio-driver.c b/drivers/dio/dio-driver.c
index 9c0c9afcd0a..a7b174ef4c8 100644
--- a/drivers/dio/dio-driver.c
+++ b/drivers/dio/dio-driver.c
@@ -140,5 +140,4 @@ postcore_initcall(dio_driver_init);
EXPORT_SYMBOL(dio_match_device);
EXPORT_SYMBOL(dio_register_driver);
EXPORT_SYMBOL(dio_unregister_driver);
-EXPORT_SYMBOL(dio_dev_driver);
EXPORT_SYMBOL(dio_bus_type);
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index b401dadad4a..eb140ff38c2 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -54,7 +54,7 @@ config DW_DMAC
config AT_HDMAC
tristate "Atmel AHB DMA support"
- depends on ARCH_AT91SAM9RL
+ depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
select DMA_ENGINE
help
Support the Atmel AHB DMA controller. This can be integrated in
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 7585c4164bd..c52ac9efd0b 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -99,7 +99,7 @@ static struct at_desc *atc_alloc_descriptor(struct dma_chan *chan,
}
/**
- * atc_desc_get - get a unsused descriptor from free_list
+ * atc_desc_get - get an unused descriptor from free_list
* @atchan: channel we want a new descriptor for
*/
static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index a38831c8264..5fdd6daa40e 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -19,26 +19,48 @@ static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES];
static struct amd64_pvt *pvt_lookup[EDAC_MAX_NUMNODES];
/*
- * See F2x80 for K8 and F2x[1,0]80 for Fam10 and later. The table below is only
- * for DDR2 DRAM mapping.
+ * Address to DRAM bank mapping: see F2x80 for K8 and F2x[1,0]80 for Fam10 and
+ * later.
*/
-u32 revf_quad_ddr2_shift[] = {
- 0, /* 0000b NULL DIMM (128mb) */
- 28, /* 0001b 256mb */
- 29, /* 0010b 512mb */
- 29, /* 0011b 512mb */
- 29, /* 0100b 512mb */
- 30, /* 0101b 1gb */
- 30, /* 0110b 1gb */
- 31, /* 0111b 2gb */
- 31, /* 1000b 2gb */
- 32, /* 1001b 4gb */
- 32, /* 1010b 4gb */
- 33, /* 1011b 8gb */
- 0, /* 1100b future */
- 0, /* 1101b future */
- 0, /* 1110b future */
- 0 /* 1111b future */
+static int ddr2_dbam_revCG[] = {
+ [0] = 32,
+ [1] = 64,
+ [2] = 128,
+ [3] = 256,
+ [4] = 512,
+ [5] = 1024,
+ [6] = 2048,
+};
+
+static int ddr2_dbam_revD[] = {
+ [0] = 32,
+ [1] = 64,
+ [2 ... 3] = 128,
+ [4] = 256,
+ [5] = 512,
+ [6] = 256,
+ [7] = 512,
+ [8 ... 9] = 1024,
+ [10] = 2048,
+};
+
+static int ddr2_dbam[] = { [0] = 128,
+ [1] = 256,
+ [2 ... 4] = 512,
+ [5 ... 6] = 1024,
+ [7 ... 8] = 2048,
+ [9 ... 10] = 4096,
+ [11] = 8192,
+};
+
+static int ddr3_dbam[] = { [0] = -1,
+ [1] = 256,
+ [2] = 512,
+ [3 ... 4] = -1,
+ [5 ... 6] = 1024,
+ [7 ... 8] = 2048,
+ [9 ... 10] = 4096,
+ [11] = 8192,
};
/*
@@ -164,11 +186,9 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw)
{
struct amd64_pvt *pvt = mci->pvt_info;
u32 scrubval = 0;
- int status = -1, i, ret = 0;
+ int status = -1, i;
- ret = pci_read_config_dword(pvt->misc_f3_ctl, K8_SCRCTRL, &scrubval);
- if (ret)
- debugf0("Reading K8_SCRCTRL failed\n");
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_SCRCTRL, &scrubval);
scrubval = scrubval & 0x001F;
@@ -189,7 +209,7 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw)
/* Map from a CSROW entry to the mask entry that operates on it */
static inline u32 amd64_map_to_dcs_mask(struct amd64_pvt *pvt, int csrow)
{
- if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_F)
+ if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F)
return csrow;
else
return csrow >> 1;
@@ -437,7 +457,7 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
u64 base;
/* only revE and later have the DRAM Hole Address Register */
- if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_E) {
+ if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
debugf1(" revision %d for node %d does not support DHAR\n",
pvt->ext_model, pvt->mc_node_id);
return 1;
@@ -743,21 +763,6 @@ static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
*input_addr_max = base | mask | pvt->dcs_mask_notused;
}
-/*
- * Extract error address from MCA NB Address Low (section 3.6.4.5) and MCA NB
- * Address High (section 3.6.4.6) register values and return the result. Address
- * is located in the info structure (nbeah and nbeal), the encoding is device
- * specific.
- */
-static u64 extract_error_address(struct mem_ctl_info *mci,
- struct err_regs *info)
-{
- struct amd64_pvt *pvt = mci->pvt_info;
-
- return pvt->ops->get_error_address(mci, info);
-}
-
-
/* Map the Error address to a PAGE and PAGE OFFSET. */
static inline void error_address_to_page_and_offset(u64 error_address,
u32 *page, u32 *offset)
@@ -787,7 +792,7 @@ static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
return csrow;
}
-static int get_channel_from_ecc_syndrome(unsigned short syndrome);
+static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
static void amd64_cpu_display_info(struct amd64_pvt *pvt)
{
@@ -797,7 +802,7 @@ static void amd64_cpu_display_info(struct amd64_pvt *pvt)
edac_printk(KERN_DEBUG, EDAC_MC, "F10h CPU detected\n");
else if (boot_cpu_data.x86 == 0xf)
edac_printk(KERN_DEBUG, EDAC_MC, "%s detected\n",
- (pvt->ext_model >= OPTERON_CPU_REV_F) ?
+ (pvt->ext_model >= K8_REV_F) ?
"Rev F or later" : "Rev E or earlier");
else
/* we'll hardly ever ever get here */
@@ -813,7 +818,7 @@ static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
int bit;
enum dev_type edac_cap = EDAC_FLAG_NONE;
- bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= OPTERON_CPU_REV_F)
+ bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
? 19
: 17;
@@ -824,111 +829,86 @@ static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
}
-static void f10_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt,
- int ganged);
+static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt);
+
+static void amd64_dump_dramcfg_low(u32 dclr, int chan)
+{
+ debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
+
+ debugf1(" DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
+ (dclr & BIT(16)) ? "un" : "",
+ (dclr & BIT(19)) ? "yes" : "no");
+
+ debugf1(" PAR/ERR parity: %s\n",
+ (dclr & BIT(8)) ? "enabled" : "disabled");
+
+ debugf1(" DCT 128bit mode width: %s\n",
+ (dclr & BIT(11)) ? "128b" : "64b");
+
+ debugf1(" x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
+ (dclr & BIT(12)) ? "yes" : "no",
+ (dclr & BIT(13)) ? "yes" : "no",
+ (dclr & BIT(14)) ? "yes" : "no",
+ (dclr & BIT(15)) ? "yes" : "no");
+}
/* Display and decode various NB registers for debug purposes. */
static void amd64_dump_misc_regs(struct amd64_pvt *pvt)
{
int ganged;
- debugf1(" nbcap:0x%8.08x DctDualCap=%s DualNode=%s 8-Node=%s\n",
- pvt->nbcap,
- (pvt->nbcap & K8_NBCAP_DCT_DUAL) ? "True" : "False",
- (pvt->nbcap & K8_NBCAP_DUAL_NODE) ? "True" : "False",
- (pvt->nbcap & K8_NBCAP_8_NODE) ? "True" : "False");
- debugf1(" ECC Capable=%s ChipKill Capable=%s\n",
- (pvt->nbcap & K8_NBCAP_SECDED) ? "True" : "False",
- (pvt->nbcap & K8_NBCAP_CHIPKILL) ? "True" : "False");
- debugf1(" DramCfg0-low=0x%08x DIMM-ECC=%s Parity=%s Width=%s\n",
- pvt->dclr0,
- (pvt->dclr0 & BIT(19)) ? "Enabled" : "Disabled",
- (pvt->dclr0 & BIT(8)) ? "Enabled" : "Disabled",
- (pvt->dclr0 & BIT(11)) ? "128b" : "64b");
- debugf1(" DIMM x4 Present: L0=%s L1=%s L2=%s L3=%s DIMM Type=%s\n",
- (pvt->dclr0 & BIT(12)) ? "Y" : "N",
- (pvt->dclr0 & BIT(13)) ? "Y" : "N",
- (pvt->dclr0 & BIT(14)) ? "Y" : "N",
- (pvt->dclr0 & BIT(15)) ? "Y" : "N",
- (pvt->dclr0 & BIT(16)) ? "UN-Buffered" : "Buffered");
-
-
- debugf1(" online-spare: 0x%8.08x\n", pvt->online_spare);
+ debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
- if (boot_cpu_data.x86 == 0xf) {
- debugf1(" dhar: 0x%8.08x Base=0x%08x Offset=0x%08x\n",
- pvt->dhar, dhar_base(pvt->dhar),
- k8_dhar_offset(pvt->dhar));
- debugf1(" DramHoleValid=%s\n",
- (pvt->dhar & DHAR_VALID) ? "True" : "False");
+ debugf1(" NB two channel DRAM capable: %s\n",
+ (pvt->nbcap & K8_NBCAP_DCT_DUAL) ? "yes" : "no");
- debugf1(" dbam-dkt: 0x%8.08x\n", pvt->dbam0);
+ debugf1(" ECC capable: %s, ChipKill ECC capable: %s\n",
+ (pvt->nbcap & K8_NBCAP_SECDED) ? "yes" : "no",
+ (pvt->nbcap & K8_NBCAP_CHIPKILL) ? "yes" : "no");
- /* everything below this point is Fam10h and above */
- return;
+ amd64_dump_dramcfg_low(pvt->dclr0, 0);
- } else {
- debugf1(" dhar: 0x%8.08x Base=0x%08x Offset=0x%08x\n",
- pvt->dhar, dhar_base(pvt->dhar),
- f10_dhar_offset(pvt->dhar));
- debugf1(" DramMemHoistValid=%s DramHoleValid=%s\n",
- (pvt->dhar & F10_DRAM_MEM_HOIST_VALID) ?
- "True" : "False",
- (pvt->dhar & DHAR_VALID) ?
- "True" : "False");
- }
+ debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
- /* Only if NOT ganged does dcl1 have valid info */
- if (!dct_ganging_enabled(pvt)) {
- debugf1(" DramCfg1-low=0x%08x DIMM-ECC=%s Parity=%s "
- "Width=%s\n", pvt->dclr1,
- (pvt->dclr1 & BIT(19)) ? "Enabled" : "Disabled",
- (pvt->dclr1 & BIT(8)) ? "Enabled" : "Disabled",
- (pvt->dclr1 & BIT(11)) ? "128b" : "64b");
- debugf1(" DIMM x4 Present: L0=%s L1=%s L2=%s L3=%s "
- "DIMM Type=%s\n",
- (pvt->dclr1 & BIT(12)) ? "Y" : "N",
- (pvt->dclr1 & BIT(13)) ? "Y" : "N",
- (pvt->dclr1 & BIT(14)) ? "Y" : "N",
- (pvt->dclr1 & BIT(15)) ? "Y" : "N",
- (pvt->dclr1 & BIT(16)) ? "UN-Buffered" : "Buffered");
+ debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, "
+ "offset: 0x%08x\n",
+ pvt->dhar,
+ dhar_base(pvt->dhar),
+ (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt->dhar)
+ : f10_dhar_offset(pvt->dhar));
+
+ debugf1(" DramHoleValid: %s\n",
+ (pvt->dhar & DHAR_VALID) ? "yes" : "no");
+
+ /* everything below this point is Fam10h and above */
+ if (boot_cpu_data.x86 == 0xf) {
+ amd64_debug_display_dimm_sizes(0, pvt);
+ return;
}
+ /* Only if NOT ganged does dclr1 have valid info */
+ if (!dct_ganging_enabled(pvt))
+ amd64_dump_dramcfg_low(pvt->dclr1, 1);
+
/*
* Determine if ganged and then dump memory sizes for first controller,
* and if NOT ganged dump info for 2nd controller.
*/
ganged = dct_ganging_enabled(pvt);
- f10_debug_display_dimm_sizes(0, pvt, ganged);
+ amd64_debug_display_dimm_sizes(0, pvt);
if (!ganged)
- f10_debug_display_dimm_sizes(1, pvt, ganged);
+ amd64_debug_display_dimm_sizes(1, pvt);
}
/* Read in both of DBAM registers */
static void amd64_read_dbam_reg(struct amd64_pvt *pvt)
{
- int err = 0;
- unsigned int reg;
-
- reg = DBAM0;
- err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dbam0);
- if (err)
- goto err_reg;
+ amd64_read_pci_cfg(pvt->dram_f2_ctl, DBAM0, &pvt->dbam0);
- if (boot_cpu_data.x86 >= 0x10) {
- reg = DBAM1;
- err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dbam1);
-
- if (err)
- goto err_reg;
- }
-
- return;
-
-err_reg:
- debugf0("Error reading F2x%03x.\n", reg);
+ if (boot_cpu_data.x86 >= 0x10)
+ amd64_read_pci_cfg(pvt->dram_f2_ctl, DBAM1, &pvt->dbam1);
}
/*
@@ -963,7 +943,7 @@ err_reg:
static void amd64_set_dct_base_and_mask(struct amd64_pvt *pvt)
{
- if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_F) {
+ if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
pvt->dcsb_base = REV_E_DCSB_BASE_BITS;
pvt->dcsm_mask = REV_E_DCSM_MASK_BITS;
pvt->dcs_mask_notused = REV_E_DCS_NOTUSED_BITS;
@@ -991,28 +971,21 @@ static void amd64_set_dct_base_and_mask(struct amd64_pvt *pvt)
*/
static void amd64_read_dct_base_mask(struct amd64_pvt *pvt)
{
- int cs, reg, err = 0;
+ int cs, reg;
amd64_set_dct_base_and_mask(pvt);
for (cs = 0; cs < pvt->cs_count; cs++) {
reg = K8_DCSB0 + (cs * 4);
- err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
- &pvt->dcsb0[cs]);
- if (unlikely(err))
- debugf0("Reading K8_DCSB0[%d] failed\n", cs);
- else
+ if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg, &pvt->dcsb0[cs]))
debugf0(" DCSB0[%d]=0x%08x reg: F2x%x\n",
cs, pvt->dcsb0[cs], reg);
/* If DCT are NOT ganged, then read in DCT1's base */
if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
reg = F10_DCSB1 + (cs * 4);
- err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
- &pvt->dcsb1[cs]);
- if (unlikely(err))
- debugf0("Reading F10_DCSB1[%d] failed\n", cs);
- else
+ if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg,
+ &pvt->dcsb1[cs]))
debugf0(" DCSB1[%d]=0x%08x reg: F2x%x\n",
cs, pvt->dcsb1[cs], reg);
} else {
@@ -1022,26 +995,20 @@ static void amd64_read_dct_base_mask(struct amd64_pvt *pvt)
for (cs = 0; cs < pvt->num_dcsm; cs++) {
reg = K8_DCSM0 + (cs * 4);
- err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
- &pvt->dcsm0[cs]);
- if (unlikely(err))
- debugf0("Reading K8_DCSM0 failed\n");
- else
+ if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg, &pvt->dcsm0[cs]))
debugf0(" DCSM0[%d]=0x%08x reg: F2x%x\n",
cs, pvt->dcsm0[cs], reg);
/* If DCT are NOT ganged, then read in DCT1's mask */
if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
reg = F10_DCSM1 + (cs * 4);
- err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
- &pvt->dcsm1[cs]);
- if (unlikely(err))
- debugf0("Reading F10_DCSM1[%d] failed\n", cs);
- else
+ if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg,
+ &pvt->dcsm1[cs]))
debugf0(" DCSM1[%d]=0x%08x reg: F2x%x\n",
cs, pvt->dcsm1[cs], reg);
- } else
+ } else {
pvt->dcsm1[cs] = 0;
+ }
}
}
@@ -1049,18 +1016,16 @@ static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt)
{
enum mem_type type;
- if (boot_cpu_data.x86 >= 0x10 || pvt->ext_model >= OPTERON_CPU_REV_F) {
- /* Rev F and later */
- type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
+ if (boot_cpu_data.x86 >= 0x10 || pvt->ext_model >= K8_REV_F) {
+ if (pvt->dchr0 & DDR3_MODE)
+ type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
+ else
+ type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
} else {
- /* Rev E and earlier */
type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
}
- debugf1(" Memory type is: %s\n",
- (type == MEM_DDR2) ? "MEM_DDR2" :
- (type == MEM_RDDR2) ? "MEM_RDDR2" :
- (type == MEM_DDR) ? "MEM_DDR" : "MEM_RDDR");
+ debugf1(" Memory type is: %s\n", edac_mem_types[type]);
return type;
}
@@ -1078,11 +1043,11 @@ static int k8_early_channel_count(struct amd64_pvt *pvt)
{
int flag, err = 0;
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
+ err = amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
if (err)
return err;
- if ((boot_cpu_data.x86_model >> 4) >= OPTERON_CPU_REV_F) {
+ if ((boot_cpu_data.x86_model >> 4) >= K8_REV_F) {
/* RevF (NPT) and later */
flag = pvt->dclr0 & F10_WIDTH_128;
} else {
@@ -1114,22 +1079,15 @@ static void k8_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
{
u32 low;
u32 off = dram << 3; /* 8 bytes between DRAM entries */
- int err;
- err = pci_read_config_dword(pvt->addr_f1_ctl,
- K8_DRAM_BASE_LOW + off, &low);
- if (err)
- debugf0("Reading K8_DRAM_BASE_LOW failed\n");
+ amd64_read_pci_cfg(pvt->addr_f1_ctl, K8_DRAM_BASE_LOW + off, &low);
/* Extract parts into separate data entries */
pvt->dram_base[dram] = ((u64) low & 0xFFFF0000) << 8;
pvt->dram_IntlvEn[dram] = (low >> 8) & 0x7;
pvt->dram_rw_en[dram] = (low & 0x3);
- err = pci_read_config_dword(pvt->addr_f1_ctl,
- K8_DRAM_LIMIT_LOW + off, &low);
- if (err)
- debugf0("Reading K8_DRAM_LIMIT_LOW failed\n");
+ amd64_read_pci_cfg(pvt->addr_f1_ctl, K8_DRAM_LIMIT_LOW + off, &low);
/*
* Extract parts into separate data entries. Limit is the HIGHEST memory
@@ -1142,7 +1100,7 @@ static void k8_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
struct err_regs *info,
- u64 SystemAddress)
+ u64 sys_addr)
{
struct mem_ctl_info *src_mci;
unsigned short syndrome;
@@ -1155,7 +1113,7 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
/* CHIPKILL enabled */
if (info->nbcfg & K8_NBCFG_CHIPKILL) {
- channel = get_channel_from_ecc_syndrome(syndrome);
+ channel = get_channel_from_ecc_syndrome(mci, syndrome);
if (channel < 0) {
/*
* Syndrome didn't map, so we don't know which of the
@@ -1177,64 +1135,46 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
* was obtained from email communication with someone at AMD.
* (Wish the email was placed in this comment - norsk)
*/
- channel = ((SystemAddress & BIT(3)) != 0);
+ channel = ((sys_addr & BIT(3)) != 0);
}
/*
* Find out which node the error address belongs to. This may be
* different from the node that detected the error.
*/
- src_mci = find_mc_by_sys_addr(mci, SystemAddress);
+ src_mci = find_mc_by_sys_addr(mci, sys_addr);
if (!src_mci) {
amd64_mc_printk(mci, KERN_ERR,
"failed to map error address 0x%lx to a node\n",
- (unsigned long)SystemAddress);
+ (unsigned long)sys_addr);
edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
return;
}
- /* Now map the SystemAddress to a CSROW */
- csrow = sys_addr_to_csrow(src_mci, SystemAddress);
+ /* Now map the sys_addr to a CSROW */
+ csrow = sys_addr_to_csrow(src_mci, sys_addr);
if (csrow < 0) {
edac_mc_handle_ce_no_info(src_mci, EDAC_MOD_STR);
} else {
- error_address_to_page_and_offset(SystemAddress, &page, &offset);
+ error_address_to_page_and_offset(sys_addr, &page, &offset);
edac_mc_handle_ce(src_mci, page, offset, syndrome, csrow,
channel, EDAC_MOD_STR);
}
}
-/*
- * determrine the number of PAGES in for this DIMM's size based on its DRAM
- * Address Mapping.
- *
- * First step is to calc the number of bits to shift a value of 1 left to
- * indicate show many pages. Start with the DBAM value as the starting bits,
- * then proceed to adjust those shift bits, based on CPU rev and the table.
- * See BKDG on the DBAM
- */
-static int k8_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
+static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
{
- int nr_pages;
+ int *dbam_map;
- if (pvt->ext_model >= OPTERON_CPU_REV_F) {
- nr_pages = 1 << (revf_quad_ddr2_shift[dram_map] - PAGE_SHIFT);
- } else {
- /*
- * RevE and less section; this line is tricky. It collapses the
- * table used by RevD and later to one that matches revisions CG
- * and earlier.
- */
- dram_map -= (pvt->ext_model >= OPTERON_CPU_REV_D) ?
- (dram_map > 8 ? 4 : (dram_map > 5 ?
- 3 : (dram_map > 2 ? 1 : 0))) : 0;
-
- /* 25 shift is 32MiB minimum DIMM size in RevE and prior */
- nr_pages = 1 << (dram_map + 25 - PAGE_SHIFT);
- }
+ if (pvt->ext_model >= K8_REV_F)
+ dbam_map = ddr2_dbam;
+ else if (pvt->ext_model >= K8_REV_D)
+ dbam_map = ddr2_dbam_revD;
+ else
+ dbam_map = ddr2_dbam_revCG;
- return nr_pages;
+ return dbam_map[cs_mode];
}
/*
@@ -1248,34 +1188,24 @@ static int k8_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
static int f10_early_channel_count(struct amd64_pvt *pvt)
{
int dbams[] = { DBAM0, DBAM1 };
- int err = 0, channels = 0;
- int i, j;
+ int i, j, channels = 0;
u32 dbam;
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
- if (err)
- goto err_reg;
-
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_1, &pvt->dclr1);
- if (err)
- goto err_reg;
-
/* If we are in 128 bit mode, then we are using 2 channels */
if (pvt->dclr0 & F10_WIDTH_128) {
- debugf0("Data WIDTH is 128 bits - 2 channels\n");
channels = 2;
return channels;
}
/*
- * Need to check if in UN-ganged mode: In such, there are 2 channels,
- * but they are NOT in 128 bit mode and thus the above 'dcl0' status bit
- * will be OFF.
+ * Need to check if in unganged mode: In such, there are 2 channels,
+ * but they are not in 128 bit mode and thus the above 'dclr0' status
+ * bit will be OFF.
*
* Need to check DCT0[0] and DCT1[0] to see if only one of them has
* their CSEnable bit on. If so, then SINGLE DIMM case.
*/
- debugf0("Data WIDTH is NOT 128 bits - need more decoding\n");
+ debugf0("Data width is not 128 bits - need more decoding\n");
/*
* Check DRAM Bank Address Mapping values for each DIMM to see if there
@@ -1283,8 +1213,7 @@ static int f10_early_channel_count(struct amd64_pvt *pvt)
* both controllers since DIMMs can be placed in either one.
*/
for (i = 0; i < ARRAY_SIZE(dbams); i++) {
- err = pci_read_config_dword(pvt->dram_f2_ctl, dbams[i], &dbam);
- if (err)
+ if (amd64_read_pci_cfg(pvt->dram_f2_ctl, dbams[i], &dbam))
goto err_reg;
for (j = 0; j < 4; j++) {
@@ -1295,6 +1224,9 @@ static int f10_early_channel_count(struct amd64_pvt *pvt)
}
}
+ if (channels > 2)
+ channels = 2;
+
debugf0("MCT channel count: %d\n", channels);
return channels;
@@ -1304,9 +1236,16 @@ err_reg:
}
-static int f10_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
+static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
{
- return 1 << (revf_quad_ddr2_shift[dram_map] - PAGE_SHIFT);
+ int *dbam_map;
+
+ if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
+ dbam_map = ddr3_dbam;
+ else
+ dbam_map = ddr2_dbam;
+
+ return dbam_map[cs_mode];
}
/* Enable extended configuration access via 0xCF8 feature */
@@ -1314,7 +1253,7 @@ static void amd64_setup(struct amd64_pvt *pvt)
{
u32 reg;
- pci_read_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
pvt->flags.cf8_extcfg = !!(reg & F10_NB_CFG_LOW_ENABLE_EXT_CFG);
reg |= F10_NB_CFG_LOW_ENABLE_EXT_CFG;
@@ -1326,7 +1265,7 @@ static void amd64_teardown(struct amd64_pvt *pvt)
{
u32 reg;
- pci_read_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
reg &= ~F10_NB_CFG_LOW_ENABLE_EXT_CFG;
if (pvt->flags.cf8_extcfg)
@@ -1355,10 +1294,10 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
high_offset = F10_DRAM_BASE_HIGH + (dram << 3);
/* read the 'raw' DRAM BASE Address register */
- pci_read_config_dword(pvt->addr_f1_ctl, low_offset, &low_base);
+ amd64_read_pci_cfg(pvt->addr_f1_ctl, low_offset, &low_base);
/* Read from the ECS data register */
- pci_read_config_dword(pvt->addr_f1_ctl, high_offset, &high_base);
+ amd64_read_pci_cfg(pvt->addr_f1_ctl, high_offset, &high_base);
/* Extract parts into separate data entries */
pvt->dram_rw_en[dram] = (low_base & 0x3);
@@ -1375,13 +1314,10 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
high_offset = F10_DRAM_LIMIT_HIGH + (dram << 3);
/* read the 'raw' LIMIT registers */
- pci_read_config_dword(pvt->addr_f1_ctl, low_offset, &low_limit);
+ amd64_read_pci_cfg(pvt->addr_f1_ctl, low_offset, &low_limit);
/* Read from the ECS data register for the HIGH portion */
- pci_read_config_dword(pvt->addr_f1_ctl, high_offset, &high_limit);
-
- debugf0(" HW Regs: BASE=0x%08x-%08x LIMIT= 0x%08x-%08x\n",
- high_base, low_base, high_limit, low_limit);
+ amd64_read_pci_cfg(pvt->addr_f1_ctl, high_offset, &high_limit);
pvt->dram_DstNode[dram] = (low_limit & 0x7);
pvt->dram_IntlvSel[dram] = (low_limit >> 8) & 0x7;
@@ -1397,32 +1333,35 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
static void f10_read_dram_ctl_register(struct amd64_pvt *pvt)
{
- int err = 0;
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_LOW,
- &pvt->dram_ctl_select_low);
- if (err) {
- debugf0("Reading F10_DCTL_SEL_LOW failed\n");
- } else {
- debugf0("DRAM_DCTL_SEL_LOW=0x%x DctSelBaseAddr=0x%x\n",
- pvt->dram_ctl_select_low, dct_sel_baseaddr(pvt));
-
- debugf0(" DRAM DCTs are=%s DRAM Is=%s DRAM-Ctl-"
- "sel-hi-range=%s\n",
- (dct_ganging_enabled(pvt) ? "GANGED" : "NOT GANGED"),
- (dct_dram_enabled(pvt) ? "Enabled" : "Disabled"),
- (dct_high_range_enabled(pvt) ? "Enabled" : "Disabled"));
-
- debugf0(" DctDatIntLv=%s MemCleared=%s DctSelIntLvAddr=0x%x\n",
- (dct_data_intlv_enabled(pvt) ? "Enabled" : "Disabled"),
- (dct_memory_cleared(pvt) ? "True " : "False "),
+ if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCTL_SEL_LOW,
+ &pvt->dram_ctl_select_low)) {
+ debugf0("F2x110 (DCTL Sel. Low): 0x%08x, "
+ "High range addresses at: 0x%x\n",
+ pvt->dram_ctl_select_low,
+ dct_sel_baseaddr(pvt));
+
+ debugf0(" DCT mode: %s, All DCTs on: %s\n",
+ (dct_ganging_enabled(pvt) ? "ganged" : "unganged"),
+ (dct_dram_enabled(pvt) ? "yes" : "no"));
+
+ if (!dct_ganging_enabled(pvt))
+ debugf0(" Address range split per DCT: %s\n",
+ (dct_high_range_enabled(pvt) ? "yes" : "no"));
+
+ debugf0(" DCT data interleave for ECC: %s, "
+ "DRAM cleared since last warm reset: %s\n",
+ (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
+ (dct_memory_cleared(pvt) ? "yes" : "no"));
+
+ debugf0(" DCT channel interleave: %s, "
+ "DCT interleave bits selector: 0x%x\n",
+ (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
dct_sel_interleave_addr(pvt));
}
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_HIGH,
- &pvt->dram_ctl_select_high);
- if (err)
- debugf0("Reading F10_DCTL_SEL_HIGH failed\n");
+ amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCTL_SEL_HIGH,
+ &pvt->dram_ctl_select_high);
}
/*
@@ -1706,10 +1645,11 @@ static int f10_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
}
/*
- * This the F10h reference code from AMD to map a @sys_addr to NodeID,
- * CSROW, Channel.
+ * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
+ * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
*
- * The @sys_addr is usually an error address received from the hardware.
+ * The @sys_addr is usually an error address received from the hardware
+ * (MCX_ADDR).
*/
static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
struct err_regs *info,
@@ -1722,133 +1662,76 @@ static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
csrow = f10_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
- if (csrow >= 0) {
- error_address_to_page_and_offset(sys_addr, &page, &offset);
-
- syndrome = HIGH_SYNDROME(info->nbsl) << 8;
- syndrome |= LOW_SYNDROME(info->nbsh);
-
- /*
- * Is CHIPKILL on? If so, then we can attempt to use the
- * syndrome to isolate which channel the error was on.
- */
- if (pvt->nbcfg & K8_NBCFG_CHIPKILL)
- chan = get_channel_from_ecc_syndrome(syndrome);
-
- if (chan >= 0) {
- edac_mc_handle_ce(mci, page, offset, syndrome,
- csrow, chan, EDAC_MOD_STR);
- } else {
- /*
- * Channel unknown, report all channels on this
- * CSROW as failed.
- */
- for (chan = 0; chan < mci->csrows[csrow].nr_channels;
- chan++) {
- edac_mc_handle_ce(mci, page, offset,
- syndrome,
- csrow, chan,
- EDAC_MOD_STR);
- }
- }
-
- } else {
+ if (csrow < 0) {
edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+ return;
}
-}
-/*
- * Input (@index) is the DBAM DIMM value (1 of 4) used as an index into a shift
- * table (revf_quad_ddr2_shift) which starts at 128MB DIMM size. Index of 0
- * indicates an empty DIMM slot, as reported by Hardware on empty slots.
- *
- * Normalize to 128MB by subracting 27 bit shift.
- */
-static int map_dbam_to_csrow_size(int index)
-{
- int mega_bytes = 0;
+ error_address_to_page_and_offset(sys_addr, &page, &offset);
- if (index > 0 && index <= DBAM_MAX_VALUE)
- mega_bytes = ((128 << (revf_quad_ddr2_shift[index]-27)));
+ syndrome = HIGH_SYNDROME(info->nbsl) << 8;
+ syndrome |= LOW_SYNDROME(info->nbsh);
- return mega_bytes;
+ /*
+ * We need the syndromes for channel detection only when we're
+ * ganged. Otherwise @chan should already contain the channel at
+ * this point.
+ */
+ if (dct_ganging_enabled(pvt) && pvt->nbcfg & K8_NBCFG_CHIPKILL)
+ chan = get_channel_from_ecc_syndrome(mci, syndrome);
+
+ if (chan >= 0)
+ edac_mc_handle_ce(mci, page, offset, syndrome, csrow, chan,
+ EDAC_MOD_STR);
+ else
+ /*
+ * Channel unknown, report all channels on this CSROW as failed.
+ */
+ for (chan = 0; chan < mci->csrows[csrow].nr_channels; chan++)
+ edac_mc_handle_ce(mci, page, offset, syndrome,
+ csrow, chan, EDAC_MOD_STR);
}
/*
- * debug routine to display the memory sizes of a DIMM (ganged or not) and it
+ * debug routine to display the memory sizes of all logical DIMMs and its
* CSROWs as well
*/
-static void f10_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt,
- int ganged)
+static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt)
{
int dimm, size0, size1;
u32 dbam;
u32 *dcsb;
- debugf1(" dbam%d: 0x%8.08x CSROW is %s\n", ctrl,
- ctrl ? pvt->dbam1 : pvt->dbam0,
- ganged ? "GANGED - dbam1 not used" : "NON-GANGED");
+ if (boot_cpu_data.x86 == 0xf) {
+ /* K8 families < revF not supported yet */
+ if (pvt->ext_model < K8_REV_F)
+ return;
+ else
+ WARN_ON(ctrl != 0);
+ }
+
+ debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
+ ctrl, ctrl ? pvt->dbam1 : pvt->dbam0);
dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
dcsb = ctrl ? pvt->dcsb1 : pvt->dcsb0;
+ edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
+
/* Dump memory sizes for DIMM and its CSROWs */
for (dimm = 0; dimm < 4; dimm++) {
size0 = 0;
if (dcsb[dimm*2] & K8_DCSB_CS_ENABLE)
- size0 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam));
+ size0 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
size1 = 0;
if (dcsb[dimm*2 + 1] & K8_DCSB_CS_ENABLE)
- size1 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam));
-
- debugf1(" CTRL-%d DIMM-%d=%5dMB CSROW-%d=%5dMB "
- "CSROW-%d=%5dMB\n",
- ctrl,
- dimm,
- size0 + size1,
- dimm * 2,
- size0,
- dimm * 2 + 1,
- size1);
- }
-}
-
-/*
- * Very early hardware probe on pci_probe thread to determine if this module
- * supports the hardware.
- *
- * Return:
- * 0 for OK
- * 1 for error
- */
-static int f10_probe_valid_hardware(struct amd64_pvt *pvt)
-{
- int ret = 0;
-
- /*
- * If we are on a DDR3 machine, we don't know yet if
- * we support that properly at this time
- */
- if ((pvt->dchr0 & F10_DCHR_Ddr3Mode) ||
- (pvt->dchr1 & F10_DCHR_Ddr3Mode)) {
-
- amd64_printk(KERN_WARNING,
- "%s() This machine is running with DDR3 memory. "
- "This is not currently supported. "
- "DCHR0=0x%x DCHR1=0x%x\n",
- __func__, pvt->dchr0, pvt->dchr1);
-
- amd64_printk(KERN_WARNING,
- " Contact '%s' module MAINTAINER to help add"
- " support.\n",
- EDAC_MOD_STR);
-
- ret = 1;
+ size1 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
+ edac_printk(KERN_DEBUG, EDAC_MC, " %d: %5dMB %d: %5dMB\n",
+ dimm * 2, size0, dimm * 2 + 1, size1);
}
- return ret;
}
/*
@@ -1868,11 +1751,11 @@ static struct amd64_family_type amd64_family_types[] = {
.addr_f1_ctl = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
.misc_f3_ctl = PCI_DEVICE_ID_AMD_K8_NB_MISC,
.ops = {
- .early_channel_count = k8_early_channel_count,
- .get_error_address = k8_get_error_address,
- .read_dram_base_limit = k8_read_dram_base_limit,
- .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
- .dbam_map_to_pages = k8_dbam_map_to_pages,
+ .early_channel_count = k8_early_channel_count,
+ .get_error_address = k8_get_error_address,
+ .read_dram_base_limit = k8_read_dram_base_limit,
+ .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
+ .dbam_to_cs = k8_dbam_to_chip_select,
}
},
[F10_CPUS] = {
@@ -1880,13 +1763,12 @@ static struct amd64_family_type amd64_family_types[] = {
.addr_f1_ctl = PCI_DEVICE_ID_AMD_10H_NB_MAP,
.misc_f3_ctl = PCI_DEVICE_ID_AMD_10H_NB_MISC,
.ops = {
- .probe_valid_hardware = f10_probe_valid_hardware,
- .early_channel_count = f10_early_channel_count,
- .get_error_address = f10_get_error_address,
- .read_dram_base_limit = f10_read_dram_base_limit,
- .read_dram_ctl_register = f10_read_dram_ctl_register,
- .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
- .dbam_map_to_pages = f10_dbam_map_to_pages,
+ .early_channel_count = f10_early_channel_count,
+ .get_error_address = f10_get_error_address,
+ .read_dram_base_limit = f10_read_dram_base_limit,
+ .read_dram_ctl_register = f10_read_dram_ctl_register,
+ .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
+ .dbam_to_cs = f10_dbam_to_chip_select,
}
},
[F11_CPUS] = {
@@ -1894,13 +1776,12 @@ static struct amd64_family_type amd64_family_types[] = {
.addr_f1_ctl = PCI_DEVICE_ID_AMD_11H_NB_MAP,
.misc_f3_ctl = PCI_DEVICE_ID_AMD_11H_NB_MISC,
.ops = {
- .probe_valid_hardware = f10_probe_valid_hardware,
- .early_channel_count = f10_early_channel_count,
- .get_error_address = f10_get_error_address,
- .read_dram_base_limit = f10_read_dram_base_limit,
- .read_dram_ctl_register = f10_read_dram_ctl_register,
- .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
- .dbam_map_to_pages = f10_dbam_map_to_pages,
+ .early_channel_count = f10_early_channel_count,
+ .get_error_address = f10_get_error_address,
+ .read_dram_base_limit = f10_read_dram_base_limit,
+ .read_dram_ctl_register = f10_read_dram_ctl_register,
+ .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
+ .dbam_to_cs = f10_dbam_to_chip_select,
}
},
};
@@ -1923,142 +1804,170 @@ static struct pci_dev *pci_get_related_function(unsigned int vendor,
}
/*
- * syndrome mapping table for ECC ChipKill devices
- *
- * The comment in each row is the token (nibble) number that is in error.
- * The least significant nibble of the syndrome is the mask for the bits
- * that are in error (need to be toggled) for the particular nibble.
- *
- * Each row contains 16 entries.
- * The first entry (0th) is the channel number for that row of syndromes.
- * The remaining 15 entries are the syndromes for the respective Error
- * bit mask index.
- *
- * 1st index entry is 0x0001 mask, indicating that the rightmost bit is the
- * bit in error.
- * The 2nd index entry is 0x0010 that the second bit is damaged.
- * The 3rd index entry is 0x0011 indicating that the rightmost 2 bits
- * are damaged.
- * Thus so on until index 15, 0x1111, whose entry has the syndrome
- * indicating that all 4 bits are damaged.
- *
- * A search is performed on this table looking for a given syndrome.
+ * These are tables of eigenvectors (one per line) which can be used for the
+ * construction of the syndrome tables. The modified syndrome search algorithm
+ * uses those to find the symbol in error and thus the DIMM.
*
- * See the AMD documentation for ECC syndromes. This ECC table is valid
- * across all the versions of the AMD64 processors.
- *
- * A fast lookup is to use the LAST four bits of the 16-bit syndrome as a
- * COLUMN index, then search all ROWS of that column, looking for a match
- * with the input syndrome. The ROW value will be the token number.
- *
- * The 0'th entry on that row, can be returned as the CHANNEL (0 or 1) of this
- * error.
+ * Algorithm courtesy of Ross LaFetra from AMD.
*/
-#define NUMBER_ECC_ROWS 36
-static const unsigned short ecc_chipkill_syndromes[NUMBER_ECC_ROWS][16] = {
- /* Channel 0 syndromes */
- {/*0*/ 0, 0xe821, 0x7c32, 0x9413, 0xbb44, 0x5365, 0xc776, 0x2f57,
- 0xdd88, 0x35a9, 0xa1ba, 0x499b, 0x66cc, 0x8eed, 0x1afe, 0xf2df },
- {/*1*/ 0, 0x5d31, 0xa612, 0xfb23, 0x9584, 0xc8b5, 0x3396, 0x6ea7,
- 0xeac8, 0xb7f9, 0x4cda, 0x11eb, 0x7f4c, 0x227d, 0xd95e, 0x846f },
- {/*2*/ 0, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
- 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f },
- {/*3*/ 0, 0x2021, 0x3032, 0x1013, 0x4044, 0x6065, 0x7076, 0x5057,
- 0x8088, 0xa0a9, 0xb0ba, 0x909b, 0xc0cc, 0xe0ed, 0xf0fe, 0xd0df },
- {/*4*/ 0, 0x5041, 0xa082, 0xf0c3, 0x9054, 0xc015, 0x30d6, 0x6097,
- 0xe0a8, 0xb0e9, 0x402a, 0x106b, 0x70fc, 0x20bd, 0xd07e, 0x803f },
- {/*5*/ 0, 0xbe21, 0xd732, 0x6913, 0x2144, 0x9f65, 0xf676, 0x4857,
- 0x3288, 0x8ca9, 0xe5ba, 0x5b9b, 0x13cc, 0xaded, 0xc4fe, 0x7adf },
- {/*6*/ 0, 0x4951, 0x8ea2, 0xc7f3, 0x5394, 0x1ac5, 0xdd36, 0x9467,
- 0xa1e8, 0xe8b9, 0x2f4a, 0x661b, 0xf27c, 0xbb2d, 0x7cde, 0x358f },
- {/*7*/ 0, 0x74e1, 0x9872, 0xec93, 0xd6b4, 0xa255, 0x4ec6, 0x3a27,
- 0x6bd8, 0x1f39, 0xf3aa, 0x874b, 0xbd6c, 0xc98d, 0x251e, 0x51ff },
- {/*8*/ 0, 0x15c1, 0x2a42, 0x3f83, 0xcef4, 0xdb35, 0xe4b6, 0xf177,
- 0x4758, 0x5299, 0x6d1a, 0x78db, 0x89ac, 0x9c6d, 0xa3ee, 0xb62f },
- {/*9*/ 0, 0x3d01, 0x1602, 0x2b03, 0x8504, 0xb805, 0x9306, 0xae07,
- 0xca08, 0xf709, 0xdc0a, 0xe10b, 0x4f0c, 0x720d, 0x590e, 0x640f },
- {/*a*/ 0, 0x9801, 0xec02, 0x7403, 0x6b04, 0xf305, 0x8706, 0x1f07,
- 0xbd08, 0x2509, 0x510a, 0xc90b, 0xd60c, 0x4e0d, 0x3a0e, 0xa20f },
- {/*b*/ 0, 0xd131, 0x6212, 0xb323, 0x3884, 0xe9b5, 0x5a96, 0x8ba7,
- 0x1cc8, 0xcdf9, 0x7eda, 0xafeb, 0x244c, 0xf57d, 0x465e, 0x976f },
- {/*c*/ 0, 0xe1d1, 0x7262, 0x93b3, 0xb834, 0x59e5, 0xca56, 0x2b87,
- 0xdc18, 0x3dc9, 0xae7a, 0x4fab, 0x542c, 0x85fd, 0x164e, 0xf79f },
- {/*d*/ 0, 0x6051, 0xb0a2, 0xd0f3, 0x1094, 0x70c5, 0xa036, 0xc067,
- 0x20e8, 0x40b9, 0x904a, 0x601b, 0x307c, 0x502d, 0x80de, 0xe08f },
- {/*e*/ 0, 0xa4c1, 0xf842, 0x5c83, 0xe6f4, 0x4235, 0x1eb6, 0xba77,
- 0x7b58, 0xdf99, 0x831a, 0x27db, 0x9dac, 0x396d, 0x65ee, 0xc12f },
- {/*f*/ 0, 0x11c1, 0x2242, 0x3383, 0xc8f4, 0xd935, 0xeab6, 0xfb77,
- 0x4c58, 0x5d99, 0x6e1a, 0x7fdb, 0x84ac, 0x956d, 0xa6ee, 0xb72f },
-
- /* Channel 1 syndromes */
- {/*10*/ 1, 0x45d1, 0x8a62, 0xcfb3, 0x5e34, 0x1be5, 0xd456, 0x9187,
- 0xa718, 0xe2c9, 0x2d7a, 0x68ab, 0xf92c, 0xbcfd, 0x734e, 0x369f },
- {/*11*/ 1, 0x63e1, 0xb172, 0xd293, 0x14b4, 0x7755, 0xa5c6, 0xc627,
- 0x28d8, 0x4b39, 0x99aa, 0xfa4b, 0x3c6c, 0x5f8d, 0x8d1e, 0xeeff },
- {/*12*/ 1, 0xb741, 0xd982, 0x6ec3, 0x2254, 0x9515, 0xfbd6, 0x4c97,
- 0x33a8, 0x84e9, 0xea2a, 0x5d6b, 0x11fc, 0xa6bd, 0xc87e, 0x7f3f },
- {/*13*/ 1, 0xdd41, 0x6682, 0xbbc3, 0x3554, 0xe815, 0x53d6, 0xce97,
- 0x1aa8, 0xc7e9, 0x7c2a, 0xa1fb, 0x2ffc, 0xf2bd, 0x497e, 0x943f },
- {/*14*/ 1, 0x2bd1, 0x3d62, 0x16b3, 0x4f34, 0x64e5, 0x7256, 0x5987,
- 0x8518, 0xaec9, 0xb87a, 0x93ab, 0xca2c, 0xe1fd, 0xf74e, 0xdc9f },
- {/*15*/ 1, 0x83c1, 0xc142, 0x4283, 0xa4f4, 0x2735, 0x65b6, 0xe677,
- 0xf858, 0x7b99, 0x391a, 0xbadb, 0x5cac, 0xdf6d, 0x9dee, 0x1e2f },
- {/*16*/ 1, 0x8fd1, 0xc562, 0x4ab3, 0xa934, 0x26e5, 0x6c56, 0xe387,
- 0xfe18, 0x71c9, 0x3b7a, 0xb4ab, 0x572c, 0xd8fd, 0x924e, 0x1d9f },
- {/*17*/ 1, 0x4791, 0x89e2, 0xce73, 0x5264, 0x15f5, 0xdb86, 0x9c17,
- 0xa3b8, 0xe429, 0x2a5a, 0x6dcb, 0xf1dc, 0xb64d, 0x783e, 0x3faf },
- {/*18*/ 1, 0x5781, 0xa9c2, 0xfe43, 0x92a4, 0xc525, 0x3b66, 0x6ce7,
- 0xe3f8, 0xb479, 0x4a3a, 0x1dbb, 0x715c, 0x26dd, 0xd89e, 0x8f1f },
- {/*19*/ 1, 0xbf41, 0xd582, 0x6ac3, 0x2954, 0x9615, 0xfcd6, 0x4397,
- 0x3ea8, 0x81e9, 0xeb2a, 0x546b, 0x17fc, 0xa8bd, 0xc27e, 0x7d3f },
- {/*1a*/ 1, 0x9891, 0xe1e2, 0x7273, 0x6464, 0xf7f5, 0x8586, 0x1617,
- 0xb8b8, 0x2b29, 0x595a, 0xcacb, 0xdcdc, 0x4f4d, 0x3d3e, 0xaeaf },
- {/*1b*/ 1, 0xcce1, 0x4472, 0x8893, 0xfdb4, 0x3f55, 0xb9c6, 0x7527,
- 0x56d8, 0x9a39, 0x12aa, 0xde4b, 0xab6c, 0x678d, 0xef1e, 0x23ff },
- {/*1c*/ 1, 0xa761, 0xf9b2, 0x5ed3, 0xe214, 0x4575, 0x1ba6, 0xbcc7,
- 0x7328, 0xd449, 0x8a9a, 0x2dfb, 0x913c, 0x365d, 0x688e, 0xcfef },
- {/*1d*/ 1, 0xff61, 0x55b2, 0xaad3, 0x7914, 0x8675, 0x2ca6, 0xd3c7,
- 0x9e28, 0x6149, 0xcb9a, 0x34fb, 0xe73c, 0x185d, 0xb28e, 0x4def },
- {/*1e*/ 1, 0x5451, 0xa8a2, 0xfcf3, 0x9694, 0xc2c5, 0x3e36, 0x6a67,
- 0xebe8, 0xbfb9, 0x434a, 0x171b, 0x7d7c, 0x292d, 0xd5de, 0x818f },
- {/*1f*/ 1, 0x6fc1, 0xb542, 0xda83, 0x19f4, 0x7635, 0xacb6, 0xc377,
- 0x2e58, 0x4199, 0x9b1a, 0xf4db, 0x37ac, 0x586d, 0x82ee, 0xed2f },
-
- /* ECC bits are also in the set of tokens and they too can go bad
- * first 2 cover channel 0, while the second 2 cover channel 1
- */
- {/*20*/ 0, 0xbe01, 0xd702, 0x6903, 0x2104, 0x9f05, 0xf606, 0x4807,
- 0x3208, 0x8c09, 0xe50a, 0x5b0b, 0x130c, 0xad0d, 0xc40e, 0x7a0f },
- {/*21*/ 0, 0x4101, 0x8202, 0xc303, 0x5804, 0x1905, 0xda06, 0x9b07,
- 0xac08, 0xed09, 0x2e0a, 0x6f0b, 0x640c, 0xb50d, 0x760e, 0x370f },
- {/*22*/ 1, 0xc441, 0x4882, 0x8cc3, 0xf654, 0x3215, 0xbed6, 0x7a97,
- 0x5ba8, 0x9fe9, 0x132a, 0xd76b, 0xadfc, 0x69bd, 0xe57e, 0x213f },
- {/*23*/ 1, 0x7621, 0x9b32, 0xed13, 0xda44, 0xac65, 0x4176, 0x3757,
- 0x6f88, 0x19a9, 0xf4ba, 0x829b, 0xb5cc, 0xc3ed, 0x2efe, 0x58df }
+static u16 x4_vectors[] = {
+ 0x2f57, 0x1afe, 0x66cc, 0xdd88,
+ 0x11eb, 0x3396, 0x7f4c, 0xeac8,
+ 0x0001, 0x0002, 0x0004, 0x0008,
+ 0x1013, 0x3032, 0x4044, 0x8088,
+ 0x106b, 0x30d6, 0x70fc, 0xe0a8,
+ 0x4857, 0xc4fe, 0x13cc, 0x3288,
+ 0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
+ 0x1f39, 0x251e, 0xbd6c, 0x6bd8,
+ 0x15c1, 0x2a42, 0x89ac, 0x4758,
+ 0x2b03, 0x1602, 0x4f0c, 0xca08,
+ 0x1f07, 0x3a0e, 0x6b04, 0xbd08,
+ 0x8ba7, 0x465e, 0x244c, 0x1cc8,
+ 0x2b87, 0x164e, 0x642c, 0xdc18,
+ 0x40b9, 0x80de, 0x1094, 0x20e8,
+ 0x27db, 0x1eb6, 0x9dac, 0x7b58,
+ 0x11c1, 0x2242, 0x84ac, 0x4c58,
+ 0x1be5, 0x2d7a, 0x5e34, 0xa718,
+ 0x4b39, 0x8d1e, 0x14b4, 0x28d8,
+ 0x4c97, 0xc87e, 0x11fc, 0x33a8,
+ 0x8e97, 0x497e, 0x2ffc, 0x1aa8,
+ 0x16b3, 0x3d62, 0x4f34, 0x8518,
+ 0x1e2f, 0x391a, 0x5cac, 0xf858,
+ 0x1d9f, 0x3b7a, 0x572c, 0xfe18,
+ 0x15f5, 0x2a5a, 0x5264, 0xa3b8,
+ 0x1dbb, 0x3b66, 0x715c, 0xe3f8,
+ 0x4397, 0xc27e, 0x17fc, 0x3ea8,
+ 0x1617, 0x3d3e, 0x6464, 0xb8b8,
+ 0x23ff, 0x12aa, 0xab6c, 0x56d8,
+ 0x2dfb, 0x1ba6, 0x913c, 0x7328,
+ 0x185d, 0x2ca6, 0x7914, 0x9e28,
+ 0x171b, 0x3e36, 0x7d7c, 0xebe8,
+ 0x4199, 0x82ee, 0x19f4, 0x2e58,
+ 0x4807, 0xc40e, 0x130c, 0x3208,
+ 0x1905, 0x2e0a, 0x5804, 0xac08,
+ 0x213f, 0x132a, 0xadfc, 0x5ba8,
+ 0x19a9, 0x2efe, 0xb5cc, 0x6f88,
};
-/*
- * Given the syndrome argument, scan each of the channel tables for a syndrome
- * match. Depending on which table it is found, return the channel number.
- */
-static int get_channel_from_ecc_syndrome(unsigned short syndrome)
+static u16 x8_vectors[] = {
+ 0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
+ 0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
+ 0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
+ 0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
+ 0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
+ 0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
+ 0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
+ 0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
+ 0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
+ 0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
+ 0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
+ 0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
+ 0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
+ 0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
+ 0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
+ 0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
+ 0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
+ 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+ 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
+};
+
+static int decode_syndrome(u16 syndrome, u16 *vectors, int num_vecs,
+ int v_dim)
{
- int row;
- int column;
+ unsigned int i, err_sym;
+
+ for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
+ u16 s = syndrome;
+ int v_idx = err_sym * v_dim;
+ int v_end = (err_sym + 1) * v_dim;
+
+ /* walk over all 16 bits of the syndrome */
+ for (i = 1; i < (1U << 16); i <<= 1) {
- /* Determine column to scan */
- column = syndrome & 0xF;
+ /* if bit is set in that eigenvector... */
+ if (v_idx < v_end && vectors[v_idx] & i) {
+ u16 ev_comp = vectors[v_idx++];
- /* Scan all rows, looking for syndrome, or end of table */
- for (row = 0; row < NUMBER_ECC_ROWS; row++) {
- if (ecc_chipkill_syndromes[row][column] == syndrome)
- return ecc_chipkill_syndromes[row][0];
+ /* ... and bit set in the modified syndrome, */
+ if (s & i) {
+ /* remove it. */
+ s ^= ev_comp;
+
+ if (!s)
+ return err_sym;
+ }
+
+ } else if (s & i)
+ /* can't get to zero, move to next symbol */
+ break;
+ }
}
debugf0("syndrome(%x) not found\n", syndrome);
return -1;
}
+static int map_err_sym_to_channel(int err_sym, int sym_size)
+{
+ if (sym_size == 4)
+ switch (err_sym) {
+ case 0x20:
+ case 0x21:
+ return 0;
+ break;
+ case 0x22:
+ case 0x23:
+ return 1;
+ break;
+ default:
+ return err_sym >> 4;
+ break;
+ }
+ /* x8 symbols */
+ else
+ switch (err_sym) {
+ /* imaginary bits not in a DIMM */
+ case 0x10:
+ WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
+ err_sym);
+ return -1;
+ break;
+
+ case 0x11:
+ return 0;
+ break;
+ case 0x12:
+ return 1;
+ break;
+ default:
+ return err_sym >> 3;
+ break;
+ }
+ return -1;
+}
+
+static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
+{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ u32 value = 0;
+ int err_sym = 0;
+
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, 0x180, &value);
+
+ /* F3x180[EccSymbolSize]=1, x8 symbols */
+ if (boot_cpu_data.x86 == 0x10 &&
+ boot_cpu_data.x86_model > 7 &&
+ value & BIT(25)) {
+ err_sym = decode_syndrome(syndrome, x8_vectors,
+ ARRAY_SIZE(x8_vectors), 8);
+ return map_err_sym_to_channel(err_sym, 8);
+ } else {
+ err_sym = decode_syndrome(syndrome, x4_vectors,
+ ARRAY_SIZE(x4_vectors), 4);
+ return map_err_sym_to_channel(err_sym, 4);
+ }
+}
+
/*
* Check for valid error in the NB Status High register. If so, proceed to read
* NB Status Low, NB Address Low and NB Address High registers and store data
@@ -2073,40 +1982,24 @@ static int amd64_get_error_info_regs(struct mem_ctl_info *mci,
{
struct amd64_pvt *pvt;
struct pci_dev *misc_f3_ctl;
- int err = 0;
pvt = mci->pvt_info;
misc_f3_ctl = pvt->misc_f3_ctl;
- err = pci_read_config_dword(misc_f3_ctl, K8_NBSH, &regs->nbsh);
- if (err)
- goto err_reg;
+ if (amd64_read_pci_cfg(misc_f3_ctl, K8_NBSH, &regs->nbsh))
+ return 0;
if (!(regs->nbsh & K8_NBSH_VALID_BIT))
return 0;
/* valid error, read remaining error information registers */
- err = pci_read_config_dword(misc_f3_ctl, K8_NBSL, &regs->nbsl);
- if (err)
- goto err_reg;
-
- err = pci_read_config_dword(misc_f3_ctl, K8_NBEAL, &regs->nbeal);
- if (err)
- goto err_reg;
-
- err = pci_read_config_dword(misc_f3_ctl, K8_NBEAH, &regs->nbeah);
- if (err)
- goto err_reg;
-
- err = pci_read_config_dword(misc_f3_ctl, K8_NBCFG, &regs->nbcfg);
- if (err)
- goto err_reg;
+ if (amd64_read_pci_cfg(misc_f3_ctl, K8_NBSL, &regs->nbsl) ||
+ amd64_read_pci_cfg(misc_f3_ctl, K8_NBEAL, &regs->nbeal) ||
+ amd64_read_pci_cfg(misc_f3_ctl, K8_NBEAH, &regs->nbeah) ||
+ amd64_read_pci_cfg(misc_f3_ctl, K8_NBCFG, &regs->nbcfg))
+ return 0;
return 1;
-
-err_reg:
- debugf0("Reading error info register failed\n");
- return 0;
}
/*
@@ -2184,7 +2077,7 @@ static void amd64_handle_ce(struct mem_ctl_info *mci,
struct err_regs *info)
{
struct amd64_pvt *pvt = mci->pvt_info;
- u64 SystemAddress;
+ u64 sys_addr;
/* Ensure that the Error Address is VALID */
if ((info->nbsh & K8_NBSH_VALID_ERROR_ADDR) == 0) {
@@ -2194,22 +2087,23 @@ static void amd64_handle_ce(struct mem_ctl_info *mci,
return;
}
- SystemAddress = extract_error_address(mci, info);
+ sys_addr = pvt->ops->get_error_address(mci, info);
amd64_mc_printk(mci, KERN_ERR,
- "CE ERROR_ADDRESS= 0x%llx\n", SystemAddress);
+ "CE ERROR_ADDRESS= 0x%llx\n", sys_addr);
- pvt->ops->map_sysaddr_to_csrow(mci, info, SystemAddress);
+ pvt->ops->map_sysaddr_to_csrow(mci, info, sys_addr);
}
/* Handle any Un-correctable Errors (UEs) */
static void amd64_handle_ue(struct mem_ctl_info *mci,
struct err_regs *info)
{
+ struct amd64_pvt *pvt = mci->pvt_info;
+ struct mem_ctl_info *log_mci, *src_mci = NULL;
int csrow;
- u64 SystemAddress;
+ u64 sys_addr;
u32 page, offset;
- struct mem_ctl_info *log_mci, *src_mci = NULL;
log_mci = mci;
@@ -2220,31 +2114,31 @@ static void amd64_handle_ue(struct mem_ctl_info *mci,
return;
}
- SystemAddress = extract_error_address(mci, info);
+ sys_addr = pvt->ops->get_error_address(mci, info);
/*
* Find out which node the error address belongs to. This may be
* different from the node that detected the error.
*/
- src_mci = find_mc_by_sys_addr(mci, SystemAddress);
+ src_mci = find_mc_by_sys_addr(mci, sys_addr);
if (!src_mci) {
amd64_mc_printk(mci, KERN_CRIT,
"ERROR ADDRESS (0x%lx) value NOT mapped to a MC\n",
- (unsigned long)SystemAddress);
+ (unsigned long)sys_addr);
edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
return;
}
log_mci = src_mci;
- csrow = sys_addr_to_csrow(log_mci, SystemAddress);
+ csrow = sys_addr_to_csrow(log_mci, sys_addr);
if (csrow < 0) {
amd64_mc_printk(mci, KERN_CRIT,
"ERROR_ADDRESS (0x%lx) value NOT mapped to 'csrow'\n",
- (unsigned long)SystemAddress);
+ (unsigned long)sys_addr);
edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
} else {
- error_address_to_page_and_offset(SystemAddress, &page, &offset);
+ error_address_to_page_and_offset(sys_addr, &page, &offset);
edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
}
}
@@ -2384,30 +2278,26 @@ static void amd64_free_mc_sibling_devices(struct amd64_pvt *pvt)
static void amd64_read_mc_registers(struct amd64_pvt *pvt)
{
u64 msr_val;
- int dram, err = 0;
+ int dram;
/*
* Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
* those are Read-As-Zero
*/
- rdmsrl(MSR_K8_TOP_MEM1, msr_val);
- pvt->top_mem = msr_val >> 23;
- debugf0(" TOP_MEM=0x%08llx\n", pvt->top_mem);
+ rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
+ debugf0(" TOP_MEM: 0x%016llx\n", pvt->top_mem);
/* check first whether TOP_MEM2 is enabled */
rdmsrl(MSR_K8_SYSCFG, msr_val);
if (msr_val & (1U << 21)) {
- rdmsrl(MSR_K8_TOP_MEM2, msr_val);
- pvt->top_mem2 = msr_val >> 23;
- debugf0(" TOP_MEM2=0x%08llx\n", pvt->top_mem2);
+ rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
+ debugf0(" TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
} else
debugf0(" TOP_MEM2 disabled.\n");
amd64_cpu_display_info(pvt);
- err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCAP, &pvt->nbcap);
- if (err)
- goto err_reg;
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCAP, &pvt->nbcap);
if (pvt->ops->read_dram_ctl_register)
pvt->ops->read_dram_ctl_register(pvt);
@@ -2425,13 +2315,12 @@ static void amd64_read_mc_registers(struct amd64_pvt *pvt)
* debug output block away.
*/
if (pvt->dram_rw_en[dram] != 0) {
- debugf1(" DRAM_BASE[%d]: 0x%8.08x-%8.08x "
- "DRAM_LIMIT: 0x%8.08x-%8.08x\n",
+ debugf1(" DRAM-BASE[%d]: 0x%016llx "
+ "DRAM-LIMIT: 0x%016llx\n",
dram,
- (u32)(pvt->dram_base[dram] >> 32),
- (u32)(pvt->dram_base[dram] & 0xFFFFFFFF),
- (u32)(pvt->dram_limit[dram] >> 32),
- (u32)(pvt->dram_limit[dram] & 0xFFFFFFFF));
+ pvt->dram_base[dram],
+ pvt->dram_limit[dram]);
+
debugf1(" IntlvEn=%s %s %s "
"IntlvSel=%d DstNode=%d\n",
pvt->dram_IntlvEn[dram] ?
@@ -2445,44 +2334,20 @@ static void amd64_read_mc_registers(struct amd64_pvt *pvt)
amd64_read_dct_base_mask(pvt);
- err = pci_read_config_dword(pvt->addr_f1_ctl, K8_DHAR, &pvt->dhar);
- if (err)
- goto err_reg;
-
+ amd64_read_pci_cfg(pvt->addr_f1_ctl, K8_DHAR, &pvt->dhar);
amd64_read_dbam_reg(pvt);
- err = pci_read_config_dword(pvt->misc_f3_ctl,
- F10_ONLINE_SPARE, &pvt->online_spare);
- if (err)
- goto err_reg;
-
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
- if (err)
- goto err_reg;
+ amd64_read_pci_cfg(pvt->misc_f3_ctl,
+ F10_ONLINE_SPARE, &pvt->online_spare);
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_0, &pvt->dchr0);
- if (err)
- goto err_reg;
+ amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
+ amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCHR_0, &pvt->dchr0);
if (!dct_ganging_enabled(pvt)) {
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_1,
- &pvt->dclr1);
- if (err)
- goto err_reg;
-
- err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_1,
- &pvt->dchr1);
- if (err)
- goto err_reg;
+ amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCLR_1, &pvt->dclr1);
+ amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCHR_1, &pvt->dchr1);
}
-
amd64_dump_misc_regs(pvt);
-
- return;
-
-err_reg:
- debugf0("Reading an MC register failed\n");
-
}
/*
@@ -2521,7 +2386,7 @@ err_reg:
*/
static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
{
- u32 dram_map, nr_pages;
+ u32 cs_mode, nr_pages;
/*
* The math on this doesn't look right on the surface because x/2*4 can
@@ -2530,9 +2395,9 @@ static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
* number of bits to shift the DBAM register to extract the proper CSROW
* field.
*/
- dram_map = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
+ cs_mode = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
- nr_pages = pvt->ops->dbam_map_to_pages(pvt, dram_map);
+ nr_pages = pvt->ops->dbam_to_cs(pvt, cs_mode) << (20 - PAGE_SHIFT);
/*
* If dual channel then double the memory size of single channel.
@@ -2540,7 +2405,7 @@ static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
*/
nr_pages <<= (pvt->channel_count - 1);
- debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, dram_map);
+ debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
debugf0(" nr_pages= %u channel-count = %d\n",
nr_pages, pvt->channel_count);
@@ -2556,13 +2421,11 @@ static int amd64_init_csrows(struct mem_ctl_info *mci)
struct csrow_info *csrow;
struct amd64_pvt *pvt;
u64 input_addr_min, input_addr_max, sys_addr;
- int i, err = 0, empty = 1;
+ int i, empty = 1;
pvt = mci->pvt_info;
- err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &pvt->nbcfg);
- if (err)
- debugf0("Reading K8_NBCFG failed\n");
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &pvt->nbcfg);
debugf0("NBCFG= 0x%x CHIPKILL= %s DRAM ECC= %s\n", pvt->nbcfg,
(pvt->nbcfg & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
@@ -2618,6 +2481,109 @@ static int amd64_init_csrows(struct mem_ctl_info *mci)
return empty;
}
+/* get all cores on this DCT */
+static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu)
+ if (amd_get_nb_id(cpu) == nid)
+ cpumask_set_cpu(cpu, mask);
+}
+
+/* check MCG_CTL on all the cpus on this node */
+static bool amd64_nb_mce_bank_enabled_on_node(int nid)
+{
+ cpumask_var_t mask;
+ struct msr *msrs;
+ int cpu, nbe, idx = 0;
+ bool ret = false;
+
+ if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
+ amd64_printk(KERN_WARNING, "%s: error allocating mask\n",
+ __func__);
+ return false;
+ }
+
+ get_cpus_on_this_dct_cpumask(mask, nid);
+
+ msrs = kzalloc(sizeof(struct msr) * cpumask_weight(mask), GFP_KERNEL);
+ if (!msrs) {
+ amd64_printk(KERN_WARNING, "%s: error allocating msrs\n",
+ __func__);
+ free_cpumask_var(mask);
+ return false;
+ }
+
+ rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
+
+ for_each_cpu(cpu, mask) {
+ nbe = msrs[idx].l & K8_MSR_MCGCTL_NBE;
+
+ debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
+ cpu, msrs[idx].q,
+ (nbe ? "enabled" : "disabled"));
+
+ if (!nbe)
+ goto out;
+
+ idx++;
+ }
+ ret = true;
+
+out:
+ kfree(msrs);
+ free_cpumask_var(mask);
+ return ret;
+}
+
+static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on)
+{
+ cpumask_var_t cmask;
+ struct msr *msrs = NULL;
+ int cpu, idx = 0;
+
+ if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
+ amd64_printk(KERN_WARNING, "%s: error allocating mask\n",
+ __func__);
+ return false;
+ }
+
+ get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id);
+
+ msrs = kzalloc(sizeof(struct msr) * cpumask_weight(cmask), GFP_KERNEL);
+ if (!msrs) {
+ amd64_printk(KERN_WARNING, "%s: error allocating msrs\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
+
+ for_each_cpu(cpu, cmask) {
+
+ if (on) {
+ if (msrs[idx].l & K8_MSR_MCGCTL_NBE)
+ pvt->flags.ecc_report = 1;
+
+ msrs[idx].l |= K8_MSR_MCGCTL_NBE;
+ } else {
+ /*
+ * Turn off ECC reporting only when it was off before
+ */
+ if (!pvt->flags.ecc_report)
+ msrs[idx].l &= ~K8_MSR_MCGCTL_NBE;
+ }
+ idx++;
+ }
+ wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
+
+ kfree(msrs);
+ free_cpumask_var(cmask);
+
+ return 0;
+}
+
/*
* Only if 'ecc_enable_override' is set AND BIOS had ECC disabled, do "we"
* enable it.
@@ -2625,24 +2591,16 @@ static int amd64_init_csrows(struct mem_ctl_info *mci)
static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
{
struct amd64_pvt *pvt = mci->pvt_info;
- const cpumask_t *cpumask = cpumask_of_node(pvt->mc_node_id);
- int cpu, idx = 0, err = 0;
- struct msr msrs[cpumask_weight(cpumask)];
- u32 value;
- u32 mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
+ u32 value, mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
if (!ecc_enable_override)
return;
- memset(msrs, 0, sizeof(msrs));
-
amd64_printk(KERN_WARNING,
"'ecc_enable_override' parameter is active, "
"Enabling AMD ECC hardware now: CAUTION\n");
- err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCTL, &value);
- if (err)
- debugf0("Reading K8_NBCTL failed\n");
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCTL, &value);
/* turn on UECCn and CECCEn bits */
pvt->old_nbctl = value & mask;
@@ -2651,20 +2609,11 @@ static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
value |= mask;
pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCTL, value);
- rdmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
-
- for_each_cpu(cpu, cpumask) {
- if (msrs[idx].l & K8_MSR_MCGCTL_NBE)
- set_bit(idx, &pvt->old_mcgctl);
+ if (amd64_toggle_ecc_err_reporting(pvt, ON))
+ amd64_printk(KERN_WARNING, "Error enabling ECC reporting over "
+ "MCGCTL!\n");
- msrs[idx].l |= K8_MSR_MCGCTL_NBE;
- idx++;
- }
- wrmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
-
- err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
- if (err)
- debugf0("Reading K8_NBCFG failed\n");
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &value);
debugf0("NBCFG(1)= 0x%x CHIPKILL= %s ECC_ENABLE= %s\n", value,
(value & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
@@ -2679,9 +2628,7 @@ static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
value |= K8_NBCFG_ECC_ENABLE;
pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCFG, value);
- err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
- if (err)
- debugf0("Reading K8_NBCFG failed\n");
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &value);
if (!(value & K8_NBCFG_ECC_ENABLE)) {
amd64_printk(KERN_WARNING,
@@ -2701,86 +2648,21 @@ static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
static void amd64_restore_ecc_error_reporting(struct amd64_pvt *pvt)
{
- const cpumask_t *cpumask = cpumask_of_node(pvt->mc_node_id);
- int cpu, idx = 0, err = 0;
- struct msr msrs[cpumask_weight(cpumask)];
- u32 value;
- u32 mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
+ u32 value, mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
if (!pvt->nbctl_mcgctl_saved)
return;
- memset(msrs, 0, sizeof(msrs));
-
- err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCTL, &value);
- if (err)
- debugf0("Reading K8_NBCTL failed\n");
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCTL, &value);
value &= ~mask;
value |= pvt->old_nbctl;
/* restore the NB Enable MCGCTL bit */
pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCTL, value);
- rdmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
-
- for_each_cpu(cpu, cpumask) {
- msrs[idx].l &= ~K8_MSR_MCGCTL_NBE;
- msrs[idx].l |=
- test_bit(idx, &pvt->old_mcgctl) << K8_MSR_MCGCTL_NBE;
- idx++;
- }
-
- wrmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
-}
-
-/* get all cores on this DCT */
-static void get_cpus_on_this_dct_cpumask(cpumask_t *mask, int nid)
-{
- int cpu;
-
- for_each_online_cpu(cpu)
- if (amd_get_nb_id(cpu) == nid)
- cpumask_set_cpu(cpu, mask);
-}
-
-/* check MCG_CTL on all the cpus on this node */
-static bool amd64_nb_mce_bank_enabled_on_node(int nid)
-{
- cpumask_t mask;
- struct msr *msrs;
- int cpu, nbe, idx = 0;
- bool ret = false;
-
- cpumask_clear(&mask);
-
- get_cpus_on_this_dct_cpumask(&mask, nid);
-
- msrs = kzalloc(sizeof(struct msr) * cpumask_weight(&mask), GFP_KERNEL);
- if (!msrs) {
- amd64_printk(KERN_WARNING, "%s: error allocating msrs\n",
- __func__);
- return false;
- }
-
- rdmsr_on_cpus(&mask, MSR_IA32_MCG_CTL, msrs);
-
- for_each_cpu(cpu, &mask) {
- nbe = msrs[idx].l & K8_MSR_MCGCTL_NBE;
-
- debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
- cpu, msrs[idx].q,
- (nbe ? "enabled" : "disabled"));
-
- if (!nbe)
- goto out;
-
- idx++;
- }
- ret = true;
-
-out:
- kfree(msrs);
- return ret;
+ if (amd64_toggle_ecc_err_reporting(pvt, OFF))
+ amd64_printk(KERN_WARNING, "Error restoring ECC reporting over "
+ "MCGCTL!\n");
}
/*
@@ -2797,13 +2679,10 @@ static const char *ecc_warning =
static int amd64_check_ecc_enabled(struct amd64_pvt *pvt)
{
u32 value;
- int err = 0;
u8 ecc_enabled = 0;
bool nb_mce_en = false;
- err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
- if (err)
- debugf0("Reading K8_NBCTL failed\n");
+ amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &value);
ecc_enabled = !!(value & K8_NBCFG_ECC_ENABLE);
if (!ecc_enabled)
@@ -2909,7 +2788,6 @@ static int amd64_probe_one_instance(struct pci_dev *dram_f2_ctl,
pvt->ext_model = boot_cpu_data.x86_model >> 4;
pvt->mc_type_index = mc_type_index;
pvt->ops = family_ops(mc_type_index);
- pvt->old_mcgctl = 0;
/*
* We have the dram_f2_ctl device as an argument, now go reserve its
@@ -2959,17 +2837,10 @@ static int amd64_init_2nd_stage(struct amd64_pvt *pvt)
{
int node_id = pvt->mc_node_id;
struct mem_ctl_info *mci;
- int ret, err = 0;
+ int ret = -ENODEV;
amd64_read_mc_registers(pvt);
- ret = -ENODEV;
- if (pvt->ops->probe_valid_hardware) {
- err = pvt->ops->probe_valid_hardware(pvt);
- if (err)
- goto err_exit;
- }
-
/*
* We need to determine how many memory channels there are. Then use
* that information for calculating the size of the dynamic instance
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
index c6f359a8520..41bc561e598 100644
--- a/drivers/edac/amd64_edac.h
+++ b/drivers/edac/amd64_edac.h
@@ -129,24 +129,22 @@
* sections 3.5.4 and 3.5.5 for more information.
*/
-#define EDAC_AMD64_VERSION " Ver: 3.2.0 " __DATE__
+#define EDAC_AMD64_VERSION " Ver: 3.3.0 " __DATE__
#define EDAC_MOD_STR "amd64_edac"
#define EDAC_MAX_NUMNODES 8
/* Extended Model from CPUID, for CPU Revision numbers */
-#define OPTERON_CPU_LE_REV_C 0
-#define OPTERON_CPU_REV_D 1
-#define OPTERON_CPU_REV_E 2
-
-/* NPT processors have the following Extended Models */
-#define OPTERON_CPU_REV_F 4
-#define OPTERON_CPU_REV_FA 5
+#define K8_REV_D 1
+#define K8_REV_E 2
+#define K8_REV_F 4
/* Hardware limit on ChipSelect rows per MC and processors per system */
#define MAX_CS_COUNT 8
#define DRAM_REG_COUNT 8
+#define ON true
+#define OFF false
/*
* PCI-defined configuration space registers
@@ -241,7 +239,7 @@
#define F10_DCHR_1 0x194
#define F10_DCHR_FOUR_RANK_DIMM BIT(18)
-#define F10_DCHR_Ddr3Mode BIT(8)
+#define DDR3_MODE BIT(8)
#define F10_DCHR_MblMode BIT(6)
@@ -382,14 +380,9 @@ enum {
#define K8_NBCAP_CORES (BIT(12)|BIT(13))
#define K8_NBCAP_CHIPKILL BIT(4)
#define K8_NBCAP_SECDED BIT(3)
-#define K8_NBCAP_8_NODE BIT(2)
-#define K8_NBCAP_DUAL_NODE BIT(1)
#define K8_NBCAP_DCT_DUAL BIT(0)
-/*
- * MSR Regs
- */
-#define K8_MSR_MCGCTL 0x017b
+/* MSRs */
#define K8_MSR_MCGCTL_NBE BIT(4)
#define K8_MSR_MC4CTL 0x0410
@@ -487,7 +480,6 @@ struct amd64_pvt {
/* Save old hw registers' values before we modified them */
u32 nbctl_mcgctl_saved; /* When true, following 2 are valid */
u32 old_nbctl;
- unsigned long old_mcgctl; /* per core on this node */
/* MC Type Index value: socket F vs Family 10h */
u32 mc_type_index;
@@ -495,6 +487,7 @@ struct amd64_pvt {
/* misc settings */
struct flags {
unsigned long cf8_extcfg:1;
+ unsigned long ecc_report:1;
} flags;
};
@@ -504,7 +497,6 @@ struct scrubrate {
};
extern struct scrubrate scrubrates[23];
-extern u32 revf_quad_ddr2_shift[16];
extern const char *tt_msgs[4];
extern const char *ll_msgs[4];
extern const char *rrrr_msgs[16];
@@ -534,17 +526,15 @@ extern struct mcidev_sysfs_attribute amd64_dbg_attrs[NUM_DBG_ATTRS],
* functions and per device encoding/decoding logic.
*/
struct low_ops {
- int (*probe_valid_hardware)(struct amd64_pvt *pvt);
- int (*early_channel_count)(struct amd64_pvt *pvt);
-
- u64 (*get_error_address)(struct mem_ctl_info *mci,
- struct err_regs *info);
- void (*read_dram_base_limit)(struct amd64_pvt *pvt, int dram);
- void (*read_dram_ctl_register)(struct amd64_pvt *pvt);
- void (*map_sysaddr_to_csrow)(struct mem_ctl_info *mci,
- struct err_regs *info,
- u64 SystemAddr);
- int (*dbam_map_to_pages)(struct amd64_pvt *pvt, int dram_map);
+ int (*early_channel_count) (struct amd64_pvt *pvt);
+
+ u64 (*get_error_address) (struct mem_ctl_info *mci,
+ struct err_regs *info);
+ void (*read_dram_base_limit) (struct amd64_pvt *pvt, int dram);
+ void (*read_dram_ctl_register) (struct amd64_pvt *pvt);
+ void (*map_sysaddr_to_csrow) (struct mem_ctl_info *mci,
+ struct err_regs *info, u64 SystemAddr);
+ int (*dbam_to_cs) (struct amd64_pvt *pvt, int cs_mode);
};
struct amd64_family_type {
@@ -566,6 +556,22 @@ static inline struct low_ops *family_ops(int index)
return &amd64_family_types[index].ops;
}
+static inline int amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
+ u32 *val, const char *func)
+{
+ int err = 0;
+
+ err = pci_read_config_dword(pdev, offset, val);
+ if (err)
+ amd64_printk(KERN_WARNING, "%s: error reading F%dx%x.\n",
+ func, PCI_FUNC(pdev->devfn), offset);
+
+ return err;
+}
+
+#define amd64_read_pci_cfg(pdev, offset, val) \
+ amd64_read_pci_cfg_dword(pdev, offset, val, __func__)
+
/*
* For future CPU versions, verify the following as new 'slow' rates appear and
* modify the necessary skip values for the supported CPU.
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index 12f355cafdb..001b2e797fb 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -74,6 +74,7 @@
#ifdef CONFIG_EDAC_DEBUG
extern int edac_debug_level;
+extern const char *edac_mem_types[];
#ifndef CONFIG_EDAC_DEBUG_VERBOSE
#define edac_debug_printk(level, fmt, arg...) \
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index b629c41756f..3630308e7b8 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -76,6 +76,30 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci)
debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
}
+/*
+ * keep those in sync with the enum mem_type
+ */
+const char *edac_mem_types[] = {
+ "Empty csrow",
+ "Reserved csrow type",
+ "Unknown csrow type",
+ "Fast page mode RAM",
+ "Extended data out RAM",
+ "Burst Extended data out RAM",
+ "Single data rate SDRAM",
+ "Registered single data rate SDRAM",
+ "Double data rate SDRAM",
+ "Registered Double data rate SDRAM",
+ "Rambus DRAM",
+ "Unbuffered DDR2 RAM",
+ "Fully buffered DDR2",
+ "Registered DDR2 RAM",
+ "Rambus XDR",
+ "Unbuffered DDR3 RAM",
+ "Registered DDR3 RAM",
+};
+EXPORT_SYMBOL_GPL(edac_mem_types);
+
#endif /* CONFIG_EDAC_DEBUG */
/* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
diff --git a/drivers/edac/edac_mce_amd.c b/drivers/edac/edac_mce_amd.c
index 713ed7d3724..c693fcc2213 100644
--- a/drivers/edac/edac_mce_amd.c
+++ b/drivers/edac/edac_mce_amd.c
@@ -3,7 +3,6 @@
static bool report_gart_errors;
static void (*nb_bus_decoder)(int node_id, struct err_regs *regs);
-static void (*orig_mce_callback)(struct mce *m);
void amd_report_gart_errors(bool v)
{
@@ -307,7 +306,7 @@ void amd_decode_nb_mce(int node_id, struct err_regs *regs, int handle_errors)
* value encoding has changed so interpret those differently
*/
if ((boot_cpu_data.x86 == 0x10) &&
- (boot_cpu_data.x86_model > 8)) {
+ (boot_cpu_data.x86_model > 7)) {
if (regs->nbsh & K8_NBSH_ERR_CPU_VAL)
pr_cont(", core: %u\n", (u8)(regs->nbsh & 0xf));
} else {
@@ -363,8 +362,10 @@ static inline void amd_decode_err_code(unsigned int ec)
pr_warning("Huh? Unknown MCE error 0x%x\n", ec);
}
-static void amd_decode_mce(struct mce *m)
+static int amd_decode_mce(struct notifier_block *nb, unsigned long val,
+ void *data)
{
+ struct mce *m = (struct mce *)data;
struct err_regs regs;
int node, ecc;
@@ -420,20 +421,22 @@ static void amd_decode_mce(struct mce *m)
}
amd_decode_err_code(m->status & 0xffff);
+
+ return NOTIFY_STOP;
}
+static struct notifier_block amd_mce_dec_nb = {
+ .notifier_call = amd_decode_mce,
+};
+
static int __init mce_amd_init(void)
{
/*
* We can decode MCEs for Opteron and later CPUs:
*/
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) &&
- (boot_cpu_data.x86 >= 0xf)) {
- /* safe the default decode mce callback */
- orig_mce_callback = x86_mce_decode_callback;
-
- x86_mce_decode_callback = amd_decode_mce;
- }
+ (boot_cpu_data.x86 >= 0xf))
+ atomic_notifier_chain_register(&x86_mce_decoder_chain, &amd_mce_dec_nb);
return 0;
}
@@ -442,7 +445,7 @@ early_initcall(mce_amd_init);
#ifdef MODULE
static void __exit mce_amd_exit(void)
{
- x86_mce_decode_callback = orig_mce_callback;
+ atomic_notifier_chain_unregister(&x86_mce_decoder_chain, &amd_mce_dec_nb);
}
MODULE_DESCRIPTION("AMD MCE decoder");
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index e4864e894e4..7083bcc1b9c 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -38,15 +38,14 @@
#include "core.h"
-int fw_compute_block_crc(u32 *block)
+int fw_compute_block_crc(__be32 *block)
{
- __be32 be32_block[256];
- int i, length;
+ int length;
+ u16 crc;
- length = (*block >> 16) & 0xff;
- for (i = 0; i < length; i++)
- be32_block[i] = cpu_to_be32(block[i + 1]);
- *block |= crc_itu_t(0, (u8 *) be32_block, length * 4);
+ length = (be32_to_cpu(block[0]) >> 16) & 0xff;
+ crc = crc_itu_t(0, (u8 *)&block[1], length * 4);
+ *block |= cpu_to_be32(crc);
return length;
}
@@ -57,6 +56,8 @@ static LIST_HEAD(card_list);
static LIST_HEAD(descriptor_list);
static int descriptor_count;
+static __be32 tmp_config_rom[256];
+
#define BIB_CRC(v) ((v) << 0)
#define BIB_CRC_LENGTH(v) ((v) << 16)
#define BIB_INFO_LENGTH(v) ((v) << 24)
@@ -72,11 +73,10 @@ static int descriptor_count;
#define BIB_CMC ((1) << 30)
#define BIB_IMC ((1) << 31)
-static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length)
+static size_t generate_config_rom(struct fw_card *card, __be32 *config_rom)
{
struct fw_descriptor *desc;
- static u32 config_rom[256];
- int i, j, length;
+ int i, j, k, length;
/*
* Initialize contents of config rom buffer. On the OHCI
@@ -87,40 +87,39 @@ static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length)
* the version stored in the OHCI registers.
*/
- memset(config_rom, 0, sizeof(config_rom));
- config_rom[0] = BIB_CRC_LENGTH(4) | BIB_INFO_LENGTH(4) | BIB_CRC(0);
- config_rom[1] = 0x31333934;
-
- config_rom[2] =
+ config_rom[0] = cpu_to_be32(
+ BIB_CRC_LENGTH(4) | BIB_INFO_LENGTH(4) | BIB_CRC(0));
+ config_rom[1] = cpu_to_be32(0x31333934);
+ config_rom[2] = cpu_to_be32(
BIB_LINK_SPEED(card->link_speed) |
BIB_GENERATION(card->config_rom_generation++ % 14 + 2) |
BIB_MAX_ROM(2) |
BIB_MAX_RECEIVE(card->max_receive) |
- BIB_BMC | BIB_ISC | BIB_CMC | BIB_IMC;
- config_rom[3] = card->guid >> 32;
- config_rom[4] = card->guid;
+ BIB_BMC | BIB_ISC | BIB_CMC | BIB_IMC);
+ config_rom[3] = cpu_to_be32(card->guid >> 32);
+ config_rom[4] = cpu_to_be32(card->guid);
/* Generate root directory. */
- i = 5;
- config_rom[i++] = 0;
- config_rom[i++] = 0x0c0083c0; /* node capabilities */
- j = i + descriptor_count;
+ config_rom[6] = cpu_to_be32(0x0c0083c0); /* node capabilities */
+ i = 7;
+ j = 7 + descriptor_count;
/* Generate root directory entries for descriptors. */
list_for_each_entry (desc, &descriptor_list, link) {
if (desc->immediate > 0)
- config_rom[i++] = desc->immediate;
- config_rom[i] = desc->key | (j - i);
+ config_rom[i++] = cpu_to_be32(desc->immediate);
+ config_rom[i] = cpu_to_be32(desc->key | (j - i));
i++;
j += desc->length;
}
/* Update root directory length. */
- config_rom[5] = (i - 5 - 1) << 16;
+ config_rom[5] = cpu_to_be32((i - 5 - 1) << 16);
/* End of root directory, now copy in descriptors. */
list_for_each_entry (desc, &descriptor_list, link) {
- memcpy(&config_rom[i], desc->data, desc->length * 4);
+ for (k = 0; k < desc->length; k++)
+ config_rom[i + k] = cpu_to_be32(desc->data[k]);
i += desc->length;
}
@@ -131,20 +130,17 @@ static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length)
for (i = 0; i < j; i += length + 1)
length = fw_compute_block_crc(config_rom + i);
- *config_rom_length = j;
-
- return config_rom;
+ return j;
}
static void update_config_roms(void)
{
struct fw_card *card;
- u32 *config_rom;
size_t length;
list_for_each_entry (card, &card_list, link) {
- config_rom = generate_config_rom(card, &length);
- card->driver->set_config_rom(card, config_rom, length);
+ length = generate_config_rom(card, tmp_config_rom);
+ card->driver->set_config_rom(card, tmp_config_rom, length);
}
}
@@ -211,11 +207,8 @@ static const char gap_count_table[] = {
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
{
- int scheduled;
-
fw_card_get(card);
- scheduled = schedule_delayed_work(&card->work, delay);
- if (!scheduled)
+ if (!schedule_delayed_work(&card->work, delay))
fw_card_put(card);
}
@@ -435,7 +428,6 @@ EXPORT_SYMBOL(fw_card_initialize);
int fw_card_add(struct fw_card *card,
u32 max_receive, u32 link_speed, u64 guid)
{
- u32 *config_rom;
size_t length;
int ret;
@@ -445,8 +437,8 @@ int fw_card_add(struct fw_card *card,
mutex_lock(&card_mutex);
- config_rom = generate_config_rom(card, &length);
- ret = card->driver->enable(card, config_rom, length);
+ length = generate_config_rom(card, tmp_config_rom);
+ ret = card->driver->enable(card, tmp_config_rom, length);
if (ret == 0)
list_add_tail(&card->link, &card_list);
@@ -465,7 +457,8 @@ EXPORT_SYMBOL(fw_card_add);
* shutdown still need to be provided by the card driver.
*/
-static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
+static int dummy_enable(struct fw_card *card,
+ const __be32 *config_rom, size_t length)
{
BUG();
return -1;
@@ -478,7 +471,7 @@ static int dummy_update_phy_reg(struct fw_card *card, int address,
}
static int dummy_set_config_rom(struct fw_card *card,
- u32 *config_rom, size_t length)
+ const __be32 *config_rom, size_t length)
{
/*
* We take the card out of card_list before setting the dummy
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 5089331544e..231e6ee5ba4 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -130,9 +130,22 @@ struct iso_resource {
struct iso_resource_event *e_alloc, *e_dealloc;
};
-static void schedule_iso_resource(struct iso_resource *);
static void release_iso_resource(struct client *, struct client_resource *);
+static void schedule_iso_resource(struct iso_resource *r, unsigned long delay)
+{
+ client_get(r->client);
+ if (!schedule_delayed_work(&r->work, delay))
+ client_put(r->client);
+}
+
+static void schedule_if_iso_resource(struct client_resource *resource)
+{
+ if (resource->release == release_iso_resource)
+ schedule_iso_resource(container_of(resource,
+ struct iso_resource, resource), 0);
+}
+
/*
* dequeue_event() just kfree()'s the event, so the event has to be
* the first field in a struct XYZ_event.
@@ -166,7 +179,7 @@ struct iso_interrupt_event {
struct iso_resource_event {
struct event event;
- struct fw_cdev_event_iso_resource resource;
+ struct fw_cdev_event_iso_resource iso_resource;
};
static inline void __user *u64_to_uptr(__u64 value)
@@ -314,11 +327,8 @@ static void for_each_client(struct fw_device *device,
static int schedule_reallocations(int id, void *p, void *data)
{
- struct client_resource *r = p;
+ schedule_if_iso_resource(p);
- if (r->release == release_iso_resource)
- schedule_iso_resource(container_of(r,
- struct iso_resource, resource));
return 0;
}
@@ -414,9 +424,7 @@ static int add_client_resource(struct client *client,
&resource->handle);
if (ret >= 0) {
client_get(client);
- if (resource->release == release_iso_resource)
- schedule_iso_resource(container_of(resource,
- struct iso_resource, resource));
+ schedule_if_iso_resource(resource);
}
spin_unlock_irqrestore(&client->lock, flags);
@@ -428,26 +436,26 @@ static int add_client_resource(struct client *client,
static int release_client_resource(struct client *client, u32 handle,
client_resource_release_fn_t release,
- struct client_resource **resource)
+ struct client_resource **return_resource)
{
- struct client_resource *r;
+ struct client_resource *resource;
spin_lock_irq(&client->lock);
if (client->in_shutdown)
- r = NULL;
+ resource = NULL;
else
- r = idr_find(&client->resource_idr, handle);
- if (r && r->release == release)
+ resource = idr_find(&client->resource_idr, handle);
+ if (resource && resource->release == release)
idr_remove(&client->resource_idr, handle);
spin_unlock_irq(&client->lock);
- if (!(r && r->release == release))
+ if (!(resource && resource->release == release))
return -EINVAL;
- if (resource)
- *resource = r;
+ if (return_resource)
+ *return_resource = resource;
else
- r->release(client, r);
+ resource->release(client, resource);
client_put(client);
@@ -699,6 +707,7 @@ static int ioctl_send_response(struct client *client, void *buffer)
struct fw_cdev_send_response *request = buffer;
struct client_resource *resource;
struct inbound_transaction_resource *r;
+ int ret = 0;
if (release_client_resource(client, request->handle,
release_request, &resource) < 0)
@@ -708,13 +717,17 @@ static int ioctl_send_response(struct client *client, void *buffer)
resource);
if (request->length < r->length)
r->length = request->length;
- if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
- return -EFAULT;
+
+ if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) {
+ ret = -EFAULT;
+ goto out;
+ }
fw_send_response(client->device->card, r->request, request->rcode);
+ out:
kfree(r);
- return 0;
+ return ret;
}
static int ioctl_initiate_bus_reset(struct client *client, void *buffer)
@@ -1028,8 +1041,7 @@ static void iso_resource_work(struct work_struct *work)
/* Allow 1000ms grace period for other reallocations. */
if (todo == ISO_RES_ALLOC &&
time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) {
- if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3)))
- client_get(client);
+ schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3));
skip = true;
} else {
/* We could be called twice within the same generation. */
@@ -1097,12 +1109,12 @@ static void iso_resource_work(struct work_struct *work)
e = r->e_dealloc;
r->e_dealloc = NULL;
}
- e->resource.handle = r->resource.handle;
- e->resource.channel = channel;
- e->resource.bandwidth = bandwidth;
+ e->iso_resource.handle = r->resource.handle;
+ e->iso_resource.channel = channel;
+ e->iso_resource.bandwidth = bandwidth;
queue_event(client, &e->event,
- &e->resource, sizeof(e->resource), NULL, 0);
+ &e->iso_resource, sizeof(e->iso_resource), NULL, 0);
if (free) {
cancel_delayed_work(&r->work);
@@ -1114,13 +1126,6 @@ static void iso_resource_work(struct work_struct *work)
client_put(client);
}
-static void schedule_iso_resource(struct iso_resource *r)
-{
- client_get(r->client);
- if (!schedule_delayed_work(&r->work, 0))
- client_put(r->client);
-}
-
static void release_iso_resource(struct client *client,
struct client_resource *resource)
{
@@ -1129,7 +1134,7 @@ static void release_iso_resource(struct client *client,
spin_lock_irq(&client->lock);
r->todo = ISO_RES_DEALLOC;
- schedule_iso_resource(r);
+ schedule_iso_resource(r, 0);
spin_unlock_irq(&client->lock);
}
@@ -1162,10 +1167,10 @@ static int init_iso_resource(struct client *client,
r->e_alloc = e1;
r->e_dealloc = e2;
- e1->resource.closure = request->closure;
- e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
- e2->resource.closure = request->closure;
- e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
+ e1->iso_resource.closure = request->closure;
+ e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
+ e2->iso_resource.closure = request->closure;
+ e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
if (todo == ISO_RES_ALLOC) {
r->resource.release = release_iso_resource;
@@ -1175,7 +1180,7 @@ static int init_iso_resource(struct client *client,
} else {
r->resource.release = NULL;
r->resource.handle = -1;
- schedule_iso_resource(r);
+ schedule_iso_resource(r, 0);
}
request->handle = r->resource.handle;
@@ -1295,7 +1300,23 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
static int dispatch_ioctl(struct client *client,
unsigned int cmd, void __user *arg)
{
- char buffer[256];
+ char buffer[sizeof(union {
+ struct fw_cdev_get_info _00;
+ struct fw_cdev_send_request _01;
+ struct fw_cdev_allocate _02;
+ struct fw_cdev_deallocate _03;
+ struct fw_cdev_send_response _04;
+ struct fw_cdev_initiate_bus_reset _05;
+ struct fw_cdev_add_descriptor _06;
+ struct fw_cdev_remove_descriptor _07;
+ struct fw_cdev_create_iso_context _08;
+ struct fw_cdev_queue_iso _09;
+ struct fw_cdev_start_iso _0a;
+ struct fw_cdev_stop_iso _0b;
+ struct fw_cdev_get_cycle_timer _0c;
+ struct fw_cdev_allocate_iso_resource _0d;
+ struct fw_cdev_send_stream_packet _13;
+ })];
int ret;
if (_IOC_TYPE(cmd) != '#' ||
@@ -1390,10 +1411,10 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
static int shutdown_resource(int id, void *p, void *data)
{
- struct client_resource *r = p;
+ struct client_resource *resource = p;
struct client *client = data;
- r->release(client, r);
+ resource->release(client, resource);
client_put(client);
return 0;
@@ -1402,7 +1423,7 @@ static int shutdown_resource(int id, void *p, void *data)
static int fw_device_op_release(struct inode *inode, struct file *file)
{
struct client *client = file->private_data;
- struct event *e, *next_e;
+ struct event *event, *next_event;
mutex_lock(&client->device->client_list_mutex);
list_del(&client->link);
@@ -1423,8 +1444,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
idr_remove_all(&client->resource_idr);
idr_destroy(&client->resource_idr);
- list_for_each_entry_safe(e, next_e, &client->event_list, link)
- kfree(e);
+ list_for_each_entry_safe(event, next_event, &client->event_list, link)
+ kfree(event);
client_put(client);
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index fddf2b35893..93ec64cdeef 100644
--- a/drivers/firewire/core-topology.c
+++ b/drivers/firewire/core-topology.c
@@ -28,9 +28,9 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include <linux/string.h>
#include <asm/atomic.h>
+#include <asm/byteorder.h>
#include <asm/system.h>
#include "core.h"
@@ -183,7 +183,7 @@ static inline struct fw_node *fw_node(struct list_head *l)
* This function builds the tree representation of the topology given
* by the self IDs from the latest bus reset. During the construction
* of the tree, the function checks that the self IDs are valid and
- * internally consistent. On succcess this function returns the
+ * internally consistent. On success this function returns the
* fw_node corresponding to the local card otherwise NULL.
*/
static struct fw_node *build_tree(struct fw_card *card,
@@ -510,13 +510,16 @@ static void update_tree(struct fw_card *card, struct fw_node *root)
static void update_topology_map(struct fw_card *card,
u32 *self_ids, int self_id_count)
{
- int node_count;
+ int node_count = (card->root_node->node_id & 0x3f) + 1;
+ __be32 *map = card->topology_map;
+
+ *map++ = cpu_to_be32((self_id_count + 2) << 16);
+ *map++ = cpu_to_be32(be32_to_cpu(card->topology_map[1]) + 1);
+ *map++ = cpu_to_be32((node_count << 16) | self_id_count);
+
+ while (self_id_count--)
+ *map++ = cpu_to_be32p(self_ids++);
- card->topology_map[1]++;
- node_count = (card->root_node->node_id & 0x3f) + 1;
- card->topology_map[2] = (node_count << 16) | self_id_count;
- card->topology_map[0] = (self_id_count + 2) << 16;
- memcpy(&card->topology_map[3], self_ids, self_id_count * 4);
fw_compute_block_crc(card->topology_map);
}
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index da628c72a46..842739df23e 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -218,12 +218,15 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
packet->header_length = 16;
packet->payload_length = 0;
break;
+
+ default:
+ WARN(1, KERN_ERR "wrong tcode %d", tcode);
}
common:
packet->speed = speed;
packet->generation = generation;
packet->ack = 0;
- packet->payload_bus = 0;
+ packet->payload_mapped = false;
}
/**
@@ -595,11 +598,10 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header,
break;
default:
- BUG();
- return;
+ WARN(1, KERN_ERR "wrong tcode %d", tcode);
}
- response->payload_bus = 0;
+ response->payload_mapped = false;
}
EXPORT_SYMBOL(fw_fill_response);
@@ -810,8 +812,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request
int speed, unsigned long long offset,
void *payload, size_t length, void *callback_data)
{
- int i, start, end;
- __be32 *map;
+ int start;
if (!TCODE_IS_READ_REQUEST(tcode)) {
fw_send_response(card, request, RCODE_TYPE_ERROR);
@@ -824,11 +825,7 @@ static void handle_topology_map(struct fw_card *card, struct fw_request *request
}
start = (offset - topology_map_region.start) / 4;
- end = start + length / 4;
- map = payload;
-
- for (i = 0; i < length / 4; i++)
- map[i] = cpu_to_be32(card->topology_map[start + i]);
+ memcpy(payload, &card->topology_map[start], length);
fw_send_response(card, request, RCODE_COMPLETE);
}
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 7ff6e758515..ed3b1a765c0 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -40,7 +40,8 @@ struct fw_card_driver {
* enable the PHY or set the link_on bit and initiate a bus
* reset.
*/
- int (*enable)(struct fw_card *card, u32 *config_rom, size_t length);
+ int (*enable)(struct fw_card *card,
+ const __be32 *config_rom, size_t length);
int (*update_phy_reg)(struct fw_card *card, int address,
int clear_bits, int set_bits);
@@ -48,10 +49,10 @@ struct fw_card_driver {
/*
* Update the config rom for an enabled card. This function
* should change the config rom that is presented on the bus
- * an initiate a bus reset.
+ * and initiate a bus reset.
*/
int (*set_config_rom)(struct fw_card *card,
- u32 *config_rom, size_t length);
+ const __be32 *config_rom, size_t length);
void (*send_request)(struct fw_card *card, struct fw_packet *packet);
void (*send_response)(struct fw_card *card, struct fw_packet *packet);
@@ -93,7 +94,7 @@ int fw_card_add(struct fw_card *card,
u32 max_receive, u32 link_speed, u64 guid);
void fw_core_remove_card(struct fw_card *card);
int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
-int fw_compute_block_crc(u32 *block);
+int fw_compute_block_crc(__be32 *block);
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
static inline struct fw_card *fw_card_get(struct fw_card *card)
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 94260aa76aa..ae4556f0c0c 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -205,7 +205,7 @@ struct fw_ohci {
dma_addr_t config_rom_bus;
__be32 *next_config_rom;
dma_addr_t next_config_rom_bus;
- u32 next_header;
+ __be32 next_header;
struct ar_context ar_request_ctx;
struct ar_context ar_response_ctx;
@@ -997,7 +997,8 @@ static int at_context_queue_packet(struct context *ctx,
packet->ack = RCODE_SEND_ERROR;
return -1;
}
- packet->payload_bus = payload_bus;
+ packet->payload_bus = payload_bus;
+ packet->payload_mapped = true;
d[2].req_count = cpu_to_le16(packet->payload_length);
d[2].data_address = cpu_to_le32(payload_bus);
@@ -1025,7 +1026,7 @@ static int at_context_queue_packet(struct context *ctx,
*/
if (ohci->generation != packet->generation ||
reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
- if (packet->payload_length > 0)
+ if (packet->payload_mapped)
dma_unmap_single(ohci->card.device, payload_bus,
packet->payload_length, DMA_TO_DEVICE);
packet->ack = RCODE_GENERATION;
@@ -1061,7 +1062,7 @@ static int handle_at_packet(struct context *context,
/* This packet was cancelled, just continue. */
return 1;
- if (packet->payload_bus)
+ if (packet->payload_mapped)
dma_unmap_single(ohci->card.device, packet->payload_bus,
packet->payload_length, DMA_TO_DEVICE);
@@ -1357,8 +1358,9 @@ static void bus_reset_tasklet(unsigned long data)
*/
reg_write(ohci, OHCI1394_BusOptions,
be32_to_cpu(ohci->config_rom[2]));
- ohci->config_rom[0] = cpu_to_be32(ohci->next_header);
- reg_write(ohci, OHCI1394_ConfigROMhdr, ohci->next_header);
+ ohci->config_rom[0] = ohci->next_header;
+ reg_write(ohci, OHCI1394_ConfigROMhdr,
+ be32_to_cpu(ohci->next_header));
}
#ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA
@@ -1477,7 +1479,17 @@ static int software_reset(struct fw_ohci *ohci)
return -EBUSY;
}
-static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
+static void copy_config_rom(__be32 *dest, const __be32 *src, size_t length)
+{
+ size_t size = length * 4;
+
+ memcpy(dest, src, size);
+ if (size < CONFIG_ROM_SIZE)
+ memset(&dest[length], 0, CONFIG_ROM_SIZE - size);
+}
+
+static int ohci_enable(struct fw_card *card,
+ const __be32 *config_rom, size_t length)
{
struct fw_ohci *ohci = fw_ohci(card);
struct pci_dev *dev = to_pci_dev(card->device);
@@ -1579,8 +1591,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
if (ohci->next_config_rom == NULL)
return -ENOMEM;
- memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
- fw_memcpy_to_be32(ohci->next_config_rom, config_rom, length * 4);
+ copy_config_rom(ohci->next_config_rom, config_rom, length);
} else {
/*
* In the suspend case, config_rom is NULL, which
@@ -1590,7 +1601,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
ohci->next_config_rom_bus = ohci->config_rom_bus;
}
- ohci->next_header = be32_to_cpu(ohci->next_config_rom[0]);
+ ohci->next_header = ohci->next_config_rom[0];
ohci->next_config_rom[0] = 0;
reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
reg_write(ohci, OHCI1394_BusOptions,
@@ -1624,7 +1635,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
}
static int ohci_set_config_rom(struct fw_card *card,
- u32 *config_rom, size_t length)
+ const __be32 *config_rom, size_t length)
{
struct fw_ohci *ohci;
unsigned long flags;
@@ -1673,9 +1684,7 @@ static int ohci_set_config_rom(struct fw_card *card,
ohci->next_config_rom = next_config_rom;
ohci->next_config_rom_bus = next_config_rom_bus;
- memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
- fw_memcpy_to_be32(ohci->next_config_rom, config_rom,
- length * 4);
+ copy_config_rom(ohci->next_config_rom, config_rom, length);
ohci->next_header = config_rom[0];
ohci->next_config_rom[0] = 0;
@@ -1729,7 +1738,7 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
if (packet->ack != 0)
goto out;
- if (packet->payload_bus)
+ if (packet->payload_mapped)
dma_unmap_single(ohci->card.device, packet->payload_bus,
packet->payload_length, DMA_TO_DEVICE);
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index 98dbbda3ad4..d485cdd8cba 100644
--- a/drivers/firewire/sbp2.c
+++ b/drivers/firewire/sbp2.c
@@ -820,20 +820,25 @@ static void sbp2_release_target(struct kref *kref)
fw_device_put(device);
}
-static struct workqueue_struct *sbp2_wq;
+static void sbp2_target_get(struct sbp2_target *tgt)
+{
+ kref_get(&tgt->kref);
+}
static void sbp2_target_put(struct sbp2_target *tgt)
{
kref_put(&tgt->kref, sbp2_release_target);
}
+static struct workqueue_struct *sbp2_wq;
+
/*
* Always get the target's kref when scheduling work on one its units.
* Each workqueue job is responsible to call sbp2_target_put() upon return.
*/
static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
{
- kref_get(&lu->tgt->kref);
+ sbp2_target_get(lu->tgt);
if (!queue_delayed_work(sbp2_wq, &lu->work, delay))
sbp2_target_put(lu->tgt);
}
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 938100f14b1..3a2ccb09e2f 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -429,7 +429,7 @@ static bool dmi_matches(const struct dmi_system_id *dmi)
for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) {
int s = dmi->matches[i].slot;
if (s == DMI_NONE)
- continue;
+ break;
if (dmi_ident[s]
&& strstr(dmi_ident[s], dmi->matches[i].substr))
continue;
@@ -440,6 +440,15 @@ static bool dmi_matches(const struct dmi_system_id *dmi)
}
/**
+ * dmi_is_end_of_table - check for end-of-table marker
+ * @dmi: pointer to the dmi_system_id structure to check
+ */
+static bool dmi_is_end_of_table(const struct dmi_system_id *dmi)
+{
+ return dmi->matches[0].slot == DMI_NONE;
+}
+
+/**
* dmi_check_system - check system DMI data
* @list: array of dmi_system_id structures to match against
* All non-null elements of the list must match
@@ -457,7 +466,7 @@ int dmi_check_system(const struct dmi_system_id *list)
int count = 0;
const struct dmi_system_id *d;
- for (d = list; d->ident; d++)
+ for (d = list; !dmi_is_end_of_table(d); d++)
if (dmi_matches(d)) {
count++;
if (d->callback && d->callback(d))
@@ -484,7 +493,7 @@ const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list)
{
const struct dmi_system_id *d;
- for (d = list; d->ident; d++)
+ for (d = list; !dmi_is_end_of_table(d); d++)
if (dmi_matches(d))
return d;
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 4a7bbdbedfc..5124401f266 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -282,7 +282,7 @@ EXPORT_SYMBOL(drm_mode_object_find);
* functions & device file and adds it to the master fd list.
*
* RETURNS:
- * Zero on success, error code on falure.
+ * Zero on success, error code on failure.
*/
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
const struct drm_framebuffer_funcs *funcs)
@@ -2433,7 +2433,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
} else if (connector->funcs->set_property)
ret = connector->funcs->set_property(connector, property, out_resp->value);
- /* store the property value if succesful */
+ /* store the property value if successful */
if (!ret)
drm_connector_property_set_value(connector, property, out_resp->value);
out:
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 917b8377ae2..8c463cf2050 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1310,7 +1310,7 @@ out_free_list:
* i915_gem_release_mmap - remove physical page mappings
* @obj: obj in question
*
- * Preserve the reservation of the mmaping with the DRM core code, but
+ * Preserve the reservation of the mmapping with the DRM core code, but
* relinquish ownership of the pages back to the system.
*
* It is vital that we remove the page mapping if we have mapped a tiled
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index d4823cc8789..371d753e362 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -70,7 +70,7 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
/**
- * Curretly it is assumed that the old framebuffer is reused.
+ * Currently it is assumed that the old framebuffer is reused.
*
* LOCKING
* caller should hold the mode config lock.
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index b04d1e63d43..3118ce274e6 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -899,7 +899,7 @@ static int intel_lid_present(void)
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- check_lid_device, &lid_present, NULL);
+ check_lid_device, NULL, &lid_present, NULL);
return lid_present;
}
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index dba5147f406..24a3dc99716 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2720,7 +2720,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
/* Wrap with our custom algo which switches to DDC mode */
intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
- /* In defaut case sdvo lvds is false */
+ /* In default case sdvo lvds is false */
intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
if (intel_sdvo_output_setup(intel_output,
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index e83927644de..5f48515c77a 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -1141,7 +1141,7 @@ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS {
/* ucTableFormatRevision=1,ucTableContentRevision=2 */
typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 {
USHORT usPixelClock; /* in 10KHz; for bios convenient */
- UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx defintions below */
+ UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx definitions below */
UCHAR ucAction; /* 0: turn off encoder */
/* 1: setup and turn on encoder */
UCHAR ucTruncate; /* bit0=0: Disable truncate */
@@ -1424,7 +1424,7 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO {
/* Structures used in FirmwareInfoTable */
/****************************************************************************/
-/* usBIOSCapability Defintion: */
+/* usBIOSCapability Definition: */
/* Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; */
/* Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; */
/* Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; */
@@ -2386,7 +2386,7 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
} ATOM_ANALOG_TV_INFO_V1_2;
/**************************************************************************/
-/* VRAM usage and their defintions */
+/* VRAM usage and their definitions */
/* One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. */
/* Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. */
@@ -3046,7 +3046,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC 2
#define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
#define ATOM_S0_CRT1_MONOb0 0x01
#define ATOM_S0_CRT1_COLORb0 0x02
#define ATOM_S0_CRT1_MASKb0 (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0)
@@ -3131,7 +3131,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
#define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30
#define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK 0xC0000000L
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
#define ATOM_S2_TV1_STANDARD_MASKb0 0x0F
#define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF
#define ATOM_S2_CRT1_DPMS_STATEb2 0x01
@@ -3190,7 +3190,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
#define ATOM_S3_ALLOW_FAST_PWR_SWITCH 0x40000000L
#define ATOM_S3_RQST_GPU_USE_MIN_PWR 0x80000000L
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
#define ATOM_S3_CRT1_ACTIVEb0 0x01
#define ATOM_S3_LCD1_ACTIVEb0 0x02
#define ATOM_S3_TV1_ACTIVEb0 0x04
@@ -3230,7 +3230,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
#define ATOM_S4_LCD1_REFRESH_MASK 0x0000FF00L
#define ATOM_S4_LCD1_REFRESH_SHIFT 8
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
#define ATOM_S4_LCD1_PANEL_ID_MASKb0 0x0FF
#define ATOM_S4_LCD1_REFRESH_MASKb1 ATOM_S4_LCD1_PANEL_ID_MASKb0
#define ATOM_S4_VRAM_INFO_MASKb2 ATOM_S4_LCD1_PANEL_ID_MASKb0
@@ -3310,7 +3310,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
#define ATOM_S6_VRI_BRIGHTNESS_CHANGE 0x40000000L
#define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK 0x80000000L
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
#define ATOM_S6_DEVICE_CHANGEb0 0x01
#define ATOM_S6_SCALER_CHANGEb0 0x02
#define ATOM_S6_LID_CHANGEb0 0x04
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index f5cf874dc62..36656bd110b 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -677,11 +677,11 @@ int r600_mc_init(struct radeon_device *rdev)
* AGP so that GPU can catch out of VRAM/AGP access
*/
if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
- /* Enought place before */
+ /* Enough place before */
rdev->mc.vram_location = rdev->mc.gtt_location -
rdev->mc.mc_vram_size;
} else if (tmp > rdev->mc.mc_vram_size) {
- /* Enought place after */
+ /* Enough place after */
rdev->mc.vram_location = rdev->mc.gtt_location +
rdev->mc.gtt_size;
} else {
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 66055b3d866..3ba213d1b06 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -59,7 +59,7 @@ static struct fb_ops radeonfb_ops = {
};
/**
- * Curretly it is assumed that the old framebuffer is reused.
+ * Currently it is assumed that the old framebuffer is reused.
*
* LOCKING
* caller should hold the mode config lock.
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
index 38537d971a3..067167cb39c 100644
--- a/drivers/gpu/drm/radeon/radeon_state.c
+++ b/drivers/gpu/drm/radeon/radeon_state.c
@@ -1950,7 +1950,7 @@ static void radeon_apply_surface_regs(int surf_index,
* Note that refcount can be at most 2, since during a free refcount=3
* might mean we have to allocate a new surface which might not always
* be available.
- * For example : we allocate three contigous surfaces ABC. If B is
+ * For example : we allocate three contiguous surfaces ABC. If B is
* freed, we suddenly need two surfaces to store A and C, which might
* not always be available.
*/
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index d2ed896cca0..5a19d529d1c 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -394,7 +394,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo,
new_mem->mem_type == TTM_PL_SYSTEM) ||
(old_mem->mem_type == TTM_PL_SYSTEM &&
new_mem->mem_type == TTM_PL_TT)) {
- /* bind is enought */
+ /* bind is enough */
radeon_move_null(bo, new_mem);
return 0;
}
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 2d124bb5776..fbb0357f1ec 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -833,11 +833,11 @@ int rv770_mc_init(struct radeon_device *rdev)
* AGP so that GPU can catch out of VRAM/AGP access
*/
if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
- /* Enought place before */
+ /* Enough place before */
rdev->mc.vram_location = rdev->mc.gtt_location -
rdev->mc.mc_vram_size;
} else if (tmp > rdev->mc.mc_vram_size) {
- /* Enought place after */
+ /* Enough place after */
rdev->mc.vram_location = rdev->mc.gtt_location +
rdev->mc.gtt_size;
} else {
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index ceae52f45c3..2ecf7d0c64f 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -428,7 +428,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
/*
* We need to use vmap to get the desired page protection
- * or to make the buffer object look contigous.
+ * or to make the buffer object look contiguous.
*/
prot = (mem->placement & TTM_PL_FLAG_CACHED) ?
PAGE_KERNEL :
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 7d05c4bb201..80792d38d25 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -39,8 +39,6 @@
* Version Information
*/
-#define DRIVER_VERSION "v2.6"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
#define DRIVER_DESC "HID core driver"
#define DRIVER_LICENSE "GPL"
@@ -1294,6 +1292,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
@@ -1326,6 +1325,8 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
@@ -1620,6 +1621,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_KYE, 0x0058) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
@@ -1918,5 +1920,8 @@ static void __exit hid_exit(void)
module_init(hid_init);
module_exit(hid_exit);
+MODULE_AUTHOR("Andreas Gal");
+MODULE_AUTHOR("Vojtech Pavlik");
+MODULE_AUTHOR("Jiri Kosina");
MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index 62e9cb10e88..998b6f443d7 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -126,6 +126,8 @@ static const struct hid_device_id cp_devices[] = {
.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2),
.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3),
+ .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
.driver_data = CP_2WHEEL_MOUSE_HACK },
{ }
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index adbef5d069c..3839340e293 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -117,6 +117,7 @@
#define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2
#define USB_DEVICE_ID_CH_COMBATSTICK 0x00f4
#define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff
+#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3
#define USB_VENDOR_ID_CHERRY 0x046a
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
@@ -145,6 +146,7 @@
#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417
#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61
#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
+#define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1
#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a
@@ -304,6 +306,8 @@
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
+#define USB_DEVICE_ID_SPACETRAVELLER 0xc623
+#define USB_DEVICE_ID_SPACENAVIGATOR 0xc626
#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704
#define USB_DEVICE_ID_DINOVO_EDGE 0xc714
#define USB_DEVICE_ID_DINOVO_MINI 0xc71f
@@ -346,6 +350,9 @@
#define USB_VENDOR_ID_NEC 0x073e
#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
+#define USB_VENDOR_ID_NEXTWINDOW 0x1926
+#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN 0x0003
+
#define USB_VENDOR_ID_NTRIG 0x1b96
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 0f870a3243e..9fcd3d017ab 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -33,6 +33,7 @@
#define LG_NOGET 0x100
#define LG_FF 0x200
#define LG_FF2 0x400
+#define LG_RDESC_REL_ABS 0x800
/*
* Certain Logitech keyboards send in report #3 keys which are far
@@ -51,6 +52,13 @@ static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[84] = rdesc[89] = 0x4d;
rdesc[85] = rdesc[90] = 0x10;
}
+ if ((quirks & LG_RDESC_REL_ABS) && rsize >= 50 &&
+ rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
+ rdesc[49] == 0x81 && rdesc[50] == 0x06) {
+ dev_info(&hdev->dev, "fixing up rel/abs in Logitech "
+ "report descriptor\n");
+ rdesc[33] = rdesc[50] = 0x02;
+ }
}
#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
@@ -89,6 +97,22 @@ static int lg_ultrax_remote_mapping(struct hid_input *hi,
return 1;
}
+static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+ return 0;
+
+ switch (usage->hid & HID_USAGE) {
+
+ case 0x00d: lg_map_key_clear(KEY_MEDIA); break;
+ default:
+ return 0;
+
+ }
+ return 1;
+}
+
static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
unsigned long **bit, int *max)
{
@@ -164,6 +188,10 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
lg_ultrax_remote_mapping(hi, usage, bit, max))
return 1;
+ if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
+ lg_dinovo_mapping(hi, usage, bit, max))
+ return 1;
+
if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
return 1;
@@ -303,8 +331,13 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
+ .driver_data = LG_RDESC_REL_ABS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
+ .driver_data = LG_RDESC_REL_ABS },
{ }
};
+
MODULE_DEVICE_TABLE(hid, lg_devices);
static struct hid_driver lg_driver = {
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 03bd703255a..0258289f3b3 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -41,8 +41,6 @@
* Version Information
*/
-#define DRIVER_VERSION "v2.6"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
#define DRIVER_DESC "USB HID core driver"
#define DRIVER_LICENSE "GPL"
@@ -998,7 +996,8 @@ static int usbhid_start(struct hid_device *hid)
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
- usbhid_init_reports(hid);
+ if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
+ usbhid_init_reports(hid);
set_bit(HID_STARTED, &usbhid->iofl);
@@ -1395,8 +1394,7 @@ static int __init hid_init(void)
retval = usb_register(&hid_driver);
if (retval)
goto usb_register_fail;
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
return 0;
usb_register_fail:
@@ -1423,6 +1421,8 @@ static void __exit hid_exit(void)
module_init(hid_init);
module_exit(hid_exit);
-MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_AUTHOR("Andreas Gal");
+MODULE_AUTHOR("Vojtech Pavlik");
+MODULE_AUTHOR("Jiri Kosina");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
index 484e3eec2f8..e565dbe91d9 100644
--- a/drivers/hid/usbhid/hid-pidff.c
+++ b/drivers/hid/usbhid/hid-pidff.c
@@ -1181,12 +1181,11 @@ static void pidff_reset(struct pidff_device *pidff)
usbhid_wait_io(hid);
if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
- int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0];
- while (sim_effects < 2) {
+ while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
if (i++ > 20) {
printk(KERN_WARNING "hid-pidff: device reports "
"%d simultaneous effects\n",
- sim_effects);
+ pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
break;
}
debug("pid_pool requested again");
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 0d9045aa2c4..38773dc2821 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -37,6 +37,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_NATSU, USB_DEVICE_ID_NATSU_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
+ { USB_VENDOR_ID_NEXTWINDOW, USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN, HID_QUIRK_MULTI_INPUT},
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
@@ -53,6 +54,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
@@ -280,7 +282,7 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
if (idVendor == USB_VENDOR_ID_NCR &&
idProduct >= USB_DEVICE_ID_NCR_FIRST &&
idProduct <= USB_DEVICE_ID_NCR_LAST)
- return HID_QUIRK_NOGET;
+ return HID_QUIRK_NO_INIT_REPORTS;
down_read(&dquirks_rwsem);
bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 8b6ee247bfe..867e08433e4 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -450,7 +450,6 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
if (!uref_multi)
return -ENOMEM;
- lock_kernel();
uref = &uref_multi->uref;
if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
if (copy_from_user(uref_multi, user_arg,
@@ -528,7 +527,6 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
case HIDIOCGCOLLECTIONINDEX:
i = field->usage[uref->usage_index].collection_index;
- unlock_kernel();
kfree(uref_multi);
return i;
case HIDIOCGUSAGES:
@@ -547,15 +545,12 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
}
goodreturn:
- unlock_kernel();
kfree(uref_multi);
return 0;
fault:
- unlock_kernel();
kfree(uref_multi);
return -EFAULT;
inval:
- unlock_kernel();
kfree(uref_multi);
return -EINVAL;
}
diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c
index b342926dd7f..f843443ba5c 100644
--- a/drivers/hid/usbhid/usbkbd.c
+++ b/drivers/hid/usbhid/usbkbd.c
@@ -266,7 +266,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
- strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
+ strlcat(kbd->phys, "/input0", sizeof(kbd->phys));
input_dev->name = kbd->name;
input_dev->phys = kbd->phys;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 700e93adeb3..9e640c62ebd 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -191,21 +191,27 @@ config SENSORS_ADT7470
will be called adt7470.
config SENSORS_ADT7473
- tristate "Analog Devices ADT7473"
+ tristate "Analog Devices ADT7473 (DEPRECATED)"
depends on I2C && EXPERIMENTAL
+ select SENSORS_ADT7475
help
If you say yes here you get support for the Analog Devices
ADT7473 temperature monitoring chips.
+ This driver is deprecated, you should use the adt7475 driver
+ instead.
+
This driver can also be built as a module. If so, the module
will be called adt7473.
config SENSORS_ADT7475
- tristate "Analog Devices ADT7475"
+ tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490"
depends on I2C && EXPERIMENTAL
+ select HWMON_VID
help
If you say yes here you get support for the Analog Devices
- ADT7475 hardware monitoring chips.
+ ADT7473, ADT7475, ADT7476 and ADT7490 hardware monitoring
+ chips.
This driver can also be build as a module. If so, the module
will be called adt7475.
@@ -305,12 +311,12 @@ config SENSORS_F71805F
will be called f71805f.
config SENSORS_F71882FG
- tristate "Fintek F71858FG, F71862FG, F71882FG and F8000"
+ tristate "Fintek F71858FG, F71862FG, F71882FG, F71889FG and F8000"
depends on EXPERIMENTAL
help
If you say yes here you get support for hardware monitoring
- features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG
- and F8000 Super-I/O chips.
+ features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG,
+ F71889FG and F8000 Super-I/O chips.
This driver can also be built as a module. If so, the module
will be called f71882fg.
@@ -442,6 +448,15 @@ config SENSORS_LM70
This driver can also be built as a module. If so, the module
will be called lm70.
+config SENSORS_LM73
+ tristate "National Semiconductor LM73"
+ depends on I2C
+ help
+ If you say yes here you get support for National Semiconductor LM73
+ sensor chips.
+ This driver can also be built as a module. If so, the module
+ will be called lm73.
+
config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles"
depends on I2C
@@ -841,7 +856,7 @@ config SENSORS_W83781D
config SENSORS_W83791D
tristate "Winbond W83791D"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
select HWMON_VID
help
If you say yes here you get support for the Winbond W83791D chip.
@@ -1008,6 +1023,12 @@ config SENSORS_APPLESMC
Say Y here if you have an applicable laptop and want to experience
the awesome power of applesmc.
+config SENSORS_MC13783_ADC
+ tristate "Freescale MC13783 ADC"
+ depends on MFD_MC13783
+ help
+ Support for the A/D converter on MC13783 PMIC.
+
if ACPI
comment "ACPI drivers"
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 9f46cb019cc..33c2ee10528 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
obj-$(CONFIG_SENSORS_LM70) += lm70.o
+obj-$(CONFIG_SENSORS_LM73) += lm73.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_LM77) += lm77.o
obj-$(CONFIG_SENSORS_LM78) += lm78.o
@@ -73,6 +74,7 @@ obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
+obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index afc59431812..33acf29531a 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -288,9 +288,8 @@ static int adm1021_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
- int i;
- const char *type_name = "";
- int conv_rate, status, config;
+ const char *type_name;
+ int conv_rate, status, config, man_id, dev_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
pr_debug("adm1021: detect failed, "
@@ -303,62 +302,37 @@ static int adm1021_detect(struct i2c_client *client, int kind,
ADM1021_REG_CONV_RATE_R);
config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R);
- /* Now, we do the remaining detection. */
- if (kind < 0) {
- if ((status & 0x03) != 0x00 || (config & 0x3F) != 0x00
- || (conv_rate & 0xF8) != 0x00) {
- pr_debug("adm1021: detect failed, "
- "chip not detected!\n");
- return -ENODEV;
- }
+ /* Check unused bits */
+ if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) {
+ pr_debug("adm1021: detect failed, chip not detected!\n");
+ return -ENODEV;
}
/* Determine the chip type. */
- if (kind <= 0) {
- i = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID);
- if (i == 0x41)
- if ((i2c_smbus_read_byte_data(client,
- ADM1021_REG_DEV_ID) & 0xF0) == 0x30)
- kind = adm1023;
- else
- kind = adm1021;
- else if (i == 0x49)
- kind = thmc10;
- else if (i == 0x23)
- kind = gl523sm;
- else if ((i == 0x4d) &&
- (i2c_smbus_read_byte_data(client,
- ADM1021_REG_DEV_ID) == 0x01))
- kind = max1617a;
- else if (i == 0x54)
- kind = mc1066;
- /* LM84 Mfr ID in a different place, and it has more unused bits */
- else if (conv_rate == 0x00
- && (kind == 0 /* skip extra detection */
- || ((config & 0x7F) == 0x00
- && (status & 0xAB) == 0x00)))
- kind = lm84;
- else
- kind = max1617;
- }
+ man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID);
+ dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID);
- if (kind == max1617) {
- type_name = "max1617";
- } else if (kind == max1617a) {
+ if (man_id == 0x4d && dev_id == 0x01)
type_name = "max1617a";
- } else if (kind == adm1021) {
- type_name = "adm1021";
- } else if (kind == adm1023) {
- type_name = "adm1023";
- } else if (kind == thmc10) {
+ else if (man_id == 0x41) {
+ if ((dev_id & 0xF0) == 0x30)
+ type_name = "adm1023";
+ else
+ type_name = "adm1021";
+ } else if (man_id == 0x49)
type_name = "thmc10";
- } else if (kind == lm84) {
- type_name = "lm84";
- } else if (kind == gl523sm) {
+ else if (man_id == 0x23)
type_name = "gl523sm";
- } else if (kind == mc1066) {
+ else if (man_id == 0x54)
type_name = "mc1066";
- }
+ /* LM84 Mfr ID in a different place, and it has more unused bits */
+ else if (conv_rate == 0x00
+ && (config & 0x7F) == 0x00
+ && (status & 0xAB) == 0x00)
+ type_name = "lm84";
+ else
+ type_name = "max1617";
+
pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n",
type_name, i2c_adapter_id(adapter), client->addr);
strlcpy(info->type, type_name, I2C_NAME_SIZE);
diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c
index 4db04d603ec..db6ac2b04f6 100644
--- a/drivers/hwmon/adm1025.c
+++ b/drivers/hwmon/adm1025.c
@@ -2,7 +2,7 @@
* adm1025.c
*
* Copyright (C) 2000 Chen-Yuan Wu <gwu@esoft.com>
- * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org>
*
* The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6
* voltages (including its own power source) and up to two temperatures
@@ -413,67 +413,34 @@ static int adm1025_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
- const char *name = "";
- u8 config;
+ const char *name;
+ u8 man_id, chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /*
- * Now we do the remaining detection. A negative kind means that
- * the driver was loaded with no force parameter (default), so we
- * must both detect and identify the chip. A zero kind means that
- * the driver was loaded with the force parameter, the detection
- * step shall be skipped. A positive kind means that the driver
- * was loaded with the force parameter and a given kind of chip is
- * requested, so both the detection and the identification steps
- * are skipped.
- */
- config = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
- if (kind < 0) { /* detection */
- if ((config & 0x80) != 0x00
- || (i2c_smbus_read_byte_data(client,
- ADM1025_REG_STATUS1) & 0xC0) != 0x00
- || (i2c_smbus_read_byte_data(client,
- ADM1025_REG_STATUS2) & 0xBC) != 0x00) {
- dev_dbg(&adapter->dev,
- "ADM1025 detection failed at 0x%02x.\n",
- client->addr);
- return -ENODEV;
- }
+ /* Check for unused bits */
+ if ((i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG) & 0x80)
+ || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS1) & 0xC0)
+ || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS2) & 0xBC)) {
+ dev_dbg(&adapter->dev, "ADM1025 detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
}
- if (kind <= 0) { /* identification */
- u8 man_id, chip_id;
-
- man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID);
-
- if (man_id == 0x41) { /* Analog Devices */
- if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */
- kind = adm1025;
- }
- } else
- if (man_id == 0xA1) { /* Philips */
- if (client->addr != 0x2E
- && (chip_id & 0xF0) == 0x20) { /* NE1619 */
- kind = ne1619;
- }
- }
-
- if (kind <= 0) { /* identification failed */
- dev_info(&adapter->dev,
- "Unsupported chip (man_id=0x%02X, "
- "chip_id=0x%02X).\n", man_id, chip_id);
- return -ENODEV;
- }
- }
+ /* Identification */
+ chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID);
+ if ((chip_id & 0xF0) != 0x20)
+ return -ENODEV;
- if (kind == adm1025) {
+ man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID);
+ if (man_id == 0x41)
name = "adm1025";
- } else if (kind == ne1619) {
+ else if (man_id == 0xA1 && client->addr != 0x2E)
name = "ne1619";
- }
+ else
+ return -ENODEV;
+
strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index ff7de40b6e3..fb5363985e2 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -1672,35 +1672,26 @@ static int adm1026_detect(struct i2c_client *client, int kind,
i2c_adapter_id(client->adapter), client->addr,
company, verstep);
- /* If auto-detecting, Determine the chip type. */
- if (kind <= 0) {
- dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x "
- "...\n", i2c_adapter_id(adapter), address);
- if (company == ADM1026_COMPANY_ANALOG_DEV
- && verstep == ADM1026_VERSTEP_ADM1026) {
- kind = adm1026;
- } else if (company == ADM1026_COMPANY_ANALOG_DEV
- && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "Unrecognized stepping "
- "0x%02x. Defaulting to ADM1026.\n", verstep);
- kind = adm1026;
- } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "Found version/stepping "
- "0x%02x. Assuming generic ADM1026.\n",
- verstep);
- kind = any_chip;
- } else {
- dev_dbg(&adapter->dev, "Autodetection failed\n");
- /* Not an ADM1026 ... */
- if (kind == 0) { /* User used force=x,y */
- dev_err(&adapter->dev, "Generic ADM1026 not "
- "found at %d,0x%02x. Try "
- "force_adm1026.\n",
- i2c_adapter_id(adapter), address);
- }
- return -ENODEV;
- }
+ /* Determine the chip type. */
+ dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x...\n",
+ i2c_adapter_id(adapter), address);
+ if (company == ADM1026_COMPANY_ANALOG_DEV
+ && verstep == ADM1026_VERSTEP_ADM1026) {
+ /* Analog Devices ADM1026 */
+ } else if (company == ADM1026_COMPANY_ANALOG_DEV
+ && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+ dev_err(&adapter->dev, "Unrecognized stepping "
+ "0x%02x. Defaulting to ADM1026.\n", verstep);
+ } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+ dev_err(&adapter->dev, "Found version/stepping "
+ "0x%02x. Assuming generic ADM1026.\n",
+ verstep);
+ } else {
+ dev_dbg(&adapter->dev, "Autodetection failed\n");
+ /* Not an ADM1026... */
+ return -ENODEV;
}
+
strlcpy(info->type, "adm1026", I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
index 36718150b47..ef91e2a4a56 100644
--- a/drivers/hwmon/adm1029.c
+++ b/drivers/hwmon/adm1029.c
@@ -301,59 +301,36 @@ static int adm1029_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ u8 man_id, chip_id, temp_devices_installed, nb_fan_support;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /* Now we do the detection and identification. A negative kind
- * means that the driver was loaded with no force parameter
- * (default), so we must both detect and identify the chip
- * (actually there is only one possible kind of chip for now, adm1029).
- * A zero kind means that the driver was loaded with the force
- * parameter, the detection step shall be skipped. A positive kind
- * means that the driver was loaded with the force parameter and a
- * given kind of chip is requested, so both the detection and the
- * identification steps are skipped. */
-
- /* Default to an adm1029 if forced */
- if (kind == 0)
- kind = adm1029;
-
/* ADM1029 doesn't have CHIP ID, check just MAN ID
* For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED,
* ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values
* documented
*/
- if (kind <= 0) { /* identification */
- u8 man_id, chip_id, temp_devices_installed, nb_fan_support;
-
- man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID);
- temp_devices_installed = i2c_smbus_read_byte_data(client,
+ man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID);
+ temp_devices_installed = i2c_smbus_read_byte_data(client,
ADM1029_REG_TEMP_DEVICES_INSTALLED);
- nb_fan_support = i2c_smbus_read_byte_data(client,
+ nb_fan_support = i2c_smbus_read_byte_data(client,
ADM1029_REG_NB_FAN_SUPPORT);
- /* 0x41 is Analog Devices */
- if (man_id == 0x41 && (temp_devices_installed & 0xf9) == 0x01
- && nb_fan_support == 0x03) {
- if ((chip_id & 0xF0) == 0x00) {
- kind = adm1029;
- } else {
- /* There are no "official" CHIP ID, so actually
- * we use Major/Minor revision for that */
- printk(KERN_INFO
- "adm1029: Unknown major revision %x, "
- "please let us know\n", chip_id);
- }
- }
+ /* 0x41 is Analog Devices */
+ if (man_id != 0x41 || (temp_devices_installed & 0xf9) != 0x01
+ || nb_fan_support != 0x03)
+ return -ENODEV;
- if (kind <= 0) { /* identification failed */
- pr_debug("adm1029: Unsupported chip (man_id=0x%02X, "
- "chip_id=0x%02X)\n", man_id, chip_id);
- return -ENODEV;
- }
+ if ((chip_id & 0xF0) != 0x00) {
+ /* There are no "official" CHIP ID, so actually
+ * we use Major/Minor revision for that */
+ pr_info("adm1029: Unknown major revision %x, "
+ "please let us know\n", chip_id);
+ return -ENODEV;
}
+
strlcpy(info->type, "adm1029", I2C_NAME_SIZE);
return 0;
@@ -432,7 +409,7 @@ static int adm1029_remove(struct i2c_client *client)
}
/*
-function that update the status of the chips (temperature for exemple)
+function that update the status of the chips (temperature for example)
*/
static struct adm1029_data *adm1029_update_device(struct device *dev)
{
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index 56905955352..0e722175aae 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -817,31 +817,19 @@ static int adm1031_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
- const char *name = "";
+ const char *name;
+ int id, co;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind < 0) {
- int id, co;
- id = i2c_smbus_read_byte_data(client, 0x3d);
- co = i2c_smbus_read_byte_data(client, 0x3e);
+ id = i2c_smbus_read_byte_data(client, 0x3d);
+ co = i2c_smbus_read_byte_data(client, 0x3e);
- if (!((id == 0x31 || id == 0x30) && co == 0x41))
- return -ENODEV;
- kind = (id == 0x30) ? adm1030 : adm1031;
- }
-
- if (kind <= 0)
- kind = adm1031;
+ if (!((id == 0x31 || id == 0x30) && co == 0x41))
+ return -ENODEV;
+ name = (id == 0x30) ? "adm1030" : "adm1031";
- /* Given the detected chip type, set the chip name and the
- * auto fan control helper table. */
- if (kind == adm1030) {
- name = "adm1030";
- } else if (kind == adm1031) {
- name = "adm1031";
- }
strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 2444b15f2e9..20e0481cc20 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -556,51 +556,34 @@ static int adm9240_detect(struct i2c_client *new_client, int kind,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind == 0) {
- kind = adm9240;
- }
-
- if (kind < 0) {
-
- /* verify chip: reg address should match i2c address */
- if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR)
- != address) {
- dev_err(&adapter->dev, "detect fail: address match, "
- "0x%02x\n", address);
- return -ENODEV;
- }
-
- /* check known chip manufacturer */
- man_id = i2c_smbus_read_byte_data(new_client,
- ADM9240_REG_MAN_ID);
- if (man_id == 0x23) {
- kind = adm9240;
- } else if (man_id == 0xda) {
- kind = ds1780;
- } else if (man_id == 0x01) {
- kind = lm81;
- } else {
- dev_err(&adapter->dev, "detect fail: unknown manuf, "
- "0x%02x\n", man_id);
- return -ENODEV;
- }
-
- /* successful detect, print chip info */
- die_rev = i2c_smbus_read_byte_data(new_client,
- ADM9240_REG_DIE_REV);
- dev_info(&adapter->dev, "found %s revision %u\n",
- man_id == 0x23 ? "ADM9240" :
- man_id == 0xda ? "DS1780" : "LM81", die_rev);
+ /* verify chip: reg address should match i2c address */
+ if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR)
+ != address) {
+ dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n",
+ address);
+ return -ENODEV;
}
- /* either forced or detected chip kind */
- if (kind == adm9240) {
+ /* check known chip manufacturer */
+ man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID);
+ if (man_id == 0x23) {
name = "adm9240";
- } else if (kind == ds1780) {
+ } else if (man_id == 0xda) {
name = "ds1780";
- } else if (kind == lm81) {
+ } else if (man_id == 0x01) {
name = "lm81";
+ } else {
+ dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n",
+ man_id);
+ return -ENODEV;
}
+
+ /* successful detect, print chip info */
+ die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV);
+ dev_info(&adapter->dev, "found %s revision %u\n",
+ man_id == 0x23 ? "ADM9240" :
+ man_id == 0xda ? "DS1780" : "LM81", die_rev);
+
strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index 5c39b4af1b2..451977bca7d 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -191,6 +191,7 @@ static int ads7828_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int ch;
/* Check we have a valid client */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
@@ -202,20 +203,17 @@ static int ads7828_detect(struct i2c_client *client, int kind,
- Read from the 8 channel addresses
- Check the top 4 bits of each result are not set (12 data bits)
*/
- if (kind < 0) {
- int ch;
- for (ch = 0; ch < ADS7828_NCH; ch++) {
- u16 in_data;
- u8 cmd = channel_cmd_byte(ch);
- in_data = ads7828_read_value(client, cmd);
- if (in_data & 0xF000) {
- printk(KERN_DEBUG
- "%s : Doesn't look like an ads7828 device\n",
- __func__);
- return -ENODEV;
- }
+ for (ch = 0; ch < ADS7828_NCH; ch++) {
+ u16 in_data;
+ u8 cmd = channel_cmd_byte(ch);
+ in_data = ads7828_read_value(client, cmd);
+ if (in_data & 0xF000) {
+ pr_debug("%s : Doesn't look like an ads7828 device\n",
+ __func__);
+ return -ENODEV;
}
}
+
strlcpy(info->type, "ads7828", I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c
index 1852f27bac5..f9c9562b6a9 100644
--- a/drivers/hwmon/adt7462.c
+++ b/drivers/hwmon/adt7462.c
@@ -1906,27 +1906,22 @@ static int adt7462_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int vendor, device, revision;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind <= 0) {
- int vendor, device, revision;
-
- vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR);
- if (vendor != ADT7462_VENDOR)
- return -ENODEV;
+ vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR);
+ if (vendor != ADT7462_VENDOR)
+ return -ENODEV;
- device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE);
- if (device != ADT7462_DEVICE)
- return -ENODEV;
+ device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE);
+ if (device != ADT7462_DEVICE)
+ return -ENODEV;
- revision = i2c_smbus_read_byte_data(client,
- ADT7462_REG_REVISION);
- if (revision != ADT7462_REVISION)
- return -ENODEV;
- } else
- dev_dbg(&adapter->dev, "detection forced\n");
+ revision = i2c_smbus_read_byte_data(client, ADT7462_REG_REVISION);
+ if (revision != ADT7462_REVISION)
+ return -ENODEV;
strlcpy(info->type, "adt7462", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 633e1a1e9d7..32b1750a689 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -1229,27 +1229,22 @@ static int adt7470_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int vendor, device, revision;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind <= 0) {
- int vendor, device, revision;
-
- vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
- if (vendor != ADT7470_VENDOR)
- return -ENODEV;
+ vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
+ if (vendor != ADT7470_VENDOR)
+ return -ENODEV;
- device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
- if (device != ADT7470_DEVICE)
- return -ENODEV;
+ device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
+ if (device != ADT7470_DEVICE)
+ return -ENODEV;
- revision = i2c_smbus_read_byte_data(client,
- ADT7470_REG_REVISION);
- if (revision != ADT7470_REVISION)
- return -ENODEV;
- } else
- dev_dbg(&adapter->dev, "detection forced\n");
+ revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION);
+ if (revision != ADT7470_REVISION)
+ return -ENODEV;
strlcpy(info->type, "adt7470", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c
index 0a6ce2367b4..aea244db974 100644
--- a/drivers/hwmon/adt7473.c
+++ b/drivers/hwmon/adt7473.c
@@ -174,7 +174,6 @@ static const struct i2c_device_id adt7473_id[] = {
{ "adt7473", adt7473 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, adt7473_id);
static struct i2c_driver adt7473_driver = {
.class = I2C_CLASS_HWMON,
@@ -1090,27 +1089,22 @@ static int adt7473_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int vendor, device, revision;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind <= 0) {
- int vendor, device, revision;
-
- vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR);
- if (vendor != ADT7473_VENDOR)
- return -ENODEV;
+ vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR);
+ if (vendor != ADT7473_VENDOR)
+ return -ENODEV;
- device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE);
- if (device != ADT7473_DEVICE)
- return -ENODEV;
+ device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE);
+ if (device != ADT7473_DEVICE)
+ return -ENODEV;
- revision = i2c_smbus_read_byte_data(client,
- ADT7473_REG_REVISION);
- if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69)
- return -ENODEV;
- } else
- dev_dbg(&adapter->dev, "detection forced\n");
+ revision = i2c_smbus_read_byte_data(client, ADT7473_REG_REVISION);
+ if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69)
+ return -ENODEV;
strlcpy(info->type, "adt7473", I2C_NAME_SIZE);
@@ -1171,6 +1165,8 @@ static int adt7473_remove(struct i2c_client *client)
static int __init adt7473_init(void)
{
+ pr_notice("The adt7473 driver is deprecated, please use the adt7475 "
+ "driver instead\n");
return i2c_add_driver(&adt7473_driver);
}
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index b5a95193c69..99abfddedbc 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -3,7 +3,8 @@
* Copyright (C) 2007-2008, Advanced Micro Devices, Inc.
* Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
* Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
-
+ * Copyright (C) 2009 Jean Delvare <khali@linux-fr.org>
+ *
* Derived from the lm83 driver by Jean Delvare
*
* This program is free software; you can redistribute it and/or modify
@@ -17,6 +18,7 @@
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
#include <linux/err.h>
/* Indexes for the sysfs hooks */
@@ -39,7 +41,12 @@
/* 7475 Common Registers */
-#define REG_VOLTAGE_BASE 0x21
+#define REG_DEVREV2 0x12 /* ADT7490 only */
+
+#define REG_VTT 0x1E /* ADT7490 only */
+#define REG_EXTEND3 0x1F /* ADT7490 only */
+
+#define REG_VOLTAGE_BASE 0x20
#define REG_TEMP_BASE 0x25
#define REG_TACH_BASE 0x28
#define REG_PWM_BASE 0x30
@@ -47,12 +54,15 @@
#define REG_DEVID 0x3D
#define REG_VENDID 0x3E
+#define REG_DEVID2 0x3F
#define REG_STATUS1 0x41
#define REG_STATUS2 0x42
-#define REG_VOLTAGE_MIN_BASE 0x46
-#define REG_VOLTAGE_MAX_BASE 0x47
+#define REG_VID 0x43 /* ADT7476 only */
+
+#define REG_VOLTAGE_MIN_BASE 0x44
+#define REG_VOLTAGE_MAX_BASE 0x45
#define REG_TEMP_MIN_BASE 0x4E
#define REG_TEMP_MAX_BASE 0x4F
@@ -73,16 +83,39 @@
#define REG_TEMP_OFFSET_BASE 0x70
+#define REG_CONFIG2 0x73
+
#define REG_EXTEND1 0x76
#define REG_EXTEND2 0x77
+
+#define REG_CONFIG3 0x78
#define REG_CONFIG5 0x7C
+#define REG_CONFIG4 0x7D
+
+#define REG_STATUS4 0x81 /* ADT7490 only */
+
+#define REG_VTT_MIN 0x84 /* ADT7490 only */
+#define REG_VTT_MAX 0x86 /* ADT7490 only */
+
+#define VID_VIDSEL 0x80 /* ADT7476 only */
+
+#define CONFIG2_ATTN 0x20
+
+#define CONFIG3_SMBALERT 0x01
+#define CONFIG3_THERM 0x02
+
+#define CONFIG4_PINFUNC 0x03
+#define CONFIG4_MAXDUTY 0x08
+#define CONFIG4_ATTN_IN10 0x30
+#define CONFIG4_ATTN_IN43 0xC0
#define CONFIG5_TWOSCOMP 0x01
#define CONFIG5_TEMPOFFSET 0x02
+#define CONFIG5_VIDGPIO 0x10 /* ADT7476 only */
/* ADT7475 Settings */
-#define ADT7475_VOLTAGE_COUNT 2
+#define ADT7475_VOLTAGE_COUNT 5 /* Not counting Vtt */
#define ADT7475_TEMP_COUNT 3
#define ADT7475_TACH_COUNT 4
#define ADT7475_PWM_COUNT 3
@@ -113,12 +146,15 @@
#define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx))
#define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx))
-static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END };
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
-I2C_CLIENT_INSMOD_1(adt7475);
+I2C_CLIENT_INSMOD_4(adt7473, adt7475, adt7476, adt7490);
static const struct i2c_device_id adt7475_id[] = {
+ { "adt7473", adt7473 },
{ "adt7475", adt7475 },
+ { "adt7476", adt7476 },
+ { "adt7490", adt7490 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adt7475_id);
@@ -131,15 +167,24 @@ struct adt7475_data {
unsigned long limits_updated;
char valid;
+ u8 config4;
u8 config5;
- u16 alarms;
- u16 voltage[3][3];
+ u8 has_voltage;
+ u8 bypass_attn; /* Bypass voltage attenuator */
+ u8 has_pwm2:1;
+ u8 has_fan4:1;
+ u8 has_vid:1;
+ u32 alarms;
+ u16 voltage[3][6];
u16 temp[7][3];
u16 tach[2][4];
u8 pwm[4][3];
u8 range[3];
u8 pwmctl[3];
u8 pwmchan[3];
+
+ u8 vid;
+ u8 vrm;
};
static struct i2c_driver adt7475_driver;
@@ -196,26 +241,35 @@ static inline u16 rpm2tach(unsigned long rpm)
return SENSORS_LIMIT((90000 * 60) / rpm, 1, 0xFFFF);
}
-static inline int reg2vcc(u16 reg)
-{
- return (4296 * reg) / 1000;
-}
+/* Scaling factors for voltage inputs, taken from the ADT7490 datasheet */
+static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 1][2] = {
+ { 45, 94 }, /* +2.5V */
+ { 175, 525 }, /* Vccp */
+ { 68, 71 }, /* Vcc */
+ { 93, 47 }, /* +5V */
+ { 120, 20 }, /* +12V */
+ { 45, 45 }, /* Vtt */
+};
-static inline int reg2vccp(u16 reg)
+static inline int reg2volt(int channel, u16 reg, u8 bypass_attn)
{
- return (2929 * reg) / 1000;
-}
+ const int *r = adt7473_in_scaling[channel];
-static inline u16 vcc2reg(long vcc)
-{
- vcc = SENSORS_LIMIT(vcc, 0, 4396);
- return (vcc * 1000) / 4296;
+ if (bypass_attn & (1 << channel))
+ return DIV_ROUND_CLOSEST(reg * 2250, 1024);
+ return DIV_ROUND_CLOSEST(reg * (r[0] + r[1]) * 2250, r[1] * 1024);
}
-static inline u16 vccp2reg(long vcc)
+static inline u16 volt2reg(int channel, long volt, u8 bypass_attn)
{
- vcc = SENSORS_LIMIT(vcc, 0, 2998);
- return (vcc * 1000) / 2929;
+ const int *r = adt7473_in_scaling[channel];
+ long reg;
+
+ if (bypass_attn & (1 << channel))
+ reg = (volt * 1024) / 2250;
+ else
+ reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250);
+ return SENSORS_LIMIT(reg, 0, 1023) & (0xff << 2);
}
static u16 adt7475_read_word(struct i2c_client *client, int reg)
@@ -271,12 +325,11 @@ static ssize_t show_voltage(struct device *dev, struct device_attribute *attr,
switch (sattr->nr) {
case ALARM:
return sprintf(buf, "%d\n",
- (data->alarms >> (sattr->index + 1)) & 1);
+ (data->alarms >> sattr->index) & 1);
default:
val = data->voltage[sattr->nr][sattr->index];
return sprintf(buf, "%d\n",
- sattr->index ==
- 0 ? reg2vccp(val) : reg2vcc(val));
+ reg2volt(sattr->index, val, data->bypass_attn));
}
}
@@ -296,12 +349,19 @@ static ssize_t set_voltage(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->lock);
data->voltage[sattr->nr][sattr->index] =
- sattr->index ? vcc2reg(val) : vccp2reg(val);
+ volt2reg(sattr->index, val, data->bypass_attn);
- if (sattr->nr == MIN)
- reg = VOLTAGE_MIN_REG(sattr->index);
- else
- reg = VOLTAGE_MAX_REG(sattr->index);
+ if (sattr->index < ADT7475_VOLTAGE_COUNT) {
+ if (sattr->nr == MIN)
+ reg = VOLTAGE_MIN_REG(sattr->index);
+ else
+ reg = VOLTAGE_MAX_REG(sattr->index);
+ } else {
+ if (sattr->nr == MIN)
+ reg = REG_VTT_MIN;
+ else
+ reg = REG_VTT_MAX;
+ }
i2c_smbus_write_byte_data(client, reg,
data->voltage[sattr->nr][sattr->index] >> 2);
@@ -778,18 +838,103 @@ static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr,
return count;
}
-static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 0);
-static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage,
+static ssize_t show_pwm_at_crit(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+ return sprintf(buf, "%d\n", !!(data->config4 & CONFIG4_MAXDUTY));
+}
+
+static ssize_t set_pwm_at_crit(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adt7475_data *data = i2c_get_clientdata(client);
+ long val;
+
+ if (strict_strtol(buf, 10, &val))
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ data->config4 = i2c_smbus_read_byte_data(client, REG_CONFIG4);
+ if (val)
+ data->config4 |= CONFIG4_MAXDUTY;
+ else
+ data->config4 &= ~CONFIG4_MAXDUTY;
+ i2c_smbus_write_byte_data(client, REG_CONFIG4, data->config4);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t show_vrm(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", (int)data->vrm);
+}
+
+static ssize_t set_vrm(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7475_data *data = dev_get_drvdata(dev);
+ long val;
+
+ if (strict_strtol(buf, 10, &val))
+ return -EINVAL;
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ data->vrm = val;
+
+ return count;
+}
+
+static ssize_t show_vid(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct adt7475_data *data = adt7475_update_device(dev);
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_voltage, NULL, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_voltage,
set_voltage, MAX, 0);
-static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage,
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_voltage,
set_voltage, MIN, 0);
-static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0);
-static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 1);
-static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage,
+static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0);
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage,
set_voltage, MAX, 1);
-static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage,
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage,
set_voltage, MIN, 1);
-static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MAX, 2);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MIN, 2);
+static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 2);
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_voltage, NULL, INPUT, 3);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MAX, 3);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MIN, 3);
+static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_voltage, NULL, ALARM, 3);
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_voltage, NULL, INPUT, 4);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MAX, 4);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MIN, 4);
+static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_voltage, NULL, ALARM, 8);
+static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_voltage, NULL, INPUT, 5);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MAX, 5);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_voltage,
+ set_voltage, MIN, 5);
+static SENSOR_DEVICE_ATTR_2(in5_alarm, S_IRUGO, show_voltage, NULL, ALARM, 31);
static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, INPUT, 0);
static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, show_temp, NULL, ALARM, 0);
static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_temp, NULL, FAULT, 0);
@@ -893,6 +1038,13 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm,
static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm,
set_pwm, MAX, 2);
+/* Non-standard name, might need revisiting */
+static DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO,
+ show_pwm_at_crit, set_pwm_at_crit);
+
+static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, set_vrm);
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
static struct attribute *adt7475_attrs[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in1_max.dev_attr.attr,
@@ -940,60 +1092,156 @@ static struct attribute *adt7475_attrs[] = {
&sensor_dev_attr_fan3_input.dev_attr.attr,
&sensor_dev_attr_fan3_min.dev_attr.attr,
&sensor_dev_attr_fan3_alarm.dev_attr.attr,
- &sensor_dev_attr_fan4_input.dev_attr.attr,
- &sensor_dev_attr_fan4_min.dev_attr.attr,
- &sensor_dev_attr_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
&sensor_dev_attr_pwm1_freq.dev_attr.attr,
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
- &sensor_dev_attr_pwm2.dev_attr.attr,
- &sensor_dev_attr_pwm2_freq.dev_attr.attr,
- &sensor_dev_attr_pwm2_enable.dev_attr.attr,
- &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
- &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
- &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm3_freq.dev_attr.attr,
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+ &dev_attr_pwm_use_point2_pwm_at_crit.attr,
NULL,
};
-struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
+static struct attribute *fan4_attrs[] = {
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *pwm2_attrs[] = {
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in0_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in3_attrs[] = {
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in4_attrs[] = {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *in5_attrs[] = {
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *vid_attrs[] = {
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
+static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs };
+static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs };
+static struct attribute_group in0_attr_group = { .attrs = in0_attrs };
+static struct attribute_group in3_attr_group = { .attrs = in3_attrs };
+static struct attribute_group in4_attr_group = { .attrs = in4_attrs };
+static struct attribute_group in5_attr_group = { .attrs = in5_attrs };
+static struct attribute_group vid_attr_group = { .attrs = vid_attrs };
static int adt7475_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int vendid, devid, devid2;
+ const char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind <= 0) {
- if (adt7475_read(REG_VENDID) != 0x41 ||
- adt7475_read(REG_DEVID) != 0x75) {
- dev_err(&adapter->dev,
- "Couldn't detect a adt7475 part at 0x%02x\n",
- (unsigned int)client->addr);
- return -ENODEV;
- }
+ vendid = adt7475_read(REG_VENDID);
+ devid2 = adt7475_read(REG_DEVID2);
+ if (vendid != 0x41 || /* Analog Devices */
+ (devid2 & 0xf8) != 0x68)
+ return -ENODEV;
+
+ devid = adt7475_read(REG_DEVID);
+ if (devid == 0x73)
+ name = "adt7473";
+ else if (devid == 0x75 && client->addr == 0x2e)
+ name = "adt7475";
+ else if (devid == 0x76)
+ name = "adt7476";
+ else if ((devid2 & 0xfc) == 0x6c)
+ name = "adt7490";
+ else {
+ dev_dbg(&adapter->dev,
+ "Couldn't detect an ADT7473/75/76/90 part at "
+ "0x%02x\n", (unsigned int)client->addr);
+ return -ENODEV;
}
- strlcpy(info->type, adt7475_id[0].name, I2C_NAME_SIZE);
+ strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
+static void adt7475_remove_files(struct i2c_client *client,
+ struct adt7475_data *data)
+{
+ sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group);
+ if (data->has_fan4)
+ sysfs_remove_group(&client->dev.kobj, &fan4_attr_group);
+ if (data->has_pwm2)
+ sysfs_remove_group(&client->dev.kobj, &pwm2_attr_group);
+ if (data->has_voltage & (1 << 0))
+ sysfs_remove_group(&client->dev.kobj, &in0_attr_group);
+ if (data->has_voltage & (1 << 3))
+ sysfs_remove_group(&client->dev.kobj, &in3_attr_group);
+ if (data->has_voltage & (1 << 4))
+ sysfs_remove_group(&client->dev.kobj, &in4_attr_group);
+ if (data->has_voltage & (1 << 5))
+ sysfs_remove_group(&client->dev.kobj, &in5_attr_group);
+ if (data->has_vid)
+ sysfs_remove_group(&client->dev.kobj, &vid_attr_group);
+}
+
static int adt7475_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ static const char *names[] = {
+ [adt7473] = "ADT7473",
+ [adt7475] = "ADT7475",
+ [adt7476] = "ADT7476",
+ [adt7490] = "ADT7490",
+ };
+
struct adt7475_data *data;
- int i, ret = 0;
+ int i, ret = 0, revision;
+ u8 config2, config3;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
@@ -1002,6 +1250,70 @@ static int adt7475_probe(struct i2c_client *client,
mutex_init(&data->lock);
i2c_set_clientdata(client, data);
+ /* Initialize device-specific values */
+ switch (id->driver_data) {
+ case adt7476:
+ data->has_voltage = 0x0e; /* in1 to in3 */
+ revision = adt7475_read(REG_DEVID2) & 0x07;
+ break;
+ case adt7490:
+ data->has_voltage = 0x3e; /* in1 to in5 */
+ revision = adt7475_read(REG_DEVID2) & 0x03;
+ if (revision == 0x03)
+ revision += adt7475_read(REG_DEVREV2);
+ break;
+ default:
+ data->has_voltage = 0x06; /* in1, in2 */
+ revision = adt7475_read(REG_DEVID2) & 0x07;
+ }
+
+ config3 = adt7475_read(REG_CONFIG3);
+ /* Pin PWM2 may alternatively be used for ALERT output */
+ if (!(config3 & CONFIG3_SMBALERT))
+ data->has_pwm2 = 1;
+ /* Meaning of this bit is inverted for the ADT7473-1 */
+ if (id->driver_data == adt7473 && revision >= 1)
+ data->has_pwm2 = !data->has_pwm2;
+
+ data->config4 = adt7475_read(REG_CONFIG4);
+ /* Pin TACH4 may alternatively be used for THERM */
+ if ((data->config4 & CONFIG4_PINFUNC) == 0x0)
+ data->has_fan4 = 1;
+
+ /* THERM configuration is more complex on the ADT7476 and ADT7490,
+ because 2 different pins (TACH4 and +2.5 Vin) can be used for
+ this function */
+ if (id->driver_data == adt7490) {
+ if ((data->config4 & CONFIG4_PINFUNC) == 0x1 &&
+ !(config3 & CONFIG3_THERM))
+ data->has_fan4 = 1;
+ }
+ if (id->driver_data == adt7476 || id->driver_data == adt7490) {
+ if (!(config3 & CONFIG3_THERM) ||
+ (data->config4 & CONFIG4_PINFUNC) == 0x1)
+ data->has_voltage |= (1 << 0); /* in0 */
+ }
+
+ /* On the ADT7476, the +12V input pin may instead be used as VID5,
+ and VID pins may alternatively be used as GPIO */
+ if (id->driver_data == adt7476) {
+ u8 vid = adt7475_read(REG_VID);
+ if (!(vid & VID_VIDSEL))
+ data->has_voltage |= (1 << 4); /* in4 */
+
+ data->has_vid = !(adt7475_read(REG_CONFIG5) & CONFIG5_VIDGPIO);
+ }
+
+ /* Voltage attenuators can be bypassed, globally or individually */
+ config2 = adt7475_read(REG_CONFIG2);
+ if (config2 & CONFIG2_ATTN) {
+ data->bypass_attn = (0x3 << 3) | 0x3;
+ } else {
+ data->bypass_attn = ((data->config4 & CONFIG4_ATTN_IN10) >> 4) |
+ ((data->config4 & CONFIG4_ATTN_IN43) >> 3);
+ }
+ data->bypass_attn &= data->has_voltage;
+
/* Call adt7475_read_pwm for all pwm's as this will reprogram any
pwm's which are disabled to manual mode with 0% duty cycle */
for (i = 0; i < ADT7475_PWM_COUNT; i++)
@@ -1011,16 +1323,70 @@ static int adt7475_probe(struct i2c_client *client,
if (ret)
goto efree;
+ /* Features that can be disabled individually */
+ if (data->has_fan4) {
+ ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group);
+ if (ret)
+ goto eremove;
+ }
+ if (data->has_pwm2) {
+ ret = sysfs_create_group(&client->dev.kobj, &pwm2_attr_group);
+ if (ret)
+ goto eremove;
+ }
+ if (data->has_voltage & (1 << 0)) {
+ ret = sysfs_create_group(&client->dev.kobj, &in0_attr_group);
+ if (ret)
+ goto eremove;
+ }
+ if (data->has_voltage & (1 << 3)) {
+ ret = sysfs_create_group(&client->dev.kobj, &in3_attr_group);
+ if (ret)
+ goto eremove;
+ }
+ if (data->has_voltage & (1 << 4)) {
+ ret = sysfs_create_group(&client->dev.kobj, &in4_attr_group);
+ if (ret)
+ goto eremove;
+ }
+ if (data->has_voltage & (1 << 5)) {
+ ret = sysfs_create_group(&client->dev.kobj, &in5_attr_group);
+ if (ret)
+ goto eremove;
+ }
+ if (data->has_vid) {
+ data->vrm = vid_which_vrm();
+ ret = sysfs_create_group(&client->dev.kobj, &vid_attr_group);
+ if (ret)
+ goto eremove;
+ }
+
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev);
goto eremove;
}
+ dev_info(&client->dev, "%s device, revision %d\n",
+ names[id->driver_data], revision);
+ if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2)
+ dev_info(&client->dev, "Optional features:%s%s%s%s%s\n",
+ (data->has_voltage & (1 << 0)) ? " in0" : "",
+ (data->has_voltage & (1 << 4)) ? " in4" : "",
+ data->has_fan4 ? " fan4" : "",
+ data->has_pwm2 ? " pwm2" : "",
+ data->has_vid ? " vid" : "");
+ if (data->bypass_attn)
+ dev_info(&client->dev, "Bypassing attenuators on:%s%s%s%s\n",
+ (data->bypass_attn & (1 << 0)) ? " in0" : "",
+ (data->bypass_attn & (1 << 1)) ? " in1" : "",
+ (data->bypass_attn & (1 << 3)) ? " in3" : "",
+ (data->bypass_attn & (1 << 4)) ? " in4" : "");
+
return 0;
eremove:
- sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group);
+ adt7475_remove_files(client, data);
efree:
kfree(data);
return ret;
@@ -1031,7 +1397,7 @@ static int adt7475_remove(struct i2c_client *client)
struct adt7475_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group);
+ adt7475_remove_files(client, data);
kfree(data);
return 0;
@@ -1116,7 +1482,7 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adt7475_data *data = i2c_get_clientdata(client);
- u8 ext;
+ u16 ext;
int i;
mutex_lock(&data->lock);
@@ -1127,25 +1493,44 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
data->alarms = adt7475_read(REG_STATUS2) << 8;
data->alarms |= adt7475_read(REG_STATUS1);
- ext = adt7475_read(REG_EXTEND1);
- for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++)
+ ext = (adt7475_read(REG_EXTEND2) << 8) |
+ adt7475_read(REG_EXTEND1);
+ for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
+ if (!(data->has_voltage & (1 << i)))
+ continue;
data->voltage[INPUT][i] =
(adt7475_read(VOLTAGE_REG(i)) << 2) |
- ((ext >> ((i + 1) * 2)) & 3);
+ ((ext >> (i * 2)) & 3);
+ }
- ext = adt7475_read(REG_EXTEND2);
for (i = 0; i < ADT7475_TEMP_COUNT; i++)
data->temp[INPUT][i] =
(adt7475_read(TEMP_REG(i)) << 2) |
- ((ext >> ((i + 1) * 2)) & 3);
+ ((ext >> ((i + 5) * 2)) & 3);
- for (i = 0; i < ADT7475_TACH_COUNT; i++)
+ if (data->has_voltage & (1 << 5)) {
+ data->alarms |= adt7475_read(REG_STATUS4) << 24;
+ ext = adt7475_read(REG_EXTEND3);
+ data->voltage[INPUT][5] = adt7475_read(REG_VTT) << 2 |
+ ((ext >> 4) & 3);
+ }
+
+ for (i = 0; i < ADT7475_TACH_COUNT; i++) {
+ if (i == 3 && !data->has_fan4)
+ continue;
data->tach[INPUT][i] =
adt7475_read_word(client, TACH_REG(i));
+ }
/* Updated by hw when in auto mode */
- for (i = 0; i < ADT7475_PWM_COUNT; i++)
+ for (i = 0; i < ADT7475_PWM_COUNT; i++) {
+ if (i == 1 && !data->has_pwm2)
+ continue;
data->pwm[INPUT][i] = adt7475_read(PWM_REG(i));
+ }
+
+ if (data->has_vid)
+ data->vid = adt7475_read(REG_VID) & 0x3f;
data->measure_updated = jiffies;
}
@@ -1153,9 +1538,12 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
/* Limits and settings, should never change update every 60 seconds */
if (time_after(jiffies, data->limits_updated + HZ * 60) ||
!data->valid) {
+ data->config4 = adt7475_read(REG_CONFIG4);
data->config5 = adt7475_read(REG_CONFIG5);
for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
+ if (!(data->has_voltage & (1 << i)))
+ continue;
/* Adjust values so they match the input precision */
data->voltage[MIN][i] =
adt7475_read(VOLTAGE_MIN_REG(i)) << 2;
@@ -1163,6 +1551,11 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
adt7475_read(VOLTAGE_MAX_REG(i)) << 2;
}
+ if (data->has_voltage & (1 << 5)) {
+ data->voltage[MIN][5] = adt7475_read(REG_VTT_MIN) << 2;
+ data->voltage[MAX][5] = adt7475_read(REG_VTT_MAX) << 2;
+ }
+
for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
/* Adjust values so they match the input precision */
data->temp[MIN][i] =
@@ -1178,11 +1571,16 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
}
adt7475_read_hystersis(client);
- for (i = 0; i < ADT7475_TACH_COUNT; i++)
+ for (i = 0; i < ADT7475_TACH_COUNT; i++) {
+ if (i == 3 && !data->has_fan4)
+ continue;
data->tach[MIN][i] =
adt7475_read_word(client, TACH_MIN_REG(i));
+ }
for (i = 0; i < ADT7475_PWM_COUNT; i++) {
+ if (i == 1 && !data->has_pwm2)
+ continue;
data->pwm[MAX][i] = adt7475_read(PWM_MAX_REG(i));
data->pwm[MIN][i] = adt7475_read(PWM_MIN_REG(i));
/* Set the channel and control information */
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index 8acf82977e7..480f80ea1fa 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -701,6 +701,7 @@ static int asb100_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int val1, val2;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
pr_debug("asb100.o: detect failed, "
@@ -708,50 +709,30 @@ static int asb100_detect(struct i2c_client *client, int kind,
return -ENODEV;
}
- /* The chip may be stuck in some other bank than bank 0. This may
- make reading other information impossible. Specify a force=... or
- force_*=... parameter, and the chip will be reset to the right
- bank. */
- if (kind < 0) {
-
- int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK);
- int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
-
- /* If we're in bank 0 */
- if ((!(val1 & 0x07)) &&
- /* Check for ASB100 ID (low byte) */
- (((!(val1 & 0x80)) && (val2 != 0x94)) ||
- /* Check for ASB100 ID (high byte ) */
- ((val1 & 0x80) && (val2 != 0x06)))) {
- pr_debug("asb100.o: detect failed, "
- "bad chip id 0x%02x!\n", val2);
- return -ENODEV;
- }
+ val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK);
+ val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
- } /* kind < 0 */
+ /* If we're in bank 0 */
+ if ((!(val1 & 0x07)) &&
+ /* Check for ASB100 ID (low byte) */
+ (((!(val1 & 0x80)) && (val2 != 0x94)) ||
+ /* Check for ASB100 ID (high byte ) */
+ ((val1 & 0x80) && (val2 != 0x06)))) {
+ pr_debug("asb100: detect failed, bad chip id 0x%02x!\n", val2);
+ return -ENODEV;
+ }
- /* We have either had a force parameter, or we have already detected
- Winbond. Put it now into bank 0 and Vendor ID High Byte */
+ /* Put it now into bank 0 and Vendor ID High Byte */
i2c_smbus_write_byte_data(client, ASB100_REG_BANK,
(i2c_smbus_read_byte_data(client, ASB100_REG_BANK) & 0x78)
| 0x80);
/* Determine the chip type. */
- if (kind <= 0) {
- int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID);
- int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
-
- if ((val1 == 0x31) && (val2 == 0x06))
- kind = asb100;
- else {
- if (kind == 0)
- dev_warn(&adapter->dev, "ignoring "
- "'force' parameter for unknown chip "
- "at adapter %d, address 0x%02x.\n",
- i2c_adapter_id(adapter), client->addr);
- return -ENODEV;
- }
- }
+ val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID);
+ val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
+
+ if (val1 != 0x31 || val2 != 0x06)
+ return -ENODEV;
strlcpy(info->type, "asb100", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index 27d62574284..4377bb0cc52 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -2220,33 +2220,23 @@ static int dme1737_i2c_detect(struct i2c_client *client, int kind,
return -ENODEV;
}
- /* A negative kind means that the driver was loaded with no force
- * parameter (default), so we must identify the chip. */
- if (kind < 0) {
- company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY);
- verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP);
-
- if (company == DME1737_COMPANY_SMSC &&
- (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
- kind = dme1737;
- } else if (company == DME1737_COMPANY_SMSC &&
- verstep == SCH5027_VERSTEP) {
- kind = sch5027;
- } else {
- return -ENODEV;
- }
- }
+ company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY);
+ verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP);
- if (kind == sch5027) {
+ if (company == DME1737_COMPANY_SMSC &&
+ verstep == SCH5027_VERSTEP) {
name = "sch5027";
- } else {
- kind = dme1737;
+
+ } else if (company == DME1737_COMPANY_SMSC &&
+ (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
name = "dme1737";
+ } else {
+ return -ENODEV;
}
dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
- kind == sch5027 ? "SCH5027" : "DME1737", client->addr,
- verstep);
+ verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737",
+ client->addr, verstep);
strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index 53f88f51181..2a4c6a05b14 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -237,20 +237,16 @@ static int ds1621_detect(struct i2c_client *client, int kind,
return -ENODEV;
/* Now, we do the remaining detection. It is lousy. */
- if (kind < 0) {
- /* The NVB bit should be low if no EEPROM write has been
- requested during the latest 10ms, which is highly
- improbable in our case. */
- conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
- if (conf < 0 || conf & DS1621_REG_CONFIG_NVB)
+ /* The NVB bit should be low if no EEPROM write has been requested
+ during the latest 10ms, which is highly improbable in our case. */
+ conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
+ if (conf < 0 || conf & DS1621_REG_CONFIG_NVB)
+ return -ENODEV;
+ /* The 7 lowest bits of a temperature should always be 0. */
+ for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) {
+ temp = i2c_smbus_read_word_data(client, DS1621_REG_TEMP[i]);
+ if (temp < 0 || (temp & 0x7f00))
return -ENODEV;
- /* The 7 lowest bits of a temperature should always be 0. */
- for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) {
- temp = i2c_smbus_read_word_data(client,
- DS1621_REG_TEMP[i]);
- if (temp < 0 || (temp & 0x7f00))
- return -ENODEV;
- }
}
strlcpy(info->type, "ds1621", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c
index 4146105f1a5..a95fa4256ca 100644
--- a/drivers/hwmon/f71882fg.c
+++ b/drivers/hwmon/f71882fg.c
@@ -48,6 +48,7 @@
#define SIO_F71858_ID 0x0507 /* Chipset ID */
#define SIO_F71862_ID 0x0601 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */
+#define SIO_F71889_ID 0x0723 /* Chipset ID */
#define SIO_F8000_ID 0x0581 /* Chipset ID */
#define REGION_LENGTH 8
@@ -95,12 +96,13 @@ static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
-enum chips { f71858fg, f71862fg, f71882fg, f8000 };
+enum chips { f71858fg, f71862fg, f71882fg, f71889fg, f8000 };
static const char *f71882fg_names[] = {
"f71858fg",
"f71862fg",
"f71882fg",
+ "f71889fg",
"f8000",
};
@@ -155,7 +157,7 @@ struct f71882fg_data {
u8 pwm_auto_point_hyst[2];
u8 pwm_auto_point_mapping[4];
u8 pwm_auto_point_pwm[4][5];
- u8 pwm_auto_point_temp[4][4];
+ s8 pwm_auto_point_temp[4][4];
};
/* Sysfs in */
@@ -258,7 +260,9 @@ static struct platform_driver f71882fg_driver = {
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-/* Temp and in attr for the f71858fg */
+/* Temp and in attr for the f71858fg, the f71858fg is special as it
+ has its temperature indexes start at 0 (the others start at 1) and
+ it only has 3 voltage inputs */
static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
@@ -302,8 +306,8 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
};
-/* Temp and in attr common to both the f71862fg and f71882fg */
-static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = {
+/* Temp and in attr common to the f71862fg, f71882fg and f71889fg */
+static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
@@ -371,8 +375,8 @@ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = {
SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3),
};
-/* Temp and in attr found only on the f71882fg */
-static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = {
+/* For models with in1 alarm capability */
+static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = {
SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max,
0, 1),
SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep,
@@ -383,6 +387,7 @@ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = {
/* Temp and in attr for the f8000
Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max)
is used as hysteresis value to clear alarms
+ Also like the f71858fg its temperature indexes start at 0
*/
static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
@@ -413,61 +418,70 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
};
/* Fan / PWM attr common to all models */
-static struct sensor_device_attribute_2 fxxxx_fan_attr[] = {
+static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { {
SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0),
SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR,
show_fan_full_speed,
store_fan_full_speed, 0, 0),
SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0),
- SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1),
- SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR,
- show_fan_full_speed,
- store_fan_full_speed, 0, 1),
- SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1),
- SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
- SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR,
- show_fan_full_speed,
- store_fan_full_speed, 0, 2),
- SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
-
SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0),
SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
store_pwm_enable, 0, 0),
SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR,
show_pwm_interpolate, store_pwm_interpolate, 0, 0),
- SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
- show_pwm_auto_point_channel,
- store_pwm_auto_point_channel, 0, 0),
-
+}, {
+ SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1),
+ SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 1),
+ SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1),
SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1),
SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
store_pwm_enable, 0, 1),
SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR,
show_pwm_interpolate, store_pwm_interpolate, 0, 1),
- SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
- show_pwm_auto_point_channel,
- store_pwm_auto_point_channel, 0, 1),
-
+}, {
+ SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
+ SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 2),
+ SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
store_pwm_enable, 0, 2),
SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR,
show_pwm_interpolate, store_pwm_interpolate, 0, 2),
- SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
- show_pwm_auto_point_channel,
- store_pwm_auto_point_channel, 0, 2),
-};
+}, {
+ SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+ SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
+ show_fan_full_speed,
+ store_fan_full_speed, 0, 3),
+ SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
+ SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
+ SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+ store_pwm_enable, 0, 3),
+ SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR,
+ show_pwm_interpolate, store_pwm_interpolate, 0, 3),
+} };
-/* Fan / PWM attr for the f71862fg, less pwms and less zones per pwm than the
- f71882fg */
-static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
+/* Attr for models which can beep on Fan alarm */
+static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = {
SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
store_fan_beep, 0, 0),
SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
store_fan_beep, 0, 1),
SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
store_fan_beep, 0, 2),
+ SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+ store_fan_beep, 0, 3),
+};
+/* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the
+ f71858fg / f71882fg / f71889fg */
+static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 0),
SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
1, 0),
@@ -487,6 +501,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
+ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 1),
SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
1, 1),
@@ -506,6 +523,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
+ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 2),
SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
1, 2),
@@ -526,8 +546,11 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
};
-/* Fan / PWM attr common to both the f71882fg and f71858fg */
-static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
+/* PWM attr common to the f71858fg, f71882fg and f71889fg */
+static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 0),
SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 0),
@@ -565,7 +588,10 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 0),
SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
-
+}, {
+ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 1),
SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 1),
@@ -603,7 +629,10 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 1),
SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
-
+}, {
+ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 2),
SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 2),
@@ -641,30 +670,7 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 2),
SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
-};
-
-/* Fan / PWM attr found on the f71882fg but not on the f71858fg */
-static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
- SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 0),
- SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 1),
- SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 2),
-
- SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
- SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
- show_fan_full_speed,
- store_fan_full_speed, 0, 3),
- SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
- store_fan_beep, 0, 3),
- SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
-
- SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
- SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
- store_pwm_enable, 0, 3),
- SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR,
- show_pwm_interpolate, store_pwm_interpolate, 0, 3),
+}, {
SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 3),
@@ -705,14 +711,20 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 2, 3),
SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 3),
-};
+} };
-/* Fan / PWM attr for the f8000, zones mapped to temp instead of to pwm!
- Also the register block at offset A0 maps to TEMP1 (so our temp2, as the
- F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */
+/* Fan attr specific to the f8000 (4th fan input can only measure speed) */
static struct sensor_device_attribute_2 f8000_fan_attr[] = {
SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+};
+/* PWM attr for the f8000, zones mapped to temp instead of to pwm!
+ Also the register block at offset A0 maps to TEMP1 (so our temp2, as the
+ F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */
+static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
+ SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 0),
SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 2),
@@ -751,6 +763,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = {
SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
+ SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 1),
SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 0),
@@ -789,6 +804,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = {
SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
+ SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+ show_pwm_auto_point_channel,
+ store_pwm_auto_point_channel, 0, 2),
SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 1),
@@ -929,7 +947,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
/* Update once every 60 seconds */
if ( time_after(jiffies, data->last_limits + 60 * HZ ) ||
!data->valid) {
- if (data->type == f71882fg) {
+ if (data->type == f71882fg || data->type == f71889fg) {
data->in1_max =
f71882fg_read8(data, F71882FG_REG_IN1_HIGH);
data->in_beep =
@@ -951,7 +969,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
F71882FG_REG_TEMP_HYST(1));
}
- if (data->type == f71862fg || data->type == f71882fg) {
+ if (data->type == f71862fg || data->type == f71882fg ||
+ data->type == f71889fg) {
data->fan_beep = f71882fg_read8(data,
F71882FG_REG_FAN_BEEP);
data->temp_beep = f71882fg_read8(data,
@@ -961,15 +980,33 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
data->temp_type[2] = (reg & 0x04) ? 2 : 4;
data->temp_type[3] = (reg & 0x08) ? 2 : 4;
}
- reg2 = f71882fg_read8(data, F71882FG_REG_PECI);
- if ((reg2 & 0x03) == 0x01)
- data->temp_type[1] = 6 /* PECI */;
- else if ((reg2 & 0x03) == 0x02)
- data->temp_type[1] = 5 /* AMDSI */;
- else if (data->type == f71862fg || data->type == f71882fg)
- data->temp_type[1] = (reg & 0x02) ? 2 : 4;
- else
- data->temp_type[1] = 2; /* Only supports BJT */
+ /* Determine temp index 1 sensor type */
+ if (data->type == f71889fg) {
+ reg2 = f71882fg_read8(data, F71882FG_REG_START);
+ switch ((reg2 & 0x60) >> 5) {
+ case 0x00: /* BJT / Thermistor */
+ data->temp_type[1] = (reg & 0x02) ? 2 : 4;
+ break;
+ case 0x01: /* AMDSI */
+ data->temp_type[1] = 5;
+ break;
+ case 0x02: /* PECI */
+ case 0x03: /* Ibex Peak ?? Report as PECI for now */
+ data->temp_type[1] = 6;
+ break;
+ }
+ } else {
+ reg2 = f71882fg_read8(data, F71882FG_REG_PECI);
+ if ((reg2 & 0x03) == 0x01)
+ data->temp_type[1] = 6; /* PECI */
+ else if ((reg2 & 0x03) == 0x02)
+ data->temp_type[1] = 5; /* AMDSI */
+ else if (data->type == f71862fg ||
+ data->type == f71882fg)
+ data->temp_type[1] = (reg & 0x02) ? 2 : 4;
+ else /* f71858fg and f8000 only support BJT */
+ data->temp_type[1] = 2;
+ }
data->pwm_enable = f71882fg_read8(data,
F71882FG_REG_PWM_ENABLE);
@@ -1046,7 +1083,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
if (data->type == f8000)
data->fan[3] = f71882fg_read16(data,
F71882FG_REG_FAN(3));
- if (data->type == f71882fg)
+ if (data->type == f71882fg || data->type == f71889fg)
data->in_status = f71882fg_read8(data,
F71882FG_REG_IN_STATUS);
for (nr = 0; nr < nr_ins; nr++)
@@ -1764,7 +1801,11 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev,
int pwm = to_sensor_dev_attr_2(devattr)->index;
int point = to_sensor_dev_attr_2(devattr)->nr;
long val = simple_strtol(buf, NULL, 10) / 1000;
- val = SENSORS_LIMIT(val, 0, 255);
+
+ if (data->type == f71889fg)
+ val = SENSORS_LIMIT(val, -128, 127);
+ else
+ val = SENSORS_LIMIT(val, 0, 127);
mutex_lock(&data->update_lock);
f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val);
@@ -1794,6 +1835,15 @@ static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev,
return 0;
}
+static void f71882fg_remove_sysfs_files(struct platform_device *pdev,
+ struct sensor_device_attribute_2 *attr, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ device_remove_file(&pdev->dev, &attr[i].dev_attr);
+}
+
static int __devinit f71882fg_probe(struct platform_device *pdev)
{
struct f71882fg_data *data;
@@ -1846,16 +1896,17 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
ARRAY_SIZE(f71858fg_in_temp_attr));
break;
case f71882fg:
+ case f71889fg:
err = f71882fg_create_sysfs_files(pdev,
- f71882fg_in_temp_attr,
- ARRAY_SIZE(f71882fg_in_temp_attr));
+ fxxxx_in1_alarm_attr,
+ ARRAY_SIZE(fxxxx_in1_alarm_attr));
if (err)
goto exit_unregister_sysfs;
/* fall through! */
case f71862fg:
err = f71882fg_create_sysfs_files(pdev,
- f718x2fg_in_temp_attr,
- ARRAY_SIZE(f718x2fg_in_temp_attr));
+ fxxxx_in_temp_attr,
+ ARRAY_SIZE(fxxxx_in_temp_attr));
break;
case f8000:
err = f71882fg_create_sysfs_files(pdev,
@@ -1883,6 +1934,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
err = (data->pwm_enable & 0x15) != 0x15;
break;
case f71882fg:
+ case f71889fg:
err = 0;
break;
case f8000:
@@ -1897,34 +1949,55 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
goto exit_unregister_sysfs;
}
- err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr,
- ARRAY_SIZE(fxxxx_fan_attr));
+ err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
+ ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
if (err)
goto exit_unregister_sysfs;
- switch (data->type) {
- case f71862fg:
- err = f71882fg_create_sysfs_files(pdev,
- f71862fg_fan_attr,
- ARRAY_SIZE(f71862fg_fan_attr));
- break;
- case f71882fg:
+ if (data->type == f71862fg || data->type == f71882fg ||
+ data->type == f71889fg) {
err = f71882fg_create_sysfs_files(pdev,
- f71882fg_fan_attr,
- ARRAY_SIZE(f71882fg_fan_attr));
+ fxxxx_fan_beep_attr, nr_fans);
if (err)
goto exit_unregister_sysfs;
- /* fall through! */
- case f71858fg:
+ }
+
+ switch (data->type) {
+ case f71862fg:
err = f71882fg_create_sysfs_files(pdev,
- f71882fg_f71858fg_fan_attr,
- ARRAY_SIZE(f71882fg_f71858fg_fan_attr));
+ f71862fg_auto_pwm_attr,
+ ARRAY_SIZE(f71862fg_auto_pwm_attr));
break;
case f8000:
err = f71882fg_create_sysfs_files(pdev,
f8000_fan_attr,
ARRAY_SIZE(f8000_fan_attr));
+ if (err)
+ goto exit_unregister_sysfs;
+ err = f71882fg_create_sysfs_files(pdev,
+ f8000_auto_pwm_attr,
+ ARRAY_SIZE(f8000_auto_pwm_attr));
break;
+ case f71889fg:
+ for (i = 0; i < nr_fans; i++) {
+ data->pwm_auto_point_mapping[i] =
+ f71882fg_read8(data,
+ F71882FG_REG_POINT_MAPPING(i));
+ if (data->pwm_auto_point_mapping[i] & 0x80)
+ break;
+ }
+ if (i != nr_fans) {
+ dev_warn(&pdev->dev,
+ "Auto pwm controlled by raw digital "
+ "data, disabling pwm auto_point "
+ "sysfs attributes\n");
+ break;
+ }
+ /* fall through */
+ default: /* f71858fg / f71882fg */
+ err = f71882fg_create_sysfs_files(pdev,
+ &fxxxx_auto_pwm_attr[0][0],
+ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
}
if (err)
goto exit_unregister_sysfs;
@@ -1954,33 +2027,76 @@ exit_free:
static int f71882fg_remove(struct platform_device *pdev)
{
- int i;
struct f71882fg_data *data = platform_get_drvdata(pdev);
+ int nr_fans = (data->type == f71882fg) ? 4 : 3;
+ u8 start_reg = f71882fg_read8(data, F71882FG_REG_START);
platform_set_drvdata(pdev, NULL);
if (data->hwmon_dev)
hwmon_device_unregister(data->hwmon_dev);
- /* Note we are not looping over all attr arrays we have as the ones
- below are supersets of the ones skipped. */
device_remove_file(&pdev->dev, &dev_attr_name);
- for (i = 0; i < ARRAY_SIZE(f718x2fg_in_temp_attr); i++)
- device_remove_file(&pdev->dev,
- &f718x2fg_in_temp_attr[i].dev_attr);
-
- for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++)
- device_remove_file(&pdev->dev,
- &f71882fg_in_temp_attr[i].dev_attr);
+ if (start_reg & 0x01) {
+ switch (data->type) {
+ case f71858fg:
+ if (data->temp_config & 0x10)
+ f71882fg_remove_sysfs_files(pdev,
+ f8000_in_temp_attr,
+ ARRAY_SIZE(f8000_in_temp_attr));
+ else
+ f71882fg_remove_sysfs_files(pdev,
+ f71858fg_in_temp_attr,
+ ARRAY_SIZE(f71858fg_in_temp_attr));
+ break;
+ case f71882fg:
+ case f71889fg:
+ f71882fg_remove_sysfs_files(pdev,
+ fxxxx_in1_alarm_attr,
+ ARRAY_SIZE(fxxxx_in1_alarm_attr));
+ /* fall through! */
+ case f71862fg:
+ f71882fg_remove_sysfs_files(pdev,
+ fxxxx_in_temp_attr,
+ ARRAY_SIZE(fxxxx_in_temp_attr));
+ break;
+ case f8000:
+ f71882fg_remove_sysfs_files(pdev,
+ f8000_in_temp_attr,
+ ARRAY_SIZE(f8000_in_temp_attr));
+ break;
+ }
+ }
- for (i = 0; i < ARRAY_SIZE(fxxxx_fan_attr); i++)
- device_remove_file(&pdev->dev, &fxxxx_fan_attr[i].dev_attr);
+ if (start_reg & 0x02) {
+ f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
+ ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
- for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++)
- device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr);
+ if (data->type == f71862fg || data->type == f71882fg ||
+ data->type == f71889fg)
+ f71882fg_remove_sysfs_files(pdev,
+ fxxxx_fan_beep_attr, nr_fans);
- for (i = 0; i < ARRAY_SIZE(f8000_fan_attr); i++)
- device_remove_file(&pdev->dev, &f8000_fan_attr[i].dev_attr);
+ switch (data->type) {
+ case f71862fg:
+ f71882fg_remove_sysfs_files(pdev,
+ f71862fg_auto_pwm_attr,
+ ARRAY_SIZE(f71862fg_auto_pwm_attr));
+ break;
+ case f8000:
+ f71882fg_remove_sysfs_files(pdev,
+ f8000_fan_attr,
+ ARRAY_SIZE(f8000_fan_attr));
+ f71882fg_remove_sysfs_files(pdev,
+ f8000_auto_pwm_attr,
+ ARRAY_SIZE(f8000_auto_pwm_attr));
+ break;
+ default: /* f71858fg / f71882fg / f71889fg */
+ f71882fg_remove_sysfs_files(pdev,
+ &fxxxx_auto_pwm_attr[0][0],
+ ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+ }
+ }
kfree(data);
@@ -2012,11 +2128,15 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
case SIO_F71882_ID:
sio_data->type = f71882fg;
break;
+ case SIO_F71889_ID:
+ sio_data->type = f71889fg;
+ break;
case SIO_F8000_ID:
sio_data->type = f8000;
break;
default:
- printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n");
+ printk(KERN_INFO DRVNAME ": Unsupported Fintek device: %04x\n",
+ (unsigned int)devid);
goto exit;
}
diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c
index e2107e533ed..40dfbcd3f3f 100644
--- a/drivers/hwmon/f75375s.c
+++ b/drivers/hwmon/f75375s.c
@@ -681,30 +681,20 @@ static int f75375_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
- u8 version = 0;
- const char *name = "";
-
- if (kind < 0) {
- u16 vendid = f75375_read16(client, F75375_REG_VENDOR);
- u16 chipid = f75375_read16(client, F75375_CHIP_ID);
- version = f75375_read8(client, F75375_REG_VERSION);
- if (chipid == 0x0306 && vendid == 0x1934) {
- kind = f75375;
- } else if (chipid == 0x0204 && vendid == 0x1934) {
- kind = f75373;
- } else {
- dev_err(&adapter->dev,
- "failed,%02X,%02X,%02X\n",
- chipid, version, vendid);
- return -ENODEV;
- }
- }
+ u16 vendid, chipid;
+ u8 version;
+ const char *name;
- if (kind == f75375) {
+ vendid = f75375_read16(client, F75375_REG_VENDOR);
+ chipid = f75375_read16(client, F75375_CHIP_ID);
+ if (chipid == 0x0306 && vendid == 0x1934)
name = "f75375";
- } else if (kind == f75373) {
+ else if (chipid == 0x0204 && vendid == 0x1934)
name = "f75373";
- }
+ else
+ return -ENODEV;
+
+ version = f75375_read8(client, F75375_REG_VERSION);
dev_info(&adapter->dev, "found %s version: %02X\n", name, version);
strlcpy(info->type, name, I2C_NAME_SIZE);
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index da1b1f9488a..281829cd153 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -1000,43 +1000,38 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy)
}
}
-static int fschmd_detect(struct i2c_client *client, int kind,
+static int fschmd_detect(struct i2c_client *client, int _kind,
struct i2c_board_info *info)
{
+ enum chips kind;
struct i2c_adapter *adapter = client->adapter;
+ char id[4];
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* Detect & Identify the chip */
- if (kind <= 0) {
- char id[4];
-
- id[0] = i2c_smbus_read_byte_data(client,
- FSCHMD_REG_IDENT_0);
- id[1] = i2c_smbus_read_byte_data(client,
- FSCHMD_REG_IDENT_1);
- id[2] = i2c_smbus_read_byte_data(client,
- FSCHMD_REG_IDENT_2);
- id[3] = '\0';
-
- if (!strcmp(id, "PEG"))
- kind = fscpos;
- else if (!strcmp(id, "HER"))
- kind = fscher;
- else if (!strcmp(id, "SCY"))
- kind = fscscy;
- else if (!strcmp(id, "HRC"))
- kind = fschrc;
- else if (!strcmp(id, "HMD"))
- kind = fschmd;
- else if (!strcmp(id, "HDS"))
- kind = fschds;
- else if (!strcmp(id, "SYL"))
- kind = fscsyl;
- else
- return -ENODEV;
- }
+ id[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_0);
+ id[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_1);
+ id[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_2);
+ id[3] = '\0';
+
+ if (!strcmp(id, "PEG"))
+ kind = fscpos;
+ else if (!strcmp(id, "HER"))
+ kind = fscher;
+ else if (!strcmp(id, "SCY"))
+ kind = fscscy;
+ else if (!strcmp(id, "HRC"))
+ kind = fschrc;
+ else if (!strcmp(id, "HMD"))
+ kind = fschmd;
+ else if (!strcmp(id, "HDS"))
+ kind = fschds;
+ else if (!strcmp(id, "SYL"))
+ kind = fscsyl;
+ else
+ return -ENODEV;
strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE);
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index 7820df45d77..1d69458aa0b 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -488,36 +488,21 @@ static int gl518_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
- int i;
+ int rev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
/* Now, we do the remaining detection. */
-
- if (kind < 0) {
- if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80)
- || (gl518_read_value(client, GL518_REG_CONF) & 0x80))
- return -ENODEV;
- }
+ if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80)
+ || (gl518_read_value(client, GL518_REG_CONF) & 0x80))
+ return -ENODEV;
/* Determine the chip type. */
- if (kind <= 0) {
- i = gl518_read_value(client, GL518_REG_REVISION);
- if (i == 0x00) {
- kind = gl518sm_r00;
- } else if (i == 0x80) {
- kind = gl518sm_r80;
- } else {
- if (kind <= 0)
- dev_info(&adapter->dev,
- "Ignoring 'force' parameter for unknown "
- "chip at adapter %d, address 0x%02x\n",
- i2c_adapter_id(adapter), client->addr);
- return -ENODEV;
- }
- }
+ rev = gl518_read_value(client, GL518_REG_REVISION);
+ if (rev != 0x00 && rev != 0x80)
+ return -ENODEV;
strlcpy(info->type, "gl518sm", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index 19616f2242b..92b5720ceaf 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -691,13 +691,11 @@ static int gl520_detect(struct i2c_client *client, int kind,
return -ENODEV;
/* Determine the chip type. */
- if (kind < 0) {
- if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) ||
- ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
- ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) {
- dev_dbg(&client->dev, "Unknown chip type, skipping\n");
- return -ENODEV;
- }
+ if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) ||
+ ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
+ ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) {
+ dev_dbg(&client->dev, "Unknown chip type, skipping\n");
+ return -ENODEV;
}
strlcpy(info->type, "gl520sm", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index a3749cb0f18..0ffe84d190b 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -124,6 +124,8 @@ superio_exit(void)
#define IT87_BASE_REG 0x60
/* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_GPIO3_REG 0x27
+#define IT87_SIO_GPIO5_REG 0x29
#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */
#define IT87_SIO_VID_REG 0xfc /* VID value */
@@ -244,7 +246,9 @@ struct it87_sio_data {
/* Values read from Super-I/O config space */
u8 revision;
u8 vid_value;
- /* Values set based on DMI strings */
+ /* Features skipped based on config or DMI */
+ u8 skip_vid;
+ u8 skip_fan;
u8 skip_pwm;
};
@@ -1028,11 +1032,35 @@ static int __init it87_find(unsigned short *address,
chip_type, *address, sio_data->revision);
/* Read GPIO config and VID value from LDN 7 (GPIO) */
- if (sio_data->type != it87) {
+ if (sio_data->type == it87) {
+ /* The IT8705F doesn't have VID pins at all */
+ sio_data->skip_vid = 1;
+ } else {
int reg;
superio_select(GPIO);
- if (sio_data->type == it8718 || sio_data->type == it8720)
+ /* We need at least 4 VID pins */
+ reg = superio_inb(IT87_SIO_GPIO3_REG);
+ if (reg & 0x0f) {
+ pr_info("it87: VID is disabled (pins used for GPIO)\n");
+ sio_data->skip_vid = 1;
+ }
+
+ /* Check if fan3 is there or not */
+ if (reg & (1 << 6))
+ sio_data->skip_pwm |= (1 << 2);
+ if (reg & (1 << 7))
+ sio_data->skip_fan |= (1 << 2);
+
+ /* Check if fan2 is there or not */
+ reg = superio_inb(IT87_SIO_GPIO5_REG);
+ if (reg & (1 << 1))
+ sio_data->skip_pwm |= (1 << 1);
+ if (reg & (1 << 2))
+ sio_data->skip_fan |= (1 << 1);
+
+ if ((sio_data->type == it8718 || sio_data->type == it8720)
+ && !(sio_data->skip_vid))
sio_data->vid_value = superio_inb(IT87_SIO_VID_REG);
reg = superio_inb(IT87_SIO_PINX2_REG);
@@ -1236,8 +1264,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
}
}
- if (data->type == it8712 || data->type == it8716
- || data->type == it8718 || data->type == it8720) {
+ if (!sio_data->skip_vid) {
data->vrm = vid_which_vrm();
/* VID reading from Super-I/O config space if available */
data->vid = sio_data->vid_value;
@@ -1355,8 +1382,10 @@ static int __devinit it87_check_pwm(struct device *dev)
/* Called when we have found a new IT87. */
static void __devinit it87_init_device(struct platform_device *pdev)
{
+ struct it87_sio_data *sio_data = pdev->dev.platform_data;
struct it87_data *data = platform_get_drvdata(pdev);
int tmp, i;
+ u8 mask;
/* initialize to sane defaults:
* - if the chip is in manual pwm mode, this will be overwritten with
@@ -1402,10 +1431,11 @@ static void __devinit it87_init_device(struct platform_device *pdev)
}
/* Check if tachometers are reset manually or by some reason */
+ mask = 0x70 & ~(sio_data->skip_fan << 4);
data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL);
- if ((data->fan_main_ctrl & 0x70) == 0) {
+ if ((data->fan_main_ctrl & mask) == 0) {
/* Enable all fan tachometers */
- data->fan_main_ctrl |= 0x70;
+ data->fan_main_ctrl |= mask;
it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
}
data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
@@ -1428,6 +1458,9 @@ static void __devinit it87_init_device(struct platform_device *pdev)
}
}
+ /* Fan input pins may be used for alternative functions */
+ data->has_fan &= ~sio_data->skip_fan;
+
/* Set current fan mode registers and the default settings for the
* other mode registers */
for (i = 0; i < 3; i++) {
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 3195a265f0e..5da66ab04f7 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -427,40 +427,34 @@ static int lm63_detect(struct i2c_client *new_client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
+ u8 man_id, chip_id, reg_config1, reg_config2;
+ u8 reg_alert_status, reg_alert_mask;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind < 0) { /* must identify */
- u8 man_id, chip_id, reg_config1, reg_config2;
- u8 reg_alert_status, reg_alert_mask;
-
- man_id = i2c_smbus_read_byte_data(new_client,
- LM63_REG_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(new_client,
- LM63_REG_CHIP_ID);
- reg_config1 = i2c_smbus_read_byte_data(new_client,
- LM63_REG_CONFIG1);
- reg_config2 = i2c_smbus_read_byte_data(new_client,
- LM63_REG_CONFIG2);
- reg_alert_status = i2c_smbus_read_byte_data(new_client,
- LM63_REG_ALERT_STATUS);
- reg_alert_mask = i2c_smbus_read_byte_data(new_client,
- LM63_REG_ALERT_MASK);
-
- if (man_id == 0x01 /* National Semiconductor */
- && chip_id == 0x41 /* LM63 */
- && (reg_config1 & 0x18) == 0x00
- && (reg_config2 & 0xF8) == 0x00
- && (reg_alert_status & 0x20) == 0x00
- && (reg_alert_mask & 0xA4) == 0xA4) {
- kind = lm63;
- } else { /* failed */
- dev_dbg(&adapter->dev, "Unsupported chip "
- "(man_id=0x%02X, chip_id=0x%02X).\n",
- man_id, chip_id);
- return -ENODEV;
- }
+ man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID);
+
+ reg_config1 = i2c_smbus_read_byte_data(new_client,
+ LM63_REG_CONFIG1);
+ reg_config2 = i2c_smbus_read_byte_data(new_client,
+ LM63_REG_CONFIG2);
+ reg_alert_status = i2c_smbus_read_byte_data(new_client,
+ LM63_REG_ALERT_STATUS);
+ reg_alert_mask = i2c_smbus_read_byte_data(new_client,
+ LM63_REG_ALERT_MASK);
+
+ if (man_id != 0x01 /* National Semiconductor */
+ || chip_id != 0x41 /* LM63 */
+ || (reg_config1 & 0x18) != 0x00
+ || (reg_config2 & 0xF8) != 0x00
+ || (reg_alert_status & 0x20) != 0x00
+ || (reg_alert_mask & 0xA4) != 0xA4) {
+ dev_dbg(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
}
strlcpy(info->type, "lm63", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
new file mode 100644
index 00000000000..0bf8b2a8e9f
--- /dev/null
+++ b/drivers/hwmon/lm73.c
@@ -0,0 +1,205 @@
+/*
+ * LM73 Sensor driver
+ * Based on LM75
+ *
+ * Copyright (C) 2007, CenoSYS (www.cenosys.com).
+ * Copyright (C) 2009, Bollore telecom (www.bolloretelecom.eu).
+ *
+ * Guillaume Ligneul <guillaume.ligneul@gmail.com>
+ * Adrien Demarez <adrien.demarez@bolloretelecom.eu>
+ * Jeremy Laine <jeremy.laine@bolloretelecom.eu>
+ *
+ * This software program is licensed subject to the GNU General Public License
+ * (GPL).Version 2,June 1991, available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
+ 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(lm73);
+
+/* LM73 registers */
+#define LM73_REG_INPUT 0x00
+#define LM73_REG_CONF 0x01
+#define LM73_REG_MAX 0x02
+#define LM73_REG_MIN 0x03
+#define LM73_REG_CTRL 0x04
+#define LM73_REG_ID 0x07
+
+#define LM73_ID 0x9001 /* or 0x190 after a swab16() */
+#define DRVNAME "lm73"
+#define LM73_TEMP_MIN (-40)
+#define LM73_TEMP_MAX 150
+
+/*-----------------------------------------------------------------------*/
+
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev);
+ long temp;
+ short value;
+
+ int status = strict_strtol(buf, 10, &temp);
+ if (status < 0)
+ return status;
+
+ /* Write value */
+ value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4),
+ (LM73_TEMP_MAX*4)) << 5;
+ i2c_smbus_write_word_data(client, attr->index, swab16(value));
+ return count;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev);
+ /* use integer division instead of equivalent right shift to
+ guarantee arithmetic shift and preserve the sign */
+ int temp = ((s16) (swab16(i2c_smbus_read_word_data(client,
+ attr->index)))*250) / 32;
+ return sprintf(buf, "%d\n", temp);
+}
+
+
+/*-----------------------------------------------------------------------*/
+
+/* sysfs attributes for hwmon */
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+ show_temp, set_temp, LM73_REG_MAX);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
+ show_temp, set_temp, LM73_REG_MIN);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+ show_temp, NULL, LM73_REG_INPUT);
+
+
+static struct attribute *lm73_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm73_group = {
+ .attrs = lm73_attributes,
+};
+
+/*-----------------------------------------------------------------------*/
+
+/* device probe and removal */
+
+static int
+lm73_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct device *hwmon_dev;
+ int status;
+
+ /* Register sysfs hooks */
+ status = sysfs_create_group(&client->dev.kobj, &lm73_group);
+ if (status)
+ return status;
+
+ hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(hwmon_dev)) {
+ status = PTR_ERR(hwmon_dev);
+ goto exit_remove;
+ }
+ i2c_set_clientdata(client, hwmon_dev);
+
+ dev_info(&client->dev, "%s: sensor '%s'\n",
+ dev_name(hwmon_dev), client->name);
+
+ return 0;
+
+exit_remove:
+ sysfs_remove_group(&client->dev.kobj, &lm73_group);
+ return status;
+}
+
+static int lm73_remove(struct i2c_client *client)
+{
+ struct device *hwmon_dev = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm73_group);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+static const struct i2c_device_id lm73_ids[] = {
+ { "lm73", lm73 },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, lm73_ids);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm73_detect(struct i2c_client *new_client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = new_client->adapter;
+ u16 id;
+ u8 ctrl;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ /* Check device ID */
+ id = i2c_smbus_read_word_data(new_client, LM73_REG_ID);
+ ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL);
+ if ((id != LM73_ID) || (ctrl & 0x10))
+ return -ENODEV;
+
+ strlcpy(info->type, "lm73", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static struct i2c_driver lm73_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm73",
+ },
+ .probe = lm73_probe,
+ .remove = lm73_remove,
+ .id_table = lm73_ids,
+ .detect = lm73_detect,
+ .address_data = &addr_data,
+};
+
+/* module glue */
+
+static int __init sensors_lm73_init(void)
+{
+ return i2c_add_driver(&lm73_driver);
+}
+
+static void __exit sensors_lm73_exit(void)
+{
+ i2c_del_driver(&lm73_driver);
+}
+
+MODULE_AUTHOR("Guillaume Ligneul <guillaume.ligneul@gmail.com>");
+MODULE_DESCRIPTION("LM73 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm73_init);
+module_exit(sensors_lm73_exit);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index 55bd87c15c9..e392548cccb 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -239,6 +239,7 @@ static int lm75_detect(struct i2c_client *new_client, int kind,
{
struct i2c_adapter *adapter = new_client->adapter;
int i;
+ int cur, conf, hyst, os;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
@@ -251,40 +252,35 @@ static int lm75_detect(struct i2c_client *new_client, int kind,
The cycling+unused addresses combination is not tested,
since it would significantly slow the detection down and would
hardly add any value. */
- if (kind < 0) {
- int cur, conf, hyst, os;
-
- /* Unused addresses */
- cur = i2c_smbus_read_word_data(new_client, 0);
- conf = i2c_smbus_read_byte_data(new_client, 1);
- hyst = i2c_smbus_read_word_data(new_client, 2);
- if (i2c_smbus_read_word_data(new_client, 4) != hyst
- || i2c_smbus_read_word_data(new_client, 5) != hyst
- || i2c_smbus_read_word_data(new_client, 6) != hyst
- || i2c_smbus_read_word_data(new_client, 7) != hyst)
- return -ENODEV;
- os = i2c_smbus_read_word_data(new_client, 3);
- if (i2c_smbus_read_word_data(new_client, 4) != os
- || i2c_smbus_read_word_data(new_client, 5) != os
- || i2c_smbus_read_word_data(new_client, 6) != os
- || i2c_smbus_read_word_data(new_client, 7) != os)
- return -ENODEV;
- /* Unused bits */
- if (conf & 0xe0)
- return -ENODEV;
+ /* Unused addresses */
+ cur = i2c_smbus_read_word_data(new_client, 0);
+ conf = i2c_smbus_read_byte_data(new_client, 1);
+ hyst = i2c_smbus_read_word_data(new_client, 2);
+ if (i2c_smbus_read_word_data(new_client, 4) != hyst
+ || i2c_smbus_read_word_data(new_client, 5) != hyst
+ || i2c_smbus_read_word_data(new_client, 6) != hyst
+ || i2c_smbus_read_word_data(new_client, 7) != hyst)
+ return -ENODEV;
+ os = i2c_smbus_read_word_data(new_client, 3);
+ if (i2c_smbus_read_word_data(new_client, 4) != os
+ || i2c_smbus_read_word_data(new_client, 5) != os
+ || i2c_smbus_read_word_data(new_client, 6) != os
+ || i2c_smbus_read_word_data(new_client, 7) != os)
+ return -ENODEV;
- /* Addresses cycling */
- for (i = 8; i < 0xff; i += 8)
- if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
- || i2c_smbus_read_word_data(new_client, i + 2) != hyst
- || i2c_smbus_read_word_data(new_client, i + 3) != os)
- return -ENODEV;
+ /* Unused bits */
+ if (conf & 0xe0)
+ return -ENODEV;
+
+ /* Addresses cycling */
+ for (i = 8; i < 0xff; i += 8) {
+ if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+ || i2c_smbus_read_word_data(new_client, i + 2) != hyst
+ || i2c_smbus_read_word_data(new_client, i + 3) != os)
+ return -ENODEV;
}
- /* NOTE: we treat "force=..." and "force_lm75=..." the same.
- * Only new-style driver binding distinguishes chip types.
- */
strlcpy(info->type, "lm75", I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index 866b401ab6e..ac067fd1948 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -249,6 +249,7 @@ static int lm77_detect(struct i2c_client *new_client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
+ int i, cur, conf, hyst, crit, min, max;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
@@ -265,51 +266,48 @@ static int lm77_detect(struct i2c_client *new_client, int kind,
4. registers cycling over 8-address boundaries
Word-sized registers are high-byte first. */
- if (kind < 0) {
- int i, cur, conf, hyst, crit, min, max;
-
- /* addresses cycling */
- cur = i2c_smbus_read_word_data(new_client, 0);
- conf = i2c_smbus_read_byte_data(new_client, 1);
- hyst = i2c_smbus_read_word_data(new_client, 2);
- crit = i2c_smbus_read_word_data(new_client, 3);
- min = i2c_smbus_read_word_data(new_client, 4);
- max = i2c_smbus_read_word_data(new_client, 5);
- for (i = 8; i <= 0xff; i += 8)
- if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
- || i2c_smbus_read_word_data(new_client, i + 2) != hyst
- || i2c_smbus_read_word_data(new_client, i + 3) != crit
- || i2c_smbus_read_word_data(new_client, i + 4) != min
- || i2c_smbus_read_word_data(new_client, i + 5) != max)
- return -ENODEV;
-
- /* sign bits */
- if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
- || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
- || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
- || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
- || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
- return -ENODEV;
- /* unused bits */
- if (conf & 0xe0)
+ /* addresses cycling */
+ cur = i2c_smbus_read_word_data(new_client, 0);
+ conf = i2c_smbus_read_byte_data(new_client, 1);
+ hyst = i2c_smbus_read_word_data(new_client, 2);
+ crit = i2c_smbus_read_word_data(new_client, 3);
+ min = i2c_smbus_read_word_data(new_client, 4);
+ max = i2c_smbus_read_word_data(new_client, 5);
+ for (i = 8; i <= 0xff; i += 8) {
+ if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+ || i2c_smbus_read_word_data(new_client, i + 2) != hyst
+ || i2c_smbus_read_word_data(new_client, i + 3) != crit
+ || i2c_smbus_read_word_data(new_client, i + 4) != min
+ || i2c_smbus_read_word_data(new_client, i + 5) != max)
return -ENODEV;
+ }
- /* 0x06 and 0x07 return the last read value */
- cur = i2c_smbus_read_word_data(new_client, 0);
- if (i2c_smbus_read_word_data(new_client, 6) != cur
- || i2c_smbus_read_word_data(new_client, 7) != cur)
- return -ENODEV;
- hyst = i2c_smbus_read_word_data(new_client, 2);
- if (i2c_smbus_read_word_data(new_client, 6) != hyst
- || i2c_smbus_read_word_data(new_client, 7) != hyst)
- return -ENODEV;
- min = i2c_smbus_read_word_data(new_client, 4);
- if (i2c_smbus_read_word_data(new_client, 6) != min
- || i2c_smbus_read_word_data(new_client, 7) != min)
- return -ENODEV;
+ /* sign bits */
+ if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
+ || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
+ || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
+ || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
+ || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
+ return -ENODEV;
- }
+ /* unused bits */
+ if (conf & 0xe0)
+ return -ENODEV;
+
+ /* 0x06 and 0x07 return the last read value */
+ cur = i2c_smbus_read_word_data(new_client, 0);
+ if (i2c_smbus_read_word_data(new_client, 6) != cur
+ || i2c_smbus_read_word_data(new_client, 7) != cur)
+ return -ENODEV;
+ hyst = i2c_smbus_read_word_data(new_client, 2);
+ if (i2c_smbus_read_word_data(new_client, 6) != hyst
+ || i2c_smbus_read_word_data(new_client, 7) != hyst)
+ return -ENODEV;
+ min = i2c_smbus_read_word_data(new_client, 4);
+ if (i2c_smbus_read_word_data(new_client, 6) != min
+ || i2c_smbus_read_word_data(new_client, 7) != min)
+ return -ENODEV;
strlcpy(info->type, "lm77", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index f7e70163e01..5978291cebb 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -576,52 +576,34 @@ static int lm78_i2c_detect(struct i2c_client *client, int kind,
if (isa)
mutex_lock(&isa->update_lock);
- if (kind < 0) {
- if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
- || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR)
- != address)
- goto err_nodev;
-
- /* Explicitly prevent the misdetection of Winbond chips */
- i = i2c_smbus_read_byte_data(client, 0x4f);
- if (i == 0xa3 || i == 0x5c)
- goto err_nodev;
- }
+ if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
+ || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) != address)
+ goto err_nodev;
+
+ /* Explicitly prevent the misdetection of Winbond chips */
+ i = i2c_smbus_read_byte_data(client, 0x4f);
+ if (i == 0xa3 || i == 0x5c)
+ goto err_nodev;
/* Determine the chip type. */
- if (kind <= 0) {
- i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
- if (i == 0x00 || i == 0x20 /* LM78 */
- || i == 0x40) /* LM78-J */
- kind = lm78;
- else if ((i & 0xfe) == 0xc0)
- kind = lm79;
- else {
- if (kind == 0)
- dev_warn(&adapter->dev, "Ignoring 'force' "
- "parameter for unknown chip at "
- "adapter %d, address 0x%02x\n",
- i2c_adapter_id(adapter), address);
- goto err_nodev;
- }
+ i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
+ if (i == 0x00 || i == 0x20 /* LM78 */
+ || i == 0x40) /* LM78-J */
+ client_name = "lm78";
+ else if ((i & 0xfe) == 0xc0)
+ client_name = "lm79";
+ else
+ goto err_nodev;
- if (lm78_alias_detect(client, i)) {
- dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
- "be the same as ISA device\n", address);
- goto err_nodev;
- }
+ if (lm78_alias_detect(client, i)) {
+ dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
+ "be the same as ISA device\n", address);
+ goto err_nodev;
}
if (isa)
mutex_unlock(&isa->update_lock);
- switch (kind) {
- case lm79:
- client_name = "lm79";
- break;
- default:
- client_name = "lm78";
- }
strlcpy(info->type, client_name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index e59e2d1f080..08b03e6ed0b 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -1,7 +1,7 @@
/*
* lm83.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org>
*
* Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
* a sensor chip made by National Semiconductor. It reports up to four
@@ -295,69 +295,40 @@ static int lm83_detect(struct i2c_client *new_client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
- const char *name = "";
+ const char *name;
+ u8 man_id, chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /* Now we do the detection and identification. A negative kind
- * means that the driver was loaded with no force parameter
- * (default), so we must both detect and identify the chip
- * (actually there is only one possible kind of chip for now, LM83).
- * A zero kind means that the driver was loaded with the force
- * parameter, the detection step shall be skipped. A positive kind
- * means that the driver was loaded with the force parameter and a
- * given kind of chip is requested, so both the detection and the
- * identification steps are skipped. */
-
- /* Default to an LM83 if forced */
- if (kind == 0)
- kind = lm83;
-
- if (kind < 0) { /* detection */
- if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
- & 0xA8) != 0x00) ||
- ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
- & 0x48) != 0x00) ||
- ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
- & 0x41) != 0x00)) {
- dev_dbg(&adapter->dev,
- "LM83 detection failed at 0x%02x.\n",
- new_client->addr);
- return -ENODEV;
- }
+ /* Detection */
+ if ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) & 0xA8) ||
+ (i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) & 0x48) ||
+ (i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) & 0x41)) {
+ dev_dbg(&adapter->dev, "LM83 detection failed at 0x%02x\n",
+ new_client->addr);
+ return -ENODEV;
}
- if (kind <= 0) { /* identification */
- u8 man_id, chip_id;
-
- man_id = i2c_smbus_read_byte_data(new_client,
- LM83_REG_R_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(new_client,
- LM83_REG_R_CHIP_ID);
-
- if (man_id == 0x01) { /* National Semiconductor */
- if (chip_id == 0x03) {
- kind = lm83;
- } else
- if (chip_id == 0x01) {
- kind = lm82;
- }
- }
-
- if (kind <= 0) { /* identification failed */
- dev_info(&adapter->dev,
- "Unsupported chip (man_id=0x%02X, "
- "chip_id=0x%02X).\n", man_id, chip_id);
- return -ENODEV;
- }
- }
+ /* Identification */
+ man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID);
+ if (man_id != 0x01) /* National Semiconductor */
+ return -ENODEV;
- if (kind == lm83) {
+ chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID);
+ switch (chip_id) {
+ case 0x03:
name = "lm83";
- } else
- if (kind == lm82) {
+ break;
+ case 0x01:
name = "lm82";
+ break;
+ default:
+ /* identification failed */
+ dev_info(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
}
strlcpy(info->type, name, I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 6c53d987de1..d56da2e7470 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -5,7 +5,7 @@
Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com>
Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de>
Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
- Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2007--2009 Jean Delvare <khali@linux-fr.org>
Chip details at <http://www.national.com/ds/LM/LM85.pdf>
@@ -1162,107 +1162,80 @@ static int lm85_detect(struct i2c_client *client, int kind,
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
const char *type_name;
+ int company, verstep;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
/* We need to be able to do byte I/O */
return -ENODEV;
}
- /* If auto-detecting, determine the chip type */
- if (kind < 0) {
- int company = lm85_read_value(client, LM85_REG_COMPANY);
- int verstep = lm85_read_value(client, LM85_REG_VERSTEP);
-
- dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
- "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
- address, company, verstep);
-
- /* All supported chips have the version in common */
- if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC &&
- (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) {
- dev_dbg(&adapter->dev, "Autodetection failed: "
- "unsupported version\n");
- return -ENODEV;
- }
- kind = any_chip;
-
- /* Now, refine the detection */
- if (company == LM85_COMPANY_NATIONAL) {
- switch (verstep) {
- case LM85_VERSTEP_LM85C:
- kind = lm85c;
- break;
- case LM85_VERSTEP_LM85B:
- kind = lm85b;
- break;
- case LM85_VERSTEP_LM96000_1:
- case LM85_VERSTEP_LM96000_2:
- /* Check for Winbond WPCD377I */
- if (lm85_is_fake(client)) {
- dev_dbg(&adapter->dev,
- "Found Winbond WPCD377I, "
- "ignoring\n");
- return -ENODEV;
- }
- break;
- }
- } else if (company == LM85_COMPANY_ANALOG_DEV) {
- switch (verstep) {
- case LM85_VERSTEP_ADM1027:
- kind = adm1027;
- break;
- case LM85_VERSTEP_ADT7463:
- case LM85_VERSTEP_ADT7463C:
- kind = adt7463;
- break;
- case LM85_VERSTEP_ADT7468_1:
- case LM85_VERSTEP_ADT7468_2:
- kind = adt7468;
- break;
- }
- } else if (company == LM85_COMPANY_SMSC) {
- switch (verstep) {
- case LM85_VERSTEP_EMC6D100_A0:
- case LM85_VERSTEP_EMC6D100_A1:
- /* Note: we can't tell a '100 from a '101 */
- kind = emc6d100;
- break;
- case LM85_VERSTEP_EMC6D102:
- kind = emc6d102;
- break;
+ /* Determine the chip type */
+ company = lm85_read_value(client, LM85_REG_COMPANY);
+ verstep = lm85_read_value(client, LM85_REG_VERSTEP);
+
+ dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
+ "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+ address, company, verstep);
+
+ /* All supported chips have the version in common */
+ if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC &&
+ (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) {
+ dev_dbg(&adapter->dev,
+ "Autodetection failed: unsupported version\n");
+ return -ENODEV;
+ }
+ type_name = "lm85";
+
+ /* Now, refine the detection */
+ if (company == LM85_COMPANY_NATIONAL) {
+ switch (verstep) {
+ case LM85_VERSTEP_LM85C:
+ type_name = "lm85c";
+ break;
+ case LM85_VERSTEP_LM85B:
+ type_name = "lm85b";
+ break;
+ case LM85_VERSTEP_LM96000_1:
+ case LM85_VERSTEP_LM96000_2:
+ /* Check for Winbond WPCD377I */
+ if (lm85_is_fake(client)) {
+ dev_dbg(&adapter->dev,
+ "Found Winbond WPCD377I, ignoring\n");
+ return -ENODEV;
}
- } else {
- dev_dbg(&adapter->dev, "Autodetection failed: "
- "unknown vendor\n");
- return -ENODEV;
+ break;
+ }
+ } else if (company == LM85_COMPANY_ANALOG_DEV) {
+ switch (verstep) {
+ case LM85_VERSTEP_ADM1027:
+ type_name = "adm1027";
+ break;
+ case LM85_VERSTEP_ADT7463:
+ case LM85_VERSTEP_ADT7463C:
+ type_name = "adt7463";
+ break;
+ case LM85_VERSTEP_ADT7468_1:
+ case LM85_VERSTEP_ADT7468_2:
+ type_name = "adt7468";
+ break;
}
+ } else if (company == LM85_COMPANY_SMSC) {
+ switch (verstep) {
+ case LM85_VERSTEP_EMC6D100_A0:
+ case LM85_VERSTEP_EMC6D100_A1:
+ /* Note: we can't tell a '100 from a '101 */
+ type_name = "emc6d100";
+ break;
+ case LM85_VERSTEP_EMC6D102:
+ type_name = "emc6d102";
+ break;
+ }
+ } else {
+ dev_dbg(&adapter->dev,
+ "Autodetection failed: unknown vendor\n");
+ return -ENODEV;
}
- switch (kind) {
- case lm85b:
- type_name = "lm85b";
- break;
- case lm85c:
- type_name = "lm85c";
- break;
- case adm1027:
- type_name = "adm1027";
- break;
- case adt7463:
- type_name = "adt7463";
- break;
- case adt7468:
- type_name = "adt7468";
- break;
- case emc6d100:
- type_name = "emc6d100";
- break;
- case emc6d102:
- type_name = "emc6d102";
- break;
- default:
- type_name = "lm85";
- }
strlcpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index 2e4a3cea95f..4929b1815ee 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -666,37 +666,32 @@ static int lm87_detect(struct i2c_client *new_client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
- static const char *names[] = { "lm87", "adm1024" };
+ const char *name;
+ u8 cid, rev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /* Default to an LM87 if forced */
- if (kind == 0)
- kind = lm87;
+ if (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)
+ return -ENODEV;
/* Now, we do the remaining detection. */
- if (kind < 0) {
- u8 cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID);
- u8 rev = lm87_read_value(new_client, LM87_REG_REVISION);
-
- if (cid == 0x02 /* National Semiconductor */
- && (rev >= 0x01 && rev <= 0x08))
- kind = lm87;
- else if (cid == 0x41 /* Analog Devices */
- && (rev & 0xf0) == 0x10)
- kind = adm1024;
-
- if (kind < 0
- || (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)) {
- dev_dbg(&adapter->dev,
- "LM87 detection failed at 0x%02x.\n",
- new_client->addr);
- return -ENODEV;
- }
+ cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID);
+ rev = lm87_read_value(new_client, LM87_REG_REVISION);
+
+ if (cid == 0x02 /* National Semiconductor */
+ && (rev >= 0x01 && rev <= 0x08))
+ name = "lm87";
+ else if (cid == 0x41 /* Analog Devices */
+ && (rev & 0xf0) == 0x10)
+ name = "adm1024";
+ else {
+ dev_dbg(&adapter->dev, "LM87 detection failed at 0x%02x\n",
+ new_client->addr);
+ return -ENODEV;
}
- strlcpy(info->type, names[kind - 1], I2C_NAME_SIZE);
+ strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 1aff7575799..b7c905f50ed 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1,7 +1,7 @@
/*
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2008 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm83 driver. The LM90 is a sensor chip made by National
* Semiconductor. It reports up to two temperatures (its own plus up to
@@ -661,154 +661,118 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
{
struct i2c_adapter *adapter = new_client->adapter;
int address = new_client->addr;
- const char *name = "";
+ const char *name = NULL;
+ int man_id, chip_id, reg_config1, reg_convrate;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /*
- * Now we do the remaining detection. A negative kind means that
- * the driver was loaded with no force parameter (default), so we
- * must both detect and identify the chip. A zero kind means that
- * the driver was loaded with the force parameter, the detection
- * step shall be skipped. A positive kind means that the driver
- * was loaded with the force parameter and a given kind of chip is
- * requested, so both the detection and the identification steps
- * are skipped.
- */
-
- /* Default to an LM90 if forced */
- if (kind == 0)
- kind = lm90;
-
- if (kind < 0) { /* detection and identification */
- int man_id, chip_id, reg_config1, reg_convrate;
-
- if ((man_id = i2c_smbus_read_byte_data(new_client,
+ /* detection and identification */
+ if ((man_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_MAN_ID)) < 0
- || (chip_id = i2c_smbus_read_byte_data(new_client,
+ || (chip_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CHIP_ID)) < 0
- || (reg_config1 = i2c_smbus_read_byte_data(new_client,
+ || (reg_config1 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG1)) < 0
- || (reg_convrate = i2c_smbus_read_byte_data(new_client,
+ || (reg_convrate = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONVRATE)) < 0)
+ return -ENODEV;
+
+ if ((address == 0x4C || address == 0x4D)
+ && man_id == 0x01) { /* National Semiconductor */
+ int reg_config2;
+
+ reg_config2 = i2c_smbus_read_byte_data(new_client,
+ LM90_REG_R_CONFIG2);
+ if (reg_config2 < 0)
return -ENODEV;
-
- if ((address == 0x4C || address == 0x4D)
- && man_id == 0x01) { /* National Semiconductor */
- int reg_config2;
-
- if ((reg_config2 = i2c_smbus_read_byte_data(new_client,
- LM90_REG_R_CONFIG2)) < 0)
- return -ENODEV;
-
- if ((reg_config1 & 0x2A) == 0x00
- && (reg_config2 & 0xF8) == 0x00
- && reg_convrate <= 0x09) {
- if (address == 0x4C
- && (chip_id & 0xF0) == 0x20) { /* LM90 */
- kind = lm90;
- } else
- if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
- kind = lm99;
- dev_info(&adapter->dev,
- "Assuming LM99 chip at "
- "0x%02x\n", address);
- dev_info(&adapter->dev,
- "If it is an LM89, pass "
- "force_lm86=%d,0x%02x when "
- "loading the lm90 driver\n",
- i2c_adapter_id(adapter),
- address);
- } else
- if (address == 0x4C
- && (chip_id & 0xF0) == 0x10) { /* LM86 */
- kind = lm86;
- }
- }
- } else
- if ((address == 0x4C || address == 0x4D)
- && man_id == 0x41) { /* Analog Devices */
- if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
- && (reg_config1 & 0x3F) == 0x00
- && reg_convrate <= 0x0A) {
- kind = adm1032;
- } else
- if (chip_id == 0x51 /* ADT7461 */
- && (reg_config1 & 0x1B) == 0x00
- && reg_convrate <= 0x0A) {
- kind = adt7461;
- }
- } else
- if (man_id == 0x4D) { /* Maxim */
- /*
- * The MAX6657, MAX6658 and MAX6659 do NOT have a
- * chip_id register. Reading from that address will
- * return the last read value, which in our case is
- * those of the man_id register. Likewise, the config1
- * register seems to lack a low nibble, so the value
- * will be those of the previous read, so in our case
- * those of the man_id register.
- */
- if (chip_id == man_id
- && (address == 0x4C || address == 0x4D)
- && (reg_config1 & 0x1F) == (man_id & 0x0F)
- && reg_convrate <= 0x09) {
- kind = max6657;
+
+ if ((reg_config1 & 0x2A) == 0x00
+ && (reg_config2 & 0xF8) == 0x00
+ && reg_convrate <= 0x09) {
+ if (address == 0x4C
+ && (chip_id & 0xF0) == 0x20) { /* LM90 */
+ name = "lm90";
} else
- /* The chip_id register of the MAX6680 and MAX6681
- * holds the revision of the chip.
- * the lowest bit of the config1 register is unused
- * and should return zero when read, so should the
- * second to last bit of config1 (software reset)
- */
- if (chip_id == 0x01
- && (reg_config1 & 0x03) == 0x00
- && reg_convrate <= 0x07) {
- kind = max6680;
+ if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
+ name = "lm99";
+ dev_info(&adapter->dev,
+ "Assuming LM99 chip at 0x%02x\n",
+ address);
+ dev_info(&adapter->dev,
+ "If it is an LM89, instantiate it "
+ "with the new_device sysfs "
+ "interface\n");
} else
- /* The chip_id register of the MAX6646/6647/6649
- * holds the revision of the chip.
- * The lowest 6 bits of the config1 register are
- * unused and should return zero when read.
- */
- if (chip_id == 0x59
- && (reg_config1 & 0x3f) == 0x00
- && reg_convrate <= 0x07) {
- kind = max6646;
+ if (address == 0x4C
+ && (chip_id & 0xF0) == 0x10) { /* LM86 */
+ name = "lm86";
}
}
-
- if (kind <= 0) { /* identification failed */
- dev_dbg(&adapter->dev,
- "Unsupported chip at 0x%02x (man_id=0x%02X, "
- "chip_id=0x%02X)\n", address, man_id, chip_id);
- return -ENODEV;
+ } else
+ if ((address == 0x4C || address == 0x4D)
+ && man_id == 0x41) { /* Analog Devices */
+ if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
+ && (reg_config1 & 0x3F) == 0x00
+ && reg_convrate <= 0x0A) {
+ name = "adm1032";
+ /* The ADM1032 supports PEC, but only if combined
+ transactions are not used. */
+ if (i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_BYTE))
+ info->flags |= I2C_CLIENT_PEC;
+ } else
+ if (chip_id == 0x51 /* ADT7461 */
+ && (reg_config1 & 0x1B) == 0x00
+ && reg_convrate <= 0x0A) {
+ name = "adt7461";
+ }
+ } else
+ if (man_id == 0x4D) { /* Maxim */
+ /*
+ * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id
+ * register. Reading from that address will return the last
+ * read value, which in our case is those of the man_id
+ * register. Likewise, the config1 register seems to lack a
+ * low nibble, so the value will be those of the previous
+ * read, so in our case those of the man_id register.
+ */
+ if (chip_id == man_id
+ && (address == 0x4C || address == 0x4D)
+ && (reg_config1 & 0x1F) == (man_id & 0x0F)
+ && reg_convrate <= 0x09) {
+ name = "max6657";
+ } else
+ /*
+ * The chip_id register of the MAX6680 and MAX6681 holds the
+ * revision of the chip. The lowest bit of the config1 register
+ * is unused and should return zero when read, so should the
+ * second to last bit of config1 (software reset).
+ */
+ if (chip_id == 0x01
+ && (reg_config1 & 0x03) == 0x00
+ && reg_convrate <= 0x07) {
+ name = "max6680";
+ } else
+ /*
+ * The chip_id register of the MAX6646/6647/6649 holds the
+ * revision of the chip. The lowest 6 bits of the config1
+ * register are unused and should return zero when read.
+ */
+ if (chip_id == 0x59
+ && (reg_config1 & 0x3f) == 0x00
+ && reg_convrate <= 0x07) {
+ name = "max6646";
}
}
- /* Fill the i2c board info */
- if (kind == lm90) {
- name = "lm90";
- } else if (kind == adm1032) {
- name = "adm1032";
- /* The ADM1032 supports PEC, but only if combined
- transactions are not used. */
- if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- info->flags |= I2C_CLIENT_PEC;
- } else if (kind == lm99) {
- name = "lm99";
- } else if (kind == lm86) {
- name = "lm86";
- } else if (kind == max6657) {
- name = "max6657";
- } else if (kind == max6680) {
- name = "max6680";
- } else if (kind == adt7461) {
- name = "adt7461";
- } else if (kind == max6646) {
- name = "max6646";
+ if (!name) { /* identification failed */
+ dev_dbg(&adapter->dev,
+ "Unsupported chip at 0x%02x (man_id=0x%02X, "
+ "chip_id=0x%02X)\n", address, man_id, chip_id);
+ return -ENODEV;
}
+
strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index b2e00c5a7ee..47ac698709d 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -323,31 +323,22 @@ static int lm92_detect(struct i2c_client *new_client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
+ u8 config;
+ u16 man_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
- /* A negative kind means that the driver was loaded with no force
- parameter (default), so we must identify the chip. */
- if (kind < 0) {
- u8 config = i2c_smbus_read_byte_data(new_client,
- LM92_REG_CONFIG);
- u16 man_id = i2c_smbus_read_word_data(new_client,
- LM92_REG_MAN_ID);
-
- if ((config & 0xe0) == 0x00
- && man_id == 0x0180) {
- pr_info("lm92: Found National Semiconductor LM92 chip\n");
- kind = lm92;
- } else
- if (max6635_check(new_client)) {
- pr_info("lm92: Found Maxim MAX6635 chip\n");
- kind = lm92; /* No separate prefix */
- }
- else
- return -ENODEV;
- }
+ config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG);
+ man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID);
+
+ if ((config & 0xe0) == 0x00 && man_id == 0x0180)
+ pr_info("lm92: Found National Semiconductor LM92 chip\n");
+ else if (max6635_check(new_client))
+ pr_info("lm92: Found Maxim MAX6635 chip\n");
+ else
+ return -ENODEV;
strlcpy(info->type, "lm92", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index fc36cadf36f..124dd7cea54 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -928,7 +928,7 @@ static void lm93_update_client_common(struct lm93_data *data,
data->prochot_interval = lm93_read_byte(client,
LM93_REG_PROCHOT_INTERVAL);
- /* Fan Boost Termperature registers */
+ /* Fan Boost Temperature registers */
for (i = 0; i < 4; i++)
data->boost[i] = lm93_read_byte(client, LM93_REG_BOOST(i));
@@ -2505,34 +2505,24 @@ static int lm93_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int mfr, ver;
if (!i2c_check_functionality(adapter, LM93_SMBUS_FUNC_MIN))
return -ENODEV;
/* detection */
- if (kind < 0) {
- int mfr = lm93_read_byte(client, LM93_REG_MFR_ID);
-
- if (mfr != 0x01) {
- dev_dbg(&adapter->dev,"detect failed, "
- "bad manufacturer id 0x%02x!\n", mfr);
- return -ENODEV;
- }
+ mfr = lm93_read_byte(client, LM93_REG_MFR_ID);
+ if (mfr != 0x01) {
+ dev_dbg(&adapter->dev,
+ "detect failed, bad manufacturer id 0x%02x!\n", mfr);
+ return -ENODEV;
}
- if (kind <= 0) {
- int ver = lm93_read_byte(client, LM93_REG_VER);
-
- if ((ver == LM93_MFR_ID) || (ver == LM93_MFR_ID_PROTOTYPE)) {
- kind = lm93;
- } else {
- dev_dbg(&adapter->dev,"detect failed, "
- "bad version id 0x%02x!\n", ver);
- if (kind == 0)
- dev_dbg(&adapter->dev,
- "(ignored 'force' parameter)\n");
- return -ENODEV;
- }
+ ver = lm93_read_byte(client, LM93_REG_VER);
+ if (ver != LM93_MFR_ID && ver != LM93_MFR_ID_PROTOTYPE) {
+ dev_dbg(&adapter->dev,
+ "detect failed, bad version id 0x%02x!\n", ver);
+ return -ENODEV;
}
strlcpy(info->type, "lm93", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index e34f9e402a2..906b896cf1d 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -315,51 +315,23 @@ static int lm95241_detect(struct i2c_client *new_client, int kind,
{
struct i2c_adapter *adapter = new_client->adapter;
int address = new_client->addr;
- const char *name = "";
+ const char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /*
- * Now we do the remaining detection. A negative kind means that
- * the driver was loaded with no force parameter (default), so we
- * must both detect and identify the chip. A zero kind means that
- * the driver was loaded with the force parameter, the detection
- * step shall be skipped. A positive kind means that the driver
- * was loaded with the force parameter and a given kind of chip is
- * requested, so both the detection and the identification steps
- * are skipped.
- */
- if (kind < 0) { /* detection */
- if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
- != MANUFACTURER_ID)
- || (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
- < DEFAULT_REVISION)) {
- dev_dbg(&adapter->dev,
- "LM95241 detection failed at 0x%02x.\n",
- address);
- return -ENODEV;
- }
- }
-
- if (kind <= 0) { /* identification */
- if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
- == MANUFACTURER_ID)
- && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
- >= DEFAULT_REVISION)) {
-
- kind = lm95241;
-
- if (kind <= 0) { /* identification failed */
- dev_info(&adapter->dev, "Unsupported chip\n");
- return -ENODEV;
- }
- }
+ if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
+ == MANUFACTURER_ID)
+ && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
+ >= DEFAULT_REVISION)) {
+ name = "lm95241";
+ } else {
+ dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n",
+ address);
+ return -ENODEV;
}
/* Fill the i2c board info */
- if (kind == lm95241)
- name = "lm95241";
strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index 7897754f3a5..7fcf5ff89e7 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -226,58 +226,34 @@ static const struct attribute_group max1619_group = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int max1619_detect(struct i2c_client *new_client, int kind,
+static int max1619_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
- struct i2c_adapter *adapter = new_client->adapter;
- u8 reg_config=0, reg_convrate=0, reg_status=0;
+ struct i2c_adapter *adapter = client->adapter;
+ u8 reg_config, reg_convrate, reg_status, man_id, chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /*
- * Now we do the remaining detection. A negative kind means that
- * the driver was loaded with no force parameter (default), so we
- * must both detect and identify the chip. A zero kind means that
- * the driver was loaded with the force parameter, the detection
- * step shall be skipped. A positive kind means that the driver
- * was loaded with the force parameter and a given kind of chip is
- * requested, so both the detection and the identification steps
- * are skipped.
- */
- if (kind < 0) { /* detection */
- reg_config = i2c_smbus_read_byte_data(new_client,
- MAX1619_REG_R_CONFIG);
- reg_convrate = i2c_smbus_read_byte_data(new_client,
- MAX1619_REG_R_CONVRATE);
- reg_status = i2c_smbus_read_byte_data(new_client,
- MAX1619_REG_R_STATUS);
- if ((reg_config & 0x03) != 0x00
- || reg_convrate > 0x07 || (reg_status & 0x61 ) !=0x00) {
- dev_dbg(&adapter->dev,
- "MAX1619 detection failed at 0x%02x.\n",
- new_client->addr);
- return -ENODEV;
- }
+ /* detection */
+ reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
+ reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE);
+ reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS);
+ if ((reg_config & 0x03) != 0x00
+ || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) {
+ dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
}
- if (kind <= 0) { /* identification */
- u8 man_id, chip_id;
-
- man_id = i2c_smbus_read_byte_data(new_client,
- MAX1619_REG_R_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(new_client,
- MAX1619_REG_R_CHIP_ID);
-
- if ((man_id == 0x4D) && (chip_id == 0x04))
- kind = max1619;
-
- if (kind <= 0) { /* identification failed */
- dev_info(&adapter->dev,
- "Unsupported chip (man_id=0x%02X, "
- "chip_id=0x%02X).\n", man_id, chip_id);
- return -ENODEV;
- }
+ /* identification */
+ man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID);
+ if (man_id != 0x4D || chip_id != 0x04) {
+ dev_info(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n",
+ man_id, chip_id);
+ return -ENODEV;
}
strlcpy(info->type, "max1619", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index 58f66be61b1..1da561e0cb3 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -534,7 +534,7 @@ static int max6650_detect(struct i2c_client *client, int kind,
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
- dev_dbg(&adapter->dev, "max6650_detect called, kind = %d\n", kind);
+ dev_dbg(&adapter->dev, "max6650_detect called\n");
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support "
@@ -542,23 +542,7 @@ static int max6650_detect(struct i2c_client *client, int kind,
return -ENODEV;
}
- /*
- * Now we do the remaining detection. A negative kind means that
- * the driver was loaded with no force parameter (default), so we
- * must both detect and identify the chip (actually there is only
- * one possible kind of chip for now, max6650). A zero kind means that
- * the driver was loaded with the force parameter, the detection
- * step shall be skipped. A positive kind means that the driver
- * was loaded with the force parameter and a given kind of chip is
- * requested, so both the detection and the identification steps
- * are skipped.
- *
- * Currently I can find no way to distinguish between a MAX6650 and
- * a MAX6651. This driver has only been tried on the former.
- */
-
- if ((kind < 0) &&
- ( (i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0)
+ if (((i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0)
||(i2c_smbus_read_byte_data(client, MAX6650_REG_GPIO_STAT) & 0xE0)
||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN) & 0xE0)
||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM) & 0xE0)
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
new file mode 100644
index 00000000000..883fa8197da
--- /dev/null
+++ b/drivers/hwmon/mc13783-adc.c
@@ -0,0 +1,236 @@
+/*
+ * Driver for the Freescale Semiconductor MC13783 adc.
+ *
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/mfd/mc13783-private.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/err.h>
+
+#define MC13783_ADC_NAME "mc13783-adc"
+
+struct mc13783_adc_priv {
+ struct mc13783 *mc13783;
+ struct device *hwmon_dev;
+};
+
+static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ return sprintf(buf, "mc13783_adc\n");
+}
+
+static int mc13783_adc_read(struct device *dev,
+ struct device_attribute *devattr, unsigned int *val)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ unsigned int channel = attr->index;
+ unsigned int sample[4];
+ int ret;
+
+ ret = mc13783_adc_do_conversion(priv->mc13783,
+ MC13783_ADC_MODE_MULT_CHAN,
+ channel, sample);
+ if (ret)
+ return ret;
+
+ channel &= 0x7;
+
+ *val = (sample[channel % 4] >> (channel > 3 ? 14 : 2)) & 0x3ff;
+
+ return 0;
+}
+
+static ssize_t mc13783_adc_read_bp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ unsigned val;
+ int ret = mc13783_adc_read(dev, devattr, &val);
+
+ if (ret)
+ return ret;
+
+ /*
+ * BP (channel 2) reports with offset 2.4V to the actual value to fit
+ * the input range of the ADC. unit = 2.25mV = 9/4 mV.
+ */
+ val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t mc13783_adc_read_gp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ unsigned val;
+ int ret = mc13783_adc_read(dev, devattr, &val);
+
+ if (ret)
+ return ret;
+
+ /*
+ * input range is [0, 2.3V], val has 10 bits, so each bit
+ * is worth 9/4 mV.
+ */
+ val = DIV_ROUND_CLOSEST(val * 9, 4);
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, mc13783_adc_show_name, NULL);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, mc13783_adc_read_bp, NULL, 2);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, mc13783_adc_read_gp, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, mc13783_adc_read_gp, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, mc13783_adc_read_gp, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, mc13783_adc_read_gp, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, mc13783_adc_read_gp, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, mc13783_adc_read_gp, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, mc13783_adc_read_gp, NULL, 11);
+static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, mc13783_adc_read_gp, NULL, 12);
+static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13);
+static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14);
+static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15);
+
+static struct attribute *mc13783_attr[] = {
+ &dev_attr_name.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mc13783_group = {
+ .attrs = mc13783_attr,
+};
+
+/* last four channels may be occupied by the touchscreen */
+static struct attribute *mc13783_attr_ts[] = {
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in13_input.dev_attr.attr,
+ &sensor_dev_attr_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mc13783_group_ts = {
+ .attrs = mc13783_attr_ts,
+};
+
+static int __init mc13783_adc_probe(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+
+ platform_set_drvdata(pdev, priv);
+
+ /* Register sysfs hooks */
+ ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group);
+ if (ret)
+ goto out_err_create1;
+
+ if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
+ if (ret)
+ goto out_err_create2;
+
+ priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(priv->hwmon_dev)) {
+ ret = PTR_ERR(priv->hwmon_dev);
+ dev_err(&pdev->dev,
+ "hwmon_device_register failed with %d.\n", ret);
+ goto out_err_register;
+ }
+
+
+ return 0;
+
+out_err_register:
+
+ if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
+out_err_create2:
+
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
+out_err_create1:
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+
+ return ret;
+}
+
+static int __devexit mc13783_adc_remove(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(priv->hwmon_dev);
+
+ if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
+
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_adc_driver = {
+ .remove = __devexit_p(mc13783_adc_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MC13783_ADC_NAME,
+ },
+};
+
+static int __init mc13783_adc_init(void)
+{
+ return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe);
+}
+
+static void __exit mc13783_adc_exit(void)
+{
+ platform_driver_unregister(&mc13783_adc_driver);
+}
+
+module_init(mc13783_adc_init);
+module_exit(mc13783_adc_exit);
+
+MODULE_DESCRIPTION("MC13783 ADC driver");
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MC13783_ADC_NAME);
diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c
index 71835412529..3f3f9a47acf 100644
--- a/drivers/hwmon/s3c-hwmon.c
+++ b/drivers/hwmon/s3c-hwmon.c
@@ -323,7 +323,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev)
}
for (i = 0; i < ARRAY_SIZE(pdata->in); i++) {
- struct s3c24xx_adc_hwmon_incfg *cfg = pdata->in[i];
+ struct s3c_hwmon_chcfg *cfg = pdata->in[i];
if (!cfg)
continue;
@@ -333,7 +333,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev)
"channel %d multiplier too large\n",
i);
- if (cfg->divider == 0) {
+ if (cfg->div == 0) {
dev_err(&dev->dev, "channel %d divider zero\n", i);
continue;
}
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
index 8bb5cb532d4..4d88c045781 100644
--- a/drivers/hwmon/smsc47m192.c
+++ b/drivers/hwmon/smsc47m192.c
@@ -491,24 +491,22 @@ static int smsc47m192_detect(struct i2c_client *client, int kind,
return -ENODEV;
/* Detection criteria from sensors_detect script */
- if (kind < 0) {
- if (i2c_smbus_read_byte_data(client,
+ version = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VERSION);
+ if (i2c_smbus_read_byte_data(client,
SMSC47M192_REG_COMPANY_ID) == 0x55
- && ((version = i2c_smbus_read_byte_data(client,
- SMSC47M192_REG_VERSION)) & 0xf0) == 0x20
- && (i2c_smbus_read_byte_data(client,
+ && (version & 0xf0) == 0x20
+ && (i2c_smbus_read_byte_data(client,
SMSC47M192_REG_VID) & 0x70) == 0x00
- && (i2c_smbus_read_byte_data(client,
+ && (i2c_smbus_read_byte_data(client,
SMSC47M192_REG_VID4) & 0xfe) == 0x80) {
- dev_info(&adapter->dev,
- "found SMSC47M192 or compatible, "
- "version 2, stepping A%d\n", version & 0x0f);
- } else {
- dev_dbg(&adapter->dev,
- "SMSC47M192 detection failed at 0x%02x\n",
- client->addr);
- return -ENODEV;
- }
+ dev_info(&adapter->dev,
+ "found SMSC47M192 or compatible, "
+ "version 2, stepping A%d\n", version & 0x0f);
+ } else {
+ dev_dbg(&adapter->dev,
+ "SMSC47M192 detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
}
strlcpy(info->type, "smsc47m192", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c
index 7d97431e132..4b793849c73 100644
--- a/drivers/hwmon/thmc50.c
+++ b/drivers/hwmon/thmc50.c
@@ -36,7 +36,11 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD_2(thmc50, adm1022);
-I2C_CLIENT_MODULE_PARM(adm1022_temp3, "List of adapter,address pairs "
+
+static unsigned short adm1022_temp3[16];
+static unsigned int adm1022_temp3_num;
+module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0);
+MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs "
"to enable 3rd temperature (ADM1022 only)");
/* Many THMC50 constants specified below */
@@ -289,7 +293,6 @@ static int thmc50_detect(struct i2c_client *client, int kind,
unsigned revision;
unsigned config;
struct i2c_adapter *adapter = client->adapter;
- int err = 0;
const char *type_name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -301,31 +304,13 @@ static int thmc50_detect(struct i2c_client *client, int kind,
pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n",
client->addr, i2c_adapter_id(client->adapter));
- /* Now, we do the remaining detection. */
company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID);
revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE);
config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
+ if (revision < 0xc0 || (config & 0x10))
+ return -ENODEV;
- if (kind == 0)
- kind = thmc50;
- else if (kind < 0) {
- err = -ENODEV;
- if (revision >= 0xc0 && ((config & 0x10) == 0)) {
- if (company == 0x49) {
- kind = thmc50;
- err = 0;
- } else if (company == 0x41) {
- kind = adm1022;
- err = 0;
- }
- }
- }
- if (err == -ENODEV) {
- pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n");
- return err;
- }
-
- if (kind == adm1022) {
+ if (company == 0x41) {
int id = i2c_adapter_id(client->adapter);
int i;
@@ -340,9 +325,13 @@ static int thmc50_detect(struct i2c_client *client, int kind,
config);
break;
}
- } else {
+ } else if (company == 0x49) {
type_name = "thmc50";
+ } else {
+ pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n");
+ return -ENODEV;
}
+
pr_debug("thmc50: Detected %s (version %x, revision %x)\n",
type_name, (revision >> 4) - 0xc, revision & 0xf);
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index 7b34f2cd08b..ee9673467c4 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -488,46 +488,43 @@ static void tmp401_init_client(struct i2c_client *client)
i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
}
-static int tmp401_detect(struct i2c_client *client, int kind,
+static int tmp401_detect(struct i2c_client *client, int _kind,
struct i2c_board_info *info)
{
+ enum chips kind;
struct i2c_adapter *adapter = client->adapter;
+ u8 reg;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* Detect and identify the chip */
- if (kind <= 0) {
- u8 reg;
-
- reg = i2c_smbus_read_byte_data(client,
- TMP401_MANUFACTURER_ID_REG);
- if (reg != TMP401_MANUFACTURER_ID)
- return -ENODEV;
-
- reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
-
- switch (reg) {
- case TMP401_DEVICE_ID:
- kind = tmp401;
- break;
- case TMP411_DEVICE_ID:
- kind = tmp411;
- break;
- default:
- return -ENODEV;
- }
+ reg = i2c_smbus_read_byte_data(client, TMP401_MANUFACTURER_ID_REG);
+ if (reg != TMP401_MANUFACTURER_ID)
+ return -ENODEV;
- reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
- if (reg & 0x1b)
- return -ENODEV;
+ reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
- reg = i2c_smbus_read_byte_data(client,
- TMP401_CONVERSION_RATE_READ);
- /* Datasheet says: 0x1-0x6 */
- if (reg > 15)
- return -ENODEV;
+ switch (reg) {
+ case TMP401_DEVICE_ID:
+ kind = tmp401;
+ break;
+ case TMP411_DEVICE_ID:
+ kind = tmp411;
+ break;
+ default:
+ return -ENODEV;
}
+
+ reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+ if (reg & 0x1b)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP401_CONVERSION_RATE_READ);
+ /* Datasheet says: 0x1-0x6 */
+ if (reg > 15)
+ return -ENODEV;
+
strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
index 20924343431..bb5464a289c 100644
--- a/drivers/hwmon/tmp421.c
+++ b/drivers/hwmon/tmp421.c
@@ -223,39 +223,36 @@ static int tmp421_init_client(struct i2c_client *client)
return 0;
}
-static int tmp421_detect(struct i2c_client *client, int kind,
+static int tmp421_detect(struct i2c_client *client, int _kind,
struct i2c_board_info *info)
{
+ enum chips kind;
struct i2c_adapter *adapter = client->adapter;
const char *names[] = { "TMP421", "TMP422", "TMP423" };
+ u8 reg;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind <= 0) {
- u8 reg;
-
- reg = i2c_smbus_read_byte_data(client,
- TMP421_MANUFACTURER_ID_REG);
- if (reg != TMP421_MANUFACTURER_ID)
- return -ENODEV;
-
- reg = i2c_smbus_read_byte_data(client,
- TMP421_DEVICE_ID_REG);
- switch (reg) {
- case TMP421_DEVICE_ID:
- kind = tmp421;
- break;
- case TMP422_DEVICE_ID:
- kind = tmp422;
- break;
- case TMP423_DEVICE_ID:
- kind = tmp423;
- break;
- default:
- return -ENODEV;
- }
+ reg = i2c_smbus_read_byte_data(client, TMP421_MANUFACTURER_ID_REG);
+ if (reg != TMP421_MANUFACTURER_ID)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, TMP421_DEVICE_ID_REG);
+ switch (reg) {
+ case TMP421_DEVICE_ID:
+ kind = tmp421;
+ break;
+ case TMP422_DEVICE_ID:
+ kind = tmp422;
+ break;
+ case TMP423_DEVICE_ID:
+ kind = tmp423;
+ break;
+ default:
+ return -ENODEV;
}
+
strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE);
dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n",
names[kind - 1], client->addr);
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index 2be28ac4ede..b257c722373 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -59,10 +59,11 @@ static struct platform_device *pdev;
#define DRVNAME "w83627hf"
enum chips { w83627hf, w83627thf, w83697hf, w83637hf, w83687thf };
-static u16 force_addr;
-module_param(force_addr, ushort, 0);
-MODULE_PARM_DESC(force_addr,
- "Initialize the base address of the sensors");
+struct w83627hf_sio_data {
+ enum chips type;
+ int sioaddr;
+};
+
static u8 force_i2c = 0x1f;
module_param(force_i2c, byte, 0);
MODULE_PARM_DESC(force_i2c,
@@ -77,9 +78,7 @@ module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
/* modified from kernel/include/traps.c */
-static int REG; /* The register to read/write */
#define DEV 0x07 /* Register: Logical device select */
-static int VAL; /* The value to read/write */
/* logical device numbers for superio_select (below) */
#define W83627HF_LD_FDC 0x00
@@ -109,37 +108,37 @@ static int VAL; /* The value to read/write */
#define W83687THF_VID_DATA 0xF1 /* w83687thf only */
static inline void
-superio_outb(int reg, int val)
+superio_outb(struct w83627hf_sio_data *sio, int reg, int val)
{
- outb(reg, REG);
- outb(val, VAL);
+ outb(reg, sio->sioaddr);
+ outb(val, sio->sioaddr + 1);
}
static inline int
-superio_inb(int reg)
+superio_inb(struct w83627hf_sio_data *sio, int reg)
{
- outb(reg, REG);
- return inb(VAL);
+ outb(reg, sio->sioaddr);
+ return inb(sio->sioaddr + 1);
}
static inline void
-superio_select(int ld)
+superio_select(struct w83627hf_sio_data *sio, int ld)
{
- outb(DEV, REG);
- outb(ld, VAL);
+ outb(DEV, sio->sioaddr);
+ outb(ld, sio->sioaddr + 1);
}
static inline void
-superio_enter(void)
+superio_enter(struct w83627hf_sio_data *sio)
{
- outb(0x87, REG);
- outb(0x87, REG);
+ outb(0x87, sio->sioaddr);
+ outb(0x87, sio->sioaddr);
}
static inline void
-superio_exit(void)
+superio_exit(struct w83627hf_sio_data *sio)
{
- outb(0xAA, REG);
+ outb(0xAA, sio->sioaddr);
}
#define W627_DEVID 0x52
@@ -380,10 +379,6 @@ struct w83627hf_data {
u8 vrm_ovt; /* Register value, 627THF/637HF/687THF only */
};
-struct w83627hf_sio_data {
- enum chips type;
-};
-
static int w83627hf_probe(struct platform_device *pdev);
static int __devexit w83627hf_remove(struct platform_device *pdev);
@@ -1140,11 +1135,8 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
"W83687THF",
};
- REG = sioaddr;
- VAL = sioaddr + 1;
-
- superio_enter();
- val = force_id ? force_id : superio_inb(DEVID);
+ superio_enter(sio_data);
+ val = force_id ? force_id : superio_inb(sio_data, DEVID);
switch (val) {
case W627_DEVID:
sio_data->type = w83627hf;
@@ -1168,16 +1160,9 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
goto exit;
}
- superio_select(W83627HF_LD_HWM);
- force_addr &= WINB_ALIGNMENT;
- if (force_addr) {
- printk(KERN_WARNING DRVNAME ": Forcing address 0x%x\n",
- force_addr);
- superio_outb(WINB_BASE_REG, force_addr >> 8);
- superio_outb(WINB_BASE_REG + 1, force_addr & 0xff);
- }
- val = (superio_inb(WINB_BASE_REG) << 8) |
- superio_inb(WINB_BASE_REG + 1);
+ superio_select(sio_data, W83627HF_LD_HWM);
+ val = (superio_inb(sio_data, WINB_BASE_REG) << 8) |
+ superio_inb(sio_data, WINB_BASE_REG + 1);
*addr = val & WINB_ALIGNMENT;
if (*addr == 0) {
printk(KERN_WARNING DRVNAME ": Base address not set, "
@@ -1185,18 +1170,19 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
goto exit;
}
- val = superio_inb(WINB_ACT_REG);
+ val = superio_inb(sio_data, WINB_ACT_REG);
if (!(val & 0x01)) {
printk(KERN_WARNING DRVNAME ": Enabling HWM logical device\n");
- superio_outb(WINB_ACT_REG, val | 0x01);
+ superio_outb(sio_data, WINB_ACT_REG, val | 0x01);
}
err = 0;
+ sio_data->sioaddr = sioaddr;
pr_info(DRVNAME ": Found %s chip at %#x\n",
names[sio_data->type], *addr);
exit:
- superio_exit();
+ superio_exit(sio_data);
return err;
}
@@ -1511,20 +1497,21 @@ static int w83627hf_read_value(struct w83627hf_data *data, u16 reg)
static int __devinit w83627thf_read_gpio5(struct platform_device *pdev)
{
+ struct w83627hf_sio_data *sio_data = pdev->dev.platform_data;
int res = 0xff, sel;
- superio_enter();
- superio_select(W83627HF_LD_GPIO5);
+ superio_enter(sio_data);
+ superio_select(sio_data, W83627HF_LD_GPIO5);
/* Make sure these GPIO pins are enabled */
- if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) {
+ if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) {
dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n");
goto exit;
}
/* Make sure the pins are configured for input
There must be at least five (VRM 9), and possibly 6 (VRM 10) */
- sel = superio_inb(W83627THF_GPIO5_IOSR) & 0x3f;
+ sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f;
if ((sel & 0x1f) != 0x1f) {
dev_dbg(&pdev->dev, "GPIO5 not configured for VID "
"function\n");
@@ -1532,37 +1519,38 @@ static int __devinit w83627thf_read_gpio5(struct platform_device *pdev)
}
dev_info(&pdev->dev, "Reading VID from GPIO5\n");
- res = superio_inb(W83627THF_GPIO5_DR) & sel;
+ res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel;
exit:
- superio_exit();
+ superio_exit(sio_data);
return res;
}
static int __devinit w83687thf_read_vid(struct platform_device *pdev)
{
+ struct w83627hf_sio_data *sio_data = pdev->dev.platform_data;
int res = 0xff;
- superio_enter();
- superio_select(W83627HF_LD_HWM);
+ superio_enter(sio_data);
+ superio_select(sio_data, W83627HF_LD_HWM);
/* Make sure these GPIO pins are enabled */
- if (!(superio_inb(W83687THF_VID_EN) & (1 << 2))) {
+ if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) {
dev_dbg(&pdev->dev, "VID disabled, no VID function\n");
goto exit;
}
/* Make sure the pins are configured for input */
- if (!(superio_inb(W83687THF_VID_CFG) & (1 << 4))) {
+ if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) {
dev_dbg(&pdev->dev, "VID configured as output, "
"no VID function\n");
goto exit;
}
- res = superio_inb(W83687THF_VID_DATA) & 0x3f;
+ res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f;
exit:
- superio_exit();
+ superio_exit(sio_data);
return res;
}
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index d27ed1bac00..7ab7967da0a 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -1054,11 +1054,11 @@ static int
w83781d_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
- int val1 = 0, val2;
+ int val1, val2;
struct w83781d_data *isa = w83781d_data_if_isa();
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
- const char *client_name = "";
+ const char *client_name;
enum vendor { winbond, asus } vendid;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -1070,98 +1070,73 @@ w83781d_detect(struct i2c_client *client, int kind,
if (isa)
mutex_lock(&isa->update_lock);
- /* The w8378?d may be stuck in some other bank than bank 0. This may
- make reading other information impossible. Specify a force=... or
- force_*=... parameter, and the Winbond will be reset to the right
- bank. */
- if (kind < 0) {
- if (i2c_smbus_read_byte_data
- (client, W83781D_REG_CONFIG) & 0x80) {
- dev_dbg(&adapter->dev, "Detection of w83781d chip "
- "failed at step 3\n");
- goto err_nodev;
- }
- val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK);
- val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
- /* Check for Winbond or Asus ID if in bank 0 */
- if ((!(val1 & 0x07)) &&
- (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
- || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) {
- dev_dbg(&adapter->dev, "Detection of w83781d chip "
- "failed at step 4\n");
+ if (i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG) & 0x80) {
+ dev_dbg(&adapter->dev,
+ "Detection of w83781d chip failed at step 3\n");
+ goto err_nodev;
+ }
+
+ val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK);
+ val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
+ /* Check for Winbond or Asus ID if in bank 0 */
+ if (!(val1 & 0x07) &&
+ ((!(val1 & 0x80) && val2 != 0xa3 && val2 != 0xc3) ||
+ ( (val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) {
+ dev_dbg(&adapter->dev,
+ "Detection of w83781d chip failed at step 4\n");
+ goto err_nodev;
+ }
+ /* If Winbond SMBus, check address at 0x48.
+ Asus doesn't support, except for as99127f rev.2 */
+ if ((!(val1 & 0x80) && val2 == 0xa3) ||
+ ( (val1 & 0x80) && val2 == 0x5c)) {
+ if (i2c_smbus_read_byte_data(client, W83781D_REG_I2C_ADDR)
+ != address) {
+ dev_dbg(&adapter->dev,
+ "Detection of w83781d chip failed at step 5\n");
goto err_nodev;
}
- /* If Winbond SMBus, check address at 0x48.
- Asus doesn't support, except for as99127f rev.2 */
- if ((!(val1 & 0x80) && (val2 == 0xa3)) ||
- ((val1 & 0x80) && (val2 == 0x5c))) {
- if (i2c_smbus_read_byte_data
- (client, W83781D_REG_I2C_ADDR) != address) {
- dev_dbg(&adapter->dev, "Detection of w83781d "
- "chip failed at step 5\n");
- goto err_nodev;
- }
- }
}
- /* We have either had a force parameter, or we have already detected the
- Winbond. Put it now into bank 0 and Vendor ID High Byte */
+ /* Put it now into bank 0 and Vendor ID High Byte */
i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
(i2c_smbus_read_byte_data(client, W83781D_REG_BANK)
& 0x78) | 0x80);
- /* Determine the chip type. */
- if (kind <= 0) {
- /* get vendor ID */
- val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
- if (val2 == 0x5c)
- vendid = winbond;
- else if (val2 == 0x12)
- vendid = asus;
- else {
- dev_dbg(&adapter->dev, "w83781d chip vendor is "
- "neither Winbond nor Asus\n");
- goto err_nodev;
- }
-
- val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID);
- if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
- kind = w83781d;
- else if (val1 == 0x30 && vendid == winbond)
- kind = w83782d;
- else if (val1 == 0x40 && vendid == winbond && address == 0x2d)
- kind = w83783s;
- else if (val1 == 0x31)
- kind = as99127f;
- else {
- if (kind == 0)
- dev_warn(&adapter->dev, "Ignoring 'force' "
- "parameter for unknown chip at "
- "address 0x%02x\n", address);
- goto err_nodev;
- }
-
- if ((kind == w83781d || kind == w83782d)
- && w83781d_alias_detect(client, val1)) {
- dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
- "be the same as ISA device\n", address);
- goto err_nodev;
- }
+ /* Get the vendor ID */
+ val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
+ if (val2 == 0x5c)
+ vendid = winbond;
+ else if (val2 == 0x12)
+ vendid = asus;
+ else {
+ dev_dbg(&adapter->dev,
+ "w83781d chip vendor is neither Winbond nor Asus\n");
+ goto err_nodev;
}
- if (isa)
- mutex_unlock(&isa->update_lock);
-
- if (kind == w83781d) {
+ /* Determine the chip type. */
+ val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID);
+ if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
client_name = "w83781d";
- } else if (kind == w83782d) {
+ else if (val1 == 0x30 && vendid == winbond)
client_name = "w83782d";
- } else if (kind == w83783s) {
+ else if (val1 == 0x40 && vendid == winbond && address == 0x2d)
client_name = "w83783s";
- } else if (kind == as99127f) {
+ else if (val1 == 0x31)
client_name = "as99127f";
+ else
+ goto err_nodev;
+
+ if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) {
+ dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
+ "be the same as ISA device\n", address);
+ goto err_nodev;
}
+ if (isa)
+ mutex_unlock(&isa->update_lock);
+
strlcpy(info->type, client_name, I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 97851c5ba3a..0410bf12c52 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -1270,56 +1270,32 @@ static int w83791d_detect(struct i2c_client *client, int kind,
return -ENODEV;
}
- /* The w83791d may be stuck in some other bank than bank 0. This may
- make reading other information impossible. Specify a force=...
- parameter, and the Winbond will be reset to the right bank. */
- if (kind < 0) {
- if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) {
- return -ENODEV;
- }
- val1 = w83791d_read(client, W83791D_REG_BANK);
- val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
- /* Check for Winbond ID if in bank 0 */
- if (!(val1 & 0x07)) {
- /* yes it is Bank0 */
- if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
- ((val1 & 0x80) && (val2 != 0x5c))) {
- return -ENODEV;
- }
- }
- /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR
- should match */
- if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) {
+ if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80)
+ return -ENODEV;
+
+ val1 = w83791d_read(client, W83791D_REG_BANK);
+ val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
+ /* Check for Winbond ID if in bank 0 */
+ if (!(val1 & 0x07)) {
+ if ((!(val1 & 0x80) && val2 != 0xa3) ||
+ ( (val1 & 0x80) && val2 != 0x5c)) {
return -ENODEV;
}
}
+ /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR
+ should match */
+ if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address)
+ return -ENODEV;
- /* We either have a force parameter or we have reason to
- believe it is a Winbond chip. Either way, we want bank 0 and
- Vendor ID high byte */
+ /* We want bank 0 and Vendor ID high byte */
val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78;
w83791d_write(client, W83791D_REG_BANK, val1 | 0x80);
/* Verify it is a Winbond w83791d */
- if (kind <= 0) {
- /* get vendor ID */
- val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
- if (val2 != 0x5c) { /* the vendor is NOT Winbond */
- return -ENODEV;
- }
- val1 = w83791d_read(client, W83791D_REG_WCHIPID);
- if (val1 == 0x71) {
- kind = w83791d;
- } else {
- if (kind == 0)
- dev_warn(&adapter->dev,
- "w83791d: Ignoring 'force' parameter "
- "for unknown chip at adapter %d, "
- "address 0x%02x\n",
- i2c_adapter_id(adapter), address);
- return -ENODEV;
- }
- }
+ val1 = w83791d_read(client, W83791D_REG_WCHIPID);
+ val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
+ if (val1 != 0x71 || val2 != 0x5c)
+ return -ENODEV;
strlcpy(info->type, "w83791d", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 2be16194ddf..38978851333 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -1273,58 +1273,33 @@ w83792d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
return -ENODEV;
}
- /* The w83792d may be stuck in some other bank than bank 0. This may
- make reading other information impossible. Specify a force=... or
- force_*=... parameter, and the Winbond will be reset to the right
- bank. */
- if (kind < 0) {
- if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) {
- return -ENODEV;
- }
- val1 = w83792d_read_value(client, W83792D_REG_BANK);
- val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
- /* Check for Winbond ID if in bank 0 */
- if (!(val1 & 0x07)) { /* is Bank0 */
- if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
- ((val1 & 0x80) && (val2 != 0x5c))) {
- return -ENODEV;
- }
- }
- /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR
- should match */
- if (w83792d_read_value(client,
- W83792D_REG_I2C_ADDR) != address) {
+ if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80)
+ return -ENODEV;
+
+ val1 = w83792d_read_value(client, W83792D_REG_BANK);
+ val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
+ /* Check for Winbond ID if in bank 0 */
+ if (!(val1 & 0x07)) { /* is Bank0 */
+ if ((!(val1 & 0x80) && val2 != 0xa3) ||
+ ( (val1 & 0x80) && val2 != 0x5c))
return -ENODEV;
- }
}
+ /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR
+ should match */
+ if (w83792d_read_value(client, W83792D_REG_I2C_ADDR) != address)
+ return -ENODEV;
- /* We have either had a force parameter, or we have already detected the
- Winbond. Put it now into bank 0 and Vendor ID High Byte */
+ /* Put it now into bank 0 and Vendor ID High Byte */
w83792d_write_value(client,
W83792D_REG_BANK,
(w83792d_read_value(client,
W83792D_REG_BANK) & 0x78) | 0x80);
/* Determine the chip type. */
- if (kind <= 0) {
- /* get vendor ID */
- val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
- if (val2 != 0x5c) { /* the vendor is NOT Winbond */
- return -ENODEV;
- }
- val1 = w83792d_read_value(client, W83792D_REG_WCHIPID);
- if (val1 == 0x7a) {
- kind = w83792d;
- } else {
- if (kind == 0)
- dev_warn(&adapter->dev,
- "w83792d: Ignoring 'force' parameter for"
- " unknown chip at adapter %d, address"
- " 0x%02x\n", i2c_adapter_id(adapter),
- address);
- return -ENODEV;
- }
- }
+ val1 = w83792d_read_value(client, W83792D_REG_WCHIPID);
+ val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
+ if (val1 != 0x7a || val2 != 0x5c)
+ return -ENODEV;
strlcpy(info->type, "w83792d", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 47dd398f725..80a2191bf12 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -1164,7 +1164,7 @@ ERROR_SC_0:
static int w83793_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
- u8 tmp, bank;
+ u8 tmp, bank, chip_id;
struct i2c_adapter *adapter = client->adapter;
unsigned short address = client->addr;
@@ -1174,44 +1174,27 @@ static int w83793_detect(struct i2c_client *client, int kind,
bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL);
- if (kind < 0) {
- tmp = bank & 0x80 ? 0x5c : 0xa3;
- /* Check Winbond vendor ID */
- if (tmp != i2c_smbus_read_byte_data(client,
- W83793_REG_VENDORID)) {
- pr_debug("w83793: Detection failed at check "
- "vendor id\n");
- return -ENODEV;
- }
-
- /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR
- should match */
- if ((bank & 0x07) == 0
- && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) !=
- (address << 1)) {
- pr_debug("w83793: Detection failed at check "
- "i2c addr\n");
- return -ENODEV;
- }
-
+ tmp = bank & 0x80 ? 0x5c : 0xa3;
+ /* Check Winbond vendor ID */
+ if (tmp != i2c_smbus_read_byte_data(client, W83793_REG_VENDORID)) {
+ pr_debug("w83793: Detection failed at check vendor id\n");
+ return -ENODEV;
}
- /* We have either had a force parameter, or we have already detected the
- Winbond. Determine the chip type now */
-
- if (kind <= 0) {
- if (0x7b == i2c_smbus_read_byte_data(client,
- W83793_REG_CHIPID)) {
- kind = w83793;
- } else {
- if (kind == 0)
- dev_warn(&adapter->dev, "w83793: Ignoring "
- "'force' parameter for unknown chip "
- "at address 0x%02x\n", address);
- return -ENODEV;
- }
+ /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR
+ should match */
+ if ((bank & 0x07) == 0
+ && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) !=
+ (address << 1)) {
+ pr_debug("w83793: Detection failed at check i2c addr\n");
+ return -ENODEV;
}
+ /* Determine the chip type now */
+ chip_id = i2c_smbus_read_byte_data(client, W83793_REG_CHIPID);
+ if (chip_id != 0x7b)
+ return -ENODEV;
+
strlcpy(info->type, "w83793", I2C_NAME_SIZE);
return 0;
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
index ea295b9fc4f..9b6c4c10fba 100644
--- a/drivers/hwmon/w83l785ts.c
+++ b/drivers/hwmon/w83l785ts.c
@@ -1,7 +1,7 @@
/*
* w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org>
*
* Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made
* by Winbond. It reports a single external temperature with a 1 deg
@@ -146,60 +146,36 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1);
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83l785ts_detect(struct i2c_client *new_client, int kind,
+static int w83l785ts_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
- struct i2c_adapter *adapter = new_client->adapter;
+ struct i2c_adapter *adapter = client->adapter;
+ u16 man_id;
+ u8 chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /*
- * Now we do the remaining detection. A negative kind means that
- * the driver was loaded with no force parameter (default), so we
- * must both detect and identify the chip (actually there is only
- * one possible kind of chip for now, W83L785TS-S). A zero kind means
- * that the driver was loaded with the force parameter, the detection
- * step shall be skipped. A positive kind means that the driver
- * was loaded with the force parameter and a given kind of chip is
- * requested, so both the detection and the identification steps
- * are skipped.
- */
- if (kind < 0) { /* detection */
- if (((w83l785ts_read_value(new_client,
- W83L785TS_REG_CONFIG, 0) & 0x80) != 0x00)
- || ((w83l785ts_read_value(new_client,
- W83L785TS_REG_TYPE, 0) & 0xFC) != 0x00)) {
- dev_dbg(&adapter->dev,
- "W83L785TS-S detection failed at 0x%02x.\n",
- new_client->addr);
- return -ENODEV;
- }
+ /* detection */
+ if ((w83l785ts_read_value(client, W83L785TS_REG_CONFIG, 0) & 0x80)
+ || (w83l785ts_read_value(client, W83L785TS_REG_TYPE, 0) & 0xFC)) {
+ dev_dbg(&adapter->dev,
+ "W83L785TS-S detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
}
- if (kind <= 0) { /* identification */
- u16 man_id;
- u8 chip_id;
-
- man_id = (w83l785ts_read_value(new_client,
- W83L785TS_REG_MAN_ID1, 0) << 8) +
- w83l785ts_read_value(new_client,
- W83L785TS_REG_MAN_ID2, 0);
- chip_id = w83l785ts_read_value(new_client,
- W83L785TS_REG_CHIP_ID, 0);
-
- if (man_id == 0x5CA3) { /* Winbond */
- if (chip_id == 0x70) { /* W83L785TS-S */
- kind = w83l785ts;
- }
- }
-
- if (kind <= 0) { /* identification failed */
- dev_info(&adapter->dev,
- "Unsupported chip (man_id=0x%04X, "
- "chip_id=0x%02X).\n", man_id, chip_id);
- return -ENODEV;
- }
+ /* Identification */
+ man_id = (w83l785ts_read_value(client, W83L785TS_REG_MAN_ID1, 0) << 8)
+ + w83l785ts_read_value(client, W83L785TS_REG_MAN_ID2, 0);
+ chip_id = w83l785ts_read_value(client, W83L785TS_REG_CHIP_ID, 0);
+
+ if (man_id != 0x5CA3 /* Winbond */
+ || chip_id != 0x70) { /* W83L785TS-S */
+ dev_dbg(&adapter->dev,
+ "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
}
strlcpy(info->type, "w83l785ts", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c
index badca769f35..27da7d2b15f 100644
--- a/drivers/hwmon/w83l786ng.c
+++ b/drivers/hwmon/w83l786ng.c
@@ -590,53 +590,31 @@ w83l786ng_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ u16 man_id;
+ u8 chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
return -ENODEV;
}
- /*
- * Now we do the remaining detection. A negative kind means that
- * the driver was loaded with no force parameter (default), so we
- * must both detect and identify the chip (actually there is only
- * one possible kind of chip for now, W83L786NG). A zero kind means
- * that the driver was loaded with the force parameter, the detection
- * step shall be skipped. A positive kind means that the driver
- * was loaded with the force parameter and a given kind of chip is
- * requested, so both the detection and the identification steps
- * are skipped.
- */
- if (kind < 0) { /* detection */
- if (((w83l786ng_read_value(client,
- W83L786NG_REG_CONFIG) & 0x80) != 0x00)) {
- dev_dbg(&adapter->dev,
- "W83L786NG detection failed at 0x%02x.\n",
- client->addr);
- return -ENODEV;
- }
+ /* Detection */
+ if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) {
+ dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n",
+ client->addr);
+ return -ENODEV;
}
- if (kind <= 0) { /* identification */
- u16 man_id;
- u8 chip_id;
-
- man_id = (w83l786ng_read_value(client,
- W83L786NG_REG_MAN_ID1) << 8) +
- w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
- chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
-
- if (man_id == 0x5CA3) { /* Winbond */
- if (chip_id == 0x80) { /* W83L786NG */
- kind = w83l786ng;
- }
- }
+ /* Identification */
+ man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) +
+ w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
+ chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
- if (kind <= 0) { /* identification failed */
- dev_info(&adapter->dev,
- "Unsupported chip (man_id=0x%04X, "
- "chip_id=0x%02X).\n", man_id, chip_id);
- return -ENODEV;
- }
+ if (man_id != 0x5CA3 || /* Winbond */
+ chip_id != 0x80) { /* W83L786NG */
+ dev_dbg(&adapter->dev,
+ "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
+ man_id, chip_id);
+ return -ENODEV;
}
strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE);
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index d7ece131b4f..8d8a00e5a30 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -5,6 +5,7 @@
menuconfig I2C
tristate "I2C support"
depends on HAS_IOMEM
+ select RT_MUTEXES
---help---
I2C (pronounce: I-square-C) is a slow serial bus protocol used in
many micro controller applications and developed by Philips. SMBus,
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e8fe7f169e2..5f318ce2977 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -640,22 +640,6 @@ config I2C_TINY_USB
This driver can also be built as a module. If so, the module
will be called i2c-tiny-usb.
-comment "Graphics adapter I2C/DDC channel drivers"
- depends on PCI
-
-config I2C_VOODOO3
- tristate "Voodoo 3 (DEPRECATED)"
- depends on PCI
- select I2C_ALGOBIT
- help
- If you say yes to this option, support will be included for the
- Voodoo 3 I2C interface. This driver is deprecated and you should
- use the tdfxfb driver instead, which additionally provides
- framebuffer support.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-voodoo3.
-
comment "Other I2C/SMBus bus drivers"
config I2C_ACORN
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ff937ac69f5..302c551977b 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -61,9 +61,6 @@ obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
-# Graphics adapter I2C/DDC channel drivers
-obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
-
# Other I2C/SMBus bus drivers
obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index d108450df06..8de7d7b87bb 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -138,7 +138,7 @@ static unsigned short ali1535_smba;
Note the differences between kernels with the old PCI BIOS interface and
newer kernels with the real PCI interface. In compat.h some things are
defined to make the transition easier. */
-static int ali1535_setup(struct pci_dev *dev)
+static int __devinit ali1535_setup(struct pci_dev *dev)
{
int retval = -ENODEV;
unsigned char temp;
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index d627fceb790..e7e3205f128 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -131,7 +131,7 @@ MODULE_PARM_DESC(force_addr,
static struct pci_driver ali15x3_driver;
static unsigned short ali15x3_smba;
-static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
+static int __devinit ali15x3_setup(struct pci_dev *ALI15X3_dev)
{
u16 a;
unsigned char temp;
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
index b444762e9b9..9e18ef97f15 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware.c
@@ -1,5 +1,5 @@
/*
- * Synopsys Designware I2C adapter driver (master only).
+ * Synopsys DesignWare I2C adapter driver (master only).
*
* Based on the TI DAVINCI I2C adapter driver.
*
@@ -49,7 +49,20 @@
#define DW_IC_FS_SCL_LCNT 0x20
#define DW_IC_INTR_STAT 0x2c
#define DW_IC_INTR_MASK 0x30
+#define DW_IC_RAW_INTR_STAT 0x34
+#define DW_IC_RX_TL 0x38
+#define DW_IC_TX_TL 0x3c
#define DW_IC_CLR_INTR 0x40
+#define DW_IC_CLR_RX_UNDER 0x44
+#define DW_IC_CLR_RX_OVER 0x48
+#define DW_IC_CLR_TX_OVER 0x4c
+#define DW_IC_CLR_RD_REQ 0x50
+#define DW_IC_CLR_TX_ABRT 0x54
+#define DW_IC_CLR_RX_DONE 0x58
+#define DW_IC_CLR_ACTIVITY 0x5c
+#define DW_IC_CLR_STOP_DET 0x60
+#define DW_IC_CLR_START_DET 0x64
+#define DW_IC_CLR_GEN_CALL 0x68
#define DW_IC_ENABLE 0x6c
#define DW_IC_STATUS 0x70
#define DW_IC_TXFLR 0x74
@@ -64,9 +77,23 @@
#define DW_IC_CON_RESTART_EN 0x20
#define DW_IC_CON_SLAVE_DISABLE 0x40
-#define DW_IC_INTR_TX_EMPTY 0x10
-#define DW_IC_INTR_TX_ABRT 0x40
+#define DW_IC_INTR_RX_UNDER 0x001
+#define DW_IC_INTR_RX_OVER 0x002
+#define DW_IC_INTR_RX_FULL 0x004
+#define DW_IC_INTR_TX_OVER 0x008
+#define DW_IC_INTR_TX_EMPTY 0x010
+#define DW_IC_INTR_RD_REQ 0x020
+#define DW_IC_INTR_TX_ABRT 0x040
+#define DW_IC_INTR_RX_DONE 0x080
+#define DW_IC_INTR_ACTIVITY 0x100
#define DW_IC_INTR_STOP_DET 0x200
+#define DW_IC_INTR_START_DET 0x400
+#define DW_IC_INTR_GEN_CALL 0x800
+
+#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
+ DW_IC_INTR_TX_EMPTY | \
+ DW_IC_INTR_TX_ABRT | \
+ DW_IC_INTR_STOP_DET)
#define DW_IC_STATUS_ACTIVITY 0x1
@@ -96,31 +123,49 @@
#define ABRT_SBYTE_ACKDET 7
#define ABRT_SBYTE_NORSTRT 9
#define ABRT_10B_RD_NORSTRT 10
-#define ARB_MASTER_DIS 11
+#define ABRT_MASTER_DIS 11
#define ARB_LOST 12
+#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK)
+#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK)
+#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK)
+#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK)
+#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK)
+#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ)
+#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET)
+#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT)
+#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT)
+#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS)
+#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST)
+
+#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
+ DW_IC_TX_ABRT_10ADDR1_NOACK | \
+ DW_IC_TX_ABRT_10ADDR2_NOACK | \
+ DW_IC_TX_ABRT_TXDATA_NOACK | \
+ DW_IC_TX_ABRT_GCALL_NOACK)
+
static char *abort_sources[] = {
- [ABRT_7B_ADDR_NOACK] =
+ [ABRT_7B_ADDR_NOACK] =
"slave address not acknowledged (7bit mode)",
- [ABRT_10ADDR1_NOACK] =
+ [ABRT_10ADDR1_NOACK] =
"first address byte not acknowledged (10bit mode)",
- [ABRT_10ADDR2_NOACK] =
+ [ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)",
- [ABRT_TXDATA_NOACK] =
+ [ABRT_TXDATA_NOACK] =
"data not acknowledged",
- [ABRT_GCALL_NOACK] =
+ [ABRT_GCALL_NOACK] =
"no acknowledgement for a general call",
- [ABRT_GCALL_READ] =
+ [ABRT_GCALL_READ] =
"read after general call",
- [ABRT_SBYTE_ACKDET] =
+ [ABRT_SBYTE_ACKDET] =
"start byte acknowledged",
- [ABRT_SBYTE_NORSTRT] =
+ [ABRT_SBYTE_NORSTRT] =
"trying to send start byte when restart is disabled",
- [ABRT_10B_RD_NORSTRT] =
+ [ABRT_10B_RD_NORSTRT] =
"trying to read when restart is disabled (10bit mode)",
- [ARB_MASTER_DIS] =
+ [ABRT_MASTER_DIS] =
"trying to use disabled adapter",
- [ARB_LOST] =
+ [ARB_LOST] =
"lost arbitration",
};
@@ -129,7 +174,6 @@ static char *abort_sources[] = {
* @dev: driver model device node
* @base: IO registers pointer
* @cmd_complete: tx completion indicator
- * @pump_msg: continue in progress transfers
* @lock: protect this struct and IO registers
* @clk: input reference clock
* @cmd_err: run time hadware error code
@@ -155,27 +199,81 @@ struct dw_i2c_dev {
struct device *dev;
void __iomem *base;
struct completion cmd_complete;
- struct tasklet_struct pump_msg;
struct mutex lock;
struct clk *clk;
int cmd_err;
struct i2c_msg *msgs;
int msgs_num;
int msg_write_idx;
- u16 tx_buf_len;
+ u32 tx_buf_len;
u8 *tx_buf;
int msg_read_idx;
- u16 rx_buf_len;
+ u32 rx_buf_len;
u8 *rx_buf;
int msg_err;
unsigned int status;
- u16 abort_source;
+ u32 abort_source;
int irq;
struct i2c_adapter adapter;
unsigned int tx_fifo_depth;
unsigned int rx_fifo_depth;
};
+static u32
+i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
+{
+ /*
+ * DesignWare I2C core doesn't seem to have solid strategy to meet
+ * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
+ * will result in violation of the tHD;STA spec.
+ */
+ if (cond)
+ /*
+ * Conditional expression:
+ *
+ * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
+ *
+ * This is based on the DW manuals, and represents an ideal
+ * configuration. The resulting I2C bus speed will be
+ * faster than any of the others.
+ *
+ * If your hardware is free from tHD;STA issue, try this one.
+ */
+ return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset;
+ else
+ /*
+ * Conditional expression:
+ *
+ * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
+ *
+ * This is just experimental rule; the tHD;STA period turned
+ * out to be proportinal to (_HCNT + 3). With this setting,
+ * we could meet both tHIGH and tHD;STA timing specs.
+ *
+ * If unsure, you'd better to take this alternative.
+ *
+ * The reason why we need to take into account "tf" here,
+ * is the same as described in i2c_dw_scl_lcnt().
+ */
+ return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset;
+}
+
+static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
+{
+ /*
+ * Conditional expression:
+ *
+ * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf)
+ *
+ * DW I2C core starts counting the SCL CNTs for the LOW period
+ * of the SCL clock (tLOW) as soon as it pulls the SCL line.
+ * In order to meet the tLOW timing spec, we need to take into
+ * account the fall time of SCL signal (tf). Default tf value
+ * should be 0.3 us, for safety.
+ */
+ return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset;
+}
+
/**
* i2c_dw_init() - initialize the designware i2c master hardware
* @dev: device private data
@@ -187,25 +285,49 @@ struct dw_i2c_dev {
static void i2c_dw_init(struct dw_i2c_dev *dev)
{
u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
- u16 ic_con;
+ u32 ic_con, hcnt, lcnt;
/* Disable the adapter */
- writeb(0, dev->base + DW_IC_ENABLE);
+ writel(0, dev->base + DW_IC_ENABLE);
/* set standard and fast speed deviders for high/low periods */
- writew((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */
- dev->base + DW_IC_SS_SCL_HCNT);
- writew((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */
- dev->base + DW_IC_SS_SCL_LCNT);
- writew((input_clock_khz * 6 / 10000)+1, /* fast speed high, 0.6us */
- dev->base + DW_IC_FS_SCL_HCNT);
- writew((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */
- dev->base + DW_IC_FS_SCL_LCNT);
+
+ /* Standard-mode */
+ hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+ 40, /* tHD;STA = tHIGH = 4.0 us */
+ 3, /* tf = 0.3 us */
+ 0, /* 0: DW default, 1: Ideal */
+ 0); /* No offset */
+ lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+ 47, /* tLOW = 4.7 us */
+ 3, /* tf = 0.3 us */
+ 0); /* No offset */
+ writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT);
+ writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT);
+ dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+
+ /* Fast-mode */
+ hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+ 6, /* tHD;STA = tHIGH = 0.6 us */
+ 3, /* tf = 0.3 us */
+ 0, /* 0: DW default, 1: Ideal */
+ 0); /* No offset */
+ lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+ 13, /* tLOW = 1.3 us */
+ 3, /* tf = 0.3 us */
+ 0); /* No offset */
+ writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT);
+ writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT);
+ dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+
+ /* Configure Tx/Rx FIFO threshold levels */
+ writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL);
+ writel(0, dev->base + DW_IC_RX_TL);
/* configure the i2c master */
ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
- writew(ic_con, dev->base + DW_IC_CON);
+ writel(ic_con, dev->base + DW_IC_CON);
}
/*
@@ -215,7 +337,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
{
int timeout = TIMEOUT;
- while (readb(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+ while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT;
@@ -227,106 +349,125 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
return 0;
}
+static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
+{
+ struct i2c_msg *msgs = dev->msgs;
+ u32 ic_con;
+
+ /* Disable the adapter */
+ writel(0, dev->base + DW_IC_ENABLE);
+
+ /* set the slave (target) address */
+ writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+
+ /* if the slave address is ten bit address, enable 10BITADDR */
+ ic_con = readl(dev->base + DW_IC_CON);
+ if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
+ ic_con |= DW_IC_CON_10BITADDR_MASTER;
+ else
+ ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
+ writel(ic_con, dev->base + DW_IC_CON);
+
+ /* Enable the adapter */
+ writel(1, dev->base + DW_IC_ENABLE);
+
+ /* Enable interrupts */
+ writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK);
+}
+
/*
- * Initiate low level master read/write transaction.
- * This function is called from i2c_dw_xfer when starting a transfer.
- * This function is also called from dw_i2c_pump_msg to continue a transfer
- * that is longer than the size of the TX FIFO.
+ * Initiate (and continue) low level master read/write transaction.
+ * This function is only called from i2c_dw_isr, and pumping i2c_msg
+ * messages into the tx buffer. Even if the size of i2c_msg data is
+ * longer than the size of the tx buffer, it handles everything.
*/
static void
-i2c_dw_xfer_msg(struct i2c_adapter *adap)
+i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
struct i2c_msg *msgs = dev->msgs;
- int num = dev->msgs_num;
- u16 ic_con, intr_mask;
- int tx_limit = dev->tx_fifo_depth - readb(dev->base + DW_IC_TXFLR);
- int rx_limit = dev->rx_fifo_depth - readb(dev->base + DW_IC_RXFLR);
- u16 addr = msgs[dev->msg_write_idx].addr;
- u16 buf_len = dev->tx_buf_len;
-
- if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
- /* Disable the adapter */
- writeb(0, dev->base + DW_IC_ENABLE);
-
- /* set the slave (target) address */
- writew(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+ u32 intr_mask;
+ int tx_limit, rx_limit;
+ u32 addr = msgs[dev->msg_write_idx].addr;
+ u32 buf_len = dev->tx_buf_len;
+ u8 *buf = dev->tx_buf;;
- /* if the slave address is ten bit address, enable 10BITADDR */
- ic_con = readw(dev->base + DW_IC_CON);
- if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
- ic_con |= DW_IC_CON_10BITADDR_MASTER;
- else
- ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
- writew(ic_con, dev->base + DW_IC_CON);
+ intr_mask = DW_IC_INTR_DEFAULT_MASK;
- /* Enable the adapter */
- writeb(1, dev->base + DW_IC_ENABLE);
- }
-
- for (; dev->msg_write_idx < num; dev->msg_write_idx++) {
- /* if target address has changed, we need to
+ for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
+ /*
+ * if target address has changed, we need to
* reprogram the target address in the i2c
* adapter when we are done with this transfer
*/
- if (msgs[dev->msg_write_idx].addr != addr)
- return;
+ if (msgs[dev->msg_write_idx].addr != addr) {
+ dev_err(dev->dev,
+ "%s: invalid target address\n", __func__);
+ dev->msg_err = -EINVAL;
+ break;
+ }
if (msgs[dev->msg_write_idx].len == 0) {
dev_err(dev->dev,
"%s: invalid message length\n", __func__);
dev->msg_err = -EINVAL;
- return;
+ break;
}
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
/* new i2c_msg */
- dev->tx_buf = msgs[dev->msg_write_idx].buf;
+ buf = msgs[dev->msg_write_idx].buf;
buf_len = msgs[dev->msg_write_idx].len;
}
+ tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR);
+ rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR);
+
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
- writew(0x100, dev->base + DW_IC_DATA_CMD);
+ writel(0x100, dev->base + DW_IC_DATA_CMD);
rx_limit--;
} else
- writew(*(dev->tx_buf++),
- dev->base + DW_IC_DATA_CMD);
+ writel(*buf++, dev->base + DW_IC_DATA_CMD);
tx_limit--; buf_len--;
}
+
+ dev->tx_buf = buf;
+ dev->tx_buf_len = buf_len;
+
+ if (buf_len > 0) {
+ /* more bytes to be written */
+ dev->status |= STATUS_WRITE_IN_PROGRESS;
+ break;
+ } else
+ dev->status &= ~STATUS_WRITE_IN_PROGRESS;
}
- intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
- if (buf_len > 0) { /* more bytes to be written */
- intr_mask |= DW_IC_INTR_TX_EMPTY;
- dev->status |= STATUS_WRITE_IN_PROGRESS;
- } else
- dev->status &= ~STATUS_WRITE_IN_PROGRESS;
- writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+ /*
+ * If i2c_msg index search is completed, we don't need TX_EMPTY
+ * interrupt any more.
+ */
+ if (dev->msg_write_idx == dev->msgs_num)
+ intr_mask &= ~DW_IC_INTR_TX_EMPTY;
+
+ if (dev->msg_err)
+ intr_mask = 0;
- dev->tx_buf_len = buf_len;
+ writel(intr_mask, dev->base + DW_IC_INTR_MASK);
}
static void
-i2c_dw_read(struct i2c_adapter *adap)
+i2c_dw_read(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
struct i2c_msg *msgs = dev->msgs;
- int num = dev->msgs_num;
- u16 addr = msgs[dev->msg_read_idx].addr;
- int rx_valid = readw(dev->base + DW_IC_RXFLR);
+ int rx_valid;
- for (; dev->msg_read_idx < num; dev->msg_read_idx++) {
- u16 len;
+ for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
+ u32 len;
u8 *buf;
if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
continue;
- /* different i2c client, reprogram the i2c adapter */
- if (msgs[dev->msg_read_idx].addr != addr)
- return;
-
if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
len = msgs[dev->msg_read_idx].len;
buf = msgs[dev->msg_read_idx].buf;
@@ -335,8 +476,10 @@ i2c_dw_read(struct i2c_adapter *adap)
buf = dev->rx_buf;
}
+ rx_valid = readl(dev->base + DW_IC_RXFLR);
+
for (; len > 0 && rx_valid > 0; len--, rx_valid--)
- *buf++ = readb(dev->base + DW_IC_DATA_CMD);
+ *buf++ = readl(dev->base + DW_IC_DATA_CMD);
if (len > 0) {
dev->status |= STATUS_READ_IN_PROGRESS;
@@ -348,6 +491,29 @@ i2c_dw_read(struct i2c_adapter *adap)
}
}
+static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
+{
+ unsigned long abort_source = dev->abort_source;
+ int i;
+
+ if (abort_source & DW_IC_TX_ABRT_NOACK) {
+ for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+ dev_dbg(dev->dev,
+ "%s: %s\n", __func__, abort_sources[i]);
+ return -EREMOTEIO;
+ }
+
+ for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+ dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
+
+ if (abort_source & DW_IC_TX_ARB_LOST)
+ return -EAGAIN;
+ else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
+ return -EINVAL; /* wrong msgs[] data */
+ else
+ return -EIO;
+}
+
/*
* Prepare controller for a transaction and call i2c_dw_xfer_msg
*/
@@ -369,13 +535,14 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
dev->msg_read_idx = 0;
dev->msg_err = 0;
dev->status = STATUS_IDLE;
+ dev->abort_source = 0;
ret = i2c_dw_wait_bus_not_busy(dev);
if (ret < 0)
goto done;
/* start the transfers */
- i2c_dw_xfer_msg(adap);
+ i2c_dw_xfer_init(dev);
/* wait for tx to complete */
ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
@@ -394,23 +561,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
/* no error */
if (likely(!dev->cmd_err)) {
- /* read rx fifo, and disable the adapter */
- do {
- i2c_dw_read(adap);
- } while (dev->status & STATUS_READ_IN_PROGRESS);
- writeb(0, dev->base + DW_IC_ENABLE);
+ /* Disable the adapter */
+ writel(0, dev->base + DW_IC_ENABLE);
ret = num;
goto done;
}
/* We have an error */
if (dev->cmd_err == DW_IC_ERR_TX_ABRT) {
- unsigned long abort_source = dev->abort_source;
- int i;
-
- for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) {
- dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
- }
+ ret = i2c_dw_handle_tx_abort(dev);
+ goto done;
}
ret = -EIO;
@@ -422,21 +582,67 @@ done:
static u32 i2c_dw_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
+ return I2C_FUNC_I2C |
+ I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
}
-static void dw_i2c_pump_msg(unsigned long data)
+static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
{
- struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data;
- u16 intr_mask;
-
- i2c_dw_read(&dev->adapter);
- i2c_dw_xfer_msg(&dev->adapter);
-
- intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
- if (dev->status & STATUS_WRITE_IN_PROGRESS)
- intr_mask |= DW_IC_INTR_TX_EMPTY;
- writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+ u32 stat;
+
+ /*
+ * The IC_INTR_STAT register just indicates "enabled" interrupts.
+ * Ths unmasked raw version of interrupt status bits are available
+ * in the IC_RAW_INTR_STAT register.
+ *
+ * That is,
+ * stat = readl(IC_INTR_STAT);
+ * equals to,
+ * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
+ *
+ * The raw version might be useful for debugging purposes.
+ */
+ stat = readl(dev->base + DW_IC_INTR_STAT);
+
+ /*
+ * Do not use the IC_CLR_INTR register to clear interrupts, or
+ * you'll miss some interrupts, triggered during the period from
+ * readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
+ *
+ * Instead, use the separately-prepared IC_CLR_* registers.
+ */
+ if (stat & DW_IC_INTR_RX_UNDER)
+ readl(dev->base + DW_IC_CLR_RX_UNDER);
+ if (stat & DW_IC_INTR_RX_OVER)
+ readl(dev->base + DW_IC_CLR_RX_OVER);
+ if (stat & DW_IC_INTR_TX_OVER)
+ readl(dev->base + DW_IC_CLR_TX_OVER);
+ if (stat & DW_IC_INTR_RD_REQ)
+ readl(dev->base + DW_IC_CLR_RD_REQ);
+ if (stat & DW_IC_INTR_TX_ABRT) {
+ /*
+ * The IC_TX_ABRT_SOURCE register is cleared whenever
+ * the IC_CLR_TX_ABRT is read. Preserve it beforehand.
+ */
+ dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE);
+ readl(dev->base + DW_IC_CLR_TX_ABRT);
+ }
+ if (stat & DW_IC_INTR_RX_DONE)
+ readl(dev->base + DW_IC_CLR_RX_DONE);
+ if (stat & DW_IC_INTR_ACTIVITY)
+ readl(dev->base + DW_IC_CLR_ACTIVITY);
+ if (stat & DW_IC_INTR_STOP_DET)
+ readl(dev->base + DW_IC_CLR_STOP_DET);
+ if (stat & DW_IC_INTR_START_DET)
+ readl(dev->base + DW_IC_CLR_START_DET);
+ if (stat & DW_IC_INTR_GEN_CALL)
+ readl(dev->base + DW_IC_CLR_GEN_CALL);
+
+ return stat;
}
/*
@@ -446,20 +652,37 @@ static void dw_i2c_pump_msg(unsigned long data)
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
- u16 stat;
+ u32 stat;
- stat = readw(dev->base + DW_IC_INTR_STAT);
+ stat = i2c_dw_read_clear_intrbits(dev);
dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
+
if (stat & DW_IC_INTR_TX_ABRT) {
- dev->abort_source = readw(dev->base + DW_IC_TX_ABRT_SOURCE);
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
dev->status = STATUS_IDLE;
- } else if (stat & DW_IC_INTR_TX_EMPTY)
- tasklet_schedule(&dev->pump_msg);
- readb(dev->base + DW_IC_CLR_INTR); /* clear interrupts */
- writew(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
- if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
+ /*
+ * Anytime TX_ABRT is set, the contents of the tx/rx
+ * buffers are flushed. Make sure to skip them.
+ */
+ writel(0, dev->base + DW_IC_INTR_MASK);
+ goto tx_aborted;
+ }
+
+ if (stat & DW_IC_INTR_RX_FULL)
+ i2c_dw_read(dev);
+
+ if (stat & DW_IC_INTR_TX_EMPTY)
+ i2c_dw_xfer_msg(dev);
+
+ /*
+ * No need to modify or disable the interrupt mask here.
+ * i2c_dw_xfer_msg() will take care of it according to
+ * the current transmit status.
+ */
+
+tx_aborted:
+ if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
complete(&dev->cmd_complete);
return IRQ_HANDLED;
@@ -474,8 +697,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
{
struct dw_i2c_dev *dev;
struct i2c_adapter *adap;
- struct resource *mem, *irq, *ioarea;
- int r;
+ struct resource *mem, *ioarea;
+ int irq, r;
/* NOTE: driver uses the static register mapping */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -484,10 +707,10 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
return -EINVAL;
}
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
- return -EINVAL;
+ return irq; /* -ENXIO */
}
ioarea = request_mem_region(mem->start, resource_size(mem),
@@ -504,10 +727,9 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
}
init_completion(&dev->cmd_complete);
- tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev);
mutex_init(&dev->lock);
dev->dev = get_device(&pdev->dev);
- dev->irq = irq->start;
+ dev->irq = irq;
platform_set_drvdata(pdev, dev);
dev->clk = clk_get(&pdev->dev, NULL);
@@ -531,8 +753,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
}
i2c_dw_init(dev);
- writew(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
- r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, dev);
+ writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
+ r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
if (r) {
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
goto err_iounmap;
@@ -587,7 +809,7 @@ static int __devexit dw_i2c_remove(struct platform_device *pdev)
clk_put(dev->clk);
dev->clk = NULL;
- writeb(0, dev->base + DW_IC_ENABLE);
+ writel(0, dev->base + DW_IC_ENABLE);
free_irq(dev->irq, dev);
kfree(dev);
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 55edcfe5b85..df6ab553f97 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -767,6 +767,9 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
/* set up the sysfs linkage to our parent device */
i801_adapter.dev.parent = &dev->dev;
+ /* Retry up to 3 times on lost arbitration */
+ i801_adapter.retries = 3;
+
snprintf(i801_adapter.name, sizeof(i801_adapter.name),
"SMBus I801 adapter at %04lx", i801_smba);
err = i2c_add_adapter(&i801_adapter);
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index a75c75e77b9..5901707fc66 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -56,12 +56,6 @@ iic_cook_addr(struct i2c_msg *msg)
if (msg->flags & I2C_M_RD)
addr |= 1;
- /*
- * Read or Write?
- */
- if (msg->flags & I2C_M_REV_DIR_ADDR)
- addr ^= 1;
-
return addr;
}
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index bbab0e16663..ed387ffa473 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -338,9 +338,6 @@ mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
if (msg->flags & I2C_M_RD)
dir = 1;
- if (msg->flags & I2C_M_REV_DIR_ADDR)
- dir ^= 1;
-
if (msg->flags & I2C_M_TEN) {
drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
drv_data->addr2 = (u32)msg->addr & 0xff;
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 827da085813..75bf3ad1809 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -178,6 +178,12 @@ struct omap_i2c_dev {
unsigned b_hw:1; /* bad h/w fixes */
unsigned idle:1;
u16 iestate; /* Saved interrupt register */
+ u16 pscstate;
+ u16 scllstate;
+ u16 sclhstate;
+ u16 bufstate;
+ u16 syscstate;
+ u16 westate;
};
static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
@@ -230,9 +236,18 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
clk_enable(dev->iclk);
clk_enable(dev->fclk);
+ if (cpu_is_omap34xx()) {
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+ omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate);
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate);
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate);
+ omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, dev->bufstate);
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, dev->syscstate);
+ omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+ }
dev->idle = 0;
- if (dev->iestate)
- omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
+ omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
}
static void omap_i2c_idle(struct omap_i2c_dev *dev)
@@ -258,7 +273,7 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev)
static int omap_i2c_init(struct omap_i2c_dev *dev)
{
- u16 psc = 0, scll = 0, sclh = 0;
+ u16 psc = 0, scll = 0, sclh = 0, buf = 0;
u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0;
unsigned long fclk_rate = 12000000;
unsigned long timeout;
@@ -287,24 +302,22 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
SYSC_AUTOIDLE_MASK);
} else if (dev->rev >= OMAP_I2C_REV_ON_3430) {
- u32 v;
-
- v = SYSC_AUTOIDLE_MASK;
- v |= SYSC_ENAWAKEUP_MASK;
- v |= (SYSC_IDLEMODE_SMART <<
+ dev->syscstate = SYSC_AUTOIDLE_MASK;
+ dev->syscstate |= SYSC_ENAWAKEUP_MASK;
+ dev->syscstate |= (SYSC_IDLEMODE_SMART <<
__ffs(SYSC_SIDLEMODE_MASK));
- v |= (SYSC_CLOCKACTIVITY_FCLK <<
+ dev->syscstate |= (SYSC_CLOCKACTIVITY_FCLK <<
__ffs(SYSC_CLOCKACTIVITY_MASK));
- omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, v);
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG,
+ dev->syscstate);
/*
* Enabling all wakup sources to stop I2C freezing on
* WFI instruction.
* REVISIT: Some wkup sources might not be needed.
*/
- omap_i2c_write_reg(dev, OMAP_I2C_WE_REG,
- OMAP_I2C_WE_ALL);
-
+ dev->westate = OMAP_I2C_WE_ALL;
+ omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
}
}
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
@@ -394,23 +407,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll);
omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh);
- if (dev->fifo_size)
- /* Note: setup required fifo size - 1 */
- omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
- (dev->fifo_size - 1) << 8 | /* RTRSH */
- OMAP_I2C_BUF_RXFIF_CLR |
- (dev->fifo_size - 1) | /* XTRSH */
- OMAP_I2C_BUF_TXFIF_CLR);
+ if (dev->fifo_size) {
+ /* Note: setup required fifo size - 1. RTRSH and XTRSH */
+ buf = (dev->fifo_size - 1) << 8 | OMAP_I2C_BUF_RXFIF_CLR |
+ (dev->fifo_size - 1) | OMAP_I2C_BUF_TXFIF_CLR;
+ omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf);
+ }
/* Take the I2C module out of reset: */
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
/* Enable interrupts */
- omap_i2c_write_reg(dev, OMAP_I2C_IE_REG,
- (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
+ dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
OMAP_I2C_IE_AL) | ((dev->fifo_size) ?
- (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0));
+ (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0);
+ omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
+ if (cpu_is_omap34xx()) {
+ dev->pscstate = psc;
+ dev->scllstate = scll;
+ dev->sclhstate = sclh;
+ dev->bufstate = buf;
+ }
return 0;
}
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index fbab6846ae6..5d1c2603a13 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -638,7 +638,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
/* Register this adapter with the I2C subsystem */
i2c_pnx->adapter->dev.parent = &pdev->dev;
- ret = i2c_add_adapter(i2c_pnx->adapter);
+ i2c_pnx->adapter->nr = pdev->id;
+ ret = i2c_add_numbered_adapter(i2c_pnx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "I2C: Failed to add bus\n");
goto out_irq;
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c
index 3c9d71f6018..1c440a70ec6 100644
--- a/drivers/i2c/busses/i2c-powermac.c
+++ b/drivers/i2c/busses/i2c-powermac.c
@@ -49,48 +49,38 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
int rc = 0;
int read = (read_write == I2C_SMBUS_READ);
int addrdir = (addr << 1) | read;
+ int mode, subsize, len;
+ u32 subaddr;
+ u8 *buf;
u8 local[2];
- rc = pmac_i2c_open(bus, 0);
- if (rc)
- return rc;
+ if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) {
+ mode = pmac_i2c_mode_std;
+ subsize = 0;
+ subaddr = 0;
+ } else {
+ mode = read ? pmac_i2c_mode_combined : pmac_i2c_mode_stdsub;
+ subsize = 1;
+ subaddr = command;
+ }
switch (size) {
case I2C_SMBUS_QUICK:
- rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 0, 0, NULL, 0);
+ buf = NULL;
+ len = 0;
break;
case I2C_SMBUS_BYTE:
- rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 0, 0, &data->byte, 1);
- break;
case I2C_SMBUS_BYTE_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 1, command, &data->byte, 1);
+ buf = &data->byte;
+ len = 1;
break;
case I2C_SMBUS_WORD_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
if (!read) {
local[0] = data->word & 0xff;
local[1] = (data->word >> 8) & 0xff;
}
- rc = pmac_i2c_xfer(bus, addrdir, 1, command, local, 2);
- if (rc == 0 && read) {
- data->word = ((u16)local[1]) << 8;
- data->word |= local[0];
- }
+ buf = local;
+ len = 2;
break;
/* Note that these are broken vs. the expected smbus API where
@@ -105,28 +95,44 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
* a repeat start/addr phase (but not stop in between)
*/
case I2C_SMBUS_BLOCK_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 1, command, data->block,
- data->block[0] + 1);
-
+ buf = data->block;
+ len = data->block[0] + 1;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
- rc = pmac_i2c_setmode(bus, read ?
- pmac_i2c_mode_combined :
- pmac_i2c_mode_stdsub);
- if (rc)
- goto bail;
- rc = pmac_i2c_xfer(bus, addrdir, 1, command,
- &data->block[1], data->block[0]);
+ buf = &data->block[1];
+ len = data->block[0];
break;
default:
- rc = -EINVAL;
+ return -EINVAL;
+ }
+
+ rc = pmac_i2c_open(bus, 0);
+ if (rc) {
+ dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc);
+ return rc;
+ }
+
+ rc = pmac_i2c_setmode(bus, mode);
+ if (rc) {
+ dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n",
+ mode, rc);
+ goto bail;
}
+
+ rc = pmac_i2c_xfer(bus, addrdir, subsize, subaddr, buf, len);
+ if (rc) {
+ dev_err(&adap->dev,
+ "I2C transfer at 0x%02x failed, size %d, err %d\n",
+ addrdir >> 1, size, rc);
+ goto bail;
+ }
+
+ if (size == I2C_SMBUS_WORD_DATA && read) {
+ data->word = ((u16)local[1]) << 8;
+ data->word |= local[0];
+ }
+
bail:
pmac_i2c_close(bus);
return rc;
@@ -146,20 +152,33 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap,
int read;
int addrdir;
+ if (num != 1) {
+ dev_err(&adap->dev,
+ "Multi-message I2C transactions not supported\n");
+ return -EOPNOTSUPP;
+ }
+
if (msgs->flags & I2C_M_TEN)
return -EINVAL;
read = (msgs->flags & I2C_M_RD) != 0;
addrdir = (msgs->addr << 1) | read;
- if (msgs->flags & I2C_M_REV_DIR_ADDR)
- addrdir ^= 1;
rc = pmac_i2c_open(bus, 0);
- if (rc)
+ if (rc) {
+ dev_err(&adap->dev, "Failed to open I2C, err %d\n", rc);
return rc;
+ }
rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std);
- if (rc)
+ if (rc) {
+ dev_err(&adap->dev, "Failed to set I2C mode %d, err %d\n",
+ pmac_i2c_mode_std, rc);
goto bail;
+ }
rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len);
+ if (rc < 0)
+ dev_err(&adap->dev, "I2C %s 0x%02x failed, err %d\n",
+ addrdir & 1 ? "read from" : "write to", addrdir >> 1,
+ rc);
bail:
pmac_i2c_close(bus);
return rc < 0 ? rc : 1;
@@ -183,19 +202,16 @@ static const struct i2c_algorithm i2c_powermac_algorithm = {
static int __devexit i2c_powermac_remove(struct platform_device *dev)
{
struct i2c_adapter *adapter = platform_get_drvdata(dev);
- struct pmac_i2c_bus *bus = i2c_get_adapdata(adapter);
int rc;
rc = i2c_del_adapter(adapter);
- pmac_i2c_detach_adapter(bus, adapter);
- i2c_set_adapdata(adapter, NULL);
/* We aren't that prepared to deal with this... */
if (rc)
printk(KERN_WARNING
"i2c-powermac.c: Failed to remove bus %s !\n",
adapter->name);
platform_set_drvdata(dev, NULL);
- kfree(adapter);
+ memset(adapter, 0, sizeof(*adapter));
return 0;
}
@@ -206,12 +222,12 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev)
struct pmac_i2c_bus *bus = dev->dev.platform_data;
struct device_node *parent = NULL;
struct i2c_adapter *adapter;
- char name[32];
const char *basename;
int rc;
if (bus == NULL)
return -EINVAL;
+ adapter = pmac_i2c_get_adapter(bus);
/* Ok, now we need to make up a name for the interface that will
* match what we used to do in the past, that is basically the
@@ -237,29 +253,22 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev)
default:
return -EINVAL;
}
- snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus));
+ snprintf(adapter->name, sizeof(adapter->name), "%s %d", basename,
+ pmac_i2c_get_channel(bus));
of_node_put(parent);
- adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
- if (adapter == NULL) {
- printk(KERN_ERR "i2c-powermac: can't allocate inteface !\n");
- return -ENOMEM;
- }
platform_set_drvdata(dev, adapter);
- strcpy(adapter->name, name);
adapter->algo = &i2c_powermac_algorithm;
i2c_set_adapdata(adapter, bus);
adapter->dev.parent = &dev->dev;
- pmac_i2c_attach_adapter(bus, adapter);
rc = i2c_add_adapter(adapter);
if (rc) {
printk(KERN_ERR "i2c-powermac: Adapter %s registration "
- "failed\n", name);
- i2c_set_adapdata(adapter, NULL);
- pmac_i2c_detach_adapter(bus, adapter);
+ "failed\n", adapter->name);
+ memset(adapter, 0, sizeof(*adapter));
}
- printk(KERN_INFO "PowerMac i2c bus %s registered\n", name);
+ printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name);
if (!strncmp(basename, "uni-n", 5)) {
struct device_node *np;
diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c
index b4a55d407bf..365e0becaf1 100644
--- a/drivers/i2c/busses/i2c-scmi.c
+++ b/drivers/i2c/busses/i2c-scmi.c
@@ -363,7 +363,7 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
smbus_cmi->cap_write = 0;
acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
- acpi_smbus_cmi_query_methods, smbus_cmi, NULL);
+ acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
if (smbus_cmi->cap_info == 0)
goto err;
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index 139f0c7f12a..844569f7d8b 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -142,7 +142,7 @@ static void sis5595_write(u8 reg, u8 data)
outb(data, sis5595_base + SMB_DAT);
}
-static int sis5595_setup(struct pci_dev *SIS5595_dev)
+static int __devinit sis5595_setup(struct pci_dev *SIS5595_dev)
{
u16 a;
u8 val;
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index 70ca41e90e5..68cff7af701 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -389,7 +389,7 @@ static u32 sis630_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static int sis630_setup(struct pci_dev *sis630_dev)
+static int __devinit sis630_setup(struct pci_dev *sis630_dev)
{
unsigned char b;
struct pci_dev *dummy = NULL;
diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c
index 1b7b2af9403..0c770eabe85 100644
--- a/drivers/i2c/busses/i2c-stub.c
+++ b/drivers/i2c/busses/i2c-stub.c
@@ -35,6 +35,10 @@ module_param_array(chip_addr, ushort, NULL, S_IRUGO);
MODULE_PARM_DESC(chip_addr,
"Chip addresses (up to 10, between 0x03 and 0x77)");
+static unsigned long functionality = ~0UL;
+module_param(functionality, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(functionality, "Override functionality bitfield");
+
struct stub_chip {
u8 pointer;
u16 words[256]; /* Byte operations use the LSB as per SMBus
@@ -48,7 +52,7 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size, union i2c_smbus_data * data)
{
s32 ret;
- int i;
+ int i, len;
struct stub_chip *chip = NULL;
/* Search for the right chip */
@@ -118,6 +122,29 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
ret = 0;
break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ len = data->block[0];
+ if (read_write == I2C_SMBUS_WRITE) {
+ for (i = 0; i < len; i++) {
+ chip->words[command + i] &= 0xff00;
+ chip->words[command + i] |= data->block[1 + i];
+ }
+ dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, "
+ "wrote %d bytes at 0x%02x.\n",
+ addr, len, command);
+ } else {
+ for (i = 0; i < len; i++) {
+ data->block[1 + i] =
+ chip->words[command + i] & 0xff;
+ }
+ dev_dbg(&adap->dev, "i2c block data - addr 0x%02x, "
+ "read %d bytes at 0x%02x.\n",
+ addr, len, command);
+ }
+
+ ret = 0;
+ break;
+
default:
dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
ret = -EOPNOTSUPP;
@@ -129,8 +156,9 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
static u32 stub_func(struct i2c_adapter *adapter)
{
- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
+ return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK) & functionality;
}
static const struct i2c_algorithm smbus_algorithm = {
diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c
deleted file mode 100644
index 7663d57833a..00000000000
--- a/drivers/i2c/busses/i2c-voodoo3.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
- Philip Edelbrock <phil@netroedge.com>,
- Ralph Metzler <rjkm@thp.uni-koeln.de>, and
- Mark D. Studebaker <mdsxyz123@yahoo.com>
-
- Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
- Simon Vogl
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-/* This interfaces to the I2C bus of the Voodoo3 to gain access to
- the BT869 and possibly other I2C devices. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <asm/io.h>
-
-/* the only registers we use */
-#define REG 0x78
-#define REG2 0x70
-
-/* bit locations in the register */
-#define DDC_ENAB 0x00040000
-#define DDC_SCL_OUT 0x00080000
-#define DDC_SDA_OUT 0x00100000
-#define DDC_SCL_IN 0x00200000
-#define DDC_SDA_IN 0x00400000
-#define I2C_ENAB 0x00800000
-#define I2C_SCL_OUT 0x01000000
-#define I2C_SDA_OUT 0x02000000
-#define I2C_SCL_IN 0x04000000
-#define I2C_SDA_IN 0x08000000
-
-/* initialization states */
-#define INIT2 0x2
-#define INIT3 0x4
-
-/* delays */
-#define CYCLE_DELAY 10
-#define TIMEOUT (HZ / 2)
-
-
-static void __iomem *ioaddr;
-
-/* The voo GPIO registers don't have individual masks for each bit
- so we always have to read before writing. */
-
-static void bit_vooi2c_setscl(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= I2C_SCL_OUT;
- else
- r &= ~I2C_SCL_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-static void bit_vooi2c_setsda(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= I2C_SDA_OUT;
- else
- r &= ~I2C_SDA_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-/* The GPIO pins are open drain, so the pins always remain outputs.
- We rely on the i2c-algo-bit routines to set the pins high before
- reading the input from other chips. */
-
-static int bit_vooi2c_getscl(void *data)
-{
- return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
-}
-
-static int bit_vooi2c_getsda(void *data)
-{
- return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
-}
-
-static void bit_vooddc_setscl(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= DDC_SCL_OUT;
- else
- r &= ~DDC_SCL_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-static void bit_vooddc_setsda(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if (val)
- r |= DDC_SDA_OUT;
- else
- r &= ~DDC_SDA_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-static int bit_vooddc_getscl(void *data)
-{
- return (0 != (readl(ioaddr + REG) & DDC_SCL_IN));
-}
-
-static int bit_vooddc_getsda(void *data)
-{
- return (0 != (readl(ioaddr + REG) & DDC_SDA_IN));
-}
-
-static int config_v3(struct pci_dev *dev)
-{
- unsigned long cadr;
-
- /* map Voodoo3 memory */
- cadr = dev->resource[0].start;
- cadr &= PCI_BASE_ADDRESS_MEM_MASK;
- ioaddr = ioremap_nocache(cadr, 0x1000);
- if (ioaddr) {
- writel(0x8160, ioaddr + REG2);
- writel(0xcffc0020, ioaddr + REG);
- dev_info(&dev->dev, "Using Banshee/Voodoo3 I2C device at %p\n", ioaddr);
- return 0;
- }
- return -ENODEV;
-}
-
-static struct i2c_algo_bit_data voo_i2c_bit_data = {
- .setsda = bit_vooi2c_setsda,
- .setscl = bit_vooi2c_setscl,
- .getsda = bit_vooi2c_getsda,
- .getscl = bit_vooi2c_getscl,
- .udelay = CYCLE_DELAY,
- .timeout = TIMEOUT
-};
-
-static struct i2c_adapter voodoo3_i2c_adapter = {
- .owner = THIS_MODULE,
- .name = "I2C Voodoo3/Banshee adapter",
- .algo_data = &voo_i2c_bit_data,
-};
-
-static struct i2c_algo_bit_data voo_ddc_bit_data = {
- .setsda = bit_vooddc_setsda,
- .setscl = bit_vooddc_setscl,
- .getsda = bit_vooddc_getsda,
- .getscl = bit_vooddc_getscl,
- .udelay = CYCLE_DELAY,
- .timeout = TIMEOUT
-};
-
-static struct i2c_adapter voodoo3_ddc_adapter = {
- .owner = THIS_MODULE,
- .class = I2C_CLASS_DDC,
- .name = "DDC Voodoo3/Banshee adapter",
- .algo_data = &voo_ddc_bit_data,
-};
-
-static struct pci_device_id voodoo3_ids[] __devinitdata = {
- { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3) },
- { PCI_DEVICE(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE (pci, voodoo3_ids);
-
-static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- int retval;
-
- retval = config_v3(dev);
- if (retval)
- return retval;
-
- /* set up the sysfs linkage to our parent device */
- voodoo3_i2c_adapter.dev.parent = &dev->dev;
- voodoo3_ddc_adapter.dev.parent = &dev->dev;
-
- retval = i2c_bit_add_bus(&voodoo3_i2c_adapter);
- if (retval)
- return retval;
- retval = i2c_bit_add_bus(&voodoo3_ddc_adapter);
- if (retval)
- i2c_del_adapter(&voodoo3_i2c_adapter);
- return retval;
-}
-
-static void __devexit voodoo3_remove(struct pci_dev *dev)
-{
- i2c_del_adapter(&voodoo3_i2c_adapter);
- i2c_del_adapter(&voodoo3_ddc_adapter);
- iounmap(ioaddr);
-}
-
-static struct pci_driver voodoo3_driver = {
- .name = "voodoo3_smbus",
- .id_table = voodoo3_ids,
- .probe = voodoo3_probe,
- .remove = __devexit_p(voodoo3_remove),
-};
-
-static int __init i2c_voodoo3_init(void)
-{
- return pci_register_driver(&voodoo3_driver);
-}
-
-static void __exit i2c_voodoo3_exit(void)
-{
- pci_unregister_driver(&voodoo3_driver);
-}
-
-
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
- "Philip Edelbrock <phil@netroedge.com>, "
- "Ralph Metzler <rjkm@thp.uni-koeln.de>, "
- "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
-MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
-MODULE_LICENSE("GPL");
-
-module_init(i2c_voodoo3_init);
-module_exit(i2c_voodoo3_exit);
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index f9618f4d4e4..ae4539d99be 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -6,16 +6,6 @@
menu "Miscellaneous I2C Chip support"
-config DS1682
- tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
- depends on EXPERIMENTAL
- help
- If you say yes here you get support for Dallas Semiconductor
- DS1682 Total Elapsed Time Recorder.
-
- This driver can also be built as a module. If so, the module
- will be called ds1682.
-
config SENSORS_TSL2550
tristate "Taos TSL2550 ambient light sensor"
depends on EXPERIMENTAL
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index 749cf360629..fe0af0f81f2 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -10,7 +10,6 @@
# * I/O expander drivers go to drivers/gpio
#
-obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 29650435514..4f34823e86b 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -558,11 +558,9 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
up_read(&__i2c_board_lock);
}
-static int i2c_do_add_adapter(struct device_driver *d, void *data)
+static int i2c_do_add_adapter(struct i2c_driver *driver,
+ struct i2c_adapter *adap)
{
- struct i2c_driver *driver = to_i2c_driver(d);
- struct i2c_adapter *adap = data;
-
/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver);
@@ -574,6 +572,11 @@ static int i2c_do_add_adapter(struct device_driver *d, void *data)
return 0;
}
+static int __process_new_adapter(struct device_driver *d, void *data)
+{
+ return i2c_do_add_adapter(to_i2c_driver(d), data);
+}
+
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
@@ -584,7 +587,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
goto out_list;
}
- mutex_init(&adap->bus_lock);
+ rt_mutex_init(&adap->bus_lock);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
@@ -614,7 +617,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
/* Notify drivers */
mutex_lock(&core_lock);
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
- i2c_do_add_adapter);
+ __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
@@ -715,10 +718,9 @@ retry:
}
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
-static int i2c_do_del_adapter(struct device_driver *d, void *data)
+static int i2c_do_del_adapter(struct i2c_driver *driver,
+ struct i2c_adapter *adapter)
{
- struct i2c_driver *driver = to_i2c_driver(d);
- struct i2c_adapter *adapter = data;
struct i2c_client *client, *_n;
int res;
@@ -750,6 +752,11 @@ static int __unregister_client(struct device *dev, void *dummy)
return 0;
}
+static int __process_removed_adapter(struct device_driver *d, void *data)
+{
+ return i2c_do_del_adapter(to_i2c_driver(d), data);
+}
+
/**
* i2c_del_adapter - unregister I2C adapter
* @adap: the adapter being unregistered
@@ -777,7 +784,7 @@ int i2c_del_adapter(struct i2c_adapter *adap)
/* Tell drivers about this removal */
mutex_lock(&core_lock);
res = bus_for_each_drv(&i2c_bus_type, NULL, adap,
- i2c_do_del_adapter);
+ __process_removed_adapter);
mutex_unlock(&core_lock);
if (res)
return res;
@@ -826,22 +833,11 @@ EXPORT_SYMBOL(i2c_del_adapter);
/* ------------------------------------------------------------------------- */
-static int __attach_adapter(struct device *dev, void *data)
+static int __process_new_driver(struct device *dev, void *data)
{
- struct i2c_adapter *adapter;
- struct i2c_driver *driver = data;
-
if (dev->type != &i2c_adapter_type)
return 0;
- adapter = to_i2c_adapter(dev);
-
- i2c_detect(adapter, driver);
-
- /* Legacy drivers scan i2c busses directly */
- if (driver->attach_adapter)
- driver->attach_adapter(adapter);
-
- return 0;
+ return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
/*
@@ -873,40 +869,18 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
mutex_lock(&core_lock);
- bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
+ bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
mutex_unlock(&core_lock);
return 0;
}
EXPORT_SYMBOL(i2c_register_driver);
-static int __detach_adapter(struct device *dev, void *data)
+static int __process_removed_driver(struct device *dev, void *data)
{
- struct i2c_adapter *adapter;
- struct i2c_driver *driver = data;
- struct i2c_client *client, *_n;
-
if (dev->type != &i2c_adapter_type)
return 0;
- adapter = to_i2c_adapter(dev);
-
- /* Remove the devices we created ourselves as the result of hardware
- * probing (using a driver's detect method) */
- list_for_each_entry_safe(client, _n, &driver->clients, detected) {
- dev_dbg(&adapter->dev, "Removing %s at 0x%x\n",
- client->name, client->addr);
- list_del(&client->detected);
- i2c_unregister_device(client);
- }
-
- if (driver->detach_adapter) {
- if (driver->detach_adapter(adapter))
- dev_err(&adapter->dev,
- "detach_adapter failed for driver [%s]\n",
- driver->driver.name);
- }
-
- return 0;
+ return i2c_do_del_adapter(data, to_i2c_adapter(dev));
}
/**
@@ -917,7 +891,7 @@ static int __detach_adapter(struct device *dev, void *data)
void i2c_del_driver(struct i2c_driver *driver)
{
mutex_lock(&core_lock);
- bus_for_each_dev(&i2c_bus_type, NULL, driver, __detach_adapter);
+ bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_removed_driver);
mutex_unlock(&core_lock);
driver_unregister(&driver->driver);
@@ -1092,12 +1066,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
#endif
if (in_atomic() || irqs_disabled()) {
- ret = mutex_trylock(&adap->bus_lock);
+ ret = rt_mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
- mutex_lock_nested(&adap->bus_lock, adap->level);
+ rt_mutex_lock(&adap->bus_lock);
}
/* Retry automatically on arbitration loss */
@@ -1109,7 +1083,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
- mutex_unlock(&adap->bus_lock);
+ rt_mutex_unlock(&adap->bus_lock);
return ret;
} else {
@@ -1180,7 +1154,7 @@ EXPORT_SYMBOL(i2c_master_recv);
* ----------------------------------------------------
*/
-static int i2c_detect_address(struct i2c_client *temp_client, int kind,
+static int i2c_detect_address(struct i2c_client *temp_client,
struct i2c_driver *driver)
{
struct i2c_board_info info;
@@ -1199,22 +1173,18 @@ static int i2c_detect_address(struct i2c_client *temp_client, int kind,
if (i2c_check_addr(adapter, addr))
return 0;
- /* Make sure there is something at this address, unless forced */
- if (kind < 0) {
- if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
- I2C_SMBUS_QUICK, NULL) < 0)
- return 0;
+ /* Make sure there is something at this address */
+ if (i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0)
+ return 0;
- /* prevent 24RF08 corruption */
- if ((addr & ~0x0f) == 0x50)
- i2c_smbus_xfer(adapter, addr, 0, 0, 0,
- I2C_SMBUS_QUICK, NULL);
- }
+ /* Prevent 24RF08 corruption */
+ if ((addr & ~0x0f) == 0x50)
+ i2c_smbus_xfer(adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL);
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
- err = driver->detect(temp_client, kind, &info);
+ err = driver->detect(temp_client, -1, &info);
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
here as this isn't an error. */
@@ -1259,40 +1229,13 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
return -ENOMEM;
temp_client->adapter = adapter;
- /* Force entries are done first, and are not affected by ignore
- entries */
- if (address_data->forces) {
- const unsigned short * const *forces = address_data->forces;
- int kind;
-
- for (kind = 0; forces[kind]; kind++) {
- for (i = 0; forces[kind][i] != I2C_CLIENT_END;
- i += 2) {
- if (forces[kind][i] == adap_id
- || forces[kind][i] == ANY_I2C_BUS) {
- dev_dbg(&adapter->dev, "found force "
- "parameter for adapter %d, "
- "addr 0x%02x, kind %d\n",
- adap_id, forces[kind][i + 1],
- kind);
- temp_client->addr = forces[kind][i + 1];
- err = i2c_detect_address(temp_client,
- kind, driver);
- if (err)
- goto exit_free;
- }
- }
- }
- }
-
/* Stop here if the classes do not match */
if (!(adapter->class & driver->class))
goto exit_free;
/* Stop here if we can't use SMBUS_QUICK */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
- if (address_data->probe[0] == I2C_CLIENT_END
- && address_data->normal_i2c[0] == I2C_CLIENT_END)
+ if (address_data->normal_i2c[0] == I2C_CLIENT_END)
goto exit_free;
dev_warn(&adapter->dev, "SMBus Quick command not supported, "
@@ -1301,48 +1244,12 @@ static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
goto exit_free;
}
- /* Probe entries are done second, and are not affected by ignore
- entries either */
- for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
- if (address_data->probe[i] == adap_id
- || address_data->probe[i] == ANY_I2C_BUS) {
- dev_dbg(&adapter->dev, "found probe parameter for "
- "adapter %d, addr 0x%02x\n", adap_id,
- address_data->probe[i + 1]);
- temp_client->addr = address_data->probe[i + 1];
- err = i2c_detect_address(temp_client, -1, driver);
- if (err)
- goto exit_free;
- }
- }
-
- /* Normal entries are done last, unless shadowed by an ignore entry */
for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
- int j, ignore;
-
- ignore = 0;
- for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
- j += 2) {
- if ((address_data->ignore[j] == adap_id ||
- address_data->ignore[j] == ANY_I2C_BUS)
- && address_data->ignore[j + 1]
- == address_data->normal_i2c[i]) {
- dev_dbg(&adapter->dev, "found ignore "
- "parameter for adapter %d, "
- "addr 0x%02x\n", adap_id,
- address_data->ignore[j + 1]);
- ignore = 1;
- break;
- }
- }
- if (ignore)
- continue;
-
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->normal_i2c[i]);
temp_client->addr = address_data->normal_i2c[i];
- err = i2c_detect_address(temp_client, -1, driver);
+ err = i2c_detect_address(temp_client, driver);
if (err)
goto exit_free;
}
@@ -1913,7 +1820,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
if (adapter->algo->smbus_xfer) {
- mutex_lock(&adapter->bus_lock);
+ rt_mutex_lock(&adapter->bus_lock);
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
@@ -1927,7 +1834,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
orig_jiffies + adapter->timeout))
break;
}
- mutex_unlock(&adapter->bus_lock);
+ rt_mutex_unlock(&adapter->bus_lock);
} else
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
command, protocol, data);
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 7e13d2df9af..f4110aa4960 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -34,7 +34,6 @@
#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
-#include <linux/smp_lock.h>
#include <linux/jiffies.h>
#include <asm/uaccess.h>
@@ -445,20 +444,14 @@ static int i2cdev_open(struct inode *inode, struct file *file)
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
- int ret = 0;
- lock_kernel();
i2c_dev = i2c_dev_get_by_minor(minor);
- if (!i2c_dev) {
- ret = -ENODEV;
- goto out;
- }
+ if (!i2c_dev)
+ return -ENODEV;
adap = i2c_get_adapter(i2c_dev->adap->nr);
- if (!adap) {
- ret = -ENODEV;
- goto out;
- }
+ if (!adap)
+ return -ENODEV;
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
@@ -470,8 +463,7 @@ static int i2cdev_open(struct inode *inode, struct file *file)
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
- ret = -ENOMEM;
- goto out;
+ return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->driver = &i2cdev_driver;
@@ -479,9 +471,7 @@ static int i2cdev_open(struct inode *inode, struct file *file)
client->adapter = adap;
file->private_data = client;
-out:
- unlock_kernel();
- return ret;
+ return 0;
}
static int i2cdev_release(struct inode *inode, struct file *file)
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 9a5d0aaac9d..98ccfeb3f5a 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -7,50 +7,25 @@ config HAVE_IDE
bool
menuconfig IDE
- tristate "ATA/ATAPI/MFM/RLL support"
+ tristate "ATA/ATAPI/MFM/RLL support (DEPRECATED)"
depends on HAVE_IDE
depends on BLOCK
---help---
- If you say Y here, your kernel will be able to manage low cost mass
- storage units such as ATA/(E)IDE and ATAPI units. The most common
- cases are IDE hard drives and ATAPI CD-ROM drives.
-
- If your system is pure SCSI and doesn't use these interfaces, you
- can say N here.
-
- Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard
- for mass storage units such as hard disks. It was designed by
- Western Digital and Compaq Computer in 1984. It was then named
- ST506. Quite a number of disks use the IDE interface.
-
- AT Attachment (ATA) is the superset of the IDE specifications.
- ST506 was also called ATA-1.
-
- Fast-IDE is ATA-2 (also named Fast ATA), Enhanced IDE (EIDE) is
- ATA-3. It provides support for larger disks (up to 8.4GB by means of
- the LBA standard), more disks (4 instead of 2) and for other mass
- storage units such as tapes and cdrom. UDMA/33 (aka UltraDMA/33) is
- ATA-4 and provides faster (and more CPU friendly) transfer modes
- than previous PIO (Programmed processor Input/Output) from previous
- ATA/IDE standards by means of fast DMA controllers.
-
- ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and
- CD-ROM drives, similar in many respects to the SCSI protocol.
-
- SMART IDE (Self Monitoring, Analysis and Reporting Technology) was
- designed in order to prevent data corruption and disk crash by
- detecting pre hardware failure conditions (heat, access time, and
- the like...). Disks built since June 1995 may follow this standard.
- The kernel itself doesn't manage this; however there are quite a
- number of user programs such as smart that can query the status of
- SMART parameters from disk drives.
+ If you say Y here, your kernel will be able to manage ATA/(E)IDE and
+ ATAPI units. The most common cases are IDE hard drives and ATAPI
+ CD-ROM drives.
+
+ This subsystem is currently in maintenance mode with only bug fix
+ changes applied. Users of ATA hardware are encouraged to migrate to
+ the newer ATA subsystem ("Serial ATA (prod) and Parallel ATA
+ (experimental) drivers") which is more actively maintained.
To compile this driver as a module, choose M here: the
module will be called ide-core.
For further information, please read <file:Documentation/ide/ide.txt>.
- If unsure, say Y.
+ If unsure, say N.
if IDE
diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c
index e59b6dee9ae..0abc43f3101 100644
--- a/drivers/ide/alim15x3.c
+++ b/drivers/ide/alim15x3.c
@@ -40,16 +40,6 @@
#define DRV_NAME "alim15x3"
/*
- * Allow UDMA on M1543C-E chipset for WDC disks that ignore CRC checking
- * (this is DANGEROUS and could result in data corruption).
- */
-static int wdc_udma;
-
-module_param(wdc_udma, bool, 0);
-MODULE_PARM_DESC(wdc_udma,
- "allow UDMA on M1543C-E chipset for WDC disks (DANGEROUS)");
-
-/*
* ALi devices are not plug in. Otherwise these static values would
* need to go. They ought to go away anyway
*/
@@ -132,7 +122,7 @@ static u8 ali_udma_filter(ide_drive_t *drive)
if (m5229_revision > 0x20 && m5229_revision < 0xC2) {
if (drive->media != ide_disk)
return 0;
- if (wdc_udma == 0 && chip_is_1543c_e &&
+ if (chip_is_1543c_e &&
strstr((char *)&drive->id[ATA_ID_PROD], "WDC "))
return 0;
}
diff --git a/drivers/ide/au1xxx-ide.c b/drivers/ide/au1xxx-ide.c
index 58121bd6c11..87cef0c440a 100644
--- a/drivers/ide/au1xxx-ide.c
+++ b/drivers/ide/au1xxx-ide.c
@@ -532,14 +532,13 @@ static int au_ide_probe(struct platform_device *dev)
goto out;
}
- if (!request_mem_region(res->start, res->end - res->start + 1,
- dev->name)) {
+ if (!request_mem_region(res->start, resource_size(res), dev->name)) {
pr_debug("%s: request_mem_region failed\n", DRV_NAME);
ret = -EBUSY;
goto out;
}
- ahwif->regbase = (u32)ioremap(res->start, res->end - res->start + 1);
+ ahwif->regbase = (u32)ioremap(res->start, resource_size(res));
if (ahwif->regbase == 0) {
ret = -ENOMEM;
goto out;
@@ -575,7 +574,7 @@ static int au_ide_remove(struct platform_device *dev)
iounmap((void *)ahwif->regbase);
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
return 0;
}
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
index ca0c46f6580..f2500c8826b 100644
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -20,14 +20,6 @@
#define DRV_NAME "cmd64x"
-#define CMD_DEBUG 0
-
-#if CMD_DEBUG
-#define cmdprintk(x...) printk(x)
-#else
-#define cmdprintk(x...)
-#endif
-
/*
* CMD64x specific registers definition.
*/
@@ -76,9 +68,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_
{15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
static const u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM2, DRWTIM3};
- cmdprintk("program_cycle_times parameters: total=%d, active=%d\n",
- cycle_time, active_time);
-
cycle_count = quantize_timing( cycle_time, clock_time);
active_count = quantize_timing(active_time, clock_time);
recovery_count = cycle_count - active_count;
@@ -94,9 +83,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_
if (active_count > 16) /* shouldn't actually happen... */
active_count = 16;
- cmdprintk("Final counts: total=%d, active=%d, recovery=%d\n",
- cycle_count, active_count, recovery_count);
-
/*
* Convert values to internal chipset representation
*/
@@ -106,7 +92,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_
/* Program the active/recovery counts into the DRWTIM register */
drwtim = (active_count << 4) | recovery_count;
(void) pci_write_config_byte(dev, drwtim_regs[drive->dn], drwtim);
- cmdprintk("Write 0x%02x to reg 0x%x\n", drwtim, drwtim_regs[drive->dn]);
}
/*
@@ -150,7 +135,6 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
if (setup_count > 5) /* shouldn't actually happen... */
setup_count = 5;
- cmdprintk("Final address setup count: %d\n", setup_count);
/*
* Program the address setup clocks into the ARTTIM registers.
@@ -162,7 +146,6 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
arttim &= ~0xc0;
arttim |= setup_values[setup_count];
(void) pci_write_config_byte(dev, arttim_regs[drive->dn], arttim);
- cmdprintk("Write 0x%02x to reg 0x%x\n", arttim, arttim_regs[drive->dn]);
}
/*
diff --git a/drivers/ide/cs5535.c b/drivers/ide/cs5535.c
index 983d957a018..b883838adc2 100644
--- a/drivers/ide/cs5535.c
+++ b/drivers/ide/cs5535.c
@@ -187,6 +187,7 @@ static int __devinit cs5535_init_one(struct pci_dev *dev,
static const struct pci_device_id cs5535_pci_tbl[] = {
{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_CS5535_IDE), 0 },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5535_IDE), },
{ 0, },
};
diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c
index 74fc5401f40..d6e2cbbc53a 100644
--- a/drivers/ide/cy82c693.c
+++ b/drivers/ide/cy82c693.c
@@ -51,11 +51,6 @@
#define DRV_NAME "cy82c693"
/*
- * The following are used to debug the driver.
- */
-#define CY82C693_DEBUG_INFO 0
-
-/*
* NOTE: the value for busmaster timeout is tricky and I got it by
* trial and error! By using a to low value will cause DMA timeouts
* and drop IDE performance, and by using a to high value will cause
@@ -176,11 +171,6 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode)
outb(index, CY82_INDEX_PORT);
outb(data, CY82_DATA_PORT);
-#if CY82C693_DEBUG_INFO
- printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n",
- drive->name, hwif->channel, drive->dn & 1, mode & 3, single);
-#endif /* CY82C693_DEBUG_INFO */
-
/*
* note: below we set the value for Bus Master IDE TimeOut Register
* I'm not absolutly sure what this does, but it solved my problem
@@ -194,11 +184,6 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode)
data = BUSMASTER_TIMEOUT;
outb(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
outb(data, CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO
- printk(KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n",
- drive->name, data);
-#endif /* CY82C693_DEBUG_INFO */
}
static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
@@ -239,8 +224,6 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
-
- addrCtrl &= 0xF;
} else {
/*
* set slave drive
@@ -257,17 +240,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
-
- addrCtrl >>= 4;
- addrCtrl &= 0xF;
}
-
-#if CY82C693_DEBUG_INFO
- printk(KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to "
- "(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n",
- drive->name, hwif->channel, drive->dn & 1,
- addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
-#endif /* CY82C693_DEBUG_INFO */
}
static void __devinit init_iops_cy82c693(ide_hwif_t *hwif)
diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
index 7ce68ef6b90..4d90ac2dbb1 100644
--- a/drivers/ide/hpt366.c
+++ b/drivers/ide/hpt366.c
@@ -297,68 +297,6 @@ static u32 twenty_five_base_hpt36x[] = {
/* XFER_PIO_0 */ 0xc0d08585
};
-#if 0
-/* These are the timing tables from the HighPoint open source drivers... */
-static u32 thirty_three_base_hpt37x[] = {
- /* XFER_UDMA_6 */ 0x12446231, /* 0x12646231 ?? */
- /* XFER_UDMA_5 */ 0x12446231,
- /* XFER_UDMA_4 */ 0x12446231,
- /* XFER_UDMA_3 */ 0x126c6231,
- /* XFER_UDMA_2 */ 0x12486231,
- /* XFER_UDMA_1 */ 0x124c6233,
- /* XFER_UDMA_0 */ 0x12506297,
-
- /* XFER_MW_DMA_2 */ 0x22406c31,
- /* XFER_MW_DMA_1 */ 0x22406c33,
- /* XFER_MW_DMA_0 */ 0x22406c97,
-
- /* XFER_PIO_4 */ 0x06414e31,
- /* XFER_PIO_3 */ 0x06414e42,
- /* XFER_PIO_2 */ 0x06414e53,
- /* XFER_PIO_1 */ 0x06814e93,
- /* XFER_PIO_0 */ 0x06814ea7
-};
-
-static u32 fifty_base_hpt37x[] = {
- /* XFER_UDMA_6 */ 0x12848242,
- /* XFER_UDMA_5 */ 0x12848242,
- /* XFER_UDMA_4 */ 0x12ac8242,
- /* XFER_UDMA_3 */ 0x128c8242,
- /* XFER_UDMA_2 */ 0x120c8242,
- /* XFER_UDMA_1 */ 0x12148254,
- /* XFER_UDMA_0 */ 0x121882ea,
-
- /* XFER_MW_DMA_2 */ 0x22808242,
- /* XFER_MW_DMA_1 */ 0x22808254,
- /* XFER_MW_DMA_0 */ 0x228082ea,
-
- /* XFER_PIO_4 */ 0x0a81f442,
- /* XFER_PIO_3 */ 0x0a81f443,
- /* XFER_PIO_2 */ 0x0a81f454,
- /* XFER_PIO_1 */ 0x0ac1f465,
- /* XFER_PIO_0 */ 0x0ac1f48a
-};
-
-static u32 sixty_six_base_hpt37x[] = {
- /* XFER_UDMA_6 */ 0x1c869c62,
- /* XFER_UDMA_5 */ 0x1cae9c62, /* 0x1c8a9c62 */
- /* XFER_UDMA_4 */ 0x1c8a9c62,
- /* XFER_UDMA_3 */ 0x1c8e9c62,
- /* XFER_UDMA_2 */ 0x1c929c62,
- /* XFER_UDMA_1 */ 0x1c9a9c62,
- /* XFER_UDMA_0 */ 0x1c829c62,
-
- /* XFER_MW_DMA_2 */ 0x2c829c62,
- /* XFER_MW_DMA_1 */ 0x2c829c66,
- /* XFER_MW_DMA_0 */ 0x2c829d2e,
-
- /* XFER_PIO_4 */ 0x0c829c62,
- /* XFER_PIO_3 */ 0x0c829c84,
- /* XFER_PIO_2 */ 0x0c829ca6,
- /* XFER_PIO_1 */ 0x0d029d26,
- /* XFER_PIO_0 */ 0x0d029d5e
-};
-#else
/*
* The following are the new timing tables with PIO mode data/taskfile transfer
* overclocking fixed...
@@ -424,16 +362,13 @@ static u32 sixty_six_base_hpt37x[] = {
/* XFER_PIO_1 */ 0x0d02ff26,
/* XFER_PIO_0 */ 0x0d42ff7f
};
-#endif
-#define HPT366_DEBUG_DRIVE_INFO 0
#define HPT371_ALLOW_ATA133_6 1
#define HPT302_ALLOW_ATA133_6 1
#define HPT372_ALLOW_ATA133_6 1
#define HPT370_ALLOW_ATA100_5 0
#define HPT366_ALLOW_ATA66_4 1
#define HPT366_ALLOW_ATA66_3 1
-#define HPT366_MAX_DEVS 8
/* Supported ATA clock frequencies */
enum ata_clock {
diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c
index 063b933d864..dd6396384c2 100644
--- a/drivers/ide/ide-cs.c
+++ b/drivers/ide/ide-cs.c
@@ -60,15 +60,6 @@ MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("PCMCIA ATA/IDE card driver");
MODULE_LICENSE("Dual MPL/GPL");
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-#ifdef CONFIG_PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-#else
-#define DEBUG(n, args...)
-#endif
-
/*====================================================================*/
typedef struct ide_info_t {
@@ -98,7 +89,7 @@ static int ide_probe(struct pcmcia_device *link)
{
ide_info_t *info;
- DEBUG(0, "ide_attach()\n");
+ dev_dbg(&link->dev, "ide_attach()\n");
/* Create new ide device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -112,7 +103,6 @@ static int ide_probe(struct pcmcia_device *link)
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.IOAddrLines = 3;
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -134,7 +124,7 @@ static void ide_detach(struct pcmcia_device *link)
ide_hwif_t *hwif = info->host->ports[0];
unsigned long data_addr, ctl_addr;
- DEBUG(0, "ide_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "ide_detach(0x%p)\n", link);
data_addr = hwif->io_ports.data_addr;
ctl_addr = hwif->io_ports.ctl_addr;
@@ -217,9 +207,6 @@ out_release:
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
struct pcmcia_config_check {
unsigned long ctl_base;
int skip_vcc;
@@ -282,11 +269,11 @@ static int ide_config(struct pcmcia_device *link)
{
ide_info_t *info = link->priv;
struct pcmcia_config_check *stk = NULL;
- int last_ret = 0, last_fn = 0, is_kme = 0;
+ int ret = 0, is_kme = 0;
unsigned long io_base, ctl_base;
struct ide_host *host;
- DEBUG(0, "ide_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "ide_config(0x%p)\n", link);
is_kme = ((link->manf_id == MANFID_KME) &&
((link->card_id == PRODID_KME_KXLC005_A) ||
@@ -306,8 +293,12 @@ static int ide_config(struct pcmcia_device *link)
io_base = link->io.BasePort1;
ctl_base = stk->ctl_base;
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/* disable drive interrupts during IDE probe */
outb(0x02, ctl_base);
@@ -342,8 +333,6 @@ err_mem:
printk(KERN_NOTICE "ide-cs: ide_config failed memory allocation\n");
goto failed;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
kfree(stk);
ide_release(link);
@@ -363,7 +352,7 @@ static void ide_release(struct pcmcia_device *link)
ide_info_t *info = link->priv;
struct ide_host *host = info->host;
- DEBUG(0, "ide_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "ide_release(0x%p)\n", link);
if (info->ndev)
/* FIXME: if this fails we need to queue the cleanup somehow
diff --git a/drivers/ide/ide-pci-generic.c b/drivers/ide/ide-pci-generic.c
index 39d4e01f5c9..a743e68a890 100644
--- a/drivers/ide/ide-pci-generic.c
+++ b/drivers/ide/ide-pci-generic.c
@@ -162,9 +162,10 @@ static const struct pci_device_id generic_pci_tbl[] = {
#ifdef CONFIG_BLK_DEV_IDE_SATA
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8237_SATA), 5 },
#endif
- { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO), 4 },
{ PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), 4 },
{ PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), 4 },
+ { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), 4 },
+ { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_5), 4 },
{ PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), 6 },
/*
* Must come last. If you add entries adjust
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 58fc920d5c3..6a0e6254216 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -221,6 +221,8 @@ typedef struct ide_tape_obj {
static DEFINE_MUTEX(idetape_ref_mutex);
+static DEFINE_MUTEX(idetape_chrdev_mutex);
+
static struct class *idetape_sysfs_class;
static void ide_tape_release(struct device *);
@@ -1457,10 +1459,11 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
if (i >= MAX_HWIFS * MAX_DRIVES)
return -ENXIO;
- lock_kernel();
+ mutex_lock(&idetape_chrdev_mutex);
+
tape = ide_tape_get(NULL, true, i);
if (!tape) {
- unlock_kernel();
+ mutex_unlock(&idetape_chrdev_mutex);
return -ENXIO;
}
@@ -1519,12 +1522,15 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
tape->door_locked = DOOR_LOCKED;
}
}
- unlock_kernel();
+ mutex_unlock(&idetape_chrdev_mutex);
+
return 0;
out_put_tape:
ide_tape_put(tape);
- unlock_kernel();
+
+ mutex_unlock(&idetape_chrdev_mutex);
+
return retval;
}
@@ -1551,7 +1557,8 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp)
ide_drive_t *drive = tape->drive;
unsigned int minor = iminor(inode);
- lock_kernel();
+ mutex_lock(&idetape_chrdev_mutex);
+
tape = drive->driver_data;
ide_debug_log(IDE_DBG_FUNC, "enter");
@@ -1575,7 +1582,9 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp)
}
clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
ide_tape_put(tape);
- unlock_kernel();
+
+ mutex_unlock(&idetape_chrdev_mutex);
+
return 0;
}
diff --git a/drivers/ide/ide_platform.c b/drivers/ide/ide_platform.c
index b579fbe8837..42965b3e30b 100644
--- a/drivers/ide/ide_platform.c
+++ b/drivers/ide/ide_platform.c
@@ -81,14 +81,14 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
if (mmio) {
base = devm_ioremap(&pdev->dev,
- res_base->start, res_base->end - res_base->start + 1);
+ res_base->start, resource_size(res_base));
alt_base = devm_ioremap(&pdev->dev,
- res_alt->start, res_alt->end - res_alt->start + 1);
+ res_alt->start, resource_size(res_alt));
} else {
base = devm_ioport_map(&pdev->dev,
- res_base->start, res_base->end - res_base->start + 1);
+ res_base->start, resource_size(res_base));
alt_base = devm_ioport_map(&pdev->dev,
- res_alt->start, res_alt->end - res_alt->start + 1);
+ res_alt->start, resource_size(res_alt));
}
memset(&hw, 0, sizeof(hw));
diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c
index cb812f3700e..35161dd840a 100644
--- a/drivers/ide/pdc202xx_old.c
+++ b/drivers/ide/pdc202xx_old.c
@@ -21,8 +21,6 @@
#define DRV_NAME "pdc202xx_old"
-#define PDC202XX_DEBUG_DRIVE_INFO 0
-
static void pdc_old_disable_66MHz_clock(ide_hwif_t *);
static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
@@ -34,11 +32,6 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
u8 AP = 0, BP = 0, CP = 0;
u8 TA = 0, TB = 0, TC = 0;
-#if PDC202XX_DEBUG_DRIVE_INFO
- u32 drive_conf = 0;
- pci_read_config_dword(dev, drive_pci, &drive_conf);
-#endif
-
/*
* TODO: do this once per channel
*/
@@ -89,14 +82,6 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
pci_write_config_byte(dev, drive_pci + 1, BP | TB);
pci_write_config_byte(dev, drive_pci + 2, CP | TC);
}
-
-#if PDC202XX_DEBUG_DRIVE_INFO
- printk(KERN_DEBUG "%s: %s drive%d 0x%08x ",
- drive->name, ide_xfer_verbose(speed),
- drive->dn, drive_conf);
- pci_read_config_dword(dev, drive_pci, &drive_conf);
- printk("0x%08x\n", drive_conf);
-#endif
}
static void pdc202xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c
index 3b88eba04c9..468706082fb 100644
--- a/drivers/ide/sis5513.c
+++ b/drivers/ide/sis5513.c
@@ -632,12 +632,3 @@ module_exit(sis5513_ide_exit);
MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik");
MODULE_DESCRIPTION("PCI driver module for SIS IDE");
MODULE_LICENSE("GPL");
-
-/*
- * TODO:
- * - CLEANUP
- * - More checks in the config registers (force values instead of
- * relying on the BIOS setting them correctly).
- * - Further optimisations ?
- * . for example ATA66+ regs 0x48 & 0x4A
- */
diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
index d698da470d6..3c2bbf0057e 100644
--- a/drivers/ide/sl82c105.c
+++ b/drivers/ide/sl82c105.c
@@ -24,13 +24,6 @@
#define DRV_NAME "sl82c105"
-#undef DEBUG
-
-#ifdef DEBUG
-#define DBG(arg) printk arg
-#else
-#define DBG(fmt,...)
-#endif
/*
* SL82C105 PCI config register 0x40 bits.
*/
@@ -104,9 +97,6 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
unsigned long timings = (unsigned long)ide_get_drivedata(drive);
u16 drv_ctrl;
- DBG(("sl82c105_tune_chipset(drive:%s, speed:%s)\n",
- drive->name, ide_xfer_verbose(speed)));
-
drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0];
/*
@@ -196,8 +186,6 @@ static void sl82c105_dma_start(ide_drive_t *drive)
struct pci_dev *dev = to_pci_dev(hwif->dev);
int reg = 0x44 + drive->dn * 4;
- DBG(("%s(drive:%s)\n", __func__, drive->name));
-
pci_write_config_word(dev, reg,
(unsigned long)ide_get_drivedata(drive) >> 16);
@@ -209,8 +197,6 @@ static void sl82c105_dma_clear(ide_drive_t *drive)
{
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
- DBG(("sl82c105_dma_clear(drive:%s)\n", drive->name));
-
sl82c105_reset_host(dev);
}
@@ -218,11 +204,7 @@ static int sl82c105_dma_end(ide_drive_t *drive)
{
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
int reg = 0x44 + drive->dn * 4;
- int ret;
-
- DBG(("%s(drive:%s)\n", __func__, drive->name));
-
- ret = ide_dma_end(drive);
+ int ret = ide_dma_end(drive);
pci_write_config_word(dev, reg,
(unsigned long)ide_get_drivedata(drive));
@@ -239,8 +221,6 @@ static void sl82c105_resetproc(ide_drive_t *drive)
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
u32 val;
- DBG(("sl82c105_resetproc(drive:%s)\n", drive->name));
-
pci_read_config_dword(dev, 0x40, &val);
val |= (CTRL_P1F16 | CTRL_P0F16);
pci_write_config_dword(dev, 0x40, val);
@@ -291,8 +271,6 @@ static int init_chipset_sl82c105(struct pci_dev *dev)
{
u32 val;
- DBG(("init_chipset_sl82c105()\n"));
-
pci_read_config_dword(dev, 0x40, &val);
val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
pci_write_config_dword(dev, 0x40, val);
diff --git a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c
index 9aec78d3bcf..1ccfb40e721 100644
--- a/drivers/ide/slc90e66.c
+++ b/drivers/ide/slc90e66.c
@@ -91,8 +91,7 @@ static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed)
if (!(reg48 & u_flag))
pci_write_config_word(dev, 0x48, reg48|u_flag);
- /* FIXME: (reg4a & a_speed) ? */
- if ((reg4a & u_speed) != u_speed) {
+ if ((reg4a & a_speed) != u_speed) {
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
pci_read_config_word(dev, 0x4a, &reg4a);
pci_write_config_word(dev, 0x4a, reg4a|u_speed);
diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c
index ea89fddeed9..fd59c0d235b 100644
--- a/drivers/ide/tx4938ide.c
+++ b/drivers/ide/tx4938ide.c
@@ -146,7 +146,7 @@ static int __init tx4938ide_probe(struct platform_device *pdev)
return -ENODEV;
if (!devm_request_mem_region(&pdev->dev, res->start,
- res->end - res->start + 1, "tx4938ide"))
+ resource_size(res), "tx4938ide"))
return -EBUSY;
mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start,
8 << pdata->ioport_shift);
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
index 2cd00b5b45b..9fd4a0d3206 100644
--- a/drivers/ieee1394/dv1394.c
+++ b/drivers/ieee1394/dv1394.c
@@ -125,7 +125,7 @@
0 - no debugging messages
1 - some debugging messages, but none during DMA frame transmission
2 - lots of messages, including during DMA frame transmission
- (will cause undeflows if your machine is too slow!)
+ (will cause underflows if your machine is too slow!)
*/
#define DV1394_DEBUG_LEVEL 0
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
index 65c1429e412..d0dc1db80b2 100644
--- a/drivers/ieee1394/ohci1394.c
+++ b/drivers/ieee1394/ohci1394.c
@@ -82,6 +82,7 @@
*
*/
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
@@ -434,7 +435,6 @@ static void initialize_dma_trm_ctx(struct dma_trm_ctx *d)
/* Count the number of available iso contexts */
static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
{
- int i,ctx=0;
u32 tmp;
reg_write(ohci, reg, 0xffffffff);
@@ -443,11 +443,7 @@ static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp);
/* Count the number of contexts */
- for (i=0; i<32; i++) {
- if (tmp & 1) ctx++;
- tmp >>= 1;
- }
- return ctx;
+ return hweight32(tmp);
}
/* Global initialization */
diff --git a/drivers/ieee802154/fakehard.c b/drivers/ieee802154/fakehard.c
index 7c544f7c74c..d9d0e13efe4 100644
--- a/drivers/ieee802154/fakehard.c
+++ b/drivers/ieee802154/fakehard.c
@@ -32,9 +32,29 @@
#include <net/nl802154.h>
#include <net/wpan-phy.h>
-struct wpan_phy *net_to_phy(struct net_device *dev)
+struct fakehard_priv {
+ struct wpan_phy *phy;
+};
+
+static struct wpan_phy *fake_to_phy(const struct net_device *dev)
{
- return container_of(dev->dev.parent, struct wpan_phy, dev);
+ struct fakehard_priv *priv = netdev_priv(dev);
+ return priv->phy;
+}
+
+/**
+ * fake_get_phy - Return a phy corresponding to this device.
+ * @dev: The network device for which to return the wan-phy object
+ *
+ * This function returns a wpan-phy object corresponding to the passed
+ * network device. Reference counter for wpan-phy object is incremented,
+ * so when the wpan-phy isn't necessary, you should drop the reference
+ * via @wpan_phy_put() call.
+ */
+static struct wpan_phy *fake_get_phy(const struct net_device *dev)
+{
+ struct wpan_phy *phy = fake_to_phy(dev);
+ return to_phy(get_device(&phy->dev));
}
/**
@@ -43,7 +63,7 @@ struct wpan_phy *net_to_phy(struct net_device *dev)
*
* Return the ID of the PAN from the PIB.
*/
-static u16 fake_get_pan_id(struct net_device *dev)
+static u16 fake_get_pan_id(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -58,7 +78,7 @@ static u16 fake_get_pan_id(struct net_device *dev)
* device. If the device has not yet had a short address assigned
* then this should return 0xFFFF to indicate a lack of association.
*/
-static u16 fake_get_short_addr(struct net_device *dev)
+static u16 fake_get_short_addr(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -78,7 +98,7 @@ static u16 fake_get_short_addr(struct net_device *dev)
* Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
* document.
*/
-static u8 fake_get_dsn(struct net_device *dev)
+static u8 fake_get_dsn(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -98,7 +118,7 @@ static u8 fake_get_dsn(struct net_device *dev)
* Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
* document.
*/
-static u8 fake_get_bsn(struct net_device *dev)
+static u8 fake_get_bsn(const struct net_device *dev)
{
BUG_ON(dev->type != ARPHRD_IEEE802154);
@@ -121,7 +141,7 @@ static u8 fake_get_bsn(struct net_device *dev)
static int fake_assoc_req(struct net_device *dev,
struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap)
{
- struct wpan_phy *phy = net_to_phy(dev);
+ struct wpan_phy *phy = fake_to_phy(dev);
mutex_lock(&phy->pib_lock);
phy->current_channel = channel;
@@ -196,7 +216,7 @@ static int fake_start_req(struct net_device *dev, struct ieee802154_addr *addr,
u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
u8 coord_realign)
{
- struct wpan_phy *phy = net_to_phy(dev);
+ struct wpan_phy *phy = fake_to_phy(dev);
mutex_lock(&phy->pib_lock);
phy->current_channel = channel;
@@ -239,6 +259,8 @@ static struct ieee802154_mlme_ops fake_mlme = {
.start_req = fake_start_req,
.scan_req = fake_scan_req,
+ .get_phy = fake_get_phy,
+
.get_pan_id = fake_get_pan_id,
.get_short_addr = fake_get_short_addr,
.get_dsn = fake_get_dsn,
@@ -310,7 +332,7 @@ static const struct net_device_ops fake_ops = {
static void ieee802154_fake_destruct(struct net_device *dev)
{
- struct wpan_phy *phy = net_to_phy(dev);
+ struct wpan_phy *phy = fake_to_phy(dev);
wpan_phy_unregister(phy);
free_netdev(dev);
@@ -335,13 +357,14 @@ static void ieee802154_fake_setup(struct net_device *dev)
static int __devinit ieee802154fake_probe(struct platform_device *pdev)
{
struct net_device *dev;
+ struct fakehard_priv *priv;
struct wpan_phy *phy = wpan_phy_alloc(0);
int err;
if (!phy)
return -ENOMEM;
- dev = alloc_netdev(0, "hardwpan%d", ieee802154_fake_setup);
+ dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d", ieee802154_fake_setup);
if (!dev) {
wpan_phy_free(phy);
return -ENOMEM;
@@ -353,12 +376,23 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev)
dev->addr_len);
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
- phy->channels_supported = (1 << 27) - 1;
+ /*
+ * For now we'd like to emulate 2.4 GHz-only device,
+ * both O-QPSK and CSS
+ */
+ /* 2.4 GHz O-QPSK 802.15.4-2003 */
+ phy->channels_supported[0] |= 0x7FFF800;
+ /* 2.4 GHz CSS 802.15.4a-2007 */
+ phy->channels_supported[3] |= 0x3fff;
+
phy->transmit_power = 0xbf;
dev->netdev_ops = &fake_ops;
dev->ml_priv = &fake_mlme;
+ priv = netdev_priv(dev);
+ priv->phy = phy;
+
/*
* If the name is a format string the caller wants us to do a
* name allocation.
@@ -369,11 +403,12 @@ static int __devinit ieee802154fake_probe(struct platform_device *pdev)
goto out;
}
+ wpan_phy_set_dev(phy, &pdev->dev);
SET_NETDEV_DEV(dev, &phy->dev);
platform_set_drvdata(pdev, dev);
- err = wpan_phy_register(&pdev->dev, phy);
+ err = wpan_phy_register(phy);
if (err)
goto out;
diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c
index 72ed3396b72..0677fc7dfd5 100644
--- a/drivers/infiniband/hw/cxgb3/cxio_hal.c
+++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c
@@ -589,7 +589,7 @@ static int cxio_hal_destroy_ctrl_qp(struct cxio_rdev *rdev_p)
/* write len bytes of data into addr (32B aligned address)
* If data is NULL, clear len byte of memory to zero.
- * caller aquires the ctrl_qp lock before the call
+ * caller acquires the ctrl_qp lock before the call
*/
static int cxio_hal_ctrl_qp_write_mem(struct cxio_rdev *rdev_p, u32 addr,
u32 len, void *data)
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 40dbe54056c..73933a41ce8 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -1821,7 +1821,6 @@ done:
static int ipath_open(struct inode *in, struct file *fp)
{
/* The real work is performed later in ipath_assign_port() */
- cycle_kernel_lock();
fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL);
return fp->private_data ? 0 : -ENOMEM;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index 4bd39c8af80..37d12e5efa4 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -381,7 +381,7 @@ static const ipath_err_t infinipath_hwe_htclnkbbyte1crcerr =
#define IPATH_GPIO_SCL \
(1ULL << (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
-/* keep the code below somewhat more readonable; not used elsewhere */
+/* keep the code below somewhat more readable; not used elsewhere */
#define _IPATH_HTLINK0_CRCBITS (infinipath_hwe_htclnkabyte0crcerr | \
infinipath_hwe_htclnkabyte1crcerr)
#define _IPATH_HTLINK1_CRCBITS (infinipath_hwe_htclnkbbyte0crcerr | \
diff --git a/drivers/infiniband/hw/ipath/ipath_sd7220.c b/drivers/infiniband/hw/ipath/ipath_sd7220.c
index aa47eb54952..2a68d9f624d 100644
--- a/drivers/infiniband/hw/ipath/ipath_sd7220.c
+++ b/drivers/infiniband/hw/ipath/ipath_sd7220.c
@@ -614,7 +614,7 @@ static int epb_trans(struct ipath_devdata *dd, u16 reg, u64 i_val, u64 *o_vp)
* @wd: Write Data - value to set in register
* @mask: ones where data should be spliced into reg.
*
- * Basic register read/modify/write, with un-needed acesses elided. That is,
+ * Basic register read/modify/write, with un-needed accesses elided. That is,
* a mask of zero will prevent write, while a mask of 0xFF will prevent read.
* returns current (presumed, if a write was done) contents of selected
* register, or <0 if errors.
@@ -989,7 +989,7 @@ static struct rxeq_init {
/* Set DFELTHFDR/HDR thresholds */
RXEQ_VAL(7, 8, 0, 0, 0, 0), /* FDR */
RXEQ_VAL(7, 0x21, 0, 0, 0, 0), /* HDR */
- /* Set TLTHFDR/HDR theshold */
+ /* Set TLTHFDR/HDR threshold */
RXEQ_VAL(7, 9, 2, 2, 2, 2), /* FDR */
RXEQ_VAL(7, 0x23, 2, 2, 2, 2), /* HDR */
/* Set Preamp setting 2 (ZFR/ZCNT) */
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 219b10397b4..256a00c6aee 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -352,7 +352,7 @@ static int set_kernel_sq_size(struct mlx4_ib_dev *dev, struct ib_qp_cap *cap,
* anymore, so we do this only if selective signaling is off.
*
* Further, on 32-bit platforms, we can't use vmap() to make
- * the QP buffer virtually contigious. Thus we have to use
+ * the QP buffer virtually contiguous. Thus we have to use
* constant-sized WRs to make sure a WR is always fully within
* a single page-sized chunk.
*
diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c
index e593af3354b..de18fdfdadf 100644
--- a/drivers/infiniband/hw/nes/nes_nic.c
+++ b/drivers/infiniband/hw/nes/nes_nic.c
@@ -1080,11 +1080,14 @@ static int nes_netdev_set_rx_csum(struct net_device *netdev, u32 enable)
/**
- * nes_netdev_get_stats_count
+ * nes_netdev_get_sset_count
*/
-static int nes_netdev_get_stats_count(struct net_device *netdev)
+static int nes_netdev_get_sset_count(struct net_device *netdev, int stringset)
{
- return NES_ETHTOOL_STAT_COUNT;
+ if (stringset == ETH_SS_STATS)
+ return NES_ETHTOOL_STAT_COUNT;
+ else
+ return -EINVAL;
}
@@ -1264,7 +1267,6 @@ static void nes_netdev_get_drvinfo(struct net_device *netdev,
sprintf(drvinfo->fw_version, "%u.%u", nesadapter->firmware_version>>16,
nesadapter->firmware_version & 0x000000ff);
strcpy(drvinfo->version, DRV_VERSION);
- drvinfo->n_stats = nes_netdev_get_stats_count(netdev);
drvinfo->testinfo_len = 0;
drvinfo->eedump_len = 0;
drvinfo->regdump_len = 0;
@@ -1516,7 +1518,7 @@ static const struct ethtool_ops nes_ethtool_ops = {
.get_rx_csum = nes_netdev_get_rx_csum,
.get_sg = ethtool_op_get_sg,
.get_strings = nes_netdev_get_strings,
- .get_stats_count = nes_netdev_get_stats_count,
+ .get_sset_count = nes_netdev_get_sset_count,
.get_ethtool_stats = nes_netdev_get_ethtool_stats,
.get_drvinfo = nes_netdev_get_drvinfo,
.get_coalesce = nes_netdev_get_coalesce,
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index add9188663f..5f7a6fca0a4 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -625,6 +625,7 @@ static struct iscsi_transport iscsi_iser_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS |
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index ea9e1556e0d..8579f32ce38 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -499,7 +499,7 @@ void iser_conn_init(struct iser_conn *ib_conn)
/**
* starts the process of connecting to the target
- * sleeps untill the connection is established or rejected
+ * sleeps until the connection is established or rejected
*/
int iser_connect(struct iser_conn *ib_conn,
struct sockaddr_in *src_addr,
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index cd50c00ab20..07c2cd43109 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -8,7 +8,7 @@ menu "Input device support"
config INPUT
tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED
default y
- ---help---
+ help
Say Y here if you have any input device (mouse, keyboard, tablet,
joystick, steering wheel ...) connected to your system and want
it to be available to applications. This includes standard PS/2
@@ -27,8 +27,7 @@ if INPUT
config INPUT_FF_MEMLESS
tristate "Support for memoryless force-feedback devices"
- default n
- ---help---
+ help
Say Y here if you have memoryless force-feedback input device
such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual
Power 2, or similar. You will also need to enable hardware-specific
@@ -52,12 +51,25 @@ config INPUT_POLLDEV
To compile this driver as a module, choose M here: the
module will be called input-polldev.
+config INPUT_SPARSEKMAP
+ tristate "Sparse keymap support library"
+ help
+ Say Y here if you are using a driver for an input
+ device that uses sparse keymap. This option is only
+ useful for out-of-tree drivers since in-tree drivers
+ select it automatically.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sparse-keymap.
+
comment "Userland interfaces"
config INPUT_MOUSEDEV
tristate "Mouse interface" if EMBEDDED
default y
- ---help---
+ help
Say Y here if you want your mouse to be accessible as char devices
13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an
emulated IntelliMouse Explorer PS/2 mouse. That way, all user space
@@ -73,7 +85,7 @@ config INPUT_MOUSEDEV_PSAUX
bool "Provide legacy /dev/psaux device"
default y
depends on INPUT_MOUSEDEV
- ---help---
+ help
Say Y here if you want your mouse also be accessible as char device
10:1 - /dev/psaux. The data available through /dev/psaux is exactly
the same as the data from /dev/input/mice.
@@ -103,7 +115,7 @@ config INPUT_MOUSEDEV_SCREEN_Y
config INPUT_JOYDEV
tristate "Joystick interface"
- ---help---
+ help
Say Y here if you want your joystick or gamepad to be
accessible as char device 13:0+ - /dev/input/jsX device.
@@ -125,7 +137,7 @@ config INPUT_EVDEV
config INPUT_EVBUG
tristate "Event debugging"
- ---help---
+ help
Say Y here if you have a problem with the input subsystem and
want all events (keypresses, mouse movements), to be output to
the system log. While this is useful for debugging, it's also
@@ -140,7 +152,7 @@ config INPUT_EVBUG
config INPUT_APMPOWER
tristate "Input Power Event -> APM Bridge" if EMBEDDED
depends on INPUT && APM_EMULATION
- ---help---
+ help
Say Y here if you want suspend key events to trigger a user
requested suspend through APM. This is useful on embedded
systems where such behaviour is desired without userspace
@@ -153,6 +165,7 @@ config XEN_KBDDEV_FRONTEND
tristate "Xen virtual keyboard and mouse support"
depends on XEN_FBDEV_FRONTEND
default y
+ select XEN_XENBUS_FRONTEND
help
This driver implements the front-end of the Xen virtual
keyboard and mouse device driver. It communicates with a back-end
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 4c9c745a702..7ad212d31f9 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -9,6 +9,7 @@ input-core-objs := input.o input-compat.o ff-core.o
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
+obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 38df81fcdc3..b2f07aa1604 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -353,7 +353,7 @@ int input_ff_create(struct input_dev *dev, int max_effects)
EXPORT_SYMBOL_GPL(input_ff_create);
/**
- * input_ff_free() - frees force feedback portion of input device
+ * input_ff_destroy() - frees force feedback portion of input device
* @dev: input device supporting force feedback
*
* This function is only needed in error path as input core will
@@ -369,6 +369,7 @@ void input_ff_destroy(struct input_dev *dev)
if (ff->destroy)
ff->destroy(ff);
kfree(ff->private);
+ kfree(ff->effects);
kfree(ff);
dev->ff = NULL;
}
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index 0d3ce7a50fb..aa6713b4a98 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -56,14 +56,10 @@ static void input_polldev_stop_workqueue(void)
mutex_unlock(&polldev_mutex);
}
-static void input_polled_device_work(struct work_struct *work)
+static void input_polldev_queue_work(struct input_polled_dev *dev)
{
- struct input_polled_dev *dev =
- container_of(work, struct input_polled_dev, work.work);
unsigned long delay;
- dev->poll(dev);
-
delay = msecs_to_jiffies(dev->poll_interval);
if (delay >= HZ)
delay = round_jiffies_relative(delay);
@@ -71,6 +67,15 @@ static void input_polled_device_work(struct work_struct *work)
queue_delayed_work(polldev_wq, &dev->work, delay);
}
+static void input_polled_device_work(struct work_struct *work)
+{
+ struct input_polled_dev *dev =
+ container_of(work, struct input_polled_dev, work.work);
+
+ dev->poll(dev);
+ input_polldev_queue_work(dev);
+}
+
static int input_open_polled_device(struct input_dev *input)
{
struct input_polled_dev *dev = input_get_drvdata(input);
@@ -80,11 +85,12 @@ static int input_open_polled_device(struct input_dev *input)
if (error)
return error;
- if (dev->flush)
- dev->flush(dev);
+ if (dev->open)
+ dev->open(dev);
- queue_delayed_work(polldev_wq, &dev->work,
- msecs_to_jiffies(dev->poll_interval));
+ /* Only start polling if polling is enabled */
+ if (dev->poll_interval > 0)
+ queue_delayed_work(polldev_wq, &dev->work, 0);
return 0;
}
@@ -95,8 +101,88 @@ static void input_close_polled_device(struct input_dev *input)
cancel_delayed_work_sync(&dev->work);
input_polldev_stop_workqueue();
+
+ if (dev->close)
+ dev->close(dev);
}
+/* SYSFS interface */
+
+static ssize_t input_polldev_get_poll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", polldev->poll_interval);
+}
+
+static ssize_t input_polldev_set_poll(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct input_polled_dev *polldev = dev_get_drvdata(dev);
+ struct input_dev *input = polldev->input;
+ unsigned long interval;
+
+ if (strict_strtoul(buf, 0, &interval))
+ return -EINVAL;
+
+ if (interval < polldev->poll_interval_min)
+ return -EINVAL;
+
+ if (interval > polldev->poll_interval_max)
+ return -EINVAL;
+
+ mutex_lock(&input->mutex);
+
+ polldev->poll_interval = interval;
+
+ if (input->users) {
+ cancel_delayed_work_sync(&polldev->work);
+ if (polldev->poll_interval > 0)
+ input_polldev_queue_work(polldev);
+ }
+
+ mutex_unlock(&input->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll,
+ input_polldev_set_poll);
+
+
+static ssize_t input_polldev_get_max(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", polldev->poll_interval_max);
+}
+
+static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL);
+
+static ssize_t input_polldev_get_min(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", polldev->poll_interval_min);
+}
+
+static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL);
+
+static struct attribute *sysfs_attrs[] = {
+ &dev_attr_poll.attr,
+ &dev_attr_max.attr,
+ &dev_attr_min.attr,
+ NULL
+};
+
+static struct attribute_group input_polldev_attribute_group = {
+ .attrs = sysfs_attrs
+};
+
/**
* input_allocate_polled_device - allocated memory polled device
*
@@ -126,7 +212,7 @@ EXPORT_SYMBOL(input_allocate_polled_device);
* @dev: device to free
*
* The function frees memory allocated for polling device and drops
- * reference to the associated input device (if present).
+ * reference to the associated input device.
*/
void input_free_polled_device(struct input_polled_dev *dev)
{
@@ -150,15 +236,38 @@ EXPORT_SYMBOL(input_free_polled_device);
int input_register_polled_device(struct input_polled_dev *dev)
{
struct input_dev *input = dev->input;
+ int error;
input_set_drvdata(input, dev);
INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
if (!dev->poll_interval)
dev->poll_interval = 500;
+ if (!dev->poll_interval_max)
+ dev->poll_interval_max = dev->poll_interval;
input->open = input_open_polled_device;
input->close = input_close_polled_device;
- return input_register_device(input);
+ error = input_register_device(input);
+ if (error)
+ return error;
+
+ error = sysfs_create_group(&input->dev.kobj,
+ &input_polldev_attribute_group);
+ if (error) {
+ input_unregister_device(input);
+ return error;
+ }
+
+ /*
+ * Take extra reference to the underlying input device so
+ * that it survives call to input_unregister_polled_device()
+ * and is deleted only after input_free_polled_device()
+ * has been invoked. This is needed to ease task of freeing
+ * sparse keymaps.
+ */
+ input_get_device(input);
+
+ return 0;
}
EXPORT_SYMBOL(input_register_polled_device);
@@ -169,13 +278,13 @@ EXPORT_SYMBOL(input_register_polled_device);
* The function unregisters previously registered polled input
* device from input layer. Polling is stopped and device is
* ready to be freed with call to input_free_polled_device().
- * Callers should not attempt to access dev->input pointer
- * after calling this function.
*/
void input_unregister_polled_device(struct input_polled_dev *dev)
{
+ sysfs_remove_group(&dev->input->dev.kobj,
+ &input_polldev_attribute_group);
+
input_unregister_device(dev->input);
- dev->input = NULL;
}
EXPORT_SYMBOL(input_unregister_polled_device);
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 2266ecbfbc0..5c16001959c 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1658,6 +1658,38 @@ void input_unregister_handler(struct input_handler *handler)
EXPORT_SYMBOL(input_unregister_handler);
/**
+ * input_handler_for_each_handle - handle iterator
+ * @handler: input handler to iterate
+ * @data: data for the callback
+ * @fn: function to be called for each handle
+ *
+ * Iterate over @bus's list of devices, and call @fn for each, passing
+ * it @data and stop when @fn returns a non-zero value. The function is
+ * using RCU to traverse the list and therefore may be usind in atonic
+ * contexts. The @fn callback is invoked from RCU critical section and
+ * thus must not sleep.
+ */
+int input_handler_for_each_handle(struct input_handler *handler, void *data,
+ int (*fn)(struct input_handle *, void *))
+{
+ struct input_handle *handle;
+ int retval = 0;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(handle, &handler->h_list, h_node) {
+ retval = fn(handle, data);
+ if (retval)
+ break;
+ }
+
+ rcu_read_unlock();
+
+ return retval;
+}
+EXPORT_SYMBOL(input_handler_for_each_handle);
+
+/**
* input_register_handle - register a new input handle
* @handle: handle to register
*
@@ -1690,7 +1722,7 @@ int input_register_handle(struct input_handle *handle)
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
- list_add_tail(&handle->h_node, &handler->h_list);
+ list_add_tail_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
@@ -1713,7 +1745,7 @@ void input_unregister_handle(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
- list_del_init(&handle->h_node);
+ list_del_rcu(&handle->h_node);
/*
* Take dev->mutex to prevent race with input_release_device().
@@ -1721,6 +1753,7 @@ void input_unregister_handle(struct input_handle *handle)
mutex_lock(&dev->mutex);
list_del_rcu(&handle->d_node);
mutex_unlock(&dev->mutex);
+
synchronize_rcu();
}
EXPORT_SYMBOL(input_unregister_handle);
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 79e3edcced1..482cb1204e4 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -125,6 +125,7 @@ static const struct xpad_device {
{ 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
{ 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
+ { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
{ 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
{ 0x0c12, 0x880a, "Pelican Eclipse PL-2023", MAP_DPAD_TO_AXES, XTYPE_XBOX },
@@ -146,6 +147,7 @@ static const struct xpad_device {
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
{ 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN }
};
@@ -212,6 +214,7 @@ static struct usb_device_id xpad_table [] = {
XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */
XPAD_XBOX360_VENDOR(0x1bad), /* Rock Band Drums */
+ XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */
{ }
};
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index ee98b1bc5d8..203b88a82b5 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -361,6 +361,16 @@ config KEYBOARD_SH_KEYSC
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
+config KEYBOARD_DAVINCI
+ tristate "TI DaVinci Key Scan"
+ depends on ARCH_DAVINCI_DM365
+ help
+ Say Y to enable keypad module support for the TI DaVinci
+ platforms (DM365).
+
+ To compile this driver as a module, choose M here: the
+ module will be called davinci_keyscan.
+
config KEYBOARD_OMAP
tristate "TI OMAP keypad support"
depends on (ARCH_OMAP1 || ARCH_OMAP2)
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index babad5e58b7..68c017235ce 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 28e6110d1ff..a3573570c52 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -1567,9 +1567,8 @@ static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id)
return 0;
}
-static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
+static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
{
- .ident = "Dell Laptop",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
@@ -1578,7 +1577,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_dell_laptop_forced_release_keys,
},
{
- .ident = "Dell Laptop",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
@@ -1587,7 +1585,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_dell_laptop_forced_release_keys,
},
{
- .ident = "HP 2133",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"),
@@ -1596,7 +1593,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_hp_forced_release_keys,
},
{
- .ident = "HP Pavilion ZV6100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"),
@@ -1605,7 +1601,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_volume_forced_release_keys,
},
{
- .ident = "HP Presario R4000",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"),
@@ -1614,7 +1609,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_volume_forced_release_keys,
},
{
- .ident = "HP Presario R4100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"),
@@ -1623,7 +1617,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_volume_forced_release_keys,
},
{
- .ident = "HP Presario R4200",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"),
@@ -1632,7 +1625,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_volume_forced_release_keys,
},
{
- .ident = "Inventec Symphony",
+ /* Inventec Symphony */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"),
DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"),
@@ -1641,7 +1634,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_volume_forced_release_keys,
},
{
- .ident = "Samsung NC10",
+ /* Samsung NC10 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
@@ -1650,7 +1643,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_samsung_forced_release_keys,
},
{
- .ident = "Samsung NC20",
+ /* Samsung NC20 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "NC20"),
@@ -1659,7 +1652,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_samsung_forced_release_keys,
},
{
- .ident = "Samsung SQ45S70S",
+ /* Samsung SQ45S70S */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
@@ -1668,7 +1661,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_samsung_forced_release_keys,
},
{
- .ident = "Fujitsu Amilo PA 1510",
+ /* Fujitsu Amilo PA 1510 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"),
@@ -1677,7 +1670,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_volume_forced_release_keys,
},
{
- .ident = "Fujitsu Amilo Pi 3525",
+ /* Fujitsu Amilo Pi 3525 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"),
@@ -1686,7 +1679,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_amilo_pi3525_forced_release_keys,
},
{
- .ident = "Fujitsu Amilo Xi 3650",
+ /* Fujitsu Amilo Xi 3650 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"),
@@ -1695,7 +1688,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkbd_amilo_xi3650_forced_release_keys,
},
{
- .ident = "Soltech Corporation TA12",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "TA12"),
@@ -1704,7 +1696,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
.driver_data = atkdb_soltech_ta12_forced_release_keys,
},
{
- .ident = "OQO Model 01+",
+ /* OQO Model 01+ */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c
new file mode 100644
index 00000000000..6e52d855f63
--- /dev/null
+++ b/drivers/input/keyboard/davinci_keyscan.c
@@ -0,0 +1,337 @@
+/*
+ * DaVinci Key Scan Driver for TI platforms
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * Intial Code: Sandeep Paulraj <s-paulraj@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/keyscan.h>
+
+/* Key scan registers */
+#define DAVINCI_KEYSCAN_KEYCTRL 0x0000
+#define DAVINCI_KEYSCAN_INTENA 0x0004
+#define DAVINCI_KEYSCAN_INTFLAG 0x0008
+#define DAVINCI_KEYSCAN_INTCLR 0x000c
+#define DAVINCI_KEYSCAN_STRBWIDTH 0x0010
+#define DAVINCI_KEYSCAN_INTERVAL 0x0014
+#define DAVINCI_KEYSCAN_CONTTIME 0x0018
+#define DAVINCI_KEYSCAN_CURRENTST 0x001c
+#define DAVINCI_KEYSCAN_PREVSTATE 0x0020
+#define DAVINCI_KEYSCAN_EMUCTRL 0x0024
+#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c
+
+/* Key Control Register (KEYCTRL) */
+#define DAVINCI_KEYSCAN_KEYEN 0x00000001
+#define DAVINCI_KEYSCAN_PREVMODE 0x00000002
+#define DAVINCI_KEYSCAN_CHATOFF 0x00000004
+#define DAVINCI_KEYSCAN_AUTODET 0x00000008
+#define DAVINCI_KEYSCAN_SCANMODE 0x00000010
+#define DAVINCI_KEYSCAN_OUTTYPE 0x00000020
+
+/* Masks for the interrupts */
+#define DAVINCI_KEYSCAN_INT_CONT 0x00000008
+#define DAVINCI_KEYSCAN_INT_OFF 0x00000004
+#define DAVINCI_KEYSCAN_INT_ON 0x00000002
+#define DAVINCI_KEYSCAN_INT_CHANGE 0x00000001
+#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f
+
+struct davinci_ks {
+ struct input_dev *input;
+ struct davinci_ks_platform_data *pdata;
+ int irq;
+ void __iomem *base;
+ resource_size_t pbase;
+ size_t base_size;
+ unsigned short keymap[];
+};
+
+/* Initializing the kp Module */
+static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks)
+{
+ struct device *dev = &davinci_ks->input->dev;
+ struct davinci_ks_platform_data *pdata = davinci_ks->pdata;
+ u32 matrix_ctrl;
+
+ /* Enable all interrupts */
+ __raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+ davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+ /* Clear interrupts if any */
+ __raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+ davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
+
+ /* Setup the scan period = strobe + interval */
+ __raw_writel(pdata->strobe,
+ davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH);
+ __raw_writel(pdata->interval,
+ davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL);
+ __raw_writel(0x01,
+ davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME);
+
+ /* Define matrix type */
+ switch (pdata->matrix_type) {
+ case DAVINCI_KEYSCAN_MATRIX_4X4:
+ matrix_ctrl = 0;
+ break;
+ case DAVINCI_KEYSCAN_MATRIX_5X3:
+ matrix_ctrl = (1 << 6);
+ break;
+ default:
+ dev_err(dev->parent, "wrong matrix type\n");
+ return -EINVAL;
+ }
+
+ /* Enable key scan module and set matrix type */
+ __raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN |
+ matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL);
+
+ return 0;
+}
+
+static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id)
+{
+ struct davinci_ks *davinci_ks = dev_id;
+ struct device *dev = &davinci_ks->input->dev;
+ unsigned short *keymap = davinci_ks->keymap;
+ int keymapsize = davinci_ks->pdata->keymapsize;
+ u32 prev_status, new_status, changed;
+ bool release;
+ int keycode = KEY_UNKNOWN;
+ int i;
+
+ /* Disable interrupt */
+ __raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+ /* Reading previous and new status of the key scan */
+ prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE);
+ new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST);
+
+ changed = prev_status ^ new_status;
+
+ if (changed) {
+ /*
+ * It goes through all bits in 'changed' to ensure
+ * that no key changes are being missed
+ */
+ for (i = 0 ; i < keymapsize; i++) {
+ if ((changed>>i) & 0x1) {
+ keycode = keymap[i];
+ release = (new_status >> i) & 0x1;
+ dev_dbg(dev->parent, "key %d %s\n", keycode,
+ release ? "released" : "pressed");
+ input_report_key(davinci_ks->input, keycode,
+ !release);
+ input_sync(davinci_ks->input);
+ }
+ }
+ /* Clearing interrupt */
+ __raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+ davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
+ }
+
+ /* Enable interrupts */
+ __raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+ return IRQ_HANDLED;
+}
+
+static int __init davinci_ks_probe(struct platform_device *pdev)
+{
+ struct davinci_ks *davinci_ks;
+ struct input_dev *key_dev;
+ struct resource *res, *mem;
+ struct device *dev = &pdev->dev;
+ struct davinci_ks_platform_data *pdata = pdev->dev.platform_data;
+ int error, i;
+
+ if (!pdata->keymap) {
+ dev_dbg(dev, "no keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ davinci_ks = kzalloc(sizeof(struct davinci_ks) +
+ sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL);
+ if (!davinci_ks) {
+ dev_dbg(dev, "could not allocate memory for private data\n");
+ return -ENOMEM;
+ }
+
+ memcpy(davinci_ks->keymap, pdata->keymap,
+ sizeof(unsigned short) * pdata->keymapsize);
+
+ key_dev = input_allocate_device();
+ if (!key_dev) {
+ dev_dbg(dev, "could not allocate input device\n");
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ davinci_ks->input = key_dev;
+
+ davinci_ks->irq = platform_get_irq(pdev, 0);
+ if (davinci_ks->irq < 0) {
+ dev_err(dev, "no key scan irq\n");
+ error = davinci_ks->irq;
+ goto fail2;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no mem resource\n");
+ error = -EINVAL;
+ goto fail2;
+ }
+
+ davinci_ks->pbase = res->start;
+ davinci_ks->base_size = resource_size(res);
+
+ mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size,
+ pdev->name);
+ if (!mem) {
+ dev_err(dev, "key scan registers at %08x are not free\n",
+ davinci_ks->pbase);
+ error = -EBUSY;
+ goto fail2;
+ }
+
+ davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size);
+ if (!davinci_ks->base) {
+ dev_err(dev, "can't ioremap MEM resource.\n");
+ error = -ENOMEM;
+ goto fail3;
+ }
+
+ /* Enable auto repeat feature of Linux input subsystem */
+ if (pdata->rep)
+ __set_bit(EV_REP, key_dev->evbit);
+
+ /* Setup input device */
+ __set_bit(EV_KEY, key_dev->evbit);
+
+ /* Setup the platform data */
+ davinci_ks->pdata = pdata;
+
+ for (i = 0; i < davinci_ks->pdata->keymapsize; i++)
+ __set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit);
+
+ key_dev->name = "davinci_keyscan";
+ key_dev->phys = "davinci_keyscan/input0";
+ key_dev->dev.parent = &pdev->dev;
+ key_dev->id.bustype = BUS_HOST;
+ key_dev->id.vendor = 0x0001;
+ key_dev->id.product = 0x0001;
+ key_dev->id.version = 0x0001;
+ key_dev->keycode = davinci_ks->keymap;
+ key_dev->keycodesize = sizeof(davinci_ks->keymap[0]);
+ key_dev->keycodemax = davinci_ks->pdata->keymapsize;
+
+ error = input_register_device(davinci_ks->input);
+ if (error < 0) {
+ dev_err(dev, "unable to register davinci key scan device\n");
+ goto fail4;
+ }
+
+ error = request_irq(davinci_ks->irq, davinci_ks_interrupt,
+ IRQF_DISABLED, pdev->name, davinci_ks);
+ if (error < 0) {
+ dev_err(dev, "unable to register davinci key scan interrupt\n");
+ goto fail5;
+ }
+
+ error = davinci_ks_initialize(davinci_ks);
+ if (error < 0) {
+ dev_err(dev, "unable to initialize davinci key scan device\n");
+ goto fail6;
+ }
+
+ platform_set_drvdata(pdev, davinci_ks);
+ return 0;
+
+fail6:
+ free_irq(davinci_ks->irq, davinci_ks);
+fail5:
+ input_unregister_device(davinci_ks->input);
+ key_dev = NULL;
+fail4:
+ iounmap(davinci_ks->base);
+fail3:
+ release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
+fail2:
+ input_free_device(key_dev);
+fail1:
+ kfree(davinci_ks);
+
+ return error;
+}
+
+static int __devexit davinci_ks_remove(struct platform_device *pdev)
+{
+ struct davinci_ks *davinci_ks = platform_get_drvdata(pdev);
+
+ free_irq(davinci_ks->irq, davinci_ks);
+
+ input_unregister_device(davinci_ks->input);
+
+ iounmap(davinci_ks->base);
+ release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(davinci_ks);
+
+ return 0;
+}
+
+static struct platform_driver davinci_ks_driver = {
+ .driver = {
+ .name = "davinci_keyscan",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(davinci_ks_remove),
+};
+
+static int __init davinci_ks_init(void)
+{
+ return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe);
+}
+module_init(davinci_ks_init);
+
+static void __exit davinci_ks_exit(void)
+{
+ platform_driver_unregister(&davinci_ks_driver);
+}
+module_exit(davinci_ks_exit);
+
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 77d13091425..1aff3b76eff 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -23,8 +23,7 @@
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
-
-#include <asm/gpio.h>
+#include <linux/gpio.h>
struct gpio_button_data {
struct gpio_keys_button *button;
@@ -38,10 +37,8 @@ struct gpio_keys_drvdata {
struct gpio_button_data data[0];
};
-static void gpio_keys_report_event(struct work_struct *work)
+static void gpio_keys_report_event(struct gpio_button_data *bdata)
{
- struct gpio_button_data *bdata =
- container_of(work, struct gpio_button_data, work);
struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
@@ -51,6 +48,14 @@ static void gpio_keys_report_event(struct work_struct *work)
input_sync(input);
}
+static void gpio_keys_work_func(struct work_struct *work)
+{
+ struct gpio_button_data *bdata =
+ container_of(work, struct gpio_button_data, work);
+
+ gpio_keys_report_event(bdata);
+}
+
static void gpio_keys_timer(unsigned long _data)
{
struct gpio_button_data *data = (struct gpio_button_data *)_data;
@@ -74,10 +79,62 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int __devinit gpio_keys_setup_key(struct device *dev,
+ struct gpio_button_data *bdata,
+ struct gpio_keys_button *button)
+{
+ char *desc = button->desc ? button->desc : "gpio_keys";
+ int irq, error;
+
+ setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
+ INIT_WORK(&bdata->work, gpio_keys_work_func);
+
+ error = gpio_request(button->gpio, desc);
+ if (error < 0) {
+ dev_err(dev, "failed to request GPIO %d, error %d\n",
+ button->gpio, error);
+ goto fail2;
+ }
+
+ error = gpio_direction_input(button->gpio);
+ if (error < 0) {
+ dev_err(dev, "failed to configure"
+ " direction for GPIO %d, error %d\n",
+ button->gpio, error);
+ goto fail3;
+ }
+
+ irq = gpio_to_irq(button->gpio);
+ if (irq < 0) {
+ error = irq;
+ dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
+ button->gpio, error);
+ goto fail3;
+ }
+
+ error = request_irq(irq, gpio_keys_isr,
+ IRQF_SHARED |
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ desc, bdata);
+ if (error) {
+ dev_err(dev, "Unable to claim irq %d; error %d\n",
+ irq, error);
+ goto fail3;
+ }
+
+ return 0;
+
+fail3:
+ gpio_free(button->gpio);
+fail2:
+ return error;
+}
+
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct gpio_keys_drvdata *ddata;
+ struct device *dev = &pdev->dev;
struct input_dev *input;
int i, error;
int wakeup = 0;
@@ -87,6 +144,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
GFP_KERNEL);
input = input_allocate_device();
if (!ddata || !input) {
+ dev_err(dev, "failed to allocate state\n");
error = -ENOMEM;
goto fail1;
}
@@ -111,52 +169,14 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
struct gpio_button_data *bdata = &ddata->data[i];
- int irq;
unsigned int type = button->type ?: EV_KEY;
bdata->input = input;
bdata->button = button;
- setup_timer(&bdata->timer,
- gpio_keys_timer, (unsigned long)bdata);
- INIT_WORK(&bdata->work, gpio_keys_report_event);
-
- error = gpio_request(button->gpio, button->desc ?: "gpio_keys");
- if (error < 0) {
- pr_err("gpio-keys: failed to request GPIO %d,"
- " error %d\n", button->gpio, error);
- goto fail2;
- }
-
- error = gpio_direction_input(button->gpio);
- if (error < 0) {
- pr_err("gpio-keys: failed to configure input"
- " direction for GPIO %d, error %d\n",
- button->gpio, error);
- gpio_free(button->gpio);
- goto fail2;
- }
-
- irq = gpio_to_irq(button->gpio);
- if (irq < 0) {
- error = irq;
- pr_err("gpio-keys: Unable to get irq number"
- " for GPIO %d, error %d\n",
- button->gpio, error);
- gpio_free(button->gpio);
- goto fail2;
- }
- error = request_irq(irq, gpio_keys_isr,
- IRQF_SHARED |
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- button->desc ? button->desc : "gpio_keys",
- bdata);
- if (error) {
- pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
- irq, error);
- gpio_free(button->gpio);
+ error = gpio_keys_setup_key(dev, bdata, button);
+ if (error)
goto fail2;
- }
if (button->wakeup)
wakeup = 1;
@@ -166,11 +186,16 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
error = input_register_device(input);
if (error) {
- pr_err("gpio-keys: Unable to register input device, "
+ dev_err(dev, "Unable to register input device, "
"error: %d\n", error);
goto fail2;
}
+ /* get current state of buttons */
+ for (i = 0; i < pdata->nbuttons; i++)
+ gpio_keys_report_event(&ddata->data[i]);
+ input_sync(input);
+
device_init_wakeup(&pdev->dev, wakeup);
return 0;
@@ -239,18 +264,21 @@ static int gpio_keys_suspend(struct device *dev)
static int gpio_keys_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
int i;
- if (device_may_wakeup(&pdev->dev)) {
- for (i = 0; i < pdata->nbuttons; i++) {
- struct gpio_keys_button *button = &pdata->buttons[i];
- if (button->wakeup) {
- int irq = gpio_to_irq(button->gpio);
- disable_irq_wake(irq);
- }
+ for (i = 0; i < pdata->nbuttons; i++) {
+
+ struct gpio_keys_button *button = &pdata->buttons[i];
+ if (button->wakeup && device_may_wakeup(&pdev->dev)) {
+ int irq = gpio_to_irq(button->gpio);
+ disable_irq_wake(irq);
}
+
+ gpio_keys_report_event(&ddata->data[i]);
}
+ input_sync(ddata->input);
return 0;
}
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
index f9847e0fb55..fa9bb6d235e 100644
--- a/drivers/input/keyboard/lkkbd.c
+++ b/drivers/input/keyboard/lkkbd.c
@@ -72,9 +72,9 @@
#define DRIVER_DESC "LK keyboard driver"
-MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
/*
* Known parameters:
@@ -85,27 +85,27 @@ MODULE_LICENSE ("GPL");
* Please notice that there's not yet an API to set these at runtime.
*/
static int bell_volume = 100; /* % */
-module_param (bell_volume, int, 0);
-MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%");
+module_param(bell_volume, int, 0);
+MODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%");
static int keyclick_volume = 100; /* % */
-module_param (keyclick_volume, int, 0);
-MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%");
+module_param(keyclick_volume, int, 0);
+MODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%");
static int ctrlclick_volume = 100; /* % */
-module_param (ctrlclick_volume, int, 0);
-MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
+module_param(ctrlclick_volume, int, 0);
+MODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
static int lk201_compose_is_alt;
-module_param (lk201_compose_is_alt, int, 0);
-MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
- "will act as an Alt key");
+module_param(lk201_compose_is_alt, int, 0);
+MODULE_PARM_DESC(lk201_compose_is_alt,
+ "If set non-zero, LK201' Compose key will act as an Alt key");
#undef LKKBD_DEBUG
#ifdef LKKBD_DEBUG
-#define DBG(x...) printk (x)
+#define DBG(x...) printk(x)
#else
#define DBG(x...) do {} while (0)
#endif
@@ -122,7 +122,7 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
#define LK_MODE_DOWN 0x80
#define LK_MODE_AUTODOWN 0x82
#define LK_MODE_UPDOWN 0x86
-#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3))
+#define LK_CMD_SET_MODE(mode, div) ((mode) | ((div) << 3))
/* Misc commands */
#define LK_CMD_ENABLE_KEYCLICK 0x1b
@@ -152,11 +152,8 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
#define LK_NUM_KEYCODES 256
#define LK_NUM_IGNORE_BYTES 6
-typedef u_int16_t lk_keycode_t;
-
-
-static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
+static unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = {
[0x56] = KEY_F1,
[0x57] = KEY_F2,
[0x58] = KEY_F3,
@@ -268,7 +265,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
};
#define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do { \
- if (test_bit (LED, (LK)->dev->led)) \
+ if (test_bit(LED, (LK)->dev->led)) \
VAR_ON |= BITS; \
else \
VAR_OFF |= BITS; \
@@ -278,7 +275,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
* Per-keyboard data
*/
struct lkkbd {
- lk_keycode_t keycode[LK_NUM_KEYCODES];
+ unsigned short keycode[LK_NUM_KEYCODES];
int ignore_bytes;
unsigned char id[LK_NUM_IGNORE_BYTES];
struct input_dev *dev;
@@ -301,26 +298,25 @@ static struct {
unsigned char *name;
} lk_response[] = {
#define RESPONSE(x) { .value = (x), .name = #x, }
- RESPONSE (LK_STUCK_KEY),
- RESPONSE (LK_SELFTEST_FAILED),
- RESPONSE (LK_ALL_KEYS_UP),
- RESPONSE (LK_METRONOME),
- RESPONSE (LK_OUTPUT_ERROR),
- RESPONSE (LK_INPUT_ERROR),
- RESPONSE (LK_KBD_LOCKED),
- RESPONSE (LK_KBD_TEST_MODE_ACK),
- RESPONSE (LK_PREFIX_KEY_DOWN),
- RESPONSE (LK_MODE_CHANGE_ACK),
- RESPONSE (LK_RESPONSE_RESERVED),
+ RESPONSE(LK_STUCK_KEY),
+ RESPONSE(LK_SELFTEST_FAILED),
+ RESPONSE(LK_ALL_KEYS_UP),
+ RESPONSE(LK_METRONOME),
+ RESPONSE(LK_OUTPUT_ERROR),
+ RESPONSE(LK_INPUT_ERROR),
+ RESPONSE(LK_KBD_LOCKED),
+ RESPONSE(LK_KBD_TEST_MODE_ACK),
+ RESPONSE(LK_PREFIX_KEY_DOWN),
+ RESPONSE(LK_MODE_CHANGE_ACK),
+ RESPONSE(LK_RESPONSE_RESERVED),
#undef RESPONSE
};
-static unsigned char *
-response_name (unsigned char value)
+static unsigned char *response_name(unsigned char value)
{
int i;
- for (i = 0; i < ARRAY_SIZE (lk_response); i++)
+ for (i = 0; i < ARRAY_SIZE(lk_response); i++)
if (lk_response[i].value == value)
return lk_response[i].name;
@@ -331,8 +327,7 @@ response_name (unsigned char value)
/*
* Calculate volume parameter byte for a given volume.
*/
-static unsigned char
-volume_to_hw (int volume_percent)
+static unsigned char volume_to_hw(int volume_percent)
{
unsigned char ret = 0;
@@ -363,8 +358,7 @@ volume_to_hw (int volume_percent)
return ret;
}
-static void
-lkkbd_detection_done (struct lkkbd *lk)
+static void lkkbd_detection_done(struct lkkbd *lk)
{
int i;
@@ -377,190 +371,202 @@ lkkbd_detection_done (struct lkkbd *lk)
* Print keyboard name and modify Compose=Alt on user's request.
*/
switch (lk->id[4]) {
- case 1:
- strlcpy (lk->name, "DEC LK201 keyboard",
- sizeof (lk->name));
-
- if (lk201_compose_is_alt)
- lk->keycode[0xb1] = KEY_LEFTALT;
- break;
-
- case 2:
- strlcpy (lk->name, "DEC LK401 keyboard",
- sizeof (lk->name));
- break;
-
- default:
- strlcpy (lk->name, "Unknown DEC keyboard",
- sizeof (lk->name));
- printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
- "please report to Jan-Benedict Glaw "
- "<jbglaw@lug-owl.de>\n", lk->phys);
- printk (KERN_ERR "lkkbd: keyboard ID'ed as:");
- for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
- printk (" 0x%02x", lk->id[i]);
- printk ("\n");
- break;
+ case 1:
+ strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
+
+ if (lk201_compose_is_alt)
+ lk->keycode[0xb1] = KEY_LEFTALT;
+ break;
+
+ case 2:
+ strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
+ break;
+
+ default:
+ strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
+ printk(KERN_ERR
+ "lkkbd: keyboard on %s is unknown, please report to "
+ "Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
+ printk(KERN_ERR "lkkbd: keyboard ID'ed as:");
+ for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
+ printk(" 0x%02x", lk->id[i]);
+ printk("\n");
+ break;
}
- printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
- lk->phys, lk->name);
+
+ printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
+ lk->phys, lk->name);
/*
* Report errors during keyboard boot-up.
*/
switch (lk->id[2]) {
- case 0x00:
- /* All okay */
- break;
-
- case LK_STUCK_KEY:
- printk (KERN_ERR "lkkbd: Stuck key on keyboard at "
- "%s\n", lk->phys);
- break;
-
- case LK_SELFTEST_FAILED:
- printk (KERN_ERR "lkkbd: Selftest failed on keyboard "
- "at %s, keyboard may not work "
- "properly\n", lk->phys);
- break;
-
- default:
- printk (KERN_ERR "lkkbd: Unknown error %02x on "
- "keyboard at %s\n", lk->id[2],
- lk->phys);
- break;
+ case 0x00:
+ /* All okay */
+ break;
+
+ case LK_STUCK_KEY:
+ printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n",
+ lk->phys);
+ break;
+
+ case LK_SELFTEST_FAILED:
+ printk(KERN_ERR
+ "lkkbd: Selftest failed on keyboard at %s, "
+ "keyboard may not work properly\n", lk->phys);
+ break;
+
+ default:
+ printk(KERN_ERR
+ "lkkbd: Unknown error %02x on keyboard at %s\n",
+ lk->id[2], lk->phys);
+ break;
}
/*
* Try to hint user if there's a stuck key.
*/
if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
- printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode "
- "is 0x%04x\n", lk->id[3],
- lk->keycode[lk->id[3]]);
-
- return;
+ printk(KERN_ERR
+ "Scancode of stuck key is 0x%02x, keycode is 0x%04x\n",
+ lk->id[3], lk->keycode[lk->id[3]]);
}
/*
* lkkbd_interrupt() is called by the low level driver when a character
* is received.
*/
-static irqreturn_t
-lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags)
+static irqreturn_t lkkbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
{
- struct lkkbd *lk = serio_get_drvdata (serio);
+ struct lkkbd *lk = serio_get_drvdata(serio);
+ struct input_dev *input_dev = lk->dev;
+ unsigned int keycode;
int i;
- DBG (KERN_INFO "Got byte 0x%02x\n", data);
+ DBG(KERN_INFO "Got byte 0x%02x\n", data);
if (lk->ignore_bytes > 0) {
- DBG (KERN_INFO "Ignoring a byte on %s\n", lk->name);
+ DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name);
lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
if (lk->ignore_bytes == 0)
- lkkbd_detection_done (lk);
+ lkkbd_detection_done(lk);
return IRQ_HANDLED;
}
switch (data) {
- case LK_ALL_KEYS_UP:
- for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++)
- if (lk->keycode[i] != KEY_RESERVED)
- input_report_key (lk->dev, lk->keycode[i], 0);
- input_sync (lk->dev);
- break;
-
- case 0x01:
- DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n");
- lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
- lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
- schedule_work (&lk->tq);
- break;
-
- case LK_METRONOME:
- case LK_OUTPUT_ERROR:
- case LK_INPUT_ERROR:
- case LK_KBD_LOCKED:
- case LK_KBD_TEST_MODE_ACK:
- case LK_PREFIX_KEY_DOWN:
- case LK_MODE_CHANGE_ACK:
- case LK_RESPONSE_RESERVED:
- DBG (KERN_INFO "Got %s and don't know how to handle...\n",
- response_name (data));
- break;
-
- default:
- if (lk->keycode[data] != KEY_RESERVED) {
- if (!test_bit (lk->keycode[data], lk->dev->key))
- input_report_key (lk->dev, lk->keycode[data], 1);
- else
- input_report_key (lk->dev, lk->keycode[data], 0);
- input_sync (lk->dev);
- } else
- printk (KERN_WARNING "%s: Unknown key with "
- "scancode 0x%02x on %s.\n",
- __FILE__, data, lk->name);
+ case LK_ALL_KEYS_UP:
+ for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++)
+ input_report_key(input_dev, lk->keycode[i], 0);
+ input_sync(input_dev);
+ break;
+
+ case 0x01:
+ DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n");
+ lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
+ lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+ schedule_work(&lk->tq);
+ break;
+
+ case LK_METRONOME:
+ case LK_OUTPUT_ERROR:
+ case LK_INPUT_ERROR:
+ case LK_KBD_LOCKED:
+ case LK_KBD_TEST_MODE_ACK:
+ case LK_PREFIX_KEY_DOWN:
+ case LK_MODE_CHANGE_ACK:
+ case LK_RESPONSE_RESERVED:
+ DBG(KERN_INFO "Got %s and don't know how to handle...\n",
+ response_name(data));
+ break;
+
+ default:
+ keycode = lk->keycode[data];
+ if (keycode != KEY_RESERVED) {
+ input_report_key(input_dev, keycode,
+ !test_bit(keycode, input_dev->key));
+ input_sync(input_dev);
+ } else {
+ printk(KERN_WARNING
+ "%s: Unknown key with scancode 0x%02x on %s.\n",
+ __FILE__, data, lk->name);
+ }
}
return IRQ_HANDLED;
}
+static void lkkbd_toggle_leds(struct lkkbd *lk)
+{
+ struct serio *serio = lk->serio;
+ unsigned char leds_on = 0;
+ unsigned char leds_off = 0;
+
+ CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
+ CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
+ CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
+ CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
+ if (leds_on != 0) {
+ serio_write(serio, LK_CMD_LED_ON);
+ serio_write(serio, leds_on);
+ }
+ if (leds_off != 0) {
+ serio_write(serio, LK_CMD_LED_OFF);
+ serio_write(serio, leds_off);
+ }
+}
+
+static void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on)
+{
+ struct serio *serio = lk->serio;
+
+ if (on) {
+ DBG("%s: Activating key clicks\n", __func__);
+ serio_write(serio, LK_CMD_ENABLE_KEYCLICK);
+ serio_write(serio, volume_to_hw(lk->keyclick_volume));
+ serio_write(serio, LK_CMD_ENABLE_CTRCLICK);
+ serio_write(serio, volume_to_hw(lk->ctrlclick_volume));
+ } else {
+ DBG("%s: Deactivating key clicks\n", __func__);
+ serio_write(serio, LK_CMD_DISABLE_KEYCLICK);
+ serio_write(serio, LK_CMD_DISABLE_CTRCLICK);
+ }
+
+}
+
/*
* lkkbd_event() handles events from the input module.
*/
-static int
-lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
- int value)
+static int lkkbd_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
{
- struct lkkbd *lk = input_get_drvdata (dev);
- unsigned char leds_on = 0;
- unsigned char leds_off = 0;
+ struct lkkbd *lk = input_get_drvdata(dev);
switch (type) {
- case EV_LED:
- CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
- CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
- CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
- CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
- if (leds_on != 0) {
- serio_write (lk->serio, LK_CMD_LED_ON);
- serio_write (lk->serio, leds_on);
- }
- if (leds_off != 0) {
- serio_write (lk->serio, LK_CMD_LED_OFF);
- serio_write (lk->serio, leds_off);
- }
+ case EV_LED:
+ lkkbd_toggle_leds(lk);
+ return 0;
+
+ case EV_SND:
+ switch (code) {
+ case SND_CLICK:
+ lkkbd_toggle_keyclick(lk, value);
return 0;
- case EV_SND:
- switch (code) {
- case SND_CLICK:
- if (value == 0) {
- DBG ("%s: Deactivating key clicks\n", __func__);
- serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
- serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
- } else {
- DBG ("%s: Activating key clicks\n", __func__);
- serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
- serio_write (lk->serio, volume_to_hw (lk->keyclick_volume));
- serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
- serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
- }
- return 0;
-
- case SND_BELL:
- if (value != 0)
- serio_write (lk->serio, LK_CMD_SOUND_BELL);
-
- return 0;
- }
- break;
-
- default:
- printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n",
- __func__, type, code, value);
+ case SND_BELL:
+ if (value != 0)
+ serio_write(lk->serio, LK_CMD_SOUND_BELL);
+
+ return 0;
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n",
+ __func__, type, code, value);
}
return -1;
@@ -570,79 +576,56 @@ lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
* lkkbd_reinit() sets leds and beeps to a state the computer remembers they
* were in.
*/
-static void
-lkkbd_reinit (struct work_struct *work)
+static void lkkbd_reinit(struct work_struct *work)
{
struct lkkbd *lk = container_of(work, struct lkkbd, tq);
int division;
- unsigned char leds_on = 0;
- unsigned char leds_off = 0;
/* Ask for ID */
- serio_write (lk->serio, LK_CMD_REQUEST_ID);
+ serio_write(lk->serio, LK_CMD_REQUEST_ID);
/* Reset parameters */
- serio_write (lk->serio, LK_CMD_SET_DEFAULTS);
+ serio_write(lk->serio, LK_CMD_SET_DEFAULTS);
/* Set LEDs */
- CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
- CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
- CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
- CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
- if (leds_on != 0) {
- serio_write (lk->serio, LK_CMD_LED_ON);
- serio_write (lk->serio, leds_on);
- }
- if (leds_off != 0) {
- serio_write (lk->serio, LK_CMD_LED_OFF);
- serio_write (lk->serio, leds_off);
- }
+ lkkbd_toggle_leds(lk);
/*
* Try to activate extended LK401 mode. This command will
* only work with a LK401 keyboard and grants access to
* LAlt, RAlt, RCompose and RShift.
*/
- serio_write (lk->serio, LK_CMD_ENABLE_LK401);
+ serio_write(lk->serio, LK_CMD_ENABLE_LK401);
/* Set all keys to UPDOWN mode */
for (division = 1; division <= 14; division++)
- serio_write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
- division));
+ serio_write(lk->serio,
+ LK_CMD_SET_MODE(LK_MODE_UPDOWN, division));
/* Enable bell and set volume */
- serio_write (lk->serio, LK_CMD_ENABLE_BELL);
- serio_write (lk->serio, volume_to_hw (lk->bell_volume));
+ serio_write(lk->serio, LK_CMD_ENABLE_BELL);
+ serio_write(lk->serio, volume_to_hw(lk->bell_volume));
/* Enable/disable keyclick (and possibly set volume) */
- if (test_bit (SND_CLICK, lk->dev->snd)) {
- serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
- serio_write (lk->serio, volume_to_hw (lk->keyclick_volume));
- serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
- serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
- } else {
- serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
- serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
- }
+ lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd));
/* Sound the bell if needed */
- if (test_bit (SND_BELL, lk->dev->snd))
- serio_write (lk->serio, LK_CMD_SOUND_BELL);
+ if (test_bit(SND_BELL, lk->dev->snd))
+ serio_write(lk->serio, LK_CMD_SOUND_BELL);
}
/*
* lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
*/
-static int
-lkkbd_connect (struct serio *serio, struct serio_driver *drv)
+static int lkkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct lkkbd *lk;
struct input_dev *input_dev;
int i;
int err;
- lk = kzalloc (sizeof (struct lkkbd), GFP_KERNEL);
- input_dev = input_allocate_device ();
+ lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
if (!lk || !input_dev) {
err = -ENOMEM;
goto fail1;
@@ -650,14 +633,14 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv)
lk->serio = serio;
lk->dev = input_dev;
- INIT_WORK (&lk->tq, lkkbd_reinit);
+ INIT_WORK(&lk->tq, lkkbd_reinit);
lk->bell_volume = bell_volume;
lk->keyclick_volume = keyclick_volume;
lk->ctrlclick_volume = ctrlclick_volume;
- memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES);
+ memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
- strlcpy (lk->name, "DEC LK keyboard", sizeof(lk->name));
- snprintf (lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
+ strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
+ snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
input_dev->name = lk->name;
input_dev->phys = lk->phys;
@@ -668,62 +651,61 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv)
input_dev->dev.parent = &serio->dev;
input_dev->event = lkkbd_event;
- input_set_drvdata (input_dev, lk);
+ input_set_drvdata(input_dev, lk);
- set_bit (EV_KEY, input_dev->evbit);
- set_bit (EV_LED, input_dev->evbit);
- set_bit (EV_SND, input_dev->evbit);
- set_bit (EV_REP, input_dev->evbit);
- set_bit (LED_CAPSL, input_dev->ledbit);
- set_bit (LED_SLEEP, input_dev->ledbit);
- set_bit (LED_COMPOSE, input_dev->ledbit);
- set_bit (LED_SCROLLL, input_dev->ledbit);
- set_bit (SND_BELL, input_dev->sndbit);
- set_bit (SND_CLICK, input_dev->sndbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_LED, input_dev->evbit);
+ __set_bit(EV_SND, input_dev->evbit);
+ __set_bit(EV_REP, input_dev->evbit);
+ __set_bit(LED_CAPSL, input_dev->ledbit);
+ __set_bit(LED_SLEEP, input_dev->ledbit);
+ __set_bit(LED_COMPOSE, input_dev->ledbit);
+ __set_bit(LED_SCROLLL, input_dev->ledbit);
+ __set_bit(SND_BELL, input_dev->sndbit);
+ __set_bit(SND_CLICK, input_dev->sndbit);
input_dev->keycode = lk->keycode;
- input_dev->keycodesize = sizeof (lk_keycode_t);
- input_dev->keycodemax = LK_NUM_KEYCODES;
+ input_dev->keycodesize = sizeof(lk->keycode[0]);
+ input_dev->keycodemax = ARRAY_SIZE(lk->keycode);
for (i = 0; i < LK_NUM_KEYCODES; i++)
- __set_bit (lk->keycode[i], input_dev->keybit);
+ __set_bit(lk->keycode[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
- serio_set_drvdata (serio, lk);
+ serio_set_drvdata(serio, lk);
- err = serio_open (serio, drv);
+ err = serio_open(serio, drv);
if (err)
goto fail2;
- err = input_register_device (lk->dev);
+ err = input_register_device(lk->dev);
if (err)
goto fail3;
- serio_write (lk->serio, LK_CMD_POWERCYCLE_RESET);
+ serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET);
return 0;
- fail3: serio_close (serio);
- fail2: serio_set_drvdata (serio, NULL);
- fail1: input_free_device (input_dev);
- kfree (lk);
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(lk);
return err;
}
/*
* lkkbd_disconnect() unregisters and closes behind us.
*/
-static void
-lkkbd_disconnect (struct serio *serio)
+static void lkkbd_disconnect(struct serio *serio)
{
- struct lkkbd *lk = serio_get_drvdata (serio);
-
- input_get_device (lk->dev);
- input_unregister_device (lk->dev);
- serio_close (serio);
- serio_set_drvdata (serio, NULL);
- input_put_device (lk->dev);
- kfree (lk);
+ struct lkkbd *lk = serio_get_drvdata(serio);
+
+ input_get_device(lk->dev);
+ input_unregister_device(lk->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(lk->dev);
+ kfree(lk);
}
static struct serio_device_id lkkbd_serio_ids[] = {
@@ -752,18 +734,16 @@ static struct serio_driver lkkbd_drv = {
/*
* The functions for insering/removing us as a module.
*/
-static int __init
-lkkbd_init (void)
+static int __init lkkbd_init(void)
{
return serio_register_driver(&lkkbd_drv);
}
-static void __exit
-lkkbd_exit (void)
+static void __exit lkkbd_exit(void)
{
serio_unregister_driver(&lkkbd_drv);
}
-module_init (lkkbd_init);
-module_exit (lkkbd_exit);
+module_init(lkkbd_init);
+module_exit(lkkbd_exit);
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 91cfe517026..34f4a29d497 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -213,8 +213,9 @@ static void matrix_keypad_stop(struct input_dev *dev)
}
#ifdef CONFIG_PM
-static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state)
+static int matrix_keypad_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;
@@ -228,8 +229,9 @@ static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t stat
return 0;
}
-static int matrix_keypad_resume(struct platform_device *pdev)
+static int matrix_keypad_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;
@@ -242,9 +244,9 @@ static int matrix_keypad_resume(struct platform_device *pdev)
return 0;
}
-#else
-#define matrix_keypad_suspend NULL
-#define matrix_keypad_resume NULL
+
+static const SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
+ matrix_keypad_suspend, matrix_keypad_resume);
#endif
static int __devinit init_matrix_gpio(struct platform_device *pdev,
@@ -417,11 +419,12 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev)
static struct platform_driver matrix_keypad_driver = {
.probe = matrix_keypad_probe,
.remove = __devexit_p(matrix_keypad_remove),
- .suspend = matrix_keypad_suspend,
- .resume = matrix_keypad_resume,
.driver = {
.name = "matrix-keypad",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &matrix_keypad_pm_ops,
+#endif
},
};
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index bba85add35a..1a494d50543 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -35,12 +35,12 @@
#include <linux/mutex.h>
#include <linux/errno.h>
#include <mach/gpio.h>
-#include <mach/keypad.h>
-#include <mach/menelaus.h>
+#include <plat/keypad.h>
+#include <plat/menelaus.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <asm/io.h>
-#include <mach/mux.h>
+#include <plat/mux.h>
#undef NEW_BOARD_LEARNING_MODE
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index 887af79b7bf..076111fc72d 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -18,9 +18,9 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
+#include <linux/input/sh_keysc.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <asm/sh_keysc.h>
#define KYCR1_OFFS 0x00
#define KYCR2_OFFS 0x04
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index a9bb2544b2d..16ec5233441 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -80,6 +80,7 @@ config INPUT_WISTRON_BTNS
tristate "x86 Wistron laptop button interface"
depends on X86 && !X86_64
select INPUT_POLLDEV
+ select INPUT_SPARSEKMAP
select NEW_LEDS
select LEDS_CLASS
select CHECK_SIGNATURE
@@ -281,6 +282,7 @@ config INPUT_RB532_BUTTON
config INPUT_DM355EVM
tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
depends on MFD_DM355EVM_MSP
+ select INPUT_SPARSEKMAP
help
Supports the pushbuttons and IR remote used with
the DM355 EVM board.
diff --git a/drivers/input/misc/ati_remote.c b/drivers/input/misc/ati_remote.c
index e290fde35e7..614b65d78fe 100644
--- a/drivers/input/misc/ati_remote.c
+++ b/drivers/input/misc/ati_remote.c
@@ -766,7 +766,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
ati_remote->interface = interface;
usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
- strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
+ strlcat(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
if (udev->manufacturer)
strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
index f2b67dc81d8..766c06911f4 100644
--- a/drivers/input/misc/dm355evm_keys.c
+++ b/drivers/input/misc/dm355evm_keys.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
@@ -33,12 +34,8 @@ struct dm355evm_keys {
int irq;
};
-/* These initial keycodes can be remapped by dm355evm_setkeycode(). */
-static struct {
- u16 event;
- u16 keycode;
-} dm355evm_keys[] = {
-
+/* These initial keycodes can be remapped */
+static const struct key_entry dm355evm_keys[] = {
/*
* Pushbuttons on the EVM board ... note that the labels for these
* are SW10/SW11/etc on the PC board. The left/right orientation
@@ -47,11 +44,11 @@ static struct {
* is to the right. (That is, rotate the board counter-clockwise
* by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
*/
- { 0x00d8, KEY_OK, }, /* SW12 */
- { 0x00b8, KEY_UP, }, /* SW13 */
- { 0x00e8, KEY_DOWN, }, /* SW11 */
- { 0x0078, KEY_LEFT, }, /* SW14 */
- { 0x00f0, KEY_RIGHT, }, /* SW10 */
+ { KE_KEY, 0x00d8, { KEY_OK } }, /* SW12 */
+ { KE_KEY, 0x00b8, { KEY_UP } }, /* SW13 */
+ { KE_KEY, 0x00e8, { KEY_DOWN } }, /* SW11 */
+ { KE_KEY, 0x0078, { KEY_LEFT } }, /* SW14 */
+ { KE_KEY, 0x00f0, { KEY_RIGHT } }, /* SW10 */
/*
* IR buttons ... codes assigned to match the universal remote
@@ -65,35 +62,35 @@ static struct {
* RC5 codes are 14 bits, with two start bits (0x3 prefix)
* and a toggle bit (masked out below).
*/
- { 0x300c, KEY_POWER, }, /* NOTE: docs omit this */
- { 0x3000, KEY_NUMERIC_0, },
- { 0x3001, KEY_NUMERIC_1, },
- { 0x3002, KEY_NUMERIC_2, },
- { 0x3003, KEY_NUMERIC_3, },
- { 0x3004, KEY_NUMERIC_4, },
- { 0x3005, KEY_NUMERIC_5, },
- { 0x3006, KEY_NUMERIC_6, },
- { 0x3007, KEY_NUMERIC_7, },
- { 0x3008, KEY_NUMERIC_8, },
- { 0x3009, KEY_NUMERIC_9, },
- { 0x3022, KEY_ENTER, },
- { 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */
- { 0x300f, KEY_SELECT, }, /* "info" */
- { 0x3020, KEY_CHANNELUP, }, /* "up" */
- { 0x302e, KEY_MENU, }, /* "in/out" */
- { 0x3011, KEY_VOLUMEDOWN, }, /* "left" */
- { 0x300d, KEY_MUTE, }, /* "ok" */
- { 0x3010, KEY_VOLUMEUP, }, /* "right" */
- { 0x301e, KEY_SUBTITLE, }, /* "cc" */
- { 0x3021, KEY_CHANNELDOWN, }, /* "down" */
- { 0x3022, KEY_PREVIOUS, },
- { 0x3026, KEY_SLEEP, },
- { 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */
- { 0x3175, KEY_PLAY, },
- { 0x3174, KEY_FASTFORWARD, },
- { 0x3177, KEY_RECORD, },
- { 0x3176, KEY_STOP, },
- { 0x3169, KEY_PAUSE, },
+ { KE_KEY, 0x300c, { KEY_POWER } }, /* NOTE: docs omit this */
+ { KE_KEY, 0x3000, { KEY_NUMERIC_0 } },
+ { KE_KEY, 0x3001, { KEY_NUMERIC_1 } },
+ { KE_KEY, 0x3002, { KEY_NUMERIC_2 } },
+ { KE_KEY, 0x3003, { KEY_NUMERIC_3 } },
+ { KE_KEY, 0x3004, { KEY_NUMERIC_4 } },
+ { KE_KEY, 0x3005, { KEY_NUMERIC_5 } },
+ { KE_KEY, 0x3006, { KEY_NUMERIC_6 } },
+ { KE_KEY, 0x3007, { KEY_NUMERIC_7 } },
+ { KE_KEY, 0x3008, { KEY_NUMERIC_8 } },
+ { KE_KEY, 0x3009, { KEY_NUMERIC_9 } },
+ { KE_KEY, 0x3022, { KEY_ENTER } },
+ { KE_KEY, 0x30ec, { KEY_MODE } }, /* "tv/vcr/..." */
+ { KE_KEY, 0x300f, { KEY_SELECT } }, /* "info" */
+ { KE_KEY, 0x3020, { KEY_CHANNELUP } }, /* "up" */
+ { KE_KEY, 0x302e, { KEY_MENU } }, /* "in/out" */
+ { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */
+ { KE_KEY, 0x300d, { KEY_MUTE } }, /* "ok" */
+ { KE_KEY, 0x3010, { KEY_VOLUMEUP } }, /* "right" */
+ { KE_KEY, 0x301e, { KEY_SUBTITLE } }, /* "cc" */
+ { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */
+ { KE_KEY, 0x3022, { KEY_PREVIOUS } },
+ { KE_KEY, 0x3026, { KEY_SLEEP } },
+ { KE_KEY, 0x3172, { KEY_REWIND } }, /* NOTE: docs wrongly say 0x30ca */
+ { KE_KEY, 0x3175, { KEY_PLAY } },
+ { KE_KEY, 0x3174, { KEY_FASTFORWARD } },
+ { KE_KEY, 0x3177, { KEY_RECORD } },
+ { KE_KEY, 0x3176, { KEY_STOP } },
+ { KE_KEY, 0x3169, { KEY_PAUSE } },
};
/*
@@ -105,19 +102,18 @@ static struct {
*/
static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
{
- struct dm355evm_keys *keys = _keys;
- int status;
+ static u16 last_event;
+ struct dm355evm_keys *keys = _keys;
+ const struct key_entry *ke;
+ unsigned int keycode;
+ int status;
+ u16 event;
/* For simplicity we ignore INPUT_COUNT and just read
* events until we get the "queue empty" indicator.
* Reading INPUT_LOW decrements the count.
*/
for (;;) {
- static u16 last_event;
- u16 event;
- int keycode;
- int i;
-
status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
if (status < 0) {
dev_dbg(keys->dev, "input high err %d\n",
@@ -156,14 +152,9 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
/* ignore the RC5 toggle bit */
event &= ~0x0800;
- /* find the key, or leave it as unknown */
- keycode = KEY_UNKNOWN;
- for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
- if (dm355evm_keys[i].event != event)
- continue;
- keycode = dm355evm_keys[i].keycode;
- break;
- }
+ /* find the key, or report it as unknown */
+ ke = sparse_keymap_entry_from_scancode(keys->input, event);
+ keycode = ke ? ke->keycode : KEY_UNKNOWN;
dev_dbg(keys->dev,
"input event 0x%04x--> keycode %d\n",
event, keycode);
@@ -174,36 +165,8 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
input_report_key(keys->input, keycode, 0);
input_sync(keys->input);
}
- return IRQ_HANDLED;
-}
-static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode)
-{
- u16 old_keycode;
- unsigned i;
-
- if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
- return -EINVAL;
-
- old_keycode = dm355evm_keys[index].keycode;
- dm355evm_keys[index].keycode = keycode;
- set_bit(keycode, dev->keybit);
-
- for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
- if (dm355evm_keys[index].keycode == old_keycode)
- goto done;
- }
- clear_bit(old_keycode, dev->keybit);
-done:
- return 0;
-}
-
-static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode)
-{
- if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
- return -EINVAL;
-
- return dm355evm_keys[index].keycode;
+ return IRQ_HANDLED;
}
/*----------------------------------------------------------------------*/
@@ -213,7 +176,6 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
struct dm355evm_keys *keys;
struct input_dev *input;
int status;
- int i;
/* allocate instance struct and input dev */
keys = kzalloc(sizeof *keys, GFP_KERNEL);
@@ -242,31 +204,30 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
input->id.product = 0x0355;
input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
- input->evbit[0] = BIT(EV_KEY);
- for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++)
- __set_bit(dm355evm_keys[i].keycode, input->keybit);
-
- input->setkeycode = dm355evm_setkeycode;
- input->getkeycode = dm355evm_getkeycode;
+ status = sparse_keymap_setup(input, dm355evm_keys, NULL);
+ if (status)
+ goto fail1;
/* REVISIT: flush the event queue? */
status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys);
if (status < 0)
- goto fail1;
+ goto fail2;
/* register */
status = input_register_device(input);
if (status < 0)
- goto fail2;
+ goto fail3;
platform_set_drvdata(pdev, keys);
return 0;
-fail2:
+fail3:
free_irq(keys->irq, keys);
+fail2:
+ sparse_keymap_free(input);
fail1:
input_free_device(input);
kfree(keys);
@@ -280,6 +241,7 @@ static int __devexit dm355evm_keys_remove(struct platform_device *pdev)
struct dm355evm_keys *keys = platform_get_drvdata(pdev);
free_irq(keys->irq, keys);
+ sparse_keymap_free(keys->input);
input_unregister_device(keys->input);
kfree(keys);
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index ea821b54696..ad730e15afc 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -35,7 +35,6 @@
#include <linux/hp_sdc.h>
#include <linux/errno.h>
-#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -409,7 +408,6 @@ static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait)
static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
{
- cycle_kernel_lock();
return 0;
}
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
index a53c4885fba..668913d1204 100644
--- a/drivers/input/misc/powermate.c
+++ b/drivers/input/misc/powermate.c
@@ -338,7 +338,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
pm->input = input_dev;
usb_make_path(udev, pm->phys, sizeof(pm->phys));
- strlcpy(pm->phys, "/input0", sizeof(pm->phys));
+ strlcat(pm->phys, "/input0", sizeof(pm->phys));
spin_lock_init(&pm->lock);
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index a932179c4c9..38da6ab0438 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -21,6 +21,7 @@
#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/input-polldev.h>
+#include <linux/input/sparse-keymap.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
@@ -224,19 +225,8 @@ static void bios_set_state(u8 subsys, int enable)
/* Hardware database */
-struct key_entry {
- char type; /* See KE_* below */
- u8 code;
- union {
- u16 keycode; /* For KE_KEY */
- struct { /* For KE_SW */
- u8 code;
- u8 value;
- } sw;
- };
-};
-
-enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH };
+#define KE_WIFI (KE_LAST + 1)
+#define KE_BLUETOOTH (KE_LAST + 2)
#define FE_MAIL_LED 0x01
#define FE_WIFI_LED 0x02
@@ -644,10 +634,10 @@ static struct key_entry keymap_prestigio[] __initdata = {
* a list of buttons and their key codes (reported when loading this module
* with force=1) and the output of dmidecode to $MODULE_AUTHOR.
*/
-static struct dmi_system_id dmi_ids[] __initdata = {
+static const struct dmi_system_id __initconst dmi_ids[] = {
{
+ /* Fujitsu-Siemens Amilo Pro V2000 */
.callback = dmi_matched,
- .ident = "Fujitsu-Siemens Amilo Pro V2000",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"),
@@ -655,8 +645,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_fs_amilo_pro_v2000
},
{
+ /* Fujitsu-Siemens Amilo Pro Edition V3505 */
.callback = dmi_matched,
- .ident = "Fujitsu-Siemens Amilo Pro Edition V3505",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"),
@@ -664,8 +654,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_fs_amilo_pro_v3505
},
{
+ /* Fujitsu-Siemens Amilo M7400 */
.callback = dmi_matched,
- .ident = "Fujitsu-Siemens Amilo M7400",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "),
@@ -673,8 +663,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_fs_amilo_pro_v2000
},
{
+ /* Maxdata Pro 7000 DX */
.callback = dmi_matched,
- .ident = "Maxdata Pro 7000 DX",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"),
@@ -682,8 +672,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_fs_amilo_pro_v2000
},
{
+ /* Fujitsu N3510 */
.callback = dmi_matched,
- .ident = "Fujitsu N3510",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "N3510"),
@@ -691,8 +681,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_fujitsu_n3510
},
{
+ /* Acer Aspire 1500 */
.callback = dmi_matched,
- .ident = "Acer Aspire 1500",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"),
@@ -700,8 +690,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_aspire_1500
},
{
+ /* Acer Aspire 1600 */
.callback = dmi_matched,
- .ident = "Acer Aspire 1600",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"),
@@ -709,8 +699,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_aspire_1600
},
{
+ /* Acer Aspire 3020 */
.callback = dmi_matched,
- .ident = "Acer Aspire 3020",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),
@@ -718,8 +708,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_aspire_5020
},
{
+ /* Acer Aspire 5020 */
.callback = dmi_matched,
- .ident = "Acer Aspire 5020",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),
@@ -727,8 +717,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_aspire_5020
},
{
+ /* Acer TravelMate 2100 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 2100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"),
@@ -736,8 +726,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_aspire_5020
},
{
+ /* Acer TravelMate 2410 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 2410",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"),
@@ -745,8 +735,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_2410
},
{
+ /* Acer TravelMate C300 */
.callback = dmi_matched,
- .ident = "Acer TravelMate C300",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"),
@@ -754,8 +744,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_300
},
{
+ /* Acer TravelMate C100 */
.callback = dmi_matched,
- .ident = "Acer TravelMate C100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"),
@@ -763,8 +753,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_300
},
{
+ /* Acer TravelMate C110 */
.callback = dmi_matched,
- .ident = "Acer TravelMate C110",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"),
@@ -772,8 +762,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_110
},
{
+ /* Acer TravelMate 380 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 380",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"),
@@ -781,8 +771,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_380
},
{
+ /* Acer TravelMate 370 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 370",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"),
@@ -790,8 +780,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */
},
{
+ /* Acer TravelMate 220 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 220",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"),
@@ -799,8 +789,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_220
},
{
+ /* Acer TravelMate 260 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 260",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"),
@@ -808,8 +798,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_220
},
{
+ /* Acer TravelMate 230 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 230",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"),
@@ -818,8 +808,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_230
},
{
+ /* Acer TravelMate 280 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 280",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"),
@@ -827,8 +817,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_230
},
{
+ /* Acer TravelMate 240 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 240",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"),
@@ -836,8 +826,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_240
},
{
+ /* Acer TravelMate 250 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 250",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"),
@@ -845,8 +835,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_240
},
{
+ /* Acer TravelMate 2424NWXCi */
.callback = dmi_matched,
- .ident = "Acer TravelMate 2424NWXCi",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),
@@ -854,8 +844,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_240
},
{
+ /* Acer TravelMate 350 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 350",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"),
@@ -863,8 +853,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_350
},
{
+ /* Acer TravelMate 360 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 360",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
@@ -872,8 +862,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_360
},
{
+ /* Acer TravelMate 610 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 610",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ACER"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"),
@@ -881,8 +871,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_610
},
{
+ /* Acer TravelMate 620 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 620",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"),
@@ -890,8 +880,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_630
},
{
+ /* Acer TravelMate 630 */
.callback = dmi_matched,
- .ident = "Acer TravelMate 630",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"),
@@ -899,8 +889,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_acer_travelmate_630
},
{
+ /* AOpen 1559AS */
.callback = dmi_matched,
- .ident = "AOpen 1559AS",
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "E2U"),
DMI_MATCH(DMI_BOARD_NAME, "E2U"),
@@ -908,8 +898,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_aopen_1559as
},
{
+ /* Medion MD 9783 */
.callback = dmi_matched,
- .ident = "Medion MD 9783",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"),
@@ -917,8 +907,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_wistron_ms2111
},
{
+ /* Medion MD 40100 */
.callback = dmi_matched,
- .ident = "Medion MD 40100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"),
@@ -926,8 +916,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_wistron_md40100
},
{
+ /* Medion MD 2900 */
.callback = dmi_matched,
- .ident = "Medion MD 2900",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"),
@@ -935,8 +925,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_wistron_md2900
},
{
+ /* Medion MD 42200 */
.callback = dmi_matched,
- .ident = "Medion MD 42200",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Medion"),
DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"),
@@ -944,8 +934,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_fs_amilo_pro_v2000
},
{
+ /* Medion MD 96500 */
.callback = dmi_matched,
- .ident = "Medion MD 96500",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"),
@@ -953,8 +943,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_wistron_md96500
},
{
+ /* Medion MD 95400 */
.callback = dmi_matched,
- .ident = "Medion MD 95400",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"),
@@ -962,8 +952,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_wistron_md96500
},
{
+ /* Fujitsu Siemens Amilo D7820 */
.callback = dmi_matched,
- .ident = "Fujitsu Siemens Amilo D7820",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */
DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"),
@@ -971,8 +961,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
.driver_data = keymap_fs_amilo_d88x0
},
{
+ /* Fujitsu Siemens Amilo D88x0 */
.callback = dmi_matched,
- .ident = "Fujitsu Siemens Amilo D88x0",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"),
@@ -1037,21 +1027,6 @@ static unsigned long jiffies_last_press;
static bool wifi_enabled;
static bool bluetooth_enabled;
-static void report_key(struct input_dev *dev, unsigned int keycode)
-{
- input_report_key(dev, keycode, 1);
- input_sync(dev);
- input_report_key(dev, keycode, 0);
- input_sync(dev);
-}
-
-static void report_switch(struct input_dev *dev, unsigned int code, int value)
-{
- input_report_switch(dev, code, value);
- input_sync(dev);
-}
-
-
/* led management */
static void wistron_mail_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
@@ -1128,43 +1103,13 @@ static inline void wistron_led_resume(void)
led_classdev_resume(&wistron_wifi_led);
}
-static struct key_entry *wistron_get_entry_by_scancode(int code)
-{
- struct key_entry *key;
-
- for (key = keymap; key->type != KE_END; key++)
- if (code == key->code)
- return key;
-
- return NULL;
-}
-
-static struct key_entry *wistron_get_entry_by_keycode(int keycode)
-{
- struct key_entry *key;
-
- for (key = keymap; key->type != KE_END; key++)
- if (key->type == KE_KEY && keycode == key->keycode)
- return key;
-
- return NULL;
-}
-
static void handle_key(u8 code)
{
- const struct key_entry *key = wistron_get_entry_by_scancode(code);
+ const struct key_entry *key =
+ sparse_keymap_entry_from_scancode(wistron_idev->input, code);
if (key) {
switch (key->type) {
- case KE_KEY:
- report_key(wistron_idev->input, key->keycode);
- break;
-
- case KE_SW:
- report_switch(wistron_idev->input,
- key->sw.code, key->sw.value);
- break;
-
case KE_WIFI:
if (have_wifi) {
wifi_enabled = !wifi_enabled;
@@ -1180,7 +1125,9 @@ static void handle_key(u8 code)
break;
default:
- BUG();
+ sparse_keymap_report_entry(wistron_idev->input,
+ key, 1, true);
+ break;
}
jiffies_last_press = jiffies;
} else
@@ -1220,42 +1167,39 @@ static void wistron_poll(struct input_polled_dev *dev)
dev->poll_interval = POLL_INTERVAL_DEFAULT;
}
-static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+static int __devinit wistron_setup_keymap(struct input_dev *dev,
+ struct key_entry *entry)
{
- const struct key_entry *key = wistron_get_entry_by_scancode(scancode);
+ switch (entry->type) {
- if (key && key->type == KE_KEY) {
- *keycode = key->keycode;
- return 0;
- }
-
- return -EINVAL;
-}
+ /* if wifi or bluetooth are not available, create normal keys */
+ case KE_WIFI:
+ if (!have_wifi) {
+ entry->type = KE_KEY;
+ entry->keycode = KEY_WLAN;
+ }
+ break;
-static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode)
-{
- struct key_entry *key;
- int old_keycode;
-
- if (keycode < 0 || keycode > KEY_MAX)
- return -EINVAL;
-
- key = wistron_get_entry_by_scancode(scancode);
- if (key && key->type == KE_KEY) {
- old_keycode = key->keycode;
- key->keycode = keycode;
- set_bit(keycode, dev->keybit);
- if (!wistron_get_entry_by_keycode(old_keycode))
- clear_bit(old_keycode, dev->keybit);
- return 0;
+ case KE_BLUETOOTH:
+ if (!have_bluetooth) {
+ entry->type = KE_KEY;
+ entry->keycode = KEY_BLUETOOTH;
+ }
+ break;
+
+ case KE_END:
+ if (entry->code & FE_UNTESTED)
+ printk(KERN_WARNING "Untested laptop multimedia keys, "
+ "please report success or failure to "
+ "eric.piel@tremplin-utc.net\n");
+ break;
}
- return -EINVAL;
+ return 0;
}
static int __devinit setup_input_dev(void)
{
- struct key_entry *key;
struct input_dev *input_dev;
int error;
@@ -1263,7 +1207,7 @@ static int __devinit setup_input_dev(void)
if (!wistron_idev)
return -ENOMEM;
- wistron_idev->flush = wistron_flush;
+ wistron_idev->open = wistron_flush;
wistron_idev->poll = wistron_poll;
wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT;
@@ -1273,56 +1217,21 @@ static int __devinit setup_input_dev(void)
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &wistron_device->dev;
- input_dev->getkeycode = wistron_getkeycode;
- input_dev->setkeycode = wistron_setkeycode;
-
- for (key = keymap; key->type != KE_END; key++) {
- switch (key->type) {
- case KE_KEY:
- set_bit(EV_KEY, input_dev->evbit);
- set_bit(key->keycode, input_dev->keybit);
- break;
-
- case KE_SW:
- set_bit(EV_SW, input_dev->evbit);
- set_bit(key->sw.code, input_dev->swbit);
- break;
-
- /* if wifi or bluetooth are not available, create normal keys */
- case KE_WIFI:
- if (!have_wifi) {
- key->type = KE_KEY;
- key->keycode = KEY_WLAN;
- key--;
- }
- break;
-
- case KE_BLUETOOTH:
- if (!have_bluetooth) {
- key->type = KE_KEY;
- key->keycode = KEY_BLUETOOTH;
- key--;
- }
- break;
-
- default:
- break;
- }
- }
-
- /* reads information flags on KE_END */
- if (key->code & FE_UNTESTED)
- printk(KERN_WARNING "Untested laptop multimedia keys, "
- "please report success or failure to eric.piel"
- "@tremplin-utc.net\n");
+ error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap);
+ if (error)
+ goto err_free_dev;
error = input_register_polled_device(wistron_idev);
- if (error) {
- input_free_polled_device(wistron_idev);
- return error;
- }
+ if (error)
+ goto err_free_keymap;
return 0;
+
+ err_free_keymap:
+ sparse_keymap_free(input_dev);
+ err_free_dev:
+ input_free_polled_device(wistron_idev);
+ return error;
}
/* Driver core */
@@ -1371,6 +1280,7 @@ static int __devexit wistron_remove(struct platform_device *dev)
{
wistron_led_remove();
input_unregister_polled_device(wistron_idev);
+ sparse_keymap_free(wistron_idev->input);
input_free_polled_device(wistron_idev);
bios_detach();
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index f36110689aa..a3f492a5085 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -28,13 +28,16 @@
#define dbg(format, arg...) do {} while (0)
#endif
-#define ALPS_DUALPOINT 0x01
-#define ALPS_WHEEL 0x02
-#define ALPS_FW_BK_1 0x04
-#define ALPS_4BTN 0x08
-#define ALPS_OLDPROTO 0x10
-#define ALPS_PASS 0x20
-#define ALPS_FW_BK_2 0x40
+
+#define ALPS_OLDPROTO 0x01 /* old style input */
+#define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */
+#define ALPS_PASS 0x04 /* device has a pass-through port */
+
+#define ALPS_WHEEL 0x08 /* hardware wheel present */
+#define ALPS_FW_BK_1 0x10 /* front & back buttons present */
+#define ALPS_FW_BK_2 0x20 /* front & back buttons present */
+#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */
+
static const struct alps_model_info alps_model_data[] = {
{ { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
@@ -56,7 +59,7 @@ static const struct alps_model_info alps_model_data[] = {
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
{ { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */
- { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 }, /* Dell Vostro 1400 */
+ { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */
};
/*
@@ -83,6 +86,7 @@ static const struct alps_model_info alps_model_data[] = {
static void alps_process_packet(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
+ const struct alps_model_info *model = priv->i;
unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2;
@@ -101,7 +105,7 @@ static void alps_process_packet(struct psmouse *psmouse)
return;
}
- if (priv->i->flags & ALPS_OLDPROTO) {
+ if (model->flags & ALPS_OLDPROTO) {
left = packet[2] & 0x10;
right = packet[2] & 0x08;
middle = 0;
@@ -117,12 +121,12 @@ static void alps_process_packet(struct psmouse *psmouse)
z = packet[5];
}
- if (priv->i->flags & ALPS_FW_BK_1) {
+ if (model->flags & ALPS_FW_BK_1) {
back = packet[0] & 0x10;
forward = packet[2] & 4;
}
- if (priv->i->flags & ALPS_FW_BK_2) {
+ if (model->flags & ALPS_FW_BK_2) {
back = packet[3] & 4;
forward = packet[2] & 4;
if ((middle = forward && back))
@@ -132,7 +136,7 @@ static void alps_process_packet(struct psmouse *psmouse)
ges = packet[2] & 1;
fin = packet[2] & 2;
- if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) {
+ if ((model->flags & ALPS_DUALPOINT) && z == 127) {
input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
@@ -150,7 +154,8 @@ static void alps_process_packet(struct psmouse *psmouse)
input_report_key(dev, BTN_MIDDLE, middle);
/* Convert hardware tap to a reasonable Z value */
- if (ges && !fin) z = 40;
+ if (ges && !fin)
+ z = 40;
/*
* A "tap and drag" operation is reported by the hardware as a transition
@@ -166,8 +171,10 @@ static void alps_process_packet(struct psmouse *psmouse)
}
priv->prev_fin = fin;
- if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
- if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
+ if (z > 30)
+ input_report_key(dev, BTN_TOUCH, 1);
+ if (z < 25)
+ input_report_key(dev, BTN_TOUCH, 0);
if (z > 0) {
input_report_abs(dev, ABS_X, x);
@@ -177,14 +184,21 @@ static void alps_process_packet(struct psmouse *psmouse)
input_report_abs(dev, ABS_PRESSURE, z);
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
- if (priv->i->flags & ALPS_WHEEL)
+ if (model->flags & ALPS_WHEEL)
input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
- if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+ if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
input_report_key(dev, BTN_FORWARD, forward);
input_report_key(dev, BTN_BACK, back);
}
+ if (model->flags & ALPS_FOUR_BUTTONS) {
+ input_report_key(dev, BTN_0, packet[2] & 4);
+ input_report_key(dev, BTN_1, packet[0] & 0x10);
+ input_report_key(dev, BTN_2, packet[3] & 4);
+ input_report_key(dev, BTN_3, packet[0] & 0x20);
+ }
+
input_sync(dev);
}
@@ -393,15 +407,12 @@ static int alps_poll(struct psmouse *psmouse)
return 0;
}
-static int alps_hw_init(struct psmouse *psmouse, int *version)
+static int alps_hw_init(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
+ const struct alps_model_info *model = priv->i;
- priv->i = alps_get_model(psmouse, version);
- if (!priv->i)
- return -1;
-
- if ((priv->i->flags & ALPS_PASS) &&
+ if ((model->flags & ALPS_PASS) &&
alps_passthrough_mode(psmouse, true)) {
return -1;
}
@@ -416,7 +427,7 @@ static int alps_hw_init(struct psmouse *psmouse, int *version)
return -1;
}
- if ((priv->i->flags & ALPS_PASS) &&
+ if ((model->flags & ALPS_PASS) &&
alps_passthrough_mode(psmouse, false)) {
return -1;
}
@@ -432,12 +443,15 @@ static int alps_hw_init(struct psmouse *psmouse, int *version)
static int alps_reconnect(struct psmouse *psmouse)
{
+ const struct alps_model_info *model;
+
psmouse_reset(psmouse);
- if (alps_hw_init(psmouse, NULL))
+ model = alps_get_model(psmouse, NULL);
+ if (!model)
return -1;
- return 0;
+ return alps_hw_init(psmouse);
}
static void alps_disconnect(struct psmouse *psmouse)
@@ -452,6 +466,7 @@ static void alps_disconnect(struct psmouse *psmouse)
int alps_init(struct psmouse *psmouse)
{
struct alps_data *priv;
+ const struct alps_model_info *model;
struct input_dev *dev1 = psmouse->dev, *dev2;
int version;
@@ -463,33 +478,48 @@ int alps_init(struct psmouse *psmouse)
priv->dev2 = dev2;
psmouse->private = priv;
- if (alps_hw_init(psmouse, &version))
+ model = alps_get_model(psmouse, &version);
+ if (!model)
+ goto init_fail;
+
+ priv->i = model;
+
+ if (alps_hw_init(psmouse))
goto init_fail;
dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);
dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER);
- dev1->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) |
- BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ dev1->keybit[BIT_WORD(BTN_LEFT)] |=
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
- if (priv->i->flags & ALPS_WHEEL) {
+ if (model->flags & ALPS_WHEEL) {
dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);
}
- if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+ if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);
dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);
}
+ if (model->flags & ALPS_FOUR_BUTTONS) {
+ dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);
+ dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
+ dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
+ dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
+ } else {
+ dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
+ }
+
snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys;
- dev2->name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
+ dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
dev2->id.bustype = BUS_I8042;
dev2->id.vendor = 0x0002;
dev2->id.product = PSMOUSE_ALPS;
@@ -497,9 +527,9 @@ int alps_init(struct psmouse *psmouse)
dev2->dev.parent = &psmouse->ps2dev.serio->dev;
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
- dev2->relbit[BIT_WORD(REL_X)] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
- dev2->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) |
- BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ dev2->keybit[BIT_WORD(BTN_LEFT)] =
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
if (input_register_device(priv->dev2))
goto init_fail;
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index fda35e615ab..b27684f267b 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -420,6 +420,7 @@ static void elantech_set_input_params(struct psmouse *psmouse)
__set_bit(EV_KEY, dev->evbit);
__set_bit(EV_ABS, dev->evbit);
+ __clear_bit(EV_REL, dev->evbit);
__set_bit(BTN_LEFT, dev->keybit);
__set_bit(BTN_RIGHT, dev->keybit);
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
index de1e553028b..b146237266d 100644
--- a/drivers/input/mouse/hgpk.c
+++ b/drivers/input/mouse/hgpk.c
@@ -430,19 +430,6 @@ static int hgpk_register(struct psmouse *psmouse)
struct input_dev *dev = psmouse->dev;
int err;
- /* unset the things that psmouse-base sets which we don't have */
- __clear_bit(BTN_MIDDLE, dev->keybit);
-
- /* set the things we do have */
- __set_bit(EV_KEY, dev->evbit);
- __set_bit(EV_REL, dev->evbit);
-
- __set_bit(REL_X, dev->relbit);
- __set_bit(REL_Y, dev->relbit);
-
- __set_bit(BTN_LEFT, dev->keybit);
- __set_bit(BTN_RIGHT, dev->keybit);
-
/* register handlers */
psmouse->protocol_handler = hgpk_process_byte;
psmouse->poll = hgpk_poll;
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index 82811558ec3..2e6bdfea016 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -25,11 +25,13 @@ struct lifebook_data {
char phys[32];
};
+static bool lifebook_present;
+
static const char *desired_serio_phys;
-static int lifebook_set_serio_phys(const struct dmi_system_id *d)
+static int lifebook_limit_serio3(const struct dmi_system_id *d)
{
- desired_serio_phys = d->driver_data;
+ desired_serio_phys = "isa0060/serio3";
return 0;
}
@@ -41,53 +43,53 @@ static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
return 0;
}
-static const struct dmi_system_id lifebook_dmi_table[] = {
+static const struct dmi_system_id __initconst lifebook_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
- .ident = "FLORA-ie 55mi",
+ /* FLORA-ie 55mi */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
},
},
{
- .ident = "LifeBook B",
+ /* LifeBook B */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
},
},
{
- .ident = "Lifebook B",
+ /* Lifebook B */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
},
},
{
- .ident = "Lifebook B-2130",
+ /* Lifebook B-2130 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"),
},
},
{
- .ident = "Lifebook B213x/B2150",
+ /* Lifebook B213x/B2150 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
},
},
{
- .ident = "Zephyr",
+ /* Zephyr */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
},
},
{
- .ident = "CF-18",
+ /* Panasonic CF-18 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
},
- .callback = lifebook_set_serio_phys,
- .driver_data = "isa0060/serio3",
+ .callback = lifebook_limit_serio3,
},
{
- .ident = "Panasonic CF-28",
+ /* Panasonic CF-28 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"),
@@ -95,7 +97,7 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
.callback = lifebook_set_6byte_proto,
},
{
- .ident = "Panasonic CF-29",
+ /* Panasonic CF-29 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
@@ -103,21 +105,27 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
.callback = lifebook_set_6byte_proto,
},
{
- .ident = "CF-72",
+ /* Panasonic CF-72 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"),
},
.callback = lifebook_set_6byte_proto,
},
{
- .ident = "Lifebook B142",
+ /* Lifebook B142 */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
},
},
{ }
+#endif
};
+void __init lifebook_module_init(void)
+{
+ lifebook_present = dmi_check_system(lifebook_dmi_table);
+}
+
static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
{
struct lifebook_data *priv = psmouse->private;
@@ -198,10 +206,10 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
return -1;
/*
- Enable absolute output -- ps2_command fails always but if
- you leave this call out the touchsreen will never send
- absolute coordinates
- */
+ * Enable absolute output -- ps2_command fails always but if
+ * you leave this call out the touchsreen will never send
+ * absolute coordinates
+ */
param = lifebook_use_6byte_proto ? 0x08 : 0x07;
ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
@@ -243,7 +251,7 @@ static void lifebook_disconnect(struct psmouse *psmouse)
int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
- if (!dmi_check_system(lifebook_dmi_table))
+ if (!lifebook_present)
return -1;
if (desired_serio_phys &&
@@ -283,8 +291,8 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
- dev2->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
- BIT_MASK(BTN_RIGHT);
+ dev2->keybit[BIT_WORD(BTN_LEFT)] =
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
error = input_register_device(priv->dev2);
if (error)
@@ -309,6 +317,7 @@ int lifebook_init(struct psmouse *psmouse)
dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
dev1->relbit[0] = 0;
+ dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0;
dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h
index 407cb226bc0..4c4326c6f50 100644
--- a/drivers/input/mouse/lifebook.h
+++ b/drivers/input/mouse/lifebook.h
@@ -12,9 +12,13 @@
#define _LIFEBOOK_H
#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
+void lifebook_module_init(void);
int lifebook_detect(struct psmouse *psmouse, bool set_properties);
int lifebook_init(struct psmouse *psmouse);
#else
+inline void lifebook_module_init(void)
+{
+}
inline int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index ab5dc5f5fd8..543c240a85f 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -404,8 +404,8 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)
}
}
- if (buttons < 3)
- __clear_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ if (buttons >= 3)
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 07c53798301..fd0bc094616 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -425,6 +425,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
return -1;
if (set_properties) {
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
__set_bit(BTN_EXTRA, psmouse->dev->keybit);
__set_bit(BTN_SIDE, psmouse->dev->keybit);
__set_bit(REL_WHEEL, psmouse->dev->relbit);
@@ -460,8 +461,10 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
__set_bit(REL_WHEEL, psmouse->dev->relbit);
- if (!psmouse->vendor) psmouse->vendor = "Generic";
- if (!psmouse->name) psmouse->name = "Wheel Mouse";
+ if (!psmouse->vendor)
+ psmouse->vendor = "Generic";
+ if (!psmouse->name)
+ psmouse->name = "Wheel Mouse";
psmouse->pktsize = 4;
}
@@ -504,8 +507,10 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
__set_bit(BTN_SIDE, psmouse->dev->keybit);
__set_bit(BTN_EXTRA, psmouse->dev->keybit);
- if (!psmouse->vendor) psmouse->vendor = "Generic";
- if (!psmouse->name) psmouse->name = "Explorer Mouse";
+ if (!psmouse->vendor)
+ psmouse->vendor = "Generic";
+ if (!psmouse->name)
+ psmouse->name = "Explorer Mouse";
psmouse->pktsize = 4;
}
@@ -536,6 +541,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
return -1;
if (set_properties) {
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
__set_bit(BTN_EXTRA, psmouse->dev->keybit);
psmouse->vendor = "Kensington";
@@ -551,8 +557,16 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
{
if (set_properties) {
- if (!psmouse->vendor) psmouse->vendor = "Generic";
- if (!psmouse->name) psmouse->name = "Mouse";
+ if (!psmouse->vendor)
+ psmouse->vendor = "Generic";
+ if (!psmouse->name)
+ psmouse->name = "Mouse";
+
+/*
+ * We have no way of figuring true number of buttons so let's
+ * assume that the device has 3.
+ */
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
}
return 0;
@@ -567,6 +581,8 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties)
if (set_properties) {
psmouse->vendor = "Cortron";
psmouse->name = "PS/2 Trackball";
+
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
__set_bit(BTN_SIDE, psmouse->dev->keybit);
}
@@ -1184,15 +1200,16 @@ static void psmouse_disconnect(struct serio *serio)
mutex_unlock(&psmouse_mutex);
}
-static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto)
+static int psmouse_switch_protocol(struct psmouse *psmouse,
+ const struct psmouse_protocol *proto)
{
struct input_dev *input_dev = psmouse->dev;
input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
- input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
- BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] =
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
psmouse->set_rate = psmouse_set_rate;
@@ -1209,8 +1226,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse
return -1;
psmouse->type = proto->type;
- }
- else
+ } else
psmouse->type = psmouse_extensions(psmouse,
psmouse_max_proto, true);
@@ -1680,6 +1696,9 @@ static int __init psmouse_init(void)
{
int err;
+ lifebook_module_init();
+ synaptics_module_init();
+
kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
if (!kpsmoused_wq) {
printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n");
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index f84cbd97c88..77b9fd0b3fb 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -836,6 +836,7 @@ int fsp_init(struct psmouse *psmouse)
priv->flags |= FSPDRV_FLAG_EN_OPC;
/* Set up various supported input event bits */
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
__set_bit(BTN_BACK, psmouse->dev->keybit);
__set_bit(BTN_FORWARD, psmouse->dev->keybit);
__set_bit(REL_WHEEL, psmouse->dev->relbit);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index f4a61252bcc..05689e73219 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -24,6 +24,7 @@
*/
#include <linux/module.h>
+#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
@@ -629,25 +630,26 @@ static int synaptics_reconnect(struct psmouse *psmouse)
return 0;
}
-#if defined(__i386__)
-#include <linux/dmi.h>
-static const struct dmi_system_id toshiba_dmi_table[] = {
+static bool impaired_toshiba_kbc;
+
+static const struct dmi_system_id __initconst toshiba_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
- .ident = "Toshiba Satellite",
+ /* Toshiba Satellite */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
},
},
{
- .ident = "Toshiba Dynabook",
+ /* Toshiba Dynabook */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
},
},
{
- .ident = "Toshiba Portege M300",
+ /* Toshiba Portege M300 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
@@ -655,7 +657,7 @@ static const struct dmi_system_id toshiba_dmi_table[] = {
},
{
- .ident = "Toshiba Portege M300",
+ /* Toshiba Portege M300 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"),
@@ -664,8 +666,13 @@ static const struct dmi_system_id toshiba_dmi_table[] = {
},
{ }
-};
#endif
+};
+
+void __init synaptics_module_init(void)
+{
+ impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
+}
int synaptics_init(struct psmouse *psmouse)
{
@@ -718,18 +725,16 @@ int synaptics_init(struct psmouse *psmouse)
if (SYN_CAP_PASS_THROUGH(priv->capabilities))
synaptics_pt_create(psmouse);
-#if defined(__i386__)
/*
* Toshiba's KBC seems to have trouble handling data from
* Synaptics as full rate, switch to lower rate which is roughly
* thye same as rate of standard PS/2 mouse.
*/
- if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) {
+ if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
dmi_get_system_info(DMI_PRODUCT_NAME));
psmouse->rate = 40;
}
-#endif
return 0;
@@ -740,6 +745,10 @@ int synaptics_init(struct psmouse *psmouse)
#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
+void __init synaptics_module_init(void)
+{
+}
+
int synaptics_init(struct psmouse *psmouse)
{
return -ENOSYS;
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 871f6fe377f..838e7f2c9b3 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -105,6 +105,7 @@ struct synaptics_data {
int scroll;
};
+void synaptics_module_init(void);
int synaptics_detect(struct psmouse *psmouse, bool set_properties);
int synaptics_init(struct psmouse *psmouse);
void synaptics_reset(struct psmouse *psmouse);
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index 7283c78044a..9867dfe2a63 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -420,8 +420,8 @@ static void synaptics_i2c_check_params(struct synaptics_i2c *touch)
}
/* Control the Device polling rate / Work Handler sleep time */
-unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch,
- bool have_data)
+static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch,
+ bool have_data)
{
unsigned long delay, nodata_count_thres;
@@ -520,7 +520,7 @@ static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch)
__set_bit(BTN_LEFT, input->keybit);
}
-struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
+static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
{
struct synaptics_i2c *touch;
diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c
index 0308a0faa94..909431c31ab 100644
--- a/drivers/input/mouse/touchkit_ps2.c
+++ b/drivers/input/mouse/touchkit_ps2.c
@@ -86,7 +86,8 @@ int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties)
if (set_properties) {
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- __set_bit(BTN_TOUCH, dev->keybit);
+ dev->keybit[BIT_WORD(BTN_MOUSE)] = 0;
+ dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0);
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index e354362f297..63d4a67830f 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -284,7 +284,6 @@ static int trackpoint_reconnect(struct psmouse *psmouse)
int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
{
- struct trackpoint_data *priv;
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char firmware_id;
unsigned char button_info;
@@ -301,8 +300,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
button_info = 0;
}
- psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
- if (!priv)
+ psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
+ if (!psmouse->private)
return -1;
psmouse->vendor = "IBM";
@@ -311,7 +310,10 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
psmouse->reconnect = trackpoint_reconnect;
psmouse->disconnect = trackpoint_disconnect;
- trackpoint_defaults(priv);
+ if ((button_info & 0x0f) >= 3)
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+
+ trackpoint_defaults(psmouse->private);
trackpoint_sync(psmouse);
error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
@@ -319,7 +321,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
printk(KERN_ERR
"trackpoint.c: failed to create sysfs attributes, error: %d\n",
error);
- kfree(priv);
+ kfree(psmouse->private);
+ psmouse->private = NULL;
return -1;
}
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
index 70111443678..bf2c0c80d6c 100644
--- a/drivers/input/mouse/vsxxxaa.c
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -86,27 +86,28 @@
#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet"
-MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
#undef VSXXXAA_DEBUG
#ifdef VSXXXAA_DEBUG
-#define DBG(x...) printk (x)
+#define DBG(x...) printk(x)
#else
#define DBG(x...) do {} while (0)
#endif
#define VSXXXAA_INTRO_MASK 0x80
#define VSXXXAA_INTRO_HEAD 0x80
-#define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \
- == VSXXXAA_INTRO_HEAD)
+#define IS_HDR_BYTE(x) \
+ (((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD)
#define VSXXXAA_PACKET_MASK 0xe0
#define VSXXXAA_PACKET_REL 0x80
#define VSXXXAA_PACKET_ABS 0xc0
#define VSXXXAA_PACKET_POR 0xa0
-#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == (type))
+#define MATCH_PACKET_TYPE(data, type) \
+ (((data) & VSXXXAA_PACKET_MASK) == (type))
@@ -123,52 +124,50 @@ struct vsxxxaa {
char phys[32];
};
-static void
-vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num)
+static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num)
{
- if (num >= mouse->count)
+ if (num >= mouse->count) {
mouse->count = 0;
- else {
- memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num);
+ } else {
+ memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num);
mouse->count -= num;
}
}
-static void
-vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte)
+static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte)
{
if (mouse->count == BUFLEN) {
- printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
- mouse->name, mouse->phys);
- vsxxxaa_drop_bytes (mouse, 1);
+ printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
+ mouse->name, mouse->phys);
+ vsxxxaa_drop_bytes(mouse, 1);
}
- DBG (KERN_INFO "Queueing byte 0x%02x\n", byte);
+
+ DBG(KERN_INFO "Queueing byte 0x%02x\n", byte);
mouse->buf[mouse->count++] = byte;
}
-static void
-vsxxxaa_detection_done (struct vsxxxaa *mouse)
+static void vsxxxaa_detection_done(struct vsxxxaa *mouse)
{
switch (mouse->type) {
- case 0x02:
- strlcpy (mouse->name, "DEC VSXXX-AA/-GA mouse",
- sizeof (mouse->name));
- break;
-
- case 0x04:
- strlcpy (mouse->name, "DEC VSXXX-AB digitizer",
- sizeof (mouse->name));
- break;
-
- default:
- snprintf (mouse->name, sizeof (mouse->name),
- "unknown DEC pointer device (type = 0x%02x)",
- mouse->type);
- break;
+ case 0x02:
+ strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
+ sizeof(mouse->name));
+ break;
+
+ case 0x04:
+ strlcpy(mouse->name, "DEC VSXXX-AB digitizer",
+ sizeof(mouse->name));
+ break;
+
+ default:
+ snprintf(mouse->name, sizeof(mouse->name),
+ "unknown DEC pointer device (type = 0x%02x)",
+ mouse->type);
+ break;
}
- printk (KERN_INFO
+ printk(KERN_INFO
"Found %s version 0x%02x from country 0x%02x on port %s\n",
mouse->name, mouse->version, mouse->country, mouse->phys);
}
@@ -176,42 +175,38 @@ vsxxxaa_detection_done (struct vsxxxaa *mouse)
/*
* Returns number of bytes to be dropped, 0 if packet is okay.
*/
-static int
-vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len)
+static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len)
{
int i;
/* First byte must be a header byte */
- if (!IS_HDR_BYTE (mouse->buf[0])) {
- DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
+ if (!IS_HDR_BYTE(mouse->buf[0])) {
+ DBG("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
return 1;
}
/* Check all following bytes */
- if (packet_len > 1) {
- for (i = 1; i < packet_len; i++) {
- if (IS_HDR_BYTE (mouse->buf[i])) {
- printk (KERN_ERR "Need to drop %d bytes "
- "of a broken packet.\n",
- i - 1);
- DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
- packet_len, i, mouse->buf[i]);
- return i - 1;
- }
+ for (i = 1; i < packet_len; i++) {
+ if (IS_HDR_BYTE(mouse->buf[i])) {
+ printk(KERN_ERR
+ "Need to drop %d bytes of a broken packet.\n",
+ i - 1);
+ DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
+ packet_len, i, mouse->buf[i]);
+ return i - 1;
}
}
return 0;
}
-static __inline__ int
-vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len)
+static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse,
+ unsigned char type, size_t len)
{
- return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type);
+ return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type);
}
-static void
-vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse)
+static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
@@ -232,43 +227,42 @@ vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse)
* 0, bit 4 of byte 0 is direction.
*/
dx = buf[1] & 0x7f;
- dx *= ((buf[0] >> 4) & 0x01)? 1: -1;
+ dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1;
/*
* Low 7 bit of byte 2 are abs(dy), bit 7 is
* 0, bit 3 of byte 0 is direction.
*/
dy = buf[2] & 0x7f;
- dy *= ((buf[0] >> 3) & 0x01)? -1: 1;
+ dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1;
/*
* Get button state. It's the low three bits
* (for three buttons) of byte 0.
*/
- left = (buf[0] & 0x04)? 1: 0;
- middle = (buf[0] & 0x02)? 1: 0;
- right = (buf[0] & 0x01)? 1: 0;
+ left = buf[0] & 0x04;
+ middle = buf[0] & 0x02;
+ right = buf[0] & 0x01;
- vsxxxaa_drop_bytes (mouse, 3);
+ vsxxxaa_drop_bytes(mouse, 3);
- DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
- mouse->name, mouse->phys, dx, dy,
- left? "L": "l", middle? "M": "m", right? "R": "r");
+ DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
+ mouse->name, mouse->phys, dx, dy,
+ left ? "L" : "l", middle ? "M" : "m", right ? "R" : "r");
/*
* Report what we've found so far...
*/
- input_report_key (dev, BTN_LEFT, left);
- input_report_key (dev, BTN_MIDDLE, middle);
- input_report_key (dev, BTN_RIGHT, right);
- input_report_key (dev, BTN_TOUCH, 0);
- input_report_rel (dev, REL_X, dx);
- input_report_rel (dev, REL_Y, dy);
- input_sync (dev);
+ input_report_key(dev, BTN_LEFT, left);
+ input_report_key(dev, BTN_MIDDLE, middle);
+ input_report_key(dev, BTN_RIGHT, right);
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_report_rel(dev, REL_X, dx);
+ input_report_rel(dev, REL_Y, dy);
+ input_sync(dev);
}
-static void
-vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse)
+static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
@@ -296,32 +290,31 @@ vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse)
/*
* Get button state. It's bits <4..1> of byte 0.
*/
- left = (buf[0] & 0x02)? 1: 0;
- middle = (buf[0] & 0x04)? 1: 0;
- right = (buf[0] & 0x08)? 1: 0;
- touch = (buf[0] & 0x10)? 1: 0;
+ left = buf[0] & 0x02;
+ middle = buf[0] & 0x04;
+ right = buf[0] & 0x08;
+ touch = buf[0] & 0x10;
- vsxxxaa_drop_bytes (mouse, 5);
+ vsxxxaa_drop_bytes(mouse, 5);
- DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
- mouse->name, mouse->phys, x, y,
- left? "L": "l", middle? "M": "m",
- right? "R": "r", touch? "T": "t");
+ DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
+ mouse->name, mouse->phys, x, y,
+ left ? "L" : "l", middle ? "M" : "m",
+ right ? "R" : "r", touch ? "T" : "t");
/*
* Report what we've found so far...
*/
- input_report_key (dev, BTN_LEFT, left);
- input_report_key (dev, BTN_MIDDLE, middle);
- input_report_key (dev, BTN_RIGHT, right);
- input_report_key (dev, BTN_TOUCH, touch);
- input_report_abs (dev, ABS_X, x);
- input_report_abs (dev, ABS_Y, y);
- input_sync (dev);
+ input_report_key(dev, BTN_LEFT, left);
+ input_report_key(dev, BTN_MIDDLE, middle);
+ input_report_key(dev, BTN_RIGHT, right);
+ input_report_key(dev, BTN_TOUCH, touch);
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_sync(dev);
}
-static void
-vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
+static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
@@ -356,24 +349,24 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
* (for three buttons) of byte 0. Maybe even the bit <3>
* has some meaning if a tablet is attached.
*/
- left = (buf[0] & 0x04)? 1: 0;
- middle = (buf[0] & 0x02)? 1: 0;
- right = (buf[0] & 0x01)? 1: 0;
+ left = buf[0] & 0x04;
+ middle = buf[0] & 0x02;
+ right = buf[0] & 0x01;
- vsxxxaa_drop_bytes (mouse, 4);
- vsxxxaa_detection_done (mouse);
+ vsxxxaa_drop_bytes(mouse, 4);
+ vsxxxaa_detection_done(mouse);
if (error <= 0x1f) {
/* No (serious) error. Report buttons */
- input_report_key (dev, BTN_LEFT, left);
- input_report_key (dev, BTN_MIDDLE, middle);
- input_report_key (dev, BTN_RIGHT, right);
- input_report_key (dev, BTN_TOUCH, 0);
- input_sync (dev);
+ input_report_key(dev, BTN_LEFT, left);
+ input_report_key(dev, BTN_MIDDLE, middle);
+ input_report_key(dev, BTN_RIGHT, right);
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
if (error != 0)
- printk (KERN_INFO "Your %s on %s reports error=0x%02x\n",
- mouse->name, mouse->phys, error);
+ printk(KERN_INFO "Your %s on %s reports error=0x%02x\n",
+ mouse->name, mouse->phys, error);
}
@@ -381,18 +374,18 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
* If the mouse was hot-plugged, we need to force differential mode
* now... However, give it a second to recover from it's reset.
*/
- printk (KERN_NOTICE "%s on %s: Forceing standard packet format, "
- "incremental streaming mode and 72 samples/sec\n",
- mouse->name, mouse->phys);
- serio_write (mouse->serio, 'S'); /* Standard format */
- mdelay (50);
- serio_write (mouse->serio, 'R'); /* Incremental */
- mdelay (50);
- serio_write (mouse->serio, 'L'); /* 72 samples/sec */
+ printk(KERN_NOTICE
+ "%s on %s: Forcing standard packet format, "
+ "incremental streaming mode and 72 samples/sec\n",
+ mouse->name, mouse->phys);
+ serio_write(mouse->serio, 'S'); /* Standard format */
+ mdelay(50);
+ serio_write(mouse->serio, 'R'); /* Incremental */
+ mdelay(50);
+ serio_write(mouse->serio, 'L'); /* 72 samples/sec */
}
-static void
-vsxxxaa_parse_buffer (struct vsxxxaa *mouse)
+static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse)
{
unsigned char *buf = mouse->buf;
int stray_bytes;
@@ -409,122 +402,107 @@ vsxxxaa_parse_buffer (struct vsxxxaa *mouse)
* activity on the mouse.
*/
while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
- printk (KERN_ERR "%s on %s: Dropping a byte to regain "
- "sync with mouse data stream...\n",
- mouse->name, mouse->phys);
- vsxxxaa_drop_bytes (mouse, 1);
+ printk(KERN_ERR "%s on %s: Dropping a byte to regain "
+ "sync with mouse data stream...\n",
+ mouse->name, mouse->phys);
+ vsxxxaa_drop_bytes(mouse, 1);
}
/*
* Check for packets we know about.
*/
- if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) {
+ if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, 3)) {
/* Check for broken packet */
- stray_bytes = vsxxxaa_check_packet (mouse, 3);
- if (stray_bytes > 0) {
- printk (KERN_ERR "Dropping %d bytes now...\n",
- stray_bytes);
- vsxxxaa_drop_bytes (mouse, stray_bytes);
- continue;
- }
-
- vsxxxaa_handle_REL_packet (mouse);
- continue; /* More to parse? */
- }
+ stray_bytes = vsxxxaa_check_packet(mouse, 3);
+ if (!stray_bytes)
+ vsxxxaa_handle_REL_packet(mouse);
- if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) {
+ } else if (vsxxxaa_smells_like_packet(mouse,
+ VSXXXAA_PACKET_ABS, 5)) {
/* Check for broken packet */
- stray_bytes = vsxxxaa_check_packet (mouse, 5);
- if (stray_bytes > 0) {
- printk (KERN_ERR "Dropping %d bytes now...\n",
- stray_bytes);
- vsxxxaa_drop_bytes (mouse, stray_bytes);
- continue;
- }
-
- vsxxxaa_handle_ABS_packet (mouse);
- continue; /* More to parse? */
- }
+ stray_bytes = vsxxxaa_check_packet(mouse, 5);
+ if (!stray_bytes)
+ vsxxxaa_handle_ABS_packet(mouse);
- if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) {
+ } else if (vsxxxaa_smells_like_packet(mouse,
+ VSXXXAA_PACKET_POR, 4)) {
/* Check for broken packet */
- stray_bytes = vsxxxaa_check_packet (mouse, 4);
- if (stray_bytes > 0) {
- printk (KERN_ERR "Dropping %d bytes now...\n",
- stray_bytes);
- vsxxxaa_drop_bytes (mouse, stray_bytes);
- continue;
- }
-
- vsxxxaa_handle_POR_packet (mouse);
- continue; /* More to parse? */
+ stray_bytes = vsxxxaa_check_packet(mouse, 4);
+ if (!stray_bytes)
+ vsxxxaa_handle_POR_packet(mouse);
+
+ } else {
+ break; /* No REL, ABS or POR packet found */
+ }
+
+ if (stray_bytes > 0) {
+ printk(KERN_ERR "Dropping %d bytes now...\n",
+ stray_bytes);
+ vsxxxaa_drop_bytes(mouse, stray_bytes);
}
- break; /* No REL, ABS or POR packet found */
} while (1);
}
-static irqreturn_t
-vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags)
+static irqreturn_t vsxxxaa_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
{
- struct vsxxxaa *mouse = serio_get_drvdata (serio);
+ struct vsxxxaa *mouse = serio_get_drvdata(serio);
- vsxxxaa_queue_byte (mouse, data);
- vsxxxaa_parse_buffer (mouse);
+ vsxxxaa_queue_byte(mouse, data);
+ vsxxxaa_parse_buffer(mouse);
return IRQ_HANDLED;
}
-static void
-vsxxxaa_disconnect (struct serio *serio)
+static void vsxxxaa_disconnect(struct serio *serio)
{
- struct vsxxxaa *mouse = serio_get_drvdata (serio);
+ struct vsxxxaa *mouse = serio_get_drvdata(serio);
- serio_close (serio);
- serio_set_drvdata (serio, NULL);
- input_unregister_device (mouse->dev);
- kfree (mouse);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(mouse->dev);
+ kfree(mouse);
}
-static int
-vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
+static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv)
{
struct vsxxxaa *mouse;
struct input_dev *input_dev;
int err = -ENOMEM;
- mouse = kzalloc (sizeof (struct vsxxxaa), GFP_KERNEL);
- input_dev = input_allocate_device ();
+ mouse = kzalloc(sizeof(struct vsxxxaa), GFP_KERNEL);
+ input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
mouse->dev = input_dev;
mouse->serio = serio;
- strlcat (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
- sizeof (mouse->name));
- snprintf (mouse->phys, sizeof (mouse->phys), "%s/input0", serio->phys);
+ strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
+ sizeof(mouse->name));
+ snprintf(mouse->phys, sizeof(mouse->phys), "%s/input0", serio->phys);
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->dev.parent = &serio->dev;
- set_bit (EV_KEY, input_dev->evbit); /* We have buttons */
- set_bit (EV_REL, input_dev->evbit);
- set_bit (EV_ABS, input_dev->evbit);
- set_bit (BTN_LEFT, input_dev->keybit); /* We have 3 buttons */
- set_bit (BTN_MIDDLE, input_dev->keybit);
- set_bit (BTN_RIGHT, input_dev->keybit);
- set_bit (BTN_TOUCH, input_dev->keybit); /* ...and Tablet */
- set_bit (REL_X, input_dev->relbit);
- set_bit (REL_Y, input_dev->relbit);
- input_set_abs_params (input_dev, ABS_X, 0, 1023, 0, 0);
- input_set_abs_params (input_dev, ABS_Y, 0, 1023, 0, 0);
-
- serio_set_drvdata (serio, mouse);
-
- err = serio_open (serio, drv);
+ __set_bit(EV_KEY, input_dev->evbit); /* We have buttons */
+ __set_bit(EV_REL, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(BTN_LEFT, input_dev->keybit); /* We have 3 buttons */
+ __set_bit(BTN_MIDDLE, input_dev->keybit);
+ __set_bit(BTN_RIGHT, input_dev->keybit);
+ __set_bit(BTN_TOUCH, input_dev->keybit); /* ...and Tablet */
+ __set_bit(REL_X, input_dev->relbit);
+ __set_bit(REL_Y, input_dev->relbit);
+ input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+
+ serio_set_drvdata(serio, mouse);
+
+ err = serio_open(serio, drv);
if (err)
goto fail2;
@@ -532,18 +510,18 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
* Request selftest. Standard packet format and differential
* mode will be requested after the device ID'ed successfully.
*/
- serio_write (serio, 'T'); /* Test */
+ serio_write(serio, 'T'); /* Test */
- err = input_register_device (input_dev);
+ err = input_register_device(input_dev);
if (err)
goto fail3;
return 0;
- fail3: serio_close (serio);
- fail2: serio_set_drvdata (serio, NULL);
- fail1: input_free_device (input_dev);
- kfree (mouse);
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(mouse);
return err;
}
@@ -570,18 +548,16 @@ static struct serio_driver vsxxxaa_drv = {
.disconnect = vsxxxaa_disconnect,
};
-static int __init
-vsxxxaa_init (void)
+static int __init vsxxxaa_init(void)
{
return serio_register_driver(&vsxxxaa_drv);
}
-static void __exit
-vsxxxaa_exit (void)
+static void __exit vsxxxaa_exit(void)
{
serio_unregister_driver(&vsxxxaa_drv);
}
-module_init (vsxxxaa_init);
-module_exit (vsxxxaa_exit);
+module_init(vsxxxaa_init);
+module_exit(vsxxxaa_exit);
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index aa533ceffe3..7e319d65ec5 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -201,4 +201,12 @@ config SERIO_XILINX_XPS_PS2
To compile this driver as a module, choose M here: the
module will be called xilinx_ps2.
+config SERIO_ALTERA_PS2
+ tristate "Altera UP PS/2 controller"
+ help
+ Say Y here if you have Altera University Program PS/2 ports.
+
+ To compile this driver as a module, choose M here: the
+ module will be called altera_ps2.
+
endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 9b6c8135955..bf945f789d0 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o
+obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c
new file mode 100644
index 00000000000..f479ea50919
--- /dev/null
+++ b/drivers/input/serio/altera_ps2.c
@@ -0,0 +1,200 @@
+/*
+ * Altera University Program PS2 controller driver
+ *
+ * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * Based on sa1111ps2.c, which is:
+ * Copyright (C) 2002 Russell King
+ *
+ * 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/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRV_NAME "altera_ps2"
+
+struct ps2if {
+ struct serio *io;
+ struct resource *iomem_res;
+ void __iomem *base;
+ unsigned irq;
+};
+
+/*
+ * Read all bytes waiting in the PS2 port. There should be
+ * at the most one, but we loop for safety.
+ */
+static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)
+{
+ struct ps2if *ps2if = dev_id;
+ unsigned int status;
+ int handled = IRQ_NONE;
+
+ while ((status = readl(ps2if->base)) & 0xffff0000) {
+ serio_interrupt(ps2if->io, status & 0xff, 0);
+ handled = IRQ_HANDLED;
+ }
+
+ return handled;
+}
+
+/*
+ * Write a byte to the PS2 port.
+ */
+static int altera_ps2_write(struct serio *io, unsigned char val)
+{
+ struct ps2if *ps2if = io->port_data;
+
+ writel(val, ps2if->base);
+ return 0;
+}
+
+static int altera_ps2_open(struct serio *io)
+{
+ struct ps2if *ps2if = io->port_data;
+
+ /* clear fifo */
+ while (readl(ps2if->base) & 0xffff0000)
+ /* empty */;
+
+ writel(1, ps2if->base + 4); /* enable rx irq */
+ return 0;
+}
+
+static void altera_ps2_close(struct serio *io)
+{
+ struct ps2if *ps2if = io->port_data;
+
+ writel(0, ps2if->base); /* disable rx irq */
+}
+
+/*
+ * Add one device to this driver.
+ */
+static int altera_ps2_probe(struct platform_device *pdev)
+{
+ struct ps2if *ps2if;
+ struct serio *serio;
+ int error;
+
+ ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!ps2if || !serio) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ serio->id.type = SERIO_8042;
+ serio->write = altera_ps2_write;
+ serio->open = altera_ps2_open;
+ serio->close = altera_ps2_close;
+ strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
+ strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
+ serio->port_data = ps2if;
+ serio->dev.parent = &pdev->dev;
+ ps2if->io = serio;
+
+ ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (ps2if->iomem_res == NULL) {
+ error = -ENOENT;
+ goto err_free_mem;
+ }
+
+ ps2if->irq = platform_get_irq(pdev, 0);
+ if (ps2if->irq < 0) {
+ error = -ENXIO;
+ goto err_free_mem;
+ }
+
+ if (!request_mem_region(ps2if->iomem_res->start,
+ resource_size(ps2if->iomem_res), pdev->name)) {
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ ps2if->base = ioremap(ps2if->iomem_res->start,
+ resource_size(ps2if->iomem_res));
+ if (!ps2if->base) {
+ error = -ENOMEM;
+ goto err_free_res;
+ }
+
+ error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if);
+ if (error) {
+ dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n",
+ ps2if->irq, error);
+ goto err_unmap;
+ }
+
+ dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq);
+
+ serio_register_port(ps2if->io);
+ platform_set_drvdata(pdev, ps2if);
+
+ return 0;
+
+ err_unmap:
+ iounmap(ps2if->base);
+ err_free_res:
+ release_mem_region(ps2if->iomem_res->start,
+ resource_size(ps2if->iomem_res));
+ err_free_mem:
+ kfree(ps2if);
+ kfree(serio);
+ return error;
+}
+
+/*
+ * Remove one device from this driver.
+ */
+static int altera_ps2_remove(struct platform_device *pdev)
+{
+ struct ps2if *ps2if = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ serio_unregister_port(ps2if->io);
+ free_irq(ps2if->irq, ps2if);
+ iounmap(ps2if->base);
+ release_mem_region(ps2if->iomem_res->start,
+ resource_size(ps2if->iomem_res));
+ kfree(ps2if);
+
+ return 0;
+}
+
+/*
+ * Our device driver structure
+ */
+static struct platform_driver altera_ps2_driver = {
+ .probe = altera_ps2_probe,
+ .remove = altera_ps2_remove,
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init altera_ps2_init(void)
+{
+ return platform_driver_register(&altera_ps2_driver);
+}
+
+static void __exit altera_ps2_exit(void)
+{
+ platform_driver_unregister(&altera_ps2_driver);
+}
+
+module_init(altera_ps2_init);
+module_exit(altera_ps2_exit);
+
+MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
+MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 1c9410d1822..bcc2d30ec24 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -955,7 +955,7 @@ static int __init hp_sdc_init_hppa(struct parisc_device *d)
INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed);
ret = hp_sdc_init();
- /* after sucessfull initialization give SDC some time to settle
+ /* after successfull initialization give SDC some time to settle
* and then load the hp_sdc_mlc upper layer driver */
if (!ret)
schedule_delayed_work(&moduleloader_work,
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
index 820e51673b2..7d2b820ef58 100644
--- a/drivers/input/serio/hp_sdc_mlc.c
+++ b/drivers/input/serio/hp_sdc_mlc.c
@@ -125,7 +125,7 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id,
break;
default:
- printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data);
+ printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data);
break;
}
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 2bcf1ace27c..7fbffe431bc 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -67,10 +67,12 @@ static inline void i8042_write_command(int val)
#include <linux/dmi.h>
-static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
{
- /* AUX LOOP command does not raise AUX IRQ */
- .ident = "Arima-Rioworks HDAMB",
+ /*
+ * Arima-Rioworks HDAMB -
+ * AUX LOOP command does not raise AUX IRQ
+ */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"),
DMI_MATCH(DMI_BOARD_NAME, "HDAMB"),
@@ -78,7 +80,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "ASUS G1S",
+ /* ASUS G1S */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
DMI_MATCH(DMI_BOARD_NAME, "G1S"),
@@ -86,8 +88,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- /* AUX LOOP command does not raise AUX IRQ */
- .ident = "ASUS P65UP5",
+ /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"),
@@ -95,7 +96,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "Compaq Proliant 8500",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
@@ -103,7 +103,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "Compaq Proliant DL760",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
@@ -111,7 +110,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "OQO Model 01",
+ /* OQO Model 01 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
@@ -119,8 +118,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- /* AUX LOOP does not work properly */
- .ident = "ULI EV4873",
+ /* ULI EV4873 - AUX LOOP does not work properly */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ULI"),
DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"),
@@ -128,7 +126,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "Microsoft Virtual Machine",
+ /* Microsoft Virtual Machine */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
@@ -136,7 +134,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "Medion MAM 2070",
+ /* Medion MAM 2070 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"),
@@ -144,7 +142,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "Blue FB5601",
+ /* Blue FB5601 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "blue"),
DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"),
@@ -152,7 +150,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "Gigabyte M912",
+ /* Gigabyte M912 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
@@ -160,7 +158,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
},
},
{
- .ident = "HP DV9700",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
@@ -177,72 +174,72 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
* ... apparently some Toshibas don't like MUX mode either and
* die horrible death on reboot.
*/
-static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
{
- .ident = "Fujitsu Lifebook P7010/P7010D",
+ /* Fujitsu Lifebook P7010/P7010D */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "P7010"),
},
},
{
- .ident = "Fujitsu Lifebook P7010",
+ /* Fujitsu Lifebook P7010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"),
},
},
{
- .ident = "Fujitsu Lifebook P5020D",
+ /* Fujitsu Lifebook P5020D */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"),
},
},
{
- .ident = "Fujitsu Lifebook S2000",
+ /* Fujitsu Lifebook S2000 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
},
},
{
- .ident = "Fujitsu Lifebook S6230",
+ /* Fujitsu Lifebook S6230 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
},
},
{
- .ident = "Fujitsu T70H",
+ /* Fujitsu T70H */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"),
},
},
{
- .ident = "Fujitsu-Siemens Lifebook T3010",
+ /* Fujitsu-Siemens Lifebook T3010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"),
},
},
{
- .ident = "Fujitsu-Siemens Lifebook E4010",
+ /* Fujitsu-Siemens Lifebook E4010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"),
},
},
{
- .ident = "Fujitsu-Siemens Amilo Pro 2010",
+ /* Fujitsu-Siemens Amilo Pro 2010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"),
},
},
{
- .ident = "Fujitsu-Siemens Amilo Pro 2030",
+ /* Fujitsu-Siemens Amilo Pro 2030 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
@@ -253,7 +250,7 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
* No data is coming from the touchscreen unless KBC
* is in legacy mode.
*/
- .ident = "Panasonic CF-29",
+ /* Panasonic CF-29 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
@@ -261,10 +258,10 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
},
{
/*
- * Errors on MUX ports are reported without raising AUXDATA
+ * HP Pavilion DV4017EA -
+ * errors on MUX ports are reported without raising AUXDATA
* causing "spurious NAK" messages.
*/
- .ident = "HP Pavilion DV4017EA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"),
@@ -272,9 +269,9 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
},
{
/*
- * Like DV4017EA does not raise AUXERR for errors on MUX ports.
+ * HP Pavilion ZT1000 -
+ * like DV4017EA does not raise AUXERR for errors on MUX ports.
*/
- .ident = "HP Pavilion ZT1000",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"),
@@ -283,44 +280,41 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
},
{
/*
- * Like DV4017EA does not raise AUXERR for errors on MUX ports.
+ * HP Pavilion DV4270ca -
+ * like DV4017EA does not raise AUXERR for errors on MUX ports.
*/
- .ident = "HP Pavilion DV4270ca",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"),
},
},
{
- .ident = "Toshiba P10",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
},
},
{
- .ident = "Toshiba Equium A110",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
},
},
{
- .ident = "Alienware Sentia",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
},
},
{
- .ident = "Sharp Actius MM20",
+ /* Sharp Actius MM20 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"),
},
},
{
- .ident = "Sony Vaio FS-115b",
+ /* Sony Vaio FS-115b */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
@@ -328,73 +322,72 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
},
{
/*
- * Reset and GET ID commands issued via KBD port are
+ * Sony Vaio FZ-240E -
+ * reset and GET ID commands issued via KBD port are
* sometimes being delivered to AUX3.
*/
- .ident = "Sony Vaio FZ-240E",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"),
},
},
{
- .ident = "Amoi M636/A737",
+ /* Amoi M636/A737 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
},
},
{
- .ident = "Lenovo 3000 n100",
+ /* Lenovo 3000 n100 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "076804U"),
},
},
{
- .ident = "Acer Aspire 1360",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
},
},
{
- .ident = "Gericom Bellagio",
+ /* Gericom Bellagio */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Gericom"),
DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"),
},
},
{
- .ident = "IBM 2656",
+ /* IBM 2656 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
DMI_MATCH(DMI_PRODUCT_NAME, "2656"),
},
},
{
- .ident = "Dell XPS M1530",
+ /* Dell XPS M1530 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"),
},
},
{
- .ident = "Compal HEL80I",
+ /* Compal HEL80I */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"),
DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
},
},
{
- .ident = "Dell Vostro 1510",
+ /* Dell Vostro 1510 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
},
},
{
- .ident = "Acer Aspire 5536",
+ /* Acer Aspire 5536 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"),
@@ -404,65 +397,65 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
{ }
};
-static struct dmi_system_id __initdata i8042_dmi_reset_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
{
- .ident = "MSI Wind U-100",
+ /* MSI Wind U-100 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "U-100"),
DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
},
},
{
- .ident = "LG Electronics X110",
+ /* LG Electronics X110 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "X110"),
DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."),
},
},
{
- .ident = "Acer Aspire One 150",
+ /* Acer Aspire One 150 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
},
},
{
- .ident = "Advent 4211",
+ /* Advent 4211 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"),
DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"),
},
},
{
- .ident = "Medion Akoya Mini E1210",
+ /* Medion Akoya Mini E1210 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
DMI_MATCH(DMI_PRODUCT_NAME, "E1210"),
},
},
{
- .ident = "Mivvy M310",
+ /* Mivvy M310 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"),
DMI_MATCH(DMI_PRODUCT_NAME, "N10"),
},
},
{
- .ident = "Dell Vostro 1320",
+ /* Dell Vostro 1320 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"),
},
},
{
- .ident = "Dell Vostro 1520",
+ /* Dell Vostro 1520 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"),
},
},
{
- .ident = "Dell Vostro 1720",
+ /* Dell Vostro 1720 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"),
@@ -472,16 +465,16 @@ static struct dmi_system_id __initdata i8042_dmi_reset_table[] = {
};
#ifdef CONFIG_PNP
-static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = {
{
- .ident = "Intel MBO Desktop D845PESV",
+ /* Intel MBO Desktop D845PESV */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "D845PESV"),
DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
},
},
{
- .ident = "MSI Wind U-100",
+ /* MSI Wind U-100 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "U-100"),
DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
@@ -490,27 +483,23 @@ static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = {
{ }
};
-static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
{
- .ident = "Portable",
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
},
},
{
- .ident = "Laptop",
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
},
},
{
- .ident = "Notebook",
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
},
},
{
- .ident = "Sub-Notebook",
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
},
@@ -525,58 +514,58 @@ static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = {
* Originally, this was just confined to older laptops, but a few Acer laptops
* have turned up in 2007 that also need this again.
*/
-static struct dmi_system_id __initdata i8042_dmi_dritek_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = {
{
- .ident = "Acer Aspire 5630",
+ /* Acer Aspire 5630 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
},
},
{
- .ident = "Acer Aspire 5650",
+ /* Acer Aspire 5650 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
},
},
{
- .ident = "Acer Aspire 5680",
+ /* Acer Aspire 5680 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
},
},
{
- .ident = "Acer Aspire 5720",
+ /* Acer Aspire 5720 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
},
},
{
- .ident = "Acer Aspire 9110",
+ /* Acer Aspire 9110 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
},
},
{
- .ident = "Acer TravelMate 660",
+ /* Acer TravelMate 660 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"),
},
},
{
- .ident = "Acer TravelMate 2490",
+ /* Acer TravelMate 2490 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
},
},
{
- .ident = "Acer TravelMate 4280",
+ /* Acer TravelMate 4280 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"),
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
new file mode 100644
index 00000000000..fbd3987af57
--- /dev/null
+++ b/drivers/input/sparse-keymap.c
@@ -0,0 +1,250 @@
+/*
+ * Generic support for sparse keymaps
+ *
+ * Copyright (c) 2009 Dmitry Torokhov
+ *
+ * Derived from wistron button driver:
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * 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/input.h>
+#include <linux/input/sparse-keymap.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("Generic support for sparse keymaps");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+
+/**
+ * sparse_keymap_entry_from_scancode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
+ unsigned int code)
+{
+ struct key_entry *key;
+
+ for (key = dev->keycode; key->type != KE_END; key++)
+ if (code == key->code)
+ return key;
+
+ return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_scancode);
+
+/**
+ * sparse_keymap_entry_from_keycode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @keycode: Key code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
+ unsigned int keycode)
+{
+ struct key_entry *key;
+
+ for (key = dev->keycode; key->type != KE_END; key++)
+ if (key->type == KE_KEY && keycode == key->keycode)
+ return key;
+
+ return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
+
+static int sparse_keymap_getkeycode(struct input_dev *dev,
+ int scancode, int *keycode)
+{
+ const struct key_entry *key =
+ sparse_keymap_entry_from_scancode(dev, scancode);
+
+ if (key && key->type == KE_KEY) {
+ *keycode = key->keycode;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int sparse_keymap_setkeycode(struct input_dev *dev,
+ int scancode, int keycode)
+{
+ struct key_entry *key;
+ int old_keycode;
+
+ if (keycode < 0 || keycode > KEY_MAX)
+ return -EINVAL;
+
+ key = sparse_keymap_entry_from_scancode(dev, scancode);
+ if (key && key->type == KE_KEY) {
+ old_keycode = key->keycode;
+ key->keycode = keycode;
+ set_bit(keycode, dev->keybit);
+ if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
+ clear_bit(old_keycode, dev->keybit);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * sparse_keymap_setup - set up sparse keymap for an input device
+ * @dev: Input device
+ * @keymap: Keymap in form of array of &key_entry structures ending
+ * with %KE_END type entry
+ * @setup: Function that can be used to adjust keymap entries
+ * depending on device's deeds, may be %NULL
+ *
+ * The function calculates size and allocates copy of the original
+ * keymap after which sets up input device event bits appropriately.
+ * Before destroying input device allocated keymap should be freed
+ * with a call to sparse_keymap_free().
+ */
+int sparse_keymap_setup(struct input_dev *dev,
+ const struct key_entry *keymap,
+ int (*setup)(struct input_dev *, struct key_entry *))
+{
+ size_t map_size = 1; /* to account for the last KE_END entry */
+ const struct key_entry *e;
+ struct key_entry *map, *entry;
+ int i;
+ int error;
+
+ for (e = keymap; e->type != KE_END; e++)
+ map_size++;
+
+ map = kcalloc(map_size, sizeof (struct key_entry), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ memcpy(map, keymap, map_size * sizeof (struct key_entry));
+
+ for (i = 0; i < map_size; i++) {
+ entry = &map[i];
+
+ if (setup) {
+ error = setup(dev, entry);
+ if (error)
+ goto err_out;
+ }
+
+ switch (entry->type) {
+ case KE_KEY:
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(entry->keycode, dev->keybit);
+ break;
+
+ case KE_SW:
+ __set_bit(EV_SW, dev->evbit);
+ __set_bit(entry->sw.code, dev->swbit);
+ break;
+ }
+ }
+
+ dev->keycode = map;
+ dev->keycodemax = map_size;
+ dev->getkeycode = sparse_keymap_getkeycode;
+ dev->setkeycode = sparse_keymap_setkeycode;
+
+ return 0;
+
+ err_out:
+ kfree(keymap);
+ return error;
+
+}
+EXPORT_SYMBOL(sparse_keymap_setup);
+
+/**
+ * sparse_keymap_free - free memory allocated for sparse keymap
+ * @dev: Input device using sparse keymap
+ *
+ * This function is used to free memory allocated by sparse keymap
+ * in an input device that was set up by sparse_keymap_setup().
+ */
+void sparse_keymap_free(struct input_dev *dev)
+{
+ kfree(dev->keycode);
+ dev->keycode = NULL;
+ dev->keycodemax = 0;
+ dev->getkeycode = NULL;
+ dev->setkeycode = NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_free);
+
+/**
+ * sparse_keymap_report_entry - report event corresponding to given key entry
+ * @dev: Input device for which event should be reported
+ * @ke: key entry describing event
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ * entries right after reporting press event, ignored by all other
+ * entries
+ *
+ * This function is used to report input event described by given
+ * &struct key_entry.
+ */
+void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
+ unsigned int value, bool autorelease)
+{
+ switch (ke->type) {
+ case KE_KEY:
+ input_report_key(dev, ke->keycode, value);
+ input_sync(dev);
+ if (value && autorelease) {
+ input_report_key(dev, ke->keycode, 0);
+ input_sync(dev);
+ }
+ break;
+
+ case KE_SW:
+ value = ke->sw.value;
+ /* fall through */
+
+ case KE_VSW:
+ input_report_switch(dev, ke->sw.code, value);
+ break;
+ }
+}
+EXPORT_SYMBOL(sparse_keymap_report_entry);
+
+/**
+ * sparse_keymap_report_event - report event corresponding to given scancode
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ * entries right after reporting press event, ignored by all other
+ * entries
+ *
+ * This function is used to perform lookup in an input device using sparse
+ * keymap and report corresponding event. Returns %true if lookup was
+ * successful and %false otherwise.
+ */
+bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
+ unsigned int value, bool autorelease)
+{
+ const struct key_entry *ke =
+ sparse_keymap_entry_from_scancode(dev, code);
+
+ if (ke) {
+ sparse_keymap_report_entry(dev, ke, value, autorelease);
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(sparse_keymap_report_event);
+
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 8cc453c85ea..32fc8ba039a 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -111,6 +111,18 @@ config TOUCHSCREEN_DA9034
Say Y here to enable the support for the touchscreen found
on Dialog Semiconductor DA9034 PMIC.
+config TOUCHSCREEN_DYNAPRO
+ tristate "Dynapro serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Dynapro serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dynapro.
+
config TOUCHSCREEN_EETI
tristate "EETI touchscreen panel support"
depends on I2C
@@ -133,6 +145,18 @@ config TOUCHSCREEN_FUJITSU
To compile this driver as a module, choose M here: the
module will be called fujitsu-ts.
+config TOUCHSCREEN_S3C2410
+ tristate "Samsung S3C2410 touchscreen input driver"
+ depends on ARCH_S3C2410
+ select S3C24XX_ADC
+ help
+ Say Y here if you have the s3c2410 touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s3c2410_ts.
+
config TOUCHSCREEN_GUNZE
tristate "Gunze AHL-51S touchscreen"
select SERIO
@@ -297,7 +321,7 @@ config TOUCHSCREEN_TOUCHWIN
config TOUCHSCREEN_ATMEL_TSADCC
tristate "Atmel Touchscreen Interface"
- depends on ARCH_AT91SAM9RL
+ depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
Say Y here if you have a 4-wire touchscreen connected to the
ADC Controller on your Atmel SoC (such as the AT91SAM9RL).
@@ -418,6 +442,7 @@ config TOUCHSCREEN_USB_COMPOSITE
- IdealTEK URTC1000
- GoTop Super_Q2/GogoPen/PenPower tablets
- JASTEC USB Touch Controller/DigiTech DTR-02U
+ - Zytronic controllers
Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff.
@@ -490,6 +515,16 @@ config TOUCHSCREEN_USB_E2I
bool "e2i Touchscreen controller (e.g. from Mimo 740)"
depends on TOUCHSCREEN_USB_COMPOSITE
+config TOUCHSCREEN_USB_ZYTRONIC
+ default y
+ bool "Zytronic controller" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETT_TC5UH
+ default y
+ bool "ET&T TC5UH touchscreen controler support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
config TOUCHSCREEN_TOUCHIT213
tristate "Sahara TouchIT-213 touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 15fa62cffc7..f1f59c9e121 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
+obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
@@ -25,7 +26,9 @@ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
+obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
+obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
@@ -41,4 +44,3 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
-obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 09c810999b9..52d2ca147d8 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -29,10 +29,9 @@
#include <linux/spi/ads7846.h>
#include <asm/irq.h>
-
/*
* This code has been heavily tested on a Nokia 770, and lightly
- * tested on other ads7846 devices (OSK/Mistral, Lubbock).
+ * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz).
* TSC2046 is just newer ads7846 silicon.
* Support for ads7843 tested on Atmel at91sam926x-EK.
* Support for ads7845 has only been stubbed in.
@@ -43,7 +42,7 @@
* have to maintain our own SW IRQ disabled status. This should be
* removed as soon as the affected platform's IRQ handling is fixed.
*
- * app note sbaa036 talks in more detail about accurate sampling...
+ * App note sbaa036 talks in more detail about accurate sampling...
* that ought to help in situations like LCDs inducing noise (which
* can also be helped by using synch signals) and more generally.
* This driver tries to utilize the measures described in the app
@@ -566,10 +565,8 @@ static void ads7846_rx(void *ads)
* once more the measurement
*/
if (packet->tc.ignore || Rt > ts->pressure_max) {
-#ifdef VERBOSE
- pr_debug("%s: ignored %d pressure %d\n",
- dev_name(&ts->spi->dev), packet->tc.ignore, Rt);
-#endif
+ dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
+ packet->tc.ignore, Rt);
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
HRTIMER_MODE_REL);
return;
@@ -598,9 +595,7 @@ static void ads7846_rx(void *ads)
if (!ts->pendown) {
input_report_key(input, BTN_TOUCH, 1);
ts->pendown = 1;
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "DOWN\n");
-#endif
+ dev_vdbg(&ts->spi->dev, "DOWN\n");
}
if (ts->swap_xy)
@@ -608,12 +603,10 @@ static void ads7846_rx(void *ads)
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
- input_report_abs(input, ABS_PRESSURE, Rt);
+ input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
input_sync(input);
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
-#endif
+ dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
}
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
@@ -723,9 +716,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
input_sync(input);
ts->pendown = 0;
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "UP\n");
-#endif
+ dev_vdbg(&ts->spi->dev, "UP\n");
}
/* measurement cycle ended */
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
index 35377f583e2..a12242f77e2 100644
--- a/drivers/input/touchscreen/atmel-wm97xx.c
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -59,7 +59,7 @@
#define ATMEL_WM97XX_AC97C_IRQ (29)
#define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */
#else
-#error Unkown CPU, this driver only supports AT32AP700X CPUs.
+#error Unknown CPU, this driver only supports AT32AP700X CPUs.
#endif
struct continuous {
diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c
index 9c7fce4d74d..3d9b5166ebe 100644
--- a/drivers/input/touchscreen/atmel_tsadcc.c
+++ b/drivers/input/touchscreen/atmel_tsadcc.c
@@ -22,6 +22,8 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
/* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */
@@ -36,7 +38,9 @@
#define ATMEL_TSADCC_LOWRES (1 << 4) /* Resolution selection */
#define ATMEL_TSADCC_SLEEP (1 << 5) /* Sleep mode */
#define ATMEL_TSADCC_PENDET (1 << 6) /* Pen Detect selection */
+#define ATMEL_TSADCC_PRES (1 << 7) /* Pressure Measurement Selection */
#define ATMEL_TSADCC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */
+#define ATMEL_TSADCC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */
#define ATMEL_TSADCC_STARTUP (0x7f << 16) /* Start Up time */
#define ATMEL_TSADCC_SHTIM (0xf << 24) /* Sample & Hold time */
#define ATMEL_TSADCC_PENDBC (0xf << 28) /* Pen Detect debouncing time */
@@ -84,7 +88,13 @@
#define ATMEL_TSADCC_CDR4 0x40 /* Channel Data 4 */
#define ATMEL_TSADCC_CDR5 0x44 /* Channel Data 5 */
-#define ADC_CLOCK 1000000
+#define ATMEL_TSADCC_XPOS 0x50
+#define ATMEL_TSADCC_Z1DAT 0x54
+#define ATMEL_TSADCC_Z2DAT 0x58
+
+#define PRESCALER_VAL(x) ((x) >> 8)
+
+#define ADC_DEFAULT_CLOCK 100000
struct atmel_tsadcc {
struct input_dev *input;
@@ -172,6 +182,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
struct atmel_tsadcc *ts_dev;
struct input_dev *input_dev;
struct resource *res;
+ struct at91_tsadcc_data *pdata = pdev->dev.platform_data;
int err = 0;
unsigned int prsc;
unsigned int reg;
@@ -242,31 +253,49 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
input_dev->phys = ts_dev->phys;
input_dev->dev.parent = &pdev->dev;
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-
+ __set_bit(EV_ABS, input_dev->evbit);
input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
/* clk_enable() always returns 0, no need to check it */
clk_enable(ts_dev->clk);
prsc = clk_get_rate(ts_dev->clk);
dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc);
- prsc = prsc / ADC_CLOCK / 2 - 1;
+ if (!pdata)
+ goto err_fail;
+
+ if (!pdata->adc_clock)
+ pdata->adc_clock = ADC_DEFAULT_CLOCK;
+
+ prsc = (prsc / (2 * pdata->adc_clock)) - 1;
+
+ /* saturate if this value is too high */
+ if (cpu_is_at91sam9rl()) {
+ if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL))
+ prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL);
+ } else {
+ if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL))
+ prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL);
+ }
+
+ dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc);
reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE |
((0x00 << 5) & ATMEL_TSADCC_SLEEP) | /* Normal Mode */
((0x01 << 6) & ATMEL_TSADCC_PENDET) | /* Enable Pen Detect */
- ((prsc << 8) & ATMEL_TSADCC_PRESCAL) | /* PRESCAL */
- ((0x13 << 16) & ATMEL_TSADCC_STARTUP) | /* STARTUP */
- ((0x0F << 28) & ATMEL_TSADCC_PENDBC); /* PENDBC */
+ (prsc << 8) |
+ ((0x26 << 16) & ATMEL_TSADCC_STARTUP) |
+ ((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC);
atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST);
atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE);
- atmel_tsadcc_write(ATMEL_TSADCC_TSR, (0x3 << 24) & ATMEL_TSADCC_TSSHTIM);
+ atmel_tsadcc_write(ATMEL_TSADCC_TSR,
+ (pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM);
atmel_tsadcc_read(ATMEL_TSADCC_SR);
atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c
new file mode 100644
index 00000000000..455353908bd
--- /dev/null
+++ b/drivers/input/touchscreen/dynapro.c
@@ -0,0 +1,206 @@
+/*
+ * Dynapro serial touchscreen driver
+ *
+ * Copyright (c) 2009 Tias Guns
+ * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and
+ * Richard Lemon
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2009/09/19 Tias Guns <tias@ulyssis.org>
+ * Copied inexio.c and edited for Dynapro protocol (from retired Xorg module)
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Dynapro serial touchscreen driver"
+
+MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define DYNAPRO_FORMAT_TOUCH_BIT 0x40
+#define DYNAPRO_FORMAT_LENGTH 3
+#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80
+
+#define DYNAPRO_MIN_XC 0
+#define DYNAPRO_MAX_XC 0x3ff
+#define DYNAPRO_MIN_YC 0
+#define DYNAPRO_MAX_YC 0x3ff
+
+#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4))
+#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7))
+#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct dynapro {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[DYNAPRO_FORMAT_LENGTH];
+ char phys[32];
+};
+
+static void dynapro_process_data(struct dynapro *pdynapro)
+{
+ struct input_dev *dev = pdynapro->dev;
+
+ if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) {
+ input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data));
+ input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data));
+ input_report_key(dev, BTN_TOUCH,
+ DYNAPRO_GET_TOUCHED(pdynapro->data));
+ input_sync(dev);
+
+ pdynapro->idx = 0;
+ }
+}
+
+static irqreturn_t dynapro_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+ pdynapro->data[pdynapro->idx] = data;
+
+ if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0])
+ dynapro_process_data(pdynapro);
+ else
+ dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+ pdynapro->data[0]);
+
+ return IRQ_HANDLED;
+}
+
+static void dynapro_disconnect(struct serio *serio)
+{
+ struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+ input_get_device(pdynapro->dev);
+ input_unregister_device(pdynapro->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(pdynapro->dev);
+ kfree(pdynapro);
+}
+
+/*
+ * dynapro_connect() is the routine that is called when someone adds a
+ * new serio device that supports dynapro protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int dynapro_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct dynapro *pdynapro;
+ struct input_dev *input_dev;
+ int err;
+
+ pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pdynapro || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pdynapro->serio = serio;
+ pdynapro->dev = input_dev;
+ snprintf(pdynapro->phys, sizeof(pdynapro->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "Dynapro Serial TouchScreen";
+ input_dev->phys = pdynapro->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_DYNAPRO;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(pdynapro->dev, ABS_X,
+ DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0);
+ input_set_abs_params(pdynapro->dev, ABS_Y,
+ DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, pdynapro);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pdynapro->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pdynapro);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id dynapro_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_DYNAPRO,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, dynapro_serio_ids);
+
+static struct serio_driver dynapro_drv = {
+ .driver = {
+ .name = "dynapro",
+ },
+ .description = DRIVER_DESC,
+ .id_table = dynapro_serio_ids,
+ .interrupt = dynapro_interrupt,
+ .connect = dynapro_connect,
+ .disconnect = dynapro_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init dynapro_init(void)
+{
+ return serio_register_driver(&dynapro_drv);
+}
+
+static void __exit dynapro_exit(void)
+{
+ serio_unregister_driver(&dynapro_drv);
+}
+
+module_init(dynapro_init);
+module_exit(dynapro_exit);
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
index 8fc3b08deb3..6cdcf2a6e03 100644
--- a/drivers/input/touchscreen/mainstone-wm97xx.c
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -14,7 +14,7 @@
*
* Notes:
* This is a wm97xx extended touch driver to capture touch
- * data in a continuous manner on the Intel XScale archictecture
+ * data in a continuous manner on the Intel XScale architecture
*
* Features:
* - codecs supported:- WM9705, WM9712, WM9713
@@ -131,7 +131,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
/* When the AC97 queue has been drained we need to allow time
* to buffer up samples otherwise we end up spinning polling
* for samples. The controller can't have a suitably low
- * threashold set to use the notifications it gives.
+ * threshold set to use the notifications it gives.
*/
schedule_timeout_uninterruptible(1);
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
new file mode 100644
index 00000000000..6386b441ef8
--- /dev/null
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -0,0 +1,457 @@
+/*
+ * Samsung S3C24XX touchscreen driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the term of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright 2008 Ben Dooks <ben-linux@fluff.org>
+ * Copyright 2009 Simtec Electronics <linux@simtec.co.uk>
+ *
+ * Additional work by Herbert Pötzl <herbert@13thfloor.at> and
+ * Harald Welte <laforge@openmoko.org>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <plat/adc.h>
+#include <plat/regs-adc.h>
+
+#include <mach/regs-gpio.h>
+#include <mach/ts.h>
+
+#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
+
+#define INT_DOWN (0)
+#define INT_UP (1 << 8)
+
+#define WAIT4INT (S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_AUTO_PST | \
+ S3C2410_ADCTSC_XY_PST(0))
+
+/* Per-touchscreen data. */
+
+/**
+ * struct s3c2410ts - driver touchscreen state.
+ * @client: The ADC client we registered with the core driver.
+ * @dev: The device we are bound to.
+ * @input: The input device we registered with the input subsystem.
+ * @clock: The clock for the adc.
+ * @io: Pointer to the IO base.
+ * @xp: The accumulated X position data.
+ * @yp: The accumulated Y position data.
+ * @irq_tc: The interrupt number for pen up/down interrupt
+ * @count: The number of samples collected.
+ * @shift: The log2 of the maximum count to read in one go.
+ */
+struct s3c2410ts {
+ struct s3c_adc_client *client;
+ struct device *dev;
+ struct input_dev *input;
+ struct clk *clock;
+ void __iomem *io;
+ unsigned long xp;
+ unsigned long yp;
+ int irq_tc;
+ int count;
+ int shift;
+};
+
+static struct s3c2410ts ts;
+
+/**
+ * s3c2410_ts_connect - configure gpio for s3c2410 systems
+ *
+ * Configure the GPIO for the S3C2410 system, where we have external FETs
+ * connected to the device (later systems such as the S3C2440 integrate
+ * these into the device).
+*/
+static inline void s3c2410_ts_connect(void)
+{
+ s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON);
+ s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON);
+ s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON);
+ s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON);
+}
+
+/**
+ * get_down - return the down state of the pen
+ * @data0: The data read from ADCDAT0 register.
+ * @data1: The data read from ADCDAT1 register.
+ *
+ * Return non-zero if both readings show that the pen is down.
+ */
+static inline bool get_down(unsigned long data0, unsigned long data1)
+{
+ /* returns true if both data values show stylus down */
+ return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
+ !(data1 & S3C2410_ADCDAT0_UPDOWN));
+}
+
+static void touch_timer_fire(unsigned long data)
+{
+ unsigned long data0;
+ unsigned long data1;
+ bool down;
+
+ data0 = readl(ts.io + S3C2410_ADCDAT0);
+ data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+ down = get_down(data0, data1);
+
+ if (ts.count == (1 << ts.shift)) {
+ ts.xp >>= ts.shift;
+ ts.yp >>= ts.shift;
+
+ dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
+ __func__, ts.xp, ts.yp, ts.count);
+
+ input_report_abs(ts.input, ABS_X, ts.xp);
+ input_report_abs(ts.input, ABS_Y, ts.yp);
+
+ input_report_key(ts.input, BTN_TOUCH, 1);
+ input_sync(ts.input);
+
+ ts.xp = 0;
+ ts.yp = 0;
+ ts.count = 0;
+ }
+
+ if (down) {
+ s3c_adc_start(ts.client, 0, 1 << ts.shift);
+ } else {
+ ts.count = 0;
+
+ input_report_key(ts.input, BTN_TOUCH, 0);
+ input_sync(ts.input);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+ }
+}
+
+static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
+
+/**
+ * stylus_irq - touchscreen stylus event interrupt
+ * @irq: The interrupt number
+ * @dev_id: The device ID.
+ *
+ * Called when the IRQ_TC is fired for a pen up or down event.
+ */
+static irqreturn_t stylus_irq(int irq, void *dev_id)
+{
+ unsigned long data0;
+ unsigned long data1;
+ bool down;
+
+ data0 = readl(ts.io + S3C2410_ADCDAT0);
+ data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+ down = get_down(data0, data1);
+
+ /* TODO we should never get an interrupt with down set while
+ * the timer is running, but maybe we ought to verify that the
+ * timer isn't running anyways. */
+
+ if (down)
+ s3c_adc_start(ts.client, 0, 1 << ts.shift);
+ else
+ dev_info(ts.dev, "%s: count=%d\n", __func__, ts.count);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * s3c24xx_ts_conversion - ADC conversion callback
+ * @client: The client that was registered with the ADC core.
+ * @data0: The reading from ADCDAT0.
+ * @data1: The reading from ADCDAT1.
+ * @left: The number of samples left.
+ *
+ * Called when a conversion has finished.
+ */
+static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
+ unsigned data0, unsigned data1,
+ unsigned *left)
+{
+ dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);
+
+ ts.xp += data0;
+ ts.yp += data1;
+
+ ts.count++;
+
+ /* From tests, it seems that it is unlikely to get a pen-up
+ * event during the conversion process which means we can
+ * ignore any pen-up events with less than the requisite
+ * count done.
+ *
+ * In several thousand conversions, no pen-ups where detected
+ * before count completed.
+ */
+}
+
+/**
+ * s3c24xx_ts_select - ADC selection callback.
+ * @client: The client that was registered with the ADC core.
+ * @select: The reason for select.
+ *
+ * Called when the ADC core selects (or deslects) us as a client.
+ */
+static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
+{
+ if (select) {
+ writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+ ts.io + S3C2410_ADCTSC);
+ } else {
+ mod_timer(&touch_timer, jiffies+1);
+ writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
+ }
+}
+
+/**
+ * s3c2410ts_probe - device core probe entry point
+ * @pdev: The device we are being bound to.
+ *
+ * Initialise, find and allocate any resources we need to run and then
+ * register with the ADC and input systems.
+ */
+static int __devinit s3c2410ts_probe(struct platform_device *pdev)
+{
+ struct s3c2410_ts_mach_info *info;
+ struct device *dev = &pdev->dev;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int ret = -EINVAL;
+
+ /* Initialise input stuff */
+ memset(&ts, 0, sizeof(struct s3c2410ts));
+
+ ts.dev = dev;
+
+ info = pdev->dev.platform_data;
+ if (!info) {
+ dev_err(dev, "no platform data, cannot attach\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "initialising touchscreen\n");
+
+ ts.clock = clk_get(dev, "adc");
+ if (IS_ERR(ts.clock)) {
+ dev_err(dev, "cannot get adc clock source\n");
+ return -ENOENT;
+ }
+
+ clk_enable(ts.clock);
+ dev_dbg(dev, "got and enabled clocks\n");
+
+ ts.irq_tc = ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "no resource for interrupt\n");
+ goto err_clk;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "no resource for registers\n");
+ ret = -ENOENT;
+ goto err_clk;
+ }
+
+ ts.io = ioremap(res->start, resource_size(res));
+ if (ts.io == NULL) {
+ dev_err(dev, "cannot map registers\n");
+ ret = -ENOMEM;
+ goto err_clk;
+ }
+
+ /* Configure the touchscreen external FETs on the S3C2410 */
+ if (!platform_get_device_id(pdev)->driver_data)
+ s3c2410_ts_connect();
+
+ ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
+ s3c24xx_ts_conversion, 1);
+ if (IS_ERR(ts.client)) {
+ dev_err(dev, "failed to register adc client\n");
+ ret = PTR_ERR(ts.client);
+ goto err_iomap;
+ }
+
+ /* Initialise registers */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(dev, "Unable to allocate the input device !!\n");
+ ret = -ENOMEM;
+ goto err_iomap;
+ }
+
+ ts.input = input_dev;
+ ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
+ input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
+
+ ts.input->name = "S3C24XX TouchScreen";
+ ts.input->id.bustype = BUS_HOST;
+ ts.input->id.vendor = 0xDEAD;
+ ts.input->id.product = 0xBEEF;
+ ts.input->id.version = 0x0102;
+
+ ts.shift = info->oversampling_shift;
+
+ ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,
+ "s3c2410_ts_pen", ts.input);
+ if (ret) {
+ dev_err(dev, "cannot get TC interrupt\n");
+ goto err_inputdev;
+ }
+
+ dev_info(dev, "driver attached, registering input device\n");
+
+ /* All went ok, so register to the input system */
+ ret = input_register_device(ts.input);
+ if (ret < 0) {
+ dev_err(dev, "failed to register input device\n");
+ ret = -EIO;
+ goto err_tcirq;
+ }
+
+ return 0;
+
+ err_tcirq:
+ free_irq(ts.irq_tc, ts.input);
+ err_inputdev:
+ input_unregister_device(ts.input);
+ err_iomap:
+ iounmap(ts.io);
+ err_clk:
+ del_timer_sync(&touch_timer);
+ clk_put(ts.clock);
+ return ret;
+}
+
+/**
+ * s3c2410ts_remove - device core removal entry point
+ * @pdev: The device we are being removed from.
+ *
+ * Free up our state ready to be removed.
+ */
+static int __devexit s3c2410ts_remove(struct platform_device *pdev)
+{
+ free_irq(ts.irq_tc, ts.input);
+ del_timer_sync(&touch_timer);
+
+ clk_disable(ts.clock);
+ clk_put(ts.clock);
+
+ input_unregister_device(ts.input);
+ iounmap(ts.io);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410ts_suspend(struct device *dev)
+{
+ writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC);
+ disable_irq(ts.irq_tc);
+ clk_disable(ts.clock);
+
+ return 0;
+}
+
+static int s3c2410ts_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s3c2410_ts_mach_info *info = pdev->dev.platform_data;
+
+ clk_enable(ts.clock);
+
+ /* Initialise registers */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+ writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+ return 0;
+}
+
+static struct dev_pm_ops s3c_ts_pmops = {
+ .suspend = s3c2410ts_suspend,
+ .resume = s3c2410ts_resume,
+};
+#endif
+
+static struct platform_device_id s3cts_driver_ids[] = {
+ { "s3c2410-ts", 0 },
+ { "s3c2440-ts", 1 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
+
+static struct platform_driver s3c_ts_driver = {
+ .driver = {
+ .name = "s3c24xx-ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &s3c_ts_pmops,
+#endif
+ },
+ .id_table = s3cts_driver_ids,
+ .probe = s3c2410ts_probe,
+ .remove = __devexit_p(s3c2410ts_remove),
+};
+
+static int __init s3c2410ts_init(void)
+{
+ return platform_driver_register(&s3c_ts_driver);
+}
+
+static void __exit s3c2410ts_exit(void)
+{
+ platform_driver_unregister(&s3c_ts_driver);
+}
+
+module_init(s3c2410ts_init);
+module_exit(s3c2410ts_exit);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "
+ "Ben Dooks <ben@simtec.co.uk>, "
+ "Simtec Electronics <linux@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C24XX Touchscreen driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index 095f84b1f56..89dcbe7b4b0 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -355,10 +355,13 @@ static int ucb1400_ts_probe(struct platform_device *dev)
goto err;
}
- error = ucb1400_ts_detect_irq(ucb);
- if (error) {
- printk(KERN_ERR "UCB1400: IRQ probe failed\n");
- goto err_free_devs;
+ /* Only in case the IRQ line wasn't supplied, try detecting it */
+ if (ucb->irq < 0) {
+ error = ucb1400_ts_detect_irq(ucb);
+ if (error) {
+ printk(KERN_ERR "UCB1400: IRQ probe failed\n");
+ goto err_free_devs;
+ }
}
init_waitqueue_head(&ucb->ts_wait);
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index 68ece5801a5..09a5e7341bd 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -14,6 +14,7 @@
* - General Touch
* - GoTop Super_Q2/GogoPen/PenPower tablets
* - JASTEC USB touch controller/DigiTech DTR-02U
+ * - Zytronic capacitive touchscreen
*
* Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -73,6 +74,15 @@ struct usbtouch_device_info {
int min_press, max_press;
int rept_size;
+ /*
+ * Always service the USB devices irq not just when the input device is
+ * open. This is useful when devices have a watchdog which prevents us
+ * from periodically polling the device. Leave this unset unless your
+ * touchscreen device requires it, as it does consume more of the USB
+ * bandwidth.
+ */
+ bool irq_always;
+
void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
/*
@@ -121,6 +131,8 @@ enum {
DEVTYPE_GOTOP,
DEVTYPE_JASTEC,
DEVTYPE_E2I,
+ DEVTYPE_ZYTRONIC,
+ DEVTYPE_TC5UH,
};
#define USB_DEVICE_HID_CLASS(vend, prod) \
@@ -201,6 +213,15 @@ static struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_E2I
{USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I},
#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+ {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
+ {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH},
+#endif
+
{}
};
@@ -538,6 +559,19 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
}
#endif
+/*****************************************************************************
+ * ET&T TC5UH part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
+static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
+ dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
/*****************************************************************************
* IdealTEK URTC1000 Part
@@ -621,6 +655,39 @@ static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
}
#endif
+/*****************************************************************************
+ * Zytronic Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ switch (pkt[0]) {
+ case 0x3A: /* command response */
+ dbg("%s: Command response %d", __func__, pkt[1]);
+ break;
+
+ case 0xC0: /* down */
+ dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+ dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+ dev->touch = 1;
+ dbg("%s: down %d,%d", __func__, dev->x, dev->y);
+ return 1;
+
+ case 0x80: /* up */
+ dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+ dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+ dev->touch = 0;
+ dbg("%s: up %d,%d", __func__, dev->x, dev->y);
+ return 1;
+
+ default:
+ dbg("%s: Unknown return %d", __func__, pkt[0]);
+ break;
+ }
+
+ return 0;
+}
+#endif
/*****************************************************************************
* the different device descriptors
@@ -783,6 +850,29 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.read_data = e2i_read_data,
},
#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+ [DEVTYPE_ZYTRONIC] = {
+ .min_xc = 0x0,
+ .max_xc = 0x03ff,
+ .min_yc = 0x0,
+ .max_yc = 0x03ff,
+ .rept_size = 5,
+ .read_data = zytronic_read_data,
+ .irq_always = true,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
+ [DEVTYPE_TC5UH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 5,
+ .read_data = tc5uh_read_data,
+ },
+#endif
};
@@ -933,8 +1023,10 @@ static int usbtouch_open(struct input_dev *input)
usbtouch->irq->dev = usbtouch->udev;
- if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
- return -EIO;
+ if (!usbtouch->type->irq_always) {
+ if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
+ return -EIO;
+ }
return 0;
}
@@ -943,7 +1035,8 @@ static void usbtouch_close(struct input_dev *input)
{
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
- usb_kill_urb(usbtouch->irq);
+ if (!usbtouch->type->irq_always)
+ usb_kill_urb(usbtouch->irq);
}
@@ -1066,6 +1159,9 @@ static int usbtouch_probe(struct usb_interface *intf,
usb_set_intfdata(intf, usbtouch);
+ if (usbtouch->type->irq_always)
+ usb_submit_urb(usbtouch->irq, GFP_KERNEL);
+
return 0;
out_free_buffers:
@@ -1087,7 +1183,7 @@ static void usbtouch_disconnect(struct usb_interface *intf)
dbg("%s - usbtouch is initialized, cleaning up", __func__);
usb_set_intfdata(intf, NULL);
- usb_kill_urb(usbtouch->irq);
+ /* this will stop IO via close */
input_unregister_device(usbtouch->input);
usb_free_urb(usbtouch->irq);
usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c
index 41e4359c277..eca54dbdf49 100644
--- a/drivers/input/touchscreen/zylonite-wm97xx.c
+++ b/drivers/input/touchscreen/zylonite-wm97xx.c
@@ -96,7 +96,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
/* When the AC97 queue has been drained we need to allow time
* to buffer up samples otherwise we end up spinning polling
* for samples. The controller can't have a suitably low
- * threashold set to use the notifications it gives.
+ * threshold set to use the notifications it gives.
*/
msleep(1);
diff --git a/drivers/isdn/act2000/Kconfig b/drivers/isdn/act2000/Kconfig
index 3fc1a5434ef..fa2673fc69c 100644
--- a/drivers/isdn/act2000/Kconfig
+++ b/drivers/isdn/act2000/Kconfig
@@ -1,6 +1,3 @@
-#
-# Config.in for IBM Active 2000 ISDN driver
-#
config ISDN_DRV_ACT2000
tristate "IBM Active 2000 support"
depends on ISA
@@ -10,4 +7,3 @@ config ISDN_DRV_ACT2000
into the card using a utility which is part of the latest
isdn4k-utils package. Please read the file
<file:Documentation/isdn/README.act2000> for more information.
-
diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig
index e1afd60924f..b2a04755c96 100644
--- a/drivers/isdn/capi/Kconfig
+++ b/drivers/isdn/capi/Kconfig
@@ -1,6 +1,3 @@
-#
-# Config.in for the CAPI subsystem
-#
config ISDN_DRV_AVMB1_VERBOSE_REASON
bool "Verbose reason code reporting"
default y
@@ -59,4 +56,3 @@ config ISDN_CAPI_CAPIDRV
the legacy isdn4linux link layer. If you have a card which is
supported by a CAPI driver, but still want to use old features like
ippp interfaces or ttyI emulation, say Y/M here.
-
diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c
index 3e6d17f42a9..66b7d7a8647 100644
--- a/drivers/isdn/capi/capidrv.c
+++ b/drivers/isdn/capi/capidrv.c
@@ -830,7 +830,7 @@ static void handle_controller(_cmsg * cmsg)
case 0: break;
case 1: s = "unknown class"; break;
case 2: s = "unknown function"; break;
- default: s = "unkown error"; break;
+ default: s = "unknown error"; break;
}
if (s)
printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig
index 18ab8652aa5..dcefedc7044 100644
--- a/drivers/isdn/gigaset/Kconfig
+++ b/drivers/isdn/gigaset/Kconfig
@@ -1,6 +1,5 @@
menuconfig ISDN_DRV_GIGASET
tristate "Siemens Gigaset support"
- depends on ISDN_I4L
select CRC_CCITT
select BITREVERSE
help
@@ -11,9 +10,33 @@ menuconfig ISDN_DRV_GIGASET
If you have one of these devices, say M here and for at least
one of the connection specific parts that follow.
This will build a module called "gigaset".
+ Note: If you build your ISDN subsystem (ISDN_CAPI or ISDN_I4L)
+ as a module, you have to build this driver as a module too,
+ otherwise the Gigaset device won't show up as an ISDN device.
if ISDN_DRV_GIGASET
+config GIGASET_CAPI
+ bool "Gigaset CAPI support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ depends on ISDN_CAPI='y'||(ISDN_CAPI='m'&&ISDN_DRV_GIGASET='m')
+ default ISDN_I4L='n'
+ help
+ Build the Gigaset driver as a CAPI 2.0 driver interfacing with
+ the Kernel CAPI subsystem. To use it with the old ISDN4Linux
+ subsystem you'll have to enable the capidrv glue driver.
+ (select ISDN_CAPI_CAPIDRV.)
+ Say N to build the old native ISDN4Linux variant.
+
+config GIGASET_I4L
+ bool
+ depends on ISDN_I4L='y'||(ISDN_I4L='m'&&ISDN_DRV_GIGASET='m')
+ default !GIGASET_CAPI
+
+config GIGASET_DUMMYLL
+ bool
+ default !GIGASET_CAPI&&!GIGASET_I4L
+
config GIGASET_BASE
tristate "Gigaset base station support"
depends on USB
diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile
index e9d3189f56b..c453b72272a 100644
--- a/drivers/isdn/gigaset/Makefile
+++ b/drivers/isdn/gigaset/Makefile
@@ -1,4 +1,7 @@
-gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o asyncdata.o
+gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
+gigaset-$(CONFIG_GIGASET_CAPI) += capi.o
+gigaset-$(CONFIG_GIGASET_I4L) += i4l.o
+gigaset-$(CONFIG_GIGASET_DUMMYLL) += dummyll.o
usb_gigaset-y := usb-gigaset.o
ser_gigaset-y := ser-gigaset.o
bas_gigaset-y := bas-gigaset.o isocdata.o
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index 44a58e6f8f6..ccb2a7b7c41 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -19,7 +19,7 @@
/* check if byte must be stuffed/escaped
* I'm not sure which data should be encoded.
- * Therefore I will go the hard way and decode every value
+ * Therefore I will go the hard way and encode every value
* less than 0x20, the flag sequence and the control escape char.
*/
static inline int muststuff(unsigned char c)
@@ -35,303 +35,383 @@ static inline int muststuff(unsigned char c)
/* == data input =========================================================== */
-/* process a block of received bytes in command mode (modem response)
+/* process a block of received bytes in command mode
+ * (mstate != MS_LOCKED && (inputstate & INS_command))
+ * Append received bytes to the command response buffer and forward them
+ * line by line to the response handler. Exit whenever a mode/state change
+ * might have occurred.
* Return value:
* number of processed bytes
*/
-static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes,
- struct inbuf_t *inbuf)
+static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)
{
+ unsigned char *src = inbuf->data + inbuf->head;
struct cardstate *cs = inbuf->cs;
- unsigned cbytes = cs->cbytes;
- int inputstate = inbuf->inputstate;
- int startbytes = numbytes;
-
- for (;;) {
- cs->respdata[cbytes] = c;
- if (c == 10 || c == 13) {
- gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
+ unsigned cbytes = cs->cbytes;
+ unsigned procbytes = 0;
+ unsigned char c;
+
+ while (procbytes < numbytes) {
+ c = *src++;
+ procbytes++;
+
+ switch (c) {
+ case '\n':
+ if (cbytes == 0 && cs->respdata[0] == '\r') {
+ /* collapse LF with preceding CR */
+ cs->respdata[0] = 0;
+ break;
+ }
+ /* --v-- fall through --v-- */
+ case '\r':
+ /* end of message line, pass to response handler */
+ gig_dbg(DEBUG_TRANSCMD, "%s: End of Message (%d Bytes)",
__func__, cbytes);
+ if (cbytes >= MAX_RESP_SIZE) {
+ dev_warn(cs->dev, "response too large (%d)\n",
+ cbytes);
+ cbytes = MAX_RESP_SIZE;
+ }
cs->cbytes = cbytes;
- gigaset_handle_modem_response(cs); /* can change
- cs->dle */
+ gigaset_handle_modem_response(cs);
cbytes = 0;
- if (cs->dle &&
- !(inputstate & INS_DLE_command)) {
- inputstate &= ~INS_command;
- break;
- }
- } else {
- /* advance in line buffer, checking for overflow */
- if (cbytes < MAX_RESP_SIZE - 1)
- cbytes++;
- else
- dev_warn(cs->dev, "response too large\n");
- }
+ /* store EOL byte for CRLF collapsing */
+ cs->respdata[0] = c;
- if (!numbytes)
- break;
- c = *src++;
- --numbytes;
- if (c == DLE_FLAG &&
- (cs->dle || inputstate & INS_DLE_command)) {
- inputstate |= INS_DLE_char;
- break;
+ /* cs->dle may have changed */
+ if (cs->dle && !(inbuf->inputstate & INS_DLE_command))
+ inbuf->inputstate &= ~INS_command;
+
+ /* return for reevaluating state */
+ goto exit;
+
+ case DLE_FLAG:
+ if (inbuf->inputstate & INS_DLE_char) {
+ /* quoted DLE: clear quote flag */
+ inbuf->inputstate &= ~INS_DLE_char;
+ } else if (cs->dle ||
+ (inbuf->inputstate & INS_DLE_command)) {
+ /* DLE escape, pass up for handling */
+ inbuf->inputstate |= INS_DLE_char;
+ goto exit;
+ }
+ /* quoted or not in DLE mode: treat as regular data */
+ /* --v-- fall through --v-- */
+ default:
+ /* append to line buffer if possible */
+ if (cbytes < MAX_RESP_SIZE)
+ cs->respdata[cbytes] = c;
+ cbytes++;
}
}
-
+exit:
cs->cbytes = cbytes;
- inbuf->inputstate = inputstate;
-
- return startbytes - numbytes;
+ return procbytes;
}
-/* process a block of received bytes in lock mode (tty i/f)
+/* process a block of received bytes in lock mode
+ * All received bytes are passed unmodified to the tty i/f.
* Return value:
* number of processed bytes
*/
-static inline int lock_loop(unsigned char *src, int numbytes,
- struct inbuf_t *inbuf)
+static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)
{
- struct cardstate *cs = inbuf->cs;
-
- gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
- numbytes, src);
- gigaset_if_receive(cs, src, numbytes);
+ unsigned char *src = inbuf->data + inbuf->head;
+ gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src);
+ gigaset_if_receive(inbuf->cs, src, numbytes);
return numbytes;
}
+/* set up next receive skb for data mode
+ */
+static void new_rcv_skb(struct bc_state *bcs)
+{
+ struct cardstate *cs = bcs->cs;
+ unsigned short hw_hdr_len = cs->hw_hdr_len;
+
+ if (bcs->ignore) {
+ bcs->skb = NULL;
+ return;
+ }
+
+ bcs->skb = dev_alloc_skb(SBUFSIZE + hw_hdr_len);
+ if (bcs->skb == NULL) {
+ dev_warn(cs->dev, "could not allocate new skb\n");
+ return;
+ }
+ skb_reserve(bcs->skb, hw_hdr_len);
+}
+
/* process a block of received bytes in HDLC data mode
+ * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
* Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
* When a frame is complete, check the FCS and pass valid frames to the LL.
* If DLE is encountered, return immediately to let the caller handle it.
* Return value:
* number of processed bytes
- * numbytes (all bytes processed) on error --FIXME
*/
-static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,
- struct inbuf_t *inbuf)
+static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
{
struct cardstate *cs = inbuf->cs;
- struct bc_state *bcs = inbuf->bcs;
+ struct bc_state *bcs = cs->bcs;
int inputstate = bcs->inputstate;
__u16 fcs = bcs->fcs;
struct sk_buff *skb = bcs->skb;
- unsigned char error;
- struct sk_buff *compskb;
- int startbytes = numbytes;
- int l;
+ unsigned char *src = inbuf->data + inbuf->head;
+ unsigned procbytes = 0;
+ unsigned char c;
- if (unlikely(inputstate & INS_byte_stuff)) {
+ if (inputstate & INS_byte_stuff) {
+ if (!numbytes)
+ return 0;
inputstate &= ~INS_byte_stuff;
goto byte_stuff;
}
- for (;;) {
- if (unlikely(c == PPP_ESCAPE)) {
- if (unlikely(!numbytes)) {
- inputstate |= INS_byte_stuff;
+
+ while (procbytes < numbytes) {
+ c = *src++;
+ procbytes++;
+ if (c == DLE_FLAG) {
+ if (inputstate & INS_DLE_char) {
+ /* quoted DLE: clear quote flag */
+ inputstate &= ~INS_DLE_char;
+ } else if (cs->dle || (inputstate & INS_DLE_command)) {
+ /* DLE escape, pass up for handling */
+ inputstate |= INS_DLE_char;
break;
}
- c = *src++;
- --numbytes;
- if (unlikely(c == DLE_FLAG &&
- (cs->dle ||
- inbuf->inputstate & INS_DLE_command))) {
- inbuf->inputstate |= INS_DLE_char;
+ }
+
+ if (c == PPP_ESCAPE) {
+ /* byte stuffing indicator: pull in next byte */
+ if (procbytes >= numbytes) {
+ /* end of buffer, save for later processing */
inputstate |= INS_byte_stuff;
break;
}
byte_stuff:
+ c = *src++;
+ procbytes++;
+ if (c == DLE_FLAG) {
+ if (inputstate & INS_DLE_char) {
+ /* quoted DLE: clear quote flag */
+ inputstate &= ~INS_DLE_char;
+ } else if (cs->dle ||
+ (inputstate & INS_DLE_command)) {
+ /* DLE escape, pass up for handling */
+ inputstate |=
+ INS_DLE_char | INS_byte_stuff;
+ break;
+ }
+ }
c ^= PPP_TRANS;
- if (unlikely(!muststuff(c)))
- gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
- } else if (unlikely(c == PPP_FLAG)) {
- if (unlikely(inputstate & INS_skip_frame)) {
-#ifdef CONFIG_GIGASET_DEBUG
- if (!(inputstate & INS_have_data)) { /* 7E 7E */
- ++bcs->emptycount;
- } else
- gig_dbg(DEBUG_HDLC,
- "7e----------------------------");
-#endif
-
- /* end of frame */
- error = 1;
- gigaset_rcv_error(NULL, cs, bcs);
- } else if (!(inputstate & INS_have_data)) { /* 7E 7E */
#ifdef CONFIG_GIGASET_DEBUG
- ++bcs->emptycount;
+ if (!muststuff(c))
+ gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
#endif
- break;
- } else {
+ } else if (c == PPP_FLAG) {
+ /* end of frame: process content if any */
+ if (inputstate & INS_have_data) {
gig_dbg(DEBUG_HDLC,
"7e----------------------------");
- /* end of frame */
- error = 0;
-
- if (unlikely(fcs != PPP_GOODFCS)) {
+ /* check and pass received frame */
+ if (!skb) {
+ /* skipped frame */
+ gigaset_isdn_rcv_err(bcs);
+ } else if (skb->len < 2) {
+ /* frame too short for FCS */
+ dev_warn(cs->dev,
+ "short frame (%d)\n",
+ skb->len);
+ gigaset_isdn_rcv_err(bcs);
+ dev_kfree_skb_any(skb);
+ } else if (fcs != PPP_GOODFCS) {
+ /* frame check error */
dev_err(cs->dev,
"Checksum failed, %u bytes corrupted!\n",
skb->len);
- compskb = NULL;
- gigaset_rcv_error(compskb, cs, bcs);
- error = 1;
+ gigaset_isdn_rcv_err(bcs);
+ dev_kfree_skb_any(skb);
} else {
- if (likely((l = skb->len) > 2)) {
- skb->tail -= 2;
- skb->len -= 2;
- } else {
- dev_kfree_skb(skb);
- skb = NULL;
- inputstate |= INS_skip_frame;
- if (l == 1) {
- dev_err(cs->dev,
- "invalid packet size (1)!\n");
- error = 1;
- gigaset_rcv_error(NULL,
- cs, bcs);
- }
- }
- if (likely(!(error ||
- (inputstate &
- INS_skip_frame)))) {
- gigaset_rcv_skb(skb, cs, bcs);
- }
+ /* good frame */
+ __skb_trim(skb, skb->len - 2);
+ gigaset_skb_rcvd(bcs, skb);
}
- }
- if (unlikely(error))
- if (skb)
- dev_kfree_skb(skb);
-
- fcs = PPP_INITFCS;
- inputstate &= ~(INS_have_data | INS_skip_frame);
- if (unlikely(bcs->ignore)) {
- inputstate |= INS_skip_frame;
- skb = NULL;
- } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) {
- skb_reserve(skb, HW_HDR_LEN);
+ /* prepare reception of next frame */
+ inputstate &= ~INS_have_data;
+ new_rcv_skb(bcs);
+ skb = bcs->skb;
} else {
- dev_warn(cs->dev,
- "could not allocate new skb\n");
- inputstate |= INS_skip_frame;
+ /* empty frame (7E 7E) */
+#ifdef CONFIG_GIGASET_DEBUG
+ ++bcs->emptycount;
+#endif
+ if (!skb) {
+ /* skipped (?) */
+ gigaset_isdn_rcv_err(bcs);
+ new_rcv_skb(bcs);
+ skb = bcs->skb;
+ }
}
- break;
- } else if (unlikely(muststuff(c))) {
+ fcs = PPP_INITFCS;
+ continue;
+#ifdef CONFIG_GIGASET_DEBUG
+ } else if (muststuff(c)) {
/* Should not happen. Possible after ZDLE=1<CR><LF>. */
gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
+#endif
}
- /* add character */
-
+ /* regular data byte, append to skb */
#ifdef CONFIG_GIGASET_DEBUG
- if (unlikely(!(inputstate & INS_have_data))) {
+ if (!(inputstate & INS_have_data)) {
gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
bcs->emptycount);
bcs->emptycount = 0;
}
#endif
-
inputstate |= INS_have_data;
-
- if (likely(!(inputstate & INS_skip_frame))) {
- if (unlikely(skb->len == SBUFSIZE)) {
+ if (skb) {
+ if (skb->len == SBUFSIZE) {
dev_warn(cs->dev, "received packet too long\n");
dev_kfree_skb_any(skb);
- skb = NULL;
- inputstate |= INS_skip_frame;
- break;
+ /* skip remainder of packet */
+ bcs->skb = skb = NULL;
+ } else {
+ *__skb_put(skb, 1) = c;
+ fcs = crc_ccitt_byte(fcs, c);
}
- *__skb_put(skb, 1) = c;
- fcs = crc_ccitt_byte(fcs, c);
- }
-
- if (unlikely(!numbytes))
- break;
- c = *src++;
- --numbytes;
- if (unlikely(c == DLE_FLAG &&
- (cs->dle ||
- inbuf->inputstate & INS_DLE_command))) {
- inbuf->inputstate |= INS_DLE_char;
- break;
}
}
+
bcs->inputstate = inputstate;
bcs->fcs = fcs;
- bcs->skb = skb;
- return startbytes - numbytes;
+ return procbytes;
}
/* process a block of received bytes in transparent data mode
+ * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC)
* Invert bytes, undoing byte stuffing and watching for DLE escapes.
* If DLE is encountered, return immediately to let the caller handle it.
* Return value:
* number of processed bytes
- * numbytes (all bytes processed) on error --FIXME
*/
-static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
- struct inbuf_t *inbuf)
+static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
{
struct cardstate *cs = inbuf->cs;
- struct bc_state *bcs = inbuf->bcs;
+ struct bc_state *bcs = cs->bcs;
int inputstate = bcs->inputstate;
struct sk_buff *skb = bcs->skb;
- int startbytes = numbytes;
+ unsigned char *src = inbuf->data + inbuf->head;
+ unsigned procbytes = 0;
+ unsigned char c;
- for (;;) {
- /* add character */
- inputstate |= INS_have_data;
+ if (!skb) {
+ /* skip this block */
+ new_rcv_skb(bcs);
+ return numbytes;
+ }
- if (likely(!(inputstate & INS_skip_frame))) {
- if (unlikely(skb->len == SBUFSIZE)) {
- //FIXME just pass skb up and allocate a new one
- dev_warn(cs->dev, "received packet too long\n");
- dev_kfree_skb_any(skb);
- skb = NULL;
- inputstate |= INS_skip_frame;
+ while (procbytes < numbytes && skb->len < SBUFSIZE) {
+ c = *src++;
+ procbytes++;
+
+ if (c == DLE_FLAG) {
+ if (inputstate & INS_DLE_char) {
+ /* quoted DLE: clear quote flag */
+ inputstate &= ~INS_DLE_char;
+ } else if (cs->dle || (inputstate & INS_DLE_command)) {
+ /* DLE escape, pass up for handling */
+ inputstate |= INS_DLE_char;
break;
}
- *__skb_put(skb, 1) = bitrev8(c);
}
- if (unlikely(!numbytes))
- break;
- c = *src++;
- --numbytes;
- if (unlikely(c == DLE_FLAG &&
- (cs->dle ||
- inbuf->inputstate & INS_DLE_command))) {
- inbuf->inputstate |= INS_DLE_char;
- break;
- }
+ /* regular data byte: append to current skb */
+ inputstate |= INS_have_data;
+ *__skb_put(skb, 1) = bitrev8(c);
}
/* pass data up */
- if (likely(inputstate & INS_have_data)) {
- if (likely(!(inputstate & INS_skip_frame))) {
- gigaset_rcv_skb(skb, cs, bcs);
- }
- inputstate &= ~(INS_have_data | INS_skip_frame);
- if (unlikely(bcs->ignore)) {
- inputstate |= INS_skip_frame;
- skb = NULL;
- } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN))
- != NULL)) {
- skb_reserve(skb, HW_HDR_LEN);
+ if (inputstate & INS_have_data) {
+ gigaset_skb_rcvd(bcs, skb);
+ inputstate &= ~INS_have_data;
+ new_rcv_skb(bcs);
+ }
+
+ bcs->inputstate = inputstate;
+ return procbytes;
+}
+
+/* process DLE escapes
+ * Called whenever a DLE sequence might be encountered in the input stream.
+ * Either processes the entire DLE sequence or, if that isn't possible,
+ * notes the fact that an initial DLE has been received in the INS_DLE_char
+ * inputstate flag and resumes processing of the sequence on the next call.
+ */
+static void handle_dle(struct inbuf_t *inbuf)
+{
+ struct cardstate *cs = inbuf->cs;
+
+ if (cs->mstate == MS_LOCKED)
+ return; /* no DLE processing in lock mode */
+
+ if (!(inbuf->inputstate & INS_DLE_char)) {
+ /* no DLE pending */
+ if (inbuf->data[inbuf->head] == DLE_FLAG &&
+ (cs->dle || inbuf->inputstate & INS_DLE_command)) {
+ /* start of DLE sequence */
+ inbuf->head++;
+ if (inbuf->head == inbuf->tail ||
+ inbuf->head == RBUFSIZE) {
+ /* end of buffer, save for later processing */
+ inbuf->inputstate |= INS_DLE_char;
+ return;
+ }
} else {
- dev_warn(cs->dev, "could not allocate new skb\n");
- inputstate |= INS_skip_frame;
+ /* regular data byte */
+ return;
}
}
- bcs->inputstate = inputstate;
- bcs->skb = skb;
- return startbytes - numbytes;
+ /* consume pending DLE */
+ inbuf->inputstate &= ~INS_DLE_char;
+
+ switch (inbuf->data[inbuf->head]) {
+ case 'X': /* begin of event message */
+ if (inbuf->inputstate & INS_command)
+ dev_notice(cs->dev,
+ "received <DLE>X in command mode\n");
+ inbuf->inputstate |= INS_command | INS_DLE_command;
+ inbuf->head++; /* byte consumed */
+ break;
+ case '.': /* end of event message */
+ if (!(inbuf->inputstate & INS_DLE_command))
+ dev_notice(cs->dev,
+ "received <DLE>. without <DLE>X\n");
+ inbuf->inputstate &= ~INS_DLE_command;
+ /* return to data mode if in DLE mode */
+ if (cs->dle)
+ inbuf->inputstate &= ~INS_command;
+ inbuf->head++; /* byte consumed */
+ break;
+ case DLE_FLAG: /* DLE in data stream */
+ /* mark as quoted */
+ inbuf->inputstate |= INS_DLE_char;
+ if (!(cs->dle || inbuf->inputstate & INS_DLE_command))
+ dev_notice(cs->dev,
+ "received <DLE><DLE> not in DLE mode\n");
+ break; /* quoted byte left in buffer */
+ default:
+ dev_notice(cs->dev, "received <DLE><%02x>\n",
+ inbuf->data[inbuf->head]);
+ /* quoted byte left in buffer */
+ }
}
/**
@@ -345,94 +425,39 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
*/
void gigaset_m10x_input(struct inbuf_t *inbuf)
{
- struct cardstate *cs;
- unsigned tail, head, numbytes;
- unsigned char *src, c;
- int procbytes;
-
- head = inbuf->head;
- tail = inbuf->tail;
- gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
-
- if (head != tail) {
- cs = inbuf->cs;
- src = inbuf->data + head;
- numbytes = (head > tail ? RBUFSIZE : tail) - head;
- gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
+ struct cardstate *cs = inbuf->cs;
+ unsigned numbytes, procbytes;
- while (numbytes) {
- if (cs->mstate == MS_LOCKED) {
- procbytes = lock_loop(src, numbytes, inbuf);
- src += procbytes;
- numbytes -= procbytes;
- } else {
- c = *src++;
- --numbytes;
- if (c == DLE_FLAG && (cs->dle ||
- inbuf->inputstate & INS_DLE_command)) {
- if (!(inbuf->inputstate & INS_DLE_char)) {
- inbuf->inputstate |= INS_DLE_char;
- goto nextbyte;
- }
- /* <DLE> <DLE> => <DLE> in data stream */
- inbuf->inputstate &= ~INS_DLE_char;
- }
+ gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail);
- if (!(inbuf->inputstate & INS_DLE_char)) {
-
- /* FIXME use function pointers? */
- if (inbuf->inputstate & INS_command)
- procbytes = cmd_loop(c, src, numbytes, inbuf);
- else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC)
- procbytes = hdlc_loop(c, src, numbytes, inbuf);
- else
- procbytes = iraw_loop(c, src, numbytes, inbuf);
-
- src += procbytes;
- numbytes -= procbytes;
- } else { /* DLE char */
- inbuf->inputstate &= ~INS_DLE_char;
- switch (c) {
- case 'X': /*begin of command*/
- if (inbuf->inputstate & INS_command)
- dev_warn(cs->dev,
- "received <DLE> 'X' in command mode\n");
- inbuf->inputstate |=
- INS_command | INS_DLE_command;
- break;
- case '.': /*end of command*/
- if (!(inbuf->inputstate & INS_command))
- dev_warn(cs->dev,
- "received <DLE> '.' in hdlc mode\n");
- inbuf->inputstate &= cs->dle ?
- ~(INS_DLE_command|INS_command)
- : ~INS_DLE_command;
- break;
- //case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */
- default:
- dev_err(cs->dev,
- "received 0x10 0x%02x!\n",
- (int) c);
- /* FIXME: reset driver?? */
- }
- }
- }
-nextbyte:
- if (!numbytes) {
- /* end of buffer, check for wrap */
- if (head > tail) {
- head = 0;
- src = inbuf->data;
- numbytes = tail;
- } else {
- head = tail;
- break;
- }
- }
- }
+ while (inbuf->head != inbuf->tail) {
+ /* check for DLE escape */
+ handle_dle(inbuf);
- gig_dbg(DEBUG_INTR, "setting head to %u", head);
- inbuf->head = head;
+ /* process a contiguous block of bytes */
+ numbytes = (inbuf->head > inbuf->tail ?
+ RBUFSIZE : inbuf->tail) - inbuf->head;
+ gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
+ /*
+ * numbytes may be 0 if handle_dle() ate the last byte.
+ * This does no harm, *_loop() will just return 0 immediately.
+ */
+
+ if (cs->mstate == MS_LOCKED)
+ procbytes = lock_loop(numbytes, inbuf);
+ else if (inbuf->inputstate & INS_command)
+ procbytes = cmd_loop(numbytes, inbuf);
+ else if (cs->bcs->proto2 == L2_HDLC)
+ procbytes = hdlc_loop(numbytes, inbuf);
+ else
+ procbytes = iraw_loop(numbytes, inbuf);
+ inbuf->head += procbytes;
+
+ /* check for buffer wraparound */
+ if (inbuf->head >= RBUFSIZE)
+ inbuf->head = 0;
+
+ gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head);
}
}
EXPORT_SYMBOL_GPL(gigaset_m10x_input);
@@ -440,16 +465,16 @@ EXPORT_SYMBOL_GPL(gigaset_m10x_input);
/* == data output ========================================================== */
-/* Encoding of a PPP packet into an octet stuffed HDLC frame
- * with FCS, opening and closing flags.
+/*
+ * Encode a data packet into an octet stuffed HDLC frame with FCS,
+ * opening and closing flags, preserving headroom data.
* parameters:
- * skb skb containing original packet (freed upon return)
- * head number of headroom bytes to allocate in result skb
- * tail number of tailroom bytes to allocate in result skb
+ * skb skb containing original packet (freed upon return)
* Return value:
* pointer to newly allocated skb containing the result frame
+ * and the original link layer header, NULL on error
*/
-static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
+static struct sk_buff *HDLC_Encode(struct sk_buff *skb)
{
struct sk_buff *hdlc_skb;
__u16 fcs;
@@ -471,16 +496,19 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
/* size of new buffer: original size + number of stuffing bytes
* + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
+ * + room for link layer header
*/
- hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);
+ hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len);
if (!hdlc_skb) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NULL;
}
- skb_reserve(hdlc_skb, head);
- /* Copy acknowledge request into new skb */
- memcpy(hdlc_skb->head, skb->head, 2);
+ /* Copy link layer header into new skb */
+ skb_reset_mac_header(hdlc_skb);
+ skb_reserve(hdlc_skb, skb->mac_len);
+ memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len);
+ hdlc_skb->mac_len = skb->mac_len;
/* Add flag sequence in front of everything.. */
*(skb_put(hdlc_skb, 1)) = PPP_FLAG;
@@ -511,33 +539,42 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
*(skb_put(hdlc_skb, 1)) = PPP_FLAG;
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return hdlc_skb;
}
-/* Encoding of a raw packet into an octet stuffed bit inverted frame
+/*
+ * Encode a data packet into an octet stuffed raw bit inverted frame,
+ * preserving headroom data.
* parameters:
- * skb skb containing original packet (freed upon return)
- * head number of headroom bytes to allocate in result skb
- * tail number of tailroom bytes to allocate in result skb
+ * skb skb containing original packet (freed upon return)
* Return value:
* pointer to newly allocated skb containing the result frame
+ * and the original link layer header, NULL on error
*/
-static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
+static struct sk_buff *iraw_encode(struct sk_buff *skb)
{
struct sk_buff *iraw_skb;
unsigned char c;
unsigned char *cp;
int len;
- /* worst case: every byte must be stuffed */
- iraw_skb = dev_alloc_skb(2*skb->len + tail + head);
+ /* size of new buffer (worst case = every byte must be stuffed):
+ * 2 * original size + room for link layer header
+ */
+ iraw_skb = dev_alloc_skb(2*skb->len + skb->mac_len);
if (!iraw_skb) {
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NULL;
}
- skb_reserve(iraw_skb, head);
+ /* copy link layer header into new skb */
+ skb_reset_mac_header(iraw_skb);
+ skb_reserve(iraw_skb, skb->mac_len);
+ memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len);
+ iraw_skb->mac_len = skb->mac_len;
+
+ /* copy and stuff data */
cp = skb->data;
len = skb->len;
while (len--) {
@@ -546,7 +583,7 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
*(skb_put(iraw_skb, 1)) = c;
*(skb_put(iraw_skb, 1)) = c;
}
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return iraw_skb;
}
@@ -555,8 +592,10 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
* @bcs: B channel descriptor structure.
* @skb: data to send.
*
- * Called by i4l.c to encode and queue an skb for sending, and start
+ * Called by LL to encode and queue an skb for sending, and start
* transmission if necessary.
+ * Once the payload data has been transmitted completely, gigaset_skb_sent()
+ * will be called with the skb's link layer header preserved.
*
* Return value:
* number of bytes accepted for sending (skb->len) if ok,
@@ -564,24 +603,25 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
*/
int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
{
+ struct cardstate *cs = bcs->cs;
unsigned len = skb->len;
unsigned long flags;
- if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
- skb = HDLC_Encode(skb, HW_HDR_LEN, 0);
+ if (bcs->proto2 == L2_HDLC)
+ skb = HDLC_Encode(skb);
else
- skb = iraw_encode(skb, HW_HDR_LEN, 0);
+ skb = iraw_encode(skb);
if (!skb) {
- dev_err(bcs->cs->dev,
+ dev_err(cs->dev,
"unable to allocate memory for encoding!\n");
return -ENOMEM;
}
skb_queue_tail(&bcs->squeue, skb);
- spin_lock_irqsave(&bcs->cs->lock, flags);
- if (bcs->cs->connected)
- tasklet_schedule(&bcs->cs->write_tasklet);
- spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->connected)
+ tasklet_schedule(&cs->write_tasklet);
+ spin_unlock_irqrestore(&cs->lock, flags);
return len; /* ok so far */
}
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 5ed1d99eb9f..95ebc512989 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -57,7 +57,7 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode");
#define USB_SX353_PRODUCT_ID 0x0022
/* table of devices that work with this driver */
-static const struct usb_device_id gigaset_table [] = {
+static const struct usb_device_id gigaset_table[] = {
{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) },
{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) },
{ USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) },
@@ -137,7 +137,7 @@ struct bas_cardstate {
#define BS_RESETTING 0x200 /* waiting for HD_RESET_INTERRUPT_PIPE_ACK */
-static struct gigaset_driver *driver = NULL;
+static struct gigaset_driver *driver;
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gigaset_usb_driver = {
@@ -601,11 +601,12 @@ static int atread_submit(struct cardstate *cs, int timeout)
ucs->dr_cmd_in.wLength = cpu_to_le16(ucs->rcvbuf_size);
usb_fill_control_urb(ucs->urb_cmd_in, ucs->udev,
usb_rcvctrlpipe(ucs->udev, 0),
- (unsigned char*) & ucs->dr_cmd_in,
+ (unsigned char *) &ucs->dr_cmd_in,
ucs->rcvbuf, ucs->rcvbuf_size,
read_ctrl_callback, cs->inbuf);
- if ((ret = usb_submit_urb(ucs->urb_cmd_in, GFP_ATOMIC)) != 0) {
+ ret = usb_submit_urb(ucs->urb_cmd_in, GFP_ATOMIC);
+ if (ret != 0) {
update_basstate(ucs, 0, BS_ATRDPEND);
dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n",
get_usb_rcmsg(ret));
@@ -652,13 +653,11 @@ static void read_int_callback(struct urb *urb)
return;
case -ENODEV: /* device removed */
case -ESHUTDOWN: /* device shut down */
- //FIXME use this as disconnect indicator?
gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__);
return;
default: /* severe trouble */
dev_warn(cs->dev, "interrupt read: %s\n",
get_usb_statmsg(status));
- //FIXME corrective action? resubmission always ok?
goto resubmit;
}
@@ -742,7 +741,8 @@ static void read_int_callback(struct urb *urb)
kfree(ucs->rcvbuf);
ucs->rcvbuf_size = 0;
}
- if ((ucs->rcvbuf = kmalloc(l, GFP_ATOMIC)) == NULL) {
+ ucs->rcvbuf = kmalloc(l, GFP_ATOMIC);
+ if (ucs->rcvbuf == NULL) {
spin_unlock_irqrestore(&cs->lock, flags);
dev_err(cs->dev, "out of memory receiving AT data\n");
error_reset(cs);
@@ -750,12 +750,12 @@ static void read_int_callback(struct urb *urb)
}
ucs->rcvbuf_size = l;
ucs->retry_cmd_in = 0;
- if ((rc = atread_submit(cs, BAS_TIMEOUT)) < 0) {
+ rc = atread_submit(cs, BAS_TIMEOUT);
+ if (rc < 0) {
kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0;
if (rc != -ENODEV) {
- //FIXME corrective action?
spin_unlock_irqrestore(&cs->lock, flags);
error_reset(cs);
break;
@@ -911,7 +911,7 @@ static int starturbs(struct bc_state *bcs)
int rc;
/* initialize L2 reception */
- if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
+ if (bcs->proto2 == L2_HDLC)
bcs->inputstate |= INS_flag_hunt;
/* submit all isochronous input URBs */
@@ -940,7 +940,8 @@ static int starturbs(struct bc_state *bcs)
}
dump_urb(DEBUG_ISO, "Initial isoc read", urb);
- if ((rc = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc != 0)
goto error;
}
@@ -1045,7 +1046,8 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
/* compute frame length according to flow control */
ifd->length = BAS_NORMFRAME;
- if ((corrbytes = atomic_read(&ubc->corrbytes)) != 0) {
+ corrbytes = atomic_read(&ubc->corrbytes);
+ if (corrbytes != 0) {
gig_dbg(DEBUG_ISO, "%s: corrbytes=%d",
__func__, corrbytes);
if (corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME)
@@ -1064,7 +1066,7 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
"%s: buffer busy at frame %d",
__func__, nframe);
/* tasklet will be restarted from
- gigaset_send_skb() */
+ gigaset_isoc_send_skb() */
} else {
dev_err(ucx->bcs->cs->dev,
"%s: buffer error %d at frame %d\n",
@@ -1284,7 +1286,8 @@ static void read_iso_tasklet(unsigned long data)
for (;;) {
/* retrieve URB */
spin_lock_irqsave(&ubc->isoinlock, flags);
- if (!(urb = ubc->isoindone)) {
+ urb = ubc->isoindone;
+ if (!urb) {
spin_unlock_irqrestore(&ubc->isoinlock, flags);
return;
}
@@ -1371,7 +1374,7 @@ static void read_iso_tasklet(unsigned long data)
"isochronous read: %d data bytes missing\n",
totleft);
- error:
+error:
/* URB processed, resubmit */
for (frame = 0; frame < BAS_NUMFRAMES; frame++) {
urb->iso_frame_desc[frame].status = 0;
@@ -1568,7 +1571,7 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout)
ucs->dr_ctrl.wLength = 0;
usb_fill_control_urb(ucs->urb_ctrl, ucs->udev,
usb_sndctrlpipe(ucs->udev, 0),
- (unsigned char*) &ucs->dr_ctrl, NULL, 0,
+ (unsigned char *) &ucs->dr_ctrl, NULL, 0,
write_ctrl_callback, ucs);
ucs->retry_ctrl = 0;
ret = usb_submit_urb(ucs->urb_ctrl, GFP_ATOMIC);
@@ -1621,7 +1624,8 @@ static int gigaset_init_bchannel(struct bc_state *bcs)
return -EHOSTUNREACH;
}
- if ((ret = starturbs(bcs)) < 0) {
+ ret = starturbs(bcs);
+ if (ret < 0) {
dev_err(cs->dev,
"could not start isochronous I/O for channel B%d: %s\n",
bcs->channel + 1,
@@ -1633,7 +1637,8 @@ static int gigaset_init_bchannel(struct bc_state *bcs)
}
req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL;
- if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) {
+ ret = req_submit(bcs, req, 0, BAS_TIMEOUT);
+ if (ret < 0) {
dev_err(cs->dev, "could not open channel B%d\n",
bcs->channel + 1);
stopurbs(bcs->hw.bas);
@@ -1677,7 +1682,8 @@ static int gigaset_close_bchannel(struct bc_state *bcs)
/* channel running: tell device to close it */
req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL;
- if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0)
+ ret = req_submit(bcs, req, 0, BAS_TIMEOUT);
+ if (ret < 0)
dev_err(cs->dev, "closing channel B%d failed\n",
bcs->channel + 1);
@@ -1703,10 +1709,12 @@ static void complete_cb(struct cardstate *cs)
gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD,
"write_command: sent %u bytes, %u left",
cs->curlen, cs->cmdbytes);
- if ((cs->cmdbuf = cb->next) != NULL) {
+ if (cb->next != NULL) {
+ cs->cmdbuf = cb->next;
cs->cmdbuf->prev = NULL;
cs->curlen = cs->cmdbuf->len;
} else {
+ cs->cmdbuf = NULL;
cs->lastcmdbuf = NULL;
cs->curlen = 0;
}
@@ -1833,7 +1841,7 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len)
ucs->dr_cmd_out.wLength = cpu_to_le16(len);
usb_fill_control_urb(ucs->urb_cmd_out, ucs->udev,
usb_sndctrlpipe(ucs->udev, 0),
- (unsigned char*) &ucs->dr_cmd_out, buf, len,
+ (unsigned char *) &ucs->dr_cmd_out, buf, len,
write_command_callback, cs);
rc = usb_submit_urb(ucs->urb_cmd_out, GFP_ATOMIC);
if (unlikely(rc)) {
@@ -1953,7 +1961,8 @@ static int gigaset_write_cmd(struct cardstate *cs,
if (len > IF_WRITEBUF)
len = IF_WRITEBUF;
- if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
+ cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC);
+ if (!cb) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
rc = -ENOMEM;
goto notqueued;
@@ -2100,14 +2109,15 @@ static int gigaset_initbcshw(struct bc_state *bcs)
}
ubc->isooutdone = ubc->isooutfree = ubc->isooutovfl = NULL;
ubc->numsub = 0;
- if (!(ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL))) {
+ ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL);
+ if (!ubc->isooutbuf) {
pr_err("out of memory\n");
kfree(ubc);
bcs->hw.bas = NULL;
return 0;
}
tasklet_init(&ubc->sent_tasklet,
- &write_iso_tasklet, (unsigned long) bcs);
+ write_iso_tasklet, (unsigned long) bcs);
spin_lock_init(&ubc->isoinlock);
for (i = 0; i < BAS_INURBS; ++i)
@@ -2128,7 +2138,7 @@ static int gigaset_initbcshw(struct bc_state *bcs)
ubc->shared0s = 0;
ubc->stolen0s = 0;
tasklet_init(&ubc->rcvd_tasklet,
- &read_iso_tasklet, (unsigned long) bcs);
+ read_iso_tasklet, (unsigned long) bcs);
return 1;
}
@@ -2252,7 +2262,8 @@ static int gigaset_probe(struct usb_interface *interface,
gig_dbg(DEBUG_ANY,
"%s: wrong alternate setting %d - trying to switch",
__func__, hostif->desc.bAlternateSetting);
- if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3) < 0) {
+ if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3)
+ < 0) {
dev_warn(&udev->dev, "usb_set_interface failed, "
"device %d interface %d altsetting %d\n",
udev->devnum, hostif->desc.bInterfaceNumber,
@@ -2321,14 +2332,16 @@ static int gigaset_probe(struct usb_interface *interface,
(endpoint->bEndpointAddress) & 0x0f),
ucs->int_in_buf, IP_MSGSIZE, read_int_callback, cs,
endpoint->bInterval);
- if ((rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL)) != 0) {
+ rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
+ if (rc != 0) {
dev_err(cs->dev, "could not submit interrupt URB: %s\n",
get_usb_rcmsg(rc));
goto error;
}
/* tell the device that the driver is ready */
- if ((rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0)) != 0)
+ rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0);
+ if (rc != 0)
goto error;
/* tell common part that the device is ready */
@@ -2524,9 +2537,10 @@ static int __init bas_gigaset_init(void)
int result;
/* allocate memory for our driver state and intialize it */
- if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
- GIGASET_MODULENAME, GIGASET_DEVNAME,
- &gigops, THIS_MODULE)) == NULL)
+ driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+ GIGASET_MODULENAME, GIGASET_DEVNAME,
+ &gigops, THIS_MODULE);
+ if (driver == NULL)
goto error;
/* register this driver with the USB subsystem */
diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c
new file mode 100644
index 00000000000..3f5cd06af10
--- /dev/null
+++ b/drivers/isdn/gigaset/capi.c
@@ -0,0 +1,2292 @@
+/*
+ * Kernel CAPI interface for the Gigaset driver
+ *
+ * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ * 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 "gigaset.h"
+#include <linux/ctype.h>
+#include <linux/isdn/capilli.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+/* missing from kernelcapi.h */
+#define CapiNcpiNotSupportedByProtocol 0x0001
+#define CapiFlagsNotSupportedByProtocol 0x0002
+#define CapiAlertAlreadySent 0x0003
+#define CapiFacilitySpecificFunctionNotSupported 0x3011
+
+/* missing from capicmd.h */
+#define CAPI_CONNECT_IND_BASELEN (CAPI_MSG_BASELEN+4+2+8*1)
+#define CAPI_CONNECT_ACTIVE_IND_BASELEN (CAPI_MSG_BASELEN+4+3*1)
+#define CAPI_CONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN+4+1)
+#define CAPI_CONNECT_B3_ACTIVE_IND_BASELEN (CAPI_MSG_BASELEN+4+1)
+#define CAPI_DATA_B3_REQ_LEN64 (CAPI_MSG_BASELEN+4+4+2+2+2+8)
+#define CAPI_DATA_B3_CONF_LEN (CAPI_MSG_BASELEN+4+2+2)
+#define CAPI_DISCONNECT_IND_LEN (CAPI_MSG_BASELEN+4+2)
+#define CAPI_DISCONNECT_B3_IND_BASELEN (CAPI_MSG_BASELEN+4+2+1)
+#define CAPI_FACILITY_CONF_BASELEN (CAPI_MSG_BASELEN+4+2+2+1)
+/* most _CONF messages contain only Controller/PLCI/NCCI and Info parameters */
+#define CAPI_STDCONF_LEN (CAPI_MSG_BASELEN+4+2)
+
+#define CAPI_FACILITY_HANDSET 0x0000
+#define CAPI_FACILITY_DTMF 0x0001
+#define CAPI_FACILITY_V42BIS 0x0002
+#define CAPI_FACILITY_SUPPSVC 0x0003
+#define CAPI_FACILITY_WAKEUP 0x0004
+#define CAPI_FACILITY_LI 0x0005
+
+#define CAPI_SUPPSVC_GETSUPPORTED 0x0000
+
+/* missing from capiutil.h */
+#define CAPIMSG_PLCI_PART(m) CAPIMSG_U8(m, 9)
+#define CAPIMSG_NCCI_PART(m) CAPIMSG_U16(m, 10)
+#define CAPIMSG_HANDLE_REQ(m) CAPIMSG_U16(m, 18) /* DATA_B3_REQ/_IND only! */
+#define CAPIMSG_FLAGS(m) CAPIMSG_U16(m, 20)
+#define CAPIMSG_SETCONTROLLER(m, contr) capimsg_setu8(m, 8, contr)
+#define CAPIMSG_SETPLCI_PART(m, plci) capimsg_setu8(m, 9, plci)
+#define CAPIMSG_SETNCCI_PART(m, ncci) capimsg_setu16(m, 10, ncci)
+#define CAPIMSG_SETFLAGS(m, flags) capimsg_setu16(m, 20, flags)
+
+/* parameters with differing location in DATA_B3_CONF/_RESP: */
+#define CAPIMSG_SETHANDLE_CONF(m, handle) capimsg_setu16(m, 12, handle)
+#define CAPIMSG_SETINFO_CONF(m, info) capimsg_setu16(m, 14, info)
+
+/* Flags (DATA_B3_REQ/_IND) */
+#define CAPI_FLAGS_DELIVERY_CONFIRMATION 0x04
+#define CAPI_FLAGS_RESERVED (~0x1f)
+
+/* buffer sizes */
+#define MAX_BC_OCTETS 11
+#define MAX_HLC_OCTETS 3
+#define MAX_NUMBER_DIGITS 20
+#define MAX_FMT_IE_LEN 20
+
+/* values for gigaset_capi_appl.connected */
+#define APCONN_NONE 0 /* inactive/listening */
+#define APCONN_SETUP 1 /* connecting */
+#define APCONN_ACTIVE 2 /* B channel up */
+
+/* registered application data structure */
+struct gigaset_capi_appl {
+ struct list_head ctrlist;
+ struct gigaset_capi_appl *bcnext;
+ u16 id;
+ u16 nextMessageNumber;
+ u32 listenInfoMask;
+ u32 listenCIPmask;
+ int connected;
+};
+
+/* CAPI specific controller data structure */
+struct gigaset_capi_ctr {
+ struct capi_ctr ctr;
+ struct list_head appls;
+ struct sk_buff_head sendqueue;
+ atomic_t sendqlen;
+ /* two _cmsg structures possibly used concurrently: */
+ _cmsg hcmsg; /* for message composition triggered from hardware */
+ _cmsg acmsg; /* for dissection of messages sent from application */
+ u8 bc_buf[MAX_BC_OCTETS+1];
+ u8 hlc_buf[MAX_HLC_OCTETS+1];
+ u8 cgpty_buf[MAX_NUMBER_DIGITS+3];
+ u8 cdpty_buf[MAX_NUMBER_DIGITS+2];
+};
+
+/* CIP Value table (from CAPI 2.0 standard, ch. 6.1) */
+static struct {
+ u8 *bc;
+ u8 *hlc;
+} cip2bchlc[] = {
+ [1] = { "8090A3", NULL },
+ /* Speech (A-law) */
+ [2] = { "8890", NULL },
+ /* Unrestricted digital information */
+ [3] = { "8990", NULL },
+ /* Restricted digital information */
+ [4] = { "9090A3", NULL },
+ /* 3,1 kHz audio (A-law) */
+ [5] = { "9190", NULL },
+ /* 7 kHz audio */
+ [6] = { "9890", NULL },
+ /* Video */
+ [7] = { "88C0C6E6", NULL },
+ /* Packet mode */
+ [8] = { "8890218F", NULL },
+ /* 56 kbit/s rate adaptation */
+ [9] = { "9190A5", NULL },
+ /* Unrestricted digital information with tones/announcements */
+ [16] = { "8090A3", "9181" },
+ /* Telephony */
+ [17] = { "9090A3", "9184" },
+ /* Group 2/3 facsimile */
+ [18] = { "8890", "91A1" },
+ /* Group 4 facsimile Class 1 */
+ [19] = { "8890", "91A4" },
+ /* Teletex service basic and mixed mode
+ and Group 4 facsimile service Classes II and III */
+ [20] = { "8890", "91A8" },
+ /* Teletex service basic and processable mode */
+ [21] = { "8890", "91B1" },
+ /* Teletex service basic mode */
+ [22] = { "8890", "91B2" },
+ /* International interworking for Videotex */
+ [23] = { "8890", "91B5" },
+ /* Telex */
+ [24] = { "8890", "91B8" },
+ /* Message Handling Systems in accordance with X.400 */
+ [25] = { "8890", "91C1" },
+ /* OSI application in accordance with X.200 */
+ [26] = { "9190A5", "9181" },
+ /* 7 kHz telephony */
+ [27] = { "9190A5", "916001" },
+ /* Video telephony, first connection */
+ [28] = { "8890", "916002" },
+ /* Video telephony, second connection */
+};
+
+/*
+ * helper functions
+ * ================
+ */
+
+/*
+ * emit unsupported parameter warning
+ */
+static inline void ignore_cstruct_param(struct cardstate *cs, _cstruct param,
+ char *msgname, char *paramname)
+{
+ if (param && *param)
+ dev_warn(cs->dev, "%s: ignoring unsupported parameter: %s\n",
+ msgname, paramname);
+}
+
+/*
+ * check for legal hex digit
+ */
+static inline int ishexdigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ return 1;
+ if (c >= 'A' && c <= 'F')
+ return 1;
+ if (c >= 'a' && c <= 'f')
+ return 1;
+ return 0;
+}
+
+/*
+ * convert hex to binary
+ */
+static inline u8 hex2bin(char c)
+{
+ int result = c & 0x0f;
+ if (c & 0x40)
+ result += 9;
+ return result;
+}
+
+/*
+ * convert an IE from Gigaset hex string to ETSI binary representation
+ * including length byte
+ * return value: result length, -1 on error
+ */
+static int encode_ie(char *in, u8 *out, int maxlen)
+{
+ int l = 0;
+ while (*in) {
+ if (!ishexdigit(in[0]) || !ishexdigit(in[1]) || l >= maxlen)
+ return -1;
+ out[++l] = (hex2bin(in[0]) << 4) + hex2bin(in[1]);
+ in += 2;
+ }
+ out[0] = l;
+ return l;
+}
+
+/*
+ * convert an IE from ETSI binary representation including length byte
+ * to Gigaset hex string
+ */
+static void decode_ie(u8 *in, char *out)
+{
+ int i = *in;
+ while (i-- > 0) {
+ /* ToDo: conversion to upper case necessary? */
+ *out++ = toupper(hex_asc_hi(*++in));
+ *out++ = toupper(hex_asc_lo(*in));
+ }
+}
+
+/*
+ * retrieve application data structure for an application ID
+ */
+static inline struct gigaset_capi_appl *
+get_appl(struct gigaset_capi_ctr *iif, u16 appl)
+{
+ struct gigaset_capi_appl *ap;
+
+ list_for_each_entry(ap, &iif->appls, ctrlist)
+ if (ap->id == appl)
+ return ap;
+ return NULL;
+}
+
+/*
+ * dump CAPI message to kernel messages for debugging
+ */
+static inline void dump_cmsg(enum debuglevel level, const char *tag, _cmsg *p)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+ _cdebbuf *cdb;
+
+ if (!(gigaset_debuglevel & level))
+ return;
+
+ cdb = capi_cmsg2str(p);
+ if (cdb) {
+ gig_dbg(level, "%s: [%d] %s", tag, p->ApplId, cdb->buf);
+ cdebbuf_free(cdb);
+ } else {
+ gig_dbg(level, "%s: [%d] %s", tag, p->ApplId,
+ capi_cmd2str(p->Command, p->Subcommand));
+ }
+#endif
+}
+
+static inline void dump_rawmsg(enum debuglevel level, const char *tag,
+ unsigned char *data)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+ char *dbgline;
+ int i, l;
+
+ if (!(gigaset_debuglevel & level))
+ return;
+
+ l = CAPIMSG_LEN(data);
+ if (l < 12) {
+ gig_dbg(level, "%s: ??? LEN=%04d", tag, l);
+ return;
+ }
+ gig_dbg(level, "%s: 0x%02x:0x%02x: ID=%03d #0x%04x LEN=%04d NCCI=0x%x",
+ tag, CAPIMSG_COMMAND(data), CAPIMSG_SUBCOMMAND(data),
+ CAPIMSG_APPID(data), CAPIMSG_MSGID(data), l,
+ CAPIMSG_CONTROL(data));
+ l -= 12;
+ dbgline = kmalloc(3*l, GFP_ATOMIC);
+ if (!dbgline)
+ return;
+ for (i = 0; i < l; i++) {
+ dbgline[3*i] = hex_asc_hi(data[12+i]);
+ dbgline[3*i+1] = hex_asc_lo(data[12+i]);
+ dbgline[3*i+2] = ' ';
+ }
+ dbgline[3*l-1] = '\0';
+ gig_dbg(level, " %s", dbgline);
+ kfree(dbgline);
+ if (CAPIMSG_COMMAND(data) == CAPI_DATA_B3 &&
+ (CAPIMSG_SUBCOMMAND(data) == CAPI_REQ ||
+ CAPIMSG_SUBCOMMAND(data) == CAPI_IND) &&
+ CAPIMSG_DATALEN(data) > 0) {
+ l = CAPIMSG_DATALEN(data);
+ dbgline = kmalloc(3*l, GFP_ATOMIC);
+ if (!dbgline)
+ return;
+ data += CAPIMSG_LEN(data);
+ for (i = 0; i < l; i++) {
+ dbgline[3*i] = hex_asc_hi(data[i]);
+ dbgline[3*i+1] = hex_asc_lo(data[i]);
+ dbgline[3*i+2] = ' ';
+ }
+ dbgline[3*l-1] = '\0';
+ gig_dbg(level, " %s", dbgline);
+ kfree(dbgline);
+ }
+#endif
+}
+
+/*
+ * format CAPI IE as string
+ */
+
+static const char *format_ie(const char *ie)
+{
+ static char result[3*MAX_FMT_IE_LEN];
+ int len, count;
+ char *pout = result;
+
+ if (!ie)
+ return "NULL";
+
+ count = len = ie[0];
+ if (count > MAX_FMT_IE_LEN)
+ count = MAX_FMT_IE_LEN-1;
+ while (count--) {
+ *pout++ = hex_asc_hi(*++ie);
+ *pout++ = hex_asc_lo(*ie);
+ *pout++ = ' ';
+ }
+ if (len > MAX_FMT_IE_LEN) {
+ *pout++ = '.';
+ *pout++ = '.';
+ *pout++ = '.';
+ }
+ *--pout = 0;
+ return result;
+}
+
+
+/*
+ * driver interface functions
+ * ==========================
+ */
+
+/**
+ * gigaset_skb_sent() - acknowledge transmission of outgoing skb
+ * @bcs: B channel descriptor structure.
+ * @skb: sent data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when the data in a
+ * skb has been successfully sent, for signalling completion to the LL.
+ */
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_ctr *iif = cs->iif;
+ struct gigaset_capi_appl *ap = bcs->ap;
+ unsigned char *req = skb_mac_header(dskb);
+ struct sk_buff *cskb;
+ u16 flags;
+
+ /* update statistics */
+ ++bcs->trans_up;
+
+ if (!ap) {
+ dev_err(cs->dev, "%s: no application\n", __func__);
+ return;
+ }
+
+ /* don't send further B3 messages if disconnected */
+ if (ap->connected < APCONN_ACTIVE) {
+ gig_dbg(DEBUG_LLDATA, "disconnected, discarding ack");
+ return;
+ }
+
+ /* ToDo: honor unset "delivery confirmation" bit */
+ flags = CAPIMSG_FLAGS(req);
+
+ /* build DATA_B3_CONF message */
+ cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC);
+ if (!cskb) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ /* frequent message, avoid _cmsg overhead */
+ CAPIMSG_SETLEN(cskb->data, CAPI_DATA_B3_CONF_LEN);
+ CAPIMSG_SETAPPID(cskb->data, ap->id);
+ CAPIMSG_SETCOMMAND(cskb->data, CAPI_DATA_B3);
+ CAPIMSG_SETSUBCOMMAND(cskb->data, CAPI_CONF);
+ CAPIMSG_SETMSGID(cskb->data, CAPIMSG_MSGID(req));
+ CAPIMSG_SETCONTROLLER(cskb->data, iif->ctr.cnr);
+ CAPIMSG_SETPLCI_PART(cskb->data, bcs->channel + 1);
+ CAPIMSG_SETNCCI_PART(cskb->data, 1);
+ CAPIMSG_SETHANDLE_CONF(cskb->data, CAPIMSG_HANDLE_REQ(req));
+ if (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION)
+ CAPIMSG_SETINFO_CONF(cskb->data,
+ CapiFlagsNotSupportedByProtocol);
+ else
+ CAPIMSG_SETINFO_CONF(cskb->data, CAPI_NOERROR);
+
+ /* emit message */
+ dump_rawmsg(DEBUG_LLDATA, "DATA_B3_CONF", cskb->data);
+ capi_ctr_handle_message(&iif->ctr, ap->id, cskb);
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+
+/**
+ * gigaset_skb_rcvd() - pass received skb to LL
+ * @bcs: B channel descriptor structure.
+ * @skb: received data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when user data has
+ * been successfully received, for passing to the LL.
+ * Warning: skb must not be accessed anymore!
+ */
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_ctr *iif = cs->iif;
+ struct gigaset_capi_appl *ap = bcs->ap;
+ int len = skb->len;
+
+ /* update statistics */
+ bcs->trans_down++;
+
+ if (!ap) {
+ dev_err(cs->dev, "%s: no application\n", __func__);
+ return;
+ }
+
+ /* don't send further B3 messages if disconnected */
+ if (ap->connected < APCONN_ACTIVE) {
+ gig_dbg(DEBUG_LLDATA, "disconnected, discarding data");
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ /*
+ * prepend DATA_B3_IND message to payload
+ * Parameters: NCCI = 1, all others 0/unused
+ * frequent message, avoid _cmsg overhead
+ */
+ skb_push(skb, CAPI_DATA_B3_REQ_LEN);
+ CAPIMSG_SETLEN(skb->data, CAPI_DATA_B3_REQ_LEN);
+ CAPIMSG_SETAPPID(skb->data, ap->id);
+ CAPIMSG_SETCOMMAND(skb->data, CAPI_DATA_B3);
+ CAPIMSG_SETSUBCOMMAND(skb->data, CAPI_IND);
+ CAPIMSG_SETMSGID(skb->data, ap->nextMessageNumber++);
+ CAPIMSG_SETCONTROLLER(skb->data, iif->ctr.cnr);
+ CAPIMSG_SETPLCI_PART(skb->data, bcs->channel + 1);
+ CAPIMSG_SETNCCI_PART(skb->data, 1);
+ /* Data parameter not used */
+ CAPIMSG_SETDATALEN(skb->data, len);
+ /* Data handle parameter not used */
+ CAPIMSG_SETFLAGS(skb->data, 0);
+ /* Data64 parameter not present */
+
+ /* emit message */
+ dump_rawmsg(DEBUG_LLDATA, "DATA_B3_IND", skb->data);
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+/**
+ * gigaset_isdn_rcv_err() - signal receive error
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when a receive error
+ * has occurred, for signalling to the LL.
+ */
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+ /* if currently ignoring packets, just count down */
+ if (bcs->ignore) {
+ bcs->ignore--;
+ return;
+ }
+
+ /* update statistics */
+ bcs->corrupted++;
+
+ /* ToDo: signal error -> LL */
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
+/**
+ * gigaset_isdn_icall() - signal incoming call
+ * @at_state: connection state structure.
+ *
+ * Called by main module at tasklet level to notify the LL that an incoming
+ * call has been received. @at_state contains the parameters of the call.
+ *
+ * Return value: call disposition (ICALL_*)
+ */
+int gigaset_isdn_icall(struct at_state_t *at_state)
+{
+ struct cardstate *cs = at_state->cs;
+ struct bc_state *bcs = at_state->bcs;
+ struct gigaset_capi_ctr *iif = cs->iif;
+ struct gigaset_capi_appl *ap;
+ u32 actCIPmask;
+ struct sk_buff *skb;
+ unsigned int msgsize;
+ int i;
+
+ /*
+ * ToDo: signal calls without a free B channel, too
+ * (requires a u8 handle for the at_state structure that can
+ * be stored in the PLCI and used in the CONNECT_RESP message
+ * handler to retrieve it)
+ */
+ if (!bcs)
+ return ICALL_IGNORE;
+
+ /* prepare CONNECT_IND message, using B channel number as PLCI */
+ capi_cmsg_header(&iif->hcmsg, 0, CAPI_CONNECT, CAPI_IND, 0,
+ iif->ctr.cnr | ((bcs->channel + 1) << 8));
+
+ /* minimum size, all structs empty */
+ msgsize = CAPI_CONNECT_IND_BASELEN;
+
+ /* Bearer Capability (mandatory) */
+ if (at_state->str_var[STR_ZBC]) {
+ /* pass on BC from Gigaset */
+ if (encode_ie(at_state->str_var[STR_ZBC], iif->bc_buf,
+ MAX_BC_OCTETS) < 0) {
+ dev_warn(cs->dev, "RING ignored - bad BC %s\n",
+ at_state->str_var[STR_ZBC]);
+ return ICALL_IGNORE;
+ }
+
+ /* look up corresponding CIP value */
+ iif->hcmsg.CIPValue = 0; /* default if nothing found */
+ for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
+ if (cip2bchlc[i].bc != NULL &&
+ cip2bchlc[i].hlc == NULL &&
+ !strcmp(cip2bchlc[i].bc,
+ at_state->str_var[STR_ZBC])) {
+ iif->hcmsg.CIPValue = i;
+ break;
+ }
+ } else {
+ /* no BC (internal call): assume CIP 1 (speech, A-law) */
+ iif->hcmsg.CIPValue = 1;
+ encode_ie(cip2bchlc[1].bc, iif->bc_buf, MAX_BC_OCTETS);
+ }
+ iif->hcmsg.BC = iif->bc_buf;
+ msgsize += iif->hcmsg.BC[0];
+
+ /* High Layer Compatibility (optional) */
+ if (at_state->str_var[STR_ZHLC]) {
+ /* pass on HLC from Gigaset */
+ if (encode_ie(at_state->str_var[STR_ZHLC], iif->hlc_buf,
+ MAX_HLC_OCTETS) < 0) {
+ dev_warn(cs->dev, "RING ignored - bad HLC %s\n",
+ at_state->str_var[STR_ZHLC]);
+ return ICALL_IGNORE;
+ }
+ iif->hcmsg.HLC = iif->hlc_buf;
+ msgsize += iif->hcmsg.HLC[0];
+
+ /* look up corresponding CIP value */
+ /* keep BC based CIP value if none found */
+ if (at_state->str_var[STR_ZBC])
+ for (i = 0; i < ARRAY_SIZE(cip2bchlc); i++)
+ if (cip2bchlc[i].hlc != NULL &&
+ !strcmp(cip2bchlc[i].hlc,
+ at_state->str_var[STR_ZHLC]) &&
+ !strcmp(cip2bchlc[i].bc,
+ at_state->str_var[STR_ZBC])) {
+ iif->hcmsg.CIPValue = i;
+ break;
+ }
+ }
+
+ /* Called Party Number (optional) */
+ if (at_state->str_var[STR_ZCPN]) {
+ i = strlen(at_state->str_var[STR_ZCPN]);
+ if (i > MAX_NUMBER_DIGITS) {
+ dev_warn(cs->dev, "RING ignored - bad number %s\n",
+ at_state->str_var[STR_ZBC]);
+ return ICALL_IGNORE;
+ }
+ iif->cdpty_buf[0] = i + 1;
+ iif->cdpty_buf[1] = 0x80; /* type / numbering plan unknown */
+ memcpy(iif->cdpty_buf+2, at_state->str_var[STR_ZCPN], i);
+ iif->hcmsg.CalledPartyNumber = iif->cdpty_buf;
+ msgsize += iif->hcmsg.CalledPartyNumber[0];
+ }
+
+ /* Calling Party Number (optional) */
+ if (at_state->str_var[STR_NMBR]) {
+ i = strlen(at_state->str_var[STR_NMBR]);
+ if (i > MAX_NUMBER_DIGITS) {
+ dev_warn(cs->dev, "RING ignored - bad number %s\n",
+ at_state->str_var[STR_ZBC]);
+ return ICALL_IGNORE;
+ }
+ iif->cgpty_buf[0] = i + 2;
+ iif->cgpty_buf[1] = 0x00; /* type / numbering plan unknown */
+ iif->cgpty_buf[2] = 0x80; /* pres. allowed, not screened */
+ memcpy(iif->cgpty_buf+3, at_state->str_var[STR_NMBR], i);
+ iif->hcmsg.CallingPartyNumber = iif->cgpty_buf;
+ msgsize += iif->hcmsg.CallingPartyNumber[0];
+ }
+
+ /* remaining parameters (not supported, always left NULL):
+ * - CalledPartySubaddress
+ * - CallingPartySubaddress
+ * - AdditionalInfo
+ * - BChannelinformation
+ * - Keypadfacility
+ * - Useruserdata
+ * - Facilitydataarray
+ */
+
+ gig_dbg(DEBUG_CMD, "icall: PLCI %x CIP %d BC %s",
+ iif->hcmsg.adr.adrPLCI, iif->hcmsg.CIPValue,
+ format_ie(iif->hcmsg.BC));
+ gig_dbg(DEBUG_CMD, "icall: HLC %s",
+ format_ie(iif->hcmsg.HLC));
+ gig_dbg(DEBUG_CMD, "icall: CgPty %s",
+ format_ie(iif->hcmsg.CallingPartyNumber));
+ gig_dbg(DEBUG_CMD, "icall: CdPty %s",
+ format_ie(iif->hcmsg.CalledPartyNumber));
+
+ /* scan application list for matching listeners */
+ bcs->ap = NULL;
+ actCIPmask = 1 | (1 << iif->hcmsg.CIPValue);
+ list_for_each_entry(ap, &iif->appls, ctrlist)
+ if (actCIPmask & ap->listenCIPmask) {
+ /* build CONNECT_IND message for this application */
+ iif->hcmsg.ApplId = ap->id;
+ iif->hcmsg.Messagenumber = ap->nextMessageNumber++;
+
+ skb = alloc_skb(msgsize, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(cs->dev, "%s: out of memory\n",
+ __func__);
+ break;
+ }
+ capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
+ dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+
+ /* add to listeners on this B channel, update state */
+ ap->bcnext = bcs->ap;
+ bcs->ap = ap;
+ bcs->chstate |= CHS_NOTIFY_LL;
+ ap->connected = APCONN_SETUP;
+
+ /* emit message */
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+ }
+
+ /*
+ * Return "accept" if any listeners.
+ * Gigaset will send ALERTING.
+ * There doesn't seem to be a way to avoid this.
+ */
+ return bcs->ap ? ICALL_ACCEPT : ICALL_IGNORE;
+}
+
+/*
+ * send a DISCONNECT_IND message to an application
+ * does not sleep, clobbers the controller's hcmsg structure
+ */
+static void send_disconnect_ind(struct bc_state *bcs,
+ struct gigaset_capi_appl *ap, u16 reason)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_ctr *iif = cs->iif;
+ struct sk_buff *skb;
+
+ if (ap->connected == APCONN_NONE)
+ return;
+
+ capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND,
+ ap->nextMessageNumber++,
+ iif->ctr.cnr | ((bcs->channel + 1) << 8));
+ iif->hcmsg.Reason = reason;
+ skb = alloc_skb(CAPI_DISCONNECT_IND_LEN, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ capi_cmsg2message(&iif->hcmsg, __skb_put(skb, CAPI_DISCONNECT_IND_LEN));
+ dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+ ap->connected = APCONN_NONE;
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/*
+ * send a DISCONNECT_B3_IND message to an application
+ * Parameters: NCCI = 1, NCPI empty, Reason_B3 = 0
+ * does not sleep, clobbers the controller's hcmsg structure
+ */
+static void send_disconnect_b3_ind(struct bc_state *bcs,
+ struct gigaset_capi_appl *ap)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_ctr *iif = cs->iif;
+ struct sk_buff *skb;
+
+ /* nothing to do if no logical connection active */
+ if (ap->connected < APCONN_ACTIVE)
+ return;
+ ap->connected = APCONN_SETUP;
+
+ capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
+ ap->nextMessageNumber++,
+ iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+ skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ capi_cmsg2message(&iif->hcmsg,
+ __skb_put(skb, CAPI_DISCONNECT_B3_IND_BASELEN));
+ dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_connD() - signal D channel connect
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the D channel
+ * connection has been established.
+ */
+void gigaset_isdn_connD(struct bc_state *bcs)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_ctr *iif = cs->iif;
+ struct gigaset_capi_appl *ap = bcs->ap;
+ struct sk_buff *skb;
+ unsigned int msgsize;
+
+ if (!ap) {
+ dev_err(cs->dev, "%s: no application\n", __func__);
+ return;
+ }
+ while (ap->bcnext) {
+ /* this should never happen */
+ dev_warn(cs->dev, "%s: dropping extra application %u\n",
+ __func__, ap->bcnext->id);
+ send_disconnect_ind(bcs, ap->bcnext,
+ CapiCallGivenToOtherApplication);
+ ap->bcnext = ap->bcnext->bcnext;
+ }
+ if (ap->connected == APCONN_NONE) {
+ dev_warn(cs->dev, "%s: application %u not connected\n",
+ __func__, ap->id);
+ return;
+ }
+
+ /* prepare CONNECT_ACTIVE_IND message
+ * Note: LLC not supported by device
+ */
+ capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_CONNECT_ACTIVE, CAPI_IND,
+ ap->nextMessageNumber++,
+ iif->ctr.cnr | ((bcs->channel + 1) << 8));
+
+ /* minimum size, all structs empty */
+ msgsize = CAPI_CONNECT_ACTIVE_IND_BASELEN;
+
+ /* ToDo: set parameter: Connected number
+ * (requires ev-layer state machine extension to collect
+ * ZCON device reply)
+ */
+
+ /* build and emit CONNECT_ACTIVE_IND message */
+ skb = alloc_skb(msgsize, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
+ dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_hupD() - signal D channel hangup
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the D channel
+ * connection has been shut down.
+ */
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+ struct gigaset_capi_appl *ap;
+
+ /*
+ * ToDo: pass on reason code reported by device
+ * (requires ev-layer state machine extension to collect
+ * ZCAU device reply)
+ */
+ for (ap = bcs->ap; ap != NULL; ap = ap->bcnext) {
+ send_disconnect_b3_ind(bcs, ap);
+ send_disconnect_ind(bcs, ap, 0);
+ }
+ bcs->ap = NULL;
+}
+
+/**
+ * gigaset_isdn_connB() - signal B channel connect
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module at tasklet level to notify the LL that the B channel
+ * connection has been established.
+ */
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_ctr *iif = cs->iif;
+ struct gigaset_capi_appl *ap = bcs->ap;
+ struct sk_buff *skb;
+ unsigned int msgsize;
+ u8 command;
+
+ if (!ap) {
+ dev_err(cs->dev, "%s: no application\n", __func__);
+ return;
+ }
+ while (ap->bcnext) {
+ /* this should never happen */
+ dev_warn(cs->dev, "%s: dropping extra application %u\n",
+ __func__, ap->bcnext->id);
+ send_disconnect_ind(bcs, ap->bcnext,
+ CapiCallGivenToOtherApplication);
+ ap->bcnext = ap->bcnext->bcnext;
+ }
+ if (!ap->connected) {
+ dev_warn(cs->dev, "%s: application %u not connected\n",
+ __func__, ap->id);
+ return;
+ }
+
+ /*
+ * emit CONNECT_B3_ACTIVE_IND if we already got CONNECT_B3_REQ;
+ * otherwise we have to emit CONNECT_B3_IND first, and follow up with
+ * CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP
+ * Parameters in both cases always: NCCI = 1, NCPI empty
+ */
+ if (ap->connected >= APCONN_ACTIVE) {
+ command = CAPI_CONNECT_B3_ACTIVE;
+ msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
+ } else {
+ command = CAPI_CONNECT_B3;
+ msgsize = CAPI_CONNECT_B3_IND_BASELEN;
+ }
+ capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND,
+ ap->nextMessageNumber++,
+ iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
+ skb = alloc_skb(msgsize, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
+ dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
+ ap->connected = APCONN_ACTIVE;
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/**
+ * gigaset_isdn_hupB() - signal B channel hangup
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+ struct cardstate *cs = bcs->cs;
+ struct gigaset_capi_appl *ap = bcs->ap;
+
+ /* ToDo: assure order of DISCONNECT_B3_IND and DISCONNECT_IND ? */
+
+ if (!ap) {
+ dev_err(cs->dev, "%s: no application\n", __func__);
+ return;
+ }
+
+ send_disconnect_b3_ind(bcs, ap);
+}
+
+/**
+ * gigaset_isdn_start() - signal device availability
+ * @cs: device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is available for
+ * use.
+ */
+void gigaset_isdn_start(struct cardstate *cs)
+{
+ struct gigaset_capi_ctr *iif = cs->iif;
+
+ /* fill profile data: manufacturer name */
+ strcpy(iif->ctr.manu, "Siemens");
+ /* CAPI and device version */
+ iif->ctr.version.majorversion = 2; /* CAPI 2.0 */
+ iif->ctr.version.minorversion = 0;
+ /* ToDo: check/assert cs->gotfwver? */
+ iif->ctr.version.majormanuversion = cs->fwver[0];
+ iif->ctr.version.minormanuversion = cs->fwver[1];
+ /* number of B channels supported */
+ iif->ctr.profile.nbchannel = cs->channels;
+ /* global options: internal controller, supplementary services */
+ iif->ctr.profile.goptions = 0x11;
+ /* B1 protocols: 64 kbit/s HDLC or transparent */
+ iif->ctr.profile.support1 = 0x03;
+ /* B2 protocols: transparent only */
+ /* ToDo: X.75 SLP ? */
+ iif->ctr.profile.support2 = 0x02;
+ /* B3 protocols: transparent only */
+ iif->ctr.profile.support3 = 0x01;
+ /* no serial number */
+ strcpy(iif->ctr.serial, "0");
+ capi_ctr_ready(&iif->ctr);
+}
+
+/**
+ * gigaset_isdn_stop() - signal device unavailability
+ * @cs: device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is no longer
+ * available for use.
+ */
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+ struct gigaset_capi_ctr *iif = cs->iif;
+ capi_ctr_down(&iif->ctr);
+}
+
+/*
+ * kernel CAPI callback methods
+ * ============================
+ */
+
+/*
+ * load firmware
+ */
+static int gigaset_load_firmware(struct capi_ctr *ctr, capiloaddata *data)
+{
+ struct cardstate *cs = ctr->driverdata;
+
+ /* AVM specific operation, not needed for Gigaset -- ignore */
+ dev_notice(cs->dev, "load_firmware ignored\n");
+
+ return 0;
+}
+
+/*
+ * reset (deactivate) controller
+ */
+static void gigaset_reset_ctr(struct capi_ctr *ctr)
+{
+ struct cardstate *cs = ctr->driverdata;
+
+ /* AVM specific operation, not needed for Gigaset -- ignore */
+ dev_notice(cs->dev, "reset_ctr ignored\n");
+}
+
+/*
+ * register CAPI application
+ */
+static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl,
+ capi_register_params *rp)
+{
+ struct gigaset_capi_ctr *iif
+ = container_of(ctr, struct gigaset_capi_ctr, ctr);
+ struct cardstate *cs = ctr->driverdata;
+ struct gigaset_capi_appl *ap;
+
+ list_for_each_entry(ap, &iif->appls, ctrlist)
+ if (ap->id == appl) {
+ dev_notice(cs->dev,
+ "application %u already registered\n", appl);
+ return;
+ }
+
+ ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+ if (!ap) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ ap->id = appl;
+
+ list_add(&ap->ctrlist, &iif->appls);
+}
+
+/*
+ * release CAPI application
+ */
+static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl)
+{
+ struct gigaset_capi_ctr *iif
+ = container_of(ctr, struct gigaset_capi_ctr, ctr);
+ struct cardstate *cs = iif->ctr.driverdata;
+ struct gigaset_capi_appl *ap, *tmp;
+
+ list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist)
+ if (ap->id == appl) {
+ if (ap->connected != APCONN_NONE) {
+ dev_err(cs->dev,
+ "%s: application %u still connected\n",
+ __func__, ap->id);
+ /* ToDo: clear active connection */
+ }
+ list_del(&ap->ctrlist);
+ kfree(ap);
+ }
+
+}
+
+/*
+ * =====================================================================
+ * outgoing CAPI message handler
+ * =====================================================================
+ */
+
+/*
+ * helper function: emit reply message with given Info value
+ */
+static void send_conf(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb,
+ u16 info)
+{
+ /*
+ * _CONF replies always only have NCCI and Info parameters
+ * so they'll fit into the _REQ message skb
+ */
+ capi_cmsg_answer(&iif->acmsg);
+ iif->acmsg.Info = info;
+ capi_cmsg2message(&iif->acmsg, skb->data);
+ __skb_trim(skb, CAPI_STDCONF_LEN);
+ dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/*
+ * process FACILITY_REQ message
+ */
+static void do_facility_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ _cmsg *cmsg = &iif->acmsg;
+ struct sk_buff *cskb;
+ u8 *pparam;
+ unsigned int msgsize = CAPI_FACILITY_CONF_BASELEN;
+ u16 function, info;
+ static u8 confparam[10]; /* max. 9 octets + length byte */
+
+ /* decode message */
+ capi_message2cmsg(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+ /*
+ * Facility Request Parameter is not decoded by capi_message2cmsg()
+ * encoding depends on Facility Selector
+ */
+ switch (cmsg->FacilitySelector) {
+ case CAPI_FACILITY_DTMF: /* ToDo */
+ info = CapiFacilityNotSupported;
+ confparam[0] = 2; /* length */
+ /* DTMF information: Unknown DTMF request */
+ capimsg_setu16(confparam, 1, 2);
+ break;
+
+ case CAPI_FACILITY_V42BIS: /* not supported */
+ info = CapiFacilityNotSupported;
+ confparam[0] = 2; /* length */
+ /* V.42 bis information: not available */
+ capimsg_setu16(confparam, 1, 1);
+ break;
+
+ case CAPI_FACILITY_SUPPSVC:
+ /* decode Function parameter */
+ pparam = cmsg->FacilityRequestParameter;
+ if (pparam == NULL || *pparam < 2) {
+ dev_notice(cs->dev, "%s: %s missing\n", "FACILITY_REQ",
+ "Facility Request Parameter");
+ send_conf(iif, ap, skb, CapiIllMessageParmCoding);
+ return;
+ }
+ function = CAPIMSG_U16(pparam, 1);
+ switch (function) {
+ case CAPI_SUPPSVC_GETSUPPORTED:
+ info = CapiSuccess;
+ /* Supplementary Service specific parameter */
+ confparam[3] = 6; /* length */
+ /* Supplementary services info: Success */
+ capimsg_setu16(confparam, 4, CapiSuccess);
+ /* Supported Services: none */
+ capimsg_setu32(confparam, 6, 0);
+ break;
+ /* ToDo: add supported services */
+ default:
+ info = CapiFacilitySpecificFunctionNotSupported;
+ /* Supplementary Service specific parameter */
+ confparam[3] = 2; /* length */
+ /* Supplementary services info: not supported */
+ capimsg_setu16(confparam, 4,
+ CapiSupplementaryServiceNotSupported);
+ }
+
+ /* Facility confirmation parameter */
+ confparam[0] = confparam[3] + 3; /* total length */
+ /* Function: copy from _REQ message */
+ capimsg_setu16(confparam, 1, function);
+ /* Supplementary Service specific parameter already set above */
+ break;
+
+ case CAPI_FACILITY_WAKEUP: /* ToDo */
+ info = CapiFacilityNotSupported;
+ confparam[0] = 2; /* length */
+ /* Number of accepted awake request parameters: 0 */
+ capimsg_setu16(confparam, 1, 0);
+ break;
+
+ default:
+ info = CapiFacilityNotSupported;
+ confparam[0] = 0; /* empty struct */
+ }
+
+ /* send FACILITY_CONF with given Info and confirmation parameter */
+ capi_cmsg_answer(cmsg);
+ cmsg->Info = info;
+ cmsg->FacilityConfirmationParameter = confparam;
+ msgsize += confparam[0]; /* length */
+ cskb = alloc_skb(msgsize, GFP_ATOMIC);
+ if (!cskb) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return;
+ }
+ capi_cmsg2message(cmsg, __skb_put(cskb, msgsize));
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+ capi_ctr_handle_message(&iif->ctr, ap->id, cskb);
+}
+
+
+/*
+ * process LISTEN_REQ message
+ * just store the masks in the application data structure
+ */
+static void do_listen_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ /* decode message */
+ capi_message2cmsg(&iif->acmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+
+ /* store listening parameters */
+ ap->listenInfoMask = iif->acmsg.InfoMask;
+ ap->listenCIPmask = iif->acmsg.CIPmask;
+ send_conf(iif, ap, skb, CapiSuccess);
+}
+
+/*
+ * process ALERT_REQ message
+ * nothing to do, Gigaset always alerts anyway
+ */
+static void do_alert_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ /* decode message */
+ capi_message2cmsg(&iif->acmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+ send_conf(iif, ap, skb, CapiAlertAlreadySent);
+}
+
+/*
+ * process CONNECT_REQ message
+ * allocate a B channel, prepare dial commands, queue a DIAL event,
+ * emit CONNECT_CONF reply
+ */
+static void do_connect_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ _cmsg *cmsg = &iif->acmsg;
+ struct bc_state *bcs;
+ char **commands;
+ char *s;
+ u8 *pp;
+ int i, l;
+ u16 info;
+
+ /* decode message */
+ capi_message2cmsg(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+ /* get free B channel & construct PLCI */
+ bcs = gigaset_get_free_channel(cs);
+ if (!bcs) {
+ dev_notice(cs->dev, "%s: no B channel available\n",
+ "CONNECT_REQ");
+ send_conf(iif, ap, skb, CapiNoPlciAvailable);
+ return;
+ }
+ ap->bcnext = NULL;
+ bcs->ap = ap;
+ cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8;
+
+ /* build command table */
+ commands = kzalloc(AT_NUM*(sizeof *commands), GFP_KERNEL);
+ if (!commands)
+ goto oom;
+
+ /* encode parameter: Called party number */
+ pp = cmsg->CalledPartyNumber;
+ if (pp == NULL || *pp == 0) {
+ dev_notice(cs->dev, "%s: %s missing\n",
+ "CONNECT_REQ", "Called party number");
+ info = CapiIllMessageParmCoding;
+ goto error;
+ }
+ l = *pp++;
+ /* check type of number/numbering plan byte */
+ switch (*pp) {
+ case 0x80: /* unknown type / unknown numbering plan */
+ case 0x81: /* unknown type / ISDN/Telephony numbering plan */
+ break;
+ default: /* others: warn about potential misinterpretation */
+ dev_notice(cs->dev, "%s: %s type/plan 0x%02x unsupported\n",
+ "CONNECT_REQ", "Called party number", *pp);
+ }
+ pp++;
+ l--;
+ /* translate "**" internal call prefix to CTP value */
+ if (l >= 2 && pp[0] == '*' && pp[1] == '*') {
+ s = "^SCTP=0\r";
+ pp += 2;
+ l -= 2;
+ } else {
+ s = "^SCTP=1\r";
+ }
+ commands[AT_TYPE] = kstrdup(s, GFP_KERNEL);
+ if (!commands[AT_TYPE])
+ goto oom;
+ commands[AT_DIAL] = kmalloc(l+3, GFP_KERNEL);
+ if (!commands[AT_DIAL])
+ goto oom;
+ snprintf(commands[AT_DIAL], l+3, "D%.*s\r", l, pp);
+
+ /* encode parameter: Calling party number */
+ pp = cmsg->CallingPartyNumber;
+ if (pp != NULL && *pp > 0) {
+ l = *pp++;
+
+ /* check type of number/numbering plan byte */
+ /* ToDo: allow for/handle Ext=1? */
+ switch (*pp) {
+ case 0x00: /* unknown type / unknown numbering plan */
+ case 0x01: /* unknown type / ISDN/Telephony num. plan */
+ break;
+ default:
+ dev_notice(cs->dev,
+ "%s: %s type/plan 0x%02x unsupported\n",
+ "CONNECT_REQ", "Calling party number", *pp);
+ }
+ pp++;
+ l--;
+
+ /* check presentation indicator */
+ if (!l) {
+ dev_notice(cs->dev, "%s: %s IE truncated\n",
+ "CONNECT_REQ", "Calling party number");
+ info = CapiIllMessageParmCoding;
+ goto error;
+ }
+ switch (*pp & 0xfc) { /* ignore Screening indicator */
+ case 0x80: /* Presentation allowed */
+ s = "^SCLIP=1\r";
+ break;
+ case 0xa0: /* Presentation restricted */
+ s = "^SCLIP=0\r";
+ break;
+ default:
+ dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+ "CONNECT_REQ",
+ "Presentation/Screening indicator",
+ *pp);
+ s = "^SCLIP=1\r";
+ }
+ commands[AT_CLIP] = kstrdup(s, GFP_KERNEL);
+ if (!commands[AT_CLIP])
+ goto oom;
+ pp++;
+ l--;
+
+ if (l) {
+ /* number */
+ commands[AT_MSN] = kmalloc(l+8, GFP_KERNEL);
+ if (!commands[AT_MSN])
+ goto oom;
+ snprintf(commands[AT_MSN], l+8, "^SMSN=%*s\r", l, pp);
+ }
+ }
+
+ /* check parameter: CIP Value */
+ if (cmsg->CIPValue > ARRAY_SIZE(cip2bchlc) ||
+ (cmsg->CIPValue > 0 && cip2bchlc[cmsg->CIPValue].bc == NULL)) {
+ dev_notice(cs->dev, "%s: unknown CIP value %d\n",
+ "CONNECT_REQ", cmsg->CIPValue);
+ info = CapiCipValueUnknown;
+ goto error;
+ }
+
+ /* check/encode parameter: BC */
+ if (cmsg->BC && cmsg->BC[0]) {
+ /* explicit BC overrides CIP */
+ l = 2*cmsg->BC[0] + 7;
+ commands[AT_BC] = kmalloc(l, GFP_KERNEL);
+ if (!commands[AT_BC])
+ goto oom;
+ strcpy(commands[AT_BC], "^SBC=");
+ decode_ie(cmsg->BC, commands[AT_BC]+5);
+ strcpy(commands[AT_BC] + l - 2, "\r");
+ } else if (cip2bchlc[cmsg->CIPValue].bc) {
+ l = strlen(cip2bchlc[cmsg->CIPValue].bc) + 7;
+ commands[AT_BC] = kmalloc(l, GFP_KERNEL);
+ if (!commands[AT_BC])
+ goto oom;
+ snprintf(commands[AT_BC], l, "^SBC=%s\r",
+ cip2bchlc[cmsg->CIPValue].bc);
+ }
+
+ /* check/encode parameter: HLC */
+ if (cmsg->HLC && cmsg->HLC[0]) {
+ /* explicit HLC overrides CIP */
+ l = 2*cmsg->HLC[0] + 7;
+ commands[AT_HLC] = kmalloc(l, GFP_KERNEL);
+ if (!commands[AT_HLC])
+ goto oom;
+ strcpy(commands[AT_HLC], "^SHLC=");
+ decode_ie(cmsg->HLC, commands[AT_HLC]+5);
+ strcpy(commands[AT_HLC] + l - 2, "\r");
+ } else if (cip2bchlc[cmsg->CIPValue].hlc) {
+ l = strlen(cip2bchlc[cmsg->CIPValue].hlc) + 7;
+ commands[AT_HLC] = kmalloc(l, GFP_KERNEL);
+ if (!commands[AT_HLC])
+ goto oom;
+ snprintf(commands[AT_HLC], l, "^SHLC=%s\r",
+ cip2bchlc[cmsg->CIPValue].hlc);
+ }
+
+ /* check/encode parameter: B Protocol */
+ if (cmsg->BProtocol == CAPI_DEFAULT) {
+ bcs->proto2 = L2_HDLC;
+ dev_warn(cs->dev,
+ "B2 Protocol X.75 SLP unsupported, using Transparent\n");
+ } else {
+ switch (cmsg->B1protocol) {
+ case 0:
+ bcs->proto2 = L2_HDLC;
+ break;
+ case 1:
+ bcs->proto2 = L2_BITSYNC;
+ break;
+ default:
+ dev_warn(cs->dev,
+ "B1 Protocol %u unsupported, using Transparent\n",
+ cmsg->B1protocol);
+ bcs->proto2 = L2_BITSYNC;
+ }
+ if (cmsg->B2protocol != 1)
+ dev_warn(cs->dev,
+ "B2 Protocol %u unsupported, using Transparent\n",
+ cmsg->B2protocol);
+ if (cmsg->B3protocol != 0)
+ dev_warn(cs->dev,
+ "B3 Protocol %u unsupported, using Transparent\n",
+ cmsg->B3protocol);
+ ignore_cstruct_param(cs, cmsg->B1configuration,
+ "CONNECT_REQ", "B1 Configuration");
+ ignore_cstruct_param(cs, cmsg->B2configuration,
+ "CONNECT_REQ", "B2 Configuration");
+ ignore_cstruct_param(cs, cmsg->B3configuration,
+ "CONNECT_REQ", "B3 Configuration");
+ }
+ commands[AT_PROTO] = kmalloc(9, GFP_KERNEL);
+ if (!commands[AT_PROTO])
+ goto oom;
+ snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+
+ /* ToDo: check/encode remaining parameters */
+ ignore_cstruct_param(cs, cmsg->CalledPartySubaddress,
+ "CONNECT_REQ", "Called pty subaddr");
+ ignore_cstruct_param(cs, cmsg->CallingPartySubaddress,
+ "CONNECT_REQ", "Calling pty subaddr");
+ ignore_cstruct_param(cs, cmsg->LLC,
+ "CONNECT_REQ", "LLC");
+ if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
+ ignore_cstruct_param(cs, cmsg->BChannelinformation,
+ "CONNECT_REQ", "B Channel Information");
+ ignore_cstruct_param(cs, cmsg->Keypadfacility,
+ "CONNECT_REQ", "Keypad Facility");
+ ignore_cstruct_param(cs, cmsg->Useruserdata,
+ "CONNECT_REQ", "User-User Data");
+ ignore_cstruct_param(cs, cmsg->Facilitydataarray,
+ "CONNECT_REQ", "Facility Data Array");
+ }
+
+ /* encode parameter: B channel to use */
+ commands[AT_ISO] = kmalloc(9, GFP_KERNEL);
+ if (!commands[AT_ISO])
+ goto oom;
+ snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
+ (unsigned) bcs->channel + 1);
+
+ /* queue & schedule EV_DIAL event */
+ if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
+ bcs->at_state.seq_index, NULL))
+ goto oom;
+ gig_dbg(DEBUG_CMD, "scheduling DIAL");
+ gigaset_schedule_event(cs);
+ ap->connected = APCONN_SETUP;
+ send_conf(iif, ap, skb, CapiSuccess);
+ return;
+
+oom:
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ info = CAPI_MSGOSRESOURCEERR;
+error:
+ if (commands)
+ for (i = 0; i < AT_NUM; i++)
+ kfree(commands[i]);
+ kfree(commands);
+ gigaset_free_channel(bcs);
+ send_conf(iif, ap, skb, info);
+}
+
+/*
+ * process CONNECT_RESP message
+ * checks protocol parameters and queues an ACCEPT or HUP event
+ */
+static void do_connect_resp(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ _cmsg *cmsg = &iif->acmsg;
+ struct bc_state *bcs;
+ struct gigaset_capi_appl *oap;
+ int channel;
+
+ /* decode message */
+ capi_message2cmsg(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+ dev_kfree_skb_any(skb);
+
+ /* extract and check channel number from PLCI */
+ channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
+ if (!channel || channel > cs->channels) {
+ dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+ "CONNECT_RESP", "PLCI", cmsg->adr.adrPLCI);
+ return;
+ }
+ bcs = cs->bcs + channel - 1;
+
+ switch (cmsg->Reject) {
+ case 0: /* Accept */
+ /* drop all competing applications, keep only this one */
+ for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
+ if (oap != ap)
+ send_disconnect_ind(bcs, oap,
+ CapiCallGivenToOtherApplication);
+ ap->bcnext = NULL;
+ bcs->ap = ap;
+ bcs->chstate |= CHS_NOTIFY_LL;
+
+ /* check/encode B channel protocol */
+ if (cmsg->BProtocol == CAPI_DEFAULT) {
+ bcs->proto2 = L2_HDLC;
+ dev_warn(cs->dev,
+ "B2 Protocol X.75 SLP unsupported, using Transparent\n");
+ } else {
+ switch (cmsg->B1protocol) {
+ case 0:
+ bcs->proto2 = L2_HDLC;
+ break;
+ case 1:
+ bcs->proto2 = L2_BITSYNC;
+ break;
+ default:
+ dev_warn(cs->dev,
+ "B1 Protocol %u unsupported, using Transparent\n",
+ cmsg->B1protocol);
+ bcs->proto2 = L2_BITSYNC;
+ }
+ if (cmsg->B2protocol != 1)
+ dev_warn(cs->dev,
+ "B2 Protocol %u unsupported, using Transparent\n",
+ cmsg->B2protocol);
+ if (cmsg->B3protocol != 0)
+ dev_warn(cs->dev,
+ "B3 Protocol %u unsupported, using Transparent\n",
+ cmsg->B3protocol);
+ ignore_cstruct_param(cs, cmsg->B1configuration,
+ "CONNECT_RESP", "B1 Configuration");
+ ignore_cstruct_param(cs, cmsg->B2configuration,
+ "CONNECT_RESP", "B2 Configuration");
+ ignore_cstruct_param(cs, cmsg->B3configuration,
+ "CONNECT_RESP", "B3 Configuration");
+ }
+
+ /* ToDo: check/encode remaining parameters */
+ ignore_cstruct_param(cs, cmsg->ConnectedNumber,
+ "CONNECT_RESP", "Connected Number");
+ ignore_cstruct_param(cs, cmsg->ConnectedSubaddress,
+ "CONNECT_RESP", "Connected Subaddress");
+ ignore_cstruct_param(cs, cmsg->LLC,
+ "CONNECT_RESP", "LLC");
+ if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
+ ignore_cstruct_param(cs, cmsg->BChannelinformation,
+ "CONNECT_RESP", "BChannel Information");
+ ignore_cstruct_param(cs, cmsg->Keypadfacility,
+ "CONNECT_RESP", "Keypad Facility");
+ ignore_cstruct_param(cs, cmsg->Useruserdata,
+ "CONNECT_RESP", "User-User Data");
+ ignore_cstruct_param(cs, cmsg->Facilitydataarray,
+ "CONNECT_RESP", "Facility Data Array");
+ }
+
+ /* Accept call */
+ if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
+ EV_ACCEPT, NULL, 0, NULL))
+ return;
+ gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
+ gigaset_schedule_event(cs);
+ return;
+
+ case 1: /* Ignore */
+ /* send DISCONNECT_IND to this application */
+ send_disconnect_ind(bcs, ap, 0);
+
+ /* remove it from the list of listening apps */
+ if (bcs->ap == ap) {
+ bcs->ap = ap->bcnext;
+ if (bcs->ap == NULL)
+ /* last one: stop ev-layer hupD notifications */
+ bcs->chstate &= ~CHS_NOTIFY_LL;
+ return;
+ }
+ for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) {
+ if (oap->bcnext == ap) {
+ oap->bcnext = oap->bcnext->bcnext;
+ return;
+ }
+ }
+ dev_err(cs->dev, "%s: application %u not found\n",
+ __func__, ap->id);
+ return;
+
+ default: /* Reject */
+ /* drop all competing applications, keep only this one */
+ for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
+ if (oap != ap)
+ send_disconnect_ind(bcs, oap,
+ CapiCallGivenToOtherApplication);
+ ap->bcnext = NULL;
+ bcs->ap = ap;
+
+ /* reject call - will trigger DISCONNECT_IND for this app */
+ dev_info(cs->dev, "%s: Reject=%x\n",
+ "CONNECT_RESP", cmsg->Reject);
+ if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
+ EV_HUP, NULL, 0, NULL))
+ return;
+ gig_dbg(DEBUG_CMD, "scheduling HUP");
+ gigaset_schedule_event(cs);
+ return;
+ }
+}
+
+/*
+ * process CONNECT_B3_REQ message
+ * build NCCI and emit CONNECT_B3_CONF reply
+ */
+static void do_connect_b3_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ _cmsg *cmsg = &iif->acmsg;
+ int channel;
+
+ /* decode message */
+ capi_message2cmsg(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+ /* extract and check channel number from PLCI */
+ channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
+ if (!channel || channel > cs->channels) {
+ dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+ "CONNECT_B3_REQ", "PLCI", cmsg->adr.adrPLCI);
+ send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+ return;
+ }
+
+ /* mark logical connection active */
+ ap->connected = APCONN_ACTIVE;
+
+ /* build NCCI: always 1 (one B3 connection only) */
+ cmsg->adr.adrNCCI |= 1 << 16;
+
+ /* NCPI parameter: not applicable for B3 Transparent */
+ ignore_cstruct_param(cs, cmsg->NCPI, "CONNECT_B3_REQ", "NCPI");
+ send_conf(iif, ap, skb, (cmsg->NCPI && cmsg->NCPI[0]) ?
+ CapiNcpiNotSupportedByProtocol : CapiSuccess);
+}
+
+/*
+ * process CONNECT_B3_RESP message
+ * Depending on the Reject parameter, either emit CONNECT_B3_ACTIVE_IND
+ * or queue EV_HUP and emit DISCONNECT_B3_IND.
+ * The emitted message is always shorter than the received one,
+ * allowing to reuse the skb.
+ */
+static void do_connect_b3_resp(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ _cmsg *cmsg = &iif->acmsg;
+ struct bc_state *bcs;
+ int channel;
+ unsigned int msgsize;
+ u8 command;
+
+ /* decode message */
+ capi_message2cmsg(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+ /* extract and check channel number and NCCI */
+ channel = (cmsg->adr.adrNCCI >> 8) & 0xff;
+ if (!channel || channel > cs->channels ||
+ ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) {
+ dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+ "CONNECT_B3_RESP", "NCCI", cmsg->adr.adrNCCI);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+ bcs = &cs->bcs[channel-1];
+
+ if (cmsg->Reject) {
+ /* Reject: clear B3 connect received flag */
+ ap->connected = APCONN_SETUP;
+
+ /* trigger hangup, causing eventual DISCONNECT_IND */
+ if (!gigaset_add_event(cs, &bcs->at_state,
+ EV_HUP, NULL, 0, NULL)) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+ gig_dbg(DEBUG_CMD, "scheduling HUP");
+ gigaset_schedule_event(cs);
+
+ /* emit DISCONNECT_B3_IND */
+ command = CAPI_DISCONNECT_B3;
+ msgsize = CAPI_DISCONNECT_B3_IND_BASELEN;
+ } else {
+ /*
+ * Accept: emit CONNECT_B3_ACTIVE_IND immediately, as
+ * we only send CONNECT_B3_IND if the B channel is up
+ */
+ command = CAPI_CONNECT_B3_ACTIVE;
+ msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
+ }
+ capi_cmsg_header(cmsg, ap->id, command, CAPI_IND,
+ ap->nextMessageNumber++, cmsg->adr.adrNCCI);
+ __skb_trim(skb, msgsize);
+ capi_cmsg2message(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+ capi_ctr_handle_message(&iif->ctr, ap->id, skb);
+}
+
+/*
+ * process DISCONNECT_REQ message
+ * schedule EV_HUP and emit DISCONNECT_B3_IND if necessary,
+ * emit DISCONNECT_CONF reply
+ */
+static void do_disconnect_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ _cmsg *cmsg = &iif->acmsg;
+ struct bc_state *bcs;
+ _cmsg *b3cmsg;
+ struct sk_buff *b3skb;
+ int channel;
+
+ /* decode message */
+ capi_message2cmsg(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+ /* extract and check channel number from PLCI */
+ channel = (cmsg->adr.adrPLCI >> 8) & 0xff;
+ if (!channel || channel > cs->channels) {
+ dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+ "DISCONNECT_REQ", "PLCI", cmsg->adr.adrPLCI);
+ send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+ return;
+ }
+ bcs = cs->bcs + channel - 1;
+
+ /* ToDo: process parameter: Additional info */
+ if (cmsg->AdditionalInfo != CAPI_DEFAULT) {
+ ignore_cstruct_param(cs, cmsg->BChannelinformation,
+ "DISCONNECT_REQ", "B Channel Information");
+ ignore_cstruct_param(cs, cmsg->Keypadfacility,
+ "DISCONNECT_REQ", "Keypad Facility");
+ ignore_cstruct_param(cs, cmsg->Useruserdata,
+ "DISCONNECT_REQ", "User-User Data");
+ ignore_cstruct_param(cs, cmsg->Facilitydataarray,
+ "DISCONNECT_REQ", "Facility Data Array");
+ }
+
+ /* skip if DISCONNECT_IND already sent */
+ if (!ap->connected)
+ return;
+
+ /* check for active logical connection */
+ if (ap->connected >= APCONN_ACTIVE) {
+ /*
+ * emit DISCONNECT_B3_IND with cause 0x3301
+ * use separate cmsg structure, as the content of iif->acmsg
+ * is still needed for creating the _CONF message
+ */
+ b3cmsg = kmalloc(sizeof(*b3cmsg), GFP_KERNEL);
+ if (!b3cmsg) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+ return;
+ }
+ capi_cmsg_header(b3cmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
+ ap->nextMessageNumber++,
+ cmsg->adr.adrPLCI | (1 << 16));
+ b3cmsg->Reason_B3 = CapiProtocolErrorLayer1;
+ b3skb = alloc_skb(CAPI_DISCONNECT_B3_IND_BASELEN, GFP_KERNEL);
+ if (b3skb == NULL) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+ return;
+ }
+ capi_cmsg2message(b3cmsg,
+ __skb_put(b3skb, CAPI_DISCONNECT_B3_IND_BASELEN));
+ kfree(b3cmsg);
+ capi_ctr_handle_message(&iif->ctr, ap->id, b3skb);
+ }
+
+ /* trigger hangup, causing eventual DISCONNECT_IND */
+ if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+ return;
+ }
+ gig_dbg(DEBUG_CMD, "scheduling HUP");
+ gigaset_schedule_event(cs);
+
+ /* emit reply */
+ send_conf(iif, ap, skb, CapiSuccess);
+}
+
+/*
+ * process DISCONNECT_B3_REQ message
+ * schedule EV_HUP and emit DISCONNECT_B3_CONF reply
+ */
+static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ _cmsg *cmsg = &iif->acmsg;
+ int channel;
+
+ /* decode message */
+ capi_message2cmsg(cmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, cmsg);
+
+ /* extract and check channel number and NCCI */
+ channel = (cmsg->adr.adrNCCI >> 8) & 0xff;
+ if (!channel || channel > cs->channels ||
+ ((cmsg->adr.adrNCCI >> 16) & 0xffff) != 1) {
+ dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+ "DISCONNECT_B3_REQ", "NCCI", cmsg->adr.adrNCCI);
+ send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+ return;
+ }
+
+ /* reject if logical connection not active */
+ if (ap->connected < APCONN_ACTIVE) {
+ send_conf(iif, ap, skb,
+ CapiMessageNotSupportedInCurrentState);
+ return;
+ }
+
+ /* trigger hangup, causing eventual DISCONNECT_B3_IND */
+ if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
+ EV_HUP, NULL, 0, NULL)) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+ return;
+ }
+ gig_dbg(DEBUG_CMD, "scheduling HUP");
+ gigaset_schedule_event(cs);
+
+ /* NCPI parameter: not applicable for B3 Transparent */
+ ignore_cstruct_param(cs, cmsg->NCPI,
+ "DISCONNECT_B3_REQ", "NCPI");
+ send_conf(iif, ap, skb, (cmsg->NCPI && cmsg->NCPI[0]) ?
+ CapiNcpiNotSupportedByProtocol : CapiSuccess);
+}
+
+/*
+ * process DATA_B3_REQ message
+ */
+static void do_data_b3_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs = iif->ctr.driverdata;
+ int channel = CAPIMSG_PLCI_PART(skb->data);
+ u16 ncci = CAPIMSG_NCCI_PART(skb->data);
+ u16 msglen = CAPIMSG_LEN(skb->data);
+ u16 datalen = CAPIMSG_DATALEN(skb->data);
+ u16 flags = CAPIMSG_FLAGS(skb->data);
+
+ /* frequent message, avoid _cmsg overhead */
+ dump_rawmsg(DEBUG_LLDATA, "DATA_B3_REQ", skb->data);
+
+ gig_dbg(DEBUG_LLDATA,
+ "Receiving data from LL (ch: %d, flg: %x, sz: %d|%d)",
+ channel, flags, msglen, datalen);
+
+ /* check parameters */
+ if (channel == 0 || channel > cs->channels || ncci != 1) {
+ dev_notice(cs->dev, "%s: invalid %s 0x%02x\n",
+ "DATA_B3_REQ", "NCCI", CAPIMSG_NCCI(skb->data));
+ send_conf(iif, ap, skb, CapiIllContrPlciNcci);
+ return;
+ }
+ if (msglen != CAPI_DATA_B3_REQ_LEN && msglen != CAPI_DATA_B3_REQ_LEN64)
+ dev_notice(cs->dev, "%s: unexpected length %d\n",
+ "DATA_B3_REQ", msglen);
+ if (msglen + datalen != skb->len)
+ dev_notice(cs->dev, "%s: length mismatch (%d+%d!=%d)\n",
+ "DATA_B3_REQ", msglen, datalen, skb->len);
+ if (msglen + datalen > skb->len) {
+ /* message too short for announced data length */
+ send_conf(iif, ap, skb, CapiIllMessageParmCoding); /* ? */
+ return;
+ }
+ if (flags & CAPI_FLAGS_RESERVED) {
+ dev_notice(cs->dev, "%s: reserved flags set (%x)\n",
+ "DATA_B3_REQ", flags);
+ send_conf(iif, ap, skb, CapiIllMessageParmCoding);
+ return;
+ }
+
+ /* reject if logical connection not active */
+ if (ap->connected < APCONN_ACTIVE) {
+ send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
+ return;
+ }
+
+ /* pull CAPI message into link layer header */
+ skb_reset_mac_header(skb);
+ skb->mac_len = msglen;
+ skb_pull(skb, msglen);
+
+ /* pass to device-specific module */
+ if (cs->ops->send_skb(&cs->bcs[channel-1], skb) < 0) {
+ send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
+ return;
+ }
+
+ /* DATA_B3_CONF reply will be sent by gigaset_skb_sent() */
+
+ /*
+ * ToDo: honor unset "delivery confirmation" bit
+ * (send DATA_B3_CONF immediately?)
+ */
+}
+
+/*
+ * process RESET_B3_REQ message
+ * just always reply "not supported by current protocol"
+ */
+static void do_reset_b3_req(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ /* decode message */
+ capi_message2cmsg(&iif->acmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+ send_conf(iif, ap, skb,
+ CapiResetProcedureNotSupportedByCurrentProtocol);
+}
+
+/*
+ * dump unsupported/ignored messages at most twice per minute,
+ * some apps send those very frequently
+ */
+static unsigned long ignored_msg_dump_time;
+
+/*
+ * unsupported CAPI message handler
+ */
+static void do_unsupported(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ /* decode message */
+ capi_message2cmsg(&iif->acmsg, skb->data);
+ if (printk_timed_ratelimit(&ignored_msg_dump_time, 30 * 1000))
+ dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+ send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
+}
+
+/*
+ * CAPI message handler: no-op
+ */
+static void do_nothing(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ if (printk_timed_ratelimit(&ignored_msg_dump_time, 30 * 1000)) {
+ /* decode message */
+ capi_message2cmsg(&iif->acmsg, skb->data);
+ dump_cmsg(DEBUG_CMD, __func__, &iif->acmsg);
+ }
+ dev_kfree_skb_any(skb);
+}
+
+static void do_data_b3_resp(struct gigaset_capi_ctr *iif,
+ struct gigaset_capi_appl *ap,
+ struct sk_buff *skb)
+{
+ dump_rawmsg(DEBUG_LLDATA, __func__, skb->data);
+ dev_kfree_skb_any(skb);
+}
+
+/* table of outgoing CAPI message handlers with lookup function */
+typedef void (*capi_send_handler_t)(struct gigaset_capi_ctr *,
+ struct gigaset_capi_appl *,
+ struct sk_buff *);
+
+static struct {
+ u16 cmd;
+ capi_send_handler_t handler;
+} capi_send_handler_table[] = {
+ /* most frequent messages first for faster lookup */
+ { CAPI_DATA_B3_REQ, do_data_b3_req },
+ { CAPI_DATA_B3_RESP, do_data_b3_resp },
+
+ { CAPI_ALERT_REQ, do_alert_req },
+ { CAPI_CONNECT_ACTIVE_RESP, do_nothing },
+ { CAPI_CONNECT_B3_ACTIVE_RESP, do_nothing },
+ { CAPI_CONNECT_B3_REQ, do_connect_b3_req },
+ { CAPI_CONNECT_B3_RESP, do_connect_b3_resp },
+ { CAPI_CONNECT_B3_T90_ACTIVE_RESP, do_nothing },
+ { CAPI_CONNECT_REQ, do_connect_req },
+ { CAPI_CONNECT_RESP, do_connect_resp },
+ { CAPI_DISCONNECT_B3_REQ, do_disconnect_b3_req },
+ { CAPI_DISCONNECT_B3_RESP, do_nothing },
+ { CAPI_DISCONNECT_REQ, do_disconnect_req },
+ { CAPI_DISCONNECT_RESP, do_nothing },
+ { CAPI_FACILITY_REQ, do_facility_req },
+ { CAPI_FACILITY_RESP, do_nothing },
+ { CAPI_LISTEN_REQ, do_listen_req },
+ { CAPI_SELECT_B_PROTOCOL_REQ, do_unsupported },
+ { CAPI_RESET_B3_REQ, do_reset_b3_req },
+ { CAPI_RESET_B3_RESP, do_nothing },
+
+ /*
+ * ToDo: support overlap sending (requires ev-layer state
+ * machine extension to generate additional ATD commands)
+ */
+ { CAPI_INFO_REQ, do_unsupported },
+ { CAPI_INFO_RESP, do_nothing },
+
+ /*
+ * ToDo: what's the proper response for these?
+ */
+ { CAPI_MANUFACTURER_REQ, do_nothing },
+ { CAPI_MANUFACTURER_RESP, do_nothing },
+};
+
+/* look up handler */
+static inline capi_send_handler_t lookup_capi_send_handler(const u16 cmd)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(capi_send_handler_table); i++)
+ if (capi_send_handler_table[i].cmd == cmd)
+ return capi_send_handler_table[i].handler;
+ return NULL;
+}
+
+
+/**
+ * gigaset_send_message() - accept a CAPI message from an application
+ * @ctr: controller descriptor structure.
+ * @skb: CAPI message.
+ *
+ * Return value: CAPI error code
+ * Note: capidrv (and probably others, too) only uses the return value to
+ * decide whether it has to free the skb (only if result != CAPI_NOERROR (0))
+ */
+static u16 gigaset_send_message(struct capi_ctr *ctr, struct sk_buff *skb)
+{
+ struct gigaset_capi_ctr *iif
+ = container_of(ctr, struct gigaset_capi_ctr, ctr);
+ struct cardstate *cs = ctr->driverdata;
+ struct gigaset_capi_appl *ap;
+ capi_send_handler_t handler;
+
+ /* can only handle linear sk_buffs */
+ if (skb_linearize(skb) < 0) {
+ dev_warn(cs->dev, "%s: skb_linearize failed\n", __func__);
+ return CAPI_MSGOSRESOURCEERR;
+ }
+
+ /* retrieve application data structure */
+ ap = get_appl(iif, CAPIMSG_APPID(skb->data));
+ if (!ap) {
+ dev_notice(cs->dev, "%s: application %u not registered\n",
+ __func__, CAPIMSG_APPID(skb->data));
+ return CAPI_ILLAPPNR;
+ }
+
+ /* look up command */
+ handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data));
+ if (!handler) {
+ /* unknown/unsupported message type */
+ if (printk_ratelimit())
+ dev_notice(cs->dev, "%s: unsupported message %u\n",
+ __func__, CAPIMSG_CMD(skb->data));
+ return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+ }
+
+ /* serialize */
+ if (atomic_add_return(1, &iif->sendqlen) > 1) {
+ /* queue behind other messages */
+ skb_queue_tail(&iif->sendqueue, skb);
+ return CAPI_NOERROR;
+ }
+
+ /* process message */
+ handler(iif, ap, skb);
+
+ /* process other messages arrived in the meantime */
+ while (atomic_sub_return(1, &iif->sendqlen) > 0) {
+ skb = skb_dequeue(&iif->sendqueue);
+ if (!skb) {
+ /* should never happen */
+ dev_err(cs->dev, "%s: send queue empty\n", __func__);
+ continue;
+ }
+ ap = get_appl(iif, CAPIMSG_APPID(skb->data));
+ if (!ap) {
+ /* could that happen? */
+ dev_warn(cs->dev, "%s: application %u vanished\n",
+ __func__, CAPIMSG_APPID(skb->data));
+ continue;
+ }
+ handler = lookup_capi_send_handler(CAPIMSG_CMD(skb->data));
+ if (!handler) {
+ /* should never happen */
+ dev_err(cs->dev, "%s: handler %x vanished\n",
+ __func__, CAPIMSG_CMD(skb->data));
+ continue;
+ }
+ handler(iif, ap, skb);
+ }
+
+ return CAPI_NOERROR;
+}
+
+/**
+ * gigaset_procinfo() - build single line description for controller
+ * @ctr: controller descriptor structure.
+ *
+ * Return value: pointer to generated string (null terminated)
+ */
+static char *gigaset_procinfo(struct capi_ctr *ctr)
+{
+ return ctr->name; /* ToDo: more? */
+}
+
+/**
+ * gigaset_ctr_read_proc() - build controller proc file entry
+ * @page: buffer of PAGE_SIZE bytes for receiving the entry.
+ * @start: unused.
+ * @off: unused.
+ * @count: unused.
+ * @eof: unused.
+ * @ctr: controller descriptor structure.
+ *
+ * Return value: length of generated entry
+ */
+static int gigaset_ctr_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctr)
+{
+ struct cardstate *cs = ctr->driverdata;
+ char *s;
+ int i;
+ int len = 0;
+ len += sprintf(page+len, "%-16s %s\n", "name", ctr->name);
+ len += sprintf(page+len, "%-16s %s %s\n", "dev",
+ dev_driver_string(cs->dev), dev_name(cs->dev));
+ len += sprintf(page+len, "%-16s %d\n", "id", cs->myid);
+ if (cs->gotfwver)
+ len += sprintf(page+len, "%-16s %d.%d.%d.%d\n", "firmware",
+ cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]);
+ len += sprintf(page+len, "%-16s %d\n", "channels",
+ cs->channels);
+ len += sprintf(page+len, "%-16s %s\n", "onechannel",
+ cs->onechannel ? "yes" : "no");
+
+ switch (cs->mode) {
+ case M_UNKNOWN:
+ s = "unknown";
+ break;
+ case M_CONFIG:
+ s = "config";
+ break;
+ case M_UNIMODEM:
+ s = "Unimodem";
+ break;
+ case M_CID:
+ s = "CID";
+ break;
+ default:
+ s = "??";
+ }
+ len += sprintf(page+len, "%-16s %s\n", "mode", s);
+
+ switch (cs->mstate) {
+ case MS_UNINITIALIZED:
+ s = "uninitialized";
+ break;
+ case MS_INIT:
+ s = "init";
+ break;
+ case MS_LOCKED:
+ s = "locked";
+ break;
+ case MS_SHUTDOWN:
+ s = "shutdown";
+ break;
+ case MS_RECOVER:
+ s = "recover";
+ break;
+ case MS_READY:
+ s = "ready";
+ break;
+ default:
+ s = "??";
+ }
+ len += sprintf(page+len, "%-16s %s\n", "mstate", s);
+
+ len += sprintf(page+len, "%-16s %s\n", "running",
+ cs->running ? "yes" : "no");
+ len += sprintf(page+len, "%-16s %s\n", "connected",
+ cs->connected ? "yes" : "no");
+ len += sprintf(page+len, "%-16s %s\n", "isdn_up",
+ cs->isdn_up ? "yes" : "no");
+ len += sprintf(page+len, "%-16s %s\n", "cidmode",
+ cs->cidmode ? "yes" : "no");
+
+ for (i = 0; i < cs->channels; i++) {
+ len += sprintf(page+len, "[%d]%-13s %d\n", i, "corrupted",
+ cs->bcs[i].corrupted);
+ len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_down",
+ cs->bcs[i].trans_down);
+ len += sprintf(page+len, "[%d]%-13s %d\n", i, "trans_up",
+ cs->bcs[i].trans_up);
+ len += sprintf(page+len, "[%d]%-13s %d\n", i, "chstate",
+ cs->bcs[i].chstate);
+ switch (cs->bcs[i].proto2) {
+ case L2_BITSYNC:
+ s = "bitsync";
+ break;
+ case L2_HDLC:
+ s = "HDLC";
+ break;
+ case L2_VOICE:
+ s = "voice";
+ break;
+ default:
+ s = "??";
+ }
+ len += sprintf(page+len, "[%d]%-13s %s\n", i, "proto2", s);
+ }
+ return len;
+}
+
+
+static struct capi_driver capi_driver_gigaset = {
+ .name = "gigaset",
+ .revision = "1.0",
+};
+
+/**
+ * gigaset_isdn_register() - register to LL
+ * @cs: device descriptor structure.
+ * @isdnid: device name.
+ *
+ * Called by main module to register the device with the LL.
+ *
+ * Return value: 1 for success, 0 for failure
+ */
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid)
+{
+ struct gigaset_capi_ctr *iif;
+ int rc;
+
+ pr_info("Kernel CAPI interface\n");
+
+ iif = kmalloc(sizeof(*iif), GFP_KERNEL);
+ if (!iif) {
+ pr_err("%s: out of memory\n", __func__);
+ return 0;
+ }
+
+ /* register driver with CAPI (ToDo: what for?) */
+ register_capi_driver(&capi_driver_gigaset);
+
+ /* prepare controller structure */
+ iif->ctr.owner = THIS_MODULE;
+ iif->ctr.driverdata = cs;
+ strncpy(iif->ctr.name, isdnid, sizeof(iif->ctr.name));
+ iif->ctr.driver_name = "gigaset";
+ iif->ctr.load_firmware = gigaset_load_firmware;
+ iif->ctr.reset_ctr = gigaset_reset_ctr;
+ iif->ctr.register_appl = gigaset_register_appl;
+ iif->ctr.release_appl = gigaset_release_appl;
+ iif->ctr.send_message = gigaset_send_message;
+ iif->ctr.procinfo = gigaset_procinfo;
+ iif->ctr.ctr_read_proc = gigaset_ctr_read_proc;
+ INIT_LIST_HEAD(&iif->appls);
+ skb_queue_head_init(&iif->sendqueue);
+ atomic_set(&iif->sendqlen, 0);
+
+ /* register controller with CAPI */
+ rc = attach_capi_ctr(&iif->ctr);
+ if (rc) {
+ pr_err("attach_capi_ctr failed (%d)\n", rc);
+ unregister_capi_driver(&capi_driver_gigaset);
+ kfree(iif);
+ return 0;
+ }
+
+ cs->iif = iif;
+ cs->hw_hdr_len = CAPI_DATA_B3_REQ_LEN;
+ return 1;
+}
+
+/**
+ * gigaset_isdn_unregister() - unregister from LL
+ * @cs: device descriptor structure.
+ *
+ * Called by main module to unregister the device from the LL.
+ */
+void gigaset_isdn_unregister(struct cardstate *cs)
+{
+ struct gigaset_capi_ctr *iif = cs->iif;
+
+ detach_capi_ctr(&iif->ctr);
+ kfree(iif);
+ cs->iif = NULL;
+ unregister_capi_driver(&capi_driver_gigaset);
+}
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index 33dcd8d72b7..82ed1cd14ff 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -108,7 +108,7 @@ int gigaset_enterconfigmode(struct cardstate *cs)
{
int i, r;
- cs->control_state = TIOCM_RTS; //FIXME
+ cs->control_state = TIOCM_RTS;
r = setflags(cs, TIOCM_DTR, 200);
if (r < 0)
@@ -132,10 +132,10 @@ int gigaset_enterconfigmode(struct cardstate *cs)
error:
dev_err(cs->dev, "error %d on setuartbits\n", -r);
- cs->control_state = TIOCM_RTS|TIOCM_DTR; // FIXME is this a good value?
+ cs->control_state = TIOCM_RTS|TIOCM_DTR;
cs->ops->set_modem_ctrl(cs, 0, TIOCM_RTS|TIOCM_DTR);
- return -1; //r
+ return -1;
}
static int test_timeout(struct at_state_t *at_state)
@@ -150,10 +150,9 @@ static int test_timeout(struct at_state_t *at_state)
}
if (!gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
- at_state->timer_index, NULL)) {
- //FIXME what should we do?
- }
-
+ at_state->timer_index, NULL))
+ dev_err(at_state->cs->dev, "%s: out of memory\n",
+ __func__);
return 1;
}
@@ -207,6 +206,32 @@ int gigaset_get_channel(struct bc_state *bcs)
return 1;
}
+struct bc_state *gigaset_get_free_channel(struct cardstate *cs)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (!try_module_get(cs->driver->owner)) {
+ gig_dbg(DEBUG_ANY,
+ "could not get module for allocating channel");
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return NULL;
+ }
+ for (i = 0; i < cs->channels; ++i)
+ if (!cs->bcs[i].use_count) {
+ ++cs->bcs[i].use_count;
+ cs->bcs[i].busy = 1;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gig_dbg(DEBUG_ANY, "allocated channel %d", i);
+ return cs->bcs + i;
+ }
+ module_put(cs->driver->owner);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gig_dbg(DEBUG_ANY, "no free channel");
+ return NULL;
+}
+
void gigaset_free_channel(struct bc_state *bcs)
{
unsigned long flags;
@@ -367,16 +392,15 @@ static void gigaset_freebcs(struct bc_state *bcs)
int i;
gig_dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel);
- if (!bcs->cs->ops->freebcshw(bcs)) {
+ if (!bcs->cs->ops->freebcshw(bcs))
gig_dbg(DEBUG_INIT, "failed");
- }
gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel);
clear_at_state(&bcs->at_state);
gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel);
+ dev_kfree_skb(bcs->skb);
+ bcs->skb = NULL;
- if (bcs->skb)
- dev_kfree_skb(bcs->skb);
for (i = 0; i < AT_NUM; ++i) {
kfree(bcs->commands[i]);
bcs->commands[i] = NULL;
@@ -463,6 +487,12 @@ void gigaset_freecs(struct cardstate *cs)
switch (cs->cs_init) {
default:
+ /* clear B channel structures */
+ for (i = 0; i < cs->channels; ++i) {
+ gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
+ gigaset_freebcs(cs->bcs + i);
+ }
+
/* clear device sysfs */
gigaset_free_dev_sysfs(cs);
@@ -471,28 +501,20 @@ void gigaset_freecs(struct cardstate *cs)
gig_dbg(DEBUG_INIT, "clearing hw");
cs->ops->freecshw(cs);
- //FIXME cmdbuf
-
/* fall through */
case 2: /* error in initcshw */
/* Deregister from LL */
make_invalid(cs, VALID_ID);
- gig_dbg(DEBUG_INIT, "clearing iif");
- gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
+ gigaset_isdn_unregister(cs);
/* fall through */
- case 1: /* error when regestering to LL */
+ case 1: /* error when registering to LL */
gig_dbg(DEBUG_INIT, "clearing at_state");
clear_at_state(&cs->at_state);
dealloc_at_states(cs);
/* fall through */
- case 0: /* error in one call to initbcs */
- for (i = 0; i < cs->channels; ++i) {
- gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
- gigaset_freebcs(cs->bcs + i);
- }
-
+ case 0: /* error in basic setup */
clear_events(cs);
gig_dbg(DEBUG_INIT, "freeing inbuf");
kfree(cs->inbuf);
@@ -534,16 +556,13 @@ void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
}
-static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs,
- struct cardstate *cs, int inputstate)
+static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct cardstate *cs)
/* inbuf->read must be allocated before! */
{
inbuf->head = 0;
inbuf->tail = 0;
inbuf->cs = cs;
- inbuf->bcs = bcs; /*base driver: NULL*/
- inbuf->rcvbuf = NULL;
- inbuf->inputstate = inputstate;
+ inbuf->inputstate = INS_command;
}
/**
@@ -599,7 +618,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
{
int i;
- bcs->tx_skb = NULL; //FIXME -> hw part
+ bcs->tx_skb = NULL;
skb_queue_head_init(&bcs->squeue);
@@ -618,13 +637,13 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
bcs->fcs = PPP_INITFCS;
bcs->inputstate = 0;
if (cs->ignoreframes) {
- bcs->inputstate |= INS_skip_frame;
bcs->skb = NULL;
- } else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
- skb_reserve(bcs->skb, HW_HDR_LEN);
- else {
- pr_err("out of memory\n");
- bcs->inputstate |= INS_skip_frame;
+ } else {
+ bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
+ if (bcs->skb != NULL)
+ skb_reserve(bcs->skb, cs->hw_hdr_len);
+ else
+ pr_err("out of memory\n");
}
bcs->channel = channel;
@@ -645,8 +664,8 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
gig_dbg(DEBUG_INIT, " failed");
gig_dbg(DEBUG_INIT, " freeing bcs[%d]->skb", channel);
- if (bcs->skb)
- dev_kfree_skb(bcs->skb);
+ dev_kfree_skb(bcs->skb);
+ bcs->skb = NULL;
return NULL;
}
@@ -673,12 +692,13 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
int onechannel, int ignoreframes,
int cidmode, const char *modulename)
{
- struct cardstate *cs = NULL;
+ struct cardstate *cs;
unsigned long flags;
int i;
gig_dbg(DEBUG_INIT, "allocating cs");
- if (!(cs = alloc_cs(drv))) {
+ cs = alloc_cs(drv);
+ if (!cs) {
pr_err("maximum number of devices exceeded\n");
return NULL;
}
@@ -707,7 +727,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
cs->ev_tail = 0;
cs->ev_head = 0;
- tasklet_init(&cs->event_tasklet, &gigaset_handle_event,
+ tasklet_init(&cs->event_tasklet, gigaset_handle_event,
(unsigned long) cs);
cs->commands_pending = 0;
cs->cur_at_seq = 0;
@@ -726,14 +746,6 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
cs->mode = M_UNKNOWN;
cs->mstate = MS_UNINITIALIZED;
- for (i = 0; i < channels; ++i) {
- gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i);
- if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
- pr_err("could not allocate channel %d data\n", i);
- goto error;
- }
- }
-
++cs->cs_init;
gig_dbg(DEBUG_INIT, "setting up at_state");
@@ -743,10 +755,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
cs->cbytes = 0;
gig_dbg(DEBUG_INIT, "setting up inbuf");
- if (onechannel) { //FIXME distinction necessary?
- gigaset_inbuf_init(cs->inbuf, cs->bcs, cs, INS_command);
- } else
- gigaset_inbuf_init(cs->inbuf, NULL, cs, INS_command);
+ gigaset_inbuf_init(cs->inbuf, cs);
cs->connected = 0;
cs->isdn_up = 0;
@@ -758,7 +767,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
cs->cmdbytes = 0;
gig_dbg(DEBUG_INIT, "setting up iif");
- if (!gigaset_register_to_LL(cs, modulename)) {
+ if (!gigaset_isdn_register(cs, modulename)) {
pr_err("error registering ISDN device\n");
goto error;
}
@@ -777,6 +786,15 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
/* set up device sysfs */
gigaset_init_dev_sysfs(cs);
+ /* set up channel data structures */
+ for (i = 0; i < channels; ++i) {
+ gig_dbg(DEBUG_INIT, "setting up bcs[%d]", i);
+ if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
+ pr_err("could not allocate channel %d data\n", i);
+ goto error;
+ }
+ }
+
spin_lock_irqsave(&cs->lock, flags);
cs->running = 1;
spin_unlock_irqrestore(&cs->lock, flags);
@@ -824,9 +842,10 @@ void gigaset_bcs_reinit(struct bc_state *bcs)
bcs->chstate = 0;
bcs->ignore = cs->ignoreframes;
- if (bcs->ignore)
- bcs->inputstate |= INS_skip_frame;
-
+ if (bcs->ignore) {
+ dev_kfree_skb(bcs->skb);
+ bcs->skb = NULL;
+ }
cs->ops->reinitbcshw(bcs);
}
@@ -847,8 +866,6 @@ static void cleanup_cs(struct cardstate *cs)
free_strings(&cs->at_state);
gigaset_at_init(&cs->at_state, NULL, cs, 0);
- kfree(cs->inbuf->rcvbuf);
- cs->inbuf->rcvbuf = NULL;
cs->inbuf->inputstate = INS_command;
cs->inbuf->head = 0;
cs->inbuf->tail = 0;
@@ -911,15 +928,13 @@ int gigaset_start(struct cardstate *cs)
cs->ops->baud_rate(cs, B115200);
cs->ops->set_line_ctrl(cs, CS8);
cs->control_state = TIOCM_DTR|TIOCM_RTS;
- } else {
- //FIXME use some saved values?
}
cs->waiting = 1;
if (!gigaset_add_event(cs, &cs->at_state, EV_START, NULL, 0, NULL)) {
cs->waiting = 0;
- //FIXME what should we do?
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
goto error;
}
@@ -959,7 +974,7 @@ int gigaset_shutdown(struct cardstate *cs)
cs->waiting = 1;
if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) {
- //FIXME what should we do?
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
goto exit;
}
@@ -990,7 +1005,7 @@ void gigaset_stop(struct cardstate *cs)
cs->waiting = 1;
if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL)) {
- //FIXME what should we do?
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
goto exit;
}
diff --git a/drivers/isdn/gigaset/dummyll.c b/drivers/isdn/gigaset/dummyll.c
new file mode 100644
index 00000000000..5b27c996af6
--- /dev/null
+++ b/drivers/isdn/gigaset/dummyll.c
@@ -0,0 +1,68 @@
+/*
+ * Dummy LL interface for the Gigaset driver
+ *
+ * Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ * 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 "gigaset.h"
+
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
+int gigaset_isdn_icall(struct at_state_t *at_state)
+{
+ return ICALL_IGNORE;
+}
+
+void gigaset_isdn_connD(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+}
+
+void gigaset_isdn_start(struct cardstate *cs)
+{
+}
+
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+}
+
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid)
+{
+ pr_info("no ISDN subsystem interface\n");
+ return 1;
+}
+
+void gigaset_isdn_unregister(struct cardstate *cs)
+{
+}
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index cc768caa38f..ddeb0456d20 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -40,8 +40,8 @@
/* Possible ASCII responses */
#define RSP_OK 0
-//#define RSP_BUSY 1
-//#define RSP_CONNECT 2
+#define RSP_BUSY 1
+#define RSP_CONNECT 2
#define RSP_ZGCI 3
#define RSP_RING 4
#define RSP_ZAOC 5
@@ -68,7 +68,6 @@
#define RSP_ZHLC (RSP_STR + STR_ZHLC)
#define RSP_ERROR -1 /* ERROR */
#define RSP_WRONG_CID -2 /* unknown cid in cmd */
-//#define RSP_EMPTY -3
#define RSP_UNKNOWN -4 /* unknown response */
#define RSP_FAIL -5 /* internal error */
#define RSP_INVAL -6 /* invalid response */
@@ -76,9 +75,9 @@
#define RSP_NONE -19
#define RSP_STRING -20
#define RSP_NULL -21
-//#define RSP_RETRYFAIL -22
-//#define RSP_RETRY -23
-//#define RSP_SKIP -24
+#define RSP_RETRYFAIL -22
+#define RSP_RETRY -23
+#define RSP_SKIP -24
#define RSP_INIT -27
#define RSP_ANY -26
#define RSP_LAST -28
@@ -127,7 +126,6 @@
#define ACT_NOTIFY_BC_UP 39
#define ACT_DIAL 40
#define ACT_ACCEPT 41
-#define ACT_PROTO_L2 42
#define ACT_HUP 43
#define ACT_IF_LOCK 44
#define ACT_START 45
@@ -159,229 +157,229 @@
#define SEQ_UMMODE 11
-// 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid), 400: hup, 500: reset, 600: dial, 700: ring
+/* 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid),
+ * 400: hup, 500: reset, 600: dial, 700: ring */
struct reply_t gigaset_tab_nocid[] =
{
- /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
-
- /* initialize device, set cid mode if possible */
- //{RSP_INIT, -1, -1,100, 900, 0, {ACT_TEST}},
- //{RSP_ERROR, 900,900, -1, 0, 0, {ACT_FAILINIT}},
- //{RSP_OK, 900,900, -1, 100, INIT_TIMEOUT,
- // {ACT_TIMEOUT}},
-
- {RSP_INIT, -1, -1,SEQ_INIT, 100, INIT_TIMEOUT,
- {ACT_TIMEOUT}}, /* wait until device is ready */
-
- {EV_TIMEOUT, 100,100, -1, 101, 3, {0}, "Z\r"}, /* device in transparent mode? try to initialize it. */
- {RSP_OK, 101,103, -1, 120, 5, {ACT_GETSTRING}, "+GMR\r"}, /* get version */
-
- {EV_TIMEOUT, 101,101, -1, 102, 5, {0}, "Z\r"}, /* timeout => try once again. */
- {RSP_ERROR, 101,101, -1, 102, 5, {0}, "Z\r"}, /* error => try once again. */
-
- {EV_TIMEOUT, 102,102, -1, 108, 5, {ACT_SETDLE1}, "^SDLE=0\r"}, /* timeout => try again in DLE mode. */
- {RSP_OK, 108,108, -1, 104,-1},
- {RSP_ZDLE, 104,104, 0, 103, 5, {0}, "Z\r"},
- {EV_TIMEOUT, 104,104, -1, 0, 0, {ACT_FAILINIT}},
- {RSP_ERROR, 108,108, -1, 0, 0, {ACT_FAILINIT}},
-
- {EV_TIMEOUT, 108,108, -1, 105, 2, {ACT_SETDLE0,
- ACT_HUPMODEM,
- ACT_TIMEOUT}}, /* still timeout => connection in unimodem mode? */
- {EV_TIMEOUT, 105,105, -1, 103, 5, {0}, "Z\r"},
-
- {RSP_ERROR, 102,102, -1, 107, 5, {0}, "^GETPRE\r"}, /* ERROR on ATZ => maybe in config mode? */
- {RSP_OK, 107,107, -1, 0, 0, {ACT_CONFIGMODE}},
- {RSP_ERROR, 107,107, -1, 0, 0, {ACT_FAILINIT}},
- {EV_TIMEOUT, 107,107, -1, 0, 0, {ACT_FAILINIT}},
-
- {RSP_ERROR, 103,103, -1, 0, 0, {ACT_FAILINIT}},
- {EV_TIMEOUT, 103,103, -1, 0, 0, {ACT_FAILINIT}},
-
- {RSP_STRING, 120,120, -1, 121,-1, {ACT_SETVER}},
-
- {EV_TIMEOUT, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}},
- {RSP_ERROR, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}},
- {RSP_OK, 121,121, -1, 0, 0, {ACT_GOTVER, ACT_INIT}},
-
- /* leave dle mode */
- {RSP_INIT, 0, 0,SEQ_DLE0, 201, 5, {0}, "^SDLE=0\r"},
- {RSP_OK, 201,201, -1, 202,-1},
- {RSP_ZDLE, 202,202, 0, 0, 0, {ACT_DLE0}},
- {RSP_NODEV, 200,249, -1, 0, 0, {ACT_FAKEDLE0}},
- {RSP_ERROR, 200,249, -1, 0, 0, {ACT_FAILDLE0}},
- {EV_TIMEOUT, 200,249, -1, 0, 0, {ACT_FAILDLE0}},
-
- /* enter dle mode */
- {RSP_INIT, 0, 0,SEQ_DLE1, 251, 5, {0}, "^SDLE=1\r"},
- {RSP_OK, 251,251, -1, 252,-1},
- {RSP_ZDLE, 252,252, 1, 0, 0, {ACT_DLE1}},
- {RSP_ERROR, 250,299, -1, 0, 0, {ACT_FAILDLE1}},
- {EV_TIMEOUT, 250,299, -1, 0, 0, {ACT_FAILDLE1}},
-
- /* incoming call */
- {RSP_RING, -1, -1, -1, -1,-1, {ACT_RING}},
-
- /* get cid */
- //{RSP_INIT, 0, 0,300, 901, 0, {ACT_TEST}},
- //{RSP_ERROR, 901,901, -1, 0, 0, {ACT_FAILCID}},
- //{RSP_OK, 901,901, -1, 301, 5, {0}, "^SGCI?\r"},
-
- {RSP_INIT, 0, 0,SEQ_CID, 301, 5, {0}, "^SGCI?\r"},
- {RSP_OK, 301,301, -1, 302,-1},
- {RSP_ZGCI, 302,302, -1, 0, 0, {ACT_CID}},
- {RSP_ERROR, 301,349, -1, 0, 0, {ACT_FAILCID}},
- {EV_TIMEOUT, 301,349, -1, 0, 0, {ACT_FAILCID}},
-
- /* enter cid mode */
- {RSP_INIT, 0, 0,SEQ_CIDMODE, 150, 5, {0}, "^SGCI=1\r"},
- {RSP_OK, 150,150, -1, 0, 0, {ACT_CMODESET}},
- {RSP_ERROR, 150,150, -1, 0, 0, {ACT_FAILCMODE}},
- {EV_TIMEOUT, 150,150, -1, 0, 0, {ACT_FAILCMODE}},
-
- /* leave cid mode */
- //{RSP_INIT, 0, 0,SEQ_UMMODE, 160, 5, {0}, "^SGCI=0\r"},
- {RSP_INIT, 0, 0,SEQ_UMMODE, 160, 5, {0}, "Z\r"},
- {RSP_OK, 160,160, -1, 0, 0, {ACT_UMODESET}},
- {RSP_ERROR, 160,160, -1, 0, 0, {ACT_FAILUMODE}},
- {EV_TIMEOUT, 160,160, -1, 0, 0, {ACT_FAILUMODE}},
-
- /* abort getting cid */
- {RSP_INIT, 0, 0,SEQ_NOCID, 0, 0, {ACT_ABORTCID}},
-
- /* reset */
- {RSP_INIT, 0, 0,SEQ_SHUTDOWN, 504, 5, {0}, "Z\r"},
- {RSP_OK, 504,504, -1, 0, 0, {ACT_SDOWN}},
- {RSP_ERROR, 501,599, -1, 0, 0, {ACT_FAILSDOWN}},
- {EV_TIMEOUT, 501,599, -1, 0, 0, {ACT_FAILSDOWN}},
- {RSP_NODEV, 501,599, -1, 0, 0, {ACT_FAKESDOWN}},
-
- {EV_PROC_CIDMODE,-1, -1, -1, -1,-1, {ACT_PROC_CIDMODE}}, //FIXME
- {EV_IF_LOCK, -1, -1, -1, -1,-1, {ACT_IF_LOCK}}, //FIXME
- {EV_IF_VER, -1, -1, -1, -1,-1, {ACT_IF_VER}}, //FIXME
- {EV_START, -1, -1, -1, -1,-1, {ACT_START}}, //FIXME
- {EV_STOP, -1, -1, -1, -1,-1, {ACT_STOP}}, //FIXME
- {EV_SHUTDOWN, -1, -1, -1, -1,-1, {ACT_SHUTDOWN}}, //FIXME
-
- /* misc. */
- {RSP_ERROR, -1, -1, -1, -1, -1, {ACT_ERROR} },
- {RSP_EMPTY, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZCFGT, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZCFG, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZLOG, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZMWI, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZABINFO, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZSMLSTCHG,-1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
-
- {RSP_ZCAU, -1, -1, -1, -1,-1, {ACT_ZCAU}},
- {RSP_NONE, -1, -1, -1, -1,-1, {ACT_DEBUG}},
- {RSP_ANY, -1, -1, -1, -1,-1, {ACT_WARN}},
- {RSP_LAST}
+/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
+ * action, command */
+
+/* initialize device, set cid mode if possible */
+{RSP_INIT, -1, -1, SEQ_INIT, 100, 1, {ACT_TIMEOUT} },
+
+{EV_TIMEOUT, 100, 100, -1, 101, 3, {0}, "Z\r"},
+{RSP_OK, 101, 103, -1, 120, 5, {ACT_GETSTRING},
+ "+GMR\r"},
+
+{EV_TIMEOUT, 101, 101, -1, 102, 5, {0}, "Z\r"},
+{RSP_ERROR, 101, 101, -1, 102, 5, {0}, "Z\r"},
+
+{EV_TIMEOUT, 102, 102, -1, 108, 5, {ACT_SETDLE1},
+ "^SDLE=0\r"},
+{RSP_OK, 108, 108, -1, 104, -1},
+{RSP_ZDLE, 104, 104, 0, 103, 5, {0}, "Z\r"},
+{EV_TIMEOUT, 104, 104, -1, 0, 0, {ACT_FAILINIT} },
+{RSP_ERROR, 108, 108, -1, 0, 0, {ACT_FAILINIT} },
+
+{EV_TIMEOUT, 108, 108, -1, 105, 2, {ACT_SETDLE0,
+ ACT_HUPMODEM,
+ ACT_TIMEOUT} },
+{EV_TIMEOUT, 105, 105, -1, 103, 5, {0}, "Z\r"},
+
+{RSP_ERROR, 102, 102, -1, 107, 5, {0}, "^GETPRE\r"},
+{RSP_OK, 107, 107, -1, 0, 0, {ACT_CONFIGMODE} },
+{RSP_ERROR, 107, 107, -1, 0, 0, {ACT_FAILINIT} },
+{EV_TIMEOUT, 107, 107, -1, 0, 0, {ACT_FAILINIT} },
+
+{RSP_ERROR, 103, 103, -1, 0, 0, {ACT_FAILINIT} },
+{EV_TIMEOUT, 103, 103, -1, 0, 0, {ACT_FAILINIT} },
+
+{RSP_STRING, 120, 120, -1, 121, -1, {ACT_SETVER} },
+
+{EV_TIMEOUT, 120, 121, -1, 0, 0, {ACT_FAILVER,
+ ACT_INIT} },
+{RSP_ERROR, 120, 121, -1, 0, 0, {ACT_FAILVER,
+ ACT_INIT} },
+{RSP_OK, 121, 121, -1, 0, 0, {ACT_GOTVER,
+ ACT_INIT} },
+
+/* leave dle mode */
+{RSP_INIT, 0, 0, SEQ_DLE0, 201, 5, {0}, "^SDLE=0\r"},
+{RSP_OK, 201, 201, -1, 202, -1},
+{RSP_ZDLE, 202, 202, 0, 0, 0, {ACT_DLE0} },
+{RSP_NODEV, 200, 249, -1, 0, 0, {ACT_FAKEDLE0} },
+{RSP_ERROR, 200, 249, -1, 0, 0, {ACT_FAILDLE0} },
+{EV_TIMEOUT, 200, 249, -1, 0, 0, {ACT_FAILDLE0} },
+
+/* enter dle mode */
+{RSP_INIT, 0, 0, SEQ_DLE1, 251, 5, {0}, "^SDLE=1\r"},
+{RSP_OK, 251, 251, -1, 252, -1},
+{RSP_ZDLE, 252, 252, 1, 0, 0, {ACT_DLE1} },
+{RSP_ERROR, 250, 299, -1, 0, 0, {ACT_FAILDLE1} },
+{EV_TIMEOUT, 250, 299, -1, 0, 0, {ACT_FAILDLE1} },
+
+/* incoming call */
+{RSP_RING, -1, -1, -1, -1, -1, {ACT_RING} },
+
+/* get cid */
+{RSP_INIT, 0, 0, SEQ_CID, 301, 5, {0}, "^SGCI?\r"},
+{RSP_OK, 301, 301, -1, 302, -1},
+{RSP_ZGCI, 302, 302, -1, 0, 0, {ACT_CID} },
+{RSP_ERROR, 301, 349, -1, 0, 0, {ACT_FAILCID} },
+{EV_TIMEOUT, 301, 349, -1, 0, 0, {ACT_FAILCID} },
+
+/* enter cid mode */
+{RSP_INIT, 0, 0, SEQ_CIDMODE, 150, 5, {0}, "^SGCI=1\r"},
+{RSP_OK, 150, 150, -1, 0, 0, {ACT_CMODESET} },
+{RSP_ERROR, 150, 150, -1, 0, 0, {ACT_FAILCMODE} },
+{EV_TIMEOUT, 150, 150, -1, 0, 0, {ACT_FAILCMODE} },
+
+/* leave cid mode */
+{RSP_INIT, 0, 0, SEQ_UMMODE, 160, 5, {0}, "Z\r"},
+{RSP_OK, 160, 160, -1, 0, 0, {ACT_UMODESET} },
+{RSP_ERROR, 160, 160, -1, 0, 0, {ACT_FAILUMODE} },
+{EV_TIMEOUT, 160, 160, -1, 0, 0, {ACT_FAILUMODE} },
+
+/* abort getting cid */
+{RSP_INIT, 0, 0, SEQ_NOCID, 0, 0, {ACT_ABORTCID} },
+
+/* reset */
+{RSP_INIT, 0, 0, SEQ_SHUTDOWN, 504, 5, {0}, "Z\r"},
+{RSP_OK, 504, 504, -1, 0, 0, {ACT_SDOWN} },
+{RSP_ERROR, 501, 599, -1, 0, 0, {ACT_FAILSDOWN} },
+{EV_TIMEOUT, 501, 599, -1, 0, 0, {ACT_FAILSDOWN} },
+{RSP_NODEV, 501, 599, -1, 0, 0, {ACT_FAKESDOWN} },
+
+{EV_PROC_CIDMODE, -1, -1, -1, -1, -1, {ACT_PROC_CIDMODE} },
+{EV_IF_LOCK, -1, -1, -1, -1, -1, {ACT_IF_LOCK} },
+{EV_IF_VER, -1, -1, -1, -1, -1, {ACT_IF_VER} },
+{EV_START, -1, -1, -1, -1, -1, {ACT_START} },
+{EV_STOP, -1, -1, -1, -1, -1, {ACT_STOP} },
+{EV_SHUTDOWN, -1, -1, -1, -1, -1, {ACT_SHUTDOWN} },
+
+/* misc. */
+{RSP_ERROR, -1, -1, -1, -1, -1, {ACT_ERROR} },
+{RSP_ZCFGT, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZCFG, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZLOG, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZMWI, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZABINFO, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZSMLSTCHG, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+
+{RSP_ZCAU, -1, -1, -1, -1, -1, {ACT_ZCAU} },
+{RSP_NONE, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ANY, -1, -1, -1, -1, -1, {ACT_WARN} },
+{RSP_LAST}
};
-// 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring, 400: hup, 750: accepted icall
+/* 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring,
+ * 400: hup, 750: accepted icall */
struct reply_t gigaset_tab_cid[] =
{
- /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
-
- /* dial */
- {EV_DIAL, -1, -1, -1, -1,-1, {ACT_DIAL}}, //FIXME
- {RSP_INIT, 0, 0,SEQ_DIAL, 601, 5, {ACT_CMD+AT_BC}},
- {RSP_OK, 601,601, -1, 602, 5, {ACT_CMD+AT_HLC}},
- {RSP_NULL, 602,602, -1, 603, 5, {ACT_CMD+AT_PROTO}},
- {RSP_OK, 602,602, -1, 603, 5, {ACT_CMD+AT_PROTO}},
- {RSP_OK, 603,603, -1, 604, 5, {ACT_CMD+AT_TYPE}},
- {RSP_OK, 604,604, -1, 605, 5, {ACT_CMD+AT_MSN}},
- {RSP_OK, 605,605, -1, 606, 5, {ACT_CMD+AT_ISO}},
- {RSP_NULL, 605,605, -1, 606, 5, {ACT_CMD+AT_ISO}},
- {RSP_OK, 606,606, -1, 607, 5, {0}, "+VLS=17\r"},
- {RSP_OK, 607,607, -1, 608,-1},
- {RSP_ZSAU, 608,608,ZSAU_PROCEEDING, 609, 5, {ACT_CMD+AT_DIAL}},
- {RSP_OK, 609,609, -1, 650, 0, {ACT_DIALING}},
-
- {RSP_ERROR, 601,609, -1, 0, 0, {ACT_ABORTDIAL}},
- {EV_TIMEOUT, 601,609, -1, 0, 0, {ACT_ABORTDIAL}},
-
- /* optional dialing responses */
- {EV_BC_OPEN, 650,650, -1, 651,-1},
- {RSP_ZVLS, 608,651, 17, -1,-1, {ACT_DEBUG}},
- {RSP_ZCTP, 609,651, -1, -1,-1, {ACT_DEBUG}},
- {RSP_ZCPN, 609,651, -1, -1,-1, {ACT_DEBUG}},
- {RSP_ZSAU, 650,651,ZSAU_CALL_DELIVERED, -1,-1, {ACT_DEBUG}},
-
- /* connect */
- {RSP_ZSAU, 650,650,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT}},
- {RSP_ZSAU, 651,651,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT,
- ACT_NOTIFY_BC_UP}},
- {RSP_ZSAU, 750,750,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT}},
- {RSP_ZSAU, 751,751,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT,
- ACT_NOTIFY_BC_UP}},
- {EV_BC_OPEN, 800,800, -1, 800,-1, {ACT_NOTIFY_BC_UP}},
-
- /* remote hangup */
- {RSP_ZSAU, 650,651,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT}},
- {RSP_ZSAU, 750,751,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP}},
- {RSP_ZSAU, 800,800,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP}},
-
- /* hangup */
- {EV_HUP, -1, -1, -1, -1,-1, {ACT_HUP}}, //FIXME
- {RSP_INIT, -1, -1,SEQ_HUP, 401, 5, {0}, "+VLS=0\r"}, /* hang up */ //-1,-1?
- {RSP_OK, 401,401, -1, 402, 5},
- {RSP_ZVLS, 402,402, 0, 403, 5},
- {RSP_ZSAU, 403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} },
- {RSP_ZSAU, 403, 403, ZSAU_NULL, 0, 0, {ACT_DISCONNECT} },
- {RSP_NODEV, 401, 403, -1, 0, 0, {ACT_FAKEHUP} },
- {RSP_ERROR, 401,401, -1, 0, 0, {ACT_ABORTHUP}},
- {EV_TIMEOUT, 401,403, -1, 0, 0, {ACT_ABORTHUP}},
-
- {EV_BC_CLOSED, 0, 0, -1, 0,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME new constate + timeout
-
- /* ring */
- {RSP_ZBC, 700,700, -1, -1,-1, {0}},
- {RSP_ZHLC, 700,700, -1, -1,-1, {0}},
- {RSP_NMBR, 700,700, -1, -1,-1, {0}},
- {RSP_ZCPN, 700,700, -1, -1,-1, {0}},
- {RSP_ZCTP, 700,700, -1, -1,-1, {0}},
- {EV_TIMEOUT, 700,700, -1, 720,720, {ACT_ICALL}},
- {EV_BC_CLOSED,720,720, -1, 0,-1, {ACT_NOTIFY_BC_DOWN}},
-
- /*accept icall*/
- {EV_ACCEPT, -1, -1, -1, -1,-1, {ACT_ACCEPT}}, //FIXME
- {RSP_INIT, 720,720,SEQ_ACCEPT, 721, 5, {ACT_CMD+AT_PROTO}},
- {RSP_OK, 721,721, -1, 722, 5, {ACT_CMD+AT_ISO}},
- {RSP_OK, 722,722, -1, 723, 5, {0}, "+VLS=17\r"}, /* set "Endgeraetemodus" */
- {RSP_OK, 723,723, -1, 724, 5, {0}},
- {RSP_ZVLS, 724,724, 17, 750,50, {ACT_ACCEPTED}},
- {RSP_ERROR, 721,729, -1, 0, 0, {ACT_ABORTACCEPT}},
- {EV_TIMEOUT, 721,729, -1, 0, 0, {ACT_ABORTACCEPT}},
- {RSP_ZSAU, 700,729,ZSAU_NULL, 0, 0, {ACT_ABORTACCEPT}},
- {RSP_ZSAU, 700,729,ZSAU_ACTIVE, 0, 0, {ACT_ABORTACCEPT}},
- {RSP_ZSAU, 700,729,ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT}},
-
- {EV_BC_OPEN, 750,750, -1, 751,-1},
- {EV_TIMEOUT, 750,751, -1, 0, 0, {ACT_CONNTIMEOUT}},
-
- /* B channel closed (general case) */
- {EV_BC_CLOSED, -1, -1, -1, -1,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME
-
- /* misc. */
- {EV_PROTO_L2, -1, -1, -1, -1,-1, {ACT_PROTO_L2}}, //FIXME
-
- {RSP_ZCON, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZCCR, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZAOC, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
- {RSP_ZCSTR, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
-
- {RSP_ZCAU, -1, -1, -1, -1,-1, {ACT_ZCAU}},
- {RSP_NONE, -1, -1, -1, -1,-1, {ACT_DEBUG}},
- {RSP_ANY, -1, -1, -1, -1,-1, {ACT_WARN}},
- {RSP_LAST}
+/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
+ * action, command */
+
+/* dial */
+{EV_DIAL, -1, -1, -1, -1, -1, {ACT_DIAL} },
+{RSP_INIT, 0, 0, SEQ_DIAL, 601, 5, {ACT_CMD+AT_BC} },
+{RSP_OK, 601, 601, -1, 602, 5, {ACT_CMD+AT_HLC} },
+{RSP_NULL, 602, 602, -1, 603, 5, {ACT_CMD+AT_PROTO} },
+{RSP_OK, 602, 602, -1, 603, 5, {ACT_CMD+AT_PROTO} },
+{RSP_OK, 603, 603, -1, 604, 5, {ACT_CMD+AT_TYPE} },
+{RSP_OK, 604, 604, -1, 605, 5, {ACT_CMD+AT_MSN} },
+{RSP_NULL, 605, 605, -1, 606, 5, {ACT_CMD+AT_CLIP} },
+{RSP_OK, 605, 605, -1, 606, 5, {ACT_CMD+AT_CLIP} },
+{RSP_NULL, 606, 606, -1, 607, 5, {ACT_CMD+AT_ISO} },
+{RSP_OK, 606, 606, -1, 607, 5, {ACT_CMD+AT_ISO} },
+{RSP_OK, 607, 607, -1, 608, 5, {0}, "+VLS=17\r"},
+{RSP_OK, 608, 608, -1, 609, -1},
+{RSP_ZSAU, 609, 609, ZSAU_PROCEEDING, 610, 5, {ACT_CMD+AT_DIAL} },
+{RSP_OK, 610, 610, -1, 650, 0, {ACT_DIALING} },
+
+{RSP_ERROR, 601, 610, -1, 0, 0, {ACT_ABORTDIAL} },
+{EV_TIMEOUT, 601, 610, -1, 0, 0, {ACT_ABORTDIAL} },
+
+/* optional dialing responses */
+{EV_BC_OPEN, 650, 650, -1, 651, -1},
+{RSP_ZVLS, 609, 651, 17, -1, -1, {ACT_DEBUG} },
+{RSP_ZCTP, 610, 651, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZCPN, 610, 651, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZSAU, 650, 651, ZSAU_CALL_DELIVERED, -1, -1, {ACT_DEBUG} },
+
+/* connect */
+{RSP_ZSAU, 650, 650, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT} },
+{RSP_ZSAU, 651, 651, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT,
+ ACT_NOTIFY_BC_UP} },
+{RSP_ZSAU, 750, 750, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT} },
+{RSP_ZSAU, 751, 751, ZSAU_ACTIVE, 800, -1, {ACT_CONNECT,
+ ACT_NOTIFY_BC_UP} },
+{EV_BC_OPEN, 800, 800, -1, 800, -1, {ACT_NOTIFY_BC_UP} },
+
+/* remote hangup */
+{RSP_ZSAU, 650, 651, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT} },
+{RSP_ZSAU, 750, 751, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },
+{RSP_ZSAU, 800, 800, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },
+
+/* hangup */
+{EV_HUP, -1, -1, -1, -1, -1, {ACT_HUP} },
+{RSP_INIT, -1, -1, SEQ_HUP, 401, 5, {0}, "+VLS=0\r"},
+{RSP_OK, 401, 401, -1, 402, 5},
+{RSP_ZVLS, 402, 402, 0, 403, 5},
+{RSP_ZSAU, 403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} },
+{RSP_ZSAU, 403, 403, ZSAU_NULL, 0, 0, {ACT_DISCONNECT} },
+{RSP_NODEV, 401, 403, -1, 0, 0, {ACT_FAKEHUP} },
+{RSP_ERROR, 401, 401, -1, 0, 0, {ACT_ABORTHUP} },
+{EV_TIMEOUT, 401, 403, -1, 0, 0, {ACT_ABORTHUP} },
+
+{EV_BC_CLOSED, 0, 0, -1, 0, -1, {ACT_NOTIFY_BC_DOWN} },
+
+/* ring */
+{RSP_ZBC, 700, 700, -1, -1, -1, {0} },
+{RSP_ZHLC, 700, 700, -1, -1, -1, {0} },
+{RSP_NMBR, 700, 700, -1, -1, -1, {0} },
+{RSP_ZCPN, 700, 700, -1, -1, -1, {0} },
+{RSP_ZCTP, 700, 700, -1, -1, -1, {0} },
+{EV_TIMEOUT, 700, 700, -1, 720, 720, {ACT_ICALL} },
+{EV_BC_CLOSED, 720, 720, -1, 0, -1, {ACT_NOTIFY_BC_DOWN} },
+
+/*accept icall*/
+{EV_ACCEPT, -1, -1, -1, -1, -1, {ACT_ACCEPT} },
+{RSP_INIT, 720, 720, SEQ_ACCEPT, 721, 5, {ACT_CMD+AT_PROTO} },
+{RSP_OK, 721, 721, -1, 722, 5, {ACT_CMD+AT_ISO} },
+{RSP_OK, 722, 722, -1, 723, 5, {0}, "+VLS=17\r"},
+{RSP_OK, 723, 723, -1, 724, 5, {0} },
+{RSP_ZVLS, 724, 724, 17, 750, 50, {ACT_ACCEPTED} },
+{RSP_ERROR, 721, 729, -1, 0, 0, {ACT_ABORTACCEPT} },
+{EV_TIMEOUT, 721, 729, -1, 0, 0, {ACT_ABORTACCEPT} },
+{RSP_ZSAU, 700, 729, ZSAU_NULL, 0, 0, {ACT_ABORTACCEPT} },
+{RSP_ZSAU, 700, 729, ZSAU_ACTIVE, 0, 0, {ACT_ABORTACCEPT} },
+{RSP_ZSAU, 700, 729, ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT} },
+
+{EV_BC_OPEN, 750, 750, -1, 751, -1},
+{EV_TIMEOUT, 750, 751, -1, 0, 0, {ACT_CONNTIMEOUT} },
+
+/* B channel closed (general case) */
+{EV_BC_CLOSED, -1, -1, -1, -1, -1, {ACT_NOTIFY_BC_DOWN} },
+
+/* misc. */
+{RSP_ZCON, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZCCR, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZAOC, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ZCSTR, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+
+{RSP_ZCAU, -1, -1, -1, -1, -1, {ACT_ZCAU} },
+{RSP_NONE, -1, -1, -1, -1, -1, {ACT_DEBUG} },
+{RSP_ANY, -1, -1, -1, -1, -1, {ACT_WARN} },
+{RSP_LAST}
};
-static const struct resp_type_t resp_type[] =
+static const struct resp_type_t {
+ unsigned char *response;
+ int resp_code;
+ int type;
+} resp_type[] =
{
- /*{"", RSP_EMPTY, RT_NOTHING},*/
{"OK", RSP_OK, RT_NOTHING},
{"ERROR", RSP_ERROR, RT_NOTHING},
{"ZSAU", RSP_ZSAU, RT_ZSAU},
@@ -405,7 +403,21 @@ static const struct resp_type_t resp_type[] =
{"ZLOG", RSP_ZLOG, RT_NOTHING},
{"ZABINFO", RSP_ZABINFO, RT_NOTHING},
{"ZSMLSTCHG", RSP_ZSMLSTCHG, RT_NOTHING},
- {NULL,0,0}
+ {NULL, 0, 0}
+};
+
+static const struct zsau_resp_t {
+ unsigned char *str;
+ int code;
+} zsau_resp[] =
+{
+ {"OUTGOING_CALL_PROCEEDING", ZSAU_OUTGOING_CALL_PROCEEDING},
+ {"CALL_DELIVERED", ZSAU_CALL_DELIVERED},
+ {"ACTIVE", ZSAU_ACTIVE},
+ {"DISCONNECT_IND", ZSAU_DISCONNECT_IND},
+ {"NULL", ZSAU_NULL},
+ {"DISCONNECT_REQ", ZSAU_DISCONNECT_REQ},
+ {NULL, ZSAU_UNKNOWN}
};
/*
@@ -470,7 +482,6 @@ static int cid_of_response(char *s)
if (cid < 1 || cid > 65535)
return -1; /* CID out of range */
return cid;
- //FIXME is ;<digit>+ at end of non-CID response really impossible?
}
/**
@@ -487,6 +498,7 @@ void gigaset_handle_modem_response(struct cardstate *cs)
int params;
int i, j;
const struct resp_type_t *rt;
+ const struct zsau_resp_t *zr;
int curarg;
unsigned long flags;
unsigned next, tail, head;
@@ -613,24 +625,14 @@ void gigaset_handle_modem_response(struct cardstate *cs)
event->parameter = ZSAU_NONE;
break;
}
- if (!strcmp(argv[curarg], "OUTGOING_CALL_PROCEEDING"))
- event->parameter = ZSAU_OUTGOING_CALL_PROCEEDING;
- else if (!strcmp(argv[curarg], "CALL_DELIVERED"))
- event->parameter = ZSAU_CALL_DELIVERED;
- else if (!strcmp(argv[curarg], "ACTIVE"))
- event->parameter = ZSAU_ACTIVE;
- else if (!strcmp(argv[curarg], "DISCONNECT_IND"))
- event->parameter = ZSAU_DISCONNECT_IND;
- else if (!strcmp(argv[curarg], "NULL"))
- event->parameter = ZSAU_NULL;
- else if (!strcmp(argv[curarg], "DISCONNECT_REQ"))
- event->parameter = ZSAU_DISCONNECT_REQ;
- else {
- event->parameter = ZSAU_UNKNOWN;
+ for (zr = zsau_resp; zr->str; ++zr)
+ if (!strcmp(argv[curarg], zr->str))
+ break;
+ event->parameter = zr->code;
+ if (!zr->str)
dev_warn(cs->dev,
"%s: unknown parameter %s after ZSAU\n",
__func__, argv[curarg]);
- }
++curarg;
break;
case RT_STRING:
@@ -714,7 +716,7 @@ static void disconnect(struct at_state_t **at_state_p)
/* notify LL */
if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
- gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+ gigaset_isdn_hupD(bcs);
}
} else {
/* no B channel assigned: just deallocate */
@@ -872,12 +874,12 @@ static void bchannel_down(struct bc_state *bcs)
{
if (bcs->chstate & CHS_B_UP) {
bcs->chstate &= ~CHS_B_UP;
- gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
+ gigaset_isdn_hupB(bcs);
}
if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
- gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+ gigaset_isdn_hupD(bcs);
}
gigaset_free_channel(bcs);
@@ -894,15 +896,17 @@ static void bchannel_up(struct bc_state *bcs)
}
bcs->chstate |= CHS_B_UP;
- gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
+ gigaset_isdn_connB(bcs);
}
-static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_index)
+static void start_dial(struct at_state_t *at_state, void *data,
+ unsigned seq_index)
{
struct bc_state *bcs = at_state->bcs;
struct cardstate *cs = at_state->cs;
- int retval;
+ char **commands = data;
unsigned long flags;
+ int i;
bcs->chstate |= CHS_NOTIFY_LL;
@@ -913,10 +917,10 @@ static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_ind
}
spin_unlock_irqrestore(&cs->lock, flags);
- retval = gigaset_isdn_setup_dial(at_state, data);
- if (retval != 0)
- goto error;
-
+ for (i = 0; i < AT_NUM; ++i) {
+ kfree(bcs->commands[i]);
+ bcs->commands[i] = commands[i];
+ }
at_state->pending_commands |= PC_CID;
gig_dbg(DEBUG_CMD, "Scheduling PC_CID");
@@ -924,6 +928,10 @@ static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_ind
return;
error:
+ for (i = 0; i < AT_NUM; ++i) {
+ kfree(commands[i]);
+ commands[i] = NULL;
+ }
at_state->pending_commands |= PC_NOCID;
gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID");
cs->commands_pending = 1;
@@ -933,20 +941,31 @@ error:
static void start_accept(struct at_state_t *at_state)
{
struct cardstate *cs = at_state->cs;
- int retval;
+ struct bc_state *bcs = at_state->bcs;
+ int i;
- retval = gigaset_isdn_setup_accept(at_state);
+ for (i = 0; i < AT_NUM; ++i) {
+ kfree(bcs->commands[i]);
+ bcs->commands[i] = NULL;
+ }
- if (retval == 0) {
- at_state->pending_commands |= PC_ACCEPT;
- gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT");
- cs->commands_pending = 1;
- } else {
+ bcs->commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
+ bcs->commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
+ if (!bcs->commands[AT_PROTO] || !bcs->commands[AT_ISO]) {
+ dev_err(at_state->cs->dev, "out of memory\n");
/* error reset */
at_state->pending_commands |= PC_HUP;
gig_dbg(DEBUG_CMD, "Scheduling PC_HUP");
cs->commands_pending = 1;
+ return;
}
+
+ snprintf(bcs->commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+ snprintf(bcs->commands[AT_ISO], 9, "^SISO=%u\r", bcs->channel + 1);
+
+ at_state->pending_commands |= PC_ACCEPT;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT");
+ cs->commands_pending = 1;
}
static void do_start(struct cardstate *cs)
@@ -957,9 +976,7 @@ static void do_start(struct cardstate *cs)
schedule_init(cs, MS_INIT);
cs->isdn_up = 1;
- gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
- // FIXME: not in locked mode
- // FIXME 2: only after init sequence
+ gigaset_isdn_start(cs);
cs->waiting = 0;
wake_up(&cs->waitqueue);
@@ -975,7 +992,7 @@ static void finish_shutdown(struct cardstate *cs)
/* Tell the LL that the device is not available .. */
if (cs->isdn_up) {
cs->isdn_up = 0;
- gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
+ gigaset_isdn_stop(cs);
}
/* The rest is done by cleanup_cs () in user mode. */
@@ -1113,7 +1130,6 @@ static int do_lock(struct cardstate *cs)
break;
case MS_LOCKED:
- //retval = -EACCES;
break;
default:
return -EBUSY;
@@ -1276,7 +1292,7 @@ static void do_action(int action, struct cardstate *cs,
break;
}
bcs->chstate |= CHS_D_UP;
- gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+ gigaset_isdn_connD(bcs);
cs->ops->init_bchannel(bcs);
break;
case ACT_DLE1:
@@ -1284,7 +1300,7 @@ static void do_action(int action, struct cardstate *cs,
bcs = cs->bcs + cs->curchannel;
bcs->chstate |= CHS_D_UP;
- gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+ gigaset_isdn_connD(bcs);
cs->ops->init_bchannel(bcs);
break;
case ACT_FAKEHUP:
@@ -1369,7 +1385,7 @@ static void do_action(int action, struct cardstate *cs,
cs->cur_at_seq = SEQ_NONE;
break;
- case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL processing */
+ case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL procssng */
disconnect(p_at_state);
break;
@@ -1443,17 +1459,6 @@ static void do_action(int action, struct cardstate *cs,
__func__, at_state->ConState);
cs->cur_at_seq = SEQ_NONE;
break;
-#ifdef CONFIG_GIGASET_DEBUG
- case ACT_TEST:
- {
- static int count = 3; //2; //1;
- *p_genresp = 1;
- *p_resp_code = count ? RSP_ERROR : RSP_OK;
- if (count > 0)
- --count;
- }
- break;
-#endif
case ACT_DEBUG:
gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d",
__func__, ev->type, at_state->ConState);
@@ -1474,11 +1479,6 @@ static void do_action(int action, struct cardstate *cs,
case ACT_ACCEPT:
start_accept(at_state);
break;
- case ACT_PROTO_L2:
- gig_dbg(DEBUG_CMD, "set protocol to %u",
- (unsigned) ev->parameter);
- at_state->bcs->proto2 = ev->parameter;
- break;
case ACT_HUP:
at_state->pending_commands |= PC_HUP;
cs->commands_pending = 1;
@@ -1493,7 +1493,7 @@ static void do_action(int action, struct cardstate *cs,
do_start(cs);
break;
- /* events from the interface */ // FIXME without ACT_xxxx?
+ /* events from the interface */
case ACT_IF_LOCK:
cs->cmd_result = ev->parameter ? do_lock(cs) : do_unlock(cs);
cs->waiting = 0;
@@ -1512,7 +1512,7 @@ static void do_action(int action, struct cardstate *cs,
wake_up(&cs->waitqueue);
break;
- /* events from the proc file system */ // FIXME without ACT_xxxx?
+ /* events from the proc file system */
case ACT_PROC_CIDMODE:
spin_lock_irqsave(&cs->lock, flags);
if (ev->parameter != cs->cidmode) {
@@ -1649,7 +1649,8 @@ static void process_event(struct cardstate *cs, struct event_t *ev)
for (curact = 0; curact < MAXACT; ++curact) {
/* The row tells us what we should do ..
*/
- do_action(rep->action[curact], cs, bcs, &at_state, &p_command, &genresp, &resp_code, ev);
+ do_action(rep->action[curact], cs, bcs, &at_state, &p_command,
+ &genresp, &resp_code, ev);
if (!at_state)
break; /* may be freed after disconnect */
}
@@ -1661,13 +1662,14 @@ static void process_event(struct cardstate *cs, struct event_t *ev)
if (genresp) {
spin_lock_irqsave(&cs->lock, flags);
- at_state->timer_expires = 0; //FIXME
- at_state->timer_active = 0; //FIXME
+ at_state->timer_expires = 0;
+ at_state->timer_active = 0;
spin_unlock_irqrestore(&cs->lock, flags);
- gigaset_add_event(cs, at_state, resp_code, NULL, 0, NULL);
+ gigaset_add_event(cs, at_state, resp_code,
+ NULL, 0, NULL);
} else {
/* Send command to modem if not NULL... */
- if (p_command/*rep->command*/) {
+ if (p_command) {
if (cs->connected)
send_command(cs, p_command,
sendcid, cs->dle,
@@ -1754,7 +1756,8 @@ static void process_command_flags(struct cardstate *cs)
}
}
- /* only switch back to unimodem mode, if no commands are pending and no channels are up */
+ /* only switch back to unimodem mode if no commands are pending and
+ * no channels are up */
spin_lock_irqsave(&cs->lock, flags);
if (cs->at_state.pending_commands == PC_UMMODE
&& !cs->cidmode
@@ -1813,9 +1816,8 @@ static void process_command_flags(struct cardstate *cs)
if (cs->at_state.pending_commands & PC_INIT) {
cs->at_state.pending_commands &= ~PC_INIT;
- cs->dle = 0; //FIXME
+ cs->dle = 0;
cs->inbuf->inputstate = INS_command;
- //FIXME reset card state (or -> LOCK0)?
schedule_sequence(cs, &cs->at_state, SEQ_INIT);
return;
}
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index a2f6125739e..e963a6c2e86 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -23,7 +23,6 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/spinlock.h>
-#include <linux/isdnif.h>
#include <linux/usb.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
@@ -35,12 +34,11 @@
#include <linux/list.h>
#include <asm/atomic.h>
-#define GIG_VERSION {0,5,0,0}
-#define GIG_COMPAT {0,4,0,0}
+#define GIG_VERSION {0, 5, 0, 0}
+#define GIG_COMPAT {0, 4, 0, 0}
#define MAX_REC_PARAMS 10 /* Max. number of params in response string */
#define MAX_RESP_SIZE 512 /* Max. size of a response string */
-#define HW_HDR_LEN 2 /* Header size used to store ack info */
#define MAX_EVENTS 64 /* size of event queue */
@@ -135,35 +133,32 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
#define IN_VENDOR_REQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
-/* int-in-events 3070 */
+/* interrupt pipe messages */
#define HD_B1_FLOW_CONTROL 0x80
#define HD_B2_FLOW_CONTROL 0x81
-#define HD_RECEIVEATDATA_ACK (0x35) // 3070
- // att: HD_RECEIVE>>AT<<DATA_ACK
-#define HD_READY_SEND_ATDATA (0x36) // 3070
-#define HD_OPEN_ATCHANNEL_ACK (0x37) // 3070
-#define HD_CLOSE_ATCHANNEL_ACK (0x38) // 3070
-#define HD_DEVICE_INIT_OK (0x11) // ISurf USB + 3070
-#define HD_OPEN_B1CHANNEL_ACK (0x51) // ISurf USB + 3070
-#define HD_OPEN_B2CHANNEL_ACK (0x52) // ISurf USB + 3070
-#define HD_CLOSE_B1CHANNEL_ACK (0x53) // ISurf USB + 3070
-#define HD_CLOSE_B2CHANNEL_ACK (0x54) // ISurf USB + 3070
-// Powermangment
-#define HD_SUSPEND_END (0x61) // ISurf USB
-// Configuration
-#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) // ISurf USB + 3070
-
-/* control requests 3070 */
-#define HD_OPEN_B1CHANNEL (0x23) // ISurf USB + 3070
-#define HD_CLOSE_B1CHANNEL (0x24) // ISurf USB + 3070
-#define HD_OPEN_B2CHANNEL (0x25) // ISurf USB + 3070
-#define HD_CLOSE_B2CHANNEL (0x26) // ISurf USB + 3070
-#define HD_RESET_INTERRUPT_PIPE (0x27) // ISurf USB + 3070
-#define HD_DEVICE_INIT_ACK (0x34) // ISurf USB + 3070
-#define HD_WRITE_ATMESSAGE (0x12) // 3070
-#define HD_READ_ATMESSAGE (0x13) // 3070
-#define HD_OPEN_ATCHANNEL (0x28) // 3070
-#define HD_CLOSE_ATCHANNEL (0x29) // 3070
+#define HD_RECEIVEATDATA_ACK (0x35) /* 3070 */
+#define HD_READY_SEND_ATDATA (0x36) /* 3070 */
+#define HD_OPEN_ATCHANNEL_ACK (0x37) /* 3070 */
+#define HD_CLOSE_ATCHANNEL_ACK (0x38) /* 3070 */
+#define HD_DEVICE_INIT_OK (0x11) /* ISurf USB + 3070 */
+#define HD_OPEN_B1CHANNEL_ACK (0x51) /* ISurf USB + 3070 */
+#define HD_OPEN_B2CHANNEL_ACK (0x52) /* ISurf USB + 3070 */
+#define HD_CLOSE_B1CHANNEL_ACK (0x53) /* ISurf USB + 3070 */
+#define HD_CLOSE_B2CHANNEL_ACK (0x54) /* ISurf USB + 3070 */
+#define HD_SUSPEND_END (0x61) /* ISurf USB */
+#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) /* ISurf USB + 3070 */
+
+/* control requests */
+#define HD_OPEN_B1CHANNEL (0x23) /* ISurf USB + 3070 */
+#define HD_CLOSE_B1CHANNEL (0x24) /* ISurf USB + 3070 */
+#define HD_OPEN_B2CHANNEL (0x25) /* ISurf USB + 3070 */
+#define HD_CLOSE_B2CHANNEL (0x26) /* ISurf USB + 3070 */
+#define HD_RESET_INTERRUPT_PIPE (0x27) /* ISurf USB + 3070 */
+#define HD_DEVICE_INIT_ACK (0x34) /* ISurf USB + 3070 */
+#define HD_WRITE_ATMESSAGE (0x12) /* 3070 */
+#define HD_READ_ATMESSAGE (0x13) /* 3070 */
+#define HD_OPEN_ATCHANNEL (0x28) /* 3070 */
+#define HD_CLOSE_ATCHANNEL (0x29) /* 3070 */
/* number of B channels supported by base driver */
#define BAS_CHANNELS 2
@@ -193,7 +188,9 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
#define AT_PROTO 4
#define AT_TYPE 5
#define AT_HLC 6
-#define AT_NUM 7
+#define AT_CLIP 7
+/* total number */
+#define AT_NUM 8
/* variables in struct at_state_t */
#define VAR_ZSAU 0
@@ -216,7 +213,6 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
#define EV_START -110
#define EV_STOP -111
#define EV_IF_LOCK -112
-#define EV_PROTO_L2 -113
#define EV_ACCEPT -114
#define EV_DIAL -115
#define EV_HUP -116
@@ -224,12 +220,11 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
#define EV_BC_CLOSED -118
/* input state */
-#define INS_command 0x0001
-#define INS_DLE_char 0x0002
+#define INS_command 0x0001 /* receiving messages (not payload data) */
+#define INS_DLE_char 0x0002 /* DLE flag received (in DLE mode) */
#define INS_byte_stuff 0x0004
#define INS_have_data 0x0008
-#define INS_skip_frame 0x0010
-#define INS_DLE_command 0x0020
+#define INS_DLE_command 0x0020 /* DLE message start (<DLE> X) received */
#define INS_flag_hunt 0x0040
/* channel state */
@@ -259,6 +254,11 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
#define SM_LOCKED 0
#define SM_ISDN 1 /* default */
+/* layer 2 protocols (AT^SBPR=...) */
+#define L2_BITSYNC 0
+#define L2_HDLC 1
+#define L2_VOICE 2
+
struct gigaset_ops;
struct gigaset_driver;
@@ -286,8 +286,6 @@ extern struct reply_t gigaset_tab_cid[];
extern struct reply_t gigaset_tab_nocid[];
struct inbuf_t {
- unsigned char *rcvbuf; /* usb-gigaset receive buffer */
- struct bc_state *bcs;
struct cardstate *cs;
int inputstate;
int head, tail;
@@ -359,12 +357,6 @@ struct at_state_t {
struct bc_state *bcs;
};
-struct resp_type_t {
- unsigned char *response;
- int resp_code; /* RSP_XXXX */
- int type; /* RT_XXXX */
-};
-
struct event_t {
int type;
void *ptr, *arg;
@@ -395,7 +387,7 @@ struct bc_state {
unsigned chstate; /* bitmap (CHS_*) */
int ignore;
- unsigned proto2; /* Layer 2 protocol (ISDN_PROTO_L2_*) */
+ unsigned proto2; /* layer 2 protocol (L2_*) */
char *commands[AT_NUM]; /* see AT_XXXX */
#ifdef CONFIG_GIGASET_DEBUG
@@ -410,6 +402,8 @@ struct bc_state {
struct usb_bc_state *usb; /* usb hardware driver (m105) */
struct bas_bc_state *bas; /* usb hardware driver (base) */
} hw;
+
+ void *ap; /* LL application structure */
};
struct cardstate {
@@ -456,12 +450,13 @@ struct cardstate {
unsigned running; /* !=0 if events are handled */
unsigned connected; /* !=0 if hardware is connected */
- unsigned isdn_up; /* !=0 after ISDN_STAT_RUN */
+ unsigned isdn_up; /* !=0 after gigaset_isdn_start() */
unsigned cidmode;
int myid; /* id for communication with LL */
- isdn_if iif;
+ void *iif; /* LL interface structure */
+ unsigned short hw_hdr_len; /* headroom needed in data skbs */
struct reply_t *tabnocid;
struct reply_t *tabcid;
@@ -476,8 +471,8 @@ struct cardstate {
struct timer_list timer;
int retry_count;
- int dle; /* !=0 if modem commands/responses are
- dle encoded */
+ int dle; /* !=0 if DLE mode is active
+ (ZDLE=1 received -- M10x only) */
int cur_at_seq; /* sequence of AT commands being
processed */
int curchannel; /* channel those commands are meant
@@ -616,7 +611,9 @@ struct gigaset_ops {
int (*baud_rate)(struct cardstate *cs, unsigned cflag);
int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag);
- /* Called from i4l.c to put an skb into the send-queue. */
+ /* Called from LL interface to put an skb into the send-queue.
+ * After sending is completed, gigaset_skb_sent() must be called
+ * with the skb's link layer header preserved. */
int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb);
/* Called from ev-layer.c to process a block of data
@@ -625,7 +622,8 @@ struct gigaset_ops {
};
-/* = Common structures and definitions ======================================= */
+/* = Common structures and definitions =======================================
+ */
/* Parser states for DLE-Event:
* <DLE-EVENT>: <DLE_FLAG> "X" <EVENT> <DLE_FLAG> "."
@@ -638,8 +636,7 @@ struct gigaset_ops {
* Functions implemented in asyncdata.c
*/
-/* Called from i4l.c to put an skb into the send-queue.
- * After sending gigaset_skb_sent() should be called. */
+/* Called from LL interface to put an skb into the send queue. */
int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb);
/* Called from ev-layer.c to process a block of data
@@ -650,8 +647,7 @@ void gigaset_m10x_input(struct inbuf_t *inbuf);
* Functions implemented in isocdata.c
*/
-/* Called from i4l.c to put an skb into the send-queue.
- * After sending gigaset_skb_sent() should be called. */
+/* Called from LL interface to put an skb into the send queue. */
int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb);
/* Called from ev-layer.c to process a block of data
@@ -674,36 +670,26 @@ void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle);
int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size);
/* ===========================================================================
- * Functions implemented in i4l.c/gigaset.h
+ * Functions implemented in LL interface
*/
-/* Called by gigaset_initcs() for setting up with the isdn4linux subsystem */
-int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid);
+/* Called from common.c for setting up/shutting down with the ISDN subsystem */
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid);
+void gigaset_isdn_unregister(struct cardstate *cs);
-/* Called from xxx-gigaset.c to indicate completion of sending an skb */
+/* Called from hardware module to indicate completion of an skb */
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb);
+void gigaset_isdn_rcv_err(struct bc_state *bcs);
/* Called from common.c/ev-layer.c to indicate events relevant to the LL */
+void gigaset_isdn_start(struct cardstate *cs);
+void gigaset_isdn_stop(struct cardstate *cs);
int gigaset_isdn_icall(struct at_state_t *at_state);
-int gigaset_isdn_setup_accept(struct at_state_t *at_state);
-int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data);
-
-void gigaset_i4l_cmd(struct cardstate *cs, int cmd);
-void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd);
-
-
-static inline void gigaset_isdn_rcv_err(struct bc_state *bcs)
-{
- isdn_ctrl response;
-
- /* error -> LL */
- gig_dbg(DEBUG_CMD, "sending L1ERR");
- response.driver = bcs->cs->myid;
- response.command = ISDN_STAT_L1ERR;
- response.arg = bcs->channel;
- response.parm.errcode = ISDN_STAT_L1ERR_RECV;
- bcs->cs->iif.statcallb(&response);
-}
+void gigaset_isdn_connD(struct bc_state *bcs);
+void gigaset_isdn_hupD(struct bc_state *bcs);
+void gigaset_isdn_connB(struct bc_state *bcs);
+void gigaset_isdn_hupB(struct bc_state *bcs);
/* ===========================================================================
* Functions implemented in ev-layer.c
@@ -732,6 +718,7 @@ void gigaset_bcs_reinit(struct bc_state *bcs);
void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
struct cardstate *cs, int cid);
int gigaset_get_channel(struct bc_state *bcs);
+struct bc_state *gigaset_get_free_channel(struct cardstate *cs);
void gigaset_free_channel(struct bc_state *bcs);
int gigaset_get_channels(struct cardstate *cs);
void gigaset_free_channels(struct cardstate *cs);
@@ -781,7 +768,7 @@ struct event_t *gigaset_add_event(struct cardstate *cs,
void *ptr, int parameter, void *arg);
/* Called on CONFIG1 command from frontend. */
-int gigaset_enterconfigmode(struct cardstate *cs); //0: success <0: errorcode
+int gigaset_enterconfigmode(struct cardstate *cs);
/* cs->lock must not be locked */
static inline void gigaset_schedule_event(struct cardstate *cs)
@@ -816,35 +803,6 @@ static inline void gigaset_bchannel_up(struct bc_state *bcs)
/* handling routines for sk_buff */
/* ============================= */
-/* pass received skb to LL
- * Warning: skb must not be accessed anymore!
- */
-static inline void gigaset_rcv_skb(struct sk_buff *skb,
- struct cardstate *cs,
- struct bc_state *bcs)
-{
- cs->iif.rcvcallb_skb(cs->myid, bcs->channel, skb);
- bcs->trans_down++;
-}
-
-/* handle reception of corrupted skb
- * Warning: skb must not be accessed anymore!
- */
-static inline void gigaset_rcv_error(struct sk_buff *procskb,
- struct cardstate *cs,
- struct bc_state *bcs)
-{
- if (procskb)
- dev_kfree_skb(procskb);
-
- if (bcs->ignore)
- --bcs->ignore;
- else {
- ++bcs->corrupted;
- gigaset_isdn_rcv_err(bcs);
- }
-}
-
/* append received bytes to inbuf */
int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
unsigned numbytes);
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index 654489d836c..c129ee47a8f 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -14,6 +14,9 @@
*/
#include "gigaset.h"
+#include <linux/isdnif.h>
+
+#define HW_HDR_LEN 2 /* Header size used to store ack info */
/* == Handling of I4L IO =====================================================*/
@@ -36,12 +39,12 @@
static int writebuf_from_LL(int driverID, int channel, int ack,
struct sk_buff *skb)
{
- struct cardstate *cs;
+ struct cardstate *cs = gigaset_get_cs_by_id(driverID);
struct bc_state *bcs;
+ unsigned char *ack_header;
unsigned len;
- unsigned skblen;
- if (!(cs = gigaset_get_cs_by_id(driverID))) {
+ if (!cs) {
pr_err("%s: invalid driver ID (%d)\n", __func__, driverID);
return -ENODEV;
}
@@ -75,11 +78,23 @@ static int writebuf_from_LL(int driverID, int channel, int ack,
return -EINVAL;
}
- skblen = ack ? len : 0;
- skb->head[0] = skblen & 0xff;
- skb->head[1] = skblen >> 8;
- gig_dbg(DEBUG_MCMD, "skb: len=%u, skblen=%u: %02x %02x",
- len, skblen, (unsigned) skb->head[0], (unsigned) skb->head[1]);
+ /* set up acknowledgement header */
+ if (skb_headroom(skb) < HW_HDR_LEN) {
+ /* should never happen */
+ dev_err(cs->dev, "%s: insufficient skb headroom\n", __func__);
+ return -ENOMEM;
+ }
+ skb_set_mac_header(skb, -HW_HDR_LEN);
+ skb->mac_len = HW_HDR_LEN;
+ ack_header = skb_mac_header(skb);
+ if (ack) {
+ ack_header[0] = len & 0xff;
+ ack_header[1] = len >> 8;
+ } else {
+ ack_header[0] = ack_header[1] = 0;
+ }
+ gig_dbg(DEBUG_MCMD, "skb: len=%u, ack=%d: %02x %02x",
+ len, ack, ack_header[0], ack_header[1]);
/* pass to device-specific module */
return cs->ops->send_skb(bcs, skb);
@@ -95,6 +110,8 @@ static int writebuf_from_LL(int driverID, int channel, int ack,
*/
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
{
+ isdn_if *iif = bcs->cs->iif;
+ unsigned char *ack_header = skb_mac_header(skb);
unsigned len;
isdn_ctrl response;
@@ -104,8 +121,7 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
dev_warn(bcs->cs->dev, "%s: skb->len==%d\n",
__func__, skb->len);
- len = (unsigned char) skb->head[0] |
- (unsigned) (unsigned char) skb->head[1] << 8;
+ len = ack_header[0] + ((unsigned) ack_header[1] << 8);
if (len) {
gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)",
bcs->cs->myid, bcs->channel, len);
@@ -114,71 +130,177 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
response.command = ISDN_STAT_BSENT;
response.arg = bcs->channel;
response.parm.length = len;
- bcs->cs->iif.statcallb(&response);
+ iif->statcallb(&response);
}
}
EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+/**
+ * gigaset_skb_rcvd() - pass received skb to LL
+ * @bcs: B channel descriptor structure.
+ * @skb: received data.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when user data has
+ * been successfully received, for passing to the LL.
+ * Warning: skb must not be accessed anymore!
+ */
+void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
+{
+ isdn_if *iif = bcs->cs->iif;
+
+ iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb);
+ bcs->trans_down++;
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
+
+/**
+ * gigaset_isdn_rcv_err() - signal receive error
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by hardware module {bas,ser,usb}_gigaset when a receive error
+ * has occurred, for signalling to the LL.
+ */
+void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+ isdn_if *iif = bcs->cs->iif;
+ isdn_ctrl response;
+
+ /* if currently ignoring packets, just count down */
+ if (bcs->ignore) {
+ bcs->ignore--;
+ return;
+ }
+
+ /* update statistics */
+ bcs->corrupted++;
+
+ /* error -> LL */
+ gig_dbg(DEBUG_CMD, "sending L1ERR");
+ response.driver = bcs->cs->myid;
+ response.command = ISDN_STAT_L1ERR;
+ response.arg = bcs->channel;
+ response.parm.errcode = ISDN_STAT_L1ERR_RECV;
+ iif->statcallb(&response);
+}
+EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
+
/* This function will be called by LL to send commands
* NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL,
* so don't put too much effort into it.
*/
static int command_from_LL(isdn_ctrl *cntrl)
{
- struct cardstate *cs = gigaset_get_cs_by_id(cntrl->driver);
+ struct cardstate *cs;
struct bc_state *bcs;
int retval = 0;
- struct setup_parm *sp;
+ char **commands;
+ int ch;
+ int i;
+ size_t l;
gigaset_debugdrivers();
- if (!cs) {
+ gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx",
+ cntrl->driver, cntrl->command, cntrl->arg);
+
+ cs = gigaset_get_cs_by_id(cntrl->driver);
+ if (cs == NULL) {
pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
return -ENODEV;
}
+ ch = cntrl->arg & 0xff;
switch (cntrl->command) {
case ISDN_CMD_IOCTL:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)",
- cntrl->driver, cntrl->arg);
-
dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
return -EINVAL;
case ISDN_CMD_DIAL:
gig_dbg(DEBUG_ANY,
- "ISDN_CMD_DIAL (driver: %d, ch: %ld, "
- "phone: %s, ownmsn: %s, si1: %d, si2: %d)",
- cntrl->driver, cntrl->arg,
+ "ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
cntrl->parm.setup.si1, cntrl->parm.setup.si2);
- if (cntrl->arg >= cs->channels) {
+ if (ch >= cs->channels) {
dev_err(cs->dev,
- "ISDN_CMD_DIAL: invalid channel (%d)\n",
- (int) cntrl->arg);
+ "ISDN_CMD_DIAL: invalid channel (%d)\n", ch);
return -EINVAL;
}
-
- bcs = cs->bcs + cntrl->arg;
-
+ bcs = cs->bcs + ch;
if (!gigaset_get_channel(bcs)) {
dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
return -EBUSY;
}
- sp = kmalloc(sizeof *sp, GFP_ATOMIC);
- if (!sp) {
+ commands = kzalloc(AT_NUM*(sizeof *commands), GFP_ATOMIC);
+ if (!commands) {
gigaset_free_channel(bcs);
dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
return -ENOMEM;
}
- *sp = cntrl->parm.setup;
- if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp,
+ l = 3 + strlen(cntrl->parm.setup.phone);
+ commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC);
+ if (!commands[AT_DIAL])
+ goto oom;
+ if (cntrl->parm.setup.phone[0] == '*' &&
+ cntrl->parm.setup.phone[1] == '*') {
+ /* internal call: translate ** prefix to CTP value */
+ commands[AT_TYPE] = kstrdup("^SCTP=0\r", GFP_ATOMIC);
+ if (!commands[AT_TYPE])
+ goto oom;
+ snprintf(commands[AT_DIAL], l,
+ "D%s\r", cntrl->parm.setup.phone+2);
+ } else {
+ commands[AT_TYPE] = kstrdup("^SCTP=1\r", GFP_ATOMIC);
+ if (!commands[AT_TYPE])
+ goto oom;
+ snprintf(commands[AT_DIAL], l,
+ "D%s\r", cntrl->parm.setup.phone);
+ }
+
+ l = strlen(cntrl->parm.setup.eazmsn);
+ if (l) {
+ l += 8;
+ commands[AT_MSN] = kmalloc(l, GFP_ATOMIC);
+ if (!commands[AT_MSN])
+ goto oom;
+ snprintf(commands[AT_MSN], l, "^SMSN=%s\r",
+ cntrl->parm.setup.eazmsn);
+ }
+
+ switch (cntrl->parm.setup.si1) {
+ case 1: /* audio */
+ /* BC = 9090A3: 3.1 kHz audio, A-law */
+ commands[AT_BC] = kstrdup("^SBC=9090A3\r", GFP_ATOMIC);
+ if (!commands[AT_BC])
+ goto oom;
+ break;
+ case 7: /* data */
+ default: /* hope the app knows what it is doing */
+ /* BC = 8890: unrestricted digital information */
+ commands[AT_BC] = kstrdup("^SBC=8890\r", GFP_ATOMIC);
+ if (!commands[AT_BC])
+ goto oom;
+ }
+ /* ToDo: other si1 values, inspect si2, set HLC/LLC */
+
+ commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
+ if (!commands[AT_PROTO])
+ goto oom;
+ snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
+
+ commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
+ if (!commands[AT_ISO])
+ goto oom;
+ snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
+ (unsigned) bcs->channel + 1);
+
+ if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
bcs->at_state.seq_index, NULL)) {
- //FIXME what should we do?
- kfree(sp);
+ for (i = 0; i < AT_NUM; ++i)
+ kfree(commands[i]);
+ kfree(commands);
gigaset_free_channel(bcs);
return -ENOMEM;
}
@@ -186,115 +308,102 @@ static int command_from_LL(isdn_ctrl *cntrl)
gig_dbg(DEBUG_CMD, "scheduling DIAL");
gigaset_schedule_event(cs);
break;
- case ISDN_CMD_ACCEPTD: //FIXME
- gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD");
-
- if (cntrl->arg >= cs->channels) {
+ case ISDN_CMD_ACCEPTD:
+ if (ch >= cs->channels) {
dev_err(cs->dev,
- "ISDN_CMD_ACCEPTD: invalid channel (%d)\n",
- (int) cntrl->arg);
+ "ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
return -EINVAL;
}
-
- if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
- EV_ACCEPT, NULL, 0, NULL)) {
- //FIXME what should we do?
+ bcs = cs->bcs + ch;
+ if (!gigaset_add_event(cs, &bcs->at_state,
+ EV_ACCEPT, NULL, 0, NULL))
return -ENOMEM;
- }
gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
gigaset_schedule_event(cs);
break;
case ISDN_CMD_ACCEPTB:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB");
break;
case ISDN_CMD_HANGUP:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_HANGUP (ch: %d)",
- (int) cntrl->arg);
-
- if (cntrl->arg >= cs->channels) {
+ if (ch >= cs->channels) {
dev_err(cs->dev,
- "ISDN_CMD_HANGUP: invalid channel (%d)\n",
- (int) cntrl->arg);
+ "ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
return -EINVAL;
}
-
- if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
- EV_HUP, NULL, 0, NULL)) {
- //FIXME what should we do?
+ bcs = cs->bcs + ch;
+ if (!gigaset_add_event(cs, &bcs->at_state,
+ EV_HUP, NULL, 0, NULL))
return -ENOMEM;
- }
gig_dbg(DEBUG_CMD, "scheduling HUP");
gigaset_schedule_event(cs);
break;
- case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME
- gig_dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ");
+ case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
+ dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
break;
- case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME
- gig_dbg(DEBUG_ANY,
- "ISDN_CMD_SETEAZ (id: %d, ch: %ld, number: %s)",
- cntrl->driver, cntrl->arg, cntrl->parm.num);
+ case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
+ dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
+ cntrl->parm.num);
break;
case ISDN_CMD_SETL2: /* Set L2 to given protocol */
- gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (ch: %ld, proto: %lx)",
- cntrl->arg & 0xff, (cntrl->arg >> 8));
-
- if ((cntrl->arg & 0xff) >= cs->channels) {
+ if (ch >= cs->channels) {
dev_err(cs->dev,
- "ISDN_CMD_SETL2: invalid channel (%d)\n",
- (int) cntrl->arg & 0xff);
+ "ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
return -EINVAL;
}
-
- if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg & 0xff].at_state,
- EV_PROTO_L2, NULL, cntrl->arg >> 8,
- NULL)) {
- //FIXME what should we do?
- return -ENOMEM;
+ bcs = cs->bcs + ch;
+ if (bcs->chstate & CHS_D_UP) {
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL2: channel active (%d)\n", ch);
+ return -EINVAL;
+ }
+ switch (cntrl->arg >> 8) {
+ case ISDN_PROTO_L2_HDLC:
+ gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC");
+ bcs->proto2 = L2_HDLC;
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE");
+ bcs->proto2 = L2_VOICE;
+ break;
+ default:
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL2: unsupported protocol (%lu)\n",
+ cntrl->arg >> 8);
+ return -EINVAL;
}
-
- gig_dbg(DEBUG_CMD, "scheduling PROTO_L2");
- gigaset_schedule_event(cs);
break;
case ISDN_CMD_SETL3: /* Set L3 to given protocol */
- gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (ch: %ld, proto: %lx)",
- cntrl->arg & 0xff, (cntrl->arg >> 8));
-
- if ((cntrl->arg & 0xff) >= cs->channels) {
+ if (ch >= cs->channels) {
dev_err(cs->dev,
- "ISDN_CMD_SETL3: invalid channel (%d)\n",
- (int) cntrl->arg & 0xff);
+ "ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
return -EINVAL;
}
if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
dev_err(cs->dev,
- "ISDN_CMD_SETL3: invalid protocol %lu\n",
+ "ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
cntrl->arg >> 8);
return -EINVAL;
}
break;
case ISDN_CMD_PROCEED:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); //FIXME
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED");
break;
case ISDN_CMD_ALERT:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT");
if (cntrl->arg >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_ALERT: invalid channel (%d)\n",
(int) cntrl->arg);
return -EINVAL;
}
- //bcs = cs->bcs + cntrl->arg;
- //bcs->proto2 = -1;
- // FIXME
break;
case ISDN_CMD_REDIR:
- gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); //FIXME
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR");
break;
case ISDN_CMD_PROT_IO:
gig_dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO");
@@ -324,149 +433,34 @@ static int command_from_LL(isdn_ctrl *cntrl)
}
return retval;
+
+oom:
+ dev_err(bcs->cs->dev, "out of memory\n");
+ for (i = 0; i < AT_NUM; ++i)
+ kfree(commands[i]);
+ return -ENOMEM;
}
-void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
+static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
{
+ isdn_if *iif = cs->iif;
isdn_ctrl command;
command.driver = cs->myid;
command.command = cmd;
command.arg = 0;
- cs->iif.statcallb(&command);
+ iif->statcallb(&command);
}
-void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
+static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
{
+ isdn_if *iif = bcs->cs->iif;
isdn_ctrl command;
command.driver = bcs->cs->myid;
command.command = cmd;
command.arg = bcs->channel;
- bcs->cs->iif.statcallb(&command);
-}
-
-int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data)
-{
- struct bc_state *bcs = at_state->bcs;
- unsigned proto;
- const char *bc;
- size_t length[AT_NUM];
- size_t l;
- int i;
- struct setup_parm *sp = data;
-
- switch (bcs->proto2) {
- case ISDN_PROTO_L2_HDLC:
- proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
- break;
- case ISDN_PROTO_L2_TRANS:
- proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
- break;
- default:
- dev_err(bcs->cs->dev, "%s: invalid L2 protocol: %u\n",
- __func__, bcs->proto2);
- return -EINVAL;
- }
-
- switch (sp->si1) {
- case 1: /* audio */
- bc = "9090A3"; /* 3.1 kHz audio, A-law */
- break;
- case 7: /* data */
- default: /* hope the app knows what it is doing */
- bc = "8890"; /* unrestricted digital information */
- }
- //FIXME add missing si1 values from 1TR6, inspect si2, set HLC/LLC
-
- length[AT_DIAL ] = 1 + strlen(sp->phone) + 1 + 1;
- l = strlen(sp->eazmsn);
- length[AT_MSN ] = l ? 6 + l + 1 + 1 : 0;
- length[AT_BC ] = 5 + strlen(bc) + 1 + 1;
- length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
- length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */
- length[AT_TYPE ] = 6 + 1 + 1 + 1; /* call type: 1 character */
- length[AT_HLC ] = 0;
-
- for (i = 0; i < AT_NUM; ++i) {
- kfree(bcs->commands[i]);
- bcs->commands[i] = NULL;
- if (length[i] &&
- !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
- dev_err(bcs->cs->dev, "out of memory\n");
- return -ENOMEM;
- }
- }
-
- /* type = 1: extern, 0: intern, 2: recall, 3: door, 4: centrex */
- if (sp->phone[0] == '*' && sp->phone[1] == '*') {
- /* internal call: translate ** prefix to CTP value */
- snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
- "D%s\r", sp->phone+2);
- strncpy(bcs->commands[AT_TYPE], "^SCTP=0\r", length[AT_TYPE]);
- } else {
- snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
- "D%s\r", sp->phone);
- strncpy(bcs->commands[AT_TYPE], "^SCTP=1\r", length[AT_TYPE]);
- }
-
- if (bcs->commands[AT_MSN])
- snprintf(bcs->commands[AT_MSN], length[AT_MSN],
- "^SMSN=%s\r", sp->eazmsn);
- snprintf(bcs->commands[AT_BC ], length[AT_BC ],
- "^SBC=%s\r", bc);
- snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
- "^SBPR=%u\r", proto);
- snprintf(bcs->commands[AT_ISO ], length[AT_ISO ],
- "^SISO=%u\r", (unsigned)bcs->channel + 1);
-
- return 0;
-}
-
-int gigaset_isdn_setup_accept(struct at_state_t *at_state)
-{
- unsigned proto;
- size_t length[AT_NUM];
- int i;
- struct bc_state *bcs = at_state->bcs;
-
- switch (bcs->proto2) {
- case ISDN_PROTO_L2_HDLC:
- proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
- break;
- case ISDN_PROTO_L2_TRANS:
- proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
- break;
- default:
- dev_err(at_state->cs->dev, "%s: invalid protocol: %u\n",
- __func__, bcs->proto2);
- return -EINVAL;
- }
-
- length[AT_DIAL ] = 0;
- length[AT_MSN ] = 0;
- length[AT_BC ] = 0;
- length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
- length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */
- length[AT_TYPE ] = 0;
- length[AT_HLC ] = 0;
-
- for (i = 0; i < AT_NUM; ++i) {
- kfree(bcs->commands[i]);
- bcs->commands[i] = NULL;
- if (length[i] &&
- !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
- dev_err(at_state->cs->dev, "out of memory\n");
- return -ENOMEM;
- }
- }
-
- snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
- "^SBPR=%u\r", proto);
- snprintf(bcs->commands[AT_ISO ], length[AT_ISO ],
- "^SISO=%u\r", (unsigned) bcs->channel + 1);
-
- return 0;
+ iif->statcallb(&command);
}
/**
@@ -482,13 +476,14 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
{
struct cardstate *cs = at_state->cs;
struct bc_state *bcs = at_state->bcs;
+ isdn_if *iif = cs->iif;
isdn_ctrl response;
int retval;
/* fill ICALL structure */
response.parm.setup.si1 = 0; /* default: unknown */
response.parm.setup.si2 = 0;
- response.parm.setup.screen = 0; //FIXME how to set these?
+ response.parm.setup.screen = 0;
response.parm.setup.plan = 0;
if (!at_state->str_var[STR_ZBC]) {
/* no BC (internal call): assume speech, A-law */
@@ -509,29 +504,27 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
return ICALL_IGNORE;
}
if (at_state->str_var[STR_NMBR]) {
- strncpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
- sizeof response.parm.setup.phone - 1);
- response.parm.setup.phone[sizeof response.parm.setup.phone - 1] = 0;
+ strlcpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
+ sizeof response.parm.setup.phone);
} else
response.parm.setup.phone[0] = 0;
if (at_state->str_var[STR_ZCPN]) {
- strncpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
- sizeof response.parm.setup.eazmsn - 1);
- response.parm.setup.eazmsn[sizeof response.parm.setup.eazmsn - 1] = 0;
+ strlcpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
+ sizeof response.parm.setup.eazmsn);
} else
response.parm.setup.eazmsn[0] = 0;
if (!bcs) {
dev_notice(cs->dev, "no channel for incoming call\n");
response.command = ISDN_STAT_ICALLW;
- response.arg = 0; //FIXME
+ response.arg = 0;
} else {
gig_dbg(DEBUG_CMD, "Sending ICALL");
response.command = ISDN_STAT_ICALL;
- response.arg = bcs->channel; //FIXME
+ response.arg = bcs->channel;
}
response.driver = cs->myid;
- retval = cs->iif.statcallb(&response);
+ retval = iif->statcallb(&response);
gig_dbg(DEBUG_CMD, "Response: %d", retval);
switch (retval) {
case 0: /* no takers */
@@ -560,16 +553,109 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
}
}
-/* Set Callback function pointer */
-int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid)
+/**
+ * gigaset_isdn_connD() - signal D channel connect
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the D channel connection has
+ * been established.
+ */
+void gigaset_isdn_connD(struct bc_state *bcs)
{
- isdn_if *iif = &cs->iif;
+ gig_dbg(DEBUG_CMD, "sending DCONN");
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+}
- gig_dbg(DEBUG_ANY, "Register driver capabilities to LL");
+/**
+ * gigaset_isdn_hupD() - signal D channel hangup
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the D channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupD(struct bc_state *bcs)
+{
+ gig_dbg(DEBUG_CMD, "sending DHUP");
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+}
+
+/**
+ * gigaset_isdn_connB() - signal B channel connect
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been established.
+ */
+void gigaset_isdn_connB(struct bc_state *bcs)
+{
+ gig_dbg(DEBUG_CMD, "sending BCONN");
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
+}
+
+/**
+ * gigaset_isdn_hupB() - signal B channel hangup
+ * @bcs: B channel descriptor structure.
+ *
+ * Called by main module to notify the LL that the B channel connection has
+ * been shut down.
+ */
+void gigaset_isdn_hupB(struct bc_state *bcs)
+{
+ gig_dbg(DEBUG_CMD, "sending BHUP");
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
+}
+
+/**
+ * gigaset_isdn_start() - signal device availability
+ * @cs: device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is available for
+ * use.
+ */
+void gigaset_isdn_start(struct cardstate *cs)
+{
+ gig_dbg(DEBUG_CMD, "sending RUN");
+ gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
+}
+
+/**
+ * gigaset_isdn_stop() - signal device unavailability
+ * @cs: device descriptor structure.
+ *
+ * Called by main module to notify the LL that the device is no longer
+ * available for use.
+ */
+void gigaset_isdn_stop(struct cardstate *cs)
+{
+ gig_dbg(DEBUG_CMD, "sending STOP");
+ gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
+}
+
+/**
+ * gigaset_isdn_register() - register to LL
+ * @cs: device descriptor structure.
+ * @isdnid: device name.
+ *
+ * Called by main module to register the device with the LL.
+ *
+ * Return value: 1 for success, 0 for failure
+ */
+int gigaset_isdn_register(struct cardstate *cs, const char *isdnid)
+{
+ isdn_if *iif;
+
+ pr_info("ISDN4Linux interface\n");
+
+ iif = kmalloc(sizeof *iif, GFP_KERNEL);
+ if (!iif) {
+ pr_err("out of memory\n");
+ return 0;
+ }
if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
>= sizeof iif->id) {
pr_err("ID too long: %s\n", isdnid);
+ kfree(iif);
return 0;
}
@@ -593,9 +679,26 @@ int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid)
if (!register_isdn(iif)) {
pr_err("register_isdn failed\n");
+ kfree(iif);
return 0;
}
+ cs->iif = iif;
cs->myid = iif->channels; /* Set my device id */
+ cs->hw_hdr_len = HW_HDR_LEN;
return 1;
}
+
+/**
+ * gigaset_isdn_unregister() - unregister from LL
+ * @cs: device descriptor structure.
+ *
+ * Called by main module to unregister the device from the LL.
+ */
+void gigaset_isdn_unregister(struct cardstate *cs)
+{
+ gig_dbg(DEBUG_CMD, "sending UNLOAD");
+ gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
+ kfree(cs->iif);
+ cs->iif = NULL;
+}
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index 6a8e1384e7b..d2260b0055f 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -162,7 +162,7 @@ static int if_open(struct tty_struct *tty, struct file *filp)
return -ENODEV;
if (mutex_lock_interruptible(&cs->mutex))
- return -ERESTARTSYS; // FIXME -EINTR?
+ return -ERESTARTSYS;
tty->driver_data = cs;
++cs->open_count;
@@ -171,7 +171,7 @@ static int if_open(struct tty_struct *tty, struct file *filp)
spin_lock_irqsave(&cs->lock, flags);
cs->tty = tty;
spin_unlock_irqrestore(&cs->lock, flags);
- tty->low_latency = 1; //FIXME test
+ tty->low_latency = 1;
}
mutex_unlock(&cs->mutex);
@@ -228,7 +228,7 @@ static int if_ioctl(struct tty_struct *tty, struct file *file,
gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
if (mutex_lock_interruptible(&cs->mutex))
- return -ERESTARTSYS; // FIXME -EINTR?
+ return -ERESTARTSYS;
if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected");
@@ -299,9 +299,8 @@ static int if_tiocmget(struct tty_struct *tty, struct file *file)
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
if (mutex_lock_interruptible(&cs->mutex))
- return -ERESTARTSYS; // FIXME -EINTR?
+ return -ERESTARTSYS;
- // FIXME read from device?
retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR);
mutex_unlock(&cs->mutex);
@@ -326,7 +325,7 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file,
cs->minor_index, __func__, set, clear);
if (mutex_lock_interruptible(&cs->mutex))
- return -ERESTARTSYS; // FIXME -EINTR?
+ return -ERESTARTSYS;
if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected");
@@ -356,7 +355,7 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
if (mutex_lock_interruptible(&cs->mutex))
- return -ERESTARTSYS; // FIXME -EINTR?
+ return -ERESTARTSYS;
if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected");
@@ -390,7 +389,7 @@ static int if_write_room(struct tty_struct *tty)
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
if (mutex_lock_interruptible(&cs->mutex))
- return -ERESTARTSYS; // FIXME -EINTR?
+ return -ERESTARTSYS;
if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected");
@@ -455,9 +454,8 @@ static void if_throttle(struct tty_struct *tty)
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
else if (!cs->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
- else {
- //FIXME
- }
+ else
+ gig_dbg(DEBUG_ANY, "%s: not implemented\n", __func__);
mutex_unlock(&cs->mutex);
}
@@ -480,9 +478,8 @@ static void if_unthrottle(struct tty_struct *tty)
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
else if (!cs->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
- else {
- //FIXME
- }
+ else
+ gig_dbg(DEBUG_ANY, "%s: not implemented\n", __func__);
mutex_unlock(&cs->mutex);
}
@@ -515,10 +512,9 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
goto out;
}
- // stolen from mct_u232.c
iflag = tty->termios->c_iflag;
cflag = tty->termios->c_cflag;
- old_cflag = old ? old->c_cflag : cflag; //FIXME?
+ old_cflag = old ? old->c_cflag : cflag;
gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
cs->minor_index, iflag, cflag, old_cflag);
@@ -588,7 +584,7 @@ void gigaset_if_init(struct cardstate *cs)
if (!drv->have_tty)
return;
- tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs);
+ tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
mutex_lock(&cs->mutex);
cs->tty_dev = tty_register_device(drv->tty, cs->minor_index, NULL);
@@ -632,7 +628,8 @@ void gigaset_if_receive(struct cardstate *cs,
struct tty_struct *tty;
spin_lock_irqsave(&cs->lock, flags);
- if ((tty = cs->tty) == NULL)
+ tty = cs->tty;
+ if (tty == NULL)
gig_dbg(DEBUG_ANY, "receive on closed device");
else {
tty_buffer_request_room(tty, len);
@@ -659,9 +656,9 @@ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
drv->have_tty = 0;
- if ((drv->tty = alloc_tty_driver(minors)) == NULL)
+ drv->tty = tty = alloc_tty_driver(minors);
+ if (tty == NULL)
goto enomem;
- tty = drv->tty;
tty->magic = TTY_DRIVER_MAGIC,
tty->major = GIG_MAJOR,
@@ -676,8 +673,8 @@ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
tty->owner = THIS_MODULE;
- tty->init_termios = tty_std_termios; //FIXME
- tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME
+ tty->init_termios = tty_std_termios;
+ tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tty, &if_ops);
ret = tty_register_driver(tty);
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
index 9f3ef7b4248..85394a6ebae 100644
--- a/drivers/isdn/gigaset/isocdata.c
+++ b/drivers/isdn/gigaset/isocdata.c
@@ -41,7 +41,8 @@ static inline int isowbuf_freebytes(struct isowbuf_t *iwb)
read = iwb->read;
write = iwb->write;
- if ((freebytes = read - write) > 0) {
+ freebytes = read - write;
+ if (freebytes > 0) {
/* no wraparound: need padding space within regular area */
return freebytes - BAS_OUTBUFPAD;
} else if (read < BAS_OUTBUFPAD) {
@@ -53,29 +54,6 @@ static inline int isowbuf_freebytes(struct isowbuf_t *iwb)
}
}
-/* compare two offsets within the buffer
- * The buffer is seen as circular, with the read position as start
- * returns -1/0/1 if position a </=/> position b without crossing 'read'
- */
-static inline int isowbuf_poscmp(struct isowbuf_t *iwb, int a, int b)
-{
- int read;
- if (a == b)
- return 0;
- read = iwb->read;
- if (a < b) {
- if (a < read && read <= b)
- return +1;
- else
- return -1;
- } else {
- if (b < read && read <= a)
- return -1;
- else
- return +1;
- }
-}
-
/* start writing
* acquire the write semaphore
* return true if acquired, false if busy
@@ -271,7 +249,7 @@ static inline void dump_bytes(enum debuglevel level, const char *tag,
* bit 14..13 = number of bits added by stuffing
*/
static const u16 stufftab[5 * 256] = {
-// previous 1s = 0:
+/* previous 1s = 0: */
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x201f,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
@@ -289,7 +267,7 @@ static const u16 stufftab[5 * 256] = {
0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x0cef,
0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x2ddf,
-// previous 1s = 1:
+/* previous 1s = 1: */
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x200f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x202f,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x204f,
@@ -307,7 +285,7 @@ static const u16 stufftab[5 * 256] = {
0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dcf,
0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x31ef,
-// previous 1s = 2:
+/* previous 1s = 2: */
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x2007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x2017,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x2027, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x2037,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x2047, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x2057,
@@ -325,7 +303,7 @@ static const u16 stufftab[5 * 256] = {
0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dc7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dd7,
0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x31e7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x41f7,
-// previous 1s = 3:
+/* previous 1s = 3: */
0x0000, 0x0001, 0x0002, 0x2003, 0x0004, 0x0005, 0x0006, 0x200b, 0x0008, 0x0009, 0x000a, 0x2013, 0x000c, 0x000d, 0x000e, 0x201b,
0x0010, 0x0011, 0x0012, 0x2023, 0x0014, 0x0015, 0x0016, 0x202b, 0x0018, 0x0019, 0x001a, 0x2033, 0x001c, 0x001d, 0x001e, 0x203b,
0x0020, 0x0021, 0x0022, 0x2043, 0x0024, 0x0025, 0x0026, 0x204b, 0x0028, 0x0029, 0x002a, 0x2053, 0x002c, 0x002d, 0x002e, 0x205b,
@@ -343,7 +321,7 @@ static const u16 stufftab[5 * 256] = {
0x0ce0, 0x0ce1, 0x0ce2, 0x2dc3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dcb, 0x0ce8, 0x0ce9, 0x0cea, 0x2dd3, 0x0cec, 0x0ced, 0x0cee, 0x2ddb,
0x10f0, 0x10f1, 0x10f2, 0x31e3, 0x10f4, 0x10f5, 0x10f6, 0x31eb, 0x20f8, 0x20f9, 0x20fa, 0x41f3, 0x257c, 0x257d, 0x29be, 0x46fb,
-// previous 1s = 4:
+/* previous 1s = 4: */
0x0000, 0x2001, 0x0002, 0x2005, 0x0004, 0x2009, 0x0006, 0x200d, 0x0008, 0x2011, 0x000a, 0x2015, 0x000c, 0x2019, 0x000e, 0x201d,
0x0010, 0x2021, 0x0012, 0x2025, 0x0014, 0x2029, 0x0016, 0x202d, 0x0018, 0x2031, 0x001a, 0x2035, 0x001c, 0x2039, 0x001e, 0x203d,
0x0020, 0x2041, 0x0022, 0x2045, 0x0024, 0x2049, 0x0026, 0x204d, 0x0028, 0x2051, 0x002a, 0x2055, 0x002c, 0x2059, 0x002e, 0x205d,
@@ -367,7 +345,8 @@ static const u16 stufftab[5 * 256] = {
* parameters:
* cin input byte
* ones number of trailing '1' bits in result before this step
- * iwb pointer to output buffer structure (write semaphore must be held)
+ * iwb pointer to output buffer structure
+ * (write semaphore must be held)
* return value:
* number of trailing '1' bits in result after this step
*/
@@ -408,7 +387,8 @@ static inline int hdlc_bitstuff_byte(struct isowbuf_t *iwb, unsigned char cin,
* parameters:
* in input buffer
* count number of bytes in input buffer
- * iwb pointer to output buffer structure (write semaphore must be held)
+ * iwb pointer to output buffer structure
+ * (write semaphore must be held)
* return value:
* position of end of packet in output buffer on success,
* -EAGAIN if write semaphore busy or buffer full
@@ -440,7 +420,8 @@ static inline int hdlc_buildframe(struct isowbuf_t *iwb,
fcs = crc_ccitt_byte(fcs, c);
}
- /* bitstuff and append FCS (complemented, least significant byte first) */
+ /* bitstuff and append FCS
+ * (complemented, least significant byte first) */
fcs ^= 0xffff;
ones = hdlc_bitstuff_byte(iwb, fcs & 0x00ff, ones);
ones = hdlc_bitstuff_byte(iwb, (fcs >> 8) & 0x00ff, ones);
@@ -459,7 +440,8 @@ static inline int hdlc_buildframe(struct isowbuf_t *iwb,
* parameters:
* in input buffer
* count number of bytes in input buffer
- * iwb pointer to output buffer structure (write semaphore must be held)
+ * iwb pointer to output buffer structure
+ * (write semaphore must be held)
* return value:
* position of end of packet in output buffer on success,
* -EAGAIN if write semaphore busy or buffer full
@@ -500,7 +482,7 @@ int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len)
int result;
switch (bcs->proto2) {
- case ISDN_PROTO_L2_HDLC:
+ case L2_HDLC:
result = hdlc_buildframe(bcs->hw.bas->isooutbuf, in, len);
gig_dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d",
__func__, len, result);
@@ -542,8 +524,9 @@ static inline void hdlc_flush(struct bc_state *bcs)
if (likely(bcs->skb != NULL))
skb_trim(bcs->skb, 0);
else if (!bcs->ignore) {
- if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
- skb_reserve(bcs->skb, HW_HDR_LEN);
+ bcs->skb = dev_alloc_skb(SBUFSIZE + bcs->cs->hw_hdr_len);
+ if (bcs->skb)
+ skb_reserve(bcs->skb, bcs->cs->hw_hdr_len);
else
dev_err(bcs->cs->dev, "could not allocate skb\n");
}
@@ -557,43 +540,46 @@ static inline void hdlc_flush(struct bc_state *bcs)
*/
static inline void hdlc_done(struct bc_state *bcs)
{
+ struct cardstate *cs = bcs->cs;
struct sk_buff *procskb;
+ unsigned int len;
if (unlikely(bcs->ignore)) {
bcs->ignore--;
hdlc_flush(bcs);
return;
}
-
- if ((procskb = bcs->skb) == NULL) {
+ procskb = bcs->skb;
+ if (procskb == NULL) {
/* previous error */
gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__);
- gigaset_rcv_error(NULL, bcs->cs, bcs);
+ gigaset_isdn_rcv_err(bcs);
} else if (procskb->len < 2) {
- dev_notice(bcs->cs->dev, "received short frame (%d octets)\n",
+ dev_notice(cs->dev, "received short frame (%d octets)\n",
procskb->len);
bcs->hw.bas->runts++;
- gigaset_rcv_error(procskb, bcs->cs, bcs);
+ dev_kfree_skb_any(procskb);
+ gigaset_isdn_rcv_err(bcs);
} else if (bcs->fcs != PPP_GOODFCS) {
- dev_notice(bcs->cs->dev, "frame check error (0x%04x)\n",
- bcs->fcs);
+ dev_notice(cs->dev, "frame check error (0x%04x)\n", bcs->fcs);
bcs->hw.bas->fcserrs++;
- gigaset_rcv_error(procskb, bcs->cs, bcs);
+ dev_kfree_skb_any(procskb);
+ gigaset_isdn_rcv_err(bcs);
} else {
- procskb->len -= 2; /* subtract FCS */
- procskb->tail -= 2;
- gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)",
- __func__, procskb->len);
+ len = procskb->len;
+ __skb_trim(procskb, len -= 2); /* subtract FCS */
+ gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", __func__, len);
dump_bytes(DEBUG_STREAM_DUMP,
- "rcv data", procskb->data, procskb->len);
- bcs->hw.bas->goodbytes += procskb->len;
- gigaset_rcv_skb(procskb, bcs->cs, bcs);
+ "rcv data", procskb->data, len);
+ bcs->hw.bas->goodbytes += len;
+ gigaset_skb_rcvd(bcs, procskb);
}
- if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
- skb_reserve(bcs->skb, HW_HDR_LEN);
+ bcs->skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
+ if (bcs->skb)
+ skb_reserve(bcs->skb, cs->hw_hdr_len);
else
- dev_err(bcs->cs->dev, "could not allocate skb\n");
+ dev_err(cs->dev, "could not allocate skb\n");
bcs->fcs = PPP_INITFCS;
}
@@ -610,12 +596,8 @@ static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits)
dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits);
bcs->hw.bas->alignerrs++;
- gigaset_rcv_error(bcs->skb, bcs->cs, bcs);
-
- if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
- skb_reserve(bcs->skb, HW_HDR_LEN);
- else
- dev_err(bcs->cs->dev, "could not allocate skb\n");
+ gigaset_isdn_rcv_err(bcs);
+ __skb_trim(bcs->skb, 0);
bcs->fcs = PPP_INITFCS;
}
@@ -646,10 +628,10 @@ static const unsigned char bitcounts[256] = {
};
/* hdlc_unpack
- * perform HDLC frame processing (bit unstuffing, flag detection, FCS calculation)
- * on a sequence of received data bytes (8 bits each, LSB first)
- * pass on successfully received, complete frames as SKBs via gigaset_rcv_skb
- * notify of errors via gigaset_rcv_error
+ * perform HDLC frame processing (bit unstuffing, flag detection, FCS
+ * calculation) on a sequence of received data bytes (8 bits each, LSB first)
+ * pass on successfully received, complete frames as SKBs via gigaset_skb_rcvd
+ * notify of errors via gigaset_isdn_rcv_err
* tally frames, errors etc. in BC structure counters
* parameters:
* src received data
@@ -665,9 +647,12 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
/* load previous state:
* inputstate = set of flag bits:
- * - INS_flag_hunt: no complete opening flag received since connection setup or last abort
- * - INS_have_data: at least one complete data byte received since last flag
- * seqlen = number of consecutive '1' bits in last 7 input stream bits (0..7)
+ * - INS_flag_hunt: no complete opening flag received since connection
+ * setup or last abort
+ * - INS_have_data: at least one complete data byte received since last
+ * flag
+ * seqlen = number of consecutive '1' bits in last 7 input stream bits
+ * (0..7)
* inbyte = accumulated partial data byte (if !INS_flag_hunt)
* inbits = number of valid bits in inbyte, starting at LSB (0..6)
*/
@@ -701,9 +686,11 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
inbyte = c >> (lead1 + 1);
inbits = 7 - lead1;
if (trail1 >= 8) {
- /* interior stuffing: omitting the MSB handles most cases */
+ /* interior stuffing:
+ * omitting the MSB handles most cases,
+ * correct the incorrectly handled
+ * cases individually */
inbits--;
- /* correct the incorrectly handled cases individually */
switch (c) {
case 0xbe:
inbyte = 0x3f;
@@ -729,13 +716,14 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
hdlc_flush(bcs);
inputstate |= INS_flag_hunt;
} else if (seqlen == 6) {
- /* closing flag, including (6 - lead1) '1's and one '0' from inbits */
+ /* closing flag, including (6 - lead1) '1's
+ * and one '0' from inbits */
if (inbits > 7 - lead1) {
hdlc_frag(bcs, inbits + lead1 - 7);
inputstate &= ~INS_have_data;
} else {
if (inbits < 7 - lead1)
- ubc->stolen0s ++;
+ ubc->stolen0s++;
if (inputstate & INS_have_data) {
hdlc_done(bcs);
inputstate &= ~INS_have_data;
@@ -744,7 +732,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
if (c == PPP_FLAG) {
/* complete flag, LSB overlaps preceding flag */
- ubc->shared0s ++;
+ ubc->shared0s++;
inbits = 0;
inbyte = 0;
} else if (trail1 != 7) {
@@ -752,9 +740,11 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
inbyte = c >> (lead1 + 1);
inbits = 7 - lead1;
if (trail1 >= 8) {
- /* interior stuffing: omitting the MSB handles most cases */
+ /* interior stuffing:
+ * omitting the MSB handles most cases,
+ * correct the incorrectly handled
+ * cases individually */
inbits--;
- /* correct the incorrectly handled cases individually */
switch (c) {
case 0xbe:
inbyte = 0x3f;
@@ -762,7 +752,8 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
}
}
} else {
- /* abort sequence follows, skb already empty anyway */
+ /* abort sequence follows,
+ * skb already empty anyway */
ubc->aborts++;
inputstate |= INS_flag_hunt;
}
@@ -787,14 +778,17 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
} else {
/* stuffed data */
if (trail1 < 7) { /* => seqlen == 5 */
- /* stuff bit at position lead1, no interior stuffing */
+ /* stuff bit at position lead1,
+ * no interior stuffing */
unsigned char mask = (1 << lead1) - 1;
c = (c & mask) | ((c & ~mask) >> 1);
inbyte |= c << inbits;
inbits += 7;
} else if (seqlen < 5) { /* trail1 >= 8 */
- /* interior stuffing: omitting the MSB handles most cases */
- /* correct the incorrectly handled cases individually */
+ /* interior stuffing:
+ * omitting the MSB handles most cases,
+ * correct the incorrectly handled
+ * cases individually */
switch (c) {
case 0xbe:
c = 0x7e;
@@ -804,8 +798,9 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
inbits += 7;
} else { /* seqlen == 5 && trail1 >= 8 */
- /* stuff bit at lead1 *and* interior stuffing */
- switch (c) { /* unstuff individually */
+ /* stuff bit at lead1 *and* interior
+ * stuffing -- unstuff individually */
+ switch (c) {
case 0x7d:
c = 0x3f;
break;
@@ -841,7 +836,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
}
/* trans_receive
- * pass on received USB frame transparently as SKB via gigaset_rcv_skb
+ * pass on received USB frame transparently as SKB via gigaset_skb_rcvd
* invert bytes
* tally frames, errors etc. in BC structure counters
* parameters:
@@ -852,6 +847,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count,
static inline void trans_receive(unsigned char *src, unsigned count,
struct bc_state *bcs)
{
+ struct cardstate *cs = bcs->cs;
struct sk_buff *skb;
int dobytes;
unsigned char *dst;
@@ -861,13 +857,14 @@ static inline void trans_receive(unsigned char *src, unsigned count,
hdlc_flush(bcs);
return;
}
- if (unlikely((skb = bcs->skb) == NULL)) {
- bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN);
+ skb = bcs->skb;
+ if (unlikely(skb == NULL)) {
+ bcs->skb = skb = dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
if (!skb) {
- dev_err(bcs->cs->dev, "could not allocate skb\n");
+ dev_err(cs->dev, "could not allocate skb\n");
return;
}
- skb_reserve(skb, HW_HDR_LEN);
+ skb_reserve(skb, cs->hw_hdr_len);
}
bcs->hw.bas->goodbytes += skb->len;
dobytes = TRANSBUFSIZE - skb->len;
@@ -881,23 +878,24 @@ static inline void trans_receive(unsigned char *src, unsigned count,
if (dobytes == 0) {
dump_bytes(DEBUG_STREAM_DUMP,
"rcv data", skb->data, skb->len);
- gigaset_rcv_skb(skb, bcs->cs, bcs);
- bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN);
+ gigaset_skb_rcvd(bcs, skb);
+ bcs->skb = skb =
+ dev_alloc_skb(SBUFSIZE + cs->hw_hdr_len);
if (!skb) {
- dev_err(bcs->cs->dev,
- "could not allocate skb\n");
+ dev_err(cs->dev, "could not allocate skb\n");
return;
}
- skb_reserve(bcs->skb, HW_HDR_LEN);
+ skb_reserve(skb, cs->hw_hdr_len);
dobytes = TRANSBUFSIZE;
}
}
}
-void gigaset_isoc_receive(unsigned char *src, unsigned count, struct bc_state *bcs)
+void gigaset_isoc_receive(unsigned char *src, unsigned count,
+ struct bc_state *bcs)
{
switch (bcs->proto2) {
- case ISDN_PROTO_L2_HDLC:
+ case L2_HDLC:
hdlc_unpack(src, count, bcs);
break;
default: /* assume transparent */
@@ -981,8 +979,10 @@ void gigaset_isoc_input(struct inbuf_t *inbuf)
* @bcs: B channel descriptor structure.
* @skb: data to send.
*
- * Called by i4l.c to queue an skb for sending, and start transmission if
+ * Called by LL to queue an skb for sending, and start transmission if
* necessary.
+ * Once the payload data has been transmitted completely, gigaset_skb_sent()
+ * will be called with the skb's link layer header preserved.
*
* Return value:
* number of bytes accepted for sending (skb->len) if ok,
diff --git a/drivers/isdn/gigaset/proc.c b/drivers/isdn/gigaset/proc.c
index 9715aad9c3f..758a00c1d2e 100644
--- a/drivers/isdn/gigaset/proc.c
+++ b/drivers/isdn/gigaset/proc.c
@@ -39,7 +39,7 @@ static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
return -EINVAL;
if (mutex_lock_interruptible(&cs->mutex))
- return -ERESTARTSYS; // FIXME -EINTR?
+ return -ERESTARTSYS;
cs->waiting = 1;
if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE,
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
index 3071a52467e..168d585d64d 100644
--- a/drivers/isdn/gigaset/ser-gigaset.c
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -164,9 +164,15 @@ static void gigaset_modem_fill(unsigned long data)
{
struct cardstate *cs = (struct cardstate *) data;
struct bc_state *bcs;
+ struct sk_buff *nextskb;
int sent = 0;
- if (!cs || !(bcs = cs->bcs)) {
+ if (!cs) {
+ gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
+ return;
+ }
+ bcs = cs->bcs;
+ if (!bcs) {
gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
return;
}
@@ -179,9 +185,11 @@ static void gigaset_modem_fill(unsigned long data)
return;
/* no command to send; get skb */
- if (!(bcs->tx_skb = skb_dequeue(&bcs->squeue)))
+ nextskb = skb_dequeue(&bcs->squeue);
+ if (!nextskb)
/* no skb either, nothing to do */
return;
+ bcs->tx_skb = nextskb;
gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)",
(unsigned long) bcs->tx_skb);
@@ -236,19 +244,20 @@ static void flush_send_queue(struct cardstate *cs)
* number of bytes queued, or error code < 0
*/
static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
- int len, struct tasklet_struct *wake_tasklet)
+ int len, struct tasklet_struct *wake_tasklet)
{
struct cmdbuf_t *cb;
unsigned long flags;
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
- DEBUG_TRANSCMD : DEBUG_LOCKCMD,
- "CMD Transmit", len, buf);
+ DEBUG_TRANSCMD : DEBUG_LOCKCMD,
+ "CMD Transmit", len, buf);
if (len <= 0)
return 0;
- if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
+ cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC);
+ if (!cb) {
dev_err(cs->dev, "%s: out of memory!\n", __func__);
return -ENOMEM;
}
@@ -392,7 +401,6 @@ static void gigaset_device_release(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
/* adapted from platform_device_release() in drivers/base/platform.c */
- //FIXME is this actually necessary?
kfree(dev->platform_data);
kfree(pdev->resource);
}
@@ -404,16 +412,20 @@ static void gigaset_device_release(struct device *dev)
static int gigaset_initcshw(struct cardstate *cs)
{
int rc;
+ struct ser_cardstate *scs;
- if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) {
+ scs = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL);
+ if (!scs) {
pr_err("out of memory\n");
return 0;
}
+ cs->hw.ser = scs;
cs->hw.ser->dev.name = GIGASET_MODULENAME;
cs->hw.ser->dev.id = cs->minor_index;
cs->hw.ser->dev.dev.release = gigaset_device_release;
- if ((rc = platform_device_register(&cs->hw.ser->dev)) != 0) {
+ rc = platform_device_register(&cs->hw.ser->dev);
+ if (rc != 0) {
pr_err("error %d registering platform device\n", rc);
kfree(cs->hw.ser);
cs->hw.ser = NULL;
@@ -422,7 +434,7 @@ static int gigaset_initcshw(struct cardstate *cs)
dev_set_drvdata(&cs->hw.ser->dev.dev, cs);
tasklet_init(&cs->write_tasklet,
- &gigaset_modem_fill, (unsigned long) cs);
+ gigaset_modem_fill, (unsigned long) cs);
return 1;
}
@@ -434,7 +446,8 @@ static int gigaset_initcshw(struct cardstate *cs)
* Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c
* and by "if_lock" and "if_termios" in interface.c
*/
-static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, unsigned new_state)
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
+ unsigned new_state)
{
struct tty_struct *tty = cs->hw.ser->tty;
unsigned int set, clear;
@@ -520,8 +533,8 @@ gigaset_tty_open(struct tty_struct *tty)
}
/* allocate memory for our device state and intialize it */
- if (!(cs = gigaset_initcs(driver, 1, 1, 0, cidmode,
- GIGASET_MODULENAME)))
+ cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
+ if (!cs)
goto error;
cs->dev = &cs->hw.ser->dev.dev;
@@ -690,7 +703,8 @@ gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
if (!cs)
return;
- if (!(inbuf = cs->inbuf)) {
+ inbuf = cs->inbuf;
+ if (!inbuf) {
dev_err(cs->dev, "%s: no inbuf\n", __func__);
cs_put(cs);
return;
@@ -770,18 +784,21 @@ static int __init ser_gigaset_init(void)
int rc;
gig_dbg(DEBUG_INIT, "%s", __func__);
- if ((rc = platform_driver_register(&device_driver)) != 0) {
+ rc = platform_driver_register(&device_driver);
+ if (rc != 0) {
pr_err("error %d registering platform driver\n", rc);
return rc;
}
/* allocate memory for our driver state and intialize it */
- if (!(driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+ driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
- &ops, THIS_MODULE)))
+ &ops, THIS_MODULE);
+ if (!driver)
goto error;
- if ((rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc)) != 0) {
+ rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
+ if (rc != 0) {
pr_err("error %d registering line discipline\n", rc);
goto error;
}
@@ -808,7 +825,8 @@ static void __exit ser_gigaset_exit(void)
driver = NULL;
}
- if ((rc = tty_unregister_ldisc(N_GIGASET_M101)) != 0)
+ rc = tty_unregister_ldisc(N_GIGASET_M101);
+ if (rc != 0)
pr_err("error %d unregistering line discipline\n", rc);
platform_driver_unregister(&device_driver);
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index 4deb1ab0dbf..3ab1daeb276 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -43,14 +43,14 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode");
#define GIGASET_MODULENAME "usb_gigaset"
#define GIGASET_DEVNAME "ttyGU"
-#define IF_WRITEBUF 2000 //FIXME // WAKEUP_CHARS: 256
+#define IF_WRITEBUF 2000 /* arbitrary limit */
/* Values for the Gigaset M105 Data */
#define USB_M105_VENDOR_ID 0x0681
#define USB_M105_PRODUCT_ID 0x0009
/* table of devices that work with this driver */
-static const struct usb_device_id gigaset_table [] = {
+static const struct usb_device_id gigaset_table[] = {
{ USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) },
{ } /* Terminating entry */
};
@@ -97,8 +97,8 @@ MODULE_DEVICE_TABLE(usb, gigaset_table);
* 41 19 -- -- -- -- 06 00 00 00 00 xx 11 13
* Used after every "configuration sequence" (RQ 12, RQs 01/03/13).
* xx is usually 0x00 but was 0x7e before starting data transfer
- * in unimodem mode. So, this might be an array of characters that need
- * special treatment ("commit all bufferd data"?), 11=^Q, 13=^S.
+ * in unimodem mode. So, this might be an array of characters that
+ * need special treatment ("commit all bufferd data"?), 11=^Q, 13=^S.
*
* Unimodem mode: use "modprobe ppp_async flag_time=0" as the device _needs_ two
* flags per packet.
@@ -114,7 +114,7 @@ static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
static int gigaset_resume(struct usb_interface *intf);
static int gigaset_pre_reset(struct usb_interface *intf);
-static struct gigaset_driver *driver = NULL;
+static struct gigaset_driver *driver;
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gigaset_usb_driver = {
@@ -141,6 +141,7 @@ struct usb_cardstate {
struct urb *bulk_out_urb;
/* Input buffer */
+ unsigned char *rcvbuf;
int rcvbuf_size;
struct urb *read_urb;
__u8 int_in_endpointAddr;
@@ -164,13 +165,11 @@ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
val = tiocm_to_gigaset(new_state);
gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask);
- // don't use this in an interrupt/BH
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41,
(val & 0xff) | ((mask & 0xff) << 8), 0,
NULL, 0, 2000 /* timeout? */);
if (r < 0)
return r;
- //..
return 0;
}
@@ -220,7 +219,6 @@ static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
cflag &= CBAUD;
switch (cflag) {
- //FIXME more values?
case B300: rate = 300; break;
case B600: rate = 600; break;
case B1200: rate = 1200; break;
@@ -273,7 +271,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
/* set the number of stop bits */
if (cflag & CSTOPB) {
if ((cflag & CSIZE) == CS5)
- val |= 1; /* 1.5 stop bits */ //FIXME is this okay?
+ val |= 1; /* 1.5 stop bits */
else
val |= 2; /* 2 stop bits */
}
@@ -282,7 +280,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
}
- /*================================================================================================================*/
+/*============================================================================*/
static int gigaset_init_bchannel(struct bc_state *bcs)
{
/* nothing to do for M10x */
@@ -344,7 +342,6 @@ static void gigaset_modem_fill(unsigned long data)
if (write_modem(cs) < 0) {
gig_dbg(DEBUG_OUTPUT,
"modem_fill: write_modem failed");
- // FIXME should we tell the LL?
again = 1; /* no callback will be called! */
}
}
@@ -356,8 +353,8 @@ static void gigaset_modem_fill(unsigned long data)
*/
static void gigaset_read_int_callback(struct urb *urb)
{
- struct inbuf_t *inbuf = urb->context;
- struct cardstate *cs = inbuf->cs;
+ struct cardstate *cs = urb->context;
+ struct inbuf_t *inbuf = cs->inbuf;
int status = urb->status;
int r;
unsigned numbytes;
@@ -368,7 +365,7 @@ static void gigaset_read_int_callback(struct urb *urb)
numbytes = urb->actual_length;
if (numbytes) {
- src = inbuf->rcvbuf;
+ src = cs->hw.usb->rcvbuf;
if (unlikely(*src))
dev_warn(cs->dev,
"%s: There was no leading 0, but 0x%02x!\n",
@@ -440,7 +437,7 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb)
struct cmdbuf_t *tcb;
unsigned long flags;
int count;
- int status = -ENOENT; // FIXME
+ int status = -ENOENT;
struct usb_cardstate *ucs = cs->hw.usb;
do {
@@ -480,7 +477,9 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb)
ucs->busy = 1;
spin_lock_irqsave(&cs->lock, flags);
- status = cs->connected ? usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) : -ENODEV;
+ status = cs->connected ?
+ usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) :
+ -ENODEV;
spin_unlock_irqrestore(&cs->lock, flags);
if (status) {
@@ -510,8 +509,8 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
if (len <= 0)
return 0;
-
- if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
+ cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC);
+ if (!cb) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
return -ENOMEM;
}
@@ -615,7 +614,7 @@ static int gigaset_initcshw(struct cardstate *cs)
ucs->bulk_out_urb = NULL;
ucs->read_urb = NULL;
tasklet_init(&cs->write_tasklet,
- &gigaset_modem_fill, (unsigned long) cs);
+ gigaset_modem_fill, (unsigned long) cs);
return 1;
}
@@ -637,9 +636,7 @@ static int write_modem(struct cardstate *cs)
return -EINVAL;
}
- /* Copy data to bulk out buffer and // FIXME copying not necessary
- * transmit data
- */
+ /* Copy data to bulk out buffer and transmit data */
count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
skb_pull(bcs->tx_skb, count);
@@ -650,7 +647,8 @@ static int write_modem(struct cardstate *cs)
if (cs->connected) {
usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
usb_sndbulkpipe(ucs->udev,
- ucs->bulk_out_endpointAddr & 0x0f),
+ ucs->bulk_out_endpointAddr &
+ 0x0f),
ucs->bulk_out_buffer, count,
gigaset_write_bulk_callback, cs);
ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC);
@@ -666,7 +664,7 @@ static int write_modem(struct cardstate *cs)
if (!bcs->tx_skb->len) {
/* skb sent completely */
- gigaset_skb_sent(bcs, bcs->tx_skb); //FIXME also, when ret<0?
+ gigaset_skb_sent(bcs, bcs->tx_skb);
gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
(unsigned long) bcs->tx_skb);
@@ -763,8 +761,8 @@ static int gigaset_probe(struct usb_interface *interface,
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
ucs->rcvbuf_size = buffer_size;
ucs->int_in_endpointAddr = endpoint->bEndpointAddress;
- cs->inbuf[0].rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
- if (!cs->inbuf[0].rcvbuf) {
+ ucs->rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!ucs->rcvbuf) {
dev_err(cs->dev, "Couldn't allocate rcvbuf\n");
retval = -ENOMEM;
goto error;
@@ -773,9 +771,9 @@ static int gigaset_probe(struct usb_interface *interface,
usb_fill_int_urb(ucs->read_urb, udev,
usb_rcvintpipe(udev,
endpoint->bEndpointAddress & 0x0f),
- cs->inbuf[0].rcvbuf, buffer_size,
+ ucs->rcvbuf, buffer_size,
gigaset_read_int_callback,
- cs->inbuf + 0, endpoint->bInterval);
+ cs, endpoint->bInterval);
retval = usb_submit_urb(ucs->read_urb, GFP_KERNEL);
if (retval) {
@@ -789,7 +787,7 @@ static int gigaset_probe(struct usb_interface *interface,
if (!gigaset_start(cs)) {
tasklet_kill(&cs->write_tasklet);
- retval = -ENODEV; //FIXME
+ retval = -ENODEV;
goto error;
}
return 0;
@@ -798,11 +796,11 @@ error:
usb_kill_urb(ucs->read_urb);
kfree(ucs->bulk_out_buffer);
usb_free_urb(ucs->bulk_out_urb);
- kfree(cs->inbuf[0].rcvbuf);
+ kfree(ucs->rcvbuf);
usb_free_urb(ucs->read_urb);
usb_set_intfdata(interface, NULL);
ucs->read_urb = ucs->bulk_out_urb = NULL;
- cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL;
+ ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
usb_put_dev(ucs->udev);
ucs->udev = NULL;
ucs->interface = NULL;
@@ -831,10 +829,10 @@ static void gigaset_disconnect(struct usb_interface *interface)
kfree(ucs->bulk_out_buffer);
usb_free_urb(ucs->bulk_out_urb);
- kfree(cs->inbuf[0].rcvbuf);
+ kfree(ucs->rcvbuf);
usb_free_urb(ucs->read_urb);
ucs->read_urb = ucs->bulk_out_urb = NULL;
- cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL;
+ ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
usb_put_dev(ucs->udev);
ucs->interface = NULL;
@@ -916,9 +914,10 @@ static int __init usb_gigaset_init(void)
int result;
/* allocate memory for our driver state and intialize it */
- if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
- GIGASET_MODULENAME, GIGASET_DEVNAME,
- &ops, THIS_MODULE)) == NULL)
+ driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+ GIGASET_MODULENAME, GIGASET_DEVNAME,
+ &ops, THIS_MODULE);
+ if (driver == NULL)
goto error;
/* register this driver with the USB subsystem */
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
index c72565520e4..5a6ae646a63 100644
--- a/drivers/isdn/hardware/avm/avm_cs.c
+++ b/drivers/isdn/hardware/avm/avm_cs.c
@@ -111,8 +111,6 @@ static int avmcs_probe(struct pcmcia_device *p_dev)
p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
-
/* General socket configuration */
p_dev->conf.Attributes = CONF_ENABLE_IRQ;
p_dev->conf.IntType = INT_MEMORY_AND_IO;
@@ -198,7 +196,6 @@ static int avmcs_config(struct pcmcia_device *link)
*/
i = pcmcia_request_irq(link, &link->irq);
if (i != 0) {
- cs_error(link, RequestIRQ, i);
/* undo */
pcmcia_disable_device(link);
break;
@@ -209,7 +206,6 @@ static int avmcs_config(struct pcmcia_device *link)
*/
i = pcmcia_request_configuration(link, &link->conf);
if (i != 0) {
- cs_error(link, RequestConfiguration, i);
pcmcia_disable_device(link);
break;
}
diff --git a/drivers/isdn/hardware/eicon/di.c b/drivers/isdn/hardware/eicon/di.c
index b029d130eb2..cb14ae3e715 100644
--- a/drivers/isdn/hardware/eicon/di.c
+++ b/drivers/isdn/hardware/eicon/di.c
@@ -806,7 +806,7 @@ static void xdi_xlog_request (byte Adapter, byte Id,
DELIVERY - indication entered isdn_rc function
RNR=... - application had returned RNR=... after the
look ahead callback
- RNum=0 - aplication had not returned any buffer to copy
+ RNum=0 - application had not returned any buffer to copy
this indication and will copy it self
COMPLETE - XDI had copied the data to the buffers provided
bu the application and is about to issue the
diff --git a/drivers/isdn/hardware/eicon/maintidi.c b/drivers/isdn/hardware/eicon/maintidi.c
index 41c26e75645..534978bdf38 100644
--- a/drivers/isdn/hardware/eicon/maintidi.c
+++ b/drivers/isdn/hardware/eicon/maintidi.c
@@ -385,7 +385,7 @@ static int SuperTraceMessageInput (void* hLib) {
}
break;
default:
- diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind (DMA mode): %02x", Ind);
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Unknown IDI Ind (DMA mode): %02x", Ind);
}
p += (this_ind_length+1);
total_length -= (4 + this_ind_length);
@@ -420,7 +420,7 @@ static int SuperTraceMessageInput (void* hLib) {
}
break;
default:
- diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind: %02x", Ind);
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Unknown IDI Ind: %02x", Ind);
}
}
}
diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c
index fc46a26cb14..a64bb6c67ba 100644
--- a/drivers/isdn/hardware/mISDN/hfcsusb.c
+++ b/drivers/isdn/hardware/mISDN/hfcsusb.c
@@ -721,7 +721,7 @@ hfcsusb_setup_bch(struct bchannel *bch, int protocol)
switch (protocol) {
case (-1): /* used for init */
bch->state = -1;
- /* fall trough */
+ /* fall through */
case (ISDN_P_NONE):
if (bch->state == ISDN_P_NONE)
return 0; /* already in idle state */
diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h
index 43efe7358fa..369196adae0 100644
--- a/drivers/isdn/hardware/mISDN/hfcsusb.h
+++ b/drivers/isdn/hardware/mISDN/hfcsusb.h
@@ -150,7 +150,7 @@ symbolic(struct hfcusb_symbolic_list list[], const int num)
for (i = 0; list[i].name != NULL; i++)
if (list[i].num == num)
return list[i].name;
- return "<unkown USB Error>";
+ return "<unknown USB Error>";
}
/* USB descriptor need to contain one of the following EndPoint combination: */
diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c
index de352a17673..09095c74711 100644
--- a/drivers/isdn/hardware/mISDN/mISDNisar.c
+++ b/drivers/isdn/hardware/mISDN/mISDNisar.c
@@ -860,7 +860,7 @@ isar_pump_statev_modem(struct isar_ch *ch, u8 devt) {
pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name);
break;
default:
- pr_info("u%s: nknown pump stev %x\n", ch->is->name, devt);
+ pr_info("u%s: unknown pump stev %x\n", ch->is->name, devt);
break;
}
}
diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c
index ff3a4e290da..7726afdbb40 100644
--- a/drivers/isdn/hardware/mISDN/speedfax.c
+++ b/drivers/isdn/hardware/mISDN/speedfax.c
@@ -110,6 +110,7 @@ set_debug(const char *val, struct kernel_param *kp)
MODULE_AUTHOR("Karsten Keil");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(SPEEDFAX_REV);
+MODULE_FIRMWARE("isdn/ISAR.BIN");
module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Speedfax debug mask");
module_param(irqloops, uint, S_IRUGO | S_IWUSR);
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
index 23560c897ec..f9bdff39cf4 100644
--- a/drivers/isdn/hisax/avma1_cs.c
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -30,22 +30,6 @@ MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA car
MODULE_AUTHOR("Carsten Paeth");
MODULE_LICENSE("GPL");
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
-static char *version =
-"avma1_cs.c 1.00 1998/01/23 10:00:00 (Carsten Paeth)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -119,7 +103,7 @@ static int avma1cs_probe(struct pcmcia_device *p_dev)
{
local_info_t *local;
- DEBUG(0, "avma1cs_attach()\n");
+ dev_dbg(&p_dev->dev, "avma1cs_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
@@ -139,8 +123,6 @@ static int avma1cs_probe(struct pcmcia_device *p_dev)
p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
-
/* General socket configuration */
p_dev->conf.Attributes = CONF_ENABLE_IRQ;
p_dev->conf.IntType = INT_MEMORY_AND_IO;
@@ -161,7 +143,7 @@ static int avma1cs_probe(struct pcmcia_device *p_dev)
static void avma1cs_detach(struct pcmcia_device *link)
{
- DEBUG(0, "avma1cs_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "avma1cs_detach(0x%p)\n", link);
avma1cs_release(link);
kfree(link->priv);
} /* avma1cs_detach */
@@ -203,7 +185,7 @@ static int avma1cs_config(struct pcmcia_device *link)
dev = link->priv;
- DEBUG(0, "avma1cs_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "avma1cs_config(0x%p)\n", link);
devname[0] = 0;
if (link->prod_id[1])
@@ -218,7 +200,6 @@ static int avma1cs_config(struct pcmcia_device *link)
*/
i = pcmcia_request_irq(link, &link->irq);
if (i != 0) {
- cs_error(link, RequestIRQ, i);
/* undo */
pcmcia_disable_device(link);
break;
@@ -229,7 +210,6 @@ static int avma1cs_config(struct pcmcia_device *link)
*/
i = pcmcia_request_configuration(link, &link->conf);
if (i != 0) {
- cs_error(link, RequestConfiguration, i);
pcmcia_disable_device(link);
break;
}
@@ -281,7 +261,7 @@ static void avma1cs_release(struct pcmcia_device *link)
{
local_info_t *local = link->priv;
- DEBUG(0, "avma1cs_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "avma1cs_release(0x%p)\n", link);
/* now unregister function with hisax */
HiSax_closecard(local->node.minor);
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
index f4d0fe29bcf..a2f709f5397 100644
--- a/drivers/isdn/hisax/elsa_cs.c
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -57,23 +57,6 @@ MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Elsa PCM cards");
MODULE_AUTHOR("Klaus Lichtenwalder");
MODULE_LICENSE("Dual MPL/GPL");
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
-static char *version =
-"elsa_cs.c $Revision: 1.2.2.4 $ $Date: 2004/01/25 15:07:06 $ (K.Lichtenwalder)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -142,7 +125,7 @@ static int elsa_cs_probe(struct pcmcia_device *link)
{
local_info_t *local;
- DEBUG(0, "elsa_cs_attach()\n");
+ dev_dbg(&link->dev, "elsa_cs_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
@@ -155,7 +138,6 @@ static int elsa_cs_probe(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
link->irq.Handler = NULL;
/*
@@ -188,7 +170,7 @@ static void elsa_cs_detach(struct pcmcia_device *link)
{
local_info_t *info = link->priv;
- DEBUG(0, "elsa_cs_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "elsa_cs_detach(0x%p)\n", link);
info->busy = 1;
elsa_cs_release(link);
@@ -231,30 +213,25 @@ static int elsa_cs_configcheck(struct pcmcia_device *p_dev,
static int elsa_cs_config(struct pcmcia_device *link)
{
local_info_t *dev;
- int i, last_fn;
+ int i;
IsdnCard_t icard;
- DEBUG(0, "elsa_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "elsa_config(0x%p)\n", link);
dev = link->priv;
i = pcmcia_loop_config(link, elsa_cs_configcheck, NULL);
- if (i != 0) {
- last_fn = RequestIO;
- goto cs_failed;
- }
+ if (i != 0)
+ goto failed;
i = pcmcia_request_irq(link, &link->irq);
if (i != 0) {
link->irq.AssignedIRQ = 0;
- last_fn = RequestIRQ;
- goto cs_failed;
+ goto failed;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- last_fn = RequestConfiguration;
- goto cs_failed;
- }
+ if (i != 0)
+ goto failed;
/* At this point, the dev_node_t structure(s) should be
initialized and arranged in a linked list at link->dev. *//* */
@@ -290,8 +267,7 @@ static int elsa_cs_config(struct pcmcia_device *link)
((local_info_t*)link->priv)->cardnr = i;
return 0;
-cs_failed:
- cs_error(link, last_fn, i);
+failed:
elsa_cs_release(link);
return -ENODEV;
} /* elsa_cs_config */
@@ -308,7 +284,7 @@ static void elsa_cs_release(struct pcmcia_device *link)
{
local_info_t *local = link->priv;
- DEBUG(0, "elsa_cs_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "elsa_cs_release(0x%p)\n", link);
if (local) {
if (local->cardnr >= 0) {
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
index a420b64472e..aaaeaafd86f 100644
--- a/drivers/isdn/hisax/hfc_usb.c
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -1086,7 +1086,7 @@ hfc_usb_l2l1(struct hisax_if *my_hisax_if, int pr, void *arg)
break;
default:
DBG(HFCUSB_DBG_STATES,
- "HFC_USB: hfc_usb_d_l2l1: unkown state : %#x", pr);
+ "HFC_USB: hfc_usb_d_l2l1: unknown state : %#x", pr);
break;
}
}
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
index 9a3c9f5e4fe..af5d393cc2d 100644
--- a/drivers/isdn/hisax/sedlbauer_cs.c
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -57,24 +57,6 @@ MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Sedlbauer cards");
MODULE_AUTHOR("Marcus Niemann");
MODULE_LICENSE("Dual MPL/GPL");
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
-static char *version =
-"sedlbauer_cs.c 1.1a 2001/01/28 15:04:04 (M.Niemann)";
-#else
-#define DEBUG(n, args...)
-#endif
-
/*====================================================================*/
@@ -151,7 +133,7 @@ static int sedlbauer_probe(struct pcmcia_device *link)
{
local_info_t *local;
- DEBUG(0, "sedlbauer_attach()\n");
+ dev_dbg(&link->dev, "sedlbauer_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
@@ -163,7 +145,6 @@ static int sedlbauer_probe(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->irq.Handler = NULL;
/*
@@ -198,7 +179,7 @@ static int sedlbauer_probe(struct pcmcia_device *link)
static void sedlbauer_detach(struct pcmcia_device *link)
{
- DEBUG(0, "sedlbauer_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "sedlbauer_detach(0x%p)\n", link);
((local_info_t *)link->priv)->stop = 1;
sedlbauer_release(link);
@@ -214,9 +195,6 @@ static void sedlbauer_detach(struct pcmcia_device *link)
device available to the system.
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int sedlbauer_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -293,11 +271,11 @@ static int sedlbauer_config_check(struct pcmcia_device *p_dev,
req->Base = mem->win[0].host_addr;
req->Size = mem->win[0].len;
req->AccessSpeed = 0;
- if (pcmcia_request_window(&p_dev, req, &p_dev->win) != 0)
+ if (pcmcia_request_window(p_dev, req, &p_dev->win) != 0)
return -ENODEV;
map.Page = 0;
map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(p_dev->win, &map) != 0)
+ if (pcmcia_map_mem_page(p_dev, p_dev->win, &map) != 0)
return -ENODEV;
}
return 0;
@@ -309,10 +287,10 @@ static int sedlbauer_config(struct pcmcia_device *link)
{
local_info_t *dev = link->priv;
win_req_t *req;
- int last_fn, last_ret;
+ int ret;
IsdnCard_t icard;
- DEBUG(0, "sedlbauer_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "sedlbauer_config(0x%p)\n", link);
req = kzalloc(sizeof(win_req_t), GFP_KERNEL);
if (!req)
@@ -330,8 +308,8 @@ static int sedlbauer_config(struct pcmcia_device *link)
these things without consulting the CIS, and most client drivers
will only use the CIS to fill in implementation-defined details.
*/
- last_ret = pcmcia_loop_config(link, sedlbauer_config_check, req);
- if (last_ret)
+ ret = pcmcia_loop_config(link, sedlbauer_config_check, req);
+ if (ret)
goto failed;
/*
@@ -339,15 +317,20 @@ static int sedlbauer_config(struct pcmcia_device *link)
handler to the interrupt, unless the 'Handler' member of the
irq structure is initialized.
*/
- if (link->conf.Attributes & CONF_ENABLE_IRQ)
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ }
/*
This actually configures the PCMCIA socket -- setting up
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -380,19 +363,18 @@ static int sedlbauer_config(struct pcmcia_device *link)
icard.protocol = protocol;
icard.typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
- last_ret = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->stop), &icard);
- if (last_ret < 0) {
- printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n",
- last_ret, link->io.BasePort1);
+ ret = hisax_init_pcmcia(link,
+ &(((local_info_t *)link->priv)->stop), &icard);
+ if (ret < 0) {
+ printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n",
+ ret, link->io.BasePort1);
sedlbauer_release(link);
return -ENODEV;
} else
- ((local_info_t*)link->priv)->cardnr = last_ret;
+ ((local_info_t *)link->priv)->cardnr = ret;
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
sedlbauer_release(link);
return -ENODEV;
@@ -410,7 +392,7 @@ failed:
static void sedlbauer_release(struct pcmcia_device *link)
{
local_info_t *local = link->priv;
- DEBUG(0, "sedlbauer_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "sedlbauer_release(0x%p)\n", link);
if (local) {
if (local->cardnr >= 0) {
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
index 623d111544d..ea705394ce2 100644
--- a/drivers/isdn/hisax/teles_cs.c
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -38,23 +38,6 @@ MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
MODULE_LICENSE("GPL");
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
-static char *version =
-"teles_cs.c 2.10 2002/07/30 22:23:34 kkeil";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -133,7 +116,7 @@ static int teles_probe(struct pcmcia_device *link)
{
local_info_t *local;
- DEBUG(0, "teles_attach()\n");
+ dev_dbg(&link->dev, "teles_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
@@ -145,7 +128,6 @@ static int teles_probe(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
link->irq.Handler = NULL;
/*
@@ -178,7 +160,7 @@ static void teles_detach(struct pcmcia_device *link)
{
local_info_t *info = link->priv;
- DEBUG(0, "teles_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "teles_detach(0x%p)\n", link);
info->busy = 1;
teles_cs_release(link);
@@ -221,30 +203,25 @@ static int teles_cs_configcheck(struct pcmcia_device *p_dev,
static int teles_cs_config(struct pcmcia_device *link)
{
local_info_t *dev;
- int i, last_fn;
+ int i;
IsdnCard_t icard;
- DEBUG(0, "teles_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "teles_config(0x%p)\n", link);
dev = link->priv;
i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
- if (i != 0) {
- last_fn = RequestIO;
+ if (i != 0)
goto cs_failed;
- }
i = pcmcia_request_irq(link, &link->irq);
if (i != 0) {
link->irq.AssignedIRQ = 0;
- last_fn = RequestIRQ;
goto cs_failed;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- last_fn = RequestConfiguration;
+ if (i != 0)
goto cs_failed;
- }
/* At this point, the dev_node_t structure(s) should be
initialized and arranged in a linked list at link->dev. *//* */
@@ -283,7 +260,6 @@ static int teles_cs_config(struct pcmcia_device *link)
return 0;
cs_failed:
- cs_error(link, last_fn, i);
teles_cs_release(link);
return -ENODEV;
} /* teles_cs_config */
@@ -300,7 +276,7 @@ static void teles_cs_release(struct pcmcia_device *link)
{
local_info_t *local = link->priv;
- DEBUG(0, "teles_cs_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "teles_cs_release(0x%p)\n", link);
if (local) {
if (local->cardnr >= 0) {
diff --git a/drivers/isdn/hysdn/Kconfig b/drivers/isdn/hysdn/Kconfig
index c9e4231968e..e86bc6583d7 100644
--- a/drivers/isdn/hysdn/Kconfig
+++ b/drivers/isdn/hysdn/Kconfig
@@ -1,6 +1,3 @@
-#
-# Config.in for HYSDN ISDN driver
-#
config HYSDN
tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
depends on m && PROC_FS && PCI
@@ -15,4 +12,3 @@ config HYSDN_CAPI
depends on HYSDN && ISDN_CAPI
help
Say Y here if you like to use Hypercope's CAPI 2.0 interface.
-
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 90b56ed8651..507e13d9a57 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -1563,7 +1563,7 @@ isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
*(__be32 *)(p + 4) = cpu_to_be32(CISCO_SLARP_KEEPALIVE);
*(__be32 *)(p + 8) = cpu_to_be32(lp->cisco_myseq);
*(__be32 *)(p + 12) = cpu_to_be32(lp->cisco_yourseq);
- *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliablity, always 0xffff
+ *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliability, always 0xffff
p += 18;
isdn_net_write_super(lp, skb);
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index 642d5aaf53c..45df6675e8e 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -836,7 +836,7 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count)
unsigned short hl;
struct sk_buff *skb;
/*
- * we need to reserve enought space in front of
+ * we need to reserve enough space in front of
* sk_buff. old call to dev_alloc_skb only reserved
* 16 bytes, now we are looking what the driver want
*/
@@ -1326,7 +1326,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
struct sk_buff *new_skb;
unsigned short hl;
/*
- * we need to reserve enought space in front of
+ * we need to reserve enough space in front of
* sk_buff. old call to dev_alloc_skb only reserved
* 16 bytes, now we are looking what the driver want.
*/
@@ -1674,7 +1674,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
* - insert new fragment into the proper sequence slot (once that's done
* newfrag will be set to NULL)
* - reassemble any complete fragment sequence (non-null 'start'
- * indicates there is a continguous sequence present)
+ * indicates there is a contiguous sequence present)
* - discard any incomplete sequences that are below minseq -- due
* to the fact that sender always increment sequence number, if there
* is an incomplete sequence below minseq, no new fragments would
diff --git a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c
index 78f7660c1d0..4c41f191d4e 100644
--- a/drivers/isdn/i4l/isdn_ttyfax.c
+++ b/drivers/isdn/i4l/isdn_ttyfax.c
@@ -470,7 +470,7 @@ isdn_tty_cmd_FCLASS2(char **p, modem_info * info)
}
return 0;
}
- /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
+ /* BADMUL=value - dummy 0=disable errorchk disabled (threshold multiplier) */
if (!strncmp(p[0], "BADMUL", 6)) {
p[0] += 6;
switch (*p[0]) {
diff --git a/drivers/isdn/icn/Kconfig b/drivers/isdn/icn/Kconfig
index 89d15eed765..4534f21a185 100644
--- a/drivers/isdn/icn/Kconfig
+++ b/drivers/isdn/icn/Kconfig
@@ -1,6 +1,3 @@
-#
-# Config.in for ICN ISDN driver
-#
config ISDN_DRV_ICN
tristate "ICN 2B and 4B support"
depends on ISA
@@ -13,4 +10,3 @@ config ISDN_DRV_ICN
separately. See <file:Documentation/isdn/README> and
<file:Documentation/isdn/README.icn> for more
information.
-
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
index 77ee2867c8b..43ff4d3b046 100644
--- a/drivers/isdn/mISDN/dsp_core.c
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -110,7 +110,7 @@
* crossconnections and conferences via software if not possible through
* hardware. If hardware capability is available, hardware is used.
*
- * Echo: Is generated by CMX and is used to check performane of hard and
+ * Echo: Is generated by CMX and is used to check performance of hard and
* software CMX.
*
* The CMX has special functions for conferences with one, two and more
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index feb0fa45b66..fcfe17a19a6 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -779,7 +779,7 @@ base_sock_create(struct net *net, struct socket *sock, int protocol)
}
static int
-mISDN_sock_create(struct net *net, struct socket *sock, int proto)
+mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern)
{
int err = -EPROTONOSUPPORT;
@@ -808,8 +808,7 @@ mISDN_sock_create(struct net *net, struct socket *sock, int proto)
return err;
}
-static struct
-net_proto_family mISDN_sock_family_ops = {
+static const struct net_proto_family mISDN_sock_family_ops = {
.owner = THIS_MODULE,
.family = PF_ISDN,
.create = mISDN_sock_create,
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
index e04bad6c5ba..6d4da609588 100644
--- a/drivers/isdn/mISDN/tei.c
+++ b/drivers/isdn/mISDN/tei.c
@@ -725,7 +725,7 @@ tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg)
if (tm->rcnt == 1) {
if (*debug & DEBUG_L2_TEI)
tm->tei_m.printdebug(fi,
- "check req for tei %d sucessful\n", tm->l2->tei);
+ "check req for tei %d successful\n", tm->l2->tei);
mISDN_FsmChangeState(fi, ST_TEI_NOP);
} else if (tm->rcnt > 1) {
/* duplicate assignment; remove */
diff --git a/drivers/isdn/pcbit/Kconfig b/drivers/isdn/pcbit/Kconfig
index ffba6eca124..e9b2dd85d41 100644
--- a/drivers/isdn/pcbit/Kconfig
+++ b/drivers/isdn/pcbit/Kconfig
@@ -1,6 +1,3 @@
-#
-# Config.in for PCBIT ISDN driver
-#
config ISDN_DRV_PCBIT
tristate "PCBIT-D support"
depends on ISA && (BROKEN || X86)
@@ -11,4 +8,3 @@ config ISDN_DRV_PCBIT
the card using a utility which is distributed separately. See
<file:Documentation/isdn/README> and
<file:Documentation/isdn/README.pcbit> for more information.
-
diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig
index e6510ca7bf4..7469863a792 100644
--- a/drivers/isdn/sc/Kconfig
+++ b/drivers/isdn/sc/Kconfig
@@ -1,6 +1,3 @@
-#
-# Config.in for Spellcaster ISDN driver
-#
config ISDN_DRV_SC
tristate "Spellcaster support"
depends on ISA
@@ -9,4 +6,3 @@ config ISDN_DRV_SC
driver currently builds only in a modularized version.
To build it, choose M here: the module will be called sc.
See <file:Documentation/isdn/README.sc> for more information.
-
diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c
index 446050759b4..b9826032450 100644
--- a/drivers/leds/leds-ams-delta.c
+++ b/drivers/leds/leds-ams-delta.c
@@ -12,7 +12,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
-#include <mach/board-ams-delta.h>
+#include <plat/board-ams-delta.h>
/*
* Our context
diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c
index 5d91362e306..1f7c10f6b7f 100644
--- a/drivers/leds/leds-locomo.c
+++ b/drivers/leds/leds-locomo.c
@@ -44,7 +44,7 @@ static void locomoled_brightness_set1(struct led_classdev *led_cdev,
static struct led_classdev locomo_led0 = {
.name = "locomo:amber:charge",
- .default_trigger = "sharpsl-charge",
+ .default_trigger = "main-battery-charging",
.brightness_set = locomoled_brightness_set0,
};
diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c
index 6a822189325..a3d25da2f27 100644
--- a/drivers/macintosh/ans-lcd.c
+++ b/drivers/macintosh/ans-lcd.c
@@ -3,7 +3,6 @@
*/
#include <linux/types.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
@@ -26,6 +25,7 @@
static unsigned long anslcd_short_delay = 80;
static unsigned long anslcd_long_delay = 3280;
static volatile unsigned char __iomem *anslcd_ptr;
+static DEFINE_MUTEX(anslcd_mutex);
#undef DEBUG
@@ -65,26 +65,31 @@ anslcd_write( struct file * file, const char __user * buf,
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
+
+ mutex_lock(&anslcd_mutex);
for ( i = *ppos; count > 0; ++i, ++p, --count )
{
char c;
__get_user(c, p);
anslcd_write_byte_data( c );
}
+ mutex_unlock(&anslcd_mutex);
*ppos = i;
return p - buf;
}
-static int
-anslcd_ioctl( struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg )
+static long
+anslcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
char ch, __user *temp;
+ long ret = 0;
#ifdef DEBUG
printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
#endif
+ mutex_lock(&anslcd_mutex);
+
switch ( cmd )
{
case ANSLCD_CLEAR:
@@ -93,7 +98,7 @@ anslcd_ioctl( struct inode * inode, struct file * file,
anslcd_write_byte_ctrl ( 0x06 );
anslcd_write_byte_ctrl ( 0x01 );
anslcd_write_byte_ctrl ( 0x02 );
- return 0;
+ break;
case ANSLCD_SENDCTRL:
temp = (char __user *) arg;
__get_user(ch, temp);
@@ -101,33 +106,37 @@ anslcd_ioctl( struct inode * inode, struct file * file,
anslcd_write_byte_ctrl ( ch );
__get_user(ch, temp);
}
- return 0;
+ break;
case ANSLCD_SETSHORTDELAY:
if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- anslcd_short_delay=arg;
- return 0;
+ ret =-EACCES;
+ else
+ anslcd_short_delay=arg;
+ break;
case ANSLCD_SETLONGDELAY:
if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- anslcd_long_delay=arg;
- return 0;
+ ret = -EACCES;
+ else
+ anslcd_long_delay=arg;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
+
+ mutex_unlock(&anslcd_mutex);
+ return ret;
}
static int
anslcd_open( struct inode * inode, struct file * file )
{
- cycle_kernel_lock();
return 0;
}
const struct file_operations anslcd_fops = {
- .write = anslcd_write,
- .ioctl = anslcd_ioctl,
- .open = anslcd_open,
+ .write = anslcd_write,
+ .unlocked_ioctl = anslcd_ioctl,
+ .open = anslcd_open,
};
static struct miscdevice anslcd_dev = {
@@ -168,6 +177,7 @@ anslcd_init(void)
printk(KERN_DEBUG "LCD: init\n");
#endif
+ mutex_lock(&anslcd_mutex);
anslcd_write_byte_ctrl ( 0x38 );
anslcd_write_byte_ctrl ( 0x0c );
anslcd_write_byte_ctrl ( 0x06 );
@@ -176,6 +186,7 @@ anslcd_init(void)
for(a=0;a<80;a++) {
anslcd_write_byte_data(anslcd_logo[a]);
}
+ mutex_unlock(&anslcd_mutex);
return 0;
}
diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c
index cc9f27514ae..7b4ef5bb556 100644
--- a/drivers/macintosh/mac_hid.c
+++ b/drivers/macintosh/mac_hid.c
@@ -27,54 +27,49 @@ static int mouse_last_keycode;
/* file(s) in /proc/sys/dev/mac_hid */
static ctl_table mac_hid_files[] = {
{
- .ctl_name = DEV_MAC_HID_MOUSE_BUTTON_EMULATION,
.procname = "mouse_button_emulation",
.data = &mouse_emulate_buttons,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
- .ctl_name = DEV_MAC_HID_MOUSE_BUTTON2_KEYCODE,
.procname = "mouse_button2_keycode",
.data = &mouse_button2_keycode,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
- .ctl_name = DEV_MAC_HID_MOUSE_BUTTON3_KEYCODE,
.procname = "mouse_button3_keycode",
.data = &mouse_button3_keycode,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
- { .ctl_name = 0 }
+ { }
};
/* dir in /proc/sys/dev */
static ctl_table mac_hid_dir[] = {
{
- .ctl_name = DEV_MAC_HID,
.procname = "mac_hid",
.maxlen = 0,
.mode = 0555,
.child = mac_hid_files,
},
- { .ctl_name = 0 }
+ { }
};
/* /proc/sys/dev itself, in case that is not there yet */
static ctl_table mac_hid_root_dir[] = {
{
- .ctl_name = CTL_DEV,
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = mac_hid_dir,
},
- { .ctl_name = 0 }
+ { }
};
static struct ctl_table_header *mac_hid_sysctl_header;
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 8b9364434aa..3fbe41b0ac0 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -15,7 +15,7 @@
*
* WARNING: This driver has only been testen on Apple's
* 1.25 MHz Dual G4 (March 03). It is tuned for a CPU
- * temperatur around 57 C.
+ * temperature around 57 C.
*
* Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
*
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index ed103816401..e412980763b 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -988,7 +988,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad_cipher;
}
- /* Compatiblity mode for old dm-crypt cipher strings */
+ /* Compatibility mode for old dm-crypt cipher strings */
if (!chainmode || (strcmp(chainmode, "plain") == 0 && !ivmode)) {
chainmode = "cbc";
ivmode = "plain";
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 32d0b878ecc..dce971dbdfa 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1116,8 +1116,9 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
return limit_reached;
}
-static void pg_init_done(struct dm_path *path, int errors)
+static void pg_init_done(void *data, int errors)
{
+ struct dm_path *path = data;
struct pgpath *pgpath = path_to_pgpath(path);
struct priority_group *pg = pgpath->pg;
struct multipath *m = pg->m;
@@ -1183,12 +1184,11 @@ static void pg_init_done(struct dm_path *path, int errors)
static void activate_path(struct work_struct *work)
{
- int ret;
struct pgpath *pgpath =
container_of(work, struct pgpath, activate_path);
- ret = scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev));
- pg_init_done(&pgpath->path, ret);
+ scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
+ pg_init_done, &pgpath->path);
}
/*
diff --git a/drivers/md/md.c b/drivers/md/md.c
index b182f86a19d..5f154ef1e4b 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -98,44 +98,40 @@ static struct ctl_table_header *raid_table_header;
static ctl_table raid_table[] = {
{
- .ctl_name = DEV_RAID_SPEED_LIMIT_MIN,
.procname = "speed_limit_min",
.data = &sysctl_speed_limit_min,
.maxlen = sizeof(int),
.mode = S_IRUGO|S_IWUSR,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
- .ctl_name = DEV_RAID_SPEED_LIMIT_MAX,
.procname = "speed_limit_max",
.data = &sysctl_speed_limit_max,
.maxlen = sizeof(int),
.mode = S_IRUGO|S_IWUSR,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table raid_dir_table[] = {
{
- .ctl_name = DEV_RAID,
.procname = "raid",
.maxlen = 0,
.mode = S_IRUGO|S_IXUGO,
.child = raid_table,
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table raid_root_table[] = {
{
- .ctl_name = CTL_DEV,
.procname = "dev",
.maxlen = 0,
.mode = 0555,
.child = raid_dir_table,
},
- { .ctl_name = 0 }
+ { }
};
static const struct block_device_operations md_fops;
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
index 351b98b9b30..169b337b7c9 100644
--- a/drivers/media/common/Makefile
+++ b/drivers/media/common/Makefile
@@ -1,6 +1,6 @@
saa7146-objs := saa7146_i2c.o saa7146_core.o
saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
-ir-common-objs := ir-functions.o ir-keymaps.o
+ir-common-objs := ir-functions.o ir-keymaps.o ir-keytable.o
obj-y += tuners/
obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c
index abd4791acb0..e616f624cea 100644
--- a/drivers/media/common/ir-functions.c
+++ b/drivers/media/common/ir-functions.c
@@ -34,22 +34,19 @@ static int repeat = 1;
module_param(repeat, int, 0444);
MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)");
-static int debug; /* debug level (0,1,2) */
-module_param(debug, int, 0644);
-
-#define dprintk(level, fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG fmt , ## arg)
+int media_ir_debug; /* media_ir_debug level (0,1,2) */
+module_param_named(debug, media_ir_debug, int, 0644);
/* -------------------------------------------------------------------------- */
static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
{
if (KEY_RESERVED == ir->keycode) {
- printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n",
- dev->name,ir->ir_key,ir->ir_raw,ir->keypressed);
+ printk(KERN_INFO "%s: unknown key: key=0x%02x down=%d\n",
+ dev->name, ir->ir_key, ir->keypressed);
return;
}
- dprintk(1,"%s: key event code=%d down=%d\n",
+ IR_dprintk(1,"%s: key event code=%d down=%d\n",
dev->name,ir->keycode,ir->keypressed);
input_report_key(dev,ir->keycode,ir->keypressed);
input_sync(dev);
@@ -57,39 +54,34 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
/* -------------------------------------------------------------------------- */
-void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
+int ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
int ir_type, struct ir_scancode_table *ir_codes)
{
- int i;
-
ir->ir_type = ir_type;
- memset(ir->ir_codes, 0, sizeof(ir->ir_codes));
+ ir->keytable.size = ir_roundup_tablesize(ir_codes->size);
+ ir->keytable.scan = kzalloc(ir->keytable.size *
+ sizeof(struct ir_scancode), GFP_KERNEL);
+ if (!ir->keytable.scan)
+ return -ENOMEM;
- /*
- * FIXME: This is a temporary workaround to use the new IR tables
- * with the old approach. Later patches will replace this to a
- * proper method
- */
+ IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n",
+ ir->keytable.size,
+ ir->keytable.size * sizeof(ir->keytable.scan));
- if (ir_codes)
- for (i = 0; i < ir_codes->size; i++)
- if (ir_codes->scan[i].scancode < IR_KEYTAB_SIZE)
- ir->ir_codes[ir_codes->scan[i].scancode] = ir_codes->scan[i].keycode;
+ ir_copy_table(&ir->keytable, ir_codes);
+ ir_set_keycode_table(dev, &ir->keytable);
- dev->keycode = ir->ir_codes;
- dev->keycodesize = sizeof(IR_KEYTAB_TYPE);
- dev->keycodemax = IR_KEYTAB_SIZE;
- for (i = 0; i < IR_KEYTAB_SIZE; i++)
- set_bit(ir->ir_codes[i], dev->keybit);
clear_bit(0, dev->keybit);
-
set_bit(EV_KEY, dev->evbit);
if (repeat)
set_bit(EV_REP, dev->evbit);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(ir_input_init);
+
void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
{
if (ir->keypressed) {
@@ -100,9 +92,9 @@ void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
EXPORT_SYMBOL_GPL(ir_input_nokey);
void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
- u32 ir_key, u32 ir_raw)
+ u32 ir_key)
{
- u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key);
+ u32 keycode = ir_g_keycode_from_table(dev, ir_key);
if (ir->keypressed && ir->keycode != keycode) {
ir->keypressed = 0;
@@ -110,7 +102,6 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
}
if (!ir->keypressed) {
ir->ir_key = ir_key;
- ir->ir_raw = ir_raw;
ir->keycode = keycode;
ir->keypressed = 1;
ir_input_key_event(dev,ir);
@@ -275,7 +266,7 @@ EXPORT_SYMBOL_GPL(ir_decode_biphase);
* saa7134 */
/* decode raw bit pattern to RC5 code */
-static u32 ir_rc5_decode(unsigned int code)
+u32 ir_rc5_decode(unsigned int code)
{
unsigned int org_code = code;
unsigned int pair;
@@ -295,15 +286,16 @@ static u32 ir_rc5_decode(unsigned int code)
rc5 |= 1;
break;
case 3:
- dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code);
+ IR_dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code);
return 0;
}
}
- dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+ IR_dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
"instr=%x\n", rc5, org_code, RC5_START(rc5),
RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
return rc5;
}
+EXPORT_SYMBOL_GPL(ir_rc5_decode);
void ir_rc5_timer_end(unsigned long data)
{
@@ -330,20 +322,20 @@ void ir_rc5_timer_end(unsigned long data)
/* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
if (gap < 28000) {
- dprintk(1, "ir-common: spurious timer_end\n");
+ IR_dprintk(1, "ir-common: spurious timer_end\n");
return;
}
if (ir->last_bit < 20) {
/* ignore spurious codes (caused by light/other remotes) */
- dprintk(1, "ir-common: short code: %x\n", ir->code);
+ IR_dprintk(1, "ir-common: short code: %x\n", ir->code);
} else {
ir->code = (ir->code << ir->shift_by) | 1;
rc5 = ir_rc5_decode(ir->code);
/* two start bits? */
if (RC5_START(rc5) != ir->start) {
- dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5));
+ IR_dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5));
/* right address? */
} else if (RC5_ADDR(rc5) == ir->addr) {
@@ -353,11 +345,10 @@ void ir_rc5_timer_end(unsigned long data)
/* Good code, decide if repeat/repress */
if (toggle != RC5_TOGGLE(ir->last_rc5) ||
instr != RC5_INSTR(ir->last_rc5)) {
- dprintk(1, "ir-common: instruction %x, toggle %x\n", instr,
+ IR_dprintk(1, "ir-common: instruction %x, toggle %x\n", instr,
toggle);
ir_input_nokey(ir->dev, &ir->ir);
- ir_input_keydown(ir->dev, &ir->ir, instr,
- instr);
+ ir_input_keydown(ir->dev, &ir->ir, instr);
}
/* Set/reset key-up timer */
@@ -376,7 +367,7 @@ void ir_rc5_timer_keyup(unsigned long data)
{
struct card_ir *ir = (struct card_ir *)data;
- dprintk(1, "ir-common: key released\n");
+ IR_dprintk(1, "ir-common: key released\n");
ir_input_nokey(ir->dev, &ir->ir);
}
EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup);
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index f6790172736..328c973a083 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -1705,6 +1705,7 @@ static struct ir_scancode ir_codes_winfast[] = {
{ 0x37, KEY_RADIO }, /* FM */
{ 0x38, KEY_DVD },
+ { 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */
{ 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */
{ 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */
{ 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */
@@ -1846,6 +1847,76 @@ struct ir_scancode_table ir_codes_hauppauge_new_table = {
};
EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table);
+/*
+ * Hauppauge:the newer, gray remotes (seems there are multiple
+ * slightly different versions), shipped with cx88+ivtv cards.
+ *
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+static struct ir_scancode ir_codes_rc5_hauppauge_new[] = {
+ /* Keys 0 to 9 */
+ { 0x1e00, KEY_0 },
+ { 0x1e01, KEY_1 },
+ { 0x1e02, KEY_2 },
+ { 0x1e03, KEY_3 },
+ { 0x1e04, KEY_4 },
+ { 0x1e05, KEY_5 },
+ { 0x1e06, KEY_6 },
+ { 0x1e07, KEY_7 },
+ { 0x1e08, KEY_8 },
+ { 0x1e09, KEY_9 },
+
+ { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */
+ { 0x1e0b, KEY_RED }, /* red button */
+ { 0x1e0c, KEY_RADIO },
+ { 0x1e0d, KEY_MENU },
+ { 0x1e0e, KEY_SUBTITLE }, /* also the # key */
+ { 0x1e0f, KEY_MUTE },
+ { 0x1e10, KEY_VOLUMEUP },
+ { 0x1e11, KEY_VOLUMEDOWN },
+ { 0x1e12, KEY_PREVIOUS }, /* previous channel */
+ { 0x1e14, KEY_UP },
+ { 0x1e15, KEY_DOWN },
+ { 0x1e16, KEY_LEFT },
+ { 0x1e17, KEY_RIGHT },
+ { 0x1e18, KEY_VIDEO }, /* Videos */
+ { 0x1e19, KEY_AUDIO }, /* Music */
+ /* 0x1e1a: Pictures - presume this means
+ "Multimedia Home Platform" -
+ no "PICTURES" key in input.h
+ */
+ { 0x1e1a, KEY_MHP },
+
+ { 0x1e1b, KEY_EPG }, /* Guide */
+ { 0x1e1c, KEY_TV },
+ { 0x1e1e, KEY_NEXTSONG }, /* skip >| */
+ { 0x1e1f, KEY_EXIT }, /* back/exit */
+ { 0x1e20, KEY_CHANNELUP }, /* channel / program + */
+ { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */
+ { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */
+ { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */
+ { 0x1e25, KEY_ENTER }, /* OK */
+ { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */
+ { 0x1e29, KEY_BLUE }, /* blue key */
+ { 0x1e2e, KEY_GREEN }, /* green button */
+ { 0x1e30, KEY_PAUSE }, /* pause */
+ { 0x1e32, KEY_REWIND }, /* backward << */
+ { 0x1e34, KEY_FASTFORWARD }, /* forward >> */
+ { 0x1e35, KEY_PLAY },
+ { 0x1e36, KEY_STOP },
+ { 0x1e37, KEY_RECORD }, /* recording */
+ { 0x1e38, KEY_YELLOW }, /* yellow key */
+ { 0x1e3b, KEY_SELECT }, /* top right button */
+ { 0x1e3c, KEY_ZOOM }, /* full */
+ { 0x1e3d, KEY_POWER }, /* system power (green button) */
+};
+
+struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = {
+ .scan = ir_codes_rc5_hauppauge_new,
+ .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new),
+};
+EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table);
+
static struct ir_scancode ir_codes_npgtech[] = {
{ 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */
{ 0x2a, KEY_FRONT },
@@ -2964,6 +3035,101 @@ struct ir_scancode_table ir_codes_dm1105_nec_table = {
};
EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table);
+static struct ir_scancode ir_codes_tevii_nec[] = {
+ { 0x0a, KEY_POWER2},
+ { 0x0c, KEY_MUTE},
+ { 0x11, KEY_1},
+ { 0x12, KEY_2},
+ { 0x13, KEY_3},
+ { 0x14, KEY_4},
+ { 0x15, KEY_5},
+ { 0x16, KEY_6},
+ { 0x17, KEY_7},
+ { 0x18, KEY_8},
+ { 0x19, KEY_9},
+ { 0x10, KEY_0},
+ { 0x1c, KEY_MENU},
+ { 0x0f, KEY_VOLUMEDOWN},
+ { 0x1a, KEY_LAST},
+ { 0x0e, KEY_OPEN},
+ { 0x04, KEY_RECORD},
+ { 0x09, KEY_VOLUMEUP},
+ { 0x08, KEY_CHANNELUP},
+ { 0x07, KEY_PVR},
+ { 0x0b, KEY_TIME},
+ { 0x02, KEY_RIGHT},
+ { 0x03, KEY_LEFT},
+ { 0x00, KEY_UP},
+ { 0x1f, KEY_OK},
+ { 0x01, KEY_DOWN},
+ { 0x05, KEY_TUNER},
+ { 0x06, KEY_CHANNELDOWN},
+ { 0x40, KEY_PLAYPAUSE},
+ { 0x1e, KEY_REWIND},
+ { 0x1b, KEY_FAVORITES},
+ { 0x1d, KEY_BACK},
+ { 0x4d, KEY_FASTFORWARD},
+ { 0x44, KEY_EPG},
+ { 0x4c, KEY_INFO},
+ { 0x41, KEY_AB},
+ { 0x43, KEY_AUDIO},
+ { 0x45, KEY_SUBTITLE},
+ { 0x4a, KEY_LIST},
+ { 0x46, KEY_F1},
+ { 0x47, KEY_F2},
+ { 0x5e, KEY_F3},
+ { 0x5c, KEY_F4},
+ { 0x52, KEY_F5},
+ { 0x5a, KEY_F6},
+ { 0x56, KEY_MODE},
+ { 0x58, KEY_SWITCHVIDEOMODE},
+};
+struct ir_scancode_table ir_codes_tevii_nec_table = {
+ .scan = ir_codes_tevii_nec,
+ .size = ARRAY_SIZE(ir_codes_tevii_nec),
+};
+EXPORT_SYMBOL_GPL(ir_codes_tevii_nec_table);
+
+static struct ir_scancode ir_codes_tbs_nec[] = {
+ { 0x04, KEY_POWER2}, /*power*/
+ { 0x14, KEY_MUTE}, /*mute*/
+ { 0x07, KEY_1},
+ { 0x06, KEY_2},
+ { 0x05, KEY_3},
+ { 0x0b, KEY_4},
+ { 0x0a, KEY_5},
+ { 0x09, KEY_6},
+ { 0x0f, KEY_7},
+ { 0x0e, KEY_8},
+ { 0x0d, KEY_9},
+ { 0x12, KEY_0},
+ { 0x16, KEY_CHANNELUP}, /*ch+*/
+ { 0x11, KEY_CHANNELDOWN},/*ch-*/
+ { 0x13, KEY_VOLUMEUP}, /*vol+*/
+ { 0x0c, KEY_VOLUMEDOWN},/*vol-*/
+ { 0x03, KEY_RECORD}, /*rec*/
+ { 0x18, KEY_PAUSE}, /*pause*/
+ { 0x19, KEY_OK}, /*ok*/
+ { 0x1a, KEY_CAMERA}, /* snapshot */
+ { 0x01, KEY_UP},
+ { 0x10, KEY_LEFT},
+ { 0x02, KEY_RIGHT},
+ { 0x08, KEY_DOWN},
+ { 0x15, KEY_FAVORITES},
+ { 0x17, KEY_SUBTITLE},
+ { 0x1d, KEY_ZOOM},
+ { 0x1f, KEY_EXIT},
+ { 0x1e, KEY_MENU},
+ { 0x1c, KEY_EPG},
+ { 0x00, KEY_PREVIOUS},
+ { 0x1b, KEY_MODE},
+};
+struct ir_scancode_table ir_codes_tbs_nec_table = {
+ .scan = ir_codes_tbs_nec,
+ .size = ARRAY_SIZE(ir_codes_tbs_nec),
+};
+EXPORT_SYMBOL_GPL(ir_codes_tbs_nec_table);
+
/* Terratec Cinergy Hybrid T USB XS
Devin Heitmueller <dheitmueller@linuxtv.org>
*/
@@ -3147,3 +3313,4 @@ struct ir_scancode_table ir_codes_gadmei_rm008z_table = {
.size = ARRAY_SIZE(ir_codes_gadmei_rm008z),
};
EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table);
+
diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c
new file mode 100644
index 00000000000..26ce5bc2fdd
--- /dev/null
+++ b/drivers/media/common/ir-keytable.c
@@ -0,0 +1,429 @@
+/* ir-register.c - handle IR scancode->keycode tables
+ *
+ * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ */
+
+#include <linux/usb/input.h>
+
+#include <media/ir-common.h>
+
+#define IR_TAB_MIN_SIZE 32
+#define IR_TAB_MAX_SIZE 1024
+
+/**
+ * ir_seek_table() - returns the element order on the table
+ * @rc_tab: the ir_scancode_table with the keymap to be used
+ * @scancode: the scancode that we're seeking
+ *
+ * This routine is used by the input routines when a key is pressed at the
+ * IR. The scancode is received and needs to be converted into a keycode.
+ * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the
+ * corresponding keycode from the table.
+ */
+static int ir_seek_table(struct ir_scancode_table *rc_tab, u32 scancode)
+{
+ int rc;
+ unsigned long flags;
+ struct ir_scancode *keymap = rc_tab->scan;
+
+ spin_lock_irqsave(&rc_tab->lock, flags);
+
+ /* FIXME: replace it by a binary search */
+
+ for (rc = 0; rc < rc_tab->size; rc++)
+ if (keymap[rc].scancode == scancode)
+ goto exit;
+
+ /* Not found */
+ rc = -EINVAL;
+
+exit:
+ spin_unlock_irqrestore(&rc_tab->lock, flags);
+ return rc;
+}
+
+/**
+ * ir_roundup_tablesize() - gets an optimum value for the table size
+ * @n_elems: minimum number of entries to store keycodes
+ *
+ * This routine is used to choose the keycode table size.
+ *
+ * In order to have some empty space for new keycodes,
+ * and knowing in advance that kmalloc allocates only power of two
+ * segments, it optimizes the allocated space to have some spare space
+ * for those new keycodes by using the maximum number of entries that
+ * will be effectively be allocated by kmalloc.
+ * In order to reduce the quantity of table resizes, it has a minimum
+ * table size of IR_TAB_MIN_SIZE.
+ */
+int ir_roundup_tablesize(int n_elems)
+{
+ size_t size;
+
+ if (n_elems < IR_TAB_MIN_SIZE)
+ n_elems = IR_TAB_MIN_SIZE;
+
+ /*
+ * As kmalloc only allocates sizes of power of two, get as
+ * much entries as possible for the allocated memory segment
+ */
+ size = roundup_pow_of_two(n_elems * sizeof(struct ir_scancode));
+ n_elems = size / sizeof(struct ir_scancode);
+
+ return n_elems;
+}
+
+/**
+ * ir_copy_table() - copies a keytable, discarding the unused entries
+ * @destin: destin table
+ * @origin: origin table
+ *
+ * Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED
+ */
+
+int ir_copy_table(struct ir_scancode_table *destin,
+ const struct ir_scancode_table *origin)
+{
+ int i, j = 0;
+
+ for (i = 0; i < origin->size; i++) {
+ if (origin->scan[i].keycode == KEY_UNKNOWN ||
+ origin->scan[i].keycode == KEY_RESERVED)
+ continue;
+
+ memcpy(&destin->scan[j], &origin->scan[i], sizeof(struct ir_scancode));
+ j++;
+ }
+ destin->size = j;
+
+ IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size);
+
+ return 0;
+}
+
+/**
+ * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table
+ * @dev: the struct input_dev device descriptor
+ * @scancode: the desired scancode
+ * @keycode: the keycode to be retorned.
+ *
+ * This routine is used to handle evdev EVIOCGKEY ioctl.
+ * If the key is not found, returns -EINVAL, otherwise, returns 0.
+ */
+static int ir_getkeycode(struct input_dev *dev,
+ int scancode, int *keycode)
+{
+ int elem;
+ struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+
+ elem = ir_seek_table(rc_tab, scancode);
+ if (elem >= 0) {
+ *keycode = rc_tab->scan[elem].keycode;
+ return 0;
+ }
+
+ /*
+ * Scancode not found and table can't be expanded
+ */
+ if (elem < 0 && rc_tab->size == IR_TAB_MAX_SIZE)
+ return -EINVAL;
+
+ /*
+ * If is there extra space, returns KEY_RESERVED,
+ * otherwise, input core won't let ir_setkeycode to work
+ */
+ *keycode = KEY_RESERVED;
+ return 0;
+}
+
+
+/**
+ * ir_is_resize_needed() - Check if the table needs rezise
+ * @table: keycode table that may need to resize
+ * @n_elems: minimum number of entries to store keycodes
+ *
+ * Considering that kmalloc uses power of two storage areas, this
+ * routine detects if the real alloced size will change. If not, it
+ * just returns without doing nothing. Otherwise, it will extend or
+ * reduce the table size to meet the new needs.
+ *
+ * It returns 0 if no resize is needed, 1 otherwise.
+ */
+static int ir_is_resize_needed(struct ir_scancode_table *table, int n_elems)
+{
+ int cur_size = ir_roundup_tablesize(table->size);
+ int new_size = ir_roundup_tablesize(n_elems);
+
+ if (cur_size == new_size)
+ return 0;
+
+ /* Resize is needed */
+ return 1;
+}
+
+/**
+ * ir_delete_key() - remove a keycode from the table
+ * @rc_tab: keycode table
+ * @elem: element to be removed
+ *
+ */
+static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem)
+{
+ unsigned long flags = 0;
+ int newsize = rc_tab->size - 1;
+ int resize = ir_is_resize_needed(rc_tab, newsize);
+ struct ir_scancode *oldkeymap = rc_tab->scan;
+ struct ir_scancode *newkeymap;
+
+ if (resize) {
+ newkeymap = kzalloc(ir_roundup_tablesize(newsize) *
+ sizeof(*newkeymap), GFP_ATOMIC);
+
+ /* There's no memory for resize. Keep the old table */
+ if (!newkeymap)
+ resize = 0;
+ }
+
+ if (!resize) {
+ newkeymap = oldkeymap;
+
+ /* We'll modify the live table. Lock it */
+ spin_lock_irqsave(&rc_tab->lock, flags);
+ }
+
+ /*
+ * Copy the elements before the one that will be deleted
+ * if (!resize), both oldkeymap and newkeymap points
+ * to the same place, so, there's no need to copy
+ */
+ if (resize && elem > 0)
+ memcpy(newkeymap, oldkeymap,
+ elem * sizeof(*newkeymap));
+
+ /*
+ * Copy the other elements overwriting the element to be removed
+ * This operation applies to both resize and non-resize case
+ */
+ if (elem < newsize)
+ memcpy(&newkeymap[elem], &oldkeymap[elem + 1],
+ (newsize - elem) * sizeof(*newkeymap));
+
+ if (resize) {
+ /*
+ * As the copy happened to a temporary table, only here
+ * it needs to lock while replacing the table pointers
+ * to use the new table
+ */
+ spin_lock_irqsave(&rc_tab->lock, flags);
+ rc_tab->size = newsize;
+ rc_tab->scan = newkeymap;
+ spin_unlock_irqrestore(&rc_tab->lock, flags);
+
+ /* Frees the old keytable */
+ kfree(oldkeymap);
+ } else {
+ rc_tab->size = newsize;
+ spin_unlock_irqrestore(&rc_tab->lock, flags);
+ }
+}
+
+/**
+ * ir_insert_key() - insert a keycode at the table
+ * @rc_tab: keycode table
+ * @scancode: the desired scancode
+ * @keycode: the keycode to be retorned.
+ *
+ */
+static int ir_insert_key(struct ir_scancode_table *rc_tab,
+ int scancode, int keycode)
+{
+ unsigned long flags;
+ int elem = rc_tab->size;
+ int newsize = rc_tab->size + 1;
+ int resize = ir_is_resize_needed(rc_tab, newsize);
+ struct ir_scancode *oldkeymap = rc_tab->scan;
+ struct ir_scancode *newkeymap;
+
+ if (resize) {
+ newkeymap = kzalloc(ir_roundup_tablesize(newsize) *
+ sizeof(*newkeymap), GFP_ATOMIC);
+ if (!newkeymap)
+ return -ENOMEM;
+
+ memcpy(newkeymap, oldkeymap,
+ rc_tab->size * sizeof(*newkeymap));
+ } else
+ newkeymap = oldkeymap;
+
+ /* Stores the new code at the table */
+ IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n",
+ rc_tab->size, scancode, keycode);
+
+ spin_lock_irqsave(&rc_tab->lock, flags);
+ rc_tab->size = newsize;
+ if (resize) {
+ rc_tab->scan = newkeymap;
+ kfree(oldkeymap);
+ }
+ newkeymap[elem].scancode = scancode;
+ newkeymap[elem].keycode = keycode;
+ spin_unlock_irqrestore(&rc_tab->lock, flags);
+
+ return 0;
+}
+
+/**
+ * ir_setkeycode() - set a keycode at the evdev scancode ->keycode table
+ * @dev: the struct input_dev device descriptor
+ * @scancode: the desired scancode
+ * @keycode: the keycode to be retorned.
+ *
+ * This routine is used to handle evdev EVIOCSKEY ioctl.
+ * There's one caveat here: how can we increase the size of the table?
+ * If the key is not found, returns -EINVAL, otherwise, returns 0.
+ */
+static int ir_setkeycode(struct input_dev *dev,
+ int scancode, int keycode)
+{
+ int rc = 0;
+ struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_scancode *keymap = rc_tab->scan;
+ unsigned long flags;
+
+ /*
+ * Handle keycode table deletions
+ *
+ * If userspace is adding a KEY_UNKNOWN or KEY_RESERVED,
+ * deal as a trial to remove an existing scancode attribution
+ * if table become too big, reduce it to save space
+ */
+ if (keycode == KEY_UNKNOWN || keycode == KEY_RESERVED) {
+ rc = ir_seek_table(rc_tab, scancode);
+ if (rc < 0)
+ return 0;
+
+ IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", rc, scancode);
+ clear_bit(keymap[rc].keycode, dev->keybit);
+ ir_delete_key(rc_tab, rc);
+
+ return 0;
+ }
+
+ /*
+ * Handle keycode replacements
+ *
+ * If the scancode exists, just replace by the new value
+ */
+ rc = ir_seek_table(rc_tab, scancode);
+ if (rc >= 0) {
+ IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n",
+ rc, scancode, keycode);
+
+ clear_bit(keymap[rc].keycode, dev->keybit);
+
+ spin_lock_irqsave(&rc_tab->lock, flags);
+ keymap[rc].keycode = keycode;
+ spin_unlock_irqrestore(&rc_tab->lock, flags);
+
+ set_bit(keycode, dev->keybit);
+
+ return 0;
+ }
+
+ /*
+ * Handle new scancode inserts
+ *
+ * reallocate table if needed and insert a new keycode
+ */
+
+ /* Avoid growing the table indefinitely */
+ if (rc_tab->size + 1 > IR_TAB_MAX_SIZE)
+ return -EINVAL;
+
+ rc = ir_insert_key(rc_tab, scancode, keycode);
+ if (rc < 0)
+ return rc;
+ set_bit(keycode, dev->keybit);
+
+ return 0;
+}
+
+/**
+ * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode
+ * @input_dev: the struct input_dev descriptor of the device
+ * @scancode: the scancode that we're seeking
+ *
+ * This routine is used by the input routines when a key is pressed at the
+ * IR. The scancode is received and needs to be converted into a keycode.
+ * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the
+ * corresponding keycode from the table.
+ */
+u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode)
+{
+ struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+ struct ir_scancode *keymap = rc_tab->scan;
+ int elem;
+
+ elem = ir_seek_table(rc_tab, scancode);
+ if (elem >= 0) {
+ IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n",
+ dev->name, scancode, keymap[elem].keycode);
+
+ return rc_tab->scan[elem].keycode;
+ }
+
+ printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n",
+ dev->name, scancode);
+
+ /* Reports userspace that an unknown keycode were got */
+ return KEY_RESERVED;
+}
+
+/**
+ * ir_set_keycode_table() - sets the IR keycode table and add the handlers
+ * for keymap table get/set
+ * @input_dev: the struct input_dev descriptor of the device
+ * @rc_tab: the struct ir_scancode_table table of scancode/keymap
+ *
+ * This routine is used to initialize the input infrastructure to work with
+ * an IR.
+ * It should be called before registering the IR device.
+ */
+int ir_set_keycode_table(struct input_dev *input_dev,
+ struct ir_scancode_table *rc_tab)
+{
+ struct ir_scancode *keymap = rc_tab->scan;
+ int i;
+
+ spin_lock_init(&rc_tab->lock);
+
+ if (rc_tab->scan == NULL || !rc_tab->size)
+ return -EINVAL;
+
+ /* set the bits for the keys */
+ IR_dprintk(1, "key map size: %d\n", rc_tab->size);
+ for (i = 0; i < rc_tab->size; i++) {
+ IR_dprintk(1, "#%d: setting bit for keycode 0x%04x\n",
+ i, keymap[i].keycode);
+ set_bit(keymap[i].keycode, input_dev->keybit);
+ }
+
+ input_dev->getkeycode = ir_getkeycode;
+ input_dev->setkeycode = ir_setkeycode;
+ input_set_drvdata(input_dev, rc_tab);
+
+ return 0;
+}
+
+void ir_input_free(struct input_dev *dev)
+{
+ struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+
+ IR_dprintk(1, "Freed keycode table\n");
+
+ rc_tab->size = 0;
+ kfree(rc_tab->scan);
+ rc_tab->scan = NULL;
+}
+EXPORT_SYMBOL_GPL(ir_input_free);
+
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
index 7e8f5681599..48cb154c7a4 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146_i2c.c
@@ -98,7 +98,7 @@ static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op)
op_count++;
- /* loop throgh all bytes of message i */
+ /* loop through all bytes of message i */
for(j = 0; j < m[i].len; j++) {
/* write back all bytes that could have been read */
m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8));
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index 552dab442d7..becbaadb3b7 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -1205,6 +1205,13 @@ static int buffer_activate (struct saa7146_dev *dev,
return 0;
}
+static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+}
+
static int buffer_prepare(struct videobuf_queue *q,
struct videobuf_buffer *vb, enum v4l2_field field)
{
@@ -1257,16 +1264,12 @@ static int buffer_prepare(struct videobuf_queue *q,
sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
+ release_all_pagetables(dev, buf);
if( 0 != IS_PLANAR(sfmt->trans)) {
- saa7146_pgtable_free(dev->pci, &buf->pt[0]);
- saa7146_pgtable_free(dev->pci, &buf->pt[1]);
- saa7146_pgtable_free(dev->pci, &buf->pt[2]);
-
saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
} else {
- saa7146_pgtable_free(dev->pci, &buf->pt[0]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
}
@@ -1329,6 +1332,9 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_CAP(("vbuf:%p\n",vb));
+
+ release_all_pagetables(dev, buf);
+
saa7146_dma_free(dev,q,buf);
}
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index 607d319ce8e..409a4261e5b 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -172,4 +172,11 @@ config MEDIA_TUNER_MC44S803
help
Say Y here to support the Freescale MC44S803 based tuners
+config MEDIA_TUNER_MAX2165
+ tristate "Maxim MAX2165 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon tuner MAX2165 from Maxim.
+
endif # MEDIA_TUNER_CUSTOMISE
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index 4132b2be79e..a5438523f30 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
+obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c
new file mode 100644
index 00000000000..3d03640cf1f
--- /dev/null
+++ b/drivers/media/common/tuners/max2165.c
@@ -0,0 +1,442 @@
+/*
+ * Driver for Maxim MAX2165 silicon tuner
+ *
+ * Copyright (c) 2009 David T. L. Wong <davidtlwong@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "max2165.h"
+#include "max2165_priv.h"
+#include "tuner-i2c.h"
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "max2165: " args); \
+ } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
+
+ msg.addr = priv->config->i2c_address;
+
+ if (debug >= 2)
+ printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
+ __func__, reg, data);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n",
+ __func__, reg, data, ret);
+
+ return (ret != 1) ? -EIO : 0;
+}
+
+static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data)
+{
+ int ret;
+ u8 dev_addr = priv->config->i2c_address;
+
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 },
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret != 2) {
+ dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n",
+ __func__, reg, ret);
+ return -EIO;
+ }
+
+ *p_data = b1[0];
+ if (debug >= 2)
+ printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
+ __func__, reg, b1[0]);
+ return 0;
+}
+
+static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg,
+ u8 mask, u8 data)
+{
+ int ret;
+ u8 v;
+
+ data &= mask;
+ ret = max2165_read_reg(priv, reg, &v);
+ if (ret != 0)
+ return ret;
+ v &= ~mask;
+ v |= data;
+ ret = max2165_write_reg(priv, reg, v);
+
+ return ret;
+}
+
+static int max2165_read_rom_table(struct max2165_priv *priv)
+{
+ u8 dat[3];
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1);
+ max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]);
+ }
+
+ priv->tf_ntch_low_cfg = dat[0] >> 4;
+ priv->tf_ntch_hi_cfg = dat[0] & 0x0F;
+ priv->tf_balun_low_ref = dat[1] & 0x0F;
+ priv->tf_balun_hi_ref = dat[1] >> 4;
+ priv->bb_filter_7mhz_cfg = dat[2] & 0x0F;
+ priv->bb_filter_8mhz_cfg = dat[2] >> 4;
+
+ dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg);
+ dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg);
+ dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref);
+ dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref);
+ dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg);
+ dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg);
+
+ return 0;
+}
+
+static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/)
+{
+ u8 v;
+
+ v = (osc / 2);
+ if (v == 2)
+ v = 0x7;
+ else
+ v -= 8;
+
+ max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v);
+
+ return 0;
+}
+
+static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw)
+{
+ u8 val;
+
+ if (bw == BANDWIDTH_8_MHZ)
+ val = priv->bb_filter_8mhz_cfg;
+ else
+ val = priv->bb_filter_7mhz_cfg;
+
+ max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4);
+
+ return 0;
+}
+
+int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction)
+{
+ u32 remainder;
+ u32 q, f = 0;
+ int i;
+
+ if (0 == divisor)
+ return -1;
+
+ q = dividend / divisor;
+ remainder = dividend - q * divisor;
+
+ for (i = 0; i < 31; i++) {
+ remainder <<= 1;
+ if (remainder >= divisor) {
+ f += 1;
+ remainder -= divisor;
+ }
+ f <<= 1;
+ }
+
+ *quotient = q;
+ *fraction = f;
+
+ return 0;
+}
+
+static int max2165_set_rf(struct max2165_priv *priv, u32 freq)
+{
+ u8 tf;
+ u8 tf_ntch;
+ u32 t;
+ u32 quotient, fraction;
+
+ /* Set PLL divider according to RF frequency */
+ fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
+ &quotient, &fraction);
+
+ /* 20-bit fraction */
+ fraction >>= 12;
+
+ max2165_write_reg(priv, REG_NDIV_INT, quotient);
+ max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16);
+ max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8);
+ max2165_write_reg(priv, REG_NDIV_FRAC0, fraction);
+
+ /* Norch Filter */
+ tf_ntch = (freq < 725000000) ?
+ priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg;
+
+ /* Tracking filter balun */
+ t = priv->tf_balun_low_ref;
+ t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref)
+ * (freq / 1000 - 470000) / (780000 - 470000);
+
+ tf = t;
+ dprintk("tf = %X\n", tf);
+ tf |= tf_ntch << 4;
+
+ max2165_write_reg(priv, REG_TRACK_FILTER, tf);
+
+ return 0;
+}
+
+static void max2165_debug_status(struct max2165_priv *priv)
+{
+ u8 status, autotune;
+ u8 auto_vco_success, auto_vco_active;
+ u8 pll_locked;
+ u8 dc_offset_low, dc_offset_hi;
+ u8 signal_lv_over_threshold;
+ u8 vco, vco_sub_band, adc;
+
+ max2165_read_reg(priv, REG_STATUS, &status);
+ max2165_read_reg(priv, REG_AUTOTUNE, &autotune);
+
+ auto_vco_success = (status >> 6) & 0x01;
+ auto_vco_active = (status >> 5) & 0x01;
+ pll_locked = (status >> 4) & 0x01;
+ dc_offset_low = (status >> 3) & 0x01;
+ dc_offset_hi = (status >> 2) & 0x01;
+ signal_lv_over_threshold = status & 0x01;
+
+ vco = autotune >> 6;
+ vco_sub_band = (autotune >> 3) & 0x7;
+ adc = autotune & 0x7;
+
+ dprintk("auto VCO active: %d, auto VCO success: %d\n",
+ auto_vco_active, auto_vco_success);
+ dprintk("PLL locked: %d\n", pll_locked);
+ dprintk("DC offset low: %d, DC offset high: %d\n",
+ dc_offset_low, dc_offset_hi);
+ dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold);
+ dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc);
+}
+
+static int max2165_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct max2165_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk("%s() frequency=%d (Hz)\n", __func__, params->frequency);
+ if (fe->ops.info.type == FE_ATSC) {
+ return -EINVAL;
+ } else if (fe->ops.info.type == FE_OFDM) {
+ dprintk("%s() OFDM\n", __func__);
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ return -EINVAL;
+ case BANDWIDTH_7_MHZ:
+ case BANDWIDTH_8_MHZ:
+ priv->frequency = params->frequency;
+ priv->bandwidth = params->u.ofdm.bandwidth;
+ break;
+ default:
+ printk(KERN_ERR "MAX2165 bandwidth not set!\n");
+ return -EINVAL;
+ }
+ } else {
+ printk(KERN_ERR "MAX2165 modulation type not supported!\n");
+ return -EINVAL;
+ }
+
+ dprintk("%s() frequency=%d\n", __func__, priv->frequency);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ max2165_set_bandwidth(priv, priv->bandwidth);
+ ret = max2165_set_rf(priv, priv->frequency);
+ mdelay(50);
+ max2165_debug_status(priv);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (ret != 0)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+ struct max2165_priv *priv = fe->tuner_priv;
+ dprintk("%s()\n", __func__);
+ *freq = priv->frequency;
+ return 0;
+}
+
+static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+ struct max2165_priv *priv = fe->tuner_priv;
+ dprintk("%s()\n", __func__);
+
+ *bw = priv->bandwidth;
+ return 0;
+}
+
+static int max2165_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct max2165_priv *priv = fe->tuner_priv;
+ u16 lock_status = 0;
+
+ dprintk("%s()\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ max2165_debug_status(priv);
+ *status = lock_status;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int max2165_sleep(struct dvb_frontend *fe)
+{
+ dprintk("%s()\n", __func__);
+ return 0;
+}
+
+static int max2165_init(struct dvb_frontend *fe)
+{
+ struct max2165_priv *priv = fe->tuner_priv;
+ dprintk("%s()\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* Setup initial values */
+ /* Fractional Mode on */
+ max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18);
+ /* LNA on */
+ max2165_write_reg(priv, REG_LNA, 0x01);
+ max2165_write_reg(priv, REG_PLL_CFG, 0x7A);
+ max2165_write_reg(priv, REG_TEST, 0x08);
+ max2165_write_reg(priv, REG_SHUTDOWN, 0x40);
+ max2165_write_reg(priv, REG_VCO_CTRL, 0x84);
+ max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3);
+ max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75);
+ max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00);
+ max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00);
+
+ max2165_set_osc(priv, priv->config->osc_clk);
+
+ max2165_read_rom_table(priv);
+
+ max2165_set_bandwidth(priv, BANDWIDTH_8_MHZ);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int max2165_release(struct dvb_frontend *fe)
+{
+ struct max2165_priv *priv = fe->tuner_priv;
+ dprintk("%s()\n", __func__);
+
+ kfree(priv);
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops max2165_tuner_ops = {
+ .info = {
+ .name = "Maxim MAX2165",
+ .frequency_min = 470000000,
+ .frequency_max = 780000000,
+ .frequency_step = 50000,
+ },
+
+ .release = max2165_release,
+ .init = max2165_init,
+ .sleep = max2165_sleep,
+
+ .set_params = max2165_set_params,
+ .set_analog_params = NULL,
+ .get_frequency = max2165_get_frequency,
+ .get_bandwidth = max2165_get_bandwidth,
+ .get_status = max2165_get_status
+};
+
+struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct max2165_config *cfg)
+{
+ struct max2165_priv *priv = NULL;
+
+ dprintk("%s(%d-%04x)\n", __func__,
+ i2c ? i2c_adapter_id(i2c) : -1,
+ cfg ? cfg->i2c_address : -1);
+
+ priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ priv->config = cfg;
+ priv->i2c = i2c;
+ fe->tuner_priv = priv;
+
+ max2165_init(fe);
+ max2165_debug_status(priv);
+
+ return fe;
+}
+EXPORT_SYMBOL(max2165_attach);
+
+MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
+MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h
new file mode 100644
index 00000000000..c063c36a93d
--- /dev/null
+++ b/drivers/media/common/tuners/max2165.h
@@ -0,0 +1,48 @@
+/*
+ * Driver for Maxim MAX2165 silicon tuner
+ *
+ * Copyright (c) 2009 David T. L. Wong <davidtlwong@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MAX2165_H__
+#define __MAX2165_H__
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct max2165_config {
+ u8 i2c_address;
+ u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */
+};
+
+#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \
+ (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE))
+extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct max2165_config *cfg);
+#else
+static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct max2165_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h
new file mode 100644
index 00000000000..91bbe021a08
--- /dev/null
+++ b/drivers/media/common/tuners/max2165_priv.h
@@ -0,0 +1,60 @@
+/*
+ * Driver for Maxim MAX2165 silicon tuner
+ *
+ * Copyright (c) 2009 David T. L. Wong <davidtlwong@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MAX2165_PRIV_H__
+#define __MAX2165_PRIV_H__
+
+#define REG_NDIV_INT 0x00
+#define REG_NDIV_FRAC2 0x01
+#define REG_NDIV_FRAC1 0x02
+#define REG_NDIV_FRAC0 0x03
+#define REG_TRACK_FILTER 0x04
+#define REG_LNA 0x05
+#define REG_PLL_CFG 0x06
+#define REG_TEST 0x07
+#define REG_SHUTDOWN 0x08
+#define REG_VCO_CTRL 0x09
+#define REG_BASEBAND_CTRL 0x0A
+#define REG_DC_OFFSET_CTRL 0x0B
+#define REG_DC_OFFSET_DAC 0x0C
+#define REG_ROM_TABLE_ADDR 0x0D
+
+/* Read Only Registers */
+#define REG_ROM_TABLE_DATA 0x10
+#define REG_STATUS 0x11
+#define REG_AUTOTUNE 0x12
+
+struct max2165_priv {
+ struct max2165_config *config;
+ struct i2c_adapter *i2c;
+
+ u32 frequency;
+ u32 bandwidth;
+
+ u8 tf_ntch_low_cfg;
+ u8 tf_ntch_hi_cfg;
+ u8 tf_balun_low_ref;
+ u8 tf_balun_hi_ref;
+ u8 bb_filter_7mhz_cfg;
+ u8 bb_filter_8mhz_cfg;
+};
+
+#endif
diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
index 0803dab58ff..605e28b7326 100644
--- a/drivers/media/common/tuners/mxl5005s.c
+++ b/drivers/media/common/tuners/mxl5005s.c
@@ -2789,7 +2789,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
/* add for 2.6.5 Special setting for QAM */
if (state->Mod_Type == MXL_QAM) {
- if (state->RF_IN < 680000000)
+ if (state->config->qam_gain != 0)
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN,
+ state->config->qam_gain);
+ else if (state->RF_IN < 680000000)
status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
else
status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h
index 7ac6815b30a..fc8a1ffc53b 100644
--- a/drivers/media/common/tuners/mxl5005s.h
+++ b/drivers/media/common/tuners/mxl5005s.h
@@ -108,6 +108,10 @@ struct mxl5005s_config {
#define MXL_LOW_IF 1
u8 if_mode;
+ /* Some boards need to override the built-in logic for determining
+ the gain when in QAM mode (the HVR-1600 is one such case) */
+ u8 qam_gain;
+
/* Stuff I don't know what to do with */
u8 AgcMasterByte;
};
diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c
index 2d02698d4f4..7eb1bf75cd0 100644
--- a/drivers/media/common/tuners/mxl5007t.c
+++ b/drivers/media/common/tuners/mxl5007t.c
@@ -196,7 +196,7 @@ static void copy_reg_bits(struct reg_pair_t *reg_pair1,
i = j = 0;
while (reg_pair1[i].reg || reg_pair1[i].val) {
- while (reg_pair2[j].reg || reg_pair2[j].reg) {
+ while (reg_pair2[j].reg || reg_pair2[j].val) {
if (reg_pair1[i].reg != reg_pair2[j].reg) {
j++;
continue;
diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
index 155c93eb75d..e1f678281a5 100644
--- a/drivers/media/common/tuners/tda18271-common.c
+++ b/drivers/media/common/tuners/tda18271-common.c
@@ -326,12 +326,24 @@ int tda18271_init_regs(struct dvb_frontend *fe)
regs[R_EB22] = 0x48;
regs[R_EB23] = 0xb0;
- if (priv->small_i2c) {
+ switch (priv->small_i2c) {
+ case TDA18271_08_BYTE_CHUNK_INIT:
+ tda18271_write_regs(fe, 0x00, 0x08);
+ tda18271_write_regs(fe, 0x08, 0x08);
+ tda18271_write_regs(fe, 0x10, 0x08);
+ tda18271_write_regs(fe, 0x18, 0x08);
+ tda18271_write_regs(fe, 0x20, 0x07);
+ break;
+ case TDA18271_16_BYTE_CHUNK_INIT:
tda18271_write_regs(fe, 0x00, 0x10);
tda18271_write_regs(fe, 0x10, 0x10);
tda18271_write_regs(fe, 0x20, 0x07);
- } else
+ break;
+ case TDA18271_39_BYTE_CHUNK_INIT:
+ default:
tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
+ break;
+ }
/* setup agc1 gain */
regs[R_EB17] = 0x00;
diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
index 3a50ce96fcb..b2e15456d5f 100644
--- a/drivers/media/common/tuners/tda18271-fe.c
+++ b/drivers/media/common/tuners/tda18271-fe.c
@@ -256,8 +256,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
struct tda18271_priv *priv = fe->tuner_priv;
struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
unsigned char *regs = priv->tda18271_regs;
- int tm_current, rfcal_comp, approx, i, ret;
- u8 dc_over_dt, rf_tab;
+ int i, ret;
+ u8 tm_current, dc_over_dt, rf_tab;
+ s32 rfcal_comp, approx;
/* power up */
ret = tda18271_set_standby_mode(fe, 0, 0, 0);
@@ -277,11 +278,11 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
return i;
if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {
- approx = map[i].rf_a1 *
- (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab;
+ approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) +
+ map[i].rf_b1 + rf_tab;
} else {
- approx = map[i].rf_a2 *
- (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab;
+ approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) +
+ map[i].rf_b2 + rf_tab;
}
if (approx < 0)
@@ -292,9 +293,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
/* calculate temperature compensation */
- rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000;
+ rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000;
- regs[R_EB14] = approx + rfcal_comp;
+ regs[R_EB14] = (unsigned char)(approx + rfcal_comp);
ret = tda18271_write_regs(fe, R_EB14, 1);
fail:
return ret;
@@ -572,6 +573,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
unsigned char *regs = priv->tda18271_regs;
int bcal, rf, i;
+ s32 divisor, dividend;
#define RF1 0
#define RF2 1
#define RF3 2
@@ -610,20 +612,22 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
switch (rf) {
case RF1:
map[i].rf_a1 = 0;
- map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1];
+ map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]);
map[i].rf1 = rf_freq[RF1] / 1000;
break;
case RF2:
- map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] -
- prog_cal[RF1] + prog_tab[RF1]) /
- (s32)((rf_freq[RF2] - rf_freq[RF1]) / 1000);
+ dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) -
+ (s32)(prog_cal[RF1] + prog_tab[RF1]);
+ divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;
+ map[i].rf_a1 = (dividend / divisor);
map[i].rf2 = rf_freq[RF2] / 1000;
break;
case RF3:
- map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] -
- prog_cal[RF2] + prog_tab[RF2]) /
- (s32)((rf_freq[RF3] - rf_freq[RF2]) / 1000);
- map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2];
+ dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) -
+ (s32)(prog_cal[RF2] + prog_tab[RF2]);
+ divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;
+ map[i].rf_a2 = (dividend / divisor);
+ map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]);
map[i].rf3 = rf_freq[RF3] / 1000;
break;
default:
@@ -1181,6 +1185,48 @@ static int tda18271_get_id(struct dvb_frontend *fe)
return ret;
}
+static int tda18271_setup_configuration(struct dvb_frontend *fe,
+ struct tda18271_config *cfg)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
+ priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
+ priv->config = (cfg) ? cfg->config : 0;
+ priv->small_i2c = (cfg) ?
+ cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT;
+ priv->output_opt = (cfg) ?
+ cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
+
+ return 0;
+}
+
+static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg)
+{
+ /* tda18271_cal_on_startup == -1 when cal module option is unset */
+ return ((tda18271_cal_on_startup == -1) ?
+ /* honor configuration setting */
+ ((cfg) && (cfg->rf_cal_on_startup)) :
+ /* module option overrides configuration setting */
+ (tda18271_cal_on_startup)) ? 1 : 0;
+}
+
+static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg;
+
+ tda18271_setup_configuration(fe, cfg);
+
+ if (tda18271_need_cal_on_startup(cfg))
+ tda18271_init(fe);
+
+ /* override default std map with values in config struct */
+ if ((cfg) && (cfg->std_map))
+ tda18271_update_std_map(fe, cfg->std_map);
+
+ return 0;
+}
+
static struct dvb_tuner_ops tda18271_tuner_ops = {
.info = {
.name = "NXP TDA18271HD",
@@ -1193,6 +1239,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = {
.set_params = tda18271_set_params,
.set_analog_params = tda18271_set_analog_params,
.release = tda18271_release,
+ .set_config = tda18271_set_config,
.get_frequency = tda18271_get_frequency,
.get_bandwidth = tda18271_get_bandwidth,
};
@@ -1213,33 +1260,14 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
case 0:
goto fail;
case 1:
- {
/* new tuner instance */
- int rf_cal_on_startup;
-
- priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
- priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
- priv->config = (cfg) ? cfg->config : 0;
- priv->small_i2c = (cfg) ? cfg->small_i2c : 0;
- priv->output_opt = (cfg) ?
- cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
-
- /* tda18271_cal_on_startup == -1 when cal
- * module option is unset */
- if (tda18271_cal_on_startup == -1) {
- /* honor attach-time configuration */
- rf_cal_on_startup =
- ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0;
- } else {
- /* module option overrides attach configuration */
- rf_cal_on_startup = tda18271_cal_on_startup;
- }
+ fe->tuner_priv = priv;
+
+ tda18271_setup_configuration(fe, cfg);
priv->cal_initialized = false;
mutex_init(&priv->lock);
- fe->tuner_priv = priv;
-
if (tda_fail(tda18271_get_id(fe)))
goto fail;
@@ -1249,12 +1277,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
mutex_lock(&priv->lock);
tda18271_init_regs(fe);
- if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2))
+ if ((tda18271_need_cal_on_startup(cfg)) &&
+ (priv->id == TDA18271HDC2))
tda18271c2_rf_cal_init(fe);
mutex_unlock(&priv->lock);
break;
- }
default:
/* existing tuner instance */
fe->tuner_priv = priv;
@@ -1271,7 +1299,11 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
priv->small_i2c = cfg->small_i2c;
if (cfg->output_opt)
priv->output_opt = cfg->output_opt;
+ if (cfg->std_map)
+ tda18271_update_std_map(fe, cfg->std_map);
}
+ if (tda18271_need_cal_on_startup(cfg))
+ tda18271_init(fe);
break;
}
@@ -1298,7 +1330,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach);
MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.3");
+MODULE_VERSION("0.4");
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c
index e21fdeff3dd..e7f84c705da 100644
--- a/drivers/media/common/tuners/tda18271-maps.c
+++ b/drivers/media/common/tuners/tda18271-maps.c
@@ -978,6 +978,7 @@ static struct tda18271_cid_target_map tda18271_cid_target[] = {
int tda18271_lookup_cid_target(struct dvb_frontend *fe,
u32 *freq, u8 *cid_target, u16 *count_limit)
{
+ struct tda18271_priv *priv = fe->tuner_priv;
int i = 0;
while ((tda18271_cid_target[i].rfmax * 1000) < *freq) {
diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h
index 2bee229acd9..9589ab0576d 100644
--- a/drivers/media/common/tuners/tda18271-priv.h
+++ b/drivers/media/common/tuners/tda18271-priv.h
@@ -80,10 +80,10 @@ struct tda18271_rf_tracking_filter_cal {
u32 rf1;
u32 rf2;
u32 rf3;
- int rf_a1;
- int rf_b1;
- int rf_a2;
- int rf_b2;
+ s32 rf_a1;
+ s32 rf_b1;
+ s32 rf_a2;
+ s32 rf_b2;
};
enum tda18271_pll {
@@ -109,11 +109,12 @@ struct tda18271_priv {
enum tda18271_i2c_gate gate;
enum tda18271_ver id;
enum tda18271_output_options output_opt;
+ enum tda18271_small_i2c small_i2c;
unsigned int config; /* interface to saa713x / tda829x */
- unsigned int tm_rfcal;
unsigned int cal_initialized:1;
- unsigned int small_i2c:1;
+
+ u8 tm_rfcal;
struct tda18271_map_layout *maps;
struct tda18271_std_map std;
@@ -135,27 +136,37 @@ extern int tda18271_debug;
#define DBG_ADV 8
#define DBG_CAL 16
-#define tda_printk(kern, fmt, arg...) \
- printk(kern "%s: " fmt, __func__, ##arg)
-
-#define tda_dprintk(lvl, fmt, arg...) do {\
+#define tda_printk(st, kern, fmt, arg...) do {\
+ if (st) { \
+ struct tda18271_priv *state = st; \
+ printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \
+ i2c_adapter_id(state->i2c_props.adap), \
+ state->i2c_props.addr, \
+ (state->role == TDA18271_MASTER) \
+ ? "M" : "S", ##arg); \
+ } else \
+ printk(kern "%s: " fmt, __func__, ##arg); \
+} while (0)
+
+#define tda_dprintk(st, lvl, fmt, arg...) do {\
if (tda18271_debug & lvl) \
- tda_printk(KERN_DEBUG, fmt, ##arg); } while (0)
+ tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0)
#define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
-#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg)
-#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg)
-#define tda_dbg(fmt, arg...) tda_dprintk(DBG_INFO, fmt, ##arg)
-#define tda_map(fmt, arg...) tda_dprintk(DBG_MAP, fmt, ##arg)
-#define tda_reg(fmt, arg...) tda_dprintk(DBG_REG, fmt, ##arg)
-#define tda_cal(fmt, arg...) tda_dprintk(DBG_CAL, fmt, ##arg)
+#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg)
+#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg)
+#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg)
+#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg)
+#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg)
+#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg)
#define tda_fail(ret) \
({ \
int __ret; \
__ret = (ret < 0); \
if (__ret) \
- tda_printk(KERN_ERR, "error %d on line %d\n", ret, __LINE__);\
+ tda_printk(priv, KERN_ERR, \
+ "error %d on line %d\n", ret, __LINE__); \
__ret; \
})
diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h
index 323f2912128..d7fcc36dc6e 100644
--- a/drivers/media/common/tuners/tda18271.h
+++ b/drivers/media/common/tuners/tda18271.h
@@ -78,6 +78,12 @@ enum tda18271_output_options {
TDA18271_OUTPUT_XT_OFF = 2,
};
+enum tda18271_small_i2c {
+ TDA18271_39_BYTE_CHUNK_INIT = 0,
+ TDA18271_16_BYTE_CHUNK_INIT = 1,
+ TDA18271_08_BYTE_CHUNK_INIT = 2,
+};
+
struct tda18271_config {
/* override default if freq / std settings (optional) */
struct tda18271_std_map *std_map;
@@ -91,12 +97,12 @@ struct tda18271_config {
/* output options that can be disabled */
enum tda18271_output_options output_opt;
+ /* some i2c providers cant write all 39 registers at once */
+ enum tda18271_small_i2c small_i2c;
+
/* force rf tracking filter calibration on startup */
unsigned int rf_cal_on_startup:1;
- /* some i2c providers cant write all 39 registers at once */
- unsigned int small_i2c:1;
-
/* interface to saa713x / tda829x */
unsigned int config;
};
diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c
index 064d14c8d7b..c190b0dedee 100644
--- a/drivers/media/common/tuners/tda8290.c
+++ b/drivers/media/common/tuners/tda8290.c
@@ -33,6 +33,7 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable verbose debug messages");
static int deemphasis_50;
+module_param(deemphasis_50, int, 0644);
MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis");
/* ---------------------------------------------------------------------- */
diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c
index 544cdbe88a6..a71c100c95d 100644
--- a/drivers/media/common/tuners/tda9887.c
+++ b/drivers/media/common/tuners/tda9887.c
@@ -463,7 +463,7 @@ static int tda9887_set_insmod(struct dvb_frontend *fe)
buf[1] &= ~cQSS;
}
- if (adjust >= 0x00 && adjust < 0x20) {
+ if (adjust < 0x20) {
buf[2] &= ~cTopMask;
buf[2] |= adjust;
}
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index f4ffcdc9b84..432003dded7 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -61,6 +61,7 @@ struct xc5000_priv {
u32 bandwidth;
u8 video_standard;
u8 rf_mode;
+ u8 radio_input;
};
/* Misc Defines */
@@ -632,8 +633,12 @@ static int xc5000_set_params(struct dvb_frontend *fe,
struct xc5000_priv *priv = fe->tuner_priv;
int ret;
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS)
- xc_load_fw_and_init_tuner(fe);
+ if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+ if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+ dprintk(1, "Unable to load firmware and init tuner\n");
+ return -EINVAL;
+ }
+ }
dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency);
@@ -739,15 +744,12 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
return ret;
}
-static int xc5000_set_analog_params(struct dvb_frontend *fe,
+static int xc5000_set_tv_freq(struct dvb_frontend *fe,
struct analog_parameters *params)
{
struct xc5000_priv *priv = fe->tuner_priv;
int ret;
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS)
- xc_load_fw_and_init_tuner(fe);
-
dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
__func__, params->frequency);
@@ -827,6 +829,86 @@ tune_channel:
return 0;
}
+static int xc5000_set_radio_freq(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret = -EINVAL;
+ u8 radio_input;
+
+ dprintk(1, "%s() frequency=%d (in units of khz)\n",
+ __func__, params->frequency);
+
+ if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) {
+ dprintk(1, "%s() radio input not configured\n", __func__);
+ return -EINVAL;
+ }
+
+ if (priv->radio_input == XC5000_RADIO_FM1)
+ radio_input = FM_Radio_INPUT1;
+ else if (priv->radio_input == XC5000_RADIO_FM2)
+ radio_input = FM_Radio_INPUT2;
+ else {
+ dprintk(1, "%s() unknown radio input %d\n", __func__,
+ priv->radio_input);
+ return -EINVAL;
+ }
+
+ priv->freq_hz = params->frequency * 125 / 2;
+
+ priv->rf_mode = XC_RF_MODE_AIR;
+
+ ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode,
+ XC5000_Standard[radio_input].AudioMode);
+
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
+
+ return 0;
+}
+
+static int xc5000_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret = -EINVAL;
+
+ if (priv->i2c_props.adap == NULL)
+ return -EINVAL;
+
+ if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+ if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+ dprintk(1, "Unable to load firmware and init tuner\n");
+ return -EINVAL;
+ }
+ }
+
+ switch (params->mode) {
+ case V4L2_TUNER_RADIO:
+ ret = xc5000_set_radio_freq(fe, params);
+ break;
+ case V4L2_TUNER_ANALOG_TV:
+ case V4L2_TUNER_DIGITAL_TV:
+ ret = xc5000_set_tv_freq(fe, params);
+ break;
+ }
+
+ return ret;
+}
+
+
static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
{
struct xc5000_priv *priv = fe->tuner_priv;
@@ -1000,6 +1082,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
priv->if_khz = cfg->if_khz;
}
+ if (priv->radio_input == 0)
+ priv->radio_input = cfg->radio_input;
+
/* Check if firmware has been loaded. It is possible that another
instance of the driver has loaded the firmware.
*/
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
index f4c146698a0..e6d7236c9ea 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -30,11 +30,17 @@ struct i2c_adapter;
struct xc5000_config {
u8 i2c_address;
u32 if_khz;
+ u8 radio_input;
};
/* xc5000 callback command */
#define XC5000_TUNER_RESET 0
+/* Possible Radio inputs */
+#define XC5000_RADIO_NOT_CONFIGURED 0
+#define XC5000_RADIO_FM1 1
+#define XC5000_RADIO_FM2 2
+
/* For each bridge framework, when it attaches either analog or digital,
* it has to store a reference back to its _core equivalent structure,
* so that it can service the hardware by steering gpio's etc.
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index b1857c19bbd..78fc469f0f6 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -215,7 +215,7 @@ static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend
freq = 2150000; /* satellite IF is 950..2150MHz */
/* decide which VCO to use for the input frequency */
- for(i = 1; (i < ARRAY_SIZE(osci)) && (osci[i] < freq); i++);
+ for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++);
printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
band=bandsel[i];
/* the gain values must be set by SetSymbolrate */
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index 2d099e27175..53e3f2a7d31 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -510,7 +510,7 @@ static void dm1105_emit_key(struct work_struct *work)
data = (ircom >> 8) & 0x7f;
- ir_input_keydown(ir->input_dev, &ir->ir, data, data);
+ ir_input_keydown(ir->input_dev, &ir->ir, data);
ir_input_nokey(ir->input_dev, &ir->ir);
}
@@ -589,7 +589,12 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
"pci-%s/ir0", pci_name(dm1105->pdev));
- ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes);
+ if (err < 0) {
+ input_free_device(input_dev);
+ return err;
+ }
+
input_dev->name = "DVB on-card IR receiver";
input_dev->phys = dm1105->ir.input_phys;
input_dev->id.bustype = BUS_PCI;
@@ -608,6 +613,7 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
err = input_register_device(input_dev);
if (err) {
+ ir_input_free(input_dev);
input_free_device(input_dev);
return err;
}
@@ -617,8 +623,8 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
{
+ ir_input_free(dm1105->ir.input_dev);
input_unregister_device(dm1105->ir.input_dev);
-
}
static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb)
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 91c537bca8a..b78cfb7d189 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -30,6 +30,7 @@
#include <linux/string.h>
#include <linux/crc32.h>
#include <asm/uaccess.h>
+#include <asm/div64.h>
#include "dvb_demux.h"
@@ -44,6 +45,11 @@ module_param(dvb_demux_tscheck, int, 0644);
MODULE_PARM_DESC(dvb_demux_tscheck,
"enable transport stream continuity and TEI check");
+static int dvb_demux_speedcheck;
+module_param(dvb_demux_speedcheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_speedcheck,
+ "enable transport stream speed check");
+
#define dprintk_tscheck(x...) do { \
if (dvb_demux_tscheck && printk_ratelimit()) \
printk(x); \
@@ -387,6 +393,39 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
u16 pid = ts_pid(buf);
int dvr_done = 0;
+ if (dvb_demux_speedcheck) {
+ struct timespec cur_time, delta_time;
+ u64 speed_bytes, speed_timedelta;
+
+ demux->speed_pkts_cnt++;
+
+ /* show speed every SPEED_PKTS_INTERVAL packets */
+ if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
+ cur_time = current_kernel_time();
+
+ if (demux->speed_last_time.tv_sec != 0 &&
+ demux->speed_last_time.tv_nsec != 0) {
+ delta_time = timespec_sub(cur_time,
+ demux->speed_last_time);
+ speed_bytes = (u64)demux->speed_pkts_cnt
+ * 188 * 8;
+ /* convert to 1024 basis */
+ speed_bytes = 1000 * div64_u64(speed_bytes,
+ 1024);
+ speed_timedelta =
+ (u64)timespec_to_ns(&delta_time);
+ speed_timedelta = div64_u64(speed_timedelta,
+ 1000000); /* nsec -> usec */
+ printk(KERN_INFO "TS speed %llu Kbits/sec \n",
+ div64_u64(speed_bytes,
+ speed_timedelta));
+ };
+
+ demux->speed_last_time = cur_time;
+ demux->speed_pkts_cnt = 0;
+ };
+ };
+
if (dvb_demux_tscheck) {
if (!demux->cnt_storage)
demux->cnt_storage = vmalloc(MAX_PID + 1);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 2fe05d03240..a7d876fd02d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -44,6 +44,8 @@
#define MAX_PID 0x1fff
+#define SPEED_PKTS_INTERVAL 50000
+
struct dvb_demux_filter {
struct dmx_section_filter filter;
u8 maskandmode[DMX_MAX_FILTER_SIZE];
@@ -131,6 +133,9 @@ struct dvb_demux {
spinlock_t lock;
uint8_t *cnt_storage; /* for TS continuity check */
+
+ struct timespec speed_last_time; /* for TS speed check */
+ uint32_t speed_pkts_cnt; /* for TS speed check */
};
int dvb_dmx_init(struct dvb_demux *dvbdemux);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 98082416aa5..07461222a7f 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -895,104 +895,27 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
}
static struct dtv_cmds_h dtv_cmds[] = {
- [DTV_TUNE] = {
- .name = "DTV_TUNE",
- .cmd = DTV_TUNE,
- .set = 1,
- },
- [DTV_CLEAR] = {
- .name = "DTV_CLEAR",
- .cmd = DTV_CLEAR,
- .set = 1,
- },
+ _DTV_CMD(DTV_TUNE, 1, 0),
+ _DTV_CMD(DTV_CLEAR, 1, 0),
/* Set */
- [DTV_FREQUENCY] = {
- .name = "DTV_FREQUENCY",
- .cmd = DTV_FREQUENCY,
- .set = 1,
- },
- [DTV_BANDWIDTH_HZ] = {
- .name = "DTV_BANDWIDTH_HZ",
- .cmd = DTV_BANDWIDTH_HZ,
- .set = 1,
- },
- [DTV_MODULATION] = {
- .name = "DTV_MODULATION",
- .cmd = DTV_MODULATION,
- .set = 1,
- },
- [DTV_INVERSION] = {
- .name = "DTV_INVERSION",
- .cmd = DTV_INVERSION,
- .set = 1,
- },
- [DTV_DISEQC_MASTER] = {
- .name = "DTV_DISEQC_MASTER",
- .cmd = DTV_DISEQC_MASTER,
- .set = 1,
- .buffer = 1,
- },
- [DTV_SYMBOL_RATE] = {
- .name = "DTV_SYMBOL_RATE",
- .cmd = DTV_SYMBOL_RATE,
- .set = 1,
- },
- [DTV_INNER_FEC] = {
- .name = "DTV_INNER_FEC",
- .cmd = DTV_INNER_FEC,
- .set = 1,
- },
- [DTV_VOLTAGE] = {
- .name = "DTV_VOLTAGE",
- .cmd = DTV_VOLTAGE,
- .set = 1,
- },
- [DTV_TONE] = {
- .name = "DTV_TONE",
- .cmd = DTV_TONE,
- .set = 1,
- },
- [DTV_PILOT] = {
- .name = "DTV_PILOT",
- .cmd = DTV_PILOT,
- .set = 1,
- },
- [DTV_ROLLOFF] = {
- .name = "DTV_ROLLOFF",
- .cmd = DTV_ROLLOFF,
- .set = 1,
- },
- [DTV_DELIVERY_SYSTEM] = {
- .name = "DTV_DELIVERY_SYSTEM",
- .cmd = DTV_DELIVERY_SYSTEM,
- .set = 1,
- },
- [DTV_HIERARCHY] = {
- .name = "DTV_HIERARCHY",
- .cmd = DTV_HIERARCHY,
- .set = 1,
- },
- [DTV_CODE_RATE_HP] = {
- .name = "DTV_CODE_RATE_HP",
- .cmd = DTV_CODE_RATE_HP,
- .set = 1,
- },
- [DTV_CODE_RATE_LP] = {
- .name = "DTV_CODE_RATE_LP",
- .cmd = DTV_CODE_RATE_LP,
- .set = 1,
- },
- [DTV_GUARD_INTERVAL] = {
- .name = "DTV_GUARD_INTERVAL",
- .cmd = DTV_GUARD_INTERVAL,
- .set = 1,
- },
- [DTV_TRANSMISSION_MODE] = {
- .name = "DTV_TRANSMISSION_MODE",
- .cmd = DTV_TRANSMISSION_MODE,
- .set = 1,
- },
+ _DTV_CMD(DTV_FREQUENCY, 1, 0),
+ _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
+ _DTV_CMD(DTV_MODULATION, 1, 0),
+ _DTV_CMD(DTV_INVERSION, 1, 0),
+ _DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
+ _DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
+ _DTV_CMD(DTV_INNER_FEC, 1, 0),
+ _DTV_CMD(DTV_VOLTAGE, 1, 0),
+ _DTV_CMD(DTV_TONE, 1, 0),
+ _DTV_CMD(DTV_PILOT, 1, 0),
+ _DTV_CMD(DTV_ROLLOFF, 1, 0),
+ _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0),
+ _DTV_CMD(DTV_HIERARCHY, 1, 0),
+ _DTV_CMD(DTV_CODE_RATE_HP, 1, 0),
+ _DTV_CMD(DTV_CODE_RATE_LP, 1, 0),
+ _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0),
+ _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0),
_DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
_DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
@@ -1035,43 +958,13 @@ static struct dtv_cmds_h dtv_cmds[] = {
_DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
/* Get */
- [DTV_DISEQC_SLAVE_REPLY] = {
- .name = "DTV_DISEQC_SLAVE_REPLY",
- .cmd = DTV_DISEQC_SLAVE_REPLY,
- .set = 0,
- .buffer = 1,
- },
-
- [DTV_API_VERSION] = {
- .name = "DTV_API_VERSION",
- .cmd = DTV_API_VERSION,
- .set = 0,
- },
- [DTV_CODE_RATE_HP] = {
- .name = "DTV_CODE_RATE_HP",
- .cmd = DTV_CODE_RATE_HP,
- .set = 0,
- },
- [DTV_CODE_RATE_LP] = {
- .name = "DTV_CODE_RATE_LP",
- .cmd = DTV_CODE_RATE_LP,
- .set = 0,
- },
- [DTV_GUARD_INTERVAL] = {
- .name = "DTV_GUARD_INTERVAL",
- .cmd = DTV_GUARD_INTERVAL,
- .set = 0,
- },
- [DTV_TRANSMISSION_MODE] = {
- .name = "DTV_TRANSMISSION_MODE",
- .cmd = DTV_TRANSMISSION_MODE,
- .set = 0,
- },
- [DTV_HIERARCHY] = {
- .name = "DTV_HIERARCHY",
- .cmd = DTV_HIERARCHY,
- .set = 0,
- },
+ _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),
};
static void dtv_property_dump(struct dtv_property *tvp)
@@ -1712,7 +1605,18 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
struct dvb_device *dvbdev = file->private_data;
struct dvb_frontend *fe = dvbdev->priv;
struct dvb_frontend_private *fepriv = fe->frontend_priv;
- int err = -EOPNOTSUPP;
+ int cb_err, err = -EOPNOTSUPP;
+
+ if (fe->dvb->fe_ioctl_override) {
+ cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
+ DVB_FE_IOCTL_PRE);
+ if (cb_err < 0)
+ return cb_err;
+ if (cb_err > 0)
+ return 0;
+ /* fe_ioctl_override returning 0 allows
+ * dvb-core to continue handling the ioctl */
+ }
switch (cmd) {
case FE_GET_INFO: {
@@ -1978,6 +1882,13 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
break;
};
+ if (fe->dvb->fe_ioctl_override) {
+ cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
+ DVB_FE_IOCTL_POST);
+ if (cb_err < 0)
+ return cb_err;
+ }
+
return err;
}
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index 810f07d6324..52e4ce4304e 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -160,7 +160,7 @@ struct tuner_state {
* search callback possible return status
*
* DVBFE_ALGO_SEARCH_SUCCESS
- * The frontend search algorithm completed and returned succesfully
+ * The frontend search algorithm completed and returned successfully
*
* DVBFE_ALGO_SEARCH_ASLEEP
* The frontend search algorithm is sleeping
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
index 0241a7c5c34..8b8558fcb04 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -1396,7 +1396,7 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
return ret;
}
- /* binary compatiblity cruft */
+ /* binary compatibility cruft */
case __NET_ADD_IF_OLD:
{
struct __dvb_net_if_old *dvbnetif = parg;
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 01fc7048474..f7b499d4a3c 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -54,6 +54,8 @@
module_param_array(adapter_nr, short, NULL, 0444); \
MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers")
+struct dvb_frontend;
+
struct dvb_adapter {
int num;
struct list_head list_head;
@@ -69,6 +71,32 @@ struct dvb_adapter {
int mfe_shared; /* indicates mutually exclusive frontends */
struct dvb_device *mfe_dvbdev; /* frontend device in use */
struct mutex mfe_lock; /* access lock for thread creation */
+
+ /* Allow the adapter/bridge driver to perform an action before and/or
+ * after the core handles an ioctl:
+ *
+ * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled.
+ * DVB_FE_IOCTL_POST indicates that the ioctl has been handled.
+ *
+ * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg:
+ *
+ * return 0 to allow dvb-core to handle the ioctl.
+ * return a positive int to prevent dvb-core from handling the ioctl,
+ * and exit without error.
+ * return a negative int to prevent dvb-core from handling the ioctl,
+ * and return that value as an error.
+ *
+ * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg:
+ *
+ * return 0 to allow the dvb_frontend ioctl handler to exit normally.
+ * return a negative int to cause the dvb_frontend ioctl handler to
+ * return that value as an error.
+ */
+#define DVB_FE_IOCTL_PRE 0
+#define DVB_FE_IOCTL_POST 1
+ int (*fe_ioctl_override)(struct dvb_frontend *fe,
+ unsigned int cmd, void *parg,
+ unsigned int stage);
};
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 0e4b97fba38..2dee1bf7357 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -322,3 +322,11 @@ config DVB_USB_FRIIO
depends on DVB_USB
help
Say Y here to support the Japanese DTV receiver Friio.
+
+config DVB_USB_EC168
+ tristate "E3C EC168 DVB-T USB2.0 support"
+ depends on DVB_USB && EXPERIMENTAL
+ select DVB_EC100
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 85b83a43d55..72c92cb69a2 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -82,6 +82,9 @@ obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
dvb-usb-friio-objs = friio.o friio-fe.o
obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
+dvb-usb-ec168-objs = ec168.o
+obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
+
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index cf042b309b4..8b60a601fb8 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -730,7 +730,7 @@ static int af9015_read_config(struct usb_device *udev)
goto error;
deb_info("%s: IR mode:%d\n", __func__, val);
for (i = 0; i < af9015_properties_count; i++) {
- if (val == AF9015_IR_MODE_DISABLED || val == 0x04) {
+ if (val == AF9015_IR_MODE_DISABLED) {
af9015_properties[i].rc_key_map = NULL;
af9015_properties[i].rc_key_map_size = 0;
} else if (dvb_usb_af9015_remote) {
@@ -868,6 +868,16 @@ static int af9015_read_config(struct usb_device *udev)
af9015_config.ir_table_size =
ARRAY_SIZE(af9015_ir_table_avermedia);
break;
+ case USB_VID_MSI_2:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_msi_digivox_iii;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii);
+ af9015_config.ir_table =
+ af9015_ir_table_msi_digivox_iii;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_msi_digivox_iii);
+ break;
}
}
}
@@ -1283,6 +1293,8 @@ static struct usb_device_id af9015_usb_table[] = {
{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)},
{USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)},
/* 25 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1296,7 +1308,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.firmware = "dvb-usb-af9015.fw",
.no_reconnect = 1,
- .size_of_priv = sizeof(struct af9015_state), \
+ .size_of_priv = sizeof(struct af9015_state),
.num_adapters = 2,
.adapter = {
@@ -1402,7 +1414,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.firmware = "dvb-usb-af9015.fw",
.no_reconnect = 1,
- .size_of_priv = sizeof(struct af9015_state), \
+ .size_of_priv = sizeof(struct af9015_state),
.num_adapters = 2,
.adapter = {
@@ -1508,7 +1520,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.firmware = "dvb-usb-af9015.fw",
.no_reconnect = 1,
- .size_of_priv = sizeof(struct af9015_state), \
+ .size_of_priv = sizeof(struct af9015_state),
.num_adapters = 2,
.adapter = {
@@ -1554,7 +1566,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.i2c_algo = &af9015_i2c_algo,
- .num_device_descs = 4, /* max 9 */
+ .num_device_descs = 6, /* max 9 */
.devices = {
{
.name = "AverMedia AVerTV Volar GPS 805 (A805)",
@@ -1577,6 +1589,17 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.cold_ids = {&af9015_usb_table[24], NULL},
.warm_ids = {NULL},
},
+ {
+ .name = "KWorld PlusTV DVB-T PCI Pro Card " \
+ "(DVB-T PC160-T)",
+ .cold_ids = {&af9015_usb_table[26], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Sveon STV20 Tuner USB DVB-T HDTV",
+ .cold_ids = {&af9015_usb_table[27], NULL},
+ .warm_ids = {NULL},
+ },
}
},
};
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index c41f30e4a1b..931c8515830 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -95,6 +95,7 @@ enum af9015_ir_mode {
AF9015_IR_MODE_HID,
AF9015_IR_MODE_RLC,
AF9015_IR_MODE_RC6,
+ AF9015_IR_MODE_POLLING, /* just guess */
};
struct af9015_state {
@@ -119,6 +120,7 @@ enum af9015_remote {
/* 5 */ AF9015_REMOTE_AVERMEDIA_KS,
};
+/* LeadTek - Y04G0051 */
/* Leadtek WinFast DTV Dongle Gold */
static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
{ 0x001e, KEY_1 },
@@ -131,64 +133,96 @@ static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
{ 0x0025, KEY_8 },
{ 0x0026, KEY_9 },
{ 0x0027, KEY_0 },
- { 0x0028, KEY_ENTER },
- { 0x004f, KEY_VOLUMEUP },
- { 0x0050, KEY_VOLUMEDOWN },
- { 0x0051, KEY_CHANNELDOWN },
- { 0x0052, KEY_CHANNELUP },
+ { 0x0028, KEY_OK },
+ { 0x004f, KEY_RIGHT },
+ { 0x0050, KEY_LEFT },
+ { 0x0051, KEY_DOWN },
+ { 0x0052, KEY_UP },
+ { 0x011a, KEY_POWER2 },
+ { 0x04b4, KEY_TV },
+ { 0x04b3, KEY_RED },
+ { 0x04b2, KEY_GREEN },
+ { 0x04b1, KEY_YELLOW },
+ { 0x04b0, KEY_BLUE },
+ { 0x003d, KEY_TEXT },
+ { 0x0113, KEY_SLEEP },
+ { 0x0010, KEY_MUTE },
+ { 0x0105, KEY_ESC },
+ { 0x0009, KEY_SCREEN },
+ { 0x010f, KEY_MENU },
+ { 0x003f, KEY_CHANNEL },
+ { 0x0013, KEY_REWIND },
+ { 0x0012, KEY_PLAY },
+ { 0x0011, KEY_FASTFORWARD },
+ { 0x0005, KEY_PREVIOUS },
+ { 0x0029, KEY_STOP },
+ { 0x002b, KEY_NEXT },
+ { 0x0041, KEY_EPG },
+ { 0x0019, KEY_VIDEO },
+ { 0x0016, KEY_AUDIO },
+ { 0x0037, KEY_DOT },
+ { 0x002a, KEY_AGAIN },
+ { 0x002c, KEY_CAMERA },
+ { 0x003c, KEY_NEW },
+ { 0x0115, KEY_RECORD },
+ { 0x010b, KEY_TIME },
+ { 0x0043, KEY_VOLUMEUP },
+ { 0x0042, KEY_VOLUMEDOWN },
+ { 0x004b, KEY_CHANNELUP },
+ { 0x004e, KEY_CHANNELDOWN },
};
static u8 af9015_ir_table_leadtek[] = {
- 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00,
- 0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00,
- 0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00,
- 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00,
- 0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00,
- 0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00,
- 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00,
- 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00,
- 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00,
- 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00,
- 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00,
- 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00,
- 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00,
- 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00,
- 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00,
- 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00,
- 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00,
- 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00,
- 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00,
- 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00,
- 0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00,
- 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00,
- 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00,
- 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00,
- 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00,
- 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00,
- 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00,
- 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00,
- 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00,
- 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00,
- 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00,
- 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00,
- 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00,
- 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00,
- 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00,
- 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00,
- 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00,
- 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00,
- 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00,
- 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00,
- 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00,
- 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00,
- 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00,
- 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00,
- 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00,
- 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00,
- 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00,
- 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00,
- 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00,
- 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00,
+ 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, /* KEY_POWER2 */
+ 0x03, 0xfc, 0x56, 0xa9, 0xb4, 0x04, 0x00, /* KEY_TV */
+ 0x03, 0xfc, 0x4b, 0xb4, 0xb3, 0x04, 0x00, /* KEY_RED */
+ 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, /* KEY_GREEN */
+ 0x03, 0xfc, 0x4d, 0xb2, 0xb1, 0x04, 0x00, /* KEY_YELLOW */
+ 0x03, 0xfc, 0x4e, 0xb1, 0xb0, 0x04, 0x00, /* KEY_BLUE */
+ 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, /* KEY_TEXT */
+ 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, /* KEY_SLEEP */
+ 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, /* KEY_MUTE */
+ 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, /* KEY_ESC */
+ 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, /* KEY_STOP (1)*/
+ 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, /* KEY_UP */
+ 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, /* KEY_SCREEN */
+ 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, /* KEY_LEFT */
+ 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, /* KEY_OK (1) */
+ 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, /* KEY_RIGHT */
+ 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, /* KEY_MENU */
+ 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, /* KEY_DOWN */
+ 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, /* KEY_CHANNEL */
+ 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, /* KEY_REWIND */
+ 0x03, 0xfc, 0x43, 0xbc, 0x12, 0x00, 0x00, /* KEY_PLAY */
+ 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, /* KEY_FASTFORWARD */
+ 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, /* KEY_VIDEO (1) */
+ 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, /* KEY_PREVIOUS */
+ 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, /* KEY_STOP (2) */
+ 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, /* KEY_NEXT */
+ 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, /* KEY_EPG */
+ 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, /* KEY_1 */
+ 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, /* KEY_2 */
+ 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, /* KEY_3 */
+ 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, /* KEY_VIDEO (2) */
+ 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, /* KEY_4 */
+ 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, /* KEY_5 */
+ 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, /* KEY_6 */
+ 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, /* KEY_AUDIO */
+ 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, /* KEY_7 */
+ 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, /* KEY_8 */
+ 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, /* KEY_9 */
+ 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* KEY_OK (2) */
+ 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, /* KEY_DOT */
+ 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, /* KEY_0 */
+ 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, /* KEY_AGAIN */
+ 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, /* KEY_CAMERA */
+ 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, /* KEY_NEW */
+ 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, /* KEY_RECORD */
+ 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, /* KEY_TIME */
+ 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, /* KEY_VOLUMEUP */
+ 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, /* KEY_VOLUMEDOWN */
+ 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, /* KEY_CHANNELUP */
+ 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, /* KEY_CHANNELDOWN */
};
/* TwinHan AzureWave AD-TU700(704J) */
@@ -746,4 +780,75 @@ static u8 af9015_ir_table_trekstor[] = {
0x00, 0xff, 0x84, 0x7b, 0x27, 0x07, 0x00,
};
+/* MSI DIGIVOX mini III */
+static struct dvb_usb_rc_key af9015_rc_keys_msi_digivox_iii[] = {
+ { 0x0713, KEY_POWER }, /* [red power button] */
+ { 0x073b, KEY_VIDEO }, /* Source */
+ { 0x073e, KEY_ZOOM }, /* Zoom */
+ { 0x070b, KEY_POWER2 }, /* ShutDown */
+ { 0x071e, KEY_1 },
+ { 0x071f, KEY_2 },
+ { 0x0720, KEY_3 },
+ { 0x0721, KEY_4 },
+ { 0x0722, KEY_5 },
+ { 0x0723, KEY_6 },
+ { 0x0724, KEY_7 },
+ { 0x0725, KEY_8 },
+ { 0x0726, KEY_9 },
+ { 0x0727, KEY_0 },
+ { 0x0752, KEY_CHANNELUP }, /* CH+ */
+ { 0x0751, KEY_CHANNELDOWN }, /* CH- */
+ { 0x0750, KEY_VOLUMEUP }, /* Vol+ */
+ { 0x074f, KEY_VOLUMEDOWN }, /* Vol- */
+ { 0x0705, KEY_ESC }, /* [back up arrow] */
+ { 0x0708, KEY_OK }, /* [enter arrow] */
+ { 0x073f, KEY_RECORD }, /* Rec */
+ { 0x0716, KEY_STOP }, /* Stop */
+ { 0x072a, KEY_PLAY }, /* Play */
+ { 0x073c, KEY_MUTE }, /* Mute */
+ { 0x0718, KEY_UP },
+ { 0x0707, KEY_DOWN },
+ { 0x070f, KEY_LEFT },
+ { 0x0715, KEY_RIGHT },
+ { 0x0736, KEY_RED },
+ { 0x0737, KEY_GREEN },
+ { 0x072d, KEY_YELLOW },
+ { 0x072e, KEY_BLUE },
+};
+
+static u8 af9015_ir_table_msi_digivox_iii[] = {
+ 0x61, 0xd6, 0x43, 0xbc, 0x13, 0x07, 0x00, /* KEY_POWER */
+ 0x61, 0xd6, 0x01, 0xfe, 0x3b, 0x07, 0x00, /* KEY_VIDEO */
+ 0x61, 0xd6, 0x0b, 0xf4, 0x3e, 0x07, 0x00, /* KEY_ZOOM */
+ 0x61, 0xd6, 0x03, 0xfc, 0x0b, 0x07, 0x00, /* KEY_POWER2 */
+ 0x61, 0xd6, 0x04, 0xfb, 0x1e, 0x07, 0x00, /* KEY_1 */
+ 0x61, 0xd6, 0x08, 0xf7, 0x1f, 0x07, 0x00, /* KEY_2 */
+ 0x61, 0xd6, 0x02, 0xfd, 0x20, 0x07, 0x00, /* KEY_3 */
+ 0x61, 0xd6, 0x0f, 0xf0, 0x21, 0x07, 0x00, /* KEY_4 */
+ 0x61, 0xd6, 0x05, 0xfa, 0x22, 0x07, 0x00, /* KEY_5 */
+ 0x61, 0xd6, 0x06, 0xf9, 0x23, 0x07, 0x00, /* KEY_6 */
+ 0x61, 0xd6, 0x0c, 0xf3, 0x24, 0x07, 0x00, /* KEY_7 */
+ 0x61, 0xd6, 0x0d, 0xf2, 0x25, 0x07, 0x00, /* KEY_8 */
+ 0x61, 0xd6, 0x0a, 0xf5, 0x26, 0x07, 0x00, /* KEY_9 */
+ 0x61, 0xd6, 0x11, 0xee, 0x27, 0x07, 0x00, /* KEY_0 */
+ 0x61, 0xd6, 0x09, 0xf6, 0x52, 0x07, 0x00, /* KEY_CHANNELUP */
+ 0x61, 0xd6, 0x07, 0xf8, 0x51, 0x07, 0x00, /* KEY_CHANNELDOWN */
+ 0x61, 0xd6, 0x0e, 0xf1, 0x50, 0x07, 0x00, /* KEY_VOLUMEUP */
+ 0x61, 0xd6, 0x13, 0xec, 0x4f, 0x07, 0x00, /* KEY_VOLUMEDOWN */
+ 0x61, 0xd6, 0x10, 0xef, 0x05, 0x07, 0x00, /* KEY_ESC */
+ 0x61, 0xd6, 0x12, 0xed, 0x08, 0x07, 0x00, /* KEY_OK */
+ 0x61, 0xd6, 0x14, 0xeb, 0x3f, 0x07, 0x00, /* KEY_RECORD */
+ 0x61, 0xd6, 0x15, 0xea, 0x16, 0x07, 0x00, /* KEY_STOP */
+ 0x61, 0xd6, 0x16, 0xe9, 0x2a, 0x07, 0x00, /* KEY_PLAY */
+ 0x61, 0xd6, 0x17, 0xe8, 0x3c, 0x07, 0x00, /* KEY_MUTE */
+ 0x61, 0xd6, 0x18, 0xe7, 0x18, 0x07, 0x00, /* KEY_UP */
+ 0x61, 0xd6, 0x19, 0xe6, 0x07, 0x07, 0x00, /* KEY_DOWN */
+ 0x61, 0xd6, 0x1a, 0xe5, 0x0f, 0x07, 0x00, /* KEY_LEFT */
+ 0x61, 0xd6, 0x1b, 0xe4, 0x15, 0x07, 0x00, /* KEY_RIGHT */
+ 0x61, 0xd6, 0x1c, 0xe3, 0x36, 0x07, 0x00, /* KEY_RED */
+ 0x61, 0xd6, 0x1d, 0xe2, 0x37, 0x07, 0x00, /* KEY_GREEN */
+ 0x61, 0xd6, 0x1e, 0xe1, 0x2d, 0x07, 0x00, /* KEY_YELLOW */
+ 0x61, 0xd6, 0x1f, 0xe0, 0x2e, 0x07, 0x00, /* KEY_BLUE */
+};
+
#endif
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index 2ae7f648eff..bb69f3719f9 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -344,7 +344,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
if (ret)
return ret;
- err("Unkown Anysee version: %02x %02x %02x. "\
+ err("Unknown Anysee version: %02x %02x %02x. "\
"Please report the <linux-dvb@linuxtv.org>.",
hw_info[0], hw_info[1], hw_info[2]);
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 2a53dd096ee..05fb28e9c69 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -36,9 +36,11 @@
#include "tuner-xc2028.h"
#include "tuner-simple.h"
#include "mxl5005s.h"
+#include "max2165.h"
#include "dib7000p.h"
#include "dib0070.h"
#include "lgs8gxx.h"
+#include "atbm8830.h"
/* debug */
static int dvb_usb_cxusb_debug;
@@ -714,6 +716,11 @@ static struct mxl5005s_config d680_dmb_tuner = {
.AgcMasterByte = 0x00,
};
+static struct max2165_config mygica_d689_max2165_cfg = {
+ .i2c_address = 0x60,
+ .osc_clk = 20
+};
+
/* Callbacks for DVB USB */
static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
@@ -813,6 +820,14 @@ static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
return (fe == NULL) ? -EIO : 0;
}
+static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_frontend *fe;
+ fe = dvb_attach(max2165_attach, adap->fe,
+ &adap->dev->i2c_adap, &mygica_d689_max2165_cfg);
+ return (fe == NULL) ? -EIO : 0;
+}
+
static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
{
u8 b;
@@ -1160,6 +1175,55 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static struct atbm8830_config mygica_d689_atbm8830_cfg = {
+ .prod = ATBM8830_PROD_8830,
+ .demod_address = 0x40,
+ .serial_ts = 0,
+ .ts_sampling_edge = 1,
+ .ts_clk_gated = 0,
+ .osc_clk_freq = 30400, /* in kHz */
+ .if_freq = 0, /* zero IF */
+ .zif_swap_iq = 1,
+};
+
+static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+
+ /* Select required USB configuration */
+ if (usb_set_interface(d->udev, 0, 0) < 0)
+ err("set interface failed");
+
+ /* Unblock all USB pipes */
+ usb_clear_halt(d->udev,
+ usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+ usb_clear_halt(d->udev,
+ usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+ usb_clear_halt(d->udev,
+ usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+
+
+ /* Reset the tuner */
+ if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) {
+ err("clear tuner gpio failed");
+ return -EIO;
+ }
+ msleep(100);
+ if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) {
+ err("set tuner gpio failed");
+ return -EIO;
+ }
+ msleep(100);
+
+ /* Attach frontend */
+ adap->fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg,
+ &d->i2c_adap);
+ if (adap->fe == NULL)
+ return -EIO;
+
+ return 0;
+}
+
/*
* DViCO has shipped two devices with the same USB ID, but only one of them
* needs a firmware download. Check the device class details to see if they
@@ -1240,6 +1304,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties;
static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties;
static struct dvb_usb_device_properties cxusb_aver_a868r_properties;
static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
+static struct dvb_usb_device_properties cxusb_mygica_d689_properties;
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -1268,6 +1333,8 @@ static int cxusb_probe(struct usb_interface *intf,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties,
THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
0)
return 0;
@@ -1294,6 +1361,7 @@ static struct usb_device_id cxusb_table [] = {
{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) },
{ USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) },
+ { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -1837,6 +1905,55 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
}
};
+static struct dvb_usb_device_properties cxusb_mygica_d689_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl,
+ .frontend_attach = cxusb_mygica_d689_frontend_attach,
+ .tuner_attach = cxusb_mygica_d689_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_d680_dmb_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = d680_dmb_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(d680_dmb_rc_keys),
+ .rc_query = cxusb_d680_dmb_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ {
+ "Mygica D689 DMB-TH",
+ { NULL },
+ { &cxusb_table[19], NULL },
+ },
+ }
+};
+
static struct usb_driver cxusb_driver = {
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 6bd8951ea02..684146f98eb 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -558,8 +558,7 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event,
struct dib0700_rc_response {
u8 report_id;
u8 data_state;
- u8 system_msb;
- u8 system_lsb;
+ u16 system;
u8 data;
u8 not_data;
};
@@ -589,37 +588,50 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
return 0;
}
- if (actlen != sizeof(buf)) {
- /* We didn't get back the 6 byte message we expected */
- err("Unexpected RC response size [%d]", actlen);
- return -1;
- }
- poll_reply.report_id = buf[0];
- poll_reply.data_state = buf[1];
- poll_reply.system_msb = buf[2];
- poll_reply.system_lsb = buf[3];
- poll_reply.data = buf[4];
- poll_reply.not_data = buf[5];
+ switch (dvb_usb_dib0700_ir_proto) {
+ case 0:
+ poll_reply.report_id = 0;
+ poll_reply.data_state = 1;
+ poll_reply.system = buf[2];
+ poll_reply.data = buf[4];
+ poll_reply.not_data = buf[5];
+
+ /* NEC protocol sends repeat code as 0 0 0 FF */
+ if ((poll_reply.system == 0x00) && (poll_reply.data == 0x00)
+ && (poll_reply.not_data == 0xff)) {
+ poll_reply.data_state = 2;
+ break;
+ }
+ break;
+ default:
+ if (actlen != sizeof(buf)) {
+ /* We didn't get back the 6 byte message we expected */
+ err("Unexpected RC response size [%d]", actlen);
+ return -1;
+ }
+
+ poll_reply.report_id = buf[0];
+ poll_reply.data_state = buf[1];
+ poll_reply.system = (buf[2] << 8) | buf[3];
+ poll_reply.data = buf[4];
+ poll_reply.not_data = buf[5];
- /*
- info("rid=%02x ds=%02x sm=%02x sl=%02x d=%02x nd=%02x\n",
- poll_reply.report_id, poll_reply.data_state,
- poll_reply.system_msb, poll_reply.system_lsb,
- poll_reply.data, poll_reply.not_data);
- */
+ break;
+ }
if ((poll_reply.data + poll_reply.not_data) != 0xff) {
/* Key failed integrity check */
- err("key failed integrity check: %02x %02x %02x %02x",
- poll_reply.system_msb, poll_reply.system_lsb,
+ err("key failed integrity check: %04x %02x %02x",
+ poll_reply.system,
poll_reply.data, poll_reply.not_data);
return -1;
}
+
/* Find the key in the map */
for (i = 0; i < d->props.rc_key_map_size; i++) {
- if (rc5_custom(&keymap[i]) == poll_reply.system_lsb &&
+ if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) &&
rc5_data(&keymap[i]) == poll_reply.data) {
*event = keymap[i].event;
found = 1;
@@ -628,8 +640,8 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
}
if (found == 0) {
- err("Unknown remote controller key: %02x %02x %02x %02x",
- poll_reply.system_msb, poll_reply.system_lsb,
+ err("Unknown remote controller key: %04x %02x %02x",
+ poll_reply.system,
poll_reply.data, poll_reply.not_data);
d->last_event = 0;
return 0;
@@ -874,6 +886,49 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = {
{ 0x1d37, KEY_RECORD },
{ 0x1d3b, KEY_GOTO },
{ 0x1d3d, KEY_POWER },
+
+ /* Key codes for the Pixelview SBTVD remote (proto NEC) */
+ { 0x8613, KEY_MUTE },
+ { 0x8612, KEY_POWER },
+ { 0x8601, KEY_1 },
+ { 0x8602, KEY_2 },
+ { 0x8603, KEY_3 },
+ { 0x8604, KEY_4 },
+ { 0x8605, KEY_5 },
+ { 0x8606, KEY_6 },
+ { 0x8607, KEY_7 },
+ { 0x8608, KEY_8 },
+ { 0x8609, KEY_9 },
+ { 0x8600, KEY_0 },
+ { 0x860d, KEY_CHANNELUP },
+ { 0x8619, KEY_CHANNELDOWN },
+ { 0x8610, KEY_VOLUMEUP },
+ { 0x860c, KEY_VOLUMEDOWN },
+
+ { 0x860a, KEY_CAMERA },
+ { 0x860b, KEY_ZOOM },
+ { 0x861b, KEY_BACKSPACE },
+ { 0x8615, KEY_ENTER },
+
+ { 0x861d, KEY_UP },
+ { 0x861e, KEY_DOWN },
+ { 0x860e, KEY_LEFT },
+ { 0x860f, KEY_RIGHT },
+
+ { 0x8618, KEY_RECORD },
+ { 0x861a, KEY_STOP },
+
+ /* Key codes for the EvolutePC TVWay+ remote (proto NEC) */
+ { 0x7a00, KEY_MENU },
+ { 0x7a01, KEY_RECORD },
+ { 0x7a02, KEY_PLAY },
+ { 0x7a03, KEY_STOP },
+ { 0x7a10, KEY_CHANNELUP },
+ { 0x7a11, KEY_CHANNELDOWN },
+ { 0x7a12, KEY_VOLUMEUP },
+ { 0x7a13, KEY_VOLUMEDOWN },
+ { 0x7a40, KEY_POWER },
+ { 0x7a41, KEY_MUTE },
};
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -1133,6 +1188,7 @@ static struct dib0070_config dib7770p_dib0070_config = {
.clock_khz = 12000,
.clock_pad_drive = 0,
.flip_chip = 1,
+ .charge_pump = 2,
};
static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
@@ -1209,6 +1265,16 @@ static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
+{
+ return dib7000p_pid_filter(adapter->fe, index, pid, onoff);
+}
+
+static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
+{
+ return dib7000p_pid_filter_ctrl(adapter->fe, onoff);
+}
+
static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
60000, 15000, // internal, sampling
1, 20, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
@@ -1500,6 +1566,15 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int stk807x_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
+{
+ return dib8000_pid_filter(adapter->fe, index, pid, onoff);
+}
+
+static int stk807x_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
+{
+ return dib8000_pid_filter_ctrl(adapter->fe, onoff);
+}
/* STK807x */
static int stk807x_frontend_attach(struct dvb_usb_adapter *adap)
@@ -1861,6 +1936,7 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) },
{ USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) },
+ { USB_DEVICE(USB_VID_EVOLUTEPC, USB_PID_TVWAY_PLUS) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1895,6 +1971,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7700p_frontend_attach,
.tuner_attach = stk7700p_tuner_attach,
@@ -1976,11 +2056,19 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7700d_frontend_attach,
.tuner_attach = stk7700d_tuner_attach,
DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
}, {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7700d_frontend_attach,
.tuner_attach = stk7700d_tuner_attach,
@@ -2023,6 +2111,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7700P2_frontend_attach,
.tuner_attach = stk7700d_tuner_attach,
@@ -2055,6 +2147,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7070p_frontend_attach,
.tuner_attach = dib7070p_tuner_attach,
@@ -2122,6 +2218,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7070p_frontend_attach,
.tuner_attach = dib7070p_tuner_attach,
@@ -2157,6 +2257,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7070pd_frontend_attach0,
.tuner_attach = dib7070p_tuner_attach,
@@ -2164,6 +2268,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.size_of_priv = sizeof(struct dib0700_adapter_state),
}, {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7070pd_frontend_attach1,
.tuner_attach = dib7070p_tuner_attach,
@@ -2210,6 +2318,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7700ph_frontend_attach,
.tuner_attach = stk7700ph_tuner_attach,
@@ -2322,6 +2434,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk70x0p_pid_filter,
+ .pid_filter_ctrl = stk70x0p_pid_filter_ctrl,
.frontend_attach = stk7070p_frontend_attach,
.tuner_attach = dib7770p_tuner_attach,
@@ -2353,6 +2469,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 1,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk807x_pid_filter,
+ .pid_filter_ctrl = stk807x_pid_filter_ctrl,
.frontend_attach = stk807x_frontend_attach,
.tuner_attach = dib807x_tuner_attach,
@@ -2363,7 +2483,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 2,
+ .num_device_descs = 3,
.devices = {
{ "DiBcom STK807xP reference design",
{ &dib0700_usb_id_table[62], NULL },
@@ -2373,6 +2493,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ &dib0700_usb_id_table[63], NULL },
{ NULL },
},
+ { "EvolutePC TVWay+",
+ { &dib0700_usb_id_table[64], NULL },
+ { NULL },
+ },
},
.rc_interval = DEFAULT_RC_INTERVAL,
@@ -2384,6 +2508,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
.num_adapters = 2,
.adapter = {
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk807x_pid_filter,
+ .pid_filter_ctrl = stk807x_pid_filter_ctrl,
.frontend_attach = stk807xpvr_frontend_attach0,
.tuner_attach = dib807x_tuner_attach,
@@ -2393,6 +2521,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
sizeof(struct dib0700_adapter_state),
},
{
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = stk807x_pid_filter,
+ .pid_filter_ctrl = stk807x_pid_filter_ctrl,
.frontend_attach = stk807xpvr_frontend_attach1,
.tuner_attach = dib807x_tuner_attach,
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c
index eeef50bff4f..5c0126dc1ff 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c
@@ -242,7 +242,7 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
{ &dibusb_dib3000mb_table[9], &dibusb_dib3000mb_table[11], NULL },
{ &dibusb_dib3000mb_table[10], &dibusb_dib3000mb_table[12], NULL },
},
- { "Unkown USB1.1 DVB-T device ???? please report the name to the author",
+ { "Unknown USB1.1 DVB-T device ???? please report the name to the author",
{ &dibusb_dib3000mb_table[13], NULL },
{ &dibusb_dib3000mb_table[14], NULL },
},
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
index 8a7d87bcd1d..df1ec3e69f4 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
@@ -88,6 +88,7 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
goto err;
}
adap->dvb_adap.priv = adap;
+ adap->dvb_adap.fe_ioctl_override = adap->props.fe_ioctl_override;
if (adap->dev->props.read_mac_address) {
if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0)
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index a548c14c194..f1602d4ace6 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -27,6 +27,7 @@
#define USB_VID_DIBCOM 0x10b8
#define USB_VID_DPOSH 0x1498
#define USB_VID_DVICO 0x0fe9
+#define USB_VID_E3C 0x18b4
#define USB_VID_ELGATO 0x0fd9
#define USB_VID_EMPIA 0xeb1a
#define USB_VID_GENPIX 0x09c0
@@ -61,6 +62,7 @@
#define USB_VID_XTENSIONS 0x1ae7
#define USB_VID_HUMAX_COEX 0x10b9
#define USB_VID_774 0x7a69
+#define USB_VID_EVOLUTEPC 0x1e59
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
@@ -103,6 +105,11 @@
#define USB_PID_DIBCOM_STK7770P 0x1e80
#define USB_PID_DPOSH_M9206_COLD 0x9206
#define USB_PID_DPOSH_M9206_WARM 0xa090
+#define USB_PID_E3C_EC168 0x1689
+#define USB_PID_E3C_EC168_2 0xfffa
+#define USB_PID_E3C_EC168_3 0xfffb
+#define USB_PID_E3C_EC168_4 0x1001
+#define USB_PID_E3C_EC168_5 0x1002
#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
@@ -115,6 +122,7 @@
#define USB_PID_KWORLD_395U_3 0xe395
#define USB_PID_KWORLD_MC810 0xc810
#define USB_PID_KWORLD_PC160_2T 0xc160
+#define USB_PID_KWORLD_PC160_T 0xc161
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
@@ -271,10 +279,13 @@
#define USB_PID_TELESTAR_STARSTICK_2 0x8000
#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
#define USB_PID_SONY_PLAYTV 0x0003
+#define USB_PID_MYGICA_D689 0xd811
#define USB_PID_ELGATO_EYETV_DTT 0x0021
#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001
#define USB_PID_FRIIO_WHITE 0x0001
+#define USB_PID_TVWAY_PLUS 0x0002
+#define USB_PID_SVEON_STV20 0xe39d
#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
index edde87c6aa3..6b5ded9e7d5 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
@@ -259,7 +259,7 @@ int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d,
*state = REMOTE_KEY_REPEAT;
break;
default:
- deb_err("unkown type of remote status: %d\n",keybuf[0]);
+ deb_err("unknown type of remote status: %d\n",keybuf[0]);
break;
}
return 0;
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
index fe2b87efb3f..0143aef19ec 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb.h
@@ -162,6 +162,9 @@ struct dvb_usb_adapter_properties {
struct usb_data_stream_properties stream;
int size_of_priv;
+
+ int (*fe_ioctl_override) (struct dvb_frontend *,
+ unsigned int, void *, unsigned int);
};
/**
diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c
new file mode 100644
index 00000000000..52f5d4f0f23
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/ec168.c
@@ -0,0 +1,440 @@
+/*
+ * E3C EC168 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "ec168.h"
+#include "ec100.h"
+#include "mxl5005s.h"
+
+/* debug */
+static int dvb_usb_ec168_debug;
+module_param_named(debug, dvb_usb_ec168_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static struct ec100_config ec168_ec100_config;
+
+static int ec168_rw_udev(struct usb_device *udev, struct ec168_req *req)
+{
+ int ret;
+ unsigned int pipe;
+ u8 request, requesttype;
+ u8 buf[req->size];
+
+ switch (req->cmd) {
+ case DOWNLOAD_FIRMWARE:
+ case GPIO:
+ case WRITE_I2C:
+ case STREAMING_CTRL:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ request = req->cmd;
+ break;
+ case READ_I2C:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ request = req->cmd;
+ break;
+ case GET_CONFIG:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ request = CONFIG;
+ break;
+ case SET_CONFIG:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ request = CONFIG;
+ break;
+ case WRITE_DEMOD:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ request = DEMOD_RW;
+ break;
+ case READ_DEMOD:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ request = DEMOD_RW;
+ break;
+ default:
+ err("unknown command:%02x", req->cmd);
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
+ /* write */
+ memcpy(buf, req->data, req->size);
+ pipe = usb_sndctrlpipe(udev, 0);
+ } else {
+ /* read */
+ pipe = usb_rcvctrlpipe(udev, 0);
+ }
+
+ msleep(1); /* avoid I2C errors */
+
+ ret = usb_control_msg(udev, pipe, request, requesttype, req->value,
+ req->index, buf, sizeof(buf), EC168_USB_TIMEOUT);
+
+ ec168_debug_dump(request, requesttype, req->value, req->index, buf,
+ req->size, deb_xfer);
+
+ if (ret < 0)
+ goto error;
+ else
+ ret = 0;
+
+ /* read request, copy returned data to return buf */
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+ memcpy(req->data, buf, req->size);
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req)
+{
+ return ec168_rw_udev(d->udev, req);
+}
+
+/* I2C */
+static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct ec168_req req;
+ int i = 0;
+ int ret;
+
+ if (num > 2)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].addr == ec168_ec100_config.demod_address) {
+ req.cmd = READ_DEMOD;
+ req.value = 0;
+ req.index = 0xff00 + msg[i].buf[0]; /* reg */
+ req.size = msg[i+1].len; /* bytes to read */
+ req.data = &msg[i+1].buf[0];
+ ret = ec168_ctrl_msg(d, &req);
+ i += 2;
+ } else {
+ err("I2C read not implemented");
+ ret = -ENOSYS;
+ i += 2;
+ }
+ } else {
+ if (msg[i].addr == ec168_ec100_config.demod_address) {
+ req.cmd = WRITE_DEMOD;
+ req.value = msg[i].buf[1]; /* val */
+ req.index = 0xff00 + msg[i].buf[0]; /* reg */
+ req.size = 0;
+ req.data = NULL;
+ ret = ec168_ctrl_msg(d, &req);
+ i += 1;
+ } else {
+ req.cmd = WRITE_I2C;
+ req.value = msg[i].buf[0]; /* val */
+ req.index = 0x0100 + msg[i].addr; /* I2C addr */
+ req.size = msg[i].len-1;
+ req.data = &msg[i].buf[1];
+ ret = ec168_ctrl_msg(d, &req);
+ i += 1;
+ }
+ }
+ if (ret)
+ goto error;
+
+ }
+ ret = i;
+
+error:
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+
+static u32 ec168_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm ec168_i2c_algo = {
+ .master_xfer = ec168_i2c_xfer,
+ .functionality = ec168_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static struct ec100_config ec168_ec100_config = {
+ .demod_address = 0xff, /* not real address, demod is integrated */
+};
+
+static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ deb_info("%s:\n", __func__);
+ adap->fe = dvb_attach(ec100_attach, &ec168_ec100_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe == NULL)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct mxl5005s_config ec168_mxl5003s_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_OFF,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ deb_info("%s:\n", __func__);
+ return dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap,
+ &ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
+}
+
+static int ec168_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL};
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+ if (onoff)
+ req.index = 0x0102;
+ return ec168_ctrl_msg(adap->dev, &req);
+}
+
+static int ec168_download_firmware(struct usb_device *udev,
+ const struct firmware *fw)
+{
+ int i, len, packets, remainder, ret;
+ u16 addr = 0x0000; /* firmware start address */
+ struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL};
+ deb_info("%s:\n", __func__);
+
+ #define FW_PACKET_MAX_DATA 2048
+ packets = fw->size / FW_PACKET_MAX_DATA;
+ remainder = fw->size % FW_PACKET_MAX_DATA;
+ len = FW_PACKET_MAX_DATA;
+ for (i = 0; i <= packets; i++) {
+ if (i == packets) /* set size of the last packet */
+ len = remainder;
+
+ req.size = len;
+ req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA);
+ req.index = addr;
+ addr += FW_PACKET_MAX_DATA;
+
+ ret = ec168_rw_udev(udev, &req);
+ if (ret) {
+ err("firmware download failed:%d packet:%d", ret, i);
+ goto error;
+ }
+ }
+ req.size = 0;
+
+ /* set "warm"? */
+ req.cmd = SET_CONFIG;
+ req.value = 0;
+ req.index = 0x0001;
+ ret = ec168_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+
+ /* really needed - no idea what does */
+ req.cmd = GPIO;
+ req.value = 0;
+ req.index = 0x0206;
+ ret = ec168_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+
+ /* activate tuner I2C? */
+ req.cmd = WRITE_I2C;
+ req.value = 0;
+ req.index = 0x00c6;
+ ret = ec168_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec168_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc, int *cold)
+{
+ int ret;
+ u8 reply;
+ struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply};
+ deb_info("%s:\n", __func__);
+
+ ret = ec168_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+
+ deb_info("%s: reply:%02x\n", __func__, reply);
+
+ if (reply == 0x01)
+ *cold = 0;
+ else
+ *cold = 1;
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties ec168_properties;
+
+static int ec168_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret;
+ deb_info("%s: interface:%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ ret = dvb_usb_device_init(intf, &ec168_properties, THIS_MODULE, NULL,
+ adapter_nr);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+#define E3C_EC168_1689 0
+#define E3C_EC168_FFFA 1
+#define E3C_EC168_FFFB 2
+#define E3C_EC168_1001 3
+#define E3C_EC168_1002 4
+
+static struct usb_device_id ec168_id[] = {
+ [E3C_EC168_1689] =
+ {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168)},
+ [E3C_EC168_FFFA] =
+ {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2)},
+ [E3C_EC168_FFFB] =
+ {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3)},
+ [E3C_EC168_1001] =
+ {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4)},
+ [E3C_EC168_1002] =
+ {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5)},
+ {} /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ec168_id);
+
+static struct dvb_usb_device_properties ec168_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = ec168_download_firmware,
+ .firmware = "dvb-usb-ec168.fw",
+ .no_reconnect = 1,
+
+ .size_of_priv = 0,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = ec168_streaming_ctrl,
+ .frontend_attach = ec168_ec100_frontend_attach,
+ .tuner_attach = ec168_mxl5003s_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = (32*512),
+ }
+ }
+ },
+ }
+ },
+
+ .identify_state = ec168_identify_state,
+
+ .i2c_algo = &ec168_i2c_algo,
+
+ .num_device_descs = 1,
+ .devices = {
+ {
+ .name = "E3C EC168 DVB-T USB2.0 reference design",
+ .cold_ids = {
+ &ec168_id[E3C_EC168_1689],
+ &ec168_id[E3C_EC168_FFFA],
+ &ec168_id[E3C_EC168_FFFB],
+ &ec168_id[E3C_EC168_1001],
+ &ec168_id[E3C_EC168_1002],
+ NULL},
+ .warm_ids = {NULL},
+ },
+ }
+};
+
+static struct usb_driver ec168_driver = {
+ .name = "dvb_usb_ec168",
+ .probe = ec168_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = ec168_id,
+};
+
+/* module stuff */
+static int __init ec168_module_init(void)
+{
+ int ret;
+ deb_info("%s:\n", __func__);
+ ret = usb_register(&ec168_driver);
+ if (ret)
+ err("module init failed:%d", ret);
+
+ return ret;
+}
+
+static void __exit ec168_module_exit(void)
+{
+ deb_info("%s:\n", __func__);
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&ec168_driver);
+}
+
+module_init(ec168_module_init);
+module_exit(ec168_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("E3C EC168 DVB-T USB2.0 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ec168.h b/drivers/media/dvb/dvb-usb/ec168.h
new file mode 100644
index 00000000000..e7e0b831314
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/ec168.h
@@ -0,0 +1,73 @@
+/*
+ * E3C EC168 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EC168_H
+#define EC168_H
+
+#define DVB_USB_LOG_PREFIX "ec168"
+#include "dvb-usb.h"
+
+#define deb_info(args...) dprintk(dvb_usb_ec168_debug, 0x01, args)
+#define deb_rc(args...) dprintk(dvb_usb_ec168_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_ec168_debug, 0x04, args)
+#define deb_reg(args...) dprintk(dvb_usb_ec168_debug, 0x08, args)
+#define deb_i2c(args...) dprintk(dvb_usb_ec168_debug, 0x10, args)
+#define deb_fw(args...) dprintk(dvb_usb_ec168_debug, 0x20, args)
+
+#define ec168_debug_dump(r, t, v, i, b, l, func) { \
+ int loop_; \
+ func("%02x %02x %02x %02x %02x %02x %02x %02x", \
+ t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
+ if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
+ func(" >>> "); \
+ else \
+ func(" <<< "); \
+ for (loop_ = 0; loop_ < l; loop_++) \
+ func("%02x ", b[loop_]); \
+ func("\n");\
+}
+
+#define EC168_USB_TIMEOUT 1000
+
+struct ec168_req {
+ u8 cmd; /* [1] */
+ u16 value; /* [2|3] */
+ u16 index; /* [4|5] */
+ u16 size; /* [6|7] */
+ u8 *data;
+};
+
+enum ec168_cmd {
+ DOWNLOAD_FIRMWARE = 0x00,
+ CONFIG = 0x01,
+ DEMOD_RW = 0x03,
+ GPIO = 0x04,
+ STREAMING_CTRL = 0x10,
+ READ_I2C = 0x20,
+ WRITE_I2C = 0x21,
+ HID_DOWNLOAD = 0x30,
+ GET_CONFIG,
+ SET_CONFIG,
+ READ_DEMOD,
+ WRITE_DEMOD,
+};
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c
index c4dfe25cf60..9cbbe42ca44 100644
--- a/drivers/media/dvb/dvb-usb/friio-fe.c
+++ b/drivers/media/dvb/dvb-usb/friio-fe.c
@@ -232,12 +232,6 @@ static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state)
return 0;
}
-static int jdvbt90502_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- *ber = 0;
- return 0;
-}
-
static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe,
u16 *strength)
{
@@ -264,26 +258,26 @@ static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe,
return 0;
}
-static int jdvbt90502_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- *snr = 0x0101;
- return 0;
-}
-
-static int jdvbt90502_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- return 0;
-}
-static int jdvbt90502_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *fs)
+/* filter out un-supported properties to notify users */
+static int jdvbt90502_set_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
{
- fs->min_delay_ms = 500;
- fs->step_size = 0;
- fs->max_drift = 0;
-
- return 0;
+ int r = 0;
+
+ switch (tvp->cmd) {
+ case DTV_DELIVERY_SYSTEM:
+ if (tvp->u.data != SYS_ISDBT)
+ r = -EINVAL;
+ break;
+ case DTV_CLEAR:
+ case DTV_TUNE:
+ case DTV_FREQUENCY:
+ break;
+ default:
+ r = -EINVAL;
+ }
+ return r;
}
static int jdvbt90502_get_frontend(struct dvb_frontend *fe,
@@ -314,6 +308,9 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe,
deb_fe("%s: Freq:%d\n", __func__, p->frequency);
+ /* for recovery from DTV_CLEAN */
+ fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+
ret = jdvbt90502_pll_set_freq(state, p->frequency);
if (ret) {
deb_fe("%s:ret == %d\n", __func__, ret);
@@ -323,12 +320,6 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe,
return 0;
}
-static int jdvbt90502_sleep(struct dvb_frontend *fe)
-{
- deb_fe("%s called.\n", __func__);
- return 0;
-}
-
/**
* (reg, val) commad list to initialize this module.
@@ -394,6 +385,7 @@ static int jdvbt90502_init(struct dvb_frontend *fe)
if (ret != 1)
goto error;
}
+ fe->dtv_property_cache.delivery_system = SYS_ISDBT;
msleep(100);
return 0;
@@ -468,16 +460,13 @@ static struct dvb_frontend_ops jdvbt90502_ops = {
.release = jdvbt90502_release,
.init = jdvbt90502_init,
- .sleep = jdvbt90502_sleep,
.write = _jdvbt90502_write,
+ .set_property = jdvbt90502_set_property,
+
.set_frontend = jdvbt90502_set_frontend,
.get_frontend = jdvbt90502_get_frontend,
- .get_tune_settings = jdvbt90502_get_tune_settings,
.read_status = jdvbt90502_read_status,
- .read_ber = jdvbt90502_read_ber,
.read_signal_strength = jdvbt90502_read_signal_strength,
- .read_snr = jdvbt90502_read_snr,
- .read_ucblocks = jdvbt90502_read_ucblocks,
};
diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c
index 9da2cc95ca1..f9702e3756b 100644
--- a/drivers/media/dvb/dvb-usb/usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/usb-urb.c
@@ -56,7 +56,7 @@ static void usb_urb_complete(struct urb *urb)
stream->complete(stream, b, urb->actual_length);
break;
default:
- err("unkown endpoint type in completition handler.");
+ err("unknown endpoint type in completition handler.");
return;
}
usb_submit_urb(urb,GFP_ATOMIC);
@@ -228,7 +228,7 @@ int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properti
case USB_ISOC:
return usb_isoc_urb_init(stream);
default:
- err("unkown URB-type for data transfer.");
+ err("unknown URB-type for data transfer.");
return -EINVAL;
}
}
diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig
index 69028253e98..4afa29256df 100644
--- a/drivers/media/dvb/firewire/Kconfig
+++ b/drivers/media/dvb/firewire/Kconfig
@@ -1,6 +1,6 @@
config DVB_FIREDTV
tristate "FireDTV and FloppyDTV"
- depends on DVB_CORE && IEEE1394
+ depends on DVB_CORE && (FIREWIRE || IEEE1394)
help
Support for DVB receivers from Digital Everywhere
which are connected via IEEE 1394 (FireWire).
@@ -13,8 +13,11 @@ config DVB_FIREDTV
if DVB_FIREDTV
+config DVB_FIREDTV_FIREWIRE
+ def_bool FIREWIRE = y || (FIREWIRE = m && DVB_FIREDTV = m)
+
config DVB_FIREDTV_IEEE1394
- def_bool IEEE1394
+ def_bool IEEE1394 = y || (IEEE1394 = m && DVB_FIREDTV = m)
config DVB_FIREDTV_INPUT
def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m)
diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile
index 2034695ba19..da84203d51c 100644
--- a/drivers/media/dvb/firewire/Makefile
+++ b/drivers/media/dvb/firewire/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_DVB_FIREDTV) += firedtv.o
firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o
+firedtv-$(CONFIG_DVB_FIREDTV_FIREWIRE) += firedtv-fw.o
firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o
firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o
diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c
index 2b6eeeab5b2..7c5459c27b7 100644
--- a/drivers/media/dvb/firewire/firedtv-1394.c
+++ b/drivers/media/dvb/firewire/firedtv-1394.c
@@ -1,5 +1,5 @@
/*
- * FireDTV driver (formerly known as FireSAT)
+ * FireDTV driver -- ieee1394 I/O backend
*
* Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
* Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com>
@@ -26,13 +26,16 @@
#include <iso.h>
#include <nodemgr.h>
+#include <dvb_demux.h>
+
#include "firedtv.h"
static LIST_HEAD(node_list);
static DEFINE_SPINLOCK(node_list_lock);
-#define FIREWIRE_HEADER_SIZE 4
-#define CIP_HEADER_SIZE 8
+#define CIP_HEADER_SIZE 8
+#define MPEG2_TS_HEADER_SIZE 4
+#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188)
static void rawiso_activity_cb(struct hpsb_iso *iso)
{
@@ -62,20 +65,20 @@ static void rawiso_activity_cb(struct hpsb_iso *iso)
buf = dma_region_i(&iso->data_buf, unsigned char,
iso->infos[packet].offset + CIP_HEADER_SIZE);
count = (iso->infos[packet].len - CIP_HEADER_SIZE) /
- (188 + FIREWIRE_HEADER_SIZE);
+ MPEG2_TS_SOURCE_PACKET_SIZE;
/* ignore empty packet */
if (iso->infos[packet].len <= CIP_HEADER_SIZE)
continue;
while (count--) {
- if (buf[FIREWIRE_HEADER_SIZE] == 0x47)
+ if (buf[MPEG2_TS_HEADER_SIZE] == 0x47)
dvb_dmx_swfilter_packets(&fdtv->demux,
- &buf[FIREWIRE_HEADER_SIZE], 1);
+ &buf[MPEG2_TS_HEADER_SIZE], 1);
else
dev_err(fdtv->device,
"skipping invalid packet\n");
- buf += 188 + FIREWIRE_HEADER_SIZE;
+ buf += MPEG2_TS_SOURCE_PACKET_SIZE;
}
}
out:
@@ -87,15 +90,20 @@ static inline struct node_entry *node_of(struct firedtv *fdtv)
return container_of(fdtv->device, struct unit_directory, device)->ne;
}
-static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg)
+static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
{
- return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data,
- (__force quadlet_t)arg);
+ int ret;
+
+ ret = hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP,
+ (__force quadlet_t *)&data[1], (__force quadlet_t)data[0]);
+ data[0] = data[1];
+
+ return ret;
}
-static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len)
+static int node_read(struct firedtv *fdtv, u64 addr, void *data)
{
- return hpsb_node_read(node_of(fdtv), addr, data, len);
+ return hpsb_node_read(node_of(fdtv), addr, data, 4);
}
static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
@@ -212,6 +220,7 @@ static int node_probe(struct device *dev)
goto fail;
avc_register_remote_control(fdtv);
+
return 0;
fail:
spin_lock_irq(&node_list_lock);
@@ -220,6 +229,7 @@ fail:
fdtv_unregister_rc(fdtv);
fail_free:
kfree(fdtv);
+
return err;
}
@@ -233,10 +243,9 @@ static int node_remove(struct device *dev)
list_del(&fdtv->list);
spin_unlock_irq(&node_list_lock);
- cancel_work_sync(&fdtv->remote_ctrl_work);
fdtv_unregister_rc(fdtv);
-
kfree(fdtv);
+
return 0;
}
@@ -252,6 +261,7 @@ static int node_update(struct unit_directory *ud)
static struct hpsb_protocol_driver fdtv_driver = {
.name = "firedtv",
+ .id_table = fdtv_id_table,
.update = node_update,
.driver = {
.probe = node_probe,
@@ -264,12 +274,11 @@ static struct hpsb_highlevel fdtv_highlevel = {
.fcp_request = fcp_request,
};
-int __init fdtv_1394_init(struct ieee1394_device_id id_table[])
+int __init fdtv_1394_init(void)
{
int ret;
hpsb_register_highlevel(&fdtv_highlevel);
- fdtv_driver.id_table = id_table;
ret = hpsb_register_protocol(&fdtv_driver);
if (ret) {
printk(KERN_ERR "firedtv: failed to register protocol\n");
diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c
index 485d061319a..50c42a4b972 100644
--- a/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/drivers/media/dvb/firewire/firedtv-avc.c
@@ -1236,14 +1236,14 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len)
#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL
-static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len)
+static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data)
{
int ret;
if (mutex_lock_interruptible(&fdtv->avc_mutex))
return -EINTR;
- ret = fdtv->backend->read(fdtv, addr, buf, len);
+ ret = fdtv->backend->read(fdtv, addr, data);
if (ret < 0)
dev_err(fdtv->device, "CMP: read I/O error\n");
@@ -1251,14 +1251,14 @@ static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len)
return ret;
}
-static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg)
+static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
{
int ret;
if (mutex_lock_interruptible(&fdtv->avc_mutex))
return -EINTR;
- ret = fdtv->backend->lock(fdtv, addr, data, arg);
+ ret = fdtv->backend->lock(fdtv, addr, data);
if (ret < 0)
dev_err(fdtv->device, "CMP: lock I/O error\n");
@@ -1288,25 +1288,25 @@ static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift)
int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel)
{
- __be32 old_opcr, opcr;
+ __be32 old_opcr, opcr[2];
u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);
int attempts = 0;
int ret;
- ret = cmp_read(fdtv, &opcr, opcr_address, 4);
+ ret = cmp_read(fdtv, opcr_address, opcr);
if (ret < 0)
return ret;
repeat:
- if (!get_opcr_online(opcr)) {
+ if (!get_opcr_online(*opcr)) {
dev_err(fdtv->device, "CMP: output offline\n");
return -EBUSY;
}
- old_opcr = opcr;
+ old_opcr = *opcr;
- if (get_opcr_p2p_connections(opcr)) {
- if (get_opcr_channel(opcr) != channel) {
+ if (get_opcr_p2p_connections(*opcr)) {
+ if (get_opcr_channel(*opcr) != channel) {
dev_err(fdtv->device, "CMP: cannot change channel\n");
return -EBUSY;
}
@@ -1314,11 +1314,11 @@ repeat:
/* We don't allocate isochronous resources. */
} else {
- set_opcr_channel(&opcr, channel);
- set_opcr_data_rate(&opcr, 2); /* S400 */
+ set_opcr_channel(opcr, channel);
+ set_opcr_data_rate(opcr, 2); /* S400 */
/* FIXME: this is for the worst case - optimize */
- set_opcr_overhead_id(&opcr, 0);
+ set_opcr_overhead_id(opcr, 0);
/*
* FIXME: allocate isochronous channel and bandwidth at IRM
@@ -1326,13 +1326,16 @@ repeat:
*/
}
- set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1);
+ set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1);
- ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr);
+ opcr[1] = *opcr;
+ opcr[0] = old_opcr;
+
+ ret = cmp_lock(fdtv, opcr_address, opcr);
if (ret < 0)
return ret;
- if (old_opcr != opcr) {
+ if (old_opcr != *opcr) {
/*
* FIXME: if old_opcr.P2P_Connections > 0,
* deallocate isochronous channel and bandwidth at IRM
@@ -1350,27 +1353,30 @@ repeat:
void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel)
{
- __be32 old_opcr, opcr;
+ __be32 old_opcr, opcr[2];
u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);
int attempts = 0;
- if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0)
+ if (cmp_read(fdtv, opcr_address, opcr) < 0)
return;
repeat:
- if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) ||
- get_opcr_channel(opcr) != channel) {
+ if (!get_opcr_online(*opcr) || !get_opcr_p2p_connections(*opcr) ||
+ get_opcr_channel(*opcr) != channel) {
dev_err(fdtv->device, "CMP: no connection to break\n");
return;
}
- old_opcr = opcr;
- set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1);
+ old_opcr = *opcr;
+ set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) - 1);
+
+ opcr[1] = *opcr;
+ opcr[0] = old_opcr;
- if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr) < 0)
+ if (cmp_lock(fdtv, opcr_address, opcr) < 0)
return;
- if (old_opcr != opcr) {
+ if (old_opcr != *opcr) {
/*
* FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last
* owner, deallocate isochronous channel and bandwidth at IRM
diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c
index 5742fde79d9..fc9996c13e1 100644
--- a/drivers/media/dvb/firewire/firedtv-dvb.c
+++ b/drivers/media/dvb/firewire/firedtv-dvb.c
@@ -297,7 +297,7 @@ struct firedtv *fdtv_alloc(struct device *dev,
#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d
#define AVC_SW_VERSION_ENTRY 0x010001
-static struct ieee1394_device_id fdtv_id_table[] = {
+const struct ieee1394_device_id fdtv_id_table[] = {
{
/* FloppyDTV S/CI and FloppyDTV S2 */
.match_flags = MATCH_FLAGS,
@@ -346,12 +346,23 @@ MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table);
static int __init fdtv_init(void)
{
- return fdtv_1394_init(fdtv_id_table);
+ int ret;
+
+ ret = fdtv_fw_init();
+ if (ret < 0)
+ return ret;
+
+ ret = fdtv_1394_init();
+ if (ret < 0)
+ fdtv_fw_exit();
+
+ return ret;
}
static void __exit fdtv_exit(void)
{
fdtv_1394_exit();
+ fdtv_fw_exit();
}
module_init(fdtv_init);
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c
new file mode 100644
index 00000000000..fe44789ab03
--- /dev/null
+++ b/drivers/media/dvb/firewire/firedtv-fw.c
@@ -0,0 +1,376 @@
+/*
+ * FireDTV driver -- firewire I/O backend
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/page.h>
+
+#include <dvb_demux.h>
+
+#include "firedtv.h"
+
+static LIST_HEAD(node_list);
+static DEFINE_SPINLOCK(node_list_lock);
+
+static inline struct fw_device *device_of(struct firedtv *fdtv)
+{
+ return fw_device(fdtv->device->parent);
+}
+
+static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len,
+ int tcode)
+{
+ struct fw_device *device = device_of(fdtv);
+ int rcode, generation = device->generation;
+
+ smp_rmb(); /* node_id vs. generation */
+
+ rcode = fw_run_transaction(device->card, tcode, device->node_id,
+ generation, device->max_speed, addr, data, len);
+
+ return rcode != RCODE_COMPLETE ? -EIO : 0;
+}
+
+static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
+{
+ return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP);
+}
+
+static int node_read(struct firedtv *fdtv, u64 addr, void *data)
+{
+ return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST);
+}
+
+static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
+{
+ return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST);
+}
+
+#define ISO_HEADER_SIZE 4
+#define CIP_HEADER_SIZE 8
+#define MPEG2_TS_HEADER_SIZE 4
+#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188)
+
+#define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */
+#define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE)
+#define N_PACKETS 64 /* buffer size */
+#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE)
+#define IRQ_INTERVAL 16
+
+struct firedtv_receive_context {
+ struct fw_iso_context *context;
+ struct fw_iso_buffer buffer;
+ int interrupt_packet;
+ int current_packet;
+ char *pages[N_PAGES];
+};
+
+static int queue_iso(struct firedtv_receive_context *ctx, int index)
+{
+ struct fw_iso_packet p;
+
+ p.payload_length = MAX_PACKET_SIZE;
+ p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1));
+ p.skip = 0;
+ p.header_length = ISO_HEADER_SIZE;
+
+ return fw_iso_context_queue(ctx->context, &p, &ctx->buffer,
+ index * MAX_PACKET_SIZE);
+}
+
+static void handle_iso(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
+{
+ struct firedtv *fdtv = data;
+ struct firedtv_receive_context *ctx = fdtv->backend_data;
+ __be32 *h, *h_end;
+ int length, err, i = ctx->current_packet;
+ char *p, *p_end;
+
+ for (h = header, h_end = h + header_length / 4; h < h_end; h++) {
+ length = be32_to_cpup(h) >> 16;
+ if (unlikely(length > MAX_PACKET_SIZE)) {
+ dev_err(fdtv->device, "length = %d\n", length);
+ length = MAX_PACKET_SIZE;
+ }
+
+ p = ctx->pages[i / PACKETS_PER_PAGE]
+ + (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE;
+ p_end = p + length;
+
+ for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end;
+ p += MPEG2_TS_SOURCE_PACKET_SIZE)
+ dvb_dmx_swfilter_packets(&fdtv->demux, p, 1);
+
+ err = queue_iso(ctx, i);
+ if (unlikely(err))
+ dev_err(fdtv->device, "requeue failed\n");
+
+ i = (i + 1) & (N_PACKETS - 1);
+ }
+ ctx->current_packet = i;
+}
+
+static int start_iso(struct firedtv *fdtv)
+{
+ struct firedtv_receive_context *ctx;
+ struct fw_device *device = device_of(fdtv);
+ int i, err;
+
+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->context = fw_iso_context_create(device->card,
+ FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel,
+ device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv);
+ if (IS_ERR(ctx->context)) {
+ err = PTR_ERR(ctx->context);
+ goto fail_free;
+ }
+
+ err = fw_iso_buffer_init(&ctx->buffer, device->card,
+ N_PAGES, DMA_FROM_DEVICE);
+ if (err)
+ goto fail_context_destroy;
+
+ ctx->interrupt_packet = 0;
+ ctx->current_packet = 0;
+
+ for (i = 0; i < N_PAGES; i++)
+ ctx->pages[i] = page_address(ctx->buffer.pages[i]);
+
+ for (i = 0; i < N_PACKETS; i++) {
+ err = queue_iso(ctx, i);
+ if (err)
+ goto fail;
+ }
+
+ err = fw_iso_context_start(ctx->context, -1, 0,
+ FW_ISO_CONTEXT_MATCH_ALL_TAGS);
+ if (err)
+ goto fail;
+
+ fdtv->backend_data = ctx;
+
+ return 0;
+fail:
+ fw_iso_buffer_destroy(&ctx->buffer, device->card);
+fail_context_destroy:
+ fw_iso_context_destroy(ctx->context);
+fail_free:
+ kfree(ctx);
+
+ return err;
+}
+
+static void stop_iso(struct firedtv *fdtv)
+{
+ struct firedtv_receive_context *ctx = fdtv->backend_data;
+
+ fw_iso_context_stop(ctx->context);
+ fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card);
+ fw_iso_context_destroy(ctx->context);
+ kfree(ctx);
+}
+
+static const struct firedtv_backend backend = {
+ .lock = node_lock,
+ .read = node_read,
+ .write = node_write,
+ .start_iso = start_iso,
+ .stop_iso = stop_iso,
+};
+
+static void handle_fcp(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ int speed, unsigned long long offset,
+ void *payload, size_t length, void *callback_data)
+{
+ struct firedtv *f, *fdtv = NULL;
+ struct fw_device *device;
+ unsigned long flags;
+ int su;
+
+ if ((tcode != TCODE_WRITE_QUADLET_REQUEST &&
+ tcode != TCODE_WRITE_BLOCK_REQUEST) ||
+ offset != CSR_REGISTER_BASE + CSR_FCP_RESPONSE ||
+ length == 0 ||
+ (((u8 *)payload)[0] & 0xf0) != 0) {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+
+ su = ((u8 *)payload)[1] & 0x7;
+
+ spin_lock_irqsave(&node_list_lock, flags);
+ list_for_each_entry(f, &node_list, list) {
+ device = device_of(f);
+ if (device->generation != generation)
+ continue;
+
+ smp_rmb(); /* node_id vs. generation */
+
+ if (device->card == card &&
+ device->node_id == source &&
+ (f->subunit == su || (f->subunit == 0 && su == 0x7))) {
+ fdtv = f;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&node_list_lock, flags);
+
+ if (fdtv) {
+ avc_recv(fdtv, payload, length);
+ fw_send_response(card, request, RCODE_COMPLETE);
+ }
+}
+
+static struct fw_address_handler fcp_handler = {
+ .length = CSR_FCP_END - CSR_FCP_RESPONSE,
+ .address_callback = handle_fcp,
+};
+
+static const struct fw_address_region fcp_region = {
+ .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE,
+ .end = CSR_REGISTER_BASE + CSR_FCP_END,
+};
+
+/* Adjust the template string if models with longer names appear. */
+#define MAX_MODEL_NAME_LEN ((int)DIV_ROUND_UP(sizeof("FireDTV ????"), 4))
+
+static size_t model_name(u32 *directory, __be32 *buffer)
+{
+ struct fw_csr_iterator ci;
+ int i, length, key, value, last_key = 0;
+ u32 *block = NULL;
+
+ fw_csr_iterator_init(&ci, directory);
+ while (fw_csr_iterator_next(&ci, &key, &value)) {
+ if (last_key == CSR_MODEL &&
+ key == (CSR_DESCRIPTOR | CSR_LEAF))
+ block = ci.p - 1 + value;
+ last_key = key;
+ }
+
+ if (block == NULL)
+ return 0;
+
+ length = min((int)(block[0] >> 16) - 2, MAX_MODEL_NAME_LEN);
+ if (length <= 0)
+ return 0;
+
+ /* fast-forward to text string */
+ block += 3;
+
+ for (i = 0; i < length; i++)
+ buffer[i] = cpu_to_be32(block[i]);
+
+ return length * 4;
+}
+
+static int node_probe(struct device *dev)
+{
+ struct firedtv *fdtv;
+ __be32 name[MAX_MODEL_NAME_LEN];
+ int name_len, err;
+
+ name_len = model_name(fw_unit(dev)->directory, name);
+
+ fdtv = fdtv_alloc(dev, &backend, (char *)name, name_len);
+ if (!fdtv)
+ return -ENOMEM;
+
+ err = fdtv_register_rc(fdtv, dev);
+ if (err)
+ goto fail_free;
+
+ spin_lock_irq(&node_list_lock);
+ list_add_tail(&fdtv->list, &node_list);
+ spin_unlock_irq(&node_list_lock);
+
+ err = avc_identify_subunit(fdtv);
+ if (err)
+ goto fail;
+
+ err = fdtv_dvb_register(fdtv);
+ if (err)
+ goto fail;
+
+ avc_register_remote_control(fdtv);
+
+ return 0;
+fail:
+ spin_lock_irq(&node_list_lock);
+ list_del(&fdtv->list);
+ spin_unlock_irq(&node_list_lock);
+ fdtv_unregister_rc(fdtv);
+fail_free:
+ kfree(fdtv);
+
+ return err;
+}
+
+static int node_remove(struct device *dev)
+{
+ struct firedtv *fdtv = dev_get_drvdata(dev);
+
+ fdtv_dvb_unregister(fdtv);
+
+ spin_lock_irq(&node_list_lock);
+ list_del(&fdtv->list);
+ spin_unlock_irq(&node_list_lock);
+
+ fdtv_unregister_rc(fdtv);
+
+ kfree(fdtv);
+ return 0;
+}
+
+static void node_update(struct fw_unit *unit)
+{
+ struct firedtv *fdtv = dev_get_drvdata(&unit->device);
+
+ if (fdtv->isochannel >= 0)
+ cmp_establish_pp_connection(fdtv, fdtv->subunit,
+ fdtv->isochannel);
+}
+
+static struct fw_driver fdtv_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "firedtv",
+ .bus = &fw_bus_type,
+ .probe = node_probe,
+ .remove = node_remove,
+ },
+ .update = node_update,
+ .id_table = fdtv_id_table,
+};
+
+int __init fdtv_fw_init(void)
+{
+ int ret;
+
+ ret = fw_core_add_address_handler(&fcp_handler, &fcp_region);
+ if (ret < 0)
+ return ret;
+
+ return driver_register(&fdtv_driver.driver);
+}
+
+void fdtv_fw_exit(void)
+{
+ driver_unregister(&fdtv_driver.driver);
+ fw_core_remove_address_handler(&fcp_handler);
+}
diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c
index 27bca2e283d..599d66e5843 100644
--- a/drivers/media/dvb/firewire/firedtv-rc.c
+++ b/drivers/media/dvb/firewire/firedtv-rc.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/workqueue.h>
#include "firedtv.h"
@@ -163,6 +164,7 @@ fail:
void fdtv_unregister_rc(struct firedtv *fdtv)
{
+ cancel_work_sync(&fdtv->remote_ctrl_work);
kfree(fdtv->remote_ctrl_dev->keycode);
input_unregister_device(fdtv->remote_ctrl_dev);
}
diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h
index d48530b81e6..35080dbb3c6 100644
--- a/drivers/media/dvb/firewire/firedtv.h
+++ b/drivers/media/dvb/firewire/firedtv.h
@@ -16,6 +16,7 @@
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
#include <linux/list.h>
+#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>
@@ -72,8 +73,8 @@ struct input_dev;
struct firedtv;
struct firedtv_backend {
- int (*lock)(struct firedtv *fdtv, u64 addr, void *data, __be32 arg);
- int (*read)(struct firedtv *fdtv, u64 addr, void *data, size_t len);
+ int (*lock)(struct firedtv *fdtv, u64 addr, __be32 data[]);
+ int (*read)(struct firedtv *fdtv, u64 addr, void *data);
int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len);
int (*start_iso)(struct firedtv *fdtv);
void (*stop_iso)(struct firedtv *fdtv);
@@ -119,10 +120,10 @@ struct firedtv {
/* firedtv-1394.c */
#ifdef CONFIG_DVB_FIREDTV_IEEE1394
-int fdtv_1394_init(struct ieee1394_device_id id_table[]);
+int fdtv_1394_init(void);
void fdtv_1394_exit(void);
#else
-static inline int fdtv_1394_init(struct ieee1394_device_id it[]) { return 0; }
+static inline int fdtv_1394_init(void) { return 0; }
static inline void fdtv_1394_exit(void) {}
#endif
@@ -163,10 +164,20 @@ struct firedtv *fdtv_alloc(struct device *dev,
const struct firedtv_backend *backend,
const char *name, size_t name_len);
extern const char *fdtv_model_names[];
+extern const struct ieee1394_device_id fdtv_id_table[];
/* firedtv-fe.c */
void fdtv_frontend_init(struct firedtv *fdtv);
+/* firedtv-fw.c */
+#ifdef CONFIG_DVB_FIREDTV_FIREWIRE
+int fdtv_fw_init(void);
+void fdtv_fw_exit(void);
+#else
+static inline int fdtv_fw_init(void) { return 0; }
+static inline void fdtv_fw_exit(void) {}
+#endif
+
/* firedtv-rc.c */
#ifdef CONFIG_DVB_FIREDTV_INPUT
int fdtv_register_rc(struct firedtv *fdtv, struct device *dev);
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index d7c4837fa71..58aac018f10 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -201,6 +201,13 @@ config DVB_SI21XX
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_DS3000
+ tristate "Montage Tehnology DS3000 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
comment "DVB-T (terrestrial) frontends"
depends on DVB_CORE
@@ -342,6 +349,13 @@ config DVB_AF9013
help
Say Y when you want to support this frontend.
+config DVB_EC100
+ tristate "E3C EC100"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
@@ -557,6 +571,13 @@ config DVB_LGS8GXX
help
A DMB-TH tuner module. Say Y when you want to support this frontend.
+config DVB_ATBM8830
+ tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
comment "Tools to develop new frontends"
config DVB_DUMMY_FE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 3523767e7a7..823482535d1 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o
+obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o
obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
obj-$(CONFIG_DVB_AF9013) += af9013.o
obj-$(CONFIG_DVB_CX24116) += cx24116.o
@@ -76,3 +77,5 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o
obj-$(CONFIG_DVB_STV090x) += stv090x.o
obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
obj-$(CONFIG_DVB_ISL6423) += isl6423.o
+obj-$(CONFIG_DVB_EC100) += ec100.o
+obj-$(CONFIG_DVB_DS3000) += ds3000.o
diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c
new file mode 100644
index 00000000000..59881a5944e
--- /dev/null
+++ b/drivers/media/dvb/frontends/atbm8830.c
@@ -0,0 +1,495 @@
+/*
+ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ * ATBM8830, ATBM8831
+ *
+ * Copyright (C) 2009 David T.L. Wong <davidtlwong@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/div64.h>
+#include "dvb_frontend.h"
+
+#include "atbm8830.h"
+#include "atbm8830_priv.h"
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "atbm8830: " args); \
+ } while (0)
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data)
+{
+ int ret = 0;
+ u8 dev_addr;
+ u8 buf1[] = { reg >> 8, reg & 0xFF };
+ u8 buf2[] = { data };
+ struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
+ struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 };
+
+ dev_addr = priv->config->demod_address;
+ msg1.addr = dev_addr;
+ msg2.addr = dev_addr;
+
+ if (debug >= 2)
+ printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n",
+ __func__, reg, data);
+
+ ret = i2c_transfer(priv->i2c, &msg1, 1);
+ if (ret != 1)
+ return -EIO;
+
+ ret = i2c_transfer(priv->i2c, &msg2, 1);
+ return (ret != 1) ? -EIO : 0;
+}
+
+static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data)
+{
+ int ret;
+ u8 dev_addr;
+
+ u8 buf1[] = { reg >> 8, reg & 0xFF };
+ u8 buf2[] = { 0 };
+ struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
+ struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 };
+
+ dev_addr = priv->config->demod_address;
+ msg1.addr = dev_addr;
+ msg2.addr = dev_addr;
+
+ ret = i2c_transfer(priv->i2c, &msg1, 1);
+ if (ret != 1) {
+ dprintk(KERN_DEBUG "%s: error reg=0x%04x, ret=%i\n",
+ __func__, reg, ret);
+ return -EIO;
+ }
+
+ ret = i2c_transfer(priv->i2c, &msg2, 1);
+ if (ret != 1)
+ return -EIO;
+
+ *p_data = buf2[0];
+ if (debug >= 2)
+ printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n",
+ __func__, reg, buf2[0]);
+
+ return 0;
+}
+
+/* Lock register latch so that multi-register read is atomic */
+static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock)
+{
+ return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0);
+}
+
+static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
+{
+ u32 val;
+ u64 t;
+
+ /* 0x100000 * freq / 30.4MHz */
+ t = (u64)0x100000 * freq;
+ do_div(t, 30400);
+ val = t;
+
+ atbm8830_write_reg(priv, REG_OSC_CLK, val);
+ atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8);
+ atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16);
+
+ return 0;
+}
+
+static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
+{
+
+ u32 fs = priv->config->osc_clk_freq;
+ u64 t;
+ u32 val;
+ u8 dat;
+
+ if (freq != 0) {
+ /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */
+ t = (u64) 2 * 31416 * (freq - fs);
+ t <<= 22;
+ do_div(t, fs);
+ do_div(t, 1000);
+ val = t;
+
+ atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1);
+ atbm8830_write_reg(priv, REG_IF_FREQ, val);
+ atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8);
+ atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16);
+
+ atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
+ dat &= 0xFC;
+ atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
+ } else {
+ /* Zero IF */
+ atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0);
+
+ atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
+ dat &= 0xFC;
+ dat |= 0x02;
+ atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
+
+ if (priv->config->zif_swap_iq)
+ atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03);
+ else
+ atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01);
+ }
+
+ return 0;
+}
+
+static int is_locked(struct atbm_state *priv, u8 *locked)
+{
+ u8 status;
+
+ atbm8830_read_reg(priv, REG_LOCK_STATUS, &status);
+
+ if (locked != NULL)
+ *locked = (status == 1);
+ return 0;
+}
+
+
+static int set_static_channel_mode(struct atbm_state *priv)
+{
+ int i;
+
+ for (i = 0; i < 5; i++)
+ atbm8830_write_reg(priv, 0x099B + i, 0x08);
+
+ atbm8830_write_reg(priv, 0x095B, 0x7F);
+ atbm8830_write_reg(priv, 0x09CB, 0x01);
+ atbm8830_write_reg(priv, 0x09CC, 0x7F);
+ atbm8830_write_reg(priv, 0x09CD, 0x7F);
+ atbm8830_write_reg(priv, 0x0E01, 0x20);
+
+ /* For single carrier */
+ atbm8830_write_reg(priv, 0x0B03, 0x0A);
+ atbm8830_write_reg(priv, 0x0935, 0x10);
+ atbm8830_write_reg(priv, 0x0936, 0x08);
+ atbm8830_write_reg(priv, 0x093E, 0x08);
+ atbm8830_write_reg(priv, 0x096E, 0x06);
+
+ /* frame_count_max0 */
+ atbm8830_write_reg(priv, 0x0B09, 0x00);
+ /* frame_count_max1 */
+ atbm8830_write_reg(priv, 0x0B0A, 0x08);
+
+ return 0;
+}
+
+static int set_ts_config(struct atbm_state *priv)
+{
+ const struct atbm8830_config *cfg = priv->config;
+
+ /*Set parallel/serial ts mode*/
+ atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0);
+ atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0);
+ /*Set ts sampling edge*/
+ atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE,
+ cfg->ts_sampling_edge ? 1 : 0);
+ /*Set ts clock freerun*/
+ atbm8830_write_reg(priv, REG_TS_CLK_FREERUN,
+ cfg->ts_clk_gated ? 0 : 1);
+
+ return 0;
+}
+
+static int atbm8830_init(struct dvb_frontend *fe)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ const struct atbm8830_config *cfg = priv->config;
+
+ /*Set oscillator frequency*/
+ set_osc_freq(priv, cfg->osc_clk_freq);
+
+ /*Set IF frequency*/
+ set_if_freq(priv, cfg->if_freq);
+
+
+ /*Set static channel mode*/
+ set_static_channel_mode(priv);
+
+ set_ts_config(priv);
+ /*Turn off DSP reset*/
+ atbm8830_write_reg(priv, 0x000A, 0);
+
+ /*SW version test*/
+ atbm8830_write_reg(priv, 0x020C, 11);
+
+ /* Run */
+ atbm8830_write_reg(priv, REG_DEMOD_RUN, 1);
+
+ return 0;
+}
+
+
+static void atbm8830_release(struct dvb_frontend *fe)
+{
+ struct atbm_state *state = fe->demodulator_priv;
+ dprintk("%s\n", __func__);
+
+ kfree(state);
+}
+
+static int atbm8830_set_fe(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fe_params)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ int i;
+ u8 locked = 0;
+ dprintk("%s\n", __func__);
+
+ /* set frequency */
+ if (fe->ops.tuner_ops.set_params) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.set_params(fe, fe_params);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ /* start auto lock */
+ for (i = 0; i < 10; i++) {
+ mdelay(100);
+ dprintk("Try %d\n", i);
+ is_locked(priv, &locked);
+ if (locked != 0) {
+ dprintk("ATBM8830 locked!\n");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int atbm8830_get_fe(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fe_params)
+{
+ dprintk("%s\n", __func__);
+
+ /* TODO: get real readings from device */
+ /* inversion status */
+ fe_params->inversion = INVERSION_OFF;
+
+ /* bandwidth */
+ fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+
+ fe_params->u.ofdm.code_rate_HP = FEC_AUTO;
+ fe_params->u.ofdm.code_rate_LP = FEC_AUTO;
+
+ fe_params->u.ofdm.constellation = QAM_AUTO;
+
+ /* transmission mode */
+ fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+
+ /* guard interval */
+ fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+
+ /* hierarchy */
+ fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+
+ return 0;
+}
+
+static int atbm8830_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 0;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ u8 locked = 0;
+ u8 agc_locked = 0;
+
+ dprintk("%s\n", __func__);
+ *fe_status = 0;
+
+ is_locked(priv, &locked);
+ if (locked) {
+ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
+
+ atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked);
+ dprintk("AGC Lock: %d\n", agc_locked);
+
+ return 0;
+}
+
+static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ u32 frame_err;
+ u8 t;
+
+ dprintk("%s\n", __func__);
+
+ atbm8830_reglatch_lock(priv, 1);
+
+ atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t);
+ frame_err = t & 0x7F;
+ frame_err <<= 8;
+ atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t);
+ frame_err |= t;
+
+ atbm8830_reglatch_lock(priv, 0);
+
+ *ber = frame_err * 100 / 32767;
+
+ dprintk("%s: ber=0x%x\n", __func__, *ber);
+ return 0;
+}
+
+static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ u32 pwm;
+ u8 t;
+
+ dprintk("%s\n", __func__);
+ atbm8830_reglatch_lock(priv, 1);
+
+ atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t);
+ pwm = t & 0x03;
+ pwm <<= 8;
+ atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t);
+ pwm |= t;
+
+ atbm8830_reglatch_lock(priv, 0);
+
+ dprintk("AGC PWM = 0x%02X\n", pwm);
+ pwm = 0x400 - pwm;
+
+ *signal = pwm * 0x10000 / 0x400;
+
+ return 0;
+}
+
+static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ dprintk("%s\n", __func__);
+ *snr = 0;
+ return 0;
+}
+
+static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ dprintk("%s\n", __func__);
+ *ucblocks = 0;
+ return 0;
+}
+
+static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+
+ return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0);
+}
+
+static struct dvb_frontend_ops atbm8830_ops = {
+ .info = {
+ .name = "AltoBeam ATBM8830/8831 DMB-TH",
+ .type = FE_OFDM,
+ .frequency_min = 474000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 10000,
+ .caps =
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO
+ },
+
+ .release = atbm8830_release,
+
+ .init = atbm8830_init,
+ .sleep = NULL,
+ .write = NULL,
+ .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl,
+
+ .set_frontend = atbm8830_set_fe,
+ .get_frontend = atbm8830_get_fe,
+ .get_tune_settings = atbm8830_get_tune_settings,
+
+ .read_status = atbm8830_read_status,
+ .read_ber = atbm8830_read_ber,
+ .read_signal_strength = atbm8830_read_signal_strength,
+ .read_snr = atbm8830_read_snr,
+ .read_ucblocks = atbm8830_read_ucblocks,
+};
+
+struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct atbm_state *priv = NULL;
+ u8 data = 0;
+
+ dprintk("%s()\n", __func__);
+
+ if (config == NULL || i2c == NULL)
+ return NULL;
+
+ priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL);
+ if (priv == NULL)
+ goto error_out;
+
+ priv->config = config;
+ priv->i2c = i2c;
+
+ /* check if the demod is there */
+ if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) {
+ dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n",
+ __func__, priv->config->demod_address);
+ goto error_out;
+ }
+ dprintk("atbm8830 chip id: 0x%02X\n", data);
+
+ memcpy(&priv->frontend.ops, &atbm8830_ops,
+ sizeof(struct dvb_frontend_ops));
+ priv->frontend.demodulator_priv = priv;
+
+ atbm8830_init(&priv->frontend);
+
+ atbm8830_i2c_gate_ctrl(&priv->frontend, 1);
+
+ return &priv->frontend;
+
+error_out:
+ dprintk("%s() error_out\n", __func__);
+ kfree(priv);
+ return NULL;
+
+}
+EXPORT_SYMBOL(atbm8830_attach);
+
+MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver");
+MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/atbm8830.h b/drivers/media/dvb/frontends/atbm8830.h
new file mode 100644
index 00000000000..e8149f39330
--- /dev/null
+++ b/drivers/media/dvb/frontends/atbm8830.h
@@ -0,0 +1,76 @@
+/*
+ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ * ATBM8830, ATBM8831
+ *
+ * Copyright (C) 2009 David T.L. Wong <davidtlwong@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ATBM8830_H__
+#define __ATBM8830_H__
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#define ATBM8830_PROD_8830 0
+#define ATBM8830_PROD_8831 1
+
+struct atbm8830_config {
+
+ /* product type */
+ u8 prod;
+
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* parallel or serial transport stream */
+ u8 serial_ts;
+
+ /* transport stream clock output only when receving valid stream */
+ u8 ts_clk_gated;
+
+ /* Decoder sample TS data at rising edge of clock */
+ u8 ts_sampling_edge;
+
+ /* Oscillator clock frequency */
+ u32 osc_clk_freq; /* in kHz */
+
+ /* IF frequency */
+ u32 if_freq; /* in kHz */
+
+ /* Swap I/Q for zero IF */
+ u8 zif_swap_iq;
+
+ /* Tuner AGC settings */
+ u8 agc_min;
+ u8 agc_max;
+ u8 agc_hold_loop;
+};
+
+#if defined(CONFIG_DVB_ATBM8830) || \
+ (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE))
+extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+ struct i2c_adapter *i2c) {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_ATBM8830 */
+
+#endif /* __ATBM8830_H__ */
diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb/frontends/atbm8830_priv.h
new file mode 100644
index 00000000000..ce960f76092
--- /dev/null
+++ b/drivers/media/dvb/frontends/atbm8830_priv.h
@@ -0,0 +1,75 @@
+/*
+ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ * ATBM8830, ATBM8831
+ *
+ * Copyright (C) 2009 David T.L. Wong <davidtlwong@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ATBM8830_PRIV_H
+#define __ATBM8830_PRIV_H
+
+struct atbm_state {
+ struct i2c_adapter *i2c;
+ /* configuration settings */
+ const struct atbm8830_config *config;
+ struct dvb_frontend frontend;
+};
+
+#define REG_CHIP_ID 0x0000
+#define REG_TUNER_BASEBAND 0x0001
+#define REG_DEMOD_RUN 0x0004
+#define REG_DSP_RESET 0x0005
+#define REG_RAM_RESET 0x0006
+#define REG_ADC_RESET 0x0007
+#define REG_TSPORT_RESET 0x0008
+#define REG_BLKERR_POL 0x000C
+#define REG_I2C_GATE 0x0103
+#define REG_TS_SAMPLE_EDGE 0x0301
+#define REG_TS_PKT_LEN_204 0x0302
+#define REG_TS_PKT_LEN_AUTO 0x0303
+#define REG_TS_SERIAL 0x0305
+#define REG_TS_CLK_FREERUN 0x0306
+#define REG_TS_VALID_MODE 0x0307
+#define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */
+
+#define REG_TS_ERRBIT_USE 0x030C
+#define REG_LOCK_STATUS 0x030D
+#define REG_ADC_CONFIG 0x0602
+#define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */
+#define REG_DETECTED_PN_MODE 0x082D
+#define REG_READ_LATCH 0x084D
+#define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */
+#define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */
+#define REG_BYPASS_CCI 0x0A06
+#define REG_ANALOG_LUMA_DETECTED 0x0A25
+#define REG_ANALOG_AUDIO_DETECTED 0x0A26
+#define REG_ANALOG_CHROMA_DETECTED 0x0A39
+#define REG_FRAME_ERR_CNT 0x0B04
+#define REG_USE_EXT_ADC 0x0C00
+#define REG_SWAP_I_Q 0x0C01
+#define REG_TPS_MANUAL 0x0D01
+#define REG_TPS_CONFIG 0x0D02
+#define REG_BYPASS_DEINTERLEAVER 0x0E00
+#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */
+#define REG_AGC_MIN 0x1020
+#define REG_AGC_MAX 0x1023
+#define REG_AGC_LOCK 0x1027
+#define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */
+#define REG_AGC_HOLD_LOOP 0x1031
+
+#endif
+
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index 74981ee923c..2dc2723b724 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -23,7 +23,6 @@
/* Developer notes:
*
* VBI support is not yet working
- * Saturation and hue setting are not yet working
* Enough is implemented here for CVBS and S-Video inputs, but the actual
* analog demodulator code isn't implemented (not needed for xc5000 since it
* has its own demodulator and outputs CVBS)
@@ -236,8 +235,10 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)
state->contrast = 0x79;
au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80);
au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80);
+ state->saturation = 0x80;
au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00);
au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00);
+ state->hue = 0x00;
/* Other decoder registers */
au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00);
@@ -315,7 +316,7 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)
if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||
input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {
/* Despite what the table says, for the HVR-950q we still need
- to be in CVBS mode for the S-Video input (reason uknown). */
+ to be in CVBS mode for the S-Video input (reason unknown). */
/* filter_coef_type = 3; */
filter_coef_type = 5;
} else {
@@ -504,7 +505,19 @@ static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
ctrl->value);
break;
case V4L2_CID_SATURATION:
+ state->saturation = ctrl->value;
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH,
+ ctrl->value);
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH,
+ ctrl->value);
+ break;
case V4L2_CID_HUE:
+ state->hue = ctrl->value;
+ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH,
+ ctrl->value >> 8);
+ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH,
+ ctrl->value & 0xFF);
+ break;
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
@@ -534,7 +547,11 @@ static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
ctrl->value = state->contrast;
break;
case V4L2_CID_SATURATION:
+ ctrl->value = state->saturation;
+ break;
case V4L2_CID_HUE:
+ ctrl->value = state->hue;
+ break;
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
@@ -632,8 +649,9 @@ static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
case V4L2_CID_BRIGHTNESS:
return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
case V4L2_CID_HUE:
- /* Not yet implemented */
+ return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0);
default:
break;
}
diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h
index f328f2b3ad3..c74c4e72fe9 100644
--- a/drivers/media/dvb/frontends/au8522_priv.h
+++ b/drivers/media/dvb/frontends/au8522_priv.h
@@ -62,6 +62,8 @@ struct au8522_state {
u32 rev;
u8 brightness;
u8 contrast;
+ u8 saturation;
+ s16 hue;
};
/* These are routines shared by both the VSB/QAM demodulator and the analog
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
index ffbcfabd83f..00a4e8f0330 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -1,4 +1,4 @@
- /*
+/*
cx24110 - Single Chip Satellite Channel Receiver driver module
Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on
@@ -96,7 +96,7 @@ static struct {u8 reg; u8 data;} cx24110_regdata[]=
{0x42,0x00}, /* @ middle bytes " */
{0x43,0x00}, /* @ LSB " */
/* leave the carrier tracking loop parameters on default */
- /* leave the bit timing loop parameters at gefault */
+ /* leave the bit timing loop parameters at default */
{0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */
/* the cx24108 data sheet for symbol rates above 15MS/s */
{0x57,0x00}, /* @ Filter sigma delta enabled, positive */
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c
index 075b2b57cf0..e9ee55592fd 100644
--- a/drivers/media/dvb/frontends/cx24113.c
+++ b/drivers/media/dvb/frontends/cx24113.c
@@ -589,7 +589,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
info("detected CX24113 variant\n");
break;
case REV_CX24113:
- info("sucessfully detected\n");
+ info("successfully detected\n");
break;
default:
err("unsupported device id: %x\n", state->rev);
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c
index 136b9d2164d..ad4c8cfd809 100644
--- a/drivers/media/dvb/frontends/dib3000mb.c
+++ b/drivers/media/dvb/frontends/dib3000mb.c
@@ -154,7 +154,7 @@ static int dib3000mb_set_frontend(struct dvb_frontend* fe,
case BANDWIDTH_AUTO:
return -EOPNOTSUPP;
default:
- err("unkown bandwidth value.");
+ err("unknown bandwidth value.");
return -EINVAL;
}
}
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index 0781f94e05d..750ae61a20f 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -108,7 +108,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
outreg = 0;
fifo_threshold = 1792;
- smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1);
+ smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1);
dprintk( "setting output mode for demod %p to %d",
&state->demod, mode);
@@ -162,18 +162,19 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff)
if (state->div_force_off) {
dprintk( "diversity combination deactivated - forced by COFDM parameters");
onoff = 0;
- }
+ dib7000p_write_word(state, 207, 0);
+ } else
+ dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0));
+
state->div_state = (u8)onoff;
if (onoff) {
dib7000p_write_word(state, 204, 6);
dib7000p_write_word(state, 205, 16);
/* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
- dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0));
} else {
dib7000p_write_word(state, 204, 1);
dib7000p_write_word(state, 205, 0);
- dib7000p_write_word(state, 207, 0);
}
return 0;
@@ -1188,7 +1189,7 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
*stat |= FE_HAS_VITERBI;
if (lock & 0x0010)
*stat |= FE_HAS_SYNC;
- if (lock & 0x0008)
+ if ((lock & 0x0038) == 0x38)
*stat |= FE_HAS_LOCK;
return 0;
@@ -1302,6 +1303,24 @@ struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum di
}
EXPORT_SYMBOL(dib7000p_get_i2c_master);
+int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 val = dib7000p_read_word(state, 235) & 0xffef;
+ val |= (onoff & 0x1) << 4;
+ dprintk("PID filter enabled %d", onoff);
+ return dib7000p_write_word(state, 235, val);
+}
+EXPORT_SYMBOL(dib7000p_pid_filter_ctrl);
+
+int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff);
+ return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0);
+}
+EXPORT_SYMBOL(dib7000p_pid_filter);
+
int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
{
struct dib7000p_state st = { .i2c_adap = i2c };
@@ -1314,8 +1333,10 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
/* designated i2c address */
new_addr = (0x40 + k) << 1;
st.i2c_addr = new_addr;
+ dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */
if (dib7000p_identify(&st) != 0) {
st.i2c_addr = default_addr;
+ dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */
if (dib7000p_identify(&st) != 0) {
dprintk("DiB7000P #%d: not identified\n", k);
return -EIO;
@@ -1372,6 +1393,8 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
demod->demodulator_priv = st;
memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
+ dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */
+
if (dib7000p_identify(st) != 0)
goto error;
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
index 02a4c82f0c7..805dd13a97e 100644
--- a/drivers/media/dvb/frontends/dib7000p.h
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -51,6 +51,8 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
+extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff);
+extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff);
#else
static inline
struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
@@ -95,6 +97,17 @@ static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap)
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
+static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
#endif
#endif
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c
index 852c790d09d..898400d331a 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb/frontends/dib8000.c
@@ -954,7 +954,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
u8 guard, crate, constellation, timeI;
u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 };
u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled
- const s16 *ncoeff, *ana_fe;
+ const s16 *ncoeff = NULL, *ana_fe;
u16 tmcc_pow = 0;
u16 coff_pow = 0x2800;
u16 init_prbs = 0xfff;
@@ -2121,7 +2121,7 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr)
else
result -= intlog10(2) * 10 * noise_exp - 100;
- *snr = result / (1 << 24);
+ *snr = result / ((1 << 24) / 10);
return 0;
}
@@ -2195,6 +2195,25 @@ struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000
EXPORT_SYMBOL(dib8000_get_i2c_master);
+int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+ struct dib8000_state *st = fe->demodulator_priv;
+ u16 val = dib8000_read_word(st, 299) & 0xffef;
+ val |= (onoff & 0x1) << 4;
+
+ dprintk("pid filter enabled %d", onoff);
+ return dib8000_write_word(st, 299, val);
+}
+EXPORT_SYMBOL(dib8000_pid_filter_ctrl);
+
+int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ struct dib8000_state *st = fe->demodulator_priv;
+ dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
+ return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0);
+}
+EXPORT_SYMBOL(dib8000_pid_filter);
+
static const struct dvb_frontend_ops dib8000_ops = {
.info = {
.name = "DiBcom 8000 ISDB-T",
diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h
index a86de340dd5..8c89482b738 100644
--- a/drivers/media/dvb/frontends/dib8000.h
+++ b/drivers/media/dvb/frontends/dib8000.h
@@ -44,6 +44,8 @@ extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u
extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value);
+extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff);
+extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff);
#else
static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg)
{
@@ -74,6 +76,18 @@ int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value)
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return -ENODEV;
}
+
+int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
#endif
#endif
diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c
index 01007553522..868b78bfae7 100644
--- a/drivers/media/dvb/frontends/drx397xD.c
+++ b/drivers/media/dvb/frontends/drx397xD.c
@@ -81,7 +81,7 @@ static struct {
#include "drx397xD_fw.h"
};
-/* use only with writer lock aquired */
+/* use only with writer lock acquired */
static void _drx_release_fw(struct drx397xD_state *s, enum fw_ix ix)
{
memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c
new file mode 100644
index 00000000000..cff3535566f
--- /dev/null
+++ b/drivers/media/dvb/frontends/ds3000.c
@@ -0,0 +1,1367 @@
+/*
+ Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver
+ Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+
+ Copyright (C) 2009 TurboSight.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "ds3000.h"
+
+static int debug;
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(args); \
+ } while (0)
+
+/* as of March 2009 current DS3000 firmware version is 1.78 */
+/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */
+#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw"
+
+#define DS3000_SAMPLE_RATE 96000 /* in kHz */
+#define DS3000_XTAL_FREQ 27000 /* in kHz */
+
+/* Register values to initialise the demod in DVB-S mode */
+static u8 ds3000_dvbs_init_tab[] = {
+ 0x23, 0x05,
+ 0x08, 0x03,
+ 0x0c, 0x00,
+ 0x21, 0x54,
+ 0x25, 0x82,
+ 0x27, 0x31,
+ 0x30, 0x08,
+ 0x31, 0x40,
+ 0x32, 0x32,
+ 0x33, 0x35,
+ 0x35, 0xff,
+ 0x3a, 0x00,
+ 0x37, 0x10,
+ 0x38, 0x10,
+ 0x39, 0x02,
+ 0x42, 0x60,
+ 0x4a, 0x40,
+ 0x4b, 0x04,
+ 0x4d, 0x91,
+ 0x5d, 0xc8,
+ 0x50, 0x77,
+ 0x51, 0x77,
+ 0x52, 0x36,
+ 0x53, 0x36,
+ 0x56, 0x01,
+ 0x63, 0x43,
+ 0x64, 0x30,
+ 0x65, 0x40,
+ 0x68, 0x26,
+ 0x69, 0x4c,
+ 0x70, 0x20,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x40,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x60,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x80,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0xa0,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x1f,
+ 0x76, 0x00,
+ 0x77, 0xd1,
+ 0x78, 0x0c,
+ 0x79, 0x80,
+ 0x7f, 0x04,
+ 0x7c, 0x00,
+ 0x80, 0x86,
+ 0x81, 0xa6,
+ 0x85, 0x04,
+ 0xcd, 0xf4,
+ 0x90, 0x33,
+ 0xa0, 0x44,
+ 0xc0, 0x18,
+ 0xc3, 0x10,
+ 0xc4, 0x08,
+ 0xc5, 0x80,
+ 0xc6, 0x80,
+ 0xc7, 0x0a,
+ 0xc8, 0x1a,
+ 0xc9, 0x80,
+ 0xfe, 0x92,
+ 0xe0, 0xf8,
+ 0xe6, 0x8b,
+ 0xd0, 0x40,
+ 0xf8, 0x20,
+ 0xfa, 0x0f,
+ 0xfd, 0x20,
+ 0xad, 0x20,
+ 0xae, 0x07,
+ 0xb8, 0x00,
+};
+
+/* Register values to initialise the demod in DVB-S2 mode */
+static u8 ds3000_dvbs2_init_tab[] = {
+ 0x23, 0x0f,
+ 0x08, 0x07,
+ 0x0c, 0x00,
+ 0x21, 0x54,
+ 0x25, 0x82,
+ 0x27, 0x31,
+ 0x30, 0x08,
+ 0x31, 0x32,
+ 0x32, 0x32,
+ 0x33, 0x35,
+ 0x35, 0xff,
+ 0x3a, 0x00,
+ 0x37, 0x10,
+ 0x38, 0x10,
+ 0x39, 0x02,
+ 0x42, 0x60,
+ 0x4a, 0x80,
+ 0x4b, 0x04,
+ 0x4d, 0x81,
+ 0x5d, 0x88,
+ 0x50, 0x36,
+ 0x51, 0x36,
+ 0x52, 0x36,
+ 0x53, 0x36,
+ 0x63, 0x60,
+ 0x64, 0x10,
+ 0x65, 0x10,
+ 0x68, 0x04,
+ 0x69, 0x29,
+ 0x70, 0x20,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x40,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x60,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x80,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0xa0,
+ 0x71, 0x70,
+ 0x72, 0x04,
+ 0x73, 0x00,
+ 0x70, 0x1f,
+ 0xa0, 0x44,
+ 0xc0, 0x08,
+ 0xc1, 0x10,
+ 0xc2, 0x08,
+ 0xc3, 0x10,
+ 0xc4, 0x08,
+ 0xc5, 0xf0,
+ 0xc6, 0xf0,
+ 0xc7, 0x0a,
+ 0xc8, 0x1a,
+ 0xc9, 0x80,
+ 0xca, 0x23,
+ 0xcb, 0x24,
+ 0xce, 0x74,
+ 0x90, 0x03,
+ 0x76, 0x80,
+ 0x77, 0x42,
+ 0x78, 0x0a,
+ 0x79, 0x80,
+ 0xad, 0x40,
+ 0xae, 0x07,
+ 0x7f, 0xd4,
+ 0x7c, 0x00,
+ 0x80, 0xa8,
+ 0x81, 0xda,
+ 0x7c, 0x01,
+ 0x80, 0xda,
+ 0x81, 0xec,
+ 0x7c, 0x02,
+ 0x80, 0xca,
+ 0x81, 0xeb,
+ 0x7c, 0x03,
+ 0x80, 0xba,
+ 0x81, 0xdb,
+ 0x85, 0x08,
+ 0x86, 0x00,
+ 0x87, 0x02,
+ 0x89, 0x80,
+ 0x8b, 0x44,
+ 0x8c, 0xaa,
+ 0x8a, 0x10,
+ 0xba, 0x00,
+ 0xf5, 0x04,
+ 0xfe, 0x44,
+ 0xd2, 0x32,
+ 0xb8, 0x00,
+};
+
+/* DS3000 doesn't need some parameters as input and auto-detects them */
+/* save input from the application of those parameters */
+struct ds3000_tuning {
+ u32 frequency;
+ u32 symbol_rate;
+ fe_spectral_inversion_t inversion;
+ enum fe_code_rate fec;
+
+ /* input values */
+ u8 inversion_val;
+ fe_modulation_t delivery;
+ u8 rolloff;
+};
+
+struct ds3000_state {
+ struct i2c_adapter *i2c;
+ const struct ds3000_config *config;
+
+ struct dvb_frontend frontend;
+
+ struct ds3000_tuning dcur;
+ struct ds3000_tuning dnxt;
+
+ u8 skip_fw_load;
+
+ /* previous uncorrected block counter for DVB-S2 */
+ u16 prevUCBS2;
+};
+
+static int ds3000_writereg(struct ds3000_state *state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err;
+
+ dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data);
+
+ err = i2c_transfer(state->i2c, &msg, 1);
+ if (err != 1) {
+ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x,"
+ " value == 0x%02x)\n", __func__, err, reg, data);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = 0x60,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err;
+
+ dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data);
+
+ ds3000_writereg(state, 0x03, 0x11);
+ err = i2c_transfer(state->i2c, &msg, 1);
+ if (err != 1) {
+ printk("%s: writereg error(err == %i, reg == 0x%02x,"
+ " value == 0x%02x)\n", __func__, err, reg, data);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+/* I2C write for 8k firmware load */
+static int ds3000_writeFW(struct ds3000_state *state, int reg,
+ const u8 *data, u16 len)
+{
+ int i, ret = -EREMOTEIO;
+ struct i2c_msg msg;
+ u8 *buf;
+
+ buf = kmalloc(3, GFP_KERNEL);
+ if (buf == NULL) {
+ printk(KERN_ERR "Unable to kmalloc\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ *(buf) = reg;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = 3;
+
+ for (i = 0; i < len; i += 2) {
+ memcpy(buf + 1, data + i, 2);
+
+ dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len);
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1) {
+ printk(KERN_ERR "%s: write error(err == %i, "
+ "reg == 0x%02x\n", __func__, ret, reg);
+ ret = -EREMOTEIO;
+ }
+ }
+
+error:
+ kfree(buf);
+
+ return ret;
+}
+
+static int ds3000_readreg(struct ds3000_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+ return ret;
+ }
+
+ dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]);
+
+ return b1[0];
+}
+
+static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = 0x60,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = 0x60,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ds3000_writereg(state, 0x03, 0x12);
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+ return ret;
+ }
+
+ dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]);
+
+ return b1[0];
+}
+
+static int ds3000_set_inversion(struct ds3000_state *state,
+ fe_spectral_inversion_t inversion)
+{
+ dprintk("%s(%d)\n", __func__, inversion);
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ case INVERSION_ON:
+ case INVERSION_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ state->dnxt.inversion = inversion;
+
+ return 0;
+}
+
+static int ds3000_set_symbolrate(struct ds3000_state *state, u32 rate)
+{
+ int ret = 0;
+
+ dprintk("%s()\n", __func__);
+
+ dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate);
+
+ /* check if symbol rate is within limits */
+ if ((state->dnxt.symbol_rate >
+ state->frontend.ops.info.symbol_rate_max) ||
+ (state->dnxt.symbol_rate <
+ state->frontend.ops.info.symbol_rate_min))
+ ret = -EOPNOTSUPP;
+
+ state->dnxt.symbol_rate = rate;
+
+ return ret;
+}
+
+static int ds3000_load_firmware(struct dvb_frontend *fe,
+ const struct firmware *fw);
+
+static int ds3000_firmware_ondemand(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret = 0;
+
+ dprintk("%s()\n", __func__);
+
+ if (ds3000_readreg(state, 0xb2) <= 0)
+ return ret;
+
+ if (state->skip_fw_load)
+ return 0;
+ /* Load firmware */
+ /* request the firmware, this will block until someone uploads it */
+ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
+ DS3000_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE,
+ state->i2c->dev.parent);
+ printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ printk(KERN_ERR "%s: No firmware uploaded (timeout or file not "
+ "found?)\n", __func__);
+ 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__);
+
+ release_firmware(fw);
+
+ 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;
+}
+
+static int ds3000_load_firmware(struct dvb_frontend *fe,
+ const struct firmware *fw)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
+ fw->size,
+ fw->data[0],
+ fw->data[1],
+ fw->data[fw->size - 2],
+ fw->data[fw->size - 1]);
+
+ /* Begin the firmware load process */
+ ds3000_writereg(state, 0xb2, 0x01);
+ /* write the entire firmware */
+ ds3000_writeFW(state, 0xb0, fw->data, fw->size);
+ ds3000_writereg(state, 0xb2, 0x00);
+
+ return 0;
+}
+
+static void ds3000_dump_registers(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int x, y, reg = 0, val;
+
+ for (y = 0; y < 16; y++) {
+ dprintk("%s: %02x: ", __func__, y);
+ for (x = 0; x < 16; x++) {
+ reg = (y << 4) + x;
+ val = ds3000_readreg(state, reg);
+ if (x != 15)
+ dprintk("%02x ", val);
+ else
+ dprintk("%02x\n", val);
+ }
+ }
+ dprintk("%s: -- DS3000 DUMP DONE --\n", __func__);
+}
+
+static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int lock;
+
+ *status = 0;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ lock = ds3000_readreg(state, 0xd1);
+ if ((lock & 0x07) == 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+
+ break;
+ case SYS_DVBS2:
+ lock = ds3000_readreg(state, 0x0d);
+ if ((lock & 0x8f) == 0x8f)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+
+ break;
+ default:
+ return 1;
+ }
+
+ dprintk("%s: status = 0x%02x\n", __func__, lock);
+
+ return 0;
+}
+
+#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK)
+static int ds3000_is_tuned(struct dvb_frontend *fe)
+{
+ fe_status_t tunerstat;
+
+ ds3000_read_status(fe, &tunerstat);
+
+ return ((tunerstat & FE_IS_TUNED) == FE_IS_TUNED);
+}
+
+/* read DS3000 BER value */
+static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 data;
+ u32 ber_reading, lpdc_frames;
+
+ dprintk("%s()\n", __func__);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ /* set the number of bytes checked during
+ BER estimation */
+ ds3000_writereg(state, 0xf9, 0x04);
+ /* read BER estimation status */
+ data = ds3000_readreg(state, 0xf8);
+ /* check if BER estimation is ready */
+ if ((data & 0x10) == 0) {
+ /* this is the number of error bits,
+ to calculate the bit error rate
+ divide to 8388608 */
+ *ber = (ds3000_readreg(state, 0xf7) << 8) |
+ ds3000_readreg(state, 0xf6);
+ /* start counting error bits */
+ /* need to be set twice
+ otherwise it fails sometimes */
+ data |= 0x10;
+ ds3000_writereg(state, 0xf8, data);
+ ds3000_writereg(state, 0xf8, data);
+ } else
+ /* used to indicate that BER estimation
+ is not ready, i.e. BER is unknown */
+ *ber = 0xffffffff;
+ break;
+ case SYS_DVBS2:
+ /* read the number of LPDC decoded frames */
+ lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) |
+ (ds3000_readreg(state, 0xd6) << 8) |
+ ds3000_readreg(state, 0xd5);
+ /* read the number of packets with bad CRC */
+ ber_reading = (ds3000_readreg(state, 0xf8) << 8) |
+ ds3000_readreg(state, 0xf7);
+ if (lpdc_frames > 750) {
+ /* clear LPDC frame counters */
+ ds3000_writereg(state, 0xd1, 0x01);
+ /* clear bad packets counter */
+ ds3000_writereg(state, 0xf9, 0x01);
+ /* enable bad packets counter */
+ ds3000_writereg(state, 0xf9, 0x00);
+ /* enable LPDC frame counters */
+ ds3000_writereg(state, 0xd1, 0x00);
+ *ber = ber_reading;
+ } else
+ /* used to indicate that BER estimation is not ready,
+ i.e. BER is unknown */
+ *ber = 0xffffffff;
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+/* read TS2020 signal strength */
+static int ds3000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ u16 sig_reading, sig_strength;
+ u8 rfgain, bbgain;
+
+ dprintk("%s()\n", __func__);
+
+ rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f;
+ bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f;
+
+ if (rfgain > 15)
+ rfgain = 15;
+ if (bbgain > 13)
+ bbgain = 13;
+
+ sig_reading = rfgain * 2 + bbgain * 3;
+
+ sig_strength = 40 + (64 - sig_reading) * 50 / 64 ;
+
+ /* cook the value to be suitable for szap-s2 human readable output */
+ *signal_strength = sig_strength * 1000;
+
+ dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__,
+ sig_reading, *signal_strength);
+
+ return 0;
+}
+
+/* calculate DS3000 snr value in dB */
+static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 snr_reading, snr_value;
+ u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp;
+ static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */
+ 0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03,
+ 0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717,
+ 0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505
+ };
+ static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */
+ 0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103,
+ 0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5,
+ 0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6,
+ 0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888,
+ 0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51,
+ 0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68,
+ 0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206,
+ 0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a,
+ 0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649,
+ 0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813,
+ 0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1,
+ 0x49e9, 0x4a20, 0x4a57
+ };
+
+ dprintk("%s()\n", __func__);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ snr_reading = ds3000_readreg(state, 0xff);
+ snr_reading /= 8;
+ if (snr_reading == 0)
+ *snr = 0x0000;
+ else {
+ if (snr_reading > 20)
+ snr_reading = 20;
+ snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026;
+ /* cook the value to be suitable for szap-s2
+ human readable output */
+ *snr = snr_value * 8 * 655;
+ }
+ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+ snr_reading, *snr);
+ break;
+ case SYS_DVBS2:
+ dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) +
+ (ds3000_readreg(state, 0x8d) << 4);
+ dvbs2_signal_reading = ds3000_readreg(state, 0x8e);
+ tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1;
+ if (dvbs2_signal_reading == 0) {
+ *snr = 0x0000;
+ return 0;
+ }
+ if (dvbs2_noise_reading == 0) {
+ snr_value = 0x0013;
+ /* cook the value to be suitable for szap-s2
+ human readable output */
+ *snr = 0xffff;
+ return 0;
+ }
+ if (tmp > dvbs2_noise_reading) {
+ snr_reading = tmp / dvbs2_noise_reading;
+ if (snr_reading > 80)
+ snr_reading = 80;
+ snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000;
+ /* cook the value to be suitable for szap-s2
+ human readable output */
+ *snr = snr_value * 5 * 655;
+ } else {
+ snr_reading = dvbs2_noise_reading / tmp;
+ if (snr_reading > 80)
+ snr_reading = 80;
+ *snr = -(dvbs2_snr_tab[snr_reading] / 1000);
+ }
+ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+ snr_reading, *snr);
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+/* read DS3000 uncorrected blocks */
+static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 data;
+ u16 _ucblocks;
+
+ dprintk("%s()\n", __func__);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ *ucblocks = (ds3000_readreg(state, 0xf5) << 8) |
+ ds3000_readreg(state, 0xf4);
+ data = ds3000_readreg(state, 0xf8);
+ /* clear packet counters */
+ data &= ~0x20;
+ ds3000_writereg(state, 0xf8, data);
+ /* enable packet counters */
+ data |= 0x20;
+ ds3000_writereg(state, 0xf8, data);
+ break;
+ case SYS_DVBS2:
+ _ucblocks = (ds3000_readreg(state, 0xe2) << 8) |
+ ds3000_readreg(state, 0xe1);
+ if (_ucblocks > state->prevUCBS2)
+ *ucblocks = _ucblocks - state->prevUCBS2;
+ else
+ *ucblocks = state->prevUCBS2 - _ucblocks;
+ state->prevUCBS2 = _ucblocks;
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void ds3000_clone_params(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
+}
+
+static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ u8 data;
+
+ dprintk("%s(%d)\n", __func__, tone);
+ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+ printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
+ return -EINVAL;
+ }
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ ds3000_writereg(state, 0xa2, data);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ dprintk("%s: setting tone on\n", __func__);
+ data = ds3000_readreg(state, 0xa1);
+ data &= ~0x43;
+ data |= 0x04;
+ ds3000_writereg(state, 0xa1, data);
+ break;
+ case SEC_TONE_OFF:
+ dprintk("%s: setting tone off\n", __func__);
+ data = ds3000_readreg(state, 0xa2);
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+ break;
+ }
+
+ return 0;
+}
+
+static int ds3000_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *d)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int i;
+ u8 data;
+
+ /* Dump DiSEqC message */
+ dprintk("%s(", __func__);
+ for (i = 0 ; i < d->msg_len;) {
+ dprintk("0x%02x", d->msg[i]);
+ if (++i < d->msg_len)
+ dprintk(", ");
+ }
+
+ /* enable DiSEqC message send pin */
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ ds3000_writereg(state, 0xa2, data);
+
+ /* DiSEqC message */
+ for (i = 0; i < d->msg_len; i++)
+ ds3000_writereg(state, 0xa3 + i, d->msg[i]);
+
+ data = ds3000_readreg(state, 0xa1);
+ /* clear DiSEqC message length and status,
+ enable DiSEqC message send */
+ data &= ~0xf8;
+ /* set DiSEqC mode, modulation active during 33 pulses,
+ set DiSEqC message length */
+ data |= ((d->msg_len - 1) << 3) | 0x07;
+ ds3000_writereg(state, 0xa1, data);
+
+ /* wait up to 150ms for DiSEqC transmission to complete */
+ for (i = 0; i < 15; i++) {
+ data = ds3000_readreg(state, 0xa1);
+ if ((data & 0x40) == 0)
+ break;
+ msleep(10);
+ }
+
+ /* DiSEqC timeout after 150ms */
+ if (i == 15) {
+ data = ds3000_readreg(state, 0xa1);
+ data &= ~0x80;
+ data |= 0x40;
+ ds3000_writereg(state, 0xa1, data);
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return 1;
+ }
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return 0;
+}
+
+/* Send DiSEqC burst */
+static int ds3000_diseqc_send_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ int i;
+ u8 data;
+
+ dprintk("%s()\n", __func__);
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ ds3000_writereg(state, 0xa2, data);
+
+ /* DiSEqC burst */
+ if (burst == SEC_MINI_A)
+ /* Unmodulated tone burst */
+ ds3000_writereg(state, 0xa1, 0x02);
+ else if (burst == SEC_MINI_B)
+ /* Modulated tone burst */
+ ds3000_writereg(state, 0xa1, 0x01);
+ else
+ return -EINVAL;
+
+ msleep(13);
+ for (i = 0; i < 5; i++) {
+ data = ds3000_readreg(state, 0xa1);
+ if ((data & 0x40) == 0)
+ break;
+ msleep(1);
+ }
+
+ if (i == 5) {
+ data = ds3000_readreg(state, 0xa1);
+ data &= ~0x80;
+ data |= 0x40;
+ ds3000_writereg(state, 0xa1, data);
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return 1;
+ }
+
+ data = ds3000_readreg(state, 0xa2);
+ data &= ~0xc0;
+ data |= 0x80;
+ ds3000_writereg(state, 0xa2, data);
+
+ return 0;
+}
+
+static void ds3000_release(struct dvb_frontend *fe)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ dprintk("%s\n", __func__);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops ds3000_ops;
+
+struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct ds3000_state *state = NULL;
+ int ret;
+
+ dprintk("%s\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct ds3000_state), GFP_KERNEL);
+ if (state == NULL) {
+ printk(KERN_ERR "Unable to kmalloc\n");
+ goto error2;
+ }
+
+ /* setup the state */
+ memset(state, 0, sizeof(struct ds3000_state));
+
+ state->config = config;
+ state->i2c = i2c;
+ state->prevUCBS2 = 0;
+
+ /* check if the demod is present */
+ ret = ds3000_readreg(state, 0x00) & 0xfe;
+ if (ret != 0xe0) {
+ printk(KERN_ERR "Invalid probe, probably not a DS3000\n");
+ goto error3;
+ }
+
+ printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n",
+ ds3000_readreg(state, 0x02),
+ ds3000_readreg(state, 0x01));
+
+ memcpy(&state->frontend.ops, &ds3000_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error3:
+ kfree(state);
+error2:
+ return NULL;
+}
+EXPORT_SYMBOL(ds3000_attach);
+
+static int ds3000_set_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int ds3000_get_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int ds3000_tune(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct ds3000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ int ret = 0, retune, i;
+ u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf;
+ u16 value, ndiv;
+ u32 f3db;
+
+ dprintk("%s() ", __func__);
+
+ /* Load the firmware if required */
+ ret = ds3000_firmware_ondemand(fe);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: Unable initialise the firmware\n",
+ __func__);
+ return ret;
+ }
+
+ state->dnxt.delivery = c->modulation;
+ state->dnxt.frequency = c->frequency;
+ state->dnxt.rolloff = 2; /* fixme */
+ state->dnxt.fec = c->fec_inner;
+
+ ret = ds3000_set_inversion(state, p->inversion);
+ if (ret != 0)
+ return ret;
+
+ ret = ds3000_set_symbolrate(state, c->symbol_rate);
+ if (ret != 0)
+ return ret;
+
+ /* discard the 'current' tuning parameters and prepare to tune */
+ ds3000_clone_params(fe);
+
+ retune = 1; /* try 1 times */
+ dprintk("%s: retune = %d\n", __func__, retune);
+ dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency);
+ dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
+ dprintk("%s: FEC = %d \n", __func__,
+ state->dcur.fec);
+ dprintk("%s: Inversion = %d\n", __func__, state->dcur.inversion);
+
+ do {
+ /* Reset status register */
+ status = 0;
+ /* Tune */
+ /* TS2020 init */
+ ds3000_tuner_writereg(state, 0x42, 0x73);
+ ds3000_tuner_writereg(state, 0x05, 0x01);
+ ds3000_tuner_writereg(state, 0x62, 0xf5);
+ /* unknown */
+ ds3000_tuner_writereg(state, 0x07, 0x02);
+ ds3000_tuner_writereg(state, 0x10, 0x00);
+ ds3000_tuner_writereg(state, 0x60, 0x79);
+ ds3000_tuner_writereg(state, 0x08, 0x01);
+ ds3000_tuner_writereg(state, 0x00, 0x01);
+ /* calculate and set freq divider */
+ if (state->dcur.frequency < 1146000) {
+ ds3000_tuner_writereg(state, 0x10, 0x11);
+ ndiv = ((state->dcur.frequency * (6 + 8) * 4) +
+ (DS3000_XTAL_FREQ / 2)) /
+ DS3000_XTAL_FREQ - 1024;
+ } else {
+ ds3000_tuner_writereg(state, 0x10, 0x01);
+ ndiv = ((state->dcur.frequency * (6 + 8) * 2) +
+ (DS3000_XTAL_FREQ / 2)) /
+ DS3000_XTAL_FREQ - 1024;
+ }
+
+ ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8);
+ ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff);
+
+ /* set pll */
+ ds3000_tuner_writereg(state, 0x03, 0x06);
+ ds3000_tuner_writereg(state, 0x51, 0x0f);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x10);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ /* unknown */
+ ds3000_tuner_writereg(state, 0x51, 0x17);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x08);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ value = ds3000_tuner_readreg(state, 0x3d);
+ value &= 0x0f;
+ if ((value > 4) && (value < 15)) {
+ value -= 3;
+ if (value < 4)
+ value = 4;
+ value = ((value << 3) | 0x01) & 0x79;
+ }
+
+ ds3000_tuner_writereg(state, 0x60, value);
+ ds3000_tuner_writereg(state, 0x51, 0x17);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x08);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+
+ /* set low-pass filter period */
+ ds3000_tuner_writereg(state, 0x04, 0x2e);
+ ds3000_tuner_writereg(state, 0x51, 0x1b);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x04);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000;
+ if ((state->dcur.symbol_rate / 1000) < 5000)
+ f3db += 3000;
+ if (f3db < 7000)
+ f3db = 7000;
+ if (f3db > 40000)
+ f3db = 40000;
+
+ /* set low-pass filter baseband */
+ value = ds3000_tuner_readreg(state, 0x26);
+ mlpf = 0x2e * 207 / ((value << 1) + 151);
+ mlpf_max = mlpf * 135 / 100;
+ mlpf_min = mlpf * 78 / 100;
+ if (mlpf_max > 63)
+ mlpf_max = 63;
+
+ /* rounded to the closest integer */
+ nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2))
+ / (2766 * DS3000_XTAL_FREQ);
+ if (nlpf > 23)
+ nlpf = 23;
+ if (nlpf < 1)
+ nlpf = 1;
+
+ /* rounded to the closest integer */
+ mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
+ (1000 * f3db / 2)) / (1000 * f3db);
+
+ if (mlpf_new < mlpf_min) {
+ nlpf++;
+ mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
+ (1000 * f3db / 2)) / (1000 * f3db);
+ }
+
+ if (mlpf_new > mlpf_max)
+ mlpf_new = mlpf_max;
+
+ ds3000_tuner_writereg(state, 0x04, mlpf_new);
+ ds3000_tuner_writereg(state, 0x06, nlpf);
+ ds3000_tuner_writereg(state, 0x51, 0x1b);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x04);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ /* unknown */
+ ds3000_tuner_writereg(state, 0x51, 0x1e);
+ ds3000_tuner_writereg(state, 0x51, 0x1f);
+ ds3000_tuner_writereg(state, 0x50, 0x01);
+ ds3000_tuner_writereg(state, 0x50, 0x00);
+ msleep(60);
+
+ /* ds3000 global reset */
+ ds3000_writereg(state, 0x07, 0x80);
+ ds3000_writereg(state, 0x07, 0x00);
+ /* ds3000 build-in uC reset */
+ ds3000_writereg(state, 0xb2, 0x01);
+ /* ds3000 software reset */
+ ds3000_writereg(state, 0x00, 0x01);
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ /* initialise the demod in DVB-S mode */
+ for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2)
+ ds3000_writereg(state,
+ ds3000_dvbs_init_tab[i],
+ ds3000_dvbs_init_tab[i + 1]);
+ value = ds3000_readreg(state, 0xfe);
+ value &= 0xc0;
+ value |= 0x1b;
+ ds3000_writereg(state, 0xfe, value);
+ break;
+ case SYS_DVBS2:
+ /* initialise the demod in DVB-S2 mode */
+ for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2)
+ ds3000_writereg(state,
+ ds3000_dvbs2_init_tab[i],
+ ds3000_dvbs2_init_tab[i + 1]);
+ ds3000_writereg(state, 0xfe, 0x54);
+ break;
+ default:
+ return 1;
+ }
+
+ /* enable 27MHz clock output */
+ ds3000_writereg(state, 0x29, 0x80);
+ /* enable ac coupling */
+ ds3000_writereg(state, 0x25, 0x8a);
+
+ /* enhance symbol rate performance */
+ if ((state->dcur.symbol_rate / 1000) <= 5000) {
+ value = 29777 / (state->dcur.symbol_rate / 1000) + 1;
+ if (value % 2 != 0)
+ value++;
+ ds3000_writereg(state, 0xc3, 0x0d);
+ ds3000_writereg(state, 0xc8, value);
+ ds3000_writereg(state, 0xc4, 0x10);
+ ds3000_writereg(state, 0xc7, 0x0e);
+ } else if ((state->dcur.symbol_rate / 1000) <= 10000) {
+ value = 92166 / (state->dcur.symbol_rate / 1000) + 1;
+ if (value % 2 != 0)
+ value++;
+ ds3000_writereg(state, 0xc3, 0x07);
+ ds3000_writereg(state, 0xc8, value);
+ ds3000_writereg(state, 0xc4, 0x09);
+ ds3000_writereg(state, 0xc7, 0x12);
+ } else if ((state->dcur.symbol_rate / 1000) <= 20000) {
+ value = 64516 / (state->dcur.symbol_rate / 1000) + 1;
+ ds3000_writereg(state, 0xc3, value);
+ ds3000_writereg(state, 0xc8, 0x0e);
+ ds3000_writereg(state, 0xc4, 0x07);
+ ds3000_writereg(state, 0xc7, 0x18);
+ } else {
+ value = 129032 / (state->dcur.symbol_rate / 1000) + 1;
+ ds3000_writereg(state, 0xc3, value);
+ ds3000_writereg(state, 0xc8, 0x0a);
+ ds3000_writereg(state, 0xc4, 0x05);
+ ds3000_writereg(state, 0xc7, 0x24);
+ }
+
+ /* normalized symbol rate rounded to the closest integer */
+ value = (((state->dcur.symbol_rate / 1000) << 16) +
+ (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE;
+ ds3000_writereg(state, 0x61, value & 0x00ff);
+ ds3000_writereg(state, 0x62, (value & 0xff00) >> 8);
+
+ /* co-channel interference cancellation disabled */
+ ds3000_writereg(state, 0x56, 0x00);
+
+ /* equalizer disabled */
+ ds3000_writereg(state, 0x76, 0x00);
+
+ /*ds3000_writereg(state, 0x08, 0x03);
+ ds3000_writereg(state, 0xfd, 0x22);
+ ds3000_writereg(state, 0x08, 0x07);
+ ds3000_writereg(state, 0xfd, 0x42);
+ ds3000_writereg(state, 0x08, 0x07);*/
+
+ /* ds3000 out of software reset */
+ ds3000_writereg(state, 0x00, 0x00);
+ /* start ds3000 build-in uC */
+ ds3000_writereg(state, 0xb2, 0x00);
+
+ /* TODO: calculate and set carrier offset */
+
+ /* wait before retrying */
+ for (i = 0; i < 30 ; i++) {
+ if (ds3000_is_tuned(fe)) {
+ dprintk("%s: Tuned\n", __func__);
+ ds3000_dump_registers(fe);
+ goto tuned;
+ }
+ msleep(1);
+ }
+
+ dprintk("%s: Not tuned\n", __func__);
+ ds3000_dump_registers(fe);
+
+ } while (--retune);
+
+tuned:
+ return ret;
+}
+
+static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe)
+{
+ dprintk("%s()\n", __func__);
+ return DVBFE_ALGO_SW;
+}
+
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int ds3000_initfe(struct dvb_frontend *fe)
+{
+ dprintk("%s()\n", __func__);
+ return 0;
+}
+
+/* Put device to sleep */
+static int ds3000_sleep(struct dvb_frontend *fe)
+{
+ dprintk("%s()\n", __func__);
+ return 0;
+}
+
+static struct dvb_frontend_ops ds3000_ops = {
+
+ .info = {
+ .name = "Montage Technology DS3000/TS2020",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1011, /* kHz for QPSK frontends */
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+
+ .release = ds3000_release,
+
+ .init = ds3000_initfe,
+ .sleep = ds3000_sleep,
+ .read_status = ds3000_read_status,
+ .read_ber = ds3000_read_ber,
+ .read_signal_strength = ds3000_read_signal_strength,
+ .read_snr = ds3000_read_snr,
+ .read_ucblocks = ds3000_read_ucblocks,
+ .set_tone = ds3000_set_tone,
+ .diseqc_send_master_cmd = ds3000_send_diseqc_msg,
+ .diseqc_send_burst = ds3000_diseqc_send_burst,
+ .get_frontend_algo = ds3000_get_algo,
+
+ .set_property = ds3000_set_property,
+ .get_property = ds3000_get_property,
+ .set_frontend = ds3000_tune,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+MODULE_DESCRIPTION("DVB Frontend module for Montage Technology "
+ "DS3000/TS2020 hardware");
+MODULE_AUTHOR("Konstantin Dimitrov");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h
new file mode 100644
index 00000000000..67f67038740
--- /dev/null
+++ b/drivers/media/dvb/frontends/ds3000.h
@@ -0,0 +1,45 @@
+/*
+ Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver
+ Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+
+ Copyright (C) 2009 TurboSight.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef DS3000_H
+#define DS3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct ds3000_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+#if defined(CONFIG_DVB_DS3000) || \
+ (defined(CONFIG_DVB_DS3000_MODULE) && defined(MODULE))
+extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_DS3000 */
+#endif /* DS3000_H */
diff --git a/drivers/media/dvb/frontends/ec100.c b/drivers/media/dvb/frontends/ec100.c
new file mode 100644
index 00000000000..2414dc6ee5d
--- /dev/null
+++ b/drivers/media/dvb/frontends/ec100.c
@@ -0,0 +1,335 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "dvb_frontend.h"
+#include "ec100_priv.h"
+#include "ec100.h"
+
+int ec100_debug;
+module_param_named(debug, ec100_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+struct ec100_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend frontend;
+ struct ec100_config config;
+
+ u16 ber;
+};
+
+/* write single register */
+static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val)
+{
+ u8 buf[2] = {reg, val};
+ struct i2c_msg msg = {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = 2,
+ .buf = buf};
+
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ warn("I2C write failed reg:%02x", reg);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/* read single register */
+static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg
+ }, {
+ .addr = state->config.demod_address,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = val
+ }
+ };
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ warn("I2C read failed reg:%02x", reg);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int ec100_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp, tmp2;
+
+ deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency,
+ params->u.ofdm.bandwidth);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, params);
+
+ ret = ec100_write_reg(state, 0x04, 0x06);
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x67, 0x58);
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x05, 0x18);
+ if (ret)
+ goto error;
+
+ /* reg/bw | 6 | 7 | 8
+ -------+------+------+------
+ A 0x1b | 0xa1 | 0xe7 | 0x2c
+ A 0x1c | 0x55 | 0x63 | 0x72
+ -------+------+------+------
+ B 0x1b | 0xb7 | 0x00 | 0x49
+ B 0x1c | 0x55 | 0x64 | 0x72 */
+
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ tmp = 0xb7;
+ tmp2 = 0x55;
+ break;
+ case BANDWIDTH_7_MHZ:
+ tmp = 0x00;
+ tmp2 = 0x64;
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ tmp = 0x49;
+ tmp2 = 0x72;
+ }
+
+ ret = ec100_write_reg(state, 0x1b, tmp);
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x1c, tmp2);
+ if (ret)
+ goto error;
+
+ ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */
+ if (ret)
+ goto error;
+
+ ret = ec100_write_reg(state, 0x08, 0x24);
+ if (ret)
+ goto error;
+
+ ret = ec100_write_reg(state, 0x00, 0x00); /* go */
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x00, 0x20); /* go */
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 300;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+ *status = 0;
+
+ ret = ec100_read_reg(state, 0x42, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp & 0x80) {
+ /* bit7 set - have lock */
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+ } else {
+ ret = ec100_read_reg(state, 0x01, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp & 0x10) {
+ /* bit4 set - have signal */
+ *status |= FE_HAS_SIGNAL;
+ if (!(tmp & 0x01)) {
+ /* bit0 clear - have ~valid signal */
+ *status |= FE_HAS_CARRIER | FE_HAS_VITERBI;
+ }
+ }
+ }
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp, tmp2;
+ u16 ber2;
+
+ *ber = 0;
+
+ ret = ec100_read_reg(state, 0x65, &tmp);
+ if (ret)
+ goto error;
+ ret = ec100_read_reg(state, 0x66, &tmp2);
+ if (ret)
+ goto error;
+
+ ber2 = (tmp2 << 8) | tmp;
+
+ /* if counter overflow or clear */
+ if (ber2 < state->ber)
+ *ber = ber2;
+ else
+ *ber = ber2 - state->ber;
+
+ state->ber = ber2;
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ ret = ec100_read_reg(state, 0x24, &tmp);
+ if (ret) {
+ *strength = 0;
+ goto error;
+ }
+
+ *strength = ((tmp << 8) | tmp);
+
+ return ret;
+error:
+ deb_info("%s: failed:%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static void ec100_release(struct dvb_frontend *fe)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops ec100_ops;
+
+struct dvb_frontend *ec100_attach(const struct ec100_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct ec100_state *state = NULL;
+ u8 tmp;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config, config, sizeof(struct ec100_config));
+
+ /* check if the demod is there */
+ ret = ec100_read_reg(state, 0x33, &tmp);
+ if (ret || tmp != 0x0b)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &ec100_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(ec100_attach);
+
+static struct dvb_frontend_ops ec100_ops = {
+ .info = {
+ .name = "E3C EC100 DVB-T",
+ .type = FE_OFDM,
+ .caps =
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = ec100_release,
+ .set_frontend = ec100_set_frontend,
+ .get_tune_settings = ec100_get_tune_settings,
+ .read_status = ec100_read_status,
+ .read_ber = ec100_read_ber,
+ .read_signal_strength = ec100_read_signal_strength,
+ .read_snr = ec100_read_snr,
+ .read_ucblocks = ec100_read_ucblocks,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/ec100.h b/drivers/media/dvb/frontends/ec100.h
new file mode 100644
index 00000000000..ee8e5241795
--- /dev/null
+++ b/drivers/media/dvb/frontends/ec100.h
@@ -0,0 +1,46 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EC100_H
+#define EC100_H
+
+#include <linux/dvb/frontend.h>
+
+struct ec100_config {
+ /* demodulator's I2C address */
+ u8 demod_address;
+};
+
+
+#if defined(CONFIG_DVB_EC100) || \
+ (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE))
+extern struct dvb_frontend *ec100_attach(const struct ec100_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ec100_attach(
+ const struct ec100_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* EC100_H */
diff --git a/drivers/media/dvb/frontends/ec100_priv.h b/drivers/media/dvb/frontends/ec100_priv.h
new file mode 100644
index 00000000000..5c990144bc4
--- /dev/null
+++ b/drivers/media/dvb/frontends/ec100_priv.h
@@ -0,0 +1,39 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EC100_PRIV
+#define EC100_PRIV
+
+#define LOG_PREFIX "ec100"
+
+#define dprintk(var, level, args...) \
+ do { if ((var & level)) printk(args); } while (0)
+
+#define deb_info(args...) dprintk(ec100_debug, 0x01, args)
+
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+#endif /* EC100_PRIV */
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index 056387b41a8..43971e63baa 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -479,7 +479,7 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
switch (state->current_modulation) {
case QAM_256:
case QAM_64:
- /* Need to undestand why there are 3 lock levels here */
+ /* Need to understand why there are 3 lock levels here */
if ((buf[0] & 0x07) == 0x07)
*status |= FE_HAS_CARRIER;
break;
@@ -520,7 +520,7 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
switch (state->current_modulation) {
case QAM_256:
case QAM_64:
- /* Need to undestand why there are 3 lock levels here */
+ /* Need to understand why there are 3 lock levels here */
if ((buf[0] & 0x07) == 0x07)
*status |= FE_HAS_CARRIER;
else
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c
index fb301151842..0e2f61a8978 100644
--- a/drivers/media/dvb/frontends/s5h1409.c
+++ b/drivers/media/dvb/frontends/s5h1409.c
@@ -44,7 +44,15 @@ struct s5h1409_state {
int if_freq;
u32 is_qam_locked;
- u32 qam_state;
+
+ /* QAM tuning state goes through the following state transitions */
+#define QAM_STATE_UNTUNED 0
+#define QAM_STATE_TUNING_STARTED 1
+#define QAM_STATE_INTERLEAVE_SET 2
+#define QAM_STATE_QAM_OPTIMIZED_L1 3
+#define QAM_STATE_QAM_OPTIMIZED_L2 4
+#define QAM_STATE_QAM_OPTIMIZED_L3 5
+ u8 qam_state;
};
static int debug;
@@ -347,7 +355,7 @@ static int s5h1409_softreset(struct dvb_frontend *fe)
s5h1409_writereg(state, 0xf5, 0);
s5h1409_writereg(state, 0xf5, 1);
state->is_qam_locked = 0;
- state->qam_state = 0;
+ state->qam_state = QAM_STATE_UNTUNED;
return 0;
}
@@ -474,6 +482,59 @@ static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
struct s5h1409_state *state = fe->demodulator_priv;
u16 reg;
+ if (state->qam_state < QAM_STATE_INTERLEAVE_SET) {
+ /* We should not perform amhum optimization until
+ the interleave mode has been configured */
+ return;
+ }
+
+ if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) {
+ /* We've already reached the maximum optimization level, so
+ dont bother banging on the status registers */
+ return;
+ }
+
+ /* QAM EQ lock check */
+ reg = s5h1409_readreg(state, 0xf0);
+
+ if ((reg >> 13) & 0x1) {
+ reg &= 0xff;
+
+ s5h1409_writereg(state, 0x96, 0x000c);
+ if (reg < 0x68) {
+ if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) {
+ dprintk("%s() setting QAM state to OPT_L3\n",
+ __func__);
+ s5h1409_writereg(state, 0x93, 0x3130);
+ s5h1409_writereg(state, 0x9e, 0x2836);
+ state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3;
+ }
+ } else {
+ if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) {
+ dprintk("%s() setting QAM state to OPT_L2\n",
+ __func__);
+ s5h1409_writereg(state, 0x93, 0x3332);
+ s5h1409_writereg(state, 0x9e, 0x2c37);
+ state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2;
+ }
+ }
+
+ } else {
+ if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) {
+ dprintk("%s() setting QAM state to OPT_L1\n", __func__);
+ s5h1409_writereg(state, 0x96, 0x0008);
+ s5h1409_writereg(state, 0x93, 0x3332);
+ s5h1409_writereg(state, 0x9e, 0x2c37);
+ state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1;
+ }
+ }
+}
+
+static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe)
+{
+ struct s5h1409_state *state = fe->demodulator_priv;
+ u16 reg;
+
if (state->is_qam_locked)
return;
@@ -506,6 +567,44 @@ static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
struct s5h1409_state *state = fe->demodulator_priv;
u16 reg, reg1, reg2;
+ if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) {
+ /* We've done the optimization already */
+ return;
+ }
+
+ reg = s5h1409_readreg(state, 0xf1);
+
+ /* Master lock */
+ if ((reg >> 15) & 0x1) {
+ if (state->qam_state == QAM_STATE_UNTUNED ||
+ state->qam_state == QAM_STATE_TUNING_STARTED) {
+ dprintk("%s() setting QAM state to INTERLEAVE_SET\n",
+ __func__);
+ reg1 = s5h1409_readreg(state, 0xb2);
+ reg2 = s5h1409_readreg(state, 0xad);
+
+ s5h1409_writereg(state, 0x96, 0x0020);
+ s5h1409_writereg(state, 0xad,
+ (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)));
+ state->qam_state = QAM_STATE_INTERLEAVE_SET;
+ }
+ } else {
+ if (state->qam_state == QAM_STATE_UNTUNED) {
+ dprintk("%s() setting QAM state to TUNING_STARTED\n",
+ __func__);
+ s5h1409_writereg(state, 0x96, 0x08);
+ s5h1409_writereg(state, 0xab,
+ s5h1409_readreg(state, 0xab) | 0x1001);
+ state->qam_state = QAM_STATE_TUNING_STARTED;
+ }
+ }
+}
+
+static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe)
+{
+ struct s5h1409_state *state = fe->demodulator_priv;
+ u16 reg, reg1, reg2;
+
reg = s5h1409_readreg(state, 0xf1);
/* Master lock */
@@ -553,16 +652,24 @@ static int s5h1409_set_frontend(struct dvb_frontend *fe,
fe->ops.i2c_gate_ctrl(fe, 0);
}
- /* Optimize the demod for QAM */
- if (p->u.vsb.modulation != VSB_8) {
- s5h1409_set_qam_amhum_mode(fe);
- s5h1409_set_qam_interleave_mode(fe);
- }
-
/* Issue a reset to the demod so it knows to resync against the
newly tuned frequency */
s5h1409_softreset(fe);
+ /* Optimize the demod for QAM */
+ if (state->current_modulation != VSB_8) {
+ /* This almost certainly applies to all boards, but for now
+ only do it for the HVR-1600. Once the other boards are
+ tested, the "legacy" versions can just go away */
+ if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+ s5h1409_set_qam_interleave_mode(fe);
+ s5h1409_set_qam_amhum_mode(fe);
+ } else {
+ s5h1409_set_qam_amhum_mode_legacy(fe);
+ s5h1409_set_qam_interleave_mode_legacy(fe);
+ }
+ }
+
return 0;
}
@@ -614,6 +721,21 @@ static int s5h1409_init(struct dvb_frontend *fe)
/* The datasheet says that after initialisation, VSB is default */
state->current_modulation = VSB_8;
+ /* Optimize for the HVR-1600 if appropriate. Note that some of these
+ may get folded into the generic case after testing with other
+ devices */
+ if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+ /* VSB AGC REF */
+ s5h1409_writereg(state, 0x09, 0x0050);
+
+ /* Unknown but Windows driver does it... */
+ s5h1409_writereg(state, 0x21, 0x0001);
+ s5h1409_writereg(state, 0x50, 0x030e);
+
+ /* QAM AGC REF */
+ s5h1409_writereg(state, 0x82, 0x0800);
+ }
+
if (state->config->output_mode == S5H1409_SERIAL_OUTPUT)
s5h1409_writereg(state, 0xab,
s5h1409_readreg(state, 0xab) | 0x100); /* Serial */
@@ -641,6 +763,17 @@ static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status)
*status = 0;
+ /* Optimize the demod for QAM */
+ if (state->current_modulation != VSB_8) {
+ /* This almost certainly applies to all boards, but for now
+ only do it for the HVR-1600. Once the other boards are
+ tested, the "legacy" versions can just go away */
+ if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+ s5h1409_set_qam_interleave_mode(fe);
+ s5h1409_set_qam_amhum_mode(fe);
+ }
+ }
+
/* Get the demodulator status */
reg = s5h1409_readreg(state, 0xf1);
if (reg & 0x1000)
diff --git a/drivers/media/dvb/frontends/s5h1409.h b/drivers/media/dvb/frontends/s5h1409.h
index 070d9743e33..91f2ebd1a53 100644
--- a/drivers/media/dvb/frontends/s5h1409.h
+++ b/drivers/media/dvb/frontends/s5h1409.h
@@ -57,6 +57,13 @@ struct s5h1409_config {
#define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2
#define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
u16 mpeg_timing;
+
+ /* HVR-1600 optimizations (to better work with MXL5005s)
+ Note: some of these are likely to be folded into the generic driver
+ after being regression tested with other boards */
+#define S5H1409_HVR1600_NOOPTIMIZE 0
+#define S5H1409_HVR1600_OPTIMIZE 1
+ u8 hvr1600_opt;
};
#if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) \
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
index a04c782fff8..1570669837e 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -1571,7 +1571,7 @@ static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_fron
* stb0899_track
* periodically check the signal level against a specified
* threshold level and perform derotator centering.
- * called once we have a lock from a succesful search
+ * called once we have a lock from a successful search
* event.
*
* Will be called periodically called to maintain the
diff --git a/drivers/media/dvb/frontends/stb6100_proc.h b/drivers/media/dvb/frontends/stb6100_proc.h
new file mode 100644
index 00000000000..112163a4862
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6100_proc.h
@@ -0,0 +1,138 @@
+/*
+ STB6100 Silicon Tuner wrapper
+ Copyright (C)2009 Igor M. Liplianin (liplianin@me.by)
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 1);
+
+ err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &state);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+ return err;
+ }
+
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 0);
+
+ *frequency = state.frequency;
+ }
+
+ return 0;
+}
+
+static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state state;
+ int err = 0;
+
+ state.frequency = frequency;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 1);
+
+ err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &state);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+ return err;
+ }
+
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 0);
+
+ }
+
+ return 0;
+}
+
+static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state state;
+ int err = 0;
+
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->get_state) {
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 1);
+
+ err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &state);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+ return err;
+ }
+
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 0);
+
+ *bandwidth = state.bandwidth;
+ }
+
+ return 0;
+}
+
+static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth)
+{
+ struct dvb_frontend_ops *frontend_ops = NULL;
+ struct dvb_tuner_ops *tuner_ops = NULL;
+ struct tuner_state state;
+ int err = 0;
+
+ state.bandwidth = bandwidth;
+ if (&fe->ops)
+ frontend_ops = &fe->ops;
+ if (&frontend_ops->tuner_ops)
+ tuner_ops = &frontend_ops->tuner_ops;
+ if (tuner_ops->set_state) {
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 1);
+
+ err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &state);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+ return err;
+ }
+
+ if (frontend_ops->i2c_gate_ctrl)
+ frontend_ops->i2c_gate_ctrl(fe, 0);
+
+ }
+
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h
index bf4e9b63304..29c3fa85c22 100644
--- a/drivers/media/dvb/frontends/stv0900.h
+++ b/drivers/media/dvb/frontends/stv0900.h
@@ -36,6 +36,7 @@ struct stv0900_reg {
struct stv0900_config {
u8 demod_address;
+ u8 demod_mode;
u32 xtal;
u8 clkmode;/* 0 for CLKI, 2 for XTALI */
@@ -48,6 +49,8 @@ struct stv0900_config {
u8 tun2_maddress;
u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */
u8 tun2_adc;
+ /* Set device param to start dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
};
#if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \
diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c
index 3bde3324a03..df49ea0983b 100644
--- a/drivers/media/dvb/frontends/stv0900_core.c
+++ b/drivers/media/dvb/frontends/stv0900_core.c
@@ -34,7 +34,7 @@
#include "stv0900_priv.h"
#include "stv0900_init.h"
-static int stvdebug = 1;
+int stvdebug = 1;
module_param_named(debug, stvdebug, int, 0644);
/* internal params node */
@@ -105,7 +105,8 @@ static struct stv0900_inode *append_internal(struct stv0900_internal *internal)
while (new_node->next_inode != NULL)
new_node = new_node->next_inode;
- new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL);
+ new_node->next_inode = kmalloc(sizeof(struct stv0900_inode),
+ GFP_KERNEL);
if (new_node->next_inode != NULL)
new_node = new_node->next_inode;
else
@@ -128,13 +129,13 @@ s32 ge2comp(s32 a, s32 width)
return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a;
}
-void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr,
+void stv0900_write_reg(struct stv0900_internal *intp, u16 reg_addr,
u8 reg_data)
{
u8 data[3];
int ret;
struct i2c_msg i2cmsg = {
- .addr = i_params->i2c_addr,
+ .addr = intp->i2c_addr,
.flags = 0,
.len = 3,
.buf = data,
@@ -144,33 +145,33 @@ void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr,
data[1] = LSB(reg_addr);
data[2] = reg_data;
- ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1);
+ ret = i2c_transfer(intp->i2c_adap, &i2cmsg, 1);
if (ret != 1)
- dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret);
+ dprintk("%s: i2c error %d\n", __func__, ret);
}
-u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg)
+u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg)
{
int ret;
u8 b0[] = { MSB(reg), LSB(reg) };
u8 buf = 0;
struct i2c_msg msg[] = {
{
- .addr = i_params->i2c_addr,
+ .addr = intp->i2c_addr,
.flags = 0,
.buf = b0,
.len = 2,
}, {
- .addr = i_params->i2c_addr,
+ .addr = intp->i2c_addr,
.flags = I2C_M_RD,
.buf = &buf,
.len = 1,
},
};
- ret = i2c_transfer(i_params->i2c_adap, msg, 2);
+ ret = i2c_transfer(intp->i2c_adap, msg, 2);
if (ret != 2)
- dprintk(KERN_ERR "%s: i2c error %d, reg[0x%02x]\n",
+ dprintk("%s: i2c error %d, reg[0x%02x]\n",
__func__, ret, reg);
return buf;
@@ -190,169 +191,165 @@ void extract_mask_pos(u32 label, u8 *mask, u8 *pos)
(*pos) = (i - 1);
}
-void stv0900_write_bits(struct stv0900_internal *i_params, u32 label, u8 val)
+void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val)
{
u8 reg, mask, pos;
- reg = stv0900_read_reg(i_params, (label >> 16) & 0xffff);
+ reg = stv0900_read_reg(intp, (label >> 16) & 0xffff);
extract_mask_pos(label, &mask, &pos);
val = mask & (val << pos);
reg = (reg & (~mask)) | val;
- stv0900_write_reg(i_params, (label >> 16) & 0xffff, reg);
+ stv0900_write_reg(intp, (label >> 16) & 0xffff, reg);
}
-u8 stv0900_get_bits(struct stv0900_internal *i_params, u32 label)
+u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label)
{
u8 val = 0xff;
u8 mask, pos;
extract_mask_pos(label, &mask, &pos);
- val = stv0900_read_reg(i_params, label >> 16);
+ val = stv0900_read_reg(intp, label >> 16);
val = (val & mask) >> pos;
return val;
}
-enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params)
+enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp)
{
s32 i;
- enum fe_stv0900_error error;
-
- if (i_params != NULL) {
- i_params->chip_id = stv0900_read_reg(i_params, R0900_MID);
- if (i_params->errs == STV0900_NO_ERROR) {
- /*Startup sequence*/
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5c);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5c);
- stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c);
- stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f);
- stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x20);
- stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x20);
- stv0900_write_reg(i_params, R0900_NCOARSE, 0x13);
- msleep(3);
- stv0900_write_reg(i_params, R0900_I2CCFG, 0x08);
-
- switch (i_params->clkmode) {
- case 0:
- case 2:
- stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20
- | i_params->clkmode);
- break;
- default:
- /* preserve SELOSCI bit */
- i = 0x02 & stv0900_read_reg(i_params, R0900_SYNTCTRL);
- stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20 | i);
- break;
- }
- msleep(3);
- for (i = 0; i < 182; i++)
- stv0900_write_reg(i_params, STV0900_InitVal[i][0], STV0900_InitVal[i][1]);
+ if (intp == NULL)
+ return STV0900_INVALID_HANDLE;
- if (stv0900_read_reg(i_params, R0900_MID) >= 0x20) {
- stv0900_write_reg(i_params, R0900_TSGENERAL, 0x0c);
- for (i = 0; i < 32; i++)
- stv0900_write_reg(i_params, STV0900_Cut20_AddOnVal[i][0], STV0900_Cut20_AddOnVal[i][1]);
- }
+ intp->chip_id = stv0900_read_reg(intp, R0900_MID);
- stv0900_write_reg(i_params, R0900_P1_FSPYCFG, 0x6c);
- stv0900_write_reg(i_params, R0900_P2_FSPYCFG, 0x6c);
- stv0900_write_reg(i_params, R0900_TSTRES0, 0x80);
- stv0900_write_reg(i_params, R0900_TSTRES0, 0x00);
- }
- error = i_params->errs;
- } else
- error = STV0900_INVALID_HANDLE;
+ if (intp->errs != STV0900_NO_ERROR)
+ return intp->errs;
- return error;
+ /*Startup sequence*/
+ stv0900_write_reg(intp, R0900_P1_DMDISTATE, 0x5c);
+ stv0900_write_reg(intp, R0900_P2_DMDISTATE, 0x5c);
+ msleep(3);
+ stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x6c);
+ stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x6f);
+ stv0900_write_reg(intp, R0900_P1_I2CRPT, 0x20);
+ stv0900_write_reg(intp, R0900_P2_I2CRPT, 0x20);
+ stv0900_write_reg(intp, R0900_NCOARSE, 0x13);
+ msleep(3);
+ stv0900_write_reg(intp, R0900_I2CCFG, 0x08);
+ switch (intp->clkmode) {
+ case 0:
+ case 2:
+ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20
+ | intp->clkmode);
+ break;
+ default:
+ /* preserve SELOSCI bit */
+ i = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL);
+ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | i);
+ break;
+ }
+
+ msleep(3);
+ for (i = 0; i < 181; i++)
+ stv0900_write_reg(intp, STV0900_InitVal[i][0],
+ STV0900_InitVal[i][1]);
+
+ if (stv0900_read_reg(intp, R0900_MID) >= 0x20) {
+ stv0900_write_reg(intp, R0900_TSGENERAL, 0x0c);
+ for (i = 0; i < 32; i++)
+ stv0900_write_reg(intp, STV0900_Cut20_AddOnVal[i][0],
+ STV0900_Cut20_AddOnVal[i][1]);
+ }
+
+ stv0900_write_reg(intp, R0900_P1_FSPYCFG, 0x6c);
+ stv0900_write_reg(intp, R0900_P2_FSPYCFG, 0x6c);
+
+ stv0900_write_reg(intp, R0900_P1_PDELCTRL2, 0x01);
+ stv0900_write_reg(intp, R0900_P2_PDELCTRL2, 0x21);
+
+ stv0900_write_reg(intp, R0900_P1_PDELCTRL3, 0x20);
+ stv0900_write_reg(intp, R0900_P2_PDELCTRL3, 0x20);
+
+ stv0900_write_reg(intp, R0900_TSTRES0, 0x80);
+ stv0900_write_reg(intp, R0900_TSTRES0, 0x00);
+
+ return STV0900_NO_ERROR;
}
-u32 stv0900_get_mclk_freq(struct stv0900_internal *i_params, u32 ext_clk)
+u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk)
{
u32 mclk = 90000000, div = 0, ad_div = 0;
- div = stv0900_get_bits(i_params, F0900_M_DIV);
- ad_div = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6);
+ div = stv0900_get_bits(intp, F0900_M_DIV);
+ ad_div = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6);
mclk = (div + 1) * ext_clk / ad_div;
- dprintk(KERN_INFO "%s: Calculated Mclk = %d\n", __func__, mclk);
+ dprintk("%s: Calculated Mclk = %d\n", __func__, mclk);
return mclk;
}
-enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *i_params, u32 mclk)
+enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk)
{
- enum fe_stv0900_error error = STV0900_NO_ERROR;
u32 m_div, clk_sel;
- dprintk(KERN_INFO "%s: Mclk set to %d, Quartz = %d\n", __func__, mclk,
- i_params->quartz);
+ dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk,
+ intp->quartz);
- if (i_params == NULL)
- error = STV0900_INVALID_HANDLE;
- else {
- if (i_params->errs)
- error = STV0900_I2C_ERROR;
- else {
- clk_sel = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6);
- m_div = ((clk_sel * mclk) / i_params->quartz) - 1;
- stv0900_write_bits(i_params, F0900_M_DIV, m_div);
- i_params->mclk = stv0900_get_mclk_freq(i_params,
- i_params->quartz);
-
- /*Set the DiseqC frequency to 22KHz */
- /*
- Formula:
- DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg)
- DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg)
- */
- m_div = i_params->mclk / 704000;
- stv0900_write_reg(i_params, R0900_P1_F22TX, m_div);
- stv0900_write_reg(i_params, R0900_P1_F22RX, m_div);
-
- stv0900_write_reg(i_params, R0900_P2_F22TX, m_div);
- stv0900_write_reg(i_params, R0900_P2_F22RX, m_div);
-
- if ((i_params->errs))
- error = STV0900_I2C_ERROR;
- }
- }
+ if (intp == NULL)
+ return STV0900_INVALID_HANDLE;
- return error;
+ if (intp->errs)
+ return STV0900_I2C_ERROR;
+
+ clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6);
+ m_div = ((clk_sel * mclk) / intp->quartz) - 1;
+ stv0900_write_bits(intp, F0900_M_DIV, m_div);
+ intp->mclk = stv0900_get_mclk_freq(intp,
+ intp->quartz);
+
+ /*Set the DiseqC frequency to 22KHz */
+ /*
+ Formula:
+ DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg)
+ DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg)
+ */
+ m_div = intp->mclk / 704000;
+ stv0900_write_reg(intp, R0900_P1_F22TX, m_div);
+ stv0900_write_reg(intp, R0900_P1_F22RX, m_div);
+
+ stv0900_write_reg(intp, R0900_P2_F22TX, m_div);
+ stv0900_write_reg(intp, R0900_P2_F22RX, m_div);
+
+ if ((intp->errs))
+ return STV0900_I2C_ERROR;
+
+ return STV0900_NO_ERROR;
}
-u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr,
+u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr,
enum fe_stv0900_demod_num demod)
{
u32 lsb, msb, hsb, err_val;
- s32 err1field_hsb, err1field_msb, err1field_lsb;
- s32 err2field_hsb, err2field_msb, err2field_lsb;
-
- dmd_reg(err1field_hsb, F0900_P1_ERR_CNT12, F0900_P2_ERR_CNT12);
- dmd_reg(err1field_msb, F0900_P1_ERR_CNT11, F0900_P2_ERR_CNT11);
- dmd_reg(err1field_lsb, F0900_P1_ERR_CNT10, F0900_P2_ERR_CNT10);
-
- dmd_reg(err2field_hsb, F0900_P1_ERR_CNT22, F0900_P2_ERR_CNT22);
- dmd_reg(err2field_msb, F0900_P1_ERR_CNT21, F0900_P2_ERR_CNT21);
- dmd_reg(err2field_lsb, F0900_P1_ERR_CNT20, F0900_P2_ERR_CNT20);
switch (cntr) {
case 0:
default:
- hsb = stv0900_get_bits(i_params, err1field_hsb);
- msb = stv0900_get_bits(i_params, err1field_msb);
- lsb = stv0900_get_bits(i_params, err1field_lsb);
+ hsb = stv0900_get_bits(intp, ERR_CNT12);
+ msb = stv0900_get_bits(intp, ERR_CNT11);
+ lsb = stv0900_get_bits(intp, ERR_CNT10);
break;
case 1:
- hsb = stv0900_get_bits(i_params, err2field_hsb);
- msb = stv0900_get_bits(i_params, err2field_msb);
- lsb = stv0900_get_bits(i_params, err2field_lsb);
+ hsb = stv0900_get_bits(intp, ERR_CNT22);
+ msb = stv0900_get_bits(intp, ERR_CNT21);
+ lsb = stv0900_get_bits(intp, ERR_CNT20);
break;
}
@@ -364,26 +361,22 @@ u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr,
static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- u32 fi2c;
-
- dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON);
-
- stv0900_write_bits(i_params, fi2c, enable);
+ stv0900_write_bits(intp, I2CT_ON, enable);
return 0;
}
-static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
+static void stv0900_set_ts_parallel_serial(struct stv0900_internal *intp,
enum fe_stv0900_clock_type path1_ts,
enum fe_stv0900_clock_type path2_ts)
{
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- if (i_params->chip_id >= 0x20) {
+ if (intp->chip_id >= 0x20) {
switch (path1_ts) {
case STV0900_PARALLEL_PUNCT_CLOCK:
case STV0900_DVBCI_CLOCK:
@@ -391,20 +384,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
case STV0900_SERIAL_PUNCT_CLOCK:
case STV0900_SERIAL_CONT_CLOCK:
default:
- stv0900_write_reg(i_params, R0900_TSGENERAL,
+ stv0900_write_reg(intp, R0900_TSGENERAL,
0x00);
break;
case STV0900_PARALLEL_PUNCT_CLOCK:
case STV0900_DVBCI_CLOCK:
- stv0900_write_reg(i_params, R0900_TSGENERAL,
+ stv0900_write_reg(intp, R0900_TSGENERAL,
0x06);
- stv0900_write_bits(i_params,
+ stv0900_write_bits(intp,
F0900_P1_TSFIFO_MANSPEED, 3);
- stv0900_write_bits(i_params,
+ stv0900_write_bits(intp,
F0900_P2_TSFIFO_MANSPEED, 0);
- stv0900_write_reg(i_params,
+ stv0900_write_reg(intp,
R0900_P1_TSSPEED, 0x14);
- stv0900_write_reg(i_params,
+ stv0900_write_reg(intp,
R0900_P2_TSSPEED, 0x28);
break;
}
@@ -416,14 +409,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
case STV0900_SERIAL_PUNCT_CLOCK:
case STV0900_SERIAL_CONT_CLOCK:
default:
- stv0900_write_reg(i_params,
+ stv0900_write_reg(intp,
R0900_TSGENERAL, 0x0C);
break;
case STV0900_PARALLEL_PUNCT_CLOCK:
case STV0900_DVBCI_CLOCK:
- stv0900_write_reg(i_params,
+ stv0900_write_reg(intp,
R0900_TSGENERAL, 0x0A);
- dprintk(KERN_INFO "%s: 0x0a\n", __func__);
+ dprintk("%s: 0x0a\n", __func__);
break;
}
break;
@@ -436,20 +429,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
case STV0900_SERIAL_PUNCT_CLOCK:
case STV0900_SERIAL_CONT_CLOCK:
default:
- stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+ stv0900_write_reg(intp, R0900_TSGENERAL1X,
0x10);
break;
case STV0900_PARALLEL_PUNCT_CLOCK:
case STV0900_DVBCI_CLOCK:
- stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+ stv0900_write_reg(intp, R0900_TSGENERAL1X,
0x16);
- stv0900_write_bits(i_params,
+ stv0900_write_bits(intp,
F0900_P1_TSFIFO_MANSPEED, 3);
- stv0900_write_bits(i_params,
+ stv0900_write_bits(intp,
F0900_P2_TSFIFO_MANSPEED, 0);
- stv0900_write_reg(i_params, R0900_P1_TSSPEED,
+ stv0900_write_reg(intp, R0900_P1_TSSPEED,
0x14);
- stv0900_write_reg(i_params, R0900_P2_TSSPEED,
+ stv0900_write_reg(intp, R0900_P2_TSSPEED,
0x28);
break;
}
@@ -462,14 +455,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
case STV0900_SERIAL_PUNCT_CLOCK:
case STV0900_SERIAL_CONT_CLOCK:
default:
- stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+ stv0900_write_reg(intp, R0900_TSGENERAL1X,
0x14);
break;
case STV0900_PARALLEL_PUNCT_CLOCK:
case STV0900_DVBCI_CLOCK:
- stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+ stv0900_write_reg(intp, R0900_TSGENERAL1X,
0x12);
- dprintk(KERN_INFO "%s: 0x12\n", __func__);
+ dprintk("%s: 0x12\n", __func__);
break;
}
@@ -479,20 +472,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
switch (path1_ts) {
case STV0900_PARALLEL_PUNCT_CLOCK:
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00);
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00);
break;
case STV0900_DVBCI_CLOCK:
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00);
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01);
break;
case STV0900_SERIAL_PUNCT_CLOCK:
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01);
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00);
break;
case STV0900_SERIAL_CONT_CLOCK:
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01);
- stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01);
+ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01);
break;
default:
break;
@@ -500,29 +493,29 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
switch (path2_ts) {
case STV0900_PARALLEL_PUNCT_CLOCK:
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00);
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00);
break;
case STV0900_DVBCI_CLOCK:
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00);
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01);
break;
case STV0900_SERIAL_PUNCT_CLOCK:
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01);
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00);
break;
case STV0900_SERIAL_CONT_CLOCK:
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01);
- stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01);
+ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01);
break;
default:
break;
}
- stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1);
- stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 0);
- stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1);
- stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 0);
+ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1);
+ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0);
+ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1);
+ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0);
}
void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency,
@@ -574,7 +567,7 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
}
}
-static s32 stv0900_get_rf_level(struct stv0900_internal *i_params,
+static s32 stv0900_get_rf_level(struct stv0900_internal *intp,
const struct stv0900_table *lookup,
enum fe_stv0900_demod_num demod)
{
@@ -584,45 +577,41 @@ static s32 stv0900_get_rf_level(struct stv0900_internal *i_params,
i,
rf_lvl = 0;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- if ((lookup != NULL) && lookup->size) {
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE1),
- stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE0));
- break;
- case STV0900_DEMOD_2:
- agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE1),
- stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE0));
- break;
- }
+ if ((lookup == NULL) || (lookup->size <= 0))
+ return 0;
- imin = 0;
- imax = lookup->size - 1;
- if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[imax].regval)) {
- while ((imax - imin) > 1) {
- i = (imax + imin) >> 1;
+ agc_gain = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1),
+ stv0900_get_bits(intp, AGCIQ_VALUE0));
- if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[i].regval))
- imax = i;
- else
- imin = i;
- }
+ imin = 0;
+ imax = lookup->size - 1;
+ if (INRANGE(lookup->table[imin].regval, agc_gain,
+ lookup->table[imax].regval)) {
+ while ((imax - imin) > 1) {
+ i = (imax + imin) >> 1;
- rf_lvl = (((s32)agc_gain - lookup->table[imin].regval)
- * (lookup->table[imax].realval - lookup->table[imin].realval)
- / (lookup->table[imax].regval - lookup->table[imin].regval))
- + lookup->table[imin].realval;
- } else if (agc_gain > lookup->table[0].regval)
- rf_lvl = 5;
- else if (agc_gain < lookup->table[lookup->size-1].regval)
- rf_lvl = -100;
+ if (INRANGE(lookup->table[imin].regval,
+ agc_gain,
+ lookup->table[i].regval))
+ imax = i;
+ else
+ imin = i;
+ }
- }
+ rf_lvl = (s32)agc_gain - lookup->table[imin].regval;
+ rf_lvl *= (lookup->table[imax].realval -
+ lookup->table[imin].realval);
+ rf_lvl /= (lookup->table[imax].regval -
+ lookup->table[imin].regval);
+ rf_lvl += lookup->table[imin].realval;
+ } else if (agc_gain > lookup->table[0].regval)
+ rf_lvl = 5;
+ else if (agc_gain < lookup->table[lookup->size-1].regval)
+ rf_lvl = -100;
- dprintk(KERN_INFO "%s: RFLevel = %d\n", __func__, rf_lvl);
+ dprintk("%s: RFLevel = %d\n", __func__, rf_lvl);
return rf_lvl;
}
@@ -634,50 +623,51 @@ static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf,
state->demod);
- *strength = (rflevel + 100) * (16383 / 105);
+ rflevel = (rflevel + 100) * (65535 / 70);
+ if (rflevel < 0)
+ rflevel = 0;
+
+ if (rflevel > 65535)
+ rflevel = 65535;
+
+ *strength = rflevel;
return 0;
}
-
static s32 stv0900_carr_get_quality(struct dvb_frontend *fe,
const struct stv0900_table *lookup)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- s32 c_n = -100,
- regval, imin, imax,
+ s32 c_n = -100,
+ regval,
+ imin,
+ imax,
i,
- lock_flag_field,
noise_field1,
noise_field0;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- dmd_reg(lock_flag_field, F0900_P1_LOCK_DEFINITIF,
- F0900_P2_LOCK_DEFINITIF);
if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) {
- dmd_reg(noise_field1, F0900_P1_NOSPLHT_NORMED1,
- F0900_P2_NOSPLHT_NORMED1);
- dmd_reg(noise_field0, F0900_P1_NOSPLHT_NORMED0,
- F0900_P2_NOSPLHT_NORMED0);
+ noise_field1 = NOSPLHT_NORMED1;
+ noise_field0 = NOSPLHT_NORMED0;
} else {
- dmd_reg(noise_field1, F0900_P1_NOSDATAT_NORMED1,
- F0900_P2_NOSDATAT_NORMED1);
- dmd_reg(noise_field0, F0900_P1_NOSDATAT_NORMED0,
- F0900_P2_NOSDATAT_NORMED0);
+ noise_field1 = NOSDATAT_NORMED1;
+ noise_field0 = NOSDATAT_NORMED0;
}
- if (stv0900_get_bits(i_params, lock_flag_field)) {
+ if (stv0900_get_bits(intp, LOCK_DEFINITIF)) {
if ((lookup != NULL) && lookup->size) {
regval = 0;
msleep(5);
for (i = 0; i < 16; i++) {
- regval += MAKEWORD(stv0900_get_bits(i_params,
+ regval += MAKEWORD(stv0900_get_bits(intp,
noise_field1),
- stv0900_get_bits(i_params,
+ stv0900_get_bits(intp,
noise_field0));
msleep(1);
}
@@ -715,10 +705,9 @@ static s32 stv0900_carr_get_quality(struct dvb_frontend *fe,
static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
u8 err_val1, err_val0;
- s32 err_field1, err_field0;
u32 header_err_val = 0;
*ucblocks = 0x0;
@@ -726,24 +715,14 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
/* DVB-S2 delineator errors count */
/* retreiving number for errnous headers */
- dmd_reg(err_field0, R0900_P1_BBFCRCKO0,
- R0900_P2_BBFCRCKO0);
- dmd_reg(err_field1, R0900_P1_BBFCRCKO1,
- R0900_P2_BBFCRCKO1);
-
- err_val1 = stv0900_read_reg(i_params, err_field1);
- err_val0 = stv0900_read_reg(i_params, err_field0);
- header_err_val = (err_val1<<8) | err_val0;
+ err_val1 = stv0900_read_reg(intp, BBFCRCKO1);
+ err_val0 = stv0900_read_reg(intp, BBFCRCKO0);
+ header_err_val = (err_val1 << 8) | err_val0;
/* retreiving number for errnous packets */
- dmd_reg(err_field0, R0900_P1_UPCRCKO0,
- R0900_P2_UPCRCKO0);
- dmd_reg(err_field1, R0900_P1_UPCRCKO1,
- R0900_P2_UPCRCKO1);
-
- err_val1 = stv0900_read_reg(i_params, err_field1);
- err_val0 = stv0900_read_reg(i_params, err_field0);
- *ucblocks = (err_val1<<8) | err_val0;
+ err_val1 = stv0900_read_reg(intp, UPCRCKO1);
+ err_val0 = stv0900_read_reg(intp, UPCRCKO0);
+ *ucblocks = (err_val1 << 8) | err_val0;
*ucblocks += header_err_val;
}
@@ -752,33 +731,27 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr)
{
- *snr = stv0900_carr_get_quality(fe,
+ s32 snrlcl = stv0900_carr_get_quality(fe,
(const struct stv0900_table *)&stv0900_s2_cn);
- *snr += 30;
- *snr *= (16383 / 1030);
+ snrlcl = (snrlcl + 30) * 384;
+ if (snrlcl < 0)
+ snrlcl = 0;
+
+ if (snrlcl > 65535)
+ snrlcl = 65535;
+
+ *snr = snrlcl;
return 0;
}
-static u32 stv0900_get_ber(struct stv0900_internal *i_params,
+static u32 stv0900_get_ber(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
u32 ber = 10000000, i;
- s32 dmd_state_reg;
s32 demod_state;
- s32 vstatus_reg;
- s32 prvit_field;
- s32 pdel_status_reg;
- s32 pdel_lock_field;
-
- dmd_reg(dmd_state_reg, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
- dmd_reg(vstatus_reg, R0900_P1_VSTATUSVIT, R0900_P2_VSTATUSVIT);
- dmd_reg(prvit_field, F0900_P1_PRFVIT, F0900_P2_PRFVIT);
- dmd_reg(pdel_status_reg, R0900_P1_PDELSTATUS1, R0900_P2_PDELSTATUS1);
- dmd_reg(pdel_lock_field, F0900_P1_PKTDELIN_LOCK,
- F0900_P2_PKTDELIN_LOCK);
- demod_state = stv0900_get_bits(i_params, dmd_state_reg);
+ demod_state = stv0900_get_bits(intp, HEADER_MODE);
switch (demod_state) {
case STV0900_SEARCH:
@@ -790,11 +763,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params,
ber = 0;
for (i = 0; i < 5; i++) {
msleep(5);
- ber += stv0900_get_err_count(i_params, 0, demod);
+ ber += stv0900_get_err_count(intp, 0, demod);
}
ber /= 5;
- if (stv0900_get_bits(i_params, prvit_field)) {
+ if (stv0900_get_bits(intp, PRFVIT)) {
ber *= 9766;
ber = ber >> 13;
}
@@ -804,11 +777,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params,
ber = 0;
for (i = 0; i < 5; i++) {
msleep(5);
- ber += stv0900_get_err_count(i_params, 0, demod);
+ ber += stv0900_get_err_count(intp, 0, demod);
}
ber /= 5;
- if (stv0900_get_bits(i_params, pdel_lock_field)) {
+ if (stv0900_get_bits(intp, PKTDELIN_LOCK)) {
ber *= 9766;
ber = ber >> 13;
}
@@ -829,20 +802,16 @@ static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber)
return 0;
}
-int stv0900_get_demod_lock(struct stv0900_internal *i_params,
+int stv0900_get_demod_lock(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod, s32 time_out)
{
s32 timer = 0,
- lock = 0,
- header_field,
- lock_field;
+ lock = 0;
enum fe_stv0900_search_state dmd_state;
- dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
- dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF);
while ((timer < time_out) && (lock == 0)) {
- dmd_state = stv0900_get_bits(i_params, header_field);
+ dmd_state = stv0900_get_bits(intp, HEADER_MODE);
dprintk("Demod State = %d\n", dmd_state);
switch (dmd_state) {
case STV0900_SEARCH:
@@ -852,7 +821,7 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params,
break;
case STV0900_DVBS2_FOUND:
case STV0900_DVBS_FOUND:
- lock = stv0900_get_bits(i_params, lock_field);
+ lock = stv0900_get_bits(intp, LOCK_DEFINITIF);
break;
}
@@ -870,56 +839,40 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params,
return lock;
}
-void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params,
+void stv0900_stop_all_s2_modcod(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
s32 regflist,
i;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- dmd_reg(regflist, R0900_P1_MODCODLST0, R0900_P2_MODCODLST0);
+ regflist = MODCODLST0;
for (i = 0; i < 16; i++)
- stv0900_write_reg(i_params, regflist + i, 0xff);
+ stv0900_write_reg(intp, regflist + i, 0xff);
}
-void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
+void stv0900_activate_s2_modcod(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
u32 matype,
- mod_code,
- fmod,
- reg_index,
- field_index;
+ mod_code,
+ fmod,
+ reg_index,
+ field_index;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- if (i_params->chip_id <= 0x11) {
+ if (intp->chip_id <= 0x11) {
msleep(5);
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- mod_code = stv0900_read_reg(i_params,
- R0900_P1_PLHMODCOD);
- matype = mod_code & 0x3;
- mod_code = (mod_code & 0x7f) >> 2;
-
- reg_index = R0900_P1_MODCODLSTF - mod_code / 2;
- field_index = mod_code % 2;
- break;
- case STV0900_DEMOD_2:
- mod_code = stv0900_read_reg(i_params,
- R0900_P2_PLHMODCOD);
- matype = mod_code & 0x3;
- mod_code = (mod_code & 0x7f) >> 2;
-
- reg_index = R0900_P2_MODCODLSTF - mod_code / 2;
- field_index = mod_code % 2;
- break;
- }
+ mod_code = stv0900_read_reg(intp, PLHMODCOD);
+ matype = mod_code & 0x3;
+ mod_code = (mod_code & 0x7f) >> 2;
+ reg_index = MODCODLSTF - mod_code / 2;
+ field_index = mod_code % 2;
switch (matype) {
case 0:
@@ -938,70 +891,41 @@ void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
}
if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910))
- && (matype <= 1)) {
+ && (matype <= 1)) {
if (field_index == 0)
- stv0900_write_reg(i_params, reg_index,
+ stv0900_write_reg(intp, reg_index,
0xf0 | fmod);
else
- stv0900_write_reg(i_params, reg_index,
+ stv0900_write_reg(intp, reg_index,
(fmod << 4) | 0xf);
}
- } else if (i_params->chip_id >= 0x12) {
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- for (reg_index = 0; reg_index < 7; reg_index++)
- stv0900_write_reg(i_params, R0900_P1_MODCODLST0 + reg_index, 0xff);
- stv0900_write_reg(i_params, R0900_P1_MODCODLSTE, 0xff);
- stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0xcf);
- for (reg_index = 0; reg_index < 8; reg_index++)
- stv0900_write_reg(i_params, R0900_P1_MODCODLST7 + reg_index, 0xcc);
+ } else if (intp->chip_id >= 0x12) {
+ for (reg_index = 0; reg_index < 7; reg_index++)
+ stv0900_write_reg(intp, MODCODLST0 + reg_index, 0xff);
- break;
- case STV0900_DEMOD_2:
- for (reg_index = 0; reg_index < 7; reg_index++)
- stv0900_write_reg(i_params, R0900_P2_MODCODLST0 + reg_index, 0xff);
+ stv0900_write_reg(intp, MODCODLSTE, 0xff);
+ stv0900_write_reg(intp, MODCODLSTF, 0xcf);
+ for (reg_index = 0; reg_index < 8; reg_index++)
+ stv0900_write_reg(intp, MODCODLST7 + reg_index, 0xcc);
- stv0900_write_reg(i_params, R0900_P2_MODCODLSTE, 0xff);
- stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0xcf);
- for (reg_index = 0; reg_index < 8; reg_index++)
- stv0900_write_reg(i_params, R0900_P2_MODCODLST7 + reg_index, 0xcc);
-
- break;
- }
}
}
-void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params,
+void stv0900_activate_s2_modcod_single(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
u32 reg_index;
- dprintk(KERN_INFO "%s\n", __func__);
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_reg(i_params, R0900_P1_MODCODLST0, 0xff);
- stv0900_write_reg(i_params, R0900_P1_MODCODLST1, 0xf0);
- stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0x0f);
- for (reg_index = 0; reg_index < 13; reg_index++)
- stv0900_write_reg(i_params,
- R0900_P1_MODCODLST2 + reg_index, 0);
+ dprintk("%s\n", __func__);
- break;
- case STV0900_DEMOD_2:
- stv0900_write_reg(i_params, R0900_P2_MODCODLST0, 0xff);
- stv0900_write_reg(i_params, R0900_P2_MODCODLST1, 0xf0);
- stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0x0f);
- for (reg_index = 0; reg_index < 13; reg_index++)
- stv0900_write_reg(i_params,
- R0900_P2_MODCODLST2 + reg_index, 0);
+ stv0900_write_reg(intp, MODCODLST0, 0xff);
+ stv0900_write_reg(intp, MODCODLST1, 0xf0);
+ stv0900_write_reg(intp, MODCODLSTF, 0x0f);
+ for (reg_index = 0; reg_index < 13; reg_index++)
+ stv0900_write_reg(intp, MODCODLST2 + reg_index, 0);
- break;
- }
}
static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe)
@@ -1012,7 +936,7 @@ static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe)
static int stb0900_set_property(struct dvb_frontend *fe,
struct dtv_property *tvp)
{
- dprintk(KERN_INFO "%s(..)\n", __func__);
+ dprintk("%s(..)\n", __func__);
return 0;
}
@@ -1020,166 +944,123 @@ static int stb0900_set_property(struct dvb_frontend *fe,
static int stb0900_get_property(struct dvb_frontend *fe,
struct dtv_property *tvp)
{
- dprintk(KERN_INFO "%s(..)\n", __func__);
+ dprintk("%s(..)\n", __func__);
return 0;
}
-void stv0900_start_search(struct stv0900_internal *i_params,
+void stv0900_start_search(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1f);
-
- if (i_params->chip_id == 0x10)
- stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xaa);
-
- if (i_params->chip_id < 0x20)
- stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55);
-
- if (i_params->dmd1_symbol_rate <= 5000000) {
- stv0900_write_reg(i_params, R0900_P1_CARCFG, 0x44);
- stv0900_write_reg(i_params, R0900_P1_CFRUP1, 0x0f);
- stv0900_write_reg(i_params, R0900_P1_CFRUP0, 0xff);
- stv0900_write_reg(i_params, R0900_P1_CFRLOW1, 0xf0);
- stv0900_write_reg(i_params, R0900_P1_CFRLOW0, 0x00);
- stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68);
+ u32 freq;
+ s16 freq_s16 ;
+
+ stv0900_write_bits(intp, DEMOD_MODE, 0x1f);
+ if (intp->chip_id == 0x10)
+ stv0900_write_reg(intp, CORRELEXP, 0xaa);
+
+ if (intp->chip_id < 0x20)
+ stv0900_write_reg(intp, CARHDR, 0x55);
+
+ if (intp->chip_id <= 0x20) {
+ if (intp->symbol_rate[0] <= 5000000) {
+ stv0900_write_reg(intp, CARCFG, 0x44);
+ stv0900_write_reg(intp, CFRUP1, 0x0f);
+ stv0900_write_reg(intp, CFRUP0, 0xff);
+ stv0900_write_reg(intp, CFRLOW1, 0xf0);
+ stv0900_write_reg(intp, CFRLOW0, 0x00);
+ stv0900_write_reg(intp, RTCS2, 0x68);
} else {
- stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xc4);
- stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44);
- }
-
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0);
-
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41);
- stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41);
-
- if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) {
- stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82);
- stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0);
- }
+ stv0900_write_reg(intp, CARCFG, 0xc4);
+ stv0900_write_reg(intp, RTCS2, 0x44);
}
- stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00);
- stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xe0);
- stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xc0);
- stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
- stv0900_write_bits(i_params, F0900_P1_S1S2_SEQUENTIAL, 0);
- stv0900_write_reg(i_params, R0900_P1_RTC, 0x88);
- if (i_params->chip_id >= 0x20) {
- if (i_params->dmd1_symbol_rate < 2000000) {
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x39);
- stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x40);
- }
-
- if (i_params->dmd1_symbol_rate < 10000000) {
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4c);
- stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20);
- } else {
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4b);
- stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20);
- }
+ } else { /*cut 3.0 above*/
+ if (intp->symbol_rate[demod] <= 5000000)
+ stv0900_write_reg(intp, RTCS2, 0x68);
+ else
+ stv0900_write_reg(intp, RTCS2, 0x44);
+ stv0900_write_reg(intp, CARCFG, 0x46);
+ if (intp->srch_algo[demod] == STV0900_WARM_START) {
+ freq = 1000 << 16;
+ freq /= (intp->mclk / 1000);
+ freq_s16 = (s16)freq;
} else {
- if (i_params->dmd1_symbol_rate < 10000000)
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xef);
+ freq = (intp->srch_range[demod] / 2000);
+ if (intp->symbol_rate[demod] <= 5000000)
+ freq += 80;
else
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
- }
+ freq += 600;
- switch (i_params->dmd1_srch_algo) {
- case STV0900_WARM_START:
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
- break;
- case STV0900_COLD_START:
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
- break;
- default:
- break;
- }
-
- break;
- case STV0900_DEMOD_2:
- stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1f);
- if (i_params->chip_id == 0x10)
- stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xaa);
-
- if (i_params->chip_id < 0x20)
- stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55);
-
- if (i_params->dmd2_symbol_rate <= 5000000) {
- stv0900_write_reg(i_params, R0900_P2_CARCFG, 0x44);
- stv0900_write_reg(i_params, R0900_P2_CFRUP1, 0x0f);
- stv0900_write_reg(i_params, R0900_P2_CFRUP0, 0xff);
- stv0900_write_reg(i_params, R0900_P2_CFRLOW1, 0xf0);
- stv0900_write_reg(i_params, R0900_P2_CFRLOW0, 0x00);
- stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68);
- } else {
- stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xc4);
- stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44);
+ freq = freq << 16;
+ freq /= (intp->mclk / 1000);
+ freq_s16 = (s16)freq;
}
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0);
+ stv0900_write_bits(intp, CFR_UP1, MSB(freq_s16));
+ stv0900_write_bits(intp, CFR_UP0, LSB(freq_s16));
+ freq_s16 *= (-1);
+ stv0900_write_bits(intp, CFR_LOW1, MSB(freq_s16));
+ stv0900_write_bits(intp, CFR_LOW0, LSB(freq_s16));
+ }
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41);
- stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41);
- if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) {
- stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82);
- stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0);
- }
- }
+ stv0900_write_reg(intp, CFRINIT1, 0);
+ stv0900_write_reg(intp, CFRINIT0, 0);
- stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00);
- stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xe0);
- stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xc0);
- stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
- stv0900_write_bits(i_params, F0900_P2_S1S2_SEQUENTIAL, 0);
- stv0900_write_reg(i_params, R0900_P2_RTC, 0x88);
- if (i_params->chip_id >= 0x20) {
- if (i_params->dmd2_symbol_rate < 2000000) {
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x39);
- stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x40);
- }
+ if (intp->chip_id >= 0x20) {
+ stv0900_write_reg(intp, EQUALCFG, 0x41);
+ stv0900_write_reg(intp, FFECFG, 0x41);
- if (i_params->dmd2_symbol_rate < 10000000) {
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4c);
- stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20);
- } else {
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4b);
- stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20);
- }
+ if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) ||
+ (intp->srch_standard[demod] == STV0900_SEARCH_DSS) ||
+ (intp->srch_standard[demod] == STV0900_AUTO_SEARCH)) {
+ stv0900_write_reg(intp, VITSCALE,
+ 0x82);
+ stv0900_write_reg(intp, VAVSRVIT, 0x0);
+ }
+ }
+ stv0900_write_reg(intp, SFRSTEP, 0x00);
+ stv0900_write_reg(intp, TMGTHRISE, 0xe0);
+ stv0900_write_reg(intp, TMGTHFALL, 0xc0);
+ stv0900_write_bits(intp, SCAN_ENABLE, 0);
+ stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+ stv0900_write_bits(intp, S1S2_SEQUENTIAL, 0);
+ stv0900_write_reg(intp, RTC, 0x88);
+ if (intp->chip_id >= 0x20) {
+ if (intp->symbol_rate[demod] < 2000000) {
+ if (intp->chip_id <= 0x20)
+ stv0900_write_reg(intp, CARFREQ, 0x39);
+ else /*cut 3.0*/
+ stv0900_write_reg(intp, CARFREQ, 0x89);
+
+ stv0900_write_reg(intp, CARHDR, 0x40);
+ } else if (intp->symbol_rate[demod] < 10000000) {
+ stv0900_write_reg(intp, CARFREQ, 0x4c);
+ stv0900_write_reg(intp, CARHDR, 0x20);
} else {
- if (i_params->dmd2_symbol_rate < 10000000)
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xef);
- else
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
+ stv0900_write_reg(intp, CARFREQ, 0x4b);
+ stv0900_write_reg(intp, CARHDR, 0x20);
}
- switch (i_params->dmd2_srch_algo) {
- case STV0900_WARM_START:
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
- break;
- case STV0900_COLD_START:
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
- break;
- default:
- break;
- }
+ } else {
+ if (intp->symbol_rate[demod] < 10000000)
+ stv0900_write_reg(intp, CARFREQ, 0xef);
+ else
+ stv0900_write_reg(intp, CARFREQ, 0xed);
+ }
+ switch (intp->srch_algo[demod]) {
+ case STV0900_WARM_START:
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, DMDISTATE, 0x18);
+ break;
+ case STV0900_COLD_START:
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, DMDISTATE, 0x15);
+ break;
+ default:
break;
}
}
@@ -1188,33 +1069,40 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode,
s32 pilot, u8 chip_id)
{
u8 aclc_value = 0x29;
- s32 i;
- const struct stv0900_car_loop_optim *car_loop_s2;
-
- dprintk(KERN_INFO "%s\n", __func__);
-
- if (chip_id <= 0x12)
- car_loop_s2 = FE_STV0900_S2CarLoop;
- else if (chip_id == 0x20)
- car_loop_s2 = FE_STV0900_S2CarLoopCut20;
- else
- car_loop_s2 = FE_STV0900_S2CarLoop;
+ s32 i;
+ const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2;
+
+ dprintk("%s\n", __func__);
+
+ if (chip_id <= 0x12) {
+ cls2 = FE_STV0900_S2CarLoop;
+ cllqs2 = FE_STV0900_S2LowQPCarLoopCut30;
+ cllas2 = FE_STV0900_S2APSKCarLoopCut30;
+ } else if (chip_id == 0x20) {
+ cls2 = FE_STV0900_S2CarLoopCut20;
+ cllqs2 = FE_STV0900_S2LowQPCarLoopCut20;
+ cllas2 = FE_STV0900_S2APSKCarLoopCut20;
+ } else {
+ cls2 = FE_STV0900_S2CarLoopCut30;
+ cllqs2 = FE_STV0900_S2LowQPCarLoopCut30;
+ cllas2 = FE_STV0900_S2APSKCarLoopCut30;
+ }
if (modcode < STV0900_QPSK_12) {
i = 0;
- while ((i < 3) && (modcode != FE_STV0900_S2LowQPCarLoopCut20[i].modcode))
+ while ((i < 3) && (modcode != cllqs2[i].modcode))
i++;
if (i >= 3)
i = 2;
} else {
i = 0;
- while ((i < 14) && (modcode != car_loop_s2[i].modcode))
+ while ((i < 14) && (modcode != cls2[i].modcode))
i++;
if (i >= 14) {
i = 0;
- while ((i < 11) && (modcode != FE_STV0900_S2APSKCarLoopCut20[i].modcode))
+ while ((i < 11) && (modcode != cllas2[i].modcode))
i++;
if (i >= 11)
@@ -1225,76 +1113,82 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode,
if (modcode <= STV0900_QPSK_25) {
if (pilot) {
if (srate <= 3000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_2;
+ aclc_value = cllqs2[i].car_loop_pilots_on_2;
else if (srate <= 7000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_5;
+ aclc_value = cllqs2[i].car_loop_pilots_on_5;
else if (srate <= 15000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_10;
+ aclc_value = cllqs2[i].car_loop_pilots_on_10;
else if (srate <= 25000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_20;
+ aclc_value = cllqs2[i].car_loop_pilots_on_20;
else
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_30;
+ aclc_value = cllqs2[i].car_loop_pilots_on_30;
} else {
if (srate <= 3000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_2;
+ aclc_value = cllqs2[i].car_loop_pilots_off_2;
else if (srate <= 7000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_5;
+ aclc_value = cllqs2[i].car_loop_pilots_off_5;
else if (srate <= 15000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_10;
+ aclc_value = cllqs2[i].car_loop_pilots_off_10;
else if (srate <= 25000000)
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_20;
+ aclc_value = cllqs2[i].car_loop_pilots_off_20;
else
- aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_30;
+ aclc_value = cllqs2[i].car_loop_pilots_off_30;
}
} else if (modcode <= STV0900_8PSK_910) {
if (pilot) {
if (srate <= 3000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_on_2;
+ aclc_value = cls2[i].car_loop_pilots_on_2;
else if (srate <= 7000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_on_5;
+ aclc_value = cls2[i].car_loop_pilots_on_5;
else if (srate <= 15000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_on_10;
+ aclc_value = cls2[i].car_loop_pilots_on_10;
else if (srate <= 25000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_on_20;
+ aclc_value = cls2[i].car_loop_pilots_on_20;
else
- aclc_value = car_loop_s2[i].car_loop_pilots_on_30;
+ aclc_value = cls2[i].car_loop_pilots_on_30;
} else {
if (srate <= 3000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_off_2;
+ aclc_value = cls2[i].car_loop_pilots_off_2;
else if (srate <= 7000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_off_5;
+ aclc_value = cls2[i].car_loop_pilots_off_5;
else if (srate <= 15000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_off_10;
+ aclc_value = cls2[i].car_loop_pilots_off_10;
else if (srate <= 25000000)
- aclc_value = car_loop_s2[i].car_loop_pilots_off_20;
+ aclc_value = cls2[i].car_loop_pilots_off_20;
else
- aclc_value = car_loop_s2[i].car_loop_pilots_off_30;
+ aclc_value = cls2[i].car_loop_pilots_off_30;
}
} else {
if (srate <= 3000000)
- aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_2;
+ aclc_value = cllas2[i].car_loop_pilots_on_2;
else if (srate <= 7000000)
- aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_5;
+ aclc_value = cllas2[i].car_loop_pilots_on_5;
else if (srate <= 15000000)
- aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_10;
+ aclc_value = cllas2[i].car_loop_pilots_on_10;
else if (srate <= 25000000)
- aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_20;
+ aclc_value = cllas2[i].car_loop_pilots_on_20;
else
- aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_30;
+ aclc_value = cllas2[i].car_loop_pilots_on_30;
}
return aclc_value;
}
-u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modulation, u8 chip_id)
+u8 stv0900_get_optim_short_carr_loop(s32 srate,
+ enum fe_stv0900_modulation modulation,
+ u8 chip_id)
{
+ const struct stv0900_short_frames_car_loop_optim *s2scl;
+ const struct stv0900_short_frames_car_loop_optim_vs_mod *s2sclc30;
s32 mod_index = 0;
-
u8 aclc_value = 0x0b;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
+
+ s2scl = FE_STV0900_S2ShortCarLoop;
+ s2sclc30 = FE_STV0900_S2ShortCarLoopCut30;
switch (modulation) {
case STV0900_QPSK:
@@ -1312,75 +1206,116 @@ u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modul
break;
}
- switch (chip_id) {
- case 0x20:
+ if (chip_id >= 0x30) {
if (srate <= 3000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_2;
+ aclc_value = s2sclc30[mod_index].car_loop_2;
else if (srate <= 7000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_5;
+ aclc_value = s2sclc30[mod_index].car_loop_5;
else if (srate <= 15000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_10;
+ aclc_value = s2sclc30[mod_index].car_loop_10;
else if (srate <= 25000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_20;
+ aclc_value = s2sclc30[mod_index].car_loop_20;
else
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_30;
+ aclc_value = s2sclc30[mod_index].car_loop_30;
- break;
- case 0x12:
- default:
+ } else if (chip_id >= 0x20) {
if (srate <= 3000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_2;
+ aclc_value = s2scl[mod_index].car_loop_cut20_2;
else if (srate <= 7000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_5;
+ aclc_value = s2scl[mod_index].car_loop_cut20_5;
else if (srate <= 15000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_10;
+ aclc_value = s2scl[mod_index].car_loop_cut20_10;
else if (srate <= 25000000)
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_20;
+ aclc_value = s2scl[mod_index].car_loop_cut20_20;
else
- aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_30;
+ aclc_value = s2scl[mod_index].car_loop_cut20_30;
+
+ } else {
+ if (srate <= 3000000)
+ aclc_value = s2scl[mod_index].car_loop_cut12_2;
+ else if (srate <= 7000000)
+ aclc_value = s2scl[mod_index].car_loop_cut12_5;
+ else if (srate <= 15000000)
+ aclc_value = s2scl[mod_index].car_loop_cut12_10;
+ else if (srate <= 25000000)
+ aclc_value = s2scl[mod_index].car_loop_cut12_20;
+ else
+ aclc_value = s2scl[mod_index].car_loop_cut12_30;
- break;
}
return aclc_value;
}
-static enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *i_params,
+static
+enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp,
enum fe_stv0900_demod_mode LDPC_Mode,
enum fe_stv0900_demod_num demod)
{
enum fe_stv0900_error error = STV0900_NO_ERROR;
+ s32 reg_ind;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
switch (LDPC_Mode) {
case STV0900_DUAL:
default:
- if ((i_params->demod_mode != STV0900_DUAL)
- || (stv0900_get_bits(i_params, F0900_DDEMOD) != 1)) {
- stv0900_write_reg(i_params, R0900_GENCFG, 0x1d);
-
- i_params->demod_mode = STV0900_DUAL;
-
- stv0900_write_bits(i_params, F0900_FRESFEC, 1);
- stv0900_write_bits(i_params, F0900_FRESFEC, 0);
+ if ((intp->demod_mode != STV0900_DUAL)
+ || (stv0900_get_bits(intp, F0900_DDEMOD) != 1)) {
+ stv0900_write_reg(intp, R0900_GENCFG, 0x1d);
+
+ intp->demod_mode = STV0900_DUAL;
+
+ stv0900_write_bits(intp, F0900_FRESFEC, 1);
+ stv0900_write_bits(intp, F0900_FRESFEC, 0);
+
+ for (reg_ind = 0; reg_ind < 7; reg_ind++)
+ stv0900_write_reg(intp,
+ R0900_P1_MODCODLST0 + reg_ind,
+ 0xff);
+ for (reg_ind = 0; reg_ind < 8; reg_ind++)
+ stv0900_write_reg(intp,
+ R0900_P1_MODCODLST7 + reg_ind,
+ 0xcc);
+
+ stv0900_write_reg(intp, R0900_P1_MODCODLSTE, 0xff);
+ stv0900_write_reg(intp, R0900_P1_MODCODLSTF, 0xcf);
+
+ for (reg_ind = 0; reg_ind < 7; reg_ind++)
+ stv0900_write_reg(intp,
+ R0900_P2_MODCODLST0 + reg_ind,
+ 0xff);
+ for (reg_ind = 0; reg_ind < 8; reg_ind++)
+ stv0900_write_reg(intp,
+ R0900_P2_MODCODLST7 + reg_ind,
+ 0xcc);
+
+ stv0900_write_reg(intp, R0900_P2_MODCODLSTE, 0xff);
+ stv0900_write_reg(intp, R0900_P2_MODCODLSTF, 0xcf);
}
break;
case STV0900_SINGLE:
- if (demod == STV0900_DEMOD_2)
- stv0900_write_reg(i_params, R0900_GENCFG, 0x06);
- else
- stv0900_write_reg(i_params, R0900_GENCFG, 0x04);
+ if (demod == STV0900_DEMOD_2) {
+ stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_1);
+ stv0900_activate_s2_modcod_single(intp,
+ STV0900_DEMOD_2);
+ stv0900_write_reg(intp, R0900_GENCFG, 0x06);
+ } else {
+ stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_2);
+ stv0900_activate_s2_modcod_single(intp,
+ STV0900_DEMOD_1);
+ stv0900_write_reg(intp, R0900_GENCFG, 0x04);
+ }
- i_params->demod_mode = STV0900_SINGLE;
+ intp->demod_mode = STV0900_SINGLE;
- stv0900_write_bits(i_params, F0900_FRESFEC, 1);
- stv0900_write_bits(i_params, F0900_FRESFEC, 0);
- stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1);
- stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0);
- stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1);
- stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0);
+ stv0900_write_bits(intp, F0900_FRESFEC, 1);
+ stv0900_write_bits(intp, F0900_FRESFEC, 0);
+ stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 1);
+ stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 0);
+ stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 1);
+ stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 0);
break;
}
@@ -1393,131 +1328,131 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
struct stv0900_state *state = fe->demodulator_priv;
enum fe_stv0900_error error = STV0900_NO_ERROR;
enum fe_stv0900_error demodError = STV0900_NO_ERROR;
+ struct stv0900_internal *intp = NULL;
+
int selosci, i;
struct stv0900_inode *temp_int = find_inode(state->i2c_adap,
state->config->demod_address);
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- if (temp_int != NULL) {
+ if ((temp_int != NULL) && (p_init->demod_mode == STV0900_DUAL)) {
state->internal = temp_int->internal;
(state->internal->dmds_used)++;
- dprintk(KERN_INFO "%s: Find Internal Structure!\n", __func__);
+ dprintk("%s: Find Internal Structure!\n", __func__);
return STV0900_NO_ERROR;
} else {
- state->internal = kmalloc(sizeof(struct stv0900_internal), GFP_KERNEL);
+ state->internal = kmalloc(sizeof(struct stv0900_internal),
+ GFP_KERNEL);
temp_int = append_internal(state->internal);
state->internal->dmds_used = 1;
state->internal->i2c_adap = state->i2c_adap;
state->internal->i2c_addr = state->config->demod_address;
state->internal->clkmode = state->config->clkmode;
state->internal->errs = STV0900_NO_ERROR;
- dprintk(KERN_INFO "%s: Create New Internal Structure!\n", __func__);
+ dprintk("%s: Create New Internal Structure!\n", __func__);
}
- if (state->internal != NULL) {
- demodError = stv0900_initialize(state->internal);
- if (demodError == STV0900_NO_ERROR) {
- error = STV0900_NO_ERROR;
- } else {
- if (demodError == STV0900_INVALID_HANDLE)
- error = STV0900_INVALID_HANDLE;
- else
- error = STV0900_I2C_ERROR;
- }
+ if (state->internal == NULL) {
+ error = STV0900_INVALID_HANDLE;
+ return error;
+ }
- if (state->internal != NULL) {
- if (error == STV0900_NO_ERROR) {
- state->internal->demod_mode = p_init->demod_mode;
-
- stv0900_st_dvbs2_single(state->internal, state->internal->demod_mode, STV0900_DEMOD_1);
-
- state->internal->chip_id = stv0900_read_reg(state->internal, R0900_MID);
- state->internal->rolloff = p_init->rolloff;
- state->internal->quartz = p_init->dmd_ref_clk;
-
- stv0900_write_bits(state->internal, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff);
- stv0900_write_bits(state->internal, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff);
-
- state->internal->ts_config = p_init->ts_config;
- if (state->internal->ts_config == NULL)
- stv0900_set_ts_parallel_serial(state->internal,
- p_init->path1_ts_clock,
- p_init->path2_ts_clock);
- else {
- for (i = 0; state->internal->ts_config[i].addr != 0xffff; i++)
- stv0900_write_reg(state->internal,
- state->internal->ts_config[i].addr,
- state->internal->ts_config[i].val);
-
- stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 1);
- stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 0);
- stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 1);
- stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 0);
- }
+ demodError = stv0900_initialize(state->internal);
+ if (demodError == STV0900_NO_ERROR) {
+ error = STV0900_NO_ERROR;
+ } else {
+ if (demodError == STV0900_INVALID_HANDLE)
+ error = STV0900_INVALID_HANDLE;
+ else
+ error = STV0900_I2C_ERROR;
- stv0900_write_bits(state->internal, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress);
- switch (p_init->tuner1_adc) {
- case 1:
- stv0900_write_reg(state->internal, R0900_TSTTNR1, 0x26);
- break;
- default:
- break;
- }
+ return error;
+ }
- stv0900_write_bits(state->internal, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress);
- switch (p_init->tuner2_adc) {
- case 1:
- stv0900_write_reg(state->internal, R0900_TSTTNR3, 0x26);
- break;
- default:
- break;
- }
+ if (state->internal == NULL) {
+ error = STV0900_INVALID_HANDLE;
+ return error;
+ }
- stv0900_write_bits(state->internal, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inversion);
- stv0900_write_bits(state->internal, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inversion);
- stv0900_set_mclk(state->internal, 135000000);
- msleep(3);
-
- switch (state->internal->clkmode) {
- case 0:
- case 2:
- stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | state->internal->clkmode);
- break;
- default:
- selosci = 0x02 & stv0900_read_reg(state->internal, R0900_SYNTCTRL);
- stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | selosci);
- break;
- }
- msleep(3);
+ intp = state->internal;
- state->internal->mclk = stv0900_get_mclk_freq(state->internal, state->internal->quartz);
- if (state->internal->errs)
- error = STV0900_I2C_ERROR;
- }
- } else {
- error = STV0900_INVALID_HANDLE;
- }
+ intp->demod_mode = p_init->demod_mode;
+ stv0900_st_dvbs2_single(intp, intp->demod_mode, STV0900_DEMOD_1);
+ intp->chip_id = stv0900_read_reg(intp, R0900_MID);
+ intp->rolloff = p_init->rolloff;
+ intp->quartz = p_init->dmd_ref_clk;
+
+ stv0900_write_bits(intp, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff);
+ stv0900_write_bits(intp, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff);
+
+ intp->ts_config = p_init->ts_config;
+ if (intp->ts_config == NULL)
+ stv0900_set_ts_parallel_serial(intp,
+ p_init->path1_ts_clock,
+ p_init->path2_ts_clock);
+ else {
+ for (i = 0; intp->ts_config[i].addr != 0xffff; i++)
+ stv0900_write_reg(intp,
+ intp->ts_config[i].addr,
+ intp->ts_config[i].val);
+
+ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1);
+ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0);
+ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1);
+ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0);
+ }
+
+ stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress);
+ switch (p_init->tuner1_adc) {
+ case 1:
+ stv0900_write_reg(intp, R0900_TSTTNR1, 0x26);
+ break;
+ default:
+ break;
+ }
+
+ stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress);
+ switch (p_init->tuner2_adc) {
+ case 1:
+ stv0900_write_reg(intp, R0900_TSTTNR3, 0x26);
+ break;
+ default:
+ break;
+ }
+
+ stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv);
+ stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv);
+ stv0900_set_mclk(intp, 135000000);
+ msleep(3);
+
+ switch (intp->clkmode) {
+ case 0:
+ case 2:
+ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | intp->clkmode);
+ break;
+ default:
+ selosci = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL);
+ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | selosci);
+ break;
}
+ msleep(3);
+
+ intp->mclk = stv0900_get_mclk_freq(intp, intp->quartz);
+ if (intp->errs)
+ error = STV0900_I2C_ERROR;
return error;
}
-static int stv0900_status(struct stv0900_internal *i_params,
+static int stv0900_status(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
enum fe_stv0900_search_state demod_state;
- s32 mode_field, delin_field, lock_field, fifo_field, lockedvit_field;
int locked = FALSE;
- dmd_reg(mode_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
- dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF);
- dmd_reg(delin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK);
- dmd_reg(fifo_field, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK);
- dmd_reg(lockedvit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT);
-
- demod_state = stv0900_get_bits(i_params, mode_field);
+ demod_state = stv0900_get_bits(intp, HEADER_MODE);
switch (demod_state) {
case STV0900_SEARCH:
case STV0900_PLH_DETECTED:
@@ -1525,17 +1460,19 @@ static int stv0900_status(struct stv0900_internal *i_params,
locked = FALSE;
break;
case STV0900_DVBS2_FOUND:
- locked = stv0900_get_bits(i_params, lock_field) &&
- stv0900_get_bits(i_params, delin_field) &&
- stv0900_get_bits(i_params, fifo_field);
+ locked = stv0900_get_bits(intp, LOCK_DEFINITIF) &&
+ stv0900_get_bits(intp, PKTDELIN_LOCK) &&
+ stv0900_get_bits(intp, TSFIFO_LINEOK);
break;
case STV0900_DVBS_FOUND:
- locked = stv0900_get_bits(i_params, lock_field) &&
- stv0900_get_bits(i_params, lockedvit_field) &&
- stv0900_get_bits(i_params, fifo_field);
+ locked = stv0900_get_bits(intp, LOCK_DEFINITIF) &&
+ stv0900_get_bits(intp, LOCKEDVIT) &&
+ stv0900_get_bits(intp, TSFIFO_LINEOK);
break;
}
+ dprintk("%s: locked = %d\n", __func__, locked);
+
return locked;
}
@@ -1543,7 +1480,8 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
+ enum fe_stv0900_demod_num demod = state->demod;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct stv0900_search_params p_search;
@@ -1551,10 +1489,16 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
enum fe_stv0900_error error = STV0900_NO_ERROR;
- dprintk(KERN_INFO "%s: ", __func__);
+ dprintk("%s: ", __func__);
+
+ if (!(INRANGE(100000, c->symbol_rate, 70000000)))
+ return DVBFE_ALGO_SEARCH_FAILED;
+
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
p_result.locked = FALSE;
- p_search.path = state->demod;
+ p_search.path = demod;
p_search.frequency = c->frequency;
p_search.symbol_rate = c->symbol_rate;
p_search.search_range = 10000000;
@@ -1563,103 +1507,47 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
p_search.iq_inversion = STV0900_IQ_AUTO;
p_search.search_algo = STV0900_BLIND_SEARCH;
- if ((INRANGE(100000, p_search.symbol_rate, 70000000)) &&
- (INRANGE(100000, p_search.search_range, 50000000))) {
- switch (p_search.path) {
- case STV0900_DEMOD_1:
- default:
- i_params->dmd1_srch_standard = p_search.standard;
- i_params->dmd1_symbol_rate = p_search.symbol_rate;
- i_params->dmd1_srch_range = p_search.search_range;
- i_params->tuner1_freq = p_search.frequency;
- i_params->dmd1_srch_algo = p_search.search_algo;
- i_params->dmd1_srch_iq_inv = p_search.iq_inversion;
- i_params->dmd1_fec = p_search.fec;
+ intp->srch_standard[demod] = p_search.standard;
+ intp->symbol_rate[demod] = p_search.symbol_rate;
+ intp->srch_range[demod] = p_search.search_range;
+ intp->freq[demod] = p_search.frequency;
+ intp->srch_algo[demod] = p_search.search_algo;
+ intp->srch_iq_inv[demod] = p_search.iq_inversion;
+ intp->fec[demod] = p_search.fec;
+ if ((stv0900_algo(fe) == STV0900_RANGEOK) &&
+ (intp->errs == STV0900_NO_ERROR)) {
+ p_result.locked = intp->result[demod].locked;
+ p_result.standard = intp->result[demod].standard;
+ p_result.frequency = intp->result[demod].frequency;
+ p_result.symbol_rate = intp->result[demod].symbol_rate;
+ p_result.fec = intp->result[demod].fec;
+ p_result.modcode = intp->result[demod].modcode;
+ p_result.pilot = intp->result[demod].pilot;
+ p_result.frame_len = intp->result[demod].frame_len;
+ p_result.spectrum = intp->result[demod].spectrum;
+ p_result.rolloff = intp->result[demod].rolloff;
+ p_result.modulation = intp->result[demod].modulation;
+ } else {
+ p_result.locked = FALSE;
+ switch (intp->err[demod]) {
+ case STV0900_I2C_ERROR:
+ error = STV0900_I2C_ERROR;
break;
-
- case STV0900_DEMOD_2:
- i_params->dmd2_srch_stndrd = p_search.standard;
- i_params->dmd2_symbol_rate = p_search.symbol_rate;
- i_params->dmd2_srch_range = p_search.search_range;
- i_params->tuner2_freq = p_search.frequency;
- i_params->dmd2_srch_algo = p_search.search_algo;
- i_params->dmd2_srch_iq_inv = p_search.iq_inversion;
- i_params->dmd2_fec = p_search.fec;
+ case STV0900_NO_ERROR:
+ default:
+ error = STV0900_SEARCH_FAILED;
break;
}
-
- if ((stv0900_algo(fe) == STV0900_RANGEOK) &&
- (i_params->errs == STV0900_NO_ERROR)) {
- switch (p_search.path) {
- case STV0900_DEMOD_1:
- default:
- p_result.locked = i_params->dmd1_rslts.locked;
- p_result.standard = i_params->dmd1_rslts.standard;
- p_result.frequency = i_params->dmd1_rslts.frequency;
- p_result.symbol_rate = i_params->dmd1_rslts.symbol_rate;
- p_result.fec = i_params->dmd1_rslts.fec;
- p_result.modcode = i_params->dmd1_rslts.modcode;
- p_result.pilot = i_params->dmd1_rslts.pilot;
- p_result.frame_length = i_params->dmd1_rslts.frame_length;
- p_result.spectrum = i_params->dmd1_rslts.spectrum;
- p_result.rolloff = i_params->dmd1_rslts.rolloff;
- p_result.modulation = i_params->dmd1_rslts.modulation;
- break;
- case STV0900_DEMOD_2:
- p_result.locked = i_params->dmd2_rslts.locked;
- p_result.standard = i_params->dmd2_rslts.standard;
- p_result.frequency = i_params->dmd2_rslts.frequency;
- p_result.symbol_rate = i_params->dmd2_rslts.symbol_rate;
- p_result.fec = i_params->dmd2_rslts.fec;
- p_result.modcode = i_params->dmd2_rslts.modcode;
- p_result.pilot = i_params->dmd2_rslts.pilot;
- p_result.frame_length = i_params->dmd2_rslts.frame_length;
- p_result.spectrum = i_params->dmd2_rslts.spectrum;
- p_result.rolloff = i_params->dmd2_rslts.rolloff;
- p_result.modulation = i_params->dmd2_rslts.modulation;
- break;
- }
-
- } else {
- p_result.locked = FALSE;
- switch (p_search.path) {
- case STV0900_DEMOD_1:
- switch (i_params->dmd1_err) {
- case STV0900_I2C_ERROR:
- error = STV0900_I2C_ERROR;
- break;
- case STV0900_NO_ERROR:
- default:
- error = STV0900_SEARCH_FAILED;
- break;
- }
- break;
- case STV0900_DEMOD_2:
- switch (i_params->dmd2_err) {
- case STV0900_I2C_ERROR:
- error = STV0900_I2C_ERROR;
- break;
- case STV0900_NO_ERROR:
- default:
- error = STV0900_SEARCH_FAILED;
- break;
- }
- break;
- }
- }
-
- } else
- error = STV0900_BAD_PARAMETER;
+ }
if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) {
- dprintk(KERN_INFO "Search Success\n");
+ dprintk("Search Success\n");
return DVBFE_ALGO_SEARCH_SUCCESS;
} else {
- dprintk(KERN_INFO "Search Fail\n");
+ dprintk("Search Fail\n");
return DVBFE_ALGO_SEARCH_FAILED;
}
- return DVBFE_ALGO_SEARCH_ERROR;
}
static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status)
@@ -1690,16 +1578,13 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- s32 rst_field;
-
- dmd_reg(rst_field, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE);
if (stop_ts == TRUE)
- stv0900_write_bits(i_params, rst_field, 1);
+ stv0900_write_bits(intp, RST_HWARE, 1);
else
- stv0900_write_bits(i_params, rst_field, 0);
+ stv0900_write_bits(intp, RST_HWARE, 0);
return 0;
}
@@ -1707,23 +1592,19 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts)
static int stv0900_diseqc_init(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- s32 mode_field, reset_field;
- dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
- dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET);
-
- stv0900_write_bits(i_params, mode_field, state->config->diseqc_mode);
- stv0900_write_bits(i_params, reset_field, 1);
- stv0900_write_bits(i_params, reset_field, 0);
+ stv0900_write_bits(intp, DISTX_MODE, state->config->diseqc_mode);
+ stv0900_write_bits(intp, DISEQC_RESET, 1);
+ stv0900_write_bits(intp, DISEQC_RESET, 0);
return 0;
}
static int stv0900_init(struct dvb_frontend *fe)
{
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
stv0900_stop_ts(fe, 1);
stv0900_diseqc_init(fe);
@@ -1731,48 +1612,24 @@ static int stv0900_init(struct dvb_frontend *fe)
return 0;
}
-static int stv0900_diseqc_send(struct stv0900_internal *i_params , u8 *Data,
+static int stv0900_diseqc_send(struct stv0900_internal *intp , u8 *data,
u32 NbData, enum fe_stv0900_demod_num demod)
{
s32 i = 0;
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 1);
- while (i < NbData) {
- while (stv0900_get_bits(i_params, F0900_P1_FIFO_FULL))
- ;/* checkpatch complains */
- stv0900_write_reg(i_params, R0900_P1_DISTXDATA, Data[i]);
- i++;
- }
-
- stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 0);
- i = 0;
- while ((stv0900_get_bits(i_params, F0900_P1_TX_IDLE) != 1) && (i < 10)) {
- msleep(10);
- i++;
- }
-
- break;
- case STV0900_DEMOD_2:
- stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 1);
-
- while (i < NbData) {
- while (stv0900_get_bits(i_params, F0900_P2_FIFO_FULL))
- ;/* checkpatch complains */
- stv0900_write_reg(i_params, R0900_P2_DISTXDATA, Data[i]);
- i++;
- }
-
- stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 0);
- i = 0;
- while ((stv0900_get_bits(i_params, F0900_P2_TX_IDLE) != 1) && (i < 10)) {
- msleep(10);
- i++;
- }
+ stv0900_write_bits(intp, DIS_PRECHARGE, 1);
+ while (i < NbData) {
+ while (stv0900_get_bits(intp, FIFO_FULL))
+ ;/* checkpatch complains */
+ stv0900_write_reg(intp, DISTXDATA, data[i]);
+ i++;
+ }
- break;
+ stv0900_write_bits(intp, DIS_PRECHARGE, 0);
+ i = 0;
+ while ((stv0900_get_bits(intp, TX_IDLE) != 1) && (i < 10)) {
+ msleep(10);
+ i++;
}
return 0;
@@ -1792,22 +1649,21 @@ static int stv0900_send_master_cmd(struct dvb_frontend *fe,
static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- s32 mode_field;
- u32 diseqc_fifo;
+ u8 data;
- dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
- dmd_reg(diseqc_fifo, R0900_P1_DISTXDATA, R0900_P2_DISTXDATA);
switch (burst) {
case SEC_MINI_A:
- stv0900_write_bits(i_params, mode_field, 3);/* Unmodulated */
- stv0900_write_reg(i_params, diseqc_fifo, 0x00);
+ stv0900_write_bits(intp, DISTX_MODE, 3);/* Unmodulated */
+ data = 0x00;
+ stv0900_diseqc_send(intp, &data, 1, state->demod);
break;
case SEC_MINI_B:
- stv0900_write_bits(i_params, mode_field, 2);/* Modulated */
- stv0900_write_reg(i_params, diseqc_fifo, 0xff);
+ stv0900_write_bits(intp, DISTX_MODE, 2);/* Modulated */
+ data = 0xff;
+ stv0900_diseqc_send(intp, &data, 1, state->demod);
break;
}
@@ -1818,68 +1674,54 @@ static int stv0900_recv_slave_reply(struct dvb_frontend *fe,
struct dvb_diseqc_slave_reply *reply)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
+ enum fe_stv0900_demod_num demod = state->demod;
s32 i = 0;
- switch (state->demod) {
- case STV0900_DEMOD_1:
- default:
- reply->msg_len = 0;
-
- while ((stv0900_get_bits(i_params, F0900_P1_RX_END) != 1) && (i < 10)) {
- msleep(10);
- i++;
- }
-
- if (stv0900_get_bits(i_params, F0900_P1_RX_END)) {
- reply->msg_len = stv0900_get_bits(i_params, F0900_P1_FIFO_BYTENBR);
-
- for (i = 0; i < reply->msg_len; i++)
- reply->msg[i] = stv0900_read_reg(i_params, R0900_P1_DISRXDATA);
- }
- break;
- case STV0900_DEMOD_2:
- reply->msg_len = 0;
+ reply->msg_len = 0;
- while ((stv0900_get_bits(i_params, F0900_P2_RX_END) != 1) && (i < 10)) {
- msleep(10);
- i++;
- }
+ while ((stv0900_get_bits(intp, RX_END) != 1) && (i < 10)) {
+ msleep(10);
+ i++;
+ }
- if (stv0900_get_bits(i_params, F0900_P2_RX_END)) {
- reply->msg_len = stv0900_get_bits(i_params, F0900_P2_FIFO_BYTENBR);
+ if (stv0900_get_bits(intp, RX_END)) {
+ reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR);
- for (i = 0; i < reply->msg_len; i++)
- reply->msg[i] = stv0900_read_reg(i_params, R0900_P2_DISRXDATA);
- }
- break;
+ for (i = 0; i < reply->msg_len; i++)
+ reply->msg[i] = stv0900_read_reg(intp, DISRXDATA);
}
return 0;
}
-static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t toneoff)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- s32 mode_field, reset_field;
-
- dprintk(KERN_INFO "%s: %s\n", __func__, ((tone == 0) ? "Off" : "On"));
- dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
- dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET);
+ dprintk("%s: %s\n", __func__, ((toneoff == 0) ? "On" : "Off"));
- if (tone) {
- /*Set the DiseqC mode to 22Khz continues tone*/
- stv0900_write_bits(i_params, mode_field, 0);
- stv0900_write_bits(i_params, reset_field, 1);
+ switch (toneoff) {
+ case SEC_TONE_ON:
+ /*Set the DiseqC mode to 22Khz _continues_ tone*/
+ stv0900_write_bits(intp, DISTX_MODE, 0);
+ stv0900_write_bits(intp, DISEQC_RESET, 1);
/*release DiseqC reset to enable the 22KHz tone*/
- stv0900_write_bits(i_params, reset_field, 0);
- } else {
- stv0900_write_bits(i_params, mode_field, 0);
+ stv0900_write_bits(intp, DISEQC_RESET, 0);
+ break;
+ case SEC_TONE_OFF:
+ /*return diseqc mode to config->diseqc_mode.
+ Usually it's without _continues_ tone */
+ stv0900_write_bits(intp, DISTX_MODE,
+ state->config->diseqc_mode);
/*maintain the DiseqC reset to disable the 22KHz tone*/
- stv0900_write_bits(i_params, reset_field, 1);
+ stv0900_write_bits(intp, DISEQC_RESET, 1);
+ stv0900_write_bits(intp, DISEQC_RESET, 0);
+ break;
+ default:
+ return -EINVAL;
}
return 0;
@@ -1889,11 +1731,11 @@ static void stv0900_release(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
if ((--(state->internal->dmds_used)) <= 0) {
- dprintk(KERN_INFO "%s: Actually removing\n", __func__);
+ dprintk("%s: Actually removing\n", __func__);
remove_inode(state->internal);
kfree(state->internal);
@@ -1963,17 +1805,17 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config,
case 0:
case 1:
init_params.dmd_ref_clk = config->xtal;
- init_params.demod_mode = STV0900_DUAL;
+ init_params.demod_mode = config->demod_mode;
init_params.rolloff = STV0900_35;
init_params.path1_ts_clock = config->path1_mode;
init_params.tun1_maddress = config->tun1_maddress;
- init_params.tun1_iq_inversion = STV0900_IQ_NORMAL;
+ init_params.tun1_iq_inv = STV0900_IQ_NORMAL;
init_params.tuner1_adc = config->tun1_adc;
init_params.path2_ts_clock = config->path2_mode;
init_params.ts_config = config->ts_config_regs;
init_params.tun2_maddress = config->tun2_maddress;
init_params.tuner2_adc = config->tun2_adc;
- init_params.tun2_iq_inversion = STV0900_IQ_SWAPPED;
+ init_params.tun2_iq_inv = STV0900_IQ_SWAPPED;
err_stv0900 = stv0900_init_internal(&state->frontend,
&init_params);
diff --git a/drivers/media/dvb/frontends/stv0900_init.h b/drivers/media/dvb/frontends/stv0900_init.h
index ff388b47a4e..b684df9995d 100644
--- a/drivers/media/dvb/frontends/stv0900_init.h
+++ b/drivers/media/dvb/frontends/stv0900_init.h
@@ -141,85 +141,228 @@ struct stv0900_short_frames_car_loop_optim {
};
+struct stv0900_short_frames_car_loop_optim_vs_mod {
+ enum fe_stv0900_modulation modulation;
+ u8 car_loop_2; /* SR<3msps */
+ u8 car_loop_5; /* 3<SR<=7msps */
+ u8 car_loop_10; /* 7<SR<=15msps */
+ u8 car_loop_20; /* 10<SR<=25msps */
+ u8 car_loop_30; /* 10<SR<=45msps */
+};
+
/* Cut 1.x Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */
-static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoop[14] = {
- /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV0900_QPSK_12, 0x1C, 0x0D, 0x1B, 0x2C, 0x3A, 0x1C, 0x2A, 0x3B, 0x2A, 0x1B },
- { STV0900_QPSK_35, 0x2C, 0x0D, 0x2B, 0x2C, 0x3A, 0x0C, 0x3A, 0x2B, 0x2A, 0x0B },
- { STV0900_QPSK_23, 0x2C, 0x0D, 0x2B, 0x2C, 0x0B, 0x0C, 0x3A, 0x1B, 0x2A, 0x3A },
- { STV0900_QPSK_34, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
- { STV0900_QPSK_45, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
- { STV0900_QPSK_56, 0x0D, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
- { STV0900_QPSK_89, 0x0D, 0x0D, 0x3B, 0x1C, 0x1B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
- { STV0900_QPSK_910, 0x1D, 0x0D, 0x3B, 0x1C, 0x1B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
- { STV0900_8PSK_35, 0x29, 0x3B, 0x09, 0x2B, 0x38, 0x0B, 0x18, 0x1A, 0x08, 0x0A },
- { STV0900_8PSK_23, 0x0A, 0x3B, 0x29, 0x2B, 0x19, 0x0B, 0x38, 0x1A, 0x18, 0x0A },
- { STV0900_8PSK_34, 0x3A, 0x3B, 0x2A, 0x2B, 0x39, 0x0B, 0x19, 0x1A, 0x38, 0x0A },
- { STV0900_8PSK_56, 0x1B, 0x3B, 0x0B, 0x2B, 0x1A, 0x0B, 0x39, 0x1A, 0x19, 0x0A },
- { STV0900_8PSK_89, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, 0x0B, 0x39, 0x1A, 0x29, 0x39 },
- { STV0900_8PSK_910, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, 0x0B, 0x39, 0x1A, 0x29, 0x39 }
+static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoop[14] = {
+ /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon
+ 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV0900_QPSK_12, 0x1C, 0x0D, 0x1B, 0x2C, 0x3A,
+ 0x1C, 0x2A, 0x3B, 0x2A, 0x1B },
+ { STV0900_QPSK_35, 0x2C, 0x0D, 0x2B, 0x2C, 0x3A,
+ 0x0C, 0x3A, 0x2B, 0x2A, 0x0B },
+ { STV0900_QPSK_23, 0x2C, 0x0D, 0x2B, 0x2C, 0x0B,
+ 0x0C, 0x3A, 0x1B, 0x2A, 0x3A },
+ { STV0900_QPSK_34, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B,
+ 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
+ { STV0900_QPSK_45, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B,
+ 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
+ { STV0900_QPSK_56, 0x0D, 0x0D, 0x3B, 0x1C, 0x0B,
+ 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
+ { STV0900_QPSK_89, 0x0D, 0x0D, 0x3B, 0x1C, 0x1B,
+ 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
+ { STV0900_QPSK_910, 0x1D, 0x0D, 0x3B, 0x1C, 0x1B,
+ 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
+ { STV0900_8PSK_35, 0x29, 0x3B, 0x09, 0x2B, 0x38,
+ 0x0B, 0x18, 0x1A, 0x08, 0x0A },
+ { STV0900_8PSK_23, 0x0A, 0x3B, 0x29, 0x2B, 0x19,
+ 0x0B, 0x38, 0x1A, 0x18, 0x0A },
+ { STV0900_8PSK_34, 0x3A, 0x3B, 0x2A, 0x2B, 0x39,
+ 0x0B, 0x19, 0x1A, 0x38, 0x0A },
+ { STV0900_8PSK_56, 0x1B, 0x3B, 0x0B, 0x2B, 0x1A,
+ 0x0B, 0x39, 0x1A, 0x19, 0x0A },
+ { STV0900_8PSK_89, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A,
+ 0x0B, 0x39, 0x1A, 0x29, 0x39 },
+ { STV0900_8PSK_910, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A,
+ 0x0B, 0x39, 0x1A, 0x29, 0x39 }
};
/* Cut 2.0 Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */
-static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut20[14] = {
- /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV0900_QPSK_12, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, 0x1F, 0x3D, 0x3E, 0x3D, 0x1E },
- { STV0900_QPSK_35, 0x2F, 0x3F, 0x2E, 0x2F, 0x3D, 0x0F, 0x0E, 0x2E, 0x3D, 0x0E },
- { STV0900_QPSK_23, 0x2F, 0x3F, 0x2E, 0x2F, 0x0E, 0x0F, 0x0E, 0x1E, 0x3D, 0x3D },
- { STV0900_QPSK_34, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
- { STV0900_QPSK_45, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
- { STV0900_QPSK_56, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
- { STV0900_QPSK_89, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
- { STV0900_QPSK_910, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
- { STV0900_8PSK_35, 0x3c, 0x0c, 0x1c, 0x3b, 0x0c, 0x3b, 0x2b, 0x2b, 0x1b, 0x2b },
- { STV0900_8PSK_23, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x3b, 0x0c, 0x2b, 0x2b, 0x2b },
- { STV0900_8PSK_34, 0x0e, 0x1c, 0x3d, 0x0c, 0x0d, 0x3b, 0x2c, 0x3b, 0x0c, 0x2b },
- { STV0900_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d },
- { STV0900_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d },
- { STV0900_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d }
+static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut20[14] = {
+ /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon
+ 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV0900_QPSK_12, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D,
+ 0x1F, 0x3D, 0x3E, 0x3D, 0x1E },
+ { STV0900_QPSK_35, 0x2F, 0x3F, 0x2E, 0x2F, 0x3D,
+ 0x0F, 0x0E, 0x2E, 0x3D, 0x0E },
+ { STV0900_QPSK_23, 0x2F, 0x3F, 0x2E, 0x2F, 0x0E,
+ 0x0F, 0x0E, 0x1E, 0x3D, 0x3D },
+ { STV0900_QPSK_34, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E,
+ 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
+ { STV0900_QPSK_45, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E,
+ 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
+ { STV0900_QPSK_56, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E,
+ 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
+ { STV0900_QPSK_89, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E,
+ 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
+ { STV0900_QPSK_910, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E,
+ 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
+ { STV0900_8PSK_35, 0x3c, 0x0c, 0x1c, 0x3b, 0x0c,
+ 0x3b, 0x2b, 0x2b, 0x1b, 0x2b },
+ { STV0900_8PSK_23, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c,
+ 0x3b, 0x0c, 0x2b, 0x2b, 0x2b },
+ { STV0900_8PSK_34, 0x0e, 0x1c, 0x3d, 0x0c, 0x0d,
+ 0x3b, 0x2c, 0x3b, 0x0c, 0x2b },
+ { STV0900_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d,
+ 0x1e, 0x3c, 0x2d, 0x2c, 0x1d },
+ { STV0900_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d,
+ 0x1e, 0x0d, 0x2d, 0x3c, 0x1d },
+ { STV0900_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d,
+ 0x1e, 0x1d, 0x2d, 0x0d, 0x1d },
};
/* Cut 2.0 Tracking carrier loop carrier 16APSK 2/3 to 32APSK 9/10 long Frame */
static const struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut20[11] = {
- /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV0900_16APSK_23, 0x0C, 0x0C, 0x0C, 0x0C, 0x1D, 0x0C, 0x3C, 0x0C, 0x2C, 0x0C },
- { STV0900_16APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0C, 0x2D, 0x0C, 0x1D, 0x0C },
- { STV0900_16APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x0C, 0x3D, 0x0C, 0x2D, 0x0C },
- { STV0900_16APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x0C, 0x3D, 0x0C, 0x2D, 0x0C },
- { STV0900_16APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, 0x0C, 0x0E, 0x0C, 0x3D, 0x0C },
- { STV0900_16APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, 0x0C, 0x0E, 0x0C, 0x3D, 0x0C },
- { STV0900_32APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
- { STV0900_32APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
- { STV0900_32APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
- { STV0900_32APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
- { STV0900_32APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }
+ /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon
+ 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV0900_16APSK_23, 0x0C, 0x0C, 0x0C, 0x0C, 0x1D,
+ 0x0C, 0x3C, 0x0C, 0x2C, 0x0C },
+ { STV0900_16APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E,
+ 0x0C, 0x2D, 0x0C, 0x1D, 0x0C },
+ { STV0900_16APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E,
+ 0x0C, 0x3D, 0x0C, 0x2D, 0x0C },
+ { STV0900_16APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E,
+ 0x0C, 0x3D, 0x0C, 0x2D, 0x0C },
+ { STV0900_16APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E,
+ 0x0C, 0x0E, 0x0C, 0x3D, 0x0C },
+ { STV0900_16APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E,
+ 0x0C, 0x0E, 0x0C, 0x3D, 0x0C },
+ { STV0900_32APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
+ { STV0900_32APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
+ { STV0900_32APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
+ { STV0900_32APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
+ { STV0900_32APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
};
/* Cut 2.0 Tracking carrier loop carrier QPSK 1/4 to QPSK 2/5 long Frame */
static const struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut20[3] = {
- /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV0900_QPSK_14, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, 0x2F, 0x2D, 0x1F, 0x3D, 0x3E },
- { STV0900_QPSK_13, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, 0x2F, 0x3D, 0x0F, 0x3D, 0x2E },
- { STV0900_QPSK_25, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, 0x1F, 0x3D, 0x3E, 0x3D, 0x2E }
+ /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon
+ 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV0900_QPSK_14, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D,
+ 0x2F, 0x2D, 0x1F, 0x3D, 0x3E },
+ { STV0900_QPSK_13, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D,
+ 0x2F, 0x3D, 0x0F, 0x3D, 0x2E },
+ { STV0900_QPSK_25, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D,
+ 0x1F, 0x3D, 0x3E, 0x3D, 0x2E }
};
/* Cut 2.0 Tracking carrier loop carrier short Frame, cut 1.2 and 2.0 */
-static const struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4] = {
- /*Mod 2M_cut1.2 2M_cut2.0 5M_cut1.2 5M_cut2.0 10M_cut1.2 10M_cut2.0 20M_cut1.2 20M_cut2.0 30M_cut1.2 30M_cut2.0 */
- { STV0900_QPSK, 0x3C, 0x2F, 0x2B, 0x2E, 0x0B, 0x0E, 0x3A, 0x0E, 0x2A, 0x3D },
- { STV0900_8PSK, 0x0B, 0x3E, 0x2A, 0x0E, 0x0A, 0x2D, 0x19, 0x0D, 0x09, 0x3C },
- { STV0900_16APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, 0x1E, 0x3A, 0x3D, 0x2A, 0x2D },
- { STV0900_32APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, 0x1E, 0x3A, 0x3D, 0x2A, 0x2D }
+static const
+struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4] = {
+ /*Mod 2Mcut1.2 2Mcut2.0 5Mcut1.2 5Mcut2.0 10Mcut1.2
+ 10Mcut2.0 20Mcut1.2 20M_cut2.0 30Mcut1.2 30Mcut2.0*/
+ { STV0900_QPSK, 0x3C, 0x2F, 0x2B, 0x2E, 0x0B,
+ 0x0E, 0x3A, 0x0E, 0x2A, 0x3D },
+ { STV0900_8PSK, 0x0B, 0x3E, 0x2A, 0x0E, 0x0A,
+ 0x2D, 0x19, 0x0D, 0x09, 0x3C },
+ { STV0900_16APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B,
+ 0x1E, 0x3A, 0x3D, 0x2A, 0x2D },
+ { STV0900_32APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B,
+ 0x1E, 0x3A, 0x3D, 0x2A, 0x2D }
+};
+
+static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut30[14] = {
+ /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon
+ 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV0900_QPSK_12, 0x3C, 0x2C, 0x0C, 0x2C, 0x1B,
+ 0x2C, 0x1B, 0x1C, 0x0B, 0x3B },
+ { STV0900_QPSK_35, 0x0D, 0x0D, 0x0C, 0x0D, 0x1B,
+ 0x3C, 0x1B, 0x1C, 0x0B, 0x3B },
+ { STV0900_QPSK_23, 0x1D, 0x0D, 0x0C, 0x1D, 0x2B,
+ 0x3C, 0x1B, 0x1C, 0x0B, 0x3B },
+ { STV0900_QPSK_34, 0x1D, 0x1D, 0x0C, 0x1D, 0x2B,
+ 0x3C, 0x1B, 0x1C, 0x0B, 0x3B },
+ { STV0900_QPSK_45, 0x2D, 0x1D, 0x1C, 0x1D, 0x2B,
+ 0x3C, 0x2B, 0x0C, 0x1B, 0x3B },
+ { STV0900_QPSK_56, 0x2D, 0x1D, 0x1C, 0x1D, 0x2B,
+ 0x3C, 0x2B, 0x0C, 0x1B, 0x3B },
+ { STV0900_QPSK_89, 0x3D, 0x2D, 0x1C, 0x1D, 0x3B,
+ 0x3C, 0x2B, 0x0C, 0x1B, 0x3B },
+ { STV0900_QPSK_910, 0x3D, 0x2D, 0x1C, 0x1D, 0x3B,
+ 0x3C, 0x2B, 0x0C, 0x1B, 0x3B },
+ { STV0900_8PSK_35, 0x39, 0x19, 0x39, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x09, 0x19 },
+ { STV0900_8PSK_23, 0x2A, 0x39, 0x1A, 0x0A, 0x39,
+ 0x0A, 0x29, 0x39, 0x29, 0x0A },
+ { STV0900_8PSK_34, 0x0B, 0x3A, 0x0B, 0x0B, 0x3A,
+ 0x1B, 0x1A, 0x0B, 0x1A, 0x3A },
+ { STV0900_8PSK_56, 0x0C, 0x1B, 0x3B, 0x2B, 0x1B,
+ 0x3B, 0x3A, 0x3B, 0x3A, 0x1B },
+ { STV0900_8PSK_89, 0x2C, 0x2C, 0x2C, 0x1C, 0x2B,
+ 0x0C, 0x0B, 0x3B, 0x0B, 0x1B },
+ { STV0900_8PSK_910, 0x2C, 0x3C, 0x2C, 0x1C, 0x3B,
+ 0x1C, 0x0B, 0x3B, 0x0B, 0x1B }
+};
+
+static const
+struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut30[11] = {
+ /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon
+ 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV0900_16APSK_23, 0x0A, 0x0A, 0x0A, 0x0A, 0x1A,
+ 0x0A, 0x3A, 0x0A, 0x2A, 0x0A },
+ { STV0900_16APSK_34, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B,
+ 0x0A, 0x3B, 0x0A, 0x1B, 0x0A },
+ { STV0900_16APSK_45, 0x0A, 0x0A, 0x0A, 0x0A, 0x1B,
+ 0x0A, 0x3B, 0x0A, 0x2B, 0x0A },
+ { STV0900_16APSK_56, 0x0A, 0x0A, 0x0A, 0x0A, 0x1B,
+ 0x0A, 0x3B, 0x0A, 0x2B, 0x0A },
+ { STV0900_16APSK_89, 0x0A, 0x0A, 0x0A, 0x0A, 0x2B,
+ 0x0A, 0x0C, 0x0A, 0x3B, 0x0A },
+ { STV0900_16APSK_910, 0x0A, 0x0A, 0x0A, 0x0A, 0x2B,
+ 0x0A, 0x0C, 0x0A, 0x3B, 0x0A },
+ { STV0900_32APSK_34, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A },
+ { STV0900_32APSK_45, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A },
+ { STV0900_32APSK_56, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A },
+ { STV0900_32APSK_89, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A },
+ { STV0900_32APSK_910, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }
+};
+
+static const
+struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut30[3] = {
+ /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon
+ 10MPoff 20MPon 20MPoff 30MPon 30MPoff*/
+ { STV0900_QPSK_14, 0x0C, 0x3C, 0x0B, 0x3C, 0x2A,
+ 0x2C, 0x2A, 0x1C, 0x3A, 0x3B },
+ { STV0900_QPSK_13, 0x0C, 0x3C, 0x0B, 0x3C, 0x2A,
+ 0x2C, 0x3A, 0x0C, 0x3A, 0x2B },
+ { STV0900_QPSK_25, 0x1C, 0x3C, 0x1B, 0x3C, 0x3A,
+ 0x1C, 0x3A, 0x3B, 0x3A, 0x2B }
+};
+
+static const struct stv0900_short_frames_car_loop_optim_vs_mod
+FE_STV0900_S2ShortCarLoopCut30[4] = {
+ /*Mod 2Mcut3.0 5Mcut3.0 10Mcut3.0 20Mcut3.0 30Mcut3.0*/
+ { STV0900_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A },
+ { STV0900_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 },
+ { STV0900_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A },
+ { STV0900_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A },
+
};
-static const u16 STV0900_InitVal[182][2] = {
+static const u16 STV0900_InitVal[181][2] = {
{ R0900_OUTCFG , 0x00 },
- { R0900_MODECFG , 0xff },
{ R0900_AGCRF1CFG , 0x11 },
{ R0900_AGCRF2CFG , 0x13 },
{ R0900_TSGENERAL1X , 0x14 },
@@ -381,7 +524,7 @@ static const u16 STV0900_InitVal[182][2] = {
{ R0900_GAINLLR_NF15 , 0x1A },
{ R0900_GAINLLR_NF16 , 0x1F },
{ R0900_GAINLLR_NF17 , 0x21 },
- { R0900_RCCFGH , 0x20 },
+ { R0900_RCCFG2 , 0x20 },
{ R0900_P1_FECM , 0x01 }, /*disable DSS modes*/
{ R0900_P2_FECM , 0x01 }, /*disable DSS modes*/
{ R0900_P1_PRVIT , 0x2F }, /*disable puncture rate 6/7*/
diff --git a/drivers/media/dvb/frontends/stv0900_priv.h b/drivers/media/dvb/frontends/stv0900_priv.h
index 5ed7a145c7d..d8ba8a984ab 100644
--- a/drivers/media/dvb/frontends/stv0900_priv.h
+++ b/drivers/media/dvb/frontends/stv0900_priv.h
@@ -46,22 +46,6 @@
#define FALSE (!TRUE)
#endif
-#define dmd_reg(a, b, c) \
- do { \
- a = 0; \
- switch (demod) { \
- case STV0900_DEMOD_1: \
- default: \
- a = b; \
- break; \
- case STV0900_DEMOD_2: \
- a = c; \
- break; \
- } \
- } while (0)
-
-static int stvdebug;
-
#define dprintk(args...) \
do { \
if (stvdebug) \
@@ -70,6 +54,8 @@ static int stvdebug;
#define STV0900_MAXLOOKUPSIZE 500
#define STV0900_BLIND_SEARCH_AGC2_TH 700
+#define STV0900_BLIND_SEARCH_AGC2_TH_CUT30 1400
+#define IQPOWER_THRESHOLD 30
/* One point of the lookup table */
struct stv000_lookpoint {
@@ -263,14 +249,14 @@ struct stv0900_init_params{
int tuner1_adc;
/* IQ from the tuner1 to the demod */
- enum stv0900_iq_inversion tun1_iq_inversion;
+ enum stv0900_iq_inversion tun1_iq_inv;
enum fe_stv0900_clock_type path2_ts_clock;
u8 tun2_maddress;
int tuner2_adc;
/* IQ from the tuner2 to the demod */
- enum stv0900_iq_inversion tun2_iq_inversion;
+ enum stv0900_iq_inversion tun2_iq_inv;
struct stv0900_reg *ts_config;
};
@@ -300,7 +286,7 @@ struct stv0900_signal_info {
enum fe_stv0900_modcode modcode;
enum fe_stv0900_modulation modulation;
enum fe_stv0900_pilot pilot;
- enum fe_stv0900_frame_length frame_length;
+ enum fe_stv0900_frame_length frame_len;
enum stv0900_iq_inversion spectrum;
enum fe_stv0900_rolloff rolloff;
@@ -318,47 +304,25 @@ struct stv0900_internal{
/* Demodulator use for single demod or for dual demod) */
enum fe_stv0900_demod_mode demod_mode;
- /*Demod 1*/
- s32 tuner1_freq;
- s32 tuner1_bw;
- s32 dmd1_symbol_rate;
- s32 dmd1_srch_range;
+ /*Demods */
+ s32 freq[2];
+ s32 bw[2];
+ s32 symbol_rate[2];
+ s32 srch_range[2];
/* algorithm for search Blind, Cold or Warm*/
- enum fe_stv0900_search_algo dmd1_srch_algo;
+ enum fe_stv0900_search_algo srch_algo[2];
/* search standard: Auto, DVBS1/DSS only or DVBS2 only*/
- enum fe_stv0900_search_standard dmd1_srch_standard;
+ enum fe_stv0900_search_standard srch_standard[2];
/* inversion search : auto, auto norma first, normal or inverted */
- enum fe_stv0900_search_iq dmd1_srch_iq_inv;
- enum fe_stv0900_modcode dmd1_modcode;
- enum fe_stv0900_modulation dmd1_modulation;
- enum fe_stv0900_fec dmd1_fec;
-
- struct stv0900_signal_info dmd1_rslts;
- enum fe_stv0900_signal_type dmd1_state;
+ enum fe_stv0900_search_iq srch_iq_inv[2];
+ enum fe_stv0900_modcode modcode[2];
+ enum fe_stv0900_modulation modulation[2];
+ enum fe_stv0900_fec fec[2];
- enum fe_stv0900_error dmd1_err;
+ struct stv0900_signal_info result[2];
+ enum fe_stv0900_error err[2];
- /*Demod 2*/
- s32 tuner2_freq;
- s32 tuner2_bw;
- s32 dmd2_symbol_rate;
- s32 dmd2_srch_range;
-
- enum fe_stv0900_search_algo dmd2_srch_algo;
- enum fe_stv0900_search_standard dmd2_srch_stndrd;
- /* inversion search : auto, auto normal first, normal or inverted */
- enum fe_stv0900_search_iq dmd2_srch_iq_inv;
- enum fe_stv0900_modcode dmd2_modcode;
- enum fe_stv0900_modulation dmd2_modulation;
- enum fe_stv0900_fec dmd2_fec;
-
- /* results of the search*/
- struct stv0900_signal_info dmd2_rslts;
- /* current state of the search algorithm */
- enum fe_stv0900_signal_type dmd2_state;
-
- enum fe_stv0900_error dmd2_err;
struct i2c_adapter *i2c_adap;
u8 i2c_addr;
@@ -379,6 +343,8 @@ struct stv0900_state {
int demod;
};
+extern int stvdebug;
+
extern s32 ge2comp(s32 a, s32 width);
extern void stv0900_write_reg(struct stv0900_internal *i_params,
@@ -418,13 +384,14 @@ extern u8 stv0900_get_optim_short_carr_loop(s32 srate,
extern void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params,
enum fe_stv0900_demod_num demod);
-extern void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
+extern void stv0900_activate_s2_modcod(struct stv0900_internal *i_params,
enum fe_stv0900_demod_num demod);
-extern void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params,
+extern void stv0900_activate_s2_modcod_single(struct stv0900_internal *i_params,
enum fe_stv0900_demod_num demod);
-extern enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
+extern enum
+fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
enum fe_stv0900_demod_num demod);
#endif
diff --git a/drivers/media/dvb/frontends/stv0900_reg.h b/drivers/media/dvb/frontends/stv0900_reg.h
index 264f9cf9a17..7b8edf192e9 100644
--- a/drivers/media/dvb/frontends/stv0900_reg.h
+++ b/drivers/media/dvb/frontends/stv0900_reg.h
@@ -14,7 +14,7 @@
*
* 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
@@ -26,3762 +26,3950 @@
#ifndef STV0900_REG_H
#define STV0900_REG_H
+extern s32 shiftx(s32 x, int demod, s32 shift);
+
+#define REGx(x) shiftx(x, demod, 0x200)
+#define FLDx(x) shiftx(x, demod, 0x2000000)
+
/*MID*/
-#define R0900_MID 0xf100
-#define F0900_MCHIP_IDENT 0xf10000f0
-#define F0900_MRELEASE 0xf100000f
+#define R0900_MID 0xf100
+#define F0900_MCHIP_IDENT 0xf10000f0
+#define F0900_MRELEASE 0xf100000f
/*DACR1*/
-#define R0900_DACR1 0xf113
-#define F0900_DAC_MODE 0xf11300e0
-#define F0900_DAC_VALUE1 0xf113000f
+#define R0900_DACR1 0xf113
+#define F0900_DAC_MODE 0xf11300e0
+#define F0900_DAC_VALUE1 0xf113000f
/*DACR2*/
-#define R0900_DACR2 0xf114
-#define F0900_DAC_VALUE0 0xf11400ff
+#define R0900_DACR2 0xf114
+#define F0900_DAC_VALUE0 0xf11400ff
/*OUTCFG*/
-#define R0900_OUTCFG 0xf11c
-#define F0900_INV_DATA6 0xf11c0080
-#define F0900_OUTSERRS1_HZ 0xf11c0040
-#define F0900_OUTSERRS2_HZ 0xf11c0020
-#define F0900_OUTSERRS3_HZ 0xf11c0010
-#define F0900_OUTPARRS3_HZ 0xf11c0008
-#define F0900_OUTHZ3_CONTROL 0xf11c0007
-
-/*MODECFG*/
-#define R0900_MODECFG 0xf11d
-#define F0900_FECSPY_SEL_2 0xf11d0020
-#define F0900_HWARE_SEL_2 0xf11d0010
-#define F0900_PKTDEL_SEL_2 0xf11d0008
-#define F0900_DISEQC_SEL_2 0xf11d0004
-#define F0900_VIT_SEL_2 0xf11d0002
-#define F0900_DEMOD_SEL_2 0xf11d0001
+#define R0900_OUTCFG 0xf11c
+#define F0900_OUTSERRS1_HZ 0xf11c0040
+#define F0900_OUTSERRS2_HZ 0xf11c0020
+#define F0900_OUTSERRS3_HZ 0xf11c0010
+#define F0900_OUTPARRS3_HZ 0xf11c0008
/*IRQSTATUS3*/
-#define R0900_IRQSTATUS3 0xf120
-#define F0900_SPLL_LOCK 0xf1200020
-#define F0900_SSTREAM_LCK_3 0xf1200010
-#define F0900_SSTREAM_LCK_2 0xf1200008
-#define F0900_SSTREAM_LCK_1 0xf1200004
-#define F0900_SDVBS1_PRF_2 0xf1200002
-#define F0900_SDVBS1_PRF_1 0xf1200001
+#define R0900_IRQSTATUS3 0xf120
+#define F0900_SPLL_LOCK 0xf1200020
+#define F0900_SSTREAM_LCK_3 0xf1200010
+#define F0900_SSTREAM_LCK_2 0xf1200008
+#define F0900_SSTREAM_LCK_1 0xf1200004
+#define F0900_SDVBS1_PRF_2 0xf1200002
+#define F0900_SDVBS1_PRF_1 0xf1200001
/*IRQSTATUS2*/
-#define R0900_IRQSTATUS2 0xf121
-#define F0900_SSPY_ENDSIM_3 0xf1210080
-#define F0900_SSPY_ENDSIM_2 0xf1210040
-#define F0900_SSPY_ENDSIM_1 0xf1210020
-#define F0900_SPKTDEL_ERROR_2 0xf1210010
-#define F0900_SPKTDEL_LOCKB_2 0xf1210008
-#define F0900_SPKTDEL_LOCK_2 0xf1210004
-#define F0900_SPKTDEL_ERROR_1 0xf1210002
-#define F0900_SPKTDEL_LOCKB_1 0xf1210001
+#define R0900_IRQSTATUS2 0xf121
+#define F0900_SSPY_ENDSIM_3 0xf1210080
+#define F0900_SSPY_ENDSIM_2 0xf1210040
+#define F0900_SSPY_ENDSIM_1 0xf1210020
+#define F0900_SPKTDEL_ERROR_2 0xf1210010
+#define F0900_SPKTDEL_LOCKB_2 0xf1210008
+#define F0900_SPKTDEL_LOCK_2 0xf1210004
+#define F0900_SPKTDEL_ERROR_1 0xf1210002
+#define F0900_SPKTDEL_LOCKB_1 0xf1210001
/*IRQSTATUS1*/
-#define R0900_IRQSTATUS1 0xf122
-#define F0900_SPKTDEL_LOCK_1 0xf1220080
-#define F0900_SEXTPINB2 0xf1220040
-#define F0900_SEXTPIN2 0xf1220020
-#define F0900_SEXTPINB1 0xf1220010
-#define F0900_SEXTPIN1 0xf1220008
-#define F0900_SDEMOD_LOCKB_2 0xf1220004
-#define F0900_SDEMOD_LOCK_2 0xf1220002
-#define F0900_SDEMOD_IRQ_2 0xf1220001
+#define R0900_IRQSTATUS1 0xf122
+#define F0900_SPKTDEL_LOCK_1 0xf1220080
+#define F0900_SDEMOD_LOCKB_2 0xf1220004
+#define F0900_SDEMOD_LOCK_2 0xf1220002
+#define F0900_SDEMOD_IRQ_2 0xf1220001
/*IRQSTATUS0*/
-#define R0900_IRQSTATUS0 0xf123
-#define F0900_SDEMOD_LOCKB_1 0xf1230080
-#define F0900_SDEMOD_LOCK_1 0xf1230040
-#define F0900_SDEMOD_IRQ_1 0xf1230020
-#define F0900_SBCH_ERRFLAG 0xf1230010
-#define F0900_SDISEQC2RX_IRQ 0xf1230008
-#define F0900_SDISEQC2TX_IRQ 0xf1230004
-#define F0900_SDISEQC1RX_IRQ 0xf1230002
-#define F0900_SDISEQC1TX_IRQ 0xf1230001
+#define R0900_IRQSTATUS0 0xf123
+#define F0900_SDEMOD_LOCKB_1 0xf1230080
+#define F0900_SDEMOD_LOCK_1 0xf1230040
+#define F0900_SDEMOD_IRQ_1 0xf1230020
+#define F0900_SBCH_ERRFLAG 0xf1230010
+#define F0900_SDISEQC2RX_IRQ 0xf1230008
+#define F0900_SDISEQC2TX_IRQ 0xf1230004
+#define F0900_SDISEQC1RX_IRQ 0xf1230002
+#define F0900_SDISEQC1TX_IRQ 0xf1230001
/*IRQMASK3*/
-#define R0900_IRQMASK3 0xf124
-#define F0900_MPLL_LOCK 0xf1240020
-#define F0900_MSTREAM_LCK_3 0xf1240010
-#define F0900_MSTREAM_LCK_2 0xf1240008
-#define F0900_MSTREAM_LCK_1 0xf1240004
-#define F0900_MDVBS1_PRF_2 0xf1240002
-#define F0900_MDVBS1_PRF_1 0xf1240001
+#define R0900_IRQMASK3 0xf124
+#define F0900_MPLL_LOCK 0xf1240020
+#define F0900_MSTREAM_LCK_3 0xf1240010
+#define F0900_MSTREAM_LCK_2 0xf1240008
+#define F0900_MSTREAM_LCK_1 0xf1240004
+#define F0900_MDVBS1_PRF_2 0xf1240002
+#define F0900_MDVBS1_PRF_1 0xf1240001
/*IRQMASK2*/
-#define R0900_IRQMASK2 0xf125
-#define F0900_MSPY_ENDSIM_3 0xf1250080
-#define F0900_MSPY_ENDSIM_2 0xf1250040
-#define F0900_MSPY_ENDSIM_1 0xf1250020
-#define F0900_MPKTDEL_ERROR_2 0xf1250010
-#define F0900_MPKTDEL_LOCKB_2 0xf1250008
-#define F0900_MPKTDEL_LOCK_2 0xf1250004
-#define F0900_MPKTDEL_ERROR_1 0xf1250002
-#define F0900_MPKTDEL_LOCKB_1 0xf1250001
+#define R0900_IRQMASK2 0xf125
+#define F0900_MSPY_ENDSIM_3 0xf1250080
+#define F0900_MSPY_ENDSIM_2 0xf1250040
+#define F0900_MSPY_ENDSIM_1 0xf1250020
+#define F0900_MPKTDEL_ERROR_2 0xf1250010
+#define F0900_MPKTDEL_LOCKB_2 0xf1250008
+#define F0900_MPKTDEL_LOCK_2 0xf1250004
+#define F0900_MPKTDEL_ERROR_1 0xf1250002
+#define F0900_MPKTDEL_LOCKB_1 0xf1250001
/*IRQMASK1*/
-#define R0900_IRQMASK1 0xf126
-#define F0900_MPKTDEL_LOCK_1 0xf1260080
-#define F0900_MEXTPINB2 0xf1260040
-#define F0900_MEXTPIN2 0xf1260020
-#define F0900_MEXTPINB1 0xf1260010
-#define F0900_MEXTPIN1 0xf1260008
-#define F0900_MDEMOD_LOCKB_2 0xf1260004
-#define F0900_MDEMOD_LOCK_2 0xf1260002
-#define F0900_MDEMOD_IRQ_2 0xf1260001
+#define R0900_IRQMASK1 0xf126
+#define F0900_MPKTDEL_LOCK_1 0xf1260080
+#define F0900_MEXTPINB2 0xf1260040
+#define F0900_MEXTPIN2 0xf1260020
+#define F0900_MEXTPINB1 0xf1260010
+#define F0900_MEXTPIN1 0xf1260008
+#define F0900_MDEMOD_LOCKB_2 0xf1260004
+#define F0900_MDEMOD_LOCK_2 0xf1260002
+#define F0900_MDEMOD_IRQ_2 0xf1260001
/*IRQMASK0*/
-#define R0900_IRQMASK0 0xf127
-#define F0900_MDEMOD_LOCKB_1 0xf1270080
-#define F0900_MDEMOD_LOCK_1 0xf1270040
-#define F0900_MDEMOD_IRQ_1 0xf1270020
-#define F0900_MBCH_ERRFLAG 0xf1270010
-#define F0900_MDISEQC2RX_IRQ 0xf1270008
-#define F0900_MDISEQC2TX_IRQ 0xf1270004
-#define F0900_MDISEQC1RX_IRQ 0xf1270002
-#define F0900_MDISEQC1TX_IRQ 0xf1270001
+#define R0900_IRQMASK0 0xf127
+#define F0900_MDEMOD_LOCKB_1 0xf1270080
+#define F0900_MDEMOD_LOCK_1 0xf1270040
+#define F0900_MDEMOD_IRQ_1 0xf1270020
+#define F0900_MBCH_ERRFLAG 0xf1270010
+#define F0900_MDISEQC2RX_IRQ 0xf1270008
+#define F0900_MDISEQC2TX_IRQ 0xf1270004
+#define F0900_MDISEQC1RX_IRQ 0xf1270002
+#define F0900_MDISEQC1TX_IRQ 0xf1270001
/*I2CCFG*/
-#define R0900_I2CCFG 0xf129
-#define F0900_I2C2_FASTMODE 0xf1290080
-#define F0900_STATUS_WR2 0xf1290040
-#define F0900_I2C2ADDR_INC 0xf1290030
-#define F0900_I2C_FASTMODE 0xf1290008
-#define F0900_STATUS_WR 0xf1290004
-#define F0900_I2CADDR_INC 0xf1290003
+#define R0900_I2CCFG 0xf129
+#define F0900_I2C_FASTMODE 0xf1290008
+#define F0900_I2CADDR_INC 0xf1290003
/*P1_I2CRPT*/
-#define R0900_P1_I2CRPT 0xf12a
-#define F0900_P1_I2CT_ON 0xf12a0080
-#define F0900_P1_ENARPT_LEVEL 0xf12a0070
-#define F0900_P1_SCLT_DELAY 0xf12a0008
-#define F0900_P1_STOP_ENABLE 0xf12a0004
-#define F0900_P1_STOP_SDAT2SDA 0xf12a0002
+#define R0900_P1_I2CRPT 0xf12a
+#define I2CRPT shiftx(R0900_P1_I2CRPT, demod, -1)
+#define F0900_P1_I2CT_ON 0xf12a0080
+#define I2CT_ON shiftx(F0900_P1_I2CT_ON, demod, -0x10000)
+#define F0900_P1_ENARPT_LEVEL 0xf12a0070
+#define F0900_P1_SCLT_DELAY 0xf12a0008
+#define F0900_P1_STOP_ENABLE 0xf12a0004
+#define F0900_P1_STOP_SDAT2SDA 0xf12a0002
/*P2_I2CRPT*/
-#define R0900_P2_I2CRPT 0xf12b
-#define F0900_P2_I2CT_ON 0xf12b0080
-#define F0900_P2_ENARPT_LEVEL 0xf12b0070
-#define F0900_P2_SCLT_DELAY 0xf12b0008
-#define F0900_P2_STOP_ENABLE 0xf12b0004
-#define F0900_P2_STOP_SDAT2SDA 0xf12b0002
+#define R0900_P2_I2CRPT 0xf12b
+#define F0900_P2_I2CT_ON 0xf12b0080
+#define F0900_P2_ENARPT_LEVEL 0xf12b0070
+#define F0900_P2_SCLT_DELAY 0xf12b0008
+#define F0900_P2_STOP_ENABLE 0xf12b0004
+#define F0900_P2_STOP_SDAT2SDA 0xf12b0002
+
+/*IOPVALUE6*/
+#define R0900_IOPVALUE6 0xf138
+#define F0900_VSCL 0xf1380004
+#define F0900_VSDA 0xf1380002
+#define F0900_VDATA3_0 0xf1380001
+
+/*IOPVALUE5*/
+#define R0900_IOPVALUE5 0xf139
+#define F0900_VDATA3_1 0xf1390080
+#define F0900_VDATA3_2 0xf1390040
+#define F0900_VDATA3_3 0xf1390020
+#define F0900_VDATA3_4 0xf1390010
+#define F0900_VDATA3_5 0xf1390008
+#define F0900_VDATA3_6 0xf1390004
+#define F0900_VDATA3_7 0xf1390002
+#define F0900_VCLKOUT3 0xf1390001
+
+/*IOPVALUE4*/
+#define R0900_IOPVALUE4 0xf13a
+#define F0900_VSTROUT3 0xf13a0080
+#define F0900_VDPN3 0xf13a0040
+#define F0900_VERROR3 0xf13a0020
+#define F0900_VDATA2_7 0xf13a0010
+#define F0900_VCLKOUT2 0xf13a0008
+#define F0900_VSTROUT2 0xf13a0004
+#define F0900_VDPN2 0xf13a0002
+#define F0900_VERROR2 0xf13a0001
+
+/*IOPVALUE3*/
+#define R0900_IOPVALUE3 0xf13b
+#define F0900_VDATA1_7 0xf13b0080
+#define F0900_VCLKOUT1 0xf13b0040
+#define F0900_VSTROUT1 0xf13b0020
+#define F0900_VDPN1 0xf13b0010
+#define F0900_VERROR1 0xf13b0008
+#define F0900_VCLKOUT27 0xf13b0004
+#define F0900_VDISEQCOUT2 0xf13b0002
+#define F0900_VSCLT2 0xf13b0001
+
+/*IOPVALUE2*/
+#define R0900_IOPVALUE2 0xf13c
+#define F0900_VSDAT2 0xf13c0080
+#define F0900_VAGCRF2 0xf13c0040
+#define F0900_VDISEQCOUT1 0xf13c0020
+#define F0900_VSCLT1 0xf13c0010
+#define F0900_VSDAT1 0xf13c0008
+#define F0900_VAGCRF1 0xf13c0004
+#define F0900_VDIRCLK 0xf13c0002
+#define F0900_VSTDBY 0xf13c0001
+
+/*IOPVALUE1*/
+#define R0900_IOPVALUE1 0xf13d
+#define F0900_VCS1 0xf13d0080
+#define F0900_VCS0 0xf13d0040
+#define F0900_VGPIO13 0xf13d0020
+#define F0900_VGPIO12 0xf13d0010
+#define F0900_VGPIO11 0xf13d0008
+#define F0900_VGPIO10 0xf13d0004
+#define F0900_VGPIO9 0xf13d0002
+#define F0900_VGPIO8 0xf13d0001
+
+/*IOPVALUE0*/
+#define R0900_IOPVALUE0 0xf13e
+#define F0900_VGPIO7 0xf13e0080
+#define F0900_VGPIO6 0xf13e0040
+#define F0900_VGPIO5 0xf13e0020
+#define F0900_VGPIO4 0xf13e0010
+#define F0900_VGPIO3 0xf13e0008
+#define F0900_VGPIO2 0xf13e0004
+#define F0900_VGPIO1 0xf13e0002
+#define F0900_VCLKI2 0xf13e0001
/*CLKI2CFG*/
-#define R0900_CLKI2CFG 0xf140
-#define F0900_CLKI2_OPD 0xf1400080
-#define F0900_CLKI2_CONFIG 0xf140007e
-#define F0900_CLKI2_XOR 0xf1400001
+#define R0900_CLKI2CFG 0xf140
+#define F0900_CLKI2_OPD 0xf1400080
+#define F0900_CLKI2_CONFIG 0xf140007e
+#define F0900_CLKI2_XOR 0xf1400001
/*GPIO1CFG*/
-#define R0900_GPIO1CFG 0xf141
-#define F0900_GPIO1_OPD 0xf1410080
-#define F0900_GPIO1_CONFIG 0xf141007e
-#define F0900_GPIO1_XOR 0xf1410001
+#define R0900_GPIO1CFG 0xf141
+#define F0900_GPIO1_OPD 0xf1410080
+#define F0900_GPIO1_CONFIG 0xf141007e
+#define F0900_GPIO1_XOR 0xf1410001
/*GPIO2CFG*/
-#define R0900_GPIO2CFG 0xf142
-#define F0900_GPIO2_OPD 0xf1420080
-#define F0900_GPIO2_CONFIG 0xf142007e
-#define F0900_GPIO2_XOR 0xf1420001
+#define R0900_GPIO2CFG 0xf142
+#define F0900_GPIO2_OPD 0xf1420080
+#define F0900_GPIO2_CONFIG 0xf142007e
+#define F0900_GPIO2_XOR 0xf1420001
/*GPIO3CFG*/
-#define R0900_GPIO3CFG 0xf143
-#define F0900_GPIO3_OPD 0xf1430080
-#define F0900_GPIO3_CONFIG 0xf143007e
-#define F0900_GPIO3_XOR 0xf1430001
+#define R0900_GPIO3CFG 0xf143
+#define F0900_GPIO3_OPD 0xf1430080
+#define F0900_GPIO3_CONFIG 0xf143007e
+#define F0900_GPIO3_XOR 0xf1430001
/*GPIO4CFG*/
-#define R0900_GPIO4CFG 0xf144
-#define F0900_GPIO4_OPD 0xf1440080
-#define F0900_GPIO4_CONFIG 0xf144007e
-#define F0900_GPIO4_XOR 0xf1440001
+#define R0900_GPIO4CFG 0xf144
+#define F0900_GPIO4_OPD 0xf1440080
+#define F0900_GPIO4_CONFIG 0xf144007e
+#define F0900_GPIO4_XOR 0xf1440001
/*GPIO5CFG*/
-#define R0900_GPIO5CFG 0xf145
-#define F0900_GPIO5_OPD 0xf1450080
-#define F0900_GPIO5_CONFIG 0xf145007e
-#define F0900_GPIO5_XOR 0xf1450001
+#define R0900_GPIO5CFG 0xf145
+#define F0900_GPIO5_OPD 0xf1450080
+#define F0900_GPIO5_CONFIG 0xf145007e
+#define F0900_GPIO5_XOR 0xf1450001
/*GPIO6CFG*/
-#define R0900_GPIO6CFG 0xf146
-#define F0900_GPIO6_OPD 0xf1460080
-#define F0900_GPIO6_CONFIG 0xf146007e
-#define F0900_GPIO6_XOR 0xf1460001
+#define R0900_GPIO6CFG 0xf146
+#define F0900_GPIO6_OPD 0xf1460080
+#define F0900_GPIO6_CONFIG 0xf146007e
+#define F0900_GPIO6_XOR 0xf1460001
/*GPIO7CFG*/
-#define R0900_GPIO7CFG 0xf147
-#define F0900_GPIO7_OPD 0xf1470080
-#define F0900_GPIO7_CONFIG 0xf147007e
-#define F0900_GPIO7_XOR 0xf1470001
+#define R0900_GPIO7CFG 0xf147
+#define F0900_GPIO7_OPD 0xf1470080
+#define F0900_GPIO7_CONFIG 0xf147007e
+#define F0900_GPIO7_XOR 0xf1470001
/*GPIO8CFG*/
-#define R0900_GPIO8CFG 0xf148
-#define F0900_GPIO8_OPD 0xf1480080
-#define F0900_GPIO8_CONFIG 0xf148007e
-#define F0900_GPIO8_XOR 0xf1480001
+#define R0900_GPIO8CFG 0xf148
+#define F0900_GPIO8_OPD 0xf1480080
+#define F0900_GPIO8_CONFIG 0xf148007e
+#define F0900_GPIO8_XOR 0xf1480001
/*GPIO9CFG*/
-#define R0900_GPIO9CFG 0xf149
-#define F0900_GPIO9_OPD 0xf1490080
-#define F0900_GPIO9_CONFIG 0xf149007e
-#define F0900_GPIO9_XOR 0xf1490001
+#define R0900_GPIO9CFG 0xf149
+#define F0900_GPIO9_OPD 0xf1490080
+#define F0900_GPIO9_CONFIG 0xf149007e
+#define F0900_GPIO9_XOR 0xf1490001
/*GPIO10CFG*/
-#define R0900_GPIO10CFG 0xf14a
-#define F0900_GPIO10_OPD 0xf14a0080
-#define F0900_GPIO10_CONFIG 0xf14a007e
-#define F0900_GPIO10_XOR 0xf14a0001
+#define R0900_GPIO10CFG 0xf14a
+#define F0900_GPIO10_OPD 0xf14a0080
+#define F0900_GPIO10_CONFIG 0xf14a007e
+#define F0900_GPIO10_XOR 0xf14a0001
/*GPIO11CFG*/
-#define R0900_GPIO11CFG 0xf14b
-#define F0900_GPIO11_OPD 0xf14b0080
-#define F0900_GPIO11_CONFIG 0xf14b007e
-#define F0900_GPIO11_XOR 0xf14b0001
+#define R0900_GPIO11CFG 0xf14b
+#define F0900_GPIO11_OPD 0xf14b0080
+#define F0900_GPIO11_CONFIG 0xf14b007e
+#define F0900_GPIO11_XOR 0xf14b0001
/*GPIO12CFG*/
-#define R0900_GPIO12CFG 0xf14c
-#define F0900_GPIO12_OPD 0xf14c0080
-#define F0900_GPIO12_CONFIG 0xf14c007e
-#define F0900_GPIO12_XOR 0xf14c0001
+#define R0900_GPIO12CFG 0xf14c
+#define F0900_GPIO12_OPD 0xf14c0080
+#define F0900_GPIO12_CONFIG 0xf14c007e
+#define F0900_GPIO12_XOR 0xf14c0001
/*GPIO13CFG*/
-#define R0900_GPIO13CFG 0xf14d
-#define F0900_GPIO13_OPD 0xf14d0080
-#define F0900_GPIO13_CONFIG 0xf14d007e
-#define F0900_GPIO13_XOR 0xf14d0001
+#define R0900_GPIO13CFG 0xf14d
+#define F0900_GPIO13_OPD 0xf14d0080
+#define F0900_GPIO13_CONFIG 0xf14d007e
+#define F0900_GPIO13_XOR 0xf14d0001
/*CS0CFG*/
-#define R0900_CS0CFG 0xf14e
-#define F0900_CS0_OPD 0xf14e0080
-#define F0900_CS0_CONFIG 0xf14e007e
-#define F0900_CS0_XOR 0xf14e0001
+#define R0900_CS0CFG 0xf14e
+#define F0900_CS0_OPD 0xf14e0080
+#define F0900_CS0_CONFIG 0xf14e007e
+#define F0900_CS0_XOR 0xf14e0001
/*CS1CFG*/
-#define R0900_CS1CFG 0xf14f
-#define F0900_CS1_OPD 0xf14f0080
-#define F0900_CS1_CONFIG 0xf14f007e
-#define F0900_CS1_XOR 0xf14f0001
+#define R0900_CS1CFG 0xf14f
+#define F0900_CS1_OPD 0xf14f0080
+#define F0900_CS1_CONFIG 0xf14f007e
+#define F0900_CS1_XOR 0xf14f0001
/*STDBYCFG*/
-#define R0900_STDBYCFG 0xf150
-#define F0900_STDBY_OPD 0xf1500080
-#define F0900_STDBY_CONFIG 0xf150007e
-#define F0900_STBDY_XOR 0xf1500001
+#define R0900_STDBYCFG 0xf150
+#define F0900_STDBY_OPD 0xf1500080
+#define F0900_STDBY_CONFIG 0xf150007e
+#define F0900_STBDY_XOR 0xf1500001
/*DIRCLKCFG*/
-#define R0900_DIRCLKCFG 0xf151
-#define F0900_DIRCLK_OPD 0xf1510080
-#define F0900_DIRCLK_CONFIG 0xf151007e
-#define F0900_DIRCLK_XOR 0xf1510001
+#define R0900_DIRCLKCFG 0xf151
+#define F0900_DIRCLK_OPD 0xf1510080
+#define F0900_DIRCLK_CONFIG 0xf151007e
+#define F0900_DIRCLK_XOR 0xf1510001
/*AGCRF1CFG*/
-#define R0900_AGCRF1CFG 0xf152
-#define F0900_AGCRF1_OPD 0xf1520080
-#define F0900_AGCRF1_CONFIG 0xf152007e
-#define F0900_AGCRF1_XOR 0xf1520001
+#define R0900_AGCRF1CFG 0xf152
+#define F0900_AGCRF1_OPD 0xf1520080
+#define F0900_AGCRF1_CONFIG 0xf152007e
+#define F0900_AGCRF1_XOR 0xf1520001
/*SDAT1CFG*/
-#define R0900_SDAT1CFG 0xf153
-#define F0900_SDAT1_OPD 0xf1530080
-#define F0900_SDAT1_CONFIG 0xf153007e
-#define F0900_SDAT1_XOR 0xf1530001
+#define R0900_SDAT1CFG 0xf153
+#define F0900_SDAT1_OPD 0xf1530080
+#define F0900_SDAT1_CONFIG 0xf153007e
+#define F0900_SDAT1_XOR 0xf1530001
/*SCLT1CFG*/
-#define R0900_SCLT1CFG 0xf154
-#define F0900_SCLT1_OPD 0xf1540080
-#define F0900_SCLT1_CONFIG 0xf154007e
-#define F0900_SCLT1_XOR 0xf1540001
+#define R0900_SCLT1CFG 0xf154
+#define F0900_SCLT1_OPD 0xf1540080
+#define F0900_SCLT1_CONFIG 0xf154007e
+#define F0900_SCLT1_XOR 0xf1540001
/*DISEQCO1CFG*/
-#define R0900_DISEQCO1CFG 0xf155
-#define F0900_DISEQCO1_OPD 0xf1550080
-#define F0900_DISEQCO1_CONFIG 0xf155007e
-#define F0900_DISEQC1_XOR 0xf1550001
+#define R0900_DISEQCO1CFG 0xf155
+#define F0900_DISEQCO1_OPD 0xf1550080
+#define F0900_DISEQCO1_CONFIG 0xf155007e
+#define F0900_DISEQC1_XOR 0xf1550001
/*AGCRF2CFG*/
-#define R0900_AGCRF2CFG 0xf156
-#define F0900_AGCRF2_OPD 0xf1560080
-#define F0900_AGCRF2_CONFIG 0xf156007e
-#define F0900_AGCRF2_XOR 0xf1560001
+#define R0900_AGCRF2CFG 0xf156
+#define F0900_AGCRF2_OPD 0xf1560080
+#define F0900_AGCRF2_CONFIG 0xf156007e
+#define F0900_AGCRF2_XOR 0xf1560001
/*SDAT2CFG*/
-#define R0900_SDAT2CFG 0xf157
-#define F0900_SDAT2_OPD 0xf1570080
-#define F0900_SDAT2_CONFIG 0xf157007e
-#define F0900_SDAT2_XOR 0xf1570001
+#define R0900_SDAT2CFG 0xf157
+#define F0900_SDAT2_OPD 0xf1570080
+#define F0900_SDAT2_CONFIG 0xf157007e
+#define F0900_SDAT2_XOR 0xf1570001
/*SCLT2CFG*/
-#define R0900_SCLT2CFG 0xf158
-#define F0900_SCLT2_OPD 0xf1580080
-#define F0900_SCLT2_CONFIG 0xf158007e
-#define F0900_SCLT2_XOR 0xf1580001
+#define R0900_SCLT2CFG 0xf158
+#define F0900_SCLT2_OPD 0xf1580080
+#define F0900_SCLT2_CONFIG 0xf158007e
+#define F0900_SCLT2_XOR 0xf1580001
/*DISEQCO2CFG*/
-#define R0900_DISEQCO2CFG 0xf159
-#define F0900_DISEQCO2_OPD 0xf1590080
-#define F0900_DISEQCO2_CONFIG 0xf159007e
-#define F0900_DISEQC2_XOR 0xf1590001
+#define R0900_DISEQCO2CFG 0xf159
+#define F0900_DISEQCO2_OPD 0xf1590080
+#define F0900_DISEQCO2_CONFIG 0xf159007e
+#define F0900_DISEQC2_XOR 0xf1590001
/*CLKOUT27CFG*/
-#define R0900_CLKOUT27CFG 0xf15a
-#define F0900_CLKOUT27_OPD 0xf15a0080
-#define F0900_CLKOUT27_CONFIG 0xf15a007e
-#define F0900_CLKOUT27_XOR 0xf15a0001
+#define R0900_CLKOUT27CFG 0xf15a
+#define F0900_CLKOUT27_OPD 0xf15a0080
+#define F0900_CLKOUT27_CONFIG 0xf15a007e
+#define F0900_CLKOUT27_XOR 0xf15a0001
/*ERROR1CFG*/
-#define R0900_ERROR1CFG 0xf15b
-#define F0900_ERROR1_OPD 0xf15b0080
-#define F0900_ERROR1_CONFIG 0xf15b007e
-#define F0900_ERROR1_XOR 0xf15b0001
+#define R0900_ERROR1CFG 0xf15b
+#define F0900_ERROR1_OPD 0xf15b0080
+#define F0900_ERROR1_CONFIG 0xf15b007e
+#define F0900_ERROR1_XOR 0xf15b0001
/*DPN1CFG*/
-#define R0900_DPN1CFG 0xf15c
-#define F0900_DPN1_OPD 0xf15c0080
-#define F0900_DPN1_CONFIG 0xf15c007e
-#define F0900_DPN1_XOR 0xf15c0001
+#define R0900_DPN1CFG 0xf15c
+#define F0900_DPN1_OPD 0xf15c0080
+#define F0900_DPN1_CONFIG 0xf15c007e
+#define F0900_DPN1_XOR 0xf15c0001
/*STROUT1CFG*/
-#define R0900_STROUT1CFG 0xf15d
-#define F0900_STROUT1_OPD 0xf15d0080
-#define F0900_STROUT1_CONFIG 0xf15d007e
-#define F0900_STROUT1_XOR 0xf15d0001
+#define R0900_STROUT1CFG 0xf15d
+#define F0900_STROUT1_OPD 0xf15d0080
+#define F0900_STROUT1_CONFIG 0xf15d007e
+#define F0900_STROUT1_XOR 0xf15d0001
/*CLKOUT1CFG*/
-#define R0900_CLKOUT1CFG 0xf15e
-#define F0900_CLKOUT1_OPD 0xf15e0080
-#define F0900_CLKOUT1_CONFIG 0xf15e007e
-#define F0900_CLKOUT1_XOR 0xf15e0001
+#define R0900_CLKOUT1CFG 0xf15e
+#define F0900_CLKOUT1_OPD 0xf15e0080
+#define F0900_CLKOUT1_CONFIG 0xf15e007e
+#define F0900_CLKOUT1_XOR 0xf15e0001
/*DATA71CFG*/
-#define R0900_DATA71CFG 0xf15f
-#define F0900_DATA71_OPD 0xf15f0080
-#define F0900_DATA71_CONFIG 0xf15f007e
-#define F0900_DATA71_XOR 0xf15f0001
+#define R0900_DATA71CFG 0xf15f
+#define F0900_DATA71_OPD 0xf15f0080
+#define F0900_DATA71_CONFIG 0xf15f007e
+#define F0900_DATA71_XOR 0xf15f0001
/*ERROR2CFG*/
-#define R0900_ERROR2CFG 0xf160
-#define F0900_ERROR2_OPD 0xf1600080
-#define F0900_ERROR2_CONFIG 0xf160007e
-#define F0900_ERROR2_XOR 0xf1600001
+#define R0900_ERROR2CFG 0xf160
+#define F0900_ERROR2_OPD 0xf1600080
+#define F0900_ERROR2_CONFIG 0xf160007e
+#define F0900_ERROR2_XOR 0xf1600001
/*DPN2CFG*/
-#define R0900_DPN2CFG 0xf161
-#define F0900_DPN2_OPD 0xf1610080
-#define F0900_DPN2_CONFIG 0xf161007e
-#define F0900_DPN2_XOR 0xf1610001
+#define R0900_DPN2CFG 0xf161
+#define F0900_DPN2_OPD 0xf1610080
+#define F0900_DPN2_CONFIG 0xf161007e
+#define F0900_DPN2_XOR 0xf1610001
/*STROUT2CFG*/
-#define R0900_STROUT2CFG 0xf162
-#define F0900_STROUT2_OPD 0xf1620080
-#define F0900_STROUT2_CONFIG 0xf162007e
-#define F0900_STROUT2_XOR 0xf1620001
+#define R0900_STROUT2CFG 0xf162
+#define F0900_STROUT2_OPD 0xf1620080
+#define F0900_STROUT2_CONFIG 0xf162007e
+#define F0900_STROUT2_XOR 0xf1620001
/*CLKOUT2CFG*/
-#define R0900_CLKOUT2CFG 0xf163
-#define F0900_CLKOUT2_OPD 0xf1630080
-#define F0900_CLKOUT2_CONFIG 0xf163007e
-#define F0900_CLKOUT2_XOR 0xf1630001
+#define R0900_CLKOUT2CFG 0xf163
+#define F0900_CLKOUT2_OPD 0xf1630080
+#define F0900_CLKOUT2_CONFIG 0xf163007e
+#define F0900_CLKOUT2_XOR 0xf1630001
/*DATA72CFG*/
-#define R0900_DATA72CFG 0xf164
-#define F0900_DATA72_OPD 0xf1640080
-#define F0900_DATA72_CONFIG 0xf164007e
-#define F0900_DATA72_XOR 0xf1640001
+#define R0900_DATA72CFG 0xf164
+#define F0900_DATA72_OPD 0xf1640080
+#define F0900_DATA72_CONFIG 0xf164007e
+#define F0900_DATA72_XOR 0xf1640001
/*ERROR3CFG*/
-#define R0900_ERROR3CFG 0xf165
-#define F0900_ERROR3_OPD 0xf1650080
-#define F0900_ERROR3_CONFIG 0xf165007e
-#define F0900_ERROR3_XOR 0xf1650001
+#define R0900_ERROR3CFG 0xf165
+#define F0900_ERROR3_OPD 0xf1650080
+#define F0900_ERROR3_CONFIG 0xf165007e
+#define F0900_ERROR3_XOR 0xf1650001
/*DPN3CFG*/
-#define R0900_DPN3CFG 0xf166
-#define F0900_DPN3_OPD 0xf1660080
-#define F0900_DPN3_CONFIG 0xf166007e
-#define F0900_DPN3_XOR 0xf1660001
+#define R0900_DPN3CFG 0xf166
+#define F0900_DPN3_OPD 0xf1660080
+#define F0900_DPN3_CONFIG 0xf166007e
+#define F0900_DPN3_XOR 0xf1660001
/*STROUT3CFG*/
-#define R0900_STROUT3CFG 0xf167
-#define F0900_STROUT3_OPD 0xf1670080
-#define F0900_STROUT3_CONFIG 0xf167007e
-#define F0900_STROUT3_XOR 0xf1670001
+#define R0900_STROUT3CFG 0xf167
+#define F0900_STROUT3_OPD 0xf1670080
+#define F0900_STROUT3_CONFIG 0xf167007e
+#define F0900_STROUT3_XOR 0xf1670001
/*CLKOUT3CFG*/
-#define R0900_CLKOUT3CFG 0xf168
-#define F0900_CLKOUT3_OPD 0xf1680080
-#define F0900_CLKOUT3_CONFIG 0xf168007e
-#define F0900_CLKOUT3_XOR 0xf1680001
+#define R0900_CLKOUT3CFG 0xf168
+#define F0900_CLKOUT3_OPD 0xf1680080
+#define F0900_CLKOUT3_CONFIG 0xf168007e
+#define F0900_CLKOUT3_XOR 0xf1680001
/*DATA73CFG*/
-#define R0900_DATA73CFG 0xf169
-#define F0900_DATA73_OPD 0xf1690080
-#define F0900_DATA73_CONFIG 0xf169007e
-#define F0900_DATA73_XOR 0xf1690001
+#define R0900_DATA73CFG 0xf169
+#define F0900_DATA73_OPD 0xf1690080
+#define F0900_DATA73_CONFIG 0xf169007e
+#define F0900_DATA73_XOR 0xf1690001
+
+/*STRSTATUS1*/
+#define R0900_STRSTATUS1 0xf16a
+#define F0900_STRSTATUS_SEL2 0xf16a00f0
+#define F0900_STRSTATUS_SEL1 0xf16a000f
+
+/*STRSTATUS2*/
+#define R0900_STRSTATUS2 0xf16b
+#define F0900_STRSTATUS_SEL4 0xf16b00f0
+#define F0900_STRSTATUS_SEL3 0xf16b000f
+
+/*STRSTATUS3*/
+#define R0900_STRSTATUS3 0xf16c
+#define F0900_STRSTATUS_SEL6 0xf16c00f0
+#define F0900_STRSTATUS_SEL5 0xf16c000f
/*FSKTFC2*/
-#define R0900_FSKTFC2 0xf170
-#define F0900_FSKT_KMOD 0xf17000fc
-#define F0900_FSKT_CAR2 0xf1700003
+#define R0900_FSKTFC2 0xf170
+#define F0900_FSKT_KMOD 0xf17000fc
+#define F0900_FSKT_CAR2 0xf1700003
/*FSKTFC1*/
-#define R0900_FSKTFC1 0xf171
-#define F0900_FSKT_CAR1 0xf17100ff
+#define R0900_FSKTFC1 0xf171
+#define F0900_FSKT_CAR1 0xf17100ff
/*FSKTFC0*/
-#define R0900_FSKTFC0 0xf172
-#define F0900_FSKT_CAR0 0xf17200ff
+#define R0900_FSKTFC0 0xf172
+#define F0900_FSKT_CAR0 0xf17200ff
/*FSKTDELTAF1*/
-#define R0900_FSKTDELTAF1 0xf173
-#define F0900_FSKT_DELTAF1 0xf173000f
+#define R0900_FSKTDELTAF1 0xf173
+#define F0900_FSKT_DELTAF1 0xf173000f
/*FSKTDELTAF0*/
-#define R0900_FSKTDELTAF0 0xf174
-#define F0900_FSKT_DELTAF0 0xf17400ff
+#define R0900_FSKTDELTAF0 0xf174
+#define F0900_FSKT_DELTAF0 0xf17400ff
/*FSKTCTRL*/
-#define R0900_FSKTCTRL 0xf175
-#define F0900_FSKT_EN_SGN 0xf1750040
-#define F0900_FSKT_MOD_SGN 0xf1750020
-#define F0900_FSKT_MOD_EN 0xf175001c
-#define F0900_FSKT_DACMODE 0xf1750003
+#define R0900_FSKTCTRL 0xf175
+#define F0900_FSKT_EN_SGN 0xf1750040
+#define F0900_FSKT_MOD_SGN 0xf1750020
+#define F0900_FSKT_MOD_EN 0xf175001c
+#define F0900_FSKT_DACMODE 0xf1750003
/*FSKRFC2*/
-#define R0900_FSKRFC2 0xf176
-#define F0900_FSKR_DETSGN 0xf1760040
-#define F0900_FSKR_OUTSGN 0xf1760020
-#define F0900_FSKR_KAGC 0xf176001c
-#define F0900_FSKR_CAR2 0xf1760003
+#define R0900_FSKRFC2 0xf176
+#define F0900_FSKR_DETSGN 0xf1760040
+#define F0900_FSKR_OUTSGN 0xf1760020
+#define F0900_FSKR_KAGC 0xf176001c
+#define F0900_FSKR_CAR2 0xf1760003
/*FSKRFC1*/
-#define R0900_FSKRFC1 0xf177
-#define F0900_FSKR_CAR1 0xf17700ff
+#define R0900_FSKRFC1 0xf177
+#define F0900_FSKR_CAR1 0xf17700ff
/*FSKRFC0*/
-#define R0900_FSKRFC0 0xf178
-#define F0900_FSKR_CAR0 0xf17800ff
+#define R0900_FSKRFC0 0xf178
+#define F0900_FSKR_CAR0 0xf17800ff
/*FSKRK1*/
-#define R0900_FSKRK1 0xf179
-#define F0900_FSKR_K1_EXP 0xf17900e0
-#define F0900_FSKR_K1_MANT 0xf179001f
+#define R0900_FSKRK1 0xf179
+#define F0900_FSKR_K1_EXP 0xf17900e0
+#define F0900_FSKR_K1_MANT 0xf179001f
/*FSKRK2*/
-#define R0900_FSKRK2 0xf17a
-#define F0900_FSKR_K2_EXP 0xf17a00e0
-#define F0900_FSKR_K2_MANT 0xf17a001f
+#define R0900_FSKRK2 0xf17a
+#define F0900_FSKR_K2_EXP 0xf17a00e0
+#define F0900_FSKR_K2_MANT 0xf17a001f
/*FSKRAGCR*/
-#define R0900_FSKRAGCR 0xf17b
-#define F0900_FSKR_OUTCTL 0xf17b00c0
-#define F0900_FSKR_AGC_REF 0xf17b003f
+#define R0900_FSKRAGCR 0xf17b
+#define F0900_FSKR_OUTCTL 0xf17b00c0
+#define F0900_FSKR_AGC_REF 0xf17b003f
/*FSKRAGC*/
-#define R0900_FSKRAGC 0xf17c
-#define F0900_FSKR_AGC_ACCU 0xf17c00ff
+#define R0900_FSKRAGC 0xf17c
+#define F0900_FSKR_AGC_ACCU 0xf17c00ff
/*FSKRALPHA*/
-#define R0900_FSKRALPHA 0xf17d
-#define F0900_FSKR_ALPHA_EXP 0xf17d001c
-#define F0900_FSKR_ALPHA_M 0xf17d0003
+#define R0900_FSKRALPHA 0xf17d
+#define F0900_FSKR_ALPHA_EXP 0xf17d001c
+#define F0900_FSKR_ALPHA_M 0xf17d0003
/*FSKRPLTH1*/
-#define R0900_FSKRPLTH1 0xf17e
-#define F0900_FSKR_BETA 0xf17e00f0
-#define F0900_FSKR_PLL_TRESH1 0xf17e000f
+#define R0900_FSKRPLTH1 0xf17e
+#define F0900_FSKR_BETA 0xf17e00f0
+#define F0900_FSKR_PLL_TRESH1 0xf17e000f
/*FSKRPLTH0*/
-#define R0900_FSKRPLTH0 0xf17f
-#define F0900_FSKR_PLL_TRESH0 0xf17f00ff
+#define R0900_FSKRPLTH0 0xf17f
+#define F0900_FSKR_PLL_TRESH0 0xf17f00ff
/*FSKRDF1*/
-#define R0900_FSKRDF1 0xf180
-#define F0900_FSKR_OUT 0xf1800080
-#define F0900_FSKR_DELTAF1 0xf180001f
+#define R0900_FSKRDF1 0xf180
+#define F0900_FSKR_OUT 0xf1800080
+#define F0900_FSKR_DELTAF1 0xf180001f
/*FSKRDF0*/
-#define R0900_FSKRDF0 0xf181
-#define F0900_FSKR_DELTAF0 0xf18100ff
+#define R0900_FSKRDF0 0xf181
+#define F0900_FSKR_DELTAF0 0xf18100ff
/*FSKRSTEPP*/
-#define R0900_FSKRSTEPP 0xf182
-#define F0900_FSKR_STEP_PLUS 0xf18200ff
+#define R0900_FSKRSTEPP 0xf182
+#define F0900_FSKR_STEP_PLUS 0xf18200ff
/*FSKRSTEPM*/
-#define R0900_FSKRSTEPM 0xf183
-#define F0900_FSKR_STEP_MINUS 0xf18300ff
+#define R0900_FSKRSTEPM 0xf183
+#define F0900_FSKR_STEP_MINUS 0xf18300ff
/*FSKRDET1*/
-#define R0900_FSKRDET1 0xf184
-#define F0900_FSKR_DETECT 0xf1840080
-#define F0900_FSKR_CARDET_ACCU1 0xf184000f
+#define R0900_FSKRDET1 0xf184
+#define F0900_FSKR_DETECT 0xf1840080
+#define F0900_FSKR_CARDET_ACCU1 0xf184000f
/*FSKRDET0*/
-#define R0900_FSKRDET0 0xf185
-#define F0900_FSKR_CARDET_ACCU0 0xf18500ff
+#define R0900_FSKRDET0 0xf185
+#define F0900_FSKR_CARDET_ACCU0 0xf18500ff
/*FSKRDTH1*/
-#define R0900_FSKRDTH1 0xf186
-#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0
-#define F0900_FSKR_CARDET_THRESH1 0xf186000f
+#define R0900_FSKRDTH1 0xf186
+#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0
+#define F0900_FSKR_CARDET_THRESH1 0xf186000f
/*FSKRDTH0*/
-#define R0900_FSKRDTH0 0xf187
-#define F0900_FSKR_CARDET_THRESH0 0xf18700ff
+#define R0900_FSKRDTH0 0xf187
+#define F0900_FSKR_CARDET_THRESH0 0xf18700ff
/*FSKRLOSS*/
-#define R0900_FSKRLOSS 0xf188
-#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff
+#define R0900_FSKRLOSS 0xf188
+#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff
/*P2_DISTXCTL*/
-#define R0900_P2_DISTXCTL 0xf190
-#define F0900_P2_TIM_OFF 0xf1900080
-#define F0900_P2_DISEQC_RESET 0xf1900040
-#define F0900_P2_TIM_CMD 0xf1900030
-#define F0900_P2_DIS_PRECHARGE 0xf1900008
-#define F0900_P2_DISTX_MODE 0xf1900007
+#define R0900_P2_DISTXCTL 0xf190
+#define F0900_P2_TIM_OFF 0xf1900080
+#define F0900_P2_DISEQC_RESET 0xf1900040
+#define F0900_P2_TIM_CMD 0xf1900030
+#define F0900_P2_DIS_PRECHARGE 0xf1900008
+#define F0900_P2_DISTX_MODE 0xf1900007
/*P2_DISRXCTL*/
-#define R0900_P2_DISRXCTL 0xf191
-#define F0900_P2_RECEIVER_ON 0xf1910080
-#define F0900_P2_IGNO_SHORT22K 0xf1910040
-#define F0900_P2_ONECHIP_TRX 0xf1910020
-#define F0900_P2_EXT_ENVELOP 0xf1910010
-#define F0900_P2_PIN_SELECT 0xf191000c
-#define F0900_P2_IRQ_RXEND 0xf1910002
-#define F0900_P2_IRQ_4NBYTES 0xf1910001
+#define R0900_P2_DISRXCTL 0xf191
+#define F0900_P2_RECEIVER_ON 0xf1910080
+#define F0900_P2_IGNO_SHORT22K 0xf1910040
+#define F0900_P2_ONECHIP_TRX 0xf1910020
+#define F0900_P2_EXT_ENVELOP 0xf1910010
+#define F0900_P2_PIN_SELECT0 0xf191000c
+#define F0900_P2_IRQ_RXEND 0xf1910002
+#define F0900_P2_IRQ_4NBYTES 0xf1910001
/*P2_DISRX_ST0*/
-#define R0900_P2_DISRX_ST0 0xf194
-#define F0900_P2_RX_END 0xf1940080
-#define F0900_P2_RX_ACTIVE 0xf1940040
-#define F0900_P2_SHORT_22KHZ 0xf1940020
-#define F0900_P2_CONT_TONE 0xf1940010
-#define F0900_P2_FIFO_4BREADY 0xf1940008
-#define F0900_P2_FIFO_EMPTY 0xf1940004
-#define F0900_P2_ABORT_DISRX 0xf1940001
+#define R0900_P2_DISRX_ST0 0xf194
+#define F0900_P2_RX_END 0xf1940080
+#define F0900_P2_RX_ACTIVE 0xf1940040
+#define F0900_P2_SHORT_22KHZ 0xf1940020
+#define F0900_P2_CONT_TONE 0xf1940010
+#define F0900_P2_FIFO_4BREADY 0xf1940008
+#define F0900_P2_FIFO_EMPTY 0xf1940004
+#define F0900_P2_ABORT_DISRX 0xf1940001
/*P2_DISRX_ST1*/
-#define R0900_P2_DISRX_ST1 0xf195
-#define F0900_P2_RX_FAIL 0xf1950080
-#define F0900_P2_FIFO_PARITYFAIL 0xf1950040
-#define F0900_P2_RX_NONBYTE 0xf1950020
-#define F0900_P2_FIFO_OVERFLOW 0xf1950010
-#define F0900_P2_FIFO_BYTENBR 0xf195000f
+#define R0900_P2_DISRX_ST1 0xf195
+#define F0900_P2_RX_FAIL 0xf1950080
+#define F0900_P2_FIFO_PARITYFAIL 0xf1950040
+#define F0900_P2_RX_NONBYTE 0xf1950020
+#define F0900_P2_FIFO_OVERFLOW 0xf1950010
+#define F0900_P2_FIFO_BYTENBR 0xf195000f
/*P2_DISRXDATA*/
-#define R0900_P2_DISRXDATA 0xf196
-#define F0900_P2_DISRX_DATA 0xf19600ff
+#define R0900_P2_DISRXDATA 0xf196
+#define F0900_P2_DISRX_DATA 0xf19600ff
/*P2_DISTXDATA*/
-#define R0900_P2_DISTXDATA 0xf197
-#define F0900_P2_DISEQC_FIFO 0xf19700ff
+#define R0900_P2_DISTXDATA 0xf197
+#define F0900_P2_DISEQC_FIFO 0xf19700ff
/*P2_DISTXSTATUS*/
-#define R0900_P2_DISTXSTATUS 0xf198
-#define F0900_P2_TX_FAIL 0xf1980080
-#define F0900_P2_FIFO_FULL 0xf1980040
-#define F0900_P2_TX_IDLE 0xf1980020
-#define F0900_P2_GAP_BURST 0xf1980010
-#define F0900_P2_TXFIFO_BYTES 0xf198000f
+#define R0900_P2_DISTXSTATUS 0xf198
+#define F0900_P2_TX_FAIL 0xf1980080
+#define F0900_P2_FIFO_FULL 0xf1980040
+#define F0900_P2_TX_IDLE 0xf1980020
+#define F0900_P2_GAP_BURST 0xf1980010
+#define F0900_P2_TXFIFO_BYTES 0xf198000f
/*P2_F22TX*/
-#define R0900_P2_F22TX 0xf199
-#define F0900_P2_F22_REG 0xf19900ff
+#define R0900_P2_F22TX 0xf199
+#define F0900_P2_F22_REG 0xf19900ff
/*P2_F22RX*/
-#define R0900_P2_F22RX 0xf19a
-#define F0900_P2_F22RX_REG 0xf19a00ff
+#define R0900_P2_F22RX 0xf19a
+#define F0900_P2_F22RX_REG 0xf19a00ff
/*P2_ACRPRESC*/
-#define R0900_P2_ACRPRESC 0xf19c
-#define F0900_P2_ACR_CODFRDY 0xf19c0008
-#define F0900_P2_ACR_PRESC 0xf19c0007
+#define R0900_P2_ACRPRESC 0xf19c
+#define F0900_P2_ACR_PRESC 0xf19c0007
/*P2_ACRDIV*/
-#define R0900_P2_ACRDIV 0xf19d
-#define F0900_P2_ACR_DIV 0xf19d00ff
+#define R0900_P2_ACRDIV 0xf19d
+#define F0900_P2_ACR_DIV 0xf19d00ff
/*P1_DISTXCTL*/
-#define R0900_P1_DISTXCTL 0xf1a0
-#define F0900_P1_TIM_OFF 0xf1a00080
-#define F0900_P1_DISEQC_RESET 0xf1a00040
-#define F0900_P1_TIM_CMD 0xf1a00030
-#define F0900_P1_DIS_PRECHARGE 0xf1a00008
-#define F0900_P1_DISTX_MODE 0xf1a00007
+#define R0900_P1_DISTXCTL 0xf1a0
+#define DISTXCTL shiftx(R0900_P1_DISTXCTL, demod, 0x10)
+#define F0900_P1_TIM_OFF 0xf1a00080
+#define F0900_P1_DISEQC_RESET 0xf1a00040
+#define DISEQC_RESET shiftx(F0900_P1_DISEQC_RESET, demod, 0x100000)
+#define F0900_P1_TIM_CMD 0xf1a00030
+#define F0900_P1_DIS_PRECHARGE 0xf1a00008
+#define DIS_PRECHARGE shiftx(F0900_P1_DIS_PRECHARGE, demod, 0x100000)
+#define F0900_P1_DISTX_MODE 0xf1a00007
+#define DISTX_MODE shiftx(F0900_P1_DISTX_MODE, demod, 0x100000)
/*P1_DISRXCTL*/
-#define R0900_P1_DISRXCTL 0xf1a1
-#define F0900_P1_RECEIVER_ON 0xf1a10080
-#define F0900_P1_IGNO_SHORT22K 0xf1a10040
-#define F0900_P1_ONECHIP_TRX 0xf1a10020
-#define F0900_P1_EXT_ENVELOP 0xf1a10010
-#define F0900_P1_PIN_SELECT 0xf1a1000c
-#define F0900_P1_IRQ_RXEND 0xf1a10002
-#define F0900_P1_IRQ_4NBYTES 0xf1a10001
+#define R0900_P1_DISRXCTL 0xf1a1
+#define DISRXCTL shiftx(R0900_P1_DISRXCTL, demod, 0x10)
+#define F0900_P1_RECEIVER_ON 0xf1a10080
+#define F0900_P1_IGNO_SHORT22K 0xf1a10040
+#define F0900_P1_ONECHIP_TRX 0xf1a10020
+#define F0900_P1_EXT_ENVELOP 0xf1a10010
+#define F0900_P1_PIN_SELECT0 0xf1a1000c
+#define F0900_P1_IRQ_RXEND 0xf1a10002
+#define F0900_P1_IRQ_4NBYTES 0xf1a10001
/*P1_DISRX_ST0*/
-#define R0900_P1_DISRX_ST0 0xf1a4
-#define F0900_P1_RX_END 0xf1a40080
-#define F0900_P1_RX_ACTIVE 0xf1a40040
-#define F0900_P1_SHORT_22KHZ 0xf1a40020
-#define F0900_P1_CONT_TONE 0xf1a40010
-#define F0900_P1_FIFO_4BREADY 0xf1a40008
-#define F0900_P1_FIFO_EMPTY 0xf1a40004
-#define F0900_P1_ABORT_DISRX 0xf1a40001
+#define R0900_P1_DISRX_ST0 0xf1a4
+#define DISRX_ST0 shiftx(R0900_P1_DISRX_ST0, demod, 0x10)
+#define F0900_P1_RX_END 0xf1a40080
+#define RX_END shiftx(F0900_P1_RX_END, demod, 0x100000)
+#define F0900_P1_RX_ACTIVE 0xf1a40040
+#define F0900_P1_SHORT_22KHZ 0xf1a40020
+#define F0900_P1_CONT_TONE 0xf1a40010
+#define F0900_P1_FIFO_4BREADY 0xf1a40008
+#define F0900_P1_FIFO_EMPTY 0xf1a40004
+#define F0900_P1_ABORT_DISRX 0xf1a40001
/*P1_DISRX_ST1*/
-#define R0900_P1_DISRX_ST1 0xf1a5
-#define F0900_P1_RX_FAIL 0xf1a50080
-#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040
-#define F0900_P1_RX_NONBYTE 0xf1a50020
-#define F0900_P1_FIFO_OVERFLOW 0xf1a50010
-#define F0900_P1_FIFO_BYTENBR 0xf1a5000f
+#define R0900_P1_DISRX_ST1 0xf1a5
+#define DISRX_ST1 shiftx(R0900_P1_DISRX_ST1, demod, 0x10)
+#define F0900_P1_RX_FAIL 0xf1a50080
+#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040
+#define F0900_P1_RX_NONBYTE 0xf1a50020
+#define F0900_P1_FIFO_OVERFLOW 0xf1a50010
+#define F0900_P1_FIFO_BYTENBR 0xf1a5000f
+#define FIFO_BYTENBR shiftx(F0900_P1_FIFO_BYTENBR, demod, 0x100000)
/*P1_DISRXDATA*/
-#define R0900_P1_DISRXDATA 0xf1a6
-#define F0900_P1_DISRX_DATA 0xf1a600ff
+#define R0900_P1_DISRXDATA 0xf1a6
+#define DISRXDATA shiftx(R0900_P1_DISRXDATA, demod, 0x10)
+#define F0900_P1_DISRX_DATA 0xf1a600ff
/*P1_DISTXDATA*/
-#define R0900_P1_DISTXDATA 0xf1a7
-#define F0900_P1_DISEQC_FIFO 0xf1a700ff
+#define R0900_P1_DISTXDATA 0xf1a7
+#define DISTXDATA shiftx(R0900_P1_DISTXDATA, demod, 0x10)
+#define F0900_P1_DISEQC_FIFO 0xf1a700ff
/*P1_DISTXSTATUS*/
-#define R0900_P1_DISTXSTATUS 0xf1a8
-#define F0900_P1_TX_FAIL 0xf1a80080
-#define F0900_P1_FIFO_FULL 0xf1a80040
-#define F0900_P1_TX_IDLE 0xf1a80020
-#define F0900_P1_GAP_BURST 0xf1a80010
-#define F0900_P1_TXFIFO_BYTES 0xf1a8000f
+#define R0900_P1_DISTXSTATUS 0xf1a8
+#define F0900_P1_TX_FAIL 0xf1a80080
+#define F0900_P1_FIFO_FULL 0xf1a80040
+#define FIFO_FULL shiftx(F0900_P1_FIFO_FULL, demod, 0x100000)
+#define F0900_P1_TX_IDLE 0xf1a80020
+#define TX_IDLE shiftx(F0900_P1_TX_IDLE, demod, 0x100000)
+#define F0900_P1_GAP_BURST 0xf1a80010
+#define F0900_P1_TXFIFO_BYTES 0xf1a8000f
/*P1_F22TX*/
-#define R0900_P1_F22TX 0xf1a9
-#define F0900_P1_F22_REG 0xf1a900ff
+#define R0900_P1_F22TX 0xf1a9
+#define F22TX shiftx(R0900_P1_F22TX, demod, 0x10)
+#define F0900_P1_F22_REG 0xf1a900ff
/*P1_F22RX*/
-#define R0900_P1_F22RX 0xf1aa
-#define F0900_P1_F22RX_REG 0xf1aa00ff
+#define R0900_P1_F22RX 0xf1aa
+#define F22RX shiftx(R0900_P1_F22RX, demod, 0x10)
+#define F0900_P1_F22RX_REG 0xf1aa00ff
/*P1_ACRPRESC*/
-#define R0900_P1_ACRPRESC 0xf1ac
-#define F0900_P1_ACR_CODFRDY 0xf1ac0008
-#define F0900_P1_ACR_PRESC 0xf1ac0007
+#define R0900_P1_ACRPRESC 0xf1ac
+#define ACRPRESC shiftx(R0900_P1_ACRPRESC, demod, 0x10)
+#define F0900_P1_ACR_PRESC 0xf1ac0007
/*P1_ACRDIV*/
-#define R0900_P1_ACRDIV 0xf1ad
-#define F0900_P1_ACR_DIV 0xf1ad00ff
+#define R0900_P1_ACRDIV 0xf1ad
+#define ACRDIV shiftx(R0900_P1_ACRDIV, demod, 0x10)
+#define F0900_P1_ACR_DIV 0xf1ad00ff
/*NCOARSE*/
-#define R0900_NCOARSE 0xf1b3
-#define F0900_M_DIV 0xf1b300ff
+#define R0900_NCOARSE 0xf1b3
+#define F0900_M_DIV 0xf1b300ff
/*SYNTCTRL*/
-#define R0900_SYNTCTRL 0xf1b6
-#define F0900_STANDBY 0xf1b60080
-#define F0900_BYPASSPLLCORE 0xf1b60040
-#define F0900_SELX1RATIO 0xf1b60020
-#define F0900_I2C_TUD 0xf1b60010
-#define F0900_STOP_PLL 0xf1b60008
-#define F0900_BYPASSPLLFSK 0xf1b60004
-#define F0900_SELOSCI 0xf1b60002
-#define F0900_BYPASSPLLADC 0xf1b60001
+#define R0900_SYNTCTRL 0xf1b6
+#define F0900_STANDBY 0xf1b60080
+#define F0900_BYPASSPLLCORE 0xf1b60040
+#define F0900_SELX1RATIO 0xf1b60020
+#define F0900_STOP_PLL 0xf1b60008
+#define F0900_BYPASSPLLFSK 0xf1b60004
+#define F0900_SELOSCI 0xf1b60002
+#define F0900_BYPASSPLLADC 0xf1b60001
/*FILTCTRL*/
-#define R0900_FILTCTRL 0xf1b7
-#define F0900_INV_CLK135 0xf1b70080
-#define F0900_PERM_BYPDIS 0xf1b70040
-#define F0900_SEL_FSKCKDIV 0xf1b70004
-#define F0900_INV_CLKFSK 0xf1b70002
-#define F0900_BYPASS_APPLI 0xf1b70001
+#define R0900_FILTCTRL 0xf1b7
+#define F0900_INV_CLK135 0xf1b70080
+#define F0900_SEL_FSKCKDIV 0xf1b70004
+#define F0900_INV_CLKFSK 0xf1b70002
+#define F0900_BYPASS_APPLI 0xf1b70001
/*PLLSTAT*/
-#define R0900_PLLSTAT 0xf1b8
-#define F0900_ACM_SEL 0xf1b80080
-#define F0900_DTV_SEL 0xf1b80040
-#define F0900_PLLLOCK 0xf1b80001
+#define R0900_PLLSTAT 0xf1b8
+#define F0900_PLLLOCK 0xf1b80001
/*STOPCLK1*/
-#define R0900_STOPCLK1 0xf1c2
-#define F0900_STOP_CLKPKDT2 0xf1c20040
-#define F0900_STOP_CLKPKDT1 0xf1c20020
-#define F0900_STOP_CLKFEC 0xf1c20010
-#define F0900_STOP_CLKADCI2 0xf1c20008
-#define F0900_INV_CLKADCI2 0xf1c20004
-#define F0900_STOP_CLKADCI1 0xf1c20002
-#define F0900_INV_CLKADCI1 0xf1c20001
+#define R0900_STOPCLK1 0xf1c2
+#define F0900_STOP_CLKPKDT2 0xf1c20040
+#define F0900_STOP_CLKPKDT1 0xf1c20020
+#define F0900_STOP_CLKFEC 0xf1c20010
+#define F0900_STOP_CLKADCI2 0xf1c20008
+#define F0900_INV_CLKADCI2 0xf1c20004
+#define F0900_STOP_CLKADCI1 0xf1c20002
+#define F0900_INV_CLKADCI1 0xf1c20001
/*STOPCLK2*/
-#define R0900_STOPCLK2 0xf1c3
-#define F0900_STOP_CLKSAMP2 0xf1c30010
-#define F0900_STOP_CLKSAMP1 0xf1c30008
-#define F0900_STOP_CLKVIT2 0xf1c30004
-#define F0900_STOP_CLKVIT1 0xf1c30002
-#define F0900_STOP_CLKTS 0xf1c30001
+#define R0900_STOPCLK2 0xf1c3
+#define F0900_STOP_CLKSAMP2 0xf1c30010
+#define F0900_STOP_CLKSAMP1 0xf1c30008
+#define F0900_STOP_CLKVIT2 0xf1c30004
+#define F0900_STOP_CLKVIT1 0xf1c30002
+#define STOP_CLKVIT shiftx(F0900_STOP_CLKVIT1, demod, -2)
+#define F0900_STOP_CLKTS 0xf1c30001
/*TSTTNR0*/
-#define R0900_TSTTNR0 0xf1df
-#define F0900_SEL_FSK 0xf1df0080
-#define F0900_FSK_PON 0xf1df0004
-#define F0900_FSK_OPENLOOP 0xf1df0002
+#define R0900_TSTTNR0 0xf1df
+#define F0900_SEL_FSK 0xf1df0080
+#define F0900_FSK_PON 0xf1df0004
/*TSTTNR1*/
-#define R0900_TSTTNR1 0xf1e0
-#define F0900_BYPASS_ADC1 0xf1e00080
-#define F0900_INVADC1_CKOUT 0xf1e00040
-#define F0900_SELIQSRC1 0xf1e00030
-#define F0900_ADC1_PON 0xf1e00002
-#define F0900_ADC1_INMODE 0xf1e00001
+#define R0900_TSTTNR1 0xf1e0
+#define F0900_ADC1_PON 0xf1e00002
+#define F0900_ADC1_INMODE 0xf1e00001
/*TSTTNR2*/
-#define R0900_TSTTNR2 0xf1e1
-#define F0900_DISEQC1_PON 0xf1e10020
-#define F0900_DISEQC1_TEST 0xf1e1001f
+#define R0900_TSTTNR2 0xf1e1
+#define F0900_DISEQC1_PON 0xf1e10020
/*TSTTNR3*/
-#define R0900_TSTTNR3 0xf1e2
-#define F0900_BYPASS_ADC2 0xf1e20080
-#define F0900_INVADC2_CKOUT 0xf1e20040
-#define F0900_SELIQSRC2 0xf1e20030
-#define F0900_ADC2_PON 0xf1e20002
-#define F0900_ADC2_INMODE 0xf1e20001
+#define R0900_TSTTNR3 0xf1e2
+#define F0900_ADC2_PON 0xf1e20002
+#define F0900_ADC2_INMODE 0xf1e20001
/*TSTTNR4*/
-#define R0900_TSTTNR4 0xf1e3
-#define F0900_DISEQC2_PON 0xf1e30020
-#define F0900_DISEQC2_TEST 0xf1e3001f
+#define R0900_TSTTNR4 0xf1e3
+#define F0900_DISEQC2_PON 0xf1e30020
/*P2_IQCONST*/
-#define R0900_P2_IQCONST 0xf200
-#define F0900_P2_CONSTEL_SELECT 0xf2000060
-#define F0900_P2_IQSYMB_SEL 0xf200001f
+#define R0900_P2_IQCONST 0xf200
+#define F0900_P2_CONSTEL_SELECT 0xf2000060
+#define F0900_P2_IQSYMB_SEL 0xf200001f
/*P2_NOSCFG*/
-#define R0900_P2_NOSCFG 0xf201
-#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020
-#define F0900_P2_NOSPLH_BETA 0xf2010018
-#define F0900_P2_NOSDATA_BETA 0xf2010007
+#define R0900_P2_NOSCFG 0xf201
+#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020
+#define F0900_P2_NOSPLH_BETA 0xf2010018
+#define F0900_P2_NOSDATA_BETA 0xf2010007
/*P2_ISYMB*/
-#define R0900_P2_ISYMB 0xf202
-#define F0900_P2_I_SYMBOL 0xf20201ff
+#define R0900_P2_ISYMB 0xf202
+#define F0900_P2_I_SYMBOL 0xf20201ff
/*P2_QSYMB*/
-#define R0900_P2_QSYMB 0xf203
-#define F0900_P2_Q_SYMBOL 0xf20301ff
+#define R0900_P2_QSYMB 0xf203
+#define F0900_P2_Q_SYMBOL 0xf20301ff
/*P2_AGC1CFG*/
-#define R0900_P2_AGC1CFG 0xf204
-#define F0900_P2_DC_FROZEN 0xf2040080
-#define F0900_P2_DC_CORRECT 0xf2040040
-#define F0900_P2_AMM_FROZEN 0xf2040020
-#define F0900_P2_AMM_CORRECT 0xf2040010
-#define F0900_P2_QUAD_FROZEN 0xf2040008
-#define F0900_P2_QUAD_CORRECT 0xf2040004
-#define F0900_P2_DCCOMP_SLOW 0xf2040002
-#define F0900_P2_IQMISM_SLOW 0xf2040001
+#define R0900_P2_AGC1CFG 0xf204
+#define F0900_P2_DC_FROZEN 0xf2040080
+#define F0900_P2_DC_CORRECT 0xf2040040
+#define F0900_P2_AMM_FROZEN 0xf2040020
+#define F0900_P2_AMM_CORRECT 0xf2040010
+#define F0900_P2_QUAD_FROZEN 0xf2040008
+#define F0900_P2_QUAD_CORRECT 0xf2040004
/*P2_AGC1CN*/
-#define R0900_P2_AGC1CN 0xf206
-#define F0900_P2_AGC1_LOCKED 0xf2060080
-#define F0900_P2_AGC1_OVERFLOW 0xf2060040
-#define F0900_P2_AGC1_NOSLOWLK 0xf2060020
-#define F0900_P2_AGC1_MINPOWER 0xf2060010
-#define F0900_P2_AGCOUT_FAST 0xf2060008
-#define F0900_P2_AGCIQ_BETA 0xf2060007
+#define R0900_P2_AGC1CN 0xf206
+#define F0900_P2_AGC1_LOCKED 0xf2060080
+#define F0900_P2_AGC1_MINPOWER 0xf2060010
+#define F0900_P2_AGCOUT_FAST 0xf2060008
+#define F0900_P2_AGCIQ_BETA 0xf2060007
/*P2_AGC1REF*/
-#define R0900_P2_AGC1REF 0xf207
-#define F0900_P2_AGCIQ_REF 0xf20700ff
+#define R0900_P2_AGC1REF 0xf207
+#define F0900_P2_AGCIQ_REF 0xf20700ff
/*P2_IDCCOMP*/
-#define R0900_P2_IDCCOMP 0xf208
-#define F0900_P2_IAVERAGE_ADJ 0xf20801ff
+#define R0900_P2_IDCCOMP 0xf208
+#define F0900_P2_IAVERAGE_ADJ 0xf20801ff
/*P2_QDCCOMP*/
-#define R0900_P2_QDCCOMP 0xf209
-#define F0900_P2_QAVERAGE_ADJ 0xf20901ff
+#define R0900_P2_QDCCOMP 0xf209
+#define F0900_P2_QAVERAGE_ADJ 0xf20901ff
/*P2_POWERI*/
-#define R0900_P2_POWERI 0xf20a
-#define F0900_P2_POWER_I 0xf20a00ff
+#define R0900_P2_POWERI 0xf20a
+#define F0900_P2_POWER_I 0xf20a00ff
/*P2_POWERQ*/
-#define R0900_P2_POWERQ 0xf20b
-#define F0900_P2_POWER_Q 0xf20b00ff
+#define R0900_P2_POWERQ 0xf20b
+#define F0900_P2_POWER_Q 0xf20b00ff
/*P2_AGC1AMM*/
-#define R0900_P2_AGC1AMM 0xf20c
-#define F0900_P2_AMM_VALUE 0xf20c00ff
+#define R0900_P2_AGC1AMM 0xf20c
+#define F0900_P2_AMM_VALUE 0xf20c00ff
/*P2_AGC1QUAD*/
-#define R0900_P2_AGC1QUAD 0xf20d
-#define F0900_P2_QUAD_VALUE 0xf20d01ff
+#define R0900_P2_AGC1QUAD 0xf20d
+#define F0900_P2_QUAD_VALUE 0xf20d01ff
/*P2_AGCIQIN1*/
-#define R0900_P2_AGCIQIN1 0xf20e
-#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff
+#define R0900_P2_AGCIQIN1 0xf20e
+#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff
/*P2_AGCIQIN0*/
-#define R0900_P2_AGCIQIN0 0xf20f
-#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff
+#define R0900_P2_AGCIQIN0 0xf20f
+#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff
/*P2_DEMOD*/
-#define R0900_P2_DEMOD 0xf210
-#define F0900_P2_DEMOD_STOP 0xf2100040
-#define F0900_P2_SPECINV_CONTROL 0xf2100030
-#define F0900_P2_FORCE_ENASAMP 0xf2100008
-#define F0900_P2_MANUAL_ROLLOFF 0xf2100004
-#define F0900_P2_ROLLOFF_CONTROL 0xf2100003
+#define R0900_P2_DEMOD 0xf210
+#define F0900_P2_MANUALS2_ROLLOFF 0xf2100080
+#define F0900_P2_SPECINV_CONTROL 0xf2100030
+#define F0900_P2_FORCE_ENASAMP 0xf2100008
+#define F0900_P2_MANUALSX_ROLLOFF 0xf2100004
+#define F0900_P2_ROLLOFF_CONTROL 0xf2100003
/*P2_DMDMODCOD*/
-#define R0900_P2_DMDMODCOD 0xf211
-#define F0900_P2_MANUAL_MODCOD 0xf2110080
-#define F0900_P2_DEMOD_MODCOD 0xf211007c
-#define F0900_P2_DEMOD_TYPE 0xf2110003
+#define R0900_P2_DMDMODCOD 0xf211
+#define F0900_P2_MANUAL_MODCOD 0xf2110080
+#define F0900_P2_DEMOD_MODCOD 0xf211007c
+#define F0900_P2_DEMOD_TYPE 0xf2110003
/*P2_DSTATUS*/
-#define R0900_P2_DSTATUS 0xf212
-#define F0900_P2_CAR_LOCK 0xf2120080
-#define F0900_P2_TMGLOCK_QUALITY 0xf2120060
-#define F0900_P2_SDVBS1_ENABLE 0xf2120010
-#define F0900_P2_LOCK_DEFINITIF 0xf2120008
-#define F0900_P2_TIMING_IS_LOCKED 0xf2120004
-#define F0900_P2_COARSE_TMGLOCK 0xf2120002
-#define F0900_P2_COARSE_CARLOCK 0xf2120001
+#define R0900_P2_DSTATUS 0xf212
+#define F0900_P2_CAR_LOCK 0xf2120080
+#define F0900_P2_TMGLOCK_QUALITY 0xf2120060
+#define F0900_P2_LOCK_DEFINITIF 0xf2120008
+#define F0900_P2_OVADC_DETECT 0xf2120001
/*P2_DSTATUS2*/
-#define R0900_P2_DSTATUS2 0xf213
-#define F0900_P2_DEMOD_DELOCK 0xf2130080
-#define F0900_P2_DEMOD_TIMEOUT 0xf2130040
-#define F0900_P2_MODCODRQ_SYNCTAG 0xf2130020
-#define F0900_P2_POLYPH_SATEVENT 0xf2130010
-#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008
-#define F0900_P2_AGC2_OVERFLOW 0xf2130004
-#define F0900_P2_CFR_OVERFLOW 0xf2130002
-#define F0900_P2_GAMMA_OVERUNDER 0xf2130001
+#define R0900_P2_DSTATUS2 0xf213
+#define F0900_P2_DEMOD_DELOCK 0xf2130080
+#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008
+#define F0900_P2_AGC2_OVERFLOW 0xf2130004
+#define F0900_P2_CFR_OVERFLOW 0xf2130002
+#define F0900_P2_GAMMA_OVERUNDER 0xf2130001
/*P2_DMDCFGMD*/
-#define R0900_P2_DMDCFGMD 0xf214
-#define F0900_P2_DVBS2_ENABLE 0xf2140080
-#define F0900_P2_DVBS1_ENABLE 0xf2140040
-#define F0900_P2_CFR_AUTOSCAN 0xf2140020
-#define F0900_P2_SCAN_ENABLE 0xf2140010
-#define F0900_P2_TUN_AUTOSCAN 0xf2140008
-#define F0900_P2_NOFORCE_RELOCK 0xf2140004
-#define F0900_P2_TUN_RNG 0xf2140003
+#define R0900_P2_DMDCFGMD 0xf214
+#define F0900_P2_DVBS2_ENABLE 0xf2140080
+#define F0900_P2_DVBS1_ENABLE 0xf2140040
+#define F0900_P2_SCAN_ENABLE 0xf2140010
+#define F0900_P2_CFR_AUTOSCAN 0xf2140008
+#define F0900_P2_TUN_RNG 0xf2140003
/*P2_DMDCFG2*/
-#define R0900_P2_DMDCFG2 0xf215
-#define F0900_P2_AGC1_WAITLOCK 0xf2150080
-#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040
-#define F0900_P2_OVERFLOW_TIMEOUT 0xf2150020
-#define F0900_P2_SCANFAIL_TIMEOUT 0xf2150010
-#define F0900_P2_DMDTOUT_BACK 0xf2150008
-#define F0900_P2_CARLOCK_S1ENABLE 0xf2150004
-#define F0900_P2_COARSE_LK3MODE 0xf2150002
-#define F0900_P2_COARSE_LK2MODE 0xf2150001
+#define R0900_P2_DMDCFG2 0xf215
+#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040
+#define F0900_P2_INFINITE_RELOCK 0xf2150010
/*P2_DMDISTATE*/
-#define R0900_P2_DMDISTATE 0xf216
-#define F0900_P2_I2C_NORESETDMODE 0xf2160080
-#define F0900_P2_FORCE_ETAPED 0xf2160040
-#define F0900_P2_SDMDRST_DIRCLK 0xf2160020
-#define F0900_P2_I2C_DEMOD_MODE 0xf216001f
+#define R0900_P2_DMDISTATE 0xf216
+#define F0900_P2_I2C_DEMOD_MODE 0xf216001f
/*P2_DMDT0M*/
-#define R0900_P2_DMDT0M 0xf217
-#define F0900_P2_DMDT0_MIN 0xf21700ff
+#define R0900_P2_DMDT0M 0xf217
+#define F0900_P2_DMDT0_MIN 0xf21700ff
/*P2_DMDSTATE*/
-#define R0900_P2_DMDSTATE 0xf21b
-#define F0900_P2_DEMOD_LOCKED 0xf21b0080
-#define F0900_P2_HEADER_MODE 0xf21b0060
-#define F0900_P2_DEMOD_MODE 0xf21b001f
+#define R0900_P2_DMDSTATE 0xf21b
+#define F0900_P2_HEADER_MODE 0xf21b0060
/*P2_DMDFLYW*/
-#define R0900_P2_DMDFLYW 0xf21c
-#define F0900_P2_I2C_IRQVAL 0xf21c00f0
-#define F0900_P2_FLYWHEEL_CPT 0xf21c000f
+#define R0900_P2_DMDFLYW 0xf21c
+#define F0900_P2_I2C_IRQVAL 0xf21c00f0
+#define F0900_P2_FLYWHEEL_CPT 0xf21c000f
/*P2_DSTATUS3*/
-#define R0900_P2_DSTATUS3 0xf21d
-#define F0900_P2_CFR_ZIGZAG 0xf21d0080
-#define F0900_P2_DEMOD_CFGMODE 0xf21d0060
-#define F0900_P2_GAMMA_LOWBAUDRATE 0xf21d0010
-#define F0900_P2_RELOCK_MODE 0xf21d0008
-#define F0900_P2_DEMOD_FAIL 0xf21d0004
-#define F0900_P2_ETAPE1A_DVBXMEM 0xf21d0003
+#define R0900_P2_DSTATUS3 0xf21d
+#define F0900_P2_DEMOD_CFGMODE 0xf21d0060
/*P2_DMDCFG3*/
-#define R0900_P2_DMDCFG3 0xf21e
-#define F0900_P2_DVBS1_TMGWAIT 0xf21e0080
-#define F0900_P2_NO_BWCENTERING 0xf21e0040
-#define F0900_P2_INV_SEQSRCH 0xf21e0020
-#define F0900_P2_DIS_SFRUPLOW_TRK 0xf21e0010
-#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008
-#define F0900_P2_LOCKTIME_MODE 0xf21e0007
+#define R0900_P2_DMDCFG3 0xf21e
+#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008
/*P2_DMDCFG4*/
-#define R0900_P2_DMDCFG4 0xf21f
-#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008
-#define F0900_P2_DIS_CLKENABLE 0xf21f0004
-#define F0900_P2_DIS_HDRDIVLOCK 0xf21f0002
-#define F0900_P2_NO_TNRWBINIT 0xf21f0001
+#define R0900_P2_DMDCFG4 0xf21f
+#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008
/*P2_CORRELMANT*/
-#define R0900_P2_CORRELMANT 0xf220
-#define F0900_P2_CORREL_MANT 0xf22000ff
+#define R0900_P2_CORRELMANT 0xf220
+#define F0900_P2_CORREL_MANT 0xf22000ff
/*P2_CORRELABS*/
-#define R0900_P2_CORRELABS 0xf221
-#define F0900_P2_CORREL_ABS 0xf22100ff
+#define R0900_P2_CORRELABS 0xf221
+#define F0900_P2_CORREL_ABS 0xf22100ff
/*P2_CORRELEXP*/
-#define R0900_P2_CORRELEXP 0xf222
-#define F0900_P2_CORREL_ABSEXP 0xf22200f0
-#define F0900_P2_CORREL_EXP 0xf222000f
+#define R0900_P2_CORRELEXP 0xf222
+#define F0900_P2_CORREL_ABSEXP 0xf22200f0
+#define F0900_P2_CORREL_EXP 0xf222000f
/*P2_PLHMODCOD*/
-#define R0900_P2_PLHMODCOD 0xf224
-#define F0900_P2_SPECINV_DEMOD 0xf2240080
-#define F0900_P2_PLH_MODCOD 0xf224007c
-#define F0900_P2_PLH_TYPE 0xf2240003
-
-/*P2_AGCK32*/
-#define R0900_P2_AGCK32 0xf22b
-#define F0900_P2_R3ADJOFF_32APSK 0xf22b0080
-#define F0900_P2_R2ADJOFF_32APSK 0xf22b0040
-#define F0900_P2_R1ADJOFF_32APSK 0xf22b0020
-#define F0900_P2_RADJ_32APSK 0xf22b001f
+#define R0900_P2_PLHMODCOD 0xf224
+#define F0900_P2_SPECINV_DEMOD 0xf2240080
+#define F0900_P2_PLH_MODCOD 0xf224007c
+#define F0900_P2_PLH_TYPE 0xf2240003
+
+/*P2_DMDREG*/
+#define R0900_P2_DMDREG 0xf225
+#define F0900_P2_DECIM_PLFRAMES 0xf2250001
/*P2_AGC2O*/
-#define R0900_P2_AGC2O 0xf22c
-#define F0900_P2_AGC2REF_ADJUSTING 0xf22c0080
-#define F0900_P2_AGC2_COARSEFAST 0xf22c0040
-#define F0900_P2_AGC2_LKSQRT 0xf22c0020
-#define F0900_P2_AGC2_LKMODE 0xf22c0010
-#define F0900_P2_AGC2_LKEQUA 0xf22c0008
-#define F0900_P2_AGC2_COEF 0xf22c0007
+#define R0900_P2_AGC2O 0xf22c
+#define F0900_P2_AGC2_COEF 0xf22c0007
/*P2_AGC2REF*/
-#define R0900_P2_AGC2REF 0xf22d
-#define F0900_P2_AGC2_REF 0xf22d00ff
+#define R0900_P2_AGC2REF 0xf22d
+#define F0900_P2_AGC2_REF 0xf22d00ff
/*P2_AGC1ADJ*/
-#define R0900_P2_AGC1ADJ 0xf22e
-#define F0900_P2_AGC1ADJ_MANUAL 0xf22e0080
-#define F0900_P2_AGC1_ADJUSTED 0xf22e017f
+#define R0900_P2_AGC1ADJ 0xf22e
+#define F0900_P2_AGC1_ADJUSTED 0xf22e007f
/*P2_AGC2I1*/
-#define R0900_P2_AGC2I1 0xf236
-#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff
+#define R0900_P2_AGC2I1 0xf236
+#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff
/*P2_AGC2I0*/
-#define R0900_P2_AGC2I0 0xf237
-#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff
+#define R0900_P2_AGC2I0 0xf237
+#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff
/*P2_CARCFG*/
-#define R0900_P2_CARCFG 0xf238
-#define F0900_P2_CFRUPLOW_AUTO 0xf2380080
-#define F0900_P2_CFRUPLOW_TEST 0xf2380040
-#define F0900_P2_EN_CAR2CENTER 0xf2380020
-#define F0900_P2_CARHDR_NODIV8 0xf2380010
-#define F0900_P2_I2C_ROTA 0xf2380008
-#define F0900_P2_ROTAON 0xf2380004
-#define F0900_P2_PH_DET_ALGO 0xf2380003
+#define R0900_P2_CARCFG 0xf238
+#define F0900_P2_CFRUPLOW_AUTO 0xf2380080
+#define F0900_P2_CFRUPLOW_TEST 0xf2380040
+#define F0900_P2_ROTAON 0xf2380004
+#define F0900_P2_PH_DET_ALGO 0xf2380003
/*P2_ACLC*/
-#define R0900_P2_ACLC 0xf239
-#define F0900_P2_STOP_S2ALPHA 0xf23900c0
-#define F0900_P2_CAR_ALPHA_MANT 0xf2390030
-#define F0900_P2_CAR_ALPHA_EXP 0xf239000f
+#define R0900_P2_ACLC 0xf239
+#define F0900_P2_CAR_ALPHA_MANT 0xf2390030
+#define F0900_P2_CAR_ALPHA_EXP 0xf239000f
/*P2_BCLC*/
-#define R0900_P2_BCLC 0xf23a
-#define F0900_P2_STOP_S2BETA 0xf23a00c0
-#define F0900_P2_CAR_BETA_MANT 0xf23a0030
-#define F0900_P2_CAR_BETA_EXP 0xf23a000f
+#define R0900_P2_BCLC 0xf23a
+#define F0900_P2_CAR_BETA_MANT 0xf23a0030
+#define F0900_P2_CAR_BETA_EXP 0xf23a000f
/*P2_CARFREQ*/
-#define R0900_P2_CARFREQ 0xf23d
-#define F0900_P2_KC_COARSE_EXP 0xf23d00f0
-#define F0900_P2_BETA_FREQ 0xf23d000f
+#define R0900_P2_CARFREQ 0xf23d
+#define F0900_P2_KC_COARSE_EXP 0xf23d00f0
+#define F0900_P2_BETA_FREQ 0xf23d000f
/*P2_CARHDR*/
-#define R0900_P2_CARHDR 0xf23e
-#define F0900_P2_K_FREQ_HDR 0xf23e00ff
+#define R0900_P2_CARHDR 0xf23e
+#define F0900_P2_K_FREQ_HDR 0xf23e00ff
/*P2_LDT*/
-#define R0900_P2_LDT 0xf23f
-#define F0900_P2_CARLOCK_THRES 0xf23f01ff
+#define R0900_P2_LDT 0xf23f
+#define F0900_P2_CARLOCK_THRES 0xf23f01ff
/*P2_LDT2*/
-#define R0900_P2_LDT2 0xf240
-#define F0900_P2_CARLOCK_THRES2 0xf24001ff
+#define R0900_P2_LDT2 0xf240
+#define F0900_P2_CARLOCK_THRES2 0xf24001ff
/*P2_CFRICFG*/
-#define R0900_P2_CFRICFG 0xf241
-#define F0900_P2_CFRINIT_UNVALRNG 0xf2410080
-#define F0900_P2_CFRINIT_LUNVALCPT 0xf2410040
-#define F0900_P2_CFRINIT_ABORTDBL 0xf2410020
-#define F0900_P2_CFRINIT_ABORTPRED 0xf2410010
-#define F0900_P2_CFRINIT_UNVALSKIP 0xf2410008
-#define F0900_P2_CFRINIT_CSTINC 0xf2410004
-#define F0900_P2_NEG_CFRSTEP 0xf2410001
+#define R0900_P2_CFRICFG 0xf241
+#define F0900_P2_NEG_CFRSTEP 0xf2410001
/*P2_CFRUP1*/
-#define R0900_P2_CFRUP1 0xf242
-#define F0900_P2_CFR_UP1 0xf24201ff
+#define R0900_P2_CFRUP1 0xf242
+#define F0900_P2_CFR_UP1 0xf24201ff
/*P2_CFRUP0*/
-#define R0900_P2_CFRUP0 0xf243
-#define F0900_P2_CFR_UP0 0xf24300ff
+#define R0900_P2_CFRUP0 0xf243
+#define F0900_P2_CFR_UP0 0xf24300ff
/*P2_CFRLOW1*/
-#define R0900_P2_CFRLOW1 0xf246
-#define F0900_P2_CFR_LOW1 0xf24601ff
+#define R0900_P2_CFRLOW1 0xf246
+#define F0900_P2_CFR_LOW1 0xf24601ff
/*P2_CFRLOW0*/
-#define R0900_P2_CFRLOW0 0xf247
-#define F0900_P2_CFR_LOW0 0xf24700ff
+#define R0900_P2_CFRLOW0 0xf247
+#define F0900_P2_CFR_LOW0 0xf24700ff
/*P2_CFRINIT1*/
-#define R0900_P2_CFRINIT1 0xf248
-#define F0900_P2_CFR_INIT1 0xf24801ff
+#define R0900_P2_CFRINIT1 0xf248
+#define F0900_P2_CFR_INIT1 0xf24801ff
/*P2_CFRINIT0*/
-#define R0900_P2_CFRINIT0 0xf249
-#define F0900_P2_CFR_INIT0 0xf24900ff
+#define R0900_P2_CFRINIT0 0xf249
+#define F0900_P2_CFR_INIT0 0xf24900ff
/*P2_CFRINC1*/
-#define R0900_P2_CFRINC1 0xf24a
-#define F0900_P2_MANUAL_CFRINC 0xf24a0080
-#define F0900_P2_CFR_INC1 0xf24a017f
+#define R0900_P2_CFRINC1 0xf24a
+#define F0900_P2_MANUAL_CFRINC 0xf24a0080
+#define F0900_P2_CFR_INC1 0xf24a003f
/*P2_CFRINC0*/
-#define R0900_P2_CFRINC0 0xf24b
-#define F0900_P2_CFR_INC0 0xf24b00f0
+#define R0900_P2_CFRINC0 0xf24b
+#define F0900_P2_CFR_INC0 0xf24b00f8
/*P2_CFR2*/
-#define R0900_P2_CFR2 0xf24c
-#define F0900_P2_CAR_FREQ2 0xf24c01ff
+#define R0900_P2_CFR2 0xf24c
+#define F0900_P2_CAR_FREQ2 0xf24c01ff
/*P2_CFR1*/
-#define R0900_P2_CFR1 0xf24d
-#define F0900_P2_CAR_FREQ1 0xf24d00ff
+#define R0900_P2_CFR1 0xf24d
+#define F0900_P2_CAR_FREQ1 0xf24d00ff
/*P2_CFR0*/
-#define R0900_P2_CFR0 0xf24e
-#define F0900_P2_CAR_FREQ0 0xf24e00ff
+#define R0900_P2_CFR0 0xf24e
+#define F0900_P2_CAR_FREQ0 0xf24e00ff
/*P2_LDI*/
-#define R0900_P2_LDI 0xf24f
-#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff
+#define R0900_P2_LDI 0xf24f
+#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff
/*P2_TMGCFG*/
-#define R0900_P2_TMGCFG 0xf250
-#define F0900_P2_TMGLOCK_BETA 0xf25000c0
-#define F0900_P2_NOTMG_GROUPDELAY 0xf2500020
-#define F0900_P2_DO_TIMING_CORR 0xf2500010
-#define F0900_P2_MANUAL_SCAN 0xf250000c
-#define F0900_P2_TMG_MINFREQ 0xf2500003
+#define R0900_P2_TMGCFG 0xf250
+#define F0900_P2_TMGLOCK_BETA 0xf25000c0
+#define F0900_P2_DO_TIMING_CORR 0xf2500010
+#define F0900_P2_TMG_MINFREQ 0xf2500003
/*P2_RTC*/
-#define R0900_P2_RTC 0xf251
-#define F0900_P2_TMGALPHA_EXP 0xf25100f0
-#define F0900_P2_TMGBETA_EXP 0xf251000f
+#define R0900_P2_RTC 0xf251
+#define F0900_P2_TMGALPHA_EXP 0xf25100f0
+#define F0900_P2_TMGBETA_EXP 0xf251000f
/*P2_RTCS2*/
-#define R0900_P2_RTCS2 0xf252
-#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0
-#define F0900_P2_TMGBETAS2_EXP 0xf252000f
+#define R0900_P2_RTCS2 0xf252
+#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0
+#define F0900_P2_TMGBETAS2_EXP 0xf252000f
/*P2_TMGTHRISE*/
-#define R0900_P2_TMGTHRISE 0xf253
-#define F0900_P2_TMGLOCK_THRISE 0xf25300ff
+#define R0900_P2_TMGTHRISE 0xf253
+#define F0900_P2_TMGLOCK_THRISE 0xf25300ff
/*P2_TMGTHFALL*/
-#define R0900_P2_TMGTHFALL 0xf254
-#define F0900_P2_TMGLOCK_THFALL 0xf25400ff
+#define R0900_P2_TMGTHFALL 0xf254
+#define F0900_P2_TMGLOCK_THFALL 0xf25400ff
/*P2_SFRUPRATIO*/
-#define R0900_P2_SFRUPRATIO 0xf255
-#define F0900_P2_SFR_UPRATIO 0xf25500ff
+#define R0900_P2_SFRUPRATIO 0xf255
+#define F0900_P2_SFR_UPRATIO 0xf25500ff
/*P2_SFRLOWRATIO*/
-#define R0900_P2_SFRLOWRATIO 0xf256
-#define F0900_P2_SFR_LOWRATIO 0xf25600ff
+#define R0900_P2_SFRLOWRATIO 0xf256
+#define F0900_P2_SFR_LOWRATIO 0xf25600ff
/*P2_KREFTMG*/
-#define R0900_P2_KREFTMG 0xf258
-#define F0900_P2_KREF_TMG 0xf25800ff
+#define R0900_P2_KREFTMG 0xf258
+#define F0900_P2_KREF_TMG 0xf25800ff
/*P2_SFRSTEP*/
-#define R0900_P2_SFRSTEP 0xf259
-#define F0900_P2_SFR_SCANSTEP 0xf25900f0
-#define F0900_P2_SFR_CENTERSTEP 0xf259000f
+#define R0900_P2_SFRSTEP 0xf259
+#define F0900_P2_SFR_SCANSTEP 0xf25900f0
+#define F0900_P2_SFR_CENTERSTEP 0xf259000f
/*P2_TMGCFG2*/
-#define R0900_P2_TMGCFG2 0xf25a
-#define F0900_P2_DIS_AUTOSAMP 0xf25a0008
-#define F0900_P2_SCANINIT_QUART 0xf25a0004
-#define F0900_P2_NOTMG_DVBS1DERAT 0xf25a0002
-#define F0900_P2_SFRRATIO_FINE 0xf25a0001
+#define R0900_P2_TMGCFG2 0xf25a
+#define F0900_P2_SFRRATIO_FINE 0xf25a0001
+
+/*P2_KREFTMG2*/
+#define R0900_P2_KREFTMG2 0xf25b
+#define F0900_P2_KREF_TMG2 0xf25b00ff
/*P2_SFRINIT1*/
-#define R0900_P2_SFRINIT1 0xf25e
-#define F0900_P2_SFR_INIT1 0xf25e00ff
+#define R0900_P2_SFRINIT1 0xf25e
+#define F0900_P2_SFR_INIT1 0xf25e007f
/*P2_SFRINIT0*/
-#define R0900_P2_SFRINIT0 0xf25f
-#define F0900_P2_SFR_INIT0 0xf25f00ff
+#define R0900_P2_SFRINIT0 0xf25f
+#define F0900_P2_SFR_INIT0 0xf25f00ff
/*P2_SFRUP1*/
-#define R0900_P2_SFRUP1 0xf260
-#define F0900_P2_AUTO_GUP 0xf2600080
-#define F0900_P2_SYMB_FREQ_UP1 0xf260007f
+#define R0900_P2_SFRUP1 0xf260
+#define F0900_P2_AUTO_GUP 0xf2600080
+#define F0900_P2_SYMB_FREQ_UP1 0xf260007f
/*P2_SFRUP0*/
-#define R0900_P2_SFRUP0 0xf261
-#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff
+#define R0900_P2_SFRUP0 0xf261
+#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff
/*P2_SFRLOW1*/
-#define R0900_P2_SFRLOW1 0xf262
-#define F0900_P2_AUTO_GLOW 0xf2620080
-#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f
+#define R0900_P2_SFRLOW1 0xf262
+#define F0900_P2_AUTO_GLOW 0xf2620080
+#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f
/*P2_SFRLOW0*/
-#define R0900_P2_SFRLOW0 0xf263
-#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff
+#define R0900_P2_SFRLOW0 0xf263
+#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff
/*P2_SFR3*/
-#define R0900_P2_SFR3 0xf264
-#define F0900_P2_SYMB_FREQ3 0xf26400ff
+#define R0900_P2_SFR3 0xf264
+#define F0900_P2_SYMB_FREQ3 0xf26400ff
/*P2_SFR2*/
-#define R0900_P2_SFR2 0xf265
-#define F0900_P2_SYMB_FREQ2 0xf26500ff
+#define R0900_P2_SFR2 0xf265
+#define F0900_P2_SYMB_FREQ2 0xf26500ff
/*P2_SFR1*/
-#define R0900_P2_SFR1 0xf266
-#define F0900_P2_SYMB_FREQ1 0xf26600ff
+#define R0900_P2_SFR1 0xf266
+#define F0900_P2_SYMB_FREQ1 0xf26600ff
/*P2_SFR0*/
-#define R0900_P2_SFR0 0xf267
-#define F0900_P2_SYMB_FREQ0 0xf26700ff
+#define R0900_P2_SFR0 0xf267
+#define F0900_P2_SYMB_FREQ0 0xf26700ff
/*P2_TMGREG2*/
-#define R0900_P2_TMGREG2 0xf268
-#define F0900_P2_TMGREG2 0xf26800ff
+#define R0900_P2_TMGREG2 0xf268
+#define F0900_P2_TMGREG2 0xf26800ff
/*P2_TMGREG1*/
-#define R0900_P2_TMGREG1 0xf269
-#define F0900_P2_TMGREG1 0xf26900ff
+#define R0900_P2_TMGREG1 0xf269
+#define F0900_P2_TMGREG1 0xf26900ff
/*P2_TMGREG0*/
-#define R0900_P2_TMGREG0 0xf26a
-#define F0900_P2_TMGREG0 0xf26a00ff
+#define R0900_P2_TMGREG0 0xf26a
+#define F0900_P2_TMGREG0 0xf26a00ff
/*P2_TMGLOCK1*/
-#define R0900_P2_TMGLOCK1 0xf26b
-#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff
+#define R0900_P2_TMGLOCK1 0xf26b
+#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff
/*P2_TMGLOCK0*/
-#define R0900_P2_TMGLOCK0 0xf26c
-#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff
+#define R0900_P2_TMGLOCK0 0xf26c
+#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff
/*P2_TMGOBS*/
-#define R0900_P2_TMGOBS 0xf26d
-#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0
-#define F0900_P2_SCAN_SIGN 0xf26d0030
-#define F0900_P2_TMG_SCANNING 0xf26d0008
-#define F0900_P2_CHCENTERING_MODE 0xf26d0004
-#define F0900_P2_TMG_SCANFAIL 0xf26d0002
+#define R0900_P2_TMGOBS 0xf26d
+#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0
/*P2_EQUALCFG*/
-#define R0900_P2_EQUALCFG 0xf26f
-#define F0900_P2_NOTMG_NEGALWAIT 0xf26f0080
-#define F0900_P2_EQUAL_ON 0xf26f0040
-#define F0900_P2_SEL_EQUALCOR 0xf26f0038
-#define F0900_P2_MU_EQUALDFE 0xf26f0007
+#define R0900_P2_EQUALCFG 0xf26f
+#define F0900_P2_EQUAL_ON 0xf26f0040
+#define F0900_P2_MU_EQUALDFE 0xf26f0007
/*P2_EQUAI1*/
-#define R0900_P2_EQUAI1 0xf270
-#define F0900_P2_EQUA_ACCI1 0xf27001ff
+#define R0900_P2_EQUAI1 0xf270
+#define F0900_P2_EQUA_ACCI1 0xf27001ff
/*P2_EQUAQ1*/
-#define R0900_P2_EQUAQ1 0xf271
-#define F0900_P2_EQUA_ACCQ1 0xf27101ff
+#define R0900_P2_EQUAQ1 0xf271
+#define F0900_P2_EQUA_ACCQ1 0xf27101ff
/*P2_EQUAI2*/
-#define R0900_P2_EQUAI2 0xf272
-#define F0900_P2_EQUA_ACCI2 0xf27201ff
+#define R0900_P2_EQUAI2 0xf272
+#define F0900_P2_EQUA_ACCI2 0xf27201ff
/*P2_EQUAQ2*/
-#define R0900_P2_EQUAQ2 0xf273
-#define F0900_P2_EQUA_ACCQ2 0xf27301ff
+#define R0900_P2_EQUAQ2 0xf273
+#define F0900_P2_EQUA_ACCQ2 0xf27301ff
/*P2_EQUAI3*/
-#define R0900_P2_EQUAI3 0xf274
-#define F0900_P2_EQUA_ACCI3 0xf27401ff
+#define R0900_P2_EQUAI3 0xf274
+#define F0900_P2_EQUA_ACCI3 0xf27401ff
/*P2_EQUAQ3*/
-#define R0900_P2_EQUAQ3 0xf275
-#define F0900_P2_EQUA_ACCQ3 0xf27501ff
+#define R0900_P2_EQUAQ3 0xf275
+#define F0900_P2_EQUA_ACCQ3 0xf27501ff
/*P2_EQUAI4*/
-#define R0900_P2_EQUAI4 0xf276
-#define F0900_P2_EQUA_ACCI4 0xf27601ff
+#define R0900_P2_EQUAI4 0xf276
+#define F0900_P2_EQUA_ACCI4 0xf27601ff
/*P2_EQUAQ4*/
-#define R0900_P2_EQUAQ4 0xf277
-#define F0900_P2_EQUA_ACCQ4 0xf27701ff
+#define R0900_P2_EQUAQ4 0xf277
+#define F0900_P2_EQUA_ACCQ4 0xf27701ff
/*P2_EQUAI5*/
-#define R0900_P2_EQUAI5 0xf278
-#define F0900_P2_EQUA_ACCI5 0xf27801ff
+#define R0900_P2_EQUAI5 0xf278
+#define F0900_P2_EQUA_ACCI5 0xf27801ff
/*P2_EQUAQ5*/
-#define R0900_P2_EQUAQ5 0xf279
-#define F0900_P2_EQUA_ACCQ5 0xf27901ff
+#define R0900_P2_EQUAQ5 0xf279
+#define F0900_P2_EQUA_ACCQ5 0xf27901ff
/*P2_EQUAI6*/
-#define R0900_P2_EQUAI6 0xf27a
-#define F0900_P2_EQUA_ACCI6 0xf27a01ff
+#define R0900_P2_EQUAI6 0xf27a
+#define F0900_P2_EQUA_ACCI6 0xf27a01ff
/*P2_EQUAQ6*/
-#define R0900_P2_EQUAQ6 0xf27b
-#define F0900_P2_EQUA_ACCQ6 0xf27b01ff
+#define R0900_P2_EQUAQ6 0xf27b
+#define F0900_P2_EQUA_ACCQ6 0xf27b01ff
/*P2_EQUAI7*/
-#define R0900_P2_EQUAI7 0xf27c
-#define F0900_P2_EQUA_ACCI7 0xf27c01ff
+#define R0900_P2_EQUAI7 0xf27c
+#define F0900_P2_EQUA_ACCI7 0xf27c01ff
/*P2_EQUAQ7*/
-#define R0900_P2_EQUAQ7 0xf27d
-#define F0900_P2_EQUA_ACCQ7 0xf27d01ff
+#define R0900_P2_EQUAQ7 0xf27d
+#define F0900_P2_EQUA_ACCQ7 0xf27d01ff
/*P2_EQUAI8*/
-#define R0900_P2_EQUAI8 0xf27e
-#define F0900_P2_EQUA_ACCI8 0xf27e01ff
+#define R0900_P2_EQUAI8 0xf27e
+#define F0900_P2_EQUA_ACCI8 0xf27e01ff
/*P2_EQUAQ8*/
-#define R0900_P2_EQUAQ8 0xf27f
-#define F0900_P2_EQUA_ACCQ8 0xf27f01ff
+#define R0900_P2_EQUAQ8 0xf27f
+#define F0900_P2_EQUA_ACCQ8 0xf27f01ff
/*P2_NNOSDATAT1*/
-#define R0900_P2_NNOSDATAT1 0xf280
-#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff
+#define R0900_P2_NNOSDATAT1 0xf280
+#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff
/*P2_NNOSDATAT0*/
-#define R0900_P2_NNOSDATAT0 0xf281
-#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff
+#define R0900_P2_NNOSDATAT0 0xf281
+#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff
/*P2_NNOSDATA1*/
-#define R0900_P2_NNOSDATA1 0xf282
-#define F0900_P2_NOSDATA_NORMED1 0xf28200ff
+#define R0900_P2_NNOSDATA1 0xf282
+#define F0900_P2_NOSDATA_NORMED1 0xf28200ff
/*P2_NNOSDATA0*/
-#define R0900_P2_NNOSDATA0 0xf283
-#define F0900_P2_NOSDATA_NORMED0 0xf28300ff
+#define R0900_P2_NNOSDATA0 0xf283
+#define F0900_P2_NOSDATA_NORMED0 0xf28300ff
/*P2_NNOSPLHT1*/
-#define R0900_P2_NNOSPLHT1 0xf284
-#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff
+#define R0900_P2_NNOSPLHT1 0xf284
+#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff
/*P2_NNOSPLHT0*/
-#define R0900_P2_NNOSPLHT0 0xf285
-#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff
+#define R0900_P2_NNOSPLHT0 0xf285
+#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff
/*P2_NNOSPLH1*/
-#define R0900_P2_NNOSPLH1 0xf286
-#define F0900_P2_NOSPLH_NORMED1 0xf28600ff
+#define R0900_P2_NNOSPLH1 0xf286
+#define F0900_P2_NOSPLH_NORMED1 0xf28600ff
/*P2_NNOSPLH0*/
-#define R0900_P2_NNOSPLH0 0xf287
-#define F0900_P2_NOSPLH_NORMED0 0xf28700ff
+#define R0900_P2_NNOSPLH0 0xf287
+#define F0900_P2_NOSPLH_NORMED0 0xf28700ff
/*P2_NOSDATAT1*/
-#define R0900_P2_NOSDATAT1 0xf288
-#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff
+#define R0900_P2_NOSDATAT1 0xf288
+#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff
/*P2_NOSDATAT0*/
-#define R0900_P2_NOSDATAT0 0xf289
-#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff
+#define R0900_P2_NOSDATAT0 0xf289
+#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff
/*P2_NOSDATA1*/
-#define R0900_P2_NOSDATA1 0xf28a
-#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff
+#define R0900_P2_NOSDATA1 0xf28a
+#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff
/*P2_NOSDATA0*/
-#define R0900_P2_NOSDATA0 0xf28b
-#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff
+#define R0900_P2_NOSDATA0 0xf28b
+#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff
/*P2_NOSPLHT1*/
-#define R0900_P2_NOSPLHT1 0xf28c
-#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff
+#define R0900_P2_NOSPLHT1 0xf28c
+#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff
/*P2_NOSPLHT0*/
-#define R0900_P2_NOSPLHT0 0xf28d
-#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff
+#define R0900_P2_NOSPLHT0 0xf28d
+#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff
/*P2_NOSPLH1*/
-#define R0900_P2_NOSPLH1 0xf28e
-#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff
+#define R0900_P2_NOSPLH1 0xf28e
+#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff
/*P2_NOSPLH0*/
-#define R0900_P2_NOSPLH0 0xf28f
-#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff
+#define R0900_P2_NOSPLH0 0xf28f
+#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff
/*P2_CAR2CFG*/
-#define R0900_P2_CAR2CFG 0xf290
-#define F0900_P2_DESCRAMB_OFF 0xf2900080
-#define F0900_P2_PN4_SELECT 0xf2900040
-#define F0900_P2_CFR2_STOPDVBS1 0xf2900020
-#define F0900_P2_STOP_CFR2UPDATE 0xf2900010
-#define F0900_P2_STOP_NCO2UPDATE 0xf2900008
-#define F0900_P2_ROTA2ON 0xf2900004
-#define F0900_P2_PH_DET_ALGO2 0xf2900003
-
-/*P2_ACLC2*/
-#define R0900_P2_ACLC2 0xf291
-#define F0900_P2_CAR2_PUNCT_ADERAT 0xf2910040
-#define F0900_P2_CAR2_ALPHA_MANT 0xf2910030
-#define F0900_P2_CAR2_ALPHA_EXP 0xf291000f
-
-/*P2_BCLC2*/
-#define R0900_P2_BCLC2 0xf292
-#define F0900_P2_DVBS2_NIP 0xf2920080
-#define F0900_P2_CAR2_PUNCT_BDERAT 0xf2920040
-#define F0900_P2_CAR2_BETA_MANT 0xf2920030
-#define F0900_P2_CAR2_BETA_EXP 0xf292000f
+#define R0900_P2_CAR2CFG 0xf290
+#define F0900_P2_CARRIER3_DISABLE 0xf2900040
+#define F0900_P2_ROTA2ON 0xf2900004
+#define F0900_P2_PH_DET_ALGO2 0xf2900003
+
+/*P2_CFR2CFR1*/
+#define R0900_P2_CFR2CFR1 0xf291
+#define F0900_P2_CFR2TOCFR1_DVBS1 0xf29100c0
+#define F0900_P2_EN_S2CAR2CENTER 0xf2910020
+#define F0900_P2_DIS_BCHERRCFR2 0xf2910010
+#define F0900_P2_CFR2TOCFR1_BETA 0xf2910007
/*P2_CFR22*/
-#define R0900_P2_CFR22 0xf293
-#define F0900_P2_CAR2_FREQ2 0xf29301ff
+#define R0900_P2_CFR22 0xf293
+#define F0900_P2_CAR2_FREQ2 0xf29301ff
/*P2_CFR21*/
-#define R0900_P2_CFR21 0xf294
-#define F0900_P2_CAR2_FREQ1 0xf29400ff
+#define R0900_P2_CFR21 0xf294
+#define F0900_P2_CAR2_FREQ1 0xf29400ff
/*P2_CFR20*/
-#define R0900_P2_CFR20 0xf295
-#define F0900_P2_CAR2_FREQ0 0xf29500ff
+#define R0900_P2_CFR20 0xf295
+#define F0900_P2_CAR2_FREQ0 0xf29500ff
/*P2_ACLC2S2Q*/
-#define R0900_P2_ACLC2S2Q 0xf297
-#define F0900_P2_ENAB_SPSKSYMB 0xf2970080
-#define F0900_P2_CAR2S2_QADERAT 0xf2970040
-#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030
-#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f
+#define R0900_P2_ACLC2S2Q 0xf297
+#define F0900_P2_ENAB_SPSKSYMB 0xf2970080
+#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030
+#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f
/*P2_ACLC2S28*/
-#define R0900_P2_ACLC2S28 0xf298
-#define F0900_P2_OLDI3Q_MODE 0xf2980080
-#define F0900_P2_CAR2S2_8ADERAT 0xf2980040
-#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030
-#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f
+#define R0900_P2_ACLC2S28 0xf298
+#define F0900_P2_OLDI3Q_MODE 0xf2980080
+#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030
+#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f
/*P2_ACLC2S216A*/
-#define R0900_P2_ACLC2S216A 0xf299
-#define F0900_P2_CAR2S2_16ADERAT 0xf2990040
-#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030
-#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f
+#define R0900_P2_ACLC2S216A 0xf299
+#define F0900_P2_DIS_C3STOPA2 0xf2990080
+#define F0900_P2_CAR2S2_16ADERAT 0xf2990040
+#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030
+#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f
/*P2_ACLC2S232A*/
-#define R0900_P2_ACLC2S232A 0xf29a
-#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040
-#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030
-#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f
+#define R0900_P2_ACLC2S232A 0xf29a
+#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040
+#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030
+#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f
/*P2_BCLC2S2Q*/
-#define R0900_P2_BCLC2S2Q 0xf29c
-#define F0900_P2_DVBS2S2Q_NIP 0xf29c0080
-#define F0900_P2_CAR2S2_QBDERAT 0xf29c0040
-#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030
-#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f
+#define R0900_P2_BCLC2S2Q 0xf29c
+#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030
+#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f
/*P2_BCLC2S28*/
-#define R0900_P2_BCLC2S28 0xf29d
-#define F0900_P2_DVBS2S28_NIP 0xf29d0080
-#define F0900_P2_CAR2S2_8BDERAT 0xf29d0040
-#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030
-#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f
+#define R0900_P2_BCLC2S28 0xf29d
+#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030
+#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f
/*P2_BCLC2S216A*/
-#define R0900_P2_BCLC2S216A 0xf29e
-#define F0900_P2_DVBS2S216A_NIP 0xf29e0080
-#define F0900_P2_CAR2S2_16BDERAT 0xf29e0040
-#define F0900_P2_CAR2S2_16A_BETA_M 0xf29e0030
-#define F0900_P2_CAR2S2_16A_BETA_E 0xf29e000f
+#define R0900_P2_BCLC2S216A 0xf29e
/*P2_BCLC2S232A*/
-#define R0900_P2_BCLC2S232A 0xf29f
-#define F0900_P2_DVBS2S232A_NIP 0xf29f0080
-#define F0900_P2_CAR2S2_32BDERAT 0xf29f0040
-#define F0900_P2_CAR2S2_32A_BETA_M 0xf29f0030
-#define F0900_P2_CAR2S2_32A_BETA_E 0xf29f000f
+#define R0900_P2_BCLC2S232A 0xf29f
/*P2_PLROOT2*/
-#define R0900_P2_PLROOT2 0xf2ac
-#define F0900_P2_SHORTFR_DISABLE 0xf2ac0080
-#define F0900_P2_LONGFR_DISABLE 0xf2ac0040
-#define F0900_P2_DUMMYPL_DISABLE 0xf2ac0020
-#define F0900_P2_SHORTFR_AVOID 0xf2ac0010
-#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c
-#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003
+#define R0900_P2_PLROOT2 0xf2ac
+#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c
+#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003
/*P2_PLROOT1*/
-#define R0900_P2_PLROOT1 0xf2ad
-#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff
+#define R0900_P2_PLROOT1 0xf2ad
+#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff
/*P2_PLROOT0*/
-#define R0900_P2_PLROOT0 0xf2ae
-#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff
+#define R0900_P2_PLROOT0 0xf2ae
+#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff
/*P2_MODCODLST0*/
-#define R0900_P2_MODCODLST0 0xf2b0
-#define F0900_P2_EN_TOKEN31 0xf2b00080
-#define F0900_P2_SYNCTAG_SELECT 0xf2b00040
-#define F0900_P2_MODCODRQ_MODE 0xf2b00030
+#define R0900_P2_MODCODLST0 0xf2b0
/*P2_MODCODLST1*/
-#define R0900_P2_MODCODLST1 0xf2b1
-#define F0900_P2_DIS_MODCOD29 0xf2b100f0
-#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f
+#define R0900_P2_MODCODLST1 0xf2b1
+#define F0900_P2_DIS_MODCOD29 0xf2b100f0
+#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f
/*P2_MODCODLST2*/
-#define R0900_P2_MODCODLST2 0xf2b2
-#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0
-#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f
+#define R0900_P2_MODCODLST2 0xf2b2
+#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0
+#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f
/*P2_MODCODLST3*/
-#define R0900_P2_MODCODLST3 0xf2b3
-#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0
-#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f
+#define R0900_P2_MODCODLST3 0xf2b3
+#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0
+#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f
/*P2_MODCODLST4*/
-#define R0900_P2_MODCODLST4 0xf2b4
-#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0
-#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f
+#define R0900_P2_MODCODLST4 0xf2b4
+#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0
+#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f
/*P2_MODCODLST5*/
-#define R0900_P2_MODCODLST5 0xf2b5
-#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0
-#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f
+#define R0900_P2_MODCODLST5 0xf2b5
+#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0
+#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f
/*P2_MODCODLST6*/
-#define R0900_P2_MODCODLST6 0xf2b6
-#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0
-#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f
+#define R0900_P2_MODCODLST6 0xf2b6
+#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0
+#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f
/*P2_MODCODLST7*/
-#define R0900_P2_MODCODLST7 0xf2b7
-#define F0900_P2_DIS_8P_9_10 0xf2b700f0
-#define F0900_P2_DIS_8P_8_9 0xf2b7000f
+#define R0900_P2_MODCODLST7 0xf2b7
+#define F0900_P2_DIS_8P_9_10 0xf2b700f0
+#define F0900_P2_DIS_8P_8_9 0xf2b7000f
/*P2_MODCODLST8*/
-#define R0900_P2_MODCODLST8 0xf2b8
-#define F0900_P2_DIS_8P_5_6 0xf2b800f0
-#define F0900_P2_DIS_8P_3_4 0xf2b8000f
+#define R0900_P2_MODCODLST8 0xf2b8
+#define F0900_P2_DIS_8P_5_6 0xf2b800f0
+#define F0900_P2_DIS_8P_3_4 0xf2b8000f
/*P2_MODCODLST9*/
-#define R0900_P2_MODCODLST9 0xf2b9
-#define F0900_P2_DIS_8P_2_3 0xf2b900f0
-#define F0900_P2_DIS_8P_3_5 0xf2b9000f
+#define R0900_P2_MODCODLST9 0xf2b9
+#define F0900_P2_DIS_8P_2_3 0xf2b900f0
+#define F0900_P2_DIS_8P_3_5 0xf2b9000f
/*P2_MODCODLSTA*/
-#define R0900_P2_MODCODLSTA 0xf2ba
-#define F0900_P2_DIS_QP_9_10 0xf2ba00f0
-#define F0900_P2_DIS_QP_8_9 0xf2ba000f
+#define R0900_P2_MODCODLSTA 0xf2ba
+#define F0900_P2_DIS_QP_9_10 0xf2ba00f0
+#define F0900_P2_DIS_QP_8_9 0xf2ba000f
/*P2_MODCODLSTB*/
-#define R0900_P2_MODCODLSTB 0xf2bb
-#define F0900_P2_DIS_QP_5_6 0xf2bb00f0
-#define F0900_P2_DIS_QP_4_5 0xf2bb000f
+#define R0900_P2_MODCODLSTB 0xf2bb
+#define F0900_P2_DIS_QP_5_6 0xf2bb00f0
+#define F0900_P2_DIS_QP_4_5 0xf2bb000f
/*P2_MODCODLSTC*/
-#define R0900_P2_MODCODLSTC 0xf2bc
-#define F0900_P2_DIS_QP_3_4 0xf2bc00f0
-#define F0900_P2_DIS_QP_2_3 0xf2bc000f
+#define R0900_P2_MODCODLSTC 0xf2bc
+#define F0900_P2_DIS_QP_3_4 0xf2bc00f0
+#define F0900_P2_DIS_QP_2_3 0xf2bc000f
/*P2_MODCODLSTD*/
-#define R0900_P2_MODCODLSTD 0xf2bd
-#define F0900_P2_DIS_QP_3_5 0xf2bd00f0
-#define F0900_P2_DIS_QP_1_2 0xf2bd000f
+#define R0900_P2_MODCODLSTD 0xf2bd
+#define F0900_P2_DIS_QP_3_5 0xf2bd00f0
+#define F0900_P2_DIS_QP_1_2 0xf2bd000f
/*P2_MODCODLSTE*/
-#define R0900_P2_MODCODLSTE 0xf2be
-#define F0900_P2_DIS_QP_2_5 0xf2be00f0
-#define F0900_P2_DIS_QP_1_3 0xf2be000f
+#define R0900_P2_MODCODLSTE 0xf2be
+#define F0900_P2_DIS_QP_2_5 0xf2be00f0
+#define F0900_P2_DIS_QP_1_3 0xf2be000f
/*P2_MODCODLSTF*/
-#define R0900_P2_MODCODLSTF 0xf2bf
-#define F0900_P2_DIS_QP_1_4 0xf2bf00f0
-#define F0900_P2_DDEMOD_SET 0xf2bf0002
-#define F0900_P2_DDEMOD_MASK 0xf2bf0001
+#define R0900_P2_MODCODLSTF 0xf2bf
+#define F0900_P2_DIS_QP_1_4 0xf2bf00f0
+
+/*P2_GAUSSR0*/
+#define R0900_P2_GAUSSR0 0xf2c0
+#define F0900_P2_EN_CCIMODE 0xf2c00080
+#define F0900_P2_R0_GAUSSIEN 0xf2c0007f
+
+/*P2_CCIR0*/
+#define R0900_P2_CCIR0 0xf2c1
+#define F0900_P2_CCIDETECT_PLHONLY 0xf2c10080
+#define F0900_P2_R0_CCI 0xf2c1007f
+
+/*P2_CCIQUANT*/
+#define R0900_P2_CCIQUANT 0xf2c2
+#define F0900_P2_CCI_BETA 0xf2c200e0
+#define F0900_P2_CCI_QUANT 0xf2c2001f
+
+/*P2_CCITHRES*/
+#define R0900_P2_CCITHRES 0xf2c3
+#define F0900_P2_CCI_THRESHOLD 0xf2c300ff
+
+/*P2_CCIACC*/
+#define R0900_P2_CCIACC 0xf2c4
+#define F0900_P2_CCI_VALUE 0xf2c400ff
/*P2_DMDRESCFG*/
-#define R0900_P2_DMDRESCFG 0xf2c6
-#define F0900_P2_DMDRES_RESET 0xf2c60080
-#define F0900_P2_DMDRES_NOISESQR 0xf2c60010
-#define F0900_P2_DMDRES_STRALL 0xf2c60008
-#define F0900_P2_DMDRES_NEWONLY 0xf2c60004
-#define F0900_P2_DMDRES_NOSTORE 0xf2c60002
-#define F0900_P2_DMDRES_AGC2MEM 0xf2c60001
+#define R0900_P2_DMDRESCFG 0xf2c6
+#define F0900_P2_DMDRES_RESET 0xf2c60080
+#define F0900_P2_DMDRES_STRALL 0xf2c60008
+#define F0900_P2_DMDRES_NEWONLY 0xf2c60004
+#define F0900_P2_DMDRES_NOSTORE 0xf2c60002
/*P2_DMDRESADR*/
-#define R0900_P2_DMDRESADR 0xf2c7
-#define F0900_P2_SUSP_PREDCANAL 0xf2c70080
-#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040
-#define F0900_P2_DMDRES_MEMFULL 0xf2c70030
-#define F0900_P2_DMDRES_RESNBR 0xf2c7000f
+#define R0900_P2_DMDRESADR 0xf2c7
+#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040
+#define F0900_P2_DMDRES_MEMFULL 0xf2c70030
+#define F0900_P2_DMDRES_RESNBR 0xf2c7000f
/*P2_DMDRESDATA7*/
-#define R0900_P2_DMDRESDATA7 0xf2c8
-#define F0900_P2_DMDRES_DATA7 0xf2c800ff
+#define R0900_P2_DMDRESDATA7 0xf2c8
+#define F0900_P2_DMDRES_DATA7 0xf2c800ff
/*P2_DMDRESDATA6*/
-#define R0900_P2_DMDRESDATA6 0xf2c9
-#define F0900_P2_DMDRES_DATA6 0xf2c900ff
+#define R0900_P2_DMDRESDATA6 0xf2c9
+#define F0900_P2_DMDRES_DATA6 0xf2c900ff
/*P2_DMDRESDATA5*/
-#define R0900_P2_DMDRESDATA5 0xf2ca
-#define F0900_P2_DMDRES_DATA5 0xf2ca00ff
+#define R0900_P2_DMDRESDATA5 0xf2ca
+#define F0900_P2_DMDRES_DATA5 0xf2ca00ff
/*P2_DMDRESDATA4*/
-#define R0900_P2_DMDRESDATA4 0xf2cb
-#define F0900_P2_DMDRES_DATA4 0xf2cb00ff
+#define R0900_P2_DMDRESDATA4 0xf2cb
+#define F0900_P2_DMDRES_DATA4 0xf2cb00ff
/*P2_DMDRESDATA3*/
-#define R0900_P2_DMDRESDATA3 0xf2cc
-#define F0900_P2_DMDRES_DATA3 0xf2cc00ff
+#define R0900_P2_DMDRESDATA3 0xf2cc
+#define F0900_P2_DMDRES_DATA3 0xf2cc00ff
/*P2_DMDRESDATA2*/
-#define R0900_P2_DMDRESDATA2 0xf2cd
-#define F0900_P2_DMDRES_DATA2 0xf2cd00ff
+#define R0900_P2_DMDRESDATA2 0xf2cd
+#define F0900_P2_DMDRES_DATA2 0xf2cd00ff
/*P2_DMDRESDATA1*/
-#define R0900_P2_DMDRESDATA1 0xf2ce
-#define F0900_P2_DMDRES_DATA1 0xf2ce00ff
+#define R0900_P2_DMDRESDATA1 0xf2ce
+#define F0900_P2_DMDRES_DATA1 0xf2ce00ff
/*P2_DMDRESDATA0*/
-#define R0900_P2_DMDRESDATA0 0xf2cf
-#define F0900_P2_DMDRES_DATA0 0xf2cf00ff
+#define R0900_P2_DMDRESDATA0 0xf2cf
+#define F0900_P2_DMDRES_DATA0 0xf2cf00ff
/*P2_FFEI1*/
-#define R0900_P2_FFEI1 0xf2d0
-#define F0900_P2_FFE_ACCI1 0xf2d001ff
+#define R0900_P2_FFEI1 0xf2d0
+#define F0900_P2_FFE_ACCI1 0xf2d001ff
/*P2_FFEQ1*/
-#define R0900_P2_FFEQ1 0xf2d1
-#define F0900_P2_FFE_ACCQ1 0xf2d101ff
+#define R0900_P2_FFEQ1 0xf2d1
+#define F0900_P2_FFE_ACCQ1 0xf2d101ff
/*P2_FFEI2*/
-#define R0900_P2_FFEI2 0xf2d2
-#define F0900_P2_FFE_ACCI2 0xf2d201ff
+#define R0900_P2_FFEI2 0xf2d2
+#define F0900_P2_FFE_ACCI2 0xf2d201ff
/*P2_FFEQ2*/
-#define R0900_P2_FFEQ2 0xf2d3
-#define F0900_P2_FFE_ACCQ2 0xf2d301ff
+#define R0900_P2_FFEQ2 0xf2d3
+#define F0900_P2_FFE_ACCQ2 0xf2d301ff
/*P2_FFEI3*/
-#define R0900_P2_FFEI3 0xf2d4
-#define F0900_P2_FFE_ACCI3 0xf2d401ff
+#define R0900_P2_FFEI3 0xf2d4
+#define F0900_P2_FFE_ACCI3 0xf2d401ff
/*P2_FFEQ3*/
-#define R0900_P2_FFEQ3 0xf2d5
-#define F0900_P2_FFE_ACCQ3 0xf2d501ff
+#define R0900_P2_FFEQ3 0xf2d5
+#define F0900_P2_FFE_ACCQ3 0xf2d501ff
/*P2_FFEI4*/
-#define R0900_P2_FFEI4 0xf2d6
-#define F0900_P2_FFE_ACCI4 0xf2d601ff
+#define R0900_P2_FFEI4 0xf2d6
+#define F0900_P2_FFE_ACCI4 0xf2d601ff
/*P2_FFEQ4*/
-#define R0900_P2_FFEQ4 0xf2d7
-#define F0900_P2_FFE_ACCQ4 0xf2d701ff
+#define R0900_P2_FFEQ4 0xf2d7
+#define F0900_P2_FFE_ACCQ4 0xf2d701ff
/*P2_FFECFG*/
-#define R0900_P2_FFECFG 0xf2d8
-#define F0900_P2_EQUALFFE_ON 0xf2d80040
-#define F0900_P2_EQUAL_USEDSYMB 0xf2d80030
-#define F0900_P2_MU_EQUALFFE 0xf2d80007
+#define R0900_P2_FFECFG 0xf2d8
+#define F0900_P2_EQUALFFE_ON 0xf2d80040
+#define F0900_P2_MU_EQUALFFE 0xf2d80007
/*P2_TNRCFG*/
-#define R0900_P2_TNRCFG 0xf2e0
-#define F0900_P2_TUN_ACKFAIL 0xf2e00080
-#define F0900_P2_TUN_TYPE 0xf2e00070
-#define F0900_P2_TUN_SECSTOP 0xf2e00008
-#define F0900_P2_TUN_VCOSRCH 0xf2e00004
-#define F0900_P2_TUN_MADDRESS 0xf2e00003
+#define R0900_P2_TNRCFG 0xf2e0
+#define F0900_P2_TUN_ACKFAIL 0xf2e00080
+#define F0900_P2_TUN_TYPE 0xf2e00070
+#define F0900_P2_TUN_SECSTOP 0xf2e00008
+#define F0900_P2_TUN_VCOSRCH 0xf2e00004
+#define F0900_P2_TUN_MADDRESS 0xf2e00003
/*P2_TNRCFG2*/
-#define R0900_P2_TNRCFG2 0xf2e1
-#define F0900_P2_TUN_IQSWAP 0xf2e10080
-#define F0900_P2_STB6110_STEP2MHZ 0xf2e10040
-#define F0900_P2_STB6120_DBLI2C 0xf2e10020
-#define F0900_P2_DIS_FCCK 0xf2e10010
-#define F0900_P2_DIS_LPEN 0xf2e10008
-#define F0900_P2_DIS_BWCALC 0xf2e10004
-#define F0900_P2_SHORT_WAITSTATES 0xf2e10002
-#define F0900_P2_DIS_2BWAGC1 0xf2e10001
+#define R0900_P2_TNRCFG2 0xf2e1
+#define F0900_P2_TUN_IQSWAP 0xf2e10080
+#define F0900_P2_DIS_BWCALC 0xf2e10004
+#define F0900_P2_SHORT_WAITSTATES 0xf2e10002
/*P2_TNRXTAL*/
-#define R0900_P2_TNRXTAL 0xf2e4
-#define F0900_P2_TUN_MCLKDECIMAL 0xf2e400e0
-#define F0900_P2_TUN_XTALFREQ 0xf2e4001f
+#define R0900_P2_TNRXTAL 0xf2e4
+#define F0900_P2_TUN_XTALFREQ 0xf2e4001f
/*P2_TNRSTEPS*/
-#define R0900_P2_TNRSTEPS 0xf2e7
-#define F0900_P2_TUNER_BW1P6 0xf2e70080
-#define F0900_P2_BWINC_OFFSET 0xf2e70070
-#define F0900_P2_SOFTSTEP_RNG 0xf2e70008
-#define F0900_P2_TUN_BWOFFSET 0xf2e70107
+#define R0900_P2_TNRSTEPS 0xf2e7
+#define F0900_P2_TUNER_BW0P125 0xf2e70080
+#define F0900_P2_BWINC_OFFSET 0xf2e70170
+#define F0900_P2_SOFTSTEP_RNG 0xf2e70008
+#define F0900_P2_TUN_BWOFFSET 0xf2e70007
/*P2_TNRGAIN*/
-#define R0900_P2_TNRGAIN 0xf2e8
-#define F0900_P2_TUN_KDIVEN 0xf2e800c0
-#define F0900_P2_STB6X00_OCK 0xf2e80030
-#define F0900_P2_TUN_GAIN 0xf2e8000f
+#define R0900_P2_TNRGAIN 0xf2e8
+#define F0900_P2_TUN_KDIVEN 0xf2e800c0
+#define F0900_P2_STB6X00_OCK 0xf2e80030
+#define F0900_P2_TUN_GAIN 0xf2e8000f
/*P2_TNRRF1*/
-#define R0900_P2_TNRRF1 0xf2e9
-#define F0900_P2_TUN_RFFREQ2 0xf2e900ff
+#define R0900_P2_TNRRF1 0xf2e9
+#define F0900_P2_TUN_RFFREQ2 0xf2e900ff
/*P2_TNRRF0*/
-#define R0900_P2_TNRRF0 0xf2ea
-#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff
+#define R0900_P2_TNRRF0 0xf2ea
+#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff
/*P2_TNRBW*/
-#define R0900_P2_TNRBW 0xf2eb
-#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0
-#define F0900_P2_TUN_BW 0xf2eb003f
+#define R0900_P2_TNRBW 0xf2eb
+#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0
+#define F0900_P2_TUN_BW 0xf2eb003f
/*P2_TNRADJ*/
-#define R0900_P2_TNRADJ 0xf2ec
-#define F0900_P2_STB61X0_RCLK 0xf2ec0080
-#define F0900_P2_STB61X0_CALTIME 0xf2ec0040
-#define F0900_P2_STB6X00_DLB 0xf2ec0038
-#define F0900_P2_STB6000_FCL 0xf2ec0007
+#define R0900_P2_TNRADJ 0xf2ec
+#define F0900_P2_STB61X0_CALTIME 0xf2ec0040
/*P2_TNRCTL2*/
-#define R0900_P2_TNRCTL2 0xf2ed
-#define F0900_P2_STB61X0_LCP1_RCCKOFF 0xf2ed0080
-#define F0900_P2_STB61X0_LCP0 0xf2ed0040
-#define F0900_P2_STB61X0_XTOUT_RFOUTS 0xf2ed0020
-#define F0900_P2_STB61X0_XTON_MCKDV 0xf2ed0010
-#define F0900_P2_STB61X0_CALOFF_DCOFF 0xf2ed0008
-#define F0900_P2_STB6110_LPT 0xf2ed0004
-#define F0900_P2_STB6110_RX 0xf2ed0002
-#define F0900_P2_STB6110_SYN 0xf2ed0001
+#define R0900_P2_TNRCTL2 0xf2ed
+#define F0900_P2_STB61X0_RCCKOFF 0xf2ed0080
+#define F0900_P2_STB61X0_ICP_SDOFF 0xf2ed0040
+#define F0900_P2_STB61X0_DCLOOPOFF 0xf2ed0020
+#define F0900_P2_STB61X0_REFOUTSEL 0xf2ed0010
+#define F0900_P2_STB61X0_CALOFF 0xf2ed0008
+#define F0900_P2_STB6XX0_LPT_BEN 0xf2ed0004
+#define F0900_P2_STB6XX0_RX_OSCP 0xf2ed0002
+#define F0900_P2_STB6XX0_SYN 0xf2ed0001
/*P2_TNRCFG3*/
-#define R0900_P2_TNRCFG3 0xf2ee
-#define F0900_P2_STB6120_DISCTRL1 0xf2ee0080
-#define F0900_P2_STB6120_INVORDER 0xf2ee0040
-#define F0900_P2_STB6120_ENCTRL6 0xf2ee0020
-#define F0900_P2_TUN_PLLFREQ 0xf2ee001c
-#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003
+#define R0900_P2_TNRCFG3 0xf2ee
+#define F0900_P2_TUN_PLLFREQ 0xf2ee001c
+#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003
/*P2_TNRLAUNCH*/
-#define R0900_P2_TNRLAUNCH 0xf2f0
+#define R0900_P2_TNRLAUNCH 0xf2f0
/*P2_TNRLD*/
-#define R0900_P2_TNRLD 0xf2f0
-#define F0900_P2_TUNLD_VCOING 0xf2f00080
-#define F0900_P2_TUN_REG1FAIL 0xf2f00040
-#define F0900_P2_TUN_REG2FAIL 0xf2f00020
-#define F0900_P2_TUN_REG3FAIL 0xf2f00010
-#define F0900_P2_TUN_REG4FAIL 0xf2f00008
-#define F0900_P2_TUN_REG5FAIL 0xf2f00004
-#define F0900_P2_TUN_BWING 0xf2f00002
-#define F0900_P2_TUN_LOCKED 0xf2f00001
+#define R0900_P2_TNRLD 0xf2f0
+#define F0900_P2_TUNLD_VCOING 0xf2f00080
+#define F0900_P2_TUN_REG1FAIL 0xf2f00040
+#define F0900_P2_TUN_REG2FAIL 0xf2f00020
+#define F0900_P2_TUN_REG3FAIL 0xf2f00010
+#define F0900_P2_TUN_REG4FAIL 0xf2f00008
+#define F0900_P2_TUN_REG5FAIL 0xf2f00004
+#define F0900_P2_TUN_BWING 0xf2f00002
+#define F0900_P2_TUN_LOCKED 0xf2f00001
/*P2_TNROBSL*/
-#define R0900_P2_TNROBSL 0xf2f6
-#define F0900_P2_TUN_I2CABORTED 0xf2f60080
-#define F0900_P2_TUN_LPEN 0xf2f60040
-#define F0900_P2_TUN_FCCK 0xf2f60020
-#define F0900_P2_TUN_I2CLOCKED 0xf2f60010
-#define F0900_P2_TUN_PROGDONE 0xf2f6000c
-#define F0900_P2_TUN_RFRESTE1 0xf2f60003
+#define R0900_P2_TNROBSL 0xf2f6
+#define F0900_P2_TUN_I2CABORTED 0xf2f60080
+#define F0900_P2_TUN_LPEN 0xf2f60040
+#define F0900_P2_TUN_FCCK 0xf2f60020
+#define F0900_P2_TUN_I2CLOCKED 0xf2f60010
+#define F0900_P2_TUN_PROGDONE 0xf2f6000c
+#define F0900_P2_TUN_RFRESTE1 0xf2f60003
/*P2_TNRRESTE*/
-#define R0900_P2_TNRRESTE 0xf2f7
-#define F0900_P2_TUN_RFRESTE0 0xf2f700ff
+#define R0900_P2_TNRRESTE 0xf2f7
+#define F0900_P2_TUN_RFRESTE0 0xf2f700ff
/*P2_SMAPCOEF7*/
-#define R0900_P2_SMAPCOEF7 0xf300
-#define F0900_P2_DIS_QSCALE 0xf3000080
-#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f
+#define R0900_P2_SMAPCOEF7 0xf300
+#define F0900_P2_DIS_QSCALE 0xf3000080
+#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f
/*P2_SMAPCOEF6*/
-#define R0900_P2_SMAPCOEF6 0xf301
-#define F0900_P2_DIS_NEWSCALE 0xf3010008
-#define F0900_P2_ADJ_8PSKLLR1 0xf3010004
-#define F0900_P2_OLD_8PSKLLR1 0xf3010002
-#define F0900_P2_DIS_AB8PSK 0xf3010001
+#define R0900_P2_SMAPCOEF6 0xf301
+#define F0900_P2_ADJ_8PSKLLR1 0xf3010004
+#define F0900_P2_OLD_8PSKLLR1 0xf3010002
+#define F0900_P2_DIS_AB8PSK 0xf3010001
/*P2_SMAPCOEF5*/
-#define R0900_P2_SMAPCOEF5 0xf302
-#define F0900_P2_DIS_8SCALE 0xf3020080
-#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f
+#define R0900_P2_SMAPCOEF5 0xf302
+#define F0900_P2_DIS_8SCALE 0xf3020080
+#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f
+
+/*P2_NCO2MAX1*/
+#define R0900_P2_NCO2MAX1 0xf314
+#define F0900_P2_TETA2_MAXVABS1 0xf31400ff
+
+/*P2_NCO2MAX0*/
+#define R0900_P2_NCO2MAX0 0xf315
+#define F0900_P2_TETA2_MAXVABS0 0xf31500ff
+
+/*P2_NCO2FR1*/
+#define R0900_P2_NCO2FR1 0xf316
+#define F0900_P2_NCO2FINAL_ANGLE1 0xf31600ff
+
+/*P2_NCO2FR0*/
+#define R0900_P2_NCO2FR0 0xf317
+#define F0900_P2_NCO2FINAL_ANGLE0 0xf31700ff
+
+/*P2_CFR2AVRGE1*/
+#define R0900_P2_CFR2AVRGE1 0xf318
+#define F0900_P2_I2C_CFR2AVERAGE1 0xf31800ff
+
+/*P2_CFR2AVRGE0*/
+#define R0900_P2_CFR2AVRGE0 0xf319
+#define F0900_P2_I2C_CFR2AVERAGE0 0xf31900ff
/*P2_DMDPLHSTAT*/
-#define R0900_P2_DMDPLHSTAT 0xf320
-#define F0900_P2_PLH_STATISTIC 0xf32000ff
+#define R0900_P2_DMDPLHSTAT 0xf320
+#define F0900_P2_PLH_STATISTIC 0xf32000ff
/*P2_LOCKTIME3*/
-#define R0900_P2_LOCKTIME3 0xf322
-#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff
+#define R0900_P2_LOCKTIME3 0xf322
+#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff
/*P2_LOCKTIME2*/
-#define R0900_P2_LOCKTIME2 0xf323
-#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff
+#define R0900_P2_LOCKTIME2 0xf323
+#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff
/*P2_LOCKTIME1*/
-#define R0900_P2_LOCKTIME1 0xf324
-#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff
+#define R0900_P2_LOCKTIME1 0xf324
+#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff
/*P2_LOCKTIME0*/
-#define R0900_P2_LOCKTIME0 0xf325
-#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff
+#define R0900_P2_LOCKTIME0 0xf325
+#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff
/*P2_VITSCALE*/
-#define R0900_P2_VITSCALE 0xf332
-#define F0900_P2_NVTH_NOSRANGE 0xf3320080
-#define F0900_P2_VERROR_MAXMODE 0xf3320040
-#define F0900_P2_KDIV_MODE 0xf3320030
-#define F0900_P2_NSLOWSN_LOCKED 0xf3320008
-#define F0900_P2_DELOCK_PRFLOSS 0xf3320004
-#define F0900_P2_DIS_RSFLOCK 0xf3320002
+#define R0900_P2_VITSCALE 0xf332
+#define F0900_P2_NVTH_NOSRANGE 0xf3320080
+#define F0900_P2_VERROR_MAXMODE 0xf3320040
+#define F0900_P2_NSLOWSN_LOCKED 0xf3320008
+#define F0900_P2_DIS_RSFLOCK 0xf3320002
/*P2_FECM*/
-#define R0900_P2_FECM 0xf333
-#define F0900_P2_DSS_DVB 0xf3330080
-#define F0900_P2_DEMOD_BYPASS 0xf3330040
-#define F0900_P2_CMP_SLOWMODE 0xf3330020
-#define F0900_P2_DSS_SRCH 0xf3330010
-#define F0900_P2_DIFF_MODEVIT 0xf3330004
-#define F0900_P2_SYNCVIT 0xf3330002
-#define F0900_P2_IQINV 0xf3330001
+#define R0900_P2_FECM 0xf333
+#define F0900_P2_DSS_DVB 0xf3330080
+#define F0900_P2_DSS_SRCH 0xf3330010
+#define F0900_P2_SYNCVIT 0xf3330002
+#define F0900_P2_IQINV 0xf3330001
/*P2_VTH12*/
-#define R0900_P2_VTH12 0xf334
-#define F0900_P2_VTH12 0xf33400ff
+#define R0900_P2_VTH12 0xf334
+#define F0900_P2_VTH12 0xf33400ff
/*P2_VTH23*/
-#define R0900_P2_VTH23 0xf335
-#define F0900_P2_VTH23 0xf33500ff
+#define R0900_P2_VTH23 0xf335
+#define F0900_P2_VTH23 0xf33500ff
/*P2_VTH34*/
-#define R0900_P2_VTH34 0xf336
-#define F0900_P2_VTH34 0xf33600ff
+#define R0900_P2_VTH34 0xf336
+#define F0900_P2_VTH34 0xf33600ff
/*P2_VTH56*/
-#define R0900_P2_VTH56 0xf337
-#define F0900_P2_VTH56 0xf33700ff
+#define R0900_P2_VTH56 0xf337
+#define F0900_P2_VTH56 0xf33700ff
/*P2_VTH67*/
-#define R0900_P2_VTH67 0xf338
-#define F0900_P2_VTH67 0xf33800ff
+#define R0900_P2_VTH67 0xf338
+#define F0900_P2_VTH67 0xf33800ff
/*P2_VTH78*/
-#define R0900_P2_VTH78 0xf339
-#define F0900_P2_VTH78 0xf33900ff
+#define R0900_P2_VTH78 0xf339
+#define F0900_P2_VTH78 0xf33900ff
/*P2_VITCURPUN*/
-#define R0900_P2_VITCURPUN 0xf33a
-#define F0900_P2_VIT_MAPPING 0xf33a00e0
-#define F0900_P2_VIT_CURPUN 0xf33a001f
+#define R0900_P2_VITCURPUN 0xf33a
+#define F0900_P2_VIT_CURPUN 0xf33a001f
/*P2_VERROR*/
-#define R0900_P2_VERROR 0xf33b
-#define F0900_P2_REGERR_VIT 0xf33b00ff
+#define R0900_P2_VERROR 0xf33b
+#define F0900_P2_REGERR_VIT 0xf33b00ff
/*P2_PRVIT*/
-#define R0900_P2_PRVIT 0xf33c
-#define F0900_P2_DIS_VTHLOCK 0xf33c0040
-#define F0900_P2_E7_8VIT 0xf33c0020
-#define F0900_P2_E6_7VIT 0xf33c0010
-#define F0900_P2_E5_6VIT 0xf33c0008
-#define F0900_P2_E3_4VIT 0xf33c0004
-#define F0900_P2_E2_3VIT 0xf33c0002
-#define F0900_P2_E1_2VIT 0xf33c0001
+#define R0900_P2_PRVIT 0xf33c
+#define F0900_P2_DIS_VTHLOCK 0xf33c0040
+#define F0900_P2_E7_8VIT 0xf33c0020
+#define F0900_P2_E6_7VIT 0xf33c0010
+#define F0900_P2_E5_6VIT 0xf33c0008
+#define F0900_P2_E3_4VIT 0xf33c0004
+#define F0900_P2_E2_3VIT 0xf33c0002
+#define F0900_P2_E1_2VIT 0xf33c0001
/*P2_VAVSRVIT*/
-#define R0900_P2_VAVSRVIT 0xf33d
-#define F0900_P2_AMVIT 0xf33d0080
-#define F0900_P2_FROZENVIT 0xf33d0040
-#define F0900_P2_SNVIT 0xf33d0030
-#define F0900_P2_TOVVIT 0xf33d000c
-#define F0900_P2_HYPVIT 0xf33d0003
+#define R0900_P2_VAVSRVIT 0xf33d
+#define F0900_P2_AMVIT 0xf33d0080
+#define F0900_P2_FROZENVIT 0xf33d0040
+#define F0900_P2_SNVIT 0xf33d0030
+#define F0900_P2_TOVVIT 0xf33d000c
+#define F0900_P2_HYPVIT 0xf33d0003
/*P2_VSTATUSVIT*/
-#define R0900_P2_VSTATUSVIT 0xf33e
-#define F0900_P2_VITERBI_ON 0xf33e0080
-#define F0900_P2_END_LOOPVIT 0xf33e0040
-#define F0900_P2_VITERBI_DEPRF 0xf33e0020
-#define F0900_P2_PRFVIT 0xf33e0010
-#define F0900_P2_LOCKEDVIT 0xf33e0008
-#define F0900_P2_VITERBI_DELOCK 0xf33e0004
-#define F0900_P2_VIT_DEMODSEL 0xf33e0002
-#define F0900_P2_VITERBI_COMPOUT 0xf33e0001
+#define R0900_P2_VSTATUSVIT 0xf33e
+#define F0900_P2_PRFVIT 0xf33e0010
+#define F0900_P2_LOCKEDVIT 0xf33e0008
/*P2_VTHINUSE*/
-#define R0900_P2_VTHINUSE 0xf33f
-#define F0900_P2_VIT_INUSE 0xf33f00ff
+#define R0900_P2_VTHINUSE 0xf33f
+#define F0900_P2_VIT_INUSE 0xf33f00ff
/*P2_KDIV12*/
-#define R0900_P2_KDIV12 0xf340
-#define F0900_P2_KDIV12_MANUAL 0xf3400080
-#define F0900_P2_K_DIVIDER_12 0xf340007f
+#define R0900_P2_KDIV12 0xf340
+#define F0900_P2_K_DIVIDER_12 0xf340007f
/*P2_KDIV23*/
-#define R0900_P2_KDIV23 0xf341
-#define F0900_P2_KDIV23_MANUAL 0xf3410080
-#define F0900_P2_K_DIVIDER_23 0xf341007f
+#define R0900_P2_KDIV23 0xf341
+#define F0900_P2_K_DIVIDER_23 0xf341007f
/*P2_KDIV34*/
-#define R0900_P2_KDIV34 0xf342
-#define F0900_P2_KDIV34_MANUAL 0xf3420080
-#define F0900_P2_K_DIVIDER_34 0xf342007f
+#define R0900_P2_KDIV34 0xf342
+#define F0900_P2_K_DIVIDER_34 0xf342007f
/*P2_KDIV56*/
-#define R0900_P2_KDIV56 0xf343
-#define F0900_P2_KDIV56_MANUAL 0xf3430080
-#define F0900_P2_K_DIVIDER_56 0xf343007f
+#define R0900_P2_KDIV56 0xf343
+#define F0900_P2_K_DIVIDER_56 0xf343007f
/*P2_KDIV67*/
-#define R0900_P2_KDIV67 0xf344
-#define F0900_P2_KDIV67_MANUAL 0xf3440080
-#define F0900_P2_K_DIVIDER_67 0xf344007f
+#define R0900_P2_KDIV67 0xf344
+#define F0900_P2_K_DIVIDER_67 0xf344007f
/*P2_KDIV78*/
-#define R0900_P2_KDIV78 0xf345
-#define F0900_P2_KDIV78_MANUAL 0xf3450080
-#define F0900_P2_K_DIVIDER_78 0xf345007f
+#define R0900_P2_KDIV78 0xf345
+#define F0900_P2_K_DIVIDER_78 0xf345007f
/*P2_PDELCTRL1*/
-#define R0900_P2_PDELCTRL1 0xf350
-#define F0900_P2_INV_MISMASK 0xf3500080
-#define F0900_P2_FORCE_ACCEPTED 0xf3500040
-#define F0900_P2_FILTER_EN 0xf3500020
-#define F0900_P2_FORCE_PKTDELINUSE 0xf3500010
-#define F0900_P2_HYSTEN 0xf3500008
-#define F0900_P2_HYSTSWRST 0xf3500004
-#define F0900_P2_EN_MIS00 0xf3500002
-#define F0900_P2_ALGOSWRST 0xf3500001
+#define R0900_P2_PDELCTRL1 0xf350
+#define F0900_P2_INV_MISMASK 0xf3500080
+#define F0900_P2_FILTER_EN 0xf3500020
+#define F0900_P2_EN_MIS00 0xf3500002
+#define F0900_P2_ALGOSWRST 0xf3500001
/*P2_PDELCTRL2*/
-#define R0900_P2_PDELCTRL2 0xf351
-#define F0900_P2_FORCE_CONTINUOUS 0xf3510080
-#define F0900_P2_RESET_UPKO_COUNT 0xf3510040
-#define F0900_P2_USER_PKTDELIN_NB 0xf3510020
-#define F0900_P2_FORCE_LOCKED 0xf3510010
-#define F0900_P2_DATA_UNBBSCRAM 0xf3510008
-#define F0900_P2_FORCE_LONGPKT 0xf3510004
-#define F0900_P2_FRAME_MODE 0xf3510002
+#define R0900_P2_PDELCTRL2 0xf351
+#define F0900_P2_RESET_UPKO_COUNT 0xf3510040
+#define F0900_P2_FRAME_MODE 0xf3510002
+#define F0900_P2_NOBCHERRFLG_USE 0xf3510001
/*P2_HYSTTHRESH*/
-#define R0900_P2_HYSTTHRESH 0xf354
-#define F0900_P2_UNLCK_THRESH 0xf35400f0
-#define F0900_P2_DELIN_LCK_THRESH 0xf354000f
+#define R0900_P2_HYSTTHRESH 0xf354
+#define F0900_P2_UNLCK_THRESH 0xf35400f0
+#define F0900_P2_DELIN_LCK_THRESH 0xf354000f
/*P2_ISIENTRY*/
-#define R0900_P2_ISIENTRY 0xf35e
-#define F0900_P2_ISI_ENTRY 0xf35e00ff
+#define R0900_P2_ISIENTRY 0xf35e
+#define F0900_P2_ISI_ENTRY 0xf35e00ff
/*P2_ISIBITENA*/
-#define R0900_P2_ISIBITENA 0xf35f
-#define F0900_P2_ISI_BIT_EN 0xf35f00ff
+#define R0900_P2_ISIBITENA 0xf35f
+#define F0900_P2_ISI_BIT_EN 0xf35f00ff
/*P2_MATSTR1*/
-#define R0900_P2_MATSTR1 0xf360
-#define F0900_P2_MATYPE_CURRENT1 0xf36000ff
+#define R0900_P2_MATSTR1 0xf360
+#define F0900_P2_MATYPE_CURRENT1 0xf36000ff
/*P2_MATSTR0*/
-#define R0900_P2_MATSTR0 0xf361
-#define F0900_P2_MATYPE_CURRENT0 0xf36100ff
+#define R0900_P2_MATSTR0 0xf361
+#define F0900_P2_MATYPE_CURRENT0 0xf36100ff
/*P2_UPLSTR1*/
-#define R0900_P2_UPLSTR1 0xf362
-#define F0900_P2_UPL_CURRENT1 0xf36200ff
+#define R0900_P2_UPLSTR1 0xf362
+#define F0900_P2_UPL_CURRENT1 0xf36200ff
/*P2_UPLSTR0*/
-#define R0900_P2_UPLSTR0 0xf363
-#define F0900_P2_UPL_CURRENT0 0xf36300ff
+#define R0900_P2_UPLSTR0 0xf363
+#define F0900_P2_UPL_CURRENT0 0xf36300ff
/*P2_DFLSTR1*/
-#define R0900_P2_DFLSTR1 0xf364
-#define F0900_P2_DFL_CURRENT1 0xf36400ff
+#define R0900_P2_DFLSTR1 0xf364
+#define F0900_P2_DFL_CURRENT1 0xf36400ff
/*P2_DFLSTR0*/
-#define R0900_P2_DFLSTR0 0xf365
-#define F0900_P2_DFL_CURRENT0 0xf36500ff
+#define R0900_P2_DFLSTR0 0xf365
+#define F0900_P2_DFL_CURRENT0 0xf36500ff
/*P2_SYNCSTR*/
-#define R0900_P2_SYNCSTR 0xf366
-#define F0900_P2_SYNC_CURRENT 0xf36600ff
+#define R0900_P2_SYNCSTR 0xf366
+#define F0900_P2_SYNC_CURRENT 0xf36600ff
/*P2_SYNCDSTR1*/
-#define R0900_P2_SYNCDSTR1 0xf367
-#define F0900_P2_SYNCD_CURRENT1 0xf36700ff
+#define R0900_P2_SYNCDSTR1 0xf367
+#define F0900_P2_SYNCD_CURRENT1 0xf36700ff
/*P2_SYNCDSTR0*/
-#define R0900_P2_SYNCDSTR0 0xf368
-#define F0900_P2_SYNCD_CURRENT0 0xf36800ff
+#define R0900_P2_SYNCDSTR0 0xf368
+#define F0900_P2_SYNCD_CURRENT0 0xf36800ff
/*P2_PDELSTATUS1*/
-#define R0900_P2_PDELSTATUS1 0xf369
-#define F0900_P2_PKTDELIN_DELOCK 0xf3690080
-#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040
-#define F0900_P2_CONTINUOUS_STREAM 0xf3690020
-#define F0900_P2_UNACCEPTED_STREAM 0xf3690010
-#define F0900_P2_BCH_ERROR_FLAG 0xf3690008
-#define F0900_P2_BBHCRCKO 0xf3690004
-#define F0900_P2_PKTDELIN_LOCK 0xf3690002
-#define F0900_P2_FIRST_LOCK 0xf3690001
+#define R0900_P2_PDELSTATUS1 0xf369
+#define F0900_P2_PKTDELIN_DELOCK 0xf3690080
+#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040
+#define F0900_P2_CONTINUOUS_STREAM 0xf3690020
+#define F0900_P2_UNACCEPTED_STREAM 0xf3690010
+#define F0900_P2_BCH_ERROR_FLAG 0xf3690008
+#define F0900_P2_PKTDELIN_LOCK 0xf3690002
+#define F0900_P2_FIRST_LOCK 0xf3690001
/*P2_PDELSTATUS2*/
-#define R0900_P2_PDELSTATUS2 0xf36a
-#define F0900_P2_PKTDEL_DEMODSEL 0xf36a0080
-#define F0900_P2_FRAME_MODCOD 0xf36a007c
-#define F0900_P2_FRAME_TYPE 0xf36a0003
+#define R0900_P2_PDELSTATUS2 0xf36a
+#define F0900_P2_FRAME_MODCOD 0xf36a007c
+#define F0900_P2_FRAME_TYPE 0xf36a0003
/*P2_BBFCRCKO1*/
-#define R0900_P2_BBFCRCKO1 0xf36b
-#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff
+#define R0900_P2_BBFCRCKO1 0xf36b
+#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff
/*P2_BBFCRCKO0*/
-#define R0900_P2_BBFCRCKO0 0xf36c
-#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff
+#define R0900_P2_BBFCRCKO0 0xf36c
+#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff
/*P2_UPCRCKO1*/
-#define R0900_P2_UPCRCKO1 0xf36d
-#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff
+#define R0900_P2_UPCRCKO1 0xf36d
+#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff
/*P2_UPCRCKO0*/
-#define R0900_P2_UPCRCKO0 0xf36e
-#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff
+#define R0900_P2_UPCRCKO0 0xf36e
+#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff
+
+/*P2_PDELCTRL3*/
+#define R0900_P2_PDELCTRL3 0xf36f
+#define F0900_P2_PKTDEL_CONTFAIL 0xf36f0080
+#define F0900_P2_NOFIFO_BCHERR 0xf36f0020
/*P2_TSSTATEM*/
-#define R0900_P2_TSSTATEM 0xf370
-#define F0900_P2_TSDIL_ON 0xf3700080
-#define F0900_P2_TSSKIPRS_ON 0xf3700040
-#define F0900_P2_TSRS_ON 0xf3700020
-#define F0900_P2_TSDESCRAMB_ON 0xf3700010
-#define F0900_P2_TSFRAME_MODE 0xf3700008
-#define F0900_P2_TS_DISABLE 0xf3700004
-#define F0900_P2_TSACM_MODE 0xf3700002
-#define F0900_P2_TSOUT_NOSYNC 0xf3700001
+#define R0900_P2_TSSTATEM 0xf370
+#define F0900_P2_TSDIL_ON 0xf3700080
+#define F0900_P2_TSRS_ON 0xf3700020
+#define F0900_P2_TSDESCRAMB_ON 0xf3700010
+#define F0900_P2_TSFRAME_MODE 0xf3700008
+#define F0900_P2_TS_DISABLE 0xf3700004
+#define F0900_P2_TSOUT_NOSYNC 0xf3700001
/*P2_TSCFGH*/
-#define R0900_P2_TSCFGH 0xf372
-#define F0900_P2_TSFIFO_DVBCI 0xf3720080
-#define F0900_P2_TSFIFO_SERIAL 0xf3720040
-#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020
-#define F0900_P2_TSFIFO_DUTY50 0xf3720010
-#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008
-#define F0900_P2_TSFIFO_ERRMODE 0xf3720006
-#define F0900_P2_RST_HWARE 0xf3720001
+#define R0900_P2_TSCFGH 0xf372
+#define F0900_P2_TSFIFO_DVBCI 0xf3720080
+#define F0900_P2_TSFIFO_SERIAL 0xf3720040
+#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020
+#define F0900_P2_TSFIFO_DUTY50 0xf3720010
+#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008
+#define F0900_P2_TSFIFO_ERRMODE 0xf3720006
+#define F0900_P2_RST_HWARE 0xf3720001
/*P2_TSCFGM*/
-#define R0900_P2_TSCFGM 0xf373
-#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0
-#define F0900_P2_TSFIFO_PERMDATA 0xf3730020
-#define F0900_P2_TSFIFO_NONEWSGNL 0xf3730010
-#define F0900_P2_TSFIFO_BITSPEED 0xf3730008
-#define F0900_P2_NPD_SPECDVBS2 0xf3730004
-#define F0900_P2_TSFIFO_STOPCKDIS 0xf3730002
-#define F0900_P2_TSFIFO_INVDATA 0xf3730001
+#define R0900_P2_TSCFGM 0xf373
+#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0
+#define F0900_P2_TSFIFO_PERMDATA 0xf3730020
+#define F0900_P2_TSFIFO_DPUNACT 0xf3730002
+#define F0900_P2_TSFIFO_INVDATA 0xf3730001
/*P2_TSCFGL*/
-#define R0900_P2_TSCFGL 0xf374
-#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0
-#define F0900_P2_BCHERROR_MODE 0xf3740030
-#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008
-#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004
-#define F0900_P2_TSFIFO_DPUNACT 0xf3740002
-#define F0900_P2_TSFIFO_NPDOFF 0xf3740001
+#define R0900_P2_TSCFGL 0xf374
+#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0
+#define F0900_P2_BCHERROR_MODE 0xf3740030
+#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008
+#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004
+#define F0900_P2_TSFIFO_BITSPEED 0xf3740003
/*P2_TSINSDELH*/
-#define R0900_P2_TSINSDELH 0xf376
-#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080
-#define F0900_P2_TSDEL_XXHEADER 0xf3760040
-#define F0900_P2_TSDEL_BBHEADER 0xf3760020
-#define F0900_P2_TSDEL_DATAFIELD 0xf3760010
-#define F0900_P2_TSINSDEL_ISCR 0xf3760008
-#define F0900_P2_TSINSDEL_NPD 0xf3760004
-#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002
-#define F0900_P2_TSINSDEL_CRC8 0xf3760001
+#define R0900_P2_TSINSDELH 0xf376
+#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080
+#define F0900_P2_TSDEL_XXHEADER 0xf3760040
+#define F0900_P2_TSDEL_BBHEADER 0xf3760020
+#define F0900_P2_TSDEL_DATAFIELD 0xf3760010
+#define F0900_P2_TSINSDEL_ISCR 0xf3760008
+#define F0900_P2_TSINSDEL_NPD 0xf3760004
+#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002
+#define F0900_P2_TSINSDEL_CRC8 0xf3760001
+
+/*P2_TSDIVN*/
+#define R0900_P2_TSDIVN 0xf379
+#define F0900_P2_TSFIFO_SPEEDMODE 0xf37900c0
+
+/*P2_TSCFG4*/
+#define R0900_P2_TSCFG4 0xf37a
+#define F0900_P2_TSFIFO_TSSPEEDMODE 0xf37a00c0
/*P2_TSSPEED*/
-#define R0900_P2_TSSPEED 0xf380
-#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff
+#define R0900_P2_TSSPEED 0xf380
+#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff
/*P2_TSSTATUS*/
-#define R0900_P2_TSSTATUS 0xf381
-#define F0900_P2_TSFIFO_LINEOK 0xf3810080
-#define F0900_P2_TSFIFO_ERROR 0xf3810040
-#define F0900_P2_TSFIFO_DATA7 0xf3810020
-#define F0900_P2_TSFIFO_NOSYNC 0xf3810010
-#define F0900_P2_ISCR_INITIALIZED 0xf3810008
-#define F0900_P2_ISCR_UPDATED 0xf3810004
-#define F0900_P2_SOFFIFO_UNREGUL 0xf3810002
-#define F0900_P2_DIL_READY 0xf3810001
+#define R0900_P2_TSSTATUS 0xf381
+#define F0900_P2_TSFIFO_LINEOK 0xf3810080
+#define F0900_P2_TSFIFO_ERROR 0xf3810040
+#define F0900_P2_DIL_READY 0xf3810001
/*P2_TSSTATUS2*/
-#define R0900_P2_TSSTATUS2 0xf382
-#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080
-#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040
-#define F0900_P2_DILXX_RESET 0xf3820020
-#define F0900_P2_TSSERIAL_IMPOS 0xf3820010
-#define F0900_P2_TSFIFO_LINENOK 0xf3820008
-#define F0900_P2_BITSPEED_EVENT 0xf3820004
-#define F0900_P2_SCRAMBDETECT 0xf3820002
-#define F0900_P2_ULDTV67_FALSELOCK 0xf3820001
+#define R0900_P2_TSSTATUS2 0xf382
+#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080
+#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040
+#define F0900_P2_DILXX_RESET 0xf3820020
+#define F0900_P2_TSSERIAL_IMPOS 0xf3820010
+#define F0900_P2_SCRAMBDETECT 0xf3820002
/*P2_TSBITRATE1*/
-#define R0900_P2_TSBITRATE1 0xf383
-#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff
+#define R0900_P2_TSBITRATE1 0xf383
+#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff
/*P2_TSBITRATE0*/
-#define R0900_P2_TSBITRATE0 0xf384
-#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff
+#define R0900_P2_TSBITRATE0 0xf384
+#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff
/*P2_ERRCTRL1*/
-#define R0900_P2_ERRCTRL1 0xf398
-#define F0900_P2_ERR_SOURCE1 0xf39800f0
-#define F0900_P2_NUM_EVENT1 0xf3980007
+#define R0900_P2_ERRCTRL1 0xf398
+#define F0900_P2_ERR_SOURCE1 0xf39800f0
+#define F0900_P2_NUM_EVENT1 0xf3980007
/*P2_ERRCNT12*/
-#define R0900_P2_ERRCNT12 0xf399
-#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080
-#define F0900_P2_ERR_CNT12 0xf399007f
+#define R0900_P2_ERRCNT12 0xf399
+#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080
+#define F0900_P2_ERR_CNT12 0xf399007f
/*P2_ERRCNT11*/
-#define R0900_P2_ERRCNT11 0xf39a
-#define F0900_P2_ERR_CNT11 0xf39a00ff
+#define R0900_P2_ERRCNT11 0xf39a
+#define F0900_P2_ERR_CNT11 0xf39a00ff
/*P2_ERRCNT10*/
-#define R0900_P2_ERRCNT10 0xf39b
-#define F0900_P2_ERR_CNT10 0xf39b00ff
+#define R0900_P2_ERRCNT10 0xf39b
+#define F0900_P2_ERR_CNT10 0xf39b00ff
/*P2_ERRCTRL2*/
-#define R0900_P2_ERRCTRL2 0xf39c
-#define F0900_P2_ERR_SOURCE2 0xf39c00f0
-#define F0900_P2_NUM_EVENT2 0xf39c0007
+#define R0900_P2_ERRCTRL2 0xf39c
+#define F0900_P2_ERR_SOURCE2 0xf39c00f0
+#define F0900_P2_NUM_EVENT2 0xf39c0007
/*P2_ERRCNT22*/
-#define R0900_P2_ERRCNT22 0xf39d
-#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080
-#define F0900_P2_ERR_CNT22 0xf39d007f
+#define R0900_P2_ERRCNT22 0xf39d
+#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080
+#define F0900_P2_ERR_CNT22 0xf39d007f
/*P2_ERRCNT21*/
-#define R0900_P2_ERRCNT21 0xf39e
-#define F0900_P2_ERR_CNT21 0xf39e00ff
+#define R0900_P2_ERRCNT21 0xf39e
+#define F0900_P2_ERR_CNT21 0xf39e00ff
/*P2_ERRCNT20*/
-#define R0900_P2_ERRCNT20 0xf39f
-#define F0900_P2_ERR_CNT20 0xf39f00ff
+#define R0900_P2_ERRCNT20 0xf39f
+#define F0900_P2_ERR_CNT20 0xf39f00ff
/*P2_FECSPY*/
-#define R0900_P2_FECSPY 0xf3a0
-#define F0900_P2_SPY_ENABLE 0xf3a00080
-#define F0900_P2_NO_SYNCBYTE 0xf3a00040
-#define F0900_P2_SERIAL_MODE 0xf3a00020
-#define F0900_P2_UNUSUAL_PACKET 0xf3a00010
-#define F0900_P2_BER_PACKMODE 0xf3a00008
-#define F0900_P2_BERMETER_LMODE 0xf3a00002
-#define F0900_P2_BERMETER_RESET 0xf3a00001
+#define R0900_P2_FECSPY 0xf3a0
+#define F0900_P2_SPY_ENABLE 0xf3a00080
+#define F0900_P2_NO_SYNCBYTE 0xf3a00040
+#define F0900_P2_SERIAL_MODE 0xf3a00020
+#define F0900_P2_UNUSUAL_PACKET 0xf3a00010
+#define F0900_P2_BERMETER_DATAMODE 0xf3a00008
+#define F0900_P2_BERMETER_LMODE 0xf3a00002
+#define F0900_P2_BERMETER_RESET 0xf3a00001
/*P2_FSPYCFG*/
-#define R0900_P2_FSPYCFG 0xf3a1
-#define F0900_P2_FECSPY_INPUT 0xf3a100c0
-#define F0900_P2_RST_ON_ERROR 0xf3a10020
-#define F0900_P2_ONE_SHOT 0xf3a10010
-#define F0900_P2_I2C_MODE 0xf3a1000c
-#define F0900_P2_SPY_HYSTERESIS 0xf3a10003
+#define R0900_P2_FSPYCFG 0xf3a1
+#define F0900_P2_FECSPY_INPUT 0xf3a100c0
+#define F0900_P2_RST_ON_ERROR 0xf3a10020
+#define F0900_P2_ONE_SHOT 0xf3a10010
+#define F0900_P2_I2C_MODE 0xf3a1000c
+#define F0900_P2_SPY_HYSTERESIS 0xf3a10003
/*P2_FSPYDATA*/
-#define R0900_P2_FSPYDATA 0xf3a2
-#define F0900_P2_SPY_STUFFING 0xf3a20080
-#define F0900_P2_NOERROR_PKTJITTER 0xf3a20040
-#define F0900_P2_SPY_CNULLPKT 0xf3a20020
-#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f
+#define R0900_P2_FSPYDATA 0xf3a2
+#define F0900_P2_SPY_STUFFING 0xf3a20080
+#define F0900_P2_SPY_CNULLPKT 0xf3a20020
+#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f
/*P2_FSPYOUT*/
-#define R0900_P2_FSPYOUT 0xf3a3
-#define F0900_P2_FSPY_DIRECT 0xf3a30080
-#define F0900_P2_SPY_OUTDATA_BUS 0xf3a30038
-#define F0900_P2_STUFF_MODE 0xf3a30007
+#define R0900_P2_FSPYOUT 0xf3a3
+#define F0900_P2_FSPY_DIRECT 0xf3a30080
+#define F0900_P2_STUFF_MODE 0xf3a30007
/*P2_FSTATUS*/
-#define R0900_P2_FSTATUS 0xf3a4
-#define F0900_P2_SPY_ENDSIM 0xf3a40080
-#define F0900_P2_VALID_SIM 0xf3a40040
-#define F0900_P2_FOUND_SIGNAL 0xf3a40020
-#define F0900_P2_DSS_SYNCBYTE 0xf3a40010
-#define F0900_P2_RESULT_STATE 0xf3a4000f
+#define R0900_P2_FSTATUS 0xf3a4
+#define F0900_P2_SPY_ENDSIM 0xf3a40080
+#define F0900_P2_VALID_SIM 0xf3a40040
+#define F0900_P2_FOUND_SIGNAL 0xf3a40020
+#define F0900_P2_DSS_SYNCBYTE 0xf3a40010
+#define F0900_P2_RESULT_STATE 0xf3a4000f
/*P2_FBERCPT4*/
-#define R0900_P2_FBERCPT4 0xf3a8
-#define F0900_P2_FBERMETER_CPT4 0xf3a800ff
+#define R0900_P2_FBERCPT4 0xf3a8
+#define F0900_P2_FBERMETER_CPT4 0xf3a800ff
/*P2_FBERCPT3*/
-#define R0900_P2_FBERCPT3 0xf3a9
-#define F0900_P2_FBERMETER_CPT3 0xf3a900ff
+#define R0900_P2_FBERCPT3 0xf3a9
+#define F0900_P2_FBERMETER_CPT3 0xf3a900ff
/*P2_FBERCPT2*/
-#define R0900_P2_FBERCPT2 0xf3aa
-#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff
+#define R0900_P2_FBERCPT2 0xf3aa
+#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff
/*P2_FBERCPT1*/
-#define R0900_P2_FBERCPT1 0xf3ab
-#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff
+#define R0900_P2_FBERCPT1 0xf3ab
+#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff
/*P2_FBERCPT0*/
-#define R0900_P2_FBERCPT0 0xf3ac
-#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff
+#define R0900_P2_FBERCPT0 0xf3ac
+#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff
/*P2_FBERERR2*/
-#define R0900_P2_FBERERR2 0xf3ad
-#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff
+#define R0900_P2_FBERERR2 0xf3ad
+#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff
/*P2_FBERERR1*/
-#define R0900_P2_FBERERR1 0xf3ae
-#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff
+#define R0900_P2_FBERERR1 0xf3ae
+#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff
/*P2_FBERERR0*/
-#define R0900_P2_FBERERR0 0xf3af
-#define F0900_P2_FBERMETER_ERR0 0xf3af00ff
+#define R0900_P2_FBERERR0 0xf3af
+#define F0900_P2_FBERMETER_ERR0 0xf3af00ff
/*P2_FSPYBER*/
-#define R0900_P2_FSPYBER 0xf3b2
-#define F0900_P2_FSPYOBS_XORREAD 0xf3b20040
-#define F0900_P2_FSPYBER_OBSMODE 0xf3b20020
-#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010
-#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008
-#define F0900_P2_FSPYBER_CTIME 0xf3b20007
+#define R0900_P2_FSPYBER 0xf3b2
+#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010
+#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008
+#define F0900_P2_FSPYBER_CTIME 0xf3b20007
/*P1_IQCONST*/
-#define R0900_P1_IQCONST 0xf400
-#define F0900_P1_CONSTEL_SELECT 0xf4000060
-#define F0900_P1_IQSYMB_SEL 0xf400001f
+#define R0900_P1_IQCONST 0xf400
+#define IQCONST REGx(R0900_P1_IQCONST)
+#define F0900_P1_CONSTEL_SELECT 0xf4000060
+#define F0900_P1_IQSYMB_SEL 0xf400001f
/*P1_NOSCFG*/
-#define R0900_P1_NOSCFG 0xf401
-#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020
-#define F0900_P1_NOSPLH_BETA 0xf4010018
-#define F0900_P1_NOSDATA_BETA 0xf4010007
+#define R0900_P1_NOSCFG 0xf401
+#define NOSCFG REGx(R0900_P1_NOSCFG)
+#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020
+#define F0900_P1_NOSPLH_BETA 0xf4010018
+#define F0900_P1_NOSDATA_BETA 0xf4010007
/*P1_ISYMB*/
-#define R0900_P1_ISYMB 0xf402
-#define F0900_P1_I_SYMBOL 0xf40201ff
+#define R0900_P1_ISYMB 0xf402
+#define ISYMB REGx(R0900_P1_ISYMB)
+#define F0900_P1_I_SYMBOL 0xf40201ff
/*P1_QSYMB*/
-#define R0900_P1_QSYMB 0xf403
-#define F0900_P1_Q_SYMBOL 0xf40301ff
+#define R0900_P1_QSYMB 0xf403
+#define QSYMB REGx(R0900_P1_QSYMB)
+#define F0900_P1_Q_SYMBOL 0xf40301ff
/*P1_AGC1CFG*/
-#define R0900_P1_AGC1CFG 0xf404
-#define F0900_P1_DC_FROZEN 0xf4040080
-#define F0900_P1_DC_CORRECT 0xf4040040
-#define F0900_P1_AMM_FROZEN 0xf4040020
-#define F0900_P1_AMM_CORRECT 0xf4040010
-#define F0900_P1_QUAD_FROZEN 0xf4040008
-#define F0900_P1_QUAD_CORRECT 0xf4040004
-#define F0900_P1_DCCOMP_SLOW 0xf4040002
-#define F0900_P1_IQMISM_SLOW 0xf4040001
+#define R0900_P1_AGC1CFG 0xf404
+#define AGC1CFG REGx(R0900_P1_AGC1CFG)
+#define F0900_P1_DC_FROZEN 0xf4040080
+#define F0900_P1_DC_CORRECT 0xf4040040
+#define F0900_P1_AMM_FROZEN 0xf4040020
+#define F0900_P1_AMM_CORRECT 0xf4040010
+#define F0900_P1_QUAD_FROZEN 0xf4040008
+#define F0900_P1_QUAD_CORRECT 0xf4040004
/*P1_AGC1CN*/
-#define R0900_P1_AGC1CN 0xf406
-#define F0900_P1_AGC1_LOCKED 0xf4060080
-#define F0900_P1_AGC1_OVERFLOW 0xf4060040
-#define F0900_P1_AGC1_NOSLOWLK 0xf4060020
-#define F0900_P1_AGC1_MINPOWER 0xf4060010
-#define F0900_P1_AGCOUT_FAST 0xf4060008
-#define F0900_P1_AGCIQ_BETA 0xf4060007
+#define R0900_P1_AGC1CN 0xf406
+#define AGC1CN REGx(R0900_P1_AGC1CN)
+#define F0900_P1_AGC1_LOCKED 0xf4060080
+#define F0900_P1_AGC1_MINPOWER 0xf4060010
+#define F0900_P1_AGCOUT_FAST 0xf4060008
+#define F0900_P1_AGCIQ_BETA 0xf4060007
/*P1_AGC1REF*/
-#define R0900_P1_AGC1REF 0xf407
-#define F0900_P1_AGCIQ_REF 0xf40700ff
+#define R0900_P1_AGC1REF 0xf407
+#define AGC1REF REGx(R0900_P1_AGC1REF)
+#define F0900_P1_AGCIQ_REF 0xf40700ff
/*P1_IDCCOMP*/
-#define R0900_P1_IDCCOMP 0xf408
-#define F0900_P1_IAVERAGE_ADJ 0xf40801ff
+#define R0900_P1_IDCCOMP 0xf408
+#define IDCCOMP REGx(R0900_P1_IDCCOMP)
+#define F0900_P1_IAVERAGE_ADJ 0xf40801ff
/*P1_QDCCOMP*/
-#define R0900_P1_QDCCOMP 0xf409
-#define F0900_P1_QAVERAGE_ADJ 0xf40901ff
+#define R0900_P1_QDCCOMP 0xf409
+#define QDCCOMP REGx(R0900_P1_QDCCOMP)
+#define F0900_P1_QAVERAGE_ADJ 0xf40901ff
/*P1_POWERI*/
-#define R0900_P1_POWERI 0xf40a
-#define F0900_P1_POWER_I 0xf40a00ff
+#define R0900_P1_POWERI 0xf40a
+#define POWERI REGx(R0900_P1_POWERI)
+#define F0900_P1_POWER_I 0xf40a00ff
+#define POWER_I FLDx(F0900_P1_POWER_I)
/*P1_POWERQ*/
-#define R0900_P1_POWERQ 0xf40b
-#define F0900_P1_POWER_Q 0xf40b00ff
+#define R0900_P1_POWERQ 0xf40b
+#define POWERQ REGx(R0900_P1_POWERQ)
+#define F0900_P1_POWER_Q 0xf40b00ff
+#define POWER_Q FLDx(F0900_P1_POWER_Q)
/*P1_AGC1AMM*/
-#define R0900_P1_AGC1AMM 0xf40c
-#define F0900_P1_AMM_VALUE 0xf40c00ff
+#define R0900_P1_AGC1AMM 0xf40c
+#define AGC1AMM REGx(R0900_P1_AGC1AMM)
+#define F0900_P1_AMM_VALUE 0xf40c00ff
/*P1_AGC1QUAD*/
-#define R0900_P1_AGC1QUAD 0xf40d
-#define F0900_P1_QUAD_VALUE 0xf40d01ff
+#define R0900_P1_AGC1QUAD 0xf40d
+#define AGC1QUAD REGx(R0900_P1_AGC1QUAD)
+#define F0900_P1_QUAD_VALUE 0xf40d01ff
/*P1_AGCIQIN1*/
-#define R0900_P1_AGCIQIN1 0xf40e
-#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff
+#define R0900_P1_AGCIQIN1 0xf40e
+#define AGCIQIN1 REGx(R0900_P1_AGCIQIN1)
+#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff
+#define AGCIQ_VALUE1 FLDx(F0900_P1_AGCIQ_VALUE1)
/*P1_AGCIQIN0*/
-#define R0900_P1_AGCIQIN0 0xf40f
-#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff
+#define R0900_P1_AGCIQIN0 0xf40f
+#define AGCIQIN0 REGx(R0900_P1_AGCIQIN0)
+#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff
+#define AGCIQ_VALUE0 FLDx(F0900_P1_AGCIQ_VALUE0)
/*P1_DEMOD*/
-#define R0900_P1_DEMOD 0xf410
-#define F0900_P1_DEMOD_STOP 0xf4100040
-#define F0900_P1_SPECINV_CONTROL 0xf4100030
-#define F0900_P1_FORCE_ENASAMP 0xf4100008
-#define F0900_P1_MANUAL_ROLLOFF 0xf4100004
-#define F0900_P1_ROLLOFF_CONTROL 0xf4100003
+#define R0900_P1_DEMOD 0xf410
+#define DEMOD REGx(R0900_P1_DEMOD)
+#define F0900_P1_MANUALS2_ROLLOFF 0xf4100080
+#define MANUALS2_ROLLOFF FLDx(F0900_P1_MANUALS2_ROLLOFF)
+
+#define F0900_P1_SPECINV_CONTROL 0xf4100030
+#define SPECINV_CONTROL FLDx(F0900_P1_SPECINV_CONTROL)
+#define F0900_P1_FORCE_ENASAMP 0xf4100008
+#define F0900_P1_MANUALSX_ROLLOFF 0xf4100004
+#define MANUALSX_ROLLOFF FLDx(F0900_P1_MANUALSX_ROLLOFF)
+#define F0900_P1_ROLLOFF_CONTROL 0xf4100003
+#define ROLLOFF_CONTROL FLDx(F0900_P1_ROLLOFF_CONTROL)
/*P1_DMDMODCOD*/
-#define R0900_P1_DMDMODCOD 0xf411
-#define F0900_P1_MANUAL_MODCOD 0xf4110080
-#define F0900_P1_DEMOD_MODCOD 0xf411007c
-#define F0900_P1_DEMOD_TYPE 0xf4110003
+#define R0900_P1_DMDMODCOD 0xf411
+#define DMDMODCOD REGx(R0900_P1_DMDMODCOD)
+#define F0900_P1_MANUAL_MODCOD 0xf4110080
+#define F0900_P1_DEMOD_MODCOD 0xf411007c
+#define DEMOD_MODCOD FLDx(F0900_P1_DEMOD_MODCOD)
+#define F0900_P1_DEMOD_TYPE 0xf4110003
+#define DEMOD_TYPE FLDx(F0900_P1_DEMOD_TYPE)
/*P1_DSTATUS*/
-#define R0900_P1_DSTATUS 0xf412
-#define F0900_P1_CAR_LOCK 0xf4120080
-#define F0900_P1_TMGLOCK_QUALITY 0xf4120060
-#define F0900_P1_SDVBS1_ENABLE 0xf4120010
-#define F0900_P1_LOCK_DEFINITIF 0xf4120008
-#define F0900_P1_TIMING_IS_LOCKED 0xf4120004
-#define F0900_P1_COARSE_TMGLOCK 0xf4120002
-#define F0900_P1_COARSE_CARLOCK 0xf4120001
+#define R0900_P1_DSTATUS 0xf412
+#define DSTATUS REGx(R0900_P1_DSTATUS)
+#define F0900_P1_CAR_LOCK 0xf4120080
+#define F0900_P1_TMGLOCK_QUALITY 0xf4120060
+#define TMGLOCK_QUALITY FLDx(F0900_P1_TMGLOCK_QUALITY)
+#define F0900_P1_LOCK_DEFINITIF 0xf4120008
+#define LOCK_DEFINITIF FLDx(F0900_P1_LOCK_DEFINITIF)
+#define F0900_P1_OVADC_DETECT 0xf4120001
/*P1_DSTATUS2*/
-#define R0900_P1_DSTATUS2 0xf413
-#define F0900_P1_DEMOD_DELOCK 0xf4130080
-#define F0900_P1_DEMOD_TIMEOUT 0xf4130040
-#define F0900_P1_MODCODRQ_SYNCTAG 0xf4130020
-#define F0900_P1_POLYPH_SATEVENT 0xf4130010
-#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008
-#define F0900_P1_AGC2_OVERFLOW 0xf4130004
-#define F0900_P1_CFR_OVERFLOW 0xf4130002
-#define F0900_P1_GAMMA_OVERUNDER 0xf4130001
+#define R0900_P1_DSTATUS2 0xf413
+#define DSTATUS2 REGx(R0900_P1_DSTATUS2)
+#define F0900_P1_DEMOD_DELOCK 0xf4130080
+#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008
+#define F0900_P1_AGC2_OVERFLOW 0xf4130004
+#define F0900_P1_CFR_OVERFLOW 0xf4130002
+#define F0900_P1_GAMMA_OVERUNDER 0xf4130001
/*P1_DMDCFGMD*/
-#define R0900_P1_DMDCFGMD 0xf414
-#define F0900_P1_DVBS2_ENABLE 0xf4140080
-#define F0900_P1_DVBS1_ENABLE 0xf4140040
-#define F0900_P1_CFR_AUTOSCAN 0xf4140020
-#define F0900_P1_SCAN_ENABLE 0xf4140010
-#define F0900_P1_TUN_AUTOSCAN 0xf4140008
-#define F0900_P1_NOFORCE_RELOCK 0xf4140004
-#define F0900_P1_TUN_RNG 0xf4140003
+#define R0900_P1_DMDCFGMD 0xf414
+#define DMDCFGMD REGx(R0900_P1_DMDCFGMD)
+#define F0900_P1_DVBS2_ENABLE 0xf4140080
+#define DVBS2_ENABLE FLDx(F0900_P1_DVBS2_ENABLE)
+#define F0900_P1_DVBS1_ENABLE 0xf4140040
+#define DVBS1_ENABLE FLDx(F0900_P1_DVBS1_ENABLE)
+#define F0900_P1_SCAN_ENABLE 0xf4140010
+#define SCAN_ENABLE FLDx(F0900_P1_SCAN_ENABLE)
+#define F0900_P1_CFR_AUTOSCAN 0xf4140008
+#define CFR_AUTOSCAN FLDx(F0900_P1_CFR_AUTOSCAN)
+#define F0900_P1_TUN_RNG 0xf4140003
/*P1_DMDCFG2*/
-#define R0900_P1_DMDCFG2 0xf415
-#define F0900_P1_AGC1_WAITLOCK 0xf4150080
-#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040
-#define F0900_P1_OVERFLOW_TIMEOUT 0xf4150020
-#define F0900_P1_SCANFAIL_TIMEOUT 0xf4150010
-#define F0900_P1_DMDTOUT_BACK 0xf4150008
-#define F0900_P1_CARLOCK_S1ENABLE 0xf4150004
-#define F0900_P1_COARSE_LK3MODE 0xf4150002
-#define F0900_P1_COARSE_LK2MODE 0xf4150001
+#define R0900_P1_DMDCFG2 0xf415
+#define DMDCFG2 REGx(R0900_P1_DMDCFG2)
+#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040
+#define S1S2_SEQUENTIAL FLDx(F0900_P1_S1S2_SEQUENTIAL)
+#define F0900_P1_INFINITE_RELOCK 0xf4150010
/*P1_DMDISTATE*/
-#define R0900_P1_DMDISTATE 0xf416
-#define F0900_P1_I2C_NORESETDMODE 0xf4160080
-#define F0900_P1_FORCE_ETAPED 0xf4160040
-#define F0900_P1_SDMDRST_DIRCLK 0xf4160020
-#define F0900_P1_I2C_DEMOD_MODE 0xf416001f
+#define R0900_P1_DMDISTATE 0xf416
+#define DMDISTATE REGx(R0900_P1_DMDISTATE)
+#define F0900_P1_I2C_DEMOD_MODE 0xf416001f
+#define DEMOD_MODE FLDx(F0900_P1_I2C_DEMOD_MODE)
/*P1_DMDT0M*/
-#define R0900_P1_DMDT0M 0xf417
-#define F0900_P1_DMDT0_MIN 0xf41700ff
+#define R0900_P1_DMDT0M 0xf417
+#define DMDT0M REGx(R0900_P1_DMDT0M)
+#define F0900_P1_DMDT0_MIN 0xf41700ff
/*P1_DMDSTATE*/
-#define R0900_P1_DMDSTATE 0xf41b
-#define F0900_P1_DEMOD_LOCKED 0xf41b0080
-#define F0900_P1_HEADER_MODE 0xf41b0060
-#define F0900_P1_DEMOD_MODE 0xf41b001f
+#define R0900_P1_DMDSTATE 0xf41b
+#define DMDSTATE REGx(R0900_P1_DMDSTATE)
+#define F0900_P1_HEADER_MODE 0xf41b0060
+#define HEADER_MODE FLDx(F0900_P1_HEADER_MODE)
/*P1_DMDFLYW*/
-#define R0900_P1_DMDFLYW 0xf41c
-#define F0900_P1_I2C_IRQVAL 0xf41c00f0
-#define F0900_P1_FLYWHEEL_CPT 0xf41c000f
+#define R0900_P1_DMDFLYW 0xf41c
+#define DMDFLYW REGx(R0900_P1_DMDFLYW)
+#define F0900_P1_I2C_IRQVAL 0xf41c00f0
+#define F0900_P1_FLYWHEEL_CPT 0xf41c000f
+#define FLYWHEEL_CPT FLDx(F0900_P1_FLYWHEEL_CPT)
/*P1_DSTATUS3*/
-#define R0900_P1_DSTATUS3 0xf41d
-#define F0900_P1_CFR_ZIGZAG 0xf41d0080
-#define F0900_P1_DEMOD_CFGMODE 0xf41d0060
-#define F0900_P1_GAMMA_LOWBAUDRATE 0xf41d0010
-#define F0900_P1_RELOCK_MODE 0xf41d0008
-#define F0900_P1_DEMOD_FAIL 0xf41d0004
-#define F0900_P1_ETAPE1A_DVBXMEM 0xf41d0003
+#define R0900_P1_DSTATUS3 0xf41d
+#define DSTATUS3 REGx(R0900_P1_DSTATUS3)
+#define F0900_P1_DEMOD_CFGMODE 0xf41d0060
/*P1_DMDCFG3*/
-#define R0900_P1_DMDCFG3 0xf41e
-#define F0900_P1_DVBS1_TMGWAIT 0xf41e0080
-#define F0900_P1_NO_BWCENTERING 0xf41e0040
-#define F0900_P1_INV_SEQSRCH 0xf41e0020
-#define F0900_P1_DIS_SFRUPLOW_TRK 0xf41e0010
-#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008
-#define F0900_P1_LOCKTIME_MODE 0xf41e0007
+#define R0900_P1_DMDCFG3 0xf41e
+#define DMDCFG3 REGx(R0900_P1_DMDCFG3)
+#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008
/*P1_DMDCFG4*/
-#define R0900_P1_DMDCFG4 0xf41f
-#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008
-#define F0900_P1_DIS_CLKENABLE 0xf41f0004
-#define F0900_P1_DIS_HDRDIVLOCK 0xf41f0002
-#define F0900_P1_NO_TNRWBINIT 0xf41f0001
+#define R0900_P1_DMDCFG4 0xf41f
+#define DMDCFG4 REGx(R0900_P1_DMDCFG4)
+#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008
/*P1_CORRELMANT*/
-#define R0900_P1_CORRELMANT 0xf420
-#define F0900_P1_CORREL_MANT 0xf42000ff
+#define R0900_P1_CORRELMANT 0xf420
+#define CORRELMANT REGx(R0900_P1_CORRELMANT)
+#define F0900_P1_CORREL_MANT 0xf42000ff
/*P1_CORRELABS*/
-#define R0900_P1_CORRELABS 0xf421
-#define F0900_P1_CORREL_ABS 0xf42100ff
+#define R0900_P1_CORRELABS 0xf421
+#define CORRELABS REGx(R0900_P1_CORRELABS)
+#define F0900_P1_CORREL_ABS 0xf42100ff
/*P1_CORRELEXP*/
-#define R0900_P1_CORRELEXP 0xf422
-#define F0900_P1_CORREL_ABSEXP 0xf42200f0
-#define F0900_P1_CORREL_EXP 0xf422000f
+#define R0900_P1_CORRELEXP 0xf422
+#define CORRELEXP REGx(R0900_P1_CORRELEXP)
+#define F0900_P1_CORREL_ABSEXP 0xf42200f0
+#define F0900_P1_CORREL_EXP 0xf422000f
/*P1_PLHMODCOD*/
-#define R0900_P1_PLHMODCOD 0xf424
-#define F0900_P1_SPECINV_DEMOD 0xf4240080
-#define F0900_P1_PLH_MODCOD 0xf424007c
-#define F0900_P1_PLH_TYPE 0xf4240003
-
-/*P1_AGCK32*/
-#define R0900_P1_AGCK32 0xf42b
-#define F0900_P1_R3ADJOFF_32APSK 0xf42b0080
-#define F0900_P1_R2ADJOFF_32APSK 0xf42b0040
-#define F0900_P1_R1ADJOFF_32APSK 0xf42b0020
-#define F0900_P1_RADJ_32APSK 0xf42b001f
+#define R0900_P1_PLHMODCOD 0xf424
+#define PLHMODCOD REGx(R0900_P1_PLHMODCOD)
+#define F0900_P1_SPECINV_DEMOD 0xf4240080
+#define SPECINV_DEMOD FLDx(F0900_P1_SPECINV_DEMOD)
+#define F0900_P1_PLH_MODCOD 0xf424007c
+#define F0900_P1_PLH_TYPE 0xf4240003
+
+/*P1_DMDREG*/
+#define R0900_P1_DMDREG 0xf425
+#define DMDREG REGx(R0900_P1_DMDREG)
+#define F0900_P1_DECIM_PLFRAMES 0xf4250001
/*P1_AGC2O*/
-#define R0900_P1_AGC2O 0xf42c
-#define F0900_P1_AGC2REF_ADJUSTING 0xf42c0080
-#define F0900_P1_AGC2_COARSEFAST 0xf42c0040
-#define F0900_P1_AGC2_LKSQRT 0xf42c0020
-#define F0900_P1_AGC2_LKMODE 0xf42c0010
-#define F0900_P1_AGC2_LKEQUA 0xf42c0008
-#define F0900_P1_AGC2_COEF 0xf42c0007
+#define R0900_P1_AGC2O 0xf42c
+#define AGC2O REGx(R0900_P1_AGC2O)
+#define F0900_P1_AGC2_COEF 0xf42c0007
/*P1_AGC2REF*/
-#define R0900_P1_AGC2REF 0xf42d
-#define F0900_P1_AGC2_REF 0xf42d00ff
+#define R0900_P1_AGC2REF 0xf42d
+#define AGC2REF REGx(R0900_P1_AGC2REF)
+#define F0900_P1_AGC2_REF 0xf42d00ff
/*P1_AGC1ADJ*/
-#define R0900_P1_AGC1ADJ 0xf42e
-#define F0900_P1_AGC1ADJ_MANUAL 0xf42e0080
-#define F0900_P1_AGC1_ADJUSTED 0xf42e017f
+#define R0900_P1_AGC1ADJ 0xf42e
+#define AGC1ADJ REGx(R0900_P1_AGC1ADJ)
+#define F0900_P1_AGC1_ADJUSTED 0xf42e007f
/*P1_AGC2I1*/
-#define R0900_P1_AGC2I1 0xf436
-#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff
+#define R0900_P1_AGC2I1 0xf436
+#define AGC2I1 REGx(R0900_P1_AGC2I1)
+#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff
/*P1_AGC2I0*/
-#define R0900_P1_AGC2I0 0xf437
-#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff
+#define R0900_P1_AGC2I0 0xf437
+#define AGC2I0 REGx(R0900_P1_AGC2I0)
+#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff
/*P1_CARCFG*/
-#define R0900_P1_CARCFG 0xf438
-#define F0900_P1_CFRUPLOW_AUTO 0xf4380080
-#define F0900_P1_CFRUPLOW_TEST 0xf4380040
-#define F0900_P1_EN_CAR2CENTER 0xf4380020
-#define F0900_P1_CARHDR_NODIV8 0xf4380010
-#define F0900_P1_I2C_ROTA 0xf4380008
-#define F0900_P1_ROTAON 0xf4380004
-#define F0900_P1_PH_DET_ALGO 0xf4380003
+#define R0900_P1_CARCFG 0xf438
+#define CARCFG REGx(R0900_P1_CARCFG)
+#define F0900_P1_CFRUPLOW_AUTO 0xf4380080
+#define F0900_P1_CFRUPLOW_TEST 0xf4380040
+#define F0900_P1_ROTAON 0xf4380004
+#define F0900_P1_PH_DET_ALGO 0xf4380003
/*P1_ACLC*/
-#define R0900_P1_ACLC 0xf439
-#define F0900_P1_STOP_S2ALPHA 0xf43900c0
-#define F0900_P1_CAR_ALPHA_MANT 0xf4390030
-#define F0900_P1_CAR_ALPHA_EXP 0xf439000f
+#define R0900_P1_ACLC 0xf439
+#define ACLC REGx(R0900_P1_ACLC)
+#define F0900_P1_CAR_ALPHA_MANT 0xf4390030
+#define F0900_P1_CAR_ALPHA_EXP 0xf439000f
/*P1_BCLC*/
-#define R0900_P1_BCLC 0xf43a
-#define F0900_P1_STOP_S2BETA 0xf43a00c0
-#define F0900_P1_CAR_BETA_MANT 0xf43a0030
-#define F0900_P1_CAR_BETA_EXP 0xf43a000f
+#define R0900_P1_BCLC 0xf43a
+#define BCLC REGx(R0900_P1_BCLC)
+#define F0900_P1_CAR_BETA_MANT 0xf43a0030
+#define F0900_P1_CAR_BETA_EXP 0xf43a000f
/*P1_CARFREQ*/
-#define R0900_P1_CARFREQ 0xf43d
-#define F0900_P1_KC_COARSE_EXP 0xf43d00f0
-#define F0900_P1_BETA_FREQ 0xf43d000f
+#define R0900_P1_CARFREQ 0xf43d
+#define CARFREQ REGx(R0900_P1_CARFREQ)
+#define F0900_P1_KC_COARSE_EXP 0xf43d00f0
+#define F0900_P1_BETA_FREQ 0xf43d000f
/*P1_CARHDR*/
-#define R0900_P1_CARHDR 0xf43e
-#define F0900_P1_K_FREQ_HDR 0xf43e00ff
+#define R0900_P1_CARHDR 0xf43e
+#define CARHDR REGx(R0900_P1_CARHDR)
+#define F0900_P1_K_FREQ_HDR 0xf43e00ff
/*P1_LDT*/
-#define R0900_P1_LDT 0xf43f
-#define F0900_P1_CARLOCK_THRES 0xf43f01ff
+#define R0900_P1_LDT 0xf43f
+#define LDT REGx(R0900_P1_LDT)
+#define F0900_P1_CARLOCK_THRES 0xf43f01ff
/*P1_LDT2*/
-#define R0900_P1_LDT2 0xf440
-#define F0900_P1_CARLOCK_THRES2 0xf44001ff
+#define R0900_P1_LDT2 0xf440
+#define LDT2 REGx(R0900_P1_LDT2)
+#define F0900_P1_CARLOCK_THRES2 0xf44001ff
/*P1_CFRICFG*/
-#define R0900_P1_CFRICFG 0xf441
-#define F0900_P1_CFRINIT_UNVALRNG 0xf4410080
-#define F0900_P1_CFRINIT_LUNVALCPT 0xf4410040
-#define F0900_P1_CFRINIT_ABORTDBL 0xf4410020
-#define F0900_P1_CFRINIT_ABORTPRED 0xf4410010
-#define F0900_P1_CFRINIT_UNVALSKIP 0xf4410008
-#define F0900_P1_CFRINIT_CSTINC 0xf4410004
-#define F0900_P1_NEG_CFRSTEP 0xf4410001
+#define R0900_P1_CFRICFG 0xf441
+#define CFRICFG REGx(R0900_P1_CFRICFG)
+#define F0900_P1_NEG_CFRSTEP 0xf4410001
/*P1_CFRUP1*/
-#define R0900_P1_CFRUP1 0xf442
-#define F0900_P1_CFR_UP1 0xf44201ff
+#define R0900_P1_CFRUP1 0xf442
+#define CFRUP1 REGx(R0900_P1_CFRUP1)
+#define F0900_P1_CFR_UP1 0xf44201ff
+#define CFR_UP1 FLDx(F0900_P1_CFR_UP1)
/*P1_CFRUP0*/
-#define R0900_P1_CFRUP0 0xf443
-#define F0900_P1_CFR_UP0 0xf44300ff
+#define R0900_P1_CFRUP0 0xf443
+#define CFRUP0 REGx(R0900_P1_CFRUP0)
+#define F0900_P1_CFR_UP0 0xf44300ff
+#define CFR_UP0 FLDx(F0900_P1_CFR_UP0)
/*P1_CFRLOW1*/
-#define R0900_P1_CFRLOW1 0xf446
-#define F0900_P1_CFR_LOW1 0xf44601ff
+#define R0900_P1_CFRLOW1 0xf446
+#define CFRLOW1 REGx(R0900_P1_CFRLOW1)
+#define F0900_P1_CFR_LOW1 0xf44601ff
+#define CFR_LOW1 FLDx(F0900_P1_CFR_LOW1)
/*P1_CFRLOW0*/
-#define R0900_P1_CFRLOW0 0xf447
-#define F0900_P1_CFR_LOW0 0xf44700ff
+#define R0900_P1_CFRLOW0 0xf447
+#define CFRLOW0 REGx(R0900_P1_CFRLOW0)
+#define F0900_P1_CFR_LOW0 0xf44700ff
+#define CFR_LOW0 FLDx(F0900_P1_CFR_LOW0)
/*P1_CFRINIT1*/
-#define R0900_P1_CFRINIT1 0xf448
-#define F0900_P1_CFR_INIT1 0xf44801ff
+#define R0900_P1_CFRINIT1 0xf448
+#define CFRINIT1 REGx(R0900_P1_CFRINIT1)
+#define F0900_P1_CFR_INIT1 0xf44801ff
+#define CFR_INIT1 FLDx(F0900_P1_CFR_INIT1)
/*P1_CFRINIT0*/
-#define R0900_P1_CFRINIT0 0xf449
-#define F0900_P1_CFR_INIT0 0xf44900ff
+#define R0900_P1_CFRINIT0 0xf449
+#define CFRINIT0 REGx(R0900_P1_CFRINIT0)
+#define F0900_P1_CFR_INIT0 0xf44900ff
+#define CFR_INIT0 FLDx(F0900_P1_CFR_INIT0)
/*P1_CFRINC1*/
-#define R0900_P1_CFRINC1 0xf44a
-#define F0900_P1_MANUAL_CFRINC 0xf44a0080
-#define F0900_P1_CFR_INC1 0xf44a017f
+#define R0900_P1_CFRINC1 0xf44a
+#define CFRINC1 REGx(R0900_P1_CFRINC1)
+#define F0900_P1_MANUAL_CFRINC 0xf44a0080
+#define F0900_P1_CFR_INC1 0xf44a003f
/*P1_CFRINC0*/
-#define R0900_P1_CFRINC0 0xf44b
-#define F0900_P1_CFR_INC0 0xf44b00f0
+#define R0900_P1_CFRINC0 0xf44b
+#define CFRINC0 REGx(R0900_P1_CFRINC0)
+#define F0900_P1_CFR_INC0 0xf44b00f8
/*P1_CFR2*/
-#define R0900_P1_CFR2 0xf44c
-#define F0900_P1_CAR_FREQ2 0xf44c01ff
+#define R0900_P1_CFR2 0xf44c
+#define CFR2 REGx(R0900_P1_CFR2)
+#define F0900_P1_CAR_FREQ2 0xf44c01ff
+#define CAR_FREQ2 FLDx(F0900_P1_CAR_FREQ2)
/*P1_CFR1*/
-#define R0900_P1_CFR1 0xf44d
-#define F0900_P1_CAR_FREQ1 0xf44d00ff
+#define R0900_P1_CFR1 0xf44d
+#define CFR1 REGx(R0900_P1_CFR1)
+#define F0900_P1_CAR_FREQ1 0xf44d00ff
+#define CAR_FREQ1 FLDx(F0900_P1_CAR_FREQ1)
/*P1_CFR0*/
-#define R0900_P1_CFR0 0xf44e
-#define F0900_P1_CAR_FREQ0 0xf44e00ff
+#define R0900_P1_CFR0 0xf44e
+#define CFR0 REGx(R0900_P1_CFR0)
+#define F0900_P1_CAR_FREQ0 0xf44e00ff
+#define CAR_FREQ0 FLDx(F0900_P1_CAR_FREQ0)
/*P1_LDI*/
-#define R0900_P1_LDI 0xf44f
-#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff
+#define R0900_P1_LDI 0xf44f
+#define LDI REGx(R0900_P1_LDI)
+#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff
/*P1_TMGCFG*/
-#define R0900_P1_TMGCFG 0xf450
-#define F0900_P1_TMGLOCK_BETA 0xf45000c0
-#define F0900_P1_NOTMG_GROUPDELAY 0xf4500020
-#define F0900_P1_DO_TIMING_CORR 0xf4500010
-#define F0900_P1_MANUAL_SCAN 0xf450000c
-#define F0900_P1_TMG_MINFREQ 0xf4500003
+#define R0900_P1_TMGCFG 0xf450
+#define TMGCFG REGx(R0900_P1_TMGCFG)
+#define F0900_P1_TMGLOCK_BETA 0xf45000c0
+#define F0900_P1_DO_TIMING_CORR 0xf4500010
+#define F0900_P1_TMG_MINFREQ 0xf4500003
/*P1_RTC*/
-#define R0900_P1_RTC 0xf451
-#define F0900_P1_TMGALPHA_EXP 0xf45100f0
-#define F0900_P1_TMGBETA_EXP 0xf451000f
+#define R0900_P1_RTC 0xf451
+#define RTC REGx(R0900_P1_RTC)
+#define F0900_P1_TMGALPHA_EXP 0xf45100f0
+#define F0900_P1_TMGBETA_EXP 0xf451000f
/*P1_RTCS2*/
-#define R0900_P1_RTCS2 0xf452
-#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0
-#define F0900_P1_TMGBETAS2_EXP 0xf452000f
+#define R0900_P1_RTCS2 0xf452
+#define RTCS2 REGx(R0900_P1_RTCS2)
+#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0
+#define F0900_P1_TMGBETAS2_EXP 0xf452000f
/*P1_TMGTHRISE*/
-#define R0900_P1_TMGTHRISE 0xf453
-#define F0900_P1_TMGLOCK_THRISE 0xf45300ff
+#define R0900_P1_TMGTHRISE 0xf453
+#define TMGTHRISE REGx(R0900_P1_TMGTHRISE)
+#define F0900_P1_TMGLOCK_THRISE 0xf45300ff
/*P1_TMGTHFALL*/
-#define R0900_P1_TMGTHFALL 0xf454
-#define F0900_P1_TMGLOCK_THFALL 0xf45400ff
+#define R0900_P1_TMGTHFALL 0xf454
+#define TMGTHFALL REGx(R0900_P1_TMGTHFALL)
+#define F0900_P1_TMGLOCK_THFALL 0xf45400ff
/*P1_SFRUPRATIO*/
-#define R0900_P1_SFRUPRATIO 0xf455
-#define F0900_P1_SFR_UPRATIO 0xf45500ff
+#define R0900_P1_SFRUPRATIO 0xf455
+#define SFRUPRATIO REGx(R0900_P1_SFRUPRATIO)
+#define F0900_P1_SFR_UPRATIO 0xf45500ff
/*P1_SFRLOWRATIO*/
-#define R0900_P1_SFRLOWRATIO 0xf456
-#define F0900_P1_SFR_LOWRATIO 0xf45600ff
+#define R0900_P1_SFRLOWRATIO 0xf456
+#define F0900_P1_SFR_LOWRATIO 0xf45600ff
/*P1_KREFTMG*/
-#define R0900_P1_KREFTMG 0xf458
-#define F0900_P1_KREF_TMG 0xf45800ff
+#define R0900_P1_KREFTMG 0xf458
+#define KREFTMG REGx(R0900_P1_KREFTMG)
+#define F0900_P1_KREF_TMG 0xf45800ff
/*P1_SFRSTEP*/
-#define R0900_P1_SFRSTEP 0xf459
-#define F0900_P1_SFR_SCANSTEP 0xf45900f0
-#define F0900_P1_SFR_CENTERSTEP 0xf459000f
+#define R0900_P1_SFRSTEP 0xf459
+#define SFRSTEP REGx(R0900_P1_SFRSTEP)
+#define F0900_P1_SFR_SCANSTEP 0xf45900f0
+#define F0900_P1_SFR_CENTERSTEP 0xf459000f
/*P1_TMGCFG2*/
-#define R0900_P1_TMGCFG2 0xf45a
-#define F0900_P1_DIS_AUTOSAMP 0xf45a0008
-#define F0900_P1_SCANINIT_QUART 0xf45a0004
-#define F0900_P1_NOTMG_DVBS1DERAT 0xf45a0002
-#define F0900_P1_SFRRATIO_FINE 0xf45a0001
+#define R0900_P1_TMGCFG2 0xf45a
+#define TMGCFG2 REGx(R0900_P1_TMGCFG2)
+#define F0900_P1_SFRRATIO_FINE 0xf45a0001
+
+/*P1_KREFTMG2*/
+#define R0900_P1_KREFTMG2 0xf45b
+#define KREFTMG2 REGx(R0900_P1_KREFTMG2)
+#define F0900_P1_KREF_TMG2 0xf45b00ff
/*P1_SFRINIT1*/
-#define R0900_P1_SFRINIT1 0xf45e
-#define F0900_P1_SFR_INIT1 0xf45e00ff
+#define R0900_P1_SFRINIT1 0xf45e
+#define SFRINIT1 REGx(R0900_P1_SFRINIT1)
+#define F0900_P1_SFR_INIT1 0xf45e007f
/*P1_SFRINIT0*/
-#define R0900_P1_SFRINIT0 0xf45f
-#define F0900_P1_SFR_INIT0 0xf45f00ff
+#define R0900_P1_SFRINIT0 0xf45f
+#define SFRINIT0 REGx(R0900_P1_SFRINIT0)
+#define F0900_P1_SFR_INIT0 0xf45f00ff
/*P1_SFRUP1*/
-#define R0900_P1_SFRUP1 0xf460
-#define F0900_P1_AUTO_GUP 0xf4600080
-#define F0900_P1_SYMB_FREQ_UP1 0xf460007f
+#define R0900_P1_SFRUP1 0xf460
+#define SFRUP1 REGx(R0900_P1_SFRUP1)
+#define F0900_P1_AUTO_GUP 0xf4600080
+#define AUTO_GUP FLDx(F0900_P1_AUTO_GUP)
+#define F0900_P1_SYMB_FREQ_UP1 0xf460007f
/*P1_SFRUP0*/
-#define R0900_P1_SFRUP0 0xf461
-#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff
+#define R0900_P1_SFRUP0 0xf461
+#define SFRUP0 REGx(R0900_P1_SFRUP0)
+#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff
/*P1_SFRLOW1*/
-#define R0900_P1_SFRLOW1 0xf462
-#define F0900_P1_AUTO_GLOW 0xf4620080
-#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f
+#define R0900_P1_SFRLOW1 0xf462
+#define SFRLOW1 REGx(R0900_P1_SFRLOW1)
+#define F0900_P1_AUTO_GLOW 0xf4620080
+#define AUTO_GLOW FLDx(F0900_P1_AUTO_GLOW)
+#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f
/*P1_SFRLOW0*/
-#define R0900_P1_SFRLOW0 0xf463
-#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff
+#define R0900_P1_SFRLOW0 0xf463
+#define SFRLOW0 REGx(R0900_P1_SFRLOW0)
+#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff
/*P1_SFR3*/
-#define R0900_P1_SFR3 0xf464
-#define F0900_P1_SYMB_FREQ3 0xf46400ff
+#define R0900_P1_SFR3 0xf464
+#define SFR3 REGx(R0900_P1_SFR3)
+#define F0900_P1_SYMB_FREQ3 0xf46400ff
+#define SYMB_FREQ3 FLDx(F0900_P1_SYMB_FREQ3)
/*P1_SFR2*/
-#define R0900_P1_SFR2 0xf465
-#define F0900_P1_SYMB_FREQ2 0xf46500ff
+#define R0900_P1_SFR2 0xf465
+#define SFR2 REGx(R0900_P1_SFR2)
+#define F0900_P1_SYMB_FREQ2 0xf46500ff
+#define SYMB_FREQ2 FLDx(F0900_P1_SYMB_FREQ2)
/*P1_SFR1*/
-#define R0900_P1_SFR1 0xf466
-#define F0900_P1_SYMB_FREQ1 0xf46600ff
+#define R0900_P1_SFR1 0xf466
+#define SFR1 REGx(R0900_P1_SFR1)
+#define F0900_P1_SYMB_FREQ1 0xf46600ff
+#define SYMB_FREQ1 FLDx(F0900_P1_SYMB_FREQ1)
/*P1_SFR0*/
-#define R0900_P1_SFR0 0xf467
-#define F0900_P1_SYMB_FREQ0 0xf46700ff
+#define R0900_P1_SFR0 0xf467
+#define SFR0 REGx(R0900_P1_SFR0)
+#define F0900_P1_SYMB_FREQ0 0xf46700ff
+#define SYMB_FREQ0 FLDx(F0900_P1_SYMB_FREQ0)
/*P1_TMGREG2*/
-#define R0900_P1_TMGREG2 0xf468
-#define F0900_P1_TMGREG2 0xf46800ff
+#define R0900_P1_TMGREG2 0xf468
+#define TMGREG2 REGx(R0900_P1_TMGREG2)
+#define F0900_P1_TMGREG2 0xf46800ff
/*P1_TMGREG1*/
-#define R0900_P1_TMGREG1 0xf469
-#define F0900_P1_TMGREG1 0xf46900ff
+#define R0900_P1_TMGREG1 0xf469
+#define TMGREG1 REGx(R0900_P1_TMGREG1)
+#define F0900_P1_TMGREG1 0xf46900ff
/*P1_TMGREG0*/
-#define R0900_P1_TMGREG0 0xf46a
-#define F0900_P1_TMGREG0 0xf46a00ff
+#define R0900_P1_TMGREG0 0xf46a
+#define TMGREG0 REGx(R0900_P1_TMGREG0)
+#define F0900_P1_TMGREG0 0xf46a00ff
/*P1_TMGLOCK1*/
-#define R0900_P1_TMGLOCK1 0xf46b
-#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff
+#define R0900_P1_TMGLOCK1 0xf46b
+#define TMGLOCK1 REGx(R0900_P1_TMGLOCK1)
+#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff
/*P1_TMGLOCK0*/
-#define R0900_P1_TMGLOCK0 0xf46c
-#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff
+#define R0900_P1_TMGLOCK0 0xf46c
+#define TMGLOCK0 REGx(R0900_P1_TMGLOCK0)
+#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff
/*P1_TMGOBS*/
-#define R0900_P1_TMGOBS 0xf46d
-#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0
-#define F0900_P1_SCAN_SIGN 0xf46d0030
-#define F0900_P1_TMG_SCANNING 0xf46d0008
-#define F0900_P1_CHCENTERING_MODE 0xf46d0004
-#define F0900_P1_TMG_SCANFAIL 0xf46d0002
+#define R0900_P1_TMGOBS 0xf46d
+#define TMGOBS REGx(R0900_P1_TMGOBS)
+#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0
+#define ROLLOFF_STATUS FLDx(F0900_P1_ROLLOFF_STATUS)
/*P1_EQUALCFG*/
-#define R0900_P1_EQUALCFG 0xf46f
-#define F0900_P1_NOTMG_NEGALWAIT 0xf46f0080
-#define F0900_P1_EQUAL_ON 0xf46f0040
-#define F0900_P1_SEL_EQUALCOR 0xf46f0038
-#define F0900_P1_MU_EQUALDFE 0xf46f0007
+#define R0900_P1_EQUALCFG 0xf46f
+#define EQUALCFG REGx(R0900_P1_EQUALCFG)
+#define F0900_P1_EQUAL_ON 0xf46f0040
+#define F0900_P1_MU_EQUALDFE 0xf46f0007
/*P1_EQUAI1*/
-#define R0900_P1_EQUAI1 0xf470
-#define F0900_P1_EQUA_ACCI1 0xf47001ff
+#define R0900_P1_EQUAI1 0xf470
+#define EQUAI1 REGx(R0900_P1_EQUAI1)
+#define F0900_P1_EQUA_ACCI1 0xf47001ff
/*P1_EQUAQ1*/
-#define R0900_P1_EQUAQ1 0xf471
-#define F0900_P1_EQUA_ACCQ1 0xf47101ff
+#define R0900_P1_EQUAQ1 0xf471
+#define EQUAQ1 REGx(R0900_P1_EQUAQ1)
+#define F0900_P1_EQUA_ACCQ1 0xf47101ff
/*P1_EQUAI2*/
-#define R0900_P1_EQUAI2 0xf472
-#define F0900_P1_EQUA_ACCI2 0xf47201ff
+#define R0900_P1_EQUAI2 0xf472
+#define EQUAI2 REGx(R0900_P1_EQUAI2)
+#define F0900_P1_EQUA_ACCI2 0xf47201ff
/*P1_EQUAQ2*/
-#define R0900_P1_EQUAQ2 0xf473
-#define F0900_P1_EQUA_ACCQ2 0xf47301ff
+#define R0900_P1_EQUAQ2 0xf473
+#define EQUAQ2 REGx(R0900_P1_EQUAQ2)
+#define F0900_P1_EQUA_ACCQ2 0xf47301ff
/*P1_EQUAI3*/
-#define R0900_P1_EQUAI3 0xf474
-#define F0900_P1_EQUA_ACCI3 0xf47401ff
+#define R0900_P1_EQUAI3 0xf474
+#define EQUAI3 REGx(R0900_P1_EQUAI3)
+#define F0900_P1_EQUA_ACCI3 0xf47401ff
/*P1_EQUAQ3*/
-#define R0900_P1_EQUAQ3 0xf475
-#define F0900_P1_EQUA_ACCQ3 0xf47501ff
+#define R0900_P1_EQUAQ3 0xf475
+#define EQUAQ3 REGx(R0900_P1_EQUAQ3)
+#define F0900_P1_EQUA_ACCQ3 0xf47501ff
/*P1_EQUAI4*/
-#define R0900_P1_EQUAI4 0xf476
-#define F0900_P1_EQUA_ACCI4 0xf47601ff
+#define R0900_P1_EQUAI4 0xf476
+#define EQUAI4 REGx(R0900_P1_EQUAI4)
+#define F0900_P1_EQUA_ACCI4 0xf47601ff
/*P1_EQUAQ4*/
-#define R0900_P1_EQUAQ4 0xf477
-#define F0900_P1_EQUA_ACCQ4 0xf47701ff
+#define R0900_P1_EQUAQ4 0xf477
+#define EQUAQ4 REGx(R0900_P1_EQUAQ4)
+#define F0900_P1_EQUA_ACCQ4 0xf47701ff
/*P1_EQUAI5*/
-#define R0900_P1_EQUAI5 0xf478
-#define F0900_P1_EQUA_ACCI5 0xf47801ff
+#define R0900_P1_EQUAI5 0xf478
+#define EQUAI5 REGx(R0900_P1_EQUAI5)
+#define F0900_P1_EQUA_ACCI5 0xf47801ff
/*P1_EQUAQ5*/
-#define R0900_P1_EQUAQ5 0xf479
-#define F0900_P1_EQUA_ACCQ5 0xf47901ff
+#define R0900_P1_EQUAQ5 0xf479
+#define EQUAQ5 REGx(R0900_P1_EQUAQ5)
+#define F0900_P1_EQUA_ACCQ5 0xf47901ff
/*P1_EQUAI6*/
-#define R0900_P1_EQUAI6 0xf47a
-#define F0900_P1_EQUA_ACCI6 0xf47a01ff
+#define R0900_P1_EQUAI6 0xf47a
+#define EQUAI6 REGx(R0900_P1_EQUAI6)
+#define F0900_P1_EQUA_ACCI6 0xf47a01ff
/*P1_EQUAQ6*/
-#define R0900_P1_EQUAQ6 0xf47b
-#define F0900_P1_EQUA_ACCQ6 0xf47b01ff
+#define R0900_P1_EQUAQ6 0xf47b
+#define EQUAQ6 REGx(R0900_P1_EQUAQ6)
+#define F0900_P1_EQUA_ACCQ6 0xf47b01ff
/*P1_EQUAI7*/
-#define R0900_P1_EQUAI7 0xf47c
-#define F0900_P1_EQUA_ACCI7 0xf47c01ff
+#define R0900_P1_EQUAI7 0xf47c
+#define EQUAI7 REGx(R0900_P1_EQUAI7)
+#define F0900_P1_EQUA_ACCI7 0xf47c01ff
/*P1_EQUAQ7*/
-#define R0900_P1_EQUAQ7 0xf47d
-#define F0900_P1_EQUA_ACCQ7 0xf47d01ff
+#define R0900_P1_EQUAQ7 0xf47d
+#define EQUAQ7 REGx(R0900_P1_EQUAQ7)
+#define F0900_P1_EQUA_ACCQ7 0xf47d01ff
/*P1_EQUAI8*/
-#define R0900_P1_EQUAI8 0xf47e
-#define F0900_P1_EQUA_ACCI8 0xf47e01ff
+#define R0900_P1_EQUAI8 0xf47e
+#define EQUAI8 REGx(R0900_P1_EQUAI8)
+#define F0900_P1_EQUA_ACCI8 0xf47e01ff
/*P1_EQUAQ8*/
-#define R0900_P1_EQUAQ8 0xf47f
-#define F0900_P1_EQUA_ACCQ8 0xf47f01ff
+#define R0900_P1_EQUAQ8 0xf47f
+#define EQUAQ8 REGx(R0900_P1_EQUAQ8)
+#define F0900_P1_EQUA_ACCQ8 0xf47f01ff
/*P1_NNOSDATAT1*/
-#define R0900_P1_NNOSDATAT1 0xf480
-#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff
+#define R0900_P1_NNOSDATAT1 0xf480
+#define NNOSDATAT1 REGx(R0900_P1_NNOSDATAT1)
+#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff
+#define NOSDATAT_NORMED1 FLDx(F0900_P1_NOSDATAT_NORMED1)
/*P1_NNOSDATAT0*/
-#define R0900_P1_NNOSDATAT0 0xf481
-#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff
+#define R0900_P1_NNOSDATAT0 0xf481
+#define NNOSDATAT0 REGx(R0900_P1_NNOSDATAT0)
+#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff
+#define NOSDATAT_NORMED0 FLDx(F0900_P1_NOSDATAT_NORMED0)
/*P1_NNOSDATA1*/
-#define R0900_P1_NNOSDATA1 0xf482
-#define F0900_P1_NOSDATA_NORMED1 0xf48200ff
+#define R0900_P1_NNOSDATA1 0xf482
+#define NNOSDATA1 REGx(R0900_P1_NNOSDATA1)
+#define F0900_P1_NOSDATA_NORMED1 0xf48200ff
/*P1_NNOSDATA0*/
-#define R0900_P1_NNOSDATA0 0xf483
-#define F0900_P1_NOSDATA_NORMED0 0xf48300ff
+#define R0900_P1_NNOSDATA0 0xf483
+#define NNOSDATA0 REGx(R0900_P1_NNOSDATA0)
+#define F0900_P1_NOSDATA_NORMED0 0xf48300ff
/*P1_NNOSPLHT1*/
-#define R0900_P1_NNOSPLHT1 0xf484
-#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff
+#define R0900_P1_NNOSPLHT1 0xf484
+#define NNOSPLHT1 REGx(R0900_P1_NNOSPLHT1)
+#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff
+#define NOSPLHT_NORMED1 FLDx(F0900_P1_NOSPLHT_NORMED1)
/*P1_NNOSPLHT0*/
-#define R0900_P1_NNOSPLHT0 0xf485
-#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff
+#define R0900_P1_NNOSPLHT0 0xf485
+#define NNOSPLHT0 REGx(R0900_P1_NNOSPLHT0)
+#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff
+#define NOSPLHT_NORMED0 FLDx(F0900_P1_NOSPLHT_NORMED0)
/*P1_NNOSPLH1*/
-#define R0900_P1_NNOSPLH1 0xf486
-#define F0900_P1_NOSPLH_NORMED1 0xf48600ff
+#define R0900_P1_NNOSPLH1 0xf486
+#define NNOSPLH1 REGx(R0900_P1_NNOSPLH1)
+#define F0900_P1_NOSPLH_NORMED1 0xf48600ff
/*P1_NNOSPLH0*/
-#define R0900_P1_NNOSPLH0 0xf487
-#define F0900_P1_NOSPLH_NORMED0 0xf48700ff
+#define R0900_P1_NNOSPLH0 0xf487
+#define NNOSPLH0 REGx(R0900_P1_NNOSPLH0)
+#define F0900_P1_NOSPLH_NORMED0 0xf48700ff
/*P1_NOSDATAT1*/
-#define R0900_P1_NOSDATAT1 0xf488
-#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff
+#define R0900_P1_NOSDATAT1 0xf488
+#define NOSDATAT1 REGx(R0900_P1_NOSDATAT1)
+#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff
/*P1_NOSDATAT0*/
-#define R0900_P1_NOSDATAT0 0xf489
-#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff
+#define R0900_P1_NOSDATAT0 0xf489
+#define NOSDATAT0 REGx(R0900_P1_NOSDATAT0)
+#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff
/*P1_NOSDATA1*/
-#define R0900_P1_NOSDATA1 0xf48a
-#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff
+#define R0900_P1_NOSDATA1 0xf48a
+#define NOSDATA1 REGx(R0900_P1_NOSDATA1)
+#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff
/*P1_NOSDATA0*/
-#define R0900_P1_NOSDATA0 0xf48b
-#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff
+#define R0900_P1_NOSDATA0 0xf48b
+#define NOSDATA0 REGx(R0900_P1_NOSDATA0)
+#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff
/*P1_NOSPLHT1*/
-#define R0900_P1_NOSPLHT1 0xf48c
-#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff
+#define R0900_P1_NOSPLHT1 0xf48c
+#define NOSPLHT1 REGx(R0900_P1_NOSPLHT1)
+#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff
/*P1_NOSPLHT0*/
-#define R0900_P1_NOSPLHT0 0xf48d
-#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff
+#define R0900_P1_NOSPLHT0 0xf48d
+#define NOSPLHT0 REGx(R0900_P1_NOSPLHT0)
+#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff
/*P1_NOSPLH1*/
-#define R0900_P1_NOSPLH1 0xf48e
-#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff
+#define R0900_P1_NOSPLH1 0xf48e
+#define NOSPLH1 REGx(R0900_P1_NOSPLH1)
+#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff
/*P1_NOSPLH0*/
-#define R0900_P1_NOSPLH0 0xf48f
-#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff
+#define R0900_P1_NOSPLH0 0xf48f
+#define NOSPLH0 REGx(R0900_P1_NOSPLH0)
+#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff
/*P1_CAR2CFG*/
-#define R0900_P1_CAR2CFG 0xf490
-#define F0900_P1_DESCRAMB_OFF 0xf4900080
-#define F0900_P1_PN4_SELECT 0xf4900040
-#define F0900_P1_CFR2_STOPDVBS1 0xf4900020
-#define F0900_P1_STOP_CFR2UPDATE 0xf4900010
-#define F0900_P1_STOP_NCO2UPDATE 0xf4900008
-#define F0900_P1_ROTA2ON 0xf4900004
-#define F0900_P1_PH_DET_ALGO2 0xf4900003
-
-/*P1_ACLC2*/
-#define R0900_P1_ACLC2 0xf491
-#define F0900_P1_CAR2_PUNCT_ADERAT 0xf4910040
-#define F0900_P1_CAR2_ALPHA_MANT 0xf4910030
-#define F0900_P1_CAR2_ALPHA_EXP 0xf491000f
-
-/*P1_BCLC2*/
-#define R0900_P1_BCLC2 0xf492
-#define F0900_P1_DVBS2_NIP 0xf4920080
-#define F0900_P1_CAR2_PUNCT_BDERAT 0xf4920040
-#define F0900_P1_CAR2_BETA_MANT 0xf4920030
-#define F0900_P1_CAR2_BETA_EXP 0xf492000f
+#define R0900_P1_CAR2CFG 0xf490
+#define CAR2CFG REGx(R0900_P1_CAR2CFG)
+#define F0900_P1_CARRIER3_DISABLE 0xf4900040
+#define F0900_P1_ROTA2ON 0xf4900004
+#define F0900_P1_PH_DET_ALGO2 0xf4900003
+
+/*P1_CFR2CFR1*/
+#define R0900_P1_CFR2CFR1 0xf491
+#define CFR2CFR1 REGx(R0900_P1_CFR2CFR1)
+#define F0900_P1_CFR2TOCFR1_DVBS1 0xf49100c0
+#define F0900_P1_EN_S2CAR2CENTER 0xf4910020
+#define F0900_P1_DIS_BCHERRCFR2 0xf4910010
+#define F0900_P1_CFR2TOCFR1_BETA 0xf4910007
/*P1_CFR22*/
-#define R0900_P1_CFR22 0xf493
-#define F0900_P1_CAR2_FREQ2 0xf49301ff
+#define R0900_P1_CFR22 0xf493
+#define CFR22 REGx(R0900_P1_CFR22)
+#define F0900_P1_CAR2_FREQ2 0xf49301ff
/*P1_CFR21*/
-#define R0900_P1_CFR21 0xf494
-#define F0900_P1_CAR2_FREQ1 0xf49400ff
+#define R0900_P1_CFR21 0xf494
+#define CFR21 REGx(R0900_P1_CFR21)
+#define F0900_P1_CAR2_FREQ1 0xf49400ff
/*P1_CFR20*/
-#define R0900_P1_CFR20 0xf495
-#define F0900_P1_CAR2_FREQ0 0xf49500ff
+#define R0900_P1_CFR20 0xf495
+#define CFR20 REGx(R0900_P1_CFR20)
+#define F0900_P1_CAR2_FREQ0 0xf49500ff
/*P1_ACLC2S2Q*/
-#define R0900_P1_ACLC2S2Q 0xf497
-#define F0900_P1_ENAB_SPSKSYMB 0xf4970080
-#define F0900_P1_CAR2S2_QADERAT 0xf4970040
-#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030
-#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f
+#define R0900_P1_ACLC2S2Q 0xf497
+#define ACLC2S2Q REGx(R0900_P1_ACLC2S2Q)
+#define F0900_P1_ENAB_SPSKSYMB 0xf4970080
+#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030
+#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f
/*P1_ACLC2S28*/
-#define R0900_P1_ACLC2S28 0xf498
-#define F0900_P1_OLDI3Q_MODE 0xf4980080
-#define F0900_P1_CAR2S2_8ADERAT 0xf4980040
-#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030
-#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f
+#define R0900_P1_ACLC2S28 0xf498
+#define ACLC2S28 REGx(R0900_P1_ACLC2S28)
+#define F0900_P1_OLDI3Q_MODE 0xf4980080
+#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030
+#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f
/*P1_ACLC2S216A*/
-#define R0900_P1_ACLC2S216A 0xf499
-#define F0900_P1_CAR2S2_16ADERAT 0xf4990040
-#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030
-#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f
+#define R0900_P1_ACLC2S216A 0xf499
+#define ACLC2S216A REGx(R0900_P1_ACLC2S216A)
+#define F0900_P1_DIS_C3STOPA2 0xf4990080
+#define F0900_P1_CAR2S2_16ADERAT 0xf4990040
+#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030
+#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f
/*P1_ACLC2S232A*/
-#define R0900_P1_ACLC2S232A 0xf49a
-#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040
-#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030
-#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f
+#define R0900_P1_ACLC2S232A 0xf49a
+#define ACLC2S232A REGx(R0900_P1_ACLC2S232A)
+#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040
+#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030
+#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f
/*P1_BCLC2S2Q*/
-#define R0900_P1_BCLC2S2Q 0xf49c
-#define F0900_P1_DVBS2S2Q_NIP 0xf49c0080
-#define F0900_P1_CAR2S2_QBDERAT 0xf49c0040
-#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030
-#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f
+#define R0900_P1_BCLC2S2Q 0xf49c
+#define BCLC2S2Q REGx(R0900_P1_BCLC2S2Q)
+#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030
+#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f
/*P1_BCLC2S28*/
-#define R0900_P1_BCLC2S28 0xf49d
-#define F0900_P1_DVBS2S28_NIP 0xf49d0080
-#define F0900_P1_CAR2S2_8BDERAT 0xf49d0040
-#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030
-#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f
+#define R0900_P1_BCLC2S28 0xf49d
+#define BCLC2S28 REGx(R0900_P1_BCLC2S28)
+#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030
+#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f
/*P1_BCLC2S216A*/
-#define R0900_P1_BCLC2S216A 0xf49e
-#define F0900_P1_DVBS2S216A_NIP 0xf49e0080
-#define F0900_P1_CAR2S2_16BDERAT 0xf49e0040
-#define F0900_P1_CAR2S2_16A_BETA_M 0xf49e0030
-#define F0900_P1_CAR2S2_16A_BETA_E 0xf49e000f
+#define R0900_P1_BCLC2S216A 0xf49e
+#define BCLC2S216A REGx(R0900_P1_BCLC2S216A)
/*P1_BCLC2S232A*/
-#define R0900_P1_BCLC2S232A 0xf49f
-#define F0900_P1_DVBS2S232A_NIP 0xf49f0080
-#define F0900_P1_CAR2S2_32BDERAT 0xf49f0040
-#define F0900_P1_CAR2S2_32A_BETA_M 0xf49f0030
-#define F0900_P1_CAR2S2_32A_BETA_E 0xf49f000f
+#define R0900_P1_BCLC2S232A 0xf49f
+#define BCLC2S232A REGx(R0900_P1_BCLC2S232A)
/*P1_PLROOT2*/
-#define R0900_P1_PLROOT2 0xf4ac
-#define F0900_P1_SHORTFR_DISABLE 0xf4ac0080
-#define F0900_P1_LONGFR_DISABLE 0xf4ac0040
-#define F0900_P1_DUMMYPL_DISABLE 0xf4ac0020
-#define F0900_P1_SHORTFR_AVOID 0xf4ac0010
-#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c
-#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003
+#define R0900_P1_PLROOT2 0xf4ac
+#define PLROOT2 REGx(R0900_P1_PLROOT2)
+#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c
+#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003
/*P1_PLROOT1*/
-#define R0900_P1_PLROOT1 0xf4ad
-#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff
+#define R0900_P1_PLROOT1 0xf4ad
+#define PLROOT1 REGx(R0900_P1_PLROOT1)
+#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff
/*P1_PLROOT0*/
-#define R0900_P1_PLROOT0 0xf4ae
-#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff
+#define R0900_P1_PLROOT0 0xf4ae
+#define PLROOT0 REGx(R0900_P1_PLROOT0)
+#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff
/*P1_MODCODLST0*/
-#define R0900_P1_MODCODLST0 0xf4b0
-#define F0900_P1_EN_TOKEN31 0xf4b00080
-#define F0900_P1_SYNCTAG_SELECT 0xf4b00040
-#define F0900_P1_MODCODRQ_MODE 0xf4b00030
+#define R0900_P1_MODCODLST0 0xf4b0
+#define MODCODLST0 REGx(R0900_P1_MODCODLST0)
/*P1_MODCODLST1*/
-#define R0900_P1_MODCODLST1 0xf4b1
-#define F0900_P1_DIS_MODCOD29 0xf4b100f0
-#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f
+#define R0900_P1_MODCODLST1 0xf4b1
+#define MODCODLST1 REGx(R0900_P1_MODCODLST1)
+#define F0900_P1_DIS_MODCOD29 0xf4b100f0
+#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f
/*P1_MODCODLST2*/
-#define R0900_P1_MODCODLST2 0xf4b2
-#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0
-#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f
+#define R0900_P1_MODCODLST2 0xf4b2
+#define MODCODLST2 REGx(R0900_P1_MODCODLST2)
+#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0
+#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f
/*P1_MODCODLST3*/
-#define R0900_P1_MODCODLST3 0xf4b3
-#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0
-#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f
+#define R0900_P1_MODCODLST3 0xf4b3
+#define MODCODLST3 REGx(R0900_P1_MODCODLST3)
+#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0
+#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f
/*P1_MODCODLST4*/
-#define R0900_P1_MODCODLST4 0xf4b4
-#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0
-#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f
+#define R0900_P1_MODCODLST4 0xf4b4
+#define MODCODLST4 REGx(R0900_P1_MODCODLST4)
+#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0
+#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f
/*P1_MODCODLST5*/
-#define R0900_P1_MODCODLST5 0xf4b5
-#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0
-#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f
+#define R0900_P1_MODCODLST5 0xf4b5
+#define MODCODLST5 REGx(R0900_P1_MODCODLST5)
+#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0
+#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f
/*P1_MODCODLST6*/
-#define R0900_P1_MODCODLST6 0xf4b6
-#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0
-#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f
+#define R0900_P1_MODCODLST6 0xf4b6
+#define MODCODLST6 REGx(R0900_P1_MODCODLST6)
+#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0
+#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f
/*P1_MODCODLST7*/
-#define R0900_P1_MODCODLST7 0xf4b7
-#define F0900_P1_DIS_8P_9_10 0xf4b700f0
-#define F0900_P1_DIS_8P_8_9 0xf4b7000f
+#define R0900_P1_MODCODLST7 0xf4b7
+#define MODCODLST7 REGx(R0900_P1_MODCODLST7)
+#define F0900_P1_DIS_8P_9_10 0xf4b700f0
+#define F0900_P1_DIS_8P_8_9 0xf4b7000f
/*P1_MODCODLST8*/
-#define R0900_P1_MODCODLST8 0xf4b8
-#define F0900_P1_DIS_8P_5_6 0xf4b800f0
-#define F0900_P1_DIS_8P_3_4 0xf4b8000f
+#define R0900_P1_MODCODLST8 0xf4b8
+#define MODCODLST8 REGx(R0900_P1_MODCODLST8)
+#define F0900_P1_DIS_8P_5_6 0xf4b800f0
+#define F0900_P1_DIS_8P_3_4 0xf4b8000f
/*P1_MODCODLST9*/
-#define R0900_P1_MODCODLST9 0xf4b9
-#define F0900_P1_DIS_8P_2_3 0xf4b900f0
-#define F0900_P1_DIS_8P_3_5 0xf4b9000f
+#define R0900_P1_MODCODLST9 0xf4b9
+#define MODCODLST9 REGx(R0900_P1_MODCODLST9)
+#define F0900_P1_DIS_8P_2_3 0xf4b900f0
+#define F0900_P1_DIS_8P_3_5 0xf4b9000f
/*P1_MODCODLSTA*/
-#define R0900_P1_MODCODLSTA 0xf4ba
-#define F0900_P1_DIS_QP_9_10 0xf4ba00f0
-#define F0900_P1_DIS_QP_8_9 0xf4ba000f
+#define R0900_P1_MODCODLSTA 0xf4ba
+#define MODCODLSTA REGx(R0900_P1_MODCODLSTA)
+#define F0900_P1_DIS_QP_9_10 0xf4ba00f0
+#define F0900_P1_DIS_QP_8_9 0xf4ba000f
/*P1_MODCODLSTB*/
-#define R0900_P1_MODCODLSTB 0xf4bb
-#define F0900_P1_DIS_QP_5_6 0xf4bb00f0
-#define F0900_P1_DIS_QP_4_5 0xf4bb000f
+#define R0900_P1_MODCODLSTB 0xf4bb
+#define MODCODLSTB REGx(R0900_P1_MODCODLSTB)
+#define F0900_P1_DIS_QP_5_6 0xf4bb00f0
+#define F0900_P1_DIS_QP_4_5 0xf4bb000f
/*P1_MODCODLSTC*/
-#define R0900_P1_MODCODLSTC 0xf4bc
-#define F0900_P1_DIS_QP_3_4 0xf4bc00f0
-#define F0900_P1_DIS_QP_2_3 0xf4bc000f
+#define R0900_P1_MODCODLSTC 0xf4bc
+#define MODCODLSTC REGx(R0900_P1_MODCODLSTC)
+#define F0900_P1_DIS_QP_3_4 0xf4bc00f0
+#define F0900_P1_DIS_QP_2_3 0xf4bc000f
/*P1_MODCODLSTD*/
-#define R0900_P1_MODCODLSTD 0xf4bd
-#define F0900_P1_DIS_QP_3_5 0xf4bd00f0
-#define F0900_P1_DIS_QP_1_2 0xf4bd000f
+#define R0900_P1_MODCODLSTD 0xf4bd
+#define MODCODLSTD REGx(R0900_P1_MODCODLSTD)
+#define F0900_P1_DIS_QP_3_5 0xf4bd00f0
+#define F0900_P1_DIS_QP_1_2 0xf4bd000f
/*P1_MODCODLSTE*/
-#define R0900_P1_MODCODLSTE 0xf4be
-#define F0900_P1_DIS_QP_2_5 0xf4be00f0
-#define F0900_P1_DIS_QP_1_3 0xf4be000f
+#define R0900_P1_MODCODLSTE 0xf4be
+#define MODCODLSTE REGx(R0900_P1_MODCODLSTE)
+#define F0900_P1_DIS_QP_2_5 0xf4be00f0
+#define F0900_P1_DIS_QP_1_3 0xf4be000f
/*P1_MODCODLSTF*/
-#define R0900_P1_MODCODLSTF 0xf4bf
-#define F0900_P1_DIS_QP_1_4 0xf4bf00f0
-#define F0900_P1_DDEMOD_SET 0xf4bf0002
-#define F0900_P1_DDEMOD_MASK 0xf4bf0001
+#define R0900_P1_MODCODLSTF 0xf4bf
+#define MODCODLSTF REGx(R0900_P1_MODCODLSTF)
+#define F0900_P1_DIS_QP_1_4 0xf4bf00f0
+
+/*P1_GAUSSR0*/
+#define R0900_P1_GAUSSR0 0xf4c0
+#define GAUSSR0 REGx(R0900_P1_GAUSSR0)
+#define F0900_P1_EN_CCIMODE 0xf4c00080
+#define F0900_P1_R0_GAUSSIEN 0xf4c0007f
+
+/*P1_CCIR0*/
+#define R0900_P1_CCIR0 0xf4c1
+#define CCIR0 REGx(R0900_P1_CCIR0)
+#define F0900_P1_CCIDETECT_PLHONLY 0xf4c10080
+#define F0900_P1_R0_CCI 0xf4c1007f
+
+/*P1_CCIQUANT*/
+#define R0900_P1_CCIQUANT 0xf4c2
+#define CCIQUANT REGx(R0900_P1_CCIQUANT)
+#define F0900_P1_CCI_BETA 0xf4c200e0
+#define F0900_P1_CCI_QUANT 0xf4c2001f
+
+/*P1_CCITHRES*/
+#define R0900_P1_CCITHRES 0xf4c3
+#define CCITHRES REGx(R0900_P1_CCITHRES)
+#define F0900_P1_CCI_THRESHOLD 0xf4c300ff
+
+/*P1_CCIACC*/
+#define R0900_P1_CCIACC 0xf4c4
+#define CCIACC REGx(R0900_P1_CCIACC)
+#define F0900_P1_CCI_VALUE 0xf4c400ff
/*P1_DMDRESCFG*/
-#define R0900_P1_DMDRESCFG 0xf4c6
-#define F0900_P1_DMDRES_RESET 0xf4c60080
-#define F0900_P1_DMDRES_NOISESQR 0xf4c60010
-#define F0900_P1_DMDRES_STRALL 0xf4c60008
-#define F0900_P1_DMDRES_NEWONLY 0xf4c60004
-#define F0900_P1_DMDRES_NOSTORE 0xf4c60002
-#define F0900_P1_DMDRES_AGC2MEM 0xf4c60001
+#define R0900_P1_DMDRESCFG 0xf4c6
+#define DMDRESCFG REGx(R0900_P1_DMDRESCFG)
+#define F0900_P1_DMDRES_RESET 0xf4c60080
+#define F0900_P1_DMDRES_STRALL 0xf4c60008
+#define F0900_P1_DMDRES_NEWONLY 0xf4c60004
+#define F0900_P1_DMDRES_NOSTORE 0xf4c60002
/*P1_DMDRESADR*/
-#define R0900_P1_DMDRESADR 0xf4c7
-#define F0900_P1_SUSP_PREDCANAL 0xf4c70080
-#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040
-#define F0900_P1_DMDRES_MEMFULL 0xf4c70030
-#define F0900_P1_DMDRES_RESNBR 0xf4c7000f
+#define R0900_P1_DMDRESADR 0xf4c7
+#define DMDRESADR REGx(R0900_P1_DMDRESADR)
+#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040
+#define F0900_P1_DMDRES_MEMFULL 0xf4c70030
+#define F0900_P1_DMDRES_RESNBR 0xf4c7000f
/*P1_DMDRESDATA7*/
-#define R0900_P1_DMDRESDATA7 0xf4c8
-#define F0900_P1_DMDRES_DATA7 0xf4c800ff
+#define R0900_P1_DMDRESDATA7 0xf4c8
+#define F0900_P1_DMDRES_DATA7 0xf4c800ff
/*P1_DMDRESDATA6*/
-#define R0900_P1_DMDRESDATA6 0xf4c9
-#define F0900_P1_DMDRES_DATA6 0xf4c900ff
+#define R0900_P1_DMDRESDATA6 0xf4c9
+#define F0900_P1_DMDRES_DATA6 0xf4c900ff
/*P1_DMDRESDATA5*/
-#define R0900_P1_DMDRESDATA5 0xf4ca
-#define F0900_P1_DMDRES_DATA5 0xf4ca00ff
+#define R0900_P1_DMDRESDATA5 0xf4ca
+#define F0900_P1_DMDRES_DATA5 0xf4ca00ff
/*P1_DMDRESDATA4*/
-#define R0900_P1_DMDRESDATA4 0xf4cb
-#define F0900_P1_DMDRES_DATA4 0xf4cb00ff
+#define R0900_P1_DMDRESDATA4 0xf4cb
+#define F0900_P1_DMDRES_DATA4 0xf4cb00ff
/*P1_DMDRESDATA3*/
-#define R0900_P1_DMDRESDATA3 0xf4cc
-#define F0900_P1_DMDRES_DATA3 0xf4cc00ff
+#define R0900_P1_DMDRESDATA3 0xf4cc
+#define F0900_P1_DMDRES_DATA3 0xf4cc00ff
/*P1_DMDRESDATA2*/
-#define R0900_P1_DMDRESDATA2 0xf4cd
-#define F0900_P1_DMDRES_DATA2 0xf4cd00ff
+#define R0900_P1_DMDRESDATA2 0xf4cd
+#define F0900_P1_DMDRES_DATA2 0xf4cd00ff
/*P1_DMDRESDATA1*/
-#define R0900_P1_DMDRESDATA1 0xf4ce
-#define F0900_P1_DMDRES_DATA1 0xf4ce00ff
+#define R0900_P1_DMDRESDATA1 0xf4ce
+#define F0900_P1_DMDRES_DATA1 0xf4ce00ff
/*P1_DMDRESDATA0*/
-#define R0900_P1_DMDRESDATA0 0xf4cf
-#define F0900_P1_DMDRES_DATA0 0xf4cf00ff
+#define R0900_P1_DMDRESDATA0 0xf4cf
+#define F0900_P1_DMDRES_DATA0 0xf4cf00ff
/*P1_FFEI1*/
-#define R0900_P1_FFEI1 0xf4d0
-#define F0900_P1_FFE_ACCI1 0xf4d001ff
+#define R0900_P1_FFEI1 0xf4d0
+#define FFEI1 REGx(R0900_P1_FFEI1)
+#define F0900_P1_FFE_ACCI1 0xf4d001ff
/*P1_FFEQ1*/
-#define R0900_P1_FFEQ1 0xf4d1
-#define F0900_P1_FFE_ACCQ1 0xf4d101ff
+#define R0900_P1_FFEQ1 0xf4d1
+#define FFEQ1 REGx(R0900_P1_FFEQ1)
+#define F0900_P1_FFE_ACCQ1 0xf4d101ff
/*P1_FFEI2*/
-#define R0900_P1_FFEI2 0xf4d2
-#define F0900_P1_FFE_ACCI2 0xf4d201ff
+#define R0900_P1_FFEI2 0xf4d2
+#define FFEI2 REGx(R0900_P1_FFEI2)
+#define F0900_P1_FFE_ACCI2 0xf4d201ff
/*P1_FFEQ2*/
-#define R0900_P1_FFEQ2 0xf4d3
-#define F0900_P1_FFE_ACCQ2 0xf4d301ff
+#define R0900_P1_FFEQ2 0xf4d3
+#define FFEQ2 REGx(R0900_P1_FFEQ2)
+#define F0900_P1_FFE_ACCQ2 0xf4d301ff
/*P1_FFEI3*/
-#define R0900_P1_FFEI3 0xf4d4
-#define F0900_P1_FFE_ACCI3 0xf4d401ff
+#define R0900_P1_FFEI3 0xf4d4
+#define FFEI3 REGx(R0900_P1_FFEI3)
+#define F0900_P1_FFE_ACCI3 0xf4d401ff
/*P1_FFEQ3*/
-#define R0900_P1_FFEQ3 0xf4d5
-#define F0900_P1_FFE_ACCQ3 0xf4d501ff
+#define R0900_P1_FFEQ3 0xf4d5
+#define FFEQ3 REGx(R0900_P1_FFEQ3)
+#define F0900_P1_FFE_ACCQ3 0xf4d501ff
/*P1_FFEI4*/
-#define R0900_P1_FFEI4 0xf4d6
-#define F0900_P1_FFE_ACCI4 0xf4d601ff
+#define R0900_P1_FFEI4 0xf4d6
+#define FFEI4 REGx(R0900_P1_FFEI4)
+#define F0900_P1_FFE_ACCI4 0xf4d601ff
/*P1_FFEQ4*/
-#define R0900_P1_FFEQ4 0xf4d7
-#define F0900_P1_FFE_ACCQ4 0xf4d701ff
+#define R0900_P1_FFEQ4 0xf4d7
+#define FFEQ4 REGx(R0900_P1_FFEQ4)
+#define F0900_P1_FFE_ACCQ4 0xf4d701ff
/*P1_FFECFG*/
-#define R0900_P1_FFECFG 0xf4d8
-#define F0900_P1_EQUALFFE_ON 0xf4d80040
-#define F0900_P1_EQUAL_USEDSYMB 0xf4d80030
-#define F0900_P1_MU_EQUALFFE 0xf4d80007
+#define R0900_P1_FFECFG 0xf4d8
+#define FFECFG REGx(R0900_P1_FFECFG)
+#define F0900_P1_EQUALFFE_ON 0xf4d80040
+#define F0900_P1_MU_EQUALFFE 0xf4d80007
/*P1_TNRCFG*/
-#define R0900_P1_TNRCFG 0xf4e0
-#define F0900_P1_TUN_ACKFAIL 0xf4e00080
-#define F0900_P1_TUN_TYPE 0xf4e00070
-#define F0900_P1_TUN_SECSTOP 0xf4e00008
-#define F0900_P1_TUN_VCOSRCH 0xf4e00004
-#define F0900_P1_TUN_MADDRESS 0xf4e00003
+#define R0900_P1_TNRCFG 0xf4e0
+#define TNRCFG REGx(R0900_P1_TNRCFG)
+#define F0900_P1_TUN_ACKFAIL 0xf4e00080
+#define F0900_P1_TUN_TYPE 0xf4e00070
+#define F0900_P1_TUN_SECSTOP 0xf4e00008
+#define F0900_P1_TUN_VCOSRCH 0xf4e00004
+#define F0900_P1_TUN_MADDRESS 0xf4e00003
/*P1_TNRCFG2*/
-#define R0900_P1_TNRCFG2 0xf4e1
-#define F0900_P1_TUN_IQSWAP 0xf4e10080
-#define F0900_P1_STB6110_STEP2MHZ 0xf4e10040
-#define F0900_P1_STB6120_DBLI2C 0xf4e10020
-#define F0900_P1_DIS_FCCK 0xf4e10010
-#define F0900_P1_DIS_LPEN 0xf4e10008
-#define F0900_P1_DIS_BWCALC 0xf4e10004
-#define F0900_P1_SHORT_WAITSTATES 0xf4e10002
-#define F0900_P1_DIS_2BWAGC1 0xf4e10001
+#define R0900_P1_TNRCFG2 0xf4e1
+#define TNRCFG2 REGx(R0900_P1_TNRCFG2)
+#define F0900_P1_TUN_IQSWAP 0xf4e10080
+#define F0900_P1_DIS_BWCALC 0xf4e10004
+#define F0900_P1_SHORT_WAITSTATES 0xf4e10002
/*P1_TNRXTAL*/
-#define R0900_P1_TNRXTAL 0xf4e4
-#define F0900_P1_TUN_MCLKDECIMAL 0xf4e400e0
-#define F0900_P1_TUN_XTALFREQ 0xf4e4001f
+#define R0900_P1_TNRXTAL 0xf4e4
+#define TNRXTAL REGx(R0900_P1_TNRXTAL)
+#define F0900_P1_TUN_XTALFREQ 0xf4e4001f
/*P1_TNRSTEPS*/
-#define R0900_P1_TNRSTEPS 0xf4e7
-#define F0900_P1_TUNER_BW1P6 0xf4e70080
-#define F0900_P1_BWINC_OFFSET 0xf4e70070
-#define F0900_P1_SOFTSTEP_RNG 0xf4e70008
-#define F0900_P1_TUN_BWOFFSET 0xf4e70107
+#define R0900_P1_TNRSTEPS 0xf4e7
+#define TNRSTEPS REGx(R0900_P1_TNRSTEPS)
+#define F0900_P1_TUNER_BW0P125 0xf4e70080
+#define F0900_P1_BWINC_OFFSET 0xf4e70170
+#define F0900_P1_SOFTSTEP_RNG 0xf4e70008
+#define F0900_P1_TUN_BWOFFSET 0xf4e70007
/*P1_TNRGAIN*/
-#define R0900_P1_TNRGAIN 0xf4e8
-#define F0900_P1_TUN_KDIVEN 0xf4e800c0
-#define F0900_P1_STB6X00_OCK 0xf4e80030
-#define F0900_P1_TUN_GAIN 0xf4e8000f
+#define R0900_P1_TNRGAIN 0xf4e8
+#define TNRGAIN REGx(R0900_P1_TNRGAIN)
+#define F0900_P1_TUN_KDIVEN 0xf4e800c0
+#define F0900_P1_STB6X00_OCK 0xf4e80030
+#define F0900_P1_TUN_GAIN 0xf4e8000f
/*P1_TNRRF1*/
-#define R0900_P1_TNRRF1 0xf4e9
-#define F0900_P1_TUN_RFFREQ2 0xf4e900ff
+#define R0900_P1_TNRRF1 0xf4e9
+#define TNRRF1 REGx(R0900_P1_TNRRF1)
+#define F0900_P1_TUN_RFFREQ2 0xf4e900ff
/*P1_TNRRF0*/
-#define R0900_P1_TNRRF0 0xf4ea
-#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff
+#define R0900_P1_TNRRF0 0xf4ea
+#define TNRRF0 REGx(R0900_P1_TNRRF0)
+#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff
/*P1_TNRBW*/
-#define R0900_P1_TNRBW 0xf4eb
-#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0
-#define F0900_P1_TUN_BW 0xf4eb003f
+#define R0900_P1_TNRBW 0xf4eb
+#define TNRBW REGx(R0900_P1_TNRBW)
+#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0
+#define F0900_P1_TUN_BW 0xf4eb003f
/*P1_TNRADJ*/
-#define R0900_P1_TNRADJ 0xf4ec
-#define F0900_P1_STB61X0_RCLK 0xf4ec0080
-#define F0900_P1_STB61X0_CALTIME 0xf4ec0040
-#define F0900_P1_STB6X00_DLB 0xf4ec0038
-#define F0900_P1_STB6000_FCL 0xf4ec0007
+#define R0900_P1_TNRADJ 0xf4ec
+#define TNRADJ REGx(R0900_P1_TNRADJ)
+#define F0900_P1_STB61X0_CALTIME 0xf4ec0040
/*P1_TNRCTL2*/
-#define R0900_P1_TNRCTL2 0xf4ed
-#define F0900_P1_STB61X0_LCP1_RCCKOFF 0xf4ed0080
-#define F0900_P1_STB61X0_LCP0 0xf4ed0040
-#define F0900_P1_STB61X0_XTOUT_RFOUTS 0xf4ed0020
-#define F0900_P1_STB61X0_XTON_MCKDV 0xf4ed0010
-#define F0900_P1_STB61X0_CALOFF_DCOFF 0xf4ed0008
-#define F0900_P1_STB6110_LPT 0xf4ed0004
-#define F0900_P1_STB6110_RX 0xf4ed0002
-#define F0900_P1_STB6110_SYN 0xf4ed0001
+#define R0900_P1_TNRCTL2 0xf4ed
+#define TNRCTL2 REGx(R0900_P1_TNRCTL2)
+#define F0900_P1_STB61X0_RCCKOFF 0xf4ed0080
+#define F0900_P1_STB61X0_ICP_SDOFF 0xf4ed0040
+#define F0900_P1_STB61X0_DCLOOPOFF 0xf4ed0020
+#define F0900_P1_STB61X0_REFOUTSEL 0xf4ed0010
+#define F0900_P1_STB61X0_CALOFF 0xf4ed0008
+#define F0900_P1_STB6XX0_LPT_BEN 0xf4ed0004
+#define F0900_P1_STB6XX0_RX_OSCP 0xf4ed0002
+#define F0900_P1_STB6XX0_SYN 0xf4ed0001
/*P1_TNRCFG3*/
-#define R0900_P1_TNRCFG3 0xf4ee
-#define F0900_P1_STB6120_DISCTRL1 0xf4ee0080
-#define F0900_P1_STB6120_INVORDER 0xf4ee0040
-#define F0900_P1_STB6120_ENCTRL6 0xf4ee0020
-#define F0900_P1_TUN_PLLFREQ 0xf4ee001c
-#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003
+#define R0900_P1_TNRCFG3 0xf4ee
+#define TNRCFG3 REGx(R0900_P1_TNRCFG3)
+#define F0900_P1_TUN_PLLFREQ 0xf4ee001c
+#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003
/*P1_TNRLAUNCH*/
-#define R0900_P1_TNRLAUNCH 0xf4f0
+#define R0900_P1_TNRLAUNCH 0xf4f0
+#define TNRLAUNCH REGx(R0900_P1_TNRLAUNCH)
/*P1_TNRLD*/
-#define R0900_P1_TNRLD 0xf4f0
-#define F0900_P1_TUNLD_VCOING 0xf4f00080
-#define F0900_P1_TUN_REG1FAIL 0xf4f00040
-#define F0900_P1_TUN_REG2FAIL 0xf4f00020
-#define F0900_P1_TUN_REG3FAIL 0xf4f00010
-#define F0900_P1_TUN_REG4FAIL 0xf4f00008
-#define F0900_P1_TUN_REG5FAIL 0xf4f00004
-#define F0900_P1_TUN_BWING 0xf4f00002
-#define F0900_P1_TUN_LOCKED 0xf4f00001
+#define R0900_P1_TNRLD 0xf4f0
+#define TNRLD REGx(R0900_P1_TNRLD)
+#define F0900_P1_TUNLD_VCOING 0xf4f00080
+#define F0900_P1_TUN_REG1FAIL 0xf4f00040
+#define F0900_P1_TUN_REG2FAIL 0xf4f00020
+#define F0900_P1_TUN_REG3FAIL 0xf4f00010
+#define F0900_P1_TUN_REG4FAIL 0xf4f00008
+#define F0900_P1_TUN_REG5FAIL 0xf4f00004
+#define F0900_P1_TUN_BWING 0xf4f00002
+#define F0900_P1_TUN_LOCKED 0xf4f00001
/*P1_TNROBSL*/
-#define R0900_P1_TNROBSL 0xf4f6
-#define F0900_P1_TUN_I2CABORTED 0xf4f60080
-#define F0900_P1_TUN_LPEN 0xf4f60040
-#define F0900_P1_TUN_FCCK 0xf4f60020
-#define F0900_P1_TUN_I2CLOCKED 0xf4f60010
-#define F0900_P1_TUN_PROGDONE 0xf4f6000c
-#define F0900_P1_TUN_RFRESTE1 0xf4f60003
+#define R0900_P1_TNROBSL 0xf4f6
+#define TNROBSL REGx(R0900_P1_TNROBSL)
+#define F0900_P1_TUN_I2CABORTED 0xf4f60080
+#define F0900_P1_TUN_LPEN 0xf4f60040
+#define F0900_P1_TUN_FCCK 0xf4f60020
+#define F0900_P1_TUN_I2CLOCKED 0xf4f60010
+#define F0900_P1_TUN_PROGDONE 0xf4f6000c
+#define F0900_P1_TUN_RFRESTE1 0xf4f60003
/*P1_TNRRESTE*/
-#define R0900_P1_TNRRESTE 0xf4f7
-#define F0900_P1_TUN_RFRESTE0 0xf4f700ff
+#define R0900_P1_TNRRESTE 0xf4f7
+#define TNRRESTE REGx(R0900_P1_TNRRESTE)
+#define F0900_P1_TUN_RFRESTE0 0xf4f700ff
/*P1_SMAPCOEF7*/
-#define R0900_P1_SMAPCOEF7 0xf500
-#define F0900_P1_DIS_QSCALE 0xf5000080
-#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f
+#define R0900_P1_SMAPCOEF7 0xf500
+#define SMAPCOEF7 REGx(R0900_P1_SMAPCOEF7)
+#define F0900_P1_DIS_QSCALE 0xf5000080
+#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f
/*P1_SMAPCOEF6*/
-#define R0900_P1_SMAPCOEF6 0xf501
-#define F0900_P1_DIS_NEWSCALE 0xf5010008
-#define F0900_P1_ADJ_8PSKLLR1 0xf5010004
-#define F0900_P1_OLD_8PSKLLR1 0xf5010002
-#define F0900_P1_DIS_AB8PSK 0xf5010001
+#define R0900_P1_SMAPCOEF6 0xf501
+#define SMAPCOEF6 REGx(R0900_P1_SMAPCOEF6)
+#define F0900_P1_ADJ_8PSKLLR1 0xf5010004
+#define F0900_P1_OLD_8PSKLLR1 0xf5010002
+#define F0900_P1_DIS_AB8PSK 0xf5010001
/*P1_SMAPCOEF5*/
-#define R0900_P1_SMAPCOEF5 0xf502
-#define F0900_P1_DIS_8SCALE 0xf5020080
-#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f
+#define R0900_P1_SMAPCOEF5 0xf502
+#define SMAPCOEF5 REGx(R0900_P1_SMAPCOEF5)
+#define F0900_P1_DIS_8SCALE 0xf5020080
+#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f
+
+/*P1_NCO2MAX1*/
+#define R0900_P1_NCO2MAX1 0xf514
+#define NCO2MAX1 REGx(R0900_P1_NCO2MAX1)
+#define F0900_P1_TETA2_MAXVABS1 0xf51400ff
+
+/*P1_NCO2MAX0*/
+#define R0900_P1_NCO2MAX0 0xf515
+#define NCO2MAX0 REGx(R0900_P1_NCO2MAX0)
+#define F0900_P1_TETA2_MAXVABS0 0xf51500ff
+
+/*P1_NCO2FR1*/
+#define R0900_P1_NCO2FR1 0xf516
+#define NCO2FR1 REGx(R0900_P1_NCO2FR1)
+#define F0900_P1_NCO2FINAL_ANGLE1 0xf51600ff
+
+/*P1_NCO2FR0*/
+#define R0900_P1_NCO2FR0 0xf517
+#define NCO2FR0 REGx(R0900_P1_NCO2FR0)
+#define F0900_P1_NCO2FINAL_ANGLE0 0xf51700ff
+
+/*P1_CFR2AVRGE1*/
+#define R0900_P1_CFR2AVRGE1 0xf518
+#define CFR2AVRGE1 REGx(R0900_P1_CFR2AVRGE1)
+#define F0900_P1_I2C_CFR2AVERAGE1 0xf51800ff
+
+/*P1_CFR2AVRGE0*/
+#define R0900_P1_CFR2AVRGE0 0xf519
+#define CFR2AVRGE0 REGx(R0900_P1_CFR2AVRGE0)
+#define F0900_P1_I2C_CFR2AVERAGE0 0xf51900ff
/*P1_DMDPLHSTAT*/
-#define R0900_P1_DMDPLHSTAT 0xf520
-#define F0900_P1_PLH_STATISTIC 0xf52000ff
+#define R0900_P1_DMDPLHSTAT 0xf520
+#define DMDPLHSTAT REGx(R0900_P1_DMDPLHSTAT)
+#define F0900_P1_PLH_STATISTIC 0xf52000ff
/*P1_LOCKTIME3*/
-#define R0900_P1_LOCKTIME3 0xf522
-#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff
+#define R0900_P1_LOCKTIME3 0xf522
+#define LOCKTIME3 REGx(R0900_P1_LOCKTIME3)
+#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff
/*P1_LOCKTIME2*/
-#define R0900_P1_LOCKTIME2 0xf523
-#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff
+#define R0900_P1_LOCKTIME2 0xf523
+#define LOCKTIME2 REGx(R0900_P1_LOCKTIME2)
+#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff
/*P1_LOCKTIME1*/
-#define R0900_P1_LOCKTIME1 0xf524
-#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff
+#define R0900_P1_LOCKTIME1 0xf524
+#define LOCKTIME1 REGx(R0900_P1_LOCKTIME1)
+#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff
/*P1_LOCKTIME0*/
-#define R0900_P1_LOCKTIME0 0xf525
-#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff
+#define R0900_P1_LOCKTIME0 0xf525
+#define LOCKTIME0 REGx(R0900_P1_LOCKTIME0)
+#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff
/*P1_VITSCALE*/
-#define R0900_P1_VITSCALE 0xf532
-#define F0900_P1_NVTH_NOSRANGE 0xf5320080
-#define F0900_P1_VERROR_MAXMODE 0xf5320040
-#define F0900_P1_KDIV_MODE 0xf5320030
-#define F0900_P1_NSLOWSN_LOCKED 0xf5320008
-#define F0900_P1_DELOCK_PRFLOSS 0xf5320004
-#define F0900_P1_DIS_RSFLOCK 0xf5320002
+#define R0900_P1_VITSCALE 0xf532
+#define VITSCALE REGx(R0900_P1_VITSCALE)
+#define F0900_P1_NVTH_NOSRANGE 0xf5320080
+#define F0900_P1_VERROR_MAXMODE 0xf5320040
+#define F0900_P1_NSLOWSN_LOCKED 0xf5320008
+#define F0900_P1_DIS_RSFLOCK 0xf5320002
/*P1_FECM*/
-#define R0900_P1_FECM 0xf533
-#define F0900_P1_DSS_DVB 0xf5330080
-#define F0900_P1_DEMOD_BYPASS 0xf5330040
-#define F0900_P1_CMP_SLOWMODE 0xf5330020
-#define F0900_P1_DSS_SRCH 0xf5330010
-#define F0900_P1_DIFF_MODEVIT 0xf5330004
-#define F0900_P1_SYNCVIT 0xf5330002
-#define F0900_P1_IQINV 0xf5330001
+#define R0900_P1_FECM 0xf533
+#define FECM REGx(R0900_P1_FECM)
+#define F0900_P1_DSS_DVB 0xf5330080
+#define DSS_DVB FLDx(F0900_P1_DSS_DVB)
+#define F0900_P1_DSS_SRCH 0xf5330010
+#define F0900_P1_SYNCVIT 0xf5330002
+#define F0900_P1_IQINV 0xf5330001
+#define IQINV FLDx(F0900_P1_IQINV)
/*P1_VTH12*/
-#define R0900_P1_VTH12 0xf534
-#define F0900_P1_VTH12 0xf53400ff
+#define R0900_P1_VTH12 0xf534
+#define VTH12 REGx(R0900_P1_VTH12)
+#define F0900_P1_VTH12 0xf53400ff
/*P1_VTH23*/
-#define R0900_P1_VTH23 0xf535
-#define F0900_P1_VTH23 0xf53500ff
+#define R0900_P1_VTH23 0xf535
+#define VTH23 REGx(R0900_P1_VTH23)
+#define F0900_P1_VTH23 0xf53500ff
/*P1_VTH34*/
-#define R0900_P1_VTH34 0xf536
-#define F0900_P1_VTH34 0xf53600ff
+#define R0900_P1_VTH34 0xf536
+#define VTH34 REGx(R0900_P1_VTH34)
+#define F0900_P1_VTH34 0xf53600ff
/*P1_VTH56*/
-#define R0900_P1_VTH56 0xf537
-#define F0900_P1_VTH56 0xf53700ff
+#define R0900_P1_VTH56 0xf537
+#define VTH56 REGx(R0900_P1_VTH56)
+#define F0900_P1_VTH56 0xf53700ff
/*P1_VTH67*/
-#define R0900_P1_VTH67 0xf538
-#define F0900_P1_VTH67 0xf53800ff
+#define R0900_P1_VTH67 0xf538
+#define VTH67 REGx(R0900_P1_VTH67)
+#define F0900_P1_VTH67 0xf53800ff
/*P1_VTH78*/
-#define R0900_P1_VTH78 0xf539
-#define F0900_P1_VTH78 0xf53900ff
+#define R0900_P1_VTH78 0xf539
+#define VTH78 REGx(R0900_P1_VTH78)
+#define F0900_P1_VTH78 0xf53900ff
/*P1_VITCURPUN*/
-#define R0900_P1_VITCURPUN 0xf53a
-#define F0900_P1_VIT_MAPPING 0xf53a00e0
-#define F0900_P1_VIT_CURPUN 0xf53a001f
+#define R0900_P1_VITCURPUN 0xf53a
+#define VITCURPUN REGx(R0900_P1_VITCURPUN)
+#define F0900_P1_VIT_CURPUN 0xf53a001f
+#define VIT_CURPUN FLDx(F0900_P1_VIT_CURPUN)
/*P1_VERROR*/
-#define R0900_P1_VERROR 0xf53b
-#define F0900_P1_REGERR_VIT 0xf53b00ff
+#define R0900_P1_VERROR 0xf53b
+#define VERROR REGx(R0900_P1_VERROR)
+#define F0900_P1_REGERR_VIT 0xf53b00ff
/*P1_PRVIT*/
-#define R0900_P1_PRVIT 0xf53c
-#define F0900_P1_DIS_VTHLOCK 0xf53c0040
-#define F0900_P1_E7_8VIT 0xf53c0020
-#define F0900_P1_E6_7VIT 0xf53c0010
-#define F0900_P1_E5_6VIT 0xf53c0008
-#define F0900_P1_E3_4VIT 0xf53c0004
-#define F0900_P1_E2_3VIT 0xf53c0002
-#define F0900_P1_E1_2VIT 0xf53c0001
+#define R0900_P1_PRVIT 0xf53c
+#define PRVIT REGx(R0900_P1_PRVIT)
+#define F0900_P1_DIS_VTHLOCK 0xf53c0040
+#define F0900_P1_E7_8VIT 0xf53c0020
+#define F0900_P1_E6_7VIT 0xf53c0010
+#define F0900_P1_E5_6VIT 0xf53c0008
+#define F0900_P1_E3_4VIT 0xf53c0004
+#define F0900_P1_E2_3VIT 0xf53c0002
+#define F0900_P1_E1_2VIT 0xf53c0001
/*P1_VAVSRVIT*/
-#define R0900_P1_VAVSRVIT 0xf53d
-#define F0900_P1_AMVIT 0xf53d0080
-#define F0900_P1_FROZENVIT 0xf53d0040
-#define F0900_P1_SNVIT 0xf53d0030
-#define F0900_P1_TOVVIT 0xf53d000c
-#define F0900_P1_HYPVIT 0xf53d0003
+#define R0900_P1_VAVSRVIT 0xf53d
+#define VAVSRVIT REGx(R0900_P1_VAVSRVIT)
+#define F0900_P1_AMVIT 0xf53d0080
+#define F0900_P1_FROZENVIT 0xf53d0040
+#define F0900_P1_SNVIT 0xf53d0030
+#define F0900_P1_TOVVIT 0xf53d000c
+#define F0900_P1_HYPVIT 0xf53d0003
/*P1_VSTATUSVIT*/
-#define R0900_P1_VSTATUSVIT 0xf53e
-#define F0900_P1_VITERBI_ON 0xf53e0080
-#define F0900_P1_END_LOOPVIT 0xf53e0040
-#define F0900_P1_VITERBI_DEPRF 0xf53e0020
-#define F0900_P1_PRFVIT 0xf53e0010
-#define F0900_P1_LOCKEDVIT 0xf53e0008
-#define F0900_P1_VITERBI_DELOCK 0xf53e0004
-#define F0900_P1_VIT_DEMODSEL 0xf53e0002
-#define F0900_P1_VITERBI_COMPOUT 0xf53e0001
+#define R0900_P1_VSTATUSVIT 0xf53e
+#define VSTATUSVIT REGx(R0900_P1_VSTATUSVIT)
+#define F0900_P1_PRFVIT 0xf53e0010
+#define PRFVIT FLDx(F0900_P1_PRFVIT)
+#define F0900_P1_LOCKEDVIT 0xf53e0008
+#define LOCKEDVIT FLDx(F0900_P1_LOCKEDVIT)
/*P1_VTHINUSE*/
-#define R0900_P1_VTHINUSE 0xf53f
-#define F0900_P1_VIT_INUSE 0xf53f00ff
+#define R0900_P1_VTHINUSE 0xf53f
+#define VTHINUSE REGx(R0900_P1_VTHINUSE)
+#define F0900_P1_VIT_INUSE 0xf53f00ff
/*P1_KDIV12*/
-#define R0900_P1_KDIV12 0xf540
-#define F0900_P1_KDIV12_MANUAL 0xf5400080
-#define F0900_P1_K_DIVIDER_12 0xf540007f
+#define R0900_P1_KDIV12 0xf540
+#define KDIV12 REGx(R0900_P1_KDIV12)
+#define F0900_P1_K_DIVIDER_12 0xf540007f
/*P1_KDIV23*/
-#define R0900_P1_KDIV23 0xf541
-#define F0900_P1_KDIV23_MANUAL 0xf5410080
-#define F0900_P1_K_DIVIDER_23 0xf541007f
+#define R0900_P1_KDIV23 0xf541
+#define KDIV23 REGx(R0900_P1_KDIV23)
+#define F0900_P1_K_DIVIDER_23 0xf541007f
/*P1_KDIV34*/
-#define R0900_P1_KDIV34 0xf542
-#define F0900_P1_KDIV34_MANUAL 0xf5420080
-#define F0900_P1_K_DIVIDER_34 0xf542007f
+#define R0900_P1_KDIV34 0xf542
+#define KDIV34 REGx(R0900_P1_KDIV34)
+#define F0900_P1_K_DIVIDER_34 0xf542007f
/*P1_KDIV56*/
-#define R0900_P1_KDIV56 0xf543
-#define F0900_P1_KDIV56_MANUAL 0xf5430080
-#define F0900_P1_K_DIVIDER_56 0xf543007f
+#define R0900_P1_KDIV56 0xf543
+#define KDIV56 REGx(R0900_P1_KDIV56)
+#define F0900_P1_K_DIVIDER_56 0xf543007f
/*P1_KDIV67*/
-#define R0900_P1_KDIV67 0xf544
-#define F0900_P1_KDIV67_MANUAL 0xf5440080
-#define F0900_P1_K_DIVIDER_67 0xf544007f
+#define R0900_P1_KDIV67 0xf544
+#define KDIV67 REGx(R0900_P1_KDIV67)
+#define F0900_P1_K_DIVIDER_67 0xf544007f
/*P1_KDIV78*/
-#define R0900_P1_KDIV78 0xf545
-#define F0900_P1_KDIV78_MANUAL 0xf5450080
-#define F0900_P1_K_DIVIDER_78 0xf545007f
+#define R0900_P1_KDIV78 0xf545
+#define KDIV78 REGx(R0900_P1_KDIV78)
+#define F0900_P1_K_DIVIDER_78 0xf545007f
/*P1_PDELCTRL1*/
-#define R0900_P1_PDELCTRL1 0xf550
-#define F0900_P1_INV_MISMASK 0xf5500080
-#define F0900_P1_FORCE_ACCEPTED 0xf5500040
-#define F0900_P1_FILTER_EN 0xf5500020
-#define F0900_P1_FORCE_PKTDELINUSE 0xf5500010
-#define F0900_P1_HYSTEN 0xf5500008
-#define F0900_P1_HYSTSWRST 0xf5500004
-#define F0900_P1_EN_MIS00 0xf5500002
-#define F0900_P1_ALGOSWRST 0xf5500001
+#define R0900_P1_PDELCTRL1 0xf550
+#define PDELCTRL1 REGx(R0900_P1_PDELCTRL1)
+#define F0900_P1_INV_MISMASK 0xf5500080
+#define F0900_P1_FILTER_EN 0xf5500020
+#define F0900_P1_EN_MIS00 0xf5500002
+#define F0900_P1_ALGOSWRST 0xf5500001
+#define ALGOSWRST FLDx(F0900_P1_ALGOSWRST)
/*P1_PDELCTRL2*/
-#define R0900_P1_PDELCTRL2 0xf551
-#define F0900_P1_FORCE_CONTINUOUS 0xf5510080
-#define F0900_P1_RESET_UPKO_COUNT 0xf5510040
-#define F0900_P1_USER_PKTDELIN_NB 0xf5510020
-#define F0900_P1_FORCE_LOCKED 0xf5510010
-#define F0900_P1_DATA_UNBBSCRAM 0xf5510008
-#define F0900_P1_FORCE_LONGPKT 0xf5510004
-#define F0900_P1_FRAME_MODE 0xf5510002
+#define R0900_P1_PDELCTRL2 0xf551
+#define PDELCTRL2 REGx(R0900_P1_PDELCTRL2)
+#define F0900_P1_RESET_UPKO_COUNT 0xf5510040
+#define RESET_UPKO_COUNT FLDx(F0900_P1_RESET_UPKO_COUNT)
+#define F0900_P1_FRAME_MODE 0xf5510002
+#define F0900_P1_NOBCHERRFLG_USE 0xf5510001
/*P1_HYSTTHRESH*/
-#define R0900_P1_HYSTTHRESH 0xf554
-#define F0900_P1_UNLCK_THRESH 0xf55400f0
-#define F0900_P1_DELIN_LCK_THRESH 0xf554000f
+#define R0900_P1_HYSTTHRESH 0xf554
+#define HYSTTHRESH REGx(R0900_P1_HYSTTHRESH)
+#define F0900_P1_UNLCK_THRESH 0xf55400f0
+#define F0900_P1_DELIN_LCK_THRESH 0xf554000f
/*P1_ISIENTRY*/
-#define R0900_P1_ISIENTRY 0xf55e
-#define F0900_P1_ISI_ENTRY 0xf55e00ff
+#define R0900_P1_ISIENTRY 0xf55e
+#define ISIENTRY REGx(R0900_P1_ISIENTRY)
+#define F0900_P1_ISI_ENTRY 0xf55e00ff
/*P1_ISIBITENA*/
-#define R0900_P1_ISIBITENA 0xf55f
-#define F0900_P1_ISI_BIT_EN 0xf55f00ff
+#define R0900_P1_ISIBITENA 0xf55f
+#define ISIBITENA REGx(R0900_P1_ISIBITENA)
+#define F0900_P1_ISI_BIT_EN 0xf55f00ff
/*P1_MATSTR1*/
-#define R0900_P1_MATSTR1 0xf560
-#define F0900_P1_MATYPE_CURRENT1 0xf56000ff
+#define R0900_P1_MATSTR1 0xf560
+#define MATSTR1 REGx(R0900_P1_MATSTR1)
+#define F0900_P1_MATYPE_CURRENT1 0xf56000ff
/*P1_MATSTR0*/
-#define R0900_P1_MATSTR0 0xf561
-#define F0900_P1_MATYPE_CURRENT0 0xf56100ff
+#define R0900_P1_MATSTR0 0xf561
+#define MATSTR0 REGx(R0900_P1_MATSTR0)
+#define F0900_P1_MATYPE_CURRENT0 0xf56100ff
/*P1_UPLSTR1*/
-#define R0900_P1_UPLSTR1 0xf562
-#define F0900_P1_UPL_CURRENT1 0xf56200ff
+#define R0900_P1_UPLSTR1 0xf562
+#define UPLSTR1 REGx(R0900_P1_UPLSTR1)
+#define F0900_P1_UPL_CURRENT1 0xf56200ff
/*P1_UPLSTR0*/
-#define R0900_P1_UPLSTR0 0xf563
-#define F0900_P1_UPL_CURRENT0 0xf56300ff
+#define R0900_P1_UPLSTR0 0xf563
+#define UPLSTR0 REGx(R0900_P1_UPLSTR0)
+#define F0900_P1_UPL_CURRENT0 0xf56300ff
/*P1_DFLSTR1*/
-#define R0900_P1_DFLSTR1 0xf564
-#define F0900_P1_DFL_CURRENT1 0xf56400ff
+#define R0900_P1_DFLSTR1 0xf564
+#define DFLSTR1 REGx(R0900_P1_DFLSTR1)
+#define F0900_P1_DFL_CURRENT1 0xf56400ff
/*P1_DFLSTR0*/
-#define R0900_P1_DFLSTR0 0xf565
-#define F0900_P1_DFL_CURRENT0 0xf56500ff
+#define R0900_P1_DFLSTR0 0xf565
+#define DFLSTR0 REGx(R0900_P1_DFLSTR0)
+#define F0900_P1_DFL_CURRENT0 0xf56500ff
/*P1_SYNCSTR*/
-#define R0900_P1_SYNCSTR 0xf566
-#define F0900_P1_SYNC_CURRENT 0xf56600ff
+#define R0900_P1_SYNCSTR 0xf566
+#define SYNCSTR REGx(R0900_P1_SYNCSTR)
+#define F0900_P1_SYNC_CURRENT 0xf56600ff
/*P1_SYNCDSTR1*/
-#define R0900_P1_SYNCDSTR1 0xf567
-#define F0900_P1_SYNCD_CURRENT1 0xf56700ff
+#define R0900_P1_SYNCDSTR1 0xf567
+#define SYNCDSTR1 REGx(R0900_P1_SYNCDSTR1)
+#define F0900_P1_SYNCD_CURRENT1 0xf56700ff
/*P1_SYNCDSTR0*/
-#define R0900_P1_SYNCDSTR0 0xf568
-#define F0900_P1_SYNCD_CURRENT0 0xf56800ff
+#define R0900_P1_SYNCDSTR0 0xf568
+#define SYNCDSTR0 REGx(R0900_P1_SYNCDSTR0)
+#define F0900_P1_SYNCD_CURRENT0 0xf56800ff
/*P1_PDELSTATUS1*/
-#define R0900_P1_PDELSTATUS1 0xf569
-#define F0900_P1_PKTDELIN_DELOCK 0xf5690080
-#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040
-#define F0900_P1_CONTINUOUS_STREAM 0xf5690020
-#define F0900_P1_UNACCEPTED_STREAM 0xf5690010
-#define F0900_P1_BCH_ERROR_FLAG 0xf5690008
-#define F0900_P1_BBHCRCKO 0xf5690004
-#define F0900_P1_PKTDELIN_LOCK 0xf5690002
-#define F0900_P1_FIRST_LOCK 0xf5690001
+#define R0900_P1_PDELSTATUS1 0xf569
+#define F0900_P1_PKTDELIN_DELOCK 0xf5690080
+#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040
+#define F0900_P1_CONTINUOUS_STREAM 0xf5690020
+#define F0900_P1_UNACCEPTED_STREAM 0xf5690010
+#define F0900_P1_BCH_ERROR_FLAG 0xf5690008
+#define F0900_P1_PKTDELIN_LOCK 0xf5690002
+#define PKTDELIN_LOCK FLDx(F0900_P1_PKTDELIN_LOCK)
+#define F0900_P1_FIRST_LOCK 0xf5690001
/*P1_PDELSTATUS2*/
-#define R0900_P1_PDELSTATUS2 0xf56a
-#define F0900_P1_PKTDEL_DEMODSEL 0xf56a0080
-#define F0900_P1_FRAME_MODCOD 0xf56a007c
-#define F0900_P1_FRAME_TYPE 0xf56a0003
+#define R0900_P1_PDELSTATUS2 0xf56a
+#define F0900_P1_FRAME_MODCOD 0xf56a007c
+#define F0900_P1_FRAME_TYPE 0xf56a0003
/*P1_BBFCRCKO1*/
-#define R0900_P1_BBFCRCKO1 0xf56b
-#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff
+#define R0900_P1_BBFCRCKO1 0xf56b
+#define BBFCRCKO1 REGx(R0900_P1_BBFCRCKO1)
+#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff
/*P1_BBFCRCKO0*/
-#define R0900_P1_BBFCRCKO0 0xf56c
-#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff
+#define R0900_P1_BBFCRCKO0 0xf56c
+#define BBFCRCKO0 REGx(R0900_P1_BBFCRCKO0)
+#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff
/*P1_UPCRCKO1*/
-#define R0900_P1_UPCRCKO1 0xf56d
-#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff
+#define R0900_P1_UPCRCKO1 0xf56d
+#define UPCRCKO1 REGx(R0900_P1_UPCRCKO1)
+#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff
/*P1_UPCRCKO0*/
-#define R0900_P1_UPCRCKO0 0xf56e
-#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff
+#define R0900_P1_UPCRCKO0 0xf56e
+#define UPCRCKO0 REGx(R0900_P1_UPCRCKO0)
+#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff
+
+/*P1_PDELCTRL3*/
+#define R0900_P1_PDELCTRL3 0xf56f
+#define PDELCTRL3 REGx(R0900_P1_PDELCTRL3)
+#define F0900_P1_PKTDEL_CONTFAIL 0xf56f0080
+#define F0900_P1_NOFIFO_BCHERR 0xf56f0020
/*P1_TSSTATEM*/
-#define R0900_P1_TSSTATEM 0xf570
-#define F0900_P1_TSDIL_ON 0xf5700080
-#define F0900_P1_TSSKIPRS_ON 0xf5700040
-#define F0900_P1_TSRS_ON 0xf5700020
-#define F0900_P1_TSDESCRAMB_ON 0xf5700010
-#define F0900_P1_TSFRAME_MODE 0xf5700008
-#define F0900_P1_TS_DISABLE 0xf5700004
-#define F0900_P1_TSACM_MODE 0xf5700002
-#define F0900_P1_TSOUT_NOSYNC 0xf5700001
+#define R0900_P1_TSSTATEM 0xf570
+#define TSSTATEM REGx(R0900_P1_TSSTATEM)
+#define F0900_P1_TSDIL_ON 0xf5700080
+#define F0900_P1_TSRS_ON 0xf5700020
+#define F0900_P1_TSDESCRAMB_ON 0xf5700010
+#define F0900_P1_TSFRAME_MODE 0xf5700008
+#define F0900_P1_TS_DISABLE 0xf5700004
+#define F0900_P1_TSOUT_NOSYNC 0xf5700001
/*P1_TSCFGH*/
-#define R0900_P1_TSCFGH 0xf572
-#define F0900_P1_TSFIFO_DVBCI 0xf5720080
-#define F0900_P1_TSFIFO_SERIAL 0xf5720040
-#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020
-#define F0900_P1_TSFIFO_DUTY50 0xf5720010
-#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008
-#define F0900_P1_TSFIFO_ERRMODE 0xf5720006
-#define F0900_P1_RST_HWARE 0xf5720001
+#define R0900_P1_TSCFGH 0xf572
+#define TSCFGH REGx(R0900_P1_TSCFGH)
+#define F0900_P1_TSFIFO_DVBCI 0xf5720080
+#define F0900_P1_TSFIFO_SERIAL 0xf5720040
+#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020
+#define F0900_P1_TSFIFO_DUTY50 0xf5720010
+#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008
+#define F0900_P1_TSFIFO_ERRMODE 0xf5720006
+#define F0900_P1_RST_HWARE 0xf5720001
+#define RST_HWARE FLDx(F0900_P1_RST_HWARE)
/*P1_TSCFGM*/
-#define R0900_P1_TSCFGM 0xf573
-#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0
-#define F0900_P1_TSFIFO_PERMDATA 0xf5730020
-#define F0900_P1_TSFIFO_NONEWSGNL 0xf5730010
-#define F0900_P1_TSFIFO_BITSPEED 0xf5730008
-#define F0900_P1_NPD_SPECDVBS2 0xf5730004
-#define F0900_P1_TSFIFO_STOPCKDIS 0xf5730002
-#define F0900_P1_TSFIFO_INVDATA 0xf5730001
+#define R0900_P1_TSCFGM 0xf573
+#define TSCFGM REGx(R0900_P1_TSCFGM)
+#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0
+#define F0900_P1_TSFIFO_PERMDATA 0xf5730020
+#define F0900_P1_TSFIFO_DPUNACT 0xf5730002
+#define F0900_P1_TSFIFO_INVDATA 0xf5730001
/*P1_TSCFGL*/
-#define R0900_P1_TSCFGL 0xf574
-#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0
-#define F0900_P1_BCHERROR_MODE 0xf5740030
-#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008
-#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004
-#define F0900_P1_TSFIFO_DPUNACT 0xf5740002
-#define F0900_P1_TSFIFO_NPDOFF 0xf5740001
+#define R0900_P1_TSCFGL 0xf574
+#define TSCFGL REGx(R0900_P1_TSCFGL)
+#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0
+#define F0900_P1_BCHERROR_MODE 0xf5740030
+#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008
+#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004
+#define F0900_P1_TSFIFO_BITSPEED 0xf5740003
/*P1_TSINSDELH*/
-#define R0900_P1_TSINSDELH 0xf576
-#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080
-#define F0900_P1_TSDEL_XXHEADER 0xf5760040
-#define F0900_P1_TSDEL_BBHEADER 0xf5760020
-#define F0900_P1_TSDEL_DATAFIELD 0xf5760010
-#define F0900_P1_TSINSDEL_ISCR 0xf5760008
-#define F0900_P1_TSINSDEL_NPD 0xf5760004
-#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002
-#define F0900_P1_TSINSDEL_CRC8 0xf5760001
+#define R0900_P1_TSINSDELH 0xf576
+#define TSINSDELH REGx(R0900_P1_TSINSDELH)
+#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080
+#define F0900_P1_TSDEL_XXHEADER 0xf5760040
+#define F0900_P1_TSDEL_BBHEADER 0xf5760020
+#define F0900_P1_TSDEL_DATAFIELD 0xf5760010
+#define F0900_P1_TSINSDEL_ISCR 0xf5760008
+#define F0900_P1_TSINSDEL_NPD 0xf5760004
+#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002
+#define F0900_P1_TSINSDEL_CRC8 0xf5760001
+
+/*P1_TSDIVN*/
+#define R0900_P1_TSDIVN 0xf579
+#define TSDIVN REGx(R0900_P1_TSDIVN)
+#define F0900_P1_TSFIFO_SPEEDMODE 0xf57900c0
+
+/*P1_TSCFG4*/
+#define R0900_P1_TSCFG4 0xf57a
+#define TSCFG4 REGx(R0900_P1_TSCFG4)
+#define F0900_P1_TSFIFO_TSSPEEDMODE 0xf57a00c0
/*P1_TSSPEED*/
-#define R0900_P1_TSSPEED 0xf580
-#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff
+#define R0900_P1_TSSPEED 0xf580
+#define TSSPEED REGx(R0900_P1_TSSPEED)
+#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff
/*P1_TSSTATUS*/
-#define R0900_P1_TSSTATUS 0xf581
-#define F0900_P1_TSFIFO_LINEOK 0xf5810080
-#define F0900_P1_TSFIFO_ERROR 0xf5810040
-#define F0900_P1_TSFIFO_DATA7 0xf5810020
-#define F0900_P1_TSFIFO_NOSYNC 0xf5810010
-#define F0900_P1_ISCR_INITIALIZED 0xf5810008
-#define F0900_P1_ISCR_UPDATED 0xf5810004
-#define F0900_P1_SOFFIFO_UNREGUL 0xf5810002
-#define F0900_P1_DIL_READY 0xf5810001
+#define R0900_P1_TSSTATUS 0xf581
+#define TSSTATUS REGx(R0900_P1_TSSTATUS)
+#define F0900_P1_TSFIFO_LINEOK 0xf5810080
+#define TSFIFO_LINEOK FLDx(F0900_P1_TSFIFO_LINEOK)
+#define F0900_P1_TSFIFO_ERROR 0xf5810040
+#define F0900_P1_DIL_READY 0xf5810001
/*P1_TSSTATUS2*/
-#define R0900_P1_TSSTATUS2 0xf582
-#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080
-#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040
-#define F0900_P1_DILXX_RESET 0xf5820020
-#define F0900_P1_TSSERIAL_IMPOS 0xf5820010
-#define F0900_P1_TSFIFO_LINENOK 0xf5820008
-#define F0900_P1_BITSPEED_EVENT 0xf5820004
-#define F0900_P1_SCRAMBDETECT 0xf5820002
-#define F0900_P1_ULDTV67_FALSELOCK 0xf5820001
+#define R0900_P1_TSSTATUS2 0xf582
+#define TSSTATUS2 REGx(R0900_P1_TSSTATUS2)
+#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080
+#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040
+#define F0900_P1_DILXX_RESET 0xf5820020
+#define F0900_P1_TSSERIAL_IMPOS 0xf5820010
+#define F0900_P1_SCRAMBDETECT 0xf5820002
/*P1_TSBITRATE1*/
-#define R0900_P1_TSBITRATE1 0xf583
-#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff
+#define R0900_P1_TSBITRATE1 0xf583
+#define TSBITRATE1 REGx(R0900_P1_TSBITRATE1)
+#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff
/*P1_TSBITRATE0*/
-#define R0900_P1_TSBITRATE0 0xf584
-#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff
+#define R0900_P1_TSBITRATE0 0xf584
+#define TSBITRATE0 REGx(R0900_P1_TSBITRATE0)
+#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff
/*P1_ERRCTRL1*/
-#define R0900_P1_ERRCTRL1 0xf598
-#define F0900_P1_ERR_SOURCE1 0xf59800f0
-#define F0900_P1_NUM_EVENT1 0xf5980007
+#define R0900_P1_ERRCTRL1 0xf598
+#define ERRCTRL1 REGx(R0900_P1_ERRCTRL1)
+#define F0900_P1_ERR_SOURCE1 0xf59800f0
+#define F0900_P1_NUM_EVENT1 0xf5980007
/*P1_ERRCNT12*/
-#define R0900_P1_ERRCNT12 0xf599
-#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080
-#define F0900_P1_ERR_CNT12 0xf599007f
+#define R0900_P1_ERRCNT12 0xf599
+#define ERRCNT12 REGx(R0900_P1_ERRCNT12)
+#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080
+#define F0900_P1_ERR_CNT12 0xf599007f
+#define ERR_CNT12 FLDx(F0900_P1_ERR_CNT12)
/*P1_ERRCNT11*/
-#define R0900_P1_ERRCNT11 0xf59a
-#define F0900_P1_ERR_CNT11 0xf59a00ff
+#define R0900_P1_ERRCNT11 0xf59a
+#define ERRCNT11 REGx(R0900_P1_ERRCNT11)
+#define F0900_P1_ERR_CNT11 0xf59a00ff
+#define ERR_CNT11 FLDx(F0900_P1_ERR_CNT11)
/*P1_ERRCNT10*/
-#define R0900_P1_ERRCNT10 0xf59b
-#define F0900_P1_ERR_CNT10 0xf59b00ff
+#define R0900_P1_ERRCNT10 0xf59b
+#define ERRCNT10 REGx(R0900_P1_ERRCNT10)
+#define F0900_P1_ERR_CNT10 0xf59b00ff
+#define ERR_CNT10 FLDx(F0900_P1_ERR_CNT10)
/*P1_ERRCTRL2*/
-#define R0900_P1_ERRCTRL2 0xf59c
-#define F0900_P1_ERR_SOURCE2 0xf59c00f0
-#define F0900_P1_NUM_EVENT2 0xf59c0007
+#define R0900_P1_ERRCTRL2 0xf59c
+#define ERRCTRL2 REGx(R0900_P1_ERRCTRL2)
+#define F0900_P1_ERR_SOURCE2 0xf59c00f0
+#define F0900_P1_NUM_EVENT2 0xf59c0007
/*P1_ERRCNT22*/
-#define R0900_P1_ERRCNT22 0xf59d
-#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080
-#define F0900_P1_ERR_CNT22 0xf59d007f
+#define R0900_P1_ERRCNT22 0xf59d
+#define ERRCNT22 REGx(R0900_P1_ERRCNT22)
+#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080
+#define F0900_P1_ERR_CNT22 0xf59d007f
+#define ERR_CNT22 FLDx(F0900_P1_ERR_CNT22)
/*P1_ERRCNT21*/
-#define R0900_P1_ERRCNT21 0xf59e
-#define F0900_P1_ERR_CNT21 0xf59e00ff
+#define R0900_P1_ERRCNT21 0xf59e
+#define ERRCNT21 REGx(R0900_P1_ERRCNT21)
+#define F0900_P1_ERR_CNT21 0xf59e00ff
+#define ERR_CNT21 FLDx(F0900_P1_ERR_CNT21)
/*P1_ERRCNT20*/
-#define R0900_P1_ERRCNT20 0xf59f
-#define F0900_P1_ERR_CNT20 0xf59f00ff
+#define R0900_P1_ERRCNT20 0xf59f
+#define ERRCNT20 REGx(R0900_P1_ERRCNT20)
+#define F0900_P1_ERR_CNT20 0xf59f00ff
+#define ERR_CNT20 FLDx(F0900_P1_ERR_CNT20)
/*P1_FECSPY*/
-#define R0900_P1_FECSPY 0xf5a0
-#define F0900_P1_SPY_ENABLE 0xf5a00080
-#define F0900_P1_NO_SYNCBYTE 0xf5a00040
-#define F0900_P1_SERIAL_MODE 0xf5a00020
-#define F0900_P1_UNUSUAL_PACKET 0xf5a00010
-#define F0900_P1_BER_PACKMODE 0xf5a00008
-#define F0900_P1_BERMETER_LMODE 0xf5a00002
-#define F0900_P1_BERMETER_RESET 0xf5a00001
+#define R0900_P1_FECSPY 0xf5a0
+#define FECSPY REGx(R0900_P1_FECSPY)
+#define F0900_P1_SPY_ENABLE 0xf5a00080
+#define F0900_P1_NO_SYNCBYTE 0xf5a00040
+#define F0900_P1_SERIAL_MODE 0xf5a00020
+#define F0900_P1_UNUSUAL_PACKET 0xf5a00010
+#define F0900_P1_BERMETER_DATAMODE 0xf5a00008
+#define F0900_P1_BERMETER_LMODE 0xf5a00002
+#define F0900_P1_BERMETER_RESET 0xf5a00001
/*P1_FSPYCFG*/
-#define R0900_P1_FSPYCFG 0xf5a1
-#define F0900_P1_FECSPY_INPUT 0xf5a100c0
-#define F0900_P1_RST_ON_ERROR 0xf5a10020
-#define F0900_P1_ONE_SHOT 0xf5a10010
-#define F0900_P1_I2C_MODE 0xf5a1000c
-#define F0900_P1_SPY_HYSTERESIS 0xf5a10003
+#define R0900_P1_FSPYCFG 0xf5a1
+#define FSPYCFG REGx(R0900_P1_FSPYCFG)
+#define F0900_P1_FECSPY_INPUT 0xf5a100c0
+#define F0900_P1_RST_ON_ERROR 0xf5a10020
+#define F0900_P1_ONE_SHOT 0xf5a10010
+#define F0900_P1_I2C_MODE 0xf5a1000c
+#define F0900_P1_SPY_HYSTERESIS 0xf5a10003
/*P1_FSPYDATA*/
-#define R0900_P1_FSPYDATA 0xf5a2
-#define F0900_P1_SPY_STUFFING 0xf5a20080
-#define F0900_P1_NOERROR_PKTJITTER 0xf5a20040
-#define F0900_P1_SPY_CNULLPKT 0xf5a20020
-#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f
+#define R0900_P1_FSPYDATA 0xf5a2
+#define FSPYDATA REGx(R0900_P1_FSPYDATA)
+#define F0900_P1_SPY_STUFFING 0xf5a20080
+#define F0900_P1_SPY_CNULLPKT 0xf5a20020
+#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f
/*P1_FSPYOUT*/
-#define R0900_P1_FSPYOUT 0xf5a3
-#define F0900_P1_FSPY_DIRECT 0xf5a30080
-#define F0900_P1_SPY_OUTDATA_BUS 0xf5a30038
-#define F0900_P1_STUFF_MODE 0xf5a30007
+#define R0900_P1_FSPYOUT 0xf5a3
+#define FSPYOUT REGx(R0900_P1_FSPYOUT)
+#define F0900_P1_FSPY_DIRECT 0xf5a30080
+#define F0900_P1_STUFF_MODE 0xf5a30007
/*P1_FSTATUS*/
-#define R0900_P1_FSTATUS 0xf5a4
-#define F0900_P1_SPY_ENDSIM 0xf5a40080
-#define F0900_P1_VALID_SIM 0xf5a40040
-#define F0900_P1_FOUND_SIGNAL 0xf5a40020
-#define F0900_P1_DSS_SYNCBYTE 0xf5a40010
-#define F0900_P1_RESULT_STATE 0xf5a4000f
+#define R0900_P1_FSTATUS 0xf5a4
+#define FSTATUS REGx(R0900_P1_FSTATUS)
+#define F0900_P1_SPY_ENDSIM 0xf5a40080
+#define F0900_P1_VALID_SIM 0xf5a40040
+#define F0900_P1_FOUND_SIGNAL 0xf5a40020
+#define F0900_P1_DSS_SYNCBYTE 0xf5a40010
+#define F0900_P1_RESULT_STATE 0xf5a4000f
/*P1_FBERCPT4*/
-#define R0900_P1_FBERCPT4 0xf5a8
-#define F0900_P1_FBERMETER_CPT4 0xf5a800ff
+#define R0900_P1_FBERCPT4 0xf5a8
+#define FBERCPT4 REGx(R0900_P1_FBERCPT4)
+#define F0900_P1_FBERMETER_CPT4 0xf5a800ff
/*P1_FBERCPT3*/
-#define R0900_P1_FBERCPT3 0xf5a9
-#define F0900_P1_FBERMETER_CPT3 0xf5a900ff
+#define R0900_P1_FBERCPT3 0xf5a9
+#define FBERCPT3 REGx(R0900_P1_FBERCPT3)
+#define F0900_P1_FBERMETER_CPT3 0xf5a900ff
/*P1_FBERCPT2*/
-#define R0900_P1_FBERCPT2 0xf5aa
-#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff
+#define R0900_P1_FBERCPT2 0xf5aa
+#define FBERCPT2 REGx(R0900_P1_FBERCPT2)
+#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff
/*P1_FBERCPT1*/
-#define R0900_P1_FBERCPT1 0xf5ab
-#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff
+#define R0900_P1_FBERCPT1 0xf5ab
+#define FBERCPT1 REGx(R0900_P1_FBERCPT1)
+#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff
/*P1_FBERCPT0*/
-#define R0900_P1_FBERCPT0 0xf5ac
-#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff
+#define R0900_P1_FBERCPT0 0xf5ac
+#define FBERCPT0 REGx(R0900_P1_FBERCPT0)
+#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff
/*P1_FBERERR2*/
-#define R0900_P1_FBERERR2 0xf5ad
-#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff
+#define R0900_P1_FBERERR2 0xf5ad
+#define FBERERR2 REGx(R0900_P1_FBERERR2)
+#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff
/*P1_FBERERR1*/
-#define R0900_P1_FBERERR1 0xf5ae
-#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff
+#define R0900_P1_FBERERR1 0xf5ae
+#define FBERERR1 REGx(R0900_P1_FBERERR1)
+#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff
/*P1_FBERERR0*/
-#define R0900_P1_FBERERR0 0xf5af
-#define F0900_P1_FBERMETER_ERR0 0xf5af00ff
+#define R0900_P1_FBERERR0 0xf5af
+#define FBERERR0 REGx(R0900_P1_FBERERR0)
+#define F0900_P1_FBERMETER_ERR0 0xf5af00ff
/*P1_FSPYBER*/
-#define R0900_P1_FSPYBER 0xf5b2
-#define F0900_P1_FSPYOBS_XORREAD 0xf5b20040
-#define F0900_P1_FSPYBER_OBSMODE 0xf5b20020
-#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010
-#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008
-#define F0900_P1_FSPYBER_CTIME 0xf5b20007
-
-/*RCCFGH*/
-#define R0900_RCCFGH 0xf600
-#define F0900_TSRCFIFO_DVBCI 0xf6000080
-#define F0900_TSRCFIFO_SERIAL 0xf6000040
-#define F0900_TSRCFIFO_DISABLE 0xf6000020
-#define F0900_TSFIFO_2TORC 0xf6000010
-#define F0900_TSRCFIFO_HSGNLOUT 0xf6000008
-#define F0900_TSRCFIFO_ERRMODE 0xf6000006
+#define R0900_P1_FSPYBER 0xf5b2
+#define FSPYBER REGx(R0900_P1_FSPYBER)
+#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010
+#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008
+#define F0900_P1_FSPYBER_CTIME 0xf5b20007
+
+/*RCCFG2*/
+#define R0900_RCCFG2 0xf600
/*TSGENERAL*/
-#define R0900_TSGENERAL 0xf630
-#define F0900_TSFIFO_BCLK1ALL 0xf6300020
-#define F0900_MUXSTREAM_OUTMODE 0xf6300008
-#define F0900_TSFIFO_PERMPARAL 0xf6300006
-#define F0900_RST_REEDSOLO 0xf6300001
+#define R0900_TSGENERAL 0xf630
+#define F0900_TSFIFO_DISTS2PAR 0xf6300040
+#define F0900_MUXSTREAM_OUTMODE 0xf6300008
+#define F0900_TSFIFO_PERMPARAL 0xf6300006
/*TSGENERAL1X*/
-#define R0900_TSGENERAL1X 0xf670
-#define F0900_TSFIFO1X_BCLK1ALL 0xf6700020
-#define F0900_MUXSTREAM1X_OUTMODE 0xf6700008
-#define F0900_TSFIFO1X_PERMPARAL 0xf6700006
-#define F0900_RST1X_REEDSOLO 0xf6700001
+#define R0900_TSGENERAL1X 0xf670
/*NBITER_NF4*/
-#define R0900_NBITER_NF4 0xfa03
-#define F0900_NBITER_NF_QP_1_2 0xfa0300ff
+#define R0900_NBITER_NF4 0xfa03
+#define F0900_NBITER_NF_QP_1_2 0xfa0300ff
/*NBITER_NF5*/
-#define R0900_NBITER_NF5 0xfa04
-#define F0900_NBITER_NF_QP_3_5 0xfa0400ff
+#define R0900_NBITER_NF5 0xfa04
+#define F0900_NBITER_NF_QP_3_5 0xfa0400ff
/*NBITER_NF6*/
-#define R0900_NBITER_NF6 0xfa05
-#define F0900_NBITER_NF_QP_2_3 0xfa0500ff
+#define R0900_NBITER_NF6 0xfa05
+#define F0900_NBITER_NF_QP_2_3 0xfa0500ff
/*NBITER_NF7*/
-#define R0900_NBITER_NF7 0xfa06
-#define F0900_NBITER_NF_QP_3_4 0xfa0600ff
+#define R0900_NBITER_NF7 0xfa06
+#define F0900_NBITER_NF_QP_3_4 0xfa0600ff
/*NBITER_NF8*/
-#define R0900_NBITER_NF8 0xfa07
-#define F0900_NBITER_NF_QP_4_5 0xfa0700ff
+#define R0900_NBITER_NF8 0xfa07
+#define F0900_NBITER_NF_QP_4_5 0xfa0700ff
/*NBITER_NF9*/
-#define R0900_NBITER_NF9 0xfa08
-#define F0900_NBITER_NF_QP_5_6 0xfa0800ff
+#define R0900_NBITER_NF9 0xfa08
+#define F0900_NBITER_NF_QP_5_6 0xfa0800ff
/*NBITER_NF10*/
-#define R0900_NBITER_NF10 0xfa09
-#define F0900_NBITER_NF_QP_8_9 0xfa0900ff
+#define R0900_NBITER_NF10 0xfa09
+#define F0900_NBITER_NF_QP_8_9 0xfa0900ff
/*NBITER_NF11*/
-#define R0900_NBITER_NF11 0xfa0a
-#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff
+#define R0900_NBITER_NF11 0xfa0a
+#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff
/*NBITER_NF12*/
-#define R0900_NBITER_NF12 0xfa0b
-#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff
+#define R0900_NBITER_NF12 0xfa0b
+#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff
/*NBITER_NF13*/
-#define R0900_NBITER_NF13 0xfa0c
-#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff
+#define R0900_NBITER_NF13 0xfa0c
+#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff
/*NBITER_NF14*/
-#define R0900_NBITER_NF14 0xfa0d
-#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff
+#define R0900_NBITER_NF14 0xfa0d
+#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff
/*NBITER_NF15*/
-#define R0900_NBITER_NF15 0xfa0e
-#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff
+#define R0900_NBITER_NF15 0xfa0e
+#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff
/*NBITER_NF16*/
-#define R0900_NBITER_NF16 0xfa0f
-#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff
+#define R0900_NBITER_NF16 0xfa0f
+#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff
/*NBITER_NF17*/
-#define R0900_NBITER_NF17 0xfa10
-#define F0900_NBITER_NF_8P_9_10 0xfa1000ff
+#define R0900_NBITER_NF17 0xfa10
+#define F0900_NBITER_NF_8P_9_10 0xfa1000ff
/*NBITERNOERR*/
-#define R0900_NBITERNOERR 0xfa3f
-#define F0900_NBITER_STOP_CRIT 0xfa3f000f
+#define R0900_NBITERNOERR 0xfa3f
+#define F0900_NBITER_STOP_CRIT 0xfa3f000f
/*GAINLLR_NF4*/
-#define R0900_GAINLLR_NF4 0xfa43
-#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f
+#define R0900_GAINLLR_NF4 0xfa43
+#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f
/*GAINLLR_NF5*/
-#define R0900_GAINLLR_NF5 0xfa44
-#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f
+#define R0900_GAINLLR_NF5 0xfa44
+#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f
/*GAINLLR_NF6*/
-#define R0900_GAINLLR_NF6 0xfa45
-#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f
+#define R0900_GAINLLR_NF6 0xfa45
+#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f
/*GAINLLR_NF7*/
-#define R0900_GAINLLR_NF7 0xfa46
-#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f
+#define R0900_GAINLLR_NF7 0xfa46
+#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f
/*GAINLLR_NF8*/
-#define R0900_GAINLLR_NF8 0xfa47
-#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f
+#define R0900_GAINLLR_NF8 0xfa47
+#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f
/*GAINLLR_NF9*/
-#define R0900_GAINLLR_NF9 0xfa48
-#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f
+#define R0900_GAINLLR_NF9 0xfa48
+#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f
/*GAINLLR_NF10*/
-#define R0900_GAINLLR_NF10 0xfa49
-#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f
+#define R0900_GAINLLR_NF10 0xfa49
+#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f
/*GAINLLR_NF11*/
-#define R0900_GAINLLR_NF11 0xfa4a
-#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f
+#define R0900_GAINLLR_NF11 0xfa4a
+#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f
/*GAINLLR_NF12*/
-#define R0900_GAINLLR_NF12 0xfa4b
-#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f
+#define R0900_GAINLLR_NF12 0xfa4b
+#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f
/*GAINLLR_NF13*/
-#define R0900_GAINLLR_NF13 0xfa4c
-#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f
+#define R0900_GAINLLR_NF13 0xfa4c
+#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f
/*GAINLLR_NF14*/
-#define R0900_GAINLLR_NF14 0xfa4d
-#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f
+#define R0900_GAINLLR_NF14 0xfa4d
+#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f
/*GAINLLR_NF15*/
-#define R0900_GAINLLR_NF15 0xfa4e
-#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f
+#define R0900_GAINLLR_NF15 0xfa4e
+#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f
/*GAINLLR_NF16*/
-#define R0900_GAINLLR_NF16 0xfa4f
-#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f
+#define R0900_GAINLLR_NF16 0xfa4f
+#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f
/*GAINLLR_NF17*/
-#define R0900_GAINLLR_NF17 0xfa50
-#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f
+#define R0900_GAINLLR_NF17 0xfa50
+#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f
/*CFGEXT*/
-#define R0900_CFGEXT 0xfa80
-#define F0900_STAGMODE 0xfa800080
-#define F0900_BYPBCH 0xfa800040
-#define F0900_BYPLDPC 0xfa800020
-#define F0900_LDPCMODE 0xfa800010
-#define F0900_INVLLRSIGN 0xfa800008
-#define F0900_SHORTMULT 0xfa800004
-#define F0900_EXTERNTX 0xfa800001
+#define R0900_CFGEXT 0xfa80
+#define F0900_STAGMODE 0xfa800080
+#define F0900_BYPBCH 0xfa800040
+#define F0900_BYPLDPC 0xfa800020
+#define F0900_LDPCMODE 0xfa800010
+#define F0900_INVLLRSIGN 0xfa800008
+#define F0900_SHORTMULT 0xfa800004
+#define F0900_EXTERNTX 0xfa800001
/*GENCFG*/
-#define R0900_GENCFG 0xfa86
-#define F0900_BROADCAST 0xfa860010
-#define F0900_NOSHFRD2 0xfa860008
-#define F0900_BCHERRFLAG 0xfa860004
-#define F0900_PRIORITY 0xfa860002
-#define F0900_DDEMOD 0xfa860001
+#define R0900_GENCFG 0xfa86
+#define F0900_BROADCAST 0xfa860010
+#define F0900_PRIORITY 0xfa860002
+#define F0900_DDEMOD 0xfa860001
/*LDPCERR1*/
-#define R0900_LDPCERR1 0xfa96
-#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff
+#define R0900_LDPCERR1 0xfa96
+#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff
/*LDPCERR0*/
-#define R0900_LDPCERR0 0xfa97
-#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff
+#define R0900_LDPCERR0 0xfa97
+#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff
/*BCHERR*/
-#define R0900_BCHERR 0xfa98
-#define F0900_ERRORFLAG 0xfa980010
-#define F0900_BCH_ERRORS_COUNTER 0xfa98000f
+#define R0900_BCHERR 0xfa98
+#define F0900_ERRORFLAG 0xfa980010
+#define F0900_BCH_ERRORS_COUNTER 0xfa98000f
/*TSTRES0*/
-#define R0900_TSTRES0 0xff11
-#define F0900_FRESFEC 0xff110080
-#define F0900_FRESTS 0xff110040
-#define F0900_FRESVIT1 0xff110020
-#define F0900_FRESVIT2 0xff110010
-#define F0900_FRESSYM1 0xff110008
-#define F0900_FRESSYM2 0xff110004
-#define F0900_FRESMAS 0xff110002
-#define F0900_FRESINT 0xff110001
+#define R0900_TSTRES0 0xff11
+#define F0900_FRESFEC 0xff110080
+
+/*P2_TCTL4*/
+#define R0900_P2_TCTL4 0xff28
+#define F0900_P2_PN4_SELECT 0xff280020
+
+/*P1_TCTL4*/
+#define R0900_P1_TCTL4 0xff48
+#define TCTL4 shiftx(R0900_P1_TCTL4, demod, 0x20)
+#define F0900_P1_PN4_SELECT 0xff480020
/*P2_TSTDISRX*/
-#define R0900_P2_TSTDISRX 0xff65
-#define F0900_P2_EN_DISRX 0xff650080
-#define F0900_P2_TST_CURRSRC 0xff650040
-#define F0900_P2_IN_DIGSIGNAL 0xff650020
-#define F0900_P2_HIZ_CURRENTSRC 0xff650010
-#define F0900_TST_P2_PIN_SELECT 0xff650008
-#define F0900_P2_TST_DISRX 0xff650007
+#define R0900_P2_TSTDISRX 0xff65
+#define F0900_P2_PIN_SELECT1 0xff650008
/*P1_TSTDISRX*/
-#define R0900_P1_TSTDISRX 0xff67
-#define F0900_P1_EN_DISRX 0xff670080
-#define F0900_P1_TST_CURRSRC 0xff670040
-#define F0900_P1_IN_DIGSIGNAL 0xff670020
-#define F0900_P1_HIZ_CURRENTSRC 0xff670010
-#define F0900_TST_P1_PIN_SELECT 0xff670008
-#define F0900_P1_TST_DISRX 0xff670007
-
-#define STV0900_NBREGS 684
-#define STV0900_NBFIELDS 1702
+#define R0900_P1_TSTDISRX 0xff67
+#define TSTDISRX shiftx(R0900_P1_TSTDISRX, demod, 2)
+#define F0900_P1_PIN_SELECT1 0xff670008
+#define PIN_SELECT1 shiftx(F0900_P1_PIN_SELECT1, demod, 0x20000)
+
+#define STV0900_NBREGS 723
+#define STV0900_NBFIELDS 1420
#endif
diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c
index 962fde1437c..b8da87fa637 100644
--- a/drivers/media/dvb/frontends/stv0900_sw.c
+++ b/drivers/media/dvb/frontends/stv0900_sw.c
@@ -27,56 +27,45 @@
#include "stv0900_reg.h"
#include "stv0900_priv.h"
-int stv0900_check_signal_presence(struct stv0900_internal *i_params,
+s32 shiftx(s32 x, int demod, s32 shift)
+{
+ if (demod == 1)
+ return x - shift;
+
+ return x;
+}
+
+int stv0900_check_signal_presence(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
- s32 carr_offset,
- agc2_integr,
- max_carrier;
+ s32 carr_offset,
+ agc2_integr,
+ max_carrier;
- int no_signal;
+ int no_signal = FALSE;
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- carr_offset = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8)
- | stv0900_read_reg(i_params,
- R0900_P1_CFR1);
- carr_offset = ge2comp(carr_offset, 16);
- agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
- | stv0900_read_reg(i_params,
- R0900_P1_AGC2I0);
- max_carrier = i_params->dmd1_srch_range / 1000;
- break;
- case STV0900_DEMOD_2:
- carr_offset = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8)
- | stv0900_read_reg(i_params,
- R0900_P2_CFR1);
- carr_offset = ge2comp(carr_offset, 16);
- agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
- | stv0900_read_reg(i_params,
- R0900_P2_AGC2I0);
- max_carrier = i_params->dmd2_srch_range / 1000;
- break;
- }
+ carr_offset = (stv0900_read_reg(intp, CFR2) << 8)
+ | stv0900_read_reg(intp, CFR1);
+ carr_offset = ge2comp(carr_offset, 16);
+ agc2_integr = (stv0900_read_reg(intp, AGC2I1) << 8)
+ | stv0900_read_reg(intp, AGC2I0);
+ max_carrier = intp->srch_range[demod] / 1000;
max_carrier += (max_carrier / 10);
max_carrier = 65536 * (max_carrier / 2);
- max_carrier /= i_params->mclk / 1000;
+ max_carrier /= intp->mclk / 1000;
if (max_carrier > 0x4000)
max_carrier = 0x4000;
if ((agc2_integr > 0x2000)
- || (carr_offset > + 2*max_carrier)
- || (carr_offset < -2*max_carrier))
+ || (carr_offset > (2 * max_carrier))
+ || (carr_offset < (-2 * max_carrier)))
no_signal = TRUE;
- else
- no_signal = FALSE;
return no_signal;
}
-static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params,
+static void stv0900_get_sw_loop_params(struct stv0900_internal *intp,
s32 *frequency_inc, s32 *sw_timeout,
s32 *steps,
enum fe_stv0900_demod_num demod)
@@ -85,30 +74,19 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params,
enum fe_stv0900_search_standard standard;
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- srate = i_params->dmd1_symbol_rate;
- max_carrier = i_params->dmd1_srch_range / 1000;
- max_carrier += max_carrier / 10;
- standard = i_params->dmd1_srch_standard;
- break;
- case STV0900_DEMOD_2:
- srate = i_params->dmd2_symbol_rate;
- max_carrier = i_params->dmd2_srch_range / 1000;
- max_carrier += max_carrier / 10;
- standard = i_params->dmd2_srch_stndrd;
- break;
- }
+ srate = intp->symbol_rate[demod];
+ max_carrier = intp->srch_range[demod] / 1000;
+ max_carrier += max_carrier / 10;
+ standard = intp->srch_standard[demod];
max_carrier = 65536 * (max_carrier / 2);
- max_carrier /= i_params->mclk / 1000;
+ max_carrier /= intp->mclk / 1000;
if (max_carrier > 0x4000)
max_carrier = 0x4000;
freq_inc = srate;
- freq_inc /= i_params->mclk >> 10;
+ freq_inc /= intp->mclk >> 10;
freq_inc = freq_inc << 6;
switch (standard) {
@@ -154,7 +132,7 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params,
}
-static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
+static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp,
s32 FreqIncr, s32 Timeout, int zigzag,
s32 MaxStep, enum fe_stv0900_demod_num demod)
{
@@ -164,20 +142,11 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
freqOffset,
max_carrier;
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- max_carrier = i_params->dmd1_srch_range / 1000;
- max_carrier += (max_carrier / 10);
- break;
- case STV0900_DEMOD_2:
- max_carrier = i_params->dmd2_srch_range / 1000;
- max_carrier += (max_carrier / 10);
- break;
- }
+ max_carrier = intp->srch_range[demod] / 1000;
+ max_carrier += (max_carrier / 10);
max_carrier = 65536 * (max_carrier / 2);
- max_carrier /= i_params->mclk / 1000;
+ max_carrier /= intp->mclk / 1000;
if (max_carrier > 0x4000)
max_carrier = 0x4000;
@@ -190,40 +159,15 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
stepCpt = 0;
do {
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1,
- (freqOffset / 256) & 0xFF);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0,
- freqOffset & 0xFF);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
- stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1);
-
- if (i_params->chip_id == 0x12) {
- stv0900_write_bits(i_params,
- F0900_P1_RST_HWARE, 1);
- stv0900_write_bits(i_params,
- F0900_P1_RST_HWARE, 0);
- }
- break;
- case STV0900_DEMOD_2:
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1,
- (freqOffset / 256) & 0xFF);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0,
- freqOffset & 0xFF);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
- stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1);
-
- if (i_params->chip_id == 0x12) {
- stv0900_write_bits(i_params,
- F0900_P2_RST_HWARE, 1);
- stv0900_write_bits(i_params,
- F0900_P2_RST_HWARE, 0);
- }
- break;
+ stv0900_write_reg(intp, DMDISTATE, 0x1c);
+ stv0900_write_reg(intp, CFRINIT1, (freqOffset / 256) & 0xff);
+ stv0900_write_reg(intp, CFRINIT0, freqOffset & 0xff);
+ stv0900_write_reg(intp, DMDISTATE, 0x18);
+ stv0900_write_bits(intp, ALGOSWRST, 1);
+
+ if (intp->chip_id == 0x12) {
+ stv0900_write_bits(intp, RST_HWARE, 1);
+ stv0900_write_bits(intp, RST_HWARE, 0);
}
if (zigzag == TRUE) {
@@ -235,8 +179,8 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
freqOffset += + 2 * FreqIncr;
stepCpt++;
- lock = stv0900_get_demod_lock(i_params, demod, Timeout);
- no_signal = stv0900_check_signal_presence(i_params, demod);
+ lock = stv0900_get_demod_lock(intp, demod, Timeout);
+ no_signal = stv0900_check_signal_presence(intp, demod);
} while ((lock == FALSE)
&& (no_signal == FALSE)
@@ -244,269 +188,138 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
&& ((freqOffset + FreqIncr) > -max_carrier)
&& (stepCpt < MaxStep));
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0);
- break;
- case STV0900_DEMOD_2:
- stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0);
- break;
- }
+ stv0900_write_bits(intp, ALGOSWRST, 0);
return lock;
}
-int stv0900_sw_algo(struct stv0900_internal *i_params,
+int stv0900_sw_algo(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
- int lock = FALSE;
-
- int no_signal,
- zigzag;
- s32 dvbs2_fly_wheel;
-
- s32 freqIncrement, softStepTimeout, trialCounter, max_steps;
-
- stv0900_get_sw_loop_params(i_params, &freqIncrement, &softStepTimeout,
+ int lock = FALSE,
+ no_signal,
+ zigzag;
+ s32 s2fw,
+ fqc_inc,
+ sft_stp_tout,
+ trial_cntr,
+ max_steps;
+
+ stv0900_get_sw_loop_params(intp, &fqc_inc, &sft_stp_tout,
&max_steps, demod);
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- switch (i_params->dmd1_srch_standard) {
- case STV0900_SEARCH_DVBS1:
- case STV0900_SEARCH_DSS:
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P1_CARFREQ,
- 0x3B);
- else
- stv0900_write_reg(i_params, R0900_P1_CARFREQ,
- 0xef);
-
- stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x49);
- zigzag = FALSE;
- break;
- case STV0900_SEARCH_DVBS2:
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P1_CORRELABS,
- 0x79);
- else
- stv0900_write_reg(i_params, R0900_P1_CORRELABS,
- 0x68);
+ switch (intp->srch_standard[demod]) {
+ case STV0900_SEARCH_DVBS1:
+ case STV0900_SEARCH_DSS:
+ if (intp->chip_id >= 0x20)
+ stv0900_write_reg(intp, CARFREQ, 0x3b);
+ else
+ stv0900_write_reg(intp, CARFREQ, 0xef);
- stv0900_write_reg(i_params, R0900_P1_DMDCFGMD,
- 0x89);
+ stv0900_write_reg(intp, DMDCFGMD, 0x49);
+ zigzag = FALSE;
+ break;
+ case STV0900_SEARCH_DVBS2:
+ if (intp->chip_id >= 0x20)
+ stv0900_write_reg(intp, CORRELABS, 0x79);
+ else
+ stv0900_write_reg(intp, CORRELABS, 0x68);
- zigzag = TRUE;
- break;
- case STV0900_AUTO_SEARCH:
- default:
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P1_CARFREQ,
- 0x3B);
- stv0900_write_reg(i_params, R0900_P1_CORRELABS,
- 0x79);
- } else {
- stv0900_write_reg(i_params, R0900_P1_CARFREQ,
- 0xef);
- stv0900_write_reg(i_params, R0900_P1_CORRELABS,
- 0x68);
- }
+ stv0900_write_reg(intp, DMDCFGMD, 0x89);
- stv0900_write_reg(i_params, R0900_P1_DMDCFGMD,
- 0xc9);
- zigzag = FALSE;
- break;
+ zigzag = TRUE;
+ break;
+ case STV0900_AUTO_SEARCH:
+ default:
+ if (intp->chip_id >= 0x20) {
+ stv0900_write_reg(intp, CARFREQ, 0x3b);
+ stv0900_write_reg(intp, CORRELABS, 0x79);
+ } else {
+ stv0900_write_reg(intp, CARFREQ, 0xef);
+ stv0900_write_reg(intp, CORRELABS, 0x68);
}
- trialCounter = 0;
- do {
- lock = stv0900_search_carr_sw_loop(i_params,
- freqIncrement,
- softStepTimeout,
- zigzag,
- max_steps,
- demod);
- no_signal = stv0900_check_signal_presence(i_params,
- demod);
- trialCounter++;
- if ((lock == TRUE)
- || (no_signal == TRUE)
- || (trialCounter == 2)) {
-
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params,
- R0900_P1_CARFREQ,
- 0x49);
- stv0900_write_reg(i_params,
- R0900_P1_CORRELABS,
- 0x9e);
- } else {
- stv0900_write_reg(i_params,
- R0900_P1_CARFREQ,
- 0xed);
- stv0900_write_reg(i_params,
- R0900_P1_CORRELABS,
- 0x88);
- }
-
- if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS2_FOUND)) {
- msleep(softStepTimeout);
- dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT);
-
- if (dvbs2_fly_wheel < 0xd) {
- msleep(softStepTimeout);
- dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT);
- }
-
- if (dvbs2_fly_wheel < 0xd) {
- lock = FALSE;
-
- if (trialCounter < 2) {
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x79);
- else
- stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x68);
-
- stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x89);
- }
- }
- }
- }
-
- } while ((lock == FALSE)
- && (trialCounter < 2)
- && (no_signal == FALSE));
-
+ stv0900_write_reg(intp, DMDCFGMD, 0xc9);
+ zigzag = FALSE;
break;
- case STV0900_DEMOD_2:
- switch (i_params->dmd2_srch_stndrd) {
- case STV0900_SEARCH_DVBS1:
- case STV0900_SEARCH_DSS:
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P2_CARFREQ,
- 0x3b);
- else
- stv0900_write_reg(i_params, R0900_P2_CARFREQ,
- 0xef);
-
- stv0900_write_reg(i_params, R0900_P2_DMDCFGMD,
- 0x49);
- zigzag = FALSE;
- break;
- case STV0900_SEARCH_DVBS2:
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P2_CORRELABS,
- 0x79);
- else
- stv0900_write_reg(i_params, R0900_P2_CORRELABS,
- 0x68);
+ }
- stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89);
- zigzag = TRUE;
- break;
- case STV0900_AUTO_SEARCH:
- default:
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P2_CARFREQ,
- 0x3b);
- stv0900_write_reg(i_params, R0900_P2_CORRELABS,
- 0x79);
+ trial_cntr = 0;
+ do {
+ lock = stv0900_search_carr_sw_loop(intp,
+ fqc_inc,
+ sft_stp_tout,
+ zigzag,
+ max_steps,
+ demod);
+ no_signal = stv0900_check_signal_presence(intp, demod);
+ trial_cntr++;
+ if ((lock == TRUE)
+ || (no_signal == TRUE)
+ || (trial_cntr == 2)) {
+
+ if (intp->chip_id >= 0x20) {
+ stv0900_write_reg(intp, CARFREQ, 0x49);
+ stv0900_write_reg(intp, CORRELABS, 0x9e);
} else {
- stv0900_write_reg(i_params, R0900_P2_CARFREQ,
- 0xef);
- stv0900_write_reg(i_params, R0900_P2_CORRELABS,
- 0x68);
+ stv0900_write_reg(intp, CARFREQ, 0xed);
+ stv0900_write_reg(intp, CORRELABS, 0x88);
}
- stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0xc9);
-
- zigzag = FALSE;
- break;
- }
+ if ((stv0900_get_bits(intp, HEADER_MODE) ==
+ STV0900_DVBS2_FOUND) &&
+ (lock == TRUE)) {
+ msleep(sft_stp_tout);
+ s2fw = stv0900_get_bits(intp, FLYWHEEL_CPT);
- trialCounter = 0;
-
- do {
- lock = stv0900_search_carr_sw_loop(i_params,
- freqIncrement,
- softStepTimeout,
- zigzag,
- max_steps,
- demod);
- no_signal = stv0900_check_signal_presence(i_params,
- demod);
- trialCounter++;
- if ((lock == TRUE)
- || (no_signal == TRUE)
- || (trialCounter == 2)) {
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params,
- R0900_P2_CARFREQ,
- 0x49);
- stv0900_write_reg(i_params,
- R0900_P2_CORRELABS,
- 0x9e);
- } else {
- stv0900_write_reg(i_params,
- R0900_P2_CARFREQ,
- 0xed);
- stv0900_write_reg(i_params,
- R0900_P2_CORRELABS,
- 0x88);
+ if (s2fw < 0xd) {
+ msleep(sft_stp_tout);
+ s2fw = stv0900_get_bits(intp,
+ FLYWHEEL_CPT);
}
- if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS2_FOUND)) {
- msleep(softStepTimeout);
- dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT);
- if (dvbs2_fly_wheel < 0xd) {
- msleep(softStepTimeout);
- dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT);
- }
+ if (s2fw < 0xd) {
+ lock = FALSE;
- if (dvbs2_fly_wheel < 0xd) {
- lock = FALSE;
- if (trialCounter < 2) {
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x79);
- else
- stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x68);
+ if (trial_cntr < 2) {
+ if (intp->chip_id >= 0x20)
+ stv0900_write_reg(intp,
+ CORRELABS,
+ 0x79);
+ else
+ stv0900_write_reg(intp,
+ CORRELABS,
+ 0x68);
- stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89);
- }
+ stv0900_write_reg(intp,
+ DMDCFGMD,
+ 0x89);
}
}
}
+ }
- } while ((lock == FALSE) && (trialCounter < 2) && (no_signal == FALSE));
-
- break;
- }
+ } while ((lock == FALSE)
+ && (trial_cntr < 2)
+ && (no_signal == FALSE));
return lock;
}
-static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params,
+static u32 stv0900_get_symbol_rate(struct stv0900_internal *intp,
u32 mclk,
enum fe_stv0900_demod_num demod)
{
- s32 sfr_field3, sfr_field2, sfr_field1, sfr_field0,
- rem1, rem2, intval1, intval2, srate;
-
- dmd_reg(sfr_field3, F0900_P1_SYMB_FREQ3, F0900_P2_SYMB_FREQ3);
- dmd_reg(sfr_field2, F0900_P1_SYMB_FREQ2, F0900_P2_SYMB_FREQ2);
- dmd_reg(sfr_field1, F0900_P1_SYMB_FREQ1, F0900_P2_SYMB_FREQ1);
- dmd_reg(sfr_field0, F0900_P1_SYMB_FREQ0, F0900_P2_SYMB_FREQ0);
-
- srate = (stv0900_get_bits(i_params, sfr_field3) << 24) +
- (stv0900_get_bits(i_params, sfr_field2) << 16) +
- (stv0900_get_bits(i_params, sfr_field1) << 8) +
- (stv0900_get_bits(i_params, sfr_field0));
+ s32 rem1, rem2, intval1, intval2, srate;
+
+ srate = (stv0900_get_bits(intp, SYMB_FREQ3) << 24) +
+ (stv0900_get_bits(intp, SYMB_FREQ2) << 16) +
+ (stv0900_get_bits(intp, SYMB_FREQ1) << 8) +
+ (stv0900_get_bits(intp, SYMB_FREQ0));
dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n",
- srate, stv0900_get_bits(i_params, sfr_field0),
- stv0900_get_bits(i_params, sfr_field1),
- stv0900_get_bits(i_params, sfr_field2),
- stv0900_get_bits(i_params, sfr_field3));
+ srate, stv0900_get_bits(intp, SYMB_FREQ0),
+ stv0900_get_bits(intp, SYMB_FREQ1),
+ stv0900_get_bits(intp, SYMB_FREQ2),
+ stv0900_get_bits(intp, SYMB_FREQ3));
intval1 = (mclk) >> 16;
intval2 = (srate) >> 16;
@@ -520,18 +333,15 @@ static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params,
return srate;
}
-static void stv0900_set_symbol_rate(struct stv0900_internal *i_params,
+static void stv0900_set_symbol_rate(struct stv0900_internal *intp,
u32 mclk, u32 srate,
enum fe_stv0900_demod_num demod)
{
- s32 sfr_init_reg;
u32 symb;
- dprintk(KERN_INFO "%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk,
+ dprintk("%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk,
srate, demod);
- dmd_reg(sfr_init_reg, R0900_P1_SFRINIT1, R0900_P2_SFRINIT1);
-
if (srate > 60000000) {
symb = srate << 4;
symb /= (mclk >> 12);
@@ -543,19 +353,16 @@ static void stv0900_set_symbol_rate(struct stv0900_internal *i_params,
symb /= (mclk >> 7);
}
- stv0900_write_reg(i_params, sfr_init_reg, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, sfr_init_reg + 1, (symb & 0xFF));
+ stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0x7f);
+ stv0900_write_reg(intp, SFRINIT1 + 1, (symb & 0xff));
}
-static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params,
+static void stv0900_set_max_symbol_rate(struct stv0900_internal *intp,
u32 mclk, u32 srate,
enum fe_stv0900_demod_num demod)
{
- s32 sfr_max_reg;
u32 symb;
- dmd_reg(sfr_max_reg, R0900_P1_SFRUP1, R0900_P2_SFRUP1);
-
srate = 105 * (srate / 100);
if (srate > 60000000) {
@@ -570,23 +377,20 @@ static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params,
}
if (symb < 0x7fff) {
- stv0900_write_reg(i_params, sfr_max_reg, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, sfr_max_reg + 1, (symb & 0xFF));
+ stv0900_write_reg(intp, SFRUP1, (symb >> 8) & 0x7f);
+ stv0900_write_reg(intp, SFRUP1 + 1, (symb & 0xff));
} else {
- stv0900_write_reg(i_params, sfr_max_reg, 0x7F);
- stv0900_write_reg(i_params, sfr_max_reg + 1, 0xFF);
+ stv0900_write_reg(intp, SFRUP1, 0x7f);
+ stv0900_write_reg(intp, SFRUP1 + 1, 0xff);
}
}
-static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params,
+static void stv0900_set_min_symbol_rate(struct stv0900_internal *intp,
u32 mclk, u32 srate,
enum fe_stv0900_demod_num demod)
{
- s32 sfr_min_reg;
u32 symb;
- dmd_reg(sfr_min_reg, R0900_P1_SFRLOW1, R0900_P2_SFRLOW1);
-
srate = 95 * (srate / 100);
if (srate > 60000000) {
symb = srate << 4;
@@ -601,22 +405,20 @@ static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params,
symb /= (mclk >> 7);
}
- stv0900_write_reg(i_params, sfr_min_reg, (symb >> 8) & 0xFF);
- stv0900_write_reg(i_params, sfr_min_reg + 1, (symb & 0xFF));
+ stv0900_write_reg(intp, SFRLOW1, (symb >> 8) & 0xff);
+ stv0900_write_reg(intp, SFRLOW1 + 1, (symb & 0xff));
}
-static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params,
+static s32 stv0900_get_timing_offst(struct stv0900_internal *intp,
u32 srate,
enum fe_stv0900_demod_num demod)
{
- s32 tmgreg,
- timingoffset;
+ s32 timingoffset;
- dmd_reg(tmgreg, R0900_P1_TMGREG2, R0900_P2_TMGREG2);
- timingoffset = (stv0900_read_reg(i_params, tmgreg) << 16) +
- (stv0900_read_reg(i_params, tmgreg + 1) << 8) +
- (stv0900_read_reg(i_params, tmgreg + 2));
+ timingoffset = (stv0900_read_reg(intp, TMGREG2) << 16) +
+ (stv0900_read_reg(intp, TMGREG2 + 1) << 8) +
+ (stv0900_read_reg(intp, TMGREG2 + 2));
timingoffset = ge2comp(timingoffset, 24);
@@ -630,22 +432,19 @@ static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params,
return timingoffset;
}
-static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *i_params,
+static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
- s32 rolloff, man_fld, matstr_reg, rolloff_ctl_fld;
-
- dmd_reg(man_fld, F0900_P1_MANUAL_ROLLOFF, F0900_P2_MANUAL_ROLLOFF);
- dmd_reg(matstr_reg, R0900_P1_MATSTR1, R0900_P2_MATSTR1);
- dmd_reg(rolloff_ctl_fld, F0900_P1_ROLLOFF_CONTROL,
- F0900_P2_ROLLOFF_CONTROL);
-
- if (i_params->chip_id == 0x10) {
- stv0900_write_bits(i_params, man_fld, 1);
- rolloff = stv0900_read_reg(i_params, matstr_reg) & 0x03;
- stv0900_write_bits(i_params, rolloff_ctl_fld, rolloff);
- } else
- stv0900_write_bits(i_params, man_fld, 0);
+ s32 rolloff;
+
+ if (intp->chip_id == 0x10) {
+ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1);
+ rolloff = stv0900_read_reg(intp, MATSTR1) & 0x03;
+ stv0900_write_bits(intp, ROLLOFF_CONTROL, rolloff);
+ } else if (intp->chip_id <= 0x20)
+ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 0);
+ else /* cut 3.0 */
+ stv0900_write_bits(intp, MANUALS2_ROLLOFF, 0);
}
static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro)
@@ -668,84 +467,47 @@ static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro)
return srate + (srate * rolloff) / 100;
}
-static int stv0900_check_timing_lock(struct stv0900_internal *i_params,
+static int stv0900_check_timing_lock(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
int timingLock = FALSE;
- s32 i,
- timingcpt = 0;
- u8 carFreq,
- tmgTHhigh,
- tmgTHLow;
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- carFreq = stv0900_read_reg(i_params, R0900_P1_CARFREQ);
- tmgTHhigh = stv0900_read_reg(i_params, R0900_P1_TMGTHRISE);
- tmgTHLow = stv0900_read_reg(i_params, R0900_P1_TMGTHFALL);
- stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20);
- stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x0);
- stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
- stv0900_write_reg(i_params, R0900_P1_RTC, 0x80);
- stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x40);
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x0);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0x0);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0x0);
- stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x65);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
- msleep(7);
-
- for (i = 0; i < 10; i++) {
- if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2)
- timingcpt++;
-
- msleep(1);
- }
-
- if (timingcpt >= 3)
- timingLock = TRUE;
-
- stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
- stv0900_write_reg(i_params, R0900_P1_RTC, 0x88);
- stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68);
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, carFreq);
- stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, tmgTHhigh);
- stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, tmgTHLow);
- break;
- case STV0900_DEMOD_2:
- carFreq = stv0900_read_reg(i_params, R0900_P2_CARFREQ);
- tmgTHhigh = stv0900_read_reg(i_params, R0900_P2_TMGTHRISE);
- tmgTHLow = stv0900_read_reg(i_params, R0900_P2_TMGTHFALL);
- stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20);
- stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0);
- stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
- stv0900_write_reg(i_params, R0900_P2_RTC, 0x80);
- stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x40);
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x0);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0x0);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0x0);
- stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x65);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
- msleep(5);
- for (i = 0; i < 10; i++) {
- if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2)
- timingcpt++;
+ s32 i,
+ timingcpt = 0;
+ u8 car_freq,
+ tmg_th_high,
+ tmg_th_low;
+
+ car_freq = stv0900_read_reg(intp, CARFREQ);
+ tmg_th_high = stv0900_read_reg(intp, TMGTHRISE);
+ tmg_th_low = stv0900_read_reg(intp, TMGTHFALL);
+ stv0900_write_reg(intp, TMGTHRISE, 0x20);
+ stv0900_write_reg(intp, TMGTHFALL, 0x0);
+ stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+ stv0900_write_reg(intp, RTC, 0x80);
+ stv0900_write_reg(intp, RTCS2, 0x40);
+ stv0900_write_reg(intp, CARFREQ, 0x0);
+ stv0900_write_reg(intp, CFRINIT1, 0x0);
+ stv0900_write_reg(intp, CFRINIT0, 0x0);
+ stv0900_write_reg(intp, AGC2REF, 0x65);
+ stv0900_write_reg(intp, DMDISTATE, 0x18);
+ msleep(7);
+
+ for (i = 0; i < 10; i++) {
+ if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2)
+ timingcpt++;
+
+ msleep(1);
+ }
- msleep(1);
- }
+ if (timingcpt >= 3)
+ timingLock = TRUE;
- if (timingcpt >= 3)
- timingLock = TRUE;
-
- stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
- stv0900_write_reg(i_params, R0900_P2_RTC, 0x88);
- stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68);
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, carFreq);
- stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, tmgTHhigh);
- stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, tmgTHLow);
- break;
- }
+ stv0900_write_reg(intp, AGC2REF, 0x38);
+ stv0900_write_reg(intp, RTC, 0x88);
+ stv0900_write_reg(intp, RTCS2, 0x68);
+ stv0900_write_reg(intp, CARFREQ, car_freq);
+ stv0900_write_reg(intp, TMGTHRISE, tmg_th_high);
+ stv0900_write_reg(intp, TMGTHFALL, tmg_th_low);
return timingLock;
}
@@ -754,142 +516,114 @@ static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe,
s32 demod_timeout)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
+ int lock = FALSE,
+ d = demod;
+ s32 srate,
+ search_range,
+ locktimeout,
+ currier_step,
+ nb_steps,
+ current_step,
+ direction,
+ tuner_freq,
+ timeout,
+ freq;
- int lock = FALSE;
- s32 srate, search_range, locktimeout,
- currier_step, nb_steps, current_step,
- direction, tuner_freq, timeout;
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- srate = i_params->dmd1_symbol_rate;
- search_range = i_params->dmd1_srch_range;
- break;
-
- case STV0900_DEMOD_2:
- srate = i_params->dmd2_symbol_rate;
- search_range = i_params->dmd2_srch_range;
- break;
- }
+ srate = intp->symbol_rate[d];
+ search_range = intp->srch_range[d];
if (srate >= 10000000)
locktimeout = demod_timeout / 3;
else
locktimeout = demod_timeout / 2;
- lock = stv0900_get_demod_lock(i_params, demod, locktimeout);
-
- if (lock == FALSE) {
- if (srate >= 10000000) {
- if (stv0900_check_timing_lock(i_params, demod) == TRUE) {
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
- break;
- case STV0900_DEMOD_2:
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
- break;
- }
+ lock = stv0900_get_demod_lock(intp, d, locktimeout);
- lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
- } else
- lock = FALSE;
- } else {
- if (srate <= 4000000)
- currier_step = 1000;
- else if (srate <= 7000000)
- currier_step = 2000;
- else if (srate <= 10000000)
- currier_step = 3000;
- else
- currier_step = 5000;
-
- nb_steps = ((search_range / 1000) / currier_step);
- nb_steps /= 2;
- nb_steps = (2 * (nb_steps + 1));
- if (nb_steps < 0)
- nb_steps = 2;
- else if (nb_steps > 12)
- nb_steps = 12;
-
- current_step = 1;
- direction = 1;
+ if (lock != FALSE)
+ return lock;
+
+ if (srate >= 10000000) {
+ if (stv0900_check_timing_lock(intp, d) == TRUE) {
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, DMDISTATE, 0x15);
+ lock = stv0900_get_demod_lock(intp, d, demod_timeout);
+ } else
+ lock = FALSE;
+
+ return lock;
+ }
+
+ if (intp->chip_id <= 0x20) {
+ if (srate <= 1000000)
+ currier_step = 500;
+ else if (srate <= 4000000)
+ currier_step = 1000;
+ else if (srate <= 7000000)
+ currier_step = 2000;
+ else if (srate <= 10000000)
+ currier_step = 3000;
+ else
+ currier_step = 5000;
+
+ if (srate >= 2000000) {
timeout = (demod_timeout / 3);
if (timeout > 1000)
timeout = 1000;
+ } else
+ timeout = (demod_timeout / 2);
+ } else {
+ /*cut 3.0 */
+ currier_step = srate / 4000;
+ timeout = (demod_timeout * 3) / 4;
+ }
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- if (lock == FALSE) {
- tuner_freq = i_params->tuner1_freq;
- i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + i_params->dmd1_symbol_rate;
+ nb_steps = ((search_range / 1000) / currier_step);
- while ((current_step <= nb_steps) && (lock == FALSE)) {
+ if ((nb_steps % 2) != 0)
+ nb_steps += 1;
- if (direction > 0)
- tuner_freq += (current_step * currier_step);
- else
- tuner_freq -= (current_step * currier_step);
-
- stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
- if (i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS2) {
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
- }
-
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
- lock = stv0900_get_demod_lock(i_params, demod, timeout);
- direction *= -1;
- current_step++;
- }
- }
- break;
- case STV0900_DEMOD_2:
- if (lock == FALSE) {
- tuner_freq = i_params->tuner2_freq;
- i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + srate;
+ if (nb_steps <= 0)
+ nb_steps = 2;
+ else if (nb_steps > 12)
+ nb_steps = 12;
- while ((current_step <= nb_steps) && (lock == FALSE)) {
+ current_step = 1;
+ direction = 1;
- if (direction > 0)
- tuner_freq += (current_step * currier_step);
- else
- tuner_freq -= (current_step * currier_step);
-
- stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
- if (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS2) {
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
- }
-
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
- lock = stv0900_get_demod_lock(i_params, demod, timeout);
- direction *= -1;
- current_step++;
- }
- }
- break;
- }
+ if (intp->chip_id <= 0x20) {
+ tuner_freq = intp->freq[d];
+ intp->bw[d] = stv0900_carrier_width(intp->symbol_rate[d],
+ intp->rolloff) + intp->symbol_rate[d];
+ } else
+ tuner_freq = 0;
+
+ while ((current_step <= nb_steps) && (lock == FALSE)) {
+ if (direction > 0)
+ tuner_freq += (current_step * currier_step);
+ else
+ tuner_freq -= (current_step * currier_step);
+
+ if (intp->chip_id <= 0x20) {
+ stv0900_set_tuner(fe, tuner_freq, intp->bw[d]);
+ stv0900_write_reg(intp, DMDISTATE, 0x1c);
+ stv0900_write_reg(intp, CFRINIT1, 0);
+ stv0900_write_reg(intp, CFRINIT0, 0);
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, DMDISTATE, 0x15);
+ } else {
+ stv0900_write_reg(intp, DMDISTATE, 0x1c);
+ freq = (tuner_freq * 65536) / (intp->mclk / 1000);
+ stv0900_write_bits(intp, CFR_INIT1, MSB(freq));
+ stv0900_write_bits(intp, CFR_INIT0, LSB(freq));
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, DMDISTATE, 0x05);
}
+
+ lock = stv0900_get_demod_lock(intp, d, timeout);
+ direction *= -1;
+ current_step++;
}
return lock;
@@ -931,9 +665,7 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout,
} else if (srate <= 20000000) {
(*demod_timeout) = 400;
(*fec_timeout) = 130;
- }
-
- else {
+ } else {
(*demod_timeout) = 300;
(*fec_timeout) = 100;
}
@@ -946,95 +678,77 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout,
(*demod_timeout) /= 2;
}
-static void stv0900_set_viterbi_tracq(struct stv0900_internal *i_params,
+static void stv0900_set_viterbi_tracq(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
- s32 vth_reg;
+ s32 vth_reg = VTH12;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12);
-
- stv0900_write_reg(i_params, vth_reg++, 0xd0);
- stv0900_write_reg(i_params, vth_reg++, 0x7d);
- stv0900_write_reg(i_params, vth_reg++, 0x53);
- stv0900_write_reg(i_params, vth_reg++, 0x2F);
- stv0900_write_reg(i_params, vth_reg++, 0x24);
- stv0900_write_reg(i_params, vth_reg++, 0x1F);
+ stv0900_write_reg(intp, vth_reg++, 0xd0);
+ stv0900_write_reg(intp, vth_reg++, 0x7d);
+ stv0900_write_reg(intp, vth_reg++, 0x53);
+ stv0900_write_reg(intp, vth_reg++, 0x2f);
+ stv0900_write_reg(intp, vth_reg++, 0x24);
+ stv0900_write_reg(intp, vth_reg++, 0x1f);
}
-static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params,
- enum fe_stv0900_search_standard Standard,
- enum fe_stv0900_fec PunctureRate,
+static void stv0900_set_viterbi_standard(struct stv0900_internal *intp,
+ enum fe_stv0900_search_standard standard,
+ enum fe_stv0900_fec fec,
enum fe_stv0900_demod_num demod)
{
+ dprintk("%s: ViterbiStandard = ", __func__);
- s32 fecmReg,
- prvitReg;
-
- dprintk(KERN_INFO "%s: ViterbiStandard = ", __func__);
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- fecmReg = R0900_P1_FECM;
- prvitReg = R0900_P1_PRVIT;
- break;
- case STV0900_DEMOD_2:
- fecmReg = R0900_P2_FECM;
- prvitReg = R0900_P2_PRVIT;
- break;
- }
-
- switch (Standard) {
+ switch (standard) {
case STV0900_AUTO_SEARCH:
dprintk("Auto\n");
- stv0900_write_reg(i_params, fecmReg, 0x10);
- stv0900_write_reg(i_params, prvitReg, 0x3F);
+ stv0900_write_reg(intp, FECM, 0x10);
+ stv0900_write_reg(intp, PRVIT, 0x3f);
break;
case STV0900_SEARCH_DVBS1:
dprintk("DVBS1\n");
- stv0900_write_reg(i_params, fecmReg, 0x00);
- switch (PunctureRate) {
+ stv0900_write_reg(intp, FECM, 0x00);
+ switch (fec) {
case STV0900_FEC_UNKNOWN:
default:
- stv0900_write_reg(i_params, prvitReg, 0x2F);
+ stv0900_write_reg(intp, PRVIT, 0x2f);
break;
case STV0900_FEC_1_2:
- stv0900_write_reg(i_params, prvitReg, 0x01);
+ stv0900_write_reg(intp, PRVIT, 0x01);
break;
case STV0900_FEC_2_3:
- stv0900_write_reg(i_params, prvitReg, 0x02);
+ stv0900_write_reg(intp, PRVIT, 0x02);
break;
case STV0900_FEC_3_4:
- stv0900_write_reg(i_params, prvitReg, 0x04);
+ stv0900_write_reg(intp, PRVIT, 0x04);
break;
case STV0900_FEC_5_6:
- stv0900_write_reg(i_params, prvitReg, 0x08);
+ stv0900_write_reg(intp, PRVIT, 0x08);
break;
case STV0900_FEC_7_8:
- stv0900_write_reg(i_params, prvitReg, 0x20);
+ stv0900_write_reg(intp, PRVIT, 0x20);
break;
}
break;
case STV0900_SEARCH_DSS:
dprintk("DSS\n");
- stv0900_write_reg(i_params, fecmReg, 0x80);
- switch (PunctureRate) {
+ stv0900_write_reg(intp, FECM, 0x80);
+ switch (fec) {
case STV0900_FEC_UNKNOWN:
default:
- stv0900_write_reg(i_params, prvitReg, 0x13);
+ stv0900_write_reg(intp, PRVIT, 0x13);
break;
case STV0900_FEC_1_2:
- stv0900_write_reg(i_params, prvitReg, 0x01);
+ stv0900_write_reg(intp, PRVIT, 0x01);
break;
case STV0900_FEC_2_3:
- stv0900_write_reg(i_params, prvitReg, 0x02);
+ stv0900_write_reg(intp, PRVIT, 0x02);
break;
case STV0900_FEC_6_7:
- stv0900_write_reg(i_params, prvitReg, 0x10);
+ stv0900_write_reg(intp, PRVIT, 0x10);
break;
}
break;
@@ -1043,340 +757,277 @@ static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params,
}
}
-static void stv0900_track_optimization(struct dvb_frontend *fe)
+static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp,
+ enum fe_stv0900_demod_num demod)
{
- struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
- enum fe_stv0900_demod_num demod = state->demod;
+ enum fe_stv0900_fec prate;
+ s32 rate_fld = stv0900_get_bits(intp, VIT_CURPUN);
- s32 srate, pilots, aclc, freq1, freq0,
- i = 0, timed, timef, blindTunSw = 0;
+ switch (rate_fld) {
+ case 13:
+ prate = STV0900_FEC_1_2;
+ break;
+ case 18:
+ prate = STV0900_FEC_2_3;
+ break;
+ case 21:
+ prate = STV0900_FEC_3_4;
+ break;
+ case 24:
+ prate = STV0900_FEC_5_6;
+ break;
+ case 25:
+ prate = STV0900_FEC_6_7;
+ break;
+ case 26:
+ prate = STV0900_FEC_7_8;
+ break;
+ default:
+ prate = STV0900_FEC_UNKNOWN;
+ break;
+ }
- enum fe_stv0900_rolloff rolloff;
- enum fe_stv0900_modcode foundModcod;
+ return prate;
+}
- dprintk(KERN_INFO "%s\n", __func__);
+void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp,
+ enum fe_stv0900_demod_num demod,
+ u32 srate)
+{
+ if (intp->chip_id >= 0x30) {
+ if (srate >= 15000000) {
+ stv0900_write_reg(intp, ACLC, 0x2b);
+ stv0900_write_reg(intp, BCLC, 0x1a);
+ } else if ((srate >= 7000000) && (15000000 > srate)) {
+ stv0900_write_reg(intp, ACLC, 0x0c);
+ stv0900_write_reg(intp, BCLC, 0x1b);
+ } else if (srate < 7000000) {
+ stv0900_write_reg(intp, ACLC, 0x2c);
+ stv0900_write_reg(intp, BCLC, 0x1c);
+ }
- srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- srate += stv0900_get_timing_offst(i_params, srate, demod);
+ } else { /*cut 2.0 and 1.x*/
+ stv0900_write_reg(intp, ACLC, 0x1a);
+ stv0900_write_reg(intp, BCLC, 0x09);
+ }
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- switch (i_params->dmd1_rslts.standard) {
- case STV0900_DVBS1_STANDARD:
- if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) {
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
- }
+}
- stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff);
- stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
- stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
- break;
- case STV0900_DSS_STANDARD:
- if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) {
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
- }
+static void stv0900_track_optimization(struct dvb_frontend *fe)
+{
+ struct stv0900_state *state = fe->demodulator_priv;
+ struct stv0900_internal *intp = state->internal;
+ enum fe_stv0900_demod_num demod = state->demod;
- stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff);
- stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
- stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
- break;
- case STV0900_DVBS2_STANDARD:
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
- stv0900_write_reg(i_params, R0900_P1_ACLC, 0);
- stv0900_write_reg(i_params, R0900_P1_BCLC, 0);
- if (i_params->dmd1_rslts.frame_length == STV0900_LONG_FRAME) {
- foundModcod = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD);
- pilots = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01;
- aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id);
- if (foundModcod <= STV0900_QPSK_910)
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc);
- else if (foundModcod <= STV0900_8PSK_910) {
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc);
- }
+ s32 srate,
+ pilots,
+ aclc,
+ freq1,
+ freq0,
+ i = 0,
+ timed,
+ timef,
+ blind_tun_sw = 0,
+ modulation;
- if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) {
- if (foundModcod <= STV0900_16APSK_910) {
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc);
- } else if (foundModcod <= STV0900_32APSK_910) {
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc);
- }
- }
+ enum fe_stv0900_rolloff rolloff;
+ enum fe_stv0900_modcode foundModcod;
- } else {
- aclc = stv0900_get_optim_short_carr_loop(srate, i_params->dmd1_rslts.modulation, i_params->chip_id);
- if (i_params->dmd1_rslts.modulation == STV0900_QPSK)
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc);
-
- else if (i_params->dmd1_rslts.modulation == STV0900_8PSK) {
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc);
- } else if (i_params->dmd1_rslts.modulation == STV0900_16APSK) {
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc);
- } else if (i_params->dmd1_rslts.modulation == STV0900_32APSK) {
- stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc);
- }
+ dprintk("%s\n", __func__);
- }
+ srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+ srate += stv0900_get_timing_offst(intp, srate, demod);
- if (i_params->chip_id <= 0x11) {
- if (i_params->demod_mode != STV0900_SINGLE)
- stv0900_activate_s2_modcode(i_params, demod);
+ switch (intp->result[demod].standard) {
+ case STV0900_DVBS1_STANDARD:
+ case STV0900_DSS_STANDARD:
+ dprintk("%s: found DVB-S or DSS\n", __func__);
+ if (intp->srch_standard[demod] == STV0900_AUTO_SEARCH) {
+ stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+ stv0900_write_bits(intp, DVBS2_ENABLE, 0);
+ }
- }
+ stv0900_write_bits(intp, ROLLOFF_CONTROL, intp->rolloff);
+ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1);
- stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67);
- break;
- case STV0900_UNKNOWN_STANDARD:
- default:
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
+ if (intp->chip_id < 0x30) {
+ stv0900_write_reg(intp, ERRCTRL1, 0x75);
break;
}
- freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2);
- freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1);
- rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS);
- if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
- stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00);
- stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
- stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
- stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
- stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod);
- stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod);
- blindTunSw = 1;
+ if (stv0900_get_vit_fec(intp, demod) == STV0900_FEC_1_2) {
+ stv0900_write_reg(intp, GAUSSR0, 0x98);
+ stv0900_write_reg(intp, CCIR0, 0x18);
+ } else {
+ stv0900_write_reg(intp, GAUSSR0, 0x18);
+ stv0900_write_reg(intp, CCIR0, 0x18);
}
- if (i_params->chip_id >= 0x20) {
- if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) {
- stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0a);
- stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x0);
+ stv0900_write_reg(intp, ERRCTRL1, 0x75);
+ break;
+ case STV0900_DVBS2_STANDARD:
+ dprintk("%s: found DVB-S2\n", __func__);
+ stv0900_write_bits(intp, DVBS1_ENABLE, 0);
+ stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+ stv0900_write_reg(intp, ACLC, 0);
+ stv0900_write_reg(intp, BCLC, 0);
+ if (intp->result[demod].frame_len == STV0900_LONG_FRAME) {
+ foundModcod = stv0900_get_bits(intp, DEMOD_MODCOD);
+ pilots = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01;
+ aclc = stv0900_get_optim_carr_loop(srate,
+ foundModcod,
+ pilots,
+ intp->chip_id);
+ if (foundModcod <= STV0900_QPSK_910)
+ stv0900_write_reg(intp, ACLC2S2Q, aclc);
+ else if (foundModcod <= STV0900_8PSK_910) {
+ stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+ stv0900_write_reg(intp, ACLC2S28, aclc);
}
- }
-
- if (i_params->chip_id < 0x20)
- stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x08);
-
- if (i_params->chip_id == 0x10)
- stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0x0A);
- stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
-
- if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd1_symbol_rate < 10000000)) {
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
- i_params->tuner1_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000;
-
- if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) {
- if (i_params->dmd1_srch_algo != STV0900_WARM_START)
- stv0900_set_bandwidth(fe, i_params->tuner1_bw);
+ if ((intp->demod_mode == STV0900_SINGLE) &&
+ (foundModcod > STV0900_8PSK_910)) {
+ if (foundModcod <= STV0900_16APSK_910) {
+ stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+ stv0900_write_reg(intp, ACLC2S216A,
+ aclc);
+ } else if (foundModcod <= STV0900_32APSK_910) {
+ stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+ stv0900_write_reg(intp, ACLC2S232A,
+ aclc);
+ }
}
- if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000))
- msleep(50);
- else
- msleep(5);
-
- stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START);
-
- if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) {
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
- i = 0;
- while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) {
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
- i++;
- }
+ } else {
+ modulation = intp->result[demod].modulation;
+ aclc = stv0900_get_optim_short_carr_loop(srate,
+ modulation, intp->chip_id);
+ if (modulation == STV0900_QPSK)
+ stv0900_write_reg(intp, ACLC2S2Q, aclc);
+ else if (modulation == STV0900_8PSK) {
+ stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+ stv0900_write_reg(intp, ACLC2S28, aclc);
+ } else if (modulation == STV0900_16APSK) {
+ stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+ stv0900_write_reg(intp, ACLC2S216A, aclc);
+ } else if (modulation == STV0900_32APSK) {
+ stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+ stv0900_write_reg(intp, ACLC2S232A, aclc);
}
}
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49);
+ if (intp->chip_id <= 0x11) {
+ if (intp->demod_mode != STV0900_SINGLE)
+ stv0900_activate_s2_modcod(intp, demod);
- if ((i_params->dmd1_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd1_rslts.standard == STV0900_DSS_STANDARD))
- stv0900_set_viterbi_tracq(i_params, demod);
+ }
+ stv0900_write_reg(intp, ERRCTRL1, 0x67);
break;
+ case STV0900_UNKNOWN_STANDARD:
+ default:
+ dprintk("%s: found unknown standard\n", __func__);
+ stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+ stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+ break;
+ }
- case STV0900_DEMOD_2:
- switch (i_params->dmd2_rslts.standard) {
- case STV0900_DVBS1_STANDARD:
-
- if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) {
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
- }
-
- stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff);
- stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
- stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
- break;
- case STV0900_DSS_STANDARD:
- if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) {
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
- }
-
- stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff);
- stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
- stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
- break;
- case STV0900_DVBS2_STANDARD:
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
- stv0900_write_reg(i_params, R0900_P2_ACLC, 0);
- stv0900_write_reg(i_params, R0900_P2_BCLC, 0);
- if (i_params->dmd2_rslts.frame_length == STV0900_LONG_FRAME) {
- foundModcod = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD);
- pilots = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01;
- aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id);
- if (foundModcod <= STV0900_QPSK_910)
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc);
- else if (foundModcod <= STV0900_8PSK_910) {
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc);
- }
+ freq1 = stv0900_read_reg(intp, CFR2);
+ freq0 = stv0900_read_reg(intp, CFR1);
+ rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS);
+ if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) {
+ stv0900_write_reg(intp, SFRSTEP, 0x00);
+ stv0900_write_bits(intp, SCAN_ENABLE, 0);
+ stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+ stv0900_write_reg(intp, TMGCFG2, 0xc1);
+ stv0900_set_symbol_rate(intp, intp->mclk, srate, demod);
+ blind_tun_sw = 1;
+ if (intp->result[demod].standard != STV0900_DVBS2_STANDARD)
+ stv0900_set_dvbs1_track_car_loop(intp, demod, srate);
- if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) {
- if (foundModcod <= STV0900_16APSK_910) {
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc);
- } else if (foundModcod <= STV0900_32APSK_910) {
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc);
- }
+ }
- }
+ if (intp->chip_id >= 0x20) {
+ if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) ||
+ (intp->srch_standard[demod] ==
+ STV0900_SEARCH_DSS) ||
+ (intp->srch_standard[demod] ==
+ STV0900_AUTO_SEARCH)) {
+ stv0900_write_reg(intp, VAVSRVIT, 0x0a);
+ stv0900_write_reg(intp, VITSCALE, 0x0);
+ }
+ }
- } else {
- aclc = stv0900_get_optim_short_carr_loop(srate,
- i_params->dmd2_rslts.modulation,
- i_params->chip_id);
-
- if (i_params->dmd2_rslts.modulation == STV0900_QPSK)
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc);
-
- else if (i_params->dmd2_rslts.modulation == STV0900_8PSK) {
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc);
- } else if (i_params->dmd2_rslts.modulation == STV0900_16APSK) {
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc);
- } else if (i_params->dmd2_rslts.modulation == STV0900_32APSK) {
- stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
- stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc);
- }
- }
+ if (intp->chip_id < 0x20)
+ stv0900_write_reg(intp, CARHDR, 0x08);
- stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67);
+ if (intp->chip_id == 0x10)
+ stv0900_write_reg(intp, CORRELEXP, 0x0a);
- break;
- case STV0900_UNKNOWN_STANDARD:
- default:
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
- break;
- }
+ stv0900_write_reg(intp, AGC2REF, 0x38);
- freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2);
- freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1);
- rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS);
- if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
- stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00);
- stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
- stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
- stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
- stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod);
- stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod);
- blindTunSw = 1;
- }
+ if ((intp->chip_id >= 0x20) ||
+ (blind_tun_sw == 1) ||
+ (intp->symbol_rate[demod] < 10000000)) {
+ stv0900_write_reg(intp, CFRINIT1, freq1);
+ stv0900_write_reg(intp, CFRINIT0, freq0);
+ intp->bw[demod] = stv0900_carrier_width(srate,
+ intp->rolloff) + 10000000;
- if (i_params->chip_id >= 0x20) {
- if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) {
- stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0a);
- stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x0);
- }
+ if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) {
+ if (intp->srch_algo[demod] != STV0900_WARM_START)
+ stv0900_set_bandwidth(fe, intp->bw[demod]);
}
- if (i_params->chip_id < 0x20)
- stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x08);
-
- if (i_params->chip_id == 0x10)
- stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0x0a);
-
- stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
- if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd2_symbol_rate < 10000000)) {
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
- i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000;
+ if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) ||
+ (intp->symbol_rate[demod] < 10000000))
+ msleep(50);
+ else
+ msleep(5);
- if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) {
- if (i_params->dmd2_srch_algo != STV0900_WARM_START)
- stv0900_set_bandwidth(fe, i_params->tuner2_bw);
- }
+ stv0900_get_lock_timeout(&timed, &timef, srate,
+ STV0900_WARM_START);
- if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000))
- msleep(50);
- else
- msleep(5);
-
- stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START);
- if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) {
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
- i = 0;
- while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) {
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
- i++;
- }
+ if (stv0900_get_demod_lock(intp, demod, timed / 2) == FALSE) {
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, CFRINIT1, freq1);
+ stv0900_write_reg(intp, CFRINIT0, freq0);
+ stv0900_write_reg(intp, DMDISTATE, 0x18);
+ i = 0;
+ while ((stv0900_get_demod_lock(intp,
+ demod,
+ timed / 2) == FALSE) &&
+ (i <= 2)) {
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, CFRINIT1, freq1);
+ stv0900_write_reg(intp, CFRINIT0, freq0);
+ stv0900_write_reg(intp, DMDISTATE, 0x18);
+ i++;
}
}
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49);
+ }
- if ((i_params->dmd2_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd2_rslts.standard == STV0900_DSS_STANDARD))
- stv0900_set_viterbi_tracq(i_params, demod);
+ if (intp->chip_id >= 0x20)
+ stv0900_write_reg(intp, CARFREQ, 0x49);
+
+ if ((intp->result[demod].standard == STV0900_DVBS1_STANDARD) ||
+ (intp->result[demod].standard == STV0900_DSS_STANDARD))
+ stv0900_set_viterbi_tracq(intp, demod);
- break;
- }
}
-static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod, s32 time_out)
+static int stv0900_get_fec_lock(struct stv0900_internal *intp,
+ enum fe_stv0900_demod_num demod, s32 time_out)
{
- s32 timer = 0, lock = 0, header_field, pktdelin_field, lock_vit_field;
+ s32 timer = 0, lock = 0;
enum fe_stv0900_search_state dmd_state;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
- dmd_reg(pktdelin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK);
- dmd_reg(lock_vit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT);
-
- dmd_state = stv0900_get_bits(i_params, header_field);
+ dmd_state = stv0900_get_bits(intp, HEADER_MODE);
while ((timer < time_out) && (lock == 0)) {
switch (dmd_state) {
@@ -1386,10 +1037,10 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09
lock = 0;
break;
case STV0900_DVBS2_FOUND:
- lock = stv0900_get_bits(i_params, pktdelin_field);
+ lock = stv0900_get_bits(intp, PKTDELIN_LOCK);
break;
case STV0900_DVBS_FOUND:
- lock = stv0900_get_bits(i_params, lock_vit_field);
+ lock = stv0900_get_bits(intp, LOCKEDVIT);
break;
}
@@ -1400,46 +1051,44 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09
}
if (lock)
- dprintk("DEMOD FEC LOCK OK\n");
+ dprintk("%s: DEMOD FEC LOCK OK\n", __func__);
else
- dprintk("DEMOD FEC LOCK FAIL\n");
+ dprintk("%s: DEMOD FEC LOCK FAIL\n", __func__);
return lock;
}
-static int stv0900_wait_for_lock(struct stv0900_internal *i_params,
+static int stv0900_wait_for_lock(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod,
s32 dmd_timeout, s32 fec_timeout)
{
- s32 timer = 0, lock = 0, str_merg_rst_fld, str_merg_lock_fld;
-
- dprintk(KERN_INFO "%s\n", __func__);
+ s32 timer = 0, lock = 0;
- dmd_reg(str_merg_rst_fld, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE);
- dmd_reg(str_merg_lock_fld, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK);
+ dprintk("%s\n", __func__);
- lock = stv0900_get_demod_lock(i_params, demod, dmd_timeout);
+ lock = stv0900_get_demod_lock(intp, demod, dmd_timeout);
if (lock)
- lock = lock && stv0900_get_fec_lock(i_params, demod, fec_timeout);
+ lock = lock && stv0900_get_fec_lock(intp, demod, fec_timeout);
if (lock) {
lock = 0;
- dprintk(KERN_INFO "%s: Timer = %d, time_out = %d\n", __func__, timer, fec_timeout);
+ dprintk("%s: Timer = %d, time_out = %d\n",
+ __func__, timer, fec_timeout);
while ((timer < fec_timeout) && (lock == 0)) {
- lock = stv0900_get_bits(i_params, str_merg_lock_fld);
+ lock = stv0900_get_bits(intp, TSFIFO_LINEOK);
msleep(1);
timer++;
}
}
if (lock)
- dprintk(KERN_INFO "%s: DEMOD LOCK OK\n", __func__);
+ dprintk("%s: DEMOD LOCK OK\n", __func__);
else
- dprintk(KERN_INFO "%s: DEMOD LOCK FAIL\n", __func__);
+ dprintk("%s: DEMOD LOCK FAIL\n", __func__);
if (lock)
return TRUE;
@@ -1451,43 +1100,43 @@ enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
enum fe_stv0900_demod_num demod)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_tracking_standard fnd_standard;
- s32 state_field,
- dss_dvb_field;
- dprintk(KERN_INFO "%s\n", __func__);
+ int hdr_mode = stv0900_get_bits(intp, HEADER_MODE);
- dmd_reg(state_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
- dmd_reg(dss_dvb_field, F0900_P1_DSS_DVB, F0900_P2_DSS_DVB);
-
- if (stv0900_get_bits(i_params, state_field) == 2)
+ switch (hdr_mode) {
+ case 2:
fnd_standard = STV0900_DVBS2_STANDARD;
-
- else if (stv0900_get_bits(i_params, state_field) == 3) {
- if (stv0900_get_bits(i_params, dss_dvb_field) == 1)
+ break;
+ case 3:
+ if (stv0900_get_bits(intp, DSS_DVB) == 1)
fnd_standard = STV0900_DSS_STANDARD;
else
fnd_standard = STV0900_DVBS1_STANDARD;
- } else
+
+ break;
+ default:
fnd_standard = STV0900_UNKNOWN_STANDARD;
+ }
+
+ dprintk("%s: standard %d\n", __func__, fnd_standard);
return fnd_standard;
}
-static s32 stv0900_get_carr_freq(struct stv0900_internal *i_params, u32 mclk,
+static s32 stv0900_get_carr_freq(struct stv0900_internal *intp, u32 mclk,
enum fe_stv0900_demod_num demod)
{
- s32 cfr_field2, cfr_field1, cfr_field0,
- derot, rem1, rem2, intval1, intval2;
+ s32 derot,
+ rem1,
+ rem2,
+ intval1,
+ intval2;
- dmd_reg(cfr_field2, F0900_P1_CAR_FREQ2, F0900_P2_CAR_FREQ2);
- dmd_reg(cfr_field1, F0900_P1_CAR_FREQ1, F0900_P2_CAR_FREQ1);
- dmd_reg(cfr_field0, F0900_P1_CAR_FREQ0, F0900_P2_CAR_FREQ0);
-
- derot = (stv0900_get_bits(i_params, cfr_field2) << 16) +
- (stv0900_get_bits(i_params, cfr_field1) << 8) +
- (stv0900_get_bits(i_params, cfr_field0));
+ derot = (stv0900_get_bits(intp, CAR_FREQ2) << 16) +
+ (stv0900_get_bits(intp, CAR_FREQ1) << 8) +
+ (stv0900_get_bits(intp, CAR_FREQ0));
derot = ge2comp(derot, 24);
intval1 = mclk >> 12;
@@ -1505,7 +1154,7 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe)
{
struct dvb_frontend_ops *frontend_ops = NULL;
struct dvb_tuner_ops *tuner_ops = NULL;
- u32 frequency = 0;
+ u32 freq = 0;
if (&fe->ops)
frontend_ops = &fe->ops;
@@ -1514,304 +1163,159 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe)
tuner_ops = &frontend_ops->tuner_ops;
if (tuner_ops->get_frequency) {
- if ((tuner_ops->get_frequency(fe, &frequency)) < 0)
+ if ((tuner_ops->get_frequency(fe, &freq)) < 0)
dprintk("%s: Invalid parameter\n", __func__);
else
- dprintk("%s: Frequency=%d\n", __func__, frequency);
-
- }
-
- return frequency;
-}
-
-static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *i_params,
- enum fe_stv0900_demod_num demod)
-{
- s32 rate_fld, vit_curpun_fld;
- enum fe_stv0900_fec prate;
+ dprintk("%s: Frequency=%d\n", __func__, freq);
- dmd_reg(vit_curpun_fld, F0900_P1_VIT_CURPUN, F0900_P2_VIT_CURPUN);
- rate_fld = stv0900_get_bits(i_params, vit_curpun_fld);
-
- switch (rate_fld) {
- case 13:
- prate = STV0900_FEC_1_2;
- break;
- case 18:
- prate = STV0900_FEC_2_3;
- break;
- case 21:
- prate = STV0900_FEC_3_4;
- break;
- case 24:
- prate = STV0900_FEC_5_6;
- break;
- case 25:
- prate = STV0900_FEC_6_7;
- break;
- case 26:
- prate = STV0900_FEC_7_8;
- break;
- default:
- prate = STV0900_FEC_UNKNOWN;
- break;
}
- return prate;
+ return freq;
}
-static enum fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
+static enum
+fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE;
- s32 offsetFreq,
- srate_offset,
- i = 0;
+ struct stv0900_signal_info *result = &intp->result[demod];
+ s32 offsetFreq,
+ srate_offset;
+ int i = 0,
+ d = demod;
u8 timing;
msleep(5);
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
- timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2);
- i = 0;
- stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x5c);
-
- while ((i <= 50) && (timing != 0) && (timing != 0xFF)) {
- timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2);
- msleep(5);
- i += 5;
- }
+ if (intp->srch_algo[d] == STV0900_BLIND_SEARCH) {
+ timing = stv0900_read_reg(intp, TMGREG2);
+ i = 0;
+ stv0900_write_reg(intp, SFRSTEP, 0x5c);
+
+ while ((i <= 50) && (timing != 0) && (timing != 0xff)) {
+ timing = stv0900_read_reg(intp, TMGREG2);
+ msleep(5);
+ i += 5;
}
+ }
- i_params->dmd1_rslts.standard = stv0900_get_standard(fe, demod);
- i_params->dmd1_rslts.frequency = stv0900_get_tuner_freq(fe);
- offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000;
- i_params->dmd1_rslts.frequency += offsetFreq;
- i_params->dmd1_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd1_rslts.symbol_rate, demod);
- i_params->dmd1_rslts.symbol_rate += srate_offset;
- i_params->dmd1_rslts.fec = stv0900_get_vit_fec(i_params, demod);
- i_params->dmd1_rslts.modcode = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD);
- i_params->dmd1_rslts.pilot = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01;
- i_params->dmd1_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE)) >> 1;
- i_params->dmd1_rslts.rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS);
- switch (i_params->dmd1_rslts.standard) {
- case STV0900_DVBS2_STANDARD:
- i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_SPECINV_DEMOD);
- if (i_params->dmd1_rslts.modcode <= STV0900_QPSK_910)
- i_params->dmd1_rslts.modulation = STV0900_QPSK;
- else if (i_params->dmd1_rslts.modcode <= STV0900_8PSK_910)
- i_params->dmd1_rslts.modulation = STV0900_8PSK;
- else if (i_params->dmd1_rslts.modcode <= STV0900_16APSK_910)
- i_params->dmd1_rslts.modulation = STV0900_16APSK;
- else if (i_params->dmd1_rslts.modcode <= STV0900_32APSK_910)
- i_params->dmd1_rslts.modulation = STV0900_32APSK;
- else
- i_params->dmd1_rslts.modulation = STV0900_UNKNOWN;
- break;
- case STV0900_DVBS1_STANDARD:
- case STV0900_DSS_STANDARD:
- i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_IQINV);
- i_params->dmd1_rslts.modulation = STV0900_QPSK;
- break;
- default:
- break;
- }
-
- if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000)) {
- offsetFreq = i_params->dmd1_rslts.frequency - i_params->tuner1_freq;
- i_params->tuner1_freq = stv0900_get_tuner_freq(fe);
- if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500))
- range = STV0900_RANGEOK;
- else
- if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd1_rslts.symbol_rate, i_params->dmd1_rslts.rolloff) / 2000))
- range = STV0900_RANGEOK;
- else
- range = STV0900_OUTOFRANGE;
-
- } else {
- if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500))
- range = STV0900_RANGEOK;
- else
- range = STV0900_OUTOFRANGE;
- }
+ result->standard = stv0900_get_standard(fe, d);
+ result->frequency = stv0900_get_tuner_freq(fe);
+ offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000;
+ result->frequency += offsetFreq;
+ result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d);
+ srate_offset = stv0900_get_timing_offst(intp, result->symbol_rate, d);
+ result->symbol_rate += srate_offset;
+ result->fec = stv0900_get_vit_fec(intp, d);
+ result->modcode = stv0900_get_bits(intp, DEMOD_MODCOD);
+ result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01;
+ result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1;
+ result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS);
+ switch (result->standard) {
+ case STV0900_DVBS2_STANDARD:
+ result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD);
+ if (result->modcode <= STV0900_QPSK_910)
+ result->modulation = STV0900_QPSK;
+ else if (result->modcode <= STV0900_8PSK_910)
+ result->modulation = STV0900_8PSK;
+ else if (result->modcode <= STV0900_16APSK_910)
+ result->modulation = STV0900_16APSK;
+ else if (result->modcode <= STV0900_32APSK_910)
+ result->modulation = STV0900_32APSK;
+ else
+ result->modulation = STV0900_UNKNOWN;
break;
- case STV0900_DEMOD_2:
- if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
- timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2);
- i = 0;
- stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x5c);
-
- while ((i <= 50) && (timing != 0) && (timing != 0xff)) {
- timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2);
- msleep(5);
- i += 5;
- }
- }
-
- i_params->dmd2_rslts.standard = stv0900_get_standard(fe, demod);
- i_params->dmd2_rslts.frequency = stv0900_get_tuner_freq(fe);
- offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000;
- i_params->dmd2_rslts.frequency += offsetFreq;
- i_params->dmd2_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd2_rslts.symbol_rate, demod);
- i_params->dmd2_rslts.symbol_rate += srate_offset;
- i_params->dmd2_rslts.fec = stv0900_get_vit_fec(i_params, demod);
- i_params->dmd2_rslts.modcode = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD);
- i_params->dmd2_rslts.pilot = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01;
- i_params->dmd2_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE)) >> 1;
- i_params->dmd2_rslts.rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS);
- switch (i_params->dmd2_rslts.standard) {
- case STV0900_DVBS2_STANDARD:
- i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_SPECINV_DEMOD);
- if (i_params->dmd2_rslts.modcode <= STV0900_QPSK_910)
- i_params->dmd2_rslts.modulation = STV0900_QPSK;
- else if (i_params->dmd2_rslts.modcode <= STV0900_8PSK_910)
- i_params->dmd2_rslts.modulation = STV0900_8PSK;
- else if (i_params->dmd2_rslts.modcode <= STV0900_16APSK_910)
- i_params->dmd2_rslts.modulation = STV0900_16APSK;
- else if (i_params->dmd2_rslts.modcode <= STV0900_32APSK_910)
- i_params->dmd2_rslts.modulation = STV0900_32APSK;
- else
- i_params->dmd2_rslts.modulation = STV0900_UNKNOWN;
- break;
- case STV0900_DVBS1_STANDARD:
- case STV0900_DSS_STANDARD:
- i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_IQINV);
- i_params->dmd2_rslts.modulation = STV0900_QPSK;
- break;
- default:
- break;
- }
+ case STV0900_DVBS1_STANDARD:
+ case STV0900_DSS_STANDARD:
+ result->spectrum = stv0900_get_bits(intp, IQINV);
+ result->modulation = STV0900_QPSK;
+ break;
+ default:
+ break;
+ }
- if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000)) {
- offsetFreq = i_params->dmd2_rslts.frequency - i_params->tuner2_freq;
- i_params->tuner2_freq = stv0900_get_tuner_freq(fe);
+ if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) ||
+ (intp->symbol_rate[d] < 10000000)) {
+ offsetFreq = result->frequency - intp->freq[d];
+ intp->freq[d] = stv0900_get_tuner_freq(fe);
+ if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
+ range = STV0900_RANGEOK;
+ else if (ABS(offsetFreq) <=
+ (stv0900_carrier_width(result->symbol_rate,
+ result->rolloff) / 2000))
+ range = STV0900_RANGEOK;
- if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500))
- range = STV0900_RANGEOK;
- else
- if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd2_rslts.symbol_rate, i_params->dmd2_rslts.rolloff) / 2000))
- range = STV0900_RANGEOK;
- else
- range = STV0900_OUTOFRANGE;
- } else {
- if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500))
- range = STV0900_RANGEOK;
- else
- range = STV0900_OUTOFRANGE;
- }
+ } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
+ range = STV0900_RANGEOK;
- break;
- }
+ dprintk("%s: range %d\n", __func__, range);
return range;
}
-static enum fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe)
+static enum
+fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
-
- s32 srate, demod_timeout,
- fec_timeout, freq1, freq0;
enum fe_stv0900_signal_type signal_type = STV0900_NODATA;
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- i_params->dmd1_rslts.locked = FALSE;
- if (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) {
- srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- srate += stv0900_get_timing_offst(i_params, srate, demod);
- if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH)
- stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
-
- stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START);
- freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2);
- freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1);
- stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
- stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
- if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
- i_params->dmd1_rslts.locked = TRUE;
- signal_type = stv0900_get_signal_params(fe);
- stv0900_track_optimization(fe);
- } else {
- stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1c);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
- if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
- i_params->dmd1_rslts.locked = TRUE;
- signal_type = stv0900_get_signal_params(fe);
- stv0900_track_optimization(fe);
- }
-
- }
-
- } else
- i_params->dmd1_rslts.locked = FALSE;
-
- break;
- case STV0900_DEMOD_2:
- i_params->dmd2_rslts.locked = FALSE;
- if (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) {
- srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- srate += stv0900_get_timing_offst(i_params, srate, demod);
-
- if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH)
- stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
-
- stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START);
- freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2);
- freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1);
- stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
- stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-
- if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
- i_params->dmd2_rslts.locked = TRUE;
+ s32 srate,
+ demod_timeout,
+ fec_timeout,
+ freq1,
+ freq0;
+
+ intp->result[demod].locked = FALSE;
+
+ if (stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) {
+ srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+ srate += stv0900_get_timing_offst(intp, srate, demod);
+ if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH)
+ stv0900_set_symbol_rate(intp, intp->mclk, srate, demod);
+
+ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout,
+ srate, STV0900_WARM_START);
+ freq1 = stv0900_read_reg(intp, CFR2);
+ freq0 = stv0900_read_reg(intp, CFR1);
+ stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+ stv0900_write_bits(intp, SPECINV_CONTROL,
+ STV0900_IQ_FORCE_SWAPPED);
+ stv0900_write_reg(intp, DMDISTATE, 0x1c);
+ stv0900_write_reg(intp, CFRINIT1, freq1);
+ stv0900_write_reg(intp, CFRINIT0, freq0);
+ stv0900_write_reg(intp, DMDISTATE, 0x18);
+ if (stv0900_wait_for_lock(intp, demod,
+ demod_timeout, fec_timeout) == TRUE) {
+ intp->result[demod].locked = TRUE;
+ signal_type = stv0900_get_signal_params(fe);
+ stv0900_track_optimization(fe);
+ } else {
+ stv0900_write_bits(intp, SPECINV_CONTROL,
+ STV0900_IQ_FORCE_NORMAL);
+ stv0900_write_reg(intp, DMDISTATE, 0x1c);
+ stv0900_write_reg(intp, CFRINIT1, freq1);
+ stv0900_write_reg(intp, CFRINIT0, freq0);
+ stv0900_write_reg(intp, DMDISTATE, 0x18);
+ if (stv0900_wait_for_lock(intp, demod,
+ demod_timeout, fec_timeout) == TRUE) {
+ intp->result[demod].locked = TRUE;
signal_type = stv0900_get_signal_params(fe);
stv0900_track_optimization(fe);
- } else {
- stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1c);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-
- if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
- i_params->dmd2_rslts.locked = TRUE;
- signal_type = stv0900_get_signal_params(fe);
- stv0900_track_optimization(fe);
- }
-
}
- } else
- i_params->dmd1_rslts.locked = FALSE;
+ }
- break;
- }
+ } else
+ intp->result[demod].locked = FALSE;
return signal_type;
}
-static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params,
+static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
u32 minagc2level = 0xffff,
@@ -1820,105 +1324,54 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params,
s32 i, j, nb_steps, direction;
- dprintk(KERN_INFO "%s\n", __func__);
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
- stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1);
-
- stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83);
- stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0);
-
- stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82);
- stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0);
- stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0);
+ dprintk("%s\n", __func__);
- stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
- nb_steps = -1 + (i_params->dmd1_srch_range / 1000000);
- nb_steps /= 2;
- nb_steps = (2 * nb_steps) + 1;
+ stv0900_write_reg(intp, AGC2REF, 0x38);
+ stv0900_write_bits(intp, SCAN_ENABLE, 0);
+ stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
- if (nb_steps < 0)
- nb_steps = 1;
+ stv0900_write_bits(intp, AUTO_GUP, 1);
+ stv0900_write_bits(intp, AUTO_GLOW, 1);
- direction = 1;
+ stv0900_write_reg(intp, DMDT0M, 0x0);
- freq_step = (1000000 << 8) / (i_params->mclk >> 8);
+ stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod);
+ nb_steps = -1 + (intp->srch_range[demod] / 1000000);
+ nb_steps /= 2;
+ nb_steps = (2 * nb_steps) + 1;
- init_freq = 0;
+ if (nb_steps < 0)
+ nb_steps = 1;
- for (i = 0; i < nb_steps; i++) {
- if (direction > 0)
- init_freq = init_freq + (freq_step * i);
- else
- init_freq = init_freq - (freq_step * i);
+ direction = 1;
- direction *= -1;
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (init_freq >> 8) & 0xff);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, init_freq & 0xff);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x58);
- msleep(10);
- agc2level = 0;
+ freq_step = (1000000 << 8) / (intp->mclk >> 8);
- for (j = 0; j < 10; j++)
- agc2level += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
- | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
+ init_freq = 0;
- agc2level /= 10;
-
- if (agc2level < minagc2level)
- minagc2level = agc2level;
- }
- break;
- case STV0900_DEMOD_2:
- stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
- stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1);
- stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83);
- stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0);
- stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82);
- stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0);
- stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0);
- stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
- nb_steps = -1 + (i_params->dmd2_srch_range / 1000000);
- nb_steps /= 2;
- nb_steps = (2 * nb_steps) + 1;
-
- if (nb_steps < 0)
- nb_steps = 1;
-
- direction = 1;
- freq_step = (1000000 << 8) / (i_params->mclk >> 8);
- init_freq = 0;
- for (i = 0; i < nb_steps; i++) {
- if (direction > 0)
- init_freq = init_freq + (freq_step * i);
- else
- init_freq = init_freq - (freq_step * i);
+ for (i = 0; i < nb_steps; i++) {
+ if (direction > 0)
+ init_freq = init_freq + (freq_step * i);
+ else
+ init_freq = init_freq - (freq_step * i);
- direction *= -1;
+ direction *= -1;
+ stv0900_write_reg(intp, DMDISTATE, 0x5C);
+ stv0900_write_reg(intp, CFRINIT1, (init_freq >> 8) & 0xff);
+ stv0900_write_reg(intp, CFRINIT0, init_freq & 0xff);
+ stv0900_write_reg(intp, DMDISTATE, 0x58);
+ msleep(10);
+ agc2level = 0;
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (init_freq >> 8) & 0xff);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, init_freq & 0xff);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x58);
+ for (j = 0; j < 10; j++)
+ agc2level += (stv0900_read_reg(intp, AGC2I1) << 8)
+ | stv0900_read_reg(intp, AGC2I0);
- msleep(10);
- agc2level = 0;
- for (j = 0; j < 10; j++)
- agc2level += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
- | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
+ agc2level /= 10;
- agc2level /= 10;
+ if (agc2level < minagc2level)
+ minagc2level = agc2level;
- if (agc2level < minagc2level)
- minagc2level = agc2level;
- }
- break;
}
return (u16)minagc2level;
@@ -1927,336 +1380,192 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params,
static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- int timingLock = FALSE;
+ int timing_lck = FALSE;
s32 i, timingcpt = 0,
direction = 1,
nb_steps,
current_step = 0,
tuner_freq;
+ u32 agc2_th,
+ coarse_srate = 0,
+ agc2_integr = 0,
+ currier_step = 1200;
- u32 coarse_srate = 0, agc2_integr = 0, currier_step = 1200;
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1F);
- stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0x12);
- stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xf0);
- stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xe0);
- stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1);
- stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83);
- stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0);
- stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82);
- stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0);
- stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0);
- stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x50);
-
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x6a);
- stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x95);
- } else {
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
- stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x73);
- }
+ if (intp->chip_id >= 0x30)
+ agc2_th = 0x2e00;
+ else
+ agc2_th = 0x1f00;
+
+ stv0900_write_bits(intp, DEMOD_MODE, 0x1f);
+ stv0900_write_reg(intp, TMGCFG, 0x12);
+ stv0900_write_reg(intp, TMGTHRISE, 0xf0);
+ stv0900_write_reg(intp, TMGTHFALL, 0xe0);
+ stv0900_write_bits(intp, SCAN_ENABLE, 1);
+ stv0900_write_bits(intp, CFR_AUTOSCAN, 1);
+ stv0900_write_reg(intp, SFRUP1, 0x83);
+ stv0900_write_reg(intp, SFRUP0, 0xc0);
+ stv0900_write_reg(intp, SFRLOW1, 0x82);
+ stv0900_write_reg(intp, SFRLOW0, 0xa0);
+ stv0900_write_reg(intp, DMDT0M, 0x0);
+ stv0900_write_reg(intp, AGC2REF, 0x50);
+
+ if (intp->chip_id >= 0x30) {
+ stv0900_write_reg(intp, CARFREQ, 0x99);
+ stv0900_write_reg(intp, SFRSTEP, 0x98);
+ } else if (intp->chip_id >= 0x20) {
+ stv0900_write_reg(intp, CARFREQ, 0x6a);
+ stv0900_write_reg(intp, SFRSTEP, 0x95);
+ } else {
+ stv0900_write_reg(intp, CARFREQ, 0xed);
+ stv0900_write_reg(intp, SFRSTEP, 0x73);
+ }
- if (i_params->dmd1_symbol_rate <= 2000000)
- currier_step = 1000;
- else if (i_params->dmd1_symbol_rate <= 5000000)
- currier_step = 2000;
- else if (i_params->dmd1_symbol_rate <= 12000000)
- currier_step = 3000;
- else
+ if (intp->symbol_rate[demod] <= 2000000)
+ currier_step = 1000;
+ else if (intp->symbol_rate[demod] <= 5000000)
+ currier_step = 2000;
+ else if (intp->symbol_rate[demod] <= 12000000)
+ currier_step = 3000;
+ else
currier_step = 5000;
- nb_steps = -1 + ((i_params->dmd1_srch_range / 1000) / currier_step);
- nb_steps /= 2;
- nb_steps = (2 * nb_steps) + 1;
-
- if (nb_steps < 0)
- nb_steps = 1;
-
- else if (nb_steps > 10) {
- nb_steps = 11;
- currier_step = (i_params->dmd1_srch_range / 1000) / 10;
- }
-
- current_step = 0;
-
- direction = 1;
- tuner_freq = i_params->tuner1_freq;
-
- while ((timingLock == FALSE) && (current_step < nb_steps)) {
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5F);
- stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x0);
-
- msleep(50);
-
- for (i = 0; i < 10; i++) {
- if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2)
- timingcpt++;
-
- agc2_integr += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
-
- }
+ nb_steps = -1 + ((intp->srch_range[demod] / 1000) / currier_step);
+ nb_steps /= 2;
+ nb_steps = (2 * nb_steps) + 1;
- agc2_integr /= 10;
- coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- current_step++;
- direction *= -1;
+ if (nb_steps < 0)
+ nb_steps = 1;
+ else if (nb_steps > 10) {
+ nb_steps = 11;
+ currier_step = (intp->srch_range[demod] / 1000) / 10;
+ }
- dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started. tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", tuner_freq, agc2_integr, coarse_srate, timingcpt);
+ current_step = 0;
+ direction = 1;
- if ((timingcpt >= 5) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000)) {
- timingLock = TRUE;
- }
+ tuner_freq = intp->freq[demod];
- else if (current_step < nb_steps) {
- if (direction > 0)
- tuner_freq += (current_step * currier_step);
- else
- tuner_freq -= (current_step * currier_step);
+ while ((timing_lck == FALSE) && (current_step < nb_steps)) {
+ stv0900_write_reg(intp, DMDISTATE, 0x5f);
+ stv0900_write_bits(intp, DEMOD_MODE, 0);
- stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw);
- }
- }
+ msleep(50);
- if (timingLock == FALSE)
- coarse_srate = 0;
- else
- coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- break;
- case STV0900_DEMOD_2:
- stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1F);
- stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0x12);
- stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xf0);
- stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xe0);
- stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1);
- stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83);
- stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0);
- stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82);
- stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0);
- stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0);
- stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x50);
-
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x6a);
- stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x95);
- } else {
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
- stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x73);
- }
-
- if (i_params->dmd2_symbol_rate <= 2000000)
- currier_step = 1000;
- else if (i_params->dmd2_symbol_rate <= 5000000)
- currier_step = 2000;
- else if (i_params->dmd2_symbol_rate <= 12000000)
- currier_step = 3000;
- else
- currier_step = 5000;
-
-
- nb_steps = -1 + ((i_params->dmd2_srch_range / 1000) / currier_step);
- nb_steps /= 2;
- nb_steps = (2 * nb_steps) + 1;
+ for (i = 0; i < 10; i++) {
+ if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2)
+ timingcpt++;
- if (nb_steps < 0)
- nb_steps = 1;
- else if (nb_steps > 10) {
- nb_steps = 11;
- currier_step = (i_params->dmd2_srch_range / 1000) / 10;
+ agc2_integr += (stv0900_read_reg(intp, AGC2I1) << 8) |
+ stv0900_read_reg(intp, AGC2I0);
}
- current_step = 0;
- direction = 1;
- tuner_freq = i_params->tuner2_freq;
-
- while ((timingLock == FALSE) && (current_step < nb_steps)) {
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5F);
- stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x0);
-
- msleep(50);
- timingcpt = 0;
-
- for (i = 0; i < 20; i++) {
- if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2)
- timingcpt++;
- agc2_integr += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
- | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
- }
-
- agc2_integr /= 20;
- coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- if ((timingcpt >= 10) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000))
- timingLock = TRUE;
- else {
- current_step++;
- direction *= -1;
-
- if (direction > 0)
- tuner_freq += (current_step * currier_step);
- else
- tuner_freq -= (current_step * currier_step);
+ agc2_integr /= 10;
+ coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+ current_step++;
+ direction *= -1;
+
+ dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started."
+ " tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n",
+ tuner_freq, agc2_integr, coarse_srate, timingcpt);
+
+ if ((timingcpt >= 5) &&
+ (agc2_integr < agc2_th) &&
+ (coarse_srate < 55000000) &&
+ (coarse_srate > 850000))
+ timing_lck = TRUE;
+ else if (current_step < nb_steps) {
+ if (direction > 0)
+ tuner_freq += (current_step * currier_step);
+ else
+ tuner_freq -= (current_step * currier_step);
- stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw);
- }
+ stv0900_set_tuner(fe, tuner_freq, intp->bw[demod]);
}
-
- if (timingLock == FALSE)
- coarse_srate = 0;
- else
- coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
- break;
}
+ if (timing_lck == FALSE)
+ coarse_srate = 0;
+ else
+ coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+
return coarse_srate;
}
static u32 stv0900_search_srate_fine(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- u32 coarse_srate,
- coarse_freq,
- symb;
+ u32 coarse_srate,
+ coarse_freq,
+ symb,
+ symbmax,
+ symbmin,
+ symbcomp;
+
+ coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+
+ if (coarse_srate > 3000000) {
+ symbmax = 13 * (coarse_srate / 10);
+ symbmax = (symbmax / 1000) * 65536;
+ symbmax /= (intp->mclk / 1000);
+
+ symbmin = 10 * (coarse_srate / 13);
+ symbmin = (symbmin / 1000)*65536;
+ symbmin /= (intp->mclk / 1000);
+
+ symb = (coarse_srate / 1000) * 65536;
+ symb /= (intp->mclk / 1000);
+ } else {
+ symbmax = 13 * (coarse_srate / 10);
+ symbmax = (symbmax / 100) * 65536;
+ symbmax /= (intp->mclk / 100);
- coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
+ symbmin = 10 * (coarse_srate / 14);
+ symbmin = (symbmin / 100) * 65536;
+ symbmin /= (intp->mclk / 100);
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- coarse_freq = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8)
- | stv0900_read_reg(i_params, R0900_P1_CFR1);
- symb = 13 * (coarse_srate / 10);
-
- if (symb < i_params->dmd1_symbol_rate)
- coarse_srate = 0;
- else {
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
- stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20);
- stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x00);
- stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2);
- stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
-
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49);
- else
- stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
-
- if (coarse_srate > 3000000) {
- symb = 13 * (coarse_srate / 10);
- symb = (symb / 1000) * 65536;
- symb /= (i_params->mclk / 1000);
- stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF));
-
- symb = 10 * (coarse_srate / 13);
- symb = (symb / 1000) * 65536;
- symb /= (i_params->mclk / 1000);
-
- stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF));
-
- symb = (coarse_srate / 1000) * 65536;
- symb /= (i_params->mclk / 1000);
- stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF);
- stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF));
- } else {
- symb = 13 * (coarse_srate / 10);
- symb = (symb / 100) * 65536;
- symb /= (i_params->mclk / 100);
- stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF));
-
- symb = 10 * (coarse_srate / 14);
- symb = (symb / 100) * 65536;
- symb /= (i_params->mclk / 100);
- stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF));
-
- symb = (coarse_srate / 100) * 65536;
- symb /= (i_params->mclk / 100);
- stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF);
- stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF));
- }
+ symb = (coarse_srate / 100) * 65536;
+ symb /= (intp->mclk / 100);
+ }
- stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (coarse_freq >> 8) & 0xff);
- stv0900_write_reg(i_params, R0900_P1_CFRINIT0, coarse_freq & 0xff);
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
- }
- break;
- case STV0900_DEMOD_2:
- coarse_freq = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8)
- | stv0900_read_reg(i_params, R0900_P2_CFR1);
-
- symb = 13 * (coarse_srate / 10);
-
- if (symb < i_params->dmd2_symbol_rate)
- coarse_srate = 0;
- else {
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
- stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
- stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20);
- stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0x00);
- stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2);
- stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
-
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49);
- else
- stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
-
- if (coarse_srate > 3000000) {
- symb = 13 * (coarse_srate / 10);
- symb = (symb / 1000) * 65536;
- symb /= (i_params->mclk / 1000);
- stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF));
-
- symb = 10 * (coarse_srate / 13);
- symb = (symb / 1000) * 65536;
- symb /= (i_params->mclk / 1000);
-
- stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF));
-
- symb = (coarse_srate / 1000) * 65536;
- symb /= (i_params->mclk / 1000);
- stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF);
- stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF));
- } else {
- symb = 13 * (coarse_srate / 10);
- symb = (symb / 100) * 65536;
- symb /= (i_params->mclk / 100);
- stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF));
-
- symb = 10 * (coarse_srate / 14);
- symb = (symb / 100) * 65536;
- symb /= (i_params->mclk / 100);
- stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F);
- stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF));
-
- symb = (coarse_srate / 100) * 65536;
- symb /= (i_params->mclk / 100);
- stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF);
- stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF));
- }
+ symbcomp = 13 * (coarse_srate / 10);
+ coarse_freq = (stv0900_read_reg(intp, CFR2) << 8)
+ | stv0900_read_reg(intp, CFR1);
+
+ if (symbcomp < intp->symbol_rate[demod])
+ coarse_srate = 0;
+ else {
+ stv0900_write_reg(intp, DMDISTATE, 0x1f);
+ stv0900_write_reg(intp, TMGCFG2, 0xc1);
+ stv0900_write_reg(intp, TMGTHRISE, 0x20);
+ stv0900_write_reg(intp, TMGTHFALL, 0x00);
+ stv0900_write_reg(intp, TMGCFG, 0xd2);
+ stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+ stv0900_write_reg(intp, AGC2REF, 0x38);
+
+ if (intp->chip_id >= 0x30)
+ stv0900_write_reg(intp, CARFREQ, 0x79);
+ else if (intp->chip_id >= 0x20)
+ stv0900_write_reg(intp, CARFREQ, 0x49);
+ else
+ stv0900_write_reg(intp, CARFREQ, 0xed);
- stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (coarse_freq >> 8) & 0xff);
- stv0900_write_reg(i_params, R0900_P2_CFRINIT0, coarse_freq & 0xff);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
- }
+ stv0900_write_reg(intp, SFRUP1, (symbmax >> 8) & 0x7f);
+ stv0900_write_reg(intp, SFRUP0, (symbmax & 0xff));
- break;
+ stv0900_write_reg(intp, SFRLOW1, (symbmin >> 8) & 0x7f);
+ stv0900_write_reg(intp, SFRLOW0, (symbmin & 0xff));
+
+ stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0xff);
+ stv0900_write_reg(intp, SFRINIT0, (symb & 0xff));
+
+ stv0900_write_reg(intp, DMDT0M, 0x20);
+ stv0900_write_reg(intp, CFRINIT1, (coarse_freq >> 8) & 0xff);
+ stv0900_write_reg(intp, CFRINIT0, coarse_freq & 0xff);
+ stv0900_write_reg(intp, DMDISTATE, 0x15);
}
return coarse_srate;
@@ -2265,163 +1574,135 @@ static u32 stv0900_search_srate_fine(struct dvb_frontend *fe)
static int stv0900_blind_search_algo(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- u8 k_ref_tmg, k_ref_tmg_max, k_ref_tmg_min;
- u32 coarse_srate;
- int lock = FALSE, coarse_fail = FALSE;
- s32 demod_timeout = 500, fec_timeout = 50, kref_tmg_reg, fail_cpt, i, agc2_overflow;
- u16 agc2_integr;
- u8 dstatus2;
-
- dprintk(KERN_INFO "%s\n", __func__);
-
- if (i_params->chip_id < 0x20) {
+ u8 k_ref_tmg,
+ k_ref_tmg_max,
+ k_ref_tmg_min;
+ u32 coarse_srate,
+ agc2_th;
+ int lock = FALSE,
+ coarse_fail = FALSE;
+ s32 demod_timeout = 500,
+ fec_timeout = 50,
+ fail_cpt,
+ i,
+ agc2_overflow;
+ u16 agc2_int;
+ u8 dstatus2;
+
+ dprintk("%s\n", __func__);
+
+ if (intp->chip_id < 0x20) {
k_ref_tmg_max = 233;
k_ref_tmg_min = 143;
} else {
- k_ref_tmg_max = 120;
- k_ref_tmg_min = 30;
+ k_ref_tmg_max = 110;
+ k_ref_tmg_min = 10;
}
- agc2_integr = stv0900_blind_check_agc2_min_level(i_params, demod);
-
- if (agc2_integr > STV0900_BLIND_SEARCH_AGC2_TH) {
- lock = FALSE;
-
- } else {
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- if (i_params->chip_id == 0x10)
- stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xAA);
-
- if (i_params->chip_id < 0x20)
- stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55);
-
- stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xC4);
- stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44);
-
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41);
- stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41);
- stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82);
- stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0);
- }
-
- kref_tmg_reg = R0900_P1_KREFTMG;
- break;
- case STV0900_DEMOD_2:
- if (i_params->chip_id == 0x10)
- stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xAA);
-
- if (i_params->chip_id < 0x20)
- stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55);
+ if (intp->chip_id <= 0x20)
+ agc2_th = STV0900_BLIND_SEARCH_AGC2_TH;
+ else
+ agc2_th = STV0900_BLIND_SEARCH_AGC2_TH_CUT30;
- stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xC4);
- stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44);
+ agc2_int = stv0900_blind_check_agc2_min_level(intp, demod);
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41);
- stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41);
- stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82);
- stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0);
- }
+ if (agc2_int > STV0900_BLIND_SEARCH_AGC2_TH)
+ return FALSE;
- kref_tmg_reg = R0900_P2_KREFTMG;
- break;
- }
+ if (intp->chip_id == 0x10)
+ stv0900_write_reg(intp, CORRELEXP, 0xaa);
- k_ref_tmg = k_ref_tmg_max;
+ if (intp->chip_id < 0x20)
+ stv0900_write_reg(intp, CARHDR, 0x55);
+ else
+ stv0900_write_reg(intp, CARHDR, 0x20);
- do {
- stv0900_write_reg(i_params, kref_tmg_reg, k_ref_tmg);
- if (stv0900_search_srate_coarse(fe) != 0) {
- coarse_srate = stv0900_search_srate_fine(fe);
+ if (intp->chip_id <= 0x20)
+ stv0900_write_reg(intp, CARCFG, 0xc4);
+ else
+ stv0900_write_reg(intp, CARCFG, 0x6);
- if (coarse_srate != 0) {
- stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, coarse_srate, STV0900_BLIND_SEARCH);
- lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
- } else
- lock = FALSE;
- } else {
- fail_cpt = 0;
- agc2_overflow = 0;
+ stv0900_write_reg(intp, RTCS2, 0x44);
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- for (i = 0; i < 10; i++) {
- agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
- | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
+ if (intp->chip_id >= 0x20) {
+ stv0900_write_reg(intp, EQUALCFG, 0x41);
+ stv0900_write_reg(intp, FFECFG, 0x41);
+ stv0900_write_reg(intp, VITSCALE, 0x82);
+ stv0900_write_reg(intp, VAVSRVIT, 0x0);
+ }
- if (agc2_integr >= 0xff00)
- agc2_overflow++;
+ k_ref_tmg = k_ref_tmg_max;
- dstatus2 = stv0900_read_reg(i_params, R0900_P1_DSTATUS2);
+ do {
+ stv0900_write_reg(intp, KREFTMG, k_ref_tmg);
+ if (stv0900_search_srate_coarse(fe) != 0) {
+ coarse_srate = stv0900_search_srate_fine(fe);
+
+ if (coarse_srate != 0) {
+ stv0900_get_lock_timeout(&demod_timeout,
+ &fec_timeout,
+ coarse_srate,
+ STV0900_BLIND_SEARCH);
+ lock = stv0900_get_demod_lock(intp,
+ demod,
+ demod_timeout);
+ } else
+ lock = FALSE;
+ } else {
+ fail_cpt = 0;
+ agc2_overflow = 0;
- if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1))
- fail_cpt++;
- }
- break;
- case STV0900_DEMOD_2:
- for (i = 0; i < 10; i++) {
- agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
- | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
+ for (i = 0; i < 10; i++) {
+ agc2_int = (stv0900_read_reg(intp, AGC2I1) << 8)
+ | stv0900_read_reg(intp, AGC2I0);
- if (agc2_integr >= 0xff00)
- agc2_overflow++;
+ if (agc2_int >= 0xff00)
+ agc2_overflow++;
- dstatus2 = stv0900_read_reg(i_params, R0900_P2_DSTATUS2);
+ dstatus2 = stv0900_read_reg(intp, DSTATUS2);
- if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1))
- fail_cpt++;
- }
- break;
- }
+ if (((dstatus2 & 0x1) == 0x1) &&
+ ((dstatus2 >> 7) == 1))
+ fail_cpt++;
+ }
- if ((fail_cpt > 7) || (agc2_overflow > 7))
- coarse_fail = TRUE;
+ if ((fail_cpt > 7) || (agc2_overflow > 7))
+ coarse_fail = TRUE;
- lock = FALSE;
- }
- k_ref_tmg -= 30;
- } while ((k_ref_tmg >= k_ref_tmg_min) && (lock == FALSE) && (coarse_fail == FALSE));
- }
+ lock = FALSE;
+ }
+ k_ref_tmg -= 30;
+ } while ((k_ref_tmg >= k_ref_tmg_min) &&
+ (lock == FALSE) &&
+ (coarse_fail == FALSE));
return lock;
}
-static void stv0900_set_viterbi_acq(struct stv0900_internal *i_params,
+static void stv0900_set_viterbi_acq(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
- s32 vth_reg;
+ s32 vth_reg = VTH12;
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12);
-
- stv0900_write_reg(i_params, vth_reg++, 0x96);
- stv0900_write_reg(i_params, vth_reg++, 0x64);
- stv0900_write_reg(i_params, vth_reg++, 0x36);
- stv0900_write_reg(i_params, vth_reg++, 0x23);
- stv0900_write_reg(i_params, vth_reg++, 0x1E);
- stv0900_write_reg(i_params, vth_reg++, 0x19);
+ stv0900_write_reg(intp, vth_reg++, 0x96);
+ stv0900_write_reg(intp, vth_reg++, 0x64);
+ stv0900_write_reg(intp, vth_reg++, 0x36);
+ stv0900_write_reg(intp, vth_reg++, 0x23);
+ stv0900_write_reg(intp, vth_reg++, 0x1e);
+ stv0900_write_reg(intp, vth_reg++, 0x19);
}
-static void stv0900_set_search_standard(struct stv0900_internal *i_params,
+static void stv0900_set_search_standard(struct stv0900_internal *intp,
enum fe_stv0900_demod_num demod)
{
- int sstndrd;
-
- dprintk(KERN_INFO "%s\n", __func__);
+ dprintk("%s\n", __func__);
- sstndrd = i_params->dmd1_srch_standard;
- if (demod == 1)
- sstndrd = i_params->dmd2_srch_stndrd;
-
- switch (sstndrd) {
+ switch (intp->srch_standard[demod]) {
case STV0900_SEARCH_DVBS1:
dprintk("Search Standard = DVBS1\n");
break;
@@ -2436,129 +1717,74 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params,
break;
}
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- switch (i_params->dmd1_srch_standard) {
- case STV0900_SEARCH_DVBS1:
- case STV0900_SEARCH_DSS:
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
-
- stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0);
- stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
- stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
- stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x22);
-
- stv0900_set_viterbi_acq(i_params, demod);
- stv0900_set_viterbi_standard(i_params,
- i_params->dmd1_srch_standard,
- i_params->dmd1_fec, demod);
-
- break;
- case STV0900_SEARCH_DVBS2:
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 1);
- stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
- stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
- stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26);
- if (i_params->demod_mode != STV0900_SINGLE) {
- if (i_params->chip_id <= 0x11)
- stv0900_stop_all_s2_modcod(i_params, demod);
- else
- stv0900_activate_s2_modcode(i_params, demod);
-
- } else
- stv0900_activate_s2_modcode_single(i_params, demod);
-
- stv0900_set_viterbi_tracq(i_params, demod);
-
- break;
- case STV0900_AUTO_SEARCH:
- default:
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0);
- stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
- stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
- stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26);
- if (i_params->demod_mode != STV0900_SINGLE) {
- if (i_params->chip_id <= 0x11)
- stv0900_stop_all_s2_modcod(i_params, demod);
- else
- stv0900_activate_s2_modcode(i_params, demod);
+ switch (intp->srch_standard[demod]) {
+ case STV0900_SEARCH_DVBS1:
+ case STV0900_SEARCH_DSS:
+ stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+ stv0900_write_bits(intp, DVBS2_ENABLE, 0);
+ stv0900_write_bits(intp, STOP_CLKVIT, 0);
+ stv0900_set_dvbs1_track_car_loop(intp,
+ demod,
+ intp->symbol_rate[demod]);
+ stv0900_write_reg(intp, CAR2CFG, 0x22);
+
+ stv0900_set_viterbi_acq(intp, demod);
+ stv0900_set_viterbi_standard(intp,
+ intp->srch_standard[demod],
+ intp->fec[demod], demod);
- } else
- stv0900_activate_s2_modcode_single(i_params, demod);
+ break;
+ case STV0900_SEARCH_DVBS2:
+ stv0900_write_bits(intp, DVBS1_ENABLE, 0);
+ stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+ stv0900_write_bits(intp, STOP_CLKVIT, 1);
+ stv0900_write_reg(intp, ACLC, 0x1a);
+ stv0900_write_reg(intp, BCLC, 0x09);
+ if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/
+ stv0900_write_reg(intp, CAR2CFG, 0x26);
+ else
+ stv0900_write_reg(intp, CAR2CFG, 0x66);
- if (i_params->dmd1_symbol_rate >= 2000000)
- stv0900_set_viterbi_acq(i_params, demod);
+ if (intp->demod_mode != STV0900_SINGLE) {
+ if (intp->chip_id <= 0x11)
+ stv0900_stop_all_s2_modcod(intp, demod);
else
- stv0900_set_viterbi_tracq(i_params, demod);
+ stv0900_activate_s2_modcod(intp, demod);
- stv0900_set_viterbi_standard(i_params, i_params->dmd1_srch_standard, i_params->dmd1_fec, demod);
+ } else
+ stv0900_activate_s2_modcod_single(intp, demod);
- break;
- }
- break;
- case STV0900_DEMOD_2:
- switch (i_params->dmd2_srch_stndrd) {
- case STV0900_SEARCH_DVBS1:
- case STV0900_SEARCH_DSS:
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0);
- stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
- stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
- stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x22);
- stv0900_set_viterbi_acq(i_params, demod);
- stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod);
- break;
- case STV0900_SEARCH_DVBS2:
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 1);
- stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
- stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
- stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26);
- if (i_params->demod_mode != STV0900_SINGLE)
- stv0900_activate_s2_modcode(i_params, demod);
- else
- stv0900_activate_s2_modcode_single(i_params, demod);
+ stv0900_set_viterbi_tracq(intp, demod);
- stv0900_set_viterbi_tracq(i_params, demod);
- break;
- case STV0900_AUTO_SEARCH:
- default:
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
- stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
- stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0);
- stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
- stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
- stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26);
- if (i_params->demod_mode != STV0900_SINGLE)
- stv0900_activate_s2_modcode(i_params, demod);
- else
- stv0900_activate_s2_modcode_single(i_params, demod);
+ break;
+ case STV0900_AUTO_SEARCH:
+ default:
+ stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+ stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+ stv0900_write_bits(intp, STOP_CLKVIT, 0);
+ stv0900_write_reg(intp, ACLC, 0x1a);
+ stv0900_write_reg(intp, BCLC, 0x09);
+ stv0900_set_dvbs1_track_car_loop(intp,
+ demod,
+ intp->symbol_rate[demod]);
+ if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/
+ stv0900_write_reg(intp, CAR2CFG, 0x26);
+ else
+ stv0900_write_reg(intp, CAR2CFG, 0x66);
- if (i_params->dmd2_symbol_rate >= 2000000)
- stv0900_set_viterbi_acq(i_params, demod);
+ if (intp->demod_mode != STV0900_SINGLE) {
+ if (intp->chip_id <= 0x11)
+ stv0900_stop_all_s2_modcod(intp, demod);
else
- stv0900_set_viterbi_tracq(i_params, demod);
+ stv0900_activate_s2_modcod(intp, demod);
- stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod);
+ } else
+ stv0900_activate_s2_modcod_single(intp, demod);
- break;
- }
+ stv0900_set_viterbi_tracq(intp, demod);
+ stv0900_set_viterbi_standard(intp,
+ intp->srch_standard[demod],
+ intp->fec[demod], demod);
break;
}
@@ -2567,10 +1793,11 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params,
enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
{
struct stv0900_state *state = fe->demodulator_priv;
- struct stv0900_internal *i_params = state->internal;
+ struct stv0900_internal *intp = state->internal;
enum fe_stv0900_demod_num demod = state->demod;
- s32 demod_timeout = 500, fec_timeout = 50, stream_merger_field;
+ s32 demod_timeout = 500, fec_timeout = 50;
+ s32 aq_power, agc1_power, i;
int lock = FALSE, low_sr = FALSE;
@@ -2578,157 +1805,117 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
enum fe_stv0900_search_algo algo;
int no_signal = FALSE;
- dprintk(KERN_INFO "%s\n", __func__);
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- algo = i_params->dmd1_srch_algo;
+ dprintk("%s\n", __func__);
- stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1);
- stream_merger_field = F0900_P1_RST_HWARE;
-
- stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C);
-
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x9e);
+ algo = intp->srch_algo[demod];
+ stv0900_write_bits(intp, RST_HWARE, 1);
+ stv0900_write_reg(intp, DMDISTATE, 0x5c);
+ if (intp->chip_id >= 0x20) {
+ if (intp->symbol_rate[demod] > 5000000)
+ stv0900_write_reg(intp, CORRELABS, 0x9e);
else
- stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x88);
-
- stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd1_symbol_rate, i_params->dmd1_srch_algo);
-
- if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
- i_params->tuner1_bw = 2 * 36000000;
-
- stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x00);
- stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70);
-
- stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
- } else {
- stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20);
- stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2);
-
- if (i_params->dmd1_symbol_rate < 2000000)
- stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x63);
- else
- stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70);
-
- stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0x5a);
-
- if (i_params->dmd1_srch_algo == STV0900_COLD_START)
- i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10;
- else if (i_params->dmd1_srch_algo == STV0900_WARM_START)
- i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000;
- } else {
- stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0xc1);
- i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10;
- }
-
- stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
-
- stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
- stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
- stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
- if (i_params->dmd1_symbol_rate >= 10000000)
- low_sr = FALSE;
- else
- low_sr = TRUE;
-
- }
-
- stv0900_set_tuner(fe, i_params->tuner1_freq, i_params->tuner1_bw);
-
- stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, i_params->dmd1_srch_iq_inv);
- stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
-
- stv0900_set_search_standard(i_params, demod);
+ stv0900_write_reg(intp, CORRELABS, 0x82);
+ } else
+ stv0900_write_reg(intp, CORRELABS, 0x88);
- if (i_params->dmd1_srch_algo != STV0900_BLIND_SEARCH)
- stv0900_start_search(i_params, demod);
- break;
- case STV0900_DEMOD_2:
- algo = i_params->dmd2_srch_algo;
+ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout,
+ intp->symbol_rate[demod],
+ intp->srch_algo[demod]);
- stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1);
+ if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) {
+ intp->bw[demod] = 2 * 36000000;
- stream_merger_field = F0900_P2_RST_HWARE;
+ stv0900_write_reg(intp, TMGCFG2, 0xc0);
+ stv0900_write_reg(intp, CORRELMANT, 0x70);
- stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C);
+ stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod);
+ } else {
+ stv0900_write_reg(intp, DMDT0M, 0x20);
+ stv0900_write_reg(intp, TMGCFG, 0xd2);
- if (i_params->chip_id >= 0x20)
- stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x9e);
+ if (intp->symbol_rate[demod] < 2000000)
+ stv0900_write_reg(intp, CORRELMANT, 0x63);
else
- stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x88);
+ stv0900_write_reg(intp, CORRELMANT, 0x70);
- stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd2_symbol_rate, i_params->dmd2_srch_algo);
+ stv0900_write_reg(intp, AGC2REF, 0x38);
- if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
- i_params->tuner2_bw = 2 * 36000000;
+ intp->bw[demod] =
+ stv0900_carrier_width(intp->symbol_rate[demod],
+ intp->rolloff);
+ if (intp->chip_id >= 0x20) {
+ stv0900_write_reg(intp, KREFTMG, 0x5a);
- stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x00);
- stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70);
+ if (intp->srch_algo[demod] == STV0900_COLD_START) {
+ intp->bw[demod] += 10000000;
+ intp->bw[demod] *= 15;
+ intp->bw[demod] /= 10;
+ } else if (intp->srch_algo[demod] == STV0900_WARM_START)
+ intp->bw[demod] += 10000000;
- stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
} else {
- stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20);
- stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2);
+ stv0900_write_reg(intp, KREFTMG, 0xc1);
+ intp->bw[demod] += 10000000;
+ intp->bw[demod] *= 15;
+ intp->bw[demod] /= 10;
+ }
- if (i_params->dmd2_symbol_rate < 2000000)
- stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x63);
- else
- stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70);
+ stv0900_write_reg(intp, TMGCFG2, 0xc1);
- if (i_params->dmd2_symbol_rate >= 10000000)
- stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
- else
- stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x60);
+ stv0900_set_symbol_rate(intp, intp->mclk,
+ intp->symbol_rate[demod], demod);
+ stv0900_set_max_symbol_rate(intp, intp->mclk,
+ intp->symbol_rate[demod], demod);
+ stv0900_set_min_symbol_rate(intp, intp->mclk,
+ intp->symbol_rate[demod], demod);
+ if (intp->symbol_rate[demod] >= 10000000)
+ low_sr = FALSE;
+ else
+ low_sr = TRUE;
- if (i_params->chip_id >= 0x20) {
- stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0x5a);
+ }
- if (i_params->dmd2_srch_algo == STV0900_COLD_START)
- i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate,
- i_params->rolloff) + 10000000)) / 10;
- else if (i_params->dmd2_srch_algo == STV0900_WARM_START)
- i_params->tuner2_bw = stv0900_carrier_width(i_params->dmd2_symbol_rate,
- i_params->rolloff) + 10000000;
- } else {
- stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0xc1);
- i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate,
- i_params->rolloff) + 10000000)) / 10;
- }
+ stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]);
- stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
+ agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1),
+ stv0900_get_bits(intp, AGCIQ_VALUE0));
- stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
- stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
- stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
- if (i_params->dmd2_symbol_rate >= 10000000)
- low_sr = FALSE;
- else
- low_sr = TRUE;
+ aq_power = 0;
- }
+ if (agc1_power == 0) {
+ for (i = 0; i < 5; i++)
+ aq_power += (stv0900_get_bits(intp, POWER_I) +
+ stv0900_get_bits(intp, POWER_Q)) / 2;
- stv0900_set_tuner(fe, i_params->tuner2_freq, i_params->tuner2_bw);
+ aq_power /= 5;
+ }
- stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, i_params->dmd2_srch_iq_inv);
- stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
+ if ((agc1_power == 0) && (aq_power < IQPOWER_THRESHOLD)) {
+ intp->result[demod].locked = FALSE;
+ signal_type = STV0900_NOAGC1;
+ dprintk("%s: NO AGC1, POWERI, POWERQ\n", __func__);
+ } else {
+ stv0900_write_bits(intp, SPECINV_CONTROL,
+ intp->srch_iq_inv[demod]);
+ if (intp->chip_id <= 0x20) /*cut 2.0*/
+ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1);
+ else /*cut 3.0*/
+ stv0900_write_bits(intp, MANUALS2_ROLLOFF, 1);
- stv0900_set_search_standard(i_params, demod);
+ stv0900_set_search_standard(intp, demod);
- if (i_params->dmd2_srch_algo != STV0900_BLIND_SEARCH)
- stv0900_start_search(i_params, demod);
- break;
+ if (intp->srch_algo[demod] != STV0900_BLIND_SEARCH)
+ stv0900_start_search(intp, demod);
}
- if (i_params->chip_id == 0x12) {
- stv0900_write_bits(i_params, stream_merger_field, 0);
+ if (signal_type == STV0900_NOAGC1)
+ return signal_type;
+
+ if (intp->chip_id == 0x12) {
+ stv0900_write_bits(intp, RST_HWARE, 0);
msleep(3);
- stv0900_write_bits(i_params, stream_merger_field, 1);
- stv0900_write_bits(i_params, stream_merger_field, 0);
+ stv0900_write_bits(intp, RST_HWARE, 1);
+ stv0900_write_bits(intp, RST_HWARE, 0);
}
if (algo == STV0900_BLIND_SEARCH)
@@ -2736,12 +1923,12 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
else if (algo == STV0900_COLD_START)
lock = stv0900_get_demod_cold_lock(fe, demod_timeout);
else if (algo == STV0900_WARM_START)
- lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
+ lock = stv0900_get_demod_lock(intp, demod, demod_timeout);
if ((lock == FALSE) && (algo == STV0900_COLD_START)) {
if (low_sr == FALSE) {
- if (stv0900_check_timing_lock(i_params, demod) == TRUE)
- lock = stv0900_sw_algo(i_params, demod);
+ if (stv0900_check_timing_lock(intp, demod) == TRUE)
+ lock = stv0900_sw_algo(intp, demod);
}
}
@@ -2750,98 +1937,64 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) {
stv0900_track_optimization(fe);
- if (i_params->chip_id <= 0x11) {
- if ((stv0900_get_standard(fe, STV0900_DEMOD_1) == STV0900_DVBS1_STANDARD) && (stv0900_get_standard(fe, STV0900_DEMOD_2) == STV0900_DVBS1_STANDARD)) {
+ if (intp->chip_id <= 0x11) {
+ if ((stv0900_get_standard(fe, 0) ==
+ STV0900_DVBS1_STANDARD) &&
+ (stv0900_get_standard(fe, 1) ==
+ STV0900_DVBS1_STANDARD)) {
msleep(20);
- stv0900_write_bits(i_params, stream_merger_field, 0);
+ stv0900_write_bits(intp, RST_HWARE, 0);
} else {
- stv0900_write_bits(i_params, stream_merger_field, 0);
+ stv0900_write_bits(intp, RST_HWARE, 0);
msleep(3);
- stv0900_write_bits(i_params, stream_merger_field, 1);
- stv0900_write_bits(i_params, stream_merger_field, 0);
+ stv0900_write_bits(intp, RST_HWARE, 1);
+ stv0900_write_bits(intp, RST_HWARE, 0);
}
- } else if (i_params->chip_id == 0x20) {
- stv0900_write_bits(i_params, stream_merger_field, 0);
+
+ } else if (intp->chip_id >= 0x20) {
+ stv0900_write_bits(intp, RST_HWARE, 0);
msleep(3);
- stv0900_write_bits(i_params, stream_merger_field, 1);
- stv0900_write_bits(i_params, stream_merger_field, 0);
+ stv0900_write_bits(intp, RST_HWARE, 1);
+ stv0900_write_bits(intp, RST_HWARE, 0);
}
- if (stv0900_wait_for_lock(i_params, demod, fec_timeout, fec_timeout) == TRUE) {
+ if (stv0900_wait_for_lock(intp, demod,
+ fec_timeout, fec_timeout) == TRUE) {
lock = TRUE;
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- i_params->dmd1_rslts.locked = TRUE;
- if (i_params->dmd1_rslts.standard == STV0900_DVBS2_STANDARD) {
- stv0900_set_dvbs2_rolloff(i_params, demod);
- stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0x40);
- stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0);
- stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67);
- } else {
- stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
- }
-
- stv0900_write_reg(i_params, R0900_P1_FBERCPT4, 0);
- stv0900_write_reg(i_params, R0900_P1_ERRCTRL2, 0xc1);
- break;
- case STV0900_DEMOD_2:
- i_params->dmd2_rslts.locked = TRUE;
-
- if (i_params->dmd2_rslts.standard == STV0900_DVBS2_STANDARD) {
- stv0900_set_dvbs2_rolloff(i_params, demod);
- stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x60);
- stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x20);
- stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67);
- } else {
- stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
- }
-
- stv0900_write_reg(i_params, R0900_P2_FBERCPT4, 0);
-
- stv0900_write_reg(i_params, R0900_P2_ERRCTRL2, 0xc1);
- break;
+ intp->result[demod].locked = TRUE;
+ if (intp->result[demod].standard ==
+ STV0900_DVBS2_STANDARD) {
+ stv0900_set_dvbs2_rolloff(intp, demod);
+ stv0900_write_bits(intp, RESET_UPKO_COUNT, 1);
+ stv0900_write_bits(intp, RESET_UPKO_COUNT, 0);
+ stv0900_write_reg(intp, ERRCTRL1, 0x67);
+ } else {
+ stv0900_write_reg(intp, ERRCTRL1, 0x75);
}
+
+ stv0900_write_reg(intp, FBERCPT4, 0);
+ stv0900_write_reg(intp, ERRCTRL2, 0xc1);
} else {
lock = FALSE;
signal_type = STV0900_NODATA;
- no_signal = stv0900_check_signal_presence(i_params, demod);
-
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- i_params->dmd1_rslts.locked = FALSE;
- break;
- case STV0900_DEMOD_2:
- i_params->dmd2_rslts.locked = FALSE;
- break;
- }
+ no_signal = stv0900_check_signal_presence(intp, demod);
+
+ intp->result[demod].locked = FALSE;
}
}
- if ((signal_type == STV0900_NODATA) && (no_signal == FALSE)) {
- switch (demod) {
- case STV0900_DEMOD_1:
- default:
- if (i_params->chip_id <= 0x11) {
- if ((stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) &&
- (i_params->dmd1_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST))
- signal_type = stv0900_dvbs1_acq_workaround(fe);
- } else
- i_params->dmd1_rslts.locked = FALSE;
+ if ((signal_type != STV0900_NODATA) || (no_signal != FALSE))
+ return signal_type;
- break;
- case STV0900_DEMOD_2:
- if (i_params->chip_id <= 0x11) {
- if ((stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) &&
- (i_params->dmd2_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST))
- signal_type = stv0900_dvbs1_acq_workaround(fe);
- } else
- i_params->dmd2_rslts.locked = FALSE;
- break;
- }
+ if (intp->chip_id > 0x11) {
+ intp->result[demod].locked = FALSE;
+ return signal_type;
}
+ if ((stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) &&
+ (intp->srch_iq_inv[demod] <= STV0900_IQ_AUTO_NORMAL_FIRST))
+ signal_type = stv0900_dvbs1_acq_workaround(fe);
+
return signal_type;
}
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index 488bdfb34fb..48edd542242 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -825,7 +825,7 @@ static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate
sym /= (state->mclk >> 7);
}
- if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0xff)) < 0) /* MSB */
+ if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */
goto err;
if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */
goto err;
@@ -1238,6 +1238,8 @@ static int stv090x_delivery_search(struct stv090x_state *state)
goto err;
}
+ if (stv090x_set_vit_thtracq(state) < 0)
+ goto err;
break;
case STV090x_SEARCH_AUTO:
@@ -1278,17 +1280,8 @@ static int stv090x_delivery_search(struct stv090x_state *state)
goto err;
}
- if (state->srate >= 2000000) {
- /* Srate >= 2MSPS, Viterbi threshold to acquire */
- if (stv090x_set_vit_thacq(state) < 0)
- goto err;
- } else {
- /* Srate < 2MSPS, Reset Viterbi thresholdto track
- * and then re-acquire
- */
- if (stv090x_set_vit_thtracq(state) < 0)
- goto err;
- }
+ if (stv090x_set_vit_thacq(state) < 0)
+ goto err;
if (stv090x_set_viterbi(state) < 0)
goto err;
@@ -1317,7 +1310,7 @@ static int stv090x_start_search(struct stv090x_state *state)
goto err;
if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0)
goto err;
- if (STV090x_WRITE_DEMOD(state, CFRUP1, 0xff) < 0)
+ if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0)
goto err;
@@ -1371,7 +1364,7 @@ static int stv090x_start_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0)
goto err;
- if (STV090x_WRITE_DEMOD(state, CFRUP1, LSB(freq)) < 0)
+ if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0)
goto err;
freq *= -1;
@@ -1422,6 +1415,9 @@ static int stv090x_start_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0)
goto err;
+ if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0)
+ goto err;
+
if (state->dev_ver >= 0x20) {
/*Frequency offset detector setting*/
if (state->srate < 2000000) {
@@ -1430,20 +1426,22 @@ static int stv090x_start_search(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0)
goto err;
} else {
- /* Cut 2 */
+ /* Cut 3 */
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0)
goto err;
}
if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0)
goto err;
- }
-
- if (state->srate < 10000000) {
+ } else if (state->srate < 10000000) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0)
goto err;
+ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
+ goto err;
} else {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0)
goto err;
+ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
+ goto err;
}
} else {
if (state->srate < 10000000) {
@@ -1485,14 +1483,14 @@ err:
static int stv090x_get_agc2_min_level(struct stv090x_state *state)
{
- u32 agc2_min = 0, agc2 = 0, freq_init, freq_step, reg;
+ u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg;
s32 i, j, steps, dir;
if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
goto err;
reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
goto err;
@@ -1509,10 +1507,8 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state)
if (stv090x_set_srate(state, 1000000) < 0)
goto err;
- steps = -1 + state->search_range / 1000000;
- steps /= 2;
- steps = (2 * steps) + 1;
- if (steps < 0)
+ steps = state->search_range / 1000000;
+ if (steps <= 0)
steps = 1;
dir = 1;
@@ -1525,7 +1521,7 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state)
else
freq_init = freq_init - (freq_step * i);
- dir = -1;
+ dir *= -1;
if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */
goto err;
@@ -1536,13 +1532,14 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */
goto err;
msleep(10);
+
+ agc2 = 0;
for (j = 0; j < 10; j++) {
- agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8;
- agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
+ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+ STV090x_READ_DEMOD(state, AGC2I0);
}
agc2 /= 10;
- agc2_min = 0xffff;
- if (agc2 < 0xffff)
+ if (agc2 < agc2_min)
agc2_min = agc2;
}
@@ -1584,6 +1581,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
int tmg_lock = 0, i;
s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq;
u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg;
+ u32 agc2th;
+
+ if (state->dev_ver >= 0x30)
+ agc2th = 0x2e00;
+ else
+ agc2th = 0x1f00;
reg = STV090x_READ_DEMOD(state, DMDISTATE);
STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */
@@ -1591,13 +1594,15 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
goto err;
if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0)
goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0)
+ goto err;
if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0)
goto err;
reg = STV090x_READ_DEMOD(state, DMDCFGMD);
STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
goto err;
@@ -1611,13 +1616,13 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
goto err;
if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0)
goto err;
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x60) < 0)
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0)
goto err;
if (state->dev_ver >= 0x30) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0)
goto err;
- if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0)
+ if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0)
goto err;
} else if (state->dev_ver >= 0x20) {
@@ -1652,23 +1657,31 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
while ((!tmg_lock) && (cur_step < steps)) {
if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */
goto err;
- reg = STV090x_READ_DEMOD(state, DMDISTATE);
- STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x00); /* trigger acquisition */
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0)
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0)
+ goto err;
+ /* trigger acquisition */
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0)
goto err;
msleep(50);
for (i = 0; i < 10; i++) {
reg = STV090x_READ_DEMOD(state, DSTATUS);
if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2)
tmg_cpt++;
- agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8;
- agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
+ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+ STV090x_READ_DEMOD(state, AGC2I0);
}
agc2 /= 10;
srate_coarse = stv090x_get_srate(state, state->mclk);
cur_step++;
dir *= -1;
- if ((tmg_cpt >= 5) && (agc2 < 0x1f00) && (srate_coarse < 55000000) && (srate_coarse > 850000))
+ if ((tmg_cpt >= 5) && (agc2 < agc2th) &&
+ (srate_coarse < 50000000) && (srate_coarse > 850000))
tmg_lock = 1;
else if (cur_step < steps) {
if (dir > 0)
@@ -1681,7 +1694,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
goto err;
if (state->config->tuner_set_frequency) {
- if (state->config->tuner_set_frequency(fe, state->frequency) < 0)
+ if (state->config->tuner_set_frequency(fe, freq) < 0)
goto err;
}
@@ -1738,7 +1751,7 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
else {
if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */
goto err;
- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0x01) < 0)
+ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0)
goto err;
@@ -1751,6 +1764,9 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
goto err;
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
+ goto err;
+
if (state->dev_ver >= 0x30) {
if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0)
goto err;
@@ -1856,12 +1872,12 @@ static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout)
static int stv090x_blind_search(struct stv090x_state *state)
{
u32 agc2, reg, srate_coarse;
- s32 timeout_dmd = 500, cpt_fail, agc2_ovflw, i;
+ s32 cpt_fail, agc2_ovflw, i;
u8 k_ref, k_max, k_min;
int coarse_fail, lock;
- k_max = 120;
- k_min = 30;
+ k_max = 110;
+ k_min = 10;
agc2 = stv090x_get_agc2_min_level(state);
@@ -1900,7 +1916,8 @@ static int stv090x_blind_search(struct stv090x_state *state)
srate_coarse = stv090x_srate_srch_fine(state);
if (srate_coarse != 0) {
stv090x_get_lock_tmg(state);
- lock = stv090x_get_dmdlock(state, timeout_dmd);
+ lock = stv090x_get_dmdlock(state,
+ state->DemodTimeout);
} else {
lock = 0;
}
@@ -1908,8 +1925,8 @@ static int stv090x_blind_search(struct stv090x_state *state)
cpt_fail = 0;
agc2_ovflw = 0;
for (i = 0; i < 10; i++) {
- agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8;
- agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
+ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+ STV090x_READ_DEMOD(state, AGC2I0);
if (agc2 >= 0xff00)
agc2_ovflw++;
reg = STV090x_READ_DEMOD(state, DSTATUS2);
@@ -1923,7 +1940,7 @@ static int stv090x_blind_search(struct stv090x_state *state)
lock = 0;
}
- k_ref -= 30;
+ k_ref -= 20;
} while ((k_ref >= k_min) && (!lock) && (!coarse_fail));
}
@@ -2062,7 +2079,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
goto err;
if (state->config->tuner_set_frequency) {
- if (state->config->tuner_set_frequency(fe, state->frequency) < 0)
+ if (state->config->tuner_set_frequency(fe, freq) < 0)
goto err;
}
@@ -2093,17 +2110,6 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
goto err;
STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c);
- if (state->delsys == STV090x_DVBS2) {
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- }
if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
goto err;
if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
@@ -2425,7 +2431,7 @@ static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk)
derot = (int_1 * int_2) +
((int_1 * tmp_2) >> 12) +
- ((int_1 * tmp_1) >> 12);
+ ((int_2 * tmp_1) >> 12);
return derot;
}
@@ -2732,7 +2738,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
switch (state->delsys) {
case STV090x_DVBS1:
case STV090x_DSS:
- if (state->algo == STV090x_SEARCH_AUTO) {
+ if (state->search_mode == STV090x_SEARCH_AUTO) {
reg = STV090x_READ_DEMOD(state, DMDCFGMD);
STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
@@ -2857,6 +2863,9 @@ static int stv090x_optimize_track(struct stv090x_state *state)
if (stv090x_set_srate(state, srate) < 0)
goto err;
blind_tune = 1;
+
+ if (stv090x_dvbs_track_crl(state) < 0)
+ goto err;
}
if (state->dev_ver >= 0x20) {
@@ -3042,7 +3051,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
struct dvb_frontend *fe = &state->frontend;
enum stv090x_signal_state signal_state = STV090x_NOCARRIER;
u32 reg;
- s32 timeout_dmd = 500, timeout_fec = 50, agc1_power, power_iq = 0, i;
+ s32 agc1_power, power_iq = 0, i;
int lock = 0, low_sr = 0, no_signal = 0;
reg = STV090x_READ_DEMOD(state, TSCFGH);
@@ -3054,8 +3063,13 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
goto err;
if (state->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) /* cut 2.0 */
- goto err;
+ if (state->srate > 5000000) {
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0)
+ goto err;
+ }
}
stv090x_get_lock_tmg(state);
@@ -3175,7 +3189,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) {
dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq);
lock = 0;
-
+ signal_state = STV090x_NOAGC1;
} else {
reg = STV090x_READ_DEMOD(state, DEMOD);
STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion);
@@ -3199,18 +3213,17 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
}
}
- /* need to check for AGC1 state */
-
-
+ if (signal_state == STV090x_NOAGC1)
+ return signal_state;
if (state->algo == STV090x_BLIND_SEARCH)
lock = stv090x_blind_search(state);
else if (state->algo == STV090x_COLD_SEARCH)
- lock = stv090x_get_coldlock(state, timeout_dmd);
+ lock = stv090x_get_coldlock(state, state->DemodTimeout);
else if (state->algo == STV090x_WARM_SEARCH)
- lock = stv090x_get_dmdlock(state, timeout_dmd);
+ lock = stv090x_get_dmdlock(state, state->DemodTimeout);
if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) {
if (!low_sr) {
@@ -3245,8 +3258,9 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
goto err;
}
- if (stv090x_get_lock(state, timeout_fec, timeout_fec)) {
- lock = 1;
+ lock = stv090x_get_lock(state, state->FecTimeout,
+ state->FecTimeout);
+ if (lock) {
if (state->delsys == STV090x_DVBS2) {
stv090x_set_s2rolloff(state);
@@ -3273,7 +3287,6 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0)
goto err;
} else {
- lock = 0;
signal_state = STV090x_NODATA;
no_signal = stv090x_chk_signal(state);
}
@@ -3296,7 +3309,13 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron
state->search_mode = STV090x_SEARCH_AUTO;
state->algo = STV090x_COLD_SEARCH;
state->fec = STV090x_PRERR;
- state->search_range = 2000000;
+ if (state->srate > 10000000) {
+ dprintk(FE_DEBUG, 1, "Search range: 10 MHz");
+ state->search_range = 10000000;
+ } else {
+ dprintk(FE_DEBUG, 1, "Search range: 5 MHz");
+ state->search_range = 5000000;
+ }
if (stv090x_algo(state) == STV090x_RANGEOK) {
dprintk(FE_DEBUG, 1, "Search success!");
@@ -3309,7 +3328,6 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron
return DVBFE_ALGO_SEARCH_ERROR;
}
-/* FIXME! */
static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
{
struct stv090x_state *state = fe->demodulator_priv;
@@ -3331,9 +3349,15 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2");
reg = STV090x_READ_DEMOD(state, DSTATUS);
if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) {
- reg = STV090x_READ_DEMOD(state, TSSTATUS);
- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
- *status = FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
+ if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) {
+ reg = STV090x_READ_DEMOD(state, TSSTATUS);
+ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
+ *status = FE_HAS_CARRIER |
+ FE_HAS_VITERBI |
+ FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
}
}
break;
@@ -3412,14 +3436,12 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val)
int res = 0;
int min = 0, med;
- if (val < tab[min].read)
- res = tab[min].real;
- else if (val >= tab[max].read)
- res = tab[max].real;
- else {
+ if ((val >= tab[min].read && val < tab[max].read) ||
+ (val >= tab[max].read && val < tab[min].read)) {
while ((max - min) > 1) {
med = (max + min) / 2;
- if (val >= tab[min].read && val < tab[med].read)
+ if ((val >= tab[min].read && val < tab[med].read) ||
+ (val >= tab[med].read && val < tab[min].read))
max = med;
else
min = med;
@@ -3428,6 +3450,18 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val)
(tab[max].real - tab[min].real) /
(tab[max].read - tab[min].read)) +
tab[min].real;
+ } else {
+ if (tab[min].read < tab[max].read) {
+ if (val < tab[min].read)
+ res = tab[min].real;
+ else if (val >= tab[max].read)
+ res = tab[max].real;
+ } else {
+ if (val >= tab[min].read)
+ res = tab[min].real;
+ else if (val < tab[max].read)
+ res = tab[max].real;
+ }
}
return res;
@@ -3437,16 +3471,22 @@ static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
struct stv090x_state *state = fe->demodulator_priv;
u32 reg;
- s32 agc;
+ s32 agc_0, agc_1, agc;
+ s32 str;
reg = STV090x_READ_DEMOD(state, AGCIQIN1);
- agc = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+ agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+ reg = STV090x_READ_DEMOD(state, AGCIQIN0);
+ agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+ agc = MAKEWORD16(agc_1, agc_0);
- *strength = stv090x_table_lookup(stv090x_rf_tab, ARRAY_SIZE(stv090x_rf_tab) - 1, agc);
+ str = stv090x_table_lookup(stv090x_rf_tab,
+ ARRAY_SIZE(stv090x_rf_tab) - 1, agc);
if (agc > stv090x_rf_tab[0].read)
- *strength = 5;
+ str = 0;
else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read)
- *strength = -100;
+ str = -100;
+ *strength = (str + 100) * 0xFFFF / 100;
return 0;
}
@@ -3457,6 +3497,8 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
u32 reg_0, reg_1, reg, i;
s32 val_0, val_1, val = 0;
u8 lock_f;
+ s32 div;
+ u32 last;
switch (state->delsys) {
case STV090x_DVBS2:
@@ -3468,14 +3510,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1);
val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD);
reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0);
- val_0 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD);
+ val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD);
val += MAKEWORD16(val_1, val_0);
msleep(1);
}
val /= 16;
- *cnr = stv090x_table_lookup(stv090x_s2cn_tab, ARRAY_SIZE(stv090x_s2cn_tab) - 1, val);
- if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s2cn_tab) - 1].read)
- *cnr = 1000;
+ last = ARRAY_SIZE(stv090x_s2cn_tab) - 1;
+ div = stv090x_s2cn_tab[0].read -
+ stv090x_s2cn_tab[last].read;
+ *cnr = 0xFFFF - ((val * 0xFFFF) / div);
}
break;
@@ -3489,14 +3532,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1);
val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD);
reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0);
- val_0 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD);
+ val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD);
val += MAKEWORD16(val_1, val_0);
msleep(1);
}
val /= 16;
- *cnr = stv090x_table_lookup(stv090x_s1cn_tab, ARRAY_SIZE(stv090x_s1cn_tab) - 1, val);
- if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s1cn_tab) - 1].read)
- *cnr = 1000;
+ last = ARRAY_SIZE(stv090x_s1cn_tab) - 1;
+ div = stv090x_s1cn_tab[0].read -
+ stv090x_s1cn_tab[last].read;
+ *cnr = 0xFFFF - ((val * 0xFFFF) / div);
}
break;
default:
@@ -3732,6 +3776,8 @@ static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc
{
u32 reg = 0;
+ reg = stv090x_read_reg(state, STV090x_GENCFG);
+
switch (ldpc_mode) {
case STV090x_DUAL:
default:
diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h
index 5a4a01740d8..5921a8d6c89 100644
--- a/drivers/media/dvb/frontends/stv090x_priv.h
+++ b/drivers/media/dvb/frontends/stv090x_priv.h
@@ -83,7 +83,7 @@
#define STV090x_IQPOWER_THRESHOLD 30
#define STV090x_SEARCH_AGC2_TH_CUT20 700
-#define STV090x_SEARCH_AGC2_TH_CUT30 1200
+#define STV090x_SEARCH_AGC2_TH_CUT30 1400
#define STV090x_SEARCH_AGC2_TH(__ver) \
((__ver <= 0x20) ? \
@@ -91,6 +91,7 @@
STV090x_SEARCH_AGC2_TH_CUT30)
enum stv090x_signal_state {
+ STV090x_NOAGC1,
STV090x_NOCARRIER,
STV090x_NODATA,
STV090x_DATAOK,
diff --git a/drivers/media/dvb/frontends/stv090x_reg.h b/drivers/media/dvb/frontends/stv090x_reg.h
index 57b6abbbd32..2502855dd78 100644
--- a/drivers/media/dvb/frontends/stv090x_reg.h
+++ b/drivers/media/dvb/frontends/stv090x_reg.h
@@ -44,7 +44,7 @@
#define STV090x_OFFST_OUTSERRS2_HZ_FIELD 5
#define STV090x_WIDTH_OUTSERRS2_HZ_FIELD 1
#define STV090x_OFFST_OUTSERRS3_HZ_FIELD 4
-#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1
+#define STV090x_WIDTH_OUTSERRS3_HZ_FIELD 1
#define STV090x_OFFST_OUTPARRS3_HZ_FIELD 3
#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1
@@ -113,24 +113,24 @@
#define STV090x_IRQMASK3 0xf124
#define STV090x_OFFST_MPLL_LOCK_FIELD 5
#define STV090x_WIDTH_MPLL_LOCK_FIELD 1
-#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 2
-#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 3
-#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 2
-#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 3
+#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 4
+#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 1
+#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 3
+#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 1
#define STV090x_OFFST_MSTREAM_LCK_1_FIELD 2
-#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 3
+#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 1
#define STV090x_OFFST_MDVBS1_PRF_2_FIELD 1
#define STV090x_WIDTH_MDVBS1_PRF_2_FIELD 1
#define STV090x_OFFST_MDVBS1_PRF_1_FIELD 0
#define STV090x_WIDTH_MDVBS1_PRF_1_FIELD 1
#define STV090x_IRQMASK2 0xf125
-#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 5
-#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 3
-#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 5
-#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 3
+#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 7
+#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 1
+#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 6
+#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 1
#define STV090x_OFFST_MSPY_ENDSIM_1_FIELD 5
-#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 3
+#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 1
#define STV090x_OFFST_MPKTDEL_ERROR_2_FIELD 4
#define STV090x_WIDTH_MPKTDEL_ERROR_2_FIELD 1
#define STV090x_OFFST_MPKTDEL_LOCKB_2_FIELD 3
@@ -370,7 +370,7 @@
#define STV090x_OFFST_SELX1RATIO_FIELD 5
#define STV090x_WIDTH_SELX1RATIO_FIELD 1
#define STV090x_OFFST_STOP_PLL_FIELD 3
-#define STV090x_WIDTH_SELX1RATIO_FIELD 1
+#define STV090x_WIDTH_STOP_PLL_FIELD 1
#define STV090x_OFFST_BYPASSPLLFSK_FIELD 2
#define STV090x_WIDTH_BYPASSPLLFSK_FIELD 1
#define STV090x_OFFST_SELOSCI_FIELD 1
@@ -616,7 +616,7 @@
#define STV090x_OFFST_Px_CONT_TONE_FIELD 4
#define STV090x_WIDTH_Px_CONT_TONE_FIELD 1
#define STV090x_OFFST_Px_FIFO_4BREADY_FIELD 3
-#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 2
+#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 1
#define STV090x_OFFST_Px_FIFO_EMPTY_FIELD 2
#define STV090x_WIDTH_Px_FIFO_EMPTY_FIELD 1
#define STV090x_OFFST_Px_ABORT_DISRX_FIELD 0
@@ -847,12 +847,10 @@
#define STV090x_WIDTH_Px_DVBS2_ENABLE_FIELD 1
#define STV090x_OFFST_Px_DVBS1_ENABLE_FIELD 6
#define STV090x_WIDTH_Px_DVBS1_ENABLE_FIELD 1
-#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 5 /* check */
-#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1
-#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 /* check */
+#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4
#define STV090x_WIDTH_Px_SCAN_ENABLE_FIELD 1
-#define STV090x_OFFST_Px_TUN_AUTOSCAN_FIELD 3
-#define STV090x_WIDTH_Px_TUN_AUTOSCAN_FIELD 1
+#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 3
+#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1
#define STV090x_OFFST_Px_NOFORCE_RELOCK_FIELD 2
#define STV090x_WIDTH_Px_NOFORCE_RELOCK_FIELD 1
#define STV090x_OFFST_Px_TUN_RNG_FIELD 0
@@ -885,7 +883,7 @@
#define STV090x_P2_DMDFLYW STV090x_Px_DMDFLYW(2)
#define STV090x_OFFST_Px_I2C_IRQVAL_FIELD 4
#define STV090x_WIDTH_Px_I2C_IRQVAL_FIELD 4
-#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 /* check */
+#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0
#define STV090x_WIDTH_Px_FLYWHEEL_CPT_FIELD 4
#define STV090x_Px_DSTATUS3(__x) (0xF41D - (__x - 1) * 0x200)
@@ -1048,12 +1046,12 @@
#define STV090x_P1_CFRINC1 STV090x_Px_CFRINC1(1)
#define STV090x_P2_CFRINC1 STV090x_Px_CFRINC1(2)
#define STV090x_OFFST_Px_CFR_INC1_FIELD 0
-#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7
+#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 /* check */
#define STV090x_Px_CFRINC0(__x) (0xF44B - (__x - 1) * 0x200)
#define STV090x_P1_CFRINC0 STV090x_Px_CFRINC0(1)
#define STV090x_P2_CFRINC0 STV090x_Px_CFRINC0(2)
-#define STV090x_OFFST_Px_CFR_INC0_FIELD 4
+#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 /* check */
#define STV090x_WIDTH_Px_CFR_INC0_FIELD 4
#define STV090x_Pn_CFRy(__x, __y) (0xF44E - (__x - 1) * 0x200 - __y * 0x1)
@@ -1145,14 +1143,14 @@
#define STV090x_Px_SFRINIT1(__x) (0xF45E - (__x - 1) * 0x200)
#define STV090x_P1_SFRINIT1 STV090x_Px_SFRINIT1(1)
#define STV090x_P2_SFRINIT1 STV090x_Px_SFRINIT1(2)
-#define STV090x_OFFST_Px_SFR_INIT_FIELD 0
-#define STV090x_WIDTH_Px_SFR_INIT_FIELD 8
+#define STV090x_OFFST_Px_SFR_INIT1_FIELD 0
+#define STV090x_WIDTH_Px_SFR_INIT1_FIELD 7
#define STV090x_Px_SFRINIT0(__x) (0xF45F - (__x - 1) * 0x200)
#define STV090x_P1_SFRINIT0 STV090x_Px_SFRINIT0(1)
#define STV090x_P2_SFRINIT0 STV090x_Px_SFRINIT0(2)
-#define STV090x_OFFST_Px_SFR_INIT_FIELD 0
-#define STV090x_WIDTH_Px_SFR_INIT_FIELD 8
+#define STV090x_OFFST_Px_SFR_INIT0_FIELD 0
+#define STV090x_WIDTH_Px_SFR_INIT0_FIELD 8
#define STV090x_Px_SFRUP1(__x) (0xF460 - (__x - 1) * 0x200)
#define STV090x_P1_SFRUP1 STV090x_Px_SFRUP1(1)
@@ -1178,7 +1176,7 @@
#define STV090x_OFFST_Px_SYMB_FREQ_LOW0_FIELD 0
#define STV090x_WIDTH_Px_SYMB_FREQ_LOW0_FIELD 8
-#define STV090x_Px_SFRy(__x, __y) (0xF464 - (__x-1) * 0x200 + (3 - __y))
+#define STV090x_Px_SFRy(__x, __y) (0xF467 - (__x-1) * 0x200 - __y)
#define STV090x_P1_SFR0 STV090x_Px_SFRy(1, 0)
#define STV090x_P1_SFR1 STV090x_Px_SFRy(1, 1)
#define STV090x_P1_SFR2 STV090x_Px_SFRy(1, 2)
@@ -1188,7 +1186,7 @@
#define STV090x_P2_SFR2 STV090x_Px_SFRy(2, 2)
#define STV090x_P2_SFR3 STV090x_Px_SFRy(2, 3)
#define STV090x_OFFST_Px_SYMB_FREQ_FIELD 0
-#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 32
+#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 8
#define STV090x_Px_TMGREG2(__x) (0xF468 - (__x - 1) * 0x200)
#define STV090x_P1_TMGREG2 STV090x_Px_TMGREG2(1)
@@ -1198,7 +1196,7 @@
#define STV090x_Px_TMGREG1(__x) (0xF469 - (__x - 1) * 0x200)
#define STV090x_P1_TMGREG1 STV090x_Px_TMGREG1(1)
-#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2)
+#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2)
#define STV090x_OFFST_Px_TMGREG_FIELD 0
#define STV090x_WIDTH_Px_TMGREG_FIELD 8
@@ -1230,7 +1228,7 @@
#define STV090x_OFFST_Px_MU_EQUALDFE_FIELD 0
#define STV090x_WIDTH_Px_MU_EQUALDFE_FIELD 3
-#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x - 1) * 0x200 + (__y - 1))
+#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x-1) * 0x200 + 2 * (__y-1))
#define STV090x_P1_EQUAI1 STV090x_Px_EQUAIy(1, 1)
#define STV090x_P1_EQUAI2 STV090x_Px_EQUAIy(1, 2)
#define STV090x_P1_EQUAI3 STV090x_Px_EQUAIy(1, 3)
@@ -1251,7 +1249,7 @@
#define STV090x_OFFST_Px_EQUA_ACCIy_FIELD 0
#define STV090x_WIDTH_Px_EQUA_ACCIy_FIELD 8
-#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x - 1) * 0x200 + (__y - 1))
+#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x-1) * 0x200 + 2 * (__y-1))
#define STV090x_P1_EQUAQ1 STV090x_Px_EQUAQy(1, 1)
#define STV090x_P1_EQUAQ2 STV090x_Px_EQUAQy(1, 2)
#define STV090x_P1_EQUAQ3 STV090x_Px_EQUAQy(1, 3)
@@ -1390,7 +1388,7 @@
#define STV090x_OFFST_Px_CAR2S2_16A_ALPH_E_FIELD 0
#define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_E_FIELD 4
-#define STV090x_Px_ACLC2S232A(__x) (0xf499 - (__x - 1) * 0x200)
+#define STV090x_Px_ACLC2S232A(__x) (0xf49A - (__x - 1) * 0x200)
#define STV090x_P1_ACLC2S232A STV090x_Px_ACLC2S232A(1)
#define STV090x_P2_ACLC2S232A STV090x_Px_ACLC2S232A(2)
#define STV090x_OFFST_Px_CAR2S2_32A_ALPH_M_FIELD 4
@@ -1414,7 +1412,7 @@
#define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0
#define STV090x_WIDTH_Px_CAR2S2_8_BETA_E_FIELD 4
-#define STV090x_Px_BCLC2S216A(__x) (0xf49d - (__x - 1) * 0x200)
+#define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200)
#define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1)
#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(1)
#define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4
@@ -1422,7 +1420,7 @@
#define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0
#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_E_FIELD 4
-#define STV090x_Px_BCLC2S232A(__x) (0xf49d - (__x - 1) * 0x200)
+#define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200)
#define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1)
#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(1)
#define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4
@@ -1458,7 +1456,7 @@
#define STV090x_P1_MODCODLST1 STV090x_Px_MODCODLST1(1)
#define STV090x_P2_MODCODLST1 STV090x_Px_MODCODLST1(2)
#define STV090x_OFFST_Px_DIS_MODCOD29_FIELD 4
-#define STV090x_WIDTH_Px_DIS_MODCOD29T_FIELD 4
+#define STV090x_WIDTH_Px_DIS_MODCOD29_FIELD 4
#define STV090x_OFFST_Px_DIS_32PSK_9_10_FIELD 0
#define STV090x_WIDTH_Px_DIS_32PSK_9_10_FIELD 4
@@ -2180,7 +2178,7 @@
#define STV090x_WIDTH_Px_TSFIFOSPEED_STORE_FIELD 1
#define STV090x_OFFST_Px_DILXX_RESET_FIELD 5
#define STV090x_WIDTH_Px_DILXX_RESET_FIELD 1
-#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 5
+#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 4
#define STV090x_WIDTH_Px_TSSERIAL_IMPOS_FIELD 1
#define STV090x_OFFST_Px_SCRAMBDETECT_FIELD 1
#define STV090x_WIDTH_Px_SCRAMBDETECT_FIELD 1
@@ -2190,7 +2188,7 @@
#define STV090x_P1_TSBITRATE1 STV090x_Px_TSBITRATEy(1, 1)
#define STV090x_P2_TSBITRATE0 STV090x_Px_TSBITRATEy(2, 0)
#define STV090x_P2_TSBITRATE1 STV090x_Px_TSBITRATEy(2, 1)
-#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 7
+#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 0
#define STV090x_WIDTH_Px_TSFIFO_BITRATE_FIELD 8
#define STV090x_Px_ERRCTRL1(__x) (0xF598 - (__x - 1) * 0x200)
diff --git a/drivers/media/dvb/frontends/stv6110.c b/drivers/media/dvb/frontends/stv6110.c
index dcf1b21ea97..bef0cc83847 100644
--- a/drivers/media/dvb/frontends/stv6110.c
+++ b/drivers/media/dvb/frontends/stv6110.c
@@ -37,6 +37,7 @@ struct stv6110_priv {
u32 mclk;
u8 clk_div;
+ u8 gain;
u8 regs[8];
};
@@ -255,7 +256,7 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency)
u8 ret = 0x04;
u32 divider, ref, p, presc, i, result_freq, vco_freq;
s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val;
- s32 srate; u8 gain;
+ s32 srate;
dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__,
frequency, priv->mclk);
@@ -273,15 +274,8 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency)
} else
srate = 15000000;
- if (srate >= 15000000)
- gain = 3; /* +6 dB */
- else if (srate >= 5000000)
- gain = 3; /* +6 dB */
- else
- gain = 3; /* +6 dB */
-
priv->regs[RSTV6110_CTRL2] &= ~0x0f;
- priv->regs[RSTV6110_CTRL2] |= (gain & 0x0f);
+ priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f);
if (frequency <= 1023000) {
p = 1;
@@ -436,6 +430,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
priv->i2c = i2c;
priv->mclk = config->mclk;
priv->clk_div = config->clk_div;
+ priv->gain = config->gain;
memcpy(&priv->regs, &reg0[1], 8);
diff --git a/drivers/media/dvb/frontends/stv6110.h b/drivers/media/dvb/frontends/stv6110.h
index 9db2402410f..fe71bba6a26 100644
--- a/drivers/media/dvb/frontends/stv6110.h
+++ b/drivers/media/dvb/frontends/stv6110.h
@@ -41,6 +41,7 @@
struct stv6110_config {
u8 i2c_address;
u32 mclk;
+ u8 gain;
u8 clk_div; /* divisor value for the output clock */
};
diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c
index 3d8a2e01c9c..bcfcb652464 100644
--- a/drivers/media/dvb/frontends/stv6110x.c
+++ b/drivers/media/dvb/frontends/stv6110x.c
@@ -95,7 +95,7 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency)
{
struct stv6110x_state *stv6110x = fe->tuner_priv;
u32 rDiv, divider;
- s32 pVal, pCalc, rDivOpt = 0;
+ s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000;
u8 i;
STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16));
@@ -121,8 +121,10 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency)
for (rDiv = 0; rDiv <= 3; rDiv++) {
pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv);
- if ((abs((s32)(pCalc - pVal))) < (abs((s32)(1000 - pVal))))
+ if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal))))
rDivOpt = rDiv;
+
+ pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt);
}
divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz;
diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c
index 1fd8306371e..81e623a90f0 100644
--- a/drivers/media/dvb/pt1/pt1.c
+++ b/drivers/media/dvb/pt1/pt1.c
@@ -27,7 +27,6 @@
#include <linux/pci.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
-#include <linux/vmalloc.h>
#include "dvbdev.h"
#include "dvb_demux.h"
diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c
index 2db940f8635..fc6594996e7 100644
--- a/drivers/media/dvb/pt1/va1j5jf8007s.c
+++ b/drivers/media/dvb/pt1/va1j5jf8007s.c
@@ -48,6 +48,60 @@ struct va1j5jf8007s_state {
enum va1j5jf8007s_tune_state tune_state;
};
+static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct va1j5jf8007s_state *state;
+ u8 addr;
+ int i;
+ u8 write_buf[1], read_buf[1];
+ struct i2c_msg msgs[2];
+ s32 word, x1, x2, x3, x4, x5, y;
+
+ state = fe->demodulator_priv;
+ addr = state->config->demod_address;
+
+ word = 0;
+ for (i = 0; i < 2; i++) {
+ write_buf[0] = 0xbc + i;
+
+ msgs[0].addr = addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(write_buf);
+ msgs[0].buf = write_buf;
+
+ msgs[1].addr = addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(read_buf);
+ msgs[1].buf = read_buf;
+
+ if (i2c_transfer(state->adap, msgs, 2) != 2)
+ return -EREMOTEIO;
+
+ word <<= 8;
+ word |= read_buf[0];
+ }
+
+ word -= 3000;
+ if (word < 0)
+ word = 0;
+
+ x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000);
+ x2 = (s64)x1 * x1 >> 31;
+ x3 = (s64)x2 * x1 >> 31;
+ x4 = (s64)x2 * x2 >> 31;
+ x5 = (s64)x4 * x1 >> 31;
+
+ y = (58857ll << 23) / 1000;
+ y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30;
+ y += (s64)x2 * ((88977ll << 24) / 1000) >> 28;
+ y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27;
+ y += (s64)x4 * ((14341ll << 27) / 1000) >> 27;
+ y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28;
+
+ *snr = y < 0 ? 0 : y >> 15;
+ return 0;
+}
+
static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
@@ -337,7 +391,7 @@ va1j5jf8007s_tune(struct dvb_frontend *fe,
{
struct va1j5jf8007s_state *state;
int ret;
- int lock;
+ int lock = 0;
state = fe->demodulator_priv;
@@ -536,6 +590,7 @@ static struct dvb_frontend_ops va1j5jf8007s_ops = {
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
},
+ .read_snr = va1j5jf8007s_read_snr,
.get_frontend_algo = va1j5jf8007s_get_frontend_algo,
.read_status = va1j5jf8007s_read_status,
.tune = va1j5jf8007s_tune,
diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c
index 71117f4ca7e..3db4f3e34e8 100644
--- a/drivers/media/dvb/pt1/va1j5jf8007t.c
+++ b/drivers/media/dvb/pt1/va1j5jf8007t.c
@@ -46,6 +46,52 @@ struct va1j5jf8007t_state {
enum va1j5jf8007t_tune_state tune_state;
};
+static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct va1j5jf8007t_state *state;
+ u8 addr;
+ int i;
+ u8 write_buf[1], read_buf[1];
+ struct i2c_msg msgs[2];
+ s32 word, x, y;
+
+ state = fe->demodulator_priv;
+ addr = state->config->demod_address;
+
+ word = 0;
+ for (i = 0; i < 3; i++) {
+ write_buf[0] = 0x8b + i;
+
+ msgs[0].addr = addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(write_buf);
+ msgs[0].buf = write_buf;
+
+ msgs[1].addr = addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(read_buf);
+ msgs[1].buf = read_buf;
+
+ if (i2c_transfer(state->adap, msgs, 2) != 2)
+ return -EREMOTEIO;
+
+ word <<= 8;
+ word |= read_buf[0];
+ }
+
+ if (!word)
+ return -EIO;
+
+ x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24));
+ y = (24ll << 46) / 1000000;
+ y = ((s64)y * x >> 30) - (16ll << 40) / 10000;
+ y = ((s64)y * x >> 29) + (398ll << 35) / 10000;
+ y = ((s64)y * x >> 30) + (5491ll << 29) / 10000;
+ y = ((s64)y * x >> 30) + (30965ll << 23) / 10000;
+ *snr = y >> 15;
+ return 0;
+}
+
static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
@@ -224,7 +270,7 @@ va1j5jf8007t_tune(struct dvb_frontend *fe,
{
struct va1j5jf8007t_state *state;
int ret;
- int lock, retry;
+ int lock = 0, retry = 0;
state = fe->demodulator_priv;
@@ -393,6 +439,7 @@ static struct dvb_frontend_ops va1j5jf8007t_ops = {
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
},
+ .read_snr = va1j5jf8007t_read_snr,
.get_frontend_algo = va1j5jf8007t_get_frontend_algo,
.read_status = va1j5jf8007t_read_status,
.tune = va1j5jf8007t_tune,
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
index 0420e2885e7..1067b22eb0c 100644
--- a/drivers/media/dvb/siano/sms-cards.c
+++ b/drivers/media/dvb/siano/sms-cards.c
@@ -97,7 +97,7 @@ static struct sms_board sms_boards[] = {
},
};
-struct sms_board *sms_get_board(int id)
+struct sms_board *sms_get_board(unsigned id)
{
BUG_ON(id >= ARRAY_SIZE(sms_boards));
@@ -294,6 +294,8 @@ int sms_board_load_modules(int id)
case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A:
case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B:
case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
request_module("smsdvb");
break;
default:
diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h
index 38f062f6ad6..8f19fc000b4 100644
--- a/drivers/media/dvb/siano/sms-cards.h
+++ b/drivers/media/dvb/siano/sms-cards.h
@@ -81,7 +81,7 @@ struct sms_board {
int led_power, led_hi, led_lo, lna_ctrl, rf_switch;
};
-struct sms_board *sms_get_board(int id);
+struct sms_board *sms_get_board(unsigned id);
extern struct smscore_device_t *coredev;
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
index fa6a62369a7..ca758bcb48c 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/dvb/siano/smscoreapi.c
@@ -1373,7 +1373,7 @@ static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum,
*pGroupCfg = 1;
- if (PinNum >= 0 && PinNum <= 1) {
+ if (PinNum <= 1) {
*pTranslatedPinNum = 0;
*pGroupNum = 9;
*pGroupCfg = 2;
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
index d1d652e7f89..24206cbda26 100644
--- a/drivers/media/dvb/siano/smssdio.c
+++ b/drivers/media/dvb/siano/smssdio.c
@@ -78,7 +78,7 @@ struct smssdio_device {
static int smssdio_sendrequest(void *context, void *buffer, size_t size)
{
- int ret;
+ int ret = 0;
struct smssdio_device *smsdev;
smsdev = context;
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 8d65c652ba5..baf3159a3aa 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -2425,7 +2425,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
* use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called
*/
saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
- /* set event counter 1 treshold to maximum allowed value (rEC p55) */
+ /* set event counter 1 threshold to maximum allowed value (rEC p55) */
saa7146_write(dev, ECT1R, 0x3fff );
#endif
/* Set RPS1 Address register to point to RPS code (r108 p42) */
@@ -2559,7 +2559,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
* use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called
*/
saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
- /* set event counter 1 treshold to maximum allowed value (rEC p55) */
+ /* set event counter 1 threshold to maximum allowed value (rEC p55) */
saa7146_write(dev, ECT1R, 0x3fff );
#endif
/* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 8ea91522767..983672aa245 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -1055,7 +1055,7 @@ static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = {
{ STB0899_TSCFGH , 0x0c },
{ STB0899_TSCFGM , 0x00 },
{ STB0899_TSCFGL , 0x0c },
- { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */
+ { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */
{ STB0899_RSSYNCDEL , 0x00 },
{ STB0899_TSINHDELH , 0x02 },
{ STB0899_TSINHDELM , 0x00 },
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index b5c681372b6..7d193ebc0ae 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -178,7 +178,7 @@ static void msp430_ir_interrupt(unsigned long data)
if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) {
ir_input_nokey(dev, &budget_ci->ir.state);
ir_input_keydown(dev, &budget_ci->ir.state,
- budget_ci->ir.ir_key, raw);
+ budget_ci->ir.ir_key);
budget_ci->ir.last_raw = raw;
}
@@ -224,8 +224,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
case 0x1011:
case 0x1012:
/* The hauppauge keymap is a superset of these remotes */
- ir_input_init(input_dev, &budget_ci->ir.state,
+ error = ir_input_init(input_dev, &budget_ci->ir.state,
IR_TYPE_RC5, &ir_codes_hauppauge_new_table);
+ if (error < 0)
+ goto out2;
if (rc5_device < 0)
budget_ci->ir.rc5_device = 0x1f;
@@ -236,8 +238,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
case 0x1017:
case 0x101a:
/* for the Technotrend 1500 bundled remote */
- ir_input_init(input_dev, &budget_ci->ir.state,
+ error = ir_input_init(input_dev, &budget_ci->ir.state,
IR_TYPE_RC5, &ir_codes_tt_1500_table);
+ if (error < 0)
+ goto out2;
if (rc5_device < 0)
budget_ci->ir.rc5_device = IR_DEVICE_ANY;
@@ -246,8 +250,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
break;
default:
/* unknown remote */
- ir_input_init(input_dev, &budget_ci->ir.state,
+ error = ir_input_init(input_dev, &budget_ci->ir.state,
IR_TYPE_RC5, &ir_codes_budget_ci_old_table);
+ if (error < 0)
+ goto out2;
if (rc5_device < 0)
budget_ci->ir.rc5_device = IR_DEVICE_ANY;
@@ -280,6 +286,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
return 0;
out2:
+ ir_input_free(input_dev);
input_free_device(input_dev);
out1:
return error;
@@ -297,6 +304,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
del_timer_sync(&dev->timer);
ir_input_nokey(dev, &budget_ci->ir.state);
+ ir_input_free(dev);
input_unregister_device(dev);
}
@@ -1248,7 +1256,7 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
{ STB0899_TSCFGH , 0x0c },
{ STB0899_TSCFGM , 0x00 },
{ STB0899_TSCFGL , 0x0c },
- { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */
+ { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */
{ STB0899_RSSYNCDEL , 0x00 },
{ STB0899_TSINHDELH , 0x02 },
{ STB0899_TSINHDELM , 0x00 },
diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c
index 60136688a9a..9c92f9ddd22 100644
--- a/drivers/media/dvb/ttpci/budget-patch.c
+++ b/drivers/media/dvb/ttpci/budget-patch.c
@@ -456,7 +456,7 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
// use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
// use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called
saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
- // set event counter 1 treshold to maximum allowed value (rEC p55)
+ // set event counter 1 threshold to maximum allowed value (rEC p55)
saa7146_write(dev, ECT1R, 0x3fff );
#endif
// Fix VSYNC level
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index d91e0638448..53baccbab17 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -1198,7 +1198,7 @@ static int ttusb_init_rc( struct ttusb_dec *dec)
int err;
usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys));
- strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys));
+ strlcat(dec->rc_phys, "/input0", sizeof(dec->rc_phys));
input_dev = input_allocate_device();
if (!input_dev)
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index a87a477c87f..4c2b8a24677 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -195,6 +195,25 @@ config RADIO_MAESTRO
To compile this driver as a module, choose M here: the
module will be called radio-maestro.
+config RADIO_MIROPCM20
+ tristate "miroSOUND PCM20 radio"
+ depends on ISA && VIDEO_V4L2 && SND
+ select SND_ISA
+ select SND_MIRO
+ ---help---
+ Choose Y here if you have this FM radio card. You also need to enable
+ the ALSA sound system. This choice automatically selects the ALSA
+ sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this
+ is required for the radio-miropcm20.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-miropcm20.
+
config RADIO_SF16FMI
tristate "SF16FMI Radio"
depends on ISA && VIDEO_V4L2
@@ -401,4 +420,16 @@ config RADIO_TEA5764_XTAL
Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N
here if TEA5764 reference frequency is connected in FREQIN.
+config RADIO_TEF6862
+ tristate "TEF6862 Car Radio Enhanced Selectivity Tuner"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to use the TEF6862 Car Radio Enhanced
+ Selectivity Tuner, found for instance on the Russellville development
+ board. On the russellville the device is connected to internal
+ timberdale I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called TEF6862.
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 2a1be3bf4f7..01922ada691 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -18,9 +18,11 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
+obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_RADIO_SI470X) += si470x/
obj-$(CONFIG_USB_MR800) += radio-mr800.o
obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
+obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c
new file mode 100644
index 00000000000..4ff885445fd
--- /dev/null
+++ b/drivers/media/radio/radio-miropcm20.c
@@ -0,0 +1,270 @@
+/* Miro PCM20 radio driver for Linux radio support
+ * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Thanks to Norberto Pellici for the ACI device interface specification
+ * The API part is based on the radiotrack driver by M. Kirkwood
+ * This driver relies on the aci mixer provided by the snd-miro
+ * ALSA driver.
+ * Look there for further info...
+ */
+
+/* What ever you think about the ACI, version 0x07 is not very well!
+ * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
+ * conditions... Robert
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <sound/aci.h>
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
+
+static int mono;
+module_param(mono, bool, 0);
+MODULE_PARM_DESC(mono, "Force tuner into mono mode.");
+
+struct pcm20 {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ unsigned long freq;
+ int muted;
+ struct snd_miro_aci *aci;
+};
+
+static struct pcm20 pcm20_card = {
+ .freq = 87*16000,
+ .muted = 1,
+};
+
+static int pcm20_mute(struct pcm20 *dev, unsigned char mute)
+{
+ dev->muted = mute;
+ return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1);
+}
+
+static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo)
+{
+ return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1);
+}
+
+static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
+{
+ unsigned char freql;
+ unsigned char freqh;
+ struct snd_miro_aci *aci = dev->aci;
+
+ dev->freq = freq;
+
+ freq /= 160;
+ if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))
+ freq /= 10; /* I don't know exactly which version
+ * needs this hack */
+ freql = freq & 0xff;
+ freqh = freq >> 8;
+
+ pcm20_stereo(dev, !mono);
+ return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
+}
+
+static const struct v4l2_file_operations pcm20_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = video_ioctl2,
+};
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ strlcpy(v->driver, "Miro PCM20", sizeof(v->driver));
+ strlcpy(v->card, "Miro PCM20", sizeof(v->card));
+ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = 0x1;
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index) /* Only 1 tuner */
+ return -EINVAL;
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = 87*16000;
+ v->rangehigh = 108*16000;
+ v->signal = 0xffff;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ return v->index ? -EINVAL : 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = dev->freq;
+ return 0;
+}
+
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ dev->freq = f->frequency;
+ pcm20_setfreq(dev, f->frequency);
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = dev->muted;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pcm20 *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ pcm20_mute(dev, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ a->index = 0;
+ strlcpy(a->name, "Radio", sizeof(a->name));
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ return a->index ? -EINVAL : 0;
+}
+
+static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+};
+
+static int __init pcm20_init(void)
+{
+ struct pcm20 *dev = &pcm20_card;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ int res;
+
+ dev->aci = snd_aci_get_aci();
+ if (dev->aci == NULL) {
+ v4l2_err(v4l2_dev,
+ "you must load the snd-miro driver first!\n");
+ return -ENODEV;
+ }
+ strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name));
+
+
+ res = v4l2_device_register(NULL, v4l2_dev);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "could not register v4l2_device\n");
+ return -EINVAL;
+ }
+
+ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
+ dev->vdev.v4l2_dev = v4l2_dev;
+ dev->vdev.fops = &pcm20_fops;
+ dev->vdev.ioctl_ops = &pcm20_ioctl_ops;
+ dev->vdev.release = video_device_release_empty;
+ video_set_drvdata(&dev->vdev, dev);
+
+ if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
+ goto fail;
+
+ v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");
+ return 0;
+fail:
+ v4l2_device_unregister(v4l2_dev);
+ return -EINVAL;
+}
+
+MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
+MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
+MODULE_LICENSE("GPL");
+
+static void __exit pcm20_cleanup(void)
+{
+ struct pcm20 *dev = &pcm20_card;
+
+ video_unregister_device(&dev->vdev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+}
+
+module_init(pcm20_init);
+module_exit(pcm20_cleanup);
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index a1239083472..949f60513d9 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -28,7 +28,7 @@
* http://av-usbradio.sourceforge.net/index.php
* http://sourceforge.net/projects/av-usbradio/
* Latest release of theirs project was in 2005.
- * Probably, this driver could be improved trough using their
+ * Probably, this driver could be improved through using their
* achievements (specifications given).
* Also, Faidon Liambotis <paravoid@debian.org> wrote nice driver for this radio
* in 2007. He allowed to use his driver to improve current mr800 radio driver.
@@ -85,6 +85,9 @@ MODULE_LICENSE("GPL");
#define amradio_dev_warn(dev, fmt, arg...) \
dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
+#define amradio_dev_err(dev, fmt, arg...) \
+ dev_err(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
+
/* Probably USB_TIMEOUT should be modified in module parameter */
#define BUFFER_LENGTH 8
#define USB_TIMEOUT 500
@@ -129,18 +132,20 @@ static int usb_amradio_resume(struct usb_interface *intf);
struct amradio_device {
/* reference to USB and video device */
struct usb_device *usbdev;
- struct video_device *videodev;
+ struct usb_interface *intf;
+ struct video_device videodev;
struct v4l2_device v4l2_dev;
unsigned char *buffer;
struct mutex lock; /* buffer locking */
int curfreq;
int stereo;
- int users;
- int removed;
int muted;
+ int initialized;
};
+#define vdev_to_amradio(r) container_of(r, struct amradio_device, videodev)
+
/* USB Device ID List */
static struct usb_device_id usb_amradio_device_table[] = {
{USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
@@ -159,7 +164,7 @@ static struct usb_driver usb_amradio_driver = {
.resume = usb_amradio_resume,
.reset_resume = usb_amradio_resume,
.id_table = usb_amradio_device_table,
- .supports_autosuspend = 0,
+ .supports_autosuspend = 1,
};
/* switch on/off the radio. Send 8 bytes to device */
@@ -168,11 +173,7 @@ static int amradio_set_mute(struct amradio_device *radio, char argument)
int retval;
int size;
- /* safety check */
- if (radio->removed)
- return -EIO;
-
- mutex_lock(&radio->lock);
+ BUG_ON(!mutex_is_locked(&radio->lock));
radio->buffer[0] = 0x00;
radio->buffer[1] = 0x55;
@@ -187,14 +188,12 @@ static int amradio_set_mute(struct amradio_device *radio, char argument)
(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
if (retval < 0 || size != BUFFER_LENGTH) {
- mutex_unlock(&radio->lock);
+ amradio_dev_warn(&radio->videodev.dev, "set mute failed\n");
return retval;
}
radio->muted = argument;
- mutex_unlock(&radio->lock);
-
return retval;
}
@@ -205,11 +204,7 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
int size;
unsigned short freq_send = 0x10 + (freq >> 3) / 25;
- /* safety check */
- if (radio->removed)
- return -EIO;
-
- mutex_lock(&radio->lock);
+ BUG_ON(!mutex_is_locked(&radio->lock));
radio->buffer[0] = 0x00;
radio->buffer[1] = 0x55;
@@ -223,10 +218,8 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
- if (retval < 0 || size != BUFFER_LENGTH) {
- mutex_unlock(&radio->lock);
- return retval;
- }
+ if (retval < 0 || size != BUFFER_LENGTH)
+ goto out_err;
/* frequency is calculated from freq_send and placed in first 2 bytes */
radio->buffer[0] = (freq_send >> 8) & 0xff;
@@ -240,13 +233,15 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
- if (retval < 0 || size != BUFFER_LENGTH) {
- mutex_unlock(&radio->lock);
- return retval;
- }
+ if (retval < 0 || size != BUFFER_LENGTH)
+ goto out_err;
- mutex_unlock(&radio->lock);
+ radio->curfreq = freq;
+ goto out;
+out_err:
+ amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n");
+out:
return retval;
}
@@ -255,11 +250,7 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument)
int retval;
int size;
- /* safety check */
- if (radio->removed)
- return -EIO;
-
- mutex_lock(&radio->lock);
+ BUG_ON(!mutex_is_locked(&radio->lock));
radio->buffer[0] = 0x00;
radio->buffer[1] = 0x55;
@@ -274,14 +265,14 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument)
(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
if (retval < 0 || size != BUFFER_LENGTH) {
- radio->stereo = -1;
- mutex_unlock(&radio->lock);
+ amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n");
return retval;
}
- radio->stereo = 1;
-
- mutex_unlock(&radio->lock);
+ if (argument == WANT_STEREO)
+ radio->stereo = 1;
+ else
+ radio->stereo = 0;
return retval;
}
@@ -296,19 +287,19 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
struct amradio_device *radio = usb_get_intfdata(intf);
mutex_lock(&radio->lock);
- radio->removed = 1;
+ radio->usbdev = NULL;
mutex_unlock(&radio->lock);
usb_set_intfdata(intf, NULL);
- video_unregister_device(radio->videodev);
v4l2_device_disconnect(&radio->v4l2_dev);
+ video_unregister_device(&radio->videodev);
}
/* vidioc_querycap - query device capabilities */
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
- struct amradio_device *radio = video_drvdata(file);
+ struct amradio_device *radio = file->private_data;
strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
@@ -322,13 +313,9 @@ static int vidioc_querycap(struct file *file, void *priv,
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+ struct amradio_device *radio = file->private_data;
int retval;
- /* safety check */
- if (radio->removed)
- return -EIO;
-
if (v->index > 0)
return -EINVAL;
@@ -341,9 +328,6 @@ static int vidioc_g_tuner(struct file *file, void *priv,
* amradio_set_stereo shouldn't be here
*/
retval = amradio_set_stereo(radio, WANT_STEREO);
- if (retval < 0)
- amradio_dev_warn(&radio->videodev->dev,
- "set stereo failed\n");
strcpy(v->name, "FM");
v->type = V4L2_TUNER_RADIO;
@@ -357,19 +341,16 @@ static int vidioc_g_tuner(struct file *file, void *priv,
v->audmode = V4L2_TUNER_MODE_MONO;
v->signal = 0xffff; /* Can't get the signal strength, sad.. */
v->afc = 0; /* Don't know what is this */
- return 0;
+
+ return retval;
}
/* vidioc_s_tuner - set tuner attributes */
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
-
- /* safety check */
- if (radio->removed)
- return -EIO;
+ struct amradio_device *radio = file->private_data;
+ int retval = -EINVAL;
if (v->index > 0)
return -EINVAL;
@@ -378,57 +359,33 @@ static int vidioc_s_tuner(struct file *file, void *priv,
switch (v->audmode) {
case V4L2_TUNER_MODE_MONO:
retval = amradio_set_stereo(radio, WANT_MONO);
- if (retval < 0)
- amradio_dev_warn(&radio->videodev->dev,
- "set mono failed\n");
break;
case V4L2_TUNER_MODE_STEREO:
retval = amradio_set_stereo(radio, WANT_STEREO);
- if (retval < 0)
- amradio_dev_warn(&radio->videodev->dev,
- "set stereo failed\n");
break;
- default:
- return -EINVAL;
}
- return 0;
+ return retval;
}
/* vidioc_s_frequency - set tuner radio frequency */
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
+ struct amradio_device *radio = file->private_data;
- /* safety check */
- if (radio->removed)
- return -EIO;
-
- mutex_lock(&radio->lock);
- radio->curfreq = f->frequency;
- mutex_unlock(&radio->lock);
-
- retval = amradio_setfreq(radio, radio->curfreq);
- if (retval < 0)
- amradio_dev_warn(&radio->videodev->dev,
- "set frequency failed\n");
- return 0;
+ return amradio_setfreq(radio, f->frequency);
}
/* vidioc_g_frequency - get tuner radio frequency */
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-
- /* safety check */
- if (radio->removed)
- return -EIO;
+ struct amradio_device *radio = file->private_data;
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
+
return 0;
}
@@ -448,17 +405,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-
- /* safety check */
- if (radio->removed)
- return -EIO;
+ struct amradio_device *radio = file->private_data;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
ctrl->value = radio->muted;
return 0;
}
+
return -EINVAL;
}
@@ -466,33 +420,20 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
-
- /* safety check */
- if (radio->removed)
- return -EIO;
+ struct amradio_device *radio = file->private_data;
+ int retval = -EINVAL;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value) {
+ if (ctrl->value)
retval = amradio_set_mute(radio, AMRADIO_STOP);
- if (retval < 0) {
- amradio_dev_warn(&radio->videodev->dev,
- "amradio_stop failed\n");
- return -1;
- }
- } else {
+ else
retval = amradio_set_mute(radio, AMRADIO_START);
- if (retval < 0) {
- amradio_dev_warn(&radio->videodev->dev,
- "amradio_start failed\n");
- return -1;
- }
- }
- return 0;
+
+ break;
}
- return -EINVAL;
+
+ return retval;
}
/* vidioc_g_audio - get audio attributes */
@@ -531,75 +472,108 @@ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
return 0;
}
-/* open device - amradio_start() and amradio_setfreq() */
-static int usb_amradio_open(struct file *file)
+static int usb_amradio_init(struct amradio_device *radio)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
int retval;
- lock_kernel();
+ retval = amradio_set_mute(radio, AMRADIO_STOP);
+ if (retval)
+ goto out_err;
- radio->users = 1;
- radio->muted = 1;
+ retval = amradio_set_stereo(radio, WANT_STEREO);
+ if (retval)
+ goto out_err;
- retval = amradio_set_mute(radio, AMRADIO_START);
- if (retval < 0) {
- amradio_dev_warn(&radio->videodev->dev,
- "radio did not start up properly\n");
- radio->users = 0;
- unlock_kernel();
- return -EIO;
+ radio->initialized = 1;
+ goto out;
+
+out_err:
+ amradio_dev_err(&radio->videodev.dev, "initialization failed\n");
+out:
+ return retval;
+}
+
+/* open device - amradio_start() and amradio_setfreq() */
+static int usb_amradio_open(struct file *file)
+{
+ struct amradio_device *radio = vdev_to_amradio(video_devdata(file));
+ int retval = 0;
+
+ mutex_lock(&radio->lock);
+
+ if (!radio->usbdev) {
+ retval = -EIO;
+ goto unlock;
}
- retval = amradio_set_stereo(radio, WANT_STEREO);
- if (retval < 0)
- amradio_dev_warn(&radio->videodev->dev,
- "set stereo failed\n");
+ file->private_data = radio;
+ retval = usb_autopm_get_interface(radio->intf);
+ if (retval)
+ goto unlock;
- retval = amradio_setfreq(radio, radio->curfreq);
- if (retval < 0)
- amradio_dev_warn(&radio->videodev->dev,
- "set frequency failed\n");
+ if (unlikely(!radio->initialized)) {
+ retval = usb_amradio_init(radio);
+ if (retval)
+ usb_autopm_put_interface(radio->intf);
+ }
- unlock_kernel();
- return 0;
+unlock:
+ mutex_unlock(&radio->lock);
+ return retval;
}
/*close device */
static int usb_amradio_close(struct file *file)
{
- struct amradio_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
-
- if (!radio)
- return -ENODEV;
+ struct amradio_device *radio = file->private_data;
+ int retval = 0;
mutex_lock(&radio->lock);
- radio->users = 0;
+
+ if (!radio->usbdev)
+ retval = -EIO;
+ else
+ usb_autopm_put_interface(radio->intf);
+
mutex_unlock(&radio->lock);
+ return retval;
+}
- if (!radio->removed) {
- retval = amradio_set_mute(radio, AMRADIO_STOP);
- if (retval < 0)
- amradio_dev_warn(&radio->videodev->dev,
- "amradio_stop failed\n");
+static long usb_amradio_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct amradio_device *radio = file->private_data;
+ long retval = 0;
+
+ mutex_lock(&radio->lock);
+
+ if (!radio->usbdev) {
+ retval = -EIO;
+ goto unlock;
}
- return 0;
+ retval = video_ioctl2(file, cmd, arg);
+
+unlock:
+ mutex_unlock(&radio->lock);
+ return retval;
}
/* Suspend device - stop device. Need to be checked and fixed */
static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
{
struct amradio_device *radio = usb_get_intfdata(intf);
- int retval;
- retval = amradio_set_mute(radio, AMRADIO_STOP);
- if (retval < 0)
- dev_warn(&intf->dev, "amradio_stop failed\n");
+ mutex_lock(&radio->lock);
+
+ if (!radio->muted && radio->initialized) {
+ amradio_set_mute(radio, AMRADIO_STOP);
+ radio->muted = 0;
+ }
dev_info(&intf->dev, "going into suspend..\n");
+ mutex_unlock(&radio->lock);
return 0;
}
@@ -607,14 +581,26 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
static int usb_amradio_resume(struct usb_interface *intf)
{
struct amradio_device *radio = usb_get_intfdata(intf);
- int retval;
- retval = amradio_set_mute(radio, AMRADIO_START);
- if (retval < 0)
- dev_warn(&intf->dev, "amradio_start failed\n");
+ mutex_lock(&radio->lock);
+
+ if (unlikely(!radio->initialized))
+ goto unlock;
+
+ if (radio->stereo)
+ amradio_set_stereo(radio, WANT_STEREO);
+ else
+ amradio_set_stereo(radio, WANT_MONO);
+
+ amradio_setfreq(radio, radio->curfreq);
+ if (!radio->muted)
+ amradio_set_mute(radio, AMRADIO_START);
+
+unlock:
dev_info(&intf->dev, "coming out of suspend..\n");
+ mutex_unlock(&radio->lock);
return 0;
}
@@ -623,7 +609,7 @@ static const struct v4l2_file_operations usb_amradio_fops = {
.owner = THIS_MODULE,
.open = usb_amradio_open,
.release = usb_amradio_close,
- .ioctl = video_ioctl2,
+ .ioctl = usb_amradio_ioctl,
};
static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
@@ -643,10 +629,7 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
static void usb_amradio_video_device_release(struct video_device *videodev)
{
- struct amradio_device *radio = video_get_drvdata(videodev);
-
- /* we call v4l to free radio->videodev */
- video_device_release(videodev);
+ struct amradio_device *radio = vdev_to_amradio(videodev);
v4l2_device_unregister(&radio->v4l2_dev);
@@ -660,70 +643,63 @@ static int usb_amradio_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct amradio_device *radio;
- struct v4l2_device *v4l2_dev;
- int retval;
+ int retval = 0;
radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL);
if (!radio) {
dev_err(&intf->dev, "kmalloc for amradio_device failed\n");
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto err;
}
radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
if (!radio->buffer) {
dev_err(&intf->dev, "kmalloc for radio->buffer failed\n");
- kfree(radio);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto err_nobuf;
}
- v4l2_dev = &radio->v4l2_dev;
- retval = v4l2_device_register(&intf->dev, v4l2_dev);
+ retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
if (retval < 0) {
dev_err(&intf->dev, "couldn't register v4l2_device\n");
- kfree(radio->buffer);
- kfree(radio);
- return retval;
+ goto err_v4l2;
}
- radio->videodev = video_device_alloc();
-
- if (!radio->videodev) {
- dev_err(&intf->dev, "video_device_alloc failed\n");
- kfree(radio->buffer);
- kfree(radio);
- return -ENOMEM;
- }
+ strlcpy(radio->videodev.name, radio->v4l2_dev.name,
+ sizeof(radio->videodev.name));
+ radio->videodev.v4l2_dev = &radio->v4l2_dev;
+ radio->videodev.fops = &usb_amradio_fops;
+ radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops;
+ radio->videodev.release = usb_amradio_video_device_release;
- strlcpy(radio->videodev->name, v4l2_dev->name, sizeof(radio->videodev->name));
- radio->videodev->v4l2_dev = v4l2_dev;
- radio->videodev->fops = &usb_amradio_fops;
- radio->videodev->ioctl_ops = &usb_amradio_ioctl_ops;
- radio->videodev->release = usb_amradio_video_device_release;
-
- radio->removed = 0;
- radio->users = 0;
radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
radio->curfreq = 95.16 * FREQ_MUL;
- radio->stereo = -1;
mutex_init(&radio->lock);
- video_set_drvdata(radio->videodev, radio);
+ video_set_drvdata(&radio->videodev, radio);
- retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
+ retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
+ radio_nr);
if (retval < 0) {
dev_err(&intf->dev, "could not register video device\n");
- video_device_release(radio->videodev);
- v4l2_device_unregister(v4l2_dev);
- kfree(radio->buffer);
- kfree(radio);
- return -EIO;
+ goto err_vdev;
}
usb_set_intfdata(intf, radio);
return 0;
+
+err_vdev:
+ v4l2_device_unregister(&radio->v4l2_dev);
+err_v4l2:
+ kfree(radio->buffer);
+err_nobuf:
+ kfree(radio);
+err:
+ return retval;
}
static int __init amradio_init(void)
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
new file mode 100644
index 00000000000..6e607ff0c16
--- /dev/null
+++ b/drivers/media/radio/tef6862.c
@@ -0,0 +1,232 @@
+/*
+ * tef6862.c Philips TEF6862 Car Radio Enhanced Selectivity Tuner
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#define DRIVER_NAME "tef6862"
+
+#define FREQ_MUL 16000
+
+#define TEF6862_LO_FREQ (875 * FREQ_MUL / 10)
+#define TEF6862_HI_FREQ (108 * FREQ_MUL)
+
+/* Write mode sub addresses */
+#define WM_SUB_BANDWIDTH 0x0
+#define WM_SUB_PLLM 0x1
+#define WM_SUB_PLLL 0x2
+#define WM_SUB_DAA 0x3
+#define WM_SUB_AGC 0x4
+#define WM_SUB_BAND 0x5
+#define WM_SUB_CONTROL 0x6
+#define WM_SUB_LEVEL 0x7
+#define WM_SUB_IFCF 0x8
+#define WM_SUB_IFCAP 0x9
+#define WM_SUB_ACD 0xA
+#define WM_SUB_TEST 0xF
+
+/* Different modes of the MSA register */
+#define MODE_BUFFER 0x0
+#define MODE_PRESET 0x1
+#define MODE_SEARCH 0x2
+#define MODE_AF_UPDATE 0x3
+#define MODE_JUMP 0x4
+#define MODE_CHECK 0x5
+#define MODE_LOAD 0x6
+#define MODE_END 0x7
+#define MODE_SHIFT 5
+
+struct tef6862_state {
+ struct v4l2_subdev sd;
+ unsigned long freq;
+};
+
+static inline struct tef6862_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tef6862_state, sd);
+}
+
+static u16 tef6862_sigstr(struct i2c_client *client)
+{
+ u8 buf[4];
+ int err = i2c_master_recv(client, buf, sizeof(buf));
+ if (err == sizeof(buf))
+ return buf[3] << 8;
+ return 0;
+}
+
+static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+
+ /* only support FM for now */
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = TEF6862_LO_FREQ;
+ v->rangehigh = TEF6862_HI_FREQ;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ v->signal = tef6862_sigstr(v4l2_get_subdevdata(sd));
+
+ return 0;
+}
+
+static int tef6862_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
+{
+ return v->index ? -EINVAL : 0;
+}
+
+static int tef6862_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tef6862_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 pll;
+ u8 i2cmsg[3];
+ int err;
+
+ if (f->tuner != 0)
+ return -EINVAL;
+
+ pll = 1964 + ((f->frequency - TEF6862_LO_FREQ) * 20) / FREQ_MUL;
+ i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM;
+ i2cmsg[1] = (pll >> 8) & 0xff;
+ i2cmsg[2] = pll & 0xff;
+
+ err = i2c_master_send(client, i2cmsg, sizeof(i2cmsg));
+ if (!err)
+ state->freq = f->frequency;
+ return err;
+}
+
+static int tef6862_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+ struct tef6862_state *state = to_state(sd);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = state->freq;
+ return 0;
+}
+
+static int tef6862_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEF6862, 0);
+}
+
+static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = {
+ .g_tuner = tef6862_g_tuner,
+ .s_tuner = tef6862_s_tuner,
+ .s_frequency = tef6862_s_frequency,
+ .g_frequency = tef6862_g_frequency,
+};
+
+static const struct v4l2_subdev_core_ops tef6862_core_ops = {
+ .g_chip_ident = tef6862_g_chip_ident,
+};
+
+static const struct v4l2_subdev_ops tef6862_ops = {
+ .core = &tef6862_core_ops,
+ .tuner = &tef6862_tuner_ops,
+};
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static int __devinit tef6862_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tef6862_state *state;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ state->freq = TEF6862_LO_FREQ;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &tef6862_ops);
+
+ return 0;
+}
+
+static int __devexit tef6862_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id tef6862_id[] = {
+ {DRIVER_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tef6862_id);
+
+static struct i2c_driver tef6862_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+ .probe = tef6862_probe,
+ .remove = tef6862_remove,
+ .id_table = tef6862_id,
+};
+
+static __init int tef6862_init(void)
+{
+ return i2c_add_driver(&tef6862_driver);
+}
+
+static __exit void tef6862_exit(void)
+{
+ i2c_del_driver(&tef6862_driver);
+}
+
+module_init(tef6862_init);
+module_exit(tef6862_exit);
+
+MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner");
+MODULE_AUTHOR("Mocean Laboratories");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index e6186b338a1..9dc74c93bf2 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -600,7 +600,7 @@ source "drivers/media/video/bt8xx/Kconfig"
config VIDEO_PMS
tristate "Mediavision Pro Movie Studio Video For Linux"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
help
Say Y if you have such a thing.
@@ -847,6 +847,12 @@ config SOC_CAMERA_MT9V022
help
This driver supports MT9V022 cameras from Micron
+config SOC_CAMERA_RJ54N1
+ tristate "rj54n1cb0c support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a rj54n1cb0c video driver
+
config SOC_CAMERA_TW9910
tristate "tw9910 support"
depends on SOC_CAMERA && I2C
@@ -865,6 +871,12 @@ config SOC_CAMERA_OV772X
help
This is a ov772x camera driver
+config SOC_CAMERA_OV9640
+ tristate "ov9640 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov9640 camera driver
+
config MX1_VIDEO
bool
@@ -939,9 +951,14 @@ source "drivers/media/video/usbvideo/Kconfig"
source "drivers/media/video/et61x251/Kconfig"
config VIDEO_OVCAMCHIP
- tristate "OmniVision Camera Chip support"
+ tristate "OmniVision Camera Chip support (DEPRECATED)"
depends on I2C && VIDEO_V4L1
---help---
+ This driver is DEPRECATED please use the gspca ov519 module
+ instead. Note that for the ov511 / ov518 support of the gspca module
+ you need atleast version 0.6.0 of libv4l and for the w9968cf
+ atleast version 0.6.3 of libv4l.
+
Support for the OmniVision OV6xxx and OV7xxx series of camera chips.
This driver is intended to be used with the ov511 and w9968cf USB
camera drivers.
@@ -950,9 +967,13 @@ config VIDEO_OVCAMCHIP
module will be called ovcamchip.
config USB_W9968CF
- tristate "USB W996[87]CF JPEG Dual Mode Camera support"
+ tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)"
depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP
---help---
+ This driver is DEPRECATED please use the gspca ov519 module
+ instead. Note that for the w9968cf support of the gspca module
+ you need atleast version 0.6.3 of libv4l.
+
Say Y here if you want support for cameras based on OV681 or
Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
@@ -995,9 +1016,13 @@ config USB_SE401
source "drivers/media/video/sn9c102/Kconfig"
config USB_STV680
- tristate "USB STV680 (Pencam) Camera support"
+ tristate "USB STV680 (Pencam) Camera support (DEPRECATED)"
depends on VIDEO_V4L1
---help---
+ This driver is DEPRECATED please use the gspca stv0680 module
+ instead. Note that for the gspca stv0680 module you need
+ atleast version 0.6.3 of libv4l.
+
Say Y here if you want to connect this type of camera to your
computer's USB port. This includes the Pencam line of cameras.
See <file:Documentation/video4linux/stv680.txt> for more information
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index e541932a789..7a2dcc34111 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -77,6 +77,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
+obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
+obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o
obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o
# And now the v4l2 drivers:
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index 1b3cbd02a7f..0826f0dabc1 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -27,17 +27,40 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <linux/mutex.h>
#define DRIVER_NAME "adv7180"
-#define ADV7180_INPUT_CONTROL_REG 0x00
-#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM 0x00
-#define ADV7180_AUTODETECT_ENABLE_REG 0x07
-#define ADV7180_AUTODETECT_DEFAULT 0x7f
-
-
-#define ADV7180_STATUS1_REG 0x10
-#define ADV7180_STATUS1_AUTOD_MASK 0x70
+#define ADV7180_INPUT_CONTROL_REG 0x00
+#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
+#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
+#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20
+#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30
+#define ADV7180_INPUT_CONTROL_NTSC_J 0x40
+#define ADV7180_INPUT_CONTROL_NTSC_M 0x50
+#define ADV7180_INPUT_CONTROL_PAL60 0x60
+#define ADV7180_INPUT_CONTROL_NTSC_443 0x70
+#define ADV7180_INPUT_CONTROL_PAL_BG 0x80
+#define ADV7180_INPUT_CONTROL_PAL_N 0x90
+#define ADV7180_INPUT_CONTROL_PAL_M 0xa0
+#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0
+#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0
+#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0
+#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0
+#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0
+
+#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04
+#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
+
+#define ADV7180_AUTODETECT_ENABLE_REG 0x07
+#define ADV7180_AUTODETECT_DEFAULT 0x7f
+
+#define ADV7180_ADI_CTRL_REG 0x0e
+#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
+
+#define ADV7180_STATUS1_REG 0x10
+#define ADV7180_STATUS1_IN_LOCK 0x01
+#define ADV7180_STATUS1_AUTOD_MASK 0x70
#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
#define ADV7180_STATUS1_AUTOD_PAL_M 0x20
@@ -50,18 +73,37 @@
#define ADV7180_IDENT_REG 0x11
#define ADV7180_ID_7180 0x18
+#define ADV7180_ICONF1_ADI 0x40
+#define ADV7180_ICONF1_ACTIVE_LOW 0x01
+#define ADV7180_ICONF1_PSYNC_ONLY 0x10
+#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
+
+#define ADV7180_IRQ1_LOCK 0x01
+#define ADV7180_IRQ1_UNLOCK 0x02
+#define ADV7180_ISR1_ADI 0x42
+#define ADV7180_ICR1_ADI 0x43
+#define ADV7180_IMR1_ADI 0x44
+#define ADV7180_IMR2_ADI 0x48
+#define ADV7180_IRQ3_AD_CHANGE 0x08
+#define ADV7180_ISR3_ADI 0x4A
+#define ADV7180_ICR3_ADI 0x4B
+#define ADV7180_IMR3_ADI 0x4C
+#define ADV7180_IMR4_ADI 0x50
struct adv7180_state {
- struct v4l2_subdev sd;
+ struct v4l2_subdev sd;
+ struct work_struct work;
+ struct mutex mutex; /* mutual excl. when accessing chip */
+ int irq;
+ v4l2_std_id curr_norm;
+ bool autodetect;
};
-static v4l2_std_id determine_norm(struct i2c_client *client)
+static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
{
- u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
-
switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
case ADV7180_STATUS1_AUTOD_NTSM_M_J:
- return V4L2_STD_NTSC_M_JP;
+ return V4L2_STD_NTSC;
case ADV7180_STATUS1_AUTOD_NTSC_4_43:
return V4L2_STD_NTSC_443;
case ADV7180_STATUS1_AUTOD_PAL_M:
@@ -81,6 +123,53 @@ static v4l2_std_id determine_norm(struct i2c_client *client)
}
}
+static int v4l2_std_to_adv7180(v4l2_std_id std)
+{
+ if (std == V4L2_STD_PAL_60)
+ return ADV7180_INPUT_CONTROL_PAL60;
+ if (std == V4L2_STD_NTSC_443)
+ return ADV7180_INPUT_CONTROL_NTSC_443;
+ if (std == V4L2_STD_PAL_N)
+ return ADV7180_INPUT_CONTROL_PAL_N;
+ if (std == V4L2_STD_PAL_M)
+ return ADV7180_INPUT_CONTROL_PAL_M;
+ if (std == V4L2_STD_PAL_Nc)
+ return ADV7180_INPUT_CONTROL_PAL_COMB_N;
+
+ if (std & V4L2_STD_PAL)
+ return ADV7180_INPUT_CONTROL_PAL_BG;
+ if (std & V4L2_STD_NTSC)
+ return ADV7180_INPUT_CONTROL_NTSC_M;
+ if (std & V4L2_STD_SECAM)
+ return ADV7180_INPUT_CONTROL_PAL_SECAM;
+
+ return -EINVAL;
+}
+
+static u32 adv7180_status_to_v4l2(u8 status1)
+{
+ if (!(status1 & ADV7180_STATUS1_IN_LOCK))
+ return V4L2_IN_ST_NO_SIGNAL;
+
+ return 0;
+}
+
+static int __adv7180_status(struct i2c_client *client, u32 *status,
+ v4l2_std_id *std)
+{
+ int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
+
+ if (status1 < 0)
+ return status1;
+
+ if (status)
+ *status = adv7180_status_to_v4l2(status1);
+ if (std)
+ *std = adv7180_std_to_v4l2(status1);
+
+ return 0;
+}
+
static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct adv7180_state, sd);
@@ -88,10 +177,31 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv7180_state *state = to_state(sd);
+ int err = mutex_lock_interruptible(&state->mutex);
+ if (err)
+ return err;
+
+ /* when we are interrupt driven we know the state */
+ if (!state->autodetect || state->irq > 0)
+ *std = state->curr_norm;
+ else
+ err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
+
+ mutex_unlock(&state->mutex);
+ return err;
+}
- *std = determine_norm(client);
- return 0;
+static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct adv7180_state *state = to_state(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
+ mutex_unlock(&state->mutex);
+ return ret;
}
static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
@@ -102,12 +212,51 @@ static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0);
}
+static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7180_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ /* all standards -> autodetect */
+ if (std == V4L2_STD_ALL) {
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_INPUT_CONTROL_REG,
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+ if (ret < 0)
+ goto out;
+
+ __adv7180_status(client, NULL, &state->curr_norm);
+ state->autodetect = true;
+ } else {
+ ret = v4l2_std_to_adv7180(std);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_INPUT_CONTROL_REG, ret);
+ if (ret < 0)
+ goto out;
+
+ state->curr_norm = std;
+ state->autodetect = false;
+ }
+ ret = 0;
+out:
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
+ .g_input_status = adv7180_g_input_status,
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.g_chip_ident = adv7180_g_chip_ident,
+ .s_std = adv7180_s_std,
};
static const struct v4l2_subdev_ops adv7180_ops = {
@@ -115,12 +264,45 @@ static const struct v4l2_subdev_ops adv7180_ops = {
.video = &adv7180_video_ops,
};
+static void adv7180_work(struct work_struct *work)
+{
+ struct adv7180_state *state = container_of(work, struct adv7180_state,
+ work);
+ struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+ u8 isr3;
+
+ mutex_lock(&state->mutex);
+ i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+ ADV7180_ADI_CTRL_IRQ_SPACE);
+ isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
+ /* clear */
+ i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
+ i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
+
+ if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
+ __adv7180_status(client, NULL, &state->curr_norm);
+ mutex_unlock(&state->mutex);
+
+ enable_irq(state->irq);
+}
+
+static irqreturn_t adv7180_irq(int irq, void *devid)
+{
+ struct adv7180_state *state = devid;
+
+ schedule_work(&state->work);
+
+ disable_irq_nosync(state->irq);
+
+ return IRQ_HANDLED;
+}
+
/*
* Generic i2c probe
* concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
*/
-static int adv7180_probe(struct i2c_client *client,
+static __devinit int adv7180_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7180_state *state;
@@ -135,32 +317,111 @@ static int adv7180_probe(struct i2c_client *client,
client->addr << 1, client->adapter->name);
state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
- if (state == NULL)
- return -ENOMEM;
+ if (state == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ state->irq = client->irq;
+ INIT_WORK(&state->work, adv7180_work);
+ mutex_init(&state->mutex);
+ state->autodetect = true;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
/* Initialize adv7180 */
- /* enable autodetection */
+ /* Enable autodetection */
ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
- ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM);
- if (ret > 0)
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_AUTODETECT_ENABLE_REG,
- ADV7180_AUTODETECT_DEFAULT);
- if (ret < 0) {
- printk(KERN_ERR DRIVER_NAME
- ": Failed to communicate to chip: %d\n", ret);
- return ret;
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG,
+ ADV7180_AUTODETECT_DEFAULT);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* ITU-R BT.656-4 compatible */
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
+ ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* read current norm */
+ __adv7180_status(client, NULL, &state->curr_norm);
+
+ /* register for interrupts */
+ if (state->irq > 0) {
+ ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
+ state);
+ if (ret)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+ ADV7180_ADI_CTRL_IRQ_SPACE);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* config the Interrupt pin to be active low */
+ ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
+ ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ /* enable AD change interrupts interrupts */
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
+ ADV7180_IRQ3_AD_CHANGE);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+ 0);
+ if (ret < 0)
+ goto err_unreg_subdev;
}
return 0;
+
+err_unreg_subdev:
+ mutex_destroy(&state->mutex);
+ v4l2_device_unregister_subdev(sd);
+ kfree(state);
+err:
+ printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
+ return ret;
}
-static int adv7180_remove(struct i2c_client *client)
+static __devexit int adv7180_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7180_state *state = to_state(sd);
+
+ if (state->irq > 0) {
+ free_irq(client->irq, state);
+ if (cancel_work_sync(&state->work)) {
+ /*
+ * Work was pending, therefore we need to enable
+ * IRQ here to balance the disable_irq() done in the
+ * interrupt handler.
+ */
+ enable_irq(state->irq);
+ }
+ }
+ mutex_destroy(&state->mutex);
v4l2_device_unregister_subdev(sd);
kfree(to_state(sd));
return 0;
@@ -179,7 +440,7 @@ static struct i2c_driver adv7180_driver = {
.name = DRIVER_NAME,
},
.probe = adv7180_probe,
- .remove = adv7180_remove,
+ .remove = __devexit_p(adv7180_remove),
.id_table = adv7180_id,
};
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
index 51527d7b55a..1485aee18d5 100644
--- a/drivers/media/video/au0828/au0828-video.c
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -830,7 +830,7 @@ static int au0828_v4l2_close(struct file *filp)
au0828_uninit_isoc(dev);
/* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
/* When close the device, set the usb intf0 into alt0 to free
USB bandwidth */
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
index f9330e3529c..5bb0f9e7158 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/video/bt819.c
@@ -299,7 +299,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "set input %x\n", input);
- if (input < 0 || input > 7)
+ if (input > 7)
return -EINVAL;
if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index ebd51afe876..84a957e52c4 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -73,12 +73,12 @@ static void ir_handle_key(struct bttv *btv)
if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
- ir_input_keydown(ir->dev,&ir->ir,data,data);
+ ir_input_keydown(ir->dev, &ir->ir, data);
} else {
/* HACK: Probably, ir->mask_keydown is missing
for this board */
if (btv->c.type == BTTV_BOARD_WINFAST2000)
- ir_input_keydown(ir->dev, &ir->ir, data, data);
+ ir_input_keydown(ir->dev, &ir->ir, data);
ir_input_nokey(ir->dev,&ir->ir);
}
@@ -104,7 +104,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
gpio, data,
(gpio & ir->mask_keyup) ? " up" : "up/down");
- ir_input_keydown(ir->dev, &ir->ir, data, data);
+ ir_input_keydown(ir->dev, &ir->ir, data);
if (keyup)
ir_input_nokey(ir->dev, &ir->ir);
} else {
@@ -118,7 +118,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
if (keyup)
ir_input_nokey(ir->dev, &ir->ir);
else
- ir_input_keydown(ir->dev, &ir->ir, data, data);
+ ir_input_keydown(ir->dev, &ir->ir, data);
}
ir->last_gpio = data | keyup;
@@ -368,7 +368,10 @@ int bttv_input_init(struct bttv *btv)
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
pci_name(btv->c.pci));
- ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ if (err < 0)
+ goto err_out_free;
+
input_dev->name = ir->name;
input_dev->phys = ir->phys;
input_dev->id.bustype = BUS_PCI;
@@ -400,6 +403,7 @@ int bttv_input_init(struct bttv *btv)
bttv_ir_stop(btv);
btv->remote = NULL;
err_out_free:
+ ir_input_free(input_dev);
input_free_device(input_dev);
kfree(ir);
return err;
@@ -411,6 +415,7 @@ void bttv_input_fini(struct bttv *btv)
return;
bttv_ir_stop(btv);
+ ir_input_free(btv->remote->dev);
input_unregister_device(btv->remote->dev);
kfree(btv->remote);
btv->remote = NULL;
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 536dedb23ba..4392c76af5d 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -99,10 +99,8 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
or_value);
}
-static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
+static void cx18_av_init(struct cx18 *cx)
{
- struct cx18 *cx = v4l2_get_subdevdata(sd);
-
/*
* The crystal freq used in calculations in this driver will be
* 28.636360 MHz.
@@ -125,7 +123,6 @@ static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
- return 0;
}
static void cx18_av_initialize(struct v4l2_subdev *sd)
@@ -198,7 +195,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd)
cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000);
/* Setup the Video and and Aux/Audio PLLs */
- cx18_av_init(sd, 0);
+ cx18_av_init(cx);
/* set video to auto-detect */
/* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */
@@ -1355,7 +1352,6 @@ static int cx18_av_s_register(struct v4l2_subdev *sd,
static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
.g_chip_ident = cx18_av_g_chip_ident,
.log_status = cx18_av_log_status,
- .init = cx18_av_init,
.load_fw = cx18_av_load_fw,
.reset = cx18_av_reset,
.queryctrl = cx18_av_queryctrl,
@@ -1399,6 +1395,7 @@ int cx18_av_probe(struct cx18 *cx)
{
struct cx18_av_state *state = &cx->av_state;
struct v4l2_subdev *sd;
+ int err;
state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
@@ -1417,5 +1414,8 @@ int cx18_av_probe(struct cx18 *cx)
snprintf(sd->name, sizeof(sd->name),
"%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
sd->grp_id = CX18_HW_418_AV;
- return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+ err = v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+ if (!err)
+ cx18_av_init(cx);
+ return err;
}
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index 9b84a0c58e0..cafb7e99b9a 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -294,7 +294,7 @@ struct cx18_av_state {
#define CXADEC_QAM_CONST_DEC 0x924
#define CXADEC_QAM_ROTATOR_FREQ 0x948
-/* Bit defintions / settings used in Mako Audio */
+/* Bit definitions / settings used in Mako Audio */
#define CXADEC_PREF_MODE_MONO_LANGA 0
#define CXADEC_PREF_MODE_MONO_LANGB 1
#define CXADEC_PREF_MODE_MONO_LANGC 2
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index 444e3c7c563..af3d71607dc 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -34,6 +34,9 @@
#define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \
CX18_HW_Z8F0811_IR_TX_HAUP)
+#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \
+ CX18_HW_Z8F0811_IR_TX_HAUP)
+
/* video inputs */
#define CX18_CARD_INPUT_VID_TUNER 1
#define CX18_CARD_INPUT_SVIDEO1 2
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 6dd51e27582..7f65a47f12e 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -87,7 +87,6 @@ static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
-/* VBI bufsize based on standards supported by card tuner for now */
static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
static int enc_ts_bufs = -1;
@@ -128,7 +127,6 @@ module_param(enc_ts_bufsize, int, 0644);
module_param(enc_mpg_bufsize, int, 0644);
module_param(enc_idx_bufsize, int, 0644);
module_param(enc_yuv_bufsize, int, 0644);
-/* VBI bufsize based on standards supported by card tuner for now */
module_param(enc_pcm_bufsize, int, 0644);
module_param(enc_ts_bufs, int, 0644);
@@ -211,7 +209,9 @@ MODULE_PARM_DESC(enc_yuv_buffers,
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
MODULE_PARM_DESC(enc_yuv_bufsize,
"Size of an encoder YUV buffer (kB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE));
+ "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
+ "\t\t\t(multiples of size required for 32 screen lines)\n"
+ "\t\t\tDefault: 102");
MODULE_PARM_DESC(enc_yuv_bufs,
"Number of encoder YUV buffers\n"
"\t\t\tDefault is computed from other enc_yuv_* parameters");
@@ -220,7 +220,7 @@ MODULE_PARM_DESC(enc_vbi_buffers,
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
MODULE_PARM_DESC(enc_vbi_bufs,
"Number of encoder VBI buffers\n"
- "\t\t\tDefault is computed from enc_vbi_buffers & tuner std");
+ "\t\t\tDefault is computed from enc_vbi_buffers");
MODULE_PARM_DESC(enc_pcm_buffers,
"Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
@@ -499,10 +499,27 @@ static void cx18_process_options(struct cx18 *cx)
continue;
}
/*
+ * YUV is a special case where the stream_buf_size needs to be
+ * an integral multiple of 33.75 kB (storage for 32 screens
+ * lines to maintain alignment in case of lost buffers
+ */
+ if (i == CX18_ENC_STREAM_TYPE_YUV) {
+ cx->stream_buf_size[i] *= 1024;
+ cx->stream_buf_size[i] -=
+ (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
+
+ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
+ cx->stream_buf_size[i] =
+ CX18_UNIT_ENC_YUV_BUFSIZE;
+ }
+ /*
+ * YUV is a special case where the stream_buf_size is
+ * now in bytes.
* VBI is a special case where the stream_buf_size is fixed
* and already in bytes
*/
- if (i == CX18_ENC_STREAM_TYPE_VBI) {
+ if (i == CX18_ENC_STREAM_TYPE_VBI ||
+ i == CX18_ENC_STREAM_TYPE_YUV) {
if (cx->stream_buffers[i] < 0) {
cx->stream_buffers[i] =
cx->options.megabytes[i] * 1024 * 1024
@@ -513,18 +530,24 @@ static void cx18_process_options(struct cx18 *cx)
cx->stream_buffers[i]
* cx->stream_buf_size[i]/(1024 * 1024);
}
- continue;
- }
- /* All other streams have stream_buf_size in kB at this point */
- if (cx->stream_buffers[i] < 0) {
- cx->stream_buffers[i] = cx->options.megabytes[i] * 1024
- / cx->stream_buf_size[i];
} else {
- /* N.B. This might round down to 0 */
- cx->options.megabytes[i] =
- cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024;
+ /* All other streams have stream_buf_size in kB here */
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] =
+ cx->options.megabytes[i] * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i]
+ * cx->stream_buf_size[i] / 1024;
+ }
+ /* convert from kB to bytes */
+ cx->stream_buf_size[i] *= 1024;
}
- cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */
+ CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
+ "%d bytes\n", i, cx->options.megabytes[i],
+ cx->stream_buffers[i], cx->stream_buf_size[i]);
}
cx->options.cardtype = cardtype[cx->instance];
@@ -669,6 +692,12 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+ /* IVTV style VBI insertion into MPEG streams */
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
+ list_add(&cx->vbi.sliced_mpeg_buf.list,
+ &cx->vbi.sliced_mpeg_mdl.buf_list);
return 0;
}
@@ -883,7 +912,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
CX18_ERR("Could not register A/V decoder subdevice\n");
goto free_map;
}
- cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0);
/* Initialize GPIO Reset Controller to do chip resets during i2c init */
if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
@@ -1200,7 +1228,7 @@ static struct pci_driver cx18_pci_driver = {
.remove = cx18_remove,
};
-static int module_start(void)
+static int __init module_start(void)
{
printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION);
@@ -1224,7 +1252,7 @@ static int module_start(void)
return 0;
}
-static void module_cleanup(void)
+static void __exit module_cleanup(void)
{
pci_unregister_driver(&cx18_pci_driver);
}
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index c6a1e907f63..e3f7911a738 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -50,6 +50,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include <media/tuner.h>
+#include <media/ir-kbd-i2c.h>
#include "cx18-mailbox.h"
#include "cx18-av-core.h"
#include "cx23418.h"
@@ -120,12 +121,16 @@
/* Maximum firmware DMA buffers per stream */
#define CX18_MAX_FW_MDLS_PER_STREAM 63
+/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */
+#define CX18_UNIT_ENC_YUV_BUFSIZE (720 * 32 * 3 / 2) /* bytes */
+#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32)
+#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32)
+
/* DMA buffer, default size in kB allocated */
#define CX18_DEFAULT_ENC_TS_BUFSIZE 32
#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32
#define CX18_DEFAULT_ENC_IDX_BUFSIZE 32
-#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128
-/* Default VBI bufsize based on standards supported by card tuner for now */
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1)
#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
/* i2c stuff */
@@ -246,8 +251,8 @@ struct cx18_options {
int radio; /* enable/disable radio */
};
-/* per-buffer bit flags */
-#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */
+/* per-mdl bit flags */
+#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */
/* per-stream, s_flags */
#define CX18_F_S_CLAIMED 3 /* this stream is claimed */
@@ -274,18 +279,29 @@ struct cx18_options {
struct cx18_buffer {
struct list_head list;
dma_addr_t dma_handle;
- u32 id;
- unsigned long b_flags;
- unsigned skipped;
char *buf;
u32 bytesused;
u32 readpos;
};
+struct cx18_mdl {
+ struct list_head list;
+ u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */
+
+ unsigned int skipped;
+ unsigned long m_flags;
+
+ struct list_head buf_list;
+ struct cx18_buffer *curr_buf; /* current buffer in list for reading */
+
+ u32 bytesused;
+ u32 readpos;
+};
+
struct cx18_queue {
struct list_head list;
- atomic_t buffers;
+ atomic_t depth;
u32 bytesused;
spinlock_t lock;
};
@@ -337,7 +353,7 @@ struct cx18_stream {
const char *name; /* name of the stream */
int type; /* stream type */
u32 handle; /* task handle */
- unsigned mdl_offset;
+ unsigned int mdl_base_idx;
u32 id;
unsigned long s_flags; /* status flags, see above */
@@ -346,14 +362,20 @@ struct cx18_stream {
PCI_DMA_NONE */
wait_queue_head_t waitq;
- /* Buffer Stats */
- u32 buffers;
- u32 buf_size;
+ /* Buffers */
+ struct list_head buf_pool; /* buffers not attached to an MDL */
+ u32 buffers; /* total buffers owned by this stream */
+ u32 buf_size; /* size in bytes of a single buffer */
+
+ /* MDL sizes - all stream MDLs are the same size */
+ u32 bufs_per_mdl;
+ u32 mdl_size; /* total bytes in all buffers in a mdl */
- /* Buffer Queues */
- struct cx18_queue q_free; /* free buffers */
- struct cx18_queue q_busy; /* busy buffers - in use by firmware */
- struct cx18_queue q_full; /* full buffers - data for user apps */
+ /* MDL Queues */
+ struct cx18_queue q_free; /* free - in rotation, not committed */
+ struct cx18_queue q_busy; /* busy - in use by firmware */
+ struct cx18_queue q_full; /* full - data for user apps */
+ struct cx18_queue q_idle; /* idle - not in rotation */
struct work_struct out_work_order;
@@ -481,10 +503,11 @@ struct vbi_info {
u32 inserted_frame;
/*
- * A dummy driver stream transfer buffer with a copy of the next
+ * A dummy driver stream transfer mdl & buffer with a copy of the next
* sliced_mpeg_data[] buffer for output to userland apps.
* Only used in cx18-fileops.c, but its state needs to persist at times.
*/
+ struct cx18_mdl sliced_mpeg_mdl;
struct cx18_buffer sliced_mpeg_buf;
};
@@ -511,10 +534,9 @@ struct cx18 {
u8 is_60hz;
u8 nof_inputs; /* number of video inputs */
u8 nof_audio_inputs; /* number of audio inputs */
- u16 buffer_id; /* buffer ID counter */
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* Hardware description of the board */
- unsigned mdl_offset;
+ unsigned int free_mdl_idx;
struct cx18_scb __iomem *scb; /* pointer to SCB */
struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
@@ -585,6 +607,8 @@ struct cx18 {
struct i2c_algo_bit_data i2c_algo[2];
struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
+ struct IR_i2c_init_data ir_i2c_init_data;
+
/* gpio */
u32 gpio_dir;
u32 gpio_val;
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 51a0c33b25b..71ad2d1b4c2 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -61,6 +61,7 @@ static struct mxl5005s_config hauppauge_hvr1600_tuner = {
.top = MXL5005S_TOP_25P2,
.mod_mode = MXL_DIGITAL_MODE,
.if_mode = MXL_ZERO_IF,
+ .qam_gain = 0x02,
.AgcMasterByte = 0x00,
};
@@ -71,7 +72,8 @@ static struct s5h1409_config hauppauge_hvr1600_config = {
.qam_if = 44000,
.inversion = S5H1409_INVERSION_OFF,
.status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE
};
/*
@@ -360,9 +362,10 @@ int cx18_dvb_register(struct cx18_stream *stream)
dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
CX18_INFO("DVB Frontend registered\n");
- CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n",
+ CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n",
stream->dvb.dvb_adapter.num, stream->name,
- stream->buffers, stream->buf_size/1024);
+ stream->buffers, stream->buf_size/1024,
+ (stream->buf_size * 100 / 1024) % 100);
mutex_init(&dvb->feedlock);
dvb->enabled = 1;
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 04d9c2508b8..4e278db31cc 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -166,11 +166,12 @@ static void cx18_dualwatch(struct cx18 *cx)
}
-static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err)
+static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block,
+ int *err)
{
struct cx18 *cx = s->cx;
struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
- struct cx18_buffer *buf;
+ struct cx18_mdl *mdl;
DEFINE_WAIT(wait);
*err = 0;
@@ -185,32 +186,33 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
}
if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
- while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
+ while ((mdl = cx18_dequeue(s_vbi,
+ &s_vbi->q_full))) {
/* byteswap and process VBI data */
- cx18_process_vbi_data(cx, buf,
+ cx18_process_vbi_data(cx, mdl,
s_vbi->type);
- cx18_stream_put_buf_fw(s_vbi, buf);
+ cx18_stream_put_mdl_fw(s_vbi, mdl);
}
}
- buf = &cx->vbi.sliced_mpeg_buf;
- if (buf->readpos != buf->bytesused)
- return buf;
+ mdl = &cx->vbi.sliced_mpeg_mdl;
+ if (mdl->readpos != mdl->bytesused)
+ return mdl;
}
/* do we have new data? */
- buf = cx18_dequeue(s, &s->q_full);
- if (buf) {
- if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP,
- &buf->b_flags))
- return buf;
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl) {
+ if (!test_and_clear_bit(CX18_F_M_NEED_SWAP,
+ &mdl->m_flags))
+ return mdl;
if (s->type == CX18_ENC_STREAM_TYPE_MPG)
/* byteswap MPG data */
- cx18_buf_swap(buf);
+ cx18_mdl_swap(mdl);
else {
/* byteswap and process VBI data */
- cx18_process_vbi_data(cx, buf, s->type);
+ cx18_process_vbi_data(cx, mdl, s->type);
}
- return buf;
+ return mdl;
}
/* return if end of stream */
@@ -229,7 +231,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
/* New buffers might have become available before we were added
to the waitqueue */
- if (!atomic_read(&s->q_full.buffers))
+ if (!atomic_read(&s->q_full.depth))
schedule();
finish_wait(&s->waitq, &wait);
if (signal_pending(current)) {
@@ -241,21 +243,28 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
}
}
-static void cx18_setup_sliced_vbi_buf(struct cx18 *cx)
+static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx)
{
+ struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl;
+ struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf;
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
- cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx];
- cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx];
- cx->vbi.sliced_mpeg_buf.readpos = 0;
+ buf->buf = cx->vbi.sliced_mpeg_data[idx];
+ buf->bytesused = cx->vbi.sliced_mpeg_size[idx];
+ buf->readpos = 0;
+
+ mdl->curr_buf = NULL;
+ mdl->bytesused = cx->vbi.sliced_mpeg_size[idx];
+ mdl->readpos = 0;
}
static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
- struct cx18_buffer *buf, char __user *ubuf, size_t ucount)
+ struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop)
{
struct cx18 *cx = s->cx;
size_t len = buf->bytesused - buf->readpos;
+ *stop = false;
if (len > ucount)
len = ucount;
if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
@@ -335,7 +344,8 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
/* We declare we actually found a Program Pack*/
cx->search_pack_header = 0; /* expect vid PES */
len = (char *)q - start;
- cx18_setup_sliced_vbi_buf(cx);
+ cx18_setup_sliced_vbi_mdl(cx);
+ *stop = true;
break;
}
}
@@ -352,6 +362,60 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
return len;
}
+/**
+ * list_entry_is_past_end - check if a previous loop cursor is off list end
+ * @pos: the type * previously used as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Check if the entry's list_head is the head of the list, thus it's not a
+ * real entry but was the loop cursor that walked past the end
+ */
+#define list_entry_is_past_end(pos, head, member) \
+ (&pos->member == (head))
+
+static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
+ struct cx18_mdl *mdl, char __user *ubuf, size_t ucount)
+{
+ size_t tot_written = 0;
+ int rc;
+ bool stop = false;
+
+ if (mdl->curr_buf == NULL)
+ mdl->curr_buf = list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list);
+
+ if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ /*
+ * For some reason we've exhausted the buffers, but the MDL
+ * object still said some data was unread.
+ * Fix that and bail out.
+ */
+ mdl->readpos = mdl->bytesused;
+ return 0;
+ }
+
+ list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+ if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+ continue;
+
+ rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written,
+ ucount - tot_written, &stop);
+ if (rc < 0)
+ return rc;
+ mdl->readpos += rc;
+ tot_written += rc;
+
+ if (stop || /* Forced stopping point for VBI insertion */
+ tot_written >= ucount || /* Reader request statisfied */
+ mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+ mdl->readpos >= mdl->bytesused) /* MDL buffers drained */
+ break;
+ }
+ return tot_written;
+}
+
static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
size_t tot_count, int non_block)
{
@@ -373,12 +437,12 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
single_frame = 1;
for (;;) {
- struct cx18_buffer *buf;
+ struct cx18_mdl *mdl;
int rc;
- buf = cx18_get_buffer(s, non_block, &rc);
+ mdl = cx18_get_mdl(s, non_block, &rc);
/* if there is no data available... */
- if (buf == NULL) {
+ if (mdl == NULL) {
/* if we got data, then return that regardless */
if (tot_written)
break;
@@ -392,20 +456,20 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
return rc;
}
- rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written,
+ rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written,
tot_count - tot_written);
- if (buf != &cx->vbi.sliced_mpeg_buf) {
- if (buf->readpos == buf->bytesused)
- cx18_stream_put_buf_fw(s, buf);
+ if (mdl != &cx->vbi.sliced_mpeg_mdl) {
+ if (mdl->readpos == mdl->bytesused)
+ cx18_stream_put_mdl_fw(s, mdl);
else
- cx18_push(s, buf, &s->q_full);
- } else if (buf->readpos == buf->bytesused) {
+ cx18_push(s, mdl, &s->q_full);
+ } else if (mdl->readpos == mdl->bytesused) {
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
cx->vbi.sliced_mpeg_size[idx] = 0;
cx->vbi.inserted_frame++;
- cx->vbi_data_inserted += buf->bytesused;
+ cx->vbi_data_inserted += mdl->bytesused;
}
if (rc < 0)
return rc;
@@ -543,7 +607,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
CX18_DEBUG_HI_FILE("Encoder poll\n");
poll_wait(filp, &s->waitq, wait);
- if (atomic_read(&s->q_full.buffers))
+ if (atomic_read(&s->q_full.depth))
return POLLIN | POLLRDNORM;
if (eof)
return POLLHUP;
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index 2477461e84d..eecf29af916 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -28,7 +28,6 @@
#include "cx18-gpio.h"
#include "cx18-i2c.h"
#include "cx18-irq.h"
-#include <media/ir-kbd-i2c.h>
#define CX18_REG_I2C_1_WR 0xf15000
#define CX18_REG_I2C_1_RD 0xf15008
@@ -97,17 +96,11 @@ static const char * const hw_devicenames[] = {
"ir_rx_z8f0811_haup",
};
-static const struct IR_i2c_init_data z8f0811_ir_init_data = {
- .ir_codes = &ir_codes_hauppauge_new_table,
- .internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR,
- .type = IR_TYPE_RC5,
- .name = "CX23418 Z8F0811 Hauppauge",
-};
-
-static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type,
- u8 addr)
+static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw,
+ const char *type, u8 addr)
{
struct i2c_board_info info;
+ struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data;
unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
memset(&info, 0, sizeof(struct i2c_board_info));
@@ -116,9 +109,11 @@ static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type,
/* Our default information for ir-kbd-i2c.c to use */
switch (hw) {
case CX18_HW_Z8F0811_IR_RX_HAUP:
- info.platform_data = (void *) &z8f0811_ir_init_data;
- break;
- default:
+ init_data->ir_codes = &ir_codes_hauppauge_new_table;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+ init_data->type = IR_TYPE_RC5;
+ init_data->name = cx->card_name;
+ info.platform_data = init_data;
break;
}
@@ -154,8 +149,8 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx)
return sd != NULL ? 0 : -1;
}
- if (hw & CX18_HW_Z8F0811_IR_HAUP)
- return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]);
+ if (hw & CX18_HW_IR_ANY)
+ return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]);
/* Is it not an I2C device or one we do not wish to register? */
if (!hw_addrs[idx])
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index fc76e4d6ffa..3e4fc192fde 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -910,7 +910,8 @@ static int cx18_log_status(struct file *file, void *fh)
continue;
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
s->name, s->s_flags,
- atomic_read(&s->q_full.buffers) * 100 / s->buffers,
+ atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
+ / s->buffers,
(s->buffers * s->buf_size) / 1024, s->buffers);
}
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index afe46c3d405..f231dd09c72 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -131,13 +131,39 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
* Functions that run in a work_queue work handling context
*/
+static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ if (!s->dvb.enabled || mdl->bytesused == 0)
+ return;
+
+ /* We ignore mdl and buf readpos accounting here - it doesn't matter */
+
+ /* The likely case */
+ if (list_is_singular(&mdl->buf_list)) {
+ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+ list);
+ if (buf->bytesused)
+ dvb_dmx_swfilter(&s->dvb.demux,
+ buf->buf, buf->bytesused);
+ return;
+ }
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ if (buf->bytesused == 0)
+ break;
+ dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused);
+ }
+}
+
static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
{
u32 handle, mdl_ack_count, id;
struct cx18_mailbox *mb;
struct cx18_mdl_ack *mdl_ack;
struct cx18_stream *s;
- struct cx18_buffer *buf;
+ struct cx18_mdl *mdl;
int i;
mb = &order->mb;
@@ -158,7 +184,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
id = mdl_ack->id;
/*
* Simple integrity check for processing a stale (and possibly
- * inconsistent mailbox): make sure the buffer id is in the
+ * inconsistent mailbox): make sure the MDL id is in the
* valid range for the stream.
*
* We go through the trouble of dealing with stale mailboxes
@@ -169,44 +195,42 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
* There are occasions when we get a half changed mailbox,
* which this check catches for a handle & id mismatch. If the
* handle and id do correspond, the worst case is that we
- * completely lost the old buffer, but pick up the new buffer
+ * completely lost the old MDL, but pick up the new MDL
* early (but the new mdl_ack is guaranteed to be good in this
* case as the firmware wouldn't point us to a new mdl_ack until
* it's filled in).
*
- * cx18_queue_get buf() will detect the lost buffers
+ * cx18_queue_get_mdl() will detect the lost MDLs
* and send them back to q_free for fw rotation eventually.
*/
if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
- !(id >= s->mdl_offset &&
- id < (s->mdl_offset + s->buffers))) {
+ !(id >= s->mdl_base_idx &&
+ id < (s->mdl_base_idx + s->buffers))) {
CX18_WARN("Fell behind! Ignoring stale mailbox with "
- " inconsistent data. Lost buffer for mailbox "
+ " inconsistent data. Lost MDL for mailbox "
"seq no %d\n", mb->request);
break;
}
- buf = cx18_queue_get_buf(s, id, mdl_ack->data_used);
+ mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used);
- CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
- if (buf == NULL) {
- CX18_WARN("Could not find buf %d for stream %s\n",
+ CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id);
+ if (mdl == NULL) {
+ CX18_WARN("Could not find MDL %d for stream %s\n",
id, s->name);
continue;
}
CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
- s->name, buf->bytesused);
+ s->name, mdl->bytesused);
if (s->type != CX18_ENC_STREAM_TYPE_TS)
- cx18_enqueue(s, buf, &s->q_full);
+ cx18_enqueue(s, mdl, &s->q_full);
else {
- if (s->dvb.enabled)
- dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
- buf->bytesused);
- cx18_enqueue(s, buf, &s->q_free);
+ cx18_mdl_send_to_dvb(s, mdl);
+ cx18_enqueue(s, mdl, &s->q_free);
}
}
- /* Put as many buffers as possible back into fw use */
+ /* Put as many MDLs as possible back into fw use */
cx18_stream_load_fw_queue(s);
wake_up(&cx->dma_waitq);
@@ -616,7 +640,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
/*
* Wait for XPU to perform extra actions for the caller in some cases.
- * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
+ * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs
* back in a burst shortly thereafter
*/
if (info->flags & API_SLOW)
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index e23aaac5b28..33a3491c453 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -39,14 +39,14 @@
struct cx18;
/*
- * This structure is used by CPU to provide completed buffers information
- * Its structure is dictrated by the layout of the SCB, required by the
- * firmware, but its defintion needs to be here, instead of in cx18-scb.h,
+ * This structure is used by CPU to provide completed MDL & buffers information.
+ * Its structure is dictated by the layout of the SCB, required by the
+ * firmware, but its definition needs to be here, instead of in cx18-scb.h,
* for mailbox work order scheduling
*/
struct cx18_mdl_ack {
u32 id; /* ID of a completed MDL */
- u32 data_used; /* Total data filled in the MDL for buffer 'id' */
+ u32 data_used; /* Total data filled in the MDL with 'id' */
};
/* The cx18_mailbox struct is the mailbox structure which is used for passing
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index fa1ed7897d9..63304823cef 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -26,6 +26,7 @@
#include "cx18-queue.h"
#include "cx18-streams.h"
#include "cx18-scb.h"
+#include "cx18-io.h"
void cx18_buf_swap(struct cx18_buffer *buf)
{
@@ -35,151 +36,312 @@ void cx18_buf_swap(struct cx18_buffer *buf)
swab32s((u32 *)(buf->buf + i));
}
+void _cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ if (buf->bytesused == 0)
+ break;
+ cx18_buf_swap(buf);
+ }
+}
+
void cx18_queue_init(struct cx18_queue *q)
{
INIT_LIST_HEAD(&q->list);
- atomic_set(&q->buffers, 0);
+ atomic_set(&q->depth, 0);
q->bytesused = 0;
}
-struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
struct cx18_queue *q, int to_front)
{
- /* clear the buffer if it is not to be enqueued to the full queue */
+ /* clear the mdl if it is not to be enqueued to the full queue */
if (q != &s->q_full) {
- buf->bytesused = 0;
- buf->readpos = 0;
- buf->b_flags = 0;
- buf->skipped = 0;
+ mdl->bytesused = 0;
+ mdl->readpos = 0;
+ mdl->m_flags = 0;
+ mdl->skipped = 0;
+ mdl->curr_buf = NULL;
}
/* q_busy is restricted to a max buffer count imposed by firmware */
if (q == &s->q_busy &&
- atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
q = &s->q_free;
spin_lock(&q->lock);
if (to_front)
- list_add(&buf->list, &q->list); /* LIFO */
+ list_add(&mdl->list, &q->list); /* LIFO */
else
- list_add_tail(&buf->list, &q->list); /* FIFO */
- q->bytesused += buf->bytesused - buf->readpos;
- atomic_inc(&q->buffers);
+ list_add_tail(&mdl->list, &q->list); /* FIFO */
+ q->bytesused += mdl->bytesused - mdl->readpos;
+ atomic_inc(&q->depth);
spin_unlock(&q->lock);
return q;
}
-struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
{
- struct cx18_buffer *buf = NULL;
+ struct cx18_mdl *mdl = NULL;
spin_lock(&q->lock);
if (!list_empty(&q->list)) {
- buf = list_first_entry(&q->list, struct cx18_buffer, list);
- list_del_init(&buf->list);
- q->bytesused -= buf->bytesused - buf->readpos;
- buf->skipped = 0;
- atomic_dec(&q->buffers);
+ mdl = list_first_entry(&q->list, struct cx18_mdl, list);
+ list_del_init(&mdl->list);
+ q->bytesused -= mdl->bytesused - mdl->readpos;
+ mdl->skipped = 0;
+ atomic_dec(&q->depth);
}
spin_unlock(&q->lock);
- return buf;
+ return mdl;
+}
+
+static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+ u32 buf_size = s->buf_size;
+ u32 bytesused = mdl->bytesused;
+
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ buf->readpos = 0;
+ if (bytesused >= buf_size) {
+ buf->bytesused = buf_size;
+ bytesused -= buf_size;
+ } else {
+ buf->bytesused = bytesused;
+ bytesused = 0;
+ }
+ cx18_buf_sync_for_cpu(s, buf);
+ }
+}
+
+static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18_buffer *buf;
+
+ if (list_is_singular(&mdl->buf_list)) {
+ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+ list);
+ buf->bytesused = mdl->bytesused;
+ buf->readpos = 0;
+ cx18_buf_sync_for_cpu(s, buf);
+ } else {
+ _cx18_mdl_update_bufs_for_cpu(s, mdl);
+ }
}
-struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
u32 bytesused)
{
struct cx18 *cx = s->cx;
- struct cx18_buffer *buf;
- struct cx18_buffer *tmp;
- struct cx18_buffer *ret = NULL;
+ struct cx18_mdl *mdl;
+ struct cx18_mdl *tmp;
+ struct cx18_mdl *ret = NULL;
LIST_HEAD(sweep_up);
/*
* We don't have to acquire multiple q locks here, because we are
* serialized by the single threaded work handler.
- * Buffers from the firmware will thus remain in order as
+ * MDLs from the firmware will thus remain in order as
* they are moved from q_busy to q_full or to the dvb ring buffer.
*/
spin_lock(&s->q_busy.lock);
- list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) {
+ list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) {
/*
* We should find what the firmware told us is done,
* right at the front of the queue. If we don't, we likely have
- * missed a buffer done message from the firmware.
- * Once we skip a buffer repeatedly, relative to the size of
+ * missed an mdl done message from the firmware.
+ * Once we skip an mdl repeatedly, relative to the size of
* q_busy, we have high confidence we've missed it.
*/
- if (buf->id != id) {
- buf->skipped++;
- if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
- /* buffer must have fallen out of rotation */
- CX18_WARN("Skipped %s, buffer %d, %d "
+ if (mdl->id != id) {
+ mdl->skipped++;
+ if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) {
+ /* mdl must have fallen out of rotation */
+ CX18_WARN("Skipped %s, MDL %d, %d "
"times - it must have dropped out of "
- "rotation\n", s->name, buf->id,
- buf->skipped);
+ "rotation\n", s->name, mdl->id,
+ mdl->skipped);
/* Sweep it up to put it back into rotation */
- list_move_tail(&buf->list, &sweep_up);
- atomic_dec(&s->q_busy.buffers);
+ list_move_tail(&mdl->list, &sweep_up);
+ atomic_dec(&s->q_busy.depth);
}
continue;
}
/*
- * We pull the desired buffer off of the queue here. Something
+ * We pull the desired mdl off of the queue here. Something
* will have to put it back on a queue later.
*/
- list_del_init(&buf->list);
- atomic_dec(&s->q_busy.buffers);
- ret = buf;
+ list_del_init(&mdl->list);
+ atomic_dec(&s->q_busy.depth);
+ ret = mdl;
break;
}
spin_unlock(&s->q_busy.lock);
/*
- * We found the buffer for which we were looking. Get it ready for
+ * We found the mdl for which we were looking. Get it ready for
* the caller to put on q_full or in the dvb ring buffer.
*/
if (ret != NULL) {
ret->bytesused = bytesused;
ret->skipped = 0;
- /* readpos and b_flags were 0'ed when the buf went on q_busy */
- cx18_buf_sync_for_cpu(s, ret);
+ /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */
+ cx18_mdl_update_bufs_for_cpu(s, ret);
if (s->type != CX18_ENC_STREAM_TYPE_TS)
- set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags);
+ set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags);
}
- /* Put any buffers the firmware is ignoring back into normal rotation */
- list_for_each_entry_safe(buf, tmp, &sweep_up, list) {
- list_del_init(&buf->list);
- cx18_enqueue(s, buf, &s->q_free);
+ /* Put any mdls the firmware is ignoring back into normal rotation */
+ list_for_each_entry_safe(mdl, tmp, &sweep_up, list) {
+ list_del_init(&mdl->list);
+ cx18_enqueue(s, mdl, &s->q_free);
}
return ret;
}
-/* Move all buffers of a queue to q_free, while flushing the buffers */
-static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
+/* Move all mdls of a queue, while flushing the mdl */
+static void cx18_queue_flush(struct cx18_stream *s,
+ struct cx18_queue *q_src, struct cx18_queue *q_dst)
{
- struct cx18_buffer *buf;
+ struct cx18_mdl *mdl;
- if (q == &s->q_free)
+ /* It only makes sense to flush to q_free or q_idle */
+ if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy)
return;
- spin_lock(&q->lock);
- while (!list_empty(&q->list)) {
- buf = list_first_entry(&q->list, struct cx18_buffer, list);
- list_move_tail(&buf->list, &s->q_free.list);
- buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0;
- atomic_inc(&s->q_free.buffers);
+ spin_lock(&q_src->lock);
+ spin_lock(&q_dst->lock);
+ while (!list_empty(&q_src->list)) {
+ mdl = list_first_entry(&q_src->list, struct cx18_mdl, list);
+ list_move_tail(&mdl->list, &q_dst->list);
+ mdl->bytesused = 0;
+ mdl->readpos = 0;
+ mdl->m_flags = 0;
+ mdl->skipped = 0;
+ mdl->curr_buf = NULL;
+ atomic_inc(&q_dst->depth);
}
- cx18_queue_init(q);
- spin_unlock(&q->lock);
+ cx18_queue_init(q_src);
+ spin_unlock(&q_src->lock);
+ spin_unlock(&q_dst->lock);
}
void cx18_flush_queues(struct cx18_stream *s)
{
- cx18_queue_flush(s, &s->q_busy);
- cx18_queue_flush(s, &s->q_full);
+ cx18_queue_flush(s, &s->q_busy, &s->q_free);
+ cx18_queue_flush(s, &s->q_full, &s->q_free);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_unload_queues(struct cx18_stream *s)
+{
+ struct cx18_queue *q_idle = &s->q_idle;
+ struct cx18_mdl *mdl;
+ struct cx18_buffer *buf;
+
+ /* Move all MDLS to q_idle */
+ cx18_queue_flush(s, &s->q_busy, q_idle);
+ cx18_queue_flush(s, &s->q_full, q_idle);
+ cx18_queue_flush(s, &s->q_free, q_idle);
+
+ /* Reset MDL id's and move all buffers back to the stream's buf_pool */
+ spin_lock(&q_idle->lock);
+ list_for_each_entry(mdl, &q_idle->list, list) {
+ while (!list_empty(&mdl->buf_list)) {
+ buf = list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list);
+ list_move_tail(&buf->list, &s->buf_pool);
+ buf->bytesused = 0;
+ buf->readpos = 0;
+ }
+ mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */
+ /* all other mdl fields were cleared by cx18_queue_flush() */
+ }
+ spin_unlock(&q_idle->lock);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_load_queues(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_mdl *mdl;
+ struct cx18_buffer *buf;
+ int mdl_id;
+ int i;
+ u32 partial_buf_size;
+
+ /*
+ * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free
+ * Excess MDLs are left on q_idle
+ * Excess buffers are left in buf_pool and/or on an MDL in q_idle
+ */
+ mdl_id = s->mdl_base_idx;
+ for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl;
+ mdl != NULL && i == s->bufs_per_mdl;
+ mdl = cx18_dequeue(s, &s->q_idle)) {
+
+ mdl->id = mdl_id;
+
+ for (i = 0; i < s->bufs_per_mdl; i++) {
+ if (list_empty(&s->buf_pool))
+ break;
+
+ buf = list_first_entry(&s->buf_pool, struct cx18_buffer,
+ list);
+ list_move_tail(&buf->list, &mdl->buf_list);
+
+ /* update the firmware's MDL array with this buffer */
+ cx18_writel(cx, buf->dma_handle,
+ &cx->scb->cpu_mdl[mdl_id + i].paddr);
+ cx18_writel(cx, s->buf_size,
+ &cx->scb->cpu_mdl[mdl_id + i].length);
+ }
+
+ if (i == s->bufs_per_mdl) {
+ /*
+ * The encoder doesn't honor s->mdl_size. So in the
+ * case of a non-integral number of buffers to meet
+ * mdl_size, we lie about the size of the last buffer
+ * in the MDL to get the encoder to really only send
+ * us mdl_size bytes per MDL transfer.
+ */
+ partial_buf_size = s->mdl_size % s->buf_size;
+ if (partial_buf_size) {
+ cx18_writel(cx, partial_buf_size,
+ &cx->scb->cpu_mdl[mdl_id + i - 1].length);
+ }
+ cx18_enqueue(s, mdl, &s->q_free);
+ } else {
+ /* Not enough buffers for this MDL; we won't use it */
+ cx18_push(s, mdl, &s->q_idle);
+ }
+ mdl_id += i;
+ }
+}
+
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+ int dma = s->dma;
+ u32 buf_size = s->buf_size;
+ struct pci_dev *pci_dev = s->cx->pci_dev;
+ struct cx18_buffer *buf;
+
+ list_for_each_entry(buf, &mdl->buf_list, list)
+ pci_dma_sync_single_for_device(pci_dev, buf->dma_handle,
+ buf_size, dma);
}
int cx18_stream_alloc(struct cx18_stream *s)
@@ -190,44 +352,62 @@ int cx18_stream_alloc(struct cx18_stream *s)
if (s->buffers == 0)
return 0;
- CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
+ CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers "
+ "(%d.%02d kB total)\n",
s->name, s->buffers, s->buf_size,
- s->buffers * s->buf_size / 1024);
+ s->buffers * s->buf_size / 1024,
+ (s->buffers * s->buf_size * 100 / 1024) % 100);
- if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
+ if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] -
(char __iomem *)cx->scb) > SCB_RESERVED_SIZE) {
unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE -
((char __iomem *)cx->scb->cpu_mdl));
CX18_ERR("Too many buffers, cannot fit in SCB area\n");
CX18_ERR("Max buffers = %zd\n",
- bufsz / sizeof(struct cx18_mdl));
+ bufsz / sizeof(struct cx18_mdl_ent));
return -ENOMEM;
}
- s->mdl_offset = cx->mdl_offset;
+ s->mdl_base_idx = cx->free_mdl_idx;
- /* allocate stream buffers. Initially all buffers are in q_free. */
+ /* allocate stream buffers and MDLs */
for (i = 0; i < s->buffers; i++) {
- struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
- GFP_KERNEL|__GFP_NOWARN);
+ struct cx18_mdl *mdl;
+ struct cx18_buffer *buf;
- if (buf == NULL)
+ /* 1 MDL per buffer to handle the worst & also default case */
+ mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN);
+ if (mdl == NULL)
break;
+
+ buf = kzalloc(sizeof(struct cx18_buffer),
+ GFP_KERNEL|__GFP_NOWARN);
+ if (buf == NULL) {
+ kfree(mdl);
+ break;
+ }
+
buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
if (buf->buf == NULL) {
+ kfree(mdl);
kfree(buf);
break;
}
- buf->id = cx->buffer_id++;
+
+ INIT_LIST_HEAD(&mdl->list);
+ INIT_LIST_HEAD(&mdl->buf_list);
+ mdl->id = s->mdl_base_idx; /* a somewhat safe value */
+ cx18_enqueue(s, mdl, &s->q_idle);
+
INIT_LIST_HEAD(&buf->list);
buf->dma_handle = pci_map_single(s->cx->pci_dev,
buf->buf, s->buf_size, s->dma);
cx18_buf_sync_for_cpu(s, buf);
- cx18_enqueue(s, buf, &s->q_free);
+ list_add_tail(&buf->list, &s->buf_pool);
}
if (i == s->buffers) {
- cx->mdl_offset += s->buffers;
+ cx->free_mdl_idx += s->buffers;
return 0;
}
CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
@@ -237,13 +417,21 @@ int cx18_stream_alloc(struct cx18_stream *s)
void cx18_stream_free(struct cx18_stream *s)
{
+ struct cx18_mdl *mdl;
struct cx18_buffer *buf;
- /* move all buffers to q_free */
- cx18_flush_queues(s);
+ /* move all buffers to buf_pool and all MDLs to q_idle */
+ cx18_unload_queues(s);
+
+ /* empty q_idle */
+ while ((mdl = cx18_dequeue(s, &s->q_idle)))
+ kfree(mdl);
+
+ /* empty buf_pool */
+ while (!list_empty(&s->buf_pool)) {
+ buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list);
+ list_del_init(&buf->list);
- /* empty q_free */
- while ((buf = cx18_dequeue(s, &s->q_free))) {
pci_unmap_single(s->cx->pci_dev, buf->dma_handle,
s->buf_size, s->dma);
kfree(buf->buf);
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 4de06269d88..88a6d34ad3b 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -40,32 +40,59 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
s->buf_size, s->dma);
}
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_sync_for_device(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ if (list_is_singular(&mdl->buf_list))
+ cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list,
+ struct cx18_buffer,
+ list));
+ else
+ _cx18_mdl_sync_for_device(s, mdl);
+}
+
void cx18_buf_swap(struct cx18_buffer *buf);
+void _cx18_mdl_swap(struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+ if (list_is_singular(&mdl->buf_list))
+ cx18_buf_swap(list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list));
+ else
+ _cx18_mdl_swap(mdl);
+}
/* cx18_queue utility functions */
-struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
struct cx18_queue *q, int to_front);
static inline
-struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
struct cx18_queue *q)
{
- return _cx18_enqueue(s, buf, q, 0); /* FIFO */
+ return _cx18_enqueue(s, mdl, q, 0); /* FIFO */
}
static inline
-struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl,
struct cx18_queue *q)
{
- return _cx18_enqueue(s, buf, q, 1); /* LIFO */
+ return _cx18_enqueue(s, mdl, q, 1); /* LIFO */
}
void cx18_queue_init(struct cx18_queue *q);
-struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
-struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
u32 bytesused);
void cx18_flush_queues(struct cx18_stream *s);
+/* queue MDL reconfiguration helpers */
+void cx18_unload_queues(struct cx18_stream *s);
+void cx18_load_queues(struct cx18_stream *s);
+
/* cx18_stream utility functions */
int cx18_stream_alloc(struct cx18_stream *s);
void cx18_stream_free(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
index 1dc1c431f5a..368f23d0870 100644
--- a/drivers/media/video/cx18/cx18-scb.h
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -81,7 +81,7 @@
/* This structure is used by EPU to provide memory descriptors in its memory */
-struct cx18_mdl {
+struct cx18_mdl_ent {
u32 paddr; /* Physical address of a buffer segment */
u32 length; /* Length of the buffer segment */
};
@@ -272,7 +272,7 @@ struct cx18_scb {
struct cx18_mailbox ppu2epu_mb;
struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
- struct cx18_mdl cpu_mdl[1];
+ struct cx18_mdl_ent cpu_mdl[1];
};
void cx18_init_scb(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 7df513a2dba..c398651dd74 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -115,6 +115,9 @@ static void cx18_stream_init(struct cx18 *cx, int type)
s->dma = cx18_stream_info[type].dma;
s->buffers = cx->stream_buffers[type];
s->buf_size = cx->stream_buf_size[type];
+ INIT_LIST_HEAD(&s->buf_pool);
+ s->bufs_per_mdl = 1;
+ s->mdl_size = s->buf_size * s->bufs_per_mdl;
init_waitqueue_head(&s->waitq);
s->id = -1;
@@ -124,6 +127,8 @@ static void cx18_stream_init(struct cx18 *cx, int type)
cx18_queue_init(&s->q_busy);
spin_lock_init(&s->q_full.lock);
cx18_queue_init(&s->q_full);
+ spin_lock_init(&s->q_idle.lock);
+ cx18_queue_init(&s->q_idle);
INIT_WORK(&s->out_work_order, cx18_out_work_handler);
}
@@ -257,9 +262,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
switch (vfl_type) {
case VFL_TYPE_GRABBER:
- CX18_INFO("Registered device video%d for %s (%d x %d kB)\n",
+ CX18_INFO("Registered device video%d for %s "
+ "(%d x %d.%02d kB)\n",
num, s->name, cx->stream_buffers[type],
- cx->stream_buf_size[type]/1024);
+ cx->stream_buf_size[type] / 1024,
+ (cx->stream_buf_size[type] * 100 / 1024) % 100);
break;
case VFL_TYPE_RADIO:
@@ -441,8 +448,8 @@ static void cx18_vbi_setup(struct cx18_stream *s)
}
static
-struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s,
- struct cx18_buffer *buf)
+struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
{
struct cx18 *cx = s->cx;
struct cx18_queue *q;
@@ -451,16 +458,16 @@ struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s,
if (s->handle == CX18_INVALID_TASK_HANDLE ||
test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
!test_bit(CX18_F_S_STREAMING, &s->s_flags))
- return cx18_enqueue(s, buf, &s->q_free);
+ return cx18_enqueue(s, mdl, &s->q_free);
- q = cx18_enqueue(s, buf, &s->q_busy);
+ q = cx18_enqueue(s, mdl, &s->q_busy);
if (q != &s->q_busy)
- return q; /* The firmware has the max buffers it can handle */
+ return q; /* The firmware has the max MDLs it can handle */
- cx18_buf_sync_for_device(s, buf);
+ cx18_mdl_sync_for_device(s, mdl);
cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
+ (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
+ s->bufs_per_mdl, mdl->id, s->mdl_size);
return q;
}
@@ -468,19 +475,19 @@ static
void _cx18_stream_load_fw_queue(struct cx18_stream *s)
{
struct cx18_queue *q;
- struct cx18_buffer *buf;
+ struct cx18_mdl *mdl;
- if (atomic_read(&s->q_free.buffers) == 0 ||
- atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ if (atomic_read(&s->q_free.depth) == 0 ||
+ atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
return;
/* Move from q_free to q_busy notifying the firmware, until the limit */
do {
- buf = cx18_dequeue(s, &s->q_free);
- if (buf == NULL)
+ mdl = cx18_dequeue(s, &s->q_free);
+ if (mdl == NULL)
break;
- q = _cx18_stream_put_buf_fw(s, buf);
- } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM
+ q = _cx18_stream_put_mdl_fw(s, mdl);
+ } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
&& q == &s->q_busy);
}
@@ -492,11 +499,51 @@ void cx18_out_work_handler(struct work_struct *work)
_cx18_stream_load_fw_queue(s);
}
+static void cx18_stream_configure_mdls(struct cx18_stream *s)
+{
+ cx18_unload_queues(s);
+
+ switch (s->type) {
+ case CX18_ENC_STREAM_TYPE_YUV:
+ /*
+ * Height should be a multiple of 32 lines.
+ * Set the MDL size to the exact size needed for one frame.
+ * Use enough buffers per MDL to cover the MDL size
+ */
+ s->mdl_size = 720 * s->cx->params.height * 3 / 2;
+ s->bufs_per_mdl = s->mdl_size / s->buf_size;
+ if (s->mdl_size % s->buf_size)
+ s->bufs_per_mdl++;
+ break;
+ case CX18_ENC_STREAM_TYPE_VBI:
+ s->bufs_per_mdl = 1;
+ if (cx18_raw_vbi(s->cx)) {
+ s->mdl_size = (s->cx->is_60hz ? 12 : 18)
+ * 2 * vbi_active_samples;
+ } else {
+ /*
+ * See comment in cx18_vbi_setup() below about the
+ * extra lines we capture in sliced VBI mode due to
+ * the lines on which EAV RP codes toggle.
+ */
+ s->mdl_size = s->cx->is_60hz
+ ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz
+ : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz;
+ }
+ break;
+ default:
+ s->bufs_per_mdl = 1;
+ s->mdl_size = s->buf_size * s->bufs_per_mdl;
+ break;
+ }
+
+ cx18_load_queues(s);
+}
+
int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
{
u32 data[MAX_MB_ARGUMENTS];
struct cx18 *cx = s->cx;
- struct cx18_buffer *buf;
int captype = 0;
struct cx18_api_func_private priv;
@@ -619,14 +666,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
/* Init all the cpu_mdls for this stream */
- cx18_flush_queues(s);
- spin_lock(&s->q_free.lock);
- list_for_each_entry(buf, &s->q_free.list, list) {
- cx18_writel(cx, buf->dma_handle,
- &cx->scb->cpu_mdl[buf->id].paddr);
- cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
- }
- spin_unlock(&s->q_free.lock);
+ cx18_stream_configure_mdls(s);
_cx18_stream_load_fw_queue(s);
/* begin_capture */
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index 1afc3fd9d82..4a01db5e5a3 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -28,18 +28,18 @@ int cx18_streams_setup(struct cx18 *cx);
int cx18_streams_register(struct cx18 *cx);
void cx18_streams_cleanup(struct cx18 *cx, int unregister);
-/* Related to submission of buffers to firmware */
+/* Related to submission of mdls to firmware */
static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
{
struct cx18 *cx = s->cx;
queue_work(cx->out_work_queue, &s->out_work_order);
}
-static inline void cx18_stream_put_buf_fw(struct cx18_stream *s,
- struct cx18_buffer *buf)
+static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
{
- /* Put buf on q_free; the out work handler will move buf(s) to q_busy */
- cx18_enqueue(s, buf, &s->q_free);
+ /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */
+ cx18_enqueue(s, mdl, &s->q_free);
cx18_stream_load_fw_queue(s);
}
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index c2aef4add31..574c1c6974f 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -105,6 +105,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
/* Compress raw VBI format, removes leading SAV codes and surplus space
after the frame. Returns new compressed size. */
+/* FIXME - this function ignores the input size. */
static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
{
u32 line_size = vbi_active_samples;
@@ -185,8 +186,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
return line;
}
-void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
- int streamtype)
+static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf)
{
/*
* The CX23418 provides a 12 byte header in its raw VBI buffers to us:
@@ -203,9 +203,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
u32 pts;
int lines;
- if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
- return;
-
/*
* The CX23418 sends us data that is 32 bit little-endian swapped,
* but we want the raw VBI bytes in the order they were in the raster
@@ -250,3 +247,31 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
copy_vbi_data(cx, lines, pts);
cx->vbi.frame++;
}
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
+ int streamtype)
+{
+ struct cx18_buffer *buf;
+ u32 orig_used;
+
+ if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
+ return;
+
+ /*
+ * Big assumption here:
+ * Every buffer hooked to the MDL's buf_list is a complete VBI frame
+ * that ends at the end of the buffer.
+ *
+ * To assume anything else would make the code in this file
+ * more complex, or require extra memcpy()'s to make the
+ * buffers satisfy the above assumption. It's just simpler to set
+ * up the encoder buffer transfers to make the assumption true.
+ */
+ list_for_each_entry(buf, &mdl->buf_list, list) {
+ orig_used = buf->bytesused;
+ if (orig_used == 0)
+ break;
+ _cx18_process_vbi_data(cx, buf);
+ mdl->bytesused -= (orig_used - buf->bytesused);
+ }
+}
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h
index e7e1ae427f3..b365cf4b466 100644
--- a/drivers/media/video/cx18/cx18-vbi.h
+++ b/drivers/media/video/cx18/cx18-vbi.h
@@ -21,6 +21,6 @@
* 02111-1307 USA
*/
-void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
int streamtype);
int cx18_used_line(struct cx18 *cx, int line, int field);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index 45494b094e7..9c0b5bb1b01 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -24,7 +24,7 @@
#define CX18_DRIVER_NAME "cx18"
#define CX18_DRIVER_VERSION_MAJOR 1
-#define CX18_DRIVER_VERSION_MINOR 2
+#define CX18_DRIVER_VERSION_MINOR 3
#define CX18_DRIVER_VERSION_PATCHLEVEL 0
#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 9956abf576c..868806effdc 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -363,7 +363,7 @@
/* Description: This command provides the offset to a Memory Descriptor List
IN[0] - Task handle. Handle of the task to start
IN[1] - Offset of the MDL from the beginning of the local DDR.
- IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1]
+ IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1]
IN[3] - Buffer ID
IN[4] - Total buffer length
ReturnCode - One of the ERR_DE_... */
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index 28f48f41f21..c2174413ab2 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -2414,9 +2414,11 @@ int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev)
cx231xx_info("No ACK after %d msec -GPIO I2C failed!",
nInit * 10);
- /* readAck
- throuth clock stretch ,slave has given a SCL signal,
- so the SDA data can be directly read. */
+ /*
+ * readAck
+ * through clock stretch, slave has given a SCL signal,
+ * so the SDA data can be directly read.
+ */
status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) {
diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c
index 48f22fa38e6..cd135f01b9c 100644
--- a/drivers/media/video/cx231xx/cx231xx-input.c
+++ b/drivers/media/video/cx231xx/cx231xx-input.c
@@ -126,8 +126,7 @@ static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
if (do_sendkey) {
dprintk("sending keypress\n");
- ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
- poll_result.rc_data[0]);
+ ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]);
ir_input_nokey(ir->input, &ir->ir);
}
@@ -198,7 +197,11 @@ int cx231xx_ir_init(struct cx231xx *dev)
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
- ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER,
+ dev->board.ir_codes);
+ if (err < 0)
+ goto err_out_free;
+
input_dev->name = ir->name;
input_dev->phys = ir->phys;
input_dev->id.bustype = BUS_USB;
@@ -223,6 +226,7 @@ err_out_stop:
cx231xx_ir_stop(ir);
dev->ir = NULL;
err_out_free:
+ ir_input_free(input_dev);
input_free_device(input_dev);
kfree(ir);
return err;
@@ -237,6 +241,7 @@ int cx231xx_ir_fini(struct cx231xx *dev)
return 0;
cx231xx_ir_stop(ir);
+ ir_input_free(ir->input);
input_unregister_device(ir->input);
kfree(ir);
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index 36503725d97..d095aa0d6d1 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -2106,7 +2106,7 @@ static int cx231xx_v4l2_close(struct file *filp)
}
/* Save some power by putting tuner to sleep */
- call_all(dev, tuner, s_standby);
+ call_all(dev, core, s_power, 0);
/* do this before setting alternate! */
cx231xx_uninit_isoc(dev);
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index fd3fc3e3198..bcdda9a9aa9 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -18,7 +18,9 @@ config VIDEO_CX23885
select DVB_TDA10048 if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
select DVB_STV6110 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
select DVB_STV0900 if !DVB_FE_CUSTOMISE
+ select DVB_DS3000 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index ab8ea35c9bf..5787ae24363 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -1,6 +1,7 @@
cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
- netup-init.o cimax2.o netup-eeprom.o
+ cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \
+ netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 6c3b51ce337..0eed852c61e 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -37,6 +37,7 @@
#include <media/cx2341x.h>
#include "cx23885.h"
+#include "cx23885-ioctl.h"
#define CX23885_FIRM_IMAGE_SIZE 376836
#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
@@ -318,7 +319,7 @@ static int mc417_wait_ready(struct cx23885_dev *dev)
}
}
-static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
+int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
{
u32 regval;
@@ -382,7 +383,7 @@ static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
return mc417_wait_ready(dev);
}
-static int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
+int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
{
int retval;
u32 regval;
@@ -1724,6 +1725,11 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
.vidioc_log_status = vidioc_log_status,
.vidioc_querymenu = vidioc_querymenu,
.vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_chip_ident = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx23885_g_register,
+ .vidioc_s_register = cx23885_s_register,
+#endif
};
static struct video_device cx23885_mpeg_template = {
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index bfdf79f1033..1ec48169277 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -28,6 +28,7 @@
#include "cx23885.h"
#include "tuner-xc2028.h"
#include "netup-init.h"
+#include "cx23888-ir.h"
/* ------------------------------------------------------------------ */
/* board config info */
@@ -199,11 +200,61 @@ struct cx23885_board cx23885_boards[] = {
},
[CX23885_BOARD_MYGICA_X8506] = {
.name = "Mygica X8506 DMB-TH",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .porta = CX23885_ANALOG_VIDEO,
.portb = CX23885_MPEG_DVB,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
},
[CX23885_BOARD_MAGICPRO_PROHDTVE2] = {
.name = "Magic-Pro ProHDTV Extreme 2",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .porta = CX23885_ANALOG_VIDEO,
.portb = CX23885_MPEG_DVB,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
},
[CX23885_BOARD_HAUPPAUGE_HVR1850] = {
.name = "Hauppauge WinTV-HVR1850",
@@ -214,6 +265,15 @@ struct cx23885_board cx23885_boards[] = {
.name = "Compro VideoMate E800",
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_HAUPPAUGE_HVR1290] = {
+ .name = "Hauppauge WinTV-HVR1290",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_MYGICA_X8558PRO] = {
+ .name = "Mygica X8558 PRO DMB-TH",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -349,6 +409,14 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x1858,
.subdevice = 0xe800,
.card = CX23885_BOARD_COMPRO_VIDEOMATE_E800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8551,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1290,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8578,
+ .card = CX23885_BOARD_MYGICA_X8558PRO,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -509,9 +577,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
* DVB-T and MPEG2 HW Encoder */
break;
case 85021:
- /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM,
+ /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM,
Dual channel ATSC and MPEG2 HW Encoder */
break;
+ case 85721:
+ /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR,
+ Dual channel ATSC and Basic analog */
+ break;
default:
printk(KERN_WARNING "%s: warning: "
"unknown hauppauge model #%d\n",
@@ -710,10 +782,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx_set(GP0_IO, 0x00040004);
break;
case CX23885_BOARD_TBS_6920:
- case CX23885_BOARD_TEVII_S470:
cx_write(MC417_CTL, 0x00000036);
cx_write(MC417_OEN, 0x00001000);
- cx_write(MC417_RWD, 0x00001800);
+ cx_set(MC417_RWD, 0x00000002);
+ mdelay(200);
+ cx_clear(MC417_RWD, 0x00000800);
+ mdelay(200);
+ cx_set(MC417_RWD, 0x00000800);
+ mdelay(200);
break;
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
/* GPIO-0 INTA from CiMax1
@@ -758,15 +834,26 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
break;
case CX23885_BOARD_MYGICA_X8506:
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ /* GPIO-0 (0)Analog / (1)Digital TV */
/* GPIO-1 reset XC5000 */
/* GPIO-2 reset LGS8GL5 / LGS8G75 */
- cx_set(GP0_IO, 0x00060000);
- cx_clear(GP0_IO, 0x00000006);
+ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1);
+ cx23885_gpio_clear(dev, GPIO_1 | GPIO_2);
mdelay(100);
- cx_set(GP0_IO, 0x00060006);
+ cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2);
+ mdelay(100);
+ break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ /* GPIO-0 reset first ATBM8830 */
+ /* GPIO-1 reset second ATBM8830 */
+ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1);
+ cx23885_gpio_clear(dev, GPIO_0 | GPIO_1);
+ mdelay(100);
+ cx23885_gpio_set(dev, GPIO_0 | GPIO_1);
mdelay(100);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
/* GPIO-0 656_CLK */
/* GPIO-1 656_D0 */
/* GPIO-2 Wake# */
@@ -801,6 +888,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
int cx23885_ir_init(struct cx23885_dev *dev)
{
+ int ret = 0;
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1500:
@@ -812,15 +900,46 @@ int cx23885_ir_init(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1275:
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1210:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
/* FIXME: Implement me */
break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ ret = cx23888_ir_probe(dev);
+ if (ret)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ dev->pci_irqmask |= PCI_MSK_IR;
+ break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
request_module("ir-kbd-i2c");
break;
}
- return 0;
+ return ret;
+}
+
+void cx23885_ir_fini(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ dev->pci_irqmask &= ~PCI_MSK_IR;
+ cx_clear(PCI_INT_MSK, PCI_MSK_IR);
+ cx23888_ir_remove(dev);
+ dev->sd_ir = NULL;
+ break;
+ }
+}
+
+void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
+ cx_set(PCI_INT_MSK, PCI_MSK_IR);
+ break;
+ }
}
void cx23885_card_setup(struct cx23885_dev *dev)
@@ -853,6 +972,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1255:
case CX23885_BOARD_HAUPPAUGE_HVR1210:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
if (dev->i2c_bus[0].i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0xc0);
break;
@@ -886,8 +1006,12 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
- case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_TBS_6920:
+ ts1->gen_ctrl_val = 0x4; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_TEVII_S470:
case CX23885_BOARD_DVBWORLD_2005:
ts1->gen_ctrl_val = 0x5; /* Parallel */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -907,6 +1031,14 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
@@ -922,6 +1054,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1210:
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
default:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -939,6 +1072,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index c31284ba19d..04b12d27bc1 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -32,6 +32,9 @@
#include "cx23885.h"
#include "cimax2.h"
+#include "cx23888-ir.h"
+#include "cx23885-ir.h"
+#include "cx23885-input.h"
MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -753,6 +756,23 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev)
__func__, dev->hwrevision);
}
+/* Find the first v4l2_subdev member of the group id in hw */
+struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw)
+{
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
+
+ spin_lock(&dev->v4l2_dev.lock);
+ v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) {
+ if (sd->grp_id == hw) {
+ result = sd;
+ break;
+ }
+ }
+ spin_unlock(&dev->v4l2_dev.lock);
+ return result;
+}
+
static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
@@ -899,7 +919,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_i2c_register(&dev->i2c_bus[1]);
cx23885_i2c_register(&dev->i2c_bus[2]);
cx23885_card_setup(dev);
- call_all(dev, tuner, s_standby);
+ call_all(dev, core, s_power, 0);
cx23885_ir_init(dev);
if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
@@ -1637,6 +1657,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
+ bool ir_handled = false;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx_read(PCI_INT_MSK);
@@ -1662,18 +1683,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n",
ts2_status, ts2_mask, ts2_count);
- if ((pci_status & PCI_MSK_RISC_RD) ||
- (pci_status & PCI_MSK_RISC_WR) ||
- (pci_status & PCI_MSK_AL_RD) ||
- (pci_status & PCI_MSK_AL_WR) ||
- (pci_status & PCI_MSK_APB_DMA) ||
- (pci_status & PCI_MSK_VID_C) ||
- (pci_status & PCI_MSK_VID_B) ||
- (pci_status & PCI_MSK_VID_A) ||
- (pci_status & PCI_MSK_AUD_INT) ||
- (pci_status & PCI_MSK_AUD_EXT) ||
- (pci_status & PCI_MSK_GPIO0) ||
- (pci_status & PCI_MSK_GPIO1)) {
+ if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR |
+ PCI_MSK_AL_RD | PCI_MSK_AL_WR | PCI_MSK_APB_DMA |
+ PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A |
+ PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
+ PCI_MSK_GPIO0 | PCI_MSK_GPIO1 |
+ PCI_MSK_IR)) {
if (pci_status & PCI_MSK_RISC_RD)
dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n",
@@ -1722,6 +1737,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if (pci_status & PCI_MSK_GPIO1)
dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n",
PCI_MSK_GPIO1);
+
+ if (pci_status & PCI_MSK_IR)
+ dprintk(7, " (PCI_MSK_IR 0x%08x)\n",
+ PCI_MSK_IR);
}
if (cx23885_boards[dev->board].cimax > 0 &&
@@ -1752,12 +1771,48 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if (vida_status)
handled += cx23885_video_irq(dev, vida_status);
+ if (pci_status & PCI_MSK_IR) {
+ v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
+ pci_status, &ir_handled);
+ if (ir_handled)
+ handled++;
+ }
+
if (handled)
cx_write(PCI_INT_STAT, pci_status);
out:
return IRQ_RETVAL(handled);
}
+static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
+ unsigned int notification, void *arg)
+{
+ struct cx23885_dev *dev;
+
+ if (sd == NULL)
+ return;
+
+ dev = to_cx23885(sd->v4l2_dev);
+
+ switch (notification) {
+ case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+ if (sd == dev->sd_ir)
+ cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
+ break;
+ case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
+ if (sd == dev->sd_ir)
+ cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
+ break;
+ }
+}
+
+static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
+{
+ INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
+ INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
+ dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
+}
+
static inline int encoder_on_portb(struct cx23885_dev *dev)
{
return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER;
@@ -1816,6 +1871,26 @@ void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask)
printk(KERN_INFO "%s: Unsupported\n", dev->name);
}
+u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask)
+{
+ if (mask & 0x00000007)
+ return (cx_read(GP0_IO) >> 8) & mask & 0x7;
+
+ if (mask & 0x0007fff8) {
+ if (encoder_on_portb(dev) || encoder_on_portc(dev))
+ printk(KERN_ERR
+ "%s: Reading GPIO moving on encoder ports\n",
+ dev->name);
+ return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3;
+ }
+
+ /* TODO: 23-19 */
+ if (mask & 0x00f80000)
+ printk(KERN_INFO "%s: Unsupported\n", dev->name);
+
+ return 0;
+}
+
void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
{
if ((mask & 0x00000007) && asoutput)
@@ -1854,6 +1929,9 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
if (err < 0)
goto fail_free;
+ /* Prepare to handle notifications from subdevices */
+ cx23885_v4l2_dev_notify_init(dev);
+
/* pci init */
dev->pci = pci_dev;
if (pci_enable_device(pci_dev)) {
@@ -1896,6 +1974,14 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
break;
}
+ /*
+ * The CX2388[58] IR controller can start firing interrupts when
+ * enabled, so these have to take place after the cx23885_irq() handler
+ * is hooked up by the call to request_irq() above.
+ */
+ cx23885_ir_pci_int_enable(dev);
+ cx23885_input_init(dev);
+
return 0;
fail_irq:
@@ -1912,6 +1998,9 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
struct cx23885_dev *dev = to_cx23885(v4l2_dev);
+ cx23885_input_fini(dev);
+ cx23885_ir_fini(dev);
+
cx23885_shutdown(dev);
pci_disable_device(pci_dev);
@@ -1957,7 +2046,7 @@ static struct pci_driver cx23885_pci_driver = {
.resume = NULL,
};
-static int cx23885_init(void)
+static int __init cx23885_init(void)
{
printk(KERN_INFO "cx23885 driver version %d.%d.%d loaded\n",
(CX23885_VERSION_CODE >> 16) & 0xff,
@@ -1970,7 +2059,7 @@ static int cx23885_init(void)
return pci_register_driver(&cx23885_pci_driver);
}
-static void cx23885_fini(void)
+static void __exit cx23885_fini(void)
{
pci_unregister_driver(&cx23885_pci_driver);
}
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 45e13ee66dc..e45d2df0813 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -38,6 +38,7 @@
#include "tda18271.h"
#include "lgdt330x.h"
#include "xc5000.h"
+#include "max2165.h"
#include "tda10048.h"
#include "tuner-xc2028.h"
#include "tuner-simple.h"
@@ -54,6 +55,9 @@
#include "netup-eeprom.h"
#include "netup-init.h"
#include "lgdt3305.h"
+#include "atbm8830.h"
+#include "ds3000.h"
+#include "cx23885-f300.h"
static unsigned int debug;
@@ -400,6 +404,7 @@ static struct stv0900_reg stv0900_ts_regs[] = {
static struct stv0900_config netup_stv0900_config = {
.demod_address = 0x68,
+ .demod_mode = 1, /* dual */
.xtal = 8000000,
.clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
.diseqc_mode = 2,/* 2/3 PWM */
@@ -414,34 +419,22 @@ static struct stv6110_config netup_stv6110_tunerconfig_a = {
.i2c_address = 0x60,
.mclk = 16000000,
.clk_div = 1,
+ .gain = 8, /* +16 dB - maximum gain */
};
static struct stv6110_config netup_stv6110_tunerconfig_b = {
.i2c_address = 0x63,
.mclk = 16000000,
.clk_div = 1,
+ .gain = 8, /* +16 dB - maximum gain */
};
-static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
-{
- struct cx23885_tsport *port = fe->dvb->priv;
- struct cx23885_dev *dev = port->dev;
-
- if (voltage == SEC_VOLTAGE_18)
- cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */
- else if (voltage == SEC_VOLTAGE_13)
- cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */
- else
- cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */
- return 0;
-}
-
static struct cx24116_config tbs_cx24116_config = {
- .demod_address = 0x05,
+ .demod_address = 0x55,
};
-static struct cx24116_config tevii_cx24116_config = {
- .demod_address = 0x55,
+static struct ds3000_config tevii_ds3000_config = {
+ .demod_address = 0x68,
};
static struct cx24116_config dvbworld_cx24116_config = {
@@ -486,11 +479,40 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe,
break;
}
break;
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ /* Select Digital TV */
+ cx23885_gpio_set(dev, GPIO_0);
+ break;
}
- return (port->set_frontend_save) ?
- port->set_frontend_save(fe, param) : -ENODEV;
+ return 0;
}
+static int cx23885_dvb_fe_ioctl_override(struct dvb_frontend *fe,
+ unsigned int cmd, void *parg,
+ unsigned int stage)
+{
+ int err = 0;
+
+ switch (stage) {
+ case DVB_FE_IOCTL_PRE:
+
+ switch (cmd) {
+ case FE_SET_FRONTEND:
+ err = cx23885_dvb_set_frontend(fe,
+ (struct dvb_frontend_parameters *) parg);
+ break;
+ }
+ break;
+
+ case DVB_FE_IOCTL_POST:
+ /* no post-ioctl handling required */
+ break;
+ }
+ return err;
+};
+
+
static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = {
.prod = LGS8GXX_PROD_LGS8G75,
.demod_address = 0x19,
@@ -511,6 +533,38 @@ static struct xc5000_config magicpro_prohdtve2_xc5000_config = {
.if_khz = 6500,
};
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = {
+ .prod = ATBM8830_PROD_8830,
+ .demod_address = 0x44,
+ .serial_ts = 0,
+ .ts_sampling_edge = 1,
+ .ts_clk_gated = 0,
+ .osc_clk_freq = 30400, /* in kHz */
+ .if_freq = 0, /* zero IF */
+ .zif_swap_iq = 1,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg1 = {
+ .i2c_address = 0x60,
+ .osc_clk = 20
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = {
+ .prod = ATBM8830_PROD_8830,
+ .demod_address = 0x44,
+ .serial_ts = 1,
+ .ts_sampling_edge = 1,
+ .ts_clk_gated = 0,
+ .osc_clk_freq = 30400, /* in kHz */
+ .if_freq = 0, /* zero IF */
+ .zif_swap_iq = 1,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
+ .i2c_address = 0x60,
+ .osc_clk = 20
+};
+
static int dvb_register(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
@@ -550,12 +604,6 @@ static int dvb_register(struct cx23885_tsport *port)
0x60, &dev->i2c_bus[1].i2c_adap,
&hauppauge_hvr127x_config);
}
-
- /* FIXME: temporary hack */
- /* define bridge override to set_frontend */
- port->set_frontend_save = fe0->dvb.frontend->ops.set_frontend;
- fe0->dvb.frontend->ops.set_frontend = cx23885_dvb_set_frontend;
-
break;
case CX23885_BOARD_HAUPPAUGE_HVR1255:
i2c_bus = &dev->i2c_bus[0];
@@ -772,23 +820,23 @@ static int dvb_register(struct cx23885_tsport *port)
}
break;
case CX23885_BOARD_TBS_6920:
- i2c_bus = &dev->i2c_bus[0];
+ i2c_bus = &dev->i2c_bus[1];
fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &tbs_cx24116_config,
- &i2c_bus->i2c_adap);
+ &tbs_cx24116_config,
+ &i2c_bus->i2c_adap);
if (fe0->dvb.frontend != NULL)
- fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
break;
case CX23885_BOARD_TEVII_S470:
i2c_bus = &dev->i2c_bus[1];
- fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &tevii_cx24116_config,
- &i2c_bus->i2c_adap);
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &i2c_bus->i2c_adap);
if (fe0->dvb.frontend != NULL)
- fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
break;
case CX23885_BOARD_DVBWORLD_2005:
@@ -814,8 +862,8 @@ static int dvb_register(struct cx23885_tsport *port)
if (!dvb_attach(lnbh24_attach,
fe0->dvb.frontend,
&i2c_bus->i2c_adap,
- LNBH24_PCL,
- LNBH24_TTX, 0x09))
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x09))
printk(KERN_ERR
"No LNBH24 found!\n");
@@ -835,8 +883,8 @@ static int dvb_register(struct cx23885_tsport *port)
if (!dvb_attach(lnbh24_attach,
fe0->dvb.frontend,
&i2c_bus->i2c_adap,
- LNBH24_PCL,
- LNBH24_TTX, 0x0a))
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x0a))
printk(KERN_ERR
"No LNBH24 found!\n");
@@ -872,6 +920,7 @@ static int dvb_register(struct cx23885_tsport *port)
}
break;
case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
i2c_bus = &dev->i2c_bus[0];
fe0->dvb.frontend = dvb_attach(s5h1411_attach,
&hcw_s5h1411_config,
@@ -881,6 +930,36 @@ static int dvb_register(struct cx23885_tsport *port)
0x60, &dev->i2c_bus[0].i2c_adap,
&hauppauge_tda18271_config);
break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ switch (port->nr) {
+ /* port B */
+ case 1:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+ &mygica_x8558pro_atbm8830_cfg1,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(max2165_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg1);
+ }
+ break;
+ /* port C */
+ case 2:
+ i2c_bus = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+ &mygica_x8558pro_atbm8830_cfg2,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(max2165_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg2);
+ }
+ break;
+ }
+ break;
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
@@ -897,14 +976,15 @@ static int dvb_register(struct cx23885_tsport *port)
fe0->dvb.frontend->callback = cx23885_tuner_callback;
/* Put the analog decoder in standby to keep it quiet */
- call_all(dev, tuner, s_standby);
+ call_all(dev, core, s_power, 0);
if (fe0->dvb.frontend->ops.analog_ops.standby)
fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
/* register everything */
ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
- &dev->pci->dev, adapter_nr, 0);
+ &dev->pci->dev, adapter_nr, 0,
+ cx23885_dvb_fe_ioctl_override);
/* init CI & MAC */
switch (dev->board) {
@@ -940,7 +1020,7 @@ int cx23885_dvb_register(struct cx23885_tsport *port)
int err, i;
/* Here we need to allocate the correct number of frontends,
- * as reflected in the cards struct. The reality is that currrently
+ * as reflected in the cards struct. The reality is that currently
* no cx23885 boards support this - yet. But, if we don't modify this
* code then the second frontend would never be allocated (later)
* and fail with error before the attach in dvb_register().
diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/video/cx23885/cx23885-f300.c
new file mode 100644
index 00000000000..93998f22098
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-f300.c
@@ -0,0 +1,177 @@
+/*
+ * Driver for Silicon Labs C8051F300 microcontroller.
+ *
+ * It is used for LNB power control in TeVii S470,
+ * TBS 6920 PCIe DVB-S2 cards.
+ *
+ * Microcontroller connected to cx23885 GPIO pins:
+ * GPIO0 - data - P0.3 F300
+ * GPIO1 - reset - P0.2 F300
+ * GPIO2 - clk - P0.1 F300
+ * GPIO3 - busy - P0.0 F300
+ *
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+
+#define F300_DATA GPIO_0
+#define F300_RESET GPIO_1
+#define F300_CLK GPIO_2
+#define F300_BUSY GPIO_3
+
+static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl)
+{
+ cx23885_gpio_enable(dev, line, 1);
+ if (lvl == 1)
+ cx23885_gpio_set(dev, line);
+ else
+ cx23885_gpio_clear(dev, line);
+}
+
+static u8 f300_get_line(struct cx23885_dev *dev, u32 line)
+{
+ cx23885_gpio_enable(dev, line, 0);
+
+ return cx23885_gpio_get(dev, line);
+}
+
+static void f300_send_byte(struct cx23885_dev *dev, u8 dta)
+{
+ u8 i;
+
+ for (i = 0; i < 8; i++) {
+ f300_set_line(dev, F300_CLK, 0);
+ udelay(30);
+ f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */
+ udelay(30);
+ dta <<= 1;
+ f300_set_line(dev, F300_CLK, 1);
+ udelay(30);
+ }
+}
+
+static u8 f300_get_byte(struct cx23885_dev *dev)
+{
+ u8 i, dta = 0;
+
+ for (i = 0; i < 8; i++) {
+ f300_set_line(dev, F300_CLK, 0);
+ udelay(30);
+ dta <<= 1;
+ f300_set_line(dev, F300_CLK, 1);
+ udelay(30);
+ dta |= f300_get_line(dev, F300_DATA);/* msb first */
+
+ }
+
+ return dta;
+}
+
+static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf)
+{
+ struct cx23885_tsport *port = fe->dvb->priv;
+ struct cx23885_dev *dev = port->dev;
+ u8 i, temp, ret = 0;
+
+ temp = buf[0];
+ for (i = 0; i < buf[0]; i++)
+ temp += buf[i + 1];
+ temp = (~temp + 1);/* get check sum */
+ buf[1 + buf[0]] = temp;
+
+ f300_set_line(dev, F300_RESET, 1);
+ f300_set_line(dev, F300_CLK, 1);
+ udelay(30);
+ f300_set_line(dev, F300_DATA, 1);
+ msleep(1);
+
+ /* question: */
+ f300_set_line(dev, F300_RESET, 0);/* begin to send data */
+ msleep(1);
+
+ f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */
+ msleep(1);
+
+ temp = buf[0];
+ temp += 2;
+ for (i = 0; i < temp; i++)
+ f300_send_byte(dev, buf[i]);
+
+ f300_set_line(dev, F300_RESET, 1);/* sent data over */
+ f300_set_line(dev, F300_DATA, 1);
+
+ /* answer: */
+ temp = 0;
+ for (i = 0; ((i < 8) & (temp == 0)); i++) {
+ msleep(1);
+ if (f300_get_line(dev, F300_BUSY) == 0)
+ temp = 1;
+ }
+
+ if (i > 7) {
+ printk(KERN_ERR "%s: timeout, the slave no response\n",
+ __func__);
+ ret = 1; /* timeout, the slave no response */
+ } else { /* the slave not busy, prepare for getting data */
+ f300_set_line(dev, F300_RESET, 0);/*ready...*/
+ msleep(1);
+ f300_send_byte(dev, 0xe1);/* 0xe1 is Read */
+ msleep(1);
+ temp = f300_get_byte(dev);/*get the data length */
+ if (temp > 14)
+ temp = 14;
+
+ for (i = 0; i < (temp + 1); i++)
+ f300_get_byte(dev);/* get data to empty buffer */
+
+ f300_set_line(dev, F300_RESET, 1);/* received data over */
+ f300_set_line(dev, F300_DATA, 1);
+ }
+
+ return ret;
+}
+
+int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ u8 buf[16];
+
+ buf[0] = 0x05;
+ buf[1] = 0x38;/* write port */
+ buf[2] = 0x01;/* A port, lnb power */
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ buf[3] = 0x01;/* power on */
+ buf[4] = 0x02;/* B port, H/V */
+ buf[5] = 0x00;/*13V v*/
+ break;
+ case SEC_VOLTAGE_18:
+ buf[3] = 0x01;
+ buf[4] = 0x02;
+ buf[5] = 0x01;/* 18V h*/
+ break;
+ case SEC_VOLTAGE_OFF:
+ buf[3] = 0x00;/* power off */
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ break;
+ }
+
+ return f300_xfer(fe, buf);
+}
diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/video/cx23885/cx23885-f300.h
new file mode 100644
index 00000000000..e73344c9496
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-f300.h
@@ -0,0 +1,2 @@
+extern int f300_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage);
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
new file mode 100644
index 00000000000..469e083dd5f
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-input.c
@@ -0,0 +1,427 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared remote control input device
+ *
+ * Most of this file is
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.net>
+ *
+ * However, the cx23885_input_{init,fini} functions contained herein are
+ * derived from Linux kernel files linux/media/video/.../...-input.c marked as:
+ *
+ * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ * Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ * Markus Rechberger <mrechberger@gmail.com>
+ * Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Sascha Sommer <saschasommer@freenet.de>
+ * Copyright (C) 2004, 2005 Chris Pascoe
+ * Copyright (C) 2003, 2004 Gerd Knorr
+ * Copyright (C) 2003 Pavel Machek
+ *
+ * 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/input.h>
+#include <media/ir-common.h>
+#include <media/v4l2-subdev.h>
+
+#include "cx23885.h"
+
+#define RC5_BITS 14
+#define RC5_HALF_BITS (2*RC5_BITS)
+#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1)
+
+#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */
+#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */
+
+#define RC5_EXTENDED_COMMAND_OFFSET 64
+
+static inline unsigned int rc5_command(u32 rc5_baseband)
+{
+ return RC5_INSTR(rc5_baseband) +
+ ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED)
+ ? RC5_EXTENDED_COMMAND_OFFSET : 0);
+}
+
+static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev)
+{
+ struct card_ir *ir_input = dev->ir_input;
+ unsigned int code, command;
+ u32 rc5;
+
+ /* Ignore codes that are too short to be valid RC-5 */
+ if (ir_input->last_bit < (RC5_HALF_BITS - 1))
+ return;
+
+ /* The library has the manchester coding backwards; XOR to adapt. */
+ code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK;
+ rc5 = ir_rc5_decode(code);
+
+ switch (RC5_START(rc5)) {
+ case RC5_START_BITS_NORMAL:
+ break;
+ case RC5_START_BITS_EXTENDED:
+ /* Don't allow if the remote only emits standard commands */
+ if (ir_input->start == RC5_START_BITS_NORMAL)
+ return;
+ break;
+ default:
+ return;
+ }
+
+ if (ir_input->addr != RC5_ADDR(rc5))
+ return;
+
+ /* Don't generate a keypress for RC-5 auto-repeated keypresses */
+ command = rc5_command(rc5);
+ if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) ||
+ command != rc5_command(ir_input->last_rc5) ||
+ /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */
+ RC5_START(ir_input->last_rc5) == 0) {
+ /* This keypress is differnet: not an auto repeat */
+ ir_input_nokey(ir_input->dev, &ir_input->ir);
+ ir_input_keydown(ir_input->dev, &ir_input->ir, command);
+ }
+ ir_input->last_rc5 = rc5;
+
+ /* Schedule when we should do the key up event: ir_input_nokey() */
+ mod_timer(&ir_input->timer_keyup,
+ jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout));
+}
+
+static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev,
+ u32 ns_pulse)
+{
+ const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */
+ struct card_ir *ir_input = dev->ir_input;
+ int i, level, quarterbits, halfbits;
+
+ if (!ir_input->active) {
+ ir_input->active = 1;
+ /* assume an initial space that we may not detect or measure */
+ ir_input->code = 0;
+ ir_input->last_bit = 0;
+ }
+
+ if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
+ ir_input->last_bit++; /* Account for the final space */
+ ir_input->active = 0;
+ cx23885_input_process_raw_rc5(dev);
+ return;
+ }
+
+ level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0;
+
+ /* Skip any leading space to sync to the start bit */
+ if (ir_input->last_bit == 0 && level == 0)
+ return;
+
+ /*
+ * With valid RC-5 we can get up to two consecutive half-bits in a
+ * single pulse measurment. Experiments have shown that the duration
+ * of a half-bit can vary. Make sure we always end up with an even
+ * number of quarter bits at the same level (mark or space).
+ */
+ ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ quarterbits = ns_pulse / rc5_quarterbit_ns;
+ if (quarterbits & 1)
+ quarterbits++;
+ halfbits = quarterbits / 2;
+
+ for (i = 0; i < halfbits; i++) {
+ ir_input->last_bit++;
+ ir_input->code |= (level << ir_input->last_bit);
+
+ if (ir_input->last_bit >= RC5_HALF_BITS-1) {
+ ir_input->active = 0;
+ cx23885_input_process_raw_rc5(dev);
+ /*
+ * If level is 1, a leading mark is invalid for RC5.
+ * If level is 0, we scan past extra intial space.
+ * Either way we don't want to reactivate collecting
+ * marks or spaces here with any left over half-bits.
+ */
+ break;
+ }
+ }
+}
+
+static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev,
+ bool add_eom)
+{
+ struct card_ir *ir_input = dev->ir_input;
+ struct ir_input_state *ir_input_state = &ir_input->ir;
+
+ u32 ns_pulse[RC5_HALF_BITS+1];
+ ssize_t num = 0;
+ int count, i;
+
+ do {
+ v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse,
+ sizeof(ns_pulse), &num);
+
+ count = num / sizeof(u32);
+
+ /* Append an end of Rx seq, if the caller requested */
+ if (add_eom && count < ARRAY_SIZE(ns_pulse)) {
+ ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+ count++;
+ }
+
+ /* Just drain the Rx FIFO, if we're called, but not RC-5 */
+ if (ir_input_state->ir_type != IR_TYPE_RC5)
+ continue;
+
+ for (i = 0; i < count; i++)
+ cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]);
+ } while (num != 0);
+}
+
+void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+{
+ struct v4l2_subdev_ir_parameters params;
+ int overrun, data_available;
+
+ if (dev->sd_ir == NULL || events == 0)
+ return;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ /*
+ * The only board we handle right now. However other boards
+ * using the CX2388x integrated IR controller should be similar
+ */
+ break;
+ default:
+ return;
+ }
+
+ overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
+ V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
+
+ data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
+ V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
+
+ if (overrun) {
+ /* If there was a FIFO overrun, stop the device */
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ params.enable = false;
+ /* Mitigate race with cx23885_input_ir_stop() */
+ params.shutdown = atomic_read(&dev->ir_input_stopping);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ }
+
+ if (data_available)
+ cx23885_input_process_pulse_widths_rc5(dev, overrun);
+
+ if (overrun) {
+ /* If there was a FIFO overrun, clear & restart the device */
+ params.enable = true;
+ /* Mitigate race with cx23885_input_ir_stop() */
+ params.shutdown = atomic_read(&dev->ir_input_stopping);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ }
+}
+
+static void cx23885_input_ir_start(struct cx23885_dev *dev)
+{
+ struct card_ir *ir_input = dev->ir_input;
+ struct ir_input_state *ir_input_state = &ir_input->ir;
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+ return;
+
+ atomic_set(&dev->ir_input_stopping, 0);
+
+ /* keyup timer set up, if needed */
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ setup_timer(&ir_input->timer_keyup,
+ ir_rc5_timer_keyup, /* Not actually RC-5 specific */
+ (unsigned long) ir_input);
+ if (ir_input_state->ir_type == IR_TYPE_RC5) {
+ /*
+ * RC-5 repeats a held key every
+ * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms
+ */
+ ir_input->rc5_key_timeout = 115;
+ }
+ break;
+ }
+
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for baseband compatible with both RC-5 and RC-6A */
+ params.modulation = false;
+ /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
+ /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
+ params.max_pulse_width = 3333333; /* ns */
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ params.noise_filter_min_width = 333333; /* ns */
+ /*
+ * This board has inverted receive sense:
+ * mark is received as low logic level;
+ * falling edges are detected as rising edges; etc.
+ */
+ params.invert = true;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+}
+
+static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+{
+ struct card_ir *ir_input = dev->ir_input;
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+ return;
+
+ /*
+ * Stop the sd_ir subdevice from generating notifications and
+ * scheduling work.
+ * It is shutdown this way in order to mitigate a race with
+ * cx23885_input_rx_work_handler() in the overrun case, which could
+ * re-enable the subdevice.
+ */
+ atomic_set(&dev->ir_input_stopping, 1);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ while (params.shutdown == false) {
+ params.enable = false;
+ params.interrupt_enable = false;
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ }
+
+ flush_scheduled_work();
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ del_timer_sync(&ir_input->timer_keyup);
+ break;
+ }
+}
+
+int cx23885_input_init(struct cx23885_dev *dev)
+{
+ struct card_ir *ir;
+ struct input_dev *input_dev;
+ struct ir_scancode_table *ir_codes = NULL;
+ int ir_type, ir_addr, ir_start;
+ int ret;
+
+ /*
+ * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
+ * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
+ */
+ if (dev->sd_ir == NULL)
+ return -ENODEV;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ /* Parameters for the grey Hauppauge remote for the HVR-1850 */
+ ir_codes = &ir_codes_hauppauge_new_table;
+ ir_type = IR_TYPE_RC5;
+ ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */
+ ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */
+ break;
+ }
+ if (ir_codes == NULL)
+ return -ENODEV;
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ir || !input_dev) {
+ ret = -ENOMEM;
+ goto err_out_free;
+ }
+
+ ir->dev = input_dev;
+ ir->addr = ir_addr;
+ ir->start = ir_start;
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)",
+ cx23885_boards[dev->board].name);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci));
+
+ ret = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ if (ret < 0)
+ goto err_out_free;
+
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+ input_dev->id.bustype = BUS_PCI;
+ input_dev->id.version = 1;
+ if (dev->pci->subsystem_vendor) {
+ input_dev->id.vendor = dev->pci->subsystem_vendor;
+ input_dev->id.product = dev->pci->subsystem_device;
+ } else {
+ input_dev->id.vendor = dev->pci->vendor;
+ input_dev->id.product = dev->pci->device;
+ }
+ input_dev->dev.parent = &dev->pci->dev;
+
+ dev->ir_input = ir;
+ cx23885_input_ir_start(dev);
+
+ ret = input_register_device(ir->dev);
+ if (ret)
+ goto err_out_stop;
+
+ return 0;
+
+err_out_stop:
+ cx23885_input_ir_stop(dev);
+ dev->ir_input = NULL;
+err_out_free:
+ ir_input_free(input_dev);
+ input_free_device(input_dev);
+ kfree(ir);
+ return ret;
+}
+
+void cx23885_input_fini(struct cx23885_dev *dev)
+{
+ /* Always stop the IR hardware from generating interrupts */
+ cx23885_input_ir_stop(dev);
+
+ if (dev->ir_input == NULL)
+ return;
+ ir_input_free(dev->ir_input->dev);
+ input_unregister_device(dev->ir_input->dev);
+ kfree(dev->ir_input);
+ dev->ir_input = NULL;
+}
diff --git a/drivers/media/video/cx23885/cx23885-input.h b/drivers/media/video/cx23885/cx23885-input.h
new file mode 100644
index 00000000000..3572cb1ecfc
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-input.h
@@ -0,0 +1,30 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared remote control input device
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.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.
+ *
+ * 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.
+ */
+
+#ifndef _CX23885_INPUT_H_
+#define _CX23885_INPUT_H_
+int 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);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.c b/drivers/media/video/cx23885/cx23885-ioctl.c
new file mode 100644
index 00000000000..dfb4627fb34
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ioctl.c
@@ -0,0 +1,208 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Various common ioctl() support functions
+ *
+ * Copyright (c) 2009 Andy Walls <awalls@radix.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+#include <media/v4l2-chip-ident.h>
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+ int err = 0;
+ u8 rev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ switch (chip->match.addr) {
+ case 0:
+ rev = cx_read(RDR_CFG2) & 0xff;
+ switch (dev->pci->device) {
+ case 0x8852:
+ /* rev 0x04 could be '885 or '888. Pick '888. */
+ if (rev == 0x04)
+ chip->ident = V4L2_IDENT_CX23888;
+ else
+ chip->ident = V4L2_IDENT_CX23885;
+ break;
+ case 0x8880:
+ if (rev == 0x0e || rev == 0x0f)
+ chip->ident = V4L2_IDENT_CX23887;
+ else
+ chip->ident = V4L2_IDENT_CX23888;
+ break;
+ default:
+ chip->ident = V4L2_IDENT_UNKNOWN;
+ break;
+ }
+ chip->revision = (dev->pci->device << 16) | (rev << 8) |
+ (dev->hwrevision & 0xff);
+ break;
+ case 1:
+ if (dev->v4l_device != NULL) {
+ chip->ident = V4L2_IDENT_CX23417;
+ chip->revision = 0;
+ }
+ break;
+ case 2:
+ /*
+ * The integrated IR controller on the CX23888 is
+ * host chip 2. It may not be used/initialized or sd_ir
+ * may be pointing at the cx25840 subdevice for the
+ * IR controller on the CX23885. Thus we find it
+ * without using the dev->sd_ir pointer.
+ */
+ call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident,
+ chip);
+ break;
+ default:
+ err = -EINVAL; /* per V4L2 spec */
+ break;
+ }
+ break;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+ call_all(dev, core, g_chip_ident, chip);
+ break;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /*
+ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+ * to look if a chip is at the address with no driver. That's a
+ * dangerous thing to do with EEPROMs anyway.
+ */
+ call_all(dev, core, g_chip_ident, chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23885_g_host_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+ return -EINVAL;
+
+ reg->size = 4;
+ reg->val = cx_read(reg->reg);
+ return 0;
+}
+
+static int cx23417_g_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ u32 value;
+
+ if (dev->v4l_device == NULL)
+ return -EINVAL;
+
+ if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+ return -EINVAL;
+
+ if (mc417_register_read(dev, (u16) reg->reg, &value))
+ return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+ reg->size = 4;
+ reg->val = value;
+ return 0;
+}
+
+int cx23885_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+ switch (reg->match.addr) {
+ case 0:
+ return cx23885_g_host_register(dev, reg);
+ case 1:
+ return cx23417_g_register(dev, reg);
+ default:
+ break;
+ }
+ }
+
+ /* FIXME - any error returns should not be ignored */
+ call_all(dev, core, g_register, reg);
+ return 0;
+}
+
+static int cx23885_s_host_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+ return -EINVAL;
+
+ reg->size = 4;
+ cx_write(reg->reg, reg->val);
+ return 0;
+}
+
+static int cx23417_s_register(struct cx23885_dev *dev,
+ struct v4l2_dbg_register *reg)
+{
+ if (dev->v4l_device == NULL)
+ return -EINVAL;
+
+ if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+ return -EINVAL;
+
+ if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val))
+ return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+ reg->size = 4;
+ return 0;
+}
+
+int cx23885_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+ switch (reg->match.addr) {
+ case 0:
+ return cx23885_s_host_register(dev, reg);
+ case 1:
+ return cx23417_s_register(dev, reg);
+ default:
+ break;
+ }
+ }
+
+ /* FIXME - any error returns should not be ignored */
+ call_all(dev, core, s_register, reg);
+ return 0;
+}
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.h b/drivers/media/video/cx23885/cx23885-ioctl.h
new file mode 100644
index 00000000000..80b0f4923c6
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ioctl.h
@@ -0,0 +1,39 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Various common ioctl() support functions
+ *
+ * Copyright (c) 2009 Andy Walls <awalls@radix.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX23885_IOCTL_H_
+#define _CX23885_IOCTL_H_
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx23885_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+
+
+int cx23885_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+
+#endif
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c
new file mode 100644
index 00000000000..6ae982cc985
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ir.c
@@ -0,0 +1,101 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.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.
+ *
+ * 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 <media/v4l2-device.h>
+
+#include "cx23885.h"
+#include "cx23885-input.h"
+
+#define CX23885_IR_RX_FIFO_SERVICE_REQ 0
+#define CX23885_IR_RX_END_OF_RX_DETECTED 1
+#define CX23885_IR_RX_HW_FIFO_OVERRUN 2
+#define CX23885_IR_RX_SW_FIFO_OVERRUN 3
+
+#define CX23885_IR_TX_FIFO_SERVICE_REQ 0
+
+
+void cx23885_ir_rx_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, ir_rx_work);
+ u32 events = 0;
+ unsigned long *notifications = &dev->ir_rx_notifications;
+
+ if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications))
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications))
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications))
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications))
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+
+ if (events == 0)
+ return;
+
+ if (dev->ir_input)
+ cx23885_input_rx_work_handler(dev, events);
+}
+
+void cx23885_ir_tx_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, ir_tx_work);
+ u32 events = 0;
+ unsigned long *notifications = &dev->ir_tx_notifications;
+
+ if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications))
+ events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+
+ if (events == 0)
+ return;
+
+}
+
+/* Called in an IRQ context */
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+ struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+ unsigned long *notifications = &dev->ir_rx_notifications;
+
+ if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ)
+ set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications);
+ if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED)
+ set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications);
+ if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN)
+ set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
+ if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
+ set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
+ schedule_work(&dev->ir_rx_work);
+}
+
+/* Called in an IRQ context */
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+ struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+ unsigned long *notifications = &dev->ir_tx_notifications;
+
+ if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
+ set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
+ schedule_work(&dev->ir_tx_work);
+}
diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/video/cx23885/cx23885-ir.h
new file mode 100644
index 00000000000..9b8a6d5d1ef
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23885-ir.h
@@ -0,0 +1,31 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.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.
+ *
+ * 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.
+ */
+
+#ifndef _CX23885_IR_H_
+#define _CX23885_IR_H_
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+
+void cx23885_ir_rx_work_handler(struct work_struct *work);
+void cx23885_ir_tx_work_handler(struct work_struct *work);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index eafbe5226ba..c0bc9a06895 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -212,8 +212,9 @@ Channel manager Data Structure entry = 20 DWORD
#define DEV_CNTRL2 0x00040000
-#define PCI_MSK_GPIO1 (1 << 24)
-#define PCI_MSK_GPIO0 (1 << 23)
+#define PCI_MSK_IR (1 << 28)
+#define PCI_MSK_GPIO1 (1 << 24)
+#define PCI_MSK_GPIO0 (1 << 23)
#define PCI_MSK_APB_DMA (1 << 12)
#define PCI_MSK_AL_WR (1 << 11)
#define PCI_MSK_AL_RD (1 << 10)
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 654cc253cd5..8b372b4f0de 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -35,6 +35,7 @@
#include "cx23885.h"
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include "cx23885-ioctl.h"
MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -401,6 +402,13 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
INPUT(input)->gpio2, INPUT(input)->gpio3);
dev->input = input;
+ if (dev->board == CX23885_BOARD_MYGICA_X8506 ||
+ dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2) {
+ /* Select Analog TV */
+ if (INPUT(input)->type == CX23885_VMUX_TELEVISION)
+ cx23885_gpio_clear(dev, GPIO_0);
+ }
+
/* Tell the internal A/V decoder */
v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
INPUT(input)->vmux, 0, 0);
@@ -1144,6 +1152,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
[CX23885_VMUX_COMPOSITE3] = "Composite3",
[CX23885_VMUX_COMPOSITE4] = "Composite4",
[CX23885_VMUX_SVIDEO] = "S-Video",
+ [CX23885_VMUX_COMPONENT] = "Component",
[CX23885_VMUX_TELEVISION] = "Television",
[CX23885_VMUX_CABLE] = "Cable TV",
[CX23885_VMUX_DVB] = "DVB",
@@ -1312,34 +1321,6 @@ static int vidioc_s_frequency(struct file *file, void *priv,
cx23885_set_freq(dev, f);
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
-
- call_all(dev, core, g_register, reg);
-
- return 0;
-}
-
-static int vidioc_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
-
- call_all(dev, core, s_register, reg);
-
- return 0;
-}
-#endif
-
/* ----------------------------------------------------------- */
static void cx23885_vid_timeout(unsigned long data)
@@ -1449,9 +1430,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_chip_ident = cx23885_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
+ .vidioc_g_register = cx23885_g_register,
+ .vidioc_s_register = cx23885_s_register,
#endif
};
@@ -1529,9 +1511,11 @@ int cx23885_video_register(struct cx23885_dev *dev)
if (sd) {
struct tuner_setup tun_setup;
+ memset(&tun_setup, 0, sizeof(tun_setup));
tun_setup.mode_mask = T_ANALOG_TV;
tun_setup.type = dev->tuner_type;
tun_setup.addr = v4l2_i2c_subdev_addr(sd);
+ tun_setup.tuner_callback = cx23885_tuner_callback;
v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
}
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index cc7a165561f..fa744764dc8 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -79,6 +79,8 @@
#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23
#define CX23885_BOARD_HAUPPAUGE_HVR1850 24
#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25
+#define CX23885_BOARD_HAUPPAUGE_HVR1290 26
+#define CX23885_BOARD_MYGICA_X8558PRO 27
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -157,6 +159,7 @@ enum cx23885_itype {
CX23885_VMUX_COMPOSITE3,
CX23885_VMUX_COMPOSITE4,
CX23885_VMUX_SVIDEO,
+ CX23885_VMUX_COMPONENT,
CX23885_VMUX_TELEVISION,
CX23885_VMUX_CABLE,
CX23885_VMUX_DVB,
@@ -297,10 +300,6 @@ struct cx23885_tsport {
/* Allow a single tsport to have multiple frontends */
u32 num_frontends;
void *port_priv;
-
- /* FIXME: temporary hack */
- int (*set_frontend_save) (struct dvb_frontend *,
- struct dvb_frontend_parameters *);
};
struct cx23885_dev {
@@ -356,6 +355,16 @@ struct cx23885_dev {
unsigned int has_radio;
struct v4l2_subdev *sd_cx25840;
+ /* Infrared */
+ struct v4l2_subdev *sd_ir;
+ struct work_struct ir_rx_work;
+ unsigned long ir_rx_notifications;
+ struct work_struct ir_tx_work;
+ unsigned long ir_tx_notifications;
+
+ struct card_ir *ir_input;
+ atomic_t ir_input_stopping;
+
/* V4l */
u32 freq;
struct video_device *video_dev;
@@ -383,6 +392,13 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
#define call_all(dev, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+#define CX23885_HW_888_IR (1 << 0)
+
+#define call_hw(dev, grpid, o, f, args...) \
+ v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
+
+extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw);
+
extern struct list_head cx23885_devlist;
#define SRAM_CH01 0 /* Video A */
@@ -455,6 +471,7 @@ extern void cx23885_wakeup(struct cx23885_tsport *port,
extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask);
extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
int asoutput);
@@ -471,6 +488,8 @@ extern int cx23885_tuner_callback(void *priv, int component,
int command, int arg);
extern void cx23885_card_list(struct cx23885_dev *dev);
extern int cx23885_ir_init(struct cx23885_dev *dev);
+extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev);
+extern void cx23885_ir_fini(struct cx23885_dev *dev);
extern void cx23885_gpio_setup(struct cx23885_dev *dev);
extern void cx23885_card_setup(struct cx23885_dev *dev);
extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
@@ -515,6 +534,10 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev);
extern void cx23885_mc417_init(struct cx23885_dev *dev);
extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value);
extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value);
+extern int mc417_register_read(struct cx23885_dev *dev,
+ u16 address, u32 *value);
+extern int mc417_register_write(struct cx23885_dev *dev,
+ u16 address, u32 value);
extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
new file mode 100644
index 00000000000..3ccc8afeccf
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -0,0 +1,1239 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * CX23888 Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.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.
+ *
+ * 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/kfifo.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "cx23885.h"
+
+static unsigned int ir_888_debug;
+module_param(ir_888_debug, int, 0644);
+MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
+
+#define CX23888_IR_REG_BASE 0x170000
+/*
+ * These CX23888 register offsets have a straightforward one to one mapping
+ * to the CX23885 register offsets of 0x200 through 0x218
+ */
+#define CX23888_IR_CNTRL_REG 0x170000
+#define CNTRL_WIN_3_3 0x00000000
+#define CNTRL_WIN_4_3 0x00000001
+#define CNTRL_WIN_3_4 0x00000002
+#define CNTRL_WIN_4_4 0x00000003
+#define CNTRL_WIN 0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG 0x0000000C
+#define CNTRL_DMD 0x00000010
+#define CNTRL_MOD 0x00000020
+#define CNTRL_RFE 0x00000040
+#define CNTRL_TFE 0x00000080
+#define CNTRL_RXE 0x00000100
+#define CNTRL_TXE 0x00000200
+#define CNTRL_RIC 0x00000400
+#define CNTRL_TIC 0x00000800
+#define CNTRL_CPL 0x00001000
+#define CNTRL_LBM 0x00002000
+#define CNTRL_R 0x00004000
+
+#define CX23888_IR_TXCLK_REG 0x170004
+#define TXCLK_TCD 0x0000FFFF
+
+#define CX23888_IR_RXCLK_REG 0x170008
+#define RXCLK_RCD 0x0000FFFF
+
+#define CX23888_IR_CDUTY_REG 0x17000C
+#define CDUTY_CDC 0x0000000F
+
+#define CX23888_IR_STATS_REG 0x170010
+#define STATS_RTO 0x00000001
+#define STATS_ROR 0x00000002
+#define STATS_RBY 0x00000004
+#define STATS_TBY 0x00000008
+#define STATS_RSR 0x00000010
+#define STATS_TSR 0x00000020
+
+#define CX23888_IR_IRQEN_REG 0x170014
+#define IRQEN_RTE 0x00000001
+#define IRQEN_ROE 0x00000002
+#define IRQEN_RSE 0x00000010
+#define IRQEN_TSE 0x00000020
+
+#define CX23888_IR_FILTR_REG 0x170018
+#define FILTR_LPF 0x0000FFFF
+
+/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */
+#define CX23888_IR_FIFO_REG 0x170040
+#define FIFO_RXTX 0x0000FFFF
+#define FIFO_RXTX_LVL 0x00010000
+#define FIFO_RXTX_RTO 0x0001FFFF
+#define FIFO_RX_NDV 0x00020000
+#define FIFO_RX_DEPTH 8
+#define FIFO_TX_DEPTH 8
+
+/* CX23888 unique registers */
+#define CX23888_IR_SEEDP_REG 0x17001C
+#define CX23888_IR_TIMOL_REG 0x170020
+#define CX23888_IR_WAKE0_REG 0x170024
+#define CX23888_IR_WAKE1_REG 0x170028
+#define CX23888_IR_WAKE2_REG 0x17002C
+#define CX23888_IR_MASK0_REG 0x170030
+#define CX23888_IR_MASK1_REG 0x170034
+#define CX23888_IR_MAKS2_REG 0x170038
+#define CX23888_IR_DPIPG_REG 0x17003C
+#define CX23888_IR_LEARN_REG 0x170044
+
+#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
+#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2)
+
+#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32))
+#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32))
+
+struct cx23888_ir_state {
+ struct v4l2_subdev sd;
+ struct cx23885_dev *dev;
+ u32 id;
+ u32 rev;
+
+ struct v4l2_subdev_ir_parameters rx_params;
+ struct mutex rx_params_lock;
+ atomic_t rxclk_divider;
+ atomic_t rx_invert;
+
+ struct kfifo *rx_kfifo;
+ spinlock_t rx_kfifo_lock;
+
+ struct v4l2_subdev_ir_parameters tx_params;
+ struct mutex tx_params_lock;
+ atomic_t txclk_divider;
+
+ struct kfifo *tx_kfifo;
+ spinlock_t tx_kfifo_lock;
+};
+
+static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd)
+{
+ return v4l2_get_subdevdata(sd);
+}
+
+/*
+ * IR register block read and write functions
+ */
+static
+inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value)
+{
+ cx_write(addr, value);
+ return 0;
+}
+
+static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr)
+{
+ return cx_read(addr);
+}
+
+static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr,
+ u32 and_mask, u32 or_value)
+{
+ cx_andor(addr, ~and_mask, or_value);
+ return 0;
+}
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+ if (d > RXCLK_RCD + 1)
+ d = RXCLK_RCD;
+ else if (d < 2)
+ d = 1;
+ else
+ d--;
+ return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+ /* Period of the Rx or Tx clock in ns */
+ return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+ CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+ return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+ unsigned int rollovers)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+ unsigned int rollovers)
+{
+ return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ,
+ (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+ if (d > FILTR_LPF)
+ d = FILTR_LPF;
+ else if (d < 4)
+ d = 0;
+ return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+ return count_to_lpf_count(
+ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in ns */
+ return DIV_ROUND_CLOSEST(count * 1000,
+ CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in us */
+ return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+ /*
+ * Resolution is the duration of 1 tick of the readable portion of
+ * of the pulse width counter as read from the FIFO. The two lsb's are
+ * not readable, hence the << 2. This function returns ns.
+ */
+ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000,
+ CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+ rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */
+ if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return n;
+}
+
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */
+ rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+ if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+ u64 clocks;
+ u32 rem;
+ clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */
+ rem = do_div(clocks, 1000); /* /1000 = cycles */
+ if (rem >= 1000 / 2)
+ clocks++;
+ return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+ u32 rem;
+
+ rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+ /* net result needs to be rounded down and decremented by 1 */
+ if (count > RXCLK_RCD + 1)
+ count = RXCLK_RCD;
+ else if (count < 2)
+ count = 1;
+ else
+ count--;
+ return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+ TX_FIFO_HALF_EMPTY = 0,
+ TX_FIFO_EMPTY = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+ RX_FIFO_HALF_FULL = 0,
+ RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct cx23885_dev *dev,
+ enum tx_fifo_watermark level)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct cx23885_dev *dev,
+ enum rx_fifo_watermark level)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct cx23885_dev *dev, bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+ enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct cx23885_dev *dev, bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+ enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct cx23885_dev *dev,
+ bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD,
+ enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct cx23885_dev *dev,
+ bool enable)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD,
+ enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct cx23885_dev *dev,
+ u32 edge_types)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+ edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct cx23885_dev *dev,
+ unsigned int carrier,
+ unsigned int *carrier_range_low,
+ unsigned int *carrier_range_high)
+{
+ u32 v;
+ unsigned int c16 = carrier * 16;
+
+ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+ v = CNTRL_WIN_3_4;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+ } else {
+ v = CNTRL_WIN_3_3;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+ }
+
+ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+ v |= CNTRL_WIN_4_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+ } else {
+ v |= CNTRL_WIN_3_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+ }
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
+ bool invert)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL,
+ invert ? CNTRL_CPL : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+ ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+ ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev,
+ unsigned int duty_cycle)
+{
+ u32 n;
+ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+ if (n != 0)
+ n--;
+ if (n > 15)
+ n = 15;
+ cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n);
+ return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns)
+{
+ u32 count = ns_to_lpf_count(min_width_ns);
+ cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count);
+ return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask)
+{
+ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+ cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG,
+ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask)
+{
+ mask &= IRQEN_TSE;
+ cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+
+ u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+ u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+ u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+
+ u32 rx_data[FIFO_RX_DEPTH];
+ int i, j, k;
+ u32 events, v;
+ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+
+ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+ ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+ *handled = false;
+ v4l2_dbg(2, ir_888_debug, sd, "IRQ Status: %s %s %s %s %s %s\n",
+ tsr ? "tsr" : " ", rsr ? "rsr" : " ",
+ rto ? "rto" : " ", ror ? "ror" : " ",
+ stats & STATS_TBY ? "tby" : " ",
+ stats & STATS_RBY ? "rby" : " ");
+
+ v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n",
+ tse ? "tse" : " ", rse ? "rse" : " ",
+ rte ? "rte" : " ", roe ? "roe" : " ");
+
+ /*
+ * Transmitter interrupt service
+ */
+ if (tse && tsr) {
+ /*
+ * TODO:
+ * Check the watermark threshold setting
+ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+ * Push the data to the hardware FIFO.
+ * If there was nothing more to send in the tx_kfifo, disable
+ * the TSR IRQ and notify the v4l2_device.
+ * If there was something in the tx_kfifo, check the tx_kfifo
+ * level and notify the v4l2_device, if it is low.
+ */
+ /* For now, inhibit TSR interrupt until Tx is implemented */
+ irqenable_tx(dev, 0);
+ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+ *handled = true;
+ }
+
+ /*
+ * Receiver interrupt service
+ */
+ kror = 0;
+ if ((rse && rsr) || (rte && rto)) {
+ /*
+ * Receive data on RSR to clear the STATS_RSR.
+ * Receive data on RTO, since we may not have yet hit the RSR
+ * watermark when we receive the RTO.
+ */
+ for (i = 0, v = FIFO_RX_NDV;
+ (v & FIFO_RX_NDV) && !kror; i = 0) {
+ for (j = 0;
+ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+ v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
+ rx_data[i++] = v & ~FIFO_RX_NDV;
+ }
+ if (i == 0)
+ break;
+ j = i * sizeof(u32);
+ k = kfifo_put(state->rx_kfifo,
+ (unsigned char *) rx_data, j);
+ if (k != j)
+ kror++; /* rx_kfifo over run */
+ }
+ *handled = true;
+ }
+
+ events = 0;
+ v = 0;
+ if (kror) {
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver software FIFO overrun\n");
+ }
+ if (roe && ror) {
+ /*
+ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+ * the Rx FIFO Over Run status (STATS_ROR)
+ */
+ v |= CNTRL_RFE;
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+ }
+ if (rte && rto) {
+ /*
+ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+ * the Rx Pulse Width Timer Time Out (STATS_RTO)
+ */
+ v |= CNTRL_RXE;
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ }
+ if (v) {
+ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v);
+ cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl);
+ *handled = true;
+ }
+ if (kfifo_len(state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2)
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+
+ if (events)
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+ return 0;
+}
+
+/* Receiver */
+static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ bool invert = (bool) atomic_read(&state->rx_invert);
+ u16 divider = (u16) atomic_read(&state->rxclk_divider);
+
+ unsigned int i, n;
+ u32 *p;
+ u32 u, v;
+
+ n = count / sizeof(u32) * sizeof(u32);
+ if (n == 0) {
+ *num = 0;
+ return 0;
+ }
+
+ n = kfifo_get(state->rx_kfifo, buf, n);
+
+ n /= sizeof(u32);
+ *num = n * sizeof(u32);
+
+ for (p = (u32 *) buf, i = 0; i < n; p++, i++) {
+ if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
+ continue;
+ }
+
+ u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0;
+ if (invert)
+ u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
+
+ v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX),
+ divider);
+ if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+ v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1;
+
+ *p = u | v;
+
+ v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n",
+ v, u ? "mark" : "space");
+ }
+ return 0;
+}
+
+static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ mutex_lock(&state->rx_params_lock);
+ memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&state->rx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+
+ mutex_lock(&state->rx_params_lock);
+
+ /* Disable or slow down all IR Rx circuits and counters */
+ irqenable_rx(dev, 0);
+ control_rx_enable(dev, false);
+ control_rx_demodulation_enable(dev, false);
+ control_rx_s_edge_detection(dev, CNTRL_EDG_NONE);
+ filter_rx_s_min_width(dev, 0);
+ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD);
+
+ state->rx_params.shutdown = true;
+
+ mutex_unlock(&state->rx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ struct v4l2_subdev_ir_parameters *o = &state->rx_params;
+ u16 rxclk_divider;
+
+ if (p->shutdown)
+ return cx23888_ir_rx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ mutex_lock(&state->rx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+ o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+
+ /* Before we tweak the hardware, we have to disable the receiver */
+ irqenable_rx(dev, 0);
+ control_rx_enable(dev, false);
+
+ control_rx_demodulation_enable(dev, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq,
+ &rxclk_divider);
+
+ o->carrier_freq = p->carrier_freq;
+
+ o->duty_cycle = p->duty_cycle = 50;
+
+ control_rx_s_carrier_window(dev, p->carrier_freq,
+ &p->carrier_range_lower,
+ &p->carrier_range_upper);
+ o->carrier_range_lower = p->carrier_range_lower;
+ o->carrier_range_upper = p->carrier_range_upper;
+ } else {
+ p->max_pulse_width =
+ rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
+ &rxclk_divider);
+ o->max_pulse_width = p->max_pulse_width;
+ }
+ atomic_set(&state->rxclk_divider, rxclk_divider);
+
+ p->noise_filter_min_width =
+ filter_rx_s_min_width(dev, p->noise_filter_min_width);
+ o->noise_filter_min_width = p->noise_filter_min_width;
+
+ p->resolution = clock_divider_to_resolution(rxclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL);
+
+ control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
+
+ o->invert = p->invert;
+ atomic_set(&state->rx_invert, p->invert);
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ kfifo_reset(state->rx_kfifo);
+ if (p->interrupt_enable)
+ irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+ control_rx_enable(dev, p->enable);
+ }
+
+ mutex_unlock(&state->rx_params_lock);
+ return 0;
+}
+
+/* Transmitter */
+static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+ irqenable_tx(dev, IRQEN_TSE);
+ *num = count;
+ return 0;
+}
+
+static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ mutex_lock(&state->tx_params_lock);
+ memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&state->tx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+
+ mutex_lock(&state->tx_params_lock);
+
+ /* Disable or slow down all IR Tx circuits and counters */
+ irqenable_tx(dev, 0);
+ control_tx_enable(dev, false);
+ control_tx_modulation_enable(dev, false);
+ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD);
+
+ state->tx_params.shutdown = true;
+
+ mutex_unlock(&state->tx_params_lock);
+ return 0;
+}
+
+static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ struct v4l2_subdev_ir_parameters *o = &state->tx_params;
+ u16 txclk_divider;
+
+ if (p->shutdown)
+ return cx23888_ir_tx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ mutex_lock(&state->tx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+ o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+
+ /* Before we tweak the hardware, we have to disable the transmitter */
+ irqenable_tx(dev, 0);
+ control_tx_enable(dev, false);
+
+ control_tx_modulation_enable(dev, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq,
+ &txclk_divider);
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
+ o->duty_cycle = p->duty_cycle;
+ } else {
+ p->max_pulse_width =
+ txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
+ &txclk_divider);
+ o->max_pulse_width = p->max_pulse_width;
+ }
+ atomic_set(&state->txclk_divider, txclk_divider);
+
+ p->resolution = clock_divider_to_resolution(txclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
+
+ control_tx_polarity_invert(dev, p->invert);
+ o->invert = p->invert;
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ kfifo_reset(state->tx_kfifo);
+ if (p->interrupt_enable)
+ irqenable_tx(dev, IRQEN_TSE);
+ control_tx_enable(dev, p->enable);
+ }
+
+ mutex_unlock(&state->tx_params_lock);
+ return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops
+ */
+static int cx23888_ir_log_status(struct v4l2_subdev *sd)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ struct cx23885_dev *dev = state->dev;
+ char *s;
+ int i, j;
+
+ u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+ u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD;
+ u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD;
+ u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC;
+ u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+ u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+ u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF;
+
+ v4l2_info(sd, "IR Receiver:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_RXE ? "yes" : "no");
+ v4l2_info(sd, "\tDemodulation from a carrier: %s\n",
+ cntrl & CNTRL_DMD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_RFE ? "enabled" : "disabled");
+ switch (cntrl & CNTRL_EDG) {
+ case CNTRL_EDG_NONE:
+ s = "disabled";
+ break;
+ case CNTRL_EDG_FALL:
+ s = "falling edge";
+ break;
+ case CNTRL_EDG_RISE:
+ s = "rising edge";
+ break;
+ case CNTRL_EDG_BOTH:
+ s = "rising & falling edges";
+ break;
+ default:
+ s = "??? edge";
+ break;
+ }
+ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s);
+ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+ cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+ v4l2_info(sd, "\tLoopback mode: %s\n",
+ cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+ if (cntrl & CNTRL_DMD) {
+ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(rxclk));
+ switch (cntrl & CNTRL_WIN) {
+ case CNTRL_WIN_3_3:
+ i = 3;
+ j = 3;
+ break;
+ case CNTRL_WIN_4_3:
+ i = 4;
+ j = 3;
+ break;
+ case CNTRL_WIN_3_4:
+ i = 3;
+ j = 4;
+ break;
+ case CNTRL_WIN_4_4:
+ i = 4;
+ j = 4;
+ break;
+ default:
+ i = 0;
+ j = 0;
+ break;
+ }
+ v4l2_info(sd, "\tNext carrier edge window: 16 clocks "
+ "-%1d/+%1d, %u to %u Hz\n", i, j,
+ clock_divider_to_freq(rxclk, 16 + j),
+ clock_divider_to_freq(rxclk, 16 - i));
+ } else {
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, "
+ "%llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+ }
+ v4l2_info(sd, "\tLow pass filter: %s\n",
+ filtr ? "enabled" : "disabled");
+ if (filtr)
+ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, "
+ "%u ns\n",
+ lpf_count_to_us(filtr),
+ lpf_count_to_ns(filtr));
+ v4l2_info(sd, "\tPulse width timer timed-out: %s\n",
+ stats & STATS_RTO ? "yes" : "no");
+ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n",
+ irqen & IRQEN_RTE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO overrun: %s\n",
+ stats & STATS_ROR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n",
+ irqen & IRQEN_ROE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_RBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_RSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+ v4l2_info(sd, "IR Transmitter:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_TXE ? "yes" : "no");
+ v4l2_info(sd, "\tModulation onto a carrier: %s\n",
+ cntrl & CNTRL_MOD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_TFE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+ v4l2_info(sd, "\tSignal polarity: %s\n",
+ cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark");
+ if (cntrl & CNTRL_MOD) {
+ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(txclk));
+ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
+ cduty + 1);
+ } else {
+ v4l2_info(sd, "\tMax pulse width: %u us, "
+ "%llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
+ }
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_TBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_TSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+ return 0;
+}
+
+static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match)
+{
+ return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2;
+}
+
+static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+
+ if (cx23888_ir_dbg_match(&chip->match)) {
+ chip->ident = state->id;
+ chip->revision = state->rev;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23888_ir_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+ if (!cx23888_ir_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((addr & 0x3) != 0)
+ return -EINVAL;
+ if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 4;
+ reg->val = cx23888_ir_read4(state->dev, addr);
+ return 0;
+}
+
+static int cx23888_ir_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx23888_ir_state *state = to_state(sd);
+ u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+ if (!cx23888_ir_dbg_match(&reg->match))
+ return -EINVAL;
+ if ((addr & 0x3) != 0)
+ return -EINVAL;
+ if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx23888_ir_write4(state->dev, addr, reg->val);
+ return 0;
+}
+#endif
+
+static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
+ .g_chip_ident = cx23888_ir_g_chip_ident,
+ .log_status = cx23888_ir_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx23888_ir_g_register,
+ .s_register = cx23888_ir_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
+ .interrupt_service_routine = cx23888_ir_irq_handler,
+
+ .rx_read = cx23888_ir_rx_read,
+ .rx_g_parameters = cx23888_ir_rx_g_parameters,
+ .rx_s_parameters = cx23888_ir_rx_s_parameters,
+
+ .tx_write = cx23888_ir_tx_write,
+ .tx_g_parameters = cx23888_ir_tx_g_parameters,
+ .tx_s_parameters = cx23888_ir_tx_s_parameters,
+};
+
+static const struct v4l2_subdev_ops cx23888_ir_controller_ops = {
+ .core = &cx23888_ir_core_ops,
+ .ir = &cx23888_ir_ir_ops,
+};
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+ .bytes_per_data_element = sizeof(u32),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */
+
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ .noise_filter_min_width = 333333, /* ns */
+ .carrier_range_lower = 35000,
+ .carrier_range_upper = 37000,
+ .invert = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+ .bytes_per_data_element = sizeof(u32),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+ .duty_cycle = 25, /* 25 % - RC-5 carrier */
+ .invert = false,
+};
+
+int cx23888_ir_probe(struct cx23885_dev *dev)
+{
+ struct cx23888_ir_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_subdev_ir_parameters default_params;
+ int ret;
+
+ state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&state->rx_kfifo_lock);
+ state->rx_kfifo = kfifo_alloc(CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL,
+ &state->rx_kfifo_lock);
+ if (state->rx_kfifo == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&state->tx_kfifo_lock);
+ state->tx_kfifo = kfifo_alloc(CX23888_IR_TX_KFIFO_SIZE, GFP_KERNEL,
+ &state->tx_kfifo_lock);
+ if (state->tx_kfifo == NULL) {
+ kfifo_free(state->rx_kfifo);
+ return -ENOMEM;
+ }
+
+ state->dev = dev;
+ state->id = V4L2_IDENT_CX23888_IR;
+ state->rev = 0;
+ sd = &state->sd;
+
+ v4l2_subdev_init(sd, &cx23888_ir_controller_ops);
+ v4l2_set_subdevdata(sd, state);
+ /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */
+ snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name);
+ sd->grp_id = CX23885_HW_888_IR;
+
+ ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd);
+ if (ret == 0) {
+ /*
+ * Ensure no interrupts arrive from '888 specific conditions,
+ * since we ignore them in this driver to have commonality with
+ * similar IR controller cores.
+ */
+ cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0);
+
+ mutex_init(&state->rx_params_lock);
+ memcpy(&default_params, &default_rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+ mutex_init(&state->tx_params_lock);
+ memcpy(&default_params, &default_tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+ } else {
+ kfifo_free(state->rx_kfifo);
+ kfifo_free(state->tx_kfifo);
+ }
+ return ret;
+}
+
+int cx23888_ir_remove(struct cx23885_dev *dev)
+{
+ struct v4l2_subdev *sd;
+ struct cx23888_ir_state *state;
+
+ sd = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ if (sd == NULL)
+ return -ENODEV;
+
+ cx23888_ir_rx_shutdown(sd);
+ cx23888_ir_tx_shutdown(sd);
+
+ state = to_state(sd);
+ v4l2_device_unregister_subdev(sd);
+ kfifo_free(state->rx_kfifo);
+ kfifo_free(state->tx_kfifo);
+ kfree(state);
+ /* Nothing more to free() as state held the actual v4l2_subdev object */
+ return 0;
+}
diff --git a/drivers/media/video/cx23885/cx23888-ir.h b/drivers/media/video/cx23885/cx23888-ir.h
new file mode 100644
index 00000000000..3d446f9eb94
--- /dev/null
+++ b/drivers/media/video/cx23885/cx23888-ir.h
@@ -0,0 +1,28 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * CX23888 Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@radix.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.
+ *
+ * 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.
+ */
+
+#ifndef _CX23888_IR_H_
+#define _CX23888_IR_H_
+int cx23888_ir_probe(struct cx23885_dev *dev);
+int cx23888_ir_remove(struct cx23885_dev *dev);
+#endif
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
index 2f846f5e0f9..45608d50529 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -23,87 +23,137 @@
#include "cx25840-core.h"
-static int set_audclk_freq(struct i2c_client *client, u32 freq)
+/*
+ * Note: The PLL and SRC parameters are based on a reference frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * However, it's not the exact reference frequency that matters, only that the
+ * firmware and modules that comprise the driver for a particular board all
+ * use the same value (close to the ideal value).
+ *
+ * Comments below will note which reference frequency is assumed for various
+ * parameters. They will usually be one of
+ *
+ * ref_freq = 28.636360 MHz
+ * or
+ * ref_freq = 28.636363 MHz
+ */
+
+static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
- if (freq != 32000 && freq != 44100 && freq != 48000)
- return -EINVAL;
-
- /* common for all inputs and rates */
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
- if (!state->is_cx23885 && !state->is_cx231xx)
- cx25840_write(client, 0x127, 0x50);
-
if (state->aud_input != CX25840_AUDIO_SERIAL) {
switch (freq) {
case 32000:
- if (state->is_cx23885) {
- /* We don't have register values
- * so avoid destroying registers. */
- break;
- }
-
- if (!state->is_cx231xx) {
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x1006040f);
-
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x01bb39ee);
- }
-
- if (state->is_cx25836)
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10
+ */
+ cx25840_write4(client, 0x108, 0x1006040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x1bb39ee
+ * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384
+ * 196.6 MHz pre-postdivide
+ * FIXME < 200 MHz is out of specified valid range
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x01bb39ee);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
break;
- /* src3/4/6_ctl = 0x0801f77f */
+ /* src3/4/6_ctl */
+ /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
cx25840_write4(client, 0x900, 0x0801f77f);
cx25840_write4(client, 0x904, 0x0801f77f);
cx25840_write4(client, 0x90c, 0x0801f77f);
break;
case 44100:
- if (state->is_cx23885) {
- /* We don't have register values
- * so avoid destroying registers. */
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10
+ */
+ cx25840_write4(client, 0x108, 0x1009040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x0ec6bd6
+ * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384
+ * 271 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
break;
- }
-
- if (!state->is_cx231xx) {
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x1009040f);
-
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x00ec6bd6);
- }
- if (state->is_cx25836)
- break;
-
- /* src3/4/6_ctl = 0x08016d59 */
+ /* src3/4/6_ctl */
+ /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
cx25840_write4(client, 0x900, 0x08016d59);
cx25840_write4(client, 0x904, 0x08016d59);
cx25840_write4(client, 0x90c, 0x08016d59);
break;
case 48000:
- if (state->is_cx23885) {
- /* We don't have register values
- * so avoid destroying registers. */
- break;
- }
-
- if (!state->is_cx231xx) {
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x100a040f);
-
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x0098d6e5);
- }
-
- if (state->is_cx25836)
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10
+ */
+ cx25840_write4(client, 0x108, 0x100a040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x098d6e5
+ * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384
+ * 295 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x0098d6e5);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
break;
- /* src3/4/6_ctl = 0x08014faa */
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
cx25840_write4(client, 0x900, 0x08014faa);
cx25840_write4(client, 0x904, 0x08014faa);
cx25840_write4(client, 0x90c, 0x08014faa);
@@ -112,91 +162,249 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
} else {
switch (freq) {
case 32000:
- if (state->is_cx23885) {
- /* We don't have register values
- * so avoid destroying registers. */
- break;
- }
-
- if (!state->is_cx231xx) {
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x1e08040f);
-
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x012a0869);
- }
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e
+ */
+ cx25840_write4(client, 0x108, 0x1e08040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x12a0869
+ * 28636363 * 0x8.9504348/0x1e = 32000 * 256
+ * 246 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x012a0869);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x54);
- if (state->is_cx25836)
+ if (is_cx2583x(state))
break;
- /* src1_ctl = 0x08010000 */
+ /* src1_ctl */
+ /* 0x1.0000 = 32000/32000 */
cx25840_write4(client, 0x8f8, 0x08010000);
- /* src3/4/6_ctl = 0x08020000 */
+ /* src3/4/6_ctl */
+ /* 0x2.0000 = 2 * (32000/32000) */
cx25840_write4(client, 0x900, 0x08020000);
cx25840_write4(client, 0x904, 0x08020000);
cx25840_write4(client, 0x90c, 0x08020000);
-
- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
- cx25840_write(client, 0x127, 0x54);
break;
case 44100:
- if (state->is_cx23885) {
- /* We don't have register values
- * so avoid destroying registers. */
- break;
- }
-
-
- if (!state->is_cx231xx) {
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x1809040f);
-
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x00ec6bd6);
- }
-
- if (state->is_cx25836)
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18
+ */
+ cx25840_write4(client, 0x108, 0x1809040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x0ec6bd6
+ * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256
+ * 271 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x00ec6bd6);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
break;
- /* src1_ctl = 0x08010000 */
+ /* src1_ctl */
+ /* 0x1.60cd = 44100/32000 */
cx25840_write4(client, 0x8f8, 0x080160cd);
- /* src3/4/6_ctl = 0x08020000 */
+ /* src3/4/6_ctl */
+ /* 0x1.7385 = 2 * (32000/44100) */
cx25840_write4(client, 0x900, 0x08017385);
cx25840_write4(client, 0x904, 0x08017385);
cx25840_write4(client, 0x90c, 0x08017385);
break;
case 48000:
- if (!state->is_cx23885 && !state->is_cx231xx) {
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x180a040f);
+ /*
+ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+ * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18
+ */
+ cx25840_write4(client, 0x108, 0x180a040f);
+
+ /*
+ * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+ * 28636360 * 0xf.15f17f0/4 = 108 MHz
+ * 432 MHz pre-postdivide
+ */
+
+ /*
+ * AUX_PLL Fraction = 0x098d6e5
+ * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256
+ * 295 MHz pre-postdivide
+ * FIXME 28636363 ref_freq doesn't match VID PLL ref
+ */
+ cx25840_write4(client, 0x110, 0x0098d6e5);
+
+ /*
+ * SA_MCLK_SEL = 1
+ * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
+ */
+ cx25840_write(client, 0x127, 0x50);
+
+ if (is_cx2583x(state))
+ break;
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x0098d6e5);
- }
+ /* src1_ctl */
+ /* 0x1.8000 = 48000/32000 */
+ cx25840_write4(client, 0x8f8, 0x08018000);
- if (state->is_cx25836)
- break;
+ /* src3/4/6_ctl */
+ /* 0x1.5555 = 2 * (32000/48000) */
+ cx25840_write4(client, 0x900, 0x08015555);
+ cx25840_write4(client, 0x904, 0x08015555);
+ cx25840_write4(client, 0x90c, 0x08015555);
+ break;
+ }
+ }
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ return cx25840_set_audclk_freq(client, freq);
+}
+
+static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+ switch (freq) {
+ case 32000:
+ case 44100:
+ case 48000:
+ /* We don't have register values
+ * so avoid destroying registers. */
+ /* FIXME return -EINVAL; */
+ break;
+ }
+ } else {
+ switch (freq) {
+ case 32000:
+ case 44100:
+ /* We don't have register values
+ * so avoid destroying registers. */
+ /* FIXME return -EINVAL; */
+ break;
+
+ case 48000:
+ /* src1_ctl */
+ /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
+ cx25840_write4(client, 0x8f8, 0x0801867c);
+
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ break;
+ }
+ }
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+ switch (freq) {
+ case 32000:
+ /* src3/4/6_ctl */
+ /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
+ cx25840_write4(client, 0x900, 0x0801f77f);
+ cx25840_write4(client, 0x904, 0x0801f77f);
+ cx25840_write4(client, 0x90c, 0x0801f77f);
+ break;
+
+ case 44100:
+ /* src3/4/6_ctl */
+ /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
+ cx25840_write4(client, 0x900, 0x08016d59);
+ cx25840_write4(client, 0x904, 0x08016d59);
+ cx25840_write4(client, 0x90c, 0x08016d59);
+ break;
+
+ case 48000:
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ break;
+ }
+ } else {
+ switch (freq) {
+ /* FIXME These cases make different assumptions about audclk */
+ case 32000:
+ /* src1_ctl */
+ /* 0x1.0000 = 32000/32000 */
+ cx25840_write4(client, 0x8f8, 0x08010000);
- if (!state->is_cx23885 && !state->is_cx231xx) {
- /* src1_ctl */
- cx25840_write4(client, 0x8f8, 0x08018000);
+ /* src3/4/6_ctl */
+ /* 0x2.0000 = 2 * (32000/32000) */
+ cx25840_write4(client, 0x900, 0x08020000);
+ cx25840_write4(client, 0x904, 0x08020000);
+ cx25840_write4(client, 0x90c, 0x08020000);
+ break;
- /* src3/4/6_ctl */
- cx25840_write4(client, 0x900, 0x08015555);
- cx25840_write4(client, 0x904, 0x08015555);
- cx25840_write4(client, 0x90c, 0x08015555);
- } else {
+ case 44100:
+ /* src1_ctl */
+ /* 0x1.60cd = 44100/32000 */
+ cx25840_write4(client, 0x8f8, 0x080160cd);
- cx25840_write4(client, 0x8f8, 0x0801867c);
+ /* src3/4/6_ctl */
+ /* 0x1.7385 = 2 * (32000/44100) */
+ cx25840_write4(client, 0x900, 0x08017385);
+ cx25840_write4(client, 0x904, 0x08017385);
+ cx25840_write4(client, 0x90c, 0x08017385);
+ break;
- cx25840_write4(client, 0x900, 0x08014faa);
- cx25840_write4(client, 0x904, 0x08014faa);
- cx25840_write4(client, 0x90c, 0x08014faa);
- }
+ case 48000:
+ /* src1_ctl */
+ /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
+ cx25840_write4(client, 0x8f8, 0x0801867c);
+
+ /* src3/4/6_ctl */
+ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
break;
}
}
@@ -206,6 +414,25 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
return 0;
}
+static int set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (freq != 32000 && freq != 44100 && freq != 48000)
+ return -EINVAL;
+
+ if (is_cx231xx(state))
+ return cx231xx_set_audclk_freq(client, freq);
+
+ if (is_cx2388x(state))
+ return cx23885_set_audclk_freq(client, freq);
+
+ if (is_cx2583x(state))
+ return cx25836_set_audclk_freq(client, freq);
+
+ return cx25840_set_audclk_freq(client, freq);
+}
+
void cx25840_audio_set_path(struct i2c_client *client)
{
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
@@ -243,7 +470,7 @@ void cx25840_audio_set_path(struct i2c_client *client)
cx25840_and_or(client, 0x810, ~0x1, 0x00);
/* Ensure the controller is running when we exit */
- if (state->is_cx23885 || state->is_cx231xx)
+ if (is_cx2388x(state) || is_cx231xx(state))
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
@@ -383,7 +610,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
struct cx25840_state *state = to_state(sd);
int retval;
- if (!state->is_cx25836)
+ if (!is_cx2583x(state))
cx25840_and_or(client, 0x810, ~0x1, 1);
if (state->aud_input != CX25840_AUDIO_SERIAL) {
cx25840_and_or(client, 0x803, ~0x10, 0);
@@ -392,7 +619,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
retval = set_audclk_freq(client, freq);
if (state->aud_input != CX25840_AUDIO_SERIAL)
cx25840_and_or(client, 0x803, ~0x10, 0x10);
- if (!state->is_cx25836)
+ if (!is_cx2583x(state))
cx25840_and_or(client, 0x810, ~0x1, 0);
return retval;
}
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 1aeaf18a9be..385ecd58f1c 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -259,6 +259,13 @@ static void cx23885_initialize(struct i2c_client *client)
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
+ /*
+ * Come out of digital power down
+ * The CX23888, at least, needs this, otherwise registers aside from
+ * 0x0-0x2 can't be read or written.
+ */
+ cx25840_write(client, 0x000, 0);
+
/* Internal Reset */
cx25840_and_or(client, 0x102, ~0x01, 0x01);
cx25840_and_or(client, 0x102, ~0x01, 0x00);
@@ -269,18 +276,45 @@ static void cx23885_initialize(struct i2c_client *client)
/* DIF in reset? */
cx25840_write(client, 0x398, 0);
- /* Trust the default xtal, no division */
- /* This changes for the cx23888 products */
+ /*
+ * Trust the default xtal, no division
+ * '885: 28.636363... MHz
+ * '887: 25.000000 MHz
+ * '888: 50.000000 MHz
+ */
cx25840_write(client, 0x2, 0x76);
- /* Bring down the regulator for AUX clk */
+ /* Power up all the PLL's and DLL */
cx25840_write(client, 0x1, 0x40);
- /* Sys PLL frac */
- cx25840_write4(client, 0x11c, 0x01d1744c);
-
- /* Sys PLL int */
- cx25840_write4(client, 0x118, 0x00000416);
+ /* Sys PLL */
+ switch (state->id) {
+ case V4L2_IDENT_CX23888_AV:
+ /*
+ * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz
+ * 572.73 MHz before post divide
+ */
+ cx25840_write4(client, 0x11c, 0x00e8ba26);
+ cx25840_write4(client, 0x118, 0x0000040b);
+ break;
+ case V4L2_IDENT_CX23887_AV:
+ /*
+ * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz
+ * 572.73 MHz before post divide
+ */
+ cx25840_write4(client, 0x11c, 0x01d1744c);
+ cx25840_write4(client, 0x118, 0x00000416);
+ break;
+ case V4L2_IDENT_CX23885_AV:
+ default:
+ /*
+ * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz
+ * 572.73 MHz before post divide
+ */
+ cx25840_write4(client, 0x11c, 0x00000000);
+ cx25840_write4(client, 0x118, 0x00000414);
+ break;
+ }
/* Disable DIF bypass */
cx25840_write4(client, 0x33c, 0x00000001);
@@ -288,11 +322,15 @@ static void cx23885_initialize(struct i2c_client *client)
/* DIF Src phase inc */
cx25840_write4(client, 0x340, 0x0df7df83);
- /* Vid PLL frac */
- cx25840_write4(client, 0x10c, 0x01b6db7b);
-
- /* Vid PLL int */
- cx25840_write4(client, 0x108, 0x00000512);
+ /*
+ * Vid PLL
+ * Setup for a BT.656 pixel clock of 13.5 Mpixels/second
+ *
+ * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz
+ * 432.0 MHz before post divide
+ */
+ cx25840_write4(client, 0x10c, 0x002be2c9);
+ cx25840_write4(client, 0x108, 0x0000040f);
/* Luma */
cx25840_write4(client, 0x414, 0x00107d12);
@@ -300,11 +338,43 @@ static void cx23885_initialize(struct i2c_client *client)
/* Chroma */
cx25840_write4(client, 0x420, 0x3d008282);
- /* Aux PLL frac */
- cx25840_write4(client, 0x114, 0x017dbf48);
-
- /* Aux PLL int */
- cx25840_write4(client, 0x110, 0x000a030e);
+ /*
+ * Aux PLL
+ * Initial setup for audio sample clock:
+ * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz
+ * Intial I2S output/master clock(?):
+ * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz
+ */
+ switch (state->id) {
+ case V4L2_IDENT_CX23888_AV:
+ /*
+ * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz
+ * 368.64 MHz before post divide
+ * 122.88 MHz / 0xa = 12.288 MHz
+ */
+ cx25840_write4(client, 0x114, 0x00bedfa4);
+ cx25840_write4(client, 0x110, 0x000a0307);
+ break;
+ case V4L2_IDENT_CX23887_AV:
+ /*
+ * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz
+ * 368.64 MHz before post divide
+ * 122.88 MHz / 0xa = 12.288 MHz
+ */
+ cx25840_write4(client, 0x114, 0x017dbf48);
+ cx25840_write4(client, 0x110, 0x000a030e);
+ break;
+ case V4L2_IDENT_CX23885_AV:
+ default:
+ /*
+ * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz
+ * 368.64 MHz before post divide
+ * 122.88 MHz / 0xa = 12.288 MHz
+ */
+ cx25840_write4(client, 0x114, 0x01bf0c9e);
+ cx25840_write4(client, 0x110, 0x000a030c);
+ break;
+ };
/* ADC2 input select */
cx25840_write(client, 0x102, 0x10);
@@ -494,7 +564,7 @@ void cx25840_std_setup(struct i2c_client *client)
}
/* DEBUG: Displays configured PLL frequency */
- if (!state->is_cx231xx) {
+ if (!is_cx231xx(state)) {
pll_int = cx25840_read(client, 0x108);
pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
pll_post = cx25840_read(client, 0x109);
@@ -615,13 +685,30 @@ static void input_change(struct i2c_client *client)
}
cx25840_write(client, 0x80b, 0x00);
} else if (std & V4L2_STD_PAL) {
- /* Follow tuner change procedure for PAL */
+ /* Autodetect audio standard and audio system */
cx25840_write(client, 0x808, 0xff);
- cx25840_write(client, 0x80b, 0x10);
+ /* Since system PAL-L is pretty much non-existant and
+ not used by any public broadcast network, force
+ 6.5 MHz carrier to be interpreted as System DK,
+ this avoids DK audio detection instability */
+ cx25840_write(client, 0x80b, 0x00);
} else if (std & V4L2_STD_SECAM) {
- /* Select autodetect for SECAM */
+ /* Autodetect audio standard and audio system */
cx25840_write(client, 0x808, 0xff);
- cx25840_write(client, 0x80b, 0x10);
+ /* If only one of SECAM-DK / SECAM-L is required, then force
+ 6.5MHz carrier, else autodetect it */
+ if ((std & V4L2_STD_SECAM_DK) &&
+ !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+ /* 6.5 MHz carrier to be interpreted as System DK */
+ cx25840_write(client, 0x80b, 0x00);
+ } else if (!(std & V4L2_STD_SECAM_DK) &&
+ (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+ /* 6.5 MHz carrier to be interpreted as System L */
+ cx25840_write(client, 0x80b, 0x08);
+ } else {
+ /* 6.5 MHz carrier to be autodetected */
+ cx25840_write(client, 0x80b, 0x10);
+ }
}
cx25840_and_or(client, 0x810, ~0x01, 0);
@@ -633,6 +720,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 is_composite = (vid_input >= CX25840_COMPOSITE1 &&
vid_input <= CX25840_COMPOSITE8);
+ u8 is_component = (vid_input & CX25840_COMPONENT_ON) ==
+ CX25840_COMPONENT_ON;
+ int luma = vid_input & 0xf0;
+ int chroma = vid_input & 0xf00;
u8 reg;
v4l_dbg(1, cx25840_debug, client,
@@ -645,18 +736,14 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
reg = vid_input & 0xff;
if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON)
is_composite = 0;
- else
+ else if ((vid_input & CX25840_COMPONENT_ON) == 0)
is_composite = 1;
v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n",
reg, is_composite);
- } else
- if (is_composite) {
+ } else if (is_composite) {
reg = 0xf0 + (vid_input - CX25840_COMPOSITE1);
} else {
- int luma = vid_input & 0xf0;
- int chroma = vid_input & 0xf00;
-
if ((vid_input & ~0xff0) ||
luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 ||
chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) {
@@ -678,7 +765,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
* configuration in reg (for the cx23885) so we have no
* need to attempt to flip bits for earlier av decoders.
*/
- if (!state->is_cx23885 && !state->is_cx231xx) {
+ if (!is_cx2388x(state) && !is_cx231xx(state)) {
switch (aud_input) {
case CX25840_AUDIO_SERIAL:
/* do nothing, use serial audio input */
@@ -698,10 +785,13 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
cx25840_write(client, 0x103, reg);
- /* Set INPUT_MODE to Composite (0) or S-Video (1) */
- cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
+ /* Set INPUT_MODE to Composite, S-Video or Component */
+ if (is_component)
+ cx25840_and_or(client, 0x401, ~0x6, 0x6);
+ else
+ cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
- if (!state->is_cx23885 && !state->is_cx231xx) {
+ if (!is_cx2388x(state) && !is_cx231xx(state)) {
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
@@ -710,22 +800,31 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
else
cx25840_and_or(client, 0x102, ~0x4, 0);
} else {
- if (is_composite)
+ /* Set DUAL_MODE_ADC2 to 1 if component*/
+ cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0);
+ if (is_composite) {
/* ADC2 input select channel 2 */
cx25840_and_or(client, 0x102, ~0x2, 0);
- else
- /* ADC2 input select channel 3 */
- cx25840_and_or(client, 0x102, ~0x2, 2);
+ } else if (!is_component) {
+ /* S-Video */
+ if (chroma >= CX25840_SVIDEO_CHROMA7) {
+ /* ADC2 input select channel 3 */
+ cx25840_and_or(client, 0x102, ~0x2, 2);
+ } else {
+ /* ADC2 input select channel 2 */
+ cx25840_and_or(client, 0x102, ~0x2, 0);
+ }
+ }
}
state->vid_input = vid_input;
state->aud_input = aud_input;
- if (!state->is_cx25836) {
+ if (!is_cx2583x(state)) {
cx25840_audio_set_path(client);
input_change(client);
}
- if (state->is_cx23885) {
+ if (is_cx2388x(state)) {
/* Audio channel 1 src : Parallel 1 */
cx25840_write(client, 0x124, 0x03);
@@ -741,7 +840,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
*/
cx25840_write(client, 0x918, 0xa0);
cx25840_write(client, 0x919, 0x01);
- } else if (state->is_cx231xx) {
+ } else if (is_cx231xx(state)) {
/* Audio channel 1 src : Parallel 1 */
cx25840_write(client, 0x124, 0x03);
@@ -805,7 +904,7 @@ static int set_v4lstd(struct i2c_client *client)
cx25840_and_or(client, 0x400, ~0xf, fmt);
cx25840_and_or(client, 0x403, ~0x3, pal_m);
cx25840_std_setup(client);
- if (!state->is_cx25836)
+ if (!is_cx2583x(state))
input_change(client);
return 0;
}
@@ -868,7 +967,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_MUTE:
- if (state->is_cx25836)
+ if (is_cx2583x(state))
return -EINVAL;
return cx25840_audio_s_ctrl(sd, ctrl);
@@ -905,7 +1004,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_BALANCE:
case V4L2_CID_AUDIO_MUTE:
- if (state->is_cx25836)
+ if (is_cx2583x(state))
return -EINVAL;
return cx25840_audio_g_ctrl(sd, ctrl);
default:
@@ -1209,11 +1308,11 @@ static int cx25840_load_fw(struct v4l2_subdev *sd)
if (!state->is_initialized) {
/* initialize and load firmware */
state->is_initialized = 1;
- if (state->is_cx25836)
+ if (is_cx2583x(state))
cx25836_initialize(client);
- else if (state->is_cx23885)
+ else if (is_cx2388x(state))
cx23885_initialize(client);
- else if (state->is_cx231xx)
+ else if (is_cx231xx(state))
cx231xx_initialize(client);
else
cx25840_initialize(client);
@@ -1256,17 +1355,17 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
v4l_dbg(1, cx25840_debug, client, "%s output\n",
enable ? "enable" : "disable");
if (enable) {
- if (state->is_cx23885 || state->is_cx231xx) {
+ if (is_cx2388x(state) || is_cx231xx(state)) {
u8 v = (cx25840_read(client, 0x421) | 0x0b);
cx25840_write(client, 0x421, v);
} else {
cx25840_write(client, 0x115,
- state->is_cx25836 ? 0x0c : 0x8c);
+ is_cx2583x(state) ? 0x0c : 0x8c);
cx25840_write(client, 0x116,
- state->is_cx25836 ? 0x04 : 0x07);
+ is_cx2583x(state) ? 0x04 : 0x07);
}
} else {
- if (state->is_cx23885 || state->is_cx231xx) {
+ if (is_cx2388x(state) || is_cx231xx(state)) {
u8 v = cx25840_read(client, 0x421) & ~(0x0b);
cx25840_write(client, 0x421, v);
} else {
@@ -1292,7 +1391,7 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
default:
break;
}
- if (state->is_cx25836)
+ if (is_cx2583x(state))
return -EINVAL;
switch (qc->id) {
@@ -1346,7 +1445,7 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd,
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->is_cx25836)
+ if (is_cx2583x(state))
return -EINVAL;
return set_input(client, state->vid_input, input);
}
@@ -1356,7 +1455,7 @@ static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (!state->is_cx25836)
+ if (!is_cx2583x(state))
input_change(client);
return 0;
}
@@ -1373,7 +1472,7 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
return 0;
vt->signal = vpres ? 0xffff : 0x0;
- if (state->is_cx25836)
+ if (is_cx2583x(state))
return 0;
vt->capability |=
@@ -1404,7 +1503,7 @@ static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->radio || state->is_cx25836)
+ if (state->radio || is_cx2583x(state))
return 0;
switch (vt->audmode) {
@@ -1445,11 +1544,11 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->is_cx25836)
+ if (is_cx2583x(state))
cx25836_initialize(client);
- else if (state->is_cx23885)
+ else if (is_cx2388x(state))
cx23885_initialize(client);
- else if (state->is_cx231xx)
+ else if (is_cx231xx(state))
cx231xx_initialize(client);
else
cx25840_initialize(client);
@@ -1470,7 +1569,7 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
struct i2c_client *client = v4l2_get_subdevdata(sd);
log_video_status(client);
- if (!state->is_cx25836)
+ if (!is_cx2583x(state))
log_audio_status(client);
return 0;
}
@@ -1521,12 +1620,50 @@ static const struct v4l2_subdev_ops cx25840_ops = {
/* ----------------------------------------------------------------------- */
+static u32 get_cx2388x_ident(struct i2c_client *client)
+{
+ u32 ret;
+
+ /* Come out of digital power down */
+ cx25840_write(client, 0x000, 0);
+
+ /* Detecting whether the part is cx23885/7/8 is more
+ * difficult than it needs to be. No ID register. Instead we
+ * probe certain registers indicated in the datasheets to look
+ * for specific defaults that differ between the silicon designs. */
+
+ /* It's either 885/7 if the IR Tx Clk Divider register exists */
+ if (cx25840_read4(client, 0x204) & 0xffff) {
+ /* CX23885 returns bogus repetitive byte values for the DIF,
+ * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */
+ ret = cx25840_read4(client, 0x300);
+ if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) {
+ /* No DIF */
+ ret = V4L2_IDENT_CX23885_AV;
+ } else {
+ /* CX23887 has a broken DIF, but the registers
+ * appear valid (but unsed), good enough to detect. */
+ ret = V4L2_IDENT_CX23887_AV;
+ }
+ } else if (cx25840_read4(client, 0x300) & 0x0fffffff) {
+ /* DIF PLL Freq Word reg exists; chip must be a CX23888 */
+ ret = V4L2_IDENT_CX23888_AV;
+ } else {
+ v4l_err(client, "Unable to detect h/w, assuming cx23887\n");
+ ret = V4L2_IDENT_CX23887_AV;
+ }
+
+ /* Back into digital power down */
+ cx25840_write(client, 0x000, 2);
+ return ret;
+}
+
static int cx25840_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct cx25840_state *state;
struct v4l2_subdev *sd;
- u32 id;
+ u32 id = V4L2_IDENT_NONE;
u16 device_id;
/* Check if the adapter supports the needed features */
@@ -1543,17 +1680,22 @@ static int cx25840_probe(struct i2c_client *client,
* 0x83 for the cx2583x and 0x84 for the cx2584x */
if ((device_id & 0xff00) == 0x8300) {
id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
- }
- else if ((device_id & 0xff00) == 0x8400) {
+ } else if ((device_id & 0xff00) == 0x8400) {
id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
} else if (device_id == 0x0000) {
- id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
- } else if (device_id == 0x1313) {
- id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ id = get_cx2388x_ident(client);
} else if ((device_id & 0xfff0) == 0x5A30) {
- id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
- }
- else {
+ /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */
+ id = V4L2_IDENT_CX2310X_AV;
+ } else if ((device_id & 0xff) == (device_id >> 8)) {
+ v4l_err(client,
+ "likely a confused/unresponsive cx2388[578] A/V decoder"
+ " found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ v4l_err(client, "A method to reset it from the cx25840 driver"
+ " software is not known at this time\n");
+ return -ENODEV;
+ } else {
v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
return -ENODEV;
}
@@ -1564,17 +1706,45 @@ static int cx25840_probe(struct i2c_client *client,
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
- /* Note: revision '(device_id & 0x0f) == 2' was never built. The
- marking skips from 0x1 == 22 to 0x3 == 23. */
- v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
- (device_id & 0xfff0) >> 4,
- (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f),
- client->addr << 1, client->adapter->name);
+ switch (id) {
+ case V4L2_IDENT_CX23885_AV:
+ v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX23887_AV:
+ v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX23888_AV:
+ v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX2310X_AV:
+ v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n",
+ device_id, client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX25840:
+ case V4L2_IDENT_CX25841:
+ case V4L2_IDENT_CX25842:
+ case V4L2_IDENT_CX25843:
+ /* Note: revision '(device_id & 0x0f) == 2' was never built. The
+ marking skips from 0x1 == 22 to 0x3 == 23. */
+ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
+ (device_id & 0xfff0) >> 4,
+ (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1
+ : (device_id & 0x0f),
+ client->addr << 1, client->adapter->name);
+ break;
+ case V4L2_IDENT_CX25836:
+ case V4L2_IDENT_CX25837:
+ default:
+ v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n",
+ (device_id & 0xfff0) >> 4, device_id & 0x0f,
+ client->addr << 1, client->adapter->name);
+ break;
+ }
state->c = client;
- state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
- state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
- state->is_cx231xx = (device_id == 0x5a3e);
state->vid_input = CX25840_COMPOSITE7;
state->aud_input = CX25840_AUDIO8;
state->audclk_freq = 48000;
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
index 814b5653699..55345444417 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/video/cx25840/cx25840-core.h
@@ -23,6 +23,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
#include <linux/i2c.h>
/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
@@ -48,9 +49,6 @@ struct cx25840_state {
int vbi_line_offset;
u32 id;
u32 rev;
- int is_cx25836;
- int is_cx23885;
- int is_cx231xx;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
@@ -61,6 +59,24 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct cx25840_state, sd);
}
+static inline bool is_cx2583x(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX25836 ||
+ state->id == V4L2_IDENT_CX25837;
+}
+
+static inline bool is_cx231xx(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX2310X_AV;
+}
+
+static inline bool is_cx2388x(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23885_AV ||
+ state->id == V4L2_IDENT_CX23887_AV ||
+ state->id == V4L2_IDENT_CX23888_AV;
+}
+
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
index 1f483c1d0db..8150200511d 100644
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -67,9 +67,9 @@ static const char *get_fw_name(struct i2c_client *client)
if (firmware[0])
return firmware;
- if (state->is_cx23885)
+ if (is_cx2388x(state))
return "v4l-cx23885-avcore-01.fw";
- if (state->is_cx231xx)
+ if (is_cx231xx(state))
return "v4l-cx231xx-avcore-01.fw";
return "v4l-cx25840.fw";
}
@@ -112,13 +112,13 @@ int cx25840_loadfw(struct i2c_client *client)
int MAX_BUF_SIZE = FWSEND;
u32 gpio_oe = 0, gpio_da = 0;
- if (state->is_cx23885) {
+ if (is_cx2388x(state)) {
/* Preserve the GPIO OE and output bits */
gpio_oe = cx25840_read(client, 0x160);
gpio_da = cx25840_read(client, 0x164);
}
- if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) {
+ if (is_cx231xx(state) && MAX_BUF_SIZE > 16) {
v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
}
@@ -156,7 +156,7 @@ int cx25840_loadfw(struct i2c_client *client)
size = fw->size;
release_firmware(fw);
- if (state->is_cx23885) {
+ if (is_cx2388x(state)) {
/* Restore GPIO configuration after f/w load */
cx25840_write(client, 0x160, gpio_oe);
cx25840_write(client, 0x164, gpio_da);
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 49952980dab..c7e5851d348 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -61,6 +61,8 @@ config VIDEO_CX88_DVB
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_STV0288 if !DVB_FE_CUSTOMISE
select DVB_STB6000 if !DVB_FE_CUSTOMISE
+ select DVB_STV0900 if !DVB_FE_CUSTOMISE
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
---help---
This adds support for DVB/ATSC cards based on the
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 33be6369871..d844f2aaa01 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -2075,6 +2075,18 @@ static const struct cx88_board cx88_boards[] = {
},
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_PROF_7301] = {
+ .name = "Prof 7301 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
};
/* ------------------------------------------------------------------ */
@@ -2535,6 +2547,10 @@ static const struct cx88_subid cx88_subids[] = {
.subvendor = 0x107d,
.subdevice = 0x6618,
.card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+ }, {
+ .subvendor = 0xb034,
+ .subdevice = 0x3034,
+ .card = CX88_BOARD_PROF_7301,
},
};
@@ -3211,6 +3227,7 @@ static void cx88_card_setup(struct cx88_core *core)
case CX88_BOARD_TBS_8920:
case CX88_BOARD_PROF_6200:
case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_PROF_7301:
case CX88_BOARD_SATTRADE_ST4200:
cx_write(MO_GP0_IO, 0x8000);
msleep(100);
@@ -3267,7 +3284,7 @@ static void cx88_card_setup(struct cx88_core *core)
ctl.fname);
call_all(core, tuner, s_config, &xc2028_cfg);
}
- call_all(core, tuner, s_standby);
+ call_all(core, core, s_power, 0);
}
/* ------------------------------------------------------------------ */
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index cf634606ba9..b35411160f0 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -624,7 +624,7 @@ int cx88_reset(struct cx88_core *core)
/* setup image format */
cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
- /* setup FIFO Threshholds */
+ /* setup FIFO Thresholds */
cx_write(MO_PDMA_STHRSH, 0x0807);
cx_write(MO_PDMA_DTHRSH, 0x0807);
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 518bcfe18bc..b1429692325 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -53,6 +53,9 @@
#include "stv0288.h"
#include "stb6000.h"
#include "cx24116.h"
+#include "stv0900.h"
+#include "stb6100.h"
+#include "stb6100_proc.h"
MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
@@ -573,6 +576,15 @@ static int cx24116_set_ts_param(struct dvb_frontend *fe,
return 0;
}
+static int stv0900_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 0;
+
+ return 0;
+}
+
static int cx24116_reset_device(struct dvb_frontend *fe)
{
struct cx8802_dev *dev = fe->dvb->priv;
@@ -601,6 +613,23 @@ static struct cx24116_config tevii_s460_config = {
.reset_device = cx24116_reset_device,
};
+static struct stv0900_config prof_7301_stv0900_config = {
+ .demod_address = 0x6a,
+/* demod_mode = 0,*/
+ .xtal = 27000000,
+ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+ .diseqc_mode = 2,/* 2/3 PWM */
+ .tun1_maddress = 0,/* 0x60 */
+ .tun1_adc = 0,/* 2 Vpp */
+ .path1_mode = 3,
+ .set_ts_params = stv0900_set_ts_param,
+};
+
+static struct stb6100_config prof_7301_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
+
static struct stv0299_config tevii_tuner_sharp_config = {
.demod_address = 0x68,
.inittab = sharp_z0194a_inittab,
@@ -1149,6 +1178,31 @@ static int dvb_register(struct cx8802_dev *dev)
goto frontend_detach;
}
break;
+ case CX88_BOARD_PROF_7301:{
+ struct dvb_tuner_ops *tuner_ops = NULL;
+
+ fe0->dvb.frontend = dvb_attach(stv0900_attach,
+ &prof_7301_stv0900_config,
+ &core->i2c_adap, 0);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6100_attach, fe0->dvb.frontend,
+ &prof_7301_stb6100_config,
+ &core->i2c_adap))
+ goto frontend_detach;
+
+ tuner_ops = &fe0->dvb.frontend->ops.tuner_ops;
+ tuner_ops->set_frequency = stb6100_set_freq;
+ tuner_ops->get_frequency = stb6100_get_freq;
+ tuner_ops->set_bandwidth = stb6100_set_bandw;
+ tuner_ops->get_bandwidth = stb6100_get_bandw;
+
+ core->prev_set_voltage =
+ fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage =
+ tevii_dvbs_set_voltage;
+ }
+ break;
+ }
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
core->name);
@@ -1170,11 +1224,11 @@ static int dvb_register(struct cx8802_dev *dev)
fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
/* Put the analog decoder in standby to keep it quiet */
- call_all(core, tuner, s_standby);
+ call_all(core, core, s_power, 0);
/* register everything */
return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
- &dev->pci->dev, adapter_nr, mfe_shared);
+ &dev->pci->dev, adapter_nr, mfe_shared, NULL);
frontend_detach:
core->gate_ctrl = NULL;
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index 78b3635178a..92b8cdf9fb8 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -118,13 +118,13 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
data = (data << 4) | ((gpio_key & 0xf0) >> 4);
- ir_input_keydown(ir->input, &ir->ir, data, data);
+ ir_input_keydown(ir->input, &ir->ir, data);
ir_input_nokey(ir->input, &ir->ir);
} else if (ir->mask_keydown) {
/* bit set on keydown */
if (gpio & ir->mask_keydown) {
- ir_input_keydown(ir->input, &ir->ir, data, data);
+ ir_input_keydown(ir->input, &ir->ir, data);
} else {
ir_input_nokey(ir->input, &ir->ir);
}
@@ -132,14 +132,14 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
} else if (ir->mask_keyup) {
/* bit cleared on keydown */
if (0 == (gpio & ir->mask_keyup)) {
- ir_input_keydown(ir->input, &ir->ir, data, data);
+ ir_input_keydown(ir->input, &ir->ir, data);
} else {
ir_input_nokey(ir->input, &ir->ir);
}
} else {
/* can't distinguish keydown/up :-/ */
- ir_input_keydown(ir->input, &ir->ir, data, data);
+ ir_input_keydown(ir->input, &ir->ir, data);
ir_input_nokey(ir->input, &ir->ir);
}
}
@@ -303,6 +303,23 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir->mask_keydown = 0x02;
ir->polling = 50; /* ms */
break;
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_SATTRADE_ST4200:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_TBS_8910:
+ case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_PROF_7301:
+ case CX88_BOARD_PROF_6200:
+ ir_codes = &ir_codes_tbs_nec_table;
+ ir_type = IR_TYPE_PD;
+ ir->sampling = 0xff00; /* address */
+ break;
+ case CX88_BOARD_TEVII_S460:
+ case CX88_BOARD_TEVII_S420:
+ ir_codes = &ir_codes_tevii_nec_table;
+ ir_type = IR_TYPE_PD;
+ ir->sampling = 0xff00; /* address */
+ break;
case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
ir_codes = &ir_codes_dntv_live_dvbt_pro_table;
ir_type = IR_TYPE_PD;
@@ -343,7 +360,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
- ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ if (err < 0)
+ goto err_out_free;
+
input_dev->name = ir->name;
input_dev->phys = ir->phys;
input_dev->id.bustype = BUS_PCI;
@@ -373,6 +393,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
cx88_ir_stop(core, ir);
core->ir = NULL;
err_out_free:
+ ir_input_free(input_dev);
input_free_device(input_dev);
kfree(ir);
return err;
@@ -387,6 +408,7 @@ int cx88_ir_fini(struct cx88_core *core)
return 0;
cx88_ir_stop(core, ir);
+ ir_input_free(ir->input);
input_unregister_device(ir->input);
kfree(ir);
@@ -432,8 +454,17 @@ void cx88_ir_irq(struct cx88_core *core)
/* decode it */
switch (core->boardnr) {
+ case CX88_BOARD_TEVII_S460:
+ case CX88_BOARD_TEVII_S420:
case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_SATTRADE_ST4200:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_TBS_8910:
+ case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_PROF_7301:
+ case CX88_BOARD_PROF_6200:
ircode = ir_decode_pulsedistance(ir->samples, ir->scount, 1, 4);
if (ircode == 0xffffffff) { /* decoding error */
@@ -461,7 +492,7 @@ void cx88_ir_irq(struct cx88_core *core)
ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f);
- ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f, (ircode >> 16) & 0xff);
+ ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f);
ir->release = jiffies + msecs_to_jiffies(120);
break;
case CX88_BOARD_HAUPPAUGE:
@@ -498,7 +529,7 @@ void cx88_ir_irq(struct cx88_core *core)
if ( dev != 0x1e && dev != 0x1f )
/* not a hauppauge remote */
break;
- ir_input_keydown(ir->input, &ir->ir, code, ircode);
+ ir_input_keydown(ir->input, &ir->ir, code);
ir->release = jiffies + msecs_to_jiffies(120);
break;
case CX88_BOARD_PINNACLE_PCTV_HD_800i:
@@ -506,7 +537,7 @@ void cx88_ir_irq(struct cx88_core *core)
ir_dprintk("biphase decoded: %x\n", ircode);
if ((ircode & 0xfffff000) != 0x3000)
break;
- ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode);
+ ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f);
ir->release = jiffies + msecs_to_jiffies(120);
break;
}
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 57e6b124109..d7e8fcee559 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -935,7 +935,7 @@ static int video_release(struct file *file)
mutex_lock(&dev->core->lock);
if(atomic_dec_and_test(&dev->core->users))
- call_all(dev->core, tuner, s_standby);
+ call_all(dev->core, core, s_power, 0);
mutex_unlock(&dev->core->lock);
return 0;
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index d5cea41f420..e1c52171010 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_HAUPPAUGE_IRONLY 80
#define CX88_BOARD_WINFAST_DTV1800H 81
#define CX88_BOARD_WINFAST_DTV2000H_J 82
+#define CX88_BOARD_PROF_7301 83
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c
index 4629cabe3f2..31439001637 100644
--- a/drivers/media/video/davinci/dm355_ccdc.c
+++ b/drivers/media/video/davinci/dm355_ccdc.c
@@ -285,7 +285,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
if ((ccdcparam->med_filt_thres < 0) ||
(ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
- dev_dbg(dev, "Invalid value of median filter thresold\n");
+ dev_dbg(dev, "Invalid value of median filter threshold\n");
return -EINVAL;
}
@@ -959,7 +959,7 @@ static struct ccdc_hw_device ccdc_hw_dev = {
},
};
-static int dm355_ccdc_init(void)
+static int __init dm355_ccdc_init(void)
{
printk(KERN_NOTICE "dm355_ccdc_init\n");
if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
@@ -969,7 +969,7 @@ static int dm355_ccdc_init(void)
return 0;
}
-static void dm355_ccdc_exit(void)
+static void __exit dm355_ccdc_exit(void)
{
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
}
diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c
index 2f19a919f47..d5fa193f32d 100644
--- a/drivers/media/video/davinci/dm644x_ccdc.c
+++ b/drivers/media/video/davinci/dm644x_ccdc.c
@@ -859,7 +859,7 @@ static struct ccdc_hw_device ccdc_hw_dev = {
},
};
-static int dm644x_ccdc_init(void)
+static int __init dm644x_ccdc_init(void)
{
printk(KERN_NOTICE "dm644x_ccdc_init\n");
if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
@@ -869,7 +869,7 @@ static int dm644x_ccdc_init(void)
return 0;
}
-static void dm644x_ccdc_exit(void)
+static void __exit dm644x_ccdc_exit(void)
{
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
}
diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c
index 402ce43ef38..12a1b3d7132 100644
--- a/drivers/media/video/davinci/vpfe_capture.c
+++ b/drivers/media/video/davinci/vpfe_capture.c
@@ -660,7 +660,7 @@ static void vpfe_detach_irq(struct vpfe_device *vpfe_dev)
frame_format = ccdc_dev->hw_ops.get_frame_format();
if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
- free_irq(IRQ_VDINT1, vpfe_dev);
+ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
}
static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
@@ -1338,7 +1338,7 @@ static int vpfe_reqbufs(struct file *file, void *priv,
vpfe_dev->memory = req_buf->memory;
videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue,
&vpfe_videobuf_qops,
- NULL,
+ vpfe_dev->pdev,
&vpfe_dev->irqlock,
req_buf->type,
vpfe_dev->fmt.fmt.pix.field,
@@ -1413,6 +1413,41 @@ static int vpfe_dqbuf(struct file *file, void *priv,
buf, file->f_flags & O_NONBLOCK);
}
+static int vpfe_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, queryctrl, qctrl);
+
+}
+
+static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, g_ctrl, ctrl);
+}
+
+static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, s_ctrl, ctrl);
+}
+
/*
* vpfe_calculate_offsets : This function calculates buffers offset
* for top and bottom field
@@ -1577,7 +1612,7 @@ static int vpfe_cropcap(struct file *file, void *priv,
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n");
- if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards))
+ if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards))
return -EINVAL;
memset(crop, 0, sizeof(struct v4l2_cropcap));
@@ -1710,6 +1745,9 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
.vidioc_querystd = vpfe_querystd,
.vidioc_s_std = vpfe_s_std,
.vidioc_g_std = vpfe_g_std,
+ .vidioc_queryctrl = vpfe_queryctrl,
+ .vidioc_g_ctrl = vpfe_g_ctrl,
+ .vidioc_s_ctrl = vpfe_s_ctrl,
.vidioc_reqbufs = vpfe_reqbufs,
.vidioc_querybuf = vpfe_querybuf,
.vidioc_qbuf = vpfe_qbuf,
@@ -1978,8 +2016,7 @@ static __init int vpfe_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, vpfe_dev);
/* set driver private data */
video_set_drvdata(vpfe_dev->video_dev, vpfe_dev);
- i2c_adap = i2c_get_adapter(1);
- vpfe_cfg = pdev->dev.platform_data;
+ i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id);
num_subdevs = vpfe_cfg->num_subdevs;
vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs,
GFP_KERNEL);
diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c
index 6d709ca8cfb..453236bd755 100644
--- a/drivers/media/video/davinci/vpss.c
+++ b/drivers/media/video/davinci/vpss.c
@@ -53,7 +53,7 @@ struct vpss_hw_ops {
int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
/* select input to ccdc */
void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
- /* clear wbl overlflow bit */
+ /* clear wbl overflow bit */
int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
};
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index ac947aecb9c..bd783387b37 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -293,7 +293,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
dprintk("opening device and trying to acquire exclusive lock\n");
if (!dev) {
- printk(KERN_ERR "BUG: em28xx can't find device struct."
+ em28xx_err("BUG: em28xx can't find device struct."
" Can't proceed with open\n");
return -ENODEV;
}
@@ -325,7 +325,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
return 0;
err:
- printk(KERN_ERR "Error while configuring em28xx mixer\n");
+ em28xx_err("Error while configuring em28xx mixer\n");
return ret;
}
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index c0fd5c6feea..82da205047b 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -225,6 +225,14 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = {
{ -1, -1, -1, -1},
};
+static struct em28xx_reg_seq vc211a_enable[] = {
+ {EM28XX_R08_GPIO, 0xff, 0x07, 10},
+ {EM28XX_R08_GPIO, 0xff, 0x0f, 10},
+ {EM28XX_R08_GPIO, 0xff, 0x0b, 10},
+ { -1, -1, -1, -1},
+};
+
+
/*
* Board definitions
*/
@@ -829,7 +837,7 @@ struct em28xx_board em28xx_boards[] = {
.mts_firmware = 1,
.has_dvb = 1,
.dvb_gpio = hauppauge_wintv_hvr_900_digital,
- .ir_codes = &ir_codes_hauppauge_new_table,
+ .ir_codes = &ir_codes_rc5_hauppauge_new_table,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
@@ -1009,6 +1017,23 @@ struct em28xx_board em28xx_boards[] = {
.amux = EM28XX_AMUX_LINE_IN,
} },
},
+ [EM2800_BOARD_VC211A] = {
+ .name = "Actionmaster/LinXcel/Digitus VC211A",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_ABSENT, /* Capture-only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = vc211a_enable,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = vc211a_enable,
+ } },
+ },
[EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
.name = "Leadtek Winfast USB II",
.is_em2800 = 1,
@@ -1381,10 +1406,14 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2882_BOARD_TERRATEC_HYBRID_XS] = {
.name = "Terratec Hybrid XS (em2882)",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
.tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
.decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = &ir_codes_terratec_cinergy_xs_table,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
@@ -1608,6 +1637,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2861),
.driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2862),
+ .driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2870),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2881),
@@ -2050,6 +2081,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
switch (dev->model) {
case EM2880_BOARD_EMPIRE_DUAL_TV:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2882_BOARD_TERRATEC_HYBRID_XS:
ctl->demod = XC3028_FE_ZARLINK456;
break;
case EM2880_BOARD_TERRATEC_HYBRID_XS:
@@ -2227,6 +2259,7 @@ static int em28xx_hint_board(struct em28xx *dev)
/* ----------------------------------------------------------------------- */
void em28xx_register_i2c_ir(struct em28xx *dev)
{
+ struct i2c_board_info info;
const unsigned short addr_list[] = {
0x30, 0x47, I2C_CLIENT_END
};
@@ -2234,9 +2267,9 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
if (disable_ir)
return;
- memset(&dev->info, 0, sizeof(&dev->info));
+ memset(&info, 0, sizeof(struct i2c_board_info));
memset(&dev->init_data, 0, sizeof(dev->init_data));
- strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE);
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
/* detect & configure */
switch (dev->model) {
@@ -2259,8 +2292,8 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
}
if (dev->init_data.name)
- dev->info.platform_data = &dev->init_data;
- i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list);
+ info.platform_data = &dev->init_data;
+ i2c_new_probed_device(&dev->i2c_adap, &info, addr_list);
}
void em28xx_card_setup(struct em28xx *dev)
@@ -2524,6 +2557,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->chip_id = retval;
switch (dev->chip_id) {
+ case CHIP_ID_EM2800:
+ em28xx_info("chip ID is em2800\n");
+ break;
case CHIP_ID_EM2710:
em28xx_info("chip ID is em2710\n");
break;
@@ -2650,7 +2686,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
em28xx_init_extension(dev);
/* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
return 0;
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index a88257a7d94..3f86d36dff2 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -50,7 +50,7 @@ MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
printk(KERN_INFO "%s %s :"fmt, \
dev->name, __func__ , ##arg); } while (0)
-static int alt = EM28XX_PINOUT;
+static int alt;
module_param(alt, int, 0644);
MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
@@ -533,8 +533,15 @@ int em28xx_audio_setup(struct em28xx *dev)
vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
if (vid1 < 0) {
- /* Device likely doesn't support AC97 */
+ /*
+ * Device likely doesn't support AC97
+ * Note: (some) em2800 devices without eeprom reports 0x91 on
+ * CHIPCFG register, even not having an AC97 chip
+ */
em28xx_warn("AC97 chip type couldn't be determined\n");
+ dev->audio_mode.ac97 = EM28XX_NO_AC97;
+ dev->has_alsa_audio = 0;
+ dev->audio_mode.has_audio = 0;
goto init_audio;
}
@@ -778,6 +785,16 @@ int em28xx_set_alternate(struct em28xx *dev)
int i;
unsigned int min_pkt_size = dev->width * 2 + 4;
+ /*
+ * alt = 0 is used only for control messages, so, only values
+ * greater than 0 can be used for streaming.
+ */
+ if (alt && alt < dev->num_alt) {
+ em28xx_coredbg("alternate forced to %d\n", dev->alt);
+ dev->alt = alt;
+ goto set_alt;
+ }
+
/* When image size is bigger than a certain value,
the frame size should be increased, otherwise, only
green screen will be received.
@@ -798,6 +815,7 @@ int em28xx_set_alternate(struct em28xx *dev)
dev->alt = i;
}
+set_alt:
if (dev->alt != prev_alt) {
em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
min_pkt_size, dev->alt);
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index db749461e5c..cc0505eb900 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -313,22 +313,20 @@ static int attach_xc3028(u8 addr, struct em28xx *dev)
cfg.i2c_addr = addr;
if (!dev->dvb->frontend) {
- printk(KERN_ERR "%s/2: dvb frontend not attached. "
- "Can't attach xc3028\n",
- dev->name);
+ em28xx_errdev("/2: dvb frontend not attached. "
+ "Can't attach xc3028\n");
return -EINVAL;
}
fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg);
if (!fe) {
- printk(KERN_ERR "%s/2: xc3028 attach failed\n",
- dev->name);
+ em28xx_errdev("/2: xc3028 attach failed\n");
dvb_frontend_detach(dev->dvb->frontend);
dev->dvb->frontend = NULL;
return -EINVAL;
}
- printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name);
+ em28xx_info("%s/2: xc3028 attached\n", dev->name);
return 0;
}
@@ -463,7 +461,7 @@ static int dvb_init(struct em28xx *dev)
dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
if (dvb == NULL) {
- printk(KERN_INFO "em28xx_dvb: memory allocation failed\n");
+ em28xx_info("em28xx_dvb: memory allocation failed\n");
return -ENOMEM;
}
dev->dvb = dvb;
@@ -493,6 +491,7 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2882_BOARD_TERRATEC_HYBRID_XS:
case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
@@ -569,15 +568,12 @@ static int dvb_init(struct em28xx *dev)
}
break;
default:
- printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
- " isn't supported yet\n",
- dev->name);
+ em28xx_errdev("/2: The frontend of your DVB/ATSC card"
+ " isn't supported yet\n");
break;
}
if (NULL == dvb->frontend) {
- printk(KERN_ERR
- "%s/2: frontend initialization failed\n",
- dev->name);
+ em28xx_errdev("/2: frontend initialization failed\n");
result = -EINVAL;
goto out_free;
}
@@ -591,7 +587,7 @@ static int dvb_init(struct em28xx *dev)
goto out_free;
em28xx_set_mode(dev, EM28XX_SUSPEND);
- printk(KERN_INFO "Successfully loaded em28xx-dvb\n");
+ em28xx_info("Successfully loaded em28xx-dvb\n");
return 0;
out_free:
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index 7a0fe3816e3..d96ec7c09dc 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -70,6 +70,7 @@ struct em28xx_IR {
int polling;
struct delayed_work work;
unsigned int last_toggle:1;
+ unsigned int full_code:1;
unsigned int last_readcount;
unsigned int repeat_interval;
@@ -246,9 +247,10 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir)
return;
}
- dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
+ dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n",
poll_result.toggle_bit, poll_result.read_count,
- ir->last_readcount, poll_result.rc_data[0]);
+ ir->last_readcount, poll_result.rc_address,
+ poll_result.rc_data[0]);
if (ir->dev->chip_id == CHIP_ID_EM2874) {
/* The em2874 clears the readcount field every time the
@@ -282,8 +284,15 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir)
if (do_sendkey) {
dprintk("sending keypress\n");
- ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
- poll_result.rc_data[0]);
+
+ if (ir->full_code)
+ ir_input_keydown(ir->input, &ir->ir,
+ poll_result.rc_address << 8 |
+ poll_result.rc_data[0]);
+ else
+ ir_input_keydown(ir->input, &ir->ir,
+ poll_result.rc_data[0]);
+
ir_input_nokey(ir->input, &ir->ir);
}
@@ -333,6 +342,8 @@ int em28xx_ir_init(struct em28xx *dev)
switch (dev->chip_id) {
case CHIP_ID_EM2860:
case CHIP_ID_EM2883:
+ if (dev->model == EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950)
+ ir->full_code = 1;
ir->get_key = default_polling_getkey;
break;
case CHIP_ID_EM2874:
@@ -356,7 +367,11 @@ int em28xx_ir_init(struct em28xx *dev)
usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
- ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER,
+ dev->board.ir_codes);
+ if (err < 0)
+ goto err_out_free;
+
input_dev->name = ir->name;
input_dev->phys = ir->phys;
input_dev->id.bustype = BUS_USB;
@@ -381,6 +396,7 @@ int em28xx_ir_init(struct em28xx *dev)
em28xx_ir_stop(ir);
dev->ir = NULL;
err_out_free:
+ ir_input_free(input_dev);
input_free_device(input_dev);
kfree(ir);
return err;
@@ -395,6 +411,7 @@ int em28xx_ir_fini(struct em28xx *dev)
return 0;
em28xx_ir_stop(ir);
+ ir_input_free(ir->input);
input_unregister_device(ir->input);
kfree(ir);
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index ed12e7ffcbd..058ac87639c 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -192,6 +192,7 @@
/* FIXME: Need to be populated with the other chip ID's */
enum em28xx_chip_id {
+ CHIP_ID_EM2800 = 7,
CHIP_ID_EM2710 = 17,
CHIP_ID_EM2820 = 18, /* Also used by some em2710 */
CHIP_ID_EM2840 = 20,
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 3a1dfb7726f..7ad65370f27 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -1060,12 +1060,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
/* the em2800 can only scale down to 50% */
height = height > (3 * maxh / 4) ? maxh : maxh / 2;
width = width > (3 * maxw / 4) ? maxw : maxw / 2;
- /* According to empiatech support the MaxPacketSize is too small
- * to support framesizes larger than 640x480 @ 30 fps or 640x576
- * @ 25 fps. As this would cut of a part of the image we prefer
- * 360x576 or 360x480 for now */
- if (width == maxw && height == maxh)
- width /= 2;
} else {
/* width must even because of the YUYV format
height must be even because of interlacing */
@@ -2225,7 +2219,7 @@ static int em28xx_v4l2_close(struct file *filp)
}
/* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
/* do this before setting alternate! */
em28xx_uninit_isoc(dev);
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 0a73e8bf0d6..441df644ddb 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -110,6 +110,7 @@
#define EM2820_BOARD_SILVERCREST_WEBCAM 71
#define EM2861_BOARD_GADMEI_UTV330PLUS 72
#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
+#define EM2800_BOARD_VC211A 74
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -143,9 +144,6 @@
*/
#define EM28XX_NUM_PACKETS 40
-/* default alternate; 0 means choose the best */
-#define EM28XX_PINOUT 0
-
#define EM28XX_INTERLACED_DEFAULT 1
/*
@@ -615,7 +613,6 @@ struct em28xx {
struct em28xx_dvb *dvb;
/* I2C keyboard data */
- struct i2c_board_info info;
struct IR_i2c_init_data init_data;
};
@@ -800,7 +797,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev)
if (dev->board.is_webcam)
return dev->sensor_xres;
- if (dev->board.max_range_640_480)
+ if (dev->board.max_range_640_480 || dev->board.is_em2800)
return 640;
return 720;
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index fe2e490ebc5..609d65b0b10 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -76,10 +76,11 @@ config USB_GSPCA_MR97310A
module will be called gspca_mr97310a.
config USB_GSPCA_OV519
- tristate "OV519 USB Camera Driver"
+ tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
help
- Say Y here if you want support for cameras based on the OV519 chip.
+ Say Y here if you want support for cameras based on one of these:
+ OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
To compile this driver as a module, choose M here: the
module will be called gspca_ov519.
@@ -103,6 +104,15 @@ config USB_GSPCA_PAC207
To compile this driver as a module, choose M here: the
module will be called gspca_pac207.
+config USB_GSPCA_PAC7302
+ tristate "Pixart PAC7302 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC7302 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7302.
+
config USB_GSPCA_PAC7311
tristate "Pixart PAC7311 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -229,6 +239,15 @@ config USB_GSPCA_STK014
To compile this driver as a module, choose M here: the
module will be called gspca_stk014.
+config USB_GSPCA_STV0680
+ tristate "STV0680 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STV0680 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stv0680.
+
config USB_GSPCA_SUNPLUS
tristate "SUNPLUS USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index b7420818037..ff2c7279d82 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
+obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o
obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o
obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o
obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o
obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
+obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o
obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
@@ -37,6 +39,7 @@ gspca_mr97310a-objs := mr97310a.o
gspca_ov519-objs := ov519.o
gspca_ov534-objs := ov534.o
gspca_pac207-objs := pac207.o
+gspca_pac7302-objs := pac7302.o
gspca_pac7311-objs := pac7311.o
gspca_sn9c20x-objs := sn9c20x.o
gspca_sonixb-objs := sonixb.o
@@ -50,6 +53,7 @@ gspca_spca561-objs := spca561.o
gspca_sq905-objs := sq905.o
gspca_sq905c-objs := sq905c.o
gspca_stk014-objs := stk014.o
+gspca_stv0680-objs := stv0680.o
gspca_sunplus-objs := sunplus.o
gspca_t613-objs := t613.o
gspca_tv8532-objs := tv8532.o
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index eca003566ae..2f0b8d621e0 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -888,8 +888,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -897,16 +896,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
if (data[0] == 0xff && data[1] == 0xd8) {
/* start of frame */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
/* put the JPEG header in the new frame */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
data += 2;
len -= 2;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static void setbrightness(struct gspca_dev*gspca_dev)
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index c1461e63647..9de86419ae1 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -752,8 +752,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
#undef LIMIT
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
int seqframe;
@@ -767,14 +766,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data[2], data[3], data[4], data[5]);
data += 30;
/* don't change datalength as the chips provided it */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
return;
}
if (len) {
data += 8;
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
} else { /* Drop Packet */
gspca_dev->last_packet_type = DISCARD_PACKET;
}
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 480ec5c87d0..5d90e744857 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -82,7 +82,6 @@ static void dostream(struct work_struct *work)
struct gspca_dev *gspca_dev = &dev->gspca_dev;
struct urb *urb = gspca_dev->urb[0];
u8 *data = urb->transfer_buffer;
- struct gspca_frame *frame;
int ret = 0;
int len;
@@ -118,10 +117,6 @@ again:
}
if (!gspca_dev->present || !gspca_dev->streaming)
goto out;
- frame = gspca_get_i_frame(&dev->gspca_dev);
- if (frame == NULL)
- gspca_dev->last_packet_type = DISCARD_PACKET;
-
if (len < FPIX_MAX_TRANSFER ||
(data[len - 2] == 0xff &&
data[len - 1] == 0xd9)) {
@@ -132,21 +127,17 @@ again:
* but there's nothing we can do. We also end
* here if the the jpeg ends right at the end
* of the frame. */
- if (frame)
- frame = gspca_frame_add(gspca_dev,
- LAST_PACKET,
- frame,
- data, len);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len);
break;
}
/* got a partial image */
- if (frame)
- gspca_frame_add(gspca_dev,
- gspca_dev->last_packet_type
- == LAST_PACKET
- ? FIRST_PACKET : INTER_PACKET,
- frame, data, len);
+ gspca_frame_add(gspca_dev,
+ gspca_dev->last_packet_type
+ == LAST_PACKET
+ ? FIRST_PACKET : INTER_PACKET,
+ data, len);
}
/* We must wait before trying reading the next
diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c
index 39f6261c1a0..1355e526ee8 100644
--- a/drivers/media/video/gspca/gl860/gl860-mi1320.c
+++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c
@@ -1,6 +1,5 @@
-/* @file gl860-mi1320.c
- * @author Olivier LORIN from my logs
- * @date 2009-08-27
+/* Subdriver for the GL860 chip with the MI1320 sensor
+ * Author Olivier LORIN from own logs
*
* 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
@@ -127,49 +126,49 @@ static u8 dat_wbalBL[] =
static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00};
-static u8 s000[] =
+static u8 dat_common00[] =
"\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42"
"\xd8\x04\x58\x00\x04\x02";
-static u8 s001[] =
+static u8 dat_common01[] =
"\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d"
"\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0";
-static u8 s002[] =
+static u8 dat_common02[] =
"\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e"
"\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00"
"\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff";
-static u8 s003[] =
+static u8 dat_common03[] =
"\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda"
"\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c"
"\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c";
-static u8 s004[] =
+static u8 dat_common04[] =
"\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43";
-static u8 s005[] =
+static u8 dat_common05[] =
"\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68"
"\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82"
"\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b";
-static u8 s006[] =
+static u8 dat_common06[] =
"\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06"
"\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4"
"\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f";
-static u8 s007[] =
+static u8 dat_common07[] =
"\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72"
"\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03"
"\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea"
"\xe1\xff\xf1\x00";
-static u8 s008[] =
+static u8 dat_common08[] =
"\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7"
"\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06"
"\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a";
-static u8 s009[] =
+static u8 dat_common09[] =
"\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03"
"\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa"
"\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14";
-static u8 s010[] =
+static u8 dat_common10[] =
"\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00"
"\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f"
"\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01"
"\xc3\x0a\xf1\x07\xc4\x00\xf1\x10";
-static u8 s011[] =
+static u8 dat_common11[] =
"\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10"
"\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00"
"\xa4\x03\xf1\xc0\xa7\x02\xf1\x81";
@@ -222,26 +221,26 @@ void mi1320_init_settings(struct gspca_dev *gspca_dev)
static void common(struct gspca_dev *gspca_dev)
{
- s32 n; /* reserved for FETCH macros */
+ s32 n; /* reserved for FETCH functions */
- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00);
ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01);
n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011);
+ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11);
keep_on_fetching_validx(gspca_dev, tbl_common,
ARRAY_SIZE(tbl_common), n);
}
diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c
index ffb09fed3e8..80cb3f1b36f 100644
--- a/drivers/media/video/gspca/gl860/gl860-mi2020.c
+++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c
@@ -1,7 +1,6 @@
-/* @file gl860-mi2020.c
- * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
+/* Subdriver for the GL860 chip with the MI2020 sensor
+ * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
* logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist.
- * @date 2009-08-27
*
* 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
@@ -41,7 +40,7 @@ static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 };
static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
-static struct validx tbl_common_a[] = {
+static struct validx tbl_common1[] = {
{0x0000, 0x0000},
{1, 0xffff}, /* msleep(35); */
{0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0},
@@ -49,7 +48,7 @@ static struct validx tbl_common_a[] = {
{0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000},
};
-static struct validx tbl_common_b[] = {
+static struct validx tbl_common2[] = {
{0x006a, 0x0007},
{35, 0xffff},
{0x00ef, 0x0006},
@@ -60,7 +59,7 @@ static struct validx tbl_common_b[] = {
{0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
};
-static struct idxdata tbl_common_c[] = {
+static struct idxdata tbl_common3[] = {
{0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"},
{6, "\xff\xff\xff"}, /* 12 */
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
@@ -109,7 +108,7 @@ static struct idxdata tbl_common_c[] = {
{0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"},
};
-static struct idxdata tbl_common_d[] = {
+static struct idxdata tbl_common4[] = {
{0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"},
{0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"},
{0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"},
@@ -118,7 +117,7 @@ static struct idxdata tbl_common_d[] = {
{0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"},
};
-static struct idxdata tbl_common_e[] = {
+static struct idxdata tbl_common5[] = {
{0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"},
{0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"},
{0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"},
@@ -180,7 +179,7 @@ static struct validx tbl_init_at_startup[] = {
{53, 0xffff},
};
-static struct idxdata tbl_init_post_alt_low_a[] = {
+static struct idxdata tbl_init_post_alt_low1[] = {
{0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"},
{0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"},
{0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"},
@@ -189,7 +188,7 @@ static struct idxdata tbl_init_post_alt_low_a[] = {
{0x33, "\x90\x00\x9b"},
};
-static struct idxdata tbl_init_post_alt_low_b[] = {
+static struct idxdata tbl_init_post_alt_low2[] = {
{0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"},
{0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
@@ -197,7 +196,7 @@ static struct idxdata tbl_init_post_alt_low_b[] = {
{2, "\xff\xff\xff"},
};
-static struct idxdata tbl_init_post_alt_low_c[] = {
+static struct idxdata tbl_init_post_alt_low3[] = {
{0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
{2, "\xff\xff\xff"},
{0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"},
@@ -221,7 +220,7 @@ static struct idxdata tbl_init_post_alt_low_c[] = {
{1, "\xff\xff\xff"},
};
-static struct idxdata tbl_init_post_alt_low_d[] = {
+static struct idxdata tbl_init_post_alt_low4[] = {
{0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
@@ -267,7 +266,7 @@ static struct idxdata tbl_init_post_alt_low_d[] = {
{0x32, "\x6c\x14\x08"},
};
-static struct idxdata tbl_init_post_alt_big_a[] = {
+static struct idxdata tbl_init_post_alt_big1[] = {
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
{2, "\xff\xff\xff"},
{0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
@@ -288,7 +287,7 @@ static struct idxdata tbl_init_post_alt_big_a[] = {
{0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
};
-static struct idxdata tbl_init_post_alt_big_b[] = {
+static struct idxdata tbl_init_post_alt_big2[] = {
{0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
{0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
{0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
@@ -317,7 +316,7 @@ static struct idxdata tbl_init_post_alt_big_b[] = {
{0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
};
-static struct idxdata tbl_init_post_alt_big_c[] = {
+static struct idxdata tbl_init_post_alt_big3[] = {
{0x33, "\x8c\xa1\x02"},
{0x33, "\x90\x00\x1f"},
{0x33, "\x8c\xa1\x02"},
@@ -388,14 +387,14 @@ static void common(struct gspca_dev *gspca_dev)
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
if (_MI2020b_) {
- fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a));
+ fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1));
} else {
if (_MI2020_)
ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL);
else
ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL);
msleep(35);
- fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b));
+ fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2));
}
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01");
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00");
@@ -403,13 +402,13 @@ static void common(struct gspca_dev *gspca_dev)
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc");
if (reso == IMAGE_1600)
msleep(2); /* 1600 */
- fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c));
+ fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3));
if (_MI2020b_ || _MI2020_)
- fetch_idxdata(gspca_dev, tbl_common_d,
- ARRAY_SIZE(tbl_common_d));
+ fetch_idxdata(gspca_dev, tbl_common4,
+ ARRAY_SIZE(tbl_common4));
- fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e));
+ fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5));
if (_MI2020b_ || _MI2020_) {
/* Different from fret */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78");
@@ -525,15 +524,15 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
12, dat_800);
if (_MI2020c_)
- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a,
- ARRAY_SIZE(tbl_init_post_alt_low_a));
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_low1,
+ ARRAY_SIZE(tbl_init_post_alt_low1));
if (reso == IMAGE_800)
- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b,
- ARRAY_SIZE(tbl_init_post_alt_low_b));
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_low2,
+ ARRAY_SIZE(tbl_init_post_alt_low2));
- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c,
- ARRAY_SIZE(tbl_init_post_alt_low_c));
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_low3,
+ ARRAY_SIZE(tbl_init_post_alt_low3));
if (_MI2020b_) {
ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
@@ -574,8 +573,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
msleep(5);/* " */
if (_MI2020c_) {
- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d,
- ARRAY_SIZE(tbl_init_post_alt_low_d));
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_low4,
+ ARRAY_SIZE(tbl_init_post_alt_low4));
} else {
ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
msleep(14); /* 0xd8 */
@@ -644,8 +643,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
3, "\x90\x04\xb0");
}
- fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a,
- ARRAY_SIZE(tbl_init_post_alt_big_a));
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_big1,
+ ARRAY_SIZE(tbl_init_post_alt_big1));
if (reso == IMAGE_1600)
msleep(13); /* 1600 */
@@ -708,8 +707,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
msleep(14);
if (_MI2020c_)
- fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b,
- ARRAY_SIZE(tbl_init_post_alt_big_b));
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_big2,
+ ARRAY_SIZE(tbl_init_post_alt_big2));
/* flip/mirror */
ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
@@ -738,8 +737,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
sd->nbIm = 0;
if (_MI2020c_)
- fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c,
- ARRAY_SIZE(tbl_init_post_alt_big_c));
+ fetch_idxdata(gspca_dev, tbl_init_post_alt_big3,
+ ARRAY_SIZE(tbl_init_post_alt_big3));
}
sd->vold.mirror = mirror;
diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c
index 14b9c373f9f..768cac5cd72 100644
--- a/drivers/media/video/gspca/gl860/gl860-ov2640.c
+++ b/drivers/media/video/gspca/gl860/gl860-ov2640.c
@@ -1,6 +1,5 @@
-/* @file gl860-ov2640.c
- * @author Olivier LORIN, from Malmostoso's logs
- * @date 2009-08-27
+/* Subdriver for the GL860 chip with the OV2640 sensor
+ * Author Olivier LORIN, from Malmostoso's logs
*
* 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
@@ -21,8 +20,12 @@
#include "gl860.h"
static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01";
-static u8 dat_init2[] = {0x61}; /* expected */
-static u8 dat_init3[] = {0x51}; /* expected */
+
+static u8 c61[] = {0x61}; /* expected */
+static u8 c51[] = {0x51}; /* expected */
+static u8 c50[] = {0x50}; /* expected */
+static u8 c28[] = {0x28}; /* expected */
+static u8 ca8[] = {0xa8}; /* expected */
static u8 dat_post[] =
"\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01";
@@ -32,10 +35,6 @@ static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21";
static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01";
static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41";
-static u8 c50[] = {0x50}; /* expected */
-static u8 c28[] = {0x28}; /* expected */
-static u8 ca8[] = {0xa8}; /* expected */
-
static struct validx tbl_init_at_startup[] = {
{0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
{0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
@@ -92,7 +91,7 @@ static struct validx tbl_common[] = {
{0x6000, 0x0010},
};
-static struct validx tbl_sensor_settings_common_a[] = {
+static struct validx tbl_sensor_settings_common1[] = {
{0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
{0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
{0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000},
@@ -104,40 +103,10 @@ static struct validx tbl_sensor_settings_common_a[] = {
{0x0040, 0x0000},
};
-static struct validx tbl_sensor_settings_common_b[] = {
+static struct validx tbl_sensor_settings_common2[] = {
{0x6001, 0x00ff}, {0x6038, 0x000c},
{10, 0xffff},
{0x6000, 0x0011},
- /* backlight=31/64 */
- {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
- /* bright=0/256 */
- {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d},
- /* wbal=64/128 */
- {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d},
- /* cntr=0/256 */
- {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d},
- /* sat=128/256 */
- {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d},
- /* sharpness=0/32 */
- {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093},
- /* hue=0/256 */
- {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d},
- /* gam=32/64 */
- {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d},
- /* image right up */
- {0xffff, 0xffff},
- {15, 0xffff},
- {0x6001, 0x00ff}, {0x6000, 0x8004},
- {0xffff, 0xffff},
- {0x60a8, 0x0004},
- {15, 0xffff},
- {0x6001, 0x00ff}, {0x6000, 0x8004},
- {0xffff, 0xffff},
- {0x60f8, 0x0004},
- /* image right up */
- {0xffff, 0xffff},
- /* backlight=31/64 */
- {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
};
static struct validx tbl_640[] = {
@@ -166,7 +135,7 @@ static struct validx tbl_800[] = {
{0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018},
};
-static struct validx tbl_big_a[] = {
+static struct validx tbl_big1[] = {
{0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
{0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045},
{0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018},
@@ -176,14 +145,14 @@ static struct validx tbl_big_a[] = {
{0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c},
};
-static struct validx tbl_big_b[] = {
+static struct validx tbl_big2[] = {
{0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052},
{0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057},
{0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3},
{0x6000, 0x008e},
};
-static struct validx tbl_big_c[] = {
+static struct validx tbl_big3[] = {
{0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd},
{0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
{0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7},
@@ -223,17 +192,19 @@ void ov2640_init_settings(struct gspca_dev *gspca_dev)
sd->vcur.hue = 0;
sd->vcur.saturation = 128;
sd->vcur.whitebal = 64;
+ sd->vcur.mirror = 0;
+ sd->vcur.flip = 0;
sd->vmax.backlight = 64;
sd->vmax.brightness = 255;
sd->vmax.sharpness = 31;
sd->vmax.contrast = 255;
sd->vmax.gamma = 64;
- sd->vmax.hue = 255 + 1;
+ sd->vmax.hue = 254 + 2;
sd->vmax.saturation = 255;
sd->vmax.whitebal = 128;
- sd->vmax.mirror = 0;
- sd->vmax.flip = 0;
+ sd->vmax.mirror = 1;
+ sd->vmax.flip = 1;
sd->vmax.AC50Hz = 0;
sd->dev_camera_settings = ov2640_camera_settings;
@@ -259,11 +230,11 @@ static int ov2640_init_at_startup(struct gspca_dev *gspca_dev)
common(gspca_dev);
- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61);
ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL);
- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51);
ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL);
/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
@@ -275,6 +246,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ sd->mirrorMask = 0;
+
sd->vold.backlight = -1;
sd->vold.brightness = -1;
sd->vold.sharpness = -1;
@@ -283,6 +256,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
sd->vold.gamma = -1;
sd->vold.hue = -1;
sd->vold.whitebal = -1;
+ sd->vold.mirror = -1;
+ sd->vold.flip = -1;
ov2640_init_post_alt(gspca_dev);
@@ -292,16 +267,16 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
- s32 n; /* reserved for FETCH macros */
+ s32 n; /* reserved for FETCH functions */
ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
- n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a,
- ARRAY_SIZE(tbl_sensor_settings_common_a));
+ n = fetch_validx(gspca_dev, tbl_sensor_settings_common1,
+ ARRAY_SIZE(tbl_sensor_settings_common1));
ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post);
common(gspca_dev);
- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a,
- ARRAY_SIZE(tbl_sensor_settings_common_a), n);
+ keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1,
+ ARRAY_SIZE(tbl_sensor_settings_common1), n);
switch (reso) {
case IMAGE_640:
@@ -316,18 +291,18 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
case IMAGE_1600:
case IMAGE_1280:
- n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a));
+ n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1));
if (reso == IMAGE_1280) {
- n = fetch_validx(gspca_dev, tbl_big_b,
- ARRAY_SIZE(tbl_big_b));
+ n = fetch_validx(gspca_dev, tbl_big2,
+ ARRAY_SIZE(tbl_big2));
} else {
ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL);
}
- n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c));
+ n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3));
if (reso == IMAGE_1280) {
ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
@@ -343,20 +318,8 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
break;
}
- n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b,
- ARRAY_SIZE(tbl_sensor_settings_common_b));
- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
- ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
- ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
+ n = fetch_validx(gspca_dev, tbl_sensor_settings_common2,
+ ARRAY_SIZE(tbl_sensor_settings_common2));
ov2640_camera_settings(gspca_dev);
@@ -393,18 +356,20 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
s32 sat = sd->vcur.saturation;
s32 hue = sd->vcur.hue;
s32 wbal = sd->vcur.whitebal;
+ s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0);
+ s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0);
if (backlight != sd->vold.backlight) {
+ /* No sd->vold.backlight=backlight; (to be done again later) */
if (backlight < 0 || backlight > sd->vmax.backlight)
backlight = 0;
ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
0, NULL);
- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024,
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024,
0, NULL);
- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
0, NULL);
- /* No sd->vold.backlight=backlight; (to be done again later) */
}
if (bright != sd->vold.brightness) {
@@ -466,7 +431,7 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL);
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d,
0, NULL);
- if (hue >= sd->vmax.hue)
+ if (hue >= 255)
sd->swapRB = 1;
else
sd->swapRB = 0;
@@ -482,14 +447,33 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL);
}
+ if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+ sd->vold.mirror = mirror;
+ sd->vold.flip = flip;
+
+ mirror = 0x80 * mirror;
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL);
+
+ flip = 0x50 * flip + mirror;
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL);
+
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
+ }
+
if (backlight != sd->vold.backlight) {
sd->vold.backlight = backlight;
ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
0, NULL);
- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024,
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024,
0, NULL);
- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
+ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
0, NULL);
}
diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c
index eda3346f939..d412694c50a 100644
--- a/drivers/media/video/gspca/gl860/gl860-ov9655.c
+++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c
@@ -1,7 +1,6 @@
-/* @file gl860-ov9655.c
- * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
+/* Subdriver for the GL860 chip with the OV9655 sensor
+ * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
* on dsd's weblog
- * @date 2009-08-27
*
* 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
@@ -104,14 +103,14 @@ static u8 *tbl_800[] = {
};
static u8 c04[] = {0x04};
-static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
-static u8 dat_post_2[] = "\x10\x10\xc1\x02";
-static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
-static u8 dat_post_4[] = "\x10\x02\xc1\x06";
-static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
-static u8 dat_post_6[] = "\x10\x10\xc1\x05";
-static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
-static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
+static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
+static u8 dat_post2[] = "\x10\x10\xc1\x02";
+static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
+static u8 dat_post4[] = "\x10\x02\xc1\x06";
+static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
+static u8 dat_post6[] = "\x10\x10\xc1\x05";
+static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
+static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
static struct validx tbl_init_post_alt[] = {
{0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff},
@@ -212,7 +211,7 @@ static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev)
static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
- s32 n; /* reserved for FETCH macros */
+ s32 n; /* reserved for FETCH functions */
s32 i;
u8 **tbl;
@@ -243,7 +242,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
@@ -259,7 +258,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
@@ -270,18 +269,18 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2);
- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3);
- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4);
- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5);
- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6);
- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7);
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7);
- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8);
+ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8);
ov9655_camera_settings(gspca_dev);
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index 6ef59ac7f50..a695e0ae13c 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -1,9 +1,7 @@
-/* @file gl860.c
- * @date 2009-08-27
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver core
*
- * Genesys Logic webcam with gl860 subdrivers
- *
- * Driver by Olivier Lorin <o.lorin@laposte.net>
+ * 2009/09/24 Olivier Lorin <o.lorin@laposte.net>
* GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
* Thanks BUGabundo and Malmostoso for your amazing help!
*
@@ -23,8 +21,8 @@
#include "gspca.h"
#include "gl860.h"
-MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>");
-MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver");
+MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>");
+MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver");
MODULE_LICENSE("GPL");
/*======================== static function declarations ====================*/
@@ -38,7 +36,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev);
static int sd_start(struct gspca_dev *gspca_dev);
static void sd_stop0(struct gspca_dev *gspca_dev);
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, u8 *data, s32 len);
+ u8 *data, int len);
static void sd_callback(struct gspca_dev *gspca_dev);
static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
@@ -53,7 +51,7 @@ MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)");
static char sensor[7];
module_param_string(sensor, sensor, sizeof(sensor), 0644);
MODULE_PARM_DESC(sensor,
- " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')");
+ " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')");
/*============================ webcam controls =============================*/
@@ -156,7 +154,7 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev)
SET_MY_CTRL(V4L2_CID_VFLIP,
V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip)
SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz)
+ V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz)
return nCtrls;
}
@@ -435,7 +433,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
/* This function is called when an image is being received */
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, u8 *data, s32 len)
+ u8 *data, int len)
{
struct sd *sd = (struct sd *) gspca_dev;
static s32 nSkipped;
@@ -447,11 +445,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* Test only against 0202h, so endianess does not matter */
switch (*(s16 *) data) {
case 0x0202: /* End of frame, start a new one */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
nSkipped = 0;
if (sd->nbIm >= 0 && sd->nbIm < 10)
sd->nbIm++;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
break;
default:
@@ -466,7 +464,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
nSkipped = nToSkip + 1;
}
gspca_frame_add(gspca_dev,
- INTER_PACKET, frame, data, len);
+ INTER_PACKET, data, len);
}
break;
}
@@ -702,6 +700,7 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
msleep(56);
+ PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX");
nOV = 0;
for (ntry = 0; ntry < 4; ntry++) {
ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
@@ -711,14 +710,14 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
msleep(10);
ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
- PDEBUG(D_PROBE, "1st probe=%02x", probe);
+ PDEBUG(D_PROBE, "probe=0x%02x", probe);
if (probe == 0xff)
nOV++;
}
if (nOV) {
- PDEBUG(D_PROBE, "0xff -> sensor OVXXXX");
- PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655");
+ PDEBUG(D_PROBE, "0xff -> OVXXXX");
+ PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655");
nb26 = nb96 = 0;
for (ntry = 0; ntry < 4; ntry++) {
@@ -728,40 +727,38 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
0, NULL);
msleep(10);
+
/* Wait for 26(OV2640) or 96(OV9655) */
ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
1, &probe);
- PDEBUG(D_PROBE, "2nd probe=%02x", probe);
- if (probe == 0x00)
- nb26++;
if (probe == 0x26 || probe == 0x40) {
+ PDEBUG(D_PROBE,
+ "probe=0x%02x -> OV2640",
+ probe);
sd->sensor = ID_OV2640;
nb26 += 4;
break;
}
if (probe == 0x96 || probe == 0x55) {
+ PDEBUG(D_PROBE,
+ "probe=0x%02x -> OV9655",
+ probe);
sd->sensor = ID_OV9655;
nb96 += 4;
break;
}
+ PDEBUG(D_PROBE, "probe=0x%02x", probe);
+ if (probe == 0x00)
+ nb26++;
if (probe == 0xff)
nb96++;
msleep(3);
}
- if (nb26 < 4 && nb96 < 4) {
- PDEBUG(D_PROBE, "No relevant answer ");
- PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655");
- PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640");
- PDEBUG(D_PROBE,
- "To force a sensor, add that line to "
- "/etc/modprobe.d/options.conf:");
- PDEBUG(D_PROBE, "options gspca_gl860 "
- "sensor=\"OV2640\" or \"OV9655\"");
+ if (nb26 < 4 && nb96 < 4)
return -1;
- }
- } else { /* probe = 0 */
- PDEBUG(D_PROBE, "No 0xff -> sensor MI2020");
+ } else {
+ PDEBUG(D_PROBE, "Not any 0xff -> MI2020");
sd->sensor = ID_MI2020;
}
}
diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h
index cef4e24c1e6..305061ff838 100644
--- a/drivers/media/video/gspca/gl860/gl860.h
+++ b/drivers/media/video/gspca/gl860/gl860.h
@@ -1,6 +1,7 @@
-/* @file gl860.h
- * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN
- * @date 2009-08-27
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver declarations
+ *
+ * 2009/10/14 Olivier LORIN <o.lorin@laposte.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
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 23d3fb77691..4076f8e5a6f 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 7, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 8, 0)
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
@@ -74,7 +74,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
#define PDEBUG_MODE(txt, pixfmt, w, h)
#endif
-/* specific memory types - !! should different from V4L2_MEMORY_xxx */
+/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */
#define GSPCA_MEMORY_READ 7
@@ -126,7 +126,6 @@ EXPORT_SYMBOL(gspca_get_i_frame);
static void fill_frame(struct gspca_dev *gspca_dev,
struct urb *urb)
{
- struct gspca_frame *frame;
u8 *data; /* address of data in the iso message */
int i, len, st;
cam_pkt_op pkt_scan;
@@ -135,21 +134,16 @@ static void fill_frame(struct gspca_dev *gspca_dev,
if (urb->status == -ESHUTDOWN)
return; /* disconnection */
#ifdef CONFIG_PM
- if (!gspca_dev->frozen)
+ if (gspca_dev->frozen)
+ return;
#endif
- PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
- return;
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ urb->status = 0;
+ goto resubmit;
}
pkt_scan = gspca_dev->sd_desc->pkt_scan;
for (i = 0; i < urb->number_of_packets; i++) {
- /* check the availability of the frame buffer */
- frame = gspca_get_i_frame(gspca_dev);
- if (!frame) {
- gspca_dev->last_packet_type = DISCARD_PACKET;
- break;
- }
-
/* check the packet status and length */
len = urb->iso_frame_desc[i].actual_length;
if (len == 0) {
@@ -171,9 +165,10 @@ static void fill_frame(struct gspca_dev *gspca_dev,
i, urb->iso_frame_desc[i].offset, len);
data = (u8 *) urb->transfer_buffer
+ urb->iso_frame_desc[i].offset;
- pkt_scan(gspca_dev, frame, data, len);
+ pkt_scan(gspca_dev, data, len);
}
+resubmit:
/* resubmit the URB */
st = usb_submit_urb(urb, GFP_ATOMIC);
if (st < 0)
@@ -201,7 +196,6 @@ static void isoc_irq(struct urb *urb)
static void bulk_irq(struct urb *urb)
{
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
- struct gspca_frame *frame;
int st;
PDEBUG(D_PACK, "bulk irq");
@@ -212,29 +206,22 @@ static void bulk_irq(struct urb *urb)
break;
case -ESHUTDOWN:
return; /* disconnection */
- case -ECONNRESET:
- urb->status = 0;
- break;
default:
#ifdef CONFIG_PM
- if (!gspca_dev->frozen)
+ if (gspca_dev->frozen)
+ return;
#endif
- PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
- return;
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ urb->status = 0;
+ goto resubmit;
}
- /* check the availability of the frame buffer */
- frame = gspca_get_i_frame(gspca_dev);
- if (!frame) {
- gspca_dev->last_packet_type = DISCARD_PACKET;
- } else {
- PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
- gspca_dev->sd_desc->pkt_scan(gspca_dev,
- frame,
- urb->transfer_buffer,
- urb->actual_length);
- }
+ PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+ gspca_dev->sd_desc->pkt_scan(gspca_dev,
+ urb->transfer_buffer,
+ urb->actual_length);
+resubmit:
/* resubmit the URB */
if (gspca_dev->cam.bulk_nurbs != 0) {
st = usb_submit_urb(urb, GFP_ATOMIC);
@@ -255,24 +242,27 @@ static void bulk_irq(struct urb *urb)
* DISCARD_PACKET invalidates the whole frame.
* On LAST_PACKET, a new frame is returned.
*/
-struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
- enum gspca_packet_type packet_type,
- struct gspca_frame *frame,
- const __u8 *data,
- int len)
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+ enum gspca_packet_type packet_type,
+ const u8 *data,
+ int len)
{
+ struct gspca_frame *frame;
int i, j;
PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
+ /* check the availability of the frame buffer */
+ frame = gspca_dev->cur_frame;
+ if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+ != V4L2_BUF_FLAG_QUEUED) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
/* when start of a new frame, if the current frame buffer
* is not queued, discard the whole frame */
if (packet_type == FIRST_PACKET) {
- if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
- != V4L2_BUF_FLAG_QUEUED) {
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return frame;
- }
frame->data_end = frame->data;
jiffies_to_timeval(get_jiffies_64(),
&frame->v4l2_buf.timestamp);
@@ -280,7 +270,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
} else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
if (packet_type == LAST_PACKET)
gspca_dev->last_packet_type = packet_type;
- return frame;
+ return;
}
/* append the packet to the frame buffer */
@@ -312,9 +302,9 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
i,
gspca_dev->fr_o);
j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
+ gspca_dev->cur_frame = &gspca_dev->frame[j];
}
- return frame;
+ return;
}
EXPORT_SYMBOL(gspca_frame_add);
@@ -395,6 +385,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev,
frame->v4l2_buf.m.offset = i * frsz;
}
gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
+ gspca_dev->cur_frame = &gspca_dev->frame[0];
gspca_dev->last_packet_type = DISCARD_PACKET;
gspca_dev->sequence = 0;
return 0;
@@ -475,10 +466,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
: USB_ENDPOINT_XFER_ISOC;
i = gspca_dev->alt; /* previous alt setting */
- while (--i >= 0) {
- ep = alt_xfer(&intf->altsetting[i], xfer);
- if (ep)
- break;
+ if (gspca_dev->cam.reverse_alts) {
+ while (++i < gspca_dev->nbalt) {
+ ep = alt_xfer(&intf->altsetting[i], xfer);
+ if (ep)
+ break;
+ }
+ } else {
+ while (--i >= 0) {
+ ep = alt_xfer(&intf->altsetting[i], xfer);
+ if (ep)
+ break;
+ }
}
if (ep == NULL) {
err("no transfer endpoint found");
@@ -599,7 +598,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
/* set the higher alternate setting and
* loop until urb submit succeeds */
- gspca_dev->alt = gspca_dev->nbalt;
+ if (gspca_dev->cam.reverse_alts)
+ gspca_dev->alt = 0;
+ else
+ gspca_dev->alt = gspca_dev->nbalt;
+
if (gspca_dev->sd_desc->isoc_init) {
ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
if (ret < 0)
@@ -641,15 +644,19 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
}
if (ret >= 0)
break;
- PDEBUG(D_ERR|D_STREAM,
- "usb_submit_urb alt %d err %d", gspca_dev->alt, ret);
gspca_dev->streaming = 0;
destroy_urbs(gspca_dev);
- if (ret != -ENOSPC)
+ if (ret != -ENOSPC) {
+ PDEBUG(D_ERR|D_STREAM,
+ "usb_submit_urb alt %d err %d",
+ gspca_dev->alt, ret);
goto out;
+ }
/* the bandwidth is not wide enough
* negociate or try a lower alternate setting */
+ PDEBUG(D_ERR|D_STREAM,
+ "bandwidth not wide enough - trying again");
msleep(20); /* wait for kill complete */
if (gspca_dev->sd_desc->isoc_nego) {
ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
@@ -980,7 +987,7 @@ static void gspca_release(struct video_device *vfd)
{
struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
- PDEBUG(D_STREAM, "device released");
+ PDEBUG(D_PROBE, "/dev/video%d released", gspca_dev->vdev.num);
kfree(gspca_dev->usb_buf);
kfree(gspca_dev);
@@ -991,7 +998,7 @@ static int dev_open(struct file *file)
struct gspca_dev *gspca_dev;
int ret;
- PDEBUG(D_STREAM, "%s open", current->comm);
+ PDEBUG(D_STREAM, "[%s] open", current->comm);
gspca_dev = (struct gspca_dev *) video_devdata(file);
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
@@ -1037,7 +1044,7 @@ static int dev_close(struct file *file)
{
struct gspca_dev *gspca_dev = file->private_data;
- PDEBUG(D_STREAM, "%s close", current->comm);
+ PDEBUG(D_STREAM, "[%s] close", current->comm);
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
gspca_dev->users--;
@@ -1138,10 +1145,13 @@ static int vidioc_queryctrl(struct file *file, void *priv,
}
} else {
ctrls = get_ctrl(gspca_dev, id);
+ i = ctrls - gspca_dev->sd_desc->ctrls;
}
if (ctrls == NULL)
return -EINVAL;
memcpy(q_ctrl, ctrls, sizeof *q_ctrl);
+ if (gspca_dev->ctrl_inac & (1 << i))
+ q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return 0;
}
@@ -1603,7 +1613,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma)
size -= PAGE_SIZE;
}
- vma->vm_ops = &gspca_vm_ops;
+ vma->vm_ops = (struct vm_operations_struct *) &gspca_vm_ops;
vma->vm_private_data = frame;
gspca_vm_open(vma);
ret = 0;
@@ -2001,11 +2011,15 @@ int gspca_dev_probe(struct usb_interface *intf,
PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct);
/* we don't handle multi-config cameras */
- if (dev->descriptor.bNumConfigurations != 1)
+ if (dev->descriptor.bNumConfigurations != 1) {
+ PDEBUG(D_ERR, "Too many config");
return -ENODEV;
+ }
interface = &intf->cur_altsetting->desc;
- if (interface->bInterfaceNumber > 0)
+ if (interface->bInterfaceNumber > 0) {
+ PDEBUG(D_ERR, "intf != 0");
return -ENODEV;
+ }
/* create the device */
if (dev_size < sizeof *gspca_dev)
@@ -2059,7 +2073,7 @@ int gspca_dev_probe(struct usb_interface *intf,
}
usb_set_intfdata(intf, gspca_dev);
- PDEBUG(D_PROBE, "probe ok");
+ PDEBUG(D_PROBE, "/dev/video%d created", gspca_dev->vdev.num);
return 0;
out:
kfree(gspca_dev->usb_buf);
@@ -2078,6 +2092,7 @@ void gspca_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ PDEBUG(D_PROBE, "/dev/video%d disconnect", gspca_dev->vdev.num);
mutex_lock(&gspca_dev->usb_lock);
gspca_dev->present = 0;
@@ -2096,7 +2111,7 @@ void gspca_disconnect(struct usb_interface *intf)
/* (this will call gspca_release() immediatly or on last close) */
video_unregister_device(&gspca_dev->vdev);
- PDEBUG(D_PROBE, "disconnect complete");
+/* PDEBUG(D_PROBE, "disconnect complete"); */
}
EXPORT_SYMBOL(gspca_disconnect);
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index 70b1fd83087..181617355ec 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -58,6 +58,7 @@ struct cam {
u8 npkt; /* number of packets in an ISOC message
* 0 is the default value: 32 packets */
u32 input_flags; /* value for ENUM_INPUT status flags */
+ char reverse_alts; /* Alt settings are in high to low order */
};
struct gspca_dev;
@@ -78,8 +79,7 @@ typedef int (*cam_streamparm_op) (struct gspca_dev *,
typedef int (*cam_qmnu_op) (struct gspca_dev *,
struct v4l2_querymenu *);
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
- struct gspca_frame *frame,
- __u8 *data,
+ u8 *data,
int len);
struct ctrl {
@@ -142,6 +142,7 @@ struct gspca_dev {
struct cam cam; /* device information */
const struct sd_desc *sd_desc; /* subdriver description */
unsigned ctrl_dis; /* disabled controls (bit map) */
+ unsigned ctrl_inac; /* inactive controls (bit map) */
#define USB_BUF_SZ 64
__u8 *usb_buf; /* buffer for USB exchanges */
@@ -149,6 +150,7 @@ struct gspca_dev {
__u8 *frbuf; /* buffer for nframes */
struct gspca_frame frame[GSPCA_MAX_FRAMES];
+ struct gspca_frame *cur_frame; /* frame beeing filled */
__u32 frsz; /* frame size */
char nframes; /* number of frames */
char fr_i; /* frame being filled */
@@ -189,11 +191,10 @@ int gspca_dev_probe(struct usb_interface *intf,
int dev_size,
struct module *module);
void gspca_disconnect(struct usb_interface *intf);
-struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
- enum gspca_packet_type packet_type,
- struct gspca_frame *frame,
- const __u8 *data,
- int len);
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+ enum gspca_packet_type packet_type,
+ const u8 *data,
+ int len);
struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev);
#ifdef CONFIG_PM
int gspca_suspend(struct usb_interface *intf, pm_message_t message);
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index a11c97ebeb0..2019b04f923 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -181,11 +181,9 @@ static void jlj_dostream(struct work_struct *work)
{
struct sd *dev = container_of(work, struct sd, work_struct);
struct gspca_dev *gspca_dev = &dev->gspca_dev;
- struct gspca_frame *frame;
int blocks_left; /* 0x200-sized blocks remaining in current frame. */
int size_in_blocks;
int act_len;
- int discarding = 0; /* true if we failed to get space for frame. */
int packet_type;
int ret;
u8 *buffer;
@@ -196,15 +194,6 @@ static void jlj_dostream(struct work_struct *work)
goto quit_stream;
}
while (gspca_dev->present && gspca_dev->streaming) {
- if (!gspca_dev->present)
- goto quit_stream;
- /* Start a new frame, and add the JPEG header, first thing */
- frame = gspca_get_i_frame(gspca_dev);
- if (frame && !discarding)
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- dev->jpeg_hdr, JPEG_HDR_SZ);
- else
- discarding = 1;
/*
* Now request data block 0. Line 0 reports the size
* to download, in blocks of size 0x200, and also tells the
@@ -222,14 +211,15 @@ static void jlj_dostream(struct work_struct *work)
size_in_blocks = buffer[0x0a];
blocks_left = buffer[0x0a] - 1;
PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
- packet_type = INTER_PACKET;
- if (frame && !discarding)
- /* Toss line 0 of data block 0, keep the rest. */
- gspca_frame_add(gspca_dev, packet_type,
- frame, buffer + FRAME_HEADER_LEN,
+
+ /* Start a new frame, and add the JPEG header, first thing */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ dev->jpeg_hdr, JPEG_HDR_SZ);
+ /* Toss line 0 of data block 0, keep the rest. */
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ buffer + FRAME_HEADER_LEN,
JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
- else
- discarding = 1;
+
while (blocks_left > 0) {
if (!gspca_dev->present)
goto quit_stream;
@@ -246,12 +236,8 @@ static void jlj_dostream(struct work_struct *work)
packet_type = LAST_PACKET;
else
packet_type = INTER_PACKET;
- if (frame && !discarding)
- gspca_frame_add(gspca_dev, packet_type,
- frame, buffer,
- JEILINJ_MAX_TRANSFER);
- else
- discarding = 1;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, JEILINJ_MAX_TRANSFER);
}
}
quit_stream:
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
index 7f1e5415850..844fc1d886d 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -274,8 +274,7 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev)
}
static void m5602_urb_complete(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame,
- __u8 *data, int len)
+ u8 *data, int len)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -295,19 +294,27 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
len -= 6;
/* Complete the last frame (if any) */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET,
- frame, data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
sd->frame_count++;
/* Create a new frame */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
PDEBUG(D_FRAM, "Starting new frame %d",
sd->frame_count);
} else {
- int cur_frame_len = frame->data_end - frame->data;
+ struct gspca_frame *frame;
+ int cur_frame_len;
+ frame = gspca_get_i_frame(gspca_dev);
+ if (frame == NULL) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ cur_frame_len = frame->data_end - frame->data;
/* Remove urb header */
data += 4;
len -= 4;
@@ -316,12 +323,12 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes",
sd->frame_count, len);
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ gspca_frame_add(gspca_dev, INTER_PACKET,
data, len);
} else if (frame->v4l2_buf.length - cur_frame_len > 0) {
/* Add the remaining data up to frame size */
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data,
- frame->v4l2_buf.length - cur_frame_len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data,
+ frame->v4l2_buf.length - cur_frame_len);
}
}
}
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index de769caf013..9cf8d68c71b 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -325,8 +325,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -348,11 +347,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|| data[5 + p] == 0x67) {
PDEBUG(D_PACK, "sof offset: %d len: %d",
p, len);
- frame = gspca_frame_add(gspca_dev, LAST_PACKET,
- frame, data, p);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, p);
/* put the JPEG header */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
data += p + 16;
len -= p + 16;
@@ -360,7 +359,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
}
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
index f8328b9efae..126d968dd9e 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/video/gspca/mr97310a.c
@@ -1,23 +1,30 @@
/*
* Mars MR97310A library
*
+ * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is
* Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
*
* Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+
* and for the routines for detecting and classifying these various cameras,
+ * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
*
+ * Support for the control settings for the CIF cameras is
+ * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li>
+ *
+ * Support for the control settings for the VGA cameras is
* Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
*
- * Acknowledgements:
+ * Several previously unsupported cameras are owned and have been tested by
+ * Hans de Goede <hdgoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li> and
+ * Theodore Kilgore <kilgota@auburn.edu> and
+ * Edmond Rodriguez <erodrig_97@yahoo.com> and
+ * Aurelien Jacobs <aurel@gnuage.org>
*
* The MR97311A support in gspca/mars.c has been helpful in understanding some
* of the registers in these cameras.
*
- * Hans de Goede <hdgoede@redhat.com> and
- * Thomas Kaiser <thomas@kaiser-linux.li>
- * have assisted with their experience. Each of them has also helped by
- * testing a previously unsupported camera.
- *
* 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
@@ -40,11 +47,9 @@
#define CAM_TYPE_CIF 0
#define CAM_TYPE_VGA 1
-#define MR97310A_BRIGHTNESS_MIN -254
-#define MR97310A_BRIGHTNESS_MAX 255
#define MR97310A_BRIGHTNESS_DEFAULT 0
-#define MR97310A_EXPOSURE_MIN 300
+#define MR97310A_EXPOSURE_MIN 0
#define MR97310A_EXPOSURE_MAX 4095
#define MR97310A_EXPOSURE_DEFAULT 1000
@@ -52,6 +57,10 @@
#define MR97310A_GAIN_MAX 31
#define MR97310A_GAIN_DEFAULT 25
+#define MR97310A_MIN_CLOCKDIV_MIN 3
+#define MR97310A_MIN_CLOCKDIV_MAX 8
+#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
+
MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,"
"Theodore Kilgore <kilgota@auburn.edu>");
MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
@@ -69,10 +78,12 @@ struct sd {
u8 cam_type; /* 0 is CIF and 1 is VGA */
u8 sensor_type; /* We use 0 and 1 here, too. */
u8 do_lcd_stop;
+ u8 adj_colors;
int brightness;
u16 exposure;
u8 gain;
+ u8 min_clockdiv;
};
struct sensor_w_data {
@@ -82,26 +93,31 @@ struct sensor_w_data {
int len;
};
+static void sd_stopN(struct gspca_dev *gspca_dev);
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
static void setbrightness(struct gspca_dev *gspca_dev);
static void setexposure(struct gspca_dev *gspca_dev);
static void setgain(struct gspca_dev *gspca_dev);
/* V4L2 controls supported by the driver */
static struct ctrl sd_ctrls[] = {
+/* Separate brightness control description for Argus QuickClix as it has
+ different limits from the other mr97310a cameras */
{
-#define BRIGHTNESS_IDX 0
+#define NORM_BRIGHTNESS_IDX 0
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
- .minimum = MR97310A_BRIGHTNESS_MIN,
- .maximum = MR97310A_BRIGHTNESS_MAX,
+ .minimum = -254,
+ .maximum = 255,
.step = 1,
.default_value = MR97310A_BRIGHTNESS_DEFAULT,
.flags = 0,
@@ -110,7 +126,22 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getbrightness,
},
{
-#define EXPOSURE_IDX 1
+#define ARGUS_QC_BRIGHTNESS_IDX 1
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+ .default_value = MR97310A_BRIGHTNESS_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ {
+#define EXPOSURE_IDX 2
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -125,7 +156,7 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getexposure,
},
{
-#define GAIN_IDX 2
+#define GAIN_IDX 3
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -139,6 +170,21 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setgain,
.get = sd_getgain,
},
+ {
+#define MIN_CLOCKDIV_IDX 4
+ {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum Clock Divider",
+ .minimum = MR97310A_MIN_CLOCKDIV_MIN,
+ .maximum = MR97310A_MIN_CLOCKDIV_MAX,
+ .step = 1,
+ .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
+ .flags = 0,
+ },
+ .set = sd_setmin_clockdiv,
+ .get = sd_getmin_clockdiv,
+ },
};
static const struct v4l2_pix_format vga_mode[] = {
@@ -230,12 +276,17 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
int rc;
buf = data;
- rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
+ confirm_reg = sd->sensor_type ? 0x13 : 0x11;
+ } else {
+ rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1);
+ confirm_reg = 0x11;
+ }
if (rc < 0)
return rc;
buf = 0x01;
- confirm_reg = sd->sensor_type ? 0x13 : 0x11;
rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1);
if (rc < 0)
return rc;
@@ -243,18 +294,26 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
return 0;
}
-static int cam_get_response16(struct gspca_dev *gspca_dev)
+static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
{
- __u8 *data = gspca_dev->usb_buf;
int err_code;
- data[0] = 0x21;
+ gspca_dev->usb_buf[0] = reg;
err_code = mr_write(gspca_dev, 1);
if (err_code < 0)
return err_code;
err_code = mr_read(gspca_dev, 16);
- return err_code;
+ if (err_code < 0)
+ return err_code;
+
+ if (verbose)
+ PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg,
+ gspca_dev->usb_buf[0],
+ gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2]);
+
+ return 0;
}
static int zero_the_pointer(struct gspca_dev *gspca_dev)
@@ -264,7 +323,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
u8 status = 0;
int tries = 0;
- err_code = cam_get_response16(gspca_dev);
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
if (err_code < 0)
return err_code;
@@ -275,7 +334,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
if (err_code < 0)
return err_code;
- err_code = cam_get_response16(gspca_dev);
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
if (err_code < 0)
return err_code;
@@ -285,7 +344,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
if (err_code < 0)
return err_code;
- err_code = cam_get_response16(gspca_dev);
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
if (err_code < 0)
return err_code;
@@ -295,7 +354,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
if (err_code < 0)
return err_code;
- err_code = cam_get_response16(gspca_dev);
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
if (err_code < 0)
return err_code;
@@ -306,7 +365,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
return err_code;
while (status != 0x0a && tries < 256) {
- err_code = cam_get_response16(gspca_dev);
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
status = data[0];
tries++;
if (err_code < 0)
@@ -323,7 +382,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
if (err_code < 0)
return err_code;
- err_code = cam_get_response16(gspca_dev);
+ err_code = cam_get_response16(gspca_dev, 0x21, 0);
status = data[0];
tries++;
if (err_code < 0)
@@ -342,89 +401,202 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
return 0;
}
-static u8 get_sensor_id(struct gspca_dev *gspca_dev)
+static int stream_start(struct gspca_dev *gspca_dev)
{
- int err_code;
-
- gspca_dev->usb_buf[0] = 0x1e;
- err_code = mr_write(gspca_dev, 1);
- if (err_code < 0)
- return err_code;
+ gspca_dev->usb_buf[0] = 0x01;
+ gspca_dev->usb_buf[1] = 0x01;
+ return mr_write(gspca_dev, 2);
+}
- err_code = mr_read(gspca_dev, 16);
- if (err_code < 0)
- return err_code;
+static void stream_stop(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->usb_buf[0] = 0x01;
+ gspca_dev->usb_buf[1] = 0x00;
+ if (mr_write(gspca_dev, 2) < 0)
+ PDEBUG(D_ERR, "Stream Stop failed");
+}
- PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]);
+static void lcd_stop(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->usb_buf[0] = 0x19;
+ gspca_dev->usb_buf[1] = 0x54;
+ if (mr_write(gspca_dev, 2) < 0)
+ PDEBUG(D_ERR, "LCD Stop failed");
+}
- return gspca_dev->usb_buf[0];
+static int isoc_enable(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->usb_buf[0] = 0x00;
+ gspca_dev->usb_buf[1] = 0x4d; /* ISOC transfering enable... */
+ return mr_write(gspca_dev, 2);
}
-/* this function is called at probe time */
+/* This function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
- __u8 *data = gspca_dev->usb_buf;
int err_code;
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
+ sd->do_lcd_stop = 0;
+
+ /* Several of the supported CIF cameras share the same USB ID but
+ * require different initializations and different control settings.
+ * The same is true of the VGA cameras. Therefore, we are forced
+ * to start the initialization process in order to determine which
+ * camera is present. Some of the supported cameras require the
+ * memory pointer to be set to 0 as the very first item of business
+ * or else they will not stream. So we do that immediately.
+ */
+ err_code = zero_the_pointer(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = stream_start(gspca_dev);
+ if (err_code < 0)
+ return err_code;
- if (id->idProduct == 0x010e) {
+ if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
sd->cam_type = CAM_TYPE_CIF;
cam->nmodes--;
-
- data[0] = 0x01;
- data[1] = 0x01;
- err_code = mr_write(gspca_dev, 2);
+ err_code = cam_get_response16(gspca_dev, 0x06, 1);
if (err_code < 0)
return err_code;
-
- msleep(200);
- data[0] = get_sensor_id(gspca_dev);
/*
- * Known CIF cameras. If you have another to report, please do
+ * All but one of the known CIF cameras share the same USB ID,
+ * but two different init routines are in use, and the control
+ * settings are different, too. We need to detect which camera
+ * of the two known varieties is connected!
*
- * Name byte just read sd->sensor_type
- * reported by
- * Sakar Spy-shot 0x28 T. Kilgore 0
- * Innovage 0xf5 (unstable) T. Kilgore 0
- * Vivitar Mini 0x53 H. De Goede 0
- * Vivitar Mini 0x04 / 0x24 E. Rodriguez 0
- * Vivitar Mini 0x08 T. Kilgore 1
- * Elta-Media 8212dc 0x23 T. Kaiser 1
- * Philips dig. keych. 0x37 T. Kilgore 1
+ * A list of known CIF cameras follows. They all report either
+ * 0002 for type 0 or 0003 for type 1.
+ * If you have another to report, please do
+ *
+ * Name sd->sensor_type reported by
+ *
+ * Sakar Spy-shot 0 T. Kilgore
+ * Innovage 0 T. Kilgore
+ * Vivitar Mini 0 H. De Goede
+ * Vivitar Mini 0 E. Rodriguez
+ * Vivitar Mini 1 T. Kilgore
+ * Elta-Media 8212dc 1 T. Kaiser
+ * Philips dig. keych. 1 T. Kilgore
+ * Trust Spyc@m 100 1 A. Jacobs
*/
- if ((data[0] & 0x78) == 8 ||
- ((data[0] & 0x2) == 0x2 && data[0] != 0x53))
- sd->sensor_type = 1;
- else
+ switch (gspca_dev->usb_buf[1]) {
+ case 2:
sd->sensor_type = 0;
-
+ break;
+ case 3:
+ sd->sensor_type = 1;
+ break;
+ default:
+ PDEBUG(D_ERR, "Unknown CIF Sensor id : %02x",
+ gspca_dev->usb_buf[1]);
+ return -ENODEV;
+ }
PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d",
sd->sensor_type);
+ } else {
+ sd->cam_type = CAM_TYPE_VGA;
- if (force_sensor_type != -1) {
- sd->sensor_type = !! force_sensor_type;
- PDEBUG(D_PROBE, "Forcing sensor type to: %d",
- sd->sensor_type);
+ err_code = cam_get_response16(gspca_dev, 0x07, 1);
+ if (err_code < 0)
+ return err_code;
+
+ /*
+ * Here is a table of the responses to the previous command
+ * from the known MR97310A VGA cameras.
+ *
+ * Name gspca_dev->usb_buf[] sd->sensor_type
+ * sd->do_lcd_stop
+ * Aiptek Pencam VGA+ 0300 0 1
+ * ION digital 0350 0 1
+ * Argus DC-1620 0450 1 0
+ * Argus QuickClix 0420 1 1
+ *
+ * Based upon these results, we assume default settings
+ * and then correct as necessary, as follows.
+ *
+ */
+
+ sd->sensor_type = 1;
+ sd->do_lcd_stop = 0;
+ sd->adj_colors = 0;
+ if ((gspca_dev->usb_buf[0] != 0x03) &&
+ (gspca_dev->usb_buf[0] != 0x04)) {
+ PDEBUG(D_ERR, "Unknown VGA Sensor id Byte 0: %02x",
+ gspca_dev->usb_buf[1]);
+ PDEBUG(D_ERR, "Defaults assumed, may not work");
+ PDEBUG(D_ERR, "Please report this");
+ }
+ /* Sakar Digital color needs to be adjusted. */
+ if ((gspca_dev->usb_buf[0] == 0x03) &&
+ (gspca_dev->usb_buf[1] == 0x50))
+ sd->adj_colors = 1;
+ if (gspca_dev->usb_buf[0] == 0x04) {
+ sd->do_lcd_stop = 1;
+ switch (gspca_dev->usb_buf[1]) {
+ case 0x50:
+ sd->sensor_type = 0;
+ PDEBUG(D_PROBE, "sensor_type corrected to 0");
+ break;
+ case 0x20:
+ /* Nothing to do here. */
+ break;
+ default:
+ PDEBUG(D_ERR,
+ "Unknown VGA Sensor id Byte 1: %02x",
+ gspca_dev->usb_buf[1]);
+ PDEBUG(D_ERR,
+ "Defaults assumed, may not work");
+ PDEBUG(D_ERR, "Please report this");
+ }
}
+ PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
+ sd->sensor_type);
+ }
+ /* Stop streaming as we've started it to probe the sensor type. */
+ sd_stopN(gspca_dev);
+
+ if (force_sensor_type != -1) {
+ sd->sensor_type = !!force_sensor_type;
+ PDEBUG(D_PROBE, "Forcing sensor type to: %d",
+ sd->sensor_type);
+ }
+ /* Setup controls depending on camera type */
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ /* No brightness for sensor_type 0 */
if (sd->sensor_type == 0)
- gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << ARGUS_QC_BRIGHTNESS_IDX);
+ else
+ gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << MIN_CLOCKDIV_IDX);
} else {
- sd->cam_type = CAM_TYPE_VGA;
- PDEBUG(D_PROBE, "MR97310A VGA camera detected");
- gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) |
- (1 << EXPOSURE_IDX) | (1 << GAIN_IDX);
+ /* All controls need to be disabled if VGA sensor_type is 0 */
+ if (sd->sensor_type == 0)
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+ (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+ (1 << EXPOSURE_IDX) |
+ (1 << GAIN_IDX) |
+ (1 << MIN_CLOCKDIV_IDX);
+ else if (sd->do_lcd_stop)
+ /* Argus QuickClix has different brightness limits */
+ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX);
+ else
+ gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX);
}
sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
sd->exposure = MR97310A_EXPOSURE_DEFAULT;
sd->gain = MR97310A_GAIN_DEFAULT;
+ sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
return 0;
}
@@ -455,11 +627,6 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
};
/* Note: Some of the above descriptions guessed from MR97113A driver */
- data[0] = 0x01;
- data[1] = 0x01;
- err_code = mr_write(gspca_dev, 2);
- if (err_code < 0)
- return err_code;
memcpy(data, startup_string, 11);
if (sd->sensor_type)
@@ -533,22 +700,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
ARRAY_SIZE(cif_sensor1_init_data));
}
- if (err_code < 0)
- return err_code;
-
- setbrightness(gspca_dev);
- setexposure(gspca_dev);
- setgain(gspca_dev);
-
- msleep(200);
-
- data[0] = 0x00;
- data[1] = 0x4d; /* ISOC transfering enable... */
- err_code = mr_write(gspca_dev, 2);
- if (err_code < 0)
- return err_code;
-
- return 0;
+ return err_code;
}
static int start_vga_cam(struct gspca_dev *gspca_dev)
@@ -558,84 +710,8 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
int err_code;
const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b,
0x00, 0x00, 0x00, 0x50, 0xc0};
-
/* What some of these mean is explained in start_cif_cam(), above */
- sd->sof_read = 0;
-
- /*
- * We have to know which camera we have, because the register writes
- * depend upon the camera. This test, run before we actually enter
- * the initialization routine, distinguishes most of the cameras, If
- * needed, another routine is done later, too.
- */
- memset(data, 0, 16);
- data[0] = 0x20;
- err_code = mr_write(gspca_dev, 1);
- if (err_code < 0)
- return err_code;
-
- err_code = mr_read(gspca_dev, 16);
- if (err_code < 0)
- return err_code;
-
- PDEBUG(D_PROBE, "Byte reported is %02x", data[0]);
-
- msleep(200);
- /*
- * Known VGA cameras. If you have another to report, please do
- *
- * Name byte just read sd->sensor_type
- * sd->do_lcd_stop
- * Aiptek Pencam VGA+ 0x31 0 1
- * ION digital 0x31 0 1
- * Argus DC-1620 0x30 1 0
- * Argus QuickClix 0x30 1 1 (not caught here)
- */
- sd->sensor_type = data[0] & 1;
- sd->do_lcd_stop = (~data[0]) & 1;
-
-
-
- /* Streaming setup begins here. */
-
-
- data[0] = 0x01;
- data[1] = 0x01;
- err_code = mr_write(gspca_dev, 2);
- if (err_code < 0)
- return err_code;
- /*
- * A second test can now resolve any remaining ambiguity in the
- * identification of the camera type,
- */
- if (!sd->sensor_type) {
- data[0] = get_sensor_id(gspca_dev);
- if (data[0] == 0x7f) {
- sd->sensor_type = 1;
- PDEBUG(D_PROBE, "sensor_type corrected to 1");
- }
- msleep(200);
- }
-
- if (force_sensor_type != -1) {
- sd->sensor_type = !! force_sensor_type;
- PDEBUG(D_PROBE, "Forcing sensor type to: %d",
- sd->sensor_type);
- }
-
- /*
- * Known VGA cameras.
- * This test is only run if the previous test returned 0x30, but
- * here is the information for all others, too, just for reference.
- *
- * Name byte just read sd->sensor_type
- *
- * Aiptek Pencam VGA+ 0xfb (this test not run) 1
- * ION digital 0xbd (this test not run) 1
- * Argus DC-1620 0xe5 (no change) 0
- * Argus QuickClix 0x7f (reclassified) 1
- */
memcpy(data, startup_string, 11);
if (!sd->sensor_type) {
data[5] = 0x00;
@@ -689,29 +765,44 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
ARRAY_SIZE(vga_sensor0_init_data));
} else { /* sd->sensor_type = 1 */
- const struct sensor_w_data vga_sensor1_init_data[] = {
+ const struct sensor_w_data color_adj[] = {
{0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
- 0x07, 0x00, 0x01}, 8},
+ /* adjusted blue, green, red gain correct
+ too much blue from the Sakar Digital */
+ 0x05, 0x01, 0x04}, 8}
+ };
+
+ const struct sensor_w_data color_no_adj[] = {
+ {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
+ /* default blue, green, red gain settings */
+ 0x07, 0x00, 0x01}, 8}
+ };
+
+ const struct sensor_w_data vga_sensor1_init_data[] = {
{0x11, 0x04, {0x01}, 1},
- /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */
- {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01,
+ {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
+ /* These settings may be better for some cameras */
+ /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
0x00, 0x0a}, 7},
{0x11, 0x04, {0x01}, 1},
{0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6},
{0x11, 0x04, {0x01}, 1},
{0, 0, {0}, 0}
};
+
+ if (sd->adj_colors)
+ err_code = sensor_write_regs(gspca_dev, color_adj,
+ ARRAY_SIZE(color_adj));
+ else
+ err_code = sensor_write_regs(gspca_dev, color_no_adj,
+ ARRAY_SIZE(color_no_adj));
+
+ if (err_code < 0)
+ return err_code;
+
err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
ARRAY_SIZE(vga_sensor1_init_data));
}
- if (err_code < 0)
- return err_code;
-
- msleep(200);
- data[0] = 0x00;
- data[1] = 0x4d; /* ISOC transfering enable... */
- err_code = mr_write(gspca_dev, 2);
-
return err_code;
}
@@ -719,97 +810,120 @@ static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err_code;
- struct cam *cam;
- cam = &gspca_dev->cam;
sd->sof_read = 0;
- /*
- * Some of the supported cameras require the memory pointer to be
- * set to 0, or else they will not stream.
- */
- zero_the_pointer(gspca_dev);
- msleep(200);
+
+ /* Some of the VGA cameras require the memory pointer
+ * to be set to 0 again. We have been forced to start the
+ * stream in sd_config() to detect the hardware, and closed it.
+ * Thus, we need here to do a completely fresh and clean start. */
+ err_code = zero_the_pointer(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = stream_start(gspca_dev);
+ if (err_code < 0)
+ return err_code;
+
if (sd->cam_type == CAM_TYPE_CIF) {
err_code = start_cif_cam(gspca_dev);
} else {
err_code = start_vga_cam(gspca_dev);
}
- return err_code;
+ if (err_code < 0)
+ return err_code;
+
+ setbrightness(gspca_dev);
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+
+ return isoc_enable(gspca_dev);
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int result;
-
- gspca_dev->usb_buf[0] = 1;
- gspca_dev->usb_buf[1] = 0;
- result = mr_write(gspca_dev, 2);
- if (result < 0)
- PDEBUG(D_ERR, "Camera Stop failed");
+ stream_stop(gspca_dev);
/* Not all the cams need this, but even if not, probably a good idea */
zero_the_pointer(gspca_dev);
- if (sd->do_lcd_stop) {
- gspca_dev->usb_buf[0] = 0x19;
- gspca_dev->usb_buf[1] = 0x54;
- result = mr_write(gspca_dev, 2);
- if (result < 0)
- PDEBUG(D_ERR, "Camera Stop failed");
- }
+ if (sd->do_lcd_stop)
+ lcd_stop(gspca_dev);
}
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
-
- if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
+ u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */
+ u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
+ const u8 quick_clix_table[] =
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15};
+ /*
+ * This control is disabled for CIF type 1 and VGA type 0 cameras.
+ * It does not quite act linearly for the Argus QuickClix camera,
+ * but it does control brightness. The values are 0 - 15 only, and
+ * the table above makes them act consecutively.
+ */
+ if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
+ (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
return;
- /* Note register 7 is also seen as 0x8x or 0xCx in dumps */
+ if (sd->cam_type == CAM_TYPE_VGA) {
+ sign_reg += 4;
+ value_reg += 4;
+ }
+
+ /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
if (sd->brightness > 0) {
- sensor_write1(gspca_dev, 7, 0x00);
+ sensor_write1(gspca_dev, sign_reg, 0x00);
val = sd->brightness;
} else {
- sensor_write1(gspca_dev, 7, 0x01);
- val = 257 - sd->brightness;
+ sensor_write1(gspca_dev, sign_reg, 0x01);
+ val = (257 - sd->brightness);
}
- sensor_write1(gspca_dev, 8, val);
+ /* Use lookup table for funky Argus QuickClix brightness */
+ if (sd->do_lcd_stop)
+ val = quick_clix_table[val];
+
+ sensor_write1(gspca_dev, value_reg, val);
}
static void setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
+ int exposure;
+ u8 buf[2];
if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
return;
- if (sd->sensor_type) {
- val = sd->exposure >> 4;
- sensor_write1(gspca_dev, 3, val);
- val = sd->exposure & 0xf;
- sensor_write1(gspca_dev, 4, val);
+ if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
+ /* This cam does not like exposure settings < 300,
+ so scale 0 - 4095 to 300 - 4095 */
+ exposure = (sd->exposure * 9267) / 10000 + 300;
+ sensor_write1(gspca_dev, 3, exposure >> 4);
+ sensor_write1(gspca_dev, 4, exposure & 0x0f);
} else {
- u8 clockdiv;
- int exposure;
-
/* We have both a clock divider and an exposure register.
We first calculate the clock divider, as that determines
- the maximum exposure and then we calculayte the exposure
+ the maximum exposure and then we calculate the exposure
register setting (which goes from 0 - 511).
Note our 0 - 4095 exposure is mapped to 0 - 511
milliseconds exposure time */
- clockdiv = (60 * sd->exposure + 7999) / 8000;
+ u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
/* Limit framerate to not exceed usb bandwidth */
- if (clockdiv < 3 && gspca_dev->width >= 320)
- clockdiv = 3;
+ if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
+ clockdiv = sd->min_clockdiv;
else if (clockdiv < 2)
clockdiv = 2;
+ if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4)
+ clockdiv = 4;
+
/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
@@ -819,9 +933,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
/* exposure register value is reversed! */
exposure = 511 - exposure;
+ buf[0] = exposure & 0xff;
+ buf[1] = exposure >> 8;
+ sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2);
sensor_write1(gspca_dev, 0x02, clockdiv);
- sensor_write1(gspca_dev, 0x0e, exposure & 0xff);
- sensor_write1(gspca_dev, 0x0f, exposure >> 8);
}
}
@@ -832,7 +947,7 @@ static void setgain(struct gspca_dev *gspca_dev)
if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
return;
- if (sd->sensor_type) {
+ if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
sensor_write1(gspca_dev, 0x0e, sd->gain);
} else {
sensor_write1(gspca_dev, 0x10, sd->gain);
@@ -893,17 +1008,35 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->min_clockdiv = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->min_clockdiv;
+ return 0;
+}
+
/* Include pac common sof detection functions */
#include "pac_common.h"
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
- int len) /* iso packet length */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
{
+ struct sd *sd = (struct sd *) gspca_dev;
unsigned char *sof;
- sof = pac_find_sof(gspca_dev, data, len);
+ sof = pac_find_sof(&sd->sof_read, data, len);
if (sof) {
int n;
@@ -913,15 +1046,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
n -= sizeof pac_sof_marker;
else
n = 0;
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ gspca_frame_add(gspca_dev, LAST_PACKET,
data, n);
/* Start next frame. */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
pac_sof_marker, sizeof pac_sof_marker);
len -= sof - data;
data = sof;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
/* sub-driver description */
@@ -938,6 +1071,7 @@ static const struct sd_desc sd_desc = {
/* -- module initialisation -- */
static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */
{USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */
{USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */
{USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index a5c190e9379..ad9ec339981 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -2,14 +2,19 @@
* OV519 driver
*
* Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
*
* This module is adapted from the ov51x-jpeg package, which itself
* was adapted from the ov511 driver.
*
* Original copyright for the ov511 driver is:
*
- * Copyright (c) 1999-2004 Mark W. McClelland
+ * Copyright (c) 1999-2006 Mark W. McClelland
* Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
*
* ov51x-jpeg original copyright is:
*
@@ -58,6 +63,8 @@ struct sd {
#define BRIDGE_OV518 2
#define BRIDGE_OV518PLUS 3
#define BRIDGE_OV519 4
+#define BRIDGE_OVFX2 5
+#define BRIDGE_W9968CF 6
#define BRIDGE_MASK 7
char invert_led;
@@ -73,6 +80,10 @@ struct sd {
__u8 vflip;
__u8 autobrightness;
__u8 freq;
+ __u8 quality;
+#define QUALITY_MIN 50
+#define QUALITY_MAX 70
+#define QUALITY_DEF 50
__u8 stopped; /* Streaming is temporarily paused */
@@ -81,17 +92,31 @@ struct sd {
char sensor; /* Type of image sensor chip (SEN_*) */
#define SEN_UNKNOWN 0
-#define SEN_OV6620 1
-#define SEN_OV6630 2
-#define SEN_OV66308AF 3
-#define SEN_OV7610 4
-#define SEN_OV7620 5
-#define SEN_OV7640 6
-#define SEN_OV7670 7
-#define SEN_OV76BE 8
-#define SEN_OV8610 9
+#define SEN_OV2610 1
+#define SEN_OV3610 2
+#define SEN_OV6620 3
+#define SEN_OV6630 4
+#define SEN_OV66308AF 5
+#define SEN_OV7610 6
+#define SEN_OV7620 7
+#define SEN_OV7640 8
+#define SEN_OV7670 9
+#define SEN_OV76BE 10
+#define SEN_OV8610 11
+
+ u8 sensor_addr;
+ int sensor_width;
+ int sensor_height;
+ int sensor_reg_cache[256];
+
+ u8 *jpeg_hdr;
};
+/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
+ the ov sensors which is already present here. When we have the time we
+ really should move the sensor drivers to v4l2 sub drivers. */
+#include "w996Xcf.c"
+
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
@@ -345,6 +370,75 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
.priv = 0},
};
+static const struct v4l2_pix_format ovfx2_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_cif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_ov2610_mode[] = {
+ {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1600,
+ .sizeimage = 1600 * 1200,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1024,
+ .sizeimage = 1024 * 768,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1600,
+ .sizeimage = 1600 * 1200,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 2048,
+ .sizeimage = 2048 * 1536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+
/* Registers common to OV511 / OV518 */
#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
#define R51x_SYS_RESET 0x50
@@ -406,6 +500,30 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */
+/*
+ * The FX2 chip does not give us a zero length read at end of frame.
+ * It does, however, give a short read at the end of a frame, if
+ * neccessary, rather than run two frames together.
+ *
+ * By choosing the right bulk transfer size, we are guaranteed to always
+ * get a short read for the last read of each frame. Frame sizes are
+ * always a composite number (width * height, or a multiple) so if we
+ * choose a prime number, we are guaranteed that the last read of a
+ * frame will be short.
+ *
+ * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB,
+ * otherwise EOVERFLOW "babbling" errors occur. I have not been able
+ * to figure out why. [PMiller]
+ *
+ * The constant (13 * 4096) is the largest "prime enough" number less than 64KB.
+ *
+ * It isn't enough to know the number of bytes per frame, in case we
+ * have data dropouts or buffer overruns (even though the FX2 double
+ * buffers, there are some pretty strict real time constraints for
+ * isochronous transfer for larger frame sizes).
+ */
+#define OVFX2_BULK_SIZE (13 * 4096)
+
/* I2C registers */
#define R51x_I2C_W_SID 0x41
#define R51x_I2C_SADDR_3 0x42
@@ -413,9 +531,11 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
#define R51x_I2C_R_SID 0x44
#define R51x_I2C_DATA 0x45
#define R518_I2C_CTL 0x47 /* OV518(+) only */
+#define OVFX2_I2C_ADDR 0x00
/* I2C ADDRESSES */
#define OV7xx0_SID 0x42
+#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */
#define OV8xx0_SID 0xa0
#define OV6xx0_SID 0xc0
@@ -508,6 +628,696 @@ struct ov_i2c_regvals {
__u8 val;
};
+/* Settings for OV2610 camera chip */
+static const struct ov_i2c_regvals norm_2610[] =
+{
+ { 0x12, 0x80 }, /* reset */
+};
+
+static const struct ov_i2c_regvals norm_3620b[] =
+{
+ /*
+ * From the datasheet: "Note that after writing to register COMH
+ * (0x12) to change the sensor mode, registers related to the
+ * sensor’s cropping window will be reset back to their default
+ * values."
+ *
+ * "wait 4096 external clock ... to make sure the sensor is
+ * stable and ready to access registers" i.e. 160us at 24MHz
+ */
+
+ { 0x12, 0x80 }, /* COMH reset */
+ { 0x12, 0x00 }, /* QXGA, master */
+
+ /*
+ * 11 CLKRC "Clock Rate Control"
+ * [7] internal frequency doublers: on
+ * [6] video port mode: master
+ * [5:0] clock divider: 1
+ */
+ { 0x11, 0x80 },
+
+ /*
+ * 13 COMI "Common Control I"
+ * = 192 (0xC0) 11000000
+ * COMI[7] "AEC speed selection"
+ * = 1 (0x01) 1....... "Faster AEC correction"
+ * COMI[6] "AEC speed step selection"
+ * = 1 (0x01) .1...... "Big steps, fast"
+ * COMI[5] "Banding filter on off"
+ * = 0 (0x00) ..0..... "Off"
+ * COMI[4] "Banding filter option"
+ * = 0 (0x00) ...0.... "Main clock is 48 MHz and
+ * the PLL is ON"
+ * COMI[3] "Reserved"
+ * = 0 (0x00) ....0...
+ * COMI[2] "AGC auto manual control selection"
+ * = 0 (0x00) .....0.. "Manual"
+ * COMI[1] "AWB auto manual control selection"
+ * = 0 (0x00) ......0. "Manual"
+ * COMI[0] "Exposure control"
+ * = 0 (0x00) .......0 "Manual"
+ */
+ { 0x13, 0xC0 },
+
+ /*
+ * 09 COMC "Common Control C"
+ * = 8 (0x08) 00001000
+ * COMC[7:5] "Reserved"
+ * = 0 (0x00) 000.....
+ * COMC[4] "Sleep Mode Enable"
+ * = 0 (0x00) ...0.... "Normal mode"
+ * COMC[3:2] "Sensor sampling reset timing selection"
+ * = 2 (0x02) ....10.. "Longer reset time"
+ * COMC[1:0] "Output drive current select"
+ * = 0 (0x00) ......00 "Weakest"
+ */
+ { 0x09, 0x08 },
+
+ /*
+ * 0C COMD "Common Control D"
+ * = 8 (0x08) 00001000
+ * COMD[7] "Reserved"
+ * = 0 (0x00) 0.......
+ * COMD[6] "Swap MSB and LSB at the output port"
+ * = 0 (0x00) .0...... "False"
+ * COMD[5:3] "Reserved"
+ * = 1 (0x01) ..001...
+ * COMD[2] "Output Average On Off"
+ * = 0 (0x00) .....0.. "Output Normal"
+ * COMD[1] "Sensor precharge voltage selection"
+ * = 0 (0x00) ......0. "Selects internal
+ * reference precharge
+ * voltage"
+ * COMD[0] "Snapshot option"
+ * = 0 (0x00) .......0 "Enable live video output
+ * after snapshot sequence"
+ */
+ { 0x0c, 0x08 },
+
+ /*
+ * 0D COME "Common Control E"
+ * = 161 (0xA1) 10100001
+ * COME[7] "Output average option"
+ * = 1 (0x01) 1....... "Output average of 4 pixels"
+ * COME[6] "Anti-blooming control"
+ * = 0 (0x00) .0...... "Off"
+ * COME[5:3] "Reserved"
+ * = 4 (0x04) ..100...
+ * COME[2] "Clock output power down pin status"
+ * = 0 (0x00) .....0.. "Tri-state data output pin
+ * on power down"
+ * COME[1] "Data output pin status selection at power down"
+ * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK,
+ * HREF, and CHSYNC pins on
+ * power down"
+ * COME[0] "Auto zero circuit select"
+ * = 1 (0x01) .......1 "On"
+ */
+ { 0x0d, 0xA1 },
+
+ /*
+ * 0E COMF "Common Control F"
+ * = 112 (0x70) 01110000
+ * COMF[7] "System clock selection"
+ * = 0 (0x00) 0....... "Use 24 MHz system clock"
+ * COMF[6:4] "Reserved"
+ * = 7 (0x07) .111....
+ * COMF[3] "Manual auto negative offset canceling selection"
+ * = 0 (0x00) ....0... "Auto detect negative
+ * offset and cancel it"
+ * COMF[2:0] "Reserved"
+ * = 0 (0x00) .....000
+ */
+ { 0x0e, 0x70 },
+
+ /*
+ * 0F COMG "Common Control G"
+ * = 66 (0x42) 01000010
+ * COMG[7] "Optical black output selection"
+ * = 0 (0x00) 0....... "Disable"
+ * COMG[6] "Black level calibrate selection"
+ * = 1 (0x01) .1...... "Use optical black pixels
+ * to calibrate"
+ * COMG[5:4] "Reserved"
+ * = 0 (0x00) ..00....
+ * COMG[3] "Channel offset adjustment"
+ * = 0 (0x00) ....0... "Disable offset adjustment"
+ * COMG[2] "ADC black level calibration option"
+ * = 0 (0x00) .....0.. "Use B/G line and G/R
+ * line to calibrate each
+ * channel's black level"
+ * COMG[1] "Reserved"
+ * = 1 (0x01) ......1.
+ * COMG[0] "ADC black level calibration enable"
+ * = 0 (0x00) .......0 "Disable"
+ */
+ { 0x0f, 0x42 },
+
+ /*
+ * 14 COMJ "Common Control J"
+ * = 198 (0xC6) 11000110
+ * COMJ[7:6] "AGC gain ceiling"
+ * = 3 (0x03) 11...... "8x"
+ * COMJ[5:4] "Reserved"
+ * = 0 (0x00) ..00....
+ * COMJ[3] "Auto banding filter"
+ * = 0 (0x00) ....0... "Banding filter is always
+ * on off depending on
+ * COMI[5] setting"
+ * COMJ[2] "VSYNC drop option"
+ * = 1 (0x01) .....1.. "SYNC is dropped if frame
+ * data is dropped"
+ * COMJ[1] "Frame data drop"
+ * = 1 (0x01) ......1. "Drop frame data if
+ * exposure is not within
+ * tolerance. In AEC mode,
+ * data is normally dropped
+ * when data is out of
+ * range."
+ * COMJ[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x14, 0xC6 },
+
+ /*
+ * 15 COMK "Common Control K"
+ * = 2 (0x02) 00000010
+ * COMK[7] "CHSYNC pin output swap"
+ * = 0 (0x00) 0....... "CHSYNC"
+ * COMK[6] "HREF pin output swap"
+ * = 0 (0x00) .0...... "HREF"
+ * COMK[5] "PCLK output selection"
+ * = 0 (0x00) ..0..... "PCLK always output"
+ * COMK[4] "PCLK edge selection"
+ * = 0 (0x00) ...0.... "Data valid on falling edge"
+ * COMK[3] "HREF output polarity"
+ * = 0 (0x00) ....0... "positive"
+ * COMK[2] "Reserved"
+ * = 0 (0x00) .....0..
+ * COMK[1] "VSYNC polarity"
+ * = 1 (0x01) ......1. "negative"
+ * COMK[0] "HSYNC polarity"
+ * = 0 (0x00) .......0 "positive"
+ */
+ { 0x15, 0x02 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 9 (0x09) 00001001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 0 (0x00) ...0.... "normal current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x09 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 80 (0x50) 01010000
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 0 (0x00) ....0000
+ */
+ { 0x34, 0x50 },
+
+ /*
+ * 36 VCHG "Sensor Precharge Voltage Control"
+ * = 0 (0x00) 00000000
+ * VCHG[7] "Reserved"
+ * = 0 (0x00) 0.......
+ * VCHG[6:4] "Sensor precharge voltage control"
+ * = 0 (0x00) .000....
+ * VCHG[3:0] "Sensor array common reference"
+ * = 0 (0x00) ....0000
+ */
+ { 0x36, 0x00 },
+
+ /*
+ * 37 ADC "ADC Reference Control"
+ * = 4 (0x04) 00000100
+ * ADC[7:4] "Reserved"
+ * = 0 (0x00) 0000....
+ * ADC[3] "ADC input signal range"
+ * = 0 (0x00) ....0... "Input signal 1.0x"
+ * ADC[2:0] "ADC range control"
+ * = 4 (0x04) .....100
+ */
+ { 0x37, 0x04 },
+
+ /*
+ * 38 ACOM "Analog Common Ground"
+ * = 82 (0x52) 01010010
+ * ACOM[7] "Analog gain control"
+ * = 0 (0x00) 0....... "Gain 1x"
+ * ACOM[6] "Analog black level calibration"
+ * = 1 (0x01) .1...... "On"
+ * ACOM[5:0] "Reserved"
+ * = 18 (0x12) ..010010
+ */
+ { 0x38, 0x52 },
+
+ /*
+ * 3A FREFA "Internal Reference Adjustment"
+ * = 0 (0x00) 00000000
+ * FREFA[7:0] "Range"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3a, 0x00 },
+
+ /*
+ * 3C FVOPT "Internal Reference Adjustment"
+ * = 31 (0x1F) 00011111
+ * FVOPT[7:0] "Range"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x3c, 0x1F },
+
+ /*
+ * 44 Undocumented = 0 (0x00) 00000000
+ * 44[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x44, 0x00 },
+
+ /*
+ * 40 Undocumented = 0 (0x00) 00000000
+ * 40[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x40, 0x00 },
+
+ /*
+ * 41 Undocumented = 0 (0x00) 00000000
+ * 41[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x41, 0x00 },
+
+ /*
+ * 42 Undocumented = 0 (0x00) 00000000
+ * 42[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x42, 0x00 },
+
+ /*
+ * 43 Undocumented = 0 (0x00) 00000000
+ * 43[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x43, 0x00 },
+
+ /*
+ * 45 Undocumented = 128 (0x80) 10000000
+ * 45[7:0] "It's a secret"
+ * = 128 (0x80) 10000000
+ */
+ { 0x45, 0x80 },
+
+ /*
+ * 48 Undocumented = 192 (0xC0) 11000000
+ * 48[7:0] "It's a secret"
+ * = 192 (0xC0) 11000000
+ */
+ { 0x48, 0xC0 },
+
+ /*
+ * 49 Undocumented = 25 (0x19) 00011001
+ * 49[7:0] "It's a secret"
+ * = 25 (0x19) 00011001
+ */
+ { 0x49, 0x19 },
+
+ /*
+ * 4B Undocumented = 128 (0x80) 10000000
+ * 4B[7:0] "It's a secret"
+ * = 128 (0x80) 10000000
+ */
+ { 0x4B, 0x80 },
+
+ /*
+ * 4D Undocumented = 196 (0xC4) 11000100
+ * 4D[7:0] "It's a secret"
+ * = 196 (0xC4) 11000100
+ */
+ { 0x4D, 0xC4 },
+
+ /*
+ * 35 VREF "Reference Voltage Control"
+ * = 76 (0x4C) 01001100
+ * VREF[7:5] "Column high reference control"
+ * = 2 (0x02) 010..... "higher voltage"
+ * VREF[4:2] "Column low reference control"
+ * = 3 (0x03) ...011.. "Highest voltage"
+ * VREF[1:0] "Reserved"
+ * = 0 (0x00) ......00
+ */
+ { 0x35, 0x4C },
+
+ /*
+ * 3D Undocumented = 0 (0x00) 00000000
+ * 3D[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3D, 0x00 },
+
+ /*
+ * 3E Undocumented = 0 (0x00) 00000000
+ * 3E[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3E, 0x00 },
+
+ /*
+ * 3B FREFB "Internal Reference Adjustment"
+ * = 24 (0x18) 00011000
+ * FREFB[7:0] "Range"
+ * = 24 (0x18) 00011000
+ */
+ { 0x3b, 0x18 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 25 (0x19) 00011001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 1 (0x01) ...1.... "double current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x19 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 90 (0x5A) 01011010
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 10 (0x0A) ....1010
+ */
+ { 0x34, 0x5A },
+
+ /*
+ * 3B FREFB "Internal Reference Adjustment"
+ * = 0 (0x00) 00000000
+ * FREFB[7:0] "Range"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3b, 0x00 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 9 (0x09) 00001001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 0 (0x00) ...0.... "normal current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x09 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 80 (0x50) 01010000
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 0 (0x00) ....0000
+ */
+ { 0x34, 0x50 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 64 (0x40) 01000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 4 (0x04) .100.... "XGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x40 },
+
+ /*
+ * 17 HREFST "Horizontal window start"
+ * = 31 (0x1F) 00011111
+ * HREFST[7:0] "Horizontal window start, 8 MSBs"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x17, 0x1F },
+
+ /*
+ * 18 HREFEND "Horizontal window end"
+ * = 95 (0x5F) 01011111
+ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+ * = 95 (0x5F) 01011111
+ */
+ { 0x18, 0x5F },
+
+ /*
+ * 19 VSTRT "Vertical window start"
+ * = 0 (0x00) 00000000
+ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+ * = 0 (0x00) 00000000
+ */
+ { 0x19, 0x00 },
+
+ /*
+ * 1A VEND "Vertical window end"
+ * = 96 (0x60) 01100000
+ * VEND[7:0] "Vertical Window End, 8 MSBs"
+ * = 96 (0x60) 01100000
+ */
+ { 0x1a, 0x60 },
+
+ /*
+ * 32 COMM "Common Control M"
+ * = 18 (0x12) 00010010
+ * COMM[7:6] "Pixel clock divide option"
+ * = 0 (0x00) 00...... "/1"
+ * COMM[5:3] "Horizontal window end position, 3 LSBs"
+ * = 2 (0x02) ..010...
+ * COMM[2:0] "Horizontal window start position, 3 LSBs"
+ * = 2 (0x02) .....010
+ */
+ { 0x32, 0x12 },
+
+ /*
+ * 03 COMA "Common Control A"
+ * = 74 (0x4A) 01001010
+ * COMA[7:4] "AWB Update Threshold"
+ * = 4 (0x04) 0100....
+ * COMA[3:2] "Vertical window end line control 2 LSBs"
+ * = 2 (0x02) ....10..
+ * COMA[1:0] "Vertical window start line control 2 LSBs"
+ * = 2 (0x02) ......10
+ */
+ { 0x03, 0x4A },
+
+ /*
+ * 11 CLKRC "Clock Rate Control"
+ * = 128 (0x80) 10000000
+ * CLKRC[7] "Internal frequency doublers on off seclection"
+ * = 1 (0x01) 1....... "On"
+ * CLKRC[6] "Digital video master slave selection"
+ * = 0 (0x00) .0...... "Master mode, sensor
+ * provides PCLK"
+ * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }"
+ * = 0 (0x00) ..000000
+ */
+ { 0x11, 0x80 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 0 (0x00) 00000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 0 (0x00) .000.... "QXGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x00 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 64 (0x40) 01000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 4 (0x04) .100.... "XGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x40 },
+
+ /*
+ * 17 HREFST "Horizontal window start"
+ * = 31 (0x1F) 00011111
+ * HREFST[7:0] "Horizontal window start, 8 MSBs"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x17, 0x1F },
+
+ /*
+ * 18 HREFEND "Horizontal window end"
+ * = 95 (0x5F) 01011111
+ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+ * = 95 (0x5F) 01011111
+ */
+ { 0x18, 0x5F },
+
+ /*
+ * 19 VSTRT "Vertical window start"
+ * = 0 (0x00) 00000000
+ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+ * = 0 (0x00) 00000000
+ */
+ { 0x19, 0x00 },
+
+ /*
+ * 1A VEND "Vertical window end"
+ * = 96 (0x60) 01100000
+ * VEND[7:0] "Vertical Window End, 8 MSBs"
+ * = 96 (0x60) 01100000
+ */
+ { 0x1a, 0x60 },
+
+ /*
+ * 32 COMM "Common Control M"
+ * = 18 (0x12) 00010010
+ * COMM[7:6] "Pixel clock divide option"
+ * = 0 (0x00) 00...... "/1"
+ * COMM[5:3] "Horizontal window end position, 3 LSBs"
+ * = 2 (0x02) ..010...
+ * COMM[2:0] "Horizontal window start position, 3 LSBs"
+ * = 2 (0x02) .....010
+ */
+ { 0x32, 0x12 },
+
+ /*
+ * 03 COMA "Common Control A"
+ * = 74 (0x4A) 01001010
+ * COMA[7:4] "AWB Update Threshold"
+ * = 4 (0x04) 0100....
+ * COMA[3:2] "Vertical window end line control 2 LSBs"
+ * = 2 (0x02) ....10..
+ * COMA[1:0] "Vertical window start line control 2 LSBs"
+ * = 2 (0x02) ......10
+ */
+ { 0x03, 0x4A },
+
+ /*
+ * 02 RED "Red Gain Control"
+ * = 175 (0xAF) 10101111
+ * RED[7] "Action"
+ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+ * RED[6:0] "Value"
+ * = 47 (0x2F) .0101111
+ */
+ { 0x02, 0xAF },
+
+ /*
+ * 2D ADDVSL "VSYNC Pulse Width"
+ * = 210 (0xD2) 11010010
+ * ADDVSL[7:0] "VSYNC pulse width, LSB"
+ * = 210 (0xD2) 11010010
+ */
+ { 0x2d, 0xD2 },
+
+ /*
+ * 00 GAIN = 24 (0x18) 00011000
+ * GAIN[7:6] "Reserved"
+ * = 0 (0x00) 00......
+ * GAIN[5] "Double"
+ * = 0 (0x00) ..0..... "False"
+ * GAIN[4] "Double"
+ * = 1 (0x01) ...1.... "True"
+ * GAIN[3:0] "Range"
+ * = 8 (0x08) ....1000
+ */
+ { 0x00, 0x18 },
+
+ /*
+ * 01 BLUE "Blue Gain Control"
+ * = 240 (0xF0) 11110000
+ * BLUE[7] "Action"
+ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+ * BLUE[6:0] "Value"
+ * = 112 (0x70) .1110000
+ */
+ { 0x01, 0xF0 },
+
+ /*
+ * 10 AEC "Automatic Exposure Control"
+ * = 10 (0x0A) 00001010
+ * AEC[7:0] "Automatic Exposure Control, 8 MSBs"
+ * = 10 (0x0A) 00001010
+ */
+ { 0x10, 0x0A },
+
+ { 0xE1, 0x67 },
+ { 0xE3, 0x03 },
+ { 0xE4, 0x26 },
+ { 0xE5, 0x3E },
+ { 0xF8, 0x01 },
+ { 0xFF, 0x01 },
+};
+
static const struct ov_i2c_regvals norm_6x20[] = {
{ 0x12, 0x80 }, /* reset */
{ 0x11, 0x01 },
@@ -678,6 +1488,7 @@ static const struct ov_i2c_regvals norm_7610[] = {
};
static const struct ov_i2c_regvals norm_7620[] = {
+ { 0x12, 0x80 }, /* reset */
{ 0x00, 0x00 }, /* gain */
{ 0x01, 0x80 }, /* blue gain */
{ 0x02, 0x80 }, /* red gain */
@@ -1042,10 +1853,28 @@ static unsigned char ov7670_abs_to_sm(unsigned char v)
}
/* Write a OV519 register */
-static int reg_w(struct sd *sd, __u16 index, __u8 value)
+static int reg_w(struct sd *sd, __u16 index, __u16 value)
{
- int ret;
- int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 2 : 1;
+ int ret, req = 0;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ req = 2;
+ break;
+ case BRIDGE_OVFX2:
+ req = 0x0a;
+ /* fall through */
+ case BRIDGE_W9968CF:
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ goto leave;
+ default:
+ req = 1;
+ }
sd->gspca_dev.usb_buf[0] = value;
ret = usb_control_msg(sd->gspca_dev.dev,
@@ -1054,17 +1883,35 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index,
sd->gspca_dev.usb_buf, 1, 500);
- if (ret < 0)
- PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value);
- return ret;
+leave:
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed",
+ value, index);
+ return ret;
+ }
+
+ PDEBUG(D_USBO, "Write reg 0x%04x -> [0x%02x]", value, index);
+ return 0;
}
-/* Read from a OV519 register */
+/* Read from a OV519 register, note not valid for the w9968cf!! */
/* returns: negative is error, pos or zero is data */
static int reg_r(struct sd *sd, __u16 index)
{
int ret;
- int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 3 : 1;
+ int req;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ req = 3;
+ break;
+ case BRIDGE_OVFX2:
+ req = 0x0b;
+ break;
+ default:
+ req = 1;
+ }
ret = usb_control_msg(sd->gspca_dev.dev,
usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
@@ -1072,10 +1919,12 @@ static int reg_r(struct sd *sd, __u16 index)
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, sd->gspca_dev.usb_buf, 1, 500);
- if (ret >= 0)
+ if (ret >= 0) {
ret = sd->gspca_dev.usb_buf[0];
- else
+ PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret);
+ } else
PDEBUG(D_ERR, "Read reg [0x%02x] failed", index);
+
return ret;
}
@@ -1095,6 +1944,7 @@ static int reg_r8(struct sd *sd,
ret = sd->gspca_dev.usb_buf[0];
else
PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index);
+
return ret;
}
@@ -1140,9 +1990,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n)
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index,
sd->gspca_dev.usb_buf, n, 500);
- if (ret < 0)
+ if (ret < 0) {
PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value);
- return ret;
+ return ret;
+ }
+
+ return 0;
}
static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value)
@@ -1324,32 +2177,110 @@ static int ov518_i2c_r(struct sd *sd, __u8 reg)
return value;
}
+static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value)
+{
+ int ret;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ (__u16)value, (__u16)reg, NULL, 0, 500);
+
+ if (ret < 0) {
+ PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
+ return ret;
+ }
+
+ PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
+ return 0;
+}
+
+static int ovfx2_i2c_r(struct sd *sd, __u8 reg)
+{
+ int ret;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ 0x03,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (__u16)reg, sd->gspca_dev.usb_buf, 1, 500);
+
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+ PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, ret);
+ } else
+ PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
+
+ return ret;
+}
+
static int i2c_w(struct sd *sd, __u8 reg, __u8 value)
{
+ int ret = -1;
+
+ if (sd->sensor_reg_cache[reg] == value)
+ return 0;
+
switch (sd->bridge) {
case BRIDGE_OV511:
case BRIDGE_OV511PLUS:
- return ov511_i2c_w(sd, reg, value);
+ ret = ov511_i2c_w(sd, reg, value);
+ break;
case BRIDGE_OV518:
case BRIDGE_OV518PLUS:
case BRIDGE_OV519:
- return ov518_i2c_w(sd, reg, value);
+ ret = ov518_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_OVFX2:
+ ret = ovfx2_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_W9968CF:
+ ret = w9968cf_i2c_w(sd, reg, value);
+ break;
}
- return -1; /* Should never happen */
+
+ if (ret >= 0) {
+ /* Up on sensor reset empty the register cache */
+ if (reg == 0x12 && (value & 0x80))
+ memset(sd->sensor_reg_cache, -1,
+ sizeof(sd->sensor_reg_cache));
+ else
+ sd->sensor_reg_cache[reg] = value;
+ }
+
+ return ret;
}
static int i2c_r(struct sd *sd, __u8 reg)
{
+ int ret = -1;
+
+ if (sd->sensor_reg_cache[reg] != -1)
+ return sd->sensor_reg_cache[reg];
+
switch (sd->bridge) {
case BRIDGE_OV511:
case BRIDGE_OV511PLUS:
- return ov511_i2c_r(sd, reg);
+ ret = ov511_i2c_r(sd, reg);
+ break;
case BRIDGE_OV518:
case BRIDGE_OV518PLUS:
case BRIDGE_OV519:
- return ov518_i2c_r(sd, reg);
+ ret = ov518_i2c_r(sd, reg);
+ break;
+ case BRIDGE_OVFX2:
+ ret = ovfx2_i2c_r(sd, reg);
+ break;
+ case BRIDGE_W9968CF:
+ ret = w9968cf_i2c_r(sd, reg);
+ break;
}
- return -1; /* Should never happen */
+
+ if (ret >= 0)
+ sd->sensor_reg_cache[reg] = ret;
+
+ return ret;
}
/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
@@ -1389,6 +2320,10 @@ static inline int ov51x_stop(struct sd *sd)
return reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a);
case BRIDGE_OV519:
return reg_w(sd, OV519_SYS_RESET1, 0x0f);
+ case BRIDGE_OVFX2:
+ return reg_w_mask(sd, 0x0f, 0x00, 0x02);
+ case BRIDGE_W9968CF:
+ return reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
}
return 0;
@@ -1418,18 +2353,27 @@ static inline int ov51x_restart(struct sd *sd)
return reg_w(sd, R51x_SYS_RESET, 0x00);
case BRIDGE_OV519:
return reg_w(sd, OV519_SYS_RESET1, 0x00);
+ case BRIDGE_OVFX2:
+ return reg_w_mask(sd, 0x0f, 0x02, 0x02);
+ case BRIDGE_W9968CF:
+ return reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
}
return 0;
}
+static int ov51x_set_slave_ids(struct sd *sd, __u8 slave);
+
/* This does an initial reset of an OmniVision sensor and ensures that I2C
* is synchronized. Returns <0 on failure.
*/
-static int init_ov_sensor(struct sd *sd)
+static int init_ov_sensor(struct sd *sd, __u8 slave)
{
int i;
+ if (ov51x_set_slave_ids(sd, slave) < 0)
+ return -EIO;
+
/* Reset the sensor */
if (i2c_w(sd, 0x12, 0x80) < 0)
return -EIO;
@@ -1466,6 +2410,14 @@ static int ov51x_set_slave_ids(struct sd *sd,
{
int rc;
+ switch (sd->bridge) {
+ case BRIDGE_OVFX2:
+ return reg_w(sd, OVFX2_I2C_ADDR, slave);
+ case BRIDGE_W9968CF:
+ sd->sensor_addr = slave;
+ return 0;
+ }
+
rc = reg_w(sd, R51x_I2C_W_SID, slave);
if (rc < 0)
return rc;
@@ -1508,6 +2460,39 @@ static int write_i2c_regvals(struct sd *sd,
*
***************************************************************************/
+/* This initializes the OV2x10 / OV3610 / OV3620 */
+static int ov_hires_configure(struct sd *sd)
+{
+ int high, low;
+
+ if (sd->bridge != BRIDGE_OVFX2) {
+ PDEBUG(D_ERR, "error hires sensors only supported with ovfx2");
+ return -1;
+ }
+
+ PDEBUG(D_PROBE, "starting ov hires configuration");
+
+ /* Detect sensor (sub)type */
+ high = i2c_r(sd, 0x0a);
+ low = i2c_r(sd, 0x0b);
+ /* info("%x, %x", high, low); */
+ if (high == 0x96 && low == 0x40) {
+ PDEBUG(D_PROBE, "Sensor is an OV2610");
+ sd->sensor = SEN_OV2610;
+ } else if (high == 0x36 && (low & 0x0f) == 0x00) {
+ PDEBUG(D_PROBE, "Sensor is an OV3610");
+ sd->sensor = SEN_OV3610;
+ } else {
+ PDEBUG(D_ERR, "Error unknown sensor type: 0x%02x%02x",
+ high, low);
+ return -1;
+ }
+
+ /* Set sensor-specific vars */
+ return 0;
+}
+
+
/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
* the same register settings as the OV8610, since they are very similar.
*/
@@ -1966,12 +2951,29 @@ static int ov519_configure(struct sd *sd)
return write_regvals(sd, init_519, ARRAY_SIZE(init_519));
}
+static int ovfx2_configure(struct sd *sd)
+{
+ static const struct ov_regvals init_fx2[] = {
+ { 0x00, 0x60 },
+ { 0x02, 0x01 },
+ { 0x0f, 0x1d },
+ { 0xe9, 0x82 },
+ { 0xea, 0xc7 },
+ { 0xeb, 0x10 },
+ { 0xec, 0xf6 },
+ };
+
+ sd->stopped = 1;
+
+ return write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
+}
+
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
+ struct cam *cam = &gspca_dev->cam;
int ret = 0;
sd->bridge = id->driver_info & BRIDGE_MASK;
@@ -1989,6 +2991,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
case BRIDGE_OV519:
ret = ov519_configure(sd);
break;
+ case BRIDGE_OVFX2:
+ ret = ovfx2_configure(sd);
+ cam->bulk_size = OVFX2_BULK_SIZE;
+ cam->bulk_nurbs = MAX_NURBS;
+ cam->bulk = 1;
+ break;
+ case BRIDGE_W9968CF:
+ ret = w9968cf_configure(sd);
+ cam->reverse_alts = 1;
+ break;
}
if (ret)
@@ -1996,49 +3008,39 @@ static int sd_config(struct gspca_dev *gspca_dev,
ov51x_led_control(sd, 0); /* turn LED off */
- /* Test for 76xx */
- if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0)
- goto error;
-
/* The OV519 must be more aggressive about sensor detection since
* I2C write will never fail if the sensor is not present. We have
* to try to initialize the sensor to detect its presence */
- if (init_ov_sensor(sd) >= 0) {
+
+ /* Test for 76xx */
+ if (init_ov_sensor(sd, OV7xx0_SID) >= 0) {
if (ov7xx0_configure(sd) < 0) {
PDEBUG(D_ERR, "Failed to configure OV7xx0");
goto error;
}
- } else {
-
- /* Test for 6xx0 */
- if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0)
+ /* Test for 6xx0 */
+ } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) {
+ if (ov6xx0_configure(sd) < 0) {
+ PDEBUG(D_ERR, "Failed to configure OV6xx0");
+ goto error;
+ }
+ /* Test for 8xx0 */
+ } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) {
+ if (ov8xx0_configure(sd) < 0) {
+ PDEBUG(D_ERR, "Failed to configure OV8xx0");
goto error;
-
- if (init_ov_sensor(sd) >= 0) {
- if (ov6xx0_configure(sd) < 0) {
- PDEBUG(D_ERR, "Failed to configure OV6xx0");
- goto error;
- }
- } else {
-
- /* Test for 8xx0 */
- if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0)
- goto error;
-
- if (init_ov_sensor(sd) < 0) {
- PDEBUG(D_ERR,
- "Can't determine sensor slave IDs");
- goto error;
- }
- if (ov8xx0_configure(sd) < 0) {
- PDEBUG(D_ERR,
- "Failed to configure OV8xx0 sensor");
- goto error;
- }
}
+ /* Test for 3xxx / 2xxx */
+ } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
+ if (ov_hires_configure(sd) < 0) {
+ PDEBUG(D_ERR, "Failed to configure high res OV");
+ goto error;
+ }
+ } else {
+ PDEBUG(D_ERR, "Can't determine sensor slave IDs");
+ goto error;
}
- cam = &gspca_dev->cam;
switch (sd->bridge) {
case BRIDGE_OV511:
case BRIDGE_OV511PLUS:
@@ -2069,6 +3071,31 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
}
break;
+ case BRIDGE_OVFX2:
+ if (sd->sensor == SEN_OV2610) {
+ cam->cam_mode = ovfx2_ov2610_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
+ } else if (sd->sensor == SEN_OV3610) {
+ cam->cam_mode = ovfx2_ov3610_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
+ } else if (!sd->sif) {
+ cam->cam_mode = ov519_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+ } else {
+ cam->cam_mode = ov519_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ }
+ break;
+ case BRIDGE_W9968CF:
+ cam->cam_mode = w9968cf_vga_mode;
+ cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
+ if (sd->sif)
+ cam->nmodes--;
+
+ /* w9968cf needs initialisation once the sensor is known */
+ if (w9968cf_init(sd) < 0)
+ goto error;
+ break;
}
sd->brightness = BRIGHTNESS_DEF;
if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF)
@@ -2087,11 +3114,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) |
(1 << OV7670_FREQ_IDX);
}
+ sd->quality = QUALITY_DEF;
if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX;
/* OV8610 Frequency filter control should work but needs testing */
if (sd->sensor == SEN_OV8610)
gspca_dev->ctrl_dis |= 1 << FREQ_IDX;
+ /* No controls for the OV2610/OV3610 */
+ if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
+ gspca_dev->ctrl_dis |= 0xFF;
return 0;
error:
@@ -2106,6 +3137,20 @@ static int sd_init(struct gspca_dev *gspca_dev)
/* initialize the sensor */
switch (sd->sensor) {
+ case SEN_OV2610:
+ if (write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610)))
+ return -EIO;
+ /* Enable autogain, autoexpo, awb, bandfilter */
+ if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0)
+ return -EIO;
+ break;
+ case SEN_OV3610:
+ if (write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b)))
+ return -EIO;
+ /* Enable autogain, autoexpo, awb, bandfilter */
+ if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0)
+ return -EIO;
+ break;
case SEN_OV6620:
if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20)))
return -EIO;
@@ -2548,19 +3593,60 @@ static int ov519_mode_init_regs(struct sd *sd)
static int mode_init_ov_sensor_regs(struct sd *sd)
{
struct gspca_dev *gspca_dev;
- int qvga;
+ int qvga, xstart, xend, ystart, yend;
+ __u8 v;
gspca_dev = &sd->gspca_dev;
qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1;
/******** Mode (VGA/QVGA) and sensor specific regs ********/
switch (sd->sensor) {
+ case SEN_OV2610:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+ i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
+ i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+ return 0;
+ case SEN_OV3610:
+ if (qvga) {
+ xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4);
+ ystart = (776 - gspca_dev->height) / 2;
+ } else {
+ xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4);
+ ystart = (1544 - gspca_dev->height) / 2;
+ }
+ xend = xstart + gspca_dev->width;
+ yend = ystart + gspca_dev->height;
+ /* Writing to the COMH register resets the other windowing regs
+ to their default values, so we must do this first. */
+ i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
+ i2c_w_mask(sd, 0x32,
+ (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7),
+ 0x3f);
+ i2c_w_mask(sd, 0x03,
+ (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3),
+ 0x0f);
+ i2c_w(sd, 0x17, xstart >> 4);
+ i2c_w(sd, 0x18, xend >> 4);
+ i2c_w(sd, 0x19, ystart >> 3);
+ i2c_w(sd, 0x1a, yend >> 3);
+ return 0;
case SEN_OV8610:
/* For OV8610 qvga means qsvga */
i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */
+ i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */
break;
case SEN_OV7610:
i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w(sd, 0x35, qvga?0x1e:0x9e);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
break;
case SEN_OV7620:
case SEN_OV76BE:
@@ -2571,6 +3657,10 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0);
i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ if (sd->sensor == SEN_OV76BE)
+ i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
break;
case SEN_OV7640:
i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
@@ -2580,6 +3670,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
/* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */
/* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */
/* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */
+ i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
break;
case SEN_OV7670:
/* set COM7_FMT_VGA or COM7_FMT_QVGA
@@ -2588,55 +3679,56 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
i2c_w_mask(sd, OV7670_REG_COM7,
qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
OV7670_COM7_FMT_MASK);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB,
+ OV7670_COM8_AWB);
+ if (qvga) { /* QVGA from ov7670.c by
+ * Jonathan Corbet */
+ xstart = 164;
+ xend = 28;
+ ystart = 14;
+ yend = 494;
+ } else { /* VGA */
+ xstart = 158;
+ xend = 14;
+ ystart = 10;
+ yend = 490;
+ }
+ /* OV7670 hardware window registers are split across
+ * multiple locations */
+ i2c_w(sd, OV7670_REG_HSTART, xstart >> 3);
+ i2c_w(sd, OV7670_REG_HSTOP, xend >> 3);
+ v = i2c_r(sd, OV7670_REG_HREF);
+ v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07);
+ msleep(10); /* need to sleep between read and write to
+ * same reg! */
+ i2c_w(sd, OV7670_REG_HREF, v);
+
+ i2c_w(sd, OV7670_REG_VSTART, ystart >> 2);
+ i2c_w(sd, OV7670_REG_VSTOP, yend >> 2);
+ v = i2c_r(sd, OV7670_REG_VREF);
+ v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03);
+ msleep(10); /* need to sleep between read and write to
+ * same reg! */
+ i2c_w(sd, OV7670_REG_VREF, v);
break;
case SEN_OV6620:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
case SEN_OV6630:
case SEN_OV66308AF:
i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
break;
default:
return -EINVAL;
}
- /******** Palette-specific regs ********/
-
- /* The OV518 needs special treatment. Although both the OV518
- * and the OV6630 support a 16-bit video bus, only the 8 bit Y
- * bus is actually used. The UV bus is tied to ground.
- * Therefore, the OV6630 needs to be in 8-bit multiplexed
- * output mode */
-
- /* OV7640 is 8-bit only */
-
- if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF &&
- sd->sensor != SEN_OV7640)
- i2c_w_mask(sd, 0x13, 0x00, 0x20);
-
/******** Clock programming ********/
i2c_w(sd, 0x11, sd->clockdiv);
- /******** Special Features ********/
-/* no evidence this is possible with OV7670, either */
- /* Test Pattern */
- if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670)
- i2c_w_mask(sd, 0x12, 0x00, 0x02);
-
- /* Enable auto white balance */
- if (sd->sensor == SEN_OV7670)
- i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB,
- OV7670_COM8_AWB);
- else
- i2c_w_mask(sd, 0x12, 0x04, 0x04);
-
- /* This will go away as soon as ov51x_mode_init_sensor_regs() */
- /* is fully tested. */
- /* 7620/6620/6630? don't have register 0x35, so play it safe */
- if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
- if (!qvga)
- i2c_w(sd, 0x35, 0x9e);
- else
- i2c_w(sd, 0x35, 0x1e);
- }
return 0;
}
@@ -2659,8 +3751,12 @@ static int set_ov_sensor_window(struct sd *sd)
struct gspca_dev *gspca_dev;
int qvga, crop;
int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
- int ret, hstart, hstop, vstop, vstart;
- __u8 v;
+ int ret;
+
+ /* mode setup is fully handled in mode_init_ov_sensor_regs for these */
+ if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610 ||
+ sd->sensor == SEN_OV7670)
+ return mode_init_ov_sensor_regs(sd);
gspca_dev = &sd->gspca_dev;
qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1;
@@ -2708,11 +3804,6 @@ static int set_ov_sensor_window(struct sd *sd)
hwebase = 0x1a;
vwsbase = vwebase = 0x03;
break;
- case SEN_OV7670:
- /*handling of OV7670 hardware sensor start and stop values
- * is very odd, compared to the other OV sensors */
- vwsbase = vwebase = hwebase = hwsbase = 0x00;
- break;
default:
return -EINVAL;
}
@@ -2753,58 +3844,11 @@ static int set_ov_sensor_window(struct sd *sd)
if (ret < 0)
return ret;
- if (sd->sensor == SEN_OV8610) {
- i2c_w_mask(sd, 0x2d, 0x05, 0x40);
- /* old 0x95, new 0x05 from windrv 090403 */
- /* bits 5-7: reserved */
- i2c_w_mask(sd, 0x28, 0x20, 0x20);
- /* bit 5: progressive mode on */
- }
-
- /* The below is wrong for OV7670s because their window registers
- * only store the high bits in 0x17 to 0x1a */
-
- /* SRH Use sd->max values instead of requested win values */
- /* SCS Since we're sticking with only the max hardware widths
- * for a given mode */
- /* I can hard code this for OV7670s */
- /* Yes, these numbers do look odd, but they're tested and work! */
- if (sd->sensor == SEN_OV7670) {
- if (qvga) { /* QVGA from ov7670.c by
- * Jonathan Corbet */
- hstart = 164;
- hstop = 28;
- vstart = 14;
- vstop = 494;
- } else { /* VGA */
- hstart = 158;
- hstop = 14;
- vstart = 10;
- vstop = 490;
- }
- /* OV7670 hardware window registers are split across
- * multiple locations */
- i2c_w(sd, OV7670_REG_HSTART, hstart >> 3);
- i2c_w(sd, OV7670_REG_HSTOP, hstop >> 3);
- v = i2c_r(sd, OV7670_REG_HREF);
- v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07);
- msleep(10); /* need to sleep between read and write to
- * same reg! */
- i2c_w(sd, OV7670_REG_HREF, v);
+ i2c_w(sd, 0x17, hwsbase);
+ i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
+ i2c_w(sd, 0x19, vwsbase);
+ i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
- i2c_w(sd, OV7670_REG_VSTART, vstart >> 2);
- i2c_w(sd, OV7670_REG_VSTOP, vstop >> 2);
- v = i2c_r(sd, OV7670_REG_VREF);
- v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03);
- msleep(10); /* need to sleep between read and write to
- * same reg! */
- i2c_w(sd, OV7670_REG_VREF, v);
- } else {
- i2c_w(sd, 0x17, hwsbase);
- i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale));
- i2c_w(sd, 0x19, vwsbase);
- i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale));
- }
return 0;
}
@@ -2814,6 +3858,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int ret = 0;
+ /* Default for most bridges, allow bridge_mode_init_regs to override */
+ sd->sensor_width = sd->gspca_dev.width;
+ sd->sensor_height = sd->gspca_dev.height;
+
switch (sd->bridge) {
case BRIDGE_OV511:
case BRIDGE_OV511PLUS:
@@ -2826,6 +3874,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
case BRIDGE_OV519:
ret = ov519_mode_init_regs(sd);
break;
+ /* case BRIDGE_OVFX2: nothing to do */
+ case BRIDGE_W9968CF:
+ ret = w9968cf_mode_init_regs(sd);
+ break;
}
if (ret < 0)
goto out;
@@ -2859,10 +3911,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
ov51x_led_control(sd, 0);
}
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge == BRIDGE_W9968CF)
+ w9968cf_stop0(sd);
+}
+
static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *in, /* isoc packet */
- int len) /* iso packet length */
+ u8 *in, /* isoc packet */
+ int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2893,11 +3952,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
return;
}
/* Add 11 byte footer to frame, might be usefull */
- gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11);
+ gspca_frame_add(gspca_dev, LAST_PACKET, in, 11);
return;
} else {
/* Frame start */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0);
sd->packet_nr = 0;
}
}
@@ -2906,12 +3965,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
len--;
/* intermediate packet */
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, in, len);
}
static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2919,8 +3977,8 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
/* A false positive here is likely, until OVT gives me
* the definitive SOF/EOF format */
if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) {
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
sd->packet_nr = 0;
}
@@ -2944,12 +4002,11 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
}
/* intermediate packet */
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
/* Header of ov519 is 16 bytes:
@@ -2972,7 +4029,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
len -= HDRSZ;
#undef HDRSZ
if (data[0] == 0xff || data[1] == 0xd8)
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
data, len);
else
gspca_dev->last_packet_type = DISCARD_PACKET;
@@ -2980,20 +4037,31 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
case 0x51: /* end of frame */
if (data[9] != 0)
gspca_dev->last_packet_type = DISCARD_PACKET;
- gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
return;
}
}
/* intermediate packet */
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ /* A short read signals EOF */
+ if (len < OVFX2_BULK_SIZE) {
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ return;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -3001,14 +4069,20 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
switch (sd->bridge) {
case BRIDGE_OV511:
case BRIDGE_OV511PLUS:
- ov511_pkt_scan(gspca_dev, frame, data, len);
+ ov511_pkt_scan(gspca_dev, data, len);
break;
case BRIDGE_OV518:
case BRIDGE_OV518PLUS:
- ov518_pkt_scan(gspca_dev, frame, data, len);
+ ov518_pkt_scan(gspca_dev, data, len);
break;
case BRIDGE_OV519:
- ov519_pkt_scan(gspca_dev, frame, data, len);
+ ov519_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OVFX2:
+ ovfx2_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_W9968CF:
+ w9968cf_pkt_scan(gspca_dev, data, len);
break;
}
}
@@ -3124,7 +4198,8 @@ static void setcolors(struct gspca_dev *gspca_dev)
static void setautobrightness(struct sd *sd)
{
- if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
+ if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670 ||
+ sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
return;
i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10);
@@ -3132,6 +4207,9 @@ static void setautobrightness(struct sd *sd)
static void setfreq(struct sd *sd)
{
+ if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
+ return;
+
if (sd->sensor == SEN_OV7670) {
switch (sd->freq) {
case 0: /* Banding filter disabled */
@@ -3301,8 +4379,12 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
struct sd *sd = (struct sd *) gspca_dev;
sd->freq = val;
- if (gspca_dev->streaming)
+ if (gspca_dev->streaming) {
setfreq(sd);
+ /* Ugly but necessary */
+ if (sd->bridge == BRIDGE_W9968CF)
+ w9968cf_set_crop_window(sd);
+ }
return 0;
}
@@ -3343,6 +4425,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
return -EINVAL;
}
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge != BRIDGE_W9968CF)
+ return -EINVAL;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = sd->quality;
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
+ V4L2_JPEG_MARKER_DRI;
+ return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge != BRIDGE_W9968CF)
+ return -EINVAL;
+
+ if (gspca_dev->streaming)
+ return -EBUSY;
+
+ if (jcomp->quality < QUALITY_MIN)
+ sd->quality = QUALITY_MIN;
+ else if (jcomp->quality > QUALITY_MAX)
+ sd->quality = QUALITY_MAX;
+ else
+ sd->quality = jcomp->quality;
+
+ /* Return resulting jcomp params to app */
+ sd_get_jcomp(gspca_dev, jcomp);
+
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -3352,18 +4473,23 @@ static const struct sd_desc sd_desc = {
.init = sd_init,
.start = sd_start,
.stopN = sd_stopN,
+ .stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.querymenu = sd_querymenu,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
};
/* -- module initialisation -- */
static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
{USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4064),
.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x041e, 0x4068),
.driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
{USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 },
@@ -3373,11 +4499,16 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
{USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
{USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
{USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS },
{USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS },
{USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
+ {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
+ {USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 },
{}
};
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 4b528b37291..4dbb882c83d 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -1,5 +1,6 @@
/*
* ov534 gspca driver
+ *
* Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
* Copyright (C) 2008 Jim Paris <jim@jtan.com>
* Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr
@@ -8,6 +9,10 @@
* USB protocol reverse engineered by Jim Paris <jim@jtan.com>
* https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
*
+ * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr
+ * PS3 Eye camera, brightness, contrast, hue, AWB control added
+ * by Max Thrun <bear24rw@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
@@ -51,16 +56,335 @@ struct sd {
u16 last_fid;
u8 frame_rate;
+ u8 brightness;
+ u8 contrast;
+ u8 gain;
+ u8 exposure;
+ u8 redblc;
+ u8 blueblc;
+ u8 hue;
+ u8 autogain;
+ u8 awb;
+ s8 sharpness;
+ u8 hflip;
+ u8 vflip;
+ u8 satur;
+ u8 lightfreq;
+
u8 sensor;
#define SENSOR_OV772X 0
#define SENSOR_OV965X 1
};
/* V4L2 controls supported by the driver */
-static struct ctrl sd_ctrls[] = {
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls_ov772x[] = {
+ { /* 0 */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BRIGHTNESS_77_DEF 20
+ .default_value = BRIGHTNESS_77_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ { /* 1 */
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define CONTRAST_77_DEF 37
+ .default_value = CONTRAST_77_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ { /* 2 */
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Main Gain",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+#define GAIN_DEF 20
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+ { /* 3 */
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define EXPO_77_DEF 120
+ .default_value = EXPO_77_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ { /* 4 */
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define RED_BALANCE_DEF 128
+ .default_value = RED_BALANCE_DEF,
+ },
+ .set = sd_setredblc,
+ .get = sd_getredblc,
+ },
+ { /* 5 */
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define BLUE_BALANCE_DEF 128
+ .default_value = BLUE_BALANCE_DEF,
+ },
+ .set = sd_setblueblc,
+ .get = sd_getblueblc,
+ },
+ { /* 6 */
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define HUE_DEF 143
+ .default_value = HUE_DEF,
+ },
+ .set = sd_sethue,
+ .get = sd_gethue,
+ },
+ { /* 7 */
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Autogain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_77_DEF 0
+ .default_value = AUTOGAIN_77_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+#define AWB_77_IDX 8
+ { /* 8 */
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AWB_DEF 0
+ .default_value = AWB_DEF,
+ },
+ .set = sd_setawb,
+ .get = sd_getawb,
+ },
+ { /* 9 */
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+#define SHARPNESS_77_DEF 0
+ .default_value = SHARPNESS_77_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+ { /* 10 */
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "HFlip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define HFLIP_DEF 0
+ .default_value = HFLIP_DEF,
+ },
+ .set = sd_sethflip,
+ .get = sd_gethflip,
+ },
+ { /* 11 */
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "VFlip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 0
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+};
+static struct ctrl sd_ctrls_ov965x[] = {
+ { /* 0 */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define BRIGHTNESS_96_DEF 7
+ .default_value = BRIGHTNESS_96_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+ { /* 1 */
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define CONTRAST_96_DEF 3
+ .default_value = CONTRAST_96_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+ { /* 2 */
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Autogain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_96_DEF 1
+ .default_value = AUTOGAIN_96_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+#define EXPO_96_IDX 3
+ { /* 3 */
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define EXPO_96_DEF 0
+ .default_value = EXPO_96_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ { /* 4 */
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = -1, /* -1 = auto */
+ .maximum = 4,
+ .step = 1,
+#define SHARPNESS_96_DEF -1
+ .default_value = SHARPNESS_96_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+ { /* 5 */
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+#define SATUR_DEF 2
+ .default_value = SATUR_DEF,
+ },
+ .set = sd_setsatur,
+ .get = sd_getsatur,
+ },
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+#define FREQ_DEF 0
+ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+ .get = sd_getfreq,
+ },
};
-static const struct v4l2_pix_format vga_yuyv_mode[] = {
+static const struct v4l2_pix_format ov772x_mode[] = {
+ {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 320 * 2,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
{640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
.bytesperline = 640 * 2,
.sizeimage = 640 * 480 * 2,
@@ -68,20 +392,35 @@ static const struct v4l2_pix_format vga_yuyv_mode[] = {
.priv = 0},
};
-static const struct v4l2_pix_format vga_jpeg_mode[] = {
+static const struct v4l2_pix_format ov965x_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
+ .priv = 4},
{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 1024,
+ .sizeimage = 1024 * 768 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.priv = 0},
};
-static const u8 bridge_init_ov722x[][2] = {
+static const u8 bridge_init_ov772x[][2] = {
{ 0xc2, 0x0c },
{ 0x88, 0xf8 },
{ 0xc3, 0x69 },
@@ -122,6 +461,7 @@ static const u8 bridge_init_ov722x[][2] = {
{ 0x1d, 0x40 },
{ 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
{ 0x1d, 0x00 }, /* payload size */
+
{ 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
{ 0x1d, 0x58 }, /* frame size */
{ 0x1d, 0x00 }, /* frame size */
@@ -138,10 +478,20 @@ static const u8 bridge_init_ov722x[][2] = {
{ 0xc1, 0x3c },
{ 0xc2, 0x0c },
};
-
-static const u8 sensor_init_ov722x[][2] = {
+static const u8 sensor_init_ov772x[][2] = {
{ 0x12, 0x80 },
{ 0x11, 0x01 },
+/*fixme: better have a delay?*/
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
+ { 0x11, 0x01 },
{ 0x3d, 0x03 },
{ 0x17, 0x26 },
@@ -154,10 +504,10 @@ static const u8 sensor_init_ov722x[][2] = {
{ 0x65, 0x20 },
{ 0x11, 0x01 },
{ 0x42, 0x7f },
- { 0x63, 0xe0 },
+ { 0x63, 0xaa }, /* AWB - was e0 */
{ 0x64, 0xff },
{ 0x66, 0x00 },
- { 0x13, 0xf0 },
+ { 0x13, 0xf0 }, /* com8 */
{ 0x0d, 0x41 },
{ 0x0f, 0xc5 },
{ 0x14, 0x11 },
@@ -170,7 +520,7 @@ static const u8 sensor_init_ov722x[][2] = {
{ 0x2a, 0x00 },
{ 0x2b, 0x00 },
{ 0x6b, 0xaa },
- { 0x13, 0xff },
+ { 0x13, 0xff }, /* AWB */
{ 0x90, 0x05 },
{ 0x91, 0x01 },
@@ -218,9 +568,51 @@ static const u8 sensor_init_ov722x[][2] = {
{ 0x14, 0x41 },
{ 0x0e, 0xcd },
{ 0xac, 0xbf },
- { 0x8e, 0x00 },
+ { 0x8e, 0x00 }, /* De-noise threshold */
{ 0x0c, 0xd0 }
};
+static const u8 bridge_start_ov772x_vga[][2] = {
+ {0x1c, 0x00},
+ {0x1d, 0x40},
+ {0x1d, 0x02},
+ {0x1d, 0x00},
+ {0x1d, 0x02},
+ {0x1d, 0x58},
+ {0x1d, 0x00},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+};
+static const u8 sensor_start_ov772x_vga[][2] = {
+ {0x12, 0x00},
+ {0x17, 0x26},
+ {0x18, 0xa0},
+ {0x19, 0x07},
+ {0x1a, 0xf0},
+ {0x29, 0xa0},
+ {0x2c, 0xf0},
+ {0x65, 0x20},
+};
+static const u8 bridge_start_ov772x_qvga[][2] = {
+ {0x1c, 0x00},
+ {0x1d, 0x40},
+ {0x1d, 0x02},
+ {0x1d, 0x00},
+ {0x1d, 0x01},
+ {0x1d, 0x4b},
+ {0x1d, 0x00},
+ {0xc0, 0x28},
+ {0xc1, 0x1e},
+};
+static const u8 sensor_start_ov772x_qvga[][2] = {
+ {0x12, 0x40},
+ {0x17, 0x3f},
+ {0x18, 0x50},
+ {0x19, 0x03},
+ {0x1a, 0x78},
+ {0x29, 0x50},
+ {0x2c, 0x78},
+ {0x65, 0x2f},
+};
static const u8 bridge_init_ov965x[][2] = {
{0x88, 0xf8},
@@ -403,7 +795,7 @@ static const u8 sensor_init_ov965x[][2] = {
{0xcb, 0xf0},
{0xcc, 0xd8},
{0xcd, 0xf1},
- {0x4f, 0x98},
+ {0x4f, 0x98}, /* matrix */
{0x50, 0x98},
{0x51, 0x00},
{0x52, 0x28},
@@ -412,6 +804,7 @@ static const u8 sensor_init_ov965x[][2] = {
{0x58, 0x1a},
{0xff, 0x41}, /* read 41, write ff 00 */
{0x41, 0x40}, /* com16 */
+
{0xc5, 0x03}, /* 60 Hz banding filter */
{0x6a, 0x02}, /* 50 Hz banding filter */
@@ -455,8 +848,8 @@ static const u8 bridge_init_ov965x_2[][2] = {
{0x52, 0x3c},
{0x53, 0x00},
{0x54, 0x00},
- {0x55, 0x00}, /* brightness */
- {0x57, 0x00}, /* contrast 2 */
+ {0x55, 0x00},
+ {0x57, 0x00},
{0x5c, 0x00},
{0x5a, 0xa0},
{0x5b, 0x78},
@@ -479,14 +872,16 @@ static const u8 sensor_init_ov965x_2[][2] = {
{0xa3, 0x3e},
{0x2d, 0x00},
{0xff, 0x42}, /* read 42, write ff 00 */
- {0x42, 0xc0},
+ {0x42, 0xc0}, /* com17 */
{0x2d, 0x00},
{0xff, 0x42}, /* read 42, write ff 00 */
- {0x42, 0xc1},
+ {0x42, 0xc1}, /* com17 */
+/* sharpness */
{0x3f, 0x01},
{0xff, 0x42}, /* read 42, write ff 00 */
- {0x42, 0xc1},
- {0x4f, 0x98},
+ {0x42, 0xc1}, /* com17 */
+/* saturation */
+ {0x4f, 0x98}, /* matrix */
{0x50, 0x98},
{0x51, 0x00},
{0x52, 0x28},
@@ -495,14 +890,17 @@ static const u8 sensor_init_ov965x_2[][2] = {
{0x58, 0x1a},
{0xff, 0x41}, /* read 41, write ff 00 */
{0x41, 0x40}, /* com16 */
+/* contrast */
{0x56, 0x40},
+/* brightness */
{0x55, 0x8f},
+/* expo */
{0x10, 0x25}, /* aech - exposure high bits */
{0xff, 0x13}, /* read 13, write ff 00 */
{0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
};
-static const u8 sensor_start_ov965x[][2] = {
+static const u8 sensor_start_ov965x_1_vga[][2] = { /* same for qvga */
{0x12, 0x62}, /* com7 - 30fps VGA YUV */
{0x36, 0xfa}, /* aref3 */
{0x69, 0x0a}, /* hv */
@@ -523,10 +921,77 @@ static const u8 sensor_start_ov965x[][2] = {
{0x1a, 0x3d}, /* vstop */
{0x32, 0xff}, /* href */
{0xc0, 0xaa},
- {}
};
-static const u8 bridge_start_ov965x[][2] = {
+static const u8 sensor_start_ov965x_1_svga[][2] = {
+ {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */
+ {0x36, 0xf8}, /* aref3 */
+ {0x69, 0x02}, /* hv */
+ {0x8c, 0x0d}, /* com22 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x01},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x1b}, /* vref */
+ {0x17, 0x1d}, /* hstart */
+ {0x18, 0xbd}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xe2},
+};
+
+static const u8 sensor_start_ov965x_1_xga[][2] = {
+ {0x12, 0x02}, /* com7 */
+ {0x36, 0xf8}, /* aref3 */
+ {0x69, 0x02}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x01},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x1b}, /* vref */
+ {0x17, 0x1d}, /* hstart */
+ {0x18, 0xbd}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xe2},
+};
+
+static const u8 sensor_start_ov965x_1_sxga[][2] = {
+ {0x12, 0x02}, /* com7 */
+ {0x36, 0xf8}, /* aref3 */
+ {0x69, 0x02}, /* hv */
+ {0x8c, 0x89}, /* com22 */
+ {0x14, 0x28}, /* com9 */
+ {0x3e, 0x0c}, /* com14 */
+ {0x41, 0x40}, /* com16 */
+ {0x72, 0x00},
+ {0x73, 0x01},
+ {0x74, 0x3a},
+ {0x75, 0x35},
+ {0x76, 0x01},
+ {0xc7, 0x80}, /* com24 */
+ {0x03, 0x1b}, /* vref */
+ {0x17, 0x1d}, /* hstart */
+ {0x18, 0x02}, /* hstop */
+ {0x19, 0x01}, /* vstrt */
+ {0x1a, 0x81}, /* vstop */
+ {0x32, 0xff}, /* href */
+ {0xc0, 0xe2},
+};
+
+static const u8 bridge_start_ov965x_qvga[][2] = {
{0x94, 0xaa},
{0xf1, 0x60},
{0xe5, 0x04},
@@ -535,10 +1000,34 @@ static const u8 bridge_start_ov965x[][2] = {
{0x8c, 0x00},
{0x8d, 0x1c},
{0x34, 0x05},
- {}
+
+ {0xc2, 0x4c},
+ {0xc3, 0xf9},
+ {0xda, 0x00},
+ {0x50, 0x00},
+ {0x51, 0xa0},
+ {0x52, 0x78},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x57, 0x00},
+ {0x5c, 0x00},
+ {0x5a, 0x50},
+ {0x5b, 0x3c},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
};
static const u8 bridge_start_ov965x_vga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
{0xc2, 0x0c},
{0xc3, 0xf9},
{0xda, 0x01},
@@ -555,30 +1044,98 @@ static const u8 bridge_start_ov965x_vga[][2] = {
{0x35, 0x02},
{0xd9, 0x10},
{0x94, 0x11},
- {}
};
-static const u8 bridge_start_ov965x_cif[][2] = {
+static const u8 bridge_start_ov965x_svga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0xa0},
+ {0xc1, 0x80},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
{0xc2, 0x4c},
{0xc3, 0xf9},
- {0xda, 0x00},
{0x50, 0x00},
- {0x51, 0xa0},
- {0x52, 0x78},
+ {0x51, 0x40},
+ {0x52, 0x00},
{0x53, 0x00},
{0x54, 0x00},
- {0x55, 0x00},
+ {0x55, 0x88},
{0x57, 0x00},
{0x5c, 0x00},
- {0x5a, 0x50},
- {0x5b, 0x3c},
+ {0x5a, 0xc8},
+ {0x5b, 0x96},
{0x35, 0x02},
{0xd9, 0x10},
+ {0xda, 0x00},
{0x94, 0x11},
- {}
};
-static const u8 sensor_start_ov965x_vga[][2] = {
+static const u8 bridge_start_ov965x_xga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0xa0},
+ {0xc1, 0x80},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+ {0xc2, 0x4c},
+ {0xc3, 0xf9},
+ {0x50, 0x00},
+ {0x51, 0x40},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x88},
+ {0x57, 0x00},
+ {0x5c, 0x01},
+ {0x5a, 0x00},
+ {0x5b, 0xc0},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0xda, 0x01},
+ {0x94, 0x11},
+};
+
+static const u8 bridge_start_ov965x_sxga[][2] = {
+ {0x94, 0xaa},
+ {0xf1, 0x60},
+ {0xe5, 0x04},
+ {0xc0, 0xa0},
+ {0xc1, 0x80},
+ {0x8c, 0x00},
+ {0x8d, 0x1c},
+ {0x34, 0x05},
+ {0xc2, 0x0c},
+ {0xc3, 0xf9},
+ {0xda, 0x00},
+ {0x35, 0x02},
+ {0xd9, 0x10},
+ {0x94, 0x11},
+};
+
+static const u8 sensor_start_ov965x_2_qvga[][2] = {
+ {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */
+ {0x1e, 0x04}, /* mvfp */
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00},
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x11, 0x01}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x02}, /* 50 Hz banding filter */
+ {0xc5, 0x03}, /* 60 Hz banding filter */
+ {0xa2, 0x96}, /* bd50 */
+ {0xa3, 0x7d}, /* bd60 */
+
+ {0xff, 0x13}, /* read 13, write ff 00 */
+ {0x13, 0xe7},
+ {0x3a, 0x80}, /* tslb - yuyv */
+};
+
+static const u8 sensor_start_ov965x_2_vga[][2] = {
{0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
@@ -592,35 +1149,36 @@ static const u8 sensor_start_ov965x_vga[][2] = {
{0xa3, 0x3e}, /* bd60 */
{0x2d, 0x00}, /* advfl */
- {}
};
-static const u8 sensor_start_ov965x_cif[][2] = {
- {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */
+static const u8 sensor_start_ov965x_2_svga[][2] = { /* same for xga */
+ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
{0x1e, 0x04}, /* mvfp */
{0x13, 0xe0}, /* com8 */
{0x00, 0x00},
{0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
{0x11, 0x01}, /* clkrc */
{0x6b, 0x5a}, /* dblv */
- {0x6a, 0x02}, /* 50 Hz banding filter */
- {0xc5, 0x03}, /* 60 Hz banding filter */
- {0xa2, 0x96}, /* bd50 */
- {0xa3, 0x7d}, /* bd60 */
-
- {0xff, 0x13}, /* read 13, write ff 00 */
- {0x13, 0xe7},
- {0x3a, 0x80}, /* tslb - yuyv */
- {}
+ {0x6a, 0x0c}, /* 50 Hz banding filter */
+ {0xc5, 0x0f}, /* 60 Hz banding filter */
+ {0xa2, 0x4e}, /* bd50 */
+ {0xa3, 0x41}, /* bd60 */
};
-static const u8 sensor_start_ov965x_2[][2] = {
- {0xff, 0x42}, /* read 42, write ff 00 */
- {0x42, 0xc1}, /* com17 - 50 Hz filter */
- {}
+static const u8 sensor_start_ov965x_2_sxga[][2] = {
+ {0x13, 0xe0}, /* com8 */
+ {0x00, 0x00},
+ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+ {0x1e, 0x04}, /* mvfp */
+ {0x11, 0x01}, /* clkrc */
+ {0x6b, 0x5a}, /* dblv */
+ {0x6a, 0x0c}, /* 50 Hz banding filter */
+ {0xc5, 0x0f}, /* 60 Hz banding filter */
+ {0xa2, 0x4e}, /* bd50 */
+ {0xa3, 0x41}, /* bd60 */
};
-
static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
{
struct usb_device *udev = gspca_dev->dev;
@@ -753,39 +1311,310 @@ static void sccb_w_array(struct gspca_dev *gspca_dev,
}
}
-/* set framerate */
-static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
+/* ov772x specific controls */
+static void set_frame_rate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ struct rate_s {
+ u8 fps;
+ u8 r11;
+ u8 r0d;
+ u8 re5;
+ };
+ const struct rate_s *r;
+ static const struct rate_s rate_0[] = { /* 640x480 */
+ {60, 0x01, 0xc1, 0x04},
+ {50, 0x01, 0x41, 0x02},
+ {40, 0x02, 0xc1, 0x04},
+ {30, 0x04, 0x81, 0x02},
+ {15, 0x03, 0x41, 0x04},
+ };
+ static const struct rate_s rate_1[] = { /* 320x240 */
+ {125, 0x02, 0x81, 0x02},
+ {100, 0x02, 0xc1, 0x04},
+ {75, 0x03, 0xc1, 0x04},
+ {60, 0x04, 0xc1, 0x04},
+ {50, 0x02, 0x41, 0x04},
+ {40, 0x03, 0x41, 0x04},
+ {30, 0x04, 0x41, 0x04},
+ };
+
+ if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) {
+ r = rate_0;
+ i = ARRAY_SIZE(rate_0);
+ } else {
+ r = rate_1;
+ i = ARRAY_SIZE(rate_1);
+ }
+ while (--i > 0) {
+ if (sd->frame_rate >= r->fps)
+ break;
+ r++;
+ }
+
+ sccb_reg_write(gspca_dev, 0x11, r->r11);
+ sccb_reg_write(gspca_dev, 0x0d, r->r0d);
+ ov534_reg_write(gspca_dev, 0xe5, r->re5);
+
+ PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
+}
+
+static void setbrightness_77(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sccb_reg_write(gspca_dev, 0x9B, sd->brightness);
+}
+
+static void setcontrast_77(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int fr = sd->frame_rate;
- switch (fr) {
- case 50:
- sccb_reg_write(gspca_dev, 0x11, 0x01);
- sccb_reg_write(gspca_dev, 0x0d, 0x41);
- ov534_reg_write(gspca_dev, 0xe5, 0x02);
+ sccb_reg_write(gspca_dev, 0x9C, sd->contrast);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->gain;
+ switch (val & 0x30) {
+ case 0x00:
+ val &= 0x0f;
break;
- case 40:
- sccb_reg_write(gspca_dev, 0x11, 0x02);
- sccb_reg_write(gspca_dev, 0x0d, 0xc1);
- ov534_reg_write(gspca_dev, 0xe5, 0x04);
+ case 0x10:
+ val &= 0x0f;
+ val |= 0x30;
break;
-/* case 30: */
- default:
- fr = 30;
- sccb_reg_write(gspca_dev, 0x11, 0x04);
- sccb_reg_write(gspca_dev, 0x0d, 0x81);
- ov534_reg_write(gspca_dev, 0xe5, 0x02);
+ case 0x20:
+ val &= 0x0f;
+ val |= 0x70;
break;
- case 15:
- sccb_reg_write(gspca_dev, 0x11, 0x03);
- sccb_reg_write(gspca_dev, 0x0d, 0x41);
- ov534_reg_write(gspca_dev, 0xe5, 0x04);
+ default:
+/* case 0x30: */
+ val &= 0x0f;
+ val |= 0xf0;
break;
}
+ sccb_reg_write(gspca_dev, 0x00, val);
+}
+
+static void setexposure_77(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->exposure;
+ sccb_reg_write(gspca_dev, 0x08, val >> 7);
+ sccb_reg_write(gspca_dev, 0x10, val << 1);
+}
+
+static void setredblc(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sccb_reg_write(gspca_dev, 0x43, sd->redblc);
+}
+
+static void setblueblc(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sccb_reg_write(gspca_dev, 0x42, sd->blueblc);
+}
+
+static void sethue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sccb_reg_write(gspca_dev, 0x01, sd->hue);
+}
+
+static void setautogain_77(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->autogain) {
+ sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */
+ sccb_reg_write(gspca_dev, 0x64,
+ sccb_reg_read(gspca_dev, 0x64) | 0x03);
+ } else {
+ sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */
+ sccb_reg_write(gspca_dev, 0x64,
+ sccb_reg_read(gspca_dev, 0x64) & 0xfc);
+ }
+}
+
+static void setawb(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->awb)
+ sccb_reg_write(gspca_dev, 0x63, 0xe0); /* AWB on */
+ else
+ sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */
+}
+
+static void setsharpness_77(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->sharpness;
+ sccb_reg_write(gspca_dev, 0x91, val); /* vga noise */
+ sccb_reg_write(gspca_dev, 0x8e, val); /* qvga noise */
+}
+
+static void sethflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->hflip == 0)
+ sccb_reg_write(gspca_dev, 0x0c,
+ sccb_reg_read(gspca_dev, 0x0c) | 0x40);
+ else
+ sccb_reg_write(gspca_dev, 0x0c,
+ sccb_reg_read(gspca_dev, 0x0c) & 0xbf);
+}
+
+static void setvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->vflip == 0)
+ sccb_reg_write(gspca_dev, 0x0c,
+ sccb_reg_read(gspca_dev, 0x0c) | 0x80);
+ else
+ sccb_reg_write(gspca_dev, 0x0c,
+ sccb_reg_read(gspca_dev, 0x0c) & 0x7f);
+}
+
+/* ov965x specific controls */
+static void setbrightness_96(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->brightness;
+ if (val < 8)
+ val = 15 - val; /* f .. 8 */
+ else
+ val = val - 8; /* 0 .. 7 */
+ sccb_reg_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
+ 0x0f | (val << 4));
+}
+
+static void setcontrast_96(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sccb_reg_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
+ sd->contrast << 4);
+}
+
+static void setexposure_96(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+ static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+
+ sccb_reg_write(gspca_dev, 0x10, /* aec[9:2] */
+ expo[sd->exposure]);
+ val = sccb_reg_read(gspca_dev, 0x13); /* com8 */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ sccb_reg_write(gspca_dev, 0x13, val);
+ val = sccb_reg_read(gspca_dev, 0xa1); /* aech */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ sccb_reg_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */
+}
+
+static void setsharpness_96(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sd->sharpness;
+ if (val < 0) { /* auto */
+ val = sccb_reg_read(gspca_dev, 0x42); /* com17 */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ sccb_reg_write(gspca_dev, 0x42, val | 0x40);
+ /* Edge enhancement strength auto adjust */
+ return;
+ }
+ if (val != 0)
+ val = 1 << (val - 1);
+ sccb_reg_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */
+ val);
+ val = sccb_reg_read(gspca_dev, 0x42); /* com17 */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ sccb_reg_write(gspca_dev, 0x42, val & 0xbf);
+}
+
+static void setautogain_96(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+/*fixme: should adjust agc/awb/aec by different controls */
+ val = sd->autogain;
+ val = sccb_reg_read(gspca_dev, 0x13); /* com8 */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ if (sd->autogain)
+ val |= 0x05; /* agc & aec */
+ else
+ val &= 0xfa;
+ sccb_reg_write(gspca_dev, 0x13, val);
+}
+
+static void setsatur(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val1, val2, val3;
+ static const u8 matrix[5][2] = {
+ {0x14, 0x38},
+ {0x1e, 0x54},
+ {0x28, 0x70},
+ {0x32, 0x8c},
+ {0x48, 0x90}
+ };
+
+ val1 = matrix[sd->satur][0];
+ val2 = matrix[sd->satur][1];
+ val3 = val1 + val2;
+ sccb_reg_write(gspca_dev, 0x4f, val3); /* matrix coeff */
+ sccb_reg_write(gspca_dev, 0x50, val3);
+ sccb_reg_write(gspca_dev, 0x51, 0x00);
+ sccb_reg_write(gspca_dev, 0x52, val1);
+ sccb_reg_write(gspca_dev, 0x53, val2);
+ sccb_reg_write(gspca_dev, 0x54, val3);
+ sccb_reg_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */
+ val1 = sccb_reg_read(gspca_dev, 0x41); /* com16 */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ sccb_reg_write(gspca_dev, 0x41, val1);
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+
+ val = sccb_reg_read(gspca_dev, 0x13); /* com8 */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ if (sd->lightfreq == 0) {
+ sccb_reg_write(gspca_dev, 0x13, val & 0xdf);
+ return;
+ }
+ sccb_reg_write(gspca_dev, 0x13, val | 0x20);
- sd->frame_rate = fr;
- PDEBUG(D_PROBE, "frame_rate: %d", fr);
+ val = sccb_reg_read(gspca_dev, 0x42); /* com17 */
+ sccb_reg_write(gspca_dev, 0xff, 0x00);
+ if (sd->lightfreq == 1)
+ val |= 0x01;
+ else
+ val &= 0xfe;
+ sccb_reg_write(gspca_dev, 0x42, val);
}
/* this function is called at probe time */
@@ -800,17 +1629,60 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
if (sd->sensor == SENSOR_OV772X) {
- cam->cam_mode = vga_yuyv_mode;
- cam->nmodes = ARRAY_SIZE(vga_yuyv_mode);
+ cam->cam_mode = ov772x_mode;
+ cam->nmodes = ARRAY_SIZE(ov772x_mode);
cam->bulk = 1;
cam->bulk_size = 16384;
cam->bulk_nurbs = 2;
} else { /* ov965x */
- cam->cam_mode = vga_jpeg_mode;
- cam->nmodes = ARRAY_SIZE(vga_jpeg_mode);
+ cam->cam_mode = ov965x_mode;
+ cam->nmodes = ARRAY_SIZE(ov965x_mode);
}
+ sd->frame_rate = 30;
+
+ if (sd->sensor == SENSOR_OV772X) {
+ sd->brightness = BRIGHTNESS_77_DEF;
+ sd->contrast = CONTRAST_77_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPO_77_DEF;
+ sd->redblc = RED_BALANCE_DEF;
+ sd->blueblc = BLUE_BALANCE_DEF;
+ sd->hue = HUE_DEF;
+#if AUTOGAIN_77_DEF != 0
+ sd->autogain = AUTOGAIN_77_DEF;
+#else
+ gspca_dev->ctrl_inac |= (1 << AWB_77_IDX);
+#endif
+#if AWB_DEF != 0
+ sd->awb = AWB_DEF
+#endif
+#if SHARPNESS_77_DEF != 0
+ sd->sharpness = SHARPNESS_77_DEF;
+#endif
+#if HFLIP_DEF != 0
+ sd->hflip = HFLIP_DEF;
+#endif
+#if VFLIP_DEF != 0
+ sd->vflip = VFLIP_DEF;
+#endif
+ } else {
+ sd->brightness = BRIGHTNESS_96_DEF;
+ sd->contrast = CONTRAST_96_DEF;
+#if AUTOGAIN_96_DEF != 0
+ sd->autogain = AUTOGAIN_96_DEF;
+ gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX);
+#endif
+#if EXPO_96_DEF != 0
+ sd->exposure = EXPO_96_DEF;
+#endif
+#if SHARPNESS_96_DEF != 0
+ sd->sharpness = SHARPNESS_96_DEF;
+#endif
+ sd->satur = SATUR_DEF;
+ sd->lightfreq = FREQ_DEF;
+ }
return 0;
}
@@ -847,14 +1719,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
/* initialize */
switch (sd->sensor) {
case SENSOR_OV772X:
- reg_w_array(gspca_dev, bridge_init_ov722x,
- ARRAY_SIZE(bridge_init_ov722x));
+ reg_w_array(gspca_dev, bridge_init_ov772x,
+ ARRAY_SIZE(bridge_init_ov772x));
ov534_set_led(gspca_dev, 1);
- sccb_w_array(gspca_dev, sensor_init_ov722x,
- ARRAY_SIZE(sensor_init_ov722x));
+ sccb_w_array(gspca_dev, sensor_init_ov772x,
+ ARRAY_SIZE(sensor_init_ov772x));
ov534_reg_write(gspca_dev, 0xe0, 0x09);
ov534_set_led(gspca_dev, 0);
- ov534_set_frame_rate(gspca_dev);
+ set_frame_rate(gspca_dev);
break;
default:
/* case SENSOR_OV965X: */
@@ -875,60 +1747,115 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static int sd_start(struct gspca_dev *gspca_dev)
+static int sd_start_ov772x(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
int mode;
- switch (sd->sensor) {
- case SENSOR_OV772X:
- ov534_set_led(gspca_dev, 1);
- ov534_reg_write(gspca_dev, 0xe0, 0x00);
- break;
- default:
-/* case SENSOR_OV965X: */
-
- sccb_w_array(gspca_dev, sensor_start_ov965x,
- ARRAY_SIZE(sensor_start_ov965x));
- reg_w_array(gspca_dev, bridge_start_ov965x,
- ARRAY_SIZE(bridge_start_ov965x));
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- if (mode != 0) { /* 320x240 */
- reg_w_array(gspca_dev, bridge_start_ov965x_cif,
- ARRAY_SIZE(bridge_start_ov965x_cif));
- sccb_w_array(gspca_dev, sensor_start_ov965x_cif,
- ARRAY_SIZE(sensor_start_ov965x_cif));
- } else { /* 640x480 */
- reg_w_array(gspca_dev, bridge_start_ov965x_vga,
- ARRAY_SIZE(bridge_start_ov965x_vga));
- sccb_w_array(gspca_dev, sensor_start_ov965x_vga,
- ARRAY_SIZE(sensor_start_ov965x_vga));
- }
- sccb_w_array(gspca_dev, sensor_start_ov965x_2,
- ARRAY_SIZE(sensor_start_ov965x_2));
- ov534_reg_write(gspca_dev, 0xe0, 0x00);
- ov534_reg_write(gspca_dev, 0xe0, 0x00);
- ov534_set_led(gspca_dev, 1);
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ if (mode != 0) { /* 320x240 */
+ reg_w_array(gspca_dev, bridge_start_ov772x_qvga,
+ ARRAY_SIZE(bridge_start_ov772x_qvga));
+ sccb_w_array(gspca_dev, sensor_start_ov772x_qvga,
+ ARRAY_SIZE(sensor_start_ov772x_qvga));
+ } else { /* 640x480 */
+ reg_w_array(gspca_dev, bridge_start_ov772x_vga,
+ ARRAY_SIZE(bridge_start_ov772x_vga));
+ sccb_w_array(gspca_dev, sensor_start_ov772x_vga,
+ ARRAY_SIZE(sensor_start_ov772x_vga));
}
+ set_frame_rate(gspca_dev);
+
+ setautogain_77(gspca_dev);
+ setawb(gspca_dev);
+ setgain(gspca_dev);
+ setredblc(gspca_dev);
+ setblueblc(gspca_dev);
+ sethue(gspca_dev);
+ setexposure_77(gspca_dev);
+ setbrightness_77(gspca_dev);
+ setcontrast_77(gspca_dev);
+ setsharpness_77(gspca_dev);
+ setvflip(gspca_dev);
+ sethflip(gspca_dev);
+
+ ov534_set_led(gspca_dev, 1);
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
return 0;
}
-static void sd_stopN(struct gspca_dev *gspca_dev)
+static int sd_start_ov965x(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ int mode;
- switch (sd->sensor) {
- case SENSOR_OV772X:
- ov534_reg_write(gspca_dev, 0xe0, 0x09);
- ov534_set_led(gspca_dev, 0);
- break;
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ switch (mode) {
default:
-/* case SENSOR_OV965X: */
- ov534_reg_write(gspca_dev, 0xe0, 0x01);
- ov534_set_led(gspca_dev, 0);
- ov534_reg_write(gspca_dev, 0xe0, 0x00);
+/* case 4: * 320x240 */
+ sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga,
+ ARRAY_SIZE(sensor_start_ov965x_1_vga));
+ reg_w_array(gspca_dev, bridge_start_ov965x_qvga,
+ ARRAY_SIZE(bridge_start_ov965x_qvga));
+ sccb_w_array(gspca_dev, sensor_start_ov965x_2_qvga,
+ ARRAY_SIZE(sensor_start_ov965x_2_qvga));
+ break;
+ case 3: /* 640x480 */
+ sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga,
+ ARRAY_SIZE(sensor_start_ov965x_1_vga));
+ reg_w_array(gspca_dev, bridge_start_ov965x_vga,
+ ARRAY_SIZE(bridge_start_ov965x_vga));
+ sccb_w_array(gspca_dev, sensor_start_ov965x_2_vga,
+ ARRAY_SIZE(sensor_start_ov965x_2_vga));
+ break;
+ case 2: /* 800x600 */
+ sccb_w_array(gspca_dev, sensor_start_ov965x_1_svga,
+ ARRAY_SIZE(sensor_start_ov965x_1_svga));
+ reg_w_array(gspca_dev, bridge_start_ov965x_svga,
+ ARRAY_SIZE(bridge_start_ov965x_svga));
+ sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga,
+ ARRAY_SIZE(sensor_start_ov965x_2_svga));
+ break;
+ case 1: /* 1024x768 */
+ sccb_w_array(gspca_dev, sensor_start_ov965x_1_xga,
+ ARRAY_SIZE(sensor_start_ov965x_1_xga));
+ reg_w_array(gspca_dev, bridge_start_ov965x_xga,
+ ARRAY_SIZE(bridge_start_ov965x_xga));
+ sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga,
+ ARRAY_SIZE(sensor_start_ov965x_2_svga));
+ break;
+ case 0: /* 1280x1024 */
+ sccb_w_array(gspca_dev, sensor_start_ov965x_1_sxga,
+ ARRAY_SIZE(sensor_start_ov965x_1_sxga));
+ reg_w_array(gspca_dev, bridge_start_ov965x_sxga,
+ ARRAY_SIZE(bridge_start_ov965x_sxga));
+ sccb_w_array(gspca_dev, sensor_start_ov965x_2_sxga,
+ ARRAY_SIZE(sensor_start_ov965x_2_sxga));
break;
}
+ setfreq(gspca_dev);
+ setautogain_96(gspca_dev);
+ setbrightness_96(gspca_dev);
+ setcontrast_96(gspca_dev);
+ setexposure_96(gspca_dev);
+ setsharpness_96(gspca_dev);
+ setsatur(gspca_dev);
+
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
+ ov534_set_led(gspca_dev, 1);
+ return 0;
+}
+
+static void sd_stopN_ov772x(struct gspca_dev *gspca_dev)
+{
+ ov534_reg_write(gspca_dev, 0xe0, 0x09);
+ ov534_set_led(gspca_dev, 0);
+}
+
+static void sd_stopN_ov965x(struct gspca_dev *gspca_dev)
+{
+ ov534_reg_write(gspca_dev, 0xe0, 0x01);
+ ov534_set_led(gspca_dev, 0);
+ ov534_reg_write(gspca_dev, 0xe0, 0x00);
}
/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
@@ -941,8 +1868,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
#define UVC_STREAM_EOF (1 << 1)
#define UVC_STREAM_FID (1 << 0)
-static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
- __u8 *data, int len)
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
{
struct sd *sd = (struct sd *) gspca_dev;
__u32 this_pts;
@@ -983,32 +1910,30 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
/* If PTS or FID has changed, start a new frame. */
if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
if (gspca_dev->last_packet_type == INTER_PACKET)
- frame = gspca_frame_add(gspca_dev,
- LAST_PACKET, frame,
- NULL, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
sd->last_pts = this_pts;
sd->last_fid = this_fid;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
data + 12, len - 12);
/* If this packet is marked as EOF, end the frame */
} else if (data[1] & UVC_STREAM_EOF) {
sd->last_pts = 0;
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data + 12, len - 12);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data + 12, len - 12);
} else {
/* Add the data from this payload */
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data + 12, len - 12);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 12, len - 12);
}
-
/* Done this payload */
goto scan_next;
discard:
/* Discard data until a new frame starts. */
- gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
scan_next:
remaining_len -= len;
@@ -1016,6 +1941,291 @@ scan_next:
} while (remaining_len > 0);
}
+/* controls */
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming) {
+ if (sd->sensor == SENSOR_OV772X)
+ setexposure_77(gspca_dev);
+ else
+ setexposure_96(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming) {
+ if (sd->sensor == SENSOR_OV772X)
+ setbrightness_77(gspca_dev);
+ else
+ setbrightness_96(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming) {
+ if (sd->sensor == SENSOR_OV772X)
+ setcontrast_77(gspca_dev);
+ else
+ setcontrast_96(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->satur = val;
+ if (gspca_dev->streaming)
+ setsatur(gspca_dev);
+ return 0;
+}
+
+static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->satur;
+ return 0;
+}
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->lightfreq = val;
+ if (gspca_dev->streaming)
+ setfreq(gspca_dev);
+ return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->lightfreq;
+ return 0;
+}
+
+static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->redblc = val;
+ if (gspca_dev->streaming)
+ setredblc(gspca_dev);
+ return 0;
+}
+
+static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->redblc;
+ return 0;
+}
+
+static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blueblc = val;
+ if (gspca_dev->streaming)
+ setblueblc(gspca_dev);
+ return 0;
+}
+
+static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blueblc;
+ return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hue = val;
+ if (gspca_dev->streaming)
+ sethue(gspca_dev);
+ return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hue;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+
+ if (gspca_dev->streaming) {
+ if (sd->sensor == SENSOR_OV772X) {
+
+ /* the auto white balance control works only
+ * when auto gain is set */
+ if (val)
+ gspca_dev->ctrl_inac &= ~(1 << AWB_77_IDX);
+ else
+ gspca_dev->ctrl_inac |= (1 << AWB_77_IDX);
+ setautogain_77(gspca_dev);
+ } else {
+ if (val)
+ gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX);
+ else
+ gspca_dev->ctrl_inac &= ~(1 << EXPO_96_IDX);
+ setautogain_96(gspca_dev);
+ }
+ }
+ return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->awb = val;
+ if (gspca_dev->streaming)
+ setawb(gspca_dev);
+ return 0;
+}
+
+static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->awb;
+ return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->sharpness = val;
+ if (gspca_dev->streaming) {
+ if (sd->sensor == SENSOR_OV772X)
+ setsharpness_77(gspca_dev);
+ else
+ setsharpness_96(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->sharpness;
+ return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ sethflip(gspca_dev);
+ return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ setvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->vflip;
+ return 0;
+}
+
/* get stream parameters (framerate) */
static int sd_get_streamparm(struct gspca_dev *gspca_dev,
struct v4l2_streamparm *parm)
@@ -1047,7 +2257,8 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev,
/* Set requested framerate */
sd->frame_rate = tpf->denominator / tpf->numerator;
- ov534_set_frame_rate(gspca_dev);
+ if (gspca_dev->streaming && sd->sensor == SENSOR_OV772X)
+ set_frame_rate(gspca_dev);
/* Return the actual framerate */
tpf->numerator = 1;
@@ -1056,20 +2267,53 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev,
return 0;
}
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
/* sub-driver description */
-static const struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc_ov772x = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
+ .ctrls = sd_ctrls_ov772x,
+ .nctrls = ARRAY_SIZE(sd_ctrls_ov772x),
.config = sd_config,
.init = sd_init,
- .start = sd_start,
- .stopN = sd_stopN,
+ .start = sd_start_ov772x,
+ .stopN = sd_stopN_ov772x,
.pkt_scan = sd_pkt_scan,
.get_streamparm = sd_get_streamparm,
.set_streamparm = sd_set_streamparm,
};
+static const struct sd_desc sd_desc_ov965x = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_ov965x,
+ .nctrls = ARRAY_SIZE(sd_ctrls_ov965x),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start_ov965x,
+ .stopN = sd_stopN_ov965x,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
+};
+
/* -- module initialisation -- */
static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X},
@@ -1082,8 +2326,12 @@ MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
- return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
- THIS_MODULE);
+ return gspca_dev_probe(intf, id,
+ id->driver_info == SENSOR_OV772X
+ ? &sd_desc_ov772x
+ : &sd_desc_ov965x,
+ sizeof(struct sd),
+ THIS_MODULE);
}
static struct usb_driver sd_driver = {
@@ -1101,6 +2349,7 @@ static struct usb_driver sd_driver = {
static int __init sd_mod_init(void)
{
int ret;
+
ret = usb_register(&sd_driver);
if (ret < 0)
return ret;
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 96659433d24..4706a823add 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -337,14 +337,13 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame,
- __u8 *data,
+ u8 *data,
int len)
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned char *sof;
- sof = pac_find_sof(gspca_dev, data, len);
+ sof = pac_find_sof(&sd->sof_read, data, len);
if (sof) {
int n;
@@ -354,10 +353,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
n -= sizeof pac_sof_marker;
else
n = 0;
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, n);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, n);
sd->header_read = 0;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
len -= sof - data;
data = sof;
}
@@ -381,7 +380,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
sd->header_read = 11;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static void setbrightness(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
new file mode 100644
index 00000000000..74acceea809
--- /dev/null
+++ b/drivers/media/video/gspca/pac7302.c
@@ -0,0 +1,1272 @@
+/*
+ * Pixart PAC7302 library
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * Separated from Pixart PAC7311 library by Márton Németh <nm127@freemail.hu>
+ *
+ * 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
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Some documentation about various registers as determined by trial and error.
+ When the register addresses differ between the 7202 and the 7311 the 2
+ different addresses are written as 7302addr/7311addr, when one of the 2
+ addresses is a - sign that register description is not valid for the
+ matching IC.
+
+ Register page 1:
+
+ Address Description
+ -/0x08 Unknown compressor related, must always be 8 except when not
+ in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
+ -/0x1b Auto white balance related, bit 0 is AWB enable (inverted)
+ bits 345 seem to toggle per color gains on/off (inverted)
+ 0x78 Global control, bit 6 controls the LED (inverted)
+ -/0x80 JPEG compression ratio ? Best not touched
+
+ Register page 3/4:
+
+ Address Description
+ 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
+ the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ -/0x0f Master gain 1-245, low value = high gain
+ 0x10/- Master gain 0-31
+ -/0x10 Another gain 0-15, limited influence (1-2x gain I guess)
+ 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+ -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to
+ completely disable the analog amplification block. Set to 0x68
+ for max gain, 0x14 for minimal gain.
+
+ The registers are accessed in the following functions:
+
+ Page | Register | Function
+ -----+------------+---------------------------------------------------
+ 0 | 0x0f..0x20 | setcolors()
+ 0 | 0xa2..0xab | setbrightcont()
+ 0 | 0xc5 | setredbalance()
+ 0 | 0xc6 | setwhitebalance()
+ 0 | 0xc7 | setbluebalance()
+ 0 | 0xdc | setbrightcont(), setcolors()
+ 3 | 0x02 | setexposure()
+ 3 | 0x10 | setgain()
+ 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
+ 3 | 0x21 | sethvflip()
+*/
+
+#define MODULE_NAME "pac7302"
+
+#include <media/v4l2-chip-ident.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7302");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor for pac7302 */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ unsigned char brightness;
+ unsigned char contrast;
+ unsigned char colors;
+ unsigned char white_balance;
+ unsigned char red_balance;
+ unsigned char blue_balance;
+ unsigned char gain;
+ unsigned char exposure;
+ unsigned char autogain;
+ __u8 hflip;
+ __u8 vflip;
+
+ u8 sof_read;
+ u8 autogain_ignore_frames;
+
+ atomic_t avg_lum;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+/* This control is pac7302 only */
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+#define BRIGHTNESS_MAX 0x20
+ .maximum = BRIGHTNESS_MAX,
+ .step = 1,
+#define BRIGHTNESS_DEF 0x10
+ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+/* This control is for both the 7302 and the 7311 */
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+#define CONTRAST_MAX 255
+ .maximum = CONTRAST_MAX,
+ .step = 1,
+#define CONTRAST_DEF 127
+ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+/* This control is pac7302 only */
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+#define COLOR_MAX 255
+ .maximum = COLOR_MAX,
+ .step = 1,
+#define COLOR_DEF 127
+ .default_value = COLOR_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+ },
+ {
+ {
+ .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "White Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+#define WHITEBALANCE_DEF 4
+ .default_value = WHITEBALANCE_DEF,
+ },
+ .set = sd_setwhitebalance,
+ .get = sd_getwhitebalance,
+ },
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define REDBALANCE_DEF 1
+ .default_value = REDBALANCE_DEF,
+ },
+ .set = sd_setredbalance,
+ .get = sd_getredbalance,
+ },
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+#define BLUEBALANCE_DEF 1
+ .default_value = BLUEBALANCE_DEF,
+ },
+ .set = sd_setbluebalance,
+ .get = sd_getbluebalance,
+ },
+/* All controls below are for both the 7302 and the 7311 */
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+#define GAIN_MAX 255
+ .maximum = GAIN_MAX,
+ .step = 1,
+#define GAIN_DEF 127
+#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 0,
+#define EXPOSURE_MAX 255
+ .maximum = EXPOSURE_MAX,
+ .step = 1,
+#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */
+#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
+ .default_value = EXPOSURE_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define HFLIP_DEF 0
+ .default_value = HFLIP_DEF,
+ },
+ .set = sd_sethflip,
+ .get = sd_gethflip,
+ },
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 0
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+#define LOAD_PAGE3 255
+#define LOAD_PAGE4 254
+#define END_OF_SEQUENCE 0
+
+/* pac 7302 */
+static const __u8 init_7302[] = {
+/* index,value */
+ 0xff, 0x01, /* page 1 */
+ 0x78, 0x00, /* deactivate */
+ 0xff, 0x01,
+ 0x78, 0x40, /* led off */
+};
+static const __u8 start_7302[] = {
+/* index, len, [value]* */
+ 0xff, 1, 0x00, /* page 0 */
+ 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
+ 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
+ 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
+ 0x26, 2, 0xaa, 0xaa,
+ 0x2e, 1, 0x31,
+ 0x38, 1, 0x01,
+ 0x3a, 3, 0x14, 0xff, 0x5a,
+ 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
+ 0x00, 0x54, 0x11,
+ 0x55, 1, 0x00,
+ 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
+ 0x6b, 1, 0x00,
+ 0x6e, 3, 0x08, 0x06, 0x00,
+ 0x72, 3, 0x00, 0xff, 0x00,
+ 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
+ 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
+ 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
+ 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
+ 0xd2, 0xeb,
+ 0xaf, 1, 0x02,
+ 0xb5, 2, 0x08, 0x08,
+ 0xb8, 2, 0x08, 0x88,
+ 0xc4, 4, 0xae, 0x01, 0x04, 0x01,
+ 0xcc, 1, 0x00,
+ 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
+ 0xc1, 0xd7, 0xec,
+ 0xdc, 1, 0x01,
+ 0xff, 1, 0x01, /* page 1 */
+ 0x12, 3, 0x02, 0x00, 0x01,
+ 0x3e, 2, 0x00, 0x00,
+ 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
+ 0x7c, 1, 0x00,
+ 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
+ 0x02, 0x00,
+ 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
+ 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
+ 0xd8, 1, 0x01,
+ 0xdb, 2, 0x00, 0x01,
+ 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
+ 0xe6, 4, 0x00, 0x00, 0x00, 0x01,
+ 0xeb, 1, 0x00,
+ 0xff, 1, 0x02, /* page 2 */
+ 0x22, 1, 0x00,
+ 0xff, 1, 0x03, /* page 3 */
+ 0, LOAD_PAGE3, /* load the page 3 */
+ 0x11, 1, 0x01,
+ 0xff, 1, 0x02, /* page 2 */
+ 0x13, 1, 0x00,
+ 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
+ 0x27, 2, 0x14, 0x0c,
+ 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
+ 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
+ 0x6e, 1, 0x08,
+ 0xff, 1, 0x01, /* page 1 */
+ 0x78, 1, 0x00,
+ 0, END_OF_SEQUENCE /* end of sequence */
+};
+
+#define SKIP 0xaa
+/* page 3 - the value SKIP says skip the index - see reg_w_page() */
+static const __u8 page3_7302[] = {
+ 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16,
+ 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
+ 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
+ 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
+ 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
+ 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
+ 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
+ 0x00
+};
+
+static int reg_w_buf(struct gspca_dev *gspca_dev,
+ __u8 index,
+ const char *buffer, int len)
+{
+ int ret;
+
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 1, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg_w_buf(): "
+ "Failed to write registers to index 0x%x, error %i",
+ index, ret);
+ return ret;
+}
+
+
+static int reg_w(struct gspca_dev *gspca_dev,
+ __u8 index,
+ __u8 value)
+{
+ int ret;
+
+ gspca_dev->usb_buf[0] = value;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg_w(): "
+ "Failed to write register to index 0x%x, value 0x%x, error %i",
+ index, value, ret);
+ return ret;
+}
+
+static int reg_w_seq(struct gspca_dev *gspca_dev,
+ const __u8 *seq, int len)
+{
+ int ret = 0;
+ while (--len >= 0) {
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, seq[0], seq[1]);
+ seq += 2;
+ }
+ return ret;
+}
+
+/* load the beginning of a page */
+static int reg_w_page(struct gspca_dev *gspca_dev,
+ const __u8 *page, int len)
+{
+ int index;
+ int ret = 0;
+
+ for (index = 0; index < len; index++) {
+ if (page[index] == SKIP) /* skip this index */
+ continue;
+ gspca_dev->usb_buf[0] = page[index];
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w_page(): "
+ "Failed to write register to index 0x%x, "
+ "value 0x%x, error %i",
+ index, page[index], ret);
+ break;
+ }
+ }
+ return ret;
+}
+
+/* output a variable sequence */
+static int reg_w_var(struct gspca_dev *gspca_dev,
+ const __u8 *seq,
+ const __u8 *page3, unsigned int page3_len,
+ const __u8 *page4, unsigned int page4_len)
+{
+ int index, len;
+ int ret = 0;
+
+ for (;;) {
+ index = *seq++;
+ len = *seq++;
+ switch (len) {
+ case END_OF_SEQUENCE:
+ return ret;
+ case LOAD_PAGE4:
+ ret = reg_w_page(gspca_dev, page4, page4_len);
+ break;
+ case LOAD_PAGE3:
+ ret = reg_w_page(gspca_dev, page3, page3_len);
+ break;
+ default:
+ if (len > USB_BUF_SZ) {
+ PDEBUG(D_ERR|D_STREAM,
+ "Incorrect variable sequence");
+ return -EINVAL;
+ }
+ while (len > 0) {
+ if (len < 8) {
+ ret = reg_w_buf(gspca_dev,
+ index, seq, len);
+ if (ret < 0)
+ return ret;
+ seq += len;
+ break;
+ }
+ ret = reg_w_buf(gspca_dev, index, seq, 8);
+ seq += 8;
+ index += 8;
+ len -= 8;
+ }
+ }
+ if (ret < 0)
+ return ret;
+ }
+ /* not reached */
+}
+
+/* this function is called at probe time for pac7302 */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ PDEBUG(D_CONF, "Find Sensor PAC7302");
+ cam->cam_mode = vga_mode; /* only 640x480 */
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->white_balance = WHITEBALANCE_DEF;
+ sd->red_balance = REDBALANCE_DEF;
+ sd->blue_balance = BLUEBALANCE_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPOSURE_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ return 0;
+}
+
+/* This function is used by pac7302 only */
+static int setbrightcont(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v;
+ int ret;
+ static const __u8 max[10] =
+ {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
+ 0xd4, 0xec};
+ static const __u8 delta[10] =
+ {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
+ 0x11, 0x0b};
+
+ ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ for (i = 0; i < 10; i++) {
+ v = max[i];
+ v += (sd->brightness - BRIGHTNESS_MAX)
+ * 150 / BRIGHTNESS_MAX; /* 200 ? */
+ v -= delta[i] * sd->contrast / CONTRAST_MAX;
+ if (v < 0)
+ v = 0;
+ else if (v > 0xff)
+ v = 0xff;
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xa2 + i, v);
+ }
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xdc, 0x01);
+ return ret;
+}
+
+/* This function is used by pac7302 only */
+static int setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v;
+ int ret;
+ static const int a[9] =
+ {217, -212, 0, -101, 170, -67, -38, -315, 355};
+ static const int b[9] =
+ {19, 106, 0, 19, 106, 1, 19, 106, 1};
+
+ ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ for (i = 0; i < 9; i++) {
+ v = a[i] * sd->colors / COLOR_MAX + b[i];
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
+ }
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xdc, 0x01);
+ PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
+ return ret;
+}
+
+static int setwhitebalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xc6, sd->white_balance);
+
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xdc, 0x01);
+ PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance);
+ return ret;
+}
+
+static int setredbalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xc5, sd->red_balance);
+
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xdc, 0x01);
+ PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance);
+ return ret;
+}
+
+static int setbluebalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xc7, sd->blue_balance);
+
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xdc, 0x01);
+ PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance);
+ return ret;
+}
+
+static int setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x10, sd->gain >> 3);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ return ret;
+}
+
+static int setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+ __u8 reg;
+
+ /* register 2 of frame 3/4 contains the clock divider configuring the
+ no fps according to the formula: 60 / reg. sd->exposure is the
+ desired exposure time in ms. */
+ reg = 120 * sd->exposure / 1000;
+ if (reg < 2)
+ reg = 2;
+ else if (reg > 63)
+ reg = 63;
+
+ /* On the pac7302 reg2 MUST be a multiple of 3, so round it to
+ the nearest multiple of 3, except when between 6 and 12? */
+ if (reg < 6 || reg > 12)
+ reg = ((reg + 1) / 3) * 3;
+ ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x02, reg);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ return ret;
+}
+
+static int sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+ __u8 data;
+
+ ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ data = (sd->hflip ? 0x08 : 0x00) | (sd->vflip ? 0x04 : 0x00);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x21, data);
+ /* load registers to sensor (Bit 0, auto clear) */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ return ret;
+}
+
+/* this function is called at probe and resume time for pac7302 */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret = 0;
+
+ sd->sof_read = 0;
+
+ ret = reg_w_var(gspca_dev, start_7302,
+ page3_7302, sizeof(page3_7302),
+ NULL, 0);
+ if (0 <= ret)
+ ret = setbrightcont(gspca_dev);
+ if (0 <= ret)
+ ret = setcolors(gspca_dev);
+ if (0 <= ret)
+ ret = setwhitebalance(gspca_dev);
+ if (0 <= ret)
+ ret = setredbalance(gspca_dev);
+ if (0 <= ret)
+ ret = setbluebalance(gspca_dev);
+ if (0 <= ret)
+ ret = setgain(gspca_dev);
+ if (0 <= ret)
+ ret = setexposure(gspca_dev);
+ if (0 <= ret)
+ ret = sethvflip(gspca_dev);
+
+ /* only resolution 640x480 is supported for pac7302 */
+
+ sd->sof_read = 0;
+ sd->autogain_ignore_frames = 0;
+ atomic_set(&sd->avg_lum, -1);
+
+ /* start stream */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x78, 0x01);
+
+ return ret;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ /* stop stream */
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x78, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect for pac7302 */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ if (!gspca_dev->present)
+ return;
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x78, 0x40);
+}
+
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum = atomic_read(&sd->avg_lum);
+ int desired_lum, deadzone;
+
+ if (avg_lum == -1)
+ return;
+
+ desired_lum = 270 + sd->brightness * 4;
+ /* Hack hack, with the 7202 the first exposure step is
+ pretty large, so if we're about to make the first
+ exposure increase make the deadzone large to avoid
+ oscilating */
+ if (desired_lum > avg_lum && sd->gain == GAIN_DEF &&
+ sd->exposure > EXPOSURE_DEF &&
+ sd->exposure < 42)
+ deadzone = 90;
+ else
+ deadzone = 30;
+
+ if (sd->autogain_ignore_frames > 0)
+ sd->autogain_ignore_frames--;
+ else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE))
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+}
+
+/* JPEG header, part 1 */
+static const unsigned char pac_jpeg_header1[] = {
+ 0xff, 0xd8, /* SOI: Start of Image */
+
+ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
+ 0x00, 0x11, /* length = 17 bytes (including this length field) */
+ 0x08 /* Precision: 8 */
+ /* 2 bytes is placed here: number of image lines */
+ /* 2 bytes is placed here: samples per line */
+};
+
+/* JPEG header, continued */
+static const unsigned char pac_jpeg_header2[] = {
+ 0x03, /* Number of image components: 3 */
+ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
+ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
+ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+ 0xff, 0xda, /* SOS: Start Of Scan */
+ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
+ 0x03, /* number of components: 3 */
+ 0x01, 0x00, /* selector 1, table 0x00 */
+ 0x02, 0x11, /* selector 2, table 0x11 */
+ 0x03, 0x11, /* selector 3, table 0x11 */
+ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
+ 0x00 /* Successive approximation: 0 */
+};
+
+static void pac_start_frame(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame,
+ __u16 lines, __u16 samples_per_line)
+{
+ unsigned char tmpbuf[4];
+
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ pac_jpeg_header1, sizeof(pac_jpeg_header1));
+
+ tmpbuf[0] = lines >> 8;
+ tmpbuf[1] = lines & 0xff;
+ tmpbuf[2] = samples_per_line >> 8;
+ tmpbuf[3] = samples_per_line & 0xff;
+
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ tmpbuf, sizeof(tmpbuf));
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ pac_jpeg_header2, sizeof(pac_jpeg_header2));
+}
+
+/* this function is run at interrupt level */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_frame *frame;
+ unsigned char *sof;
+
+ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n, lum_offset, footer_length;
+
+ frame = gspca_get_i_frame(gspca_dev);
+ if (frame == NULL) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ /* 6 bytes after the FF D9 EOF marker a number of lumination
+ bytes are send corresponding to different parts of the
+ image, the 14th and 15th byte after the EOF seem to
+ correspond to the center of the image */
+ lum_offset = 61 + sizeof pac_sof_marker;
+ footer_length = 74;
+
+ /* Finish decoding current frame */
+ n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+ if (n < 0) {
+ frame->data_end += n;
+ n = 0;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, n);
+ if (gspca_dev->last_packet_type != DISCARD_PACKET &&
+ frame->data_end[-2] == 0xff &&
+ frame->data_end[-1] == 0xd9)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+
+ n = sof - data;
+ len -= n;
+ data = sof;
+
+ /* Get average lumination */
+ if (gspca_dev->last_packet_type == LAST_PACKET &&
+ n >= lum_offset)
+ atomic_set(&sd->avg_lum, data[-lum_offset] +
+ data[-lum_offset + 1]);
+ else
+ atomic_set(&sd->avg_lum, -1);
+
+ /* Start the new frame with the jpeg header */
+ /* The PAC7302 has the image rotated 90 degrees */
+ pac_start_frame(gspca_dev, frame,
+ gspca_dev->width, gspca_dev->height);
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightcont(gspca_dev);
+ return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->brightness;
+ return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->contrast = val;
+ if (gspca_dev->streaming) {
+ setbrightcont(gspca_dev);
+ }
+ return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->contrast;
+ return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->colors;
+ return 0;
+}
+
+static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret = 0;
+
+ sd->white_balance = val;
+ if (gspca_dev->streaming)
+ ret = setwhitebalance(gspca_dev);
+ if (0 <= ret)
+ ret = 0;
+ return ret;
+}
+
+static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->white_balance;
+ return 0;
+}
+
+static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret = 0;
+
+ sd->red_balance = val;
+ if (gspca_dev->streaming)
+ ret = setredbalance(gspca_dev);
+ if (0 <= ret)
+ ret = 0;
+ return ret;
+}
+
+static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red_balance;
+ return 0;
+}
+
+static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret = 0;
+
+ sd->blue_balance = val;
+ if (gspca_dev->streaming)
+ ret = setbluebalance(gspca_dev);
+ if (0 <= ret)
+ ret = 0;
+ return ret;
+}
+
+static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue_balance;
+ return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ if (sd->autogain) {
+ sd->exposure = EXPOSURE_DEF;
+ sd->gain = GAIN_DEF;
+ if (gspca_dev->streaming) {
+ sd->autogain_ignore_frames =
+ PAC_AUTOGAIN_IGNORE_FRAMES;
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ }
+ }
+
+ return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->vflip;
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ int ret = -EINVAL;
+ __u8 index;
+ __u8 value;
+
+ /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
+ long on the USB bus)
+ */
+ if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
+ reg->match.addr == 0 &&
+ (reg->reg < 0x000000ff) &&
+ (reg->val <= 0x000000ff)
+ ) {
+ /* Currently writing to page 0 is only supported. */
+ /* reg_w() only supports 8bit index */
+ index = reg->reg & 0x000000ff;
+ value = reg->val & 0x000000ff;
+
+ /* Note that there shall be no access to other page
+ by any other function between the page swith and
+ the actual register write */
+ ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, index, value);
+
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xdc, 0x01);
+ }
+ return ret;
+}
+
+static int sd_chip_ident(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int ret = -EINVAL;
+
+ if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
+ chip->match.addr == 0) {
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_UNKNOWN;
+ ret = 0;
+ }
+ return ret;
+}
+#endif
+
+/* sub-driver description for pac7302 */
+static struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .set_register = sd_dbg_s_register,
+ .get_chip_ident = sd_chip_ident,
+#endif
+};
+
+/* -- module initialisation -- */
+static __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06f8, 0x3009)},
+ {USB_DEVICE(0x093a, 0x2620)},
+ {USB_DEVICE(0x093a, 0x2621)},
+ {USB_DEVICE(0x093a, 0x2622)},
+ {USB_DEVICE(0x093a, 0x2624)},
+ {USB_DEVICE(0x093a, 0x2626)},
+ {USB_DEVICE(0x093a, 0x2628)},
+ {USB_DEVICE(0x093a, 0x2629)},
+ {USB_DEVICE(0x093a, 0x262a)},
+ {USB_DEVICE(0x093a, 0x262c)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ int ret;
+ ret = usb_register(&sd_driver);
+ if (ret < 0)
+ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index 052714484e8..e5697a6345e 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -57,23 +57,17 @@ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
MODULE_DESCRIPTION("Pixart PAC7311");
MODULE_LICENSE("GPL");
-/* specific webcam descriptor */
+/* specific webcam descriptor for pac7311 */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char brightness;
unsigned char contrast;
- unsigned char colors;
unsigned char gain;
unsigned char exposure;
unsigned char autogain;
__u8 hflip;
__u8 vflip;
- __u8 sensor;
-#define SENSOR_PAC7302 0
-#define SENSOR_PAC7311 1
-
u8 sof_read;
u8 autogain_ignore_frames;
@@ -81,12 +75,8 @@ struct sd {
};
/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
@@ -99,23 +89,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
-/* This control is pac7302 only */
-#define BRIGHTNESS_IDX 0
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
-#define BRIGHTNESS_MAX 0x20
- .maximum = BRIGHTNESS_MAX,
- .step = 1,
-#define BRIGHTNESS_DEF 0x10
- .default_value = BRIGHTNESS_DEF,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
/* This control is for both the 7302 and the 7311 */
{
{
@@ -132,23 +105,6 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setcontrast,
.get = sd_getcontrast,
},
-/* This control is pac7302 only */
-#define SATURATION_IDX 2
- {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
-#define COLOR_MAX 255
- .maximum = COLOR_MAX,
- .step = 1,
-#define COLOR_DEF 127
- .default_value = COLOR_DEF,
- },
- .set = sd_setcolors,
- .get = sd_getcolors,
- },
/* All controls below are for both the 7302 and the 7311 */
{
{
@@ -244,101 +200,9 @@ static const struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-/* pac 7302 */
-static const __u8 init_7302[] = {
-/* index,value */
- 0xff, 0x01, /* page 1 */
- 0x78, 0x00, /* deactivate */
- 0xff, 0x01,
- 0x78, 0x40, /* led off */
-};
-static const __u8 start_7302[] = {
-/* index, len, [value]* */
- 0xff, 1, 0x00, /* page 0 */
- 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
- 0x00, 0x00, 0x00, 0x00,
- 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
- 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
- 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
- 0x26, 2, 0xaa, 0xaa,
- 0x2e, 1, 0x31,
- 0x38, 1, 0x01,
- 0x3a, 3, 0x14, 0xff, 0x5a,
- 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
- 0x00, 0x54, 0x11,
- 0x55, 1, 0x00,
- 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
- 0x6b, 1, 0x00,
- 0x6e, 3, 0x08, 0x06, 0x00,
- 0x72, 3, 0x00, 0xff, 0x00,
- 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
- 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
- 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
- 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
- 0xd2, 0xeb,
- 0xaf, 1, 0x02,
- 0xb5, 2, 0x08, 0x08,
- 0xb8, 2, 0x08, 0x88,
- 0xc4, 4, 0xae, 0x01, 0x04, 0x01,
- 0xcc, 1, 0x00,
- 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
- 0xc1, 0xd7, 0xec,
- 0xdc, 1, 0x01,
- 0xff, 1, 0x01, /* page 1 */
- 0x12, 3, 0x02, 0x00, 0x01,
- 0x3e, 2, 0x00, 0x00,
- 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
- 0x7c, 1, 0x00,
- 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
- 0x02, 0x00,
- 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
- 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
- 0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
- 0xd8, 1, 0x01,
- 0xdb, 2, 0x00, 0x01,
- 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
- 0xe6, 4, 0x00, 0x00, 0x00, 0x01,
- 0xeb, 1, 0x00,
- 0xff, 1, 0x02, /* page 2 */
- 0x22, 1, 0x00,
- 0xff, 1, 0x03, /* page 3 */
- 0x00, 255, /* load the page 3 */
- 0x11, 1, 0x01,
- 0xff, 1, 0x02, /* page 2 */
- 0x13, 1, 0x00,
- 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
- 0x27, 2, 0x14, 0x0c,
- 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
- 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
- 0x6e, 1, 0x08,
- 0xff, 1, 0x01, /* page 1 */
- 0x78, 1, 0x00,
- 0, 0 /* end of sequence */
-};
-
-/* page 3 - the value 0xaa says skip the index - see reg_w_page() */
-static const __u8 page3_7302[] = {
- 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16,
- 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
- 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
- 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
- 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
- 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
- 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
- 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
- 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
- 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
- 0x00
-};
+#define LOAD_PAGE3 255
+#define LOAD_PAGE4 254
+#define END_OF_SEQUENCE 0
/* pac 7311 */
static const __u8 init_7311[] = {
@@ -378,119 +242,154 @@ static const __u8 start_7311[] = {
0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
0x3f, 0x00, 0x0a, 0x01, 0x00,
0xff, 1, 0x04, /* page 4 */
- 0x00, 254, /* load the page 4 */
+ 0, LOAD_PAGE4, /* load the page 4 */
0x11, 1, 0x01,
- 0, 0 /* end of sequence */
+ 0, END_OF_SEQUENCE /* end of sequence */
};
-/* page 4 - the value 0xaa says skip the index - see reg_w_page() */
+#define SKIP 0xaa
+/* page 4 - the value SKIP says skip the index - see reg_w_page() */
static const __u8 page4_7311[] = {
- 0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
- 0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62,
- 0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa,
- 0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68,
+ SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
+ 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
+ 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
+ SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
0x23, 0x28, 0x04, 0x11, 0x00, 0x00
};
-static void reg_w_buf(struct gspca_dev *gspca_dev,
+static int reg_w_buf(struct gspca_dev *gspca_dev,
__u8 index,
const char *buffer, int len)
{
+ int ret;
+
memcpy(gspca_dev->usb_buf, buffer, len);
- usb_control_msg(gspca_dev->dev,
+ ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
1, /* request */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, /* value */
index, gspca_dev->usb_buf, len,
500);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg_w_buf(): "
+ "Failed to write registers to index 0x%x, error %i",
+ index, ret);
+ return ret;
}
-static void reg_w(struct gspca_dev *gspca_dev,
+static int reg_w(struct gspca_dev *gspca_dev,
__u8 index,
__u8 value)
{
+ int ret;
+
gspca_dev->usb_buf[0] = value;
- usb_control_msg(gspca_dev->dev,
+ ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0, /* request */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, gspca_dev->usb_buf, 1,
500);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg_w(): "
+ "Failed to write register to index 0x%x, value 0x%x, error %i",
+ index, value, ret);
+ return ret;
}
-static void reg_w_seq(struct gspca_dev *gspca_dev,
+static int reg_w_seq(struct gspca_dev *gspca_dev,
const __u8 *seq, int len)
{
+ int ret = 0;
while (--len >= 0) {
- reg_w(gspca_dev, seq[0], seq[1]);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, seq[0], seq[1]);
seq += 2;
}
+ return ret;
}
/* load the beginning of a page */
-static void reg_w_page(struct gspca_dev *gspca_dev,
+static int reg_w_page(struct gspca_dev *gspca_dev,
const __u8 *page, int len)
{
int index;
+ int ret = 0;
for (index = 0; index < len; index++) {
- if (page[index] == 0xaa) /* skip this index */
+ if (page[index] == SKIP) /* skip this index */
continue;
gspca_dev->usb_buf[0] = page[index];
- usb_control_msg(gspca_dev->dev,
+ ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0, /* request */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, gspca_dev->usb_buf, 1,
500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w_page(): "
+ "Failed to write register to index 0x%x, "
+ "value 0x%x, error %i",
+ index, page[index], ret);
+ break;
+ }
}
+ return ret;
}
/* output a variable sequence */
-static void reg_w_var(struct gspca_dev *gspca_dev,
- const __u8 *seq)
+static int reg_w_var(struct gspca_dev *gspca_dev,
+ const __u8 *seq,
+ const __u8 *page3, unsigned int page3_len,
+ const __u8 *page4, unsigned int page4_len)
{
int index, len;
+ int ret = 0;
for (;;) {
index = *seq++;
len = *seq++;
switch (len) {
- case 0:
- return;
- case 254:
- reg_w_page(gspca_dev, page4_7311, sizeof page4_7311);
+ case END_OF_SEQUENCE:
+ return ret;
+ case LOAD_PAGE4:
+ ret = reg_w_page(gspca_dev, page4, page4_len);
break;
- case 255:
- reg_w_page(gspca_dev, page3_7302, sizeof page3_7302);
+ case LOAD_PAGE3:
+ ret = reg_w_page(gspca_dev, page3, page3_len);
break;
default:
- if (len > 64) {
+ if (len > USB_BUF_SZ) {
PDEBUG(D_ERR|D_STREAM,
"Incorrect variable sequence");
- return;
+ return -EINVAL;
}
while (len > 0) {
if (len < 8) {
- reg_w_buf(gspca_dev, index, seq, len);
+ ret = reg_w_buf(gspca_dev,
+ index, seq, len);
+ if (ret < 0)
+ return ret;
seq += len;
break;
}
- reg_w_buf(gspca_dev, index, seq, 8);
+ ret = reg_w_buf(gspca_dev, index, seq, 8);
seq += 8;
index += 8;
len -= 8;
}
}
+ if (ret < 0)
+ return ret;
}
/* not reached */
}
-/* this function is called at probe time */
+/* this function is called at probe time for pac7311 */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
@@ -499,22 +398,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
- sd->sensor = id->driver_info;
- if (sd->sensor == SENSOR_PAC7302) {
- PDEBUG(D_CONF, "Find Sensor PAC7302");
- cam->cam_mode = &vga_mode[2]; /* only 640x480 */
- cam->nmodes = 1;
- } else {
- PDEBUG(D_CONF, "Find Sensor PAC7311");
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX)
- | (1 << SATURATION_IDX);
- }
+ PDEBUG(D_CONF, "Find Sensor PAC7311");
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
- sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
sd->gain = GAIN_DEF;
sd->exposure = EXPOSURE_DEF;
sd->autogain = AUTOGAIN_DEF;
@@ -523,91 +411,47 @@ static int sd_config(struct gspca_dev *gspca_dev,
return 0;
}
-/* This function is used by pac7302 only */
-static void setbrightcont(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, v;
- static const __u8 max[10] =
- {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
- 0xd4, 0xec};
- static const __u8 delta[10] =
- {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
- 0x11, 0x0b};
-
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- for (i = 0; i < 10; i++) {
- v = max[i];
- v += (sd->brightness - BRIGHTNESS_MAX)
- * 150 / BRIGHTNESS_MAX; /* 200 ? */
- v -= delta[i] * sd->contrast / CONTRAST_MAX;
- if (v < 0)
- v = 0;
- else if (v > 0xff)
- v = 0xff;
- reg_w(gspca_dev, 0xa2 + i, v);
- }
- reg_w(gspca_dev, 0xdc, 0x01);
-}
-
/* This function is used by pac7311 only */
-static void setcontrast(struct gspca_dev *gspca_dev)
+static int setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
- reg_w(gspca_dev, 0xff, 0x04);
- reg_w(gspca_dev, 0x10, sd->contrast >> 4);
+ ret = reg_w(gspca_dev, 0xff, 0x04);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x10, sd->contrast >> 4);
/* load registers to sensor (Bit 0, auto clear) */
- reg_w(gspca_dev, 0x11, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ return ret;
}
-/* This function is used by pac7302 only */
-static void setcolors(struct gspca_dev *gspca_dev)
+static int setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int i, v;
- static const int a[9] =
- {217, -212, 0, -101, 170, -67, -38, -315, 355};
- static const int b[9] =
- {19, 106, 0, 19, 106, 1, 19, 106, 1};
-
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x11, 0x01);
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- for (i = 0; i < 9; i++) {
- v = a[i] * sd->colors / COLOR_MAX + b[i];
- reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
- reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
- }
- reg_w(gspca_dev, 0xdc, 0x01);
- PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
-}
+ int gain = GAIN_MAX - sd->gain;
+ int ret;
-static void setgain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ if (gain < 1)
+ gain = 1;
+ else if (gain > 245)
+ gain = 245;
+ ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x0e, 0x00);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x0f, gain);
- if (sd->sensor == SENSOR_PAC7302) {
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x10, sd->gain >> 3);
- } else {
- int gain = GAIN_MAX - sd->gain;
- if (gain < 1)
- gain = 1;
- else if (gain > 245)
- gain = 245;
- reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
- reg_w(gspca_dev, 0x0e, 0x00);
- reg_w(gspca_dev, 0x0f, gain);
- }
/* load registers to sensor (Bit 0, auto clear) */
- reg_w(gspca_dev, 0x11, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ return ret;
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static int setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
__u8 reg;
/* register 2 of frame 3/4 contains the clock divider configuring the
@@ -619,97 +463,94 @@ static void setexposure(struct gspca_dev *gspca_dev)
else if (reg > 63)
reg = 63;
- if (sd->sensor == SENSOR_PAC7302) {
- /* On the pac7302 reg2 MUST be a multiple of 3, so round it to
- the nearest multiple of 3, except when between 6 and 12? */
- if (reg < 6 || reg > 12)
- reg = ((reg + 1) / 3) * 3;
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x02, reg);
+ ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x02, reg);
+ /* Page 1 register 8 must always be 0x08 except when not in
+ 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
+ reg <= 3) {
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x08, 0x09);
} else {
- reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
- reg_w(gspca_dev, 0x02, reg);
- /* Page 1 register 8 must always be 0x08 except when not in
- 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
- reg_w(gspca_dev, 0xff, 0x01);
- if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
- reg <= 3)
- reg_w(gspca_dev, 0x08, 0x09);
- else
- reg_w(gspca_dev, 0x08, 0x08);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x08, 0x08);
}
+
/* load registers to sensor (Bit 0, auto clear) */
- reg_w(gspca_dev, 0x11, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ return ret;
}
-static void sethvflip(struct gspca_dev *gspca_dev)
+static int sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
__u8 data;
- if (sd->sensor == SENSOR_PAC7302) {
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- data = (sd->hflip ? 0x08 : 0x00)
- | (sd->vflip ? 0x04 : 0x00);
- } else {
- reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
- data = (sd->hflip ? 0x04 : 0x00)
- | (sd->vflip ? 0x08 : 0x00);
- }
- reg_w(gspca_dev, 0x21, data);
+ ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+ data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x21, data);
/* load registers to sensor (Bit 0, auto clear) */
- reg_w(gspca_dev, 0x11, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x11, 0x01);
+ return ret;
}
-/* this function is called at probe and resume time */
+/* this function is called at probe and resume time for pac7311 */
static int sd_init(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor == SENSOR_PAC7302)
- reg_w_seq(gspca_dev, init_7302, sizeof init_7302);
- else
- reg_w_seq(gspca_dev, init_7311, sizeof init_7311);
-
- return 0;
+ return reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
sd->sof_read = 0;
- if (sd->sensor == SENSOR_PAC7302) {
- reg_w_var(gspca_dev, start_7302);
- setbrightcont(gspca_dev);
- setcolors(gspca_dev);
- } else {
- reg_w_var(gspca_dev, start_7311);
- setcontrast(gspca_dev);
- }
- setgain(gspca_dev);
- setexposure(gspca_dev);
- sethvflip(gspca_dev);
+ ret = reg_w_var(gspca_dev, start_7311,
+ NULL, 0,
+ page4_7311, sizeof(page4_7311));
+ if (0 <= ret)
+ ret = setcontrast(gspca_dev);
+ if (0 <= ret)
+ ret = setgain(gspca_dev);
+ if (0 <= ret)
+ ret = setexposure(gspca_dev);
+ if (0 <= ret)
+ ret = sethvflip(gspca_dev);
/* set correct resolution */
switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
case 2: /* 160x120 pac7311 */
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x17, 0x20);
- reg_w(gspca_dev, 0x87, 0x10);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x17, 0x20);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x87, 0x10);
break;
case 1: /* 320x240 pac7311 */
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x17, 0x30);
- reg_w(gspca_dev, 0x87, 0x11);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x17, 0x30);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x87, 0x11);
break;
case 0: /* 640x480 */
- if (sd->sensor == SENSOR_PAC7302)
- break;
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x17, 0x00);
- reg_w(gspca_dev, 0x87, 0x12);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x17, 0x00);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x87, 0x12);
break;
}
@@ -718,47 +559,42 @@ static int sd_start(struct gspca_dev *gspca_dev)
atomic_set(&sd->avg_lum, -1);
/* start stream */
- reg_w(gspca_dev, 0xff, 0x01);
- if (sd->sensor == SENSOR_PAC7302)
- reg_w(gspca_dev, 0x78, 0x01);
- else
- reg_w(gspca_dev, 0x78, 0x05);
- return 0;
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x78, 0x05);
+
+ return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor == SENSOR_PAC7302) {
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x00);
- reg_w(gspca_dev, 0x78, 0x00);
- return;
- }
- reg_w(gspca_dev, 0xff, 0x04);
- reg_w(gspca_dev, 0x27, 0x80);
- reg_w(gspca_dev, 0x28, 0xca);
- reg_w(gspca_dev, 0x29, 0x53);
- reg_w(gspca_dev, 0x2a, 0x0e);
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x3e, 0x20);
- reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
- reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
- reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
-}
+ int ret;
-/* called on streamoff with alt 0 and on disconnect */
+ ret = reg_w(gspca_dev, 0xff, 0x04);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x27, 0x80);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x28, 0xca);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x29, 0x53);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x2a, 0x0e);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0xff, 0x01);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x3e, 0x20);
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+ if (0 <= ret)
+ ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+}
+
+/* called on streamoff with alt 0 and on disconnect for 7311 */
static void sd_stop0(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (!gspca_dev->present)
- return;
- if (sd->sensor == SENSOR_PAC7302) {
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x40);
- }
}
/* Include pac common sof detection functions */
@@ -773,22 +609,8 @@ static void do_autogain(struct gspca_dev *gspca_dev)
if (avg_lum == -1)
return;
- if (sd->sensor == SENSOR_PAC7302) {
- desired_lum = 270 + sd->brightness * 4;
- /* Hack hack, with the 7202 the first exposure step is
- pretty large, so if we're about to make the first
- exposure increase make the deadzone large to avoid
- oscilating */
- if (desired_lum > avg_lum && sd->gain == GAIN_DEF &&
- sd->exposure > EXPOSURE_DEF &&
- sd->exposure < 42)
- deadzone = 90;
- else
- deadzone = 30;
- } else {
- desired_lum = 200;
- deadzone = 20;
- }
+ desired_lum = 200;
+ deadzone = 20;
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
@@ -797,53 +619,92 @@ static void do_autogain(struct gspca_dev *gspca_dev)
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
}
-static const unsigned char pac7311_jpeg_header1[] = {
- 0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08
+/* JPEG header, part 1 */
+static const unsigned char pac_jpeg_header1[] = {
+ 0xff, 0xd8, /* SOI: Start of Image */
+
+ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
+ 0x00, 0x11, /* length = 17 bytes (including this length field) */
+ 0x08 /* Precision: 8 */
+ /* 2 bytes is placed here: number of image lines */
+ /* 2 bytes is placed here: samples per line */
};
-static const unsigned char pac7311_jpeg_header2[] = {
- 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda,
- 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+/* JPEG header, continued */
+static const unsigned char pac_jpeg_header2[] = {
+ 0x03, /* Number of image components: 3 */
+ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
+ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
+ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+ 0xff, 0xda, /* SOS: Start Of Scan */
+ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
+ 0x03, /* number of components: 3 */
+ 0x01, 0x00, /* selector 1, table 0x00 */
+ 0x02, 0x11, /* selector 2, table 0x11 */
+ 0x03, 0x11, /* selector 3, table 0x11 */
+ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
+ 0x00 /* Successive approximation: 0 */
};
+static void pac_start_frame(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame,
+ __u16 lines, __u16 samples_per_line)
+{
+ unsigned char tmpbuf[4];
+
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ pac_jpeg_header1, sizeof(pac_jpeg_header1));
+
+ tmpbuf[0] = lines >> 8;
+ tmpbuf[1] = lines & 0xff;
+ tmpbuf[2] = samples_per_line >> 8;
+ tmpbuf[3] = samples_per_line & 0xff;
+
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ tmpbuf, sizeof(tmpbuf));
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ pac_jpeg_header2, sizeof(pac_jpeg_header2));
+}
+
/* this function is run at interrupt level */
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned char *sof;
+ struct gspca_frame *frame;
- sof = pac_find_sof(gspca_dev, data, len);
+ sof = pac_find_sof(&sd->sof_read, data, len);
if (sof) {
- unsigned char tmpbuf[4];
int n, lum_offset, footer_length;
- if (sd->sensor == SENSOR_PAC7302) {
- /* 6 bytes after the FF D9 EOF marker a number of lumination
- bytes are send corresponding to different parts of the
- image, the 14th and 15th byte after the EOF seem to
- correspond to the center of the image */
- lum_offset = 61 + sizeof pac_sof_marker;
- footer_length = 74;
- } else {
- lum_offset = 24 + sizeof pac_sof_marker;
- footer_length = 26;
+ frame = gspca_get_i_frame(gspca_dev);
+ if (frame == NULL) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
}
+ /* 6 bytes after the FF D9 EOF marker a number of lumination
+ bytes are send corresponding to different parts of the
+ image, the 14th and 15th byte after the EOF seem to
+ correspond to the center of the image */
+ lum_offset = 24 + sizeof pac_sof_marker;
+ footer_length = 26;
+
/* Finish decoding current frame */
n = (sof - data) - (footer_length + sizeof pac_sof_marker);
if (n < 0) {
frame->data_end += n;
n = 0;
}
- frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ gspca_frame_add(gspca_dev, INTER_PACKET,
data, n);
if (gspca_dev->last_packet_type != DISCARD_PACKET &&
frame->data_end[-2] == 0xff &&
frame->data_end[-1] == 0xd9)
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ gspca_frame_add(gspca_dev, LAST_PACKET,
NULL, 0);
n = sof - data;
@@ -859,43 +720,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
atomic_set(&sd->avg_lum, -1);
/* Start the new frame with the jpeg header */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1));
- if (sd->sensor == SENSOR_PAC7302) {
- /* The PAC7302 has the image rotated 90 degrees */
- tmpbuf[0] = gspca_dev->width >> 8;
- tmpbuf[1] = gspca_dev->width & 0xff;
- tmpbuf[2] = gspca_dev->height >> 8;
- tmpbuf[3] = gspca_dev->height & 0xff;
- } else {
- tmpbuf[0] = gspca_dev->height >> 8;
- tmpbuf[1] = gspca_dev->height & 0xff;
- tmpbuf[2] = gspca_dev->width >> 8;
- tmpbuf[3] = gspca_dev->width & 0xff;
- }
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4);
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2));
+ pac_start_frame(gspca_dev, frame,
+ gspca_dev->height, gspca_dev->width);
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
-}
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightcont(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
@@ -904,10 +732,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
sd->contrast = val;
if (gspca_dev->streaming) {
- if (sd->sensor == SENSOR_PAC7302)
- setbrightcont(gspca_dev);
- else
- setcontrast(gspca_dev);
+ setcontrast(gspca_dev);
}
return 0;
}
@@ -920,24 +745,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1041,7 +848,7 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
-/* sub-driver description */
+/* sub-driver description for pac7311 */
static struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
@@ -1057,21 +864,12 @@ static struct sd_desc sd_desc = {
/* -- module initialisation -- */
static __devinitdata struct usb_device_id device_table[] = {
- {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311},
- {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311},
- {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311},
- {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311},
- {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
- {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
- {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
- {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302},
+ {USB_DEVICE(0x093a, 0x2600)},
+ {USB_DEVICE(0x093a, 0x2601)},
+ {USB_DEVICE(0x093a, 0x2603)},
+ {USB_DEVICE(0x093a, 0x2608)},
+ {USB_DEVICE(0x093a, 0x260e)},
+ {USB_DEVICE(0x093a, 0x260f)},
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h
index 34d4b1494cd..20f67d9b8c0 100644
--- a/drivers/media/video/gspca/pac_common.h
+++ b/drivers/media/video/gspca/pac_common.h
@@ -33,26 +33,101 @@
static const unsigned char pac_sof_marker[5] =
{ 0xff, 0xff, 0x00, 0xff, 0x96 };
-static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev,
+/*
+ The following state machine finds the SOF marker sequence
+ 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream.
+
+ +----------+
+ | 0: START |<---------------\
+ +----------+<-\ |
+ | \---/otherwise |
+ v 0xff |
+ +----------+ otherwise |
+ | 1 |--------------->*
+ | | ^
+ +----------+ |
+ | |
+ v 0xff |
+ +----------+<-\0xff |
+ /->| |--/ |
+ | | 2 |--------------->*
+ | | | otherwise ^
+ | +----------+ |
+ | | |
+ | v 0x00 |
+ | +----------+ |
+ | | 3 | |
+ | | |--------------->*
+ | +----------+ otherwise ^
+ | | |
+ 0xff | v 0xff |
+ | +----------+ |
+ \--| 4 | |
+ | |----------------/
+ +----------+ otherwise
+ |
+ v 0x96
+ +----------+
+ | FOUND |
+ +----------+
+*/
+
+static unsigned char *pac_find_sof(u8 *sof_read,
unsigned char *m, int len)
{
- struct sd *sd = (struct sd *) gspca_dev;
int i;
/* Search for the SOF marker (fixed part) in the header */
for (i = 0; i < len; i++) {
- if (m[i] == pac_sof_marker[sd->sof_read]) {
- sd->sof_read++;
- if (sd->sof_read == sizeof(pac_sof_marker)) {
+ switch (*sof_read) {
+ case 0:
+ if (m[i] == 0xff)
+ *sof_read = 1;
+ break;
+ case 1:
+ if (m[i] == 0xff)
+ *sof_read = 2;
+ else
+ *sof_read = 0;
+ break;
+ case 2:
+ switch (m[i]) {
+ case 0x00:
+ *sof_read = 3;
+ break;
+ case 0xff:
+ /* stay in this state */
+ break;
+ default:
+ *sof_read = 0;
+ }
+ break;
+ case 3:
+ if (m[i] == 0xff)
+ *sof_read = 4;
+ else
+ *sof_read = 0;
+ break;
+ case 4:
+ switch (m[i]) {
+ case 0x96:
+ /* Pattern found */
PDEBUG(D_FRAM,
"SOF found, bytes to analyze: %u."
" Frame starts at byte #%u",
len, i + 1);
- sd->sof_read = 0;
+ *sof_read = 0;
return m + i + 1;
+ break;
+ case 0xff:
+ *sof_read = 2;
+ break;
+ default:
+ *sof_read = 0;
}
- } else {
- sd->sof_read = 0;
+ break;
+ default:
+ *sof_read = 0;
}
}
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index cdad3db3336..b1944a7cbb0 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -2342,7 +2342,6 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
@@ -2378,22 +2377,22 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
avg_lum >>= 9;
atomic_set(&sd->avg_lum, avg_lum);
gspca_frame_add(gspca_dev, LAST_PACKET,
- frame, data, len);
+ data, len);
return;
}
if (gspca_dev->last_packet_type == LAST_PACKET) {
if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
& MODE_JPEG) {
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ gspca_frame_add(gspca_dev, INTER_PACKET,
data, len);
} else {
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
data, len);
}
} else {
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
}
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index cf3af8de6e9..5be95bc6513 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -304,7 +304,7 @@ static const __u8 initOv6650[] = {
};
static const __u8 ov6650_sensor_init[][8] =
{
- /* Bright, contrast, etc are set througth SCBB interface.
+ /* Bright, contrast, etc are set through SCBB interface.
* AVCAP on win2 do not send any data on this controls. */
/* Anyway, some registers appears to alter bright and constrat */
@@ -995,8 +995,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- unsigned char *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
int i;
@@ -1054,12 +1053,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
pkt_type = DISCARD_PACKET;
}
- frame = gspca_frame_add(gspca_dev, pkt_type,
- frame, data, 0);
+ gspca_frame_add(gspca_dev, pkt_type,
+ NULL, 0);
data += i + fr_h_sz;
len -= i + fr_h_sz;
gspca_frame_add(gspca_dev, FIRST_PACKET,
- frame, data, len);
+ data, len);
return;
}
}
@@ -1068,15 +1067,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) {
/* In raw mode we sometimes get some garbage after the frame
ignore this */
- int used = frame->data_end - frame->data;
+ struct gspca_frame *frame;
+ int used;
int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+ frame = gspca_get_i_frame(gspca_dev);
+ if (frame == NULL) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ used = frame->data_end - frame->data;
if (used + len > size)
len = size - used;
}
- gspca_frame_add(gspca_dev, INTER_PACKET,
- frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 33f4d0a1f6f..0bd36a00dd2 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -1,8 +1,8 @@
/*
- * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library
- * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
*
- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
*
* 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
@@ -72,8 +72,9 @@ struct sd {
#define SENSOR_OV7630 5
#define SENSOR_OV7648 6
#define SENSOR_OV7660 7
-#define SENSOR_SP80708 8
- u8 i2c_base;
+#define SENSOR_PO1030 8
+#define SENSOR_SP80708 9
+ u8 i2c_addr;
u8 *jpeg_hdr;
};
@@ -250,7 +251,7 @@ static struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
.step = 1,
-#define FREQ_DEF 2
+#define FREQ_DEF 1
.default_value = FREQ_DEF,
},
.set = sd_setfreq,
@@ -277,7 +278,9 @@ static __u32 ctrl_dis[] = {
(1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
/* SENSOR_OV7660 7 */
(1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
- (1 << FREQ_IDX), /* SENSOR_SP80708 8 */
+ (1 << FREQ_IDX), /* SENSOR_PO1030 8 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
+ (1 << FREQ_IDX), /* SENSOR_SP80708 9 */
};
static const struct v4l2_pix_format vga_mode[] = {
@@ -304,7 +307,7 @@ static const u8 sn_hv7131[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41,
/* reg18 reg19 reg1a reg1b */
@@ -315,7 +318,7 @@ static const u8 sn_mi0360[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61,
/* reg18 reg19 reg1a reg1b */
@@ -337,7 +340,7 @@ static const u8 sn_mt9v111[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x5c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40,
/* reg18 reg19 reg1a reg1b */
@@ -346,7 +349,7 @@ static const u8 sn_mt9v111[0x1c] = {
static const u8 sn_om6802[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20,
+ 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19,
/* reg8 reg9 rega regb regc regd rege regf */
0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
@@ -359,7 +362,7 @@ static const u8 sn_ov7630[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2,
/* reg18 reg19 reg1a reg1b */
@@ -370,7 +373,7 @@ static const u8 sn_ov7648[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
/* reg18 reg19 reg1a reg1b */
@@ -388,11 +391,22 @@ static const u8 sn_ov7660[0x1c] = {
0x07, 0x00, 0x00, 0x00
};
+static const u8 sn_po1030[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
static const u8 sn_sp80708[0x1c] = {
/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00,
/* reg18 reg19 reg1a reg1b */
@@ -409,6 +423,7 @@ static const u8 *sn_tb[] = {
sn_ov7630,
sn_ov7648,
sn_ov7660,
+ sn_po1030,
sn_sp80708
};
@@ -455,7 +470,7 @@ static const u8 hv7131r_sensor_init[][8] = {
{0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
@@ -464,6 +479,8 @@ static const u8 hv7131r_sensor_init[][8] = {
{0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10},
+ /* set sensor clock */
{}
};
static const u8 mi0360_sensor_init[][8] = {
@@ -545,7 +562,7 @@ static const u8 mo4000_sensor_init[][8] = {
};
static const u8 mt9v111_sensor_init[][8] = {
{0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
- /* delay 20 ms */
+ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
{0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
{0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
@@ -572,7 +589,9 @@ static const u8 mt9v111_sensor_init[][8] = {
{0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
{0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
{0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
- /*******/
+ {}
+};
+static const u8 mt9v111_sensor_param1[][8] = {
{0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10},
@@ -585,14 +604,20 @@ static const u8 mt9v111_sensor_init[][8] = {
{0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */
{}
};
+static const u8 om6802_init0[2][8] = {
+/*fixme: variable*/
+ {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10},
+};
static const u8 om6802_sensor_init[][8] = {
- {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10},
+ /* factory mode */
{0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10},
+ /* output raw RGB */
+ {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */
{0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10},
- /* white balance & auto-exposure */
+ /* auto-exposure speed (0) / white balance mode (auto RGB) */
/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10},
* set color mode */
/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10},
@@ -606,26 +631,29 @@ static const u8 om6802_sensor_init[][8] = {
/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10},
* preset gamma */
{0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10},
- /* luminance mode (0x4f = AE) */
+ /* luminance mode (0x4f -> AutoExpo on) */
{0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10},
/* preset shutter */
/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10},
* auto frame rate */
/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */
-
-/* {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */
-/* {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */
-/* {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */
-/* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 om6802_sensor_param1[][8] = {
+ {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10},
{}
};
static const u8 ov7630_sensor_init[][8] = {
{0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
-/* win: delay 20ms */
+ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
-/* win: delay 20ms */
+ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
/* win: i2c_r from 00 to 80 */
{0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
@@ -677,6 +705,7 @@ static const u8 ov7630_sensor_init[][8] = {
static const u8 ov7648_sensor_init[][8] = {
{0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
{0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
{0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
@@ -701,7 +730,9 @@ static const u8 ov7648_sensor_init[][8] = {
/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */
-/*...*/
+ {}
+};
+static const u8 ov7648_sensor_param1[][8] = {
/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN
* set by setvflip */
@@ -723,7 +754,7 @@ static const u8 ov7648_sensor_init[][8] = {
static const u8 ov7660_sensor_init[][8] = {
{0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
-/* (delay 20ms) */
+ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
{0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
/* Outformat = rawRGB */
{0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
@@ -783,8 +814,11 @@ static const u8 ov7660_sensor_init[][8] = {
{0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
{0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
{0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
+/* not in all ms-win traces*/
{0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
-/****** (some exchanges in the win trace) ******/
+ {}
+};
+static const u8 ov7660_sensor_param1[][8] = {
{0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
/* bits[3..0]reserved */
{0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
@@ -797,6 +831,7 @@ static const u8 ov7660_sensor_init[][8] = {
{0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */
/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */
/****** (some exchanges in the win trace) ******/
+/*fixme:param2*/
{0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
{0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */
{0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */
@@ -804,6 +839,7 @@ static const u8 ov7660_sensor_init[][8] = {
/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */
/****** (some exchanges in the win trace) ******/
/******!! startsensor KO if changed !!****/
+/*fixme: param3*/
{0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
@@ -811,6 +847,60 @@ static const u8 ov7660_sensor_init[][8] = {
{}
};
+static const u8 po1030_sensor_init[][8] = {
+/* the sensor registers are described in m5602/m5602_po1030.h */
+ {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
+ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10},
+ {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10},
+ {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
+ {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */
+ {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10},
+ {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */
+ {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10},
+ {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */
+ {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10},
+ {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10},
+ {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
+ {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10},
+ {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10},
+ {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10},
+ {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */
+ {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10},
+
+ {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10},
+ {}
+};
+static const u8 po1030_sensor_param1[][8] = {
+/* from ms-win traces - these values change with auto gain/expo/wb.. */
+ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+/* mean values */
+ {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */
+ {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */
+ {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */
+
+ {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */
+ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */
+ {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10},
+/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */
+ {}
+};
+
static const u8 sp80708_sensor_init[][8] = {
{0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
@@ -883,7 +973,9 @@ static const u8 sp80708_sensor_init[][8] = {
{0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
- /********/
+ {}
+};
+static const u8 sp80708_sensor_param1[][8] = {
{0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
@@ -894,6 +986,19 @@ static const u8 sp80708_sensor_init[][8] = {
{}
};
+static const u8 (*sensor_init[10])[8] = {
+ hv7131r_sensor_init, /* HV7131R 0 */
+ mi0360_sensor_init, /* MI0360 1 */
+ mo4000_sensor_init, /* MO4000 2 */
+ mt9v111_sensor_init, /* MT9V111 3 */
+ om6802_sensor_init, /* OM6802 4 */
+ ov7630_sensor_init, /* OV7630 5 */
+ ov7648_sensor_init, /* OV7648 6 */
+ ov7660_sensor_init, /* OV7660 7 */
+ po1030_sensor_init, /* PO1030 8 */
+ sp80708_sensor_init, /* SP80708 9 */
+};
+
/* read <len> bytes to gspca_dev->usb_buf */
static void reg_r(struct gspca_dev *gspca_dev,
u16 value, int len)
@@ -958,8 +1063,15 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
struct sd *sd = (struct sd *) gspca_dev;
PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val);
- gspca_dev->usb_buf[0] = 0x81 | (2 << 4); /* = a1 */
- gspca_dev->usb_buf[1] = sd->i2c_base;
+ switch (sd->sensor) {
+ case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */
+ gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
+ break;
+ default: /* i2c command = a1 (400 kHz) */
+ gspca_dev->usb_buf[0] = 0x81 | (2 << 4);
+ break;
+ }
+ gspca_dev->usb_buf[1] = sd->i2c_addr;
gspca_dev->usb_buf[2] = reg;
gspca_dev->usb_buf[3] = val;
gspca_dev->usb_buf[4] = 0;
@@ -991,14 +1103,21 @@ static void i2c_w8(struct gspca_dev *gspca_dev,
msleep(2);
}
-/* read 5 bytes in gspca_dev->usb_buf */
-static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg)
+/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
+static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 mode[8];
- mode[0] = 0x81 | 0x10;
- mode[1] = sd->i2c_base;
+ switch (sd->sensor) {
+ case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */
+ mode[0] = 0x80 | 0x10;
+ break;
+ default: /* i2c command = 91 (400 kHz) */
+ mode[0] = 0x81 | 0x10;
+ break;
+ }
+ mode[1] = sd->i2c_addr;
mode[2] = reg;
mode[3] = 0;
mode[4] = 0;
@@ -1007,33 +1126,43 @@ static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg)
mode[7] = 0x10;
i2c_w8(gspca_dev, mode);
msleep(2);
- mode[0] = 0x81 | (5 << 4) | 0x02;
+ mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02;
mode[2] = 0;
i2c_w8(gspca_dev, mode);
msleep(2);
reg_r(gspca_dev, 0x0a, 5);
}
-static int hv7131r_probe(struct gspca_dev *gspca_dev)
+static void i2c_w_seq(struct gspca_dev *gspca_dev,
+ const u8 (*data)[8])
+{
+ while ((*data)[0] != 0) {
+ if ((*data)[0] != 0xdd)
+ i2c_w8(gspca_dev, *data);
+ else
+ msleep((*data)[1]);
+ data++;
+ }
+}
+
+static void hv7131r_probe(struct gspca_dev *gspca_dev)
{
i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
msleep(10);
reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
msleep(10);
- i2c_r5(gspca_dev, 0); /* read sensor id */
+ i2c_r(gspca_dev, 0, 5); /* read sensor id */
if (gspca_dev->usb_buf[0] == 0x02
&& gspca_dev->usb_buf[1] == 0x09
&& gspca_dev->usb_buf[2] == 0x01
&& gspca_dev->usb_buf[3] == 0x00
&& gspca_dev->usb_buf[4] == 0x00) {
- PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
- return 0;
+ PDEBUG(D_PROBE, "Sensor sn9c102P HV7131R found");
+ return;
}
- PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x",
+ PDEBUG(D_PROBE, "Sensor 0x%02x 0x%02x 0x%02x - sn9c102P not found",
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
gspca_dev->usb_buf[2]);
- PDEBUG(D_PROBE, "Sensor sn9c102P Not found");
- return -ENODEV;
}
static void mi0360_probe(struct gspca_dev *gspca_dev)
@@ -1075,7 +1204,6 @@ static void mi0360_probe(struct gspca_dev *gspca_dev)
case 0x823a:
PDEBUG(D_PROBE, "Sensor mt9v111");
sd->sensor = SENSOR_MT9V111;
- sd->i2c_base = 0x5c;
break;
case 0x8243:
PDEBUG(D_PROBE, "Sensor mi0360");
@@ -1086,7 +1214,42 @@ static void mi0360_probe(struct gspca_dev *gspca_dev)
}
}
-static int configure_gpio(struct gspca_dev *gspca_dev,
+static void ov7648_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* check ov76xx */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x21;
+ i2c_r(gspca_dev, 0x0a, 2);
+ if (gspca_dev->usb_buf[3] == 0x76) { /* ov76xx */
+ PDEBUG(D_PROBE, "Sensor ov%02x%02x",
+ gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]);
+ return;
+ }
+
+ /* reset */
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+
+ /* check po1030 */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x6e;
+ i2c_r(gspca_dev, 0x00, 2);
+ if (gspca_dev->usb_buf[3] == 0x10 /* po1030 */
+ && gspca_dev->usb_buf[4] == 0x30) {
+ PDEBUG(D_PROBE, "Sensor po1030");
+ sd->sensor = SENSOR_PO1030;
+ return;
+ }
+
+ PDEBUG(D_PROBE, "Unknown sensor %02x%02x",
+ gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]);
+}
+
+static void bridge_init(struct gspca_dev *gspca_dev,
const u8 *sn9c1xx)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1103,9 +1266,10 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
/* configure gpio */
reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2);
reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
- reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */
+ reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);
switch (sd->sensor) {
case SENSOR_OV7660:
+ case SENSOR_PO1030:
case SENSOR_SP80708:
reg9a = reg9a_spec;
break;
@@ -1115,7 +1279,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
}
reg_w(gspca_dev, 0x9a, reg9a, 6);
- reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); /*fixme:jfm was 60 only*/
+ reg_w(gspca_dev, 0xd4, regd4, sizeof regd4);
reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
@@ -1127,10 +1291,22 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
reg_w1(gspca_dev, 0x01, 0x40);
break;
case SENSOR_OM6802:
- reg_w1(gspca_dev, 0x02, 0x71);
- reg_w1(gspca_dev, 0x01, 0x42);
+ msleep(10);
+ reg_w1(gspca_dev, 0x02, 0x73);
+ reg_w1(gspca_dev, 0x17, 0x60);
+ reg_w1(gspca_dev, 0x01, 0x22);
+ msleep(100);
+ reg_w1(gspca_dev, 0x01, 0x62);
+ reg_w1(gspca_dev, 0x17, 0x64);
reg_w1(gspca_dev, 0x17, 0x64);
reg_w1(gspca_dev, 0x01, 0x42);
+ msleep(10);
+ reg_w1(gspca_dev, 0x01, 0x42);
+ i2c_w8(gspca_dev, om6802_init0[0]);
+ i2c_w8(gspca_dev, om6802_init0[1]);
+ msleep(15);
+ reg_w1(gspca_dev, 0x02, 0x71);
+ msleep(150);
break;
case SENSOR_OV7630:
reg_w1(gspca_dev, 0x01, 0x61);
@@ -1144,7 +1320,14 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
reg_w1(gspca_dev, 0x01, 0x62);
reg_w1(gspca_dev, 0x01, 0x42);
break;
+ case SENSOR_PO1030:
+ reg_w1(gspca_dev, 0x01, 0x61);
+ reg_w1(gspca_dev, 0x17, 0x20);
+ reg_w1(gspca_dev, 0x01, 0x60);
+ reg_w1(gspca_dev, 0x01, 0x40);
+ break;
case SENSOR_OV7660:
+ /* fall thru */
case SENSOR_SP80708:
reg_w1(gspca_dev, 0x01, 0x63);
reg_w1(gspca_dev, 0x17, 0x20);
@@ -1153,143 +1336,18 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
msleep(100);
reg_w1(gspca_dev, 0x02, 0x62);
break;
+ default:
/* case SENSOR_HV7131R: */
/* case SENSOR_MI0360: */
/* case SENSOR_MO4000: */
- default:
reg_w1(gspca_dev, 0x01, 0x43);
reg_w1(gspca_dev, 0x17, 0x61);
reg_w1(gspca_dev, 0x01, 0x42);
- if (sd->sensor == SENSOR_HV7131R) {
- if (hv7131r_probe(gspca_dev) < 0)
- return -ENODEV;
- }
+ if (sd->sensor == SENSOR_HV7131R
+ && sd->bridge == BRIDGE_SN9C102P)
+ hv7131r_probe(gspca_dev);
break;
}
- return 0;
-}
-
-static void hv7131R_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
- static const u8 SetSensorClk[] = /* 0x08 Mclk */
- { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 };
-
- while (hv7131r_sensor_init[i][0]) {
- i2c_w8(gspca_dev, hv7131r_sensor_init[i]);
- i++;
- }
- i2c_w8(gspca_dev, SetSensorClk);
-}
-
-static void mi0360_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- while (mi0360_sensor_init[i][0]) {
- i2c_w8(gspca_dev, mi0360_sensor_init[i]);
- i++;
- }
-}
-
-static void mo4000_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- while (mo4000_sensor_init[i][0]) {
- i2c_w8(gspca_dev, mo4000_sensor_init[i]);
- i++;
- }
-}
-
-static void mt9v111_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
- i++;
- msleep(20);
- while (mt9v111_sensor_init[i][0]) {
- i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
- i++;
- }
-}
-
-static void om6802_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- while (om6802_sensor_init[i][0]) {
- i2c_w8(gspca_dev, om6802_sensor_init[i]);
- i++;
- }
-}
-
-static void ov7630_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 76 01 */
- i++;
- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 (RGB+SRST) */
- i++;
- msleep(20);
- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */
- i++;
- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 */
- i++;
- msleep(20);
- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */
- i++;
-/*jfm:win i2c_r from 00 to 80*/
-
- while (ov7630_sensor_init[i][0]) {
- i2c_w8(gspca_dev, ov7630_sensor_init[i]);
- i++;
- }
-}
-
-static void ov7648_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- i2c_w8(gspca_dev, ov7648_sensor_init[i]);
- i++;
-/* win: dble reset */
- i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */
- i++;
- msleep(20);
-/* win: i2c reg read 00..7f */
- while (ov7648_sensor_init[i][0]) {
- i2c_w8(gspca_dev, ov7648_sensor_init[i]);
- i++;
- }
-}
-
-static void ov7660_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- i2c_w8(gspca_dev, ov7660_sensor_init[i]); /* reset SCCB */
- i++;
- msleep(20);
- while (ov7660_sensor_init[i][0]) {
- i2c_w8(gspca_dev, ov7660_sensor_init[i]);
- i++;
- }
-}
-
-static void sp80708_InitSensor(struct gspca_dev *gspca_dev)
-{
- int i = 0;
-
- i2c_w8(gspca_dev, sp80708_sensor_init[i]); /* reset SCCB */
- i++;
- msleep(20);
- while (sp80708_sensor_init[i][0]) {
- i2c_w8(gspca_dev, sp80708_sensor_init[i]);
- i++;
- }
}
/* this function is called at probe time */
@@ -1305,8 +1363,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->npkt = 24; /* 24 packets per ISOC message */
sd->bridge = id->driver_info >> 16;
- sd->sensor = id->driver_info >> 8;
- sd->i2c_base = id->driver_info;
+ sd->sensor = id->driver_info;
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
@@ -1322,7 +1379,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->quality = QUALITY_DEF;
sd->jpegqual = 80;
- gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
return 0;
}
@@ -1330,6 +1386,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ const u8 *sn9c1xx;
u8 regGpio[] = { 0x29, 0x74 };
u8 regF1;
@@ -1356,8 +1413,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
case BRIDGE_SN9C120:
if (regF1 != 0x12)
return -ENODEV;
- if (sd->sensor == SENSOR_MI0360)
+ switch (sd->sensor) {
+ case SENSOR_MI0360:
mi0360_probe(gspca_dev);
+ break;
+ case SENSOR_OV7648:
+ ov7648_probe(gspca_dev);
+ break;
+ }
regGpio[1] = 0x70;
reg_w(gspca_dev, 0x01, regGpio, 2);
break;
@@ -1372,6 +1435,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0xf1, 0x01);
+ /* set the i2c address */
+ sn9c1xx = sn_tb[sd->sensor];
+ sd->i2c_addr = sn9c1xx[9];
+
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+
return 0;
}
@@ -1383,7 +1452,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
switch (sd->sensor) {
case SENSOR_HV7131R: {
u8 Expodoit[] =
- { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 };
+ { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 };
Expodoit[3] = expo >> 16;
Expodoit[4] = expo >> 8;
@@ -1393,7 +1462,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
}
case SENSOR_MI0360: {
u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
- { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 };
+ { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
static const u8 doit[] = /* update sensor */
{ 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
static const u8 sensorgo[] = /* sensor on */
@@ -1412,9 +1481,9 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
}
case SENSOR_MO4000: {
u8 expoMof[] =
- { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 };
+ { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 };
u8 expoMo10[] =
- { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 };
+ { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 };
static const u8 gainMo[] =
{ 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
@@ -1450,6 +1519,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
case SENSOR_OM6802: {
u8 gainOm[] =
{ 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ /* preset AGC - works when AutoExpo = off */
if (expo > 0x03ff)
expo = 0x03ff;
@@ -1457,7 +1527,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
expo = 0x0001;
gainOm[3] = expo >> 2;
i2c_w8(gspca_dev, gainOm);
- reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f);
+ reg_w1(gspca_dev, 0x96, expo >> 5);
PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
break;
}
@@ -1489,7 +1559,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
case SENSOR_MT9V111:
expo = sd->brightness >> 8;
sd->exposure = setexposure(gspca_dev, expo);
- break;
+ return; /* don't set the Y offset */
case SENSOR_OM6802:
expo = sd->brightness >> 6;
sd->exposure = setexposure(gspca_dev, expo);
@@ -1497,8 +1567,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
break;
}
- if (sd->sensor != SENSOR_MT9V111)
- reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
+ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
}
static void setcontrast(struct gspca_dev *gspca_dev)
@@ -1526,6 +1595,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
-24, -38, 64, /* UR UG UB */
62, -51, -9 /* VR VG VB */
};
+
for (i = 0; i < 6; i++) {
v = uv[i] * sd->colors / COLOR_DEF;
reg8a[i * 2] = v;
@@ -1605,6 +1675,8 @@ static void setvflip(struct sd *sd)
{
u8 comn;
+ if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX))
+ return;
if (sd->sensor == SENSOR_OV7630) {
comn = 0x02;
if (!sd->vflip)
@@ -1726,8 +1798,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
- u8 reg1, reg17;
+ u8 reg1, reg2, reg17;
const u8 *sn9c1xx;
+ const u8 (*init)[8];
int mode;
static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
@@ -1743,8 +1816,26 @@ static int sd_start(struct gspca_dev *gspca_dev)
0x21); /* JPEG 422 */
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- sn9c1xx = sn_tb[(int) sd->sensor];
- configure_gpio(gspca_dev, sn9c1xx);
+ /* initialize the bridge */
+ sn9c1xx = sn_tb[sd->sensor];
+ bridge_init(gspca_dev, sn9c1xx);
+
+ /* initialize the sensor */
+ i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
+
+ switch (sd->sensor) {
+ case SENSOR_OM6802:
+ reg2 = 0x71;
+ break;
+ case SENSOR_SP80708:
+ reg2 = 0x62;
+ break;
+ default:
+ reg2 = 0x40;
+ break;
+ }
+ reg_w1(gspca_dev, 0x02, reg2);
+ reg_w1(gspca_dev, 0x02, reg2);
reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]);
reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]);
@@ -1771,6 +1862,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_OV7660:
reg17 = 0xa0;
break;
+ case SENSOR_PO1030:
+ reg17 = 0xa0;
+ break;
default:
reg17 = 0x60;
break;
@@ -1791,6 +1885,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x9a, 0x07);
reg_w1(gspca_dev, 0x99, 0x59);
break;
+ case SENSOR_OM6802:
+ reg_w1(gspca_dev, 0x9a, 0x08);
+ reg_w1(gspca_dev, 0x99, 0x10);
+ break;
case SENSOR_OV7648:
reg_w1(gspca_dev, 0x9a, 0x0a);
reg_w1(gspca_dev, 0x99, 0x60);
@@ -1806,21 +1904,20 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
- mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */
+ reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */
+ reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */
+
+ init = NULL;
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
if (mode)
reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */
else
reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */
reg17 = 0x61; /* 0x:20: enable sensor clock */
switch (sd->sensor) {
- case SENSOR_HV7131R:
- hv7131R_InitSensor(gspca_dev);
- break;
- case SENSOR_MI0360:
- mi0360_InitSensor(gspca_dev);
- break;
case SENSOR_MO4000:
- mo4000_InitSensor(gspca_dev);
if (mode) {
/* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */
reg1 = 0x06; /* clk 24Mz */
@@ -1830,7 +1927,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
break;
case SENSOR_MT9V111:
- mt9v111_InitSensor(gspca_dev);
+ init = mt9v111_sensor_param1;
if (mode) {
reg1 = 0x04; /* 320 clk 48Mhz */
} else {
@@ -1839,22 +1936,21 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
break;
case SENSOR_OM6802:
- om6802_InitSensor(gspca_dev);
+ init = om6802_sensor_param1;
reg17 = 0x64; /* 640 MCKSIZE */
break;
case SENSOR_OV7630:
- ov7630_InitSensor(gspca_dev);
setvflip(sd);
reg17 = 0xe2;
reg1 = 0x44;
break;
case SENSOR_OV7648:
- ov7648_InitSensor(gspca_dev);
+ init = ov7648_sensor_param1;
reg17 = 0x21;
/* reg1 = 0x42; * 42 - 46? */
break;
case SENSOR_OV7660:
- ov7660_InitSensor(gspca_dev);
+ init = ov7660_sensor_param1;
if (sd->bridge == BRIDGE_SN9C120) {
if (mode) { /* 320x240 - 160x120 */
reg17 = 0xa2;
@@ -1866,9 +1962,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
* inverse power down */
}
break;
+ case SENSOR_PO1030:
+ init = po1030_sensor_param1;
+ reg17 = 0xa2;
+ reg1 = 0x44;
+ break;
default:
/* case SENSOR_SP80708: */
- sp80708_InitSensor(gspca_dev);
+ init = sp80708_sensor_param1;
if (mode) {
/*?? reg1 = 0x04; * 320 clk 48Mhz */
} else {
@@ -1877,6 +1978,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
break;
}
+
+ /* more sensor initialization - param1 */
+ if (init != NULL) {
+ i2c_w_seq(gspca_dev, init);
+/* init = NULL; */
+ }
+
reg_w(gspca_dev, 0xc0, C0, 6);
reg_w(gspca_dev, 0xca, CA, 4);
switch (sd->sensor) {
@@ -1891,6 +1999,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
+
/* here change size mode 0 -> VGA; 1 -> CIF */
sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
reg_w1(gspca_dev, 0x18, sd->reg18);
@@ -1898,6 +2007,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x17, reg17);
reg_w1(gspca_dev, 0x01, reg1);
+
switch (sd->sensor) {
case SENSOR_OV7630:
setvflip(sd);
@@ -1937,14 +2047,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
/* fall thru */
case SENSOR_MT9V111:
case SENSOR_OV7630:
+ case SENSOR_PO1030:
data = 0x29;
break;
- default:
-/* case SENSOR_MO4000: */
-/* case SENSOR_OV7660: */
- break;
}
- sn9c1xx = sn_tb[(int) sd->sensor];
+ sn9c1xx = sn_tb[sd->sensor];
reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]);
reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
@@ -1987,11 +2094,19 @@ static void do_autogain(struct gspca_dev *gspca_dev)
sd->exposure = setexposure(gspca_dev,
(unsigned int) (expotimes << 8));
break;
+ case SENSOR_OM6802:
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 2;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = setexposure(gspca_dev,
+ (unsigned int) expotimes);
+ setredblue(gspca_dev);
+ break;
default:
/* case SENSOR_MO4000: */
/* case SENSOR_MI0360: */
/* case SENSOR_MT9V111: */
-/* case SENSOR_OM6802: */
expotimes = sd->exposure;
expotimes += (luma_mean - delta) >> 6;
if (expotimes < 0)
@@ -2007,7 +2122,6 @@ static void do_autogain(struct gspca_dev *gspca_dev)
/* scan the URB packets */
/* This function is run at interrupt level. */
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
@@ -2019,7 +2133,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* end of frame */
gspca_frame_add(gspca_dev, LAST_PACKET,
- frame, data, sof + 2);
+ data, sof + 2);
if (sd->ag_cnt < 0)
return;
/* w1 w2 w3 */
@@ -2042,10 +2156,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
if (gspca_dev->last_packet_type == LAST_PACKET) {
/* put the JPEG 422 header */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -2295,69 +2409,69 @@ static const struct sd_desc sd_desc = {
};
/* -- module initialisation -- */
-#define BSI(bridge, sensor, i2c_addr) \
+#define BS(bridge, sensor) \
.driver_info = (BRIDGE_ ## bridge << 16) \
- | (SENSOR_ ## sensor << 8) \
- | (i2c_addr)
+ | SENSOR_ ## sensor
static const __devinitdata struct usb_device_id device_table[] = {
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
- {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)},
- {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)},
+ {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)},
+ {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)},
#endif
- {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
- {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
- {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
- {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
- {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
- {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)},
- {USB_DEVICE(0x06f8, 0x3008), BSI(SN9C105, OV7660, 0x21)},
- {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)},
-/* bw600.inf:
- {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */
-/* {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */
-/* {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */
- {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)},
-/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */
- {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)},
-/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */
-/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */
- {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)},
-/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */
-/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */
- {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)},
+ {USB_DEVICE(0x045e, 0x00f5), BS(SN9C105, OV7660)},
+ {USB_DEVICE(0x045e, 0x00f7), BS(SN9C105, OV7660)},
+ {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)},
+ {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)},
+/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */
+ {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)},
+/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */
+/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */
+ {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
+/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
+ {USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)},
+/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
+/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
+/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
+ {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)},
+/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */
+/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */
+ {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
- {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)},
- {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)},
+ {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)},
+ {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
#endif
- {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/
-/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */
-/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */
- {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/
- {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/
- {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/
- {USB_DEVICE(0x0c45, 0x610e), BSI(SN9C120, OV7630, 0x21)}, /*sn9c128*/
-/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */
-/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
- {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/
+ {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/
+/* {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, P1030xC)}, */
+/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
+ {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/
+/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */
+/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */
+/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */
+ {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/
/*bw600.inf:*/
- {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/
- {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)},
- {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)},
-/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */
+ {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/
+ {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
+ {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
+/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
- {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)},
+ {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
#endif
-/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */
- {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)},
- {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)},
+/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
+ {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
+ {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
- {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
+ {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)},
#endif
- {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
- {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)},
-/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/
- {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/
- {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)},
+ {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)},
+/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index fab7ef85a6c..fe46868a87f 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -589,7 +589,7 @@ static void spca500_reinit(struct gspca_dev *gspca_dev)
int err;
__u8 Data;
- /* some unknow command from Aiptek pocket dv and family300 */
+ /* some unknown command from Aiptek pocket dv and family300 */
reg_w(gspca_dev, 0x00, 0x0d01, 0x01);
reg_w(gspca_dev, 0x00, 0x0d03, 0x00);
@@ -899,8 +899,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -913,11 +912,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* gspca_dev->last_packet_type = DISCARD_PACKET; */
return;
}
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ gspca_frame_add(gspca_dev, LAST_PACKET,
ffd9, 2);
/* put the JPEG header in the new frame */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
data += SPCA500_OFFSET_DATA;
@@ -931,7 +930,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
i = 0;
do {
if (data[i] == 0xff) {
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ gspca_frame_add(gspca_dev, INTER_PACKET,
data, i + 1);
len -= i;
data += i;
@@ -940,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
i++;
} while (i < len);
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static void setbrightness(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index b74a34218da..6761a3048a9 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -1636,7 +1636,7 @@ static const __u16 spca501c_arowana_init_data[][3] = {
{}
};
-/* Unknow camera from Ori Usbid 0x0000:0x0000 */
+/* Unknown camera from Ori Usbid 0x0000:0x0000 */
/* Based on snoops from Ori Cohen */
static const __u16 spca501c_mysterious_open_data[][3] = {
{0x02, 0x000f, 0x0005},
@@ -1945,7 +1945,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
goto error;
break;
case MystFromOriUnknownCamera:
- /* UnKnow Ori CMOS Camera data */
+ /* Unknown Ori CMOS Camera data */
if (write_vector(gspca_dev, spca501c_mysterious_open_data))
goto error;
break;
@@ -1978,7 +1978,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
write_vector(gspca_dev, spca501c_arowana_open_data);
break;
case MystFromOriUnknownCamera:
- /* UnKnow CMOS Camera data */
+ /* Unknown CMOS Camera data */
write_vector(gspca_dev, spca501c_mysterious_init_data);
break;
default:
@@ -2032,20 +2032,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
switch (data[0]) {
case 0: /* start of frame */
- frame = gspca_frame_add(gspca_dev,
- LAST_PACKET,
- frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
data += SPCA501_OFFSET_DATA;
len -= SPCA501_OFFSET_DATA;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
return;
case 0xff: /* drop */
/* gspca_dev->last_packet_type = DISCARD_PACKET; */
@@ -2053,8 +2048,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
data++;
len--;
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index ea8c9fe2e96..0f9232ff128 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -739,26 +739,22 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
switch (data[0]) {
case 0: /* start of frame */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
data += SPCA50X_OFFSET_DATA;
len -= SPCA50X_OFFSET_DATA;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
break;
case 0xff: /* drop */
break;
default:
data += 1;
len -= 1;
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
break;
}
}
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index a199298a641..ab28cc23e41 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -543,18 +543,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
switch (data[0]) {
case 0: /* start of frame */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
data += SPCA50X_OFFSET_DATA;
len -= SPCA50X_OFFSET_DATA;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
break;
case 0xff: /* drop */
/* gspca_dev->last_packet_type = DISCARD_PACKET; */
@@ -562,8 +559,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
default:
data += 1;
len -= 1;
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
break;
}
}
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index 9696c4caf5c..4d8e6cf75d5 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -1447,26 +1447,22 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
switch (data[0]) {
case 0: /* start of frame */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
data += SPCA508_OFFSET_DATA;
len -= SPCA508_OFFSET_DATA;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
break;
case 0xff: /* drop */
break;
default:
data += 1;
len -= 1;
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
break;
}
}
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index 27e82b35f3e..58c2f0039af 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -779,8 +779,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -788,12 +787,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
len--;
switch (*data++) { /* sequence number */
case 0: /* start of frame */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
if (data[1] & 0x10) {
/* compressed bayer */
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- frame, data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
} else {
/* raw bayer (with a header, which we skip) */
if (sd->chip_revision == Rev012A) {
@@ -803,14 +800,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data += 16;
len -= 16;
}
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- frame, data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
}
return;
case 0xff: /* drop (empty mpackets) */
return;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
/* rev 72a only */
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
index 715a68f0156..1fcaca6a87f 100644
--- a/drivers/media/video/gspca/sq905.c
+++ b/drivers/media/video/gspca/sq905.c
@@ -168,18 +168,22 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev)
* request and read a block of data - see warning on sq905_command.
*/
static int
-sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size)
+sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
{
int ret;
int act_len;
gspca_dev->usb_buf[0] = '\0';
+ if (need_lock)
+ mutex_lock(&gspca_dev->usb_lock);
ret = usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
USB_REQ_SYNCH_FRAME, /* request */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SQ905_BULK_READ, size, gspca_dev->usb_buf,
1, SQ905_CMD_TIMEOUT);
+ if (need_lock)
+ mutex_unlock(&gspca_dev->usb_lock);
if (ret < 0) {
PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret);
return ret;
@@ -210,11 +214,9 @@ static void sq905_dostream(struct work_struct *work)
{
struct sd *dev = container_of(work, struct sd, work_struct);
struct gspca_dev *gspca_dev = &dev->gspca_dev;
- struct gspca_frame *frame;
int bytes_left; /* bytes remaining in current frame. */
int data_len; /* size to use for the next read. */
int header_read; /* true if we have already read the frame header. */
- int discarding; /* true if we failed to get space for frame. */
int packet_type;
int frame_sz;
int ret;
@@ -222,7 +224,6 @@ static void sq905_dostream(struct work_struct *work)
u8 *buffer;
buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
- mutex_lock(&gspca_dev->usb_lock);
if (!buffer) {
PDEBUG(D_ERR, "Couldn't allocate USB buffer");
goto quit_stream;
@@ -232,28 +233,22 @@ static void sq905_dostream(struct work_struct *work)
+ FRAME_HEADER_LEN;
while (gspca_dev->present && gspca_dev->streaming) {
- /* Need a short delay to ensure streaming flag was set by
- * gspca and to make sure gspca can grab the mutex. */
- mutex_unlock(&gspca_dev->usb_lock);
- msleep(1);
-
/* request some data and then read it until we have
* a complete frame. */
bytes_left = frame_sz;
header_read = 0;
- discarding = 0;
- while (bytes_left > 0) {
+ /* Note we do not check for gspca_dev->streaming here, as
+ we must finish reading an entire frame, otherwise the
+ next time we stream we start reading in the middle of a
+ frame. */
+ while (bytes_left > 0 && gspca_dev->present) {
data_len = bytes_left > SQ905_MAX_TRANSFER ?
SQ905_MAX_TRANSFER : bytes_left;
- mutex_lock(&gspca_dev->usb_lock);
- if (!gspca_dev->present)
- goto quit_stream;
- ret = sq905_read_data(gspca_dev, buffer, data_len);
+ ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
if (ret < 0)
goto quit_stream;
- mutex_unlock(&gspca_dev->usb_lock);
- PDEBUG(D_STREAM,
+ PDEBUG(D_PACK,
"Got %d bytes out of %d for frame",
data_len, bytes_left);
bytes_left -= data_len;
@@ -270,34 +265,30 @@ static void sq905_dostream(struct work_struct *work)
} else {
packet_type = INTER_PACKET;
}
- frame = gspca_get_i_frame(gspca_dev);
- if (frame && !discarding) {
- frame = gspca_frame_add(gspca_dev, packet_type,
- frame, data, data_len);
- /* If entire frame fits in one packet we still
- need to add a LAST_PACKET */
- if (packet_type == FIRST_PACKET &&
- bytes_left == 0)
- frame = gspca_frame_add(gspca_dev,
- LAST_PACKET,
- frame, data, 0);
- } else {
- discarding = 1;
- }
+ gspca_frame_add(gspca_dev, packet_type,
+ data, data_len);
+ /* If entire frame fits in one packet we still
+ need to add a LAST_PACKET */
+ if (packet_type == FIRST_PACKET &&
+ bytes_left == 0)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ }
+ if (gspca_dev->present) {
+ /* acknowledge the frame */
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = sq905_ack_frame(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ goto quit_stream;
}
- /* acknowledge the frame */
- mutex_lock(&gspca_dev->usb_lock);
- if (!gspca_dev->present)
- goto quit_stream;
- ret = sq905_ack_frame(gspca_dev);
- if (ret < 0)
- goto quit_stream;
}
quit_stream:
- /* the usb_lock is already acquired */
- if (gspca_dev->present)
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
sq905_command(gspca_dev, SQ905_CLEAR);
- mutex_unlock(&gspca_dev->usb_lock);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
kfree(buffer);
}
@@ -346,7 +337,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
ret = sq905_command(gspca_dev, SQ905_ID);
if (ret < 0)
return ret;
- ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4);
+ ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
if (ret < 0)
return ret;
/* usb_buf is allocated with kmalloc so is aligned.
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
index 91689250543..d70b156872d 100644
--- a/drivers/media/video/gspca/sq905c.c
+++ b/drivers/media/video/gspca/sq905c.c
@@ -115,11 +115,9 @@ static void sq905c_dostream(struct work_struct *work)
{
struct sd *dev = container_of(work, struct sd, work_struct);
struct gspca_dev *gspca_dev = &dev->gspca_dev;
- struct gspca_frame *frame;
int bytes_left; /* bytes remaining in current frame. */
int data_len; /* size to use for the next read. */
int act_len;
- int discarding = 0; /* true if we failed to get space for frame. */
int packet_type;
int ret;
u8 *buffer;
@@ -131,8 +129,6 @@ static void sq905c_dostream(struct work_struct *work)
}
while (gspca_dev->present && gspca_dev->streaming) {
- if (!gspca_dev->present)
- goto quit_stream;
/* Request the header, which tells the size to download */
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x81),
@@ -149,17 +145,11 @@ static void sq905c_dostream(struct work_struct *work)
PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
/* We keep the header. It has other information, too. */
packet_type = FIRST_PACKET;
- frame = gspca_get_i_frame(gspca_dev);
- if (frame && !discarding) {
- gspca_frame_add(gspca_dev, packet_type,
- frame, buffer, FRAME_HEADER_LEN);
- } else
- discarding = 1;
- while (bytes_left > 0) {
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, FRAME_HEADER_LEN);
+ while (bytes_left > 0 && gspca_dev->present) {
data_len = bytes_left > SQ905C_MAX_TRANSFER ?
SQ905C_MAX_TRANSFER : bytes_left;
- if (!gspca_dev->present)
- goto quit_stream;
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x81),
buffer, data_len, &act_len,
@@ -174,19 +164,16 @@ static void sq905c_dostream(struct work_struct *work)
packet_type = LAST_PACKET;
else
packet_type = INTER_PACKET;
- frame = gspca_get_i_frame(gspca_dev);
- if (frame && !discarding)
- gspca_frame_add(gspca_dev, packet_type,
- frame, buffer, data_len);
- else
- discarding = 1;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, data_len);
}
}
quit_stream:
- mutex_lock(&gspca_dev->usb_lock);
- if (gspca_dev->present)
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
- mutex_unlock(&gspca_dev->usb_lock);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
kfree(buffer);
}
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 47628964801..8e23320d7ab 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -418,8 +418,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -435,11 +434,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
* (without ending - ff d9)
*/
if (data[0] == 0xff && data[1] == 0xfe) {
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- ffd9, 2);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
/* put the JPEG 411 header */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
/* beginning of the frame */
@@ -447,7 +446,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data += STKHDRSZ;
len -= STKHDRSZ;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
new file mode 100644
index 00000000000..2a69d7ccb50
--- /dev/null
+++ b/drivers/media/video/gspca/stv0680.c
@@ -0,0 +1,364 @@
+/*
+ * STV0680 USB Camera Driver
+ *
+ * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 stv680 driver:
+ *
+ * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *
+ * Thanks to STMicroelectronics for information on the usb commands, and
+ * to Steve Miller at STM for his help and encouragement while I was
+ * writing this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define MODULE_NAME "stv0680"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
+MODULE_DESCRIPTION("STV0680 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct v4l2_pix_format mode;
+ u8 orig_mode;
+ u8 video_mode;
+ u8 current_mode;
+};
+
+/* V4L2 controls supported by the driver */
+static struct ctrl sd_ctrls[] = {
+};
+
+static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
+ int size)
+{
+ int ret = -1;
+ u8 req_type = 0;
+
+ switch (set) {
+ case 0: /* 0xc1 */
+ req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ break;
+ case 1: /* 0x41 */
+ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+ break;
+ case 2: /* 0x80 */
+ req_type = USB_DIR_IN | USB_RECIP_DEVICE;
+ break;
+ case 3: /* 0x40 */
+ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ break;
+ }
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req, req_type,
+ val, 0, gspca_dev->usb_buf, size, 500);
+
+ if ((ret < 0) && (req != 0x0a))
+ PDEBUG(D_ERR,
+ "usb_control_msg error %i, request = 0x%x, error = %i",
+ set, req, ret);
+
+ return ret;
+}
+
+static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret)
+{
+ stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */
+ PDEBUG(D_ERR, "last error: %i, command = 0x%x",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ return ret;
+}
+
+static int stv0680_get_video_mode(struct gspca_dev *gspca_dev)
+{
+ /* Note not sure if this init of usb_buf is really necessary */
+ memset(gspca_dev->usb_buf, 0, 8);
+ gspca_dev->usb_buf[0] = 0x0f;
+
+ if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) {
+ PDEBUG(D_ERR, "Get_Camera_Mode failed");
+ return stv0680_handle_error(gspca_dev, -EIO);
+ }
+
+ return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */
+}
+
+static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->current_mode == mode)
+ return 0;
+
+ memset(gspca_dev->usb_buf, 0, 8);
+ gspca_dev->usb_buf[0] = mode;
+
+ if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) {
+ PDEBUG(D_ERR, "Set_Camera_Mode failed");
+ return stv0680_handle_error(gspca_dev, -EIO);
+ }
+
+ /* Verify we got what we've asked for */
+ if (stv0680_get_video_mode(gspca_dev) != mode) {
+ PDEBUG(D_ERR, "Error setting camera video mode!");
+ return -EIO;
+ }
+
+ sd->current_mode = mode;
+
+ return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ int ret;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ /* ping camera to be sure STV0680 is present */
+ if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 ||
+ gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) {
+ PDEBUG(D_ERR, "STV(e): camera ping failed!!");
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ }
+
+ /* get camera descriptor */
+ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+
+ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 ||
+ gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) {
+ PDEBUG(D_ERR, "Could not get descriptor 0200.");
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ }
+ if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+ if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+ return stv0680_handle_error(gspca_dev, -ENODEV);
+
+ if (!(gspca_dev->usb_buf[7] & 0x09)) {
+ PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode");
+ return -ENODEV;
+ }
+ if (gspca_dev->usb_buf[7] & 0x01)
+ PDEBUG(D_PROBE, "Camera supports CIF mode");
+ if (gspca_dev->usb_buf[7] & 0x02)
+ PDEBUG(D_PROBE, "Camera supports VGA mode");
+ if (gspca_dev->usb_buf[7] & 0x08)
+ PDEBUG(D_PROBE, "Camera supports QVGA mode");
+
+ if (gspca_dev->usb_buf[7] & 0x01)
+ sd->video_mode = 0x00; /* CIF */
+ else
+ sd->video_mode = 0x03; /* QVGA */
+
+ /* FW rev, ASIC rev, sensor ID */
+ PDEBUG(D_PROBE, "Firmware rev is %i.%i",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+ PDEBUG(D_PROBE, "ASIC rev is %i.%i",
+ gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]);
+ PDEBUG(D_PROBE, "Sensor ID is %i",
+ (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4));
+
+
+ ret = stv0680_get_video_mode(gspca_dev);
+ if (ret < 0)
+ return ret;
+ sd->current_mode = sd->orig_mode = ret;
+
+ ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+ if (ret < 0)
+ return ret;
+
+ /* Get mode details */
+ if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10)
+ return stv0680_handle_error(gspca_dev, -EIO);
+
+ cam->bulk = 1;
+ cam->bulk_nurbs = 1; /* The cam cannot handle more */
+ cam->bulk_size = (gspca_dev->usb_buf[0] << 24) |
+ (gspca_dev->usb_buf[1] << 16) |
+ (gspca_dev->usb_buf[2] << 8) |
+ (gspca_dev->usb_buf[3]);
+ sd->mode.width = (gspca_dev->usb_buf[4] << 8) |
+ (gspca_dev->usb_buf[5]); /* 322, 356, 644 */
+ sd->mode.height = (gspca_dev->usb_buf[6] << 8) |
+ (gspca_dev->usb_buf[7]); /* 242, 292, 484 */
+ sd->mode.pixelformat = V4L2_PIX_FMT_STV0680;
+ sd->mode.field = V4L2_FIELD_NONE;
+ sd->mode.bytesperline = sd->mode.width;
+ sd->mode.sizeimage = cam->bulk_size;
+ sd->mode.colorspace = V4L2_COLORSPACE_SRGB;
+
+ /* origGain = gspca_dev->usb_buf[12]; */
+
+ cam->cam_mode = &sd->mode;
+ cam->nmodes = 1;
+
+
+ ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+ if (ret < 0)
+ return ret;
+
+ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
+ gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
+ PDEBUG(D_ERR, "Could not get descriptor 0100.");
+ return stv0680_handle_error(gspca_dev, -EIO);
+ }
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ int ret;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+ if (ret < 0)
+ return ret;
+
+ if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+ return stv0680_handle_error(gspca_dev, -EIO);
+
+ /* Start stream at:
+ 0x0000 = CIF (352x288)
+ 0x0100 = VGA (640x480)
+ 0x0300 = QVGA (320x240) */
+ if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0)
+ return stv0680_handle_error(gspca_dev, -EIO);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ /* This is a high priority command; it stops all lower order cmds */
+ if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0)
+ stv0680_handle_error(gspca_dev, -EIO);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->gspca_dev.present)
+ return;
+
+ stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Every now and then the camera sends a 16 byte packet, no idea
+ what it contains, but it is not image data, when this
+ happens the frame received before this packet is corrupt,
+ so discard it. */
+ if (len != sd->mode.sizeimage) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+
+ /* Finish the previous frame, we do this upon reception of the next
+ packet, even though it is already complete so that the strange 16
+ byte packets send after a corrupt frame can discard it. */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ /* Store the just received frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0553, 0x0202)},
+ {USB_DEVICE(0x041e, 0x4007)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ int ret;
+ ret = usb_register(&sd_driver);
+ if (ret < 0)
+ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
index bfae63f5584..5d0241bb161 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -312,8 +312,7 @@ out:
* The 0005 and 0100 chunks seem to appear only in compressed stream.
*/
static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -366,7 +365,7 @@ frame_data:
sd->to_skip -= skip;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ gspca_frame_add(gspca_dev, INTER_PACKET,
data, chunk_len);
break;
@@ -378,7 +377,7 @@ frame_data:
/* Create a new frame, chunk length should be zero */
gspca_frame_add(gspca_dev, FIRST_PACKET,
- frame, data, 0);
+ NULL, 0);
if (sd->bridge == BRIDGE_ST6422)
sd->to_skip = gspca_dev->width * 4;
@@ -394,8 +393,8 @@ frame_data:
PDEBUG(D_PACK, "End of frame detected");
/* Complete the last frame (if any) */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET,
- frame, data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
if (chunk_len)
PDEBUG(D_ERR, "Chunk length is "
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index aa8f995ce04..72bf3b4f0a3 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -631,7 +631,7 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
count = 200;
while (--count > 0) {
msleep(10);
- /* gsmart mini2 write a each wait setting 1 ms is enought */
+ /* gsmart mini2 write a each wait setting 1 ms is enough */
/* reg_w_riv(dev, req, idx, val); */
status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
if (status == endcode) {
@@ -1116,7 +1116,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
@@ -1186,11 +1185,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
break;
}
if (sof) { /* start of frame */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- ffd9, 2);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
/* put the JPEG header in the new frame */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
}
@@ -1198,7 +1197,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
i = 0;
do {
if (data[i] == 0xff) {
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ gspca_frame_add(gspca_dev, INTER_PACKET,
data, i + 1);
len -= i;
data += i;
@@ -1207,7 +1206,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
}
i++;
} while (i < len);
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index 1d321c30d22..55ef6a74442 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -938,7 +938,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
@@ -956,9 +955,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* extra bytes....., could be processed too but would be
* a waste of time, right now leave the application and
* libjpeg do it for ourserlves.. */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ gspca_frame_add(gspca_dev, LAST_PACKET,
ffd9, 2);
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
return;
}
@@ -967,7 +966,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
* other's do not include it... */
len -= 2;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
index 4b44dde9f8b..b74a3b6489c 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/video/gspca/tv8532.c
@@ -398,8 +398,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
+ u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -424,9 +423,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
* - 4 bytes
*/
gspca_frame_add(gspca_dev, packet_type0,
- frame, data + 2, gspca_dev->width);
+ data + 2, gspca_dev->width);
gspca_frame_add(gspca_dev, packet_type1,
- frame, data + gspca_dev->width + 5, gspca_dev->width);
+ data + gspca_dev->width + 5, gspca_dev->width);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index 589042f6adb..c090efcd804 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -2987,7 +2987,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
u8 *data, /* isoc packet */
int len) /* iso pkt length */
{
@@ -2996,21 +2995,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
if (data[0] == 0xff && data[1] == 0xd8) {
PDEBUG(D_PACK,
"vc032x header packet found len %d", len);
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
data += sd->image_offset;
len -= sd->image_offset;
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
- data, len);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
return;
}
/* The vc0321 sends some additional data after sending the complete
* frame, we ignore this. */
- if (sd->bridge == BRIDGE_VC0321
- && len > frame->v4l2_buf.length - (frame->data_end - frame->data))
- len = frame->v4l2_buf.length - (frame->data_end - frame->data);
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ if (sd->bridge == BRIDGE_VC0321) {
+ struct gspca_frame *frame;
+ int l;
+
+ frame = gspca_get_i_frame(gspca_dev);
+ l = frame->data_end - frame->data;
+ if (len > frame->v4l2_buf.length - l)
+ len = frame->v4l2_buf.length - l;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -3092,6 +3095,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
switch (menu->id) {
case V4L2_CID_POWER_LINE_FREQUENCY:
+ if (menu->index >= ARRAY_SIZE(freq_nm))
+ break;
strcpy((char *) menu->name, freq_nm[menu->index]);
return 0;
}
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
new file mode 100644
index 00000000000..2fffe203bed
--- /dev/null
+++ b/drivers/media/video/gspca/w996Xcf.c
@@ -0,0 +1,609 @@
+/**
+ *
+ * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip.
+ *
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 w9968cf driver:
+ *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
+ *
+ * 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
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Note this is not a stand alone driver, it gets included in ov519.c, this
+ is a bit of a hack, but it needs the driver code for a lot of different
+ ov sensors which is already present in ov519.c (the old v4l1 driver used
+ the ovchipcam framework). When we have the time we really should move
+ the sensor drivers to v4l2 sub drivers, and properly split of this
+ driver from ov519.c */
+
+/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
+#define CONEX_CAM
+#include "jpeg.h"
+
+#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
+
+#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET)
+#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET)
+
+static const struct v4l2_pix_format w9968cf_vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+ .bytesperline = 160 * 2,
+ .sizeimage = 160 * 120 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+ .bytesperline = 176 * 2,
+ .sizeimage = 176 * 144 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320 * 2,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352 * 2,
+ .sizeimage = 352 * 288 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 2,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static int reg_w(struct sd *sd, __u16 index, __u16 value);
+
+/*--------------------------------------------------------------------------
+ Write 64-bit data to the fast serial bus registers.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_fsb(struct sd *sd, u16* data)
+{
+ struct usb_device* udev = sd->gspca_dev.dev;
+ u16 value;
+ int ret;
+
+ value = *data++;
+ memcpy(sd->gspca_dev.usb_buf, data, 6);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*--------------------------------------------------------------------------
+ Write data to the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_sb(struct sd *sd, u16 value)
+{
+ int ret;
+
+ /* We don't use reg_w here, as that would cause all writes when
+ bitbanging i2c to be logged, making the logs impossible to read */
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0x01, NULL, 0, 500);
+
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*--------------------------------------------------------------------------
+ Read data from the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct sd *sd)
+{
+ int ret;
+
+ /* We don't use reg_r here, as the w9968cf is special and has 16
+ bit registers instead of 8 bit */
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ 1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0x01, sd->gspca_dev.usb_buf, 2, 500);
+ if (ret >= 0)
+ ret = sd->gspca_dev.usb_buf[0] |
+ (sd->gspca_dev.usb_buf[1] << 8);
+ else
+ PDEBUG(D_ERR, "Read SB reg [01] failed");
+
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return ret;
+}
+
+/*--------------------------------------------------------------------------
+ Upload quantization tables for the JPEG compression.
+ This function is called by w9968cf_start_transfer().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_upload_quantizationtables(struct sd *sd)
+{
+ u16 a, b;
+ int ret = 0, i, j;
+
+ ret += reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */
+
+ for (i = 0, j = 0; i < 32; i++, j += 2) {
+ a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
+ b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
+ ret += reg_w(sd, 0x40+i, a);
+ ret += reg_w(sd, 0x60+i, b);
+ }
+ ret += reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */
+
+ return ret;
+}
+
+/****************************************************************************
+ * Low-level I2C I/O functions. *
+ * The adapter supports the following I2C transfer functions: *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) *
+ * i2c_adap_read_byte_data() *
+ * i2c_adap_read_byte() *
+ ****************************************************************************/
+
+static int w9968cf_smbus_start(struct sd *sd)
+{
+ int ret = 0;
+
+ ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+ return ret;
+}
+
+static int w9968cf_smbus_stop(struct sd *sd)
+{
+ int ret = 0;
+
+ ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+ ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+
+ return ret;
+}
+
+static int w9968cf_smbus_write_byte(struct sd *sd, u8 v)
+{
+ u8 bit;
+ int ret = 0, sda;
+
+ for (bit = 0 ; bit < 8 ; bit++) {
+ sda = (v & 0x80) ? 2 : 0;
+ v <<= 1;
+ /* SDE=1, SDA=sda, SCL=0 */
+ ret += w9968cf_write_sb(sd, 0x10 | sda);
+ /* SDE=1, SDA=sda, SCL=1 */
+ ret += w9968cf_write_sb(sd, 0x11 | sda);
+ /* SDE=1, SDA=sda, SCL=0 */
+ ret += w9968cf_write_sb(sd, 0x10 | sda);
+ }
+
+ return ret;
+}
+
+static int w9968cf_smbus_read_byte(struct sd *sd, u8* v)
+{
+ u8 bit;
+ int ret = 0;
+
+ /* No need to ensure SDA is high as we are always called after
+ read_ack which ends with SDA high */
+ *v = 0;
+ for (bit = 0 ; bit < 8 ; bit++) {
+ *v <<= 1;
+ /* SDE=1, SDA=1, SCL=1 */
+ ret += w9968cf_write_sb(sd, 0x0013);
+ *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0;
+ /* SDE=1, SDA=1, SCL=0 */
+ ret += w9968cf_write_sb(sd, 0x0012);
+ }
+
+ return ret;
+}
+
+static int w9968cf_smbus_write_nack(struct sd *sd)
+{
+ int ret = 0;
+
+ /* No need to ensure SDA is high as we are always called after
+ read_byte which ends with SDA high */
+ ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+ ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+
+ return ret;
+}
+
+static int w9968cf_smbus_read_ack(struct sd *sd)
+{
+ int ret = 0, sda;
+
+ /* Ensure SDA is high before raising clock to avoid a spurious stop */
+ ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+ ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+ sda = w9968cf_read_sb(sd);
+ ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+ if (sda < 0)
+ ret += sda;
+ else if (sda & 0x08) {
+ PDEBUG(D_USBI, "Did not receive i2c ACK");
+ ret += -1;
+ }
+
+ return ret;
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static int w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ u16* data = (u16 *)sd->gspca_dev.usb_buf;
+ int ret = 0;
+
+ data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0);
+ data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0;
+ data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0);
+ data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0;
+ data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0;
+ data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0);
+ data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0;
+ data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0;
+ data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0);
+ data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0;
+
+ ret += w9968cf_write_fsb(sd, data);
+
+ data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (reg & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (reg & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (reg & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (reg & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (reg & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (reg & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (reg & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0x001d;
+
+ ret += w9968cf_write_fsb(sd, data);
+
+ data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0xfe1d;
+
+ ret += w9968cf_write_fsb(sd, data);
+
+ if (!ret)
+ PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
+ else
+ PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
+
+ return ret;
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int w9968cf_i2c_r(struct sd *sd, u8 reg)
+{
+ int ret = 0;
+ u8 value;
+
+ /* Fast serial bus data control disable */
+ ret += w9968cf_write_sb(sd, 0x0013); /* don't change ! */
+
+ ret += w9968cf_smbus_start(sd);
+ ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr);
+ ret += w9968cf_smbus_read_ack(sd);
+ ret += w9968cf_smbus_write_byte(sd, reg);
+ ret += w9968cf_smbus_read_ack(sd);
+ ret += w9968cf_smbus_stop(sd);
+ ret += w9968cf_smbus_start(sd);
+ ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1);
+ ret += w9968cf_smbus_read_ack(sd);
+ ret += w9968cf_smbus_read_byte(sd, &value);
+ /* signal we don't want to read anymore, the v4l1 driver used to
+ send an ack here which is very wrong! (and then fixed
+ the issues this gave by retrying reads) */
+ ret += w9968cf_smbus_write_nack(sd);
+ ret += w9968cf_smbus_stop(sd);
+
+ /* Fast serial bus data control re-enable */
+ ret += w9968cf_write_sb(sd, 0x0030);
+
+ if (!ret) {
+ ret = value;
+ PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value);
+ } else
+ PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
+
+ return ret;
+}
+
+
+/*--------------------------------------------------------------------------
+ Turn on the LED on some webcams. A beep should be heard too.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_configure(struct sd *sd)
+{
+ int ret = 0;
+
+ ret += reg_w(sd, 0x00, 0xff00); /* power-down */
+ ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
+ ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
+ ret += reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */
+ ret += reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */
+ ret += reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */
+ ret += reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */
+
+ if (ret)
+ PDEBUG(D_ERR, "Couldn't turn on the LED");
+
+ sd->stopped = 1;
+
+ return ret;
+}
+
+static int w9968cf_init(struct sd *sd)
+{
+ int ret = 0;
+ unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2),
+ y0 = 0x0000,
+ u0 = y0 + hw_bufsize/2,
+ v0 = u0 + hw_bufsize/4,
+ y1 = v0 + hw_bufsize/4,
+ u1 = y1 + hw_bufsize/2,
+ v1 = u1 + hw_bufsize/4;
+
+ ret += reg_w(sd, 0x00, 0xff00); /* power off */
+ ret += reg_w(sd, 0x00, 0xbf10); /* power on */
+
+ ret += reg_w(sd, 0x03, 0x405d); /* DRAM timings */
+ ret += reg_w(sd, 0x04, 0x0030); /* SDRAM timings */
+
+ ret += reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */
+ ret += reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */
+ ret += reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */
+ ret += reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */
+ ret += reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */
+ ret += reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */
+
+ ret += reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */
+ ret += reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */
+ ret += reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */
+ ret += reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */
+ ret += reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */
+ ret += reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */
+
+ ret += reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */
+ ret += reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */
+
+ ret += reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */
+ ret += reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */
+
+ ret += reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */
+ ret += reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/
+ ret += reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */
+ ret += reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */
+
+ return ret;
+}
+
+static int w9968cf_set_crop_window(struct sd *sd)
+{
+ int ret = 0, start_cropx, start_cropy, x, y, fw, fh, cw, ch,
+ max_width, max_height;
+
+ if (sd->sif) {
+ max_width = 352;
+ max_height = 288;
+ } else {
+ max_width = 640;
+ max_height = 480;
+ }
+
+ if (sd->sensor == SEN_OV7620) {
+ /* Sigh, this is dependend on the clock / framerate changes
+ made by the frequency control, sick. */
+ if (sd->freq == 1) {
+ start_cropx = 277;
+ start_cropy = 37;
+ } else {
+ start_cropx = 105;
+ start_cropy = 37;
+ }
+ } else {
+ start_cropx = 320;
+ start_cropy = 35;
+ }
+
+ /* Work around to avoid FP arithmetics */
+ #define SC(x) ((x) << 10)
+
+ /* Scaling factors */
+ fw = SC(sd->gspca_dev.width) / max_width;
+ fh = SC(sd->gspca_dev.height) / max_height;
+
+ cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width)/fh;
+ ch = (fw >= fh) ? SC(sd->gspca_dev.height)/fw : max_height;
+
+ sd->sensor_width = max_width;
+ sd->sensor_height = max_height;
+
+ x = (max_width - cw) / 2;
+ y = (max_height - ch) / 2;
+
+ ret += reg_w(sd, 0x10, start_cropx + x);
+ ret += reg_w(sd, 0x11, start_cropy + y);
+ ret += reg_w(sd, 0x12, start_cropx + x + cw);
+ ret += reg_w(sd, 0x13, start_cropy + y + ch);
+
+ return ret;
+}
+
+static int w9968cf_mode_init_regs(struct sd *sd)
+{
+ int ret = 0, val, vs_polarity, hs_polarity;
+
+ ret += w9968cf_set_crop_window(sd);
+
+ ret += reg_w(sd, 0x14, sd->gspca_dev.width);
+ ret += reg_w(sd, 0x15, sd->gspca_dev.height);
+
+ /* JPEG width & height */
+ ret += reg_w(sd, 0x30, sd->gspca_dev.width);
+ ret += reg_w(sd, 0x31, sd->gspca_dev.height);
+
+ /* Y & UV frame buffer strides (in WORD) */
+ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ ret += reg_w(sd, 0x2c, sd->gspca_dev.width/2);
+ ret += reg_w(sd, 0x2d, sd->gspca_dev.width/4);
+ } else
+ ret += reg_w(sd, 0x2c, sd->gspca_dev.width);
+
+ ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
+ ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
+
+ /* Transfer size in WORDS (for UYVY format only) */
+ val = sd->gspca_dev.width * sd->gspca_dev.height;
+ ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */
+ ret += reg_w(sd, 0x3e, val >> 16); /* high bits */
+
+ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ /* We may get called multiple times (usb isoc bw negotiat.) */
+ if (!sd->jpeg_hdr)
+ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ if (!sd->jpeg_hdr)
+ return -ENOMEM;
+
+ jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
+ sd->gspca_dev.width, 0x22); /* JPEG 420 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ ret += w9968cf_upload_quantizationtables(sd);
+ }
+
+ /* Video Capture Control Register */
+ if (sd->sensor == SEN_OV7620) {
+ /* Seems to work around a bug in the image sensor */
+ vs_polarity = 1;
+ hs_polarity = 1;
+ } else {
+ vs_polarity = 1;
+ hs_polarity = 0;
+ }
+
+ val = (vs_polarity << 12) | (hs_polarity << 11);
+
+ /* NOTE: We may not have enough memory to do double buffering while
+ doing compression (amount of memory differs per model cam).
+ So we use the second image buffer also as jpeg stream buffer
+ (see w9968cf_init), and disable double buffering. */
+ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ /* val |= 0x0002; YUV422P */
+ val |= 0x0003; /* YUV420P */
+ } else
+ val |= 0x0080; /* Enable HW double buffering */
+
+ /* val |= 0x0020; enable clamping */
+ /* val |= 0x0008; enable (1-2-1) filter */
+ /* val |= 0x000c; enable (2-3-6-3-2) filter */
+
+ val |= 0x8000; /* capt. enable */
+
+ ret += reg_w(sd, 0x16, val);
+
+ sd->gspca_dev.empty_packet = 0;
+
+ return ret;
+}
+
+static void w9968cf_stop0(struct sd *sd)
+{
+ if (sd->gspca_dev.present) {
+ reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
+ reg_w(sd, 0x16, 0x0000); /* stop video capture */
+ }
+
+ kfree(sd->jpeg_hdr);
+ sd->jpeg_hdr = NULL;
+}
+
+/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF
+ for the next frame). This seems to simply not be true when operating
+ in JPEG mode, in this case there may be empty packets within the
+ frame. So in JPEG mode use the JPEG SOI marker to detect SOF.
+
+ Note to make things even more interesting the w9968cf sends *PLANAR* jpeg,
+ to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS,
+ V-data, EOI. */
+static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat ==
+ V4L2_PIX_FMT_JPEG) {
+ if (len >= 2 &&
+ data[0] == 0xff &&
+ data[1] == 0xd8) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ /* Strip the ff d8, our own header (which adds
+ huffman and quantization tables) already has this */
+ len -= 2;
+ data += 2;
+ }
+ } else {
+ /* In UYVY mode an empty packet signals EOF */
+ if (gspca_dev->empty_packet) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ NULL, 0);
+ gspca_dev->empty_packet = 0;
+ }
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index cdf3357b4c9..69e5dc4fc9d 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -61,17 +61,18 @@ struct sd {
#define SENSOR_HV7131C 6
#define SENSOR_ICM105A 7
#define SENSOR_MC501CB 8
-#define SENSOR_OV7620 9
-/*#define SENSOR_OV7648 9 - same values */
-#define SENSOR_OV7630C 10
-#define SENSOR_PAS106 11
-#define SENSOR_PAS202B 12
-#define SENSOR_PB0330 13
-#define SENSOR_PO2030 14
-#define SENSOR_TAS5130CK 15
-#define SENSOR_TAS5130CXX 16
-#define SENSOR_TAS5130C_VF0250 17
-#define SENSOR_MAX 18
+#define SENSOR_MI0360SOC 9
+#define SENSOR_OV7620 10
+/*#define SENSOR_OV7648 10 - same values */
+#define SENSOR_OV7630C 11
+#define SENSOR_PAS106 12
+#define SENSOR_PAS202B 13
+#define SENSOR_PB0330 14 /* (MI0360) */
+#define SENSOR_PO2030 15
+#define SENSOR_TAS5130CK 16
+#define SENSOR_TAS5130CXX 17
+#define SENSOR_TAS5130C_VF0250 18
+#define SENSOR_MAX 19
unsigned short chip_revision;
u8 *jpeg_hdr;
@@ -420,9 +421,7 @@ static const struct usb_action adcm2700_NoFliker[] = {
{0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
{}
};
-static const struct usb_action cs2102_Initial[] = {
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0008},
+static const struct usb_action cs2102_Initial[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -471,88 +470,10 @@ static const struct usb_action cs2102_Initial[] = {
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x68, ZC3XX_R18D_YTARGET},
{0xa0, 0x00, 0x01ad},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */
- {0xa0, 0x44, ZC3XX_R121_GAMMA01},
- {0xa0, 0x64, ZC3XX_R122_GAMMA02},
- {0xa0, 0x84, ZC3XX_R123_GAMMA03},
- {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
- {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
- {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
- {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
- {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
- {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
- {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x18, ZC3XX_R130_GAMMA10},
- {0xa0, 0x20, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
- {0xa0, 0x58, ZC3XX_R10E_RGB11},
- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
- {0xa0, 0xf4, ZC3XX_R110_RGB20},
- {0xa0, 0xf4, ZC3XX_R111_RGB21},
- {0xa0, 0x58, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x23, 0x0001},
- {0xaa, 0x24, 0x0055},
- {0xaa, 0x25, 0x00cc},
- {0xaa, 0x21, 0x003f},
- {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action cs2102_InitialScale[] = {
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0008},
+static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -601,57 +522,75 @@ static const struct usb_action cs2102_InitialScale[] = {
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x68, ZC3XX_R18D_YTARGET},
{0xa0, 0x00, 0x01ad},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */
- {0xa0, 0x44, ZC3XX_R121_GAMMA01},
- {0xa0, 0x64, ZC3XX_R122_GAMMA02},
- {0xa0, 0x84, ZC3XX_R123_GAMMA03},
- {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
- {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
- {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
- {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
- {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
- {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
- {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x18, ZC3XX_R130_GAMMA10},
- {0xa0, 0x20, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
- {0xa0, 0x58, ZC3XX_R10E_RGB11},
- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
- {0xa0, 0xf4, ZC3XX_R110_RGB20},
- {0xa0, 0xf4, ZC3XX_R111_RGB21},
- {0xa0, 0x58, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action cs2102_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x005f},
+ {0xaa, 0x25, 0x0090},
+ {0xaa, 0x21, 0x00dd},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00af},
+ {0xaa, 0x25, 0x00c8},
+ {0xaa, 0x21, 0x0068},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x68, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x0055},
+ {0xaa, 0x25, 0x00cc},
+ {0xaa, 0x21, 0x003f},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_60HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xaa, 0x23, 0x0000},
{0xaa, 0x24, 0x00aa},
@@ -671,162 +610,50 @@ static const struct usb_action cs2102_InitialScale[] = {
{0xa0, 0xa5, ZC3XX_R01E_HSYNC_1},
{0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action cs2102_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */
- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
- {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */
- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
- {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */
- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
- {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */
- {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
- {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
- {}
-};
-static const struct usb_action cs2102_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */
- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
- {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */
- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
- {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */
- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
- {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
- {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */
- {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
- {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
- {}
-};
-static const struct usb_action cs2102_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */
- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
- {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */
- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
- {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */
- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
- {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
- {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
- {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */
- {}
-};
-static const struct usb_action cs2102_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */
- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
- {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */
- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
- {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */
- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
- {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
- {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
- {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
{}
};
static const struct usb_action cs2102_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
- {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
- {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
- {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x005f},
+ {0xaa, 0x25, 0x0000},
+ {0xaa, 0x21, 0x0001},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
static const struct usb_action cs2102_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
- {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
- {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
- {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00af},
+ {0xaa, 0x25, 0x0080},
+ {0xaa, 0x21, 0x0001},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{}
};
@@ -4409,170 +4236,80 @@ static const struct usb_action pas202b_NoFlikerScale[] = {
{}
};
-static const struct usb_action pb03303x_Initial[] = {
+/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */
+static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
+/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
{0xaa, 0x01, 0x0001},
{0xaa, 0x06, 0x0000},
{0xaa, 0x08, 0x0483},
{0xaa, 0x01, 0x0004},
{0xaa, 0x08, 0x0006},
{0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e7},
- {0xaa, 0x04, 0x0287},
+ {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/
+ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
{0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x1100},
- {0xaa, 0x35, 0x0050},
+ {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
+ {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
{0xaa, 0x58, 0x0078},
{0xaa, 0x62, 0x0411},
{0xaa, 0x2b, 0x0028},
- {0xaa, 0x2c, 0x0030},
- {0xaa, 0x2d, 0x0030},
- {0xaa, 0x2e, 0x0028},
+ {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/
+ {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/
+ {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */
{0xa0, 0x61, ZC3XX_R116_RGAIN},
{0xa0, 0x65, ZC3XX_R118_BGAIN},
-
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x0d, 0x003a},
- {0xa0, 0x02, 0x003b},
- {0xa0, 0x00, 0x0038},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0009},
- {0xaa, 0x09, 0x0134},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-
-static const struct usb_action pb03303x_InitialScale[] = {
+static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
+/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
{0xaa, 0x01, 0x0001},
{0xaa, 0x06, 0x0000},
{0xaa, 0x08, 0x0483},
@@ -4582,111 +4319,111 @@ static const struct usb_action pb03303x_InitialScale[] = {
{0xaa, 0x03, 0x01e7},
{0xaa, 0x04, 0x0287},
{0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x1100},
- {0xaa, 0x35, 0x0050},
+ {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
+ {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
{0xaa, 0x58, 0x0078},
{0xaa, 0x62, 0x0411},
- {0xaa, 0x2b, 0x0028},
- {0xaa, 0x2c, 0x0030},
- {0xaa, 0x2d, 0x0030},
- {0xaa, 0x2e, 0x0028},
+ {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/
+ {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/
+ {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/
+ {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/
{0xa0, 0x61, ZC3XX_R116_RGAIN},
{0xa0, 0x65, ZC3XX_R118_BGAIN},
-
- {0xa1, 0x01, 0x0002},
-
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
-
- {0xa0, 0x0d, 0x003a},
- {0xa0, 0x02, 0x003b},
- {0xa0, 0x00, 0x0038},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
+ {}
+};
+static const struct usb_action mi360soc_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0562},
+ {0xbb, 0x01, 0x09aa},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mi360soc_AE50HZScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0934},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mi360soc_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x053d},
+ {0xbb, 0x01, 0x096e},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mi360soc_AE60HZScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0009},
- {0xaa, 0x09, 0x0134},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0983},
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
{0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
@@ -4696,20 +4433,60 @@ static const struct usb_action pb03303x_InitialScale[] = {
{0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
+ {}
+};
+static const struct usb_action mi360soc_AENoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0960},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action pb0330xx_Initial[] = {
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0008},
+static const struct usb_action mi360soc_AENoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0534},
+ {0xbb, 0x02, 0x0960},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action pb0330_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -4721,11 +4498,12 @@ static const struct usb_action pb0330xx_Initial[] = {
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x0006},
{0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e7},
- {0xaa, 0x04, 0x0287},
+ {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/
+ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
{0xaa, 0x06, 0x0003},
{0xaa, 0x07, 0x3002},
{0xaa, 0x20, 0x1100},
@@ -4743,88 +4521,21 @@ static const struct usb_action pb0330xx_Initial[] = {
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */
+ {0xa0, 0x15, 0x01ae},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0066},
- {0xaa, 0x09, 0x02b2},
- {0xaa, 0x10, 0x0002},
-
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0007},
-/* {0xa0, 0x30, 0x0007}, */
-/* {0xa0, 0x00, 0x0007}, */
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
{}
};
-
-static const struct usb_action pb0330xx_InitialScale[] = {
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0008},
+static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 10 */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -4836,6 +4547,7 @@ static const struct usb_action pb0330xx_InitialScale[] = {
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x0006},
{0xaa, 0x02, 0x0011},
@@ -4858,53 +4570,43 @@ static const struct usb_action pb0330xx_InitialScale[] = {
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x09, 0x01ad},
+ {0xa0, 0x15, 0x01ae},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+};
+static const struct usb_action pb0330_50HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0066},
- {0xaa, 0x09, 0x02b2},
- {0xaa, 0x10, 0x0002},
+ {0xbb, 0x00, 0x055c},
+ {0xbb, 0x01, 0x09aa},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0566},
+ {0xbb, 0x02, 0x09b2},
+ {0xbb, 0x00, 0x1002},
{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
@@ -4912,124 +4614,102 @@ static const struct usb_action pb0330xx_InitialScale[] = {
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
{0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
{0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
{0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0007},
-/* {0xa0, 0x30, 0x0007}, */
-/* {0xa0, 0x00, 0x0007}, */
- {}
-};
-static const struct usb_action pb0330_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
- {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
- {}
-};
-static const struct usb_action pb0330_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
- {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */
- {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */
- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
{}
};
static const struct usb_action pb0330_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
- {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */
- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x01, 0x0974},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
{}
};
static const struct usb_action pb0330_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
- {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x02, 0x096c},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
{}
};
static const struct usb_action pb0330_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x02, 0x0940},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
{}
};
static const struct usb_action pb0330_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x01, 0x0980},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
{}
};
@@ -5655,7 +5335,7 @@ static const struct usb_action tas5130CK_InitialScale[] = {
{}
};
-static const struct usb_action tas5130cxx_Initial[] = {
+static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -5684,74 +5364,19 @@ static const struct usb_action tas5130cxx_Initial[] = {
{0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x68, ZC3XX_R18D_YTARGET},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x95, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xec, ZC3XX_R10B_RGB01},
- {0xa0, 0xec, ZC3XX_R10C_RGB02},
- {0xa0, 0xec, ZC3XX_R10D_RGB10},
- {0xa0, 0x68, ZC3XX_R10E_RGB11},
- {0xa0, 0xec, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0xec, ZC3XX_R111_RGB21},
- {0xa0, 0x68, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x018d},
- {0xa0, 0x90, ZC3XX_R18D_YTARGET}, /* 90 */
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-
- {0xaa, 0xa3, 0x0001},
- {0xaa, 0xa4, 0x0077},
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
- {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW},
-
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 00 */
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 03 */
- {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* e8 */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 0 */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 0 */
- {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 7d */
-
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 08 */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 24 */
- {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
- {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 50 */
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action tas5130cxx_InitialScale[] = {
-/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */
+static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
-
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa1, 0x01, 0x0008},
-
{0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
@@ -5775,63 +5400,13 @@ static const struct usb_action tas5130cxx_InitialScale[] = {
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x68, ZC3XX_R18D_YTARGET},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x95, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
-
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa1, 0x01, 0x0008}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xec, ZC3XX_R10B_RGB01},
- {0xa0, 0xec, ZC3XX_R10C_RGB02},
- {0xa0, 0xec, ZC3XX_R10D_RGB10},
- {0xa0, 0x68, ZC3XX_R10E_RGB11},
- {0xa0, 0xec, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0xec, ZC3XX_R111_RGB21},
- {0xa0, 0x68, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x018d},
- {0xa0, 0x90, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0xa3, 0x0001},
- {0xaa, 0xa4, 0x0063},
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
- {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xda, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
- {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
static const struct usb_action tas5130cxx_50HZ[] = {
@@ -5841,20 +5416,22 @@ static const struct usb_action tas5130cxx_50HZ[] = {
{0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
{0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
- {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */
{0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */
{0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
static const struct usb_action tas5130cxx_50HZScale[] = {
@@ -5864,20 +5441,22 @@ static const struct usb_action tas5130cxx_50HZScale[] = {
{0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
{0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
- {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
- {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */
{0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */
{0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
static const struct usb_action tas5130cxx_60HZ[] = {
@@ -5887,20 +5466,22 @@ static const struct usb_action tas5130cxx_60HZ[] = {
{0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
{0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */
{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
{0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
static const struct usb_action tas5130cxx_60HZScale[] = {
@@ -5910,20 +5491,22 @@ static const struct usb_action tas5130cxx_60HZScale[] = {
{0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
{0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
- {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
+ {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
- {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
{0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
{0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
static const struct usb_action tas5130cxx_NoFliker[] = {
@@ -5933,13 +5516,13 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
{0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
{0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
{0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
@@ -5947,6 +5530,8 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
{0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
@@ -5957,13 +5542,13 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = {
{0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
{0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
{0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
{0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
@@ -5971,6 +5556,8 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = {
{0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
{0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
{0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
@@ -6303,8 +5890,10 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev,
reg_w_i(gspca_dev->dev, reg, 0x0092);
reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */
- msleep(25);
+ msleep(20);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
+ if (retbyte != 0x00)
+ err("i2c_r status error %02x", retbyte);
retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */
retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */
PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)",
@@ -6323,8 +5912,10 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev,
reg_w_i(gspca_dev->dev, valL, 0x93);
reg_w_i(gspca_dev->dev, valH, 0x94);
reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */
- msleep(15);
+ msleep(1);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
+ if (retbyte != 0x00)
+ err("i2c_w status error %02x", retbyte);
PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
reg, valH, valL, retbyte);
return retbyte;
@@ -6359,7 +5950,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
break;
}
action++;
-/* msleep(1); */
+ msleep(1);
}
}
@@ -6380,11 +5971,13 @@ static void setmatrix(struct gspca_dev *gspca_dev)
{0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
static const __u8 po2030_matrix[9] =
{0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
+ static const u8 tas5130c_matrix[9] =
+ {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68};
static const __u8 vf0250_matrix[9] =
{0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
static const __u8 *matrix_tb[SENSOR_MAX] = {
adcm2700_matrix, /* SENSOR_ADCM2700 0 */
- NULL, /* SENSOR_CS2102 1 */
+ ov7620_matrix, /* SENSOR_CS2102 1 */
NULL, /* SENSOR_CS2102K 2 */
gc0305_matrix, /* SENSOR_GC0305 3 */
NULL, /* SENSOR_HDCS2020b 4 */
@@ -6392,15 +5985,16 @@ static void setmatrix(struct gspca_dev *gspca_dev)
NULL, /* SENSOR_HV7131C 6 */
NULL, /* SENSOR_ICM105A 7 */
NULL, /* SENSOR_MC501CB 8 */
- ov7620_matrix, /* SENSOR_OV7620 9 */
- NULL, /* SENSOR_OV7630C 10 */
- NULL, /* SENSOR_PAS106 11 */
- pas202b_matrix, /* SENSOR_PAS202B 12 */
- NULL, /* SENSOR_PB0330 13 */
- po2030_matrix, /* SENSOR_PO2030 14 */
- NULL, /* SENSOR_TAS5130CK 15 */
- NULL, /* SENSOR_TAS5130CXX 16 */
- vf0250_matrix, /* SENSOR_TAS5130C_VF0250 17 */
+ gc0305_matrix, /* SENSOR_MI0360SOC 9 */
+ ov7620_matrix, /* SENSOR_OV7620 10 */
+ NULL, /* SENSOR_OV7630C 11 */
+ NULL, /* SENSOR_PAS106 12 */
+ pas202b_matrix, /* SENSOR_PAS202B 13 */
+ gc0305_matrix, /* SENSOR_PB0330 14 */
+ po2030_matrix, /* SENSOR_PO2030 15 */
+ NULL, /* SENSOR_TAS5130CK 16 */
+ tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */
+ vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */
};
matrix = matrix_tb[sd->sensor];
@@ -6640,39 +6234,43 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
{MC501CB_NoFliker, MC501CB_NoFlikerScale,
MC501CB_50HZ, MC501CB_50HZScale,
MC501CB_60HZ, MC501CB_60HZScale},
-/* SENSOR_OV7620 9 */
+/* SENSOR_MI0360SOC 9 */
+ {mi360soc_AENoFlikerScale, mi360soc_AENoFliker,
+ mi360soc_AE50HZScale, mi360soc_AE50HZ,
+ mi360soc_AE60HZScale, mi360soc_AE60HZ},
+/* SENSOR_OV7620 10 */
{OV7620_NoFliker, OV7620_NoFliker,
OV7620_50HZ, OV7620_50HZ,
OV7620_60HZ, OV7620_60HZ},
-/* SENSOR_OV7630C 10 */
+/* SENSOR_OV7630C 11 */
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_PAS106 11 */
+/* SENSOR_PAS106 12 */
{pas106b_NoFliker, pas106b_NoFliker,
pas106b_50HZ, pas106b_50HZ,
pas106b_60HZ, pas106b_60HZ},
-/* SENSOR_PAS202B 12 */
+/* SENSOR_PAS202B 13 */
{pas202b_NoFlikerScale, pas202b_NoFliker,
pas202b_50HZScale, pas202b_50HZ,
pas202b_60HZScale, pas202b_60HZ},
-/* SENSOR_PB0330 13 */
- {pb0330_NoFliker, pb0330_NoFlikerScale,
- pb0330_50HZ, pb0330_50HZScale,
- pb0330_60HZ, pb0330_60HZScale},
-/* SENSOR_PO2030 14 */
+/* SENSOR_PB0330 14 */
+ {pb0330_NoFlikerScale, pb0330_NoFliker,
+ pb0330_50HZScale, pb0330_50HZ,
+ pb0330_60HZScale, pb0330_60HZ},
+/* SENSOR_PO2030 15 */
{PO2030_NoFliker, PO2030_NoFliker,
PO2030_50HZ, PO2030_50HZ,
PO2030_60HZ, PO2030_60HZ},
-/* SENSOR_TAS5130CK 15 */
- {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
- tas5130cxx_50HZ, tas5130cxx_50HZScale,
- tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130CXX 16 */
- {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
- tas5130cxx_50HZ, tas5130cxx_50HZScale,
- tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130C_VF0250 17 */
+/* SENSOR_TAS5130CK 16 */
+ {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker,
+ tas5130cxx_50HZScale, tas5130cxx_50HZ,
+ tas5130cxx_60HZScale, tas5130cxx_60HZ},
+/* SENSOR_TAS5130CXX 17 */
+ {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker,
+ tas5130cxx_50HZScale, tas5130cxx_50HZ,
+ tas5130cxx_60HZScale, tas5130cxx_60HZ},
+/* SENSOR_TAS5130C_VF0250 18 */
{tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
@@ -6729,6 +6327,7 @@ static void send_unknown(struct usb_device *dev, int sensor)
case SENSOR_ADCM2700:
case SENSOR_GC0305:
case SENSOR_OV7620:
+ case SENSOR_MI0360SOC:
case SENSOR_PB0330:
case SENSOR_PO2030:
reg_w(dev, 0x0d, 0x003a);
@@ -6820,7 +6419,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
start_2wr_probe(dev, 0x0e); /* PAS202BCB */
reg_w(dev, 0x08, 0x008d);
i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
- msleep(500);
+ msleep(50);
retword = i2c_read(gspca_dev, 0x03);
if (retword != 0)
return 0x0e; /* PAS202BCB */
@@ -6863,7 +6462,8 @@ struct sensor_by_chipset_revision {
__u8 internal_sensor_id;
};
static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
- {0xc001, 0x13}, /* MI0360 */
+ {0xc000, 0x12}, /* TAS5130C */
+ {0xc001, 0x13}, /* MI0360SOC */
{0xe001, 0x13},
{0x8001, 0x13},
{0x8000, 0x14}, /* CS2102K */
@@ -6963,7 +6563,6 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
reg_w(dev, 0x01, 0x0001);
reg_w(dev, 0xee, 0x008b);
reg_w(dev, 0x03, 0x0012);
-/* msleep(150); */
reg_w(dev, 0x01, 0x0012);
reg_w(dev, 0x05, 0x0012);
retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
@@ -7025,7 +6624,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
int vga = 1; /* 1: vga, 0: sif */
static const __u8 gamma[SENSOR_MAX] = {
4, /* SENSOR_ADCM2700 0 */
- 5, /* SENSOR_CS2102 1 */
+ 4, /* SENSOR_CS2102 1 */
5, /* SENSOR_CS2102K 2 */
4, /* SENSOR_GC0305 3 */
4, /* SENSOR_HDCS2020b 4 */
@@ -7033,15 +6632,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
4, /* SENSOR_HV7131C 6 */
4, /* SENSOR_ICM105A 7 */
4, /* SENSOR_MC501CB 8 */
- 3, /* SENSOR_OV7620 9 */
- 4, /* SENSOR_OV7630C 10 */
- 4, /* SENSOR_PAS106 11 */
- 4, /* SENSOR_PAS202B 12 */
- 4, /* SENSOR_PB0330 13 */
- 4, /* SENSOR_PO2030 14 */
- 4, /* SENSOR_TAS5130CK 15 */
- 4, /* SENSOR_TAS5130CXX 16 */
- 3, /* SENSOR_TAS5130C_VF0250 17 */
+ 4, /* SENSOR_MI0360SOC 9 */
+ 3, /* SENSOR_OV7620 10 */
+ 4, /* SENSOR_OV7630C 11 */
+ 4, /* SENSOR_PAS106 12 */
+ 4, /* SENSOR_PAS202B 13 */
+ 4, /* SENSOR_PB0330 14 */
+ 4, /* SENSOR_PO2030 15 */
+ 4, /* SENSOR_TAS5130CK 16 */
+ 3, /* SENSOR_TAS5130CXX 17 */
+ 3, /* SENSOR_TAS5130C_VF0250 18 */
};
/* define some sensors from the vendor/product */
@@ -7065,7 +6665,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
default:
PDEBUG(D_PROBE,
- "Sensor UNKNOW_0 force Tas5130");
+ "Sensor UNKNOWN_0 force Tas5130");
sd->sensor = SENSOR_TAS5130CXX;
}
break;
@@ -7103,7 +6703,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
case 0x10:
case 0x12:
- PDEBUG(D_PROBE, "Find Sensor TAS5130");
+ PDEBUG(D_PROBE, "Find Sensor TAS5130C");
sd->sensor = SENSOR_TAS5130CXX;
break;
case 0x11:
@@ -7112,9 +6712,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
case 0x13:
PDEBUG(D_PROBE,
- "Find Sensor MI0360. Chip revision %x",
+ "Find Sensor MI0360SOC. Chip revision %x",
sd->chip_revision);
- sd->sensor = SENSOR_PB0330;
+ sd->sensor = SENSOR_MI0360SOC;
break;
case 0x14:
PDEBUG(D_PROBE,
@@ -7228,17 +6828,17 @@ static int sd_start(struct gspca_dev *gspca_dev)
{hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */
{icm105axx_InitialScale, icm105axx_Initial}, /* 7 */
{MC501CB_InitialScale, MC501CB_Initial}, /* 8 */
- {OV7620_mode0, OV7620_mode1}, /* 9 */
- {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */
- {pas106b_InitialScale, pas106b_Initial}, /* 11 */
- {pas202b_Initial, pas202b_InitialScale}, /* 12 */
- {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */
-/* or {pb03303x_InitialScale, pb03303x_Initial}, */
- {PO2030_mode0, PO2030_mode1}, /* 14 */
- {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */
- {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */
+ {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */
+ {OV7620_mode0, OV7620_mode1}, /* 10 */
+ {ov7630c_InitialScale, ov7630c_Initial}, /* 11 */
+ {pas106b_InitialScale, pas106b_Initial}, /* 12 */
+ {pas202b_Initial, pas202b_InitialScale}, /* 13 */
+ {pb0330_Initial, pb0330_InitialScale}, /* 14 */
+ {PO2030_mode0, PO2030_mode1}, /* 15 */
+ {tas5130CK_InitialScale, tas5130CK_Initial}, /* 16 */
+ {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */
{tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial},
- /* 17 */
+ /* 18 */
};
/* create the JPEG header */
@@ -7258,19 +6858,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_PAS106:
usb_exchange(gspca_dev, pas106b_Initial_com);
break;
- case SENSOR_PB0330:
- if (mode) {
- if (sd->chip_revision == 0xc001
- || sd->chip_revision == 0xe001
- || sd->chip_revision == 0x8001)
- zc3_init = pb03303x_Initial;
- } else {
- if (sd->chip_revision == 0xc001
- || sd->chip_revision == 0xe001
- || sd->chip_revision == 0x8001)
- zc3_init = pb03303x_InitialScale;
- }
- break;
}
usb_exchange(gspca_dev, zc3_init);
@@ -7310,10 +6897,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* set the gamma tables when not set */
switch (sd->sensor) {
- case SENSOR_CS2102: /* gamma set in xxx_Initial */
- case SENSOR_CS2102K:
+ case SENSOR_CS2102K: /* gamma set in xxx_Initial */
case SENSOR_HDCS2020b:
- case SENSOR_PB0330: /* pb with chip_revision - see above */
case SENSOR_OV7630C:
case SENSOR_TAS5130CK:
break;
@@ -7365,7 +6950,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
setautogain(gspca_dev);
switch (sd->sensor) {
case SENSOR_PO2030:
- msleep(500);
+ msleep(50);
reg_r(gspca_dev, 0x0008);
reg_r(gspca_dev, 0x0007);
/*fall thru*/
@@ -7389,17 +6974,16 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame,
- __u8 *data,
+ u8 *data,
int len)
{
struct sd *sd = (struct sd *) gspca_dev;
if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- data, 0);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
/* put the JPEG header in the new frame */
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
/* remove the webcam's header:
@@ -7411,7 +6995,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data += 18;
len -= 18;
}
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index 2eb9dc2ebe5..b5439cabb38 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -139,7 +139,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
- goto exit;
+ goto exit_urb;
}
buf->urb = urb;
@@ -148,7 +148,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
if (!mem) {
v4l2_err(&dev->v4l2_dev,
"cannot allocate usb transfer buffer\n");
- goto exit;
+ goto exit_urb_buffer;
}
usb_fill_bulk_urb(buf->urb, dev->udev,
@@ -161,6 +161,10 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
list_add_tail(&buf->buff_list, &dev->free_buff_list);
}
return 0;
+exit_urb_buffer:
+ usb_free_urb(urb);
+exit_urb:
+ kfree(buf);
exit:
hdpvr_free_buffers(dev);
return retval;
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index 71c211402eb..60d992ee258 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -251,7 +251,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
- if (input < 0 || input >= HEXIUM_INPUTS)
+ if (input >= HEXIUM_INPUTS)
return -EINVAL;
hexium->cur_input = input;
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 39d65ca41c6..938a1f8f880 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -350,7 +350,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct hexium *hexium = (struct hexium *) dev->ext_priv;
- if (input < 0 || input >= HEXIUM_INPUTS)
+ if (input >= HEXIUM_INPUTS)
return -EINVAL;
hexium->cur_input = input;
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index 247d3115a9b..64360d26b32 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -275,7 +275,7 @@ static void ir_key_poll(struct IR_i2c *ir)
if (0 == rc) {
ir_input_nokey(ir->input, &ir->ir);
} else {
- ir_input_keydown(ir->input, &ir->ir, ir_key, ir_raw);
+ ir_input_keydown(ir->input, &ir->ir, ir_key);
}
}
@@ -299,7 +299,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct ir_scancode_table *ir_codes = NULL;
const char *name = NULL;
- int ir_type;
+ int ir_type = 0;
struct IR_i2c *ir;
struct input_dev *input_dev;
struct i2c_adapter *adap = client->adapter;
@@ -353,10 +353,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
ir_type = IR_TYPE_RC5;
ir_codes = &ir_codes_fusionhdtv_mce_table;
break;
- case 0x7a:
case 0x47:
case 0x71:
- case 0x2d:
if (adap->id == I2C_HW_B_CX2388x ||
adap->id == I2C_HW_B_CX2341X) {
/* Handled by cx88-input */
@@ -381,10 +379,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
ir_type = IR_TYPE_OTHER;
ir_codes = &ir_codes_avermedia_cardbus_table;
break;
- default:
- dprintk(1, DEVNAME ": Unsupported i2c address 0x%02x\n", addr);
- err = -ENODEV;
- goto err_out_free;
}
/* Let the caller override settings */
@@ -427,7 +421,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
/* Make sure we are all setup before going on */
- if (!name || !ir->get_key || !ir_codes) {
+ if (!name || !ir->get_key || !ir_type || !ir_codes) {
dprintk(1, DEVNAME ": Unsupported device at address 0x%02x\n",
addr);
err = -ENODEV;
@@ -443,7 +437,10 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_name(&client->dev));
/* init + register input device */
- ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
+ if (err < 0)
+ goto err_out_free;
+
input_dev->id.bustype = BUS_I2C;
input_dev->name = ir->name;
input_dev->phys = ir->phys;
@@ -462,6 +459,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
return 0;
err_out_free:
+ ir_input_free(input_dev);
input_free_device(input_dev);
kfree(ir);
return err;
@@ -475,6 +473,7 @@ static int ir_remove(struct i2c_client *client)
cancel_delayed_work_sync(&ir->work);
/* unregister device */
+ ir_input_free(ir->input);
input_unregister_device(ir->input);
/* free memory */
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index 4873b6ca580..79d0fe4990d 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -136,7 +136,8 @@ static const struct ivtv_card ivtv_card_pvr350 = {
.hw_audio = IVTV_HW_MSP34XX,
.hw_audio_ctrl = IVTV_HW_MSP34XX,
.hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
- IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+ IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 },
@@ -199,7 +200,9 @@ static const struct ivtv_card ivtv_card_pvr150 = {
.hw_audio_ctrl = IVTV_HW_CX25840,
.hw_muxer = IVTV_HW_WM8775,
.hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
- IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+ IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+ IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT |
+ IVTV_HW_Z8F0811_IR_HAUP,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 },
@@ -955,7 +958,8 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
- .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 |
+ IVTV_HW_I2C_IR_RX_AVER,
.video_inputs = {
{ IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
{ IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 },
@@ -965,6 +969,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
{ IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
},
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
/* enable line-in */
.gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
.xceive_pin = 10,
@@ -1025,13 +1030,15 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = {
/* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */
static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = {
- { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 },
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */
{ 0, 0, 0 }
};
static const struct ivtv_card ivtv_card_aver_ultra1500mce = {
.type = IVTV_CARD_AVER_ULTRA1500MCE,
.name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner",
+ .comment = "For non-NTSC tuners, use the pal= or secam= module options",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
@@ -1058,6 +1065,7 @@ static const struct ivtv_card ivtv_card_aver_ultra1500mce = {
.tuners = {
/* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */
{ .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 },
},
.pci_list = ivtv_pci_aver_ultra1500mce,
.i2c = &ivtv_i2c_std,
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
index e99a0a25557..6148827ec88 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -87,26 +87,43 @@
#define IVTV_PCI_ID_GOTVIEW1 0xffac
#define IVTV_PCI_ID_GOTVIEW2 0xffad
-/* hardware flags, no gaps allowed, IVTV_HW_GPIO must always be last */
-#define IVTV_HW_CX25840 (1 << 0)
-#define IVTV_HW_SAA7115 (1 << 1)
-#define IVTV_HW_SAA7127 (1 << 2)
-#define IVTV_HW_MSP34XX (1 << 3)
-#define IVTV_HW_TUNER (1 << 4)
-#define IVTV_HW_WM8775 (1 << 5)
-#define IVTV_HW_CS53L32A (1 << 6)
-#define IVTV_HW_TVEEPROM (1 << 7)
-#define IVTV_HW_SAA7114 (1 << 8)
-#define IVTV_HW_UPD64031A (1 << 9)
-#define IVTV_HW_UPD6408X (1 << 10)
-#define IVTV_HW_SAA717X (1 << 11)
-#define IVTV_HW_WM8739 (1 << 12)
-#define IVTV_HW_VP27SMPX (1 << 13)
-#define IVTV_HW_M52790 (1 << 14)
-#define IVTV_HW_GPIO (1 << 15)
+/* hardware flags, no gaps allowed */
+#define IVTV_HW_CX25840 (1 << 0)
+#define IVTV_HW_SAA7115 (1 << 1)
+#define IVTV_HW_SAA7127 (1 << 2)
+#define IVTV_HW_MSP34XX (1 << 3)
+#define IVTV_HW_TUNER (1 << 4)
+#define IVTV_HW_WM8775 (1 << 5)
+#define IVTV_HW_CS53L32A (1 << 6)
+#define IVTV_HW_TVEEPROM (1 << 7)
+#define IVTV_HW_SAA7114 (1 << 8)
+#define IVTV_HW_UPD64031A (1 << 9)
+#define IVTV_HW_UPD6408X (1 << 10)
+#define IVTV_HW_SAA717X (1 << 11)
+#define IVTV_HW_WM8739 (1 << 12)
+#define IVTV_HW_VP27SMPX (1 << 13)
+#define IVTV_HW_M52790 (1 << 14)
+#define IVTV_HW_GPIO (1 << 15)
+#define IVTV_HW_I2C_IR_RX_AVER (1 << 16)
+#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */
+#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18)
+#define IVTV_HW_Z8F0811_IR_TX_HAUP (1 << 19)
+#define IVTV_HW_Z8F0811_IR_RX_HAUP (1 << 20)
+
+#define IVTV_HW_Z8F0811_IR_HAUP (IVTV_HW_Z8F0811_IR_RX_HAUP | \
+ IVTV_HW_Z8F0811_IR_TX_HAUP)
#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
+#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \
+ IVTV_HW_I2C_IR_RX_HAUP_EXT | \
+ IVTV_HW_I2C_IR_RX_HAUP_INT | \
+ IVTV_HW_Z8F0811_IR_RX_HAUP)
+
+#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP)
+
+#define IVTV_HW_IR_ANY (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY)
+
/* video inputs */
#define IVTV_CARD_INPUT_VID_TUNER 1
#define IVTV_CARD_INPUT_SVIDEO1 2
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 463ec3457d7..347c3344f56 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -91,10 +91,15 @@ static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 };
+static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
static unsigned int cardtype_c = 1;
static unsigned int tuner_c = 1;
static unsigned int radio_c = 1;
+static unsigned int i2c_clock_period_c = 1;
static char pal[] = "---";
static char secam[] = "--";
static char ntsc[] = "-";
@@ -151,6 +156,7 @@ module_param(dec_vbi_buffers, int, 0644);
module_param(tunertype, int, 0644);
module_param(newi2c, int, 0644);
+module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644);
MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
"\t\t\tsee tuner.h for values");
@@ -245,6 +251,10 @@ MODULE_PARM_DESC(newi2c,
"Use new I2C implementation\n"
"\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
"\t\t\tDefault is autodetect");
+MODULE_PARM_DESC(i2c_clock_period,
+ "Period of SCL for the I2C bus controlled by the CX23415/6\n"
+ "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD));
MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card");
@@ -600,6 +610,15 @@ static void ivtv_process_options(struct ivtv *itv)
itv->options.cardtype = cardtype[itv->instance];
itv->options.tuner = tuner[itv->instance];
itv->options.radio = radio[itv->instance];
+
+ itv->options.i2c_clock_period = i2c_clock_period[itv->instance];
+ if (itv->options.i2c_clock_period == -1)
+ itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD;
+ else if (itv->options.i2c_clock_period < 10)
+ itv->options.i2c_clock_period = 10;
+ else if (itv->options.i2c_clock_period > 4500)
+ itv->options.i2c_clock_period = 4500;
+
itv->options.newi2c = newi2c;
if (tunertype < -1 || tunertype > 1) {
IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
@@ -865,6 +884,10 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
itv->hw_flags |= device;
}
+ /* probe for legacy IR controllers that aren't in card definitions */
+ if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0)
+ ivtv_i2c_new_ir_legacy(itv);
+
if (itv->card->hw_all & IVTV_HW_CX25840)
itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
else if (itv->card->hw_all & IVTV_HW_SAA717X)
@@ -1361,7 +1384,7 @@ static struct pci_driver ivtv_pci_driver = {
.remove = ivtv_remove,
};
-static int module_start(void)
+static int __init module_start(void)
{
printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
@@ -1385,7 +1408,7 @@ static int module_start(void)
return 0;
}
-static void module_cleanup(void)
+static void __exit module_cleanup(void)
{
pci_unregister_driver(&ivtv_pci_driver);
}
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 440f7328a7e..e4816da6482 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -64,6 +64,7 @@
#include <media/v4l2-device.h>
#include <media/tuner.h>
#include <media/cx2341x.h>
+#include <media/ir-kbd-i2c.h>
#include <linux/ivtv.h>
@@ -176,12 +177,16 @@ extern int ivtv_debug;
#define IVTV_MAX_PGM_INDEX (400)
+/* Default I2C SCL period in microseconds */
+#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20
+
struct ivtv_options {
int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */
int cardtype; /* force card type on load */
int tuner; /* set tuner on load */
int radio; /* enable/disable radio */
int newi2c; /* new I2C algorithm */
+ int i2c_clock_period; /* period of SCL for I2C bus */
};
/* ivtv-specific mailbox template */
@@ -677,6 +682,7 @@ struct ivtv {
int i2c_state; /* i2c bit state */
struct mutex i2c_bus_lock; /* lock i2c bus */
+ struct IR_i2c_init_data ir_i2c_init_data;
/* Program Index information */
u32 pgm_info_offset; /* start of pgm info in encoder memory */
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index b9c71e61f7d..2ee03c2a1b5 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -88,6 +88,11 @@
#define IVTV_UPD64083_I2C_ADDR 0x5c
#define IVTV_VP27SMPX_I2C_ADDR 0x5b
#define IVTV_M52790_I2C_ADDR 0x48
+#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR 0x40
+#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR 0x1a
+#define IVTV_HAUP_INT_IR_RX_I2C_ADDR 0x18
+#define IVTV_Z8F0811_IR_TX_I2C_ADDR 0x70
+#define IVTV_Z8F0811_IR_RX_I2C_ADDR 0x71
/* This array should match the IVTV_HW_ defines */
static const u8 hw_addrs[] = {
@@ -106,7 +111,12 @@ static const u8 hw_addrs[] = {
IVTV_WM8739_I2C_ADDR,
IVTV_VP27SMPX_I2C_ADDR,
IVTV_M52790_I2C_ADDR,
- 0 /* IVTV_HW_GPIO dummy driver ID */
+ 0, /* IVTV_HW_GPIO dummy driver ID */
+ IVTV_AVERMEDIA_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_AVER */
+ IVTV_HAUP_EXT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+ IVTV_HAUP_INT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+ IVTV_Z8F0811_IR_TX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+ IVTV_Z8F0811_IR_RX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_RX_HAUP */
};
/* This array should match the IVTV_HW_ defines */
@@ -126,7 +136,12 @@ static const char *hw_modules[] = {
"wm8739",
"vp27smpx",
"m52790",
- NULL
+ NULL,
+ NULL, /* IVTV_HW_I2C_IR_RX_AVER */
+ NULL, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+ NULL, /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+ NULL, /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+ NULL, /* IVTV_HW_Z8F0811_IR_RX_HAUP */
};
/* This array should match the IVTV_HW_ defines */
@@ -147,8 +162,95 @@ static const char * const hw_devicenames[] = {
"vp27smpx",
"m52790",
"gpio",
+ "ir_video", /* IVTV_HW_I2C_IR_RX_AVER */
+ "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+ "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+ "ir_tx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+ "ir_rx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_RX_HAUP */
};
+static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
+{
+ struct i2c_board_info info;
+ struct i2c_adapter *adap = &itv->i2c_adap;
+ struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data;
+ unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
+
+ /* Only allow one IR transmitter to be registered per board */
+ if (hw & IVTV_HW_IR_TX_ANY) {
+ if (itv->hw_flags & IVTV_HW_IR_TX_ANY)
+ return -1;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, type, I2C_NAME_SIZE);
+ return i2c_new_probed_device(adap, &info, addr_list) == NULL
+ ? -1 : 0;
+ }
+
+ /* Only allow one IR receiver to be registered per board */
+ if (itv->hw_flags & IVTV_HW_IR_RX_ANY)
+ return -1;
+
+ /* Our default information for ir-kbd-i2c.c to use */
+ switch (hw) {
+ case IVTV_HW_I2C_IR_RX_AVER:
+ init_data->ir_codes = &ir_codes_avermedia_cardbus_table;
+ init_data->internal_get_key_func =
+ IR_KBD_GET_KEY_AVERMEDIA_CARDBUS;
+ init_data->type = IR_TYPE_OTHER;
+ init_data->name = "AVerMedia AVerTV card";
+ break;
+ case IVTV_HW_I2C_IR_RX_HAUP_EXT:
+ case IVTV_HW_I2C_IR_RX_HAUP_INT:
+ /* Default to old black remote */
+ init_data->ir_codes = &ir_codes_rc5_tv_table;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
+ init_data->type = IR_TYPE_RC5;
+ init_data->name = itv->card_name;
+ break;
+ case IVTV_HW_Z8F0811_IR_RX_HAUP:
+ /* Default to grey remote */
+ init_data->ir_codes = &ir_codes_hauppauge_new_table;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+ init_data->type = IR_TYPE_RC5;
+ init_data->name = itv->card_name;
+ break;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.platform_data = init_data;
+ strlcpy(info.type, type, I2C_NAME_SIZE);
+
+ return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0;
+}
+
+/* Instantiate the IR receiver device using probing -- undesirable */
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv)
+{
+ struct i2c_board_info info;
+ /*
+ * The external IR receiver is at i2c address 0x34.
+ * The internal IR receiver is at i2c address 0x30.
+ *
+ * In theory, both can be fitted, and Hauppauge suggests an external
+ * overrides an internal. That's why we probe 0x1a (~0x34) first. CB
+ *
+ * Some of these addresses we probe may collide with other i2c address
+ * allocations, so this function must be called after all other i2c
+ * devices we care about are registered.
+ */
+ const unsigned short addr_list[] = {
+ 0x1a, /* Hauppauge IR external - collides with WM8739 */
+ 0x18, /* Hauppauge IR internal */
+ 0x71, /* Hauppauge IR (PVR150) */
+ 0x6b, /* Adaptec IR */
+ I2C_CLIENT_END
+ };
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+ return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list);
+}
+
int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
{
struct v4l2_subdev *sd;
@@ -178,8 +280,15 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
sd->grp_id = 1 << idx;
return sd ? 0 : -1;
}
+
+ if (hw & IVTV_HW_IR_ANY)
+ return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]);
+
+ /* Is it not an I2C device or one we do not wish to register? */
if (!hw_addrs[idx])
return -1;
+
+ /* It's an I2C device other than an analog tuner or IR chip */
if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
@@ -564,20 +673,22 @@ static struct i2c_adapter ivtv_i2c_adap_template = {
.owner = THIS_MODULE,
};
+#define IVTV_ALGO_BIT_TIMEOUT (2) /* seconds */
+
static const struct i2c_algo_bit_data ivtv_i2c_algo_template = {
.setsda = ivtv_setsda_old,
.setscl = ivtv_setscl_old,
.getsda = ivtv_getsda_old,
.getscl = ivtv_getscl_old,
- .udelay = 10,
- .timeout = 200,
+ .udelay = IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2, /* microseconds */
+ .timeout = IVTV_ALGO_BIT_TIMEOUT * HZ, /* jiffies */
};
static struct i2c_client ivtv_i2c_client_template = {
.name = "ivtv internal",
};
-/* init + register i2c adapter + instantiate IR receiver */
+/* init + register i2c adapter */
int init_ivtv_i2c(struct ivtv *itv)
{
int retval;
@@ -585,11 +696,10 @@ int init_ivtv_i2c(struct ivtv *itv)
IVTV_DEBUG_I2C("i2c init\n");
/* Sanity checks for the I2C hardware arrays. They must be the
- * same size and GPIO must be the last entry.
+ * same size.
*/
if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
- ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) ||
- IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) {
+ ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules)) {
IVTV_ERR("Mismatched I2C hardware arrays\n");
return -ENODEV;
}
@@ -602,6 +712,7 @@ int init_ivtv_i2c(struct ivtv *itv)
memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
sizeof(struct i2c_algo_bit_data));
}
+ itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2;
itv->i2c_algo.data = itv;
itv->i2c_adap.algo_data = &itv->i2c_algo;
@@ -623,32 +734,6 @@ int init_ivtv_i2c(struct ivtv *itv)
else
retval = i2c_bit_add_bus(&itv->i2c_adap);
- /* Instantiate the IR receiver device, if present */
- if (retval == 0) {
- struct i2c_board_info info;
- /* The external IR receiver is at i2c address 0x34 (0x35 for
- reads). Future Hauppauge cards will have an internal
- receiver at 0x30 (0x31 for reads). In theory, both can be
- fitted, and Hauppauge suggest an external overrides an
- internal.
-
- That's why we probe 0x1a (~0x34) first. CB
- */
- const unsigned short addr_list[] = {
- 0x1a, /* Hauppauge IR external */
- 0x18, /* Hauppauge IR internal */
- 0x71, /* Hauppauge IR (PVR150) */
- 0x64, /* Pixelview IR */
- 0x30, /* KNC ONE IR */
- 0x6b, /* Adaptec IR */
- I2C_CLIENT_END
- };
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
- i2c_new_probed_device(&itv->i2c_adap, &info, addr_list);
- }
-
return retval;
}
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h
index 396928a06a5..9332920ca4f 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.h
+++ b/drivers/media/video/ivtv/ivtv-i2c.h
@@ -21,6 +21,7 @@
#ifndef IVTV_I2C_H
#define IVTV_I2C_H
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv);
int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw);
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index 3454070e63f..c1fc6dc776f 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -478,7 +478,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
- if (input < 0 || input >= MXB_INPUTS)
+ if (input >= MXB_INPUTS)
return -EINVAL;
mxb->cur_input = input;
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index eccb40ab7fe..20522933346 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -247,7 +247,7 @@
/* COM5 */
#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
-#define AFR_SPPED 0x40 /* Auto frame rate control speed slection */
+#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */
/* Auto frame rate max rate control */
#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
new file mode 100644
index 00000000000..c81ae219288
--- /dev/null
+++ b/drivers/media/video/ov9640.c
@@ -0,0 +1,801 @@
+/*
+ * OmniVision OV96xx Camera Driver
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/soc_camera.h>
+
+#include "ov9640.h"
+
+/* default register setup */
+static const struct ov9640_reg ov9640_regs_dflt[] = {
+ { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP },
+ { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS |
+ OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN },
+ { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) },
+ { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD },
+ { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY },
+ { OV9640_COM16, OV9640_COM16_RB_AVG },
+
+ /* Gamma curve P */
+ { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 },
+ { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 },
+ { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },
+ { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 },
+
+ /* Gamma curve T */
+ { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 },
+ { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },
+ { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e },
+ { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 },
+};
+
+/* Configurations
+ * NOTE: for YUV, alter the following registers:
+ * COM12 |= OV9640_COM12_YUV_AVG
+ *
+ * for RGB, alter the following registers:
+ * COM7 |= OV9640_COM7_RGB
+ * COM13 |= OV9640_COM13_RGB_AVG
+ * COM15 |= proper RGB color encoding mode
+ */
+static const struct ov9640_reg ov9640_regs_qqcif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) },
+ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QCIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qqvga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QVGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qcif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QCIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qvga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QVGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_cif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, OV9640_COM7_CIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_vga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, OV9640_COM7_VGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_sxga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, 0 },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_yuv[] = {
+ { OV9640_MTX1, 0x58 },
+ { OV9640_MTX2, 0x48 },
+ { OV9640_MTX3, 0x10 },
+ { OV9640_MTX4, 0x28 },
+ { OV9640_MTX5, 0x48 },
+ { OV9640_MTX6, 0x70 },
+ { OV9640_MTX7, 0x40 },
+ { OV9640_MTX8, 0x40 },
+ { OV9640_MTX9, 0x40 },
+ { OV9640_MTXS, 0x0f },
+};
+
+static const struct ov9640_reg ov9640_regs_rgb[] = {
+ { OV9640_MTX1, 0x71 },
+ { OV9640_MTX2, 0x3e },
+ { OV9640_MTX3, 0x0c },
+ { OV9640_MTX4, 0x33 },
+ { OV9640_MTX5, 0x72 },
+ { OV9640_MTX6, 0x00 },
+ { OV9640_MTX7, 0x2b },
+ { OV9640_MTX8, 0x66 },
+ { OV9640_MTX9, 0xd2 },
+ { OV9640_MTXS, 0x65 },
+};
+
+/*
+ * TODO: this sensor also supports RGB555 and RGB565 formats, but support for
+ * them has not yet been sufficiently tested and so it is not included with
+ * this version of the driver. To test and debug these formats add two entries
+ * to the below array, see ov722x.c for an example.
+ */
+static const struct soc_camera_data_format ov9640_fmt_lists[] = {
+ {
+ .name = "UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+static const struct v4l2_queryctrl ov9640_controls[] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Vertically",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Horizontally",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
+/* read a register */
+static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret;
+ u8 data = reg;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ msg.flags = I2C_M_RD;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ *val = data;
+ return 0;
+
+err:
+ dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+ return ret;
+}
+
+/* write a register */
+static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+ u8 _val;
+ unsigned char data[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+ return ret;
+ }
+
+ /* we have to read the register back ... no idea why, maybe HW bug */
+ ret = ov9640_reg_read(client, reg, &_val);
+ if (ret)
+ dev_err(&client->dev,
+ "Failed reading back register 0x%02x!\n", reg);
+
+ return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset)
+{
+ u8 val;
+ int ret;
+
+ ret = ov9640_reg_read(client, reg, &val);
+ if (ret) {
+ dev_err(&client->dev,
+ "[Read]-Modify-Write of register %02x failed!\n", reg);
+ return val;
+ }
+
+ val |= set;
+ val &= ~unset;
+
+ ret = ov9640_reg_write(client, reg, val);
+ if (ret)
+ dev_err(&client->dev,
+ "Read-Modify-[Write] of register %02x failed!\n", reg);
+
+ return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov9640_reset(struct i2c_client *client)
+{
+ int ret;
+
+ ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET);
+ if (ret)
+ dev_err(&client->dev,
+ "An error occured while entering soft reset!\n");
+
+ return ret;
+}
+
+/* Start/Stop streaming from the device */
+static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov9640_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ return 0;
+}
+
+/* Request bus settings on camera side */
+static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+ /*
+ * REVISIT: the camera probably can do 10 bit transfers, but I don't
+ * have those pins connected on my hardware.
+ */
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+/* Get status of additional camera capabilities */
+static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct i2c_client *client = sd->priv;
+ struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
+ struct ov9640_priv, subdev);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ ctrl->value = priv->flag_vflip;
+ break;
+ case V4L2_CID_HFLIP:
+ ctrl->value = priv->flag_hflip;
+ break;
+ }
+ return 0;
+}
+
+/* Set status of additional camera capabilities */
+static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct i2c_client *client = sd->priv;
+ struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
+ struct ov9640_priv, subdev);
+
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ priv->flag_vflip = ctrl->value;
+ if (ctrl->value)
+ ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ OV9640_MVFP_V, 0);
+ else
+ ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ 0, OV9640_MVFP_V);
+ break;
+ case V4L2_CID_HFLIP:
+ priv->flag_hflip = ctrl->value;
+ if (ctrl->value)
+ ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ OV9640_MVFP_H, 0);
+ else
+ ret = ov9640_reg_rmw(client, OV9640_MVFP,
+ 0, OV9640_MVFP_H);
+ break;
+ }
+
+ return ret;
+}
+
+/* Get chip identification */
+static int ov9640_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = sd->priv;
+ struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
+ struct ov9640_priv, subdev);
+
+ id->ident = priv->model;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov9640_get_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = sd->priv;
+ int ret;
+ u8 val;
+
+ if (reg->reg & ~0xff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = ov9640_reg_read(client, reg->reg, &val);
+ if (ret)
+ return ret;
+
+ reg->val = (__u64)val;
+
+ return 0;
+}
+
+static int ov9640_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = sd->priv;
+
+ if (reg->reg & ~0xff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov9640_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+/* select nearest higher resolution for capture */
+static void ov9640_res_roundup(u32 *width, u32 *height)
+{
+ int i;
+ enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+ int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
+ int res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
+
+ for (i = 0; i < ARRAY_SIZE(res_x); i++) {
+ if (res_x[i] >= *width && res_y[i] >= *height) {
+ *width = res_x[i];
+ *height = res_y[i];
+ return;
+ }
+ }
+
+ *width = res_x[SXGA];
+ *height = res_y[SXGA];
+}
+
+/* Prepare necessary register changes depending on color encoding */
+static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt)
+{
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_UYVY:
+ alt->com12 = OV9640_COM12_YUV_AVG;
+ alt->com13 = OV9640_COM13_Y_DELAY_EN |
+ OV9640_COM13_YUV_DLY(0x01);
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ alt->com7 = OV9640_COM7_RGB;
+ alt->com13 = OV9640_COM13_RGB_AVG;
+ alt->com15 = OV9640_COM15_RGB_555;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ alt->com7 = OV9640_COM7_RGB;
+ alt->com13 = OV9640_COM13_RGB_AVG;
+ alt->com15 = OV9640_COM15_RGB_565;
+ break;
+ };
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9640_write_regs(struct i2c_client *client,
+ u32 width, u32 pixfmt, struct ov9640_reg_alt *alts)
+{
+ const struct ov9640_reg *ov9640_regs, *matrix_regs;
+ int ov9640_regs_len, matrix_regs_len;
+ int i, ret;
+ u8 val;
+
+ /* select register configuration for given resolution */
+ switch (width) {
+ case W_QQCIF:
+ ov9640_regs = ov9640_regs_qqcif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif);
+ break;
+ case W_QQVGA:
+ ov9640_regs = ov9640_regs_qqvga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga);
+ break;
+ case W_QCIF:
+ ov9640_regs = ov9640_regs_qcif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif);
+ break;
+ case W_QVGA:
+ ov9640_regs = ov9640_regs_qvga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga);
+ break;
+ case W_CIF:
+ ov9640_regs = ov9640_regs_cif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif);
+ break;
+ case W_VGA:
+ ov9640_regs = ov9640_regs_vga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga);
+ break;
+ case W_SXGA:
+ ov9640_regs = ov9640_regs_sxga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga);
+ break;
+ default:
+ dev_err(&client->dev, "Failed to select resolution!\n");
+ return -EINVAL;
+ }
+
+ /* select color matrix configuration for given color encoding */
+ if (pixfmt == V4L2_PIX_FMT_UYVY) {
+ matrix_regs = ov9640_regs_yuv;
+ matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
+ } else {
+ matrix_regs = ov9640_regs_rgb;
+ matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb);
+ }
+
+ /* write register settings into the module */
+ for (i = 0; i < ov9640_regs_len; i++) {
+ val = ov9640_regs[i].val;
+
+ switch (ov9640_regs[i].reg) {
+ case OV9640_COM7:
+ val |= alts->com7;
+ break;
+ case OV9640_COM12:
+ val |= alts->com12;
+ break;
+ case OV9640_COM13:
+ val |= alts->com13;
+ break;
+ case OV9640_COM15:
+ val |= alts->com15;
+ break;
+ }
+
+ ret = ov9640_reg_write(client, ov9640_regs[i].reg, val);
+ if (ret)
+ return ret;
+ }
+
+ /* write color matrix configuration into the module */
+ for (i = 0; i < matrix_regs_len; i++) {
+ ret = ov9640_reg_write(client, matrix_regs[i].reg,
+ matrix_regs[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* program default register values */
+static int ov9640_prog_dflt(struct i2c_client *client)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) {
+ ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg,
+ ov9640_regs_dflt[i].val);
+ if (ret)
+ return ret;
+ }
+
+ /* wait for the changes to actually happen, 140ms are not enough yet */
+ mdelay(150);
+
+ return 0;
+}
+
+/* set the format we will capture in */
+static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct ov9640_reg_alt alts = {0};
+ int ret;
+
+ ov9640_res_roundup(&pix->width, &pix->height);
+ ov9640_alter_regs(pix->pixelformat, &alts);
+
+ ov9640_reset(client);
+
+ ret = ov9640_prog_dflt(client);
+ if (ret)
+ return ret;
+
+ return ov9640_write_regs(client, pix->width, pix->pixelformat, &alts);
+}
+
+static int ov9640_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ ov9640_res_roundup(&pix->width, &pix->height);
+ pix->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = W_SXGA;
+ a->c.height = H_SXGA;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = W_SXGA;
+ a->bounds.height = H_SXGA;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+
+
+static int ov9640_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
+{
+ struct ov9640_priv *priv = i2c_get_clientdata(client);
+ u8 pid, ver, midh, midl;
+ const char *devname;
+ int ret = 0;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
+ dev_err(&client->dev, "Parent missing or invalid!\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ icd->formats = ov9640_fmt_lists;
+ icd->num_formats = ARRAY_SIZE(ov9640_fmt_lists);
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+
+ ret = ov9640_reg_read(client, OV9640_PID, &pid);
+ if (ret)
+ goto err;
+
+ ret = ov9640_reg_read(client, OV9640_VER, &ver);
+ if (ret)
+ goto err;
+
+ ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
+ if (ret)
+ goto err;
+
+ ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
+ if (ret)
+ goto err;
+
+ switch (VERSION(pid, ver)) {
+ case OV9640_V2:
+ devname = "ov9640";
+ priv->model = V4L2_IDENT_OV9640;
+ priv->revision = 2;
+ case OV9640_V3:
+ devname = "ov9640";
+ priv->model = V4L2_IDENT_OV9640;
+ priv->revision = 3;
+ break;
+ default:
+ dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname, pid, ver, midh, midl);
+
+err:
+ return ret;
+}
+
+static struct soc_camera_ops ov9640_ops = {
+ .set_bus_param = ov9640_set_bus_param,
+ .query_bus_param = ov9640_query_bus_param,
+ .controls = ov9640_controls,
+ .num_controls = ARRAY_SIZE(ov9640_controls),
+};
+
+static struct v4l2_subdev_core_ops ov9640_core_ops = {
+ .g_ctrl = ov9640_g_ctrl,
+ .s_ctrl = ov9640_s_ctrl,
+ .g_chip_ident = ov9640_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov9640_get_register,
+ .s_register = ov9640_set_register,
+#endif
+
+};
+
+static struct v4l2_subdev_video_ops ov9640_video_ops = {
+ .s_stream = ov9640_s_stream,
+ .s_fmt = ov9640_s_fmt,
+ .try_fmt = ov9640_try_fmt,
+ .cropcap = ov9640_cropcap,
+ .g_crop = ov9640_g_crop,
+
+};
+
+static struct v4l2_subdev_ops ov9640_subdev_ops = {
+ .core = &ov9640_core_ops,
+ .video = &ov9640_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov9640_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov9640_priv *priv;
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl;
+ int ret;
+
+ if (!icd) {
+ dev_err(&client->dev, "Missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
+ if (!icl) {
+ dev_err(&client->dev, "Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev,
+ "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
+
+ icd->ops = &ov9640_ops;
+
+ ret = ov9640_video_probe(icd, client);
+
+ if (ret) {
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov9640_remove(struct i2c_client *client)
+{
+ struct ov9640_priv *priv = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+ { "ov9640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640_i2c_driver = {
+ .driver = {
+ .name = "ov9640",
+ },
+ .probe = ov9640_probe,
+ .remove = ov9640_remove,
+ .id_table = ov9640_id,
+};
+
+static int __init ov9640_module_init(void)
+{
+ return i2c_add_driver(&ov9640_i2c_driver);
+}
+
+static void __exit ov9640_module_exit(void)
+{
+ i2c_del_driver(&ov9640_i2c_driver);
+}
+
+module_init(ov9640_module_init);
+module_exit(ov9640_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h
new file mode 100644
index 00000000000..f8a51b70792
--- /dev/null
+++ b/drivers/media/video/ov9640.h
@@ -0,0 +1,209 @@
+/*
+ * OmniVision OV96xx Camera Header File
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@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 __DRIVERS_MEDIA_VIDEO_OV9640_H__
+#define __DRIVERS_MEDIA_VIDEO_OV9640_H__
+
+/* Register definitions */
+#define OV9640_GAIN 0x00
+#define OV9640_BLUE 0x01
+#define OV9640_RED 0x02
+#define OV9640_VFER 0x03
+#define OV9640_COM1 0x04
+#define OV9640_BAVE 0x05
+#define OV9640_GEAVE 0x06
+#define OV9640_RSID 0x07
+#define OV9640_RAVE 0x08
+#define OV9640_COM2 0x09
+#define OV9640_PID 0x0a
+#define OV9640_VER 0x0b
+#define OV9640_COM3 0x0c
+#define OV9640_COM4 0x0d
+#define OV9640_COM5 0x0e
+#define OV9640_COM6 0x0f
+#define OV9640_AECH 0x10
+#define OV9640_CLKRC 0x11
+#define OV9640_COM7 0x12
+#define OV9640_COM8 0x13
+#define OV9640_COM9 0x14
+#define OV9640_COM10 0x15
+/* 0x16 - RESERVED */
+#define OV9640_HSTART 0x17
+#define OV9640_HSTOP 0x18
+#define OV9640_VSTART 0x19
+#define OV9640_VSTOP 0x1a
+#define OV9640_PSHFT 0x1b
+#define OV9640_MIDH 0x1c
+#define OV9640_MIDL 0x1d
+#define OV9640_MVFP 0x1e
+#define OV9640_LAEC 0x1f
+#define OV9640_BOS 0x20
+#define OV9640_GBOS 0x21
+#define OV9640_GROS 0x22
+#define OV9640_ROS 0x23
+#define OV9640_AEW 0x24
+#define OV9640_AEB 0x25
+#define OV9640_VPT 0x26
+#define OV9640_BBIAS 0x27
+#define OV9640_GBBIAS 0x28
+/* 0x29 - RESERVED */
+#define OV9640_EXHCH 0x2a
+#define OV9640_EXHCL 0x2b
+#define OV9640_RBIAS 0x2c
+#define OV9640_ADVFL 0x2d
+#define OV9640_ADVFH 0x2e
+#define OV9640_YAVE 0x2f
+#define OV9640_HSYST 0x30
+#define OV9640_HSYEN 0x31
+#define OV9640_HREF 0x32
+#define OV9640_CHLF 0x33
+#define OV9640_ARBLM 0x34
+/* 0x35..0x36 - RESERVED */
+#define OV9640_ADC 0x37
+#define OV9640_ACOM 0x38
+#define OV9640_OFON 0x39
+#define OV9640_TSLB 0x3a
+#define OV9640_COM11 0x3b
+#define OV9640_COM12 0x3c
+#define OV9640_COM13 0x3d
+#define OV9640_COM14 0x3e
+#define OV9640_EDGE 0x3f
+#define OV9640_COM15 0x40
+#define OV9640_COM16 0x41
+#define OV9640_COM17 0x42
+/* 0x43..0x4e - RESERVED */
+#define OV9640_MTX1 0x4f
+#define OV9640_MTX2 0x50
+#define OV9640_MTX3 0x51
+#define OV9640_MTX4 0x52
+#define OV9640_MTX5 0x53
+#define OV9640_MTX6 0x54
+#define OV9640_MTX7 0x55
+#define OV9640_MTX8 0x56
+#define OV9640_MTX9 0x57
+#define OV9640_MTXS 0x58
+/* 0x59..0x61 - RESERVED */
+#define OV9640_LCC1 0x62
+#define OV9640_LCC2 0x63
+#define OV9640_LCC3 0x64
+#define OV9640_LCC4 0x65
+#define OV9640_LCC5 0x66
+#define OV9640_MANU 0x67
+#define OV9640_MANV 0x68
+#define OV9640_HV 0x69
+#define OV9640_MBD 0x6a
+#define OV9640_DBLV 0x6b
+#define OV9640_GSP 0x6c /* ... till 0x7b */
+#define OV9640_GST 0x7c /* ... till 0x8a */
+
+#define OV9640_CLKRC_DPLL_EN 0x80
+#define OV9640_CLKRC_DIRECT 0x40
+#define OV9640_CLKRC_DIV(x) ((x) & 0x3f)
+
+#define OV9640_PSHFT_VAL(x) ((x) & 0xff)
+
+#define OV9640_ACOM_2X_ANALOG 0x80
+#define OV9640_ACOM_RSVD 0x12
+
+#define OV9640_MVFP_V 0x10
+#define OV9640_MVFP_H 0x20
+
+#define OV9640_COM1_HREF_NOSKIP 0x00
+#define OV9640_COM1_HREF_2SKIP 0x04
+#define OV9640_COM1_HREF_3SKIP 0x08
+#define OV9640_COM1_QQFMT 0x20
+
+#define OV9640_COM2_SSM 0x10
+
+#define OV9640_COM3_VP 0x04
+
+#define OV9640_COM4_QQ_VP 0x80
+#define OV9640_COM4_RSVD 0x40
+
+#define OV9640_COM5_SYSCLK 0x80
+#define OV9640_COM5_LONGEXP 0x01
+
+#define OV9640_COM6_OPT_BLC 0x40
+#define OV9640_COM6_ADBLC_BIAS 0x08
+#define OV9640_COM6_FMT_RST 0x82
+#define OV9640_COM6_ADBLC_OPTEN 0x01
+
+#define OV9640_COM7_RAW_RGB 0x01
+#define OV9640_COM7_RGB 0x04
+#define OV9640_COM7_QCIF 0x08
+#define OV9640_COM7_QVGA 0x10
+#define OV9640_COM7_CIF 0x20
+#define OV9640_COM7_VGA 0x40
+#define OV9640_COM7_SCCB_RESET 0x80
+
+#define OV9640_TSLB_YVYU_YUYV 0x04
+#define OV9640_TSLB_YUYV_UYVY 0x08
+
+#define OV9640_COM12_YUV_AVG 0x04
+#define OV9640_COM12_RSVD 0x40
+
+#define OV9640_COM13_GAMMA_NONE 0x00
+#define OV9640_COM13_GAMMA_Y 0x40
+#define OV9640_COM13_GAMMA_RAW 0x80
+#define OV9640_COM13_RGB_AVG 0x20
+#define OV9640_COM13_MATRIX_EN 0x10
+#define OV9640_COM13_Y_DELAY_EN 0x08
+#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07)
+
+#define OV9640_COM15_OR_00FF 0x00
+#define OV9640_COM15_OR_01FE 0x40
+#define OV9640_COM15_OR_10F0 0xc0
+#define OV9640_COM15_RGB_NORM 0x00
+#define OV9640_COM15_RGB_565 0x10
+#define OV9640_COM15_RGB_555 0x30
+
+#define OV9640_COM16_RB_AVG 0x01
+
+/* IDs */
+#define OV9640_V2 0x9648
+#define OV9640_V3 0x9649
+#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF))
+
+/* supported resolutions */
+enum {
+ W_QQCIF = 88,
+ W_QQVGA = 160,
+ W_QCIF = 176,
+ W_QVGA = 320,
+ W_CIF = 352,
+ W_VGA = 640,
+ W_SXGA = 1280
+};
+#define H_SXGA 960
+
+/* Misc. structures */
+struct ov9640_reg_alt {
+ u8 com7;
+ u8 com12;
+ u8 com13;
+ u8 com15;
+};
+
+struct ov9640_reg {
+ u8 reg;
+ u8 val;
+};
+
+struct ov9640_priv {
+ struct v4l2_subdev subdev;
+
+ int model;
+ int revision;
+
+ bool flag_vflip;
+ bool flag_hflip;
+};
+
+#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index a1ad38fc49c..73ec970ca5c 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -14,8 +14,10 @@
* unless the userspace driver also doesn't work for you...
*
* Changes:
- * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
- * - pms_capture: report back -EFAULT
+ * 25-11-2009 Hans Verkuil <hverkuil@xs4all.nl>
+ * - converted to version 2 of the V4L API.
+ * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
+ * - pms_capture: report back -EFAULT
*/
#include <linux/module.h>
@@ -27,175 +29,183 @@
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
#include <asm/io.h>
-#include <linux/videodev.h>
+
+#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
+#include <media/v4l2-device.h>
-#include <asm/uaccess.h>
+MODULE_LICENSE("GPL");
#define MOTOROLA 1
-#define PHILIPS2 2
+#define PHILIPS2 2 /* SAA7191 */
#define PHILIPS1 3
#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
-struct pms_device
-{
- struct video_device v;
- struct video_picture picture;
- int height;
- int width;
- unsigned long in_use;
- struct mutex lock;
-};
-
-struct i2c_info
-{
+struct i2c_info {
u8 slave;
u8 sub;
u8 data;
u8 hits;
};
-static int i2c_count;
-static struct i2c_info i2cinfo[64];
+struct pms {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ int height;
+ int width;
+ int depth;
+ int input;
+ s32 brightness, saturation, hue, contrast;
+ unsigned long in_use;
+ struct mutex lock;
+ int i2c_count;
+ struct i2c_info i2cinfo[64];
+
+ int decoder;
+ int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+ v4l2_std_id std;
+ int io;
+ int data;
+ void __iomem *mem;
+};
-static int decoder = PHILIPS2;
-static int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+static struct pms pms_card;
/*
* I/O ports and Shared Memory
*/
-static int io_port = 0x250;
-static int data_port = 0x251;
-static int mem_base = 0xC8000;
-static void __iomem *mem;
-static int video_nr = -1;
+static int io_port = 0x250;
+module_param(io_port, int, 0);
+static int mem_base = 0xc8000;
+module_param(mem_base, int, 0);
+static int video_nr = -1;
+module_param(video_nr, int, 0);
-static inline void mvv_write(u8 index, u8 value)
+
+static inline void mvv_write(struct pms *dev, u8 index, u8 value)
{
- outw(index|(value<<8), io_port);
+ outw(index | (value << 8), dev->io);
}
-static inline u8 mvv_read(u8 index)
+static inline u8 mvv_read(struct pms *dev, u8 index)
{
- outb(index, io_port);
- return inb(data_port);
+ outb(index, dev->io);
+ return inb(dev->data);
}
-static int pms_i2c_stat(u8 slave)
+static int pms_i2c_stat(struct pms *dev, u8 slave)
{
- int counter;
+ int counter = 0;
int i;
- outb(0x28, io_port);
+ outb(0x28, dev->io);
- counter=0;
- while((inb(data_port)&0x01)==0)
- if(counter++==256)
+ while ((inb(dev->data) & 0x01) == 0)
+ if (counter++ == 256)
break;
- while((inb(data_port)&0x01)!=0)
- if(counter++==256)
+ while ((inb(dev->data) & 0x01) != 0)
+ if (counter++ == 256)
break;
- outb(slave, io_port);
+ outb(slave, dev->io);
- counter=0;
- while((inb(data_port)&0x01)==0)
- if(counter++==256)
+ counter = 0;
+ while ((inb(dev->data) & 0x01) == 0)
+ if (counter++ == 256)
break;
- while((inb(data_port)&0x01)!=0)
- if(counter++==256)
+ while ((inb(dev->data) & 0x01) != 0)
+ if (counter++ == 256)
break;
- for(i=0;i<12;i++)
- {
- char st=inb(data_port);
- if((st&2)!=0)
+ for (i = 0; i < 12; i++) {
+ char st = inb(dev->data);
+
+ if ((st & 2) != 0)
return -1;
- if((st&1)==0)
+ if ((st & 1) == 0)
break;
}
- outb(0x29, io_port);
- return inb(data_port);
+ outb(0x29, dev->io);
+ return inb(dev->data);
}
-static int pms_i2c_write(u16 slave, u16 sub, u16 data)
+static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
{
- int skip=0;
+ int skip = 0;
int count;
int i;
- for(i=0;i<i2c_count;i++)
- {
- if((i2cinfo[i].slave==slave) &&
- (i2cinfo[i].sub == sub))
- {
- if(i2cinfo[i].data==data)
- skip=1;
- i2cinfo[i].data=data;
- i=i2c_count+1;
+ for (i = 0; i < dev->i2c_count; i++) {
+ if ((dev->i2cinfo[i].slave == slave) &&
+ (dev->i2cinfo[i].sub == sub)) {
+ if (dev->i2cinfo[i].data == data)
+ skip = 1;
+ dev->i2cinfo[i].data = data;
+ i = dev->i2c_count + 1;
}
}
- if(i==i2c_count && i2c_count<64)
- {
- i2cinfo[i2c_count].slave=slave;
- i2cinfo[i2c_count].sub=sub;
- i2cinfo[i2c_count].data=data;
- i2c_count++;
+ if (i == dev->i2c_count && dev->i2c_count < 64) {
+ dev->i2cinfo[dev->i2c_count].slave = slave;
+ dev->i2cinfo[dev->i2c_count].sub = sub;
+ dev->i2cinfo[dev->i2c_count].data = data;
+ dev->i2c_count++;
}
- if(skip)
+ if (skip)
return 0;
- mvv_write(0x29, sub);
- mvv_write(0x2A, data);
- mvv_write(0x28, slave);
+ mvv_write(dev, 0x29, sub);
+ mvv_write(dev, 0x2A, data);
+ mvv_write(dev, 0x28, slave);
- outb(0x28, io_port);
+ outb(0x28, dev->io);
- count=0;
- while((inb(data_port)&1)==0)
- if(count>255)
+ count = 0;
+ while ((inb(dev->data) & 1) == 0)
+ if (count > 255)
break;
- while((inb(data_port)&1)!=0)
- if(count>255)
+ while ((inb(dev->data) & 1) != 0)
+ if (count > 255)
break;
- count=inb(data_port);
+ count = inb(dev->data);
- if(count&2)
+ if (count & 2)
return -1;
return count;
}
-static int pms_i2c_read(int slave, int sub)
+static int pms_i2c_read(struct pms *dev, int slave, int sub)
{
- int i=0;
- for(i=0;i<i2c_count;i++)
- {
- if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
- return i2cinfo[i].data;
+ int i;
+
+ for (i = 0; i < dev->i2c_count; i++) {
+ if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
+ return dev->i2cinfo[i].data;
}
return 0;
}
-static void pms_i2c_andor(int slave, int sub, int and, int or)
+static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
{
u8 tmp;
- tmp=pms_i2c_read(slave, sub);
- tmp = (tmp&and)|or;
- pms_i2c_write(slave, sub, tmp);
+ tmp = pms_i2c_read(dev, slave, sub);
+ tmp = (tmp & and) | or;
+ pms_i2c_write(dev, slave, sub, tmp);
}
/*
@@ -203,100 +213,108 @@ static void pms_i2c_andor(int slave, int sub, int and, int or)
*/
-static void pms_videosource(short source)
+static void pms_videosource(struct pms *dev, short source)
{
- mvv_write(0x2E, source?0x31:0x30);
+ switch (dev->decoder) {
+ case MOTOROLA:
+ break;
+ case PHILIPS2:
+ pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
+ break;
+ case PHILIPS1:
+ break;
+ }
+ mvv_write(dev, 0x2E, 0x31);
+ /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
+ But could not make this work correctly. Only Composite input
+ worked for me. */
}
-static void pms_hue(short hue)
+static void pms_hue(struct pms *dev, short hue)
{
- switch(decoder)
- {
- case MOTOROLA:
- pms_i2c_write(0x8A, 0x00, hue);
- break;
- case PHILIPS2:
- pms_i2c_write(0x8A, 0x07, hue);
- break;
- case PHILIPS1:
- pms_i2c_write(0x42, 0x07, hue);
- break;
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, hue);
+ break;
+ case PHILIPS2:
+ pms_i2c_write(dev, 0x8a, 0x07, hue);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x07, hue);
+ break;
}
}
-static void pms_colour(short colour)
+static void pms_saturation(struct pms *dev, short sat)
{
- switch(decoder)
- {
- case MOTOROLA:
- pms_i2c_write(0x8A, 0x00, colour);
- break;
- case PHILIPS1:
- pms_i2c_write(0x42, 0x12, colour);
- break;
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, sat);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x12, sat);
+ break;
}
}
-static void pms_contrast(short contrast)
+static void pms_contrast(struct pms *dev, short contrast)
{
- switch(decoder)
- {
- case MOTOROLA:
- pms_i2c_write(0x8A, 0x00, contrast);
- break;
- case PHILIPS1:
- pms_i2c_write(0x42, 0x13, contrast);
- break;
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, contrast);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x13, contrast);
+ break;
}
}
-static void pms_brightness(short brightness)
+static void pms_brightness(struct pms *dev, short brightness)
{
- switch(decoder)
- {
- case MOTOROLA:
- pms_i2c_write(0x8A, 0x00, brightness);
- pms_i2c_write(0x8A, 0x00, brightness);
- pms_i2c_write(0x8A, 0x00, brightness);
- break;
- case PHILIPS1:
- pms_i2c_write(0x42, 0x19, brightness);
- break;
+ switch (dev->decoder) {
+ case MOTOROLA:
+ pms_i2c_write(dev, 0x8a, 0x00, brightness);
+ pms_i2c_write(dev, 0x8a, 0x00, brightness);
+ pms_i2c_write(dev, 0x8a, 0x00, brightness);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x42, 0x19, brightness);
+ break;
}
}
-static void pms_format(short format)
+static void pms_format(struct pms *dev, short format)
{
int target;
- standard = format;
- if(decoder==PHILIPS1)
- target=0x42;
- else if(decoder==PHILIPS2)
- target=0x8A;
+ dev->standard = format;
+
+ if (dev->decoder == PHILIPS1)
+ target = 0x42;
+ else if (dev->decoder == PHILIPS2)
+ target = 0x8a;
else
return;
- switch(format)
- {
- case 0: /* Auto */
- pms_i2c_andor(target, 0x0D, 0xFE,0x00);
- pms_i2c_andor(target, 0x0F, 0x3F,0x80);
- break;
- case 1: /* NTSC */
- pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
- pms_i2c_andor(target, 0x0F, 0x3F, 0x40);
- break;
- case 2: /* PAL */
- pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
- pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
- break;
- case 3: /* SECAM */
- pms_i2c_andor(target, 0x0D, 0xFE, 0x01);
- pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
- break;
+ switch (format) {
+ case 0: /* Auto */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
+ break;
+ case 1: /* NTSC */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
+ break;
+ case 2: /* PAL */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+ break;
+ case 3: /* SECAM */
+ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
+ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+ break;
}
}
@@ -308,18 +326,17 @@ static void pms_format(short format)
* people need it. We also don't yet use the PMS interrupt.
*/
-static void pms_hstart(short start)
+static void pms_hstart(struct pms *dev, short start)
{
- switch(decoder)
- {
- case PHILIPS1:
- pms_i2c_write(0x8A, 0x05, start);
- pms_i2c_write(0x8A, 0x18, start);
- break;
- case PHILIPS2:
- pms_i2c_write(0x42, 0x05, start);
- pms_i2c_write(0x42, 0x18, start);
- break;
+ switch (dev->decoder) {
+ case PHILIPS1:
+ pms_i2c_write(dev, 0x8a, 0x05, start);
+ pms_i2c_write(dev, 0x8a, 0x18, start);
+ break;
+ case PHILIPS2:
+ pms_i2c_write(dev, 0x42, 0x05, start);
+ pms_i2c_write(dev, 0x42, 0x18, start);
+ break;
}
}
@@ -327,293 +344,271 @@ static void pms_hstart(short start)
* Bandpass filters
*/
-static void pms_bandpass(short pass)
+static void pms_bandpass(struct pms *dev, short pass)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
}
-static void pms_antisnow(short snow)
+static void pms_antisnow(struct pms *dev, short snow)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
}
-static void pms_sharpness(short sharp)
+static void pms_sharpness(struct pms *dev, short sharp)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
}
-static void pms_chromaagc(short agc)
+static void pms_chromaagc(struct pms *dev, short agc)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
}
-static void pms_vertnoise(short noise)
+static void pms_vertnoise(struct pms *dev, short noise)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x10, 0xFC, noise&3);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
}
-static void pms_forcecolour(short colour)
+static void pms_forcecolour(struct pms *dev, short colour)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
}
-static void pms_antigamma(short gamma)
+static void pms_antigamma(struct pms *dev, short gamma)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
}
-static void pms_prefilter(short filter)
+static void pms_prefilter(struct pms *dev, short filter)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
}
-static void pms_hfilter(short filter)
+static void pms_hfilter(struct pms *dev, short filter)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
}
-static void pms_vfilter(short filter)
+static void pms_vfilter(struct pms *dev, short filter)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
}
-static void pms_killcolour(short colour)
+static void pms_killcolour(struct pms *dev, short colour)
{
- if(decoder==PHILIPS2)
- {
- pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
- pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
- }
- else if(decoder==PHILIPS1)
- {
- pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
- pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
+ if (dev->decoder == PHILIPS2) {
+ pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
+ pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
+ } else if (dev->decoder == PHILIPS1) {
+ pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
+ pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
}
}
-static void pms_chromagain(short chroma)
+static void pms_chromagain(struct pms *dev, short chroma)
{
- if(decoder==PHILIPS2)
- {
- pms_i2c_write(0x8A, 0x11, chroma);
- }
- else if(decoder==PHILIPS1)
- {
- pms_i2c_write(0x42, 0x11, chroma);
- }
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_write(dev, 0x8a, 0x11, chroma);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_write(dev, 0x42, 0x11, chroma);
}
-static void pms_spacialcompl(short data)
+static void pms_spacialcompl(struct pms *dev, short data)
{
- mvv_write(0x3B, data);
+ mvv_write(dev, 0x3b, data);
}
-static void pms_spacialcomph(short data)
+static void pms_spacialcomph(struct pms *dev, short data)
{
- mvv_write(0x3A, data);
+ mvv_write(dev, 0x3a, data);
}
-static void pms_vstart(short start)
+static void pms_vstart(struct pms *dev, short start)
{
- mvv_write(0x16, start);
- mvv_write(0x17, (start>>8)&0x01);
+ mvv_write(dev, 0x16, start);
+ mvv_write(dev, 0x17, (start >> 8) & 0x01);
}
#endif
-static void pms_secamcross(short cross)
+static void pms_secamcross(struct pms *dev, short cross)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
}
-static void pms_swsense(short sense)
+static void pms_swsense(struct pms *dev, short sense)
{
- if(decoder==PHILIPS2)
- {
- pms_i2c_write(0x8A, 0x0A, sense);
- pms_i2c_write(0x8A, 0x0B, sense);
- }
- else if(decoder==PHILIPS1)
- {
- pms_i2c_write(0x42, 0x0A, sense);
- pms_i2c_write(0x42, 0x0B, sense);
+ if (dev->decoder == PHILIPS2) {
+ pms_i2c_write(dev, 0x8a, 0x0a, sense);
+ pms_i2c_write(dev, 0x8a, 0x0b, sense);
+ } else if (dev->decoder == PHILIPS1) {
+ pms_i2c_write(dev, 0x42, 0x0a, sense);
+ pms_i2c_write(dev, 0x42, 0x0b, sense);
}
}
-static void pms_framerate(short frr)
+static void pms_framerate(struct pms *dev, short frr)
{
- int fps=(standard==1)?30:25;
- if(frr==0)
+ int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
+
+ if (frr == 0)
return;
- fps=fps/frr;
- mvv_write(0x14,0x80|fps);
- mvv_write(0x15,1);
+ fps = fps/frr;
+ mvv_write(dev, 0x14, 0x80 | fps);
+ mvv_write(dev, 0x15, 1);
}
-static void pms_vert(u8 deciden, u8 decinum)
+static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
{
- mvv_write(0x1C, deciden); /* Denominator */
- mvv_write(0x1D, decinum); /* Numerator */
+ mvv_write(dev, 0x1c, deciden); /* Denominator */
+ mvv_write(dev, 0x1d, decinum); /* Numerator */
}
/*
* Turn 16bit ratios into best small ratio the chipset can grok
*/
-static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
+static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
{
- /* Knock it down by /5 once */
- if(decinum%5==0)
- {
- deciden/=5;
- decinum/=5;
+ /* Knock it down by / 5 once */
+ if (decinum % 5 == 0) {
+ deciden /= 5;
+ decinum /= 5;
}
/*
* 3's
*/
- while(decinum%3==0 && deciden%3==0)
- {
- deciden/=3;
- decinum/=3;
+ while (decinum % 3 == 0 && deciden % 3 == 0) {
+ deciden /= 3;
+ decinum /= 3;
}
/*
* 2's
*/
- while(decinum%2==0 && deciden%2==0)
- {
- decinum/=2;
- deciden/=2;
+ while (decinum % 2 == 0 && deciden % 2 == 0) {
+ decinum /= 2;
+ deciden /= 2;
}
/*
* Fudgyify
*/
- while(deciden>32)
- {
- deciden/=2;
- decinum=(decinum+1)/2;
+ while (deciden > 32) {
+ deciden /= 2;
+ decinum = (decinum + 1) / 2;
}
- if(deciden==32)
+ if (deciden == 32)
deciden--;
- pms_vert(deciden,decinum);
+ pms_vert(dev, deciden, decinum);
}
-static void pms_horzdeci(short decinum, short deciden)
+static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
{
- if(decinum<=512)
- {
- if(decinum%5==0)
- {
- decinum/=5;
- deciden/=5;
+ if (decinum <= 512) {
+ if (decinum % 5 == 0) {
+ decinum /= 5;
+ deciden /= 5;
}
- }
- else
- {
- decinum=512;
- deciden=640; /* 768 would be ideal */
+ } else {
+ decinum = 512;
+ deciden = 640; /* 768 would be ideal */
}
- while(((decinum|deciden)&1)==0)
- {
- decinum>>=1;
- deciden>>=1;
+ while (((decinum | deciden) & 1) == 0) {
+ decinum >>= 1;
+ deciden >>= 1;
}
- while(deciden>32)
- {
- deciden>>=1;
- decinum=(decinum+1)>>1;
+ while (deciden > 32) {
+ deciden >>= 1;
+ decinum = (decinum + 1) >> 1;
}
- if(deciden==32)
+ if (deciden == 32)
deciden--;
- mvv_write(0x24, 0x80|deciden);
- mvv_write(0x25, decinum);
+ mvv_write(dev, 0x24, 0x80 | deciden);
+ mvv_write(dev, 0x25, decinum);
}
-static void pms_resolution(short width, short height)
+static void pms_resolution(struct pms *dev, short width, short height)
{
int fg_height;
- fg_height=height;
- if(fg_height>280)
- fg_height=280;
+ fg_height = height;
+ if (fg_height > 280)
+ fg_height = 280;
- mvv_write(0x18, fg_height);
- mvv_write(0x19, fg_height>>8);
+ mvv_write(dev, 0x18, fg_height);
+ mvv_write(dev, 0x19, fg_height >> 8);
- if(standard==1)
- {
- mvv_write(0x1A, 0xFC);
- mvv_write(0x1B, 0x00);
- if(height>fg_height)
- pms_vertdeci(240,240);
+ if (dev->std & V4L2_STD_525_60) {
+ mvv_write(dev, 0x1a, 0xfc);
+ mvv_write(dev, 0x1b, 0x00);
+ if (height > fg_height)
+ pms_vertdeci(dev, 240, 240);
else
- pms_vertdeci(fg_height,240);
- }
- else
- {
- mvv_write(0x1A, 0x1A);
- mvv_write(0x1B, 0x01);
- if(fg_height>256)
- pms_vertdeci(270,270);
+ pms_vertdeci(dev, fg_height, 240);
+ } else {
+ mvv_write(dev, 0x1a, 0x1a);
+ mvv_write(dev, 0x1b, 0x01);
+ if (fg_height > 256)
+ pms_vertdeci(dev, 270, 270);
else
- pms_vertdeci(fg_height, 270);
+ pms_vertdeci(dev, fg_height, 270);
}
- mvv_write(0x12,0);
- mvv_write(0x13, MVVMEMORYWIDTH);
- mvv_write(0x42, 0x00);
- mvv_write(0x43, 0x00);
- mvv_write(0x44, MVVMEMORYWIDTH);
+ mvv_write(dev, 0x12, 0);
+ mvv_write(dev, 0x13, MVVMEMORYWIDTH);
+ mvv_write(dev, 0x42, 0x00);
+ mvv_write(dev, 0x43, 0x00);
+ mvv_write(dev, 0x44, MVVMEMORYWIDTH);
- mvv_write(0x22, width+8);
- mvv_write(0x23, (width+8)>> 8);
+ mvv_write(dev, 0x22, width + 8);
+ mvv_write(dev, 0x23, (width + 8) >> 8);
- if(standard==1)
- pms_horzdeci(width,640);
+ if (dev->std & V4L2_STD_525_60)
+ pms_horzdeci(dev, width, 640);
else
- pms_horzdeci(width+8, 768);
+ pms_horzdeci(dev, width + 8, 768);
- mvv_write(0x30, mvv_read(0x30)&0xFE);
- mvv_write(0x08, mvv_read(0x08)|0x01);
- mvv_write(0x01, mvv_read(0x01)&0xFD);
- mvv_write(0x32, 0x00);
- mvv_write(0x33, MVVMEMORYWIDTH);
+ mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
+ mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
+ mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
+ mvv_write(dev, 0x32, 0x00);
+ mvv_write(dev, 0x33, MVVMEMORYWIDTH);
}
@@ -621,52 +616,49 @@ static void pms_resolution(short width, short height)
* Set Input
*/
-static void pms_vcrinput(short input)
+static void pms_vcrinput(struct pms *dev, short input)
{
- if(decoder==PHILIPS2)
- pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
- else if(decoder==PHILIPS1)
- pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
+ if (dev->decoder == PHILIPS2)
+ pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
+ else if (dev->decoder == PHILIPS1)
+ pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
}
-static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count)
+static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
{
int y;
- int dw = 2*dev->width;
-
- char tmp[dw+32]; /* using a temp buffer is faster than direct */
+ int dw = 2 * dev->width;
+ char tmp[dw + 32]; /* using a temp buffer is faster than direct */
int cnt = 0;
- int len=0;
+ int len = 0;
unsigned char r8 = 0x5; /* value for reg8 */
if (rgb555)
r8 |= 0x20; /* else use untranslated rgb = 565 */
- mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */
+ mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
- for (y = 0; y < dev->height; y++ )
- {
- writeb(0, mem); /* synchronisiert neue Zeile */
+ for (y = 0; y < dev->height; y++) {
+ writeb(0, dev->mem); /* synchronisiert neue Zeile */
/*
* This is in truth a fifo, be very careful as if you
* forgot this odd things will occur 8)
*/
- memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */
+ memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
cnt -= dev->height;
- while (cnt <= 0)
- {
+ while (cnt <= 0) {
/*
* Don't copy too far
*/
- int dt=dw;
- if(dt+len>count)
- dt=count-len;
+ int dt = dw;
+ if (dt + len > count)
+ dt = count - len;
cnt += dev->height;
- if (copy_to_user(buf, tmp+32, dt))
+ if (copy_to_user(buf, tmp + 32, dt))
return len ? len : -EFAULT;
buf += dt;
len += dt;
@@ -680,221 +672,278 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int
* Video4linux interfacing
*/
-static long pms_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int pms_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vcap)
{
- struct video_device *dev = video_devdata(file);
- struct pms_device *pd=(struct pms_device *)dev;
-
- switch(cmd)
- {
- case VIDIOCGCAP:
- {
- struct video_capability *b = arg;
- strcpy(b->name, "Mediavision PMS");
- b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
- b->channels = 4;
- b->audios = 0;
- b->maxwidth = 640;
- b->maxheight = 480;
- b->minwidth = 16;
- b->minheight = 16;
- return 0;
- }
- case VIDIOCGCHAN:
- {
- struct video_channel *v = arg;
- if(v->channel<0 || v->channel>3)
- return -EINVAL;
- v->flags=0;
- v->tuners=1;
- /* Good question.. its composite or SVHS so.. */
- v->type = VIDEO_TYPE_CAMERA;
- switch(v->channel)
- {
- case 0:
- strcpy(v->name, "Composite");break;
- case 1:
- strcpy(v->name, "SVideo");break;
- case 2:
- strcpy(v->name, "Composite(VCR)");break;
- case 3:
- strcpy(v->name, "SVideo(VCR)");break;
- }
- return 0;
- }
- case VIDIOCSCHAN:
- {
- struct video_channel *v = arg;
- if(v->channel<0 || v->channel>3)
- return -EINVAL;
- mutex_lock(&pd->lock);
- pms_videosource(v->channel&1);
- pms_vcrinput(v->channel>>1);
- mutex_unlock(&pd->lock);
- return 0;
- }
- case VIDIOCGTUNER:
- {
- struct video_tuner *v = arg;
- if(v->tuner)
- return -EINVAL;
- strcpy(v->name, "Format");
- v->rangelow=0;
- v->rangehigh=0;
- v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
- switch(standard)
- {
- case 0:
- v->mode = VIDEO_MODE_AUTO;
- break;
- case 1:
- v->mode = VIDEO_MODE_NTSC;
- break;
- case 2:
- v->mode = VIDEO_MODE_PAL;
- break;
- case 3:
- v->mode = VIDEO_MODE_SECAM;
- break;
- }
- return 0;
- }
- case VIDIOCSTUNER:
- {
- struct video_tuner *v = arg;
- if(v->tuner)
- return -EINVAL;
- mutex_lock(&pd->lock);
- switch(v->mode)
- {
- case VIDEO_MODE_AUTO:
- pms_framerate(25);
- pms_secamcross(0);
- pms_format(0);
- break;
- case VIDEO_MODE_NTSC:
- pms_framerate(30);
- pms_secamcross(0);
- pms_format(1);
- break;
- case VIDEO_MODE_PAL:
- pms_framerate(25);
- pms_secamcross(0);
- pms_format(2);
- break;
- case VIDEO_MODE_SECAM:
- pms_framerate(25);
- pms_secamcross(1);
- pms_format(2);
- break;
- default:
- mutex_unlock(&pd->lock);
- return -EINVAL;
- }
- mutex_unlock(&pd->lock);
- return 0;
- }
- case VIDIOCGPICT:
- {
- struct video_picture *p = arg;
- *p = pd->picture;
- return 0;
- }
- case VIDIOCSPICT:
- {
- struct video_picture *p = arg;
- if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16)
- ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15)))
- return -EINVAL;
- pd->picture= *p;
+ struct pms *dev = video_drvdata(file);
- /*
- * Now load the card.
- */
+ strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
+ strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
+ strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
+ vcap->version = KERNEL_VERSION(0, 0, 3);
+ vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+}
- mutex_lock(&pd->lock);
- pms_brightness(p->brightness>>8);
- pms_hue(p->hue>>8);
- pms_colour(p->colour>>8);
- pms_contrast(p->contrast>>8);
- mutex_unlock(&pd->lock);
- return 0;
- }
- case VIDIOCSWIN:
- {
- struct video_window *vw = arg;
- if(vw->flags)
- return -EINVAL;
- if(vw->clipcount)
- return -EINVAL;
- if(vw->height<16||vw->height>480)
- return -EINVAL;
- if(vw->width<16||vw->width>640)
- return -EINVAL;
- pd->width=vw->width;
- pd->height=vw->height;
- mutex_lock(&pd->lock);
- pms_resolution(pd->width, pd->height);
- mutex_unlock(&pd->lock); /* Ok we figured out what to use from our wide choice */
- return 0;
- }
- case VIDIOCGWIN:
- {
- struct video_window *vw = arg;
- memset(vw,0,sizeof(*vw));
- vw->width=pd->width;
- vw->height=pd->height;
- return 0;
- }
- case VIDIOCKEY:
- return 0;
- case VIDIOCCAPTURE:
- case VIDIOCGFBUF:
- case VIDIOCSFBUF:
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- return -EINVAL;
- default:
- return -ENOIOCTLCMD;
+static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ static const char *inputs[4] = {
+ "Composite",
+ "S-Video",
+ "Composite (VCR)",
+ "S-Video (VCR)"
+ };
+
+ if (vin->index > 3)
+ return -EINVAL;
+ strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
+ vin->type = V4L2_INPUT_TYPE_CAMERA;
+ vin->audioset = 0;
+ vin->tuner = 0;
+ vin->std = V4L2_STD_ALL;
+ vin->status = 0;
+ return 0;
+}
+
+static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+ struct pms *dev = video_drvdata(file);
+
+ *inp = dev->input;
+ return 0;
+}
+
+static int pms_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ struct pms *dev = video_drvdata(file);
+
+ if (inp > 3)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ dev->input = inp;
+ pms_videosource(dev, inp & 1);
+ pms_vcrinput(dev, inp >> 1);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct pms *dev = video_drvdata(file);
+
+ *std = dev->std;
+ return 0;
+}
+
+static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct pms *dev = video_drvdata(file);
+ int ret = 0;
+
+ dev->std = *std;
+ mutex_lock(&dev->lock);
+ if (dev->std & V4L2_STD_NTSC) {
+ pms_framerate(dev, 30);
+ pms_secamcross(dev, 0);
+ pms_format(dev, 1);
+ } else if (dev->std & V4L2_STD_PAL) {
+ pms_framerate(dev, 25);
+ pms_secamcross(dev, 0);
+ pms_format(dev, 2);
+ } else if (dev->std & V4L2_STD_SECAM) {
+ pms_framerate(dev, 25);
+ pms_secamcross(dev, 1);
+ pms_format(dev, 2);
+ } else {
+ ret = -EINVAL;
+ }
+ /*
+ switch (v->mode) {
+ case VIDEO_MODE_AUTO:
+ pms_framerate(dev, 25);
+ pms_secamcross(dev, 0);
+ pms_format(dev, 0);
+ break;
+ }*/
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int pms_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
+ }
+ return -EINVAL;
+}
+
+static int pms_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pms *dev = video_drvdata(file);
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dev->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dev->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = dev->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dev->hue;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int pms_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct pms *dev = video_drvdata(file);
+ int ret = 0;
+
+ mutex_lock(&dev->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->brightness = ctrl->value;
+ pms_brightness(dev, dev->brightness);
+ break;
+ case V4L2_CID_CONTRAST:
+ dev->contrast = ctrl->value;
+ pms_contrast(dev, dev->contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ dev->saturation = ctrl->value;
+ pms_saturation(dev, dev->saturation);
+ break;
+ case V4L2_CID_HUE:
+ dev->hue = ctrl->value;
+ pms_hue(dev, dev->hue);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
}
+ mutex_unlock(&dev->lock);
+ return ret;
+}
+
+static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct pms *dev = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ pix->width = dev->width;
+ pix->height = dev->height;
+ pix->pixelformat = dev->width == 15 ?
+ V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 2 * dev->width;
+ pix->sizeimage = 2 * dev->width * dev->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
-static long pms_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
+static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
- return video_usercopy(file, cmd, arg, pms_do_ioctl);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ if (pix->height < 16 || pix->height > 480)
+ return -EINVAL;
+ if (pix->width < 16 || pix->width > 640)
+ return -EINVAL;
+ if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
+ pix->pixelformat != V4L2_PIX_FMT_RGB565)
+ return -EINVAL;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = 2 * pix->width;
+ pix->sizeimage = 2 * pix->width * pix->height;
+ /* Just a guess */
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct pms *dev = video_drvdata(file);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int ret = pms_try_fmt_vid_cap(file, fh, fmt);
+
+ if (ret)
+ return ret;
+ mutex_lock(&dev->lock);
+ dev->width = pix->width;
+ dev->height = pix->height;
+ dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
+ pms_resolution(dev, dev->width, dev->height);
+ /* Ok we figured out what to use from our wide choice */
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
+ { 0, 0, 0, 0 }
+ },
+ { 0, 0, 0,
+ "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
+ { 0, 0, 0, 0 }
+ },
+ };
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 1)
+ return -EINVAL;
+
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
}
static ssize_t pms_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct video_device *v = video_devdata(file);
- struct pms_device *pd=(struct pms_device *)v;
+ struct pms *dev = video_drvdata(file);
int len;
- mutex_lock(&pd->lock);
- len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
- mutex_unlock(&pd->lock);
+ mutex_lock(&dev->lock);
+ len = pms_capture(dev, buf, (dev->depth == 15), count);
+ mutex_unlock(&dev->lock);
return len;
}
static int pms_exclusive_open(struct file *file)
{
- struct video_device *v = video_devdata(file);
- struct pms_device *pd = (struct pms_device *)v;
+ struct pms *dev = video_drvdata(file);
- return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0;
+ return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0;
}
static int pms_exclusive_release(struct file *file)
{
- struct video_device *v = video_devdata(file);
- struct pms_device *pd = (struct pms_device *)v;
+ struct pms *dev = video_drvdata(file);
- clear_bit(0, &pd->in_use);
+ clear_bit(0, &dev->in_use);
return 0;
}
@@ -902,78 +951,81 @@ static const struct v4l2_file_operations pms_fops = {
.owner = THIS_MODULE,
.open = pms_exclusive_open,
.release = pms_exclusive_release,
- .ioctl = pms_ioctl,
+ .ioctl = video_ioctl2,
.read = pms_read,
};
-static struct video_device pms_template=
-{
- .name = "Mediavision PMS",
- .fops = &pms_fops,
- .release = video_device_release_empty,
+static const struct v4l2_ioctl_ops pms_ioctl_ops = {
+ .vidioc_querycap = pms_querycap,
+ .vidioc_g_input = pms_g_input,
+ .vidioc_s_input = pms_s_input,
+ .vidioc_enum_input = pms_enum_input,
+ .vidioc_g_std = pms_g_std,
+ .vidioc_s_std = pms_s_std,
+ .vidioc_queryctrl = pms_queryctrl,
+ .vidioc_g_ctrl = pms_g_ctrl,
+ .vidioc_s_ctrl = pms_s_ctrl,
+ .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
};
-static struct pms_device pms_device;
-
-
/*
* Probe for and initialise the Mediavision PMS
*/
-static int init_mediavision(void)
+static int init_mediavision(struct pms *dev)
{
int id;
int idec, decst;
int i;
-
- unsigned char i2c_defs[]={
- 0x4C,0x30,0x00,0xE8,
- 0xB6,0xE2,0x00,0x00,
- 0xFF,0xFF,0x00,0x00,
- 0x00,0x00,0x78,0x98,
- 0x00,0x00,0x00,0x00,
- 0x34,0x0A,0xF4,0xCE,
- 0xE4
+ static const unsigned char i2c_defs[] = {
+ 0x4c, 0x30, 0x00, 0xe8,
+ 0xb6, 0xe2, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x98,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x34, 0x0a, 0xf4, 0xce,
+ 0xe4
};
- mem = ioremap(mem_base, 0x800);
- if (!mem)
+ dev->mem = ioremap(mem_base, 0x800);
+ if (!dev->mem)
return -ENOMEM;
- if (!request_region(0x9A01, 1, "Mediavision PMS config"))
- {
- printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
- iounmap(mem);
+ if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
+ printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
+ iounmap(dev->mem);
return -EBUSY;
}
- if (!request_region(io_port, 3, "Mediavision PMS"))
- {
- printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
- release_region(0x9A01, 1);
- iounmap(mem);
+ if (!request_region(dev->io, 3, "Mediavision PMS")) {
+ printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
return -EBUSY;
}
- outb(0xB8, 0x9A01); /* Unlock */
- outb(io_port>>4, 0x9A01); /* Set IO port */
+ outb(0xb8, 0x9a01); /* Unlock */
+ outb(dev->io >> 4, 0x9a01); /* Set IO port */
- id=mvv_read(3);
- decst=pms_i2c_stat(0x43);
+ id = mvv_read(dev, 3);
+ decst = pms_i2c_stat(dev, 0x43);
- if(decst!=-1)
- idec=2;
- else if(pms_i2c_stat(0xb9)!=-1)
- idec=3;
- else if(pms_i2c_stat(0x8b)!=-1)
- idec=1;
+ if (decst != -1)
+ idec = 2;
+ else if (pms_i2c_stat(dev, 0xb9) != -1)
+ idec = 3;
+ else if (pms_i2c_stat(dev, 0x8b) != -1)
+ idec = 1;
else
- idec=0;
+ idec = 0;
printk(KERN_INFO "PMS type is %d\n", idec);
- if(idec == 0) {
- release_region(io_port, 3);
- release_region(0x9A01, 1);
- iounmap(mem);
+ if (idec == 0) {
+ release_region(dev->io, 3);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
return -ENODEV;
}
@@ -981,51 +1033,50 @@ static int init_mediavision(void)
* Ok we have a PMS of some sort
*/
- mvv_write(0x04, mem_base>>12); /* Set the memory area */
+ mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
/* Ok now load the defaults */
- for(i=0;i<0x19;i++)
- {
- if(i2c_defs[i]==0xFF)
- pms_i2c_andor(0x8A, i, 0x07,0x00);
+ for (i = 0; i < 0x19; i++) {
+ if (i2c_defs[i] == 0xff)
+ pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
else
- pms_i2c_write(0x8A, i, i2c_defs[i]);
+ pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
}
- pms_i2c_write(0xB8,0x00,0x12);
- pms_i2c_write(0xB8,0x04,0x00);
- pms_i2c_write(0xB8,0x07,0x00);
- pms_i2c_write(0xB8,0x08,0x00);
- pms_i2c_write(0xB8,0x09,0xFF);
- pms_i2c_write(0xB8,0x0A,0x00);
- pms_i2c_write(0xB8,0x0B,0x10);
- pms_i2c_write(0xB8,0x10,0x03);
-
- mvv_write(0x01, 0x00);
- mvv_write(0x05, 0xA0);
- mvv_write(0x08, 0x25);
- mvv_write(0x09, 0x00);
- mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
-
- mvv_write(0x10, 0x02);
- mvv_write(0x1E, 0x0C);
- mvv_write(0x1F, 0x03);
- mvv_write(0x26, 0x06);
-
- mvv_write(0x2B, 0x00);
- mvv_write(0x2C, 0x20);
- mvv_write(0x2D, 0x00);
- mvv_write(0x2F, 0x70);
- mvv_write(0x32, 0x00);
- mvv_write(0x33, MVVMEMORYWIDTH);
- mvv_write(0x34, 0x00);
- mvv_write(0x35, 0x00);
- mvv_write(0x3A, 0x80);
- mvv_write(0x3B, 0x10);
- mvv_write(0x20, 0x00);
- mvv_write(0x21, 0x00);
- mvv_write(0x30, 0x22);
+ pms_i2c_write(dev, 0xb8, 0x00, 0x12);
+ pms_i2c_write(dev, 0xb8, 0x04, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x07, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x08, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x09, 0xff);
+ pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
+ pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
+ pms_i2c_write(dev, 0xb8, 0x10, 0x03);
+
+ mvv_write(dev, 0x01, 0x00);
+ mvv_write(dev, 0x05, 0xa0);
+ mvv_write(dev, 0x08, 0x25);
+ mvv_write(dev, 0x09, 0x00);
+ mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
+
+ mvv_write(dev, 0x10, 0x02);
+ mvv_write(dev, 0x1e, 0x0c);
+ mvv_write(dev, 0x1f, 0x03);
+ mvv_write(dev, 0x26, 0x06);
+
+ mvv_write(dev, 0x2b, 0x00);
+ mvv_write(dev, 0x2c, 0x20);
+ mvv_write(dev, 0x2d, 0x00);
+ mvv_write(dev, 0x2f, 0x70);
+ mvv_write(dev, 0x32, 0x00);
+ mvv_write(dev, 0x33, MVVMEMORYWIDTH);
+ mvv_write(dev, 0x34, 0x00);
+ mvv_write(dev, 0x35, 0x00);
+ mvv_write(dev, 0x3a, 0x80);
+ mvv_write(dev, 0x3b, 0x10);
+ mvv_write(dev, 0x20, 0x00);
+ mvv_write(dev, 0x21, 0x00);
+ mvv_write(dev, 0x30, 0x22);
return 0;
}
@@ -1038,53 +1089,77 @@ static int enable;
module_param(enable, int, 0);
#endif
-static int __init init_pms_cards(void)
+static int __init pms_init(void)
{
- printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
+ struct pms *dev = &pms_card;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ int res;
+
+ strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
+
+ v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
#ifndef MODULE
if (!enable) {
- printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to "
- "probe\n");
+ v4l2_err(v4l2_dev,
+ "PMS: not enabled, use pms.enable=1 to probe\n");
return -ENODEV;
}
#endif
- data_port = io_port +1;
+ dev->decoder = PHILIPS2;
+ dev->io = io_port;
+ dev->data = io_port + 1;
- if(init_mediavision())
- {
- printk(KERN_INFO "Board not found.\n");
+ if (init_mediavision(dev)) {
+ v4l2_err(v4l2_dev, "Board not found.\n");
return -ENODEV;
}
- memcpy(&pms_device, &pms_template, sizeof(pms_template));
- mutex_init(&pms_device.lock);
- pms_device.height=240;
- pms_device.width=320;
- pms_swsense(75);
- pms_resolution(320,240);
- return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr);
-}
-
-module_param(io_port, int, 0);
-module_param(mem_base, int, 0);
-module_param(video_nr, int, 0);
-MODULE_LICENSE("GPL");
+ res = v4l2_device_register(NULL, v4l2_dev);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ return res;
+ }
-static void __exit shutdown_mediavision(void)
-{
- release_region(io_port,3);
- release_region(0x9A01, 1);
+ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
+ dev->vdev.v4l2_dev = v4l2_dev;
+ dev->vdev.fops = &pms_fops;
+ dev->vdev.ioctl_ops = &pms_ioctl_ops;
+ dev->vdev.release = video_device_release_empty;
+ video_set_drvdata(&dev->vdev, dev);
+ mutex_init(&dev->lock);
+ dev->std = V4L2_STD_NTSC_M;
+ dev->height = 240;
+ dev->width = 320;
+ dev->depth = 15;
+ dev->brightness = 139;
+ dev->contrast = 70;
+ dev->hue = 0;
+ dev->saturation = 64;
+ pms_swsense(dev, 75);
+ pms_resolution(dev, 320, 240);
+ pms_videosource(dev, 0);
+ pms_vcrinput(dev, 0);
+ if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+ v4l2_device_unregister(&dev->v4l2_dev);
+ release_region(dev->io, 3);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
+ return -EINVAL;
+ }
+ return 0;
}
-static void __exit cleanup_pms_module(void)
+static void __exit pms_exit(void)
{
- shutdown_mediavision();
- video_unregister_device((struct video_device *)&pms_device);
- iounmap(mem);
-}
+ struct pms *dev = &pms_card;
-module_init(init_pms_cards);
-module_exit(cleanup_pms_module);
+ video_unregister_device(&dev->vdev);
+ release_region(dev->io, 3);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
+}
+module_init(pms_init);
+module_exit(pms_exit);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
index fbe3856bdca..ae977668c49 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -142,6 +142,9 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
{
int bcnt = 0;
int ccnt;
+ ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n",
+ pvr2_hdw_get_desc(hdw));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
ccnt = scnprintf(buf,acnt,"Driver state info:\n");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
@@ -249,11 +252,15 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
if (scnt && wptr) {
count -= scnt; buf += scnt;
- if (debugifc_match_keyword(wptr,wlen,"prom")) {
- pvr2_hdw_cpufw_set_enabled(hdw,!0,!0);
- } else if (debugifc_match_keyword(wptr,wlen,
- "ram")) {
- pvr2_hdw_cpufw_set_enabled(hdw,0,!0);
+ if (debugifc_match_keyword(wptr, wlen,
+ "prom")) {
+ pvr2_hdw_cpufw_set_enabled(hdw, 2, !0);
+ } else if (debugifc_match_keyword(wptr, wlen,
+ "ram8k")) {
+ pvr2_hdw_cpufw_set_enabled(hdw, 0, !0);
+ } else if (debugifc_match_keyword(wptr, wlen,
+ "ram16k")) {
+ pvr2_hdw_cpufw_set_enabled(hdw, 1, !0);
} else {
return -EINVAL;
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
index e4d7c13cab8..6bc16c13cce 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -58,7 +58,7 @@ static const char *pvr2_fw1_names_29xxx[] = {
};
static const struct pvr2_device_desc pvr2_device_29xxx = {
- .description = "WinTV PVR USB2 Model Category 29xxx",
+ .description = "WinTV PVR USB2 Model 29xxx",
.shortname = "29xxx",
.client_table.lst = pvr2_cli_29xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx),
@@ -91,7 +91,7 @@ static const char *pvr2_fw1_names_24xxx[] = {
};
static const struct pvr2_device_desc pvr2_device_24xxx = {
- .description = "WinTV PVR USB2 Model Category 24xxx",
+ .description = "WinTV PVR USB2 Model 24xxx",
.shortname = "24xxx",
.client_table.lst = pvr2_cli_24xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx),
@@ -340,7 +340,7 @@ static const char *pvr2_fw1_names_73xxx[] = {
};
static const struct pvr2_device_desc pvr2_device_73xxx = {
- .description = "WinTV HVR-1900 Model Category 73xxx",
+ .description = "WinTV HVR-1900 Model 73xxx",
.shortname = "73xxx",
.client_table.lst = pvr2_cli_73xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
@@ -351,6 +351,7 @@ static const struct pvr2_device_desc pvr2_device_73xxx = {
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
@@ -445,7 +446,7 @@ static const char *pvr2_fw1_names_75xxx[] = {
};
static const struct pvr2_device_desc pvr2_device_750xx = {
- .description = "WinTV HVR-1950 Model Category 750xx",
+ .description = "WinTV HVR-1950 Model 750xx",
.shortname = "750xx",
.client_table.lst = pvr2_cli_73xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
@@ -456,6 +457,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = {
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.default_std_mask = V4L2_STD_NTSC_M,
@@ -467,7 +469,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = {
};
static const struct pvr2_device_desc pvr2_device_751xx = {
- .description = "WinTV HVR-1950 Model Category 751xx",
+ .description = "WinTV HVR-1950 Model 751xx",
.shortname = "751xx",
.client_table.lst = pvr2_cli_73xxx,
.client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
@@ -478,6 +480,7 @@ static const struct pvr2_device_desc pvr2_device_751xx = {
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.default_std_mask = V4L2_STD_NTSC_M,
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
index ea04ecf8aa3..e5b9594eb5f 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
@@ -176,6 +176,7 @@ struct pvr2_device_desc {
unsigned int flag_has_analogtuner:1; /* Has analog tuner */
unsigned int flag_has_composite:1; /* Has composite input */
unsigned int flag_has_svideo:1; /* Has s-video input */
+ unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */
};
extern struct usb_device_id pvr2_device_table[];
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 54ac5349dee..e046fdaec5a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -294,7 +294,10 @@ static int pvr2_encoder_cmd(void *ctxt,
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Giving up on command."
- " This is normally recovered by the driver.");
+ " This is normally recovered via a firmware"
+ " reload and re-initialization; concern"
+ " is only warranted if this happens repeatedly"
+ " and rapidly.");
break;
}
wrData[0] = 0x7;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index 5b152ff20bd..de5485f506b 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -270,6 +270,7 @@ struct pvr2_hdw {
int force_dirty; /* consider all controls dirty if true */
int flag_ok; /* device in known good state */
+ int flag_modulefail; /* true if at least one module failed to load */
int flag_disconnected; /* flag_ok == 0 due to disconnect */
int flag_init_ok; /* true if structure is fully initialized */
int fw1_state; /* current situation with fw1 */
@@ -332,7 +333,7 @@ struct pvr2_hdw {
/* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */
unsigned int input_avail_mask;
- /* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */
+ /* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */
unsigned int input_allowed_mask;
/* Location of eeprom or a negative number if none */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 13639b30270..1bbdab08fe0 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -1447,6 +1447,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
const struct firmware *fw_entry = NULL;
void *fw_ptr;
unsigned int pipe;
+ unsigned int fwsize;
int ret;
u16 address;
@@ -1473,9 +1474,21 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+ fwsize = fw_entry->size;
- if (fw_entry->size != 0x2000){
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size");
+ if ((fwsize != 0x2000) &&
+ (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) {
+ if (hdw->hdw_desc->flag_fx2_16kb) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Wrong fx2 firmware size"
+ " (expected 8192 or 16384, got %u)",
+ fwsize);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Wrong fx2 firmware size"
+ " (expected 8192, got %u)",
+ fwsize);
+ }
release_firmware(fw_entry);
return -ENOMEM;
}
@@ -1493,7 +1506,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
chunk. */
ret = 0;
- for(address = 0; address < fw_entry->size; address += 0x800) {
+ for (address = 0; address < fwsize; address += 0x800) {
memcpy(fw_ptr, fw_entry->data + address, 0x800);
ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
0, fw_ptr, 0x800, HZ);
@@ -1509,8 +1522,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
trace_firmware("Upload done (%d bytes sent)",ret);
- /* We should have written 8192 bytes */
- if (ret == 8192) {
+ /* We should have written fwsize bytes */
+ if (ret == fwsize) {
hdw->fw1_state = FW1_STATE_RELOAD;
return 0;
}
@@ -2030,7 +2043,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
if (!fname) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Module ID %u for device %s has no name",
+ "Module ID %u for device %s has no name?"
+ " The driver might have a configuration problem.",
mid,
hdw->hdw_desc->description);
return -EINVAL;
@@ -2058,7 +2072,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
if (!i2ccnt) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Module ID %u (%s) for device %s:"
- " No i2c addresses",
+ " No i2c addresses."
+ " The driver might have a configuration problem.",
mid, fname, hdw->hdw_desc->description);
return -EINVAL;
}
@@ -2090,7 +2105,9 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
if (!sd) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Module ID %u (%s) for device %s failed to load",
+ "Module ID %u (%s) for device %s failed to load."
+ " Possible missing sub-device kernel module or"
+ " initialization failure within module.",
mid, fname, hdw->hdw_desc->description);
return -EIO;
}
@@ -2132,7 +2149,10 @@ static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
for (idx = 0; idx < ct->cnt; idx++) {
if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
}
- if (!okFl) pvr2_hdw_render_useless(hdw);
+ if (!okFl) {
+ hdw->flag_modulefail = !0;
+ pvr2_hdw_render_useless(hdw);
+ }
}
@@ -2334,6 +2354,20 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
break;
}
}
+ if (hdw->flag_modulefail) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** pvrusb2 driver initialization"
+ " failed due to the failure of one or more"
+ " sub-device kernel modules.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "You need to resolve the failing condition"
+ " before this driver can function. There"
+ " should be some earlier messages giving more"
+ " information about the problem.");
+ break;
+ }
if (procreload) {
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
@@ -2419,6 +2453,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
hdw,hdw_desc->description);
+ pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s",
+ hdw_desc->description);
if (!hdw) goto fail;
init_timer(&hdw->quiescent_timer);
@@ -3480,7 +3516,7 @@ static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
- int prom_flag,
+ int mode,
int enable_flag)
{
int ret;
@@ -3503,11 +3539,12 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
break;
}
- hdw->fw_cpu_flag = (prom_flag == 0);
+ hdw->fw_cpu_flag = (mode != 2);
if (hdw->fw_cpu_flag) {
+ hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000;
pvr2_trace(PVR2_TRACE_FIRMWARE,
- "Preparing to suck out CPU firmware");
- hdw->fw_size = 0x2000;
+ "Preparing to suck out CPU firmware"
+ " (size=%u)", hdw->fw_size);
hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
if (!hdw->fw_buffer) {
hdw->fw_size = 0;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 7b6940554e9..56e70eae20c 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -219,7 +219,7 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
this may prevent the device from running (and leaving this mode may
imply a device reset). */
void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *,
- int prom_flag,
+ int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */
int enable_flag);
/* Return true if we're in a mode for retrieval CPU firmware */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index a334b1a966a..7cbe18c4ca9 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -50,6 +50,7 @@ MODULE_PARM_DESC(disable_autoload_ir_video,
/* Mapping of IR schemes to known I2C addresses - if any */
static const unsigned char ir_video_addresses[] = {
+ [PVR2_IR_SCHEME_ZILOG] = 0x71,
[PVR2_IR_SCHEME_29XXX] = 0x18,
[PVR2_IR_SCHEME_24XXX] = 0x18,
};
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 2d8825e5b1b..6aa48e0ae73 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -913,6 +913,15 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
}
+static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
+{
+ if (!dip) return;
+ if (!dip->devbase.parent) return;
+ dip->devbase.parent = NULL;
+ device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
+}
+
+
static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
{
if (vp->dev_video) {
@@ -943,6 +952,8 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
struct pvr2_v4l2 *vp;
vp = container_of(chp,struct pvr2_v4l2,channel);
if (!vp->channel.mc_head->disconnect_flag) return;
+ pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
+ pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
if (vp->vfirst) return;
pvr2_v4l2_destroy_no_lock(vp);
}
@@ -1250,12 +1261,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
struct pvr2_v4l2 *vp,
int v4l_type)
{
+ struct usb_device *usbdev;
int mindevnum;
int unit_number;
int *nr_ptr = NULL;
dip->v4lp = vp;
-
+ usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw);
dip->v4l_type = v4l_type;
switch (v4l_type) {
case VFL_TYPE_GRABBER:
@@ -1296,6 +1308,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
mindevnum = nr_ptr[unit_number];
}
+ dip->devbase.parent = &usbdev->dev;
if ((video_register_device(&dip->devbase,
dip->v4l_type, mindevnum) < 0) &&
(video_register_device(&dip->devbase,
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index f976df452a3..89b620f6db7 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -68,6 +68,7 @@
#endif
#include <linux/vmalloc.h>
#include <asm/io.h>
+#include <linux/kernel.h> /* simple_strtol() */
#include "pwc.h"
#include "pwc-kiara.h"
@@ -1916,19 +1917,6 @@ disconnect_out:
unlock_kernel();
}
-/* *grunt* We have to do atoi ourselves :-( */
-static int pwc_atoi(const char *s)
-{
- int k = 0;
-
- k = 0;
- while (*s != '\0' && *s >= '0' && *s <= '9') {
- k = 10 * k + (*s - '0');
- s++;
- }
- return k;
-}
-
/*
* Initialization code & module stuff
@@ -2078,13 +2066,16 @@ static int __init usb_pwc_init(void)
}
else {
/* No type or serial number specified, just a number. */
- device_hint[i].device_node = pwc_atoi(s);
+ device_hint[i].device_node =
+ simple_strtol(s, NULL, 10);
}
}
else {
/* There's a colon, so we have at least a type and a device node */
- device_hint[i].type = pwc_atoi(s);
- device_hint[i].device_node = pwc_atoi(colon + 1);
+ device_hint[i].type =
+ simple_strtol(s, NULL, 10);
+ device_hint[i].device_node =
+ simple_strtol(colon + 1, NULL, 10);
if (*dot != '\0') {
/* There's a serial number as well */
int k;
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
new file mode 100644
index 00000000000..373f2a30a67
--- /dev/null
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -0,0 +1,1219 @@
+/*
+ * Driver for RJ54N1CB0C CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/soc_camera.h>
+
+#define RJ54N1_DEV_CODE 0x0400
+#define RJ54N1_DEV_CODE2 0x0401
+#define RJ54N1_OUT_SEL 0x0403
+#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404
+#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405
+#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406
+#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407
+#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408
+#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409
+#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a
+#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b
+#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c
+#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d
+#define RJ54N1_RESIZE_N 0x040e
+#define RJ54N1_RESIZE_N_STEP 0x040f
+#define RJ54N1_RESIZE_STEP 0x0410
+#define RJ54N1_RESIZE_HOLD_H 0x0411
+#define RJ54N1_RESIZE_HOLD_L 0x0412
+#define RJ54N1_H_OBEN_OFS 0x0413
+#define RJ54N1_V_OBEN_OFS 0x0414
+#define RJ54N1_RESIZE_CONTROL 0x0415
+#define RJ54N1_INC_USE_SEL_H 0x0425
+#define RJ54N1_INC_USE_SEL_L 0x0426
+#define RJ54N1_MIRROR_STILL_MODE 0x0427
+#define RJ54N1_INIT_START 0x0428
+#define RJ54N1_SCALE_1_2_LEV 0x0429
+#define RJ54N1_SCALE_4_LEV 0x042a
+#define RJ54N1_Y_GAIN 0x04d8
+#define RJ54N1_APT_GAIN_UP 0x04fa
+#define RJ54N1_RA_SEL_UL 0x0530
+#define RJ54N1_BYTE_SWAP 0x0531
+#define RJ54N1_OUT_SIGPO 0x053b
+#define RJ54N1_FRAME_LENGTH_S_H 0x0595
+#define RJ54N1_FRAME_LENGTH_S_L 0x0596
+#define RJ54N1_FRAME_LENGTH_P_H 0x0597
+#define RJ54N1_FRAME_LENGTH_P_L 0x0598
+#define RJ54N1_IOC 0x05ef
+#define RJ54N1_TG_BYPASS 0x0700
+#define RJ54N1_PLL_L 0x0701
+#define RJ54N1_PLL_N 0x0702
+#define RJ54N1_PLL_EN 0x0704
+#define RJ54N1_RATIO_TG 0x0706
+#define RJ54N1_RATIO_T 0x0707
+#define RJ54N1_RATIO_R 0x0708
+#define RJ54N1_RAMP_TGCLK_EN 0x0709
+#define RJ54N1_OCLK_DSP 0x0710
+#define RJ54N1_RATIO_OP 0x0711
+#define RJ54N1_RATIO_O 0x0712
+#define RJ54N1_OCLK_SEL_EN 0x0713
+#define RJ54N1_CLK_RST 0x0717
+#define RJ54N1_RESET_STANDBY 0x0718
+
+#define E_EXCLK (1 << 7)
+#define SOFT_STDBY (1 << 4)
+#define SEN_RSTX (1 << 2)
+#define TG_RSTX (1 << 1)
+#define DSP_RSTX (1 << 0)
+
+#define RESIZE_HOLD_SEL (1 << 2)
+#define RESIZE_GO (1 << 1)
+
+#define RJ54N1_COLUMN_SKIP 0
+#define RJ54N1_ROW_SKIP 0
+#define RJ54N1_MAX_WIDTH 1600
+#define RJ54N1_MAX_HEIGHT 1200
+
+/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
+
+static const struct soc_camera_data_format rj54n1_colour_formats[] = {
+ {
+ .name = "YUYV",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ }, {
+ .name = "RGB565",
+ .depth = 16,
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }
+};
+
+struct rj54n1_clock_div {
+ u8 ratio_tg;
+ u8 ratio_t;
+ u8 ratio_r;
+ u8 ratio_op;
+ u8 ratio_o;
+};
+
+struct rj54n1 {
+ struct v4l2_subdev subdev;
+ struct v4l2_rect rect; /* Sensor window */
+ unsigned short width; /* Output window */
+ unsigned short height;
+ unsigned short resize; /* Sensor * 1024 / resize = Output */
+ struct rj54n1_clock_div clk_div;
+ u32 fourcc;
+ unsigned short scale;
+ u8 bank;
+};
+
+struct rj54n1_reg_val {
+ u16 reg;
+ u8 val;
+};
+
+const static struct rj54n1_reg_val bank_4[] = {
+ {0x417, 0},
+ {0x42c, 0},
+ {0x42d, 0xf0},
+ {0x42e, 0},
+ {0x42f, 0x50},
+ {0x430, 0xf5},
+ {0x431, 0x16},
+ {0x432, 0x20},
+ {0x433, 0},
+ {0x434, 0xc8},
+ {0x43c, 8},
+ {0x43e, 0x90},
+ {0x445, 0x83},
+ {0x4ba, 0x58},
+ {0x4bb, 4},
+ {0x4bc, 0x20},
+ {0x4db, 4},
+ {0x4fe, 2},
+};
+
+const static struct rj54n1_reg_val bank_5[] = {
+ {0x514, 0},
+ {0x516, 0},
+ {0x518, 0},
+ {0x51a, 0},
+ {0x51d, 0xff},
+ {0x56f, 0x28},
+ {0x575, 0x40},
+ {0x5bc, 0x48},
+ {0x5c1, 6},
+ {0x5e5, 0x11},
+ {0x5e6, 0x43},
+ {0x5e7, 0x33},
+ {0x5e8, 0x21},
+ {0x5e9, 0x30},
+ {0x5ea, 0x0},
+ {0x5eb, 0xa5},
+ {0x5ec, 0xff},
+ {0x5fe, 2},
+};
+
+const static struct rj54n1_reg_val bank_7[] = {
+ {0x70a, 0},
+ {0x714, 0xff},
+ {0x715, 0xff},
+ {0x716, 0x1f},
+ {0x7FE, 0x02},
+};
+
+const static struct rj54n1_reg_val bank_8[] = {
+ {0x800, 0x00},
+ {0x801, 0x01},
+ {0x802, 0x61},
+ {0x805, 0x00},
+ {0x806, 0x00},
+ {0x807, 0x00},
+ {0x808, 0x00},
+ {0x809, 0x01},
+ {0x80A, 0x61},
+ {0x80B, 0x00},
+ {0x80C, 0x01},
+ {0x80D, 0x00},
+ {0x80E, 0x00},
+ {0x80F, 0x00},
+ {0x810, 0x00},
+ {0x811, 0x01},
+ {0x812, 0x61},
+ {0x813, 0x00},
+ {0x814, 0x11},
+ {0x815, 0x00},
+ {0x816, 0x41},
+ {0x817, 0x00},
+ {0x818, 0x51},
+ {0x819, 0x01},
+ {0x81A, 0x1F},
+ {0x81B, 0x00},
+ {0x81C, 0x01},
+ {0x81D, 0x00},
+ {0x81E, 0x11},
+ {0x81F, 0x00},
+ {0x820, 0x41},
+ {0x821, 0x00},
+ {0x822, 0x51},
+ {0x823, 0x00},
+ {0x824, 0x00},
+ {0x825, 0x00},
+ {0x826, 0x47},
+ {0x827, 0x01},
+ {0x828, 0x4F},
+ {0x829, 0x00},
+ {0x82A, 0x00},
+ {0x82B, 0x00},
+ {0x82C, 0x30},
+ {0x82D, 0x00},
+ {0x82E, 0x40},
+ {0x82F, 0x00},
+ {0x830, 0xB3},
+ {0x831, 0x00},
+ {0x832, 0xE3},
+ {0x833, 0x00},
+ {0x834, 0x00},
+ {0x835, 0x00},
+ {0x836, 0x00},
+ {0x837, 0x00},
+ {0x838, 0x00},
+ {0x839, 0x01},
+ {0x83A, 0x61},
+ {0x83B, 0x00},
+ {0x83C, 0x01},
+ {0x83D, 0x00},
+ {0x83E, 0x00},
+ {0x83F, 0x00},
+ {0x840, 0x00},
+ {0x841, 0x01},
+ {0x842, 0x61},
+ {0x843, 0x00},
+ {0x844, 0x1D},
+ {0x845, 0x00},
+ {0x846, 0x00},
+ {0x847, 0x00},
+ {0x848, 0x00},
+ {0x849, 0x01},
+ {0x84A, 0x1F},
+ {0x84B, 0x00},
+ {0x84C, 0x05},
+ {0x84D, 0x00},
+ {0x84E, 0x19},
+ {0x84F, 0x01},
+ {0x850, 0x21},
+ {0x851, 0x01},
+ {0x852, 0x5D},
+ {0x853, 0x00},
+ {0x854, 0x00},
+ {0x855, 0x00},
+ {0x856, 0x19},
+ {0x857, 0x01},
+ {0x858, 0x21},
+ {0x859, 0x00},
+ {0x85A, 0x00},
+ {0x85B, 0x00},
+ {0x85C, 0x00},
+ {0x85D, 0x00},
+ {0x85E, 0x00},
+ {0x85F, 0x00},
+ {0x860, 0xB3},
+ {0x861, 0x00},
+ {0x862, 0xE3},
+ {0x863, 0x00},
+ {0x864, 0x00},
+ {0x865, 0x00},
+ {0x866, 0x00},
+ {0x867, 0x00},
+ {0x868, 0x00},
+ {0x869, 0xE2},
+ {0x86A, 0x00},
+ {0x86B, 0x01},
+ {0x86C, 0x06},
+ {0x86D, 0x00},
+ {0x86E, 0x00},
+ {0x86F, 0x00},
+ {0x870, 0x60},
+ {0x871, 0x8C},
+ {0x872, 0x10},
+ {0x873, 0x00},
+ {0x874, 0xE0},
+ {0x875, 0x00},
+ {0x876, 0x27},
+ {0x877, 0x01},
+ {0x878, 0x00},
+ {0x879, 0x00},
+ {0x87A, 0x00},
+ {0x87B, 0x03},
+ {0x87C, 0x00},
+ {0x87D, 0x00},
+ {0x87E, 0x00},
+ {0x87F, 0x00},
+ {0x880, 0x00},
+ {0x881, 0x00},
+ {0x882, 0x00},
+ {0x883, 0x00},
+ {0x884, 0x00},
+ {0x885, 0x00},
+ {0x886, 0xF8},
+ {0x887, 0x00},
+ {0x888, 0x03},
+ {0x889, 0x00},
+ {0x88A, 0x64},
+ {0x88B, 0x00},
+ {0x88C, 0x03},
+ {0x88D, 0x00},
+ {0x88E, 0xB1},
+ {0x88F, 0x00},
+ {0x890, 0x03},
+ {0x891, 0x01},
+ {0x892, 0x1D},
+ {0x893, 0x00},
+ {0x894, 0x03},
+ {0x895, 0x01},
+ {0x896, 0x4B},
+ {0x897, 0x00},
+ {0x898, 0xE5},
+ {0x899, 0x00},
+ {0x89A, 0x01},
+ {0x89B, 0x00},
+ {0x89C, 0x01},
+ {0x89D, 0x04},
+ {0x89E, 0xC8},
+ {0x89F, 0x00},
+ {0x8A0, 0x01},
+ {0x8A1, 0x01},
+ {0x8A2, 0x61},
+ {0x8A3, 0x00},
+ {0x8A4, 0x01},
+ {0x8A5, 0x00},
+ {0x8A6, 0x00},
+ {0x8A7, 0x00},
+ {0x8A8, 0x00},
+ {0x8A9, 0x00},
+ {0x8AA, 0x7F},
+ {0x8AB, 0x03},
+ {0x8AC, 0x00},
+ {0x8AD, 0x00},
+ {0x8AE, 0x00},
+ {0x8AF, 0x00},
+ {0x8B0, 0x00},
+ {0x8B1, 0x00},
+ {0x8B6, 0x00},
+ {0x8B7, 0x01},
+ {0x8B8, 0x00},
+ {0x8B9, 0x00},
+ {0x8BA, 0x02},
+ {0x8BB, 0x00},
+ {0x8BC, 0xFF},
+ {0x8BD, 0x00},
+ {0x8FE, 0x02},
+};
+
+const static struct rj54n1_reg_val bank_10[] = {
+ {0x10bf, 0x69}
+};
+
+/* Clock dividers - these are default register values, divider = register + 1 */
+const static struct rj54n1_clock_div clk_div = {
+ .ratio_tg = 3 /* default: 5 */,
+ .ratio_t = 4 /* default: 1 */,
+ .ratio_r = 4 /* default: 0 */,
+ .ratio_op = 1 /* default: 5 */,
+ .ratio_o = 9 /* default: 0 */,
+};
+
+static struct rj54n1 *to_rj54n1(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct rj54n1, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u16 reg)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* set bank */
+ if (rj54n1->bank != reg >> 8) {
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+ if (ret < 0)
+ return ret;
+ rj54n1->bank = reg >> 8;
+ }
+ return i2c_smbus_read_byte_data(client, reg & 0xff);
+}
+
+static int reg_write(struct i2c_client *client, const u16 reg,
+ const u8 data)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* set bank */
+ if (rj54n1->bank != reg >> 8) {
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+ if (ret < 0)
+ return ret;
+ rj54n1->bank = reg >> 8;
+ }
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data);
+ return i2c_smbus_write_byte_data(client, reg & 0xff, data);
+}
+
+static int reg_set(struct i2c_client *client, const u16 reg,
+ const u8 data, const u8 mask)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, (ret & ~mask) | (data & mask));
+}
+
+static int reg_write_multiple(struct i2c_client *client,
+ const struct rj54n1_reg_val *rv, const int n)
+{
+ int i, ret;
+
+ for (i = 0; i < n; i++) {
+ ret = reg_write(client, rv->reg, rv->val);
+ if (ret < 0)
+ return ret;
+ rv++;
+ }
+
+ return 0;
+}
+
+static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ /* TODO: start / stop streaming */
+ return 0;
+}
+
+static int rj54n1_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct i2c_client *client = sd->priv;
+ /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
+
+ if (flags & SOCAM_PCLK_SAMPLE_RISING)
+ return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
+ else
+ return reg_write(client, RJ54N1_OUT_SIGPO, 0);
+}
+
+static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ const unsigned long flags =
+ SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
+ SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
+ SOCAM_DATA_ACTIVE_HIGH;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static int rj54n1_set_rect(struct i2c_client *client,
+ u16 reg_x, u16 reg_y, u16 reg_xy,
+ u32 width, u32 height)
+{
+ int ret;
+
+ ret = reg_write(client, reg_xy,
+ ((width >> 4) & 0x70) |
+ ((height >> 8) & 7));
+
+ if (!ret)
+ ret = reg_write(client, reg_x, width & 0xff);
+ if (!ret)
+ ret = reg_write(client, reg_y, height & 0xff);
+
+ return ret;
+}
+
+/*
+ * Some commands, specifically certain initialisation sequences, require
+ * a commit operation.
+ */
+static int rj54n1_commit(struct i2c_client *client)
+{
+ int ret = reg_write(client, RJ54N1_INIT_START, 1);
+ msleep(10);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_INIT_START, 0);
+ return ret;
+}
+
+static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+
+ a->c = rj54n1->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = RJ54N1_COLUMN_SKIP;
+ a->bounds.top = RJ54N1_ROW_SKIP;
+ a->bounds.width = RJ54N1_MAX_WIDTH;
+ a->bounds.height = RJ54N1_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int rj54n1_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ pix->pixelformat = rj54n1->fourcc;
+ pix->field = V4L2_FIELD_NONE;
+ pix->width = rj54n1->width;
+ pix->height = rj54n1->height;
+
+ return 0;
+}
+
+/*
+ * The actual geometry configuration routine. It scales the input window into
+ * the output one, updates the window sizes and returns an error or the resize
+ * coefficient on success. Note: we only use the "Fixed Scaling" on this camera.
+ */
+static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
+ u32 *out_w, u32 *out_h)
+{
+ struct i2c_client *client = sd->priv;
+ unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
+ output_w = *out_w, output_h = *out_h;
+ u16 inc_sel;
+ int ret;
+
+ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
+ RJ54N1_Y_OUTPUT_SIZE_S_L,
+ RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h);
+ if (!ret)
+ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L,
+ RJ54N1_Y_OUTPUT_SIZE_P_L,
+ RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h);
+
+ if (ret < 0)
+ return ret;
+
+ if (output_w > input_w || output_h > input_h) {
+ input_w = output_w;
+ input_h = output_h;
+
+ resize = 1024;
+ } else {
+ unsigned int resize_x, resize_y;
+ resize_x = input_w * 1024 / output_w;
+ resize_y = input_h * 1024 / output_h;
+
+ resize = min(resize_x, resize_y);
+
+ /* Prohibited value ranges */
+ switch (resize) {
+ case 2040 ... 2047:
+ resize = 2039;
+ break;
+ case 4080 ... 4095:
+ resize = 4079;
+ break;
+ case 8160 ... 8191:
+ resize = 8159;
+ break;
+ case 16320 ... 16383:
+ resize = 16319;
+ }
+
+ input_w = output_w * resize / 1024;
+ input_h = output_h * resize / 1024;
+ }
+
+ /* Set scaling */
+ ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8);
+
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Configure a skipping bitmask. The sensor will select a skipping value
+ * among set bits automatically.
+ */
+ skip = min(resize / 1024, (unsigned)15);
+ inc_sel = 1 << skip;
+
+ if (inc_sel <= 2)
+ inc_sel = 0xc;
+ else if (resize & 1023 && skip < 15)
+ inc_sel |= 1 << (skip + 1);
+
+ ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8);
+
+ /* Start resizing */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+ RESIZE_HOLD_SEL | RESIZE_GO | 1);
+
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "resize %u, skip %u\n", resize, skip);
+
+ /* Constant taken from manufacturer's example */
+ msleep(230);
+
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1);
+ if (ret < 0)
+ return ret;
+
+ *in_w = input_w;
+ *in_h = input_h;
+ *out_w = output_w;
+ *out_h = output_h;
+
+ return resize;
+}
+
+static int rj54n1_set_clock(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* Enable external clock */
+ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY);
+ /* Leave stand-by */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_L, 2);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_N, 0x31);
+
+ /* TGCLK dividers */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_TG,
+ rj54n1->clk_div.ratio_tg);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_T,
+ rj54n1->clk_div.ratio_t);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_R,
+ rj54n1->clk_div.ratio_r);
+
+ /* Enable TGCLK & RAMP */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3);
+
+ /* Disable clock output */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_DSP, 0);
+
+ /* Set divisors */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_OP,
+ rj54n1->clk_div.ratio_op);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_O,
+ rj54n1->clk_div.ratio_o);
+
+ /* Enable OCLK */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+ /* Use PLL for Timing Generator, write 2 to reserved bits */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_TG_BYPASS, 2);
+
+ /* Take sensor out of reset */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | SEN_RSTX);
+ /* Enable PLL */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_EN, 1);
+
+ /* Wait for PLL to stabilise */
+ msleep(10);
+
+ /* Enable clock to frequency divider */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_CLK_RST, 1);
+
+ if (!ret)
+ ret = reg_read(client, RJ54N1_CLK_RST);
+ if (ret != 1) {
+ dev_err(&client->dev,
+ "Resetting RJ54N1CB0C clock failed: %d!\n", ret);
+ return -EIO;
+ }
+ /* Start the PLL */
+ ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
+
+ /* Enable OCLK */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+ return ret;
+}
+
+static int rj54n1_reg_init(struct i2c_client *client)
+{
+ int ret = rj54n1_set_clock(client);
+
+ if (!ret)
+ ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7));
+ if (!ret)
+ ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10));
+
+ /* Set binning divisors */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4));
+ if (!ret)
+ ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf);
+
+ /* Switch to fixed resize mode */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+ RESIZE_HOLD_SEL | 1);
+
+ /* Set gain */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
+
+ /* Mirror the image back: default is upside down and left-to-right... */
+ if (!ret)
+ ret = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 3, 3);
+
+ if (!ret)
+ ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
+ if (!ret)
+ ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
+ if (!ret)
+ ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | DSP_RSTX | SEN_RSTX);
+
+ /* Commit init */
+ if (!ret)
+ ret = rj54n1_commit(client);
+
+ /* Take DSP, TG, sensor out of reset */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
+
+ if (!ret)
+ ret = reg_write(client, 0x7fe, 2);
+
+ /* Constant taken from manufacturer's example */
+ msleep(700);
+
+ return ret;
+}
+
+/* FIXME: streaming output only up to 800x600 is functional */
+static int rj54n1_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ pix->field = V4L2_FIELD_NONE;
+
+ if (pix->width > 800)
+ pix->width = 800;
+ if (pix->height > 600)
+ pix->height = 600;
+
+ return 0;
+}
+
+static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ unsigned int output_w, output_h,
+ input_w = rj54n1->rect.width, input_h = rj54n1->rect.height;
+ int ret;
+
+ /*
+ * The host driver can call us without .try_fmt(), so, we have to take
+ * care ourseleves
+ */
+ ret = rj54n1_try_fmt(sd, f);
+
+ /*
+ * Verify if the sensor has just been powered on. TODO: replace this
+ * with proper PM, when a suitable API is available.
+ */
+ if (!ret)
+ ret = reg_read(client, RJ54N1_RESET_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & E_EXCLK)) {
+ ret = rj54n1_reg_init(client);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Supported scales 1:1 - 1:16 */
+ if (pix->width < input_w / 16)
+ pix->width = input_w / 16;
+ if (pix->height < input_h / 16)
+ pix->height = input_h / 16;
+
+ output_w = pix->width;
+ output_h = pix->height;
+
+ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
+ if (ret < 0)
+ return ret;
+
+ rj54n1->fourcc = pix->pixelformat;
+ rj54n1->resize = ret;
+ rj54n1->rect.width = input_w;
+ rj54n1->rect.height = input_h;
+ rj54n1->width = output_w;
+ rj54n1->height = output_h;
+
+ pix->width = output_w;
+ pix->height = output_h;
+ pix->field = V4L2_FIELD_NONE;
+
+ return ret;
+}
+
+static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = sd->priv;
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_RJ54N1CB0C;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int rj54n1_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = sd->priv;
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+ reg->reg < 0x400 || reg->reg > 0x1fff)
+ /* Registers > 0x0800 are only available from Sharp support */
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->size = 1;
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xff)
+ return -EIO;
+
+ return 0;
+}
+
+static int rj54n1_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = sd->priv;
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+ reg->reg < 0x400 || reg->reg > 0x1fff)
+ /* Registers >= 0x0800 are only available from Sharp support */
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static const struct v4l2_queryctrl rj54n1_controls[] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Vertically",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Horizontally",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 66,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },
+};
+
+static struct soc_camera_ops rj54n1_ops = {
+ .set_bus_param = rj54n1_set_bus_param,
+ .query_bus_param = rj54n1_query_bus_param,
+ .controls = rj54n1_controls,
+ .num_controls = ARRAY_SIZE(rj54n1_controls),
+};
+
+static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct i2c_client *client = sd->priv;
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !(data & 1);
+ break;
+ case V4L2_CID_HFLIP:
+ data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !(data & 2);
+ break;
+ case V4L2_CID_GAIN:
+ data = reg_read(client, RJ54N1_Y_GAIN);
+ if (data < 0)
+ return -EIO;
+
+ ctrl->value = data / 2;
+ break;
+ }
+
+ return 0;
+}
+
+static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ int data;
+ struct i2c_client *client = sd->priv;
+ const struct v4l2_queryctrl *qctrl;
+
+ qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id);
+ if (!qctrl)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->value)
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
+ else
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
+ if (data < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_HFLIP:
+ if (ctrl->value)
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
+ else
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
+ if (data < 0)
+ return -EIO;
+ break;
+ case V4L2_CID_GAIN:
+ if (ctrl->value > qctrl->maximum ||
+ ctrl->value < qctrl->minimum)
+ return -EINVAL;
+ else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0)
+ return -EIO;
+ break;
+ }
+
+ return 0;
+}
+
+static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
+ .g_ctrl = rj54n1_g_ctrl,
+ .s_ctrl = rj54n1_s_ctrl,
+ .g_chip_ident = rj54n1_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = rj54n1_g_register,
+ .s_register = rj54n1_s_register,
+#endif
+};
+
+static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
+ .s_stream = rj54n1_s_stream,
+ .s_fmt = rj54n1_s_fmt,
+ .g_fmt = rj54n1_g_fmt,
+ .try_fmt = rj54n1_try_fmt,
+ .g_crop = rj54n1_g_crop,
+ .cropcap = rj54n1_cropcap,
+};
+
+static struct v4l2_subdev_ops rj54n1_subdev_ops = {
+ .core = &rj54n1_subdev_core_ops,
+ .video = &rj54n1_subdev_video_ops,
+};
+
+static int rj54n1_pin_config(struct i2c_client *client)
+{
+ /*
+ * Experimentally found out IOCTRL wired to 0. TODO: add to platform
+ * data: 0 or 1 << 7.
+ */
+ return reg_write(client, RJ54N1_IOC, 0);
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int rj54n1_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
+{
+ int data1, data2;
+ int ret;
+
+ /* This could be a BUG_ON() or a WARN_ON(), or remove it completely */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ /* Read out the chip version register */
+ data1 = reg_read(client, RJ54N1_DEV_CODE);
+ data2 = reg_read(client, RJ54N1_DEV_CODE2);
+
+ if (data1 != 0x51 || data2 != 0x10) {
+ ret = -ENODEV;
+ dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n",
+ data1, data2);
+ goto ei2c;
+ }
+
+ ret = rj54n1_pin_config(client);
+ if (ret < 0)
+ goto ei2c;
+
+ dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n",
+ data1, data2);
+
+ei2c:
+ return ret;
+}
+
+static int rj54n1_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct rj54n1 *rj54n1;
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl;
+ int ret;
+
+ if (!icd) {
+ dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
+ if (!icl) {
+ dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL);
+ if (!rj54n1)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
+
+ icd->ops = &rj54n1_ops;
+
+ rj54n1->clk_div = clk_div;
+ rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
+ rj54n1->rect.top = RJ54N1_ROW_SKIP;
+ rj54n1->rect.width = RJ54N1_MAX_WIDTH;
+ rj54n1->rect.height = RJ54N1_MAX_HEIGHT;
+ rj54n1->width = RJ54N1_MAX_WIDTH;
+ rj54n1->height = RJ54N1_MAX_HEIGHT;
+ rj54n1->fourcc = V4L2_PIX_FMT_YUYV;
+ rj54n1->resize = 1024;
+
+ ret = rj54n1_video_probe(icd, client);
+ if (ret < 0) {
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(rj54n1);
+ return ret;
+ }
+
+ icd->formats = rj54n1_colour_formats;
+ icd->num_formats = ARRAY_SIZE(rj54n1_colour_formats);
+
+ return ret;
+}
+
+static int rj54n1_remove(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+ icd->ops = NULL;
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ i2c_set_clientdata(client, NULL);
+ client->driver = NULL;
+ kfree(rj54n1);
+
+ return 0;
+}
+
+static const struct i2c_device_id rj54n1_id[] = {
+ { "rj54n1cb0c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rj54n1_id);
+
+static struct i2c_driver rj54n1_i2c_driver = {
+ .driver = {
+ .name = "rj54n1cb0c",
+ },
+ .probe = rj54n1_probe,
+ .remove = rj54n1_remove,
+ .id_table = rj54n1_id,
+};
+
+static int __init rj54n1_mod_init(void)
+{
+ return i2c_add_driver(&rj54n1_i2c_driver);
+}
+
+static void __exit rj54n1_mod_exit(void)
+{
+ i2c_del_driver(&rj54n1_i2c_driver);
+}
+
+module_init(rj54n1_mod_init);
+module_exit(rj54n1_mod_exit);
+
+MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 2c0bb06cab3..41765f3c7c2 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -1958,7 +1958,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
if (pdword[1] >= MAX_CHANNELS)
break;
cc = G_chnmap[pdword[1]];
- if (!(cc >= 0 && cc < MAX_CHANNELS))
+ if (cc >= MAX_CHANNELS)
break;
switch (pdword[2]) {
case S2255_RESPONSE_SETMODE:
@@ -1980,7 +1980,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
wake_up(&dev->fw_data->wait_fw);
break;
default:
- printk(KERN_INFO "s2255 unknwn resp\n");
+ printk(KERN_INFO "s2255 unknown resp\n");
}
default:
pdata++;
diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c
index 5c24c993ac1..3bca744e43a 100644
--- a/drivers/media/video/saa7110.c
+++ b/drivers/media/video/saa7110.c
@@ -304,7 +304,7 @@ static int saa7110_s_routing(struct v4l2_subdev *sd,
{
struct saa7110 *decoder = to_saa7110(sd);
- if (input < 0 || input >= SAA7110_MAX_INPUT) {
+ if (input >= SAA7110_MAX_INPUT) {
v4l2_dbg(1, debug, sd, "input=%d not available\n", input);
return -EINVAL;
}
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 09013229d4a..7e40d6d99dd 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -5239,6 +5239,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
.inputs = { {
.name = name_tv,
.vmux = 2,
@@ -5279,6 +5280,46 @@ struct saa7134_board saa7134_boards[] = {
.amux = TV,
},
},
+ [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = {
+ .name = "Asus Europa Hybrid OEM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TD1316,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ },
+ [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = {
+ .name = "Leadtek Winfast DTV1000S",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_comp1,
+ .vmux = 3,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ } },
+ },
};
@@ -6418,6 +6459,18 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x2004,
.driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI,
}, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1043,
+ .subdevice = 0x4847,
+ .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x107d,
+ .subdevice = 0x6655,
+ .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S,
+ }, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -6748,6 +6801,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
dev->has_remote = SAA7134_REMOTE_GPIO;
break;
case SAA7134_BOARD_FLYDVBS_LR300:
@@ -7079,6 +7133,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
/* break intentionally omitted */
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+ case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
{
/* The Philips EUROPA based hybrid boards have the tuner
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index f87757fccc7..0ba7f5af0fc 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -1032,7 +1032,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
saa7134_irq_video_signalchange(dev);
if (TUNER_ABSENT != dev->tuner_type)
- saa_call_all(dev, tuner, s_standby);
+ saa_call_all(dev, core, s_power, 0);
/* register v4l devices */
if (saa7134_no_overlay > 0)
@@ -1319,7 +1319,7 @@ static struct pci_driver saa7134_pci_driver = {
#endif
};
-static int saa7134_init(void)
+static int __init saa7134_init(void)
{
INIT_LIST_HEAD(&saa7134_devlist);
printk(KERN_INFO "saa7130/34: v4l2 driver version %d.%d.%d loaded\n",
@@ -1333,7 +1333,7 @@ static int saa7134_init(void)
return pci_register_driver(&saa7134_pci_driver);
}
-static void saa7134_fini(void)
+static void __exit saa7134_fini(void)
{
pci_unregister_driver(&saa7134_pci_driver);
}
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index a26e997a9ce..73739d2a63d 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -40,6 +40,7 @@
#include "tda1004x.h"
#include "nxt200x.h"
#include "tuner-xc2028.h"
+#include "xc5000.h"
#include "tda10086.h"
#include "tda826x.h"
@@ -871,6 +872,20 @@ static struct zl10353_config behold_h6_config = {
.disable_i2c_gate_ctrl = 1,
};
+static struct xc5000_config behold_x7_tunerconfig = {
+ .i2c_address = 0xc2>>1,
+ .if_khz = 4560,
+ .radio_input = XC5000_RADIO_FM1,
+};
+
+static struct zl10353_config behold_x7_config = {
+ .demod_address = 0x1e>>1,
+ .if2 = 45600,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
/* ==================================================================
* tda10086 based DVB-S cards, helper functions
*/
@@ -1030,6 +1045,32 @@ static struct tda18271_config zolid_tda18271_config = {
.gate = TDA18271_GATE_ANALOG,
};
+static struct tda10048_config dtv1000s_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3800,
+ .dtv8_if_freq_khz = TDA10048_IF_4300,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda18271_std_map dtv1000s_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config dtv1000s_tda18271_config = {
+ .std_map = &dtv1000s_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+};
+
/* ==================================================================
* Core code
*/
@@ -1116,6 +1157,7 @@ static int dvb_init(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_PHILIPS_EUROPA:
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
fe0->dvb.frontend = dvb_attach(tda10046_attach,
&philips_europa_config,
&dev->i2c_adap);
@@ -1482,6 +1524,15 @@ static int dvb_init(struct saa7134_dev *dev)
TUNER_PHILIPS_FMD1216MEX_MK3);
}
break;
+ case SAA7134_BOARD_BEHOLD_X7:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &behold_x7_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, &behold_x7_tunerconfig);
+ }
+ break;
case SAA7134_BOARD_AVERMEDIA_A700_PRO:
case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
/* Zarlink ZL10313 */
@@ -1518,6 +1569,19 @@ static int dvb_init(struct saa7134_dev *dev)
&zolid_tda18271_config);
}
break;
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &dtv1000s_tda10048_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &dtv1000s_tda18271_config);
+ }
+ break;
default:
wprintk("Huh? unknown DVB card?\n");
break;
@@ -1550,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev)
/* register everything else */
ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
- &dev->pci->dev, adapter_nr, 0);
+ &dev->pci->dev, adapter_nr, 0, NULL);
/* this sequence is necessary to make the tda1004x load its firmware
* and to enter analog mode of hybrid boards
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index a0e8c62e6ae..744918b1cd4 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -102,14 +102,14 @@ static int build_key(struct saa7134_dev *dev)
if (data == ir->mask_keycode)
ir_input_nokey(ir->dev, &ir->ir);
else
- ir_input_keydown(ir->dev, &ir->ir, data, data);
+ ir_input_keydown(ir->dev, &ir->ir, data);
return 0;
}
if (ir->polling) {
if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
- ir_input_keydown(ir->dev, &ir->ir, data, data);
+ ir_input_keydown(ir->dev, &ir->ir, data);
} else {
ir_input_nokey(ir->dev, &ir->ir);
}
@@ -117,7 +117,7 @@ static int build_key(struct saa7134_dev *dev)
else { /* IRQ driven mode - handle key press and release in one go */
if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
- ir_input_keydown(ir->dev, &ir->ir, data, data);
+ ir_input_keydown(ir->dev, &ir->ir, data);
ir_input_nokey(ir->dev, &ir->ir);
}
}
@@ -616,6 +616,12 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keycode = 0x003f00;
mask_keydown = 0x040000;
break;
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+ ir_codes = &ir_codes_winfast_table;
+ mask_keycode = 0x5f00;
+ mask_keyup = 0x020000;
+ polling = 50; /* ms */
+ break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
@@ -646,7 +652,10 @@ int saa7134_input_init1(struct saa7134_dev *dev)
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
pci_name(dev->pci));
- ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ if (err < 0)
+ goto err_out_free;
+
input_dev->name = ir->name;
input_dev->phys = ir->phys;
input_dev->id.bustype = BUS_PCI;
@@ -677,6 +686,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
saa7134_ir_stop(dev);
dev->remote = NULL;
err_out_free:
+ ir_input_free(input_dev);
input_free_device(input_dev);
kfree(ir);
return err;
@@ -688,6 +698,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
return;
saa7134_ir_stop(dev);
+ ir_input_free(dev->remote->dev);
input_unregister_device(dev->remote->dev);
kfree(dev->remote);
dev->remote = NULL;
@@ -695,10 +706,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
{
- const unsigned short addr_list[] = {
- 0x7a, 0x47, 0x71, 0x2d,
- I2C_CLIENT_END
- };
+ struct i2c_board_info info;
struct i2c_msg msg_msi = {
.addr = 0x50,
@@ -714,9 +722,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
return;
}
- memset(&dev->info, 0, sizeof(dev->info));
+ memset(&info, 0, sizeof(struct i2c_board_info));
memset(&dev->init_data, 0, sizeof(dev->init_data));
- strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE);
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
switch (dev->board) {
case SAA7134_BOARD_PINNACLE_PCTV_110i:
@@ -725,23 +733,24 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
if (pinnacle_remote == 0) {
dev->init_data.get_key = get_key_pinnacle_color;
dev->init_data.ir_codes = &ir_codes_pinnacle_color_table;
- dev->info.addr = 0x47;
+ info.addr = 0x47;
} else {
dev->init_data.get_key = get_key_pinnacle_grey;
dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table;
- dev->info.addr = 0x47;
+ info.addr = 0x47;
}
break;
case SAA7134_BOARD_UPMOST_PURPLE_TV:
dev->init_data.name = "Purple TV";
dev->init_data.get_key = get_key_purpletv;
dev->init_data.ir_codes = &ir_codes_purpletv_table;
+ info.addr = 0x7a;
break;
case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
dev->init_data.name = "MSI TV@nywhere Plus";
dev->init_data.get_key = get_key_msi_tvanywhere_plus;
dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table;
- dev->info.addr = 0x30;
+ info.addr = 0x30;
/* MSI TV@nywhere Plus controller doesn't seem to
respond to probes unless we read something from
an existing device. Weird...
@@ -755,6 +764,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
dev->init_data.name = "HVR 1110";
dev->init_data.get_key = get_key_hvr1110;
dev->init_data.ir_codes = &ir_codes_hauppauge_new_table;
+ info.addr = 0x71;
break;
case SAA7134_BOARD_BEHOLD_607FM_MK3:
case SAA7134_BOARD_BEHOLD_607FM_MK5:
@@ -772,23 +782,20 @@ 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 = &ir_codes_behold_table;
+ info.addr = 0x2d;
break;
case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
- dev->info.addr = 0x40;
+ info.addr = 0x40;
break;
- }
-
- if (dev->init_data.name)
- dev->info.platform_data = &dev->init_data;
- /* No need to probe if address is known */
- if (dev->info.addr) {
- i2c_new_device(&dev->i2c_adap, &dev->info);
+ default:
+ dprintk("No I2C IR support for board %x\n", dev->board);
return;
}
- /* Address not known, fallback to probing */
- i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list);
+ if (dev->init_data.name)
+ info.platform_data = &dev->init_data;
+ i2c_new_device(&dev->i2c_adap, &info);
}
static int saa7134_rc5_irq(struct saa7134_dev *dev)
@@ -936,7 +943,7 @@ static void nec_task(unsigned long data)
dprintk("scancode = 0x%02x (code = 0x%02x, notcode= 0x%02x)\n",
ir->code, ircode, not_code);
- ir_input_keydown(ir->dev, &ir->ir, ir->code, ir->code);
+ ir_input_keydown(ir->dev, &ir->ir, ir->code);
} else
dprintk("Repeat last key\n");
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index da26f476a30..35f8daa3a35 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -1499,7 +1499,7 @@ static int video_release(struct file *file)
saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
- saa_call_all(dev, tuner, s_standby);
+ saa_call_all(dev, core, s_power, 0);
if (fh->radio)
saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd);
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index f8697d46ff5..53b7e0b8a2f 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -297,6 +297,8 @@ struct saa7134_format {
#define SAA7134_BOARD_BEHOLD_X7 171
#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172
#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173
+#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174
+#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175
#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
@@ -592,7 +594,6 @@ struct saa7134_dev {
unsigned int insuspend;
/* I2C keyboard data */
- struct i2c_board_info info;
struct IR_i2c_init_data init_data;
/* SAA7134_MPEG_* */
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
index 709affc3104..e6aa0fbd1e9 100644
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -724,13 +724,13 @@ static struct pci_driver saa7164_pci_driver = {
.resume = NULL,
};
-static int saa7164_init(void)
+static int __init saa7164_init(void)
{
printk(KERN_INFO "saa7164 driver loaded\n");
return pci_register_driver(&saa7164_pci_driver);
}
-static void saa7164_fini(void)
+static void __exit saa7164_fini(void)
{
pci_unregister_driver(&saa7164_pci_driver);
}
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c
index 6a2d847d6a8..cf099c59b38 100644
--- a/drivers/media/video/saa7164/saa7164-dvb.c
+++ b/drivers/media/video/saa7164/saa7164-dvb.c
@@ -68,6 +68,7 @@ static struct tda18271_config hauppauge_hvr22x0s_tuner_config = {
.std_map = &hauppauge_tda18271_std_map,
.gate = TDA18271_GATE_ANALOG,
.role = TDA18271_SLAVE,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
.rf_cal_on_startup = 1
};
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
index b15c40908e8..6818df57116 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/video/saa717x.c
@@ -1115,7 +1115,7 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd,
v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input);
/* inputs from 0-9 are available*/
/* saa717x have mode0-mode9 but mode5 is reserved. */
- if (input < 0 || input > 9 || input == 5)
+ if (input > 9 || input == 5)
return -EINVAL;
if (decoder->input != input) {
@@ -1312,7 +1312,7 @@ static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
"MONO", "STEREO", "LANG1", "LANG2/SAP"
};
- audio_mode = V4L2_TUNER_MODE_STEREO;
+ audio_mode = TUNER_AUDIO_STEREO;
switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 9c8b7c7b89e..a4f3472d4db 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -153,6 +153,40 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
return ioread32(priv->base + reg_offs);
}
+static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
+{
+ int i, success = 0;
+ struct soc_camera_device *icd = pcdev->icd;
+
+ ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+
+ /* wait CSTSR.CPTON bit */
+ for (i = 0; i < 1000; i++) {
+ if (!(ceu_read(pcdev, CSTSR) & 1)) {
+ success++;
+ break;
+ }
+ udelay(1);
+ }
+
+ /* wait CAPSR.CPKIL bit */
+ for (i = 0; i < 1000; i++) {
+ if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) {
+ success++;
+ break;
+ }
+ udelay(1);
+ }
+
+
+ if (2 != success) {
+ dev_warn(&icd->dev, "soft reset time out\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
/*
* Videobuf operations
*/
@@ -202,26 +236,45 @@ static void free_buffer(struct videobuf_queue *vq,
#define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */
#define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */
#define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */
+#define CEU_CEIER_VBP (1 << 20) /* vbp error */
#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
+#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP)
-static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
+/*
+ * return value doesn't reflex the success/failure to queue the new buffer,
+ * but rather the status of the previous buffer.
+ */
+static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
{
struct soc_camera_device *icd = pcdev->icd;
dma_addr_t phys_addr_top, phys_addr_bottom;
+ u32 status;
+ int ret = 0;
/* The hardware is _very_ picky about this sequence. Especially
* the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
* several not-so-well documented interrupt sources in CETCR.
*/
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE);
- ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC);
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE);
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK);
+ status = ceu_read(pcdev, CETCR);
+ ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC);
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK);
ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
+ /*
+ * When a VBP interrupt occurs, a capture end interrupt does not occur
+ * and the image of that frame is not captured correctly. So, soft reset
+ * is needed here.
+ */
+ if (status & CEU_CEIER_VBP) {
+ sh_mobile_ceu_soft_reset(pcdev);
+ ret = -EIO;
+ }
+
if (!pcdev->active)
- return;
+ return ret;
phys_addr_top = videobuf_to_dma_contig(pcdev->active);
ceu_write(pcdev, CDAYR, phys_addr_top);
@@ -247,6 +300,8 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
pcdev->active->state = VIDEOBUF_ACTIVE;
ceu_write(pcdev, CAPSR, 0x1); /* start capture */
+
+ return ret;
}
static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
@@ -319,6 +374,11 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
list_add_tail(&vb->queue, &pcdev->capture);
if (!pcdev->active) {
+ /*
+ * Because there were no active buffer at this moment,
+ * we are not interested in the return value of
+ * sh_mobile_ceu_capture here.
+ */
pcdev->active = vb;
sh_mobile_ceu_capture(pcdev);
}
@@ -379,9 +439,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
else
pcdev->active = NULL;
- sh_mobile_ceu_capture(pcdev);
-
- vb->state = VIDEOBUF_DONE;
+ vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ?
+ VIDEOBUF_ERROR : VIDEOBUF_DONE;
do_gettimeofday(&vb->ts);
vb->field_count++;
wake_up(&vb->done);
@@ -407,13 +466,9 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
pm_runtime_get_sync(ici->v4l2_dev.dev);
- ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
- while (ceu_read(pcdev, CSTSR) & 1)
- msleep(1);
-
pcdev->icd = icd;
- return 0;
+ return sh_mobile_ceu_soft_reset(pcdev);
}
/* Called with .video_lock held */
@@ -427,7 +482,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
/* disable capture, disable interrupts */
ceu_write(pcdev, CEIER, 0);
- ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+ sh_mobile_ceu_soft_reset(pcdev);
/* make sure active buffer is canceled */
spin_lock_irqsave(&pcdev->lock, flags);
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index aba92e2313d..5b3eaa16afd 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -320,6 +320,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
unsigned char buffer[4];
+ int tune_now = 1;
if (type == UNSET || type == TUNER_ABSENT) {
tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
@@ -328,7 +329,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->type = type;
/* prevent invalid config values */
- t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0;
+ t->config = new_config < 256 ? new_config : 0;
if (tuner_callback != NULL) {
tuner_dbg("defining GPIO callback\n");
t->fe.callback = tuner_callback;
@@ -404,6 +405,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
};
if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
goto attach_failed;
+ tune_now = 0;
break;
}
case TUNER_TDA9887:
@@ -419,6 +421,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
if (!dvb_attach(xc5000_attach,
&t->fe, t->i2c->adapter, &xc5000_cfg))
goto attach_failed;
+ tune_now = 0;
break;
}
case TUNER_NXP_TDA18271:
@@ -430,6 +433,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr,
t->i2c->adapter, &cfg))
goto attach_failed;
+ tune_now = 0;
break;
}
default:
@@ -458,12 +462,13 @@ static void set_type(struct i2c_client *c, unsigned int type,
if (t->mode_mask == T_UNINITIALIZED)
t->mode_mask = new_mode_mask;
- /* xc2028/3028 and xc5000 requires a firmware to be set-up later
+ /* Some tuners require more initialization setup before use,
+ such as firmware download or device calibration.
trying to set a frequency here will just fail
FIXME: better to move set_freq to the tuner code. This is needed
on analog tuners for PLL to properly work
*/
- if (t->type != TUNER_XC2028 && t->type != TUNER_XC5000)
+ if (tune_now)
set_freq(c, (V4L2_TUNER_RADIO == t->mode) ?
t->radio_freq : t->tv_freq);
@@ -752,14 +757,17 @@ static int tuner_s_radio(struct v4l2_subdev *sd)
return 0;
}
-static int tuner_s_standby(struct v4l2_subdev *sd)
+static int tuner_s_power(struct v4l2_subdev *sd, int on)
{
struct tuner *t = to_tuner(sd);
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ if (on)
+ return 0;
+
tuner_dbg("Putting tuner to sleep\n");
- if (check_mode(t, "s_standby") == -EINVAL)
+ if (check_mode(t, "s_power") == -EINVAL)
return 0;
t->mode = T_STANDBY;
if (analog_ops->standby)
@@ -961,6 +969,7 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
static const struct v4l2_subdev_core_ops tuner_core_ops = {
.log_status = tuner_log_status,
.s_std = tuner_s_std,
+ .s_power = tuner_s_power,
};
static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
@@ -971,7 +980,6 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
.g_frequency = tuner_g_frequency,
.s_type_addr = tuner_s_type_addr,
.s_config = tuner_s_config,
- .s_standby = tuner_s_standby,
};
static const struct v4l2_subdev_ops tuner_ops = {
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index 0869bafc2b5..800fc1b111e 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -1919,7 +1919,7 @@ static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
.s_radio = tvaudio_s_radio,
.s_frequency = tvaudio_s_frequency,
.s_tuner = tvaudio_s_tuner,
- .s_tuner = tvaudio_g_tuner,
+ .g_tuner = tvaudio_g_tuner,
};
static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
index 244372627df..26b4e718cd6 100644
--- a/drivers/media/video/tvp514x.c
+++ b/drivers/media/video/tvp514x.c
@@ -272,7 +272,7 @@ static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg)
read_again:
err = i2c_smbus_read_byte_data(client, reg);
- if (err == -1) {
+ if (err < 0) {
if (retry <= I2C_RETRY_COUNT) {
v4l2_warn(sd, "Read: retry ... %d\n", retry);
retry++;
diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c
index 31d57f2d09e..a0addcb0429 100644
--- a/drivers/media/video/usbvideo/konicawc.c
+++ b/drivers/media/video/usbvideo/konicawc.c
@@ -225,7 +225,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev
int error;
usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
- strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
+ strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname));
cam->input = input_dev = input_allocate_device();
if (!input_dev) {
diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c
index 803d3e4e29a..c4d1b96b5ce 100644
--- a/drivers/media/video/usbvideo/quickcam_messenger.c
+++ b/drivers/media/video/usbvideo/quickcam_messenger.c
@@ -89,7 +89,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
int error;
usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
- strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
+ strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname));
cam->input = input_dev = input_allocate_device();
if (!input_dev) {
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index a2a50d608a3..c07b0ac452a 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -601,7 +601,7 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input)
{
struct usb_usbvision *usbvision = video_drvdata(file);
- if ((input >= usbvision->video_inputs) || (input < 0) )
+ if (input >= usbvision->video_inputs)
return -EINVAL;
mutex_lock(&usbvision->lock);
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index 1b89735e62f..0469d7a876a 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -742,17 +742,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
v4l2_id &= V4L2_CTRL_ID_MASK;
/* Find the control. */
- __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next);
- if (ctrl && !next)
- return ctrl;
-
- list_for_each_entry(entity, &chain->iterms, chain) {
- __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
- if (ctrl && !next)
- return ctrl;
- }
-
- list_for_each_entry(entity, &chain->extensions, chain) {
+ list_for_each_entry(entity, &chain->entities, chain) {
__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
@@ -826,6 +816,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
ret = 0;
goto out;
+ case V4L2_CTRL_TYPE_BUTTON:
+ v4l2_ctrl->minimum = 0;
+ v4l2_ctrl->maximum = 0;
+ v4l2_ctrl->step = 0;
+ ret = 0;
+ goto out;
+
default:
break;
}
@@ -944,17 +941,7 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
int ret = 0;
/* Find the control. */
- ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback);
- if (ret < 0)
- goto done;
-
- list_for_each_entry(entity, &chain->iterms, chain) {
- ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
- if (ret < 0)
- goto done;
- }
-
- list_for_each_entry(entity, &chain->extensions, chain) {
+ list_for_each_entry(entity, &chain->entities, chain) {
ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0)
goto done;
@@ -1068,8 +1055,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
int ret;
/* Find the extension unit. */
- list_for_each_entry(entity, &chain->extensions, chain) {
- if (entity->id == xctrl->unit)
+ list_for_each_entry(entity, &chain->entities, chain) {
+ if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
+ entity->id == xctrl->unit)
break;
}
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 8756be56915..c31bc50113b 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -46,6 +46,7 @@
unsigned int uvc_no_drop_param;
static unsigned int uvc_quirks_param;
unsigned int uvc_trace_param;
+unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
/* ------------------------------------------------------------------------
* Video formats
@@ -248,29 +249,9 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
entity = list_entry(&dev->entities, struct uvc_entity, list);
list_for_each_entry_continue(entity, &dev->entities, list) {
- switch (UVC_ENTITY_TYPE(entity)) {
- case UVC_TT_STREAMING:
- if (entity->output.bSourceID == id)
- return entity;
- break;
-
- case UVC_VC_PROCESSING_UNIT:
- if (entity->processing.bSourceID == id)
+ for (i = 0; i < entity->bNrInPins; ++i)
+ if (entity->baSourceID[i] == id)
return entity;
- break;
-
- case UVC_VC_SELECTOR_UNIT:
- for (i = 0; i < entity->selector.bNrInPins; ++i)
- if (entity->selector.baSourceID[i] == id)
- return entity;
- break;
-
- case UVC_VC_EXTENSION_UNIT:
- for (i = 0; i < entity->extension.bNrInPins; ++i)
- if (entity->extension.baSourceID[i] == id)
- return entity;
- break;
- }
}
return NULL;
@@ -426,7 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev,
/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
* based formats have frame descriptors.
*/
- while (buflen > 2 && buffer[2] == ftype) {
+ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == ftype) {
frame = &format->frame[format->nframes];
if (ftype != UVC_VS_FRAME_FRAME_BASED)
n = buflen > 25 ? buffer[25] : 0;
@@ -503,12 +485,14 @@ static int uvc_parse_format(struct uvc_device *dev,
buffer += buffer[0];
}
- if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
+ if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
buflen -= buffer[0];
buffer += buffer[0];
}
- if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) {
+ if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == UVC_VS_COLORFORMAT) {
if (buflen < 6) {
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
"interface %d COLORFORMAT error\n",
@@ -749,6 +733,11 @@ static int uvc_parse_streaming(struct uvc_device *dev,
buffer += buffer[0];
}
+ if (buflen)
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d has %u bytes of trailing descriptor garbage.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
+
/* Parse the alternate settings to find the maximum bandwidth. */
for (i = 0; i < intf->num_altsetting; ++i) {
struct usb_host_endpoint *ep;
@@ -776,6 +765,28 @@ error:
return ret;
}
+static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
+ unsigned int num_pads, unsigned int extra_size)
+{
+ struct uvc_entity *entity;
+ unsigned int num_inputs;
+ unsigned int size;
+
+ num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
+ size = sizeof(*entity) + extra_size + num_inputs;
+ entity = kzalloc(size, GFP_KERNEL);
+ if (entity == NULL)
+ return NULL;
+
+ entity->id = id;
+ entity->type = type;
+
+ entity->bNrInPins = num_inputs;
+ entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size;
+
+ return entity;
+}
+
/* Parse vendor-specific extensions. */
static int uvc_parse_vendor_control(struct uvc_device *dev,
const unsigned char *buffer, int buflen)
@@ -827,21 +838,18 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
break;
}
- unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL);
+ unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
+ p + 1, 2*n);
if (unit == NULL)
return -ENOMEM;
- unit->id = buffer[3];
- unit->type = UVC_VC_EXTENSION_UNIT;
memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
- unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
- unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
- memcpy(unit->extension.baSourceID, &buffer[22], p);
+ memcpy(unit->baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
- unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
- unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit
- + p + n;
+ unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
+ unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
+ + n;
memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
if (buffer[24+p+2*n] != 0)
@@ -938,13 +946,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return -EINVAL;
}
- term = kzalloc(sizeof *term + n + p, GFP_KERNEL);
+ term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
+ 1, n + p);
if (term == NULL)
return -ENOMEM;
- term->id = buffer[3];
- term->type = type | UVC_TERM_INPUT;
-
if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
term->camera.bControlSize = n;
term->camera.bmControls = (__u8 *)term + sizeof *term;
@@ -999,13 +1005,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return 0;
}
- term = kzalloc(sizeof *term, GFP_KERNEL);
+ term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
+ 1, 0);
if (term == NULL)
return -ENOMEM;
- term->id = buffer[3];
- term->type = type | UVC_TERM_OUTPUT;
- term->output.bSourceID = buffer[7];
+ memcpy(term->baSourceID, &buffer[7], 1);
if (buffer[8] != 0)
usb_string(udev, buffer[8], term->name,
@@ -1026,15 +1031,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return -EINVAL;
}
- unit = kzalloc(sizeof *unit + p, GFP_KERNEL);
+ unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
if (unit == NULL)
return -ENOMEM;
- unit->id = buffer[3];
- unit->type = buffer[2];
- unit->selector.bNrInPins = buffer[4];
- unit->selector.baSourceID = (__u8 *)unit + sizeof *unit;
- memcpy(unit->selector.baSourceID, &buffer[5], p);
+ memcpy(unit->baSourceID, &buffer[5], p);
if (buffer[5+p] != 0)
usb_string(udev, buffer[5+p], unit->name,
@@ -1056,13 +1057,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return -EINVAL;
}
- unit = kzalloc(sizeof *unit + n, GFP_KERNEL);
+ unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
if (unit == NULL)
return -ENOMEM;
- unit->id = buffer[3];
- unit->type = buffer[2];
- unit->processing.bSourceID = buffer[4];
+ memcpy(unit->baSourceID, &buffer[4], 1);
unit->processing.wMaxMultiplier =
get_unaligned_le16(&buffer[5]);
unit->processing.bControlSize = buffer[7];
@@ -1091,19 +1090,15 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
return -EINVAL;
}
- unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL);
+ unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
if (unit == NULL)
return -ENOMEM;
- unit->id = buffer[3];
- unit->type = buffer[2];
memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
unit->extension.bNumControls = buffer[20];
- unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
- unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
- memcpy(unit->extension.baSourceID, &buffer[22], p);
+ memcpy(unit->baSourceID, &buffer[22], p);
unit->extension.bControlSize = buffer[22+p];
- unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
+ unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
memcpy(unit->extension.bmControls, &buffer[23+p], n);
if (buffer[23+p+n] != 0)
@@ -1209,13 +1204,12 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- XU %d", entity->id);
- if (entity->extension.bNrInPins != 1) {
+ if (entity->bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
"than 1 input pin.\n", entity->id);
return -1;
}
- list_add_tail(&entity->chain, &chain->extensions);
break;
case UVC_VC_PROCESSING_UNIT:
@@ -1236,7 +1230,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
printk(" <- SU %d", entity->id);
/* Single-input selector units are ignored. */
- if (entity->selector.bNrInPins == 1)
+ if (entity->bNrInPins == 1)
break;
if (chain->selector != NULL) {
@@ -1254,20 +1248,17 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id);
- list_add_tail(&entity->chain, &chain->iterms);
break;
case UVC_TT_STREAMING:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" <- IT %d\n", entity->id);
-
- if (!UVC_ENTITY_IS_ITERM(entity)) {
- uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
- "terminal %u.\n", entity->id);
- return -1;
+ if (UVC_ENTITY_IS_ITERM(entity)) {
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+ } else {
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" OT %d", entity->id);
}
- list_add_tail(&entity->chain, &chain->iterms);
break;
default:
@@ -1276,6 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
return -1;
}
+ list_add_tail(&entity->chain, &chain->entities);
return 0;
}
@@ -1299,14 +1291,14 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
switch (UVC_ENTITY_TYPE(forward)) {
case UVC_VC_EXTENSION_UNIT:
- if (forward->extension.bNrInPins != 1) {
+ if (forward->bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
"has more than 1 input pin.\n",
entity->id);
return -EINVAL;
}
- list_add_tail(&forward->chain, &chain->extensions);
+ list_add_tail(&forward->chain, &chain->entities);
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
@@ -1326,7 +1318,7 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
return -EINVAL;
}
- list_add_tail(&forward->chain, &chain->oterms);
+ list_add_tail(&forward->chain, &chain->entities);
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
@@ -1344,24 +1336,22 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
}
static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
- struct uvc_entity *entity)
+ struct uvc_entity **_entity)
{
+ struct uvc_entity *entity = *_entity;
struct uvc_entity *term;
- int id = -1, i;
+ int id = -EINVAL, i;
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_EXTENSION_UNIT:
- id = entity->extension.baSourceID[0];
- break;
-
case UVC_VC_PROCESSING_UNIT:
- id = entity->processing.bSourceID;
+ id = entity->baSourceID[0];
break;
case UVC_VC_SELECTOR_UNIT:
/* Single-input selector units are ignored. */
- if (entity->selector.bNrInPins == 1) {
- id = entity->selector.baSourceID[0];
+ if (entity->bNrInPins == 1) {
+ id = entity->baSourceID[0];
break;
}
@@ -1369,8 +1359,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
printk(" <- IT");
chain->selector = entity;
- for (i = 0; i < entity->selector.bNrInPins; ++i) {
- id = entity->selector.baSourceID[i];
+ for (i = 0; i < entity->bNrInPins; ++i) {
+ id = entity->baSourceID[i];
term = uvc_entity_by_id(chain->dev, id);
if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
@@ -1382,7 +1372,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" %d", term->id);
- list_add_tail(&term->chain, &chain->iterms);
+ list_add_tail(&term->chain, &chain->entities);
uvc_scan_chain_forward(chain, term, entity);
}
@@ -1391,34 +1381,49 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
id = 0;
break;
+
+ case UVC_ITT_VENDOR_SPECIFIC:
+ case UVC_ITT_CAMERA:
+ case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ case UVC_TT_STREAMING:
+ id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
+ break;
+ }
+
+ if (id <= 0) {
+ *_entity = NULL;
+ return id;
}
- return id;
+ entity = uvc_entity_by_id(chain->dev, id);
+ if (entity == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "unknown entity %d.\n", id);
+ return -EINVAL;
+ }
+
+ *_entity = entity;
+ return 0;
}
static int uvc_scan_chain(struct uvc_video_chain *chain,
- struct uvc_entity *oterm)
+ struct uvc_entity *term)
{
struct uvc_entity *entity, *prev;
- int id;
- entity = oterm;
- list_add_tail(&entity->chain, &chain->oterms);
- uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+ uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
- id = entity->output.bSourceID;
- while (id != 0) {
- prev = entity;
- entity = uvc_entity_by_id(chain->dev, id);
- if (entity == NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "unknown entity %d.\n", id);
- return -EINVAL;
- }
+ entity = term;
+ prev = NULL;
+ while (entity != NULL) {
+ /* Entity must not be part of an existing chain */
if (entity->chain.next || entity->chain.prev) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "entity %d already in chain.\n", id);
+ "entity %d already in chain.\n", entity->id);
return -EINVAL;
}
@@ -1430,34 +1435,34 @@ static int uvc_scan_chain(struct uvc_video_chain *chain,
if (uvc_scan_chain_forward(chain, entity, prev) < 0)
return -EINVAL;
- /* Stop when a terminal is found. */
- if (UVC_ENTITY_IS_TERM(entity))
- break;
-
/* Backward scan */
- id = uvc_scan_chain_backward(chain, entity);
- if (id < 0)
- return id;
+ prev = entity;
+ if (uvc_scan_chain_backward(chain, &entity) < 0)
+ return -EINVAL;
}
return 0;
}
-static unsigned int uvc_print_terms(struct list_head *terms, char *buffer)
+static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
+ char *buffer)
{
struct uvc_entity *term;
unsigned int nterms = 0;
char *p = buffer;
list_for_each_entry(term, terms, chain) {
- p += sprintf(p, "%u", term->id);
- if (term->chain.next != terms) {
+ if (!UVC_ENTITY_IS_TERM(term) ||
+ UVC_TERM_DIRECTION(term) != dir)
+ continue;
+
+ if (nterms)
p += sprintf(p, ",");
- if (++nterms >= 4) {
- p += sprintf(p, "...");
- break;
- }
+ if (++nterms >= 4) {
+ p += sprintf(p, "...");
+ break;
}
+ p += sprintf(p, "%u", term->id);
}
return p - buffer;
@@ -1468,9 +1473,9 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain)
static char buffer[43];
char *p = buffer;
- p += uvc_print_terms(&chain->iterms, p);
+ p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
p += sprintf(p, " -> ");
- uvc_print_terms(&chain->oterms, p);
+ uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
return buffer;
}
@@ -1501,9 +1506,7 @@ static int uvc_scan_device(struct uvc_device *dev)
if (chain == NULL)
return -ENOMEM;
- INIT_LIST_HEAD(&chain->iterms);
- INIT_LIST_HEAD(&chain->oterms);
- INIT_LIST_HEAD(&chain->extensions);
+ INIT_LIST_HEAD(&chain->entities);
mutex_init(&chain->ctrl_mutex);
chain->dev = dev;
@@ -1531,22 +1534,92 @@ static int uvc_scan_device(struct uvc_device *dev)
*/
/*
+ * Delete the UVC device.
+ *
+ * Called by the kernel when the last reference to the uvc_device structure
+ * is released.
+ *
+ * As this function is called after or during disconnect(), all URBs have
+ * already been canceled by the USB core. There is no need to kill the
+ * interrupt URB manually.
+ */
+static void uvc_delete(struct uvc_device *dev)
+{
+ struct list_head *p, *n;
+
+ usb_put_intf(dev->intf);
+ usb_put_dev(dev->udev);
+
+ uvc_status_cleanup(dev);
+ uvc_ctrl_cleanup_device(dev);
+
+ list_for_each_safe(p, n, &dev->chains) {
+ struct uvc_video_chain *chain;
+ chain = list_entry(p, struct uvc_video_chain, list);
+ kfree(chain);
+ }
+
+ list_for_each_safe(p, n, &dev->entities) {
+ struct uvc_entity *entity;
+ entity = list_entry(p, struct uvc_entity, list);
+ kfree(entity);
+ }
+
+ list_for_each_safe(p, n, &dev->streams) {
+ struct uvc_streaming *streaming;
+ streaming = list_entry(p, struct uvc_streaming, list);
+ usb_driver_release_interface(&uvc_driver.driver,
+ streaming->intf);
+ usb_put_intf(streaming->intf);
+ kfree(streaming->format);
+ kfree(streaming->header.bmaControls);
+ kfree(streaming);
+ }
+
+ kfree(dev);
+}
+
+static void uvc_release(struct video_device *vdev)
+{
+ struct uvc_streaming *stream = video_get_drvdata(vdev);
+ struct uvc_device *dev = stream->dev;
+
+ video_device_release(vdev);
+
+ /* Decrement the registered streams count and delete the device when it
+ * reaches zero.
+ */
+ if (atomic_dec_and_test(&dev->nstreams))
+ uvc_delete(dev);
+}
+
+/*
* Unregister the video devices.
*/
static void uvc_unregister_video(struct uvc_device *dev)
{
struct uvc_streaming *stream;
+ /* Unregistering all video devices might result in uvc_delete() being
+ * called from inside the loop if there's no open file handle. To avoid
+ * that, increment the stream count before iterating over the streams
+ * and decrement it when done.
+ */
+ atomic_inc(&dev->nstreams);
+
list_for_each_entry(stream, &dev->streams, list) {
if (stream->vdev == NULL)
continue;
- if (stream->vdev->minor == -1)
- video_device_release(stream->vdev);
- else
- video_unregister_device(stream->vdev);
+ video_unregister_device(stream->vdev);
stream->vdev = NULL;
}
+
+ /* Decrement the stream count and call uvc_delete explicitly if there
+ * are no stream left.
+ */
+ if (atomic_dec_and_test(&dev->nstreams))
+ uvc_delete(dev);
}
static int uvc_register_video(struct uvc_device *dev,
@@ -1580,7 +1653,7 @@ static int uvc_register_video(struct uvc_device *dev,
vdev->parent = &dev->intf->dev;
vdev->minor = -1;
vdev->fops = &uvc_fops;
- vdev->release = video_device_release;
+ vdev->release = uvc_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
/* Set the driver data before calling video_register_device, otherwise
@@ -1598,6 +1671,7 @@ static int uvc_register_video(struct uvc_device *dev,
return ret;
}
+ atomic_inc(&dev->nstreams);
return 0;
}
@@ -1605,13 +1679,13 @@ static int uvc_register_video(struct uvc_device *dev,
* Register all video devices in all chains.
*/
static int uvc_register_terms(struct uvc_device *dev,
- struct uvc_video_chain *chain, struct list_head *terms)
+ struct uvc_video_chain *chain)
{
struct uvc_streaming *stream;
struct uvc_entity *term;
int ret;
- list_for_each_entry(term, terms, chain) {
+ list_for_each_entry(term, &chain->entities, chain) {
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
continue;
@@ -1637,11 +1711,7 @@ static int uvc_register_chains(struct uvc_device *dev)
int ret;
list_for_each_entry(chain, &dev->chains, list) {
- ret = uvc_register_terms(dev, chain, &chain->iterms);
- if (ret < 0)
- return ret;
-
- ret = uvc_register_terms(dev, chain, &chain->oterms);
+ ret = uvc_register_terms(dev, chain);
if (ret < 0)
return ret;
}
@@ -1653,61 +1723,6 @@ static int uvc_register_chains(struct uvc_device *dev)
* USB probe, disconnect, suspend and resume
*/
-/*
- * Delete the UVC device.
- *
- * Called by the kernel when the last reference to the uvc_device structure
- * is released.
- *
- * Unregistering the video devices is done here because every opened instance
- * must be closed before the device can be unregistered. An alternative would
- * have been to use another reference count for uvc_v4l2_open/uvc_release, and
- * unregister the video devices on disconnect when that reference count drops
- * to zero.
- *
- * As this function is called after or during disconnect(), all URBs have
- * already been canceled by the USB core. There is no need to kill the
- * interrupt URB manually.
- */
-void uvc_delete(struct kref *kref)
-{
- struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
- struct list_head *p, *n;
-
- /* Unregister the video devices. */
- uvc_unregister_video(dev);
- usb_put_intf(dev->intf);
- usb_put_dev(dev->udev);
-
- uvc_status_cleanup(dev);
- uvc_ctrl_cleanup_device(dev);
-
- list_for_each_safe(p, n, &dev->chains) {
- struct uvc_video_chain *chain;
- chain = list_entry(p, struct uvc_video_chain, list);
- kfree(chain);
- }
-
- list_for_each_safe(p, n, &dev->entities) {
- struct uvc_entity *entity;
- entity = list_entry(p, struct uvc_entity, list);
- kfree(entity);
- }
-
- list_for_each_safe(p, n, &dev->streams) {
- struct uvc_streaming *streaming;
- streaming = list_entry(p, struct uvc_streaming, list);
- usb_driver_release_interface(&uvc_driver.driver,
- streaming->intf);
- usb_put_intf(streaming->intf);
- kfree(streaming->format);
- kfree(streaming->header.bmaControls);
- kfree(streaming);
- }
-
- kfree(dev);
-}
-
static int uvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1730,7 +1745,7 @@ static int uvc_probe(struct usb_interface *intf,
INIT_LIST_HEAD(&dev->entities);
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams);
- kref_init(&dev->kref);
+ atomic_set(&dev->nstreams, 0);
atomic_set(&dev->users, 0);
dev->udev = usb_get_dev(udev);
@@ -1792,7 +1807,7 @@ static int uvc_probe(struct usb_interface *intf,
return 0;
error:
- kref_put(&dev->kref, uvc_delete);
+ uvc_unregister_video(dev);
return -ENODEV;
}
@@ -1809,21 +1824,9 @@ static void uvc_disconnect(struct usb_interface *intf)
UVC_SC_VIDEOSTREAMING)
return;
- /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide
- * lock is needed to prevent uvc_disconnect from releasing its
- * reference to the uvc_device instance after uvc_v4l2_open() received
- * the pointer to the device (video_devdata) but before it got the
- * chance to increase the reference count (kref_get).
- *
- * Note that the reference can't be released with the lock held,
- * otherwise a AB-BA deadlock can occur with videodev_lock that
- * videodev acquires in videodev_open() and video_unregister_device().
- */
- mutex_lock(&uvc_driver.open_mutex);
dev->state |= UVC_DEV_DISCONNECTED;
- mutex_unlock(&uvc_driver.open_mutex);
- kref_put(&dev->kref, uvc_delete);
+ uvc_unregister_video(dev);
}
static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
@@ -1899,6 +1902,15 @@ static int uvc_reset_resume(struct usb_interface *intf)
* though they are compliant.
*/
static struct usb_device_id uvc_ids[] = {
+ /* Genius eFace 2025 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0458,
+ .idProduct = 0x706e,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Microsoft Lifecam NX-6000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2123,6 +2135,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STATUS_INTERVAL },
+ /* MSI StarCam 370i */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1b3b,
+ .idProduct = 0x2951,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* SiGma Micro USB Web Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2159,7 +2180,6 @@ static int __init uvc_init(void)
INIT_LIST_HEAD(&uvc_driver.devices);
INIT_LIST_HEAD(&uvc_driver.controls);
- mutex_init(&uvc_driver.open_mutex);
mutex_init(&uvc_driver.ctrl_mutex);
uvc_ctrl_init();
@@ -2184,6 +2204,8 @@ module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(quirks, "Forced device quirks");
module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(trace, "Trace level bitmask");
+module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index a2bdd806efa..23239a4adef 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -364,37 +364,30 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
* unprivileged state. Only a single instance can be in a privileged state at
* a given time. Trying to perform an operation that requires privileges will
* automatically acquire the required privileges if possible, or return -EBUSY
- * otherwise. Privileges are dismissed when closing the instance.
+ * otherwise. Privileges are dismissed when closing the instance or when
+ * freeing the video buffers using VIDIOC_REQBUFS.
*
* Operations that require privileges are:
*
* - VIDIOC_S_INPUT
* - VIDIOC_S_PARM
* - VIDIOC_S_FMT
- * - VIDIOC_TRY_FMT
* - VIDIOC_REQBUFS
*/
static int uvc_acquire_privileges(struct uvc_fh *handle)
{
- int ret = 0;
-
/* Always succeed if the handle is already privileged. */
if (handle->state == UVC_HANDLE_ACTIVE)
return 0;
/* Check if the device already has a privileged handle. */
- mutex_lock(&uvc_driver.open_mutex);
if (atomic_inc_return(&handle->stream->active) != 1) {
atomic_dec(&handle->stream->active);
- ret = -EBUSY;
- goto done;
+ return -EBUSY;
}
handle->state = UVC_HANDLE_ACTIVE;
-
-done:
- mutex_unlock(&uvc_driver.open_mutex);
- return ret;
+ return 0;
}
static void uvc_dismiss_privileges(struct uvc_fh *handle)
@@ -421,24 +414,20 @@ static int uvc_v4l2_open(struct file *file)
int ret = 0;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
- mutex_lock(&uvc_driver.open_mutex);
stream = video_drvdata(file);
- if (stream->dev->state & UVC_DEV_DISCONNECTED) {
- ret = -ENODEV;
- goto done;
- }
+ if (stream->dev->state & UVC_DEV_DISCONNECTED)
+ return -ENODEV;
ret = usb_autopm_get_interface(stream->dev->intf);
if (ret < 0)
- goto done;
+ return ret;
/* Create the device handle. */
handle = kzalloc(sizeof *handle, GFP_KERNEL);
if (handle == NULL) {
usb_autopm_put_interface(stream->dev->intf);
- ret = -ENOMEM;
- goto done;
+ return -ENOMEM;
}
if (atomic_inc_return(&stream->dev->users) == 1) {
@@ -447,7 +436,7 @@ static int uvc_v4l2_open(struct file *file)
usb_autopm_put_interface(stream->dev->intf);
atomic_dec(&stream->dev->users);
kfree(handle);
- goto done;
+ return ret;
}
}
@@ -456,11 +445,7 @@ static int uvc_v4l2_open(struct file *file)
handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;
- kref_get(&stream->dev->kref);
-
-done:
- mutex_unlock(&uvc_driver.open_mutex);
- return ret;
+ return 0;
}
static int uvc_v4l2_release(struct file *file)
@@ -490,7 +475,6 @@ static int uvc_v4l2_release(struct file *file)
uvc_status_stop(stream->dev);
usb_autopm_put_interface(stream->dev->intf);
- kref_put(&stream->dev->kref, uvc_delete);
return 0;
}
@@ -636,12 +620,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (index != 0)
return -EINVAL;
- iterm = list_first_entry(&chain->iterms,
- struct uvc_entity, chain);
+ list_for_each_entry(iterm, &chain->entities, chain) {
+ if (UVC_ENTITY_IS_ITERM(iterm))
+ break;
+ }
pin = iterm->id;
- } else if (pin < selector->selector.bNrInPins) {
- pin = selector->selector.baSourceID[index];
- list_for_each_entry(iterm, chain->iterms.next, chain) {
+ } else if (pin < selector->bNrInPins) {
+ pin = selector->baSourceID[index];
+ list_for_each_entry(iterm, &chain->entities, chain) {
+ if (!UVC_ENTITY_IS_ITERM(iterm))
+ continue;
if (iterm->id == pin)
break;
}
@@ -692,7 +680,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
break;
}
- if (input == 0 || input > chain->selector->selector.bNrInPins)
+ if (input == 0 || input > chain->selector->bNrInPins)
return -EINVAL;
return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
@@ -731,9 +719,6 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct uvc_streaming_control probe;
- if ((ret = uvc_acquire_privileges(handle)) < 0)
- return ret;
-
return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
}
@@ -888,6 +873,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (ret < 0)
return ret;
+ if (ret == 0)
+ uvc_dismiss_privileges(handle);
+
rb->count = ret;
ret = 0;
break;
@@ -1051,7 +1039,7 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
- return -ENODEV;
+ return -EINVAL;
}
/*
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index a6e41d12b22..05139a4f14f 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -135,7 +135,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
- size, UVC_CTRL_STREAMING_TIMEOUT);
+ size, uvc_timeout_param);
if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) {
/* Some cameras, mostly based on Bison Electronics chipsets,
@@ -239,7 +239,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream,
ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum,
probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
- size, UVC_CTRL_STREAMING_TIMEOUT);
+ size, uvc_timeout_param);
if (ret != size) {
uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
"%d (exp. %u).\n", probe ? "probe" : "commit",
@@ -770,8 +770,9 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
/* Retry allocations until one succeed. */
for (; npackets > 1; npackets /= 2) {
for (i = 0; i < UVC_URBS; ++i) {
+ stream->urb_size = psize * npackets;
stream->urb_buffer[i] = usb_buffer_alloc(
- stream->dev->udev, psize * npackets,
+ stream->dev->udev, stream->urb_size,
gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
if (!stream->urb_buffer[i]) {
uvc_free_urb_buffers(stream);
@@ -780,11 +781,15 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
}
if (i == UVC_URBS) {
- stream->urb_size = psize * npackets;
+ uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
+ "of %ux%u bytes each.\n", UVC_URBS, npackets,
+ psize);
return npackets;
}
}
+ uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
+ "per packet).\n", psize);
return 0;
}
@@ -935,10 +940,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
if (bandwidth == 0) {
- uvc_printk(KERN_WARNING, "device %s requested null "
- "bandwidth, defaulting to lowest.\n",
- stream->dev->name);
+ uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
+ "bandwidth, defaulting to lowest.\n");
bandwidth = 1;
+ } else {
+ uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
+ "B/frame bandwidth.\n", bandwidth);
}
for (i = 0; i < intf->num_altsetting; ++i) {
@@ -955,8 +962,11 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
break;
}
- if (i >= intf->num_altsetting)
+ if (i >= intf->num_altsetting) {
+ uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
+ "for requested bandwidth.\n");
return -EIO;
+ }
ret = usb_set_interface(stream->dev->udev, intfnum, i);
if (ret < 0)
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index e7958aa454c..7ec9a04ced5 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -75,6 +75,7 @@ struct uvc_xu_control {
#define UVC_TERM_INPUT 0x0000
#define UVC_TERM_OUTPUT 0x8000
+#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000)
#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff)
#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
@@ -148,7 +149,7 @@ struct uvc_xu_control {
#define UVC_MAX_STATUS_SIZE 16
#define UVC_CTRL_CONTROL_TIMEOUT 300
-#define UVC_CTRL_STREAMING_TIMEOUT 1000
+#define UVC_CTRL_STREAMING_TIMEOUT 3000
/* Devices quirks */
#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
@@ -292,11 +293,9 @@ struct uvc_entity {
} media;
struct {
- __u8 bSourceID;
} output;
struct {
- __u8 bSourceID;
__u16 wMaxMultiplier;
__u8 bControlSize;
__u8 *bmControls;
@@ -304,21 +303,20 @@ struct uvc_entity {
} processing;
struct {
- __u8 bNrInPins;
- __u8 *baSourceID;
} selector;
struct {
__u8 guidExtensionCode[16];
__u8 bNumControls;
- __u8 bNrInPins;
- __u8 *baSourceID;
__u8 bControlSize;
__u8 *bmControls;
__u8 *bmControlsType;
} extension;
};
+ __u8 bNrInPins;
+ __u8 *baSourceID;
+
unsigned int ncontrols;
struct uvc_control *controls;
};
@@ -408,11 +406,9 @@ struct uvc_video_chain {
struct uvc_device *dev;
struct list_head list;
- struct list_head iterms; /* Input terminals */
- struct list_head oterms; /* Output terminals */
+ struct list_head entities; /* All entities */
struct uvc_entity *processing; /* Processing unit */
struct uvc_entity *selector; /* Selector unit */
- struct list_head extensions; /* Extension units */
struct mutex ctrl_mutex;
};
@@ -475,7 +471,6 @@ struct uvc_device {
char name[32];
enum uvc_device_state state;
- struct kref kref;
struct list_head list;
atomic_t users;
@@ -488,6 +483,7 @@ struct uvc_device {
/* Video Streaming interfaces */
struct list_head streams;
+ atomic_t nstreams;
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
@@ -511,8 +507,6 @@ struct uvc_fh {
struct uvc_driver {
struct usb_driver driver;
- struct mutex open_mutex; /* protects from open/disconnect race */
-
struct list_head devices; /* struct uvc_device list */
struct list_head controls; /* struct uvc_control_info list */
struct mutex ctrl_mutex; /* protects controls and devices
@@ -533,12 +527,14 @@ struct uvc_driver {
#define UVC_TRACE_FRAME (1 << 7)
#define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9)
+#define UVC_TRACE_VIDEO (1 << 10)
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
extern unsigned int uvc_no_drop_param;
extern unsigned int uvc_trace_param;
+extern unsigned int uvc_timeout_param;
#define uvc_trace(flag, msg...) \
do { \
@@ -571,7 +567,6 @@ extern unsigned int uvc_trace_param;
/* Core driver */
extern struct uvc_driver uvc_driver;
-extern void uvc_delete(struct kref *kref);
/* Video buffers queue management. */
extern void uvc_queue_init(struct uvc_video_queue *queue,
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index f5a93ae3cdf..e8e5affbabc 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -431,6 +431,8 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
case V4L2_CID_COLOR_KILLER: return "Color Killer";
case V4L2_CID_COLORFX: return "Color Effects";
+ case V4L2_CID_ROTATE: return "Rotate";
+ case V4L2_CID_BG_COLOR: return "Background color";
/* MPEG controls */
case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
@@ -587,6 +589,13 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
min = max = step = def = 0;
break;
+ case V4L2_CID_BG_COLOR:
+ qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+ step = 1;
+ min = 0;
+ /* Max is calculated as RGB888 that is 2^24 */
+ max = 0xFFFFFF;
+ break;
default:
qctrl->type = V4L2_CTRL_TYPE_INTEGER;
break;
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index 8e93c6f25c8..bb0a1c8de41 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc);
void videobuf_queue_core_init(struct videobuf_queue *q,
- struct videobuf_queue_ops *ops,
+ const struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
@@ -360,7 +360,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q,
q->bufs[i]->bsize = bsize;
switch (memory) {
case V4L2_MEMORY_MMAP:
- q->bufs[i]->boff = bsize * i;
+ q->bufs[i]->boff = PAGE_ALIGN(bsize) * i;
break;
case V4L2_MEMORY_USERPTR:
case V4L2_MEMORY_OVERLAY:
@@ -430,9 +430,9 @@ int videobuf_reqbufs(struct videobuf_queue *q,
count = VIDEO_MAX_FRAME;
size = 0;
q->ops->buf_setup(q, &count, &size);
- size = PAGE_ALIGN(size);
- dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n",
- count, size, (count*size)>>PAGE_SHIFT);
+ dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n",
+ count, size,
+ (unsigned int)((count*PAGE_ALIGN(size))>>PAGE_SHIFT) );
retval = __videobuf_mmap_setup(q, count, size, req->memory);
if (retval < 0) {
@@ -1099,7 +1099,7 @@ int videobuf_cgmbuf(struct videobuf_queue *q,
mbuf->size = 0;
for (i = 0; i < mbuf->frames; i++) {
mbuf->offsets[i] = q->bufs[i]->boff;
- mbuf->size += q->bufs[i]->bsize;
+ mbuf->size += PAGE_ALIGN(q->bufs[i]->bsize);
}
return 0;
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index c3065c4bcba..d25f28461da 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -429,7 +429,7 @@ static struct videobuf_qtype_ops qops = {
};
void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
- struct videobuf_queue_ops *ops,
+ const struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c
index 032ebae0134..fa78555b118 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/video/videobuf-dma-sg.c
@@ -588,7 +588,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
retval = -EBUSY;
goto done;
}
- size += q->bufs[last]->bsize;
+ size += PAGE_ALIGN(q->bufs[last]->bsize);
if (size == (vma->vm_end - vma->vm_start))
break;
}
@@ -610,7 +610,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
continue;
q->bufs[i]->map = map;
q->bufs[i]->baddr = vma->vm_start + size;
- size += q->bufs[i]->bsize;
+ size += PAGE_ALIGN(q->bufs[i]->bsize);
}
map->count = 1;
@@ -702,7 +702,7 @@ void *videobuf_sg_alloc(size_t size)
}
void videobuf_queue_sg_init(struct videobuf_queue* q,
- struct videobuf_queue_ops *ops,
+ const struct videobuf_queue_ops *ops,
struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c
index 0e7dcba8e4a..a56cf0d3a6d 100644
--- a/drivers/media/video/videobuf-dvb.c
+++ b/drivers/media/video/videobuf-dvb.c
@@ -139,7 +139,9 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
struct device *device,
char *adapter_name,
short *adapter_nr,
- int mfe_shared)
+ int mfe_shared,
+ int (*fe_ioctl_override)(struct dvb_frontend *,
+ unsigned int, void *, unsigned int))
{
int result;
@@ -154,6 +156,7 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
}
fe->adapter.priv = adapter_priv;
fe->adapter.mfe_shared = mfe_shared;
+ fe->adapter.fe_ioctl_override = fe_ioctl_override;
return result;
}
@@ -253,7 +256,9 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
void *adapter_priv,
struct device *device,
short *adapter_nr,
- int mfe_shared)
+ int mfe_shared,
+ int (*fe_ioctl_override)(struct dvb_frontend *,
+ unsigned int, void *, unsigned int))
{
struct list_head *list, *q;
struct videobuf_dvb_frontend *fe;
@@ -267,7 +272,7 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
/* Bring up the adapter */
res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
- fe->dvb.name, adapter_nr, mfe_shared);
+ fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override);
if (res < 0) {
printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
return res;
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c
index 35f3900c563..d6e6a28fb6b 100644
--- a/drivers/media/video/videobuf-vmalloc.c
+++ b/drivers/media/video/videobuf-vmalloc.c
@@ -391,8 +391,8 @@ static struct videobuf_qtype_ops qops = {
};
void videobuf_queue_vmalloc_init(struct videobuf_queue* q,
- struct videobuf_queue_ops *ops,
- void *dev,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index 97e0ce28ff1..33205d7537d 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -391,7 +391,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd,
{0x0e, 1}
};
- if (input < 0 || input > 2)
+ if (input > 2)
return -EINVAL;
v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h
index d439c76b27e..cb1de7ea197 100644
--- a/drivers/media/video/zoran/zoran.h
+++ b/drivers/media/video/zoran/zoran.h
@@ -106,7 +106,7 @@ struct zoran_params {
unsigned long jpeg_markers; /* Which markers should go into the JPEG output.
* Unless you exactly know what you do, leave them untouched.
* Inluding less markers will make the resulting code
- * smaller, but there will be fewer aplications
+ * smaller, but there will be fewer applications
* which can read it.
* The presence of the APP and COM marker is
* influenced by APP0_len and COM_len ONLY! */
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index 47137deafcf..e9f72ca458f 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -2764,7 +2764,7 @@ static int zoran_enum_input(struct file *file, void *__fh,
struct zoran_fh *fh = __fh;
struct zoran *zr = fh->zr;
- if (inp->index < 0 || inp->index >= zr->card.inputs)
+ if (inp->index >= zr->card.inputs)
return -EINVAL;
else {
int id = inp->index;
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index 9aae011d92a..2ef110b5221 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -115,6 +115,7 @@ static struct usb_device_id device_table[] = {
{USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 },
{USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 },
{USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 },
+ {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 },
{} /* Terminating entry */
};
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index 8dd4d219e43..b4948671eb9 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -76,8 +76,8 @@
#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif
-#define MPT_LINUX_VERSION_COMMON "3.04.12"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.12"
+#define MPT_LINUX_VERSION_COMMON "3.04.13"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.13"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
index 9b2e2198aee..352acd05c46 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -621,11 +621,8 @@ __mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
*/
iocnumX = khdr.iocnum & 0xFF;
if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
- (iocp == NULL)) {
- printk(KERN_DEBUG MYNAM "%s::mptctl_ioctl() @%d - ioc%d not found!\n",
- __FILE__, __LINE__, iocnumX);
+ (iocp == NULL))
return -ENODEV;
- }
if (!iocp->active) {
printk(KERN_DEBUG MYNAM "%s::mptctl_ioctl() @%d - Controller disabled.\n",
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index c2957861450..57752751712 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -792,11 +792,36 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
* precedence!
*/
sc->result = (DID_OK << 16) | scsi_status;
- if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
- /* Have already saved the status and sense data
+ if (!(scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)) {
+
+ /*
+ * For an Errata on LSI53C1030
+ * When the length of request data
+ * and transfer data are different
+ * with result of command (READ or VERIFY),
+ * DID_SOFT_ERROR is set.
*/
- ;
- } else {
+ if (ioc->bus_type == SPI) {
+ if (pScsiReq->CDB[0] == READ_6 ||
+ pScsiReq->CDB[0] == READ_10 ||
+ pScsiReq->CDB[0] == READ_12 ||
+ pScsiReq->CDB[0] == READ_16 ||
+ pScsiReq->CDB[0] == VERIFY ||
+ pScsiReq->CDB[0] == VERIFY_16) {
+ if (scsi_bufflen(sc) !=
+ xfer_cnt) {
+ sc->result =
+ DID_SOFT_ERROR << 16;
+ printk(KERN_WARNING "Errata"
+ "on LSI53C1030 occurred."
+ "sc->req_bufflen=0x%02x,"
+ "xfer_cnt=0x%02x\n",
+ scsi_bufflen(sc),
+ xfer_cnt);
+ }
+ }
+ }
+
if (xfer_cnt < sc->underflow) {
if (scsi_status == SAM_STAT_BUSY)
sc->result = SAM_STAT_BUSY;
@@ -835,7 +860,58 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
sc->result = (DID_OK << 16) | scsi_status;
if (scsi_state == 0) {
;
- } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
+ } else if (scsi_state &
+ MPI_SCSI_STATE_AUTOSENSE_VALID) {
+
+ /*
+ * For potential trouble on LSI53C1030.
+ * (date:2007.xx.)
+ * It is checked whether the length of
+ * request data is equal to
+ * the length of transfer and residual.
+ * MEDIUM_ERROR is set by incorrect data.
+ */
+ if ((ioc->bus_type == SPI) &&
+ (sc->sense_buffer[2] & 0x20)) {
+ u32 difftransfer;
+ difftransfer =
+ sc->sense_buffer[3] << 24 |
+ sc->sense_buffer[4] << 16 |
+ sc->sense_buffer[5] << 8 |
+ sc->sense_buffer[6];
+ if (((sc->sense_buffer[3] & 0x80) ==
+ 0x80) && (scsi_bufflen(sc)
+ != xfer_cnt)) {
+ sc->sense_buffer[2] =
+ MEDIUM_ERROR;
+ sc->sense_buffer[12] = 0xff;
+ sc->sense_buffer[13] = 0xff;
+ printk(KERN_WARNING"Errata"
+ "on LSI53C1030 occurred."
+ "sc->req_bufflen=0x%02x,"
+ "xfer_cnt=0x%02x\n" ,
+ scsi_bufflen(sc),
+ xfer_cnt);
+ }
+ if (((sc->sense_buffer[3] & 0x80)
+ != 0x80) &&
+ (scsi_bufflen(sc) !=
+ xfer_cnt + difftransfer)) {
+ sc->sense_buffer[2] =
+ MEDIUM_ERROR;
+ sc->sense_buffer[12] = 0xff;
+ sc->sense_buffer[13] = 0xff;
+ printk(KERN_WARNING
+ "Errata on LSI53C1030 occurred"
+ "sc->req_bufflen=0x%02x,"
+ " xfer_cnt=0x%02x,"
+ "difftransfer=0x%02x\n",
+ scsi_bufflen(sc),
+ xfer_cnt,
+ difftransfer);
+ }
+ }
+
/*
* If running against circa 200003dd 909 MPT f/w,
* may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
@@ -2275,11 +2351,12 @@ mptscsih_slave_destroy(struct scsi_device *sdev)
* mptscsih_change_queue_depth - This function will set a devices queue depth
* @sdev: per scsi_device pointer
* @qdepth: requested queue depth
+ * @reason: calling context
*
* Adding support for new 'change_queue_depth' api.
*/
int
-mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
{
MPT_SCSI_HOST *hd = shost_priv(sdev->host);
VirtTarget *vtarget;
@@ -2291,6 +2368,9 @@ mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
starget = scsi_target(sdev);
vtarget = starget->hostdata;
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (ioc->bus_type == SPI) {
if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES))
max_depth = 1;
@@ -2357,7 +2437,8 @@ mptscsih_slave_configure(struct scsi_device *sdev)
ioc->name, vtarget->negoFlags, vtarget->maxOffset,
vtarget->minSyncFactor));
- mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
+ mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH,
+ SCSI_QDEPTH_DEFAULT);
dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
"tagged %d, simple %d, ordered %d\n",
ioc->name,sdev->tagged_supported, sdev->simple_tags,
diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h
index e0b33e04a33..45a5ff3eff6 100644
--- a/drivers/message/fusion/mptscsih.h
+++ b/drivers/message/fusion/mptscsih.h
@@ -128,7 +128,8 @@ extern int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_F
extern int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
-extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth);
+extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason);
extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern struct device_attribute *mptscsih_host_attrs[];
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index d505b68cd37..e39986a7827 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -940,7 +940,7 @@ static const struct block_device_operations i2o_block_fops = {
* Allocate memory for the i2o_block_device struct, gendisk and request
* queue and initialize them as far as no additional information is needed.
*
- * Returns a pointer to the allocated I2O Block device on succes or a
+ * Returns a pointer to the allocated I2O Block device on success or a
* negative error code on failure.
*/
static struct i2o_block_device *i2o_block_device_alloc(void)
diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c
index 27cf4af0e13..e5ab6214150 100644
--- a/drivers/message/i2o/iop.c
+++ b/drivers/message/i2o/iop.c
@@ -132,7 +132,7 @@ u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
* Removes a previously added pointer from the context list and returns
* the matching context id.
*
- * Returns context id on succes or 0 on failure.
+ * Returns context id on success or 0 on failure.
*/
u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
{
@@ -198,7 +198,7 @@ void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
* @c: controller to which the context list belong
* @ptr: pointer to which the context id should be fetched
*
- * Returns context id which matches to the pointer on succes or 0 on
+ * Returns context id which matches to the pointer on success or 0 on
* failure.
*/
u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 570be139f9d..a296e717e86 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -35,6 +35,14 @@ config MFD_ASIC3
This driver supports the ASIC3 multifunction chip found on many
PDAs (mainly iPAQ and HTC based ones)
+config MFD_SH_MOBILE_SDHI
+ bool "Support for SuperH Mobile SDHI"
+ depends on SUPERH
+ select MFD_CORE
+ ---help---
+ This driver supports the SDHI hardware block found in many
+ SuperH Mobile SoCs.
+
config MFD_DM355EVM_MSP
bool "DaVinci DM355 EVM microcontroller"
depends on I2C && MACH_DAVINCI_DM355_EVM
@@ -121,6 +129,12 @@ config TWL4030_POWER
and load scripts controling which resources are switched off/on
or reset when a sleep, wakeup or warm reset event occurs.
+config TWL4030_CODEC
+ bool
+ depends on TWL4030_CORE
+ select MFD_CORE
+ default n
+
config MFD_TMIO
bool
default n
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f3b277b90d4..11350c1d930 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_MFD_ASIC3) += asic3.o
+obj-$(CONFIG_MFD_SH_MOBILE_SDHI) += sh_mobile_sdhi.o
obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
@@ -26,6 +27,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
+obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c
index 57271cb3b31..84815f9ef63 100644
--- a/drivers/mfd/mcp-core.c
+++ b/drivers/mfd/mcp-core.c
@@ -17,11 +17,11 @@
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/mfd/mcp.h>
#include <mach/dma.h>
#include <asm/system.h>
-#include "mcp.h"
#define to_mcp(d) container_of(d, struct mcp, attached_device)
#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 62b32dabf62..25842723272 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -19,6 +19,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
+#include <linux/mfd/mcp.h>
#include <mach/dma.h>
#include <mach/hardware.h>
@@ -28,7 +29,6 @@
#include <mach/assabet.h>
-#include "mcp.h"
struct mcp_sa11x0 {
u32 mccr0;
@@ -163,6 +163,7 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
mcp->dma_audio_wr = DMA_Ser4MCP0Wr;
mcp->dma_telco_rd = DMA_Ser4MCP1Rd;
mcp->dma_telco_wr = DMA_Ser4MCP1Wr;
+ mcp->gpio_base = data->gpio_base;
platform_set_drvdata(pdev, mcp);
diff --git a/drivers/mfd/mcp.h b/drivers/mfd/mcp.h
deleted file mode 100644
index c093a93b880..00000000000
--- a/drivers/mfd/mcp.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * linux/drivers/mfd/mcp.h
- *
- * Copyright (C) 2001 Russell King, All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License.
- */
-#ifndef MCP_H
-#define MCP_H
-
-struct mcp_ops;
-
-struct mcp {
- struct module *owner;
- struct mcp_ops *ops;
- spinlock_t lock;
- int use_count;
- unsigned int sclk_rate;
- unsigned int rw_timeout;
- dma_device_t dma_audio_rd;
- dma_device_t dma_audio_wr;
- dma_device_t dma_telco_rd;
- dma_device_t dma_telco_wr;
- struct device attached_device;
-};
-
-struct mcp_ops {
- void (*set_telecom_divisor)(struct mcp *, unsigned int);
- void (*set_audio_divisor)(struct mcp *, unsigned int);
- void (*reg_write)(struct mcp *, unsigned int, unsigned int);
- unsigned int (*reg_read)(struct mcp *, unsigned int);
- void (*enable)(struct mcp *);
- void (*disable)(struct mcp *);
-};
-
-void mcp_set_telecom_divisor(struct mcp *, unsigned int);
-void mcp_set_audio_divisor(struct mcp *, unsigned int);
-void mcp_reg_write(struct mcp *, unsigned int, unsigned int);
-unsigned int mcp_reg_read(struct mcp *, unsigned int);
-void mcp_enable(struct mcp *);
-void mcp_disable(struct mcp *);
-#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate)
-
-struct mcp *mcp_host_alloc(struct device *, size_t);
-int mcp_host_register(struct mcp *);
-void mcp_host_unregister(struct mcp *);
-
-struct mcp_driver {
- struct device_driver drv;
- int (*probe)(struct mcp *);
- void (*remove)(struct mcp *);
- int (*suspend)(struct mcp *, pm_message_t);
- int (*resume)(struct mcp *);
-};
-
-int mcp_driver_register(struct mcp_driver *);
-void mcp_driver_unregister(struct mcp_driver *);
-
-#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device)
-#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d)
-
-#define mcp_priv(mcp) ((void *)((mcp)+1))
-
-#endif
diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c
index 4b364bae6b3..970afa10326 100644
--- a/drivers/mfd/menelaus.c
+++ b/drivers/mfd/menelaus.c
@@ -44,7 +44,7 @@
#include <asm/mach/irq.h>
#include <mach/gpio.h>
-#include <mach/menelaus.h>
+#include <plat/menelaus.h>
#define DRIVER_NAME "menelaus"
diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c
new file mode 100644
index 00000000000..03efae8041a
--- /dev/null
+++ b/drivers/mfd/sh_mobile_sdhi.c
@@ -0,0 +1,156 @@
+/*
+ * SuperH Mobile SDHI
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * 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.
+ *
+ * Based on "Compaq ASIC3 support":
+ *
+ * Copyright 2001 Compaq Computer Corporation.
+ * Copyright 2004-2005 Phil Blundell
+ * Copyright 2007-2008 OpenedHand Ltd.
+ *
+ * Authors: Phil Blundell <pb@handhelds.org>,
+ * Samuel Ortiz <sameo@openedhand.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mfd/sh_mobile_sdhi.h>
+
+struct sh_mobile_sdhi {
+ struct clk *clk;
+ struct tmio_mmc_data mmc_data;
+ struct mfd_cell cell_mmc;
+};
+
+static struct resource sh_mobile_sdhi_resources[] = {
+ {
+ .start = 0x000,
+ .end = 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell sh_mobile_sdhi_cell = {
+ .name = "tmio-mmc",
+ .num_resources = ARRAY_SIZE(sh_mobile_sdhi_resources),
+ .resources = sh_mobile_sdhi_resources,
+};
+
+static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state)
+{
+ struct platform_device *pdev = to_platform_device(tmio->dev.parent);
+ struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
+
+ if (p && p->set_pwr)
+ p->set_pwr(pdev, state);
+}
+
+static int __init sh_mobile_sdhi_probe(struct platform_device *pdev)
+{
+ struct sh_mobile_sdhi *priv;
+ struct resource *mem;
+ char clk_name[8];
+ int ret, irq;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ dev_err(&pdev->dev, "missing MEM resource\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ dev_err(&pdev->dev, "missing IRQ resource\n");
+
+ if (!mem || (irq < 0))
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
+ priv->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ ret = PTR_ERR(priv->clk);
+ kfree(priv);
+ return ret;
+ }
+
+ clk_enable(priv->clk);
+
+ /* FIXME: silly const unsigned int hclk */
+ *(unsigned int *)&priv->mmc_data.hclk = clk_get_rate(priv->clk);
+ priv->mmc_data.set_pwr = sh_mobile_sdhi_set_pwr;
+
+ memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc));
+ priv->cell_mmc.driver_data = &priv->mmc_data;
+ priv->cell_mmc.platform_data = &priv->cell_mmc;
+ priv->cell_mmc.data_size = sizeof(priv->cell_mmc);
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = mfd_add_devices(&pdev->dev, pdev->id,
+ &priv->cell_mmc, 1, mem, irq);
+ if (ret) {
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int sh_mobile_sdhi_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_sdhi *priv = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver sh_mobile_sdhi_driver = {
+ .driver = {
+ .name = "sh_mobile_sdhi",
+ .owner = THIS_MODULE,
+ },
+ .probe = sh_mobile_sdhi_probe,
+ .remove = __devexit_p(sh_mobile_sdhi_remove),
+};
+
+static int __init sh_mobile_sdhi_init(void)
+{
+ return platform_driver_register(&sh_mobile_sdhi_driver);
+}
+
+static void __exit sh_mobile_sdhi_exit(void)
+{
+ platform_driver_unregister(&sh_mobile_sdhi_driver);
+}
+
+module_init(sh_mobile_sdhi_init);
+module_exit(sh_mobile_sdhi_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
new file mode 100644
index 00000000000..77b914907d7
--- /dev/null
+++ b/drivers/mfd/twl4030-codec.c
@@ -0,0 +1,276 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright: (C) 2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-codec.h>
+
+#define TWL4030_CODEC_CELLS 2
+
+static struct platform_device *twl4030_codec_dev;
+
+struct twl4030_codec_resource {
+ int request_count;
+ u8 reg;
+ u8 mask;
+};
+
+struct twl4030_codec {
+ unsigned int audio_mclk;
+ struct mutex mutex;
+ struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
+ struct mfd_cell cells[TWL4030_CODEC_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ u8 val;
+
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ codec->resource[id].reg);
+
+ if (enable)
+ val |= codec->resource[id].mask;
+ else
+ val &= ~codec->resource[id].mask;
+
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ val, codec->resource[id].reg);
+
+ return val;
+}
+
+static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ u8 val;
+
+ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+ codec->resource[id].reg);
+
+ return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_enable_resource(enum twl4030_codec_res id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ int val;
+
+ if (id >= TWL4030_CODEC_RES_MAX) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&codec->mutex);
+ if (!codec->resource[id].request_count)
+ /* Resource was disabled, enable it */
+ val = twl4030_codec_set_resource(id, 1);
+ else
+ val = twl4030_codec_get_resource(id);
+
+ codec->resource[id].request_count++;
+ mutex_unlock(&codec->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_disable_resource(unsigned id)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+ int val;
+
+ if (id >= TWL4030_CODEC_RES_MAX) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Invalid resource ID (%u)\n", id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&codec->mutex);
+ if (!codec->resource[id].request_count) {
+ dev_err(&twl4030_codec_dev->dev,
+ "Resource has been disabled already (%u)\n", id);
+ mutex_unlock(&codec->mutex);
+ return -EPERM;
+ }
+ codec->resource[id].request_count--;
+
+ if (!codec->resource[id].request_count)
+ /* Resource can be disabled now */
+ val = twl4030_codec_set_resource(id, 0);
+ else
+ val = twl4030_codec_get_resource(id);
+
+ mutex_unlock(&codec->mutex);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
+
+unsigned int twl4030_codec_get_mclk(void)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+
+ return codec->audio_mclk;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
+
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+ struct twl4030_codec *codec;
+ struct twl4030_codec_data *pdata = pdev->dev.platform_data;
+ struct mfd_cell *cell = NULL;
+ int ret, childs = 0;
+ u8 val;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -EINVAL;
+ }
+
+ /* Configure APLL_INFREQ and disable APLL if enabled */
+ val = 0;
+ switch (pdata->audio_mclk) {
+ case 19200000:
+ val |= TWL4030_APLL_INFREQ_19200KHZ;
+ break;
+ case 26000000:
+ val |= TWL4030_APLL_INFREQ_26000KHZ;
+ break;
+ case 38400000:
+ val |= TWL4030_APLL_INFREQ_38400KHZ;
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid audio_mclk\n");
+ return -EINVAL;
+ }
+ twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ val, TWL4030_REG_APLL_CTL);
+
+ codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, codec);
+
+ twl4030_codec_dev = pdev;
+ mutex_init(&codec->mutex);
+ codec->audio_mclk = pdata->audio_mclk;
+
+ /* Codec power */
+ codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+ codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
+
+ /* PLL */
+ codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+ codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
+
+ if (pdata->audio) {
+ cell = &codec->cells[childs];
+ cell->name = "twl4030_codec_audio";
+ cell->platform_data = pdata->audio;
+ cell->data_size = sizeof(*pdata->audio);
+ childs++;
+ }
+ if (pdata->vibra) {
+ cell = &codec->cells[childs];
+ cell->name = "twl4030_codec_vibra";
+ cell->platform_data = pdata->vibra;
+ cell->data_size = sizeof(*pdata->vibra);
+ childs++;
+ }
+
+ if (childs)
+ ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
+ childs, NULL, 0);
+ else {
+ dev_err(&pdev->dev, "No platform data found for childs\n");
+ ret = -ENODEV;
+ }
+
+ if (!ret)
+ return 0;
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(codec);
+ twl4030_codec_dev = NULL;
+ return ret;
+}
+
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
+{
+ struct twl4030_codec *codec = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(codec);
+ twl4030_codec_dev = NULL;
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:twl4030_codec");
+
+static struct platform_driver twl4030_codec_driver = {
+ .probe = twl4030_codec_probe,
+ .remove = __devexit_p(twl4030_codec_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "twl4030_codec",
+ },
+};
+
+static int __devinit twl4030_codec_init(void)
+{
+ return platform_driver_register(&twl4030_codec_driver);
+}
+module_init(twl4030_codec_init);
+
+static void __devexit twl4030_codec_exit(void)
+{
+ platform_driver_unregister(&twl4030_codec_driver);
+}
+module_exit(twl4030_codec_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
index a1c47ee95c0..40449cdf09d 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl4030-core.c
@@ -39,7 +39,7 @@
#include <linux/i2c/twl4030.h>
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
-#include <mach/cpu.h>
+#include <plat/cpu.h>
#endif
/*
@@ -114,6 +114,12 @@
#define twl_has_watchdog() false
#endif
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#define twl_has_codec() true
+#else
+#define twl_has_codec() false
+#endif
+
/* Triton Core internal information (BEGIN) */
/* Last - for index max*/
@@ -601,6 +607,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
+ if (twl_has_codec() && pdata->codec) {
+ child = add_child(1, "twl4030_codec",
+ pdata->codec, sizeof(*pdata->codec),
+ false, 0, 0);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ }
+
if (twl_has_regulator()) {
/*
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
@@ -763,7 +777,7 @@ static int twl4030_remove(struct i2c_client *client)
}
/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
-static int
+static int __init
twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int status;
diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c
index fa294b6d600..85fd9421be9 100644
--- a/drivers/mfd/ucb1400_core.c
+++ b/drivers/mfd/ucb1400_core.c
@@ -51,6 +51,7 @@ static int ucb1400_core_probe(struct device *dev)
struct ucb1400_ts ucb_ts;
struct ucb1400_gpio ucb_gpio;
struct snd_ac97 *ac97;
+ struct ucb1400_pdata *pdata = dev->platform_data;
memset(&ucb_ts, 0, sizeof(ucb_ts));
memset(&ucb_gpio, 0, sizeof(ucb_gpio));
@@ -88,6 +89,12 @@ static int ucb1400_core_probe(struct device *dev)
/* TOUCHSCREEN */
ucb_ts.ac97 = ac97;
+
+ if (pdata != NULL && pdata->irq >= 0)
+ ucb_ts.irq = pdata->irq;
+ else
+ ucb_ts.irq = -1;
+
ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1);
if (!ucb->ucb1400_ts) {
err = -ENOMEM;
diff --git a/drivers/mfd/ucb1x00-assabet.c b/drivers/mfd/ucb1x00-assabet.c
index 86fed4870f9..cea9da60850 100644
--- a/drivers/mfd/ucb1x00-assabet.c
+++ b/drivers/mfd/ucb1x00-assabet.c
@@ -14,10 +14,10 @@
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
+#include <linux/mfd/ucb1x00.h>
#include <mach/dma.h>
-#include "ucb1x00.h"
#define UCB1X00_ATTR(name,input)\
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index 60c3988f3cf..252b74188ec 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -25,12 +25,12 @@
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/mutex.h>
+#include <linux/mfd/ucb1x00.h>
+#include <linux/gpio.h>
#include <mach/dma.h>
#include <mach/hardware.h>
-#include "ucb1x00.h"
-
static DEFINE_MUTEX(ucb1x00_mutex);
static LIST_HEAD(ucb1x00_drivers);
static LIST_HEAD(ucb1x00_devices);
@@ -108,6 +108,60 @@ unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
return ucb1x00_reg_read(ucb, UCB_IO_DATA);
}
+static void ucb1x00_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ucb->io_lock, flags);
+ if (value)
+ ucb->io_out |= 1 << offset;
+ else
+ ucb->io_out &= ~(1 << offset);
+
+ ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+ spin_unlock_irqrestore(&ucb->io_lock, flags);
+}
+
+static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+ return ucb1x00_reg_read(ucb, UCB_IO_DATA) & (1 << offset);
+}
+
+static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ucb->io_lock, flags);
+ ucb->io_dir &= ~(1 << offset);
+ ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+ spin_unlock_irqrestore(&ucb->io_lock, flags);
+
+ return 0;
+}
+
+static int ucb1x00_gpio_direction_output(struct gpio_chip *chip, unsigned offset
+ , int value)
+{
+ struct ucb1x00 *ucb = container_of(chip, struct ucb1x00, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ucb->io_lock, flags);
+ ucb->io_dir |= (1 << offset);
+ ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
+
+ if (value)
+ ucb->io_out |= 1 << offset;
+ else
+ ucb->io_out &= ~(1 << offset);
+ ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
+ spin_unlock_irqrestore(&ucb->io_lock, flags);
+
+ return 0;
+}
+
/*
* UCB1300 data sheet says we must:
* 1. enable ADC => 5us (including reference startup time)
@@ -476,6 +530,7 @@ static int ucb1x00_probe(struct mcp *mcp)
struct ucb1x00_driver *drv;
unsigned int id;
int ret = -ENODEV;
+ int temp;
mcp_enable(mcp);
id = mcp_reg_read(mcp, UCB_ID);
@@ -508,12 +563,27 @@ static int ucb1x00_probe(struct mcp *mcp)
goto err_free;
}
+ ucb->gpio.base = -1;
+ if (mcp->gpio_base != 0) {
+ ucb->gpio.label = dev_name(&ucb->dev);
+ ucb->gpio.base = mcp->gpio_base;
+ ucb->gpio.ngpio = 10;
+ ucb->gpio.set = ucb1x00_gpio_set;
+ ucb->gpio.get = ucb1x00_gpio_get;
+ ucb->gpio.direction_input = ucb1x00_gpio_direction_input;
+ ucb->gpio.direction_output = ucb1x00_gpio_direction_output;
+ ret = gpiochip_add(&ucb->gpio);
+ if (ret)
+ goto err_free;
+ } else
+ dev_info(&ucb->dev, "gpio_base not set so no gpiolib support");
+
ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING,
"UCB1x00", ucb);
if (ret) {
printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
ucb->irq, ret);
- goto err_free;
+ goto err_gpio;
}
mcp_set_drvdata(mcp, ucb);
@@ -522,6 +592,7 @@ static int ucb1x00_probe(struct mcp *mcp)
if (ret)
goto err_irq;
+
INIT_LIST_HEAD(&ucb->devs);
mutex_lock(&ucb1x00_mutex);
list_add(&ucb->node, &ucb1x00_devices);
@@ -529,10 +600,14 @@ static int ucb1x00_probe(struct mcp *mcp)
ucb1x00_add_dev(ucb, drv);
}
mutex_unlock(&ucb1x00_mutex);
+
goto out;
err_irq:
free_irq(ucb->irq, ucb);
+ err_gpio:
+ if (ucb->gpio.base != -1)
+ temp = gpiochip_remove(&ucb->gpio);
err_free:
kfree(ucb);
err_disable:
@@ -545,6 +620,7 @@ static void ucb1x00_remove(struct mcp *mcp)
{
struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
struct list_head *l, *n;
+ int ret;
mutex_lock(&ucb1x00_mutex);
list_del(&ucb->node);
@@ -554,6 +630,12 @@ static void ucb1x00_remove(struct mcp *mcp)
}
mutex_unlock(&ucb1x00_mutex);
+ if (ucb->gpio.base != -1) {
+ ret = gpiochip_remove(&ucb->gpio);
+ if (ret)
+ dev_err(&ucb->dev, "Can't remove gpio chip: %d\n", ret);
+ }
+
free_irq(ucb->irq, ucb);
device_unregister(&ucb->dev);
}
@@ -604,6 +686,7 @@ static int ucb1x00_resume(struct mcp *mcp)
struct ucb1x00 *ucb = mcp_get_drvdata(mcp);
struct ucb1x00_dev *dev;
+ ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
mutex_lock(&ucb1x00_mutex);
list_for_each_entry(dev, &ucb->devs, dev_node) {
if (dev->drv->resume)
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c
index 61b7d3eb9a2..000cb414a78 100644
--- a/drivers/mfd/ucb1x00-ts.c
+++ b/drivers/mfd/ucb1x00-ts.c
@@ -30,12 +30,12 @@
#include <linux/freezer.h>
#include <linux/slab.h>
#include <linux/kthread.h>
+#include <linux/mfd/ucb1x00.h>
#include <mach/dma.h>
#include <mach/collie.h>
#include <asm/mach-types.h>
-#include "ucb1x00.h"
struct ucb1x00_ts {
diff --git a/drivers/mfd/ucb1x00.h b/drivers/mfd/ucb1x00.h
deleted file mode 100644
index a8ad8a0ed5d..00000000000
--- a/drivers/mfd/ucb1x00.h
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * linux/drivers/mfd/ucb1x00.h
- *
- * Copyright (C) 2001 Russell King, All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License.
- */
-#ifndef UCB1200_H
-#define UCB1200_H
-
-#define UCB_IO_DATA 0x00
-#define UCB_IO_DIR 0x01
-
-#define UCB_IO_0 (1 << 0)
-#define UCB_IO_1 (1 << 1)
-#define UCB_IO_2 (1 << 2)
-#define UCB_IO_3 (1 << 3)
-#define UCB_IO_4 (1 << 4)
-#define UCB_IO_5 (1 << 5)
-#define UCB_IO_6 (1 << 6)
-#define UCB_IO_7 (1 << 7)
-#define UCB_IO_8 (1 << 8)
-#define UCB_IO_9 (1 << 9)
-
-#define UCB_IE_RIS 0x02
-#define UCB_IE_FAL 0x03
-#define UCB_IE_STATUS 0x04
-#define UCB_IE_CLEAR 0x04
-#define UCB_IE_ADC (1 << 11)
-#define UCB_IE_TSPX (1 << 12)
-#define UCB_IE_TSMX (1 << 13)
-#define UCB_IE_TCLIP (1 << 14)
-#define UCB_IE_ACLIP (1 << 15)
-
-#define UCB_IRQ_TSPX 12
-
-#define UCB_TC_A 0x05
-#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */
-#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */
-
-#define UCB_TC_B 0x06
-#define UCB_TC_B_VOICE_ENA (1 << 3)
-#define UCB_TC_B_CLIP (1 << 4)
-#define UCB_TC_B_ATT (1 << 6)
-#define UCB_TC_B_SIDE_ENA (1 << 11)
-#define UCB_TC_B_MUTE (1 << 13)
-#define UCB_TC_B_IN_ENA (1 << 14)
-#define UCB_TC_B_OUT_ENA (1 << 15)
-
-#define UCB_AC_A 0x07
-#define UCB_AC_B 0x08
-#define UCB_AC_B_LOOP (1 << 8)
-#define UCB_AC_B_MUTE (1 << 13)
-#define UCB_AC_B_IN_ENA (1 << 14)
-#define UCB_AC_B_OUT_ENA (1 << 15)
-
-#define UCB_TS_CR 0x09
-#define UCB_TS_CR_TSMX_POW (1 << 0)
-#define UCB_TS_CR_TSPX_POW (1 << 1)
-#define UCB_TS_CR_TSMY_POW (1 << 2)
-#define UCB_TS_CR_TSPY_POW (1 << 3)
-#define UCB_TS_CR_TSMX_GND (1 << 4)
-#define UCB_TS_CR_TSPX_GND (1 << 5)
-#define UCB_TS_CR_TSMY_GND (1 << 6)
-#define UCB_TS_CR_TSPY_GND (1 << 7)
-#define UCB_TS_CR_MODE_INT (0 << 8)
-#define UCB_TS_CR_MODE_PRES (1 << 8)
-#define UCB_TS_CR_MODE_POS (2 << 8)
-#define UCB_TS_CR_BIAS_ENA (1 << 11)
-#define UCB_TS_CR_TSPX_LOW (1 << 12)
-#define UCB_TS_CR_TSMX_LOW (1 << 13)
-
-#define UCB_ADC_CR 0x0a
-#define UCB_ADC_SYNC_ENA (1 << 0)
-#define UCB_ADC_VREFBYP_CON (1 << 1)
-#define UCB_ADC_INP_TSPX (0 << 2)
-#define UCB_ADC_INP_TSMX (1 << 2)
-#define UCB_ADC_INP_TSPY (2 << 2)
-#define UCB_ADC_INP_TSMY (3 << 2)
-#define UCB_ADC_INP_AD0 (4 << 2)
-#define UCB_ADC_INP_AD1 (5 << 2)
-#define UCB_ADC_INP_AD2 (6 << 2)
-#define UCB_ADC_INP_AD3 (7 << 2)
-#define UCB_ADC_EXT_REF (1 << 5)
-#define UCB_ADC_START (1 << 7)
-#define UCB_ADC_ENA (1 << 15)
-
-#define UCB_ADC_DATA 0x0b
-#define UCB_ADC_DAT_VAL (1 << 15)
-#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5)
-
-#define UCB_ID 0x0c
-#define UCB_ID_1200 0x1004
-#define UCB_ID_1300 0x1005
-#define UCB_ID_TC35143 0x9712
-
-#define UCB_MODE 0x0d
-#define UCB_MODE_DYN_VFLAG_ENA (1 << 12)
-#define UCB_MODE_AUD_OFF_CAN (1 << 13)
-
-#include "mcp.h"
-
-struct ucb1x00_irq {
- void *devid;
- void (*fn)(int, void *);
-};
-
-struct ucb1x00 {
- spinlock_t lock;
- struct mcp *mcp;
- unsigned int irq;
- struct semaphore adc_sem;
- spinlock_t io_lock;
- u16 id;
- u16 io_dir;
- u16 io_out;
- u16 adc_cr;
- u16 irq_fal_enbl;
- u16 irq_ris_enbl;
- struct ucb1x00_irq irq_handler[16];
- struct device dev;
- struct list_head node;
- struct list_head devs;
-};
-
-struct ucb1x00_driver;
-
-struct ucb1x00_dev {
- struct list_head dev_node;
- struct list_head drv_node;
- struct ucb1x00 *ucb;
- struct ucb1x00_driver *drv;
- void *priv;
-};
-
-struct ucb1x00_driver {
- struct list_head node;
- struct list_head devs;
- int (*add)(struct ucb1x00_dev *dev);
- void (*remove)(struct ucb1x00_dev *dev);
- int (*suspend)(struct ucb1x00_dev *dev, pm_message_t state);
- int (*resume)(struct ucb1x00_dev *dev);
-};
-
-#define classdev_to_ucb1x00(cd) container_of(cd, struct ucb1x00, dev)
-
-int ucb1x00_register_driver(struct ucb1x00_driver *);
-void ucb1x00_unregister_driver(struct ucb1x00_driver *);
-
-/**
- * ucb1x00_clkrate - return the UCB1x00 SIB clock rate
- * @ucb: UCB1x00 structure describing chip
- *
- * Return the SIB clock rate in Hz.
- */
-static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb)
-{
- return mcp_get_sclk_rate(ucb->mcp);
-}
-
-/**
- * ucb1x00_enable - enable the UCB1x00 SIB clock
- * @ucb: UCB1x00 structure describing chip
- *
- * Enable the SIB clock. This can be called multiple times.
- */
-static inline void ucb1x00_enable(struct ucb1x00 *ucb)
-{
- mcp_enable(ucb->mcp);
-}
-
-/**
- * ucb1x00_disable - disable the UCB1x00 SIB clock
- * @ucb: UCB1x00 structure describing chip
- *
- * Disable the SIB clock. The SIB clock will only be disabled
- * when the number of ucb1x00_enable calls match the number of
- * ucb1x00_disable calls.
- */
-static inline void ucb1x00_disable(struct ucb1x00 *ucb)
-{
- mcp_disable(ucb->mcp);
-}
-
-/**
- * ucb1x00_reg_write - write a UCB1x00 register
- * @ucb: UCB1x00 structure describing chip
- * @reg: UCB1x00 4-bit register index to write
- * @val: UCB1x00 16-bit value to write
- *
- * Write the UCB1x00 register @reg with value @val. The SIB
- * clock must be running for this function to return.
- */
-static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val)
-{
- mcp_reg_write(ucb->mcp, reg, val);
-}
-
-/**
- * ucb1x00_reg_read - read a UCB1x00 register
- * @ucb: UCB1x00 structure describing chip
- * @reg: UCB1x00 4-bit register index to write
- *
- * Read the UCB1x00 register @reg and return its value. The SIB
- * clock must be running for this function to return.
- */
-static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg)
-{
- return mcp_reg_read(ucb->mcp, reg);
-}
-/**
- * ucb1x00_set_audio_divisor -
- * @ucb: UCB1x00 structure describing chip
- * @div: SIB clock divisor
- */
-static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div)
-{
- mcp_set_audio_divisor(ucb->mcp, div);
-}
-
-/**
- * ucb1x00_set_telecom_divisor -
- * @ucb: UCB1x00 structure describing chip
- * @div: SIB clock divisor
- */
-static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div)
-{
- mcp_set_telecom_divisor(ucb->mcp, div);
-}
-
-void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int);
-void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int);
-unsigned int ucb1x00_io_read(struct ucb1x00 *ucb);
-
-#define UCB_NOSYNC (0)
-#define UCB_SYNC (1)
-
-unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync);
-void ucb1x00_adc_enable(struct ucb1x00 *ucb);
-void ucb1x00_adc_disable(struct ucb1x00 *ucb);
-
-/*
- * Which edges of the IRQ do you want to control today?
- */
-#define UCB_RISING (1 << 0)
-#define UCB_FALLING (1 << 1)
-
-int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid);
-void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
-void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
-int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid);
-
-#endif
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index df1f86b5c83..2c16ca6501d 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -246,8 +246,19 @@ config EP93XX_PWM
To compile this driver as a module, choose M here: the module will
be called ep93xx_pwm.
+config DS1682
+ tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS1682 Total Elapsed Time Recorder.
+
+ This driver can also be built as a module. If so, the module
+ will be called ds1682.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
+source "drivers/misc/iwmc3200top/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f982d2ecfde..906a0edcea4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
+obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_C2PORT) += c2port/
+obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-y += eeprom/
obj-y += cb710/
diff --git a/drivers/i2c/chips/ds1682.c b/drivers/misc/ds1682.c
index f3ee4a1abb7..f3ee4a1abb7 100644
--- a/drivers/i2c/chips/ds1682.c
+++ b/drivers/misc/ds1682.c
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c
index 6e43ab4231a..4bb7a3af9ad 100644
--- a/drivers/misc/ics932s401.c
+++ b/drivers/misc/ics932s401.c
@@ -417,32 +417,25 @@ static int ics932s401_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int vendor, device, revision;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (kind <= 0) {
- int vendor, device, revision;
-
- vendor = i2c_smbus_read_word_data(client,
- ICS932S401_REG_VENDOR_REV);
- vendor >>= 8;
- revision = vendor >> ICS932S401_REV_SHIFT;
- vendor &= ICS932S401_VENDOR_MASK;
- if (vendor != ICS932S401_VENDOR)
- return -ENODEV;
-
- device = i2c_smbus_read_word_data(client,
- ICS932S401_REG_DEVICE);
- device >>= 8;
- if (device != ICS932S401_DEVICE)
- return -ENODEV;
-
- if (revision != ICS932S401_REV)
- dev_info(&adapter->dev, "Unknown revision %d\n",
- revision);
- } else
- dev_dbg(&adapter->dev, "detection forced\n");
+ vendor = i2c_smbus_read_word_data(client, ICS932S401_REG_VENDOR_REV);
+ vendor >>= 8;
+ revision = vendor >> ICS932S401_REV_SHIFT;
+ vendor &= ICS932S401_VENDOR_MASK;
+ if (vendor != ICS932S401_VENDOR)
+ return -ENODEV;
+
+ device = i2c_smbus_read_word_data(client, ICS932S401_REG_DEVICE);
+ device >>= 8;
+ if (device != ICS932S401_DEVICE)
+ return -ENODEV;
+
+ if (revision != ICS932S401_REV)
+ dev_info(&adapter->dev, "Unknown revision %d\n", revision);
strlcpy(info->type, "ics932s401", I2C_NAME_SIZE);
diff --git a/drivers/misc/iwmc3200top/Kconfig b/drivers/misc/iwmc3200top/Kconfig
new file mode 100644
index 00000000000..9e4b88fb57f
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Kconfig
@@ -0,0 +1,20 @@
+config IWMC3200TOP
+ tristate "Intel Wireless MultiCom Top Driver"
+ depends on MMC && EXPERIMENTAL
+ select FW_LOADER
+ ---help---
+ Intel Wireless MultiCom 3200 Top driver is responsible for
+ for firmware load and enabled coms enumeration
+
+config IWMC3200TOP_DEBUG
+ bool "Enable full debug output of iwmc3200top Driver"
+ depends on IWMC3200TOP
+ ---help---
+ Enable full debug output of iwmc3200top Driver
+
+config IWMC3200TOP_DEBUGFS
+ bool "Enable Debugfs debugging interface for iwmc3200top"
+ depends on IWMC3200TOP
+ ---help---
+ Enable creation of debugfs files for iwmc3200top
+
diff --git a/drivers/misc/iwmc3200top/Makefile b/drivers/misc/iwmc3200top/Makefile
new file mode 100644
index 00000000000..fbf53fb4634
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Makefile
@@ -0,0 +1,29 @@
+# iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+# drivers/misc/iwmc3200top/Makefile
+#
+# Copyright (C) 2009 Intel Corporation. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version
+# 2 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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.
+#
+#
+# Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+# -
+#
+#
+
+obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o
+iwmc3200top-objs := main.o fw-download.o
+iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o
+iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o
diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c
new file mode 100644
index 00000000000..0c8ea0a1c8a
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.c
@@ -0,0 +1,133 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/debufs.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+#include <linux/debugfs.h>
+
+#include "iwmc3200top.h"
+#include "fw-msg.h"
+#include "log.h"
+#include "debugfs.h"
+
+
+
+/* Constants definition */
+#define HEXADECIMAL_RADIX 16
+
+/* Functions definition */
+
+
+#define DEBUGFS_ADD(name, parent) do { \
+ dbgfs->dbgfs_##parent##_files.file_##name = \
+ debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \
+ &iwmct_dbgfs_##name##_ops); \
+} while (0)
+
+#define DEBUGFS_RM(name) do { \
+ debugfs_remove(name); \
+ name = NULL; \
+} while (0)
+
+#define DEBUGFS_READ_FUNC(name) \
+ssize_t iwmct_dbgfs_##name##_read(struct file *file, \
+ char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name) \
+ssize_t iwmct_dbgfs_##name##_write(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos);
+
+#define DEBUGFS_READ_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = { \
+ .read = iwmct_dbgfs_##name##_read, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+#define DEBUGFS_WRITE_FILE_OPS(name) \
+ DEBUGFS_WRITE_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = { \
+ .write = iwmct_dbgfs_##name##_write, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
+ DEBUGFS_READ_FUNC(name) \
+ DEBUGFS_WRITE_FUNC(name) \
+ static const struct file_operations iwmct_dbgfs_##name##_ops = {\
+ .write = iwmct_dbgfs_##name##_write, \
+ .read = iwmct_dbgfs_##name##_read, \
+ .open = iwmct_dbgfs_open_file_generic, \
+ };
+
+
+/* Debugfs file ops definitions */
+
+/*
+ * Create the debugfs files and directories
+ *
+ */
+void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
+{
+ struct iwmct_debugfs *dbgfs;
+
+ dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL);
+ if (!dbgfs) {
+ LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n",
+ sizeof(struct iwmct_debugfs));
+ return;
+ }
+
+ priv->dbgfs = dbgfs;
+ dbgfs->name = name;
+ dbgfs->dir_drv = debugfs_create_dir(name, NULL);
+ if (!dbgfs->dir_drv) {
+ LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n");
+ return;
+ }
+
+ return;
+}
+
+/**
+ * Remove the debugfs files and directories
+ *
+ */
+void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
+{
+ if (!dbgfs)
+ return;
+
+ DEBUGFS_RM(dbgfs->dir_drv);
+ kfree(dbgfs);
+ dbgfs = NULL;
+}
+
diff --git a/drivers/misc/iwmc3200top/debugfs.h b/drivers/misc/iwmc3200top/debugfs.h
new file mode 100644
index 00000000000..71d45759b40
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.h
@@ -0,0 +1,58 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/debufs.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __DEBUGFS_H__
+#define __DEBUGFS_H__
+
+
+#ifdef CONFIG_IWMC3200TOP_DEBUGFS
+
+struct iwmct_debugfs {
+ const char *name;
+ struct dentry *dir_drv;
+ struct dir_drv_files {
+ } dbgfs_drv_files;
+};
+
+void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name);
+void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs);
+
+#else /* CONFIG_IWMC3200TOP_DEBUGFS */
+
+struct iwmct_debugfs;
+
+static inline void
+iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
+{}
+
+static inline void
+iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
+{}
+
+#endif /* CONFIG_IWMC3200TOP_DEBUGFS */
+
+#endif /* __DEBUGFS_H__ */
+
diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c
new file mode 100644
index 00000000000..50d431e469f
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-download.c
@@ -0,0 +1,355 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/fw-download.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include <asm/unaligned.h>
+
+#include "iwmc3200top.h"
+#include "log.h"
+#include "fw-msg.h"
+
+#define CHECKSUM_BYTES_NUM sizeof(u32)
+
+/**
+ init parser struct with file
+ */
+static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file,
+ size_t file_size, size_t block_size)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_hdr *fw_hdr = &parser->versions;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size);
+
+ parser->file = file;
+ parser->file_size = file_size;
+ parser->cur_pos = 0;
+ parser->buf = NULL;
+
+ parser->buf = kzalloc(block_size, GFP_KERNEL);
+ if (!parser->buf) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n");
+ return -ENOMEM;
+ }
+ parser->buf_size = block_size;
+
+ /* extract fw versions */
+ memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr));
+ LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n"
+ "top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n",
+ fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision,
+ fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision,
+ fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision,
+ fw_hdr->tic_name);
+
+ parser->cur_pos += sizeof(struct iwmct_fw_hdr);
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+static bool iwmct_checksum(struct iwmct_priv *priv)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ __le32 *file = (__le32 *)parser->file;
+ int i, pad, steps;
+ u32 accum = 0;
+ u32 checksum;
+ u32 mask = 0xffffffff;
+
+ pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4;
+ steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4;
+
+ LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps);
+
+ for (i = 0; i < steps; i++)
+ accum += le32_to_cpu(file[i]);
+
+ if (pad) {
+ mask <<= 8 * (4 - pad);
+ accum += le32_to_cpu(file[steps]) & mask;
+ }
+
+ checksum = get_unaligned_le32((__le32 *)(parser->file +
+ parser->file_size - CHECKSUM_BYTES_NUM));
+
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "compare checksum accum=0x%x to checksum=0x%x\n",
+ accum, checksum);
+
+ return checksum == accum;
+}
+
+static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec,
+ size_t *sec_size, __le32 *sec_addr)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_dbg *dbg = &priv->dbg;
+ struct iwmct_fw_sec_hdr *sec_hdr;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr)
+ <= parser->file_size) {
+
+ sec_hdr = (struct iwmct_fw_sec_hdr *)
+ (parser->file + parser->cur_pos);
+ parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr);
+
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "sec hdr: type=%s addr=0x%x size=%d\n",
+ sec_hdr->type, sec_hdr->target_addr,
+ sec_hdr->data_size);
+
+ if (strcmp(sec_hdr->type, "ENT") == 0)
+ parser->entry_point = le32_to_cpu(sec_hdr->target_addr);
+ else if (strcmp(sec_hdr->type, "LBL") == 0)
+ strcpy(dbg->label_fw, parser->file + parser->cur_pos);
+ else if (((strcmp(sec_hdr->type, "TOP") == 0) &&
+ (priv->barker & BARKER_DNLOAD_TOP_MSK)) ||
+ ((strcmp(sec_hdr->type, "GPS") == 0) &&
+ (priv->barker & BARKER_DNLOAD_GPS_MSK)) ||
+ ((strcmp(sec_hdr->type, "BTH") == 0) &&
+ (priv->barker & BARKER_DNLOAD_BT_MSK))) {
+ *sec_addr = sec_hdr->target_addr;
+ *sec_size = le32_to_cpu(sec_hdr->data_size);
+ *p_sec = parser->file + parser->cur_pos;
+ parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
+ return 1;
+ } else if (strcmp(sec_hdr->type, "LOG") != 0)
+ LOG_WARNING(priv, FW_DOWNLOAD,
+ "skipping section type %s\n",
+ sec_hdr->type);
+
+ parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "finished with section cur_pos=%zd\n", parser->cur_pos);
+ }
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec,
+ size_t sec_size, __le32 addr)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
+ const u8 *cur_block = p_sec;
+ size_t sent = 0;
+ int cnt = 0;
+ int ret = 0;
+ u32 cmd = 0;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+ LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n",
+ addr, sec_size);
+
+ while (sent < sec_size) {
+ int i;
+ u32 chksm = 0;
+ u32 reset = atomic_read(&priv->reset);
+ /* actual FW data */
+ u32 data_size = min(parser->buf_size - sizeof(*hdr),
+ sec_size - sent);
+ /* Pad to block size */
+ u32 trans_size = (data_size + sizeof(*hdr) +
+ IWMC_SDIO_BLK_SIZE - 1) &
+ ~(IWMC_SDIO_BLK_SIZE - 1);
+ ++cnt;
+
+ /* in case of reset, interrupt FW DOWNLAOD */
+ if (reset) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "Reset detected. Abort FW download!!!");
+ ret = -ECANCELED;
+ goto exit;
+ }
+
+ memset(parser->buf, 0, parser->buf_size);
+ cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS;
+ cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
+ cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS;
+ cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS;
+ hdr->data_size = cpu_to_le32(data_size);
+ hdr->target_addr = addr;
+
+ /* checksum is allowed for sizes divisible by 4 */
+ if (data_size & 0x3)
+ cmd &= ~CMD_HDR_USE_CHECKSUM_MSK;
+
+ memcpy(hdr->data, cur_block, data_size);
+
+
+ if (cmd & CMD_HDR_USE_CHECKSUM_MSK) {
+
+ chksm = data_size + le32_to_cpu(addr) + cmd;
+ for (i = 0; i < data_size >> 2; i++)
+ chksm += ((u32 *)cur_block)[i];
+
+ hdr->block_chksm = cpu_to_le32(chksm);
+ LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n",
+ hdr->block_chksm);
+ }
+
+ LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, "
+ "sec_size=%zd, startAddress 0x%X\n",
+ cnt, trans_size, sent, sec_size, addr);
+
+ if (priv->dbg.dump)
+ LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size);
+
+
+ hdr->cmd = cpu_to_le32(cmd);
+ /* send it down */
+ /* TODO: add more proper sending and error checking */
+ ret = iwmct_tx(priv, 0, parser->buf, trans_size);
+ if (ret != 0) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "iwmct_tx returned %d\n", ret);
+ goto exit;
+ }
+
+ addr = cpu_to_le32(le32_to_cpu(addr) + data_size);
+ sent += data_size;
+ cur_block = p_sec + sent;
+
+ if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) {
+ LOG_INFO(priv, FW_DOWNLOAD,
+ "Block number limit is reached [%d]\n",
+ priv->dbg.blocks);
+ break;
+ }
+ }
+
+ if (sent < sec_size)
+ ret = -EINVAL;
+exit:
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return ret;
+}
+
+static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump)
+{
+ struct iwmct_parser *parser = &priv->parser;
+ struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
+ int ret;
+ u32 cmd;
+
+ LOG_INFOEX(priv, INIT, "-->\n");
+
+ memset(parser->buf, 0, parser->buf_size);
+ cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
+ if (jump) {
+ cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS;
+ hdr->target_addr = cpu_to_le32(parser->entry_point);
+ LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n",
+ parser->entry_point);
+ } else {
+ cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS;
+ LOG_INFO(priv, FW_DOWNLOAD, "last command\n");
+ }
+
+ hdr->cmd = cpu_to_le32(cmd);
+
+ LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr));
+ /* send it down */
+ /* TODO: add more proper sending and error checking */
+ ret = iwmct_tx(priv, 0, parser->buf, IWMC_SDIO_BLK_SIZE);
+ if (ret)
+ LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret);
+
+ LOG_INFOEX(priv, INIT, "<--\n");
+ return 0;
+}
+
+int iwmct_fw_load(struct iwmct_priv *priv)
+{
+ const u8 *fw_name = FW_NAME(FW_API_VER);
+ const struct firmware *raw;
+ const u8 *pdata;
+ size_t len;
+ __le32 addr;
+ int ret;
+
+ /* clear parser struct */
+ memset(&priv->parser, 0, sizeof(struct iwmct_parser));
+
+ /* get the firmware */
+ ret = request_firmware(&raw, fw_name, &priv->func->dev);
+ if (ret < 0) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n",
+ fw_name, ret);
+ goto exit;
+ }
+
+ if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n",
+ fw_name, sizeof(struct iwmct_fw_sec_hdr), raw->size);
+ goto exit;
+ }
+
+ LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", fw_name);
+
+ ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len);
+ if (ret < 0) {
+ LOG_ERROR(priv, FW_DOWNLOAD,
+ "iwmct_parser_init failed: Reason %d\n", ret);
+ goto exit;
+ }
+
+ /* checksum */
+ if (!iwmct_checksum(priv)) {
+ LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* download firmware to device */
+ while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) {
+ if (iwmct_download_section(priv, pdata, len, addr)) {
+ LOG_ERROR(priv, FW_DOWNLOAD,
+ "%s download section failed\n", fw_name);
+ ret = -EIO;
+ goto exit;
+ }
+ }
+
+ iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK));
+
+exit:
+ kfree(priv->parser.buf);
+
+ if (raw)
+ release_firmware(raw);
+
+ raw = NULL;
+
+ return ret;
+}
diff --git a/drivers/misc/iwmc3200top/fw-msg.h b/drivers/misc/iwmc3200top/fw-msg.h
new file mode 100644
index 00000000000..9e26b75bd48
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-msg.h
@@ -0,0 +1,113 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/fw-msg.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __FWMSG_H__
+#define __FWMSG_H__
+
+#define COMM_TYPE_D2H 0xFF
+#define COMM_TYPE_H2D 0xEE
+
+#define COMM_CATEGORY_OPERATIONAL 0x00
+#define COMM_CATEGORY_DEBUG 0x01
+#define COMM_CATEGORY_TESTABILITY 0x02
+#define COMM_CATEGORY_DIAGNOSTICS 0x03
+
+#define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A)
+
+#define FW_LOG_SRC_MAX 32
+#define FW_LOG_SRC_ALL 255
+
+#define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000)
+
+#define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001)
+#define CMD_TST_DEV_RESET cpu_to_le16(0x0060)
+#define CMD_TST_FUNC_RESET cpu_to_le16(0x0062)
+#define CMD_TST_IFACE_RESET cpu_to_le16(0x0064)
+#define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065)
+#define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080)
+#define CMD_TST_WAKEUP cpu_to_le16(0x0081)
+#define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082)
+#define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083)
+#define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096)
+
+#define OP_OPR_ALIVE cpu_to_le16(0x0010)
+#define OP_OPR_CMD_ACK cpu_to_le16(0x001F)
+#define OP_OPR_CMD_NACK cpu_to_le16(0x0020)
+#define OP_TST_MEM_DUMP cpu_to_le16(0x0043)
+
+#define CMD_FLAG_PADDING_256 0x80
+
+#define FW_HCMD_BLOCK_SIZE 256
+
+struct msg_hdr {
+ u8 type;
+ u8 category;
+ __le16 opcode;
+ u8 seqnum;
+ u8 flags;
+ __le16 length;
+} __attribute__((__packed__));
+
+struct log_hdr {
+ __le32 timestamp;
+ u8 severity;
+ u8 logsource;
+ __le16 reserved;
+} __attribute__((__packed__));
+
+struct mdump_hdr {
+ u8 dmpid;
+ u8 frag;
+ __le16 size;
+ __le32 addr;
+} __attribute__((__packed__));
+
+struct top_msg {
+ struct msg_hdr hdr;
+ union {
+ /* D2H messages */
+ struct {
+ struct log_hdr log_hdr;
+ u8 data[1];
+ } __attribute__((__packed__)) log;
+
+ struct {
+ struct log_hdr log_hdr;
+ struct mdump_hdr md_hdr;
+ u8 data[1];
+ } __attribute__((__packed__)) mdump;
+
+ /* H2D messages */
+ struct {
+ u8 logsource;
+ u8 sevmask;
+ } __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX];
+ struct mdump_hdr mdump_req;
+ } u;
+} __attribute__((__packed__));
+
+
+#endif /* __FWMSG_H__ */
diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h
new file mode 100644
index 00000000000..43bd510e187
--- /dev/null
+++ b/drivers/misc/iwmc3200top/iwmc3200top.h
@@ -0,0 +1,209 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/iwmc3200top.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __IWMC3200TOP_H__
+#define __IWMC3200TOP_H__
+
+#include <linux/workqueue.h>
+
+#define DRV_NAME "iwmc3200top"
+#define FW_API_VER 1
+#define _FW_NAME(api) DRV_NAME "." #api ".fw"
+#define FW_NAME(api) _FW_NAME(api)
+
+#define IWMC_SDIO_BLK_SIZE 256
+#define IWMC_DEFAULT_TR_BLK 64
+#define IWMC_SDIO_DATA_ADDR 0x0
+#define IWMC_SDIO_INTR_ENABLE_ADDR 0x14
+#define IWMC_SDIO_INTR_STATUS_ADDR 0x13
+#define IWMC_SDIO_INTR_CLEAR_ADDR 0x13
+#define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C
+
+#define COMM_HUB_HEADER_LENGTH 16
+#define LOGGER_HEADER_LENGTH 10
+
+
+#define BARKER_DNLOAD_BT_POS 0
+#define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS)
+#define BARKER_DNLOAD_GPS_POS 1
+#define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS)
+#define BARKER_DNLOAD_TOP_POS 2
+#define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS)
+#define BARKER_DNLOAD_RESERVED1_POS 3
+#define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS)
+#define BARKER_DNLOAD_JUMP_POS 4
+#define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS)
+#define BARKER_DNLOAD_SYNC_POS 5
+#define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS)
+#define BARKER_DNLOAD_RESERVED2_POS 6
+#define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS)
+#define BARKER_DNLOAD_BARKER_POS 8
+#define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS)
+
+#define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS)
+/* whole field barker */
+#define IWMC_BARKER_ACK 0xfeedbabe
+
+#define IWMC_CMD_SIGNATURE 0xcbbc
+
+#define CMD_HDR_OPCODE_POS 0
+#define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS)
+#define CMD_HDR_RESPONSE_CODE_POS 4
+#define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS)
+#define CMD_HDR_USE_CHECKSUM_POS 8
+#define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS)
+#define CMD_HDR_RESPONSE_REQUIRED_POS 9
+#define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS)
+#define CMD_HDR_DIRECT_ACCESS_POS 10
+#define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS)
+#define CMD_HDR_RESERVED_POS 11
+#define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS)
+#define CMD_HDR_SIGNATURE_POS 16
+#define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS)
+
+enum {
+ IWMC_OPCODE_PING = 0,
+ IWMC_OPCODE_READ = 1,
+ IWMC_OPCODE_WRITE = 2,
+ IWMC_OPCODE_JUMP = 3,
+ IWMC_OPCODE_REBOOT = 4,
+ IWMC_OPCODE_PERSISTENT_WRITE = 5,
+ IWMC_OPCODE_PERSISTENT_READ = 6,
+ IWMC_OPCODE_READ_MODIFY_WRITE = 7,
+ IWMC_OPCODE_LAST_COMMAND = 15
+};
+
+struct iwmct_fw_load_hdr {
+ __le32 cmd;
+ __le32 target_addr;
+ __le32 data_size;
+ __le32 block_chksm;
+ u8 data[0];
+};
+
+/**
+ * struct iwmct_fw_hdr
+ * holds all sw components versions
+ */
+struct iwmct_fw_hdr {
+ u8 top_major;
+ u8 top_minor;
+ u8 top_revision;
+ u8 gps_major;
+ u8 gps_minor;
+ u8 gps_revision;
+ u8 bt_major;
+ u8 bt_minor;
+ u8 bt_revision;
+ u8 tic_name[31];
+};
+
+/**
+ * struct iwmct_fw_sec_hdr
+ * @type: function type
+ * @data_size: section's data size
+ * @target_addr: download address
+ */
+struct iwmct_fw_sec_hdr {
+ u8 type[4];
+ __le32 data_size;
+ __le32 target_addr;
+};
+
+/**
+ * struct iwmct_parser
+ * @file: fw image
+ * @file_size: fw size
+ * @cur_pos: position in file
+ * @buf: temp buf for download
+ * @buf_size: size of buf
+ * @entry_point: address to jump in fw kick-off
+ */
+struct iwmct_parser {
+ const u8 *file;
+ size_t file_size;
+ size_t cur_pos;
+ u8 *buf;
+ size_t buf_size;
+ u32 entry_point;
+ struct iwmct_fw_hdr versions;
+};
+
+
+struct iwmct_work_struct {
+ struct list_head list;
+ ssize_t iosize;
+};
+
+struct iwmct_dbg {
+ int blocks;
+ bool dump;
+ bool jump;
+ bool direct;
+ bool checksum;
+ bool fw_download;
+ int block_size;
+ int download_trans_blks;
+
+ char label_fw[256];
+};
+
+struct iwmct_debugfs;
+
+struct iwmct_priv {
+ struct sdio_func *func;
+ struct iwmct_debugfs *dbgfs;
+ struct iwmct_parser parser;
+ atomic_t reset;
+ atomic_t dev_sync;
+ u32 trans_len;
+ u32 barker;
+ struct iwmct_dbg dbg;
+
+ /* drivers work queue */
+ struct workqueue_struct *wq;
+ struct workqueue_struct *bus_rescan_wq;
+ struct work_struct bus_rescan_worker;
+ struct work_struct isr_worker;
+
+ /* drivers wait queue */
+ wait_queue_head_t wait_q;
+
+ /* rx request list */
+ struct list_head read_req_list;
+};
+
+extern int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
+ void *src, int count);
+
+extern int iwmct_fw_load(struct iwmct_priv *priv);
+
+extern void iwmct_dbg_init_params(struct iwmct_priv *drv);
+extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv);
+extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv);
+extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len);
+
+#endif /* __IWMC3200TOP_H__ */
diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c
new file mode 100644
index 00000000000..d569279698f
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.c
@@ -0,0 +1,347 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/log.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/ctype.h>
+#include "fw-msg.h"
+#include "iwmc3200top.h"
+#include "log.h"
+
+/* Maximal hexadecimal string size of the FW memdump message */
+#define LOG_MSG_SIZE_MAX 12400
+
+/* iwmct_logdefs is a global used by log macros */
+u8 iwmct_logdefs[LOG_SRC_MAX];
+static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
+
+
+static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
+{
+ int i;
+
+ if (src < size)
+ logdefs[src] = logmask;
+ else if (src == LOG_SRC_ALL)
+ for (i = 0; i < size; i++)
+ logdefs[i] = logmask;
+ else
+ return -1;
+
+ return 0;
+}
+
+
+int iwmct_log_set_filter(u8 src, u8 logmask)
+{
+ return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
+}
+
+
+int iwmct_log_set_fw_filter(u8 src, u8 logmask)
+{
+ return _log_set_log_filter(iwmct_fw_logdefs,
+ FW_LOG_SRC_MAX, src, logmask);
+}
+
+
+static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
+ int ilen, char *pref)
+{
+ int pos = 0;
+ int i;
+ int len;
+
+ for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
+ str[pos] = pref[i];
+
+ for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
+ len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
+
+ if (i < ilen)
+ return -1;
+
+ return 0;
+}
+
+/* NOTE: This function is not thread safe.
+ Currently it's called only from sdio rx worker - no race there
+*/
+void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
+{
+ struct top_msg *msg;
+ static char logbuf[LOG_MSG_SIZE_MAX];
+
+ msg = (struct top_msg *)buf;
+
+ if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
+ LOG_ERROR(priv, FW_MSG, "Log message from TOP "
+ "is too short %d (expected %zd)\n",
+ len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
+ return;
+ }
+
+ if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
+ BIT(msg->u.log.log_hdr.severity)) ||
+ !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
+ return;
+
+ switch (msg->hdr.category) {
+ case COMM_CATEGORY_TESTABILITY:
+ if (!(iwmct_logdefs[LOG_SRC_TST] &
+ BIT(msg->u.log.log_hdr.severity)))
+ return;
+ if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
+ le16_to_cpu(msg->hdr.length) +
+ sizeof(msg->hdr), "<TST>"))
+ LOG_WARNING(priv, TST,
+ "TOP TST message is too long, truncating...");
+ LOG_WARNING(priv, TST, "%s\n", logbuf);
+ break;
+ case COMM_CATEGORY_DEBUG:
+ if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
+ LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
+ ((u8 *)msg) + sizeof(msg->hdr)
+ + sizeof(msg->u.log.log_hdr));
+ else {
+ if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
+ le16_to_cpu(msg->hdr.length)
+ + sizeof(msg->hdr),
+ "<DBG>"))
+ LOG_WARNING(priv, FW_MSG,
+ "TOP DBG message is too long,"
+ "truncating...");
+ LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
+{
+ int i, pos, len;
+ for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
+ len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
+ i, logdefs[i]);
+ pos += len;
+ }
+ buf[pos-1] = '\n';
+ buf[pos] = '\0';
+
+ if (i < logdefsz)
+ return -1;
+ return 0;
+}
+
+int log_get_filter_str(char *buf, int size)
+{
+ return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
+}
+
+int log_get_fw_filter_str(char *buf, int size)
+{
+ return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
+}
+
+#define HEXADECIMAL_RADIX 16
+#define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */
+
+ssize_t show_iwmct_log_level(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ char *str_buf;
+ int buf_size;
+ ssize_t ret;
+
+ buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
+ str_buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %d bytes\n", buf_size);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (log_get_filter_str(str_buf, buf_size) < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = sprintf(buf, "%s", str_buf);
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t store_iwmct_log_level(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ char *token, *str_buf = NULL;
+ long val;
+ ssize_t ret = count;
+ u8 src, mask;
+
+ if (!count)
+ goto exit;
+
+ str_buf = kzalloc(count, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %zd bytes\n", count);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(str_buf, buf, count);
+
+ while ((token = strsep(&str_buf, ",")) != NULL) {
+ while (isspace(*token))
+ ++token;
+ if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to convert string to long %s\n",
+ token);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ mask = val & 0xFF;
+ src = (val & 0XFF00) >> 8;
+ iwmct_log_set_filter(src, mask);
+ }
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t show_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ char *str_buf;
+ int buf_size;
+ ssize_t ret;
+
+ buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
+
+ str_buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %d bytes\n", buf_size);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = sprintf(buf, "%s", str_buf);
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
+ssize_t store_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ struct top_msg cmd;
+ char *token, *str_buf = NULL;
+ ssize_t ret = count;
+ u16 cmdlen = 0;
+ int i;
+ long val;
+ u8 src, mask;
+
+ if (!count)
+ goto exit;
+
+ str_buf = kzalloc(count, GFP_KERNEL);
+ if (!str_buf) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to allocate %zd bytes\n", count);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(str_buf, buf, count);
+
+ cmd.hdr.type = COMM_TYPE_H2D;
+ cmd.hdr.category = COMM_CATEGORY_DEBUG;
+ cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
+
+ for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
+ (i < FW_LOG_SRC_MAX); i++) {
+
+ while (isspace(*token))
+ ++token;
+
+ if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
+ LOG_ERROR(priv, DEBUGFS,
+ "failed to convert string to long %s\n",
+ token);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ mask = val & 0xFF; /* LSB */
+ src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
+ iwmct_log_set_fw_filter(src, mask);
+
+ cmd.u.logdefs[i].logsource = src;
+ cmd.u.logdefs[i].sevmask = mask;
+ }
+
+ cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
+ cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
+
+ ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
+ if (ret) {
+ LOG_ERROR(priv, DEBUGFS,
+ "Failed to send %d bytes of fwcmd, ret=%zd\n",
+ cmdlen, ret);
+ goto exit;
+ } else
+ LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
+
+ ret = count;
+
+exit:
+ kfree(str_buf);
+ return ret;
+}
+
diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h
new file mode 100644
index 00000000000..aba8121f978
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.h
@@ -0,0 +1,158 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/log.h
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+
+/* log severity:
+ * The log levels here match FW log levels
+ * so values need to stay as is */
+#define LOG_SEV_CRITICAL 0
+#define LOG_SEV_ERROR 1
+#define LOG_SEV_WARNING 2
+#define LOG_SEV_INFO 3
+#define LOG_SEV_INFOEX 4
+
+#define LOG_SEV_FILTER_ALL \
+ (BIT(LOG_SEV_CRITICAL) | \
+ BIT(LOG_SEV_ERROR) | \
+ BIT(LOG_SEV_WARNING) | \
+ BIT(LOG_SEV_INFO) | \
+ BIT(LOG_SEV_INFOEX))
+
+/* log source */
+#define LOG_SRC_INIT 0
+#define LOG_SRC_DEBUGFS 1
+#define LOG_SRC_FW_DOWNLOAD 2
+#define LOG_SRC_FW_MSG 3
+#define LOG_SRC_TST 4
+#define LOG_SRC_IRQ 5
+
+#define LOG_SRC_MAX 6
+#define LOG_SRC_ALL 0xFF
+
+/**
+ * Default intitialization runtime log level
+ */
+#ifndef LOG_SEV_FILTER_RUNTIME
+#define LOG_SEV_FILTER_RUNTIME \
+ (BIT(LOG_SEV_CRITICAL) | \
+ BIT(LOG_SEV_ERROR) | \
+ BIT(LOG_SEV_WARNING))
+#endif
+
+#ifndef FW_LOG_SEV_FILTER_RUNTIME
+#define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL
+#endif
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+/**
+ * Log macros
+ */
+
+#define priv2dev(priv) (&(priv->func)->dev)
+
+#define LOG_CRITICAL(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \
+ dev_crit(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_ERROR(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \
+ dev_err(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_WARNING(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \
+ dev_warn(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_INFO(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \
+ dev_info(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_INFOEX(priv, src, fmt, args...) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
+ dev_dbg(priv2dev(priv), "%s %d: " fmt, \
+ __func__, __LINE__, ##args); \
+} while (0)
+
+#define LOG_HEXDUMP(src, ptr, len) \
+do { \
+ if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFOEX)) \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \
+ 16, 1, ptr, len, false); \
+} while (0)
+
+void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len);
+
+extern u8 iwmct_logdefs[];
+
+int iwmct_log_set_filter(u8 src, u8 logmask);
+int iwmct_log_set_fw_filter(u8 src, u8 logmask);
+
+ssize_t show_iwmct_log_level(struct device *d,
+ struct device_attribute *attr, char *buf);
+ssize_t store_iwmct_log_level(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+ssize_t show_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr, char *buf);
+ssize_t store_iwmct_log_level_fw(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#else
+
+#define LOG_CRITICAL(priv, src, fmt, args...)
+#define LOG_ERROR(priv, src, fmt, args...)
+#define LOG_WARNING(priv, src, fmt, args...)
+#define LOG_INFO(priv, src, fmt, args...)
+#define LOG_INFOEX(priv, src, fmt, args...)
+#define LOG_HEXDUMP(src, ptr, len)
+
+static inline void iwmct_log_top_message(struct iwmct_priv *priv,
+ u8 *buf, int len) {}
+static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; }
+static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; }
+
+#endif /* CONFIG_IWMC3200TOP_DEBUG */
+
+int log_get_filter_str(char *buf, int size);
+int log_get_fw_filter_str(char *buf, int size);
+
+#endif /* __LOG_H__ */
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c
new file mode 100644
index 00000000000..fafcaa481d7
--- /dev/null
+++ b/drivers/misc/iwmc3200top/main.c
@@ -0,0 +1,678 @@
+/*
+ * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
+ * drivers/misc/iwmc3200top/main.c
+ *
+ * Copyright (C) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ *
+ *
+ * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
+ * -
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+
+#include "iwmc3200top.h"
+#include "log.h"
+#include "fw-msg.h"
+#include "debugfs.h"
+
+
+#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver"
+#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation."
+
+#define DRIVER_VERSION "0.1.62"
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_COPYRIGHT);
+MODULE_FIRMWARE(FW_NAME(FW_API_VER));
+
+/*
+ * This workers main task is to wait for OP_OPR_ALIVE
+ * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed.
+ * When OP_OPR_ALIVE received it will issue
+ * a call to "bus_rescan_devices".
+ */
+static void iwmct_rescan_worker(struct work_struct *ws)
+{
+ struct iwmct_priv *priv;
+ int ret;
+
+ priv = container_of(ws, struct iwmct_priv, bus_rescan_worker);
+
+ LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n");
+
+ ret = bus_rescan_devices(priv->func->dev.bus);
+ if (ret < 0)
+ LOG_INFO(priv, FW_DOWNLOAD, "bus_rescan_devices FAILED!!!\n");
+}
+
+static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg)
+{
+ switch (msg->hdr.opcode) {
+ case OP_OPR_ALIVE:
+ LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n");
+ queue_work(priv->bus_rescan_wq, &priv->bus_rescan_worker);
+ break;
+ default:
+ LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n",
+ msg->hdr.opcode);
+ break;
+ }
+}
+
+
+static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len)
+{
+ struct top_msg *msg;
+
+ msg = (struct top_msg *)buf;
+
+ if (msg->hdr.type != COMM_TYPE_D2H) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP with invalid message type 0x%X\n",
+ msg->hdr.type);
+ return;
+ }
+
+ if (len < sizeof(msg->hdr)) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP is too short for message header "
+ "received %d bytes, expected at least %zd bytes\n",
+ len, sizeof(msg->hdr));
+ return;
+ }
+
+ if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) {
+ LOG_ERROR(priv, FW_MSG,
+ "Message length (%d bytes) is shorter than "
+ "in header (%d bytes)\n",
+ len, le16_to_cpu(msg->hdr.length));
+ return;
+ }
+
+ switch (msg->hdr.category) {
+ case COMM_CATEGORY_OPERATIONAL:
+ op_top_message(priv, (struct top_msg *)buf);
+ break;
+
+ case COMM_CATEGORY_DEBUG:
+ case COMM_CATEGORY_TESTABILITY:
+ case COMM_CATEGORY_DIAGNOSTICS:
+ iwmct_log_top_message(priv, buf, len);
+ break;
+
+ default:
+ LOG_ERROR(priv, FW_MSG,
+ "Message from TOP with unknown category 0x%X\n",
+ msg->hdr.category);
+ break;
+ }
+}
+
+int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len)
+{
+ int ret;
+ u8 *buf;
+
+ LOG_INFOEX(priv, FW_MSG, "Sending hcmd:\n");
+
+ /* add padding to 256 for IWMC */
+ ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256;
+
+ LOG_HEXDUMP(FW_MSG, cmd, len);
+
+ if (len > FW_HCMD_BLOCK_SIZE) {
+ LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n",
+ len, FW_HCMD_BLOCK_SIZE);
+ return -1;
+ }
+
+ buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n",
+ FW_HCMD_BLOCK_SIZE);
+ return -1;
+ }
+
+ memcpy(buf, cmd, len);
+
+ sdio_claim_host(priv->func);
+ ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, buf,
+ FW_HCMD_BLOCK_SIZE);
+ sdio_release_host(priv->func);
+
+ kfree(buf);
+ return ret;
+}
+
+int iwmct_tx(struct iwmct_priv *priv, unsigned int addr,
+ void *src, int count)
+{
+ int ret;
+
+ sdio_claim_host(priv->func);
+ ret = sdio_memcpy_toio(priv->func, addr, src, count);
+ sdio_release_host(priv->func);
+
+ return ret;
+}
+
+static void iwmct_irq_read_worker(struct work_struct *ws)
+{
+ struct iwmct_priv *priv;
+ struct iwmct_work_struct *read_req;
+ __le32 *buf = NULL;
+ int ret;
+ int iosize;
+ u32 barker;
+ bool is_barker;
+
+ priv = container_of(ws, struct iwmct_priv, isr_worker);
+
+ LOG_INFO(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws);
+
+ /* --------------------- Handshake with device -------------------- */
+ sdio_claim_host(priv->func);
+
+ /* all list manipulations have to be protected by
+ * sdio_claim_host/sdio_release_host */
+ if (list_empty(&priv->read_req_list)) {
+ LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n");
+ goto exit_release;
+ }
+
+ read_req = list_entry(priv->read_req_list.next,
+ struct iwmct_work_struct, list);
+
+ list_del(&read_req->list);
+ iosize = read_req->iosize;
+ kfree(read_req);
+
+ buf = kzalloc(iosize, GFP_KERNEL);
+ if (!buf) {
+ LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize);
+ goto exit_release;
+ }
+
+ LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n",
+ iosize, buf, priv->func->num);
+
+ /* read from device */
+ ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize);
+ if (ret) {
+ LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret);
+ goto exit_release;
+ }
+
+ LOG_HEXDUMP(IRQ, (u8 *)buf, iosize);
+
+ barker = le32_to_cpu(buf[0]);
+
+ /* Verify whether it's a barker and if not - treat as regular Rx */
+ if (barker == IWMC_BARKER_ACK ||
+ (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) {
+
+ /* Valid Barker is equal on first 4 dwords */
+ is_barker = (buf[1] == buf[0]) &&
+ (buf[2] == buf[0]) &&
+ (buf[3] == buf[0]);
+
+ if (!is_barker) {
+ LOG_WARNING(priv, IRQ,
+ "Potentially inconsistent barker "
+ "%08X_%08X_%08X_%08X\n",
+ le32_to_cpu(buf[0]), le32_to_cpu(buf[1]),
+ le32_to_cpu(buf[2]), le32_to_cpu(buf[3]));
+ }
+ } else {
+ is_barker = false;
+ }
+
+ /* Handle Top CommHub message */
+ if (!is_barker) {
+ sdio_release_host(priv->func);
+ handle_top_message(priv, (u8 *)buf, iosize);
+ goto exit;
+ } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */
+ if (atomic_read(&priv->dev_sync) == 0) {
+ LOG_ERROR(priv, IRQ,
+ "ACK barker arrived out-of-sync\n");
+ goto exit_release;
+ }
+
+ /* Continuing to FW download (after Sync is completed)*/
+ atomic_set(&priv->dev_sync, 0);
+ LOG_INFO(priv, IRQ, "ACK barker arrived "
+ "- starting FW download\n");
+ } else { /* REBOOT barker */
+ LOG_INFO(priv, IRQ, "Recieved reboot barker: %x\n", barker);
+ priv->barker = barker;
+
+ if (barker & BARKER_DNLOAD_SYNC_MSK) {
+ /* Send the same barker back */
+ ret = sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR,
+ buf, iosize);
+ if (ret) {
+ LOG_ERROR(priv, IRQ,
+ "error %d echoing barker\n", ret);
+ goto exit_release;
+ }
+ LOG_INFO(priv, IRQ, "Echoing barker to device\n");
+ atomic_set(&priv->dev_sync, 1);
+ goto exit_release;
+ }
+
+ /* Continuing to FW download (without Sync) */
+ LOG_INFO(priv, IRQ, "No sync requested "
+ "- starting FW download\n");
+ }
+
+ sdio_release_host(priv->func);
+
+
+ LOG_INFO(priv, IRQ, "barker download request 0x%x is:\n", priv->barker);
+ LOG_INFO(priv, IRQ, "******* Top FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not");
+ LOG_INFO(priv, IRQ, "******* GPS FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not");
+ LOG_INFO(priv, IRQ, "******* BT FW %s requested ********\n",
+ (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not");
+
+ if (priv->dbg.fw_download)
+ iwmct_fw_load(priv);
+ else
+ LOG_ERROR(priv, IRQ, "FW download not allowed\n");
+
+ goto exit;
+
+exit_release:
+ sdio_release_host(priv->func);
+exit:
+ kfree(buf);
+ LOG_INFO(priv, IRQ, "exit iwmct_irq_read_worker\n");
+}
+
+static void iwmct_irq(struct sdio_func *func)
+{
+ struct iwmct_priv *priv;
+ int val, ret;
+ int iosize;
+ int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR;
+ struct iwmct_work_struct *read_req;
+
+ priv = sdio_get_drvdata(func);
+
+ LOG_INFO(priv, IRQ, "enter iwmct_irq\n");
+
+ /* read the function's status register */
+ val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret);
+
+ LOG_INFO(priv, IRQ, "iir value = %d, ret=%d\n", val, ret);
+
+ if (!val) {
+ LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n");
+ goto exit_clear_intr;
+ }
+
+
+ /*
+ * read 2 bytes of the transaction size
+ * IMPORTANT: sdio transaction size has to be read before clearing
+ * sdio interrupt!!!
+ */
+ val = sdio_readb(priv->func, addr++, &ret);
+ iosize = val;
+ val = sdio_readb(priv->func, addr++, &ret);
+ iosize += val << 8;
+
+ LOG_INFO(priv, IRQ, "READ size %d\n", iosize);
+
+ if (iosize == 0) {
+ LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize);
+ goto exit_clear_intr;
+ }
+
+ /* allocate a work structure to pass iosize to the worker */
+ read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL);
+ if (!read_req) {
+ LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n");
+ goto exit_clear_intr;
+ }
+
+ INIT_LIST_HEAD(&read_req->list);
+ read_req->iosize = iosize;
+
+ list_add_tail(&priv->read_req_list, &read_req->list);
+
+ /* clear the function's interrupt request bit (write 1 to clear) */
+ sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
+
+ queue_work(priv->wq, &priv->isr_worker);
+
+ LOG_INFO(priv, IRQ, "exit iwmct_irq\n");
+
+ return;
+
+exit_clear_intr:
+ /* clear the function's interrupt request bit (write 1 to clear) */
+ sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
+}
+
+
+static int blocks;
+module_param(blocks, int, 0604);
+MODULE_PARM_DESC(blocks, "max_blocks_to_send");
+
+static int dump;
+module_param(dump, bool, 0604);
+MODULE_PARM_DESC(dump, "dump_hex_content");
+
+static int jump = 1;
+module_param(jump, bool, 0604);
+
+static int direct = 1;
+module_param(direct, bool, 0604);
+
+static int checksum = 1;
+module_param(checksum, bool, 0604);
+
+static int fw_download = 1;
+module_param(fw_download, bool, 0604);
+
+static int block_size = IWMC_SDIO_BLK_SIZE;
+module_param(block_size, int, 0404);
+
+static int download_trans_blks = IWMC_DEFAULT_TR_BLK;
+module_param(download_trans_blks, int, 0604);
+
+static int rubbish_barker;
+module_param(rubbish_barker, bool, 0604);
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+static int log_level[LOG_SRC_MAX];
+static unsigned int log_level_argc;
+module_param_array(log_level, int, &log_level_argc, 0604);
+MODULE_PARM_DESC(log_level, "log_level");
+
+static int log_level_fw[FW_LOG_SRC_MAX];
+static unsigned int log_level_fw_argc;
+module_param_array(log_level_fw, int, &log_level_fw_argc, 0604);
+MODULE_PARM_DESC(log_level_fw, "log_level_fw");
+#endif
+
+void iwmct_dbg_init_params(struct iwmct_priv *priv)
+{
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+ int i;
+
+ for (i = 0; i < log_level_argc; i++) {
+ dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n",
+ i, log_level[i]);
+ iwmct_log_set_filter((log_level[i] >> 8) & 0xFF,
+ log_level[i] & 0xFF);
+ }
+ for (i = 0; i < log_level_fw_argc; i++) {
+ dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n",
+ i, log_level_fw[i]);
+ iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF,
+ log_level_fw[i] & 0xFF);
+ }
+#endif
+
+ priv->dbg.blocks = blocks;
+ LOG_INFO(priv, INIT, "blocks=%d\n", blocks);
+ priv->dbg.dump = (bool)dump;
+ LOG_INFO(priv, INIT, "dump=%d\n", dump);
+ priv->dbg.jump = (bool)jump;
+ LOG_INFO(priv, INIT, "jump=%d\n", jump);
+ priv->dbg.direct = (bool)direct;
+ LOG_INFO(priv, INIT, "direct=%d\n", direct);
+ priv->dbg.checksum = (bool)checksum;
+ LOG_INFO(priv, INIT, "checksum=%d\n", checksum);
+ priv->dbg.fw_download = (bool)fw_download;
+ LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download);
+ priv->dbg.block_size = block_size;
+ LOG_INFO(priv, INIT, "block_size=%d\n", block_size);
+ priv->dbg.download_trans_blks = download_trans_blks;
+ LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks);
+}
+
+/*****************************************************************************
+ *
+ * sysfs attributes
+ *
+ *****************************************************************************/
+static ssize_t show_iwmct_fw_version(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwmct_priv *priv = dev_get_drvdata(d);
+ return sprintf(buf, "%s\n", priv->dbg.label_fw);
+}
+static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL);
+
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO,
+ show_iwmct_log_level, store_iwmct_log_level);
+static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO,
+ show_iwmct_log_level_fw, store_iwmct_log_level_fw);
+#endif
+
+static struct attribute *iwmct_sysfs_entries[] = {
+ &dev_attr_cc_label_fw.attr,
+#ifdef CONFIG_IWMC3200TOP_DEBUG
+ &dev_attr_log_level.attr,
+ &dev_attr_log_level_fw.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group iwmct_attribute_group = {
+ .name = NULL, /* put in device directory */
+ .attrs = iwmct_sysfs_entries,
+};
+
+
+static int iwmct_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct iwmct_priv *priv;
+ int ret;
+ int val = 1;
+ int addr = IWMC_SDIO_INTR_ENABLE_ADDR;
+
+ dev_dbg(&func->dev, "enter iwmct_probe\n");
+
+ dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n",
+ jiffies_to_msecs(2147483647), HZ);
+
+ priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&func->dev, "kzalloc error\n");
+ return -ENOMEM;
+ }
+ priv->func = func;
+ sdio_set_drvdata(func, priv);
+
+
+ /* create drivers work queue */
+ priv->wq = create_workqueue(DRV_NAME "_wq");
+ priv->bus_rescan_wq = create_workqueue(DRV_NAME "_rescan_wq");
+ INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker);
+ INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker);
+
+ init_waitqueue_head(&priv->wait_q);
+
+ sdio_claim_host(func);
+ /* FIXME: Remove after it is fixed in the Boot ROM upgrade */
+ func->enable_timeout = 10;
+
+ /* In our HW, setting the block size also wakes up the boot rom. */
+ ret = sdio_set_block_size(func, priv->dbg.block_size);
+ if (ret) {
+ LOG_ERROR(priv, INIT,
+ "sdio_set_block_size() failure: %d\n", ret);
+ goto error_sdio_enable;
+ }
+
+ ret = sdio_enable_func(func);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret);
+ goto error_sdio_enable;
+ }
+
+ /* init reset and dev_sync states */
+ atomic_set(&priv->reset, 0);
+ atomic_set(&priv->dev_sync, 0);
+
+ /* init read req queue */
+ INIT_LIST_HEAD(&priv->read_req_list);
+
+ /* process configurable parameters */
+ iwmct_dbg_init_params(priv);
+ ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "Failed to register attributes and "
+ "initialize module_params\n");
+ goto error_dev_attrs;
+ }
+
+ iwmct_dbgfs_register(priv, DRV_NAME);
+
+ if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) {
+ LOG_INFO(priv, INIT,
+ "Reducing transaction to 8 blocks = 2K (from %d)\n",
+ priv->dbg.download_trans_blks);
+ priv->dbg.download_trans_blks = 8;
+ }
+ priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size;
+ LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len);
+
+ ret = sdio_claim_irq(func, iwmct_irq);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret);
+ goto error_claim_irq;
+ }
+
+
+ /* Enable function's interrupt */
+ sdio_writeb(priv->func, val, addr, &ret);
+ if (ret) {
+ LOG_ERROR(priv, INIT, "Failure writing to "
+ "Interrupt Enable Register (%d): %d\n", addr, ret);
+ goto error_enable_int;
+ }
+
+ sdio_release_host(func);
+
+ LOG_INFO(priv, INIT, "exit iwmct_probe\n");
+
+ return ret;
+
+error_enable_int:
+ sdio_release_irq(func);
+error_claim_irq:
+ sdio_disable_func(func);
+error_dev_attrs:
+ iwmct_dbgfs_unregister(priv->dbgfs);
+ sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
+error_sdio_enable:
+ sdio_release_host(func);
+ return ret;
+}
+
+static void iwmct_remove(struct sdio_func *func)
+{
+ struct iwmct_work_struct *read_req;
+ struct iwmct_priv *priv = sdio_get_drvdata(func);
+
+ priv = sdio_get_drvdata(func);
+
+ LOG_INFO(priv, INIT, "enter\n");
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+
+ /* Safely destroy osc workqueue */
+ destroy_workqueue(priv->bus_rescan_wq);
+ destroy_workqueue(priv->wq);
+
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
+ iwmct_dbgfs_unregister(priv->dbgfs);
+ sdio_release_host(func);
+
+ /* free read requests */
+ while (!list_empty(&priv->read_req_list)) {
+ read_req = list_entry(priv->read_req_list.next,
+ struct iwmct_work_struct, list);
+
+ list_del(&read_req->list);
+ kfree(read_req);
+ }
+
+ kfree(priv);
+}
+
+
+static const struct sdio_device_id iwmct_ids[] = {
+ /* Intel Wireless MultiCom 3200 Top Driver */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1404)},
+ { }, /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, iwmct_ids);
+
+static struct sdio_driver iwmct_driver = {
+ .probe = iwmct_probe,
+ .remove = iwmct_remove,
+ .name = DRV_NAME,
+ .id_table = iwmct_ids,
+};
+
+static int __init iwmct_init(void)
+{
+ int rc;
+
+ /* Default log filter settings */
+ iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME);
+ iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FILTER_ALL);
+ iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME);
+
+ rc = sdio_register_driver(&iwmct_driver);
+
+ return rc;
+}
+
+static void __exit iwmct_exit(void)
+{
+ sdio_unregister_driver(&iwmct_driver);
+}
+
+module_init(iwmct_init);
+module_exit(iwmct_exit);
+
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c
index 41c8fe2a928..ce5eda985ab 100644
--- a/drivers/misc/sgi-gru/grufile.c
+++ b/drivers/misc/sgi-gru/grufile.c
@@ -92,7 +92,7 @@ static void gru_vma_close(struct vm_area_struct *vma)
/*
* gru_file_mmap
*
- * Called when mmaping the device. Initializes the vma with a fault handler
+ * Called when mmapping the device. Initializes the vma with a fault handler
* and private data structure necessary to allocate, track, and free the
* underlying pages.
*/
diff --git a/drivers/misc/sgi-xp/xpc_main.c b/drivers/misc/sgi-xp/xpc_main.c
index fd3688a3e23..832ed4c88cf 100644
--- a/drivers/misc/sgi-xp/xpc_main.c
+++ b/drivers/misc/sgi-xp/xpc_main.c
@@ -89,48 +89,40 @@ static int xpc_disengage_max_timelimit = 120;
static ctl_table xpc_sys_xpc_hb_dir[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "hb_interval",
.data = &xpc_hb_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = &xpc_hb_min_interval,
.extra2 = &xpc_hb_max_interval},
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "hb_check_interval",
.data = &xpc_hb_check_interval,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = &xpc_hb_check_min_interval,
.extra2 = &xpc_hb_check_max_interval},
{}
};
static ctl_table xpc_sys_xpc_dir[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "hb",
.mode = 0555,
.child = xpc_sys_xpc_hb_dir},
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "disengage_timelimit",
.data = &xpc_disengage_timelimit,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
- .strategy = &sysctl_intvec,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = &xpc_disengage_min_timelimit,
.extra2 = &xpc_disengage_max_timelimit},
{}
};
static ctl_table xpc_sys_dir[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "xpc",
.mode = 0555,
.child = xpc_sys_xpc_dir},
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
index c76677afda1..b5bbe59f9c5 100644
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ b/drivers/misc/sgi-xp/xpc_uv.c
@@ -106,7 +106,8 @@ xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name)
int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
#if defined CONFIG_X86_64
- mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset);
+ mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset,
+ UV_AFFINITY_CPU);
if (mq->irq < 0) {
dev_err(xpc_part, "uv_setup_irq() returned error=%d\n",
-mq->irq);
@@ -136,7 +137,7 @@ static void
xpc_release_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq)
{
#if defined CONFIG_X86_64
- uv_teardown_irq(mq->irq, mq->mmr_blade, mq->mmr_offset);
+ uv_teardown_irq(mq->irq);
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
int mmr_pnode;
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
index 36a8d53ad2a..b8e7c5ae981 100644
--- a/drivers/mmc/card/sdio_uart.c
+++ b/drivers/mmc/card/sdio_uart.c
@@ -231,7 +231,8 @@ static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
return ret;
}
-static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
+static void sdio_uart_write_mctrl(struct sdio_uart_port *port,
+ unsigned int mctrl)
{
unsigned char mcr = 0;
@@ -387,7 +388,8 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port)
sdio_out(port, UART_IER, port->ier);
}
-static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status)
+static void sdio_uart_receive_chars(struct sdio_uart_port *port,
+ unsigned int *status)
{
struct tty_struct *tty = port->tty;
unsigned int ch, flag;
@@ -399,7 +401,7 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s
port->icount.rx++;
if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
- UART_LSR_FE | UART_LSR_OE))) {
+ UART_LSR_FE | UART_LSR_OE))) {
/*
* For statistics only
*/
@@ -417,9 +419,9 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s
* Mask off conditions which should be ignored.
*/
*status &= port->read_status_mask;
- if (*status & UART_LSR_BI) {
+ if (*status & UART_LSR_BI)
flag = TTY_BREAK;
- } else if (*status & UART_LSR_PE)
+ else if (*status & UART_LSR_PE)
flag = TTY_PARITY;
else if (*status & UART_LSR_FE)
flag = TTY_FRAME;
@@ -574,7 +576,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
*/
sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
- UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
sdio_out(port, UART_FCR, 0);
/*
@@ -635,7 +637,7 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port)
if (port->tty->termios->c_cflag & HUPCL)
sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
- /* Disable interrupts from this port */
+ /* Disable interrupts from this port */
sdio_release_irq(port->func);
port->ier = 0;
sdio_out(port, UART_IER, 0);
@@ -659,7 +661,7 @@ skip:
free_page((unsigned long)port->xmit.buf);
}
-static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
+static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
{
struct sdio_uart_port *port;
int ret;
@@ -846,7 +848,7 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t
struct sdio_uart_port *port = tty->driver_data;
unsigned int cflag = tty->termios->c_cflag;
-#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
if ((cflag ^ old_termios->c_cflag) == 0 &&
RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
@@ -925,7 +927,7 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
struct sdio_uart_port *port = tty->driver_data;
int result;
- result =sdio_uart_claim_func(port);
+ result = sdio_uart_claim_func(port);
if(!result) {
sdio_uart_update_mctrl(port, set, clear);
sdio_uart_release_func(port);
@@ -946,31 +948,31 @@ static int sdio_uart_proc_show(struct seq_file *m, void *v)
seq_printf(m, "%d: uart:SDIO", i);
if(capable(CAP_SYS_ADMIN)) {
seq_printf(m, " tx:%d rx:%d",
- port->icount.tx, port->icount.rx);
+ port->icount.tx, port->icount.rx);
if (port->icount.frame)
seq_printf(m, " fe:%d",
- port->icount.frame);
+ port->icount.frame);
if (port->icount.parity)
seq_printf(m, " pe:%d",
- port->icount.parity);
+ port->icount.parity);
if (port->icount.brk)
seq_printf(m, " brk:%d",
- port->icount.brk);
+ port->icount.brk);
if (port->icount.overrun)
seq_printf(m, " oe:%d",
- port->icount.overrun);
+ port->icount.overrun);
if (port->icount.cts)
seq_printf(m, " cts:%d",
- port->icount.cts);
+ port->icount.cts);
if (port->icount.dsr)
seq_printf(m, " dsr:%d",
- port->icount.dsr);
+ port->icount.dsr);
if (port->icount.rng)
seq_printf(m, " rng:%d",
- port->icount.rng);
+ port->icount.rng);
if (port->icount.dcd)
seq_printf(m, " dcd:%d",
- port->icount.dcd);
+ port->icount.dcd);
}
sdio_uart_port_put(port);
seq_putc(m, '\n');
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 432ae8358c8..e04b751680d 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -329,7 +329,7 @@ config MMC_SDRICOH_CS
config MMC_TMIO
tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
- depends on MFD_TMIO || MFD_ASIC3
+ depends on MFD_TMIO || MFD_ASIC3 || SUPERH
help
This provides support for the SD/MMC cell found in TC6393XB,
T7L66XB and also HTC ASIC3
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 705a5894a6b..90d168ad03b 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -56,7 +56,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
clk = 255;
host->cclk = host->mclk / (2 * (clk + 1));
}
- if (host->hw_designer == 0x80)
+ if (host->hw_designer == AMBA_VENDOR_ST)
clk |= MCI_FCEN; /* Bug fix in ST IP block */
clk |= MCI_CLK_ENABLE;
/* This hasn't proven to be worthwhile */
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index b8fd7af1cee..5f970e253e5 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -30,12 +30,12 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <mach/board.h>
-#include <mach/mmc.h>
+#include <plat/board.h>
+#include <plat/mmc.h>
#include <mach/gpio.h>
-#include <mach/dma.h>
-#include <mach/mux.h>
-#include <mach/fpga.h>
+#include <plat/dma.h>
+#include <plat/mux.h>
+#include <plat/fpga.h>
#define OMAP_MMC_REG_CMD 0x00
#define OMAP_MMC_REG_ARGL 0x04
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 0aecaaebef3..4b232251890 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -30,11 +30,11 @@
#include <linux/mmc/core.h>
#include <linux/io.h>
#include <linux/semaphore.h>
-#include <mach/dma.h>
+#include <plat/dma.h>
#include <mach/hardware.h>
-#include <mach/board.h>
-#include <mach/mmc.h>
-#include <mach/cpu.h>
+#include <plat/board.h>
+#include <plat/mmc.h>
+#include <plat/cpu.h>
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSCONFIG 0x0010
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 9fb480bb0e0..bb47ff465c0 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -43,6 +43,9 @@
#define NR_SG 1
#define CLKRT_OFF (~0)
+#define mmc_has_26MHz() (cpu_is_pxa300() || cpu_is_pxa310() \
+ || cpu_is_pxa935())
+
struct pxamci_host {
struct mmc_host *mmc;
spinlock_t lock;
@@ -457,7 +460,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_enable(host->clk);
if (ios->clock == 26000000) {
- /* to support 26MHz on pxa300/pxa310 */
+ /* to support 26MHz */
host->clkrt = 7;
} else {
/* to handle (19.5MHz, 26MHz) */
@@ -608,8 +611,7 @@ static int pxamci_probe(struct platform_device *pdev)
* Calculate minimum clock rate, rounding up.
*/
mmc->f_min = (host->clkrate + 63) / 64;
- mmc->f_max = (cpu_is_pxa300() || cpu_is_pxa310()) ? 26000000
- : host->clkrate;
+ mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate;
pxamci_init_ocr(host);
@@ -618,7 +620,7 @@ static int pxamci_probe(struct platform_device *pdev)
if (!cpu_is_pxa25x()) {
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
host->cmdat |= CMDAT_SDIO_INT_EN;
- if (cpu_is_pxa300() || cpu_is_pxa310())
+ if (mmc_has_26MHz())
mmc->caps |= MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_SD_HIGHSPEED;
}
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 99b74a35102..941a4d35ef8 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1360,7 +1360,7 @@ static struct mmc_host_ops s3cmci_ops = {
static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
/* This is currently here to avoid a number of if (host->pdata)
- * checks. Any zero fields to ensure reaonable defaults are picked. */
+ * checks. Any zero fields to ensure reasonable defaults are picked. */
};
#ifdef CONFIG_CPU_FREQ
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 9408099eec4..35c6a23b183 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -1,5 +1,3 @@
-# drivers/mtd/chips/Kconfig
-
menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n
@@ -242,4 +240,3 @@ config MTD_XIP
then say N.
endmenu
-
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index c222514bb70..35081ce77fb 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -1,5 +1,3 @@
-# drivers/mtd/maps/Kconfig
-
menu "Self-contained MTD device drivers"
depends on MTD!=n
@@ -308,4 +306,3 @@ config MTD_DOCPROBE_55AA
you have managed to wipe the first block.
endmenu
-
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index 3aa05cd18ea..592016a0668 100644
--- a/drivers/mtd/devices/slram.c
+++ b/drivers/mtd/devices/slram.c
@@ -18,7 +18,7 @@
to specify the offset instead of the absolute address
NOTE:
- With slram it's only possible to map a contigous memory region. Therfore
+ With slram it's only possible to map a contiguous memory region. Therefore
if there's a device mapped somewhere in the region specified slram will
fail to load (see kernel log if modprobe fails).
diff --git a/drivers/mtd/lpddr/Kconfig b/drivers/mtd/lpddr/Kconfig
index 5a401d8047a..265f969817e 100644
--- a/drivers/mtd/lpddr/Kconfig
+++ b/drivers/mtd/lpddr/Kconfig
@@ -1,5 +1,3 @@
-# drivers/mtd/chips/Kconfig
-
menu "LPDDR flash memory drivers"
depends on MTD!=n
@@ -20,4 +18,3 @@ config MTD_QINFO_PROBE
families of devices. This serves similar purpose of CFI on legacy
Flash products
endmenu
-
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 14be0755d7c..847e214ade5 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -1,5 +1,3 @@
-# drivers/mtd/maps/Kconfig
-
menu "Mapping drivers for chip access"
depends on MTD!=n
@@ -389,9 +387,9 @@ config MTD_IXP2000
depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP2000
help
This enables MTD access to flash devices on platforms based
- on Intel's IXP2000 family of network processors such as the
- IXDP425 and Coyote. If you have an IXP2000 based board and
- would like to use the flash chips on it, say 'Y'.
+ on Intel's IXP2000 family of network processors. If you have an
+ IXP2000 based board and would like to use the flash chips on it,
+ say 'Y'.
config MTD_FORTUNET
tristate "CFI Flash device mapped on the FortuNet board"
diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c
index a24478102b1..ead0b2fab67 100644
--- a/drivers/mtd/maps/omap_nor.c
+++ b/drivers/mtd/maps/omap_nor.c
@@ -45,7 +45,7 @@
#include <asm/io.h>
#include <mach/hardware.h>
#include <asm/mach/flash.h>
-#include <mach/tc.h>
+#include <plat/tc.h>
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { /* "RedBoot", */ "cmdlinepart", NULL };
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index d600c2deff7..689d6a79ffc 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -118,11 +118,9 @@ static caddr_t remap_window(struct map_info *map, unsigned long to)
DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x",
dev->offset, mrq.CardOffset);
mrq.Page = 0;
- ret = pcmcia_map_mem_page(win, &mrq);
- if (ret != 0) {
- cs_error(dev->p_dev, MapMemPage, ret);
+ ret = pcmcia_map_mem_page(dev->p_dev, win, &mrq);
+ if (ret != 0)
return NULL;
- }
dev->offset = mrq.CardOffset;
}
return dev->win_base + (to & (dev->win_size-1));
@@ -327,8 +325,6 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on)
DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp);
ret = pcmcia_modify_configuration(link, &mod);
- if (ret != 0)
- cs_error(link, ModifyConfiguration, ret);
}
@@ -348,107 +344,116 @@ static void pcmciamtd_release(struct pcmcia_device *link)
iounmap(dev->win_base);
dev->win_base = NULL;
}
- pcmcia_release_window(link->win);
+ pcmcia_release_window(link, link->win);
}
pcmcia_disable_device(link);
}
-static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name)
+#ifdef CONFIG_MTD_DEBUG
+static int pcmciamtd_cistpl_format(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data)
{
- int rc;
- tuple_t tuple;
cisparse_t parse;
- u_char buf[64];
-
- tuple.Attributes = 0;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = RETURN_FIRST_TUPLE;
-
- rc = pcmcia_get_first_tuple(link, &tuple);
- while (rc == 0) {
- rc = pcmcia_get_tuple_data(link, &tuple);
- if (rc != 0) {
- cs_error(link, GetTupleData, rc);
- break;
- }
- rc = pcmcia_parse_tuple(&tuple, &parse);
- if (rc != 0) {
- cs_error(link, ParseTuple, rc);
- break;
- }
- switch(tuple.TupleCode) {
- case CISTPL_FORMAT: {
- cistpl_format_t *t = &parse.format;
- (void)t; /* Shut up, gcc */
- DEBUG(2, "Format type: %u, Error Detection: %u, offset = %u, length =%u",
- t->type, t->edc, t->offset, t->length);
- break;
+ if (!pcmcia_parse_tuple(tuple, &parse)) {
+ cistpl_format_t *t = &parse.format;
+ (void)t; /* Shut up, gcc */
+ DEBUG(2, "Format type: %u, Error Detection: %u, offset = %u, length =%u",
+ t->type, t->edc, t->offset, t->length);
+ }
+ return -ENOSPC;
+}
- }
+static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data)
+{
+ cisparse_t parse;
+ int i;
- case CISTPL_DEVICE: {
- cistpl_device_t *t = &parse.device;
- int i;
- DEBUG(2, "Common memory:");
- dev->pcmcia_map.size = t->dev[0].size;
- for(i = 0; i < t->ndev; i++) {
- DEBUG(2, "Region %d, type = %u", i, t->dev[i].type);
- DEBUG(2, "Region %d, wp = %u", i, t->dev[i].wp);
- DEBUG(2, "Region %d, speed = %u ns", i, t->dev[i].speed);
- DEBUG(2, "Region %d, size = %u bytes", i, t->dev[i].size);
- }
- break;
- }
+ if (!pcmcia_parse_tuple(tuple, &parse)) {
+ cistpl_jedec_t *t = &parse.jedec;
+ for (i = 0; i < t->nid; i++)
+ DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info);
+ }
+ return -ENOSPC;
+}
+#endif
- case CISTPL_VERS_1: {
- cistpl_vers_1_t *t = &parse.version_1;
- int i;
- if(t->ns) {
- dev->mtd_name[0] = '\0';
- for(i = 0; i < t->ns; i++) {
- if(i)
- strcat(dev->mtd_name, " ");
- strcat(dev->mtd_name, t->str+t->ofs[i]);
- }
- }
- DEBUG(2, "Found name: %s", dev->mtd_name);
- break;
- }
+static int pcmciamtd_cistpl_device(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data)
+{
+ struct pcmciamtd_dev *dev = priv_data;
+ cisparse_t parse;
+ cistpl_device_t *t = &parse.device;
+ int i;
- case CISTPL_JEDEC_C: {
- cistpl_jedec_t *t = &parse.jedec;
- int i;
- for(i = 0; i < t->nid; i++) {
- DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info);
- }
- break;
- }
+ if (pcmcia_parse_tuple(tuple, &parse))
+ return -EINVAL;
+
+ DEBUG(2, "Common memory:");
+ dev->pcmcia_map.size = t->dev[0].size;
+ /* from here on: DEBUG only */
+ for (i = 0; i < t->ndev; i++) {
+ DEBUG(2, "Region %d, type = %u", i, t->dev[i].type);
+ DEBUG(2, "Region %d, wp = %u", i, t->dev[i].wp);
+ DEBUG(2, "Region %d, speed = %u ns", i, t->dev[i].speed);
+ DEBUG(2, "Region %d, size = %u bytes", i, t->dev[i].size);
+ }
+ return 0;
+}
- case CISTPL_DEVICE_GEO: {
- cistpl_device_geo_t *t = &parse.device_geo;
- int i;
- dev->pcmcia_map.bankwidth = t->geo[0].buswidth;
- for(i = 0; i < t->ngeo; i++) {
- DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth);
- DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block);
- DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block);
- DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block);
- DEBUG(2, "region: %d partition = %u", i, t->geo[i].partition);
- DEBUG(2, "region: %d interleave = %u", i, t->geo[i].interleave);
- }
- break;
- }
+static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data)
+{
+ struct pcmciamtd_dev *dev = priv_data;
+ cisparse_t parse;
+ cistpl_device_geo_t *t = &parse.device_geo;
+ int i;
- default:
- DEBUG(2, "Unknown tuple code %d", tuple.TupleCode);
- }
+ if (pcmcia_parse_tuple(tuple, &parse))
+ return -EINVAL;
+
+ dev->pcmcia_map.bankwidth = t->geo[0].buswidth;
+ /* from here on: DEBUG only */
+ for (i = 0; i < t->ngeo; i++) {
+ DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth);
+ DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block);
+ DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block);
+ DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block);
+ DEBUG(2, "region: %d partition = %u", i, t->geo[i].partition);
+ DEBUG(2, "region: %d interleave = %u", i, t->geo[i].interleave);
+ }
+ return 0;
+}
+
+
+static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name)
+{
+ int i;
- rc = pcmcia_get_next_tuple(link, &tuple);
+ if (p_dev->prod_id[0]) {
+ dev->mtd_name[0] = '\0';
+ for (i = 0; i < 4; i++) {
+ if (i)
+ strcat(dev->mtd_name, " ");
+ if (p_dev->prod_id[i])
+ strcat(dev->mtd_name, p_dev->prod_id[i]);
+ }
+ DEBUG(2, "Found name: %s", dev->mtd_name);
}
+
+#ifdef CONFIG_MTD_DEBUG
+ pcmcia_loop_tuple(p_dev, CISTPL_FORMAT, pcmciamtd_cistpl_format, NULL);
+ pcmcia_loop_tuple(p_dev, CISTPL_JEDEC_C, pcmciamtd_cistpl_jedec, NULL);
+#endif
+ pcmcia_loop_tuple(p_dev, CISTPL_DEVICE, pcmciamtd_cistpl_device, dev);
+ pcmcia_loop_tuple(p_dev, CISTPL_DEVICE_GEO, pcmciamtd_cistpl_geo, dev);
+
if(!dev->pcmcia_map.size)
dev->pcmcia_map.size = MAX_PCMCIA_ADDR;
@@ -481,16 +486,12 @@ static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link,
* MTD device available to the system.
*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int pcmciamtd_config(struct pcmcia_device *link)
{
struct pcmciamtd_dev *dev = link->priv;
struct mtd_info *mtd = NULL;
cs_status_t status;
win_req_t req;
- int last_ret = 0, last_fn = 0;
int ret;
int i;
static char *probes[] = { "jedec_probe", "cfi_probe" };
@@ -529,7 +530,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
int ret;
DEBUG(2, "requesting window with size = %dKiB memspeed = %d",
req.Size >> 10, req.AccessSpeed);
- ret = pcmcia_request_window(&link, &req, &link->win);
+ ret = pcmcia_request_window(link, &req, &link->win);
DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size);
if(ret) {
req.Size >>= 1;
@@ -577,7 +578,6 @@ static int pcmciamtd_config(struct pcmcia_device *link)
DEBUG(2, "Setting Configuration");
ret = pcmcia_request_configuration(link, &link->conf);
if (ret != 0) {
- cs_error(link, RequestConfiguration, ret);
if (dev->win_base) {
iounmap(dev->win_base);
dev->win_base = NULL;
@@ -652,8 +652,7 @@ static int pcmciamtd_config(struct pcmcia_device *link)
link->dev_node = &dev->node;
return 0;
- cs_failed:
- cs_error(link, last_fn, last_ret);
+ failed:
err("CS Error, exiting");
pcmciamtd_release(link);
return -ENODEV;
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 8ca17a3e96e..64e2b379a35 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -59,12 +59,14 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->readsect(dev, block, buf))
return -EIO;
+ rq_flush_dcache_pages(req);
return 0;
case WRITE:
if (!tr->writesect)
return -EIO;
+ rq_flush_dcache_pages(req);
for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->writesect(dev, block, buf))
return -EIO;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2fda0b61524..0e35e1aefd2 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,5 +1,3 @@
-# drivers/mtd/nand/Kconfig
-
menuconfig MTD_NAND
tristate "NAND Device Support"
depends on MTD
@@ -358,7 +356,7 @@ endchoice
config MTD_NAND_PXA3xx
tristate "Support for NAND flash devices on PXA3xx"
- depends on MTD_NAND && PXA3xx
+ depends on MTD_NAND && (PXA3xx || ARCH_MMP)
help
This enables the driver for the NAND flash device found on
PXA3xx processors
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index 005b91f096f..2548e1065bf 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -25,7 +25,7 @@
#include <mach/hardware.h>
#include <asm/sizes.h>
#include <mach/gpio.h>
-#include <mach/board-ams-delta.h>
+#include <plat/board-ams-delta.h>
/*
* MTD structure for E3 (Delta)
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index e51c1ed7ac1..b126cf88747 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -1056,7 +1056,7 @@ static struct nand_ecclayout doc200x_oobinfo = {
};
/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
- On sucessful return, buf will contain a copy of the media header for
+ On successful return, buf will contain a copy of the media header for
further processing. id is the string to scan for, and will presumably be
either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media
header. The page #s of the found media headers are placed in mh0_page and
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
index db7ae9d6a29..92320a64327 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/nand_ecc.c
@@ -475,7 +475,7 @@ int __nand_correct_data(unsigned char *buf,
*
* The b2 shift is there to get rid of the lowest two bits.
* We could also do addressbits[b2] >> 1 but for the
- * performace it does not make any difference
+ * performance it does not make any difference
*/
if (eccsize_mult == 1)
byte_addr = (addressbits[b1] << 4) + addressbits[b0];
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 090ab87086b..1bb799f0125 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -18,9 +18,9 @@
#include <linux/mtd/partitions.h>
#include <linux/io.h>
-#include <mach/dma.h>
-#include <mach/gpmc.h>
-#include <mach/nand.h>
+#include <plat/dma.h>
+#include <plat/gpmc.h>
+#include <plat/nand.h>
#define GPMC_IRQ_STATUS 0x18
#define GPMC_ECC_CONFIG 0x1F4
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 6ea520ae241..1a5a0365c98 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
@@ -22,7 +23,7 @@
#include <linux/irq.h>
#include <mach/dma.h>
-#include <mach/pxa3xx_nand.h>
+#include <plat/pxa3xx_nand.h>
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
@@ -84,10 +85,6 @@
#define NDCB0_CMD1_MASK (0xff)
#define NDCB0_ADDR_CYC_SHIFT (16)
-/* dma-able I/O address for the NAND data and commands */
-#define NDCB0_DMA_ADDR (0x43100048)
-#define NDDB_DMA_ADDR (0x43100040)
-
/* macros for registers read/write */
#define nand_writel(info, off, val) \
__raw_writel((val), (info)->mmio_base + (off))
@@ -123,6 +120,7 @@ struct pxa3xx_nand_info {
struct clk *clk;
void __iomem *mmio_base;
+ unsigned long mmio_phys;
unsigned int buf_start;
unsigned int buf_count;
@@ -228,13 +226,35 @@ static struct pxa3xx_nand_flash samsung512MbX16 = {
.chip_id = 0x46ec,
};
+static struct pxa3xx_nand_flash samsung2GbX8 = {
+ .timing = &samsung512MbX16_timing,
+ .cmdset = &smallpage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 2048,
+ .chip_id = 0xdaec,
+};
+
+static struct pxa3xx_nand_flash samsung32GbX8 = {
+ .timing = &samsung512MbX16_timing,
+ .cmdset = &smallpage_cmdset,
+ .page_per_block = 128,
+ .page_size = 4096,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 8192,
+ .chip_id = 0xd7ec,
+};
+
static struct pxa3xx_nand_timing micron_timing = {
.tCH = 10,
.tCS = 25,
.tWH = 15,
.tWP = 25,
.tRH = 15,
- .tRP = 25,
+ .tRP = 30,
.tR = 25000,
.tWHR = 60,
.tAR = 10,
@@ -262,6 +282,28 @@ static struct pxa3xx_nand_flash micron1GbX16 = {
.chip_id = 0xb12c,
};
+static struct pxa3xx_nand_flash micron4GbX8 = {
+ .timing = &micron_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 8,
+ .dfc_width = 8,
+ .num_blocks = 4096,
+ .chip_id = 0xdc2c,
+};
+
+static struct pxa3xx_nand_flash micron4GbX16 = {
+ .timing = &micron_timing,
+ .cmdset = &largepage_cmdset,
+ .page_per_block = 64,
+ .page_size = 2048,
+ .flash_width = 16,
+ .dfc_width = 16,
+ .num_blocks = 4096,
+ .chip_id = 0xcc2c,
+};
+
static struct pxa3xx_nand_timing stm2GbX16_timing = {
.tCH = 10,
.tCS = 35,
@@ -287,8 +329,12 @@ static struct pxa3xx_nand_flash stm2GbX16 = {
static struct pxa3xx_nand_flash *builtin_flash_types[] = {
&samsung512MbX16,
+ &samsung2GbX8,
+ &samsung32GbX8,
&micron1GbX8,
&micron1GbX16,
+ &micron4GbX8,
+ &micron4GbX16,
&stm2GbX16,
};
#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */
@@ -489,7 +535,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
switch (info->state) {
case STATE_PIO_WRITING:
__raw_writesl(info->mmio_base + NDDB, info->data_buff,
- info->data_size << 2);
+ DIV_ROUND_UP(info->data_size, 4));
enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
@@ -501,7 +547,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
break;
case STATE_PIO_READING:
__raw_readsl(info->mmio_base + NDDB, info->data_buff,
- info->data_size << 2);
+ DIV_ROUND_UP(info->data_size, 4));
break;
default:
printk(KERN_ERR "%s: invalid state %d\n", __func__,
@@ -523,11 +569,11 @@ static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out)
if (dir_out) {
desc->dsadr = info->data_buff_phys;
- desc->dtadr = NDDB_DMA_ADDR;
+ desc->dtadr = info->mmio_phys + NDDB;
desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG;
} else {
desc->dtadr = info->data_buff_phys;
- desc->dsadr = NDDB_DMA_ADDR;
+ desc->dsadr = info->mmio_phys + NDDB;
desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;
}
@@ -669,6 +715,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
/* disable HW ECC to get all the OOB data */
info->buf_count = mtd->writesize + mtd->oobsize;
info->buf_start = mtd->writesize + column;
+ memset(info->data_buff, 0xFF, info->buf_count);
if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr))
break;
@@ -1239,13 +1286,17 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
ret = -ENODEV;
goto fail_free_res;
}
+ info->mmio_phys = r->start;
ret = pxa3xx_nand_init_buff(info);
if (ret)
goto fail_free_io;
- ret = request_irq(IRQ_NAND, pxa3xx_nand_irq, IRQF_DISABLED,
- pdev->name, info);
+ /* initialize all interrupts to be disabled */
+ disable_int(info, NDSR_MASK);
+
+ ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED,
+ pdev->name, info);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto fail_free_buf;
@@ -1271,7 +1322,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts);
fail_free_irq:
- free_irq(IRQ_NAND, info);
+ free_irq(irq, info);
fail_free_buf:
if (use_dma) {
pxa_free_dma(info->data_dma_ch);
@@ -1296,12 +1347,15 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
struct mtd_info *mtd = platform_get_drvdata(pdev);
struct pxa3xx_nand_info *info = mtd->priv;
struct resource *r;
+ int irq;
platform_set_drvdata(pdev, NULL);
del_mtd_device(mtd);
del_mtd_partitions(mtd);
- free_irq(IRQ_NAND, info);
+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0)
+ free_irq(irq, info);
if (use_dma) {
pxa_free_dma(info->data_dma_ch);
dma_free_writecombine(&pdev->dev, info->data_buff_size,
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 11dc7e69c4f..68b5b3a486a 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -875,7 +875,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
* @info: The controller instance.
* @nmtd: The driver version of the MTD instance.
*
- * This routine is called after the chip probe has succesfully completed
+ * This routine is called after the chip probe has successfully completed
* and the relevant per-chip information updated. This call ensure that
* we update the internal state accordingly.
*
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index a38f580c2bb..3a9f1578460 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -1,7 +1,3 @@
-#
-# linux/drivers/mtd/onenand/Kconfig
-#
-
menuconfig MTD_ONENAND
tristate "OneNAND Device Support"
depends on MTD
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index 0108ed42e87..86c4f6dcdc6 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -36,13 +36,13 @@
#include <linux/io.h>
#include <asm/mach/flash.h>
-#include <mach/gpmc.h>
-#include <mach/onenand.h>
+#include <plat/gpmc.h>
+#include <plat/onenand.h>
#include <mach/gpio.h>
-#include <mach/dma.h>
+#include <plat/dma.h>
-#include <mach/board.h>
+#include <plat/board.h>
#define DRIVER_NAME "omap2-onenand"
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index b1cd7a1a219..0a8c7ea764a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -1,5 +1,3 @@
-# drivers/mtd/ubi/Kconfig
-
menu "UBI - Unsorted block images"
depends on MTD
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index 88a72e9c8be..277786ebaa2 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -22,6 +22,8 @@
#include <linux/module.h>
#include <linux/err.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
#include <asm/div64.h>
#include "ubi.h"
@@ -280,6 +282,44 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
/**
+ * ubi_open_volume_path - open UBI volume by its character device node path.
+ * @pathname: volume character device node path
+ * @mode: open mode
+ *
+ * This function is similar to 'ubi_open_volume()', but opens a volume the path
+ * to its character device node.
+ */
+struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode)
+{
+ int error, ubi_num, vol_id;
+ struct ubi_volume_desc *ret;
+ struct inode *inode;
+ struct path path;
+
+ dbg_gen("open volume %s, mode %d", pathname, mode);
+
+ if (!pathname || !*pathname)
+ return ERR_PTR(-EINVAL);
+
+ error = kern_path(pathname, LOOKUP_FOLLOW, &path);
+ if (error)
+ return ERR_PTR(error);
+
+ inode = path.dentry->d_inode;
+ ubi_num = ubi_major2num(imajor(inode));
+ vol_id = iminor(inode) - 1;
+
+ if (vol_id >= 0 && ubi_num >= 0)
+ ret = ubi_open_volume(ubi_num, vol_id, mode);
+ else
+ ret = ERR_PTR(-ENODEV);
+
+ path_put(&path);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ubi_open_volume_path);
+
+/**
* ubi_close_volume - close UBI volume.
* @desc: volume descriptor
*/
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
index 74fdc40c862..c1d7b880c79 100644
--- a/drivers/mtd/ubi/upd.c
+++ b/drivers/mtd/ubi/upd.c
@@ -147,12 +147,14 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
}
if (bytes == 0) {
+ err = ubi_wl_flush(ubi);
+ if (err)
+ return err;
+
err = clear_update_marker(ubi, vol, 0);
if (err)
return err;
- err = ubi_wl_flush(ubi);
- if (!err)
- vol->updating = 0;
+ vol->updating = 0;
}
vol->upd_buf = vmalloc(ubi->leb_size);
@@ -362,16 +364,16 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
ubi_assert(vol->upd_received <= vol->upd_bytes);
if (vol->upd_received == vol->upd_bytes) {
+ err = ubi_wl_flush(ubi);
+ if (err)
+ return err;
/* The update is finished, clear the update marker */
err = clear_update_marker(ubi, vol, vol->upd_bytes);
if (err)
return err;
- err = ubi_wl_flush(ubi);
- if (err == 0) {
- vol->updating = 0;
- err = to_write;
- vfree(vol->upd_buf);
- }
+ vol->updating = 0;
+ err = to_write;
+ vfree(vol->upd_buf);
}
return err;
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index f60309175ef..4d4cad393dc 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -249,11 +249,11 @@ static int __init el1_probe1(struct net_device *dev, int ioaddr)
* for the Sager NP943 prefix.
*/
- if (station_addr[0] == 0x02 && station_addr[1] == 0x60
- && station_addr[2] == 0x8c)
+ if (station_addr[0] == 0x02 && station_addr[1] == 0x60 &&
+ station_addr[2] == 0x8c)
mname = "3c501";
- else if (station_addr[0] == 0x00 && station_addr[1] == 0x80
- && station_addr[2] == 0xC8)
+ else if (station_addr[0] == 0x00 && station_addr[1] == 0x80 &&
+ station_addr[2] == 0xC8)
mname = "NP943";
else {
release_region(ioaddr, EL1_IO_EXTENT);
@@ -345,7 +345,7 @@ static int el_open(struct net_device *dev)
if (el_debug > 2)
pr_debug("%s: Doing el_open()...\n", dev->name);
- retval = request_irq(dev->irq, &el_interrupt, 0, dev->name, dev);
+ retval = request_irq(dev->irq, el_interrupt, 0, dev->name, dev);
if (retval)
return retval;
diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c
index c71e12d05f6..66e0323c183 100644
--- a/drivers/net/3c503.c
+++ b/drivers/net/3c503.c
@@ -214,8 +214,8 @@ el2_probe1(struct net_device *dev, int ioaddr)
iobase_reg = inb(ioaddr+0x403);
membase_reg = inb(ioaddr+0x404);
/* ASIC location registers should be 0 or have only a single bit set. */
- if ( (iobase_reg & (iobase_reg - 1))
- || (membase_reg & (membase_reg - 1))) {
+ if ((iobase_reg & (iobase_reg - 1)) ||
+ (membase_reg & (membase_reg - 1))) {
retval = -ENODEV;
goto out1;
}
@@ -291,8 +291,8 @@ el2_probe1(struct net_device *dev, int ioaddr)
writel(0xba5eba5e, mem_base);
for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) {
writel(test_val, mem_base + i);
- if (readl(mem_base) != 0xba5eba5e
- || readl(mem_base + i) != test_val) {
+ if (readl(mem_base) != 0xba5eba5e ||
+ readl(mem_base + i) != test_val) {
pr_warning("3c503: memory failure or memory address conflict.\n");
dev->mem_start = 0;
ei_status.name = "3c503-PIO";
@@ -397,9 +397,10 @@ el2_open(struct net_device *dev)
unsigned long cookie = probe_irq_on();
outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
outb_p(0x00, E33G_IDCFR);
- if (*irqp == probe_irq_off(cookie) /* It's a good IRQ line! */
- && ((retval = request_irq(dev->irq = *irqp,
- eip_interrupt, 0, dev->name, dev)) == 0))
+ if (*irqp == probe_irq_off(cookie) && /* It's a good IRQ line! */
+ ((retval = request_irq(dev->irq = *irqp,
+ eip_interrupt, 0,
+ dev->name, dev)) == 0))
break;
} else {
if (retval != -EBUSY)
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
index a21c9d15ef8..9257d7ce037 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/3c505.c
@@ -886,7 +886,7 @@ static int elp_open(struct net_device *dev)
/*
* install our interrupt service routine
*/
- if ((retval = request_irq(dev->irq, &elp_interrupt, 0, dev->name, dev))) {
+ if ((retval = request_irq(dev->irq, elp_interrupt, 0, dev->name, dev))) {
pr_err("%s: could not allocate IRQ%d\n", dev->name, dev->irq);
return retval;
}
diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c
index a6dc8bcbc7d..fbc231153e5 100644
--- a/drivers/net/3c507.c
+++ b/drivers/net/3c507.c
@@ -399,7 +399,7 @@ static int __init el16_probe1(struct net_device *dev, int ioaddr)
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
- irqval = request_irq(irq, &el16_interrupt, 0, DRV_NAME, dev);
+ irqval = request_irq(irq, el16_interrupt, 0, DRV_NAME, dev);
if (irqval) {
pr_cont("\n");
pr_err("3c507: unable to get IRQ %d (irqval=%d).\n", irq, irqval);
@@ -836,8 +836,8 @@ static void el16_rx(struct net_device *dev)
void __iomem *data_frame = lp->base + data_buffer_addr;
ushort pkt_len = readw(data_frame);
- if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
- || (pkt_len & 0xC000) != 0xC000) {
+ if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 ||
+ (pkt_len & 0xC000) != 0xC000) {
pr_err("%s: Rx frame at %#x corrupted, "
"status %04x cmd %04x next %04x "
"data-buf @%04x %04x.\n",
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index 3b00a4e927a..9d85efce591 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -253,9 +253,9 @@ static int el3_isa_id_sequence(__be16 *phys_addr)
This check is needed in order not to register them twice. */
for (i = 0; i < el3_cards; i++) {
struct el3_private *lp = netdev_priv(el3_devs[i]);
- if (lp->type == EL3_PNP
- && !memcmp(phys_addr, el3_devs[i]->dev_addr,
- ETH_ALEN)) {
+ if (lp->type == EL3_PNP &&
+ !memcmp(phys_addr, el3_devs[i]->dev_addr,
+ ETH_ALEN)) {
if (el3_debug > 3)
pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
phys_addr[0] & 0xff, phys_addr[0] >> 8,
@@ -780,7 +780,7 @@ el3_open(struct net_device *dev)
outw(RxReset, ioaddr + EL3_CMD);
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
- i = request_irq(dev->irq, &el3_interrupt, 0, dev->name, dev);
+ i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
if (i)
return i;
@@ -835,8 +835,8 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
#ifndef final_version
{ /* Error-checking code, delete someday. */
ushort status = inw(ioaddr + EL3_STATUS);
- if (status & 0x0001 /* IRQ line active, missed one. */
- && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
+ if (status & 0x0001 && /* IRQ line active, missed one. */
+ inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
pr_debug("%s: Missed interrupt, status then %04x now %04x"
" Tx %2.2x Rx %4.4x.\n", dev->name, status,
inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c
index 4adcb950f5f..063b049ffe5 100644
--- a/drivers/net/3c515.c
+++ b/drivers/net/3c515.c
@@ -764,13 +764,14 @@ static int corkscrew_open(struct net_device *dev)
/* Use the now-standard shared IRQ implementation. */
if (vp->capabilities == 0x11c7) {
/* Corkscrew: Cannot share ISA resources. */
- if (dev->irq == 0
- || dev->dma == 0
- || request_irq(dev->irq, &corkscrew_interrupt, 0,
- vp->product_name, dev)) return -EAGAIN;
+ if (dev->irq == 0 ||
+ dev->dma == 0 ||
+ request_irq(dev->irq, corkscrew_interrupt, 0,
+ vp->product_name, dev))
+ return -EAGAIN;
enable_dma(dev->dma);
set_dma_mode(dev->dma, DMA_MODE_CASCADE);
- } else if (request_irq(dev->irq, &corkscrew_interrupt, IRQF_SHARED,
+ } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
vp->product_name, dev)) {
return -EAGAIN;
}
@@ -1368,8 +1369,8 @@ static int boomerang_rx(struct net_device *dev)
/* Check if the packet is long enough to just accept without
copying to a properly sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 4)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 4)) != NULL) {
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
memcpy(skb_put(skb, pkt_len),
diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c
index cb0b730799b..27d80ca5e4c 100644
--- a/drivers/net/3c523.c
+++ b/drivers/net/3c523.c
@@ -288,7 +288,7 @@ static int elmc_open(struct net_device *dev)
elmc_id_attn586(); /* disable interrupts */
- ret = request_irq(dev->irq, &elmc_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM,
+ ret = request_irq(dev->irq, elmc_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM,
dev->name, dev);
if (ret) {
pr_err("%s: couldn't get irq %d\n", dev->name, dev->irq);
diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c
index 6021e6dded8..36c4191e7bc 100644
--- a/drivers/net/3c527.c
+++ b/drivers/net/3c527.c
@@ -443,7 +443,7 @@ static int __init mc32_probe1(struct net_device *dev, int slot)
* Grab the IRQ
*/
- err = request_irq(dev->irq, &mc32_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM, DRV_NAME, dev);
+ err = request_irq(dev->irq, mc32_interrupt, IRQF_SHARED | IRQF_SAMPLE_RANDOM, DRV_NAME, dev);
if (err) {
release_region(dev->base_addr, MC32_IO_EXTENT);
pr_err("%s: unable to get IRQ %d.\n", DRV_NAME, dev->irq);
@@ -1168,8 +1168,8 @@ static void mc32_rx_ring(struct net_device *dev)
/* Try to save time by avoiding a copy on big frames */
- if ((length > RX_COPYBREAK)
- && ((newskb=dev_alloc_skb(1532)) != NULL))
+ if ((length > RX_COPYBREAK) &&
+ ((newskb=dev_alloc_skb(1532)) != NULL))
{
skb=lp->rx_ring[rx_ring_tail].skb;
skb_put(skb, length);
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 975e25b19eb..78b7167a8ce 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -1942,8 +1942,8 @@ vortex_error(struct net_device *dev, int status)
if (status & TxComplete) { /* Really "TxError" for us. */
tx_status = ioread8(ioaddr + TxStatus);
/* Presumably a tx-timeout. We must merely re-enable. */
- if (vortex_debug > 2
- || (tx_status != 0x88 && vortex_debug > 0)) {
+ if (vortex_debug > 2 ||
+ (tx_status != 0x88 && vortex_debug > 0)) {
pr_err("%s: Transmit error, Tx status register %2.2x.\n",
dev->name, tx_status);
if (tx_status == 0x82) {
@@ -2560,7 +2560,7 @@ boomerang_rx(struct net_device *dev)
struct sk_buff *skb;
entry = vp->dirty_rx % RX_RING_SIZE;
if (vp->rx_skbuff[entry] == NULL) {
- skb = netdev_alloc_skb(dev, PKT_BUF_SZ + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(dev, PKT_BUF_SZ);
if (skb == NULL) {
static unsigned long last_jif;
if (time_after(jiffies, last_jif + 10 * HZ)) {
@@ -2572,7 +2572,6 @@ boomerang_rx(struct net_device *dev)
break; /* Bad news! */
}
- skb_reserve(skb, NET_IP_ALIGN);
vp->rx_ring[entry].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data, PKT_BUF_SZ, PCI_DMA_FROMDEVICE));
vp->rx_skbuff[entry] = skb;
}
diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
index 83a1922e68e..3f452bcbfb9 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/8139cp.c
@@ -549,14 +549,12 @@ rx_status_loop:
pr_debug("%s: rx slot %d status 0x%x len %d\n",
dev->name, rx_tail, status, len);
- new_skb = netdev_alloc_skb(dev, buflen + NET_IP_ALIGN);
+ new_skb = netdev_alloc_skb_ip_align(dev, buflen);
if (!new_skb) {
dev->stats.rx_dropped++;
goto rx_next;
}
- skb_reserve(new_skb, NET_IP_ALIGN);
-
dma_unmap_single(&cp->pdev->dev, mapping,
buflen, PCI_DMA_FROMDEVICE);
@@ -911,8 +909,8 @@ static void __cp_set_rx_mode (struct net_device *dev)
AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
AcceptAllPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter perfectly -- accept all multicasts. */
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
@@ -1057,12 +1055,10 @@ static int cp_refill_rx(struct cp_private *cp)
struct sk_buff *skb;
dma_addr_t mapping;
- skb = netdev_alloc_skb(dev, cp->rx_buf_sz + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(dev, cp->rx_buf_sz);
if (!skb)
goto err_out;
- skb_reserve(skb, NET_IP_ALIGN);
-
mapping = dma_map_single(&cp->pdev->dev, skb->data,
cp->rx_buf_sz, PCI_DMA_FROMDEVICE);
cp->rx_skb[i] = skb;
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index 4a362875502..25f7339daab 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -1549,8 +1549,8 @@ static inline void rtl8139_thread_iter (struct net_device *dev,
mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
if (!tp->mii.force_media && mii_lpa != 0xffff) {
- int duplex = (mii_lpa & LPA_100FULL)
- || (mii_lpa & 0x01C0) == 0x0040;
+ int duplex = ((mii_lpa & LPA_100FULL) ||
+ (mii_lpa & 0x01C0) == 0x0040);
if (tp->mii.full_duplex != duplex) {
tp->mii.full_duplex = duplex;
@@ -1936,8 +1936,8 @@ static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
RTL_R16 (RxBufAddr),
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
- while (netif_running(dev) && received < budget
- && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
+ while (netif_running(dev) && received < budget &&
+ (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
u32 ring_offset = cur_rx % RX_BUF_LEN;
u32 rx_status;
unsigned int pkt_size;
@@ -2004,9 +2004,8 @@ no_early_rx:
/* Malloc up new buffer, compatible with net-2e. */
/* Omit the four octet CRC from the length. */
- skb = netdev_alloc_skb(dev, pkt_size + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(dev, pkt_size);
if (likely(skb)) {
- skb_reserve (skb, NET_IP_ALIGN); /* 16 byte align the IP fields. */
#if RX_BUF_IDX == 3
wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
#else
@@ -2522,8 +2521,8 @@ static void __set_rx_mode (struct net_device *dev)
AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
AcceptAllPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter perfectly -- accept all multicasts. */
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
diff --git a/drivers/net/82596.c b/drivers/net/82596.c
index ea6b139b812..1663bc9e45d 100644
--- a/drivers/net/82596.c
+++ b/drivers/net/82596.c
@@ -19,7 +19,7 @@
TBD:
* look at deferring rx frames rather than discarding (as per tulip)
* handle tx ring full as per tulip
- * performace test to tune rx_copybreak
+ * performance test to tune rx_copybreak
Most of my modifications relate to the braindead big-endian
implementation by Intel. When the i596 is operating in
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index b2f71f79baa..0bbd5ae4986 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1001,7 +1001,7 @@ config SMC911X
config SMSC911X
tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
- depends on ARM || SUPERH || BLACKFIN
+ depends on ARM || SUPERH || BLACKFIN || MIPS
select CRC32
select MII
select PHYLIB
@@ -3235,7 +3235,7 @@ config VIRTIO_NET
config VMXNET3
tristate "VMware VMXNET3 ethernet driver"
- depends on PCI && X86 && INET
+ depends on PCI && INET
help
This driver supports VMware's vmxnet3 virtual ethernet NIC.
To compile this driver as a module, choose M here: the
diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c
index 4e6359fff0e..766aabfdfc7 100644
--- a/drivers/net/amd8111e.c
+++ b/drivers/net/amd8111e.c
@@ -1633,8 +1633,13 @@ static int amd8111e_enable_link_change(struct amd8111e_priv* lp)
readl(lp->mmio + CMD7);
return 0;
}
-/* This function is called when a packet transmission fails to complete within a resonable period, on the assumption that an interrupts have been failed or the interface is locked up. This function will reinitialize the hardware */
+/*
+ * This function is called when a packet transmission fails to complete
+ * within a reasonable period, on the assumption that an interrupt have
+ * failed or the interface is locked up. This function will reinitialize
+ * the hardware.
+ */
static void amd8111e_tx_timeout(struct net_device *dev)
{
struct amd8111e_priv* lp = netdev_priv(dev);
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index b5dc7f55072..73b38c204eb 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -120,7 +120,7 @@ static int irq = 5; /* Default IRQ */
* DAYNA driver mode:
* Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95,
* Farallon PhoneNET PC III, Farallon PhoneNET PC II
- * Other cards possibly supported mode unkown though:
+ * Other cards possibly supported mode unknown though:
* Dayna DL2000 (Full length), COPS LT/M (Micro-Channel)
*
* Cards NOT supported by this driver but supported by the ltpc.c
@@ -328,7 +328,7 @@ static int __init cops_probe1(struct net_device *dev, int ioaddr)
/* Reserve any actual interrupt. */
if (dev->irq) {
- retval = request_irq(dev->irq, &cops_interrupt, 0, dev->name, dev);
+ retval = request_irq(dev->irq, cops_interrupt, 0, dev->name, dev);
if (retval)
goto err_out;
}
diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c
index aaf14d306a4..eb0448b03f4 100644
--- a/drivers/net/appletalk/ipddp.c
+++ b/drivers/net/appletalk/ipddp.c
@@ -230,9 +230,9 @@ static int ipddp_delete(struct ipddp_route *rt)
spin_lock_bh(&ipddp_route_lock);
while((tmp = *r) != NULL)
{
- if(tmp->ip == rt->ip
- && tmp->at.s_net == rt->at.s_net
- && tmp->at.s_node == rt->at.s_node)
+ if(tmp->ip == rt->ip &&
+ tmp->at.s_net == rt->at.s_net &&
+ tmp->at.s_node == rt->at.s_node)
{
*r = tmp->next;
spin_unlock_bh(&ipddp_route_lock);
@@ -255,9 +255,9 @@ static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
for(f = ipddp_route_list; f != NULL; f = f->next)
{
- if(f->ip == rt->ip
- && f->at.s_net == rt->at.s_net
- && f->at.s_node == rt->at.s_node)
+ if(f->ip == rt->ip &&
+ f->at.s_net == rt->at.s_net &&
+ f->at.s_node == rt->at.s_node)
return (f);
}
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index 08760baece7..dbfbd3b7ff8 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -1158,7 +1158,7 @@ struct net_device * __init ltpc_probe(void)
}
/* grab it and don't let go :-) */
- if (irq && request_irq( irq, &ltpc_interrupt, 0, "ltpc", dev) >= 0)
+ if (irq && request_irq( irq, ltpc_interrupt, 0, "ltpc", dev) >= 0)
{
(void) inb_p(io+7); /* enable interrupts from board */
(void) inb_p(io+7); /* and reset irq line */
diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c
index e3082a9350f..e6afab2455b 100644
--- a/drivers/net/arcnet/arc-rimi.c
+++ b/drivers/net/arcnet/arc-rimi.c
@@ -156,7 +156,7 @@ static int __init arcrimi_found(struct net_device *dev)
}
/* reserve the irq */
- if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
+ if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
iounmap(p);
release_mem_region(dev->mem_start, MIRROR_SIZE);
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
@@ -174,9 +174,9 @@ static int __init arcrimi_found(struct net_device *dev)
* 2k (or there are no mirrors at all) but on some, it's 4k.
*/
mirror_size = MIRROR_SIZE;
- if (readb(p) == TESTvalue
- && check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0
- && check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
+ if (readb(p) == TESTvalue &&
+ check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 &&
+ check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1)
mirror_size = 2 * MIRROR_SIZE;
first_mirror = shmem - mirror_size;
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c
index 75a57256090..d8f02930375 100644
--- a/drivers/net/arcnet/arcnet.c
+++ b/drivers/net/arcnet/arcnet.c
@@ -301,8 +301,8 @@ static int choose_mtu(void)
/* choose the smallest MTU of all available encaps */
for (count = 0; count < 256; count++) {
- if (arc_proto_map[count] != &arc_proto_null
- && arc_proto_map[count]->mtu < mtu) {
+ if (arc_proto_map[count] != &arc_proto_null &&
+ arc_proto_map[count]->mtu < mtu) {
mtu = arc_proto_map[count]->mtu;
}
}
@@ -953,13 +953,13 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
* > RECON_THRESHOLD/min;
* then print a warning message.
*/
- if (!lp->network_down
- && (lp->last_recon - lp->first_recon) <= HZ * 60
- && lp->num_recons >= RECON_THRESHOLD) {
+ if (!lp->network_down &&
+ (lp->last_recon - lp->first_recon) <= HZ * 60 &&
+ lp->num_recons >= RECON_THRESHOLD) {
lp->network_down = 1;
BUGMSG(D_NORMAL, "many reconfigurations detected: cabling problem?\n");
- } else if (!lp->network_down
- && lp->last_recon - lp->first_recon > HZ * 60) {
+ } else if (!lp->network_down &&
+ lp->last_recon - lp->first_recon > HZ * 60) {
/* reset counters if we've gone for over a minute. */
lp->first_recon = lp->last_recon;
lp->num_recons = 1;
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
index 651275a5f3d..0a74f21409c 100644
--- a/drivers/net/arcnet/com20020.c
+++ b/drivers/net/arcnet/com20020.c
@@ -200,7 +200,7 @@ int com20020_found(struct net_device *dev, int shared)
outb(dev->dev_addr[0], _XREG);
/* reserve the irq */
- if (request_irq(dev->irq, &arcnet_interrupt, shared,
+ if (request_irq(dev->irq, arcnet_interrupt, shared,
"arcnet (COM20020)", dev)) {
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
return -ENODEV;
diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c
index 89de29b3b1d..28dea518d55 100644
--- a/drivers/net/arcnet/com90io.c
+++ b/drivers/net/arcnet/com90io.c
@@ -238,7 +238,7 @@ static int __init com90io_found(struct net_device *dev)
int err;
/* Reserve the irq */
- if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) {
+ if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) {
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
return -ENODEV;
}
diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c
index d762fe46251..112e230cb13 100644
--- a/drivers/net/arcnet/com90xx.c
+++ b/drivers/net/arcnet/com90xx.c
@@ -501,7 +501,7 @@ static int __init com90xx_found(int ioaddr, int airq, u_long shmem, void __iomem
goto err_free_dev;
/* reserve the irq */
- if (request_irq(airq, &arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
+ if (request_irq(airq, arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", airq);
goto err_release_mem;
}
diff --git a/drivers/net/ariadne.h b/drivers/net/ariadne.h
index bb613f292e0..727be5cdd1e 100644
--- a/drivers/net/ariadne.h
+++ b/drivers/net/ariadne.h
@@ -244,7 +244,7 @@ struct Am79C960 {
#define DLNKTST 0x0010 /* Disable Link Status */
#define DAPC 0x0008 /* Disable Automatic Polarity Correction */
#define MENDECL 0x0004 /* MENDEC Loopback Mode */
-#define LRTTSEL 0x0002 /* Low Receive Treshold/Transmit Mode Select */
+#define LRTTSEL 0x0002 /* Low Receive Threshold/Transmit Mode Select */
#define PORTSEL1 0x0001 /* Port Select Bits */
#define PORTSEL2 0x8000 /* Port Select Bits */
#define INTL 0x4000 /* Internal Loopback */
diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c
index 2a7b7745cc5..be256b34cea 100644
--- a/drivers/net/arm/ks8695net.c
+++ b/drivers/net/arm/ks8695net.c
@@ -35,11 +35,13 @@
#include <mach/regs-switch.h>
#include <mach/regs-misc.h>
+#include <asm/mach/irq.h>
+#include <mach/regs-irq.h>
#include "ks8695net.h"
#define MODULENAME "ks8695_ether"
-#define MODULEVERSION "1.01"
+#define MODULEVERSION "1.02"
/*
* Transmit and device reset timeout, default 5 seconds.
@@ -95,6 +97,9 @@ struct ks8695_skbuff {
#define MAX_RX_DESC 16
#define MAX_RX_DESC_MASK 0xf
+/*napi_weight have better more than rx DMA buffers*/
+#define NAPI_WEIGHT 64
+
#define MAX_RXBUF_SIZE 0x700
#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
@@ -120,6 +125,7 @@ enum ks8695_dtype {
* @dev: The platform device object for this interface
* @dtype: The type of this device
* @io_regs: The ioremapped registers for this interface
+ * @napi : Add support NAPI for Rx
* @rx_irq_name: The textual name of the RX IRQ from the platform data
* @tx_irq_name: The textual name of the TX IRQ from the platform data
* @link_irq_name: The textual name of the link IRQ from the
@@ -143,6 +149,7 @@ enum ks8695_dtype {
* @rx_ring_dma: The DMA mapped equivalent of rx_ring
* @rx_buffers: The sk_buff mappings for the RX ring
* @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ * @rx_lock: A lock to protect Rx irq function
* @msg_enable: The flags for which messages to emit
*/
struct ks8695_priv {
@@ -152,6 +159,8 @@ struct ks8695_priv {
enum ks8695_dtype dtype;
void __iomem *io_regs;
+ struct napi_struct napi;
+
const char *rx_irq_name, *tx_irq_name, *link_irq_name;
int rx_irq, tx_irq, link_irq;
@@ -172,6 +181,7 @@ struct ks8695_priv {
dma_addr_t rx_ring_dma;
struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
int next_rx_desc_read;
+ spinlock_t rx_lock;
int msg_enable;
};
@@ -392,29 +402,74 @@ ks8695_tx_irq(int irq, void *dev_id)
}
/**
+ * ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit
+ * @ksp: Private data for the KS8695 Ethernet
+ *
+ * For KS8695 document:
+ * Interrupt Enable Register (offset 0xE204)
+ * Bit29 : WAN MAC Receive Interrupt Enable
+ * Bit16 : LAN MAC Receive Interrupt Enable
+ * Interrupt Status Register (Offset 0xF208)
+ * Bit29: WAN MAC Receive Status
+ * Bit16: LAN MAC Receive Status
+ * So, this Rx interrrupt enable/status bit number is equal
+ * as Rx IRQ number.
+ */
+static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp)
+{
+ return ksp->rx_irq;
+}
+
+/**
* ks8695_rx_irq - Receive IRQ handler
* @irq: The IRQ which went off (ignored)
* @dev_id: The net_device for the interrupt
*
- * Process the RX ring, passing any received packets up to the
- * host. If we received anything other than errors, we then
- * refill the ring.
+ * Inform NAPI that packet reception needs to be scheduled
*/
+
static irqreturn_t
ks8695_rx_irq(int irq, void *dev_id)
{
struct net_device *ndev = (struct net_device *)dev_id;
struct ks8695_priv *ksp = netdev_priv(ndev);
+
+ spin_lock(&ksp->rx_lock);
+
+ if (napi_schedule_prep(&ksp->napi)) {
+ unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN);
+ unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
+ /*disable rx interrupt*/
+ status &= ~mask_bit;
+ writel(status , KS8695_IRQ_VA + KS8695_INTEN);
+ __napi_schedule(&ksp->napi);
+ }
+
+ spin_unlock(&ksp->rx_lock);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8695_rx - Receive packets called by NAPI poll method
+ * @ksp: Private data for the KS8695 Ethernet
+ * @budget: The max packets would be receive
+ */
+
+static int ks8695_rx(struct ks8695_priv *ksp, int budget)
+{
+ struct net_device *ndev = ksp->ndev;
struct sk_buff *skb;
int buff_n;
u32 flags;
int pktlen;
int last_rx_processed = -1;
+ int received = 0;
buff_n = ksp->next_rx_desc_read;
- do {
- if (ksp->rx_buffers[buff_n].skb &&
- !(ksp->rx_ring[buff_n].status & cpu_to_le32(RDES_OWN))) {
+ while (received < budget
+ && ksp->rx_buffers[buff_n].skb
+ && (!(ksp->rx_ring[buff_n].status &
+ cpu_to_le32(RDES_OWN)))) {
rmb();
flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
/* Found an SKB which we own, this means we
@@ -464,7 +519,7 @@ ks8695_rx_irq(int irq, void *dev_id)
/* Relinquish the SKB to the network layer */
skb_put(skb, pktlen);
skb->protocol = eth_type_trans(skb, ndev);
- netif_rx(skb);
+ netif_receive_skb(skb);
/* Record stats */
ndev->stats.rx_packets++;
@@ -478,29 +533,55 @@ rx_failure:
/* Give the ring entry back to the hardware */
ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
rx_finished:
+ received++;
/* And note this as processed so we can start
* from here next time
*/
last_rx_processed = buff_n;
- } else {
- /* Ran out of things to process, stop now */
- break;
- }
- buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
- } while (buff_n != ksp->next_rx_desc_read);
-
- /* And note which RX descriptor we last did anything with */
- if (likely(last_rx_processed != -1))
- ksp->next_rx_desc_read =
- (last_rx_processed + 1) & MAX_RX_DESC_MASK;
-
+ buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+ /*And note which RX descriptor we last did */
+ if (likely(last_rx_processed != -1))
+ ksp->next_rx_desc_read =
+ (last_rx_processed + 1) &
+ MAX_RX_DESC_MASK;
+ }
/* And refill the buffers */
ks8695_refill_rxbuffers(ksp);
- /* Kick the RX DMA engine, in case it became suspended */
+ /* Kick the RX DMA engine, in case it became
+ * suspended */
ks8695_writereg(ksp, KS8695_DRSC, 0);
+ return received;
+}
- return IRQ_HANDLED;
+
+/**
+ * ks8695_poll - Receive packet by NAPI poll method
+ * @ksp: Private data for the KS8695 Ethernet
+ * @budget: The remaining number packets for network subsystem
+ *
+ * Invoked by the network core when it requests for new
+ * packets from the driver
+ */
+static int ks8695_poll(struct napi_struct *napi, int budget)
+{
+ struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi);
+ unsigned long work_done;
+
+ unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN);
+ unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
+
+ work_done = ks8695_rx(ksp, budget);
+
+ if (work_done < budget) {
+ unsigned long flags;
+ spin_lock_irqsave(&ksp->rx_lock, flags);
+ /*enable rx interrupt*/
+ writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN);
+ __napi_complete(napi);
+ spin_unlock_irqrestore(&ksp->rx_lock, flags);
+ }
+ return work_done;
}
/**
@@ -1253,6 +1334,7 @@ ks8695_stop(struct net_device *ndev)
struct ks8695_priv *ksp = netdev_priv(ndev);
netif_stop_queue(ndev);
+ napi_disable(&ksp->napi);
netif_carrier_off(ndev);
ks8695_shutdown(ksp);
@@ -1287,6 +1369,7 @@ ks8695_open(struct net_device *ndev)
return ret;
}
+ napi_enable(&ksp->napi);
netif_start_queue(ndev);
return 0;
@@ -1472,6 +1555,8 @@ ks8695_probe(struct platform_device *pdev)
SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT);
+
/* Retrieve the default MAC addr from the chip. */
/* The bootloader should have left it in there for us. */
@@ -1505,6 +1590,7 @@ ks8695_probe(struct platform_device *pdev)
/* And initialise the queue's lock */
spin_lock_init(&ksp->txq_lock);
+ spin_lock_init(&ksp->rx_lock);
/* Specify the RX DMA ring buffer */
ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
@@ -1626,6 +1712,7 @@ ks8695_drv_remove(struct platform_device *pdev)
struct ks8695_priv *ksp = netdev_priv(ndev);
platform_set_drvdata(pdev, NULL);
+ netif_napi_del(&ksp->napi);
unregister_netdev(ndev);
ks8695_release_device(ksp);
diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c
index 25e2627eb11..b7f3866d546 100644
--- a/drivers/net/arm/w90p910_ether.c
+++ b/drivers/net/arm/w90p910_ether.c
@@ -160,8 +160,8 @@ struct w90p910_ether {
struct mii_if_info mii;
struct timer_list check_timer;
void __iomem *reg;
- unsigned int rxirq;
- unsigned int txirq;
+ int rxirq;
+ int txirq;
unsigned int cur_tx;
unsigned int cur_rx;
unsigned int finish_tx;
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c
index 544d5af6950..b14f4799d5d 100644
--- a/drivers/net/at1700.c
+++ b/drivers/net/at1700.c
@@ -350,13 +350,13 @@ static int __init at1700_probe1(struct net_device *dev, int ioaddr)
slot = -1;
/* We must check for the EEPROM-config boards first, else accessing
IOCONFIG0 will move the board! */
- if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr
- && read_eeprom(ioaddr, 4) == 0x0000
- && (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400)
+ if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr &&
+ read_eeprom(ioaddr, 4) == 0x0000 &&
+ (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400)
is_at1700 = 1;
- else if (inb(ioaddr + SAPROM ) == 0x00
- && inb(ioaddr + SAPROM + 1) == 0x00
- && inb(ioaddr + SAPROM + 2) == 0x0e)
+ else if (inb(ioaddr + SAPROM ) == 0x00 &&
+ inb(ioaddr + SAPROM + 1) == 0x00 &&
+ inb(ioaddr + SAPROM + 2) == 0x0e)
is_fmv18x = 1;
else {
goto err_out;
@@ -468,7 +468,7 @@ found:
lp->jumpered = is_fmv18x;
lp->mca_slot = slot;
/* Snarf the interrupt vector now. */
- ret = request_irq(irq, &net_interrupt, 0, DRV_NAME, dev);
+ ret = request_irq(irq, net_interrupt, 0, DRV_NAME, dev);
if (ret) {
printk(KERN_ERR "AT1700 at %#3x is unusable due to a "
"conflict on IRQ %d.\n",
@@ -839,8 +839,8 @@ set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) {
memset(mc_filter, 0xff, sizeof(mc_filter));
outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
- } else if (dev->mc_count > MC_FILTERBREAK
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if (dev->mc_count > MC_FILTERBREAK ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter perfectly -- accept all multicasts. */
memset(mc_filter, 0xff, sizeof(mc_filter));
outb(2, ioaddr + RX_MODE); /* Use normal mode. */
diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c
index 0c0deceb693..c5721cb3826 100644
--- a/drivers/net/atarilance.c
+++ b/drivers/net/atarilance.c
@@ -930,8 +930,8 @@ static irqreturn_t lance_interrupt( int irq, void *dev_id )
}
#endif
- if (lp->tx_full && (netif_queue_stopped(dev))
- && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ if (lp->tx_full && (netif_queue_stopped(dev)) &&
+ dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
/* The ring is no longer full, clear tbusy. */
lp->tx_full = 0;
netif_wake_queue (dev);
diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h
index 2a1120ad2e7..a348a22551d 100644
--- a/drivers/net/atl1c/atl1c.h
+++ b/drivers/net/atl1c/atl1c.h
@@ -470,12 +470,28 @@ struct atl1c_ring_header {
struct atl1c_buffer {
struct sk_buff *skb; /* socket buffer */
u16 length; /* rx buffer length */
- u16 state; /* state of buffer */
-#define ATL1_BUFFER_FREE 0
-#define ATL1_BUFFER_BUSY 1
+ u16 flags; /* information of buffer */
+#define ATL1C_BUFFER_FREE 0x0001
+#define ATL1C_BUFFER_BUSY 0x0002
+#define ATL1C_BUFFER_STATE_MASK 0x0003
+
+#define ATL1C_PCIMAP_SINGLE 0x0004
+#define ATL1C_PCIMAP_PAGE 0x0008
+#define ATL1C_PCIMAP_TYPE_MASK 0x000C
+
dma_addr_t dma;
};
+#define ATL1C_SET_BUFFER_STATE(buff, state) do { \
+ ((buff)->flags) &= ~ATL1C_BUFFER_STATE_MASK; \
+ ((buff)->flags) |= (state); \
+ } while (0)
+
+#define ATL1C_SET_PCIMAP_TYPE(buff, type) do { \
+ ((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \
+ ((buff)->flags) |= (type); \
+ } while (0)
+
/* transimit packet descriptor (tpd) ring */
struct atl1c_tpd_ring {
void *desc; /* descriptor ring virtual address */
diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c
index 1372e9a99f5..6eb9241cee0 100644
--- a/drivers/net/atl1c/atl1c_main.c
+++ b/drivers/net/atl1c/atl1c_main.c
@@ -710,6 +710,29 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter)
return 0;
}
+static inline void atl1c_clean_buffer(struct pci_dev *pdev,
+ struct atl1c_buffer *buffer_info, int in_irq)
+{
+ if (buffer_info->flags & ATL1C_BUFFER_FREE)
+ return;
+ if (buffer_info->dma) {
+ if (buffer_info->flags & ATL1C_PCIMAP_SINGLE)
+ pci_unmap_single(pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ else if (buffer_info->flags & ATL1C_PCIMAP_PAGE)
+ pci_unmap_page(pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ }
+ if (buffer_info->skb) {
+ if (in_irq)
+ dev_kfree_skb_irq(buffer_info->skb);
+ else
+ dev_kfree_skb(buffer_info->skb);
+ }
+ buffer_info->dma = 0;
+ buffer_info->skb = NULL;
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
+}
/*
* atl1c_clean_tx_ring - Free Tx-skb
* @adapter: board private structure
@@ -725,22 +748,12 @@ static void atl1c_clean_tx_ring(struct atl1c_adapter *adapter,
ring_count = tpd_ring->count;
for (index = 0; index < ring_count; index++) {
buffer_info = &tpd_ring->buffer_info[index];
- if (buffer_info->state == ATL1_BUFFER_FREE)
- continue;
- if (buffer_info->dma)
- pci_unmap_single(pdev, buffer_info->dma,
- buffer_info->length,
- PCI_DMA_TODEVICE);
- if (buffer_info->skb)
- dev_kfree_skb(buffer_info->skb);
- buffer_info->dma = 0;
- buffer_info->skb = NULL;
- buffer_info->state = ATL1_BUFFER_FREE;
+ atl1c_clean_buffer(pdev, buffer_info, 0);
}
/* Zero out Tx-buffers */
memset(tpd_ring->desc, 0, sizeof(struct atl1c_tpd_desc) *
- ring_count);
+ ring_count);
atomic_set(&tpd_ring->next_to_clean, 0);
tpd_ring->next_to_use = 0;
}
@@ -760,16 +773,7 @@ static void atl1c_clean_rx_ring(struct atl1c_adapter *adapter)
for (i = 0; i < adapter->num_rx_queues; i++) {
for (j = 0; j < rfd_ring[i].count; j++) {
buffer_info = &rfd_ring[i].buffer_info[j];
- if (buffer_info->state == ATL1_BUFFER_FREE)
- continue;
- if (buffer_info->dma)
- pci_unmap_single(pdev, buffer_info->dma,
- buffer_info->length,
- PCI_DMA_FROMDEVICE);
- if (buffer_info->skb)
- dev_kfree_skb(buffer_info->skb);
- buffer_info->state = ATL1_BUFFER_FREE;
- buffer_info->skb = NULL;
+ atl1c_clean_buffer(pdev, buffer_info, 0);
}
/* zero out the descriptor ring */
memset(rfd_ring[i].desc, 0, rfd_ring[i].size);
@@ -796,7 +800,8 @@ static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter)
atomic_set(&tpd_ring[i].next_to_clean, 0);
buffer_info = tpd_ring[i].buffer_info;
for (j = 0; j < tpd_ring->count; j++)
- buffer_info[i].state = ATL1_BUFFER_FREE;
+ ATL1C_SET_BUFFER_STATE(&buffer_info[i],
+ ATL1C_BUFFER_FREE);
}
for (i = 0; i < adapter->num_rx_queues; i++) {
rfd_ring[i].next_to_use = 0;
@@ -805,7 +810,7 @@ static void atl1c_init_ring_ptrs(struct atl1c_adapter *adapter)
rrd_ring[i].next_to_clean = 0;
for (j = 0; j < rfd_ring[i].count; j++) {
buffer_info = &rfd_ring[i].buffer_info[j];
- buffer_info->state = ATL1_BUFFER_FREE;
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_FREE);
}
}
}
@@ -1447,6 +1452,7 @@ static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter,
struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *)
&adapter->tpd_ring[type];
struct atl1c_buffer *buffer_info;
+ struct pci_dev *pdev = adapter->pdev;
u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean);
u16 hw_next_to_clean;
u16 shift;
@@ -1462,16 +1468,7 @@ static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter,
while (next_to_clean != hw_next_to_clean) {
buffer_info = &tpd_ring->buffer_info[next_to_clean];
- if (buffer_info->state == ATL1_BUFFER_BUSY) {
- pci_unmap_page(adapter->pdev, buffer_info->dma,
- buffer_info->length, PCI_DMA_TODEVICE);
- buffer_info->dma = 0;
- if (buffer_info->skb) {
- dev_kfree_skb_irq(buffer_info->skb);
- buffer_info->skb = NULL;
- }
- buffer_info->state = ATL1_BUFFER_FREE;
- }
+ atl1c_clean_buffer(pdev, buffer_info, 1);
if (++next_to_clean == tpd_ring->count)
next_to_clean = 0;
atomic_set(&tpd_ring->next_to_clean, next_to_clean);
@@ -1543,7 +1540,7 @@ static irqreturn_t atl1c_intr(int irq, void *data)
if (status & ISR_OVER)
if (netif_msg_intr(adapter))
dev_warn(&pdev->dev,
- "TX/RX over flow (status = 0x%x)\n",
+ "TX/RX overflow (status = 0x%x)\n",
status & ISR_OVER);
/* link event */
@@ -1587,7 +1584,7 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid
buffer_info = &rfd_ring->buffer_info[rfd_next_to_use];
next_info = &rfd_ring->buffer_info[next_next];
- while (next_info->state == ATL1_BUFFER_FREE) {
+ while (next_info->flags & ATL1C_BUFFER_FREE) {
rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use);
skb = dev_alloc_skb(adapter->rx_buffer_len);
@@ -1603,12 +1600,13 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid
* the 14 byte MAC header is removed
*/
vir_addr = skb->data;
- buffer_info->state = ATL1_BUFFER_BUSY;
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
buffer_info->skb = skb;
buffer_info->length = adapter->rx_buffer_len;
buffer_info->dma = pci_map_single(pdev, vir_addr,
buffer_info->length,
PCI_DMA_FROMDEVICE);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE);
rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
rfd_next_to_use = next_next;
if (++next_next == rfd_ring->count)
@@ -1653,7 +1651,8 @@ static void atl1c_clean_rfd(struct atl1c_rfd_ring *rfd_ring,
RRS_RX_RFD_INDEX_MASK;
for (i = 0; i < num; i++) {
buffer_info[rfd_index].skb = NULL;
- buffer_info[rfd_index].state = ATL1_BUFFER_FREE;
+ ATL1C_SET_BUFFER_STATE(&buffer_info[rfd_index],
+ ATL1C_BUFFER_FREE);
if (++rfd_index == rfd_ring->count)
rfd_index = 0;
}
@@ -1967,7 +1966,8 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
buffer_info->length = map_len;
buffer_info->dma = pci_map_single(adapter->pdev,
skb->data, hdr_len, PCI_DMA_TODEVICE);
- buffer_info->state = ATL1_BUFFER_BUSY;
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE);
mapped_len += map_len;
use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
use_tpd->buffer_len = cpu_to_le16(buffer_info->length);
@@ -1981,16 +1981,14 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
else {
use_tpd = atl1c_get_tpd(adapter, type);
memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc));
- use_tpd = atl1c_get_tpd(adapter, type);
- memcpy(use_tpd, tpd, sizeof(struct atl1c_tpd_desc));
}
buffer_info = atl1c_get_tx_buffer(adapter, use_tpd);
buffer_info->length = buf_len - mapped_len;
buffer_info->dma =
pci_map_single(adapter->pdev, skb->data + mapped_len,
buffer_info->length, PCI_DMA_TODEVICE);
- buffer_info->state = ATL1_BUFFER_BUSY;
-
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE);
use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
use_tpd->buffer_len = cpu_to_le16(buffer_info->length);
}
@@ -2010,8 +2008,8 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter,
frag->page_offset,
buffer_info->length,
PCI_DMA_TODEVICE);
- buffer_info->state = ATL1_BUFFER_BUSY;
-
+ ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY);
+ ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE);
use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma);
use_tpd->buffer_len = cpu_to_le16(buffer_info->length);
}
@@ -2137,7 +2135,7 @@ static int atl1c_request_irq(struct atl1c_adapter *adapter)
if (!adapter->have_msi)
flags |= IRQF_SHARED;
- err = request_irq(adapter->pdev->irq, &atl1c_intr, flags,
+ err = request_irq(adapter->pdev->irq, atl1c_intr, flags,
netdev->name, netdev);
if (err) {
if (netif_msg_ifup(adapter))
diff --git a/drivers/net/atl1e/atl1e_ethtool.c b/drivers/net/atl1e/atl1e_ethtool.c
index 60edb9f232b..a76006c1bc6 100644
--- a/drivers/net/atl1e/atl1e_ethtool.c
+++ b/drivers/net/atl1e/atl1e_ethtool.c
@@ -131,11 +131,6 @@ static int atl1e_set_settings(struct net_device *netdev,
return 0;
}
-static u32 atl1e_get_tx_csum(struct net_device *netdev)
-{
- return (netdev->features & NETIF_F_HW_CSUM) != 0;
-}
-
static u32 atl1e_get_msglevel(struct net_device *netdev)
{
#ifdef DBG
@@ -145,10 +140,6 @@ static u32 atl1e_get_msglevel(struct net_device *netdev)
#endif
}
-static void atl1e_set_msglevel(struct net_device *netdev, u32 data)
-{
-}
-
static int atl1e_get_regs_len(struct net_device *netdev)
{
return AT_REGS_LEN * sizeof(u32);
@@ -387,18 +378,14 @@ static const struct ethtool_ops atl1e_ethtool_ops = {
.get_wol = atl1e_get_wol,
.set_wol = atl1e_set_wol,
.get_msglevel = atl1e_get_msglevel,
- .set_msglevel = atl1e_set_msglevel,
.nway_reset = atl1e_nway_reset,
.get_link = ethtool_op_get_link,
.get_eeprom_len = atl1e_get_eeprom_len,
.get_eeprom = atl1e_get_eeprom,
.set_eeprom = atl1e_set_eeprom,
- .get_tx_csum = atl1e_get_tx_csum,
- .get_sg = ethtool_op_get_sg,
+ .set_tx_csum = ethtool_op_set_tx_hw_csum,
.set_sg = ethtool_op_set_sg,
-#ifdef NETIF_F_TSO
- .get_tso = ethtool_op_get_tso,
-#endif
+ .set_tso = ethtool_op_set_tso,
};
void atl1e_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c
index 955da733c2a..08f8c0969e9 100644
--- a/drivers/net/atl1e/atl1e_main.c
+++ b/drivers/net/atl1e/atl1e_main.c
@@ -1433,14 +1433,12 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que,
packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) &
RRS_PKT_SIZE_MASK) - 4; /* CRC */
- skb = netdev_alloc_skb(netdev,
- packet_size + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(netdev, packet_size);
if (skb == NULL) {
dev_warn(&pdev->dev, "%s: Memory squeeze,"
"deferring packet.\n", netdev->name);
goto skip_pkt;
}
- skb_reserve(skb, NET_IP_ALIGN);
skb->dev = netdev;
memcpy(skb->data, (u8 *)(prrs + 1), packet_size);
skb_put(skb, packet_size);
@@ -1666,41 +1664,6 @@ static int atl1e_tso_csum(struct atl1e_adapter *adapter,
}
return 0;
}
-
- if (offload_type & SKB_GSO_TCPV6) {
- real_len = (((unsigned char *)ipv6_hdr(skb) - skb->data)
- + ntohs(ipv6_hdr(skb)->payload_len));
- if (real_len < skb->len)
- pskb_trim(skb, real_len);
-
- /* check payload == 0 byte ? */
- hdr_len = (skb_transport_offset(skb) + tcp_hdrlen(skb));
- if (unlikely(skb->len == hdr_len)) {
- /* only xsum need */
- dev_warn(&pdev->dev,
- "IPV6 tso with zero data??\n");
- goto check_sum;
- } else {
- tcp_hdr(skb)->check = ~csum_ipv6_magic(
- &ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr,
- 0, IPPROTO_TCP, 0);
- tpd->word3 |= 1 << TPD_IP_VERSION_SHIFT;
- hdr_len >>= 1;
- tpd->word3 |= (hdr_len & TPD_V6_IPHLLO_MASK) <<
- TPD_V6_IPHLLO_SHIFT;
- tpd->word3 |= ((hdr_len >> 3) &
- TPD_V6_IPHLHI_MASK) <<
- TPD_V6_IPHLHI_SHIFT;
- tpd->word3 |= (tcp_hdrlen(skb) >> 2 &
- TPD_TCPHDRLEN_MASK) <<
- TPD_TCPHDRLEN_SHIFT;
- tpd->word3 |= ((skb_shinfo(skb)->gso_size) &
- TPD_MSS_MASK) << TPD_MSS_SHIFT;
- tpd->word3 |= 1 << TPD_SEGMENT_EN_SHIFT;
- }
- }
- return 0;
}
check_sum:
@@ -1932,7 +1895,7 @@ static int atl1e_request_irq(struct atl1e_adapter *adapter)
if (!adapter->have_msi)
flags |= IRQF_SHARED;
- err = request_irq(adapter->pdev->irq, &atl1e_intr, flags,
+ err = request_irq(adapter->pdev->irq, atl1e_intr, flags,
netdev->name, netdev);
if (err) {
dev_dbg(&pdev->dev,
@@ -2289,7 +2252,6 @@ static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev)
NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
netdev->features |= NETIF_F_LLTX;
netdev->features |= NETIF_F_TSO;
- netdev->features |= NETIF_F_TSO6;
return 0;
}
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c
index 00569dc1313..b6cf3263127 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/atlx/atl1.c
@@ -1344,8 +1344,8 @@ static u32 atl1_check_link(struct atl1_adapter *adapter)
/* link result is our setting */
if (!reconfig) {
- if (adapter->link_speed != speed
- || adapter->link_duplex != duplex) {
+ if (adapter->link_speed != speed ||
+ adapter->link_duplex != duplex) {
adapter->link_speed = speed;
adapter->link_duplex = duplex;
atl1_setup_mac_ctrl(adapter);
@@ -1864,21 +1864,14 @@ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter)
rfd_desc = ATL1_RFD_DESC(rfd_ring, rfd_next_to_use);
- skb = netdev_alloc_skb(adapter->netdev,
- adapter->rx_buffer_len + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(adapter->netdev,
+ adapter->rx_buffer_len);
if (unlikely(!skb)) {
/* Better luck next round */
adapter->netdev->stats.rx_dropped++;
break;
}
- /*
- * Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->alloced = 1;
buffer_info->skb = skb;
buffer_info->length = (u16) adapter->rx_buffer_len;
@@ -2094,8 +2087,8 @@ static void atl1_intr_tx(struct atl1_adapter *adapter)
}
atomic_set(&tpd_ring->next_to_clean, sw_tpd_next_to_clean);
- if (netif_queue_stopped(adapter->netdev)
- && netif_carrier_ok(adapter->netdev))
+ if (netif_queue_stopped(adapter->netdev) &&
+ netif_carrier_ok(adapter->netdev))
netif_wake_queue(adapter->netdev);
}
@@ -2596,7 +2589,7 @@ static s32 atl1_up(struct atl1_adapter *adapter)
irq_flags |= IRQF_SHARED;
}
- err = request_irq(adapter->pdev->irq, &atl1_intr, irq_flags,
+ err = request_irq(adapter->pdev->irq, atl1_intr, irq_flags,
netdev->name, netdev);
if (unlikely(err))
goto err_up;
diff --git a/drivers/net/atlx/atl2.c b/drivers/net/atlx/atl2.c
index ab688862093..c0451d75cdc 100644
--- a/drivers/net/atlx/atl2.c
+++ b/drivers/net/atlx/atl2.c
@@ -409,7 +409,7 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
if (rxd->status.ok && rxd->status.pkt_size >= 60) {
int rx_size = (int)(rxd->status.pkt_size - 4);
/* alloc new buffer */
- skb = netdev_alloc_skb(netdev, rx_size + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(netdev, rx_size);
if (NULL == skb) {
printk(KERN_WARNING
"%s: Mem squeeze, deferring packet.\n",
@@ -421,7 +421,6 @@ static void atl2_intr_rx(struct atl2_adapter *adapter)
netdev->stats.rx_dropped++;
break;
}
- skb_reserve(skb, NET_IP_ALIGN);
skb->dev = netdev;
memcpy(skb->data, rxd->packet, rx_size);
skb_put(skb, rx_size);
@@ -652,7 +651,7 @@ static int atl2_request_irq(struct atl2_adapter *adapter)
if (adapter->have_msi)
flags &= ~IRQF_SHARED;
- return request_irq(adapter->pdev->irq, &atl2_intr, flags, netdev->name,
+ return request_irq(adapter->pdev->irq, atl2_intr, flags, netdev->name,
netdev);
}
diff --git a/drivers/net/atp.c b/drivers/net/atp.c
index 9043294fe61..2f8261c9614 100644
--- a/drivers/net/atp.c
+++ b/drivers/net/atp.c
@@ -437,7 +437,7 @@ static int net_open(struct net_device *dev)
/* The interrupt line is turned off (tri-stated) when the device isn't in
use. That's especially important for "attached" interfaces where the
port or interrupt may be shared. */
- ret = request_irq(dev->irq, &atp_interrupt, 0, dev->name, dev);
+ ret = request_irq(dev->irq, atp_interrupt, 0, dev->name, dev);
if (ret)
return ret;
@@ -673,8 +673,8 @@ static irqreturn_t atp_interrupt(int irq, void *dev_instance)
netif_wake_queue(dev); /* Inform upper layers. */
}
num_tx_since_rx++;
- } else if (num_tx_since_rx > 8
- && time_after(jiffies, dev->last_rx + HZ)) {
+ } else if (num_tx_since_rx > 8 &&
+ time_after(jiffies, dev->last_rx + HZ)) {
if (net_debug > 2)
printk(KERN_DEBUG "%s: Missed packet? No Rx after %d Tx and "
"%ld jiffies status %02x CMR1 %02x.\n", dev->name,
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
index 3f4b4300f53..6bac04603a8 100644
--- a/drivers/net/au1000_eth.c
+++ b/drivers/net/au1000_eth.c
@@ -881,7 +881,7 @@ static int au1000_open(struct net_device *dev)
if (au1000_debug > 4)
printk("%s: open: dev=%p\n", dev->name, dev);
- if ((retval = request_irq(dev->irq, &au1000_interrupt, 0,
+ if ((retval = request_irq(dev->irq, au1000_interrupt, 0,
dev->name, dev))) {
printk(KERN_ERR "%s: unable to get IRQ %d\n",
dev->name, dev->irq);
diff --git a/drivers/net/bcm63xx_enet.c b/drivers/net/bcm63xx_enet.c
index ba29dc319b3..1f6c5486d71 100644
--- a/drivers/net/bcm63xx_enet.c
+++ b/drivers/net/bcm63xx_enet.c
@@ -320,16 +320,13 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
if (len < copybreak) {
struct sk_buff *nskb;
- nskb = netdev_alloc_skb(dev, len + NET_IP_ALIGN);
+ nskb = netdev_alloc_skb_ip_align(dev, len);
if (!nskb) {
/* forget packet, just rearm desc */
priv->stats.rx_dropped++;
continue;
}
- /* since we're copying the data, we can align
- * them properly */
- skb_reserve(nskb, NET_IP_ALIGN);
dma_sync_single_for_cpu(kdev, desc->address,
len, DMA_FROM_DEVICE);
memcpy(nskb->data, skb->data, len);
diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h
index 3b79a225628..9e56014d27e 100644
--- a/drivers/net/benet/be.h
+++ b/drivers/net/benet/be.h
@@ -32,23 +32,34 @@
#include "be_hw.h"
-#define DRV_VER "2.101.205"
+#define DRV_VER "2.101.346u"
#define DRV_NAME "be2net"
#define BE_NAME "ServerEngines BladeEngine2 10Gbps NIC"
+#define BE3_NAME "ServerEngines BladeEngine3 10Gbps NIC"
#define OC_NAME "Emulex OneConnect 10Gbps NIC"
+#define OC_NAME1 "Emulex OneConnect 10Gbps NIC (be3)"
#define DRV_DESC BE_NAME "Driver"
#define BE_VENDOR_ID 0x19a2
#define BE_DEVICE_ID1 0x211
+#define BE_DEVICE_ID2 0x221
#define OC_DEVICE_ID1 0x700
#define OC_DEVICE_ID2 0x701
+#define OC_DEVICE_ID3 0x710
static inline char *nic_name(struct pci_dev *pdev)
{
- if (pdev->device == OC_DEVICE_ID1 || pdev->device == OC_DEVICE_ID2)
+ switch (pdev->device) {
+ case OC_DEVICE_ID1:
+ case OC_DEVICE_ID2:
return OC_NAME;
- else
+ case OC_DEVICE_ID3:
+ return OC_NAME1;
+ case BE_DEVICE_ID2:
+ return BE3_NAME;
+ default:
return BE_NAME;
+ }
}
/* Number of bytes of an RX frame that are copied to skb->data */
@@ -159,7 +170,7 @@ struct be_drvr_stats {
u32 cache_barrier[16];
u32 be_ethrx_post_fail;/* number of ethrx buffer alloc failures */
- u32 be_polls; /* number of times NAPI called poll function */
+ u32 be_rx_polls; /* number of times NAPI called poll function */
u32 be_rx_events; /* number of ucast rx completion events */
u32 be_rx_compl; /* number of rx completion entries processed */
ulong be_rx_jiffies;
@@ -181,7 +192,6 @@ struct be_drvr_stats {
struct be_stats_obj {
struct be_drvr_stats drvr_stats;
- struct net_device_stats net_stats;
struct be_dma_mem cmd;
};
@@ -244,6 +254,7 @@ struct be_adapter {
struct vlan_group *vlan_grp;
u16 num_vlans;
u8 vlan_tag[VLAN_GROUP_ARRAY_LEN];
+ struct be_dma_mem mc_cmd_mem;
struct be_stats_obj stats;
/* Work queue used to perform periodic tasks like getting statistics */
@@ -258,9 +269,12 @@ struct be_adapter {
bool link_up;
u32 port_num;
bool promiscuous;
+ bool wol;
u32 cap;
u32 rx_fc; /* Rx flow control */
u32 tx_fc; /* Tx flow control */
+ int link_speed;
+ u8 port_type;
};
extern const struct ethtool_ops be_ethtool_ops;
diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c
index 28a0eda9268..1b68bd98dc0 100644
--- a/drivers/net/benet/be_cmds.c
+++ b/drivers/net/benet/be_cmds.c
@@ -71,8 +71,8 @@ static int be_mcc_compl_process(struct be_adapter *adapter,
extd_status = (compl->status >> CQE_STATUS_EXTD_SHIFT) &
CQE_STATUS_EXTD_MASK;
dev_warn(&adapter->pdev->dev,
- "Error in cmd completion: status(compl/extd)=%d/%d\n",
- compl_status, extd_status);
+ "Error in cmd completion - opcode %d, compl %d, extd %d\n",
+ compl->tag0, compl_status, extd_status);
}
return compl_status;
}
@@ -277,7 +277,7 @@ static inline struct be_sge *nonembedded_sgl(struct be_mcc_wrb *wrb)
/* Don't touch the hdr after it's prepared */
static void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
- bool embedded, u8 sge_cnt)
+ bool embedded, u8 sge_cnt, u32 opcode)
{
if (embedded)
wrb->embedded |= MCC_WRB_EMBEDDED_MASK;
@@ -285,6 +285,7 @@ static void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
wrb->embedded |= (sge_cnt & MCC_WRB_SGE_CNT_MASK) <<
MCC_WRB_SGE_CNT_SHIFT;
wrb->payload_length = payload_len;
+ wrb->tag0 = opcode;
be_dws_cpu_to_le(wrb, 20);
}
@@ -349,7 +350,11 @@ static struct be_mcc_wrb *wrb_from_mccq(struct be_adapter *adapter)
struct be_queue_info *mccq = &adapter->mcc_obj.q;
struct be_mcc_wrb *wrb;
- BUG_ON(atomic_read(&mccq->used) >= mccq->len);
+ if (atomic_read(&mccq->used) >= mccq->len) {
+ dev_err(&adapter->pdev->dev, "Out of MCCQ wrbs\n");
+ return NULL;
+ }
+
wrb = queue_head_node(mccq);
queue_head_inc(mccq);
atomic_inc(&mccq->used);
@@ -357,6 +362,57 @@ static struct be_mcc_wrb *wrb_from_mccq(struct be_adapter *adapter)
return wrb;
}
+/* Tell fw we're about to start firing cmds by writing a
+ * special pattern across the wrb hdr; uses mbox
+ */
+int be_cmd_fw_init(struct be_adapter *adapter)
+{
+ u8 *wrb;
+ int status;
+
+ spin_lock(&adapter->mbox_lock);
+
+ wrb = (u8 *)wrb_from_mbox(adapter);
+ *wrb++ = 0xFF;
+ *wrb++ = 0x12;
+ *wrb++ = 0x34;
+ *wrb++ = 0xFF;
+ *wrb++ = 0xFF;
+ *wrb++ = 0x56;
+ *wrb++ = 0x78;
+ *wrb = 0xFF;
+
+ status = be_mbox_notify_wait(adapter);
+
+ spin_unlock(&adapter->mbox_lock);
+ return status;
+}
+
+/* Tell fw we're done with firing cmds by writing a
+ * special pattern across the wrb hdr; uses mbox
+ */
+int be_cmd_fw_clean(struct be_adapter *adapter)
+{
+ u8 *wrb;
+ int status;
+
+ spin_lock(&adapter->mbox_lock);
+
+ wrb = (u8 *)wrb_from_mbox(adapter);
+ *wrb++ = 0xFF;
+ *wrb++ = 0xAA;
+ *wrb++ = 0xBB;
+ *wrb++ = 0xFF;
+ *wrb++ = 0xFF;
+ *wrb++ = 0xCC;
+ *wrb++ = 0xDD;
+ *wrb = 0xFF;
+
+ status = be_mbox_notify_wait(adapter);
+
+ spin_unlock(&adapter->mbox_lock);
+ return status;
+}
int be_cmd_eq_create(struct be_adapter *adapter,
struct be_queue_info *eq, int eq_delay)
{
@@ -370,7 +426,7 @@ int be_cmd_eq_create(struct be_adapter *adapter,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, OPCODE_COMMON_EQ_CREATE);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_EQ_CREATE, sizeof(*req));
@@ -414,7 +470,8 @@ int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_NTWK_MAC_QUERY);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_MAC_QUERY, sizeof(*req));
@@ -448,9 +505,14 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_NTWK_PMAC_ADD);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_PMAC_ADD, sizeof(*req));
@@ -464,6 +526,7 @@ int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr,
*pmac_id = le32_to_cpu(resp->pmac_id);
}
+err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -478,9 +541,14 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, u32 pmac_id)
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_NTWK_PMAC_DEL);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_PMAC_DEL, sizeof(*req));
@@ -490,8 +558,8 @@ int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, u32 pmac_id)
status = be_mcc_notify_wait(adapter);
+err:
spin_unlock_bh(&adapter->mcc_lock);
-
return status;
}
@@ -512,7 +580,8 @@ int be_cmd_cq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_CQ_CREATE);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_CQ_CREATE, sizeof(*req));
@@ -569,7 +638,8 @@ int be_cmd_mccq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_MCC_CREATE);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_MCC_CREATE, sizeof(*req));
@@ -613,7 +683,8 @@ int be_cmd_txq_create(struct be_adapter *adapter,
req = embedded_payload(wrb);
ctxt = &req->context;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_ETH_TX_CREATE);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_TX_CREATE,
sizeof(*req));
@@ -660,7 +731,8 @@ int be_cmd_rxq_create(struct be_adapter *adapter,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_ETH_RX_CREATE);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_RX_CREATE,
sizeof(*req));
@@ -701,8 +773,6 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
-
switch (queue_type) {
case QTYPE_EQ:
subsys = CMD_SUBSYSTEM_COMMON;
@@ -727,6 +797,9 @@ int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
default:
BUG();
}
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, opcode);
+
be_cmd_hdr_prepare(&req->hdr, subsys, opcode, sizeof(*req));
req->id = cpu_to_le16(q->id);
@@ -752,7 +825,8 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags,
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_NTWK_INTERFACE_CREATE);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_INTERFACE_CREATE, sizeof(*req));
@@ -787,7 +861,8 @@ int be_cmd_if_destroy(struct be_adapter *adapter, u32 interface_id)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_NTWK_INTERFACE_DESTROY);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_INTERFACE_DESTROY, sizeof(*req));
@@ -810,15 +885,20 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_stats *req;
struct be_sge *sge;
+ int status = 0;
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = nonemb_cmd->va;
sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1);
- wrb->tag0 = OPCODE_ETH_GET_STATISTICS;
+ be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
+ OPCODE_ETH_GET_STATISTICS);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
OPCODE_ETH_GET_STATISTICS, sizeof(*req));
@@ -828,13 +908,14 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd)
be_mcc_notify(adapter);
+err:
spin_unlock_bh(&adapter->mcc_lock);
- return 0;
+ return status;
}
/* Uses synchronous mcc */
int be_cmd_link_status_query(struct be_adapter *adapter,
- bool *link_up)
+ bool *link_up, u8 *mac_speed, u16 *link_speed)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_link_status *req;
@@ -843,11 +924,16 @@ int be_cmd_link_status_query(struct be_adapter *adapter,
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
*link_up = false;
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_NTWK_LINK_STATUS_QUERY);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_LINK_STATUS_QUERY, sizeof(*req));
@@ -855,10 +941,14 @@ int be_cmd_link_status_query(struct be_adapter *adapter,
status = be_mcc_notify_wait(adapter);
if (!status) {
struct be_cmd_resp_link_status *resp = embedded_payload(wrb);
- if (resp->mac_speed != PHY_LINK_SPEED_ZERO)
+ if (resp->mac_speed != PHY_LINK_SPEED_ZERO) {
*link_up = true;
+ *link_speed = le16_to_cpu(resp->link_speed);
+ *mac_speed = resp->mac_speed;
+ }
}
+err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -875,7 +965,8 @@ int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_GET_FW_VERSION);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_GET_FW_VERSION, sizeof(*req));
@@ -897,13 +988,19 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_modify_eq_delay *req;
+ int status = 0;
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_MODIFY_EQ_DELAY);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req));
@@ -915,8 +1012,9 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd)
be_mcc_notify(adapter);
+err:
spin_unlock_bh(&adapter->mcc_lock);
- return 0;
+ return status;
}
/* Uses sycnhronous mcc */
@@ -930,9 +1028,14 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_NTWK_VLAN_CONFIG);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req));
@@ -948,6 +1051,7 @@ int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
status = be_mcc_notify_wait(adapter);
+err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -964,9 +1068,13 @@ int be_cmd_promiscuous_config(struct be_adapter *adapter, u8 port_num, bool en)
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0, OPCODE_ETH_PROMISCUOUS);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
OPCODE_ETH_PROMISCUOUS, sizeof(*req));
@@ -978,6 +1086,7 @@ int be_cmd_promiscuous_config(struct be_adapter *adapter, u8 port_num, bool en)
status = be_mcc_notify_wait(adapter);
+err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -987,24 +1096,35 @@ int be_cmd_promiscuous_config(struct be_adapter *adapter, u8 port_num, bool en)
* (mc == NULL) => multicast promiscous
*/
int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id,
- struct dev_mc_list *mc_list, u32 mc_count)
+ struct dev_mc_list *mc_list, u32 mc_count,
+ struct be_dma_mem *mem)
{
-#define BE_MAX_MC 32 /* set mcast promisc if > 32 */
struct be_mcc_wrb *wrb;
- struct be_cmd_req_mcast_mac_config *req;
+ struct be_cmd_req_mcast_mac_config *req = mem->va;
+ struct be_sge *sge;
+ int status;
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
- req = embedded_payload(wrb);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ sge = nonembedded_sgl(wrb);
+ memset(req, 0, sizeof(*req));
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
+ OPCODE_COMMON_NTWK_MULTICAST_SET);
+ sge->pa_hi = cpu_to_le32(upper_32_bits(mem->dma));
+ sge->pa_lo = cpu_to_le32(mem->dma & 0xFFFFFFFF);
+ sge->len = cpu_to_le32(mem->size);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_MULTICAST_SET, sizeof(*req));
req->interface_id = if_id;
- if (mc_list && mc_count <= BE_MAX_MC) {
+ if (mc_list) {
int i;
struct dev_mc_list *mc;
@@ -1016,11 +1136,11 @@ int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id,
req->promiscuous = 1;
}
- be_mcc_notify_wait(adapter);
+ status = be_mcc_notify_wait(adapter);
+err:
spin_unlock_bh(&adapter->mcc_lock);
-
- return 0;
+ return status;
}
/* Uses synchrounous mcc */
@@ -1033,9 +1153,14 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_SET_FLOW_CONTROL);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_SET_FLOW_CONTROL, sizeof(*req));
@@ -1045,6 +1170,7 @@ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc)
status = be_mcc_notify_wait(adapter);
+err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -1059,9 +1185,14 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_GET_FLOW_CONTROL);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_GET_FLOW_CONTROL, sizeof(*req));
@@ -1074,6 +1205,7 @@ int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc)
*rx_fc = le16_to_cpu(resp->rx_flow_control);
}
+err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
@@ -1090,7 +1222,8 @@ int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num, u32 *cap)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_QUERY_FIRMWARE_CONFIG);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
@@ -1118,7 +1251,8 @@ int be_cmd_reset_function(struct be_adapter *adapter)
wrb = wrb_from_mbox(adapter);
req = embedded_payload(wrb);
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_FUNCTION_RESET);
be_cmd_hdr_prepare(req, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_FUNCTION_RESET, sizeof(*req));
@@ -1129,6 +1263,113 @@ int be_cmd_reset_function(struct be_adapter *adapter)
return status;
}
+/* Uses sync mcc */
+int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num,
+ u8 bcn, u8 sts, u8 state)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_enable_disable_beacon *req;
+ int status;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_ENABLE_DISABLE_BEACON);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_ENABLE_DISABLE_BEACON, sizeof(*req));
+
+ req->port_num = port_num;
+ req->beacon_state = state;
+ req->beacon_duration = bcn;
+ req->status_duration = sts;
+
+ status = be_mcc_notify_wait(adapter);
+
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
+/* Uses sync mcc */
+int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, u32 *state)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_get_beacon_state *req;
+ int status;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_COMMON_GET_BEACON_STATE);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_GET_BEACON_STATE, sizeof(*req));
+
+ req->port_num = port_num;
+
+ status = be_mcc_notify_wait(adapter);
+ if (!status) {
+ struct be_cmd_resp_get_beacon_state *resp =
+ embedded_payload(wrb);
+ *state = resp->beacon_state;
+ }
+
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
+/* Uses sync mcc */
+int be_cmd_read_port_type(struct be_adapter *adapter, u32 port,
+ u8 *connector)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_port_type *req;
+ int status;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(struct be_cmd_resp_port_type), true, 0,
+ OPCODE_COMMON_READ_TRANSRECV_DATA);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_READ_TRANSRECV_DATA, sizeof(*req));
+
+ req->port = cpu_to_le32(port);
+ req->page_num = cpu_to_le32(TR_PAGE_A0);
+ status = be_mcc_notify_wait(adapter);
+ if (!status) {
+ struct be_cmd_resp_port_type *resp = embedded_payload(wrb);
+ *connector = resp->data.connector;
+ }
+
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
u32 flash_type, u32 flash_opcode, u32 buf_size)
{
@@ -1140,9 +1381,15 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = cmd->va;
sge = nonembedded_sgl(wrb);
- be_wrb_hdr_prepare(wrb, cmd->size, false, 1);
+ be_wrb_hdr_prepare(wrb, cmd->size, false, 1,
+ OPCODE_COMMON_WRITE_FLASHROM);
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_WRITE_FLASHROM, cmd->size);
@@ -1156,6 +1403,171 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
status = be_mcc_notify_wait(adapter);
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
+int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_write_flashrom *req;
+ int status;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req)+4, true, 0,
+ OPCODE_COMMON_READ_FLASHROM);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_READ_FLASHROM, sizeof(*req)+4);
+
+ req->params.op_type = cpu_to_le32(FLASHROM_TYPE_REDBOOT);
+ req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT);
+ req->params.offset = 0x3FFFC;
+ req->params.data_buf_size = 0x4;
+
+ status = be_mcc_notify_wait(adapter);
+ if (!status)
+ memcpy(flashed_crc, req->params.data_buf, 4);
+
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
+extern int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
+ struct be_dma_mem *nonemb_cmd)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_acpi_wol_magic_config *req;
+ struct be_sge *sge;
+ int status;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = nonemb_cmd->va;
+ sge = nonembedded_sgl(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req), false, 1,
+ OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH,
+ OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, sizeof(*req));
+ memcpy(req->magic_mac, mac, ETH_ALEN);
+
+ sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd->dma));
+ sge->pa_lo = cpu_to_le32(nonemb_cmd->dma & 0xFFFFFFFF);
+ sge->len = cpu_to_le32(nonemb_cmd->size);
+
+ status = be_mcc_notify_wait(adapter);
+
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
+int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
+ u32 loopback_type, u32 pkt_size, u32 num_pkts, u64 pattern)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_loopback_test *req;
+ int status;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+
+ req = embedded_payload(wrb);
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0,
+ OPCODE_LOWLEVEL_LOOPBACK_TEST);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_LOOPBACK_TEST, sizeof(*req));
+
+ req->pattern = cpu_to_le64(pattern);
+ req->src_port = cpu_to_le32(port_num);
+ req->dest_port = cpu_to_le32(port_num);
+ req->pkt_size = cpu_to_le32(pkt_size);
+ req->num_pkts = cpu_to_le32(num_pkts);
+ req->loopback_type = cpu_to_le32(loopback_type);
+
+ status = be_mcc_notify_wait(adapter);
+ if (!status) {
+ struct be_cmd_resp_loopback_test *resp = embedded_payload(wrb);
+ status = le32_to_cpu(resp->status);
+ }
+
+err:
+ spin_unlock_bh(&adapter->mcc_lock);
+ return status;
+}
+
+int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
+ u32 byte_cnt, struct be_dma_mem *cmd)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_ddrdma_test *req;
+ struct be_sge *sge;
+ int status;
+ int i, j = 0;
+
+ spin_lock_bh(&adapter->mcc_lock);
+
+ wrb = wrb_from_mccq(adapter);
+ if (!wrb) {
+ status = -EBUSY;
+ goto err;
+ }
+ req = cmd->va;
+ sge = nonembedded_sgl(wrb);
+ be_wrb_hdr_prepare(wrb, cmd->size, false, 1,
+ OPCODE_LOWLEVEL_HOST_DDR_DMA);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_LOWLEVEL,
+ OPCODE_LOWLEVEL_HOST_DDR_DMA, cmd->size);
+
+ sge->pa_hi = cpu_to_le32(upper_32_bits(cmd->dma));
+ sge->pa_lo = cpu_to_le32(cmd->dma & 0xFFFFFFFF);
+ sge->len = cpu_to_le32(cmd->size);
+
+ req->pattern = cpu_to_le64(pattern);
+ req->byte_count = cpu_to_le32(byte_cnt);
+ for (i = 0; i < byte_cnt; i++) {
+ req->snd_buff[i] = (u8)(pattern >> (j*8));
+ j++;
+ if (j > 7)
+ j = 0;
+ }
+
+ status = be_mcc_notify_wait(adapter);
+
+ if (!status) {
+ struct be_cmd_resp_ddrdma_test *resp;
+ resp = cmd->va;
+ if ((memcmp(resp->rcv_buff, req->snd_buff, byte_cnt) != 0) ||
+ resp->snd_err) {
+ status = -1;
+ }
+ }
+
+err:
spin_unlock_bh(&adapter->mcc_lock);
return status;
}
diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h
index e5f9676cf1b..92b87ef156e 100644
--- a/drivers/net/benet/be_cmds.h
+++ b/drivers/net/benet/be_cmds.h
@@ -112,12 +112,14 @@ struct be_mcc_mailbox {
#define CMD_SUBSYSTEM_COMMON 0x1
#define CMD_SUBSYSTEM_ETH 0x3
+#define CMD_SUBSYSTEM_LOWLEVEL 0xb
#define OPCODE_COMMON_NTWK_MAC_QUERY 1
#define OPCODE_COMMON_NTWK_MAC_SET 2
#define OPCODE_COMMON_NTWK_MULTICAST_SET 3
#define OPCODE_COMMON_NTWK_VLAN_CONFIG 4
#define OPCODE_COMMON_NTWK_LINK_STATUS_QUERY 5
+#define OPCODE_COMMON_READ_FLASHROM 6
#define OPCODE_COMMON_WRITE_FLASHROM 7
#define OPCODE_COMMON_CQ_CREATE 12
#define OPCODE_COMMON_EQ_CREATE 13
@@ -138,6 +140,9 @@ struct be_mcc_mailbox {
#define OPCODE_COMMON_NTWK_PMAC_ADD 59
#define OPCODE_COMMON_NTWK_PMAC_DEL 60
#define OPCODE_COMMON_FUNCTION_RESET 61
+#define OPCODE_COMMON_ENABLE_DISABLE_BEACON 69
+#define OPCODE_COMMON_GET_BEACON_STATE 70
+#define OPCODE_COMMON_READ_TRANSRECV_DATA 73
#define OPCODE_ETH_ACPI_CONFIG 2
#define OPCODE_ETH_PROMISCUOUS 3
@@ -146,6 +151,10 @@ struct be_mcc_mailbox {
#define OPCODE_ETH_RX_CREATE 8
#define OPCODE_ETH_TX_DESTROY 9
#define OPCODE_ETH_RX_DESTROY 10
+#define OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG 12
+
+#define OPCODE_LOWLEVEL_HOST_DDR_DMA 17
+#define OPCODE_LOWLEVEL_LOOPBACK_TEST 18
struct be_cmd_req_hdr {
u8 opcode; /* dword 0 */
@@ -435,7 +444,7 @@ enum be_if_flags {
* filtering capabilities. */
struct be_cmd_req_if_create {
struct be_cmd_req_hdr hdr;
- u32 version; /* ignore currntly */
+ u32 version; /* ignore currently */
u32 capability_flags;
u32 enable_flags;
u8 mac_addr[ETH_ALEN];
@@ -587,6 +596,8 @@ struct be_cmd_req_promiscuous_config {
u16 rsvd0;
} __packed;
+/******************** Multicast MAC Config *******************/
+#define BE_MAX_MC 64 /* set mcast promisc if > 64 */
struct macaddr {
u8 byte[ETH_ALEN];
};
@@ -596,7 +607,7 @@ struct be_cmd_req_mcast_mac_config {
u16 num_mac;
u8 promiscuous;
u8 interface_id;
- struct macaddr mac[32];
+ struct macaddr mac[BE_MAX_MC];
} __packed;
static inline struct be_hw_stats *
@@ -633,9 +644,47 @@ struct be_cmd_resp_link_status {
u8 mac_fault;
u8 mgmt_mac_duplex;
u8 mgmt_mac_speed;
- u16 rsvd0;
+ u16 link_speed;
+ u32 rsvd0;
} __packed;
+/******************** Port Identification ***************************/
+/* Identifies the type of port attached to NIC */
+struct be_cmd_req_port_type {
+ struct be_cmd_req_hdr hdr;
+ u32 page_num;
+ u32 port;
+};
+
+enum {
+ TR_PAGE_A0 = 0xa0,
+ TR_PAGE_A2 = 0xa2
+};
+
+struct be_cmd_resp_port_type {
+ struct be_cmd_resp_hdr hdr;
+ u32 page_num;
+ u32 port;
+ struct data {
+ u8 identifier;
+ u8 identifier_ext;
+ u8 connector;
+ u8 transceiver[8];
+ u8 rsvd0[3];
+ u8 length_km;
+ u8 length_hm;
+ u8 length_om1;
+ u8 length_om2;
+ u8 length_cu;
+ u8 length_cu_m;
+ u8 vendor_name[16];
+ u8 rsvd;
+ u8 vendor_oui[3];
+ u8 vendor_pn[16];
+ u8 vendor_rev[4];
+ } data;
+};
+
/******************** Get FW Version *******************/
struct be_cmd_req_get_fw_version {
struct be_cmd_req_hdr hdr;
@@ -699,6 +748,37 @@ struct be_cmd_resp_query_fw_cfg {
u32 rsvd[26];
};
+/******************** Port Beacon ***************************/
+
+#define BEACON_STATE_ENABLED 0x1
+#define BEACON_STATE_DISABLED 0x0
+
+struct be_cmd_req_enable_disable_beacon {
+ struct be_cmd_req_hdr hdr;
+ u8 port_num;
+ u8 beacon_state;
+ u8 beacon_duration;
+ u8 status_duration;
+} __packed;
+
+struct be_cmd_resp_enable_disable_beacon {
+ struct be_cmd_resp_hdr resp_hdr;
+ u32 rsvd0;
+} __packed;
+
+struct be_cmd_req_get_beacon_state {
+ struct be_cmd_req_hdr hdr;
+ u8 port_num;
+ u8 rsvd0;
+ u16 rsvd1;
+} __packed;
+
+struct be_cmd_resp_get_beacon_state {
+ struct be_cmd_resp_hdr resp_hdr;
+ u8 beacon_state;
+ u8 rsvd0[3];
+} __packed;
+
/****************** Firmware Flash ******************/
struct flashrom_params {
u32 op_code;
@@ -713,6 +793,53 @@ struct be_cmd_write_flashrom {
struct flashrom_params params;
};
+/************************ WOL *******************************/
+struct be_cmd_req_acpi_wol_magic_config{
+ struct be_cmd_req_hdr hdr;
+ u32 rsvd0[145];
+ u8 magic_mac[6];
+ u8 rsvd2[2];
+} __packed;
+
+/********************** LoopBack test *********************/
+struct be_cmd_req_loopback_test {
+ struct be_cmd_req_hdr hdr;
+ u32 loopback_type;
+ u32 num_pkts;
+ u64 pattern;
+ u32 src_port;
+ u32 dest_port;
+ u32 pkt_size;
+};
+
+struct be_cmd_resp_loopback_test {
+ struct be_cmd_resp_hdr resp_hdr;
+ u32 status;
+ u32 num_txfer;
+ u32 num_rx;
+ u32 miscomp_off;
+ u32 ticks_compl;
+};
+
+/********************** DDR DMA test *********************/
+struct be_cmd_req_ddrdma_test {
+ struct be_cmd_req_hdr hdr;
+ u64 pattern;
+ u32 byte_count;
+ u32 rsvd0;
+ u8 snd_buff[4096];
+ u8 rsvd1[4096];
+};
+
+struct be_cmd_resp_ddrdma_test {
+ struct be_cmd_resp_hdr hdr;
+ u64 pattern;
+ u32 byte_cnt;
+ u32 snd_err;
+ u8 rsvd0[4096];
+ u8 rcv_buff[4096];
+};
+
extern int be_pci_fnum_get(struct be_adapter *adapter);
extern int be_cmd_POST(struct be_adapter *adapter);
extern int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr,
@@ -743,7 +870,7 @@ extern int be_cmd_rxq_create(struct be_adapter *adapter,
extern int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q,
int type);
extern int be_cmd_link_status_query(struct be_adapter *adapter,
- bool *link_up);
+ bool *link_up, u8 *mac_speed, u16 *link_speed);
extern int be_cmd_reset(struct be_adapter *adapter);
extern int be_cmd_get_stats(struct be_adapter *adapter,
struct be_dma_mem *nonemb_cmd);
@@ -756,7 +883,8 @@ extern int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id,
extern int be_cmd_promiscuous_config(struct be_adapter *adapter,
u8 port_num, bool en);
extern int be_cmd_multicast_set(struct be_adapter *adapter, u32 if_id,
- struct dev_mc_list *mc_list, u32 mc_count);
+ struct dev_mc_list *mc_list, u32 mc_count,
+ struct be_dma_mem *mem);
extern int be_cmd_set_flow_control(struct be_adapter *adapter,
u32 tx_fc, u32 rx_fc);
extern int be_cmd_get_flow_control(struct be_adapter *adapter,
@@ -765,6 +893,22 @@ extern int be_cmd_query_fw_cfg(struct be_adapter *adapter,
u32 *port_num, u32 *cap);
extern int be_cmd_reset_function(struct be_adapter *adapter);
extern int be_process_mcc(struct be_adapter *adapter);
+extern int be_cmd_set_beacon_state(struct be_adapter *adapter,
+ u8 port_num, u8 beacon, u8 status, u8 state);
+extern int be_cmd_get_beacon_state(struct be_adapter *adapter,
+ u8 port_num, u32 *state);
+extern int be_cmd_read_port_type(struct be_adapter *adapter, u32 port,
+ u8 *connector);
extern int be_cmd_write_flashrom(struct be_adapter *adapter,
struct be_dma_mem *cmd, u32 flash_oper,
u32 flash_opcode, u32 buf_size);
+extern int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc);
+extern int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
+ struct be_dma_mem *nonemb_cmd);
+extern int be_cmd_fw_init(struct be_adapter *adapter);
+extern int be_cmd_fw_clean(struct be_adapter *adapter);
+extern int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num,
+ u32 loopback_type, u32 pkt_size,
+ u32 num_pkts, u64 pattern);
+extern int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern,
+ u32 byte_cnt, struct be_dma_mem *cmd);
diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/benet/be_ethtool.c
index f0fd95b43c0..298b92cbd68 100644
--- a/drivers/net/benet/be_ethtool.c
+++ b/drivers/net/benet/be_ethtool.c
@@ -55,7 +55,7 @@ static const struct be_ethtool_stat et_stats[] = {
{DRVSTAT_INFO(be_tx_stops)},
{DRVSTAT_INFO(be_fwd_reqs)},
{DRVSTAT_INFO(be_tx_wrbs)},
- {DRVSTAT_INFO(be_polls)},
+ {DRVSTAT_INFO(be_rx_polls)},
{DRVSTAT_INFO(be_tx_events)},
{DRVSTAT_INFO(be_rx_events)},
{DRVSTAT_INFO(be_tx_compl)},
@@ -107,6 +107,18 @@ static const struct be_ethtool_stat et_stats[] = {
};
#define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats)
+static const char et_self_tests[][ETH_GSTRING_LEN] = {
+ "MAC Loopback test",
+ "PHY Loopback test",
+ "External Loopback test",
+ "DDR DMA test"
+};
+
+#define ETHTOOL_TESTS_NUM ARRAY_SIZE(et_self_tests)
+#define BE_MAC_LOOPBACK 0x0
+#define BE_PHY_LOOPBACK 0x1
+#define BE_ONE_PORT_EXT_LOOPBACK 0x2
+
static void
be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
{
@@ -234,7 +246,7 @@ be_get_ethtool_stats(struct net_device *netdev,
struct be_rxf_stats *rxf_stats = &hw_stats->rxf;
struct be_port_rxf_stats *port_stats =
&rxf_stats->port[adapter->port_num];
- struct net_device_stats *net_stats = &adapter->stats.net_stats;
+ struct net_device_stats *net_stats = &netdev->stats;
struct be_erx_stats *erx_stats = &hw_stats->erx;
void *p = NULL;
int i;
@@ -278,19 +290,78 @@ be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
data += ETH_GSTRING_LEN;
}
break;
+ case ETH_SS_TEST:
+ for (i = 0; i < ETHTOOL_TESTS_NUM; i++) {
+ memcpy(data, et_self_tests[i], ETH_GSTRING_LEN);
+ data += ETH_GSTRING_LEN;
+ }
+ break;
}
}
-static int be_get_stats_count(struct net_device *netdev)
+static int be_get_sset_count(struct net_device *netdev, int stringset)
{
- return ETHTOOL_STATS_NUM;
+ switch (stringset) {
+ case ETH_SS_TEST:
+ return ETHTOOL_TESTS_NUM;
+ case ETH_SS_STATS:
+ return ETHTOOL_STATS_NUM;
+ default:
+ return -EINVAL;
+ }
}
static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{
- ecmd->speed = SPEED_10000;
+ struct be_adapter *adapter = netdev_priv(netdev);
+ u8 mac_speed = 0, connector = 0;
+ u16 link_speed = 0;
+ bool link_up = false;
+ int status;
+
+ if (adapter->link_speed < 0) {
+ status = be_cmd_link_status_query(adapter, &link_up,
+ &mac_speed, &link_speed);
+
+ /* link_speed is in units of 10 Mbps */
+ if (link_speed) {
+ ecmd->speed = link_speed*10;
+ } else {
+ switch (mac_speed) {
+ case PHY_LINK_SPEED_1GBPS:
+ ecmd->speed = SPEED_1000;
+ break;
+ case PHY_LINK_SPEED_10GBPS:
+ ecmd->speed = SPEED_10000;
+ break;
+ }
+ }
+
+ status = be_cmd_read_port_type(adapter, adapter->port_num,
+ &connector);
+ switch (connector) {
+ case 7:
+ ecmd->port = PORT_FIBRE;
+ break;
+ default:
+ ecmd->port = PORT_TP;
+ break;
+ }
+
+ /* Save for future use */
+ adapter->link_speed = ecmd->speed;
+ adapter->port_type = ecmd->port;
+ } else {
+ ecmd->speed = adapter->link_speed;
+ ecmd->port = adapter->port_type;
+ }
+
ecmd->duplex = DUPLEX_FULL;
ecmd->autoneg = AUTONEG_DISABLE;
+ ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_TP);
+ ecmd->phy_address = adapter->port_num;
+ ecmd->transceiver = XCVR_INTERNAL;
+
return 0;
}
@@ -335,6 +406,123 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd)
}
static int
+be_phys_id(struct net_device *netdev, u32 data)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ int status;
+ u32 cur;
+
+ be_cmd_get_beacon_state(adapter, adapter->port_num, &cur);
+
+ if (cur == BEACON_STATE_ENABLED)
+ return 0;
+
+ if (data < 2)
+ data = 2;
+
+ status = be_cmd_set_beacon_state(adapter, adapter->port_num, 0, 0,
+ BEACON_STATE_ENABLED);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(data*HZ);
+
+ status = be_cmd_set_beacon_state(adapter, adapter->port_num, 0, 0,
+ BEACON_STATE_DISABLED);
+
+ return status;
+}
+
+static void
+be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ wol->supported = WAKE_MAGIC;
+ if (adapter->wol)
+ wol->wolopts = WAKE_MAGIC;
+ else
+ wol->wolopts = 0;
+ memset(&wol->sopass, 0, sizeof(wol->sopass));
+ return;
+}
+
+static int
+be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ if (wol->wolopts & ~WAKE_MAGIC)
+ return -EINVAL;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ adapter->wol = true;
+ else
+ adapter->wol = false;
+
+ return 0;
+}
+
+static int
+be_test_ddr_dma(struct be_adapter *adapter)
+{
+ int ret, i;
+ struct be_dma_mem ddrdma_cmd;
+ u64 pattern[2] = {0x5a5a5a5a5a5a5a5a, 0xa5a5a5a5a5a5a5a5};
+
+ ddrdma_cmd.size = sizeof(struct be_cmd_req_ddrdma_test);
+ ddrdma_cmd.va = pci_alloc_consistent(adapter->pdev, ddrdma_cmd.size,
+ &ddrdma_cmd.dma);
+ if (!ddrdma_cmd.va) {
+ dev_err(&adapter->pdev->dev, "Memory allocation failure \n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = be_cmd_ddr_dma_test(adapter, pattern[i],
+ 4096, &ddrdma_cmd);
+ if (ret != 0)
+ goto err;
+ }
+
+err:
+ pci_free_consistent(adapter->pdev, ddrdma_cmd.size,
+ ddrdma_cmd.va, ddrdma_cmd.dma);
+ return ret;
+}
+
+static void
+be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ memset(data, 0, sizeof(u64) * ETHTOOL_TESTS_NUM);
+
+ if (test->flags & ETH_TEST_FL_OFFLINE) {
+ data[0] = be_cmd_loopback_test(adapter, adapter->port_num,
+ BE_MAC_LOOPBACK, 1500,
+ 2, 0xabc);
+ if (data[0] != 0)
+ test->flags |= ETH_TEST_FL_FAILED;
+
+ data[1] = be_cmd_loopback_test(adapter, adapter->port_num,
+ BE_PHY_LOOPBACK, 1500,
+ 2, 0xabc);
+ if (data[1] != 0)
+ test->flags |= ETH_TEST_FL_FAILED;
+
+ data[2] = be_cmd_loopback_test(adapter, adapter->port_num,
+ BE_ONE_PORT_EXT_LOOPBACK,
+ 1500, 2, 0xabc);
+ if (data[2] != 0)
+ test->flags |= ETH_TEST_FL_FAILED;
+
+ data[3] = be_test_ddr_dma(adapter);
+ if (data[3] != 0)
+ test->flags |= ETH_TEST_FL_FAILED;
+ }
+
+}
+
+static int
be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
{
struct be_adapter *adapter = netdev_priv(netdev);
@@ -351,6 +539,8 @@ be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
const struct ethtool_ops be_ethtool_ops = {
.get_settings = be_get_settings,
.get_drvinfo = be_get_drvinfo,
+ .get_wol = be_get_wol,
+ .set_wol = be_set_wol,
.get_link = ethtool_op_get_link,
.get_coalesce = be_get_coalesce,
.set_coalesce = be_set_coalesce,
@@ -366,7 +556,9 @@ const struct ethtool_ops be_ethtool_ops = {
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
.get_strings = be_get_stat_strings,
- .get_stats_count = be_get_stats_count,
+ .phys_id = be_phys_id,
+ .get_sset_count = be_get_sset_count,
.get_ethtool_stats = be_get_ethtool_stats,
.flash_device = be_do_flash,
+ .self_test = be_self_test,
};
diff --git a/drivers/net/benet/be_hw.h b/drivers/net/benet/be_hw.h
index a3394b4aa14..e2b3beffd49 100644
--- a/drivers/net/benet/be_hw.h
+++ b/drivers/net/benet/be_hw.h
@@ -52,6 +52,10 @@
*/
#define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK (1 << 29) /* bit 29 */
+/********* Power managment (WOL) **********/
+#define PCICFG_PM_CONTROL_OFFSET 0x44
+#define PCICFG_PM_CONTROL_MASK 0x108 /* bits 3 & 8 */
+
/********* ISR0 Register offset **********/
#define CEV_ISR0_OFFSET 0xC18
#define CEV_ISR_SIZE 4
@@ -225,6 +229,7 @@ struct be_eth_rx_compl {
#define NUM_FLASHDIR_ENTRIES 32
#define FLASHROM_TYPE_ISCSI_ACTIVE 0
+#define FLASHROM_TYPE_REDBOOT 1
#define FLASHROM_TYPE_BIOS 2
#define FLASHROM_TYPE_PXE_BIOS 3
#define FLASHROM_TYPE_FCOE_BIOS 8
@@ -234,9 +239,11 @@ struct be_eth_rx_compl {
#define FLASHROM_OPER_FLASH 1
#define FLASHROM_OPER_SAVE 2
+#define FLASHROM_OPER_REPORT 4
#define FLASH_IMAGE_MAX_SIZE (1310720) /* Max firmware image size */
#define FLASH_BIOS_IMAGE_MAX_SIZE (262144) /* Max OPTION ROM image sz */
+#define FLASH_REDBOOT_IMAGE_MAX_SIZE (262144) /* Max redboot image sz */
/* Offsets for components on Flash. */
#define FLASH_iSCSI_PRIMARY_IMAGE_START (1048576)
@@ -246,6 +253,8 @@ struct be_eth_rx_compl {
#define FLASH_iSCSI_BIOS_START (7340032)
#define FLASH_PXE_BIOS_START (7864320)
#define FLASH_FCoE_BIOS_START (524288)
+#define FLASH_REDBOOT_START (32768)
+#define FLASH_REDBOOT_ISM_START (0)
struct controller_id {
u32 vendor;
diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c
index 876b357101f..24c7d9900ba 100644
--- a/drivers/net/benet/be_main.c
+++ b/drivers/net/benet/be_main.c
@@ -31,8 +31,10 @@ MODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data.");
static DEFINE_PCI_DEVICE_TABLE(be_dev_ids) = {
{ PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
+ { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) },
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
{ PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
+ { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, be_dev_ids);
@@ -123,6 +125,9 @@ static int be_mac_addr_set(struct net_device *netdev, void *p)
struct sockaddr *addr = p;
int status = 0;
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
status = be_cmd_pmac_del(adapter, adapter->if_handle, adapter->pmac_id);
if (status)
return status;
@@ -141,7 +146,7 @@ void netdev_stats_update(struct be_adapter *adapter)
struct be_rxf_stats *rxf_stats = &hw_stats->rxf;
struct be_port_rxf_stats *port_stats =
&rxf_stats->port[adapter->port_num];
- struct net_device_stats *dev_stats = &adapter->stats.net_stats;
+ struct net_device_stats *dev_stats = &adapter->netdev->stats;
struct be_erx_stats *erx_stats = &hw_stats->erx;
dev_stats->rx_packets = port_stats->rx_total_frames;
@@ -168,7 +173,8 @@ void netdev_stats_update(struct be_adapter *adapter)
port_stats->rx_udp_checksum_errs;
/* no space in linux buffers: best possible approximation */
- dev_stats->rx_dropped = erx_stats->rx_drops_no_fragments[0];
+ dev_stats->rx_dropped =
+ erx_stats->rx_drops_no_fragments[adapter->rx_obj.q.id];
/* detailed rx errors */
dev_stats->rx_length_errors = port_stats->rx_in_range_errors +
@@ -214,6 +220,7 @@ void be_link_status_update(struct be_adapter *adapter, bool link_up)
/* If link came up or went down */
if (adapter->link_up != link_up) {
+ adapter->link_speed = -1;
if (link_up) {
netif_start_queue(netdev);
netif_carrier_on(netdev);
@@ -269,9 +276,7 @@ static void be_rx_eqd_update(struct be_adapter *adapter)
static struct net_device_stats *be_get_stats(struct net_device *dev)
{
- struct be_adapter *adapter = netdev_priv(dev);
-
- return &adapter->stats.net_stats;
+ return &dev->stats;
}
static u32 be_calc_rate(u64 bytes, unsigned long ticks)
@@ -389,15 +394,11 @@ static int make_tx_wrbs(struct be_adapter *adapter,
atomic_add(wrb_cnt, &txq->used);
queue_head_inc(txq);
- if (skb_dma_map(&pdev->dev, skb, DMA_TO_DEVICE)) {
- dev_err(&pdev->dev, "TX DMA mapping failed\n");
- return 0;
- }
-
if (skb->len > skb->data_len) {
int len = skb->len - skb->data_len;
+ busaddr = pci_map_single(pdev, skb->data, len,
+ PCI_DMA_TODEVICE);
wrb = queue_head_node(txq);
- busaddr = skb_shinfo(skb)->dma_head;
wrb_fill(wrb, busaddr, len);
be_dws_cpu_to_le(wrb, sizeof(*wrb));
queue_head_inc(txq);
@@ -407,8 +408,9 @@ static int make_tx_wrbs(struct be_adapter *adapter,
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[i];
-
- busaddr = skb_shinfo(skb)->dma_maps[i];
+ busaddr = pci_map_page(pdev, frag->page,
+ frag->page_offset,
+ frag->size, PCI_DMA_TODEVICE);
wrb = queue_head_node(txq);
wrb_fill(wrb, busaddr, frag->size);
be_dws_cpu_to_le(wrb, sizeof(*wrb));
@@ -562,13 +564,15 @@ static void be_set_multicast_list(struct net_device *netdev)
be_cmd_promiscuous_config(adapter, adapter->port_num, 0);
}
- if (netdev->flags & IFF_ALLMULTI) {
- be_cmd_multicast_set(adapter, adapter->if_handle, NULL, 0);
+ /* Enable multicast promisc if num configured exceeds what we support */
+ if (netdev->flags & IFF_ALLMULTI || netdev->mc_count > BE_MAX_MC) {
+ be_cmd_multicast_set(adapter, adapter->if_handle, NULL, 0,
+ &adapter->mc_cmd_mem);
goto done;
}
be_cmd_multicast_set(adapter, adapter->if_handle, netdev->mc_list,
- netdev->mc_count);
+ netdev->mc_count, &adapter->mc_cmd_mem);
done:
return;
}
@@ -758,7 +762,7 @@ static void be_rx_compl_process(struct be_adapter *adapter,
if ((adapter->cap == 0x400) && !vtm)
vlanf = 0;
- skb = netdev_alloc_skb(adapter->netdev, BE_HDR_LEN + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(adapter->netdev, BE_HDR_LEN);
if (!skb) {
if (net_ratelimit())
dev_warn(&adapter->pdev->dev, "skb alloc failed\n");
@@ -766,8 +770,6 @@ static void be_rx_compl_process(struct be_adapter *adapter,
return;
}
- skb_reserve(skb, NET_IP_ALIGN);
-
skb_fill_rx_data(adapter, skb, rxcp);
if (do_pkt_csum(rxcp, adapter->rx_csum))
@@ -981,23 +983,41 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq)
static void be_tx_compl_process(struct be_adapter *adapter, u16 last_index)
{
struct be_queue_info *txq = &adapter->tx_obj.q;
+ struct be_eth_wrb *wrb;
struct sk_buff **sent_skbs = adapter->tx_obj.sent_skb_list;
struct sk_buff *sent_skb;
+ u64 busaddr;
u16 cur_index, num_wrbs = 0;
cur_index = txq->tail;
sent_skb = sent_skbs[cur_index];
BUG_ON(!sent_skb);
sent_skbs[cur_index] = NULL;
+ wrb = queue_tail_node(txq);
+ be_dws_le_to_cpu(wrb, sizeof(*wrb));
+ busaddr = ((u64)wrb->frag_pa_hi << 32) | (u64)wrb->frag_pa_lo;
+ if (busaddr != 0) {
+ pci_unmap_single(adapter->pdev, busaddr,
+ wrb->frag_len, PCI_DMA_TODEVICE);
+ }
+ num_wrbs++;
+ queue_tail_inc(txq);
- do {
+ while (cur_index != last_index) {
cur_index = txq->tail;
+ wrb = queue_tail_node(txq);
+ be_dws_le_to_cpu(wrb, sizeof(*wrb));
+ busaddr = ((u64)wrb->frag_pa_hi << 32) | (u64)wrb->frag_pa_lo;
+ if (busaddr != 0) {
+ pci_unmap_page(adapter->pdev, busaddr,
+ wrb->frag_len, PCI_DMA_TODEVICE);
+ }
num_wrbs++;
queue_tail_inc(txq);
- } while (cur_index != last_index);
+ }
atomic_sub(num_wrbs, &txq->used);
- skb_dma_unmap(&adapter->pdev->dev, sent_skb, DMA_TO_DEVICE);
+
kfree_skb(sent_skb);
}
@@ -1377,6 +1397,7 @@ int be_poll_rx(struct napi_struct *napi, int budget)
struct be_eth_rx_compl *rxcp;
u32 work_done;
+ adapter->stats.drvr_stats.be_rx_polls++;
for (work_done = 0; work_done < budget; work_done++) {
rxcp = be_rx_compl_get(adapter);
if (!rxcp)
@@ -1475,6 +1496,14 @@ static void be_worker(struct work_struct *work)
schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000));
}
+static void be_msix_disable(struct be_adapter *adapter)
+{
+ if (adapter->msix_enabled) {
+ pci_disable_msix(adapter->pdev);
+ adapter->msix_enabled = false;
+ }
+}
+
static void be_msix_enable(struct be_adapter *adapter)
{
int i, status;
@@ -1590,6 +1619,8 @@ static int be_open(struct net_device *netdev)
struct be_eq_obj *tx_eq = &adapter->tx_eq;
bool link_up;
int status;
+ u8 mac_speed;
+ u16 link_speed;
/* First time posting */
be_post_rx_frags(adapter);
@@ -1608,7 +1639,8 @@ static int be_open(struct net_device *netdev)
/* Rx compl queue may be in unarmed state; rearm it */
be_cq_notify(adapter, adapter->rx_obj.cq.id, true, 0);
- status = be_cmd_link_status_query(adapter, &link_up);
+ status = be_cmd_link_status_query(adapter, &link_up, &mac_speed,
+ &link_speed);
if (status)
goto ret_sts;
be_link_status_update(adapter, link_up);
@@ -1627,6 +1659,44 @@ ret_sts:
return status;
}
+static int be_setup_wol(struct be_adapter *adapter, bool enable)
+{
+ struct be_dma_mem cmd;
+ int status = 0;
+ u8 mac[ETH_ALEN];
+
+ memset(mac, 0, ETH_ALEN);
+
+ cmd.size = sizeof(struct be_cmd_req_acpi_wol_magic_config);
+ cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+ if (cmd.va == NULL)
+ return -1;
+ memset(cmd.va, 0, cmd.size);
+
+ if (enable) {
+ status = pci_write_config_dword(adapter->pdev,
+ PCICFG_PM_CONTROL_OFFSET, PCICFG_PM_CONTROL_MASK);
+ if (status) {
+ dev_err(&adapter->pdev->dev,
+ "Could not enable Wake-on-lan \n");
+ pci_free_consistent(adapter->pdev, cmd.size, cmd.va,
+ cmd.dma);
+ return status;
+ }
+ status = be_cmd_enable_magic_wol(adapter,
+ adapter->netdev->dev_addr, &cmd);
+ pci_enable_wake(adapter->pdev, PCI_D3hot, 1);
+ pci_enable_wake(adapter->pdev, PCI_D3cold, 1);
+ } else {
+ status = be_cmd_enable_magic_wol(adapter, mac, &cmd);
+ pci_enable_wake(adapter->pdev, PCI_D3hot, 0);
+ pci_enable_wake(adapter->pdev, PCI_D3cold, 0);
+ }
+
+ pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+ return status;
+}
+
static int be_setup(struct be_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
@@ -1658,6 +1728,8 @@ static int be_setup(struct be_adapter *adapter)
if (status != 0)
goto rx_qs_destroy;
+ adapter->link_speed = -1;
+
return 0;
rx_qs_destroy:
@@ -1678,6 +1750,8 @@ static int be_clear(struct be_adapter *adapter)
be_cmd_if_destroy(adapter, adapter->if_handle);
+ /* tell fw we're done with firing cmds */
+ be_cmd_fw_clean(adapter);
return 0;
}
@@ -1720,6 +1794,31 @@ static int be_close(struct net_device *netdev)
#define FW_FILE_HDR_SIGN "ServerEngines Corp. "
char flash_cookie[2][16] = {"*** SE FLAS",
"H DIRECTORY *** "};
+
+static bool be_flash_redboot(struct be_adapter *adapter,
+ const u8 *p)
+{
+ u32 crc_offset;
+ u8 flashed_crc[4];
+ int status;
+ crc_offset = FLASH_REDBOOT_START + FLASH_REDBOOT_IMAGE_MAX_SIZE - 4
+ + sizeof(struct flash_file_hdr) - 32*1024;
+ p += crc_offset;
+ status = be_cmd_get_flash_crc(adapter, flashed_crc);
+ if (status) {
+ dev_err(&adapter->pdev->dev,
+ "could not get crc from flash, not flashing redboot\n");
+ return false;
+ }
+
+ /*update redboot only if crc does not match*/
+ if (!memcmp(flashed_crc, p, 4))
+ return false;
+ else
+ return true;
+
+}
+
static int be_flash_image(struct be_adapter *adapter,
const struct firmware *fw,
struct be_dma_mem *flash_cmd, u32 flash_type)
@@ -1759,6 +1858,12 @@ static int be_flash_image(struct be_adapter *adapter,
image_offset = FLASH_PXE_BIOS_START;
image_size = FLASH_BIOS_IMAGE_MAX_SIZE;
break;
+ case FLASHROM_TYPE_REDBOOT:
+ if (!be_flash_redboot(adapter, fw->data))
+ return 0;
+ image_offset = FLASH_REDBOOT_ISM_START;
+ image_size = FLASH_REDBOOT_IMAGE_MAX_SIZE;
+ break;
default:
return 0;
}
@@ -1877,7 +1982,7 @@ int be_load_fw(struct be_adapter *adapter, u8 *func)
goto fw_exit;
}
- dev_info(&adapter->pdev->dev, "Firmware flashed succesfully\n");
+ dev_info(&adapter->pdev->dev, "Firmware flashed successfully\n");
fw_exit:
release_firmware(fw);
@@ -1906,6 +2011,8 @@ static void be_netdev_init(struct net_device *netdev)
NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_HW_CSUM |
NETIF_F_GRO;
+ netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_HW_CSUM;
+
netdev->flags |= IFF_MULTICAST;
adapter->rx_csum = true;
@@ -1977,34 +2084,61 @@ static void be_ctrl_cleanup(struct be_adapter *adapter)
if (mem->va)
pci_free_consistent(adapter->pdev, mem->size,
mem->va, mem->dma);
+
+ mem = &adapter->mc_cmd_mem;
+ if (mem->va)
+ pci_free_consistent(adapter->pdev, mem->size,
+ mem->va, mem->dma);
}
static int be_ctrl_init(struct be_adapter *adapter)
{
struct be_dma_mem *mbox_mem_alloc = &adapter->mbox_mem_alloced;
struct be_dma_mem *mbox_mem_align = &adapter->mbox_mem;
+ struct be_dma_mem *mc_cmd_mem = &adapter->mc_cmd_mem;
int status;
status = be_map_pci_bars(adapter);
if (status)
- return status;
+ goto done;
mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
mbox_mem_alloc->va = pci_alloc_consistent(adapter->pdev,
mbox_mem_alloc->size, &mbox_mem_alloc->dma);
if (!mbox_mem_alloc->va) {
- be_unmap_pci_bars(adapter);
- return -1;
+ status = -ENOMEM;
+ goto unmap_pci_bars;
}
+
mbox_mem_align->size = sizeof(struct be_mcc_mailbox);
mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16);
mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
+
+ mc_cmd_mem->size = sizeof(struct be_cmd_req_mcast_mac_config);
+ mc_cmd_mem->va = pci_alloc_consistent(adapter->pdev, mc_cmd_mem->size,
+ &mc_cmd_mem->dma);
+ if (mc_cmd_mem->va == NULL) {
+ status = -ENOMEM;
+ goto free_mbox;
+ }
+ memset(mc_cmd_mem->va, 0, mc_cmd_mem->size);
+
spin_lock_init(&adapter->mbox_lock);
spin_lock_init(&adapter->mcc_lock);
spin_lock_init(&adapter->mcc_cq_lock);
return 0;
+
+free_mbox:
+ pci_free_consistent(adapter->pdev, mbox_mem_alloc->size,
+ mbox_mem_alloc->va, mbox_mem_alloc->dma);
+
+unmap_pci_bars:
+ be_unmap_pci_bars(adapter);
+
+done:
+ return status;
}
static void be_stats_cleanup(struct be_adapter *adapter)
@@ -2032,6 +2166,7 @@ static int be_stats_init(struct be_adapter *adapter)
static void __devexit be_remove(struct pci_dev *pdev)
{
struct be_adapter *adapter = pci_get_drvdata(pdev);
+
if (!adapter)
return;
@@ -2043,10 +2178,7 @@ static void __devexit be_remove(struct pci_dev *pdev)
be_ctrl_cleanup(adapter);
- if (adapter->msix_enabled) {
- pci_disable_msix(adapter->pdev);
- adapter->msix_enabled = false;
- }
+ be_msix_disable(adapter);
pci_set_drvdata(pdev, NULL);
pci_release_regions(pdev);
@@ -2055,25 +2187,33 @@ static void __devexit be_remove(struct pci_dev *pdev)
free_netdev(adapter->netdev);
}
-static int be_hw_up(struct be_adapter *adapter)
+static int be_get_config(struct be_adapter *adapter)
{
int status;
+ u8 mac[ETH_ALEN];
- status = be_cmd_POST(adapter);
+ status = be_cmd_get_fw_ver(adapter, adapter->fw_ver);
if (status)
return status;
- status = be_cmd_reset_function(adapter);
+ status = be_cmd_query_fw_cfg(adapter,
+ &adapter->port_num, &adapter->cap);
if (status)
return status;
- status = be_cmd_get_fw_ver(adapter, adapter->fw_ver);
+ memset(mac, 0, ETH_ALEN);
+ status = be_cmd_mac_addr_query(adapter, mac,
+ MAC_ADDRESS_TYPE_NETWORK, true /*permanent */, 0);
if (status)
return status;
- status = be_cmd_query_fw_cfg(adapter,
- &adapter->port_num, &adapter->cap);
- return status;
+ if (!is_valid_ether_addr(mac))
+ return -EADDRNOTAVAIL;
+
+ memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN);
+ memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN);
+
+ return 0;
}
static int __devinit be_probe(struct pci_dev *pdev,
@@ -2082,7 +2222,6 @@ static int __devinit be_probe(struct pci_dev *pdev,
int status = 0;
struct be_adapter *adapter;
struct net_device *netdev;
- u8 mac[ETH_ALEN];
status = pci_enable_device(pdev);
if (status)
@@ -2102,6 +2241,8 @@ static int __devinit be_probe(struct pci_dev *pdev,
adapter->pdev = pdev;
pci_set_drvdata(pdev, adapter);
adapter->netdev = netdev;
+ be_netdev_init(netdev);
+ SET_NETDEV_DEV(netdev, &pdev->dev);
be_msix_enable(adapter);
@@ -2120,27 +2261,34 @@ static int __devinit be_probe(struct pci_dev *pdev,
if (status)
goto free_netdev;
- status = be_stats_init(adapter);
+ /* sync up with fw's ready state */
+ status = be_cmd_POST(adapter);
if (status)
goto ctrl_clean;
- status = be_hw_up(adapter);
+ /* tell fw we're ready to fire cmds */
+ status = be_cmd_fw_init(adapter);
if (status)
- goto stats_clean;
+ goto ctrl_clean;
- status = be_cmd_mac_addr_query(adapter, mac, MAC_ADDRESS_TYPE_NETWORK,
- true /* permanent */, 0);
+ status = be_cmd_reset_function(adapter);
+ if (status)
+ goto ctrl_clean;
+
+ status = be_stats_init(adapter);
+ if (status)
+ goto ctrl_clean;
+
+ status = be_get_config(adapter);
if (status)
goto stats_clean;
- memcpy(netdev->dev_addr, mac, ETH_ALEN);
INIT_DELAYED_WORK(&adapter->work, be_worker);
- be_netdev_init(netdev);
- SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
status = be_setup(adapter);
if (status)
goto stats_clean;
+
status = register_netdev(netdev);
if (status != 0)
goto unsetup;
@@ -2155,7 +2303,9 @@ stats_clean:
ctrl_clean:
be_ctrl_cleanup(adapter);
free_netdev:
+ be_msix_disable(adapter);
free_netdev(adapter->netdev);
+ pci_set_drvdata(pdev, NULL);
rel_reg:
pci_release_regions(pdev);
disable_dev:
@@ -2170,6 +2320,9 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state)
struct be_adapter *adapter = pci_get_drvdata(pdev);
struct net_device *netdev = adapter->netdev;
+ if (adapter->wol)
+ be_setup_wol(adapter, true);
+
netif_device_detach(netdev);
if (netif_running(netdev)) {
rtnl_lock();
@@ -2200,6 +2353,11 @@ static int be_resume(struct pci_dev *pdev)
pci_set_power_state(pdev, 0);
pci_restore_state(pdev);
+ /* tell fw we're ready to fire cmds */
+ status = be_cmd_fw_init(adapter);
+ if (status)
+ return status;
+
be_setup(adapter);
if (netif_running(netdev)) {
rtnl_lock();
@@ -2207,6 +2365,9 @@ static int be_resume(struct pci_dev *pdev)
rtnl_unlock();
}
netif_device_attach(netdev);
+
+ if (adapter->wol)
+ be_setup_wol(adapter, false);
return 0;
}
@@ -2221,8 +2382,8 @@ static struct pci_driver be_driver = {
static int __init be_init_module(void)
{
- if (rx_frag_size != 8192 && rx_frag_size != 4096
- && rx_frag_size != 2048) {
+ if (rx_frag_size != 8192 && rx_frag_size != 4096 &&
+ rx_frag_size != 2048) {
printk(KERN_WARNING DRV_NAME
" : Module param rx_frag_size must be 2048/4096/8192."
" Using 2048\n");
diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
index 14bd3801f7d..8ffea3990d0 100644
--- a/drivers/net/bfin_mac.c
+++ b/drivers/net/bfin_mac.c
@@ -554,8 +554,8 @@ static void adjust_tx_list(void)
{
int timeout_cnt = MAX_TIMEOUT_CNT;
- if (tx_list_head->status.status_word != 0
- && current_tx_ptr != tx_list_head) {
+ if (tx_list_head->status.status_word != 0 &&
+ current_tx_ptr != tx_list_head) {
goto adjust_head; /* released something, just return; */
}
@@ -567,8 +567,8 @@ static void adjust_tx_list(void)
if (current_tx_ptr->next->next == tx_list_head) {
while (tx_list_head->status.status_word == 0) {
udelay(10);
- if (tx_list_head->status.status_word != 0
- || !(bfin_read_DMA2_IRQ_STATUS() & DMA_RUN)) {
+ if (tx_list_head->status.status_word != 0 ||
+ !(bfin_read_DMA2_IRQ_STATUS() & DMA_RUN)) {
goto adjust_head;
}
if (timeout_cnt-- < 0) {
@@ -596,8 +596,8 @@ adjust_head:
": no sk_buff in a transmitted frame!\n");
}
tx_list_head = tx_list_head->next;
- } while (tx_list_head->status.status_word != 0
- && current_tx_ptr != tx_list_head);
+ } while (tx_list_head->status.status_word != 0 &&
+ current_tx_ptr != tx_list_head);
return;
}
diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c
index 406f0642425..9b587c34419 100644
--- a/drivers/net/bmac.c
+++ b/drivers/net/bmac.c
@@ -438,8 +438,8 @@ bmac_init_phy(struct net_device *dev)
ctrl = bmac_mif_read(dev, 0);
capable = ((bmac_mif_read(dev, 1) & 0xf800) >> 6) | 1;
- if (bmac_mif_read(dev, 4) != capable
- || (ctrl & 0x1000) == 0) {
+ if (bmac_mif_read(dev, 4) != capable ||
+ (ctrl & 0x1000) == 0) {
bmac_mif_write(dev, 4, capable);
bmac_mif_write(dev, 0, 0x1200);
} else
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 08cddb6ff74..4bfc8081292 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -59,8 +59,8 @@
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "2.0.2"
-#define DRV_MODULE_RELDATE "Aug 21, 2009"
+#define DRV_MODULE_VERSION "2.0.3"
+#define DRV_MODULE_RELDATE "Dec 03, 2009"
#define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-5.0.0.j3.fw"
#define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-5.0.0.j3.fw"
#define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-5.0.0.j3.fw"
@@ -1466,6 +1466,8 @@ bnx2_enable_forced_2g5(struct bnx2 *bp)
} else if (CHIP_NUM(bp) == CHIP_NUM_5708) {
bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);
bmcr |= BCM5708S_BMCR_FORCE_2500;
+ } else {
+ return;
}
if (bp->autoneg & AUTONEG_SPEED) {
@@ -1500,6 +1502,8 @@ bnx2_disable_forced_2g5(struct bnx2 *bp)
} else if (CHIP_NUM(bp) == CHIP_NUM_5708) {
bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);
bmcr &= ~BCM5708S_BMCR_FORCE_2500;
+ } else {
+ return;
}
if (bp->autoneg & AUTONEG_SPEED)
@@ -2811,13 +2815,21 @@ bnx2_tx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget)
}
}
- skb_dma_unmap(&bp->pdev->dev, skb, DMA_TO_DEVICE);
+ pci_unmap_single(bp->pdev, pci_unmap_addr(tx_buf, mapping),
+ skb_headlen(skb), PCI_DMA_TODEVICE);
tx_buf->skb = NULL;
last = tx_buf->nr_frags;
for (i = 0; i < last; i++) {
sw_cons = NEXT_TX_BD(sw_cons);
+
+ pci_unmap_page(bp->pdev,
+ pci_unmap_addr(
+ &txr->tx_buf_ring[TX_RING_IDX(sw_cons)],
+ mapping),
+ skb_shinfo(skb)->frags[i].size,
+ PCI_DMA_TODEVICE);
}
sw_cons = NEXT_TX_BD(sw_cons);
@@ -5146,8 +5158,12 @@ bnx2_init_rx_ring(struct bnx2 *bp, int ring_num)
ring_prod = prod = rxr->rx_pg_prod;
for (i = 0; i < bp->rx_pg_ring_size; i++) {
- if (bnx2_alloc_rx_page(bp, rxr, ring_prod) < 0)
+ if (bnx2_alloc_rx_page(bp, rxr, ring_prod) < 0) {
+ printk(KERN_WARNING PFX "%s: init'ed rx page ring %d "
+ "with %d/%d pages only\n",
+ bp->dev->name, ring_num, i, bp->rx_pg_ring_size);
break;
+ }
prod = NEXT_RX_BD(prod);
ring_prod = RX_PG_RING_IDX(prod);
}
@@ -5155,8 +5171,12 @@ bnx2_init_rx_ring(struct bnx2 *bp, int ring_num)
ring_prod = prod = rxr->rx_prod;
for (i = 0; i < bp->rx_ring_size; i++) {
- if (bnx2_alloc_rx_skb(bp, rxr, ring_prod) < 0)
+ if (bnx2_alloc_rx_skb(bp, rxr, ring_prod) < 0) {
+ printk(KERN_WARNING PFX "%s: init'ed rx ring %d with "
+ "%d/%d skbs only\n",
+ bp->dev->name, ring_num, i, bp->rx_ring_size);
break;
+ }
prod = NEXT_RX_BD(prod);
ring_prod = RX_RING_IDX(prod);
}
@@ -5291,17 +5311,29 @@ bnx2_free_tx_skbs(struct bnx2 *bp)
for (j = 0; j < TX_DESC_CNT; ) {
struct sw_tx_bd *tx_buf = &txr->tx_buf_ring[j];
struct sk_buff *skb = tx_buf->skb;
+ int k, last;
if (skb == NULL) {
j++;
continue;
}
- skb_dma_unmap(&bp->pdev->dev, skb, DMA_TO_DEVICE);
+ pci_unmap_single(bp->pdev,
+ pci_unmap_addr(tx_buf, mapping),
+ skb_headlen(skb),
+ PCI_DMA_TODEVICE);
tx_buf->skb = NULL;
- j += skb_shinfo(skb)->nr_frags + 1;
+ last = tx_buf->nr_frags;
+ j++;
+ for (k = 0; k < last; k++, j++) {
+ tx_buf = &txr->tx_buf_ring[TX_RING_IDX(j)];
+ pci_unmap_page(bp->pdev,
+ pci_unmap_addr(tx_buf, mapping),
+ skb_shinfo(skb)->frags[k].size,
+ PCI_DMA_TODEVICE);
+ }
dev_kfree_skb(skb);
}
}
@@ -5680,11 +5712,12 @@ bnx2_run_loopback(struct bnx2 *bp, int loopback_mode)
for (i = 14; i < pkt_size; i++)
packet[i] = (unsigned char) (i & 0xff);
- if (skb_dma_map(&bp->pdev->dev, skb, DMA_TO_DEVICE)) {
+ map = pci_map_single(bp->pdev, skb->data, pkt_size,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(bp->pdev, map)) {
dev_kfree_skb(skb);
return -EIO;
}
- map = skb_shinfo(skb)->dma_head;
REG_WR(bp, BNX2_HC_COMMAND,
bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
@@ -5719,7 +5752,7 @@ bnx2_run_loopback(struct bnx2 *bp, int loopback_mode)
udelay(5);
- skb_dma_unmap(&bp->pdev->dev, skb, DMA_TO_DEVICE);
+ pci_unmap_single(bp->pdev, map, pkt_size, PCI_DMA_TODEVICE);
dev_kfree_skb(skb);
if (bnx2_get_hw_tx_cons(tx_napi) != txr->tx_prod)
@@ -6238,8 +6271,11 @@ bnx2_reset_task(struct work_struct *work)
{
struct bnx2 *bp = container_of(work, struct bnx2, reset_task);
- if (!netif_running(bp->dev))
+ rtnl_lock();
+ if (!netif_running(bp->dev)) {
+ rtnl_unlock();
return;
+ }
bnx2_netif_stop(bp);
@@ -6247,6 +6283,28 @@ bnx2_reset_task(struct work_struct *work)
atomic_set(&bp->intr_sem, 1);
bnx2_netif_start(bp);
+ rtnl_unlock();
+}
+
+static void
+bnx2_dump_state(struct bnx2 *bp)
+{
+ struct net_device *dev = bp->dev;
+
+ printk(KERN_ERR PFX "%s DEBUG: intr_sem[%x]\n", dev->name,
+ atomic_read(&bp->intr_sem));
+ printk(KERN_ERR PFX "%s DEBUG: EMAC_TX_STATUS[%08x] "
+ "RPM_MGMT_PKT_CTRL[%08x]\n", dev->name,
+ REG_RD(bp, BNX2_EMAC_TX_STATUS),
+ REG_RD(bp, BNX2_RPM_MGMT_PKT_CTRL));
+ printk(KERN_ERR PFX "%s DEBUG: MCP_STATE_P0[%08x] MCP_STATE_P1[%08x]\n",
+ dev->name, bnx2_reg_rd_ind(bp, BNX2_MCP_STATE_P0),
+ bnx2_reg_rd_ind(bp, BNX2_MCP_STATE_P1));
+ printk(KERN_ERR PFX "%s DEBUG: HC_STATS_INTERRUPT_STATUS[%08x]\n",
+ dev->name, REG_RD(bp, BNX2_HC_STATS_INTERRUPT_STATUS));
+ if (bp->flags & BNX2_FLAG_USING_MSIX)
+ printk(KERN_ERR PFX "%s DEBUG: PBA[%08x]\n", dev->name,
+ REG_RD(bp, BNX2_PCI_GRC_WINDOW3_BASE));
}
static void
@@ -6254,6 +6312,8 @@ bnx2_tx_timeout(struct net_device *dev)
{
struct bnx2 *bp = netdev_priv(dev);
+ bnx2_dump_state(bp);
+
/* This allows the netif to be shutdown gracefully before resetting */
schedule_work(&bp->reset_task);
}
@@ -6298,7 +6358,6 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct bnx2_napi *bnapi;
struct bnx2_tx_ring_info *txr;
struct netdev_queue *txq;
- struct skb_shared_info *sp;
/* Determine which tx ring we will be placed on */
i = skb_get_queue_mapping(skb);
@@ -6363,16 +6422,15 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
} else
mss = 0;
- if (skb_dma_map(&bp->pdev->dev, skb, DMA_TO_DEVICE)) {
+ mapping = pci_map_single(bp->pdev, skb->data, len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(bp->pdev, mapping)) {
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
- sp = skb_shinfo(skb);
- mapping = sp->dma_head;
-
tx_buf = &txr->tx_buf_ring[ring_prod];
tx_buf->skb = skb;
+ pci_unmap_addr_set(tx_buf, mapping, mapping);
txbd = &txr->tx_desc_ring[ring_prod];
@@ -6393,7 +6451,12 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
txbd = &txr->tx_desc_ring[ring_prod];
len = frag->size;
- mapping = sp->dma_maps[i];
+ mapping = pci_map_page(bp->pdev, frag->page, frag->page_offset,
+ len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(bp->pdev, mapping))
+ goto dma_error;
+ pci_unmap_addr_set(&txr->tx_buf_ring[ring_prod], mapping,
+ mapping);
txbd->tx_bd_haddr_hi = (u64) mapping >> 32;
txbd->tx_bd_haddr_lo = (u64) mapping & 0xffffffff;
@@ -6420,6 +6483,30 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
return NETDEV_TX_OK;
+dma_error:
+ /* save value of frag that failed */
+ last_frag = i;
+
+ /* start back at beginning and unmap skb */
+ prod = txr->tx_prod;
+ ring_prod = TX_RING_IDX(prod);
+ tx_buf = &txr->tx_buf_ring[ring_prod];
+ tx_buf->skb = NULL;
+ pci_unmap_single(bp->pdev, pci_unmap_addr(tx_buf, mapping),
+ skb_headlen(skb), PCI_DMA_TODEVICE);
+
+ /* unmap remaining mapped pages */
+ for (i = 0; i < last_frag; i++) {
+ prod = NEXT_TX_BD(prod);
+ ring_prod = TX_RING_IDX(prod);
+ tx_buf = &txr->tx_buf_ring[ring_prod];
+ pci_unmap_page(bp->pdev, pci_unmap_addr(tx_buf, mapping),
+ skb_shinfo(skb)->frags[i].size,
+ PCI_DMA_TODEVICE);
+ }
+
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
}
/* Called with rtnl_lock */
@@ -7635,6 +7722,86 @@ bnx2_get_pci_speed(struct bnx2 *bp)
}
+static void __devinit
+bnx2_read_vpd_fw_ver(struct bnx2 *bp)
+{
+ int rc, i, v0_len = 0;
+ u8 *data;
+ u8 *v0_str = NULL;
+ bool mn_match = false;
+
+#define BNX2_VPD_NVRAM_OFFSET 0x300
+#define BNX2_VPD_LEN 128
+#define BNX2_MAX_VER_SLEN 30
+
+ data = kmalloc(256, GFP_KERNEL);
+ if (!data)
+ return;
+
+ rc = bnx2_nvram_read(bp, BNX2_VPD_NVRAM_OFFSET, data + BNX2_VPD_LEN,
+ BNX2_VPD_LEN);
+ if (rc)
+ goto vpd_done;
+
+ for (i = 0; i < BNX2_VPD_LEN; i += 4) {
+ data[i] = data[i + BNX2_VPD_LEN + 3];
+ data[i + 1] = data[i + BNX2_VPD_LEN + 2];
+ data[i + 2] = data[i + BNX2_VPD_LEN + 1];
+ data[i + 3] = data[i + BNX2_VPD_LEN];
+ }
+
+ for (i = 0; i <= BNX2_VPD_LEN - 3; ) {
+ unsigned char val = data[i];
+ unsigned int block_end;
+
+ if (val == 0x82 || val == 0x91) {
+ i = (i + 3 + (data[i + 1] + (data[i + 2] << 8)));
+ continue;
+ }
+
+ if (val != 0x90)
+ goto vpd_done;
+
+ block_end = (i + 3 + (data[i + 1] + (data[i + 2] << 8)));
+ i += 3;
+
+ if (block_end > BNX2_VPD_LEN)
+ goto vpd_done;
+
+ while (i < (block_end - 2)) {
+ int len = data[i + 2];
+
+ if (i + 3 + len > block_end)
+ goto vpd_done;
+
+ if (data[i] == 'M' && data[i + 1] == 'N') {
+ if (len != 4 ||
+ memcmp(&data[i + 3], "1028", 4))
+ goto vpd_done;
+ mn_match = true;
+
+ } else if (data[i] == 'V' && data[i + 1] == '0') {
+ if (len > BNX2_MAX_VER_SLEN)
+ goto vpd_done;
+
+ v0_len = len;
+ v0_str = &data[i + 3];
+ }
+ i += 3 + len;
+
+ if (mn_match && v0_str) {
+ memcpy(bp->fw_version, v0_str, v0_len);
+ bp->fw_version[v0_len] = ' ';
+ goto vpd_done;
+ }
+ }
+ goto vpd_done;
+ }
+
+vpd_done:
+ kfree(data);
+}
+
static int __devinit
bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
{
@@ -7808,10 +7975,18 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
goto err_out_unmap;
}
+ bnx2_read_vpd_fw_ver(bp);
+
+ j = strlen(bp->fw_version);
reg = bnx2_shmem_rd(bp, BNX2_DEV_INFO_BC_REV);
- for (i = 0, j = 0; i < 3; i++) {
+ for (i = 0; i < 3 && j < 24; i++) {
u8 num, k, skip0;
+ if (i == 0) {
+ bp->fw_version[j++] = 'b';
+ bp->fw_version[j++] = 'c';
+ bp->fw_version[j++] = ' ';
+ }
num = (u8) (reg >> (24 - (i * 8)));
for (k = 100, skip0 = 1; k >= 1; num %= k, k /= 10) {
if (num >= k || !skip0 || k == 1) {
@@ -7842,8 +8017,9 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
reg != BNX2_CONDITION_MFW_RUN_NONE) {
u32 addr = bnx2_shmem_rd(bp, BNX2_MFW_VER_PTR);
- bp->fw_version[j++] = ' ';
- for (i = 0; i < 3; i++) {
+ if (j < 32)
+ bp->fw_version[j++] = ' ';
+ for (i = 0; i < 3 && j < 28; i++) {
reg = bnx2_reg_rd_ind(bp, addr + i * 4);
reg = swab32(reg);
memcpy(&bp->fw_version[j], &reg, 4);
@@ -8264,6 +8440,7 @@ static pci_ers_result_t bnx2_io_slot_reset(struct pci_dev *pdev)
}
pci_set_master(pdev);
pci_restore_state(pdev);
+ pci_save_state(pdev);
if (netif_running(dev)) {
bnx2_set_power_state(bp, PCI_D0);
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index a4d83409f20..939dc44d50a 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -6345,6 +6345,8 @@ struct l2_fhdr {
#define BNX2_MCP_ROM 0x00150000
#define BNX2_MCP_SCRATCH 0x00160000
+#define BNX2_MCP_STATE_P1 0x0016f9c8
+#define BNX2_MCP_STATE_P0 0x0016fdc8
#define BNX2_SHM_HDR_SIGNATURE BNX2_MCP_SCRATCH
#define BNX2_SHM_HDR_SIGNATURE_SIG_MASK 0xffff0000
@@ -6559,6 +6561,7 @@ struct sw_pg {
struct sw_tx_bd {
struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(mapping)
unsigned short is_gso;
unsigned short nr_frags;
};
diff --git a/drivers/net/bnx2x.h b/drivers/net/bnx2x.h
index bbf842284eb..602ab86b639 100644
--- a/drivers/net/bnx2x.h
+++ b/drivers/net/bnx2x.h
@@ -24,6 +24,10 @@
#define BCM_VLAN 1
#endif
+#if defined(CONFIG_CNIC) || defined(CONFIG_CNIC_MODULE)
+#define BCM_CNIC 1
+#include "cnic_if.h"
+#endif
#define BNX2X_MULTI_QUEUE
@@ -255,9 +259,6 @@ struct bnx2x_eth_q_stats {
struct bnx2x_fastpath {
struct napi_struct napi;
-
- u8 is_rx_queue;
-
struct host_status_block *status_blk;
dma_addr_t status_blk_mapping;
@@ -762,7 +763,11 @@ struct bnx2x_eth_stats {
(offsetof(struct bnx2x_eth_stats, stat_name) / 4)
+#ifdef BCM_CNIC
+#define MAX_CONTEXT 15
+#else
#define MAX_CONTEXT 16
+#endif
union cdu_context {
struct eth_context eth;
@@ -811,13 +816,21 @@ struct bnx2x {
struct bnx2x_fastpath fp[MAX_CONTEXT];
void __iomem *regview;
void __iomem *doorbells;
+#ifdef BCM_CNIC
+#define BNX2X_DB_SIZE (18*BCM_PAGE_SIZE)
+#else
#define BNX2X_DB_SIZE (16*BCM_PAGE_SIZE)
+#endif
struct net_device *dev;
struct pci_dev *pdev;
atomic_t intr_sem;
+#ifdef BCM_CNIC
+ struct msix_entry msix_table[MAX_CONTEXT+2];
+#else
struct msix_entry msix_table[MAX_CONTEXT+1];
+#endif
#define INT_MODE_INTx 1
#define INT_MODE_MSI 2
#define INT_MODE_MSIX 3
@@ -863,8 +876,8 @@ struct bnx2x {
/* Flags for marking that there is a STAT_QUERY or
SET_MAC ramrod pending */
- u8 stats_pending;
- u8 set_mac_pending;
+ int stats_pending;
+ int set_mac_pending;
/* End of fields used in the performance code paths */
@@ -884,6 +897,7 @@ struct bnx2x {
#define BP_NOMCP(bp) (bp->flags & NO_MCP_FLAG)
#define HW_VLAN_TX_FLAG 0x400
#define HW_VLAN_RX_FLAG 0x800
+#define MF_FUNC_DIS 0x1000
int func;
#define BP_PORT(bp) (bp->func % PORT_MAX)
@@ -891,6 +905,11 @@ struct bnx2x {
#define BP_E1HVN(bp) (bp->func >> 1)
#define BP_L_ID(bp) (BP_E1HVN(bp) << 2)
+#ifdef BCM_CNIC
+#define BCM_CNIC_CID_START 16
+#define BCM_ISCSI_ETH_CL_ID 17
+#endif
+
int pm_cap;
int pcie_cap;
int mrrs;
@@ -944,13 +963,11 @@ struct bnx2x {
#define BNX2X_STATE_CLOSING_WAIT4_HALT 0x4000
#define BNX2X_STATE_CLOSING_WAIT4_DELETE 0x5000
#define BNX2X_STATE_CLOSING_WAIT4_UNLOAD 0x6000
-#define BNX2X_STATE_DISABLED 0xd000
#define BNX2X_STATE_DIAG 0xe000
#define BNX2X_STATE_ERROR 0xf000
int multi_mode;
- int num_rx_queues;
- int num_tx_queues;
+ int num_queues;
u32 rx_mode;
#define BNX2X_RX_MODE_NONE 0
@@ -960,28 +977,51 @@ struct bnx2x {
#define BNX2X_MAX_MULTICAST 64
#define BNX2X_MAX_EMUL_MULTI 16
+ u32 rx_mode_cl_mask;
+
dma_addr_t def_status_blk_mapping;
struct bnx2x_slowpath *slowpath;
dma_addr_t slowpath_mapping;
-#ifdef BCM_ISCSI
- void *t1;
- dma_addr_t t1_mapping;
- void *t2;
- dma_addr_t t2_mapping;
- void *timers;
- dma_addr_t timers_mapping;
- void *qm;
- dma_addr_t qm_mapping;
-#endif
-
int dropless_fc;
+#ifdef BCM_CNIC
+ u32 cnic_flags;
+#define BNX2X_CNIC_FLAG_MAC_SET 1
+
+ void *t1;
+ dma_addr_t t1_mapping;
+ void *t2;
+ dma_addr_t t2_mapping;
+ void *timers;
+ dma_addr_t timers_mapping;
+ void *qm;
+ dma_addr_t qm_mapping;
+ struct cnic_ops *cnic_ops;
+ void *cnic_data;
+ u32 cnic_tag;
+ struct cnic_eth_dev cnic_eth_dev;
+ struct host_status_block *cnic_sb;
+ dma_addr_t cnic_sb_mapping;
+#define CNIC_SB_ID(bp) BP_L_ID(bp)
+ struct eth_spe *cnic_kwq;
+ struct eth_spe *cnic_kwq_prod;
+ struct eth_spe *cnic_kwq_cons;
+ struct eth_spe *cnic_kwq_last;
+ u16 cnic_kwq_pending;
+ u16 cnic_spq_pending;
+ struct mutex cnic_mutex;
+ u8 iscsi_mac[6];
+#endif
+
int dmae_ready;
/* used to synchronize dmae accesses */
struct mutex dmae_mutex;
+ /* used to protect the FW mail box */
+ struct mutex fw_mb_mutex;
+
/* used to synchronize stats collecting */
int stats_state;
/* used by dmae command loader */
@@ -1030,20 +1070,15 @@ struct bnx2x {
};
-#define BNX2X_MAX_QUEUES(bp) (IS_E1HMF(bp) ? (MAX_CONTEXT/(2 * E1HVN_MAX)) \
- : (MAX_CONTEXT/2))
-#define BNX2X_NUM_QUEUES(bp) (bp->num_rx_queues + bp->num_tx_queues)
-#define is_multi(bp) (BNX2X_NUM_QUEUES(bp) > 2)
+#define BNX2X_MAX_QUEUES(bp) (IS_E1HMF(bp) ? (MAX_CONTEXT/E1HVN_MAX) \
+ : MAX_CONTEXT)
+#define BNX2X_NUM_QUEUES(bp) (bp->num_queues)
+#define is_multi(bp) (BNX2X_NUM_QUEUES(bp) > 1)
-#define for_each_rx_queue(bp, var) \
- for (var = 0; var < bp->num_rx_queues; var++)
-#define for_each_tx_queue(bp, var) \
- for (var = bp->num_rx_queues; \
- var < BNX2X_NUM_QUEUES(bp); var++)
#define for_each_queue(bp, var) \
for (var = 0; var < BNX2X_NUM_QUEUES(bp); var++)
#define for_each_nondefault_queue(bp, var) \
- for (var = 1; var < bp->num_rx_queues; var++)
+ for (var = 1; var < BNX2X_NUM_QUEUES(bp); var++)
void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32);
@@ -1147,7 +1182,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms,
#define MAX_SP_DESC_CNT (SP_DESC_CNT - 1)
-#define BNX2X_BTR 3
+#define BNX2X_BTR 1
#define MAX_SPQ_PENDING 8
diff --git a/drivers/net/bnx2x_hsi.h b/drivers/net/bnx2x_hsi.h
index 8e2261fad48..52585338ada 100644
--- a/drivers/net/bnx2x_hsi.h
+++ b/drivers/net/bnx2x_hsi.h
@@ -7,6 +7,20 @@
* the Free Software Foundation.
*/
+struct license_key {
+ u32 reserved[6];
+
+#if defined(__BIG_ENDIAN)
+ u16 max_iscsi_init_conn;
+ u16 max_iscsi_trgt_conn;
+#elif defined(__LITTLE_ENDIAN)
+ u16 max_iscsi_trgt_conn;
+ u16 max_iscsi_init_conn;
+#endif
+
+ u32 reserved_a[6];
+};
+
#define PORT_0 0
#define PORT_1 1
@@ -250,6 +264,7 @@ struct port_hw_cfg { /* port 0: 0x12c port 1: 0x2bc */
#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101 0x00000800
#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727 0x00000900
#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727_NOC 0x00000a00
+#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823 0x00000b00
#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE 0x0000fd00
#define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN 0x0000ff00
@@ -881,7 +896,7 @@ struct shmem_region { /* SharedMem Offset (size) */
struct shm_dev_info dev_info; /* 0x8 (0x438) */
- u8 reserved[52*PORT_MAX];
+ struct license_key drv_lic_key[PORT_MAX]; /* 0x440 (52*2=0x68) */
/* FW information (for internal FW use) */
u32 fw_info_fio_offset; /* 0x4a8 (0x4) */
@@ -1245,8 +1260,8 @@ struct host_func_stats {
#define BCM_5710_FW_MAJOR_VERSION 5
-#define BCM_5710_FW_MINOR_VERSION 0
-#define BCM_5710_FW_REVISION_VERSION 21
+#define BCM_5710_FW_MINOR_VERSION 2
+#define BCM_5710_FW_REVISION_VERSION 7
#define BCM_5710_FW_ENGINEERING_VERSION 0
#define BCM_5710_FW_COMPILE_FLAGS 1
diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c
index e32d3370862..cf5778919b4 100644
--- a/drivers/net/bnx2x_link.c
+++ b/drivers/net/bnx2x_link.c
@@ -1107,18 +1107,21 @@ static void bnx2x_set_parallel_detection(struct link_params *params,
MDIO_REG_BANK_SERDES_DIGITAL,
MDIO_SERDES_DIGITAL_A_1000X_CONTROL2,
&control2);
-
-
- control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN;
-
-
+ if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)
+ control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN;
+ else
+ control2 &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN;
+ DP(NETIF_MSG_LINK, "params->speed_cap_mask = 0x%x, control2 = 0x%x\n",
+ params->speed_cap_mask, control2);
CL45_WR_OVER_CL22(bp, params->port,
params->phy_addr,
MDIO_REG_BANK_SERDES_DIGITAL,
MDIO_SERDES_DIGITAL_A_1000X_CONTROL2,
control2);
- if (phy_flags & PHY_XGXS_FLAG) {
+ if ((phy_flags & PHY_XGXS_FLAG) &&
+ (params->speed_cap_mask &
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) {
DP(NETIF_MSG_LINK, "XGXS\n");
CL45_WR_OVER_CL22(bp, params->port,
@@ -1225,7 +1228,7 @@ static void bnx2x_set_autoneg(struct link_params *params,
params->phy_addr,
MDIO_REG_BANK_CL73_USERB0,
MDIO_CL73_USERB0_CL73_UCTRL,
- MDIO_CL73_USERB0_CL73_UCTRL_USTAT1_MUXSEL);
+ 0xe);
/* Enable BAM Station Manager*/
CL45_WR_OVER_CL22(bp, params->port,
@@ -1236,29 +1239,25 @@ static void bnx2x_set_autoneg(struct link_params *params,
MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_STATION_MNGR_EN |
MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_NP_AFTER_BP_EN);
- /* Merge CL73 and CL37 aneg resolution */
- CL45_RD_OVER_CL22(bp, params->port,
- params->phy_addr,
- MDIO_REG_BANK_CL73_USERB0,
- MDIO_CL73_USERB0_CL73_BAM_CTRL3,
- &reg_val);
-
- if (params->speed_cap_mask &
- PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) {
- /* Set the CL73 AN speed */
+ /* Advertise CL73 link speeds */
CL45_RD_OVER_CL22(bp, params->port,
params->phy_addr,
MDIO_REG_BANK_CL73_IEEEB1,
MDIO_CL73_IEEEB1_AN_ADV2,
&reg_val);
+ if (params->speed_cap_mask &
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)
+ reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4;
+ if (params->speed_cap_mask &
+ PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)
+ reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M_KX;
CL45_WR_OVER_CL22(bp, params->port,
params->phy_addr,
MDIO_REG_BANK_CL73_IEEEB1,
MDIO_CL73_IEEEB1_AN_ADV2,
- reg_val | MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4);
+ reg_val);
- }
/* CL73 Autoneg Enabled */
reg_val = MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN;
@@ -1351,6 +1350,7 @@ static void bnx2x_set_brcm_cl37_advertisment(struct link_params *params)
static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u16 *ieee_fc)
{
+ struct bnx2x *bp = params->bp;
*ieee_fc = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX;
/* resolve pause mode and advertisement
* Please refer to Table 28B-3 of the 802.3ab-1999 spec */
@@ -1380,18 +1380,30 @@ static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u16 *ieee_fc)
*ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE;
break;
}
+ DP(NETIF_MSG_LINK, "ieee_fc = 0x%x\n", *ieee_fc);
}
static void bnx2x_set_ieee_aneg_advertisment(struct link_params *params,
u16 ieee_fc)
{
struct bnx2x *bp = params->bp;
+ u16 val;
/* for AN, we are always publishing full duplex */
CL45_WR_OVER_CL22(bp, params->port,
params->phy_addr,
MDIO_REG_BANK_COMBO_IEEE0,
MDIO_COMBO_IEEE0_AUTO_NEG_ADV, ieee_fc);
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_CL73_IEEEB1,
+ MDIO_CL73_IEEEB1_AN_ADV1, &val);
+ val &= ~MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_BOTH;
+ val |= ((ieee_fc<<3) & MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_MASK);
+ CL45_WR_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_CL73_IEEEB1,
+ MDIO_CL73_IEEEB1_AN_ADV1, val);
}
static void bnx2x_restart_autoneg(struct link_params *params, u8 enable_cl73)
@@ -1609,6 +1621,39 @@ static u8 bnx2x_ext_phy_resolve_fc(struct link_params *params,
return ret;
}
+static u8 bnx2x_direct_parallel_detect_used(struct link_params *params)
+{
+ struct bnx2x *bp = params->bp;
+ u16 pd_10g, status2_1000x;
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_SERDES_DIGITAL,
+ MDIO_SERDES_DIGITAL_A_1000X_STATUS2,
+ &status2_1000x);
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_SERDES_DIGITAL,
+ MDIO_SERDES_DIGITAL_A_1000X_STATUS2,
+ &status2_1000x);
+ if (status2_1000x & MDIO_SERDES_DIGITAL_A_1000X_STATUS2_AN_DISABLED) {
+ DP(NETIF_MSG_LINK, "1G parallel detect link on port %d\n",
+ params->port);
+ return 1;
+ }
+
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_10G_PARALLEL_DETECT,
+ MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS,
+ &pd_10g);
+
+ if (pd_10g & MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS_PD_LINK) {
+ DP(NETIF_MSG_LINK, "10G parallel detect link on port %d\n",
+ params->port);
+ return 1;
+ }
+ return 0;
+}
static void bnx2x_flow_ctrl_resolve(struct link_params *params,
struct link_vars *vars,
@@ -1627,21 +1672,53 @@ static void bnx2x_flow_ctrl_resolve(struct link_params *params,
(!(vars->phy_flags & PHY_SGMII_FLAG)) &&
(XGXS_EXT_PHY_TYPE(params->ext_phy_config) ==
PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)) {
- CL45_RD_OVER_CL22(bp, params->port,
- params->phy_addr,
- MDIO_REG_BANK_COMBO_IEEE0,
- MDIO_COMBO_IEEE0_AUTO_NEG_ADV,
- &ld_pause);
- CL45_RD_OVER_CL22(bp, params->port,
- params->phy_addr,
- MDIO_REG_BANK_COMBO_IEEE0,
- MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1,
- &lp_pause);
- pause_result = (ld_pause &
+ if (bnx2x_direct_parallel_detect_used(params)) {
+ vars->flow_ctrl = params->req_fc_auto_adv;
+ return;
+ }
+ if ((gp_status &
+ (MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_AUTONEG_COMPLETE |
+ MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_MR_LP_NP_AN_ABLE)) ==
+ (MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_AUTONEG_COMPLETE |
+ MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_MR_LP_NP_AN_ABLE)) {
+
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_CL73_IEEEB1,
+ MDIO_CL73_IEEEB1_AN_ADV1,
+ &ld_pause);
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_CL73_IEEEB1,
+ MDIO_CL73_IEEEB1_AN_LP_ADV1,
+ &lp_pause);
+ pause_result = (ld_pause &
+ MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_MASK)
+ >> 8;
+ pause_result |= (lp_pause &
+ MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE_MASK)
+ >> 10;
+ DP(NETIF_MSG_LINK, "pause_result CL73 0x%x\n",
+ pause_result);
+ } else {
+
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_COMBO_IEEE0,
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV,
+ &ld_pause);
+ CL45_RD_OVER_CL22(bp, params->port,
+ params->phy_addr,
+ MDIO_REG_BANK_COMBO_IEEE0,
+ MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1,
+ &lp_pause);
+ pause_result = (ld_pause &
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>5;
- pause_result |= (lp_pause &
+ pause_result |= (lp_pause &
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>7;
- DP(NETIF_MSG_LINK, "pause_result 0x%x\n", pause_result);
+ DP(NETIF_MSG_LINK, "pause_result CL37 0x%x\n",
+ pause_result);
+ }
bnx2x_pause_resolve(vars, pause_result);
} else if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) &&
(bnx2x_ext_phy_resolve_fc(params, vars))) {
@@ -1853,6 +1930,8 @@ static u8 bnx2x_link_settings_status(struct link_params *params,
(XGXS_EXT_PHY_TYPE(params->ext_phy_config) ==
PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) ||
(XGXS_EXT_PHY_TYPE(params->ext_phy_config) ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706) ||
+ (XGXS_EXT_PHY_TYPE(params->ext_phy_config) ==
PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726))) {
vars->autoneg = AUTO_NEG_ENABLED;
@@ -1987,8 +2066,7 @@ static u8 bnx2x_emac_program(struct link_params *params,
GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE,
mode);
- bnx2x_set_led(bp, params->port, LED_MODE_OPER,
- line_speed, params->hw_led_mode, params->chip_id);
+ bnx2x_set_led(params, LED_MODE_OPER, line_speed);
return 0;
}
@@ -2122,6 +2200,8 @@ static void bnx2x_ext_phy_reset(struct link_params *params,
MDIO_PMA_REG_CTRL,
1<<15);
break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823:
+ break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE:
DP(NETIF_MSG_LINK, "XGXS PHY Failure detected\n");
break;
@@ -2512,16 +2592,11 @@ static void bnx2x_bcm8726_external_rom_boot(struct link_params *params)
/* Need to wait 100ms after reset */
msleep(100);
- /* Set serial boot control for external load */
- bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr,
- MDIO_PMA_DEVAD,
- MDIO_PMA_REG_MISC_CTRL1, 0x0001);
-
/* Micro controller re-boot */
bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr,
MDIO_PMA_DEVAD,
MDIO_PMA_REG_GEN_CTRL,
- MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP);
+ 0x018B);
/* Set soft reset */
bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr,
@@ -2529,14 +2604,10 @@ static void bnx2x_bcm8726_external_rom_boot(struct link_params *params)
MDIO_PMA_REG_GEN_CTRL,
MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET);
- /* Set PLL register value to be same like in P13 ver */
bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_PLL_CTRL,
- 0x73A0);
+ MDIO_PMA_REG_MISC_CTRL1, 0x0001);
- /* Clear soft reset.
- Will automatically reset micro-controller re-boot */
bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr,
MDIO_PMA_DEVAD,
MDIO_PMA_REG_GEN_CTRL,
@@ -3462,8 +3533,8 @@ static void bnx2x_8481_set_10G_led_mode(struct link_params *params,
MDIO_PMA_REG_8481_LINK_SIGNAL,
&val1);
/* Set bit 2 to 0, and bits [1:0] to 10 */
- val1 &= ~((1<<0) | (1<<2)); /* Clear bits 0,2*/
- val1 |= (1<<1); /* Set bit 1 */
+ val1 &= ~((1<<0) | (1<<2) | (1<<7)); /* Clear bits 0,2,7*/
+ val1 |= ((1<<1) | (1<<6)); /* Set bit 1, 6 */
bnx2x_cl45_write(bp, params->port,
ext_phy_type,
@@ -3497,36 +3568,19 @@ static void bnx2x_8481_set_10G_led_mode(struct link_params *params,
MDIO_PMA_REG_8481_LED2_MASK,
0);
- /* LED3 (10G/1G/100/10G Activity) */
- bnx2x_cl45_read(bp, params->port,
- ext_phy_type,
- ext_phy_addr,
- MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LINK_SIGNAL,
- &val1);
- /* Enable blink based on source 4(Activity) */
- val1 &= ~((1<<7) | (1<<8)); /* Clear bits 7,8 */
- val1 |= (1<<6); /* Set only bit 6 */
+ /* Unmask LED3 for 10G link */
bnx2x_cl45_write(bp, params->port,
ext_phy_type,
ext_phy_addr,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LINK_SIGNAL,
- val1);
-
- bnx2x_cl45_read(bp, params->port,
- ext_phy_type,
- ext_phy_addr,
- MDIO_PMA_DEVAD,
MDIO_PMA_REG_8481_LED3_MASK,
- &val1);
- val1 |= (1<<4); /* Unmask LED3 for 10G link */
+ 0x6);
bnx2x_cl45_write(bp, params->port,
ext_phy_type,
ext_phy_addr,
MDIO_PMA_DEVAD,
- MDIO_PMA_REG_8481_LED3_MASK,
- val1);
+ MDIO_PMA_REG_8481_LED3_BLINK,
+ 0);
}
@@ -3544,7 +3598,10 @@ static void bnx2x_init_internal_phy(struct link_params *params,
bnx2x_set_preemphasis(params);
/* forced speed requested? */
- if (vars->line_speed != SPEED_AUTO_NEG) {
+ if (vars->line_speed != SPEED_AUTO_NEG ||
+ ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) ==
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) &&
+ params->loopback_mode == LOOPBACK_EXT)) {
DP(NETIF_MSG_LINK, "not SGMII, no AN\n");
/* disable autoneg */
@@ -3693,19 +3750,6 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars)
}
}
/* Force speed */
- /* First enable LASI */
- bnx2x_cl45_write(bp, params->port,
- ext_phy_type,
- ext_phy_addr,
- MDIO_PMA_DEVAD,
- MDIO_PMA_REG_RX_ALARM_CTRL,
- 0x0400);
- bnx2x_cl45_write(bp, params->port,
- ext_phy_type,
- ext_phy_addr,
- MDIO_PMA_DEVAD,
- MDIO_PMA_REG_LASI_CTRL, 0x0004);
-
if (params->req_line_speed == SPEED_10000) {
DP(NETIF_MSG_LINK, "XGXS 8706 force 10Gbps\n");
@@ -3715,6 +3759,9 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars)
MDIO_PMA_DEVAD,
MDIO_PMA_REG_DIGITAL_CTRL,
0x400);
+ bnx2x_cl45_write(bp, params->port, ext_phy_type,
+ ext_phy_addr, MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_LASI_CTRL, 1);
} else {
/* Force 1Gbps using autoneg with 1G
advertisment */
@@ -3756,6 +3803,17 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars)
MDIO_AN_DEVAD,
MDIO_AN_REG_CTRL,
0x1200);
+ bnx2x_cl45_write(bp, params->port,
+ ext_phy_type,
+ ext_phy_addr,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_RX_ALARM_CTRL,
+ 0x0400);
+ bnx2x_cl45_write(bp, params->port,
+ ext_phy_type,
+ ext_phy_addr,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_LASI_CTRL, 0x0004);
}
bnx2x_save_bcm_spirom_ver(bp, params->port,
@@ -4291,6 +4349,7 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars)
break;
}
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823:
/* This phy uses the NIG latch mechanism since link
indication arrives through its LED4 and not via
its LASI signal, so we get steady signal
@@ -4298,6 +4357,12 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars)
bnx2x_bits_en(bp, NIG_REG_LATCH_BC_0 + params->port*4,
1 << NIG_LATCH_BC_ENABLE_MI_INT);
+ bnx2x_cl45_write(bp, params->port,
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481,
+ ext_phy_addr,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_CTRL, 0x0000);
+
bnx2x_8481_set_led4(params, ext_phy_type, ext_phy_addr);
if (params->req_line_speed == SPEED_AUTO_NEG) {
@@ -4394,17 +4459,12 @@ static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars)
PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) {
DP(NETIF_MSG_LINK, "Advertising 10G\n");
/* Restart autoneg for 10G*/
- bnx2x_cl45_read(bp, params->port,
- ext_phy_type,
- ext_phy_addr,
- MDIO_AN_DEVAD,
- MDIO_AN_REG_CTRL, &val);
- val |= 0x200;
+
bnx2x_cl45_write(bp, params->port,
ext_phy_type,
ext_phy_addr,
MDIO_AN_DEVAD,
- MDIO_AN_REG_CTRL, val);
+ MDIO_AN_REG_CTRL, 0x3200);
}
} else {
/* Force speed */
@@ -4657,8 +4717,8 @@ static u8 bnx2x_ext_phy_is_link_up(struct link_params *params,
0xc809, &val1);
DP(NETIF_MSG_LINK, "8705 1.c809 val=0x%x\n", val1);
- ext_phy_link_up = ((rx_sd & 0x1) && (val1 & (1<<9))
- && ((val1 & (1<<8)) == 0));
+ ext_phy_link_up = ((rx_sd & 0x1) && (val1 & (1<<9)) &&
+ ((val1 & (1<<8)) == 0));
if (ext_phy_link_up)
vars->line_speed = SPEED_10000;
break;
@@ -5148,6 +5208,7 @@ static u8 bnx2x_ext_phy_is_link_up(struct link_params *params,
}
break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823:
/* Check 10G-BaseT link status */
/* Check PMD signal ok */
bnx2x_cl45_read(bp, params->port, ext_phy_type,
@@ -5363,8 +5424,10 @@ static void bnx2x_link_int_ack(struct link_params *params,
(NIG_STATUS_XGXS0_LINK10G |
NIG_STATUS_XGXS0_LINK_STATUS |
NIG_STATUS_SERDES0_LINK_STATUS));
- if (XGXS_EXT_PHY_TYPE(params->ext_phy_config)
- == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481) {
+ if ((XGXS_EXT_PHY_TYPE(params->ext_phy_config)
+ == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481) ||
+ (XGXS_EXT_PHY_TYPE(params->ext_phy_config)
+ == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823)) {
bnx2x_8481_rearm_latch_signal(bp, port, is_mi_int);
}
if (vars->phy_link_up) {
@@ -5477,6 +5540,7 @@ u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded,
status = bnx2x_format_ver(spirom_ver, version, len);
break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823:
spirom_ver = ((spirom_ver & 0xF80) >> 7) << 16 |
(spirom_ver & 0x7F);
status = bnx2x_format_ver(spirom_ver, version, len);
@@ -5728,13 +5792,15 @@ u8 bnx2x_override_led_value(struct bnx2x *bp, u8 port,
}
-u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed,
- u16 hw_led_mode, u32 chip_id)
+u8 bnx2x_set_led(struct link_params *params, u8 mode, u32 speed)
{
+ u8 port = params->port;
+ u16 hw_led_mode = params->hw_led_mode;
u8 rc = 0;
u32 tmp;
u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
-
+ u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config);
+ struct bnx2x *bp = params->bp;
DP(NETIF_MSG_LINK, "bnx2x_set_led: port %x, mode %d\n", port, mode);
DP(NETIF_MSG_LINK, "speed 0x%x, hw_led_mode 0x%x\n",
speed, hw_led_mode);
@@ -5749,7 +5815,14 @@ u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed,
break;
case LED_MODE_OPER:
- REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode);
+ if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) {
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
+ REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 1);
+ } else {
+ REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
+ hw_led_mode);
+ }
+
REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 +
port*4, 0);
/* Set blinking rate to ~15.9Hz */
@@ -5761,7 +5834,7 @@ u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed,
EMAC_WR(bp, EMAC_REG_EMAC_LED,
(tmp & (~EMAC_LED_OVERRIDE)));
- if (!CHIP_IS_E1H(bp) &&
+ if (CHIP_IS_E1(bp) &&
((speed == SPEED_2500) ||
(speed == SPEED_1000) ||
(speed == SPEED_100) ||
@@ -5864,6 +5937,7 @@ static u8 bnx2x_link_initialize(struct link_params *params,
if (non_ext_phy ||
(ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) ||
+ (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706) ||
(ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) ||
(params->loopback_mode == LOOPBACK_EXT_PHY)) {
if (params->req_line_speed == SPEED_AUTO_NEG)
@@ -6030,10 +6104,7 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars)
REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE +
params->port*4, 0);
- bnx2x_set_led(bp, params->port, LED_MODE_OPER,
- vars->line_speed, params->hw_led_mode,
- params->chip_id);
-
+ bnx2x_set_led(params, LED_MODE_OPER, vars->line_speed);
} else
/* No loopback */
{
@@ -6091,15 +6162,13 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
{
struct bnx2x *bp = params->bp;
u32 ext_phy_config = params->ext_phy_config;
- u16 hw_led_mode = params->hw_led_mode;
- u32 chip_id = params->chip_id;
u8 port = params->port;
u32 ext_phy_type = XGXS_EXT_PHY_TYPE(ext_phy_config);
u32 val = REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region, dev_info.
port_feature_config[params->port].
config));
-
+ DP(NETIF_MSG_LINK, "Resetting the link of port %d\n", port);
/* disable attentions */
vars->link_status = 0;
bnx2x_update_mng(params, vars->link_status);
@@ -6127,7 +6196,7 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
* Hold it as vars low
*/
/* clear link led */
- bnx2x_set_led(bp, port, LED_MODE_OFF, 0, hw_led_mode, chip_id);
+ bnx2x_set_led(params, LED_MODE_OFF, 0);
if (reset_ext_phy) {
switch (ext_phy_type) {
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
@@ -6163,6 +6232,22 @@ u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
bnx2x_8726_reset_phy(bp, params->port, ext_phy_addr);
break;
}
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823:
+ {
+ u8 ext_phy_addr =
+ XGXS_EXT_PHY_ADDR(params->ext_phy_config);
+ bnx2x_cl45_write(bp, port,
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481,
+ ext_phy_addr,
+ MDIO_AN_DEVAD,
+ MDIO_AN_REG_CTRL, 0x0000);
+ bnx2x_cl45_write(bp, port,
+ PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481,
+ ext_phy_addr,
+ MDIO_PMA_DEVAD,
+ MDIO_PMA_REG_CTRL, 1);
+ break;
+ }
default:
/* HW reset */
bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1,
@@ -6198,9 +6283,7 @@ static u8 bnx2x_update_link_down(struct link_params *params,
u8 port = params->port;
DP(NETIF_MSG_LINK, "Port %x: Link is down\n", port);
- bnx2x_set_led(bp, port, LED_MODE_OFF,
- 0, params->hw_led_mode,
- params->chip_id);
+ bnx2x_set_led(params, LED_MODE_OFF, 0);
/* indicate no mac active */
vars->mac_type = MAC_TYPE_NONE;
@@ -6237,15 +6320,13 @@ static u8 bnx2x_update_link_up(struct link_params *params,
vars->link_status |= LINK_STATUS_LINK_UP;
if (link_10g) {
bnx2x_bmac_enable(params, vars, 0);
- bnx2x_set_led(bp, port, LED_MODE_OPER,
- SPEED_10000, params->hw_led_mode,
- params->chip_id);
-
+ bnx2x_set_led(params, LED_MODE_OPER, SPEED_10000);
} else {
- bnx2x_emac_enable(params, vars, 0);
rc = bnx2x_emac_program(params, vars->line_speed,
vars->duplex);
+ bnx2x_emac_enable(params, vars, 0);
+
/* AN complete? */
if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) {
if (!(vars->phy_flags &
@@ -6343,6 +6424,7 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars)
if ((ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT) &&
(ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) &&
+ (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706) &&
(ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) &&
(ext_phy_link_up && !vars->phy_link_up))
bnx2x_init_internal_phy(params, vars, 0);
@@ -6578,6 +6660,13 @@ static u8 bnx2x_8726_common_init_phy(struct bnx2x *bp, u32 shmem_base)
return 0;
}
+
+static u8 bnx2x_84823_common_init_phy(struct bnx2x *bp, u32 shmem_base)
+{
+ /* HW reset */
+ bnx2x_ext_phy_hw_reset(bp, 1);
+ return 0;
+}
u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base)
{
u8 rc = 0;
@@ -6607,7 +6696,9 @@ u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base)
/* GPIO1 affects both ports, so there's need to pull
it for single port alone */
rc = bnx2x_8726_common_init_phy(bp, shmem_base);
-
+ break;
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823:
+ rc = bnx2x_84823_common_init_phy(bp, shmem_base);
break;
default:
DP(NETIF_MSG_LINK,
diff --git a/drivers/net/bnx2x_link.h b/drivers/net/bnx2x_link.h
index f3e252264e1..40c2981de8e 100644
--- a/drivers/net/bnx2x_link.h
+++ b/drivers/net/bnx2x_link.h
@@ -178,8 +178,7 @@ u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded,
Basically, the CLC takes care of the led for the link, but in case one needs
to set/unset the led unnaturally, set the "mode" to LED_MODE_OPER to
blink the led, and LED_MODE_OFF to set the led off.*/
-u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed,
- u16 hw_led_mode, u32 chip_id);
+u8 bnx2x_set_led(struct link_params *params, u8 mode, u32 speed);
#define LED_MODE_OFF 0
#define LED_MODE_OPER 2
diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c
index 20f0ed956df..77ba13520d8 100644
--- a/drivers/net/bnx2x_main.c
+++ b/drivers/net/bnx2x_main.c
@@ -49,6 +49,7 @@
#include <linux/prefetch.h>
#include <linux/zlib.h>
#include <linux/io.h>
+#include <linux/stringify.h>
#include "bnx2x.h"
@@ -56,15 +57,20 @@
#include "bnx2x_init_ops.h"
#include "bnx2x_dump.h"
-#define DRV_MODULE_VERSION "1.52.1"
-#define DRV_MODULE_RELDATE "2009/08/12"
+#define DRV_MODULE_VERSION "1.52.1-5"
+#define DRV_MODULE_RELDATE "2009/11/09"
#define BNX2X_BC_VER 0x040200
#include <linux/firmware.h>
#include "bnx2x_fw_file_hdr.h"
/* FW files */
-#define FW_FILE_PREFIX_E1 "bnx2x-e1-"
-#define FW_FILE_PREFIX_E1H "bnx2x-e1h-"
+#define FW_FILE_VERSION \
+ __stringify(BCM_5710_FW_MAJOR_VERSION) "." \
+ __stringify(BCM_5710_FW_MINOR_VERSION) "." \
+ __stringify(BCM_5710_FW_REVISION_VERSION) "." \
+ __stringify(BCM_5710_FW_ENGINEERING_VERSION)
+#define FW_FILE_NAME_E1 "bnx2x-e1-" FW_FILE_VERSION ".fw"
+#define FW_FILE_NAME_E1H "bnx2x-e1h-" FW_FILE_VERSION ".fw"
/* Time in jiffies before concluding the transmitter is hung */
#define TX_TIMEOUT (5*HZ)
@@ -77,21 +83,18 @@ MODULE_AUTHOR("Eliezer Tamir");
MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710/57711/57711E Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
+MODULE_FIRMWARE(FW_FILE_NAME_E1);
+MODULE_FIRMWARE(FW_FILE_NAME_E1H);
static int multi_mode = 1;
module_param(multi_mode, int, 0);
MODULE_PARM_DESC(multi_mode, " Multi queue mode "
"(0 Disable; 1 Enable (default))");
-static int num_rx_queues;
-module_param(num_rx_queues, int, 0);
-MODULE_PARM_DESC(num_rx_queues, " Number of Rx queues for multi_mode=1"
- " (default is half number of CPUs)");
-
-static int num_tx_queues;
-module_param(num_tx_queues, int, 0);
-MODULE_PARM_DESC(num_tx_queues, " Number of Tx queues for multi_mode=1"
- " (default is half number of CPUs)");
+static int num_queues;
+module_param(num_queues, int, 0);
+MODULE_PARM_DESC(num_queues, " Number of queues for multi_mode=1"
+ " (default is as a number of CPUs)");
static int disable_tpa;
module_param(disable_tpa, int, 0);
@@ -550,7 +553,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp)
bp->def_att_idx, bp->attn_state, bp->spq_prod_idx);
/* Rx */
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
BNX2X_ERR("fp%d: rx_bd_prod(%x) rx_bd_cons(%x)"
@@ -567,7 +570,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp)
}
/* Tx */
- for_each_tx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
BNX2X_ERR("fp%d: tx_pkt_prod(%x) tx_pkt_cons(%x)"
@@ -582,7 +585,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp)
/* Rings */
/* Rx */
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
start = RX_BD(le16_to_cpu(*fp->rx_cons_sb) - 10);
@@ -616,7 +619,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp)
}
/* Tx */
- for_each_tx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
start = TX_BD(le16_to_cpu(*fp->tx_cons_sb) - 10);
@@ -742,6 +745,9 @@ static void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw)
if (msix) {
synchronize_irq(bp->msix_table[0].vector);
offset = 1;
+#ifdef BCM_CNIC
+ offset++;
+#endif
for_each_queue(bp, i)
synchronize_irq(bp->msix_table[i + offset].vector);
} else
@@ -781,21 +787,13 @@ static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 sb_id,
barrier();
}
-static inline u16 bnx2x_update_fpsb_idx(struct bnx2x_fastpath *fp)
+static inline void bnx2x_update_fpsb_idx(struct bnx2x_fastpath *fp)
{
struct host_status_block *fpsb = fp->status_blk;
- u16 rc = 0;
barrier(); /* status block is written to by the chip */
- if (fp->fp_c_idx != fpsb->c_status_block.status_block_index) {
- fp->fp_c_idx = fpsb->c_status_block.status_block_index;
- rc |= 1;
- }
- if (fp->fp_u_idx != fpsb->u_status_block.status_block_index) {
- fp->fp_u_idx = fpsb->u_status_block.status_block_index;
- rc |= 2;
- }
- return rc;
+ fp->fp_c_idx = fpsb->c_status_block.status_block_index;
+ fp->fp_u_idx = fpsb->u_status_block.status_block_index;
}
static u16 bnx2x_ack_int(struct bnx2x *bp)
@@ -835,6 +833,9 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
u16 bd_idx = TX_BD(tx_buf->first_bd), new_cons;
int nbd;
+ /* prefetch skb end pointer to speedup dev_kfree_skb() */
+ prefetch(&skb->end);
+
DP(BNX2X_MSG_OFF, "pkt_idx %d buff @(%p)->skb %p\n",
idx, tx_buf, skb);
@@ -879,7 +880,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
/* release skb */
WARN_ON(!skb);
- dev_kfree_skb_any(skb);
+ dev_kfree_skb(skb);
tx_buf->first_bd = 0;
tx_buf->skb = NULL;
@@ -909,19 +910,28 @@ static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp)
return (s16)(fp->bp->tx_ring_size) - used;
}
-static void bnx2x_tx_int(struct bnx2x_fastpath *fp)
+static inline int bnx2x_has_tx_work(struct bnx2x_fastpath *fp)
+{
+ u16 hw_cons;
+
+ /* Tell compiler that status block fields can change */
+ barrier();
+ hw_cons = le16_to_cpu(*fp->tx_cons_sb);
+ return hw_cons != fp->tx_pkt_cons;
+}
+
+static int bnx2x_tx_int(struct bnx2x_fastpath *fp)
{
struct bnx2x *bp = fp->bp;
struct netdev_queue *txq;
u16 hw_cons, sw_cons, bd_cons = fp->tx_bd_cons;
- int done = 0;
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
- return;
+ return -1;
#endif
- txq = netdev_get_tx_queue(bp->dev, fp->index - bp->num_rx_queues);
+ txq = netdev_get_tx_queue(bp->dev, fp->index);
hw_cons = le16_to_cpu(*fp->tx_cons_sb);
sw_cons = fp->tx_pkt_cons;
@@ -942,7 +952,6 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp)
*/
bd_cons = bnx2x_free_tx_pkt(bp, fp, pkt_cons);
sw_cons++;
- done++;
}
fp->tx_pkt_cons = sw_cons;
@@ -964,8 +973,12 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp)
(bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3))
netif_tx_wake_queue(txq);
}
+ return 0;
}
+#ifdef BCM_CNIC
+static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid);
+#endif
static void bnx2x_sp_event(struct bnx2x_fastpath *fp,
union eth_rx_cqe *rr_cqe)
@@ -1022,16 +1035,24 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp,
bnx2x_fp(bp, cid, state) = BNX2X_FP_STATE_CLOSED;
break;
+#ifdef BCM_CNIC
+ case (RAMROD_CMD_ID_ETH_CFC_DEL | BNX2X_STATE_OPEN):
+ DP(NETIF_MSG_IFDOWN, "got delete ramrod for CID %d\n", cid);
+ bnx2x_cnic_cfc_comp(bp, cid);
+ break;
+#endif
case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_OPEN):
case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_DIAG):
DP(NETIF_MSG_IFUP, "got set mac ramrod\n");
- bp->set_mac_pending = 0;
+ bp->set_mac_pending--;
+ smp_wmb();
break;
case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_CLOSING_WAIT4_HALT):
- case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_DISABLED):
DP(NETIF_MSG_IFDOWN, "got (un)set mac ramrod\n");
+ bp->set_mac_pending--;
+ smp_wmb();
break;
default:
@@ -1539,6 +1560,8 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
} else {
rx_buf = &fp->rx_buf_ring[bd_cons];
skb = rx_buf->skb;
+ prefetch(skb);
+ prefetch((u8 *)skb + 256);
len = le16_to_cpu(cqe->fast_path_cqe.pkt_len);
pad = cqe->fast_path_cqe.placement_offset;
@@ -1720,27 +1743,13 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
if (unlikely(bp->panic))
return IRQ_HANDLED;
#endif
- /* Handle Rx or Tx according to MSI-X vector */
- if (fp->is_rx_queue) {
- prefetch(fp->rx_cons_sb);
- prefetch(&fp->status_blk->u_status_block.status_block_index);
- napi_schedule(&bnx2x_fp(bp, fp->index, napi));
-
- } else {
- prefetch(fp->tx_cons_sb);
- prefetch(&fp->status_blk->c_status_block.status_block_index);
-
- bnx2x_update_fpsb_idx(fp);
- rmb();
- bnx2x_tx_int(fp);
-
- /* Re-enable interrupts */
- bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID,
- le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1);
- bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID,
- le16_to_cpu(fp->fp_c_idx), IGU_INT_ENABLE, 1);
- }
+ /* Handle Rx and Tx according to MSI-X vector */
+ prefetch(fp->rx_cons_sb);
+ prefetch(fp->tx_cons_sb);
+ prefetch(&fp->status_blk->u_status_block.status_block_index);
+ prefetch(&fp->status_blk->c_status_block.status_block_index);
+ napi_schedule(&bnx2x_fp(bp, fp->index, napi));
return IRQ_HANDLED;
}
@@ -1775,35 +1784,32 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
mask = 0x2 << fp->sb_id;
if (status & mask) {
- /* Handle Rx or Tx according to SB id */
- if (fp->is_rx_queue) {
- prefetch(fp->rx_cons_sb);
- prefetch(&fp->status_blk->u_status_block.
- status_block_index);
-
- napi_schedule(&bnx2x_fp(bp, fp->index, napi));
-
- } else {
- prefetch(fp->tx_cons_sb);
- prefetch(&fp->status_blk->c_status_block.
- status_block_index);
-
- bnx2x_update_fpsb_idx(fp);
- rmb();
- bnx2x_tx_int(fp);
-
- /* Re-enable interrupts */
- bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID,
- le16_to_cpu(fp->fp_u_idx),
- IGU_INT_NOP, 1);
- bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID,
- le16_to_cpu(fp->fp_c_idx),
- IGU_INT_ENABLE, 1);
- }
+ /* Handle Rx and Tx according to SB id */
+ prefetch(fp->rx_cons_sb);
+ prefetch(&fp->status_blk->u_status_block.
+ status_block_index);
+ prefetch(fp->tx_cons_sb);
+ prefetch(&fp->status_blk->c_status_block.
+ status_block_index);
+ napi_schedule(&bnx2x_fp(bp, fp->index, napi));
status &= ~mask;
}
}
+#ifdef BCM_CNIC
+ mask = 0x2 << CNIC_SB_ID(bp);
+ if (status & (mask | 0x1)) {
+ struct cnic_ops *c_ops = NULL;
+
+ rcu_read_lock();
+ c_ops = rcu_dereference(bp->cnic_ops);
+ if (c_ops)
+ c_ops->cnic_handler(bp->cnic_data, NULL);
+ rcu_read_unlock();
+
+ status &= ~mask;
+ }
+#endif
if (unlikely(status & 0x1)) {
queue_delayed_work(bnx2x_wq, &bp->sp_task, 0);
@@ -2128,18 +2134,30 @@ static void bnx2x_calc_fc_adv(struct bnx2x *bp)
static void bnx2x_link_report(struct bnx2x *bp)
{
- if (bp->state == BNX2X_STATE_DISABLED) {
+ if (bp->flags & MF_FUNC_DIS) {
netif_carrier_off(bp->dev);
printk(KERN_ERR PFX "%s NIC Link is Down\n", bp->dev->name);
return;
}
if (bp->link_vars.link_up) {
+ u16 line_speed;
+
if (bp->state == BNX2X_STATE_OPEN)
netif_carrier_on(bp->dev);
printk(KERN_INFO PFX "%s NIC Link is Up, ", bp->dev->name);
- printk("%d Mbps ", bp->link_vars.line_speed);
+ line_speed = bp->link_vars.line_speed;
+ if (IS_E1HMF(bp)) {
+ u16 vn_max_rate;
+
+ vn_max_rate =
+ ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >>
+ FUNC_MF_CFG_MAX_BW_SHIFT) * 100;
+ if (vn_max_rate < line_speed)
+ line_speed = vn_max_rate;
+ }
+ printk("%d Mbps ", line_speed);
if (bp->link_vars.duplex == DUPLEX_FULL)
printk("full duplex");
@@ -2304,8 +2322,14 @@ static void bnx2x_calc_vn_weight_sum(struct bnx2x *bp)
}
/* ... only if all min rates are zeros - disable fairness */
- if (all_zero)
- bp->vn_weight_sum = 0;
+ if (all_zero) {
+ bp->cmng.flags.cmng_enables &=
+ ~CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
+ DP(NETIF_MSG_IFUP, "All MIN values are zeroes"
+ " fairness will be disabled\n");
+ } else
+ bp->cmng.flags.cmng_enables |=
+ CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
}
static void bnx2x_init_vn_minmax(struct bnx2x *bp, int func)
@@ -2324,17 +2348,14 @@ static void bnx2x_init_vn_minmax(struct bnx2x *bp, int func)
} else {
vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >>
FUNC_MF_CFG_MIN_BW_SHIFT) * 100;
- /* If fairness is enabled (not all min rates are zeroes) and
- if current min rate is zero - set it to 1.
- This is a requirement of the algorithm. */
- if (bp->vn_weight_sum && (vn_min_rate == 0))
+ /* If min rate is zero - set it to 1 */
+ if (!vn_min_rate)
vn_min_rate = DEF_MIN_RATE;
vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >>
FUNC_MF_CFG_MAX_BW_SHIFT) * 100;
}
-
DP(NETIF_MSG_IFUP,
- "func %d: vn_min_rate=%d vn_max_rate=%d vn_weight_sum=%d\n",
+ "func %d: vn_min_rate %d vn_max_rate %d vn_weight_sum %d\n",
func, vn_min_rate, vn_max_rate, bp->vn_weight_sum);
memset(&m_rs_vn, 0, sizeof(struct rate_shaping_vars_per_vn));
@@ -2405,8 +2426,7 @@ static void bnx2x_link_attn(struct bnx2x *bp)
memset(&(pstats->mac_stx[0]), 0,
sizeof(struct mac_stx));
}
- if ((bp->state == BNX2X_STATE_OPEN) ||
- (bp->state == BNX2X_STATE_DISABLED))
+ if (bp->state == BNX2X_STATE_OPEN)
bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
}
@@ -2449,9 +2469,7 @@ static void bnx2x_link_attn(struct bnx2x *bp)
static void bnx2x__link_status_update(struct bnx2x *bp)
{
- int func = BP_FUNC(bp);
-
- if (bp->state != BNX2X_STATE_OPEN)
+ if ((bp->state != BNX2X_STATE_OPEN) || (bp->flags & MF_FUNC_DIS))
return;
bnx2x_link_status_update(&bp->link_params, &bp->link_vars);
@@ -2461,7 +2479,6 @@ static void bnx2x__link_status_update(struct bnx2x *bp)
else
bnx2x_stats_handle(bp, STATS_EVENT_STOP);
- bp->mf_config = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config);
bnx2x_calc_vn_weight_sum(bp);
/* indicate link status */
@@ -2501,6 +2518,7 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command)
u32 cnt = 1;
u8 delay = CHIP_REV_IS_SLOW(bp) ? 100 : 10;
+ mutex_lock(&bp->fw_mb_mutex);
SHMEM_WR(bp, func_mb[func].drv_mb_header, (command | seq));
DP(BNX2X_MSG_MCP, "wrote command (%x) to FW MB\n", (command | seq));
@@ -2510,8 +2528,8 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command)
rc = SHMEM_RD(bp, func_mb[func].fw_mb_header);
- /* Give the FW up to 2 second (200*10ms) */
- } while ((seq != (rc & FW_MSG_SEQ_NUMBER_MASK)) && (cnt++ < 200));
+ /* Give the FW up to 5 second (500*10ms) */
+ } while ((seq != (rc & FW_MSG_SEQ_NUMBER_MASK)) && (cnt++ < 500));
DP(BNX2X_MSG_MCP, "[after %d ms] read (%x) seq is (%x) from FW MB\n",
cnt*delay, rc, seq);
@@ -2525,32 +2543,23 @@ u32 bnx2x_fw_command(struct bnx2x *bp, u32 command)
bnx2x_fw_dump(bp);
rc = 0;
}
+ mutex_unlock(&bp->fw_mb_mutex);
return rc;
}
static void bnx2x_set_storm_rx_mode(struct bnx2x *bp);
-static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp, int set);
+static void bnx2x_set_eth_mac_addr_e1h(struct bnx2x *bp, int set);
static void bnx2x_set_rx_mode(struct net_device *dev);
static void bnx2x_e1h_disable(struct bnx2x *bp)
{
int port = BP_PORT(bp);
- int i;
-
- bp->rx_mode = BNX2X_RX_MODE_NONE;
- bnx2x_set_storm_rx_mode(bp);
netif_tx_disable(bp->dev);
- bp->dev->trans_start = jiffies; /* prevent tx timeout */
REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0);
- bnx2x_set_mac_addr_e1h(bp, 0);
-
- for (i = 0; i < MC_HASH_SIZE; i++)
- REG_WR(bp, MC_HASH_OFFSET(bp, i), 0);
-
netif_carrier_off(bp->dev);
}
@@ -2560,13 +2569,13 @@ static void bnx2x_e1h_enable(struct bnx2x *bp)
REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 1);
- bnx2x_set_mac_addr_e1h(bp, 1);
-
/* Tx queue should be only reenabled */
netif_tx_wake_all_queues(bp->dev);
- /* Initialize the receive filter. */
- bnx2x_set_rx_mode(bp->dev);
+ /*
+ * Should not call netif_carrier_on since it will be called if the link
+ * is up when checking for link state
+ */
}
static void bnx2x_update_min_max(struct bnx2x *bp)
@@ -2605,21 +2614,23 @@ static void bnx2x_update_min_max(struct bnx2x *bp)
static void bnx2x_dcc_event(struct bnx2x *bp, u32 dcc_event)
{
- int func = BP_FUNC(bp);
-
DP(BNX2X_MSG_MCP, "dcc_event 0x%x\n", dcc_event);
- bp->mf_config = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config);
if (dcc_event & DRV_STATUS_DCC_DISABLE_ENABLE_PF) {
+ /*
+ * This is the only place besides the function initialization
+ * where the bp->flags can change so it is done without any
+ * locks
+ */
if (bp->mf_config & FUNC_MF_CFG_FUNC_DISABLED) {
DP(NETIF_MSG_IFDOWN, "mf_cfg function disabled\n");
- bp->state = BNX2X_STATE_DISABLED;
+ bp->flags |= MF_FUNC_DIS;
bnx2x_e1h_disable(bp);
} else {
DP(NETIF_MSG_IFUP, "mf_cfg function enabled\n");
- bp->state = BNX2X_STATE_OPEN;
+ bp->flags &= ~MF_FUNC_DIS;
bnx2x_e1h_enable(bp);
}
@@ -2638,11 +2649,40 @@ static void bnx2x_dcc_event(struct bnx2x *bp, u32 dcc_event)
bnx2x_fw_command(bp, DRV_MSG_CODE_DCC_OK);
}
+/* must be called under the spq lock */
+static inline struct eth_spe *bnx2x_sp_get_next(struct bnx2x *bp)
+{
+ struct eth_spe *next_spe = bp->spq_prod_bd;
+
+ if (bp->spq_prod_bd == bp->spq_last_bd) {
+ bp->spq_prod_bd = bp->spq;
+ bp->spq_prod_idx = 0;
+ DP(NETIF_MSG_TIMER, "end of spq\n");
+ } else {
+ bp->spq_prod_bd++;
+ bp->spq_prod_idx++;
+ }
+ return next_spe;
+}
+
+/* must be called under the spq lock */
+static inline void bnx2x_sp_prod_update(struct bnx2x *bp)
+{
+ int func = BP_FUNC(bp);
+
+ /* Make sure that BD data is updated before writing the producer */
+ wmb();
+
+ REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func),
+ bp->spq_prod_idx);
+ mmiowb();
+}
+
/* the slow path queue is odd since completions arrive on the fastpath ring */
static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
u32 data_hi, u32 data_lo, int common)
{
- int func = BP_FUNC(bp);
+ struct eth_spe *spe;
DP(BNX2X_MSG_SP/*NETIF_MSG_TIMER*/,
"SPQE (%x:%x) command %d hw_cid %x data (%x:%x) left %x\n",
@@ -2664,38 +2704,23 @@ static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
return -EBUSY;
}
+ spe = bnx2x_sp_get_next(bp);
+
/* CID needs port number to be encoded int it */
- bp->spq_prod_bd->hdr.conn_and_cmd_data =
+ spe->hdr.conn_and_cmd_data =
cpu_to_le32(((command << SPE_HDR_CMD_ID_SHIFT) |
HW_CID(bp, cid)));
- bp->spq_prod_bd->hdr.type = cpu_to_le16(ETH_CONNECTION_TYPE);
+ spe->hdr.type = cpu_to_le16(ETH_CONNECTION_TYPE);
if (common)
- bp->spq_prod_bd->hdr.type |=
+ spe->hdr.type |=
cpu_to_le16((1 << SPE_HDR_COMMON_RAMROD_SHIFT));
- bp->spq_prod_bd->data.mac_config_addr.hi = cpu_to_le32(data_hi);
- bp->spq_prod_bd->data.mac_config_addr.lo = cpu_to_le32(data_lo);
+ spe->data.mac_config_addr.hi = cpu_to_le32(data_hi);
+ spe->data.mac_config_addr.lo = cpu_to_le32(data_lo);
bp->spq_left--;
- if (bp->spq_prod_bd == bp->spq_last_bd) {
- bp->spq_prod_bd = bp->spq;
- bp->spq_prod_idx = 0;
- DP(NETIF_MSG_TIMER, "end of spq\n");
-
- } else {
- bp->spq_prod_bd++;
- bp->spq_prod_idx++;
- }
-
- /* Make sure that BD data is updated before writing the producer */
- wmb();
-
- REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func),
- bp->spq_prod_idx);
-
- mmiowb();
-
+ bnx2x_sp_prod_update(bp);
spin_unlock_bh(&bp->spq_lock);
return 0;
}
@@ -3024,6 +3049,8 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
int func = BP_FUNC(bp);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0);
+ bp->mf_config = SHMEM_RD(bp,
+ mf_cfg.func_mf_config[func].config);
val = SHMEM_RD(bp, func_mb[func].drv_status);
if (val & DRV_STATUS_DCC_EVENT_MASK)
bnx2x_dcc_event(bp,
@@ -3227,6 +3254,17 @@ static irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance)
return IRQ_HANDLED;
#endif
+#ifdef BCM_CNIC
+ {
+ struct cnic_ops *c_ops;
+
+ rcu_read_lock();
+ c_ops = rcu_dereference(bp->cnic_ops);
+ if (c_ops)
+ c_ops->cnic_handler(bp->cnic_data, NULL);
+ rcu_read_unlock();
+ }
+#endif
queue_delayed_work(bnx2x_wq, &bp->sp_task, 0);
return IRQ_HANDLED;
@@ -3958,7 +3996,7 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp)
estats->no_buff_discard_hi = 0;
estats->no_buff_discard_lo = 0;
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
int cl_id = fp->cl_id;
struct tstorm_per_client_stats *tclient =
@@ -4175,7 +4213,7 @@ static void bnx2x_net_stats_update(struct bnx2x *bp)
nstats->tx_bytes = bnx2x_hilo(&estats->total_bytes_transmitted_hi);
nstats->rx_dropped = estats->mac_discard;
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
nstats->rx_dropped +=
le32_to_cpu(bp->fp[i].old_tclient.checksum_discard);
@@ -4229,7 +4267,7 @@ static void bnx2x_drv_stats_update(struct bnx2x *bp)
estats->rx_err_discard_pkt = 0;
estats->rx_skb_alloc_failed = 0;
estats->hw_csum_err = 0;
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_eth_q_stats *qstats = &bp->fp[i].eth_q_stats;
estats->driver_xoff += qstats->driver_xoff;
@@ -4260,7 +4298,7 @@ static void bnx2x_stats_update(struct bnx2x *bp)
if (bp->msglevel & NETIF_MSG_TIMER) {
struct bnx2x_fastpath *fp0_rx = bp->fp;
- struct bnx2x_fastpath *fp0_tx = &(bp->fp[bp->num_rx_queues]);
+ struct bnx2x_fastpath *fp0_tx = bp->fp;
struct tstorm_per_client_stats *old_tclient =
&bp->fp->old_tclient;
struct bnx2x_eth_q_stats *qstats = &bp->fp->eth_q_stats;
@@ -4640,8 +4678,7 @@ static void bnx2x_timer(unsigned long data)
}
}
- if ((bp->state == BNX2X_STATE_OPEN) ||
- (bp->state == BNX2X_STATE_DISABLED))
+ if (bp->state == BNX2X_STATE_OPEN)
bnx2x_stats_handle(bp, STATS_EVENT_UPDATE);
timer_restart:
@@ -4860,21 +4897,21 @@ static void bnx2x_update_coalesce(struct bnx2x *bp)
REG_WR8(bp, BAR_CSTRORM_INTMEM +
CSTORM_SB_HC_TIMEOUT_U_OFFSET(port, sb_id,
U_SB_ETH_RX_CQ_INDEX),
- bp->rx_ticks/12);
+ bp->rx_ticks/(4 * BNX2X_BTR));
REG_WR16(bp, BAR_CSTRORM_INTMEM +
CSTORM_SB_HC_DISABLE_U_OFFSET(port, sb_id,
U_SB_ETH_RX_CQ_INDEX),
- (bp->rx_ticks/12) ? 0 : 1);
+ (bp->rx_ticks/(4 * BNX2X_BTR)) ? 0 : 1);
/* HC_INDEX_C_ETH_TX_CQ_CONS */
REG_WR8(bp, BAR_CSTRORM_INTMEM +
CSTORM_SB_HC_TIMEOUT_C_OFFSET(port, sb_id,
C_SB_ETH_TX_CQ_INDEX),
- bp->tx_ticks/12);
+ bp->tx_ticks/(4 * BNX2X_BTR));
REG_WR16(bp, BAR_CSTRORM_INTMEM +
CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id,
C_SB_ETH_TX_CQ_INDEX),
- (bp->tx_ticks/12) ? 0 : 1);
+ (bp->tx_ticks/(4 * BNX2X_BTR)) ? 0 : 1);
}
}
@@ -4916,7 +4953,7 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp)
if (bp->flags & TPA_ENABLE_FLAG) {
- for_each_rx_queue(bp, j) {
+ for_each_queue(bp, j) {
struct bnx2x_fastpath *fp = &bp->fp[j];
for (i = 0; i < max_agg_queues; i++) {
@@ -4939,16 +4976,13 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp)
}
}
- for_each_rx_queue(bp, j) {
+ for_each_queue(bp, j) {
struct bnx2x_fastpath *fp = &bp->fp[j];
fp->rx_bd_cons = 0;
fp->rx_cons_sb = BNX2X_RX_SB_INDEX;
fp->rx_bd_cons_sb = BNX2X_RX_SB_BD_INDEX;
- /* Mark queue as Rx */
- fp->is_rx_queue = 1;
-
/* "next page" elements initialization */
/* SGE ring */
for (i = 1; i <= NUM_RX_SGE_PAGES; i++) {
@@ -5054,7 +5088,7 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp)
{
int i, j;
- for_each_tx_queue(bp, j) {
+ for_each_queue(bp, j) {
struct bnx2x_fastpath *fp = &bp->fp[j];
for (i = 1; i <= NUM_TX_RINGS; i++) {
@@ -5080,10 +5114,6 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp)
fp->tx_cons_sb = BNX2X_TX_SB_INDEX;
fp->tx_pkt = 0;
}
-
- /* clean tx statistics */
- for_each_rx_queue(bp, i)
- bnx2x_fp(bp, i, tx_pkt) = 0;
}
static void bnx2x_init_sp_ring(struct bnx2x *bp)
@@ -5112,7 +5142,8 @@ static void bnx2x_init_context(struct bnx2x *bp)
{
int i;
- for_each_rx_queue(bp, i) {
+ /* Rx */
+ for_each_queue(bp, i) {
struct eth_context *context = bnx2x_sp(bp, context[i].eth);
struct bnx2x_fastpath *fp = &bp->fp[i];
u8 cl_id = fp->cl_id;
@@ -5164,10 +5195,11 @@ static void bnx2x_init_context(struct bnx2x *bp)
ETH_CONNECTION_TYPE);
}
- for_each_tx_queue(bp, i) {
+ /* Tx */
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
struct eth_context *context =
- bnx2x_sp(bp, context[i - bp->num_rx_queues].eth);
+ bnx2x_sp(bp, context[i].eth);
context->cstorm_st_context.sb_index_number =
C_SB_ETH_TX_CQ_INDEX;
@@ -5195,7 +5227,7 @@ static void bnx2x_init_ind_table(struct bnx2x *bp)
for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++)
REG_WR8(bp, BAR_TSTRORM_INTMEM +
TSTORM_INDIRECTION_TABLE_OFFSET(func) + i,
- bp->fp->cl_id + (i % bp->num_rx_queues));
+ bp->fp->cl_id + (i % bp->num_queues));
}
static void bnx2x_set_client_config(struct bnx2x *bp)
@@ -5235,7 +5267,7 @@ static void bnx2x_set_storm_rx_mode(struct bnx2x *bp)
{
struct tstorm_eth_mac_filter_config tstorm_mac_filter = {0};
int mode = bp->rx_mode;
- int mask = (1 << BP_L_ID(bp));
+ int mask = bp->rx_mode_cl_mask;
int func = BP_FUNC(bp);
int port = BP_PORT(bp);
int i;
@@ -5348,6 +5380,7 @@ static void bnx2x_init_internal_func(struct bnx2x *bp)
(*(u32 *)&tstorm_config));
bp->rx_mode = BNX2X_RX_MODE_NONE; /* no rx until link is up */
+ bp->rx_mode_cl_mask = (1 << BP_L_ID(bp));
bnx2x_set_storm_rx_mode(bp);
for_each_queue(bp, i) {
@@ -5438,7 +5471,7 @@ static void bnx2x_init_internal_func(struct bnx2x *bp)
min((u32)(min((u32)8, (u32)MAX_SKB_FRAGS) *
SGE_PAGE_SIZE * PAGES_PER_SGE),
(u32)0xffff);
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
REG_WR(bp, BAR_USTRORM_INTMEM +
@@ -5473,7 +5506,7 @@ static void bnx2x_init_internal_func(struct bnx2x *bp)
rx_pause.cqe_thr_high = 350;
rx_pause.sge_thr_high = 0;
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
if (!fp->disable_tpa) {
@@ -5504,20 +5537,18 @@ static void bnx2x_init_internal_func(struct bnx2x *bp)
bp->link_vars.line_speed = SPEED_10000;
bnx2x_init_port_minmax(bp);
+ if (!BP_NOMCP(bp))
+ bp->mf_config =
+ SHMEM_RD(bp, mf_cfg.func_mf_config[func].config);
bnx2x_calc_vn_weight_sum(bp);
for (vn = VN_0; vn < E1HVN_MAX; vn++)
bnx2x_init_vn_minmax(bp, 2*vn + port);
/* Enable rate shaping and fairness */
- bp->cmng.flags.cmng_enables =
+ bp->cmng.flags.cmng_enables |=
CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN;
- if (bp->vn_weight_sum)
- bp->cmng.flags.cmng_enables |=
- CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
- else
- DP(NETIF_MSG_IFUP, "All MIN values are zeroes"
- " fairness will be disabled\n");
+
} else {
/* rate shaping and fairness are disabled */
DP(NETIF_MSG_IFUP,
@@ -5565,10 +5596,11 @@ static void bnx2x_nic_init(struct bnx2x *bp, u32 load_code)
fp->state = BNX2X_FP_STATE_CLOSED;
fp->index = i;
fp->cl_id = BP_L_ID(bp) + i;
+#ifdef BCM_CNIC
+ fp->sb_id = fp->cl_id + 1;
+#else
fp->sb_id = fp->cl_id;
- /* Suitable Rx and Tx SBs are served by the same client */
- if (i >= bp->num_rx_queues)
- fp->cl_id -= bp->num_rx_queues;
+#endif
DP(NETIF_MSG_IFUP,
"queue[%d]: bnx2x_init_sb(%p,%p) cl_id %d sb %d\n",
i, bp, fp->status_blk, fp->cl_id, fp->sb_id);
@@ -5867,7 +5899,7 @@ static int bnx2x_int_mem_test(struct bnx2x *bp)
msleep(50);
bnx2x_init_block(bp, BRB1_BLOCK, COMMON_STAGE);
bnx2x_init_block(bp, PRS_BLOCK, COMMON_STAGE);
-#ifndef BCM_ISCSI
+#ifndef BCM_CNIC
/* set NIC mode */
REG_WR(bp, PRS_REG_NIC_MODE, 1);
#endif
@@ -6006,6 +6038,9 @@ static void bnx2x_setup_fan_failure_detection(struct bnx2x *bp)
static int bnx2x_init_common(struct bnx2x *bp)
{
u32 val, i;
+#ifdef BCM_CNIC
+ u32 wb_write[2];
+#endif
DP(BNX2X_MSG_MCP, "starting common init func %d\n", BP_FUNC(bp));
@@ -6048,7 +6083,7 @@ static int bnx2x_init_common(struct bnx2x *bp)
#endif
REG_WR(bp, PXP2_REG_RQ_CDU_P_SIZE, 2);
-#ifdef BCM_ISCSI
+#ifdef BCM_CNIC
REG_WR(bp, PXP2_REG_RQ_TM_P_SIZE, 5);
REG_WR(bp, PXP2_REG_RQ_QM_P_SIZE, 5);
REG_WR(bp, PXP2_REG_RQ_SRC_P_SIZE, 5);
@@ -6091,11 +6126,26 @@ static int bnx2x_init_common(struct bnx2x *bp)
bnx2x_read_dmae(bp, USEM_REG_PASSIVE_BUFFER, 3);
bnx2x_init_block(bp, QM_BLOCK, COMMON_STAGE);
+
+#ifdef BCM_CNIC
+ wb_write[0] = 0;
+ wb_write[1] = 0;
+ for (i = 0; i < 64; i++) {
+ REG_WR(bp, QM_REG_BASEADDR + i*4, 1024 * 4 * (i%16));
+ bnx2x_init_ind_wr(bp, QM_REG_PTRTBL + i*8, wb_write, 2);
+
+ if (CHIP_IS_E1H(bp)) {
+ REG_WR(bp, QM_REG_BASEADDR_EXT_A + i*4, 1024*4*(i%16));
+ bnx2x_init_ind_wr(bp, QM_REG_PTRTBL_EXT_A + i*8,
+ wb_write, 2);
+ }
+ }
+#endif
/* soft reset pulse */
REG_WR(bp, QM_REG_SOFT_RESET, 1);
REG_WR(bp, QM_REG_SOFT_RESET, 0);
-#ifdef BCM_ISCSI
+#ifdef BCM_CNIC
bnx2x_init_block(bp, TIMERS_BLOCK, COMMON_STAGE);
#endif
@@ -6109,8 +6159,10 @@ static int bnx2x_init_common(struct bnx2x *bp)
bnx2x_init_block(bp, BRB1_BLOCK, COMMON_STAGE);
bnx2x_init_block(bp, PRS_BLOCK, COMMON_STAGE);
REG_WR(bp, PRS_REG_A_PRSU_20, 0xf);
+#ifndef BCM_CNIC
/* set NIC mode */
REG_WR(bp, PRS_REG_NIC_MODE, 1);
+#endif
if (CHIP_IS_E1H(bp))
REG_WR(bp, PRS_REG_E1HOV_MODE, IS_E1HMF(bp));
@@ -6145,6 +6197,18 @@ static int bnx2x_init_common(struct bnx2x *bp)
/* TODO: replace with something meaningful */
}
bnx2x_init_block(bp, SRCH_BLOCK, COMMON_STAGE);
+#ifdef BCM_CNIC
+ REG_WR(bp, SRC_REG_KEYSEARCH_0, 0x63285672);
+ REG_WR(bp, SRC_REG_KEYSEARCH_1, 0x24b8f2cc);
+ REG_WR(bp, SRC_REG_KEYSEARCH_2, 0x223aef9b);
+ REG_WR(bp, SRC_REG_KEYSEARCH_3, 0x26001e3a);
+ REG_WR(bp, SRC_REG_KEYSEARCH_4, 0x7ae91116);
+ REG_WR(bp, SRC_REG_KEYSEARCH_5, 0x5ce5230b);
+ REG_WR(bp, SRC_REG_KEYSEARCH_6, 0x298d8adf);
+ REG_WR(bp, SRC_REG_KEYSEARCH_7, 0x6eb0ff09);
+ REG_WR(bp, SRC_REG_KEYSEARCH_8, 0x1830f82f);
+ REG_WR(bp, SRC_REG_KEYSEARCH_9, 0x01e46be7);
+#endif
REG_WR(bp, SRC_REG_SOFT_RST, 0);
if (sizeof(union cdu_context) != 1024)
@@ -6261,38 +6325,14 @@ static int bnx2x_init_port(struct bnx2x *bp)
bnx2x_init_block(bp, TCM_BLOCK, init_stage);
bnx2x_init_block(bp, UCM_BLOCK, init_stage);
bnx2x_init_block(bp, CCM_BLOCK, init_stage);
-#ifdef BCM_ISCSI
- /* Port0 1
- * Port1 385 */
- i++;
- wb_write[0] = ONCHIP_ADDR1(bp->timers_mapping);
- wb_write[1] = ONCHIP_ADDR2(bp->timers_mapping);
- REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2);
- REG_WR(bp, PXP2_REG_PSWRQ_TM0_L2P + func*4, PXP_ONE_ILT(i));
-
- /* Port0 2
- * Port1 386 */
- i++;
- wb_write[0] = ONCHIP_ADDR1(bp->qm_mapping);
- wb_write[1] = ONCHIP_ADDR2(bp->qm_mapping);
- REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2);
- REG_WR(bp, PXP2_REG_PSWRQ_QM0_L2P + func*4, PXP_ONE_ILT(i));
-
- /* Port0 3
- * Port1 387 */
- i++;
- wb_write[0] = ONCHIP_ADDR1(bp->t1_mapping);
- wb_write[1] = ONCHIP_ADDR2(bp->t1_mapping);
- REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2);
- REG_WR(bp, PXP2_REG_PSWRQ_SRC0_L2P + func*4, PXP_ONE_ILT(i));
-#endif
bnx2x_init_block(bp, XCM_BLOCK, init_stage);
-#ifdef BCM_ISCSI
- REG_WR(bp, TM_REG_LIN0_SCAN_TIME + func*4, 1024/64*20);
- REG_WR(bp, TM_REG_LIN0_MAX_ACTIVE_CID + func*4, 31);
+#ifdef BCM_CNIC
+ REG_WR(bp, QM_REG_CONNNUM_0 + port*4, 1024/16 - 1);
bnx2x_init_block(bp, TIMERS_BLOCK, init_stage);
+ REG_WR(bp, TM_REG_LIN0_SCAN_TIME + port*4, 20);
+ REG_WR(bp, TM_REG_LIN0_MAX_ACTIVE_CID + port*4, 31);
#endif
bnx2x_init_block(bp, DQ_BLOCK, init_stage);
@@ -6350,18 +6390,8 @@ static int bnx2x_init_port(struct bnx2x *bp)
msleep(5);
REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0);
-#ifdef BCM_ISCSI
- /* tell the searcher where the T2 table is */
- REG_WR(bp, SRC_REG_COUNTFREE0 + func*4, 16*1024/64);
-
- wb_write[0] = U64_LO(bp->t2_mapping);
- wb_write[1] = U64_HI(bp->t2_mapping);
- REG_WR_DMAE(bp, SRC_REG_FIRSTFREE0 + func*4, wb_write, 2);
- wb_write[0] = U64_LO((u64)bp->t2_mapping + 16*1024 - 64);
- wb_write[1] = U64_HI((u64)bp->t2_mapping + 16*1024 - 64);
- REG_WR_DMAE(bp, SRC_REG_LASTFREE0 + func*4, wb_write, 2);
-
- REG_WR(bp, SRC_REG_NUMBER_HASH_BITS0 + func*4, 10);
+#ifdef BCM_CNIC
+ bnx2x_init_block(bp, SRCH_BLOCK, init_stage);
#endif
bnx2x_init_block(bp, CDU_BLOCK, init_stage);
bnx2x_init_block(bp, CFC_BLOCK, init_stage);
@@ -6470,7 +6500,12 @@ static int bnx2x_init_port(struct bnx2x *bp)
#define PXP_ONE_ILT(x) (((x) << 10) | x)
#define PXP_ILT_RANGE(f, l) (((l) << 10) | f)
+#ifdef BCM_CNIC
+#define CNIC_ILT_LINES 127
+#define CNIC_CTX_PER_ILT 16
+#else
#define CNIC_ILT_LINES 0
+#endif
static void bnx2x_ilt_wr(struct bnx2x *bp, u32 index, dma_addr_t addr)
{
@@ -6509,6 +6544,46 @@ static int bnx2x_init_func(struct bnx2x *bp)
REG_WR(bp, PXP2_REG_PSWRQ_CDU0_L2P + func*4,
PXP_ILT_RANGE(i, i + CNIC_ILT_LINES));
+#ifdef BCM_CNIC
+ i += 1 + CNIC_ILT_LINES;
+ bnx2x_ilt_wr(bp, i, bp->timers_mapping);
+ if (CHIP_IS_E1(bp))
+ REG_WR(bp, PXP2_REG_PSWRQ_TM0_L2P + func*4, PXP_ONE_ILT(i));
+ else {
+ REG_WR(bp, PXP2_REG_RQ_TM_FIRST_ILT, i);
+ REG_WR(bp, PXP2_REG_RQ_TM_LAST_ILT, i);
+ }
+
+ i++;
+ bnx2x_ilt_wr(bp, i, bp->qm_mapping);
+ if (CHIP_IS_E1(bp))
+ REG_WR(bp, PXP2_REG_PSWRQ_QM0_L2P + func*4, PXP_ONE_ILT(i));
+ else {
+ REG_WR(bp, PXP2_REG_RQ_QM_FIRST_ILT, i);
+ REG_WR(bp, PXP2_REG_RQ_QM_LAST_ILT, i);
+ }
+
+ i++;
+ bnx2x_ilt_wr(bp, i, bp->t1_mapping);
+ if (CHIP_IS_E1(bp))
+ REG_WR(bp, PXP2_REG_PSWRQ_SRC0_L2P + func*4, PXP_ONE_ILT(i));
+ else {
+ REG_WR(bp, PXP2_REG_RQ_SRC_FIRST_ILT, i);
+ REG_WR(bp, PXP2_REG_RQ_SRC_LAST_ILT, i);
+ }
+
+ /* tell the searcher where the T2 table is */
+ REG_WR(bp, SRC_REG_COUNTFREE0 + port*4, 16*1024/64);
+
+ bnx2x_wb_wr(bp, SRC_REG_FIRSTFREE0 + port*16,
+ U64_LO(bp->t2_mapping), U64_HI(bp->t2_mapping));
+
+ bnx2x_wb_wr(bp, SRC_REG_LASTFREE0 + port*16,
+ U64_LO((u64)bp->t2_mapping + 16*1024 - 64),
+ U64_HI((u64)bp->t2_mapping + 16*1024 - 64));
+
+ REG_WR(bp, SRC_REG_NUMBER_HASH_BITS0 + port*4, 10);
+#endif
if (CHIP_IS_E1H(bp)) {
bnx2x_init_block(bp, MISC_BLOCK, FUNC0_STAGE + func);
@@ -6593,6 +6668,9 @@ static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code)
bnx2x_zero_def_sb(bp);
for_each_queue(bp, i)
bnx2x_zero_sb(bp, BP_L_ID(bp) + i);
+#ifdef BCM_CNIC
+ bnx2x_zero_sb(bp, BP_L_ID(bp) + i);
+#endif
init_hw_err:
bnx2x_gunzip_end(bp);
@@ -6632,7 +6710,7 @@ static void bnx2x_free_mem(struct bnx2x *bp)
sizeof(struct host_status_block));
}
/* Rx */
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
/* fastpath rx rings: rx_buf rx_desc rx_comp */
BNX2X_FREE(bnx2x_fp(bp, i, rx_buf_ring));
@@ -6652,7 +6730,7 @@ static void bnx2x_free_mem(struct bnx2x *bp)
BCM_PAGE_SIZE * NUM_RX_SGE_PAGES);
}
/* Tx */
- for_each_tx_queue(bp, i) {
+ for_each_queue(bp, i) {
/* fastpath tx rings: tx_buf tx_desc */
BNX2X_FREE(bnx2x_fp(bp, i, tx_buf_ring));
@@ -6668,11 +6746,13 @@ static void bnx2x_free_mem(struct bnx2x *bp)
BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping,
sizeof(struct bnx2x_slowpath));
-#ifdef BCM_ISCSI
+#ifdef BCM_CNIC
BNX2X_PCI_FREE(bp->t1, bp->t1_mapping, 64*1024);
BNX2X_PCI_FREE(bp->t2, bp->t2_mapping, 16*1024);
BNX2X_PCI_FREE(bp->timers, bp->timers_mapping, 8*1024);
BNX2X_PCI_FREE(bp->qm, bp->qm_mapping, 128*1024);
+ BNX2X_PCI_FREE(bp->cnic_sb, bp->cnic_sb_mapping,
+ sizeof(struct host_status_block));
#endif
BNX2X_PCI_FREE(bp->spq, bp->spq_mapping, BCM_PAGE_SIZE);
@@ -6712,7 +6792,7 @@ static int bnx2x_alloc_mem(struct bnx2x *bp)
sizeof(struct host_status_block));
}
/* Rx */
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
/* fastpath rx rings: rx_buf rx_desc rx_comp */
BNX2X_ALLOC(bnx2x_fp(bp, i, rx_buf_ring),
@@ -6734,7 +6814,7 @@ static int bnx2x_alloc_mem(struct bnx2x *bp)
BCM_PAGE_SIZE * NUM_RX_SGE_PAGES);
}
/* Tx */
- for_each_tx_queue(bp, i) {
+ for_each_queue(bp, i) {
/* fastpath tx rings: tx_buf tx_desc */
BNX2X_ALLOC(bnx2x_fp(bp, i, tx_buf_ring),
@@ -6751,32 +6831,26 @@ static int bnx2x_alloc_mem(struct bnx2x *bp)
BNX2X_PCI_ALLOC(bp->slowpath, &bp->slowpath_mapping,
sizeof(struct bnx2x_slowpath));
-#ifdef BCM_ISCSI
+#ifdef BCM_CNIC
BNX2X_PCI_ALLOC(bp->t1, &bp->t1_mapping, 64*1024);
- /* Initialize T1 */
- for (i = 0; i < 64*1024; i += 64) {
- *(u64 *)((char *)bp->t1 + i + 56) = 0x0UL;
- *(u64 *)((char *)bp->t1 + i + 3) = 0x0UL;
- }
-
/* allocate searcher T2 table
we allocate 1/4 of alloc num for T2
(which is not entered into the ILT) */
BNX2X_PCI_ALLOC(bp->t2, &bp->t2_mapping, 16*1024);
- /* Initialize T2 */
+ /* Initialize T2 (for 1024 connections) */
for (i = 0; i < 16*1024; i += 64)
- * (u64 *)((char *)bp->t2 + i + 56) = bp->t2_mapping + i + 64;
-
- /* now fixup the last line in the block to point to the next block */
- *(u64 *)((char *)bp->t2 + 1024*16-8) = bp->t2_mapping;
+ *(u64 *)((char *)bp->t2 + i + 56) = bp->t2_mapping + i + 64;
- /* Timer block array (MAX_CONN*8) phys uncached for now 1024 conns */
+ /* Timer block array (8*MAX_CONN) phys uncached for now 1024 conns */
BNX2X_PCI_ALLOC(bp->timers, &bp->timers_mapping, 8*1024);
/* QM queues (128*MAX_CONN) */
BNX2X_PCI_ALLOC(bp->qm, &bp->qm_mapping, 128*1024);
+
+ BNX2X_PCI_ALLOC(bp->cnic_sb, &bp->cnic_sb_mapping,
+ sizeof(struct host_status_block));
#endif
/* Slow path ring */
@@ -6796,7 +6870,7 @@ static void bnx2x_free_tx_skbs(struct bnx2x *bp)
{
int i;
- for_each_tx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
u16 bd_cons = fp->tx_bd_cons;
@@ -6814,7 +6888,7 @@ static void bnx2x_free_rx_skbs(struct bnx2x *bp)
{
int i, j;
- for_each_rx_queue(bp, j) {
+ for_each_queue(bp, j) {
struct bnx2x_fastpath *fp = &bp->fp[j];
for (i = 0; i < NUM_RX_BD; i++) {
@@ -6852,6 +6926,9 @@ static void bnx2x_free_msix_irqs(struct bnx2x *bp)
DP(NETIF_MSG_IFDOWN, "released sp irq (%d)\n",
bp->msix_table[0].vector);
+#ifdef BCM_CNIC
+ offset++;
+#endif
for_each_queue(bp, i) {
DP(NETIF_MSG_IFDOWN, "about to release fp #%d->%d irq "
"state %x\n", i, bp->msix_table[i + offset].vector,
@@ -6885,6 +6962,12 @@ static int bnx2x_enable_msix(struct bnx2x *bp)
bp->msix_table[0].entry = igu_vec;
DP(NETIF_MSG_IFUP, "msix_table[0].entry = %d (slowpath)\n", igu_vec);
+#ifdef BCM_CNIC
+ igu_vec = BP_L_ID(bp) + offset;
+ bp->msix_table[1].entry = igu_vec;
+ DP(NETIF_MSG_IFUP, "msix_table[1].entry = %d (CNIC)\n", igu_vec);
+ offset++;
+#endif
for_each_queue(bp, i) {
igu_vec = BP_L_ID(bp) + offset + i;
bp->msix_table[i + offset].entry = igu_vec;
@@ -6915,14 +6998,13 @@ static int bnx2x_req_msix_irqs(struct bnx2x *bp)
return -EBUSY;
}
+#ifdef BCM_CNIC
+ offset++;
+#endif
for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
-
- if (i < bp->num_rx_queues)
- sprintf(fp->name, "%s-rx-%d", bp->dev->name, i);
- else
- sprintf(fp->name, "%s-tx-%d",
- bp->dev->name, i - bp->num_rx_queues);
+ snprintf(fp->name, sizeof(fp->name), "%s-fp-%d",
+ bp->dev->name, i);
rc = request_irq(bp->msix_table[i + offset].vector,
bnx2x_msix_fp_int, 0, fp->name, fp);
@@ -6981,7 +7063,7 @@ static void bnx2x_napi_enable(struct bnx2x *bp)
{
int i;
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
napi_enable(&bnx2x_fp(bp, i, napi));
}
@@ -6989,7 +7071,7 @@ static void bnx2x_napi_disable(struct bnx2x *bp)
{
int i;
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
napi_disable(&bnx2x_fp(bp, i, napi));
}
@@ -7015,14 +7097,25 @@ static void bnx2x_netif_stop(struct bnx2x *bp, int disable_hw)
bnx2x_int_disable_sync(bp, disable_hw);
bnx2x_napi_disable(bp);
netif_tx_disable(bp->dev);
- bp->dev->trans_start = jiffies; /* prevent tx timeout */
}
/*
* Init service functions
*/
-static void bnx2x_set_mac_addr_e1(struct bnx2x *bp, int set)
+/**
+ * Sets a MAC in a CAM for a few L2 Clients for E1 chip
+ *
+ * @param bp driver descriptor
+ * @param set set or clear an entry (1 or 0)
+ * @param mac pointer to a buffer containing a MAC
+ * @param cl_bit_vec bit vector of clients to register a MAC for
+ * @param cam_offset offset in a CAM to use
+ * @param with_bcast set broadcast MAC as well
+ */
+static void bnx2x_set_mac_addr_e1_gen(struct bnx2x *bp, int set, u8 *mac,
+ u32 cl_bit_vec, u8 cam_offset,
+ u8 with_bcast)
{
struct mac_configuration_cmd *config = bnx2x_sp(bp, mac_config);
int port = BP_PORT(bp);
@@ -7031,25 +7124,25 @@ static void bnx2x_set_mac_addr_e1(struct bnx2x *bp, int set)
* unicasts 0-31:port0 32-63:port1
* multicast 64-127:port0 128-191:port1
*/
- config->hdr.length = 2;
- config->hdr.offset = port ? 32 : 0;
- config->hdr.client_id = bp->fp->cl_id;
+ config->hdr.length = 1 + (with_bcast ? 1 : 0);
+ config->hdr.offset = cam_offset;
+ config->hdr.client_id = 0xff;
config->hdr.reserved1 = 0;
/* primary MAC */
config->config_table[0].cam_entry.msb_mac_addr =
- swab16(*(u16 *)&bp->dev->dev_addr[0]);
+ swab16(*(u16 *)&mac[0]);
config->config_table[0].cam_entry.middle_mac_addr =
- swab16(*(u16 *)&bp->dev->dev_addr[2]);
+ swab16(*(u16 *)&mac[2]);
config->config_table[0].cam_entry.lsb_mac_addr =
- swab16(*(u16 *)&bp->dev->dev_addr[4]);
+ swab16(*(u16 *)&mac[4]);
config->config_table[0].cam_entry.flags = cpu_to_le16(port);
if (set)
config->config_table[0].target_table_entry.flags = 0;
else
CAM_INVALIDATE(config->config_table[0]);
config->config_table[0].target_table_entry.clients_bit_vector =
- cpu_to_le32(1 << BP_L_ID(bp));
+ cpu_to_le32(cl_bit_vec);
config->config_table[0].target_table_entry.vlan_id = 0;
DP(NETIF_MSG_IFUP, "%s MAC (%04x:%04x:%04x)\n",
@@ -7059,47 +7152,58 @@ static void bnx2x_set_mac_addr_e1(struct bnx2x *bp, int set)
config->config_table[0].cam_entry.lsb_mac_addr);
/* broadcast */
- config->config_table[1].cam_entry.msb_mac_addr = cpu_to_le16(0xffff);
- config->config_table[1].cam_entry.middle_mac_addr = cpu_to_le16(0xffff);
- config->config_table[1].cam_entry.lsb_mac_addr = cpu_to_le16(0xffff);
- config->config_table[1].cam_entry.flags = cpu_to_le16(port);
- if (set)
- config->config_table[1].target_table_entry.flags =
- TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST;
- else
- CAM_INVALIDATE(config->config_table[1]);
- config->config_table[1].target_table_entry.clients_bit_vector =
- cpu_to_le32(1 << BP_L_ID(bp));
- config->config_table[1].target_table_entry.vlan_id = 0;
+ if (with_bcast) {
+ config->config_table[1].cam_entry.msb_mac_addr =
+ cpu_to_le16(0xffff);
+ config->config_table[1].cam_entry.middle_mac_addr =
+ cpu_to_le16(0xffff);
+ config->config_table[1].cam_entry.lsb_mac_addr =
+ cpu_to_le16(0xffff);
+ config->config_table[1].cam_entry.flags = cpu_to_le16(port);
+ if (set)
+ config->config_table[1].target_table_entry.flags =
+ TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST;
+ else
+ CAM_INVALIDATE(config->config_table[1]);
+ config->config_table[1].target_table_entry.clients_bit_vector =
+ cpu_to_le32(cl_bit_vec);
+ config->config_table[1].target_table_entry.vlan_id = 0;
+ }
bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0,
U64_HI(bnx2x_sp_mapping(bp, mac_config)),
U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0);
}
-static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp, int set)
+/**
+ * Sets a MAC in a CAM for a few L2 Clients for E1H chip
+ *
+ * @param bp driver descriptor
+ * @param set set or clear an entry (1 or 0)
+ * @param mac pointer to a buffer containing a MAC
+ * @param cl_bit_vec bit vector of clients to register a MAC for
+ * @param cam_offset offset in a CAM to use
+ */
+static void bnx2x_set_mac_addr_e1h_gen(struct bnx2x *bp, int set, u8 *mac,
+ u32 cl_bit_vec, u8 cam_offset)
{
struct mac_configuration_cmd_e1h *config =
(struct mac_configuration_cmd_e1h *)bnx2x_sp(bp, mac_config);
- /* CAM allocation for E1H
- * unicasts: by func number
- * multicast: 20+FUNC*20, 20 each
- */
config->hdr.length = 1;
- config->hdr.offset = BP_FUNC(bp);
- config->hdr.client_id = bp->fp->cl_id;
+ config->hdr.offset = cam_offset;
+ config->hdr.client_id = 0xff;
config->hdr.reserved1 = 0;
/* primary MAC */
config->config_table[0].msb_mac_addr =
- swab16(*(u16 *)&bp->dev->dev_addr[0]);
+ swab16(*(u16 *)&mac[0]);
config->config_table[0].middle_mac_addr =
- swab16(*(u16 *)&bp->dev->dev_addr[2]);
+ swab16(*(u16 *)&mac[2]);
config->config_table[0].lsb_mac_addr =
- swab16(*(u16 *)&bp->dev->dev_addr[4]);
+ swab16(*(u16 *)&mac[4]);
config->config_table[0].clients_bit_vector =
- cpu_to_le32(1 << BP_L_ID(bp));
+ cpu_to_le32(cl_bit_vec);
config->config_table[0].vlan_id = 0;
config->config_table[0].e1hov_id = cpu_to_le16(bp->e1hov);
if (set)
@@ -7108,11 +7212,11 @@ static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp, int set)
config->config_table[0].flags =
MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE;
- DP(NETIF_MSG_IFUP, "%s MAC (%04x:%04x:%04x) E1HOV %d CLID %d\n",
+ DP(NETIF_MSG_IFUP, "%s MAC (%04x:%04x:%04x) E1HOV %d CLID mask %d\n",
(set ? "setting" : "clearing"),
config->config_table[0].msb_mac_addr,
config->config_table[0].middle_mac_addr,
- config->config_table[0].lsb_mac_addr, bp->e1hov, BP_L_ID(bp));
+ config->config_table[0].lsb_mac_addr, bp->e1hov, cl_bit_vec);
bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0,
U64_HI(bnx2x_sp_mapping(bp, mac_config)),
@@ -7164,6 +7268,69 @@ static int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx,
return -EBUSY;
}
+static void bnx2x_set_eth_mac_addr_e1h(struct bnx2x *bp, int set)
+{
+ bp->set_mac_pending++;
+ smp_wmb();
+
+ bnx2x_set_mac_addr_e1h_gen(bp, set, bp->dev->dev_addr,
+ (1 << bp->fp->cl_id), BP_FUNC(bp));
+
+ /* Wait for a completion */
+ bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, set ? 0 : 1);
+}
+
+static void bnx2x_set_eth_mac_addr_e1(struct bnx2x *bp, int set)
+{
+ bp->set_mac_pending++;
+ smp_wmb();
+
+ bnx2x_set_mac_addr_e1_gen(bp, set, bp->dev->dev_addr,
+ (1 << bp->fp->cl_id), (BP_PORT(bp) ? 32 : 0),
+ 1);
+
+ /* Wait for a completion */
+ bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, set ? 0 : 1);
+}
+
+#ifdef BCM_CNIC
+/**
+ * Set iSCSI MAC(s) at the next enties in the CAM after the ETH
+ * MAC(s). This function will wait until the ramdord completion
+ * returns.
+ *
+ * @param bp driver handle
+ * @param set set or clear the CAM entry
+ *
+ * @return 0 if cussess, -ENODEV if ramrod doesn't return.
+ */
+static int bnx2x_set_iscsi_eth_mac_addr(struct bnx2x *bp, int set)
+{
+ u32 cl_bit_vec = (1 << BCM_ISCSI_ETH_CL_ID);
+
+ bp->set_mac_pending++;
+ smp_wmb();
+
+ /* Send a SET_MAC ramrod */
+ if (CHIP_IS_E1(bp))
+ bnx2x_set_mac_addr_e1_gen(bp, set, bp->iscsi_mac,
+ cl_bit_vec, (BP_PORT(bp) ? 32 : 0) + 2,
+ 1);
+ else
+ /* CAM allocation for E1H
+ * unicasts: by func number
+ * multicast: 20+FUNC*20, 20 each
+ */
+ bnx2x_set_mac_addr_e1h_gen(bp, set, bp->iscsi_mac,
+ cl_bit_vec, E1H_FUNC_MAX + BP_FUNC(bp));
+
+ /* Wait for a completion when setting */
+ bnx2x_wait_ramrod(bp, 0, 0, &bp->set_mac_pending, set ? 0 : 1);
+
+ return 0;
+}
+#endif
+
static int bnx2x_setup_leading(struct bnx2x *bp)
{
int rc;
@@ -7199,96 +7366,67 @@ static int bnx2x_setup_multi(struct bnx2x *bp, int index)
static int bnx2x_poll(struct napi_struct *napi, int budget);
-static void bnx2x_set_int_mode_msix(struct bnx2x *bp, int *num_rx_queues_out,
- int *num_tx_queues_out)
+static void bnx2x_set_num_queues_msix(struct bnx2x *bp)
{
- int _num_rx_queues = 0, _num_tx_queues = 0;
switch (bp->multi_mode) {
case ETH_RSS_MODE_DISABLED:
- _num_rx_queues = 1;
- _num_tx_queues = 1;
+ bp->num_queues = 1;
break;
case ETH_RSS_MODE_REGULAR:
- if (num_rx_queues)
- _num_rx_queues = min_t(u32, num_rx_queues,
- BNX2X_MAX_QUEUES(bp));
+ if (num_queues)
+ bp->num_queues = min_t(u32, num_queues,
+ BNX2X_MAX_QUEUES(bp));
else
- _num_rx_queues = min_t(u32, num_online_cpus(),
- BNX2X_MAX_QUEUES(bp));
-
- if (num_tx_queues)
- _num_tx_queues = min_t(u32, num_tx_queues,
- BNX2X_MAX_QUEUES(bp));
- else
- _num_tx_queues = min_t(u32, num_online_cpus(),
- BNX2X_MAX_QUEUES(bp));
-
- /* There must be not more Tx queues than Rx queues */
- if (_num_tx_queues > _num_rx_queues) {
- BNX2X_ERR("number of tx queues (%d) > "
- "number of rx queues (%d)"
- " defaulting to %d\n",
- _num_tx_queues, _num_rx_queues,
- _num_rx_queues);
- _num_tx_queues = _num_rx_queues;
- }
+ bp->num_queues = min_t(u32, num_online_cpus(),
+ BNX2X_MAX_QUEUES(bp));
break;
default:
- _num_rx_queues = 1;
- _num_tx_queues = 1;
+ bp->num_queues = 1;
break;
}
-
- *num_rx_queues_out = _num_rx_queues;
- *num_tx_queues_out = _num_tx_queues;
}
-static int bnx2x_set_int_mode(struct bnx2x *bp)
+static int bnx2x_set_num_queues(struct bnx2x *bp)
{
int rc = 0;
switch (int_mode) {
case INT_MODE_INTx:
case INT_MODE_MSI:
- bp->num_rx_queues = 1;
- bp->num_tx_queues = 1;
+ bp->num_queues = 1;
DP(NETIF_MSG_IFUP, "set number of queues to 1\n");
break;
case INT_MODE_MSIX:
default:
- /* Set interrupt mode according to bp->multi_mode value */
- bnx2x_set_int_mode_msix(bp, &bp->num_rx_queues,
- &bp->num_tx_queues);
+ /* Set number of queues according to bp->multi_mode value */
+ bnx2x_set_num_queues_msix(bp);
- DP(NETIF_MSG_IFUP, "set number of queues to: rx %d tx %d\n",
- bp->num_rx_queues, bp->num_tx_queues);
+ DP(NETIF_MSG_IFUP, "set number of queues to %d\n",
+ bp->num_queues);
/* if we can't use MSI-X we only need one fp,
* so try to enable MSI-X with the requested number of fp's
* and fallback to MSI or legacy INTx with one fp
*/
rc = bnx2x_enable_msix(bp);
- if (rc) {
+ if (rc)
/* failed to enable MSI-X */
- if (bp->multi_mode)
- BNX2X_ERR("Multi requested but failed to "
- "enable MSI-X (rx %d tx %d), "
- "set number of queues to 1\n",
- bp->num_rx_queues, bp->num_tx_queues);
- bp->num_rx_queues = 1;
- bp->num_tx_queues = 1;
- }
+ bp->num_queues = 1;
break;
}
- bp->dev->real_num_tx_queues = bp->num_tx_queues;
+ bp->dev->real_num_tx_queues = bp->num_queues;
return rc;
}
+#ifdef BCM_CNIC
+static int bnx2x_cnic_notify(struct bnx2x *bp, int cmd);
+static void bnx2x_setup_cnic_irq_info(struct bnx2x *bp);
+#endif
/* must be called with rtnl_lock */
static int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
@@ -7303,16 +7441,16 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD;
- rc = bnx2x_set_int_mode(bp);
+ rc = bnx2x_set_num_queues(bp);
if (bnx2x_alloc_mem(bp))
return -ENOMEM;
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
bnx2x_fp(bp, i, disable_tpa) =
((bp->flags & TPA_ENABLE_FLAG) == 0);
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
bnx2x_poll, 128);
@@ -7326,7 +7464,7 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
}
} else {
/* Fall to INTx if failed to enable MSI-X due to lack of
- memory (in bnx2x_set_int_mode()) */
+ memory (in bnx2x_set_num_queues()) */
if ((rc != -ENOMEM) && (int_mode != INT_MODE_INTx))
bnx2x_enable_msi(bp);
bnx2x_ack_int(bp);
@@ -7427,20 +7565,37 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
if (CHIP_IS_E1H(bp))
if (bp->mf_config & FUNC_MF_CFG_FUNC_DISABLED) {
DP(NETIF_MSG_IFUP, "mf_cfg function disabled\n");
- bp->state = BNX2X_STATE_DISABLED;
+ bp->flags |= MF_FUNC_DIS;
}
if (bp->state == BNX2X_STATE_OPEN) {
+#ifdef BCM_CNIC
+ /* Enable Timer scan */
+ REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + BP_PORT(bp)*4, 1);
+#endif
for_each_nondefault_queue(bp, i) {
rc = bnx2x_setup_multi(bp, i);
if (rc)
+#ifdef BCM_CNIC
+ goto load_error4;
+#else
goto load_error3;
+#endif
}
if (CHIP_IS_E1(bp))
- bnx2x_set_mac_addr_e1(bp, 1);
+ bnx2x_set_eth_mac_addr_e1(bp, 1);
else
- bnx2x_set_mac_addr_e1h(bp, 1);
+ bnx2x_set_eth_mac_addr_e1h(bp, 1);
+#ifdef BCM_CNIC
+ /* Set iSCSI L2 MAC */
+ mutex_lock(&bp->cnic_mutex);
+ if (bp->cnic_eth_dev.drv_state & CNIC_DRV_STATE_REGD) {
+ bnx2x_set_iscsi_eth_mac_addr(bp, 1);
+ bp->cnic_flags |= BNX2X_CNIC_FLAG_MAC_SET;
+ }
+ mutex_unlock(&bp->cnic_mutex);
+#endif
}
if (bp->port.pmf)
@@ -7481,9 +7636,19 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
/* start the timer */
mod_timer(&bp->timer, jiffies + bp->current_interval);
+#ifdef BCM_CNIC
+ bnx2x_setup_cnic_irq_info(bp);
+ if (bp->state == BNX2X_STATE_OPEN)
+ bnx2x_cnic_notify(bp, CNIC_CTL_START_CMD);
+#endif
return 0;
+#ifdef BCM_CNIC
+load_error4:
+ /* Disable Timer scan */
+ REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + BP_PORT(bp)*4, 0);
+#endif
load_error3:
bnx2x_int_disable_sync(bp, 1);
if (!BP_NOMCP(bp)) {
@@ -7493,14 +7658,14 @@ load_error3:
bp->port.pmf = 0;
/* Free SKBs, SGEs, TPA pool and driver internals */
bnx2x_free_skbs(bp);
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE);
load_error2:
/* Release IRQs */
bnx2x_free_irq(bp);
load_error1:
bnx2x_napi_disable(bp);
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
netif_napi_del(&bnx2x_fp(bp, i, napi));
bnx2x_free_mem(bp);
@@ -7591,6 +7756,19 @@ static void bnx2x_reset_func(struct bnx2x *bp)
REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0);
REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0);
+#ifdef BCM_CNIC
+ /* Disable Timer scan */
+ REG_WR(bp, TM_REG_EN_LINEAR0_TIMER + port*4, 0);
+ /*
+ * Wait for at least 10ms and up to 2 second for the timers scan to
+ * complete
+ */
+ for (i = 0; i < 200; i++) {
+ msleep(10);
+ if (!REG_RD(bp, TM_REG_LIN0_SCAN_ON + port*4))
+ break;
+ }
+#endif
/* Clear ILT */
base = FUNC_ILT_BASE(func);
for (i = base; i < base + ILT_PER_FUNC; i++)
@@ -7657,6 +7835,9 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
u32 reset_code = 0;
int i, cnt, rc;
+#ifdef BCM_CNIC
+ bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD);
+#endif
bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
/* Set "drop all" */
@@ -7675,7 +7856,7 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
bnx2x_free_irq(bp);
/* Wait until tx fastpath tasks complete */
- for_each_tx_queue(bp, i) {
+ for_each_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
cnt = 1000;
@@ -7703,7 +7884,7 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
struct mac_configuration_cmd *config =
bnx2x_sp(bp, mcast_config);
- bnx2x_set_mac_addr_e1(bp, 0);
+ bnx2x_set_eth_mac_addr_e1(bp, 0);
for (i = 0; i < config->hdr.length; i++)
CAM_INVALIDATE(config->config_table[i]);
@@ -7716,6 +7897,9 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
config->hdr.client_id = bp->fp->cl_id;
config->hdr.reserved1 = 0;
+ bp->set_mac_pending++;
+ smp_wmb();
+
bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0,
U64_HI(bnx2x_sp_mapping(bp, mcast_config)),
U64_LO(bnx2x_sp_mapping(bp, mcast_config)), 0);
@@ -7723,13 +7907,22 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
} else { /* E1H */
REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0);
- bnx2x_set_mac_addr_e1h(bp, 0);
+ bnx2x_set_eth_mac_addr_e1h(bp, 0);
for (i = 0; i < MC_HASH_SIZE; i++)
REG_WR(bp, MC_HASH_OFFSET(bp, i), 0);
REG_WR(bp, MISC_REG_E1HMF_MODE, 0);
}
+#ifdef BCM_CNIC
+ /* Clear iSCSI L2 MAC */
+ mutex_lock(&bp->cnic_mutex);
+ if (bp->cnic_flags & BNX2X_CNIC_FLAG_MAC_SET) {
+ bnx2x_set_iscsi_eth_mac_addr(bp, 0);
+ bp->cnic_flags &= ~BNX2X_CNIC_FLAG_MAC_SET;
+ }
+ mutex_unlock(&bp->cnic_mutex);
+#endif
if (unload_mode == UNLOAD_NORMAL)
reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS;
@@ -7806,9 +7999,9 @@ unload_error:
/* Free SKBs, SGEs, TPA pool and driver internals */
bnx2x_free_skbs(bp);
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE);
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
netif_napi_del(&bnx2x_fp(bp, i, napi));
bnx2x_free_mem(bp);
@@ -8506,6 +8699,14 @@ static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp)
bp->link_params.req_flow_ctrl, bp->port.advertising);
}
+static void __devinit bnx2x_set_mac_buf(u8 *mac_buf, u32 mac_lo, u16 mac_hi)
+{
+ mac_hi = cpu_to_be16(mac_hi);
+ mac_lo = cpu_to_be32(mac_lo);
+ memcpy(mac_buf, &mac_hi, sizeof(mac_hi));
+ memcpy(mac_buf + sizeof(mac_hi), &mac_lo, sizeof(mac_lo));
+}
+
static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp)
{
int port = BP_PORT(bp);
@@ -8587,14 +8788,15 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp)
val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_upper);
val = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_lower);
- bp->dev->dev_addr[0] = (u8)(val2 >> 8 & 0xff);
- bp->dev->dev_addr[1] = (u8)(val2 & 0xff);
- bp->dev->dev_addr[2] = (u8)(val >> 24 & 0xff);
- bp->dev->dev_addr[3] = (u8)(val >> 16 & 0xff);
- bp->dev->dev_addr[4] = (u8)(val >> 8 & 0xff);
- bp->dev->dev_addr[5] = (u8)(val & 0xff);
+ bnx2x_set_mac_buf(bp->dev->dev_addr, val, val2);
memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, ETH_ALEN);
memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN);
+
+#ifdef BCM_CNIC
+ val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].iscsi_mac_upper);
+ val = SHMEM_RD(bp, dev_info.port_hw_config[port].iscsi_mac_lower);
+ bnx2x_set_mac_buf(bp->iscsi_mac, val, val2);
+#endif
}
static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
@@ -8690,6 +8892,10 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp)
smp_wmb(); /* Ensure that bp->intr_sem update is SMP-safe */
mutex_init(&bp->port.phy_mutex);
+ mutex_init(&bp->fw_mb_mutex);
+#ifdef BCM_CNIC
+ mutex_init(&bp->cnic_mutex);
+#endif
INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task);
INIT_WORK(&bp->reset_task, bnx2x_reset_task);
@@ -8738,8 +8944,9 @@ static int __devinit bnx2x_init_bp(struct bnx2x *bp)
bp->rx_csum = 1;
- bp->tx_ticks = 50;
- bp->rx_ticks = 25;
+ /* make sure that the numbers are in the right granularity */
+ bp->tx_ticks = (50 / (4 * BNX2X_BTR)) * (4 * BNX2X_BTR);
+ bp->rx_ticks = (25 / (4 * BNX2X_BTR)) * (4 * BNX2X_BTR);
timer_interval = (CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ);
bp->current_interval = (poll ? poll : timer_interval);
@@ -8765,20 +8972,23 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
cmd->supported = bp->port.supported;
cmd->advertising = bp->port.advertising;
- if (netif_carrier_ok(dev)) {
+ if ((bp->state == BNX2X_STATE_OPEN) &&
+ !(bp->flags & MF_FUNC_DIS) &&
+ (bp->link_vars.link_up)) {
cmd->speed = bp->link_vars.line_speed;
cmd->duplex = bp->link_vars.duplex;
- } else {
- cmd->speed = bp->link_params.req_line_speed;
- cmd->duplex = bp->link_params.req_duplex;
- }
- if (IS_E1HMF(bp)) {
- u16 vn_max_rate;
+ if (IS_E1HMF(bp)) {
+ u16 vn_max_rate;
- vn_max_rate = ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >>
+ vn_max_rate =
+ ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >>
FUNC_MF_CFG_MAX_BW_SHIFT) * 100;
- if (vn_max_rate < cmd->speed)
- cmd->speed = vn_max_rate;
+ if (vn_max_rate < cmd->speed)
+ cmd->speed = vn_max_rate;
+ }
+ } else {
+ cmd->speed = -1;
+ cmd->duplex = -1;
}
if (bp->link_params.switch_cfg == SWITCH_CFG_10G) {
@@ -9163,6 +9373,9 @@ static u32 bnx2x_get_link(struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
+ if (bp->flags & MF_FUNC_DIS)
+ return 0;
+
return bp->link_vars.link_up;
}
@@ -9567,8 +9780,7 @@ static int bnx2x_set_eeprom(struct net_device *dev,
} else if (eeprom->magic == 0x50485952) {
/* 'PHYR' (0x50485952): re-init link after FW upgrade */
- if ((bp->state == BNX2X_STATE_OPEN) ||
- (bp->state == BNX2X_STATE_DISABLED)) {
+ if (bp->state == BNX2X_STATE_OPEN) {
bnx2x_acquire_phy_lock(bp);
rc |= bnx2x_link_reset(&bp->link_params,
&bp->link_vars, 1);
@@ -9818,11 +10030,6 @@ static const struct {
{ "idle check (online)" }
};
-static int bnx2x_self_test_count(struct net_device *dev)
-{
- return BNX2X_NUM_TESTS;
-}
-
static int bnx2x_test_registers(struct bnx2x *bp)
{
int idx, i, rc = -ENODEV;
@@ -9990,7 +10197,7 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
struct sk_buff *skb;
unsigned char *packet;
struct bnx2x_fastpath *fp_rx = &bp->fp[0];
- struct bnx2x_fastpath *fp_tx = &bp->fp[bp->num_rx_queues];
+ struct bnx2x_fastpath *fp_tx = &bp->fp[0];
u16 tx_start_idx, tx_idx;
u16 rx_start_idx, rx_idx;
u16 pkt_prod, bd_prod;
@@ -10067,13 +10274,12 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
fp_tx->tx_db.data.prod += 2;
barrier();
- DOORBELL(bp, fp_tx->index - bp->num_rx_queues, fp_tx->tx_db.raw);
+ DOORBELL(bp, fp_tx->index, fp_tx->tx_db.raw);
mmiowb();
num_pkts++;
fp_tx->tx_bd_prod += 2; /* start + pbd */
- bp->dev->trans_start = jiffies;
udelay(100);
@@ -10223,14 +10429,16 @@ static int bnx2x_test_intr(struct bnx2x *bp)
config->hdr.client_id = bp->fp->cl_id;
config->hdr.reserved1 = 0;
+ bp->set_mac_pending++;
+ smp_wmb();
rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0,
U64_HI(bnx2x_sp_mapping(bp, mac_config)),
U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0);
if (rc == 0) {
- bp->set_mac_pending++;
for (i = 0; i < 10; i++) {
if (!bp->set_mac_pending)
break;
+ smp_rmb();
msleep_interruptible(10);
}
if (i == 10)
@@ -10264,7 +10472,7 @@ static void bnx2x_self_test(struct net_device *dev,
/* disable input for TX port IF */
REG_WR(bp, NIG_REG_EGRESS_UMP0_IN_EN + port*4, 0);
- link_up = bp->link_vars.link_up;
+ link_up = (bnx2x_link_test(bp) == 0);
bnx2x_nic_unload(bp, UNLOAD_NORMAL);
bnx2x_nic_load(bp, LOAD_DIAG);
/* wait until link state is restored */
@@ -10436,6 +10644,36 @@ static const struct {
#define IS_E1HMF_MODE_STAT(bp) \
(IS_E1HMF(bp) && !(bp->msglevel & BNX2X_MSG_STATS))
+static int bnx2x_get_sset_count(struct net_device *dev, int stringset)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+ int i, num_stats;
+
+ switch(stringset) {
+ case ETH_SS_STATS:
+ if (is_multi(bp)) {
+ num_stats = BNX2X_NUM_Q_STATS * bp->num_queues;
+ if (!IS_E1HMF_MODE_STAT(bp))
+ num_stats += BNX2X_NUM_STATS;
+ } else {
+ if (IS_E1HMF_MODE_STAT(bp)) {
+ num_stats = 0;
+ for (i = 0; i < BNX2X_NUM_STATS; i++)
+ if (IS_FUNC_STAT(i))
+ num_stats++;
+ } else
+ num_stats = BNX2X_NUM_STATS;
+ }
+ return num_stats;
+
+ case ETH_SS_TEST:
+ return BNX2X_NUM_TESTS;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
{
struct bnx2x *bp = netdev_priv(dev);
@@ -10445,7 +10683,7 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
case ETH_SS_STATS:
if (is_multi(bp)) {
k = 0;
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
for (j = 0; j < BNX2X_NUM_Q_STATS; j++)
sprintf(buf + (k + j)*ETH_GSTRING_LEN,
bnx2x_q_stats_arr[j].string, i);
@@ -10473,28 +10711,6 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
}
}
-static int bnx2x_get_stats_count(struct net_device *dev)
-{
- struct bnx2x *bp = netdev_priv(dev);
- int i, num_stats;
-
- if (is_multi(bp)) {
- num_stats = BNX2X_NUM_Q_STATS * bp->num_rx_queues;
- if (!IS_E1HMF_MODE_STAT(bp))
- num_stats += BNX2X_NUM_STATS;
- } else {
- if (IS_E1HMF_MODE_STAT(bp)) {
- num_stats = 0;
- for (i = 0; i < BNX2X_NUM_STATS; i++)
- if (IS_FUNC_STAT(i))
- num_stats++;
- } else
- num_stats = BNX2X_NUM_STATS;
- }
-
- return num_stats;
-}
-
static void bnx2x_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *buf)
{
@@ -10504,7 +10720,7 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev,
if (is_multi(bp)) {
k = 0;
- for_each_rx_queue(bp, i) {
+ for_each_queue(bp, i) {
hw_stats = (u32 *)&bp->fp[i].eth_q_stats;
for (j = 0; j < BNX2X_NUM_Q_STATS; j++) {
if (bnx2x_q_stats_arr[j].size == 0) {
@@ -10570,7 +10786,6 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev,
static int bnx2x_phys_id(struct net_device *dev, u32 data)
{
struct bnx2x *bp = netdev_priv(dev);
- int port = BP_PORT(bp);
int i;
if (!netif_running(dev))
@@ -10584,13 +10799,10 @@ static int bnx2x_phys_id(struct net_device *dev, u32 data)
for (i = 0; i < (data * 2); i++) {
if ((i % 2) == 0)
- bnx2x_set_led(bp, port, LED_MODE_OPER, SPEED_1000,
- bp->link_params.hw_led_mode,
- bp->link_params.chip_id);
+ bnx2x_set_led(&bp->link_params, LED_MODE_OPER,
+ SPEED_1000);
else
- bnx2x_set_led(bp, port, LED_MODE_OFF, 0,
- bp->link_params.hw_led_mode,
- bp->link_params.chip_id);
+ bnx2x_set_led(&bp->link_params, LED_MODE_OFF, 0);
msleep_interruptible(500);
if (signal_pending(current))
@@ -10598,10 +10810,8 @@ static int bnx2x_phys_id(struct net_device *dev, u32 data)
}
if (bp->link_vars.link_up)
- bnx2x_set_led(bp, port, LED_MODE_OPER,
- bp->link_vars.line_speed,
- bp->link_params.hw_led_mode,
- bp->link_params.chip_id);
+ bnx2x_set_led(&bp->link_params, LED_MODE_OPER,
+ bp->link_vars.line_speed);
return 0;
}
@@ -10637,11 +10847,10 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = bnx2x_set_tso,
- .self_test_count = bnx2x_self_test_count,
.self_test = bnx2x_self_test,
+ .get_sset_count = bnx2x_get_sset_count,
.get_strings = bnx2x_get_strings,
.phys_id = bnx2x_phys_id,
- .get_stats_count = bnx2x_get_stats_count,
.get_ethtool_stats = bnx2x_get_ethtool_stats,
};
@@ -10707,54 +10916,60 @@ static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp)
static int bnx2x_poll(struct napi_struct *napi, int budget)
{
+ int work_done = 0;
struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath,
napi);
struct bnx2x *bp = fp->bp;
- int work_done = 0;
+ while (1) {
#ifdef BNX2X_STOP_ON_ERROR
- if (unlikely(bp->panic))
- goto poll_panic;
+ if (unlikely(bp->panic)) {
+ napi_complete(napi);
+ return 0;
+ }
#endif
- prefetch(fp->rx_buf_ring[RX_BD(fp->rx_bd_cons)].skb);
- prefetch((char *)(fp->rx_buf_ring[RX_BD(fp->rx_bd_cons)].skb) + 256);
-
- bnx2x_update_fpsb_idx(fp);
-
- if (bnx2x_has_rx_work(fp)) {
- work_done = bnx2x_rx_int(fp, budget);
+ if (bnx2x_has_tx_work(fp))
+ bnx2x_tx_int(fp);
- /* must not complete if we consumed full budget */
- if (work_done >= budget)
- goto poll_again;
- }
+ if (bnx2x_has_rx_work(fp)) {
+ work_done += bnx2x_rx_int(fp, budget - work_done);
- /* bnx2x_has_rx_work() reads the status block, thus we need to
- * ensure that status block indices have been actually read
- * (bnx2x_update_fpsb_idx) prior to this check (bnx2x_has_rx_work)
- * so that we won't write the "newer" value of the status block to IGU
- * (if there was a DMA right after bnx2x_has_rx_work and
- * if there is no rmb, the memory reading (bnx2x_update_fpsb_idx)
- * may be postponed to right before bnx2x_ack_sb). In this case
- * there will never be another interrupt until there is another update
- * of the status block, while there is still unhandled work.
- */
- rmb();
+ /* must not complete if we consumed full budget */
+ if (work_done >= budget)
+ break;
+ }
- if (!bnx2x_has_rx_work(fp)) {
-#ifdef BNX2X_STOP_ON_ERROR
-poll_panic:
-#endif
- napi_complete(napi);
+ /* Fall out from the NAPI loop if needed */
+ if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
+ bnx2x_update_fpsb_idx(fp);
+ /* bnx2x_has_rx_work() reads the status block, thus we need
+ * to ensure that status block indices have been actually read
+ * (bnx2x_update_fpsb_idx) prior to this check
+ * (bnx2x_has_rx_work) so that we won't write the "newer"
+ * value of the status block to IGU (if there was a DMA right
+ * after bnx2x_has_rx_work and if there is no rmb, the memory
+ * reading (bnx2x_update_fpsb_idx) may be postponed to right
+ * before bnx2x_ack_sb). In this case there will never be
+ * another interrupt until there is another update of the
+ * status block, while there is still unhandled work.
+ */
+ rmb();
- bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID,
- le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1);
- bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID,
- le16_to_cpu(fp->fp_c_idx), IGU_INT_ENABLE, 1);
+ if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
+ napi_complete(napi);
+ /* Re-enable interrupts */
+ bnx2x_ack_sb(bp, fp->sb_id, CSTORM_ID,
+ le16_to_cpu(fp->fp_c_idx),
+ IGU_INT_NOP, 1);
+ bnx2x_ack_sb(bp, fp->sb_id, USTORM_ID,
+ le16_to_cpu(fp->fp_u_idx),
+ IGU_INT_ENABLE, 1);
+ break;
+ }
+ }
}
-poll_again:
return work_done;
}
@@ -10843,10 +11058,10 @@ static inline u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb)
}
if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4)
- rc |= XMIT_GSO_V4;
+ rc |= (XMIT_GSO_V4 | XMIT_CSUM_V4 | XMIT_CSUM_TCP);
else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)
- rc |= XMIT_GSO_V6;
+ rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP | XMIT_CSUM_V6);
return rc;
}
@@ -10939,7 +11154,7 @@ exit_lbl:
static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
- struct bnx2x_fastpath *fp, *fp_stat;
+ struct bnx2x_fastpath *fp;
struct netdev_queue *txq;
struct sw_tx_bd *tx_buf;
struct eth_tx_start_bd *tx_start_bd;
@@ -10961,11 +11176,10 @@ static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
fp_index = skb_get_queue_mapping(skb);
txq = netdev_get_tx_queue(dev, fp_index);
- fp = &bp->fp[fp_index + bp->num_rx_queues];
- fp_stat = &bp->fp[fp_index];
+ fp = &bp->fp[fp_index];
if (unlikely(bnx2x_tx_avail(fp) < (skb_shinfo(skb)->nr_frags + 3))) {
- fp_stat->eth_q_stats.driver_xoff++;
+ fp->eth_q_stats.driver_xoff++;
netif_tx_stop_queue(txq);
BNX2X_ERR("BUG! Tx ring full when queue awake!\n");
return NETDEV_TX_BUSY;
@@ -11191,7 +11405,7 @@ static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
fp->tx_db.data.prod += nbd;
barrier();
- DOORBELL(bp, fp->index - bp->num_rx_queues, fp->tx_db.raw);
+ DOORBELL(bp, fp->index, fp->tx_db.raw);
mmiowb();
@@ -11202,11 +11416,11 @@ static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* We want bnx2x_tx_int to "see" the updated tx_bd_prod
if we put Tx into XOFF state. */
smp_mb();
- fp_stat->eth_q_stats.driver_xoff++;
+ fp->eth_q_stats.driver_xoff++;
if (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3)
netif_tx_wake_queue(txq);
}
- fp_stat->tx_pkt++;
+ fp->tx_pkt++;
return NETDEV_TX_OK;
}
@@ -11321,6 +11535,9 @@ static void bnx2x_set_rx_mode(struct net_device *dev)
config->hdr.client_id = bp->fp->cl_id;
config->hdr.reserved1 = 0;
+ bp->set_mac_pending++;
+ smp_wmb();
+
bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0,
U64_HI(bnx2x_sp_mapping(bp, mcast_config)),
U64_LO(bnx2x_sp_mapping(bp, mcast_config)),
@@ -11370,9 +11587,9 @@ static int bnx2x_change_mac_addr(struct net_device *dev, void *p)
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
if (netif_running(dev)) {
if (CHIP_IS_E1(bp))
- bnx2x_set_mac_addr_e1(bp, 1);
+ bnx2x_set_eth_mac_addr_e1(bp, 1);
else
- bnx2x_set_mac_addr_e1h(bp, 1);
+ bnx2x_set_eth_mac_addr_e1h(bp, 1);
}
return 0;
@@ -11830,21 +12047,14 @@ static inline void be16_to_cpu_n(const u8 *_source, u8 *_target, u32 n)
static int __devinit bnx2x_init_firmware(struct bnx2x *bp, struct device *dev)
{
- char fw_file_name[40] = {0};
+ const char *fw_file_name;
struct bnx2x_fw_file_hdr *fw_hdr;
- int rc, offset;
+ int rc;
- /* Create a FW file name */
if (CHIP_IS_E1(bp))
- offset = sprintf(fw_file_name, FW_FILE_PREFIX_E1);
+ fw_file_name = FW_FILE_NAME_E1;
else
- offset = sprintf(fw_file_name, FW_FILE_PREFIX_E1H);
-
- sprintf(fw_file_name + offset, "%d.%d.%d.%d.fw",
- BCM_5710_FW_MAJOR_VERSION,
- BCM_5710_FW_MINOR_VERSION,
- BCM_5710_FW_REVISION_VERSION,
- BCM_5710_FW_ENGINEERING_VERSION);
+ fw_file_name = FW_FILE_NAME_E1H;
printk(KERN_INFO PFX "Loading %s\n", fw_file_name);
@@ -12098,9 +12308,9 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
/* Free SKBs, SGEs, TPA pool and driver internals */
bnx2x_free_skbs(bp);
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE);
- for_each_rx_queue(bp, i)
+ for_each_queue(bp, i)
netif_napi_del(&bnx2x_fp(bp, i, napi));
bnx2x_free_mem(bp);
@@ -12276,4 +12486,287 @@ static void __exit bnx2x_cleanup(void)
module_init(bnx2x_init);
module_exit(bnx2x_cleanup);
+#ifdef BCM_CNIC
+
+/* count denotes the number of new completions we have seen */
+static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count)
+{
+ struct eth_spe *spe;
+
+#ifdef BNX2X_STOP_ON_ERROR
+ if (unlikely(bp->panic))
+ return;
+#endif
+
+ spin_lock_bh(&bp->spq_lock);
+ bp->cnic_spq_pending -= count;
+
+ for (; bp->cnic_spq_pending < bp->cnic_eth_dev.max_kwqe_pending;
+ bp->cnic_spq_pending++) {
+
+ if (!bp->cnic_kwq_pending)
+ break;
+
+ spe = bnx2x_sp_get_next(bp);
+ *spe = *bp->cnic_kwq_cons;
+
+ bp->cnic_kwq_pending--;
+
+ DP(NETIF_MSG_TIMER, "pending on SPQ %d, on KWQ %d count %d\n",
+ bp->cnic_spq_pending, bp->cnic_kwq_pending, count);
+
+ if (bp->cnic_kwq_cons == bp->cnic_kwq_last)
+ bp->cnic_kwq_cons = bp->cnic_kwq;
+ else
+ bp->cnic_kwq_cons++;
+ }
+ bnx2x_sp_prod_update(bp);
+ spin_unlock_bh(&bp->spq_lock);
+}
+
+static int bnx2x_cnic_sp_queue(struct net_device *dev,
+ struct kwqe_16 *kwqes[], u32 count)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+ int i;
+
+#ifdef BNX2X_STOP_ON_ERROR
+ if (unlikely(bp->panic))
+ return -EIO;
+#endif
+
+ spin_lock_bh(&bp->spq_lock);
+
+ for (i = 0; i < count; i++) {
+ struct eth_spe *spe = (struct eth_spe *)kwqes[i];
+
+ if (bp->cnic_kwq_pending == MAX_SP_DESC_CNT)
+ break;
+
+ *bp->cnic_kwq_prod = *spe;
+
+ bp->cnic_kwq_pending++;
+
+ DP(NETIF_MSG_TIMER, "L5 SPQE %x %x %x:%x pos %d\n",
+ spe->hdr.conn_and_cmd_data, spe->hdr.type,
+ spe->data.mac_config_addr.hi,
+ spe->data.mac_config_addr.lo,
+ bp->cnic_kwq_pending);
+
+ if (bp->cnic_kwq_prod == bp->cnic_kwq_last)
+ bp->cnic_kwq_prod = bp->cnic_kwq;
+ else
+ bp->cnic_kwq_prod++;
+ }
+
+ spin_unlock_bh(&bp->spq_lock);
+
+ if (bp->cnic_spq_pending < bp->cnic_eth_dev.max_kwqe_pending)
+ bnx2x_cnic_sp_post(bp, 0);
+
+ return i;
+}
+
+static int bnx2x_cnic_ctl_send(struct bnx2x *bp, struct cnic_ctl_info *ctl)
+{
+ struct cnic_ops *c_ops;
+ int rc = 0;
+
+ mutex_lock(&bp->cnic_mutex);
+ c_ops = bp->cnic_ops;
+ if (c_ops)
+ rc = c_ops->cnic_ctl(bp->cnic_data, ctl);
+ mutex_unlock(&bp->cnic_mutex);
+
+ return rc;
+}
+
+static int bnx2x_cnic_ctl_send_bh(struct bnx2x *bp, struct cnic_ctl_info *ctl)
+{
+ struct cnic_ops *c_ops;
+ int rc = 0;
+
+ rcu_read_lock();
+ c_ops = rcu_dereference(bp->cnic_ops);
+ if (c_ops)
+ rc = c_ops->cnic_ctl(bp->cnic_data, ctl);
+ rcu_read_unlock();
+
+ return rc;
+}
+
+/*
+ * for commands that have no data
+ */
+static int bnx2x_cnic_notify(struct bnx2x *bp, int cmd)
+{
+ struct cnic_ctl_info ctl = {0};
+
+ ctl.cmd = cmd;
+
+ return bnx2x_cnic_ctl_send(bp, &ctl);
+}
+
+static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid)
+{
+ struct cnic_ctl_info ctl;
+
+ /* first we tell CNIC and only then we count this as a completion */
+ ctl.cmd = CNIC_CTL_COMPLETION_CMD;
+ ctl.data.comp.cid = cid;
+
+ bnx2x_cnic_ctl_send_bh(bp, &ctl);
+ bnx2x_cnic_sp_post(bp, 1);
+}
+
+static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+ int rc = 0;
+
+ switch (ctl->cmd) {
+ case DRV_CTL_CTXTBL_WR_CMD: {
+ u32 index = ctl->data.io.offset;
+ dma_addr_t addr = ctl->data.io.dma_addr;
+
+ bnx2x_ilt_wr(bp, index, addr);
+ break;
+ }
+
+ case DRV_CTL_COMPLETION_CMD: {
+ int count = ctl->data.comp.comp_count;
+
+ bnx2x_cnic_sp_post(bp, count);
+ break;
+ }
+
+ /* rtnl_lock is held. */
+ case DRV_CTL_START_L2_CMD: {
+ u32 cli = ctl->data.ring.client_id;
+
+ bp->rx_mode_cl_mask |= (1 << cli);
+ bnx2x_set_storm_rx_mode(bp);
+ break;
+ }
+
+ /* rtnl_lock is held. */
+ case DRV_CTL_STOP_L2_CMD: {
+ u32 cli = ctl->data.ring.client_id;
+
+ bp->rx_mode_cl_mask &= ~(1 << cli);
+ bnx2x_set_storm_rx_mode(bp);
+ break;
+ }
+
+ default:
+ BNX2X_ERR("unknown command %x\n", ctl->cmd);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
+static void bnx2x_setup_cnic_irq_info(struct bnx2x *bp)
+{
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ if (bp->flags & USING_MSIX_FLAG) {
+ cp->drv_state |= CNIC_DRV_STATE_USING_MSIX;
+ cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX;
+ cp->irq_arr[0].vector = bp->msix_table[1].vector;
+ } else {
+ cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX;
+ cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX;
+ }
+ cp->irq_arr[0].status_blk = bp->cnic_sb;
+ cp->irq_arr[0].status_blk_num = CNIC_SB_ID(bp);
+ cp->irq_arr[1].status_blk = bp->def_status_blk;
+ cp->irq_arr[1].status_blk_num = DEF_SB_ID;
+
+ cp->num_irq = 2;
+}
+
+static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops,
+ void *data)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ if (ops == NULL)
+ return -EINVAL;
+
+ if (atomic_read(&bp->intr_sem) != 0)
+ return -EBUSY;
+
+ bp->cnic_kwq = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!bp->cnic_kwq)
+ return -ENOMEM;
+
+ bp->cnic_kwq_cons = bp->cnic_kwq;
+ bp->cnic_kwq_prod = bp->cnic_kwq;
+ bp->cnic_kwq_last = bp->cnic_kwq + MAX_SP_DESC_CNT;
+
+ bp->cnic_spq_pending = 0;
+ bp->cnic_kwq_pending = 0;
+
+ bp->cnic_data = data;
+
+ cp->num_irq = 0;
+ cp->drv_state = CNIC_DRV_STATE_REGD;
+
+ bnx2x_init_sb(bp, bp->cnic_sb, bp->cnic_sb_mapping, CNIC_SB_ID(bp));
+
+ bnx2x_setup_cnic_irq_info(bp);
+ bnx2x_set_iscsi_eth_mac_addr(bp, 1);
+ bp->cnic_flags |= BNX2X_CNIC_FLAG_MAC_SET;
+ rcu_assign_pointer(bp->cnic_ops, ops);
+
+ return 0;
+}
+
+static int bnx2x_unregister_cnic(struct net_device *dev)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ mutex_lock(&bp->cnic_mutex);
+ if (bp->cnic_flags & BNX2X_CNIC_FLAG_MAC_SET) {
+ bp->cnic_flags &= ~BNX2X_CNIC_FLAG_MAC_SET;
+ bnx2x_set_iscsi_eth_mac_addr(bp, 0);
+ }
+ cp->drv_state = 0;
+ rcu_assign_pointer(bp->cnic_ops, NULL);
+ mutex_unlock(&bp->cnic_mutex);
+ synchronize_rcu();
+ kfree(bp->cnic_kwq);
+ bp->cnic_kwq = NULL;
+
+ return 0;
+}
+
+struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev)
+{
+ struct bnx2x *bp = netdev_priv(dev);
+ struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
+
+ cp->drv_owner = THIS_MODULE;
+ cp->chip_id = CHIP_ID(bp);
+ cp->pdev = bp->pdev;
+ cp->io_base = bp->regview;
+ cp->io_base2 = bp->doorbells;
+ cp->max_kwqe_pending = 8;
+ cp->ctx_blk_size = CNIC_CTX_PER_ILT * sizeof(union cdu_context);
+ cp->ctx_tbl_offset = FUNC_ILT_BASE(BP_FUNC(bp)) + 1;
+ cp->ctx_tbl_len = CNIC_ILT_LINES;
+ cp->starting_cid = BCM_CNIC_CID_START;
+ cp->drv_submit_kwqes_16 = bnx2x_cnic_sp_queue;
+ cp->drv_ctl = bnx2x_drv_ctl;
+ cp->drv_register_cnic = bnx2x_register_cnic;
+ cp->drv_unregister_cnic = bnx2x_unregister_cnic;
+
+ return cp;
+}
+EXPORT_SYMBOL(bnx2x_cnic_probe);
+
+#endif /* BCM_CNIC */
diff --git a/drivers/net/bnx2x_reg.h b/drivers/net/bnx2x_reg.h
index aa76cbada5e..944964e78c8 100644
--- a/drivers/net/bnx2x_reg.h
+++ b/drivers/net/bnx2x_reg.h
@@ -2536,7 +2536,7 @@
/* [RC 1] A flag to indicate that overflow error occurred in one of the
queues. */
#define QM_REG_OVFERROR 0x16805c
-/* [RC 7] the Q were the qverflow occurs */
+/* [RC 7] the Q where the overflow occurs */
#define QM_REG_OVFQNUM 0x168058
/* [R 16] Pause state for physical queues 15-0 */
#define QM_REG_PAUSESTATE0 0x168410
@@ -4772,18 +4772,28 @@
#define PCI_ID_VAL2 0x438
-#define MDIO_REG_BANK_CL73_IEEEB0 0x0
-#define MDIO_CL73_IEEEB0_CL73_AN_CONTROL 0x0
+#define MDIO_REG_BANK_CL73_IEEEB0 0x0
+#define MDIO_CL73_IEEEB0_CL73_AN_CONTROL 0x0
#define MDIO_CL73_IEEEB0_CL73_AN_CONTROL_RESTART_AN 0x0200
#define MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN 0x1000
#define MDIO_CL73_IEEEB0_CL73_AN_CONTROL_MAIN_RST 0x8000
-#define MDIO_REG_BANK_CL73_IEEEB1 0x10
-#define MDIO_CL73_IEEEB1_AN_ADV2 0x01
+#define MDIO_REG_BANK_CL73_IEEEB1 0x10
+#define MDIO_CL73_IEEEB1_AN_ADV1 0x00
+#define MDIO_CL73_IEEEB1_AN_ADV1_PAUSE 0x0400
+#define MDIO_CL73_IEEEB1_AN_ADV1_ASYMMETRIC 0x0800
+#define MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_BOTH 0x0C00
+#define MDIO_CL73_IEEEB1_AN_ADV1_PAUSE_MASK 0x0C00
+#define MDIO_CL73_IEEEB1_AN_ADV2 0x01
#define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M 0x0000
#define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M_KX 0x0020
#define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4 0x0040
#define MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KR 0x0080
+#define MDIO_CL73_IEEEB1_AN_LP_ADV1 0x03
+#define MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE 0x0400
+#define MDIO_CL73_IEEEB1_AN_LP_ADV1_ASYMMETRIC 0x0800
+#define MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE_BOTH 0x0C00
+#define MDIO_CL73_IEEEB1_AN_LP_ADV1_PAUSE_MASK 0x0C00
#define MDIO_REG_BANK_RX0 0x80b0
#define MDIO_RX0_RX_STATUS 0x10
@@ -4910,6 +4920,8 @@
#define MDIO_REG_BANK_10G_PARALLEL_DETECT 0x8130
+#define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS 0x10
+#define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_STATUS_PD_LINK 0x8000
#define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL 0x11
#define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL_PARDET10G_EN 0x1
#define MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK 0x13
@@ -4934,6 +4946,8 @@
#define MDIO_SERDES_DIGITAL_A_1000X_STATUS1_SPEED_1G 0x0010
#define MDIO_SERDES_DIGITAL_A_1000X_STATUS1_SPEED_100M 0x0008
#define MDIO_SERDES_DIGITAL_A_1000X_STATUS1_SPEED_10M 0x0000
+#define MDIO_SERDES_DIGITAL_A_1000X_STATUS2 0x15
+#define MDIO_SERDES_DIGITAL_A_1000X_STATUS2_AN_DISABLED 0x0002
#define MDIO_SERDES_DIGITAL_MISC1 0x18
#define MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_MASK 0xE000
#define MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_25M 0x0000
@@ -5115,6 +5129,7 @@ Theotherbitsarereservedandshouldbezero*/
#define MDIO_PMA_REG_8481_LED1_MASK 0xa82c
#define MDIO_PMA_REG_8481_LED2_MASK 0xa82f
#define MDIO_PMA_REG_8481_LED3_MASK 0xa832
+#define MDIO_PMA_REG_8481_LED3_BLINK 0xa834
#define MDIO_PMA_REG_8481_SIGNAL_MASK 0xa835
#define MDIO_PMA_REG_8481_LINK_SIGNAL 0xa83b
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index c3fa31c9f2a..d69e6838f21 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -446,6 +446,48 @@ static u16 __ad_timer_to_ticks(u16 timer_type, u16 par)
/////////////////////////////////////////////////////////////////////////////////
/**
+ * __choose_matched - update a port's matched variable from a received lacpdu
+ * @lacpdu: the lacpdu we've received
+ * @port: the port we're looking at
+ *
+ * Update the value of the matched variable, using parameter values from a
+ * newly received lacpdu. Parameter values for the partner carried in the
+ * received PDU are compared with the corresponding operational parameter
+ * values for the actor. Matched is set to TRUE if all of these parameters
+ * match and the PDU parameter partner_state.aggregation has the same value as
+ * actor_oper_port_state.aggregation and lacp will actively maintain the link
+ * in the aggregation. Matched is also set to TRUE if the value of
+ * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates
+ * an individual link and lacp will actively maintain the link. Otherwise,
+ * matched is set to FALSE. LACP is considered to be actively maintaining the
+ * link if either the PDU's actor_state.lacp_activity variable is TRUE or both
+ * the actor's actor_oper_port_state.lacp_activity and the PDU's
+ * partner_state.lacp_activity variables are TRUE.
+ *
+ * Note: the AD_PORT_MATCHED "variable" is not specified by 802.3ad; it is
+ * used here to implement the language from 802.3ad 43.4.9 that requires
+ * recordPDU to "match" the LACPDU parameters to the stored values.
+ */
+static void __choose_matched(struct lacpdu *lacpdu, struct port *port)
+{
+ // check if all parameters are alike
+ if (((ntohs(lacpdu->partner_port) == port->actor_port_number) &&
+ (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) &&
+ !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) &&
+ (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) &&
+ (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) &&
+ ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) ||
+ // or this is individual link(aggregation == FALSE)
+ ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0)
+ ) {
+ // update the state machine Matched variable
+ port->sm_vars |= AD_PORT_MATCHED;
+ } else {
+ port->sm_vars &= ~AD_PORT_MATCHED;
+ }
+}
+
+/**
* __record_pdu - record parameters from a received lacpdu
* @lacpdu: the lacpdu we've received
* @port: the port we're looking at
@@ -459,6 +501,7 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
if (lacpdu && port) {
struct port_params *partner = &port->partner_oper;
+ __choose_matched(lacpdu, port);
// record the new parameter values for the partner operational
partner->port_number = ntohs(lacpdu->actor_port);
partner->port_priority = ntohs(lacpdu->actor_port_priority);
@@ -518,12 +561,12 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port)
const struct port_params *partner = &port->partner_oper;
// check if any parameter is different
- if (ntohs(lacpdu->actor_port) != partner->port_number
- || ntohs(lacpdu->actor_port_priority) != partner->port_priority
- || MAC_ADDRESS_COMPARE(&lacpdu->actor_system, &partner->system)
- || ntohs(lacpdu->actor_system_priority) != partner->system_priority
- || ntohs(lacpdu->actor_key) != partner->key
- || (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) {
+ if (ntohs(lacpdu->actor_port) != partner->port_number ||
+ ntohs(lacpdu->actor_port_priority) != partner->port_priority ||
+ MAC_ADDRESS_COMPARE(&lacpdu->actor_system, &partner->system) ||
+ ntohs(lacpdu->actor_system_priority) != partner->system_priority ||
+ ntohs(lacpdu->actor_key) != partner->key ||
+ (lacpdu->actor_state & AD_STATE_AGGREGATION) != (partner->port_state & AD_STATE_AGGREGATION)) {
// update the state machine Selected variable
port->sm_vars &= ~AD_PORT_SELECTED;
}
@@ -549,12 +592,12 @@ static void __update_default_selected(struct port *port)
const struct port_params *oper = &port->partner_oper;
// check if any parameter is different
- if (admin->port_number != oper->port_number
- || admin->port_priority != oper->port_priority
- || MAC_ADDRESS_COMPARE(&admin->system, &oper->system)
- || admin->system_priority != oper->system_priority
- || admin->key != oper->key
- || (admin->port_state & AD_STATE_AGGREGATION)
+ if (admin->port_number != oper->port_number ||
+ admin->port_priority != oper->port_priority ||
+ MAC_ADDRESS_COMPARE(&admin->system, &oper->system) ||
+ admin->system_priority != oper->system_priority ||
+ admin->key != oper->key ||
+ (admin->port_state & AD_STATE_AGGREGATION)
!= (oper->port_state & AD_STATE_AGGREGATION)) {
// update the state machine Selected variable
port->sm_vars &= ~AD_PORT_SELECTED;
@@ -563,47 +606,6 @@ static void __update_default_selected(struct port *port)
}
/**
- * __choose_matched - update a port's matched variable from a received lacpdu
- * @lacpdu: the lacpdu we've received
- * @port: the port we're looking at
- *
- * Update the value of the matched variable, using parameter values from a
- * newly received lacpdu. Parameter values for the partner carried in the
- * received PDU are compared with the corresponding operational parameter
- * values for the actor. Matched is set to TRUE if all of these parameters
- * match and the PDU parameter partner_state.aggregation has the same value as
- * actor_oper_port_state.aggregation and lacp will actively maintain the link
- * in the aggregation. Matched is also set to TRUE if the value of
- * actor_state.aggregation in the received PDU is set to FALSE, i.e., indicates
- * an individual link and lacp will actively maintain the link. Otherwise,
- * matched is set to FALSE. LACP is considered to be actively maintaining the
- * link if either the PDU's actor_state.lacp_activity variable is TRUE or both
- * the actor's actor_oper_port_state.lacp_activity and the PDU's
- * partner_state.lacp_activity variables are TRUE.
- */
-static void __choose_matched(struct lacpdu *lacpdu, struct port *port)
-{
- // validate lacpdu and port
- if (lacpdu && port) {
- // check if all parameters are alike
- if (((ntohs(lacpdu->partner_port) == port->actor_port_number) &&
- (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) &&
- !MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) &&
- (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) &&
- (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) &&
- ((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) ||
- // or this is individual link(aggregation == FALSE)
- ((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0)
- ) {
- // update the state machine Matched variable
- port->sm_vars |= AD_PORT_MATCHED;
- } else {
- port->sm_vars &= ~AD_PORT_MATCHED;
- }
- }
-}
-
-/**
* __update_ntt - update a port's ntt variable from a received lacpdu
* @lacpdu: the lacpdu we've received
* @port: the port we're looking at
@@ -1134,7 +1136,6 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
__update_selected(lacpdu, port);
__update_ntt(lacpdu, port);
__record_pdu(lacpdu, port);
- __choose_matched(lacpdu, port);
port->sm_rx_timer_counter = __ad_timer_to_ticks(AD_CURRENT_WHILE_TIMER, (u16)(port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT));
port->actor_oper_port_state &= ~AD_STATE_EXPIRED;
// verify that if the aggregator is enabled, the port is enabled too.
@@ -1956,7 +1957,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
struct port *port, *prev_port, *temp_port;
struct aggregator *aggregator, *new_aggregator, *temp_aggregator;
int select_new_active_agg = 0;
-
+
// find the aggregator related to this slave
aggregator = &(SLAVE_AD_INFO(slave).aggregator);
@@ -2024,7 +2025,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
// clear the aggregator
ad_clear_agg(aggregator);
-
+
if (select_new_active_agg) {
ad_agg_selection_logic(__get_first_agg(port));
}
@@ -2075,7 +2076,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
}
}
}
- port->slave=NULL;
+ port->slave=NULL;
}
/**
@@ -2301,7 +2302,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
}
/*
- * set link state for bonding master: if we have an active
+ * set link state for bonding master: if we have an active
* aggregator, we're up, if not, we're down. Presumes that we cannot
* have an active aggregator if there are no slaves with link up.
*
@@ -2395,7 +2396,7 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
goto out;
}
- slave_agg_no = bond->xmit_hash_policy(skb, dev, slaves_in_agg);
+ slave_agg_no = bond->xmit_hash_policy(skb, slaves_in_agg);
bond_for_each_slave(bond, slave, i) {
struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator;
@@ -2445,9 +2446,6 @@ int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct pac
struct slave *slave = NULL;
int ret = NET_RX_DROP;
- if (dev_net(dev) != &init_net)
- goto out;
-
if (!(dev->flags & IFF_MASTER))
goto out;
@@ -2468,4 +2466,3 @@ out:
return ret;
}
-
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 9b5936f072d..00ab51ef312 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -355,9 +355,6 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct
struct arp_pkt *arp = (struct arp_pkt *)skb->data;
int res = NET_RX_DROP;
- if (dev_net(bond_dev) != &init_net)
- goto out;
-
while (bond_dev->priv_flags & IFF_802_1Q_VLAN)
bond_dev = vlan_dev_real_dev(bond_dev);
@@ -559,7 +556,7 @@ static void rlb_update_rx_clients(struct bonding *bond)
}
}
- /* do not update the entries again untill this counter is zero so that
+ /* do not update the entries again until this counter is zero so that
* not to confuse the clients.
*/
bond_info->rlb_update_delay_counter = RLB_UPDATE_DELAY;
diff --git a/drivers/net/bonding/bond_ipv6.c b/drivers/net/bonding/bond_ipv6.c
index 83921abae12..b72e1dc8cf8 100644
--- a/drivers/net/bonding/bond_ipv6.c
+++ b/drivers/net/bonding/bond_ipv6.c
@@ -25,6 +25,7 @@
#include <net/ipv6.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
+#include <net/netns/generic.h>
#include "bonding.h"
/*
@@ -152,11 +153,9 @@ static int bond_inet6addr_event(struct notifier_block *this,
struct net_device *vlan_dev, *event_dev = ifa->idev->dev;
struct bonding *bond;
struct vlan_entry *vlan;
+ struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id);
- if (dev_net(event_dev) != &init_net)
- return NOTIFY_DONE;
-
- list_for_each_entry(bond, &bond_dev_list, bond_list) {
+ list_for_each_entry(bond, &bn->dev_list, bond_list) {
if (bond->dev == event_dev) {
switch (event) {
case NETDEV_UP:
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 40fb5eefc72..af9b9c4eb49 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -75,6 +75,7 @@
#include <linux/jiffies.h>
#include <net/route.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include "bonding.h"
#include "bond_3ad.h"
#include "bond_alb.h"
@@ -94,6 +95,7 @@ static int downdelay;
static int use_carrier = 1;
static char *mode;
static char *primary;
+static char *primary_reselect;
static char *lacp_rate;
static char *ad_select;
static char *xmit_hash_policy;
@@ -126,6 +128,14 @@ MODULE_PARM_DESC(mode, "Mode of operation : 0 for balance-rr, "
"6 for balance-alb");
module_param(primary, charp, 0);
MODULE_PARM_DESC(primary, "Primary network device to use");
+module_param(primary_reselect, charp, 0);
+MODULE_PARM_DESC(primary_reselect, "Reselect primary slave "
+ "once it comes up; "
+ "0 for always (default), "
+ "1 for only if speed of primary is "
+ "better, "
+ "2 for only on active slave "
+ "failure");
module_param(lacp_rate, charp, 0);
MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner "
"(slow/fast)");
@@ -148,11 +158,7 @@ MODULE_PARM_DESC(fail_over_mac, "For active-backup, do not set all slaves to the
static const char * const version =
DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n";
-LIST_HEAD(bond_dev_list);
-
-#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry *bond_proc_dir;
-#endif
+int bond_net_id __read_mostly;
static __be32 arp_target[BOND_MAX_ARP_TARGETS];
static int arp_ip_count;
@@ -200,6 +206,13 @@ const struct bond_parm_tbl fail_over_mac_tbl[] = {
{ NULL, -1},
};
+const struct bond_parm_tbl pri_reselect_tbl[] = {
+{ "always", BOND_PRI_RESELECT_ALWAYS},
+{ "better", BOND_PRI_RESELECT_BETTER},
+{ "failure", BOND_PRI_RESELECT_FAILURE},
+{ NULL, -1},
+};
+
struct bond_parm_tbl ad_select_tbl[] = {
{ "stable", BOND_AD_STABLE},
{ "bandwidth", BOND_AD_BANDWIDTH},
@@ -211,7 +224,7 @@ struct bond_parm_tbl ad_select_tbl[] = {
static void bond_send_gratuitous_arp(struct bonding *bond);
static int bond_init(struct net_device *bond_dev);
-static void bond_deinit(struct net_device *bond_dev);
+static void bond_uninit(struct net_device *bond_dev);
/*---------------------------- General routines -----------------------------*/
@@ -1070,6 +1083,25 @@ out:
}
+static bool bond_should_change_active(struct bonding *bond)
+{
+ struct slave *prim = bond->primary_slave;
+ struct slave *curr = bond->curr_active_slave;
+
+ if (!prim || !curr || curr->link != BOND_LINK_UP)
+ return true;
+ if (bond->force_primary) {
+ bond->force_primary = false;
+ return true;
+ }
+ if (bond->params.primary_reselect == BOND_PRI_RESELECT_BETTER &&
+ (prim->speed < curr->speed ||
+ (prim->speed == curr->speed && prim->duplex <= curr->duplex)))
+ return false;
+ if (bond->params.primary_reselect == BOND_PRI_RESELECT_FAILURE)
+ return false;
+ return true;
+}
/**
* find_best_interface - select the best available slave to be the active one
@@ -1084,7 +1116,7 @@ static struct slave *bond_find_best_slave(struct bonding *bond)
int mintime = bond->params.updelay;
int i;
- new_active = old_active = bond->curr_active_slave;
+ new_active = bond->curr_active_slave;
if (!new_active) { /* there were no active slaves left */
if (bond->slave_cnt > 0) /* found one slave */
@@ -1094,7 +1126,8 @@ static struct slave *bond_find_best_slave(struct bonding *bond)
}
if ((bond->primary_slave) &&
- bond->primary_slave->link == BOND_LINK_UP) {
+ bond->primary_slave->link == BOND_LINK_UP &&
+ bond_should_change_active(bond)) {
new_active = bond->primary_slave;
}
@@ -1678,8 +1711,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
/* if there is a primary slave, remember it */
- if (strcmp(bond->params.primary, new_slave->dev->name) == 0)
+ if (strcmp(bond->params.primary, new_slave->dev->name) == 0) {
bond->primary_slave = new_slave;
+ bond->force_primary = true;
+ }
}
write_lock_bh(&bond->curr_slave_lock);
@@ -1817,8 +1852,8 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
}
if (!bond->params.fail_over_mac) {
- if (!compare_ether_addr(bond_dev->dev_addr, slave->perm_hwaddr)
- && bond->slave_cnt > 1)
+ if (!compare_ether_addr(bond_dev->dev_addr, slave->perm_hwaddr) &&
+ bond->slave_cnt > 1)
pr_warning(DRV_NAME
": %s: Warning: the permanent HWaddr of %s - "
"%pM - is still in use by %s. "
@@ -1965,25 +2000,6 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
}
/*
-* Destroy a bonding device.
-* Must be under rtnl_lock when this function is called.
-*/
-static void bond_uninit(struct net_device *bond_dev)
-{
- struct bonding *bond = netdev_priv(bond_dev);
-
- bond_deinit(bond_dev);
- bond_destroy_sysfs_entry(bond);
-
- if (bond->wq)
- destroy_workqueue(bond->wq);
-
- netif_addr_lock_bh(bond_dev);
- bond_mc_list_destroy(bond);
- netif_addr_unlock_bh(bond_dev);
-}
-
-/*
* First release a slave and than destroy the bond if no more slaves are left.
* Must be under rtnl_lock when this function is called.
*/
@@ -2567,7 +2583,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
fl.fl4_dst = targets[i];
fl.fl4_tos = RTO_ONLINK;
- rv = ip_route_output_key(&init_net, &rt, &fl);
+ rv = ip_route_output_key(dev_net(bond->dev), &rt, &fl);
if (rv) {
if (net_ratelimit()) {
pr_warning(DRV_NAME
@@ -2675,9 +2691,6 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
unsigned char *arp_ptr;
__be32 sip, tip;
- if (dev_net(dev) != &init_net)
- goto out;
-
if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER))
goto out;
@@ -3201,11 +3214,14 @@ static void bond_info_show_master(struct seq_file *seq)
}
if (USES_PRIMARY(bond->params.mode)) {
- seq_printf(seq, "Primary Slave: %s\n",
+ seq_printf(seq, "Primary Slave: %s",
(bond->primary_slave) ?
bond->primary_slave->dev->name : "None");
+ if (bond->primary_slave)
+ seq_printf(seq, " (primary_reselect %s)",
+ pri_reselect_tbl[bond->params.primary_reselect].modename);
- seq_printf(seq, "Currently Active Slave: %s\n",
+ seq_printf(seq, "\nCurrently Active Slave: %s\n",
(curr) ? curr->dev->name : "None");
}
@@ -3334,13 +3350,14 @@ static const struct file_operations bond_info_fops = {
.release = seq_release,
};
-static int bond_create_proc_entry(struct bonding *bond)
+static void bond_create_proc_entry(struct bonding *bond)
{
struct net_device *bond_dev = bond->dev;
+ struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id);
- if (bond_proc_dir) {
+ if (bn->proc_dir) {
bond->proc_entry = proc_create_data(bond_dev->name,
- S_IRUGO, bond_proc_dir,
+ S_IRUGO, bn->proc_dir,
&bond_info_fops, bond);
if (bond->proc_entry == NULL)
pr_warning(DRV_NAME
@@ -3349,14 +3366,15 @@ static int bond_create_proc_entry(struct bonding *bond)
else
memcpy(bond->proc_file_name, bond_dev->name, IFNAMSIZ);
}
-
- return 0;
}
static void bond_remove_proc_entry(struct bonding *bond)
{
- if (bond_proc_dir && bond->proc_entry) {
- remove_proc_entry(bond->proc_file_name, bond_proc_dir);
+ struct net_device *bond_dev = bond->dev;
+ struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id);
+
+ if (bn->proc_dir && bond->proc_entry) {
+ remove_proc_entry(bond->proc_file_name, bn->proc_dir);
memset(bond->proc_file_name, 0, IFNAMSIZ);
bond->proc_entry = NULL;
}
@@ -3365,11 +3383,11 @@ static void bond_remove_proc_entry(struct bonding *bond)
/* Create the bonding directory under /proc/net, if doesn't exist yet.
* Caller must hold rtnl_lock.
*/
-static void bond_create_proc_dir(void)
+static void bond_create_proc_dir(struct bond_net *bn)
{
- if (!bond_proc_dir) {
- bond_proc_dir = proc_mkdir(DRV_NAME, init_net.proc_net);
- if (!bond_proc_dir)
+ if (!bn->proc_dir) {
+ bn->proc_dir = proc_mkdir(DRV_NAME, bn->net->proc_net);
+ if (!bn->proc_dir)
pr_warning(DRV_NAME
": Warning: cannot create /proc/net/%s\n",
DRV_NAME);
@@ -3379,17 +3397,17 @@ static void bond_create_proc_dir(void)
/* Destroy the bonding directory under /proc/net, if empty.
* Caller must hold rtnl_lock.
*/
-static void bond_destroy_proc_dir(void)
+static void bond_destroy_proc_dir(struct bond_net *bn)
{
- if (bond_proc_dir) {
- remove_proc_entry(DRV_NAME, init_net.proc_net);
- bond_proc_dir = NULL;
+ if (bn->proc_dir) {
+ remove_proc_entry(DRV_NAME, bn->net->proc_net);
+ bn->proc_dir = NULL;
}
}
#else /* !CONFIG_PROC_FS */
-static int bond_create_proc_entry(struct bonding *bond)
+static void bond_create_proc_entry(struct bonding *bond)
{
}
@@ -3397,11 +3415,11 @@ static void bond_remove_proc_entry(struct bonding *bond)
{
}
-static void bond_create_proc_dir(void)
+static void bond_create_proc_dir(struct bond_net *bn)
{
}
-static void bond_destroy_proc_dir(void)
+static void bond_destroy_proc_dir(struct bond_net *bn)
{
}
@@ -3418,9 +3436,6 @@ static int bond_event_changename(struct bonding *bond)
bond_remove_proc_entry(bond);
bond_create_proc_entry(bond);
- bond_destroy_sysfs_entry(bond);
- bond_create_sysfs_entry(bond);
-
return NOTIFY_DONE;
}
@@ -3432,9 +3447,6 @@ static int bond_master_netdev_event(unsigned long event,
switch (event) {
case NETDEV_CHANGENAME:
return bond_event_changename(event_bond);
- case NETDEV_UNREGISTER:
- bond_release_all(event_bond->dev);
- break;
default:
break;
}
@@ -3526,9 +3538,6 @@ static int bond_netdev_event(struct notifier_block *this,
{
struct net_device *event_dev = (struct net_device *)ptr;
- if (dev_net(event_dev) != &init_net)
- return NOTIFY_DONE;
-
pr_debug("event_dev: %s, event: %lx\n",
(event_dev ? event_dev->name : "None"),
event);
@@ -3561,13 +3570,11 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event,
{
struct in_ifaddr *ifa = ptr;
struct net_device *vlan_dev, *event_dev = ifa->ifa_dev->dev;
+ struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id);
struct bonding *bond;
struct vlan_entry *vlan;
- if (dev_net(ifa->ifa_dev->dev) != &init_net)
- return NOTIFY_DONE;
-
- list_for_each_entry(bond, &bond_dev_list, bond_list) {
+ list_for_each_entry(bond, &bn->dev_list, bond_list) {
if (bond->dev == event_dev) {
switch (event) {
case NETDEV_UP:
@@ -3657,8 +3664,7 @@ void bond_unregister_arp(struct bonding *bond)
* Hash for the output device based upon layer 2 and layer 3 data. If
* the packet is not IP mimic bond_xmit_hash_policy_l2()
*/
-static int bond_xmit_hash_policy_l23(struct sk_buff *skb,
- struct net_device *bond_dev, int count)
+static int bond_xmit_hash_policy_l23(struct sk_buff *skb, int count)
{
struct ethhdr *data = (struct ethhdr *)skb->data;
struct iphdr *iph = ip_hdr(skb);
@@ -3676,8 +3682,7 @@ static int bond_xmit_hash_policy_l23(struct sk_buff *skb,
* the packet is a frag or not TCP or UDP, just use layer 3 data. If it is
* altogether not IP, mimic bond_xmit_hash_policy_l2()
*/
-static int bond_xmit_hash_policy_l34(struct sk_buff *skb,
- struct net_device *bond_dev, int count)
+static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count)
{
struct ethhdr *data = (struct ethhdr *)skb->data;
struct iphdr *iph = ip_hdr(skb);
@@ -3701,8 +3706,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb,
/*
* Hash for the output device based upon layer 2 data
*/
-static int bond_xmit_hash_policy_l2(struct sk_buff *skb,
- struct net_device *bond_dev, int count)
+static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count)
{
struct ethhdr *data = (struct ethhdr *)skb->data;
@@ -3939,7 +3943,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- slave_dev = dev_get_by_name(&init_net, ifr->ifr_slave);
+ slave_dev = dev_get_by_name(dev_net(bond_dev), ifr->ifr_slave);
pr_debug("slave_dev=%p: \n", slave_dev);
@@ -4295,7 +4299,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
if (!BOND_IS_OK(bond))
goto out;
- slave_no = bond->xmit_hash_policy(skb, bond_dev, bond->slave_cnt);
+ slave_no = bond->xmit_hash_policy(skb, bond->slave_cnt);
bond_for_each_slave(bond, slave, i) {
slave_no--;
@@ -4576,37 +4580,29 @@ static void bond_work_cancel_all(struct bonding *bond)
cancel_delayed_work(&bond->ad_work);
}
-/* De-initialize device specific data.
- * Caller must hold rtnl_lock.
- */
-static void bond_deinit(struct net_device *bond_dev)
+/*
+* Destroy a bonding device.
+* Must be under rtnl_lock when this function is called.
+*/
+static void bond_uninit(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
+ /* Release the bonded slaves */
+ bond_release_all(bond_dev);
+
list_del(&bond->bond_list);
bond_work_cancel_all(bond);
bond_remove_proc_entry(bond);
-}
-
-/* Unregister and free all bond devices.
- * Caller must hold rtnl_lock.
- */
-static void bond_free_all(void)
-{
- struct bonding *bond, *nxt;
- list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
- struct net_device *bond_dev = bond->dev;
-
- bond_work_cancel_all(bond);
- /* Release the bonded slaves */
- bond_release_all(bond_dev);
- unregister_netdevice(bond_dev);
- }
+ if (bond->wq)
+ destroy_workqueue(bond->wq);
- bond_destroy_proc_dir();
+ netif_addr_lock_bh(bond_dev);
+ bond_mc_list_destroy(bond);
+ netif_addr_unlock_bh(bond_dev);
}
/*------------------------- Module initialization ---------------------------*/
@@ -4646,7 +4642,7 @@ int bond_parse_parm(const char *buf, const struct bond_parm_tbl *tbl)
static int bond_check_params(struct bond_params *params)
{
- int arp_validate_value, fail_over_mac_value;
+ int arp_validate_value, fail_over_mac_value, primary_reselect_value;
/*
* Convert string parameters.
@@ -4665,7 +4661,8 @@ static int bond_check_params(struct bond_params *params)
if ((bond_mode != BOND_MODE_XOR) &&
(bond_mode != BOND_MODE_8023AD)) {
pr_info(DRV_NAME
- ": xor_mode param is irrelevant in mode %s\n",
+ ": xmit_hash_policy param is irrelevant in"
+ " mode %s\n",
bond_mode_name(bond_mode));
} else {
xmit_hashtype = bond_parse_parm(xmit_hash_policy,
@@ -4945,6 +4942,20 @@ static int bond_check_params(struct bond_params *params)
primary = NULL;
}
+ if (primary && primary_reselect) {
+ primary_reselect_value = bond_parse_parm(primary_reselect,
+ pri_reselect_tbl);
+ if (primary_reselect_value == -1) {
+ pr_err(DRV_NAME
+ ": Error: Invalid primary_reselect \"%s\"\n",
+ primary_reselect ==
+ NULL ? "NULL" : primary_reselect);
+ return -EINVAL;
+ }
+ } else {
+ primary_reselect_value = BOND_PRI_RESELECT_ALWAYS;
+ }
+
if (fail_over_mac) {
fail_over_mac_value = bond_parse_parm(fail_over_mac,
fail_over_mac_tbl);
@@ -4976,6 +4987,7 @@ static int bond_check_params(struct bond_params *params)
params->use_carrier = use_carrier;
params->lacp_fast = lacp_fast;
params->primary[0] = 0;
+ params->primary_reselect = primary_reselect_value;
params->fail_over_mac = fail_over_mac_value;
if (primary) {
@@ -5012,6 +5024,7 @@ static void bond_set_lockdep_class(struct net_device *dev)
static int bond_init(struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
+ struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id);
pr_debug("Begin bond_init for %s\n", bond_dev->name);
@@ -5024,30 +5037,41 @@ static int bond_init(struct net_device *bond_dev)
netif_carrier_off(bond_dev);
bond_create_proc_entry(bond);
- list_add_tail(&bond->bond_list, &bond_dev_list);
+ list_add_tail(&bond->bond_list, &bn->dev_list);
+ bond_prepare_sysfs_group(bond);
+ return 0;
+}
+
+static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ if (tb[IFLA_ADDRESS]) {
+ if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+ return -EINVAL;
+ if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+ return -EADDRNOTAVAIL;
+ }
return 0;
}
+static struct rtnl_link_ops bond_link_ops __read_mostly = {
+ .kind = "bond",
+ .priv_size = sizeof(struct bonding),
+ .setup = bond_setup,
+ .validate = bond_validate,
+};
+
/* Create a new bond based on the specified name and bonding parameters.
* If name is NULL, obtain a suitable "bond%d" name for us.
* Caller must NOT hold rtnl_lock; we need to release it here before we
* set up our sysfs entries.
*/
-int bond_create(const char *name)
+int bond_create(struct net *net, const char *name)
{
struct net_device *bond_dev;
int res;
rtnl_lock();
- /* Check to see if the bond already exists. */
- /* FIXME: pass netns from caller */
- if (name && __dev_get_by_name(&init_net, name)) {
- pr_err(DRV_NAME ": cannot add bond %s; already exists\n",
- name);
- res = -EEXIST;
- goto out_rtnl;
- }
bond_dev = alloc_netdev(sizeof(struct bonding), name ? name : "",
bond_setup);
@@ -5055,9 +5079,12 @@ int bond_create(const char *name)
pr_err(DRV_NAME ": %s: eek! can't alloc netdev!\n",
name);
res = -ENOMEM;
- goto out_rtnl;
+ goto out;
}
+ dev_net_set(bond_dev, net);
+ bond_dev->rtnl_link_ops = &bond_link_ops;
+
if (!name) {
res = dev_alloc_name(bond_dev, "bond%d");
if (res < 0)
@@ -5065,27 +5092,41 @@ int bond_create(const char *name)
}
res = register_netdevice(bond_dev);
- if (res < 0)
- goto out_bond;
-
- res = bond_create_sysfs_entry(netdev_priv(bond_dev));
- if (res < 0)
- goto out_unreg;
+out:
rtnl_unlock();
- return 0;
-
-out_unreg:
- unregister_netdevice(bond_dev);
-out_bond:
- bond_deinit(bond_dev);
+ return res;
out_netdev:
free_netdev(bond_dev);
-out_rtnl:
- rtnl_unlock();
- return res;
+ goto out;
+}
+
+static int bond_net_init(struct net *net)
+{
+ struct bond_net *bn = net_generic(net, bond_net_id);
+
+ bn->net = net;
+ INIT_LIST_HEAD(&bn->dev_list);
+
+ bond_create_proc_dir(bn);
+
+ return 0;
}
+static void bond_net_exit(struct net *net)
+{
+ struct bond_net *bn = net_generic(net, bond_net_id);
+
+ bond_destroy_proc_dir(bn);
+}
+
+static struct pernet_operations bond_net_ops = {
+ .init = bond_net_init,
+ .exit = bond_net_exit,
+ .id = &bond_net_id,
+ .size = sizeof(struct bond_net),
+};
+
static int __init bonding_init(void)
{
int i;
@@ -5097,10 +5138,16 @@ static int __init bonding_init(void)
if (res)
goto out;
- bond_create_proc_dir();
+ res = register_pernet_subsys(&bond_net_ops);
+ if (res)
+ goto out;
+
+ res = rtnl_link_register(&bond_link_ops);
+ if (res)
+ goto err_link;
for (i = 0; i < max_bonds; i++) {
- res = bond_create(NULL);
+ res = bond_create(&init_net, NULL);
if (res)
goto err;
}
@@ -5112,14 +5159,13 @@ static int __init bonding_init(void)
register_netdevice_notifier(&bond_netdev_notifier);
register_inetaddr_notifier(&bond_inetaddr_notifier);
bond_register_ipv6_notifier();
-
- goto out;
-err:
- rtnl_lock();
- bond_free_all();
- rtnl_unlock();
out:
return res;
+err:
+ rtnl_link_unregister(&bond_link_ops);
+err_link:
+ unregister_pernet_subsys(&bond_net_ops);
+ goto out;
}
@@ -5131,9 +5177,8 @@ static void __exit bonding_exit(void)
bond_destroy_sysfs();
- rtnl_lock();
- bond_free_all();
- rtnl_unlock();
+ rtnl_link_unregister(&bond_link_ops);
+ unregister_pernet_subsys(&bond_net_ops);
}
module_init(bonding_init);
@@ -5142,3 +5187,4 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION);
MODULE_AUTHOR("Thomas Davis, tadavis@lbl.gov and many others");
+MODULE_ALIAS_RTNL_LINK("bond");
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 8762a27a2a1..4e00b4f8364 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -36,6 +36,8 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <linux/nsproxy.h>
#include "bonding.h"
@@ -48,12 +50,14 @@
*/
static ssize_t bonding_show_bonds(struct class *cls, char *buf)
{
+ struct net *net = current->nsproxy->net_ns;
+ struct bond_net *bn = net_generic(net, bond_net_id);
int res = 0;
struct bonding *bond;
rtnl_lock();
- list_for_each_entry(bond, &bond_dev_list, bond_list) {
+ list_for_each_entry(bond, &bn->dev_list, bond_list) {
if (res > (PAGE_SIZE - IFNAMSIZ)) {
/* not enough space for another interface name */
if ((PAGE_SIZE - res) > 10)
@@ -70,11 +74,12 @@ static ssize_t bonding_show_bonds(struct class *cls, char *buf)
return res;
}
-static struct net_device *bond_get_by_name(const char *ifname)
+static struct net_device *bond_get_by_name(struct net *net, const char *ifname)
{
+ struct bond_net *bn = net_generic(net, bond_net_id);
struct bonding *bond;
- list_for_each_entry(bond, &bond_dev_list, bond_list) {
+ list_for_each_entry(bond, &bn->dev_list, bond_list) {
if (strncmp(bond->dev->name, ifname, IFNAMSIZ) == 0)
return bond->dev;
}
@@ -92,6 +97,7 @@ static struct net_device *bond_get_by_name(const char *ifname)
static ssize_t bonding_store_bonds(struct class *cls,
const char *buffer, size_t count)
{
+ struct net *net = current->nsproxy->net_ns;
char command[IFNAMSIZ + 1] = {0, };
char *ifname;
int rv, res = count;
@@ -105,7 +111,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
if (command[0] == '+') {
pr_info(DRV_NAME
": %s is being created...\n", ifname);
- rv = bond_create(ifname);
+ rv = bond_create(net, ifname);
if (rv) {
pr_info(DRV_NAME ": Bond creation failed.\n");
res = rv;
@@ -114,7 +120,7 @@ static ssize_t bonding_store_bonds(struct class *cls,
struct net_device *bond_dev;
rtnl_lock();
- bond_dev = bond_get_by_name(ifname);
+ bond_dev = bond_get_by_name(net, ifname);
if (bond_dev) {
pr_info(DRV_NAME ": %s is being deleted...\n",
ifname);
@@ -239,8 +245,7 @@ static ssize_t bonding_store_slaves(struct device *d,
/* Got a slave name in ifname. Is it already in the list? */
found = 0;
- /* FIXME: get netns from sysfs object */
- dev = __dev_get_by_name(&init_net, ifname);
+ dev = __dev_get_by_name(dev_net(bond->dev), ifname);
if (!dev) {
pr_info(DRV_NAME
": %s: Interface %s does not exist!\n",
@@ -1214,6 +1219,58 @@ static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR,
bonding_show_primary, bonding_store_primary);
/*
+ * Show and set the primary_reselect flag.
+ */
+static ssize_t bonding_show_primary_reselect(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bonding *bond = to_bond(d);
+
+ return sprintf(buf, "%s %d\n",
+ pri_reselect_tbl[bond->params.primary_reselect].modename,
+ bond->params.primary_reselect);
+}
+
+static ssize_t bonding_store_primary_reselect(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(d);
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ new_value = bond_parse_parm(buf, pri_reselect_tbl);
+ if (new_value < 0) {
+ pr_err(DRV_NAME
+ ": %s: Ignoring invalid primary_reselect value %.*s.\n",
+ bond->dev->name,
+ (int) strlen(buf) - 1, buf);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ bond->params.primary_reselect = new_value;
+ pr_info(DRV_NAME ": %s: setting primary_reselect to %s (%d).\n",
+ bond->dev->name, pri_reselect_tbl[new_value].modename,
+ new_value);
+
+ read_lock(&bond->lock);
+ write_lock_bh(&bond->curr_slave_lock);
+ bond_select_active_slave(bond);
+ write_unlock_bh(&bond->curr_slave_lock);
+ read_unlock(&bond->lock);
+out:
+ rtnl_unlock();
+ return ret;
+}
+static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR,
+ bonding_show_primary_reselect,
+ bonding_store_primary_reselect);
+
+/*
* Show and set the use_carrier flag.
*/
static ssize_t bonding_show_carrier(struct device *d,
@@ -1502,6 +1559,7 @@ static struct attribute *per_bond_attrs[] = {
&dev_attr_num_unsol_na.attr,
&dev_attr_miimon.attr,
&dev_attr_primary.attr,
+ &dev_attr_primary_reselect.attr,
&dev_attr_use_carrier.attr,
&dev_attr_active_slave.attr,
&dev_attr_mii_status.attr,
@@ -1564,24 +1622,8 @@ void bond_destroy_sysfs(void)
* Initialize sysfs for each bond. This sets up and registers
* the 'bondctl' directory for each individual bond under /sys/class/net.
*/
-int bond_create_sysfs_entry(struct bonding *bond)
+void bond_prepare_sysfs_group(struct bonding *bond)
{
- struct net_device *dev = bond->dev;
- int err;
-
- err = sysfs_create_group(&(dev->dev.kobj), &bonding_group);
- if (err)
- pr_emerg("eek! didn't create group!\n");
-
- return err;
-}
-/*
- * Remove sysfs entries for each bond.
- */
-void bond_destroy_sysfs_entry(struct bonding *bond)
-{
- struct net_device *dev = bond->dev;
-
- sysfs_remove_group(&(dev->dev.kobj), &bonding_group);
+ bond->dev->sysfs_groups[0] = &bonding_group;
}
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 68247714466..558ec135252 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -23,15 +23,13 @@
#include "bond_3ad.h"
#include "bond_alb.h"
-#define DRV_VERSION "3.5.0"
-#define DRV_RELDATE "November 4, 2008"
+#define DRV_VERSION "3.6.0"
+#define DRV_RELDATE "September 26, 2009"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
#define BOND_MAX_ARP_TARGETS 16
-extern struct list_head bond_dev_list;
-
#define IS_UP(dev) \
((((dev)->flags & IFF_UP) == IFF_UP) && \
netif_running(dev) && \
@@ -131,6 +129,7 @@ struct bond_params {
int lacp_fast;
int ad_select;
char primary[IFNAMSIZ];
+ int primary_reselect;
__be32 arp_targets[BOND_MAX_ARP_TARGETS];
};
@@ -190,6 +189,7 @@ struct bonding {
struct slave *curr_active_slave;
struct slave *current_arp_slave;
struct slave *primary_slave;
+ bool force_primary;
s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
rwlock_t lock;
rwlock_t curr_slave_lock;
@@ -204,7 +204,7 @@ struct bonding {
#endif /* CONFIG_PROC_FS */
struct list_head bond_list;
struct dev_mc_list *mc_list;
- int (*xmit_hash_policy)(struct sk_buff *, struct net_device *, int);
+ int (*xmit_hash_policy)(struct sk_buff *, int);
__be32 master_ip;
u16 flags;
u16 rr_tx_counter;
@@ -254,10 +254,14 @@ static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
static inline bool bond_is_lb(const struct bonding *bond)
{
- return bond->params.mode == BOND_MODE_TLB
- || bond->params.mode == BOND_MODE_ALB;
+ return (bond->params.mode == BOND_MODE_TLB ||
+ bond->params.mode == BOND_MODE_ALB);
}
+#define BOND_PRI_RESELECT_ALWAYS 0
+#define BOND_PRI_RESELECT_BETTER 1
+#define BOND_PRI_RESELECT_FAILURE 2
+
#define BOND_FOM_NONE 0
#define BOND_FOM_ACTIVE 1
#define BOND_FOM_FOLLOW 2
@@ -321,12 +325,11 @@ static inline void bond_unset_master_alb_flags(struct bonding *bond)
struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr);
int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
-int bond_create(const char *name);
+int bond_create(struct net *net, const char *name);
int bond_release_and_destroy(struct net_device *bond_dev, struct net_device *slave_dev);
int bond_create_sysfs(void);
void bond_destroy_sysfs(void);
-void bond_destroy_sysfs_entry(struct bonding *bond);
-int bond_create_sysfs_entry(struct bonding *bond);
+void bond_prepare_sysfs_group(struct bonding *bond);
int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave);
void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave);
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev);
@@ -341,13 +344,22 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
void bond_register_arp(struct bonding *);
void bond_unregister_arp(struct bonding *);
+struct bond_net {
+ struct net * net; /* Associated network namespace */
+ struct list_head dev_list;
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry * proc_dir;
+#endif
+};
+
/* exported from bond_main.c */
-extern struct list_head bond_dev_list;
+extern int bond_net_id;
extern const struct bond_parm_tbl bond_lacp_tbl[];
extern const struct bond_parm_tbl bond_mode_tbl[];
extern const struct bond_parm_tbl xmit_hashtype_tbl[];
extern const struct bond_parm_tbl arp_validate_tbl[];
extern const struct bond_parm_tbl fail_over_mac_tbl[];
+extern const struct bond_parm_tbl pri_reselect_tbl[];
extern struct bond_parm_tbl ad_select_tbl[];
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -370,4 +382,3 @@ static inline void bond_unregister_ipv6_notifier(void)
#endif
#endif /* _LINUX_BONDING_H */
-
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 772f6d2489c..bb803fa1e6a 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -41,6 +41,21 @@ config CAN_AT91
---help---
This is a driver for the SoC CAN controller in Atmel's AT91SAM9263.
+config CAN_TI_HECC
+ depends on CAN_DEV && ARCH_OMAP3
+ tristate "TI High End CAN Controller"
+ ---help---
+ Driver for TI HECC (High End CAN Controller) module found on many
+ TI devices. The device specifications are available from www.ti.com
+
+config CAN_MCP251X
+ tristate "Microchip MCP251x SPI CAN controllers"
+ depends on CAN_DEV && SPI
+ ---help---
+ Driver for the Microchip MCP251x SPI CAN controllers.
+
+source "drivers/net/can/mscan/Kconfig"
+
source "drivers/net/can/sja1000/Kconfig"
source "drivers/net/can/usb/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 0dea62721f2..56899fef1c6 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -10,6 +10,9 @@ can-dev-y := dev.o
obj-y += usb/
obj-$(CONFIG_CAN_SJA1000) += sja1000/
+obj-$(CONFIG_CAN_MSCAN) += mscan/
obj-$(CONFIG_CAN_AT91) += at91_can.o
+obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
+obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index f67ae285a35..cbe3fce53e3 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -221,38 +221,6 @@ static inline void set_mb_mode(const struct at91_priv *priv, unsigned int mb,
set_mb_mode_prio(priv, mb, mode, 0);
}
-static struct sk_buff *alloc_can_skb(struct net_device *dev,
- struct can_frame **cf)
-{
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, sizeof(struct can_frame));
- if (unlikely(!skb))
- return NULL;
-
- skb->protocol = htons(ETH_P_CAN);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
-
- return skb;
-}
-
-static struct sk_buff *alloc_can_err_skb(struct net_device *dev,
- struct can_frame **cf)
-{
- struct sk_buff *skb;
-
- skb = alloc_can_skb(dev, cf);
- if (unlikely(!skb))
- return NULL;
-
- memset(*cf, 0, sizeof(struct can_frame));
- (*cf)->can_id = CAN_ERR_FLAG;
- (*cf)->can_dlc = CAN_ERR_DLC;
-
- return skb;
-}
-
/*
* Swtich transceiver on or off
*/
@@ -1087,7 +1055,7 @@ static int __init at91_can_probe(struct platform_device *pdev)
goto exit_release;
}
- dev = alloc_candev(sizeof(struct at91_priv));
+ dev = alloc_candev(sizeof(struct at91_priv), AT91_MB_TX_NUM);
if (!dev) {
err = -ENOMEM;
goto exit_iounmap;
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 2868fe842a4..c1bb29f0322 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -245,7 +245,7 @@ static void can_flush_echo_skb(struct net_device *dev)
struct net_device_stats *stats = &dev->stats;
int i;
- for (i = 0; i < CAN_ECHO_SKB_MAX; i++) {
+ for (i = 0; i < priv->echo_skb_max; i++) {
if (priv->echo_skb[i]) {
kfree_skb(priv->echo_skb[i]);
priv->echo_skb[i] = NULL;
@@ -262,10 +262,13 @@ static void can_flush_echo_skb(struct net_device *dev)
* of the device driver. The driver must protect access to
* priv->echo_skb, if necessary.
*/
-void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx)
+void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
+ unsigned int idx)
{
struct can_priv *priv = netdev_priv(dev);
+ BUG_ON(idx >= priv->echo_skb_max);
+
/* check flag whether this packet has to be looped back */
if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) {
kfree_skb(skb);
@@ -311,10 +314,12 @@ EXPORT_SYMBOL_GPL(can_put_echo_skb);
* is handled in the device driver. The driver must protect
* access to priv->echo_skb, if necessary.
*/
-void can_get_echo_skb(struct net_device *dev, int idx)
+void can_get_echo_skb(struct net_device *dev, unsigned int idx)
{
struct can_priv *priv = netdev_priv(dev);
+ BUG_ON(idx >= priv->echo_skb_max);
+
if (priv->echo_skb[idx]) {
netif_rx(priv->echo_skb[idx]);
priv->echo_skb[idx] = NULL;
@@ -327,10 +332,12 @@ EXPORT_SYMBOL_GPL(can_get_echo_skb);
*
* The function is typically called when TX failed.
*/
-void can_free_echo_skb(struct net_device *dev, int idx)
+void can_free_echo_skb(struct net_device *dev, unsigned int idx)
{
struct can_priv *priv = netdev_priv(dev);
+ BUG_ON(idx >= priv->echo_skb_max);
+
if (priv->echo_skb[idx]) {
kfree_skb(priv->echo_skb[idx]);
priv->echo_skb[idx] = NULL;
@@ -359,17 +366,12 @@ void can_restart(unsigned long data)
can_flush_echo_skb(dev);
/* send restart message upstream */
- skb = dev_alloc_skb(sizeof(struct can_frame));
+ skb = alloc_can_err_skb(dev, &cf);
if (skb == NULL) {
err = -ENOMEM;
goto restart;
}
- skb->dev = dev;
- skb->protocol = htons(ETH_P_CAN);
- cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
- memset(cf, 0, sizeof(struct can_frame));
- cf->can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED;
- cf->can_dlc = CAN_ERR_DLC;
+ cf->can_id |= CAN_ERR_RESTARTED;
netif_rx(skb);
@@ -442,20 +444,66 @@ static void can_setup(struct net_device *dev)
dev->features = NETIF_F_NO_CSUM;
}
+struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
+{
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, sizeof(struct can_frame));
+ if (unlikely(!skb))
+ return NULL;
+
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ *cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(*cf, 0, sizeof(struct can_frame));
+
+ return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_can_skb);
+
+struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_can_skb(dev, cf);
+ if (unlikely(!skb))
+ return NULL;
+
+ (*cf)->can_id = CAN_ERR_FLAG;
+ (*cf)->can_dlc = CAN_ERR_DLC;
+
+ return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_can_err_skb);
+
/*
* Allocate and setup space for the CAN network device
*/
-struct net_device *alloc_candev(int sizeof_priv)
+struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
{
struct net_device *dev;
struct can_priv *priv;
+ int size;
- dev = alloc_netdev(sizeof_priv, "can%d", can_setup);
+ if (echo_skb_max)
+ size = ALIGN(sizeof_priv, sizeof(struct sk_buff *)) +
+ echo_skb_max * sizeof(struct sk_buff *);
+ else
+ size = sizeof_priv;
+
+ dev = alloc_netdev(size, "can%d", can_setup);
if (!dev)
return NULL;
priv = netdev_priv(dev);
+ if (echo_skb_max) {
+ priv->echo_skb_max = echo_skb_max;
+ priv->echo_skb = (void *)priv +
+ ALIGN(sizeof_priv, sizeof(struct sk_buff *));
+ }
+
priv->state = CAN_STATE_STOPPED;
init_timer(&priv->restart_timer);
@@ -647,7 +695,7 @@ nla_put_failure:
return -EMSGSIZE;
}
-static int can_newlink(struct net_device *dev,
+static int can_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
return -EOPNOTSUPP;
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c
new file mode 100644
index 00000000000..78b1b69b292
--- /dev/null
+++ b/drivers/net/can/mcp251x.c
@@ -0,0 +1,1166 @@
+/*
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ * MCP2510 support and bug fixes by Christian Pellegrin
+ * <chripell@evolware.org>
+ *
+ * Copyright 2009 Christian Pellegrin EVOL S.r.l.
+ *
+ * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.
+ * Written under contract by:
+ * Chris Elston, Katalix Systems, Ltd.
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ * Based on CAN bus driver for the CCAN controller written by
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ * Copyright 2007
+ *
+ * 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
+ *
+ * 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
+ *
+ *
+ *
+ * Your platform definition file should specify something like:
+ *
+ * static struct mcp251x_platform_data mcp251x_info = {
+ * .oscillator_frequency = 8000000,
+ * .board_specific_setup = &mcp251x_setup,
+ * .model = CAN_MCP251X_MCP2510,
+ * .power_enable = mcp251x_power_enable,
+ * .transceiver_enable = NULL,
+ * };
+ *
+ * static struct spi_board_info spi_board_info[] = {
+ * {
+ * .modalias = "mcp251x",
+ * .platform_data = &mcp251x_info,
+ * .irq = IRQ_EINT13,
+ * .max_speed_hz = 2*1000*1000,
+ * .chip_select = 2,
+ * },
+ * };
+ *
+ * Please see mcp251x.h for a description of the fields in
+ * struct mcp251x_platform_data.
+ *
+ */
+
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/platform/mcp251x.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+
+/* SPI interface instruction set */
+#define INSTRUCTION_WRITE 0x02
+#define INSTRUCTION_READ 0x03
+#define INSTRUCTION_BIT_MODIFY 0x05
+#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n))
+#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94)
+#define INSTRUCTION_RESET 0xC0
+
+/* MPC251x registers */
+#define CANSTAT 0x0e
+#define CANCTRL 0x0f
+# define CANCTRL_REQOP_MASK 0xe0
+# define CANCTRL_REQOP_CONF 0x80
+# define CANCTRL_REQOP_LISTEN_ONLY 0x60
+# define CANCTRL_REQOP_LOOPBACK 0x40
+# define CANCTRL_REQOP_SLEEP 0x20
+# define CANCTRL_REQOP_NORMAL 0x00
+# define CANCTRL_OSM 0x08
+# define CANCTRL_ABAT 0x10
+#define TEC 0x1c
+#define REC 0x1d
+#define CNF1 0x2a
+# define CNF1_SJW_SHIFT 6
+#define CNF2 0x29
+# define CNF2_BTLMODE 0x80
+# define CNF2_SAM 0x40
+# define CNF2_PS1_SHIFT 3
+#define CNF3 0x28
+# define CNF3_SOF 0x08
+# define CNF3_WAKFIL 0x04
+# define CNF3_PHSEG2_MASK 0x07
+#define CANINTE 0x2b
+# define CANINTE_MERRE 0x80
+# define CANINTE_WAKIE 0x40
+# define CANINTE_ERRIE 0x20
+# define CANINTE_TX2IE 0x10
+# define CANINTE_TX1IE 0x08
+# define CANINTE_TX0IE 0x04
+# define CANINTE_RX1IE 0x02
+# define CANINTE_RX0IE 0x01
+#define CANINTF 0x2c
+# define CANINTF_MERRF 0x80
+# define CANINTF_WAKIF 0x40
+# define CANINTF_ERRIF 0x20
+# define CANINTF_TX2IF 0x10
+# define CANINTF_TX1IF 0x08
+# define CANINTF_TX0IF 0x04
+# define CANINTF_RX1IF 0x02
+# define CANINTF_RX0IF 0x01
+#define EFLG 0x2d
+# define EFLG_EWARN 0x01
+# define EFLG_RXWAR 0x02
+# define EFLG_TXWAR 0x04
+# define EFLG_RXEP 0x08
+# define EFLG_TXEP 0x10
+# define EFLG_TXBO 0x20
+# define EFLG_RX0OVR 0x40
+# define EFLG_RX1OVR 0x80
+#define TXBCTRL(n) (((n) * 0x10) + 0x30 + TXBCTRL_OFF)
+# define TXBCTRL_ABTF 0x40
+# define TXBCTRL_MLOA 0x20
+# define TXBCTRL_TXERR 0x10
+# define TXBCTRL_TXREQ 0x08
+#define TXBSIDH(n) (((n) * 0x10) + 0x30 + TXBSIDH_OFF)
+# define SIDH_SHIFT 3
+#define TXBSIDL(n) (((n) * 0x10) + 0x30 + TXBSIDL_OFF)
+# define SIDL_SID_MASK 7
+# define SIDL_SID_SHIFT 5
+# define SIDL_EXIDE_SHIFT 3
+# define SIDL_EID_SHIFT 16
+# define SIDL_EID_MASK 3
+#define TXBEID8(n) (((n) * 0x10) + 0x30 + TXBEID8_OFF)
+#define TXBEID0(n) (((n) * 0x10) + 0x30 + TXBEID0_OFF)
+#define TXBDLC(n) (((n) * 0x10) + 0x30 + TXBDLC_OFF)
+# define DLC_RTR_SHIFT 6
+#define TXBCTRL_OFF 0
+#define TXBSIDH_OFF 1
+#define TXBSIDL_OFF 2
+#define TXBEID8_OFF 3
+#define TXBEID0_OFF 4
+#define TXBDLC_OFF 5
+#define TXBDAT_OFF 6
+#define RXBCTRL(n) (((n) * 0x10) + 0x60 + RXBCTRL_OFF)
+# define RXBCTRL_BUKT 0x04
+# define RXBCTRL_RXM0 0x20
+# define RXBCTRL_RXM1 0x40
+#define RXBSIDH(n) (((n) * 0x10) + 0x60 + RXBSIDH_OFF)
+# define RXBSIDH_SHIFT 3
+#define RXBSIDL(n) (((n) * 0x10) + 0x60 + RXBSIDL_OFF)
+# define RXBSIDL_IDE 0x08
+# define RXBSIDL_EID 3
+# define RXBSIDL_SHIFT 5
+#define RXBEID8(n) (((n) * 0x10) + 0x60 + RXBEID8_OFF)
+#define RXBEID0(n) (((n) * 0x10) + 0x60 + RXBEID0_OFF)
+#define RXBDLC(n) (((n) * 0x10) + 0x60 + RXBDLC_OFF)
+# define RXBDLC_LEN_MASK 0x0f
+# define RXBDLC_RTR 0x40
+#define RXBCTRL_OFF 0
+#define RXBSIDH_OFF 1
+#define RXBSIDL_OFF 2
+#define RXBEID8_OFF 3
+#define RXBEID0_OFF 4
+#define RXBDLC_OFF 5
+#define RXBDAT_OFF 6
+
+#define GET_BYTE(val, byte) \
+ (((val) >> ((byte) * 8)) & 0xff)
+#define SET_BYTE(val, byte) \
+ (((val) & 0xff) << ((byte) * 8))
+
+/*
+ * Buffer size required for the largest SPI transfer (i.e., reading a
+ * frame)
+ */
+#define CAN_FRAME_MAX_DATA_LEN 8
+#define SPI_TRANSFER_BUF_LEN (6 + CAN_FRAME_MAX_DATA_LEN)
+#define CAN_FRAME_MAX_BITS 128
+
+#define TX_ECHO_SKB_MAX 1
+
+#define DEVICE_NAME "mcp251x"
+
+static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
+module_param(mcp251x_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+
+static struct can_bittiming_const mcp251x_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 3,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mcp251x_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+
+ struct mutex spi_lock; /* SPI buffer lock */
+ u8 *spi_tx_buf;
+ u8 *spi_rx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+
+ struct sk_buff *tx_skb;
+ int tx_len;
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct completion awake;
+ int wake;
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+#define AFTER_SUSPEND_RESTART 8
+ int restart_tx;
+};
+
+static void mcp251x_clean(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ net->stats.tx_errors++;
+ if (priv->tx_skb)
+ dev_kfree_skb(priv->tx_skb);
+ if (priv->tx_len)
+ can_free_echo_skb(priv->net, 0);
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+}
+
+/*
+ * Note about handling of error return of mcp251x_spi_trans: accessing
+ * registers via SPI is not really different conceptually than using
+ * normal I/O assembler instructions, although it's much more
+ * complicated from a practical POV. So it's not advisable to always
+ * check the return value of this function. Imagine that every
+ * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0)
+ * error();", it would be a great mess (well there are some situation
+ * when exception handling C++ like could be useful after all). So we
+ * just check that transfers are OK at the beginning of our
+ * conversation with the chip and to avoid doing really nasty things
+ * (like injecting bogus packets in the network stack).
+ */
+static int mcp251x_spi_trans(struct spi_device *spi, int len)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = len,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if (ret)
+ dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret);
+ return ret;
+}
+
+static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ u8 val = 0;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_READ;
+ priv->spi_tx_buf[1] = reg;
+
+ mcp251x_spi_trans(spi, 3);
+ val = priv->spi_rx_buf[2];
+
+ mutex_unlock(&priv->spi_lock);
+
+ return val;
+}
+
+static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_WRITE;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = val;
+
+ mcp251x_spi_trans(spi, 3);
+
+ mutex_unlock(&priv->spi_lock);
+}
+
+static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
+ u8 mask, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = mask;
+ priv->spi_tx_buf[3] = val;
+
+ mcp251x_spi_trans(spi, 4);
+
+ mutex_unlock(&priv->spi_lock);
+}
+
+static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
+ int len, int tx_buf_idx)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+
+ for (i = 1; i < TXBDAT_OFF + len; i++)
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + i,
+ buf[i]);
+ } else {
+ mutex_lock(&priv->spi_lock);
+ memcpy(priv->spi_tx_buf, buf, TXBDAT_OFF + len);
+ mcp251x_spi_trans(spi, TXBDAT_OFF + len);
+ mutex_unlock(&priv->spi_lock);
+ }
+}
+
+static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
+ int tx_buf_idx)
+{
+ u32 sid, eid, exide, rtr;
+ u8 buf[SPI_TRANSFER_BUF_LEN];
+
+ exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */
+ if (exide)
+ sid = (frame->can_id & CAN_EFF_MASK) >> 18;
+ else
+ sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */
+ eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */
+ rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */
+
+ buf[TXBCTRL_OFF] = INSTRUCTION_LOAD_TXB(tx_buf_idx);
+ buf[TXBSIDH_OFF] = sid >> SIDH_SHIFT;
+ buf[TXBSIDL_OFF] = ((sid & SIDL_SID_MASK) << SIDL_SID_SHIFT) |
+ (exide << SIDL_EXIDE_SHIFT) |
+ ((eid >> SIDL_EID_SHIFT) & SIDL_EID_MASK);
+ buf[TXBEID8_OFF] = GET_BYTE(eid, 1);
+ buf[TXBEID0_OFF] = GET_BYTE(eid, 0);
+ buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->can_dlc;
+ memcpy(buf + TXBDAT_OFF, frame->data, frame->can_dlc);
+ mcp251x_hw_tx_frame(spi, buf, frame->can_dlc, tx_buf_idx);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+}
+
+static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
+ int buf_idx)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i, len;
+
+ for (i = 1; i < RXBDAT_OFF; i++)
+ buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);
+ len = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK;
+ if (len > 8)
+ len = 8;
+ for (; i < (RXBDAT_OFF + len); i++)
+ buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);
+ } else {
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[RXBCTRL_OFF] = INSTRUCTION_READ_RXB(buf_idx);
+ mcp251x_spi_trans(spi, SPI_TRANSFER_BUF_LEN);
+ memcpy(buf, priv->spi_rx_buf, SPI_TRANSFER_BUF_LEN);
+
+ mutex_unlock(&priv->spi_lock);
+ }
+}
+
+static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u8 buf[SPI_TRANSFER_BUF_LEN];
+
+ skb = alloc_can_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+
+ mcp251x_hw_rx_frame(spi, buf, buf_idx);
+ if (buf[RXBSIDL_OFF] & RXBSIDL_IDE) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |=
+ /* Extended ID part */
+ SET_BYTE(buf[RXBSIDL_OFF] & RXBSIDL_EID, 2) |
+ SET_BYTE(buf[RXBEID8_OFF], 1) |
+ SET_BYTE(buf[RXBEID0_OFF], 0) |
+ /* Standard ID part */
+ (((buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) |
+ (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT)) << 18);
+ /* Remote transmission request */
+ if (buf[RXBDLC_OFF] & RXBDLC_RTR)
+ frame->can_id |= CAN_RTR_FLAG;
+ } else {
+ /* Standard ID format */
+ frame->can_id =
+ (buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) |
+ (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT);
+ }
+ /* Data length */
+ frame->can_dlc = buf[RXBDLC_OFF] & RXBDLC_LEN_MASK;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+ memcpy(frame->data, buf + RXBDAT_OFF, frame->can_dlc);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+ netif_rx(skb);
+}
+
+static void mcp251x_hw_sleep(struct spi_device *spi)
+{
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP);
+}
+
+static void mcp251x_hw_wakeup(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ priv->wake = 1;
+
+ /* Can only wake up by generating a wake-up interrupt. */
+ mcp251x_write_bits(spi, CANINTE, CANINTE_WAKIE, CANINTE_WAKIE);
+ mcp251x_write_bits(spi, CANINTF, CANINTF_WAKIF, CANINTF_WAKIF);
+
+ /* Wait until the device is awake */
+ if (!wait_for_completion_timeout(&priv->awake, HZ))
+ dev_err(&spi->dev, "MCP251x didn't wake-up\n");
+}
+
+static netdev_tx_t mcp251x_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ if (priv->tx_skb || priv->tx_len) {
+ dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
+ netif_stop_queue(net);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb->len != sizeof(struct can_frame)) {
+ dev_err(&spi->dev, "dropping packet - bad length\n");
+ dev_kfree_skb(skb);
+ net->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ net->trans_start = jiffies;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ /* We have to delay work since SPI I/O may sleep */
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ priv->restart_tx = 1;
+ if (priv->can.restart_ms == 0)
+ priv->after_suspend = AFTER_SUSPEND_RESTART;
+ queue_work(priv->wq, &priv->irq_work);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void mcp251x_set_normal_mode(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ unsigned long timeout;
+
+ /* Enable interrupts */
+ mcp251x_write_reg(spi, CANINTE,
+ CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE |
+ CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE |
+ CANINTF_MERRF);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK);
+ } else {
+ /* Put device into normal mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL);
+
+ /* Wait for the device to enter normal mode */
+ timeout = jiffies + HZ;
+ while (mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) {
+ schedule();
+ if (time_after(jiffies, timeout)) {
+ dev_err(&spi->dev, "MCP251x didn't"
+ " enter in normal mode\n");
+ return;
+ }
+ }
+ }
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+static int mcp251x_do_set_bittiming(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+
+ mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << CNF1_SJW_SHIFT) |
+ (bt->brp - 1));
+ mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE |
+ (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES ?
+ CNF2_SAM : 0) |
+ ((bt->phase_seg1 - 1) << CNF2_PS1_SHIFT) |
+ (bt->prop_seg - 1));
+ mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK,
+ (bt->phase_seg2 - 1));
+ dev_info(&spi->dev, "CNF: 0x%02x 0x%02x 0x%02x\n",
+ mcp251x_read_reg(spi, CNF1),
+ mcp251x_read_reg(spi, CNF2),
+ mcp251x_read_reg(spi, CNF3));
+
+ return 0;
+}
+
+static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
+ struct spi_device *spi)
+{
+ mcp251x_do_set_bittiming(net);
+
+ /* Enable RX0->RX1 buffer roll over and disable filters */
+ mcp251x_write_bits(spi, RXBCTRL(0),
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1);
+ mcp251x_write_bits(spi, RXBCTRL(1),
+ RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_RXM0 | RXBCTRL_RXM1);
+ return 0;
+}
+
+static void mcp251x_hw_reset(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_RESET;
+
+ ret = spi_write(spi, priv->spi_tx_buf, 1);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret)
+ dev_err(&spi->dev, "reset failed: ret = %d\n", ret);
+ /* Wait for reset to finish */
+ mdelay(10);
+}
+
+static int mcp251x_hw_probe(struct spi_device *spi)
+{
+ int st1, st2;
+
+ mcp251x_hw_reset(spi);
+
+ /*
+ * Please note that these are "magic values" based on after
+ * reset defaults taken from data sheet which allows us to see
+ * if we really have a chip on the bus (we avoid common all
+ * zeroes or all ones situations)
+ */
+ st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
+ st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
+
+ dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2);
+
+ /* Check for power up default values */
+ return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
+}
+
+static irqreturn_t mcp251x_can_isr(int irq, void *dev_id)
+{
+ struct net_device *net = (struct net_device *)dev_id;
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ /* Schedule bottom half */
+ if (!work_pending(&priv->irq_work))
+ queue_work(priv->wq, &priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static int mcp251x_open(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ int ret;
+
+ ret = open_candev(net);
+ if (ret) {
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ return ret;
+ }
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ ret = request_irq(spi->irq, mcp251x_can_isr,
+ IRQF_TRIGGER_FALLING, DEVICE_NAME, net);
+ if (ret) {
+ dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+ close_candev(net);
+ return ret;
+ }
+
+ mcp251x_hw_wakeup(spi);
+ mcp251x_hw_reset(spi);
+ ret = mcp251x_setup(net, priv, spi);
+ if (ret) {
+ free_irq(spi->irq, net);
+ mcp251x_hw_sleep(spi);
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+ close_candev(net);
+ return ret;
+ }
+ mcp251x_set_normal_mode(spi);
+ netif_wake_queue(net);
+
+ return 0;
+}
+
+static int mcp251x_stop(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ close_candev(net);
+
+ /* Disable and clear pending interrupts */
+ mcp251x_write_reg(spi, CANINTE, 0x00);
+ mcp251x_write_reg(spi, CANINTF, 0x00);
+
+ priv->force_quit = 1;
+ free_irq(spi->irq, net);
+ flush_workqueue(priv->wq);
+
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb || priv->tx_len)
+ mcp251x_clean(net);
+
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ return 0;
+}
+
+static void mcp251x_tx_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ tx_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ struct can_frame *frame;
+
+ if (priv->tx_skb) {
+ frame = (struct can_frame *)priv->tx_skb->data;
+
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ mcp251x_clean(net);
+ netif_wake_queue(net);
+ return;
+ }
+ if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN)
+ frame->can_dlc = CAN_FRAME_MAX_DATA_LEN;
+ mcp251x_hw_tx(spi, frame, 0);
+ priv->tx_len = 1 + frame->can_dlc;
+ can_put_echo_skb(priv->tx_skb, net, 0);
+ priv->tx_skb = NULL;
+ }
+}
+
+static void mcp251x_irq_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ irq_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ u8 txbnctrl;
+ u8 intf;
+ enum can_state new_state;
+
+ if (priv->after_suspend) {
+ mdelay(10);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
+ mcp251x_set_normal_mode(spi);
+ } else if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ /* Clean since we lost tx buffer */
+ if (priv->tx_skb || priv->tx_len) {
+ mcp251x_clean(net);
+ netif_wake_queue(net);
+ }
+ mcp251x_set_normal_mode(spi);
+ } else {
+ mcp251x_hw_sleep(spi);
+ }
+ priv->after_suspend = 0;
+ }
+
+ if (priv->can.restart_ms == 0 && priv->can.state == CAN_STATE_BUS_OFF)
+ return;
+
+ while (!priv->force_quit && !freezing(current)) {
+ u8 eflag = mcp251x_read_reg(spi, EFLG);
+ int can_id = 0, data1 = 0;
+
+ mcp251x_write_reg(spi, EFLG, 0x00);
+
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb || priv->tx_len)
+ mcp251x_clean(net);
+ netif_wake_queue(net);
+ can_id |= CAN_ERR_RESTARTED;
+ }
+
+ if (priv->wake) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ priv->wake = 0;
+ }
+
+ intf = mcp251x_read_reg(spi, CANINTF);
+ mcp251x_write_bits(spi, CANINTF, intf, 0x00);
+
+ /* Update can state */
+ if (eflag & EFLG_TXBO) {
+ new_state = CAN_STATE_BUS_OFF;
+ can_id |= CAN_ERR_BUSOFF;
+ } else if (eflag & EFLG_TXEP) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (eflag & EFLG_RXEP) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_RX_PASSIVE;
+ } else if (eflag & EFLG_TXWAR) {
+ new_state = CAN_STATE_ERROR_WARNING;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_TX_WARNING;
+ } else if (eflag & EFLG_RXWAR) {
+ new_state = CAN_STATE_ERROR_WARNING;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_RX_WARNING;
+ } else {
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ /* Update can state statistics */
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (new_state >= CAN_STATE_ERROR_WARNING &&
+ new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_warning++;
+ case CAN_STATE_ERROR_WARNING: /* fallthrough */
+ if (new_state >= CAN_STATE_ERROR_PASSIVE &&
+ new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_passive++;
+ break;
+ default:
+ break;
+ }
+ priv->can.state = new_state;
+
+ if ((intf & CANINTF_ERRIF) || (can_id & CAN_ERR_RESTARTED)) {
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ /* Create error frame */
+ skb = alloc_can_err_skb(net, &frame);
+ if (skb) {
+ /* Set error frame flags based on bus state */
+ frame->can_id = can_id;
+ frame->data[1] = data1;
+
+ /* Update net stats for overflows */
+ if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) {
+ if (eflag & EFLG_RX0OVR)
+ net->stats.rx_over_errors++;
+ if (eflag & EFLG_RX1OVR)
+ net->stats.rx_over_errors++;
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+
+ netif_rx(skb);
+ } else {
+ dev_info(&spi->dev,
+ "cannot allocate error skb\n");
+ }
+ }
+
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ if (priv->can.restart_ms == 0) {
+ can_bus_off(net);
+ mcp251x_hw_sleep(spi);
+ return;
+ }
+ }
+
+ if (intf == 0)
+ break;
+
+ if (intf & CANINTF_WAKIF)
+ complete(&priv->awake);
+
+ if (intf & CANINTF_MERRF) {
+ /* If there are pending Tx buffers, restart queue */
+ txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0));
+ if (!(txbnctrl & TXBCTRL_TXREQ)) {
+ if (priv->tx_skb || priv->tx_len)
+ mcp251x_clean(net);
+ netif_wake_queue(net);
+ }
+ }
+
+ if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += priv->tx_len - 1;
+ if (priv->tx_len) {
+ can_get_echo_skb(net, 0);
+ priv->tx_len = 0;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (intf & CANINTF_RX0IF)
+ mcp251x_hw_rx(spi, 0);
+
+ if (intf & CANINTF_RX1IF)
+ mcp251x_hw_rx(spi, 1);
+ }
+}
+
+static const struct net_device_ops mcp251x_netdev_ops = {
+ .ndo_open = mcp251x_open,
+ .ndo_stop = mcp251x_stop,
+ .ndo_start_xmit = mcp251x_hard_start_xmit,
+};
+
+static int __devinit mcp251x_can_probe(struct spi_device *spi)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ int ret = -ENODEV;
+
+ if (!pdata)
+ /* Platform data is required for osc freq */
+ goto error_out;
+
+ /* Allocate can/net device */
+ net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX);
+ if (!net) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ net->netdev_ops = &mcp251x_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ priv = netdev_priv(net);
+ priv->can.bittiming_const = &mcp251x_bittiming_const;
+ priv->can.do_set_mode = mcp251x_do_set_mode;
+ priv->can.clock.freq = pdata->oscillator_frequency / 2;
+ priv->net = net;
+ dev_set_drvdata(&spi->dev, priv);
+
+ priv->spi = spi;
+ mutex_init(&priv->spi_lock);
+
+ /* If requested, allocate DMA buffers */
+ if (mcp251x_enable_dma) {
+ spi->dev.coherent_dma_mask = ~0;
+
+ /*
+ * Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ * that much and share it between Tx and Rx DMA buffers.
+ */
+ priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+ PAGE_SIZE,
+ &priv->spi_tx_dma,
+ GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf +
+ (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ mcp251x_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!mcp251x_enable_dma) {
+ priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_tx_buf;
+ }
+ priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_rx_buf;
+ }
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(1);
+
+ /* Call out to platform specific setup */
+ if (pdata->board_specific_setup)
+ pdata->board_specific_setup(spi);
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ priv->wq = create_freezeable_workqueue("mcp251x_wq");
+
+ INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
+ INIT_WORK(&priv->irq_work, mcp251x_irq_work_handler);
+
+ init_completion(&priv->awake);
+
+ /* Configure the SPI bus */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ if (!mcp251x_hw_probe(spi)) {
+ dev_info(&spi->dev, "Probe failed\n");
+ goto error_probe;
+ }
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ ret = register_candev(net);
+ if (!ret) {
+ dev_info(&spi->dev, "probed\n");
+ return ret;
+ }
+error_probe:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_rx_buf);
+error_rx_buf:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_tx_buf);
+error_tx_buf:
+ free_candev(net);
+ if (mcp251x_enable_dma)
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+error_alloc:
+ if (pdata->power_enable)
+ pdata->power_enable(0);
+ dev_err(&spi->dev, "probe failed\n");
+error_out:
+ return ret;
+}
+
+static int __devexit mcp251x_can_remove(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ unregister_candev(net);
+ free_candev(net);
+
+ priv->force_quit = 1;
+ flush_workqueue(priv->wq);
+ destroy_workqueue(priv->wq);
+
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ } else {
+ kfree(priv->spi_tx_buf);
+ kfree(priv->spi_rx_buf);
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp251x_hw_sleep(spi);
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else {
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+ }
+
+ if (pdata->power_enable) {
+ pdata->power_enable(0);
+ priv->after_suspend |= AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int mcp251x_can_resume(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER) {
+ pdata->power_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else {
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else {
+ priv->after_suspend = 0;
+ }
+ }
+ return 0;
+}
+#else
+#define mcp251x_can_suspend NULL
+#define mcp251x_can_resume NULL
+#endif
+
+static struct spi_driver mcp251x_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = mcp251x_can_probe,
+ .remove = __devexit_p(mcp251x_can_remove),
+ .suspend = mcp251x_can_suspend,
+ .resume = mcp251x_can_resume,
+};
+
+static int __init mcp251x_can_init(void)
+{
+ return spi_register_driver(&mcp251x_can_driver);
+}
+
+static void __exit mcp251x_can_exit(void)
+{
+ spi_unregister_driver(&mcp251x_can_driver);
+}
+
+module_init(mcp251x_can_init);
+module_exit(mcp251x_can_exit);
+
+MODULE_AUTHOR("Chris Elston <celston@katalix.com>, "
+ "Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("Microchip 251x CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/mscan/Kconfig b/drivers/net/can/mscan/Kconfig
new file mode 100644
index 00000000000..cd0f2d6f375
--- /dev/null
+++ b/drivers/net/can/mscan/Kconfig
@@ -0,0 +1,23 @@
+config CAN_MSCAN
+ depends on CAN_DEV && (PPC || M68K || M68KNOMMU)
+ tristate "Support for Freescale MSCAN based chips"
+ ---help---
+ The Motorola Scalable Controller Area Network (MSCAN) definition
+ is based on the MSCAN12 definition which is the specific
+ implementation of the Motorola Scalable CAN concept targeted for
+ the Motorola MC68HC12 Microcontroller Family.
+
+if CAN_MSCAN
+
+config CAN_MPC5XXX
+ tristate "Freescale MPC5xxx onboard CAN controller"
+ depends on PPC_MPC52xx
+ ---help---
+ If you say yes here you get support for Freescale's MPC5xxx
+ onboard CAN controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called mscan-mpc5xxx.ko.
+
+endif
+
diff --git a/drivers/net/can/mscan/Makefile b/drivers/net/can/mscan/Makefile
new file mode 100644
index 00000000000..c9fab17cd8b
--- /dev/null
+++ b/drivers/net/can/mscan/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_CAN_MPC5XXX) += mscan-mpc5xxx.o
+mscan-mpc5xxx-objs := mscan.o mpc5xxx_can.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c
new file mode 100644
index 00000000000..1de6f6349b1
--- /dev/null
+++ b/drivers/net/can/mscan/mpc5xxx_can.c
@@ -0,0 +1,259 @@
+/*
+ * CAN bus driver for the Freescale MPC5xxx embedded CPU.
+ *
+ * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix <w.sang@pengutronix.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
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/of_platform.h>
+#include <sysdev/fsl_soc.h>
+#include <linux/io.h>
+#include <asm/mpc52xx.h>
+
+#include "mscan.h"
+
+#define DRV_NAME "mpc5xxx_can"
+
+static struct of_device_id mpc52xx_cdm_ids[] __devinitdata = {
+ { .compatible = "fsl,mpc5200-cdm", },
+ {}
+};
+
+/*
+ * Get frequency of the MSCAN clock source
+ *
+ * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock (IP_CLK)
+ * can be selected. According to the MPC5200 user's manual, the oscillator
+ * clock is the better choice as it has less jitter but due to a hardware
+ * bug, it can not be selected for the old MPC5200 Rev. A chips.
+ */
+
+static unsigned int __devinit mpc52xx_can_clock_freq(struct of_device *of,
+ int clock_src)
+{
+ unsigned int pvr;
+ struct mpc52xx_cdm __iomem *cdm;
+ struct device_node *np_cdm;
+ unsigned int freq;
+ u32 val;
+
+ pvr = mfspr(SPRN_PVR);
+
+ freq = mpc5xxx_get_bus_frequency(of->node);
+ if (!freq)
+ return 0;
+
+ if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011)
+ return freq;
+
+ /* Determine SYS_XTAL_IN frequency from the clock domain settings */
+ np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids);
+ if (!np_cdm) {
+ dev_err(&of->dev, "can't get clock node!\n");
+ return 0;
+ }
+ cdm = of_iomap(np_cdm, 0);
+ of_node_put(np_cdm);
+
+ if (in_8(&cdm->ipb_clk_sel) & 0x1)
+ freq *= 2;
+ val = in_be32(&cdm->rstcfg);
+
+ freq *= (val & (1 << 5)) ? 8 : 4;
+ freq /= (val & (1 << 6)) ? 12 : 16;
+
+ iounmap(cdm);
+
+ return freq;
+}
+
+static int __devinit mpc5xxx_can_probe(struct of_device *ofdev,
+ const struct of_device_id *id)
+{
+ struct device_node *np = ofdev->node;
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ void __iomem *base;
+ const char *clk_src;
+ int err, irq, clock_src;
+
+ base = of_iomap(ofdev->node, 0);
+ if (!base) {
+ dev_err(&ofdev->dev, "couldn't ioremap\n");
+ err = -ENOMEM;
+ goto exit_release_mem;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq) {
+ dev_err(&ofdev->dev, "no irq found\n");
+ err = -ENODEV;
+ goto exit_unmap_mem;
+ }
+
+ dev = alloc_mscandev();
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_dispose_irq;
+ }
+
+ priv = netdev_priv(dev);
+ priv->reg_base = base;
+ dev->irq = irq;
+
+ /*
+ * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock
+ * (IP_CLK) can be selected as MSCAN clock source. According to
+ * the MPC5200 user's manual, the oscillator clock is the better
+ * choice as it has less jitter. For this reason, it is selected
+ * by default.
+ */
+ clk_src = of_get_property(np, "fsl,mscan-clock-source", NULL);
+ if (clk_src && strcmp(clk_src, "ip") == 0)
+ clock_src = MSCAN_CLKSRC_BUS;
+ else
+ clock_src = MSCAN_CLKSRC_XTAL;
+ priv->can.clock.freq = mpc52xx_can_clock_freq(ofdev, clock_src);
+ if (!priv->can.clock.freq) {
+ dev_err(&ofdev->dev, "couldn't get MSCAN clock frequency\n");
+ err = -ENODEV;
+ goto exit_free_mscan;
+ }
+
+ SET_NETDEV_DEV(dev, &ofdev->dev);
+
+ err = register_mscandev(dev, clock_src);
+ if (err) {
+ dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free_mscan;
+ }
+
+ dev_set_drvdata(&ofdev->dev, dev);
+
+ dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n",
+ priv->reg_base, dev->irq, priv->can.clock.freq);
+
+ return 0;
+
+exit_free_mscan:
+ free_candev(dev);
+exit_dispose_irq:
+ irq_dispose_mapping(irq);
+exit_unmap_mem:
+ iounmap(base);
+exit_release_mem:
+ return err;
+}
+
+static int __devexit mpc5xxx_can_remove(struct of_device *ofdev)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct mscan_priv *priv = netdev_priv(dev);
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+
+ unregister_mscandev(dev);
+ iounmap(priv->reg_base);
+ irq_dispose_mapping(dev->irq);
+ free_candev(dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static struct mscan_regs saved_regs;
+static int mpc5xxx_can_suspend(struct of_device *ofdev, pm_message_t state)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+
+ _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+ return 0;
+}
+
+static int mpc5xxx_can_resume(struct of_device *ofdev)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+
+ regs->canctl0 |= MSCAN_INITRQ;
+ while (!(regs->canctl1 & MSCAN_INITAK))
+ udelay(10);
+
+ regs->canctl1 = saved_regs.canctl1;
+ regs->canbtr0 = saved_regs.canbtr0;
+ regs->canbtr1 = saved_regs.canbtr1;
+ regs->canidac = saved_regs.canidac;
+
+ /* restore masks, buffers etc. */
+ _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
+ sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+ regs->canctl0 &= ~MSCAN_INITRQ;
+ regs->cantbsel = saved_regs.cantbsel;
+ regs->canrier = saved_regs.canrier;
+ regs->cantier = saved_regs.cantier;
+ regs->canctl0 = saved_regs.canctl0;
+
+ return 0;
+}
+#endif
+
+static struct of_device_id __devinitdata mpc5xxx_can_table[] = {
+ {.compatible = "fsl,mpc5200-mscan"},
+ {},
+};
+
+static struct of_platform_driver mpc5xxx_can_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc5xxx_can",
+ .probe = mpc5xxx_can_probe,
+ .remove = __devexit_p(mpc5xxx_can_remove),
+#ifdef CONFIG_PM
+ .suspend = mpc5xxx_can_suspend,
+ .resume = mpc5xxx_can_resume,
+#endif
+ .match_table = mpc5xxx_can_table,
+};
+
+static int __init mpc5xxx_can_init(void)
+{
+ return of_register_platform_driver(&mpc5xxx_can_driver);
+}
+module_init(mpc5xxx_can_init);
+
+static void __exit mpc5xxx_can_exit(void)
+{
+ return of_unregister_platform_driver(&mpc5xxx_can_driver);
+};
+module_exit(mpc5xxx_can_exit);
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c
new file mode 100644
index 00000000000..bb06dfb58f2
--- /dev/null
+++ b/drivers/net/can/mscan/mscan.c
@@ -0,0 +1,668 @@
+/*
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ * Copytight (C) 2008-2009 Pengutronix <kernel@pengutronix.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
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/list.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/io.h>
+
+#include "mscan.h"
+
+static struct can_bittiming_const mscan_bittiming_const = {
+ .name = "mscan",
+ .tseg1_min = 4,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mscan_state {
+ u8 mode;
+ u8 canrier;
+ u8 cantier;
+};
+
+static enum can_state state_map[] = {
+ CAN_STATE_ERROR_ACTIVE,
+ CAN_STATE_ERROR_WARNING,
+ CAN_STATE_ERROR_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ int ret = 0;
+ int i;
+ u8 canctl1;
+
+ if (mode != MSCAN_NORMAL_MODE) {
+ if (priv->tx_active) {
+ /* Abort transfers before going to sleep */#
+ out_8(&regs->cantarq, priv->tx_active);
+ /* Suppress TX done interrupts */
+ out_8(&regs->cantier, 0);
+ }
+
+ canctl1 = in_8(&regs->canctl1);
+ if ((mode & MSCAN_SLPRQ) && !(canctl1 & MSCAN_SLPAK)) {
+ setbits8(&regs->canctl0, MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_SLPAK)
+ break;
+ udelay(100);
+ }
+ /*
+ * The mscan controller will fail to enter sleep mode,
+ * while there are irregular activities on bus, like
+ * somebody keeps retransmitting. This behavior is
+ * undocumented and seems to differ between mscan built
+ * in mpc5200b and mpc5200. We proceed in that case,
+ * since otherwise the slprq will be kept set and the
+ * controller will get stuck. NOTE: INITRQ or CSWAI
+ * will abort all active transmit actions, if still
+ * any, at once.
+ */
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ dev_dbg(dev->dev.parent,
+ "device failed to enter sleep mode. "
+ "We proceed anyhow.\n");
+ else
+ priv->can.state = CAN_STATE_SLEEPING;
+ }
+
+ if ((mode & MSCAN_INITRQ) && !(canctl1 & MSCAN_INITAK)) {
+ setbits8(&regs->canctl0, MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_INITAK)
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ if (!ret)
+ priv->can.state = CAN_STATE_STOPPED;
+
+ if (mode & MSCAN_CSWAI)
+ setbits8(&regs->canctl0, MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(&regs->canctl1);
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+ clrbits8(&regs->canctl0, MSCAN_SLPRQ | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ canctl1 = in_8(&regs->canctl1);
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ else
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+ return ret;
+}
+
+static int mscan_start(struct net_device *dev)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ u8 canrflg;
+ int err;
+
+ out_8(&regs->canrier, 0);
+
+ INIT_LIST_HEAD(&priv->tx_head);
+ priv->prev_buf_id = 0;
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+ priv->shadow_canrier = 0;
+ priv->flags = 0;
+
+ err = mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ if (err)
+ return err;
+
+ canrflg = in_8(&regs->canrflg);
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ out_8(&regs->cantier, 0);
+
+ /* Enable receive interrupts. */
+ out_8(&regs->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ return 0;
+}
+
+static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ int i, rtr, buf_id;
+ u32 can_id;
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ out_8(&regs->cantier, 0);
+
+ i = ~priv->tx_active & MSCAN_TXE;
+ buf_id = ffs(i) - 1;
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(dev->dev.parent, "Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /*
+ * if buf_id < 3, then current frame will be send out of order,
+ * since buffer with lower id have higher priority (hell..)
+ */
+ netif_stop_queue(dev);
+ case 2:
+ if (buf_id < priv->prev_buf_id) {
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff) {
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(dev);
+ }
+ }
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ break;
+ }
+ priv->prev_buf_id = buf_id;
+ out_8(&regs->cantbsel, i);
+
+ rtr = frame->can_id & CAN_RTR_FLAG;
+
+ /* RTR is always the lowest bit of interest, then IDs follow */
+ if (frame->can_id & CAN_EFF_FLAG) {
+ can_id = (frame->can_id & CAN_EFF_MASK)
+ << (MSCAN_EFF_RTR_SHIFT + 1);
+ if (rtr)
+ can_id |= 1 << MSCAN_EFF_RTR_SHIFT;
+ out_be16(&regs->tx.idr3_2, can_id);
+
+ can_id >>= 16;
+ /* EFF_FLAGS are inbetween the IDs :( */
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0)
+ | MSCAN_EFF_FLAGS;
+ } else {
+ can_id = (frame->can_id & CAN_SFF_MASK)
+ << (MSCAN_SFF_RTR_SHIFT + 1);
+ if (rtr)
+ can_id |= 1 << MSCAN_SFF_RTR_SHIFT;
+ }
+ out_be16(&regs->tx.idr1_0, can_id);
+
+ if (!rtr) {
+ void __iomem *data = &regs->tx.dsr1_0;
+ u16 *payload = (u16 *)frame->data;
+
+ /* It is safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ out_be16(data, *payload++);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->tx.dlr, frame->can_dlc);
+ out_8(&regs->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(&regs->cantflg, 1 << buf_id);
+
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
+
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
+
+ can_put_echo_skb(skb, dev, buf_id);
+
+ /* Enable interrupt. */
+ priv->tx_active |= 1 << buf_id;
+ out_8(&regs->cantier, priv->tx_active);
+
+ return NETDEV_TX_OK;
+}
+
+/* This function returns the old state to see where we came from */
+static enum can_state check_set_state(struct net_device *dev, u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ enum can_state state, old_state = priv->can.state;
+
+ if (canrflg & MSCAN_CSCIF && old_state <= CAN_STATE_BUS_OFF) {
+ state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ priv->can.state = state;
+ }
+ return old_state;
+}
+
+static void mscan_get_rx_frame(struct net_device *dev, struct can_frame *frame)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ u32 can_id;
+ int i;
+
+ can_id = in_be16(&regs->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) | in_be16(&regs->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
+ can_id >>= 4;
+ frame->can_id = 0;
+ }
+
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = in_8(&regs->rx.dlr) & 0xf;
+
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ void __iomem *data = &regs->rx.dsr1_0;
+ u16 *payload = (u16 *)frame->data;
+
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ *payload++ = in_be16(data);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->canrflg, MSCAN_RXF);
+}
+
+static void mscan_get_err_frame(struct net_device *dev, struct can_frame *frame,
+ u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct net_device_stats *stats = &dev->stats;
+ enum can_state old_state;
+
+ dev_dbg(dev->dev.parent, "error interrupt (canrflg=%#x)\n", canrflg);
+ frame->can_id = CAN_ERR_FLAG;
+
+ if (canrflg & MSCAN_OVRIF) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ } else {
+ frame->data[1] = 0;
+ }
+
+ old_state = check_set_state(dev, canrflg);
+ /* State changed */
+ if (old_state != priv->can.state) {
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_WARNING:
+ frame->can_id |= CAN_ERR_CRTL;
+ priv->can.can_stats.error_warning++;
+ if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) <
+ (canrflg & MSCAN_RSTAT_MSK))
+ frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) <
+ (canrflg & MSCAN_TSTAT_MSK))
+ frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ break;
+ case CAN_STATE_ERROR_PASSIVE:
+ frame->can_id |= CAN_ERR_CRTL;
+ priv->can.can_stats.error_passive++;
+ frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ case CAN_STATE_BUS_OFF:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ /*
+ * The MSCAN on the MPC5200 does recover from bus-off
+ * automatically. To avoid that we stop the chip doing
+ * a light-weight stop (we are in irq-context).
+ */
+ out_8(&regs->cantier, 0);
+ out_8(&regs->canrier, 0);
+ setbits8(&regs->canctl0, MSCAN_SLPRQ | MSCAN_INITRQ);
+ can_bus_off(dev);
+ break;
+ default:
+ break;
+ }
+ }
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ frame->can_dlc = CAN_ERR_DLC;
+ out_8(&regs->canrflg, MSCAN_ERR_IF);
+}
+
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = napi->dev;
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct net_device_stats *stats = &dev->stats;
+ int npackets = 0;
+ int ret = 1;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u8 canrflg;
+
+ while (npackets < quota) {
+ canrflg = in_8(&regs->canrflg);
+ if (!(canrflg & (MSCAN_RXF | MSCAN_ERR_IF)))
+ break;
+
+ skb = alloc_can_skb(dev, &frame);
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(dev->dev.parent, "packet dropped\n");
+ stats->rx_dropped++;
+ out_8(&regs->canrflg, canrflg);
+ continue;
+ }
+
+ if (canrflg & MSCAN_RXF)
+ mscan_get_rx_frame(dev, frame);
+ else if (canrflg & MSCAN_ERR_IF)
+ mscan_get_err_frame(dev, frame, canrflg);
+
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ npackets++;
+ netif_receive_skb(skb);
+ }
+
+ if (!(in_8(&regs->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+ napi_complete(&priv->napi);
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ if (priv->can.state < CAN_STATE_BUS_OFF)
+ out_8(&regs->canrier, priv->shadow_canrier);
+ ret = 0;
+ }
+ return ret;
+}
+
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct net_device_stats *stats = &dev->stats;
+ u8 cantier, cantflg, canrflg;
+ irqreturn_t ret = IRQ_NONE;
+
+ cantier = in_8(&regs->cantier) & MSCAN_TXE;
+ cantflg = in_8(&regs->cantflg) & cantier;
+
+ if (cantier && cantflg) {
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ struct tx_queue_entry *entry =
+ list_entry(pos, struct tx_queue_entry, list);
+ u8 mask = entry->mask;
+
+ if (!(cantflg & mask))
+ continue;
+
+ out_8(&regs->cantbsel, mask);
+ stats->tx_bytes += in_8(&regs->tx.dlr);
+ stats->tx_packets++;
+ can_get_echo_skb(dev, entry->id);
+ priv->tx_active &= ~mask;
+ list_del(pos);
+ }
+
+ if (list_empty(&priv->tx_head)) {
+ clear_bit(F_TX_WAIT_ALL, &priv->flags);
+ clear_bit(F_TX_PROGRESS, &priv->flags);
+ priv->cur_pri = 0;
+ } else {
+ dev->trans_start = jiffies;
+ }
+
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
+
+ out_8(&regs->cantier, priv->tx_active);
+ ret = IRQ_HANDLED;
+ }
+
+ canrflg = in_8(&regs->canrflg);
+ if ((canrflg & ~MSCAN_STAT_MSK) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(&regs->canrier);
+ out_8(&regs->canrier, 0);
+ napi_schedule(&priv->napi);
+ ret = IRQ_HANDLED;
+ } else {
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ }
+ }
+ return ret;
+}
+
+static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ if (priv->can.state <= CAN_STATE_BUS_OFF)
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ ret = mscan_start(dev);
+ if (ret)
+ break;
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int mscan_do_set_bittiming(struct net_device *dev)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw);
+ btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) |
+ BTR1_SET_TSEG2(bt->phase_seg2) |
+ BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES));
+
+ dev_info(dev->dev.parent, "setting BTR0=0x%02x BTR1=0x%02x\n",
+ btr0, btr1);
+
+ out_8(&regs->canbtr0, btr0);
+ out_8(&regs->canbtr1, btr1);
+
+ return 0;
+}
+
+static int mscan_open(struct net_device *dev)
+{
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+
+ /* common open */
+ ret = open_candev(dev);
+ if (ret)
+ return ret;
+
+ napi_enable(&priv->napi);
+
+ ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev);
+ if (ret < 0) {
+ dev_err(dev->dev.parent, "failed to attach interrupt\n");
+ goto exit_napi_disable;
+ }
+
+ priv->open_time = jiffies;
+
+ clrbits8(&regs->canctl1, MSCAN_LISTEN);
+
+ ret = mscan_start(dev);
+ if (ret)
+ goto exit_free_irq;
+
+ netif_start_queue(dev);
+
+ return 0;
+
+exit_free_irq:
+ priv->open_time = 0;
+ free_irq(dev->irq, dev);
+exit_napi_disable:
+ napi_disable(&priv->napi);
+ close_candev(dev);
+ return ret;
+}
+
+static int mscan_close(struct net_device *dev)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+
+ netif_stop_queue(dev);
+ napi_disable(&priv->napi);
+
+ out_8(&regs->cantier, 0);
+ out_8(&regs->canrier, 0);
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ close_candev(dev);
+ free_irq(dev->irq, dev);
+ priv->open_time = 0;
+
+ return 0;
+}
+
+static const struct net_device_ops mscan_netdev_ops = {
+ .ndo_open = mscan_open,
+ .ndo_stop = mscan_close,
+ .ndo_start_xmit = mscan_start_xmit,
+};
+
+int register_mscandev(struct net_device *dev, int clock_src)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ u8 ctl1;
+
+ ctl1 = in_8(&regs->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(&regs->canctl1, ctl1);
+ udelay(100);
+
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(&regs->canidar1_0, 0);
+ out_be16(&regs->canidar3_2, 0);
+ out_be16(&regs->canidar5_4, 0);
+ out_be16(&regs->canidar7_6, 0);
+
+ out_be16(&regs->canidmr1_0, 0xffff);
+ out_be16(&regs->canidmr3_2, 0xffff);
+ out_be16(&regs->canidmr5_4, 0xffff);
+ out_be16(&regs->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(&regs->canidac, MSCAN_AF_32BIT);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+
+ return register_candev(dev);
+}
+
+void unregister_mscandev(struct net_device *dev)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ clrbits8(&regs->canctl1, MSCAN_CANE);
+ unregister_candev(dev);
+}
+
+struct net_device *alloc_mscandev(void)
+{
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
+
+ dev = alloc_candev(sizeof(struct mscan_priv), MSCAN_ECHO_SKB_MAX);
+ if (!dev)
+ return NULL;
+ priv = netdev_priv(dev);
+
+ dev->netdev_ops = &mscan_netdev_ops;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+
+ priv->can.bittiming_const = &mscan_bittiming_const;
+ priv->can.do_set_bittiming = mscan_do_set_bittiming;
+ priv->can.do_set_mode = mscan_do_set_mode;
+
+ for (i = 0; i < TX_QUEUE_SIZE; i++) {
+ priv->tx_queue[i].id = i;
+ priv->tx_queue[i].mask = 1 << i;
+ }
+
+ return dev;
+}
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips");
diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h
new file mode 100644
index 00000000000..00fc4aaf1ed
--- /dev/null
+++ b/drivers/net/can/mscan/mscan.h
@@ -0,0 +1,296 @@
+/*
+ * Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ *
+ * 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
+ *
+ * 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
+ */
+
+#ifndef __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM 0x80
+#define MSCAN_RXACT 0x40
+#define MSCAN_CSWAI 0x20
+#define MSCAN_SYNCH 0x10
+#define MSCAN_TIME 0x08
+#define MSCAN_WUPE 0x04
+#define MSCAN_SLPRQ 0x02
+#define MSCAN_INITRQ 0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE 0x80
+#define MSCAN_CLKSRC 0x40
+#define MSCAN_LOOPB 0x20
+#define MSCAN_LISTEN 0x10
+#define MSCAN_WUPM 0x04
+#define MSCAN_SLPAK 0x02
+#define MSCAN_INITAK 0x01
+
+/* Use the MPC5200 MSCAN variant? */
+#ifdef CONFIG_PPC
+#define MSCAN_FOR_MPC5200
+#endif
+
+#ifdef MSCAN_FOR_MPC5200
+#define MSCAN_CLKSRC_BUS 0
+#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL 0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF 0x80
+#define MSCAN_CSCIF 0x40
+#define MSCAN_RSTAT1 0x20
+#define MSCAN_RSTAT0 0x10
+#define MSCAN_TSTAT1 0x08
+#define MSCAN_TSTAT0 0x04
+#define MSCAN_OVRIF 0x02
+#define MSCAN_RXF 0x01
+#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \
+ MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE 0
+#define MSCAN_STATE_WARNING 1
+#define MSCAN_STATE_PASSIVE 2
+#define MSCAN_STATE_BUSOFF 3
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE 0x80
+#define MSCAN_CSCIE 0x40
+#define MSCAN_RSTATE1 0x20
+#define MSCAN_RSTATE0 0x10
+#define MSCAN_TSTATE1 0x08
+#define MSCAN_TSTATE0 0x04
+#define MSCAN_OVRIE 0x02
+#define MSCAN_RXFIE 0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2 0x04
+#define MSCAN_TXE1 0x02
+#define MSCAN_TXE0 0x01
+#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2 0x04
+#define MSCAN_TXIE1 0x02
+#define MSCAN_TXIE0 0x01
+#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2 0x04
+#define MSCAN_ABTRQ1 0x02
+#define MSCAN_ABTRQ0 0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2 0x04
+#define MSCAN_ABTAK1 0x02
+#define MSCAN_ABTAK0 0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2 0x04
+#define MSCAN_TX1 0x02
+#define MSCAN_TX0 0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1 0x20
+#define MSCAN_IDAM0 0x10
+#define MSCAN_IDHIT2 0x04
+#define MSCAN_IDHIT1 0x02
+#define MSCAN_IDHIT0 0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT MSCAN_IDAM1
+#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD 0x01
+
+/* MSCAN Identifier Register (IDR) bits */
+#define MSCAN_SFF_RTR_SHIFT 4
+#define MSCAN_EFF_RTR_SHIFT 0
+#define MSCAN_EFF_FLAGS 0x18 /* IDE + SRR */
+
+#ifdef MSCAN_FOR_MPC5200
+#define _MSCAN_RESERVED_(n, num) u8 _res##n[num]
+#define _MSCAN_RESERVED_DSR_SIZE 2
+#else
+#define _MSCAN_RESERVED_(n, num)
+#define _MSCAN_RESERVED_DSR_SIZE 0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+ /* (see doc S12MSCANV3/D) MPC5200 MSCAN */
+ u8 canctl0; /* + 0x00 0x00 */
+ u8 canctl1; /* + 0x01 0x01 */
+ _MSCAN_RESERVED_(1, 2); /* + 0x02 */
+ u8 canbtr0; /* + 0x04 0x02 */
+ u8 canbtr1; /* + 0x05 0x03 */
+ _MSCAN_RESERVED_(2, 2); /* + 0x06 */
+ u8 canrflg; /* + 0x08 0x04 */
+ u8 canrier; /* + 0x09 0x05 */
+ _MSCAN_RESERVED_(3, 2); /* + 0x0a */
+ u8 cantflg; /* + 0x0c 0x06 */
+ u8 cantier; /* + 0x0d 0x07 */
+ _MSCAN_RESERVED_(4, 2); /* + 0x0e */
+ u8 cantarq; /* + 0x10 0x08 */
+ u8 cantaak; /* + 0x11 0x09 */
+ _MSCAN_RESERVED_(5, 2); /* + 0x12 */
+ u8 cantbsel; /* + 0x14 0x0a */
+ u8 canidac; /* + 0x15 0x0b */
+ u8 reserved; /* + 0x16 0x0c */
+ _MSCAN_RESERVED_(6, 5); /* + 0x17 */
+#ifndef MSCAN_FOR_MPC5200
+ u8 canmisc; /* 0x0d */
+#endif
+ u8 canrxerr; /* + 0x1c 0x0e */
+ u8 cantxerr; /* + 0x1d 0x0f */
+ _MSCAN_RESERVED_(7, 2); /* + 0x1e */
+ u16 canidar1_0; /* + 0x20 0x10 */
+ _MSCAN_RESERVED_(8, 2); /* + 0x22 */
+ u16 canidar3_2; /* + 0x24 0x12 */
+ _MSCAN_RESERVED_(9, 2); /* + 0x26 */
+ u16 canidmr1_0; /* + 0x28 0x14 */
+ _MSCAN_RESERVED_(10, 2); /* + 0x2a */
+ u16 canidmr3_2; /* + 0x2c 0x16 */
+ _MSCAN_RESERVED_(11, 2); /* + 0x2e */
+ u16 canidar5_4; /* + 0x30 0x18 */
+ _MSCAN_RESERVED_(12, 2); /* + 0x32 */
+ u16 canidar7_6; /* + 0x34 0x1a */
+ _MSCAN_RESERVED_(13, 2); /* + 0x36 */
+ u16 canidmr5_4; /* + 0x38 0x1c */
+ _MSCAN_RESERVED_(14, 2); /* + 0x3a */
+ u16 canidmr7_6; /* + 0x3c 0x1e */
+ _MSCAN_RESERVED_(15, 2); /* + 0x3e */
+ struct {
+ u16 idr1_0; /* + 0x40 0x20 */
+ _MSCAN_RESERVED_(16, 2); /* + 0x42 */
+ u16 idr3_2; /* + 0x44 0x22 */
+ _MSCAN_RESERVED_(17, 2); /* + 0x46 */
+ u16 dsr1_0; /* + 0x48 0x24 */
+ _MSCAN_RESERVED_(18, 2); /* + 0x4a */
+ u16 dsr3_2; /* + 0x4c 0x26 */
+ _MSCAN_RESERVED_(19, 2); /* + 0x4e */
+ u16 dsr5_4; /* + 0x50 0x28 */
+ _MSCAN_RESERVED_(20, 2); /* + 0x52 */
+ u16 dsr7_6; /* + 0x54 0x2a */
+ _MSCAN_RESERVED_(21, 2); /* + 0x56 */
+ u8 dlr; /* + 0x58 0x2c */
+ u8:8; /* + 0x59 0x2d */
+ _MSCAN_RESERVED_(22, 2); /* + 0x5a */
+ u16 time; /* + 0x5c 0x2e */
+ } rx;
+ _MSCAN_RESERVED_(23, 2); /* + 0x5e */
+ struct {
+ u16 idr1_0; /* + 0x60 0x30 */
+ _MSCAN_RESERVED_(24, 2); /* + 0x62 */
+ u16 idr3_2; /* + 0x64 0x32 */
+ _MSCAN_RESERVED_(25, 2); /* + 0x66 */
+ u16 dsr1_0; /* + 0x68 0x34 */
+ _MSCAN_RESERVED_(26, 2); /* + 0x6a */
+ u16 dsr3_2; /* + 0x6c 0x36 */
+ _MSCAN_RESERVED_(27, 2); /* + 0x6e */
+ u16 dsr5_4; /* + 0x70 0x38 */
+ _MSCAN_RESERVED_(28, 2); /* + 0x72 */
+ u16 dsr7_6; /* + 0x74 0x3a */
+ _MSCAN_RESERVED_(29, 2); /* + 0x76 */
+ u8 dlr; /* + 0x78 0x3c */
+ u8 tbpr; /* + 0x79 0x3d */
+ _MSCAN_RESERVED_(30, 2); /* + 0x7a */
+ u16 time; /* + 0x7c 0x3e */
+ } tx;
+ _MSCAN_RESERVED_(31, 2); /* + 0x7e */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION sizeof(struct mscan)
+
+#define MSCAN_NORMAL_MODE 0
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
+#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
+#define MSCAN_SET_MODE_RETRIES 255
+#define MSCAN_ECHO_SKB_MAX 3
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) ((sam) ? 1 << BTR1_SAM_SHIFT : 0)
+
+#define F_RX_PROGRESS 0
+#define F_TX_PROGRESS 1
+#define F_TX_WAIT_ALL 2
+
+#define TX_QUEUE_SIZE 3
+
+struct tx_queue_entry {
+ struct list_head list;
+ u8 mask;
+ u8 id;
+};
+
+struct mscan_priv {
+ struct can_priv can; /* must be the first member */
+ long open_time;
+ unsigned long flags;
+ void __iomem *reg_base; /* ioremap'ed address to registers */
+ u8 shadow_statflg;
+ u8 shadow_canrier;
+ u8 cur_pri;
+ u8 prev_buf_id;
+ u8 tx_active;
+
+ struct list_head tx_head;
+ struct tx_queue_entry tx_queue[TX_QUEUE_SIZE];
+ struct napi_struct napi;
+};
+
+extern struct net_device *alloc_mscandev(void);
+/*
+ * clock_src:
+ * 1 = The MSCAN clock source is the onchip Bus Clock.
+ * 0 = The MSCAN clock source is the chip Oscillator Clock.
+ */
+extern int register_mscandev(struct net_device *dev, int clock_src);
+extern void unregister_mscandev(struct net_device *dev);
+
+#endif /* __MSCAN_H__ */
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
index 16d2ecd2a3b..b4ba88a3107 100644
--- a/drivers/net/can/sja1000/sja1000.c
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -296,11 +296,9 @@ static void sja1000_rx(struct net_device *dev)
uint8_t dlc;
int i;
- skb = dev_alloc_skb(sizeof(struct can_frame));
+ skb = alloc_can_skb(dev, &cf);
if (skb == NULL)
return;
- skb->dev = dev;
- skb->protocol = htons(ETH_P_CAN);
fi = priv->read_reg(priv, REG_FI);
dlc = fi & 0x0F;
@@ -323,8 +321,6 @@ static void sja1000_rx(struct net_device *dev)
if (fi & FI_RTR)
id |= CAN_RTR_FLAG;
- cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
- memset(cf, 0, sizeof(struct can_frame));
cf->can_id = id;
cf->can_dlc = dlc;
for (i = 0; i < dlc; i++)
@@ -351,15 +347,9 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
enum can_state state = priv->can.state;
uint8_t ecc, alc;
- skb = dev_alloc_skb(sizeof(struct can_frame));
+ skb = alloc_can_err_skb(dev, &cf);
if (skb == NULL)
return -ENOMEM;
- skb->dev = dev;
- skb->protocol = htons(ETH_P_CAN);
- cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
- memset(cf, 0, sizeof(struct can_frame));
- cf->can_id = CAN_ERR_FLAG;
- cf->can_dlc = CAN_ERR_DLC;
if (isrc & IRQ_DOI) {
/* data overrun interrupt */
@@ -526,7 +516,7 @@ static int sja1000_open(struct net_device *dev)
/* register interrupt handler, if not done by the device driver */
if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) {
- err = request_irq(dev->irq, &sja1000_interrupt, priv->irq_flags,
+ err = request_irq(dev->irq, sja1000_interrupt, priv->irq_flags,
dev->name, (void *)dev);
if (err) {
close_candev(dev);
@@ -565,7 +555,8 @@ struct net_device *alloc_sja1000dev(int sizeof_priv)
struct net_device *dev;
struct sja1000_priv *priv;
- dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv);
+ dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv,
+ SJA1000_ECHO_SKB_MAX);
if (!dev)
return NULL;
diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h
index 302d2c763ad..97a622b9302 100644
--- a/drivers/net/can/sja1000/sja1000.h
+++ b/drivers/net/can/sja1000/sja1000.h
@@ -50,6 +50,8 @@
#include <linux/can/dev.h>
#include <linux/can/platform/sja1000.h>
+#define SJA1000_ECHO_SKB_MAX 1 /* the SJA1000 has one TX buffer object */
+
#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
new file mode 100644
index 00000000000..07e8016b17e
--- /dev/null
+++ b/drivers/net/can/ti_hecc.c
@@ -0,0 +1,993 @@
+/*
+ * TI HECC (CAN) device driver
+ *
+ * This driver supports TI's HECC (High End CAN Controller module) and the
+ * specs for the same is available at <http://www.ti.com>
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed as is WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Your platform definitions should specify module ram offsets and interrupt
+ * number to use as follows:
+ *
+ * static struct ti_hecc_platform_data am3517_evm_hecc_pdata = {
+ * .scc_hecc_offset = 0,
+ * .scc_ram_offset = 0x3000,
+ * .hecc_ram_offset = 0x3000,
+ * .mbx_offset = 0x2000,
+ * .int_line = 0,
+ * .revision = 1,
+ * };
+ *
+ * Please see include/can/platform/ti_hecc.h for description of above fields
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/platform/ti_hecc.h>
+
+#define DRV_NAME "ti_hecc"
+#define HECC_MODULE_VERSION "0.7"
+MODULE_VERSION(HECC_MODULE_VERSION);
+#define DRV_DESC "TI High End CAN Controller Driver " HECC_MODULE_VERSION
+
+/* TX / RX Mailbox Configuration */
+#define HECC_MAX_MAILBOXES 32 /* hardware mailboxes - do not change */
+#define MAX_TX_PRIO 0x3F /* hardware value - do not change */
+
+/*
+ * Important Note: TX mailbox configuration
+ * TX mailboxes should be restricted to the number of SKB buffers to avoid
+ * maintaining SKB buffers separately. TX mailboxes should be a power of 2
+ * for the mailbox logic to work. Top mailbox numbers are reserved for RX
+ * and lower mailboxes for TX.
+ *
+ * HECC_MAX_TX_MBOX HECC_MB_TX_SHIFT
+ * 4 (default) 2
+ * 8 3
+ * 16 4
+ */
+#define HECC_MB_TX_SHIFT 2 /* as per table above */
+#define HECC_MAX_TX_MBOX BIT(HECC_MB_TX_SHIFT)
+
+#define HECC_TX_PRIO_SHIFT (HECC_MB_TX_SHIFT)
+#define HECC_TX_PRIO_MASK (MAX_TX_PRIO << HECC_MB_TX_SHIFT)
+#define HECC_TX_MB_MASK (HECC_MAX_TX_MBOX - 1)
+#define HECC_TX_MASK ((HECC_MAX_TX_MBOX - 1) | HECC_TX_PRIO_MASK)
+#define HECC_TX_MBOX_MASK (~(BIT(HECC_MAX_TX_MBOX) - 1))
+#define HECC_DEF_NAPI_WEIGHT HECC_MAX_RX_MBOX
+
+/*
+ * Important Note: RX mailbox configuration
+ * RX mailboxes are further logically split into two - main and buffer
+ * mailboxes. The goal is to get all packets into main mailboxes as
+ * driven by mailbox number and receive priority (higher to lower) and
+ * buffer mailboxes are used to receive pkts while main mailboxes are being
+ * processed. This ensures in-order packet reception.
+ *
+ * Here are the recommended values for buffer mailbox. Note that RX mailboxes
+ * start after TX mailboxes:
+ *
+ * HECC_MAX_RX_MBOX HECC_RX_BUFFER_MBOX No of buffer mailboxes
+ * 28 12 8
+ * 16 20 4
+ */
+
+#define HECC_MAX_RX_MBOX (HECC_MAX_MAILBOXES - HECC_MAX_TX_MBOX)
+#define HECC_RX_BUFFER_MBOX 12 /* as per table above */
+#define HECC_RX_FIRST_MBOX (HECC_MAX_MAILBOXES - 1)
+#define HECC_RX_HIGH_MBOX_MASK (~(BIT(HECC_RX_BUFFER_MBOX) - 1))
+
+/* TI HECC module registers */
+#define HECC_CANME 0x0 /* Mailbox enable */
+#define HECC_CANMD 0x4 /* Mailbox direction */
+#define HECC_CANTRS 0x8 /* Transmit request set */
+#define HECC_CANTRR 0xC /* Transmit request */
+#define HECC_CANTA 0x10 /* Transmission acknowledge */
+#define HECC_CANAA 0x14 /* Abort acknowledge */
+#define HECC_CANRMP 0x18 /* Receive message pending */
+#define HECC_CANRML 0x1C /* Remote message lost */
+#define HECC_CANRFP 0x20 /* Remote frame pending */
+#define HECC_CANGAM 0x24 /* SECC only:Global acceptance mask */
+#define HECC_CANMC 0x28 /* Master control */
+#define HECC_CANBTC 0x2C /* Bit timing configuration */
+#define HECC_CANES 0x30 /* Error and status */
+#define HECC_CANTEC 0x34 /* Transmit error counter */
+#define HECC_CANREC 0x38 /* Receive error counter */
+#define HECC_CANGIF0 0x3C /* Global interrupt flag 0 */
+#define HECC_CANGIM 0x40 /* Global interrupt mask */
+#define HECC_CANGIF1 0x44 /* Global interrupt flag 1 */
+#define HECC_CANMIM 0x48 /* Mailbox interrupt mask */
+#define HECC_CANMIL 0x4C /* Mailbox interrupt level */
+#define HECC_CANOPC 0x50 /* Overwrite protection control */
+#define HECC_CANTIOC 0x54 /* Transmit I/O control */
+#define HECC_CANRIOC 0x58 /* Receive I/O control */
+#define HECC_CANLNT 0x5C /* HECC only: Local network time */
+#define HECC_CANTOC 0x60 /* HECC only: Time-out control */
+#define HECC_CANTOS 0x64 /* HECC only: Time-out status */
+#define HECC_CANTIOCE 0x68 /* SCC only:Enhanced TX I/O control */
+#define HECC_CANRIOCE 0x6C /* SCC only:Enhanced RX I/O control */
+
+/* Mailbox registers */
+#define HECC_CANMID 0x0
+#define HECC_CANMCF 0x4
+#define HECC_CANMDL 0x8
+#define HECC_CANMDH 0xC
+
+#define HECC_SET_REG 0xFFFFFFFF
+#define HECC_CANID_MASK 0x3FF /* 18 bits mask for extended id's */
+#define HECC_CCE_WAIT_COUNT 100 /* Wait for ~1 sec for CCE bit */
+
+#define HECC_CANMC_SCM BIT(13) /* SCC compat mode */
+#define HECC_CANMC_CCR BIT(12) /* Change config request */
+#define HECC_CANMC_PDR BIT(11) /* Local Power down - for sleep mode */
+#define HECC_CANMC_ABO BIT(7) /* Auto Bus On */
+#define HECC_CANMC_STM BIT(6) /* Self test mode - loopback */
+#define HECC_CANMC_SRES BIT(5) /* Software reset */
+
+#define HECC_CANTIOC_EN BIT(3) /* Enable CAN TX I/O pin */
+#define HECC_CANRIOC_EN BIT(3) /* Enable CAN RX I/O pin */
+
+#define HECC_CANMID_IDE BIT(31) /* Extended frame format */
+#define HECC_CANMID_AME BIT(30) /* Acceptance mask enable */
+#define HECC_CANMID_AAM BIT(29) /* Auto answer mode */
+
+#define HECC_CANES_FE BIT(24) /* form error */
+#define HECC_CANES_BE BIT(23) /* bit error */
+#define HECC_CANES_SA1 BIT(22) /* stuck at dominant error */
+#define HECC_CANES_CRCE BIT(21) /* CRC error */
+#define HECC_CANES_SE BIT(20) /* stuff bit error */
+#define HECC_CANES_ACKE BIT(19) /* ack error */
+#define HECC_CANES_BO BIT(18) /* Bus off status */
+#define HECC_CANES_EP BIT(17) /* Error passive status */
+#define HECC_CANES_EW BIT(16) /* Error warning status */
+#define HECC_CANES_SMA BIT(5) /* suspend mode ack */
+#define HECC_CANES_CCE BIT(4) /* Change config enabled */
+#define HECC_CANES_PDA BIT(3) /* Power down mode ack */
+
+#define HECC_CANBTC_SAM BIT(7) /* sample points */
+
+#define HECC_BUS_ERROR (HECC_CANES_FE | HECC_CANES_BE |\
+ HECC_CANES_CRCE | HECC_CANES_SE |\
+ HECC_CANES_ACKE)
+
+#define HECC_CANMCF_RTR BIT(4) /* Remote transmit request */
+
+#define HECC_CANGIF_MAIF BIT(17) /* Message alarm interrupt */
+#define HECC_CANGIF_TCOIF BIT(16) /* Timer counter overflow int */
+#define HECC_CANGIF_GMIF BIT(15) /* Global mailbox interrupt */
+#define HECC_CANGIF_AAIF BIT(14) /* Abort ack interrupt */
+#define HECC_CANGIF_WDIF BIT(13) /* Write denied interrupt */
+#define HECC_CANGIF_WUIF BIT(12) /* Wake up interrupt */
+#define HECC_CANGIF_RMLIF BIT(11) /* Receive message lost interrupt */
+#define HECC_CANGIF_BOIF BIT(10) /* Bus off interrupt */
+#define HECC_CANGIF_EPIF BIT(9) /* Error passive interrupt */
+#define HECC_CANGIF_WLIF BIT(8) /* Warning level interrupt */
+#define HECC_CANGIF_MBOX_MASK 0x1F /* Mailbox number mask */
+#define HECC_CANGIM_I1EN BIT(1) /* Int line 1 enable */
+#define HECC_CANGIM_I0EN BIT(0) /* Int line 0 enable */
+#define HECC_CANGIM_DEF_MASK 0x700 /* only busoff/warning/passive */
+#define HECC_CANGIM_SIL BIT(2) /* system interrupts to int line 1 */
+
+/* CAN Bittiming constants as per HECC specs */
+static struct can_bittiming_const ti_hecc_bittiming_const = {
+ .name = DRV_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 256,
+ .brp_inc = 1,
+};
+
+struct ti_hecc_priv {
+ struct can_priv can; /* MUST be first member/field */
+ struct napi_struct napi;
+ struct net_device *ndev;
+ struct clk *clk;
+ void __iomem *base;
+ u32 scc_ram_offset;
+ u32 hecc_ram_offset;
+ u32 mbx_offset;
+ u32 int_line;
+ spinlock_t mbx_lock; /* CANME register needs protection */
+ u32 tx_head;
+ u32 tx_tail;
+ u32 rx_next;
+};
+
+static inline int get_tx_head_mb(struct ti_hecc_priv *priv)
+{
+ return priv->tx_head & HECC_TX_MB_MASK;
+}
+
+static inline int get_tx_tail_mb(struct ti_hecc_priv *priv)
+{
+ return priv->tx_tail & HECC_TX_MB_MASK;
+}
+
+static inline int get_tx_head_prio(struct ti_hecc_priv *priv)
+{
+ return (priv->tx_head >> HECC_TX_PRIO_SHIFT) & MAX_TX_PRIO;
+}
+
+static inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val)
+{
+ __raw_writel(val, priv->base + priv->hecc_ram_offset + mbxno * 4);
+}
+
+static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno,
+ u32 reg, u32 val)
+{
+ __raw_writel(val, priv->base + priv->mbx_offset + mbxno * 0x10 +
+ reg);
+}
+
+static inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg)
+{
+ return __raw_readl(priv->base + priv->mbx_offset + mbxno * 0x10 +
+ reg);
+}
+
+static inline void hecc_write(struct ti_hecc_priv *priv, u32 reg, u32 val)
+{
+ __raw_writel(val, priv->base + reg);
+}
+
+static inline u32 hecc_read(struct ti_hecc_priv *priv, int reg)
+{
+ return __raw_readl(priv->base + reg);
+}
+
+static inline void hecc_set_bit(struct ti_hecc_priv *priv, int reg,
+ u32 bit_mask)
+{
+ hecc_write(priv, reg, hecc_read(priv, reg) | bit_mask);
+}
+
+static inline void hecc_clear_bit(struct ti_hecc_priv *priv, int reg,
+ u32 bit_mask)
+{
+ hecc_write(priv, reg, hecc_read(priv, reg) & ~bit_mask);
+}
+
+static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask)
+{
+ return (hecc_read(priv, reg) & bit_mask) ? 1 : 0;
+}
+
+static int ti_hecc_get_state(const struct net_device *ndev,
+ enum can_state *state)
+{
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+
+ *state = priv->can.state;
+ return 0;
+}
+
+static int ti_hecc_set_btc(struct ti_hecc_priv *priv)
+{
+ struct can_bittiming *bit_timing = &priv->can.bittiming;
+ u32 can_btc;
+
+ can_btc = (bit_timing->phase_seg2 - 1) & 0x7;
+ can_btc |= ((bit_timing->phase_seg1 + bit_timing->prop_seg - 1)
+ & 0xF) << 3;
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) {
+ if (bit_timing->brp > 4)
+ can_btc |= HECC_CANBTC_SAM;
+ else
+ dev_warn(priv->ndev->dev.parent, "WARN: Triple" \
+ "sampling not set due to h/w limitations");
+ }
+ can_btc |= ((bit_timing->sjw - 1) & 0x3) << 8;
+ can_btc |= ((bit_timing->brp - 1) & 0xFF) << 16;
+
+ /* ERM being set to 0 by default meaning resync at falling edge */
+
+ hecc_write(priv, HECC_CANBTC, can_btc);
+ dev_info(priv->ndev->dev.parent, "setting CANBTC=%#x\n", can_btc);
+
+ return 0;
+}
+
+static void ti_hecc_reset(struct net_device *ndev)
+{
+ u32 cnt;
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+
+ dev_dbg(ndev->dev.parent, "resetting hecc ...\n");
+ hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SRES);
+
+ /* Set change control request and wait till enabled */
+ hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
+
+ /*
+ * INFO: It has been observed that at times CCE bit may not be
+ * set and hw seems to be ok even if this bit is not set so
+ * timing out with a timing of 1ms to respect the specs
+ */
+ cnt = HECC_CCE_WAIT_COUNT;
+ while (!hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) {
+ --cnt;
+ udelay(10);
+ }
+
+ /*
+ * Note: On HECC, BTC can be programmed only in initialization mode, so
+ * it is expected that the can bittiming parameters are set via ip
+ * utility before the device is opened
+ */
+ ti_hecc_set_btc(priv);
+
+ /* Clear CCR (and CANMC register) and wait for CCE = 0 enable */
+ hecc_write(priv, HECC_CANMC, 0);
+
+ /*
+ * INFO: CAN net stack handles bus off and hence disabling auto-bus-on
+ * hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_ABO);
+ */
+
+ /*
+ * INFO: It has been observed that at times CCE bit may not be
+ * set and hw seems to be ok even if this bit is not set so
+ */
+ cnt = HECC_CCE_WAIT_COUNT;
+ while (hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) {
+ --cnt;
+ udelay(10);
+ }
+
+ /* Enable TX and RX I/O Control pins */
+ hecc_write(priv, HECC_CANTIOC, HECC_CANTIOC_EN);
+ hecc_write(priv, HECC_CANRIOC, HECC_CANRIOC_EN);
+
+ /* Clear registers for clean operation */
+ hecc_write(priv, HECC_CANTA, HECC_SET_REG);
+ hecc_write(priv, HECC_CANRMP, HECC_SET_REG);
+ hecc_write(priv, HECC_CANGIF0, HECC_SET_REG);
+ hecc_write(priv, HECC_CANGIF1, HECC_SET_REG);
+ hecc_write(priv, HECC_CANME, 0);
+ hecc_write(priv, HECC_CANMD, 0);
+
+ /* SCC compat mode NOT supported (and not needed too) */
+ hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SCM);
+}
+
+static void ti_hecc_start(struct net_device *ndev)
+{
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+ u32 cnt, mbxno, mbx_mask;
+
+ /* put HECC in initialization mode and set btc */
+ ti_hecc_reset(ndev);
+
+ priv->tx_head = priv->tx_tail = HECC_TX_MASK;
+ priv->rx_next = HECC_RX_FIRST_MBOX;
+
+ /* Enable local and global acceptance mask registers */
+ hecc_write(priv, HECC_CANGAM, HECC_SET_REG);
+
+ /* Prepare configured mailboxes to receive messages */
+ for (cnt = 0; cnt < HECC_MAX_RX_MBOX; cnt++) {
+ mbxno = HECC_MAX_MAILBOXES - 1 - cnt;
+ mbx_mask = BIT(mbxno);
+ hecc_clear_bit(priv, HECC_CANME, mbx_mask);
+ hecc_write_mbx(priv, mbxno, HECC_CANMID, HECC_CANMID_AME);
+ hecc_write_lam(priv, mbxno, HECC_SET_REG);
+ hecc_set_bit(priv, HECC_CANMD, mbx_mask);
+ hecc_set_bit(priv, HECC_CANME, mbx_mask);
+ hecc_set_bit(priv, HECC_CANMIM, mbx_mask);
+ }
+
+ /* Prevent message over-write & Enable interrupts */
+ hecc_write(priv, HECC_CANOPC, HECC_SET_REG);
+ if (priv->int_line) {
+ hecc_write(priv, HECC_CANMIL, HECC_SET_REG);
+ hecc_write(priv, HECC_CANGIM, HECC_CANGIM_DEF_MASK |
+ HECC_CANGIM_I1EN | HECC_CANGIM_SIL);
+ } else {
+ hecc_write(priv, HECC_CANMIL, 0);
+ hecc_write(priv, HECC_CANGIM,
+ HECC_CANGIM_DEF_MASK | HECC_CANGIM_I0EN);
+ }
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+static void ti_hecc_stop(struct net_device *ndev)
+{
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+
+ /* Disable interrupts and disable mailboxes */
+ hecc_write(priv, HECC_CANGIM, 0);
+ hecc_write(priv, HECC_CANMIM, 0);
+ hecc_write(priv, HECC_CANME, 0);
+ priv->can.state = CAN_STATE_STOPPED;
+}
+
+static int ti_hecc_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ int ret = 0;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ ti_hecc_start(ndev);
+ netif_wake_queue(ndev);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * ti_hecc_xmit: HECC Transmit
+ *
+ * The transmit mailboxes start from 0 to HECC_MAX_TX_MBOX. In HECC the
+ * priority of the mailbox for tranmission is dependent upon priority setting
+ * field in mailbox registers. The mailbox with highest value in priority field
+ * is transmitted first. Only when two mailboxes have the same value in
+ * priority field the highest numbered mailbox is transmitted first.
+ *
+ * To utilize the HECC priority feature as described above we start with the
+ * highest numbered mailbox with highest priority level and move on to the next
+ * mailbox with the same priority level and so on. Once we loop through all the
+ * transmit mailboxes we choose the next priority level (lower) and so on
+ * until we reach the lowest priority level on the lowest numbered mailbox
+ * when we stop transmission until all mailboxes are transmitted and then
+ * restart at highest numbered mailbox with highest priority.
+ *
+ * Two counters (head and tail) are used to track the next mailbox to transmit
+ * and to track the echo buffer for already transmitted mailbox. The queue
+ * is stopped when all the mailboxes are busy or when there is a priority
+ * value roll-over happens.
+ */
+static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ u32 mbxno, mbx_mask, data;
+ unsigned long flags;
+
+ mbxno = get_tx_head_mb(priv);
+ mbx_mask = BIT(mbxno);
+ spin_lock_irqsave(&priv->mbx_lock, flags);
+ if (unlikely(hecc_read(priv, HECC_CANME) & mbx_mask)) {
+ spin_unlock_irqrestore(&priv->mbx_lock, flags);
+ netif_stop_queue(ndev);
+ dev_err(priv->ndev->dev.parent,
+ "BUG: TX mbx not ready tx_head=%08X, tx_tail=%08X\n",
+ priv->tx_head, priv->tx_tail);
+ return NETDEV_TX_BUSY;
+ }
+ spin_unlock_irqrestore(&priv->mbx_lock, flags);
+
+ /* Prepare mailbox for transmission */
+ data = min_t(u8, cf->can_dlc, 8);
+ if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */
+ data |= HECC_CANMCF_RTR;
+ data |= get_tx_head_prio(priv) << 8;
+ hecc_write_mbx(priv, mbxno, HECC_CANMCF, data);
+
+ if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */
+ data = (cf->can_id & CAN_EFF_MASK) | HECC_CANMID_IDE;
+ else /* Standard frame format */
+ data = (cf->can_id & CAN_SFF_MASK) << 18;
+ hecc_write_mbx(priv, mbxno, HECC_CANMID, data);
+ hecc_write_mbx(priv, mbxno, HECC_CANMDL,
+ be32_to_cpu(*(u32 *)(cf->data)));
+ if (cf->can_dlc > 4)
+ hecc_write_mbx(priv, mbxno, HECC_CANMDH,
+ be32_to_cpu(*(u32 *)(cf->data + 4)));
+ else
+ *(u32 *)(cf->data + 4) = 0;
+ can_put_echo_skb(skb, ndev, mbxno);
+
+ spin_lock_irqsave(&priv->mbx_lock, flags);
+ --priv->tx_head;
+ if ((hecc_read(priv, HECC_CANME) & BIT(get_tx_head_mb(priv))) ||
+ (priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK) {
+ netif_stop_queue(ndev);
+ }
+ hecc_set_bit(priv, HECC_CANME, mbx_mask);
+ spin_unlock_irqrestore(&priv->mbx_lock, flags);
+
+ hecc_clear_bit(priv, HECC_CANMD, mbx_mask);
+ hecc_set_bit(priv, HECC_CANMIM, mbx_mask);
+ hecc_write(priv, HECC_CANTRS, mbx_mask);
+
+ return NETDEV_TX_OK;
+}
+
+static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno)
+{
+ struct net_device_stats *stats = &priv->ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ u32 data, mbx_mask;
+ unsigned long flags;
+
+ skb = alloc_can_skb(priv->ndev, &cf);
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_err(priv->ndev->dev.parent,
+ "ti_hecc_rx_pkt: alloc_can_skb() failed\n");
+ return -ENOMEM;
+ }
+
+ mbx_mask = BIT(mbxno);
+ data = hecc_read_mbx(priv, mbxno, HECC_CANMID);
+ if (data & HECC_CANMID_IDE)
+ cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ cf->can_id = (data >> 18) & CAN_SFF_MASK;
+ data = hecc_read_mbx(priv, mbxno, HECC_CANMCF);
+ if (data & HECC_CANMCF_RTR)
+ cf->can_id |= CAN_RTR_FLAG;
+ cf->can_dlc = data & 0xF;
+ data = hecc_read_mbx(priv, mbxno, HECC_CANMDL);
+ *(u32 *)(cf->data) = cpu_to_be32(data);
+ if (cf->can_dlc > 4) {
+ data = hecc_read_mbx(priv, mbxno, HECC_CANMDH);
+ *(u32 *)(cf->data + 4) = cpu_to_be32(data);
+ } else {
+ *(u32 *)(cf->data + 4) = 0;
+ }
+ spin_lock_irqsave(&priv->mbx_lock, flags);
+ hecc_clear_bit(priv, HECC_CANME, mbx_mask);
+ hecc_write(priv, HECC_CANRMP, mbx_mask);
+ /* enable mailbox only if it is part of rx buffer mailboxes */
+ if (priv->rx_next < HECC_RX_BUFFER_MBOX)
+ hecc_set_bit(priv, HECC_CANME, mbx_mask);
+ spin_unlock_irqrestore(&priv->mbx_lock, flags);
+
+ stats->rx_bytes += cf->can_dlc;
+ netif_receive_skb(skb);
+ stats->rx_packets++;
+
+ return 0;
+}
+
+/*
+ * ti_hecc_rx_poll - HECC receive pkts
+ *
+ * The receive mailboxes start from highest numbered mailbox till last xmit
+ * mailbox. On CAN frame reception the hardware places the data into highest
+ * numbered mailbox that matches the CAN ID filter. Since all receive mailboxes
+ * have same filtering (ALL CAN frames) packets will arrive in the highest
+ * available RX mailbox and we need to ensure in-order packet reception.
+ *
+ * To ensure the packets are received in the right order we logically divide
+ * the RX mailboxes into main and buffer mailboxes. Packets are received as per
+ * mailbox priotity (higher to lower) in the main bank and once it is full we
+ * disable further reception into main mailboxes. While the main mailboxes are
+ * processed in NAPI, further packets are received in buffer mailboxes.
+ *
+ * We maintain a RX next mailbox counter to process packets and once all main
+ * mailboxe packets are passed to the upper stack we enable all of them but
+ * continue to process packets received in buffer mailboxes. With each packet
+ * received from buffer mailbox we enable it immediately so as to handle the
+ * overflow from higher mailboxes.
+ */
+static int ti_hecc_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct net_device *ndev = napi->dev;
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+ u32 num_pkts = 0;
+ u32 mbx_mask;
+ unsigned long pending_pkts, flags;
+
+ if (!netif_running(ndev))
+ return 0;
+
+ while ((pending_pkts = hecc_read(priv, HECC_CANRMP)) &&
+ num_pkts < quota) {
+ mbx_mask = BIT(priv->rx_next); /* next rx mailbox to process */
+ if (mbx_mask & pending_pkts) {
+ if (ti_hecc_rx_pkt(priv, priv->rx_next) < 0)
+ return num_pkts;
+ ++num_pkts;
+ } else if (priv->rx_next > HECC_RX_BUFFER_MBOX) {
+ break; /* pkt not received yet */
+ }
+ --priv->rx_next;
+ if (priv->rx_next == HECC_RX_BUFFER_MBOX) {
+ /* enable high bank mailboxes */
+ spin_lock_irqsave(&priv->mbx_lock, flags);
+ mbx_mask = hecc_read(priv, HECC_CANME);
+ mbx_mask |= HECC_RX_HIGH_MBOX_MASK;
+ hecc_write(priv, HECC_CANME, mbx_mask);
+ spin_unlock_irqrestore(&priv->mbx_lock, flags);
+ } else if (priv->rx_next == HECC_MAX_TX_MBOX - 1) {
+ priv->rx_next = HECC_RX_FIRST_MBOX;
+ break;
+ }
+ }
+
+ /* Enable packet interrupt if all pkts are handled */
+ if (hecc_read(priv, HECC_CANRMP) == 0) {
+ napi_complete(napi);
+ /* Re-enable RX mailbox interrupts */
+ mbx_mask = hecc_read(priv, HECC_CANMIM);
+ mbx_mask |= HECC_TX_MBOX_MASK;
+ hecc_write(priv, HECC_CANMIM, mbx_mask);
+ }
+
+ return num_pkts;
+}
+
+static int ti_hecc_error(struct net_device *ndev, int int_status,
+ int err_status)
+{
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+
+ /* propogate the error condition to the can stack */
+ skb = alloc_can_err_skb(ndev, &cf);
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_err(priv->ndev->dev.parent,
+ "ti_hecc_error: alloc_can_err_skb() failed\n");
+ return -ENOMEM;
+ }
+
+ if (int_status & HECC_CANGIF_WLIF) { /* warning level int */
+ if ((int_status & HECC_CANGIF_BOIF) == 0) {
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ ++priv->can.can_stats.error_warning;
+ cf->can_id |= CAN_ERR_CRTL;
+ if (hecc_read(priv, HECC_CANTEC) > 96)
+ cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ if (hecc_read(priv, HECC_CANREC) > 96)
+ cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ }
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW);
+ dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n");
+ hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
+ }
+
+ if (int_status & HECC_CANGIF_EPIF) { /* error passive int */
+ if ((int_status & HECC_CANGIF_BOIF) == 0) {
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ ++priv->can.can_stats.error_passive;
+ cf->can_id |= CAN_ERR_CRTL;
+ if (hecc_read(priv, HECC_CANTEC) > 127)
+ cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+ if (hecc_read(priv, HECC_CANREC) > 127)
+ cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_EP);
+ dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n");
+ hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
+ }
+
+ /*
+ * Need to check busoff condition in error status register too to
+ * ensure warning interrupts don't hog the system
+ */
+ if ((int_status & HECC_CANGIF_BOIF) || (err_status & HECC_CANES_BO)) {
+ priv->can.state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_BO);
+ hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
+ /* Disable all interrupts in bus-off to avoid int hog */
+ hecc_write(priv, HECC_CANGIM, 0);
+ can_bus_off(ndev);
+ }
+
+ if (err_status & HECC_BUS_ERROR) {
+ ++priv->can.can_stats.bus_error;
+ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ if (err_status & HECC_CANES_FE) {
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_FE);
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ }
+ if (err_status & HECC_CANES_BE) {
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_BE);
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ }
+ if (err_status & HECC_CANES_SE) {
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_SE);
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ }
+ if (err_status & HECC_CANES_CRCE) {
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE);
+ cf->data[2] |= CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL;
+ }
+ if (err_status & HECC_CANES_ACKE) {
+ hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE);
+ cf->data[2] |= CAN_ERR_PROT_LOC_ACK |
+ CAN_ERR_PROT_LOC_ACK_DEL;
+ }
+ }
+
+ netif_receive_skb(skb);
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ return 0;
+}
+
+static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *)dev_id;
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+ struct net_device_stats *stats = &ndev->stats;
+ u32 mbxno, mbx_mask, int_status, err_status;
+ unsigned long ack, flags;
+
+ int_status = hecc_read(priv,
+ (priv->int_line) ? HECC_CANGIF1 : HECC_CANGIF0);
+
+ if (!int_status)
+ return IRQ_NONE;
+
+ err_status = hecc_read(priv, HECC_CANES);
+ if (err_status & (HECC_BUS_ERROR | HECC_CANES_BO |
+ HECC_CANES_EP | HECC_CANES_EW))
+ ti_hecc_error(ndev, int_status, err_status);
+
+ if (int_status & HECC_CANGIF_GMIF) {
+ while (priv->tx_tail - priv->tx_head > 0) {
+ mbxno = get_tx_tail_mb(priv);
+ mbx_mask = BIT(mbxno);
+ if (!(mbx_mask & hecc_read(priv, HECC_CANTA)))
+ break;
+ hecc_clear_bit(priv, HECC_CANMIM, mbx_mask);
+ hecc_write(priv, HECC_CANTA, mbx_mask);
+ spin_lock_irqsave(&priv->mbx_lock, flags);
+ hecc_clear_bit(priv, HECC_CANME, mbx_mask);
+ spin_unlock_irqrestore(&priv->mbx_lock, flags);
+ stats->tx_bytes += hecc_read_mbx(priv, mbxno,
+ HECC_CANMCF) & 0xF;
+ stats->tx_packets++;
+ can_get_echo_skb(ndev, mbxno);
+ --priv->tx_tail;
+ }
+
+ /* restart queue if wrap-up or if queue stalled on last pkt */
+ if (((priv->tx_head == priv->tx_tail) &&
+ ((priv->tx_head & HECC_TX_MASK) != HECC_TX_MASK)) ||
+ (((priv->tx_tail & HECC_TX_MASK) == HECC_TX_MASK) &&
+ ((priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK)))
+ netif_wake_queue(ndev);
+
+ /* Disable RX mailbox interrupts and let NAPI reenable them */
+ if (hecc_read(priv, HECC_CANRMP)) {
+ ack = hecc_read(priv, HECC_CANMIM);
+ ack &= BIT(HECC_MAX_TX_MBOX) - 1;
+ hecc_write(priv, HECC_CANMIM, ack);
+ napi_schedule(&priv->napi);
+ }
+ }
+
+ /* clear all interrupt conditions - read back to avoid spurious ints */
+ if (priv->int_line) {
+ hecc_write(priv, HECC_CANGIF1, HECC_SET_REG);
+ int_status = hecc_read(priv, HECC_CANGIF1);
+ } else {
+ hecc_write(priv, HECC_CANGIF0, HECC_SET_REG);
+ int_status = hecc_read(priv, HECC_CANGIF0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ti_hecc_open(struct net_device *ndev)
+{
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+ int err;
+
+ err = request_irq(ndev->irq, ti_hecc_interrupt, IRQF_SHARED,
+ ndev->name, ndev);
+ if (err) {
+ dev_err(ndev->dev.parent, "error requesting interrupt\n");
+ return err;
+ }
+
+ /* Open common can device */
+ err = open_candev(ndev);
+ if (err) {
+ dev_err(ndev->dev.parent, "open_candev() failed %d\n", err);
+ free_irq(ndev->irq, ndev);
+ return err;
+ }
+
+ clk_enable(priv->clk);
+ ti_hecc_start(ndev);
+ napi_enable(&priv->napi);
+ netif_start_queue(ndev);
+
+ return 0;
+}
+
+static int ti_hecc_close(struct net_device *ndev)
+{
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ ti_hecc_stop(ndev);
+ free_irq(ndev->irq, ndev);
+ clk_disable(priv->clk);
+ close_candev(ndev);
+
+ return 0;
+}
+
+static const struct net_device_ops ti_hecc_netdev_ops = {
+ .ndo_open = ti_hecc_open,
+ .ndo_stop = ti_hecc_close,
+ .ndo_start_xmit = ti_hecc_xmit,
+};
+
+static int ti_hecc_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev = (struct net_device *)0;
+ struct ti_hecc_priv *priv;
+ struct ti_hecc_platform_data *pdata;
+ struct resource *mem, *irq;
+ void __iomem *addr;
+ int err = -ENODEV;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data\n");
+ goto probe_exit;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No mem resources\n");
+ goto probe_exit;
+ }
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "No irq resource\n");
+ goto probe_exit;
+ }
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(&pdev->dev, "HECC region already claimed\n");
+ err = -EBUSY;
+ goto probe_exit;
+ }
+ addr = ioremap(mem->start, resource_size(mem));
+ if (!addr) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ err = -ENOMEM;
+ goto probe_exit_free_region;
+ }
+
+ ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX);
+ if (!ndev) {
+ dev_err(&pdev->dev, "alloc_candev failed\n");
+ err = -ENOMEM;
+ goto probe_exit_iounmap;
+ }
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ priv->base = addr;
+ priv->scc_ram_offset = pdata->scc_ram_offset;
+ priv->hecc_ram_offset = pdata->hecc_ram_offset;
+ priv->mbx_offset = pdata->mbx_offset;
+ priv->int_line = pdata->int_line;
+
+ priv->can.bittiming_const = &ti_hecc_bittiming_const;
+ priv->can.do_set_mode = ti_hecc_do_set_mode;
+ priv->can.do_get_state = ti_hecc_get_state;
+
+ ndev->irq = irq->start;
+ ndev->flags |= IFF_ECHO;
+ platform_set_drvdata(pdev, ndev);
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+ ndev->netdev_ops = &ti_hecc_netdev_ops;
+
+ priv->clk = clk_get(&pdev->dev, "hecc_ck");
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "No clock available\n");
+ err = PTR_ERR(priv->clk);
+ priv->clk = NULL;
+ goto probe_exit_candev;
+ }
+ priv->can.clock.freq = clk_get_rate(priv->clk);
+ netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll,
+ HECC_DEF_NAPI_WEIGHT);
+
+ err = register_candev(ndev);
+ if (err) {
+ dev_err(&pdev->dev, "register_candev() failed\n");
+ goto probe_exit_clk;
+ }
+ dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n",
+ priv->base, (u32) ndev->irq);
+
+ return 0;
+
+probe_exit_clk:
+ clk_put(priv->clk);
+probe_exit_candev:
+ free_candev(ndev);
+probe_exit_iounmap:
+ iounmap(addr);
+probe_exit_free_region:
+ release_mem_region(mem->start, resource_size(mem));
+probe_exit:
+ return err;
+}
+
+static int __devexit ti_hecc_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct ti_hecc_priv *priv = netdev_priv(ndev);
+
+ clk_put(priv->clk);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iounmap(priv->base);
+ release_mem_region(res->start, resource_size(res));
+ unregister_candev(ndev);
+ free_candev(ndev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/* TI HECC netdevice driver: platform driver structure */
+static struct platform_driver ti_hecc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ti_hecc_probe,
+ .remove = __devexit_p(ti_hecc_remove),
+};
+
+static int __init ti_hecc_init_driver(void)
+{
+ printk(KERN_INFO DRV_DESC "\n");
+ return platform_driver_register(&ti_hecc_driver);
+}
+module_init(ti_hecc_init_driver);
+
+static void __exit ti_hecc_exit_driver(void)
+{
+ printk(KERN_INFO DRV_DESC " unloaded\n");
+ platform_driver_unregister(&ti_hecc_driver);
+}
+module_exit(ti_hecc_exit_driver);
+
+MODULE_AUTHOR("Anant Gole <anantgole@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(DRV_DESC);
diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c
index abdbd9c2b78..591eb0eb1c2 100644
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@ -232,7 +232,7 @@ MODULE_DEVICE_TABLE(usb, ems_usb_table);
#define INTR_IN_BUFFER_SIZE 4
#define MAX_RX_URBS 10
-#define MAX_TX_URBS CAN_ECHO_SKB_MAX
+#define MAX_TX_URBS 10
struct ems_usb;
@@ -311,23 +311,19 @@ static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg)
int i;
struct net_device_stats *stats = &dev->netdev->stats;
- skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame));
+ skb = alloc_can_skb(dev->netdev, &cf);
if (skb == NULL)
return;
- skb->protocol = htons(ETH_P_CAN);
-
- cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
-
cf->can_id = le32_to_cpu(msg->msg.can_msg.id);
cf->can_dlc = min_t(u8, msg->msg.can_msg.length, 8);
- if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME
- || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME)
+ if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME ||
+ msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME)
cf->can_id |= CAN_EFF_FLAG;
- if (msg->type == CPC_MSG_TYPE_RTR_FRAME
- || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) {
+ if (msg->type == CPC_MSG_TYPE_RTR_FRAME ||
+ msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) {
cf->can_id |= CAN_RTR_FLAG;
} else {
for (i = 0; i < cf->can_dlc; i++)
@@ -346,18 +342,10 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg)
struct sk_buff *skb;
struct net_device_stats *stats = &dev->netdev->stats;
- skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame));
+ skb = alloc_can_err_skb(dev->netdev, &cf);
if (skb == NULL)
return;
- skb->protocol = htons(ETH_P_CAN);
-
- cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
- memset(cf, 0, sizeof(struct can_frame));
-
- cf->can_id = CAN_ERR_FLAG;
- cf->can_dlc = CAN_ERR_DLC;
-
if (msg->type == CPC_MSG_TYPE_CAN_STATE) {
u8 state = msg->msg.can_state;
@@ -1015,7 +1003,7 @@ static int ems_usb_probe(struct usb_interface *intf,
struct ems_usb *dev;
int i, err = -ENOMEM;
- netdev = alloc_candev(sizeof(struct ems_usb));
+ netdev = alloc_candev(sizeof(struct ems_usb), MAX_TX_URBS);
if (!netdev) {
dev_err(netdev->dev.parent, "Couldn't alloc candev\n");
return -ENOMEM;
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c
index 3bf1b04f2ca..d4c6e7fcff5 100644
--- a/drivers/net/cnic.c
+++ b/drivers/net/cnic.c
@@ -33,10 +33,16 @@
#include <net/route.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
+#include <net/ip6_checksum.h>
#include <scsi/iscsi_if.h>
#include "cnic_if.h"
#include "bnx2.h"
+#include "bnx2x_reg.h"
+#include "bnx2x_fw_defs.h"
+#include "bnx2x_hsi.h"
+#include "../scsi/bnx2i/57xx_iscsi_constants.h"
+#include "../scsi/bnx2i/57xx_iscsi_hsi.h"
#include "cnic.h"
#include "cnic_defs.h"
@@ -59,6 +65,7 @@ static DEFINE_MUTEX(cnic_lock);
static struct cnic_ulp_ops *cnic_ulp_tbl[MAX_CNIC_ULP_TYPE];
static int cnic_service_bnx2(void *, void *);
+static int cnic_service_bnx2x(void *, void *);
static int cnic_ctl(void *, struct cnic_ctl_info *);
static struct cnic_ops cnic_bnx2_ops = {
@@ -67,9 +74,14 @@ static struct cnic_ops cnic_bnx2_ops = {
.cnic_ctl = cnic_ctl,
};
-static void cnic_shutdown_bnx2_rx_ring(struct cnic_dev *);
-static void cnic_init_bnx2_tx_ring(struct cnic_dev *);
-static void cnic_init_bnx2_rx_ring(struct cnic_dev *);
+static struct cnic_ops cnic_bnx2x_ops = {
+ .cnic_owner = THIS_MODULE,
+ .cnic_handler = cnic_service_bnx2x,
+ .cnic_ctl = cnic_ctl,
+};
+
+static void cnic_shutdown_rings(struct cnic_dev *);
+static void cnic_init_rings(struct cnic_dev *);
static int cnic_cm_set_pg(struct cnic_sock *);
static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode)
@@ -83,10 +95,16 @@ static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode)
if (cp->uio_dev != -1)
return -EBUSY;
+ rtnl_lock();
+ if (!test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
+ rtnl_unlock();
+ return -ENODEV;
+ }
+
cp->uio_dev = iminor(inode);
- cnic_init_bnx2_tx_ring(dev);
- cnic_init_bnx2_rx_ring(dev);
+ cnic_init_rings(dev);
+ rtnl_unlock();
return 0;
}
@@ -96,7 +114,7 @@ static int cnic_uio_close(struct uio_info *uinfo, struct inode *inode)
struct cnic_dev *dev = uinfo->priv;
struct cnic_local *cp = dev->cnic_priv;
- cnic_shutdown_bnx2_rx_ring(dev);
+ cnic_shutdown_rings(dev);
cp->uio_dev = -1;
return 0;
@@ -162,6 +180,36 @@ static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val)
ethdev->drv_ctl(dev->netdev, &info);
}
+static void cnic_ctx_tbl_wr(struct cnic_dev *dev, u32 off, dma_addr_t addr)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ struct drv_ctl_info info;
+ struct drv_ctl_io *io = &info.data.io;
+
+ info.cmd = DRV_CTL_CTXTBL_WR_CMD;
+ io->offset = off;
+ io->dma_addr = addr;
+ ethdev->drv_ctl(dev->netdev, &info);
+}
+
+static void cnic_ring_ctl(struct cnic_dev *dev, u32 cid, u32 cl_id, int start)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ struct drv_ctl_info info;
+ struct drv_ctl_l2_ring *ring = &info.data.ring;
+
+ if (start)
+ info.cmd = DRV_CTL_START_L2_CMD;
+ else
+ info.cmd = DRV_CTL_STOP_L2_CMD;
+
+ ring->cid = cid;
+ ring->client_id = cl_id;
+ ethdev->drv_ctl(dev->netdev, &info);
+}
+
static void cnic_reg_wr_ind(struct cnic_dev *dev, u32 off, u32 val)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -204,6 +252,19 @@ static void cnic_kwq_completion(struct cnic_dev *dev, u32 count)
ethdev->drv_ctl(dev->netdev, &info);
}
+static int cnic_get_l5_cid(struct cnic_local *cp, u32 cid, u32 *l5_cid)
+{
+ u32 i;
+
+ for (i = 0; i < MAX_ISCSI_TBL_SZ; i++) {
+ if (cp->ctx_tbl[i].cid == cid) {
+ *l5_cid = i;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
static int cnic_send_nlmsg(struct cnic_local *cp, u32 type,
struct cnic_sock *csk)
{
@@ -347,7 +408,7 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
{
struct cnic_dev *dev;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n",
ulp_type);
return -EINVAL;
@@ -393,7 +454,7 @@ int cnic_unregister_driver(int ulp_type)
struct cnic_ulp_ops *ulp_ops;
int i = 0;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
ulp_type);
return -EINVAL;
@@ -449,7 +510,7 @@ static int cnic_register_device(struct cnic_dev *dev, int ulp_type,
struct cnic_local *cp = dev->cnic_priv;
struct cnic_ulp_ops *ulp_ops;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n",
ulp_type);
return -EINVAL;
@@ -490,7 +551,7 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
struct cnic_local *cp = dev->cnic_priv;
int i = 0;
- if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+ if (ulp_type < 0 || ulp_type >= MAX_CNIC_ULP_TYPE) {
printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
ulp_type);
return -EINVAL;
@@ -606,14 +667,14 @@ static void cnic_free_dma(struct cnic_dev *dev, struct cnic_dma *dma)
for (i = 0; i < dma->num_pages; i++) {
if (dma->pg_arr[i]) {
- pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE,
- dma->pg_arr[i], dma->pg_map_arr[i]);
+ dma_free_coherent(&dev->pcidev->dev, BCM_PAGE_SIZE,
+ dma->pg_arr[i], dma->pg_map_arr[i]);
dma->pg_arr[i] = NULL;
}
}
if (dma->pgtbl) {
- pci_free_consistent(dev->pcidev, dma->pgtbl_size,
- dma->pgtbl, dma->pgtbl_map);
+ dma_free_coherent(&dev->pcidev->dev, dma->pgtbl_size,
+ dma->pgtbl, dma->pgtbl_map);
dma->pgtbl = NULL;
}
kfree(dma->pg_arr);
@@ -635,6 +696,20 @@ static void cnic_setup_page_tbl(struct cnic_dev *dev, struct cnic_dma *dma)
}
}
+static void cnic_setup_page_tbl_le(struct cnic_dev *dev, struct cnic_dma *dma)
+{
+ int i;
+ u32 *page_table = dma->pgtbl;
+
+ for (i = 0; i < dma->num_pages; i++) {
+ /* Each entry needs to be in little endian format. */
+ *page_table = dma->pg_map_arr[i] & 0xffffffff;
+ page_table++;
+ *page_table = (u32) ((u64) dma->pg_map_arr[i] >> 32);
+ page_table++;
+ }
+}
+
static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma,
int pages, int use_pg_tbl)
{
@@ -650,9 +725,10 @@ static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma,
dma->num_pages = pages;
for (i = 0; i < pages; i++) {
- dma->pg_arr[i] = pci_alloc_consistent(dev->pcidev,
- BCM_PAGE_SIZE,
- &dma->pg_map_arr[i]);
+ dma->pg_arr[i] = dma_alloc_coherent(&dev->pcidev->dev,
+ BCM_PAGE_SIZE,
+ &dma->pg_map_arr[i],
+ GFP_ATOMIC);
if (dma->pg_arr[i] == NULL)
goto error;
}
@@ -661,8 +737,8 @@ static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma,
dma->pgtbl_size = ((pages * 8) + BCM_PAGE_SIZE - 1) &
~(BCM_PAGE_SIZE - 1);
- dma->pgtbl = pci_alloc_consistent(dev->pcidev, dma->pgtbl_size,
- &dma->pgtbl_map);
+ dma->pgtbl = dma_alloc_coherent(&dev->pcidev->dev, dma->pgtbl_size,
+ &dma->pgtbl_map, GFP_ATOMIC);
if (dma->pgtbl == NULL)
goto error;
@@ -675,6 +751,21 @@ error:
return -ENOMEM;
}
+static void cnic_free_context(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int i;
+
+ for (i = 0; i < cp->ctx_blks; i++) {
+ if (cp->ctx_arr[i].ctx) {
+ dma_free_coherent(&dev->pcidev->dev, cp->ctx_blk_size,
+ cp->ctx_arr[i].ctx,
+ cp->ctx_arr[i].mapping);
+ cp->ctx_arr[i].ctx = NULL;
+ }
+ }
+}
+
static void cnic_free_resc(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -691,25 +782,18 @@ static void cnic_free_resc(struct cnic_dev *dev)
}
if (cp->l2_buf) {
- pci_free_consistent(dev->pcidev, cp->l2_buf_size,
- cp->l2_buf, cp->l2_buf_map);
+ dma_free_coherent(&dev->pcidev->dev, cp->l2_buf_size,
+ cp->l2_buf, cp->l2_buf_map);
cp->l2_buf = NULL;
}
if (cp->l2_ring) {
- pci_free_consistent(dev->pcidev, cp->l2_ring_size,
- cp->l2_ring, cp->l2_ring_map);
+ dma_free_coherent(&dev->pcidev->dev, cp->l2_ring_size,
+ cp->l2_ring, cp->l2_ring_map);
cp->l2_ring = NULL;
}
- for (i = 0; i < cp->ctx_blks; i++) {
- if (cp->ctx_arr[i].ctx) {
- pci_free_consistent(dev->pcidev, cp->ctx_blk_size,
- cp->ctx_arr[i].ctx,
- cp->ctx_arr[i].mapping);
- cp->ctx_arr[i].ctx = NULL;
- }
- }
+ cnic_free_context(dev);
kfree(cp->ctx_arr);
cp->ctx_arr = NULL;
cp->ctx_blks = 0;
@@ -717,6 +801,7 @@ static void cnic_free_resc(struct cnic_dev *dev)
cnic_free_dma(dev, &cp->gbl_buf_info);
cnic_free_dma(dev, &cp->conn_buf_info);
cnic_free_dma(dev, &cp->kwq_info);
+ cnic_free_dma(dev, &cp->kwq_16_data_info);
cnic_free_dma(dev, &cp->kcq_info);
kfree(cp->iscsi_tbl);
cp->iscsi_tbl = NULL;
@@ -765,8 +850,10 @@ static int cnic_alloc_context(struct cnic_dev *dev)
for (i = 0; i < cp->ctx_blks; i++) {
cp->ctx_arr[i].ctx =
- pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE,
- &cp->ctx_arr[i].mapping);
+ dma_alloc_coherent(&dev->pcidev->dev,
+ BCM_PAGE_SIZE,
+ &cp->ctx_arr[i].mapping,
+ GFP_KERNEL);
if (cp->ctx_arr[i].ctx == NULL)
return -ENOMEM;
}
@@ -779,15 +866,17 @@ static int cnic_alloc_l2_rings(struct cnic_dev *dev, int pages)
struct cnic_local *cp = dev->cnic_priv;
cp->l2_ring_size = pages * BCM_PAGE_SIZE;
- cp->l2_ring = pci_alloc_consistent(dev->pcidev, cp->l2_ring_size,
- &cp->l2_ring_map);
+ cp->l2_ring = dma_alloc_coherent(&dev->pcidev->dev, cp->l2_ring_size,
+ &cp->l2_ring_map,
+ GFP_KERNEL | __GFP_COMP);
if (!cp->l2_ring)
return -ENOMEM;
cp->l2_buf_size = (cp->l2_rx_ring_size + 1) * cp->l2_single_buf_size;
cp->l2_buf_size = PAGE_ALIGN(cp->l2_buf_size);
- cp->l2_buf = pci_alloc_consistent(dev->pcidev, cp->l2_buf_size,
- &cp->l2_buf_map);
+ cp->l2_buf = dma_alloc_coherent(&dev->pcidev->dev, cp->l2_buf_size,
+ &cp->l2_buf_map,
+ GFP_KERNEL | __GFP_COMP);
if (!cp->l2_buf)
return -ENOMEM;
@@ -808,14 +897,20 @@ static int cnic_alloc_uio(struct cnic_dev *dev) {
uinfo->mem[0].size = dev->netdev->mem_end - dev->netdev->mem_start;
uinfo->mem[0].memtype = UIO_MEM_PHYS;
- uinfo->mem[1].addr = (unsigned long) cp->status_blk & PAGE_MASK;
if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) {
+ uinfo->mem[1].addr = (unsigned long) cp->status_blk & PAGE_MASK;
if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX)
uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE * 9;
else
uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE;
uinfo->name = "bnx2_cnic";
+ } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
+ uinfo->mem[1].addr = (unsigned long) cp->bnx2x_def_status_blk &
+ PAGE_MASK;
+ uinfo->mem[1].size = sizeof(struct host_def_status_block);
+
+ uinfo->name = "bnx2x_cnic";
}
uinfo->mem[1].memtype = UIO_MEM_LOGICAL;
@@ -880,6 +975,152 @@ error:
return ret;
}
+static int cnic_alloc_bnx2x_context(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ int ctx_blk_size = cp->ethdev->ctx_blk_size;
+ int total_mem, blks, i, cid_space;
+
+ if (BNX2X_ISCSI_START_CID < ethdev->starting_cid)
+ return -EINVAL;
+
+ cid_space = MAX_ISCSI_TBL_SZ +
+ (BNX2X_ISCSI_START_CID - ethdev->starting_cid);
+
+ total_mem = BNX2X_CONTEXT_MEM_SIZE * cid_space;
+ blks = total_mem / ctx_blk_size;
+ if (total_mem % ctx_blk_size)
+ blks++;
+
+ if (blks > cp->ethdev->ctx_tbl_len)
+ return -ENOMEM;
+
+ cp->ctx_arr = kzalloc(blks * sizeof(struct cnic_ctx), GFP_KERNEL);
+ if (cp->ctx_arr == NULL)
+ return -ENOMEM;
+
+ cp->ctx_blks = blks;
+ cp->ctx_blk_size = ctx_blk_size;
+ if (BNX2X_CHIP_IS_E1H(cp->chip_id))
+ cp->ctx_align = 0;
+ else
+ cp->ctx_align = ctx_blk_size;
+
+ cp->cids_per_blk = ctx_blk_size / BNX2X_CONTEXT_MEM_SIZE;
+
+ for (i = 0; i < blks; i++) {
+ cp->ctx_arr[i].ctx =
+ dma_alloc_coherent(&dev->pcidev->dev, cp->ctx_blk_size,
+ &cp->ctx_arr[i].mapping,
+ GFP_KERNEL);
+ if (cp->ctx_arr[i].ctx == NULL)
+ return -ENOMEM;
+
+ if (cp->ctx_align && cp->ctx_blk_size == ctx_blk_size) {
+ if (cp->ctx_arr[i].mapping & (cp->ctx_align - 1)) {
+ cnic_free_context(dev);
+ cp->ctx_blk_size += cp->ctx_align;
+ i = -1;
+ continue;
+ }
+ }
+ }
+ return 0;
+}
+
+static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int i, j, n, ret, pages;
+ struct cnic_dma *kwq_16_dma = &cp->kwq_16_data_info;
+
+ cp->iscsi_tbl = kzalloc(sizeof(struct cnic_iscsi) * MAX_ISCSI_TBL_SZ,
+ GFP_KERNEL);
+ if (!cp->iscsi_tbl)
+ goto error;
+
+ cp->ctx_tbl = kzalloc(sizeof(struct cnic_context) *
+ MAX_CNIC_L5_CONTEXT, GFP_KERNEL);
+ if (!cp->ctx_tbl)
+ goto error;
+
+ for (i = 0; i < MAX_ISCSI_TBL_SZ; i++) {
+ cp->ctx_tbl[i].proto.iscsi = &cp->iscsi_tbl[i];
+ cp->ctx_tbl[i].ulp_proto_id = CNIC_ULP_ISCSI;
+ }
+
+ pages = PAGE_ALIGN(MAX_CNIC_L5_CONTEXT * CNIC_KWQ16_DATA_SIZE) /
+ PAGE_SIZE;
+
+ ret = cnic_alloc_dma(dev, kwq_16_dma, pages, 0);
+ if (ret)
+ return -ENOMEM;
+
+ n = PAGE_SIZE / CNIC_KWQ16_DATA_SIZE;
+ for (i = 0, j = 0; i < MAX_ISCSI_TBL_SZ; i++) {
+ long off = CNIC_KWQ16_DATA_SIZE * (i % n);
+
+ cp->ctx_tbl[i].kwqe_data = kwq_16_dma->pg_arr[j] + off;
+ cp->ctx_tbl[i].kwqe_data_mapping = kwq_16_dma->pg_map_arr[j] +
+ off;
+
+ if ((i % n) == (n - 1))
+ j++;
+ }
+
+ ret = cnic_alloc_dma(dev, &cp->kcq_info, KCQ_PAGE_CNT, 0);
+ if (ret)
+ goto error;
+ cp->kcq = (struct kcqe **) cp->kcq_info.pg_arr;
+
+ for (i = 0; i < KCQ_PAGE_CNT; i++) {
+ struct bnx2x_bd_chain_next *next =
+ (struct bnx2x_bd_chain_next *)
+ &cp->kcq[i][MAX_KCQE_CNT];
+ int j = i + 1;
+
+ if (j >= KCQ_PAGE_CNT)
+ j = 0;
+ next->addr_hi = (u64) cp->kcq_info.pg_map_arr[j] >> 32;
+ next->addr_lo = cp->kcq_info.pg_map_arr[j] & 0xffffffff;
+ }
+
+ pages = PAGE_ALIGN(BNX2X_ISCSI_NUM_CONNECTIONS *
+ BNX2X_ISCSI_CONN_BUF_SIZE) / PAGE_SIZE;
+ ret = cnic_alloc_dma(dev, &cp->conn_buf_info, pages, 1);
+ if (ret)
+ goto error;
+
+ pages = PAGE_ALIGN(BNX2X_ISCSI_GLB_BUF_SIZE) / PAGE_SIZE;
+ ret = cnic_alloc_dma(dev, &cp->gbl_buf_info, pages, 0);
+ if (ret)
+ goto error;
+
+ ret = cnic_alloc_bnx2x_context(dev);
+ if (ret)
+ goto error;
+
+ cp->bnx2x_status_blk = cp->status_blk;
+ cp->bnx2x_def_status_blk = cp->ethdev->irq_arr[1].status_blk;
+
+ cp->l2_rx_ring_size = 15;
+
+ ret = cnic_alloc_l2_rings(dev, 4);
+ if (ret)
+ goto error;
+
+ ret = cnic_alloc_uio(dev);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ cnic_free_resc(dev);
+ return -ENOMEM;
+}
+
static inline u32 cnic_kwq_avail(struct cnic_local *cp)
{
return cp->max_kwq_idx -
@@ -921,6 +1162,880 @@ static int cnic_submit_bnx2_kwqes(struct cnic_dev *dev, struct kwqe *wqes[],
return 0;
}
+static void *cnic_get_kwqe_16_data(struct cnic_local *cp, u32 l5_cid,
+ union l5cm_specific_data *l5_data)
+{
+ struct cnic_context *ctx = &cp->ctx_tbl[l5_cid];
+ dma_addr_t map;
+
+ map = ctx->kwqe_data_mapping;
+ l5_data->phy_address.lo = (u64) map & 0xffffffff;
+ l5_data->phy_address.hi = (u64) map >> 32;
+ return ctx->kwqe_data;
+}
+
+static int cnic_submit_kwqe_16(struct cnic_dev *dev, u32 cmd, u32 cid,
+ u32 type, union l5cm_specific_data *l5_data)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct l5cm_spe kwqe;
+ struct kwqe_16 *kwq[1];
+ int ret;
+
+ kwqe.hdr.conn_and_cmd_data =
+ cpu_to_le32(((cmd << SPE_HDR_CMD_ID_SHIFT) |
+ BNX2X_HW_CID(cid, cp->func)));
+ kwqe.hdr.type = cpu_to_le16(type);
+ kwqe.hdr.reserved = 0;
+ kwqe.data.phy_address.lo = cpu_to_le32(l5_data->phy_address.lo);
+ kwqe.data.phy_address.hi = cpu_to_le32(l5_data->phy_address.hi);
+
+ kwq[0] = (struct kwqe_16 *) &kwqe;
+
+ spin_lock_bh(&cp->cnic_ulp_lock);
+ ret = cp->ethdev->drv_submit_kwqes_16(dev->netdev, kwq, 1);
+ spin_unlock_bh(&cp->cnic_ulp_lock);
+
+ if (ret == 1)
+ return 0;
+
+ return -EBUSY;
+}
+
+static void cnic_reply_bnx2x_kcqes(struct cnic_dev *dev, int ulp_type,
+ struct kcqe *cqes[], u32 num_cqes)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_ulp_ops *ulp_ops;
+
+ rcu_read_lock();
+ ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]);
+ if (likely(ulp_ops)) {
+ ulp_ops->indicate_kcqes(cp->ulp_handle[ulp_type],
+ cqes, num_cqes);
+ }
+ rcu_read_unlock();
+}
+
+static int cnic_bnx2x_iscsi_init1(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct iscsi_kwqe_init1 *req1 = (struct iscsi_kwqe_init1 *) kwqe;
+ int func = cp->func, pages;
+ int hq_bds;
+
+ cp->num_iscsi_tasks = req1->num_tasks_per_conn;
+ cp->num_ccells = req1->num_ccells_per_conn;
+ cp->task_array_size = BNX2X_ISCSI_TASK_CONTEXT_SIZE *
+ cp->num_iscsi_tasks;
+ cp->r2tq_size = cp->num_iscsi_tasks * BNX2X_ISCSI_MAX_PENDING_R2TS *
+ BNX2X_ISCSI_R2TQE_SIZE;
+ cp->hq_size = cp->num_ccells * BNX2X_ISCSI_HQ_BD_SIZE;
+ pages = PAGE_ALIGN(cp->hq_size) / PAGE_SIZE;
+ hq_bds = pages * (PAGE_SIZE / BNX2X_ISCSI_HQ_BD_SIZE);
+ cp->num_cqs = req1->num_cqs;
+
+ if (!dev->max_iscsi_conn)
+ return 0;
+
+ /* init Tstorm RAM */
+ CNIC_WR16(dev, BAR_TSTRORM_INTMEM + TSTORM_ISCSI_RQ_SIZE_OFFSET(func),
+ req1->rq_num_wqes);
+ CNIC_WR16(dev, BAR_TSTRORM_INTMEM + TSTORM_ISCSI_PAGE_SIZE_OFFSET(func),
+ PAGE_SIZE);
+ CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT);
+ CNIC_WR16(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func),
+ req1->num_tasks_per_conn);
+
+ /* init Ustorm RAM */
+ CNIC_WR16(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(func),
+ req1->rq_buffer_size);
+ CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_PAGE_SIZE_OFFSET(func),
+ PAGE_SIZE);
+ CNIC_WR8(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT);
+ CNIC_WR16(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_NUM_OF_TASKS_OFFSET(func),
+ req1->num_tasks_per_conn);
+ CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_RQ_SIZE_OFFSET(func),
+ req1->rq_num_wqes);
+ CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_CQ_SIZE_OFFSET(func),
+ req1->cq_num_wqes);
+ CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_R2TQ_SIZE_OFFSET(func),
+ cp->num_iscsi_tasks * BNX2X_ISCSI_MAX_PENDING_R2TS);
+
+ /* init Xstorm RAM */
+ CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_PAGE_SIZE_OFFSET(func),
+ PAGE_SIZE);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT);
+ CNIC_WR16(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func),
+ req1->num_tasks_per_conn);
+ CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_HQ_SIZE_OFFSET(func),
+ hq_bds);
+ CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_SQ_SIZE_OFFSET(func),
+ req1->num_tasks_per_conn);
+ CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_R2TQ_SIZE_OFFSET(func),
+ cp->num_iscsi_tasks * BNX2X_ISCSI_MAX_PENDING_R2TS);
+
+ /* init Cstorm RAM */
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_PAGE_SIZE_OFFSET(func),
+ PAGE_SIZE);
+ CNIC_WR8(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), PAGE_SHIFT);
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func),
+ req1->num_tasks_per_conn);
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_CQ_SIZE_OFFSET(func),
+ req1->cq_num_wqes);
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_HQ_SIZE_OFFSET(func),
+ hq_bds);
+
+ return 0;
+}
+
+static int cnic_bnx2x_iscsi_init2(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct iscsi_kwqe_init2 *req2 = (struct iscsi_kwqe_init2 *) kwqe;
+ struct cnic_local *cp = dev->cnic_priv;
+ int func = cp->func;
+ struct iscsi_kcqe kcqe;
+ struct kcqe *cqes[1];
+
+ memset(&kcqe, 0, sizeof(kcqe));
+ if (!dev->max_iscsi_conn) {
+ kcqe.completion_status =
+ ISCSI_KCQE_COMPLETION_STATUS_ISCSI_NOT_SUPPORTED;
+ goto done;
+ }
+
+ CNIC_WR(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_ERROR_BITMAP_OFFSET(func), req2->error_bit_map[0]);
+ CNIC_WR(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_ERROR_BITMAP_OFFSET(func) + 4,
+ req2->error_bit_map[1]);
+
+ CNIC_WR16(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(func), req2->max_cq_sqn);
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_ERROR_BITMAP_OFFSET(func), req2->error_bit_map[0]);
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_ERROR_BITMAP_OFFSET(func) + 4,
+ req2->error_bit_map[1]);
+
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(func), req2->max_cq_sqn);
+
+ kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_SUCCESS;
+
+done:
+ kcqe.op_code = ISCSI_KCQE_OPCODE_INIT;
+ cqes[0] = (struct kcqe *) &kcqe;
+ cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_ISCSI, cqes, 1);
+
+ return 0;
+}
+
+static void cnic_free_bnx2x_conn_resc(struct cnic_dev *dev, u32 l5_cid)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_context *ctx = &cp->ctx_tbl[l5_cid];
+
+ if (ctx->ulp_proto_id == CNIC_ULP_ISCSI) {
+ struct cnic_iscsi *iscsi = ctx->proto.iscsi;
+
+ cnic_free_dma(dev, &iscsi->hq_info);
+ cnic_free_dma(dev, &iscsi->r2tq_info);
+ cnic_free_dma(dev, &iscsi->task_array_info);
+ }
+ cnic_free_id(&cp->cid_tbl, ctx->cid);
+ ctx->cid = 0;
+}
+
+static int cnic_alloc_bnx2x_conn_resc(struct cnic_dev *dev, u32 l5_cid)
+{
+ u32 cid;
+ int ret, pages;
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_context *ctx = &cp->ctx_tbl[l5_cid];
+ struct cnic_iscsi *iscsi = ctx->proto.iscsi;
+
+ cid = cnic_alloc_new_id(&cp->cid_tbl);
+ if (cid == -1) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ctx->cid = cid;
+ pages = PAGE_ALIGN(cp->task_array_size) / PAGE_SIZE;
+
+ ret = cnic_alloc_dma(dev, &iscsi->task_array_info, pages, 1);
+ if (ret)
+ goto error;
+
+ pages = PAGE_ALIGN(cp->r2tq_size) / PAGE_SIZE;
+ ret = cnic_alloc_dma(dev, &iscsi->r2tq_info, pages, 1);
+ if (ret)
+ goto error;
+
+ pages = PAGE_ALIGN(cp->hq_size) / PAGE_SIZE;
+ ret = cnic_alloc_dma(dev, &iscsi->hq_info, pages, 1);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ cnic_free_bnx2x_conn_resc(dev, l5_cid);
+ return ret;
+}
+
+static void *cnic_get_bnx2x_ctx(struct cnic_dev *dev, u32 cid, int init,
+ struct regpair *ctx_addr)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ int blk = (cid - ethdev->starting_cid) / cp->cids_per_blk;
+ int off = (cid - ethdev->starting_cid) % cp->cids_per_blk;
+ unsigned long align_off = 0;
+ dma_addr_t ctx_map;
+ void *ctx;
+
+ if (cp->ctx_align) {
+ unsigned long mask = cp->ctx_align - 1;
+
+ if (cp->ctx_arr[blk].mapping & mask)
+ align_off = cp->ctx_align -
+ (cp->ctx_arr[blk].mapping & mask);
+ }
+ ctx_map = cp->ctx_arr[blk].mapping + align_off +
+ (off * BNX2X_CONTEXT_MEM_SIZE);
+ ctx = cp->ctx_arr[blk].ctx + align_off +
+ (off * BNX2X_CONTEXT_MEM_SIZE);
+ if (init)
+ memset(ctx, 0, BNX2X_CONTEXT_MEM_SIZE);
+
+ ctx_addr->lo = ctx_map & 0xffffffff;
+ ctx_addr->hi = (u64) ctx_map >> 32;
+ return ctx;
+}
+
+static int cnic_setup_bnx2x_ctx(struct cnic_dev *dev, struct kwqe *wqes[],
+ u32 num)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct iscsi_kwqe_conn_offload1 *req1 =
+ (struct iscsi_kwqe_conn_offload1 *) wqes[0];
+ struct iscsi_kwqe_conn_offload2 *req2 =
+ (struct iscsi_kwqe_conn_offload2 *) wqes[1];
+ struct iscsi_kwqe_conn_offload3 *req3;
+ struct cnic_context *ctx = &cp->ctx_tbl[req1->iscsi_conn_id];
+ struct cnic_iscsi *iscsi = ctx->proto.iscsi;
+ u32 cid = ctx->cid;
+ u32 hw_cid = BNX2X_HW_CID(cid, cp->func);
+ struct iscsi_context *ictx;
+ struct regpair context_addr;
+ int i, j, n = 2, n_max;
+
+ ctx->ctx_flags = 0;
+ if (!req2->num_additional_wqes)
+ return -EINVAL;
+
+ n_max = req2->num_additional_wqes + 2;
+
+ ictx = cnic_get_bnx2x_ctx(dev, cid, 1, &context_addr);
+ if (ictx == NULL)
+ return -ENOMEM;
+
+ req3 = (struct iscsi_kwqe_conn_offload3 *) wqes[n++];
+
+ ictx->xstorm_ag_context.hq_prod = 1;
+
+ ictx->xstorm_st_context.iscsi.first_burst_length =
+ ISCSI_DEF_FIRST_BURST_LEN;
+ ictx->xstorm_st_context.iscsi.max_send_pdu_length =
+ ISCSI_DEF_MAX_RECV_SEG_LEN;
+ ictx->xstorm_st_context.iscsi.sq_pbl_base.lo =
+ req1->sq_page_table_addr_lo;
+ ictx->xstorm_st_context.iscsi.sq_pbl_base.hi =
+ req1->sq_page_table_addr_hi;
+ ictx->xstorm_st_context.iscsi.sq_curr_pbe.lo = req2->sq_first_pte.hi;
+ ictx->xstorm_st_context.iscsi.sq_curr_pbe.hi = req2->sq_first_pte.lo;
+ ictx->xstorm_st_context.iscsi.hq_pbl_base.lo =
+ iscsi->hq_info.pgtbl_map & 0xffffffff;
+ ictx->xstorm_st_context.iscsi.hq_pbl_base.hi =
+ (u64) iscsi->hq_info.pgtbl_map >> 32;
+ ictx->xstorm_st_context.iscsi.hq_curr_pbe_base.lo =
+ iscsi->hq_info.pgtbl[0];
+ ictx->xstorm_st_context.iscsi.hq_curr_pbe_base.hi =
+ iscsi->hq_info.pgtbl[1];
+ ictx->xstorm_st_context.iscsi.r2tq_pbl_base.lo =
+ iscsi->r2tq_info.pgtbl_map & 0xffffffff;
+ ictx->xstorm_st_context.iscsi.r2tq_pbl_base.hi =
+ (u64) iscsi->r2tq_info.pgtbl_map >> 32;
+ ictx->xstorm_st_context.iscsi.r2tq_curr_pbe_base.lo =
+ iscsi->r2tq_info.pgtbl[0];
+ ictx->xstorm_st_context.iscsi.r2tq_curr_pbe_base.hi =
+ iscsi->r2tq_info.pgtbl[1];
+ ictx->xstorm_st_context.iscsi.task_pbl_base.lo =
+ iscsi->task_array_info.pgtbl_map & 0xffffffff;
+ ictx->xstorm_st_context.iscsi.task_pbl_base.hi =
+ (u64) iscsi->task_array_info.pgtbl_map >> 32;
+ ictx->xstorm_st_context.iscsi.task_pbl_cache_idx =
+ BNX2X_ISCSI_PBL_NOT_CACHED;
+ ictx->xstorm_st_context.iscsi.flags.flags |=
+ XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA;
+ ictx->xstorm_st_context.iscsi.flags.flags |=
+ XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T;
+
+ ictx->tstorm_st_context.iscsi.hdr_bytes_2_fetch = ISCSI_HEADER_SIZE;
+ /* TSTORM requires the base address of RQ DB & not PTE */
+ ictx->tstorm_st_context.iscsi.rq_db_phy_addr.lo =
+ req2->rq_page_table_addr_lo & PAGE_MASK;
+ ictx->tstorm_st_context.iscsi.rq_db_phy_addr.hi =
+ req2->rq_page_table_addr_hi;
+ ictx->tstorm_st_context.iscsi.iscsi_conn_id = req1->iscsi_conn_id;
+ ictx->tstorm_st_context.tcp.cwnd = 0x5A8;
+ ictx->tstorm_st_context.tcp.flags2 |=
+ TSTORM_TCP_ST_CONTEXT_SECTION_DA_EN;
+
+ ictx->timers_context.flags |= ISCSI_TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG;
+
+ ictx->ustorm_st_context.ring.rq.pbl_base.lo =
+ req2->rq_page_table_addr_lo;
+ ictx->ustorm_st_context.ring.rq.pbl_base.hi =
+ req2->rq_page_table_addr_hi;
+ ictx->ustorm_st_context.ring.rq.curr_pbe.lo = req3->qp_first_pte[0].hi;
+ ictx->ustorm_st_context.ring.rq.curr_pbe.hi = req3->qp_first_pte[0].lo;
+ ictx->ustorm_st_context.ring.r2tq.pbl_base.lo =
+ iscsi->r2tq_info.pgtbl_map & 0xffffffff;
+ ictx->ustorm_st_context.ring.r2tq.pbl_base.hi =
+ (u64) iscsi->r2tq_info.pgtbl_map >> 32;
+ ictx->ustorm_st_context.ring.r2tq.curr_pbe.lo =
+ iscsi->r2tq_info.pgtbl[0];
+ ictx->ustorm_st_context.ring.r2tq.curr_pbe.hi =
+ iscsi->r2tq_info.pgtbl[1];
+ ictx->ustorm_st_context.ring.cq_pbl_base.lo =
+ req1->cq_page_table_addr_lo;
+ ictx->ustorm_st_context.ring.cq_pbl_base.hi =
+ req1->cq_page_table_addr_hi;
+ ictx->ustorm_st_context.ring.cq[0].cq_sn = ISCSI_INITIAL_SN;
+ ictx->ustorm_st_context.ring.cq[0].curr_pbe.lo = req2->cq_first_pte.hi;
+ ictx->ustorm_st_context.ring.cq[0].curr_pbe.hi = req2->cq_first_pte.lo;
+ ictx->ustorm_st_context.task_pbe_cache_index =
+ BNX2X_ISCSI_PBL_NOT_CACHED;
+ ictx->ustorm_st_context.task_pdu_cache_index =
+ BNX2X_ISCSI_PDU_HEADER_NOT_CACHED;
+
+ for (i = 1, j = 1; i < cp->num_cqs; i++, j++) {
+ if (j == 3) {
+ if (n >= n_max)
+ break;
+ req3 = (struct iscsi_kwqe_conn_offload3 *) wqes[n++];
+ j = 0;
+ }
+ ictx->ustorm_st_context.ring.cq[i].cq_sn = ISCSI_INITIAL_SN;
+ ictx->ustorm_st_context.ring.cq[i].curr_pbe.lo =
+ req3->qp_first_pte[j].hi;
+ ictx->ustorm_st_context.ring.cq[i].curr_pbe.hi =
+ req3->qp_first_pte[j].lo;
+ }
+
+ ictx->ustorm_st_context.task_pbl_base.lo =
+ iscsi->task_array_info.pgtbl_map & 0xffffffff;
+ ictx->ustorm_st_context.task_pbl_base.hi =
+ (u64) iscsi->task_array_info.pgtbl_map >> 32;
+ ictx->ustorm_st_context.tce_phy_addr.lo =
+ iscsi->task_array_info.pgtbl[0];
+ ictx->ustorm_st_context.tce_phy_addr.hi =
+ iscsi->task_array_info.pgtbl[1];
+ ictx->ustorm_st_context.iscsi_conn_id = req1->iscsi_conn_id;
+ ictx->ustorm_st_context.num_cqs = cp->num_cqs;
+ ictx->ustorm_st_context.negotiated_rx |= ISCSI_DEF_MAX_RECV_SEG_LEN;
+ ictx->ustorm_st_context.negotiated_rx_and_flags |=
+ ISCSI_DEF_MAX_BURST_LEN;
+ ictx->ustorm_st_context.negotiated_rx |=
+ ISCSI_DEFAULT_MAX_OUTSTANDING_R2T <<
+ USTORM_ISCSI_ST_CONTEXT_MAX_OUTSTANDING_R2TS_SHIFT;
+
+ ictx->cstorm_st_context.hq_pbl_base.lo =
+ iscsi->hq_info.pgtbl_map & 0xffffffff;
+ ictx->cstorm_st_context.hq_pbl_base.hi =
+ (u64) iscsi->hq_info.pgtbl_map >> 32;
+ ictx->cstorm_st_context.hq_curr_pbe.lo = iscsi->hq_info.pgtbl[0];
+ ictx->cstorm_st_context.hq_curr_pbe.hi = iscsi->hq_info.pgtbl[1];
+ ictx->cstorm_st_context.task_pbl_base.lo =
+ iscsi->task_array_info.pgtbl_map & 0xffffffff;
+ ictx->cstorm_st_context.task_pbl_base.hi =
+ (u64) iscsi->task_array_info.pgtbl_map >> 32;
+ /* CSTORM and USTORM initialization is different, CSTORM requires
+ * CQ DB base & not PTE addr */
+ ictx->cstorm_st_context.cq_db_base.lo =
+ req1->cq_page_table_addr_lo & PAGE_MASK;
+ ictx->cstorm_st_context.cq_db_base.hi = req1->cq_page_table_addr_hi;
+ ictx->cstorm_st_context.iscsi_conn_id = req1->iscsi_conn_id;
+ ictx->cstorm_st_context.cq_proc_en_bit_map = (1 << cp->num_cqs) - 1;
+ for (i = 0; i < cp->num_cqs; i++) {
+ ictx->cstorm_st_context.cq_c_prod_sqn_arr.sqn[i] =
+ ISCSI_INITIAL_SN;
+ ictx->cstorm_st_context.cq_c_sqn_2_notify_arr.sqn[i] =
+ ISCSI_INITIAL_SN;
+ }
+
+ ictx->xstorm_ag_context.cdu_reserved =
+ CDU_RSRVD_VALUE_TYPE_A(hw_cid, CDU_REGION_NUMBER_XCM_AG,
+ ISCSI_CONNECTION_TYPE);
+ ictx->ustorm_ag_context.cdu_usage =
+ CDU_RSRVD_VALUE_TYPE_A(hw_cid, CDU_REGION_NUMBER_UCM_AG,
+ ISCSI_CONNECTION_TYPE);
+ return 0;
+
+}
+
+static int cnic_bnx2x_iscsi_ofld1(struct cnic_dev *dev, struct kwqe *wqes[],
+ u32 num, int *work)
+{
+ struct iscsi_kwqe_conn_offload1 *req1;
+ struct iscsi_kwqe_conn_offload2 *req2;
+ struct cnic_local *cp = dev->cnic_priv;
+ struct iscsi_kcqe kcqe;
+ struct kcqe *cqes[1];
+ u32 l5_cid;
+ int ret;
+
+ if (num < 2) {
+ *work = num;
+ return -EINVAL;
+ }
+
+ req1 = (struct iscsi_kwqe_conn_offload1 *) wqes[0];
+ req2 = (struct iscsi_kwqe_conn_offload2 *) wqes[1];
+ if ((num - 2) < req2->num_additional_wqes) {
+ *work = num;
+ return -EINVAL;
+ }
+ *work = 2 + req2->num_additional_wqes;;
+
+ l5_cid = req1->iscsi_conn_id;
+ if (l5_cid >= MAX_ISCSI_TBL_SZ)
+ return -EINVAL;
+
+ memset(&kcqe, 0, sizeof(kcqe));
+ kcqe.op_code = ISCSI_KCQE_OPCODE_OFFLOAD_CONN;
+ kcqe.iscsi_conn_id = l5_cid;
+ kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE;
+
+ if (atomic_inc_return(&cp->iscsi_conn) > dev->max_iscsi_conn) {
+ atomic_dec(&cp->iscsi_conn);
+ ret = 0;
+ goto done;
+ }
+ ret = cnic_alloc_bnx2x_conn_resc(dev, l5_cid);
+ if (ret) {
+ atomic_dec(&cp->iscsi_conn);
+ ret = 0;
+ goto done;
+ }
+ ret = cnic_setup_bnx2x_ctx(dev, wqes, num);
+ if (ret < 0) {
+ cnic_free_bnx2x_conn_resc(dev, l5_cid);
+ atomic_dec(&cp->iscsi_conn);
+ goto done;
+ }
+
+ kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_SUCCESS;
+ kcqe.iscsi_conn_context_id = BNX2X_HW_CID(cp->ctx_tbl[l5_cid].cid,
+ cp->func);
+
+done:
+ cqes[0] = (struct kcqe *) &kcqe;
+ cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_ISCSI, cqes, 1);
+ return ret;
+}
+
+
+static int cnic_bnx2x_iscsi_update(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct iscsi_kwqe_conn_update *req =
+ (struct iscsi_kwqe_conn_update *) kwqe;
+ void *data;
+ union l5cm_specific_data l5_data;
+ u32 l5_cid, cid = BNX2X_SW_CID(req->context_id);
+ int ret;
+
+ if (cnic_get_l5_cid(cp, cid, &l5_cid) != 0)
+ return -EINVAL;
+
+ data = cnic_get_kwqe_16_data(cp, l5_cid, &l5_data);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(data, kwqe, sizeof(struct kwqe));
+
+ ret = cnic_submit_kwqe_16(dev, ISCSI_RAMROD_CMD_ID_UPDATE_CONN,
+ req->context_id, ISCSI_CONNECTION_TYPE, &l5_data);
+ return ret;
+}
+
+static int cnic_bnx2x_iscsi_destroy(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct iscsi_kwqe_conn_destroy *req =
+ (struct iscsi_kwqe_conn_destroy *) kwqe;
+ union l5cm_specific_data l5_data;
+ u32 l5_cid = req->reserved0;
+ struct cnic_context *ctx = &cp->ctx_tbl[l5_cid];
+ int ret = 0;
+ struct iscsi_kcqe kcqe;
+ struct kcqe *cqes[1];
+
+ if (!(ctx->ctx_flags & CTX_FL_OFFLD_START))
+ goto skip_cfc_delete;
+
+ while (!time_after(jiffies, ctx->timestamp + (2 * HZ)))
+ msleep(250);
+
+ init_waitqueue_head(&ctx->waitq);
+ ctx->wait_cond = 0;
+ memset(&l5_data, 0, sizeof(l5_data));
+ ret = cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_CFC_DEL,
+ req->context_id,
+ ETH_CONNECTION_TYPE |
+ (1 << SPE_HDR_COMMON_RAMROD_SHIFT),
+ &l5_data);
+ if (ret == 0)
+ wait_event(ctx->waitq, ctx->wait_cond);
+
+skip_cfc_delete:
+ cnic_free_bnx2x_conn_resc(dev, l5_cid);
+
+ atomic_dec(&cp->iscsi_conn);
+
+ memset(&kcqe, 0, sizeof(kcqe));
+ kcqe.op_code = ISCSI_KCQE_OPCODE_DESTROY_CONN;
+ kcqe.iscsi_conn_id = l5_cid;
+ kcqe.completion_status = ISCSI_KCQE_COMPLETION_STATUS_SUCCESS;
+ kcqe.iscsi_conn_context_id = req->context_id;
+
+ cqes[0] = (struct kcqe *) &kcqe;
+ cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_ISCSI, cqes, 1);
+
+ return ret;
+}
+
+static void cnic_init_storm_conn_bufs(struct cnic_dev *dev,
+ struct l4_kwq_connect_req1 *kwqe1,
+ struct l4_kwq_connect_req3 *kwqe3,
+ struct l5cm_active_conn_buffer *conn_buf)
+{
+ struct l5cm_conn_addr_params *conn_addr = &conn_buf->conn_addr_buf;
+ struct l5cm_xstorm_conn_buffer *xstorm_buf =
+ &conn_buf->xstorm_conn_buffer;
+ struct l5cm_tstorm_conn_buffer *tstorm_buf =
+ &conn_buf->tstorm_conn_buffer;
+ struct regpair context_addr;
+ u32 cid = BNX2X_SW_CID(kwqe1->cid);
+ struct in6_addr src_ip, dst_ip;
+ int i;
+ u32 *addrp;
+
+ addrp = (u32 *) &conn_addr->local_ip_addr;
+ for (i = 0; i < 4; i++, addrp++)
+ src_ip.in6_u.u6_addr32[i] = cpu_to_be32(*addrp);
+
+ addrp = (u32 *) &conn_addr->remote_ip_addr;
+ for (i = 0; i < 4; i++, addrp++)
+ dst_ip.in6_u.u6_addr32[i] = cpu_to_be32(*addrp);
+
+ cnic_get_bnx2x_ctx(dev, cid, 0, &context_addr);
+
+ xstorm_buf->context_addr.hi = context_addr.hi;
+ xstorm_buf->context_addr.lo = context_addr.lo;
+ xstorm_buf->mss = 0xffff;
+ xstorm_buf->rcv_buf = kwqe3->rcv_buf;
+ if (kwqe1->tcp_flags & L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE)
+ xstorm_buf->params |= L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE;
+ xstorm_buf->pseudo_header_checksum =
+ swab16(~csum_ipv6_magic(&src_ip, &dst_ip, 0, IPPROTO_TCP, 0));
+
+ if (!(kwqe1->tcp_flags & L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK))
+ tstorm_buf->params |=
+ L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE;
+ if (kwqe3->ka_timeout) {
+ tstorm_buf->ka_enable = 1;
+ tstorm_buf->ka_timeout = kwqe3->ka_timeout;
+ tstorm_buf->ka_interval = kwqe3->ka_interval;
+ tstorm_buf->ka_max_probe_count = kwqe3->ka_max_probe_count;
+ }
+ tstorm_buf->rcv_buf = kwqe3->rcv_buf;
+ tstorm_buf->snd_buf = kwqe3->snd_buf;
+ tstorm_buf->max_rt_time = 0xffffffff;
+}
+
+static void cnic_init_bnx2x_mac(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int func = CNIC_FUNC(cp);
+ u8 *mac = dev->mac_addr;
+
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_MAC_ADDR0_OFFSET(func), mac[0]);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_MAC_ADDR1_OFFSET(func), mac[1]);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_MAC_ADDR2_OFFSET(func), mac[2]);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_MAC_ADDR3_OFFSET(func), mac[3]);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_MAC_ADDR4_OFFSET(func), mac[4]);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_MAC_ADDR5_OFFSET(func), mac[5]);
+
+ CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(func), mac[5]);
+ CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_TCP_VARS_LSB_LOCAL_MAC_ADDR_OFFSET(func) + 1,
+ mac[4]);
+ CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func), mac[3]);
+ CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func) + 1,
+ mac[2]);
+ CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func) + 2,
+ mac[1]);
+ CNIC_WR8(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_TCP_VARS_MSB_LOCAL_MAC_ADDR_OFFSET(func) + 3,
+ mac[0]);
+}
+
+static void cnic_bnx2x_set_tcp_timestamp(struct cnic_dev *dev, int tcp_ts)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u8 xstorm_flags = XSTORM_L5CM_TCP_FLAGS_WND_SCL_EN;
+ u16 tstorm_flags = 0;
+
+ if (tcp_ts) {
+ xstorm_flags |= XSTORM_L5CM_TCP_FLAGS_TS_ENABLED;
+ tstorm_flags |= TSTORM_L5CM_TCP_FLAGS_TS_ENABLED;
+ }
+
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(cp->func), xstorm_flags);
+
+ CNIC_WR16(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(cp->func), tstorm_flags);
+}
+
+static int cnic_bnx2x_connect(struct cnic_dev *dev, struct kwqe *wqes[],
+ u32 num, int *work)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct l4_kwq_connect_req1 *kwqe1 =
+ (struct l4_kwq_connect_req1 *) wqes[0];
+ struct l4_kwq_connect_req3 *kwqe3;
+ struct l5cm_active_conn_buffer *conn_buf;
+ struct l5cm_conn_addr_params *conn_addr;
+ union l5cm_specific_data l5_data;
+ u32 l5_cid = kwqe1->pg_cid;
+ struct cnic_sock *csk = &cp->csk_tbl[l5_cid];
+ struct cnic_context *ctx = &cp->ctx_tbl[l5_cid];
+ int ret;
+
+ if (num < 2) {
+ *work = num;
+ return -EINVAL;
+ }
+
+ if (kwqe1->conn_flags & L4_KWQ_CONNECT_REQ1_IP_V6)
+ *work = 3;
+ else
+ *work = 2;
+
+ if (num < *work) {
+ *work = num;
+ return -EINVAL;
+ }
+
+ if (sizeof(*conn_buf) > CNIC_KWQ16_DATA_SIZE) {
+ printk(KERN_ERR PFX "%s: conn_buf size too big\n",
+ dev->netdev->name);
+ return -ENOMEM;
+ }
+ conn_buf = cnic_get_kwqe_16_data(cp, l5_cid, &l5_data);
+ if (!conn_buf)
+ return -ENOMEM;
+
+ memset(conn_buf, 0, sizeof(*conn_buf));
+
+ conn_addr = &conn_buf->conn_addr_buf;
+ conn_addr->remote_addr_0 = csk->ha[0];
+ conn_addr->remote_addr_1 = csk->ha[1];
+ conn_addr->remote_addr_2 = csk->ha[2];
+ conn_addr->remote_addr_3 = csk->ha[3];
+ conn_addr->remote_addr_4 = csk->ha[4];
+ conn_addr->remote_addr_5 = csk->ha[5];
+
+ if (kwqe1->conn_flags & L4_KWQ_CONNECT_REQ1_IP_V6) {
+ struct l4_kwq_connect_req2 *kwqe2 =
+ (struct l4_kwq_connect_req2 *) wqes[1];
+
+ conn_addr->local_ip_addr.ip_addr_hi_hi = kwqe2->src_ip_v6_4;
+ conn_addr->local_ip_addr.ip_addr_hi_lo = kwqe2->src_ip_v6_3;
+ conn_addr->local_ip_addr.ip_addr_lo_hi = kwqe2->src_ip_v6_2;
+
+ conn_addr->remote_ip_addr.ip_addr_hi_hi = kwqe2->dst_ip_v6_4;
+ conn_addr->remote_ip_addr.ip_addr_hi_lo = kwqe2->dst_ip_v6_3;
+ conn_addr->remote_ip_addr.ip_addr_lo_hi = kwqe2->dst_ip_v6_2;
+ conn_addr->params |= L5CM_CONN_ADDR_PARAMS_IP_VERSION;
+ }
+ kwqe3 = (struct l4_kwq_connect_req3 *) wqes[*work - 1];
+
+ conn_addr->local_ip_addr.ip_addr_lo_lo = kwqe1->src_ip;
+ conn_addr->remote_ip_addr.ip_addr_lo_lo = kwqe1->dst_ip;
+ conn_addr->local_tcp_port = kwqe1->src_port;
+ conn_addr->remote_tcp_port = kwqe1->dst_port;
+
+ conn_addr->pmtu = kwqe3->pmtu;
+ cnic_init_storm_conn_bufs(dev, kwqe1, kwqe3, conn_buf);
+
+ CNIC_WR16(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_VLAN_OFFSET(cp->func), csk->vlan_id);
+
+ cnic_bnx2x_set_tcp_timestamp(dev,
+ kwqe1->tcp_flags & L4_KWQ_CONNECT_REQ1_TIME_STAMP);
+
+ ret = cnic_submit_kwqe_16(dev, L5CM_RAMROD_CMD_ID_TCP_CONNECT,
+ kwqe1->cid, ISCSI_CONNECTION_TYPE, &l5_data);
+ if (!ret)
+ ctx->ctx_flags |= CTX_FL_OFFLD_START;
+
+ return ret;
+}
+
+static int cnic_bnx2x_close(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct l4_kwq_close_req *req = (struct l4_kwq_close_req *) kwqe;
+ union l5cm_specific_data l5_data;
+ int ret;
+
+ memset(&l5_data, 0, sizeof(l5_data));
+ ret = cnic_submit_kwqe_16(dev, L5CM_RAMROD_CMD_ID_CLOSE,
+ req->cid, ISCSI_CONNECTION_TYPE, &l5_data);
+ return ret;
+}
+
+static int cnic_bnx2x_reset(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct l4_kwq_reset_req *req = (struct l4_kwq_reset_req *) kwqe;
+ union l5cm_specific_data l5_data;
+ int ret;
+
+ memset(&l5_data, 0, sizeof(l5_data));
+ ret = cnic_submit_kwqe_16(dev, L5CM_RAMROD_CMD_ID_ABORT,
+ req->cid, ISCSI_CONNECTION_TYPE, &l5_data);
+ return ret;
+}
+static int cnic_bnx2x_offload_pg(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct l4_kwq_offload_pg *req = (struct l4_kwq_offload_pg *) kwqe;
+ struct l4_kcq kcqe;
+ struct kcqe *cqes[1];
+
+ memset(&kcqe, 0, sizeof(kcqe));
+ kcqe.pg_host_opaque = req->host_opaque;
+ kcqe.pg_cid = req->host_opaque;
+ kcqe.op_code = L4_KCQE_OPCODE_VALUE_OFFLOAD_PG;
+ cqes[0] = (struct kcqe *) &kcqe;
+ cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_L4, cqes, 1);
+ return 0;
+}
+
+static int cnic_bnx2x_update_pg(struct cnic_dev *dev, struct kwqe *kwqe)
+{
+ struct l4_kwq_update_pg *req = (struct l4_kwq_update_pg *) kwqe;
+ struct l4_kcq kcqe;
+ struct kcqe *cqes[1];
+
+ memset(&kcqe, 0, sizeof(kcqe));
+ kcqe.pg_host_opaque = req->pg_host_opaque;
+ kcqe.pg_cid = req->pg_cid;
+ kcqe.op_code = L4_KCQE_OPCODE_VALUE_UPDATE_PG;
+ cqes[0] = (struct kcqe *) &kcqe;
+ cnic_reply_bnx2x_kcqes(dev, CNIC_ULP_L4, cqes, 1);
+ return 0;
+}
+
+static int cnic_submit_bnx2x_kwqes(struct cnic_dev *dev, struct kwqe *wqes[],
+ u32 num_wqes)
+{
+ int i, work, ret;
+ u32 opcode;
+ struct kwqe *kwqe;
+
+ if (!test_bit(CNIC_F_CNIC_UP, &dev->flags))
+ return -EAGAIN; /* bnx2 is down */
+
+ for (i = 0; i < num_wqes; ) {
+ kwqe = wqes[i];
+ opcode = KWQE_OPCODE(kwqe->kwqe_op_flag);
+ work = 1;
+
+ switch (opcode) {
+ case ISCSI_KWQE_OPCODE_INIT1:
+ ret = cnic_bnx2x_iscsi_init1(dev, kwqe);
+ break;
+ case ISCSI_KWQE_OPCODE_INIT2:
+ ret = cnic_bnx2x_iscsi_init2(dev, kwqe);
+ break;
+ case ISCSI_KWQE_OPCODE_OFFLOAD_CONN1:
+ ret = cnic_bnx2x_iscsi_ofld1(dev, &wqes[i],
+ num_wqes - i, &work);
+ break;
+ case ISCSI_KWQE_OPCODE_UPDATE_CONN:
+ ret = cnic_bnx2x_iscsi_update(dev, kwqe);
+ break;
+ case ISCSI_KWQE_OPCODE_DESTROY_CONN:
+ ret = cnic_bnx2x_iscsi_destroy(dev, kwqe);
+ break;
+ case L4_KWQE_OPCODE_VALUE_CONNECT1:
+ ret = cnic_bnx2x_connect(dev, &wqes[i], num_wqes - i,
+ &work);
+ break;
+ case L4_KWQE_OPCODE_VALUE_CLOSE:
+ ret = cnic_bnx2x_close(dev, kwqe);
+ break;
+ case L4_KWQE_OPCODE_VALUE_RESET:
+ ret = cnic_bnx2x_reset(dev, kwqe);
+ break;
+ case L4_KWQE_OPCODE_VALUE_OFFLOAD_PG:
+ ret = cnic_bnx2x_offload_pg(dev, kwqe);
+ break;
+ case L4_KWQE_OPCODE_VALUE_UPDATE_PG:
+ ret = cnic_bnx2x_update_pg(dev, kwqe);
+ break;
+ case L4_KWQE_OPCODE_VALUE_UPLOAD_PG:
+ ret = 0;
+ break;
+ default:
+ ret = 0;
+ printk(KERN_ERR PFX "%s: Unknown type of KWQE(0x%x)\n",
+ dev->netdev->name, opcode);
+ break;
+ }
+ if (ret < 0)
+ printk(KERN_ERR PFX "%s: KWQE(0x%x) failed\n",
+ dev->netdev->name, opcode);
+ i += work;
+ }
+ return 0;
+}
+
static void service_kcqes(struct cnic_dev *dev, int num_cqes)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -987,6 +2102,22 @@ static u16 cnic_bnx2_hw_idx(u16 idx)
return idx;
}
+static u16 cnic_bnx2x_next_idx(u16 idx)
+{
+ idx++;
+ if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT)
+ idx++;
+
+ return idx;
+}
+
+static u16 cnic_bnx2x_hw_idx(u16 idx)
+{
+ if ((idx & MAX_KCQE_CNT) == MAX_KCQE_CNT)
+ idx++;
+ return idx;
+}
+
static int cnic_get_kcqes(struct cnic_dev *dev, u16 hw_prod, u16 *sw_prod)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -1012,7 +2143,7 @@ static int cnic_get_kcqes(struct cnic_dev *dev, u16 hw_prod, u16 *sw_prod)
return last_cnt;
}
-static void cnic_chk_bnx2_pkt_rings(struct cnic_local *cp)
+static void cnic_chk_pkt_rings(struct cnic_local *cp)
{
u16 rx_cons = *cp->rx_cons_ptr;
u16 tx_cons = *cp->tx_cons_ptr;
@@ -1020,6 +2151,7 @@ static void cnic_chk_bnx2_pkt_rings(struct cnic_local *cp)
if (cp->tx_cons != tx_cons || cp->rx_cons != rx_cons) {
cp->tx_cons = tx_cons;
cp->rx_cons = rx_cons;
+
uio_event_notify(cp->cnic_uinfo);
}
}
@@ -1062,7 +2194,7 @@ done:
cp->kcq_prod_idx = sw_prod;
- cnic_chk_bnx2_pkt_rings(cp);
+ cnic_chk_pkt_rings(cp);
return status_idx;
}
@@ -1100,7 +2232,7 @@ done:
CNIC_WR16(dev, cp->kcq_io_addr, sw_prod);
cp->kcq_prod_idx = sw_prod;
- cnic_chk_bnx2_pkt_rings(cp);
+ cnic_chk_pkt_rings(cp);
cp->last_status_idx = status_idx;
CNIC_WR(dev, BNX2_PCICFG_INT_ACK_CMD, cp->int_num |
@@ -1125,6 +2257,91 @@ static irqreturn_t cnic_irq(int irq, void *dev_instance)
return IRQ_HANDLED;
}
+static inline void cnic_ack_bnx2x_int(struct cnic_dev *dev, u8 id, u8 storm,
+ u16 index, u8 op, u8 update)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 hc_addr = (HC_REG_COMMAND_REG + CNIC_PORT(cp) * 32 +
+ COMMAND_REG_INT_ACK);
+ struct igu_ack_register igu_ack;
+
+ igu_ack.status_block_index = index;
+ igu_ack.sb_id_and_flags =
+ ((id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) |
+ (storm << IGU_ACK_REGISTER_STORM_ID_SHIFT) |
+ (update << IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT) |
+ (op << IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT));
+
+ CNIC_WR(dev, hc_addr, (*(u32 *)&igu_ack));
+}
+
+static void cnic_ack_bnx2x_msix(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+
+ cnic_ack_bnx2x_int(dev, cp->status_blk_num, CSTORM_ID, 0,
+ IGU_INT_DISABLE, 0);
+}
+
+static void cnic_service_bnx2x_bh(unsigned long data)
+{
+ struct cnic_dev *dev = (struct cnic_dev *) data;
+ struct cnic_local *cp = dev->cnic_priv;
+ u16 hw_prod, sw_prod;
+ struct cstorm_status_block_c *sblk =
+ &cp->bnx2x_status_blk->c_status_block;
+ u32 status_idx = sblk->status_block_index;
+ int kcqe_cnt;
+
+ if (unlikely(!test_bit(CNIC_F_CNIC_UP, &dev->flags)))
+ return;
+
+ hw_prod = sblk->index_values[HC_INDEX_C_ISCSI_EQ_CONS];
+ hw_prod = cp->hw_idx(hw_prod);
+ sw_prod = cp->kcq_prod_idx;
+ while (sw_prod != hw_prod) {
+ kcqe_cnt = cnic_get_kcqes(dev, hw_prod, &sw_prod);
+ if (kcqe_cnt == 0)
+ goto done;
+
+ service_kcqes(dev, kcqe_cnt);
+
+ /* Tell compiler that sblk fields can change. */
+ barrier();
+ if (status_idx == sblk->status_block_index)
+ break;
+
+ status_idx = sblk->status_block_index;
+ hw_prod = sblk->index_values[HC_INDEX_C_ISCSI_EQ_CONS];
+ hw_prod = cp->hw_idx(hw_prod);
+ }
+
+done:
+ CNIC_WR16(dev, cp->kcq_io_addr, sw_prod + MAX_KCQ_IDX);
+ cnic_ack_bnx2x_int(dev, cp->status_blk_num, CSTORM_ID,
+ status_idx, IGU_INT_ENABLE, 1);
+
+ cp->kcq_prod_idx = sw_prod;
+ return;
+}
+
+static int cnic_service_bnx2x(void *data, void *status_blk)
+{
+ struct cnic_dev *dev = data;
+ struct cnic_local *cp = dev->cnic_priv;
+ u16 prod = cp->kcq_prod_idx & MAX_KCQ_IDX;
+
+ prefetch(cp->status_blk);
+ prefetch(&cp->kcq[KCQ_PG(prod)][KCQ_IDX(prod)]);
+
+ if (likely(test_bit(CNIC_F_CNIC_UP, &dev->flags)))
+ tasklet_schedule(&cp->cnic_irq_task);
+
+ cnic_chk_pkt_rings(cp);
+
+ return 0;
+}
+
static void cnic_ulp_stop(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -1197,6 +2414,19 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info)
cnic_put(dev);
break;
+ case CNIC_CTL_COMPLETION_CMD: {
+ u32 cid = BNX2X_SW_CID(info->data.comp.cid);
+ u32 l5_cid;
+ struct cnic_local *cp = dev->cnic_priv;
+
+ if (cnic_get_l5_cid(cp, cid, &l5_cid) == 0) {
+ struct cnic_context *ctx = &cp->ctx_tbl[l5_cid];
+
+ ctx->wait_cond = 1;
+ wake_up(&ctx->waitq);
+ }
+ break;
+ }
default:
return -EINVAL;
}
@@ -1872,6 +3102,8 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe)
/* fall through */
case L4_KCQE_OPCODE_VALUE_CLOSE_COMP:
case L4_KCQE_OPCODE_VALUE_RESET_COMP:
+ case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE:
+ case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD:
cp->close_conn(csk, opcode);
break;
@@ -1957,6 +3189,76 @@ static int cnic_cm_init_bnx2_hw(struct cnic_dev *dev)
return 0;
}
+static void cnic_close_bnx2x_conn(struct cnic_sock *csk, u32 opcode)
+{
+ struct cnic_dev *dev = csk->dev;
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_context *ctx = &cp->ctx_tbl[csk->l5_cid];
+ union l5cm_specific_data l5_data;
+ u32 cmd = 0;
+ int close_complete = 0;
+
+ switch (opcode) {
+ case L4_KCQE_OPCODE_VALUE_RESET_RECEIVED:
+ case L4_KCQE_OPCODE_VALUE_CLOSE_COMP:
+ case L4_KCQE_OPCODE_VALUE_RESET_COMP:
+ if (cnic_ready_to_close(csk, opcode))
+ cmd = L5CM_RAMROD_CMD_ID_SEARCHER_DELETE;
+ break;
+ case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE:
+ cmd = L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD;
+ break;
+ case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD:
+ close_complete = 1;
+ break;
+ }
+ if (cmd) {
+ memset(&l5_data, 0, sizeof(l5_data));
+
+ cnic_submit_kwqe_16(dev, cmd, csk->cid, ISCSI_CONNECTION_TYPE,
+ &l5_data);
+ } else if (close_complete) {
+ ctx->timestamp = jiffies;
+ cnic_close_conn(csk);
+ cnic_cm_upcall(cp, csk, csk->state);
+ }
+}
+
+static void cnic_cm_stop_bnx2x_hw(struct cnic_dev *dev)
+{
+}
+
+static int cnic_cm_init_bnx2x_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int func = CNIC_FUNC(cp);
+
+ cnic_init_bnx2x_mac(dev);
+ cnic_bnx2x_set_tcp_timestamp(dev, 1);
+
+ CNIC_WR16(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_LOCAL_VLAN_OFFSET(func), 0);
+
+ CNIC_WR(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_ENABLED_OFFSET(func), 1);
+ CNIC_WR(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_TCP_GLOBAL_DEL_ACK_COUNTER_MAX_COUNT_OFFSET(func),
+ DEF_MAX_DA_COUNT);
+
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_TCP_VARS_TTL_OFFSET(func), DEF_TTL);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_TCP_VARS_TOS_OFFSET(func), DEF_TOS);
+ CNIC_WR8(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_ISCSI_TCP_VARS_ADV_WND_SCL_OFFSET(func), 2);
+ CNIC_WR(dev, BAR_XSTRORM_INTMEM +
+ XSTORM_TCP_TX_SWS_TIMER_VAL_OFFSET(func), DEF_SWS_TIMER);
+
+ CNIC_WR(dev, BAR_TSTRORM_INTMEM + TSTORM_TCP_MAX_CWND_OFFSET(func),
+ DEF_MAX_CWND);
+ return 0;
+}
+
static int cnic_cm_open(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -2091,7 +3393,7 @@ static int cnic_init_bnx2_irq(struct cnic_dev *dev)
cp->bnx2_status_blk = cp->status_blk;
cp->last_status_idx = cp->bnx2_status_blk->status_idx;
- tasklet_init(&cp->cnic_irq_task, &cnic_service_bnx2_msix,
+ tasklet_init(&cp->cnic_irq_task, cnic_service_bnx2_msix,
(unsigned long) dev);
err = request_irq(ethdev->irq_arr[0].vector, cnic_irq, 0,
"cnic", dev);
@@ -2464,6 +3766,426 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev)
return 0;
}
+static void cnic_setup_bnx2x_context(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ u32 start_offset = ethdev->ctx_tbl_offset;
+ int i;
+
+ for (i = 0; i < cp->ctx_blks; i++) {
+ struct cnic_ctx *ctx = &cp->ctx_arr[i];
+ dma_addr_t map = ctx->mapping;
+
+ if (cp->ctx_align) {
+ unsigned long mask = cp->ctx_align - 1;
+
+ map = (map + mask) & ~mask;
+ }
+
+ cnic_ctx_tbl_wr(dev, start_offset + i, map);
+ }
+}
+
+static int cnic_init_bnx2x_irq(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct cnic_eth_dev *ethdev = cp->ethdev;
+ int err = 0;
+
+ tasklet_init(&cp->cnic_irq_task, cnic_service_bnx2x_bh,
+ (unsigned long) dev);
+ if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
+ err = request_irq(ethdev->irq_arr[0].vector, cnic_irq, 0,
+ "cnic", dev);
+ if (err)
+ tasklet_disable(&cp->cnic_irq_task);
+ }
+ return err;
+}
+
+static void cnic_enable_bnx2x_int(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u8 sb_id = cp->status_blk_num;
+ int port = CNIC_PORT(cp);
+
+ CNIC_WR8(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_SB_HC_TIMEOUT_C_OFFSET(port, sb_id,
+ HC_INDEX_C_ISCSI_EQ_CONS),
+ 64 / 12);
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_SB_HC_DISABLE_C_OFFSET(port, sb_id,
+ HC_INDEX_C_ISCSI_EQ_CONS), 0);
+}
+
+static void cnic_disable_bnx2x_int_sync(struct cnic_dev *dev)
+{
+}
+
+static void cnic_init_bnx2x_tx_ring(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ union eth_tx_bd_types *txbd = (union eth_tx_bd_types *) cp->l2_ring;
+ struct eth_context *context;
+ struct regpair context_addr;
+ dma_addr_t buf_map;
+ int func = CNIC_FUNC(cp);
+ int port = CNIC_PORT(cp);
+ int i;
+ int cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp));
+ u32 val;
+
+ memset(txbd, 0, BCM_PAGE_SIZE);
+
+ buf_map = cp->l2_buf_map;
+ for (i = 0; i < MAX_TX_DESC_CNT; i += 3, txbd += 3) {
+ struct eth_tx_start_bd *start_bd = &txbd->start_bd;
+ struct eth_tx_bd *reg_bd = &((txbd + 2)->reg_bd);
+
+ start_bd->addr_hi = cpu_to_le32((u64) buf_map >> 32);
+ start_bd->addr_lo = cpu_to_le32(buf_map & 0xffffffff);
+ reg_bd->addr_hi = start_bd->addr_hi;
+ reg_bd->addr_lo = start_bd->addr_lo + 0x10;
+ start_bd->nbytes = cpu_to_le16(0x10);
+ start_bd->nbd = cpu_to_le16(3);
+ start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD;
+ start_bd->general_data = (UNICAST_ADDRESS <<
+ ETH_TX_START_BD_ETH_ADDR_TYPE_SHIFT);
+ start_bd->general_data |= (1 << ETH_TX_START_BD_HDR_NBDS_SHIFT);
+
+ }
+ context = cnic_get_bnx2x_ctx(dev, BNX2X_ISCSI_L2_CID, 1, &context_addr);
+
+ val = (u64) cp->l2_ring_map >> 32;
+ txbd->next_bd.addr_hi = cpu_to_le32(val);
+
+ context->xstorm_st_context.tx_bd_page_base_hi = val;
+
+ val = (u64) cp->l2_ring_map & 0xffffffff;
+ txbd->next_bd.addr_lo = cpu_to_le32(val);
+
+ context->xstorm_st_context.tx_bd_page_base_lo = val;
+
+ context->cstorm_st_context.sb_index_number =
+ HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS;
+ context->cstorm_st_context.status_block_id = BNX2X_DEF_SB_ID;
+
+ context->xstorm_st_context.statistics_data = (cli |
+ XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE);
+
+ context->xstorm_ag_context.cdu_reserved =
+ CDU_RSRVD_VALUE_TYPE_A(BNX2X_HW_CID(BNX2X_ISCSI_L2_CID, func),
+ CDU_REGION_NUMBER_XCM_AG,
+ ETH_CONNECTION_TYPE);
+
+ /* reset xstorm per client statistics */
+ val = BAR_XSTRORM_INTMEM +
+ XSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli);
+ for (i = 0; i < sizeof(struct xstorm_per_client_stats) / 4; i++)
+ CNIC_WR(dev, val + i * 4, 0);
+
+ cp->tx_cons_ptr =
+ &cp->bnx2x_def_status_blk->c_def_status_block.index_values[
+ HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS];
+}
+
+static void cnic_init_bnx2x_rx_ring(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ struct eth_rx_bd *rxbd = (struct eth_rx_bd *) (cp->l2_ring +
+ BCM_PAGE_SIZE);
+ struct eth_rx_cqe_next_page *rxcqe = (struct eth_rx_cqe_next_page *)
+ (cp->l2_ring + (2 * BCM_PAGE_SIZE));
+ struct eth_context *context;
+ struct regpair context_addr;
+ int i;
+ int port = CNIC_PORT(cp);
+ int func = CNIC_FUNC(cp);
+ int cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp));
+ u32 val;
+ struct tstorm_eth_client_config tstorm_client = {0};
+
+ for (i = 0; i < BNX2X_MAX_RX_DESC_CNT; i++, rxbd++) {
+ dma_addr_t buf_map;
+ int n = (i % cp->l2_rx_ring_size) + 1;
+
+ buf_map = cp->l2_buf_map + (n * cp->l2_single_buf_size);
+ rxbd->addr_hi = cpu_to_le32((u64) buf_map >> 32);
+ rxbd->addr_lo = cpu_to_le32(buf_map & 0xffffffff);
+ }
+ context = cnic_get_bnx2x_ctx(dev, BNX2X_ISCSI_L2_CID, 0, &context_addr);
+
+ val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) >> 32;
+ rxbd->addr_hi = cpu_to_le32(val);
+
+ context->ustorm_st_context.common.bd_page_base_hi = val;
+
+ val = (u64) (cp->l2_ring_map + BCM_PAGE_SIZE) & 0xffffffff;
+ rxbd->addr_lo = cpu_to_le32(val);
+
+ context->ustorm_st_context.common.bd_page_base_lo = val;
+
+ context->ustorm_st_context.common.sb_index_numbers =
+ BNX2X_ISCSI_RX_SB_INDEX_NUM;
+ context->ustorm_st_context.common.clientId = cli;
+ context->ustorm_st_context.common.status_block_id = BNX2X_DEF_SB_ID;
+ context->ustorm_st_context.common.flags =
+ USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_STATISTICS;
+ context->ustorm_st_context.common.statistics_counter_id = cli;
+ context->ustorm_st_context.common.mc_alignment_log_size = 0;
+ context->ustorm_st_context.common.bd_buff_size =
+ cp->l2_single_buf_size;
+
+ context->ustorm_ag_context.cdu_usage =
+ CDU_RSRVD_VALUE_TYPE_A(BNX2X_HW_CID(BNX2X_ISCSI_L2_CID, func),
+ CDU_REGION_NUMBER_UCM_AG,
+ ETH_CONNECTION_TYPE);
+
+ rxcqe += BNX2X_MAX_RCQ_DESC_CNT;
+ val = (u64) (cp->l2_ring_map + (2 * BCM_PAGE_SIZE)) >> 32;
+ rxcqe->addr_hi = cpu_to_le32(val);
+
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_CQE_PAGE_BASE_OFFSET(port, cli) + 4, val);
+
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_CQE_PAGE_NEXT_OFFSET(port, cli) + 4, val);
+
+ val = (u64) (cp->l2_ring_map + (2 * BCM_PAGE_SIZE)) & 0xffffffff;
+ rxcqe->addr_lo = cpu_to_le32(val);
+
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_CQE_PAGE_BASE_OFFSET(port, cli), val);
+
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_CQE_PAGE_NEXT_OFFSET(port, cli), val);
+
+ /* client tstorm info */
+ tstorm_client.mtu = cp->l2_single_buf_size - 14;
+ tstorm_client.config_flags =
+ (TSTORM_ETH_CLIENT_CONFIG_E1HOV_REM_ENABLE |
+ TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE);
+ tstorm_client.statistics_counter_id = cli;
+
+ CNIC_WR(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_CLIENT_CONFIG_OFFSET(port, cli),
+ ((u32 *)&tstorm_client)[0]);
+ CNIC_WR(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_CLIENT_CONFIG_OFFSET(port, cli) + 4,
+ ((u32 *)&tstorm_client)[1]);
+
+ /* reset tstorm per client statistics */
+ val = BAR_TSTRORM_INTMEM +
+ TSTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli);
+ for (i = 0; i < sizeof(struct tstorm_per_client_stats) / 4; i++)
+ CNIC_WR(dev, val + i * 4, 0);
+
+ /* reset ustorm per client statistics */
+ val = BAR_USTRORM_INTMEM +
+ USTORM_PER_COUNTER_ID_STATS_OFFSET(port, cli);
+ for (i = 0; i < sizeof(struct ustorm_per_client_stats) / 4; i++)
+ CNIC_WR(dev, val + i * 4, 0);
+
+ cp->rx_cons_ptr =
+ &cp->bnx2x_def_status_blk->u_def_status_block.index_values[
+ HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS];
+}
+
+static void cnic_get_bnx2x_iscsi_info(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 base, addr, val;
+ int port = CNIC_PORT(cp);
+
+ dev->max_iscsi_conn = 0;
+ base = CNIC_RD(dev, MISC_REG_SHARED_MEM_ADDR);
+ if (base < 0xa0000 || base >= 0xc0000)
+ return;
+
+ addr = BNX2X_SHMEM_ADDR(base,
+ dev_info.port_hw_config[port].iscsi_mac_upper);
+
+ val = CNIC_RD(dev, addr);
+
+ dev->mac_addr[0] = (u8) (val >> 8);
+ dev->mac_addr[1] = (u8) val;
+
+ addr = BNX2X_SHMEM_ADDR(base,
+ dev_info.port_hw_config[port].iscsi_mac_lower);
+
+ val = CNIC_RD(dev, addr);
+
+ dev->mac_addr[2] = (u8) (val >> 24);
+ dev->mac_addr[3] = (u8) (val >> 16);
+ dev->mac_addr[4] = (u8) (val >> 8);
+ dev->mac_addr[5] = (u8) val;
+
+ addr = BNX2X_SHMEM_ADDR(base, validity_map[port]);
+ val = CNIC_RD(dev, addr);
+
+ if (!(val & SHR_MEM_VALIDITY_LIC_NO_KEY_IN_EFFECT)) {
+ u16 val16;
+
+ addr = BNX2X_SHMEM_ADDR(base,
+ drv_lic_key[port].max_iscsi_init_conn);
+ val16 = CNIC_RD16(dev, addr);
+
+ if (val16)
+ val16 ^= 0x1e1e;
+ dev->max_iscsi_conn = val16;
+ }
+ if (BNX2X_CHIP_IS_E1H(cp->chip_id)) {
+ int func = CNIC_FUNC(cp);
+
+ addr = BNX2X_SHMEM_ADDR(base,
+ mf_cfg.func_mf_config[func].e1hov_tag);
+ val = CNIC_RD(dev, addr);
+ val &= FUNC_MF_CFG_E1HOV_TAG_MASK;
+ if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT) {
+ addr = BNX2X_SHMEM_ADDR(base,
+ mf_cfg.func_mf_config[func].config);
+ val = CNIC_RD(dev, addr);
+ val &= FUNC_MF_CFG_PROTOCOL_MASK;
+ if (val != FUNC_MF_CFG_PROTOCOL_ISCSI)
+ dev->max_iscsi_conn = 0;
+ }
+ }
+}
+
+static int cnic_start_bnx2x_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ int func = CNIC_FUNC(cp), ret, i;
+ int port = CNIC_PORT(cp);
+ u16 eq_idx;
+ u8 sb_id = cp->status_blk_num;
+
+ ret = cnic_init_id_tbl(&cp->cid_tbl, MAX_ISCSI_TBL_SZ,
+ BNX2X_ISCSI_START_CID);
+
+ if (ret)
+ return -ENOMEM;
+
+ cp->kcq_io_addr = BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_PROD_OFFSET(func, 0);
+ cp->kcq_prod_idx = 0;
+
+ cnic_get_bnx2x_iscsi_info(dev);
+
+ /* Only 1 EQ */
+ CNIC_WR16(dev, cp->kcq_io_addr, MAX_KCQ_IDX);
+ CNIC_WR(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_CONS_OFFSET(func, 0), 0);
+ CNIC_WR(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(func, 0),
+ cp->kcq_info.pg_map_arr[1] & 0xffffffff);
+ CNIC_WR(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(func, 0) + 4,
+ (u64) cp->kcq_info.pg_map_arr[1] >> 32);
+ CNIC_WR(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(func, 0),
+ cp->kcq_info.pg_map_arr[0] & 0xffffffff);
+ CNIC_WR(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(func, 0) + 4,
+ (u64) cp->kcq_info.pg_map_arr[0] >> 32);
+ CNIC_WR8(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_VALID_OFFSET(func, 0), 1);
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_SB_NUM_OFFSET(func, 0), cp->status_blk_num);
+ CNIC_WR8(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(func, 0),
+ HC_INDEX_C_ISCSI_EQ_CONS);
+
+ for (i = 0; i < cp->conn_buf_info.num_pages; i++) {
+ CNIC_WR(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_CONN_BUF_PBL_OFFSET(func, i),
+ cp->conn_buf_info.pgtbl[2 * i]);
+ CNIC_WR(dev, BAR_TSTRORM_INTMEM +
+ TSTORM_ISCSI_CONN_BUF_PBL_OFFSET(func, i) + 4,
+ cp->conn_buf_info.pgtbl[(2 * i) + 1]);
+ }
+
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(func),
+ cp->gbl_buf_info.pg_map_arr[0] & 0xffffffff);
+ CNIC_WR(dev, BAR_USTRORM_INTMEM +
+ USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(func) + 4,
+ (u64) cp->gbl_buf_info.pg_map_arr[0] >> 32);
+
+ cnic_setup_bnx2x_context(dev);
+
+ eq_idx = CNIC_RD16(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id) +
+ offsetof(struct cstorm_status_block_c,
+ index_values[HC_INDEX_C_ISCSI_EQ_CONS]));
+ if (eq_idx != 0) {
+ printk(KERN_ERR PFX "%s: EQ cons index %x != 0\n",
+ dev->netdev->name, eq_idx);
+ return -EBUSY;
+ }
+ ret = cnic_init_bnx2x_irq(dev);
+ if (ret)
+ return ret;
+
+ cnic_init_bnx2x_tx_ring(dev);
+ cnic_init_bnx2x_rx_ring(dev);
+
+ return 0;
+}
+
+static void cnic_init_rings(struct cnic_dev *dev)
+{
+ if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) {
+ cnic_init_bnx2_tx_ring(dev);
+ cnic_init_bnx2_rx_ring(dev);
+ } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp));
+ union l5cm_specific_data l5_data;
+ struct ustorm_eth_rx_producers rx_prods = {0};
+ u32 off, i;
+
+ rx_prods.bd_prod = 0;
+ rx_prods.cqe_prod = BNX2X_MAX_RCQ_DESC_CNT;
+ barrier();
+
+ off = BAR_USTRORM_INTMEM +
+ USTORM_RX_PRODS_OFFSET(CNIC_PORT(cp), cli);
+
+ for (i = 0; i < sizeof(struct ustorm_eth_rx_producers) / 4; i++)
+ CNIC_WR(dev, off + i * 4, ((u32 *) &rx_prods)[i]);
+
+ cnic_init_bnx2x_tx_ring(dev);
+ cnic_init_bnx2x_rx_ring(dev);
+
+ l5_data.phy_address.lo = cli;
+ l5_data.phy_address.hi = 0;
+ cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_CLIENT_SETUP,
+ BNX2X_ISCSI_L2_CID, ETH_CONNECTION_TYPE, &l5_data);
+ cnic_ring_ctl(dev, BNX2X_ISCSI_L2_CID, cli, 1);
+ }
+}
+
+static void cnic_shutdown_rings(struct cnic_dev *dev)
+{
+ if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) {
+ cnic_shutdown_bnx2_rx_ring(dev);
+ } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
+ struct cnic_local *cp = dev->cnic_priv;
+ u32 cli = BNX2X_ISCSI_CL_ID(CNIC_E1HVN(cp));
+ union l5cm_specific_data l5_data;
+
+ cnic_ring_ctl(dev, BNX2X_ISCSI_L2_CID, cli, 0);
+
+ l5_data.phy_address.lo = cli;
+ l5_data.phy_address.hi = 0;
+ cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_HALT,
+ BNX2X_ISCSI_L2_CID, ETH_CONNECTION_TYPE, &l5_data);
+ msleep(10);
+ }
+}
+
static int cnic_register_netdev(struct cnic_dev *dev)
{
struct cnic_local *cp = dev->cnic_priv;
@@ -2554,6 +4276,22 @@ static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
cnic_free_resc(dev);
}
+
+static void cnic_stop_bnx2x_hw(struct cnic_dev *dev)
+{
+ struct cnic_local *cp = dev->cnic_priv;
+ u8 sb_id = cp->status_blk_num;
+ int port = CNIC_PORT(cp);
+
+ cnic_free_irq(dev);
+ CNIC_WR16(dev, BAR_CSTRORM_INTMEM +
+ CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id) +
+ offsetof(struct cstorm_status_block_c,
+ index_values[HC_INDEX_C_ISCSI_EQ_CONS]),
+ 0);
+ cnic_free_resc(dev);
+}
+
static void cnic_stop_hw(struct cnic_dev *dev)
{
if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
@@ -2685,6 +4423,57 @@ cnic_err:
return NULL;
}
+static struct cnic_dev *init_bnx2x_cnic(struct net_device *dev)
+{
+ struct pci_dev *pdev;
+ struct cnic_dev *cdev;
+ struct cnic_local *cp;
+ struct cnic_eth_dev *ethdev = NULL;
+ struct cnic_eth_dev *(*probe)(struct net_device *) = NULL;
+
+ probe = symbol_get(bnx2x_cnic_probe);
+ if (probe) {
+ ethdev = (*probe)(dev);
+ symbol_put(bnx2x_cnic_probe);
+ }
+ if (!ethdev)
+ return NULL;
+
+ pdev = ethdev->pdev;
+ if (!pdev)
+ return NULL;
+
+ dev_hold(dev);
+ cdev = cnic_alloc_dev(dev, pdev);
+ if (cdev == NULL) {
+ dev_put(dev);
+ return NULL;
+ }
+
+ set_bit(CNIC_F_BNX2X_CLASS, &cdev->flags);
+ cdev->submit_kwqes = cnic_submit_bnx2x_kwqes;
+
+ cp = cdev->cnic_priv;
+ cp->ethdev = ethdev;
+ cdev->pcidev = pdev;
+
+ cp->cnic_ops = &cnic_bnx2x_ops;
+ cp->start_hw = cnic_start_bnx2x_hw;
+ cp->stop_hw = cnic_stop_bnx2x_hw;
+ cp->setup_pgtbl = cnic_setup_page_tbl_le;
+ cp->alloc_resc = cnic_alloc_bnx2x_resc;
+ cp->free_resc = cnic_free_resc;
+ cp->start_cm = cnic_cm_init_bnx2x_hw;
+ cp->stop_cm = cnic_cm_stop_bnx2x_hw;
+ cp->enable_int = cnic_enable_bnx2x_int;
+ cp->disable_int_sync = cnic_disable_bnx2x_int_sync;
+ cp->ack_int = cnic_ack_bnx2x_msix;
+ cp->close_conn = cnic_close_bnx2x_conn;
+ cp->next_idx = cnic_bnx2x_next_idx;
+ cp->hw_idx = cnic_bnx2x_hw_idx;
+ return cdev;
+}
+
static struct cnic_dev *is_cnic_dev(struct net_device *dev)
{
struct ethtool_drvinfo drvinfo;
@@ -2696,6 +4485,8 @@ static struct cnic_dev *is_cnic_dev(struct net_device *dev)
if (!strcmp(drvinfo.driver, "bnx2"))
cdev = init_bnx2_cnic(dev);
+ if (!strcmp(drvinfo.driver, "bnx2x"))
+ cdev = init_bnx2x_cnic(dev);
if (cdev) {
write_lock(&cnic_dev_lock);
list_add(&cdev->list, &cnic_dev_list);
diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h
index a94b302bb46..241d09acc0d 100644
--- a/drivers/net/cnic.h
+++ b/drivers/net/cnic.h
@@ -227,6 +227,7 @@ struct cnic_local {
void *status_blk;
struct status_block_msix *bnx2_status_blk;
struct host_status_block *bnx2x_status_blk;
+ struct host_def_status_block *bnx2x_def_status_blk;
u32 status_blk_num;
u32 int_num;
@@ -258,6 +259,7 @@ struct cnic_local {
struct cnic_ctx *ctx_arr;
int ctx_blks;
int ctx_blk_size;
+ unsigned long ctx_align;
int cids_per_blk;
u32 chip_id;
@@ -290,11 +292,73 @@ struct bnx2x_bd_chain_next {
u8 reserved[8];
};
+#define ISCSI_DEFAULT_MAX_OUTSTANDING_R2T (1)
+
#define ISCSI_RAMROD_CMD_ID_UPDATE_CONN (ISCSI_KCQE_OPCODE_UPDATE_CONN)
#define ISCSI_RAMROD_CMD_ID_INIT (ISCSI_KCQE_OPCODE_INIT)
#define CDU_REGION_NUMBER_XCM_AG 2
#define CDU_REGION_NUMBER_UCM_AG 4
+#define CDU_VALID_DATA(_cid, _region, _type) \
+ (((_cid) << 8) | (((_region)&0xf)<<4) | (((_type)&0xf)))
+
+#define CDU_CRC8(_cid, _region, _type) \
+ (calc_crc8(CDU_VALID_DATA(_cid, _region, _type), 0xff))
+
+#define CDU_RSRVD_VALUE_TYPE_A(_cid, _region, _type) \
+ (0x80 | ((CDU_CRC8(_cid, _region, _type)) & 0x7f))
+
+#define BNX2X_CONTEXT_MEM_SIZE 1024
+#define BNX2X_FCOE_CID 16
+
+/* iSCSI client IDs are 17, 19, 21, 23 */
+#define BNX2X_ISCSI_BASE_CL_ID 17
+#define BNX2X_ISCSI_CL_ID(vn) (BNX2X_ISCSI_BASE_CL_ID + ((vn) << 1))
+
+#define BNX2X_ISCSI_L2_CID 17
+#define BNX2X_ISCSI_START_CID 18
+#define BNX2X_ISCSI_NUM_CONNECTIONS 128
+#define BNX2X_ISCSI_TASK_CONTEXT_SIZE 128
+#define BNX2X_ISCSI_MAX_PENDING_R2TS 4
+#define BNX2X_ISCSI_R2TQE_SIZE 8
+#define BNX2X_ISCSI_HQ_BD_SIZE 64
+#define BNX2X_ISCSI_CONN_BUF_SIZE 64
+#define BNX2X_ISCSI_GLB_BUF_SIZE 64
+#define BNX2X_ISCSI_PBL_NOT_CACHED 0xff
+#define BNX2X_ISCSI_PDU_HEADER_NOT_CACHED 0xff
+#define BNX2X_HW_CID(x, func) ((x) | (((func) % PORT_MAX) << 23) | \
+ (((func) >> 1) << 17))
+#define BNX2X_SW_CID(x) (x & 0x1ffff)
+#define BNX2X_CHIP_NUM_57711 0x164f
+#define BNX2X_CHIP_NUM_57711E 0x1650
+#define BNX2X_CHIP_NUM(x) (x >> 16)
+#define BNX2X_CHIP_IS_57711(x) \
+ (BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57711)
+#define BNX2X_CHIP_IS_57711E(x) \
+ (BNX2X_CHIP_NUM(x) == BNX2X_CHIP_NUM_57711E)
+#define BNX2X_CHIP_IS_E1H(x) \
+ (BNX2X_CHIP_IS_57711(x) || BNX2X_CHIP_IS_57711E(x))
+#define IS_E1H_OFFSET BNX2X_CHIP_IS_E1H(cp->chip_id)
+
+#define BNX2X_RX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_rx_bd))
+#define BNX2X_MAX_RX_DESC_CNT (BNX2X_RX_DESC_CNT - 2)
+#define BNX2X_RCQ_DESC_CNT (BCM_PAGE_SIZE / sizeof(union eth_rx_cqe))
+#define BNX2X_MAX_RCQ_DESC_CNT (BNX2X_RCQ_DESC_CNT - 1)
+
+#define BNX2X_DEF_SB_ID 16
+
+#define BNX2X_ISCSI_RX_SB_INDEX_NUM \
+ ((HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS << \
+ USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT) & \
+ USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER)
+
+#define BNX2X_SHMEM_ADDR(base, field) (base + \
+ offsetof(struct shmem_region, field))
+
+#define CNIC_PORT(cp) ((cp)->func % PORT_MAX)
+#define CNIC_FUNC(cp) ((cp)->func)
+#define CNIC_E1HVN(cp) ((cp)->func >> 1)
+
#endif
diff --git a/drivers/net/cnic_defs.h b/drivers/net/cnic_defs.h
index cee80f69445..9827b278dc7 100644
--- a/drivers/net/cnic_defs.h
+++ b/drivers/net/cnic_defs.h
@@ -51,6 +51,9 @@
#define L4_KCQE_COMPLETION_STATUS_SUCCESS (0)
#define L4_KCQE_COMPLETION_STATUS_TIMEOUT (0x93)
+#define L4_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAIL (0x83)
+#define L4_KCQE_COMPLETION_STATUS_OFFLOADED_PG (0x89)
+
#define L4_LAYER_CODE (4)
#define L2_LAYER_CODE (2)
@@ -577,4 +580,1918 @@ struct l4_kwq_upload {
u32 reserved2[6];
};
+/*
+ * bnx2x structures
+ */
+
+/*
+ * iSCSI context region, used only in iSCSI
+ */
+struct ustorm_iscsi_rq_db {
+ struct regpair pbl_base;
+ struct regpair curr_pbe;
+};
+
+/*
+ * iSCSI context region, used only in iSCSI
+ */
+struct ustorm_iscsi_r2tq_db {
+ struct regpair pbl_base;
+ struct regpair curr_pbe;
+};
+
+/*
+ * iSCSI context region, used only in iSCSI
+ */
+struct ustorm_iscsi_cq_db {
+#if defined(__BIG_ENDIAN)
+ u16 cq_sn;
+ u16 prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 prod;
+ u16 cq_sn;
+#endif
+ struct regpair curr_pbe;
+};
+
+/*
+ * iSCSI context region, used only in iSCSI
+ */
+struct rings_db {
+ struct ustorm_iscsi_rq_db rq;
+ struct ustorm_iscsi_r2tq_db r2tq;
+ struct ustorm_iscsi_cq_db cq[8];
+#if defined(__BIG_ENDIAN)
+ u16 rq_prod;
+ u16 r2tq_prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 r2tq_prod;
+ u16 rq_prod;
+#endif
+ struct regpair cq_pbl_base;
+};
+
+/*
+ * iSCSI context region, used only in iSCSI
+ */
+struct ustorm_iscsi_placement_db {
+ u32 sgl_base_lo;
+ u32 sgl_base_hi;
+ u32 local_sge_0_address_hi;
+ u32 local_sge_0_address_lo;
+#if defined(__BIG_ENDIAN)
+ u16 curr_sge_offset;
+ u16 local_sge_0_size;
+#elif defined(__LITTLE_ENDIAN)
+ u16 local_sge_0_size;
+ u16 curr_sge_offset;
+#endif
+ u32 local_sge_1_address_hi;
+ u32 local_sge_1_address_lo;
+#if defined(__BIG_ENDIAN)
+ u16 reserved6;
+ u16 local_sge_1_size;
+#elif defined(__LITTLE_ENDIAN)
+ u16 local_sge_1_size;
+ u16 reserved6;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 sgl_size;
+ u8 local_sge_index_2b;
+ u16 reserved7;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved7;
+ u8 local_sge_index_2b;
+ u8 sgl_size;
+#endif
+ u32 rem_pdu;
+ u32 place_db_bitfield_1;
+#define USTORM_ISCSI_PLACEMENT_DB_REM_PDU_PAYLOAD (0xFFFFFF<<0)
+#define USTORM_ISCSI_PLACEMENT_DB_REM_PDU_PAYLOAD_SHIFT 0
+#define USTORM_ISCSI_PLACEMENT_DB_CQ_ID (0xFF<<24)
+#define USTORM_ISCSI_PLACEMENT_DB_CQ_ID_SHIFT 24
+ u32 place_db_bitfield_2;
+#define USTORM_ISCSI_PLACEMENT_DB_BYTES_2_TRUNCATE (0xFFFFFF<<0)
+#define USTORM_ISCSI_PLACEMENT_DB_BYTES_2_TRUNCATE_SHIFT 0
+#define USTORM_ISCSI_PLACEMENT_DB_HOST_SGE_INDEX (0xFF<<24)
+#define USTORM_ISCSI_PLACEMENT_DB_HOST_SGE_INDEX_SHIFT 24
+ u32 nal;
+#define USTORM_ISCSI_PLACEMENT_DB_REM_SGE_SIZE (0xFFFFFF<<0)
+#define USTORM_ISCSI_PLACEMENT_DB_REM_SGE_SIZE_SHIFT 0
+#define USTORM_ISCSI_PLACEMENT_DB_EXP_PADDING_2B (0x3<<24)
+#define USTORM_ISCSI_PLACEMENT_DB_EXP_PADDING_2B_SHIFT 24
+#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B (0x7<<26)
+#define USTORM_ISCSI_PLACEMENT_DB_EXP_DIGEST_3B_SHIFT 26
+#define USTORM_ISCSI_PLACEMENT_DB_NAL_LEN_3B (0x7<<29)
+#define USTORM_ISCSI_PLACEMENT_DB_NAL_LEN_3B_SHIFT 29
+};
+
+/*
+ * Ustorm iSCSI Storm Context
+ */
+struct ustorm_iscsi_st_context {
+ u32 exp_stat_sn;
+ u32 exp_data_sn;
+ struct rings_db ring;
+ struct regpair task_pbl_base;
+ struct regpair tce_phy_addr;
+ struct ustorm_iscsi_placement_db place_db;
+ u32 data_rcv_seq;
+ u32 rem_rcv_len;
+#if defined(__BIG_ENDIAN)
+ u16 hdr_itt;
+ u16 iscsi_conn_id;
+#elif defined(__LITTLE_ENDIAN)
+ u16 iscsi_conn_id;
+ u16 hdr_itt;
+#endif
+ u32 nal_bytes;
+#if defined(__BIG_ENDIAN)
+ u8 hdr_second_byte_union;
+ u8 bitfield_0;
+#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU (0x1<<0)
+#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU_SHIFT 0
+#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE (0x1<<1)
+#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE_SHIFT 1
+#define USTORM_ISCSI_ST_CONTEXT_RESERVED1 (0x3F<<2)
+#define USTORM_ISCSI_ST_CONTEXT_RESERVED1_SHIFT 2
+ u8 task_pdu_cache_index;
+ u8 task_pbe_cache_index;
+#elif defined(__LITTLE_ENDIAN)
+ u8 task_pbe_cache_index;
+ u8 task_pdu_cache_index;
+ u8 bitfield_0;
+#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU (0x1<<0)
+#define USTORM_ISCSI_ST_CONTEXT_BMIDDLEOFPDU_SHIFT 0
+#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE (0x1<<1)
+#define USTORM_ISCSI_ST_CONTEXT_BFENCECQE_SHIFT 1
+#define USTORM_ISCSI_ST_CONTEXT_RESERVED1 (0x3F<<2)
+#define USTORM_ISCSI_ST_CONTEXT_RESERVED1_SHIFT 2
+ u8 hdr_second_byte_union;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserved3;
+ u8 reserved2;
+ u8 acDecrement;
+#elif defined(__LITTLE_ENDIAN)
+ u8 acDecrement;
+ u8 reserved2;
+ u16 reserved3;
+#endif
+ u32 task_stat;
+#if defined(__BIG_ENDIAN)
+ u8 hdr_opcode;
+ u8 num_cqs;
+ u16 reserved5;
+#elif defined(__LITTLE_ENDIAN)
+ u16 reserved5;
+ u8 num_cqs;
+ u8 hdr_opcode;
+#endif
+ u32 negotiated_rx;
+#define USTORM_ISCSI_ST_CONTEXT_MAX_RECV_PDU_LENGTH (0xFFFFFF<<0)
+#define USTORM_ISCSI_ST_CONTEXT_MAX_RECV_PDU_LENGTH_SHIFT 0
+#define USTORM_ISCSI_ST_CONTEXT_MAX_OUTSTANDING_R2TS (0xFF<<24)
+#define USTORM_ISCSI_ST_CONTEXT_MAX_OUTSTANDING_R2TS_SHIFT 24
+ u32 negotiated_rx_and_flags;
+#define USTORM_ISCSI_ST_CONTEXT_MAX_BURST_LENGTH (0xFFFFFF<<0)
+#define USTORM_ISCSI_ST_CONTEXT_MAX_BURST_LENGTH_SHIFT 0
+#define USTORM_ISCSI_ST_CONTEXT_B_CQE_POSTED_OR_HEADER_CACHED (0x1<<24)
+#define USTORM_ISCSI_ST_CONTEXT_B_CQE_POSTED_OR_HEADER_CACHED_SHIFT 24
+#define USTORM_ISCSI_ST_CONTEXT_B_HDR_DIGEST_EN (0x1<<25)
+#define USTORM_ISCSI_ST_CONTEXT_B_HDR_DIGEST_EN_SHIFT 25
+#define USTORM_ISCSI_ST_CONTEXT_B_DATA_DIGEST_EN (0x1<<26)
+#define USTORM_ISCSI_ST_CONTEXT_B_DATA_DIGEST_EN_SHIFT 26
+#define USTORM_ISCSI_ST_CONTEXT_B_PROTOCOL_ERROR (0x1<<27)
+#define USTORM_ISCSI_ST_CONTEXT_B_PROTOCOL_ERROR_SHIFT 27
+#define USTORM_ISCSI_ST_CONTEXT_B_TASK_VALID (0x1<<28)
+#define USTORM_ISCSI_ST_CONTEXT_B_TASK_VALID_SHIFT 28
+#define USTORM_ISCSI_ST_CONTEXT_TASK_TYPE (0x3<<29)
+#define USTORM_ISCSI_ST_CONTEXT_TASK_TYPE_SHIFT 29
+#define USTORM_ISCSI_ST_CONTEXT_B_ALL_DATA_ACKED (0x1<<31)
+#define USTORM_ISCSI_ST_CONTEXT_B_ALL_DATA_ACKED_SHIFT 31
+};
+
+/*
+ * TCP context region, shared in TOE, RDMA and ISCSI
+ */
+struct tstorm_tcp_st_context_section {
+ u32 flags1;
+#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_SRTT_20B (0xFFFFFF<<0)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_SRTT_20B_SHIFT 0
+#define TSTORM_TCP_ST_CONTEXT_SECTION_PAWS_INVALID (0x1<<24)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_PAWS_INVALID_SHIFT 24
+#define TSTORM_TCP_ST_CONTEXT_SECTION_TIMESTAMP_EXISTS (0x1<<25)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_TIMESTAMP_EXISTS_SHIFT 25
+#define TSTORM_TCP_ST_CONTEXT_SECTION_ISLE_EXISTS (0x1<<26)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_ISLE_EXISTS_SHIFT 26
+#define TSTORM_TCP_ST_CONTEXT_SECTION_STOP_RX_PAYLOAD (0x1<<27)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_STOP_RX_PAYLOAD_SHIFT 27
+#define TSTORM_TCP_ST_CONTEXT_SECTION_KA_ENABLED (0x1<<28)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_KA_ENABLED_SHIFT 28
+#define TSTORM_TCP_ST_CONTEXT_SECTION_FIRST_RTO_ESTIMATE (0x1<<29)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_FIRST_RTO_ESTIMATE_SHIFT 29
+#define TSTORM_TCP_ST_CONTEXT_SECTION_MAX_SEG_RETRANSMIT_EN (0x1<<30)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_MAX_SEG_RETRANSMIT_EN_SHIFT 30
+#define TSTORM_TCP_ST_CONTEXT_SECTION_RESERVED3 (0x1<<31)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_RESERVED3_SHIFT 31
+ u32 flags2;
+#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_VARIATION_20B (0xFFFFFF<<0)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_RTT_VARIATION_20B_SHIFT 0
+#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_EN (0x1<<24)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_EN_SHIFT 24
+#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_COUNTER_EN (0x1<<25)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_DA_COUNTER_EN_SHIFT 25
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_KA_PROBE_SENT (0x1<<26)
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_KA_PROBE_SENT_SHIFT 26
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_PERSIST_PROBE_SENT (0x1<<27)
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_PERSIST_PROBE_SENT_SHIFT 27
+#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<28)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 28
+#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<29)
+#define TSTORM_TCP_ST_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 29
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_SECOND_ISLE_DROPPED (0x1<<30)
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_SECOND_ISLE_DROPPED_SHIFT 30
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_DONT_SUPPORT_OOO (0x1<<31)
+#define __TSTORM_TCP_ST_CONTEXT_SECTION_DONT_SUPPORT_OOO_SHIFT 31
+#if defined(__BIG_ENDIAN)
+ u16 reserved_slowpath;
+ u8 tcp_sm_state_3b;
+ u8 rto_exp_3b;
+#elif defined(__LITTLE_ENDIAN)
+ u8 rto_exp_3b;
+ u8 tcp_sm_state_3b;
+ u16 reserved_slowpath;
+#endif
+ u32 rcv_nxt;
+ u32 timestamp_recent;
+ u32 timestamp_recent_time;
+ u32 cwnd;
+ u32 ss_thresh;
+ u32 cwnd_accum;
+ u32 prev_seg_seq;
+ u32 expected_rel_seq;
+ u32 recover;
+#if defined(__BIG_ENDIAN)
+ u8 retransmit_count;
+ u8 ka_max_probe_count;
+ u8 persist_probe_count;
+ u8 ka_probe_count;
+#elif defined(__LITTLE_ENDIAN)
+ u8 ka_probe_count;
+ u8 persist_probe_count;
+ u8 ka_max_probe_count;
+ u8 retransmit_count;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 statistics_counter_id;
+ u8 ooo_support_mode;
+ u8 snd_wnd_scale_4b;
+ u8 dup_ack_count;
+#elif defined(__LITTLE_ENDIAN)
+ u8 dup_ack_count;
+ u8 snd_wnd_scale_4b;
+ u8 ooo_support_mode;
+ u8 statistics_counter_id;
+#endif
+ u32 retransmit_start_time;
+ u32 ka_timeout;
+ u32 ka_interval;
+ u32 isle_start_seq;
+ u32 isle_end_seq;
+#if defined(__BIG_ENDIAN)
+ u16 mss;
+ u16 recent_seg_wnd;
+#elif defined(__LITTLE_ENDIAN)
+ u16 recent_seg_wnd;
+ u16 mss;
+#endif
+ u32 reserved4;
+ u32 max_rt_time;
+#if defined(__BIG_ENDIAN)
+ u16 lsb_mac_address;
+ u16 vlan_id;
+#elif defined(__LITTLE_ENDIAN)
+ u16 vlan_id;
+ u16 lsb_mac_address;
+#endif
+ u32 msb_mac_address;
+ u32 reserved2;
+};
+
+/*
+ * Termination variables
+ */
+struct iscsi_term_vars {
+ u8 BitMap;
+#define ISCSI_TERM_VARS_TCP_STATE (0xF<<0)
+#define ISCSI_TERM_VARS_TCP_STATE_SHIFT 0
+#define ISCSI_TERM_VARS_FIN_RECEIVED_SBIT (0x1<<4)
+#define ISCSI_TERM_VARS_FIN_RECEIVED_SBIT_SHIFT 4
+#define ISCSI_TERM_VARS_ACK_ON_FIN_RECEIVED_SBIT (0x1<<5)
+#define ISCSI_TERM_VARS_ACK_ON_FIN_RECEIVED_SBIT_SHIFT 5
+#define ISCSI_TERM_VARS_TERM_ON_CHIP (0x1<<6)
+#define ISCSI_TERM_VARS_TERM_ON_CHIP_SHIFT 6
+#define ISCSI_TERM_VARS_RSRV (0x1<<7)
+#define ISCSI_TERM_VARS_RSRV_SHIFT 7
+};
+
+/*
+ * iSCSI context region, used only in iSCSI
+ */
+struct tstorm_iscsi_st_context_section {
+#if defined(__BIG_ENDIAN)
+ u16 rem_tcp_data_len;
+ u16 brb_offset;
+#elif defined(__LITTLE_ENDIAN)
+ u16 brb_offset;
+ u16 rem_tcp_data_len;
+#endif
+ u32 b2nh;
+#if defined(__BIG_ENDIAN)
+ u16 rq_cons;
+ u8 flags;
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN (0x1<<0)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN_SHIFT 0
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN (0x1<<1)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN_SHIFT 1
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER (0x1<<2)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER_SHIFT 2
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE (0x1<<3)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE_SHIFT 3
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS (0x1<<4)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS_SHIFT 4
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV (0x7<<5)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV_SHIFT 5
+ u8 hdr_bytes_2_fetch;
+#elif defined(__LITTLE_ENDIAN)
+ u8 hdr_bytes_2_fetch;
+ u8 flags;
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN (0x1<<0)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN_SHIFT 0
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN (0x1<<1)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN_SHIFT 1
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER (0x1<<2)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_PARTIAL_HEADER_SHIFT 2
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE (0x1<<3)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_FULL_FEATURE_SHIFT 3
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS (0x1<<4)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DROP_ALL_PDUS_SHIFT 4
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV (0x7<<5)
+#define TSTORM_ISCSI_ST_CONTEXT_SECTION_FLAGS_RSRV_SHIFT 5
+ u16 rq_cons;
+#endif
+ struct regpair rq_db_phy_addr;
+#if defined(__BIG_ENDIAN)
+ struct iscsi_term_vars term_vars;
+ u8 scratchpad_idx;
+ u16 iscsi_conn_id;
+#elif defined(__LITTLE_ENDIAN)
+ u16 iscsi_conn_id;
+ u8 scratchpad_idx;
+ struct iscsi_term_vars term_vars;
+#endif
+ u32 reserved2;
+};
+
+/*
+ * The iSCSI non-aggregative context of Tstorm
+ */
+struct tstorm_iscsi_st_context {
+ struct tstorm_tcp_st_context_section tcp;
+ struct tstorm_iscsi_st_context_section iscsi;
+};
+
+/*
+ * The tcp aggregative context section of Xstorm
+ */
+struct xstorm_tcp_tcp_ag_context_section {
+#if defined(__BIG_ENDIAN)
+ u8 __tcp_agg_vars1;
+ u8 __da_cnt;
+ u16 mss;
+#elif defined(__LITTLE_ENDIAN)
+ u16 mss;
+ u8 __da_cnt;
+ u8 __tcp_agg_vars1;
+#endif
+ u32 snd_nxt;
+ u32 tx_wnd;
+ u32 snd_una;
+ u32 local_adv_wnd;
+#if defined(__BIG_ENDIAN)
+ u8 __agg_val8_th;
+ u8 __agg_val8;
+ u16 tcp_agg_vars2;
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN (0x1<<7)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN_SHIFT 7
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 tcp_agg_vars2;
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG (0x1<<0)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED (0x1<<1)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_UNBLOCKED_SHIFT 1
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE (0x1<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_TIMER_ACTIVE_SHIFT 2
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG (0x1<<3)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_FLAG_SHIFT 3
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG (0x1<<4)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX4_FLAG_SHIFT 4
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE (0x1<<5)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DA_ENABLE_SHIFT 5
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN (0x1<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ACK_TO_FE_UPDATED_EN_SHIFT 6
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN (0x1<<7)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX3_CF_EN_SHIFT 7
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN (0x1<<8)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_FIN_FLAG_EN_SHIFT 8
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<9)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 9
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF (0x3<<10)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_SET_RTO_CF_SHIFT 10
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF (0x3<<12)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_UPDATED_CF_SHIFT 12
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF (0x3<<14)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_SHIFT 14
+ u8 __agg_val8;
+ u8 __agg_val8_th;
+#endif
+ u32 ack_to_far_end;
+ u32 rto_timer;
+ u32 ka_timer;
+ u32 ts_to_echo;
+#if defined(__BIG_ENDIAN)
+ u16 __agg_val7_th;
+ u16 __agg_val7;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_val7;
+ u16 __agg_val7_th;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 __tcp_agg_vars5;
+ u8 __tcp_agg_vars4;
+ u8 __tcp_agg_vars3;
+ u8 __force_pure_ack_cnt;
+#elif defined(__LITTLE_ENDIAN)
+ u8 __force_pure_ack_cnt;
+ u8 __tcp_agg_vars3;
+ u8 __tcp_agg_vars4;
+ u8 __tcp_agg_vars5;
+#endif
+ u32 tcp_agg_vars6;
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN (0x1<<0)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TS_TO_ECHO_CF_EN_SHIFT 0
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_EN (0x1<<1)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX8_CF_EN_SHIFT 1
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN (0x1<<2)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_EN_SHIFT 2
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<3)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 3
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG (0x1<<4)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX6_FLAG_SHIFT 4
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG (0x1<<5)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX7_FLAG_SHIFT 5
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF (0x3<<6)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX5_CF_SHIFT 6
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF (0x3<<8)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX9_CF_SHIFT 8
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF (0x3<<10)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_SHIFT 10
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF (0x3<<12)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_SHIFT 12
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF (0x3<<14)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_SHIFT 14
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF (0x3<<16)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX13_CF_SHIFT 16
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF (0x3<<18)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX14_CF_SHIFT 18
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF (0x3<<20)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX15_CF_SHIFT 20
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF (0x3<<22)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX16_CF_SHIFT 22
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF (0x3<<24)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX17_CF_SHIFT 24
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG (0x1<<26)
+#define XSTORM_TCP_TCP_AG_CONTEXT_SECTION_ECE_FLAG_SHIFT 26
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71 (0x1<<27)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED71_SHIFT 27
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY (0x1<<28)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_FORCE_PURE_ACK_CNT_DIRTY_SHIFT 28
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG (0x1<<29)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_TCP_AUTO_STOP_FLAG_SHIFT 29
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG (0x1<<30)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_DO_TS_UPDATE_FLAG_SHIFT 30
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG (0x1<<31)
+#define __XSTORM_TCP_TCP_AG_CONTEXT_SECTION_CANCEL_RETRANSMIT_FLAG_SHIFT 31
+#if defined(__BIG_ENDIAN)
+ u16 __agg_misc6;
+ u16 __tcp_agg_vars7;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __tcp_agg_vars7;
+ u16 __agg_misc6;
+#endif
+ u32 __agg_val10;
+ u32 __agg_val10_th;
+#if defined(__BIG_ENDIAN)
+ u16 __reserved3;
+ u8 __reserved2;
+ u8 __da_only_cnt;
+#elif defined(__LITTLE_ENDIAN)
+ u8 __da_only_cnt;
+ u8 __reserved2;
+ u16 __reserved3;
+#endif
+};
+
+/*
+ * The iscsi aggregative context of Xstorm
+ */
+struct xstorm_iscsi_ag_context {
+#if defined(__BIG_ENDIAN)
+ u16 agg_val1;
+ u8 agg_vars1;
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
+ u8 state;
+#elif defined(__LITTLE_ENDIAN)
+ u8 state;
+ u8 agg_vars1;
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_MORE_TO_SEND_EN_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN (0x1<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_NAGLE_EN_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG (0x1<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_SHIFT 6
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_UNA_GT_NXT_EN_SHIFT 7
+ u16 agg_val1;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 cdu_reserved;
+ u8 agg_vars4;
+#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF (0x3<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF (0x3<<2)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_SHIFT 2
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN_SHIFT 4
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN (0x1<<5)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN (0x1<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN_SHIFT 6
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN_SHIFT 7
+ u8 agg_vars3;
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF (0x3<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_SHIFT 6
+ u8 agg_vars2;
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+#elif defined(__LITTLE_ENDIAN)
+ u8 agg_vars2;
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF (0x3<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN (0x1<<2)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_SPARE_FLAG_EN_SHIFT 2
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX8_FLAG_SHIFT 3
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX9_FLAG_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1 (0x3<<5)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE1_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+ u8 agg_vars3;
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2 (0x3F<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM2_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF (0x3<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_SHIFT 6
+ u8 agg_vars4;
+#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF (0x3<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF (0x3<<2)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_SHIFT 2
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN (0x1<<4)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_EN_SHIFT 4
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN (0x1<<5)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX19_CF_EN_SHIFT 5
+#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN (0x1<<6)
+#define __XSTORM_ISCSI_AG_CONTEXT_R2TQ_PROD_CF_EN_SHIFT 6
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN (0x1<<7)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX21_CF_EN_SHIFT 7
+ u8 cdu_reserved;
+#endif
+ u32 more_to_send;
+#if defined(__BIG_ENDIAN)
+ u16 agg_vars5;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14
+ u16 sq_cons;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sq_cons;
+ u16 agg_vars5;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5 (0x3<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE5_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0 (0x3F<<2)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM0_SHIFT 2
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1 (0x3F<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_PHYSICAL_QUEUE_NUM1_SHIFT 8
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2 (0x3<<14)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE2_SHIFT 14
+#endif
+ struct xstorm_tcp_tcp_ag_context_section tcp;
+#if defined(__BIG_ENDIAN)
+ u16 agg_vars7;
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3
+#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF (0x3<<4)
+#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG (0x1<<15)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG_SHIFT 15
+ u8 agg_val3_th;
+ u8 agg_vars6;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6
+#elif defined(__LITTLE_ENDIAN)
+ u8 agg_vars6;
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6 (0x7<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE6_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7 (0x7<<3)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE7_SHIFT 3
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE4_SHIFT 6
+ u8 agg_val3_th;
+ u16 agg_vars7;
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE (0x7<<0)
+#define __XSTORM_ISCSI_AG_CONTEXT_AGG_VAL11_DECISION_RULE_SHIFT 0
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG (0x1<<3)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX13_FLAG_SHIFT 3
+#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF (0x3<<4)
+#define XSTORM_ISCSI_AG_CONTEXT_AUX18_CF_SHIFT 4
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3 (0x3<<6)
+#define XSTORM_ISCSI_AG_CONTEXT_DECISION_RULE3_SHIFT 6
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF (0x3<<8)
+#define XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_SHIFT 8
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK (0x1<<10)
+#define __XSTORM_ISCSI_AG_CONTEXT_COMPLETION_SEQ_DECISION_MASK_SHIFT 10
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN (0x1<<11)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX1_CF_EN_SHIFT 11
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG (0x1<<12)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX10_FLAG_SHIFT 12
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG (0x1<<13)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX11_FLAG_SHIFT 13
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG (0x1<<14)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX12_FLAG_SHIFT 14
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG (0x1<<15)
+#define __XSTORM_ISCSI_AG_CONTEXT_AUX2_FLAG_SHIFT 15
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __agg_val11_th;
+ u16 __agg_val11;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_val11;
+ u16 __agg_val11_th;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 __reserved1;
+ u8 __agg_val6_th;
+ u16 __agg_val9;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_val9;
+ u8 __agg_val6_th;
+ u8 __reserved1;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 hq_prod;
+ u16 hq_cons;
+#elif defined(__LITTLE_ENDIAN)
+ u16 hq_cons;
+ u16 hq_prod;
+#endif
+ u32 agg_vars8;
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2 (0xFFFFFF<<0)
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC2_SHIFT 0
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3 (0xFF<<24)
+#define XSTORM_ISCSI_AG_CONTEXT_AGG_MISC3_SHIFT 24
+#if defined(__BIG_ENDIAN)
+ u16 r2tq_prod;
+ u16 sq_prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sq_prod;
+ u16 r2tq_prod;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 agg_val3;
+ u8 agg_val6;
+ u8 agg_val5_th;
+ u8 agg_val5;
+#elif defined(__LITTLE_ENDIAN)
+ u8 agg_val5;
+ u8 agg_val5_th;
+ u8 agg_val6;
+ u8 agg_val3;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __agg_misc1;
+ u16 agg_limit1;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_limit1;
+ u16 __agg_misc1;
+#endif
+ u32 hq_cons_tcp_seq;
+ u32 exp_stat_sn;
+ u32 agg_misc5;
+};
+
+/*
+ * The tcp aggregative context section of Tstorm
+ */
+struct tstorm_tcp_tcp_ag_context_section {
+ u32 __agg_val1;
+#if defined(__BIG_ENDIAN)
+ u8 __tcp_agg_vars2;
+ u8 __agg_val3;
+ u16 __agg_val2;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_val2;
+ u8 __agg_val3;
+ u8 __tcp_agg_vars2;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __agg_val5;
+ u8 __agg_val6;
+ u8 __tcp_agg_vars3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 __tcp_agg_vars3;
+ u8 __agg_val6;
+ u16 __agg_val5;
+#endif
+ u32 snd_nxt;
+ u32 rtt_seq;
+ u32 rtt_time;
+ u32 __reserved66;
+ u32 wnd_right_edge;
+ u32 tcp_agg_vars1;
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<0)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 0
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG (0x1<<1)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_LAST_PACKET_FIN_FLAG_SHIFT 1
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF (0x3<<2)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_SHIFT 2
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF (0x3<<4)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_SHIFT 4
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN (0x1<<6)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_WND_UPD_CF_EN_SHIFT 6
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN (0x1<<7)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TIMEOUT_CF_EN_SHIFT 7
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN (0x1<<8)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_SEQ_EN_SHIFT 8
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN (0x1<<9)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_SND_NXT_EN_SHIFT 9
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG (0x1<<10)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_FLAG_SHIFT 10
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG (0x1<<11)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_FLAG_SHIFT 11
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN (0x1<<12)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_EN_SHIFT 12
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN (0x1<<13)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_EN_SHIFT 13
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF (0x3<<14)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX1_CF_SHIFT 14
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF (0x3<<16)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX2_CF_SHIFT 16
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED (0x1<<18)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_TX_BLOCKED_SHIFT 18
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN (0x1<<19)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX10_CF_EN_SHIFT 19
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN (0x1<<20)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX11_CF_EN_SHIFT 20
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN (0x1<<21)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_AUX12_CF_EN_SHIFT 21
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1 (0x3<<22)
+#define __TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RESERVED1_SHIFT 22
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ (0xF<<24)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_PEND_SEQ_SHIFT 24
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ (0xF<<28)
+#define TSTORM_TCP_TCP_AG_CONTEXT_SECTION_RETRANSMIT_DONE_SEQ_SHIFT 28
+ u32 snd_max;
+ u32 snd_una;
+ u32 __reserved2;
+};
+
+/*
+ * The iscsi aggregative context of Tstorm
+ */
+struct tstorm_iscsi_ag_context {
+#if defined(__BIG_ENDIAN)
+ u16 ulp_credit;
+ u8 agg_vars1;
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG (0x1<<7)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG_SHIFT 7
+ u8 state;
+#elif defined(__LITTLE_ENDIAN)
+ u8 state;
+ u8 agg_vars1;
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define TSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG (0x1<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX3_FLAG_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG (0x1<<7)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_FLAG_SHIFT 7
+ u16 ulp_credit;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __agg_val4;
+ u16 agg_vars2;
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG (0x1<<0)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG_SHIFT 0
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG (0x1<<1)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG_SHIFT 1
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF (0x3<<2)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_SHIFT 2
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10
+#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<11)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 11
+#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN (0x1<<12)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN_SHIFT 12
+#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN (0x1<<13)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN_SHIFT 13
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_vars2;
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG (0x1<<0)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_FLAG_SHIFT 0
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG (0x1<<1)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_FLAG_SHIFT 1
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF (0x3<<2)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_SHIFT 2
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF (0x3<<4)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_SHIFT 4
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF (0x3<<6)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_SHIFT 6
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF (0x3<<8)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_SHIFT 8
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG (0x1<<10)
+#define __TSTORM_ISCSI_AG_CONTEXT_AUX7_FLAG_SHIFT 10
+#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<11)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 11
+#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN (0x1<<12)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX4_CF_EN_SHIFT 12
+#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN (0x1<<13)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX5_CF_EN_SHIFT 13
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN (0x1<<14)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX6_CF_EN_SHIFT 14
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN (0x1<<15)
+#define TSTORM_ISCSI_AG_CONTEXT_AUX7_CF_EN_SHIFT 15
+ u16 __agg_val4;
+#endif
+ struct tstorm_tcp_tcp_ag_context_section tcp;
+};
+
+/*
+ * The iscsi aggregative context of Cstorm
+ */
+struct cstorm_iscsi_ag_context {
+ u32 agg_vars1;
+#define CSTORM_ISCSI_AG_CONTEXT_STATE (0xFF<<0)
+#define CSTORM_ISCSI_AG_CONTEXT_STATE_SHIFT 0
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<8)
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 8
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<9)
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 9
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<10)
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 10
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<11)
+#define __CSTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 11
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_SE_CF_EN (0x1<<12)
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_SE_CF_EN_SHIFT 12
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_INV_CF_EN (0x1<<13)
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED_ULP_RX_INV_CF_EN_SHIFT 13
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF (0x3<<14)
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_SHIFT 14
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED66 (0x3<<16)
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED66_SHIFT 16
+#define __CSTORM_ISCSI_AG_CONTEXT_FIN_RECEIVED_CF_EN (0x1<<18)
+#define __CSTORM_ISCSI_AG_CONTEXT_FIN_RECEIVED_CF_EN_SHIFT 18
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION0_CF_EN (0x1<<19)
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION0_CF_EN_SHIFT 19
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION1_CF_EN (0x1<<20)
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION1_CF_EN_SHIFT 20
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION2_CF_EN (0x1<<21)
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION2_CF_EN_SHIFT 21
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_EN (0x1<<22)
+#define __CSTORM_ISCSI_AG_CONTEXT_PENDING_COMPLETION3_CF_EN_SHIFT 22
+#define __CSTORM_ISCSI_AG_CONTEXT_REL_SEQ_RULE (0x7<<23)
+#define __CSTORM_ISCSI_AG_CONTEXT_REL_SEQ_RULE_SHIFT 23
+#define CSTORM_ISCSI_AG_CONTEXT_HQ_PROD_RULE (0x3<<26)
+#define CSTORM_ISCSI_AG_CONTEXT_HQ_PROD_RULE_SHIFT 26
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED52 (0x3<<28)
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED52_SHIFT 28
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED53 (0x3<<30)
+#define __CSTORM_ISCSI_AG_CONTEXT_RESERVED53_SHIFT 30
+#if defined(__BIG_ENDIAN)
+ u8 __aux1_th;
+ u8 __aux1_val;
+ u16 __agg_vars2;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __agg_vars2;
+ u8 __aux1_val;
+ u8 __aux1_th;
+#endif
+ u32 rel_seq;
+ u32 rel_seq_th;
+#if defined(__BIG_ENDIAN)
+ u16 hq_cons;
+ u16 hq_prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 hq_prod;
+ u16 hq_cons;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 __reserved62;
+ u8 __reserved61;
+ u8 __reserved60;
+ u8 __reserved59;
+#elif defined(__LITTLE_ENDIAN)
+ u8 __reserved59;
+ u8 __reserved60;
+ u8 __reserved61;
+ u8 __reserved62;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __reserved64;
+ u16 __cq_u_prod0;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __cq_u_prod0;
+ u16 __reserved64;
+#endif
+ u32 __cq_u_prod1;
+#if defined(__BIG_ENDIAN)
+ u16 __agg_vars3;
+ u16 __cq_u_prod2;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __cq_u_prod2;
+ u16 __agg_vars3;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __aux2_th;
+ u16 __cq_u_prod3;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __cq_u_prod3;
+ u16 __aux2_th;
+#endif
+};
+
+/*
+ * The iscsi aggregative context of Ustorm
+ */
+struct ustorm_iscsi_ag_context {
+#if defined(__BIG_ENDIAN)
+ u8 __aux_counter_flags;
+ u8 agg_vars2;
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
+ u8 agg_vars1;
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6
+ u8 state;
+#elif defined(__LITTLE_ENDIAN)
+ u8 state;
+ u8 agg_vars1;
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0 (0x1<<0)
+#define __USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM0_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1 (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM1_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2 (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM2_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3 (0x1<<3)
+#define USTORM_ISCSI_AG_CONTEXT_EXISTS_IN_QM3_SHIFT 3
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF (0x3<<4)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_SHIFT 4
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF (0x3<<6)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_SHIFT 6
+ u8 agg_vars2;
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF (0x3<<0)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF (0x3<<2)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_SHIFT 2
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE (0x7<<4)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_MISC4_RULE_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_MASK_SHIFT 7
+ u8 __aux_counter_flags;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 cdu_usage;
+ u8 agg_misc2;
+ u16 __cq_local_comp_itt_val;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __cq_local_comp_itt_val;
+ u8 agg_misc2;
+ u8 cdu_usage;
+#endif
+ u32 agg_misc4;
+#if defined(__BIG_ENDIAN)
+ u8 agg_val3_th;
+ u8 agg_val3;
+ u16 agg_misc3;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_misc3;
+ u8 agg_val3;
+ u8 agg_val3_th;
+#endif
+ u32 agg_val1;
+ u32 agg_misc4_th;
+#if defined(__BIG_ENDIAN)
+ u16 agg_val2_th;
+ u16 agg_val2;
+#elif defined(__LITTLE_ENDIAN)
+ u16 agg_val2;
+ u16 agg_val2_th;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 __reserved2;
+ u8 decision_rules;
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7
+ u8 decision_rule_enable_bits;
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4)
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5)
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+#elif defined(__LITTLE_ENDIAN)
+ u8 decision_rule_enable_bits;
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN (0x1<<0)
+#define USTORM_ISCSI_AG_CONTEXT_INV_CF_EN_SHIFT 0
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN (0x1<<1)
+#define USTORM_ISCSI_AG_CONTEXT_COMPLETION_CF_EN_SHIFT 1
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN (0x1<<2)
+#define USTORM_ISCSI_AG_CONTEXT_TX_CF_EN_SHIFT 2
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN (0x1<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_TIMER_CF_EN_SHIFT 3
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN (0x1<<4)
+#define __USTORM_ISCSI_AG_CONTEXT_CQ_LOCAL_COMP_CF_EN_SHIFT 4
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN (0x1<<5)
+#define __USTORM_ISCSI_AG_CONTEXT_QUEUES_FLUSH_Q0_CF_EN_SHIFT 5
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN (0x1<<6)
+#define __USTORM_ISCSI_AG_CONTEXT_AUX3_CF_EN_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_DQ_CF_EN_SHIFT 7
+ u8 decision_rules;
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE (0x7<<0)
+#define USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_RULE_SHIFT 0
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE (0x7<<3)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL3_RULE_SHIFT 3
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG (0x1<<6)
+#define __USTORM_ISCSI_AG_CONTEXT_AGG_VAL2_ARM_N_FLAG_SHIFT 6
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1 (0x1<<7)
+#define __USTORM_ISCSI_AG_CONTEXT_RESERVED1_SHIFT 7
+ u16 __reserved2;
+#endif
+};
+
+/*
+ * Timers connection context
+ */
+struct iscsi_timers_block_context {
+ u32 __reserved_0;
+ u32 __reserved_1;
+ u32 __reserved_2;
+ u32 flags;
+#define __ISCSI_TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS (0x3<<0)
+#define __ISCSI_TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS_SHIFT 0
+#define ISCSI_TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG (0x1<<2)
+#define ISCSI_TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG_SHIFT 2
+#define __ISCSI_TIMERS_BLOCK_CONTEXT_RESERVED0 (0x1FFFFFFF<<3)
+#define __ISCSI_TIMERS_BLOCK_CONTEXT_RESERVED0_SHIFT 3
+};
+
+/*
+ * Ethernet context section, shared in TOE, RDMA and ISCSI
+ */
+struct xstorm_eth_context_section {
+#if defined(__BIG_ENDIAN)
+ u8 remote_addr_4;
+ u8 remote_addr_5;
+ u8 local_addr_0;
+ u8 local_addr_1;
+#elif defined(__LITTLE_ENDIAN)
+ u8 local_addr_1;
+ u8 local_addr_0;
+ u8 remote_addr_5;
+ u8 remote_addr_4;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 remote_addr_0;
+ u8 remote_addr_1;
+ u8 remote_addr_2;
+ u8 remote_addr_3;
+#elif defined(__LITTLE_ENDIAN)
+ u8 remote_addr_3;
+ u8 remote_addr_2;
+ u8 remote_addr_1;
+ u8 remote_addr_0;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 reserved_vlan_type;
+ u16 params;
+#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID (0xFFF<<0)
+#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID_SHIFT 0
+#define XSTORM_ETH_CONTEXT_SECTION_CFI (0x1<<12)
+#define XSTORM_ETH_CONTEXT_SECTION_CFI_SHIFT 12
+#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY (0x7<<13)
+#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY_SHIFT 13
+#elif defined(__LITTLE_ENDIAN)
+ u16 params;
+#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID (0xFFF<<0)
+#define XSTORM_ETH_CONTEXT_SECTION_VLAN_ID_SHIFT 0
+#define XSTORM_ETH_CONTEXT_SECTION_CFI (0x1<<12)
+#define XSTORM_ETH_CONTEXT_SECTION_CFI_SHIFT 12
+#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY (0x7<<13)
+#define XSTORM_ETH_CONTEXT_SECTION_PRIORITY_SHIFT 13
+ u16 reserved_vlan_type;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 local_addr_2;
+ u8 local_addr_3;
+ u8 local_addr_4;
+ u8 local_addr_5;
+#elif defined(__LITTLE_ENDIAN)
+ u8 local_addr_5;
+ u8 local_addr_4;
+ u8 local_addr_3;
+ u8 local_addr_2;
+#endif
+};
+
+/*
+ * IpV4 context section, shared in TOE, RDMA and ISCSI
+ */
+struct xstorm_ip_v4_context_section {
+#if defined(__BIG_ENDIAN)
+ u16 __pbf_hdr_cmd_rsvd_id;
+ u16 __pbf_hdr_cmd_rsvd_flags_offset;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __pbf_hdr_cmd_rsvd_flags_offset;
+ u16 __pbf_hdr_cmd_rsvd_id;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 __pbf_hdr_cmd_rsvd_ver_ihl;
+ u8 tos;
+ u16 __pbf_hdr_cmd_rsvd_length;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __pbf_hdr_cmd_rsvd_length;
+ u8 tos;
+ u8 __pbf_hdr_cmd_rsvd_ver_ihl;
+#endif
+ u32 ip_local_addr;
+#if defined(__BIG_ENDIAN)
+ u8 ttl;
+ u8 __pbf_hdr_cmd_rsvd_protocol;
+ u16 __pbf_hdr_cmd_rsvd_csum;
+#elif defined(__LITTLE_ENDIAN)
+ u16 __pbf_hdr_cmd_rsvd_csum;
+ u8 __pbf_hdr_cmd_rsvd_protocol;
+ u8 ttl;
+#endif
+ u32 __pbf_hdr_cmd_rsvd_1;
+ u32 ip_remote_addr;
+};
+
+/*
+ * context section, shared in TOE, RDMA and ISCSI
+ */
+struct xstorm_padded_ip_v4_context_section {
+ struct xstorm_ip_v4_context_section ip_v4;
+ u32 reserved1[4];
+};
+
+/*
+ * IpV6 context section, shared in TOE, RDMA and ISCSI
+ */
+struct xstorm_ip_v6_context_section {
+#if defined(__BIG_ENDIAN)
+ u16 pbf_hdr_cmd_rsvd_payload_len;
+ u8 pbf_hdr_cmd_rsvd_nxt_hdr;
+ u8 hop_limit;
+#elif defined(__LITTLE_ENDIAN)
+ u8 hop_limit;
+ u8 pbf_hdr_cmd_rsvd_nxt_hdr;
+ u16 pbf_hdr_cmd_rsvd_payload_len;
+#endif
+ u32 priority_flow_label;
+#define XSTORM_IP_V6_CONTEXT_SECTION_FLOW_LABEL (0xFFFFF<<0)
+#define XSTORM_IP_V6_CONTEXT_SECTION_FLOW_LABEL_SHIFT 0
+#define XSTORM_IP_V6_CONTEXT_SECTION_TRAFFIC_CLASS (0xFF<<20)
+#define XSTORM_IP_V6_CONTEXT_SECTION_TRAFFIC_CLASS_SHIFT 20
+#define XSTORM_IP_V6_CONTEXT_SECTION_PBF_HDR_CMD_RSVD_VER (0xF<<28)
+#define XSTORM_IP_V6_CONTEXT_SECTION_PBF_HDR_CMD_RSVD_VER_SHIFT 28
+ u32 ip_local_addr_lo_hi;
+ u32 ip_local_addr_lo_lo;
+ u32 ip_local_addr_hi_hi;
+ u32 ip_local_addr_hi_lo;
+ u32 ip_remote_addr_lo_hi;
+ u32 ip_remote_addr_lo_lo;
+ u32 ip_remote_addr_hi_hi;
+ u32 ip_remote_addr_hi_lo;
+};
+
+union xstorm_ip_context_section_types {
+ struct xstorm_padded_ip_v4_context_section padded_ip_v4;
+ struct xstorm_ip_v6_context_section ip_v6;
+};
+
+/*
+ * TCP context section, shared in TOE, RDMA and ISCSI
+ */
+struct xstorm_tcp_context_section {
+ u32 snd_max;
+#if defined(__BIG_ENDIAN)
+ u16 remote_port;
+ u16 local_port;
+#elif defined(__LITTLE_ENDIAN)
+ u16 local_port;
+ u16 remote_port;
+#endif
+#if defined(__BIG_ENDIAN)
+ u8 original_nagle_1b;
+ u8 ts_enabled_1b;
+ u16 tcp_params;
+#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE (0xFF<<0)
+#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE_SHIFT 0
+#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT (0x1<<8)
+#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT_SHIFT 8
+#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED (0x1<<9)
+#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED_SHIFT 9
+#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED (0x1<<10)
+#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED_SHIFT 10
+#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE (0x1<<11)
+#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE_SHIFT 11
+#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<12)
+#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 12
+#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED (0x1<<13)
+#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED_SHIFT 13
+#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER (0x3<<14)
+#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER_SHIFT 14
+#elif defined(__LITTLE_ENDIAN)
+ u16 tcp_params;
+#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE (0xFF<<0)
+#define XSTORM_TCP_CONTEXT_SECTION_TOTAL_HEADER_SIZE_SHIFT 0
+#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT (0x1<<8)
+#define __XSTORM_TCP_CONTEXT_SECTION_ECT_BIT_SHIFT 8
+#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED (0x1<<9)
+#define __XSTORM_TCP_CONTEXT_SECTION_ECN_ENABLED_SHIFT 9
+#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED (0x1<<10)
+#define XSTORM_TCP_CONTEXT_SECTION_SACK_ENABLED_SHIFT 10
+#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE (0x1<<11)
+#define XSTORM_TCP_CONTEXT_SECTION_KA_STATE_SHIFT 11
+#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG (0x1<<12)
+#define XSTORM_TCP_CONTEXT_SECTION_FIN_SENT_FLAG_SHIFT 12
+#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED (0x1<<13)
+#define XSTORM_TCP_CONTEXT_SECTION_WINDOW_SATURATED_SHIFT 13
+#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER (0x3<<14)
+#define XSTORM_TCP_CONTEXT_SECTION_SLOWPATH_QUEUES_FLUSH_COUNTER_SHIFT 14
+ u8 ts_enabled_1b;
+ u8 original_nagle_1b;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 pseudo_csum;
+ u16 window_scaling_factor;
+#elif defined(__LITTLE_ENDIAN)
+ u16 window_scaling_factor;
+ u16 pseudo_csum;
+#endif
+ u32 reserved2;
+ u32 ts_time_diff;
+ u32 __next_timer_expir;
+};
+
+/*
+ * Common context section, shared in TOE, RDMA and ISCSI
+ */
+struct xstorm_common_context_section {
+ struct xstorm_eth_context_section ethernet;
+ union xstorm_ip_context_section_types ip_union;
+ struct xstorm_tcp_context_section tcp;
+#if defined(__BIG_ENDIAN)
+ u16 reserved;
+ u8 statistics_params;
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0)
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1)
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1
+#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID (0x1F<<2)
+#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID_SHIFT 2
+#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0 (0x1<<7)
+#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0_SHIFT 7
+ u8 ip_version_1b;
+#elif defined(__LITTLE_ENDIAN)
+ u8 ip_version_1b;
+ u8 statistics_params;
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS (0x1<<0)
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L2_STATSTICS_SHIFT 0
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS (0x1<<1)
+#define XSTORM_COMMON_CONTEXT_SECTION_UPDATE_L4_STATSTICS_SHIFT 1
+#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID (0x1F<<2)
+#define XSTORM_COMMON_CONTEXT_SECTION_STATISTICS_COUNTER_ID_SHIFT 2
+#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0 (0x1<<7)
+#define XSTORM_COMMON_CONTEXT_SECTION_RESERVED0_SHIFT 7
+ u16 reserved;
+#endif
+};
+
+/*
+ * Flags used in ISCSI context section
+ */
+struct xstorm_iscsi_context_flags {
+ u8 flags;
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA (0x1<<0)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA_SHIFT 0
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T (0x1<<1)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T_SHIFT 1
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_HEADER_DIGEST (0x1<<2)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_HEADER_DIGEST_SHIFT 2
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_DATA_DIGEST (0x1<<3)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_DATA_DIGEST_SHIFT 3
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_HQ_BD_WRITTEN (0x1<<4)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_HQ_BD_WRITTEN_SHIFT 4
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_LAST_OP_SQ (0x1<<5)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_LAST_OP_SQ_SHIFT 5
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_UPDATE_SND_NXT (0x1<<6)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_B_UPDATE_SND_NXT_SHIFT 6
+#define XSTORM_ISCSI_CONTEXT_FLAGS_RESERVED4 (0x1<<7)
+#define XSTORM_ISCSI_CONTEXT_FLAGS_RESERVED4_SHIFT 7
+};
+
+struct iscsi_task_context_entry_x {
+ u32 data_out_buffer_offset;
+ u32 itt;
+ u32 data_sn;
+};
+
+struct iscsi_task_context_entry_xuc_x_write_only {
+ u32 tx_r2t_sn;
+};
+
+struct iscsi_task_context_entry_xuc_xu_write_both {
+ u32 sgl_base_lo;
+ u32 sgl_base_hi;
+#if defined(__BIG_ENDIAN)
+ u8 sgl_size;
+ u8 sge_index;
+ u16 sge_offset;
+#elif defined(__LITTLE_ENDIAN)
+ u16 sge_offset;
+ u8 sge_index;
+ u8 sgl_size;
+#endif
+};
+
+/*
+ * iSCSI context section
+ */
+struct xstorm_iscsi_context_section {
+ u32 first_burst_length;
+ u32 max_send_pdu_length;
+ struct regpair sq_pbl_base;
+ struct regpair sq_curr_pbe;
+ struct regpair hq_pbl_base;
+ struct regpair hq_curr_pbe_base;
+ struct regpair r2tq_pbl_base;
+ struct regpair r2tq_curr_pbe_base;
+ struct regpair task_pbl_base;
+#if defined(__BIG_ENDIAN)
+ u16 data_out_count;
+ struct xstorm_iscsi_context_flags flags;
+ u8 task_pbl_cache_idx;
+#elif defined(__LITTLE_ENDIAN)
+ u8 task_pbl_cache_idx;
+ struct xstorm_iscsi_context_flags flags;
+ u16 data_out_count;
+#endif
+ u32 seq_more_2_send;
+ u32 pdu_more_2_send;
+ struct iscsi_task_context_entry_x temp_tce_x;
+ struct iscsi_task_context_entry_xuc_x_write_only temp_tce_x_wr;
+ struct iscsi_task_context_entry_xuc_xu_write_both temp_tce_xu_wr;
+ struct regpair lun;
+ u32 exp_data_transfer_len_ttt;
+ u32 pdu_data_2_rxmit;
+ u32 rxmit_bytes_2_dr;
+#if defined(__BIG_ENDIAN)
+ u16 rxmit_sge_offset;
+ u16 hq_rxmit_cons;
+#elif defined(__LITTLE_ENDIAN)
+ u16 hq_rxmit_cons;
+ u16 rxmit_sge_offset;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 r2tq_cons;
+ u8 rxmit_flags;
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD (0x1<<0)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD_SHIFT 0
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR (0x1<<1)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR_SHIFT 1
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU (0x1<<2)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU_SHIFT 2
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR (0x1<<3)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR_SHIFT 3
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR (0x1<<4)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR_SHIFT 4
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING (0x3<<5)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING_SHIFT 5
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT (0x1<<7)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT_SHIFT 7
+ u8 rxmit_sge_idx;
+#elif defined(__LITTLE_ENDIAN)
+ u8 rxmit_sge_idx;
+ u8 rxmit_flags;
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD (0x1<<0)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_NEW_HQ_BD_SHIFT 0
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR (0x1<<1)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PDU_HDR_SHIFT 1
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU (0x1<<2)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_END_PDU_SHIFT 2
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR (0x1<<3)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_DR_SHIFT 3
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR (0x1<<4)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_START_DR_SHIFT 4
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING (0x3<<5)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_RXMIT_PADDING_SHIFT 5
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT (0x1<<7)
+#define XSTORM_ISCSI_CONTEXT_SECTION_B_ISCSI_CONT_FAST_RXMIT_SHIFT 7
+ u16 r2tq_cons;
+#endif
+ u32 hq_rxmit_tcp_seq;
+};
+
+/*
+ * Xstorm iSCSI Storm Context
+ */
+struct xstorm_iscsi_st_context {
+ struct xstorm_common_context_section common;
+ struct xstorm_iscsi_context_section iscsi;
+};
+
+/*
+ * CQ DB CQ producer and pending completion counter
+ */
+struct iscsi_cq_db_prod_pnd_cmpltn_cnt {
+#if defined(__BIG_ENDIAN)
+ u16 cntr;
+ u16 prod;
+#elif defined(__LITTLE_ENDIAN)
+ u16 prod;
+ u16 cntr;
+#endif
+};
+
+/*
+ * CQ DB pending completion ITT array
+ */
+struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr {
+ struct iscsi_cq_db_prod_pnd_cmpltn_cnt prod_pend_comp[8];
+};
+
+/*
+ * Cstorm CQ sequence to notify array, updated by driver
+ */
+struct iscsi_cq_db_sqn_2_notify_arr {
+ u16 sqn[8];
+};
+
+/*
+ * Cstorm iSCSI Storm Context
+ */
+struct cstorm_iscsi_st_context {
+ struct iscsi_cq_db_prod_pnd_cmpltn_cnt_arr cq_c_prod_pend_comp_ctr_arr;
+ struct iscsi_cq_db_sqn_2_notify_arr cq_c_prod_sqn_arr;
+ struct iscsi_cq_db_sqn_2_notify_arr cq_c_sqn_2_notify_arr;
+ struct regpair hq_pbl_base;
+ struct regpair hq_curr_pbe;
+ struct regpair task_pbl_base;
+ struct regpair cq_db_base;
+#if defined(__BIG_ENDIAN)
+ u16 hq_bd_itt;
+ u16 iscsi_conn_id;
+#elif defined(__LITTLE_ENDIAN)
+ u16 iscsi_conn_id;
+ u16 hq_bd_itt;
+#endif
+ u32 hq_bd_data_segment_len;
+ u32 hq_bd_buffer_offset;
+#if defined(__BIG_ENDIAN)
+ u8 timer_entry_idx;
+ u8 cq_proc_en_bit_map;
+ u8 cq_pend_comp_itt_valid_bit_map;
+ u8 hq_bd_opcode;
+#elif defined(__LITTLE_ENDIAN)
+ u8 hq_bd_opcode;
+ u8 cq_pend_comp_itt_valid_bit_map;
+ u8 cq_proc_en_bit_map;
+ u8 timer_entry_idx;
+#endif
+ u32 hq_tcp_seq;
+#if defined(__BIG_ENDIAN)
+ u16 flags;
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0)
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1)
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5)
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5
+ u16 hq_cons;
+#elif defined(__LITTLE_ENDIAN)
+ u16 hq_cons;
+ u16 flags;
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN (0x1<<0)
+#define CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN_SHIFT 0
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN (0x1<<1)
+#define CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN_SHIFT 1
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID (0x1<<2)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_CTXT_VALID_SHIFT 2
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG (0x1<<3)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_LCL_CMPLN_FLG_SHIFT 3
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK (0x1<<4)
+#define CSTORM_ISCSI_ST_CONTEXT_HQ_BD_WRITE_TASK_SHIFT 4
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV (0x7FF<<5)
+#define CSTORM_ISCSI_ST_CONTEXT_CTRL_FLAGS_RSRV_SHIFT 5
+#endif
+ struct regpair rsrv1;
+};
+
+/*
+ * Iscsi connection context
+ */
+struct iscsi_context {
+ struct ustorm_iscsi_st_context ustorm_st_context;
+ struct tstorm_iscsi_st_context tstorm_st_context;
+ struct xstorm_iscsi_ag_context xstorm_ag_context;
+ struct tstorm_iscsi_ag_context tstorm_ag_context;
+ struct cstorm_iscsi_ag_context cstorm_ag_context;
+ struct ustorm_iscsi_ag_context ustorm_ag_context;
+ struct iscsi_timers_block_context timers_context;
+ struct regpair upb_context;
+ struct xstorm_iscsi_st_context xstorm_st_context;
+ struct regpair xpb_context;
+ struct cstorm_iscsi_st_context cstorm_st_context;
+};
+
+/*
+ * Buffer per connection, used in Tstorm
+ */
+struct iscsi_conn_buf {
+ struct regpair reserved[8];
+};
+
+/*
+ * ipv6 structure
+ */
+struct ip_v6_addr {
+ u32 ip_addr_lo_lo;
+ u32 ip_addr_lo_hi;
+ u32 ip_addr_hi_lo;
+ u32 ip_addr_hi_hi;
+};
+
+/*
+ * l5cm- connection identification params
+ */
+struct l5cm_conn_addr_params {
+ u32 pmtu;
+#if defined(__BIG_ENDIAN)
+ u8 remote_addr_3;
+ u8 remote_addr_2;
+ u8 remote_addr_1;
+ u8 remote_addr_0;
+#elif defined(__LITTLE_ENDIAN)
+ u8 remote_addr_0;
+ u8 remote_addr_1;
+ u8 remote_addr_2;
+ u8 remote_addr_3;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 params;
+#define L5CM_CONN_ADDR_PARAMS_IP_VERSION (0x1<<0)
+#define L5CM_CONN_ADDR_PARAMS_IP_VERSION_SHIFT 0
+#define L5CM_CONN_ADDR_PARAMS_RSRV (0x7FFF<<1)
+#define L5CM_CONN_ADDR_PARAMS_RSRV_SHIFT 1
+ u8 remote_addr_5;
+ u8 remote_addr_4;
+#elif defined(__LITTLE_ENDIAN)
+ u8 remote_addr_4;
+ u8 remote_addr_5;
+ u16 params;
+#define L5CM_CONN_ADDR_PARAMS_IP_VERSION (0x1<<0)
+#define L5CM_CONN_ADDR_PARAMS_IP_VERSION_SHIFT 0
+#define L5CM_CONN_ADDR_PARAMS_RSRV (0x7FFF<<1)
+#define L5CM_CONN_ADDR_PARAMS_RSRV_SHIFT 1
+#endif
+ struct ip_v6_addr local_ip_addr;
+ struct ip_v6_addr remote_ip_addr;
+ u32 ipv6_flow_label_20b;
+ u32 reserved1;
+#if defined(__BIG_ENDIAN)
+ u16 remote_tcp_port;
+ u16 local_tcp_port;
+#elif defined(__LITTLE_ENDIAN)
+ u16 local_tcp_port;
+ u16 remote_tcp_port;
+#endif
+};
+
+/*
+ * l5cm-xstorm connection buffer
+ */
+struct l5cm_xstorm_conn_buffer {
+#if defined(__BIG_ENDIAN)
+ u16 rsrv1;
+ u16 params;
+#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE (0x1<<0)
+#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE_SHIFT 0
+#define L5CM_XSTORM_CONN_BUFFER_RSRV (0x7FFF<<1)
+#define L5CM_XSTORM_CONN_BUFFER_RSRV_SHIFT 1
+#elif defined(__LITTLE_ENDIAN)
+ u16 params;
+#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE (0x1<<0)
+#define L5CM_XSTORM_CONN_BUFFER_NAGLE_ENABLE_SHIFT 0
+#define L5CM_XSTORM_CONN_BUFFER_RSRV (0x7FFF<<1)
+#define L5CM_XSTORM_CONN_BUFFER_RSRV_SHIFT 1
+ u16 rsrv1;
+#endif
+#if defined(__BIG_ENDIAN)
+ u16 mss;
+ u16 pseudo_header_checksum;
+#elif defined(__LITTLE_ENDIAN)
+ u16 pseudo_header_checksum;
+ u16 mss;
+#endif
+ u32 rcv_buf;
+ u32 rsrv2;
+ struct regpair context_addr;
+};
+
+/*
+ * l5cm-tstorm connection buffer
+ */
+struct l5cm_tstorm_conn_buffer {
+ u32 snd_buf;
+ u32 rcv_buf;
+#if defined(__BIG_ENDIAN)
+ u16 params;
+#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE (0x1<<0)
+#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE_SHIFT 0
+#define L5CM_TSTORM_CONN_BUFFER_RSRV (0x7FFF<<1)
+#define L5CM_TSTORM_CONN_BUFFER_RSRV_SHIFT 1
+ u8 ka_max_probe_count;
+ u8 ka_enable;
+#elif defined(__LITTLE_ENDIAN)
+ u8 ka_enable;
+ u8 ka_max_probe_count;
+ u16 params;
+#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE (0x1<<0)
+#define L5CM_TSTORM_CONN_BUFFER_DELAYED_ACK_ENABLE_SHIFT 0
+#define L5CM_TSTORM_CONN_BUFFER_RSRV (0x7FFF<<1)
+#define L5CM_TSTORM_CONN_BUFFER_RSRV_SHIFT 1
+#endif
+ u32 ka_timeout;
+ u32 ka_interval;
+ u32 max_rt_time;
+};
+
+/*
+ * l5cm connection buffer for active side
+ */
+struct l5cm_active_conn_buffer {
+ struct l5cm_conn_addr_params conn_addr_buf;
+ struct l5cm_xstorm_conn_buffer xstorm_conn_buffer;
+ struct l5cm_tstorm_conn_buffer tstorm_conn_buffer;
+};
+
+/*
+ * l5cm slow path element
+ */
+struct l5cm_packet_size {
+ u32 size;
+ u32 rsrv;
+};
+
+/*
+ * l5cm connection parameters
+ */
+union l5cm_reduce_param_union {
+ u32 passive_side_scramble_key;
+ u32 pcs_id;
+};
+
+/*
+ * l5cm connection parameters
+ */
+struct l5cm_reduce_conn {
+ union l5cm_reduce_param_union param;
+ u32 isn;
+};
+
+/*
+ * l5cm slow path element
+ */
+union l5cm_specific_data {
+ u8 protocol_data[8];
+ struct regpair phy_address;
+ struct l5cm_packet_size packet_size;
+ struct l5cm_reduce_conn reduced_conn;
+};
+
+/*
+ * l5 slow path element
+ */
+struct l5cm_spe {
+ struct spe_hdr hdr;
+ union l5cm_specific_data data;
+};
+
+/*
+ * Tstorm Tcp flags
+ */
+struct tstorm_l5cm_tcp_flags {
+ u16 flags;
+#define TSTORM_L5CM_TCP_FLAGS_VLAN_ID (0xFFF<<0)
+#define TSTORM_L5CM_TCP_FLAGS_VLAN_ID_SHIFT 0
+#define TSTORM_L5CM_TCP_FLAGS_RSRV0 (0x1<<12)
+#define TSTORM_L5CM_TCP_FLAGS_RSRV0_SHIFT 12
+#define TSTORM_L5CM_TCP_FLAGS_TS_ENABLED (0x1<<13)
+#define TSTORM_L5CM_TCP_FLAGS_TS_ENABLED_SHIFT 13
+#define TSTORM_L5CM_TCP_FLAGS_RSRV1 (0x3<<14)
+#define TSTORM_L5CM_TCP_FLAGS_RSRV1_SHIFT 14
+};
+
+/*
+ * Xstorm Tcp flags
+ */
+struct xstorm_l5cm_tcp_flags {
+ u8 flags;
+#define XSTORM_L5CM_TCP_FLAGS_ENC_ENABLED (0x1<<0)
+#define XSTORM_L5CM_TCP_FLAGS_ENC_ENABLED_SHIFT 0
+#define XSTORM_L5CM_TCP_FLAGS_TS_ENABLED (0x1<<1)
+#define XSTORM_L5CM_TCP_FLAGS_TS_ENABLED_SHIFT 1
+#define XSTORM_L5CM_TCP_FLAGS_WND_SCL_EN (0x1<<2)
+#define XSTORM_L5CM_TCP_FLAGS_WND_SCL_EN_SHIFT 2
+#define XSTORM_L5CM_TCP_FLAGS_RSRV (0x1F<<3)
+#define XSTORM_L5CM_TCP_FLAGS_RSRV_SHIFT 3
+};
+
#endif /* CNIC_DEFS_H */
diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h
index d8b09efdcb5..8aaf98bdd4f 100644
--- a/drivers/net/cnic_if.h
+++ b/drivers/net/cnic_if.h
@@ -12,8 +12,8 @@
#ifndef CNIC_IF_H
#define CNIC_IF_H
-#define CNIC_MODULE_VERSION "2.0.1"
-#define CNIC_MODULE_RELDATE "Oct 01, 2009"
+#define CNIC_MODULE_VERSION "2.1.0"
+#define CNIC_MODULE_RELDATE "Oct 10, 2009"
#define CNIC_ULP_RDMA 0
#define CNIC_ULP_ISCSI 1
@@ -81,6 +81,8 @@ struct kcqe {
#define DRV_CTL_CTX_WR_CMD 0x103
#define DRV_CTL_CTXTBL_WR_CMD 0x104
#define DRV_CTL_COMPLETION_CMD 0x105
+#define DRV_CTL_START_L2_CMD 0x106
+#define DRV_CTL_STOP_L2_CMD 0x107
struct cnic_ctl_completion {
u32 cid;
@@ -105,11 +107,17 @@ struct drv_ctl_io {
dma_addr_t dma_addr;
};
+struct drv_ctl_l2_ring {
+ u32 client_id;
+ u32 cid;
+};
+
struct drv_ctl_info {
int cmd;
union {
struct drv_ctl_completion comp;
struct drv_ctl_io io;
+ struct drv_ctl_l2_ring ring;
char bytes[MAX_DRV_CTL_DATA];
} data;
};
@@ -143,6 +151,7 @@ struct cnic_eth_dev {
u32 max_kwqe_pending;
struct pci_dev *pdev;
void __iomem *io_base;
+ void __iomem *io_base2;
u32 ctx_tbl_offset;
u32 ctx_tbl_len;
@@ -298,5 +307,6 @@ extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops);
extern int cnic_unregister_driver(int ulp_type);
extern struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev);
+extern struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev);
#endif
diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
index 61f9da2b494..67822238940 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/cpmac.c
@@ -380,9 +380,8 @@ static struct sk_buff *cpmac_rx_one(struct cpmac_priv *priv,
return NULL;
}
- skb = netdev_alloc_skb(priv->dev, CPMAC_SKB_SIZE);
+ skb = netdev_alloc_skb_ip_align(priv->dev, CPMAC_SKB_SIZE);
if (likely(skb)) {
- skb_reserve(skb, 2);
skb_put(desc->skb, desc->datalen);
desc->skb->protocol = eth_type_trans(desc->skb, priv->dev);
desc->skb->ip_summed = CHECKSUM_NONE;
@@ -991,12 +990,11 @@ static int cpmac_open(struct net_device *dev)
priv->rx_head = &priv->desc_ring[CPMAC_QUEUES];
for (i = 0, desc = priv->rx_head; i < priv->ring_size; i++, desc++) {
- skb = netdev_alloc_skb(dev, CPMAC_SKB_SIZE);
+ skb = netdev_alloc_skb_ip_align(dev, CPMAC_SKB_SIZE);
if (unlikely(!skb)) {
res = -ENOMEM;
goto fail_desc;
}
- skb_reserve(skb, 2);
desc->skb = skb;
desc->data_mapping = dma_map_single(&dev->dev, skb->data,
CPMAC_SKB_SIZE,
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 0c54219960e..af9321617ce 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -1323,7 +1323,7 @@ net_open(struct net_device *dev)
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
#endif
write_irq(dev, lp->chip_type, dev->irq);
- ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev);
+ ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev);
if (ret) {
if (net_debug)
printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq);
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h
index 2b1aea6aa55..3e8618b4efb 100644
--- a/drivers/net/cxgb3/adapter.h
+++ b/drivers/net/cxgb3/adapter.h
@@ -48,12 +48,27 @@
struct vlan_group;
struct adapter;
struct sge_qset;
+struct port_info;
enum { /* rx_offload flags */
T3_RX_CSUM = 1 << 0,
T3_LRO = 1 << 1,
};
+enum mac_idx_types {
+ LAN_MAC_IDX = 0,
+ SAN_MAC_IDX,
+
+ MAX_MAC_IDX
+};
+
+struct iscsi_config {
+ __u8 mac_addr[ETH_ALEN];
+ __u32 flags;
+ int (*send)(struct port_info *pi, struct sk_buff **skb);
+ int (*recv)(struct port_info *pi, struct sk_buff *skb);
+};
+
struct port_info {
struct adapter *adapter;
struct vlan_group *vlan_grp;
@@ -68,6 +83,7 @@ struct port_info {
struct net_device_stats netstats;
int activity;
__be32 iscsi_ipv4addr;
+ struct iscsi_config iscsic;
int link_fault; /* link fault was detected */
};
diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h
index 1b2c305fb82..6ff356d4c7a 100644
--- a/drivers/net/cxgb3/common.h
+++ b/drivers/net/cxgb3/common.h
@@ -125,11 +125,9 @@ enum { /* adapter interrupt-maintained statistics */
IRQ_NUM_STATS /* keep last */
};
-enum {
- TP_VERSION_MAJOR = 1,
- TP_VERSION_MINOR = 1,
- TP_VERSION_MICRO = 0
-};
+#define TP_VERSION_MAJOR 1
+#define TP_VERSION_MINOR 1
+#define TP_VERSION_MICRO 0
#define S_TP_VERSION_MAJOR 16
#define M_TP_VERSION_MAJOR 0xFF
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c
index 34e776c5f06..cef3f882e2b 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/cxgb3/cxgb3_main.c
@@ -44,6 +44,7 @@
#include <linux/rtnetlink.h>
#include <linux/firmware.h>
#include <linux/log2.h>
+#include <linux/stringify.h>
#include <asm/uaccess.h>
#include "common.h"
@@ -344,8 +345,10 @@ static void link_start(struct net_device *dev)
init_rx_mode(&rm, dev, dev->mc_list);
t3_mac_reset(mac);
+ t3_mac_set_num_ucast(mac, MAX_MAC_IDX);
t3_mac_set_mtu(mac, dev->mtu);
- t3_mac_set_address(mac, 0, dev->dev_addr);
+ t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr);
+ t3_mac_set_address(mac, SAN_MAC_IDX, pi->iscsic.mac_addr);
t3_mac_set_rx_mode(mac, &rm);
t3_link_start(&pi->phy, mac, &pi->link_config);
t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
@@ -903,6 +906,7 @@ static inline int offload_tx(struct t3cdev *tdev, struct sk_buff *skb)
static int write_smt_entry(struct adapter *adapter, int idx)
{
struct cpl_smt_write_req *req;
+ struct port_info *pi = netdev_priv(adapter->port[idx]);
struct sk_buff *skb = alloc_skb(sizeof(*req), GFP_KERNEL);
if (!skb)
@@ -913,8 +917,8 @@ static int write_smt_entry(struct adapter *adapter, int idx)
OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, idx));
req->mtu_idx = NMTUS - 1; /* should be 0 but there's a T3 bug */
req->iff = idx;
- memset(req->src_mac1, 0, sizeof(req->src_mac1));
memcpy(req->src_mac0, adapter->port[idx]->dev_addr, ETH_ALEN);
+ memcpy(req->src_mac1, pi->iscsic.mac_addr, ETH_ALEN);
skb->priority = 1;
offload_tx(&adapter->tdev, skb);
return 0;
@@ -989,11 +993,21 @@ static int bind_qsets(struct adapter *adap)
return err;
}
-#define FW_FNAME "cxgb3/t3fw-%d.%d.%d.bin"
-#define TPSRAM_NAME "cxgb3/t3%c_psram-%d.%d.%d.bin"
+#define FW_VERSION __stringify(FW_VERSION_MAJOR) "." \
+ __stringify(FW_VERSION_MINOR) "." __stringify(FW_VERSION_MICRO)
+#define FW_FNAME "cxgb3/t3fw-" FW_VERSION ".bin"
+#define TPSRAM_VERSION __stringify(TP_VERSION_MAJOR) "." \
+ __stringify(TP_VERSION_MINOR) "." __stringify(TP_VERSION_MICRO)
+#define TPSRAM_NAME "cxgb3/t3%c_psram-" TPSRAM_VERSION ".bin"
#define AEL2005_OPT_EDC_NAME "cxgb3/ael2005_opt_edc.bin"
#define AEL2005_TWX_EDC_NAME "cxgb3/ael2005_twx_edc.bin"
#define AEL2020_TWX_EDC_NAME "cxgb3/ael2020_twx_edc.bin"
+MODULE_FIRMWARE(FW_FNAME);
+MODULE_FIRMWARE("cxgb3/t3b_psram-" TPSRAM_VERSION ".bin");
+MODULE_FIRMWARE("cxgb3/t3c_psram-" TPSRAM_VERSION ".bin");
+MODULE_FIRMWARE(AEL2005_OPT_EDC_NAME);
+MODULE_FIRMWARE(AEL2005_TWX_EDC_NAME);
+MODULE_FIRMWARE(AEL2020_TWX_EDC_NAME);
static inline const char *get_edc_fw_name(int edc_idx)
{
@@ -1064,16 +1078,13 @@ int t3_get_edc_fw(struct cphy *phy, int edc_idx, int size)
static int upgrade_fw(struct adapter *adap)
{
int ret;
- char buf[64];
const struct firmware *fw;
struct device *dev = &adap->pdev->dev;
- snprintf(buf, sizeof(buf), FW_FNAME, FW_VERSION_MAJOR,
- FW_VERSION_MINOR, FW_VERSION_MICRO);
- ret = request_firmware(&fw, buf, dev);
+ ret = request_firmware(&fw, FW_FNAME, dev);
if (ret < 0) {
dev_err(dev, "could not upgrade firmware: unable to load %s\n",
- buf);
+ FW_FNAME);
return ret;
}
ret = t3_load_fw(adap, fw->data, fw->size);
@@ -1117,8 +1128,7 @@ static int update_tpsram(struct adapter *adap)
if (!rev)
return 0;
- snprintf(buf, sizeof(buf), TPSRAM_NAME, rev,
- TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO);
+ snprintf(buf, sizeof(buf), TPSRAM_NAME, rev);
ret = request_firmware(&tpsram, buf, dev);
if (ret < 0) {
@@ -2107,19 +2117,19 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr)
if (t.qset_idx >= SGE_QSETS)
return -EINVAL;
if (!in_range(t.intr_lat, 0, M_NEWTIMER) ||
- !in_range(t.cong_thres, 0, 255) ||
- !in_range(t.txq_size[0], MIN_TXQ_ENTRIES,
- MAX_TXQ_ENTRIES) ||
- !in_range(t.txq_size[1], MIN_TXQ_ENTRIES,
- MAX_TXQ_ENTRIES) ||
- !in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES,
- MAX_CTRL_TXQ_ENTRIES) ||
- !in_range(t.fl_size[0], MIN_FL_ENTRIES,
- MAX_RX_BUFFERS)
- || !in_range(t.fl_size[1], MIN_FL_ENTRIES,
- MAX_RX_JUMBO_BUFFERS)
- || !in_range(t.rspq_size, MIN_RSPQ_ENTRIES,
- MAX_RSPQ_ENTRIES))
+ !in_range(t.cong_thres, 0, 255) ||
+ !in_range(t.txq_size[0], MIN_TXQ_ENTRIES,
+ MAX_TXQ_ENTRIES) ||
+ !in_range(t.txq_size[1], MIN_TXQ_ENTRIES,
+ MAX_TXQ_ENTRIES) ||
+ !in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES,
+ MAX_CTRL_TXQ_ENTRIES) ||
+ !in_range(t.fl_size[0], MIN_FL_ENTRIES,
+ MAX_RX_BUFFERS) ||
+ !in_range(t.fl_size[1], MIN_FL_ENTRIES,
+ MAX_RX_JUMBO_BUFFERS) ||
+ !in_range(t.rspq_size, MIN_RSPQ_ENTRIES,
+ MAX_RSPQ_ENTRIES))
return -EINVAL;
if ((adapter->flags & FULL_INIT_DONE) && t.lro > 0)
@@ -2516,7 +2526,7 @@ static int cxgb_set_mac_addr(struct net_device *dev, void *p)
return -EINVAL;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- t3_mac_set_address(&pi->mac, 0, dev->dev_addr);
+ t3_mac_set_address(&pi->mac, LAN_MAC_IDX, dev->dev_addr);
if (offload_running(adapter))
write_smt_entry(adapter, pi->port_id);
return 0;
@@ -2654,7 +2664,7 @@ static void check_t3b2_mac(struct adapter *adapter)
struct cmac *mac = &p->mac;
t3_mac_set_mtu(mac, dev->mtu);
- t3_mac_set_address(mac, 0, dev->dev_addr);
+ t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr);
cxgb_set_rxmode(dev);
t3_link_start(&p->phy, mac, &p->link_config);
t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
@@ -3112,6 +3122,14 @@ static const struct net_device_ops cxgb_netdev_ops = {
#endif
};
+static void __devinit cxgb3_init_iscsi_mac(struct net_device *dev)
+{
+ struct port_info *pi = netdev_priv(dev);
+
+ memcpy(pi->iscsic.mac_addr, dev->dev_addr, ETH_ALEN);
+ pi->iscsic.mac_addr[3] |= 0x80;
+}
+
static int __devinit init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -3270,6 +3288,9 @@ static int __devinit init_one(struct pci_dev *pdev,
goto out_free_dev;
}
+ for_each_port(adapter, i)
+ cxgb3_init_iscsi_mac(adapter->port[i]);
+
/* Driver's ready. Reflect it on LEDs */
t3_led_ready(adapter);
diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c
index 6366061712f..bdbd14727e4 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -1260,7 +1260,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
if (should_restart_tx(q) &&
test_and_clear_bit(TXQ_ETH, &qs->txq_stopped)) {
q->restarts++;
- netif_tx_wake_queue(txq);
+ netif_tx_start_queue(txq);
}
}
@@ -1285,7 +1285,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
/*
* We do not use Tx completion interrupts to free DMAd Tx packets.
- * This is good for performamce but means that we rely on new Tx
+ * This is good for performance but means that we rely on new Tx
* packets arriving to run the destructors of completed packets,
* which open up space in their sockets' send queues. Sometimes
* we do not get such new packets causing Tx to stall. A single
@@ -1946,10 +1946,9 @@ static void restart_tx(struct sge_qset *qs)
* Check if the ARP request is probing the private IP address
* dedicated to iSCSI, generate an ARP reply if so.
*/
-static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb)
+static void cxgb3_arp_process(struct port_info *pi, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- struct port_info *pi;
struct arphdr *arp;
unsigned char *arp_ptr;
unsigned char *sha;
@@ -1972,12 +1971,11 @@ static void cxgb3_arp_process(struct adapter *adapter, struct sk_buff *skb)
arp_ptr += dev->addr_len;
memcpy(&tip, arp_ptr, sizeof(tip));
- pi = netdev_priv(dev);
if (tip != pi->iscsi_ipv4addr)
return;
arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha,
- dev->dev_addr, sha);
+ pi->iscsic.mac_addr, sha);
}
@@ -1986,6 +1984,19 @@ static inline int is_arp(struct sk_buff *skb)
return skb->protocol == htons(ETH_P_ARP);
}
+static void cxgb3_process_iscsi_prov_pack(struct port_info *pi,
+ struct sk_buff *skb)
+{
+ if (is_arp(skb)) {
+ cxgb3_arp_process(pi, skb);
+ return;
+ }
+
+ if (pi->iscsic.recv)
+ pi->iscsic.recv(pi, skb);
+
+}
+
/**
* rx_eth - process an ingress ethernet packet
* @adap: the adapter
@@ -2024,13 +2035,12 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
vlan_gro_receive(&qs->napi, grp,
ntohs(p->vlan), skb);
else {
- if (unlikely(pi->iscsi_ipv4addr &&
- is_arp(skb))) {
+ if (unlikely(pi->iscsic.flags)) {
unsigned short vtag = ntohs(p->vlan) &
VLAN_VID_MASK;
skb->dev = vlan_group_get_device(grp,
vtag);
- cxgb3_arp_process(adap, skb);
+ cxgb3_process_iscsi_prov_pack(pi, skb);
}
__vlan_hwaccel_rx(skb, grp, ntohs(p->vlan),
rq->polling);
@@ -2041,8 +2051,8 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq,
if (lro)
napi_gro_receive(&qs->napi, skb);
else {
- if (unlikely(pi->iscsi_ipv4addr && is_arp(skb)))
- cxgb3_arp_process(adap, skb);
+ if (unlikely(pi->iscsic.flags))
+ cxgb3_process_iscsi_prov_pack(pi, skb);
netif_receive_skb(skb);
}
} else
@@ -2125,6 +2135,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs,
if (!complete)
return;
+ skb_record_rx_queue(skb, qs - &adap->sge.qs[0]);
skb->ip_summed = CHECKSUM_UNNECESSARY;
cpl = qs->lro_va;
diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c
index e3478314c00..8edac8915ea 100644
--- a/drivers/net/davinci_emac.c
+++ b/drivers/net/davinci_emac.c
@@ -2803,11 +2803,33 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev)
return 0;
}
+static
+int davinci_emac_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ if (netif_running(dev))
+ emac_dev_stop(dev);
+
+ clk_disable(emac_clk);
+
+ return 0;
+}
+
+static int davinci_emac_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+
+ clk_enable(emac_clk);
+
+ if (netif_running(dev))
+ emac_dev_open(dev);
+
+ return 0;
+}
+
/**
* davinci_emac_driver: EMAC platform driver structure
- *
- * We implement only probe and remove functions - suspend/resume and
- * others not supported by this module
*/
static struct platform_driver davinci_emac_driver = {
.driver = {
@@ -2816,6 +2838,8 @@ static struct platform_driver davinci_emac_driver = {
},
.probe = davinci_emac_probe,
.remove = __devexit_p(davinci_emac_remove),
+ .suspend = davinci_emac_suspend,
+ .resume = davinci_emac_resume,
};
/**
diff --git a/drivers/net/declance.c b/drivers/net/declance.c
index a31696a3928..be9590253aa 100644
--- a/drivers/net/declance.c
+++ b/drivers/net/declance.c
@@ -801,14 +801,14 @@ static int lance_open(struct net_device *dev)
netif_start_queue(dev);
/* Associate IRQ with lance_interrupt */
- if (request_irq(dev->irq, &lance_interrupt, 0, "lance", dev)) {
+ if (request_irq(dev->irq, lance_interrupt, 0, "lance", dev)) {
printk("%s: Can't get IRQ %d\n", dev->name, dev->irq);
return -EAGAIN;
}
if (lp->dma_irq >= 0) {
unsigned long flags;
- if (request_irq(lp->dma_irq, &lance_dma_merr_int, 0,
+ if (request_irq(lp->dma_irq, lance_dma_merr_int, 0,
"lance error", dev)) {
free_irq(dev->irq, dev);
printk("%s: Can't get DMA IRQ %d\n", dev->name,
diff --git a/drivers/net/depca.c b/drivers/net/depca.c
index 7a3bdac84ab..0c1f491d20b 100644
--- a/drivers/net/depca.c
+++ b/drivers/net/depca.c
@@ -849,7 +849,7 @@ static int depca_open(struct net_device *dev)
depca_dbg_open(dev);
- if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, dev)) {
+ if (request_irq(dev->irq, depca_interrupt, 0, lp->adapter_name, dev)) {
printk("depca_open(): Requested IRQ%d is busy\n", dev->irq);
status = -EAGAIN;
} else {
diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c
index 7fa7a907f13..2a8b6a7c0b8 100644
--- a/drivers/net/dl2k.c
+++ b/drivers/net/dl2k.c
@@ -163,8 +163,8 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
strcmp (media[card_idx], "4") == 0) {
np->speed = 100;
np->full_duplex = 1;
- } else if (strcmp (media[card_idx], "100mbps_hd") == 0
- || strcmp (media[card_idx], "3") == 0) {
+ } else if (strcmp (media[card_idx], "100mbps_hd") == 0 ||
+ strcmp (media[card_idx], "3") == 0) {
np->speed = 100;
np->full_duplex = 0;
} else if (strcmp (media[card_idx], "10mbps_fd") == 0 ||
@@ -411,7 +411,7 @@ rio_open (struct net_device *dev)
int i;
u16 macctrl;
- i = request_irq (dev->irq, &rio_interrupt, IRQF_SHARED, dev->name, dev);
+ i = request_irq (dev->irq, rio_interrupt, IRQF_SHARED, dev->name, dev);
if (i)
return i;
@@ -505,7 +505,8 @@ rio_timer (unsigned long data)
entry = np->old_rx % RX_RING_SIZE;
/* Dropped packets don't need to re-allocate */
if (np->rx_skbuff[entry] == NULL) {
- skb = netdev_alloc_skb (dev, np->rx_buf_sz);
+ skb = netdev_alloc_skb_ip_align(dev,
+ np->rx_buf_sz);
if (skb == NULL) {
np->rx_ring[entry].fraginfo = 0;
printk (KERN_INFO
@@ -514,8 +515,6 @@ rio_timer (unsigned long data)
break;
}
np->rx_skbuff[entry] = skb;
- /* 16 byte align the IP header */
- skb_reserve (skb, 2);
np->rx_ring[entry].fraginfo =
cpu_to_le64 (pci_map_single
(np->pdev, skb->data, np->rx_buf_sz,
@@ -576,7 +575,9 @@ alloc_list (struct net_device *dev)
/* Allocate the rx buffers */
for (i = 0; i < RX_RING_SIZE; i++) {
/* Allocated fixed size of skbuff */
- struct sk_buff *skb = netdev_alloc_skb (dev, np->rx_buf_sz);
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz);
np->rx_skbuff[i] = skb;
if (skb == NULL) {
printk (KERN_ERR
@@ -584,7 +585,6 @@ alloc_list (struct net_device *dev)
dev->name);
break;
}
- skb_reserve (skb, 2); /* 16 byte align the IP header. */
/* Rubicon now supports 40 bits of addressing space. */
np->rx_ring[i].fraginfo =
cpu_to_le64 ( pci_map_single (
@@ -871,13 +871,11 @@ receive_packet (struct net_device *dev)
PCI_DMA_FROMDEVICE);
skb_put (skb = np->rx_skbuff[entry], pkt_len);
np->rx_skbuff[entry] = NULL;
- } else if ((skb = netdev_alloc_skb(dev, pkt_len + 2))) {
+ } else if ((skb = netdev_alloc_skb_ip_align(dev, pkt_len))) {
pci_dma_sync_single_for_cpu(np->pdev,
desc_to_dma(desc),
np->rx_buf_sz,
PCI_DMA_FROMDEVICE);
- /* 16 byte align the IP header */
- skb_reserve (skb, 2);
skb_copy_to_linear_data (skb,
np->rx_skbuff[entry]->data,
pkt_len);
@@ -907,7 +905,7 @@ receive_packet (struct net_device *dev)
struct sk_buff *skb;
/* Dropped packets don't need to re-allocate */
if (np->rx_skbuff[entry] == NULL) {
- skb = netdev_alloc_skb(dev, np->rx_buf_sz);
+ skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz);
if (skb == NULL) {
np->rx_ring[entry].fraginfo = 0;
printk (KERN_INFO
@@ -917,8 +915,6 @@ receive_packet (struct net_device *dev)
break;
}
np->rx_skbuff[entry] = skb;
- /* 16 byte align the IP header */
- skb_reserve (skb, 2);
np->rx_ring[entry].fraginfo =
cpu_to_le64 (pci_map_single
(np->pdev, skb->data, np->rx_buf_sz,
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 31b8bef49d2..0cbe3c0e7c0 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -100,6 +100,7 @@ typedef struct board_info {
unsigned int flags;
unsigned int in_suspend :1;
+ unsigned int wake_supported :1;
int debug_level;
enum dm9000_type type;
@@ -116,6 +117,8 @@ typedef struct board_info {
struct resource *data_req;
struct resource *irq_res;
+ int irq_wake;
+
struct mutex addr_lock; /* phy and eeprom access lock */
struct delayed_work phy_poll;
@@ -125,6 +128,7 @@ typedef struct board_info {
struct mii_if_info mii;
u32 msg_enable;
+ u32 wake_state;
int rx_csum;
int can_csum;
@@ -568,6 +572,54 @@ static int dm9000_set_eeprom(struct net_device *dev,
return 0;
}
+static void dm9000_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
+{
+ board_info_t *dm = to_dm9000_board(dev);
+
+ memset(w, 0, sizeof(struct ethtool_wolinfo));
+
+ /* note, we could probably support wake-phy too */
+ w->supported = dm->wake_supported ? WAKE_MAGIC : 0;
+ w->wolopts = dm->wake_state;
+}
+
+static int dm9000_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
+{
+ board_info_t *dm = to_dm9000_board(dev);
+ unsigned long flags;
+ u32 opts = w->wolopts;
+ u32 wcr = 0;
+
+ if (!dm->wake_supported)
+ return -EOPNOTSUPP;
+
+ if (opts & ~WAKE_MAGIC)
+ return -EINVAL;
+
+ if (opts & WAKE_MAGIC)
+ wcr |= WCR_MAGICEN;
+
+ mutex_lock(&dm->addr_lock);
+
+ spin_lock_irqsave(&dm->lock, flags);
+ iow(dm, DM9000_WCR, wcr);
+ spin_unlock_irqrestore(&dm->lock, flags);
+
+ mutex_unlock(&dm->addr_lock);
+
+ if (dm->wake_state != opts) {
+ /* change in wol state, update IRQ state */
+
+ if (!dm->wake_state)
+ set_irq_wake(dm->irq_wake, 1);
+ else if (dm->wake_state & !opts)
+ set_irq_wake(dm->irq_wake, 0);
+ }
+
+ dm->wake_state = opts;
+ return 0;
+}
+
static const struct ethtool_ops dm9000_ethtool_ops = {
.get_drvinfo = dm9000_get_drvinfo,
.get_settings = dm9000_get_settings,
@@ -576,6 +628,8 @@ static const struct ethtool_ops dm9000_ethtool_ops = {
.set_msglevel = dm9000_set_msglevel,
.nway_reset = dm9000_nway_reset,
.get_link = dm9000_get_link,
+ .get_wol = dm9000_get_wol,
+ .set_wol = dm9000_set_wol,
.get_eeprom_len = dm9000_get_eeprom_len,
.get_eeprom = dm9000_get_eeprom,
.set_eeprom = dm9000_set_eeprom,
@@ -722,6 +776,7 @@ dm9000_init_dm9000(struct net_device *dev)
{
board_info_t *db = netdev_priv(dev);
unsigned int imr;
+ unsigned int ncr;
dm9000_dbg(db, 1, "entering %s\n", __func__);
@@ -736,8 +791,15 @@ dm9000_init_dm9000(struct net_device *dev)
iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */
iow(db, DM9000_GPR, 0); /* Enable PHY */
- if (db->flags & DM9000_PLATF_EXT_PHY)
- iow(db, DM9000_NCR, NCR_EXT_PHY);
+ ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
+
+ /* if wol is needed, then always set NCR_WAKEEN otherwise we end
+ * up dumping the wake events if we disable this. There is already
+ * a wake-mask in DM9000_WCR */
+ if (db->wake_supported)
+ ncr |= NCR_WAKEEN;
+
+ iow(db, DM9000_NCR, ncr);
/* Program operating register */
iow(db, DM9000_TCR, 0); /* TX Polling clear */
@@ -962,8 +1024,8 @@ dm9000_rx(struct net_device *dev)
}
/* Move data from DM9000 */
- if (GoodPacket
- && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
+ if (GoodPacket &&
+ ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
skb_reserve(skb, 2);
rdptr = (u8 *) skb_put(skb, RxLen - 4);
@@ -1045,6 +1107,41 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ board_info_t *db = netdev_priv(dev);
+ unsigned long flags;
+ unsigned nsr, wcr;
+
+ spin_lock_irqsave(&db->lock, flags);
+
+ nsr = ior(db, DM9000_NSR);
+ wcr = ior(db, DM9000_WCR);
+
+ dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr);
+
+ if (nsr & NSR_WAKEST) {
+ /* clear, so we can avoid */
+ iow(db, DM9000_NSR, NSR_WAKEST);
+
+ if (wcr & WCR_LINKST)
+ dev_info(db->dev, "wake by link status change\n");
+ if (wcr & WCR_SAMPLEST)
+ dev_info(db->dev, "wake by sample packet\n");
+ if (wcr & WCR_MAGICST )
+ dev_info(db->dev, "wake by magic packet\n");
+ if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST)))
+ dev_err(db->dev, "wake signalled with no reason? "
+ "NSR=0x%02x, WSR=0x%02x\n", nsr, wcr);
+
+ }
+
+ spin_unlock_irqrestore(&db->lock, flags);
+
+ return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE;
+}
+
#ifdef CONFIG_NET_POLL_CONTROLLER
/*
*Used by netconsole
@@ -1078,7 +1175,7 @@ dm9000_open(struct net_device *dev)
irqflags |= IRQF_SHARED;
- if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))
+ if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
return -EAGAIN;
/* Initialize DM9000 board */
@@ -1299,6 +1396,29 @@ dm9000_probe(struct platform_device *pdev)
goto out;
}
+ db->irq_wake = platform_get_irq(pdev, 1);
+ if (db->irq_wake >= 0) {
+ dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);
+
+ ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
+ IRQF_SHARED, dev_name(db->dev), ndev);
+ if (ret) {
+ dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);
+ } else {
+
+ /* test to see if irq is really wakeup capable */
+ ret = set_irq_wake(db->irq_wake, 1);
+ if (ret) {
+ dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",
+ db->irq_wake, ret);
+ ret = 0;
+ } else {
+ set_irq_wake(db->irq_wake, 0);
+ db->wake_supported = 1;
+ }
+ }
+ }
+
iosize = resource_size(db->addr_res);
db->addr_req = request_mem_region(db->addr_res->start, iosize,
pdev->name);
@@ -1490,10 +1610,14 @@ dm9000_drv_suspend(struct device *dev)
db = netdev_priv(ndev);
db->in_suspend = 1;
- if (netif_running(ndev)) {
- netif_device_detach(ndev);
+ if (!netif_running(ndev))
+ return 0;
+
+ netif_device_detach(ndev);
+
+ /* only shutdown if not using WoL */
+ if (!db->wake_state)
dm9000_shutdown(ndev);
- }
}
return 0;
}
@@ -1506,10 +1630,13 @@ dm9000_drv_resume(struct device *dev)
board_info_t *db = netdev_priv(ndev);
if (ndev) {
-
if (netif_running(ndev)) {
- dm9000_reset(db);
- dm9000_init_dm9000(ndev);
+ /* reset if we were not in wake mode to ensure if
+ * the device was powered off it is in a known state */
+ if (!db->wake_state) {
+ dm9000_reset(db);
+ dm9000_init_dm9000(ndev);
+ }
netif_device_attach(ndev);
}
diff --git a/drivers/net/dm9000.h b/drivers/net/dm9000.h
index fb1c924d79b..55688bd1a3e 100644
--- a/drivers/net/dm9000.h
+++ b/drivers/net/dm9000.h
@@ -111,6 +111,13 @@
#define RSR_CE (1<<1)
#define RSR_FOE (1<<0)
+#define WCR_LINKEN (1 << 5)
+#define WCR_SAMPLEEN (1 << 4)
+#define WCR_MAGICEN (1 << 3)
+#define WCR_LINKST (1 << 2)
+#define WCR_SAMPLEST (1 << 1)
+#define WCR_MAGICST (1 << 0)
+
#define FCTR_HWOT(ot) (( ot & 0xf ) << 4 )
#define FCTR_LWOT(ot) ( ot & 0xf )
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index d269a68ce35..929701ca07d 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -624,6 +624,7 @@ struct nic {
u16 eeprom_wc;
__le16 eeprom[256];
spinlock_t mdio_lock;
+ const struct firmware *fw;
};
static inline void e100_write_flush(struct nic *nic)
@@ -1225,9 +1226,9 @@ static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb)
static const struct firmware *e100_request_firmware(struct nic *nic)
{
const char *fw_name;
- const struct firmware *fw;
+ const struct firmware *fw = nic->fw;
u8 timer, bundle, min_size;
- int err;
+ int err = 0;
/* do not load u-code for ICH devices */
if (nic->flags & ich)
@@ -1243,12 +1244,20 @@ static const struct firmware *e100_request_firmware(struct nic *nic)
else /* No ucode on other devices */
return NULL;
- err = request_firmware(&fw, fw_name, &nic->pdev->dev);
+ /* If the firmware has not previously been loaded, request a pointer
+ * to it. If it was previously loaded, we are reinitializing the
+ * adapter, possibly in a resume from hibernate, in which case
+ * request_firmware() cannot be used.
+ */
+ if (!fw)
+ err = request_firmware(&fw, fw_name, &nic->pdev->dev);
+
if (err) {
DPRINTK(PROBE, ERR, "Failed to load firmware \"%s\": %d\n",
fw_name, err);
return ERR_PTR(err);
}
+
/* Firmware should be precisely UCODE_SIZE (words) plus three bytes
indicating the offsets for BUNDLESMALL, BUNDLEMAX, INTDELAY */
if (fw->size != UCODE_SIZE * 4 + 3) {
@@ -1271,7 +1280,10 @@ static const struct firmware *e100_request_firmware(struct nic *nic)
release_firmware(fw);
return ERR_PTR(-EINVAL);
}
- /* OK, firmware is validated and ready to use... */
+
+ /* OK, firmware is validated and ready to use. Save a pointer
+ * to it in the nic */
+ nic->fw = fw;
return fw;
}
@@ -1852,11 +1864,10 @@ static inline void e100_start_receiver(struct nic *nic, struct rx *rx)
#define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN)
static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
{
- if (!(rx->skb = netdev_alloc_skb(nic->netdev, RFD_BUF_LEN + NET_IP_ALIGN)))
+ if (!(rx->skb = netdev_alloc_skb_ip_align(nic->netdev, RFD_BUF_LEN)))
return -ENOMEM;
- /* Align, init, and map the RFD. */
- skb_reserve(rx->skb, NET_IP_ALIGN);
+ /* Init, and map the RFD. */
skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd));
rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,
RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
index 42e2b7e21c2..2a567df3ea7 100644
--- a/drivers/net/e1000/e1000.h
+++ b/drivers/net/e1000/e1000.h
@@ -167,6 +167,7 @@ struct e1000_buffer {
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
+ u16 mapped_as_page;
};
struct e1000_tx_ring {
@@ -302,7 +303,6 @@ struct e1000_adapter {
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
- struct net_device_stats net_stats;
/* structs defined in e1000_hw.h */
struct e1000_hw hw;
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c
index 490b2b7cd3a..13e9ece1688 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/e1000/e1000_ethtool.c
@@ -31,14 +31,22 @@
#include "e1000.h"
#include <asm/uaccess.h>
+enum {NETDEV_STATS, E1000_STATS};
+
struct e1000_stats {
char stat_string[ETH_GSTRING_LEN];
+ int type;
int sizeof_stat;
int stat_offset;
};
-#define E1000_STAT(m) FIELD_SIZEOF(struct e1000_adapter, m), \
- offsetof(struct e1000_adapter, m)
+#define E1000_STAT(m) E1000_STATS, \
+ sizeof(((struct e1000_adapter *)0)->m), \
+ offsetof(struct e1000_adapter, m)
+#define E1000_NETDEV_STAT(m) NETDEV_STATS, \
+ sizeof(((struct net_device *)0)->m), \
+ offsetof(struct net_device, m)
+
static const struct e1000_stats e1000_gstrings_stats[] = {
{ "rx_packets", E1000_STAT(stats.gprc) },
{ "tx_packets", E1000_STAT(stats.gptc) },
@@ -50,19 +58,19 @@ static const struct e1000_stats e1000_gstrings_stats[] = {
{ "tx_multicast", E1000_STAT(stats.mptc) },
{ "rx_errors", E1000_STAT(stats.rxerrc) },
{ "tx_errors", E1000_STAT(stats.txerrc) },
- { "tx_dropped", E1000_STAT(net_stats.tx_dropped) },
+ { "tx_dropped", E1000_NETDEV_STAT(stats.tx_dropped) },
{ "multicast", E1000_STAT(stats.mprc) },
{ "collisions", E1000_STAT(stats.colc) },
{ "rx_length_errors", E1000_STAT(stats.rlerrc) },
- { "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) },
+ { "rx_over_errors", E1000_NETDEV_STAT(stats.rx_over_errors) },
{ "rx_crc_errors", E1000_STAT(stats.crcerrs) },
- { "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) },
+ { "rx_frame_errors", E1000_NETDEV_STAT(stats.rx_frame_errors) },
{ "rx_no_buffer_count", E1000_STAT(stats.rnbc) },
{ "rx_missed_errors", E1000_STAT(stats.mpc) },
{ "tx_aborted_errors", E1000_STAT(stats.ecol) },
{ "tx_carrier_errors", E1000_STAT(stats.tncrs) },
- { "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) },
- { "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) },
+ { "tx_fifo_errors", E1000_NETDEV_STAT(stats.tx_fifo_errors) },
+ { "tx_heartbeat_errors", E1000_NETDEV_STAT(stats.tx_heartbeat_errors) },
{ "tx_window_errors", E1000_STAT(stats.latecol) },
{ "tx_abort_late_coll", E1000_STAT(stats.latecol) },
{ "tx_deferred_ok", E1000_STAT(stats.dc) },
@@ -861,10 +869,10 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
/* NOTE: we don't test MSI interrupts here, yet */
/* Hook up test interrupt handler just for this test */
- if (!request_irq(irq, &e1000_test_intr, IRQF_PROBE_SHARED, netdev->name,
+ if (!request_irq(irq, e1000_test_intr, IRQF_PROBE_SHARED, netdev->name,
netdev))
shared_int = false;
- else if (request_irq(irq, &e1000_test_intr, IRQF_SHARED,
+ else if (request_irq(irq, e1000_test_intr, IRQF_SHARED,
netdev->name, netdev)) {
*data = 1;
return -1;
@@ -1830,10 +1838,21 @@ static void e1000_get_ethtool_stats(struct net_device *netdev,
{
struct e1000_adapter *adapter = netdev_priv(netdev);
int i;
+ char *p = NULL;
e1000_update_stats(adapter);
for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
- char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset;
+ switch (e1000_gstrings_stats[i].type) {
+ case NETDEV_STATS:
+ p = (char *) netdev +
+ e1000_gstrings_stats[i].stat_offset;
+ break;
+ case E1000_STATS:
+ p = (char *) adapter +
+ e1000_gstrings_stats[i].stat_offset;
+ break;
+ }
+
data[i] = (e1000_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index bcd192ca47b..7e855f9bbd9 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -1839,10 +1839,17 @@ void e1000_free_all_tx_resources(struct e1000_adapter *adapter)
static void e1000_unmap_and_free_tx_resource(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info)
{
- buffer_info->dma = 0;
+ if (buffer_info->dma) {
+ if (buffer_info->mapped_as_page)
+ pci_unmap_page(adapter->pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ else
+ pci_unmap_single(adapter->pdev, buffer_info->dma,
+ buffer_info->length,
+ PCI_DMA_TODEVICE);
+ buffer_info->dma = 0;
+ }
if (buffer_info->skb) {
- skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb,
- DMA_TO_DEVICE);
dev_kfree_skb_any(buffer_info->skb);
buffer_info->skb = NULL;
}
@@ -2683,22 +2690,14 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
unsigned int mss)
{
struct e1000_hw *hw = &adapter->hw;
+ struct pci_dev *pdev = adapter->pdev;
struct e1000_buffer *buffer_info;
unsigned int len = skb_headlen(skb);
- unsigned int offset, size, count = 0, i;
+ unsigned int offset = 0, size, count = 0, i;
unsigned int f;
- dma_addr_t *map;
i = tx_ring->next_to_use;
- if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) {
- dev_err(&adapter->pdev->dev, "TX DMA map failed\n");
- return 0;
- }
-
- map = skb_shinfo(skb)->dma_maps;
- offset = 0;
-
while (len) {
buffer_info = &tx_ring->buffer_info[i];
size = min(len, max_per_txd);
@@ -2735,7 +2734,11 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
buffer_info->length = size;
/* set time_stamp *before* dma to help avoid a possible race */
buffer_info->time_stamp = jiffies;
- buffer_info->dma = skb_shinfo(skb)->dma_head + offset;
+ buffer_info->mapped_as_page = false;
+ buffer_info->dma = pci_map_single(pdev, skb->data + offset,
+ size, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
buffer_info->next_to_watch = i;
len -= size;
@@ -2753,7 +2756,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
frag = &skb_shinfo(skb)->frags[f];
len = frag->size;
- offset = 0;
+ offset = frag->page_offset;
while (len) {
i++;
@@ -2777,7 +2780,12 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
buffer_info->length = size;
buffer_info->time_stamp = jiffies;
- buffer_info->dma = map[f] + offset;
+ buffer_info->mapped_as_page = true;
+ buffer_info->dma = pci_map_page(pdev, frag->page,
+ offset, size,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
buffer_info->next_to_watch = i;
len -= size;
@@ -2790,6 +2798,22 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
tx_ring->buffer_info[first].next_to_watch = i;
return count;
+
+dma_error:
+ dev_err(&pdev->dev, "TX DMA map failed\n");
+ buffer_info->dma = 0;
+ count--;
+
+ while (count >= 0) {
+ count--;
+ i--;
+ if (i < 0)
+ i += tx_ring->count;
+ buffer_info = &tx_ring->buffer_info[i];
+ e1000_unmap_and_free_tx_resource(adapter, buffer_info);
+ }
+
+ return 0;
}
static void e1000_tx_queue(struct e1000_adapter *adapter,
@@ -3101,10 +3125,8 @@ static void e1000_reset_task(struct work_struct *work)
static struct net_device_stats *e1000_get_stats(struct net_device *netdev)
{
- struct e1000_adapter *adapter = netdev_priv(netdev);
-
/* only return the current stats */
- return &adapter->net_stats;
+ return &netdev->stats;
}
/**
@@ -3196,6 +3218,7 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu)
void e1000_update_stats(struct e1000_adapter *adapter)
{
+ struct net_device *netdev = adapter->netdev;
struct e1000_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
unsigned long flags;
@@ -3288,32 +3311,32 @@ void e1000_update_stats(struct e1000_adapter *adapter)
}
/* Fill out the OS statistics structure */
- adapter->net_stats.multicast = adapter->stats.mprc;
- adapter->net_stats.collisions = adapter->stats.colc;
+ netdev->stats.multicast = adapter->stats.mprc;
+ netdev->stats.collisions = adapter->stats.colc;
/* Rx Errors */
/* RLEC on some newer hardware can be incorrect so build
* our own version based on RUC and ROC */
- adapter->net_stats.rx_errors = adapter->stats.rxerrc +
+ netdev->stats.rx_errors = adapter->stats.rxerrc +
adapter->stats.crcerrs + adapter->stats.algnerrc +
adapter->stats.ruc + adapter->stats.roc +
adapter->stats.cexterr;
adapter->stats.rlerrc = adapter->stats.ruc + adapter->stats.roc;
- adapter->net_stats.rx_length_errors = adapter->stats.rlerrc;
- adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
- adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc;
- adapter->net_stats.rx_missed_errors = adapter->stats.mpc;
+ netdev->stats.rx_length_errors = adapter->stats.rlerrc;
+ netdev->stats.rx_crc_errors = adapter->stats.crcerrs;
+ netdev->stats.rx_frame_errors = adapter->stats.algnerrc;
+ netdev->stats.rx_missed_errors = adapter->stats.mpc;
/* Tx Errors */
adapter->stats.txerrc = adapter->stats.ecol + adapter->stats.latecol;
- adapter->net_stats.tx_errors = adapter->stats.txerrc;
- adapter->net_stats.tx_aborted_errors = adapter->stats.ecol;
- adapter->net_stats.tx_window_errors = adapter->stats.latecol;
- adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs;
+ netdev->stats.tx_errors = adapter->stats.txerrc;
+ netdev->stats.tx_aborted_errors = adapter->stats.ecol;
+ netdev->stats.tx_window_errors = adapter->stats.latecol;
+ netdev->stats.tx_carrier_errors = adapter->stats.tncrs;
if (hw->bad_tx_carr_stats_fd &&
adapter->link_duplex == FULL_DUPLEX) {
- adapter->net_stats.tx_carrier_errors = 0;
+ netdev->stats.tx_carrier_errors = 0;
adapter->stats.tncrs = 0;
}
@@ -3484,8 +3507,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
adapter->detect_tx_hung = false;
if (tx_ring->buffer_info[eop].time_stamp &&
time_after(jiffies, tx_ring->buffer_info[eop].time_stamp +
- (adapter->tx_timeout_factor * HZ))
- && !(er32(STATUS) & E1000_STATUS_TXOFF)) {
+ (adapter->tx_timeout_factor * HZ)) &&
+ !(er32(STATUS) & E1000_STATUS_TXOFF)) {
/* detected Tx unit hang */
DPRINTK(DRV, ERR, "Detected Tx Unit Hang\n"
@@ -3514,8 +3537,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
}
adapter->total_tx_bytes += total_tx_bytes;
adapter->total_tx_packets += total_tx_packets;
- adapter->net_stats.tx_bytes += total_tx_bytes;
- adapter->net_stats.tx_packets += total_tx_packets;
+ netdev->stats.tx_bytes += total_tx_bytes;
+ netdev->stats.tx_packets += total_tx_packets;
return (count < tx_ring->count);
}
@@ -3767,8 +3790,8 @@ next_desc:
adapter->total_rx_packets += total_rx_packets;
adapter->total_rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_packets += total_rx_packets;
+ netdev->stats.rx_bytes += total_rx_bytes;
+ netdev->stats.rx_packets += total_rx_packets;
return cleaned;
}
@@ -3867,9 +3890,8 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
* of reassembly being done in the stack */
if (length < copybreak) {
struct sk_buff *new_skb =
- netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
+ netdev_alloc_skb_ip_align(netdev, length);
if (new_skb) {
- skb_reserve(new_skb, NET_IP_ALIGN);
skb_copy_to_linear_data_offset(new_skb,
-NET_IP_ALIGN,
(skb->data -
@@ -3916,8 +3938,8 @@ next_desc:
adapter->total_rx_packets += total_rx_packets;
adapter->total_rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_packets += total_rx_packets;
+ netdev->stats.rx_bytes += total_rx_bytes;
+ netdev->stats.rx_packets += total_rx_packets;
return cleaned;
}
@@ -3938,9 +3960,7 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info;
struct sk_buff *skb;
unsigned int i;
- unsigned int bufsz = 256 -
- 16 /*for skb_reserve */ -
- NET_IP_ALIGN;
+ unsigned int bufsz = 256 - 16 /*for skb_reserve */ ;
i = rx_ring->next_to_use;
buffer_info = &rx_ring->buffer_info[i];
@@ -3952,7 +3972,7 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
goto check_page;
}
- skb = netdev_alloc_skb(netdev, bufsz);
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
if (unlikely(!skb)) {
/* Better luck next round */
adapter->alloc_rx_buff_failed++;
@@ -3965,7 +3985,7 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
DPRINTK(PROBE, ERR, "skb align check failed: %u bytes "
"at %p\n", bufsz, skb->data);
/* Try again, without freeing the previous */
- skb = netdev_alloc_skb(netdev, bufsz);
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
/* Failed allocation, critical failure */
if (!skb) {
dev_kfree_skb(oldskb);
@@ -3983,12 +4003,6 @@ e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
/* Use new allocation */
dev_kfree_skb(oldskb);
}
- /* Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
buffer_info->length = adapter->rx_buffer_len;
check_page:
@@ -4045,7 +4059,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info;
struct sk_buff *skb;
unsigned int i;
- unsigned int bufsz = adapter->rx_buffer_len + NET_IP_ALIGN;
+ unsigned int bufsz = adapter->rx_buffer_len;
i = rx_ring->next_to_use;
buffer_info = &rx_ring->buffer_info[i];
@@ -4057,7 +4071,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
goto map_skb;
}
- skb = netdev_alloc_skb(netdev, bufsz);
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
if (unlikely(!skb)) {
/* Better luck next round */
adapter->alloc_rx_buff_failed++;
@@ -4070,7 +4084,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
DPRINTK(RX_ERR, ERR, "skb align check failed: %u bytes "
"at %p\n", bufsz, skb->data);
/* Try again, without freeing the previous */
- skb = netdev_alloc_skb(netdev, bufsz);
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
/* Failed allocation, critical failure */
if (!skb) {
dev_kfree_skb(oldskb);
@@ -4089,12 +4103,6 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
/* Use new allocation */
dev_kfree_skb(oldskb);
}
- /* Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
buffer_info->length = adapter->rx_buffer_len;
map_skb:
diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c
index d1e0563a67d..c1a42cfc80b 100644
--- a/drivers/net/e1000e/82571.c
+++ b/drivers/net/e1000e/82571.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -43,10 +43,6 @@
* 82583V Gigabit Network Connection
*/
-#include <linux/netdevice.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-
#include "e1000.h"
#define ID_LED_RESERVED_F746 0xF746
@@ -69,15 +65,15 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw);
static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw);
static s32 e1000_setup_link_82571(struct e1000_hw *hw);
static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw);
+static void e1000_clear_vfta_82571(struct e1000_hw *hw);
static bool e1000_check_mng_mode_82574(struct e1000_hw *hw);
static s32 e1000_led_on_82574(struct e1000_hw *hw);
static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw);
+static void e1000_power_down_phy_copper_82571(struct e1000_hw *hw);
/**
* e1000_init_phy_params_82571 - Init PHY func ptrs.
* @hw: pointer to the HW structure
- *
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
{
@@ -93,6 +89,9 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
phy->reset_delay_us = 100;
+ phy->ops.power_up = e1000_power_up_phy_copper;
+ phy->ops.power_down = e1000_power_down_phy_copper_82571;
+
switch (hw->mac.type) {
case e1000_82571:
case e1000_82572:
@@ -140,8 +139,6 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw)
/**
* e1000_init_nvm_params_82571 - Init NVM func ptrs.
* @hw: pointer to the HW structure
- *
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw)
{
@@ -205,8 +202,6 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw)
/**
* e1000_init_mac_params_82571 - Init MAC func ptrs.
* @hw: pointer to the HW structure
- *
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
{
@@ -240,7 +235,8 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
/* Set rar entry count */
mac->rar_entry_count = E1000_RAR_ENTRIES;
/* Set if manageability features are enabled. */
- mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK) ? 1 : 0;
+ mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK)
+ ? true : false;
/* check for link */
switch (hw->phy.media_type) {
@@ -313,7 +309,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_adapter *adapter)
* indicates that the bootagent or EFI code has
* improperly left this bit enabled
*/
- hw_dbg(hw, "Please update your 82571 Bootagent\n");
+ e_dbg("Please update your 82571 Bootagent\n");
}
ew32(SWSM, swsm & ~E1000_SWSM_SMBI);
}
@@ -487,7 +483,7 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
}
if (i == sw_timeout) {
- hw_dbg(hw, "Driver can't access device - SMBI bit is set.\n");
+ e_dbg("Driver can't access device - SMBI bit is set.\n");
hw->dev_spec.e82571.smb_counter++;
}
/* Get the FW semaphore. */
@@ -505,7 +501,7 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
if (i == fw_timeout) {
/* Release semaphores */
e1000_put_hw_semaphore_82571(hw);
- hw_dbg(hw, "Driver can't access the NVM\n");
+ e_dbg("Driver can't access the NVM\n");
return -E1000_ERR_NVM;
}
@@ -702,8 +698,7 @@ static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset,
u16 words, u16 *data)
{
struct e1000_nvm_info *nvm = &hw->nvm;
- u32 i;
- u32 eewr = 0;
+ u32 i, eewr = 0;
s32 ret_val = 0;
/*
@@ -712,7 +707,7 @@ static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset,
*/
if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
(words == 0)) {
- hw_dbg(hw, "nvm parameter(s) out of bounds\n");
+ e_dbg("nvm parameter(s) out of bounds\n");
return -E1000_ERR_NVM;
}
@@ -753,7 +748,7 @@ static s32 e1000_get_cfg_done_82571(struct e1000_hw *hw)
timeout--;
}
if (!timeout) {
- hw_dbg(hw, "MNG configuration cycle has not completed.\n");
+ e_dbg("MNG configuration cycle has not completed.\n");
return -E1000_ERR_RESET;
}
@@ -763,7 +758,7 @@ static s32 e1000_get_cfg_done_82571(struct e1000_hw *hw)
/**
* e1000_set_d0_lplu_state_82571 - Set Low Power Linkup D0 state
* @hw: pointer to the HW structure
- * @active: TRUE to enable LPLU, FALSE to disable
+ * @active: true to enable LPLU, false to disable
*
* Sets the LPLU D0 state according to the active flag. When activating LPLU
* this function also disables smart speed and vice versa. LPLU will not be
@@ -834,15 +829,11 @@ static s32 e1000_set_d0_lplu_state_82571(struct e1000_hw *hw, bool active)
* e1000_reset_hw_82571 - Reset hardware
* @hw: pointer to the HW structure
*
- * This resets the hardware into a known state. This is a
- * function pointer entry point called by the api module.
+ * This resets the hardware into a known state.
**/
static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
{
- u32 ctrl;
- u32 extcnf_ctrl;
- u32 ctrl_ext;
- u32 icr;
+ u32 ctrl, extcnf_ctrl, ctrl_ext, icr;
s32 ret_val;
u16 i = 0;
@@ -852,9 +843,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
*/
ret_val = e1000e_disable_pcie_master(hw);
if (ret_val)
- hw_dbg(hw, "PCI-E Master disable polling has failed.\n");
+ e_dbg("PCI-E Master disable polling has failed.\n");
- hw_dbg(hw, "Masking off all interrupts\n");
+ e_dbg("Masking off all interrupts\n");
ew32(IMC, 0xffffffff);
ew32(RCTL, 0);
@@ -893,7 +884,7 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
ctrl = er32(CTRL);
- hw_dbg(hw, "Issuing a global reset to MAC\n");
+ e_dbg("Issuing a global reset to MAC\n");
ew32(CTRL, ctrl | E1000_CTRL_RST);
if (hw->nvm.type == e1000_nvm_flash_hw) {
@@ -951,21 +942,19 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
struct e1000_mac_info *mac = &hw->mac;
u32 reg_data;
s32 ret_val;
- u16 i;
- u16 rar_count = mac->rar_entry_count;
+ u16 i, rar_count = mac->rar_entry_count;
e1000_initialize_hw_bits_82571(hw);
/* Initialize identification LED */
ret_val = e1000e_id_led_init(hw);
- if (ret_val) {
- hw_dbg(hw, "Error initializing identification LED\n");
- return ret_val;
- }
+ if (ret_val)
+ e_dbg("Error initializing identification LED\n");
+ /* This is not fatal and we should not stop init due to this */
/* Disabling VLAN filtering */
- hw_dbg(hw, "Initializing the IEEE VLAN\n");
- e1000e_clear_vfta(hw);
+ e_dbg("Initializing the IEEE VLAN\n");
+ mac->ops.clear_vfta(hw);
/* Setup the receive address. */
/*
@@ -978,7 +967,7 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw)
e1000e_init_rx_addrs(hw, rar_count);
/* Zero out the Multicast HASH table */
- hw_dbg(hw, "Zeroing the MTA\n");
+ e_dbg("Zeroing the MTA\n");
for (i = 0; i < mac->mta_reg_count; i++)
E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
@@ -1125,6 +1114,13 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
reg |= (1 << 22);
ew32(GCR, reg);
+ /*
+ * Workaround for hardware errata.
+ * apply workaround for hardware errata documented in errata
+ * docs Fixes issue where some error prone or unreliable PCIe
+ * completions are occurring, particularly with ASPM enabled.
+ * Without fix, issue can cause tx timeouts.
+ */
reg = er32(GCR2);
reg |= 1;
ew32(GCR2, reg);
@@ -1137,13 +1133,13 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw)
}
/**
- * e1000e_clear_vfta - Clear VLAN filter table
+ * e1000_clear_vfta_82571 - Clear VLAN filter table
* @hw: pointer to the HW structure
*
* Clears the register array which contains the VLAN filter table by
* setting all the values to 0.
**/
-void e1000e_clear_vfta(struct e1000_hw *hw)
+static void e1000_clear_vfta_82571(struct e1000_hw *hw)
{
u32 offset;
u32 vfta_value = 0;
@@ -1360,8 +1356,20 @@ static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw)
* e1000_check_for_serdes_link_82571 - Check for link (Serdes)
* @hw: pointer to the HW structure
*
- * Checks for link up on the hardware. If link is not up and we have
- * a signal, then we need to force link up.
+ * Reports the link state as up or down.
+ *
+ * If autonegotiation is supported by the link partner, the link state is
+ * determined by the result of autonegotiation. This is the most likely case.
+ * If autonegotiation is not supported by the link partner, and the link
+ * has a valid signal, force the link up.
+ *
+ * The link state is represented internally here by 4 states:
+ *
+ * 1) down
+ * 2) autoneg_progress
+ * 3) autoneg_complete (the link sucessfully autonegotiated)
+ * 4) forced_up (the link has been forced up, it did not autonegotiate)
+ *
**/
static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
{
@@ -1387,7 +1395,8 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
*/
mac->serdes_link_state =
e1000_serdes_link_autoneg_progress;
- hw_dbg(hw, "AN_UP -> AN_PROG\n");
+ mac->serdes_has_link = false;
+ e_dbg("AN_UP -> AN_PROG\n");
}
break;
@@ -1401,79 +1410,86 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
if (rxcw & E1000_RXCW_C) {
/* Enable autoneg, and unforce link up */
ew32(TXCW, mac->txcw);
- ew32(CTRL,
- (ctrl & ~E1000_CTRL_SLU));
+ ew32(CTRL, (ctrl & ~E1000_CTRL_SLU));
mac->serdes_link_state =
e1000_serdes_link_autoneg_progress;
- hw_dbg(hw, "FORCED_UP -> AN_PROG\n");
+ mac->serdes_has_link = false;
+ e_dbg("FORCED_UP -> AN_PROG\n");
}
break;
case e1000_serdes_link_autoneg_progress:
- /*
- * If the LU bit is set in the STATUS register,
- * autoneg has completed sucessfully. If not,
- * try foring the link because the far end may be
- * available but not capable of autonegotiation.
- */
- if (status & E1000_STATUS_LU) {
- mac->serdes_link_state =
- e1000_serdes_link_autoneg_complete;
- hw_dbg(hw, "AN_PROG -> AN_UP\n");
+ if (rxcw & E1000_RXCW_C) {
+ /*
+ * We received /C/ ordered sets, meaning the
+ * link partner has autonegotiated, and we can
+ * trust the Link Up (LU) status bit.
+ */
+ if (status & E1000_STATUS_LU) {
+ mac->serdes_link_state =
+ e1000_serdes_link_autoneg_complete;
+ e_dbg("AN_PROG -> AN_UP\n");
+ mac->serdes_has_link = true;
+ } else {
+ /* Autoneg completed, but failed. */
+ mac->serdes_link_state =
+ e1000_serdes_link_down;
+ e_dbg("AN_PROG -> DOWN\n");
+ }
} else {
/*
- * Disable autoneg, force link up and
- * full duplex, and change state to forced
+ * The link partner did not autoneg.
+ * Force link up and full duplex, and change
+ * state to forced.
*/
- ew32(TXCW,
- (mac->txcw & ~E1000_TXCW_ANE));
+ ew32(TXCW, (mac->txcw & ~E1000_TXCW_ANE));
ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD);
ew32(CTRL, ctrl);
/* Configure Flow Control after link up. */
- ret_val =
- e1000e_config_fc_after_link_up(hw);
+ ret_val = e1000e_config_fc_after_link_up(hw);
if (ret_val) {
- hw_dbg(hw, "Error config flow control\n");
+ e_dbg("Error config flow control\n");
break;
}
mac->serdes_link_state =
e1000_serdes_link_forced_up;
- hw_dbg(hw, "AN_PROG -> FORCED_UP\n");
+ mac->serdes_has_link = true;
+ e_dbg("AN_PROG -> FORCED_UP\n");
}
- mac->serdes_has_link = true;
break;
case e1000_serdes_link_down:
default:
- /* The link was down but the receiver has now gained
+ /*
+ * The link was down but the receiver has now gained
* valid sync, so lets see if we can bring the link
- * up. */
+ * up.
+ */
ew32(TXCW, mac->txcw);
- ew32(CTRL,
- (ctrl & ~E1000_CTRL_SLU));
+ ew32(CTRL, (ctrl & ~E1000_CTRL_SLU));
mac->serdes_link_state =
e1000_serdes_link_autoneg_progress;
- hw_dbg(hw, "DOWN -> AN_PROG\n");
+ e_dbg("DOWN -> AN_PROG\n");
break;
}
} else {
if (!(rxcw & E1000_RXCW_SYNCH)) {
mac->serdes_has_link = false;
mac->serdes_link_state = e1000_serdes_link_down;
- hw_dbg(hw, "ANYSTATE -> DOWN\n");
+ e_dbg("ANYSTATE -> DOWN\n");
} else {
/*
- * We have sync, and can tolerate one
- * invalid (IV) codeword before declaring
- * link down, so reread to look again
+ * We have sync, and can tolerate one invalid (IV)
+ * codeword before declaring link down, so reread
+ * to look again.
*/
udelay(10);
rxcw = er32(RXCW);
if (rxcw & E1000_RXCW_IV) {
mac->serdes_link_state = e1000_serdes_link_down;
mac->serdes_has_link = false;
- hw_dbg(hw, "ANYSTATE -> DOWN\n");
+ e_dbg("ANYSTATE -> DOWN\n");
}
}
}
@@ -1495,7 +1511,7 @@ static s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data)
ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
@@ -1525,7 +1541,7 @@ static s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data)
bool e1000e_get_laa_state_82571(struct e1000_hw *hw)
{
if (hw->mac.type != e1000_82571)
- return 0;
+ return false;
return hw->dev_spec.e82571.laa_is_present;
}
@@ -1535,7 +1551,7 @@ bool e1000e_get_laa_state_82571(struct e1000_hw *hw)
* @hw: pointer to the HW structure
* @state: enable/disable locally administered address
*
- * Enable/Disable the current locally administers address state.
+ * Enable/Disable the current locally administered address state.
**/
void e1000e_set_laa_state_82571(struct e1000_hw *hw, bool state)
{
@@ -1609,6 +1625,28 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw)
}
/**
+ * e1000_power_down_phy_copper_82571 - Remove link during PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+static void e1000_power_down_phy_copper_82571(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ struct e1000_mac_info *mac = &hw->mac;
+
+ if (!(phy->ops.check_reset_block))
+ return;
+
+ /* If the management interface is not enabled, then power down */
+ if (!(mac->ops.check_mng_mode(hw) || phy->ops.check_reset_block(hw)))
+ e1000_power_down_phy_copper(hw);
+
+ return;
+}
+
+/**
* e1000_clear_hw_cntrs_82571 - Clear device specific hardware counters
* @hw: pointer to the HW structure
*
@@ -1616,44 +1654,42 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw)
**/
static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw)
{
- u32 temp;
-
e1000e_clear_hw_cntrs_base(hw);
- temp = er32(PRC64);
- temp = er32(PRC127);
- temp = er32(PRC255);
- temp = er32(PRC511);
- temp = er32(PRC1023);
- temp = er32(PRC1522);
- temp = er32(PTC64);
- temp = er32(PTC127);
- temp = er32(PTC255);
- temp = er32(PTC511);
- temp = er32(PTC1023);
- temp = er32(PTC1522);
-
- temp = er32(ALGNERRC);
- temp = er32(RXERRC);
- temp = er32(TNCRS);
- temp = er32(CEXTERR);
- temp = er32(TSCTC);
- temp = er32(TSCTFC);
-
- temp = er32(MGTPRC);
- temp = er32(MGTPDC);
- temp = er32(MGTPTC);
-
- temp = er32(IAC);
- temp = er32(ICRXOC);
-
- temp = er32(ICRXPTC);
- temp = er32(ICRXATC);
- temp = er32(ICTXPTC);
- temp = er32(ICTXATC);
- temp = er32(ICTXQEC);
- temp = er32(ICTXQMTC);
- temp = er32(ICRXDMTC);
+ er32(PRC64);
+ er32(PRC127);
+ er32(PRC255);
+ er32(PRC511);
+ er32(PRC1023);
+ er32(PRC1522);
+ er32(PTC64);
+ er32(PTC127);
+ er32(PTC255);
+ er32(PTC511);
+ er32(PTC1023);
+ er32(PTC1522);
+
+ er32(ALGNERRC);
+ er32(RXERRC);
+ er32(TNCRS);
+ er32(CEXTERR);
+ er32(TSCTC);
+ er32(TSCTFC);
+
+ er32(MGTPRC);
+ er32(MGTPDC);
+ er32(MGTPTC);
+
+ er32(IAC);
+ er32(ICRXOC);
+
+ er32(ICRXPTC);
+ er32(ICRXATC);
+ er32(ICTXPTC);
+ er32(ICTXATC);
+ er32(ICTXQEC);
+ er32(ICTXQMTC);
+ er32(ICRXDMTC);
}
static struct e1000_mac_operations e82571_mac_ops = {
@@ -1667,6 +1703,8 @@ static struct e1000_mac_operations e82571_mac_ops = {
/* .led_on: mac type dependent */
.led_off = e1000e_led_off_generic,
.update_mc_addr_list = e1000_update_mc_addr_list_82571,
+ .write_vfta = e1000_write_vfta_generic,
+ .clear_vfta = e1000_clear_vfta_82571,
.reset_hw = e1000_reset_hw_82571,
.init_hw = e1000_init_hw_82571,
.setup_link = e1000_setup_link_82571,
@@ -1675,64 +1713,67 @@ static struct e1000_mac_operations e82571_mac_ops = {
};
static struct e1000_phy_operations e82_phy_ops_igp = {
- .acquire_phy = e1000_get_hw_semaphore_82571,
+ .acquire = e1000_get_hw_semaphore_82571,
+ .check_polarity = e1000_check_polarity_igp,
.check_reset_block = e1000e_check_reset_block_generic,
- .commit_phy = NULL,
+ .commit = NULL,
.force_speed_duplex = e1000e_phy_force_speed_duplex_igp,
.get_cfg_done = e1000_get_cfg_done_82571,
.get_cable_length = e1000e_get_cable_length_igp_2,
- .get_phy_info = e1000e_get_phy_info_igp,
- .read_phy_reg = e1000e_read_phy_reg_igp,
- .release_phy = e1000_put_hw_semaphore_82571,
- .reset_phy = e1000e_phy_hw_reset_generic,
+ .get_info = e1000e_get_phy_info_igp,
+ .read_reg = e1000e_read_phy_reg_igp,
+ .release = e1000_put_hw_semaphore_82571,
+ .reset = e1000e_phy_hw_reset_generic,
.set_d0_lplu_state = e1000_set_d0_lplu_state_82571,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
- .write_phy_reg = e1000e_write_phy_reg_igp,
+ .write_reg = e1000e_write_phy_reg_igp,
.cfg_on_link_up = NULL,
};
static struct e1000_phy_operations e82_phy_ops_m88 = {
- .acquire_phy = e1000_get_hw_semaphore_82571,
+ .acquire = e1000_get_hw_semaphore_82571,
+ .check_polarity = e1000_check_polarity_m88,
.check_reset_block = e1000e_check_reset_block_generic,
- .commit_phy = e1000e_phy_sw_reset,
+ .commit = e1000e_phy_sw_reset,
.force_speed_duplex = e1000e_phy_force_speed_duplex_m88,
.get_cfg_done = e1000e_get_cfg_done,
.get_cable_length = e1000e_get_cable_length_m88,
- .get_phy_info = e1000e_get_phy_info_m88,
- .read_phy_reg = e1000e_read_phy_reg_m88,
- .release_phy = e1000_put_hw_semaphore_82571,
- .reset_phy = e1000e_phy_hw_reset_generic,
+ .get_info = e1000e_get_phy_info_m88,
+ .read_reg = e1000e_read_phy_reg_m88,
+ .release = e1000_put_hw_semaphore_82571,
+ .reset = e1000e_phy_hw_reset_generic,
.set_d0_lplu_state = e1000_set_d0_lplu_state_82571,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
- .write_phy_reg = e1000e_write_phy_reg_m88,
+ .write_reg = e1000e_write_phy_reg_m88,
.cfg_on_link_up = NULL,
};
static struct e1000_phy_operations e82_phy_ops_bm = {
- .acquire_phy = e1000_get_hw_semaphore_82571,
+ .acquire = e1000_get_hw_semaphore_82571,
+ .check_polarity = e1000_check_polarity_m88,
.check_reset_block = e1000e_check_reset_block_generic,
- .commit_phy = e1000e_phy_sw_reset,
+ .commit = e1000e_phy_sw_reset,
.force_speed_duplex = e1000e_phy_force_speed_duplex_m88,
.get_cfg_done = e1000e_get_cfg_done,
.get_cable_length = e1000e_get_cable_length_m88,
- .get_phy_info = e1000e_get_phy_info_m88,
- .read_phy_reg = e1000e_read_phy_reg_bm2,
- .release_phy = e1000_put_hw_semaphore_82571,
- .reset_phy = e1000e_phy_hw_reset_generic,
+ .get_info = e1000e_get_phy_info_m88,
+ .read_reg = e1000e_read_phy_reg_bm2,
+ .release = e1000_put_hw_semaphore_82571,
+ .reset = e1000e_phy_hw_reset_generic,
.set_d0_lplu_state = e1000_set_d0_lplu_state_82571,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
- .write_phy_reg = e1000e_write_phy_reg_bm2,
+ .write_reg = e1000e_write_phy_reg_bm2,
.cfg_on_link_up = NULL,
};
static struct e1000_nvm_operations e82571_nvm_ops = {
- .acquire_nvm = e1000_acquire_nvm_82571,
- .read_nvm = e1000e_read_nvm_eerd,
- .release_nvm = e1000_release_nvm_82571,
- .update_nvm = e1000_update_nvm_checksum_82571,
+ .acquire = e1000_acquire_nvm_82571,
+ .read = e1000e_read_nvm_eerd,
+ .release = e1000_release_nvm_82571,
+ .update = e1000_update_nvm_checksum_82571,
.valid_led_default = e1000_valid_led_default_82571,
- .validate_nvm = e1000_validate_nvm_checksum_82571,
- .write_nvm = e1000_write_nvm_82571,
+ .validate = e1000_validate_nvm_checksum_82571,
+ .write = e1000_write_nvm_82571,
};
struct e1000_info e1000_82571_info = {
diff --git a/drivers/net/e1000e/defines.h b/drivers/net/e1000e/defines.h
index 1190167a8b3..86d2809763c 100644
--- a/drivers/net/e1000e/defines.h
+++ b/drivers/net/e1000e/defines.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h
index 3e187b0e420..cebbd9079d5 100644
--- a/drivers/net/e1000e/e1000.h
+++ b/drivers/net/e1000e/e1000.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -36,6 +36,7 @@
#include <linux/workqueue.h>
#include <linux/io.h>
#include <linux/netdevice.h>
+#include <linux/pci.h>
#include "hw.h"
@@ -47,9 +48,9 @@ struct e1000_info;
#ifdef DEBUG
#define e_dbg(format, arg...) \
- e_printk(KERN_DEBUG , adapter, format, ## arg)
+ e_printk(KERN_DEBUG , hw->adapter, format, ## arg)
#else
-#define e_dbg(format, arg...) do { (void)(adapter); } while (0)
+#define e_dbg(format, arg...) do { (void)(hw); } while (0)
#endif
#define e_err(format, arg...) \
@@ -193,12 +194,15 @@ struct e1000_buffer {
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
+ u16 mapped_as_page;
};
/* Rx */
- /* arrays of page information for packet split */
- struct e1000_ps_page *ps_pages;
+ struct {
+ /* arrays of page information for packet split */
+ struct e1000_ps_page *ps_pages;
+ struct page *page;
+ };
};
- struct page *page;
};
struct e1000_ring {
@@ -331,7 +335,6 @@ struct e1000_adapter {
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
- struct net_device_stats net_stats;
/* structs defined in e1000_hw.h */
struct e1000_hw hw;
@@ -366,6 +369,7 @@ struct e1000_adapter {
struct work_struct downshift_task;
struct work_struct update_phy_task;
struct work_struct led_blink_task;
+ struct work_struct print_hang_task;
};
struct e1000_info {
@@ -488,6 +492,7 @@ extern void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
extern void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw);
extern void e1000e_gig_downshift_workaround_ich8lan(struct e1000_hw *hw);
extern void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw);
+extern s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
extern s32 e1000e_check_for_copper_link(struct e1000_hw *hw);
extern s32 e1000e_check_for_fiber_link(struct e1000_hw *hw);
@@ -507,7 +512,7 @@ extern s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw);
extern s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw);
extern s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw);
extern s32 e1000e_setup_link(struct e1000_hw *hw);
-extern void e1000e_clear_vfta(struct e1000_hw *hw);
+extern void e1000_clear_vfta_generic(struct e1000_hw *hw);
extern void e1000e_init_rx_addrs(struct e1000_hw *hw, u16 rar_count);
extern void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw,
u8 *mc_addr_list,
@@ -523,7 +528,7 @@ extern void e1000e_config_collision_dist(struct e1000_hw *hw);
extern s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw);
extern s32 e1000e_force_mac_fc(struct e1000_hw *hw);
extern s32 e1000e_blink_led(struct e1000_hw *hw);
-extern void e1000e_write_vfta(struct e1000_hw *hw, u32 offset, u32 value);
+extern void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value);
extern void e1000e_reset_adaptive(struct e1000_hw *hw);
extern void e1000e_update_adaptive(struct e1000_hw *hw);
@@ -566,6 +571,8 @@ extern s32 e1000e_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset,
extern s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations,
u32 usec_interval, bool *success);
extern s32 e1000e_phy_reset_dsp(struct e1000_hw *hw);
+extern void e1000_power_up_phy_copper(struct e1000_hw *hw);
+extern void e1000_power_down_phy_copper(struct e1000_hw *hw);
extern s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
extern s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
extern s32 e1000e_check_downshift(struct e1000_hw *hw);
@@ -583,9 +590,15 @@ extern s32 e1000_get_phy_info_82577(struct e1000_hw *hw);
extern s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw);
extern s32 e1000_get_cable_length_82577(struct e1000_hw *hw);
+extern s32 e1000_check_polarity_m88(struct e1000_hw *hw);
+extern s32 e1000_get_phy_info_ife(struct e1000_hw *hw);
+extern s32 e1000_check_polarity_ife(struct e1000_hw *hw);
+extern s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw);
+extern s32 e1000_check_polarity_igp(struct e1000_hw *hw);
+
static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw)
{
- return hw->phy.ops.reset_phy(hw);
+ return hw->phy.ops.reset(hw);
}
static inline s32 e1000_check_reset_block(struct e1000_hw *hw)
@@ -595,12 +608,12 @@ static inline s32 e1000_check_reset_block(struct e1000_hw *hw)
static inline s32 e1e_rphy(struct e1000_hw *hw, u32 offset, u16 *data)
{
- return hw->phy.ops.read_phy_reg(hw, offset, data);
+ return hw->phy.ops.read_reg(hw, offset, data);
}
static inline s32 e1e_wphy(struct e1000_hw *hw, u32 offset, u16 data)
{
- return hw->phy.ops.write_phy_reg(hw, offset, data);
+ return hw->phy.ops.write_reg(hw, offset, data);
}
static inline s32 e1000_get_cable_length(struct e1000_hw *hw)
@@ -620,27 +633,27 @@ extern s32 e1000e_read_mac_addr(struct e1000_hw *hw);
static inline s32 e1000_validate_nvm_checksum(struct e1000_hw *hw)
{
- return hw->nvm.ops.validate_nvm(hw);
+ return hw->nvm.ops.validate(hw);
}
static inline s32 e1000e_update_nvm_checksum(struct e1000_hw *hw)
{
- return hw->nvm.ops.update_nvm(hw);
+ return hw->nvm.ops.update(hw);
}
static inline s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
{
- return hw->nvm.ops.read_nvm(hw, offset, words, data);
+ return hw->nvm.ops.read(hw, offset, words, data);
}
static inline s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
{
- return hw->nvm.ops.write_nvm(hw, offset, words, data);
+ return hw->nvm.ops.write(hw, offset, words, data);
}
static inline s32 e1000_get_phy_info(struct e1000_hw *hw)
{
- return hw->phy.ops.get_phy_info(hw);
+ return hw->phy.ops.get_info(hw);
}
static inline s32 e1000e_check_mng_mode(struct e1000_hw *hw)
diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/e1000e/es2lan.c
index ae5d7368935..d2a10479460 100644
--- a/drivers/net/e1000e/es2lan.c
+++ b/drivers/net/e1000e/es2lan.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -31,11 +31,6 @@
* 80003ES2LAN Gigabit Ethernet Controller (Serdes)
*/
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-
#include "e1000.h"
#define E1000_KMRNCTRLSTA_OFFSET_FIFO_CTRL 0x00
@@ -104,6 +99,8 @@
*/
static const u16 e1000_gg82563_cable_length_table[] =
{ 0, 60, 115, 150, 150, 60, 115, 150, 180, 180, 0xFF };
+#define GG82563_CABLE_LENGTH_TABLE_SIZE \
+ ARRAY_SIZE(e1000_gg82563_cable_length_table)
static s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw);
static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask);
@@ -117,12 +114,11 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
u16 *data);
static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
u16 data);
+static void e1000_power_down_phy_copper_80003es2lan(struct e1000_hw *hw);
/**
* e1000_init_phy_params_80003es2lan - Init ESB2 PHY func ptrs.
* @hw: pointer to the HW structure
- *
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw)
{
@@ -132,6 +128,9 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw)
if (hw->phy.media_type != e1000_media_type_copper) {
phy->type = e1000_phy_none;
return 0;
+ } else {
+ phy->ops.power_up = e1000_power_up_phy_copper;
+ phy->ops.power_down = e1000_power_down_phy_copper_80003es2lan;
}
phy->addr = 1;
@@ -152,8 +151,6 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw)
/**
* e1000_init_nvm_params_80003es2lan - Init ESB2 NVM func ptrs.
* @hw: pointer to the HW structure
- *
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
{
@@ -200,8 +197,6 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw)
/**
* e1000_init_mac_params_80003es2lan - Init ESB2 MAC func ptrs.
* @hw: pointer to the HW structure
- *
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_init_mac_params_80003es2lan(struct e1000_adapter *adapter)
{
@@ -224,7 +219,8 @@ static s32 e1000_init_mac_params_80003es2lan(struct e1000_adapter *adapter)
/* Set rar entry count */
mac->rar_entry_count = E1000_RAR_ENTRIES;
/* Set if manageability features are enabled. */
- mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK) ? 1 : 0;
+ mac->arc_subsystem_valid = (er32(FWSM) & E1000_FWSM_MODE_MASK)
+ ? true : false;
/* check for link */
switch (hw->phy.media_type) {
@@ -272,8 +268,7 @@ static s32 e1000_get_variants_80003es2lan(struct e1000_adapter *adapter)
* e1000_acquire_phy_80003es2lan - Acquire rights to access PHY
* @hw: pointer to the HW structure
*
- * A wrapper to acquire access rights to the correct PHY. This is a
- * function pointer entry point called by the api module.
+ * A wrapper to acquire access rights to the correct PHY.
**/
static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw)
{
@@ -287,8 +282,7 @@ static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw)
* e1000_release_phy_80003es2lan - Release rights to access PHY
* @hw: pointer to the HW structure
*
- * A wrapper to release access rights to the correct PHY. This is a
- * function pointer entry point called by the api module.
+ * A wrapper to release access rights to the correct PHY.
**/
static void e1000_release_phy_80003es2lan(struct e1000_hw *hw)
{
@@ -333,8 +327,7 @@ static void e1000_release_mac_csr_80003es2lan(struct e1000_hw *hw)
* e1000_acquire_nvm_80003es2lan - Acquire rights to access NVM
* @hw: pointer to the HW structure
*
- * Acquire the semaphore to access the EEPROM. This is a function
- * pointer entry point called by the api module.
+ * Acquire the semaphore to access the EEPROM.
**/
static s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw)
{
@@ -356,8 +349,7 @@ static s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw)
* e1000_release_nvm_80003es2lan - Relinquish rights to access NVM
* @hw: pointer to the HW structure
*
- * Release the semaphore used to access the EEPROM. This is a
- * function pointer entry point called by the api module.
+ * Release the semaphore used to access the EEPROM.
**/
static void e1000_release_nvm_80003es2lan(struct e1000_hw *hw)
{
@@ -399,8 +391,7 @@ static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask)
}
if (i == timeout) {
- hw_dbg(hw,
- "Driver can't access resource, SW_FW_SYNC timeout.\n");
+ e_dbg("Driver can't access resource, SW_FW_SYNC timeout.\n");
return -E1000_ERR_SWFW_SYNC;
}
@@ -440,8 +431,7 @@ static void e1000_release_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask)
* @offset: offset of the register to read
* @data: pointer to the data returned from the operation
*
- * Read the GG82563 PHY register. This is a function pointer entry
- * point called by the api module.
+ * Read the GG82563 PHY register.
**/
static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
u32 offset, u16 *data)
@@ -505,8 +495,7 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
* @offset: offset of the register to read
* @data: value to write to the register
*
- * Write to the GG82563 PHY register. This is a function pointer entry
- * point called by the api module.
+ * Write to the GG82563 PHY register.
**/
static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
u32 offset, u16 data)
@@ -571,8 +560,7 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
* @words: number of words to write
* @data: buffer of data to write to the NVM
*
- * Write "words" of data to the ESB2 NVM. This is a function
- * pointer entry point called by the api module.
+ * Write "words" of data to the ESB2 NVM.
**/
static s32 e1000_write_nvm_80003es2lan(struct e1000_hw *hw, u16 offset,
u16 words, u16 *data)
@@ -602,7 +590,7 @@ static s32 e1000_get_cfg_done_80003es2lan(struct e1000_hw *hw)
timeout--;
}
if (!timeout) {
- hw_dbg(hw, "MNG configuration cycle has not completed.\n");
+ e_dbg("MNG configuration cycle has not completed.\n");
return -E1000_ERR_RESET;
}
@@ -635,7 +623,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- hw_dbg(hw, "GG82563 PSCR: %X\n", phy_data);
+ e_dbg("GG82563 PSCR: %X\n", phy_data);
ret_val = e1e_rphy(hw, PHY_CONTROL, &phy_data);
if (ret_val)
@@ -653,7 +641,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw)
udelay(1);
if (hw->phy.autoneg_wait_to_complete) {
- hw_dbg(hw, "Waiting for forced speed/duplex link "
+ e_dbg("Waiting for forced speed/duplex link "
"on GG82563 phy.\n");
ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
@@ -712,21 +700,27 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw)
static s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
- s32 ret_val;
- u16 phy_data;
- u16 index;
+ s32 ret_val = 0;
+ u16 phy_data, index;
ret_val = e1e_rphy(hw, GG82563_PHY_DSP_DISTANCE, &phy_data);
if (ret_val)
- return ret_val;
+ goto out;
index = phy_data & GG82563_DSPD_CABLE_LENGTH;
+
+ if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5) {
+ ret_val = -E1000_ERR_PHY;
+ goto out;
+ }
+
phy->min_cable_length = e1000_gg82563_cable_length_table[index];
- phy->max_cable_length = e1000_gg82563_cable_length_table[index+5];
+ phy->max_cable_length = e1000_gg82563_cable_length_table[index + 5];
phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
- return 0;
+out:
+ return ret_val;
}
/**
@@ -736,7 +730,6 @@ static s32 e1000_get_cable_length_80003es2lan(struct e1000_hw *hw)
* @duplex: pointer to duplex buffer
*
* Retrieve the current speed and duplex configuration.
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed,
u16 *duplex)
@@ -762,12 +755,10 @@ static s32 e1000_get_link_up_info_80003es2lan(struct e1000_hw *hw, u16 *speed,
* @hw: pointer to the HW structure
*
* Perform a global reset to the ESB2 controller.
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw)
{
- u32 ctrl;
- u32 icr;
+ u32 ctrl, icr;
s32 ret_val;
/*
@@ -776,9 +767,9 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw)
*/
ret_val = e1000e_disable_pcie_master(hw);
if (ret_val)
- hw_dbg(hw, "PCI-E Master disable polling has failed.\n");
+ e_dbg("PCI-E Master disable polling has failed.\n");
- hw_dbg(hw, "Masking off all interrupts\n");
+ e_dbg("Masking off all interrupts\n");
ew32(IMC, 0xffffffff);
ew32(RCTL, 0);
@@ -790,7 +781,7 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw)
ctrl = er32(CTRL);
ret_val = e1000_acquire_phy_80003es2lan(hw);
- hw_dbg(hw, "Issuing a global reset to MAC\n");
+ e_dbg("Issuing a global reset to MAC\n");
ew32(CTRL, ctrl | E1000_CTRL_RST);
e1000_release_phy_80003es2lan(hw);
@@ -811,7 +802,6 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw)
* @hw: pointer to the HW structure
*
* Initialize the hw bits, LED, VFTA, MTA, link and hw counters.
- * This is a function pointer entry point called by the api module.
**/
static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
{
@@ -824,20 +814,19 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw)
/* Initialize identification LED */
ret_val = e1000e_id_led_init(hw);
- if (ret_val) {
- hw_dbg(hw, "Error initializing identification LED\n");
- return ret_val;
- }
+ if (ret_val)
+ e_dbg("Error initializing identification LED\n");
+ /* This is not fatal and we should not stop init due to this */
/* Disabling VLAN filtering */
- hw_dbg(hw, "Initializing the IEEE VLAN\n");
- e1000e_clear_vfta(hw);
+ e_dbg("Initializing the IEEE VLAN\n");
+ mac->ops.clear_vfta(hw);
/* Setup the receive address. */
e1000e_init_rx_addrs(hw, mac->rar_entry_count);
/* Zero out the Multicast HASH table */
- hw_dbg(hw, "Zeroing the MTA\n");
+ e_dbg("Zeroing the MTA\n");
for (i = 0; i < mac->mta_reg_count; i++)
E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
@@ -994,7 +983,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw)
/* SW Reset the PHY so all changes take effect */
ret_val = e1000e_commit_phy(hw);
if (ret_val) {
- hw_dbg(hw, "Error Resetting the PHY\n");
+ e_dbg("Error Resetting the PHY\n");
return ret_val;
}
@@ -1318,6 +1307,23 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
}
/**
+ * e1000_power_down_phy_copper_80003es2lan - Remove link during PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+static void e1000_power_down_phy_copper_80003es2lan(struct e1000_hw *hw)
+{
+ /* If the management interface is not enabled, then power down */
+ if (!(hw->mac.ops.check_mng_mode(hw) ||
+ hw->phy.ops.check_reset_block(hw)))
+ e1000_power_down_phy_copper(hw);
+
+ return;
+}
+
+/**
* e1000_clear_hw_cntrs_80003es2lan - Clear device specific hardware counters
* @hw: pointer to the HW structure
*
@@ -1325,44 +1331,42 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
**/
static void e1000_clear_hw_cntrs_80003es2lan(struct e1000_hw *hw)
{
- u32 temp;
-
e1000e_clear_hw_cntrs_base(hw);
- temp = er32(PRC64);
- temp = er32(PRC127);
- temp = er32(PRC255);
- temp = er32(PRC511);
- temp = er32(PRC1023);
- temp = er32(PRC1522);
- temp = er32(PTC64);
- temp = er32(PTC127);
- temp = er32(PTC255);
- temp = er32(PTC511);
- temp = er32(PTC1023);
- temp = er32(PTC1522);
-
- temp = er32(ALGNERRC);
- temp = er32(RXERRC);
- temp = er32(TNCRS);
- temp = er32(CEXTERR);
- temp = er32(TSCTC);
- temp = er32(TSCTFC);
-
- temp = er32(MGTPRC);
- temp = er32(MGTPDC);
- temp = er32(MGTPTC);
-
- temp = er32(IAC);
- temp = er32(ICRXOC);
-
- temp = er32(ICRXPTC);
- temp = er32(ICRXATC);
- temp = er32(ICTXPTC);
- temp = er32(ICTXATC);
- temp = er32(ICTXQEC);
- temp = er32(ICTXQMTC);
- temp = er32(ICRXDMTC);
+ er32(PRC64);
+ er32(PRC127);
+ er32(PRC255);
+ er32(PRC511);
+ er32(PRC1023);
+ er32(PRC1522);
+ er32(PTC64);
+ er32(PTC127);
+ er32(PTC255);
+ er32(PTC511);
+ er32(PTC1023);
+ er32(PTC1522);
+
+ er32(ALGNERRC);
+ er32(RXERRC);
+ er32(TNCRS);
+ er32(CEXTERR);
+ er32(TSCTC);
+ er32(TSCTFC);
+
+ er32(MGTPRC);
+ er32(MGTPDC);
+ er32(MGTPTC);
+
+ er32(IAC);
+ er32(ICRXOC);
+
+ er32(ICRXPTC);
+ er32(ICRXATC);
+ er32(ICTXPTC);
+ er32(ICTXATC);
+ er32(ICTXQEC);
+ er32(ICTXQMTC);
+ er32(ICRXDMTC);
}
static struct e1000_mac_operations es2_mac_ops = {
@@ -1376,6 +1380,8 @@ static struct e1000_mac_operations es2_mac_ops = {
.led_on = e1000e_led_on_generic,
.led_off = e1000e_led_off_generic,
.update_mc_addr_list = e1000e_update_mc_addr_list_generic,
+ .write_vfta = e1000_write_vfta_generic,
+ .clear_vfta = e1000_clear_vfta_generic,
.reset_hw = e1000_reset_hw_80003es2lan,
.init_hw = e1000_init_hw_80003es2lan,
.setup_link = e1000e_setup_link,
@@ -1384,30 +1390,31 @@ static struct e1000_mac_operations es2_mac_ops = {
};
static struct e1000_phy_operations es2_phy_ops = {
- .acquire_phy = e1000_acquire_phy_80003es2lan,
+ .acquire = e1000_acquire_phy_80003es2lan,
+ .check_polarity = e1000_check_polarity_m88,
.check_reset_block = e1000e_check_reset_block_generic,
- .commit_phy = e1000e_phy_sw_reset,
+ .commit = e1000e_phy_sw_reset,
.force_speed_duplex = e1000_phy_force_speed_duplex_80003es2lan,
.get_cfg_done = e1000_get_cfg_done_80003es2lan,
.get_cable_length = e1000_get_cable_length_80003es2lan,
- .get_phy_info = e1000e_get_phy_info_m88,
- .read_phy_reg = e1000_read_phy_reg_gg82563_80003es2lan,
- .release_phy = e1000_release_phy_80003es2lan,
- .reset_phy = e1000e_phy_hw_reset_generic,
+ .get_info = e1000e_get_phy_info_m88,
+ .read_reg = e1000_read_phy_reg_gg82563_80003es2lan,
+ .release = e1000_release_phy_80003es2lan,
+ .reset = e1000e_phy_hw_reset_generic,
.set_d0_lplu_state = NULL,
.set_d3_lplu_state = e1000e_set_d3_lplu_state,
- .write_phy_reg = e1000_write_phy_reg_gg82563_80003es2lan,
+ .write_reg = e1000_write_phy_reg_gg82563_80003es2lan,
.cfg_on_link_up = e1000_cfg_on_link_up_80003es2lan,
};
static struct e1000_nvm_operations es2_nvm_ops = {
- .acquire_nvm = e1000_acquire_nvm_80003es2lan,
- .read_nvm = e1000e_read_nvm_eerd,
- .release_nvm = e1000_release_nvm_80003es2lan,
- .update_nvm = e1000e_update_nvm_checksum_generic,
+ .acquire = e1000_acquire_nvm_80003es2lan,
+ .read = e1000e_read_nvm_eerd,
+ .release = e1000_release_nvm_80003es2lan,
+ .update = e1000e_update_nvm_checksum_generic,
.valid_led_default = e1000e_valid_led_default,
- .validate_nvm = e1000e_validate_nvm_checksum_generic,
- .write_nvm = e1000_write_nvm_80003es2lan,
+ .validate = e1000e_validate_nvm_checksum_generic,
+ .write = e1000_write_nvm_80003es2lan,
};
struct e1000_info e1000_es2_info = {
diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c
index e82638ecae8..0aa50c229c7 100644
--- a/drivers/net/e1000e/ethtool.c
+++ b/drivers/net/e1000e/ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -35,14 +35,22 @@
#include "e1000.h"
+enum {NETDEV_STATS, E1000_STATS};
+
struct e1000_stats {
char stat_string[ETH_GSTRING_LEN];
+ int type;
int sizeof_stat;
int stat_offset;
};
-#define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \
- offsetof(struct e1000_adapter, m)
+#define E1000_STAT(m) E1000_STATS, \
+ sizeof(((struct e1000_adapter *)0)->m), \
+ offsetof(struct e1000_adapter, m)
+#define E1000_NETDEV_STAT(m) NETDEV_STATS, \
+ sizeof(((struct net_device *)0)->m), \
+ offsetof(struct net_device, m)
+
static const struct e1000_stats e1000_gstrings_stats[] = {
{ "rx_packets", E1000_STAT(stats.gprc) },
{ "tx_packets", E1000_STAT(stats.gptc) },
@@ -52,21 +60,21 @@ static const struct e1000_stats e1000_gstrings_stats[] = {
{ "tx_broadcast", E1000_STAT(stats.bptc) },
{ "rx_multicast", E1000_STAT(stats.mprc) },
{ "tx_multicast", E1000_STAT(stats.mptc) },
- { "rx_errors", E1000_STAT(net_stats.rx_errors) },
- { "tx_errors", E1000_STAT(net_stats.tx_errors) },
- { "tx_dropped", E1000_STAT(net_stats.tx_dropped) },
+ { "rx_errors", E1000_NETDEV_STAT(stats.rx_errors) },
+ { "tx_errors", E1000_NETDEV_STAT(stats.tx_errors) },
+ { "tx_dropped", E1000_NETDEV_STAT(stats.tx_dropped) },
{ "multicast", E1000_STAT(stats.mprc) },
{ "collisions", E1000_STAT(stats.colc) },
- { "rx_length_errors", E1000_STAT(net_stats.rx_length_errors) },
- { "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) },
+ { "rx_length_errors", E1000_NETDEV_STAT(stats.rx_length_errors) },
+ { "rx_over_errors", E1000_NETDEV_STAT(stats.rx_over_errors) },
{ "rx_crc_errors", E1000_STAT(stats.crcerrs) },
- { "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) },
+ { "rx_frame_errors", E1000_NETDEV_STAT(stats.rx_frame_errors) },
{ "rx_no_buffer_count", E1000_STAT(stats.rnbc) },
{ "rx_missed_errors", E1000_STAT(stats.mpc) },
{ "tx_aborted_errors", E1000_STAT(stats.ecol) },
{ "tx_carrier_errors", E1000_STAT(stats.tncrs) },
- { "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) },
- { "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) },
+ { "tx_fifo_errors", E1000_NETDEV_STAT(stats.tx_fifo_errors) },
+ { "tx_heartbeat_errors", E1000_NETDEV_STAT(stats.tx_heartbeat_errors) },
{ "tx_window_errors", E1000_STAT(stats.latecol) },
{ "tx_abort_late_coll", E1000_STAT(stats.latecol) },
{ "tx_deferred_ok", E1000_STAT(stats.dc) },
@@ -182,6 +190,17 @@ static int e1000_get_settings(struct net_device *netdev,
static u32 e1000_get_link(struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_mac_info *mac = &adapter->hw.mac;
+
+ /*
+ * If the link is not reported up to netdev, interrupts are disabled,
+ * and so the physical link state may have changed since we last
+ * looked. Set get_link_status to make sure that the true link
+ * state is interrogated, rather than pulling a cached and possibly
+ * stale link state from the driver.
+ */
+ if (!netif_carrier_ok(netdev))
+ mac->get_link_status = 1;
return e1000_has_link(adapter);
}
@@ -516,7 +535,8 @@ static int e1000_get_eeprom(struct net_device *netdev,
if (ret_val) {
/* a read error occurred, throw away the result */
- memset(eeprom_buff, 0xff, sizeof(eeprom_buff));
+ memset(eeprom_buff, 0xff, sizeof(u16) *
+ (last_word - first_word + 1));
} else {
/* Device's eeprom is always little-endian, word addressable */
for (i = 0; i < last_word - first_word + 1; i++)
@@ -596,7 +616,9 @@ static int e1000_set_eeprom(struct net_device *netdev,
* and flush shadow RAM for applicable controllers
*/
if ((first_word <= NVM_CHECKSUM_REG) ||
- (hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82573))
+ (hw->mac.type == e1000_82583) ||
+ (hw->mac.type == e1000_82574) ||
+ (hw->mac.type == e1000_82573))
ret_val = e1000e_update_nvm_checksum(hw);
out:
@@ -929,10 +951,10 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
e1000e_set_interrupt_capability(adapter);
}
/* Hook up test interrupt handler just for this test */
- if (!request_irq(irq, &e1000_test_intr, IRQF_PROBE_SHARED, netdev->name,
+ if (!request_irq(irq, e1000_test_intr, IRQF_PROBE_SHARED, netdev->name,
netdev)) {
shared_int = 0;
- } else if (request_irq(irq, &e1000_test_intr, IRQF_SHARED,
+ } else if (request_irq(irq, e1000_test_intr, IRQF_SHARED,
netdev->name, netdev)) {
*data = 1;
ret_val = -1;
@@ -1239,6 +1261,10 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
hw->mac.autoneg = 0;
+ /* Workaround: K1 must be disabled for stable 1Gbps operation */
+ if (hw->mac.type == e1000_pchlan)
+ e1000_configure_k1_ich8lan(hw, false);
+
if (hw->phy.type == e1000_phy_m88) {
/* Auto-MDI/MDIX Off */
e1e_wphy(hw, M88E1000_PHY_SPEC_CTRL, 0x0808);
@@ -1769,12 +1795,11 @@ static int e1000_set_wol(struct net_device *netdev,
{
struct e1000_adapter *adapter = netdev_priv(netdev);
- if (wol->wolopts & WAKE_MAGICSECURE)
- return -EOPNOTSUPP;
-
if (!(adapter->flags & FLAG_HAS_WOL) ||
- !device_can_wakeup(&adapter->pdev->dev))
- return wol->wolopts ? -EOPNOTSUPP : 0;
+ !device_can_wakeup(&adapter->pdev->dev) ||
+ (wol->wolopts & ~(WAKE_UCAST | WAKE_MCAST | WAKE_BCAST |
+ WAKE_MAGIC | WAKE_PHY | WAKE_ARP)))
+ return -EOPNOTSUPP;
/* these settings will always override what we currently have */
adapter->wol = 0;
@@ -1832,6 +1857,7 @@ static int e1000_phys_id(struct net_device *netdev, u32 data)
if ((hw->phy.type == e1000_phy_ife) ||
(hw->mac.type == e1000_pchlan) ||
+ (hw->mac.type == e1000_82583) ||
(hw->mac.type == e1000_82574)) {
INIT_WORK(&adapter->led_blink_task, e1000e_led_blink_task);
if (!adapter->blink_timer.function) {
@@ -1912,10 +1938,21 @@ static void e1000_get_ethtool_stats(struct net_device *netdev,
{
struct e1000_adapter *adapter = netdev_priv(netdev);
int i;
+ char *p = NULL;
e1000e_update_stats(adapter);
for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) {
- char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset;
+ switch (e1000_gstrings_stats[i].type) {
+ case NETDEV_STATS:
+ p = (char *) netdev +
+ e1000_gstrings_stats[i].stat_offset;
+ break;
+ case E1000_STATS:
+ p = (char *) adapter +
+ e1000_gstrings_stats[i].stat_offset;
+ break;
+ }
+
data[i] = (e1000_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
@@ -1975,6 +2012,8 @@ static const struct ethtool_ops e1000_ethtool_ops = {
.get_sset_count = e1000e_get_sset_count,
.get_coalesce = e1000_get_coalesce,
.set_coalesce = e1000_set_coalesce,
+ .get_flags = ethtool_op_get_flags,
+ .set_flags = ethtool_op_set_flags,
};
void e1000e_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h
index aaea41ef794..a7d08dae79c 100644
--- a/drivers/net/e1000e/hw.h
+++ b/drivers/net/e1000e/hw.h
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -219,7 +219,7 @@ enum e1e_registers {
E1000_HICR = 0x08F00, /* Host Interface Control */
};
-/* RSS registers */
+#define E1000_MAX_PHY_ADDR 4
/* IGP01E1000 Specific Registers */
#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */
@@ -356,6 +356,7 @@ enum e1e_registers {
#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA
#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB
+#define E1000_DEV_ID_ICH8_82567V_3 0x1501
#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049
#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A
#define E1000_DEV_ID_ICH8_IGP_C 0x104B
@@ -741,6 +742,7 @@ struct e1000_mac_operations {
s32 (*check_for_link)(struct e1000_hw *);
s32 (*cleanup_led)(struct e1000_hw *);
void (*clear_hw_cntrs)(struct e1000_hw *);
+ void (*clear_vfta)(struct e1000_hw *);
s32 (*get_bus_info)(struct e1000_hw *);
s32 (*get_link_up_info)(struct e1000_hw *, u16 *, u16 *);
s32 (*led_on)(struct e1000_hw *);
@@ -751,38 +753,41 @@ struct e1000_mac_operations {
s32 (*setup_link)(struct e1000_hw *);
s32 (*setup_physical_interface)(struct e1000_hw *);
s32 (*setup_led)(struct e1000_hw *);
+ void (*write_vfta)(struct e1000_hw *, u32, u32);
};
/* Function pointers for the PHY. */
struct e1000_phy_operations {
- s32 (*acquire_phy)(struct e1000_hw *);
+ s32 (*acquire)(struct e1000_hw *);
+ s32 (*cfg_on_link_up)(struct e1000_hw *);
s32 (*check_polarity)(struct e1000_hw *);
s32 (*check_reset_block)(struct e1000_hw *);
- s32 (*commit_phy)(struct e1000_hw *);
+ s32 (*commit)(struct e1000_hw *);
s32 (*force_speed_duplex)(struct e1000_hw *);
s32 (*get_cfg_done)(struct e1000_hw *hw);
s32 (*get_cable_length)(struct e1000_hw *);
- s32 (*get_phy_info)(struct e1000_hw *);
- s32 (*read_phy_reg)(struct e1000_hw *, u32, u16 *);
- s32 (*read_phy_reg_locked)(struct e1000_hw *, u32, u16 *);
- void (*release_phy)(struct e1000_hw *);
- s32 (*reset_phy)(struct e1000_hw *);
+ s32 (*get_info)(struct e1000_hw *);
+ s32 (*read_reg)(struct e1000_hw *, u32, u16 *);
+ s32 (*read_reg_locked)(struct e1000_hw *, u32, u16 *);
+ void (*release)(struct e1000_hw *);
+ s32 (*reset)(struct e1000_hw *);
s32 (*set_d0_lplu_state)(struct e1000_hw *, bool);
s32 (*set_d3_lplu_state)(struct e1000_hw *, bool);
- s32 (*write_phy_reg)(struct e1000_hw *, u32, u16);
- s32 (*write_phy_reg_locked)(struct e1000_hw *, u32, u16);
- s32 (*cfg_on_link_up)(struct e1000_hw *);
+ s32 (*write_reg)(struct e1000_hw *, u32, u16);
+ s32 (*write_reg_locked)(struct e1000_hw *, u32, u16);
+ void (*power_up)(struct e1000_hw *);
+ void (*power_down)(struct e1000_hw *);
};
/* Function pointers for the NVM. */
struct e1000_nvm_operations {
- s32 (*acquire_nvm)(struct e1000_hw *);
- s32 (*read_nvm)(struct e1000_hw *, u16, u16, u16 *);
- void (*release_nvm)(struct e1000_hw *);
- s32 (*update_nvm)(struct e1000_hw *);
+ s32 (*acquire)(struct e1000_hw *);
+ s32 (*read)(struct e1000_hw *, u16, u16, u16 *);
+ void (*release)(struct e1000_hw *);
+ s32 (*update)(struct e1000_hw *);
s32 (*valid_led_default)(struct e1000_hw *, u16 *);
- s32 (*validate_nvm)(struct e1000_hw *);
- s32 (*write_nvm)(struct e1000_hw *, u16, u16, u16 *);
+ s32 (*validate)(struct e1000_hw *);
+ s32 (*write)(struct e1000_hw *, u16, u16, u16 *);
};
struct e1000_mac_info {
@@ -925,15 +930,4 @@ struct e1000_hw {
} dev_spec;
};
-#ifdef DEBUG
-#define hw_dbg(hw, format, arg...) \
- printk(KERN_DEBUG "%s: " format, e1000e_get_hw_dev_name(hw), ##arg)
-#else
-static inline int __attribute__ ((format (printf, 2, 3)))
-hw_dbg(struct e1000_hw *hw, const char *format, ...)
-{
- return 0;
-}
-#endif
-
#endif
diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
index eff3f478365..7b33be98a2c 100644
--- a/drivers/net/e1000e/ich8lan.c
+++ b/drivers/net/e1000e/ich8lan.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -54,11 +54,6 @@
* 82578DC Gigabit Network Connection
*/
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-
#include "e1000.h"
#define ICH_FLASH_GFPREG 0x0000
@@ -200,7 +195,6 @@ union ich8_flash_protected_range {
static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw);
static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw);
static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw);
-static s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw);
static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank);
static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
u32 offset, u8 byte);
@@ -222,9 +216,9 @@ static s32 e1000_cleanup_led_pchlan(struct e1000_hw *hw);
static s32 e1000_led_on_pchlan(struct e1000_hw *hw);
static s32 e1000_led_off_pchlan(struct e1000_hw *hw);
static s32 e1000_set_lplu_state_pchlan(struct e1000_hw *hw, bool active);
+static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw);
static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw);
static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link);
-static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
static inline u16 __er16flash(struct e1000_hw *hw, unsigned long reg)
{
@@ -265,26 +259,37 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw)
phy->addr = 1;
phy->reset_delay_us = 100;
- phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan;
- phy->ops.read_phy_reg = e1000_read_phy_reg_hv;
- phy->ops.read_phy_reg_locked = e1000_read_phy_reg_hv_locked;
+ phy->ops.read_reg = e1000_read_phy_reg_hv;
+ phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked;
phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan;
phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan;
- phy->ops.write_phy_reg = e1000_write_phy_reg_hv;
- phy->ops.write_phy_reg_locked = e1000_write_phy_reg_hv_locked;
+ phy->ops.write_reg = e1000_write_phy_reg_hv;
+ phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked;
+ phy->ops.power_up = e1000_power_up_phy_copper;
+ phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
phy->id = e1000_phy_unknown;
e1000e_get_phy_id(hw);
phy->type = e1000e_get_phy_type_from_id(phy->id);
- if (phy->type == e1000_phy_82577) {
+ switch (phy->type) {
+ case e1000_phy_82577:
phy->ops.check_polarity = e1000_check_polarity_82577;
phy->ops.force_speed_duplex =
e1000_phy_force_speed_duplex_82577;
- phy->ops.get_cable_length = e1000_get_cable_length_82577;
- phy->ops.get_phy_info = e1000_get_phy_info_82577;
- phy->ops.commit_phy = e1000e_phy_sw_reset;
+ phy->ops.get_cable_length = e1000_get_cable_length_82577;
+ phy->ops.get_info = e1000_get_phy_info_82577;
+ phy->ops.commit = e1000e_phy_sw_reset;
+ case e1000_phy_82578:
+ phy->ops.check_polarity = e1000_check_polarity_m88;
+ phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_m88;
+ phy->ops.get_cable_length = e1000e_get_cable_length_m88;
+ phy->ops.get_info = e1000e_get_phy_info_m88;
+ break;
+ default:
+ ret_val = -E1000_ERR_PHY;
+ break;
}
return ret_val;
@@ -305,17 +310,22 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
phy->addr = 1;
phy->reset_delay_us = 100;
+ phy->ops.power_up = e1000_power_up_phy_copper;
+ phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
+
/*
* We may need to do this twice - once for IGP and if that fails,
* we'll set BM func pointers and try again
*/
ret_val = e1000e_determine_phy_address(hw);
if (ret_val) {
- hw->phy.ops.write_phy_reg = e1000e_write_phy_reg_bm;
- hw->phy.ops.read_phy_reg = e1000e_read_phy_reg_bm;
+ phy->ops.write_reg = e1000e_write_phy_reg_bm;
+ phy->ops.read_reg = e1000e_read_phy_reg_bm;
ret_val = e1000e_determine_phy_address(hw);
- if (ret_val)
+ if (ret_val) {
+ e_dbg("Cannot determine PHY addr. Erroring out\n");
return ret_val;
+ }
}
phy->id = 0;
@@ -332,29 +342,36 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
case IGP03E1000_E_PHY_ID:
phy->type = e1000_phy_igp_3;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
- phy->ops.read_phy_reg_locked = e1000e_read_phy_reg_igp_locked;
- phy->ops.write_phy_reg_locked = e1000e_write_phy_reg_igp_locked;
+ phy->ops.read_reg_locked = e1000e_read_phy_reg_igp_locked;
+ phy->ops.write_reg_locked = e1000e_write_phy_reg_igp_locked;
+ phy->ops.get_info = e1000e_get_phy_info_igp;
+ phy->ops.check_polarity = e1000_check_polarity_igp;
+ phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_igp;
break;
case IFE_E_PHY_ID:
case IFE_PLUS_E_PHY_ID:
case IFE_C_E_PHY_ID:
phy->type = e1000_phy_ife;
phy->autoneg_mask = E1000_ALL_NOT_GIG;
+ phy->ops.get_info = e1000_get_phy_info_ife;
+ phy->ops.check_polarity = e1000_check_polarity_ife;
+ phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
break;
case BME1000_E_PHY_ID:
phy->type = e1000_phy_bm;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
- hw->phy.ops.read_phy_reg = e1000e_read_phy_reg_bm;
- hw->phy.ops.write_phy_reg = e1000e_write_phy_reg_bm;
- hw->phy.ops.commit_phy = e1000e_phy_sw_reset;
+ phy->ops.read_reg = e1000e_read_phy_reg_bm;
+ phy->ops.write_reg = e1000e_write_phy_reg_bm;
+ phy->ops.commit = e1000e_phy_sw_reset;
+ phy->ops.get_info = e1000e_get_phy_info_m88;
+ phy->ops.check_polarity = e1000_check_polarity_m88;
+ phy->ops.force_speed_duplex = e1000e_phy_force_speed_duplex_m88;
break;
default:
return -E1000_ERR_PHY;
break;
}
- phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan;
-
return 0;
}
@@ -374,7 +391,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
/* Can't read flash registers if the register set isn't mapped. */
if (!hw->flash_address) {
- hw_dbg(hw, "ERROR: Flash registers not mapped\n");
+ e_dbg("ERROR: Flash registers not mapped\n");
return -E1000_ERR_CONFIG;
}
@@ -407,7 +424,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw)
/* Clear shadow ram */
for (i = 0; i < nvm->word_size; i++) {
- dev_spec->shadow_ram[i].modified = 0;
+ dev_spec->shadow_ram[i].modified = false;
dev_spec->shadow_ram[i].value = 0xFFFF;
}
@@ -436,7 +453,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter)
if (mac->type == e1000_ich8lan)
mac->rar_entry_count--;
/* Set if manageability features are enabled. */
- mac->arc_subsystem_valid = 1;
+ mac->arc_subsystem_valid = true;
/* LED operations */
switch (mac->type) {
@@ -470,7 +487,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_adapter *adapter)
/* Enable PCS Lock-loss workaround for ICH8 */
if (mac->type == e1000_ich8lan)
- e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw, 1);
+ e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw, true);
return 0;
}
@@ -556,7 +573,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
*/
ret_val = e1000e_config_fc_after_link_up(hw);
if (ret_val)
- hw_dbg(hw, "Error configuring flow control\n");
+ e_dbg("Error configuring flow control\n");
out:
return ret_val;
@@ -636,8 +653,6 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT;
s32 ret_val = 0;
- might_sleep();
-
mutex_lock(&swflag_mutex);
while (timeout) {
@@ -650,7 +665,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
}
if (!timeout) {
- hw_dbg(hw, "SW/FW/HW has locked the resource for too long.\n");
+ e_dbg("SW/FW/HW has locked the resource for too long.\n");
ret_val = -E1000_ERR_CONFIG;
goto out;
}
@@ -670,7 +685,7 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw)
}
if (!timeout) {
- hw_dbg(hw, "Failed to acquire the semaphore.\n");
+ e_dbg("Failed to acquire the semaphore.\n");
extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
ew32(EXTCNF_CTRL, extcnf_ctrl);
ret_val = -E1000_ERR_CONFIG;
@@ -714,7 +729,9 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw)
**/
static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw)
{
- u32 fwsm = er32(FWSM);
+ u32 fwsm;
+
+ fwsm = er32(FWSM);
return (fwsm & E1000_FWSM_MODE_MASK) ==
(E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT);
@@ -738,77 +755,6 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
}
/**
- * e1000_phy_force_speed_duplex_ich8lan - Force PHY speed & duplex
- * @hw: pointer to the HW structure
- *
- * Forces the speed and duplex settings of the PHY.
- * This is a function pointer entry point only called by
- * PHY setup routines.
- **/
-static s32 e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw)
-{
- struct e1000_phy_info *phy = &hw->phy;
- s32 ret_val;
- u16 data;
- bool link;
-
- if (phy->type != e1000_phy_ife) {
- ret_val = e1000e_phy_force_speed_duplex_igp(hw);
- return ret_val;
- }
-
- ret_val = e1e_rphy(hw, PHY_CONTROL, &data);
- if (ret_val)
- return ret_val;
-
- e1000e_phy_force_speed_duplex_setup(hw, &data);
-
- ret_val = e1e_wphy(hw, PHY_CONTROL, data);
- if (ret_val)
- return ret_val;
-
- /* Disable MDI-X support for 10/100 */
- ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data);
- if (ret_val)
- return ret_val;
-
- data &= ~IFE_PMC_AUTO_MDIX;
- data &= ~IFE_PMC_FORCE_MDIX;
-
- ret_val = e1e_wphy(hw, IFE_PHY_MDIX_CONTROL, data);
- if (ret_val)
- return ret_val;
-
- hw_dbg(hw, "IFE PMC: %X\n", data);
-
- udelay(1);
-
- if (phy->autoneg_wait_to_complete) {
- hw_dbg(hw, "Waiting for forced speed/duplex link on IFE phy.\n");
-
- ret_val = e1000e_phy_has_link_generic(hw,
- PHY_FORCE_LIMIT,
- 100000,
- &link);
- if (ret_val)
- return ret_val;
-
- if (!link)
- hw_dbg(hw, "Link taking longer than expected.\n");
-
- /* Try once more */
- ret_val = e1000e_phy_has_link_generic(hw,
- PHY_FORCE_LIMIT,
- 100000,
- &link);
- if (ret_val)
- return ret_val;
- }
-
- return 0;
-}
-
-/**
* e1000_sw_lcd_config_ich8lan - SW-based LCD Configuration
* @hw: pointer to the HW structure
*
@@ -822,7 +768,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
s32 ret_val;
u16 word_addr, reg_data, reg_addr, phy_page = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -918,7 +864,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
reg_addr &= PHY_REG_MASK;
reg_addr |= phy_page;
- ret_val = phy->ops.write_phy_reg_locked(hw,
+ ret_val = phy->ops.write_reg_locked(hw,
(u32)reg_addr,
reg_data);
if (ret_val)
@@ -927,7 +873,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
}
out:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -951,15 +897,14 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
goto out;
/* Wrap the whole flow with the sw flag */
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
goto out;
/* Disable K1 when link is 1Gbps, otherwise use the NVM setting */
if (link) {
if (hw->phy.type == e1000_phy_82578) {
- ret_val = hw->phy.ops.read_phy_reg_locked(hw,
- BM_CS_STATUS,
+ ret_val = hw->phy.ops.read_reg_locked(hw, BM_CS_STATUS,
&status_reg);
if (ret_val)
goto release;
@@ -975,8 +920,7 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
}
if (hw->phy.type == e1000_phy_82577) {
- ret_val = hw->phy.ops.read_phy_reg_locked(hw,
- HV_M_STATUS,
+ ret_val = hw->phy.ops.read_reg_locked(hw, HV_M_STATUS,
&status_reg);
if (ret_val)
goto release;
@@ -992,14 +936,14 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
}
/* Link stall fix for link up */
- ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19),
+ ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19),
0x0100);
if (ret_val)
goto release;
} else {
/* Link stall fix for link down */
- ret_val = hw->phy.ops.write_phy_reg_locked(hw, PHY_REG(770, 19),
+ ret_val = hw->phy.ops.write_reg_locked(hw, PHY_REG(770, 19),
0x4100);
if (ret_val)
goto release;
@@ -1008,7 +952,7 @@ static s32 e1000_k1_gig_workaround_hv(struct e1000_hw *hw, bool link)
ret_val = e1000_configure_k1_ich8lan(hw, k1_enable);
release:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
out:
return ret_val;
}
@@ -1023,7 +967,7 @@ out:
*
* Success returns 0, Failure returns -E1000_ERR_PHY (-2)
**/
-static s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
+s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
{
s32 ret_val = 0;
u32 ctrl_reg = 0;
@@ -1084,7 +1028,7 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
if (hw->mac.type != e1000_pchlan)
return ret_val;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -1098,7 +1042,7 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
mac_reg = er32(PHY_CTRL);
- ret_val = hw->phy.ops.read_phy_reg_locked(hw, HV_OEM_BITS, &oem_reg);
+ ret_val = hw->phy.ops.read_reg_locked(hw, HV_OEM_BITS, &oem_reg);
if (ret_val)
goto out;
@@ -1120,10 +1064,10 @@ static s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
/* Restart auto-neg to activate the bits */
if (!e1000_check_reset_block(hw))
oem_reg |= HV_OEM_BITS_RESTART_AN;
- ret_val = hw->phy.ops.write_phy_reg_locked(hw, HV_OEM_BITS, oem_reg);
+ ret_val = hw->phy.ops.write_reg_locked(hw, HV_OEM_BITS, oem_reg);
out:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -1166,7 +1110,7 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
}
/* Select page 0 */
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -1174,7 +1118,7 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
if (ret_val)
goto out;
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
/*
* Configure the K1 Si workaround during phy reset assuming there is
@@ -1210,7 +1154,7 @@ static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw)
* leave the PHY in a bad state possibly resulting in no link.
*/
if (loop == 0)
- hw_dbg(hw, "LAN_INIT_DONE not set, increase timeout\n");
+ e_dbg("LAN_INIT_DONE not set, increase timeout\n");
/* Clear the Init Done bit for the next init event */
data = er32(STATUS);
@@ -1262,122 +1206,6 @@ out:
}
/**
- * e1000_get_phy_info_ife_ich8lan - Retrieves various IFE PHY states
- * @hw: pointer to the HW structure
- *
- * Populates "phy" structure with various feature states.
- * This function is only called by other family-specific
- * routines.
- **/
-static s32 e1000_get_phy_info_ife_ich8lan(struct e1000_hw *hw)
-{
- struct e1000_phy_info *phy = &hw->phy;
- s32 ret_val;
- u16 data;
- bool link;
-
- ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
- if (ret_val)
- return ret_val;
-
- if (!link) {
- hw_dbg(hw, "Phy info is only valid if link is up\n");
- return -E1000_ERR_CONFIG;
- }
-
- ret_val = e1e_rphy(hw, IFE_PHY_SPECIAL_CONTROL, &data);
- if (ret_val)
- return ret_val;
- phy->polarity_correction = (!(data & IFE_PSC_AUTO_POLARITY_DISABLE));
-
- if (phy->polarity_correction) {
- ret_val = phy->ops.check_polarity(hw);
- if (ret_val)
- return ret_val;
- } else {
- /* Polarity is forced */
- phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY)
- ? e1000_rev_polarity_reversed
- : e1000_rev_polarity_normal;
- }
-
- ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data);
- if (ret_val)
- return ret_val;
-
- phy->is_mdix = (data & IFE_PMC_MDIX_STATUS);
-
- /* The following parameters are undefined for 10/100 operation. */
- phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
- phy->local_rx = e1000_1000t_rx_status_undefined;
- phy->remote_rx = e1000_1000t_rx_status_undefined;
-
- return 0;
-}
-
-/**
- * e1000_get_phy_info_ich8lan - Calls appropriate PHY type get_phy_info
- * @hw: pointer to the HW structure
- *
- * Wrapper for calling the get_phy_info routines for the appropriate phy type.
- * This is a function pointer entry point called by drivers
- * or other shared routines.
- **/
-static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw)
-{
- switch (hw->phy.type) {
- case e1000_phy_ife:
- return e1000_get_phy_info_ife_ich8lan(hw);
- break;
- case e1000_phy_igp_3:
- case e1000_phy_bm:
- case e1000_phy_82578:
- case e1000_phy_82577:
- return e1000e_get_phy_info_igp(hw);
- break;
- default:
- break;
- }
-
- return -E1000_ERR_PHY_TYPE;
-}
-
-/**
- * e1000_check_polarity_ife_ich8lan - Check cable polarity for IFE PHY
- * @hw: pointer to the HW structure
- *
- * Polarity is determined on the polarity reversal feature being enabled.
- * This function is only called by other family-specific
- * routines.
- **/
-static s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw)
-{
- struct e1000_phy_info *phy = &hw->phy;
- s32 ret_val;
- u16 phy_data, offset, mask;
-
- /*
- * Polarity is determined based on the reversal feature being enabled.
- */
- if (phy->polarity_correction) {
- offset = IFE_PHY_EXTENDED_STATUS_CONTROL;
- mask = IFE_PESC_POLARITY_REVERSED;
- } else {
- offset = IFE_PHY_SPECIAL_CONTROL;
- mask = IFE_PSC_FORCE_POLARITY;
- }
-
- ret_val = e1e_rphy(hw, offset, &phy_data);
-
- if (!ret_val)
- phy->cable_polarity = (phy_data & mask)
- ? e1000_rev_polarity_reversed
- : e1000_rev_polarity_normal;
-
- return ret_val;
-}
-
-/**
* e1000_set_lplu_state_pchlan - Set Low Power Link Up state
* @hw: pointer to the HW structure
* @active: true to enable LPLU, false to disable
@@ -1412,7 +1240,7 @@ out:
/**
* e1000_set_d0_lplu_state_ich8lan - Set Low Power Linkup D0 state
* @hw: pointer to the HW structure
- * @active: TRUE to enable LPLU, FALSE to disable
+ * @active: true to enable LPLU, false to disable
*
* Sets the LPLU D0 state according to the active flag. When
* activating LPLU this function also disables smart speed
@@ -1498,7 +1326,7 @@ static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active)
/**
* e1000_set_d3_lplu_state_ich8lan - Set Low Power Linkup D3 state
* @hw: pointer to the HW structure
- * @active: TRUE to enable LPLU, FALSE to disable
+ * @active: true to enable LPLU, false to disable
*
* Sets the LPLU D3 state according to the active flag. When
* activating LPLU this function also disables smart speed
@@ -1611,7 +1439,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
return 0;
}
- hw_dbg(hw, "Unable to determine valid NVM bank via EEC - "
+ e_dbg("Unable to determine valid NVM bank via EEC - "
"reading flash signature\n");
/* fall-thru */
default:
@@ -1641,7 +1469,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
return 0;
}
- hw_dbg(hw, "ERROR: No valid NVM bank present\n");
+ e_dbg("ERROR: No valid NVM bank present\n");
return -E1000_ERR_NVM;
}
@@ -1669,16 +1497,16 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
(words == 0)) {
- hw_dbg(hw, "nvm parameter(s) out of bounds\n");
+ e_dbg("nvm parameter(s) out of bounds\n");
ret_val = -E1000_ERR_NVM;
goto out;
}
- nvm->ops.acquire_nvm(hw);
+ nvm->ops.acquire(hw);
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
if (ret_val) {
- hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n");
+ e_dbg("Could not detect valid bank, assuming bank 0\n");
bank = 0;
}
@@ -1700,11 +1528,11 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
}
}
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
out:
if (ret_val)
- hw_dbg(hw, "NVM read error: %d\n", ret_val);
+ e_dbg("NVM read error: %d\n", ret_val);
return ret_val;
}
@@ -1726,7 +1554,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
/* Check if the flash descriptor is valid */
if (hsfsts.hsf_status.fldesvalid == 0) {
- hw_dbg(hw, "Flash descriptor invalid. "
+ e_dbg("Flash descriptor invalid. "
"SW Sequencing must be used.");
return -E1000_ERR_NVM;
}
@@ -1749,7 +1577,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
if (hsfsts.hsf_status.flcinprog == 0) {
/*
* There is no cycle running at present,
- * so we can start a cycle
+ * so we can start a cycle.
* Begin by setting Flash Cycle Done.
*/
hsfsts.hsf_status.flcdone = 1;
@@ -1757,7 +1585,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
ret_val = 0;
} else {
/*
- * otherwise poll for sometime so the current
+ * Otherwise poll for sometime so the current
* cycle has a chance to end before giving up.
*/
for (i = 0; i < ICH_FLASH_READ_COMMAND_TIMEOUT; i++) {
@@ -1776,7 +1604,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw)
hsfsts.hsf_status.flcdone = 1;
ew16flash(ICH_FLASH_HSFSTS, hsfsts.regval);
} else {
- hw_dbg(hw, "Flash controller busy, cannot get access");
+ e_dbg("Flash controller busy, cannot get access");
}
}
@@ -1926,7 +1754,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
/* Repeat for some time before giving up. */
continue;
} else if (hsfsts.hsf_status.flcdone == 0) {
- hw_dbg(hw, "Timeout error - flash cycle "
+ e_dbg("Timeout error - flash cycle "
"did not complete.");
break;
}
@@ -1954,18 +1782,18 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) ||
(words == 0)) {
- hw_dbg(hw, "nvm parameter(s) out of bounds\n");
+ e_dbg("nvm parameter(s) out of bounds\n");
return -E1000_ERR_NVM;
}
- nvm->ops.acquire_nvm(hw);
+ nvm->ops.acquire(hw);
for (i = 0; i < words; i++) {
- dev_spec->shadow_ram[offset+i].modified = 1;
+ dev_spec->shadow_ram[offset+i].modified = true;
dev_spec->shadow_ram[offset+i].value = data[i];
}
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
return 0;
}
@@ -1996,7 +1824,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
if (nvm->type != e1000_nvm_flash_sw)
goto out;
- nvm->ops.acquire_nvm(hw);
+ nvm->ops.acquire(hw);
/*
* We're writing to the opposite bank so if we're on bank 1,
@@ -2005,7 +1833,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
*/
ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
if (ret_val) {
- hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n");
+ e_dbg("Could not detect valid bank, assuming bank 0\n");
bank = 0;
}
@@ -2014,7 +1842,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
old_bank_offset = 0;
ret_val = e1000_erase_flash_bank_ich8lan(hw, 1);
if (ret_val) {
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
goto out;
}
} else {
@@ -2022,7 +1850,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
new_bank_offset = 0;
ret_val = e1000_erase_flash_bank_ich8lan(hw, 0);
if (ret_val) {
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
goto out;
}
}
@@ -2079,8 +1907,8 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
*/
if (ret_val) {
/* Possibly read-only, see e1000e_write_protect_nvm_ich8lan() */
- hw_dbg(hw, "Flash commit failed.\n");
- nvm->ops.release_nvm(hw);
+ e_dbg("Flash commit failed.\n");
+ nvm->ops.release(hw);
goto out;
}
@@ -2093,7 +1921,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data);
if (ret_val) {
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
goto out;
}
data &= 0xBFFF;
@@ -2101,7 +1929,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
act_offset * 2 + 1,
(u8)(data >> 8));
if (ret_val) {
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
goto out;
}
@@ -2114,17 +1942,17 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
act_offset = (old_bank_offset + E1000_ICH_NVM_SIG_WORD) * 2 + 1;
ret_val = e1000_retry_write_flash_byte_ich8lan(hw, act_offset, 0);
if (ret_val) {
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
goto out;
}
/* Great! Everything worked, we can now clear the cached entries. */
for (i = 0; i < E1000_ICH8_SHADOW_RAM_WORDS; i++) {
- dev_spec->shadow_ram[i].modified = 0;
+ dev_spec->shadow_ram[i].modified = false;
dev_spec->shadow_ram[i].value = 0xFFFF;
}
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
/*
* Reload the EEPROM, or else modifications will not appear
@@ -2135,7 +1963,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
out:
if (ret_val)
- hw_dbg(hw, "NVM update error: %d\n", ret_val);
+ e_dbg("NVM update error: %d\n", ret_val);
return ret_val;
}
@@ -2193,7 +2021,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw)
union ich8_hws_flash_status hsfsts;
u32 gfpreg;
- nvm->ops.acquire_nvm(hw);
+ nvm->ops.acquire(hw);
gfpreg = er32flash(ICH_FLASH_GFPREG);
@@ -2214,7 +2042,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw)
hsfsts.hsf_status.flockdn = true;
ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval);
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
}
/**
@@ -2285,7 +2113,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset,
/* Repeat for some time before giving up. */
continue;
if (hsfsts.hsf_status.flcdone == 0) {
- hw_dbg(hw, "Timeout error - flash cycle "
+ e_dbg("Timeout error - flash cycle "
"did not complete.");
break;
}
@@ -2330,7 +2158,7 @@ static s32 e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
return ret_val;
for (program_retries = 0; program_retries < 100; program_retries++) {
- hw_dbg(hw, "Retrying Byte %2.2X at offset %u\n", byte, offset);
+ e_dbg("Retrying Byte %2.2X at offset %u\n", byte, offset);
udelay(100);
ret_val = e1000_write_flash_byte_ich8lan(hw, offset, byte);
if (!ret_val)
@@ -2360,9 +2188,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank)
u32 flash_bank_size = nvm->flash_bank_size * 2;
s32 ret_val;
s32 count = 0;
- s32 iteration;
- s32 sector_size;
- s32 j;
+ s32 j, iteration, sector_size;
hsfsts.regval = er16flash(ICH_FLASH_HSFSTS);
@@ -2465,7 +2291,7 @@ static s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw, u16 *data)
ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
@@ -2595,10 +2421,10 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
*/
ret_val = e1000e_disable_pcie_master(hw);
if (ret_val) {
- hw_dbg(hw, "PCI-E Master disable polling has failed.\n");
+ e_dbg("PCI-E Master disable polling has failed.\n");
}
- hw_dbg(hw, "Masking off all interrupts\n");
+ e_dbg("Masking off all interrupts\n");
ew32(IMC, 0xffffffff);
/*
@@ -2649,8 +2475,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
ctrl |= E1000_CTRL_PHY_RST;
}
ret_val = e1000_acquire_swflag_ich8lan(hw);
- /* Whether or not the swflag was acquired, we need to reset the part */
- hw_dbg(hw, "Issuing a global reset to ich8lan\n");
+ e_dbg("Issuing a global reset to ich8lan\n");
ew32(CTRL, (ctrl | E1000_CTRL_RST));
msleep(20);
@@ -2670,7 +2495,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
* return with an error. This can happen in situations
* where there is no eeprom and prevents getting link.
*/
- hw_dbg(hw, "Auto Read Done did not complete\n");
+ e_dbg("Auto Read Done did not complete\n");
}
}
/* Dummy read to clear the phy wakeup bit after lcd reset */
@@ -2731,16 +2556,15 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
/* Initialize identification LED */
ret_val = mac->ops.id_led_init(hw);
- if (ret_val) {
- hw_dbg(hw, "Error initializing identification LED\n");
- return ret_val;
- }
+ if (ret_val)
+ e_dbg("Error initializing identification LED\n");
+ /* This is not fatal and we should not stop init due to this */
/* Setup the receive address. */
e1000e_init_rx_addrs(hw, mac->rar_entry_count);
/* Zero out the Multicast HASH table */
- hw_dbg(hw, "Zeroing the MTA\n");
+ e_dbg("Zeroing the MTA\n");
for (i = 0; i < mac->mta_reg_count; i++)
E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
@@ -2750,7 +2574,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
* Reset the phy after disabling host wakeup to reset the Rx buffer.
*/
if (hw->phy.type == e1000_phy_82578) {
- hw->phy.ops.read_phy_reg(hw, BM_WUC, &i);
+ hw->phy.ops.read_reg(hw, BM_WUC, &i);
ret_val = e1000_phy_hw_reset_ich8lan(hw);
if (ret_val)
return ret_val;
@@ -2886,7 +2710,7 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw)
*/
hw->fc.current_mode = hw->fc.requested_mode;
- hw_dbg(hw, "After fix-ups FlowControl is now = %x\n",
+ e_dbg("After fix-ups FlowControl is now = %x\n",
hw->fc.current_mode);
/* Continue to configure the copper link. */
@@ -2897,7 +2721,7 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw)
ew32(FCTTV, hw->fc.pause_time);
if ((hw->phy.type == e1000_phy_82578) ||
(hw->phy.type == e1000_phy_82577)) {
- ret_val = hw->phy.ops.write_phy_reg(hw,
+ ret_val = hw->phy.ops.write_reg(hw,
PHY_REG(BM_PORT_CTRL_PAGE, 27),
hw->fc.pause_time);
if (ret_val)
@@ -2960,7 +2784,7 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
return ret_val;
break;
case e1000_phy_ife:
- ret_val = hw->phy.ops.read_phy_reg(hw, IFE_PHY_MDIX_CONTROL,
+ ret_val = hw->phy.ops.read_reg(hw, IFE_PHY_MDIX_CONTROL,
&reg_data);
if (ret_val)
return ret_val;
@@ -2979,7 +2803,7 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw)
reg_data |= IFE_PMC_AUTO_MDIX;
break;
}
- ret_val = hw->phy.ops.write_phy_reg(hw, IFE_PHY_MDIX_CONTROL,
+ ret_val = hw->phy.ops.write_reg(hw, IFE_PHY_MDIX_CONTROL,
reg_data);
if (ret_val)
return ret_val;
@@ -3092,8 +2916,8 @@ static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw)
* @hw: pointer to the HW structure
* @state: boolean value used to set the current Kumeran workaround state
*
- * If ICH8, set the current Kumeran workaround state (enabled - TRUE
- * /disabled - FALSE).
+ * If ICH8, set the current Kumeran workaround state (enabled - true
+ * /disabled - false).
**/
void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
bool state)
@@ -3101,7 +2925,7 @@ void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
if (hw->mac.type != e1000_ich8lan) {
- hw_dbg(hw, "Workaround applies to ICH8 only.\n");
+ e_dbg("Workaround applies to ICH8 only.\n");
return;
}
@@ -3209,6 +3033,7 @@ void e1000e_disable_gig_wol_ich8lan(struct e1000_hw *hw)
u32 phy_ctrl;
switch (hw->mac.type) {
+ case e1000_ich8lan:
case e1000_ich9lan:
case e1000_ich10lan:
case e1000_pchlan:
@@ -3281,7 +3106,7 @@ static s32 e1000_led_off_ich8lan(struct e1000_hw *hw)
**/
static s32 e1000_setup_led_pchlan(struct e1000_hw *hw)
{
- return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG,
+ return hw->phy.ops.write_reg(hw, HV_LED_CONFIG,
(u16)hw->mac.ledctl_mode1);
}
@@ -3293,7 +3118,7 @@ static s32 e1000_setup_led_pchlan(struct e1000_hw *hw)
**/
static s32 e1000_cleanup_led_pchlan(struct e1000_hw *hw)
{
- return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG,
+ return hw->phy.ops.write_reg(hw, HV_LED_CONFIG,
(u16)hw->mac.ledctl_default);
}
@@ -3325,7 +3150,7 @@ static s32 e1000_led_on_pchlan(struct e1000_hw *hw)
}
}
- return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG, data);
+ return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data);
}
/**
@@ -3356,7 +3181,7 @@ static s32 e1000_led_off_pchlan(struct e1000_hw *hw)
}
}
- return hw->phy.ops.write_phy_reg(hw, HV_LED_CONFIG, data);
+ return hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data);
}
/**
@@ -3379,8 +3204,7 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
if (status & E1000_STATUS_PHYRA)
ew32(STATUS, status & ~E1000_STATUS_PHYRA);
else
- hw_dbg(hw,
- "PHY Reset Asserted not set - needs delay\n");
+ e_dbg("PHY Reset Asserted not set - needs delay\n");
}
e1000e_get_cfg_done(hw);
@@ -3395,7 +3219,7 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
} else {
if (e1000_valid_nvm_bank_detect_ich8lan(hw, &bank)) {
/* Maybe we should do a basic PHY config */
- hw_dbg(hw, "EEPROM not present\n");
+ e_dbg("EEPROM not present\n");
return -E1000_ERR_CONFIG;
}
}
@@ -3404,6 +3228,23 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
}
/**
+ * e1000_power_down_phy_copper_ich8lan - Remove link during PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw)
+{
+ /* If the management interface is not enabled, then power down */
+ if (!(hw->mac.ops.check_mng_mode(hw) ||
+ hw->phy.ops.check_reset_block(hw)))
+ e1000_power_down_phy_copper(hw);
+
+ return;
+}
+
+/**
* e1000_clear_hw_cntrs_ich8lan - Clear statistical counters
* @hw: pointer to the HW structure
*
@@ -3412,42 +3253,41 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
**/
static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw)
{
- u32 temp;
u16 phy_data;
e1000e_clear_hw_cntrs_base(hw);
- temp = er32(ALGNERRC);
- temp = er32(RXERRC);
- temp = er32(TNCRS);
- temp = er32(CEXTERR);
- temp = er32(TSCTC);
- temp = er32(TSCTFC);
+ er32(ALGNERRC);
+ er32(RXERRC);
+ er32(TNCRS);
+ er32(CEXTERR);
+ er32(TSCTC);
+ er32(TSCTFC);
- temp = er32(MGTPRC);
- temp = er32(MGTPDC);
- temp = er32(MGTPTC);
+ er32(MGTPRC);
+ er32(MGTPDC);
+ er32(MGTPTC);
- temp = er32(IAC);
- temp = er32(ICRXOC);
+ er32(IAC);
+ er32(ICRXOC);
/* Clear PHY statistics registers */
if ((hw->phy.type == e1000_phy_82578) ||
(hw->phy.type == e1000_phy_82577)) {
- hw->phy.ops.read_phy_reg(hw, HV_SCC_UPPER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_SCC_LOWER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_ECOL_UPPER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_ECOL_LOWER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_MCC_UPPER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_MCC_LOWER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_LATECOL_UPPER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_LATECOL_LOWER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_COLC_UPPER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_COLC_LOWER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_DC_UPPER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_DC_LOWER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_TNCRS_UPPER, &phy_data);
- hw->phy.ops.read_phy_reg(hw, HV_TNCRS_LOWER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_SCC_UPPER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_SCC_LOWER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_ECOL_UPPER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_ECOL_LOWER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_MCC_UPPER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_MCC_LOWER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_LATECOL_UPPER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_LATECOL_LOWER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_COLC_UPPER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_COLC_LOWER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_DC_UPPER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_DC_LOWER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_TNCRS_UPPER, &phy_data);
+ hw->phy.ops.read_reg(hw, HV_TNCRS_LOWER, &phy_data);
}
}
@@ -3470,29 +3310,27 @@ static struct e1000_mac_operations ich8_mac_ops = {
};
static struct e1000_phy_operations ich8_phy_ops = {
- .acquire_phy = e1000_acquire_swflag_ich8lan,
+ .acquire = e1000_acquire_swflag_ich8lan,
.check_reset_block = e1000_check_reset_block_ich8lan,
- .commit_phy = NULL,
- .force_speed_duplex = e1000_phy_force_speed_duplex_ich8lan,
+ .commit = NULL,
.get_cfg_done = e1000_get_cfg_done_ich8lan,
.get_cable_length = e1000e_get_cable_length_igp_2,
- .get_phy_info = e1000_get_phy_info_ich8lan,
- .read_phy_reg = e1000e_read_phy_reg_igp,
- .release_phy = e1000_release_swflag_ich8lan,
- .reset_phy = e1000_phy_hw_reset_ich8lan,
+ .read_reg = e1000e_read_phy_reg_igp,
+ .release = e1000_release_swflag_ich8lan,
+ .reset = e1000_phy_hw_reset_ich8lan,
.set_d0_lplu_state = e1000_set_d0_lplu_state_ich8lan,
.set_d3_lplu_state = e1000_set_d3_lplu_state_ich8lan,
- .write_phy_reg = e1000e_write_phy_reg_igp,
+ .write_reg = e1000e_write_phy_reg_igp,
};
static struct e1000_nvm_operations ich8_nvm_ops = {
- .acquire_nvm = e1000_acquire_nvm_ich8lan,
- .read_nvm = e1000_read_nvm_ich8lan,
- .release_nvm = e1000_release_nvm_ich8lan,
- .update_nvm = e1000_update_nvm_checksum_ich8lan,
+ .acquire = e1000_acquire_nvm_ich8lan,
+ .read = e1000_read_nvm_ich8lan,
+ .release = e1000_release_nvm_ich8lan,
+ .update = e1000_update_nvm_checksum_ich8lan,
.valid_led_default = e1000_valid_led_default_ich8lan,
- .validate_nvm = e1000_validate_nvm_checksum_ich8lan,
- .write_nvm = e1000_write_nvm_ich8lan,
+ .validate = e1000_validate_nvm_checksum_ich8lan,
+ .write = e1000_write_nvm_ich8lan,
};
struct e1000_info e1000_ich8_info = {
diff --git a/drivers/net/e1000e/lib.c b/drivers/net/e1000e/lib.c
index 99ba2b8a2a0..a86c17548c1 100644
--- a/drivers/net/e1000e/lib.c
+++ b/drivers/net/e1000e/lib.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -26,11 +26,6 @@
*******************************************************************************/
-#include <linux/netdevice.h>
-#include <linux/ethtool.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-
#include "e1000.h"
enum e1000_mng_mode {
@@ -87,7 +82,24 @@ s32 e1000e_get_bus_info_pcie(struct e1000_hw *hw)
}
/**
- * e1000e_write_vfta - Write value to VLAN filter table
+ * e1000_clear_vfta_generic - Clear VLAN filter table
+ * @hw: pointer to the HW structure
+ *
+ * Clears the register array which contains the VLAN filter table by
+ * setting all the values to 0.
+ **/
+void e1000_clear_vfta_generic(struct e1000_hw *hw)
+{
+ u32 offset;
+
+ for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
+ E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, 0);
+ e1e_flush();
+ }
+}
+
+/**
+ * e1000_write_vfta_generic - Write value to VLAN filter table
* @hw: pointer to the HW structure
* @offset: register offset in VLAN filter table
* @value: register value written to VLAN filter table
@@ -95,7 +107,7 @@ s32 e1000e_get_bus_info_pcie(struct e1000_hw *hw)
* Writes value at the given offset in the register array which stores
* the VLAN filter table.
**/
-void e1000e_write_vfta(struct e1000_hw *hw, u32 offset, u32 value)
+void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value)
{
E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value);
e1e_flush();
@@ -115,12 +127,12 @@ void e1000e_init_rx_addrs(struct e1000_hw *hw, u16 rar_count)
u32 i;
/* Setup the receive address */
- hw_dbg(hw, "Programming MAC Address into RAR[0]\n");
+ e_dbg("Programming MAC Address into RAR[0]\n");
e1000e_rar_set(hw, hw->mac.addr, 0);
/* Zero out the other (rar_entry_count - 1) receive addresses */
- hw_dbg(hw, "Clearing RAR[1-%u]\n", rar_count-1);
+ e_dbg("Clearing RAR[1-%u]\n", rar_count-1);
for (i = 1; i < rar_count; i++) {
E1000_WRITE_REG_ARRAY(hw, E1000_RA, (i << 1), 0);
e1e_flush();
@@ -276,7 +288,7 @@ void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw,
for (; mc_addr_count > 0; mc_addr_count--) {
u32 hash_value, hash_reg, hash_bit, mta;
hash_value = e1000_hash_mc_addr(hw, mc_addr_list);
- hw_dbg(hw, "Hash value = 0x%03X\n", hash_value);
+ e_dbg("Hash value = 0x%03X\n", hash_value);
hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1);
hash_bit = hash_value & 0x1F;
mta = (1 << hash_bit);
@@ -300,45 +312,43 @@ void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw,
**/
void e1000e_clear_hw_cntrs_base(struct e1000_hw *hw)
{
- u32 temp;
-
- temp = er32(CRCERRS);
- temp = er32(SYMERRS);
- temp = er32(MPC);
- temp = er32(SCC);
- temp = er32(ECOL);
- temp = er32(MCC);
- temp = er32(LATECOL);
- temp = er32(COLC);
- temp = er32(DC);
- temp = er32(SEC);
- temp = er32(RLEC);
- temp = er32(XONRXC);
- temp = er32(XONTXC);
- temp = er32(XOFFRXC);
- temp = er32(XOFFTXC);
- temp = er32(FCRUC);
- temp = er32(GPRC);
- temp = er32(BPRC);
- temp = er32(MPRC);
- temp = er32(GPTC);
- temp = er32(GORCL);
- temp = er32(GORCH);
- temp = er32(GOTCL);
- temp = er32(GOTCH);
- temp = er32(RNBC);
- temp = er32(RUC);
- temp = er32(RFC);
- temp = er32(ROC);
- temp = er32(RJC);
- temp = er32(TORL);
- temp = er32(TORH);
- temp = er32(TOTL);
- temp = er32(TOTH);
- temp = er32(TPR);
- temp = er32(TPT);
- temp = er32(MPTC);
- temp = er32(BPTC);
+ er32(CRCERRS);
+ er32(SYMERRS);
+ er32(MPC);
+ er32(SCC);
+ er32(ECOL);
+ er32(MCC);
+ er32(LATECOL);
+ er32(COLC);
+ er32(DC);
+ er32(SEC);
+ er32(RLEC);
+ er32(XONRXC);
+ er32(XONTXC);
+ er32(XOFFRXC);
+ er32(XOFFTXC);
+ er32(FCRUC);
+ er32(GPRC);
+ er32(BPRC);
+ er32(MPRC);
+ er32(GPTC);
+ er32(GORCL);
+ er32(GORCH);
+ er32(GOTCL);
+ er32(GOTCH);
+ er32(RNBC);
+ er32(RUC);
+ er32(RFC);
+ er32(ROC);
+ er32(RJC);
+ er32(TORL);
+ er32(TORH);
+ er32(TOTL);
+ er32(TOTH);
+ er32(TPR);
+ er32(TPT);
+ er32(MPTC);
+ er32(BPTC);
}
/**
@@ -376,7 +386,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw)
if (!link)
return ret_val; /* No link detected */
- mac->get_link_status = 0;
+ mac->get_link_status = false;
/*
* Check if there was DownShift, must be checked
@@ -408,7 +418,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw)
*/
ret_val = e1000e_config_fc_after_link_up(hw);
if (ret_val) {
- hw_dbg(hw, "Error configuring flow control\n");
+ e_dbg("Error configuring flow control\n");
}
return ret_val;
@@ -448,7 +458,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw)
mac->autoneg_failed = 1;
return 0;
}
- hw_dbg(hw, "NOT RXing /C/, disable AutoNeg and force link.\n");
+ e_dbg("NOT RXing /C/, disable AutoNeg and force link.\n");
/* Disable auto-negotiation in the TXCW register */
ew32(TXCW, (mac->txcw & ~E1000_TXCW_ANE));
@@ -461,7 +471,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw)
/* Configure Flow Control after forcing link up. */
ret_val = e1000e_config_fc_after_link_up(hw);
if (ret_val) {
- hw_dbg(hw, "Error configuring flow control\n");
+ e_dbg("Error configuring flow control\n");
return ret_val;
}
} else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) {
@@ -471,7 +481,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw)
* and disable forced link in the Device Control register
* in an attempt to auto-negotiate with our link partner.
*/
- hw_dbg(hw, "RXing /C/, enable AutoNeg and stop forcing link.\n");
+ e_dbg("RXing /C/, enable AutoNeg and stop forcing link.\n");
ew32(TXCW, mac->txcw);
ew32(CTRL, (ctrl & ~E1000_CTRL_SLU));
@@ -513,7 +523,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
mac->autoneg_failed = 1;
return 0;
}
- hw_dbg(hw, "NOT RXing /C/, disable AutoNeg and force link.\n");
+ e_dbg("NOT RXing /C/, disable AutoNeg and force link.\n");
/* Disable auto-negotiation in the TXCW register */
ew32(TXCW, (mac->txcw & ~E1000_TXCW_ANE));
@@ -526,7 +536,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
/* Configure Flow Control after forcing link up. */
ret_val = e1000e_config_fc_after_link_up(hw);
if (ret_val) {
- hw_dbg(hw, "Error configuring flow control\n");
+ e_dbg("Error configuring flow control\n");
return ret_val;
}
} else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) {
@@ -536,7 +546,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
* and disable forced link in the Device Control register
* in an attempt to auto-negotiate with our link partner.
*/
- hw_dbg(hw, "RXing /C/, enable AutoNeg and stop forcing link.\n");
+ e_dbg("RXing /C/, enable AutoNeg and stop forcing link.\n");
ew32(TXCW, mac->txcw);
ew32(CTRL, (ctrl & ~E1000_CTRL_SLU));
@@ -553,11 +563,11 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
if (rxcw & E1000_RXCW_SYNCH) {
if (!(rxcw & E1000_RXCW_IV)) {
mac->serdes_has_link = true;
- hw_dbg(hw, "SERDES: Link up - forced.\n");
+ e_dbg("SERDES: Link up - forced.\n");
}
} else {
mac->serdes_has_link = false;
- hw_dbg(hw, "SERDES: Link down - force failed.\n");
+ e_dbg("SERDES: Link down - force failed.\n");
}
}
@@ -570,20 +580,20 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw)
if (rxcw & E1000_RXCW_SYNCH) {
if (!(rxcw & E1000_RXCW_IV)) {
mac->serdes_has_link = true;
- hw_dbg(hw, "SERDES: Link up - autoneg "
+ e_dbg("SERDES: Link up - autoneg "
"completed sucessfully.\n");
} else {
mac->serdes_has_link = false;
- hw_dbg(hw, "SERDES: Link down - invalid"
+ e_dbg("SERDES: Link down - invalid"
"codewords detected in autoneg.\n");
}
} else {
mac->serdes_has_link = false;
- hw_dbg(hw, "SERDES: Link down - no sync.\n");
+ e_dbg("SERDES: Link down - no sync.\n");
}
} else {
mac->serdes_has_link = false;
- hw_dbg(hw, "SERDES: Link down - autoneg failed\n");
+ e_dbg("SERDES: Link down - autoneg failed\n");
}
}
@@ -614,7 +624,7 @@ static s32 e1000_set_default_fc_generic(struct e1000_hw *hw)
ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &nvm_data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
@@ -667,7 +677,7 @@ s32 e1000e_setup_link(struct e1000_hw *hw)
*/
hw->fc.current_mode = hw->fc.requested_mode;
- hw_dbg(hw, "After fix-ups FlowControl is now = %x\n",
+ e_dbg("After fix-ups FlowControl is now = %x\n",
hw->fc.current_mode);
/* Call the necessary media_type subroutine to configure the link. */
@@ -681,7 +691,7 @@ s32 e1000e_setup_link(struct e1000_hw *hw)
* control is disabled, because it does not hurt anything to
* initialize these registers.
*/
- hw_dbg(hw, "Initializing the Flow Control address, type and timer regs\n");
+ e_dbg("Initializing the Flow Control address, type and timer regs\n");
ew32(FCT, FLOW_CONTROL_TYPE);
ew32(FCAH, FLOW_CONTROL_ADDRESS_HIGH);
ew32(FCAL, FLOW_CONTROL_ADDRESS_LOW);
@@ -751,7 +761,7 @@ static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw)
txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
break;
default:
- hw_dbg(hw, "Flow control param set incorrectly\n");
+ e_dbg("Flow control param set incorrectly\n");
return -E1000_ERR_CONFIG;
break;
}
@@ -789,7 +799,7 @@ static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw)
break;
}
if (i == FIBER_LINK_UP_LIMIT) {
- hw_dbg(hw, "Never got a valid link from auto-neg!!!\n");
+ e_dbg("Never got a valid link from auto-neg!!!\n");
mac->autoneg_failed = 1;
/*
* AutoNeg failed to achieve a link, so we'll call
@@ -799,13 +809,13 @@ static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw)
*/
ret_val = mac->ops.check_for_link(hw);
if (ret_val) {
- hw_dbg(hw, "Error while checking for link\n");
+ e_dbg("Error while checking for link\n");
return ret_val;
}
mac->autoneg_failed = 0;
} else {
mac->autoneg_failed = 0;
- hw_dbg(hw, "Valid Link Found\n");
+ e_dbg("Valid Link Found\n");
}
return 0;
@@ -841,7 +851,7 @@ s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw)
* then the link-up status bit will be set and the flow control enable
* bits (RFCE and TFCE) will be set according to their negotiated value.
*/
- hw_dbg(hw, "Auto-negotiation enabled\n");
+ e_dbg("Auto-negotiation enabled\n");
ew32(CTRL, ctrl);
e1e_flush();
@@ -856,7 +866,7 @@ s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw)
(er32(CTRL) & E1000_CTRL_SWDPIN1)) {
ret_val = e1000_poll_fiber_serdes_link_generic(hw);
} else {
- hw_dbg(hw, "No signal detected\n");
+ e_dbg("No signal detected\n");
}
return 0;
@@ -952,7 +962,7 @@ s32 e1000e_force_mac_fc(struct e1000_hw *hw)
* 3: Both Rx and Tx flow control (symmetric) is enabled.
* other: No other values should be possible at this point.
*/
- hw_dbg(hw, "hw->fc.current_mode = %u\n", hw->fc.current_mode);
+ e_dbg("hw->fc.current_mode = %u\n", hw->fc.current_mode);
switch (hw->fc.current_mode) {
case e1000_fc_none:
@@ -970,7 +980,7 @@ s32 e1000e_force_mac_fc(struct e1000_hw *hw)
ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE);
break;
default:
- hw_dbg(hw, "Flow control param set incorrectly\n");
+ e_dbg("Flow control param set incorrectly\n");
return -E1000_ERR_CONFIG;
}
@@ -1011,7 +1021,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
}
if (ret_val) {
- hw_dbg(hw, "Error forcing flow control settings\n");
+ e_dbg("Error forcing flow control settings\n");
return ret_val;
}
@@ -1035,7 +1045,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
return ret_val;
if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) {
- hw_dbg(hw, "Copper PHY and Auto Neg "
+ e_dbg("Copper PHY and Auto Neg "
"has not completed.\n");
return ret_val;
}
@@ -1076,7 +1086,6 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
* 1 | 1 | 0 | 0 | e1000_fc_none
* 1 | 1 | 0 | 1 | e1000_fc_rx_pause
*
- *
* Are both PAUSE bits set to 1? If so, this implies
* Symmetric Flow Control is enabled at both ends. The
* ASM_DIR bits are irrelevant per the spec.
@@ -1100,10 +1109,10 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
*/
if (hw->fc.requested_mode == e1000_fc_full) {
hw->fc.current_mode = e1000_fc_full;
- hw_dbg(hw, "Flow Control = FULL.\r\n");
+ e_dbg("Flow Control = FULL.\r\n");
} else {
hw->fc.current_mode = e1000_fc_rx_pause;
- hw_dbg(hw, "Flow Control = "
+ e_dbg("Flow Control = "
"RX PAUSE frames only.\r\n");
}
}
@@ -1114,14 +1123,13 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
*-------|---------|-------|---------|--------------------
* 0 | 1 | 1 | 1 | e1000_fc_tx_pause
- *
*/
else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc.current_mode = e1000_fc_tx_pause;
- hw_dbg(hw, "Flow Control = Tx PAUSE frames only.\r\n");
+ e_dbg("Flow Control = Tx PAUSE frames only.\r\n");
}
/*
* For transmitting PAUSE frames ONLY.
@@ -1130,21 +1138,20 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
*-------|---------|-------|---------|--------------------
* 1 | 1 | 0 | 1 | e1000_fc_rx_pause
- *
*/
else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
!(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc.current_mode = e1000_fc_rx_pause;
- hw_dbg(hw, "Flow Control = Rx PAUSE frames only.\r\n");
+ e_dbg("Flow Control = Rx PAUSE frames only.\r\n");
} else {
/*
* Per the IEEE spec, at this point flow control
* should be disabled.
*/
hw->fc.current_mode = e1000_fc_none;
- hw_dbg(hw, "Flow Control = NONE.\r\n");
+ e_dbg("Flow Control = NONE.\r\n");
}
/*
@@ -1154,7 +1161,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
*/
ret_val = mac->ops.get_link_up_info(hw, &speed, &duplex);
if (ret_val) {
- hw_dbg(hw, "Error getting link speed and duplex\n");
+ e_dbg("Error getting link speed and duplex\n");
return ret_val;
}
@@ -1167,7 +1174,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw)
*/
ret_val = e1000e_force_mac_fc(hw);
if (ret_val) {
- hw_dbg(hw, "Error forcing flow control settings\n");
+ e_dbg("Error forcing flow control settings\n");
return ret_val;
}
}
@@ -1191,21 +1198,21 @@ s32 e1000e_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed, u16 *dup
status = er32(STATUS);
if (status & E1000_STATUS_SPEED_1000) {
*speed = SPEED_1000;
- hw_dbg(hw, "1000 Mbs, ");
+ e_dbg("1000 Mbs, ");
} else if (status & E1000_STATUS_SPEED_100) {
*speed = SPEED_100;
- hw_dbg(hw, "100 Mbs, ");
+ e_dbg("100 Mbs, ");
} else {
*speed = SPEED_10;
- hw_dbg(hw, "10 Mbs, ");
+ e_dbg("10 Mbs, ");
}
if (status & E1000_STATUS_FD) {
*duplex = FULL_DUPLEX;
- hw_dbg(hw, "Full Duplex\n");
+ e_dbg("Full Duplex\n");
} else {
*duplex = HALF_DUPLEX;
- hw_dbg(hw, "Half Duplex\n");
+ e_dbg("Half Duplex\n");
}
return 0;
@@ -1251,7 +1258,7 @@ s32 e1000e_get_hw_semaphore(struct e1000_hw *hw)
}
if (i == timeout) {
- hw_dbg(hw, "Driver can't access device - SMBI bit is set.\n");
+ e_dbg("Driver can't access device - SMBI bit is set.\n");
return -E1000_ERR_NVM;
}
@@ -1270,7 +1277,7 @@ s32 e1000e_get_hw_semaphore(struct e1000_hw *hw)
if (i == timeout) {
/* Release semaphores */
e1000e_put_hw_semaphore(hw);
- hw_dbg(hw, "Driver can't access the NVM\n");
+ e_dbg("Driver can't access the NVM\n");
return -E1000_ERR_NVM;
}
@@ -1310,7 +1317,7 @@ s32 e1000e_get_auto_rd_done(struct e1000_hw *hw)
}
if (i == AUTO_READ_DONE_TIMEOUT) {
- hw_dbg(hw, "Auto read by HW from NVM has not completed.\n");
+ e_dbg("Auto read by HW from NVM has not completed.\n");
return -E1000_ERR_RESET;
}
@@ -1331,7 +1338,7 @@ s32 e1000e_valid_led_default(struct e1000_hw *hw, u16 *data)
ret_val = e1000_read_nvm(hw, NVM_ID_LED_SETTINGS, 1, data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
@@ -1585,7 +1592,7 @@ s32 e1000e_disable_pcie_master(struct e1000_hw *hw)
}
if (!timeout) {
- hw_dbg(hw, "Master requests are pending.\n");
+ e_dbg("Master requests are pending.\n");
return -E1000_ERR_MASTER_REQUESTS_PENDING;
}
@@ -1608,7 +1615,7 @@ void e1000e_reset_adaptive(struct e1000_hw *hw)
mac->ifs_step_size = IFS_STEP;
mac->ifs_ratio = IFS_RATIO;
- mac->in_ifs_mode = 0;
+ mac->in_ifs_mode = false;
ew32(AIT, 0);
}
@@ -1625,7 +1632,7 @@ void e1000e_update_adaptive(struct e1000_hw *hw)
if ((mac->collision_delta * mac->ifs_ratio) > mac->tx_packet_delta) {
if (mac->tx_packet_delta > MIN_NUM_XMITS) {
- mac->in_ifs_mode = 1;
+ mac->in_ifs_mode = true;
if (mac->current_ifs_val < mac->ifs_max_val) {
if (!mac->current_ifs_val)
mac->current_ifs_val = mac->ifs_min_val;
@@ -1639,7 +1646,7 @@ void e1000e_update_adaptive(struct e1000_hw *hw)
if (mac->in_ifs_mode &&
(mac->tx_packet_delta <= MIN_NUM_XMITS)) {
mac->current_ifs_val = 0;
- mac->in_ifs_mode = 0;
+ mac->in_ifs_mode = false;
ew32(AIT, 0);
}
}
@@ -1809,7 +1816,7 @@ s32 e1000e_acquire_nvm(struct e1000_hw *hw)
if (!timeout) {
eecd &= ~E1000_EECD_REQ;
ew32(EECD, eecd);
- hw_dbg(hw, "Could not acquire NVM grant\n");
+ e_dbg("Could not acquire NVM grant\n");
return -E1000_ERR_NVM;
}
@@ -1914,7 +1921,7 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw)
}
if (!timeout) {
- hw_dbg(hw, "SPI NVM Status error\n");
+ e_dbg("SPI NVM Status error\n");
return -E1000_ERR_NVM;
}
}
@@ -1943,7 +1950,7 @@ s32 e1000e_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
*/
if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
(words == 0)) {
- hw_dbg(hw, "nvm parameter(s) out of bounds\n");
+ e_dbg("nvm parameter(s) out of bounds\n");
return -E1000_ERR_NVM;
}
@@ -1986,11 +1993,11 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
*/
if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
(words == 0)) {
- hw_dbg(hw, "nvm parameter(s) out of bounds\n");
+ e_dbg("nvm parameter(s) out of bounds\n");
return -E1000_ERR_NVM;
}
- ret_val = nvm->ops.acquire_nvm(hw);
+ ret_val = nvm->ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -2001,7 +2008,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
ret_val = e1000_ready_nvm_eeprom(hw);
if (ret_val) {
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
return ret_val;
}
@@ -2040,7 +2047,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
}
msleep(10);
- nvm->ops.release_nvm(hw);
+ nvm->ops.release(hw);
return 0;
}
@@ -2066,7 +2073,7 @@ s32 e1000e_read_mac_addr(struct e1000_hw *hw)
ret_val = e1000_read_nvm(hw, NVM_ALT_MAC_ADDR_PTR, 1,
&mac_addr_offset);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
if (mac_addr_offset == 0xFFFF)
@@ -2081,7 +2088,7 @@ s32 e1000e_read_mac_addr(struct e1000_hw *hw)
ret_val = e1000_read_nvm(hw, mac_addr_offset, 1,
&nvm_data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
if (nvm_data & 0x0001)
@@ -2096,7 +2103,7 @@ s32 e1000e_read_mac_addr(struct e1000_hw *hw)
offset = mac_addr_offset + (i >> 1);
ret_val = e1000_read_nvm(hw, offset, 1, &nvm_data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
@@ -2129,14 +2136,14 @@ s32 e1000e_validate_nvm_checksum_generic(struct e1000_hw *hw)
for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
ret_val = e1000_read_nvm(hw, i, 1, &nvm_data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
checksum += nvm_data;
}
if (checksum != (u16) NVM_SUM) {
- hw_dbg(hw, "NVM Checksum Invalid\n");
+ e_dbg("NVM Checksum Invalid\n");
return -E1000_ERR_NVM;
}
@@ -2160,7 +2167,7 @@ s32 e1000e_update_nvm_checksum_generic(struct e1000_hw *hw)
for (i = 0; i < NVM_CHECKSUM_REG; i++) {
ret_val = e1000_read_nvm(hw, i, 1, &nvm_data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error while updating checksum.\n");
+ e_dbg("NVM Read Error while updating checksum.\n");
return ret_val;
}
checksum += nvm_data;
@@ -2168,7 +2175,7 @@ s32 e1000e_update_nvm_checksum_generic(struct e1000_hw *hw)
checksum = (u16) NVM_SUM - checksum;
ret_val = e1000_write_nvm(hw, NVM_CHECKSUM_REG, 1, &checksum);
if (ret_val)
- hw_dbg(hw, "NVM Write Error while updating checksum.\n");
+ e_dbg("NVM Write Error while updating checksum.\n");
return ret_val;
}
@@ -2231,7 +2238,7 @@ static s32 e1000_mng_enable_host_if(struct e1000_hw *hw)
/* Check that the host interface is enabled. */
hicr = er32(HICR);
if ((hicr & E1000_HICR_EN) == 0) {
- hw_dbg(hw, "E1000_HOST_EN bit disabled.\n");
+ e_dbg("E1000_HOST_EN bit disabled.\n");
return -E1000_ERR_HOST_INTERFACE_COMMAND;
}
/* check the previous command is completed */
@@ -2243,7 +2250,7 @@ static s32 e1000_mng_enable_host_if(struct e1000_hw *hw)
}
if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) {
- hw_dbg(hw, "Previous command timeout failed .\n");
+ e_dbg("Previous command timeout failed .\n");
return -E1000_ERR_HOST_INTERFACE_COMMAND;
}
@@ -2282,7 +2289,7 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw)
/* No manageability, no filtering */
if (!e1000e_check_mng_mode(hw)) {
- hw->mac.tx_pkt_filtering = 0;
+ hw->mac.tx_pkt_filtering = false;
return 0;
}
@@ -2292,7 +2299,7 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw)
*/
ret_val = e1000_mng_enable_host_if(hw);
if (ret_val != 0) {
- hw->mac.tx_pkt_filtering = 0;
+ hw->mac.tx_pkt_filtering = false;
return ret_val;
}
@@ -2311,17 +2318,17 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw)
* take the safe route of assuming Tx filtering is enabled.
*/
if ((hdr_csum != csum) || (hdr->signature != E1000_IAMT_SIGNATURE)) {
- hw->mac.tx_pkt_filtering = 1;
+ hw->mac.tx_pkt_filtering = true;
return 1;
}
/* Cookie area is valid, make the final check for filtering. */
if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING)) {
- hw->mac.tx_pkt_filtering = 0;
+ hw->mac.tx_pkt_filtering = false;
return 0;
}
- hw->mac.tx_pkt_filtering = 1;
+ hw->mac.tx_pkt_filtering = true;
return 1;
}
@@ -2353,7 +2360,7 @@ static s32 e1000_mng_write_cmd_header(struct e1000_hw *hw,
}
/**
- * e1000_mng_host_if_write - Writes to the manageability host interface
+ * e1000_mng_host_if_write - Write to the manageability host interface
* @hw: pointer to the HW structure
* @buffer: pointer to the host interface buffer
* @length: size of the buffer
@@ -2478,7 +2485,7 @@ bool e1000e_enable_mng_pass_thru(struct e1000_hw *hw)
{
u32 manc;
u32 fwsm, factps;
- bool ret_val = 0;
+ bool ret_val = false;
manc = er32(MANC);
@@ -2493,13 +2500,13 @@ bool e1000e_enable_mng_pass_thru(struct e1000_hw *hw)
if (!(factps & E1000_FACTPS_MNGCG) &&
((fwsm & E1000_FWSM_MODE_MASK) ==
(e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT))) {
- ret_val = 1;
+ ret_val = true;
return ret_val;
}
} else {
if ((manc & E1000_MANC_SMBUS_EN) &&
!(manc & E1000_MANC_ASF_EN)) {
- ret_val = 1;
+ ret_val = true;
return ret_val;
}
}
@@ -2514,14 +2521,14 @@ s32 e1000e_read_pba_num(struct e1000_hw *hw, u32 *pba_num)
ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_0, 1, &nvm_data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
*pba_num = (u32)(nvm_data << 16);
ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_1, 1, &nvm_data);
if (ret_val) {
- hw_dbg(hw, "NVM Read Error\n");
+ e_dbg("NVM Read Error\n");
return ret_val;
}
*pba_num |= nvm_data;
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index fad8f9ea004..c3105c5087e 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -65,17 +65,6 @@ static const struct e1000_info *e1000_info_tbl[] = {
[board_pchlan] = &e1000_pch_info,
};
-#ifdef DEBUG
-/**
- * e1000_get_hw_dev_name - return device name string
- * used by hardware layer to print debugging information
- **/
-char *e1000e_get_hw_dev_name(struct e1000_hw *hw)
-{
- return hw->adapter->netdev->name;
-}
-#endif
-
/**
* e1000_desc_unused - calculate if we have unused descriptors
**/
@@ -167,7 +156,7 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info;
struct sk_buff *skb;
unsigned int i;
- unsigned int bufsz = adapter->rx_buffer_len + NET_IP_ALIGN;
+ unsigned int bufsz = adapter->rx_buffer_len;
i = rx_ring->next_to_use;
buffer_info = &rx_ring->buffer_info[i];
@@ -179,20 +168,13 @@ static void e1000_alloc_rx_buffers(struct e1000_adapter *adapter,
goto map_skb;
}
- skb = netdev_alloc_skb(netdev, bufsz);
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
if (!skb) {
/* Better luck next round */
adapter->alloc_rx_buff_failed++;
break;
}
- /*
- * Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
map_skb:
buffer_info->dma = pci_map_single(pdev, skb->data,
@@ -284,21 +266,14 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter,
cpu_to_le64(ps_page->dma);
}
- skb = netdev_alloc_skb(netdev,
- adapter->rx_ps_bsize0 + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(netdev,
+ adapter->rx_ps_bsize0);
if (!skb) {
adapter->alloc_rx_buff_failed++;
break;
}
- /*
- * Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
buffer_info->dma = pci_map_single(pdev, skb->data,
adapter->rx_ps_bsize0,
@@ -359,9 +334,7 @@ static void e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info;
struct sk_buff *skb;
unsigned int i;
- unsigned int bufsz = 256 -
- 16 /* for skb_reserve */ -
- NET_IP_ALIGN;
+ unsigned int bufsz = 256 - 16 /* for skb_reserve */;
i = rx_ring->next_to_use;
buffer_info = &rx_ring->buffer_info[i];
@@ -373,19 +346,13 @@ static void e1000_alloc_jumbo_rx_buffers(struct e1000_adapter *adapter,
goto check_page;
}
- skb = netdev_alloc_skb(netdev, bufsz);
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
if (unlikely(!skb)) {
/* Better luck next round */
adapter->alloc_rx_buff_failed++;
break;
}
- /* Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
check_page:
/* allocate a new page if necessary */
@@ -437,6 +404,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
{
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
+ struct e1000_hw *hw = &adapter->hw;
struct e1000_ring *rx_ring = adapter->rx_ring;
struct e1000_rx_desc *rx_desc, *next_rxd;
struct e1000_buffer *buffer_info, *next_buffer;
@@ -486,8 +454,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
* packet, also make sure the frame isn't just CRC only */
if (!(status & E1000_RXD_STAT_EOP) || (length <= 4)) {
/* All receives must fit into a single buffer */
- e_dbg("%s: Receive packet consumed multiple buffers\n",
- netdev->name);
+ e_dbg("Receive packet consumed multiple buffers\n");
/* recycle */
buffer_info->skb = skb;
goto next_desc;
@@ -513,9 +480,8 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
*/
if (length < copybreak) {
struct sk_buff *new_skb =
- netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
+ netdev_alloc_skb_ip_align(netdev, length);
if (new_skb) {
- skb_reserve(new_skb, NET_IP_ALIGN);
skb_copy_to_linear_data_offset(new_skb,
-NET_IP_ALIGN,
(skb->data -
@@ -560,33 +526,52 @@ next_desc:
adapter->total_rx_bytes += total_rx_bytes;
adapter->total_rx_packets += total_rx_packets;
- adapter->net_stats.rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_packets += total_rx_packets;
+ netdev->stats.rx_bytes += total_rx_bytes;
+ netdev->stats.rx_packets += total_rx_packets;
return cleaned;
}
static void e1000_put_txbuf(struct e1000_adapter *adapter,
struct e1000_buffer *buffer_info)
{
- buffer_info->dma = 0;
+ if (buffer_info->dma) {
+ if (buffer_info->mapped_as_page)
+ pci_unmap_page(adapter->pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ else
+ pci_unmap_single(adapter->pdev, buffer_info->dma,
+ buffer_info->length,
+ PCI_DMA_TODEVICE);
+ buffer_info->dma = 0;
+ }
if (buffer_info->skb) {
- skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb,
- DMA_TO_DEVICE);
dev_kfree_skb_any(buffer_info->skb);
buffer_info->skb = NULL;
}
buffer_info->time_stamp = 0;
}
-static void e1000_print_tx_hang(struct e1000_adapter *adapter)
+static void e1000_print_hw_hang(struct work_struct *work)
{
+ struct e1000_adapter *adapter = container_of(work,
+ struct e1000_adapter,
+ print_hang_task);
struct e1000_ring *tx_ring = adapter->tx_ring;
unsigned int i = tx_ring->next_to_clean;
unsigned int eop = tx_ring->buffer_info[i].next_to_watch;
struct e1000_tx_desc *eop_desc = E1000_TX_DESC(*tx_ring, eop);
+ struct e1000_hw *hw = &adapter->hw;
+ u16 phy_status, phy_1000t_status, phy_ext_status;
+ u16 pci_status;
- /* detected Tx unit hang */
- e_err("Detected Tx Unit Hang:\n"
+ e1e_rphy(hw, PHY_STATUS, &phy_status);
+ e1e_rphy(hw, PHY_1000T_STATUS, &phy_1000t_status);
+ e1e_rphy(hw, PHY_EXT_STATUS, &phy_ext_status);
+
+ pci_read_config_word(adapter->pdev, PCI_STATUS, &pci_status);
+
+ /* detected Hardware unit hang */
+ e_err("Detected Hardware Unit Hang:\n"
" TDH <%x>\n"
" TDT <%x>\n"
" next_to_use <%x>\n"
@@ -595,7 +580,12 @@ static void e1000_print_tx_hang(struct e1000_adapter *adapter)
" time_stamp <%lx>\n"
" next_to_watch <%x>\n"
" jiffies <%lx>\n"
- " next_to_watch.status <%x>\n",
+ " next_to_watch.status <%x>\n"
+ "MAC Status <%x>\n"
+ "PHY Status <%x>\n"
+ "PHY 1000BASE-T Status <%x>\n"
+ "PHY Extended Status <%x>\n"
+ "PCI Status <%x>\n",
readl(adapter->hw.hw_addr + tx_ring->head),
readl(adapter->hw.hw_addr + tx_ring->tail),
tx_ring->next_to_use,
@@ -603,7 +593,12 @@ static void e1000_print_tx_hang(struct e1000_adapter *adapter)
tx_ring->buffer_info[eop].time_stamp,
eop,
jiffies,
- eop_desc->upper.fields.status);
+ eop_desc->upper.fields.status,
+ er32(STATUS),
+ phy_status,
+ phy_1000t_status,
+ phy_ext_status,
+ pci_status);
}
/**
@@ -677,21 +672,23 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter)
}
if (adapter->detect_tx_hung) {
- /* Detect a transmit hang in hardware, this serializes the
- * check with the clearing of time_stamp and movement of i */
+ /*
+ * Detect a transmit hang in hardware, this serializes the
+ * check with the clearing of time_stamp and movement of i
+ */
adapter->detect_tx_hung = 0;
if (tx_ring->buffer_info[i].time_stamp &&
time_after(jiffies, tx_ring->buffer_info[i].time_stamp
- + (adapter->tx_timeout_factor * HZ))
- && !(er32(STATUS) & E1000_STATUS_TXOFF)) {
- e1000_print_tx_hang(adapter);
+ + (adapter->tx_timeout_factor * HZ)) &&
+ !(er32(STATUS) & E1000_STATUS_TXOFF)) {
+ schedule_work(&adapter->print_hang_task);
netif_stop_queue(netdev);
}
}
adapter->total_tx_bytes += total_tx_bytes;
adapter->total_tx_packets += total_tx_packets;
- adapter->net_stats.tx_bytes += total_tx_bytes;
- adapter->net_stats.tx_packets += total_tx_packets;
+ netdev->stats.tx_bytes += total_tx_bytes;
+ netdev->stats.tx_packets += total_tx_packets;
return (count < tx_ring->count);
}
@@ -705,6 +702,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter)
static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter,
int *work_done, int work_to_do)
{
+ struct e1000_hw *hw = &adapter->hw;
union e1000_rx_desc_packet_split *rx_desc, *next_rxd;
struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
@@ -748,8 +746,8 @@ static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter,
buffer_info->dma = 0;
if (!(staterr & E1000_RXD_STAT_EOP)) {
- e_dbg("%s: Packet Split buffers didn't pick up the "
- "full packet\n", netdev->name);
+ e_dbg("Packet Split buffers didn't pick up the full "
+ "packet\n");
dev_kfree_skb_irq(skb);
goto next_desc;
}
@@ -762,8 +760,8 @@ static bool e1000_clean_rx_irq_ps(struct e1000_adapter *adapter,
length = le16_to_cpu(rx_desc->wb.middle.length0);
if (!length) {
- e_dbg("%s: Last part of the packet spanning multiple "
- "descriptors\n", netdev->name);
+ e_dbg("Last part of the packet spanning multiple "
+ "descriptors\n");
dev_kfree_skb_irq(skb);
goto next_desc;
}
@@ -871,8 +869,8 @@ next_desc:
adapter->total_rx_bytes += total_rx_bytes;
adapter->total_rx_packets += total_rx_packets;
- adapter->net_stats.rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_packets += total_rx_packets;
+ netdev->stats.rx_bytes += total_rx_bytes;
+ netdev->stats.rx_packets += total_rx_packets;
return cleaned;
}
@@ -1051,8 +1049,8 @@ next_desc:
adapter->total_rx_bytes += total_rx_bytes;
adapter->total_rx_packets += total_rx_packets;
- adapter->net_stats.rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_packets += total_rx_packets;
+ netdev->stats.rx_bytes += total_rx_bytes;
+ netdev->stats.rx_packets += total_rx_packets;
return cleaned;
}
@@ -1199,7 +1197,7 @@ static irqreturn_t e1000_intr(int irq, void *data)
struct e1000_hw *hw = &adapter->hw;
u32 rctl, icr = er32(ICR);
- if (!icr)
+ if (!icr || test_bit(__E1000_DOWN, &adapter->state))
return IRQ_NONE; /* Not our interrupt */
/*
@@ -1481,7 +1479,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter)
else
memcpy(adapter->rx_ring->name, netdev->name, IFNAMSIZ);
err = request_irq(adapter->msix_entries[vector].vector,
- &e1000_intr_msix_rx, 0, adapter->rx_ring->name,
+ e1000_intr_msix_rx, 0, adapter->rx_ring->name,
netdev);
if (err)
goto out;
@@ -1494,7 +1492,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter)
else
memcpy(adapter->tx_ring->name, netdev->name, IFNAMSIZ);
err = request_irq(adapter->msix_entries[vector].vector,
- &e1000_intr_msix_tx, 0, adapter->tx_ring->name,
+ e1000_intr_msix_tx, 0, adapter->tx_ring->name,
netdev);
if (err)
goto out;
@@ -1503,7 +1501,7 @@ static int e1000_request_msix(struct e1000_adapter *adapter)
vector++;
err = request_irq(adapter->msix_entries[vector].vector,
- &e1000_msix_other, 0, netdev->name, netdev);
+ e1000_msix_other, 0, netdev->name, netdev);
if (err)
goto out;
@@ -1534,7 +1532,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter)
e1000e_set_interrupt_capability(adapter);
}
if (adapter->flags & FLAG_MSI_ENABLED) {
- err = request_irq(adapter->pdev->irq, &e1000_intr_msi, 0,
+ err = request_irq(adapter->pdev->irq, e1000_intr_msi, 0,
netdev->name, netdev);
if (!err)
return err;
@@ -1544,7 +1542,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter)
adapter->int_mode = E1000E_INT_MODE_LEGACY;
}
- err = request_irq(adapter->pdev->irq, &e1000_intr, IRQF_SHARED,
+ err = request_irq(adapter->pdev->irq, e1000_intr, IRQF_SHARED,
netdev->name, netdev);
if (err)
e_err("Unable to allocate interrupt, Error: %d\n", err);
@@ -2040,11 +2038,14 @@ static void e1000_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
E1000_MNG_DHCP_COOKIE_STATUS_VLAN) &&
(vid == adapter->mng_vlan_id))
return;
+
/* add VID to filter table */
- index = (vid >> 5) & 0x7F;
- vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index);
- vfta |= (1 << (vid & 0x1F));
- e1000e_write_vfta(hw, index, vfta);
+ if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER) {
+ index = (vid >> 5) & 0x7F;
+ vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index);
+ vfta |= (1 << (vid & 0x1F));
+ hw->mac.ops.write_vfta(hw, index, vfta);
+ }
}
static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
@@ -2069,10 +2070,12 @@ static void e1000_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
}
/* remove VID from filter table */
- index = (vid >> 5) & 0x7F;
- vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index);
- vfta &= ~(1 << (vid & 0x1F));
- e1000e_write_vfta(hw, index, vfta);
+ if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER) {
+ index = (vid >> 5) & 0x7F;
+ vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index);
+ vfta &= ~(1 << (vid & 0x1F));
+ hw->mac.ops.write_vfta(hw, index, vfta);
+ }
}
static void e1000_update_mng_vlan(struct e1000_adapter *adapter)
@@ -2464,8 +2467,6 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
ew32(ITR, 1000000000 / (adapter->itr * 256));
ctrl_ext = er32(CTRL_EXT);
- /* Reset delay timers after every interrupt */
- ctrl_ext |= E1000_CTRL_EXT_INT_TIMER_CLR;
/* Auto-Mask interrupts upon ICR access */
ctrl_ext |= E1000_CTRL_EXT_IAME;
ew32(IAM, 0xffffffff);
@@ -2507,21 +2508,23 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
* packet size is equal or larger than the specified value (in 8 byte
* units), e.g. using jumbo frames when setting to E1000_ERT_2048
*/
- if ((adapter->flags & FLAG_HAS_ERT) &&
- (adapter->netdev->mtu > ETH_DATA_LEN)) {
- u32 rxdctl = er32(RXDCTL(0));
- ew32(RXDCTL(0), rxdctl | 0x3);
- ew32(ERT, E1000_ERT_2048 | (1 << 13));
- /*
- * With jumbo frames and early-receive enabled, excessive
- * C4->C2 latencies result in dropped transactions.
- */
- pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY,
- e1000e_driver_name, 55);
- } else {
- pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY,
- e1000e_driver_name,
- PM_QOS_DEFAULT_VALUE);
+ if (adapter->flags & FLAG_HAS_ERT) {
+ if (adapter->netdev->mtu > ETH_DATA_LEN) {
+ u32 rxdctl = er32(RXDCTL(0));
+ ew32(RXDCTL(0), rxdctl | 0x3);
+ ew32(ERT, E1000_ERT_2048 | (1 << 13));
+ /*
+ * With jumbo frames and early-receive enabled,
+ * excessive C-state transition latencies result in
+ * dropped transactions.
+ */
+ pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY,
+ adapter->netdev->name, 55);
+ } else {
+ pm_qos_update_requirement(PM_QOS_CPU_DMA_LATENCY,
+ adapter->netdev->name,
+ PM_QOS_DEFAULT_VALUE);
+ }
}
/* Enable Receives */
@@ -2645,18 +2648,8 @@ static void e1000_configure(struct e1000_adapter *adapter)
**/
void e1000e_power_up_phy(struct e1000_adapter *adapter)
{
- u16 mii_reg = 0;
-
- /* Just clear the power down bit to wake the phy back up */
- if (adapter->hw.phy.media_type == e1000_media_type_copper) {
- /*
- * According to the manual, the phy will retain its
- * settings across a power-down/up cycle
- */
- e1e_rphy(&adapter->hw, PHY_CONTROL, &mii_reg);
- mii_reg &= ~MII_CR_POWER_DOWN;
- e1e_wphy(&adapter->hw, PHY_CONTROL, mii_reg);
- }
+ if (adapter->hw.phy.ops.power_up)
+ adapter->hw.phy.ops.power_up(&adapter->hw);
adapter->hw.mac.ops.setup_link(&adapter->hw);
}
@@ -2664,35 +2657,17 @@ void e1000e_power_up_phy(struct e1000_adapter *adapter)
/**
* e1000_power_down_phy - Power down the PHY
*
- * Power down the PHY so no link is implied when interface is down
- * The PHY cannot be powered down is management or WoL is active
+ * Power down the PHY so no link is implied when interface is down.
+ * The PHY cannot be powered down if management or WoL is active.
*/
static void e1000_power_down_phy(struct e1000_adapter *adapter)
{
- struct e1000_hw *hw = &adapter->hw;
- u16 mii_reg;
-
/* WoL is enabled */
if (adapter->wol)
return;
- /* non-copper PHY? */
- if (adapter->hw.phy.media_type != e1000_media_type_copper)
- return;
-
- /* reset is blocked because of a SoL/IDER session */
- if (e1000e_check_mng_mode(hw) || e1000_check_reset_block(hw))
- return;
-
- /* manageability (AMT) is enabled */
- if (er32(MANC) & E1000_MANC_SMBUS_EN)
- return;
-
- /* power down the PHY */
- e1e_rphy(hw, PHY_CONTROL, &mii_reg);
- mii_reg |= MII_CR_POWER_DOWN;
- e1e_wphy(hw, PHY_CONTROL, mii_reg);
- mdelay(1);
+ if (adapter->hw.phy.ops.power_down)
+ adapter->hw.phy.ops.power_down(&adapter->hw);
}
/**
@@ -2856,6 +2831,12 @@ int e1000e_up(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
+ /* DMA latency requirement to workaround early-receive/jumbo issue */
+ if (adapter->flags & FLAG_HAS_ERT)
+ pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY,
+ adapter->netdev->name,
+ PM_QOS_DEFAULT_VALUE);
+
/* hardware has been reset, we need to reload some things */
e1000_configure(adapter);
@@ -2916,6 +2897,10 @@ void e1000e_down(struct e1000_adapter *adapter)
e1000_clean_tx_ring(adapter);
e1000_clean_rx_ring(adapter);
+ if (adapter->flags & FLAG_HAS_ERT)
+ pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY,
+ adapter->netdev->name);
+
/*
* TODO: for power management, we could drop the link and
* pci_disable_device here.
@@ -2973,7 +2958,7 @@ static irqreturn_t e1000_intr_msi_test(int irq, void *data)
struct e1000_hw *hw = &adapter->hw;
u32 icr = er32(ICR);
- e_dbg("%s: icr is %08X\n", netdev->name, icr);
+ e_dbg("icr is %08X\n", icr);
if (icr & E1000_ICR_RXSEQ) {
adapter->flags &= ~FLAG_MSI_TEST_FAILED;
wmb();
@@ -3010,7 +2995,7 @@ static int e1000_test_msi_interrupt(struct e1000_adapter *adapter)
if (err)
goto msi_test_failed;
- err = request_irq(adapter->pdev->irq, &e1000_intr_msi_test, 0,
+ err = request_irq(adapter->pdev->irq, e1000_intr_msi_test, 0,
netdev->name, netdev);
if (err) {
pci_disable_msi(adapter->pdev);
@@ -3043,7 +3028,7 @@ static int e1000_test_msi_interrupt(struct e1000_adapter *adapter)
goto msi_test_failed;
/* okay so the test worked, restore settings */
- e_dbg("%s: MSI interrupt test succeeded!\n", netdev->name);
+ e_dbg("MSI interrupt test succeeded!\n");
msi_test_failed:
e1000e_set_interrupt_capability(adapter);
e1000_request_irq(adapter);
@@ -3304,6 +3289,7 @@ static void e1000_update_phy_info(unsigned long data)
**/
void e1000e_update_stats(struct e1000_adapter *adapter)
{
+ struct net_device *netdev = adapter->netdev;
struct e1000_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
u16 phy_data;
@@ -3398,8 +3384,8 @@ void e1000e_update_stats(struct e1000_adapter *adapter)
adapter->stats.tsctfc += er32(TSCTFC);
/* Fill out the OS statistics structure */
- adapter->net_stats.multicast = adapter->stats.mprc;
- adapter->net_stats.collisions = adapter->stats.colc;
+ netdev->stats.multicast = adapter->stats.mprc;
+ netdev->stats.collisions = adapter->stats.colc;
/* Rx Errors */
@@ -3407,22 +3393,22 @@ void e1000e_update_stats(struct e1000_adapter *adapter)
* RLEC on some newer hardware can be incorrect so build
* our own version based on RUC and ROC
*/
- adapter->net_stats.rx_errors = adapter->stats.rxerrc +
+ netdev->stats.rx_errors = adapter->stats.rxerrc +
adapter->stats.crcerrs + adapter->stats.algnerrc +
adapter->stats.ruc + adapter->stats.roc +
adapter->stats.cexterr;
- adapter->net_stats.rx_length_errors = adapter->stats.ruc +
+ netdev->stats.rx_length_errors = adapter->stats.ruc +
adapter->stats.roc;
- adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
- adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc;
- adapter->net_stats.rx_missed_errors = adapter->stats.mpc;
+ netdev->stats.rx_crc_errors = adapter->stats.crcerrs;
+ netdev->stats.rx_frame_errors = adapter->stats.algnerrc;
+ netdev->stats.rx_missed_errors = adapter->stats.mpc;
/* Tx Errors */
- adapter->net_stats.tx_errors = adapter->stats.ecol +
+ netdev->stats.tx_errors = adapter->stats.ecol +
adapter->stats.latecol;
- adapter->net_stats.tx_aborted_errors = adapter->stats.ecol;
- adapter->net_stats.tx_window_errors = adapter->stats.latecol;
- adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs;
+ netdev->stats.tx_aborted_errors = adapter->stats.ecol;
+ netdev->stats.tx_window_errors = adapter->stats.latecol;
+ netdev->stats.tx_carrier_errors = adapter->stats.tncrs;
/* Tx Dropped needs to be maintained elsewhere */
@@ -3776,68 +3762,64 @@ static int e1000_tso(struct e1000_adapter *adapter,
u8 ipcss, ipcso, tucss, tucso, hdr_len;
int err;
- if (skb_is_gso(skb)) {
- if (skb_header_cloned(skb)) {
- err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
- if (err)
- return err;
- }
+ if (!skb_is_gso(skb))
+ return 0;
- hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
- mss = skb_shinfo(skb)->gso_size;
- if (skb->protocol == htons(ETH_P_IP)) {
- struct iphdr *iph = ip_hdr(skb);
- iph->tot_len = 0;
- iph->check = 0;
- tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
- iph->daddr, 0,
- IPPROTO_TCP,
- 0);
- cmd_length = E1000_TXD_CMD_IP;
- ipcse = skb_transport_offset(skb) - 1;
- } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
- ipv6_hdr(skb)->payload_len = 0;
- tcp_hdr(skb)->check =
- ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr,
- 0, IPPROTO_TCP, 0);
- ipcse = 0;
- }
- ipcss = skb_network_offset(skb);
- ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data;
- tucss = skb_transport_offset(skb);
- tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data;
- tucse = 0;
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (err)
+ return err;
+ }
- cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE |
- E1000_TXD_CMD_TCP | (skb->len - (hdr_len)));
+ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ mss = skb_shinfo(skb)->gso_size;
+ if (skb->protocol == htons(ETH_P_IP)) {
+ struct iphdr *iph = ip_hdr(skb);
+ iph->tot_len = 0;
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+ 0, IPPROTO_TCP, 0);
+ cmd_length = E1000_TXD_CMD_IP;
+ ipcse = skb_transport_offset(skb) - 1;
+ } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ ipcse = 0;
+ }
+ ipcss = skb_network_offset(skb);
+ ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data;
+ tucss = skb_transport_offset(skb);
+ tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data;
+ tucse = 0;
- i = tx_ring->next_to_use;
- context_desc = E1000_CONTEXT_DESC(*tx_ring, i);
- buffer_info = &tx_ring->buffer_info[i];
+ cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE |
+ E1000_TXD_CMD_TCP | (skb->len - (hdr_len)));
- context_desc->lower_setup.ip_fields.ipcss = ipcss;
- context_desc->lower_setup.ip_fields.ipcso = ipcso;
- context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse);
- context_desc->upper_setup.tcp_fields.tucss = tucss;
- context_desc->upper_setup.tcp_fields.tucso = tucso;
- context_desc->upper_setup.tcp_fields.tucse = cpu_to_le16(tucse);
- context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss);
- context_desc->tcp_seg_setup.fields.hdr_len = hdr_len;
- context_desc->cmd_and_length = cpu_to_le32(cmd_length);
+ i = tx_ring->next_to_use;
+ context_desc = E1000_CONTEXT_DESC(*tx_ring, i);
+ buffer_info = &tx_ring->buffer_info[i];
- buffer_info->time_stamp = jiffies;
- buffer_info->next_to_watch = i;
+ context_desc->lower_setup.ip_fields.ipcss = ipcss;
+ context_desc->lower_setup.ip_fields.ipcso = ipcso;
+ context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse);
+ context_desc->upper_setup.tcp_fields.tucss = tucss;
+ context_desc->upper_setup.tcp_fields.tucso = tucso;
+ context_desc->upper_setup.tcp_fields.tucse = cpu_to_le16(tucse);
+ context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss);
+ context_desc->tcp_seg_setup.fields.hdr_len = hdr_len;
+ context_desc->cmd_and_length = cpu_to_le32(cmd_length);
- i++;
- if (i == tx_ring->count)
- i = 0;
- tx_ring->next_to_use = i;
+ buffer_info->time_stamp = jiffies;
+ buffer_info->next_to_watch = i;
- return 1;
- }
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+ tx_ring->next_to_use = i;
- return 0;
+ return 1;
}
static bool e1000_tx_csum(struct e1000_adapter *adapter, struct sk_buff *skb)
@@ -3909,23 +3891,14 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
unsigned int mss)
{
struct e1000_ring *tx_ring = adapter->tx_ring;
+ struct pci_dev *pdev = adapter->pdev;
struct e1000_buffer *buffer_info;
unsigned int len = skb_headlen(skb);
- unsigned int offset, size, count = 0, i;
+ unsigned int offset = 0, size, count = 0, i;
unsigned int f;
- dma_addr_t *map;
i = tx_ring->next_to_use;
- if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) {
- dev_err(&adapter->pdev->dev, "TX DMA map failed\n");
- adapter->tx_dma_failed++;
- return 0;
- }
-
- map = skb_shinfo(skb)->dma_maps;
- offset = 0;
-
while (len) {
buffer_info = &tx_ring->buffer_info[i];
size = min(len, max_per_txd);
@@ -3933,11 +3906,15 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
buffer_info->length = size;
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- buffer_info->dma = skb_shinfo(skb)->dma_head + offset;
- count++;
+ buffer_info->dma = pci_map_single(pdev, skb->data + offset,
+ size, PCI_DMA_TODEVICE);
+ buffer_info->mapped_as_page = false;
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
len -= size;
offset += size;
+ count++;
if (len) {
i++;
@@ -3951,7 +3928,7 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
frag = &skb_shinfo(skb)->frags[f];
len = frag->size;
- offset = 0;
+ offset = frag->page_offset;
while (len) {
i++;
@@ -3964,7 +3941,12 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
buffer_info->length = size;
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- buffer_info->dma = map[f] + offset;
+ buffer_info->dma = pci_map_page(pdev, frag->page,
+ offset, size,
+ PCI_DMA_TODEVICE);
+ buffer_info->mapped_as_page = true;
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
len -= size;
offset += size;
@@ -3976,6 +3958,22 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
tx_ring->buffer_info[first].next_to_watch = i;
return count;
+
+dma_error:
+ dev_err(&pdev->dev, "TX DMA map failed\n");
+ buffer_info->dma = 0;
+ count--;
+
+ while (count >= 0) {
+ count--;
+ i--;
+ if (i < 0)
+ i += tx_ring->count;
+ buffer_info = &tx_ring->buffer_info[i];
+ e1000_put_txbuf(adapter, buffer_info);;
+ }
+
+ return 0;
}
static void e1000_tx_queue(struct e1000_adapter *adapter,
@@ -4048,8 +4046,8 @@ static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter,
u16 length, offset;
if (vlan_tx_tag_present(skb)) {
- if (!((vlan_tx_tag_get(skb) == adapter->hw.mng_cookie.vlan_id)
- && (adapter->hw.mng_cookie.status &
+ if (!((vlan_tx_tag_get(skb) == adapter->hw.mng_cookie.vlan_id) &&
+ (adapter->hw.mng_cookie.status &
E1000_MNG_DHCP_COOKIE_STATUS_VLAN)))
return 0;
}
@@ -4271,10 +4269,8 @@ static void e1000_reset_task(struct work_struct *work)
**/
static struct net_device_stats *e1000_get_stats(struct net_device *netdev)
{
- struct e1000_adapter *adapter = netdev_priv(netdev);
-
/* only return the current stats */
- return &adapter->net_stats;
+ return &netdev->stats;
}
/**
@@ -4362,6 +4358,8 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
data->phy_id = adapter->hw.phy.addr;
break;
case SIOCGMIIREG:
+ e1000_phy_read_status(adapter);
+
switch (data->reg_num & 0x1F) {
case MII_BMCR:
data->val_out = adapter->phy_regs.bmcr;
@@ -4469,7 +4467,7 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc)
e1e_wphy(&adapter->hw, BM_WUC, E1000_WUC_PME_EN);
/* activate PHY wakeup */
- retval = hw->phy.ops.acquire_phy(hw);
+ retval = hw->phy.ops.acquire(hw);
if (retval) {
e_err("Could not acquire PHY\n");
return retval;
@@ -4486,7 +4484,7 @@ static int e1000_init_phy_wakeup(struct e1000_adapter *adapter, u32 wufc)
if (retval)
e_err("Could not set PHY Host Wakeup bit\n");
out:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return retval;
}
@@ -5160,6 +5158,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);
INIT_WORK(&adapter->downshift_task, e1000e_downshift_workaround);
INIT_WORK(&adapter->update_phy_task, e1000e_update_phy_task);
+ INIT_WORK(&adapter->print_hang_task, e1000_print_hw_hang);
/* Initialize link parameters. User can change them with ethtool */
adapter->hw.mac.autoneg = 1;
@@ -5283,19 +5282,24 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
del_timer_sync(&adapter->watchdog_timer);
del_timer_sync(&adapter->phy_info_timer);
+ cancel_work_sync(&adapter->reset_task);
+ cancel_work_sync(&adapter->watchdog_task);
+ cancel_work_sync(&adapter->downshift_task);
+ cancel_work_sync(&adapter->update_phy_task);
+ cancel_work_sync(&adapter->print_hang_task);
flush_scheduled_work();
+ if (!(netdev->flags & IFF_UP))
+ e1000_power_down_phy(adapter);
+
+ unregister_netdev(netdev);
+
/*
* Release control of h/w to f/w. If f/w is AMT enabled, this
* would have already happened in close and is redundant.
*/
e1000_release_hw_control(adapter);
- unregister_netdev(netdev);
-
- if (!e1000_check_reset_block(&adapter->hw))
- e1000_phy_hw_reset(&adapter->hw);
-
e1000e_reset_interrupt_capability(adapter);
kfree(adapter->tx_ring);
kfree(adapter->rx_ring);
@@ -5361,6 +5365,7 @@ static struct pci_device_id e1000_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_C), board_ich8lan },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M), board_ich8lan },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_IGP_M_AMT), board_ich8lan },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH8_82567V_3), board_ich8lan },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE), board_ich9lan },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IFE_G), board_ich9lan },
@@ -5414,12 +5419,10 @@ static int __init e1000_init_module(void)
int ret;
printk(KERN_INFO "%s: Intel(R) PRO/1000 Network Driver - %s\n",
e1000e_driver_name, e1000e_driver_version);
- printk(KERN_INFO "%s: Copyright (c) 1999-2008 Intel Corporation.\n",
+ printk(KERN_INFO "%s: Copyright (c) 1999 - 2009 Intel Corporation.\n",
e1000e_driver_name);
ret = pci_register_driver(&e1000_driver);
- pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, e1000e_driver_name,
- PM_QOS_DEFAULT_VALUE);
-
+
return ret;
}
module_init(e1000_init_module);
@@ -5433,7 +5436,6 @@ module_init(e1000_init_module);
static void __exit e1000_exit_module(void)
{
pci_unregister_driver(&e1000_driver);
- pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, e1000e_driver_name);
}
module_exit(e1000_exit_module);
diff --git a/drivers/net/e1000e/param.c b/drivers/net/e1000e/param.c
index 1342e0b1815..2e399778cae 100644
--- a/drivers/net/e1000e/param.c
+++ b/drivers/net/e1000e/param.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c
index 85f955f7041..55a2c0acfee 100644
--- a/drivers/net/e1000e/phy.c
+++ b/drivers/net/e1000e/phy.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2008 Intel Corporation.
+ Copyright(c) 1999 - 2009 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -44,6 +44,8 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
/* Cable length tables */
static const u16 e1000_m88_cable_length_table[] =
{ 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED };
+#define M88E1000_CABLE_LENGTH_TABLE_SIZE \
+ ARRAY_SIZE(e1000_m88_cable_length_table)
static const u16 e1000_igp_2_cable_length_table[] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, 0, 0, 0, 3,
@@ -130,7 +132,7 @@ s32 e1000e_get_phy_id(struct e1000_hw *hw)
u16 phy_id;
u16 retry_count = 0;
- if (!(phy->ops.read_phy_reg))
+ if (!(phy->ops.read_reg))
goto out;
while (retry_count < 2) {
@@ -151,29 +153,29 @@ s32 e1000e_get_phy_id(struct e1000_hw *hw)
goto out;
/*
- * If the PHY ID is still unknown, we may have an 82577i
- * without link. We will try again after setting Slow
- * MDIC mode. No harm in trying again in this case since
- * the PHY ID is unknown at this point anyway
+ * If the PHY ID is still unknown, we may have an 82577
+ * without link. We will try again after setting Slow MDIC
+ * mode. No harm in trying again in this case since the PHY
+ * ID is unknown at this point anyway.
*/
- ret_val = phy->ops.acquire_phy(hw);
+ ret_val = phy->ops.acquire(hw);
if (ret_val)
goto out;
ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
if (ret_val)
goto out;
- phy->ops.release_phy(hw);
+ phy->ops.release(hw);
retry_count++;
}
out:
/* Revert to MDIO fast mode, if applicable */
if (retry_count) {
- ret_val = phy->ops.acquire_phy(hw);
+ ret_val = phy->ops.acquire(hw);
if (ret_val)
return ret_val;
ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
- phy->ops.release_phy(hw);
+ phy->ops.release(hw);
}
return ret_val;
@@ -211,7 +213,7 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
u32 i, mdic = 0;
if (offset > MAX_PHY_REG_ADDRESS) {
- hw_dbg(hw, "PHY Address %d is out of range\n", offset);
+ e_dbg("PHY Address %d is out of range\n", offset);
return -E1000_ERR_PARAM;
}
@@ -238,11 +240,11 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
break;
}
if (!(mdic & E1000_MDIC_READY)) {
- hw_dbg(hw, "MDI Read did not complete\n");
+ e_dbg("MDI Read did not complete\n");
return -E1000_ERR_PHY;
}
if (mdic & E1000_MDIC_ERROR) {
- hw_dbg(hw, "MDI Error\n");
+ e_dbg("MDI Error\n");
return -E1000_ERR_PHY;
}
*data = (u16) mdic;
@@ -264,7 +266,7 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
u32 i, mdic = 0;
if (offset > MAX_PHY_REG_ADDRESS) {
- hw_dbg(hw, "PHY Address %d is out of range\n", offset);
+ e_dbg("PHY Address %d is out of range\n", offset);
return -E1000_ERR_PARAM;
}
@@ -292,11 +294,11 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
break;
}
if (!(mdic & E1000_MDIC_READY)) {
- hw_dbg(hw, "MDI Write did not complete\n");
+ e_dbg("MDI Write did not complete\n");
return -E1000_ERR_PHY;
}
if (mdic & E1000_MDIC_ERROR) {
- hw_dbg(hw, "MDI Error\n");
+ e_dbg("MDI Error\n");
return -E1000_ERR_PHY;
}
@@ -317,14 +319,14 @@ s32 e1000e_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data)
{
s32 ret_val;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -342,14 +344,14 @@ s32 e1000e_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data)
{
s32 ret_val;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -371,10 +373,10 @@ static s32 __e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data,
s32 ret_val = 0;
if (!locked) {
- if (!(hw->phy.ops.acquire_phy))
+ if (!(hw->phy.ops.acquire))
goto out;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
goto out;
}
@@ -392,7 +394,7 @@ static s32 __e1000e_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data,
release:
if (!locked)
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
out:
return ret_val;
}
@@ -442,10 +444,10 @@ static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data,
s32 ret_val = 0;
if (!locked) {
- if (!(hw->phy.ops.acquire_phy))
+ if (!(hw->phy.ops.acquire))
goto out;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
goto out;
}
@@ -463,7 +465,7 @@ static s32 __e1000e_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data,
release:
if (!locked)
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
out:
return ret_val;
@@ -515,10 +517,10 @@ static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data,
s32 ret_val = 0;
if (!locked) {
- if (!(hw->phy.ops.acquire_phy))
+ if (!(hw->phy.ops.acquire))
goto out;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
goto out;
}
@@ -533,7 +535,7 @@ static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data,
*data = (u16)kmrnctrlsta;
if (!locked)
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
out:
return ret_val;
@@ -587,10 +589,10 @@ static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data,
s32 ret_val = 0;
if (!locked) {
- if (!(hw->phy.ops.acquire_phy))
+ if (!(hw->phy.ops.acquire))
goto out;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
goto out;
}
@@ -602,7 +604,7 @@ static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data,
udelay(2);
if (!locked)
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
out:
return ret_val;
@@ -649,7 +651,7 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw)
u16 phy_data;
/* Enable CRS on TX. This must be set for half-duplex operation. */
- ret_val = phy->ops.read_phy_reg(hw, I82577_CFG_REG, &phy_data);
+ ret_val = phy->ops.read_reg(hw, I82577_CFG_REG, &phy_data);
if (ret_val)
goto out;
@@ -658,7 +660,7 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw)
/* Enable downshift */
phy_data |= I82577_CFG_ENABLE_DOWNSHIFT;
- ret_val = phy->ops.write_phy_reg(hw, I82577_CFG_REG, phy_data);
+ ret_val = phy->ops.write_reg(hw, I82577_CFG_REG, phy_data);
out:
return ret_val;
@@ -776,12 +778,12 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw)
/* Commit the changes. */
ret_val = e1000e_commit_phy(hw);
if (ret_val) {
- hw_dbg(hw, "Error committing the PHY changes\n");
+ e_dbg("Error committing the PHY changes\n");
return ret_val;
}
if (phy->type == e1000_phy_82578) {
- ret_val = phy->ops.read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+ ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
&phy_data);
if (ret_val)
return ret_val;
@@ -789,7 +791,7 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw)
/* 82578 PHY - set the downshift count to 1x. */
phy_data |= I82578_EPSCR_DOWNSHIFT_ENABLE;
phy_data &= ~I82578_EPSCR_DOWNSHIFT_COUNTER_MASK;
- ret_val = phy->ops.write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+ ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
phy_data);
if (ret_val)
return ret_val;
@@ -813,7 +815,7 @@ s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw)
ret_val = e1000_phy_hw_reset(hw);
if (ret_val) {
- hw_dbg(hw, "Error resetting the PHY.\n");
+ e_dbg("Error resetting the PHY.\n");
return ret_val;
}
@@ -824,9 +826,9 @@ s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw)
msleep(100);
/* disable lplu d0 during driver init */
- ret_val = e1000_set_d0_lplu_state(hw, 0);
+ ret_val = e1000_set_d0_lplu_state(hw, false);
if (ret_val) {
- hw_dbg(hw, "Error Disabling LPLU D0\n");
+ e_dbg("Error Disabling LPLU D0\n");
return ret_val;
}
/* Configure mdi-mdix settings */
@@ -962,39 +964,39 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw)
NWAY_AR_10T_HD_CAPS);
mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS);
- hw_dbg(hw, "autoneg_advertised %x\n", phy->autoneg_advertised);
+ e_dbg("autoneg_advertised %x\n", phy->autoneg_advertised);
/* Do we want to advertise 10 Mb Half Duplex? */
if (phy->autoneg_advertised & ADVERTISE_10_HALF) {
- hw_dbg(hw, "Advertise 10mb Half duplex\n");
+ e_dbg("Advertise 10mb Half duplex\n");
mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
}
/* Do we want to advertise 10 Mb Full Duplex? */
if (phy->autoneg_advertised & ADVERTISE_10_FULL) {
- hw_dbg(hw, "Advertise 10mb Full duplex\n");
+ e_dbg("Advertise 10mb Full duplex\n");
mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
}
/* Do we want to advertise 100 Mb Half Duplex? */
if (phy->autoneg_advertised & ADVERTISE_100_HALF) {
- hw_dbg(hw, "Advertise 100mb Half duplex\n");
+ e_dbg("Advertise 100mb Half duplex\n");
mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
}
/* Do we want to advertise 100 Mb Full Duplex? */
if (phy->autoneg_advertised & ADVERTISE_100_FULL) {
- hw_dbg(hw, "Advertise 100mb Full duplex\n");
+ e_dbg("Advertise 100mb Full duplex\n");
mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
}
/* We do not allow the Phy to advertise 1000 Mb Half Duplex */
if (phy->autoneg_advertised & ADVERTISE_1000_HALF)
- hw_dbg(hw, "Advertise 1000mb Half duplex request denied!\n");
+ e_dbg("Advertise 1000mb Half duplex request denied!\n");
/* Do we want to advertise 1000 Mb Full Duplex? */
if (phy->autoneg_advertised & ADVERTISE_1000_FULL) {
- hw_dbg(hw, "Advertise 1000mb Full duplex\n");
+ e_dbg("Advertise 1000mb Full duplex\n");
mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
}
@@ -1053,7 +1055,7 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw)
mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
break;
default:
- hw_dbg(hw, "Flow control param set incorrectly\n");
+ e_dbg("Flow control param set incorrectly\n");
ret_val = -E1000_ERR_CONFIG;
return ret_val;
}
@@ -1062,7 +1064,7 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- hw_dbg(hw, "Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
+ e_dbg("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
ret_val = e1e_wphy(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg);
@@ -1099,13 +1101,13 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw)
if (phy->autoneg_advertised == 0)
phy->autoneg_advertised = phy->autoneg_mask;
- hw_dbg(hw, "Reconfiguring auto-neg advertisement params\n");
+ e_dbg("Reconfiguring auto-neg advertisement params\n");
ret_val = e1000_phy_setup_autoneg(hw);
if (ret_val) {
- hw_dbg(hw, "Error Setting up Auto-Negotiation\n");
+ e_dbg("Error Setting up Auto-Negotiation\n");
return ret_val;
}
- hw_dbg(hw, "Restarting Auto-Neg\n");
+ e_dbg("Restarting Auto-Neg\n");
/*
* Restart auto-negotiation by setting the Auto Neg Enable bit and
@@ -1127,7 +1129,7 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw)
if (phy->autoneg_wait_to_complete) {
ret_val = e1000_wait_autoneg(hw);
if (ret_val) {
- hw_dbg(hw, "Error while waiting for "
+ e_dbg("Error while waiting for "
"autoneg to complete\n");
return ret_val;
}
@@ -1165,10 +1167,10 @@ s32 e1000e_setup_copper_link(struct e1000_hw *hw)
* PHY will be set to 10H, 10F, 100H or 100F
* depending on user settings.
*/
- hw_dbg(hw, "Forcing Speed and Duplex\n");
+ e_dbg("Forcing Speed and Duplex\n");
ret_val = e1000_phy_force_speed_duplex(hw);
if (ret_val) {
- hw_dbg(hw, "Error Forcing Speed and Duplex\n");
+ e_dbg("Error Forcing Speed and Duplex\n");
return ret_val;
}
}
@@ -1185,11 +1187,11 @@ s32 e1000e_setup_copper_link(struct e1000_hw *hw)
return ret_val;
if (link) {
- hw_dbg(hw, "Valid link established!!!\n");
+ e_dbg("Valid link established!!!\n");
e1000e_config_collision_dist(hw);
ret_val = e1000e_config_fc_after_link_up(hw);
} else {
- hw_dbg(hw, "Unable to establish link!!!\n");
+ e_dbg("Unable to establish link!!!\n");
}
return ret_val;
@@ -1235,12 +1237,12 @@ s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- hw_dbg(hw, "IGP PSCR: %X\n", phy_data);
+ e_dbg("IGP PSCR: %X\n", phy_data);
udelay(1);
if (phy->autoneg_wait_to_complete) {
- hw_dbg(hw, "Waiting for forced speed/duplex link on IGP phy.\n");
+ e_dbg("Waiting for forced speed/duplex link on IGP phy.\n");
ret_val = e1000e_phy_has_link_generic(hw,
PHY_FORCE_LIMIT,
@@ -1250,7 +1252,7 @@ s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw)
return ret_val;
if (!link)
- hw_dbg(hw, "Link taking longer than expected.\n");
+ e_dbg("Link taking longer than expected.\n");
/* Try once more */
ret_val = e1000e_phy_has_link_generic(hw,
@@ -1294,7 +1296,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- hw_dbg(hw, "M88E1000 PSCR: %X\n", phy_data);
+ e_dbg("M88E1000 PSCR: %X\n", phy_data);
ret_val = e1e_rphy(hw, PHY_CONTROL, &phy_data);
if (ret_val)
@@ -1312,7 +1314,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
return ret_val;
if (phy->autoneg_wait_to_complete) {
- hw_dbg(hw, "Waiting for forced speed/duplex link on M88 phy.\n");
+ e_dbg("Waiting for forced speed/duplex link on M88 phy.\n");
ret_val = e1000e_phy_has_link_generic(hw, PHY_FORCE_LIMIT,
100000, &link);
@@ -1320,17 +1322,22 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
return ret_val;
if (!link) {
- /*
- * We didn't get link.
- * Reset the DSP and cross our fingers.
- */
- ret_val = e1e_wphy(hw, M88E1000_PHY_PAGE_SELECT,
- 0x001d);
- if (ret_val)
- return ret_val;
- ret_val = e1000e_phy_reset_dsp(hw);
- if (ret_val)
- return ret_val;
+ if (hw->phy.type != e1000_phy_m88) {
+ e_dbg("Link taking longer than expected.\n");
+ } else {
+ /*
+ * We didn't get link.
+ * Reset the DSP and cross our fingers.
+ */
+ ret_val = e1e_wphy(hw,
+ M88E1000_PHY_PAGE_SELECT,
+ 0x001d);
+ if (ret_val)
+ return ret_val;
+ ret_val = e1000e_phy_reset_dsp(hw);
+ if (ret_val)
+ return ret_val;
+ }
}
/* Try once more */
@@ -1340,6 +1347,9 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
return ret_val;
}
+ if (hw->phy.type != e1000_phy_m88)
+ return 0;
+
ret_val = e1e_rphy(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data);
if (ret_val)
return ret_val;
@@ -1369,6 +1379,73 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw)
}
/**
+ * e1000_phy_force_speed_duplex_ife - Force PHY speed & duplex
+ * @hw: pointer to the HW structure
+ *
+ * Forces the speed and duplex settings of the PHY.
+ * This is a function pointer entry point only called by
+ * PHY setup routines.
+ **/
+s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 data;
+ bool link;
+
+ ret_val = e1e_rphy(hw, PHY_CONTROL, &data);
+ if (ret_val)
+ goto out;
+
+ e1000e_phy_force_speed_duplex_setup(hw, &data);
+
+ ret_val = e1e_wphy(hw, PHY_CONTROL, data);
+ if (ret_val)
+ goto out;
+
+ /* Disable MDI-X support for 10/100 */
+ ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data);
+ if (ret_val)
+ goto out;
+
+ data &= ~IFE_PMC_AUTO_MDIX;
+ data &= ~IFE_PMC_FORCE_MDIX;
+
+ ret_val = e1e_wphy(hw, IFE_PHY_MDIX_CONTROL, data);
+ if (ret_val)
+ goto out;
+
+ e_dbg("IFE PMC: %X\n", data);
+
+ udelay(1);
+
+ if (phy->autoneg_wait_to_complete) {
+ e_dbg("Waiting for forced speed/duplex link on IFE phy.\n");
+
+ ret_val = e1000e_phy_has_link_generic(hw,
+ PHY_FORCE_LIMIT,
+ 100000,
+ &link);
+ if (ret_val)
+ goto out;
+
+ if (!link)
+ e_dbg("Link taking longer than expected.\n");
+
+ /* Try once more */
+ ret_val = e1000e_phy_has_link_generic(hw,
+ PHY_FORCE_LIMIT,
+ 100000,
+ &link);
+ if (ret_val)
+ goto out;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
* e1000e_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex
* @hw: pointer to the HW structure
* @phy_ctrl: pointer to current value of PHY_CONTROL
@@ -1403,11 +1480,11 @@ void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl)
if (mac->forced_speed_duplex & E1000_ALL_HALF_DUPLEX) {
ctrl &= ~E1000_CTRL_FD;
*phy_ctrl &= ~MII_CR_FULL_DUPLEX;
- hw_dbg(hw, "Half Duplex\n");
+ e_dbg("Half Duplex\n");
} else {
ctrl |= E1000_CTRL_FD;
*phy_ctrl |= MII_CR_FULL_DUPLEX;
- hw_dbg(hw, "Full Duplex\n");
+ e_dbg("Full Duplex\n");
}
/* Forcing 10mb or 100mb? */
@@ -1415,12 +1492,12 @@ void e1000e_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl)
ctrl |= E1000_CTRL_SPD_100;
*phy_ctrl |= MII_CR_SPEED_100;
*phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10);
- hw_dbg(hw, "Forcing 100mb\n");
+ e_dbg("Forcing 100mb\n");
} else {
ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
*phy_ctrl |= MII_CR_SPEED_10;
*phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100);
- hw_dbg(hw, "Forcing 10mb\n");
+ e_dbg("Forcing 10mb\n");
}
e1000e_config_collision_dist(hw);
@@ -1523,8 +1600,8 @@ s32 e1000e_check_downshift(struct e1000_hw *hw)
switch (phy->type) {
case e1000_phy_m88:
case e1000_phy_gg82563:
+ case e1000_phy_bm:
case e1000_phy_82578:
- case e1000_phy_82577:
offset = M88E1000_PHY_SPEC_STATUS;
mask = M88E1000_PSSR_DOWNSHIFT;
break;
@@ -1535,7 +1612,7 @@ s32 e1000e_check_downshift(struct e1000_hw *hw)
break;
default:
/* speed downshift not supported */
- phy->speed_downgraded = 0;
+ phy->speed_downgraded = false;
return 0;
}
@@ -1555,7 +1632,7 @@ s32 e1000e_check_downshift(struct e1000_hw *hw)
*
* Polarity is determined based on the PHY specific status register.
**/
-static s32 e1000_check_polarity_m88(struct e1000_hw *hw)
+s32 e1000_check_polarity_m88(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
@@ -1580,7 +1657,7 @@ static s32 e1000_check_polarity_m88(struct e1000_hw *hw)
* Polarity is determined based on the PHY port status register, and the
* current speed (since there is no polarity at 100Mbps).
**/
-static s32 e1000_check_polarity_igp(struct e1000_hw *hw)
+s32 e1000_check_polarity_igp(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
@@ -1618,6 +1695,39 @@ static s32 e1000_check_polarity_igp(struct e1000_hw *hw)
}
/**
+ * e1000_check_polarity_ife - Check cable polarity for IFE PHY
+ * @hw: pointer to the HW structure
+ *
+ * Polarity is determined on the polarity reversal feature being enabled.
+ **/
+s32 e1000_check_polarity_ife(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 phy_data, offset, mask;
+
+ /*
+ * Polarity is determined based on the reversal feature being enabled.
+ */
+ if (phy->polarity_correction) {
+ offset = IFE_PHY_EXTENDED_STATUS_CONTROL;
+ mask = IFE_PESC_POLARITY_REVERSED;
+ } else {
+ offset = IFE_PHY_SPECIAL_CONTROL;
+ mask = IFE_PSC_FORCE_POLARITY;
+ }
+
+ ret_val = e1e_rphy(hw, offset, &phy_data);
+
+ if (!ret_val)
+ phy->cable_polarity = (phy_data & mask)
+ ? e1000_rev_polarity_reversed
+ : e1000_rev_polarity_normal;
+
+ return ret_val;
+}
+
+/**
* e1000_wait_autoneg - Wait for auto-neg completion
* @hw: pointer to the HW structure
*
@@ -1717,15 +1827,21 @@ s32 e1000e_get_cable_length_m88(struct e1000_hw *hw)
ret_val = e1e_rphy(hw, M88E1000_PHY_SPEC_STATUS, &phy_data);
if (ret_val)
- return ret_val;
+ goto out;
index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
- M88E1000_PSSR_CABLE_LENGTH_SHIFT;
+ M88E1000_PSSR_CABLE_LENGTH_SHIFT;
+ if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) {
+ ret_val = -E1000_ERR_PHY;
+ goto out;
+ }
+
phy->min_cable_length = e1000_m88_cable_length_table[index];
- phy->max_cable_length = e1000_m88_cable_length_table[index+1];
+ phy->max_cable_length = e1000_m88_cable_length_table[index + 1];
phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
+out:
return ret_val;
}
@@ -1736,7 +1852,7 @@ s32 e1000e_get_cable_length_m88(struct e1000_hw *hw)
* The automatic gain control (agc) normalizes the amplitude of the
* received signal, adjusting for the attenuation produced by the
* cable. By reading the AGC registers, which represent the
- * combination of course and fine gain value, the value can be put
+ * combination of coarse and fine gain value, the value can be put
* into a lookup table to obtain the approximate cable length
* for each channel.
**/
@@ -1761,7 +1877,7 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw)
/*
* Getting bits 15:9, which represent the combination of
- * course and fine gain values. The result is a number
+ * coarse and fine gain values. The result is a number
* that can be put into the lookup table to obtain the
* approximate cable length.
*/
@@ -1815,8 +1931,8 @@ s32 e1000e_get_phy_info_m88(struct e1000_hw *hw)
u16 phy_data;
bool link;
- if (hw->phy.media_type != e1000_media_type_copper) {
- hw_dbg(hw, "Phy info is only valid for copper media\n");
+ if (phy->media_type != e1000_media_type_copper) {
+ e_dbg("Phy info is only valid for copper media\n");
return -E1000_ERR_CONFIG;
}
@@ -1825,7 +1941,7 @@ s32 e1000e_get_phy_info_m88(struct e1000_hw *hw)
return ret_val;
if (!link) {
- hw_dbg(hw, "Phy info is only valid if link is up\n");
+ e_dbg("Phy info is only valid if link is up\n");
return -E1000_ERR_CONFIG;
}
@@ -1893,11 +2009,11 @@ s32 e1000e_get_phy_info_igp(struct e1000_hw *hw)
return ret_val;
if (!link) {
- hw_dbg(hw, "Phy info is only valid if link is up\n");
+ e_dbg("Phy info is only valid if link is up\n");
return -E1000_ERR_CONFIG;
}
- phy->polarity_correction = 1;
+ phy->polarity_correction = true;
ret_val = e1000_check_polarity_igp(hw);
if (ret_val)
@@ -1936,6 +2052,61 @@ s32 e1000e_get_phy_info_igp(struct e1000_hw *hw)
}
/**
+ * e1000_get_phy_info_ife - Retrieves various IFE PHY states
+ * @hw: pointer to the HW structure
+ *
+ * Populates "phy" structure with various feature states.
+ **/
+s32 e1000_get_phy_info_ife(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 data;
+ bool link;
+
+ ret_val = e1000e_phy_has_link_generic(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link) {
+ e_dbg("Phy info is only valid if link is up\n");
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ ret_val = e1e_rphy(hw, IFE_PHY_SPECIAL_CONTROL, &data);
+ if (ret_val)
+ goto out;
+ phy->polarity_correction = (data & IFE_PSC_AUTO_POLARITY_DISABLE)
+ ? false : true;
+
+ if (phy->polarity_correction) {
+ ret_val = e1000_check_polarity_ife(hw);
+ if (ret_val)
+ goto out;
+ } else {
+ /* Polarity is forced */
+ phy->cable_polarity = (data & IFE_PSC_FORCE_POLARITY)
+ ? e1000_rev_polarity_reversed
+ : e1000_rev_polarity_normal;
+ }
+
+ ret_val = e1e_rphy(hw, IFE_PHY_MDIX_CONTROL, &data);
+ if (ret_val)
+ goto out;
+
+ phy->is_mdix = (data & IFE_PMC_MDIX_STATUS) ? true : false;
+
+ /* The following parameters are undefined for 10/100 operation. */
+ phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
+ phy->local_rx = e1000_1000t_rx_status_undefined;
+ phy->remote_rx = e1000_1000t_rx_status_undefined;
+
+out:
+ return ret_val;
+}
+
+/**
* e1000e_phy_sw_reset - PHY software reset
* @hw: pointer to the HW structure
*
@@ -1980,7 +2151,7 @@ s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw)
if (ret_val)
return 0;
- ret_val = phy->ops.acquire_phy(hw);
+ ret_val = phy->ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -1995,7 +2166,7 @@ s32 e1000e_phy_hw_reset_generic(struct e1000_hw *hw)
udelay(150);
- phy->ops.release_phy(hw);
+ phy->ops.release(hw);
return e1000_get_phy_cfg_done(hw);
}
@@ -2021,7 +2192,7 @@ s32 e1000e_get_cfg_done(struct e1000_hw *hw)
**/
s32 e1000e_phy_init_script_igp3(struct e1000_hw *hw)
{
- hw_dbg(hw, "Running IGP 3 PHY init script\n");
+ e_dbg("Running IGP 3 PHY init script\n");
/* PHY init IGP 3 */
/* Enable rise/fall, 10-mode work in class-A */
@@ -2189,28 +2360,34 @@ enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id)
s32 e1000e_determine_phy_address(struct e1000_hw *hw)
{
s32 ret_val = -E1000_ERR_PHY_TYPE;
- u32 phy_addr= 0;
- u32 i = 0;
+ u32 phy_addr = 0;
+ u32 i;
enum e1000_phy_type phy_type = e1000_phy_unknown;
- do {
- for (phy_addr = 0; phy_addr < 4; phy_addr++) {
- hw->phy.addr = phy_addr;
+ hw->phy.id = phy_type;
+
+ for (phy_addr = 0; phy_addr < E1000_MAX_PHY_ADDR; phy_addr++) {
+ hw->phy.addr = phy_addr;
+ i = 0;
+
+ do {
e1000e_get_phy_id(hw);
phy_type = e1000e_get_phy_type_from_id(hw->phy.id);
- /*
+ /*
* If phy_type is valid, break - we found our
* PHY address
*/
if (phy_type != e1000_phy_unknown) {
ret_val = 0;
- break;
+ goto out;
}
- }
- i++;
- } while ((ret_val != 0) && (i < 100));
+ msleep(1);
+ i++;
+ } while (i < 10);
+ }
+out:
return ret_val;
}
@@ -2246,7 +2423,7 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
u32 page = offset >> IGP_PAGE_SHIFT;
u32 page_shift = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -2284,7 +2461,7 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data)
data);
out:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2305,7 +2482,7 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
u32 page = offset >> IGP_PAGE_SHIFT;
u32 page_shift = 0;
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -2342,7 +2519,7 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data)
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
out:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2361,7 +2538,7 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
s32 ret_val;
u16 page = (u16)(offset >> IGP_PAGE_SHIFT);
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -2387,7 +2564,7 @@ s32 e1000e_read_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 *data)
ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset,
data);
out:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2405,7 +2582,7 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
s32 ret_val;
u16 page = (u16)(offset >> IGP_PAGE_SHIFT);
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
@@ -2431,7 +2608,7 @@ s32 e1000e_write_phy_reg_bm2(struct e1000_hw *hw, u32 offset, u16 data)
data);
out:
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2464,7 +2641,7 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
/* Gig must be disabled for MDIO accesses to page 800 */
if ((hw->mac.type == e1000_pchlan) &&
(!(er32(PHY_CTRL) & E1000_PHY_CTRL_GBE_DISABLE)))
- hw_dbg(hw, "Attempting to access page 800 while gig enabled\n");
+ e_dbg("Attempting to access page 800 while gig enabled.\n");
/* All operations in this function are phy address 1 */
hw->phy.addr = 1;
@@ -2474,20 +2651,26 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
(BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT));
ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &phy_reg);
- if (ret_val)
+ if (ret_val) {
+ e_dbg("Could not read PHY page 769\n");
goto out;
+ }
/* First clear bit 4 to avoid a power state change */
phy_reg &= ~(BM_WUC_HOST_WU_BIT);
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
- if (ret_val)
+ if (ret_val) {
+ e_dbg("Could not clear PHY page 769 bit 4\n");
goto out;
+ }
/* Write bit 2 = 1, and clear bit 4 to 769_17 */
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG,
phy_reg | BM_WUC_ENABLE_BIT);
- if (ret_val)
+ if (ret_val) {
+ e_dbg("Could not write PHY page 769 bit 2\n");
goto out;
+ }
/* Select page 800 */
ret_val = e1000e_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
@@ -2495,21 +2678,25 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
/* Write the page 800 offset value using opcode 0x11 */
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ADDRESS_OPCODE, reg);
- if (ret_val)
+ if (ret_val) {
+ e_dbg("Could not write address opcode to page 800\n");
goto out;
+ }
if (read) {
/* Read the page 800 value using opcode 0x12 */
ret_val = e1000e_read_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
data);
} else {
- /* Read the page 800 value using opcode 0x12 */
+ /* Write the page 800 value using opcode 0x12 */
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
*data);
}
- if (ret_val)
+ if (ret_val) {
+ e_dbg("Could not access data value from page 800\n");
goto out;
+ }
/*
* Restore 769_17.2 to its original value
@@ -2520,12 +2707,53 @@ static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
/* Clear 769_17.2 */
ret_val = e1000e_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, phy_reg);
+ if (ret_val) {
+ e_dbg("Could not clear PHY page 769 bit 2\n");
+ goto out;
+ }
out:
return ret_val;
}
/**
+ * e1000_power_up_phy_copper - Restore copper link in case of PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, restore the link to previous
+ * settings.
+ **/
+void e1000_power_up_phy_copper(struct e1000_hw *hw)
+{
+ u16 mii_reg = 0;
+
+ /* The PHY will retain its settings across a power down/up cycle */
+ e1e_rphy(hw, PHY_CONTROL, &mii_reg);
+ mii_reg &= ~MII_CR_POWER_DOWN;
+ e1e_wphy(hw, PHY_CONTROL, mii_reg);
+}
+
+/**
+ * e1000_power_down_phy_copper - Restore copper link in case of PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, restore the link to previous
+ * settings.
+ **/
+void e1000_power_down_phy_copper(struct e1000_hw *hw)
+{
+ u16 mii_reg = 0;
+
+ /* The PHY will retain its settings across a power down/up cycle */
+ e1e_rphy(hw, PHY_CONTROL, &mii_reg);
+ mii_reg |= MII_CR_POWER_DOWN;
+ e1e_wphy(hw, PHY_CONTROL, mii_reg);
+ msleep(1);
+}
+
+/**
* e1000e_commit_phy - Soft PHY reset
* @hw: pointer to the HW structure
*
@@ -2534,8 +2762,8 @@ out:
**/
s32 e1000e_commit_phy(struct e1000_hw *hw)
{
- if (hw->phy.ops.commit_phy)
- return hw->phy.ops.commit_phy(hw);
+ if (hw->phy.ops.commit)
+ return hw->phy.ops.commit(hw);
return 0;
}
@@ -2614,7 +2842,7 @@ static s32 __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
bool in_slow_mode = false;
if (!locked) {
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
}
@@ -2670,7 +2898,7 @@ out:
ret_val |= e1000_set_mdio_slow_mode_hv(hw, false);
if (!locked)
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2723,7 +2951,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
bool in_slow_mode = false;
if (!locked) {
- ret_val = hw->phy.ops.acquire_phy(hw);
+ ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return ret_val;
}
@@ -2796,7 +3024,7 @@ out:
ret_val |= e1000_set_mdio_slow_mode_hv(hw, false);
if (!locked)
- hw->phy.ops.release_phy(hw);
+ hw->phy.ops.release(hw);
return ret_val;
}
@@ -2872,7 +3100,7 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
/* masking with 0x3F to remove the page from offset */
ret_val = e1000e_write_phy_reg_mdic(hw, addr_reg, (u16)offset & 0x3F);
if (ret_val) {
- hw_dbg(hw, "Could not write PHY the HV address register\n");
+ e_dbg("Could not write PHY the HV address register\n");
goto out;
}
@@ -2883,7 +3111,7 @@ static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
ret_val = e1000e_write_phy_reg_mdic(hw, data_reg, *data);
if (ret_val) {
- hw_dbg(hw, "Could not read data value from HV data register\n");
+ e_dbg("Could not read data value from HV data register\n");
goto out;
}
@@ -2911,12 +3139,12 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw)
goto out;
/* Do not apply workaround if in PHY loopback bit 14 set */
- hw->phy.ops.read_phy_reg(hw, PHY_CONTROL, &data);
+ hw->phy.ops.read_reg(hw, PHY_CONTROL, &data);
if (data & PHY_CONTROL_LB)
goto out;
/* check if link is up and at 1Gbps */
- ret_val = hw->phy.ops.read_phy_reg(hw, BM_CS_STATUS, &data);
+ ret_val = hw->phy.ops.read_reg(hw, BM_CS_STATUS, &data);
if (ret_val)
goto out;
@@ -2932,13 +3160,13 @@ s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw)
mdelay(200);
/* flush the packets in the fifo buffer */
- ret_val = hw->phy.ops.write_phy_reg(hw, HV_MUX_DATA_CTRL,
+ ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL,
HV_MUX_DATA_CTRL_GEN_TO_MAC |
HV_MUX_DATA_CTRL_FORCE_SPEED);
if (ret_val)
goto out;
- ret_val = hw->phy.ops.write_phy_reg(hw, HV_MUX_DATA_CTRL,
+ ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL,
HV_MUX_DATA_CTRL_GEN_TO_MAC);
out:
@@ -2959,7 +3187,7 @@ s32 e1000_check_polarity_82577(struct e1000_hw *hw)
s32 ret_val;
u16 data;
- ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_STATUS_2, &data);
+ ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data);
if (!ret_val)
phy->cable_polarity = (data & I82577_PHY_STATUS2_REV_POLARITY)
@@ -2984,13 +3212,13 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
u16 phy_data;
bool link;
- ret_val = phy->ops.read_phy_reg(hw, PHY_CONTROL, &phy_data);
+ ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
if (ret_val)
goto out;
e1000e_phy_force_speed_duplex_setup(hw, &phy_data);
- ret_val = phy->ops.write_phy_reg(hw, PHY_CONTROL, phy_data);
+ ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
if (ret_val)
goto out;
@@ -2998,23 +3226,23 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
* Clear Auto-Crossover to force MDI manually. 82577 requires MDI
* forced whenever speed and duplex are forced.
*/
- ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_CTRL_2, &phy_data);
+ ret_val = phy->ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data);
if (ret_val)
goto out;
phy_data &= ~I82577_PHY_CTRL2_AUTO_MDIX;
phy_data &= ~I82577_PHY_CTRL2_FORCE_MDI_MDIX;
- ret_val = phy->ops.write_phy_reg(hw, I82577_PHY_CTRL_2, phy_data);
+ ret_val = phy->ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data);
if (ret_val)
goto out;
- hw_dbg(hw, "I82577_PHY_CTRL_2: %X\n", phy_data);
+ e_dbg("I82577_PHY_CTRL_2: %X\n", phy_data);
udelay(1);
if (phy->autoneg_wait_to_complete) {
- hw_dbg(hw, "Waiting for forced speed/duplex link on 82577 phy\n");
+ e_dbg("Waiting for forced speed/duplex link on 82577 phy\n");
ret_val = e1000e_phy_has_link_generic(hw,
PHY_FORCE_LIMIT,
@@ -3024,7 +3252,7 @@ s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
goto out;
if (!link)
- hw_dbg(hw, "Link taking longer than expected.\n");
+ e_dbg("Link taking longer than expected.\n");
/* Try once more */
ret_val = e1000e_phy_has_link_generic(hw,
@@ -3060,7 +3288,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw)
goto out;
if (!link) {
- hw_dbg(hw, "Phy info is only valid if link is up\n");
+ e_dbg("Phy info is only valid if link is up\n");
ret_val = -E1000_ERR_CONFIG;
goto out;
}
@@ -3071,7 +3299,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw)
if (ret_val)
goto out;
- ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_STATUS_2, &data);
+ ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data);
if (ret_val)
goto out;
@@ -3083,7 +3311,7 @@ s32 e1000_get_phy_info_82577(struct e1000_hw *hw)
if (ret_val)
goto out;
- ret_val = phy->ops.read_phy_reg(hw, PHY_1000T_STATUS, &data);
+ ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data);
if (ret_val)
goto out;
@@ -3117,7 +3345,7 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw)
s32 ret_val;
u16 phy_data, length;
- ret_val = phy->ops.read_phy_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data);
+ ret_val = phy->ops.read_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data);
if (ret_val)
goto out;
@@ -3125,7 +3353,7 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw)
I82577_DSTATUS_CABLE_LENGTH_SHIFT;
if (length == E1000_CABLE_LENGTH_UNDEFINED)
- ret_val = E1000_ERR_PHY;
+ ret_val = -E1000_ERR_PHY;
phy->cable_length = length;
diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c
index d2f6ee1a629..ca93c9a9d37 100644
--- a/drivers/net/e2100.c
+++ b/drivers/net/e2100.c
@@ -186,9 +186,9 @@ static int __init e21_probe1(struct net_device *dev, int ioaddr)
return -EBUSY;
/* First check the station address for the Ctron prefix. */
- if (inb(ioaddr + E21_SAPROM + 0) != 0x00
- || inb(ioaddr + E21_SAPROM + 1) != 0x00
- || inb(ioaddr + E21_SAPROM + 2) != 0x1d) {
+ if (inb(ioaddr + E21_SAPROM + 0) != 0x00 ||
+ inb(ioaddr + E21_SAPROM + 1) != 0x00 ||
+ inb(ioaddr + E21_SAPROM + 2) != 0x1d) {
retval = -ENODEV;
goto out;
}
diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c
index 1e934160062..94c59498cdb 100644
--- a/drivers/net/eepro.c
+++ b/drivers/net/eepro.c
@@ -990,7 +990,7 @@ static int eepro_open(struct net_device *dev)
return -EAGAIN;
}
- if (request_irq(dev->irq , &eepro_interrupt, 0, dev->name, dev)) {
+ if (request_irq(dev->irq , eepro_interrupt, 0, dev->name, dev)) {
printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq);
return -EAGAIN;
}
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index 592de8f1668..6fbfc8eee63 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -457,7 +457,7 @@ static int eexp_open(struct net_device *dev)
if (!dev->irq || !irqrmap[dev->irq])
return -ENXIO;
- ret = request_irq(dev->irq, &eexp_irq, 0, dev->name, dev);
+ ret = request_irq(dev->irq, eexp_irq, 0, dev->name, dev);
if (ret)
return ret;
diff --git a/drivers/net/ehea/ehea_ethtool.c b/drivers/net/ehea/ehea_ethtool.c
index d7688522336..75b099ce49c 100644
--- a/drivers/net/ehea/ehea_ethtool.c
+++ b/drivers/net/ehea/ehea_ethtool.c
@@ -118,7 +118,7 @@ doit:
ret = ehea_set_portspeed(port, sp);
if (!ret)
- ehea_info("%s: Port speed succesfully set: %dMbps "
+ ehea_info("%s: Port speed successfully set: %dMbps "
"%s Duplex",
port->netdev->name, port->port_speed,
port->full_duplex == 1 ? "Full" : "Half");
@@ -134,7 +134,7 @@ static int ehea_nway_reset(struct net_device *dev)
ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
if (!ret)
- ehea_info("%s: Port speed succesfully set: %dMbps "
+ ehea_info("%s: Port speed successfully set: %dMbps "
"%s Duplex",
port->netdev->name, port->port_speed,
port->full_duplex == 1 ? "Full" : "Half");
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 41bd7aeafd8..7b62336e673 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -189,8 +189,8 @@ static void ehea_update_firmware_handles(void)
for (k = 0; k < EHEA_MAX_PORTS; k++) {
struct ehea_port *port = adapter->port[k];
- if (!port || (port->state != EHEA_PORT_UP)
- || (num_ports == 0))
+ if (!port || (port->state != EHEA_PORT_UP) ||
+ (num_ports == 0))
continue;
for (l = 0;
@@ -447,7 +447,9 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr,
max_index_mask = q_skba->len - 1;
for (i = 0; i < fill_wqes; i++) {
u64 tmp_addr;
- struct sk_buff *skb = netdev_alloc_skb(dev, packet_size);
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb_ip_align(dev, packet_size);
if (!skb) {
q_skba->os_skbs = fill_wqes - i;
if (q_skba->os_skbs == q_skba->len - 2) {
@@ -457,7 +459,6 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr,
}
break;
}
- skb_reserve(skb, NET_IP_ALIGN);
skb_arr[index] = skb;
tmp_addr = ehea_map_vaddr(skb->data);
@@ -500,7 +501,7 @@ static int ehea_refill_rq2(struct ehea_port_res *pr, int nr_of_wqes)
{
return ehea_refill_rq_def(pr, &pr->rq2_skba, 2,
nr_of_wqes, EHEA_RWQE2_TYPE,
- EHEA_RQ2_PKT_SIZE + NET_IP_ALIGN);
+ EHEA_RQ2_PKT_SIZE);
}
@@ -508,7 +509,7 @@ static int ehea_refill_rq3(struct ehea_port_res *pr, int nr_of_wqes)
{
return ehea_refill_rq_def(pr, &pr->rq3_skba, 3,
nr_of_wqes, EHEA_RWQE3_TYPE,
- EHEA_MAX_PACKET_SIZE + NET_IP_ALIGN);
+ EHEA_MAX_PACKET_SIZE);
}
static inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num)
@@ -656,8 +657,8 @@ static int get_skb_hdr(struct sk_buff *skb, void **iphdr,
static void ehea_proc_skb(struct ehea_port_res *pr, struct ehea_cqe *cqe,
struct sk_buff *skb)
{
- int vlan_extracted = (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
- && pr->port->vgrp;
+ int vlan_extracted = ((cqe->status & EHEA_CQE_VLAN_TAG_XTRACT) &&
+ pr->port->vgrp);
if (use_lro) {
if (vlan_extracted)
@@ -1388,8 +1389,8 @@ out:
int ehea_rem_smrs(struct ehea_port_res *pr)
{
- if ((ehea_rem_mr(&pr->send_mr))
- || (ehea_rem_mr(&pr->recv_mr)))
+ if ((ehea_rem_mr(&pr->send_mr)) ||
+ (ehea_rem_mr(&pr->recv_mr)))
return -EIO;
else
return 0;
@@ -2030,8 +2031,8 @@ static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
write_ip_start_end(swqe, skb);
if (iph->protocol == IPPROTO_UDP) {
- if ((iph->frag_off & IP_MF)
- || (iph->frag_off & IP_OFFSET))
+ if ((iph->frag_off & IP_MF) ||
+ (iph->frag_off & IP_OFFSET))
/* IP fragment, so don't change cs */
swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM;
else
@@ -2076,8 +2077,8 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
write_tcp_offset_end(swqe, skb);
} else if (iph->protocol == IPPROTO_UDP) {
- if ((iph->frag_off & IP_MF)
- || (iph->frag_off & IP_OFFSET))
+ if ((iph->frag_off & IP_MF) ||
+ (iph->frag_off & IP_OFFSET))
/* IP fragment, so don't change cs */
swqe->tx_control |= EHEA_SWQE_CRC
| EHEA_SWQE_IMM_DATA_PRESENT;
diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ehea/ehea_qmr.c
index bc7c5b7abb8..18d405f78c0 100644
--- a/drivers/net/ehea/ehea_qmr.c
+++ b/drivers/net/ehea/ehea_qmr.c
@@ -837,8 +837,8 @@ static u64 ehea_reg_mr_section(int top, int dir, int idx, u64 *pt,
hret = ehea_h_register_rpage_mr(adapter->handle, mr->handle, 0,
0, pt_abs, EHEA_MAX_RPAGE);
- if ((hret != H_SUCCESS)
- && (hret != H_PAGE_REGISTERED)) {
+ if ((hret != H_SUCCESS) &&
+ (hret != H_PAGE_REGISTERED)) {
ehea_h_free_resource(adapter->handle, mr->handle,
FORCE_FREE);
ehea_error("register_rpage_mr failed");
diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c
index d69d52ed772..f875751af15 100644
--- a/drivers/net/enic/enic_main.c
+++ b/drivers/net/enic/enic_main.c
@@ -870,19 +870,6 @@ static void enic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
dev_kfree_skb_any(buf->os_buf);
}
-static inline struct sk_buff *enic_rq_alloc_skb(struct net_device *netdev,
- unsigned int size)
-{
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(netdev, size + NET_IP_ALIGN);
-
- if (skb)
- skb_reserve(skb, NET_IP_ALIGN);
-
- return skb;
-}
-
static int enic_rq_alloc_buf(struct vnic_rq *rq)
{
struct enic *enic = vnic_dev_priv(rq->vdev);
@@ -892,7 +879,7 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)
unsigned int os_buf_index = 0;
dma_addr_t dma_addr;
- skb = enic_rq_alloc_skb(netdev, len);
+ skb = netdev_alloc_skb_ip_align(netdev, len);
if (!skb)
return -ENOMEM;
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 641a10d2e84..41494f7b2ec 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -630,8 +630,8 @@ static int mdio_read(struct net_device *dev, int phy_id, int location)
barrier();
if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) {
/* Work around read failure bug. */
- if (phy_id == 1 && location < 6
- && inw(ioaddr + MIIData) == 0xffff) {
+ if (phy_id == 1 && location < 6 &&
+ inw(ioaddr + MIIData) == 0xffff) {
outl(read_cmd, ioaddr + MIICtrl);
continue;
}
@@ -668,7 +668,7 @@ static int epic_open(struct net_device *dev)
outl(0x4001, ioaddr + GENCTL);
napi_enable(&ep->napi);
- if ((retval = request_irq(dev->irq, &epic_interrupt, IRQF_SHARED, dev->name, dev))) {
+ if ((retval = request_irq(dev->irq, epic_interrupt, IRQF_SHARED, dev->name, dev))) {
napi_disable(&ep->napi);
return retval;
}
@@ -1205,8 +1205,8 @@ static int epic_rx(struct net_device *dev, int budget)
}
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(ep->pci_dev,
ep->rx_ring[entry].bufaddr,
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index f7d9ac8324c..bd1db92aec1 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -406,10 +406,10 @@ static int ethoc_rx(struct net_device *dev, int limit)
if (ethoc_update_rx_stats(priv, &bd) == 0) {
int size = bd.stat >> 16;
- struct sk_buff *skb = netdev_alloc_skb(dev, size);
+ struct sk_buff *skb;
size -= 4; /* strip the CRC */
- skb_reserve(skb, 2); /* align TCP/IP header */
+ skb = netdev_alloc_skb_ip_align(dev, size);
if (likely(skb)) {
void *src = phys_to_virt(bd.addr);
@@ -641,7 +641,7 @@ static int ethoc_mdio_probe(struct net_device *dev)
return -ENXIO;
}
- phy = phy_connect(dev, dev_name(&phy->dev), &ethoc_mdio_poll, 0,
+ phy = phy_connect(dev, dev_name(&phy->dev), ethoc_mdio_poll, 0,
PHY_INTERFACE_MODE_GMII);
if (IS_ERR(phy)) {
dev_err(&dev->dev, "could not attach to PHY\n");
diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c
index 18d5fbb9673..dac4e595589 100644
--- a/drivers/net/fealnx.c
+++ b/drivers/net/fealnx.c
@@ -839,7 +839,7 @@ static int netdev_open(struct net_device *dev)
iowrite32(0x00000001, ioaddr + BCR); /* Reset */
- if (request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev))
+ if (request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev))
return -EAGAIN;
for (i = 0; i < 3; i++)
@@ -1629,8 +1629,8 @@ static int netdev_rx(struct net_device *dev)
if (debug)
printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", rx_status);
- if ((!((rx_status & RXFSD) && (rx_status & RXLSD)))
- || (rx_status & ErrorSummary)) {
+ if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) ||
+ (rx_status & ErrorSummary)) {
if (rx_status & ErrorSummary) { /* there was a fatal error */
if (debug)
printk(KERN_DEBUG
@@ -1655,8 +1655,8 @@ static int netdev_rx(struct net_device *dev)
cur = np->cur_rx;
while (desno <= np->really_rx_count) {
++desno;
- if ((!(cur->status & RXOWN))
- && (cur->status & RXLSD))
+ if ((!(cur->status & RXOWN)) &&
+ (cur->status & RXLSD))
break;
/* goto next rx descriptor */
cur = cur->next_desc_logical;
@@ -1786,8 +1786,8 @@ static void __set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
memset(mc_filter, 0xff, sizeof(mc_filter));
rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to match, or accept all multicasts. */
memset(mc_filter, 0xff, sizeof(mc_filter));
rx_mode = CR_W_AB | CR_W_AM;
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c
index 66dace6d324..6407672b28e 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/fec_mpc52xx.c
@@ -226,17 +226,17 @@ static int mpc52xx_fec_open(struct net_device *dev)
phy_start(priv->phydev);
}
- if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_SHARED,
+ if (request_irq(dev->irq, mpc52xx_fec_interrupt, IRQF_SHARED,
DRIVER_NAME "_ctrl", dev)) {
dev_err(&dev->dev, "ctrl interrupt request failed\n");
goto free_phy;
}
- if (request_irq(priv->r_irq, &mpc52xx_fec_rx_interrupt, 0,
+ if (request_irq(priv->r_irq, mpc52xx_fec_rx_interrupt, 0,
DRIVER_NAME "_rx", dev)) {
dev_err(&dev->dev, "rx interrupt request failed\n");
goto free_ctrl_irq;
}
- if (request_irq(priv->t_irq, &mpc52xx_fec_tx_interrupt, 0,
+ if (request_irq(priv->t_irq, mpc52xx_fec_tx_interrupt, 0,
DRIVER_NAME "_tx", dev)) {
dev_err(&dev->dev, "tx interrupt request failed\n");
goto free_2irqs;
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 3116601dbfe..3c340489804 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -4004,7 +4004,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
/* Request irq for rx handling */
sprintf(np->name_rx, "%s-rx", dev->name);
if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector,
- &nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev) != 0) {
+ nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev) != 0) {
printk(KERN_INFO "forcedeth: request_irq failed for rx %d\n", ret);
pci_disable_msix(np->pci_dev);
np->msi_flags &= ~NV_MSI_X_ENABLED;
@@ -4013,7 +4013,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
/* Request irq for tx handling */
sprintf(np->name_tx, "%s-tx", dev->name);
if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector,
- &nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev) != 0) {
+ nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev) != 0) {
printk(KERN_INFO "forcedeth: request_irq failed for tx %d\n", ret);
pci_disable_msix(np->pci_dev);
np->msi_flags &= ~NV_MSI_X_ENABLED;
@@ -4022,7 +4022,7 @@ static int nv_request_irq(struct net_device *dev, int intr_test)
/* Request irq for link and timer handling */
sprintf(np->name_other, "%s-other", dev->name);
if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector,
- &nv_nic_irq_other, IRQF_SHARED, np->name_other, dev) != 0) {
+ nv_nic_irq_other, IRQF_SHARED, np->name_other, dev) != 0) {
printk(KERN_INFO "forcedeth: request_irq failed for link %d\n", ret);
pci_disable_msix(np->pci_dev);
np->msi_flags &= ~NV_MSI_X_ENABLED;
diff --git a/drivers/net/fsl_pq_mdio.c b/drivers/net/fsl_pq_mdio.c
index efbf67689ec..25fabb3eedc 100644
--- a/drivers/net/fsl_pq_mdio.c
+++ b/drivers/net/fsl_pq_mdio.c
@@ -3,8 +3,9 @@
* Provides Bus interface for MIIM regs
*
* Author: Andy Fleming <afleming@freescale.com>
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2004,2008 Freescale Semiconductor, Inc.
+ * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc.
*
* Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips)
*
@@ -102,13 +103,18 @@ int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs,
return value;
}
+static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus)
+{
+ return (void __iomem __force *)bus->priv;
+}
+
/*
* Write value to the PHY at mii_id at register regnum,
* on the bus, waiting until the write is done before returning.
*/
int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
{
- struct fsl_pq_mdio __iomem *regs = (void __iomem *)bus->priv;
+ struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
/* Write to the local MII regs */
return(fsl_pq_local_mdio_write(regs, mii_id, regnum, value));
@@ -120,7 +126,7 @@ int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
*/
int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
{
- struct fsl_pq_mdio __iomem *regs = (void __iomem *)bus->priv;
+ struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
/* Read the local MII regs */
return(fsl_pq_local_mdio_read(regs, mii_id, regnum));
@@ -129,7 +135,7 @@ int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
/* Reset the MIIM registers, and wait for the bus to free */
static int fsl_pq_mdio_reset(struct mii_bus *bus)
{
- struct fsl_pq_mdio __iomem *regs = (void __iomem *)bus->priv;
+ struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
int timeout = PHY_INIT_TIMEOUT;
mutex_lock(&bus->mdio_lock);
@@ -189,19 +195,29 @@ static int fsl_pq_mdio_find_free(struct mii_bus *new_bus)
#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
-static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs)
+static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs, struct device_node *np)
{
struct gfar __iomem *enet_regs;
+ u32 __iomem *ioremap_tbipa;
+ u64 addr, size;
/*
* This is mildly evil, but so is our hardware for doing this.
* Also, we have to cast back to struct gfar because of
* definition weirdness done in gianfar.h.
*/
- enet_regs = (struct gfar __iomem *)
- ((char __iomem *)regs - offsetof(struct gfar, gfar_mii_regs));
-
- return &enet_regs->tbipa;
+ if(of_device_is_compatible(np, "fsl,gianfar-mdio") ||
+ of_device_is_compatible(np, "fsl,gianfar-tbi") ||
+ of_device_is_compatible(np, "gianfar")) {
+ enet_regs = (struct gfar __iomem *)regs;
+ return &enet_regs->tbipa;
+ } else if (of_device_is_compatible(np, "fsl,etsec2-mdio") ||
+ of_device_is_compatible(np, "fsl,etsec2-tbi")) {
+ addr = of_translate_address(np, of_get_address(np, 1, &size, NULL));
+ ioremap_tbipa = ioremap(addr, size);
+ return ioremap_tbipa;
+ } else
+ return NULL;
}
#endif
@@ -250,11 +266,12 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
{
struct device_node *np = ofdev->node;
struct device_node *tbi;
- struct fsl_pq_mdio __iomem *regs;
+ struct fsl_pq_mdio __iomem *regs = NULL;
+ void __iomem *map;
u32 __iomem *tbipa;
struct mii_bus *new_bus;
int tbiaddr = -1;
- u64 addr, size;
+ u64 addr = 0, size = 0;
int err = 0;
new_bus = mdiobus_alloc();
@@ -269,13 +286,19 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
/* Set the PHY base address */
addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
- regs = ioremap(addr, size);
-
- if (NULL == regs) {
+ map = ioremap(addr, size);
+ if (!map) {
err = -ENOMEM;
goto err_free_bus;
}
+ if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
+ of_device_is_compatible(np, "fsl,gianfar-tbi") ||
+ of_device_is_compatible(np, "fsl,ucc-mdio") ||
+ of_device_is_compatible(np, "ucc_geth_phy"))
+ map -= offsetof(struct fsl_pq_mdio, miimcfg);
+ regs = map;
+
new_bus->priv = (void __force *)regs;
new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
@@ -290,9 +313,15 @@ static int fsl_pq_mdio_probe(struct of_device *ofdev,
if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
of_device_is_compatible(np, "fsl,gianfar-tbi") ||
+ of_device_is_compatible(np, "fsl,etsec2-mdio") ||
+ of_device_is_compatible(np, "fsl,etsec2-tbi") ||
of_device_is_compatible(np, "gianfar")) {
#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
- tbipa = get_gfar_tbipa(regs);
+ tbipa = get_gfar_tbipa(regs, np);
+ if (!tbipa) {
+ err = -EINVAL;
+ goto err_free_irqs;
+ }
#else
err = -ENODEV;
goto err_free_irqs;
@@ -380,7 +409,7 @@ static int fsl_pq_mdio_remove(struct of_device *ofdev)
dev_set_drvdata(device, NULL);
- iounmap((void __iomem *)bus->priv);
+ iounmap(fsl_pq_mdio_get_regs(bus));
bus->priv = NULL;
mdiobus_free(bus);
@@ -405,6 +434,12 @@ static struct of_device_id fsl_pq_mdio_match[] = {
{
.compatible = "fsl,gianfar-mdio",
},
+ {
+ .compatible = "fsl,etsec2-tbi",
+ },
+ {
+ .compatible = "fsl,etsec2-mdio",
+ },
{},
};
MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match);
diff --git a/drivers/net/fsl_pq_mdio.h b/drivers/net/fsl_pq_mdio.h
index 36dad527410..1f7d865cedb 100644
--- a/drivers/net/fsl_pq_mdio.h
+++ b/drivers/net/fsl_pq_mdio.h
@@ -3,8 +3,9 @@
* Driver for the MDIO bus controller on Freescale PowerQUICC processors
*
* Author: Andy Fleming
+ * Modifier: Sandeep Gopalpet
*
- * Copyright (c) 2002-2004,2008 Freescale Semiconductor, Inc.
+ * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -23,6 +24,12 @@
#define MII_READ_COMMAND 0x00000001
struct fsl_pq_mdio {
+ u8 res1[16];
+ u32 ieventm; /* MDIO Interrupt event register (for etsec2)*/
+ u32 imaskm; /* MDIO Interrupt mask register (for etsec2)*/
+ u8 res2[4];
+ u32 emapm; /* MDIO Event mapping register (for etsec2)*/
+ u8 res3[1280];
u32 miimcfg; /* MII management configuration reg */
u32 miimcom; /* MII management command reg */
u32 miimadd; /* MII management address reg */
@@ -31,9 +38,9 @@ struct fsl_pq_mdio {
u32 miimind; /* MII management indication reg */
u8 reserved[28]; /* Space holder */
u32 utbipar; /* TBI phy address reg (only on UCC) */
+ u8 res4[2728];
} __attribute__ ((packed));
-
int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id,
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 5bf31f1509c..16def131c39 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -8,9 +8,10 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2006 Freescale Semiconductor, Inc.
- * Copyright (c) 2007 MontaVista Software, Inc.
+ * Copyright 2002-2009 Freescale Semiconductor, Inc.
+ * Copyright 2007 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -109,7 +110,7 @@ static void gfar_reset_task(struct work_struct *work);
static void gfar_timeout(struct net_device *dev);
static int gfar_close(struct net_device *dev);
struct sk_buff *gfar_new_skb(struct net_device *dev);
-static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
+static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
struct sk_buff *skb);
static int gfar_set_mac_address(struct net_device *dev);
static int gfar_change_mtu(struct net_device *dev, int new_mtu);
@@ -130,8 +131,8 @@ static int gfar_poll(struct napi_struct *napi, int budget);
#ifdef CONFIG_NET_POLL_CONTROLLER
static void gfar_netpoll(struct net_device *dev);
#endif
-int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
-static int gfar_clean_tx_ring(struct net_device *dev);
+int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
+static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int amount_pull);
static void gfar_vlan_rx_register(struct net_device *netdev,
@@ -142,11 +143,277 @@ void gfar_start(struct net_device *dev);
static void gfar_clear_exact_match(struct net_device *dev);
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);
static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb);
MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
MODULE_LICENSE("GPL");
+static void gfar_init_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
+ dma_addr_t buf)
+{
+ u32 lstatus;
+
+ bdp->bufPtr = buf;
+
+ lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT);
+ if (bdp == rx_queue->rx_bd_base + rx_queue->rx_ring_size - 1)
+ lstatus |= BD_LFLAG(RXBD_WRAP);
+
+ eieio();
+
+ bdp->lstatus = lstatus;
+}
+
+static int gfar_init_bds(struct net_device *ndev)
+{
+ struct gfar_private *priv = netdev_priv(ndev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct txbd8 *txbdp;
+ struct rxbd8 *rxbdp;
+ int i, j;
+
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ /* Initialize some variables in our dev structure */
+ tx_queue->num_txbdfree = tx_queue->tx_ring_size;
+ tx_queue->dirty_tx = tx_queue->tx_bd_base;
+ tx_queue->cur_tx = tx_queue->tx_bd_base;
+ tx_queue->skb_curtx = 0;
+ tx_queue->skb_dirtytx = 0;
+
+ /* Initialize Transmit Descriptor Ring */
+ txbdp = tx_queue->tx_bd_base;
+ for (j = 0; j < tx_queue->tx_ring_size; j++) {
+ txbdp->lstatus = 0;
+ txbdp->bufPtr = 0;
+ txbdp++;
+ }
+
+ /* Set the last descriptor in the ring to indicate wrap */
+ txbdp--;
+ txbdp->status |= TXBD_WRAP;
+ }
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->cur_rx = rx_queue->rx_bd_base;
+ rx_queue->skb_currx = 0;
+ rxbdp = rx_queue->rx_bd_base;
+
+ for (j = 0; j < rx_queue->rx_ring_size; j++) {
+ struct sk_buff *skb = rx_queue->rx_skbuff[j];
+
+ if (skb) {
+ gfar_init_rxbdp(rx_queue, rxbdp,
+ rxbdp->bufPtr);
+ } else {
+ skb = gfar_new_skb(ndev);
+ if (!skb) {
+ pr_err("%s: Can't allocate RX buffers\n",
+ ndev->name);
+ goto err_rxalloc_fail;
+ }
+ rx_queue->rx_skbuff[j] = skb;
+
+ gfar_new_rxbdp(rx_queue, rxbdp, skb);
+ }
+
+ rxbdp++;
+ }
+
+ }
+
+ return 0;
+
+err_rxalloc_fail:
+ free_skb_resources(priv);
+ return -ENOMEM;
+}
+
+static int gfar_alloc_skb_resources(struct net_device *ndev)
+{
+ void *vaddr;
+ dma_addr_t addr;
+ int i, j, k;
+ struct gfar_private *priv = netdev_priv(ndev);
+ struct device *dev = &priv->ofdev->dev;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+
+ priv->total_tx_ring_size = 0;
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->total_tx_ring_size += priv->tx_queue[i]->tx_ring_size;
+
+ priv->total_rx_ring_size = 0;
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->total_rx_ring_size += priv->rx_queue[i]->rx_ring_size;
+
+ /* Allocate memory for the buffer descriptors */
+ vaddr = dma_alloc_coherent(dev,
+ sizeof(struct txbd8) * priv->total_tx_ring_size +
+ sizeof(struct rxbd8) * priv->total_rx_ring_size,
+ &addr, GFP_KERNEL);
+ if (!vaddr) {
+ if (netif_msg_ifup(priv))
+ pr_err("%s: Could not allocate buffer descriptors!\n",
+ ndev->name);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ tx_queue->tx_bd_base = (struct txbd8 *) vaddr;
+ tx_queue->tx_bd_dma_base = addr;
+ tx_queue->dev = ndev;
+ /* enet DMA only understands physical addresses */
+ addr += sizeof(struct txbd8) *tx_queue->tx_ring_size;
+ vaddr += sizeof(struct txbd8) *tx_queue->tx_ring_size;
+ }
+
+ /* Start the rx descriptor ring where the tx ring leaves off */
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->rx_bd_base = (struct rxbd8 *) vaddr;
+ rx_queue->rx_bd_dma_base = addr;
+ rx_queue->dev = ndev;
+ addr += sizeof (struct rxbd8) * rx_queue->rx_ring_size;
+ vaddr += sizeof (struct rxbd8) * rx_queue->rx_ring_size;
+ }
+
+ /* Setup the skbuff rings */
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) *
+ tx_queue->tx_ring_size, GFP_KERNEL);
+ if (!tx_queue->tx_skbuff) {
+ if (netif_msg_ifup(priv))
+ pr_err("%s: Could not allocate tx_skbuff\n",
+ ndev->name);
+ goto cleanup;
+ }
+
+ for (k = 0; k < tx_queue->tx_ring_size; k++)
+ tx_queue->tx_skbuff[k] = NULL;
+ }
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) *
+ rx_queue->rx_ring_size, GFP_KERNEL);
+
+ if (!rx_queue->rx_skbuff) {
+ if (netif_msg_ifup(priv))
+ pr_err("%s: Could not allocate rx_skbuff\n",
+ ndev->name);
+ goto cleanup;
+ }
+
+ for (j = 0; j < rx_queue->rx_ring_size; j++)
+ rx_queue->rx_skbuff[j] = NULL;
+ }
+
+ if (gfar_init_bds(ndev))
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ free_skb_resources(priv);
+ return -ENOMEM;
+}
+
+static void gfar_init_tx_rx_base(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 __iomem *baddr;
+ int i;
+
+ baddr = &regs->tbase0;
+ for(i = 0; i < priv->num_tx_queues; i++) {
+ gfar_write(baddr, priv->tx_queue[i]->tx_bd_dma_base);
+ baddr += 2;
+ }
+
+ baddr = &regs->rbase0;
+ for(i = 0; i < priv->num_rx_queues; i++) {
+ gfar_write(baddr, priv->rx_queue[i]->rx_bd_dma_base);
+ baddr += 2;
+ }
+}
+
+static void gfar_init_mac(struct net_device *ndev)
+{
+ struct gfar_private *priv = netdev_priv(ndev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 rctrl = 0;
+ u32 tctrl = 0;
+ u32 attrs = 0;
+
+ /* write the tx/rx base registers */
+ gfar_init_tx_rx_base(priv);
+
+ /* Configure the coalescing support */
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
+
+ if (priv->rx_filer_enable)
+ rctrl |= RCTRL_FILREN;
+
+ if (priv->rx_csum_enable)
+ rctrl |= RCTRL_CHECKSUMMING;
+
+ if (priv->extended_hash) {
+ rctrl |= RCTRL_EXTHASH;
+
+ gfar_clear_exact_match(ndev);
+ rctrl |= RCTRL_EMEN;
+ }
+
+ if (priv->padding) {
+ rctrl &= ~RCTRL_PAL_MASK;
+ rctrl |= RCTRL_PADDING(priv->padding);
+ }
+
+ /* keep vlan related bits if it's enabled */
+ if (priv->vlgrp) {
+ rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
+ tctrl |= TCTRL_VLINS;
+ }
+
+ /* Init rctrl based on our settings */
+ gfar_write(&regs->rctrl, rctrl);
+
+ if (ndev->features & NETIF_F_IP_CSUM)
+ tctrl |= TCTRL_INIT_CSUM;
+
+ tctrl |= TCTRL_TXSCHED_PRIO;
+
+ gfar_write(&regs->tctrl, tctrl);
+
+ /* Set the extraction length and index */
+ attrs = ATTRELI_EL(priv->rx_stash_size) |
+ ATTRELI_EI(priv->rx_stash_index);
+
+ gfar_write(&regs->attreli, attrs);
+
+ /* Start with defaults, and add stashing or locking
+ * depending on the approprate variables */
+ attrs = ATTR_INIT_SETTINGS;
+
+ if (priv->bd_stash_en)
+ attrs |= ATTR_BDSTASH;
+
+ if (priv->rx_stash_size != 0)
+ attrs |= ATTR_BUFSTASH;
+
+ gfar_write(&regs->attr, attrs);
+
+ gfar_write(&regs->fifo_tx_thr, priv->fifo_threshold);
+ gfar_write(&regs->fifo_tx_starve, priv->fifo_starve);
+ gfar_write(&regs->fifo_tx_starve_shutoff, priv->fifo_starve_off);
+}
+
static const struct net_device_ops gfar_netdev_ops = {
.ndo_open = gfar_enet_open,
.ndo_start_xmit = gfar_start_xmit,
@@ -155,6 +422,7 @@ static const struct net_device_ops gfar_netdev_ops = {
.ndo_set_multicast_list = gfar_set_multi,
.ndo_tx_timeout = gfar_timeout,
.ndo_do_ioctl = gfar_ioctl,
+ .ndo_select_queue = gfar_select_queue,
.ndo_vlan_rx_register = gfar_vlan_rx_register,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
@@ -163,56 +431,252 @@ static const struct net_device_ops gfar_netdev_ops = {
#endif
};
+unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
+unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
+
+void lock_rx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ spin_lock(&priv->rx_queue[i]->rxlock);
+}
+
+void lock_tx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ spin_lock(&priv->tx_queue[i]->txlock);
+}
+
+void unlock_rx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ spin_unlock(&priv->rx_queue[i]->rxlock);
+}
+
+void unlock_tx_qs(struct gfar_private *priv)
+{
+ int i = 0x0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ spin_unlock(&priv->tx_queue[i]->txlock);
+}
+
/* Returns 1 if incoming frames use an FCB */
static inline int gfar_uses_fcb(struct gfar_private *priv)
{
return priv->vlgrp || priv->rx_csum_enable;
}
-static int gfar_of_init(struct net_device *dev)
+u16 gfar_select_queue(struct net_device *dev, struct sk_buff *skb)
+{
+ return skb_get_queue_mapping(skb);
+}
+static void free_tx_pointers(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_tx_queues; i++)
+ kfree(priv->tx_queue[i]);
+}
+
+static void free_rx_pointers(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_rx_queues; i++)
+ kfree(priv->rx_queue[i]);
+}
+
+static void unmap_group_regs(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < MAXGROUPS; i++)
+ if (priv->gfargrp[i].regs)
+ iounmap(priv->gfargrp[i].regs);
+}
+
+static void disable_napi(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_grps; i++)
+ napi_disable(&priv->gfargrp[i].napi);
+}
+
+static void enable_napi(struct gfar_private *priv)
+{
+ int i = 0;
+
+ for (i = 0; i < priv->num_grps; i++)
+ napi_enable(&priv->gfargrp[i].napi);
+}
+
+static int gfar_parse_group(struct device_node *np,
+ struct gfar_private *priv, const char *model)
+{
+ u32 *queue_mask;
+ u64 addr, size;
+
+ addr = of_translate_address(np,
+ of_get_address(np, 0, &size, NULL));
+ priv->gfargrp[priv->num_grps].regs = ioremap(addr, size);
+
+ if (!priv->gfargrp[priv->num_grps].regs)
+ return -ENOMEM;
+
+ priv->gfargrp[priv->num_grps].interruptTransmit =
+ irq_of_parse_and_map(np, 0);
+
+ /* If we aren't the FEC we have multiple interrupts */
+ if (model && strcasecmp(model, "FEC")) {
+ priv->gfargrp[priv->num_grps].interruptReceive =
+ irq_of_parse_and_map(np, 1);
+ priv->gfargrp[priv->num_grps].interruptError =
+ irq_of_parse_and_map(np,2);
+ if (priv->gfargrp[priv->num_grps].interruptTransmit < 0 ||
+ priv->gfargrp[priv->num_grps].interruptReceive < 0 ||
+ priv->gfargrp[priv->num_grps].interruptError < 0) {
+ return -EINVAL;
+ }
+ }
+
+ priv->gfargrp[priv->num_grps].grp_id = priv->num_grps;
+ priv->gfargrp[priv->num_grps].priv = priv;
+ spin_lock_init(&priv->gfargrp[priv->num_grps].grplock);
+ if(priv->mode == MQ_MG_MODE) {
+ queue_mask = (u32 *)of_get_property(np,
+ "fsl,rx-bit-map", NULL);
+ priv->gfargrp[priv->num_grps].rx_bit_map =
+ queue_mask ? *queue_mask :(DEFAULT_MAPPING >> priv->num_grps);
+ queue_mask = (u32 *)of_get_property(np,
+ "fsl,tx-bit-map", NULL);
+ priv->gfargrp[priv->num_grps].tx_bit_map =
+ queue_mask ? *queue_mask : (DEFAULT_MAPPING >> priv->num_grps);
+ } else {
+ priv->gfargrp[priv->num_grps].rx_bit_map = 0xFF;
+ priv->gfargrp[priv->num_grps].tx_bit_map = 0xFF;
+ }
+ priv->num_grps++;
+
+ return 0;
+}
+
+static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
{
const char *model;
const char *ctype;
const void *mac_addr;
- u64 addr, size;
- int err = 0;
- struct gfar_private *priv = netdev_priv(dev);
- struct device_node *np = priv->node;
+ int err = 0, i;
+ struct net_device *dev = NULL;
+ struct gfar_private *priv = NULL;
+ struct device_node *np = ofdev->node;
+ struct device_node *child = NULL;
const u32 *stash;
const u32 *stash_len;
const u32 *stash_idx;
+ unsigned int num_tx_qs, num_rx_qs;
+ u32 *tx_queues, *rx_queues;
if (!np || !of_device_is_available(np))
return -ENODEV;
- /* get a pointer to the register memory */
- addr = of_translate_address(np, of_get_address(np, 0, &size, NULL));
- priv->regs = ioremap(addr, size);
+ /* parse the num of tx and rx queues */
+ tx_queues = (u32 *)of_get_property(np, "fsl,num_tx_queues", NULL);
+ num_tx_qs = tx_queues ? *tx_queues : 1;
+
+ if (num_tx_qs > MAX_TX_QS) {
+ printk(KERN_ERR "num_tx_qs(=%d) greater than MAX_TX_QS(=%d)\n",
+ num_tx_qs, MAX_TX_QS);
+ printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ return -EINVAL;
+ }
+
+ rx_queues = (u32 *)of_get_property(np, "fsl,num_rx_queues", NULL);
+ num_rx_qs = rx_queues ? *rx_queues : 1;
- if (priv->regs == NULL)
+ if (num_rx_qs > MAX_RX_QS) {
+ printk(KERN_ERR "num_rx_qs(=%d) greater than MAX_RX_QS(=%d)\n",
+ num_tx_qs, MAX_TX_QS);
+ printk(KERN_ERR "Cannot do alloc_etherdev, aborting\n");
+ return -EINVAL;
+ }
+
+ *pdev = alloc_etherdev_mq(sizeof(*priv), num_tx_qs);
+ dev = *pdev;
+ if (NULL == dev)
return -ENOMEM;
- priv->interruptTransmit = irq_of_parse_and_map(np, 0);
+ priv = netdev_priv(dev);
+ priv->node = ofdev->node;
+ priv->ndev = dev;
+
+ dev->num_tx_queues = num_tx_qs;
+ dev->real_num_tx_queues = num_tx_qs;
+ priv->num_tx_queues = num_tx_qs;
+ priv->num_rx_queues = num_rx_qs;
+ priv->num_grps = 0x0;
model = of_get_property(np, "model", NULL);
- /* If we aren't the FEC we have multiple interrupts */
- if (model && strcasecmp(model, "FEC")) {
- priv->interruptReceive = irq_of_parse_and_map(np, 1);
+ for (i = 0; i < MAXGROUPS; i++)
+ priv->gfargrp[i].regs = NULL;
+
+ /* Parse and initialize group specific information */
+ if (of_device_is_compatible(np, "fsl,etsec2")) {
+ priv->mode = MQ_MG_MODE;
+ for_each_child_of_node(np, child) {
+ err = gfar_parse_group(child, priv, model);
+ if (err)
+ goto err_grp_init;
+ }
+ } else {
+ priv->mode = SQ_SG_MODE;
+ err = gfar_parse_group(np, priv, model);
+ if(err)
+ goto err_grp_init;
+ }
- priv->interruptError = irq_of_parse_and_map(np, 2);
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i] = NULL;
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i] = NULL;
+
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i] = (struct gfar_priv_tx_q *)kmalloc(
+ sizeof (struct gfar_priv_tx_q), GFP_KERNEL);
+ if (!priv->tx_queue[i]) {
+ err = -ENOMEM;
+ goto tx_alloc_failed;
+ }
+ priv->tx_queue[i]->tx_skbuff = NULL;
+ priv->tx_queue[i]->qindex = i;
+ priv->tx_queue[i]->dev = dev;
+ spin_lock_init(&(priv->tx_queue[i]->txlock));
+ }
- if (priv->interruptTransmit < 0 ||
- priv->interruptReceive < 0 ||
- priv->interruptError < 0) {
- err = -EINVAL;
- goto err_out;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i] = (struct gfar_priv_rx_q *)kmalloc(
+ sizeof (struct gfar_priv_rx_q), GFP_KERNEL);
+ if (!priv->rx_queue[i]) {
+ err = -ENOMEM;
+ goto rx_alloc_failed;
}
+ priv->rx_queue[i]->rx_skbuff = NULL;
+ priv->rx_queue[i]->qindex = i;
+ priv->rx_queue[i]->dev = dev;
+ spin_lock_init(&(priv->rx_queue[i]->rxlock));
}
+
stash = of_get_property(np, "bd-stash", NULL);
- if(stash) {
+ if (stash) {
priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING;
priv->bd_stash_en = 1;
}
@@ -270,8 +734,13 @@ static int gfar_of_init(struct net_device *dev)
return 0;
-err_out:
- iounmap(priv->regs);
+rx_alloc_failed:
+ free_rx_pointers(priv);
+tx_alloc_failed:
+ free_tx_pointers(priv);
+err_grp_init:
+ unmap_group_regs(priv);
+ free_netdev(dev);
return err;
}
@@ -289,6 +758,85 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
}
+static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs)
+{
+ unsigned int new_bit_map = 0x0;
+ int mask = 0x1 << (max_qs - 1), i;
+ for (i = 0; i < max_qs; i++) {
+ if (bit_map & mask)
+ new_bit_map = new_bit_map + (1 << i);
+ mask = mask >> 0x1;
+ }
+ return new_bit_map;
+}
+
+static u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar,
+ u32 class)
+{
+ u32 rqfpr = FPR_FILER_MASK;
+ u32 rqfcr = 0x0;
+
+ rqfar--;
+ rqfcr = RQFCR_CLE | RQFCR_PID_MASK | RQFCR_CMP_EXACT;
+ ftp_rqfpr[rqfar] = rqfpr;
+ ftp_rqfcr[rqfar] = rqfcr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_NOMATCH;
+ ftp_rqfpr[rqfar] = rqfpr;
+ ftp_rqfcr[rqfar] = rqfcr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND;
+ rqfpr = class;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar--;
+ rqfcr = RQFCR_CMP_EXACT | RQFCR_PID_MASK | RQFCR_AND;
+ rqfpr = class;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ return rqfar;
+}
+
+static void gfar_init_filer_table(struct gfar_private *priv)
+{
+ int i = 0x0;
+ u32 rqfar = MAX_FILER_IDX;
+ u32 rqfcr = 0x0;
+ u32 rqfpr = FPR_FILER_MASK;
+
+ /* Default rule */
+ rqfcr = RQFCR_CMP_MATCH;
+ ftp_rqfcr[rqfar] = rqfcr;
+ ftp_rqfpr[rqfar] = rqfpr;
+ gfar_write_filer(priv, rqfar, rqfcr, rqfpr);
+
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_UDP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_TCP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_UDP);
+ rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_TCP);
+
+ /* cur_filer_idx indicated the fisrt non-masked rule */
+ priv->cur_filer_idx = rqfar;
+
+ /* Rest are masked rules */
+ rqfcr = RQFCR_CMP_NOMATCH;
+ for (i = 0; i < rqfar; i++) {
+ ftp_rqfcr[i] = rqfcr;
+ ftp_rqfpr[i] = rqfpr;
+ gfar_write_filer(priv, i, rqfcr, rqfpr);
+ }
+}
+
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
static int gfar_probe(struct of_device *ofdev,
@@ -297,14 +845,17 @@ static int gfar_probe(struct of_device *ofdev,
u32 tempval;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
- int err = 0;
+ struct gfar __iomem *regs = NULL;
+ int err = 0, i, grp_idx = 0;
int len_devname;
+ u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0;
+ u32 isrg = 0;
+ u32 __iomem *baddr;
- /* Create an ethernet device instance */
- dev = alloc_etherdev(sizeof (*priv));
+ err = gfar_of_init(ofdev, &dev);
- if (NULL == dev)
- return -ENOMEM;
+ if (err)
+ return err;
priv = netdev_priv(dev);
priv->ndev = dev;
@@ -312,50 +863,46 @@ static int gfar_probe(struct of_device *ofdev,
priv->node = ofdev->node;
SET_NETDEV_DEV(dev, &ofdev->dev);
- err = gfar_of_init(dev);
-
- if (err)
- goto regs_fail;
-
- spin_lock_init(&priv->txlock);
- spin_lock_init(&priv->rxlock);
spin_lock_init(&priv->bflock);
INIT_WORK(&priv->reset_task, gfar_reset_task);
dev_set_drvdata(&ofdev->dev, priv);
+ regs = priv->gfargrp[0].regs;
/* Stop the DMA engine now, in case it was running before */
/* (The firmware could have used it, and left it running). */
gfar_halt(dev);
/* Reset MAC layer */
- gfar_write(&priv->regs->maccfg1, MACCFG1_SOFT_RESET);
+ gfar_write(&regs->maccfg1, MACCFG1_SOFT_RESET);
/* We need to delay at least 3 TX clocks */
udelay(2);
tempval = (MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
- gfar_write(&priv->regs->maccfg1, tempval);
+ gfar_write(&regs->maccfg1, tempval);
/* Initialize MACCFG2. */
- gfar_write(&priv->regs->maccfg2, MACCFG2_INIT_SETTINGS);
+ gfar_write(&regs->maccfg2, MACCFG2_INIT_SETTINGS);
/* Initialize ECNTRL */
- gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
+ gfar_write(&regs->ecntrl, ECNTRL_INIT_SETTINGS);
/* Set the dev->base_addr to the gfar reg region */
- dev->base_addr = (unsigned long) (priv->regs);
+ dev->base_addr = (unsigned long) regs;
SET_NETDEV_DEV(dev, &ofdev->dev);
/* Fill in the dev structure */
dev->watchdog_timeo = TX_TIMEOUT;
- netif_napi_add(dev, &priv->napi, gfar_poll, GFAR_DEV_WEIGHT);
dev->mtu = 1500;
-
dev->netdev_ops = &gfar_netdev_ops;
dev->ethtool_ops = &gfar_ethtool_ops;
+ /* Register for napi ...We are registering NAPI for each grp */
+ for (i = 0; i < priv->num_grps; i++)
+ netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, GFAR_DEV_WEIGHT);
+
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
priv->rx_csum_enable = 1;
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA;
@@ -371,35 +918,35 @@ static int gfar_probe(struct of_device *ofdev,
priv->extended_hash = 1;
priv->hash_width = 9;
- priv->hash_regs[0] = &priv->regs->igaddr0;
- priv->hash_regs[1] = &priv->regs->igaddr1;
- priv->hash_regs[2] = &priv->regs->igaddr2;
- priv->hash_regs[3] = &priv->regs->igaddr3;
- priv->hash_regs[4] = &priv->regs->igaddr4;
- priv->hash_regs[5] = &priv->regs->igaddr5;
- priv->hash_regs[6] = &priv->regs->igaddr6;
- priv->hash_regs[7] = &priv->regs->igaddr7;
- priv->hash_regs[8] = &priv->regs->gaddr0;
- priv->hash_regs[9] = &priv->regs->gaddr1;
- priv->hash_regs[10] = &priv->regs->gaddr2;
- priv->hash_regs[11] = &priv->regs->gaddr3;
- priv->hash_regs[12] = &priv->regs->gaddr4;
- priv->hash_regs[13] = &priv->regs->gaddr5;
- priv->hash_regs[14] = &priv->regs->gaddr6;
- priv->hash_regs[15] = &priv->regs->gaddr7;
+ priv->hash_regs[0] = &regs->igaddr0;
+ priv->hash_regs[1] = &regs->igaddr1;
+ priv->hash_regs[2] = &regs->igaddr2;
+ priv->hash_regs[3] = &regs->igaddr3;
+ priv->hash_regs[4] = &regs->igaddr4;
+ priv->hash_regs[5] = &regs->igaddr5;
+ priv->hash_regs[6] = &regs->igaddr6;
+ priv->hash_regs[7] = &regs->igaddr7;
+ priv->hash_regs[8] = &regs->gaddr0;
+ priv->hash_regs[9] = &regs->gaddr1;
+ priv->hash_regs[10] = &regs->gaddr2;
+ priv->hash_regs[11] = &regs->gaddr3;
+ priv->hash_regs[12] = &regs->gaddr4;
+ priv->hash_regs[13] = &regs->gaddr5;
+ priv->hash_regs[14] = &regs->gaddr6;
+ priv->hash_regs[15] = &regs->gaddr7;
} else {
priv->extended_hash = 0;
priv->hash_width = 8;
- priv->hash_regs[0] = &priv->regs->gaddr0;
- priv->hash_regs[1] = &priv->regs->gaddr1;
- priv->hash_regs[2] = &priv->regs->gaddr2;
- priv->hash_regs[3] = &priv->regs->gaddr3;
- priv->hash_regs[4] = &priv->regs->gaddr4;
- priv->hash_regs[5] = &priv->regs->gaddr5;
- priv->hash_regs[6] = &priv->regs->gaddr6;
- priv->hash_regs[7] = &priv->regs->gaddr7;
+ priv->hash_regs[0] = &regs->gaddr0;
+ priv->hash_regs[1] = &regs->gaddr1;
+ priv->hash_regs[2] = &regs->gaddr2;
+ priv->hash_regs[3] = &regs->gaddr3;
+ priv->hash_regs[4] = &regs->gaddr4;
+ priv->hash_regs[5] = &regs->gaddr5;
+ priv->hash_regs[6] = &regs->gaddr6;
+ priv->hash_regs[7] = &regs->gaddr7;
}
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_PADDING)
@@ -410,15 +957,70 @@ static int gfar_probe(struct of_device *ofdev,
if (dev->features & NETIF_F_IP_CSUM)
dev->hard_header_len += GMAC_FCB_LEN;
+ /* Program the isrg regs only if number of grps > 1 */
+ if (priv->num_grps > 1) {
+ baddr = &regs->isrg0;
+ for (i = 0; i < priv->num_grps; i++) {
+ isrg |= (priv->gfargrp[i].rx_bit_map << ISRG_SHIFT_RX);
+ isrg |= (priv->gfargrp[i].tx_bit_map << ISRG_SHIFT_TX);
+ gfar_write(baddr, isrg);
+ baddr++;
+ isrg = 0x0;
+ }
+ }
+
+ /* Need to reverse the bit maps as bit_map's MSB is q0
+ * but, for_each_bit parses from right to left, which
+ * basically reverses the queue numbers */
+ for (i = 0; i< priv->num_grps; i++) {
+ priv->gfargrp[i].tx_bit_map = reverse_bitmap(
+ priv->gfargrp[i].tx_bit_map, MAX_TX_QS);
+ priv->gfargrp[i].rx_bit_map = reverse_bitmap(
+ priv->gfargrp[i].rx_bit_map, MAX_RX_QS);
+ }
+
+ /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values,
+ * also assign queues to groups */
+ for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) {
+ priv->gfargrp[grp_idx].num_rx_queues = 0x0;
+ for_each_bit(i, &priv->gfargrp[grp_idx].rx_bit_map,
+ priv->num_rx_queues) {
+ priv->gfargrp[grp_idx].num_rx_queues++;
+ priv->rx_queue[i]->grp = &priv->gfargrp[grp_idx];
+ rstat = rstat | (RSTAT_CLEAR_RHALT >> i);
+ rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i);
+ }
+ priv->gfargrp[grp_idx].num_tx_queues = 0x0;
+ for_each_bit (i, &priv->gfargrp[grp_idx].tx_bit_map,
+ priv->num_tx_queues) {
+ priv->gfargrp[grp_idx].num_tx_queues++;
+ priv->tx_queue[i]->grp = &priv->gfargrp[grp_idx];
+ tstat = tstat | (TSTAT_CLEAR_THALT >> i);
+ tqueue = tqueue | (TQUEUE_EN0 >> i);
+ }
+ priv->gfargrp[grp_idx].rstat = rstat;
+ priv->gfargrp[grp_idx].tstat = tstat;
+ rstat = tstat =0;
+ }
+
+ gfar_write(&regs->rqueue, rqueue);
+ gfar_write(&regs->tqueue, tqueue);
+
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
- priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
- priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
- priv->num_txbdfree = DEFAULT_TX_RING_SIZE;
- priv->txcoalescing = DEFAULT_TX_COALESCE;
- priv->txic = DEFAULT_TXIC;
- priv->rxcoalescing = DEFAULT_RX_COALESCE;
- priv->rxic = DEFAULT_RXIC;
+ /* Initializing some of the rx/tx queue level parameters */
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i]->tx_ring_size = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue[i]->num_txbdfree = DEFAULT_TX_RING_SIZE;
+ priv->tx_queue[i]->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->tx_queue[i]->txic = DEFAULT_TXIC;
+ }
+
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rx_ring_size = DEFAULT_RX_RING_SIZE;
+ priv->rx_queue[i]->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rx_queue[i]->rxic = DEFAULT_RXIC;
+ }
/* Enable most messages by default */
priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
@@ -439,20 +1041,43 @@ static int gfar_probe(struct of_device *ofdev,
/* fill out IRQ number and name fields */
len_devname = strlen(dev->name);
- strncpy(&priv->int_name_tx[0], dev->name, len_devname);
- if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- strncpy(&priv->int_name_tx[len_devname],
- "_tx", sizeof("_tx") + 1);
-
- strncpy(&priv->int_name_rx[0], dev->name, len_devname);
- strncpy(&priv->int_name_rx[len_devname],
- "_rx", sizeof("_rx") + 1);
+ for (i = 0; i < priv->num_grps; i++) {
+ strncpy(&priv->gfargrp[i].int_name_tx[0], dev->name,
+ len_devname);
+ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
+ strncpy(&priv->gfargrp[i].int_name_tx[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_tx[
+ strlen(priv->gfargrp[i].int_name_tx)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_tx[strlen(
+ priv->gfargrp[i].int_name_tx)],
+ "_tx", sizeof("_tx") + 1);
+
+ strncpy(&priv->gfargrp[i].int_name_rx[0], dev->name,
+ len_devname);
+ strncpy(&priv->gfargrp[i].int_name_rx[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_rx[
+ strlen(priv->gfargrp[i].int_name_rx)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_rx[strlen(
+ priv->gfargrp[i].int_name_rx)],
+ "_rx", sizeof("_rx") + 1);
+
+ strncpy(&priv->gfargrp[i].int_name_er[0], dev->name,
+ len_devname);
+ strncpy(&priv->gfargrp[i].int_name_er[len_devname],
+ "_g", sizeof("_g"));
+ priv->gfargrp[i].int_name_er[strlen(
+ priv->gfargrp[i].int_name_er)] = i+48;
+ strncpy(&priv->gfargrp[i].int_name_er[strlen(\
+ priv->gfargrp[i].int_name_er)],
+ "_er", sizeof("_er") + 1);
+ } else
+ priv->gfargrp[i].int_name_tx[len_devname] = '\0';
+ }
- strncpy(&priv->int_name_er[0], dev->name, len_devname);
- strncpy(&priv->int_name_er[len_devname],
- "_er", sizeof("_er") + 1);
- } else
- priv->int_name_tx[len_devname] = '\0';
+ /* Initialize the filer table */
+ gfar_init_filer_table(priv);
/* Create all the sysfs files */
gfar_init_sysfs(dev);
@@ -463,14 +1088,19 @@ static int gfar_probe(struct of_device *ofdev,
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. */
printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
- printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n",
- dev->name, priv->rx_ring_size, priv->tx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ printk(KERN_INFO "%s: :RX BD ring size for Q[%d]: %d\n",
+ dev->name, i, priv->rx_queue[i]->rx_ring_size);
+ for(i = 0; i < priv->num_tx_queues; i++)
+ printk(KERN_INFO "%s:TX BD ring size for Q[%d]: %d\n",
+ dev->name, i, priv->tx_queue[i]->tx_ring_size);
return 0;
register_fail:
- iounmap(priv->regs);
-regs_fail:
+ unmap_group_regs(priv);
+ free_tx_pointers(priv);
+ free_rx_pointers(priv);
if (priv->phy_node)
of_node_put(priv->phy_node);
if (priv->tbi_node)
@@ -491,54 +1121,59 @@ static int gfar_remove(struct of_device *ofdev)
dev_set_drvdata(&ofdev->dev, NULL);
unregister_netdev(priv->ndev);
- iounmap(priv->regs);
+ unmap_group_regs(priv);
free_netdev(priv->ndev);
return 0;
}
#ifdef CONFIG_PM
-static int gfar_suspend(struct of_device *ofdev, pm_message_t state)
+
+static int gfar_suspend(struct device *dev)
{
- struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
- struct net_device *dev = priv->ndev;
+ struct gfar_private *priv = dev_get_drvdata(dev);
+ struct net_device *ndev = priv->ndev;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
- netif_device_detach(dev);
+ netif_device_detach(ndev);
- if (netif_running(dev)) {
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ if (netif_running(ndev)) {
- gfar_halt_nodisable(dev);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
+
+ gfar_halt_nodisable(ndev);
/* Disable Tx, and Rx if wake-on-LAN is disabled. */
- tempval = gfar_read(&priv->regs->maccfg1);
+ tempval = gfar_read(&regs->maccfg1);
tempval &= ~MACCFG1_TX_EN;
if (!magic_packet)
tempval &= ~MACCFG1_RX_EN;
- gfar_write(&priv->regs->maccfg1, tempval);
+ gfar_write(&regs->maccfg1, tempval);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
- napi_disable(&priv->napi);
+ disable_napi(priv);
if (magic_packet) {
/* Enable interrupt on Magic Packet */
- gfar_write(&priv->regs->imask, IMASK_MAG);
+ gfar_write(&regs->imask, IMASK_MAG);
/* Enable Magic Packet mode */
- tempval = gfar_read(&priv->regs->maccfg2);
+ tempval = gfar_read(&regs->maccfg2);
tempval |= MACCFG2_MPEN;
- gfar_write(&priv->regs->maccfg2, tempval);
+ gfar_write(&regs->maccfg2, tempval);
} else {
phy_stop(priv->phydev);
}
@@ -547,17 +1182,18 @@ static int gfar_suspend(struct of_device *ofdev, pm_message_t state)
return 0;
}
-static int gfar_resume(struct of_device *ofdev)
+static int gfar_resume(struct device *dev)
{
- struct gfar_private *priv = dev_get_drvdata(&ofdev->dev);
- struct net_device *dev = priv->ndev;
+ struct gfar_private *priv = dev_get_drvdata(dev);
+ struct net_device *ndev = priv->ndev;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
u32 tempval;
int magic_packet = priv->wol_en &&
(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
- if (!netif_running(dev)) {
- netif_device_attach(dev);
+ if (!netif_running(ndev)) {
+ netif_device_attach(ndev);
return 0;
}
@@ -567,28 +1203,80 @@ static int gfar_resume(struct of_device *ofdev)
/* Disable Magic Packet mode, in case something
* else woke us up.
*/
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
-
- tempval = gfar_read(&priv->regs->maccfg2);
+ tempval = gfar_read(&regs->maccfg2);
tempval &= ~MACCFG2_MPEN;
- gfar_write(&priv->regs->maccfg2, tempval);
+ gfar_write(&regs->maccfg2, tempval);
- gfar_start(dev);
+ gfar_start(ndev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
- netif_device_attach(dev);
+ netif_device_attach(ndev);
- napi_enable(&priv->napi);
+ enable_napi(priv);
return 0;
}
+
+static int gfar_restore(struct device *dev)
+{
+ struct gfar_private *priv = dev_get_drvdata(dev);
+ struct net_device *ndev = priv->ndev;
+
+ if (!netif_running(ndev))
+ return 0;
+
+ gfar_init_bds(ndev);
+ init_registers(ndev);
+ gfar_set_mac_address(ndev);
+ gfar_init_mac(ndev);
+ gfar_start(ndev);
+
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->oldduplex = -1;
+
+ if (priv->phydev)
+ phy_start(priv->phydev);
+
+ netif_device_attach(ndev);
+ enable_napi(priv);
+
+ return 0;
+}
+
+static struct dev_pm_ops gfar_pm_ops = {
+ .suspend = gfar_suspend,
+ .resume = gfar_resume,
+ .freeze = gfar_suspend,
+ .thaw = gfar_resume,
+ .restore = gfar_restore,
+};
+
+#define GFAR_PM_OPS (&gfar_pm_ops)
+
+static int gfar_legacy_suspend(struct of_device *ofdev, pm_message_t state)
+{
+ return gfar_suspend(&ofdev->dev);
+}
+
+static int gfar_legacy_resume(struct of_device *ofdev)
+{
+ return gfar_resume(&ofdev->dev);
+}
+
#else
-#define gfar_suspend NULL
-#define gfar_resume NULL
+
+#define GFAR_PM_OPS NULL
+#define gfar_legacy_suspend NULL
+#define gfar_legacy_resume NULL
+
#endif
/* Reads the controller's registers to determine what interface
@@ -597,7 +1285,10 @@ static int gfar_resume(struct of_device *ofdev)
static phy_interface_t gfar_get_interface(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- u32 ecntrl = gfar_read(&priv->regs->ecntrl);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 ecntrl;
+
+ ecntrl = gfar_read(&regs->ecntrl);
if (ecntrl & ECNTRL_SGMII_MODE)
return PHY_INTERFACE_MODE_SGMII;
@@ -719,46 +1410,52 @@ static void gfar_configure_serdes(struct net_device *dev)
static void init_registers(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = NULL;
+ int i = 0;
- /* Clear IEVENT */
- gfar_write(&priv->regs->ievent, IEVENT_INIT_CLEAR);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Clear IEVENT */
+ gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
- /* Initialize IMASK */
- gfar_write(&priv->regs->imask, IMASK_INIT_CLEAR);
+ /* Initialize IMASK */
+ gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+ }
+ regs = priv->gfargrp[0].regs;
/* Init hash registers to zero */
- gfar_write(&priv->regs->igaddr0, 0);
- gfar_write(&priv->regs->igaddr1, 0);
- gfar_write(&priv->regs->igaddr2, 0);
- gfar_write(&priv->regs->igaddr3, 0);
- gfar_write(&priv->regs->igaddr4, 0);
- gfar_write(&priv->regs->igaddr5, 0);
- gfar_write(&priv->regs->igaddr6, 0);
- gfar_write(&priv->regs->igaddr7, 0);
-
- gfar_write(&priv->regs->gaddr0, 0);
- gfar_write(&priv->regs->gaddr1, 0);
- gfar_write(&priv->regs->gaddr2, 0);
- gfar_write(&priv->regs->gaddr3, 0);
- gfar_write(&priv->regs->gaddr4, 0);
- gfar_write(&priv->regs->gaddr5, 0);
- gfar_write(&priv->regs->gaddr6, 0);
- gfar_write(&priv->regs->gaddr7, 0);
+ gfar_write(&regs->igaddr0, 0);
+ gfar_write(&regs->igaddr1, 0);
+ gfar_write(&regs->igaddr2, 0);
+ gfar_write(&regs->igaddr3, 0);
+ gfar_write(&regs->igaddr4, 0);
+ gfar_write(&regs->igaddr5, 0);
+ gfar_write(&regs->igaddr6, 0);
+ gfar_write(&regs->igaddr7, 0);
+
+ gfar_write(&regs->gaddr0, 0);
+ gfar_write(&regs->gaddr1, 0);
+ gfar_write(&regs->gaddr2, 0);
+ gfar_write(&regs->gaddr3, 0);
+ gfar_write(&regs->gaddr4, 0);
+ gfar_write(&regs->gaddr5, 0);
+ gfar_write(&regs->gaddr6, 0);
+ gfar_write(&regs->gaddr7, 0);
/* Zero out the rmon mib registers if it has them */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- memset_io(&(priv->regs->rmon), 0, sizeof (struct rmon_mib));
+ memset_io(&(regs->rmon), 0, sizeof (struct rmon_mib));
/* Mask off the CAM interrupts */
- gfar_write(&priv->regs->rmon.cam1, 0xffffffff);
- gfar_write(&priv->regs->rmon.cam2, 0xffffffff);
+ gfar_write(&regs->rmon.cam1, 0xffffffff);
+ gfar_write(&regs->rmon.cam2, 0xffffffff);
}
/* Initialize the max receive buffer length */
- gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
+ gfar_write(&regs->mrblr, priv->rx_buffer_size);
/* Initialize the Minimum Frame Length Register */
- gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS);
+ gfar_write(&regs->minflr, MINFLR_INIT_SETTINGS);
}
@@ -766,23 +1463,28 @@ static void init_registers(struct net_device *dev)
static void gfar_halt_nodisable(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = NULL;
u32 tempval;
+ int i = 0;
- /* Mask all interrupts */
- gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Mask all interrupts */
+ gfar_write(&regs->imask, IMASK_INIT_CLEAR);
- /* Clear all interrupts */
- gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
+ /* Clear all interrupts */
+ gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
+ }
+ regs = priv->gfargrp[0].regs;
/* Stop the DMA, and wait for it to stop */
- tempval = gfar_read(&priv->regs->dmactrl);
+ tempval = gfar_read(&regs->dmactrl);
if ((tempval & (DMACTRL_GRS | DMACTRL_GTS))
!= (DMACTRL_GRS | DMACTRL_GTS)) {
tempval |= (DMACTRL_GRS | DMACTRL_GTS);
- gfar_write(&priv->regs->dmactrl, tempval);
+ gfar_write(&regs->dmactrl, tempval);
- while (!(gfar_read(&priv->regs->ievent) &
+ while (!(gfar_read(&regs->ievent) &
(IEVENT_GRSC | IEVENT_GTSC)))
cpu_relax();
}
@@ -792,7 +1494,7 @@ static void gfar_halt_nodisable(struct net_device *dev)
void gfar_halt(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
gfar_halt_nodisable(dev);
@@ -803,101 +1505,131 @@ void gfar_halt(struct net_device *dev)
gfar_write(&regs->maccfg1, tempval);
}
+static void free_grp_irqs(struct gfar_priv_grp *grp)
+{
+ free_irq(grp->interruptError, grp);
+ free_irq(grp->interruptTransmit, grp);
+ free_irq(grp->interruptReceive, grp);
+}
+
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
unsigned long flags;
+ int i;
phy_stop(priv->phydev);
+
/* Lock it down */
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
/* Free the IRQs */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- free_irq(priv->interruptError, dev);
- free_irq(priv->interruptTransmit, dev);
- free_irq(priv->interruptReceive, dev);
+ for (i = 0; i < priv->num_grps; i++)
+ free_grp_irqs(&priv->gfargrp[i]);
} else {
- free_irq(priv->interruptTransmit, dev);
+ for (i = 0; i < priv->num_grps; i++)
+ free_irq(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
}
free_skb_resources(priv);
-
- dma_free_coherent(&priv->ofdev->dev,
- sizeof(struct txbd8)*priv->tx_ring_size
- + sizeof(struct rxbd8)*priv->rx_ring_size,
- priv->tx_bd_base,
- gfar_read(&regs->tbase0));
}
-/* If there are any tx skbs or rx skbs still around, free them.
- * Then free tx_skbuff and rx_skbuff */
-static void free_skb_resources(struct gfar_private *priv)
+static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue)
{
- struct rxbd8 *rxbdp;
struct txbd8 *txbdp;
+ struct gfar_private *priv = netdev_priv(tx_queue->dev);
int i, j;
- /* Go through all the buffer descriptors and free their data buffers */
- txbdp = priv->tx_bd_base;
+ txbdp = tx_queue->tx_bd_base;
- for (i = 0; i < priv->tx_ring_size; i++) {
- if (!priv->tx_skbuff[i])
+ for (i = 0; i < tx_queue->tx_ring_size; i++) {
+ if (!tx_queue->tx_skbuff[i])
continue;
dma_unmap_single(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
txbdp->lstatus = 0;
- for (j = 0; j < skb_shinfo(priv->tx_skbuff[i])->nr_frags; j++) {
+ for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags;
+ j++) {
txbdp++;
dma_unmap_page(&priv->ofdev->dev, txbdp->bufPtr,
txbdp->length, DMA_TO_DEVICE);
}
txbdp++;
- dev_kfree_skb_any(priv->tx_skbuff[i]);
- priv->tx_skbuff[i] = NULL;
+ dev_kfree_skb_any(tx_queue->tx_skbuff[i]);
+ tx_queue->tx_skbuff[i] = NULL;
}
+ kfree(tx_queue->tx_skbuff);
+}
- kfree(priv->tx_skbuff);
-
- rxbdp = priv->rx_bd_base;
+static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue)
+{
+ struct rxbd8 *rxbdp;
+ struct gfar_private *priv = netdev_priv(rx_queue->dev);
+ int i;
- /* rx_skbuff is not guaranteed to be allocated, so only
- * free it and its contents if it is allocated */
- if(priv->rx_skbuff != NULL) {
- for (i = 0; i < priv->rx_ring_size; i++) {
- if (priv->rx_skbuff[i]) {
- dma_unmap_single(&priv->ofdev->dev, rxbdp->bufPtr,
- priv->rx_buffer_size,
- DMA_FROM_DEVICE);
+ rxbdp = rx_queue->rx_bd_base;
- dev_kfree_skb_any(priv->rx_skbuff[i]);
- priv->rx_skbuff[i] = NULL;
- }
+ for (i = 0; i < rx_queue->rx_ring_size; i++) {
+ if (rx_queue->rx_skbuff[i]) {
+ dma_unmap_single(&priv->ofdev->dev,
+ rxbdp->bufPtr, priv->rx_buffer_size,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(rx_queue->rx_skbuff[i]);
+ rx_queue->rx_skbuff[i] = NULL;
+ }
+ rxbdp->lstatus = 0;
+ rxbdp->bufPtr = 0;
+ rxbdp++;
+ }
+ kfree(rx_queue->rx_skbuff);
+}
- rxbdp->lstatus = 0;
- rxbdp->bufPtr = 0;
+/* If there are any tx skbs or rx skbs still around, free them.
+ * Then free tx_skbuff and rx_skbuff */
+static void free_skb_resources(struct gfar_private *priv)
+{
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ int i;
- rxbdp++;
- }
+ /* Go through all the buffer descriptors and free their data buffers */
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ tx_queue = priv->tx_queue[i];
+ if(!tx_queue->tx_skbuff)
+ free_skb_tx_queue(tx_queue);
+ }
- kfree(priv->rx_skbuff);
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ rx_queue = priv->rx_queue[i];
+ if(!rx_queue->rx_skbuff)
+ free_skb_rx_queue(rx_queue);
}
+
+ dma_free_coherent(&priv->ofdev->dev,
+ sizeof(struct txbd8) * priv->total_tx_ring_size +
+ sizeof(struct rxbd8) * priv->total_rx_ring_size,
+ priv->tx_queue[0]->tx_bd_base,
+ priv->tx_queue[0]->tx_bd_dma_base);
}
void gfar_start(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
+ int i = 0;
/* Enable Rx and Tx in MACCFG1 */
tempval = gfar_read(&regs->maccfg1);
@@ -905,269 +1637,159 @@ void gfar_start(struct net_device *dev)
gfar_write(&regs->maccfg1, tempval);
/* Initialize DMACTRL to have WWR and WOP */
- tempval = gfar_read(&priv->regs->dmactrl);
+ tempval = gfar_read(&regs->dmactrl);
tempval |= DMACTRL_INIT_SETTINGS;
- gfar_write(&priv->regs->dmactrl, tempval);
+ gfar_write(&regs->dmactrl, tempval);
/* Make sure we aren't stopped */
- tempval = gfar_read(&priv->regs->dmactrl);
+ tempval = gfar_read(&regs->dmactrl);
tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
- gfar_write(&priv->regs->dmactrl, tempval);
-
- /* Clear THLT/RHLT, so that the DMA starts polling now */
- gfar_write(&regs->tstat, TSTAT_CLEAR_THALT);
- gfar_write(&regs->rstat, RSTAT_CLEAR_RHALT);
-
- /* Unmask the interrupts we look for */
- gfar_write(&regs->imask, IMASK_DEFAULT);
+ gfar_write(&regs->dmactrl, tempval);
+
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Clear THLT/RHLT, so that the DMA starts polling now */
+ gfar_write(&regs->tstat, priv->gfargrp[i].tstat);
+ gfar_write(&regs->rstat, priv->gfargrp[i].rstat);
+ /* Unmask the interrupts we look for */
+ gfar_write(&regs->imask, IMASK_DEFAULT);
+ }
dev->trans_start = jiffies;
}
-/* Bring the controller up and running */
-int startup_gfar(struct net_device *dev)
+void gfar_configure_coalescing(struct gfar_private *priv,
+ unsigned long tx_mask, unsigned long rx_mask)
{
- struct txbd8 *txbdp;
- struct rxbd8 *rxbdp;
- dma_addr_t addr = 0;
- unsigned long vaddr;
- int i;
- struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
- int err = 0;
- u32 rctrl = 0;
- u32 tctrl = 0;
- u32 attrs = 0;
-
- gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 __iomem *baddr;
+ int i = 0;
- /* Allocate memory for the buffer descriptors */
- vaddr = (unsigned long) dma_alloc_coherent(&priv->ofdev->dev,
- sizeof (struct txbd8) * priv->tx_ring_size +
- sizeof (struct rxbd8) * priv->rx_ring_size,
- &addr, GFP_KERNEL);
-
- if (vaddr == 0) {
- if (netif_msg_ifup(priv))
- printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
- dev->name);
- return -ENOMEM;
- }
-
- priv->tx_bd_base = (struct txbd8 *) vaddr;
-
- /* enet DMA only understands physical addresses */
- gfar_write(&regs->tbase0, addr);
-
- /* Start the rx descriptor ring where the tx ring leaves off */
- addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
- vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size;
- priv->rx_bd_base = (struct rxbd8 *) vaddr;
- gfar_write(&regs->rbase0, addr);
-
- /* Setup the skbuff rings */
- priv->tx_skbuff =
- (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
- priv->tx_ring_size, GFP_KERNEL);
-
- if (NULL == priv->tx_skbuff) {
- if (netif_msg_ifup(priv))
- printk(KERN_ERR "%s: Could not allocate tx_skbuff\n",
- dev->name);
- err = -ENOMEM;
- goto tx_skb_fail;
- }
-
- for (i = 0; i < priv->tx_ring_size; i++)
- priv->tx_skbuff[i] = NULL;
-
- priv->rx_skbuff =
- (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
- priv->rx_ring_size, GFP_KERNEL);
-
- if (NULL == priv->rx_skbuff) {
- if (netif_msg_ifup(priv))
- printk(KERN_ERR "%s: Could not allocate rx_skbuff\n",
- dev->name);
- err = -ENOMEM;
- goto rx_skb_fail;
- }
-
- for (i = 0; i < priv->rx_ring_size; i++)
- priv->rx_skbuff[i] = NULL;
-
- /* Initialize some variables in our dev structure */
- priv->num_txbdfree = priv->tx_ring_size;
- priv->dirty_tx = priv->cur_tx = priv->tx_bd_base;
- priv->cur_rx = priv->rx_bd_base;
- priv->skb_curtx = priv->skb_dirtytx = 0;
- priv->skb_currx = 0;
-
- /* Initialize Transmit Descriptor Ring */
- txbdp = priv->tx_bd_base;
- for (i = 0; i < priv->tx_ring_size; i++) {
- txbdp->lstatus = 0;
- txbdp->bufPtr = 0;
- txbdp++;
- }
-
- /* Set the last descriptor in the ring to indicate wrap */
- txbdp--;
- txbdp->status |= TXBD_WRAP;
-
- rxbdp = priv->rx_bd_base;
- for (i = 0; i < priv->rx_ring_size; i++) {
- struct sk_buff *skb;
-
- skb = gfar_new_skb(dev);
-
- if (!skb) {
- printk(KERN_ERR "%s: Can't allocate RX buffers\n",
- dev->name);
+ /* Backward compatible case ---- even if we enable
+ * multiple queues, there's only single reg to program
+ */
+ gfar_write(&regs->txic, 0);
+ if(likely(priv->tx_queue[0]->txcoalescing))
+ gfar_write(&regs->txic, priv->tx_queue[0]->txic);
- goto err_rxalloc_fail;
+ gfar_write(&regs->rxic, 0);
+ if(unlikely(priv->rx_queue[0]->rxcoalescing))
+ gfar_write(&regs->rxic, priv->rx_queue[0]->rxic);
+
+ if (priv->mode == MQ_MG_MODE) {
+ baddr = &regs->txic0;
+ for_each_bit (i, &tx_mask, priv->num_tx_queues) {
+ if (likely(priv->tx_queue[i]->txcoalescing)) {
+ gfar_write(baddr + i, 0);
+ gfar_write(baddr + i, priv->tx_queue[i]->txic);
+ }
}
- priv->rx_skbuff[i] = skb;
-
- gfar_new_rxbdp(dev, rxbdp, skb);
-
- rxbdp++;
+ baddr = &regs->rxic0;
+ for_each_bit (i, &rx_mask, priv->num_rx_queues) {
+ if (likely(priv->rx_queue[i]->rxcoalescing)) {
+ gfar_write(baddr + i, 0);
+ gfar_write(baddr + i, priv->rx_queue[i]->rxic);
+ }
+ }
}
+}
- /* Set the last descriptor in the ring to wrap */
- rxbdp--;
- rxbdp->status |= RXBD_WRAP;
+static int register_grp_irqs(struct gfar_priv_grp *grp)
+{
+ struct gfar_private *priv = grp->priv;
+ struct net_device *dev = priv->ndev;
+ int err;
/* If the device has multiple interrupts, register for
* them. Otherwise, only register for the one */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
/* Install our interrupt handlers for Error,
* Transmit, and Receive */
- if (request_irq(priv->interruptError, gfar_error,
- 0, priv->int_name_er, dev) < 0) {
+ if ((err = request_irq(grp->interruptError, gfar_error, 0,
+ grp->int_name_er,grp)) < 0) {
if (netif_msg_intr(priv))
printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, priv->interruptError);
+ dev->name, grp->interruptError);
- err = -1;
- goto err_irq_fail;
+ goto err_irq_fail;
}
- if (request_irq(priv->interruptTransmit, gfar_transmit,
- 0, priv->int_name_tx, dev) < 0) {
+ if ((err = request_irq(grp->interruptTransmit, gfar_transmit,
+ 0, grp->int_name_tx, grp)) < 0) {
if (netif_msg_intr(priv))
printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, priv->interruptTransmit);
-
- err = -1;
-
+ dev->name, grp->interruptTransmit);
goto tx_irq_fail;
}
- if (request_irq(priv->interruptReceive, gfar_receive,
- 0, priv->int_name_rx, dev) < 0) {
+ if ((err = request_irq(grp->interruptReceive, gfar_receive, 0,
+ grp->int_name_rx, grp)) < 0) {
if (netif_msg_intr(priv))
- printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n",
- dev->name, priv->interruptReceive);
-
- err = -1;
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, grp->interruptReceive);
goto rx_irq_fail;
}
} else {
- if (request_irq(priv->interruptTransmit, gfar_interrupt,
- 0, priv->int_name_tx, dev) < 0) {
+ if ((err = request_irq(grp->interruptTransmit, gfar_interrupt, 0,
+ grp->int_name_tx, grp)) < 0) {
if (netif_msg_intr(priv))
printk(KERN_ERR "%s: Can't get IRQ %d\n",
- dev->name, priv->interruptTransmit);
-
- err = -1;
+ dev->name, grp->interruptTransmit);
goto err_irq_fail;
}
}
- phy_start(priv->phydev);
-
- /* Configure the coalescing support */
- gfar_write(&regs->txic, 0);
- if (priv->txcoalescing)
- gfar_write(&regs->txic, priv->txic);
-
- gfar_write(&regs->rxic, 0);
- if (priv->rxcoalescing)
- gfar_write(&regs->rxic, priv->rxic);
-
- if (priv->rx_csum_enable)
- rctrl |= RCTRL_CHECKSUMMING;
+ return 0;
- if (priv->extended_hash) {
- rctrl |= RCTRL_EXTHASH;
+rx_irq_fail:
+ free_irq(grp->interruptTransmit, grp);
+tx_irq_fail:
+ free_irq(grp->interruptError, grp);
+err_irq_fail:
+ return err;
- gfar_clear_exact_match(dev);
- rctrl |= RCTRL_EMEN;
- }
+}
- if (priv->padding) {
- rctrl &= ~RCTRL_PAL_MASK;
- rctrl |= RCTRL_PADDING(priv->padding);
- }
+/* Bring the controller up and running */
+int startup_gfar(struct net_device *ndev)
+{
+ struct gfar_private *priv = netdev_priv(ndev);
+ struct gfar __iomem *regs = NULL;
+ int err, i, j;
- /* keep vlan related bits if it's enabled */
- if (priv->vlgrp) {
- rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
- tctrl |= TCTRL_VLINS;
+ for (i = 0; i < priv->num_grps; i++) {
+ regs= priv->gfargrp[i].regs;
+ gfar_write(&regs->imask, IMASK_INIT_CLEAR);
}
- /* Init rctrl based on our settings */
- gfar_write(&priv->regs->rctrl, rctrl);
-
- if (dev->features & NETIF_F_IP_CSUM)
- tctrl |= TCTRL_INIT_CSUM;
-
- gfar_write(&priv->regs->tctrl, tctrl);
-
- /* Set the extraction length and index */
- attrs = ATTRELI_EL(priv->rx_stash_size) |
- ATTRELI_EI(priv->rx_stash_index);
-
- gfar_write(&priv->regs->attreli, attrs);
-
- /* Start with defaults, and add stashing or locking
- * depending on the approprate variables */
- attrs = ATTR_INIT_SETTINGS;
+ regs= priv->gfargrp[0].regs;
+ err = gfar_alloc_skb_resources(ndev);
+ if (err)
+ return err;
- if (priv->bd_stash_en)
- attrs |= ATTR_BDSTASH;
+ gfar_init_mac(ndev);
- if (priv->rx_stash_size != 0)
- attrs |= ATTR_BUFSTASH;
+ for (i = 0; i < priv->num_grps; i++) {
+ err = register_grp_irqs(&priv->gfargrp[i]);
+ if (err) {
+ for (j = 0; j < i; j++)
+ free_grp_irqs(&priv->gfargrp[j]);
+ goto irq_fail;
+ }
+ }
- gfar_write(&priv->regs->attr, attrs);
+ /* Start the controller */
+ gfar_start(ndev);
- gfar_write(&priv->regs->fifo_tx_thr, priv->fifo_threshold);
- gfar_write(&priv->regs->fifo_tx_starve, priv->fifo_starve);
- gfar_write(&priv->regs->fifo_tx_starve_shutoff, priv->fifo_starve_off);
+ phy_start(priv->phydev);
- /* Start the controller */
- gfar_start(dev);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
return 0;
-rx_irq_fail:
- free_irq(priv->interruptTransmit, dev);
-tx_irq_fail:
- free_irq(priv->interruptError, dev);
-err_irq_fail:
-err_rxalloc_fail:
-rx_skb_fail:
+irq_fail:
free_skb_resources(priv);
-tx_skb_fail:
- dma_free_coherent(&priv->ofdev->dev,
- sizeof(struct txbd8)*priv->tx_ring_size
- + sizeof(struct rxbd8)*priv->rx_ring_size,
- priv->tx_bd_base,
- gfar_read(&regs->tbase0));
-
return err;
}
@@ -1178,7 +1800,7 @@ static int gfar_enet_open(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev);
int err;
- napi_enable(&priv->napi);
+ enable_napi(priv);
skb_queue_head_init(&priv->rx_recycle);
@@ -1189,18 +1811,18 @@ static int gfar_enet_open(struct net_device *dev)
err = init_phy(dev);
- if(err) {
- napi_disable(&priv->napi);
+ if (err) {
+ disable_napi(priv);
return err;
}
err = startup_gfar(dev);
if (err) {
- napi_disable(&priv->napi);
+ disable_napi(priv);
return err;
}
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
device_set_wakeup_enable(&dev->dev, priv->wol_en);
@@ -1269,15 +1891,23 @@ static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base,
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct netdev_queue *txq;
+ struct gfar __iomem *regs = NULL;
struct txfcb *fcb = NULL;
struct txbd8 *txbdp, *txbdp_start, *base;
u32 lstatus;
- int i;
+ int i, rq = 0;
u32 bufaddr;
unsigned long flags;
unsigned int nr_frags, length;
- base = priv->tx_bd_base;
+
+ rq = skb->queue_mapping;
+ tx_queue = priv->tx_queue[rq];
+ txq = netdev_get_tx_queue(dev, rq);
+ base = tx_queue->tx_bd_base;
+ regs = tx_queue->grp->regs;
/* make space for additional header when fcb is needed */
if (((skb->ip_summed == CHECKSUM_PARTIAL) ||
@@ -1298,21 +1928,18 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* total number of fragments in the SKB */
nr_frags = skb_shinfo(skb)->nr_frags;
- spin_lock_irqsave(&priv->txlock, flags);
-
/* check if there is space to queue this packet */
- if ((nr_frags+1) > priv->num_txbdfree) {
+ if ((nr_frags+1) > tx_queue->num_txbdfree) {
/* no space, stop the queue */
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
dev->stats.tx_fifo_errors++;
- spin_unlock_irqrestore(&priv->txlock, flags);
return NETDEV_TX_BUSY;
}
/* Update transmit stats */
dev->stats.tx_bytes += skb->len;
- txbdp = txbdp_start = priv->cur_tx;
+ txbdp = txbdp_start = tx_queue->cur_tx;
if (nr_frags == 0) {
lstatus = txbdp->lstatus | BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT);
@@ -1320,7 +1947,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Place the fragment addresses and lengths into the TxBDs */
for (i = 0; i < nr_frags; i++) {
/* Point at the next BD, wrapping as needed */
- txbdp = next_txbd(txbdp, base, priv->tx_ring_size);
+ txbdp = next_txbd(txbdp, base, tx_queue->tx_ring_size);
length = skb_shinfo(skb)->frags[i].size;
@@ -1362,13 +1989,27 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
/* setup the TxBD length and buffer pointer for the first BD */
- priv->tx_skbuff[priv->skb_curtx] = skb;
+ tx_queue->tx_skbuff[tx_queue->skb_curtx] = skb;
txbdp_start->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb);
/*
+ * We can work in parallel with gfar_clean_tx_ring(), except
+ * when modifying num_txbdfree. Note that we didn't grab the lock
+ * when we were reading the num_txbdfree and checking for available
+ * space, that's because outside of this function it can only grow,
+ * and once we've got needed space, it cannot suddenly disappear.
+ *
+ * The lock also protects us from gfar_error(), which can modify
+ * regs->tstat and thus retrigger the transfers, which is why we
+ * also must grab the lock before setting ready bit for the first
+ * to be transmitted BD.
+ */
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+
+ /*
* The powerpc-specific eieio() is used, as wmb() has too strong
* semantics (it requires synchronization between cacheable and
* uncacheable mappings, which eieio doesn't provide and which we
@@ -1382,29 +2023,29 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Update the current skb pointer to the next entry we will use
* (wrapping if necessary) */
- priv->skb_curtx = (priv->skb_curtx + 1) &
- TX_RING_MOD_MASK(priv->tx_ring_size);
+ tx_queue->skb_curtx = (tx_queue->skb_curtx + 1) &
+ TX_RING_MOD_MASK(tx_queue->tx_ring_size);
- priv->cur_tx = next_txbd(txbdp, base, priv->tx_ring_size);
+ tx_queue->cur_tx = next_txbd(txbdp, base, tx_queue->tx_ring_size);
/* reduce TxBD free count */
- priv->num_txbdfree -= (nr_frags + 1);
+ tx_queue->num_txbdfree -= (nr_frags + 1);
dev->trans_start = jiffies;
/* If the next BD still needs to be cleaned up, then the bds
are full. We need to tell the kernel to stop sending us stuff. */
- if (!priv->num_txbdfree) {
- netif_stop_queue(dev);
+ if (!tx_queue->num_txbdfree) {
+ netif_tx_stop_queue(txq);
dev->stats.tx_fifo_errors++;
}
/* Tell the DMA to go go go */
- gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(&regs->tstat, TSTAT_CLEAR_THALT >> tx_queue->qindex);
/* Unlock priv */
- spin_unlock_irqrestore(&priv->txlock, flags);
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
return NETDEV_TX_OK;
}
@@ -1414,7 +2055,7 @@ static int gfar_close(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- napi_disable(&priv->napi);
+ disable_napi(priv);
skb_queue_purge(&priv->rx_recycle);
cancel_work_sync(&priv->reset_task);
@@ -1424,7 +2065,7 @@ static int gfar_close(struct net_device *dev)
phy_disconnect(priv->phydev);
priv->phydev = NULL;
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
return 0;
}
@@ -1443,50 +2084,55 @@ static void gfar_vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = NULL;
unsigned long flags;
u32 tempval;
- spin_lock_irqsave(&priv->rxlock, flags);
+ regs = priv->gfargrp[0].regs;
+ local_irq_save(flags);
+ lock_rx_qs(priv);
priv->vlgrp = grp;
if (grp) {
/* Enable VLAN tag insertion */
- tempval = gfar_read(&priv->regs->tctrl);
+ tempval = gfar_read(&regs->tctrl);
tempval |= TCTRL_VLINS;
- gfar_write(&priv->regs->tctrl, tempval);
+ gfar_write(&regs->tctrl, tempval);
/* Enable VLAN tag extraction */
- tempval = gfar_read(&priv->regs->rctrl);
+ tempval = gfar_read(&regs->rctrl);
tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT);
- gfar_write(&priv->regs->rctrl, tempval);
+ gfar_write(&regs->rctrl, tempval);
} else {
/* Disable VLAN tag insertion */
- tempval = gfar_read(&priv->regs->tctrl);
+ tempval = gfar_read(&regs->tctrl);
tempval &= ~TCTRL_VLINS;
- gfar_write(&priv->regs->tctrl, tempval);
+ gfar_write(&regs->tctrl, tempval);
/* Disable VLAN tag extraction */
- tempval = gfar_read(&priv->regs->rctrl);
+ tempval = gfar_read(&regs->rctrl);
tempval &= ~RCTRL_VLEX;
/* If parse is no longer required, then disable parser */
if (tempval & RCTRL_REQ_PARSER)
tempval |= RCTRL_PRSDEP_INIT;
else
tempval &= ~RCTRL_PRSDEP_INIT;
- gfar_write(&priv->regs->rctrl, tempval);
+ gfar_write(&regs->rctrl, tempval);
}
gfar_change_mtu(dev, dev->mtu);
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
}
static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{
int tempsize, tempval;
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int oldsize = priv->rx_buffer_size;
int frame_size = new_mtu + ETH_HLEN;
@@ -1518,20 +2164,20 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
dev->mtu = new_mtu;
- gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
- gfar_write(&priv->regs->maxfrm, priv->rx_buffer_size);
+ gfar_write(&regs->mrblr, priv->rx_buffer_size);
+ gfar_write(&regs->maxfrm, priv->rx_buffer_size);
/* If the mtu is larger than the max size for standard
* ethernet frames (ie, a jumbo frame), then set maccfg2
* to allow huge frames, and to check the length */
- tempval = gfar_read(&priv->regs->maccfg2);
+ tempval = gfar_read(&regs->maccfg2);
if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE)
tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
else
tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
- gfar_write(&priv->regs->maccfg2, tempval);
+ gfar_write(&regs->maccfg2, tempval);
if ((oldsize != tempsize) && (dev->flags & IFF_UP))
startup_gfar(dev);
@@ -1551,10 +2197,10 @@ static void gfar_reset_task(struct work_struct *work)
struct net_device *dev = priv->ndev;
if (dev->flags & IFF_UP) {
- netif_stop_queue(dev);
+ netif_tx_stop_all_queues(dev);
stop_gfar(dev);
startup_gfar(dev);
- netif_start_queue(dev);
+ netif_tx_start_all_queues(dev);
}
netif_tx_schedule_all(dev);
@@ -1569,24 +2215,29 @@ static void gfar_timeout(struct net_device *dev)
}
/* Interrupt Handler for Transmit complete */
-static int gfar_clean_tx_ring(struct net_device *dev)
+static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
{
+ struct net_device *dev = tx_queue->dev;
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_rx_q *rx_queue = NULL;
struct txbd8 *bdp;
struct txbd8 *lbdp = NULL;
- struct txbd8 *base = priv->tx_bd_base;
+ struct txbd8 *base = tx_queue->tx_bd_base;
struct sk_buff *skb;
int skb_dirtytx;
- int tx_ring_size = priv->tx_ring_size;
+ int tx_ring_size = tx_queue->tx_ring_size;
int frags = 0;
int i;
int howmany = 0;
u32 lstatus;
- bdp = priv->dirty_tx;
- skb_dirtytx = priv->skb_dirtytx;
+ rx_queue = priv->rx_queue[tx_queue->qindex];
+ bdp = tx_queue->dirty_tx;
+ skb_dirtytx = tx_queue->skb_dirtytx;
+
+ while ((skb = tx_queue->tx_skbuff[skb_dirtytx])) {
+ unsigned long flags;
- while ((skb = priv->tx_skbuff[skb_dirtytx])) {
frags = skb_shinfo(skb)->nr_frags;
lbdp = skip_txbd(bdp, frags, base, tx_ring_size);
@@ -1618,82 +2269,73 @@ static int gfar_clean_tx_ring(struct net_device *dev)
* If there's room in the queue (limit it to rx_buffer_size)
* we add this skb back into the pool, if it's the right size
*/
- if (skb_queue_len(&priv->rx_recycle) < priv->rx_ring_size &&
+ if (skb_queue_len(&priv->rx_recycle) < rx_queue->rx_ring_size &&
skb_recycle_check(skb, priv->rx_buffer_size +
RXBUF_ALIGNMENT))
__skb_queue_head(&priv->rx_recycle, skb);
else
dev_kfree_skb_any(skb);
- priv->tx_skbuff[skb_dirtytx] = NULL;
+ tx_queue->tx_skbuff[skb_dirtytx] = NULL;
skb_dirtytx = (skb_dirtytx + 1) &
TX_RING_MOD_MASK(tx_ring_size);
howmany++;
- priv->num_txbdfree += frags + 1;
+ spin_lock_irqsave(&tx_queue->txlock, flags);
+ tx_queue->num_txbdfree += frags + 1;
+ spin_unlock_irqrestore(&tx_queue->txlock, flags);
}
/* If we freed a buffer, we can restart transmission, if necessary */
- if (netif_queue_stopped(dev) && priv->num_txbdfree)
- netif_wake_queue(dev);
+ if (__netif_subqueue_stopped(dev, tx_queue->qindex) && tx_queue->num_txbdfree)
+ netif_wake_subqueue(dev, tx_queue->qindex);
/* Update dirty indicators */
- priv->skb_dirtytx = skb_dirtytx;
- priv->dirty_tx = bdp;
+ tx_queue->skb_dirtytx = skb_dirtytx;
+ tx_queue->dirty_tx = bdp;
dev->stats.tx_packets += howmany;
return howmany;
}
-static void gfar_schedule_cleanup(struct net_device *dev)
+static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
{
- struct gfar_private *priv = netdev_priv(dev);
unsigned long flags;
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
-
- if (napi_schedule_prep(&priv->napi)) {
- gfar_write(&priv->regs->imask, IMASK_RTX_DISABLED);
- __napi_schedule(&priv->napi);
+ spin_lock_irqsave(&gfargrp->grplock, flags);
+ if (napi_schedule_prep(&gfargrp->napi)) {
+ gfar_write(&gfargrp->regs->imask, IMASK_RTX_DISABLED);
+ __napi_schedule(&gfargrp->napi);
} else {
/*
* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived.
*/
- gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
+ gfar_write(&gfargrp->regs->ievent, IEVENT_RTX_MASK);
}
+ spin_unlock_irqrestore(&gfargrp->grplock, flags);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
}
/* Interrupt Handler for Transmit complete */
-static irqreturn_t gfar_transmit(int irq, void *dev_id)
+static irqreturn_t gfar_transmit(int irq, void *grp_id)
{
- gfar_schedule_cleanup((struct net_device *)dev_id);
+ gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id);
return IRQ_HANDLED;
}
-static void gfar_new_rxbdp(struct net_device *dev, struct rxbd8 *bdp,
+static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp,
struct sk_buff *skb)
{
+ struct net_device *dev = rx_queue->dev;
struct gfar_private *priv = netdev_priv(dev);
- u32 lstatus;
-
- bdp->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data,
- priv->rx_buffer_size, DMA_FROM_DEVICE);
-
- lstatus = BD_LFLAG(RXBD_EMPTY | RXBD_INTERRUPT);
-
- if (bdp == priv->rx_bd_base + priv->rx_ring_size - 1)
- lstatus |= BD_LFLAG(RXBD_WRAP);
-
- eieio();
+ dma_addr_t buf;
- bdp->lstatus = lstatus;
+ buf = dma_map_single(&priv->ofdev->dev, skb->data,
+ priv->rx_buffer_size, DMA_FROM_DEVICE);
+ gfar_init_rxbdp(rx_queue, bdp, buf);
}
@@ -1760,9 +2402,9 @@ static inline void count_errors(unsigned short status, struct net_device *dev)
}
}
-irqreturn_t gfar_receive(int irq, void *dev_id)
+irqreturn_t gfar_receive(int irq, void *grp_id)
{
- gfar_schedule_cleanup((struct net_device *)dev_id);
+ gfar_schedule_cleanup((struct gfar_priv_grp *)grp_id);
return IRQ_HANDLED;
}
@@ -1792,6 +2434,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
fcb = (struct rxfcb *)skb->data;
/* Remove the FCB from the skb */
+ skb_set_queue_mapping(skb, fcb->rq);
/* Remove the padded bytes, if there are any */
if (amount_pull)
skb_pull(skb, amount_pull);
@@ -1818,8 +2461,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
* until the budget/quota has been reached. Returns the number
* of frames handled
*/
-int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
+int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
{
+ struct net_device *dev = rx_queue->dev;
struct rxbd8 *bdp, *base;
struct sk_buff *skb;
int pkt_len;
@@ -1828,8 +2472,8 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
struct gfar_private *priv = netdev_priv(dev);
/* Get the first full descriptor */
- bdp = priv->cur_rx;
- base = priv->rx_bd_base;
+ bdp = rx_queue->cur_rx;
+ base = rx_queue->rx_bd_base;
amount_pull = (gfar_uses_fcb(priv) ? GMAC_FCB_LEN : 0) +
priv->padding;
@@ -1841,7 +2485,7 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
/* Add another skb for the future */
newskb = gfar_new_skb(dev);
- skb = priv->rx_skbuff[priv->skb_currx];
+ skb = rx_queue->rx_skbuff[rx_queue->skb_currx];
dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr,
priv->rx_buffer_size, DMA_FROM_DEVICE);
@@ -1875,8 +2519,6 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
skb_put(skb, pkt_len);
dev->stats.rx_bytes += pkt_len;
- if (in_irq() || irqs_disabled())
- printk("Interrupt problem!\n");
gfar_process_frame(dev, skb, amount_pull);
} else {
@@ -1889,46 +2531,70 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
}
- priv->rx_skbuff[priv->skb_currx] = newskb;
+ rx_queue->rx_skbuff[rx_queue->skb_currx] = newskb;
/* Setup the new bdp */
- gfar_new_rxbdp(dev, bdp, newskb);
+ gfar_new_rxbdp(rx_queue, bdp, newskb);
/* Update to the next pointer */
- bdp = next_bd(bdp, base, priv->rx_ring_size);
+ bdp = next_bd(bdp, base, rx_queue->rx_ring_size);
/* update to point at the next skb */
- priv->skb_currx =
- (priv->skb_currx + 1) &
- RX_RING_MOD_MASK(priv->rx_ring_size);
+ rx_queue->skb_currx =
+ (rx_queue->skb_currx + 1) &
+ RX_RING_MOD_MASK(rx_queue->rx_ring_size);
}
/* Update the current rxbd pointer to be the next one */
- priv->cur_rx = bdp;
+ rx_queue->cur_rx = bdp;
return howmany;
}
static int gfar_poll(struct napi_struct *napi, int budget)
{
- struct gfar_private *priv = container_of(napi, struct gfar_private, napi);
- struct net_device *dev = priv->ndev;
- int tx_cleaned = 0;
- int rx_cleaned = 0;
- unsigned long flags;
+ struct gfar_priv_grp *gfargrp = container_of(napi,
+ struct gfar_priv_grp, napi);
+ struct gfar_private *priv = gfargrp->priv;
+ struct gfar __iomem *regs = gfargrp->regs;
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0;
+ int tx_cleaned = 0, i, left_over_budget = budget;
+ unsigned long serviced_queues = 0;
+ int num_queues = 0;
+
+ num_queues = gfargrp->num_rx_queues;
+ budget_per_queue = budget/num_queues;
/* Clear IEVENT, so interrupts aren't called again
* because of the packets that have already arrived */
- gfar_write(&priv->regs->ievent, IEVENT_RTX_MASK);
-
- /* If we fail to get the lock, don't bother with the TX BDs */
- if (spin_trylock_irqsave(&priv->txlock, flags)) {
- tx_cleaned = gfar_clean_tx_ring(dev);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ gfar_write(&regs->ievent, IEVENT_RTX_MASK);
+
+ while (num_queues && left_over_budget) {
+
+ budget_per_queue = left_over_budget/num_queues;
+ left_over_budget = 0;
+
+ for_each_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
+ if (test_bit(i, &serviced_queues))
+ continue;
+ rx_queue = priv->rx_queue[i];
+ tx_queue = priv->tx_queue[rx_queue->qindex];
+
+ tx_cleaned += gfar_clean_tx_ring(tx_queue);
+ rx_cleaned_per_queue = gfar_clean_rx_ring(rx_queue,
+ budget_per_queue);
+ rx_cleaned += rx_cleaned_per_queue;
+ if(rx_cleaned_per_queue < budget_per_queue) {
+ left_over_budget = left_over_budget +
+ (budget_per_queue - rx_cleaned_per_queue);
+ set_bit(i, &serviced_queues);
+ num_queues--;
+ }
+ }
}
- rx_cleaned = gfar_clean_rx_ring(dev, budget);
-
if (tx_cleaned)
return budget;
@@ -1936,20 +2602,14 @@ static int gfar_poll(struct napi_struct *napi, int budget)
napi_complete(napi);
/* Clear the halt bit in RSTAT */
- gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+ gfar_write(&regs->rstat, gfargrp->rstat);
- gfar_write(&priv->regs->imask, IMASK_DEFAULT);
+ gfar_write(&regs->imask, IMASK_DEFAULT);
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
- if (likely(priv->rxcoalescing)) {
- gfar_write(&priv->regs->rxic, 0);
- gfar_write(&priv->regs->rxic, priv->rxic);
- }
- if (likely(priv->txcoalescing)) {
- gfar_write(&priv->regs->txic, 0);
- gfar_write(&priv->regs->txic, priv->txic);
- }
+ gfar_configure_coalescing(priv,
+ gfargrp->rx_bit_map, gfargrp->tx_bit_map);
}
return rx_cleaned;
@@ -1964,44 +2624,49 @@ static int gfar_poll(struct napi_struct *napi, int budget)
static void gfar_netpoll(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
+ int i = 0;
/* If the device has multiple interrupts, run tx/rx */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- disable_irq(priv->interruptTransmit);
- disable_irq(priv->interruptReceive);
- disable_irq(priv->interruptError);
- gfar_interrupt(priv->interruptTransmit, dev);
- enable_irq(priv->interruptError);
- enable_irq(priv->interruptReceive);
- enable_irq(priv->interruptTransmit);
+ for (i = 0; i < priv->num_grps; i++) {
+ disable_irq(priv->gfargrp[i].interruptTransmit);
+ disable_irq(priv->gfargrp[i].interruptReceive);
+ disable_irq(priv->gfargrp[i].interruptError);
+ gfar_interrupt(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
+ enable_irq(priv->gfargrp[i].interruptError);
+ enable_irq(priv->gfargrp[i].interruptReceive);
+ enable_irq(priv->gfargrp[i].interruptTransmit);
+ }
} else {
- disable_irq(priv->interruptTransmit);
- gfar_interrupt(priv->interruptTransmit, dev);
- enable_irq(priv->interruptTransmit);
+ for (i = 0; i < priv->num_grps; i++) {
+ disable_irq(priv->gfargrp[i].interruptTransmit);
+ gfar_interrupt(priv->gfargrp[i].interruptTransmit,
+ &priv->gfargrp[i]);
+ enable_irq(priv->gfargrp[i].interruptTransmit);
}
}
#endif
/* The interrupt handler for devices with one interrupt */
-static irqreturn_t gfar_interrupt(int irq, void *dev_id)
+static irqreturn_t gfar_interrupt(int irq, void *grp_id)
{
- struct net_device *dev = dev_id;
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_grp *gfargrp = grp_id;
/* Save ievent for future reference */
- u32 events = gfar_read(&priv->regs->ievent);
+ u32 events = gfar_read(&gfargrp->regs->ievent);
/* Check for reception */
if (events & IEVENT_RX_MASK)
- gfar_receive(irq, dev_id);
+ gfar_receive(irq, grp_id);
/* Check for transmit completion */
if (events & IEVENT_TX_MASK)
- gfar_transmit(irq, dev_id);
+ gfar_transmit(irq, grp_id);
/* Check for errors */
if (events & IEVENT_ERR_MASK)
- gfar_error(irq, dev_id);
+ gfar_error(irq, grp_id);
return IRQ_HANDLED;
}
@@ -2015,12 +2680,14 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id)
static void adjust_link(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned long flags;
struct phy_device *phydev = priv->phydev;
int new_state = 0;
- spin_lock_irqsave(&priv->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+
if (phydev->link) {
u32 tempval = gfar_read(&regs->maccfg2);
u32 ecntrl = gfar_read(&regs->ecntrl);
@@ -2085,8 +2752,8 @@ static void adjust_link(struct net_device *dev)
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
-
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
}
/* Update the hash table based on the current list of multicast
@@ -2097,10 +2764,10 @@ static void gfar_set_multi(struct net_device *dev)
{
struct dev_mc_list *mc_ptr;
struct gfar_private *priv = netdev_priv(dev);
- struct gfar __iomem *regs = priv->regs;
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval;
- if(dev->flags & IFF_PROMISC) {
+ if (dev->flags & IFF_PROMISC) {
/* Set RCTRL to PROM */
tempval = gfar_read(&regs->rctrl);
tempval |= RCTRL_PROM;
@@ -2112,7 +2779,7 @@ static void gfar_set_multi(struct net_device *dev)
gfar_write(&regs->rctrl, tempval);
}
- if(dev->flags & IFF_ALLMULTI) {
+ if (dev->flags & IFF_ALLMULTI) {
/* Set the hash to rx all multicast frames */
gfar_write(&regs->igaddr0, 0xffffffff);
gfar_write(&regs->igaddr1, 0xffffffff);
@@ -2164,7 +2831,7 @@ static void gfar_set_multi(struct net_device *dev)
em_num = 0;
}
- if(dev->mc_count == 0)
+ if (dev->mc_count == 0)
return;
/* Parse the list, and set the appropriate bits */
@@ -2230,10 +2897,11 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int idx;
char tmpbuf[MAC_ADDR_LEN];
u32 tempval;
- u32 __iomem *macptr = &priv->regs->macstnaddr1;
+ u32 __iomem *macptr = &regs->macstnaddr1;
macptr += num*2;
@@ -2250,16 +2918,18 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
}
/* GFAR error interrupt handler */
-static irqreturn_t gfar_error(int irq, void *dev_id)
+static irqreturn_t gfar_error(int irq, void *grp_id)
{
- struct net_device *dev = dev_id;
- struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_grp *gfargrp = grp_id;
+ struct gfar __iomem *regs = gfargrp->regs;
+ struct gfar_private *priv= gfargrp->priv;
+ struct net_device *dev = priv->ndev;
/* Save ievent for future reference */
- u32 events = gfar_read(&priv->regs->ievent);
+ u32 events = gfar_read(&regs->ievent);
/* Clear IEVENT */
- gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK);
+ gfar_write(&regs->ievent, events & IEVENT_ERR_MASK);
/* Magic Packet is not an error. */
if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
@@ -2269,7 +2939,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
/* Hmm... */
if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",
- dev->name, events, gfar_read(&priv->regs->imask));
+ dev->name, events, gfar_read(&regs->imask));
/* Update the error counters */
if (events & IEVENT_TXE) {
@@ -2280,14 +2950,22 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
if (events & IEVENT_CRL)
dev->stats.tx_aborted_errors++;
if (events & IEVENT_XFUN) {
+ unsigned long flags;
+
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: TX FIFO underrun, "
"packet dropped.\n", dev->name);
dev->stats.tx_dropped++;
priv->extra_stats.tx_underrun++;
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+
/* Reactivate the Tx Queues */
- gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+ gfar_write(&regs->tstat, gfargrp->tstat);
+
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
}
if (netif_msg_tx_err(priv))
printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
@@ -2296,11 +2974,11 @@ static irqreturn_t gfar_error(int irq, void *dev_id)
dev->stats.rx_errors++;
priv->extra_stats.rx_bsy++;
- gfar_receive(irq, dev_id);
+ gfar_receive(irq, grp_id);
if (netif_msg_rx_err(priv))
printk(KERN_DEBUG "%s: busy error (rstat: %x)\n",
- dev->name, gfar_read(&priv->regs->rstat));
+ dev->name, gfar_read(&regs->rstat));
}
if (events & IEVENT_BABR) {
dev->stats.rx_errors++;
@@ -2331,6 +3009,9 @@ static struct of_device_id gfar_match[] =
.type = "network",
.compatible = "gianfar",
},
+ {
+ .compatible = "fsl,etsec2",
+ },
{},
};
MODULE_DEVICE_TABLE(of, gfar_match);
@@ -2342,8 +3023,9 @@ static struct of_platform_driver gfar_driver = {
.probe = gfar_probe,
.remove = gfar_remove,
- .suspend = gfar_suspend,
- .resume = gfar_resume,
+ .suspend = gfar_legacy_suspend,
+ .resume = gfar_legacy_resume,
+ .driver.pm = GFAR_PM_OPS,
};
static int __init gfar_init(void)
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 2cd94338b5d..cbb451011cb 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -7,8 +7,9 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ * Copyright 2002-2009 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -74,6 +75,13 @@
extern const char gfar_driver_name[];
extern const char gfar_driver_version[];
+/* MAXIMUM NUMBER OF QUEUES SUPPORTED */
+#define MAX_TX_QS 0x8
+#define MAX_RX_QS 0x8
+
+/* MAXIMUM NUMBER OF GROUPS SUPPORTED */
+#define MAXGROUPS 0x2
+
/* These need to be powers of 2 for this driver */
#define DEFAULT_TX_RING_SIZE 256
#define DEFAULT_RX_RING_SIZE 256
@@ -171,12 +179,63 @@ extern const char gfar_driver_version[];
#define MINFLR_INIT_SETTINGS 0x00000040
+/* Tqueue control */
+#define TQUEUE_EN0 0x00008000
+#define TQUEUE_EN1 0x00004000
+#define TQUEUE_EN2 0x00002000
+#define TQUEUE_EN3 0x00001000
+#define TQUEUE_EN4 0x00000800
+#define TQUEUE_EN5 0x00000400
+#define TQUEUE_EN6 0x00000200
+#define TQUEUE_EN7 0x00000100
+#define TQUEUE_EN_ALL 0x0000FF00
+
+#define TR03WT_WT0_MASK 0xFF000000
+#define TR03WT_WT1_MASK 0x00FF0000
+#define TR03WT_WT2_MASK 0x0000FF00
+#define TR03WT_WT3_MASK 0x000000FF
+
+#define TR47WT_WT4_MASK 0xFF000000
+#define TR47WT_WT5_MASK 0x00FF0000
+#define TR47WT_WT6_MASK 0x0000FF00
+#define TR47WT_WT7_MASK 0x000000FF
+
+/* Rqueue control */
+#define RQUEUE_EX0 0x00800000
+#define RQUEUE_EX1 0x00400000
+#define RQUEUE_EX2 0x00200000
+#define RQUEUE_EX3 0x00100000
+#define RQUEUE_EX4 0x00080000
+#define RQUEUE_EX5 0x00040000
+#define RQUEUE_EX6 0x00020000
+#define RQUEUE_EX7 0x00010000
+#define RQUEUE_EX_ALL 0x00FF0000
+
+#define RQUEUE_EN0 0x00000080
+#define RQUEUE_EN1 0x00000040
+#define RQUEUE_EN2 0x00000020
+#define RQUEUE_EN3 0x00000010
+#define RQUEUE_EN4 0x00000008
+#define RQUEUE_EN5 0x00000004
+#define RQUEUE_EN6 0x00000002
+#define RQUEUE_EN7 0x00000001
+#define RQUEUE_EN_ALL 0x000000FF
+
/* Init to do tx snooping for buffers and descriptors */
#define DMACTRL_INIT_SETTINGS 0x000000c3
#define DMACTRL_GRS 0x00000010
#define DMACTRL_GTS 0x00000008
-#define TSTAT_CLEAR_THALT 0x80000000
+#define TSTAT_CLEAR_THALT_ALL 0xFF000000
+#define TSTAT_CLEAR_THALT 0x80000000
+#define TSTAT_CLEAR_THALT0 0x80000000
+#define TSTAT_CLEAR_THALT1 0x40000000
+#define TSTAT_CLEAR_THALT2 0x20000000
+#define TSTAT_CLEAR_THALT3 0x10000000
+#define TSTAT_CLEAR_THALT4 0x08000000
+#define TSTAT_CLEAR_THALT5 0x04000000
+#define TSTAT_CLEAR_THALT6 0x02000000
+#define TSTAT_CLEAR_THALT7 0x01000000
/* Interrupt coalescing macros */
#define IC_ICEN 0x80000000
@@ -227,6 +286,13 @@ extern const char gfar_driver_version[];
#define TCTRL_IPCSEN 0x00004000
#define TCTRL_TUCSEN 0x00002000
#define TCTRL_VLINS 0x00001000
+#define TCTRL_THDF 0x00000800
+#define TCTRL_RFCPAUSE 0x00000010
+#define TCTRL_TFCPAUSE 0x00000008
+#define TCTRL_TXSCHED_MASK 0x00000006
+#define TCTRL_TXSCHED_INIT 0x00000000
+#define TCTRL_TXSCHED_PRIO 0x00000002
+#define TCTRL_TXSCHED_WRRS 0x00000004
#define TCTRL_INIT_CSUM (TCTRL_TUCSEN | TCTRL_IPCSEN)
#define IEVENT_INIT_CLEAR 0xffffffff
@@ -315,6 +381,84 @@ extern const char gfar_driver_version[];
#define BD_LFLAG(flags) ((flags) << 16)
#define BD_LENGTH_MASK 0x0000ffff
+#define CLASS_CODE_UNRECOG 0x00
+#define CLASS_CODE_DUMMY1 0x01
+#define CLASS_CODE_ETHERTYPE1 0x02
+#define CLASS_CODE_ETHERTYPE2 0x03
+#define CLASS_CODE_USER_PROG1 0x04
+#define CLASS_CODE_USER_PROG2 0x05
+#define CLASS_CODE_USER_PROG3 0x06
+#define CLASS_CODE_USER_PROG4 0x07
+#define CLASS_CODE_TCP_IPV4 0x08
+#define CLASS_CODE_UDP_IPV4 0x09
+#define CLASS_CODE_AH_ESP_IPV4 0x0a
+#define CLASS_CODE_SCTP_IPV4 0x0b
+#define CLASS_CODE_TCP_IPV6 0x0c
+#define CLASS_CODE_UDP_IPV6 0x0d
+#define CLASS_CODE_AH_ESP_IPV6 0x0e
+#define CLASS_CODE_SCTP_IPV6 0x0f
+
+#define FPR_FILER_MASK 0xFFFFFFFF
+#define MAX_FILER_IDX 0xFF
+
+/* RQFCR register bits */
+#define RQFCR_GPI 0x80000000
+#define RQFCR_HASHTBL_Q 0x00000000
+#define RQFCR_HASHTBL_0 0x00020000
+#define RQFCR_HASHTBL_1 0x00040000
+#define RQFCR_HASHTBL_2 0x00060000
+#define RQFCR_HASHTBL_3 0x00080000
+#define RQFCR_HASH 0x00010000
+#define RQFCR_CLE 0x00000200
+#define RQFCR_RJE 0x00000100
+#define RQFCR_AND 0x00000080
+#define RQFCR_CMP_EXACT 0x00000000
+#define RQFCR_CMP_MATCH 0x00000020
+#define RQFCR_CMP_NOEXACT 0x00000040
+#define RQFCR_CMP_NOMATCH 0x00000060
+
+/* RQFCR PID values */
+#define RQFCR_PID_MASK 0x00000000
+#define RQFCR_PID_PARSE 0x00000001
+#define RQFCR_PID_ARB 0x00000002
+#define RQFCR_PID_DAH 0x00000003
+#define RQFCR_PID_DAL 0x00000004
+#define RQFCR_PID_SAH 0x00000005
+#define RQFCR_PID_SAL 0x00000006
+#define RQFCR_PID_ETY 0x00000007
+#define RQFCR_PID_VID 0x00000008
+#define RQFCR_PID_PRI 0x00000009
+#define RQFCR_PID_TOS 0x0000000A
+#define RQFCR_PID_L4P 0x0000000B
+#define RQFCR_PID_DIA 0x0000000C
+#define RQFCR_PID_SIA 0x0000000D
+#define RQFCR_PID_DPT 0x0000000E
+#define RQFCR_PID_SPT 0x0000000F
+
+/* RQFPR when PID is 0x0001 */
+#define RQFPR_HDR_GE_512 0x00200000
+#define RQFPR_LERR 0x00100000
+#define RQFPR_RAR 0x00080000
+#define RQFPR_RARQ 0x00040000
+#define RQFPR_AR 0x00020000
+#define RQFPR_ARQ 0x00010000
+#define RQFPR_EBC 0x00008000
+#define RQFPR_VLN 0x00004000
+#define RQFPR_CFI 0x00002000
+#define RQFPR_JUM 0x00001000
+#define RQFPR_IPF 0x00000800
+#define RQFPR_FIF 0x00000400
+#define RQFPR_IPV4 0x00000200
+#define RQFPR_IPV6 0x00000100
+#define RQFPR_ICC 0x00000080
+#define RQFPR_ICV 0x00000040
+#define RQFPR_TCP 0x00000020
+#define RQFPR_UDP 0x00000010
+#define RQFPR_TUC 0x00000008
+#define RQFPR_TUV 0x00000004
+#define RQFPR_PER 0x00000002
+#define RQFPR_EER 0x00000001
+
/* TxBD status field bits */
#define TXBD_READY 0x8000
#define TXBD_PADCRC 0x4000
@@ -503,25 +647,32 @@ struct gfar_stats {
struct gfar {
u32 tsec_id; /* 0x.000 - Controller ID register */
- u8 res1[12];
+ u32 tsec_id2; /* 0x.004 - Controller ID2 register */
+ u8 res1[8];
u32 ievent; /* 0x.010 - Interrupt Event Register */
u32 imask; /* 0x.014 - Interrupt Mask Register */
u32 edis; /* 0x.018 - Error Disabled Register */
- u8 res2[4];
+ u32 emapg; /* 0x.01c - Group Error mapping register */
u32 ecntrl; /* 0x.020 - Ethernet Control Register */
u32 minflr; /* 0x.024 - Minimum Frame Length Register */
u32 ptv; /* 0x.028 - Pause Time Value Register */
u32 dmactrl; /* 0x.02c - DMA Control Register */
u32 tbipa; /* 0x.030 - TBI PHY Address Register */
- u8 res3[88];
+ u8 res2[28];
+ u32 fifo_rx_pause; /* 0x.050 - FIFO receive pause start threshold
+ register */
+ u32 fifo_rx_pause_shutoff; /* x.054 - FIFO receive starve shutoff
+ register */
+ u32 fifo_rx_alarm; /* 0x.058 - FIFO receive alarm start threshold
+ register */
+ u32 fifo_rx_alarm_shutoff; /*0x.05c - FIFO receive alarm starve
+ shutoff register */
+ u8 res3[44];
u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */
u8 res4[8];
u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */
u32 fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */
- u8 res5[4];
- u32 fifo_rx_pause; /* 0x.0a4 - FIFO receive pause threshold register */
- u32 fifo_rx_alarm; /* 0x.0a8 - FIFO receive alarm threshold register */
- u8 res6[84];
+ u8 res5[96];
u32 tctrl; /* 0x.100 - Transmit Control Register */
u32 tstat; /* 0x.104 - Transmit Status Register */
u32 dfvlan; /* 0x.108 - Default VLAN Control word */
@@ -572,7 +723,11 @@ struct gfar {
u8 res12[8];
u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */
u32 rqueue; /* 0x.314 - Receive queue control register */
- u8 res13[24];
+ u32 rir0; /* 0x.318 - Ring mapping register 0 */
+ u32 rir1; /* 0x.31c - Ring mapping register 1 */
+ u32 rir2; /* 0x.320 - Ring mapping register 2 */
+ u32 rir3; /* 0x.324 - Ring mapping register 3 */
+ u8 res13[8];
u32 rbifx; /* 0x.330 - Receive bit field extract control register */
u32 rqfar; /* 0x.334 - Receive queue filing table address register */
u32 rqfcr; /* 0x.338 - Receive queue filing table control register */
@@ -621,7 +776,7 @@ struct gfar {
u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */
u8 res18[12];
u8 gfar_mii_regs[24]; /* See gianfar_phy.h */
- u8 res19[4];
+ u32 ifctrl; /* 0x.538 - Interface control register */
u32 ifstat; /* 0x.53c - Interface Status Register */
u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */
u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */
@@ -682,8 +837,30 @@ struct gfar {
u8 res23c[248];
u32 attr; /* 0x.bf8 - Attributes Register */
u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */
- u8 res24[1024];
-
+ u8 res24[688];
+ u32 isrg0; /* 0x.eb0 - Interrupt steering group 0 register */
+ u32 isrg1; /* 0x.eb4 - Interrupt steering group 1 register */
+ u32 isrg2; /* 0x.eb8 - Interrupt steering group 2 register */
+ u32 isrg3; /* 0x.ebc - Interrupt steering group 3 register */
+ u8 res25[16];
+ u32 rxic0; /* 0x.ed0 - Ring 0 Rx interrupt coalescing */
+ u32 rxic1; /* 0x.ed4 - Ring 1 Rx interrupt coalescing */
+ u32 rxic2; /* 0x.ed8 - Ring 2 Rx interrupt coalescing */
+ u32 rxic3; /* 0x.edc - Ring 3 Rx interrupt coalescing */
+ u32 rxic4; /* 0x.ee0 - Ring 4 Rx interrupt coalescing */
+ u32 rxic5; /* 0x.ee4 - Ring 5 Rx interrupt coalescing */
+ u32 rxic6; /* 0x.ee8 - Ring 6 Rx interrupt coalescing */
+ u32 rxic7; /* 0x.eec - Ring 7 Rx interrupt coalescing */
+ u8 res26[32];
+ u32 txic0; /* 0x.f10 - Ring 0 Tx interrupt coalescing */
+ u32 txic1; /* 0x.f14 - Ring 1 Tx interrupt coalescing */
+ u32 txic2; /* 0x.f18 - Ring 2 Tx interrupt coalescing */
+ u32 txic3; /* 0x.f1c - Ring 3 Tx interrupt coalescing */
+ u32 txic4; /* 0x.f20 - Ring 4 Tx interrupt coalescing */
+ u32 txic5; /* 0x.f24 - Ring 5 Tx interrupt coalescing */
+ u32 txic6; /* 0x.f28 - Ring 6 Tx interrupt coalescing */
+ u32 txic7; /* 0x.f2c - Ring 7 Tx interrupt coalescing */
+ u8 res27[208];
};
/* Flags related to gianfar device features */
@@ -699,6 +876,133 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
+#if (MAXGROUPS == 2)
+#define DEFAULT_MAPPING 0xAA
+#else
+#define DEFAULT_MAPPING 0xFF
+#endif
+
+#define ISRG_SHIFT_TX 0x10
+#define ISRG_SHIFT_RX 0x18
+
+/* The same driver can operate in two modes */
+/* SQ_SG_MODE: Single Queue Single Group Mode
+ * (Backward compatible mode)
+ * MQ_MG_MODE: Multi Queue Multi Group mode
+ */
+enum {
+ SQ_SG_MODE = 0,
+ MQ_MG_MODE
+};
+
+/**
+ * struct gfar_priv_tx_q - per tx queue structure
+ * @txlock: per queue tx spin lock
+ * @tx_skbuff:skb pointers
+ * @skb_curtx: to be used skb pointer
+ * @skb_dirtytx:the last used skb pointer
+ * @qindex: index of this queue
+ * @dev: back pointer to the dev structure
+ * @grp: back pointer to the group to which this queue belongs
+ * @tx_bd_base: First tx buffer descriptor
+ * @cur_tx: Next free ring entry
+ * @dirty_tx: First buffer in line to be transmitted
+ * @tx_ring_size: Tx ring size
+ * @num_txbdfree: number of free TxBds
+ * @txcoalescing: enable/disable tx coalescing
+ * @txic: transmit interrupt coalescing value
+ * @txcount: coalescing value if based on tx frame count
+ * @txtime: coalescing value if based on time
+ */
+struct gfar_priv_tx_q {
+ spinlock_t txlock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct sk_buff ** tx_skbuff;
+ /* Buffer descriptor pointers */
+ dma_addr_t tx_bd_dma_base;
+ struct txbd8 *tx_bd_base;
+ struct txbd8 *cur_tx;
+ struct txbd8 *dirty_tx;
+ struct net_device *dev;
+ struct gfar_priv_grp *grp;
+ u16 skb_curtx;
+ u16 skb_dirtytx;
+ u16 qindex;
+ unsigned int tx_ring_size;
+ unsigned int num_txbdfree;
+ /* Configuration info for the coalescing features */
+ unsigned char txcoalescing;
+ unsigned long txic;
+ unsigned short txcount;
+ unsigned short txtime;
+};
+
+/**
+ * struct gfar_priv_rx_q - per rx queue structure
+ * @rxlock: per queue rx spin lock
+ * @rx_skbuff: skb pointers
+ * @skb_currx: currently use skb pointer
+ * @rx_bd_base: First rx buffer descriptor
+ * @cur_rx: Next free rx ring entry
+ * @qindex: index of this queue
+ * @dev: back pointer to the dev structure
+ * @rx_ring_size: Rx ring size
+ * @rxcoalescing: enable/disable rx-coalescing
+ * @rxic: receive interrupt coalescing vlaue
+ */
+
+struct gfar_priv_rx_q {
+ spinlock_t rxlock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct sk_buff ** rx_skbuff;
+ dma_addr_t rx_bd_dma_base;
+ struct rxbd8 *rx_bd_base;
+ struct rxbd8 *cur_rx;
+ struct net_device *dev;
+ struct gfar_priv_grp *grp;
+ u16 skb_currx;
+ u16 qindex;
+ unsigned int rx_ring_size;
+ /* RX Coalescing values */
+ unsigned char rxcoalescing;
+ unsigned long rxic;
+};
+
+/**
+ * struct gfar_priv_grp - per group structure
+ * @napi: the napi poll function
+ * @priv: back pointer to the priv structure
+ * @regs: the ioremapped register space for this group
+ * @grp_id: group id for this group
+ * @interruptTransmit: The TX interrupt number for this group
+ * @interruptReceive: The RX interrupt number for this group
+ * @interruptError: The ERROR interrupt number for this group
+ * @int_name_tx: tx interrupt name for this group
+ * @int_name_rx: rx interrupt name for this group
+ * @int_name_er: er interrupt name for this group
+ */
+
+struct gfar_priv_grp {
+ spinlock_t grplock __attribute__ ((aligned (SMP_CACHE_BYTES)));
+ struct napi_struct napi;
+ struct gfar_private *priv;
+ struct gfar __iomem *regs;
+ unsigned int grp_id;
+ unsigned long rx_bit_map;
+ unsigned long tx_bit_map;
+ unsigned long num_tx_queues;
+ unsigned long num_rx_queues;
+ unsigned int rstat;
+ unsigned int tstat;
+ unsigned int imask;
+ unsigned int ievent;
+ unsigned int interruptTransmit;
+ unsigned int interruptReceive;
+ unsigned int interruptError;
+
+ char int_name_tx[GFAR_INT_NAME_MAX];
+ char int_name_rx[GFAR_INT_NAME_MAX];
+ char int_name_er[GFAR_INT_NAME_MAX];
+};
+
/* Struct stolen almost completely (and shamelessly) from the FCC enet source
* (Ok, that's not so true anymore, but there is a family resemblence)
* The GFAR buffer descriptors track the ring buffers. The rx_bd_base
@@ -709,62 +1013,36 @@ struct gfar {
* the buffer descriptor determines the actual condition.
*/
struct gfar_private {
- /* Fields controlled by TX lock */
- spinlock_t txlock;
- /* Pointer to the array of skbuffs */
- struct sk_buff ** tx_skbuff;
+ /* Indicates how many tx, rx queues are enabled */
+ unsigned int num_tx_queues;
+ unsigned int num_rx_queues;
+ unsigned int num_grps;
+ unsigned int mode;
- /* next free skb in the array */
- u16 skb_curtx;
-
- /* First skb in line to be transmitted */
- u16 skb_dirtytx;
-
- /* Configuration info for the coalescing features */
- unsigned char txcoalescing;
- unsigned long txic;
-
- /* Buffer descriptor pointers */
- struct txbd8 *tx_bd_base; /* First tx buffer descriptor */
- struct txbd8 *cur_tx; /* Next free ring entry */
- struct txbd8 *dirty_tx; /* First buffer in line
- to be transmitted */
- unsigned int tx_ring_size;
- unsigned int num_txbdfree; /* number of TxBDs free */
-
- /* RX Locked fields */
- spinlock_t rxlock;
+ /* The total tx and rx ring size for the enabled queues */
+ unsigned int total_tx_ring_size;
+ unsigned int total_rx_ring_size;
struct device_node *node;
struct net_device *ndev;
struct of_device *ofdev;
- struct napi_struct napi;
-
- /* skb array and index */
- struct sk_buff ** rx_skbuff;
- u16 skb_currx;
-
- /* RX Coalescing values */
- unsigned char rxcoalescing;
- unsigned long rxic;
- struct rxbd8 *rx_bd_base; /* First Rx buffers */
- struct rxbd8 *cur_rx; /* Next free rx ring entry */
+ struct gfar_priv_grp gfargrp[MAXGROUPS];
+ struct gfar_priv_tx_q *tx_queue[MAX_TX_QS];
+ struct gfar_priv_rx_q *rx_queue[MAX_RX_QS];
- /* RX parameters */
- unsigned int rx_ring_size;
+ /* RX per device parameters */
unsigned int rx_buffer_size;
unsigned int rx_stash_size;
unsigned int rx_stash_index;
+ u32 cur_filer_idx;
+
struct sk_buff_head rx_recycle;
struct vlan_group *vlgrp;
- /* Unprotected fields */
- /* Pointer to the GFAR memory mapped Registers */
- struct gfar __iomem *regs;
/* Hash registers and their width */
u32 __iomem *hash_regs[16];
@@ -785,13 +1063,10 @@ struct gfar_private {
unsigned char rx_csum_enable:1,
extended_hash:1,
bd_stash_en:1,
+ rx_filer_enable:1,
wol_en:1; /* Wake-on-LAN enabled */
unsigned short padding;
- unsigned int interruptTransmit;
- unsigned int interruptReceive;
- unsigned int interruptError;
-
/* PHY stuff */
struct phy_device *phydev;
struct mii_bus *mii_bus;
@@ -803,14 +1078,13 @@ struct gfar_private {
struct work_struct reset_task;
- char int_name_tx[GFAR_INT_NAME_MAX];
- char int_name_rx[GFAR_INT_NAME_MAX];
- char int_name_er[GFAR_INT_NAME_MAX];
-
/* Network Statistics */
struct gfar_extra_stats extra_stats;
};
+extern unsigned int ftp_rqfpr[MAX_FILER_IDX + 1];
+extern unsigned int ftp_rqfcr[MAX_FILER_IDX + 1];
+
static inline u32 gfar_read(volatile unsigned __iomem *addr)
{
u32 val;
@@ -823,12 +1097,28 @@ static inline void gfar_write(volatile unsigned __iomem *addr, u32 val)
out_be32(addr, val);
}
+static inline void gfar_write_filer(struct gfar_private *priv,
+ unsigned int far, unsigned int fcr, unsigned int fpr)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+
+ gfar_write(&regs->rqfar, far);
+ gfar_write(&regs->rqfcr, fcr);
+ gfar_write(&regs->rqfpr, fpr);
+}
+
+extern void lock_rx_qs(struct gfar_private *priv);
+extern void lock_tx_qs(struct gfar_private *priv);
+extern void unlock_rx_qs(struct gfar_private *priv);
+extern void unlock_tx_qs(struct gfar_private *priv);
extern irqreturn_t gfar_receive(int irq, void *dev_id);
extern int startup_gfar(struct net_device *dev);
extern void stop_gfar(struct net_device *dev);
extern void gfar_halt(struct net_device *dev);
extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
int enable, u32 regnum, u32 read);
+extern void gfar_configure_coalescing(struct gfar_private *priv,
+ unsigned long tx_mask, unsigned long rx_mask);
void gfar_init_sysfs(struct net_device *dev);
extern const struct ethtool_ops gfar_ethtool_ops;
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 6c144b525b4..1010367695e 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -7,8 +7,9 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2003,2004 Freescale Semiconductor, Inc.
+ * Copyright 2003-2006, 2008-2009 Freescale Semiconductor, Inc.
*
* This software may be used and distributed according to
* the terms of the GNU Public License, Version 2, incorporated herein
@@ -41,7 +42,7 @@
#include "gianfar.h"
extern void gfar_start(struct net_device *dev);
-extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
+extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
#define GFAR_MAX_COAL_USECS 0xffff
#define GFAR_MAX_COAL_FRAMES 0xff
@@ -136,10 +137,11 @@ static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
{
int i;
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
u64 *extra = (u64 *) & priv->extra_stats;
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_RMON) {
- u32 __iomem *rmon = (u32 __iomem *) & priv->regs->rmon;
+ u32 __iomem *rmon = (u32 __iomem *) &regs->rmon;
struct gfar_stats *stats = (struct gfar_stats *) buf;
for (i = 0; i < GFAR_RMON_LEN; i++)
@@ -197,12 +199,18 @@ static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct gfar_private *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_priv_tx_q *tx_queue = NULL;
if (NULL == phydev)
return -ENODEV;
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
- cmd->maxtxpkt = get_icft_value(priv->txic);
- cmd->maxrxpkt = get_icft_value(priv->rxic);
+ /* etsec-1.7 and older versions have only one txic
+ * and rxic regs although they support multiple queues */
+ cmd->maxtxpkt = get_icft_value(tx_queue->txic);
+ cmd->maxrxpkt = get_icft_value(rx_queue->rxic);
return phy_ethtool_gset(phydev, cmd);
}
@@ -218,7 +226,7 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, voi
{
int i;
struct gfar_private *priv = netdev_priv(dev);
- u32 __iomem *theregs = (u32 __iomem *) priv->regs;
+ u32 __iomem *theregs = (u32 __iomem *) priv->gfargrp[0].regs;
u32 *buf = (u32 *) regbuf;
for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
@@ -279,6 +287,8 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_rx_q *rx_queue = NULL;
+ struct gfar_priv_tx_q *tx_queue = NULL;
unsigned long rxtime;
unsigned long rxcount;
unsigned long txtime;
@@ -290,10 +300,13 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
if (NULL == priv->phydev)
return -ENODEV;
- rxtime = get_ictt_value(priv->rxic);
- rxcount = get_icft_value(priv->rxic);
- txtime = get_ictt_value(priv->txic);
- txcount = get_icft_value(priv->txic);
+ rx_queue = priv->rx_queue[0];
+ tx_queue = priv->tx_queue[0];
+
+ rxtime = get_ictt_value(rx_queue->rxic);
+ rxcount = get_icft_value(rx_queue->rxic);
+ txtime = get_ictt_value(tx_queue->txic);
+ txcount = get_icft_value(tx_queue->txic);
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime);
cvals->rx_max_coalesced_frames = rxcount;
@@ -339,16 +352,23 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ int i = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP;
/* Set up rx coalescing */
+ /* As of now, we will enable/disable coalescing for all
+ * queues together in case of eTSEC2, this will be modified
+ * along with the ethtool interface */
if ((cvals->rx_coalesce_usecs == 0) ||
- (cvals->rx_max_coalesced_frames == 0))
- priv->rxcoalescing = 0;
- else
- priv->rxcoalescing = 1;
+ (cvals->rx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_rx_queues; i++)
+ priv->rx_queue[i]->rxcoalescing = 1;
+ }
if (NULL == priv->phydev)
return -ENODEV;
@@ -366,15 +386,21 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->rxic = mk_ic_value(cvals->rx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rxic = mk_ic_value(
+ cvals->rx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs));
+ }
/* Set up tx coalescing */
if ((cvals->tx_coalesce_usecs == 0) ||
- (cvals->tx_max_coalesced_frames == 0))
- priv->txcoalescing = 0;
- else
- priv->txcoalescing = 1;
+ (cvals->tx_max_coalesced_frames == 0)) {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 0;
+ } else {
+ for (i = 0; i < priv->num_tx_queues; i++)
+ priv->tx_queue[i]->txcoalescing = 1;
+ }
/* Check the bounds of the values */
if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
@@ -389,16 +415,13 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
return -EINVAL;
}
- priv->txic = mk_ic_value(cvals->tx_max_coalesced_frames,
- gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
-
- gfar_write(&priv->regs->rxic, 0);
- if (priv->rxcoalescing)
- gfar_write(&priv->regs->rxic, priv->rxic);
+ for (i = 0; i < priv->num_tx_queues; i++) {
+ priv->tx_queue[i]->txic = mk_ic_value(
+ cvals->tx_max_coalesced_frames,
+ gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs));
+ }
- gfar_write(&priv->regs->txic, 0);
- if (priv->txcoalescing)
- gfar_write(&priv->regs->txic, priv->txic);
+ gfar_configure_coalescing(priv, 0xFF, 0xFF);
return 0;
}
@@ -409,6 +432,11 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
struct gfar_private *priv = netdev_priv(dev);
+ struct gfar_priv_tx_q *tx_queue = NULL;
+ struct gfar_priv_rx_q *rx_queue = NULL;
+
+ tx_queue = priv->tx_queue[0];
+ rx_queue = priv->rx_queue[0];
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -418,10 +446,10 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
/* Values changeable by the user. The valid values are
* in the range 1 to the "*_max_pending" counterpart above.
*/
- rvals->rx_pending = priv->rx_ring_size;
- rvals->rx_mini_pending = priv->rx_ring_size;
- rvals->rx_jumbo_pending = priv->rx_ring_size;
- rvals->tx_pending = priv->tx_ring_size;
+ rvals->rx_pending = rx_queue->rx_ring_size;
+ rvals->rx_mini_pending = rx_queue->rx_ring_size;
+ rvals->rx_jumbo_pending = rx_queue->rx_ring_size;
+ rvals->tx_pending = tx_queue->tx_ring_size;
}
/* Change the current ring parameters, stopping the controller if
@@ -431,7 +459,7 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv
static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
struct gfar_private *priv = netdev_priv(dev);
- int err = 0;
+ int err = 0, i = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
return -EINVAL;
@@ -451,34 +479,41 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva
return -EINVAL;
}
+
if (dev->flags & IFF_UP) {
unsigned long flags;
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_rx_qs(priv);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
- gfar_clean_rx_ring(dev, priv->rx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
}
/* Change the size */
- priv->rx_ring_size = rvals->rx_pending;
- priv->tx_ring_size = rvals->tx_pending;
- priv->num_txbdfree = priv->tx_ring_size;
+ for (i = 0; i < priv->num_rx_queues; i++) {
+ priv->rx_queue[i]->rx_ring_size = rvals->rx_pending;
+ priv->tx_queue[i]->tx_ring_size = rvals->tx_pending;
+ priv->tx_queue[i]->num_txbdfree = priv->tx_queue[i]->tx_ring_size;
+ }
/* Rebuild the rings with the new size */
if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
}
return err;
}
@@ -487,23 +522,28 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
{
struct gfar_private *priv = netdev_priv(dev);
unsigned long flags;
- int err = 0;
+ int err = 0, i = 0;
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM))
return -EOPNOTSUPP;
+
if (dev->flags & IFF_UP) {
/* Halt TX and RX, and process the frames which
* have already been received */
- spin_lock_irqsave(&priv->txlock, flags);
- spin_lock(&priv->rxlock);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
+ lock_rx_qs(priv);
gfar_halt(dev);
- spin_unlock(&priv->rxlock);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_tx_qs(priv);
+ unlock_rx_qs(priv);
+ local_irq_save(flags);
- gfar_clean_rx_ring(dev, priv->rx_ring_size);
+ for (i = 0; i < priv->num_rx_queues; i++)
+ gfar_clean_rx_ring(priv->rx_queue[i],
+ priv->rx_queue[i]->rx_ring_size);
/* Now we take down the rings to rebuild them */
stop_gfar(dev);
@@ -515,7 +555,7 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data)
if (dev->flags & IFF_UP) {
err = startup_gfar(dev);
- netif_wake_queue(dev);
+ netif_tx_wake_all_queues(dev);
}
return err;
}
@@ -605,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
}
#endif
+static int gfar_ethflow_to_class(int flow_type, u64 *class)
+{
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ *class = CLASS_CODE_TCP_IPV4;
+ break;
+ case UDP_V4_FLOW:
+ *class = CLASS_CODE_UDP_IPV4;
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV4;
+ break;
+ case SCTP_V4_FLOW:
+ *class = CLASS_CODE_SCTP_IPV4;
+ break;
+ case TCP_V6_FLOW:
+ *class = CLASS_CODE_TCP_IPV6;
+ break;
+ case UDP_V6_FLOW:
+ *class = CLASS_CODE_UDP_IPV6;
+ break;
+ case AH_V6_FLOW:
+ case ESP_V6_FLOW:
+ *class = CLASS_CODE_AH_ESP_IPV6;
+ break;
+ case SCTP_V6_FLOW:
+ *class = CLASS_CODE_SCTP_IPV6;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
+{
+ u32 fcr = 0x0, fpr = FPR_FILER_MASK;
+
+ if (ethflow & RXH_L2DA) {
+ fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+
+ fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_VLAN) {
+ fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_IP_SRC) {
+ fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & (RXH_IP_DST)) {
+ fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L3_PROTO) {
+ fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_0_1) {
+ fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ if (ethflow & RXH_L4_B_2_3) {
+ fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
+ RQFCR_AND | RQFCR_HASHTBL_0;
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+}
+
+static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
+{
+ unsigned int last_rule_idx = priv->cur_filer_idx;
+ unsigned int cmp_rqfpr;
+ unsigned int local_rqfpr[MAX_FILER_IDX + 1];
+ unsigned int local_rqfcr[MAX_FILER_IDX + 1];
+ int i = 0x0, k = 0x0;
+ int j = MAX_FILER_IDX, l = 0x0;
+
+ switch (class) {
+ case TCP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
+ break;
+ case UDP_V4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
+ break;
+ case TCP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
+ break;
+ case UDP_V6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
+ break;
+ case IPV4_FLOW:
+ cmp_rqfpr = RQFPR_IPV4;
+ case IPV6_FLOW:
+ cmp_rqfpr = RQFPR_IPV6;
+ break;
+ default:
+ printk(KERN_ERR "Right now this class is not supported\n");
+ return 0;
+ }
+
+ for (i = 0; i < MAX_FILER_IDX + 1; i++) {
+ local_rqfpr[j] = ftp_rqfpr[i];
+ local_rqfcr[j] = ftp_rqfcr[i];
+ j--;
+ if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE |
+ RQFCR_CLE |RQFCR_AND)) &&
+ (ftp_rqfpr[i] == cmp_rqfpr))
+ break;
+ }
+
+ if (i == MAX_FILER_IDX + 1) {
+ printk(KERN_ERR "No parse rule found, ");
+ printk(KERN_ERR "can't create hash rules\n");
+ return 0;
+ }
+
+ /* If a match was found, then it begins the starting of a cluster rule
+ * if it was already programmed, we need to overwrite these rules
+ */
+ for (l = i+1; l < MAX_FILER_IDX; l++) {
+ if ((ftp_rqfcr[l] & RQFCR_CLE) &&
+ !(ftp_rqfcr[l] & RQFCR_AND)) {
+ ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
+ RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
+ ftp_rqfpr[l] = FPR_FILER_MASK;
+ gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]);
+ break;
+ }
+
+ if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND))
+ continue;
+ else {
+ local_rqfpr[j] = ftp_rqfpr[l];
+ local_rqfcr[j] = ftp_rqfcr[l];
+ j--;
+ }
+ }
+
+ priv->cur_filer_idx = l - 1;
+ last_rule_idx = l;
+
+ /* hash rules */
+ ethflow_to_filer_rules(priv, ethflow);
+
+ /* Write back the popped out rules again */
+ for (k = j+1; k < MAX_FILER_IDX; k++) {
+ ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
+ ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
+ gfar_write_filer(priv, priv->cur_filer_idx,
+ local_rqfcr[k], local_rqfpr[k]);
+ if (!priv->cur_filer_idx)
+ break;
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
+ }
+
+ return 1;
+}
+
+static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
+{
+ u64 class;
+
+ if (!gfar_ethflow_to_class(cmd->flow_type, &class))
+ return -EINVAL;
+
+ if (class < CLASS_CODE_USER_PROG1 ||
+ class > CLASS_CODE_SCTP_IPV6)
+ return -EINVAL;
+
+ /* write the filer rules here */
+ if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
+ return -1;
+
+ return 0;
+}
+
+static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int ret = 0;
+
+ switch(cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ ret = gfar_set_hash_opts(priv, cmd);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
const struct ethtool_ops gfar_ethtool_ops = {
.get_settings = gfar_gsettings,
.set_settings = gfar_ssettings,
@@ -630,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
.get_wol = gfar_get_wol,
.set_wol = gfar_set_wol,
#endif
+ .set_rxnfc = gfar_set_nfc,
};
diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c
index dd26da74f27..b98c6c51229 100644
--- a/drivers/net/gianfar_sysfs.c
+++ b/drivers/net/gianfar_sysfs.c
@@ -8,8 +8,9 @@
*
* Author: Andy Fleming
* Maintainer: Kumar Gala (galak@kernel.crashing.org)
+ * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
*
- * Copyright (c) 2002-2005 Freescale Semiconductor, Inc.
+ * Copyright 2002-2009 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -49,6 +50,7 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
int new_setting = 0;
u32 temp;
unsigned long flags;
@@ -56,30 +58,34 @@ static ssize_t gfar_set_bd_stash(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BD_STASHING))
return count;
+
/* Find out the new setting */
if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
new_setting = 1;
- else if (!strncmp("off", buf, count - 1)
- || !strncmp("0", buf, count - 1))
+ else if (!strncmp("off", buf, count - 1) ||
+ !strncmp("0", buf, count - 1))
new_setting = 0;
else
return count;
- spin_lock_irqsave(&priv->rxlock, flags);
+
+ local_irq_save(flags);
+ lock_rx_qs(priv);
/* Set the new stashing value */
priv->bd_stash_en = new_setting;
- temp = gfar_read(&priv->regs->attr);
+ temp = gfar_read(&regs->attr);
if (new_setting)
temp |= ATTR_BDSTASH;
else
temp &= ~(ATTR_BDSTASH);
- gfar_write(&priv->regs->attr, temp);
+ gfar_write(&regs->attr, temp);
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -99,6 +105,7 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -106,7 +113,9 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- spin_lock_irqsave(&priv->rxlock, flags);
+ local_irq_save(flags);
+ lock_rx_qs(priv);
+
if (length > priv->rx_buffer_size)
goto out;
@@ -115,23 +124,24 @@ static ssize_t gfar_set_rx_stash_size(struct device *dev,
priv->rx_stash_size = length;
- temp = gfar_read(&priv->regs->attreli);
+ temp = gfar_read(&regs->attreli);
temp &= ~ATTRELI_EL_MASK;
temp |= ATTRELI_EL(length);
- gfar_write(&priv->regs->attreli, temp);
+ gfar_write(&regs->attreli, temp);
/* Turn stashing on/off as appropriate */
- temp = gfar_read(&priv->regs->attr);
+ temp = gfar_read(&regs->attr);
if (length)
temp |= ATTR_BUFSTASH;
else
temp &= ~(ATTR_BUFSTASH);
- gfar_write(&priv->regs->attr, temp);
+ gfar_write(&regs->attr, temp);
out:
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -154,6 +164,7 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned short index = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -161,7 +172,9 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_BUF_STASHING))
return count;
- spin_lock_irqsave(&priv->rxlock, flags);
+ local_irq_save(flags);
+ lock_rx_qs(priv);
+
if (index > priv->rx_stash_size)
goto out;
@@ -170,13 +183,14 @@ static ssize_t gfar_set_rx_stash_index(struct device *dev,
priv->rx_stash_index = index;
- temp = gfar_read(&priv->regs->attreli);
+ temp = gfar_read(&regs->attreli);
temp &= ~ATTRELI_EI_MASK;
temp |= ATTRELI_EI(index);
- gfar_write(&priv->regs->attreli, flags);
+ gfar_write(&regs->attreli, temp);
out:
- spin_unlock_irqrestore(&priv->rxlock, flags);
+ unlock_rx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -198,6 +212,7 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int length = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -205,16 +220,18 @@ static ssize_t gfar_set_fifo_threshold(struct device *dev,
if (length > GFAR_MAX_FIFO_THRESHOLD)
return count;
- spin_lock_irqsave(&priv->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_threshold = length;
- temp = gfar_read(&priv->regs->fifo_tx_thr);
+ temp = gfar_read(&regs->fifo_tx_thr);
temp &= ~FIFO_TX_THR_MASK;
temp |= length;
- gfar_write(&priv->regs->fifo_tx_thr, temp);
+ gfar_write(&regs->fifo_tx_thr, temp);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -235,6 +252,7 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -242,16 +260,18 @@ static ssize_t gfar_set_fifo_starve(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE)
return count;
- spin_lock_irqsave(&priv->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_starve = num;
- temp = gfar_read(&priv->regs->fifo_tx_starve);
+ temp = gfar_read(&regs->fifo_tx_starve);
temp &= ~FIFO_TX_STARVE_MASK;
temp |= num;
- gfar_write(&priv->regs->fifo_tx_starve, temp);
+ gfar_write(&regs->fifo_tx_starve, temp);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
@@ -273,6 +293,7 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
const char *buf, size_t count)
{
struct gfar_private *priv = netdev_priv(to_net_dev(dev));
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
unsigned int num = simple_strtoul(buf, NULL, 0);
u32 temp;
unsigned long flags;
@@ -280,16 +301,18 @@ static ssize_t gfar_set_fifo_starve_off(struct device *dev,
if (num > GFAR_MAX_FIFO_STARVE_OFF)
return count;
- spin_lock_irqsave(&priv->txlock, flags);
+ local_irq_save(flags);
+ lock_tx_qs(priv);
priv->fifo_starve_off = num;
- temp = gfar_read(&priv->regs->fifo_tx_starve_shutoff);
+ temp = gfar_read(&regs->fifo_tx_starve_shutoff);
temp &= ~FIFO_TX_STARVE_OFF_MASK;
temp |= num;
- gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp);
+ gfar_write(&regs->fifo_tx_starve_shutoff, temp);
- spin_unlock_irqrestore(&priv->txlock, flags);
+ unlock_tx_qs(priv);
+ local_irq_restore(flags);
return count;
}
diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c
index f7519a59494..ea85075a89a 100644
--- a/drivers/net/hamachi.c
+++ b/drivers/net/hamachi.c
@@ -407,10 +407,9 @@ that case.
/* A few values that may be tweaked. */
/* Size of each temporary Rx buffer, calculated as:
* 1518 bytes (ethernet packet) + 2 bytes (to get 8 byte alignment for
- * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum +
- * 2 more because we use skb_reserve.
+ * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum
*/
-#define PKT_BUF_SZ 1538
+#define PKT_BUF_SZ 1536
/* For now, this is going to be set to the maximum size of an ethernet
* packet. Eventually, we may want to make it a variable that is
@@ -873,7 +872,7 @@ static int hamachi_open(struct net_device *dev)
u32 rx_int_var, tx_int_var;
u16 fifo_info;
- i = request_irq(dev->irq, &hamachi_interrupt, IRQF_SHARED, dev->name, dev);
+ i = request_irq(dev->irq, hamachi_interrupt, IRQF_SHARED, dev->name, dev);
if (i)
return i;
@@ -1152,12 +1151,13 @@ static void hamachi_tx_timeout(struct net_device *dev)
}
/* Fill in the Rx buffers. Handle allocation failure gracefully. */
for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = netdev_alloc_skb(dev, hmp->rx_buf_sz);
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb_ip_align(dev, hmp->rx_buf_sz);
hmp->rx_skbuff[i] = skb;
if (skb == NULL)
break;
- skb_reserve(skb, 2); /* 16 byte align the IP header. */
hmp->rx_ring[i].addr = cpu_to_leXX(pci_map_single(hmp->pci_dev,
skb->data, hmp->rx_buf_sz, PCI_DMA_FROMDEVICE));
hmp->rx_ring[i].status_n_length = cpu_to_le32(DescOwn |
@@ -1196,7 +1196,7 @@ static void hamachi_init_ring(struct net_device *dev)
* card. -KDU
*/
hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ :
- (((dev->mtu+26+7) & ~7) + 2 + 16));
+ (((dev->mtu+26+7) & ~7) + 16));
/* Initialize all Rx descriptors. */
for (i = 0; i < RX_RING_SIZE; i++) {
@@ -1566,8 +1566,8 @@ static int hamachi_rx(struct net_device *dev)
#endif
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
#ifdef RX_CHECKSUM
printk(KERN_ERR "%s: rx_copybreak non-zero "
"not good with RX_CHECKSUM\n", dev->name);
@@ -1722,10 +1722,10 @@ static void hamachi_error(struct net_device *dev, int intr_status)
readl(ioaddr + 0x370);
readl(ioaddr + 0x3F0);
}
- if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone))
- && hamachi_debug)
+ if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone)) &&
+ hamachi_debug)
printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
- dev->name, intr_status);
+ dev->name, intr_status);
/* Hmmmmm, it's not clear how to recover from PCI faults. */
if (intr_status & (IntrTxPCIErr | IntrTxPCIFault))
hmp->stats.tx_fifo_errors++;
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index fb588301a05..689b9bd377a 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -34,6 +34,7 @@
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/semaphore.h>
+#include <linux/compat.h>
#include <asm/atomic.h>
#define SIXPACK_VERSION "Revision: 0.3.0"
@@ -777,6 +778,23 @@ static int sixpack_ioctl(struct tty_struct *tty, struct file *file,
return err;
}
+#ifdef CONFIG_COMPAT
+static long sixpack_compat_ioctl(struct tty_struct * tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case SIOCGIFNAME:
+ case SIOCGIFENCAP:
+ case SIOCSIFENCAP:
+ case SIOCSIFHWADDR:
+ return sixpack_ioctl(tty, file, cmd,
+ (unsigned long)compat_ptr(arg));
+ }
+
+ return -ENOIOCTLCMD;
+}
+#endif
+
static struct tty_ldisc_ops sp_ldisc = {
.owner = THIS_MODULE,
.magic = TTY_LDISC_MAGIC,
@@ -784,6 +802,9 @@ static struct tty_ldisc_ops sp_ldisc = {
.open = sixpack_open,
.close = sixpack_close,
.ioctl = sixpack_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = sixpack_compat_ioctl,
+#endif
.receive_buf = sixpack_receive_buf,
.write_wakeup = sixpack_write_wakeup,
};
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index e344c84c0ef..a3c0dc9d8b9 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -596,16 +596,16 @@ static int receive(struct net_device *dev, int cnt)
if (!(notbitstream & (0x1fc << j)))
state = 0;
- /* not flag received */
- else if (!(bitstream & (0x1fe << j)) != (0x0fc << j)) {
+ /* flag received */
+ else if ((bitstream & (0x1fe << j)) == (0x0fc << j)) {
if (state)
do_rxpacket(dev);
bc->hdlcrx.bufcnt = 0;
bc->hdlcrx.bufptr = bc->hdlcrx.buf;
state = 1;
numbits = 7-j;
- }
}
+ }
/* stuffed bit */
else if (unlikely((bitstream & (0x1f8 << j)) == (0xf8 << j))) {
diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c
index ed60fd66427..0cab992b3d1 100644
--- a/drivers/net/hamradio/baycom_ser_fdx.c
+++ b/drivers/net/hamradio/baycom_ser_fdx.c
@@ -35,7 +35,7 @@
* driver only supports standard serial hardware (8250, 16450, 16550A)
*
* This modem usually draws its supply current out of the otherwise unused
- * TXD pin of the serial port. Thus a contignuous stream of 0x00-bytes
+ * TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes
* is transmitted to achieve a positive supply voltage.
*
* hsk: This is a 4800 baud FSK modem, designed for TNC use. It works fine
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index fe893c91a01..ae5f11c8fc1 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -167,10 +167,7 @@ static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev)
static inline int dev_is_ethdev(struct net_device *dev)
{
- return (
- dev->type == ARPHRD_ETHER
- && strncmp(dev->name, "dummy", 5)
- );
+ return (dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5));
}
/* ------------------------------------------------------------------------ */
@@ -186,7 +183,7 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty
struct ethhdr *eth;
struct bpqdev *bpq;
- if (dev_net(dev) != &init_net)
+ if (!net_eq(dev_net(dev), &init_net))
goto drop;
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
@@ -552,7 +549,7 @@ static int bpq_device_event(struct notifier_block *this,unsigned long event, voi
{
struct net_device *dev = (struct net_device *)ptr;
- if (dev_net(dev) != &init_net)
+ if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (!dev_is_ethdev(dev))
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 950f3bb21f9..9ee76b42668 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -331,8 +331,8 @@ static int __init dmascc_init(void)
for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) {
j = (io[i] -
hw[h].io_region) / hw[h].io_delta;
- if (j >= 0 && j < hw[h].num_devs
- && hw[h].io_region +
+ if (j >= 0 && j < hw[h].num_devs &&
+ hw[h].io_region +
j * hw[h].io_delta == io[i]) {
base[j] = io[i];
}
@@ -396,8 +396,8 @@ static int __init dmascc_init(void)
t_val =
inb(t1[i]) + (inb(t1[i]) << 8);
/* Also check whether counter did wrap */
- if (t_val == 0
- || t_val > TMR_0_HZ / HZ * 10)
+ if (t_val == 0 ||
+ t_val > TMR_0_HZ / HZ * 10)
counting[i] = 0;
delay[i] = jiffies - start[i];
}
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
index db4b7f1603f..7db0a1c3216 100644
--- a/drivers/net/hamradio/mkiss.c
+++ b/drivers/net/hamradio/mkiss.c
@@ -36,6 +36,7 @@
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/jiffies.h>
+#include <linux/compat.h>
#include <net/ax25.h>
@@ -898,6 +899,23 @@ static int mkiss_ioctl(struct tty_struct *tty, struct file *file,
return err;
}
+#ifdef CONFIG_COMPAT
+static long mkiss_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case SIOCGIFNAME:
+ case SIOCGIFENCAP:
+ case SIOCSIFENCAP:
+ case SIOCSIFHWADDR:
+ return mkiss_ioctl(tty, file, cmd,
+ (unsigned long)compat_ptr(arg));
+ }
+
+ return -ENOIOCTLCMD;
+}
+#endif
+
/*
* Handle the 'receiver data ready' interrupt.
* This function is called by the 'tty_io' module in the kernel when
@@ -972,6 +990,9 @@ static struct tty_ldisc_ops ax_ldisc = {
.open = mkiss_open,
.close = mkiss_close,
.ioctl = mkiss_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = mkiss_compat_ioctl,
+#endif
.receive_buf = mkiss_receive_buf,
.write_wakeup = mkiss_write_wakeup
};
diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c
index 0486cbe01ad..efdbcad63c6 100644
--- a/drivers/net/hp-plus.c
+++ b/drivers/net/hp-plus.c
@@ -187,8 +187,8 @@ static int __init hpp_probe1(struct net_device *dev, int ioaddr)
return -EBUSY;
/* Check for the HP+ signature, 50 48 0x 53. */
- if (inw(ioaddr + HP_ID) != 0x4850
- || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) {
+ if (inw(ioaddr + HP_ID) != 0x4850 ||
+ (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) {
retval = -ENODEV;
goto out;
}
diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c
index dd866513806..90f890e7c5e 100644
--- a/drivers/net/hp100.c
+++ b/drivers/net/hp100.c
@@ -993,8 +993,8 @@ static void hp100_mmuinit(struct net_device *dev)
if (lp->mode == 1) { /* only needed for Busmaster */
int xmit_stop, recv_stop;
- if ((lp->chip == HP100_CHIPID_RAINIER)
- || (lp->chip == HP100_CHIPID_SHASTA)) {
+ if ((lp->chip == HP100_CHIPID_RAINIER) ||
+ (lp->chip == HP100_CHIPID_SHASTA)) {
int pdl_stop;
/*
diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c
index 3fae8755979..fb5e019169e 100644
--- a/drivers/net/ibm_newemac/core.c
+++ b/drivers/net/ibm_newemac/core.c
@@ -1976,27 +1976,27 @@ static int emac_ethtool_set_settings(struct net_device *ndev,
if (cmd->autoneg == AUTONEG_DISABLE) {
switch (cmd->speed) {
case SPEED_10:
- if (cmd->duplex == DUPLEX_HALF
- && !(f & SUPPORTED_10baseT_Half))
+ if (cmd->duplex == DUPLEX_HALF &&
+ !(f & SUPPORTED_10baseT_Half))
return -EINVAL;
- if (cmd->duplex == DUPLEX_FULL
- && !(f & SUPPORTED_10baseT_Full))
+ if (cmd->duplex == DUPLEX_FULL &&
+ !(f & SUPPORTED_10baseT_Full))
return -EINVAL;
break;
case SPEED_100:
- if (cmd->duplex == DUPLEX_HALF
- && !(f & SUPPORTED_100baseT_Half))
+ if (cmd->duplex == DUPLEX_HALF &&
+ !(f & SUPPORTED_100baseT_Half))
return -EINVAL;
- if (cmd->duplex == DUPLEX_FULL
- && !(f & SUPPORTED_100baseT_Full))
+ if (cmd->duplex == DUPLEX_FULL &&
+ !(f & SUPPORTED_100baseT_Full))
return -EINVAL;
break;
case SPEED_1000:
- if (cmd->duplex == DUPLEX_HALF
- && !(f & SUPPORTED_1000baseT_Half))
+ if (cmd->duplex == DUPLEX_HALF &&
+ !(f & SUPPORTED_1000baseT_Half))
return -EINVAL;
- if (cmd->duplex == DUPLEX_FULL
- && !(f & SUPPORTED_1000baseT_Full))
+ if (cmd->duplex == DUPLEX_FULL &&
+ !(f & SUPPORTED_1000baseT_Full))
return -EINVAL;
break;
default:
@@ -2149,9 +2149,12 @@ static int emac_ethtool_nway_reset(struct net_device *ndev)
return res;
}
-static int emac_ethtool_get_stats_count(struct net_device *ndev)
+static int emac_ethtool_get_sset_count(struct net_device *ndev, int stringset)
{
- return EMAC_ETHTOOL_STATS_COUNT;
+ if (stringset == ETH_SS_STATS)
+ return EMAC_ETHTOOL_STATS_COUNT;
+ else
+ return -EINVAL;
}
static void emac_ethtool_get_strings(struct net_device *ndev, u32 stringset,
@@ -2182,7 +2185,6 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev,
info->fw_version[0] = '\0';
sprintf(info->bus_info, "PPC 4xx EMAC-%d %s",
dev->cell_index, dev->ofdev->node->full_name);
- info->n_stats = emac_ethtool_get_stats_count(ndev);
info->regdump_len = emac_ethtool_get_regs_len(ndev);
}
@@ -2202,7 +2204,7 @@ static const struct ethtool_ops emac_ethtool_ops = {
.get_rx_csum = emac_ethtool_get_rx_csum,
.get_strings = emac_ethtool_get_strings,
- .get_stats_count = emac_ethtool_get_stats_count,
+ .get_sset_count = emac_ethtool_get_sset_count,
.get_ethtool_stats = emac_ethtool_get_ethtool_stats,
.get_link = ethtool_op_get_link,
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index 5862282ab2f..a86693906ac 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -625,7 +625,7 @@ static int ibmveth_open(struct net_device *netdev)
}
ibmveth_debug_printk("registering irq 0x%x\n", netdev->irq);
- if((rc = request_irq(netdev->irq, &ibmveth_interrupt, 0, netdev->name, netdev)) != 0) {
+ if((rc = request_irq(netdev->irq, ibmveth_interrupt, 0, netdev->name, netdev)) != 0) {
ibmveth_error_printk("unable to request irq 0x%x, rc %d\n", netdev->irq, rc);
do {
rc = h_free_logical_lan(adapter->vdev->unit_address);
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 030913f8bd2..f4081c0a2d9 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -98,14 +98,16 @@ static void ri_tasklet(unsigned long dev)
stats->tx_packets++;
stats->tx_bytes +=skb->len;
- skb->dev = dev_get_by_index(&init_net, skb->iif);
+ rcu_read_lock();
+ skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif);
if (!skb->dev) {
+ rcu_read_unlock();
dev_kfree_skb(skb);
stats->tx_dropped++;
break;
}
- dev_put(skb->dev);
- skb->iif = _dev->ifindex;
+ rcu_read_unlock();
+ skb->skb_iif = _dev->ifindex;
if (from & AT_EGRESS) {
dp->st_rx_frm_egr++;
@@ -170,7 +172,7 @@ static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
stats->rx_packets++;
stats->rx_bytes+=skb->len;
- if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) {
+ if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) {
dev_kfree_skb(skb);
stats->rx_dropped++;
return NETDEV_TX_OK;
diff --git a/drivers/net/igb/e1000_82575.c b/drivers/net/igb/e1000_82575.c
index f8f5772557c..e8e9e9194a8 100644
--- a/drivers/net/igb/e1000_82575.c
+++ b/drivers/net/igb/e1000_82575.c
@@ -46,7 +46,10 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *);
static s32 igb_init_hw_82575(struct e1000_hw *);
static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *);
static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *, u32, u16 *);
+static s32 igb_read_phy_reg_82580(struct e1000_hw *, u32, u16 *);
+static s32 igb_write_phy_reg_82580(struct e1000_hw *, u32, u16);
static s32 igb_reset_hw_82575(struct e1000_hw *);
+static s32 igb_reset_hw_82580(struct e1000_hw *);
static s32 igb_set_d0_lplu_state_82575(struct e1000_hw *, bool);
static s32 igb_setup_copper_link_82575(struct e1000_hw *);
static s32 igb_setup_serdes_link_82575(struct e1000_hw *);
@@ -62,6 +65,12 @@ static s32 igb_reset_init_script_82575(struct e1000_hw *);
static s32 igb_read_mac_addr_82575(struct e1000_hw *);
static s32 igb_set_pcie_completion_timeout(struct e1000_hw *hw);
+static const u16 e1000_82580_rxpbs_table[] =
+ { 36, 72, 144, 1, 2, 4, 8, 16,
+ 35, 70, 140 };
+#define E1000_82580_RXPBS_TABLE_SIZE \
+ (sizeof(e1000_82580_rxpbs_table)/sizeof(u16))
+
static s32 igb_get_invariants_82575(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
@@ -81,12 +90,20 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
break;
case E1000_DEV_ID_82576:
case E1000_DEV_ID_82576_NS:
+ case E1000_DEV_ID_82576_NS_SERDES:
case E1000_DEV_ID_82576_FIBER:
case E1000_DEV_ID_82576_SERDES:
case E1000_DEV_ID_82576_QUAD_COPPER:
case E1000_DEV_ID_82576_SERDES_QUAD:
mac->type = e1000_82576;
break;
+ case E1000_DEV_ID_82580_COPPER:
+ case E1000_DEV_ID_82580_FIBER:
+ case E1000_DEV_ID_82580_SERDES:
+ case E1000_DEV_ID_82580_SGMII:
+ case E1000_DEV_ID_82580_COPPER_DUAL:
+ mac->type = e1000_82580;
+ break;
default:
return -E1000_ERR_MAC_INIT;
break;
@@ -109,6 +126,7 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
dev_spec->sgmii_active = true;
ctrl_ext |= E1000_CTRL_I2C_ENA;
break;
+ case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
hw->phy.media_type = e1000_media_type_internal_serdes;
ctrl_ext |= E1000_CTRL_I2C_ENA;
@@ -120,12 +138,26 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
wr32(E1000_CTRL_EXT, ctrl_ext);
+ /*
+ * if using i2c make certain the MDICNFG register is cleared to prevent
+ * communications from being misrouted to the mdic registers
+ */
+ if ((ctrl_ext & E1000_CTRL_I2C_ENA) && (hw->mac.type == e1000_82580))
+ wr32(E1000_MDICNFG, 0);
+
/* Set mta register count */
mac->mta_reg_count = 128;
/* Set rar entry count */
mac->rar_entry_count = E1000_RAR_ENTRIES_82575;
if (mac->type == e1000_82576)
mac->rar_entry_count = E1000_RAR_ENTRIES_82576;
+ if (mac->type == e1000_82580)
+ mac->rar_entry_count = E1000_RAR_ENTRIES_82580;
+ /* reset */
+ if (mac->type == e1000_82580)
+ mac->ops.reset_hw = igb_reset_hw_82580;
+ else
+ mac->ops.reset_hw = igb_reset_hw_82575;
/* Set if part includes ASF firmware */
mac->asf_firmware_present = true;
/* Set if manageability features are enabled. */
@@ -193,6 +225,10 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
phy->ops.reset = igb_phy_hw_reset_sgmii_82575;
phy->ops.read_reg = igb_read_phy_reg_sgmii_82575;
phy->ops.write_reg = igb_write_phy_reg_sgmii_82575;
+ } else if (hw->mac.type == e1000_82580) {
+ phy->ops.reset = igb_phy_hw_reset;
+ phy->ops.read_reg = igb_read_phy_reg_82580;
+ phy->ops.write_reg = igb_write_phy_reg_82580;
} else {
phy->ops.reset = igb_phy_hw_reset;
phy->ops.read_reg = igb_read_phy_reg_igp;
@@ -224,6 +260,12 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
phy->ops.set_d0_lplu_state = igb_set_d0_lplu_state_82575;
phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state;
break;
+ case I82580_I_PHY_ID:
+ phy->type = e1000_phy_82580;
+ phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_82580;
+ phy->ops.get_cable_length = igb_get_cable_length_82580;
+ phy->ops.get_phy_info = igb_get_phy_info_82580;
+ break;
default:
return -E1000_ERR_PHY;
}
@@ -240,9 +282,10 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
**/
static s32 igb_acquire_phy_82575(struct e1000_hw *hw)
{
- u16 mask;
+ u16 mask = E1000_SWFW_PHY0_SM;
- mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM;
+ if (hw->bus.func == E1000_FUNC_1)
+ mask = E1000_SWFW_PHY1_SM;
return igb_acquire_swfw_sync_82575(hw, mask);
}
@@ -256,9 +299,11 @@ static s32 igb_acquire_phy_82575(struct e1000_hw *hw)
**/
static void igb_release_phy_82575(struct e1000_hw *hw)
{
- u16 mask;
+ u16 mask = E1000_SWFW_PHY0_SM;
+
+ if (hw->bus.func == E1000_FUNC_1)
+ mask = E1000_SWFW_PHY1_SM;
- mask = hw->bus.func ? E1000_SWFW_PHY1_SM : E1000_SWFW_PHY0_SM;
igb_release_swfw_sync_82575(hw, mask);
}
@@ -274,45 +319,23 @@ static void igb_release_phy_82575(struct e1000_hw *hw)
static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
u16 *data)
{
- struct e1000_phy_info *phy = &hw->phy;
- u32 i, i2ccmd = 0;
+ s32 ret_val = -E1000_ERR_PARAM;
if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) {
hw_dbg("PHY Address %u is out of range\n", offset);
- return -E1000_ERR_PARAM;
+ goto out;
}
- /*
- * Set up Op-code, Phy Address, and register address in the I2CCMD
- * register. The MAC will take care of interfacing with the
- * PHY to retrieve the desired data.
- */
- i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
- (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
- (E1000_I2CCMD_OPCODE_READ));
-
- wr32(E1000_I2CCMD, i2ccmd);
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
- /* Poll the ready bit to see if the I2C read completed */
- for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
- udelay(50);
- i2ccmd = rd32(E1000_I2CCMD);
- if (i2ccmd & E1000_I2CCMD_READY)
- break;
- }
- if (!(i2ccmd & E1000_I2CCMD_READY)) {
- hw_dbg("I2CCMD Read did not complete\n");
- return -E1000_ERR_PHY;
- }
- if (i2ccmd & E1000_I2CCMD_ERROR) {
- hw_dbg("I2CCMD Error bit set\n");
- return -E1000_ERR_PHY;
- }
+ ret_val = igb_read_phy_reg_i2c(hw, offset, data);
- /* Need to byte-swap the 16-bit value. */
- *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00);
+ hw->phy.ops.release(hw);
- return 0;
+out:
+ return ret_val;
}
/**
@@ -327,47 +350,24 @@ static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
static s32 igb_write_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset,
u16 data)
{
- struct e1000_phy_info *phy = &hw->phy;
- u32 i, i2ccmd = 0;
- u16 phy_data_swapped;
+ s32 ret_val = -E1000_ERR_PARAM;
+
if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) {
hw_dbg("PHY Address %d is out of range\n", offset);
- return -E1000_ERR_PARAM;
+ goto out;
}
- /* Swap the data bytes for the I2C interface */
- phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
- /*
- * Set up Op-code, Phy Address, and register address in the I2CCMD
- * register. The MAC will take care of interfacing with the
- * PHY to retrieve the desired data.
- */
- i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
- (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
- E1000_I2CCMD_OPCODE_WRITE |
- phy_data_swapped);
-
- wr32(E1000_I2CCMD, i2ccmd);
-
- /* Poll the ready bit to see if the I2C read completed */
- for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
- udelay(50);
- i2ccmd = rd32(E1000_I2CCMD);
- if (i2ccmd & E1000_I2CCMD_READY)
- break;
- }
- if (!(i2ccmd & E1000_I2CCMD_READY)) {
- hw_dbg("I2CCMD Write did not complete\n");
- return -E1000_ERR_PHY;
- }
- if (i2ccmd & E1000_I2CCMD_ERROR) {
- hw_dbg("I2CCMD Error bit set\n");
- return -E1000_ERR_PHY;
- }
+ ret_val = igb_write_phy_reg_i2c(hw, offset, data);
- return 0;
+ hw->phy.ops.release(hw);
+
+out:
+ return ret_val;
}
/**
@@ -676,6 +676,10 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *hw)
if (hw->bus.func == 1)
mask = E1000_NVM_CFG_DONE_PORT_1;
+ else if (hw->bus.func == E1000_FUNC_2)
+ mask = E1000_NVM_CFG_DONE_PORT_2;
+ else if (hw->bus.func == E1000_FUNC_3)
+ mask = E1000_NVM_CFG_DONE_PORT_3;
while (timeout) {
if (rd32(E1000_EEMNGCTL) & mask)
@@ -706,9 +710,7 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
s32 ret_val;
u16 speed, duplex;
- /* SGMII link check is done through the PCS register. */
- if ((hw->phy.media_type != e1000_media_type_copper) ||
- (igb_sgmii_active_82575(hw))) {
+ if (hw->phy.media_type != e1000_media_type_copper) {
ret_val = igb_get_pcs_speed_and_duplex_82575(hw, &speed,
&duplex);
/*
@@ -723,6 +725,7 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw)
return ret_val;
}
+
/**
* igb_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex
* @hw: pointer to the HW structure
@@ -788,13 +791,27 @@ static s32 igb_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, u16 *speed,
void igb_shutdown_serdes_link_82575(struct e1000_hw *hw)
{
u32 reg;
+ u16 eeprom_data = 0;
if (hw->phy.media_type != e1000_media_type_internal_serdes ||
igb_sgmii_active_82575(hw))
return;
- /* if the management interface is not enabled, then power down */
- if (!igb_enable_mng_pass_thru(hw)) {
+ if (hw->bus.func == E1000_FUNC_0)
+ hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+ else if (hw->mac.type == e1000_82580)
+ hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A +
+ NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1,
+ &eeprom_data);
+ else if (hw->bus.func == E1000_FUNC_1)
+ hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
+
+ /*
+ * If APM is not enabled in the EEPROM and management interface is
+ * not enabled, then power down.
+ */
+ if (!(eeprom_data & E1000_NVM_APME_82575) &&
+ !igb_enable_mng_pass_thru(hw)) {
/* Disable PCS to turn off link */
reg = rd32(E1000_PCS_CFG0);
reg &= ~E1000_PCS_CFG_PCS_EN;
@@ -908,6 +925,11 @@ static s32 igb_init_hw_82575(struct e1000_hw *hw)
for (i = 0; i < mac->mta_reg_count; i++)
array_wr32(E1000_MTA, i, 0);
+ /* Zero out the Unicast HASH table */
+ hw_dbg("Zeroing the UTA\n");
+ for (i = 0; i < mac->uta_reg_count; i++)
+ array_wr32(E1000_UTA, i, 0);
+
/* Setup link and flow control */
ret_val = igb_setup_link(hw);
@@ -934,7 +956,6 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
{
u32 ctrl;
s32 ret_val;
- bool link;
ctrl = rd32(E1000_CTRL);
ctrl |= E1000_CTRL_SLU;
@@ -946,6 +967,9 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
goto out;
if (igb_sgmii_active_82575(hw) && !hw->phy.reset_disable) {
+ /* allow time for SFP cage time to power up phy */
+ msleep(300);
+
ret_val = hw->phy.ops.reset(hw);
if (ret_val) {
hw_dbg("Error resetting the PHY.\n");
@@ -959,6 +983,9 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
case e1000_phy_igp_3:
ret_val = igb_copper_link_setup_igp(hw);
break;
+ case e1000_phy_82580:
+ ret_val = igb_copper_link_setup_82580(hw);
+ break;
default:
ret_val = -E1000_ERR_PHY;
break;
@@ -967,57 +994,24 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
if (ret_val)
goto out;
- if (hw->mac.autoneg) {
- /*
- * Setup autoneg and flow control advertisement
- * and perform autonegotiation.
- */
- ret_val = igb_copper_link_autoneg(hw);
- if (ret_val)
- goto out;
- } else {
- /*
- * PHY will be set to 10H, 10F, 100H or 100F
- * depending on user settings.
- */
- hw_dbg("Forcing Speed and Duplex\n");
- ret_val = hw->phy.ops.force_speed_duplex(hw);
- if (ret_val) {
- hw_dbg("Error Forcing Speed and Duplex\n");
- goto out;
- }
- }
-
- /*
- * Check link status. Wait up to 100 microseconds for link to become
- * valid.
- */
- ret_val = igb_phy_has_link(hw, COPPER_LINK_UP_LIMIT, 10, &link);
- if (ret_val)
- goto out;
-
- if (link) {
- hw_dbg("Valid link established!!!\n");
- /* Config the MAC and PHY after link is up */
- igb_config_collision_dist(hw);
- ret_val = igb_config_fc_after_link_up(hw);
- } else {
- hw_dbg("Unable to establish link!!!\n");
- }
-
+ ret_val = igb_setup_copper_link(hw);
out:
return ret_val;
}
/**
- * igb_setup_serdes_link_82575 - Setup link for fiber/serdes
+ * igb_setup_serdes_link_82575 - Setup link for serdes
* @hw: pointer to the HW structure
*
- * Configures speed and duplex for fiber and serdes links.
+ * Configure the physical coding sub-layer (PCS) link. The PCS link is
+ * used on copper connections where the serialized gigabit media independent
+ * interface (sgmii), or serdes fiber is being used. Configures the link
+ * for auto-negotiation or forces speed/duplex.
**/
static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
{
- u32 ctrl_reg, reg;
+ u32 ctrl_ext, ctrl_reg, reg;
+ bool pcs_autoneg;
if ((hw->phy.media_type != e1000_media_type_internal_serdes) &&
!igb_sgmii_active_82575(hw))
@@ -1032,9 +1026,9 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
wr32(E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK);
/* power on the sfp cage if present */
- reg = rd32(E1000_CTRL_EXT);
- reg &= ~E1000_CTRL_EXT_SDP3_DATA;
- wr32(E1000_CTRL_EXT, reg);
+ ctrl_ext = rd32(E1000_CTRL_EXT);
+ ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA;
+ wr32(E1000_CTRL_EXT, ctrl_ext);
ctrl_reg = rd32(E1000_CTRL);
ctrl_reg |= E1000_CTRL_SLU;
@@ -1051,15 +1045,31 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
reg = rd32(E1000_PCS_LCTL);
- if (igb_sgmii_active_82575(hw)) {
- /* allow time for SFP cage to power up phy */
- msleep(300);
+ /* default pcs_autoneg to the same setting as mac autoneg */
+ pcs_autoneg = hw->mac.autoneg;
- /* AN time out should be disabled for SGMII mode */
+ switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {
+ case E1000_CTRL_EXT_LINK_MODE_SGMII:
+ /* sgmii mode lets the phy handle forcing speed/duplex */
+ pcs_autoneg = true;
+ /* autoneg time out should be disabled for SGMII mode */
reg &= ~(E1000_PCS_LCTL_AN_TIMEOUT);
- } else {
+ break;
+ case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+ /* disable PCS autoneg and support parallel detect only */
+ pcs_autoneg = false;
+ default:
+ /*
+ * non-SGMII modes only supports a speed of 1000/Full for the
+ * link so it is best to just force the MAC and let the pcs
+ * link either autoneg or be forced to 1000/Full
+ */
ctrl_reg |= E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD |
E1000_CTRL_FD | E1000_CTRL_FRCDPX;
+
+ /* set speed of 1000/Full if speed/duplex is forced */
+ reg |= E1000_PCS_LCTL_FSV_1000 | E1000_PCS_LCTL_FDV_FULL;
+ break;
}
wr32(E1000_CTRL, ctrl_reg);
@@ -1070,7 +1080,6 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
* mode that will be compatible with older link partners and switches.
* However, both are supported by the hardware and some drivers/tools.
*/
-
reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP |
E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK);
@@ -1080,25 +1089,18 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw)
*/
reg |= E1000_PCS_LCTL_FORCE_FCTRL;
- /*
- * we always set sgmii to autoneg since it is the phy that will be
- * forcing the link and the serdes is just a go-between
- */
- if (hw->mac.autoneg || igb_sgmii_active_82575(hw)) {
+ if (pcs_autoneg) {
/* Set PCS register for autoneg */
- reg |= E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */
- E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */
- E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */
- E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */
- hw_dbg("Configuring Autoneg; PCS_LCTL = 0x%08X\n", reg);
+ reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */
+ E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */
+ hw_dbg("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg);
} else {
- /* Set PCS register for forced speed */
- reg |= E1000_PCS_LCTL_FLV_LINK_UP | /* Force link up */
- E1000_PCS_LCTL_FSV_1000 | /* Force 1000 */
- E1000_PCS_LCTL_FDV_FULL | /* SerDes Full duplex */
- E1000_PCS_LCTL_FSD | /* Force Speed */
- E1000_PCS_LCTL_FORCE_LINK; /* Force Link */
- hw_dbg("Configuring Forced Link; PCS_LCTL = 0x%08X\n", reg);
+ /* Set PCS register for forced link */
+ reg |= E1000_PCS_LCTL_FSD | /* Force Speed */
+ E1000_PCS_LCTL_FORCE_LINK | /* Force Link */
+ E1000_PCS_LCTL_FLV_LINK_UP; /* Force link value up */
+
+ hw_dbg("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg);
}
wr32(E1000_PCS_LCTL, reg);
@@ -1167,9 +1169,18 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw)
{
s32 ret_val = 0;
- if (igb_check_alt_mac_addr(hw))
- ret_val = igb_read_mac_addr(hw);
+ /*
+ * If there's an alternate MAC address place it in RAR0
+ * so that it will override the Si installed default perm
+ * address.
+ */
+ ret_val = igb_check_alt_mac_addr(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = igb_read_mac_addr(hw);
+out:
return ret_val;
}
@@ -1181,61 +1192,59 @@ static s32 igb_read_mac_addr_82575(struct e1000_hw *hw)
**/
static void igb_clear_hw_cntrs_82575(struct e1000_hw *hw)
{
- u32 temp;
-
igb_clear_hw_cntrs_base(hw);
- temp = rd32(E1000_PRC64);
- temp = rd32(E1000_PRC127);
- temp = rd32(E1000_PRC255);
- temp = rd32(E1000_PRC511);
- temp = rd32(E1000_PRC1023);
- temp = rd32(E1000_PRC1522);
- temp = rd32(E1000_PTC64);
- temp = rd32(E1000_PTC127);
- temp = rd32(E1000_PTC255);
- temp = rd32(E1000_PTC511);
- temp = rd32(E1000_PTC1023);
- temp = rd32(E1000_PTC1522);
-
- temp = rd32(E1000_ALGNERRC);
- temp = rd32(E1000_RXERRC);
- temp = rd32(E1000_TNCRS);
- temp = rd32(E1000_CEXTERR);
- temp = rd32(E1000_TSCTC);
- temp = rd32(E1000_TSCTFC);
-
- temp = rd32(E1000_MGTPRC);
- temp = rd32(E1000_MGTPDC);
- temp = rd32(E1000_MGTPTC);
-
- temp = rd32(E1000_IAC);
- temp = rd32(E1000_ICRXOC);
-
- temp = rd32(E1000_ICRXPTC);
- temp = rd32(E1000_ICRXATC);
- temp = rd32(E1000_ICTXPTC);
- temp = rd32(E1000_ICTXATC);
- temp = rd32(E1000_ICTXQEC);
- temp = rd32(E1000_ICTXQMTC);
- temp = rd32(E1000_ICRXDMTC);
-
- temp = rd32(E1000_CBTMPC);
- temp = rd32(E1000_HTDPMC);
- temp = rd32(E1000_CBRMPC);
- temp = rd32(E1000_RPTHC);
- temp = rd32(E1000_HGPTC);
- temp = rd32(E1000_HTCBDPC);
- temp = rd32(E1000_HGORCL);
- temp = rd32(E1000_HGORCH);
- temp = rd32(E1000_HGOTCL);
- temp = rd32(E1000_HGOTCH);
- temp = rd32(E1000_LENERRS);
+ rd32(E1000_PRC64);
+ rd32(E1000_PRC127);
+ rd32(E1000_PRC255);
+ rd32(E1000_PRC511);
+ rd32(E1000_PRC1023);
+ rd32(E1000_PRC1522);
+ rd32(E1000_PTC64);
+ rd32(E1000_PTC127);
+ rd32(E1000_PTC255);
+ rd32(E1000_PTC511);
+ rd32(E1000_PTC1023);
+ rd32(E1000_PTC1522);
+
+ rd32(E1000_ALGNERRC);
+ rd32(E1000_RXERRC);
+ rd32(E1000_TNCRS);
+ rd32(E1000_CEXTERR);
+ rd32(E1000_TSCTC);
+ rd32(E1000_TSCTFC);
+
+ rd32(E1000_MGTPRC);
+ rd32(E1000_MGTPDC);
+ rd32(E1000_MGTPTC);
+
+ rd32(E1000_IAC);
+ rd32(E1000_ICRXOC);
+
+ rd32(E1000_ICRXPTC);
+ rd32(E1000_ICRXATC);
+ rd32(E1000_ICTXPTC);
+ rd32(E1000_ICTXATC);
+ rd32(E1000_ICTXQEC);
+ rd32(E1000_ICTXQMTC);
+ rd32(E1000_ICRXDMTC);
+
+ rd32(E1000_CBTMPC);
+ rd32(E1000_HTDPMC);
+ rd32(E1000_CBRMPC);
+ rd32(E1000_RPTHC);
+ rd32(E1000_HGPTC);
+ rd32(E1000_HTCBDPC);
+ rd32(E1000_HGORCL);
+ rd32(E1000_HGORCH);
+ rd32(E1000_HGOTCL);
+ rd32(E1000_HGOTCH);
+ rd32(E1000_LENERRS);
/* This register should not be read in copper configurations */
if (hw->phy.media_type == e1000_media_type_internal_serdes ||
igb_sgmii_active_82575(hw))
- temp = rd32(E1000_SCVPC);
+ rd32(E1000_SCVPC);
}
/**
@@ -1400,8 +1409,183 @@ void igb_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable)
wr32(E1000_VT_CTL, vt_ctl);
}
+/**
+ * igb_read_phy_reg_82580 - Read 82580 MDI control register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the MDI control register in the PHY at offset and stores the
+ * information read to data.
+ **/
+static s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ u32 mdicnfg = 0;
+ s32 ret_val;
+
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+
+ /*
+ * We config the phy address in MDICNFG register now. Same bits
+ * as before. The values in MDIC can be written but will be
+ * ignored. This allows us to call the old function after
+ * configuring the PHY address in the new register
+ */
+ mdicnfg = (hw->phy.addr << E1000_MDIC_PHY_SHIFT);
+ wr32(E1000_MDICNFG, mdicnfg);
+
+ ret_val = igb_read_phy_reg_mdic(hw, offset, data);
+
+ hw->phy.ops.release(hw);
+
+out:
+ return ret_val;
+}
+
+/**
+ * igb_write_phy_reg_82580 - Write 82580 MDI control register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write to register at offset
+ *
+ * Writes data to MDI control register in the PHY at offset.
+ **/
+static s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ u32 mdicnfg = 0;
+ s32 ret_val;
+
+
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+
+ /*
+ * We config the phy address in MDICNFG register now. Same bits
+ * as before. The values in MDIC can be written but will be
+ * ignored. This allows us to call the old function after
+ * configuring the PHY address in the new register
+ */
+ mdicnfg = (hw->phy.addr << E1000_MDIC_PHY_SHIFT);
+ wr32(E1000_MDICNFG, mdicnfg);
+
+ ret_val = igb_write_phy_reg_mdic(hw, offset, data);
+
+ hw->phy.ops.release(hw);
+
+out:
+ return ret_val;
+}
+
+/**
+ * igb_reset_hw_82580 - Reset hardware
+ * @hw: pointer to the HW structure
+ *
+ * This resets function or entire device (all ports, etc.)
+ * to a known state.
+ **/
+static s32 igb_reset_hw_82580(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+ /* BH SW mailbox bit in SW_FW_SYNC */
+ u16 swmbsw_mask = E1000_SW_SYNCH_MB;
+ u32 ctrl, icr;
+ bool global_device_reset = hw->dev_spec._82575.global_device_reset;
+
+
+ hw->dev_spec._82575.global_device_reset = false;
+
+ /* Get current control state. */
+ ctrl = rd32(E1000_CTRL);
+
+ /*
+ * Prevent the PCI-E bus from sticking if there is no TLP connection
+ * on the last TLP read/write transaction when MAC is reset.
+ */
+ ret_val = igb_disable_pcie_master(hw);
+ if (ret_val)
+ hw_dbg("PCI-E Master disable polling has failed.\n");
+
+ hw_dbg("Masking off all interrupts\n");
+ wr32(E1000_IMC, 0xffffffff);
+ wr32(E1000_RCTL, 0);
+ wr32(E1000_TCTL, E1000_TCTL_PSP);
+ wrfl();
+
+ msleep(10);
+
+ /* Determine whether or not a global dev reset is requested */
+ if (global_device_reset &&
+ igb_acquire_swfw_sync_82575(hw, swmbsw_mask))
+ global_device_reset = false;
+
+ if (global_device_reset &&
+ !(rd32(E1000_STATUS) & E1000_STAT_DEV_RST_SET))
+ ctrl |= E1000_CTRL_DEV_RST;
+ else
+ ctrl |= E1000_CTRL_RST;
+
+ wr32(E1000_CTRL, ctrl);
+
+ /* Add delay to insure DEV_RST has time to complete */
+ if (global_device_reset)
+ msleep(5);
+
+ ret_val = igb_get_auto_rd_done(hw);
+ if (ret_val) {
+ /*
+ * When auto config read does not complete, do not
+ * return with an error. This can happen in situations
+ * where there is no eeprom and prevents getting link.
+ */
+ hw_dbg("Auto Read Done did not complete\n");
+ }
+
+ /* If EEPROM is not present, run manual init scripts */
+ if ((rd32(E1000_EECD) & E1000_EECD_PRES) == 0)
+ igb_reset_init_script_82575(hw);
+
+ /* clear global device reset status bit */
+ wr32(E1000_STATUS, E1000_STAT_DEV_RST_SET);
+
+ /* Clear any pending interrupt events. */
+ wr32(E1000_IMC, 0xffffffff);
+ icr = rd32(E1000_ICR);
+
+ /* Install any alternate MAC address into RAR0 */
+ ret_val = igb_check_alt_mac_addr(hw);
+
+ /* Release semaphore */
+ if (global_device_reset)
+ igb_release_swfw_sync_82575(hw, swmbsw_mask);
+
+ return ret_val;
+}
+
+/**
+ * igb_rxpbs_adjust_82580 - adjust RXPBS value to reflect actual RX PBA size
+ * @data: data received by reading RXPBS register
+ *
+ * The 82580 uses a table based approach for packet buffer allocation sizes.
+ * This function converts the retrieved value into the correct table value
+ * 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7
+ * 0x0 36 72 144 1 2 4 8 16
+ * 0x8 35 70 140 rsv rsv rsv rsv rsv
+ */
+u16 igb_rxpbs_adjust_82580(u32 data)
+{
+ u16 ret_val = 0;
+
+ if (data < E1000_82580_RXPBS_TABLE_SIZE)
+ ret_val = e1000_82580_rxpbs_table[data];
+
+ return ret_val;
+}
+
static struct e1000_mac_operations e1000_mac_ops_82575 = {
- .reset_hw = igb_reset_hw_82575,
.init_hw = igb_init_hw_82575,
.check_for_link = igb_check_for_link_82575,
.rar_set = igb_rar_set,
diff --git a/drivers/net/igb/e1000_82575.h b/drivers/net/igb/e1000_82575.h
index ebd146fd4e1..d51c9927c81 100644
--- a/drivers/net/igb/e1000_82575.h
+++ b/drivers/net/igb/e1000_82575.h
@@ -38,6 +38,11 @@ extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw);
#define E1000_RAR_ENTRIES_82575 16
#define E1000_RAR_ENTRIES_82576 24
+#define E1000_RAR_ENTRIES_82580 24
+
+#define E1000_SW_SYNCH_MB 0x00000100
+#define E1000_STAT_DEV_RST_SET 0x00100000
+#define E1000_CTRL_DEV_RST 0x20000000
/* SRRCTL bit definitions */
#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */
@@ -66,6 +71,8 @@ extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw);
E1000_EICR_RX_QUEUE3)
/* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */
+#define E1000_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */
+#define E1000_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of ctrl bits */
/* Receive Descriptor - Advanced */
union e1000_adv_rx_desc {
@@ -98,6 +105,7 @@ union e1000_adv_rx_desc {
#define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0
#define E1000_RXDADV_HDRBUFLEN_SHIFT 5
+#define E1000_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */
/* Transmit Descriptor - Advanced */
union e1000_adv_tx_desc {
@@ -167,6 +175,18 @@ struct e1000_adv_tx_context_desc {
#define E1000_DCA_TXCTRL_CPUID_SHIFT 24 /* Tx CPUID now in the last byte */
#define E1000_DCA_RXCTRL_CPUID_SHIFT 24 /* Rx CPUID now in the last byte */
+/* ETQF register bit definitions */
+#define E1000_ETQF_FILTER_ENABLE (1 << 26)
+#define E1000_ETQF_1588 (1 << 30)
+
+/* FTQF register bit definitions */
+#define E1000_FTQF_VF_BP 0x00008000
+#define E1000_FTQF_1588_TIME_STAMP 0x08000000
+#define E1000_FTQF_MASK 0xF0000000
+#define E1000_FTQF_MASK_PROTO_BP 0x10000000
+#define E1000_FTQF_MASK_SOURCE_PORT_BP 0x80000000
+
+#define E1000_NVM_APME_82575 0x0400
#define MAX_NUM_VFS 8
#define E1000_DTXSWC_VMDQ_LOOPBACK_EN (1 << 31) /* global VF LB enable */
@@ -202,9 +222,21 @@ struct e1000_adv_tx_context_desc {
#define E1000_IOVCTL 0x05BBC
#define E1000_IOVCTL_REUSE_VFQ 0x00000001
+#define E1000_RPLOLR_STRVLAN 0x40000000
+#define E1000_RPLOLR_STRCRC 0x80000000
+
+#define E1000_DTXCTL_8023LL 0x0004
+#define E1000_DTXCTL_VLAN_ADDED 0x0008
+#define E1000_DTXCTL_OOS_ENABLE 0x0010
+#define E1000_DTXCTL_MDP_EN 0x0020
+#define E1000_DTXCTL_SPOOF_INT 0x0040
+
#define ALL_QUEUES 0xFFFF
+/* RX packet buffer size defines */
+#define E1000_RXPBS_SIZE_MASK_82576 0x0000007F
void igb_vmdq_set_loopback_pf(struct e1000_hw *, bool);
void igb_vmdq_set_replication_pf(struct e1000_hw *, bool);
+u16 igb_rxpbs_adjust_82580(u32 data);
#endif
diff --git a/drivers/net/igb/e1000_defines.h b/drivers/net/igb/e1000_defines.h
index cb916833f30..6e036ae3138 100644
--- a/drivers/net/igb/e1000_defines.h
+++ b/drivers/net/igb/e1000_defines.h
@@ -49,6 +49,7 @@
#define E1000_CTRL_EXT_PFRSTD 0x00004000
#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000
#define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX 0x00400000
#define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000
#define E1000_CTRL_EXT_EIAME 0x01000000
#define E1000_CTRL_EXT_IRCA 0x00000001
@@ -329,6 +330,7 @@
#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */
#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */
#define E1000_ICR_VMMB 0x00000100 /* VM MB event */
+#define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */
/* If this bit asserted, the driver should claim the interrupt */
#define E1000_ICR_INT_ASSERTED 0x80000000
/* LAN connected device generates an interrupt */
@@ -370,6 +372,7 @@
#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_IMS_DRSTA E1000_ICR_DRSTA /* Device Reset Asserted */
#define E1000_IMS_DOUTSYNC E1000_ICR_DOUTSYNC /* NIC DMA out of sync */
/* Extended Interrupt Mask Set */
@@ -378,6 +381,7 @@
/* Interrupt Cause Set */
#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */
#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_ICS_DRSTA E1000_ICR_DRSTA /* Device Reset Aserted */
/* Extended Interrupt Cause Set */
@@ -435,6 +439,39 @@
/* Flow Control */
#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */
+#define E1000_TSYNCTXCTL_VALID 0x00000001 /* tx timestamp valid */
+#define E1000_TSYNCTXCTL_ENABLED 0x00000010 /* enable tx timestampping */
+
+#define E1000_TSYNCRXCTL_VALID 0x00000001 /* rx timestamp valid */
+#define E1000_TSYNCRXCTL_TYPE_MASK 0x0000000E /* rx type mask */
+#define E1000_TSYNCRXCTL_TYPE_L2_V2 0x00
+#define E1000_TSYNCRXCTL_TYPE_L4_V1 0x02
+#define E1000_TSYNCRXCTL_TYPE_L2_L4_V2 0x04
+#define E1000_TSYNCRXCTL_TYPE_ALL 0x08
+#define E1000_TSYNCRXCTL_TYPE_EVENT_V2 0x0A
+#define E1000_TSYNCRXCTL_ENABLED 0x00000010 /* enable rx timestampping */
+
+#define E1000_TSYNCRXCFG_PTP_V1_CTRLT_MASK 0x000000FF
+#define E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE 0x00
+#define E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE 0x01
+#define E1000_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE 0x02
+#define E1000_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE 0x03
+#define E1000_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE 0x04
+
+#define E1000_TSYNCRXCFG_PTP_V2_MSGID_MASK 0x00000F00
+#define E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE 0x0000
+#define E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE 0x0100
+#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE 0x0200
+#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE 0x0300
+#define E1000_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE 0x0800
+#define E1000_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE 0x0900
+#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE 0x0A00
+#define E1000_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE 0x0B00
+#define E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE 0x0C00
+#define E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE 0x0D00
+
+#define E1000_TIMINCA_16NS_SHIFT 24
+
/* PCI Express Control */
#define E1000_GCR_CMPL_TMOUT_MASK 0x0000F000
#define E1000_GCR_CMPL_TMOUT_10ms 0x00001000
@@ -524,8 +561,12 @@
#define NVM_ALT_MAC_ADDR_PTR 0x0037
#define NVM_CHECKSUM_REG 0x003F
-#define E1000_NVM_CFG_DONE_PORT_0 0x40000 /* MNG config cycle done */
-#define E1000_NVM_CFG_DONE_PORT_1 0x80000 /* ...for second port */
+#define E1000_NVM_CFG_DONE_PORT_0 0x040000 /* MNG config cycle done */
+#define E1000_NVM_CFG_DONE_PORT_1 0x080000 /* ...for second port */
+#define E1000_NVM_CFG_DONE_PORT_2 0x100000 /* ...for third port */
+#define E1000_NVM_CFG_DONE_PORT_3 0x200000 /* ...for fourth port */
+
+#define NVM_82580_LAN_FUNC_OFFSET(a) (a ? (0x40 + (0x40 * a)) : 0)
/* Mask bits for fields in Word 0x0f of the NVM */
#define NVM_WORD0F_PAUSE_MASK 0x3000
@@ -592,6 +633,7 @@
*/
#define M88E1111_I_PHY_ID 0x01410CC0
#define IGP03E1000_E_PHY_ID 0x02A80390
+#define I82580_I_PHY_ID 0x015403A0
#define M88_VENDOR 0x0141
/* M88E1000 Specific Registers */
@@ -678,4 +720,8 @@
#define E1000_VFTA_ENTRY_MASK 0x7F
#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F
+/* DMA Coalescing register fields */
+#define E1000_PCIEMISC_LX_DECISION 0x00000080 /* Lx power decision based
+ on DMA coal */
+
#endif
diff --git a/drivers/net/igb/e1000_hw.h b/drivers/net/igb/e1000_hw.h
index 119869b1124..dbaeb5f5e0c 100644
--- a/drivers/net/igb/e1000_hw.h
+++ b/drivers/net/igb/e1000_hw.h
@@ -42,20 +42,35 @@ struct e1000_hw;
#define E1000_DEV_ID_82576_SERDES 0x10E7
#define E1000_DEV_ID_82576_QUAD_COPPER 0x10E8
#define E1000_DEV_ID_82576_NS 0x150A
+#define E1000_DEV_ID_82576_NS_SERDES 0x1518
#define E1000_DEV_ID_82576_SERDES_QUAD 0x150D
#define E1000_DEV_ID_82575EB_COPPER 0x10A7
#define E1000_DEV_ID_82575EB_FIBER_SERDES 0x10A9
#define E1000_DEV_ID_82575GB_QUAD_COPPER 0x10D6
+#define E1000_DEV_ID_82580_COPPER 0x150E
+#define E1000_DEV_ID_82580_FIBER 0x150F
+#define E1000_DEV_ID_82580_SERDES 0x1510
+#define E1000_DEV_ID_82580_SGMII 0x1511
+#define E1000_DEV_ID_82580_COPPER_DUAL 0x1516
#define E1000_REVISION_2 2
#define E1000_REVISION_4 4
+#define E1000_FUNC_0 0
#define E1000_FUNC_1 1
+#define E1000_FUNC_2 2
+#define E1000_FUNC_3 3
+
+#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN0 0
+#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN1 3
+#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN2 6
+#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN3 9
enum e1000_mac_type {
e1000_undefined = 0,
e1000_82575,
e1000_82576,
+ e1000_82580,
e1000_num_macs /* List is 1-based, so subtract 1 for true count. */
};
@@ -70,7 +85,6 @@ enum e1000_nvm_type {
e1000_nvm_unknown = 0,
e1000_nvm_none,
e1000_nvm_eeprom_spi,
- e1000_nvm_eeprom_microwire,
e1000_nvm_flash_hw,
e1000_nvm_flash_sw
};
@@ -79,8 +93,6 @@ enum e1000_nvm_override {
e1000_nvm_override_none = 0,
e1000_nvm_override_spi_small,
e1000_nvm_override_spi_large,
- e1000_nvm_override_microwire_small,
- e1000_nvm_override_microwire_large
};
enum e1000_phy_type {
@@ -92,6 +104,7 @@ enum e1000_phy_type {
e1000_phy_gg82563,
e1000_phy_igp_3,
e1000_phy_ife,
+ e1000_phy_82580,
};
enum e1000_bus_type {
@@ -288,6 +301,7 @@ struct e1000_mac_operations {
struct e1000_phy_operations {
s32 (*acquire)(struct e1000_hw *);
+ s32 (*check_polarity)(struct e1000_hw *);
s32 (*check_reset_block)(struct e1000_hw *);
s32 (*force_speed_duplex)(struct e1000_hw *);
s32 (*get_cfg_done)(struct e1000_hw *hw);
@@ -339,6 +353,7 @@ struct e1000_mac_info {
u16 ifs_ratio;
u16 ifs_step_size;
u16 mta_reg_count;
+ u16 uta_reg_count;
/* Maximum size of the MTA register table in all supported adapters */
#define MAX_MTA_REG 128
@@ -463,6 +478,7 @@ struct e1000_mbx_info {
struct e1000_dev_spec_82575 {
bool sgmii_active;
+ bool global_device_reset;
};
struct e1000_hw {
diff --git a/drivers/net/igb/e1000_mac.c b/drivers/net/igb/e1000_mac.c
index 7d76bb085e1..2ad358a240b 100644
--- a/drivers/net/igb/e1000_mac.c
+++ b/drivers/net/igb/e1000_mac.c
@@ -185,13 +185,12 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
}
if (nvm_alt_mac_addr_offset == 0xFFFF) {
- ret_val = -(E1000_NOT_IMPLEMENTED);
+ /* There is no Alternate MAC Address */
goto out;
}
if (hw->bus.func == E1000_FUNC_1)
- nvm_alt_mac_addr_offset += ETH_ALEN/sizeof(u16);
-
+ nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1;
for (i = 0; i < ETH_ALEN; i += 2) {
offset = nvm_alt_mac_addr_offset + (i >> 1);
ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
@@ -206,14 +205,16 @@ s32 igb_check_alt_mac_addr(struct e1000_hw *hw)
/* if multicast bit is set, the alternate address will not be used */
if (alt_mac_addr[0] & 0x01) {
- ret_val = -(E1000_NOT_IMPLEMENTED);
+ hw_dbg("Ignoring Alternate Mac Address with MC bit set\n");
goto out;
}
- for (i = 0; i < ETH_ALEN; i++)
- hw->mac.addr[i] = hw->mac.perm_addr[i] = alt_mac_addr[i];
-
- hw->mac.ops.rar_set(hw, hw->mac.perm_addr, 0);
+ /*
+ * We have a valid alternate MAC address, and we want to treat it the
+ * same as the normal permanent MAC address stored by the HW into the
+ * RAR. Do this by mapping this address into RAR0.
+ */
+ hw->mac.ops.rar_set(hw, alt_mac_addr, 0);
out:
return ret_val;
@@ -246,8 +247,15 @@ void igb_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
if (rar_low || rar_high)
rar_high |= E1000_RAH_AV;
+ /*
+ * Some bridges will combine consecutive 32-bit writes into
+ * a single burst write, which will malfunction on some parts.
+ * The flushes avoid this.
+ */
wr32(E1000_RAL(index), rar_low);
+ wrfl();
wr32(E1000_RAH(index), rar_high);
+ wrfl();
}
/**
@@ -399,45 +407,43 @@ void igb_update_mc_addr_list(struct e1000_hw *hw,
**/
void igb_clear_hw_cntrs_base(struct e1000_hw *hw)
{
- u32 temp;
-
- temp = rd32(E1000_CRCERRS);
- temp = rd32(E1000_SYMERRS);
- temp = rd32(E1000_MPC);
- temp = rd32(E1000_SCC);
- temp = rd32(E1000_ECOL);
- temp = rd32(E1000_MCC);
- temp = rd32(E1000_LATECOL);
- temp = rd32(E1000_COLC);
- temp = rd32(E1000_DC);
- temp = rd32(E1000_SEC);
- temp = rd32(E1000_RLEC);
- temp = rd32(E1000_XONRXC);
- temp = rd32(E1000_XONTXC);
- temp = rd32(E1000_XOFFRXC);
- temp = rd32(E1000_XOFFTXC);
- temp = rd32(E1000_FCRUC);
- temp = rd32(E1000_GPRC);
- temp = rd32(E1000_BPRC);
- temp = rd32(E1000_MPRC);
- temp = rd32(E1000_GPTC);
- temp = rd32(E1000_GORCL);
- temp = rd32(E1000_GORCH);
- temp = rd32(E1000_GOTCL);
- temp = rd32(E1000_GOTCH);
- temp = rd32(E1000_RNBC);
- temp = rd32(E1000_RUC);
- temp = rd32(E1000_RFC);
- temp = rd32(E1000_ROC);
- temp = rd32(E1000_RJC);
- temp = rd32(E1000_TORL);
- temp = rd32(E1000_TORH);
- temp = rd32(E1000_TOTL);
- temp = rd32(E1000_TOTH);
- temp = rd32(E1000_TPR);
- temp = rd32(E1000_TPT);
- temp = rd32(E1000_MPTC);
- temp = rd32(E1000_BPTC);
+ rd32(E1000_CRCERRS);
+ rd32(E1000_SYMERRS);
+ rd32(E1000_MPC);
+ rd32(E1000_SCC);
+ rd32(E1000_ECOL);
+ rd32(E1000_MCC);
+ rd32(E1000_LATECOL);
+ rd32(E1000_COLC);
+ rd32(E1000_DC);
+ rd32(E1000_SEC);
+ rd32(E1000_RLEC);
+ rd32(E1000_XONRXC);
+ rd32(E1000_XONTXC);
+ rd32(E1000_XOFFRXC);
+ rd32(E1000_XOFFTXC);
+ rd32(E1000_FCRUC);
+ rd32(E1000_GPRC);
+ rd32(E1000_BPRC);
+ rd32(E1000_MPRC);
+ rd32(E1000_GPTC);
+ rd32(E1000_GORCL);
+ rd32(E1000_GORCH);
+ rd32(E1000_GOTCL);
+ rd32(E1000_GOTCH);
+ rd32(E1000_RNBC);
+ rd32(E1000_RUC);
+ rd32(E1000_RFC);
+ rd32(E1000_ROC);
+ rd32(E1000_RJC);
+ rd32(E1000_TORL);
+ rd32(E1000_TORH);
+ rd32(E1000_TOTL);
+ rd32(E1000_TOTH);
+ rd32(E1000_TPR);
+ rd32(E1000_TPT);
+ rd32(E1000_MPTC);
+ rd32(E1000_BPTC);
}
/**
diff --git a/drivers/net/igb/e1000_mbx.c b/drivers/net/igb/e1000_mbx.c
index ed9058eca45..c474cdb7004 100644
--- a/drivers/net/igb/e1000_mbx.c
+++ b/drivers/net/igb/e1000_mbx.c
@@ -143,12 +143,16 @@ static s32 igb_poll_for_msg(struct e1000_hw *hw, u16 mbx_id)
if (!countdown || !mbx->ops.check_for_msg)
goto out;
- while (mbx->ops.check_for_msg(hw, mbx_id)) {
+ while (countdown && mbx->ops.check_for_msg(hw, mbx_id)) {
countdown--;
if (!countdown)
break;
udelay(mbx->usec_delay);
}
+
+ /* if we failed, all future posted messages fail until reset */
+ if (!countdown)
+ mbx->timeout = 0;
out:
return countdown ? 0 : -E1000_ERR_MBX;
}
@@ -168,12 +172,16 @@ static s32 igb_poll_for_ack(struct e1000_hw *hw, u16 mbx_id)
if (!countdown || !mbx->ops.check_for_ack)
goto out;
- while (mbx->ops.check_for_ack(hw, mbx_id)) {
+ while (countdown && mbx->ops.check_for_ack(hw, mbx_id)) {
countdown--;
if (!countdown)
break;
udelay(mbx->usec_delay);
}
+
+ /* if we failed, all future posted messages fail until reset */
+ if (!countdown)
+ mbx->timeout = 0;
out:
return countdown ? 0 : -E1000_ERR_MBX;
}
@@ -217,12 +225,13 @@ out:
static s32 igb_write_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id)
{
struct e1000_mbx_info *mbx = &hw->mbx;
- s32 ret_val = 0;
+ s32 ret_val = -E1000_ERR_MBX;
- if (!mbx->ops.write)
+ /* exit if either we can't write or there isn't a defined timeout */
+ if (!mbx->ops.write || !mbx->timeout)
goto out;
- /* send msg*/
+ /* send msg */
ret_val = mbx->ops.write(hw, msg, size, mbx_id);
/* if msg sent wait until we receive an ack */
@@ -305,6 +314,30 @@ static s32 igb_check_for_rst_pf(struct e1000_hw *hw, u16 vf_number)
}
/**
+ * igb_obtain_mbx_lock_pf - obtain mailbox lock
+ * @hw: pointer to the HW structure
+ * @vf_number: the VF index
+ *
+ * return SUCCESS if we obtained the mailbox lock
+ **/
+static s32 igb_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number)
+{
+ s32 ret_val = -E1000_ERR_MBX;
+ u32 p2v_mailbox;
+
+
+ /* Take ownership of the buffer */
+ wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU);
+
+ /* reserve mailbox for vf use */
+ p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number));
+ if (p2v_mailbox & E1000_P2VMAILBOX_PFU)
+ ret_val = 0;
+
+ return ret_val;
+}
+
+/**
* igb_write_mbx_pf - Places a message in the mailbox
* @hw: pointer to the HW structure
* @msg: The message buffer
@@ -316,27 +349,17 @@ static s32 igb_check_for_rst_pf(struct e1000_hw *hw, u16 vf_number)
static s32 igb_write_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
u16 vf_number)
{
- u32 p2v_mailbox;
- s32 ret_val = 0;
+ s32 ret_val;
u16 i;
- /* Take ownership of the buffer */
- wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU);
-
- /* Make sure we have ownership now... */
- p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number));
- if (!(p2v_mailbox & E1000_P2VMAILBOX_PFU)) {
- /* failed to grab ownership */
- ret_val = -E1000_ERR_MBX;
+ /* lock the mailbox to prevent pf/vf race condition */
+ ret_val = igb_obtain_mbx_lock_pf(hw, vf_number);
+ if (ret_val)
goto out_no_write;
- }
- /*
- * flush any ack or msg which may already be in the queue
- * as they are likely the result of an error
- */
- igb_check_for_ack_pf(hw, vf_number);
+ /* flush msg and acks as we are overwriting the message buffer */
igb_check_for_msg_pf(hw, vf_number);
+ igb_check_for_ack_pf(hw, vf_number);
/* copy the caller specified message to the mailbox memory buffer */
for (i = 0; i < size; i++)
@@ -367,20 +390,13 @@ out_no_write:
static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
u16 vf_number)
{
- u32 p2v_mailbox;
- s32 ret_val = 0;
+ s32 ret_val;
u16 i;
- /* Take ownership of the buffer */
- wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU);
-
- /* Make sure we have ownership now... */
- p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number));
- if (!(p2v_mailbox & E1000_P2VMAILBOX_PFU)) {
- /* failed to grab ownership */
- ret_val = -E1000_ERR_MBX;
+ /* lock the mailbox to prevent pf/vf race condition */
+ ret_val = igb_obtain_mbx_lock_pf(hw, vf_number);
+ if (ret_val)
goto out_no_read;
- }
/* copy the message to the mailbox memory buffer */
for (i = 0; i < size; i++)
@@ -392,8 +408,6 @@ static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size,
/* update stats */
hw->mbx.stats.msgs_rx++;
- ret_val = 0;
-
out_no_read:
return ret_val;
}
diff --git a/drivers/net/igb/e1000_mbx.h b/drivers/net/igb/e1000_mbx.h
index ebc02ea3f19..bb112fb6c3a 100644
--- a/drivers/net/igb/e1000_mbx.h
+++ b/drivers/net/igb/e1000_mbx.h
@@ -58,10 +58,12 @@
#define E1000_VT_MSGINFO_MASK (0xFF << E1000_VT_MSGINFO_SHIFT)
#define E1000_VF_RESET 0x01 /* VF requests reset */
-#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests PF to set MAC addr */
-#define E1000_VF_SET_MULTICAST 0x03 /* VF requests PF to set MC addr */
-#define E1000_VF_SET_VLAN 0x04 /* VF requests PF to set VLAN */
-#define E1000_VF_SET_LPE 0x05 /* VF requests PF to set VMOLR.LPE */
+#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests to set MAC addr */
+#define E1000_VF_SET_MULTICAST 0x03 /* VF requests to set MC addr */
+#define E1000_VF_SET_VLAN 0x04 /* VF requests to set VLAN */
+#define E1000_VF_SET_LPE 0x05 /* VF requests to set VMOLR.LPE */
+#define E1000_VF_SET_PROMISC 0x06 /*VF requests to clear VMOLR.ROPE/MPME*/
+#define E1000_VF_SET_PROMISC_MULTICAST (0x02 << E1000_VT_MSGINFO_SHIFT)
#define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */
diff --git a/drivers/net/igb/e1000_nvm.c b/drivers/net/igb/e1000_nvm.c
index a88bfe2f1e8..d83b77fa403 100644
--- a/drivers/net/igb/e1000_nvm.c
+++ b/drivers/net/igb/e1000_nvm.c
@@ -78,9 +78,7 @@ static void igb_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count)
u32 mask;
mask = 0x01 << (count - 1);
- if (nvm->type == e1000_nvm_eeprom_microwire)
- eecd &= ~E1000_EECD_DO;
- else if (nvm->type == e1000_nvm_eeprom_spi)
+ if (nvm->type == e1000_nvm_eeprom_spi)
eecd |= E1000_EECD_DO;
do {
@@ -220,22 +218,7 @@ static void igb_standby_nvm(struct e1000_hw *hw)
struct e1000_nvm_info *nvm = &hw->nvm;
u32 eecd = rd32(E1000_EECD);
- if (nvm->type == e1000_nvm_eeprom_microwire) {
- eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
- wr32(E1000_EECD, eecd);
- wrfl();
- udelay(nvm->delay_usec);
-
- igb_raise_eec_clk(hw, &eecd);
-
- /* Select EEPROM */
- eecd |= E1000_EECD_CS;
- wr32(E1000_EECD, eecd);
- wrfl();
- udelay(nvm->delay_usec);
-
- igb_lower_eec_clk(hw, &eecd);
- } else if (nvm->type == e1000_nvm_eeprom_spi) {
+ if (nvm->type == e1000_nvm_eeprom_spi) {
/* Toggle CS to flush commands */
eecd |= E1000_EECD_CS;
wr32(E1000_EECD, eecd);
@@ -263,12 +246,6 @@ static void e1000_stop_nvm(struct e1000_hw *hw)
/* Pull CS high */
eecd |= E1000_EECD_CS;
igb_lower_eec_clk(hw, &eecd);
- } else if (hw->nvm.type == e1000_nvm_eeprom_microwire) {
- /* CS on Microcwire is active-high */
- eecd &= ~(E1000_EECD_CS | E1000_EECD_DI);
- wr32(E1000_EECD, eecd);
- igb_raise_eec_clk(hw, &eecd);
- igb_lower_eec_clk(hw, &eecd);
}
}
@@ -304,14 +281,7 @@ static s32 igb_ready_nvm_eeprom(struct e1000_hw *hw)
u8 spi_stat_reg;
- if (nvm->type == e1000_nvm_eeprom_microwire) {
- /* Clear SK and DI */
- eecd &= ~(E1000_EECD_DI | E1000_EECD_SK);
- wr32(E1000_EECD, eecd);
- /* Set CS */
- eecd |= E1000_EECD_CS;
- wr32(E1000_EECD, eecd);
- } else if (nvm->type == e1000_nvm_eeprom_spi) {
+ if (nvm->type == e1000_nvm_eeprom_spi) {
/* Clear SK and CS */
eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
wr32(E1000_EECD, eecd);
diff --git a/drivers/net/igb/e1000_phy.c b/drivers/net/igb/e1000_phy.c
index ee460600e74..5c9d73e9bb8 100644
--- a/drivers/net/igb/e1000_phy.c
+++ b/drivers/net/igb/e1000_phy.c
@@ -39,6 +39,9 @@ static s32 igb_wait_autoneg(struct e1000_hw *hw);
/* Cable length tables */
static const u16 e1000_m88_cable_length_table[] =
{ 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED };
+#define M88E1000_CABLE_LENGTH_TABLE_SIZE \
+ (sizeof(e1000_m88_cable_length_table) / \
+ sizeof(e1000_m88_cable_length_table[0]))
static const u16 e1000_igp_2_cable_length_table[] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21,
@@ -109,7 +112,10 @@ out:
**/
static s32 igb_phy_reset_dsp(struct e1000_hw *hw)
{
- s32 ret_val;
+ s32 ret_val = 0;
+
+ if (!(hw->phy.ops.write_reg))
+ goto out;
ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1);
if (ret_val)
@@ -130,7 +136,7 @@ out:
* Reads the MDI control regsiter in the PHY at offset and stores the
* information read to data.
**/
-static s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
+s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
{
struct e1000_phy_info *phy = &hw->phy;
u32 i, mdic = 0;
@@ -188,7 +194,7 @@ out:
*
* Writes data to MDI control register in the PHY at offset.
**/
-static s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
+s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
{
struct e1000_phy_info *phy = &hw->phy;
u32 i, mdic = 0;
@@ -239,6 +245,103 @@ out:
}
/**
+ * igb_read_phy_reg_i2c - Read PHY register using i2c
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset using the i2c interface and stores the
+ * retrieved information in data.
+ **/
+s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ u32 i, i2ccmd = 0;
+
+
+ /*
+ * Set up Op-code, Phy Address, and register address in the I2CCMD
+ * register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+ (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+ (E1000_I2CCMD_OPCODE_READ));
+
+ wr32(E1000_I2CCMD, i2ccmd);
+
+ /* Poll the ready bit to see if the I2C read completed */
+ for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+ udelay(50);
+ i2ccmd = rd32(E1000_I2CCMD);
+ if (i2ccmd & E1000_I2CCMD_READY)
+ break;
+ }
+ if (!(i2ccmd & E1000_I2CCMD_READY)) {
+ hw_dbg("I2CCMD Read did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (i2ccmd & E1000_I2CCMD_ERROR) {
+ hw_dbg("I2CCMD Error bit set\n");
+ return -E1000_ERR_PHY;
+ }
+
+ /* Need to byte-swap the 16-bit value. */
+ *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00);
+
+ return 0;
+}
+
+/**
+ * igb_write_phy_reg_i2c - Write PHY register using i2c
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset using the i2c interface.
+ **/
+s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ u32 i, i2ccmd = 0;
+ u16 phy_data_swapped;
+
+
+ /* Swap the data bytes for the I2C interface */
+ phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
+
+ /*
+ * Set up Op-code, Phy Address, and register address in the I2CCMD
+ * register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+ (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+ E1000_I2CCMD_OPCODE_WRITE |
+ phy_data_swapped);
+
+ wr32(E1000_I2CCMD, i2ccmd);
+
+ /* Poll the ready bit to see if the I2C read completed */
+ for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+ udelay(50);
+ i2ccmd = rd32(E1000_I2CCMD);
+ if (i2ccmd & E1000_I2CCMD_READY)
+ break;
+ }
+ if (!(i2ccmd & E1000_I2CCMD_READY)) {
+ hw_dbg("I2CCMD Write did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (i2ccmd & E1000_I2CCMD_ERROR) {
+ hw_dbg("I2CCMD Error bit set\n");
+ return -E1000_ERR_PHY;
+ }
+
+ return 0;
+}
+
+/**
* igb_read_phy_reg_igp - Read igp PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
@@ -318,6 +421,57 @@ out:
}
/**
+ * igb_copper_link_setup_82580 - Setup 82580 PHY for copper link
+ * @hw: pointer to the HW structure
+ *
+ * Sets up Carrier-sense on Transmit and downshift values.
+ **/
+s32 igb_copper_link_setup_82580(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 phy_data;
+
+
+ if (phy->reset_disable) {
+ ret_val = 0;
+ goto out;
+ }
+
+ if (phy->type == e1000_phy_82580) {
+ ret_val = hw->phy.ops.reset(hw);
+ if (ret_val) {
+ hw_dbg("Error resetting the PHY.\n");
+ goto out;
+ }
+ }
+
+ /* Enable CRS on TX. This must be set for half-duplex operation. */
+ ret_val = phy->ops.read_reg(hw, I82580_CFG_REG, &phy_data);
+ if (ret_val)
+ goto out;
+
+ phy_data |= I82580_CFG_ASSERT_CRS_ON_TX;
+
+ /* Enable downshift */
+ phy_data |= I82580_CFG_ENABLE_DOWNSHIFT;
+
+ ret_val = phy->ops.write_reg(hw, I82580_CFG_REG, phy_data);
+ if (ret_val)
+ goto out;
+
+ /* Set number of link attempts before downshift */
+ ret_val = phy->ops.read_reg(hw, I82580_CTRL_REG, &phy_data);
+ if (ret_val)
+ goto out;
+ phy_data &= ~I82580_CTRL_DOWNSHIFT_MASK;
+ ret_val = phy->ops.write_reg(hw, I82580_CTRL_REG, phy_data);
+
+out:
+ return ret_val;
+}
+
+/**
* igb_copper_link_setup_m88 - Setup m88 PHY's for copper link
* @hw: pointer to the HW structure
*
@@ -572,7 +726,7 @@ out:
* and restart the negotiation process between the link partner. If
* autoneg_wait_to_complete, then wait for autoneg to complete before exiting.
**/
-s32 igb_copper_link_autoneg(struct e1000_hw *hw)
+static s32 igb_copper_link_autoneg(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
@@ -796,6 +950,65 @@ out:
}
/**
+ * igb_setup_copper_link - Configure copper link settings
+ * @hw: pointer to the HW structure
+ *
+ * Calls the appropriate function to configure the link for auto-neg or forced
+ * speed and duplex. Then we check for link, once link is established calls
+ * to configure collision distance and flow control are called. If link is
+ * not established, we return -E1000_ERR_PHY (-2).
+ **/
+s32 igb_setup_copper_link(struct e1000_hw *hw)
+{
+ s32 ret_val;
+ bool link;
+
+
+ if (hw->mac.autoneg) {
+ /*
+ * Setup autoneg and flow control advertisement and perform
+ * autonegotiation.
+ */
+ ret_val = igb_copper_link_autoneg(hw);
+ if (ret_val)
+ goto out;
+ } else {
+ /*
+ * PHY will be set to 10H, 10F, 100H or 100F
+ * depending on user settings.
+ */
+ hw_dbg("Forcing Speed and Duplex\n");
+ ret_val = hw->phy.ops.force_speed_duplex(hw);
+ if (ret_val) {
+ hw_dbg("Error Forcing Speed and Duplex\n");
+ goto out;
+ }
+ }
+
+ /*
+ * Check link status. Wait up to 100 microseconds for link to become
+ * valid.
+ */
+ ret_val = igb_phy_has_link(hw,
+ COPPER_LINK_UP_LIMIT,
+ 10,
+ &link);
+ if (ret_val)
+ goto out;
+
+ if (link) {
+ hw_dbg("Valid link established!!!\n");
+ igb_config_collision_dist(hw);
+ ret_val = igb_config_fc_after_link_up(hw);
+ } else {
+ hw_dbg("Unable to establish link!!!\n");
+ }
+
+out:
+ return ret_val;
+}
+
+/**
* igb_phy_force_speed_duplex_igp - Force speed/duplex for igp PHY
* @hw: pointer to the HW structure
*
@@ -903,22 +1116,19 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
igb_phy_force_speed_duplex_setup(hw, &phy_data);
- /* Reset the phy to commit changes. */
- phy_data |= MII_CR_RESET;
-
ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
if (ret_val)
goto out;
- udelay(1);
+ /* Reset the phy to commit changes. */
+ ret_val = igb_phy_sw_reset(hw);
+ if (ret_val)
+ goto out;
if (phy->autoneg_wait_to_complete) {
hw_dbg("Waiting for forced speed/duplex link on M88 phy.\n");
- ret_val = igb_phy_has_link(hw,
- PHY_FORCE_LIMIT,
- 100000,
- &link);
+ ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT, 100000, &link);
if (ret_val)
goto out;
@@ -928,8 +1138,8 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
* Reset the DSP and cross our fingers.
*/
ret_val = phy->ops.write_reg(hw,
- M88E1000_PHY_PAGE_SELECT,
- 0x001d);
+ M88E1000_PHY_PAGE_SELECT,
+ 0x001d);
if (ret_val)
goto out;
ret_val = igb_phy_reset_dsp(hw);
@@ -939,7 +1149,7 @@ s32 igb_phy_force_speed_duplex_m88(struct e1000_hw *hw)
/* Try once more */
ret_val = igb_phy_has_link(hw, PHY_FORCE_LIMIT,
- 100000, &link);
+ 100000, &link);
if (ret_val)
goto out;
}
@@ -1051,9 +1261,12 @@ static void igb_phy_force_speed_duplex_setup(struct e1000_hw *hw,
s32 igb_set_d3_lplu_state(struct e1000_hw *hw, bool active)
{
struct e1000_phy_info *phy = &hw->phy;
- s32 ret_val;
+ s32 ret_val = 0;
u16 data;
+ if (!(hw->phy.ops.read_reg))
+ goto out;
+
ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data);
if (ret_val)
goto out;
@@ -1288,8 +1501,14 @@ s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
* it across the board.
*/
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
- if (ret_val)
- break;
+ if (ret_val) {
+ /*
+ * If the first read fails, another entity may have
+ * ownership of the resources, wait and try again to
+ * see if they have relinquished the resources yet.
+ */
+ udelay(usec_interval);
+ }
ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
if (ret_val)
break;
@@ -1333,8 +1552,13 @@ s32 igb_get_cable_length_m88(struct e1000_hw *hw)
index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
M88E1000_PSSR_CABLE_LENGTH_SHIFT;
+ if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) {
+ ret_val = -E1000_ERR_PHY;
+ goto out;
+ }
+
phy->min_cable_length = e1000_m88_cable_length_table[index];
- phy->max_cable_length = e1000_m88_cable_length_table[index+1];
+ phy->max_cable_length = e1000_m88_cable_length_table[index + 1];
phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
@@ -1715,3 +1939,194 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw)
return 0;
}
+/**
+ * igb_check_polarity_82580 - Checks the polarity.
+ * @hw: pointer to the HW structure
+ *
+ * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
+ *
+ * Polarity is determined based on the PHY specific status register.
+ **/
+static s32 igb_check_polarity_82580(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 data;
+
+
+ ret_val = phy->ops.read_reg(hw, I82580_PHY_STATUS_2, &data);
+
+ if (!ret_val)
+ phy->cable_polarity = (data & I82580_PHY_STATUS2_REV_POLARITY)
+ ? e1000_rev_polarity_reversed
+ : e1000_rev_polarity_normal;
+
+ return ret_val;
+}
+
+/**
+ * igb_phy_force_speed_duplex_82580 - Force speed/duplex for I82580 PHY
+ * @hw: pointer to the HW structure
+ *
+ * Calls the PHY setup function to force speed and duplex. Clears the
+ * auto-crossover to force MDI manually. Waits for link and returns
+ * successful if link up is successful, else -E1000_ERR_PHY (-2).
+ **/
+s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 phy_data;
+ bool link;
+
+
+ ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
+ if (ret_val)
+ goto out;
+
+ igb_phy_force_speed_duplex_setup(hw, &phy_data);
+
+ ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
+ if (ret_val)
+ goto out;
+
+ /*
+ * Clear Auto-Crossover to force MDI manually. 82580 requires MDI
+ * forced whenever speed and duplex are forced.
+ */
+ ret_val = phy->ops.read_reg(hw, I82580_PHY_CTRL_2, &phy_data);
+ if (ret_val)
+ goto out;
+
+ phy_data &= ~I82580_PHY_CTRL2_AUTO_MDIX;
+ phy_data &= ~I82580_PHY_CTRL2_FORCE_MDI_MDIX;
+
+ ret_val = phy->ops.write_reg(hw, I82580_PHY_CTRL_2, phy_data);
+ if (ret_val)
+ goto out;
+
+ hw_dbg("I82580_PHY_CTRL_2: %X\n", phy_data);
+
+ udelay(1);
+
+ if (phy->autoneg_wait_to_complete) {
+ hw_dbg("Waiting for forced speed/duplex link on 82580 phy\n");
+
+ ret_val = igb_phy_has_link(hw,
+ PHY_FORCE_LIMIT,
+ 100000,
+ &link);
+ if (ret_val)
+ goto out;
+
+ if (!link)
+ hw_dbg("Link taking longer than expected.\n");
+
+ /* Try once more */
+ ret_val = igb_phy_has_link(hw,
+ PHY_FORCE_LIMIT,
+ 100000,
+ &link);
+ if (ret_val)
+ goto out;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * igb_get_phy_info_82580 - Retrieve I82580 PHY information
+ * @hw: pointer to the HW structure
+ *
+ * Read PHY status to determine if link is up. If link is up, then
+ * set/determine 10base-T extended distance and polarity correction. Read
+ * PHY port status to determine MDI/MDIx and speed. Based on the speed,
+ * determine on the cable length, local and remote receiver.
+ **/
+s32 igb_get_phy_info_82580(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 data;
+ bool link;
+
+
+ ret_val = igb_phy_has_link(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link) {
+ hw_dbg("Phy info is only valid if link is up\n");
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ phy->polarity_correction = true;
+
+ ret_val = igb_check_polarity_82580(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.read_reg(hw, I82580_PHY_STATUS_2, &data);
+ if (ret_val)
+ goto out;
+
+ phy->is_mdix = (data & I82580_PHY_STATUS2_MDIX) ? true : false;
+
+ if ((data & I82580_PHY_STATUS2_SPEED_MASK) ==
+ I82580_PHY_STATUS2_SPEED_1000MBPS) {
+ ret_val = hw->phy.ops.get_cable_length(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data);
+ if (ret_val)
+ goto out;
+
+ phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
+ ? e1000_1000t_rx_status_ok
+ : e1000_1000t_rx_status_not_ok;
+
+ phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
+ ? e1000_1000t_rx_status_ok
+ : e1000_1000t_rx_status_not_ok;
+ } else {
+ phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
+ phy->local_rx = e1000_1000t_rx_status_undefined;
+ phy->remote_rx = e1000_1000t_rx_status_undefined;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * igb_get_cable_length_82580 - Determine cable length for 82580 PHY
+ * @hw: pointer to the HW structure
+ *
+ * Reads the diagnostic status register and verifies result is valid before
+ * placing it in the phy_cable_length field.
+ **/
+s32 igb_get_cable_length_82580(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val;
+ u16 phy_data, length;
+
+
+ ret_val = phy->ops.read_reg(hw, I82580_PHY_DIAG_STATUS, &phy_data);
+ if (ret_val)
+ goto out;
+
+ length = (phy_data & I82580_DSTATUS_CABLE_LENGTH) >>
+ I82580_DSTATUS_CABLE_LENGTH_SHIFT;
+
+ if (length == E1000_CABLE_LENGTH_UNDEFINED)
+ ret_val = -E1000_ERR_PHY;
+
+ phy->cable_length = length;
+
+out:
+ return ret_val;
+}
diff --git a/drivers/net/igb/e1000_phy.h b/drivers/net/igb/e1000_phy.h
index ebe4b616db8..555eb54bb6e 100644
--- a/drivers/net/igb/e1000_phy.h
+++ b/drivers/net/igb/e1000_phy.h
@@ -43,7 +43,6 @@ enum e1000_smart_speed {
s32 igb_check_downshift(struct e1000_hw *hw);
s32 igb_check_reset_block(struct e1000_hw *hw);
-s32 igb_copper_link_autoneg(struct e1000_hw *hw);
s32 igb_copper_link_setup_igp(struct e1000_hw *hw);
s32 igb_copper_link_setup_m88(struct e1000_hw *hw);
s32 igb_phy_force_speed_duplex_igp(struct e1000_hw *hw);
@@ -57,10 +56,19 @@ s32 igb_phy_sw_reset(struct e1000_hw *hw);
s32 igb_phy_hw_reset(struct e1000_hw *hw);
s32 igb_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data);
s32 igb_set_d3_lplu_state(struct e1000_hw *hw, bool active);
+s32 igb_setup_copper_link(struct e1000_hw *hw);
s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations,
u32 usec_interval, bool *success);
s32 igb_phy_init_script_igp3(struct e1000_hw *hw);
+s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
+s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data);
+s32 igb_copper_link_setup_82580(struct e1000_hw *hw);
+s32 igb_get_phy_info_82580(struct e1000_hw *hw);
+s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw);
+s32 igb_get_cable_length_82580(struct e1000_hw *hw);
/* IGP01E1000 Specific Registers */
#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */
@@ -75,6 +83,33 @@ s32 igb_phy_init_script_igp3(struct e1000_hw *hw);
#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */
#define IGP01E1000_PSCFR_SMART_SPEED 0x0080
+#define I82580_ADDR_REG 16
+#define I82580_CFG_REG 22
+#define I82580_CFG_ASSERT_CRS_ON_TX (1 << 15)
+#define I82580_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */
+#define I82580_CTRL_REG 23
+#define I82580_CTRL_DOWNSHIFT_MASK (7 << 10)
+
+/* 82580 specific PHY registers */
+#define I82580_PHY_CTRL_2 18
+#define I82580_PHY_LBK_CTRL 19
+#define I82580_PHY_STATUS_2 26
+#define I82580_PHY_DIAG_STATUS 31
+
+/* I82580 PHY Status 2 */
+#define I82580_PHY_STATUS2_REV_POLARITY 0x0400
+#define I82580_PHY_STATUS2_MDIX 0x0800
+#define I82580_PHY_STATUS2_SPEED_MASK 0x0300
+#define I82580_PHY_STATUS2_SPEED_1000MBPS 0x0200
+#define I82580_PHY_STATUS2_SPEED_100MBPS 0x0100
+
+/* I82580 PHY Control 2 */
+#define I82580_PHY_CTRL2_AUTO_MDIX 0x0400
+#define I82580_PHY_CTRL2_FORCE_MDI_MDIX 0x0200
+
+/* I82580 PHY Diagnostics Status */
+#define I82580_DSTATUS_CABLE_LENGTH 0x03FC
+#define I82580_DSTATUS_CABLE_LENGTH_SHIFT 2
/* Enable flexible speed on link-up */
#define IGP02E1000_PM_D0_LPLU 0x0002 /* For D0a states */
#define IGP02E1000_PM_D3_LPLU 0x0004 /* For all other states */
diff --git a/drivers/net/igb/e1000_regs.h b/drivers/net/igb/e1000_regs.h
index 345d1442d6d..dd4e6ffd29f 100644
--- a/drivers/net/igb/e1000_regs.h
+++ b/drivers/net/igb/e1000_regs.h
@@ -34,6 +34,7 @@
#define E1000_EERD 0x00014 /* EEPROM Read - RW */
#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */
#define E1000_MDIC 0x00020 /* MDI Control - RW */
+#define E1000_MDICNFG 0x00E04 /* MDI Config - RW */
#define E1000_SCTL 0x00024 /* SerDes Control - RW */
#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */
#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */
@@ -76,59 +77,20 @@
#define E1000_FCRTV 0x02460 /* Flow Control Refresh Timer Value - RW */
/* IEEE 1588 TIMESYNCH */
-#define E1000_TSYNCTXCTL 0x0B614
-#define E1000_TSYNCTXCTL_VALID (1<<0)
-#define E1000_TSYNCTXCTL_ENABLED (1<<4)
-#define E1000_TSYNCRXCTL 0x0B620
-#define E1000_TSYNCRXCTL_VALID (1<<0)
-#define E1000_TSYNCRXCTL_ENABLED (1<<4)
-enum {
- E1000_TSYNCRXCTL_TYPE_L2_V2 = 0,
- E1000_TSYNCRXCTL_TYPE_L4_V1 = (1<<1),
- E1000_TSYNCRXCTL_TYPE_L2_L4_V2 = (1<<2),
- E1000_TSYNCRXCTL_TYPE_ALL = (1<<3),
- E1000_TSYNCRXCTL_TYPE_EVENT_V2 = (1<<3) | (1<<1),
-};
-#define E1000_TSYNCRXCFG 0x05F50
-enum {
- E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE = 0<<0,
- E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE = 1<<0,
- E1000_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE = 2<<0,
- E1000_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE = 3<<0,
- E1000_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE = 4<<0,
-
- E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE = 0<<8,
- E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE = 1<<8,
- E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE = 2<<8,
- E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE = 3<<8,
- E1000_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE = 8<<8,
- E1000_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE = 9<<8,
- E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE = 0xA<<8,
- E1000_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE = 0xB<<8,
- E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE = 0xC<<8,
- E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE = 0xD<<8,
-};
-#define E1000_SYSTIML 0x0B600
-#define E1000_SYSTIMH 0x0B604
-#define E1000_TIMINCA 0x0B608
-
-#define E1000_RXMTRL 0x0B634
-#define E1000_RXSTMPL 0x0B624
-#define E1000_RXSTMPH 0x0B628
-#define E1000_RXSATRL 0x0B62C
-#define E1000_RXSATRH 0x0B630
-
-#define E1000_TXSTMPL 0x0B618
-#define E1000_TXSTMPH 0x0B61C
-
-#define E1000_ETQF0 0x05CB0
-#define E1000_ETQF1 0x05CB4
-#define E1000_ETQF2 0x05CB8
-#define E1000_ETQF3 0x05CBC
-#define E1000_ETQF4 0x05CC0
-#define E1000_ETQF5 0x05CC4
-#define E1000_ETQF6 0x05CC8
-#define E1000_ETQF7 0x05CCC
+#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */
+#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */
+#define E1000_TSYNCRXCFG 0x05F50 /* Time Sync Rx Configuration - RW */
+#define E1000_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */
+#define E1000_RXSTMPH 0x0B628 /* Rx timestamp High - RO */
+#define E1000_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */
+#define E1000_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */
+#define E1000_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */
+#define E1000_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */
+#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */
+#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */
+#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */
+#define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */
+#define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */
/* Filtering Registers */
#define E1000_SAQF(_n) (0x5980 + 4 * (_n))
@@ -143,7 +105,9 @@ enum {
#define E1000_ETQF(_n) (0x05CB0 + (4 * (_n))) /* EType Queue Fltr */
#define E1000_RQDPC(_n) (0x0C030 + ((_n) * 0x40))
+
/* Split and Replication RX Control - RW */
+#define E1000_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */
/*
* Convenience macros
*
@@ -288,10 +252,17 @@ enum {
#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */
#define E1000_RA 0x05400 /* Receive Address - RW Array */
#define E1000_RA2 0x054E0 /* 2nd half of receive address array - RW Array */
+#define E1000_PSRTYPE(_i) (0x05480 + ((_i) * 4))
#define E1000_RAL(_i) (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
(0x054E0 + ((_i - 16) * 8)))
#define E1000_RAH(_i) (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
(0x054E4 + ((_i - 16) * 8)))
+#define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8))
+#define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4))
+#define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4))
+#define E1000_FFMT_REG(_i) (0x09000 + ((_i) * 8))
+#define E1000_FFVT_REG(_i) (0x09800 + ((_i) * 8))
+#define E1000_FFLT_REG(_i) (0x05F00 + ((_i) * 8))
#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */
#define E1000_VT_CTL 0x0581C /* VMDq Control - RW */
#define E1000_WUC 0x05800 /* Wakeup Control - RW */
@@ -331,6 +302,7 @@ enum {
#define E1000_QDE 0x02408 /* Queue Drop Enable - RW */
#define E1000_DTXSWC 0x03500 /* DMA Tx Switch Control - RW */
#define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */
+#define E1000_UTA 0x0A000 /* Unicast Table Array - RW */
#define E1000_IOVTCL 0x05BBC /* IOV Control Register */
/* These act per VF so an array friendly macro is used */
#define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n)))
@@ -348,4 +320,6 @@ enum {
#define array_rd32(reg, offset) \
(readl(hw->hw_addr + reg + ((offset) << 2)))
+/* DMA Coalescing registers */
+#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */
#endif
diff --git a/drivers/net/igb/igb.h b/drivers/net/igb/igb.h
index 7126fea26fe..b1c1eb88893 100644
--- a/drivers/net/igb/igb.h
+++ b/drivers/net/igb/igb.h
@@ -55,12 +55,14 @@ struct igb_adapter;
#define IGB_DEFAULT_ITR 3 /* dynamic */
#define IGB_MAX_ITR_USECS 10000
#define IGB_MIN_ITR_USECS 10
+#define NON_Q_VECTORS 1
+#define MAX_Q_VECTORS 8
/* Transmit and receive queues */
-#define IGB_MAX_RX_QUEUES (adapter->vfs_allocated_count ? \
- (adapter->vfs_allocated_count > 6 ? 1 : 2) : 4)
-#define IGB_MAX_TX_QUEUES IGB_MAX_RX_QUEUES
-#define IGB_ABS_MAX_TX_QUEUES 4
+#define IGB_MAX_RX_QUEUES (adapter->vfs_allocated_count ? 2 : \
+ (hw->mac.type > e1000_82575 ? 8 : 4))
+#define IGB_ABS_MAX_TX_QUEUES 8
+#define IGB_MAX_TX_QUEUES IGB_MAX_RX_QUEUES
#define IGB_MAX_VF_MC_ENTRIES 30
#define IGB_MAX_VF_FUNCTIONS 8
@@ -71,9 +73,14 @@ struct vf_data_storage {
u16 vf_mc_hashes[IGB_MAX_VF_MC_ENTRIES];
u16 num_vf_mc_hashes;
u16 vlans_enabled;
- bool clear_to_send;
+ u32 flags;
+ unsigned long last_nack;
};
+#define IGB_VF_FLAG_CTS 0x00000001 /* VF is clear to send data */
+#define IGB_VF_FLAG_UNI_PROMISC 0x00000002 /* VF has unicast promisc */
+#define IGB_VF_FLAG_MULTI_PROMISC 0x00000004 /* VF has multicast promisc */
+
/* RX descriptor control thresholds.
* PTHRESH - MAC will consider prefetch if it has fewer than this number of
* descriptors available in its onboard memory.
@@ -85,17 +92,19 @@ struct vf_data_storage {
* descriptors until either it has this many to write back, or the
* ITR timer expires.
*/
-#define IGB_RX_PTHRESH 16
+#define IGB_RX_PTHRESH (hw->mac.type <= e1000_82576 ? 16 : 8)
#define IGB_RX_HTHRESH 8
#define IGB_RX_WTHRESH 1
+#define IGB_TX_PTHRESH 8
+#define IGB_TX_HTHRESH 1
+#define IGB_TX_WTHRESH ((hw->mac.type == e1000_82576 && \
+ adapter->msix_entries) ? 0 : 16)
/* this is the size past which hardware will drop packets when setting LPE=0 */
#define MAXIMUM_ETHERNET_VLAN_SIZE 1522
/* Supported Rx Buffer Sizes */
#define IGB_RXBUFFER_128 128 /* Used for packet split */
-#define IGB_RXBUFFER_256 256 /* Used for packet split */
-#define IGB_RXBUFFER_512 512
#define IGB_RXBUFFER_1024 1024
#define IGB_RXBUFFER_2048 2048
#define IGB_RXBUFFER_16384 16384
@@ -128,12 +137,13 @@ struct igb_buffer {
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
+ u16 mapped_as_page;
};
/* RX */
struct {
struct page *page;
- u64 page_dma;
- unsigned int page_offset;
+ dma_addr_t page_dma;
+ u16 page_offset;
};
};
};
@@ -141,36 +151,55 @@ struct igb_buffer {
struct igb_tx_queue_stats {
u64 packets;
u64 bytes;
+ u64 restart_queue;
};
struct igb_rx_queue_stats {
u64 packets;
u64 bytes;
u64 drops;
+ u64 csum_err;
+ u64 alloc_failed;
};
-struct igb_ring {
+struct igb_q_vector {
struct igb_adapter *adapter; /* backlink */
- void *desc; /* descriptor ring memory */
- dma_addr_t dma; /* phys address of the ring */
- unsigned int size; /* length of desc. ring in bytes */
- unsigned int count; /* number of desc. in the ring */
- u16 next_to_use;
- u16 next_to_clean;
- u16 head;
- u16 tail;
- struct igb_buffer *buffer_info; /* array of buffer info structs */
+ struct igb_ring *rx_ring;
+ struct igb_ring *tx_ring;
+ struct napi_struct napi;
u32 eims_value;
- u32 itr_val;
- u16 itr_register;
u16 cpu;
- u16 queue_index;
- u16 reg_idx;
+ u16 itr_val;
+ u8 set_itr;
+ u8 itr_shift;
+ void __iomem *itr_register;
+
+ char name[IFNAMSIZ + 9];
+};
+
+struct igb_ring {
+ struct igb_q_vector *q_vector; /* backlink to q_vector */
+ struct net_device *netdev; /* back pointer to net_device */
+ struct pci_dev *pdev; /* pci device for dma mapping */
+ dma_addr_t dma; /* phys address of the ring */
+ void *desc; /* descriptor ring memory */
+ unsigned int size; /* length of desc. ring in bytes */
+ u16 count; /* number of desc. in the ring */
+ u16 next_to_use;
+ u16 next_to_clean;
+ u8 queue_index;
+ u8 reg_idx;
+ void __iomem *head;
+ void __iomem *tail;
+ struct igb_buffer *buffer_info; /* array of buffer info structs */
+
unsigned int total_bytes;
unsigned int total_packets;
+ u32 flags;
+
union {
/* TX */
struct {
@@ -180,16 +209,18 @@ struct igb_ring {
/* RX */
struct {
struct igb_rx_queue_stats rx_stats;
- u64 rx_queue_drops;
- struct napi_struct napi;
- int set_itr;
- struct igb_ring *buddy;
+ u32 rx_buffer_len;
};
};
-
- char name[IFNAMSIZ + 5];
};
+#define IGB_RING_FLAG_RX_CSUM 0x00000001 /* RX CSUM enabled */
+#define IGB_RING_FLAG_RX_SCTP_CSUM 0x00000002 /* SCTP CSUM offload enabled */
+
+#define IGB_RING_FLAG_TX_CTX_IDX 0x00000001 /* HW requires context index */
+
+#define IGB_ADVTXD_DCMD (E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS)
+
#define E1000_RX_DESC_ADV(R, i) \
(&(((union e1000_adv_rx_desc *)((R).desc))[i]))
#define E1000_TX_DESC_ADV(R, i) \
@@ -197,6 +228,15 @@ struct igb_ring {
#define E1000_TX_CTXTDESC_ADV(R, i) \
(&(((struct e1000_adv_tx_context_desc *)((R).desc))[i]))
+/* igb_desc_unused - calculate if we have unused descriptors */
+static inline int igb_desc_unused(struct igb_ring *ring)
+{
+ if (ring->next_to_clean > ring->next_to_use)
+ return ring->next_to_clean - ring->next_to_use - 1;
+
+ return ring->count + ring->next_to_clean - ring->next_to_use - 1;
+}
+
/* board specific private data structure */
struct igb_adapter {
@@ -205,18 +245,14 @@ struct igb_adapter {
struct vlan_group *vlgrp;
u16 mng_vlan_id;
u32 bd_number;
- u32 rx_buffer_len;
u32 wol;
u32 en_mng_pt;
u16 link_speed;
u16 link_duplex;
- unsigned int total_tx_bytes;
- unsigned int total_tx_packets;
- unsigned int total_rx_bytes;
- unsigned int total_rx_packets;
+
/* Interrupt Throttle Rate */
- u32 itr;
- u32 itr_setting;
+ u32 rx_itr_setting;
+ u32 tx_itr_setting;
u16 tx_itr;
u16 rx_itr;
@@ -229,13 +265,7 @@ struct igb_adapter {
/* TX */
struct igb_ring *tx_ring; /* One per active queue */
- unsigned int restart_queue;
unsigned long tx_queue_len;
- u32 txd_cmd;
- u32 gotc;
- u64 gotc_old;
- u64 tpt_old;
- u64 colc_old;
u32 tx_timeout_count;
/* RX */
@@ -243,20 +273,12 @@ struct igb_adapter {
int num_tx_queues;
int num_rx_queues;
- u64 hw_csum_err;
- u64 hw_csum_good;
- u32 alloc_rx_buff_failed;
- u32 gorc;
- u64 gorc_old;
- u16 rx_ps_hdr_size;
u32 max_frame_size;
u32 min_frame_size;
/* OS defined structs */
struct net_device *netdev;
- struct napi_struct napi;
struct pci_dev *pdev;
- struct net_device_stats net_stats;
struct cyclecounter cycles;
struct timecounter clock;
struct timecompare compare;
@@ -273,6 +295,9 @@ struct igb_adapter {
struct igb_ring test_rx_ring;
int msg_enable;
+
+ unsigned int num_q_vectors;
+ struct igb_q_vector *q_vector[MAX_Q_VECTORS];
struct msix_entry *msix_entries;
u32 eims_enable_mask;
u32 eims_other;
@@ -283,18 +308,20 @@ struct igb_adapter {
u32 eeprom_wol;
struct igb_ring *multi_tx_table[IGB_ABS_MAX_TX_QUEUES];
- unsigned int tx_ring_count;
- unsigned int rx_ring_count;
+ u16 tx_ring_count;
+ u16 rx_ring_count;
unsigned int vfs_allocated_count;
struct vf_data_storage *vf_data;
+ u32 rss_queues;
};
#define IGB_FLAG_HAS_MSI (1 << 0)
#define IGB_FLAG_DCA_ENABLED (1 << 1)
#define IGB_FLAG_QUAD_PORT_A (1 << 2)
-#define IGB_FLAG_NEED_CTX_IDX (1 << 3)
-#define IGB_FLAG_RX_CSUM_DISABLED (1 << 4)
+#define IGB_FLAG_QUEUE_PAIRS (1 << 3)
+#define IGB_82576_TSYNC_SHIFT 19
+#define IGB_82580_TSYNC_SHIFT 24
enum e1000_state_t {
__IGB_TESTING,
__IGB_RESETTING,
@@ -314,10 +341,18 @@ extern void igb_down(struct igb_adapter *);
extern void igb_reinit_locked(struct igb_adapter *);
extern void igb_reset(struct igb_adapter *);
extern int igb_set_spd_dplx(struct igb_adapter *, u16);
-extern int igb_setup_tx_resources(struct igb_adapter *, struct igb_ring *);
-extern int igb_setup_rx_resources(struct igb_adapter *, struct igb_ring *);
+extern int igb_setup_tx_resources(struct igb_ring *);
+extern int igb_setup_rx_resources(struct igb_ring *);
extern void igb_free_tx_resources(struct igb_ring *);
extern void igb_free_rx_resources(struct igb_ring *);
+extern void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *);
+extern void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *);
+extern void igb_setup_tctl(struct igb_adapter *);
+extern void igb_setup_rctl(struct igb_adapter *);
+extern netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *, struct igb_ring *);
+extern void igb_unmap_and_free_tx_resource(struct igb_ring *,
+ struct igb_buffer *);
+extern void igb_alloc_rx_buffers_adv(struct igb_ring *, int);
extern void igb_update_stats(struct igb_adapter *);
extern void igb_set_ethtool_ops(struct net_device *);
diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c
index b243ed3b0c3..ac9d5272650 100644
--- a/drivers/net/igb/igb_ethtool.c
+++ b/drivers/net/igb/igb_ethtool.c
@@ -44,78 +44,94 @@ struct igb_stats {
int stat_offset;
};
-#define IGB_STAT(m) FIELD_SIZEOF(struct igb_adapter, m), \
- offsetof(struct igb_adapter, m)
+#define IGB_STAT(_name, _stat) { \
+ .stat_string = _name, \
+ .sizeof_stat = FIELD_SIZEOF(struct igb_adapter, _stat), \
+ .stat_offset = offsetof(struct igb_adapter, _stat) \
+}
static const struct igb_stats igb_gstrings_stats[] = {
- { "rx_packets", IGB_STAT(stats.gprc) },
- { "tx_packets", IGB_STAT(stats.gptc) },
- { "rx_bytes", IGB_STAT(stats.gorc) },
- { "tx_bytes", IGB_STAT(stats.gotc) },
- { "rx_broadcast", IGB_STAT(stats.bprc) },
- { "tx_broadcast", IGB_STAT(stats.bptc) },
- { "rx_multicast", IGB_STAT(stats.mprc) },
- { "tx_multicast", IGB_STAT(stats.mptc) },
- { "rx_errors", IGB_STAT(net_stats.rx_errors) },
- { "tx_errors", IGB_STAT(net_stats.tx_errors) },
- { "tx_dropped", IGB_STAT(net_stats.tx_dropped) },
- { "multicast", IGB_STAT(stats.mprc) },
- { "collisions", IGB_STAT(stats.colc) },
- { "rx_length_errors", IGB_STAT(net_stats.rx_length_errors) },
- { "rx_over_errors", IGB_STAT(net_stats.rx_over_errors) },
- { "rx_crc_errors", IGB_STAT(stats.crcerrs) },
- { "rx_frame_errors", IGB_STAT(net_stats.rx_frame_errors) },
- { "rx_no_buffer_count", IGB_STAT(stats.rnbc) },
- { "rx_queue_drop_packet_count", IGB_STAT(net_stats.rx_fifo_errors) },
- { "rx_missed_errors", IGB_STAT(stats.mpc) },
- { "tx_aborted_errors", IGB_STAT(stats.ecol) },
- { "tx_carrier_errors", IGB_STAT(stats.tncrs) },
- { "tx_fifo_errors", IGB_STAT(net_stats.tx_fifo_errors) },
- { "tx_heartbeat_errors", IGB_STAT(net_stats.tx_heartbeat_errors) },
- { "tx_window_errors", IGB_STAT(stats.latecol) },
- { "tx_abort_late_coll", IGB_STAT(stats.latecol) },
- { "tx_deferred_ok", IGB_STAT(stats.dc) },
- { "tx_single_coll_ok", IGB_STAT(stats.scc) },
- { "tx_multi_coll_ok", IGB_STAT(stats.mcc) },
- { "tx_timeout_count", IGB_STAT(tx_timeout_count) },
- { "tx_restart_queue", IGB_STAT(restart_queue) },
- { "rx_long_length_errors", IGB_STAT(stats.roc) },
- { "rx_short_length_errors", IGB_STAT(stats.ruc) },
- { "rx_align_errors", IGB_STAT(stats.algnerrc) },
- { "tx_tcp_seg_good", IGB_STAT(stats.tsctc) },
- { "tx_tcp_seg_failed", IGB_STAT(stats.tsctfc) },
- { "rx_flow_control_xon", IGB_STAT(stats.xonrxc) },
- { "rx_flow_control_xoff", IGB_STAT(stats.xoffrxc) },
- { "tx_flow_control_xon", IGB_STAT(stats.xontxc) },
- { "tx_flow_control_xoff", IGB_STAT(stats.xofftxc) },
- { "rx_long_byte_count", IGB_STAT(stats.gorc) },
- { "rx_csum_offload_good", IGB_STAT(hw_csum_good) },
- { "rx_csum_offload_errors", IGB_STAT(hw_csum_err) },
- { "tx_dma_out_of_sync", IGB_STAT(stats.doosync) },
- { "alloc_rx_buff_failed", IGB_STAT(alloc_rx_buff_failed) },
- { "tx_smbus", IGB_STAT(stats.mgptc) },
- { "rx_smbus", IGB_STAT(stats.mgprc) },
- { "dropped_smbus", IGB_STAT(stats.mgpdc) },
+ IGB_STAT("rx_packets", stats.gprc),
+ IGB_STAT("tx_packets", stats.gptc),
+ IGB_STAT("rx_bytes", stats.gorc),
+ IGB_STAT("tx_bytes", stats.gotc),
+ IGB_STAT("rx_broadcast", stats.bprc),
+ IGB_STAT("tx_broadcast", stats.bptc),
+ IGB_STAT("rx_multicast", stats.mprc),
+ IGB_STAT("tx_multicast", stats.mptc),
+ IGB_STAT("multicast", stats.mprc),
+ IGB_STAT("collisions", stats.colc),
+ IGB_STAT("rx_crc_errors", stats.crcerrs),
+ IGB_STAT("rx_no_buffer_count", stats.rnbc),
+ IGB_STAT("rx_missed_errors", stats.mpc),
+ IGB_STAT("tx_aborted_errors", stats.ecol),
+ IGB_STAT("tx_carrier_errors", stats.tncrs),
+ IGB_STAT("tx_window_errors", stats.latecol),
+ IGB_STAT("tx_abort_late_coll", stats.latecol),
+ IGB_STAT("tx_deferred_ok", stats.dc),
+ IGB_STAT("tx_single_coll_ok", stats.scc),
+ IGB_STAT("tx_multi_coll_ok", stats.mcc),
+ IGB_STAT("tx_timeout_count", tx_timeout_count),
+ IGB_STAT("rx_long_length_errors", stats.roc),
+ IGB_STAT("rx_short_length_errors", stats.ruc),
+ IGB_STAT("rx_align_errors", stats.algnerrc),
+ IGB_STAT("tx_tcp_seg_good", stats.tsctc),
+ IGB_STAT("tx_tcp_seg_failed", stats.tsctfc),
+ IGB_STAT("rx_flow_control_xon", stats.xonrxc),
+ IGB_STAT("rx_flow_control_xoff", stats.xoffrxc),
+ IGB_STAT("tx_flow_control_xon", stats.xontxc),
+ IGB_STAT("tx_flow_control_xoff", stats.xofftxc),
+ IGB_STAT("rx_long_byte_count", stats.gorc),
+ IGB_STAT("tx_dma_out_of_sync", stats.doosync),
+ IGB_STAT("tx_smbus", stats.mgptc),
+ IGB_STAT("rx_smbus", stats.mgprc),
+ IGB_STAT("dropped_smbus", stats.mgpdc),
+};
+
+#define IGB_NETDEV_STAT(_net_stat) { \
+ .stat_string = __stringify(_net_stat), \
+ .sizeof_stat = FIELD_SIZEOF(struct net_device_stats, _net_stat), \
+ .stat_offset = offsetof(struct net_device_stats, _net_stat) \
+}
+static const struct igb_stats igb_gstrings_net_stats[] = {
+ IGB_NETDEV_STAT(rx_errors),
+ IGB_NETDEV_STAT(tx_errors),
+ IGB_NETDEV_STAT(tx_dropped),
+ IGB_NETDEV_STAT(rx_length_errors),
+ IGB_NETDEV_STAT(rx_over_errors),
+ IGB_NETDEV_STAT(rx_frame_errors),
+ IGB_NETDEV_STAT(rx_fifo_errors),
+ IGB_NETDEV_STAT(tx_fifo_errors),
+ IGB_NETDEV_STAT(tx_heartbeat_errors)
};
-#define IGB_QUEUE_STATS_LEN \
- (((((struct igb_adapter *)netdev_priv(netdev))->num_rx_queues)* \
- (sizeof(struct igb_rx_queue_stats) / sizeof(u64))) + \
- ((((struct igb_adapter *)netdev_priv(netdev))->num_tx_queues) * \
- (sizeof(struct igb_tx_queue_stats) / sizeof(u64))))
#define IGB_GLOBAL_STATS_LEN \
- sizeof(igb_gstrings_stats) / sizeof(struct igb_stats)
-#define IGB_STATS_LEN (IGB_GLOBAL_STATS_LEN + IGB_QUEUE_STATS_LEN)
+ (sizeof(igb_gstrings_stats) / sizeof(struct igb_stats))
+#define IGB_NETDEV_STATS_LEN \
+ (sizeof(igb_gstrings_net_stats) / sizeof(struct igb_stats))
+#define IGB_RX_QUEUE_STATS_LEN \
+ (sizeof(struct igb_rx_queue_stats) / sizeof(u64))
+#define IGB_TX_QUEUE_STATS_LEN \
+ (sizeof(struct igb_tx_queue_stats) / sizeof(u64))
+#define IGB_QUEUE_STATS_LEN \
+ ((((struct igb_adapter *)netdev_priv(netdev))->num_rx_queues * \
+ IGB_RX_QUEUE_STATS_LEN) + \
+ (((struct igb_adapter *)netdev_priv(netdev))->num_tx_queues * \
+ IGB_TX_QUEUE_STATS_LEN))
+#define IGB_STATS_LEN \
+ (IGB_GLOBAL_STATS_LEN + IGB_NETDEV_STATS_LEN + IGB_QUEUE_STATS_LEN)
+
static const char igb_gstrings_test[][ETH_GSTRING_LEN] = {
"Register test (offline)", "Eeprom test (offline)",
"Interrupt test (offline)", "Loopback test (offline)",
"Link test (on/offline)"
};
-#define IGB_TEST_LEN sizeof(igb_gstrings_test) / ETH_GSTRING_LEN
+#define IGB_TEST_LEN (sizeof(igb_gstrings_test) / ETH_GSTRING_LEN)
static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ u32 status;
if (hw->phy.media_type == e1000_media_type_copper) {
@@ -150,17 +166,20 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
ecmd->transceiver = XCVR_INTERNAL;
- if (rd32(E1000_STATUS) & E1000_STATUS_LU) {
+ status = rd32(E1000_STATUS);
- adapter->hw.mac.ops.get_speed_and_duplex(hw,
- &adapter->link_speed,
- &adapter->link_duplex);
- ecmd->speed = adapter->link_speed;
+ if (status & E1000_STATUS_LU) {
- /* unfortunately FULL_DUPLEX != DUPLEX_FULL
- * and HALF_DUPLEX != DUPLEX_HALF */
+ if ((status & E1000_STATUS_SPEED_1000) ||
+ hw->phy.media_type != e1000_media_type_copper)
+ ecmd->speed = SPEED_1000;
+ else if (status & E1000_STATUS_SPEED_100)
+ ecmd->speed = SPEED_100;
+ else
+ ecmd->speed = SPEED_10;
- if (adapter->link_duplex == FULL_DUPLEX)
+ if ((status & E1000_STATUS_FD) ||
+ hw->phy.media_type != e1000_media_type_copper)
ecmd->duplex = DUPLEX_FULL;
else
ecmd->duplex = DUPLEX_HALF;
@@ -251,8 +270,9 @@ static int igb_set_pauseparam(struct net_device *netdev,
if (netif_running(adapter->netdev)) {
igb_down(adapter);
igb_up(adapter);
- } else
+ } else {
igb_reset(adapter);
+ }
} else {
if (pause->rx_pause && pause->tx_pause)
hw->fc.requested_mode = e1000_fc_full;
@@ -276,17 +296,20 @@ static int igb_set_pauseparam(struct net_device *netdev,
static u32 igb_get_rx_csum(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
- return !(adapter->flags & IGB_FLAG_RX_CSUM_DISABLED);
+ return !!(adapter->rx_ring[0].flags & IGB_RING_FLAG_RX_CSUM);
}
static int igb_set_rx_csum(struct net_device *netdev, u32 data)
{
struct igb_adapter *adapter = netdev_priv(netdev);
+ int i;
- if (data)
- adapter->flags &= ~IGB_FLAG_RX_CSUM_DISABLED;
- else
- adapter->flags |= IGB_FLAG_RX_CSUM_DISABLED;
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ if (data)
+ adapter->rx_ring[i].flags |= IGB_RING_FLAG_RX_CSUM;
+ else
+ adapter->rx_ring[i].flags &= ~IGB_RING_FLAG_RX_CSUM;
+ }
return 0;
}
@@ -302,7 +325,7 @@ static int igb_set_tx_csum(struct net_device *netdev, u32 data)
if (data) {
netdev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
- if (adapter->hw.mac.type == e1000_82576)
+ if (adapter->hw.mac.type >= e1000_82576)
netdev->features |= NETIF_F_SCTP_CSUM;
} else {
netdev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
@@ -496,19 +519,10 @@ static void igb_get_regs(struct net_device *netdev,
regs_buff[119] = adapter->stats.scvpc;
regs_buff[120] = adapter->stats.hrmpc;
- /* These should probably be added to e1000_regs.h instead */
- #define E1000_PSRTYPE_REG(_i) (0x05480 + ((_i) * 4))
- #define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8))
- #define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4))
- #define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4))
- #define E1000_FFMT_REG(_i) (0x09000 + ((_i) * 8))
- #define E1000_FFVT_REG(_i) (0x09800 + ((_i) * 8))
- #define E1000_FFLT_REG(_i) (0x05F00 + ((_i) * 8))
-
for (i = 0; i < 4; i++)
regs_buff[121 + i] = rd32(E1000_SRRCTL(i));
for (i = 0; i < 4; i++)
- regs_buff[125 + i] = rd32(E1000_PSRTYPE_REG(i));
+ regs_buff[125 + i] = rd32(E1000_PSRTYPE(i));
for (i = 0; i < 4; i++)
regs_buff[129 + i] = rd32(E1000_RDBAL(i));
for (i = 0; i < 4; i++)
@@ -733,17 +747,17 @@ static int igb_set_ringparam(struct net_device *netdev,
struct igb_adapter *adapter = netdev_priv(netdev);
struct igb_ring *temp_ring;
int i, err = 0;
- u32 new_rx_count, new_tx_count;
+ u16 new_rx_count, new_tx_count;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
return -EINVAL;
- new_rx_count = max(ring->rx_pending, (u32)IGB_MIN_RXD);
- new_rx_count = min(new_rx_count, (u32)IGB_MAX_RXD);
+ new_rx_count = min_t(u32, ring->rx_pending, IGB_MAX_RXD);
+ new_rx_count = max_t(u16, new_rx_count, IGB_MIN_RXD);
new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE);
- new_tx_count = max(ring->tx_pending, (u32)IGB_MIN_TXD);
- new_tx_count = min(new_tx_count, (u32)IGB_MAX_TXD);
+ new_tx_count = min_t(u32, ring->tx_pending, IGB_MAX_TXD);
+ new_tx_count = max_t(u16, new_tx_count, IGB_MIN_TXD);
new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE);
if ((new_tx_count == adapter->tx_ring_count) &&
@@ -788,7 +802,7 @@ static int igb_set_ringparam(struct net_device *netdev,
for (i = 0; i < adapter->num_tx_queues; i++) {
temp_ring[i].count = new_tx_count;
- err = igb_setup_tx_resources(adapter, &temp_ring[i]);
+ err = igb_setup_tx_resources(&temp_ring[i]);
if (err) {
while (i) {
i--;
@@ -813,7 +827,7 @@ static int igb_set_ringparam(struct net_device *netdev,
for (i = 0; i < adapter->num_rx_queues; i++) {
temp_ring[i].count = new_rx_count;
- err = igb_setup_rx_resources(adapter, &temp_ring[i]);
+ err = igb_setup_rx_resources(&temp_ring[i]);
if (err) {
while (i) {
i--;
@@ -867,6 +881,49 @@ struct igb_reg_test {
#define TABLE64_TEST_LO 5
#define TABLE64_TEST_HI 6
+/* 82580 reg test */
+static struct igb_reg_test reg_test_82580[] = {
+ { E1000_FCAL, 0x100, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_FCAH, 0x100, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF },
+ { E1000_FCT, 0x100, 1, PATTERN_TEST, 0x0000FFFF, 0xFFFFFFFF },
+ { E1000_VET, 0x100, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_RDBAL(0), 0x100, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF },
+ { E1000_RDBAH(0), 0x100, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_RDLEN(0), 0x100, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
+ { E1000_RDBAL(4), 0x40, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF },
+ { E1000_RDBAH(4), 0x40, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_RDLEN(4), 0x40, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
+ /* RDH is read-only for 82580, only test RDT. */
+ { E1000_RDT(0), 0x100, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
+ { E1000_RDT(4), 0x40, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
+ { E1000_FCRTH, 0x100, 1, PATTERN_TEST, 0x0000FFF0, 0x0000FFF0 },
+ { E1000_FCTTV, 0x100, 1, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
+ { E1000_TIPG, 0x100, 1, PATTERN_TEST, 0x3FFFFFFF, 0x3FFFFFFF },
+ { E1000_TDBAL(0), 0x100, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF },
+ { E1000_TDBAH(0), 0x100, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_TDLEN(0), 0x100, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
+ { E1000_TDBAL(4), 0x40, 4, PATTERN_TEST, 0xFFFFFF80, 0xFFFFFFFF },
+ { E1000_TDBAH(4), 0x40, 4, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_TDLEN(4), 0x40, 4, PATTERN_TEST, 0x000FFFF0, 0x000FFFFF },
+ { E1000_TDT(0), 0x100, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
+ { E1000_TDT(4), 0x40, 4, PATTERN_TEST, 0x0000FFFF, 0x0000FFFF },
+ { E1000_RCTL, 0x100, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
+ { E1000_RCTL, 0x100, 1, SET_READ_TEST, 0x04CFB0FE, 0x003FFFFB },
+ { E1000_RCTL, 0x100, 1, SET_READ_TEST, 0x04CFB0FE, 0xFFFFFFFF },
+ { E1000_TCTL, 0x100, 1, SET_READ_TEST, 0xFFFFFFFF, 0x00000000 },
+ { E1000_RA, 0, 16, TABLE64_TEST_LO,
+ 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_RA, 0, 16, TABLE64_TEST_HI,
+ 0x83FFFFFF, 0xFFFFFFFF },
+ { E1000_RA2, 0, 8, TABLE64_TEST_LO,
+ 0xFFFFFFFF, 0xFFFFFFFF },
+ { E1000_RA2, 0, 8, TABLE64_TEST_HI,
+ 0x83FFFFFF, 0xFFFFFFFF },
+ { E1000_MTA, 0, 128, TABLE32_TEST,
+ 0xFFFFFFFF, 0xFFFFFFFF },
+ { 0, 0, 0, 0 }
+};
+
/* 82576 reg test */
static struct igb_reg_test reg_test_82576[] = {
{ E1000_FCAL, 0x100, 1, PATTERN_TEST, 0xFFFFFFFF, 0xFFFFFFFF },
@@ -944,7 +1001,7 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data,
{
struct e1000_hw *hw = &adapter->hw;
u32 pat, val;
- u32 _test[] =
+ static const u32 _test[] =
{0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF};
for (pat = 0; pat < ARRAY_SIZE(_test); pat++) {
wr32(reg, (_test[pat] & write));
@@ -957,6 +1014,7 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data,
return 1;
}
}
+
return 0;
}
@@ -974,6 +1032,7 @@ static bool reg_set_and_check(struct igb_adapter *adapter, u64 *data,
*data = reg;
return 1;
}
+
return 0;
}
@@ -996,14 +1055,18 @@ static int igb_reg_test(struct igb_adapter *adapter, u64 *data)
u32 value, before, after;
u32 i, toggle;
- toggle = 0x7FFFF3FF;
-
switch (adapter->hw.mac.type) {
+ case e1000_82580:
+ test = reg_test_82580;
+ toggle = 0x7FEFF3FF;
+ break;
case e1000_82576:
test = reg_test_82576;
+ toggle = 0x7FFFF3FF;
break;
default:
test = reg_test_82575;
+ toggle = 0x7FFFF3FF;
break;
}
@@ -1081,8 +1144,7 @@ static int igb_eeprom_test(struct igb_adapter *adapter, u64 *data)
*data = 0;
/* Read and add up the contents of the EEPROM */
for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
- if ((adapter->hw.nvm.ops.read(&adapter->hw, i, 1, &temp))
- < 0) {
+ if ((adapter->hw.nvm.ops.read(&adapter->hw, i, 1, &temp)) < 0) {
*data = 1;
break;
}
@@ -1098,8 +1160,7 @@ static int igb_eeprom_test(struct igb_adapter *adapter, u64 *data)
static irqreturn_t igb_test_intr(int irq, void *data)
{
- struct net_device *netdev = (struct net_device *) data;
- struct igb_adapter *adapter = netdev_priv(netdev);
+ struct igb_adapter *adapter = (struct igb_adapter *) data;
struct e1000_hw *hw = &adapter->hw;
adapter->test_icr |= rd32(E1000_ICR);
@@ -1117,38 +1178,45 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
*data = 0;
/* Hook up test interrupt handler just for this test */
- if (adapter->msix_entries)
- /* NOTE: we don't test MSI-X interrupts here, yet */
- return 0;
-
- if (adapter->flags & IGB_FLAG_HAS_MSI) {
+ if (adapter->msix_entries) {
+ if (request_irq(adapter->msix_entries[0].vector,
+ igb_test_intr, 0, netdev->name, adapter)) {
+ *data = 1;
+ return -1;
+ }
+ } else if (adapter->flags & IGB_FLAG_HAS_MSI) {
shared_int = false;
- if (request_irq(irq, &igb_test_intr, 0, netdev->name, netdev)) {
+ if (request_irq(irq,
+ igb_test_intr, 0, netdev->name, adapter)) {
*data = 1;
return -1;
}
- } else if (!request_irq(irq, &igb_test_intr, IRQF_PROBE_SHARED,
- netdev->name, netdev)) {
+ } else if (!request_irq(irq, igb_test_intr, IRQF_PROBE_SHARED,
+ netdev->name, adapter)) {
shared_int = false;
- } else if (request_irq(irq, &igb_test_intr, IRQF_SHARED,
- netdev->name, netdev)) {
+ } else if (request_irq(irq, igb_test_intr, IRQF_SHARED,
+ netdev->name, adapter)) {
*data = 1;
return -1;
}
dev_info(&adapter->pdev->dev, "testing %s interrupt\n",
(shared_int ? "shared" : "unshared"));
+
/* Disable all the interrupts */
- wr32(E1000_IMC, 0xFFFFFFFF);
+ wr32(E1000_IMC, ~0);
msleep(10);
/* Define all writable bits for ICS */
- switch(hw->mac.type) {
+ switch (hw->mac.type) {
case e1000_82575:
ics_mask = 0x37F47EDD;
break;
case e1000_82576:
ics_mask = 0x77D4FBFD;
break;
+ case e1000_82580:
+ ics_mask = 0x77DCFED5;
+ break;
default:
ics_mask = 0x7FFFFFFF;
break;
@@ -1232,190 +1300,61 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
msleep(10);
/* Unhook test interrupt handler */
- free_irq(irq, netdev);
+ if (adapter->msix_entries)
+ free_irq(adapter->msix_entries[0].vector, adapter);
+ else
+ free_irq(irq, adapter);
return *data;
}
static void igb_free_desc_rings(struct igb_adapter *adapter)
{
- struct igb_ring *tx_ring = &adapter->test_tx_ring;
- struct igb_ring *rx_ring = &adapter->test_rx_ring;
- struct pci_dev *pdev = adapter->pdev;
- int i;
-
- if (tx_ring->desc && tx_ring->buffer_info) {
- for (i = 0; i < tx_ring->count; i++) {
- struct igb_buffer *buf = &(tx_ring->buffer_info[i]);
- if (buf->dma)
- pci_unmap_single(pdev, buf->dma, buf->length,
- PCI_DMA_TODEVICE);
- if (buf->skb)
- dev_kfree_skb(buf->skb);
- }
- }
-
- if (rx_ring->desc && rx_ring->buffer_info) {
- for (i = 0; i < rx_ring->count; i++) {
- struct igb_buffer *buf = &(rx_ring->buffer_info[i]);
- if (buf->dma)
- pci_unmap_single(pdev, buf->dma,
- IGB_RXBUFFER_2048,
- PCI_DMA_FROMDEVICE);
- if (buf->skb)
- dev_kfree_skb(buf->skb);
- }
- }
-
- if (tx_ring->desc) {
- pci_free_consistent(pdev, tx_ring->size, tx_ring->desc,
- tx_ring->dma);
- tx_ring->desc = NULL;
- }
- if (rx_ring->desc) {
- pci_free_consistent(pdev, rx_ring->size, rx_ring->desc,
- rx_ring->dma);
- rx_ring->desc = NULL;
- }
-
- kfree(tx_ring->buffer_info);
- tx_ring->buffer_info = NULL;
- kfree(rx_ring->buffer_info);
- rx_ring->buffer_info = NULL;
-
- return;
+ igb_free_tx_resources(&adapter->test_tx_ring);
+ igb_free_rx_resources(&adapter->test_rx_ring);
}
static int igb_setup_desc_rings(struct igb_adapter *adapter)
{
- struct e1000_hw *hw = &adapter->hw;
struct igb_ring *tx_ring = &adapter->test_tx_ring;
struct igb_ring *rx_ring = &adapter->test_rx_ring;
- struct pci_dev *pdev = adapter->pdev;
- struct igb_buffer *buffer_info;
- u32 rctl;
- int i, ret_val;
+ struct e1000_hw *hw = &adapter->hw;
+ int ret_val;
/* Setup Tx descriptor ring and Tx buffers */
+ tx_ring->count = IGB_DEFAULT_TXD;
+ tx_ring->pdev = adapter->pdev;
+ tx_ring->netdev = adapter->netdev;
+ tx_ring->reg_idx = adapter->vfs_allocated_count;
- if (!tx_ring->count)
- tx_ring->count = IGB_DEFAULT_TXD;
-
- tx_ring->buffer_info = kcalloc(tx_ring->count,
- sizeof(struct igb_buffer),
- GFP_KERNEL);
- if (!tx_ring->buffer_info) {
+ if (igb_setup_tx_resources(tx_ring)) {
ret_val = 1;
goto err_nomem;
}
- tx_ring->size = tx_ring->count * sizeof(union e1000_adv_tx_desc);
- tx_ring->size = ALIGN(tx_ring->size, 4096);
- tx_ring->desc = pci_alloc_consistent(pdev, tx_ring->size,
- &tx_ring->dma);
- if (!tx_ring->desc) {
- ret_val = 2;
- goto err_nomem;
- }
- tx_ring->next_to_use = tx_ring->next_to_clean = 0;
-
- wr32(E1000_TDBAL(0),
- ((u64) tx_ring->dma & 0x00000000FFFFFFFF));
- wr32(E1000_TDBAH(0), ((u64) tx_ring->dma >> 32));
- wr32(E1000_TDLEN(0),
- tx_ring->count * sizeof(union e1000_adv_tx_desc));
- wr32(E1000_TDH(0), 0);
- wr32(E1000_TDT(0), 0);
- wr32(E1000_TCTL,
- E1000_TCTL_PSP | E1000_TCTL_EN |
- E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT |
- E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT);
-
- for (i = 0; i < tx_ring->count; i++) {
- union e1000_adv_tx_desc *tx_desc;
- struct sk_buff *skb;
- unsigned int size = 1024;
-
- tx_desc = E1000_TX_DESC_ADV(*tx_ring, i);
- skb = alloc_skb(size, GFP_KERNEL);
- if (!skb) {
- ret_val = 3;
- goto err_nomem;
- }
- skb_put(skb, size);
- buffer_info = &tx_ring->buffer_info[i];
- buffer_info->skb = skb;
- buffer_info->length = skb->len;
- buffer_info->dma = pci_map_single(pdev, skb->data, skb->len,
- PCI_DMA_TODEVICE);
- tx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma);
- tx_desc->read.olinfo_status = cpu_to_le32(skb->len) <<
- E1000_ADVTXD_PAYLEN_SHIFT;
- tx_desc->read.cmd_type_len = cpu_to_le32(skb->len);
- tx_desc->read.cmd_type_len |= cpu_to_le32(E1000_TXD_CMD_EOP |
- E1000_TXD_CMD_IFCS |
- E1000_TXD_CMD_RS |
- E1000_ADVTXD_DTYP_DATA |
- E1000_ADVTXD_DCMD_DEXT);
- }
+ igb_setup_tctl(adapter);
+ igb_configure_tx_ring(adapter, tx_ring);
/* Setup Rx descriptor ring and Rx buffers */
-
- if (!rx_ring->count)
- rx_ring->count = IGB_DEFAULT_RXD;
-
- rx_ring->buffer_info = kcalloc(rx_ring->count,
- sizeof(struct igb_buffer),
- GFP_KERNEL);
- if (!rx_ring->buffer_info) {
- ret_val = 4;
+ rx_ring->count = IGB_DEFAULT_RXD;
+ rx_ring->pdev = adapter->pdev;
+ rx_ring->netdev = adapter->netdev;
+ rx_ring->rx_buffer_len = IGB_RXBUFFER_2048;
+ rx_ring->reg_idx = adapter->vfs_allocated_count;
+
+ if (igb_setup_rx_resources(rx_ring)) {
+ ret_val = 3;
goto err_nomem;
}
- rx_ring->size = rx_ring->count * sizeof(union e1000_adv_rx_desc);
- rx_ring->desc = pci_alloc_consistent(pdev, rx_ring->size,
- &rx_ring->dma);
- if (!rx_ring->desc) {
- ret_val = 5;
- goto err_nomem;
- }
- rx_ring->next_to_use = rx_ring->next_to_clean = 0;
+ /* set the default queue to queue 0 of PF */
+ wr32(E1000_MRQC, adapter->vfs_allocated_count << 3);
- rctl = rd32(E1000_RCTL);
- wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN);
- wr32(E1000_RDBAL(0),
- ((u64) rx_ring->dma & 0xFFFFFFFF));
- wr32(E1000_RDBAH(0),
- ((u64) rx_ring->dma >> 32));
- wr32(E1000_RDLEN(0), rx_ring->size);
- wr32(E1000_RDH(0), 0);
- wr32(E1000_RDT(0), 0);
- rctl &= ~(E1000_RCTL_LBM_TCVR | E1000_RCTL_LBM_MAC);
- rctl = E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_RDMTS_HALF |
- (adapter->hw.mac.mc_filter_type << E1000_RCTL_MO_SHIFT);
- wr32(E1000_RCTL, rctl);
- wr32(E1000_SRRCTL(0), E1000_SRRCTL_DESCTYPE_ADV_ONEBUF);
-
- for (i = 0; i < rx_ring->count; i++) {
- union e1000_adv_rx_desc *rx_desc;
- struct sk_buff *skb;
-
- buffer_info = &rx_ring->buffer_info[i];
- rx_desc = E1000_RX_DESC_ADV(*rx_ring, i);
- skb = alloc_skb(IGB_RXBUFFER_2048 + NET_IP_ALIGN,
- GFP_KERNEL);
- if (!skb) {
- ret_val = 6;
- goto err_nomem;
- }
- skb_reserve(skb, NET_IP_ALIGN);
- buffer_info->skb = skb;
- buffer_info->dma = pci_map_single(pdev, skb->data,
- IGB_RXBUFFER_2048,
- PCI_DMA_FROMDEVICE);
- rx_desc->read.pkt_addr = cpu_to_le64(buffer_info->dma);
- memset(skb->data, 0x00, skb->len);
- }
+ /* enable receive ring */
+ igb_setup_rctl(adapter);
+ igb_configure_rx_ring(adapter, rx_ring);
+
+ igb_alloc_rx_buffers_adv(rx_ring, igb_desc_unused(rx_ring));
return 0;
@@ -1449,6 +1388,9 @@ static int igb_integrated_phy_loopback(struct igb_adapter *adapter)
igb_write_phy_reg(hw, PHY_CONTROL, 0x9140);
/* autoneg off */
igb_write_phy_reg(hw, PHY_CONTROL, 0x8140);
+ } else if (hw->phy.type == e1000_phy_82580) {
+ /* enable MII loopback */
+ igb_write_phy_reg(hw, I82580_PHY_LBK_CTRL, 0x8041);
}
ctrl_reg = rd32(E1000_CTRL);
@@ -1491,7 +1433,10 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
u32 reg;
- if (hw->phy.media_type == e1000_media_type_internal_serdes) {
+ reg = rd32(E1000_CTRL_EXT);
+
+ /* use CTRL_EXT to identify link type as SGMII can appear as copper */
+ if (reg & E1000_CTRL_EXT_LINK_MODE_MASK) {
reg = rd32(E1000_RCTL);
reg |= E1000_RCTL_LBM_TCVR;
wr32(E1000_RCTL, reg);
@@ -1522,11 +1467,9 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter)
wr32(E1000_PCS_LCTL, reg);
return 0;
- } else if (hw->phy.media_type == e1000_media_type_copper) {
- return igb_set_phy_loopback(adapter);
}
- return 7;
+ return igb_set_phy_loopback(adapter);
}
static void igb_loopback_cleanup(struct igb_adapter *adapter)
@@ -1552,35 +1495,99 @@ static void igb_create_lbtest_frame(struct sk_buff *skb,
unsigned int frame_size)
{
memset(skb->data, 0xFF, frame_size);
- frame_size &= ~1;
- memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1);
- memset(&skb->data[frame_size / 2 + 10], 0xBE, 1);
- memset(&skb->data[frame_size / 2 + 12], 0xAF, 1);
+ frame_size /= 2;
+ memset(&skb->data[frame_size], 0xAA, frame_size - 1);
+ memset(&skb->data[frame_size + 10], 0xBE, 1);
+ memset(&skb->data[frame_size + 12], 0xAF, 1);
}
static int igb_check_lbtest_frame(struct sk_buff *skb, unsigned int frame_size)
{
- frame_size &= ~1;
- if (*(skb->data + 3) == 0xFF)
- if ((*(skb->data + frame_size / 2 + 10) == 0xBE) &&
- (*(skb->data + frame_size / 2 + 12) == 0xAF))
+ frame_size /= 2;
+ if (*(skb->data + 3) == 0xFF) {
+ if ((*(skb->data + frame_size + 10) == 0xBE) &&
+ (*(skb->data + frame_size + 12) == 0xAF)) {
return 0;
+ }
+ }
return 13;
}
+static int igb_clean_test_rings(struct igb_ring *rx_ring,
+ struct igb_ring *tx_ring,
+ unsigned int size)
+{
+ union e1000_adv_rx_desc *rx_desc;
+ struct igb_buffer *buffer_info;
+ int rx_ntc, tx_ntc, count = 0;
+ u32 staterr;
+
+ /* initialize next to clean and descriptor values */
+ rx_ntc = rx_ring->next_to_clean;
+ tx_ntc = tx_ring->next_to_clean;
+ rx_desc = E1000_RX_DESC_ADV(*rx_ring, rx_ntc);
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+
+ while (staterr & E1000_RXD_STAT_DD) {
+ /* check rx buffer */
+ buffer_info = &rx_ring->buffer_info[rx_ntc];
+
+ /* unmap rx buffer, will be remapped by alloc_rx_buffers */
+ pci_unmap_single(rx_ring->pdev,
+ buffer_info->dma,
+ rx_ring->rx_buffer_len,
+ PCI_DMA_FROMDEVICE);
+ buffer_info->dma = 0;
+
+ /* verify contents of skb */
+ if (!igb_check_lbtest_frame(buffer_info->skb, size))
+ count++;
+
+ /* unmap buffer on tx side */
+ buffer_info = &tx_ring->buffer_info[tx_ntc];
+ igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
+
+ /* increment rx/tx next to clean counters */
+ rx_ntc++;
+ if (rx_ntc == rx_ring->count)
+ rx_ntc = 0;
+ tx_ntc++;
+ if (tx_ntc == tx_ring->count)
+ tx_ntc = 0;
+
+ /* fetch next descriptor */
+ rx_desc = E1000_RX_DESC_ADV(*rx_ring, rx_ntc);
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+ }
+
+ /* re-map buffers to ring, store next to clean values */
+ igb_alloc_rx_buffers_adv(rx_ring, count);
+ rx_ring->next_to_clean = rx_ntc;
+ tx_ring->next_to_clean = tx_ntc;
+
+ return count;
+}
+
static int igb_run_loopback_test(struct igb_adapter *adapter)
{
- struct e1000_hw *hw = &adapter->hw;
struct igb_ring *tx_ring = &adapter->test_tx_ring;
struct igb_ring *rx_ring = &adapter->test_rx_ring;
- struct pci_dev *pdev = adapter->pdev;
- int i, j, k, l, lc, good_cnt;
- int ret_val = 0;
- unsigned long time;
+ int i, j, lc, good_cnt, ret_val = 0;
+ unsigned int size = 1024;
+ netdev_tx_t tx_ret_val;
+ struct sk_buff *skb;
- wr32(E1000_RDT(0), rx_ring->count - 1);
+ /* allocate test skb */
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb)
+ return 11;
- /* Calculate the loop count based on the largest descriptor ring
+ /* place data into test skb */
+ igb_create_lbtest_frame(skb, size);
+ skb_put(skb, size);
+
+ /*
+ * Calculate the loop count based on the largest descriptor ring
* The idea is to wrap the largest ring a number of times using 64
* send/receive pairs during each loop
*/
@@ -1590,50 +1597,36 @@ static int igb_run_loopback_test(struct igb_adapter *adapter)
else
lc = ((rx_ring->count / 64) * 2) + 1;
- k = l = 0;
for (j = 0; j <= lc; j++) { /* loop count loop */
- for (i = 0; i < 64; i++) { /* send the packets */
- igb_create_lbtest_frame(tx_ring->buffer_info[k].skb,
- 1024);
- pci_dma_sync_single_for_device(pdev,
- tx_ring->buffer_info[k].dma,
- tx_ring->buffer_info[k].length,
- PCI_DMA_TODEVICE);
- k++;
- if (k == tx_ring->count)
- k = 0;
- }
- wr32(E1000_TDT(0), k);
- msleep(200);
- time = jiffies; /* set the start time for the receive */
+ /* reset count of good packets */
good_cnt = 0;
- do { /* receive the sent packets */
- pci_dma_sync_single_for_cpu(pdev,
- rx_ring->buffer_info[l].dma,
- IGB_RXBUFFER_2048,
- PCI_DMA_FROMDEVICE);
-
- ret_val = igb_check_lbtest_frame(
- rx_ring->buffer_info[l].skb, 1024);
- if (!ret_val)
+
+ /* place 64 packets on the transmit queue*/
+ for (i = 0; i < 64; i++) {
+ skb_get(skb);
+ tx_ret_val = igb_xmit_frame_ring_adv(skb, tx_ring);
+ if (tx_ret_val == NETDEV_TX_OK)
good_cnt++;
- l++;
- if (l == rx_ring->count)
- l = 0;
- /* time + 20 msecs (200 msecs on 2.4) is more than
- * enough time to complete the receives, if it's
- * exceeded, break and error off
- */
- } while (good_cnt < 64 && jiffies < (time + 20));
+ }
+
if (good_cnt != 64) {
- ret_val = 13; /* ret_val is the same as mis-compare */
+ ret_val = 12;
break;
}
- if (jiffies >= (time + 20)) {
- ret_val = 14; /* error code for time out error */
+
+ /* allow 200 milliseconds for packets to go from tx to rx */
+ msleep(200);
+
+ good_cnt = igb_clean_test_rings(rx_ring, tx_ring, size);
+ if (good_cnt != 64) {
+ ret_val = 13;
break;
}
} /* end loop count loop */
+
+ /* free the original skb */
+ kfree_skb(skb);
+
return ret_val;
}
@@ -1686,8 +1679,7 @@ static int igb_link_test(struct igb_adapter *adapter, u64 *data)
if (hw->mac.autoneg)
msleep(4000);
- if (!(rd32(E1000_STATUS) &
- E1000_STATUS_LU))
+ if (!(rd32(E1000_STATUS) & E1000_STATUS_LU))
*data = 1;
}
return *data;
@@ -1869,7 +1861,6 @@ static int igb_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
adapter->wol |= E1000_WUFC_BC;
if (wol->wolopts & WAKE_MAGIC)
adapter->wol |= E1000_WUFC_MAG;
-
device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
return 0;
@@ -1882,12 +1873,19 @@ static int igb_phys_id(struct net_device *netdev, u32 data)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
+ unsigned long timeout;
- if (!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))
- data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ);
+ timeout = data * 1000;
+
+ /*
+ * msleep_interruptable only accepts unsigned int so we are limited
+ * in how long a duration we can wait
+ */
+ if (!timeout || timeout > UINT_MAX)
+ timeout = UINT_MAX;
igb_blink_led(hw);
- msleep_interruptible(data * 1000);
+ msleep_interruptible(timeout);
igb_led_off(hw);
clear_bit(IGB_LED_ON, &adapter->led_status);
@@ -1900,7 +1898,6 @@ static int igb_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec)
{
struct igb_adapter *adapter = netdev_priv(netdev);
- struct e1000_hw *hw = &adapter->hw;
int i;
if ((ec->rx_coalesce_usecs > IGB_MAX_ITR_USECS) ||
@@ -1909,17 +1906,39 @@ static int igb_set_coalesce(struct net_device *netdev,
(ec->rx_coalesce_usecs == 2))
return -EINVAL;
+ if ((ec->tx_coalesce_usecs > IGB_MAX_ITR_USECS) ||
+ ((ec->tx_coalesce_usecs > 3) &&
+ (ec->tx_coalesce_usecs < IGB_MIN_ITR_USECS)) ||
+ (ec->tx_coalesce_usecs == 2))
+ return -EINVAL;
+
+ if ((adapter->flags & IGB_FLAG_QUEUE_PAIRS) && ec->tx_coalesce_usecs)
+ return -EINVAL;
+
/* convert to rate of irq's per second */
- if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3) {
- adapter->itr_setting = ec->rx_coalesce_usecs;
- adapter->itr = IGB_START_ITR;
- } else {
- adapter->itr_setting = ec->rx_coalesce_usecs << 2;
- adapter->itr = adapter->itr_setting;
- }
+ if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3)
+ adapter->rx_itr_setting = ec->rx_coalesce_usecs;
+ else
+ adapter->rx_itr_setting = ec->rx_coalesce_usecs << 2;
- for (i = 0; i < adapter->num_rx_queues; i++)
- wr32(adapter->rx_ring[i].itr_register, adapter->itr);
+ /* convert to rate of irq's per second */
+ if (adapter->flags & IGB_FLAG_QUEUE_PAIRS)
+ adapter->tx_itr_setting = adapter->rx_itr_setting;
+ else if (ec->tx_coalesce_usecs && ec->tx_coalesce_usecs <= 3)
+ adapter->tx_itr_setting = ec->tx_coalesce_usecs;
+ else
+ adapter->tx_itr_setting = ec->tx_coalesce_usecs << 2;
+
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ if (q_vector->rx_ring)
+ q_vector->itr_val = adapter->rx_itr_setting;
+ else
+ q_vector->itr_val = adapter->tx_itr_setting;
+ if (q_vector->itr_val && q_vector->itr_val <= 3)
+ q_vector->itr_val = IGB_START_ITR;
+ q_vector->set_itr = 1;
+ }
return 0;
}
@@ -1929,15 +1948,21 @@ static int igb_get_coalesce(struct net_device *netdev,
{
struct igb_adapter *adapter = netdev_priv(netdev);
- if (adapter->itr_setting <= 3)
- ec->rx_coalesce_usecs = adapter->itr_setting;
+ if (adapter->rx_itr_setting <= 3)
+ ec->rx_coalesce_usecs = adapter->rx_itr_setting;
else
- ec->rx_coalesce_usecs = adapter->itr_setting >> 2;
+ ec->rx_coalesce_usecs = adapter->rx_itr_setting >> 2;
+
+ if (!(adapter->flags & IGB_FLAG_QUEUE_PAIRS)) {
+ if (adapter->tx_itr_setting <= 3)
+ ec->tx_coalesce_usecs = adapter->tx_itr_setting;
+ else
+ ec->tx_coalesce_usecs = adapter->tx_itr_setting >> 2;
+ }
return 0;
}
-
static int igb_nway_reset(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
@@ -1962,31 +1987,32 @@ static void igb_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
struct igb_adapter *adapter = netdev_priv(netdev);
+ struct net_device_stats *net_stats = &netdev->stats;
u64 *queue_stat;
- int stat_count_tx = sizeof(struct igb_tx_queue_stats) / sizeof(u64);
- int stat_count_rx = sizeof(struct igb_rx_queue_stats) / sizeof(u64);
- int j;
- int i;
+ int i, j, k;
+ char *p;
igb_update_stats(adapter);
+
for (i = 0; i < IGB_GLOBAL_STATS_LEN; i++) {
- char *p = (char *)adapter+igb_gstrings_stats[i].stat_offset;
+ p = (char *)adapter + igb_gstrings_stats[i].stat_offset;
data[i] = (igb_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
+ for (j = 0; j < IGB_NETDEV_STATS_LEN; j++, i++) {
+ p = (char *)net_stats + igb_gstrings_net_stats[j].stat_offset;
+ data[i] = (igb_gstrings_net_stats[j].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ }
for (j = 0; j < adapter->num_tx_queues; j++) {
- int k;
queue_stat = (u64 *)&adapter->tx_ring[j].tx_stats;
- for (k = 0; k < stat_count_tx; k++)
- data[i + k] = queue_stat[k];
- i += k;
+ for (k = 0; k < IGB_TX_QUEUE_STATS_LEN; k++, i++)
+ data[i] = queue_stat[k];
}
for (j = 0; j < adapter->num_rx_queues; j++) {
- int k;
queue_stat = (u64 *)&adapter->rx_ring[j].rx_stats;
- for (k = 0; k < stat_count_rx; k++)
- data[i + k] = queue_stat[k];
- i += k;
+ for (k = 0; k < IGB_RX_QUEUE_STATS_LEN; k++, i++)
+ data[i] = queue_stat[k];
}
}
@@ -2007,11 +2033,18 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
+ for (i = 0; i < IGB_NETDEV_STATS_LEN; i++) {
+ memcpy(p, igb_gstrings_net_stats[i].stat_string,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
for (i = 0; i < adapter->num_tx_queues; i++) {
sprintf(p, "tx_queue_%u_packets", i);
p += ETH_GSTRING_LEN;
sprintf(p, "tx_queue_%u_bytes", i);
p += ETH_GSTRING_LEN;
+ sprintf(p, "tx_queue_%u_restart", i);
+ p += ETH_GSTRING_LEN;
}
for (i = 0; i < adapter->num_rx_queues; i++) {
sprintf(p, "rx_queue_%u_packets", i);
@@ -2020,6 +2053,10 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
p += ETH_GSTRING_LEN;
sprintf(p, "rx_queue_%u_drops", i);
p += ETH_GSTRING_LEN;
+ sprintf(p, "rx_queue_%u_csum_err", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rx_queue_%u_alloc_failed", i);
+ p += ETH_GSTRING_LEN;
}
/* BUG_ON(p - data != IGB_STATS_LEN * ETH_GSTRING_LEN); */
break;
diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c
index 714c3a4a44e..16349ba6873 100644
--- a/drivers/net/igb/igb_main.c
+++ b/drivers/net/igb/igb_main.c
@@ -49,7 +49,7 @@
#endif
#include "igb.h"
-#define DRV_VERSION "1.3.16-k2"
+#define DRV_VERSION "2.1.0-k2"
char igb_driver_name[] = "igb";
char igb_driver_version[] = DRV_VERSION;
static const char igb_driver_string[] =
@@ -61,8 +61,14 @@ static const struct e1000_info *igb_info_tbl[] = {
};
static struct pci_device_id igb_pci_tbl[] = {
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER), board_82575 },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_FIBER), board_82575 },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SERDES), board_82575 },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII), board_82575 },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER_DUAL), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS), board_82575 },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_NS_SERDES), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_FIBER), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES), board_82575 },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES_QUAD), board_82575 },
@@ -81,6 +87,7 @@ static int igb_setup_all_tx_resources(struct igb_adapter *);
static int igb_setup_all_rx_resources(struct igb_adapter *);
static void igb_free_all_tx_resources(struct igb_adapter *);
static void igb_free_all_rx_resources(struct igb_adapter *);
+static void igb_setup_mrqc(struct igb_adapter *);
void igb_update_stats(struct igb_adapter *);
static int igb_probe(struct pci_dev *, const struct pci_device_id *);
static void __devexit igb_remove(struct pci_dev *pdev);
@@ -89,7 +96,6 @@ static int igb_open(struct net_device *);
static int igb_close(struct net_device *);
static void igb_configure_tx(struct igb_adapter *);
static void igb_configure_rx(struct igb_adapter *);
-static void igb_setup_rctl(struct igb_adapter *);
static void igb_clean_all_tx_rings(struct igb_adapter *);
static void igb_clean_all_rx_rings(struct igb_adapter *);
static void igb_clean_tx_ring(struct igb_ring *);
@@ -98,28 +104,22 @@ static void igb_set_rx_mode(struct net_device *);
static void igb_update_phy_info(unsigned long);
static void igb_watchdog(unsigned long);
static void igb_watchdog_task(struct work_struct *);
-static netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *,
- struct net_device *,
- struct igb_ring *);
-static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb,
- struct net_device *);
+static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb, struct net_device *);
static struct net_device_stats *igb_get_stats(struct net_device *);
static int igb_change_mtu(struct net_device *, int);
static int igb_set_mac(struct net_device *, void *);
+static void igb_set_uta(struct igb_adapter *adapter);
static irqreturn_t igb_intr(int irq, void *);
static irqreturn_t igb_intr_msi(int irq, void *);
static irqreturn_t igb_msix_other(int irq, void *);
-static irqreturn_t igb_msix_rx(int irq, void *);
-static irqreturn_t igb_msix_tx(int irq, void *);
+static irqreturn_t igb_msix_ring(int irq, void *);
#ifdef CONFIG_IGB_DCA
-static void igb_update_rx_dca(struct igb_ring *);
-static void igb_update_tx_dca(struct igb_ring *);
+static void igb_update_dca(struct igb_q_vector *);
static void igb_setup_dca(struct igb_adapter *);
#endif /* CONFIG_IGB_DCA */
-static bool igb_clean_tx_irq(struct igb_ring *);
+static bool igb_clean_tx_irq(struct igb_q_vector *);
static int igb_poll(struct napi_struct *, int);
-static bool igb_clean_rx_irq_adv(struct igb_ring *, int *, int);
-static void igb_alloc_rx_buffers_adv(struct igb_ring *, int);
+static bool igb_clean_rx_irq_adv(struct igb_q_vector *, int *, int);
static int igb_ioctl(struct net_device *, struct ifreq *, int cmd);
static void igb_tx_timeout(struct net_device *);
static void igb_reset_task(struct work_struct *);
@@ -127,57 +127,13 @@ static void igb_vlan_rx_register(struct net_device *, struct vlan_group *);
static void igb_vlan_rx_add_vid(struct net_device *, u16);
static void igb_vlan_rx_kill_vid(struct net_device *, u16);
static void igb_restore_vlan(struct igb_adapter *);
+static void igb_rar_set_qsel(struct igb_adapter *, u8 *, u32 , u8);
static void igb_ping_all_vfs(struct igb_adapter *);
static void igb_msg_task(struct igb_adapter *);
-static int igb_rcv_msg_from_vf(struct igb_adapter *, u32);
-static inline void igb_set_rah_pool(struct e1000_hw *, int , int);
static void igb_vmm_control(struct igb_adapter *);
-static int igb_set_vf_mac(struct igb_adapter *adapter, int, unsigned char *);
+static int igb_set_vf_mac(struct igb_adapter *, int, unsigned char *);
static void igb_restore_vf_multicasts(struct igb_adapter *adapter);
-static inline void igb_set_vmolr(struct e1000_hw *hw, int vfn)
-{
- u32 reg_data;
-
- reg_data = rd32(E1000_VMOLR(vfn));
- reg_data |= E1000_VMOLR_BAM | /* Accept broadcast */
- E1000_VMOLR_ROPE | /* Accept packets matched in UTA */
- E1000_VMOLR_ROMPE | /* Accept packets matched in MTA */
- E1000_VMOLR_AUPE | /* Accept untagged packets */
- E1000_VMOLR_STRVLAN; /* Strip vlan tags */
- wr32(E1000_VMOLR(vfn), reg_data);
-}
-
-static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size,
- int vfn)
-{
- struct e1000_hw *hw = &adapter->hw;
- u32 vmolr;
-
- /* if it isn't the PF check to see if VFs are enabled and
- * increase the size to support vlan tags */
- if (vfn < adapter->vfs_allocated_count &&
- adapter->vf_data[vfn].vlans_enabled)
- size += VLAN_TAG_SIZE;
-
- vmolr = rd32(E1000_VMOLR(vfn));
- vmolr &= ~E1000_VMOLR_RLPML_MASK;
- vmolr |= size | E1000_VMOLR_LPE;
- wr32(E1000_VMOLR(vfn), vmolr);
-
- return 0;
-}
-
-static inline void igb_set_rah_pool(struct e1000_hw *hw, int pool, int entry)
-{
- u32 reg_data;
-
- reg_data = rd32(E1000_RAH(entry));
- reg_data &= ~E1000_RAH_POOL_MASK;
- reg_data |= E1000_RAH_POOL_1 << pool;;
- wr32(E1000_RAH(entry), reg_data);
-}
-
#ifdef CONFIG_PM
static int igb_suspend(struct pci_dev *, pm_message_t);
static int igb_resume(struct pci_dev *);
@@ -228,46 +184,12 @@ static struct pci_driver igb_driver = {
.err_handler = &igb_err_handler
};
-static int global_quad_port_a; /* global quad port a indication */
-
MODULE_AUTHOR("Intel Corporation, <e1000-devel@lists.sourceforge.net>");
MODULE_DESCRIPTION("Intel(R) Gigabit Ethernet Network Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
/**
- * Scale the NIC clock cycle by a large factor so that
- * relatively small clock corrections can be added or
- * substracted at each clock tick. The drawbacks of a
- * large factor are a) that the clock register overflows
- * more quickly (not such a big deal) and b) that the
- * increment per tick has to fit into 24 bits.
- *
- * Note that
- * TIMINCA = IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS *
- * IGB_TSYNC_SCALE
- * TIMINCA += TIMINCA * adjustment [ppm] / 1e9
- *
- * The base scale factor is intentionally a power of two
- * so that the division in %struct timecounter can be done with
- * a shift.
- */
-#define IGB_TSYNC_SHIFT (19)
-#define IGB_TSYNC_SCALE (1<<IGB_TSYNC_SHIFT)
-
-/**
- * The duration of one clock cycle of the NIC.
- *
- * @todo This hard-coded value is part of the specification and might change
- * in future hardware revisions. Add revision check.
- */
-#define IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS 16
-
-#if (IGB_TSYNC_SCALE * IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS) >= (1<<24)
-# error IGB_TSYNC_SCALE and/or IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS are too large to fit into TIMINCA
-#endif
-
-/**
* igb_read_clock - read raw cycle counter (to be used by time counter)
*/
static cycle_t igb_read_clock(const struct cyclecounter *tc)
@@ -275,11 +197,21 @@ static cycle_t igb_read_clock(const struct cyclecounter *tc)
struct igb_adapter *adapter =
container_of(tc, struct igb_adapter, cycles);
struct e1000_hw *hw = &adapter->hw;
- u64 stamp;
+ u64 stamp = 0;
+ int shift = 0;
- stamp = rd32(E1000_SYSTIML);
- stamp |= (u64)rd32(E1000_SYSTIMH) << 32ULL;
+ /*
+ * The timestamp latches on lowest register read. For the 82580
+ * the lowest register is SYSTIMR instead of SYSTIML. However we never
+ * adjusted TIMINCA so SYSTIMR will just read as all 0s so ignore it.
+ */
+ if (hw->mac.type == e1000_82580) {
+ stamp = rd32(E1000_SYSTIMR) >> 8;
+ shift = IGB_82580_TSYNC_SHIFT;
+ }
+ stamp |= (u64)rd32(E1000_SYSTIML) << shift;
+ stamp |= (u64)rd32(E1000_SYSTIMH) << (shift + 32);
return stamp;
}
@@ -320,17 +252,6 @@ static char *igb_get_time_str(struct igb_adapter *adapter,
#endif
/**
- * igb_desc_unused - calculate if we have unused descriptors
- **/
-static int igb_desc_unused(struct igb_ring *ring)
-{
- if (ring->next_to_clean > ring->next_to_use)
- return ring->next_to_clean - ring->next_to_use - 1;
-
- return ring->count + ring->next_to_clean - ring->next_to_use - 1;
-}
-
-/**
* igb_init_module - Driver Registration Routine
*
* igb_init_module is the first routine called when the driver is
@@ -344,12 +265,9 @@ static int __init igb_init_module(void)
printk(KERN_INFO "%s\n", igb_copyright);
- global_quad_port_a = 0;
-
#ifdef CONFIG_IGB_DCA
dca_register_notify(&dca_notifier);
#endif
-
ret = pci_register_driver(&igb_driver);
return ret;
}
@@ -382,8 +300,8 @@ module_exit(igb_exit_module);
**/
static void igb_cache_ring_register(struct igb_adapter *adapter)
{
- int i;
- unsigned int rbase_offset = adapter->vfs_allocated_count;
+ int i = 0, j = 0;
+ u32 rbase_offset = adapter->vfs_allocated_count;
switch (adapter->hw.mac.type) {
case e1000_82576:
@@ -392,23 +310,37 @@ static void igb_cache_ring_register(struct igb_adapter *adapter)
* In order to avoid collision we start at the first free queue
* and continue consuming queues in the same sequence
*/
- for (i = 0; i < adapter->num_rx_queues; i++)
- adapter->rx_ring[i].reg_idx = rbase_offset +
- Q_IDX_82576(i);
- for (i = 0; i < adapter->num_tx_queues; i++)
- adapter->tx_ring[i].reg_idx = rbase_offset +
- Q_IDX_82576(i);
- break;
+ if (adapter->vfs_allocated_count) {
+ for (; i < adapter->rss_queues; i++)
+ adapter->rx_ring[i].reg_idx = rbase_offset +
+ Q_IDX_82576(i);
+ for (; j < adapter->rss_queues; j++)
+ adapter->tx_ring[j].reg_idx = rbase_offset +
+ Q_IDX_82576(j);
+ }
case e1000_82575:
+ case e1000_82580:
default:
- for (i = 0; i < adapter->num_rx_queues; i++)
- adapter->rx_ring[i].reg_idx = i;
- for (i = 0; i < adapter->num_tx_queues; i++)
- adapter->tx_ring[i].reg_idx = i;
+ for (; i < adapter->num_rx_queues; i++)
+ adapter->rx_ring[i].reg_idx = rbase_offset + i;
+ for (; j < adapter->num_tx_queues; j++)
+ adapter->tx_ring[j].reg_idx = rbase_offset + j;
break;
}
}
+static void igb_free_queues(struct igb_adapter *adapter)
+{
+ kfree(adapter->tx_ring);
+ kfree(adapter->rx_ring);
+
+ adapter->tx_ring = NULL;
+ adapter->rx_ring = NULL;
+
+ adapter->num_rx_queues = 0;
+ adapter->num_tx_queues = 0;
+}
+
/**
* igb_alloc_queues - Allocate memory for all rings
* @adapter: board private structure to initialize
@@ -423,59 +355,61 @@ static int igb_alloc_queues(struct igb_adapter *adapter)
adapter->tx_ring = kcalloc(adapter->num_tx_queues,
sizeof(struct igb_ring), GFP_KERNEL);
if (!adapter->tx_ring)
- return -ENOMEM;
+ goto err;
adapter->rx_ring = kcalloc(adapter->num_rx_queues,
sizeof(struct igb_ring), GFP_KERNEL);
- if (!adapter->rx_ring) {
- kfree(adapter->tx_ring);
- return -ENOMEM;
- }
-
- adapter->rx_ring->buddy = adapter->tx_ring;
+ if (!adapter->rx_ring)
+ goto err;
for (i = 0; i < adapter->num_tx_queues; i++) {
struct igb_ring *ring = &(adapter->tx_ring[i]);
ring->count = adapter->tx_ring_count;
- ring->adapter = adapter;
ring->queue_index = i;
+ ring->pdev = adapter->pdev;
+ ring->netdev = adapter->netdev;
+ /* For 82575, context index must be unique per ring. */
+ if (adapter->hw.mac.type == e1000_82575)
+ ring->flags = IGB_RING_FLAG_TX_CTX_IDX;
}
+
for (i = 0; i < adapter->num_rx_queues; i++) {
struct igb_ring *ring = &(adapter->rx_ring[i]);
ring->count = adapter->rx_ring_count;
- ring->adapter = adapter;
ring->queue_index = i;
- ring->itr_register = E1000_ITR;
-
- /* set a default napi handler for each rx_ring */
- netif_napi_add(adapter->netdev, &ring->napi, igb_poll, 64);
+ ring->pdev = adapter->pdev;
+ ring->netdev = adapter->netdev;
+ ring->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
+ ring->flags = IGB_RING_FLAG_RX_CSUM; /* enable rx checksum */
+ /* set flag indicating ring supports SCTP checksum offload */
+ if (adapter->hw.mac.type >= e1000_82576)
+ ring->flags |= IGB_RING_FLAG_RX_SCTP_CSUM;
}
igb_cache_ring_register(adapter);
- return 0;
-}
-
-static void igb_free_queues(struct igb_adapter *adapter)
-{
- int i;
- for (i = 0; i < adapter->num_rx_queues; i++)
- netif_napi_del(&adapter->rx_ring[i].napi);
+ return 0;
- adapter->num_rx_queues = 0;
- adapter->num_tx_queues = 0;
+err:
+ igb_free_queues(adapter);
- kfree(adapter->tx_ring);
- kfree(adapter->rx_ring);
+ return -ENOMEM;
}
#define IGB_N0_QUEUE -1
-static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue,
- int tx_queue, int msix_vector)
+static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector)
{
u32 msixbm = 0;
+ struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw;
u32 ivar, index;
+ int rx_queue = IGB_N0_QUEUE;
+ int tx_queue = IGB_N0_QUEUE;
+
+ if (q_vector->rx_ring)
+ rx_queue = q_vector->rx_ring->reg_idx;
+ if (q_vector->tx_ring)
+ tx_queue = q_vector->tx_ring->reg_idx;
switch (hw->mac.type) {
case e1000_82575:
@@ -483,16 +417,12 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue,
bitmask for the EICR/EIMS/EIMC registers. To assign one
or more queues to a vector, we write the appropriate bits
into the MSIXBM register for that vector. */
- if (rx_queue > IGB_N0_QUEUE) {
+ if (rx_queue > IGB_N0_QUEUE)
msixbm = E1000_EICR_RX_QUEUE0 << rx_queue;
- adapter->rx_ring[rx_queue].eims_value = msixbm;
- }
- if (tx_queue > IGB_N0_QUEUE) {
+ if (tx_queue > IGB_N0_QUEUE)
msixbm |= E1000_EICR_TX_QUEUE0 << tx_queue;
- adapter->tx_ring[tx_queue].eims_value =
- E1000_EICR_TX_QUEUE0 << tx_queue;
- }
array_wr32(E1000_MSIXBM(0), msix_vector, msixbm);
+ q_vector->eims_value = msixbm;
break;
case e1000_82576:
/* 82576 uses a table-based method for assigning vectors.
@@ -500,7 +430,40 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue,
a vector number along with a "valid" bit. Sadly, the layout
of the table is somewhat counterintuitive. */
if (rx_queue > IGB_N0_QUEUE) {
- index = (rx_queue >> 1) + adapter->vfs_allocated_count;
+ index = (rx_queue & 0x7);
+ ivar = array_rd32(E1000_IVAR0, index);
+ if (rx_queue < 8) {
+ /* vector goes into low byte of register */
+ ivar = ivar & 0xFFFFFF00;
+ ivar |= msix_vector | E1000_IVAR_VALID;
+ } else {
+ /* vector goes into third byte of register */
+ ivar = ivar & 0xFF00FFFF;
+ ivar |= (msix_vector | E1000_IVAR_VALID) << 16;
+ }
+ array_wr32(E1000_IVAR0, index, ivar);
+ }
+ if (tx_queue > IGB_N0_QUEUE) {
+ index = (tx_queue & 0x7);
+ ivar = array_rd32(E1000_IVAR0, index);
+ if (tx_queue < 8) {
+ /* vector goes into second byte of register */
+ ivar = ivar & 0xFFFF00FF;
+ ivar |= (msix_vector | E1000_IVAR_VALID) << 8;
+ } else {
+ /* vector goes into high byte of register */
+ ivar = ivar & 0x00FFFFFF;
+ ivar |= (msix_vector | E1000_IVAR_VALID) << 24;
+ }
+ array_wr32(E1000_IVAR0, index, ivar);
+ }
+ q_vector->eims_value = 1 << msix_vector;
+ break;
+ case e1000_82580:
+ /* 82580 uses the same table-based approach as 82576 but has fewer
+ entries as a result we carry over for queues greater than 4. */
+ if (rx_queue > IGB_N0_QUEUE) {
+ index = (rx_queue >> 1);
ivar = array_rd32(E1000_IVAR0, index);
if (rx_queue & 0x1) {
/* vector goes into third byte of register */
@@ -511,11 +474,10 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue,
ivar = ivar & 0xFFFFFF00;
ivar |= msix_vector | E1000_IVAR_VALID;
}
- adapter->rx_ring[rx_queue].eims_value= 1 << msix_vector;
array_wr32(E1000_IVAR0, index, ivar);
}
if (tx_queue > IGB_N0_QUEUE) {
- index = (tx_queue >> 1) + adapter->vfs_allocated_count;
+ index = (tx_queue >> 1);
ivar = array_rd32(E1000_IVAR0, index);
if (tx_queue & 0x1) {
/* vector goes into high byte of register */
@@ -526,9 +488,9 @@ static void igb_assign_vector(struct igb_adapter *adapter, int rx_queue,
ivar = ivar & 0xFFFF00FF;
ivar |= (msix_vector | E1000_IVAR_VALID) << 8;
}
- adapter->tx_ring[tx_queue].eims_value= 1 << msix_vector;
array_wr32(E1000_IVAR0, index, ivar);
}
+ q_vector->eims_value = 1 << msix_vector;
break;
default:
BUG();
@@ -549,43 +511,10 @@ static void igb_configure_msix(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
adapter->eims_enable_mask = 0;
- if (hw->mac.type == e1000_82576)
- /* Turn on MSI-X capability first, or our settings
- * won't stick. And it will take days to debug. */
- wr32(E1000_GPIE, E1000_GPIE_MSIX_MODE |
- E1000_GPIE_PBA | E1000_GPIE_EIAME |
- E1000_GPIE_NSICR);
-
- for (i = 0; i < adapter->num_tx_queues; i++) {
- struct igb_ring *tx_ring = &adapter->tx_ring[i];
- igb_assign_vector(adapter, IGB_N0_QUEUE, i, vector++);
- adapter->eims_enable_mask |= tx_ring->eims_value;
- if (tx_ring->itr_val)
- writel(tx_ring->itr_val,
- hw->hw_addr + tx_ring->itr_register);
- else
- writel(1, hw->hw_addr + tx_ring->itr_register);
- }
-
- for (i = 0; i < adapter->num_rx_queues; i++) {
- struct igb_ring *rx_ring = &adapter->rx_ring[i];
- rx_ring->buddy = NULL;
- igb_assign_vector(adapter, i, IGB_N0_QUEUE, vector++);
- adapter->eims_enable_mask |= rx_ring->eims_value;
- if (rx_ring->itr_val)
- writel(rx_ring->itr_val,
- hw->hw_addr + rx_ring->itr_register);
- else
- writel(1, hw->hw_addr + rx_ring->itr_register);
- }
-
/* set vector for other causes, i.e. link changes */
switch (hw->mac.type) {
case e1000_82575:
- array_wr32(E1000_MSIXBM(0), vector++,
- E1000_EIMS_OTHER);
-
tmp = rd32(E1000_CTRL_EXT);
/* enable MSI-X PBA support*/
tmp |= E1000_CTRL_EXT_PBA_CLR;
@@ -595,22 +524,41 @@ static void igb_configure_msix(struct igb_adapter *adapter)
tmp |= E1000_CTRL_EXT_IRCA;
wr32(E1000_CTRL_EXT, tmp);
- adapter->eims_enable_mask |= E1000_EIMS_OTHER;
+
+ /* enable msix_other interrupt */
+ array_wr32(E1000_MSIXBM(0), vector++,
+ E1000_EIMS_OTHER);
adapter->eims_other = E1000_EIMS_OTHER;
break;
case e1000_82576:
+ case e1000_82580:
+ /* Turn on MSI-X capability first, or our settings
+ * won't stick. And it will take days to debug. */
+ wr32(E1000_GPIE, E1000_GPIE_MSIX_MODE |
+ E1000_GPIE_PBA | E1000_GPIE_EIAME |
+ E1000_GPIE_NSICR);
+
+ /* enable msix_other interrupt */
+ adapter->eims_other = 1 << vector;
tmp = (vector++ | E1000_IVAR_VALID) << 8;
- wr32(E1000_IVAR_MISC, tmp);
- adapter->eims_enable_mask = (1 << (vector)) - 1;
- adapter->eims_other = 1 << (vector - 1);
+ wr32(E1000_IVAR_MISC, tmp);
break;
default:
/* do nothing, since nothing else supports MSI-X */
break;
} /* switch (hw->mac.type) */
+
+ adapter->eims_enable_mask |= adapter->eims_other;
+
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ igb_assign_vector(q_vector, vector++);
+ adapter->eims_enable_mask |= q_vector->eims_value;
+ }
+
wrfl();
}
@@ -623,43 +571,40 @@ static void igb_configure_msix(struct igb_adapter *adapter)
static int igb_request_msix(struct igb_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
+ struct e1000_hw *hw = &adapter->hw;
int i, err = 0, vector = 0;
- vector = 0;
-
- for (i = 0; i < adapter->num_tx_queues; i++) {
- struct igb_ring *ring = &(adapter->tx_ring[i]);
- sprintf(ring->name, "%s-tx-%d", netdev->name, i);
- err = request_irq(adapter->msix_entries[vector].vector,
- &igb_msix_tx, 0, ring->name,
- &(adapter->tx_ring[i]));
- if (err)
- goto out;
- ring->itr_register = E1000_EITR(0) + (vector << 2);
- ring->itr_val = 976; /* ~4000 ints/sec */
- vector++;
- }
- for (i = 0; i < adapter->num_rx_queues; i++) {
- struct igb_ring *ring = &(adapter->rx_ring[i]);
- if (strlen(netdev->name) < (IFNAMSIZ - 5))
- sprintf(ring->name, "%s-rx-%d", netdev->name, i);
+ err = request_irq(adapter->msix_entries[vector].vector,
+ igb_msix_other, 0, netdev->name, adapter);
+ if (err)
+ goto out;
+ vector++;
+
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+
+ q_vector->itr_register = hw->hw_addr + E1000_EITR(vector);
+
+ if (q_vector->rx_ring && q_vector->tx_ring)
+ sprintf(q_vector->name, "%s-TxRx-%u", netdev->name,
+ q_vector->rx_ring->queue_index);
+ else if (q_vector->tx_ring)
+ sprintf(q_vector->name, "%s-tx-%u", netdev->name,
+ q_vector->tx_ring->queue_index);
+ else if (q_vector->rx_ring)
+ sprintf(q_vector->name, "%s-rx-%u", netdev->name,
+ q_vector->rx_ring->queue_index);
else
- memcpy(ring->name, netdev->name, IFNAMSIZ);
+ sprintf(q_vector->name, "%s-unused", netdev->name);
+
err = request_irq(adapter->msix_entries[vector].vector,
- &igb_msix_rx, 0, ring->name,
- &(adapter->rx_ring[i]));
+ igb_msix_ring, 0, q_vector->name,
+ q_vector);
if (err)
goto out;
- ring->itr_register = E1000_EITR(0) + (vector << 2);
- ring->itr_val = adapter->itr;
vector++;
}
- err = request_irq(adapter->msix_entries[vector].vector,
- &igb_msix_other, 0, netdev->name, netdev);
- if (err)
- goto out;
-
igb_configure_msix(adapter);
return 0;
out:
@@ -672,11 +617,44 @@ static void igb_reset_interrupt_capability(struct igb_adapter *adapter)
pci_disable_msix(adapter->pdev);
kfree(adapter->msix_entries);
adapter->msix_entries = NULL;
- } else if (adapter->flags & IGB_FLAG_HAS_MSI)
+ } else if (adapter->flags & IGB_FLAG_HAS_MSI) {
pci_disable_msi(adapter->pdev);
- return;
+ }
+}
+
+/**
+ * igb_free_q_vectors - Free memory allocated for interrupt vectors
+ * @adapter: board private structure to initialize
+ *
+ * This function frees the memory allocated to the q_vectors. In addition if
+ * NAPI is enabled it will delete any references to the NAPI struct prior
+ * to freeing the q_vector.
+ **/
+static void igb_free_q_vectors(struct igb_adapter *adapter)
+{
+ int v_idx;
+
+ for (v_idx = 0; v_idx < adapter->num_q_vectors; v_idx++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[v_idx];
+ adapter->q_vector[v_idx] = NULL;
+ netif_napi_del(&q_vector->napi);
+ kfree(q_vector);
+ }
+ adapter->num_q_vectors = 0;
}
+/**
+ * igb_clear_interrupt_scheme - reset the device to a state of no interrupts
+ *
+ * This function resets the device so that it has 0 rx queues, tx queues, and
+ * MSI-X interrupts allocated.
+ */
+static void igb_clear_interrupt_scheme(struct igb_adapter *adapter)
+{
+ igb_free_queues(adapter);
+ igb_free_q_vectors(adapter);
+ igb_reset_interrupt_capability(adapter);
+}
/**
* igb_set_interrupt_capability - set MSI or MSI-X if supported
@@ -690,11 +668,21 @@ static void igb_set_interrupt_capability(struct igb_adapter *adapter)
int numvecs, i;
/* Number of supported queues. */
- /* Having more queues than CPUs doesn't make sense. */
- adapter->num_rx_queues = min_t(u32, IGB_MAX_RX_QUEUES, num_online_cpus());
- adapter->num_tx_queues = min_t(u32, IGB_MAX_TX_QUEUES, num_online_cpus());
+ adapter->num_rx_queues = adapter->rss_queues;
+ adapter->num_tx_queues = adapter->rss_queues;
+
+ /* start with one vector for every rx queue */
+ numvecs = adapter->num_rx_queues;
+
+ /* if tx handler is seperate add 1 for every tx queue */
+ if (!(adapter->flags & IGB_FLAG_QUEUE_PAIRS))
+ numvecs += adapter->num_tx_queues;
+
+ /* store the number of vectors reserved for queues */
+ adapter->num_q_vectors = numvecs;
- numvecs = adapter->num_tx_queues + adapter->num_rx_queues + 1;
+ /* add 1 vector for link status interrupts */
+ numvecs++;
adapter->msix_entries = kcalloc(numvecs, sizeof(struct msix_entry),
GFP_KERNEL);
if (!adapter->msix_entries)
@@ -728,8 +716,12 @@ msi_only:
dev_info(&adapter->pdev->dev, "IOV Disabled\n");
}
#endif
+ adapter->vfs_allocated_count = 0;
+ adapter->rss_queues = 1;
+ adapter->flags |= IGB_FLAG_QUEUE_PAIRS;
adapter->num_rx_queues = 1;
adapter->num_tx_queues = 1;
+ adapter->num_q_vectors = 1;
if (!pci_enable_msi(adapter->pdev))
adapter->flags |= IGB_FLAG_HAS_MSI;
out:
@@ -739,6 +731,143 @@ out:
}
/**
+ * igb_alloc_q_vectors - Allocate memory for interrupt vectors
+ * @adapter: board private structure to initialize
+ *
+ * We allocate one q_vector per queue interrupt. If allocation fails we
+ * return -ENOMEM.
+ **/
+static int igb_alloc_q_vectors(struct igb_adapter *adapter)
+{
+ struct igb_q_vector *q_vector;
+ struct e1000_hw *hw = &adapter->hw;
+ int v_idx;
+
+ for (v_idx = 0; v_idx < adapter->num_q_vectors; v_idx++) {
+ q_vector = kzalloc(sizeof(struct igb_q_vector), GFP_KERNEL);
+ if (!q_vector)
+ goto err_out;
+ q_vector->adapter = adapter;
+ q_vector->itr_shift = (hw->mac.type == e1000_82575) ? 16 : 0;
+ q_vector->itr_register = hw->hw_addr + E1000_EITR(0);
+ q_vector->itr_val = IGB_START_ITR;
+ q_vector->set_itr = 1;
+ netif_napi_add(adapter->netdev, &q_vector->napi, igb_poll, 64);
+ adapter->q_vector[v_idx] = q_vector;
+ }
+ return 0;
+
+err_out:
+ while (v_idx) {
+ v_idx--;
+ q_vector = adapter->q_vector[v_idx];
+ netif_napi_del(&q_vector->napi);
+ kfree(q_vector);
+ adapter->q_vector[v_idx] = NULL;
+ }
+ return -ENOMEM;
+}
+
+static void igb_map_rx_ring_to_vector(struct igb_adapter *adapter,
+ int ring_idx, int v_idx)
+{
+ struct igb_q_vector *q_vector;
+
+ q_vector = adapter->q_vector[v_idx];
+ q_vector->rx_ring = &adapter->rx_ring[ring_idx];
+ q_vector->rx_ring->q_vector = q_vector;
+ q_vector->itr_val = adapter->rx_itr_setting;
+ if (q_vector->itr_val && q_vector->itr_val <= 3)
+ q_vector->itr_val = IGB_START_ITR;
+}
+
+static void igb_map_tx_ring_to_vector(struct igb_adapter *adapter,
+ int ring_idx, int v_idx)
+{
+ struct igb_q_vector *q_vector;
+
+ q_vector = adapter->q_vector[v_idx];
+ q_vector->tx_ring = &adapter->tx_ring[ring_idx];
+ q_vector->tx_ring->q_vector = q_vector;
+ q_vector->itr_val = adapter->tx_itr_setting;
+ if (q_vector->itr_val && q_vector->itr_val <= 3)
+ q_vector->itr_val = IGB_START_ITR;
+}
+
+/**
+ * igb_map_ring_to_vector - maps allocated queues to vectors
+ *
+ * This function maps the recently allocated queues to vectors.
+ **/
+static int igb_map_ring_to_vector(struct igb_adapter *adapter)
+{
+ int i;
+ int v_idx = 0;
+
+ if ((adapter->num_q_vectors < adapter->num_rx_queues) ||
+ (adapter->num_q_vectors < adapter->num_tx_queues))
+ return -ENOMEM;
+
+ if (adapter->num_q_vectors >=
+ (adapter->num_rx_queues + adapter->num_tx_queues)) {
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ igb_map_rx_ring_to_vector(adapter, i, v_idx++);
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ igb_map_tx_ring_to_vector(adapter, i, v_idx++);
+ } else {
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ if (i < adapter->num_tx_queues)
+ igb_map_tx_ring_to_vector(adapter, i, v_idx);
+ igb_map_rx_ring_to_vector(adapter, i, v_idx++);
+ }
+ for (; i < adapter->num_tx_queues; i++)
+ igb_map_tx_ring_to_vector(adapter, i, v_idx++);
+ }
+ return 0;
+}
+
+/**
+ * igb_init_interrupt_scheme - initialize interrupts, allocate queues/vectors
+ *
+ * This function initializes the interrupts and allocates all of the queues.
+ **/
+static int igb_init_interrupt_scheme(struct igb_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ int err;
+
+ igb_set_interrupt_capability(adapter);
+
+ err = igb_alloc_q_vectors(adapter);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate memory for vectors\n");
+ goto err_alloc_q_vectors;
+ }
+
+ err = igb_alloc_queues(adapter);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
+ goto err_alloc_queues;
+ }
+
+ err = igb_map_ring_to_vector(adapter);
+ if (err) {
+ dev_err(&pdev->dev, "Invalid q_vector to ring mapping\n");
+ goto err_map_queues;
+ }
+
+
+ return 0;
+err_map_queues:
+ igb_free_queues(adapter);
+err_alloc_queues:
+ igb_free_q_vectors(adapter);
+err_alloc_q_vectors:
+ igb_reset_interrupt_capability(adapter);
+ return err;
+}
+
+/**
* igb_request_irq - initialize interrupts
*
* Attempts to configure interrupts using the best available
@@ -747,6 +876,7 @@ out:
static int igb_request_irq(struct igb_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
+ struct pci_dev *pdev = adapter->pdev;
struct e1000_hw *hw = &adapter->hw;
int err = 0;
@@ -755,19 +885,38 @@ static int igb_request_irq(struct igb_adapter *adapter)
if (!err)
goto request_done;
/* fall back to MSI */
- igb_reset_interrupt_capability(adapter);
+ igb_clear_interrupt_scheme(adapter);
if (!pci_enable_msi(adapter->pdev))
adapter->flags |= IGB_FLAG_HAS_MSI;
igb_free_all_tx_resources(adapter);
igb_free_all_rx_resources(adapter);
+ adapter->num_tx_queues = 1;
adapter->num_rx_queues = 1;
- igb_alloc_queues(adapter);
+ adapter->num_q_vectors = 1;
+ err = igb_alloc_q_vectors(adapter);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Unable to allocate memory for vectors\n");
+ goto request_done;
+ }
+ err = igb_alloc_queues(adapter);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Unable to allocate memory for queues\n");
+ igb_free_q_vectors(adapter);
+ goto request_done;
+ }
+ igb_setup_all_tx_resources(adapter);
+ igb_setup_all_rx_resources(adapter);
} else {
switch (hw->mac.type) {
case e1000_82575:
wr32(E1000_MSIXBM(0),
- (E1000_EICR_RX_QUEUE0 | E1000_EIMS_OTHER));
+ (E1000_EICR_RX_QUEUE0 |
+ E1000_EICR_TX_QUEUE0 |
+ E1000_EIMS_OTHER));
break;
+ case e1000_82580:
case e1000_82576:
wr32(E1000_IVAR0, E1000_IVAR_VALID);
break;
@@ -777,17 +926,18 @@ static int igb_request_irq(struct igb_adapter *adapter)
}
if (adapter->flags & IGB_FLAG_HAS_MSI) {
- err = request_irq(adapter->pdev->irq, &igb_intr_msi, 0,
- netdev->name, netdev);
+ err = request_irq(adapter->pdev->irq, igb_intr_msi, 0,
+ netdev->name, adapter);
if (!err)
goto request_done;
+
/* fall back to legacy interrupts */
igb_reset_interrupt_capability(adapter);
adapter->flags &= ~IGB_FLAG_HAS_MSI;
}
- err = request_irq(adapter->pdev->irq, &igb_intr, IRQF_SHARED,
- netdev->name, netdev);
+ err = request_irq(adapter->pdev->irq, igb_intr, IRQF_SHARED,
+ netdev->name, adapter);
if (err)
dev_err(&adapter->pdev->dev, "Error %d getting interrupt\n",
@@ -799,23 +949,19 @@ request_done:
static void igb_free_irq(struct igb_adapter *adapter)
{
- struct net_device *netdev = adapter->netdev;
-
if (adapter->msix_entries) {
int vector = 0, i;
- for (i = 0; i < adapter->num_tx_queues; i++)
- free_irq(adapter->msix_entries[vector++].vector,
- &(adapter->tx_ring[i]));
- for (i = 0; i < adapter->num_rx_queues; i++)
- free_irq(adapter->msix_entries[vector++].vector,
- &(adapter->rx_ring[i]));
+ free_irq(adapter->msix_entries[vector++].vector, adapter);
- free_irq(adapter->msix_entries[vector++].vector, netdev);
- return;
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ free_irq(adapter->msix_entries[vector++].vector,
+ q_vector);
+ }
+ } else {
+ free_irq(adapter->pdev->irq, adapter);
}
-
- free_irq(adapter->pdev->irq, netdev);
}
/**
@@ -826,6 +972,11 @@ static void igb_irq_disable(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
+ /*
+ * we need to be careful when disabling interrupts. The VFs are also
+ * mapped into these registers and so clearing the bits can cause
+ * issues on the VF drivers so we only need to clear what we set
+ */
if (adapter->msix_entries) {
u32 regval = rd32(E1000_EIAM);
wr32(E1000_EIAM, regval & ~adapter->eims_enable_mask);
@@ -849,41 +1000,47 @@ static void igb_irq_enable(struct igb_adapter *adapter)
struct e1000_hw *hw = &adapter->hw;
if (adapter->msix_entries) {
+ u32 ims = E1000_IMS_LSC | E1000_IMS_DOUTSYNC;
u32 regval = rd32(E1000_EIAC);
wr32(E1000_EIAC, regval | adapter->eims_enable_mask);
regval = rd32(E1000_EIAM);
wr32(E1000_EIAM, regval | adapter->eims_enable_mask);
wr32(E1000_EIMS, adapter->eims_enable_mask);
- if (adapter->vfs_allocated_count)
+ if (adapter->vfs_allocated_count) {
wr32(E1000_MBVFIMR, 0xFF);
- wr32(E1000_IMS, (E1000_IMS_LSC | E1000_IMS_VMMB |
- E1000_IMS_DOUTSYNC));
+ ims |= E1000_IMS_VMMB;
+ }
+ if (adapter->hw.mac.type == e1000_82580)
+ ims |= E1000_IMS_DRSTA;
+
+ wr32(E1000_IMS, ims);
} else {
- wr32(E1000_IMS, IMS_ENABLE_MASK);
- wr32(E1000_IAM, IMS_ENABLE_MASK);
+ wr32(E1000_IMS, IMS_ENABLE_MASK |
+ E1000_IMS_DRSTA);
+ wr32(E1000_IAM, IMS_ENABLE_MASK |
+ E1000_IMS_DRSTA);
}
}
static void igb_update_mng_vlan(struct igb_adapter *adapter)
{
- struct net_device *netdev = adapter->netdev;
+ struct e1000_hw *hw = &adapter->hw;
u16 vid = adapter->hw.mng_cookie.vlan_id;
u16 old_vid = adapter->mng_vlan_id;
- if (adapter->vlgrp) {
- if (!vlan_group_get_device(adapter->vlgrp, vid)) {
- if (adapter->hw.mng_cookie.status &
- E1000_MNG_DHCP_COOKIE_STATUS_VLAN) {
- igb_vlan_rx_add_vid(netdev, vid);
- adapter->mng_vlan_id = vid;
- } else
- adapter->mng_vlan_id = IGB_MNG_VLAN_NONE;
- if ((old_vid != (u16)IGB_MNG_VLAN_NONE) &&
- (vid != old_vid) &&
- !vlan_group_get_device(adapter->vlgrp, old_vid))
- igb_vlan_rx_kill_vid(netdev, old_vid);
- } else
- adapter->mng_vlan_id = vid;
+ if (hw->mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN) {
+ /* add VID to filter table */
+ igb_vfta_set(hw, vid, true);
+ adapter->mng_vlan_id = vid;
+ } else {
+ adapter->mng_vlan_id = IGB_MNG_VLAN_NONE;
+ }
+
+ if ((old_vid != (u16)IGB_MNG_VLAN_NONE) &&
+ (vid != old_vid) &&
+ !vlan_group_get_device(adapter->vlgrp, old_vid)) {
+ /* remove VID from filter table */
+ igb_vfta_set(hw, old_vid, false);
}
}
@@ -907,7 +1064,6 @@ static void igb_release_hw_control(struct igb_adapter *adapter)
ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
}
-
/**
* igb_get_hw_control - get control of the h/w from f/w
* @adapter: address of board private structure
@@ -942,8 +1098,11 @@ static void igb_configure(struct igb_adapter *adapter)
igb_restore_vlan(adapter);
- igb_configure_tx(adapter);
+ igb_setup_tctl(adapter);
+ igb_setup_mrqc(adapter);
igb_setup_rctl(adapter);
+
+ igb_configure_tx(adapter);
igb_configure_rx(adapter);
igb_rx_fifo_flush_82575(&adapter->hw);
@@ -965,7 +1124,6 @@ static void igb_configure(struct igb_adapter *adapter)
* igb_up - Open the interface and prepare it to handle traffic
* @adapter: board private structure
**/
-
int igb_up(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
@@ -976,30 +1134,37 @@ int igb_up(struct igb_adapter *adapter)
clear_bit(__IGB_DOWN, &adapter->state);
- for (i = 0; i < adapter->num_rx_queues; i++)
- napi_enable(&adapter->rx_ring[i].napi);
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ napi_enable(&q_vector->napi);
+ }
if (adapter->msix_entries)
igb_configure_msix(adapter);
- igb_vmm_control(adapter);
- igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0);
- igb_set_vmolr(hw, adapter->vfs_allocated_count);
-
/* Clear any pending interrupts. */
rd32(E1000_ICR);
igb_irq_enable(adapter);
+ /* notify VFs that reset has been completed */
+ if (adapter->vfs_allocated_count) {
+ u32 reg_data = rd32(E1000_CTRL_EXT);
+ reg_data |= E1000_CTRL_EXT_PFRSTD;
+ wr32(E1000_CTRL_EXT, reg_data);
+ }
+
netif_tx_start_all_queues(adapter->netdev);
- /* Fire a link change interrupt to start the watchdog. */
- wr32(E1000_ICS, E1000_ICS_LSC);
+ /* start the watchdog. */
+ hw->mac.get_link_status = 1;
+ schedule_work(&adapter->watchdog_task);
+
return 0;
}
void igb_down(struct igb_adapter *adapter)
{
- struct e1000_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
+ struct e1000_hw *hw = &adapter->hw;
u32 tctl, rctl;
int i;
@@ -1022,8 +1187,10 @@ void igb_down(struct igb_adapter *adapter)
wrfl();
msleep(10);
- for (i = 0; i < adapter->num_rx_queues; i++)
- napi_disable(&adapter->rx_ring[i].napi);
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ napi_disable(&q_vector->napi);
+ }
igb_irq_disable(adapter);
@@ -1062,6 +1229,7 @@ void igb_reinit_locked(struct igb_adapter *adapter)
void igb_reset(struct igb_adapter *adapter)
{
+ struct pci_dev *pdev = adapter->pdev;
struct e1000_hw *hw = &adapter->hw;
struct e1000_mac_info *mac = &hw->mac;
struct e1000_fc_info *fc = &hw->fc;
@@ -1072,8 +1240,13 @@ void igb_reset(struct igb_adapter *adapter)
* To take effect CTRL.RST is required.
*/
switch (mac->type) {
+ case e1000_82580:
+ pba = rd32(E1000_RXPBS);
+ pba = igb_rxpbs_adjust_82580(pba);
+ break;
case e1000_82576:
- pba = E1000_PBA_64K;
+ pba = rd32(E1000_RXPBS);
+ pba &= E1000_RXPBS_SIZE_MASK_82576;
break;
case e1000_82575:
default:
@@ -1148,10 +1321,10 @@ void igb_reset(struct igb_adapter *adapter)
if (adapter->vfs_allocated_count) {
int i;
for (i = 0 ; i < adapter->vfs_allocated_count; i++)
- adapter->vf_data[i].clear_to_send = false;
+ adapter->vf_data[i].flags = 0;
/* ping all the active vfs to let them know we are going down */
- igb_ping_all_vfs(adapter);
+ igb_ping_all_vfs(adapter);
/* disable transmits and receives */
wr32(E1000_VFRE, 0);
@@ -1159,23 +1332,28 @@ void igb_reset(struct igb_adapter *adapter)
}
/* Allow time for pending master requests to run */
- adapter->hw.mac.ops.reset_hw(&adapter->hw);
+ hw->mac.ops.reset_hw(hw);
wr32(E1000_WUC, 0);
- if (adapter->hw.mac.ops.init_hw(&adapter->hw))
- dev_err(&adapter->pdev->dev, "Hardware Error\n");
+ if (hw->mac.ops.init_hw(hw))
+ dev_err(&pdev->dev, "Hardware Error\n");
+ if (hw->mac.type == e1000_82580) {
+ u32 reg = rd32(E1000_PCIEMISC);
+ wr32(E1000_PCIEMISC,
+ reg & ~E1000_PCIEMISC_LX_DECISION);
+ }
igb_update_mng_vlan(adapter);
/* Enable h/w to recognize an 802.1Q VLAN Ethernet packet */
wr32(E1000_VET, ETHERNET_IEEE_VLAN_TYPE);
- igb_reset_adaptive(&adapter->hw);
- igb_get_phy_info(&adapter->hw);
+ igb_reset_adaptive(hw);
+ igb_get_phy_info(hw);
}
static const struct net_device_ops igb_netdev_ops = {
- .ndo_open = igb_open,
+ .ndo_open = igb_open,
.ndo_stop = igb_close,
.ndo_start_xmit = igb_xmit_frame_adv,
.ndo_get_stats = igb_get_stats,
@@ -1211,10 +1389,11 @@ static int __devinit igb_probe(struct pci_dev *pdev,
struct net_device *netdev;
struct igb_adapter *adapter;
struct e1000_hw *hw;
+ u16 eeprom_data = 0;
+ static int global_quad_port_a; /* global quad port a indication */
const struct e1000_info *ei = igb_info_tbl[ent->driver_data];
unsigned long mmio_start, mmio_len;
int err, pci_using_dac;
- u16 eeprom_data = 0;
u16 eeprom_apme_mask = IGB_EEPROM_APME;
u32 part_num;
@@ -1291,8 +1470,6 @@ static int __devinit igb_probe(struct pci_dev *pdev,
hw->subsystem_vendor_id = pdev->subsystem_vendor;
hw->subsystem_device_id = pdev->subsystem_device;
- /* setup the private structure */
- hw->back = adapter;
/* Copy the default MAC, PHY and NVM function pointers */
memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops));
memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops));
@@ -1302,46 +1479,6 @@ static int __devinit igb_probe(struct pci_dev *pdev,
if (err)
goto err_sw_init;
-#ifdef CONFIG_PCI_IOV
- /* since iov functionality isn't critical to base device function we
- * can accept failure. If it fails we don't allow iov to be enabled */
- if (hw->mac.type == e1000_82576) {
- /* 82576 supports a maximum of 7 VFs in addition to the PF */
- unsigned int num_vfs = (max_vfs > 7) ? 7 : max_vfs;
- int i;
- unsigned char mac_addr[ETH_ALEN];
-
- if (num_vfs) {
- adapter->vf_data = kcalloc(num_vfs,
- sizeof(struct vf_data_storage),
- GFP_KERNEL);
- if (!adapter->vf_data) {
- dev_err(&pdev->dev,
- "Could not allocate VF private data - "
- "IOV enable failed\n");
- } else {
- err = pci_enable_sriov(pdev, num_vfs);
- if (!err) {
- adapter->vfs_allocated_count = num_vfs;
- dev_info(&pdev->dev,
- "%d vfs allocated\n",
- num_vfs);
- for (i = 0;
- i < adapter->vfs_allocated_count;
- i++) {
- random_ether_addr(mac_addr);
- igb_set_vf_mac(adapter, i,
- mac_addr);
- }
- } else {
- kfree(adapter->vf_data);
- adapter->vf_data = NULL;
- }
- }
- }
- }
-
-#endif
/* setup the private structure */
err = igb_sw_init(adapter);
if (err)
@@ -1349,16 +1486,6 @@ static int __devinit igb_probe(struct pci_dev *pdev,
igb_get_bus_info_pcie(hw);
- /* set flags */
- switch (hw->mac.type) {
- case e1000_82575:
- adapter->flags |= IGB_FLAG_NEED_CTX_IDX;
- break;
- case e1000_82576:
- default:
- break;
- }
-
hw->phy.autoneg_wait_to_complete = false;
hw->mac.adaptive_ifs = true;
@@ -1382,7 +1509,6 @@ static int __devinit igb_probe(struct pci_dev *pdev,
netdev->features |= NETIF_F_IPV6_CSUM;
netdev->features |= NETIF_F_TSO;
netdev->features |= NETIF_F_TSO6;
-
netdev->features |= NETIF_F_GRO;
netdev->vlan_features |= NETIF_F_TSO;
@@ -1394,10 +1520,10 @@ static int __devinit igb_probe(struct pci_dev *pdev,
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
- if (adapter->hw.mac.type == e1000_82576)
+ if (hw->mac.type >= e1000_82576)
netdev->features |= NETIF_F_SCTP_CSUM;
- adapter->en_mng_pt = igb_enable_mng_pass_thru(&adapter->hw);
+ adapter->en_mng_pt = igb_enable_mng_pass_thru(hw);
/* before reading the NVM, reset the controller to put the device in a
* known good starting state */
@@ -1439,9 +1565,6 @@ static int __devinit igb_probe(struct pci_dev *pdev,
hw->fc.requested_mode = e1000_fc_default;
hw->fc.current_mode = e1000_fc_default;
- adapter->itr_setting = IGB_DEFAULT_ITR;
- adapter->itr = IGB_START_ITR;
-
igb_validate_mdi_setting(hw);
/* Initial Wake on LAN setting If APM wake is enabled in the EEPROM,
@@ -1450,6 +1573,10 @@ static int __devinit igb_probe(struct pci_dev *pdev,
if (hw->bus.func == 0)
hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
+ else if (hw->mac.type == e1000_82580)
+ hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A +
+ NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1,
+ &eeprom_data);
else if (hw->bus.func == 1)
hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
@@ -1508,66 +1635,14 @@ static int __devinit igb_probe(struct pci_dev *pdev,
dev_info(&pdev->dev, "DCA enabled\n");
igb_setup_dca(adapter);
}
-#endif
-
- /*
- * Initialize hardware timer: we keep it running just in case
- * that some program needs it later on.
- */
- memset(&adapter->cycles, 0, sizeof(adapter->cycles));
- adapter->cycles.read = igb_read_clock;
- adapter->cycles.mask = CLOCKSOURCE_MASK(64);
- adapter->cycles.mult = 1;
- adapter->cycles.shift = IGB_TSYNC_SHIFT;
- wr32(E1000_TIMINCA,
- (1<<24) |
- IGB_TSYNC_CYCLE_TIME_IN_NANOSECONDS * IGB_TSYNC_SCALE);
-#if 0
- /*
- * Avoid rollover while we initialize by resetting the time counter.
- */
- wr32(E1000_SYSTIML, 0x00000000);
- wr32(E1000_SYSTIMH, 0x00000000);
-#else
- /*
- * Set registers so that rollover occurs soon to test this.
- */
- wr32(E1000_SYSTIML, 0x00000000);
- wr32(E1000_SYSTIMH, 0xFF800000);
-#endif
- wrfl();
- timecounter_init(&adapter->clock,
- &adapter->cycles,
- ktime_to_ns(ktime_get_real()));
- /*
- * Synchronize our NIC clock against system wall clock. NIC
- * time stamp reading requires ~3us per sample, each sample
- * was pretty stable even under load => only require 10
- * samples for each offset comparison.
- */
- memset(&adapter->compare, 0, sizeof(adapter->compare));
- adapter->compare.source = &adapter->clock;
- adapter->compare.target = ktime_get_real;
- adapter->compare.num_samples = 10;
- timecompare_update(&adapter->compare, 0);
-
-#ifdef DEBUG
- {
- char buffer[160];
- printk(KERN_DEBUG
- "igb: %s: hw %p initialized timer\n",
- igb_get_time_str(adapter, buffer),
- &adapter->hw);
- }
#endif
-
dev_info(&pdev->dev, "Intel(R) Gigabit Ethernet Network Connection\n");
/* print bus type/speed/width info */
dev_info(&pdev->dev, "%s: (PCIe:%s:%s) %pM\n",
netdev->name,
- ((hw->bus.speed == e1000_bus_speed_2500)
- ? "2.5Gb/s" : "unknown"),
+ ((hw->bus.speed == e1000_bus_speed_2500) ? "2.5Gb/s" :
+ "unknown"),
((hw->bus.width == e1000_bus_width_pcie_x4) ? "Width x4" :
(hw->bus.width == e1000_bus_width_pcie_x2) ? "Width x2" :
(hw->bus.width == e1000_bus_width_pcie_x1) ? "Width x1" :
@@ -1594,15 +1669,14 @@ err_eeprom:
if (hw->flash_address)
iounmap(hw->flash_address);
-
- igb_free_queues(adapter);
err_sw_init:
+ igb_clear_interrupt_scheme(adapter);
iounmap(hw->hw_addr);
err_ioremap:
free_netdev(netdev);
err_alloc_etherdev:
- pci_release_selected_regions(pdev, pci_select_bars(pdev,
- IORESOURCE_MEM));
+ pci_release_selected_regions(pdev,
+ pci_select_bars(pdev, IORESOURCE_MEM));
err_pci_reg:
err_dma:
pci_disable_device(pdev);
@@ -1647,12 +1721,10 @@ static void __devexit igb_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
- if (!igb_check_reset_block(&adapter->hw))
- igb_reset_phy(&adapter->hw);
-
- igb_reset_interrupt_capability(adapter);
+ if (!igb_check_reset_block(hw))
+ igb_reset_phy(hw);
- igb_free_queues(adapter);
+ igb_clear_interrupt_scheme(adapter);
#ifdef CONFIG_PCI_IOV
/* reclaim resources allocated to VFs */
@@ -1668,11 +1740,12 @@ static void __devexit igb_remove(struct pci_dev *pdev)
dev_info(&pdev->dev, "IOV Disabled\n");
}
#endif
+
iounmap(hw->hw_addr);
if (hw->flash_address)
iounmap(hw->flash_address);
- pci_release_selected_regions(pdev, pci_select_bars(pdev,
- IORESOURCE_MEM));
+ pci_release_selected_regions(pdev,
+ pci_select_bars(pdev, IORESOURCE_MEM));
free_netdev(netdev);
@@ -1682,6 +1755,160 @@ static void __devexit igb_remove(struct pci_dev *pdev)
}
/**
+ * igb_probe_vfs - Initialize vf data storage and add VFs to pci config space
+ * @adapter: board private structure to initialize
+ *
+ * This function initializes the vf specific data storage and then attempts to
+ * allocate the VFs. The reason for ordering it this way is because it is much
+ * mor expensive time wise to disable SR-IOV than it is to allocate and free
+ * the memory for the VFs.
+ **/
+static void __devinit igb_probe_vfs(struct igb_adapter * adapter)
+{
+#ifdef CONFIG_PCI_IOV
+ struct pci_dev *pdev = adapter->pdev;
+
+ if (adapter->vfs_allocated_count > 7)
+ adapter->vfs_allocated_count = 7;
+
+ if (adapter->vfs_allocated_count) {
+ adapter->vf_data = kcalloc(adapter->vfs_allocated_count,
+ sizeof(struct vf_data_storage),
+ GFP_KERNEL);
+ /* if allocation failed then we do not support SR-IOV */
+ if (!adapter->vf_data) {
+ adapter->vfs_allocated_count = 0;
+ dev_err(&pdev->dev, "Unable to allocate memory for VF "
+ "Data Storage\n");
+ }
+ }
+
+ if (pci_enable_sriov(pdev, adapter->vfs_allocated_count)) {
+ kfree(adapter->vf_data);
+ adapter->vf_data = NULL;
+#endif /* CONFIG_PCI_IOV */
+ adapter->vfs_allocated_count = 0;
+#ifdef CONFIG_PCI_IOV
+ } else {
+ unsigned char mac_addr[ETH_ALEN];
+ int i;
+ dev_info(&pdev->dev, "%d vfs allocated\n",
+ adapter->vfs_allocated_count);
+ for (i = 0; i < adapter->vfs_allocated_count; i++) {
+ random_ether_addr(mac_addr);
+ igb_set_vf_mac(adapter, i, mac_addr);
+ }
+ }
+#endif /* CONFIG_PCI_IOV */
+}
+
+
+/**
+ * igb_init_hw_timer - Initialize hardware timer used with IEEE 1588 timestamp
+ * @adapter: board private structure to initialize
+ *
+ * igb_init_hw_timer initializes the function pointer and values for the hw
+ * timer found in hardware.
+ **/
+static void igb_init_hw_timer(struct igb_adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+
+ switch (hw->mac.type) {
+ case e1000_82580:
+ memset(&adapter->cycles, 0, sizeof(adapter->cycles));
+ adapter->cycles.read = igb_read_clock;
+ adapter->cycles.mask = CLOCKSOURCE_MASK(64);
+ adapter->cycles.mult = 1;
+ /*
+ * The 82580 timesync updates the system timer every 8ns by 8ns
+ * and the value cannot be shifted. Instead we need to shift
+ * the registers to generate a 64bit timer value. As a result
+ * SYSTIMR/L/H, TXSTMPL/H, RXSTMPL/H all have to be shifted by
+ * 24 in order to generate a larger value for synchronization.
+ */
+ adapter->cycles.shift = IGB_82580_TSYNC_SHIFT;
+ /* disable system timer temporarily by setting bit 31 */
+ wr32(E1000_TSAUXC, 0x80000000);
+ wrfl();
+
+ /* Set registers so that rollover occurs soon to test this. */
+ wr32(E1000_SYSTIMR, 0x00000000);
+ wr32(E1000_SYSTIML, 0x80000000);
+ wr32(E1000_SYSTIMH, 0x000000FF);
+ wrfl();
+
+ /* enable system timer by clearing bit 31 */
+ wr32(E1000_TSAUXC, 0x0);
+ wrfl();
+
+ timecounter_init(&adapter->clock,
+ &adapter->cycles,
+ ktime_to_ns(ktime_get_real()));
+ /*
+ * Synchronize our NIC clock against system wall clock. NIC
+ * time stamp reading requires ~3us per sample, each sample
+ * was pretty stable even under load => only require 10
+ * samples for each offset comparison.
+ */
+ memset(&adapter->compare, 0, sizeof(adapter->compare));
+ adapter->compare.source = &adapter->clock;
+ adapter->compare.target = ktime_get_real;
+ adapter->compare.num_samples = 10;
+ timecompare_update(&adapter->compare, 0);
+ break;
+ case e1000_82576:
+ /*
+ * Initialize hardware timer: we keep it running just in case
+ * that some program needs it later on.
+ */
+ memset(&adapter->cycles, 0, sizeof(adapter->cycles));
+ adapter->cycles.read = igb_read_clock;
+ adapter->cycles.mask = CLOCKSOURCE_MASK(64);
+ adapter->cycles.mult = 1;
+ /**
+ * Scale the NIC clock cycle by a large factor so that
+ * relatively small clock corrections can be added or
+ * substracted at each clock tick. The drawbacks of a large
+ * factor are a) that the clock register overflows more quickly
+ * (not such a big deal) and b) that the increment per tick has
+ * to fit into 24 bits. As a result we need to use a shift of
+ * 19 so we can fit a value of 16 into the TIMINCA register.
+ */
+ adapter->cycles.shift = IGB_82576_TSYNC_SHIFT;
+ wr32(E1000_TIMINCA,
+ (1 << E1000_TIMINCA_16NS_SHIFT) |
+ (16 << IGB_82576_TSYNC_SHIFT));
+
+ /* Set registers so that rollover occurs soon to test this. */
+ wr32(E1000_SYSTIML, 0x00000000);
+ wr32(E1000_SYSTIMH, 0xFF800000);
+ wrfl();
+
+ timecounter_init(&adapter->clock,
+ &adapter->cycles,
+ ktime_to_ns(ktime_get_real()));
+ /*
+ * Synchronize our NIC clock against system wall clock. NIC
+ * time stamp reading requires ~3us per sample, each sample
+ * was pretty stable even under load => only require 10
+ * samples for each offset comparison.
+ */
+ memset(&adapter->compare, 0, sizeof(adapter->compare));
+ adapter->compare.source = &adapter->clock;
+ adapter->compare.target = ktime_get_real;
+ adapter->compare.num_samples = 10;
+ timecompare_update(&adapter->compare, 0);
+ break;
+ case e1000_82575:
+ /* 82575 does not support timesync */
+ default:
+ break;
+ }
+
+}
+
+/**
* igb_sw_init - Initialize general software structures (struct igb_adapter)
* @adapter: board private structure to initialize
*
@@ -1699,20 +1926,37 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter)
adapter->tx_ring_count = IGB_DEFAULT_TXD;
adapter->rx_ring_count = IGB_DEFAULT_RXD;
- adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
- adapter->rx_ps_hdr_size = 0; /* disable packet split */
+ adapter->rx_itr_setting = IGB_DEFAULT_ITR;
+ adapter->tx_itr_setting = IGB_DEFAULT_ITR;
+
adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN;
- /* This call may decrease the number of queues depending on
- * interrupt mode. */
- igb_set_interrupt_capability(adapter);
+#ifdef CONFIG_PCI_IOV
+ if (hw->mac.type == e1000_82576)
+ adapter->vfs_allocated_count = max_vfs;
- if (igb_alloc_queues(adapter)) {
+#endif /* CONFIG_PCI_IOV */
+ adapter->rss_queues = min_t(u32, IGB_MAX_RX_QUEUES, num_online_cpus());
+
+ /*
+ * if rss_queues > 4 or vfs are going to be allocated with rss_queues
+ * then we should combine the queues into a queue pair in order to
+ * conserve interrupts due to limited supply
+ */
+ if ((adapter->rss_queues > 4) ||
+ ((adapter->rss_queues > 1) && (adapter->vfs_allocated_count > 6)))
+ adapter->flags |= IGB_FLAG_QUEUE_PAIRS;
+
+ /* This call may decrease the number of queues */
+ if (igb_init_interrupt_scheme(adapter)) {
dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
return -ENOMEM;
}
+ igb_init_hw_timer(adapter);
+ igb_probe_vfs(adapter);
+
/* Explicitly disable IRQ since the NIC can be in any state. */
igb_irq_disable(adapter);
@@ -1757,21 +2001,12 @@ static int igb_open(struct net_device *netdev)
/* e1000_power_up_phy(adapter); */
- adapter->mng_vlan_id = IGB_MNG_VLAN_NONE;
- if ((adapter->hw.mng_cookie.status &
- E1000_MNG_DHCP_COOKIE_STATUS_VLAN))
- igb_update_mng_vlan(adapter);
-
/* before we allocate an interrupt, we must be ready to handle it.
* Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt
* as soon as we call pci_request_irq, so we have to setup our
* clean_rx handler before we do so. */
igb_configure(adapter);
- igb_vmm_control(adapter);
- igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0);
- igb_set_vmolr(hw, adapter->vfs_allocated_count);
-
err = igb_request_irq(adapter);
if (err)
goto err_req_irq;
@@ -1779,18 +2014,28 @@ static int igb_open(struct net_device *netdev)
/* From here on the code is the same as igb_up() */
clear_bit(__IGB_DOWN, &adapter->state);
- for (i = 0; i < adapter->num_rx_queues; i++)
- napi_enable(&adapter->rx_ring[i].napi);
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ napi_enable(&q_vector->napi);
+ }
/* Clear any pending interrupts. */
rd32(E1000_ICR);
igb_irq_enable(adapter);
+ /* notify VFs that reset has been completed */
+ if (adapter->vfs_allocated_count) {
+ u32 reg_data = rd32(E1000_CTRL_EXT);
+ reg_data |= E1000_CTRL_EXT_PFRSTD;
+ wr32(E1000_CTRL_EXT, reg_data);
+ }
+
netif_tx_start_all_queues(netdev);
- /* Fire a link status change interrupt to start the watchdog. */
- wr32(E1000_ICS, E1000_ICS_LSC);
+ /* start the watchdog. */
+ hw->mac.get_link_status = 1;
+ schedule_work(&adapter->watchdog_task);
return 0;
@@ -1829,28 +2074,18 @@ static int igb_close(struct net_device *netdev)
igb_free_all_tx_resources(adapter);
igb_free_all_rx_resources(adapter);
- /* kill manageability vlan ID if supported, but not if a vlan with
- * the same ID is registered on the host OS (let 8021q kill it) */
- if ((adapter->hw.mng_cookie.status &
- E1000_MNG_DHCP_COOKIE_STATUS_VLAN) &&
- !(adapter->vlgrp &&
- vlan_group_get_device(adapter->vlgrp, adapter->mng_vlan_id)))
- igb_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
-
return 0;
}
/**
* igb_setup_tx_resources - allocate Tx resources (Descriptors)
- * @adapter: board private structure
* @tx_ring: tx descriptor ring (for a specific queue) to setup
*
* Return 0 on success, negative on failure
**/
-int igb_setup_tx_resources(struct igb_adapter *adapter,
- struct igb_ring *tx_ring)
+int igb_setup_tx_resources(struct igb_ring *tx_ring)
{
- struct pci_dev *pdev = adapter->pdev;
+ struct pci_dev *pdev = tx_ring->pdev;
int size;
size = sizeof(struct igb_buffer) * tx_ring->count;
@@ -1863,20 +2098,20 @@ int igb_setup_tx_resources(struct igb_adapter *adapter,
tx_ring->size = tx_ring->count * sizeof(union e1000_adv_tx_desc);
tx_ring->size = ALIGN(tx_ring->size, 4096);
- tx_ring->desc = pci_alloc_consistent(pdev, tx_ring->size,
+ tx_ring->desc = pci_alloc_consistent(pdev,
+ tx_ring->size,
&tx_ring->dma);
if (!tx_ring->desc)
goto err;
- tx_ring->adapter = adapter;
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
return 0;
err:
vfree(tx_ring->buffer_info);
- dev_err(&adapter->pdev->dev,
+ dev_err(&pdev->dev,
"Unable to allocate memory for the transmit descriptor ring\n");
return -ENOMEM;
}
@@ -1890,13 +2125,13 @@ err:
**/
static int igb_setup_all_tx_resources(struct igb_adapter *adapter)
{
+ struct pci_dev *pdev = adapter->pdev;
int i, err = 0;
- int r_idx;
for (i = 0; i < adapter->num_tx_queues; i++) {
- err = igb_setup_tx_resources(adapter, &adapter->tx_ring[i]);
+ err = igb_setup_tx_resources(&adapter->tx_ring[i]);
if (err) {
- dev_err(&adapter->pdev->dev,
+ dev_err(&pdev->dev,
"Allocation for Tx Queue %u failed\n", i);
for (i--; i >= 0; i--)
igb_free_tx_resources(&adapter->tx_ring[i]);
@@ -1904,57 +2139,24 @@ static int igb_setup_all_tx_resources(struct igb_adapter *adapter)
}
}
- for (i = 0; i < IGB_MAX_TX_QUEUES; i++) {
- r_idx = i % adapter->num_tx_queues;
+ for (i = 0; i < IGB_ABS_MAX_TX_QUEUES; i++) {
+ int r_idx = i % adapter->num_tx_queues;
adapter->multi_tx_table[i] = &adapter->tx_ring[r_idx];
}
return err;
}
/**
- * igb_configure_tx - Configure transmit Unit after Reset
- * @adapter: board private structure
- *
- * Configure the Tx unit of the MAC after a reset.
+ * igb_setup_tctl - configure the transmit control registers
+ * @adapter: Board private structure
**/
-static void igb_configure_tx(struct igb_adapter *adapter)
+void igb_setup_tctl(struct igb_adapter *adapter)
{
- u64 tdba;
struct e1000_hw *hw = &adapter->hw;
u32 tctl;
- u32 txdctl, txctrl;
- int i, j;
-
- for (i = 0; i < adapter->num_tx_queues; i++) {
- struct igb_ring *ring = &adapter->tx_ring[i];
- j = ring->reg_idx;
- wr32(E1000_TDLEN(j),
- ring->count * sizeof(union e1000_adv_tx_desc));
- tdba = ring->dma;
- wr32(E1000_TDBAL(j),
- tdba & 0x00000000ffffffffULL);
- wr32(E1000_TDBAH(j), tdba >> 32);
-
- ring->head = E1000_TDH(j);
- ring->tail = E1000_TDT(j);
- writel(0, hw->hw_addr + ring->tail);
- writel(0, hw->hw_addr + ring->head);
- txdctl = rd32(E1000_TXDCTL(j));
- txdctl |= E1000_TXDCTL_QUEUE_ENABLE;
- wr32(E1000_TXDCTL(j), txdctl);
-
- /* Turn off Relaxed Ordering on head write-backs. The
- * writebacks MUST be delivered in order or it will
- * completely screw up our bookeeping.
- */
- txctrl = rd32(E1000_DCA_TXCTRL(j));
- txctrl &= ~E1000_DCA_TXCTRL_TX_WB_RO_EN;
- wr32(E1000_DCA_TXCTRL(j), txctrl);
- }
- /* disable queue 0 to prevent tail bump w/o re-configuration */
- if (adapter->vfs_allocated_count)
- wr32(E1000_TXDCTL(0), 0);
+ /* disable queue 0 which is enabled by default on 82575 and 82576 */
+ wr32(E1000_TXDCTL(0), 0);
/* Program the Transmit Control Register */
tctl = rd32(E1000_TCTL);
@@ -1964,9 +2166,6 @@ static void igb_configure_tx(struct igb_adapter *adapter)
igb_config_collision_dist(hw);
- /* Setup Transmit Descriptor Settings for eop descriptor */
- adapter->txd_cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_RS;
-
/* Enable transmits */
tctl |= E1000_TCTL_EN;
@@ -1974,16 +2173,69 @@ static void igb_configure_tx(struct igb_adapter *adapter)
}
/**
- * igb_setup_rx_resources - allocate Rx resources (Descriptors)
+ * igb_configure_tx_ring - Configure transmit ring after Reset
+ * @adapter: board private structure
+ * @ring: tx ring to configure
+ *
+ * Configure a transmit ring after a reset.
+ **/
+void igb_configure_tx_ring(struct igb_adapter *adapter,
+ struct igb_ring *ring)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 txdctl;
+ u64 tdba = ring->dma;
+ int reg_idx = ring->reg_idx;
+
+ /* disable the queue */
+ txdctl = rd32(E1000_TXDCTL(reg_idx));
+ wr32(E1000_TXDCTL(reg_idx),
+ txdctl & ~E1000_TXDCTL_QUEUE_ENABLE);
+ wrfl();
+ mdelay(10);
+
+ wr32(E1000_TDLEN(reg_idx),
+ ring->count * sizeof(union e1000_adv_tx_desc));
+ wr32(E1000_TDBAL(reg_idx),
+ tdba & 0x00000000ffffffffULL);
+ wr32(E1000_TDBAH(reg_idx), tdba >> 32);
+
+ ring->head = hw->hw_addr + E1000_TDH(reg_idx);
+ ring->tail = hw->hw_addr + E1000_TDT(reg_idx);
+ writel(0, ring->head);
+ writel(0, ring->tail);
+
+ txdctl |= IGB_TX_PTHRESH;
+ txdctl |= IGB_TX_HTHRESH << 8;
+ txdctl |= IGB_TX_WTHRESH << 16;
+
+ txdctl |= E1000_TXDCTL_QUEUE_ENABLE;
+ wr32(E1000_TXDCTL(reg_idx), txdctl);
+}
+
+/**
+ * igb_configure_tx - Configure transmit Unit after Reset
* @adapter: board private structure
+ *
+ * Configure the Tx unit of the MAC after a reset.
+ **/
+static void igb_configure_tx(struct igb_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ igb_configure_tx_ring(adapter, &adapter->tx_ring[i]);
+}
+
+/**
+ * igb_setup_rx_resources - allocate Rx resources (Descriptors)
* @rx_ring: rx descriptor ring (for a specific queue) to setup
*
* Returns 0 on success, negative on failure
**/
-int igb_setup_rx_resources(struct igb_adapter *adapter,
- struct igb_ring *rx_ring)
+int igb_setup_rx_resources(struct igb_ring *rx_ring)
{
- struct pci_dev *pdev = adapter->pdev;
+ struct pci_dev *pdev = rx_ring->pdev;
int size, desc_len;
size = sizeof(struct igb_buffer) * rx_ring->count;
@@ -2007,13 +2259,12 @@ int igb_setup_rx_resources(struct igb_adapter *adapter,
rx_ring->next_to_clean = 0;
rx_ring->next_to_use = 0;
- rx_ring->adapter = adapter;
-
return 0;
err:
vfree(rx_ring->buffer_info);
- dev_err(&adapter->pdev->dev, "Unable to allocate memory for "
+ rx_ring->buffer_info = NULL;
+ dev_err(&pdev->dev, "Unable to allocate memory for "
"the receive descriptor ring\n");
return -ENOMEM;
}
@@ -2027,12 +2278,13 @@ err:
**/
static int igb_setup_all_rx_resources(struct igb_adapter *adapter)
{
+ struct pci_dev *pdev = adapter->pdev;
int i, err = 0;
for (i = 0; i < adapter->num_rx_queues; i++) {
- err = igb_setup_rx_resources(adapter, &adapter->rx_ring[i]);
+ err = igb_setup_rx_resources(&adapter->rx_ring[i]);
if (err) {
- dev_err(&adapter->pdev->dev,
+ dev_err(&pdev->dev,
"Allocation for Rx Queue %u failed\n", i);
for (i--; i >= 0; i--)
igb_free_rx_resources(&adapter->rx_ring[i]);
@@ -2044,15 +2296,122 @@ static int igb_setup_all_rx_resources(struct igb_adapter *adapter)
}
/**
+ * igb_setup_mrqc - configure the multiple receive queue control registers
+ * @adapter: Board private structure
+ **/
+static void igb_setup_mrqc(struct igb_adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 mrqc, rxcsum;
+ u32 j, num_rx_queues, shift = 0, shift2 = 0;
+ union e1000_reta {
+ u32 dword;
+ u8 bytes[4];
+ } reta;
+ static const u8 rsshash[40] = {
+ 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67,
+ 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb,
+ 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30,
+ 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa };
+
+ /* Fill out hash function seeds */
+ for (j = 0; j < 10; j++) {
+ u32 rsskey = rsshash[(j * 4)];
+ rsskey |= rsshash[(j * 4) + 1] << 8;
+ rsskey |= rsshash[(j * 4) + 2] << 16;
+ rsskey |= rsshash[(j * 4) + 3] << 24;
+ array_wr32(E1000_RSSRK(0), j, rsskey);
+ }
+
+ num_rx_queues = adapter->rss_queues;
+
+ if (adapter->vfs_allocated_count) {
+ /* 82575 and 82576 supports 2 RSS queues for VMDq */
+ switch (hw->mac.type) {
+ case e1000_82580:
+ num_rx_queues = 1;
+ shift = 0;
+ break;
+ case e1000_82576:
+ shift = 3;
+ num_rx_queues = 2;
+ break;
+ case e1000_82575:
+ shift = 2;
+ shift2 = 6;
+ default:
+ break;
+ }
+ } else {
+ if (hw->mac.type == e1000_82575)
+ shift = 6;
+ }
+
+ for (j = 0; j < (32 * 4); j++) {
+ reta.bytes[j & 3] = (j % num_rx_queues) << shift;
+ if (shift2)
+ reta.bytes[j & 3] |= num_rx_queues << shift2;
+ if ((j & 3) == 3)
+ wr32(E1000_RETA(j >> 2), reta.dword);
+ }
+
+ /*
+ * Disable raw packet checksumming so that RSS hash is placed in
+ * descriptor on writeback. No need to enable TCP/UDP/IP checksum
+ * offloads as they are enabled by default
+ */
+ rxcsum = rd32(E1000_RXCSUM);
+ rxcsum |= E1000_RXCSUM_PCSD;
+
+ if (adapter->hw.mac.type >= e1000_82576)
+ /* Enable Receive Checksum Offload for SCTP */
+ rxcsum |= E1000_RXCSUM_CRCOFL;
+
+ /* Don't need to set TUOFL or IPOFL, they default to 1 */
+ wr32(E1000_RXCSUM, rxcsum);
+
+ /* If VMDq is enabled then we set the appropriate mode for that, else
+ * we default to RSS so that an RSS hash is calculated per packet even
+ * if we are only using one queue */
+ if (adapter->vfs_allocated_count) {
+ if (hw->mac.type > e1000_82575) {
+ /* Set the default pool for the PF's first queue */
+ u32 vtctl = rd32(E1000_VT_CTL);
+ vtctl &= ~(E1000_VT_CTL_DEFAULT_POOL_MASK |
+ E1000_VT_CTL_DISABLE_DEF_POOL);
+ vtctl |= adapter->vfs_allocated_count <<
+ E1000_VT_CTL_DEFAULT_POOL_SHIFT;
+ wr32(E1000_VT_CTL, vtctl);
+ }
+ if (adapter->rss_queues > 1)
+ mrqc = E1000_MRQC_ENABLE_VMDQ_RSS_2Q;
+ else
+ mrqc = E1000_MRQC_ENABLE_VMDQ;
+ } else {
+ mrqc = E1000_MRQC_ENABLE_RSS_4Q;
+ }
+ igb_vmm_control(adapter);
+
+ mrqc |= (E1000_MRQC_RSS_FIELD_IPV4 |
+ E1000_MRQC_RSS_FIELD_IPV4_TCP);
+ mrqc |= (E1000_MRQC_RSS_FIELD_IPV6 |
+ E1000_MRQC_RSS_FIELD_IPV6_TCP);
+ mrqc |= (E1000_MRQC_RSS_FIELD_IPV4_UDP |
+ E1000_MRQC_RSS_FIELD_IPV6_UDP);
+ mrqc |= (E1000_MRQC_RSS_FIELD_IPV6_UDP_EX |
+ E1000_MRQC_RSS_FIELD_IPV6_TCP_EX);
+
+ wr32(E1000_MRQC, mrqc);
+}
+
+/**
* igb_setup_rctl - configure the receive control registers
* @adapter: Board private structure
**/
-static void igb_setup_rctl(struct igb_adapter *adapter)
+void igb_setup_rctl(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
u32 rctl;
- u32 srrctl = 0;
- int i;
rctl = rd32(E1000_RCTL);
@@ -2069,75 +2428,45 @@ static void igb_setup_rctl(struct igb_adapter *adapter)
*/
rctl |= E1000_RCTL_SECRC;
- /*
- * disable store bad packets and clear size bits.
- */
+ /* disable store bad packets and clear size bits. */
rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_SZ_256);
- /* enable LPE when to prevent packets larger than max_frame_size */
- rctl |= E1000_RCTL_LPE;
-
- /* Setup buffer sizes */
- switch (adapter->rx_buffer_len) {
- case IGB_RXBUFFER_256:
- rctl |= E1000_RCTL_SZ_256;
- break;
- case IGB_RXBUFFER_512:
- rctl |= E1000_RCTL_SZ_512;
- break;
- default:
- srrctl = ALIGN(adapter->rx_buffer_len, 1024)
- >> E1000_SRRCTL_BSIZEPKT_SHIFT;
- break;
- }
+ /* enable LPE to prevent packets larger than max_frame_size */
+ rctl |= E1000_RCTL_LPE;
- /* 82575 and greater support packet-split where the protocol
- * header is placed in skb->data and the packet data is
- * placed in pages hanging off of skb_shinfo(skb)->nr_frags.
- * In the case of a non-split, skb->data is linearly filled,
- * followed by the page buffers. Therefore, skb->data is
- * sized to hold the largest protocol header.
- */
- /* allocations using alloc_page take too long for regular MTU
- * so only enable packet split for jumbo frames */
- if (adapter->netdev->mtu > ETH_DATA_LEN) {
- adapter->rx_ps_hdr_size = IGB_RXBUFFER_128;
- srrctl |= adapter->rx_ps_hdr_size <<
- E1000_SRRCTL_BSIZEHDRSIZE_SHIFT;
- srrctl |= E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS;
- } else {
- adapter->rx_ps_hdr_size = 0;
- srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF;
- }
+ /* disable queue 0 to prevent tail write w/o re-config */
+ wr32(E1000_RXDCTL(0), 0);
/* Attention!!! For SR-IOV PF driver operations you must enable
* queue drop for all VF and PF queues to prevent head of line blocking
* if an un-trusted VF does not provide descriptors to hardware.
*/
if (adapter->vfs_allocated_count) {
- u32 vmolr;
-
/* set all queue drop enable bits */
wr32(E1000_QDE, ALL_QUEUES);
- srrctl |= E1000_SRRCTL_DROP_EN;
+ }
- /* disable queue 0 to prevent tail write w/o re-config */
- wr32(E1000_RXDCTL(0), 0);
+ wr32(E1000_RCTL, rctl);
+}
- vmolr = rd32(E1000_VMOLR(adapter->vfs_allocated_count));
- if (rctl & E1000_RCTL_LPE)
- vmolr |= E1000_VMOLR_LPE;
- if (adapter->num_rx_queues > 1)
- vmolr |= E1000_VMOLR_RSSE;
- wr32(E1000_VMOLR(adapter->vfs_allocated_count), vmolr);
- }
+static inline int igb_set_vf_rlpml(struct igb_adapter *adapter, int size,
+ int vfn)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 vmolr;
- for (i = 0; i < adapter->num_rx_queues; i++) {
- int j = adapter->rx_ring[i].reg_idx;
- wr32(E1000_SRRCTL(j), srrctl);
- }
+ /* if it isn't the PF check to see if VFs are enabled and
+ * increase the size to support vlan tags */
+ if (vfn < adapter->vfs_allocated_count &&
+ adapter->vf_data[vfn].vlans_enabled)
+ size += VLAN_TAG_SIZE;
- wr32(E1000_RCTL, rctl);
+ vmolr = rd32(E1000_VMOLR(vfn));
+ vmolr &= ~E1000_VMOLR_RLPML_MASK;
+ vmolr |= size | E1000_VMOLR_LPE;
+ wr32(E1000_VMOLR(vfn), vmolr);
+
+ return 0;
}
/**
@@ -2159,33 +2488,107 @@ static void igb_rlpml_set(struct igb_adapter *adapter)
* size and set the VMOLR RLPML to the size we need */
if (pf_id) {
igb_set_vf_rlpml(adapter, max_frame_size, pf_id);
- max_frame_size = MAX_STD_JUMBO_FRAME_SIZE + VLAN_TAG_SIZE;
+ max_frame_size = MAX_JUMBO_FRAME_SIZE;
}
wr32(E1000_RLPML, max_frame_size);
}
+static inline void igb_set_vmolr(struct igb_adapter *adapter, int vfn)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 vmolr;
+
+ /*
+ * This register exists only on 82576 and newer so if we are older then
+ * we should exit and do nothing
+ */
+ if (hw->mac.type < e1000_82576)
+ return;
+
+ vmolr = rd32(E1000_VMOLR(vfn));
+ vmolr |= E1000_VMOLR_AUPE | /* Accept untagged packets */
+ E1000_VMOLR_STRVLAN; /* Strip vlan tags */
+
+ /* clear all bits that might not be set */
+ vmolr &= ~(E1000_VMOLR_BAM | E1000_VMOLR_RSSE);
+
+ if (adapter->rss_queues > 1 && vfn == adapter->vfs_allocated_count)
+ vmolr |= E1000_VMOLR_RSSE; /* enable RSS */
+ /*
+ * for VMDq only allow the VFs and pool 0 to accept broadcast and
+ * multicast packets
+ */
+ if (vfn <= adapter->vfs_allocated_count)
+ vmolr |= E1000_VMOLR_BAM; /* Accept broadcast */
+
+ wr32(E1000_VMOLR(vfn), vmolr);
+}
+
/**
- * igb_configure_vt_default_pool - Configure VT default pool
+ * igb_configure_rx_ring - Configure a receive ring after Reset
* @adapter: board private structure
+ * @ring: receive ring to be configured
*
- * Configure the default pool
+ * Configure the Rx unit of the MAC after a reset.
**/
-static void igb_configure_vt_default_pool(struct igb_adapter *adapter)
+void igb_configure_rx_ring(struct igb_adapter *adapter,
+ struct igb_ring *ring)
{
struct e1000_hw *hw = &adapter->hw;
- u16 pf_id = adapter->vfs_allocated_count;
- u32 vtctl;
+ u64 rdba = ring->dma;
+ int reg_idx = ring->reg_idx;
+ u32 srrctl, rxdctl;
+
+ /* disable the queue */
+ rxdctl = rd32(E1000_RXDCTL(reg_idx));
+ wr32(E1000_RXDCTL(reg_idx),
+ rxdctl & ~E1000_RXDCTL_QUEUE_ENABLE);
+
+ /* Set DMA base address registers */
+ wr32(E1000_RDBAL(reg_idx),
+ rdba & 0x00000000ffffffffULL);
+ wr32(E1000_RDBAH(reg_idx), rdba >> 32);
+ wr32(E1000_RDLEN(reg_idx),
+ ring->count * sizeof(union e1000_adv_rx_desc));
+
+ /* initialize head and tail */
+ ring->head = hw->hw_addr + E1000_RDH(reg_idx);
+ ring->tail = hw->hw_addr + E1000_RDT(reg_idx);
+ writel(0, ring->head);
+ writel(0, ring->tail);
+
+ /* set descriptor configuration */
+ if (ring->rx_buffer_len < IGB_RXBUFFER_1024) {
+ srrctl = ALIGN(ring->rx_buffer_len, 64) <<
+ E1000_SRRCTL_BSIZEHDRSIZE_SHIFT;
+#if (PAGE_SIZE / 2) > IGB_RXBUFFER_16384
+ srrctl |= IGB_RXBUFFER_16384 >>
+ E1000_SRRCTL_BSIZEPKT_SHIFT;
+#else
+ srrctl |= (PAGE_SIZE / 2) >>
+ E1000_SRRCTL_BSIZEPKT_SHIFT;
+#endif
+ srrctl |= E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS;
+ } else {
+ srrctl = ALIGN(ring->rx_buffer_len, 1024) >>
+ E1000_SRRCTL_BSIZEPKT_SHIFT;
+ srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF;
+ }
- /* not in sr-iov mode - do nothing */
- if (!pf_id)
- return;
+ wr32(E1000_SRRCTL(reg_idx), srrctl);
+
+ /* set filtering for VMDQ pools */
+ igb_set_vmolr(adapter, reg_idx & 0x7);
- vtctl = rd32(E1000_VT_CTL);
- vtctl &= ~(E1000_VT_CTL_DEFAULT_POOL_MASK |
- E1000_VT_CTL_DISABLE_DEF_POOL);
- vtctl |= pf_id << E1000_VT_CTL_DEFAULT_POOL_SHIFT;
- wr32(E1000_VT_CTL, vtctl);
+ /* enable receive descriptor fetching */
+ rxdctl = rd32(E1000_RXDCTL(reg_idx));
+ rxdctl |= E1000_RXDCTL_QUEUE_ENABLE;
+ rxdctl &= 0xFFF00000;
+ rxdctl |= IGB_RX_PTHRESH;
+ rxdctl |= IGB_RX_HTHRESH << 8;
+ rxdctl |= IGB_RX_WTHRESH << 16;
+ wr32(E1000_RXDCTL(reg_idx), rxdctl);
}
/**
@@ -2196,112 +2599,19 @@ static void igb_configure_vt_default_pool(struct igb_adapter *adapter)
**/
static void igb_configure_rx(struct igb_adapter *adapter)
{
- u64 rdba;
- struct e1000_hw *hw = &adapter->hw;
- u32 rctl, rxcsum;
- u32 rxdctl;
int i;
- /* disable receives while setting up the descriptors */
- rctl = rd32(E1000_RCTL);
- wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN);
- wrfl();
- mdelay(10);
+ /* set UTA to appropriate mode */
+ igb_set_uta(adapter);
- if (adapter->itr_setting > 3)
- wr32(E1000_ITR, adapter->itr);
+ /* set the correct pool for the PF default MAC address in entry 0 */
+ igb_rar_set_qsel(adapter, adapter->hw.mac.addr, 0,
+ adapter->vfs_allocated_count);
/* Setup the HW Rx Head and Tail Descriptor Pointers and
* the Base and Length of the Rx Descriptor Ring */
- for (i = 0; i < adapter->num_rx_queues; i++) {
- struct igb_ring *ring = &adapter->rx_ring[i];
- int j = ring->reg_idx;
- rdba = ring->dma;
- wr32(E1000_RDBAL(j),
- rdba & 0x00000000ffffffffULL);
- wr32(E1000_RDBAH(j), rdba >> 32);
- wr32(E1000_RDLEN(j),
- ring->count * sizeof(union e1000_adv_rx_desc));
-
- ring->head = E1000_RDH(j);
- ring->tail = E1000_RDT(j);
- writel(0, hw->hw_addr + ring->tail);
- writel(0, hw->hw_addr + ring->head);
-
- rxdctl = rd32(E1000_RXDCTL(j));
- rxdctl |= E1000_RXDCTL_QUEUE_ENABLE;
- rxdctl &= 0xFFF00000;
- rxdctl |= IGB_RX_PTHRESH;
- rxdctl |= IGB_RX_HTHRESH << 8;
- rxdctl |= IGB_RX_WTHRESH << 16;
- wr32(E1000_RXDCTL(j), rxdctl);
- }
-
- if (adapter->num_rx_queues > 1) {
- u32 random[10];
- u32 mrqc;
- u32 j, shift;
- union e1000_reta {
- u32 dword;
- u8 bytes[4];
- } reta;
-
- get_random_bytes(&random[0], 40);
-
- if (hw->mac.type >= e1000_82576)
- shift = 0;
- else
- shift = 6;
- for (j = 0; j < (32 * 4); j++) {
- reta.bytes[j & 3] =
- adapter->rx_ring[(j % adapter->num_rx_queues)].reg_idx << shift;
- if ((j & 3) == 3)
- writel(reta.dword,
- hw->hw_addr + E1000_RETA(0) + (j & ~3));
- }
- if (adapter->vfs_allocated_count)
- mrqc = E1000_MRQC_ENABLE_VMDQ_RSS_2Q;
- else
- mrqc = E1000_MRQC_ENABLE_RSS_4Q;
-
- /* Fill out hash function seeds */
- for (j = 0; j < 10; j++)
- array_wr32(E1000_RSSRK(0), j, random[j]);
-
- mrqc |= (E1000_MRQC_RSS_FIELD_IPV4 |
- E1000_MRQC_RSS_FIELD_IPV4_TCP);
- mrqc |= (E1000_MRQC_RSS_FIELD_IPV6 |
- E1000_MRQC_RSS_FIELD_IPV6_TCP);
- mrqc |= (E1000_MRQC_RSS_FIELD_IPV4_UDP |
- E1000_MRQC_RSS_FIELD_IPV6_UDP);
- mrqc |= (E1000_MRQC_RSS_FIELD_IPV6_UDP_EX |
- E1000_MRQC_RSS_FIELD_IPV6_TCP_EX);
-
- wr32(E1000_MRQC, mrqc);
- } else if (adapter->vfs_allocated_count) {
- /* Enable multi-queue for sr-iov */
- wr32(E1000_MRQC, E1000_MRQC_ENABLE_VMDQ);
- }
-
- /* Enable Receive Checksum Offload for TCP and UDP */
- rxcsum = rd32(E1000_RXCSUM);
- /* Disable raw packet checksumming */
- rxcsum |= E1000_RXCSUM_PCSD;
-
- if (adapter->hw.mac.type == e1000_82576)
- /* Enable Receive Checksum Offload for SCTP */
- rxcsum |= E1000_RXCSUM_CRCOFL;
-
- /* Don't need to set TUOFL or IPOFL, they default to 1 */
- wr32(E1000_RXCSUM, rxcsum);
-
- /* Set the default pool for the PF's first queue */
- igb_configure_vt_default_pool(adapter);
-
- igb_rlpml_set(adapter);
-
- /* Enable Receives */
- wr32(E1000_RCTL, rctl);
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ igb_configure_rx_ring(adapter, &adapter->rx_ring[i]);
}
/**
@@ -2312,14 +2622,17 @@ static void igb_configure_rx(struct igb_adapter *adapter)
**/
void igb_free_tx_resources(struct igb_ring *tx_ring)
{
- struct pci_dev *pdev = tx_ring->adapter->pdev;
-
igb_clean_tx_ring(tx_ring);
vfree(tx_ring->buffer_info);
tx_ring->buffer_info = NULL;
- pci_free_consistent(pdev, tx_ring->size, tx_ring->desc, tx_ring->dma);
+ /* if not set, then don't free */
+ if (!tx_ring->desc)
+ return;
+
+ pci_free_consistent(tx_ring->pdev, tx_ring->size,
+ tx_ring->desc, tx_ring->dma);
tx_ring->desc = NULL;
}
@@ -2338,18 +2651,30 @@ static void igb_free_all_tx_resources(struct igb_adapter *adapter)
igb_free_tx_resources(&adapter->tx_ring[i]);
}
-static void igb_unmap_and_free_tx_resource(struct igb_adapter *adapter,
- struct igb_buffer *buffer_info)
+void igb_unmap_and_free_tx_resource(struct igb_ring *tx_ring,
+ struct igb_buffer *buffer_info)
{
- buffer_info->dma = 0;
+ if (buffer_info->dma) {
+ if (buffer_info->mapped_as_page)
+ pci_unmap_page(tx_ring->pdev,
+ buffer_info->dma,
+ buffer_info->length,
+ PCI_DMA_TODEVICE);
+ else
+ pci_unmap_single(tx_ring->pdev,
+ buffer_info->dma,
+ buffer_info->length,
+ PCI_DMA_TODEVICE);
+ buffer_info->dma = 0;
+ }
if (buffer_info->skb) {
- skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb,
- DMA_TO_DEVICE);
dev_kfree_skb_any(buffer_info->skb);
buffer_info->skb = NULL;
}
buffer_info->time_stamp = 0;
- /* buffer_info must be completely set up in the transmit path */
+ buffer_info->length = 0;
+ buffer_info->next_to_watch = 0;
+ buffer_info->mapped_as_page = false;
}
/**
@@ -2358,7 +2683,6 @@ static void igb_unmap_and_free_tx_resource(struct igb_adapter *adapter,
**/
static void igb_clean_tx_ring(struct igb_ring *tx_ring)
{
- struct igb_adapter *adapter = tx_ring->adapter;
struct igb_buffer *buffer_info;
unsigned long size;
unsigned int i;
@@ -2369,21 +2693,17 @@ static void igb_clean_tx_ring(struct igb_ring *tx_ring)
for (i = 0; i < tx_ring->count; i++) {
buffer_info = &tx_ring->buffer_info[i];
- igb_unmap_and_free_tx_resource(adapter, buffer_info);
+ igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
}
size = sizeof(struct igb_buffer) * tx_ring->count;
memset(tx_ring->buffer_info, 0, size);
/* Zero out the descriptor ring */
-
memset(tx_ring->desc, 0, tx_ring->size);
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
-
- writel(0, adapter->hw.hw_addr + tx_ring->head);
- writel(0, adapter->hw.hw_addr + tx_ring->tail);
}
/**
@@ -2406,14 +2726,17 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter)
**/
void igb_free_rx_resources(struct igb_ring *rx_ring)
{
- struct pci_dev *pdev = rx_ring->adapter->pdev;
-
igb_clean_rx_ring(rx_ring);
vfree(rx_ring->buffer_info);
rx_ring->buffer_info = NULL;
- pci_free_consistent(pdev, rx_ring->size, rx_ring->desc, rx_ring->dma);
+ /* if not set, then don't free */
+ if (!rx_ring->desc)
+ return;
+
+ pci_free_consistent(rx_ring->pdev, rx_ring->size,
+ rx_ring->desc, rx_ring->dma);
rx_ring->desc = NULL;
}
@@ -2438,26 +2761,21 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter)
**/
static void igb_clean_rx_ring(struct igb_ring *rx_ring)
{
- struct igb_adapter *adapter = rx_ring->adapter;
struct igb_buffer *buffer_info;
- struct pci_dev *pdev = adapter->pdev;
unsigned long size;
unsigned int i;
if (!rx_ring->buffer_info)
return;
+
/* Free all the Rx ring sk_buffs */
for (i = 0; i < rx_ring->count; i++) {
buffer_info = &rx_ring->buffer_info[i];
if (buffer_info->dma) {
- if (adapter->rx_ps_hdr_size)
- pci_unmap_single(pdev, buffer_info->dma,
- adapter->rx_ps_hdr_size,
- PCI_DMA_FROMDEVICE);
- else
- pci_unmap_single(pdev, buffer_info->dma,
- adapter->rx_buffer_len,
- PCI_DMA_FROMDEVICE);
+ pci_unmap_single(rx_ring->pdev,
+ buffer_info->dma,
+ rx_ring->rx_buffer_len,
+ PCI_DMA_FROMDEVICE);
buffer_info->dma = 0;
}
@@ -2465,14 +2783,16 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring)
dev_kfree_skb(buffer_info->skb);
buffer_info->skb = NULL;
}
+ if (buffer_info->page_dma) {
+ pci_unmap_page(rx_ring->pdev,
+ buffer_info->page_dma,
+ PAGE_SIZE / 2,
+ PCI_DMA_FROMDEVICE);
+ buffer_info->page_dma = 0;
+ }
if (buffer_info->page) {
- if (buffer_info->page_dma)
- pci_unmap_page(pdev, buffer_info->page_dma,
- PAGE_SIZE / 2,
- PCI_DMA_FROMDEVICE);
put_page(buffer_info->page);
buffer_info->page = NULL;
- buffer_info->page_dma = 0;
buffer_info->page_offset = 0;
}
}
@@ -2485,9 +2805,6 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring)
rx_ring->next_to_clean = 0;
rx_ring->next_to_use = 0;
-
- writel(0, adapter->hw.hw_addr + rx_ring->head);
- writel(0, adapter->hw.hw_addr + rx_ring->tail);
}
/**
@@ -2521,61 +2838,90 @@ static int igb_set_mac(struct net_device *netdev, void *p)
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
- igb_rar_set(hw, hw->mac.addr, 0);
- igb_set_rah_pool(hw, adapter->vfs_allocated_count, 0);
+ /* set the correct pool for the new PF MAC address in entry 0 */
+ igb_rar_set_qsel(adapter, hw->mac.addr, 0,
+ adapter->vfs_allocated_count);
return 0;
}
/**
- * igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
+ * igb_write_mc_addr_list - write multicast addresses to MTA
* @netdev: network interface device structure
*
- * The set_rx_mode entry point is called whenever the unicast or multicast
- * address lists or the network interface flags are updated. This routine is
- * responsible for configuring the hardware for proper unicast, multicast,
- * promiscuous mode, and all-multi behavior.
+ * Writes multicast address list to the MTA hash table.
+ * Returns: -ENOMEM on failure
+ * 0 on no addresses written
+ * X on writing X addresses to MTA
**/
-static void igb_set_rx_mode(struct net_device *netdev)
+static int igb_write_mc_addr_list(struct net_device *netdev)
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- unsigned int rar_entries = hw->mac.rar_entry_count -
- (adapter->vfs_allocated_count + 1);
struct dev_mc_list *mc_ptr = netdev->mc_list;
- u8 *mta_list = NULL;
- u32 rctl;
+ u8 *mta_list;
+ u32 vmolr = 0;
int i;
- /* Check for Promiscuous and All Multicast modes */
- rctl = rd32(E1000_RCTL);
+ if (!netdev->mc_count) {
+ /* nothing to program, so clear mc list */
+ igb_update_mc_addr_list(hw, NULL, 0);
+ igb_restore_vf_multicasts(adapter);
+ return 0;
+ }
- if (netdev->flags & IFF_PROMISC) {
- rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
- rctl &= ~E1000_RCTL_VFE;
- } else {
- if (netdev->flags & IFF_ALLMULTI)
- rctl |= E1000_RCTL_MPE;
- else
- rctl &= ~E1000_RCTL_MPE;
+ mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC);
+ if (!mta_list)
+ return -ENOMEM;
- if (netdev->uc.count > rar_entries)
- rctl |= E1000_RCTL_UPE;
- else
- rctl &= ~E1000_RCTL_UPE;
- rctl |= E1000_RCTL_VFE;
+ /* set vmolr receive overflow multicast bit */
+ vmolr |= E1000_VMOLR_ROMPE;
+
+ /* The shared function expects a packed array of only addresses. */
+ mc_ptr = netdev->mc_list;
+
+ for (i = 0; i < netdev->mc_count; i++) {
+ if (!mc_ptr)
+ break;
+ memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN);
+ mc_ptr = mc_ptr->next;
}
- wr32(E1000_RCTL, rctl);
+ igb_update_mc_addr_list(hw, mta_list, i);
+ kfree(mta_list);
+
+ return netdev->mc_count;
+}
+
+/**
+ * igb_write_uc_addr_list - write unicast addresses to RAR table
+ * @netdev: network interface device structure
+ *
+ * Writes unicast address list to the RAR table.
+ * Returns: -ENOMEM on failure/insufficient address space
+ * 0 on no addresses written
+ * X on writing X addresses to the RAR table
+ **/
+static int igb_write_uc_addr_list(struct net_device *netdev)
+{
+ struct igb_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
+ unsigned int vfn = adapter->vfs_allocated_count;
+ unsigned int rar_entries = hw->mac.rar_entry_count - (vfn + 1);
+ int count = 0;
+
+ /* return ENOMEM indicating insufficient memory for addresses */
+ if (netdev->uc.count > rar_entries)
+ return -ENOMEM;
if (netdev->uc.count && rar_entries) {
struct netdev_hw_addr *ha;
list_for_each_entry(ha, &netdev->uc.list, list) {
if (!rar_entries)
break;
- igb_rar_set(hw, ha->addr, rar_entries);
- igb_set_rah_pool(hw, adapter->vfs_allocated_count,
- rar_entries);
- rar_entries--;
+ igb_rar_set_qsel(adapter, ha->addr,
+ rar_entries--,
+ vfn);
+ count++;
}
}
/* write the addresses in reverse order to avoid write combining */
@@ -2585,29 +2931,79 @@ static void igb_set_rx_mode(struct net_device *netdev)
}
wrfl();
- if (!netdev->mc_count) {
- /* nothing to program, so clear mc list */
- igb_update_mc_addr_list(hw, NULL, 0);
- igb_restore_vf_multicasts(adapter);
- return;
+ return count;
+}
+
+/**
+ * igb_set_rx_mode - Secondary Unicast, Multicast and Promiscuous mode set
+ * @netdev: network interface device structure
+ *
+ * The set_rx_mode entry point is called whenever the unicast or multicast
+ * address lists or the network interface flags are updated. This routine is
+ * responsible for configuring the hardware for proper unicast, multicast,
+ * promiscuous mode, and all-multi behavior.
+ **/
+static void igb_set_rx_mode(struct net_device *netdev)
+{
+ struct igb_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
+ unsigned int vfn = adapter->vfs_allocated_count;
+ u32 rctl, vmolr = 0;
+ int count;
+
+ /* Check for Promiscuous and All Multicast modes */
+ rctl = rd32(E1000_RCTL);
+
+ /* clear the effected bits */
+ rctl &= ~(E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_VFE);
+
+ if (netdev->flags & IFF_PROMISC) {
+ rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE);
+ vmolr |= (E1000_VMOLR_ROPE | E1000_VMOLR_MPME);
+ } else {
+ if (netdev->flags & IFF_ALLMULTI) {
+ rctl |= E1000_RCTL_MPE;
+ vmolr |= E1000_VMOLR_MPME;
+ } else {
+ /*
+ * Write addresses to the MTA, if the attempt fails
+ * then we should just turn on promiscous mode so
+ * that we can at least receive multicast traffic
+ */
+ count = igb_write_mc_addr_list(netdev);
+ if (count < 0) {
+ rctl |= E1000_RCTL_MPE;
+ vmolr |= E1000_VMOLR_MPME;
+ } else if (count) {
+ vmolr |= E1000_VMOLR_ROMPE;
+ }
+ }
+ /*
+ * Write addresses to available RAR registers, if there is not
+ * sufficient space to store all the addresses then enable
+ * unicast promiscous mode
+ */
+ count = igb_write_uc_addr_list(netdev);
+ if (count < 0) {
+ rctl |= E1000_RCTL_UPE;
+ vmolr |= E1000_VMOLR_ROPE;
+ }
+ rctl |= E1000_RCTL_VFE;
}
+ wr32(E1000_RCTL, rctl);
- mta_list = kzalloc(netdev->mc_count * 6, GFP_ATOMIC);
- if (!mta_list) {
- dev_err(&adapter->pdev->dev,
- "failed to allocate multicast filter list\n");
+ /*
+ * In order to support SR-IOV and eventually VMDq it is necessary to set
+ * the VMOLR to enable the appropriate modes. Without this workaround
+ * we will have issues with VLAN tag stripping not being done for frames
+ * that are only arriving because we are the default pool
+ */
+ if (hw->mac.type < e1000_82576)
return;
- }
- /* The shared function expects a packed array of only addresses. */
- for (i = 0; i < netdev->mc_count; i++) {
- if (!mc_ptr)
- break;
- memcpy(mta_list + (i*ETH_ALEN), mc_ptr->dmi_addr, ETH_ALEN);
- mc_ptr = mc_ptr->next;
- }
- igb_update_mc_addr_list(hw, mta_list, i);
- kfree(mta_list);
+ vmolr |= rd32(E1000_VMOLR(vfn)) &
+ ~(E1000_VMOLR_ROPE | E1000_VMOLR_MPME | E1000_VMOLR_ROMPE);
+ wr32(E1000_VMOLR(vfn), vmolr);
igb_restore_vf_multicasts(adapter);
}
@@ -2669,37 +3065,33 @@ static void igb_watchdog(unsigned long data)
static void igb_watchdog_task(struct work_struct *work)
{
struct igb_adapter *adapter = container_of(work,
- struct igb_adapter, watchdog_task);
+ struct igb_adapter,
+ watchdog_task);
struct e1000_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
- struct igb_ring *tx_ring = adapter->tx_ring;
u32 link;
- u32 eics = 0;
int i;
link = igb_has_link(adapter);
- if ((netif_carrier_ok(netdev)) && link)
- goto link_up;
-
if (link) {
if (!netif_carrier_ok(netdev)) {
u32 ctrl;
- hw->mac.ops.get_speed_and_duplex(&adapter->hw,
- &adapter->link_speed,
- &adapter->link_duplex);
+ hw->mac.ops.get_speed_and_duplex(hw,
+ &adapter->link_speed,
+ &adapter->link_duplex);
ctrl = rd32(E1000_CTRL);
/* Links status message must follow this format */
printk(KERN_INFO "igb: %s NIC Link is Up %d Mbps %s, "
"Flow Control: %s\n",
- netdev->name,
- adapter->link_speed,
- adapter->link_duplex == FULL_DUPLEX ?
+ netdev->name,
+ adapter->link_speed,
+ adapter->link_duplex == FULL_DUPLEX ?
"Full Duplex" : "Half Duplex",
- ((ctrl & E1000_CTRL_TFCE) && (ctrl &
- E1000_CTRL_RFCE)) ? "RX/TX" : ((ctrl &
- E1000_CTRL_RFCE) ? "RX" : ((ctrl &
- E1000_CTRL_TFCE) ? "TX" : "None")));
+ ((ctrl & E1000_CTRL_TFCE) &&
+ (ctrl & E1000_CTRL_RFCE)) ? "RX/TX" :
+ ((ctrl & E1000_CTRL_RFCE) ? "RX" :
+ ((ctrl & E1000_CTRL_TFCE) ? "TX" : "None")));
/* tweak tx_queue_len according to speed/duplex and
* adjust the timeout factor */
@@ -2743,46 +3135,40 @@ static void igb_watchdog_task(struct work_struct *work)
}
}
-link_up:
igb_update_stats(adapter);
+ igb_update_adaptive(hw);
- hw->mac.tx_packet_delta = adapter->stats.tpt - adapter->tpt_old;
- adapter->tpt_old = adapter->stats.tpt;
- hw->mac.collision_delta = adapter->stats.colc - adapter->colc_old;
- adapter->colc_old = adapter->stats.colc;
-
- adapter->gorc = adapter->stats.gorc - adapter->gorc_old;
- adapter->gorc_old = adapter->stats.gorc;
- adapter->gotc = adapter->stats.gotc - adapter->gotc_old;
- adapter->gotc_old = adapter->stats.gotc;
-
- igb_update_adaptive(&adapter->hw);
-
- if (!netif_carrier_ok(netdev)) {
- if (igb_desc_unused(tx_ring) + 1 < tx_ring->count) {
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ struct igb_ring *tx_ring = &adapter->tx_ring[i];
+ if (!netif_carrier_ok(netdev)) {
/* We've lost link, so the controller stops DMA,
* but we've got queued Tx work that's never going
* to get done, so reset controller to flush Tx.
* (Do the reset outside of interrupt context). */
- adapter->tx_timeout_count++;
- schedule_work(&adapter->reset_task);
- /* return immediately since reset is imminent */
- return;
+ if (igb_desc_unused(tx_ring) + 1 < tx_ring->count) {
+ adapter->tx_timeout_count++;
+ schedule_work(&adapter->reset_task);
+ /* return immediately since reset is imminent */
+ return;
+ }
}
+
+ /* Force detection of hung controller every watchdog period */
+ tx_ring->detect_tx_hung = true;
}
/* Cause software interrupt to ensure rx ring is cleaned */
if (adapter->msix_entries) {
- for (i = 0; i < adapter->num_rx_queues; i++)
- eics |= adapter->rx_ring[i].eims_value;
+ u32 eics = 0;
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ eics |= q_vector->eims_value;
+ }
wr32(E1000_EICS, eics);
} else {
wr32(E1000_ICS, E1000_ICS_RXDMT0);
}
- /* Force detection of hung controller every watchdog period */
- tx_ring->detect_tx_hung = true;
-
/* Reset the timer */
if (!test_bit(__IGB_DOWN, &adapter->state))
mod_timer(&adapter->watchdog_timer,
@@ -2796,7 +3182,6 @@ enum latency_range {
latency_invalid = 255
};
-
/**
* igb_update_ring_itr - update the dynamic ITR value based on packet size
*
@@ -2811,25 +3196,37 @@ enum latency_range {
* parameter (see igb_param.c)
* NOTE: This function is called only when operating in a multiqueue
* receive environment.
- * @rx_ring: pointer to ring
+ * @q_vector: pointer to q_vector
**/
-static void igb_update_ring_itr(struct igb_ring *rx_ring)
+static void igb_update_ring_itr(struct igb_q_vector *q_vector)
{
- int new_val = rx_ring->itr_val;
+ int new_val = q_vector->itr_val;
int avg_wire_size = 0;
- struct igb_adapter *adapter = rx_ring->adapter;
-
- if (!rx_ring->total_packets)
- goto clear_counts; /* no packets, so don't do anything */
+ struct igb_adapter *adapter = q_vector->adapter;
/* For non-gigabit speeds, just fix the interrupt rate at 4000
* ints/sec - ITR timer value of 120 ticks.
*/
if (adapter->link_speed != SPEED_1000) {
- new_val = 120;
+ new_val = 976;
goto set_itr_val;
}
- avg_wire_size = rx_ring->total_bytes / rx_ring->total_packets;
+
+ if (q_vector->rx_ring && q_vector->rx_ring->total_packets) {
+ struct igb_ring *ring = q_vector->rx_ring;
+ avg_wire_size = ring->total_bytes / ring->total_packets;
+ }
+
+ if (q_vector->tx_ring && q_vector->tx_ring->total_packets) {
+ struct igb_ring *ring = q_vector->tx_ring;
+ avg_wire_size = max_t(u32, avg_wire_size,
+ (ring->total_bytes /
+ ring->total_packets));
+ }
+
+ /* if avg_wire_size isn't set no work was done */
+ if (!avg_wire_size)
+ goto clear_counts;
/* Add 24 bytes to size to account for CRC, preamble, and gap */
avg_wire_size += 24;
@@ -2844,13 +3241,19 @@ static void igb_update_ring_itr(struct igb_ring *rx_ring)
new_val = avg_wire_size / 2;
set_itr_val:
- if (new_val != rx_ring->itr_val) {
- rx_ring->itr_val = new_val;
- rx_ring->set_itr = 1;
+ if (new_val != q_vector->itr_val) {
+ q_vector->itr_val = new_val;
+ q_vector->set_itr = 1;
}
clear_counts:
- rx_ring->total_bytes = 0;
- rx_ring->total_packets = 0;
+ if (q_vector->rx_ring) {
+ q_vector->rx_ring->total_bytes = 0;
+ q_vector->rx_ring->total_packets = 0;
+ }
+ if (q_vector->tx_ring) {
+ q_vector->tx_ring->total_bytes = 0;
+ q_vector->tx_ring->total_packets = 0;
+ }
}
/**
@@ -2867,7 +3270,7 @@ clear_counts:
* NOTE: These calculations are only valid when operating in a single-
* queue environment.
* @adapter: pointer to adapter
- * @itr_setting: current adapter->itr
+ * @itr_setting: current q_vector->itr_val
* @packets: the number of packets during this measurement interval
* @bytes: the number of bytes during this measurement interval
**/
@@ -2919,8 +3322,9 @@ update_itr_done:
static void igb_set_itr(struct igb_adapter *adapter)
{
+ struct igb_q_vector *q_vector = adapter->q_vector[0];
u16 current_itr;
- u32 new_itr = adapter->itr;
+ u32 new_itr = q_vector->itr_val;
/* for non-gigabit speeds, just fix the interrupt rate at 4000 */
if (adapter->link_speed != SPEED_1000) {
@@ -2934,18 +3338,14 @@ static void igb_set_itr(struct igb_adapter *adapter)
adapter->rx_ring->total_packets,
adapter->rx_ring->total_bytes);
- if (adapter->rx_ring->buddy) {
- adapter->tx_itr = igb_update_itr(adapter,
- adapter->tx_itr,
- adapter->tx_ring->total_packets,
- adapter->tx_ring->total_bytes);
- current_itr = max(adapter->rx_itr, adapter->tx_itr);
- } else {
- current_itr = adapter->rx_itr;
- }
+ adapter->tx_itr = igb_update_itr(adapter,
+ adapter->tx_itr,
+ adapter->tx_ring->total_packets,
+ adapter->tx_ring->total_bytes);
+ current_itr = max(adapter->rx_itr, adapter->tx_itr);
/* conservative mode (itr 3) eliminates the lowest_latency setting */
- if (adapter->itr_setting == 3 && current_itr == lowest_latency)
+ if (adapter->rx_itr_setting == 3 && current_itr == lowest_latency)
current_itr = low_latency;
switch (current_itr) {
@@ -2966,18 +3366,17 @@ static void igb_set_itr(struct igb_adapter *adapter)
set_itr_now:
adapter->rx_ring->total_bytes = 0;
adapter->rx_ring->total_packets = 0;
- if (adapter->rx_ring->buddy) {
- adapter->rx_ring->buddy->total_bytes = 0;
- adapter->rx_ring->buddy->total_packets = 0;
- }
+ adapter->tx_ring->total_bytes = 0;
+ adapter->tx_ring->total_packets = 0;
- if (new_itr != adapter->itr) {
+ if (new_itr != q_vector->itr_val) {
/* this attempts to bias the interrupt rate towards Bulk
* by adding intermediate steps when interrupt rate is
* increasing */
- new_itr = new_itr > adapter->itr ?
- max((new_itr * adapter->itr) /
- (new_itr + (adapter->itr >> 2)), new_itr) :
+ new_itr = new_itr > q_vector->itr_val ?
+ max((new_itr * q_vector->itr_val) /
+ (new_itr + (q_vector->itr_val >> 2)),
+ new_itr) :
new_itr;
/* Don't write the value here; it resets the adapter's
* internal timer, and causes us to delay far longer than
@@ -2985,25 +3384,22 @@ set_itr_now:
* value at the beginning of the next interrupt so the timing
* ends up being correct.
*/
- adapter->itr = new_itr;
- adapter->rx_ring->itr_val = new_itr;
- adapter->rx_ring->set_itr = 1;
+ q_vector->itr_val = new_itr;
+ q_vector->set_itr = 1;
}
return;
}
-
#define IGB_TX_FLAGS_CSUM 0x00000001
#define IGB_TX_FLAGS_VLAN 0x00000002
#define IGB_TX_FLAGS_TSO 0x00000004
#define IGB_TX_FLAGS_IPV4 0x00000008
-#define IGB_TX_FLAGS_TSTAMP 0x00000010
-#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000
-#define IGB_TX_FLAGS_VLAN_SHIFT 16
+#define IGB_TX_FLAGS_TSTAMP 0x00000010
+#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000
+#define IGB_TX_FLAGS_VLAN_SHIFT 16
-static inline int igb_tso_adv(struct igb_adapter *adapter,
- struct igb_ring *tx_ring,
+static inline int igb_tso_adv(struct igb_ring *tx_ring,
struct sk_buff *skb, u32 tx_flags, u8 *hdr_len)
{
struct e1000_adv_tx_context_desc *context_desc;
@@ -3065,8 +3461,8 @@ static inline int igb_tso_adv(struct igb_adapter *adapter,
mss_l4len_idx |= (l4len << E1000_ADVTXD_L4LEN_SHIFT);
/* For 82575, context index must be unique per ring. */
- if (adapter->flags & IGB_FLAG_NEED_CTX_IDX)
- mss_l4len_idx |= tx_ring->queue_index << 4;
+ if (tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX)
+ mss_l4len_idx |= tx_ring->reg_idx << 4;
context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
context_desc->seqnum_seed = 0;
@@ -3083,14 +3479,14 @@ static inline int igb_tso_adv(struct igb_adapter *adapter,
return true;
}
-static inline bool igb_tx_csum_adv(struct igb_adapter *adapter,
- struct igb_ring *tx_ring,
- struct sk_buff *skb, u32 tx_flags)
+static inline bool igb_tx_csum_adv(struct igb_ring *tx_ring,
+ struct sk_buff *skb, u32 tx_flags)
{
struct e1000_adv_tx_context_desc *context_desc;
- unsigned int i;
+ struct pci_dev *pdev = tx_ring->pdev;
struct igb_buffer *buffer_info;
u32 info = 0, tu_cmd = 0;
+ unsigned int i;
if ((skb->ip_summed == CHECKSUM_PARTIAL) ||
(tx_flags & IGB_TX_FLAGS_VLAN)) {
@@ -3100,6 +3496,7 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter,
if (tx_flags & IGB_TX_FLAGS_VLAN)
info |= (tx_flags & IGB_TX_FLAGS_VLAN_MASK);
+
info |= (skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT);
if (skb->ip_summed == CHECKSUM_PARTIAL)
info |= skb_network_header_len(skb);
@@ -3137,7 +3534,7 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter,
break;
default:
if (unlikely(net_ratelimit()))
- dev_warn(&adapter->pdev->dev,
+ dev_warn(&pdev->dev,
"partial checksum but proto=%x!\n",
skb->protocol);
break;
@@ -3146,11 +3543,9 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter,
context_desc->type_tucmd_mlhl = cpu_to_le32(tu_cmd);
context_desc->seqnum_seed = 0;
- if (adapter->flags & IGB_FLAG_NEED_CTX_IDX)
+ if (tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX)
context_desc->mss_l4len_idx =
- cpu_to_le32(tx_ring->queue_index << 4);
- else
- context_desc->mss_l4len_idx = 0;
+ cpu_to_le32(tx_ring->reg_idx << 4);
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
@@ -3169,32 +3564,27 @@ static inline bool igb_tx_csum_adv(struct igb_adapter *adapter,
#define IGB_MAX_TXD_PWR 16
#define IGB_MAX_DATA_PER_TXD (1<<IGB_MAX_TXD_PWR)
-static inline int igb_tx_map_adv(struct igb_adapter *adapter,
- struct igb_ring *tx_ring, struct sk_buff *skb,
+static inline int igb_tx_map_adv(struct igb_ring *tx_ring, struct sk_buff *skb,
unsigned int first)
{
struct igb_buffer *buffer_info;
+ struct pci_dev *pdev = tx_ring->pdev;
unsigned int len = skb_headlen(skb);
unsigned int count = 0, i;
unsigned int f;
- dma_addr_t *map;
i = tx_ring->next_to_use;
- if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) {
- dev_err(&adapter->pdev->dev, "TX DMA map failed\n");
- return 0;
- }
-
- map = skb_shinfo(skb)->dma_maps;
-
buffer_info = &tx_ring->buffer_info[i];
BUG_ON(len >= IGB_MAX_DATA_PER_TXD);
buffer_info->length = len;
/* set time_stamp *before* dma to help avoid a possible race */
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- buffer_info->dma = skb_shinfo(skb)->dma_head;
+ buffer_info->dma = pci_map_single(pdev, skb->data, len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
struct skb_frag_struct *frag;
@@ -3211,25 +3601,55 @@ static inline int igb_tx_map_adv(struct igb_adapter *adapter,
buffer_info->length = len;
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- buffer_info->dma = map[count];
+ buffer_info->mapped_as_page = true;
+ buffer_info->dma = pci_map_page(pdev,
+ frag->page,
+ frag->page_offset,
+ len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
+
count++;
}
tx_ring->buffer_info[i].skb = skb;
tx_ring->buffer_info[first].next_to_watch = i;
- return count + 1;
+ return ++count;
+
+dma_error:
+ dev_err(&pdev->dev, "TX DMA map failed\n");
+
+ /* clear timestamp and dma mappings for failed buffer_info mapping */
+ buffer_info->dma = 0;
+ buffer_info->time_stamp = 0;
+ buffer_info->length = 0;
+ buffer_info->next_to_watch = 0;
+ buffer_info->mapped_as_page = false;
+ count--;
+
+ /* clear timestamp and dma mappings for remaining portion of packet */
+ while (count >= 0) {
+ count--;
+ i--;
+ if (i < 0)
+ i += tx_ring->count;
+ buffer_info = &tx_ring->buffer_info[i];
+ igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
+ }
+
+ return 0;
}
-static inline void igb_tx_queue_adv(struct igb_adapter *adapter,
- struct igb_ring *tx_ring,
+static inline void igb_tx_queue_adv(struct igb_ring *tx_ring,
int tx_flags, int count, u32 paylen,
u8 hdr_len)
{
- union e1000_adv_tx_desc *tx_desc = NULL;
+ union e1000_adv_tx_desc *tx_desc;
struct igb_buffer *buffer_info;
u32 olinfo_status = 0, cmd_type_len;
- unsigned int i;
+ unsigned int i = tx_ring->next_to_use;
cmd_type_len = (E1000_ADVTXD_DTYP_DATA | E1000_ADVTXD_DCMD_IFCS |
E1000_ADVTXD_DCMD_DEXT);
@@ -3254,27 +3674,28 @@ static inline void igb_tx_queue_adv(struct igb_adapter *adapter,
olinfo_status |= E1000_TXD_POPTS_TXSM << 8;
}
- if ((adapter->flags & IGB_FLAG_NEED_CTX_IDX) &&
- (tx_flags & (IGB_TX_FLAGS_CSUM | IGB_TX_FLAGS_TSO |
+ if ((tx_ring->flags & IGB_RING_FLAG_TX_CTX_IDX) &&
+ (tx_flags & (IGB_TX_FLAGS_CSUM |
+ IGB_TX_FLAGS_TSO |
IGB_TX_FLAGS_VLAN)))
- olinfo_status |= tx_ring->queue_index << 4;
+ olinfo_status |= tx_ring->reg_idx << 4;
olinfo_status |= ((paylen - hdr_len) << E1000_ADVTXD_PAYLEN_SHIFT);
- i = tx_ring->next_to_use;
- while (count--) {
+ do {
buffer_info = &tx_ring->buffer_info[i];
tx_desc = E1000_TX_DESC_ADV(*tx_ring, i);
tx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma);
tx_desc->read.cmd_type_len =
cpu_to_le32(cmd_type_len | buffer_info->length);
tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
+ count--;
i++;
if (i == tx_ring->count)
i = 0;
- }
+ } while (count > 0);
- tx_desc->read.cmd_type_len |= cpu_to_le32(adapter->txd_cmd);
+ tx_desc->read.cmd_type_len |= cpu_to_le32(IGB_ADVTXD_DCMD);
/* Force memory writes to complete before letting h/w
* know there are new descriptors to fetch. (Only
* applicable for weak-ordered memory model archs,
@@ -3282,16 +3703,15 @@ static inline void igb_tx_queue_adv(struct igb_adapter *adapter,
wmb();
tx_ring->next_to_use = i;
- writel(i, adapter->hw.hw_addr + tx_ring->tail);
+ writel(i, tx_ring->tail);
/* we need this if more than one processor can write to our tail
* at a time, it syncronizes IO on IA64/Altix systems */
mmiowb();
}
-static int __igb_maybe_stop_tx(struct net_device *netdev,
- struct igb_ring *tx_ring, int size)
+static int __igb_maybe_stop_tx(struct igb_ring *tx_ring, int size)
{
- struct igb_adapter *adapter = netdev_priv(netdev);
+ struct net_device *netdev = tx_ring->netdev;
netif_stop_subqueue(netdev, tx_ring->queue_index);
@@ -3307,66 +3727,43 @@ static int __igb_maybe_stop_tx(struct net_device *netdev,
/* A reprieve! */
netif_wake_subqueue(netdev, tx_ring->queue_index);
- ++adapter->restart_queue;
+ tx_ring->tx_stats.restart_queue++;
return 0;
}
-static int igb_maybe_stop_tx(struct net_device *netdev,
- struct igb_ring *tx_ring, int size)
+static int igb_maybe_stop_tx(struct igb_ring *tx_ring, int size)
{
if (igb_desc_unused(tx_ring) >= size)
return 0;
- return __igb_maybe_stop_tx(netdev, tx_ring, size);
+ return __igb_maybe_stop_tx(tx_ring, size);
}
-static netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb,
- struct net_device *netdev,
- struct igb_ring *tx_ring)
+netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb,
+ struct igb_ring *tx_ring)
{
- struct igb_adapter *adapter = netdev_priv(netdev);
+ struct igb_adapter *adapter = netdev_priv(tx_ring->netdev);
unsigned int first;
unsigned int tx_flags = 0;
u8 hdr_len = 0;
- int count = 0;
- int tso = 0;
- union skb_shared_tx *shtx;
-
- if (test_bit(__IGB_DOWN, &adapter->state)) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
-
- if (skb->len <= 0) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
- }
+ int tso = 0, count;
+ union skb_shared_tx *shtx = skb_tx(skb);
/* need: 1 descriptor per page,
* + 2 desc gap to keep tail from touching head,
* + 1 desc for skb->data,
* + 1 desc for context descriptor,
* otherwise try next time */
- if (igb_maybe_stop_tx(netdev, tx_ring, skb_shinfo(skb)->nr_frags + 4)) {
+ if (igb_maybe_stop_tx(tx_ring, skb_shinfo(skb)->nr_frags + 4)) {
/* this is a hard error */
return NETDEV_TX_BUSY;
}
- /*
- * TODO: check that there currently is no other packet with
- * time stamping in the queue
- *
- * When doing time stamping, keep the connection to the socket
- * a while longer: it is still needed by skb_hwtstamp_tx(),
- * called either in igb_tx_hwtstamp() or by our caller when
- * doing software time stamping.
- */
- shtx = skb_tx(skb);
if (unlikely(shtx->hardware)) {
shtx->in_progress = 1;
tx_flags |= IGB_TX_FLAGS_TSTAMP;
}
- if (adapter->vlgrp && vlan_tx_tag_present(skb)) {
+ if (vlan_tx_tag_present(skb) && adapter->vlgrp) {
tx_flags |= IGB_TX_FLAGS_VLAN;
tx_flags |= (vlan_tx_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT);
}
@@ -3375,37 +3772,38 @@ static netdev_tx_t igb_xmit_frame_ring_adv(struct sk_buff *skb,
tx_flags |= IGB_TX_FLAGS_IPV4;
first = tx_ring->next_to_use;
- tso = skb_is_gso(skb) ? igb_tso_adv(adapter, tx_ring, skb, tx_flags,
- &hdr_len) : 0;
+ if (skb_is_gso(skb)) {
+ tso = igb_tso_adv(tx_ring, skb, tx_flags, &hdr_len);
- if (tso < 0) {
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
+ if (tso < 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
}
if (tso)
tx_flags |= IGB_TX_FLAGS_TSO;
- else if (igb_tx_csum_adv(adapter, tx_ring, skb, tx_flags) &&
+ else if (igb_tx_csum_adv(tx_ring, skb, tx_flags) &&
(skb->ip_summed == CHECKSUM_PARTIAL))
tx_flags |= IGB_TX_FLAGS_CSUM;
/*
- * count reflects descriptors mapped, if 0 then mapping error
+ * count reflects descriptors mapped, if 0 or less then mapping error
* has occured and we need to rewind the descriptor queue
*/
- count = igb_tx_map_adv(adapter, tx_ring, skb, first);
-
- if (count) {
- igb_tx_queue_adv(adapter, tx_ring, tx_flags, count,
- skb->len, hdr_len);
- /* Make sure there is space in the ring for the next send. */
- igb_maybe_stop_tx(netdev, tx_ring, MAX_SKB_FRAGS + 4);
- } else {
+ count = igb_tx_map_adv(tx_ring, skb, first);
+ if (!count) {
dev_kfree_skb_any(skb);
tx_ring->buffer_info[first].time_stamp = 0;
tx_ring->next_to_use = first;
+ return NETDEV_TX_OK;
}
+ igb_tx_queue_adv(tx_ring, tx_flags, count, skb->len, hdr_len);
+
+ /* Make sure there is space in the ring for the next send. */
+ igb_maybe_stop_tx(tx_ring, MAX_SKB_FRAGS + 4);
+
return NETDEV_TX_OK;
}
@@ -3414,8 +3812,18 @@ static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb,
{
struct igb_adapter *adapter = netdev_priv(netdev);
struct igb_ring *tx_ring;
-
int r_idx = 0;
+
+ if (test_bit(__IGB_DOWN, &adapter->state)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (skb->len <= 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
r_idx = skb->queue_mapping & (IGB_ABS_MAX_TX_QUEUES - 1);
tx_ring = adapter->multi_tx_table[r_idx];
@@ -3423,7 +3831,7 @@ static netdev_tx_t igb_xmit_frame_adv(struct sk_buff *skb,
* to a flow. Right now, performance is impacted slightly negatively
* if using multiple tx queues. If the stack breaks away from a
* single qdisc implementation, we can look at this again. */
- return igb_xmit_frame_ring_adv(skb, netdev, tx_ring);
+ return igb_xmit_frame_ring_adv(skb, tx_ring);
}
/**
@@ -3437,6 +3845,10 @@ static void igb_tx_timeout(struct net_device *netdev)
/* Do the reset outside of interrupt context */
adapter->tx_timeout_count++;
+
+ if (hw->mac.type == e1000_82580)
+ hw->dev_spec._82575.global_device_reset = true;
+
schedule_work(&adapter->reset_task);
wr32(E1000_EICS,
(adapter->eims_enable_mask & ~adapter->eims_other));
@@ -3459,10 +3871,8 @@ static void igb_reset_task(struct work_struct *work)
**/
static struct net_device_stats *igb_get_stats(struct net_device *netdev)
{
- struct igb_adapter *adapter = netdev_priv(netdev);
-
/* only return the current stats */
- return &adapter->net_stats;
+ return &netdev->stats;
}
/**
@@ -3475,16 +3885,17 @@ static struct net_device_stats *igb_get_stats(struct net_device *netdev)
static int igb_change_mtu(struct net_device *netdev, int new_mtu)
{
struct igb_adapter *adapter = netdev_priv(netdev);
+ struct pci_dev *pdev = adapter->pdev;
int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+ u32 rx_buffer_len, i;
- if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) ||
- (max_frame > MAX_JUMBO_FRAME_SIZE)) {
- dev_err(&adapter->pdev->dev, "Invalid MTU setting\n");
+ if ((new_mtu < 68) || (max_frame > MAX_JUMBO_FRAME_SIZE)) {
+ dev_err(&pdev->dev, "Invalid MTU setting\n");
return -EINVAL;
}
if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) {
- dev_err(&adapter->pdev->dev, "MTU > 9216 not supported.\n");
+ dev_err(&pdev->dev, "MTU > 9216 not supported.\n");
return -EINVAL;
}
@@ -3493,8 +3904,6 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
/* igb_down has a dependency on max_frame_size */
adapter->max_frame_size = max_frame;
- if (netif_running(netdev))
- igb_down(adapter);
/* NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN
* means we reserve 2 more, this pushes us to allocate from the next
@@ -3502,35 +3911,23 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
* i.e. RXBUFFER_2048 --> size-4096 slab
*/
- if (max_frame <= IGB_RXBUFFER_256)
- adapter->rx_buffer_len = IGB_RXBUFFER_256;
- else if (max_frame <= IGB_RXBUFFER_512)
- adapter->rx_buffer_len = IGB_RXBUFFER_512;
- else if (max_frame <= IGB_RXBUFFER_1024)
- adapter->rx_buffer_len = IGB_RXBUFFER_1024;
- else if (max_frame <= IGB_RXBUFFER_2048)
- adapter->rx_buffer_len = IGB_RXBUFFER_2048;
+ if (max_frame <= IGB_RXBUFFER_1024)
+ rx_buffer_len = IGB_RXBUFFER_1024;
+ else if (max_frame <= MAXIMUM_ETHERNET_VLAN_SIZE)
+ rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
else
-#if (PAGE_SIZE / 2) > IGB_RXBUFFER_16384
- adapter->rx_buffer_len = IGB_RXBUFFER_16384;
-#else
- adapter->rx_buffer_len = PAGE_SIZE / 2;
-#endif
+ rx_buffer_len = IGB_RXBUFFER_128;
- /* if sr-iov is enabled we need to force buffer size to 1K or larger */
- if (adapter->vfs_allocated_count &&
- (adapter->rx_buffer_len < IGB_RXBUFFER_1024))
- adapter->rx_buffer_len = IGB_RXBUFFER_1024;
-
- /* adjust allocation if LPE protects us, and we aren't using SBP */
- if ((max_frame == ETH_FRAME_LEN + ETH_FCS_LEN) ||
- (max_frame == MAXIMUM_ETHERNET_VLAN_SIZE))
- adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE;
+ if (netif_running(netdev))
+ igb_down(adapter);
- dev_info(&adapter->pdev->dev, "changing MTU from %d to %d\n",
+ dev_info(&pdev->dev, "changing MTU from %d to %d\n",
netdev->mtu, new_mtu);
netdev->mtu = new_mtu;
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ adapter->rx_ring[i].rx_buffer_len = rx_buffer_len;
+
if (netif_running(netdev))
igb_up(adapter);
else
@@ -3548,9 +3945,13 @@ static int igb_change_mtu(struct net_device *netdev, int new_mtu)
void igb_update_stats(struct igb_adapter *adapter)
{
+ struct net_device_stats *net_stats = igb_get_stats(adapter->netdev);
struct e1000_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
+ u32 rnbc;
u16 phy_tmp;
+ int i;
+ u64 bytes, packets;
#define PHY_IDLE_ERROR_COUNT_MASK 0x00FF
@@ -3563,6 +3964,29 @@ void igb_update_stats(struct igb_adapter *adapter)
if (pci_channel_offline(pdev))
return;
+ bytes = 0;
+ packets = 0;
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ u32 rqdpc_tmp = rd32(E1000_RQDPC(i)) & 0x0FFF;
+ adapter->rx_ring[i].rx_stats.drops += rqdpc_tmp;
+ net_stats->rx_fifo_errors += rqdpc_tmp;
+ bytes += adapter->rx_ring[i].rx_stats.bytes;
+ packets += adapter->rx_ring[i].rx_stats.packets;
+ }
+
+ net_stats->rx_bytes = bytes;
+ net_stats->rx_packets = packets;
+
+ bytes = 0;
+ packets = 0;
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ bytes += adapter->tx_ring[i].tx_stats.bytes;
+ packets += adapter->tx_ring[i].tx_stats.packets;
+ }
+ net_stats->tx_bytes = bytes;
+ net_stats->tx_packets = packets;
+
+ /* read stats registers */
adapter->stats.crcerrs += rd32(E1000_CRCERRS);
adapter->stats.gprc += rd32(E1000_GPRC);
adapter->stats.gorc += rd32(E1000_GORCL);
@@ -3595,7 +4019,9 @@ void igb_update_stats(struct igb_adapter *adapter)
adapter->stats.gptc += rd32(E1000_GPTC);
adapter->stats.gotc += rd32(E1000_GOTCL);
rd32(E1000_GOTCH); /* clear GOTCL */
- adapter->stats.rnbc += rd32(E1000_RNBC);
+ rnbc = rd32(E1000_RNBC);
+ adapter->stats.rnbc += rnbc;
+ net_stats->rx_fifo_errors += rnbc;
adapter->stats.ruc += rd32(E1000_RUC);
adapter->stats.rfc += rd32(E1000_RFC);
adapter->stats.rjc += rd32(E1000_RJC);
@@ -3614,7 +4040,6 @@ void igb_update_stats(struct igb_adapter *adapter)
adapter->stats.bptc += rd32(E1000_BPTC);
/* used for adaptive IFS */
-
hw->mac.tx_packet_delta = rd32(E1000_TPT);
adapter->stats.tpt += hw->mac.tx_packet_delta;
hw->mac.collision_delta = rd32(E1000_COLC);
@@ -3637,56 +4062,29 @@ void igb_update_stats(struct igb_adapter *adapter)
adapter->stats.icrxdmtc += rd32(E1000_ICRXDMTC);
/* Fill out the OS statistics structure */
- adapter->net_stats.multicast = adapter->stats.mprc;
- adapter->net_stats.collisions = adapter->stats.colc;
+ net_stats->multicast = adapter->stats.mprc;
+ net_stats->collisions = adapter->stats.colc;
/* Rx Errors */
- if (hw->mac.type != e1000_82575) {
- u32 rqdpc_tmp;
- u64 rqdpc_total = 0;
- int i;
- /* Read out drops stats per RX queue. Notice RQDPC (Receive
- * Queue Drop Packet Count) stats only gets incremented, if
- * the DROP_EN but it set (in the SRRCTL register for that
- * queue). If DROP_EN bit is NOT set, then the some what
- * equivalent count is stored in RNBC (not per queue basis).
- * Also note the drop count is due to lack of available
- * descriptors.
- */
- for (i = 0; i < adapter->num_rx_queues; i++) {
- rqdpc_tmp = rd32(E1000_RQDPC(i)) & 0xFFF;
- adapter->rx_ring[i].rx_stats.drops += rqdpc_tmp;
- rqdpc_total += adapter->rx_ring[i].rx_stats.drops;
- }
- adapter->net_stats.rx_fifo_errors = rqdpc_total;
- }
-
- /* Note RNBC (Receive No Buffers Count) is an not an exact
- * drop count as the hardware FIFO might save the day. Thats
- * one of the reason for saving it in rx_fifo_errors, as its
- * potentially not a true drop.
- */
- adapter->net_stats.rx_fifo_errors += adapter->stats.rnbc;
-
/* RLEC on some newer hardware can be incorrect so build
* our own version based on RUC and ROC */
- adapter->net_stats.rx_errors = adapter->stats.rxerrc +
+ net_stats->rx_errors = adapter->stats.rxerrc +
adapter->stats.crcerrs + adapter->stats.algnerrc +
adapter->stats.ruc + adapter->stats.roc +
adapter->stats.cexterr;
- adapter->net_stats.rx_length_errors = adapter->stats.ruc +
- adapter->stats.roc;
- adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
- adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc;
- adapter->net_stats.rx_missed_errors = adapter->stats.mpc;
+ net_stats->rx_length_errors = adapter->stats.ruc +
+ adapter->stats.roc;
+ net_stats->rx_crc_errors = adapter->stats.crcerrs;
+ net_stats->rx_frame_errors = adapter->stats.algnerrc;
+ net_stats->rx_missed_errors = adapter->stats.mpc;
/* Tx Errors */
- adapter->net_stats.tx_errors = adapter->stats.ecol +
- adapter->stats.latecol;
- adapter->net_stats.tx_aborted_errors = adapter->stats.ecol;
- adapter->net_stats.tx_window_errors = adapter->stats.latecol;
- adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs;
+ net_stats->tx_errors = adapter->stats.ecol +
+ adapter->stats.latecol;
+ net_stats->tx_aborted_errors = adapter->stats.ecol;
+ net_stats->tx_window_errors = adapter->stats.latecol;
+ net_stats->tx_carrier_errors = adapter->stats.tncrs;
/* Tx Dropped needs to be maintained elsewhere */
@@ -3707,14 +4105,12 @@ void igb_update_stats(struct igb_adapter *adapter)
static irqreturn_t igb_msix_other(int irq, void *data)
{
- struct net_device *netdev = data;
- struct igb_adapter *adapter = netdev_priv(netdev);
+ struct igb_adapter *adapter = data;
struct e1000_hw *hw = &adapter->hw;
u32 icr = rd32(E1000_ICR);
-
/* reading ICR causes bit 31 of EICR to be cleared */
- if(icr & E1000_ICR_DOUTSYNC) {
+ if (icr & E1000_ICR_DOUTSYNC) {
/* HW is reporting DMA is out of sync */
adapter->stats.doosync++;
}
@@ -3730,125 +4126,90 @@ static irqreturn_t igb_msix_other(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC | E1000_IMS_VMMB);
+ if (adapter->vfs_allocated_count)
+ wr32(E1000_IMS, E1000_IMS_LSC |
+ E1000_IMS_VMMB |
+ E1000_IMS_DOUTSYNC);
+ else
+ wr32(E1000_IMS, E1000_IMS_LSC | E1000_IMS_DOUTSYNC);
wr32(E1000_EIMS, adapter->eims_other);
return IRQ_HANDLED;
}
-static irqreturn_t igb_msix_tx(int irq, void *data)
+static void igb_write_itr(struct igb_q_vector *q_vector)
{
- struct igb_ring *tx_ring = data;
- struct igb_adapter *adapter = tx_ring->adapter;
- struct e1000_hw *hw = &adapter->hw;
+ u32 itr_val = q_vector->itr_val & 0x7FFC;
-#ifdef CONFIG_IGB_DCA
- if (adapter->flags & IGB_FLAG_DCA_ENABLED)
- igb_update_tx_dca(tx_ring);
-#endif
+ if (!q_vector->set_itr)
+ return;
- tx_ring->total_bytes = 0;
- tx_ring->total_packets = 0;
+ if (!itr_val)
+ itr_val = 0x4;
- /* auto mask will automatically reenable the interrupt when we write
- * EICS */
- if (!igb_clean_tx_irq(tx_ring))
- /* Ring was not completely cleaned, so fire another interrupt */
- wr32(E1000_EICS, tx_ring->eims_value);
+ if (q_vector->itr_shift)
+ itr_val |= itr_val << q_vector->itr_shift;
else
- wr32(E1000_EIMS, tx_ring->eims_value);
+ itr_val |= 0x8000000;
- return IRQ_HANDLED;
-}
-
-static void igb_write_itr(struct igb_ring *ring)
-{
- struct e1000_hw *hw = &ring->adapter->hw;
- if ((ring->adapter->itr_setting & 3) && ring->set_itr) {
- switch (hw->mac.type) {
- case e1000_82576:
- wr32(ring->itr_register, ring->itr_val |
- 0x80000000);
- break;
- default:
- wr32(ring->itr_register, ring->itr_val |
- (ring->itr_val << 16));
- break;
- }
- ring->set_itr = 0;
- }
+ writel(itr_val, q_vector->itr_register);
+ q_vector->set_itr = 0;
}
-static irqreturn_t igb_msix_rx(int irq, void *data)
+static irqreturn_t igb_msix_ring(int irq, void *data)
{
- struct igb_ring *rx_ring = data;
-
- /* Write the ITR value calculated at the end of the
- * previous interrupt.
- */
+ struct igb_q_vector *q_vector = data;
- igb_write_itr(rx_ring);
+ /* Write the ITR value calculated from the previous interrupt. */
+ igb_write_itr(q_vector);
- if (napi_schedule_prep(&rx_ring->napi))
- __napi_schedule(&rx_ring->napi);
+ napi_schedule(&q_vector->napi);
-#ifdef CONFIG_IGB_DCA
- if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED)
- igb_update_rx_dca(rx_ring);
-#endif
- return IRQ_HANDLED;
+ return IRQ_HANDLED;
}
#ifdef CONFIG_IGB_DCA
-static void igb_update_rx_dca(struct igb_ring *rx_ring)
+static void igb_update_dca(struct igb_q_vector *q_vector)
{
- u32 dca_rxctrl;
- struct igb_adapter *adapter = rx_ring->adapter;
+ struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw;
int cpu = get_cpu();
- int q = rx_ring->reg_idx;
- if (rx_ring->cpu != cpu) {
- dca_rxctrl = rd32(E1000_DCA_RXCTRL(q));
- if (hw->mac.type == e1000_82576) {
- dca_rxctrl &= ~E1000_DCA_RXCTRL_CPUID_MASK_82576;
- dca_rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) <<
- E1000_DCA_RXCTRL_CPUID_SHIFT;
+ if (q_vector->cpu == cpu)
+ goto out_no_update;
+
+ if (q_vector->tx_ring) {
+ int q = q_vector->tx_ring->reg_idx;
+ u32 dca_txctrl = rd32(E1000_DCA_TXCTRL(q));
+ if (hw->mac.type == e1000_82575) {
+ dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK;
+ dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu);
} else {
+ dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK_82576;
+ dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) <<
+ E1000_DCA_TXCTRL_CPUID_SHIFT;
+ }
+ dca_txctrl |= E1000_DCA_TXCTRL_DESC_DCA_EN;
+ wr32(E1000_DCA_TXCTRL(q), dca_txctrl);
+ }
+ if (q_vector->rx_ring) {
+ int q = q_vector->rx_ring->reg_idx;
+ u32 dca_rxctrl = rd32(E1000_DCA_RXCTRL(q));
+ if (hw->mac.type == e1000_82575) {
dca_rxctrl &= ~E1000_DCA_RXCTRL_CPUID_MASK;
dca_rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu);
+ } else {
+ dca_rxctrl &= ~E1000_DCA_RXCTRL_CPUID_MASK_82576;
+ dca_rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) <<
+ E1000_DCA_RXCTRL_CPUID_SHIFT;
}
dca_rxctrl |= E1000_DCA_RXCTRL_DESC_DCA_EN;
dca_rxctrl |= E1000_DCA_RXCTRL_HEAD_DCA_EN;
dca_rxctrl |= E1000_DCA_RXCTRL_DATA_DCA_EN;
wr32(E1000_DCA_RXCTRL(q), dca_rxctrl);
- rx_ring->cpu = cpu;
- }
- put_cpu();
-}
-
-static void igb_update_tx_dca(struct igb_ring *tx_ring)
-{
- u32 dca_txctrl;
- struct igb_adapter *adapter = tx_ring->adapter;
- struct e1000_hw *hw = &adapter->hw;
- int cpu = get_cpu();
- int q = tx_ring->reg_idx;
-
- if (tx_ring->cpu != cpu) {
- dca_txctrl = rd32(E1000_DCA_TXCTRL(q));
- if (hw->mac.type == e1000_82576) {
- dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK_82576;
- dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu) <<
- E1000_DCA_TXCTRL_CPUID_SHIFT;
- } else {
- dca_txctrl &= ~E1000_DCA_TXCTRL_CPUID_MASK;
- dca_txctrl |= dca3_get_tag(&adapter->pdev->dev, cpu);
- }
- dca_txctrl |= E1000_DCA_TXCTRL_DESC_DCA_EN;
- wr32(E1000_DCA_TXCTRL(q), dca_txctrl);
- tx_ring->cpu = cpu;
}
+ q_vector->cpu = cpu;
+out_no_update:
put_cpu();
}
@@ -3863,13 +4224,10 @@ static void igb_setup_dca(struct igb_adapter *adapter)
/* Always use CB2 mode, difference is masked in the CB driver. */
wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_CB2);
- for (i = 0; i < adapter->num_tx_queues; i++) {
- adapter->tx_ring[i].cpu = -1;
- igb_update_tx_dca(&adapter->tx_ring[i]);
- }
- for (i = 0; i < adapter->num_rx_queues; i++) {
- adapter->rx_ring[i].cpu = -1;
- igb_update_rx_dca(&adapter->rx_ring[i]);
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ q_vector->cpu = -1;
+ igb_update_dca(q_vector);
}
}
@@ -3877,6 +4235,7 @@ static int __igb_notify_dca(struct device *dev, void *data)
{
struct net_device *netdev = dev_get_drvdata(dev);
struct igb_adapter *adapter = netdev_priv(netdev);
+ struct pci_dev *pdev = adapter->pdev;
struct e1000_hw *hw = &adapter->hw;
unsigned long event = *(unsigned long *)data;
@@ -3885,12 +4244,9 @@ static int __igb_notify_dca(struct device *dev, void *data)
/* if already enabled, don't do it again */
if (adapter->flags & IGB_FLAG_DCA_ENABLED)
break;
- /* Always use CB2 mode, difference is masked
- * in the CB driver. */
- wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_CB2);
if (dca_add_requester(dev) == 0) {
adapter->flags |= IGB_FLAG_DCA_ENABLED;
- dev_info(&adapter->pdev->dev, "DCA enabled\n");
+ dev_info(&pdev->dev, "DCA enabled\n");
igb_setup_dca(adapter);
break;
}
@@ -3898,9 +4254,9 @@ static int __igb_notify_dca(struct device *dev, void *data)
case DCA_PROVIDER_REMOVE:
if (adapter->flags & IGB_FLAG_DCA_ENABLED) {
/* without this a class_device is left
- * hanging around in the sysfs model */
+ * hanging around in the sysfs model */
dca_remove_requester(dev);
- dev_info(&adapter->pdev->dev, "DCA disabled\n");
+ dev_info(&pdev->dev, "DCA disabled\n");
adapter->flags &= ~IGB_FLAG_DCA_ENABLED;
wr32(E1000_DCA_CTRL, E1000_DCA_CTRL_DCA_MODE_DISABLE);
}
@@ -3930,12 +4286,51 @@ static void igb_ping_all_vfs(struct igb_adapter *adapter)
for (i = 0 ; i < adapter->vfs_allocated_count; i++) {
ping = E1000_PF_CONTROL_MSG;
- if (adapter->vf_data[i].clear_to_send)
+ if (adapter->vf_data[i].flags & IGB_VF_FLAG_CTS)
ping |= E1000_VT_MSGTYPE_CTS;
igb_write_mbx(hw, &ping, 1, i);
}
}
+static int igb_set_vf_promisc(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 vmolr = rd32(E1000_VMOLR(vf));
+ struct vf_data_storage *vf_data = &adapter->vf_data[vf];
+
+ vf_data->flags |= ~(IGB_VF_FLAG_UNI_PROMISC |
+ IGB_VF_FLAG_MULTI_PROMISC);
+ vmolr &= ~(E1000_VMOLR_ROPE | E1000_VMOLR_ROMPE | E1000_VMOLR_MPME);
+
+ if (*msgbuf & E1000_VF_SET_PROMISC_MULTICAST) {
+ vmolr |= E1000_VMOLR_MPME;
+ *msgbuf &= ~E1000_VF_SET_PROMISC_MULTICAST;
+ } else {
+ /*
+ * if we have hashes and we are clearing a multicast promisc
+ * flag we need to write the hashes to the MTA as this step
+ * was previously skipped
+ */
+ if (vf_data->num_vf_mc_hashes > 30) {
+ vmolr |= E1000_VMOLR_MPME;
+ } else if (vf_data->num_vf_mc_hashes) {
+ int j;
+ vmolr |= E1000_VMOLR_ROMPE;
+ for (j = 0; j < vf_data->num_vf_mc_hashes; j++)
+ igb_mta_set(hw, vf_data->vf_mc_hashes[j]);
+ }
+ }
+
+ wr32(E1000_VMOLR(vf), vmolr);
+
+ /* there are flags left unprocessed, likely not supported */
+ if (*msgbuf & E1000_VT_MSGINFO_MASK)
+ return -EINVAL;
+
+ return 0;
+
+}
+
static int igb_set_vf_multicasts(struct igb_adapter *adapter,
u32 *msgbuf, u32 vf)
{
@@ -3944,18 +4339,17 @@ static int igb_set_vf_multicasts(struct igb_adapter *adapter,
struct vf_data_storage *vf_data = &adapter->vf_data[vf];
int i;
- /* only up to 30 hash values supported */
- if (n > 30)
- n = 30;
-
- /* salt away the number of multi cast addresses assigned
+ /* salt away the number of multicast addresses assigned
* to this VF for later use to restore when the PF multi cast
* list changes
*/
vf_data->num_vf_mc_hashes = n;
- /* VFs are limited to using the MTA hash table for their multicast
- * addresses */
+ /* only up to 30 hash values supported */
+ if (n > 30)
+ n = 30;
+
+ /* store the hashes for later use */
for (i = 0; i < n; i++)
vf_data->vf_mc_hashes[i] = hash_list[i];
@@ -3972,9 +4366,20 @@ static void igb_restore_vf_multicasts(struct igb_adapter *adapter)
int i, j;
for (i = 0; i < adapter->vfs_allocated_count; i++) {
+ u32 vmolr = rd32(E1000_VMOLR(i));
+ vmolr &= ~(E1000_VMOLR_ROMPE | E1000_VMOLR_MPME);
+
vf_data = &adapter->vf_data[i];
- for (j = 0; j < vf_data->num_vf_mc_hashes; j++)
- igb_mta_set(hw, vf_data->vf_mc_hashes[j]);
+
+ if ((vf_data->num_vf_mc_hashes > 30) ||
+ (vf_data->flags & IGB_VF_FLAG_MULTI_PROMISC)) {
+ vmolr |= E1000_VMOLR_MPME;
+ } else if (vf_data->num_vf_mc_hashes) {
+ vmolr |= E1000_VMOLR_ROMPE;
+ for (j = 0; j < vf_data->num_vf_mc_hashes; j++)
+ igb_mta_set(hw, vf_data->vf_mc_hashes[j]);
+ }
+ wr32(E1000_VMOLR(i), vmolr);
}
}
@@ -4012,7 +4417,11 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
struct e1000_hw *hw = &adapter->hw;
u32 reg, i;
- /* It is an error to call this function when VFs are not enabled */
+ /* The vlvf table only exists on 82576 hardware and newer */
+ if (hw->mac.type < e1000_82576)
+ return -1;
+
+ /* we only need to do this if VMDq is enabled */
if (!adapter->vfs_allocated_count)
return -1;
@@ -4042,16 +4451,12 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
/* if !enabled we need to set this up in vfta */
if (!(reg & E1000_VLVF_VLANID_ENABLE)) {
- /* add VID to filter table, if bit already set
- * PF must have added it outside of table */
- if (igb_vfta_set(hw, vid, true))
- reg |= 1 << (E1000_VLVF_POOLSEL_SHIFT +
- adapter->vfs_allocated_count);
+ /* add VID to filter table */
+ igb_vfta_set(hw, vid, true);
reg |= E1000_VLVF_VLANID_ENABLE;
}
reg &= ~E1000_VLVF_VLANID_MASK;
reg |= vid;
-
wr32(E1000_VLVF(i), reg);
/* do not modify RLPML for PF devices */
@@ -4067,8 +4472,8 @@ static s32 igb_vlvf_set(struct igb_adapter *adapter, u32 vid, bool add, u32 vf)
reg |= size;
wr32(E1000_VMOLR(vf), reg);
}
- adapter->vf_data[vf].vlans_enabled++;
+ adapter->vf_data[vf].vlans_enabled++;
return 0;
}
} else {
@@ -4110,15 +4515,14 @@ static int igb_set_vf_vlan(struct igb_adapter *adapter, u32 *msgbuf, u32 vf)
return igb_vlvf_set(adapter, vid, add, vf);
}
-static inline void igb_vf_reset_event(struct igb_adapter *adapter, u32 vf)
+static inline void igb_vf_reset(struct igb_adapter *adapter, u32 vf)
{
- struct e1000_hw *hw = &adapter->hw;
-
- /* disable mailbox functionality for vf */
- adapter->vf_data[vf].clear_to_send = false;
+ /* clear all flags */
+ adapter->vf_data[vf].flags = 0;
+ adapter->vf_data[vf].last_nack = jiffies;
/* reset offloads to defaults */
- igb_set_vmolr(hw, vf);
+ igb_set_vmolr(adapter, vf);
/* reset vlans for device */
igb_clear_vf_vfta(adapter, vf);
@@ -4130,7 +4534,18 @@ static inline void igb_vf_reset_event(struct igb_adapter *adapter, u32 vf)
igb_set_rx_mode(adapter->netdev);
}
-static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf)
+static void igb_vf_reset_event(struct igb_adapter *adapter, u32 vf)
+{
+ unsigned char *vf_mac = adapter->vf_data[vf].vf_mac_addresses;
+
+ /* generate a new mac address as we were hotplug removed/added */
+ random_ether_addr(vf_mac);
+
+ /* process remaining reset events */
+ igb_vf_reset(adapter, vf);
+}
+
+static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf)
{
struct e1000_hw *hw = &adapter->hw;
unsigned char *vf_mac = adapter->vf_data[vf].vf_mac_addresses;
@@ -4139,11 +4554,10 @@ static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf)
u8 *addr = (u8 *)(&msgbuf[1]);
/* process all the same items cleared in a function level reset */
- igb_vf_reset_event(adapter, vf);
+ igb_vf_reset(adapter, vf);
/* set vf mac address */
- igb_rar_set(hw, vf_mac, rar_entry);
- igb_set_rah_pool(hw, vf, rar_entry);
+ igb_rar_set_qsel(adapter, vf_mac, rar_entry, vf);
/* enable transmit and receive for vf */
reg = rd32(E1000_VFTE);
@@ -4151,8 +4565,7 @@ static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf)
reg = rd32(E1000_VFRE);
wr32(E1000_VFRE, reg | (1 << vf));
- /* enable mailbox functionality for vf */
- adapter->vf_data[vf].clear_to_send = true;
+ adapter->vf_data[vf].flags = IGB_VF_FLAG_CTS;
/* reply to reset with ack and vf mac address */
msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK;
@@ -4162,66 +4575,45 @@ static inline void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf)
static int igb_set_vf_mac_addr(struct igb_adapter *adapter, u32 *msg, int vf)
{
- unsigned char *addr = (char *)&msg[1];
- int err = -1;
+ unsigned char *addr = (char *)&msg[1];
+ int err = -1;
- if (is_valid_ether_addr(addr))
- err = igb_set_vf_mac(adapter, vf, addr);
-
- return err;
+ if (is_valid_ether_addr(addr))
+ err = igb_set_vf_mac(adapter, vf, addr);
+ return err;
}
static void igb_rcv_ack_from_vf(struct igb_adapter *adapter, u32 vf)
{
struct e1000_hw *hw = &adapter->hw;
+ struct vf_data_storage *vf_data = &adapter->vf_data[vf];
u32 msg = E1000_VT_MSGTYPE_NACK;
/* if device isn't clear to send it shouldn't be reading either */
- if (!adapter->vf_data[vf].clear_to_send)
+ if (!(vf_data->flags & IGB_VF_FLAG_CTS) &&
+ time_after(jiffies, vf_data->last_nack + (2 * HZ))) {
igb_write_mbx(hw, &msg, 1, vf);
-}
-
-
-static void igb_msg_task(struct igb_adapter *adapter)
-{
- struct e1000_hw *hw = &adapter->hw;
- u32 vf;
-
- for (vf = 0; vf < adapter->vfs_allocated_count; vf++) {
- /* process any reset requests */
- if (!igb_check_for_rst(hw, vf)) {
- adapter->vf_data[vf].clear_to_send = false;
- igb_vf_reset_event(adapter, vf);
- }
-
- /* process any messages pending */
- if (!igb_check_for_msg(hw, vf))
- igb_rcv_msg_from_vf(adapter, vf);
-
- /* process any acks */
- if (!igb_check_for_ack(hw, vf))
- igb_rcv_ack_from_vf(adapter, vf);
-
+ vf_data->last_nack = jiffies;
}
}
-static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
+static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
{
- u32 mbx_size = E1000_VFMAILBOX_SIZE;
- u32 msgbuf[mbx_size];
+ struct pci_dev *pdev = adapter->pdev;
+ u32 msgbuf[E1000_VFMAILBOX_SIZE];
struct e1000_hw *hw = &adapter->hw;
+ struct vf_data_storage *vf_data = &adapter->vf_data[vf];
s32 retval;
- retval = igb_read_mbx(hw, msgbuf, mbx_size, vf);
+ retval = igb_read_mbx(hw, msgbuf, E1000_VFMAILBOX_SIZE, vf);
if (retval)
- dev_err(&adapter->pdev->dev,
- "Error receiving message from VF\n");
+ dev_err(&pdev->dev, "Error receiving message from VF\n");
/* this is a message we already processed, do nothing */
if (msgbuf[0] & (E1000_VT_MSGTYPE_ACK | E1000_VT_MSGTYPE_NACK))
- return retval;
+ return;
/*
* until the vf completes a reset it should not be
@@ -4230,20 +4622,25 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
if (msgbuf[0] == E1000_VF_RESET) {
igb_vf_reset_msg(adapter, vf);
-
- return retval;
+ return;
}
- if (!adapter->vf_data[vf].clear_to_send) {
- msgbuf[0] |= E1000_VT_MSGTYPE_NACK;
- igb_write_mbx(hw, msgbuf, 1, vf);
- return retval;
+ if (!(vf_data->flags & IGB_VF_FLAG_CTS)) {
+ msgbuf[0] = E1000_VT_MSGTYPE_NACK;
+ if (time_after(jiffies, vf_data->last_nack + (2 * HZ))) {
+ igb_write_mbx(hw, msgbuf, 1, vf);
+ vf_data->last_nack = jiffies;
+ }
+ return;
}
switch ((msgbuf[0] & 0xFFFF)) {
case E1000_VF_SET_MAC_ADDR:
retval = igb_set_vf_mac_addr(adapter, msgbuf, vf);
break;
+ case E1000_VF_SET_PROMISC:
+ retval = igb_set_vf_promisc(adapter, msgbuf, vf);
+ break;
case E1000_VF_SET_MULTICAST:
retval = igb_set_vf_multicasts(adapter, msgbuf, vf);
break;
@@ -4254,7 +4651,7 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
retval = igb_set_vf_vlan(adapter, msgbuf, vf);
break;
default:
- dev_err(&adapter->pdev->dev, "Unhandled Msg %08x\n", msgbuf[0]);
+ dev_err(&pdev->dev, "Unhandled Msg %08x\n", msgbuf[0]);
retval = -1;
break;
}
@@ -4268,8 +4665,53 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
msgbuf[0] |= E1000_VT_MSGTYPE_CTS;
igb_write_mbx(hw, msgbuf, 1, vf);
+}
- return retval;
+static void igb_msg_task(struct igb_adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ u32 vf;
+
+ for (vf = 0; vf < adapter->vfs_allocated_count; vf++) {
+ /* process any reset requests */
+ if (!igb_check_for_rst(hw, vf))
+ igb_vf_reset_event(adapter, vf);
+
+ /* process any messages pending */
+ if (!igb_check_for_msg(hw, vf))
+ igb_rcv_msg_from_vf(adapter, vf);
+
+ /* process any acks */
+ if (!igb_check_for_ack(hw, vf))
+ igb_rcv_ack_from_vf(adapter, vf);
+ }
+}
+
+/**
+ * igb_set_uta - Set unicast filter table address
+ * @adapter: board private structure
+ *
+ * The unicast table address is a register array of 32-bit registers.
+ * The table is meant to be used in a way similar to how the MTA is used
+ * however due to certain limitations in the hardware it is necessary to
+ * set all the hash bits to 1 and use the VMOLR ROPE bit as a promiscous
+ * enable bit to allow vlan tag stripping when promiscous mode is enabled
+ **/
+static void igb_set_uta(struct igb_adapter *adapter)
+{
+ struct e1000_hw *hw = &adapter->hw;
+ int i;
+
+ /* The UTA table only exists on 82576 hardware and newer */
+ if (hw->mac.type < e1000_82576)
+ return;
+
+ /* we only need to do this if VMDq is enabled */
+ if (!adapter->vfs_allocated_count)
+ return;
+
+ for (i = 0; i < hw->mac.uta_reg_count; i++)
+ array_wr32(E1000_UTA, i, ~0);
}
/**
@@ -4279,15 +4721,15 @@ static int igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf)
**/
static irqreturn_t igb_intr_msi(int irq, void *data)
{
- struct net_device *netdev = data;
- struct igb_adapter *adapter = netdev_priv(netdev);
+ struct igb_adapter *adapter = data;
+ struct igb_q_vector *q_vector = adapter->q_vector[0];
struct e1000_hw *hw = &adapter->hw;
/* read ICR disables interrupts using IAM */
u32 icr = rd32(E1000_ICR);
- igb_write_itr(adapter->rx_ring);
+ igb_write_itr(q_vector);
- if(icr & E1000_ICR_DOUTSYNC) {
+ if (icr & E1000_ICR_DOUTSYNC) {
/* HW is reporting DMA is out of sync */
adapter->stats.doosync++;
}
@@ -4298,7 +4740,7 @@ static irqreturn_t igb_intr_msi(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- napi_schedule(&adapter->rx_ring[0].napi);
+ napi_schedule(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -4310,8 +4752,8 @@ static irqreturn_t igb_intr_msi(int irq, void *data)
**/
static irqreturn_t igb_intr(int irq, void *data)
{
- struct net_device *netdev = data;
- struct igb_adapter *adapter = netdev_priv(netdev);
+ struct igb_adapter *adapter = data;
+ struct igb_q_vector *q_vector = adapter->q_vector[0];
struct e1000_hw *hw = &adapter->hw;
/* Interrupt Auto-Mask...upon reading ICR, interrupts are masked. No
* need for the IMC write */
@@ -4319,14 +4761,14 @@ static irqreturn_t igb_intr(int irq, void *data)
if (!icr)
return IRQ_NONE; /* Not our interrupt */
- igb_write_itr(adapter->rx_ring);
+ igb_write_itr(q_vector);
/* IMS will not auto-mask if INT_ASSERTED is not set, and if it is
* not set, then the adapter didn't send an interrupt */
if (!(icr & E1000_ICR_INT_ASSERTED))
return IRQ_NONE;
- if(icr & E1000_ICR_DOUTSYNC) {
+ if (icr & E1000_ICR_DOUTSYNC) {
/* HW is reporting DMA is out of sync */
adapter->stats.doosync++;
}
@@ -4338,26 +4780,27 @@ static irqreturn_t igb_intr(int irq, void *data)
mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
- napi_schedule(&adapter->rx_ring[0].napi);
+ napi_schedule(&q_vector->napi);
return IRQ_HANDLED;
}
-static inline void igb_rx_irq_enable(struct igb_ring *rx_ring)
+static inline void igb_ring_irq_enable(struct igb_q_vector *q_vector)
{
- struct igb_adapter *adapter = rx_ring->adapter;
+ struct igb_adapter *adapter = q_vector->adapter;
struct e1000_hw *hw = &adapter->hw;
- if (adapter->itr_setting & 3) {
- if (adapter->num_rx_queues == 1)
+ if ((q_vector->rx_ring && (adapter->rx_itr_setting & 3)) ||
+ (!q_vector->rx_ring && (adapter->tx_itr_setting & 3))) {
+ if (!adapter->msix_entries)
igb_set_itr(adapter);
else
- igb_update_ring_itr(rx_ring);
+ igb_update_ring_itr(q_vector);
}
if (!test_bit(__IGB_DOWN, &adapter->state)) {
if (adapter->msix_entries)
- wr32(E1000_EIMS, rx_ring->eims_value);
+ wr32(E1000_EIMS, q_vector->eims_value);
else
igb_irq_enable(adapter);
}
@@ -4370,76 +4813,101 @@ static inline void igb_rx_irq_enable(struct igb_ring *rx_ring)
**/
static int igb_poll(struct napi_struct *napi, int budget)
{
- struct igb_ring *rx_ring = container_of(napi, struct igb_ring, napi);
- int work_done = 0;
+ struct igb_q_vector *q_vector = container_of(napi,
+ struct igb_q_vector,
+ napi);
+ int tx_clean_complete = 1, work_done = 0;
#ifdef CONFIG_IGB_DCA
- if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED)
- igb_update_rx_dca(rx_ring);
+ if (q_vector->adapter->flags & IGB_FLAG_DCA_ENABLED)
+ igb_update_dca(q_vector);
#endif
- igb_clean_rx_irq_adv(rx_ring, &work_done, budget);
+ if (q_vector->tx_ring)
+ tx_clean_complete = igb_clean_tx_irq(q_vector);
- if (rx_ring->buddy) {
-#ifdef CONFIG_IGB_DCA
- if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED)
- igb_update_tx_dca(rx_ring->buddy);
-#endif
- if (!igb_clean_tx_irq(rx_ring->buddy))
- work_done = budget;
- }
+ if (q_vector->rx_ring)
+ igb_clean_rx_irq_adv(q_vector, &work_done, budget);
+
+ if (!tx_clean_complete)
+ work_done = budget;
/* If not enough Rx work done, exit the polling mode */
if (work_done < budget) {
napi_complete(napi);
- igb_rx_irq_enable(rx_ring);
+ igb_ring_irq_enable(q_vector);
}
return work_done;
}
/**
- * igb_hwtstamp - utility function which checks for TX time stamp
+ * igb_systim_to_hwtstamp - convert system time value to hw timestamp
* @adapter: board private structure
+ * @shhwtstamps: timestamp structure to update
+ * @regval: unsigned 64bit system time value.
+ *
+ * We need to convert the system time value stored in the RX/TXSTMP registers
+ * into a hwtstamp which can be used by the upper level timestamping functions
+ */
+static void igb_systim_to_hwtstamp(struct igb_adapter *adapter,
+ struct skb_shared_hwtstamps *shhwtstamps,
+ u64 regval)
+{
+ u64 ns;
+
+ /*
+ * The 82580 starts with 1ns at bit 0 in RX/TXSTMPL, shift this up to
+ * 24 to match clock shift we setup earlier.
+ */
+ if (adapter->hw.mac.type == e1000_82580)
+ regval <<= IGB_82580_TSYNC_SHIFT;
+
+ ns = timecounter_cyc2time(&adapter->clock, regval);
+ timecompare_update(&adapter->compare, ns);
+ memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
+ shhwtstamps->hwtstamp = ns_to_ktime(ns);
+ shhwtstamps->syststamp = timecompare_transform(&adapter->compare, ns);
+}
+
+/**
+ * igb_tx_hwtstamp - utility function which checks for TX time stamp
+ * @q_vector: pointer to q_vector containing needed info
* @skb: packet that was just sent
*
* If we were asked to do hardware stamping and such a time stamp is
* available, then it must have been for this skb here because we only
* allow only one such packet into the queue.
*/
-static void igb_tx_hwtstamp(struct igb_adapter *adapter, struct sk_buff *skb)
+static void igb_tx_hwtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb)
{
+ struct igb_adapter *adapter = q_vector->adapter;
union skb_shared_tx *shtx = skb_tx(skb);
struct e1000_hw *hw = &adapter->hw;
+ struct skb_shared_hwtstamps shhwtstamps;
+ u64 regval;
- if (unlikely(shtx->hardware)) {
- u32 valid = rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID;
- if (valid) {
- u64 regval = rd32(E1000_TXSTMPL);
- u64 ns;
- struct skb_shared_hwtstamps shhwtstamps;
-
- memset(&shhwtstamps, 0, sizeof(shhwtstamps));
- regval |= (u64)rd32(E1000_TXSTMPH) << 32;
- ns = timecounter_cyc2time(&adapter->clock,
- regval);
- timecompare_update(&adapter->compare, ns);
- shhwtstamps.hwtstamp = ns_to_ktime(ns);
- shhwtstamps.syststamp =
- timecompare_transform(&adapter->compare, ns);
- skb_tstamp_tx(skb, &shhwtstamps);
- }
- }
+ /* if skb does not support hw timestamp or TX stamp not valid exit */
+ if (likely(!shtx->hardware) ||
+ !(rd32(E1000_TSYNCTXCTL) & E1000_TSYNCTXCTL_VALID))
+ return;
+
+ regval = rd32(E1000_TXSTMPL);
+ regval |= (u64)rd32(E1000_TXSTMPH) << 32;
+
+ igb_systim_to_hwtstamp(adapter, &shhwtstamps, regval);
+ skb_tstamp_tx(skb, &shhwtstamps);
}
/**
* igb_clean_tx_irq - Reclaim resources after transmit completes
- * @adapter: board private structure
+ * @q_vector: pointer to q_vector containing needed info
* returns true if ring is completely cleaned
**/
-static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
+static bool igb_clean_tx_irq(struct igb_q_vector *q_vector)
{
- struct igb_adapter *adapter = tx_ring->adapter;
- struct net_device *netdev = adapter->netdev;
+ struct igb_adapter *adapter = q_vector->adapter;
+ struct igb_ring *tx_ring = q_vector->tx_ring;
+ struct net_device *netdev = tx_ring->netdev;
struct e1000_hw *hw = &adapter->hw;
struct igb_buffer *buffer_info;
struct sk_buff *skb;
@@ -4470,10 +4938,10 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
total_packets += segs;
total_bytes += bytecount;
- igb_tx_hwtstamp(adapter, skb);
+ igb_tx_hwtstamp(q_vector, skb);
}
- igb_unmap_and_free_tx_resource(adapter, buffer_info);
+ igb_unmap_and_free_tx_resource(tx_ring, buffer_info);
tx_desc->wb.status = 0;
i++;
@@ -4496,7 +4964,7 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
if (__netif_subqueue_stopped(netdev, tx_ring->queue_index) &&
!(test_bit(__IGB_DOWN, &adapter->state))) {
netif_wake_subqueue(netdev, tx_ring->queue_index);
- ++adapter->restart_queue;
+ tx_ring->tx_stats.restart_queue++;
}
}
@@ -4506,12 +4974,11 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
tx_ring->detect_tx_hung = false;
if (tx_ring->buffer_info[i].time_stamp &&
time_after(jiffies, tx_ring->buffer_info[i].time_stamp +
- (adapter->tx_timeout_factor * HZ))
- && !(rd32(E1000_STATUS) &
- E1000_STATUS_TXOFF)) {
+ (adapter->tx_timeout_factor * HZ)) &&
+ !(rd32(E1000_STATUS) & E1000_STATUS_TXOFF)) {
/* detected Tx unit hang */
- dev_err(&adapter->pdev->dev,
+ dev_err(&tx_ring->pdev->dev,
"Detected Tx Unit Hang\n"
" Tx Queue <%d>\n"
" TDH <%x>\n"
@@ -4524,11 +4991,11 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
" jiffies <%lx>\n"
" desc.status <%x>\n",
tx_ring->queue_index,
- readl(adapter->hw.hw_addr + tx_ring->head),
- readl(adapter->hw.hw_addr + tx_ring->tail),
+ readl(tx_ring->head),
+ readl(tx_ring->tail),
tx_ring->next_to_use,
tx_ring->next_to_clean,
- tx_ring->buffer_info[i].time_stamp,
+ tx_ring->buffer_info[eop].time_stamp,
eop,
jiffies,
eop_desc->wb.status);
@@ -4539,43 +5006,38 @@ static bool igb_clean_tx_irq(struct igb_ring *tx_ring)
tx_ring->total_packets += total_packets;
tx_ring->tx_stats.bytes += total_bytes;
tx_ring->tx_stats.packets += total_packets;
- adapter->net_stats.tx_bytes += total_bytes;
- adapter->net_stats.tx_packets += total_packets;
return (count < tx_ring->count);
}
/**
* igb_receive_skb - helper function to handle rx indications
- * @ring: pointer to receive ring receving this packet
- * @status: descriptor status field as written by hardware
- * @rx_desc: receive descriptor containing vlan and type information.
- * @skb: pointer to sk_buff to be indicated to stack
+ * @q_vector: structure containing interrupt and ring information
+ * @skb: packet to send up
+ * @vlan_tag: vlan tag for packet
**/
-static void igb_receive_skb(struct igb_ring *ring, u8 status,
- union e1000_adv_rx_desc * rx_desc,
- struct sk_buff *skb)
-{
- struct igb_adapter * adapter = ring->adapter;
- bool vlan_extracted = (adapter->vlgrp && (status & E1000_RXD_STAT_VP));
-
- skb_record_rx_queue(skb, ring->queue_index);
- if (vlan_extracted)
- vlan_gro_receive(&ring->napi, adapter->vlgrp,
- le16_to_cpu(rx_desc->wb.upper.vlan),
- skb);
+static void igb_receive_skb(struct igb_q_vector *q_vector,
+ struct sk_buff *skb,
+ u16 vlan_tag)
+{
+ struct igb_adapter *adapter = q_vector->adapter;
+
+ if (vlan_tag)
+ vlan_gro_receive(&q_vector->napi, adapter->vlgrp,
+ vlan_tag, skb);
else
- napi_gro_receive(&ring->napi, skb);
+ napi_gro_receive(&q_vector->napi, skb);
}
-static inline void igb_rx_checksum_adv(struct igb_adapter *adapter,
+static inline void igb_rx_checksum_adv(struct igb_ring *ring,
u32 status_err, struct sk_buff *skb)
{
skb->ip_summed = CHECKSUM_NONE;
/* Ignore Checksum bit is set or checksum is disabled through ethtool */
- if ((status_err & E1000_RXD_STAT_IXSM) ||
- (adapter->flags & IGB_FLAG_RX_CSUM_DISABLED))
+ if (!(ring->flags & IGB_RING_FLAG_RX_CSUM) ||
+ (status_err & E1000_RXD_STAT_IXSM))
return;
+
/* TCP/UDP checksum error bit is set */
if (status_err &
(E1000_RXDEXT_STATERR_TCPE | E1000_RXDEXT_STATERR_IPE)) {
@@ -4584,9 +5046,10 @@ static inline void igb_rx_checksum_adv(struct igb_adapter *adapter,
* L4E bit is set incorrectly on 64 byte (60 byte w/o crc)
* packets, (aka let the stack check the crc32c)
*/
- if (!((adapter->hw.mac.type == e1000_82576) &&
- (skb->len == 60)))
- adapter->hw_csum_err++;
+ if ((skb->len == 60) &&
+ (ring->flags & IGB_RING_FLAG_RX_SCTP_CSUM))
+ ring->rx_stats.csum_err++;
+
/* let the stack verify checksum errors */
return;
}
@@ -4594,11 +5057,38 @@ static inline void igb_rx_checksum_adv(struct igb_adapter *adapter,
if (status_err & (E1000_RXD_STAT_TCPCS | E1000_RXD_STAT_UDPCS))
skb->ip_summed = CHECKSUM_UNNECESSARY;
- dev_dbg(&adapter->pdev->dev, "cksum success: bits %08X\n", status_err);
- adapter->hw_csum_good++;
+ dev_dbg(&ring->pdev->dev, "cksum success: bits %08X\n", status_err);
}
-static inline u16 igb_get_hlen(struct igb_adapter *adapter,
+static inline void igb_rx_hwtstamp(struct igb_q_vector *q_vector, u32 staterr,
+ struct sk_buff *skb)
+{
+ struct igb_adapter *adapter = q_vector->adapter;
+ struct e1000_hw *hw = &adapter->hw;
+ u64 regval;
+
+ /*
+ * If this bit is set, then the RX registers contain the time stamp. No
+ * other packet will be time stamped until we read these registers, so
+ * read the registers to make them available again. Because only one
+ * packet can be time stamped at a time, we know that the register
+ * values must belong to this one here and therefore we don't need to
+ * compare any of the additional attributes stored for it.
+ *
+ * If nothing went wrong, then it should have a skb_shared_tx that we
+ * can turn into a skb_shared_hwtstamps.
+ */
+ if (likely(!(staterr & E1000_RXDADV_STAT_TS)))
+ return;
+ if (!(rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID))
+ return;
+
+ regval = rd32(E1000_RXSTMPL);
+ regval |= (u64)rd32(E1000_RXSTMPH) << 32;
+
+ igb_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
+}
+static inline u16 igb_get_hlen(struct igb_ring *rx_ring,
union e1000_adv_rx_desc *rx_desc)
{
/* HW will not DMA in data larger than the given buffer, even if it
@@ -4607,27 +5097,28 @@ static inline u16 igb_get_hlen(struct igb_adapter *adapter,
*/
u16 hlen = (le16_to_cpu(rx_desc->wb.lower.lo_dword.hdr_info) &
E1000_RXDADV_HDRBUFLEN_MASK) >> E1000_RXDADV_HDRBUFLEN_SHIFT;
- if (hlen > adapter->rx_ps_hdr_size)
- hlen = adapter->rx_ps_hdr_size;
+ if (hlen > rx_ring->rx_buffer_len)
+ hlen = rx_ring->rx_buffer_len;
return hlen;
}
-static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
- int *work_done, int budget)
+static bool igb_clean_rx_irq_adv(struct igb_q_vector *q_vector,
+ int *work_done, int budget)
{
- struct igb_adapter *adapter = rx_ring->adapter;
- struct net_device *netdev = adapter->netdev;
- struct e1000_hw *hw = &adapter->hw;
- struct pci_dev *pdev = adapter->pdev;
+ struct igb_ring *rx_ring = q_vector->rx_ring;
+ struct net_device *netdev = rx_ring->netdev;
+ struct pci_dev *pdev = rx_ring->pdev;
union e1000_adv_rx_desc *rx_desc , *next_rxd;
struct igb_buffer *buffer_info , *next_buffer;
struct sk_buff *skb;
bool cleaned = false;
int cleaned_count = 0;
+ int current_node = numa_node_id();
unsigned int total_bytes = 0, total_packets = 0;
unsigned int i;
u32 staterr;
u16 length;
+ u16 vlan_tag;
i = rx_ring->next_to_clean;
buffer_info = &rx_ring->buffer_info[i];
@@ -4646,6 +5137,7 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
i++;
if (i == rx_ring->count)
i = 0;
+
next_rxd = E1000_RX_DESC_ADV(*rx_ring, i);
prefetch(next_rxd);
next_buffer = &rx_ring->buffer_info[i];
@@ -4654,23 +5146,16 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
cleaned = true;
cleaned_count++;
- /* this is the fast path for the non-packet split case */
- if (!adapter->rx_ps_hdr_size) {
- pci_unmap_single(pdev, buffer_info->dma,
- adapter->rx_buffer_len,
- PCI_DMA_FROMDEVICE);
- buffer_info->dma = 0;
- skb_put(skb, length);
- goto send_up;
- }
-
if (buffer_info->dma) {
- u16 hlen = igb_get_hlen(adapter, rx_desc);
pci_unmap_single(pdev, buffer_info->dma,
- adapter->rx_ps_hdr_size,
+ rx_ring->rx_buffer_len,
PCI_DMA_FROMDEVICE);
buffer_info->dma = 0;
- skb_put(skb, hlen);
+ if (rx_ring->rx_buffer_len >= IGB_RXBUFFER_1024) {
+ skb_put(skb, length);
+ goto send_up;
+ }
+ skb_put(skb, igb_get_hlen(rx_ring, rx_desc));
}
if (length) {
@@ -4683,15 +5168,14 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
buffer_info->page_offset,
length);
- if ((adapter->rx_buffer_len > (PAGE_SIZE / 2)) ||
- (page_count(buffer_info->page) != 1))
+ if ((page_count(buffer_info->page) != 1) ||
+ (page_to_nid(buffer_info->page) != current_node))
buffer_info->page = NULL;
else
get_page(buffer_info->page);
skb->len += length;
skb->data_len += length;
-
skb->truesize += length;
}
@@ -4703,60 +5187,24 @@ static bool igb_clean_rx_irq_adv(struct igb_ring *rx_ring,
goto next_desc;
}
send_up:
- /*
- * If this bit is set, then the RX registers contain
- * the time stamp. No other packet will be time
- * stamped until we read these registers, so read the
- * registers to make them available again. Because
- * only one packet can be time stamped at a time, we
- * know that the register values must belong to this
- * one here and therefore we don't need to compare
- * any of the additional attributes stored for it.
- *
- * If nothing went wrong, then it should have a
- * skb_shared_tx that we can turn into a
- * skb_shared_hwtstamps.
- *
- * TODO: can time stamping be triggered (thus locking
- * the registers) without the packet reaching this point
- * here? In that case RX time stamping would get stuck.
- *
- * TODO: in "time stamp all packets" mode this bit is
- * not set. Need a global flag for this mode and then
- * always read the registers. Cannot be done without
- * a race condition.
- */
- if (unlikely(staterr & E1000_RXD_STAT_TS)) {
- u64 regval;
- u64 ns;
- struct skb_shared_hwtstamps *shhwtstamps =
- skb_hwtstamps(skb);
-
- WARN(!(rd32(E1000_TSYNCRXCTL) & E1000_TSYNCRXCTL_VALID),
- "igb: no RX time stamp available for time stamped packet");
- regval = rd32(E1000_RXSTMPL);
- regval |= (u64)rd32(E1000_RXSTMPH) << 32;
- ns = timecounter_cyc2time(&adapter->clock, regval);
- timecompare_update(&adapter->compare, ns);
- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
- shhwtstamps->hwtstamp = ns_to_ktime(ns);
- shhwtstamps->syststamp =
- timecompare_transform(&adapter->compare, ns);
- }
-
if (staterr & E1000_RXDEXT_ERR_FRAME_ERR_MASK) {
dev_kfree_skb_irq(skb);
goto next_desc;
}
+ igb_rx_hwtstamp(q_vector, staterr, skb);
total_bytes += skb->len;
total_packets++;
- igb_rx_checksum_adv(adapter, staterr, skb);
+ igb_rx_checksum_adv(rx_ring, staterr, skb);
skb->protocol = eth_type_trans(skb, netdev);
+ skb_record_rx_queue(skb, rx_ring->queue_index);
- igb_receive_skb(rx_ring, staterr, rx_desc, skb);
+ vlan_tag = ((staterr & E1000_RXD_STAT_VP) ?
+ le16_to_cpu(rx_desc->wb.upper.vlan) : 0);
+
+ igb_receive_skb(q_vector, skb, vlan_tag);
next_desc:
rx_desc->wb.upper.status_error = 0;
@@ -4783,8 +5231,6 @@ next_desc:
rx_ring->total_bytes += total_bytes;
rx_ring->rx_stats.packets += total_packets;
rx_ring->rx_stats.bytes += total_bytes;
- adapter->net_stats.rx_bytes += total_bytes;
- adapter->net_stats.rx_packets += total_packets;
return cleaned;
}
@@ -4792,12 +5238,9 @@ next_desc:
* igb_alloc_rx_buffers_adv - Replace used receive buffers; packet split
* @adapter: address of board private structure
**/
-static void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring,
- int cleaned_count)
+void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring, int cleaned_count)
{
- struct igb_adapter *adapter = rx_ring->adapter;
- struct net_device *netdev = adapter->netdev;
- struct pci_dev *pdev = adapter->pdev;
+ struct net_device *netdev = rx_ring->netdev;
union e1000_adv_rx_desc *rx_desc;
struct igb_buffer *buffer_info;
struct sk_buff *skb;
@@ -4807,19 +5250,16 @@ static void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring,
i = rx_ring->next_to_use;
buffer_info = &rx_ring->buffer_info[i];
- if (adapter->rx_ps_hdr_size)
- bufsz = adapter->rx_ps_hdr_size;
- else
- bufsz = adapter->rx_buffer_len;
+ bufsz = rx_ring->rx_buffer_len;
while (cleaned_count--) {
rx_desc = E1000_RX_DESC_ADV(*rx_ring, i);
- if (adapter->rx_ps_hdr_size && !buffer_info->page_dma) {
+ if ((bufsz < IGB_RXBUFFER_1024) && !buffer_info->page_dma) {
if (!buffer_info->page) {
- buffer_info->page = alloc_page(GFP_ATOMIC);
+ buffer_info->page = netdev_alloc_page(netdev);
if (!buffer_info->page) {
- adapter->alloc_rx_buff_failed++;
+ rx_ring->rx_stats.alloc_failed++;
goto no_buffers;
}
buffer_info->page_offset = 0;
@@ -4827,39 +5267,48 @@ static void igb_alloc_rx_buffers_adv(struct igb_ring *rx_ring,
buffer_info->page_offset ^= PAGE_SIZE / 2;
}
buffer_info->page_dma =
- pci_map_page(pdev, buffer_info->page,
+ pci_map_page(rx_ring->pdev, buffer_info->page,
buffer_info->page_offset,
PAGE_SIZE / 2,
PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(rx_ring->pdev,
+ buffer_info->page_dma)) {
+ buffer_info->page_dma = 0;
+ rx_ring->rx_stats.alloc_failed++;
+ goto no_buffers;
+ }
}
- if (!buffer_info->skb) {
- skb = netdev_alloc_skb(netdev, bufsz + NET_IP_ALIGN);
+ skb = buffer_info->skb;
+ if (!skb) {
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
if (!skb) {
- adapter->alloc_rx_buff_failed++;
+ rx_ring->rx_stats.alloc_failed++;
goto no_buffers;
}
- /* Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
- buffer_info->dma = pci_map_single(pdev, skb->data,
+ }
+ if (!buffer_info->dma) {
+ buffer_info->dma = pci_map_single(rx_ring->pdev,
+ skb->data,
bufsz,
PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(rx_ring->pdev,
+ buffer_info->dma)) {
+ buffer_info->dma = 0;
+ rx_ring->rx_stats.alloc_failed++;
+ goto no_buffers;
+ }
}
/* Refresh the desc even if buffer_addrs didn't change because
* each write-back erases this info. */
- if (adapter->rx_ps_hdr_size) {
+ if (bufsz < IGB_RXBUFFER_1024) {
rx_desc->read.pkt_addr =
cpu_to_le64(buffer_info->page_dma);
rx_desc->read.hdr_addr = cpu_to_le64(buffer_info->dma);
} else {
- rx_desc->read.pkt_addr =
- cpu_to_le64(buffer_info->dma);
+ rx_desc->read.pkt_addr = cpu_to_le64(buffer_info->dma);
rx_desc->read.hdr_addr = 0;
}
@@ -4882,7 +5331,7 @@ no_buffers:
* applicable for weak-ordered memory model archs,
* such as IA-64). */
wmb();
- writel(i, adapter->hw.hw_addr + rx_ring->tail);
+ writel(i, rx_ring->tail);
}
}
@@ -4941,13 +5390,11 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev,
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
struct hwtstamp_config config;
- u32 tsync_tx_ctl_bit = E1000_TSYNCTXCTL_ENABLED;
- u32 tsync_rx_ctl_bit = E1000_TSYNCRXCTL_ENABLED;
- u32 tsync_rx_ctl_type = 0;
+ u32 tsync_tx_ctl = E1000_TSYNCTXCTL_ENABLED;
+ u32 tsync_rx_ctl = E1000_TSYNCRXCTL_ENABLED;
u32 tsync_rx_cfg = 0;
- int is_l4 = 0;
- int is_l2 = 0;
- short port = 319; /* PTP */
+ bool is_l4 = false;
+ bool is_l2 = false;
u32 regval;
if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
@@ -4959,10 +5406,8 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev,
switch (config.tx_type) {
case HWTSTAMP_TX_OFF:
- tsync_tx_ctl_bit = 0;
- break;
+ tsync_tx_ctl = 0;
case HWTSTAMP_TX_ON:
- tsync_tx_ctl_bit = E1000_TSYNCTXCTL_ENABLED;
break;
default:
return -ERANGE;
@@ -4970,7 +5415,7 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev,
switch (config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
- tsync_rx_ctl_bit = 0;
+ tsync_rx_ctl = 0;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
@@ -4981,86 +5426,97 @@ static int igb_hwtstamp_ioctl(struct net_device *netdev,
* possible to time stamp both Sync and Delay_Req messages
* => fall back to time stamping all packets
*/
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_ALL;
+ tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_ALL;
config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L4_V1;
+ tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE;
- is_l4 = 1;
+ is_l4 = true;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L4_V1;
+ tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L4_V1;
tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE;
- is_l4 = 1;
+ is_l4 = true;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
+ tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE;
- is_l2 = 1;
- is_l4 = 1;
+ is_l2 = true;
+ is_l4 = true;
config.rx_filter = HWTSTAMP_FILTER_SOME;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
+ tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_L2_L4_V2;
tsync_rx_cfg = E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE;
- is_l2 = 1;
- is_l4 = 1;
+ is_l2 = true;
+ is_l4 = true;
config.rx_filter = HWTSTAMP_FILTER_SOME;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
- tsync_rx_ctl_type = E1000_TSYNCRXCTL_TYPE_EVENT_V2;
+ tsync_rx_ctl |= E1000_TSYNCRXCTL_TYPE_EVENT_V2;
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
- is_l2 = 1;
+ is_l2 = true;
break;
default:
return -ERANGE;
}
+ if (hw->mac.type == e1000_82575) {
+ if (tsync_rx_ctl | tsync_tx_ctl)
+ return -EINVAL;
+ return 0;
+ }
+
/* enable/disable TX */
regval = rd32(E1000_TSYNCTXCTL);
- regval = (regval & ~E1000_TSYNCTXCTL_ENABLED) | tsync_tx_ctl_bit;
+ regval &= ~E1000_TSYNCTXCTL_ENABLED;
+ regval |= tsync_tx_ctl;
wr32(E1000_TSYNCTXCTL, regval);
- /* enable/disable RX, define which PTP packets are time stamped */
+ /* enable/disable RX */
regval = rd32(E1000_TSYNCRXCTL);
- regval = (regval & ~E1000_TSYNCRXCTL_ENABLED) | tsync_rx_ctl_bit;
- regval = (regval & ~0xE) | tsync_rx_ctl_type;
+ regval &= ~(E1000_TSYNCRXCTL_ENABLED | E1000_TSYNCRXCTL_TYPE_MASK);
+ regval |= tsync_rx_ctl;
wr32(E1000_TSYNCRXCTL, regval);
- wr32(E1000_TSYNCRXCFG, tsync_rx_cfg);
- /*
- * Ethertype Filter Queue Filter[0][15:0] = 0x88F7
- * (Ethertype to filter on)
- * Ethertype Filter Queue Filter[0][26] = 0x1 (Enable filter)
- * Ethertype Filter Queue Filter[0][30] = 0x1 (Enable Timestamping)
- */
- wr32(E1000_ETQF0, is_l2 ? 0x440088f7 : 0);
-
- /* L4 Queue Filter[0]: only filter by source and destination port */
- wr32(E1000_SPQF0, htons(port));
- wr32(E1000_IMIREXT(0), is_l4 ?
- ((1<<12) | (1<<19) /* bypass size and control flags */) : 0);
- wr32(E1000_IMIR(0), is_l4 ?
- (htons(port)
- | (0<<16) /* immediate interrupt disabled */
- | 0 /* (1<<17) bit cleared: do not bypass
- destination port check */)
- : 0);
- wr32(E1000_FTQF0, is_l4 ?
- (0x11 /* UDP */
- | (1<<15) /* VF not compared */
- | (1<<27) /* Enable Timestamping */
- | (7<<28) /* only source port filter enabled,
- source/target address and protocol
- masked */)
- : ((1<<15) | (15<<28) /* all mask bits set = filter not
- enabled */));
+ /* define which PTP packets are time stamped */
+ wr32(E1000_TSYNCRXCFG, tsync_rx_cfg);
+ /* define ethertype filter for timestamped packets */
+ if (is_l2)
+ wr32(E1000_ETQF(3),
+ (E1000_ETQF_FILTER_ENABLE | /* enable filter */
+ E1000_ETQF_1588 | /* enable timestamping */
+ ETH_P_1588)); /* 1588 eth protocol type */
+ else
+ wr32(E1000_ETQF(3), 0);
+
+#define PTP_PORT 319
+ /* L4 Queue Filter[3]: filter by destination port and protocol */
+ if (is_l4) {
+ u32 ftqf = (IPPROTO_UDP /* UDP */
+ | E1000_FTQF_VF_BP /* VF not compared */
+ | E1000_FTQF_1588_TIME_STAMP /* Enable Timestamping */
+ | E1000_FTQF_MASK); /* mask all inputs */
+ ftqf &= ~E1000_FTQF_MASK_PROTO_BP; /* enable protocol check */
+
+ wr32(E1000_IMIR(3), htons(PTP_PORT));
+ wr32(E1000_IMIREXT(3),
+ (E1000_IMIREXT_SIZE_BP | E1000_IMIREXT_CTRL_BP));
+ if (hw->mac.type == e1000_82576) {
+ /* enable source port check */
+ wr32(E1000_SPQF(3), htons(PTP_PORT));
+ ftqf &= ~E1000_FTQF_MASK_SOURCE_PORT_BP;
+ }
+ wr32(E1000_FTQF(3), ftqf);
+ } else {
+ wr32(E1000_FTQF(3), E1000_FTQF_MASK);
+ }
wrfl();
adapter->hwtstamp_config = config;
@@ -5137,21 +5593,15 @@ static void igb_vlan_rx_register(struct net_device *netdev,
ctrl |= E1000_CTRL_VME;
wr32(E1000_CTRL, ctrl);
- /* enable VLAN receive filtering */
+ /* Disable CFI check */
rctl = rd32(E1000_RCTL);
rctl &= ~E1000_RCTL_CFIEN;
wr32(E1000_RCTL, rctl);
- igb_update_mng_vlan(adapter);
} else {
/* disable VLAN tag insert/strip */
ctrl = rd32(E1000_CTRL);
ctrl &= ~E1000_CTRL_VME;
wr32(E1000_CTRL, ctrl);
-
- if (adapter->mng_vlan_id != (u16)IGB_MNG_VLAN_NONE) {
- igb_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
- adapter->mng_vlan_id = IGB_MNG_VLAN_NONE;
- }
}
igb_rlpml_set(adapter);
@@ -5166,16 +5616,11 @@ static void igb_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
struct e1000_hw *hw = &adapter->hw;
int pf_id = adapter->vfs_allocated_count;
- if ((hw->mng_cookie.status &
- E1000_MNG_DHCP_COOKIE_STATUS_VLAN) &&
- (vid == adapter->mng_vlan_id))
- return;
-
- /* add vid to vlvf if sr-iov is enabled,
- * if that fails add directly to filter table */
- if (igb_vlvf_set(adapter, vid, true, pf_id))
- igb_vfta_set(hw, vid, true);
+ /* attempt to add filter to vlvf array */
+ igb_vlvf_set(adapter, vid, true, pf_id);
+ /* add the filter since PF can receive vlans w/o entry in vlvf */
+ igb_vfta_set(hw, vid, true);
}
static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
@@ -5183,6 +5628,7 @@ static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
struct igb_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
int pf_id = adapter->vfs_allocated_count;
+ s32 err;
igb_irq_disable(adapter);
vlan_group_set_device(adapter->vlgrp, vid, NULL);
@@ -5190,17 +5636,11 @@ static void igb_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
if (!test_bit(__IGB_DOWN, &adapter->state))
igb_irq_enable(adapter);
- if ((adapter->hw.mng_cookie.status &
- E1000_MNG_DHCP_COOKIE_STATUS_VLAN) &&
- (vid == adapter->mng_vlan_id)) {
- /* release control to f/w */
- igb_release_hw_control(adapter);
- return;
- }
+ /* remove vlan from VLVF table array */
+ err = igb_vlvf_set(adapter, vid, false, pf_id);
- /* remove vid from vlvf if sr-iov is enabled,
- * if not in vlvf remove from vfta */
- if (igb_vlvf_set(adapter, vid, false, pf_id))
+ /* if vid was not present in VLVF just remove it from table */
+ if (err)
igb_vfta_set(hw, vid, false);
}
@@ -5220,6 +5660,7 @@ static void igb_restore_vlan(struct igb_adapter *adapter)
int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx)
{
+ struct pci_dev *pdev = adapter->pdev;
struct e1000_mac_info *mac = &adapter->hw.mac;
mac->autoneg = 0;
@@ -5243,8 +5684,7 @@ int igb_set_spd_dplx(struct igb_adapter *adapter, u16 spddplx)
break;
case SPEED_1000 + DUPLEX_HALF: /* not supported */
default:
- dev_err(&adapter->pdev->dev,
- "Unsupported Speed/Duplex configuration\n");
+ dev_err(&pdev->dev, "Unsupported Speed/Duplex configuration\n");
return -EINVAL;
}
return 0;
@@ -5266,9 +5706,7 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake)
if (netif_running(netdev))
igb_close(netdev);
- igb_reset_interrupt_capability(adapter);
-
- igb_free_queues(adapter);
+ igb_clear_interrupt_scheme(adapter);
#ifdef CONFIG_PM
retval = pci_save_state(pdev);
@@ -5300,7 +5738,7 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake)
wr32(E1000_CTRL, ctrl);
/* Allow time for pending master requests to run */
- igb_disable_pcie_master(&adapter->hw);
+ igb_disable_pcie_master(hw);
wr32(E1000_WUC, E1000_WUC_PME_EN);
wr32(E1000_WUFC, wufc);
@@ -5363,9 +5801,7 @@ static int igb_resume(struct pci_dev *pdev)
pci_enable_wake(pdev, PCI_D3hot, 0);
pci_enable_wake(pdev, PCI_D3cold, 0);
- igb_set_interrupt_capability(adapter);
-
- if (igb_alloc_queues(adapter)) {
+ if (igb_init_interrupt_scheme(adapter)) {
dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
return -ENOMEM;
}
@@ -5417,22 +5853,16 @@ static void igb_netpoll(struct net_device *netdev)
int i;
if (!adapter->msix_entries) {
+ struct igb_q_vector *q_vector = adapter->q_vector[0];
igb_irq_disable(adapter);
- napi_schedule(&adapter->rx_ring[0].napi);
+ napi_schedule(&q_vector->napi);
return;
}
- for (i = 0; i < adapter->num_tx_queues; i++) {
- struct igb_ring *tx_ring = &adapter->tx_ring[i];
- wr32(E1000_EIMC, tx_ring->eims_value);
- igb_clean_tx_irq(tx_ring);
- wr32(E1000_EIMS, tx_ring->eims_value);
- }
-
- for (i = 0; i < adapter->num_rx_queues; i++) {
- struct igb_ring *rx_ring = &adapter->rx_ring[i];
- wr32(E1000_EIMC, rx_ring->eims_value);
- napi_schedule(&rx_ring->napi);
+ for (i = 0; i < adapter->num_q_vectors; i++) {
+ struct igb_q_vector *q_vector = adapter->q_vector[i];
+ wr32(E1000_EIMC, q_vector->eims_value);
+ napi_schedule(&q_vector->napi);
}
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
@@ -5532,6 +5962,33 @@ static void igb_io_resume(struct pci_dev *pdev)
igb_get_hw_control(adapter);
}
+static void igb_rar_set_qsel(struct igb_adapter *adapter, u8 *addr, u32 index,
+ u8 qsel)
+{
+ u32 rar_low, rar_high;
+ struct e1000_hw *hw = &adapter->hw;
+
+ /* HW expects these in little endian so we reverse the byte order
+ * from network order (big endian) to little endian
+ */
+ rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) |
+ ((u32) addr[2] << 16) | ((u32) addr[3] << 24));
+ rar_high = ((u32) addr[4] | ((u32) addr[5] << 8));
+
+ /* Indicate to hardware the Address is Valid. */
+ rar_high |= E1000_RAH_AV;
+
+ if (hw->mac.type == e1000_82575)
+ rar_high |= E1000_RAH_POOL_1 * qsel;
+ else
+ rar_high |= E1000_RAH_POOL_1 << qsel;
+
+ wr32(E1000_RAL(index), rar_low);
+ wrfl();
+ wr32(E1000_RAH(index), rar_high);
+ wrfl();
+}
+
static int igb_set_vf_mac(struct igb_adapter *adapter,
int vf, unsigned char *mac_addr)
{
@@ -5542,8 +5999,7 @@ static int igb_set_vf_mac(struct igb_adapter *adapter,
memcpy(adapter->vf_data[vf].vf_mac_addresses, mac_addr, ETH_ALEN);
- igb_rar_set(hw, mac_addr, rar_entry);
- igb_set_rah_pool(hw, vf, rar_entry);
+ igb_rar_set_qsel(adapter, mac_addr, rar_entry, vf);
return 0;
}
@@ -5551,19 +6007,29 @@ static int igb_set_vf_mac(struct igb_adapter *adapter,
static void igb_vmm_control(struct igb_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
- u32 reg_data;
+ u32 reg;
- if (!adapter->vfs_allocated_count)
+ /* replication is not supported for 82575 */
+ if (hw->mac.type == e1000_82575)
return;
- /* VF's need PF reset indication before they
- * can send/receive mail */
- reg_data = rd32(E1000_CTRL_EXT);
- reg_data |= E1000_CTRL_EXT_PFRSTD;
- wr32(E1000_CTRL_EXT, reg_data);
+ /* enable replication vlan tag stripping */
+ reg = rd32(E1000_RPLOLR);
+ reg |= E1000_RPLOLR_STRVLAN;
+ wr32(E1000_RPLOLR, reg);
- igb_vmdq_set_loopback_pf(hw, true);
- igb_vmdq_set_replication_pf(hw, true);
+ /* notify HW that the MAC is adding vlan tags */
+ reg = rd32(E1000_DTXCTL);
+ reg |= E1000_DTXCTL_VLAN_ADDED;
+ wr32(E1000_DTXCTL, reg);
+
+ if (adapter->vfs_allocated_count) {
+ igb_vmdq_set_loopback_pf(hw, true);
+ igb_vmdq_set_replication_pf(hw, true);
+ } else {
+ igb_vmdq_set_loopback_pf(hw, false);
+ igb_vmdq_set_replication_pf(hw, false);
+ }
}
/* igb_main.c */
diff --git a/drivers/net/igbvf/ethtool.c b/drivers/net/igbvf/ethtool.c
index c68265bd0d1..8afff07ff55 100644
--- a/drivers/net/igbvf/ethtool.c
+++ b/drivers/net/igbvf/ethtool.c
@@ -367,16 +367,6 @@ static int igbvf_link_test(struct igbvf_adapter *adapter, u64 *data)
return *data;
}
-static int igbvf_get_self_test_count(struct net_device *netdev)
-{
- return IGBVF_TEST_LEN;
-}
-
-static int igbvf_get_stats_count(struct net_device *netdev)
-{
- return IGBVF_GLOBAL_STATS_LEN;
-}
-
static void igbvf_diag_test(struct net_device *netdev,
struct ethtool_test *eth_test, u64 *data)
{
@@ -484,6 +474,18 @@ static void igbvf_get_ethtool_stats(struct net_device *netdev,
}
+static int igbvf_get_sset_count(struct net_device *dev, int stringset)
+{
+ switch(stringset) {
+ case ETH_SS_TEST:
+ return IGBVF_TEST_LEN;
+ case ETH_SS_STATS:
+ return IGBVF_GLOBAL_STATS_LEN;
+ default:
+ return -EINVAL;
+ }
+}
+
static void igbvf_get_strings(struct net_device *netdev, u32 stringset,
u8 *data)
{
@@ -532,11 +534,10 @@ static const struct ethtool_ops igbvf_ethtool_ops = {
.get_tso = ethtool_op_get_tso,
.set_tso = igbvf_set_tso,
.self_test = igbvf_diag_test,
+ .get_sset_count = igbvf_get_sset_count,
.get_strings = igbvf_get_strings,
.phys_id = igbvf_phys_id,
.get_ethtool_stats = igbvf_get_ethtool_stats,
- .self_test_count = igbvf_get_self_test_count,
- .get_stats_count = igbvf_get_stats_count,
.get_coalesce = igbvf_get_coalesce,
.set_coalesce = igbvf_set_coalesce,
};
diff --git a/drivers/net/igbvf/igbvf.h b/drivers/net/igbvf/igbvf.h
index 8e9b67ebbf8..3d1ee7a8478 100644
--- a/drivers/net/igbvf/igbvf.h
+++ b/drivers/net/igbvf/igbvf.h
@@ -117,6 +117,7 @@ struct igbvf_buffer {
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
+ u16 mapped_as_page;
};
/* Rx */
struct {
diff --git a/drivers/net/igbvf/netdev.c b/drivers/net/igbvf/netdev.c
index 91024a3cdad..a127620dc65 100644
--- a/drivers/net/igbvf/netdev.c
+++ b/drivers/net/igbvf/netdev.c
@@ -170,18 +170,12 @@ static void igbvf_alloc_rx_buffers(struct igbvf_ring *rx_ring,
}
if (!buffer_info->skb) {
- skb = netdev_alloc_skb(netdev, bufsz + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(netdev, bufsz);
if (!skb) {
adapter->alloc_rx_buff_failed++;
goto no_buffers;
}
- /* Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
buffer_info->dma = pci_map_single(pdev, skb->data,
bufsz,
@@ -372,10 +366,20 @@ next_desc:
static void igbvf_put_txbuf(struct igbvf_adapter *adapter,
struct igbvf_buffer *buffer_info)
{
- buffer_info->dma = 0;
+ if (buffer_info->dma) {
+ if (buffer_info->mapped_as_page)
+ pci_unmap_page(adapter->pdev,
+ buffer_info->dma,
+ buffer_info->length,
+ PCI_DMA_TODEVICE);
+ else
+ pci_unmap_single(adapter->pdev,
+ buffer_info->dma,
+ buffer_info->length,
+ PCI_DMA_TODEVICE);
+ buffer_info->dma = 0;
+ }
if (buffer_info->skb) {
- skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb,
- DMA_TO_DEVICE);
dev_kfree_skb_any(buffer_info->skb);
buffer_info->skb = NULL;
}
@@ -823,8 +827,8 @@ static bool igbvf_clean_tx_irq(struct igbvf_ring *tx_ring)
adapter->detect_tx_hung = false;
if (tx_ring->buffer_info[i].time_stamp &&
time_after(jiffies, tx_ring->buffer_info[i].time_stamp +
- (adapter->tx_timeout_factor * HZ))
- && !(er32(STATUS) & E1000_STATUS_TXOFF)) {
+ (adapter->tx_timeout_factor * HZ)) &&
+ !(er32(STATUS) & E1000_STATUS_TXOFF)) {
tx_desc = IGBVF_TX_DESC_ADV(*tx_ring, i);
/* detected Tx unit hang */
@@ -1049,7 +1053,7 @@ static int igbvf_request_msix(struct igbvf_adapter *adapter)
}
err = request_irq(adapter->msix_entries[vector].vector,
- &igbvf_intr_msix_tx, 0, adapter->tx_ring->name,
+ igbvf_intr_msix_tx, 0, adapter->tx_ring->name,
netdev);
if (err)
goto out;
@@ -1059,7 +1063,7 @@ static int igbvf_request_msix(struct igbvf_adapter *adapter)
vector++;
err = request_irq(adapter->msix_entries[vector].vector,
- &igbvf_intr_msix_rx, 0, adapter->rx_ring->name,
+ igbvf_intr_msix_rx, 0, adapter->rx_ring->name,
netdev);
if (err)
goto out;
@@ -1069,7 +1073,7 @@ static int igbvf_request_msix(struct igbvf_adapter *adapter)
vector++;
err = request_irq(adapter->msix_entries[vector].vector,
- &igbvf_msix_other, 0, netdev->name, netdev);
+ igbvf_msix_other, 0, netdev->name, netdev);
if (err)
goto out;
@@ -2094,27 +2098,24 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter,
unsigned int first)
{
struct igbvf_buffer *buffer_info;
+ struct pci_dev *pdev = adapter->pdev;
unsigned int len = skb_headlen(skb);
unsigned int count = 0, i;
unsigned int f;
- dma_addr_t *map;
i = tx_ring->next_to_use;
- if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) {
- dev_err(&adapter->pdev->dev, "TX DMA map failed\n");
- return 0;
- }
-
- map = skb_shinfo(skb)->dma_maps;
-
buffer_info = &tx_ring->buffer_info[i];
BUG_ON(len >= IGBVF_MAX_DATA_PER_TXD);
buffer_info->length = len;
/* set time_stamp *before* dma to help avoid a possible race */
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- buffer_info->dma = skb_shinfo(skb)->dma_head;
+ buffer_info->dma = pci_map_single(pdev, skb->data, len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
+
for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) {
struct skb_frag_struct *frag;
@@ -2131,14 +2132,44 @@ static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter,
buffer_info->length = len;
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- buffer_info->dma = map[count];
+ buffer_info->mapped_as_page = true;
+ buffer_info->dma = pci_map_page(pdev,
+ frag->page,
+ frag->page_offset,
+ len,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
count++;
}
tx_ring->buffer_info[i].skb = skb;
tx_ring->buffer_info[first].next_to_watch = i;
- return count + 1;
+ return ++count;
+
+dma_error:
+ dev_err(&pdev->dev, "TX DMA map failed\n");
+
+ /* clear timestamp and dma mappings for failed buffer_info mapping */
+ buffer_info->dma = 0;
+ buffer_info->time_stamp = 0;
+ buffer_info->length = 0;
+ buffer_info->next_to_watch = 0;
+ buffer_info->mapped_as_page = false;
+ count--;
+
+ /* clear timestamp and dma mappings for remaining portion of packet */
+ while (count >= 0) {
+ count--;
+ i--;
+ if (i < 0)
+ i += tx_ring->count;
+ buffer_info = &tx_ring->buffer_info[i];
+ igbvf_put_txbuf(adapter, buffer_info);
+ }
+
+ return 0;
}
static inline void igbvf_tx_queue_adv(struct igbvf_adapter *adapter,
diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c
index 9f7b5d4172b..ba8d246d05a 100644
--- a/drivers/net/ipg.c
+++ b/drivers/net/ipg.c
@@ -738,17 +738,12 @@ static int ipg_get_rxbuff(struct net_device *dev, int entry)
IPG_DEBUG_MSG("_get_rxbuff\n");
- skb = netdev_alloc_skb(dev, sp->rxsupport_size + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(dev, sp->rxsupport_size);
if (!skb) {
sp->rx_buff[entry] = NULL;
return -ENOMEM;
}
- /* Adjust the data start location within the buffer to
- * align IP address field to a 16 byte boundary.
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
/* Associate the receive buffer with the IPG NIC. */
skb->dev = dev;
@@ -1756,7 +1751,7 @@ static int ipg_nic_open(struct net_device *dev)
/* Register the interrupt line to be used by the IPG within
* the Linux system.
*/
- rc = request_irq(pdev->irq, &ipg_interrupt_handler, IRQF_SHARED,
+ rc = request_irq(pdev->irq, ipg_interrupt_handler, IRQF_SHARED,
dev->name, dev);
if (rc < 0) {
printk(KERN_INFO "%s: Error when requesting interrupt.\n",
diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c
index eb424681202..9b2eebdbb25 100644
--- a/drivers/net/irda/au1k_ir.c
+++ b/drivers/net/irda/au1k_ir.c
@@ -353,13 +353,13 @@ static int au1k_irda_start(struct net_device *dev)
return retval;
}
- if ((retval = request_irq(AU1000_IRDA_TX_INT, &au1k_irda_interrupt,
+ if ((retval = request_irq(AU1000_IRDA_TX_INT, au1k_irda_interrupt,
0, dev->name, dev))) {
printk(KERN_ERR "%s: unable to get IRQ %d\n",
dev->name, dev->irq);
return retval;
}
- if ((retval = request_irq(AU1000_IRDA_RX_INT, &au1k_irda_interrupt,
+ if ((retval = request_irq(AU1000_IRDA_RX_INT, au1k_irda_interrupt,
0, dev->name, dev))) {
free_irq(AU1000_IRDA_TX_INT, dev);
printk(KERN_ERR "%s: unable to get IRQ %d\n",
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 215adf6377d..e8e33bb9d87 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -852,7 +852,7 @@ static void irda_usb_receive(struct urb *urb)
* hot unplug of the dongle...
* Lowest effective timer is 10ms...
* Jean II */
- self->rx_defer_timer.function = &irda_usb_rx_defer_expired;
+ self->rx_defer_timer.function = irda_usb_rx_defer_expired;
self->rx_defer_timer.data = (unsigned long) urb;
mod_timer(&self->rx_defer_timer, jiffies + (10 * HZ / 1000));
return;
@@ -1124,11 +1124,11 @@ static int stir421x_patch_device(struct irda_usb_cb *self)
* The actual image starts after the "STMP" keyword
* so forward to the firmware header tag
*/
- for (i = 0; (fw->data[i] != STIR421X_PATCH_END_OF_HDR_TAG)
- && (i < fw->size); i++) ;
+ for (i = 0; (fw->data[i] != STIR421X_PATCH_END_OF_HDR_TAG) &&
+ (i < fw->size); i++) ;
/* here we check for the out of buffer case */
- if ((STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i])
- && (i < STIR421X_PATCH_CODE_OFFSET)) {
+ if ((STIR421X_PATCH_END_OF_HDR_TAG == fw->data[i]) &&
+ (i < STIR421X_PATCH_CODE_OFFSET)) {
if (!memcmp(fw->data + i + 1, STIR421X_PATCH_STMP_TAG,
sizeof(STIR421X_PATCH_STMP_TAG) - 1)) {
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index 1e8dd8c74a6..8f7d0d146f2 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -115,7 +115,7 @@ struct smsc_ircc_subsystem_configuration {
unsigned short vendor; /* PCI vendor ID */
unsigned short device; /* PCI vendor ID */
unsigned short subvendor; /* PCI subsystem vendor ID */
- unsigned short subdevice; /* PCI sybsystem device ID */
+ unsigned short subdevice; /* PCI subsystem device ID */
unsigned short sir_io; /* I/O port for SIR */
unsigned short fir_io; /* I/O port for FIR */
unsigned char fir_irq; /* FIR IRQ */
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index 528767dec9d..e5698fa30a4 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -612,16 +612,16 @@ static int fifo_txwait(struct stir_cb *stir, int space)
pr_debug("fifo status 0x%lx count %lu\n", status, count);
/* is fifo receiving already, or empty */
- if (!(status & FIFOCTL_DIR)
- || (status & FIFOCTL_EMPTY))
+ if (!(status & FIFOCTL_DIR) ||
+ (status & FIFOCTL_EMPTY))
return 0;
if (signal_pending(current))
return -EINTR;
/* shutting down? */
- if (!netif_running(stir->netdev)
- || !netif_device_present(stir->netdev))
+ if (!netif_running(stir->netdev) ||
+ !netif_device_present(stir->netdev))
return -ESHUTDOWN;
/* only waiting for some space */
@@ -776,8 +776,8 @@ static int stir_transmit_thread(void *arg)
}
/* nothing to send? start receiving */
- if (!stir->receiving
- && irda_device_txqueue_empty(dev)) {
+ if (!stir->receiving &&
+ irda_device_txqueue_empty(dev)) {
/* Wait otherwise chip gets confused. */
if (fifo_txwait(stir, -1))
break;
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
index a5ca71cec02..fddb4efd545 100644
--- a/drivers/net/irda/via-ircc.c
+++ b/drivers/net/irda/via-ircc.c
@@ -1185,8 +1185,8 @@ F01_E */
* if frame size,data ptr,or skb ptr are wrong ,the get next
* entry.
*/
- if ((skb == NULL) || (skb->data == NULL)
- || (self->rx_buff.data == NULL) || (len < 6)) {
+ if ((skb == NULL) || (skb->data == NULL) ||
+ (self->rx_buff.data == NULL) || (len < 6)) {
self->netdev->stats.rx_dropped++;
return TRUE;
}
@@ -1284,8 +1284,8 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
self->RetryCount++;
if ((self->RetryCount >= 1) ||
- ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize)
- || (st_fifo->len >= (MAX_RX_WINDOW))) {
+ ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) ||
+ (st_fifo->len >= (MAX_RX_WINDOW))) {
while (st_fifo->len > 0) { //upload frame
// Put this entry back in fifo
if (st_fifo->head > MAX_RX_WINDOW)
@@ -1300,8 +1300,8 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
* if frame size, data ptr, or skb ptr are wrong,
* then get next entry.
*/
- if ((skb == NULL) || (skb->data == NULL)
- || (self->rx_buff.data == NULL) || (len < 6)) {
+ if ((skb == NULL) || (skb->data == NULL) ||
+ (self->rx_buff.data == NULL) || (len < 6)) {
self->netdev->stats.rx_dropped++;
continue;
}
@@ -1332,8 +1332,8 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase)
* if frame is receive complete at this routine ,then upload
* frame.
*/
- if ((GetRXStatus(iobase) & 0x10)
- && (RxCurCount(iobase, self) != self->RxLastCount)) {
+ if ((GetRXStatus(iobase) & 0x10) &&
+ (RxCurCount(iobase, self) != self->RxLastCount)) {
upload_rxdata(self, iobase);
if (irda_device_txqueue_empty(self->netdev))
via_ircc_dma_receive(self);
diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c
index 7cfb8b6593c..bd3c6b5ee76 100644
--- a/drivers/net/irda/vlsi_ir.c
+++ b/drivers/net/irda/vlsi_ir.c
@@ -431,8 +431,8 @@ static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr
memset(rd, 0, sizeof(*rd));
rd->hw = hwmap + i;
rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA);
- if (rd->buf == NULL
- || !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) {
+ if (rd->buf == NULL ||
+ !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) {
if (rd->buf) {
IRDA_ERROR("%s: failed to create PCI-MAP for %p",
__func__, rd->buf);
@@ -955,8 +955,8 @@ static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb,
}
for(;;) {
do_gettimeofday(&now);
- if (now.tv_sec > ready.tv_sec
- || (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec))
+ if (now.tv_sec > ready.tv_sec ||
+ (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec))
break;
udelay(100);
/* must not sleep here - called under netif_tx_lock! */
@@ -1594,8 +1594,8 @@ static int vlsi_irda_init(struct net_device *ndev)
* see include file for details why we need these 2 masks, in this order!
*/
- if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW)
- || pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) {
+ if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) ||
+ pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) {
IRDA_ERROR("%s: aborting due to PCI BM-DMA address limitations\n", __func__);
return -1;
}
@@ -1641,8 +1641,8 @@ vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id)
IRDA_MESSAGE("%s: IrDA PCI controller %s detected\n",
drivername, pci_name(pdev));
- if ( !pci_resource_start(pdev,0)
- || !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) {
+ if ( !pci_resource_start(pdev,0) ||
+ !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) {
IRDA_ERROR("%s: bar 0 invalid", __func__);
goto out_disable;
}
diff --git a/drivers/net/isa-skeleton.c b/drivers/net/isa-skeleton.c
index 9706e64e367..04d0502726c 100644
--- a/drivers/net/isa-skeleton.c
+++ b/drivers/net/isa-skeleton.c
@@ -214,9 +214,9 @@ static int __init netcard_probe1(struct net_device *dev, int ioaddr)
* contains the manufacturer's unique code. That might be a good probe
* method. Ideally you would add additional checks.
*/
- if (inb(ioaddr + 0) != SA_ADDR0
- || inb(ioaddr + 1) != SA_ADDR1
- || inb(ioaddr + 2) != SA_ADDR2)
+ if (inb(ioaddr + 0) != SA_ADDR0 ||
+ inb(ioaddr + 1) != SA_ADDR1 ||
+ inb(ioaddr + 2) != SA_ADDR2)
goto out;
if (net_debug && version_printed++ == 0)
@@ -260,7 +260,7 @@ static int __init netcard_probe1(struct net_device *dev, int ioaddr)
dev->irq = 9;
{
- int irqval = request_irq(dev->irq, &net_interrupt, 0, cardname, dev);
+ int irqval = request_irq(dev->irq, net_interrupt, 0, cardname, dev);
if (irqval) {
printk("%s: unable to get IRQ %d (irqval=%d).\n",
dev->name, dev->irq, irqval);
@@ -378,7 +378,7 @@ net_open(struct net_device *dev)
* This is used if the interrupt line can turned off (shared).
* See 3c503.c for an example of selecting the IRQ at config-time.
*/
- if (request_irq(dev->irq, &net_interrupt, 0, cardname, dev)) {
+ if (request_irq(dev->irq, net_interrupt, 0, cardname, dev)) {
return -EAGAIN;
}
/*
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index aa7286bc436..16c91910d6c 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -604,10 +604,10 @@ static int veth_process_caps(struct veth_lpar_connection *cnx)
/* Convert timer to jiffies */
cnx->ack_timeout = remote_caps->ack_timeout * HZ / 1000000;
- if ( (remote_caps->num_buffers == 0)
- || (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG)
- || (remote_caps->ack_threshold == 0)
- || (cnx->ack_timeout == 0) ) {
+ if ( (remote_caps->num_buffers == 0) ||
+ (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG) ||
+ (remote_caps->ack_threshold == 0) ||
+ (cnx->ack_timeout == 0) ) {
veth_error("Received incompatible capabilities from LPAR %d.\n",
cnx->remote_lp);
return HvLpEvent_Rc_InvalidSubtypeData;
@@ -714,8 +714,8 @@ static void veth_statemachine(struct work_struct *work)
cnx->state |= VETH_STATE_OPEN;
}
- if ( (cnx->state & VETH_STATE_OPEN)
- && !(cnx->state & VETH_STATE_SENTMON) ) {
+ if ( (cnx->state & VETH_STATE_OPEN) &&
+ !(cnx->state & VETH_STATE_SENTMON) ) {
rc = veth_signalevent(cnx, VETH_EVENT_MONITOR,
HvLpEvent_AckInd_DoAck,
HvLpEvent_AckType_DeferredAck,
@@ -724,8 +724,8 @@ static void veth_statemachine(struct work_struct *work)
if (rc == HvLpEvent_Rc_Good) {
cnx->state |= VETH_STATE_SENTMON;
} else {
- if ( (rc != HvLpEvent_Rc_PartitionDead)
- && (rc != HvLpEvent_Rc_PathClosed) )
+ if ( (rc != HvLpEvent_Rc_PartitionDead) &&
+ (rc != HvLpEvent_Rc_PathClosed) )
veth_error("Error sending monitor to LPAR %d, "
"rc = %d\n", rlp, rc);
@@ -735,8 +735,8 @@ static void veth_statemachine(struct work_struct *work)
}
}
- if ( (cnx->state & VETH_STATE_OPEN)
- && !(cnx->state & VETH_STATE_SENTCAPS)) {
+ if ( (cnx->state & VETH_STATE_OPEN) &&
+ !(cnx->state & VETH_STATE_SENTCAPS)) {
u64 *rawcap = (u64 *)&cnx->local_caps;
rc = veth_signalevent(cnx, VETH_EVENT_CAP,
@@ -748,8 +748,8 @@ static void veth_statemachine(struct work_struct *work)
if (rc == HvLpEvent_Rc_Good) {
cnx->state |= VETH_STATE_SENTCAPS;
} else {
- if ( (rc != HvLpEvent_Rc_PartitionDead)
- && (rc != HvLpEvent_Rc_PathClosed) )
+ if ( (rc != HvLpEvent_Rc_PartitionDead) &&
+ (rc != HvLpEvent_Rc_PathClosed) )
veth_error("Error sending caps to LPAR %d, "
"rc = %d\n", rlp, rc);
@@ -759,8 +759,8 @@ static void veth_statemachine(struct work_struct *work)
}
}
- if ((cnx->state & VETH_STATE_GOTCAPS)
- && !(cnx->state & VETH_STATE_SENTCAPACK)) {
+ if ((cnx->state & VETH_STATE_GOTCAPS) &&
+ !(cnx->state & VETH_STATE_SENTCAPACK)) {
struct veth_cap_data *remote_caps = &cnx->remote_caps;
memcpy(remote_caps, &cnx->cap_event.u.caps_data,
@@ -783,9 +783,9 @@ static void veth_statemachine(struct work_struct *work)
goto cant_cope;
}
- if ((cnx->state & VETH_STATE_GOTCAPACK)
- && (cnx->state & VETH_STATE_GOTCAPS)
- && !(cnx->state & VETH_STATE_READY)) {
+ if ((cnx->state & VETH_STATE_GOTCAPACK) &&
+ (cnx->state & VETH_STATE_GOTCAPS) &&
+ !(cnx->state & VETH_STATE_READY)) {
if (cnx->cap_ack_event.base_event.xRc == HvLpEvent_Rc_Good) {
/* Start the ACK timer */
cnx->ack_timer.expires = jiffies + cnx->ack_timeout;
@@ -818,8 +818,8 @@ static int veth_init_connection(u8 rlp)
struct veth_msg *msgs;
int i;
- if ( (rlp == this_lp)
- || ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) )
+ if ( (rlp == this_lp) ||
+ ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) )
return 0;
cnx = kzalloc(sizeof(*cnx), GFP_KERNEL);
@@ -1384,7 +1384,7 @@ static inline void veth_build_dma_list(struct dma_chunk *list,
unsigned long done;
int i = 1;
- /* FIXME: skbs are continguous in real addresses. Do we
+ /* FIXME: skbs are contiguous in real addresses. Do we
* really need to break it into PAGE_SIZE chunks, or can we do
* it just at the granularity of iSeries real->absolute
* mapping? Indeed, given the way the allocator works, can we
@@ -1538,8 +1538,8 @@ static void veth_receive(struct veth_lpar_connection *cnx,
cnx->pending_acks[cnx->num_pending_acks++] =
event->base_event.xCorrelationToken;
- if ( (cnx->num_pending_acks >= cnx->remote_caps.ack_threshold)
- || (cnx->num_pending_acks >= VETH_MAX_ACKS_PER_MSG) )
+ if ( (cnx->num_pending_acks >= cnx->remote_caps.ack_threshold) ||
+ (cnx->num_pending_acks >= VETH_MAX_ACKS_PER_MSG) )
veth_flush_acks(cnx);
spin_unlock_irqrestore(&cnx->lock, flags);
diff --git a/drivers/net/ixgb/ixgb.h b/drivers/net/ixgb/ixgb.h
index d85717e3022..5257ae08b9f 100644
--- a/drivers/net/ixgb/ixgb.h
+++ b/drivers/net/ixgb/ixgb.h
@@ -117,6 +117,7 @@ struct ixgb_buffer {
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
+ u16 mapped_as_page;
};
struct ixgb_desc_ring {
@@ -183,7 +184,6 @@ struct ixgb_adapter {
struct napi_struct napi;
struct net_device *netdev;
struct pci_dev *pdev;
- struct net_device_stats net_stats;
/* structs defined in ixgb_hw.h */
struct ixgb_hw hw;
diff --git a/drivers/net/ixgb/ixgb_ethtool.c b/drivers/net/ixgb/ixgb_ethtool.c
index 288ee1d0f43..a4ed96caae6 100644
--- a/drivers/net/ixgb/ixgb_ethtool.c
+++ b/drivers/net/ixgb/ixgb_ethtool.c
@@ -34,38 +34,46 @@
#define IXGB_ALL_RAR_ENTRIES 16
+enum {NETDEV_STATS, IXGB_STATS};
+
struct ixgb_stats {
char stat_string[ETH_GSTRING_LEN];
+ int type;
int sizeof_stat;
int stat_offset;
};
-#define IXGB_STAT(m) FIELD_SIZEOF(struct ixgb_adapter, m), \
- offsetof(struct ixgb_adapter, m)
+#define IXGB_STAT(m) IXGB_STATS, \
+ FIELD_SIZEOF(struct ixgb_adapter, m), \
+ offsetof(struct ixgb_adapter, m)
+#define IXGB_NETDEV_STAT(m) NETDEV_STATS, \
+ FIELD_SIZEOF(struct net_device, m), \
+ offsetof(struct net_device, m)
+
static struct ixgb_stats ixgb_gstrings_stats[] = {
- {"rx_packets", IXGB_STAT(net_stats.rx_packets)},
- {"tx_packets", IXGB_STAT(net_stats.tx_packets)},
- {"rx_bytes", IXGB_STAT(net_stats.rx_bytes)},
- {"tx_bytes", IXGB_STAT(net_stats.tx_bytes)},
- {"rx_errors", IXGB_STAT(net_stats.rx_errors)},
- {"tx_errors", IXGB_STAT(net_stats.tx_errors)},
- {"rx_dropped", IXGB_STAT(net_stats.rx_dropped)},
- {"tx_dropped", IXGB_STAT(net_stats.tx_dropped)},
- {"multicast", IXGB_STAT(net_stats.multicast)},
- {"collisions", IXGB_STAT(net_stats.collisions)},
-
-/* { "rx_length_errors", IXGB_STAT(net_stats.rx_length_errors) }, */
- {"rx_over_errors", IXGB_STAT(net_stats.rx_over_errors)},
- {"rx_crc_errors", IXGB_STAT(net_stats.rx_crc_errors)},
- {"rx_frame_errors", IXGB_STAT(net_stats.rx_frame_errors)},
+ {"rx_packets", IXGB_NETDEV_STAT(stats.rx_packets)},
+ {"tx_packets", IXGB_NETDEV_STAT(stats.tx_packets)},
+ {"rx_bytes", IXGB_NETDEV_STAT(stats.rx_bytes)},
+ {"tx_bytes", IXGB_NETDEV_STAT(stats.tx_bytes)},
+ {"rx_errors", IXGB_NETDEV_STAT(stats.rx_errors)},
+ {"tx_errors", IXGB_NETDEV_STAT(stats.tx_errors)},
+ {"rx_dropped", IXGB_NETDEV_STAT(stats.rx_dropped)},
+ {"tx_dropped", IXGB_NETDEV_STAT(stats.tx_dropped)},
+ {"multicast", IXGB_NETDEV_STAT(stats.multicast)},
+ {"collisions", IXGB_NETDEV_STAT(stats.collisions)},
+
+/* { "rx_length_errors", IXGB_NETDEV_STAT(stats.rx_length_errors) }, */
+ {"rx_over_errors", IXGB_NETDEV_STAT(stats.rx_over_errors)},
+ {"rx_crc_errors", IXGB_NETDEV_STAT(stats.rx_crc_errors)},
+ {"rx_frame_errors", IXGB_NETDEV_STAT(stats.rx_frame_errors)},
{"rx_no_buffer_count", IXGB_STAT(stats.rnbc)},
- {"rx_fifo_errors", IXGB_STAT(net_stats.rx_fifo_errors)},
- {"rx_missed_errors", IXGB_STAT(net_stats.rx_missed_errors)},
- {"tx_aborted_errors", IXGB_STAT(net_stats.tx_aborted_errors)},
- {"tx_carrier_errors", IXGB_STAT(net_stats.tx_carrier_errors)},
- {"tx_fifo_errors", IXGB_STAT(net_stats.tx_fifo_errors)},
- {"tx_heartbeat_errors", IXGB_STAT(net_stats.tx_heartbeat_errors)},
- {"tx_window_errors", IXGB_STAT(net_stats.tx_window_errors)},
+ {"rx_fifo_errors", IXGB_NETDEV_STAT(stats.rx_fifo_errors)},
+ {"rx_missed_errors", IXGB_NETDEV_STAT(stats.rx_missed_errors)},
+ {"tx_aborted_errors", IXGB_NETDEV_STAT(stats.tx_aborted_errors)},
+ {"tx_carrier_errors", IXGB_NETDEV_STAT(stats.tx_carrier_errors)},
+ {"tx_fifo_errors", IXGB_NETDEV_STAT(stats.tx_fifo_errors)},
+ {"tx_heartbeat_errors", IXGB_NETDEV_STAT(stats.tx_heartbeat_errors)},
+ {"tx_window_errors", IXGB_NETDEV_STAT(stats.tx_window_errors)},
{"tx_deferred_ok", IXGB_STAT(stats.dc)},
{"tx_timeout_count", IXGB_STAT(tx_timeout_count) },
{"tx_restart_queue", IXGB_STAT(restart_queue) },
@@ -662,10 +670,21 @@ ixgb_get_ethtool_stats(struct net_device *netdev,
{
struct ixgb_adapter *adapter = netdev_priv(netdev);
int i;
+ char *p = NULL;
ixgb_update_stats(adapter);
for (i = 0; i < IXGB_STATS_LEN; i++) {
- char *p = (char *)adapter+ixgb_gstrings_stats[i].stat_offset;
+ switch (ixgb_gstrings_stats[i].type) {
+ case NETDEV_STATS:
+ p = (char *) netdev +
+ ixgb_gstrings_stats[i].stat_offset;
+ break;
+ case IXGB_STATS:
+ p = (char *) adapter +
+ ixgb_gstrings_stats[i].stat_offset;
+ break;
+ }
+
data[i] = (ixgb_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
index 8aa44dca57e..bcd0f01d5fe 100644
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ixgb/ixgb_main.c
@@ -233,7 +233,7 @@ ixgb_up(struct ixgb_adapter *adapter)
/* proceed to try to request regular interrupt */
}
- err = request_irq(adapter->pdev->irq, &ixgb_intr, irq_flags,
+ err = request_irq(adapter->pdev->irq, ixgb_intr, irq_flags,
netdev->name, netdev);
if (err) {
if (adapter->have_msi)
@@ -892,10 +892,18 @@ static void
ixgb_unmap_and_free_tx_resource(struct ixgb_adapter *adapter,
struct ixgb_buffer *buffer_info)
{
- buffer_info->dma = 0;
+ if (buffer_info->dma) {
+ if (buffer_info->mapped_as_page)
+ pci_unmap_page(adapter->pdev, buffer_info->dma,
+ buffer_info->length, PCI_DMA_TODEVICE);
+ else
+ pci_unmap_single(adapter->pdev, buffer_info->dma,
+ buffer_info->length,
+ PCI_DMA_TODEVICE);
+ buffer_info->dma = 0;
+ }
+
if (buffer_info->skb) {
- skb_dma_unmap(&adapter->pdev->dev, buffer_info->skb,
- DMA_TO_DEVICE);
dev_kfree_skb_any(buffer_info->skb);
buffer_info->skb = NULL;
}
@@ -1272,24 +1280,16 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
unsigned int first)
{
struct ixgb_desc_ring *tx_ring = &adapter->tx_ring;
+ struct pci_dev *pdev = adapter->pdev;
struct ixgb_buffer *buffer_info;
int len = skb_headlen(skb);
unsigned int offset = 0, size, count = 0, i;
unsigned int mss = skb_shinfo(skb)->gso_size;
-
unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
unsigned int f;
- dma_addr_t *map;
i = tx_ring->next_to_use;
- if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) {
- dev_err(&adapter->pdev->dev, "TX DMA map failed\n");
- return 0;
- }
-
- map = skb_shinfo(skb)->dma_maps;
-
while (len) {
buffer_info = &tx_ring->buffer_info[i];
size = min(len, IXGB_MAX_DATA_PER_TXD);
@@ -1301,11 +1301,11 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
buffer_info->length = size;
WARN_ON(buffer_info->dma != 0);
buffer_info->time_stamp = jiffies;
- buffer_info->dma = skb_shinfo(skb)->dma_head + offset;
- pci_map_single(adapter->pdev,
- skb->data + offset,
- size,
- PCI_DMA_TODEVICE);
+ buffer_info->mapped_as_page = false;
+ buffer_info->dma = pci_map_single(pdev, skb->data + offset,
+ size, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
buffer_info->next_to_watch = 0;
len -= size;
@@ -1323,7 +1323,7 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
frag = &skb_shinfo(skb)->frags[f];
len = frag->size;
- offset = 0;
+ offset = frag->page_offset;
while (len) {
i++;
@@ -1341,7 +1341,13 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
buffer_info->length = size;
buffer_info->time_stamp = jiffies;
- buffer_info->dma = map[f] + offset;
+ buffer_info->mapped_as_page = true;
+ buffer_info->dma =
+ pci_map_page(pdev, frag->page,
+ offset, size,
+ PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, buffer_info->dma))
+ goto dma_error;
buffer_info->next_to_watch = 0;
len -= size;
@@ -1353,6 +1359,22 @@ ixgb_tx_map(struct ixgb_adapter *adapter, struct sk_buff *skb,
tx_ring->buffer_info[first].next_to_watch = i;
return count;
+
+dma_error:
+ dev_err(&pdev->dev, "TX DMA map failed\n");
+ buffer_info->dma = 0;
+ count--;
+
+ while (count >= 0) {
+ count--;
+ i--;
+ if (i < 0)
+ i += tx_ring->count;
+ buffer_info = &tx_ring->buffer_info[i];
+ ixgb_unmap_and_free_tx_resource(adapter, buffer_info);
+ }
+
+ return 0;
}
static void
@@ -1537,9 +1559,7 @@ ixgb_tx_timeout_task(struct work_struct *work)
static struct net_device_stats *
ixgb_get_stats(struct net_device *netdev)
{
- struct ixgb_adapter *adapter = netdev_priv(netdev);
-
- return &adapter->net_stats;
+ return &netdev->stats;
}
/**
@@ -1676,16 +1696,16 @@ ixgb_update_stats(struct ixgb_adapter *adapter)
/* Fill out the OS statistics structure */
- adapter->net_stats.rx_packets = adapter->stats.gprcl;
- adapter->net_stats.tx_packets = adapter->stats.gptcl;
- adapter->net_stats.rx_bytes = adapter->stats.gorcl;
- adapter->net_stats.tx_bytes = adapter->stats.gotcl;
- adapter->net_stats.multicast = adapter->stats.mprcl;
- adapter->net_stats.collisions = 0;
+ netdev->stats.rx_packets = adapter->stats.gprcl;
+ netdev->stats.tx_packets = adapter->stats.gptcl;
+ netdev->stats.rx_bytes = adapter->stats.gorcl;
+ netdev->stats.tx_bytes = adapter->stats.gotcl;
+ netdev->stats.multicast = adapter->stats.mprcl;
+ netdev->stats.collisions = 0;
/* ignore RLEC as it reports errors for padded (<64bytes) frames
* with a length in the type/len field */
- adapter->net_stats.rx_errors =
+ netdev->stats.rx_errors =
/* adapter->stats.rnbc + */ adapter->stats.crcerrs +
adapter->stats.ruc +
adapter->stats.roc /*+ adapter->stats.rlec */ +
@@ -1693,21 +1713,21 @@ ixgb_update_stats(struct ixgb_adapter *adapter)
adapter->stats.ecbc + adapter->stats.mpc;
/* see above
- * adapter->net_stats.rx_length_errors = adapter->stats.rlec;
+ * netdev->stats.rx_length_errors = adapter->stats.rlec;
*/
- adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
- adapter->net_stats.rx_fifo_errors = adapter->stats.mpc;
- adapter->net_stats.rx_missed_errors = adapter->stats.mpc;
- adapter->net_stats.rx_over_errors = adapter->stats.mpc;
-
- adapter->net_stats.tx_errors = 0;
- adapter->net_stats.rx_frame_errors = 0;
- adapter->net_stats.tx_aborted_errors = 0;
- adapter->net_stats.tx_carrier_errors = 0;
- adapter->net_stats.tx_fifo_errors = 0;
- adapter->net_stats.tx_heartbeat_errors = 0;
- adapter->net_stats.tx_window_errors = 0;
+ netdev->stats.rx_crc_errors = adapter->stats.crcerrs;
+ netdev->stats.rx_fifo_errors = adapter->stats.mpc;
+ netdev->stats.rx_missed_errors = adapter->stats.mpc;
+ netdev->stats.rx_over_errors = adapter->stats.mpc;
+
+ netdev->stats.tx_errors = 0;
+ netdev->stats.rx_frame_errors = 0;
+ netdev->stats.tx_aborted_errors = 0;
+ netdev->stats.tx_carrier_errors = 0;
+ netdev->stats.tx_fifo_errors = 0;
+ netdev->stats.tx_heartbeat_errors = 0;
+ netdev->stats.tx_window_errors = 0;
}
#define IXGB_MAX_INTR 10
@@ -1974,9 +1994,8 @@ ixgb_clean_rx_irq(struct ixgb_adapter *adapter, int *work_done, int work_to_do)
* of reassembly being done in the stack */
if (length < copybreak) {
struct sk_buff *new_skb =
- netdev_alloc_skb(netdev, length + NET_IP_ALIGN);
+ netdev_alloc_skb_ip_align(netdev, length);
if (new_skb) {
- skb_reserve(new_skb, NET_IP_ALIGN);
skb_copy_to_linear_data_offset(new_skb,
-NET_IP_ALIGN,
(skb->data -
@@ -2059,20 +2078,13 @@ ixgb_alloc_rx_buffers(struct ixgb_adapter *adapter, int cleaned_count)
goto map_skb;
}
- skb = netdev_alloc_skb(netdev, adapter->rx_buffer_len
- + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(netdev, adapter->rx_buffer_len);
if (unlikely(!skb)) {
/* Better luck next round */
adapter->alloc_rx_buff_failed++;
break;
}
- /* Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
-
buffer_info->skb = skb;
buffer_info->length = adapter->rx_buffer_len;
map_skb:
diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h
index 385be601666..8da8eb53508 100644
--- a/drivers/net/ixgbe/ixgbe.h
+++ b/drivers/net/ixgbe/ixgbe.h
@@ -51,11 +51,11 @@
__func__ , ## args)))
/* TX/RX descriptor defines */
-#define IXGBE_DEFAULT_TXD 1024
+#define IXGBE_DEFAULT_TXD 512
#define IXGBE_MAX_TXD 4096
#define IXGBE_MIN_TXD 64
-#define IXGBE_DEFAULT_RXD 1024
+#define IXGBE_DEFAULT_RXD 512
#define IXGBE_MAX_RXD 4096
#define IXGBE_MIN_RXD 64
@@ -106,6 +106,7 @@ struct ixgbe_tx_buffer {
unsigned long time_stamp;
u16 length;
u16 next_to_watch;
+ u16 mapped_as_page;
};
struct ixgbe_rx_buffer {
@@ -159,10 +160,13 @@ struct ixgbe_ring {
struct ixgbe_queue_stats stats;
unsigned long reinit_state;
u64 rsc_count; /* stat for coalesced packets */
+ u64 rsc_flush; /* stats for flushed packets */
+ u32 restart_queue; /* track tx queue restarts */
+ u32 non_eop_descs; /* track hardware descriptor chaining */
unsigned int size; /* length in bytes */
dma_addr_t dma; /* phys. address of descriptor ring */
-};
+} ____cacheline_internodealigned_in_smp;
enum ixgbe_ring_f_enum {
RING_F_NONE = 0,
@@ -187,7 +191,7 @@ enum ixgbe_ring_f_enum {
struct ixgbe_ring_feature {
int indices;
int mask;
-};
+} ____cacheline_internodealigned_in_smp;
#define MAX_RX_QUEUES 128
#define MAX_TX_QUEUES 128
@@ -273,29 +277,25 @@ struct ixgbe_adapter {
u16 eitr_high;
/* TX */
- struct ixgbe_ring *tx_ring; /* One per active queue */
+ struct ixgbe_ring *tx_ring ____cacheline_aligned_in_smp; /* One per active queue */
int num_tx_queues;
- u64 restart_queue;
- u64 hw_csum_tx_good;
- u64 lsc_int;
- u64 hw_tso_ctxt;
- u64 hw_tso6_ctxt;
u32 tx_timeout_count;
bool detect_tx_hung;
+ u64 restart_queue;
+ u64 lsc_int;
+
/* RX */
- struct ixgbe_ring *rx_ring; /* One per active queue */
+ struct ixgbe_ring *rx_ring ____cacheline_aligned_in_smp; /* One per active queue */
int num_rx_queues;
u64 hw_csum_rx_error;
u64 hw_rx_no_dma_resources;
- u64 hw_csum_rx_good;
u64 non_eop_descs;
int num_msix_vectors;
int max_msix_q_vectors; /* true count of q_vectors for device */
struct ixgbe_ring_feature ring_feature[RING_F_ARRAY_SIZE];
struct msix_entry *msix_entries;
- u64 rx_hdr_split;
u32 alloc_rx_page_failed;
u32 alloc_rx_buff_failed;
@@ -340,7 +340,6 @@ struct ixgbe_adapter {
/* OS defined structs */
struct net_device *netdev;
struct pci_dev *pdev;
- struct net_device_stats net_stats;
u32 test_icr;
struct ixgbe_ring test_tx_ring;
@@ -376,7 +375,8 @@ struct ixgbe_adapter {
#ifdef IXGBE_FCOE
struct ixgbe_fcoe fcoe;
#endif /* IXGBE_FCOE */
- u64 rsc_count;
+ u64 rsc_total_count;
+ u64 rsc_total_flush;
u32 wol;
u16 eeprom_version;
};
@@ -397,7 +397,7 @@ enum ixgbe_boards {
extern struct ixgbe_info ixgbe_82598_info;
extern struct ixgbe_info ixgbe_82599_info;
#ifdef CONFIG_IXGBE_DCB
-extern struct dcbnl_rtnl_ops dcbnl_ops;
+extern const struct dcbnl_rtnl_ops dcbnl_ops;
extern int ixgbe_copy_dcb_cfg(struct ixgbe_dcb_config *src_dcb_cfg,
struct ixgbe_dcb_config *dst_dcb_cfg,
int tc_max);
@@ -458,6 +458,7 @@ extern int ixgbe_fcoe_disable(struct net_device *netdev);
extern u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter);
extern u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up);
#endif /* CONFIG_IXGBE_DCB */
+extern int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type);
#endif /* IXGBE_FCOE */
#endif /* _IXGBE_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c
index 34b04924c8a..72106898a5c 100644
--- a/drivers/net/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ixgbe/ixgbe_82599.c
@@ -42,6 +42,10 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
ixgbe_link_speed speed,
bool autoneg,
bool autoneg_wait_to_complete);
+static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw,
+ ixgbe_link_speed speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete);
s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw,
bool autoneg_wait_to_complete);
s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
@@ -64,7 +68,13 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw)
/* Set up dual speed SFP+ support */
mac->ops.setup_link = &ixgbe_setup_mac_link_multispeed_fiber;
} else {
- mac->ops.setup_link = &ixgbe_setup_mac_link_82599;
+ if ((mac->ops.get_media_type(hw) ==
+ ixgbe_media_type_backplane) &&
+ (hw->phy.smart_speed == ixgbe_smart_speed_auto ||
+ hw->phy.smart_speed == ixgbe_smart_speed_on))
+ mac->ops.setup_link = &ixgbe_setup_mac_link_smartspeed;
+ else
+ mac->ops.setup_link = &ixgbe_setup_mac_link_82599;
}
}
@@ -337,6 +347,7 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw)
media_type = ixgbe_media_type_backplane;
break;
case IXGBE_DEV_ID_82599_SFP:
+ case IXGBE_DEV_ID_82599_SFP_EM:
media_type = ixgbe_media_type_fiber;
break;
case IXGBE_DEV_ID_82599_CX4:
@@ -479,7 +490,12 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
hw->mac.autotry_restart = false;
}
- /* The controller may take up to 500ms at 10g to acquire link */
+ /*
+ * Wait for the controller to acquire link. Per IEEE 802.3ap,
+ * Section 73.10.2, we may have to wait up to 500ms if KR is
+ * attempted. 82599 uses the same timing for 10g SFI.
+ */
+
for (i = 0; i < 5; i++) {
/* Wait for the link partner to also set speed */
msleep(100);
@@ -567,6 +583,111 @@ out:
}
/**
+ * ixgbe_setup_mac_link_smartspeed - Set MAC link speed using SmartSpeed
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg: true if autonegotiation enabled
+ * @autoneg_wait_to_complete: true when waiting for completion is needed
+ *
+ * Implements the Intel SmartSpeed algorithm.
+ **/
+static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw,
+ ixgbe_link_speed speed, bool autoneg,
+ bool autoneg_wait_to_complete)
+{
+ s32 status = 0;
+ ixgbe_link_speed link_speed;
+ s32 i, j;
+ bool link_up = false;
+ u32 autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+
+ hw_dbg(hw, "ixgbe_setup_mac_link_smartspeed.\n");
+
+ /* Set autoneg_advertised value based on input link speed */
+ hw->phy.autoneg_advertised = 0;
+
+ if (speed & IXGBE_LINK_SPEED_10GB_FULL)
+ hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
+
+ if (speed & IXGBE_LINK_SPEED_1GB_FULL)
+ hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+
+ if (speed & IXGBE_LINK_SPEED_100_FULL)
+ hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_100_FULL;
+
+ /*
+ * Implement Intel SmartSpeed algorithm. SmartSpeed will reduce the
+ * autoneg advertisement if link is unable to be established at the
+ * highest negotiated rate. This can sometimes happen due to integrity
+ * issues with the physical media connection.
+ */
+
+ /* First, try to get link with full advertisement */
+ hw->phy.smart_speed_active = false;
+ for (j = 0; j < IXGBE_SMARTSPEED_MAX_RETRIES; j++) {
+ status = ixgbe_setup_mac_link_82599(hw, speed, autoneg,
+ autoneg_wait_to_complete);
+ if (status)
+ goto out;
+
+ /*
+ * Wait for the controller to acquire link. Per IEEE 802.3ap,
+ * Section 73.10.2, we may have to wait up to 500ms if KR is
+ * attempted, or 200ms if KX/KX4/BX/BX4 is attempted, per
+ * Table 9 in the AN MAS.
+ */
+ for (i = 0; i < 5; i++) {
+ mdelay(100);
+
+ /* If we have link, just jump out */
+ hw->mac.ops.check_link(hw, &link_speed,
+ &link_up, false);
+ if (link_up)
+ goto out;
+ }
+ }
+
+ /*
+ * We didn't get link. If we advertised KR plus one of KX4/KX
+ * (or BX4/BX), then disable KR and try again.
+ */
+ if (((autoc_reg & IXGBE_AUTOC_KR_SUPP) == 0) ||
+ ((autoc_reg & IXGBE_AUTOC_KX4_KX_SUPP_MASK) == 0))
+ goto out;
+
+ /* Turn SmartSpeed on to disable KR support */
+ hw->phy.smart_speed_active = true;
+ status = ixgbe_setup_mac_link_82599(hw, speed, autoneg,
+ autoneg_wait_to_complete);
+ if (status)
+ goto out;
+
+ /*
+ * Wait for the controller to acquire link. 600ms will allow for
+ * the AN link_fail_inhibit_timer as well for multiple cycles of
+ * parallel detect, both 10g and 1g. This allows for the maximum
+ * connect attempts as defined in the AN MAS table 73-7.
+ */
+ for (i = 0; i < 6; i++) {
+ mdelay(100);
+
+ /* If we have link, just jump out */
+ hw->mac.ops.check_link(hw, &link_speed,
+ &link_up, false);
+ if (link_up)
+ goto out;
+ }
+
+ /* We didn't get link. Turn SmartSpeed back off. */
+ hw->phy.smart_speed_active = false;
+ status = ixgbe_setup_mac_link_82599(hw, speed, autoneg,
+ autoneg_wait_to_complete);
+
+out:
+ return status;
+}
+
+/**
* ixgbe_check_mac_link_82599 - Determine link and speed status
* @hw: pointer to hardware structure
* @speed: pointer to link speed
@@ -669,7 +790,8 @@ s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
if (speed & IXGBE_LINK_SPEED_10GB_FULL)
if (orig_autoc & IXGBE_AUTOC_KX4_SUPP)
autoc |= IXGBE_AUTOC_KX4_SUPP;
- if (orig_autoc & IXGBE_AUTOC_KR_SUPP)
+ if ((orig_autoc & IXGBE_AUTOC_KR_SUPP) &&
+ (hw->phy.smart_speed_active == false))
autoc |= IXGBE_AUTOC_KR_SUPP;
if (speed & IXGBE_LINK_SPEED_1GB_FULL)
autoc |= IXGBE_AUTOC_KX_SUPP;
@@ -878,6 +1000,10 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw)
hw->mac.num_rar_entries--;
}
+ /* Store the alternative WWNN/WWPN prefix */
+ hw->mac.ops.get_wwn_prefix(hw, &hw->mac.wwnn_prefix,
+ &hw->mac.wwpn_prefix);
+
reset_hw_out:
return status;
}
@@ -2414,6 +2540,51 @@ fw_version_out:
return status;
}
+/**
+ * ixgbe_get_wwn_prefix_82599 - Get alternative WWNN/WWPN prefix from
+ * the EEPROM
+ * @hw: pointer to hardware structure
+ * @wwnn_prefix: the alternative WWNN prefix
+ * @wwpn_prefix: the alternative WWPN prefix
+ *
+ * This function will read the EEPROM from the alternative SAN MAC address
+ * block to check the support for the alternative WWNN/WWPN prefix support.
+ **/
+static s32 ixgbe_get_wwn_prefix_82599(struct ixgbe_hw *hw, u16 *wwnn_prefix,
+ u16 *wwpn_prefix)
+{
+ u16 offset, caps;
+ u16 alt_san_mac_blk_offset;
+
+ /* clear output first */
+ *wwnn_prefix = 0xFFFF;
+ *wwpn_prefix = 0xFFFF;
+
+ /* check if alternative SAN MAC is supported */
+ hw->eeprom.ops.read(hw, IXGBE_ALT_SAN_MAC_ADDR_BLK_PTR,
+ &alt_san_mac_blk_offset);
+
+ if ((alt_san_mac_blk_offset == 0) ||
+ (alt_san_mac_blk_offset == 0xFFFF))
+ goto wwn_prefix_out;
+
+ /* check capability in alternative san mac address block */
+ offset = alt_san_mac_blk_offset + IXGBE_ALT_SAN_MAC_ADDR_CAPS_OFFSET;
+ hw->eeprom.ops.read(hw, offset, &caps);
+ if (!(caps & IXGBE_ALT_SAN_MAC_ADDR_CAPS_ALTWWN))
+ goto wwn_prefix_out;
+
+ /* get the corresponding prefix for WWNN/WWPN */
+ offset = alt_san_mac_blk_offset + IXGBE_ALT_SAN_MAC_ADDR_WWNN_OFFSET;
+ hw->eeprom.ops.read(hw, offset, wwnn_prefix);
+
+ offset = alt_san_mac_blk_offset + IXGBE_ALT_SAN_MAC_ADDR_WWPN_OFFSET;
+ hw->eeprom.ops.read(hw, offset, wwpn_prefix);
+
+wwn_prefix_out:
+ return 0;
+}
+
static struct ixgbe_mac_operations mac_ops_82599 = {
.init_hw = &ixgbe_init_hw_generic,
.reset_hw = &ixgbe_reset_hw_82599,
@@ -2425,6 +2596,7 @@ static struct ixgbe_mac_operations mac_ops_82599 = {
.get_mac_addr = &ixgbe_get_mac_addr_generic,
.get_san_mac_addr = &ixgbe_get_san_mac_addr_82599,
.get_device_caps = &ixgbe_get_device_caps_82599,
+ .get_wwn_prefix = &ixgbe_get_wwn_prefix_82599,
.stop_adapter = &ixgbe_stop_adapter_generic,
.get_bus_info = &ixgbe_get_bus_info_generic,
.set_lan_id = &ixgbe_set_lan_id_multi_port_pcie,
diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c
index 40ff120a9ad..688b8ca5da3 100644
--- a/drivers/net/ixgbe/ixgbe_common.c
+++ b/drivers/net/ixgbe/ixgbe_common.c
@@ -1382,10 +1382,10 @@ s32 ixgbe_update_uc_addr_list_generic(struct ixgbe_hw *hw,
hw->addr_ctrl.overflow_promisc = 0;
/* Zero out the other receive addresses */
- hw_dbg(hw, "Clearing RAR[1-%d]\n", uc_addr_in_use);
- for (i = 1; i <= uc_addr_in_use; i++) {
- IXGBE_WRITE_REG(hw, IXGBE_RAL(i), 0);
- IXGBE_WRITE_REG(hw, IXGBE_RAH(i), 0);
+ hw_dbg(hw, "Clearing RAR[1-%d]\n", uc_addr_in_use + 1);
+ for (i = 0; i < uc_addr_in_use; i++) {
+ IXGBE_WRITE_REG(hw, IXGBE_RAL(1+i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_RAH(1+i), 0);
}
/* Add the new addresses */
@@ -1755,17 +1755,24 @@ s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw)
/*
* On backplane, bail out if
* - backplane autoneg was not completed, or if
- * - link partner is not AN enabled
+ * - we are 82599 and link partner is not AN enabled
*/
if (hw->phy.media_type == ixgbe_media_type_backplane) {
links = IXGBE_READ_REG(hw, IXGBE_LINKS);
- links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2);
- if (((links & IXGBE_LINKS_KX_AN_COMP) == 0) ||
- ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0)) {
+ if ((links & IXGBE_LINKS_KX_AN_COMP) == 0) {
hw->fc.fc_was_autonegged = false;
hw->fc.current_mode = hw->fc.requested_mode;
goto out;
}
+
+ if (hw->mac.type == ixgbe_mac_82599EB) {
+ links2 = IXGBE_READ_REG(hw, IXGBE_LINKS2);
+ if ((links2 & IXGBE_LINKS2_AN_SUPPORTED) == 0) {
+ hw->fc.fc_was_autonegged = false;
+ hw->fc.current_mode = hw->fc.requested_mode;
+ goto out;
+ }
+ }
}
/*
@@ -1784,6 +1791,20 @@ s32 ixgbe_fc_autoneg(struct ixgbe_hw *hw)
}
/*
+ * Bail out on
+ * - copper or CX4 adapters
+ * - fiber adapters running at 10gig
+ */
+ if ((hw->phy.media_type == ixgbe_media_type_copper) ||
+ (hw->phy.media_type == ixgbe_media_type_cx4) ||
+ ((hw->phy.media_type == ixgbe_media_type_fiber) &&
+ (speed == IXGBE_LINK_SPEED_10GB_FULL))) {
+ hw->fc.fc_was_autonegged = false;
+ hw->fc.current_mode = hw->fc.requested_mode;
+ goto out;
+ }
+
+ /*
* Read the AN advertisement and LP ability registers and resolve
* local flow control settings accordingly
*/
diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c
index a6bc1ef28f9..3c7a79a7d7c 100644
--- a/drivers/net/ixgbe/ixgbe_dcb_nl.c
+++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c
@@ -563,7 +563,7 @@ static u8 ixgbe_dcbnl_setapp(struct net_device *netdev,
return rval;
}
-struct dcbnl_rtnl_ops dcbnl_ops = {
+const struct dcbnl_rtnl_ops dcbnl_ops = {
.getstate = ixgbe_dcbnl_get_state,
.setstate = ixgbe_dcbnl_set_state,
.getpermhwaddr = ixgbe_dcbnl_get_perm_hw_addr,
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
index 856c18c207f..06a9d18bbdb 100644
--- a/drivers/net/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ixgbe/ixgbe_ethtool.c
@@ -40,19 +40,27 @@
#define IXGBE_ALL_RAR_ENTRIES 16
+enum {NETDEV_STATS, IXGBE_STATS};
+
struct ixgbe_stats {
char stat_string[ETH_GSTRING_LEN];
+ int type;
int sizeof_stat;
int stat_offset;
};
-#define IXGBE_STAT(m) sizeof(((struct ixgbe_adapter *)0)->m), \
- offsetof(struct ixgbe_adapter, m)
+#define IXGBE_STAT(m) IXGBE_STATS, \
+ sizeof(((struct ixgbe_adapter *)0)->m), \
+ offsetof(struct ixgbe_adapter, m)
+#define IXGBE_NETDEV_STAT(m) NETDEV_STATS, \
+ sizeof(((struct net_device *)0)->m), \
+ offsetof(struct net_device, m)
+
static struct ixgbe_stats ixgbe_gstrings_stats[] = {
- {"rx_packets", IXGBE_STAT(net_stats.rx_packets)},
- {"tx_packets", IXGBE_STAT(net_stats.tx_packets)},
- {"rx_bytes", IXGBE_STAT(net_stats.rx_bytes)},
- {"tx_bytes", IXGBE_STAT(net_stats.tx_bytes)},
+ {"rx_packets", IXGBE_NETDEV_STAT(stats.rx_packets)},
+ {"tx_packets", IXGBE_NETDEV_STAT(stats.tx_packets)},
+ {"rx_bytes", IXGBE_NETDEV_STAT(stats.rx_bytes)},
+ {"tx_bytes", IXGBE_NETDEV_STAT(stats.tx_bytes)},
{"rx_pkts_nic", IXGBE_STAT(stats.gprc)},
{"tx_pkts_nic", IXGBE_STAT(stats.gptc)},
{"rx_bytes_nic", IXGBE_STAT(stats.gorc)},
@@ -60,40 +68,36 @@ static struct ixgbe_stats ixgbe_gstrings_stats[] = {
{"lsc_int", IXGBE_STAT(lsc_int)},
{"tx_busy", IXGBE_STAT(tx_busy)},
{"non_eop_descs", IXGBE_STAT(non_eop_descs)},
- {"rx_errors", IXGBE_STAT(net_stats.rx_errors)},
- {"tx_errors", IXGBE_STAT(net_stats.tx_errors)},
- {"rx_dropped", IXGBE_STAT(net_stats.rx_dropped)},
- {"tx_dropped", IXGBE_STAT(net_stats.tx_dropped)},
- {"multicast", IXGBE_STAT(net_stats.multicast)},
+ {"rx_errors", IXGBE_NETDEV_STAT(stats.rx_errors)},
+ {"tx_errors", IXGBE_NETDEV_STAT(stats.tx_errors)},
+ {"rx_dropped", IXGBE_NETDEV_STAT(stats.rx_dropped)},
+ {"tx_dropped", IXGBE_NETDEV_STAT(stats.tx_dropped)},
+ {"multicast", IXGBE_NETDEV_STAT(stats.multicast)},
{"broadcast", IXGBE_STAT(stats.bprc)},
{"rx_no_buffer_count", IXGBE_STAT(stats.rnbc[0]) },
- {"collisions", IXGBE_STAT(net_stats.collisions)},
- {"rx_over_errors", IXGBE_STAT(net_stats.rx_over_errors)},
- {"rx_crc_errors", IXGBE_STAT(net_stats.rx_crc_errors)},
- {"rx_frame_errors", IXGBE_STAT(net_stats.rx_frame_errors)},
- {"hw_rsc_count", IXGBE_STAT(rsc_count)},
+ {"collisions", IXGBE_NETDEV_STAT(stats.collisions)},
+ {"rx_over_errors", IXGBE_NETDEV_STAT(stats.rx_over_errors)},
+ {"rx_crc_errors", IXGBE_NETDEV_STAT(stats.rx_crc_errors)},
+ {"rx_frame_errors", IXGBE_NETDEV_STAT(stats.rx_frame_errors)},
+ {"hw_rsc_aggregated", IXGBE_STAT(rsc_total_count)},
+ {"hw_rsc_flushed", IXGBE_STAT(rsc_total_flush)},
{"fdir_match", IXGBE_STAT(stats.fdirmatch)},
{"fdir_miss", IXGBE_STAT(stats.fdirmiss)},
- {"rx_fifo_errors", IXGBE_STAT(net_stats.rx_fifo_errors)},
- {"rx_missed_errors", IXGBE_STAT(net_stats.rx_missed_errors)},
- {"tx_aborted_errors", IXGBE_STAT(net_stats.tx_aborted_errors)},
- {"tx_carrier_errors", IXGBE_STAT(net_stats.tx_carrier_errors)},
- {"tx_fifo_errors", IXGBE_STAT(net_stats.tx_fifo_errors)},
- {"tx_heartbeat_errors", IXGBE_STAT(net_stats.tx_heartbeat_errors)},
+ {"rx_fifo_errors", IXGBE_NETDEV_STAT(stats.rx_fifo_errors)},
+ {"rx_missed_errors", IXGBE_NETDEV_STAT(stats.rx_missed_errors)},
+ {"tx_aborted_errors", IXGBE_NETDEV_STAT(stats.tx_aborted_errors)},
+ {"tx_carrier_errors", IXGBE_NETDEV_STAT(stats.tx_carrier_errors)},
+ {"tx_fifo_errors", IXGBE_NETDEV_STAT(stats.tx_fifo_errors)},
+ {"tx_heartbeat_errors", IXGBE_NETDEV_STAT(stats.tx_heartbeat_errors)},
{"tx_timeout_count", IXGBE_STAT(tx_timeout_count)},
{"tx_restart_queue", IXGBE_STAT(restart_queue)},
{"rx_long_length_errors", IXGBE_STAT(stats.roc)},
{"rx_short_length_errors", IXGBE_STAT(stats.ruc)},
- {"tx_tcp4_seg_ctxt", IXGBE_STAT(hw_tso_ctxt)},
- {"tx_tcp6_seg_ctxt", IXGBE_STAT(hw_tso6_ctxt)},
{"tx_flow_control_xon", IXGBE_STAT(stats.lxontxc)},
{"rx_flow_control_xon", IXGBE_STAT(stats.lxonrxc)},
{"tx_flow_control_xoff", IXGBE_STAT(stats.lxofftxc)},
{"rx_flow_control_xoff", IXGBE_STAT(stats.lxoffrxc)},
- {"rx_csum_offload_good", IXGBE_STAT(hw_csum_rx_good)},
{"rx_csum_offload_errors", IXGBE_STAT(hw_csum_rx_error)},
- {"tx_csum_offload_ctxt", IXGBE_STAT(hw_csum_tx_good)},
- {"rx_header_split", IXGBE_STAT(rx_hdr_split)},
{"alloc_rx_page_failed", IXGBE_STAT(alloc_rx_page_failed)},
{"alloc_rx_buff_failed", IXGBE_STAT(alloc_rx_buff_failed)},
{"rx_no_dma_resources", IXGBE_STAT(hw_rx_no_dma_resources)},
@@ -196,6 +200,56 @@ static int ixgbe_get_settings(struct net_device *netdev,
ecmd->autoneg = AUTONEG_DISABLE;
}
+ /* Get PHY type */
+ switch (adapter->hw.phy.type) {
+ case ixgbe_phy_tn:
+ case ixgbe_phy_cu_unknown:
+ /* Copper 10G-BASET */
+ ecmd->port = PORT_TP;
+ break;
+ case ixgbe_phy_qt:
+ ecmd->port = PORT_FIBRE;
+ break;
+ case ixgbe_phy_nl:
+ case ixgbe_phy_tw_tyco:
+ case ixgbe_phy_tw_unknown:
+ case ixgbe_phy_sfp_ftl:
+ case ixgbe_phy_sfp_avago:
+ case ixgbe_phy_sfp_intel:
+ case ixgbe_phy_sfp_unknown:
+ switch (adapter->hw.phy.sfp_type) {
+ /* SFP+ devices, further checking needed */
+ case ixgbe_sfp_type_da_cu:
+ case ixgbe_sfp_type_da_cu_core0:
+ case ixgbe_sfp_type_da_cu_core1:
+ ecmd->port = PORT_DA;
+ break;
+ case ixgbe_sfp_type_sr:
+ case ixgbe_sfp_type_lr:
+ case ixgbe_sfp_type_srlr_core0:
+ case ixgbe_sfp_type_srlr_core1:
+ ecmd->port = PORT_FIBRE;
+ break;
+ case ixgbe_sfp_type_not_present:
+ ecmd->port = PORT_NONE;
+ break;
+ case ixgbe_sfp_type_unknown:
+ default:
+ ecmd->port = PORT_OTHER;
+ break;
+ }
+ break;
+ case ixgbe_phy_xaui:
+ ecmd->port = PORT_NONE;
+ break;
+ case ixgbe_phy_unknown:
+ case ixgbe_phy_generic:
+ case ixgbe_phy_sfp_unsupported:
+ default:
+ ecmd->port = PORT_OTHER;
+ break;
+ }
+
hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
if (link_up) {
ecmd->speed = (link_speed == IXGBE_LINK_SPEED_10GB_FULL) ?
@@ -933,10 +987,21 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev,
int stat_count = sizeof(struct ixgbe_queue_stats) / sizeof(u64);
int j, k;
int i;
+ char *p = NULL;
ixgbe_update_stats(adapter);
for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
- char *p = (char *)adapter + ixgbe_gstrings_stats[i].stat_offset;
+ switch (ixgbe_gstrings_stats[i].type) {
+ case NETDEV_STATS:
+ p = (char *) netdev +
+ ixgbe_gstrings_stats[i].stat_offset;
+ break;
+ case IXGBE_STATS:
+ p = (char *) adapter +
+ ixgbe_gstrings_stats[i].stat_offset;
+ break;
+ }
+
data[i] = (ixgbe_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
@@ -1255,15 +1320,15 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
return 0;
} else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) {
shared_int = false;
- if (request_irq(irq, &ixgbe_test_intr, 0, netdev->name,
+ if (request_irq(irq, ixgbe_test_intr, 0, netdev->name,
netdev)) {
*data = 1;
return -1;
}
- } else if (!request_irq(irq, &ixgbe_test_intr, IRQF_PROBE_SHARED,
+ } else if (!request_irq(irq, ixgbe_test_intr, IRQF_PROBE_SHARED,
netdev->name, netdev)) {
shared_int = false;
- } else if (request_irq(irq, &ixgbe_test_intr, IRQF_SHARED,
+ } else if (request_irq(irq, ixgbe_test_intr, IRQF_SHARED,
netdev->name, netdev)) {
*data = 1;
return -1;
@@ -1952,6 +2017,10 @@ static int ixgbe_get_coalesce(struct net_device *netdev,
break;
}
+ /* if in mixed tx/rx queues per vector mode, report only rx settings */
+ if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count)
+ return 0;
+
/* only valid if in constant ITR mode */
switch (adapter->tx_itr_setting) {
case 0:
@@ -1977,12 +2046,9 @@ static int ixgbe_set_coalesce(struct net_device *netdev,
struct ixgbe_q_vector *q_vector;
int i;
- /*
- * don't accept tx specific changes if we've got mixed RxTx vectors
- * test and jump out here if needed before changing the rx numbers
- */
- if ((1000000/ec->tx_coalesce_usecs) != adapter->tx_eitr_param &&
- adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count)
+ /* don't accept tx specific changes if we've got mixed RxTx vectors */
+ if (adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count
+ && ec->tx_coalesce_usecs)
return -EINVAL;
if (ec->tx_max_coalesced_frames_irq)
diff --git a/drivers/net/ixgbe/ixgbe_fcoe.c b/drivers/net/ixgbe/ixgbe_fcoe.c
index a3c9f99515e..da32a108a7b 100644
--- a/drivers/net/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ixgbe/ixgbe_fcoe.c
@@ -499,6 +499,10 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
struct ixgbe_fcoe *fcoe = &adapter->fcoe;
struct ixgbe_ring_feature *f = &adapter->ring_feature[RING_F_FCOE];
+#ifdef CONFIG_IXGBE_DCB
+ u8 tc;
+ u32 up2tc;
+#endif
/* create the pool for ddp if not created yet */
if (!fcoe->pool) {
@@ -540,6 +544,17 @@ void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
IXGBE_FCRXCTRL_FCOELLI |
IXGBE_FCRXCTRL_FCCRCBO |
(FC_FCOE_VER << IXGBE_FCRXCTRL_FCOEVER_SHIFT));
+#ifdef CONFIG_IXGBE_DCB
+ up2tc = IXGBE_READ_REG(&adapter->hw, IXGBE_RTTUP2TC);
+ for (i = 0; i < MAX_USER_PRIORITY; i++) {
+ tc = (u8)(up2tc >> (i * IXGBE_RTTUP2TC_UP_SHIFT));
+ tc &= (MAX_TRAFFIC_CLASS - 1);
+ if (fcoe->tc == tc) {
+ fcoe->up = i;
+ break;
+ }
+ }
+#endif
}
/**
@@ -671,19 +686,7 @@ out_disable:
*/
u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter)
{
- int i;
- u8 tc;
- u32 up2tc;
-
- up2tc = IXGBE_READ_REG(&adapter->hw, IXGBE_RTTUP2TC);
- for (i = 0; i < MAX_USER_PRIORITY; i++) {
- tc = (u8)(up2tc >> (i * IXGBE_RTTUP2TC_UP_SHIFT));
- tc &= (MAX_TRAFFIC_CLASS - 1);
- if (adapter->fcoe.tc == tc)
- return 1 << i;
- }
-
- return 0;
+ return 1 << adapter->fcoe.up;
}
/**
@@ -710,6 +713,7 @@ u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up)
up2tc >>= (i * IXGBE_RTTUP2TC_UP_SHIFT);
up2tc &= (MAX_TRAFFIC_CLASS - 1);
adapter->fcoe.tc = (u8)up2tc;
+ adapter->fcoe.up = i;
return 0;
}
}
@@ -718,3 +722,49 @@ u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up)
return 1;
}
#endif /* CONFIG_IXGBE_DCB */
+
+/**
+ * ixgbe_fcoe_get_wwn - get world wide name for the node or the port
+ * @netdev : ixgbe adapter
+ * @wwn : the world wide name
+ * @type: the type of world wide name
+ *
+ * Returns the node or port world wide name if both the prefix and the san
+ * mac address are valid, then the wwn is formed based on the NAA-2 for
+ * IEEE Extended name identifier (ref. to T10 FC-LS Spec., Sec. 15.3).
+ *
+ * Returns : 0 on success
+ */
+int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
+{
+ int rc = -EINVAL;
+ u16 prefix = 0xffff;
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_mac_info *mac = &adapter->hw.mac;
+
+ switch (type) {
+ case NETDEV_FCOE_WWNN:
+ prefix = mac->wwnn_prefix;
+ break;
+ case NETDEV_FCOE_WWPN:
+ prefix = mac->wwpn_prefix;
+ break;
+ default:
+ break;
+ }
+
+ if ((prefix != 0xffff) &&
+ is_valid_ether_addr(mac->san_addr)) {
+ *wwn = ((u64) prefix << 48) |
+ ((u64) mac->san_addr[0] << 40) |
+ ((u64) mac->san_addr[1] << 32) |
+ ((u64) mac->san_addr[2] << 24) |
+ ((u64) mac->san_addr[3] << 16) |
+ ((u64) mac->san_addr[4] << 8) |
+ ((u64) mac->san_addr[5]);
+ rc = 0;
+ }
+ return rc;
+}
+
+
diff --git a/drivers/net/ixgbe/ixgbe_fcoe.h b/drivers/net/ixgbe/ixgbe_fcoe.h
index b5dee7b3ef1..de8ff53187d 100644
--- a/drivers/net/ixgbe/ixgbe_fcoe.h
+++ b/drivers/net/ixgbe/ixgbe_fcoe.h
@@ -62,7 +62,10 @@ struct ixgbe_fcoe_ddp {
};
struct ixgbe_fcoe {
+#ifdef CONFIG_IXGBE_DCB
u8 tc;
+ u8 up;
+#endif
spinlock_t lock;
struct pci_pool *pool;
struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX];
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index a456578b857..247ed2a2476 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -98,6 +98,8 @@ static struct pci_device_id ixgbe_pci_tbl[] = {
board_82599 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP),
board_82599 },
+ {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM),
+ board_82599 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KX4_MEZZ),
board_82599 },
{PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_CX4),
@@ -216,10 +218,20 @@ static void ixgbe_unmap_and_free_tx_resource(struct ixgbe_adapter *adapter,
struct ixgbe_tx_buffer
*tx_buffer_info)
{
- tx_buffer_info->dma = 0;
+ if (tx_buffer_info->dma) {
+ if (tx_buffer_info->mapped_as_page)
+ pci_unmap_page(adapter->pdev,
+ tx_buffer_info->dma,
+ tx_buffer_info->length,
+ PCI_DMA_TODEVICE);
+ else
+ pci_unmap_single(adapter->pdev,
+ tx_buffer_info->dma,
+ tx_buffer_info->length,
+ PCI_DMA_TODEVICE);
+ tx_buffer_info->dma = 0;
+ }
if (tx_buffer_info->skb) {
- skb_dma_unmap(&adapter->pdev->dev, tx_buffer_info->skb,
- DMA_TO_DEVICE);
dev_kfree_skb_any(tx_buffer_info->skb);
tx_buffer_info->skb = NULL;
}
@@ -401,7 +413,7 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector,
if (__netif_subqueue_stopped(netdev, tx_ring->queue_index) &&
!test_bit(__IXGBE_DOWN, &adapter->state)) {
netif_wake_subqueue(netdev, tx_ring->queue_index);
- ++adapter->restart_queue;
+ ++tx_ring->restart_queue;
}
}
@@ -423,8 +435,8 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector,
tx_ring->total_packets += total_packets;
tx_ring->stats.packets += total_packets;
tx_ring->stats.bytes += total_bytes;
- adapter->net_stats.tx_bytes += total_bytes;
- adapter->net_stats.tx_packets += total_packets;
+ netdev->stats.tx_bytes += total_bytes;
+ netdev->stats.tx_packets += total_packets;
return (count < tx_ring->work_limit);
}
@@ -612,7 +624,6 @@ static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter,
/* It must be a TCP or UDP packet with a valid checksum */
skb->ip_summed = CHECKSUM_UNNECESSARY;
- adapter->hw_csum_rx_good++;
}
static inline void ixgbe_release_rx_desc(struct ixgbe_hw *hw,
@@ -669,21 +680,18 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
if (!bi->skb) {
struct sk_buff *skb;
- skb = netdev_alloc_skb(adapter->netdev,
- (rx_ring->rx_buf_len +
- NET_IP_ALIGN));
+ /* netdev_alloc_skb reserves 32 bytes up front!! */
+ uint bufsz = rx_ring->rx_buf_len + SMP_CACHE_BYTES;
+ skb = netdev_alloc_skb(adapter->netdev, bufsz);
if (!skb) {
adapter->alloc_rx_buff_failed++;
goto no_buffers;
}
- /*
- * Make buffer alignment 2 beyond a 16 byte boundary
- * this will result in a 16 byte aligned IP header after
- * the 14 byte MAC header is removed
- */
- skb_reserve(skb, NET_IP_ALIGN);
+ /* advance the data pointer to the next cache line */
+ skb_reserve(skb, (PTR_ALIGN(skb->data, SMP_CACHE_BYTES)
+ - skb->data));
bi->skb = skb;
bi->dma = pci_map_single(pdev, skb->data,
@@ -735,12 +743,14 @@ static inline u32 ixgbe_get_rsc_count(union ixgbe_adv_rx_desc *rx_desc)
/**
* ixgbe_transform_rsc_queue - change rsc queue into a full packet
* @skb: pointer to the last skb in the rsc queue
+ * @count: pointer to number of packets coalesced in this context
*
* This function changes a queue full of hw rsc buffers into a completed
* packet. It uses the ->prev pointers to find the first packet and then
* turns it into the frag list owner.
**/
-static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb)
+static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb,
+ u64 *count)
{
unsigned int frag_list_size = 0;
@@ -749,6 +759,7 @@ static inline struct sk_buff *ixgbe_transform_rsc_queue(struct sk_buff *skb)
frag_list_size += skb->len;
skb->prev = NULL;
skb = prev;
+ *count += 1;
}
skb_shinfo(skb)->frag_list = skb->next;
@@ -764,6 +775,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
int *work_done, int work_to_do)
{
struct ixgbe_adapter *adapter = q_vector->adapter;
+ struct net_device *netdev = adapter->netdev;
struct pci_dev *pdev = adapter->pdev;
union ixgbe_adv_rx_desc *rx_desc, *next_rxd;
struct ixgbe_rx_buffer *rx_buffer_info, *next_buffer;
@@ -793,8 +805,6 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
hdr_info = le16_to_cpu(ixgbe_get_hdr_info(rx_desc));
len = (hdr_info & IXGBE_RXDADV_HDRBUFLEN_MASK) >>
IXGBE_RXDADV_HDRBUFLEN_SHIFT;
- if (hdr_info & IXGBE_RXDADV_SPH)
- adapter->rx_hdr_split++;
if (len > IXGBE_RX_HDR_SIZE)
len = IXGBE_RX_HDR_SIZE;
upper_len = le16_to_cpu(rx_desc->wb.upper.length);
@@ -804,7 +814,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
cleaned = true;
skb = rx_buffer_info->skb;
- prefetch(skb->data - NET_IP_ALIGN);
+ prefetch(skb->data);
rx_buffer_info->skb = NULL;
if (rx_buffer_info->dma) {
@@ -850,14 +860,20 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
u32 nextp = (staterr & IXGBE_RXDADV_NEXTP_MASK) >>
IXGBE_RXDADV_NEXTP_SHIFT;
next_buffer = &rx_ring->rx_buffer_info[nextp];
- rx_ring->rsc_count += (rsc_count - 1);
} else {
next_buffer = &rx_ring->rx_buffer_info[i];
}
if (staterr & IXGBE_RXD_STAT_EOP) {
if (skb->prev)
- skb = ixgbe_transform_rsc_queue(skb);
+ skb = ixgbe_transform_rsc_queue(skb, &(rx_ring->rsc_count));
+ if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
+ if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED)
+ rx_ring->rsc_count += skb_shinfo(skb)->nr_frags;
+ else
+ rx_ring->rsc_count++;
+ rx_ring->rsc_flush++;
+ }
rx_ring->stats.packets++;
rx_ring->stats.bytes += skb->len;
} else {
@@ -870,7 +886,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
skb->next = next_buffer->skb;
skb->next->prev = skb;
}
- adapter->non_eop_descs++;
+ rx_ring->non_eop_descs++;
goto next_desc;
}
@@ -935,8 +951,8 @@ next_desc:
rx_ring->total_packets += total_rx_packets;
rx_ring->total_bytes += total_rx_bytes;
- adapter->net_stats.rx_bytes += total_rx_bytes;
- adapter->net_stats.rx_packets += total_rx_packets;
+ netdev->stats.rx_bytes += total_rx_bytes;
+ netdev->stats.rx_packets += total_rx_packets;
return cleaned;
}
@@ -1209,6 +1225,7 @@ static void ixgbe_check_lsc(struct ixgbe_adapter *adapter)
adapter->link_check_timeout = jiffies;
if (!test_bit(__IXGBE_DOWN, &adapter->state)) {
IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_LSC);
+ IXGBE_WRITE_FLUSH(hw);
schedule_work(&adapter->watchdog_task);
}
}
@@ -1312,8 +1329,7 @@ static irqreturn_t ixgbe_msix_clean_tx(int irq, void *data)
r_idx + 1);
}
- /* disable interrupts on this vector only */
- ixgbe_irq_disable_queues(adapter, ((u64)1 << q_vector->v_idx));
+ /* EIAM disabled interrupts (on this vector) for us */
napi_schedule(&q_vector->napi);
return IRQ_HANDLED;
@@ -1344,10 +1360,8 @@ static irqreturn_t ixgbe_msix_clean_rx(int irq, void *data)
if (!q_vector->rxr_count)
return IRQ_HANDLED;
- r_idx = find_first_bit(q_vector->rxr_idx, adapter->num_rx_queues);
- rx_ring = &(adapter->rx_ring[r_idx]);
/* disable interrupts on this vector only */
- ixgbe_irq_disable_queues(adapter, ((u64)1 << q_vector->v_idx));
+ /* EIAM disabled interrupts (on this vector) for us */
napi_schedule(&q_vector->napi);
return IRQ_HANDLED;
@@ -1382,8 +1396,7 @@ static irqreturn_t ixgbe_msix_clean_many(int irq, void *data)
r_idx + 1);
}
- /* disable interrupts on this vector only */
- ixgbe_irq_disable_queues(adapter, ((u64)1 << q_vector->v_idx));
+ /* EIAM disabled interrupts (on this vector) for us */
napi_schedule(&q_vector->napi);
return IRQ_HANDLED;
@@ -1667,7 +1680,7 @@ static int ixgbe_request_msix_irqs(struct ixgbe_adapter *adapter)
sprintf(adapter->name[vector], "%s:lsc", netdev->name);
err = request_irq(adapter->msix_entries[vector].vector,
- &ixgbe_msix_lsc, 0, adapter->name[vector], netdev);
+ ixgbe_msix_lsc, 0, adapter->name[vector], netdev);
if (err) {
DPRINTK(PROBE, ERR,
"request_irq for msix_lsc failed: %d\n", err);
@@ -1838,10 +1851,10 @@ static int ixgbe_request_irq(struct ixgbe_adapter *adapter)
if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
err = ixgbe_request_msix_irqs(adapter);
} else if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) {
- err = request_irq(adapter->pdev->irq, &ixgbe_intr, 0,
+ err = request_irq(adapter->pdev->irq, ixgbe_intr, 0,
netdev->name, netdev);
} else {
- err = request_irq(adapter->pdev->irq, &ixgbe_intr, IRQF_SHARED,
+ err = request_irq(adapter->pdev->irq, ixgbe_intr, IRQF_SHARED,
netdev->name, netdev);
}
@@ -2063,18 +2076,18 @@ static u32 ixgbe_setup_mrqc(struct ixgbe_adapter *adapter)
* ixgbe_configure_rscctl - enable RSC for the indicated ring
* @adapter: address of board private structure
* @index: index of ring to set
- * @rx_buf_len: rx buffer length
**/
-static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, int index,
- int rx_buf_len)
+static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, int index)
{
struct ixgbe_ring *rx_ring;
struct ixgbe_hw *hw = &adapter->hw;
int j;
u32 rscctrl;
+ int rx_buf_len;
rx_ring = &adapter->rx_ring[index];
j = rx_ring->reg_idx;
+ rx_buf_len = rx_ring->rx_buf_len;
rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j));
rscctrl |= IXGBE_RSCCTL_RSCEN;
/*
@@ -2282,7 +2295,7 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
/* Enable 82599 HW-RSC */
for (i = 0; i < adapter->num_rx_queues; i++)
- ixgbe_configure_rscctl(adapter, i, rx_buf_len);
+ ixgbe_configure_rscctl(adapter, i);
/* Disable RSC for ACK packets */
IXGBE_WRITE_REG(hw, IXGBE_RSCDBU,
@@ -2333,23 +2346,25 @@ static void ixgbe_vlan_rx_register(struct net_device *netdev,
* not in DCB mode.
*/
ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL);
+
+ /* Disable CFI check */
+ ctrl &= ~IXGBE_VLNCTRL_CFIEN;
+
+ /* enable VLAN tag stripping */
if (adapter->hw.mac.type == ixgbe_mac_82598EB) {
- ctrl |= IXGBE_VLNCTRL_VME | IXGBE_VLNCTRL_VFE;
- ctrl &= ~IXGBE_VLNCTRL_CFIEN;
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl);
+ ctrl |= IXGBE_VLNCTRL_VME;
} else if (adapter->hw.mac.type == ixgbe_mac_82599EB) {
- ctrl |= IXGBE_VLNCTRL_VFE;
- /* enable VLAN tag insert/strip */
- ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL);
- ctrl &= ~IXGBE_VLNCTRL_CFIEN;
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl);
for (i = 0; i < adapter->num_rx_queues; i++) {
+ u32 ctrl;
j = adapter->rx_ring[i].reg_idx;
ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_RXDCTL(j));
ctrl |= IXGBE_RXDCTL_VME;
IXGBE_WRITE_REG(&adapter->hw, IXGBE_RXDCTL(j), ctrl);
}
}
+
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl);
+
ixgbe_vlan_rx_add_vid(netdev, 0);
if (!test_bit(__IXGBE_DOWN, &adapter->state))
@@ -2699,7 +2714,22 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter)
IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie);
}
- if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED)) {
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+ /*
+ * use EIAM to auto-mask when MSI-X interrupt is asserted
+ * this saves a register write for every interrupt
+ */
+ switch (hw->mac.type) {
+ case ixgbe_mac_82598EB:
+ IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE);
+ break;
+ default:
+ case ixgbe_mac_82599EB:
+ IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(0), 0xFFFFFFFF);
+ IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(1), 0xFFFFFFFF);
+ break;
+ }
+ } else {
/* legacy interrupts, use EIAM to auto-mask when reading EICR,
* specifically only auto mask tx and rx interrupts */
IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE);
@@ -3632,10 +3662,10 @@ static int ixgbe_set_interrupt_capability(struct ixgbe_adapter *adapter)
* It's easy to be greedy for MSI-X vectors, but it really
* doesn't do us much good if we have a lot more vectors
* than CPU's. So let's be conservative and only ask for
- * (roughly) twice the number of vectors as there are CPU's.
+ * (roughly) the same number of vectors as there are CPU's.
*/
v_budget = min(adapter->num_rx_queues + adapter->num_tx_queues,
- (int)(num_online_cpus() * 2)) + NON_Q_VECTORS;
+ (int)num_online_cpus()) + NON_Q_VECTORS;
/*
* At the same time, hardware can only support a maximum of
@@ -3943,8 +3973,10 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
adapter->flags |= IXGBE_FLAG_FCOE_CAPABLE;
adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED;
adapter->ring_feature[RING_F_FCOE].indices = 0;
+#ifdef CONFIG_IXGBE_DCB
/* Default traffic class to use for FCoE */
adapter->fcoe.tc = IXGBE_FCOE_DEFTC;
+#endif
#endif /* IXGBE_FCOE */
}
@@ -4475,20 +4507,32 @@ static void ixgbe_shutdown(struct pci_dev *pdev)
**/
void ixgbe_update_stats(struct ixgbe_adapter *adapter)
{
+ struct net_device *netdev = adapter->netdev;
struct ixgbe_hw *hw = &adapter->hw;
u64 total_mpc = 0;
u32 i, missed_rx = 0, mpc, bprc, lxon, lxoff, xon_off_tot;
- if (hw->mac.type == ixgbe_mac_82599EB) {
+ if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
u64 rsc_count = 0;
+ u64 rsc_flush = 0;
for (i = 0; i < 16; i++)
adapter->hw_rx_no_dma_resources +=
IXGBE_READ_REG(hw, IXGBE_QPRDC(i));
- for (i = 0; i < adapter->num_rx_queues; i++)
+ for (i = 0; i < adapter->num_rx_queues; i++) {
rsc_count += adapter->rx_ring[i].rsc_count;
- adapter->rsc_count = rsc_count;
+ rsc_flush += adapter->rx_ring[i].rsc_flush;
+ }
+ adapter->rsc_total_count = rsc_count;
+ adapter->rsc_total_flush = rsc_flush;
}
+ /* gather some stats to the adapter struct that are per queue */
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ adapter->restart_queue += adapter->tx_ring[i].restart_queue;
+
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ adapter->non_eop_descs += adapter->tx_ring[i].non_eop_descs;
+
adapter->stats.crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS);
for (i = 0; i < 8; i++) {
/* for packet buffers not used, the register should read 0 */
@@ -4594,15 +4638,15 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter)
adapter->stats.bptc += IXGBE_READ_REG(hw, IXGBE_BPTC);
/* Fill out the OS statistics structure */
- adapter->net_stats.multicast = adapter->stats.mprc;
+ netdev->stats.multicast = adapter->stats.mprc;
/* Rx Errors */
- adapter->net_stats.rx_errors = adapter->stats.crcerrs +
+ netdev->stats.rx_errors = adapter->stats.crcerrs +
adapter->stats.rlec;
- adapter->net_stats.rx_dropped = 0;
- adapter->net_stats.rx_length_errors = adapter->stats.rlec;
- adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
- adapter->net_stats.rx_missed_errors = total_mpc;
+ netdev->stats.rx_dropped = 0;
+ netdev->stats.rx_length_errors = adapter->stats.rlec;
+ netdev->stats.rx_crc_errors = adapter->stats.crcerrs;
+ netdev->stats.rx_missed_errors = total_mpc;
}
/**
@@ -4871,14 +4915,12 @@ static int ixgbe_tso(struct ixgbe_adapter *adapter,
iph->daddr, 0,
IPPROTO_TCP,
0);
- adapter->hw_tso_ctxt++;
} else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
ipv6_hdr(skb)->payload_len = 0;
tcp_hdr(skb)->check =
~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
0, IPPROTO_TCP, 0);
- adapter->hw_tso6_ctxt++;
}
i = tx_ring->next_to_use;
@@ -4997,7 +5039,6 @@ static bool ixgbe_tx_csum(struct ixgbe_adapter *adapter,
tx_buffer_info->time_stamp = jiffies;
tx_buffer_info->next_to_watch = i;
- adapter->hw_csum_tx_good++;
i++;
if (i == tx_ring->count)
i = 0;
@@ -5014,23 +5055,16 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
struct sk_buff *skb, u32 tx_flags,
unsigned int first)
{
+ struct pci_dev *pdev = adapter->pdev;
struct ixgbe_tx_buffer *tx_buffer_info;
unsigned int len;
unsigned int total = skb->len;
unsigned int offset = 0, size, count = 0, i;
unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
unsigned int f;
- dma_addr_t *map;
i = tx_ring->next_to_use;
- if (skb_dma_map(&adapter->pdev->dev, skb, DMA_TO_DEVICE)) {
- dev_err(&adapter->pdev->dev, "TX DMA map failed\n");
- return 0;
- }
-
- map = skb_shinfo(skb)->dma_maps;
-
if (tx_flags & IXGBE_TX_FLAGS_FCOE)
/* excluding fcoe_crc_eof for FCoE */
total -= sizeof(struct fcoe_crc_eof);
@@ -5041,7 +5075,12 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
tx_buffer_info->length = size;
- tx_buffer_info->dma = skb_shinfo(skb)->dma_head + offset;
+ tx_buffer_info->mapped_as_page = false;
+ tx_buffer_info->dma = pci_map_single(pdev,
+ skb->data + offset,
+ size, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pdev, tx_buffer_info->dma))
+ goto dma_error;
tx_buffer_info->time_stamp = jiffies;
tx_buffer_info->next_to_watch = i;
@@ -5062,7 +5101,7 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
frag = &skb_shinfo(skb)->frags[f];
len = min((unsigned int)frag->size, total);
- offset = 0;
+ offset = frag->page_offset;
while (len) {
i++;
@@ -5073,7 +5112,13 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
tx_buffer_info->length = size;
- tx_buffer_info->dma = map[f] + offset;
+ tx_buffer_info->dma = pci_map_page(adapter->pdev,
+ frag->page,
+ offset, size,
+ PCI_DMA_TODEVICE);
+ tx_buffer_info->mapped_as_page = true;
+ if (pci_dma_mapping_error(pdev, tx_buffer_info->dma))
+ goto dma_error;
tx_buffer_info->time_stamp = jiffies;
tx_buffer_info->next_to_watch = i;
@@ -5090,6 +5135,27 @@ static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
tx_ring->tx_buffer_info[first].next_to_watch = i;
return count;
+
+dma_error:
+ dev_err(&pdev->dev, "TX DMA map failed\n");
+
+ /* clear timestamp and dma mappings for failed tx_buffer_info map */
+ tx_buffer_info->dma = 0;
+ tx_buffer_info->time_stamp = 0;
+ tx_buffer_info->next_to_watch = 0;
+ count--;
+
+ /* clear timestamp and dma mappings for remaining portion of packet */
+ while (count >= 0) {
+ count--;
+ i--;
+ if (i < 0)
+ i += tx_ring->count;
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ ixgbe_unmap_and_free_tx_resource(adapter, tx_buffer_info);
+ }
+
+ return count;
}
static void ixgbe_tx_queue(struct ixgbe_adapter *adapter,
@@ -5209,8 +5275,6 @@ static void ixgbe_atr(struct ixgbe_adapter *adapter, struct sk_buff *skb,
static int __ixgbe_maybe_stop_tx(struct net_device *netdev,
struct ixgbe_ring *tx_ring, int size)
{
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
-
netif_stop_subqueue(netdev, tx_ring->queue_index);
/* Herbert's original patch had:
* smp_mb__after_netif_stop_queue();
@@ -5224,7 +5288,7 @@ static int __ixgbe_maybe_stop_tx(struct net_device *netdev,
/* A reprieve! - use start_queue because it doesn't call schedule */
netif_start_subqueue(netdev, tx_ring->queue_index);
- ++adapter->restart_queue;
+ ++tx_ring->restart_queue;
return 0;
}
@@ -5239,10 +5303,19 @@ static int ixgbe_maybe_stop_tx(struct net_device *netdev,
static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
+ int txq = smp_processor_id();
if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE)
- return smp_processor_id();
+ return txq;
+#ifdef IXGBE_FCOE
+ if ((adapter->flags & IXGBE_FLAG_FCOE_ENABLED) &&
+ (skb->protocol == htons(ETH_P_FCOE))) {
+ txq &= (adapter->ring_feature[RING_F_FCOE].indices - 1);
+ txq += adapter->ring_feature[RING_F_FCOE].mask;
+ return txq;
+ }
+#endif
if (adapter->flags & IXGBE_FLAG_DCB_ENABLED)
return (skb->vlan_tci & IXGBE_TX_FLAGS_VLAN_PRIO_MASK) >> 13;
@@ -5257,7 +5330,7 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb,
unsigned int first;
unsigned int tx_flags = 0;
u8 hdr_len = 0;
- int r_idx = 0, tso;
+ int tso;
int count = 0;
unsigned int f;
@@ -5265,13 +5338,13 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb,
tx_flags |= vlan_tx_tag_get(skb);
if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
tx_flags &= ~IXGBE_TX_FLAGS_VLAN_PRIO_MASK;
- tx_flags |= (skb->queue_mapping << 13);
+ tx_flags |= ((skb->queue_mapping & 0x7) << 13);
}
tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT;
tx_flags |= IXGBE_TX_FLAGS_VLAN;
} else if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) {
if (skb->priority != TC_PRIO_CONTROL) {
- tx_flags |= (skb->queue_mapping << 13);
+ tx_flags |= ((skb->queue_mapping & 0x7) << 13);
tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT;
tx_flags |= IXGBE_TX_FLAGS_VLAN;
} else {
@@ -5280,17 +5353,18 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb,
}
}
- r_idx = skb->queue_mapping;
- tx_ring = &adapter->tx_ring[r_idx];
+ tx_ring = &adapter->tx_ring[skb->queue_mapping];
if ((adapter->flags & IXGBE_FLAG_FCOE_ENABLED) &&
(skb->protocol == htons(ETH_P_FCOE))) {
tx_flags |= IXGBE_TX_FLAGS_FCOE;
#ifdef IXGBE_FCOE
- r_idx = smp_processor_id();
- r_idx &= (adapter->ring_feature[RING_F_FCOE].indices - 1);
- r_idx += adapter->ring_feature[RING_F_FCOE].mask;
- tx_ring = &adapter->tx_ring[r_idx];
+#ifdef CONFIG_IXGBE_DCB
+ tx_flags &= ~(IXGBE_TX_FLAGS_VLAN_PRIO_MASK
+ << IXGBE_TX_FLAGS_VLAN_SHIFT);
+ tx_flags |= ((adapter->fcoe.up << 13)
+ << IXGBE_TX_FLAGS_VLAN_SHIFT);
+#endif
#endif
}
/* four things can cause us to need a context descriptor */
@@ -5372,10 +5446,8 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb,
**/
static struct net_device_stats *ixgbe_get_stats(struct net_device *netdev)
{
- struct ixgbe_adapter *adapter = netdev_priv(netdev);
-
/* only return the current stats */
- return &adapter->net_stats;
+ return &netdev->stats;
}
/**
@@ -5527,6 +5599,7 @@ static const struct net_device_ops ixgbe_netdev_ops = {
.ndo_fcoe_ddp_done = ixgbe_fcoe_ddp_put,
.ndo_fcoe_enable = ixgbe_fcoe_enable,
.ndo_fcoe_disable = ixgbe_fcoe_disable,
+ .ndo_fcoe_get_wwn = ixgbe_fcoe_get_wwn,
#endif /* IXGBE_FCOE */
};
diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h
index ef4bdd58e01..21b6633da57 100644
--- a/drivers/net/ixgbe/ixgbe_type.h
+++ b/drivers/net/ixgbe/ixgbe_type.h
@@ -52,6 +52,7 @@
#define IXGBE_DEV_ID_82599_KX4_MEZZ 0x1514
#define IXGBE_DEV_ID_82599_CX4 0x10F9
#define IXGBE_DEV_ID_82599_SFP 0x10FB
+#define IXGBE_DEV_ID_82599_SFP_EM 0x1507
#define IXGBE_DEV_ID_82599_XAUI_LOM 0x10FC
#define IXGBE_DEV_ID_82599_COMBO_BACKPLANE 0x10F8
@@ -1538,6 +1539,16 @@
#define IXGBE_FW_PASSTHROUGH_PATCH_CONFIG_PTR 0x4
#define IXGBE_FW_PATCH_VERSION_4 0x7
+/* Alternative SAN MAC Address Block */
+#define IXGBE_ALT_SAN_MAC_ADDR_BLK_PTR 0x27 /* Alt. SAN MAC block */
+#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_OFFSET 0x0 /* Alt. SAN MAC capability */
+#define IXGBE_ALT_SAN_MAC_ADDR_PORT0_OFFSET 0x1 /* Alt. SAN MAC 0 offset */
+#define IXGBE_ALT_SAN_MAC_ADDR_PORT1_OFFSET 0x4 /* Alt. SAN MAC 1 offset */
+#define IXGBE_ALT_SAN_MAC_ADDR_WWNN_OFFSET 0x7 /* Alt. WWNN prefix offset */
+#define IXGBE_ALT_SAN_MAC_ADDR_WWPN_OFFSET 0x8 /* Alt. WWPN prefix offset */
+#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_SANMAC 0x0 /* Alt. SAN MAC exists */
+#define IXGBE_ALT_SAN_MAC_ADDR_CAPS_ALTWWN 0x1 /* Alt. WWN base exists */
+
/* PCI Bus Info */
#define IXGBE_PCI_LINK_STATUS 0xB2
#define IXGBE_PCI_DEVICE_CONTROL2 0xC8
@@ -2171,6 +2182,14 @@ enum ixgbe_fc_mode {
ixgbe_fc_default
};
+/* Smart Speed Settings */
+#define IXGBE_SMARTSPEED_MAX_RETRIES 3
+enum ixgbe_smart_speed {
+ ixgbe_smart_speed_auto = 0,
+ ixgbe_smart_speed_on,
+ ixgbe_smart_speed_off
+};
+
/* PCI bus types */
enum ixgbe_bus_type {
ixgbe_bus_type_unknown = 0,
@@ -2336,6 +2355,7 @@ struct ixgbe_mac_operations {
s32 (*get_mac_addr)(struct ixgbe_hw *, u8 *);
s32 (*get_san_mac_addr)(struct ixgbe_hw *, u8 *);
s32 (*get_device_caps)(struct ixgbe_hw *, u16 *);
+ s32 (*get_wwn_prefix)(struct ixgbe_hw *, u16 *, u16 *);
s32 (*stop_adapter)(struct ixgbe_hw *);
s32 (*get_bus_info)(struct ixgbe_hw *);
void (*set_lan_id)(struct ixgbe_hw *);
@@ -2407,6 +2427,10 @@ struct ixgbe_mac_info {
u8 addr[IXGBE_ETH_LENGTH_OF_ADDRESS];
u8 perm_addr[IXGBE_ETH_LENGTH_OF_ADDRESS];
u8 san_addr[IXGBE_ETH_LENGTH_OF_ADDRESS];
+ /* prefix for World Wide Node Name (WWNN) */
+ u16 wwnn_prefix;
+ /* prefix for World Wide Port Name (WWPN) */
+ u16 wwpn_prefix;
s32 mc_filter_type;
u32 mcft_size;
u32 vft_size;
@@ -2431,6 +2455,8 @@ struct ixgbe_phy_info {
enum ixgbe_media_type media_type;
bool reset_disable;
ixgbe_autoneg_advertised autoneg_advertised;
+ enum ixgbe_smart_speed smart_speed;
+ bool smart_speed_active;
bool multispeed_fiber;
};
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
index 9aee0cc922c..e9d9d595e1b 100644
--- a/drivers/net/ixp2000/ixpdev.c
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -109,9 +109,8 @@ static int ixpdev_rx(struct net_device *dev, int processed, int budget)
if (unlikely(!netif_running(nds[desc->channel])))
goto err;
- skb = netdev_alloc_skb(dev, desc->pkt_length + 2);
+ skb = netdev_alloc_skb_ip_align(dev, desc->pkt_length);
if (likely(skb != NULL)) {
- skb_reserve(skb, 2);
skb_copy_to_linear_data(skb, buf, desc->pkt_length);
skb_put(skb, desc->pkt_length);
skb->protocol = eth_type_trans(skb, nds[desc->channel]);
diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c
index 6e5b3f30527..f47d4d663b1 100644
--- a/drivers/net/jazzsonic.c
+++ b/drivers/net/jazzsonic.c
@@ -81,7 +81,7 @@ static unsigned short known_revisions[] =
static int jazzsonic_open(struct net_device* dev)
{
- if (request_irq(dev->irq, &sonic_interrupt, IRQF_DISABLED, "sonic", dev)) {
+ if (request_irq(dev->irq, sonic_interrupt, IRQF_DISABLED, "sonic", dev)) {
printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq);
return -EAGAIN;
}
@@ -130,8 +130,8 @@ static int __devinit sonic_probe1(struct net_device *dev)
printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
i = 0;
- while (known_revisions[i] != 0xffff
- && known_revisions[i] != silicon_revision)
+ while (known_revisions[i] != 0xffff &&
+ known_revisions[i] != silicon_revision)
i++;
if (known_revisions[i] == 0xffff) {
diff --git a/drivers/net/jme.c b/drivers/net/jme.c
index 1d2a32544ed..792b88fc357 100644
--- a/drivers/net/jme.c
+++ b/drivers/net/jme.c
@@ -1050,8 +1050,8 @@ jme_dynamic_pcc(struct jme_adapter *jme)
if ((NET_STAT(jme).rx_bytes - dpi->last_bytes) > PCC_P3_THRESHOLD)
jme_attempt_pcc(dpi, PCC_P3);
- else if ((NET_STAT(jme).rx_packets - dpi->last_pkts) > PCC_P2_THRESHOLD
- || dpi->intr_cnt > PCC_INTR_THRESHOLD)
+ else if ((NET_STAT(jme).rx_packets - dpi->last_pkts) > PCC_P2_THRESHOLD ||
+ dpi->intr_cnt > PCC_INTR_THRESHOLD)
jme_attempt_pcc(dpi, PCC_P2);
else
jme_attempt_pcc(dpi, PCC_P1);
@@ -2199,8 +2199,8 @@ jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd)
if (netif_running(netdev))
return -EBUSY;
- if (ecmd->use_adaptive_rx_coalesce
- && test_bit(JME_FLAG_POLL, &jme->flags)) {
+ if (ecmd->use_adaptive_rx_coalesce &&
+ test_bit(JME_FLAG_POLL, &jme->flags)) {
clear_bit(JME_FLAG_POLL, &jme->flags);
jme->jme_rx = netif_rx;
jme->jme_vlan_rx = vlan_hwaccel_rx;
@@ -2209,8 +2209,8 @@ jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd)
dpi->cnt = 0;
jme_set_rx_pcc(jme, PCC_P1);
jme_interrupt_mode(jme);
- } else if (!(ecmd->use_adaptive_rx_coalesce)
- && !(test_bit(JME_FLAG_POLL, &jme->flags))) {
+ } else if (!(ecmd->use_adaptive_rx_coalesce) &&
+ !(test_bit(JME_FLAG_POLL, &jme->flags))) {
set_bit(JME_FLAG_POLL, &jme->flags);
jme->jme_rx = netif_receive_skb;
jme->jme_vlan_rx = vlan_hwaccel_receive_skb;
@@ -2764,19 +2764,19 @@ jme_init_one(struct pci_dev *pdev,
atomic_set(&jme->rx_empty, 1);
tasklet_init(&jme->pcc_task,
- &jme_pcc_tasklet,
+ jme_pcc_tasklet,
(unsigned long) jme);
tasklet_init(&jme->linkch_task,
- &jme_link_change_tasklet,
+ jme_link_change_tasklet,
(unsigned long) jme);
tasklet_init(&jme->txclean_task,
- &jme_tx_clean_tasklet,
+ jme_tx_clean_tasklet,
(unsigned long) jme);
tasklet_init(&jme->rxclean_task,
- &jme_rx_clean_tasklet,
+ jme_rx_clean_tasklet,
(unsigned long) jme);
tasklet_init(&jme->rxempty_task,
- &jme_rx_empty_tasklet,
+ jme_rx_empty_tasklet,
(unsigned long) jme);
tasklet_disable_nosync(&jme->linkch_task);
tasklet_disable_nosync(&jme->txclean_task);
diff --git a/drivers/net/korina.c b/drivers/net/korina.c
index 03199fa1000..25e2af6997e 100644
--- a/drivers/net/korina.c
+++ b/drivers/net/korina.c
@@ -400,7 +400,7 @@ static int korina_rx(struct net_device *dev, int limit)
dma_cache_inv((unsigned long)pkt_buf, pkt_len - 4);
/* Malloc up new buffer. */
- skb_new = netdev_alloc_skb(dev, KORINA_RBSIZE + 2);
+ skb_new = netdev_alloc_skb_ip_align(dev, KORINA_RBSIZE);
if (!skb_new)
break;
@@ -417,9 +417,6 @@ static int korina_rx(struct net_device *dev, int limit)
if (devcs & ETH_RX_MP)
dev->stats.multicast++;
- /* 16 bit align */
- skb_reserve(skb_new, 2);
-
lp->rx_skb[lp->rx_next_done] = skb_new;
}
@@ -1017,14 +1014,14 @@ static int korina_open(struct net_device *dev)
/* Install the interrupt handler
* that handles the Done Finished
* Ovr and Und Events */
- ret = request_irq(lp->rx_irq, &korina_rx_dma_interrupt,
+ ret = request_irq(lp->rx_irq, korina_rx_dma_interrupt,
IRQF_DISABLED, "Korina ethernet Rx", dev);
if (ret < 0) {
printk(KERN_ERR "%s: unable to get Rx DMA IRQ %d\n",
dev->name, lp->rx_irq);
goto err_release;
}
- ret = request_irq(lp->tx_irq, &korina_tx_dma_interrupt,
+ ret = request_irq(lp->tx_irq, korina_tx_dma_interrupt,
IRQF_DISABLED, "Korina ethernet Tx", dev);
if (ret < 0) {
printk(KERN_ERR "%s: unable to get Tx DMA IRQ %d\n",
@@ -1033,7 +1030,7 @@ static int korina_open(struct net_device *dev)
}
/* Install handler for overrun error. */
- ret = request_irq(lp->ovr_irq, &korina_ovr_interrupt,
+ ret = request_irq(lp->ovr_irq, korina_ovr_interrupt,
IRQF_DISABLED, "Ethernet Overflow", dev);
if (ret < 0) {
printk(KERN_ERR "%s: unable to get OVR IRQ %d\n",
@@ -1042,7 +1039,7 @@ static int korina_open(struct net_device *dev)
}
/* Install handler for underflow error. */
- ret = request_irq(lp->und_irq, &korina_und_interrupt,
+ ret = request_irq(lp->und_irq, korina_und_interrupt,
IRQF_DISABLED, "Ethernet Underflow", dev);
if (ret < 0) {
printk(KERN_ERR "%s: unable to get UND IRQ %d\n",
diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c
index 99e954167fa..5c45cb58d02 100644
--- a/drivers/net/ks8842.c
+++ b/drivers/net/ks8842.c
@@ -357,7 +357,7 @@ static void ks8842_rx_frame(struct net_device *netdev,
/* check the status */
if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) {
- struct sk_buff *skb = netdev_alloc_skb(netdev, len + 2);
+ struct sk_buff *skb = netdev_alloc_skb_ip_align(netdev, len);
dev_dbg(&adapter->pdev->dev, "%s, got package, len: %d\n",
__func__, len);
@@ -369,9 +369,6 @@ static void ks8842_rx_frame(struct net_device *netdev,
if (status & RXSR_MULTICAST)
netdev->stats.multicast++;
- /* Align socket buffer in 4-byte boundary for
- better performance. */
- skb_reserve(skb, 2);
data = (u32 *)skb_put(skb, len);
ks8842_select_bank(adapter, 17);
diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c
index a23f739d222..6d3ac65bc35 100644
--- a/drivers/net/ks8851.c
+++ b/drivers/net/ks8851.c
@@ -1,4 +1,4 @@
-/* drivers/net/ks8651.c
+/* drivers/net/ks8851.c
*
* Copyright 2009 Simtec Electronics
* http://www.simtec.co.uk/
@@ -714,7 +714,7 @@ static void ks8851_tx_work(struct work_struct *work)
{
struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work);
struct sk_buff *txb;
- bool last = false;
+ bool last = skb_queue_empty(&ks->txq);
mutex_lock(&ks->lock);
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index dcda30338b6..8d7d3d4625f 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -493,14 +493,14 @@ static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int
static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360};
int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99;
/* We can have boards other than the built-in! Verify this is on-board. */
- if ((inb(hp_port) & 0xc0) == 0x80
- && ioaddr_table[inb(hp_port) & 3] == ioaddr)
+ if ((inb(hp_port) & 0xc0) == 0x80 &&
+ ioaddr_table[inb(hp_port) & 3] == ioaddr)
hp_builtin = hp_port;
}
iounmap(bios);
/* We also recognize the HP Vectra on-board here, but check below. */
- hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00
- && inb(ioaddr+2) == 0x09);
+ hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 &&
+ inb(ioaddr+2) == 0x09);
/* Reset the LANCE. */
reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
@@ -755,7 +755,7 @@ lance_open(struct net_device *dev)
int i;
if (dev->irq == 0 ||
- request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) {
+ request_irq(dev->irq, lance_interrupt, 0, lp->name, dev)) {
return -EAGAIN;
}
@@ -1035,8 +1035,8 @@ static irqreturn_t lance_interrupt(int irq, void *dev_id)
spin_lock (&lp->devlock);
outw(0x00, dev->base_addr + LANCE_ADDR);
- while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600
- && --boguscnt >= 0) {
+ while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 &&
+ --boguscnt >= 0) {
/* Acknowledge all of the current interrupt sources ASAP. */
outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c
index a0c578585a5..b77238dbafb 100644
--- a/drivers/net/lasi_82596.c
+++ b/drivers/net/lasi_82596.c
@@ -47,7 +47,7 @@
TBD:
* look at deferring rx frames rather than discarding (as per tulip)
* handle tx ring full as per tulip
- * performace test to tune rx_copybreak
+ * performance test to tune rx_copybreak
Most of my modifications relate to the braindead big-endian
implementation by Intel. When the i596 is operating in
diff --git a/drivers/net/lib82596.c b/drivers/net/lib82596.c
index 51e11c3e53e..b117f7f8b19 100644
--- a/drivers/net/lib82596.c
+++ b/drivers/net/lib82596.c
@@ -47,7 +47,7 @@
TBD:
* look at deferring rx frames rather than discarding (as per tulip)
* handle tx ring full as per tulip
- * performace test to tune rx_copybreak
+ * performance test to tune rx_copybreak
Most of my modifications relate to the braindead big-endian
implementation by Intel. When the i596 is operating in
@@ -470,11 +470,11 @@ static inline int init_rx_bufs(struct net_device *dev)
for (i = 0, rbd = dma->rbds; i < rx_ring_size; i++, rbd++) {
dma_addr_t dma_addr;
- struct sk_buff *skb = netdev_alloc_skb(dev, PKT_BUF_SZ + 4);
+ struct sk_buff *skb;
+ skb = netdev_alloc_skb_ip_align(dev, PKT_BUF_SZ);
if (skb == NULL)
return -1;
- skb_reserve(skb, 2);
dma_addr = dma_map_single(dev->dev.parent, skb->data,
PKT_BUF_SZ, DMA_FROM_DEVICE);
rbd->v_next = rbd+1;
@@ -588,7 +588,7 @@ static int init_i596_mem(struct net_device *dev)
"%s: i82596 initialization successful\n",
dev->name));
- if (request_irq(dev->irq, &i596_interrupt, 0, "i82596", dev)) {
+ if (request_irq(dev->irq, i596_interrupt, 0, "i82596", dev)) {
printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq);
goto failed;
}
@@ -697,12 +697,12 @@ static inline int i596_rx(struct net_device *dev)
(dma_addr_t)SWAP32(rbd->b_data),
PKT_BUF_SZ, DMA_FROM_DEVICE);
/* Get fresh skbuff to replace filled one. */
- newskb = netdev_alloc_skb(dev, PKT_BUF_SZ + 4);
+ newskb = netdev_alloc_skb_ip_align(dev,
+ PKT_BUF_SZ);
if (newskb == NULL) {
skb = NULL; /* drop pkt */
goto memory_squeeze;
}
- skb_reserve(newskb, 2);
/* Pass up the skb already on the Rx ring. */
skb_put(skb, pkt_len);
@@ -716,7 +716,7 @@ static inline int i596_rx(struct net_device *dev)
rbd->b_data = SWAP32(dma_addr);
DMA_WBACK_INV(dev, rbd, sizeof(struct i596_rbd));
} else
- skb = netdev_alloc_skb(dev, pkt_len + 2);
+ skb = netdev_alloc_skb_ip_align(dev, pkt_len);
memory_squeeze:
if (skb == NULL) {
/* XXX tulip.c can defer packets here!! */
@@ -730,7 +730,6 @@ memory_squeeze:
dma_sync_single_for_cpu(dev->dev.parent,
(dma_addr_t)SWAP32(rbd->b_data),
PKT_BUF_SZ, DMA_FROM_DEVICE);
- skb_reserve(skb, 2);
memcpy(skb_put(skb, pkt_len), rbd->v_data, pkt_len);
dma_sync_single_for_device(dev->dev.parent,
(dma_addr_t)SWAP32(rbd->b_data),
diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c
index 256119882b1..57f25848fe8 100644
--- a/drivers/net/lib8390.c
+++ b/drivers/net/lib8390.c
@@ -464,8 +464,8 @@ static irqreturn_t __ei_interrupt(int irq, void *dev_id)
ei_inb_p(e8390_base + EN0_ISR));
/* !!Assumption!! -- we stay in page 0. Don't break this. */
- while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0
- && ++nr_serviced < MAX_SERVICE)
+ while ((interrupts = ei_inb_p(e8390_base + EN0_ISR)) != 0 &&
+ ++nr_serviced < MAX_SERVICE)
{
if (!netif_running(dev)) {
printk(KERN_WARNING "%s: interrupt from stopped card\n", dev->name);
@@ -721,10 +721,10 @@ static void ei_receive(struct net_device *dev)
/* Check for bogosity warned by 3c503 book: the status byte is never
written. This happened a lot during testing! This code should be
cleaned up someday. */
- if (rx_frame.next != next_frame
- && rx_frame.next != next_frame + 1
- && rx_frame.next != next_frame - num_rx_pages
- && rx_frame.next != next_frame + 1 - num_rx_pages) {
+ if (rx_frame.next != next_frame &&
+ rx_frame.next != next_frame + 1 &&
+ rx_frame.next != next_frame - num_rx_pages &&
+ rx_frame.next != next_frame + 1 - num_rx_pages) {
ei_local->current_page = rxing_page;
ei_outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
dev->stats.rx_errors++;
diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c
index f2a197fd47a..336e7c7a927 100644
--- a/drivers/net/ll_temac_main.c
+++ b/drivers/net/ll_temac_main.c
@@ -231,8 +231,8 @@ static void temac_set_multicast_list(struct net_device *ndev)
int i;
mutex_lock(&lp->indirect_mutex);
- if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC)
- || ndev->mc_count > MULTICAST_CAM_TABLE_NUM) {
+ if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) ||
+ ndev->mc_count > MULTICAST_CAM_TABLE_NUM) {
/*
* We must make the kernel realise we had to move
* into promisc mode or we start all out war on
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index 1bc654a73c4..eae4ad749e9 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -207,20 +207,12 @@ static __net_init int loopback_net_init(struct net *net)
out_free_netdev:
free_netdev(dev);
out:
- if (net == &init_net)
+ if (net_eq(net, &init_net))
panic("loopback: Failed to register netdevice: %d\n", err);
return err;
}
-static __net_exit void loopback_net_exit(struct net *net)
-{
- struct net_device *dev = net->loopback_dev;
-
- unregister_netdev(dev);
-}
-
/* Registered in net/core/dev.c */
struct pernet_operations __net_initdata loopback_net_ops = {
.init = loopback_net_init,
- .exit = loopback_net_exit,
};
diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c
index cc3ed9cf28b..e20fefc73c8 100644
--- a/drivers/net/lp486e.c
+++ b/drivers/net/lp486e.c
@@ -845,7 +845,7 @@ static int i596_open(struct net_device *dev)
{
int i;
- i = request_irq(dev->irq, &i596_interrupt, IRQF_SHARED, dev->name, dev);
+ i = request_irq(dev->irq, i596_interrupt, IRQF_SHARED, dev->name, dev);
if (i) {
printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq);
return i;
diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c
index 149e0ed4a05..23b633e2ac4 100644
--- a/drivers/net/mac89x0.c
+++ b/drivers/net/mac89x0.c
@@ -222,8 +222,8 @@ struct net_device * __init mac89x0_probe(int unit)
int card_present;
local_irq_save(flags);
- card_present = hwreg_present((void*) ioaddr+4)
- && hwreg_present((void*) ioaddr + DATA_PORT);
+ card_present = (hwreg_present((void*) ioaddr+4) &&
+ hwreg_present((void*) ioaddr + DATA_PORT));
local_irq_restore(flags);
if (!card_present)
@@ -337,7 +337,7 @@ net_open(struct net_device *dev)
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
/* Grab the interrupt */
- if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev))
+ if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev))
return -EAGAIN;
/* Set up the IRQ - Apparently magic */
diff --git a/drivers/net/mace.c b/drivers/net/mace.c
index 7d7577b598e..d9fbad38638 100644
--- a/drivers/net/mace.c
+++ b/drivers/net/mace.c
@@ -897,8 +897,8 @@ static irqreturn_t mace_rxdma_intr(int irq, void *dev_id)
if (next >= N_RX_RING)
next = 0;
np = mp->rx_cmds + next;
- if (next != mp->rx_fill
- && (ld_le16(&np->xfer_status) & ACTIVE) != 0) {
+ if (next != mp->rx_fill &&
+ (ld_le16(&np->xfer_status) & ACTIVE) != 0) {
printk(KERN_DEBUG "mace: lost a status word\n");
++mace_lost_status;
} else
diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c
index b3d7d8d77f4..875d361fb79 100644
--- a/drivers/net/macsonic.c
+++ b/drivers/net/macsonic.c
@@ -140,7 +140,7 @@ static irqreturn_t macsonic_interrupt(int irq, void *dev_id)
static int macsonic_open(struct net_device* dev)
{
- if (request_irq(dev->irq, &sonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) {
+ if (request_irq(dev->irq, sonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) {
printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq);
return -EAGAIN;
}
@@ -149,7 +149,7 @@ static int macsonic_open(struct net_device* dev)
* rupt as well, which must prevent re-entrance of the sonic handler.
*/
if (dev->irq == IRQ_AUTO_3)
- if (request_irq(IRQ_NUBUS_9, &macsonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) {
+ if (request_irq(IRQ_NUBUS_9, macsonic_interrupt, IRQ_FLG_FAST, "sonic", dev)) {
printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, IRQ_NUBUS_9);
free_irq(dev->irq, dev);
return -EAGAIN;
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 2490aa39804..21a9c9ab4b3 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -29,6 +29,7 @@
#include <linux/if_link.h>
#include <linux/if_macvlan.h>
#include <net/rtnetlink.h>
+#include <net/xfrm.h>
#define MACVLAN_HASH_SIZE (1 << BITS_PER_BYTE)
@@ -38,12 +39,28 @@ struct macvlan_port {
struct list_head vlans;
};
+/**
+ * struct macvlan_rx_stats - MACVLAN percpu rx stats
+ * @rx_packets: number of received packets
+ * @rx_bytes: number of received bytes
+ * @multicast: number of received multicast packets
+ * @rx_errors: number of errors
+ */
+struct macvlan_rx_stats {
+ unsigned long rx_packets;
+ unsigned long rx_bytes;
+ unsigned long multicast;
+ unsigned long rx_errors;
+};
+
struct macvlan_dev {
struct net_device *dev;
struct list_head list;
struct hlist_node hlist;
struct macvlan_port *port;
struct net_device *lowerdev;
+ struct macvlan_rx_stats *rx_stats;
+ enum macvlan_mode mode;
};
@@ -101,41 +118,67 @@ static int macvlan_addr_busy(const struct macvlan_port *port,
return 0;
}
+static inline void macvlan_count_rx(const struct macvlan_dev *vlan,
+ unsigned int len, bool success,
+ bool multicast)
+{
+ struct macvlan_rx_stats *rx_stats;
+
+ rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id());
+ if (likely(success)) {
+ rx_stats->rx_packets++;;
+ rx_stats->rx_bytes += len;
+ if (multicast)
+ rx_stats->multicast++;
+ } else {
+ rx_stats->rx_errors++;
+ }
+}
+
+static int macvlan_broadcast_one(struct sk_buff *skb, struct net_device *dev,
+ const struct ethhdr *eth, bool local)
+{
+ if (!skb)
+ return NET_RX_DROP;
+
+ if (local)
+ return dev_forward_skb(dev, skb);
+
+ skb->dev = dev;
+ if (!compare_ether_addr_64bits(eth->h_dest,
+ dev->broadcast))
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_MULTICAST;
+
+ return netif_rx(skb);
+}
+
static void macvlan_broadcast(struct sk_buff *skb,
- const struct macvlan_port *port)
+ const struct macvlan_port *port,
+ struct net_device *src,
+ enum macvlan_mode mode)
{
const struct ethhdr *eth = eth_hdr(skb);
const struct macvlan_dev *vlan;
struct hlist_node *n;
- struct net_device *dev;
struct sk_buff *nskb;
unsigned int i;
+ int err;
if (skb->protocol == htons(ETH_P_PAUSE))
return;
for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) {
- dev = vlan->dev;
-
- nskb = skb_clone(skb, GFP_ATOMIC);
- if (nskb == NULL) {
- dev->stats.rx_errors++;
- dev->stats.rx_dropped++;
+ if (vlan->dev == src || !(vlan->mode & mode))
continue;
- }
-
- dev->stats.rx_bytes += skb->len + ETH_HLEN;
- dev->stats.rx_packets++;
- dev->stats.multicast++;
-
- nskb->dev = dev;
- if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast))
- nskb->pkt_type = PACKET_BROADCAST;
- else
- nskb->pkt_type = PACKET_MULTICAST;
- netif_rx(nskb);
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ err = macvlan_broadcast_one(nskb, vlan->dev, eth,
+ mode == MACVLAN_MODE_BRIDGE);
+ macvlan_count_rx(vlan, skb->len + ETH_HLEN,
+ err == NET_RX_SUCCESS, 1);
}
}
}
@@ -146,14 +189,34 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
const struct ethhdr *eth = eth_hdr(skb);
const struct macvlan_port *port;
const struct macvlan_dev *vlan;
+ const struct macvlan_dev *src;
struct net_device *dev;
+ unsigned int len;
port = rcu_dereference(skb->dev->macvlan_port);
if (port == NULL)
return skb;
if (is_multicast_ether_addr(eth->h_dest)) {
- macvlan_broadcast(skb, port);
+ src = macvlan_hash_lookup(port, eth->h_source);
+ if (!src)
+ /* frame comes from an external address */
+ macvlan_broadcast(skb, port, NULL,
+ MACVLAN_MODE_PRIVATE |
+ MACVLAN_MODE_VEPA |
+ MACVLAN_MODE_BRIDGE);
+ else if (src->mode == MACVLAN_MODE_VEPA)
+ /* flood to everyone except source */
+ macvlan_broadcast(skb, port, src->dev,
+ MACVLAN_MODE_VEPA |
+ MACVLAN_MODE_BRIDGE);
+ else if (src->mode == MACVLAN_MODE_BRIDGE)
+ /*
+ * flood only to VEPA ports, bridge ports
+ * already saw the frame on the way out.
+ */
+ macvlan_broadcast(skb, port, src->dev,
+ MACVLAN_MODE_VEPA);
return skb;
}
@@ -166,16 +229,11 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
kfree_skb(skb);
return NULL;
}
-
+ len = skb->len + ETH_HLEN;
skb = skb_share_check(skb, GFP_ATOMIC);
- if (skb == NULL) {
- dev->stats.rx_errors++;
- dev->stats.rx_dropped++;
+ macvlan_count_rx(vlan, len, skb != NULL, 0);
+ if (!skb)
return NULL;
- }
-
- dev->stats.rx_bytes += skb->len + ETH_HLEN;
- dev->stats.rx_packets++;
skb->dev = dev;
skb->pkt_type = PACKET_HOST;
@@ -184,25 +242,53 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
return NULL;
}
+static int macvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ const struct macvlan_dev *vlan = netdev_priv(dev);
+ const struct macvlan_port *port = vlan->port;
+ const struct macvlan_dev *dest;
+
+ if (vlan->mode == MACVLAN_MODE_BRIDGE) {
+ const struct ethhdr *eth = (void *)skb->data;
+
+ /* send to other bridge ports directly */
+ if (is_multicast_ether_addr(eth->h_dest)) {
+ macvlan_broadcast(skb, port, dev, MACVLAN_MODE_BRIDGE);
+ goto xmit_world;
+ }
+
+ dest = macvlan_hash_lookup(port, eth->h_dest);
+ if (dest && dest->mode == MACVLAN_MODE_BRIDGE) {
+ unsigned int length = skb->len + ETH_HLEN;
+ int ret = dev_forward_skb(dest->dev, skb);
+ macvlan_count_rx(dest, length,
+ ret == NET_RX_SUCCESS, 0);
+
+ return NET_XMIT_SUCCESS;
+ }
+ }
+
+xmit_world:
+ skb->dev = vlan->lowerdev;
+ return dev_queue_xmit(skb);
+}
+
static netdev_tx_t macvlan_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
int i = skb_get_queue_mapping(skb);
struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
- const struct macvlan_dev *vlan = netdev_priv(dev);
unsigned int len = skb->len;
int ret;
- skb->dev = vlan->lowerdev;
- ret = dev_queue_xmit(skb);
-
+ ret = macvlan_queue_xmit(skb, dev);
if (likely(ret == NET_XMIT_SUCCESS)) {
txq->tx_packets++;
txq->tx_bytes += len;
} else
txq->tx_dropped++;
- return NETDEV_TX_OK;
+ return ret;
}
static int macvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
@@ -366,9 +452,47 @@ static int macvlan_init(struct net_device *dev)
macvlan_set_lockdep_class(dev);
+ vlan->rx_stats = alloc_percpu(struct macvlan_rx_stats);
+ if (!vlan->rx_stats)
+ return -ENOMEM;
+
return 0;
}
+static void macvlan_uninit(struct net_device *dev)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ free_percpu(vlan->rx_stats);
+}
+
+static struct net_device_stats *macvlan_dev_get_stats(struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ dev_txq_stats_fold(dev, stats);
+
+ if (vlan->rx_stats) {
+ struct macvlan_rx_stats *p, rx = {0};
+ int i;
+
+ for_each_possible_cpu(i) {
+ p = per_cpu_ptr(vlan->rx_stats, i);
+ rx.rx_packets += p->rx_packets;
+ rx.rx_bytes += p->rx_bytes;
+ rx.rx_errors += p->rx_errors;
+ rx.multicast += p->multicast;
+ }
+ stats->rx_packets = rx.rx_packets;
+ stats->rx_bytes = rx.rx_bytes;
+ stats->rx_errors = rx.rx_errors;
+ stats->rx_dropped = rx.rx_errors;
+ stats->multicast = rx.multicast;
+ }
+ return stats;
+}
+
static void macvlan_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *drvinfo)
{
@@ -405,6 +529,7 @@ static const struct ethtool_ops macvlan_ethtool_ops = {
static const struct net_device_ops macvlan_netdev_ops = {
.ndo_init = macvlan_init,
+ .ndo_uninit = macvlan_uninit,
.ndo_open = macvlan_open,
.ndo_stop = macvlan_stop,
.ndo_start_xmit = macvlan_start_xmit,
@@ -412,6 +537,7 @@ static const struct net_device_ops macvlan_netdev_ops = {
.ndo_change_rx_flags = macvlan_change_rx_flags,
.ndo_set_mac_address = macvlan_set_mac_address,
.ndo_set_multicast_list = macvlan_set_multicast_list,
+ .ndo_get_stats = macvlan_dev_get_stats,
.ndo_validate_addr = eth_validate_addr,
};
@@ -456,25 +582,6 @@ static void macvlan_port_destroy(struct net_device *dev)
kfree(port);
}
-static void macvlan_transfer_operstate(struct net_device *dev)
-{
- struct macvlan_dev *vlan = netdev_priv(dev);
- const struct net_device *lowerdev = vlan->lowerdev;
-
- if (lowerdev->operstate == IF_OPER_DORMANT)
- netif_dormant_on(dev);
- else
- netif_dormant_off(dev);
-
- if (netif_carrier_ok(lowerdev)) {
- if (!netif_carrier_ok(dev))
- netif_carrier_on(dev);
- } else {
- if (netif_carrier_ok(dev))
- netif_carrier_off(dev);
- }
-}
-
static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
{
if (tb[IFLA_ADDRESS]) {
@@ -483,6 +590,17 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
return -EADDRNOTAVAIL;
}
+
+ if (data && data[IFLA_MACVLAN_MODE]) {
+ switch (nla_get_u32(data[IFLA_MACVLAN_MODE])) {
+ case MACVLAN_MODE_PRIVATE:
+ case MACVLAN_MODE_VEPA:
+ case MACVLAN_MODE_BRIDGE:
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
return 0;
}
@@ -505,7 +623,7 @@ static int macvlan_get_tx_queues(struct net *net,
return 0;
}
-static int macvlan_newlink(struct net_device *dev,
+static int macvlan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
struct macvlan_dev *vlan = netdev_priv(dev);
@@ -516,7 +634,7 @@ static int macvlan_newlink(struct net_device *dev,
if (!tb[IFLA_LINK])
return -EINVAL;
- lowerdev = __dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK]));
+ lowerdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
if (lowerdev == NULL)
return -ENODEV;
@@ -547,27 +665,61 @@ static int macvlan_newlink(struct net_device *dev,
vlan->dev = dev;
vlan->port = port;
+ vlan->mode = MACVLAN_MODE_VEPA;
+ if (data && data[IFLA_MACVLAN_MODE])
+ vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
+
err = register_netdevice(dev);
if (err < 0)
return err;
list_add_tail(&vlan->list, &port->vlans);
- macvlan_transfer_operstate(dev);
+ netif_stacked_transfer_operstate(lowerdev, dev);
return 0;
}
-static void macvlan_dellink(struct net_device *dev)
+static void macvlan_dellink(struct net_device *dev, struct list_head *head)
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvlan_port *port = vlan->port;
list_del(&vlan->list);
- unregister_netdevice(dev);
+ unregister_netdevice_queue(dev, head);
if (list_empty(&port->vlans))
macvlan_port_destroy(port->dev);
}
+static int macvlan_changelink(struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+ if (data && data[IFLA_MACVLAN_MODE])
+ vlan->mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
+ return 0;
+}
+
+static size_t macvlan_get_size(const struct net_device *dev)
+{
+ return nla_total_size(4);
+}
+
+static int macvlan_fill_info(struct sk_buff *skb,
+ const struct net_device *dev)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ NLA_PUT_U32(skb, IFLA_MACVLAN_MODE, vlan->mode);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static const struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX + 1] = {
+ [IFLA_MACVLAN_MODE] = { .type = NLA_U32 },
+};
+
static struct rtnl_link_ops macvlan_link_ops __read_mostly = {
.kind = "macvlan",
.priv_size = sizeof(struct macvlan_dev),
@@ -576,6 +728,11 @@ static struct rtnl_link_ops macvlan_link_ops __read_mostly = {
.validate = macvlan_validate,
.newlink = macvlan_newlink,
.dellink = macvlan_dellink,
+ .maxtype = IFLA_MACVLAN_MAX,
+ .policy = macvlan_policy,
+ .changelink = macvlan_changelink,
+ .get_size = macvlan_get_size,
+ .fill_info = macvlan_fill_info,
};
static int macvlan_device_event(struct notifier_block *unused,
@@ -592,7 +749,8 @@ static int macvlan_device_event(struct notifier_block *unused,
switch (event) {
case NETDEV_CHANGE:
list_for_each_entry(vlan, &port->vlans, list)
- macvlan_transfer_operstate(vlan->dev);
+ netif_stacked_transfer_operstate(vlan->lowerdev,
+ vlan->dev);
break;
case NETDEV_FEAT_CHANGE:
list_for_each_entry(vlan, &port->vlans, list) {
@@ -603,7 +761,7 @@ static int macvlan_device_event(struct notifier_block *unused,
break;
case NETDEV_UNREGISTER:
list_for_each_entry_safe(vlan, next, &port->vlans, list)
- macvlan_dellink(vlan->dev);
+ macvlan_dellink(vlan->dev, NULL);
break;
}
return NOTIFY_DONE;
diff --git a/drivers/net/mdio.c b/drivers/net/mdio.c
index 21f8754fcf4..e85bf04cf81 100644
--- a/drivers/net/mdio.c
+++ b/drivers/net/mdio.c
@@ -162,6 +162,10 @@ static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr)
result |= ADVERTISED_100baseT_Half;
if (reg & ADVERTISE_100FULL)
result |= ADVERTISED_100baseT_Full;
+ if (reg & ADVERTISE_PAUSE_CAP)
+ result |= ADVERTISED_Pause;
+ if (reg & ADVERTISE_PAUSE_ASYM)
+ result |= ADVERTISED_Asym_Pause;
return result;
}
@@ -344,11 +348,9 @@ void mdio45_ethtool_spauseparam_an(const struct mdio_if_info *mdio,
old_adv = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN,
MDIO_AN_ADVERTISE);
- adv = old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
- if (ecmd->autoneg)
- adv |= mii_advertise_flowctrl(
- (ecmd->rx_pause ? FLOW_CTRL_RX : 0) |
- (ecmd->tx_pause ? FLOW_CTRL_TX : 0));
+ adv = ((old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) |
+ mii_advertise_flowctrl((ecmd->rx_pause ? FLOW_CTRL_RX : 0) |
+ (ecmd->tx_pause ? FLOW_CTRL_TX : 0)));
if (adv != old_adv) {
mdio->mdio_write(mdio->dev, mdio->prtad, MDIO_MMD_AN,
MDIO_AN_ADVERTISE, adv);
diff --git a/drivers/net/mipsnet.c b/drivers/net/mipsnet.c
index 8ea98bd89ff..8e9704f5c12 100644
--- a/drivers/net/mipsnet.c
+++ b/drivers/net/mipsnet.c
@@ -211,7 +211,7 @@ static int mipsnet_open(struct net_device *dev)
{
int err;
- err = request_irq(dev->irq, &mipsnet_interrupt,
+ err = request_irq(dev->irq, mipsnet_interrupt,
IRQF_SHARED, dev->name, (void *) dev);
if (err) {
release_region(dev->base_addr, sizeof(struct mipsnet_regs));
diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c
index 03b781a7a18..829b9ec9ff6 100644
--- a/drivers/net/mlx4/en_rx.c
+++ b/drivers/net/mlx4/en_rx.c
@@ -204,7 +204,7 @@ static void mlx4_en_free_rx_desc(struct mlx4_en_priv *priv,
en_dbg(DRV, priv, "Freeing fragment:%d\n", nr);
dma = be64_to_cpu(rx_desc->data[nr].addr);
- en_dbg(DRV, priv, "Unmaping buffer at dma:0x%llx\n", (u64) dma);
+ en_dbg(DRV, priv, "Unmapping buffer at dma:0x%llx\n", (u64) dma);
pci_unmap_single(mdev->pdev, dma, skb_frags[nr].size,
PCI_DMA_FROMDEVICE);
put_page(skb_frags[nr].page);
diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c
index 8c7279965b4..3d1396af946 100644
--- a/drivers/net/mlx4/en_tx.c
+++ b/drivers/net/mlx4/en_tx.c
@@ -47,7 +47,7 @@ enum {
static int inline_thold __read_mostly = MAX_INLINE;
module_param_named(inline_thold, inline_thold, int, 0444);
-MODULE_PARM_DESC(inline_thold, "treshold for using inline data");
+MODULE_PARM_DESC(inline_thold, "threshold for using inline data");
int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
struct mlx4_en_tx_ring *ring, u32 size,
diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h
index 4376147b0ea..82c3ebc584e 100644
--- a/drivers/net/mlx4/mlx4_en.h
+++ b/drivers/net/mlx4/mlx4_en.h
@@ -162,7 +162,7 @@ enum {
#define MLX4_EN_DEF_RX_PAUSE 1
#define MLX4_EN_DEF_TX_PAUSE 1
-/* Interval between sucessive polls in the Tx routine when polling is used
+/* Interval between successive polls in the Tx routine when polling is used
instead of interrupts (in per-core Tx rings) - should be power of 2 */
#define MLX4_EN_TX_POLL_MODER 16
#define MLX4_EN_TX_POLL_TIMEOUT (HZ / 4)
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index b62e61d4ca3..796a493f95a 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -849,7 +849,7 @@ no_csum:
return 0;
}
-static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct mv643xx_eth_private *mp = netdev_priv(dev);
int queue;
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index f3624517cb0..d38921906bb 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -207,7 +207,6 @@ struct myri10ge_priv {
int big_bytes;
int max_intr_slots;
struct net_device *dev;
- struct net_device_stats stats;
spinlock_t stats_lock;
u8 __iomem *sram;
int sram_size;
@@ -264,6 +263,10 @@ static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat";
static char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat";
static char *myri10ge_fw_rss_unaligned = "myri10ge_rss_ethp_z8e.dat";
static char *myri10ge_fw_rss_aligned = "myri10ge_rss_eth_z8e.dat";
+MODULE_FIRMWARE("myri10ge_ethp_z8e.dat");
+MODULE_FIRMWARE("myri10ge_eth_z8e.dat");
+MODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat");
+MODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat");
static char *myri10ge_fw_name = NULL;
module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
@@ -407,8 +410,8 @@ myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd,
* and try to get the completion quickly
* (1ms will be enough for those commands) */
for (sleep_total = 0;
- sleep_total < 1000
- && response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
+ sleep_total < 1000 &&
+ response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
sleep_total += 10) {
udelay(10);
mb();
@@ -416,8 +419,8 @@ myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd,
} else {
/* use msleep for most command */
for (sleep_total = 0;
- sleep_total < 15
- && response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
+ sleep_total < 15 &&
+ response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
sleep_total++)
msleep(1);
}
@@ -554,8 +557,8 @@ myri10ge_validate_firmware(struct myri10ge_priv *mgp,
sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major,
&mgp->fw_ver_minor, &mgp->fw_ver_tiny);
- if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR
- && mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
+ if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR &&
+ mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
dev_err(dev, "Found firmware version %s\n", mgp->fw_version);
dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR,
MXGEFW_VERSION_MINOR);
@@ -1409,8 +1412,8 @@ myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index)
}
/* start the queue if we've stopped it */
- if (netif_tx_queue_stopped(dev_queue)
- && tx->req - tx->done < (tx->mask >> 1)) {
+ if (netif_tx_queue_stopped(dev_queue) &&
+ tx->req - tx->done < (tx->mask >> 1)) {
tx->wake_queue++;
netif_tx_wake_queue(dev_queue);
}
@@ -1832,7 +1835,7 @@ myri10ge_get_ethtool_stats(struct net_device *netdev,
/* force stats update */
(void)myri10ge_get_stats(netdev);
for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
- data[i] = ((unsigned long *)&mgp->stats)[i];
+ data[i] = ((unsigned long *)&netdev->stats)[i];
data[i++] = (unsigned int)mgp->tx_boundary;
data[i++] = (unsigned int)mgp->wc_enabled;
@@ -3002,7 +3005,7 @@ static struct net_device_stats *myri10ge_get_stats(struct net_device *dev)
{
struct myri10ge_priv *mgp = netdev_priv(dev);
struct myri10ge_slice_netstats *slice_stats;
- struct net_device_stats *stats = &mgp->stats;
+ struct net_device_stats *stats = &dev->stats;
int i;
spin_lock(&mgp->stats_lock);
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index 29ebebc6a95..b3513ad3b70 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -1084,7 +1084,7 @@ static int __devinit myri_sbus_probe(struct of_device *op, const struct of_devic
/* Register interrupt handler now. */
DET(("Requesting MYRIcom IRQ line.\n"));
- if (request_irq(dev->irq, &myri_interrupt,
+ if (request_irq(dev->irq, myri_interrupt,
IRQF_SHARED, "MyriCOM Ethernet", (void *) dev)) {
printk("MyriCOM: Cannot register interrupt handler.\n");
goto err;
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index b2722c44337..797fe164ce2 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -683,8 +683,8 @@ static ssize_t natsemi_set_dspcfg_workaround(struct device *dev,
/* Find out the new setting */
if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
new_setting = 1;
- else if (!strncmp("off", buf, count - 1)
- || !strncmp("0", buf, count - 1))
+ else if (!strncmp("off", buf, count - 1) ||
+ !strncmp("0", buf, count - 1))
new_setting = 0;
else
return count;
@@ -757,8 +757,8 @@ static void __devinit natsemi_init_media (struct net_device *dev)
np->autoneg = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;
np->advertising= mdio_read(dev, MII_ADVERTISE);
- if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL
- && netif_msg_probe(np)) {
+ if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL &&
+ netif_msg_probe(np)) {
printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "
"10%s %s duplex.\n",
pci_name(np->pci_dev),
@@ -1153,8 +1153,8 @@ static void init_phy_fixup(struct net_device *dev)
tmp = mdio_read(dev, MII_BMCR);
if (np->autoneg == AUTONEG_ENABLE) {
/* renegotiate if something changed */
- if ((tmp & BMCR_ANENABLE) == 0
- || np->advertising != mdio_read(dev, MII_ADVERTISE))
+ if ((tmp & BMCR_ANENABLE) == 0 ||
+ np->advertising != mdio_read(dev, MII_ADVERTISE))
{
/* turn on autonegotiation and force negotiation */
tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
@@ -1535,7 +1535,7 @@ static int netdev_open(struct net_device *dev)
/* Reset the chip, just in case. */
natsemi_reset(dev);
- i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
+ i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev);
if (i) return i;
if (netif_msg_ifup(np))
@@ -2164,8 +2164,8 @@ static void netdev_tx_done(struct net_device *dev)
dev_kfree_skb_irq(np->tx_skbuff[entry]);
np->tx_skbuff[entry] = NULL;
}
- if (netif_queue_stopped(dev)
- && np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+ if (netif_queue_stopped(dev) &&
+ np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
/* The ring is no longer full, wake queue. */
netif_wake_queue(dev);
}
@@ -2343,8 +2343,8 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
/* Omit CRC size. */
/* Check if the packet is long enough to accept
* without copying to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) {
/* 16 byte align the IP header */
skb_reserve(skb, RX_OFFSET);
pci_dma_sync_single_for_cpu(np->pci_dev,
@@ -2390,8 +2390,8 @@ static void netdev_error(struct net_device *dev, int intr_status)
spin_lock(&np->lock);
if (intr_status & LinkChange) {
u16 lpa = mdio_read(dev, MII_LPA);
- if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE
- && netif_msg_link(np)) {
+ if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE &&
+ netif_msg_link(np)) {
printk(KERN_INFO
"%s: Autonegotiation advertising"
" %#04x partner %#04x.\n", dev->name,
@@ -2488,8 +2488,8 @@ static void __set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
rx_mode = RxFilterEnable | AcceptBroadcast
| AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
rx_mode = RxFilterEnable | AcceptBroadcast
| AcceptAllMulticast | AcceptMyPhys;
} else {
diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c
index 9f4235466d5..64770298c4f 100644
--- a/drivers/net/netx-eth.c
+++ b/drivers/net/netx-eth.c
@@ -212,7 +212,7 @@ static int netx_eth_open(struct net_device *ndev)
struct netx_eth_priv *priv = netdev_priv(ndev);
if (request_irq
- (ndev->irq, &netx_eth_interrupt, IRQF_SHARED, ndev->name, ndev))
+ (ndev->irq, netx_eth_interrupt, IRQF_SHARED, ndev->name, ndev))
return -EAGAIN;
writel(ndev->dev_addr[0] |
@@ -510,3 +510,6 @@ module_exit(netx_eth_cleanup);
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" CARDNAME);
+MODULE_FIRMWARE("xc0.bin");
+MODULE_FIRMWARE("xc1.bin");
+MODULE_FIRMWARE("xc2.bin");
diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
index e1237b80287..76cd1f3e9fc 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/netxen/netxen_nic.h
@@ -53,8 +53,8 @@
#define _NETXEN_NIC_LINUX_MAJOR 4
#define _NETXEN_NIC_LINUX_MINOR 0
-#define _NETXEN_NIC_LINUX_SUBVERSION 50
-#define NETXEN_NIC_LINUX_VERSIONID "4.0.50"
+#define _NETXEN_NIC_LINUX_SUBVERSION 65
+#define NETXEN_NIC_LINUX_VERSIONID "4.0.65"
#define NETXEN_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c))
#define _major(v) (((v) >> 24) & 0xff)
@@ -74,8 +74,6 @@
#define NETXEN_FLASH_TOTAL_SIZE (NETXEN_NUM_FLASH_SECTORS \
* NETXEN_FLASH_SECTOR_SIZE)
-#define PHAN_VENDOR_ID 0x4040
-
#define RCV_DESC_RINGSIZE(rds_ring) \
(sizeof(struct rcv_desc) * (rds_ring)->num_desc)
#define RCV_BUFF_RINGSIZE(rds_ring) \
@@ -117,9 +115,11 @@
#define NX_P3_B0 0x40
#define NX_P3_B1 0x41
#define NX_P3_B2 0x42
+#define NX_P3P_A0 0x50
#define NX_IS_REVISION_P2(REVISION) (REVISION <= NX_P2_C1)
#define NX_IS_REVISION_P3(REVISION) (REVISION >= NX_P3_A0)
+#define NX_IS_REVISION_P3P(REVISION) (REVISION >= NX_P3P_A0)
#define FIRST_PAGE_GROUP_START 0
#define FIRST_PAGE_GROUP_END 0x100000
@@ -419,6 +419,34 @@ struct status_desc {
__le64 status_desc_data[2];
} __attribute__ ((aligned(16)));
+/* UNIFIED ROMIMAGE *************************/
+#define NX_UNI_FW_MIN_SIZE 0x3eb000
+#define NX_UNI_DIR_SECT_PRODUCT_TBL 0x0
+#define NX_UNI_DIR_SECT_BOOTLD 0x6
+#define NX_UNI_DIR_SECT_FW 0x7
+
+/*Offsets */
+#define NX_UNI_CHIP_REV_OFF 10
+#define NX_UNI_FLAGS_OFF 11
+#define NX_UNI_BIOS_VERSION_OFF 12
+#define NX_UNI_BOOTLD_IDX_OFF 27
+#define NX_UNI_FIRMWARE_IDX_OFF 29
+
+struct uni_table_desc{
+ uint32_t findex;
+ uint32_t num_entries;
+ uint32_t entry_size;
+ uint32_t reserved[5];
+};
+
+struct uni_data_desc{
+ uint32_t findex;
+ uint32_t size;
+ uint32_t reserved[5];
+};
+
+/* UNIFIED ROMIMAGE *************************/
+
/* The version of the main data structure */
#define NETXEN_BDINFO_VERSION 1
@@ -485,7 +513,15 @@ struct status_desc {
#define NX_P2_MN_ROMIMAGE 0
#define NX_P3_CT_ROMIMAGE 1
#define NX_P3_MN_ROMIMAGE 2
-#define NX_FLASH_ROMIMAGE 3
+#define NX_UNIFIED_ROMIMAGE 3
+#define NX_FLASH_ROMIMAGE 4
+#define NX_UNKNOWN_ROMIMAGE 0xff
+
+#define NX_P2_MN_ROMIMAGE_NAME "nxromimg.bin"
+#define NX_P3_CT_ROMIMAGE_NAME "nx3fwct.bin"
+#define NX_P3_MN_ROMIMAGE_NAME "nx3fwmn.bin"
+#define NX_UNIFIED_ROMIMAGE_NAME "phanfw.bin"
+#define NX_FLASH_ROMIMAGE_NAME "flash"
extern char netxen_nic_driver_name[];
@@ -543,13 +579,16 @@ struct netxen_hardware_context {
void __iomem *pci_base1;
void __iomem *pci_base2;
void __iomem *db_base;
+ void __iomem *ocm_win_crb;
+
unsigned long db_len;
unsigned long pci_len0;
- int qdr_sn_window;
- int ddr_mn_window;
- u32 mn_win_crb;
- u32 ms_win_crb;
+ u32 ocm_win;
+ u32 crb_win;
+
+ rwlock_t crb_lock;
+ spinlock_t mem_lock;
u8 cut_through;
u8 revision_id;
@@ -1039,6 +1078,9 @@ typedef struct {
#define LINKEVENT_LINKSPEED_MBPS 0
#define LINKEVENT_LINKSPEED_ENCODED 1
+#define AUTO_FW_RESET_ENABLED 0xEF10AF12
+#define AUTO_FW_RESET_DISABLED 0xDCBAAF12
+
/* firmware response header:
* 63:58 - message type
* 57:56 - owner
@@ -1086,6 +1128,7 @@ typedef struct {
#define NETXEN_NIC_MSIX_ENABLED 0x04
#define NETXEN_NIC_LRO_ENABLED 0x08
#define NETXEN_NIC_BRIDGE_ENABLED 0X10
+#define NETXEN_NIC_DIAG_ENABLED 0x20
#define NETXEN_IS_MSI_FAMILY(adapter) \
((adapter)->flags & (NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED))
@@ -1115,10 +1158,6 @@ struct netxen_adapter {
struct pci_dev *pdev;
struct list_head mac_list;
- u32 curr_window;
- u32 crb_win;
- rwlock_t adapter_lock;
-
spinlock_t tx_clean_lock;
u16 num_txd;
@@ -1182,11 +1221,10 @@ struct netxen_adapter {
u32 (*crb_read)(struct netxen_adapter *, ulong);
int (*crb_write)(struct netxen_adapter *, ulong, u32);
- int (*pci_mem_read)(struct netxen_adapter *, u64, void *, int);
- int (*pci_mem_write)(struct netxen_adapter *, u64, void *, int);
+ int (*pci_mem_read)(struct netxen_adapter *, u64, u64 *);
+ int (*pci_mem_write)(struct netxen_adapter *, u64, u64);
- unsigned long (*pci_set_window)(struct netxen_adapter *,
- unsigned long long);
+ int (*pci_set_window)(struct netxen_adapter *, u64, u32 *);
u32 (*io_read)(struct netxen_adapter *, void __iomem *);
void (*io_write)(struct netxen_adapter *, void __iomem *, u32);
@@ -1205,12 +1243,10 @@ struct netxen_adapter {
struct work_struct tx_timeout_task;
- struct net_device_stats net_stats;
-
nx_nic_intr_coalesce_t coal;
unsigned long state;
- u32 resv5;
+ __le32 file_prd_off; /*File fw product offset*/
u32 fw_version;
const struct firmware *fw;
};
@@ -1273,7 +1309,7 @@ int netxen_load_firmware(struct netxen_adapter *adapter);
int netxen_need_fw_reset(struct netxen_adapter *adapter);
void netxen_request_firmware(struct netxen_adapter *adapter);
void netxen_release_firmware(struct netxen_adapter *adapter);
-int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose);
+int netxen_pinit_from_rom(struct netxen_adapter *adapter);
int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp);
int netxen_rom_fast_read_words(struct netxen_adapter *adapter, int addr,
diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c
index 714f38791a9..ddd704ae018 100644
--- a/drivers/net/netxen/netxen_nic_ethtool.c
+++ b/drivers/net/netxen/netxen_nic_ethtool.c
@@ -85,11 +85,9 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
strncpy(drvinfo->driver, netxen_nic_driver_name, 32);
strncpy(drvinfo->version, NETXEN_NIC_LINUX_VERSIONID, 32);
- read_lock(&adapter->adapter_lock);
fw_major = NXRD32(adapter, NETXEN_FW_VERSION_MAJOR);
fw_minor = NXRD32(adapter, NETXEN_FW_VERSION_MINOR);
fw_build = NXRD32(adapter, NETXEN_FW_VERSION_SUB);
- read_unlock(&adapter->adapter_lock);
sprintf(drvinfo->fw_version, "%d.%d.%d", fw_major, fw_minor, fw_build);
strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
@@ -259,18 +257,18 @@ netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
/* read which mode */
if (adapter->ahw.port_type == NETXEN_NIC_GBE) {
/* autonegotiation */
- if (adapter->phy_write
- && adapter->phy_write(adapter,
- NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG,
- ecmd->autoneg) != 0)
+ if (adapter->phy_write &&
+ adapter->phy_write(adapter,
+ NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG,
+ ecmd->autoneg) != 0)
return -EIO;
else
adapter->link_autoneg = ecmd->autoneg;
- if (adapter->phy_read
- && adapter->phy_read(adapter,
- NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
- &status) != 0)
+ if (adapter->phy_read &&
+ adapter->phy_read(adapter,
+ NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
+ &status) != 0)
return -EIO;
/* speed */
@@ -290,10 +288,10 @@ netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
netxen_clear_phy_duplex(status);
if (ecmd->duplex == DUPLEX_FULL)
netxen_set_phy_duplex(status);
- if (adapter->phy_write
- && adapter->phy_write(adapter,
- NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
- *((int *)&status)) != 0)
+ if (adapter->phy_write &&
+ adapter->phy_write(adapter,
+ NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
+ *((int *)&status)) != 0)
return -EIO;
else {
adapter->link_speed = ecmd->speed;
@@ -444,10 +442,10 @@ static u32 netxen_nic_test_link(struct net_device *dev)
/* read which mode */
if (adapter->ahw.port_type == NETXEN_NIC_GBE) {
- if (adapter->phy_read
- && adapter->phy_read(adapter,
- NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
- &status) != 0)
+ if (adapter->phy_read &&
+ adapter->phy_read(adapter,
+ NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
+ &status) != 0)
return -EIO;
else {
val = netxen_get_phy_link(status);
@@ -690,8 +688,8 @@ static int netxen_nic_reg_test(struct net_device *dev)
u32 data_read, data_written;
data_read = NXRD32(adapter, NETXEN_PCIX_PH_REG(0));
- if ((data_read & 0xffff) != PHAN_VENDOR_ID)
- return 1;
+ if ((data_read & 0xffff) != adapter->pdev->vendor)
+ return 1;
data_written = (u32)0xa5a5a5a5;
diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h
index 17bb3818d84..d138fc22927 100644
--- a/drivers/net/netxen/netxen_nic_hdr.h
+++ b/drivers/net/netxen/netxen_nic_hdr.h
@@ -664,40 +664,51 @@ enum {
#define NETXEN_NIU_AP_STATION_ADDR_0(I) (NETXEN_CRB_NIU+0xa0040+(I)*0x10000)
#define NETXEN_NIU_AP_STATION_ADDR_1(I) (NETXEN_CRB_NIU+0xa0044+(I)*0x10000)
+
+#define TEST_AGT_CTRL (0x00)
+
+#define TA_CTL_START 1
+#define TA_CTL_ENABLE 2
+#define TA_CTL_WRITE 4
+#define TA_CTL_BUSY 8
+
/*
* Register offsets for MN
*/
-#define MIU_CONTROL (0x000)
-#define MIU_TEST_AGT_CTRL (0x090)
-#define MIU_TEST_AGT_ADDR_LO (0x094)
-#define MIU_TEST_AGT_ADDR_HI (0x098)
-#define MIU_TEST_AGT_WRDATA_LO (0x0a0)
-#define MIU_TEST_AGT_WRDATA_HI (0x0a4)
-#define MIU_TEST_AGT_WRDATA(i) (0x0a0+(4*(i)))
-#define MIU_TEST_AGT_RDDATA_LO (0x0a8)
-#define MIU_TEST_AGT_RDDATA_HI (0x0ac)
-#define MIU_TEST_AGT_RDDATA(i) (0x0a8+(4*(i)))
-#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8
-#define MIU_TEST_AGT_UPPER_ADDR(off) (0)
-
-/* MIU_TEST_AGT_CTRL flags. work for SIU as well */
-#define MIU_TA_CTL_START 1
-#define MIU_TA_CTL_ENABLE 2
-#define MIU_TA_CTL_WRITE 4
-#define MIU_TA_CTL_BUSY 8
-
-#define SIU_TEST_AGT_CTRL (0x060)
-#define SIU_TEST_AGT_ADDR_LO (0x064)
-#define SIU_TEST_AGT_ADDR_HI (0x078)
-#define SIU_TEST_AGT_WRDATA_LO (0x068)
-#define SIU_TEST_AGT_WRDATA_HI (0x06c)
-#define SIU_TEST_AGT_WRDATA(i) (0x068+(4*(i)))
-#define SIU_TEST_AGT_RDDATA_LO (0x070)
-#define SIU_TEST_AGT_RDDATA_HI (0x074)
-#define SIU_TEST_AGT_RDDATA(i) (0x070+(4*(i)))
-
-#define SIU_TEST_AGT_ADDR_MASK 0x3ffff8
-#define SIU_TEST_AGT_UPPER_ADDR(off) ((off)>>22)
+#define MIU_TEST_AGT_BASE (0x90)
+
+#define MIU_TEST_AGT_ADDR_LO (0x04)
+#define MIU_TEST_AGT_ADDR_HI (0x08)
+#define MIU_TEST_AGT_WRDATA_LO (0x10)
+#define MIU_TEST_AGT_WRDATA_HI (0x14)
+#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x20)
+#define MIU_TEST_AGT_WRDATA_UPPER_HI (0x24)
+#define MIU_TEST_AGT_WRDATA(i) (0x10+(0x10*((i)>>1))+(4*((i)&1)))
+#define MIU_TEST_AGT_RDDATA_LO (0x18)
+#define MIU_TEST_AGT_RDDATA_HI (0x1c)
+#define MIU_TEST_AGT_RDDATA_UPPER_LO (0x28)
+#define MIU_TEST_AGT_RDDATA_UPPER_HI (0x2c)
+#define MIU_TEST_AGT_RDDATA(i) (0x18+(0x10*((i)>>1))+(4*((i)&1)))
+
+#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8
+#define MIU_TEST_AGT_UPPER_ADDR(off) (0)
+
+/*
+ * Register offsets for MS
+ */
+#define SIU_TEST_AGT_BASE (0x60)
+
+#define SIU_TEST_AGT_ADDR_LO (0x04)
+#define SIU_TEST_AGT_ADDR_HI (0x18)
+#define SIU_TEST_AGT_WRDATA_LO (0x08)
+#define SIU_TEST_AGT_WRDATA_HI (0x0c)
+#define SIU_TEST_AGT_WRDATA(i) (0x08+(4*(i)))
+#define SIU_TEST_AGT_RDDATA_LO (0x10)
+#define SIU_TEST_AGT_RDDATA_HI (0x14)
+#define SIU_TEST_AGT_RDDATA(i) (0x10+(4*(i)))
+
+#define SIU_TEST_AGT_ADDR_MASK 0x3ffff8
+#define SIU_TEST_AGT_UPPER_ADDR(off) ((off)>>22)
/* XG Link status */
#define XG_LINK_UP 0x10
@@ -859,6 +870,9 @@ enum {
(PCIX_SN_WINDOW_F0 + (0x20 * (func))) :\
(PCIX_SN_WINDOW_F4 + (0x10 * ((func)-4))))
+#define PCIX_OCM_WINDOW (0x10800)
+#define PCIX_OCM_WINDOW_REG(func) (PCIX_OCM_WINDOW + 0x20 * (func))
+
#define PCIX_TARGET_STATUS (0x10118)
#define PCIX_TARGET_STATUS_F1 (0x10160)
#define PCIX_TARGET_STATUS_F2 (0x10164)
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
index 52a3798d8d9..2e364fee3cb 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/netxen/netxen_nic_hw.c
@@ -31,6 +31,7 @@
#define MASK(n) ((1ULL<<(n))-1)
#define MN_WIN(addr) (((addr & 0x1fc0000) >> 1) | ((addr >> 25) & 0x3ff))
#define OCM_WIN(addr) (((addr & 0x1ff0000) >> 1) | ((addr >> 25) & 0x3ff))
+#define OCM_WIN_P3P(addr) (addr & 0xffc0000)
#define MS_WIN(addr) (addr & 0x0ffc0000)
#define GET_MEM_OFFS_2M(addr) (addr & MASK(18))
@@ -41,6 +42,11 @@
#define CRB_HI(off) ((crb_hub_agt[CRB_BLK(off)] << 20) | ((off) & 0xf0000))
#define CRB_INDIRECT_2M (0x1e0000UL)
+static void netxen_nic_io_write_128M(struct netxen_adapter *adapter,
+ void __iomem *addr, u32 data);
+static u32 netxen_nic_io_read_128M(struct netxen_adapter *adapter,
+ void __iomem *addr);
+
#ifndef readq
static inline u64 readq(void __iomem *addr)
{
@@ -326,7 +332,7 @@ netxen_pcie_sem_lock(struct netxen_adapter *adapter, int sem, u32 id_reg)
if (done == 1)
break;
if (++timeout >= NETXEN_PCIE_SEM_TIMEOUT)
- return -1;
+ return -EIO;
msleep(1);
}
@@ -1073,89 +1079,71 @@ int netxen_p3_get_mac_addr(struct netxen_adapter *adapter, __le64 *mac)
* Changes the CRB window to the specified window.
*/
static void
-netxen_nic_pci_change_crbwindow_128M(struct netxen_adapter *adapter, u32 wndw)
+netxen_nic_pci_set_crbwindow_128M(struct netxen_adapter *adapter,
+ u32 window)
{
void __iomem *offset;
- u32 tmp;
- int count = 0;
- uint8_t func = adapter->ahw.pci_func;
+ int count = 10;
+ u8 func = adapter->ahw.pci_func;
- if (adapter->curr_window == wndw)
+ if (adapter->ahw.crb_win == window)
return;
- /*
- * Move the CRB window.
- * We need to write to the "direct access" region of PCI
- * to avoid a race condition where the window register has
- * not been successfully written across CRB before the target
- * register address is received by PCI. The direct region bypasses
- * the CRB bus.
- */
+
offset = PCI_OFFSET_SECOND_RANGE(adapter,
NETXEN_PCIX_PH_REG(PCIE_CRB_WINDOW_REG(func)));
- if (wndw & 0x1)
- wndw = NETXEN_WINDOW_ONE;
+ writel(window, offset);
+ do {
+ if (window == readl(offset))
+ break;
- writel(wndw, offset);
+ if (printk_ratelimit())
+ dev_warn(&adapter->pdev->dev,
+ "failed to set CRB window to %d\n",
+ (window == NETXEN_WINDOW_ONE));
+ udelay(1);
- /* MUST make sure window is set before we forge on... */
- while ((tmp = readl(offset)) != wndw) {
- printk(KERN_WARNING "%s: %s WARNING: CRB window value not "
- "registered properly: 0x%08x.\n",
- netxen_nic_driver_name, __func__, tmp);
- mdelay(1);
- if (count >= 10)
- break;
- count++;
- }
+ } while (--count > 0);
- if (wndw == NETXEN_WINDOW_ONE)
- adapter->curr_window = 1;
- else
- adapter->curr_window = 0;
+ if (count > 0)
+ adapter->ahw.crb_win = window;
}
/*
- * Return -1 if off is not valid,
+ * Returns < 0 if off is not valid,
* 1 if window access is needed. 'off' is set to offset from
* CRB space in 128M pci map
* 0 if no window access is needed. 'off' is set to 2M addr
* In: 'off' is offset from base in 128M pci map
*/
static int
-netxen_nic_pci_get_crb_addr_2M(struct netxen_adapter *adapter, ulong *off)
+netxen_nic_pci_get_crb_addr_2M(struct netxen_adapter *adapter,
+ ulong off, void __iomem **addr)
{
crb_128M_2M_sub_block_map_t *m;
- if (*off >= NETXEN_CRB_MAX)
- return -1;
-
- if (*off >= NETXEN_PCI_CAMQM && (*off < NETXEN_PCI_CAMQM_2M_END)) {
- *off = (*off - NETXEN_PCI_CAMQM) + NETXEN_PCI_CAMQM_2M_BASE +
- (ulong)adapter->ahw.pci_base0;
- return 0;
- }
-
- if (*off < NETXEN_PCI_CRBSPACE)
- return -1;
+ if ((off >= NETXEN_CRB_MAX) || (off < NETXEN_PCI_CRBSPACE))
+ return -EINVAL;
- *off -= NETXEN_PCI_CRBSPACE;
+ off -= NETXEN_PCI_CRBSPACE;
/*
* Try direct map
*/
- m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)];
+ m = &crb_128M_2M_map[CRB_BLK(off)].sub_block[CRB_SUBBLK(off)];
- if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) {
- *off = *off + m->start_2M - m->start_128M +
- (ulong)adapter->ahw.pci_base0;
+ if (m->valid && (m->start_128M <= off) && (m->end_128M > off)) {
+ *addr = adapter->ahw.pci_base0 + m->start_2M +
+ (off - m->start_128M);
return 0;
}
/*
* Not in direct map, use crb window
*/
+ *addr = adapter->ahw.pci_base0 + CRB_INDIRECT_2M +
+ (off & MASK(16));
return 1;
}
@@ -1165,52 +1153,78 @@ netxen_nic_pci_get_crb_addr_2M(struct netxen_adapter *adapter, ulong *off)
* side effect: lock crb window
*/
static void
-netxen_nic_pci_set_crbwindow_2M(struct netxen_adapter *adapter, ulong *off)
+netxen_nic_pci_set_crbwindow_2M(struct netxen_adapter *adapter, ulong off)
{
- u32 win_read;
+ u32 window;
+ void __iomem *addr = adapter->ahw.pci_base0 + CRB_WINDOW_2M;
- adapter->crb_win = CRB_HI(*off);
- writel(adapter->crb_win, (adapter->ahw.pci_base0 + CRB_WINDOW_2M));
- /*
- * Read back value to make sure write has gone through before trying
- * to use it.
- */
- win_read = readl(adapter->ahw.pci_base0 + CRB_WINDOW_2M);
- if (win_read != adapter->crb_win) {
- printk(KERN_ERR "%s: Written crbwin (0x%x) != "
- "Read crbwin (0x%x), off=0x%lx\n",
- __func__, adapter->crb_win, win_read, *off);
+ off -= NETXEN_PCI_CRBSPACE;
+
+ window = CRB_HI(off);
+
+ if (adapter->ahw.crb_win == window)
+ return;
+
+ writel(window, addr);
+ if (readl(addr) != window) {
+ if (printk_ratelimit())
+ dev_warn(&adapter->pdev->dev,
+ "failed to set CRB window to %d off 0x%lx\n",
+ window, off);
}
- *off = (*off & MASK(16)) + CRB_INDIRECT_2M +
- (ulong)adapter->ahw.pci_base0;
+ adapter->ahw.crb_win = window;
+}
+
+static void __iomem *
+netxen_nic_map_indirect_address_128M(struct netxen_adapter *adapter,
+ ulong win_off, void __iomem **mem_ptr)
+{
+ ulong off = win_off;
+ void __iomem *addr;
+ resource_size_t mem_base;
+
+ if (ADDR_IN_WINDOW1(win_off))
+ off = NETXEN_CRB_NORMAL(win_off);
+
+ addr = pci_base_offset(adapter, off);
+ if (addr)
+ return addr;
+
+ if (adapter->ahw.pci_len0 == 0)
+ off -= NETXEN_PCI_CRBSPACE;
+
+ mem_base = pci_resource_start(adapter->pdev, 0);
+ *mem_ptr = ioremap(mem_base + (off & PAGE_MASK), PAGE_SIZE);
+ if (*mem_ptr)
+ addr = *mem_ptr + (off & (PAGE_SIZE - 1));
+
+ return addr;
}
static int
netxen_nic_hw_write_wx_128M(struct netxen_adapter *adapter, ulong off, u32 data)
{
unsigned long flags;
- void __iomem *addr;
-
- if (ADDR_IN_WINDOW1(off))
- addr = NETXEN_CRB_NORMALIZE(adapter, off);
- else
- addr = pci_base_offset(adapter, off);
+ void __iomem *addr, *mem_ptr = NULL;
- BUG_ON(!addr);
+ addr = netxen_nic_map_indirect_address_128M(adapter, off, &mem_ptr);
+ if (!addr)
+ return -EIO;
- if (ADDR_IN_WINDOW1(off)) { /* Window 1 */
- read_lock(&adapter->adapter_lock);
+ if (ADDR_IN_WINDOW1(off)) { /* Window 1 */
+ netxen_nic_io_write_128M(adapter, addr, data);
+ } else { /* Window 0 */
+ write_lock_irqsave(&adapter->ahw.crb_lock, flags);
+ netxen_nic_pci_set_crbwindow_128M(adapter, 0);
writel(data, addr);
- read_unlock(&adapter->adapter_lock);
- } else { /* Window 0 */
- write_lock_irqsave(&adapter->adapter_lock, flags);
- addr = pci_base_offset(adapter, off);
- netxen_nic_pci_change_crbwindow_128M(adapter, 0);
- writel(data, addr);
- netxen_nic_pci_change_crbwindow_128M(adapter, 1);
- write_unlock_irqrestore(&adapter->adapter_lock, flags);
+ netxen_nic_pci_set_crbwindow_128M(adapter,
+ NETXEN_WINDOW_ONE);
+ write_unlock_irqrestore(&adapter->ahw.crb_lock, flags);
}
+ if (mem_ptr)
+ iounmap(mem_ptr);
+
return 0;
}
@@ -1218,28 +1232,27 @@ static u32
netxen_nic_hw_read_wx_128M(struct netxen_adapter *adapter, ulong off)
{
unsigned long flags;
- void __iomem *addr;
+ void __iomem *addr, *mem_ptr = NULL;
u32 data;
- if (ADDR_IN_WINDOW1(off))
- addr = NETXEN_CRB_NORMALIZE(adapter, off);
- else
- addr = pci_base_offset(adapter, off);
-
- BUG_ON(!addr);
+ addr = netxen_nic_map_indirect_address_128M(adapter, off, &mem_ptr);
+ if (!addr)
+ return -EIO;
- if (ADDR_IN_WINDOW1(off)) { /* Window 1 */
- read_lock(&adapter->adapter_lock);
- data = readl(addr);
- read_unlock(&adapter->adapter_lock);
- } else { /* Window 0 */
- write_lock_irqsave(&adapter->adapter_lock, flags);
- netxen_nic_pci_change_crbwindow_128M(adapter, 0);
+ if (ADDR_IN_WINDOW1(off)) { /* Window 1 */
+ data = netxen_nic_io_read_128M(adapter, addr);
+ } else { /* Window 0 */
+ write_lock_irqsave(&adapter->ahw.crb_lock, flags);
+ netxen_nic_pci_set_crbwindow_128M(adapter, 0);
data = readl(addr);
- netxen_nic_pci_change_crbwindow_128M(adapter, 1);
- write_unlock_irqrestore(&adapter->adapter_lock, flags);
+ netxen_nic_pci_set_crbwindow_128M(adapter,
+ NETXEN_WINDOW_ONE);
+ write_unlock_irqrestore(&adapter->ahw.crb_lock, flags);
}
+ if (mem_ptr)
+ iounmap(mem_ptr);
+
return data;
}
@@ -1248,28 +1261,30 @@ netxen_nic_hw_write_wx_2M(struct netxen_adapter *adapter, ulong off, u32 data)
{
unsigned long flags;
int rv;
+ void __iomem *addr = NULL;
- rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off);
+ rv = netxen_nic_pci_get_crb_addr_2M(adapter, off, &addr);
- if (rv == -1) {
- printk(KERN_ERR "%s: invalid offset: 0x%016lx\n",
- __func__, off);
- dump_stack();
- return -1;
+ if (rv == 0) {
+ writel(data, addr);
+ return 0;
}
- if (rv == 1) {
- write_lock_irqsave(&adapter->adapter_lock, flags);
+ if (rv > 0) {
+ /* indirect access */
+ write_lock_irqsave(&adapter->ahw.crb_lock, flags);
crb_win_lock(adapter);
- netxen_nic_pci_set_crbwindow_2M(adapter, &off);
- writel(data, (void __iomem *)off);
+ netxen_nic_pci_set_crbwindow_2M(adapter, off);
+ writel(data, addr);
crb_win_unlock(adapter);
- write_unlock_irqrestore(&adapter->adapter_lock, flags);
- } else
- writel(data, (void __iomem *)off);
-
+ write_unlock_irqrestore(&adapter->ahw.crb_lock, flags);
+ return 0;
+ }
- return 0;
+ dev_err(&adapter->pdev->dev,
+ "%s: invalid offset: 0x%016lx\n", __func__, off);
+ dump_stack();
+ return -EIO;
}
static u32
@@ -1278,102 +1293,37 @@ netxen_nic_hw_read_wx_2M(struct netxen_adapter *adapter, ulong off)
unsigned long flags;
int rv;
u32 data;
+ void __iomem *addr = NULL;
- rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off);
+ rv = netxen_nic_pci_get_crb_addr_2M(adapter, off, &addr);
- if (rv == -1) {
- printk(KERN_ERR "%s: invalid offset: 0x%016lx\n",
- __func__, off);
- dump_stack();
- return -1;
- }
+ if (rv == 0)
+ return readl(addr);
- if (rv == 1) {
- write_lock_irqsave(&adapter->adapter_lock, flags);
+ if (rv > 0) {
+ /* indirect access */
+ write_lock_irqsave(&adapter->ahw.crb_lock, flags);
crb_win_lock(adapter);
- netxen_nic_pci_set_crbwindow_2M(adapter, &off);
- data = readl((void __iomem *)off);
+ netxen_nic_pci_set_crbwindow_2M(adapter, off);
+ data = readl(addr);
crb_win_unlock(adapter);
- write_unlock_irqrestore(&adapter->adapter_lock, flags);
- } else
- data = readl((void __iomem *)off);
-
- return data;
-}
-
-static int netxen_pci_set_window_warning_count;
-
-static unsigned long
-netxen_nic_pci_set_window_128M(struct netxen_adapter *adapter,
- unsigned long long addr)
-{
- void __iomem *offset;
- int window;
- unsigned long long qdr_max;
- uint8_t func = adapter->ahw.pci_func;
-
- if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
- qdr_max = NETXEN_ADDR_QDR_NET_MAX_P2;
- } else {
- qdr_max = NETXEN_ADDR_QDR_NET_MAX_P3;
+ write_unlock_irqrestore(&adapter->ahw.crb_lock, flags);
+ return data;
}
- if (ADDR_IN_RANGE(addr, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
- /* DDR network side */
- addr -= NETXEN_ADDR_DDR_NET;
- window = (addr >> 25) & 0x3ff;
- if (adapter->ahw.ddr_mn_window != window) {
- adapter->ahw.ddr_mn_window = window;
- offset = PCI_OFFSET_SECOND_RANGE(adapter,
- NETXEN_PCIX_PH_REG(PCIE_MN_WINDOW_REG(func)));
- writel(window, offset);
- /* MUST make sure window is set before we forge on... */
- readl(offset);
- }
- addr -= (window * NETXEN_WINDOW_ONE);
- addr += NETXEN_PCI_DDR_NET;
- } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) {
- addr -= NETXEN_ADDR_OCM0;
- addr += NETXEN_PCI_OCM0;
- } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) {
- addr -= NETXEN_ADDR_OCM1;
- addr += NETXEN_PCI_OCM1;
- } else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_QDR_NET, qdr_max)) {
- /* QDR network side */
- addr -= NETXEN_ADDR_QDR_NET;
- window = (addr >> 22) & 0x3f;
- if (adapter->ahw.qdr_sn_window != window) {
- adapter->ahw.qdr_sn_window = window;
- offset = PCI_OFFSET_SECOND_RANGE(adapter,
- NETXEN_PCIX_PH_REG(PCIE_SN_WINDOW_REG(func)));
- writel((window << 22), offset);
- /* MUST make sure window is set before we forge on... */
- readl(offset);
- }
- addr -= (window * 0x400000);
- addr += NETXEN_PCI_QDR_NET;
- } else {
- /*
- * peg gdb frequently accesses memory that doesn't exist,
- * this limits the chit chat so debugging isn't slowed down.
- */
- if ((netxen_pci_set_window_warning_count++ < 8)
- || (netxen_pci_set_window_warning_count % 64 == 0))
- printk("%s: Warning:netxen_nic_pci_set_window()"
- " Unknown address range!\n",
- netxen_nic_driver_name);
- addr = -1UL;
- }
- return addr;
+ dev_err(&adapter->pdev->dev,
+ "%s: invalid offset: 0x%016lx\n", __func__, off);
+ dump_stack();
+ return -1;
}
/* window 1 registers only */
static void netxen_nic_io_write_128M(struct netxen_adapter *adapter,
void __iomem *addr, u32 data)
{
- read_lock(&adapter->adapter_lock);
+ read_lock(&adapter->ahw.crb_lock);
writel(data, addr);
- read_unlock(&adapter->adapter_lock);
+ read_unlock(&adapter->ahw.crb_lock);
}
static u32 netxen_nic_io_read_128M(struct netxen_adapter *adapter,
@@ -1381,9 +1331,9 @@ static u32 netxen_nic_io_read_128M(struct netxen_adapter *adapter,
{
u32 val;
- read_lock(&adapter->adapter_lock);
+ read_lock(&adapter->ahw.crb_lock);
val = readl(addr);
- read_unlock(&adapter->adapter_lock);
+ read_unlock(&adapter->ahw.crb_lock);
return val;
}
@@ -1403,488 +1353,437 @@ static u32 netxen_nic_io_read_2M(struct netxen_adapter *adapter,
void __iomem *
netxen_get_ioaddr(struct netxen_adapter *adapter, u32 offset)
{
- ulong off = offset;
+ void __iomem *addr = NULL;
if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
- if (offset < NETXEN_CRB_PCIX_HOST2 &&
- offset > NETXEN_CRB_PCIX_HOST)
- return PCI_OFFSET_SECOND_RANGE(adapter, offset);
- return NETXEN_CRB_NORMALIZE(adapter, offset);
+ if ((offset < NETXEN_CRB_PCIX_HOST2) &&
+ (offset > NETXEN_CRB_PCIX_HOST))
+ addr = PCI_OFFSET_SECOND_RANGE(adapter, offset);
+ else
+ addr = NETXEN_CRB_NORMALIZE(adapter, offset);
+ } else {
+ WARN_ON(netxen_nic_pci_get_crb_addr_2M(adapter,
+ offset, &addr));
}
- BUG_ON(netxen_nic_pci_get_crb_addr_2M(adapter, &off));
- return (void __iomem *)off;
+ return addr;
}
-static unsigned long
-netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter,
- unsigned long long addr)
+static int
+netxen_nic_pci_set_window_128M(struct netxen_adapter *adapter,
+ u64 addr, u32 *start)
{
- int window;
- u32 win_read;
-
- if (ADDR_IN_RANGE(addr, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
- /* DDR network side */
- window = MN_WIN(addr);
- adapter->ahw.ddr_mn_window = window;
- NXWR32(adapter, adapter->ahw.mn_win_crb, window);
- win_read = NXRD32(adapter, adapter->ahw.mn_win_crb);
- if ((win_read << 17) != window) {
- printk(KERN_INFO "Written MNwin (0x%x) != "
- "Read MNwin (0x%x)\n", window, win_read);
- }
- addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_DDR_NET;
+ if (ADDR_IN_RANGE(addr, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) {
+ *start = (addr - NETXEN_ADDR_OCM0 + NETXEN_PCI_OCM0);
+ return 0;
} else if (ADDR_IN_RANGE(addr,
- NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) {
- if ((addr & 0x00ff800) == 0xff800) {
- printk("%s: QM access not handled.\n", __func__);
- addr = -1UL;
- }
+ NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) {
+ *start = (addr - NETXEN_ADDR_OCM1 + NETXEN_PCI_OCM1);
+ return 0;
+ }
+
+ return -EIO;
+}
+static int
+netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter,
+ u64 addr, u32 *start)
+{
+ u32 window;
+ struct pci_dev *pdev = adapter->pdev;
+
+ if ((addr & 0x00ff800) == 0xff800) {
+ if (printk_ratelimit())
+ dev_warn(&pdev->dev, "QM access not handled\n");
+ return -EIO;
+ }
+
+ if (NX_IS_REVISION_P3P(adapter->ahw.revision_id))
+ window = OCM_WIN_P3P(addr);
+ else
window = OCM_WIN(addr);
- adapter->ahw.ddr_mn_window = window;
- NXWR32(adapter, adapter->ahw.mn_win_crb, window);
- win_read = NXRD32(adapter, adapter->ahw.mn_win_crb);
- if ((win_read >> 7) != window) {
- printk(KERN_INFO "%s: Written OCMwin (0x%x) != "
- "Read OCMwin (0x%x)\n",
- __func__, window, win_read);
- }
- addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_OCM0_2M;
- } else if (ADDR_IN_RANGE(addr,
- NETXEN_ADDR_QDR_NET, NETXEN_ADDR_QDR_NET_MAX_P3)) {
- /* QDR network side */
- window = MS_WIN(addr);
- adapter->ahw.qdr_sn_window = window;
- NXWR32(adapter, adapter->ahw.ms_win_crb, window);
- win_read = NXRD32(adapter, adapter->ahw.ms_win_crb);
- if (win_read != window) {
- printk(KERN_INFO "%s: Written MSwin (0x%x) != "
- "Read MSwin (0x%x)\n",
- __func__, window, win_read);
- }
- addr = GET_MEM_OFFS_2M(addr) + NETXEN_PCI_QDR_NET;
+ writel(window, adapter->ahw.ocm_win_crb);
+ /* read back to flush */
+ readl(adapter->ahw.ocm_win_crb);
- } else {
- /*
- * peg gdb frequently accesses memory that doesn't exist,
- * this limits the chit chat so debugging isn't slowed down.
- */
- if ((netxen_pci_set_window_warning_count++ < 8)
- || (netxen_pci_set_window_warning_count%64 == 0)) {
- printk("%s: Warning:%s Unknown address range!\n",
- __func__, netxen_nic_driver_name);
+ adapter->ahw.ocm_win = window;
+ *start = NETXEN_PCI_OCM0_2M + GET_MEM_OFFS_2M(addr);
+ return 0;
}
- addr = -1UL;
+
+static int
+netxen_nic_pci_mem_access_direct(struct netxen_adapter *adapter, u64 off,
+ u64 *data, int op)
+{
+ void __iomem *addr, *mem_ptr = NULL;
+ resource_size_t mem_base;
+ int ret = -EIO;
+ u32 start;
+
+ spin_lock(&adapter->ahw.mem_lock);
+
+ ret = adapter->pci_set_window(adapter, off, &start);
+ if (ret != 0)
+ goto unlock;
+
+ addr = pci_base_offset(adapter, start);
+ if (addr)
+ goto noremap;
+
+ mem_base = pci_resource_start(adapter->pdev, 0) + (start & PAGE_MASK);
+
+ mem_ptr = ioremap(mem_base, PAGE_SIZE);
+ if (mem_ptr == NULL) {
+ ret = -EIO;
+ goto unlock;
}
- return addr;
+
+ addr = mem_ptr + (start & (PAGE_SIZE - 1));
+
+noremap:
+ if (op == 0) /* read */
+ *data = readq(addr);
+ else /* write */
+ writeq(*data, addr);
+
+unlock:
+ spin_unlock(&adapter->ahw.mem_lock);
+
+ if (mem_ptr)
+ iounmap(mem_ptr);
+ return ret;
}
#define MAX_CTL_CHECK 1000
static int
netxen_nic_pci_mem_write_128M(struct netxen_adapter *adapter,
- u64 off, void *data, int size)
+ u64 off, u64 data)
{
- unsigned long flags;
- int i, j, ret = 0, loop, sz[2], off0;
- uint32_t temp;
- uint64_t off8, tmpw, word[2] = {0, 0};
+ int j, ret;
+ u32 temp, off_lo, off_hi, addr_hi, data_hi, data_lo;
void __iomem *mem_crb;
- if (size != 8)
+ /* Only 64-bit aligned access */
+ if (off & 7)
return -EIO;
+ /* P2 has different SIU and MIU test agent base addr */
if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
NETXEN_ADDR_QDR_NET_MAX_P2)) {
- mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET);
+ mem_crb = pci_base_offset(adapter,
+ NETXEN_CRB_QDR_NET+SIU_TEST_AGT_BASE);
+ addr_hi = SIU_TEST_AGT_ADDR_HI;
+ data_lo = SIU_TEST_AGT_WRDATA_LO;
+ data_hi = SIU_TEST_AGT_WRDATA_HI;
+ off_lo = off & SIU_TEST_AGT_ADDR_MASK;
+ off_hi = SIU_TEST_AGT_UPPER_ADDR(off);
goto correct;
}
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
- mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
+ mem_crb = pci_base_offset(adapter,
+ NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
+ addr_hi = MIU_TEST_AGT_ADDR_HI;
+ data_lo = MIU_TEST_AGT_WRDATA_LO;
+ data_hi = MIU_TEST_AGT_WRDATA_HI;
+ off_lo = off & MIU_TEST_AGT_ADDR_MASK;
+ off_hi = 0;
goto correct;
}
- return -EIO;
-
-correct:
- off8 = off & 0xfffffff8;
- off0 = off & 0x7;
- sz[0] = (size < (8 - off0)) ? size : (8 - off0);
- sz[1] = size - sz[0];
- loop = ((off0 + size - 1) >> 3) + 1;
-
- if ((size != 8) || (off0 != 0)) {
- for (i = 0; i < loop; i++) {
- if (adapter->pci_mem_read(adapter,
- off8 + (i << 3), &word[i], 8))
- return -1;
+ if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX) ||
+ ADDR_IN_RANGE(off, NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) {
+ if (adapter->ahw.pci_len0 != 0) {
+ return netxen_nic_pci_mem_access_direct(adapter,
+ off, &data, 1);
}
}
- switch (size) {
- case 1:
- tmpw = *((uint8_t *)data);
- break;
- case 2:
- tmpw = *((uint16_t *)data);
- break;
- case 4:
- tmpw = *((uint32_t *)data);
- break;
- case 8:
- default:
- tmpw = *((uint64_t *)data);
- break;
- }
- word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8));
- word[0] |= tmpw << (off0 * 8);
+ return -EIO;
- if (loop == 2) {
- word[1] &= ~(~0ULL << (sz[1] * 8));
- word[1] |= tmpw >> (sz[0] * 8);
+correct:
+ spin_lock(&adapter->ahw.mem_lock);
+ netxen_nic_pci_set_crbwindow_128M(adapter, 0);
+
+ writel(off_lo, (mem_crb + MIU_TEST_AGT_ADDR_LO));
+ writel(off_hi, (mem_crb + addr_hi));
+ writel(data & 0xffffffff, (mem_crb + data_lo));
+ writel((data >> 32) & 0xffffffff, (mem_crb + data_hi));
+ writel((TA_CTL_ENABLE | TA_CTL_WRITE), (mem_crb + TEST_AGT_CTRL));
+ writel((TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE),
+ (mem_crb + TEST_AGT_CTRL));
+
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ temp = readl((mem_crb + TEST_AGT_CTRL));
+ if ((temp & TA_CTL_BUSY) == 0)
+ break;
}
- write_lock_irqsave(&adapter->adapter_lock, flags);
- netxen_nic_pci_change_crbwindow_128M(adapter, 0);
-
- for (i = 0; i < loop; i++) {
- writel((uint32_t)(off8 + (i << 3)),
- (mem_crb+MIU_TEST_AGT_ADDR_LO));
- writel(0,
- (mem_crb+MIU_TEST_AGT_ADDR_HI));
- writel(word[i] & 0xffffffff,
- (mem_crb+MIU_TEST_AGT_WRDATA_LO));
- writel((word[i] >> 32) & 0xffffffff,
- (mem_crb+MIU_TEST_AGT_WRDATA_HI));
- writel(MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE,
- (mem_crb+MIU_TEST_AGT_CTRL));
- writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE,
- (mem_crb+MIU_TEST_AGT_CTRL));
-
- for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = readl(
- (mem_crb+MIU_TEST_AGT_CTRL));
- if ((temp & MIU_TA_CTL_BUSY) == 0)
- break;
- }
-
- if (j >= MAX_CTL_CHECK) {
- if (printk_ratelimit())
- dev_err(&adapter->pdev->dev,
+ if (j >= MAX_CTL_CHECK) {
+ if (printk_ratelimit())
+ dev_err(&adapter->pdev->dev,
"failed to write through agent\n");
- ret = -1;
- break;
- }
- }
+ ret = -EIO;
+ } else
+ ret = 0;
- netxen_nic_pci_change_crbwindow_128M(adapter, 1);
- write_unlock_irqrestore(&adapter->adapter_lock, flags);
+ netxen_nic_pci_set_crbwindow_128M(adapter, NETXEN_WINDOW_ONE);
+ spin_unlock(&adapter->ahw.mem_lock);
return ret;
}
static int
netxen_nic_pci_mem_read_128M(struct netxen_adapter *adapter,
- u64 off, void *data, int size)
+ u64 off, u64 *data)
{
- unsigned long flags;
- int i, j = 0, k, start, end, loop, sz[2], off0[2];
- uint32_t temp;
- uint64_t off8, val, word[2] = {0, 0};
+ int j, ret;
+ u32 temp, off_lo, off_hi, addr_hi, data_hi, data_lo;
+ u64 val;
void __iomem *mem_crb;
- if (size != 8)
+ /* Only 64-bit aligned access */
+ if (off & 7)
return -EIO;
+ /* P2 has different SIU and MIU test agent base addr */
if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
NETXEN_ADDR_QDR_NET_MAX_P2)) {
- mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET);
+ mem_crb = pci_base_offset(adapter,
+ NETXEN_CRB_QDR_NET+SIU_TEST_AGT_BASE);
+ addr_hi = SIU_TEST_AGT_ADDR_HI;
+ data_lo = SIU_TEST_AGT_RDDATA_LO;
+ data_hi = SIU_TEST_AGT_RDDATA_HI;
+ off_lo = off & SIU_TEST_AGT_ADDR_MASK;
+ off_hi = SIU_TEST_AGT_UPPER_ADDR(off);
goto correct;
}
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
- mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
+ mem_crb = pci_base_offset(adapter,
+ NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
+ addr_hi = MIU_TEST_AGT_ADDR_HI;
+ data_lo = MIU_TEST_AGT_RDDATA_LO;
+ data_hi = MIU_TEST_AGT_RDDATA_HI;
+ off_lo = off & MIU_TEST_AGT_ADDR_MASK;
+ off_hi = 0;
goto correct;
}
+ if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX) ||
+ ADDR_IN_RANGE(off, NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) {
+ if (adapter->ahw.pci_len0 != 0) {
+ return netxen_nic_pci_mem_access_direct(adapter,
+ off, data, 0);
+ }
+ }
+
return -EIO;
correct:
- off8 = off & 0xfffffff8;
- off0[0] = off & 0x7;
- off0[1] = 0;
- sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]);
- sz[1] = size - sz[0];
- loop = ((off0[0] + size - 1) >> 3) + 1;
-
- write_lock_irqsave(&adapter->adapter_lock, flags);
- netxen_nic_pci_change_crbwindow_128M(adapter, 0);
-
- for (i = 0; i < loop; i++) {
- writel((uint32_t)(off8 + (i << 3)),
- (mem_crb+MIU_TEST_AGT_ADDR_LO));
- writel(0,
- (mem_crb+MIU_TEST_AGT_ADDR_HI));
- writel(MIU_TA_CTL_ENABLE,
- (mem_crb+MIU_TEST_AGT_CTRL));
- writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE,
- (mem_crb+MIU_TEST_AGT_CTRL));
+ spin_lock(&adapter->ahw.mem_lock);
+ netxen_nic_pci_set_crbwindow_128M(adapter, 0);
- for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = readl(
- (mem_crb+MIU_TEST_AGT_CTRL));
- if ((temp & MIU_TA_CTL_BUSY) == 0)
- break;
- }
+ writel(off_lo, (mem_crb + MIU_TEST_AGT_ADDR_LO));
+ writel(off_hi, (mem_crb + addr_hi));
+ writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL));
+ writel((TA_CTL_START|TA_CTL_ENABLE), (mem_crb + TEST_AGT_CTRL));
- if (j >= MAX_CTL_CHECK) {
- if (printk_ratelimit())
- dev_err(&adapter->pdev->dev,
- "failed to read through agent\n");
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ temp = readl(mem_crb + TEST_AGT_CTRL);
+ if ((temp & TA_CTL_BUSY) == 0)
break;
- }
-
- start = off0[i] >> 2;
- end = (off0[i] + sz[i] - 1) >> 2;
- for (k = start; k <= end; k++) {
- word[i] |= ((uint64_t) readl(
- (mem_crb +
- MIU_TEST_AGT_RDDATA(k))) << (32*k));
- }
}
- netxen_nic_pci_change_crbwindow_128M(adapter, 1);
- write_unlock_irqrestore(&adapter->adapter_lock, flags);
-
- if (j >= MAX_CTL_CHECK)
- return -1;
-
- if (sz[0] == 8) {
- val = word[0];
+ if (j >= MAX_CTL_CHECK) {
+ if (printk_ratelimit())
+ dev_err(&adapter->pdev->dev,
+ "failed to read through agent\n");
+ ret = -EIO;
} else {
- val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) |
- ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8));
- }
- switch (size) {
- case 1:
- *(uint8_t *)data = val;
- break;
- case 2:
- *(uint16_t *)data = val;
- break;
- case 4:
- *(uint32_t *)data = val;
- break;
- case 8:
- *(uint64_t *)data = val;
- break;
+ temp = readl(mem_crb + data_hi);
+ val = ((u64)temp << 32);
+ val |= readl(mem_crb + data_lo);
+ *data = val;
+ ret = 0;
}
- return 0;
+
+ netxen_nic_pci_set_crbwindow_128M(adapter, NETXEN_WINDOW_ONE);
+ spin_unlock(&adapter->ahw.mem_lock);
+
+ return ret;
}
static int
netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter,
- u64 off, void *data, int size)
+ u64 off, u64 data)
{
- int i, j, ret = 0, loop, sz[2], off0;
- uint32_t temp;
- uint64_t off8, tmpw, word[2] = {0, 0};
+ int i, j, ret;
+ u32 temp, off8;
+ u64 stride;
void __iomem *mem_crb;
- if (size != 8)
+ /* Only 64-bit aligned access */
+ if (off & 7)
return -EIO;
+ /* P3 onward, test agent base for MIU and SIU is same */
if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
NETXEN_ADDR_QDR_NET_MAX_P3)) {
- mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET);
+ mem_crb = netxen_get_ioaddr(adapter,
+ NETXEN_CRB_QDR_NET+MIU_TEST_AGT_BASE);
goto correct;
}
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
- mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET);
+ mem_crb = netxen_get_ioaddr(adapter,
+ NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
goto correct;
}
+ if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX))
+ return netxen_nic_pci_mem_access_direct(adapter, off, &data, 1);
+
return -EIO;
correct:
- off8 = off & 0xfffffff8;
- off0 = off & 0x7;
- sz[0] = (size < (8 - off0)) ? size : (8 - off0);
- sz[1] = size - sz[0];
- loop = ((off0 + size - 1) >> 3) + 1;
-
- if ((size != 8) || (off0 != 0)) {
- for (i = 0; i < loop; i++) {
- if (adapter->pci_mem_read(adapter,
- off8 + (i << 3), &word[i], 8))
- return -1;
- }
- }
-
- switch (size) {
- case 1:
- tmpw = *((uint8_t *)data);
- break;
- case 2:
- tmpw = *((uint16_t *)data);
- break;
- case 4:
- tmpw = *((uint32_t *)data);
- break;
- case 8:
- default:
- tmpw = *((uint64_t *)data);
- break;
- }
+ stride = NX_IS_REVISION_P3P(adapter->ahw.revision_id) ? 16 : 8;
- word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8));
- word[0] |= tmpw << (off0 * 8);
+ off8 = off & ~(stride-1);
- if (loop == 2) {
- word[1] &= ~(~0ULL << (sz[1] * 8));
- word[1] |= tmpw >> (sz[0] * 8);
- }
+ spin_lock(&adapter->ahw.mem_lock);
- /*
- * don't lock here - write_wx gets the lock if each time
- * write_lock_irqsave(&adapter->adapter_lock, flags);
- * netxen_nic_pci_change_crbwindow_128M(adapter, 0);
- */
+ writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO));
+ writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI));
- for (i = 0; i < loop; i++) {
- writel(off8 + (i << 3), mem_crb+MIU_TEST_AGT_ADDR_LO);
- writel(0, mem_crb+MIU_TEST_AGT_ADDR_HI);
- writel(word[i] & 0xffffffff, mem_crb+MIU_TEST_AGT_WRDATA_LO);
- writel((word[i] >> 32) & 0xffffffff,
- mem_crb+MIU_TEST_AGT_WRDATA_HI);
- writel((MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE),
- mem_crb+MIU_TEST_AGT_CTRL);
- writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE,
- mem_crb+MIU_TEST_AGT_CTRL);
+ i = 0;
+ if (stride == 16) {
+ writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL));
+ writel((TA_CTL_START | TA_CTL_ENABLE),
+ (mem_crb + TEST_AGT_CTRL));
for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = readl(mem_crb + MIU_TEST_AGT_CTRL);
- if ((temp & MIU_TA_CTL_BUSY) == 0)
+ temp = readl(mem_crb + TEST_AGT_CTRL);
+ if ((temp & TA_CTL_BUSY) == 0)
break;
}
if (j >= MAX_CTL_CHECK) {
- if (printk_ratelimit())
- dev_err(&adapter->pdev->dev,
- "failed to write through agent\n");
- ret = -1;
- break;
+ ret = -EIO;
+ goto done;
}
+
+ i = (off & 0xf) ? 0 : 2;
+ writel(readl(mem_crb + MIU_TEST_AGT_RDDATA(i)),
+ mem_crb + MIU_TEST_AGT_WRDATA(i));
+ writel(readl(mem_crb + MIU_TEST_AGT_RDDATA(i+1)),
+ mem_crb + MIU_TEST_AGT_WRDATA(i+1));
+ i = (off & 0xf) ? 2 : 0;
}
- /*
- * netxen_nic_pci_change_crbwindow_128M(adapter, 1);
- * write_unlock_irqrestore(&adapter->adapter_lock, flags);
- */
+ writel(data & 0xffffffff,
+ mem_crb + MIU_TEST_AGT_WRDATA(i));
+ writel((data >> 32) & 0xffffffff,
+ mem_crb + MIU_TEST_AGT_WRDATA(i+1));
+
+ writel((TA_CTL_ENABLE | TA_CTL_WRITE), (mem_crb + TEST_AGT_CTRL));
+ writel((TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE),
+ (mem_crb + TEST_AGT_CTRL));
+
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ temp = readl(mem_crb + TEST_AGT_CTRL);
+ if ((temp & TA_CTL_BUSY) == 0)
+ break;
+ }
+
+ if (j >= MAX_CTL_CHECK) {
+ if (printk_ratelimit())
+ dev_err(&adapter->pdev->dev,
+ "failed to write through agent\n");
+ ret = -EIO;
+ } else
+ ret = 0;
+
+done:
+ spin_unlock(&adapter->ahw.mem_lock);
+
return ret;
}
static int
netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter,
- u64 off, void *data, int size)
+ u64 off, u64 *data)
{
- int i, j = 0, k, start, end, loop, sz[2], off0[2];
- uint32_t temp;
- uint64_t off8, val, word[2] = {0, 0};
+ int j, ret;
+ u32 temp, off8;
+ u64 val, stride;
void __iomem *mem_crb;
- if (size != 8)
+ /* Only 64-bit aligned access */
+ if (off & 7)
return -EIO;
+ /* P3 onward, test agent base for MIU and SIU is same */
if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
NETXEN_ADDR_QDR_NET_MAX_P3)) {
- mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET);
+ mem_crb = netxen_get_ioaddr(adapter,
+ NETXEN_CRB_QDR_NET+MIU_TEST_AGT_BASE);
goto correct;
}
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
- mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET);
+ mem_crb = netxen_get_ioaddr(adapter,
+ NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
goto correct;
}
+ if (ADDR_IN_RANGE(off, NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) {
+ return netxen_nic_pci_mem_access_direct(adapter,
+ off, data, 0);
+ }
+
return -EIO;
correct:
- off8 = off & 0xfffffff8;
- off0[0] = off & 0x7;
- off0[1] = 0;
- sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]);
- sz[1] = size - sz[0];
- loop = ((off0[0] + size - 1) >> 3) + 1;
+ stride = NX_IS_REVISION_P3P(adapter->ahw.revision_id) ? 16 : 8;
- /*
- * don't lock here - write_wx gets the lock if each time
- * write_lock_irqsave(&adapter->adapter_lock, flags);
- * netxen_nic_pci_change_crbwindow_128M(adapter, 0);
- */
+ off8 = off & ~(stride-1);
- for (i = 0; i < loop; i++) {
- writel(off8 + (i << 3), mem_crb + MIU_TEST_AGT_ADDR_LO);
- writel(0, mem_crb + MIU_TEST_AGT_ADDR_HI);
- writel(MIU_TA_CTL_ENABLE, mem_crb + MIU_TEST_AGT_CTRL);
- writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE,
- mem_crb + MIU_TEST_AGT_CTRL);
+ spin_lock(&adapter->ahw.mem_lock);
- for (j = 0; j < MAX_CTL_CHECK; j++) {
- temp = readl(mem_crb + MIU_TEST_AGT_CTRL);
- if ((temp & MIU_TA_CTL_BUSY) == 0)
- break;
- }
+ writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO));
+ writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI));
+ writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL));
+ writel((TA_CTL_START | TA_CTL_ENABLE), (mem_crb + TEST_AGT_CTRL));
- if (j >= MAX_CTL_CHECK) {
- if (printk_ratelimit())
- dev_err(&adapter->pdev->dev,
- "failed to read through agent\n");
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ temp = readl(mem_crb + TEST_AGT_CTRL);
+ if ((temp & TA_CTL_BUSY) == 0)
break;
- }
-
- start = off0[i] >> 2;
- end = (off0[i] + sz[i] - 1) >> 2;
- for (k = start; k <= end; k++) {
- temp = readl(mem_crb + MIU_TEST_AGT_RDDATA(k));
- word[i] |= ((uint64_t)temp << (32 * k));
- }
}
- /*
- * netxen_nic_pci_change_crbwindow_128M(adapter, 1);
- * write_unlock_irqrestore(&adapter->adapter_lock, flags);
- */
-
- if (j >= MAX_CTL_CHECK)
- return -1;
-
- if (sz[0] == 8) {
- val = word[0];
+ if (j >= MAX_CTL_CHECK) {
+ if (printk_ratelimit())
+ dev_err(&adapter->pdev->dev,
+ "failed to read through agent\n");
+ ret = -EIO;
} else {
- val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) |
- ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8));
- }
+ off8 = MIU_TEST_AGT_RDDATA_LO;
+ if ((stride == 16) && (off & 0xf))
+ off8 = MIU_TEST_AGT_RDDATA_UPPER_LO;
- switch (size) {
- case 1:
- *(uint8_t *)data = val;
- break;
- case 2:
- *(uint16_t *)data = val;
- break;
- case 4:
- *(uint32_t *)data = val;
- break;
- case 8:
- *(uint64_t *)data = val;
- break;
+ temp = readl(mem_crb + off8 + 4);
+ val = (u64)temp << 32;
+ val |= readl(mem_crb + off8);
+ *data = val;
+ ret = 0;
}
- return 0;
+
+ spin_unlock(&adapter->ahw.mem_lock);
+
+ return ret;
}
void
@@ -2037,10 +1936,10 @@ void netxen_nic_set_link_parameters(struct netxen_adapter *adapter)
return;
}
- if (adapter->phy_read
- && adapter->phy_read(adapter,
- NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
- &status) == 0) {
+ if (adapter->phy_read &&
+ adapter->phy_read(adapter,
+ NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS,
+ &status) == 0) {
if (netxen_get_phy_link(status)) {
switch (netxen_get_phy_speed(status)) {
case 0:
@@ -2067,10 +1966,10 @@ void netxen_nic_set_link_parameters(struct netxen_adapter *adapter)
adapter->link_duplex = -1;
break;
}
- if (adapter->phy_read
- && adapter->phy_read(adapter,
- NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG,
- &autoneg) != 0)
+ if (adapter->phy_read &&
+ adapter->phy_read(adapter,
+ NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG,
+ &autoneg) != 0)
adapter->link_autoneg = autoneg;
} else
goto link_down;
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index 8a0904368e0..80a66746051 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -46,6 +46,7 @@ static unsigned int crb_addr_xform[NETXEN_MAX_CRB_XFORM];
static void
netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter,
struct nx_host_rds_ring *rds_ring);
+static int netxen_p3_has_mn(struct netxen_adapter *adapter);
static void crb_addr_transform_setup(void)
{
@@ -437,7 +438,7 @@ int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp)
#define NETXEN_BOARDNUM 0x400c
#define NETXEN_CHIPNUM 0x4010
-int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
+int netxen_pinit_from_rom(struct netxen_adapter *adapter)
{
int addr, val;
int i, n, init_delay = 0;
@@ -450,21 +451,6 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
NXWR32(adapter, NETXEN_ROMUSB_GLB_SW_RESET, 0xffffffff);
netxen_rom_unlock(adapter);
- if (verbose) {
- if (netxen_rom_fast_read(adapter, NETXEN_BOARDTYPE, &val) == 0)
- printk("P2 ROM board type: 0x%08x\n", val);
- else
- printk("Could not read board type\n");
- if (netxen_rom_fast_read(adapter, NETXEN_BOARDNUM, &val) == 0)
- printk("P2 ROM board num: 0x%08x\n", val);
- else
- printk("Could not read board number\n");
- if (netxen_rom_fast_read(adapter, NETXEN_CHIPNUM, &val) == 0)
- printk("P2 ROM chip num: 0x%08x\n", val);
- else
- printk("Could not read chip number\n");
- }
-
if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
if (netxen_rom_fast_read(adapter, 0, &n) != 0 ||
(n != 0xcafecafe) ||
@@ -486,11 +472,7 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
n &= ~0x80000000;
}
- if (n < 1024) {
- if (verbose)
- printk(KERN_DEBUG "%s: %d CRB init values found"
- " in ROM.\n", netxen_nic_driver_name, n);
- } else {
+ if (n >= 1024) {
printk(KERN_ERR "%s:n=0x%x Error! NetXen card flash not"
" initialized.\n", __func__, n);
return -EIO;
@@ -502,6 +484,7 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
netxen_nic_driver_name);
return -ENOMEM;
}
+
for (i = 0; i < n; i++) {
if (netxen_rom_fast_read(adapter, 8*i + 4*offset, &val) != 0 ||
netxen_rom_fast_read(adapter, 8*i + 4*offset + 4, &addr) != 0) {
@@ -512,11 +495,8 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
buf[i].addr = addr;
buf[i].data = val;
- if (verbose)
- printk(KERN_DEBUG "%s: PCI: 0x%08x == 0x%08x\n",
- netxen_nic_driver_name,
- (u32)netxen_decode_crb_addr(addr), val);
}
+
for (i = 0; i < n; i++) {
off = netxen_decode_crb_addr(buf[i].addr);
@@ -526,6 +506,10 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
continue;
}
off += NETXEN_PCI_CRBSPACE;
+
+ if (off & 1)
+ continue;
+
/* skipping cold reboot MAGIC */
if (off == NETXEN_CAM_RAM(0x1fc))
continue;
@@ -546,7 +530,8 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
continue;
if ((off & 0x0ff00000) == NETXEN_CRB_DDR_NET)
continue;
- if (off == (NETXEN_CRB_PEG_NET_1 + 0x18))
+ if (off == (NETXEN_CRB_PEG_NET_1 + 0x18) &&
+ !NX_IS_REVISION_P3P(adapter->ahw.revision_id))
buf[i].data = 0x1020;
/* skip the function enable register */
if (off == NETXEN_PCIE_REG(PCIE_SETUP_FUNCTION))
@@ -607,6 +592,172 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose)
return 0;
}
+static struct uni_table_desc *nx_get_table_desc(const u8 *unirom, int section)
+{
+ uint32_t i;
+ struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0];
+ __le32 entries = cpu_to_le32(directory->num_entries);
+
+ for (i = 0; i < entries; i++) {
+
+ __le32 offs = cpu_to_le32(directory->findex) +
+ (i * cpu_to_le32(directory->entry_size));
+ __le32 tab_type = cpu_to_le32(*((u32 *)&unirom[offs] + 8));
+
+ if (tab_type == section)
+ return (struct uni_table_desc *) &unirom[offs];
+ }
+
+ return NULL;
+}
+
+static int
+nx_set_product_offs(struct netxen_adapter *adapter)
+{
+ struct uni_table_desc *ptab_descr;
+ const u8 *unirom = adapter->fw->data;
+ uint32_t i;
+ __le32 entries;
+
+ ptab_descr = nx_get_table_desc(unirom, NX_UNI_DIR_SECT_PRODUCT_TBL);
+ if (ptab_descr == NULL)
+ return -1;
+
+ entries = cpu_to_le32(ptab_descr->num_entries);
+
+ for (i = 0; i < entries; i++) {
+
+ __le32 flags, file_chiprev, offs;
+ u8 chiprev = adapter->ahw.revision_id;
+ int mn_present = netxen_p3_has_mn(adapter);
+ uint32_t flagbit;
+
+ offs = cpu_to_le32(ptab_descr->findex) +
+ (i * cpu_to_le32(ptab_descr->entry_size));
+ flags = cpu_to_le32(*((int *)&unirom[offs] + NX_UNI_FLAGS_OFF));
+ file_chiprev = cpu_to_le32(*((int *)&unirom[offs] +
+ NX_UNI_CHIP_REV_OFF));
+
+ flagbit = mn_present ? 1 : 2;
+
+ if ((chiprev == file_chiprev) &&
+ ((1ULL << flagbit) & flags)) {
+ adapter->file_prd_off = offs;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static struct uni_data_desc *nx_get_data_desc(struct netxen_adapter *adapter,
+ u32 section, u32 idx_offset)
+{
+ const u8 *unirom = adapter->fw->data;
+ int idx = cpu_to_le32(*((int *)&unirom[adapter->file_prd_off] +
+ idx_offset));
+ struct uni_table_desc *tab_desc;
+ __le32 offs;
+
+ tab_desc = nx_get_table_desc(unirom, section);
+
+ if (tab_desc == NULL)
+ return NULL;
+
+ offs = cpu_to_le32(tab_desc->findex) +
+ (cpu_to_le32(tab_desc->entry_size) * idx);
+
+ return (struct uni_data_desc *)&unirom[offs];
+}
+
+static u8 *
+nx_get_bootld_offs(struct netxen_adapter *adapter)
+{
+ u32 offs = NETXEN_BOOTLD_START;
+
+ if (adapter->fw_type == NX_UNIFIED_ROMIMAGE)
+ offs = cpu_to_le32((nx_get_data_desc(adapter,
+ NX_UNI_DIR_SECT_BOOTLD,
+ NX_UNI_BOOTLD_IDX_OFF))->findex);
+
+ return (u8 *)&adapter->fw->data[offs];
+}
+
+static u8 *
+nx_get_fw_offs(struct netxen_adapter *adapter)
+{
+ u32 offs = NETXEN_IMAGE_START;
+
+ if (adapter->fw_type == NX_UNIFIED_ROMIMAGE)
+ offs = cpu_to_le32((nx_get_data_desc(adapter,
+ NX_UNI_DIR_SECT_FW,
+ NX_UNI_FIRMWARE_IDX_OFF))->findex);
+
+ return (u8 *)&adapter->fw->data[offs];
+}
+
+static __le32
+nx_get_fw_size(struct netxen_adapter *adapter)
+{
+ if (adapter->fw_type == NX_UNIFIED_ROMIMAGE)
+ return cpu_to_le32((nx_get_data_desc(adapter,
+ NX_UNI_DIR_SECT_FW,
+ NX_UNI_FIRMWARE_IDX_OFF))->size);
+ else
+ return cpu_to_le32(
+ *(u32 *)&adapter->fw->data[NX_FW_SIZE_OFFSET]);
+}
+
+static __le32
+nx_get_fw_version(struct netxen_adapter *adapter)
+{
+ struct uni_data_desc *fw_data_desc;
+ const struct firmware *fw = adapter->fw;
+ __le32 major, minor, sub;
+ const u8 *ver_str;
+ int i, ret = 0;
+
+ if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) {
+
+ fw_data_desc = nx_get_data_desc(adapter,
+ NX_UNI_DIR_SECT_FW, NX_UNI_FIRMWARE_IDX_OFF);
+ ver_str = fw->data + cpu_to_le32(fw_data_desc->findex) +
+ cpu_to_le32(fw_data_desc->size) - 17;
+
+ for (i = 0; i < 12; i++) {
+ if (!strncmp(&ver_str[i], "REV=", 4)) {
+ ret = sscanf(&ver_str[i+4], "%u.%u.%u ",
+ &major, &minor, &sub);
+ break;
+ }
+ }
+
+ if (ret != 3)
+ return 0;
+
+ return major + (minor << 8) + (sub << 16);
+
+ } else
+ return cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]);
+}
+
+static __le32
+nx_get_bios_version(struct netxen_adapter *adapter)
+{
+ const struct firmware *fw = adapter->fw;
+ __le32 bios_ver, prd_off = adapter->file_prd_off;
+
+ if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) {
+ bios_ver = cpu_to_le32(*((u32 *) (&fw->data[prd_off])
+ + NX_UNI_BIOS_VERSION_OFF));
+ return (bios_ver << 24) + ((bios_ver >> 8) & 0xff00) +
+ (bios_ver >> 24);
+ } else
+ return cpu_to_le32(*(u32 *)&fw->data[NX_BIOS_VERSION_OFFSET]);
+
+}
+
int
netxen_need_fw_reset(struct netxen_adapter *adapter)
{
@@ -646,9 +797,8 @@ netxen_need_fw_reset(struct netxen_adapter *adapter)
/* check if we have got newer or different file firmware */
if (adapter->fw) {
- const struct firmware *fw = adapter->fw;
+ val = nx_get_fw_version(adapter);
- val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]);
version = NETXEN_DECODE_VERSION(val);
major = NXRD32(adapter, NETXEN_FW_VERSION_MAJOR);
@@ -658,7 +808,8 @@ netxen_need_fw_reset(struct netxen_adapter *adapter)
if (version > NETXEN_VERSION_CODE(major, minor, build))
return 1;
- if (version == NETXEN_VERSION_CODE(major, minor, build)) {
+ if (version == NETXEN_VERSION_CODE(major, minor, build) &&
+ adapter->fw_type != NX_UNIFIED_ROMIMAGE) {
val = NXRD32(adapter, NETXEN_MIU_MN_CONTROL);
fw_type = (val & 0x4) ?
@@ -673,7 +824,11 @@ netxen_need_fw_reset(struct netxen_adapter *adapter)
}
static char *fw_name[] = {
- "nxromimg.bin", "nx3fwct.bin", "nx3fwmn.bin", "flash",
+ NX_P2_MN_ROMIMAGE_NAME,
+ NX_P3_CT_ROMIMAGE_NAME,
+ NX_P3_MN_ROMIMAGE_NAME,
+ NX_UNIFIED_ROMIMAGE_NAME,
+ NX_FLASH_ROMIMAGE_NAME,
};
int
@@ -695,26 +850,28 @@ netxen_load_firmware(struct netxen_adapter *adapter)
size = (NETXEN_IMAGE_START - NETXEN_BOOTLD_START) / 8;
- ptr64 = (u64 *)&fw->data[NETXEN_BOOTLD_START];
+ ptr64 = (u64 *)nx_get_bootld_offs(adapter);
flashaddr = NETXEN_BOOTLD_START;
for (i = 0; i < size; i++) {
data = cpu_to_le64(ptr64[i]);
- adapter->pci_mem_write(adapter, flashaddr, &data, 8);
+
+ if (adapter->pci_mem_write(adapter, flashaddr, data))
+ return -EIO;
+
flashaddr += 8;
}
- size = *(u32 *)&fw->data[NX_FW_SIZE_OFFSET];
- size = (__force u32)cpu_to_le32(size) / 8;
+ size = (__force u32)nx_get_fw_size(adapter) / 8;
- ptr64 = (u64 *)&fw->data[NETXEN_IMAGE_START];
+ ptr64 = (u64 *)nx_get_fw_offs(adapter);
flashaddr = NETXEN_IMAGE_START;
for (i = 0; i < size; i++) {
data = cpu_to_le64(ptr64[i]);
if (adapter->pci_mem_write(adapter,
- flashaddr, &data, 8))
+ flashaddr, data))
return -EIO;
flashaddr += 8;
@@ -728,17 +885,17 @@ netxen_load_firmware(struct netxen_adapter *adapter)
for (i = 0; i < size; i++) {
if (netxen_rom_fast_read(adapter,
- flashaddr, &lo) != 0)
+ flashaddr, (int *)&lo) != 0)
return -EIO;
if (netxen_rom_fast_read(adapter,
- flashaddr + 4, &hi) != 0)
+ flashaddr + 4, (int *)&hi) != 0)
return -EIO;
/* hi, lo are already in host endian byteorder */
data = (((u64)hi << 32) | lo);
if (adapter->pci_mem_write(adapter,
- flashaddr, &data, 8))
+ flashaddr, data))
return -EIO;
flashaddr += 8;
@@ -746,7 +903,10 @@ netxen_load_firmware(struct netxen_adapter *adapter)
}
msleep(1);
- if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
+ if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) {
+ NXWR32(adapter, NETXEN_CRB_PEG_NET_0 + 0x18, 0x1020);
+ NXWR32(adapter, NETXEN_ROMUSB_GLB_SW_RESET, 0x80001e);
+ } else if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
NXWR32(adapter, NETXEN_ROMUSB_GLB_SW_RESET, 0x80001d);
else {
NXWR32(adapter, NETXEN_ROMUSB_GLB_CHIP_CLK_CTRL, 0x3fff);
@@ -757,21 +917,31 @@ netxen_load_firmware(struct netxen_adapter *adapter)
}
static int
-netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname)
+netxen_validate_firmware(struct netxen_adapter *adapter)
{
__le32 val;
- u32 ver, min_ver, bios;
+ u32 ver, min_ver, bios, min_size;
struct pci_dev *pdev = adapter->pdev;
const struct firmware *fw = adapter->fw;
+ u8 fw_type = adapter->fw_type;
- if (fw->size < NX_FW_MIN_SIZE)
- return -EINVAL;
+ if (fw_type == NX_UNIFIED_ROMIMAGE) {
+ if (nx_set_product_offs(adapter))
+ return -EINVAL;
+
+ min_size = NX_UNI_FW_MIN_SIZE;
+ } else {
+ val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_MAGIC_OFFSET]);
+ if ((__force u32)val != NETXEN_BDINFO_MAGIC)
+ return -EINVAL;
+
+ min_size = NX_FW_MIN_SIZE;
+ }
- val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_MAGIC_OFFSET]);
- if ((__force u32)val != NETXEN_BDINFO_MAGIC)
+ if (fw->size < min_size)
return -EINVAL;
- val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]);
+ val = nx_get_fw_version(adapter);
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
min_ver = NETXEN_VERSION_CODE(4, 0, 216);
@@ -783,15 +953,15 @@ netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname)
if ((_major(ver) > _NETXEN_NIC_LINUX_MAJOR) || (ver < min_ver)) {
dev_err(&pdev->dev,
"%s: firmware version %d.%d.%d unsupported\n",
- fwname, _major(ver), _minor(ver), _build(ver));
+ fw_name[fw_type], _major(ver), _minor(ver), _build(ver));
return -EINVAL;
}
- val = cpu_to_le32(*(u32 *)&fw->data[NX_BIOS_VERSION_OFFSET]);
+ val = nx_get_bios_version(adapter);
netxen_rom_fast_read(adapter, NX_BIOS_VERSION_OFFSET, (int *)&bios);
if ((__force u32)val != bios) {
dev_err(&pdev->dev, "%s: firmware bios is incompatible\n",
- fwname);
+ fw_name[fw_type]);
return -EINVAL;
}
@@ -802,7 +972,7 @@ netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname)
val = NETXEN_DECODE_VERSION(val);
if (val > ver) {
dev_info(&pdev->dev, "%s: firmware is older than flash\n",
- fwname);
+ fw_name[fw_type]);
return -EINVAL;
}
@@ -810,6 +980,41 @@ netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname)
return 0;
}
+static void
+nx_get_next_fwtype(struct netxen_adapter *adapter)
+{
+ u8 fw_type;
+
+ switch (adapter->fw_type) {
+ case NX_UNKNOWN_ROMIMAGE:
+ fw_type = NX_UNIFIED_ROMIMAGE;
+ break;
+
+ case NX_UNIFIED_ROMIMAGE:
+ if (NX_IS_REVISION_P3P(adapter->ahw.revision_id))
+ fw_type = NX_FLASH_ROMIMAGE;
+ else if (NX_IS_REVISION_P2(adapter->ahw.revision_id))
+ fw_type = NX_P2_MN_ROMIMAGE;
+ else if (netxen_p3_has_mn(adapter))
+ fw_type = NX_P3_MN_ROMIMAGE;
+ else
+ fw_type = NX_P3_CT_ROMIMAGE;
+ break;
+
+ case NX_P3_MN_ROMIMAGE:
+ fw_type = NX_P3_CT_ROMIMAGE;
+ break;
+
+ case NX_P2_MN_ROMIMAGE:
+ case NX_P3_CT_ROMIMAGE:
+ default:
+ fw_type = NX_FLASH_ROMIMAGE;
+ break;
+ }
+
+ adapter->fw_type = fw_type;
+}
+
static int
netxen_p3_has_mn(struct netxen_adapter *adapter)
{
@@ -831,49 +1036,29 @@ netxen_p3_has_mn(struct netxen_adapter *adapter)
void netxen_request_firmware(struct netxen_adapter *adapter)
{
- u8 fw_type;
struct pci_dev *pdev = adapter->pdev;
int rc = 0;
- if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
- fw_type = NX_P2_MN_ROMIMAGE;
- goto request_fw;
- }
+ adapter->fw_type = NX_UNKNOWN_ROMIMAGE;
- fw_type = netxen_p3_has_mn(adapter) ?
- NX_P3_MN_ROMIMAGE : NX_P3_CT_ROMIMAGE;
+next:
+ nx_get_next_fwtype(adapter);
-request_fw:
- rc = request_firmware(&adapter->fw, fw_name[fw_type], &pdev->dev);
- if (rc != 0) {
- if (fw_type == NX_P3_MN_ROMIMAGE) {
- msleep(1);
- fw_type = NX_P3_CT_ROMIMAGE;
- goto request_fw;
- }
-
- fw_type = NX_FLASH_ROMIMAGE;
+ if (adapter->fw_type == NX_FLASH_ROMIMAGE) {
adapter->fw = NULL;
- goto done;
- }
-
- rc = netxen_validate_firmware(adapter, fw_name[fw_type]);
- if (rc != 0) {
- release_firmware(adapter->fw);
-
- if (fw_type == NX_P3_MN_ROMIMAGE) {
+ } else {
+ rc = request_firmware(&adapter->fw,
+ fw_name[adapter->fw_type], &pdev->dev);
+ if (rc != 0)
+ goto next;
+
+ rc = netxen_validate_firmware(adapter);
+ if (rc != 0) {
+ release_firmware(adapter->fw);
msleep(1);
- fw_type = NX_P3_CT_ROMIMAGE;
- goto request_fw;
+ goto next;
}
-
- fw_type = NX_FLASH_ROMIMAGE;
- adapter->fw = NULL;
- goto done;
}
-
-done:
- adapter->fw_type = fw_type;
}
@@ -1508,10 +1693,8 @@ netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ringid,
(rds_ring->num_desc - 1)));
netxen_set_msg_ctxid(msg, adapter->portnum);
netxen_set_msg_opcode(msg, NETXEN_RCV_PRODUCER(ringid));
- read_lock(&adapter->adapter_lock);
- writel(msg, DB_NORMALIZE(adapter,
- NETXEN_RCV_PRODUCER_OFFSET));
- read_unlock(&adapter->adapter_lock);
+ NXWRIO(adapter, DB_NORMALIZE(adapter,
+ NETXEN_RCV_PRODUCER_OFFSET), msg);
}
}
}
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 3bf78dbfbf0..e5d187fce51 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -34,13 +34,18 @@
#include <net/ip.h>
#include <linux/ipv6.h>
#include <linux/inetdevice.h>
+#include <linux/sysfs.h>
-MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver");
+MODULE_DESCRIPTION("QLogic/NetXen (1/10) GbE Converged Ethernet Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(NETXEN_NIC_LINUX_VERSIONID);
+MODULE_FIRMWARE(NX_P2_MN_ROMIMAGE_NAME);
+MODULE_FIRMWARE(NX_P3_CT_ROMIMAGE_NAME);
+MODULE_FIRMWARE(NX_P3_MN_ROMIMAGE_NAME);
+MODULE_FIRMWARE(NX_UNIFIED_ROMIMAGE_NAME);
char netxen_nic_driver_name[] = "netxen_nic";
-static char netxen_nic_driver_string[] = "NetXen Network Driver version "
+static char netxen_nic_driver_string[] = "QLogic/NetXen Network Driver v"
NETXEN_NIC_LINUX_VERSIONID;
static int port_mode = NETXEN_PORT_MODE_AUTO_NEG;
@@ -52,7 +57,8 @@ static int use_msi = 1;
static int use_msi_x = 1;
-/* Local functions to NetXen NIC driver */
+static unsigned long auto_fw_reset = AUTO_FW_RESET_ENABLED;
+
static int __devinit netxen_nic_probe(struct pci_dev *pdev,
const struct pci_device_id *ent);
static void __devexit netxen_nic_remove(struct pci_dev *pdev);
@@ -73,6 +79,8 @@ static void netxen_nic_poll_controller(struct net_device *netdev);
static void netxen_create_sysfs_entries(struct netxen_adapter *adapter);
static void netxen_remove_sysfs_entries(struct netxen_adapter *adapter);
+static void netxen_create_diag_entries(struct netxen_adapter *adapter);
+static void netxen_remove_diag_entries(struct netxen_adapter *adapter);
static int nx_decr_dev_ref_cnt(struct netxen_adapter *adapter);
static int netxen_can_start_firmware(struct netxen_adapter *adapter);
@@ -609,14 +617,12 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
* Set the CRB window to invalid. If any register in window 0 is
* accessed it should set the window to 0 and then reset it to 1.
*/
- adapter->curr_window = 255;
- adapter->ahw.qdr_sn_window = -1;
- adapter->ahw.ddr_mn_window = -1;
+ adapter->ahw.crb_win = -1;
+ adapter->ahw.ocm_win = -1;
/* remap phys address */
mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
mem_len = pci_resource_len(pdev, 0);
- pci_len0 = 0;
/* 128 Meg of memory */
if (mem_len == NETXEN_PCI_128MB_SIZE) {
@@ -625,6 +631,7 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
SECOND_PAGE_GROUP_SIZE);
mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START,
THIRD_PAGE_GROUP_SIZE);
+ pci_len0 = FIRST_PAGE_GROUP_SIZE;
} else if (mem_len == NETXEN_PCI_32MB_SIZE) {
mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE);
mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START -
@@ -637,19 +644,6 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
return -EIO;
}
pci_len0 = mem_len;
-
- adapter->ahw.ddr_mn_window = 0;
- adapter->ahw.qdr_sn_window = 0;
-
- adapter->ahw.mn_win_crb = NETXEN_PCI_CRBSPACE +
- 0x100000 + PCIX_MN_WINDOW + (pci_func * 0x20);
- adapter->ahw.ms_win_crb = NETXEN_PCI_CRBSPACE +
- 0x100000 + PCIX_SN_WINDOW;
- if (pci_func < 4)
- adapter->ahw.ms_win_crb += (pci_func * 0x20);
- else
- adapter->ahw.ms_win_crb +=
- 0xA0 + ((pci_func - 4) * 0x10);
} else {
return -EIO;
}
@@ -663,6 +657,15 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
adapter->ahw.pci_base1 = mem_ptr1;
adapter->ahw.pci_base2 = mem_ptr2;
+ if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) {
+ adapter->ahw.ocm_win_crb = netxen_get_ioaddr(adapter,
+ NETXEN_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(pci_func)));
+
+ } else if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
+ adapter->ahw.ocm_win_crb = netxen_get_ioaddr(adapter,
+ NETXEN_PCIX_PS_REG(PCIE_MN_WINDOW_REG(pci_func)));
+ }
+
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
goto skip_doorbell;
@@ -727,7 +730,8 @@ netxen_check_options(struct netxen_adapter *adapter)
if (adapter->portnum == 0) {
get_brd_name_by_type(adapter->ahw.board_type, brd_name);
- printk(KERN_INFO "NetXen %s Board S/N %s Chip rev 0x%x\n",
+ pr_info("%s: %s Board S/N %s Chip rev 0x%x\n",
+ module_name(THIS_MODULE),
brd_name, serial_num, adapter->ahw.revision_id);
}
@@ -815,11 +819,11 @@ netxen_start_firmware(struct netxen_adapter *adapter)
if (err < 0)
goto err_out;
if (err == 0)
- goto ready;
+ goto wait_init;
if (first_boot != 0x55555555) {
NXWR32(adapter, CRB_CMDPEG_STATE, 0);
- netxen_pinit_from_rom(adapter, 0);
+ netxen_pinit_from_rom(adapter);
msleep(1);
}
@@ -858,9 +862,6 @@ netxen_start_firmware(struct netxen_adapter *adapter)
| (_NETXEN_NIC_LINUX_SUBVERSION);
NXWR32(adapter, CRB_DRIVER_VERSION, val);
-ready:
- NXWR32(adapter, NX_CRB_DEV_STATE, NX_DEV_READY);
-
wait_init:
/* Handshake with the card before we register the devices. */
err = netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
@@ -869,6 +870,8 @@ wait_init:
goto err_out;
}
+ NXWR32(adapter, NX_CRB_DEV_STATE, NX_DEV_READY);
+
nx_update_dma_mask(adapter);
netxen_check_options(adapter);
@@ -1209,16 +1212,10 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
int pci_func_id = PCI_FUNC(pdev->devfn);
uint8_t revision_id;
- if (pdev->class != 0x020000) {
- printk(KERN_DEBUG "NetXen function %d, class %x will not "
- "be enabled.\n",pci_func_id, pdev->class);
- return -ENODEV;
- }
-
if (pdev->revision >= NX_P3_A0 && pdev->revision < NX_P3_B1) {
- printk(KERN_WARNING "NetXen chip revisions between 0x%x-0x%x"
+ pr_warning("%s: chip revisions between 0x%x-0x%x"
"will not be enabled.\n",
- NX_P3_A0, NX_P3_B1);
+ module_name(THIS_MODULE), NX_P3_A0, NX_P3_B1);
return -ENODEV;
}
@@ -1252,7 +1249,9 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
revision_id = pdev->revision;
adapter->ahw.revision_id = revision_id;
- rwlock_init(&adapter->adapter_lock);
+ rwlock_init(&adapter->ahw.crb_lock);
+ spin_lock_init(&adapter->ahw.mem_lock);
+
spin_lock_init(&adapter->tx_clean_lock);
INIT_LIST_HEAD(&adapter->mac_list);
@@ -1282,7 +1281,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = netxen_start_firmware(adapter);
if (err)
- goto err_out_iounmap;
+ goto err_out_decr_ref;
/*
* See if the firmware gave us a virtual-physical port mapping.
@@ -1317,6 +1316,8 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
}
+ netxen_create_diag_entries(adapter);
+
return 0;
err_out_disable_msi:
@@ -1324,6 +1325,7 @@ err_out_disable_msi:
netxen_free_dummy_dma(adapter);
+err_out_decr_ref:
nx_decr_dev_ref_cnt(adapter);
err_out_iounmap:
@@ -1369,6 +1371,8 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
netxen_teardown_intr(adapter);
+ netxen_remove_diag_entries(adapter);
+
netxen_cleanup_pci_map(adapter);
netxen_release_firmware(adapter);
@@ -1449,7 +1453,8 @@ netxen_nic_resume(struct pci_dev *pdev)
if (err)
return err;
- adapter->curr_window = 255;
+ adapter->ahw.crb_win = -1;
+ adapter->ahw.ocm_win = -1;
err = netxen_start_firmware(adapter);
if (err) {
@@ -1927,7 +1932,7 @@ request_reset:
struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev)
{
struct netxen_adapter *adapter = netdev_priv(netdev);
- struct net_device_stats *stats = &adapter->net_stats;
+ struct net_device_stats *stats = &netdev->stats;
memset(stats, 0, sizeof(*stats));
@@ -2184,14 +2189,13 @@ netxen_fwinit_work(struct work_struct *work)
netxen_fwinit_work, 2 * FW_POLL_DELAY);
return;
}
- break;
case NX_DEV_FAILED:
default:
+ nx_incr_dev_ref_cnt(adapter);
break;
}
- nx_incr_dev_ref_cnt(adapter);
clear_bit(__NX_RESETTING, &adapter->state);
}
@@ -2213,18 +2217,23 @@ netxen_detach_work(struct work_struct *work)
status = NXRD32(adapter, NETXEN_PEG_HALT_STATUS1);
- ref_cnt = nx_decr_dev_ref_cnt(adapter);
-
if (status & NX_RCODE_FATAL_ERROR)
- return;
+ goto err_ret;
if (adapter->temp == NX_TEMP_PANIC)
- return;
+ goto err_ret;
+
+ ref_cnt = nx_decr_dev_ref_cnt(adapter);
delay = (ref_cnt == 0) ? 0 : (2 * FW_POLL_DELAY);
adapter->fw_wait_cnt = 0;
netxen_schedule_work(adapter, netxen_fwinit_work, delay);
+
+ return;
+
+err_ret:
+ clear_bit(__NX_RESETTING, &adapter->state);
}
static int
@@ -2263,7 +2272,8 @@ netxen_check_health(struct netxen_adapter *adapter)
dev_info(&netdev->dev, "firmware hang detected\n");
detach:
- if (!test_and_set_bit(__NX_RESETTING, &adapter->state))
+ if ((auto_fw_reset == AUTO_FW_RESET_ENABLED) &&
+ !test_and_set_bit(__NX_RESETTING, &adapter->state))
netxen_schedule_work(adapter, netxen_detach_work, 0);
return 1;
}
@@ -2341,6 +2351,197 @@ static struct device_attribute dev_attr_bridged_mode = {
.store = netxen_store_bridged_mode,
};
+static ssize_t
+netxen_store_diag_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct netxen_adapter *adapter = dev_get_drvdata(dev);
+ unsigned long new;
+
+ if (strict_strtoul(buf, 2, &new))
+ return -EINVAL;
+
+ if (!!new != !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED))
+ adapter->flags ^= NETXEN_NIC_DIAG_ENABLED;
+
+ return len;
+}
+
+static ssize_t
+netxen_show_diag_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct netxen_adapter *adapter = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n",
+ !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED));
+}
+
+static struct device_attribute dev_attr_diag_mode = {
+ .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)},
+ .show = netxen_show_diag_mode,
+ .store = netxen_store_diag_mode,
+};
+
+static int
+netxen_sysfs_validate_crb(struct netxen_adapter *adapter,
+ loff_t offset, size_t size)
+{
+ if (!(adapter->flags & NETXEN_NIC_DIAG_ENABLED))
+ return -EIO;
+
+ if ((size != 4) || (offset & 0x3))
+ return -EINVAL;
+
+ if (offset < NETXEN_PCI_CRBSPACE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t
+netxen_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct netxen_adapter *adapter = dev_get_drvdata(dev);
+ u32 data;
+ int ret;
+
+ ret = netxen_sysfs_validate_crb(adapter, offset, size);
+ if (ret != 0)
+ return ret;
+
+ data = NXRD32(adapter, offset);
+ memcpy(buf, &data, size);
+ return size;
+}
+
+static ssize_t
+netxen_sysfs_write_crb(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct netxen_adapter *adapter = dev_get_drvdata(dev);
+ u32 data;
+ int ret;
+
+ ret = netxen_sysfs_validate_crb(adapter, offset, size);
+ if (ret != 0)
+ return ret;
+
+ memcpy(&data, buf, size);
+ NXWR32(adapter, offset, data);
+ return size;
+}
+
+static int
+netxen_sysfs_validate_mem(struct netxen_adapter *adapter,
+ loff_t offset, size_t size)
+{
+ if (!(adapter->flags & NETXEN_NIC_DIAG_ENABLED))
+ return -EIO;
+
+ if ((size != 8) || (offset & 0x7))
+ return -EIO;
+
+ return 0;
+}
+
+static ssize_t
+netxen_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t offset, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct netxen_adapter *adapter = dev_get_drvdata(dev);
+ u64 data;
+ int ret;
+
+ ret = netxen_sysfs_validate_mem(adapter, offset, size);
+ if (ret != 0)
+ return ret;
+
+ if (adapter->pci_mem_read(adapter, offset, &data))
+ return -EIO;
+
+ memcpy(buf, &data, size);
+
+ return size;
+}
+
+ssize_t netxen_sysfs_write_mem(struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t offset, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct netxen_adapter *adapter = dev_get_drvdata(dev);
+ u64 data;
+ int ret;
+
+ ret = netxen_sysfs_validate_mem(adapter, offset, size);
+ if (ret != 0)
+ return ret;
+
+ memcpy(&data, buf, size);
+
+ if (adapter->pci_mem_write(adapter, offset, data))
+ return -EIO;
+
+ return size;
+}
+
+
+static struct bin_attribute bin_attr_crb = {
+ .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)},
+ .size = 0,
+ .read = netxen_sysfs_read_crb,
+ .write = netxen_sysfs_write_crb,
+};
+
+static struct bin_attribute bin_attr_mem = {
+ .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)},
+ .size = 0,
+ .read = netxen_sysfs_read_mem,
+ .write = netxen_sysfs_write_mem,
+};
+
+#ifdef CONFIG_MODULES
+static ssize_t
+netxen_store_auto_fw_reset(struct module_attribute *mattr,
+ struct module *mod, const char *buf, size_t count)
+
+{
+ unsigned long new;
+
+ if (strict_strtoul(buf, 16, &new))
+ return -EINVAL;
+
+ if ((new == AUTO_FW_RESET_ENABLED) || (new == AUTO_FW_RESET_DISABLED)) {
+ auto_fw_reset = new;
+ return count;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t
+netxen_show_auto_fw_reset(struct module_attribute *mattr,
+ struct module *mod, char *buf)
+
+{
+ if (auto_fw_reset == AUTO_FW_RESET_ENABLED)
+ return sprintf(buf, "enabled\n");
+ else
+ return sprintf(buf, "disabled\n");
+}
+
+static struct module_attribute mod_attr_fw_reset = {
+ .attr = {.name = "auto_fw_reset", .mode = (S_IRUGO | S_IWUSR)},
+ .show = netxen_show_auto_fw_reset,
+ .store = netxen_store_auto_fw_reset,
+};
+#endif
+
static void
netxen_create_sysfs_entries(struct netxen_adapter *adapter)
{
@@ -2366,6 +2567,33 @@ netxen_remove_sysfs_entries(struct netxen_adapter *adapter)
device_remove_file(dev, &dev_attr_bridged_mode);
}
+static void
+netxen_create_diag_entries(struct netxen_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct device *dev;
+
+ dev = &pdev->dev;
+ if (device_create_file(dev, &dev_attr_diag_mode))
+ dev_info(dev, "failed to create diag_mode sysfs entry\n");
+ if (device_create_bin_file(dev, &bin_attr_crb))
+ dev_info(dev, "failed to create crb sysfs entry\n");
+ if (device_create_bin_file(dev, &bin_attr_mem))
+ dev_info(dev, "failed to create mem sysfs entry\n");
+}
+
+
+static void
+netxen_remove_diag_entries(struct netxen_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ struct device *dev = &pdev->dev;
+
+ device_remove_file(dev, &dev_attr_diag_mode);
+ device_remove_bin_file(dev, &bin_attr_crb);
+ device_remove_bin_file(dev, &bin_attr_mem);
+}
+
#ifdef CONFIG_INET
#define is_netxen_netdev(dev) (dev->netdev_ops == &netxen_netdev_ops)
@@ -2518,6 +2746,10 @@ static struct pci_driver netxen_driver = {
static int __init netxen_init_module(void)
{
+#ifdef CONFIG_MODULES
+ struct module *mod = THIS_MODULE;
+#endif
+
printk(KERN_INFO "%s\n", netxen_nic_driver_string);
#ifdef CONFIG_INET
@@ -2525,6 +2757,12 @@ static int __init netxen_init_module(void)
register_inetaddr_notifier(&netxen_inetaddr_cb);
#endif
+#ifdef CONFIG_MODULES
+ if (sysfs_create_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr))
+ printk(KERN_ERR "%s: Failed to create auto_fw_reset "
+ "sysfs entry.", netxen_nic_driver_name);
+#endif
+
return pci_register_driver(&netxen_driver);
}
@@ -2532,6 +2770,12 @@ module_init(netxen_init_module);
static void __exit netxen_exit_module(void)
{
+#ifdef CONFIG_MODULES
+ struct module *mod = THIS_MODULE;
+
+ sysfs_remove_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr);
+#endif
+
pci_unregister_driver(&netxen_driver);
#ifdef CONFIG_INET
diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c
index 462d20f2643..6a87d810e59 100644
--- a/drivers/net/ni5010.c
+++ b/drivers/net/ni5010.c
@@ -377,7 +377,7 @@ static int ni5010_open(struct net_device *dev)
PRINTK2((KERN_DEBUG "%s: entering ni5010_open()\n", dev->name));
- if (request_irq(dev->irq, &ni5010_interrupt, 0, boardname, dev)) {
+ if (request_irq(dev->irq, ni5010_interrupt, 0, boardname, dev)) {
printk(KERN_WARNING "%s: Cannot get irq %#2x\n", dev->name, dev->irq);
return -EAGAIN;
}
diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c
index aad3b370c56..b42f5e522f9 100644
--- a/drivers/net/ni52.c
+++ b/drivers/net/ni52.c
@@ -284,7 +284,7 @@ static int ni52_open(struct net_device *dev)
startrecv586(dev);
ni_enaint();
- ret = request_irq(dev->irq, &ni52_interrupt, 0, dev->name, dev);
+ ret = request_irq(dev->irq, ni52_interrupt, 0, dev->name, dev);
if (ret) {
ni_reset586();
return ret;
@@ -477,8 +477,8 @@ static int __init ni52_probe1(struct net_device *dev, int ioaddr)
for (i = 0; i < ETH_ALEN; i++)
dev->dev_addr[i] = inb(dev->base_addr+i);
- if (dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1
- || dev->dev_addr[2] != NI52_ADDR2) {
+ if (dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1 ||
+ dev->dev_addr[2] != NI52_ADDR2) {
retval = -ENODEV;
goto out;
}
diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c
index 752c2e4d9cf..ae19aafd2c7 100644
--- a/drivers/net/ni65.c
+++ b/drivers/net/ni65.c
@@ -294,7 +294,7 @@ static void ni65_set_performance(struct priv *p)
static int ni65_open(struct net_device *dev)
{
struct priv *p = dev->ml_priv;
- int irqval = request_irq(dev->irq, &ni65_interrupt,0,
+ int irqval = request_irq(dev->irq, ni65_interrupt,0,
cards[p->cardno].cardname,dev);
if (irqval) {
printk(KERN_ERR "%s: unable to get IRQ %d (irqval=%d).\n",
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index d6c7ac68f6e..8ce58c4c7dd 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -45,10 +45,6 @@ MODULE_DESCRIPTION("NIU ethernet driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
-#ifndef DMA_44BIT_MASK
-#define DMA_44BIT_MASK 0x00000fffffffffffULL
-#endif
-
#ifndef readq
static u64 readq(void __iomem *reg)
{
@@ -7855,10 +7851,13 @@ static void niu_get_strings(struct net_device *dev, u32 stringset, u8 *data)
}
}
-static int niu_get_stats_count(struct net_device *dev)
+static int niu_get_sset_count(struct net_device *dev, int stringset)
{
struct niu *np = netdev_priv(dev);
+ if (stringset != ETH_SS_STATS)
+ return -EINVAL;
+
return ((np->flags & NIU_FLAGS_XMAC ?
NUM_XMAC_STAT_KEYS :
NUM_BMAC_STAT_KEYS) +
@@ -7978,7 +7977,7 @@ static const struct ethtool_ops niu_ethtool_ops = {
.get_settings = niu_get_settings,
.set_settings = niu_set_settings,
.get_strings = niu_get_strings,
- .get_stats_count = niu_get_stats_count,
+ .get_sset_count = niu_get_sset_count,
.get_ethtool_stats = niu_get_ethtool_stats,
.phys_id = niu_phys_id,
.get_rxnfc = niu_get_nfc,
@@ -8144,7 +8143,7 @@ static void __devinit niu_vpd_parse_version(struct niu *np)
int i;
for (i = 0; i < len - 5; i++) {
- if (!strncmp(s + i, "FCode ", 5))
+ if (!strncmp(s + i, "FCode ", 6))
break;
}
if (i >= len - 5)
@@ -9915,7 +9914,7 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
PCI_EXP_DEVCTL_RELAX_EN);
pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
- dma_mask = DMA_44BIT_MASK;
+ dma_mask = DMA_BIT_MASK(44);
err = pci_set_dma_mask(pdev, dma_mask);
if (!err) {
dev->features |= NETIF_F_HIGHDMA;
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
index 57fd483dbb1..1f6327d4153 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ns83820.c
@@ -648,8 +648,8 @@ static void phy_intr(struct net_device *ndev)
dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n",
tbisr, tanar, tanlpar);
- if ( (fullduplex = (tanlpar & TANAR_FULL_DUP)
- && (tanar & TANAR_FULL_DUP)) ) {
+ if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) &&
+ (tanar & TANAR_FULL_DUP)) ) {
/* both of us are full duplex */
writel(readl(dev->base + TXCFG)
@@ -661,12 +661,12 @@ static void phy_intr(struct net_device *ndev)
writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
dev->base + GPIOR);
- } else if(((tanlpar & TANAR_HALF_DUP)
- && (tanar & TANAR_HALF_DUP))
- || ((tanlpar & TANAR_FULL_DUP)
- && (tanar & TANAR_HALF_DUP))
- || ((tanlpar & TANAR_HALF_DUP)
- && (tanar & TANAR_FULL_DUP))) {
+ } else if (((tanlpar & TANAR_HALF_DUP) &&
+ (tanar & TANAR_HALF_DUP)) ||
+ ((tanlpar & TANAR_FULL_DUP) &&
+ (tanar & TANAR_HALF_DUP)) ||
+ ((tanlpar & TANAR_HALF_DUP) &&
+ (tanar & TANAR_FULL_DUP))) {
/* one or both of us are half duplex */
writel((readl(dev->base + TXCFG)
@@ -720,16 +720,16 @@ static void phy_intr(struct net_device *ndev)
newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN;
- if (newlinkstate & LINK_UP
- && dev->linkstate != newlinkstate) {
+ if (newlinkstate & LINK_UP &&
+ dev->linkstate != newlinkstate) {
netif_start_queue(ndev);
netif_wake_queue(ndev);
printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n",
ndev->name,
speeds[speed],
fullduplex ? "full" : "half");
- } else if (newlinkstate & LINK_DOWN
- && dev->linkstate != newlinkstate) {
+ } else if (newlinkstate & LINK_DOWN &&
+ dev->linkstate != newlinkstate) {
netif_stop_queue(ndev);
printk(KERN_INFO "%s: link now down.\n", ndev->name);
}
diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c
index c254a7f5b9f..1673eb045e1 100644
--- a/drivers/net/pasemi_mac.c
+++ b/drivers/net/pasemi_mac.c
@@ -1216,7 +1216,7 @@ static int pasemi_mac_open(struct net_device *dev)
snprintf(mac->tx_irq_name, sizeof(mac->tx_irq_name), "%s tx",
dev->name);
- ret = request_irq(mac->tx->chan.irq, &pasemi_mac_tx_intr, IRQF_DISABLED,
+ ret = request_irq(mac->tx->chan.irq, pasemi_mac_tx_intr, IRQF_DISABLED,
mac->tx_irq_name, mac->tx);
if (ret) {
dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n",
@@ -1227,7 +1227,7 @@ static int pasemi_mac_open(struct net_device *dev)
snprintf(mac->rx_irq_name, sizeof(mac->rx_irq_name), "%s rx",
dev->name);
- ret = request_irq(mac->rx->chan.irq, &pasemi_mac_rx_intr, IRQF_DISABLED,
+ ret = request_irq(mac->rx->chan.irq, pasemi_mac_rx_intr, IRQF_DISABLED,
mac->rx_irq_name, mac->rx);
if (ret) {
dev_err(&mac->pdev->dev, "request_irq of irq %d failed: %d\n",
diff --git a/drivers/net/pasemi_mac_ethtool.c b/drivers/net/pasemi_mac_ethtool.c
index 28a86224879..fefa79e34b9 100644
--- a/drivers/net/pasemi_mac_ethtool.c
+++ b/drivers/net/pasemi_mac_ethtool.c
@@ -77,6 +77,19 @@ pasemi_mac_ethtool_get_settings(struct net_device *netdev,
return phy_ethtool_gset(phydev, cmd);
}
+static int
+pasemi_mac_ethtool_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct pasemi_mac *mac = netdev_priv(netdev);
+ struct phy_device *phydev = mac->phydev;
+
+ if (!phydev)
+ return -EOPNOTSUPP;
+
+ return phy_ethtool_sset(phydev, cmd);
+}
+
static void
pasemi_mac_ethtool_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *drvinfo)
@@ -150,6 +163,7 @@ static void pasemi_mac_get_strings(struct net_device *netdev, u32 stringset,
const struct ethtool_ops pasemi_mac_ethtool_ops = {
.get_settings = pasemi_mac_ethtool_get_settings,
+ .set_settings = pasemi_mac_ethtool_set_settings,
.get_drvinfo = pasemi_mac_ethtool_get_drvinfo,
.get_msglevel = pasemi_mac_ethtool_get_msglevel,
.set_msglevel = pasemi_mac_ethtool_set_msglevel,
diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c
index 0c44b48f138..480af402aff 100644
--- a/drivers/net/pci-skeleton.c
+++ b/drivers/net/pci-skeleton.c
@@ -1225,8 +1225,8 @@ static void netdrv_timer (unsigned long data)
mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
if (!tp->duplex_lock && mii_lpa != 0xffff) {
- int duplex = (mii_lpa & LPA_100FULL)
- || (mii_lpa & 0x01C0) == 0x0040;
+ int duplex = ((mii_lpa & LPA_100FULL) ||
+ (mii_lpa & 0x01C0) == 0x0040);
if (tp->full_duplex != duplex) {
tp->full_duplex = duplex;
printk (KERN_INFO
@@ -1612,8 +1612,8 @@ static void netdrv_weird_interrupt (struct net_device *dev,
(tp->drv_flags & HAS_LNK_CHNG)) {
/* Really link-change on new chips. */
int lpar = NETDRV_R16 (NWayLPAR);
- int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040
- || tp->duplex_lock;
+ int duplex = ((lpar & 0x0100) || (lpar & 0x01C0) == 0x0040 ||
+ tp->duplex_lock);
if (tp->full_duplex != duplex) {
tp->full_duplex = duplex;
NETDRV_W8 (Cfg9346, Cfg9346_Unlock);
@@ -1820,8 +1820,8 @@ static void netdrv_set_rx_mode (struct net_device *dev)
AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
AcceptAllPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter perfectly -- accept all multicasts. */
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index b58965a2b3a..17a27225cc9 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -118,14 +118,6 @@ INT_MODULE_PARM(full_duplex, 0);
/* Autodetect link polarity reversal? */
INT_MODULE_PARM(auto_polarity, 1);
-#ifdef PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"3c574_cs.c 1.65ac1 2003/04/07 Donald Becker/David Hinds, becker@scyld.com.\n";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -278,7 +270,7 @@ static int tc574_probe(struct pcmcia_device *link)
struct el3_private *lp;
struct net_device *dev;
- DEBUG(0, "3c574_attach()\n");
+ dev_dbg(&link->dev, "3c574_attach()\n");
/* Create the PC card device object. */
dev = alloc_etherdev(sizeof(struct el3_private));
@@ -291,10 +283,8 @@ static int tc574_probe(struct pcmcia_device *link)
spin_lock_init(&lp->window_lock);
link->io.NumPorts1 = 32;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = &el3_interrupt;
- link->irq.Instance = dev;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.ConfigIndex = 1;
@@ -319,7 +309,7 @@ static void tc574_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "3c574_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "3c574_detach()\n");
if (link->dev_node)
unregister_netdev(dev);
@@ -335,26 +325,23 @@ static void tc574_detach(struct pcmcia_device *link)
ethernet device available to the system.
*/
-#define CS_CHECK(fn, ret) \
- do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
static int tc574_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
struct el3_private *lp = netdev_priv(dev);
- tuple_t tuple;
- __le16 buf[32];
- int last_fn, last_ret, i, j;
+ int ret, i, j;
unsigned int ioaddr;
__be16 *phys_addr;
char *cardname;
__u32 config;
+ u8 *buf;
+ size_t len;
phys_addr = (__be16 *)dev->dev_addr;
- DEBUG(0, "3c574_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "3c574_config()\n");
link->io.IOAddrLines = 16;
for (i = j = 0; j < 0x400; j += 0x20) {
@@ -363,12 +350,16 @@ static int tc574_config(struct pcmcia_device *link)
if (i == 0)
break;
}
- if (i != 0) {
- cs_error(link, RequestIO, i);
+ if (i != 0)
+ goto failed;
+
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
@@ -378,16 +369,14 @@ static int tc574_config(struct pcmcia_device *link)
/* The 3c574 normally uses an EEPROM for configuration info, including
the hardware address. The future products may include a modem chip
and put the address in the CIS. */
- tuple.Attributes = 0;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = 0x88;
- if (pcmcia_get_first_tuple(link, &tuple) == 0) {
- pcmcia_get_tuple_data(link, &tuple);
+
+ len = pcmcia_get_tuple(link, 0x88, &buf);
+ if (buf && len >= 6) {
for (i = 0; i < 3; i++)
- phys_addr[i] = htons(le16_to_cpu(buf[i]));
+ phys_addr[i] = htons(le16_to_cpu(buf[i * 2]));
+ kfree(buf);
} else {
+ kfree(buf); /* 0 < len < 6 */
EL3WINDOW(0);
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i + 10));
@@ -435,7 +424,8 @@ static int tc574_config(struct pcmcia_device *link)
mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
if (mii_status != 0xffff) {
lp->phys = phy & 0x1f;
- DEBUG(0, " MII transceiver at index %d, status %x.\n",
+ dev_dbg(&link->dev, " MII transceiver at "
+ "index %d, status %x.\n",
phy, mii_status);
if ((mii_status & 0x0040) == 0)
mii_preamble_required = 1;
@@ -457,7 +447,7 @@ static int tc574_config(struct pcmcia_device *link)
}
link->dev_node = &lp->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n");
@@ -478,8 +468,6 @@ static int tc574_config(struct pcmcia_device *link)
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
tc574_release(link);
return -ENODEV;
@@ -738,7 +726,7 @@ static int el3_open(struct net_device *dev)
lp->media.expires = jiffies + HZ;
add_timer(&lp->media);
- DEBUG(2, "%s: opened, status %4.4x.\n",
+ dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
dev->name, inw(dev->base_addr + EL3_STATUS));
return 0;
@@ -772,7 +760,7 @@ static void pop_tx_status(struct net_device *dev)
if (tx_status & 0x30)
tc574_wait_for_completion(dev, TxReset);
if (tx_status & 0x38) {
- DEBUG(1, "%s: transmit error: status 0x%02x\n",
+ pr_debug("%s: transmit error: status 0x%02x\n",
dev->name, tx_status);
outw(TxEnable, ioaddr + EL3_CMD);
dev->stats.tx_aborted_errors++;
@@ -788,7 +776,7 @@ static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
struct el3_private *lp = netdev_priv(dev);
unsigned long flags;
- DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
+ pr_debug("%s: el3_start_xmit(length = %ld) called, "
"status %4.4x.\n", dev->name, (long)skb->len,
inw(ioaddr + EL3_STATUS));
@@ -827,7 +815,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
return IRQ_NONE;
ioaddr = dev->base_addr;
- DEBUG(3, "%s: interrupt, status %4.4x.\n",
+ pr_debug("%s: interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS));
spin_lock(&lp->window_lock);
@@ -836,7 +824,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
(IntLatch | RxComplete | RxEarly | StatsFull)) {
if (!netif_device_present(dev) ||
((status & 0xe000) != 0x2000)) {
- DEBUG(1, "%s: Interrupt from dead card\n", dev->name);
+ pr_debug("%s: Interrupt from dead card\n", dev->name);
break;
}
@@ -846,7 +834,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
work_budget = el3_rx(dev, work_budget);
if (status & TxAvailable) {
- DEBUG(3, " TX room bit was handled.\n");
+ pr_debug(" TX room bit was handled.\n");
/* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue(dev);
@@ -886,7 +874,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
}
if (--work_budget < 0) {
- DEBUG(0, "%s: Too much work in interrupt, "
+ pr_debug("%s: Too much work in interrupt, "
"status %4.4x.\n", dev->name, status);
/* Clear all interrupts */
outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
@@ -896,7 +884,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
}
- DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
+ pr_debug("%s: exiting interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS));
spin_unlock(&lp->window_lock);
@@ -1003,7 +991,7 @@ static void update_stats(struct net_device *dev)
unsigned int ioaddr = dev->base_addr;
u8 rx, tx, up;
- DEBUG(2, "%s: updating the statistics.\n", dev->name);
+ pr_debug("%s: updating the statistics.\n", dev->name);
if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
return;
@@ -1039,7 +1027,7 @@ static int el3_rx(struct net_device *dev, int worklimit)
unsigned int ioaddr = dev->base_addr;
short rx_status;
- DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&
worklimit > 0) {
@@ -1061,7 +1049,7 @@ static int el3_rx(struct net_device *dev, int worklimit)
skb = dev_alloc_skb(pkt_len+5);
- DEBUG(3, " Receiving packet size %d status %4.4x.\n",
+ pr_debug(" Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
skb_reserve(skb, 2);
@@ -1072,7 +1060,7 @@ static int el3_rx(struct net_device *dev, int worklimit)
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
- DEBUG(1, "%s: couldn't allocate a sk_buff of"
+ pr_debug("%s: couldn't allocate a sk_buff of"
" size %d.\n", dev->name, pkt_len);
dev->stats.rx_dropped++;
}
@@ -1101,7 +1089,7 @@ static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
struct mii_ioctl_data *data = if_mii(rq);
int phy = lp->phys & 0x1f;
- DEBUG(2, "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
+ pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
dev->name, rq->ifr_ifrn.ifrn_name, cmd,
data->phy_id, data->reg_num, data->val_in, data->val_out);
@@ -1178,7 +1166,7 @@ static int el3_close(struct net_device *dev)
struct el3_private *lp = netdev_priv(dev);
struct pcmcia_device *link = lp->p_dev;
- DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+ dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
if (pcmcia_dev_present(link)) {
unsigned long flags;
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index 569fb06793c..6f8d7e2e592 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -130,14 +130,6 @@ MODULE_LICENSE("GPL");
/* Special hook for setting if_port when module is loaded */
INT_MODULE_PARM(if_port, 0);
-#ifdef PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -189,7 +181,7 @@ static int tc589_probe(struct pcmcia_device *link)
struct el3_private *lp;
struct net_device *dev;
- DEBUG(0, "3c589_attach()\n");
+ dev_dbg(&link->dev, "3c589_attach()\n");
/* Create new ethernet device */
dev = alloc_etherdev(sizeof(struct el3_private));
@@ -202,10 +194,8 @@ static int tc589_probe(struct pcmcia_device *link)
spin_lock_init(&lp->lock);
link->io.NumPorts1 = 16;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = &el3_interrupt;
- link->irq.Instance = dev;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.ConfigIndex = 1;
@@ -231,7 +221,7 @@ static void tc589_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "3c589_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "3c589_detach\n");
if (link->dev_node)
unregister_netdev(dev);
@@ -249,29 +239,20 @@ static void tc589_detach(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int tc589_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
struct el3_private *lp = netdev_priv(dev);
- tuple_t tuple;
- __le16 buf[32];
__be16 *phys_addr;
- int last_fn, last_ret, i, j, multi = 0, fifo;
+ int ret, i, j, multi = 0, fifo;
unsigned int ioaddr;
char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+ u8 *buf;
+ size_t len;
- DEBUG(0, "3c589_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "3c589_config\n");
phys_addr = (__be16 *)dev->dev_addr;
- tuple.Attributes = 0;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- tuple.Attributes = TUPLE_RETURN_COMMON;
-
/* Is this a 3c562? */
if (link->manf_id != MANFID_3COM)
printk(KERN_INFO "3c589_cs: hmmm, is this really a "
@@ -287,12 +268,16 @@ static int tc589_config(struct pcmcia_device *link)
if (i == 0)
break;
}
- if (i != 0) {
- cs_error(link, RequestIO, i);
+ if (i != 0)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
@@ -301,12 +286,13 @@ static int tc589_config(struct pcmcia_device *link)
/* The 3c589 has an extra EEPROM for configuration info, including
the hardware address. The 3c562 puts the address in the CIS. */
- tuple.DesiredTuple = 0x88;
- if (pcmcia_get_first_tuple(link, &tuple) == 0) {
- pcmcia_get_tuple_data(link, &tuple);
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(le16_to_cpu(buf[i]));
+ len = pcmcia_get_tuple(link, 0x88, &buf);
+ if (buf && len >= 6) {
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(le16_to_cpu(buf[i*2]));
+ kfree(buf);
} else {
+ kfree(buf); /* 0 < len < 6 */
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
if (phys_addr[0] == htons(0x6060)) {
@@ -328,7 +314,7 @@ static int tc589_config(struct pcmcia_device *link)
printk(KERN_ERR "3c589_cs: invalid if_port requested\n");
link->dev_node = &lp->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
printk(KERN_ERR "3c589_cs: register_netdev() failed\n");
@@ -347,8 +333,6 @@ static int tc589_config(struct pcmcia_device *link)
if_names[dev->if_port]);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
tc589_release(link);
return -ENODEV;
@@ -511,24 +495,8 @@ static void netdev_get_drvinfo(struct net_device *dev,
sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr);
}
-#ifdef PCMCIA_DEBUG
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
- return pc_debug;
-}
-
-static void netdev_set_msglevel(struct net_device *dev, u32 level)
-{
- pc_debug = level;
-}
-#endif /* PCMCIA_DEBUG */
-
static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
-#ifdef PCMCIA_DEBUG
- .get_msglevel = netdev_get_msglevel,
- .set_msglevel = netdev_set_msglevel,
-#endif /* PCMCIA_DEBUG */
};
static int el3_config(struct net_device *dev, struct ifmap *map)
@@ -563,7 +531,7 @@ static int el3_open(struct net_device *dev)
lp->media.expires = jiffies + HZ;
add_timer(&lp->media);
- DEBUG(1, "%s: opened, status %4.4x.\n",
+ dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
dev->name, inw(dev->base_addr + EL3_STATUS));
return 0;
@@ -596,7 +564,7 @@ static void pop_tx_status(struct net_device *dev)
if (tx_status & 0x30)
tc589_wait_for_completion(dev, TxReset);
if (tx_status & 0x38) {
- DEBUG(1, "%s: transmit error: status 0x%02x\n",
+ pr_debug("%s: transmit error: status 0x%02x\n",
dev->name, tx_status);
outw(TxEnable, ioaddr + EL3_CMD);
dev->stats.tx_aborted_errors++;
@@ -612,7 +580,7 @@ static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
struct el3_private *priv = netdev_priv(dev);
unsigned long flags;
- DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
+ pr_debug("%s: el3_start_xmit(length = %ld) called, "
"status %4.4x.\n", dev->name, (long)skb->len,
inw(ioaddr + EL3_STATUS));
@@ -654,14 +622,14 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
ioaddr = dev->base_addr;
- DEBUG(3, "%s: interrupt, status %4.4x.\n",
+ pr_debug("%s: interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS));
spin_lock(&lp->lock);
while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | StatsFull)) {
if ((status & 0xe000) != 0x2000) {
- DEBUG(1, "%s: interrupt from dead card\n", dev->name);
+ pr_debug("%s: interrupt from dead card\n", dev->name);
handled = 0;
break;
}
@@ -670,7 +638,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
el3_rx(dev);
if (status & TxAvailable) {
- DEBUG(3, " TX room bit was handled.\n");
+ pr_debug(" TX room bit was handled.\n");
/* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue(dev);
@@ -722,7 +690,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
lp->last_irq = jiffies;
spin_unlock(&lp->lock);
- DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
+ pr_debug("%s: exiting interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS));
return IRQ_RETVAL(handled);
}
@@ -833,7 +801,7 @@ static void update_stats(struct net_device *dev)
{
unsigned int ioaddr = dev->base_addr;
- DEBUG(2, "%s: updating the statistics.\n", dev->name);
+ pr_debug("%s: updating the statistics.\n", dev->name);
/* Turn off statistics updates while reading. */
outw(StatsDisable, ioaddr + EL3_CMD);
/* Switch to the stats window, and read everything. */
@@ -861,7 +829,7 @@ static int el3_rx(struct net_device *dev)
int worklimit = 32;
short rx_status;
- DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
worklimit > 0) {
@@ -883,7 +851,7 @@ static int el3_rx(struct net_device *dev)
skb = dev_alloc_skb(pkt_len+5);
- DEBUG(3, " Receiving packet size %d status %4.4x.\n",
+ pr_debug(" Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
skb_reserve(skb, 2);
@@ -894,7 +862,7 @@ static int el3_rx(struct net_device *dev)
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
- DEBUG(1, "%s: couldn't allocate a sk_buff of"
+ pr_debug("%s: couldn't allocate a sk_buff of"
" size %d.\n", dev->name, pkt_len);
dev->stats.rx_dropped++;
}
@@ -935,7 +903,7 @@ static int el3_close(struct net_device *dev)
struct pcmcia_device *link = lp->p_dev;
unsigned int ioaddr = dev->base_addr;
- DEBUG(1, "%s: shutting down ethercard.\n", dev->name);
+ dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
if (pcmcia_dev_present(link)) {
/* Turn off statistics ASAP. We update dev->stats below. */
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index 3131a59a8d3..81bafd57847 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -75,16 +75,6 @@ MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
MODULE_LICENSE("GPL");
-#ifdef PCMCIA_DEBUG
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"axnet_cs.c 1.28 2002/06/29 06:27:37 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -167,7 +157,7 @@ static int axnet_probe(struct pcmcia_device *link)
struct net_device *dev;
struct ei_device *ei_local;
- DEBUG(0, "axnet_attach()\n");
+ dev_dbg(&link->dev, "axnet_attach()\n");
dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(axnet_dev_t));
if (!dev)
@@ -180,7 +170,6 @@ static int axnet_probe(struct pcmcia_device *link)
info->p_dev = link;
link->priv = dev;
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -205,7 +194,7 @@ static void axnet_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "axnet_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link);
if (link->dev_node)
unregister_netdev(dev);
@@ -272,9 +261,6 @@ static int get_prom(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int try_io_port(struct pcmcia_device *link)
{
int j, ret;
@@ -341,26 +327,29 @@ static int axnet_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
axnet_dev_t *info = PRIV(dev);
- int i, j, j2, last_ret, last_fn;
+ int i, j, j2, ret;
- DEBUG(0, "axnet_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "axnet_config(0x%p)\n", link);
/* don't trust the CIS on this; Linksys got it wrong */
link->conf.Present = 0x63;
- last_ret = pcmcia_loop_config(link, axnet_configcheck, NULL);
- if (last_ret != 0) {
- cs_error(link, RequestIO, last_ret);
+ ret = pcmcia_loop_config(link, axnet_configcheck, NULL);
+ if (ret != 0)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
if (link->io.NumPorts2 == 8) {
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
}
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
+
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
@@ -410,7 +399,7 @@ static int axnet_config(struct pcmcia_device *link)
info->phy_id = (i < 32) ? i : -1;
link->dev_node = &info->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n");
@@ -426,14 +415,12 @@ static int axnet_config(struct pcmcia_device *link)
dev->base_addr, dev->irq,
dev->dev_addr);
if (info->phy_id != -1) {
- DEBUG(0, " MII transceiver at index %d, status %x.\n", info->phy_id, j);
+ dev_dbg(&link->dev, " MII transceiver at index %d, status %x.\n", info->phy_id, j);
} else {
printk(KERN_NOTICE " No MII transceivers found!\n");
}
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
axnet_release(link);
return -ENODEV;
@@ -543,7 +530,7 @@ static int axnet_open(struct net_device *dev)
struct pcmcia_device *link = info->p_dev;
unsigned int nic_base = dev->base_addr;
- DEBUG(2, "axnet_open('%s')\n", dev->name);
+ dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name);
if (!pcmcia_dev_present(link))
return -ENODEV;
@@ -572,7 +559,7 @@ static int axnet_close(struct net_device *dev)
axnet_dev_t *info = PRIV(dev);
struct pcmcia_device *link = info->p_dev;
- DEBUG(2, "axnet_close('%s')\n", dev->name);
+ dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name);
ax_close(dev);
free_irq(dev->irq, dev);
@@ -741,10 +728,8 @@ static void block_input(struct net_device *dev, int count,
int xfer_count = count;
char *buf = skb->data;
-#ifdef PCMCIA_DEBUG
if ((ei_debug > 4) && (count != 4))
- printk(KERN_DEBUG "%s: [bi=%d]\n", dev->name, count+4);
-#endif
+ pr_debug("%s: [bi=%d]\n", dev->name, count+4);
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
@@ -762,10 +747,7 @@ static void block_output(struct net_device *dev, int count,
{
unsigned int nic_base = dev->base_addr;
-#ifdef PCMCIA_DEBUG
- if (ei_debug > 4)
- printk(KERN_DEBUG "%s: [bo=%d]\n", dev->name, count);
-#endif
+ pr_debug("%s: [bo=%d]\n", dev->name, count);
/* Round the count up for word writes. Do we need to do this?
What effect will an odd byte count have on the 8390?
@@ -1232,8 +1214,8 @@ static irqreturn_t ax_interrupt(int irq, void *dev_id)
ei_local->irqlock = 1;
/* !!Assumption!! -- we stay in page 0. Don't break this. */
- while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
- && ++nr_serviced < MAX_SERVICE)
+ while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 &&
+ ++nr_serviced < MAX_SERVICE)
{
if (!netif_running(dev) || (interrupts == 0xff)) {
if (ei_debug > 1)
diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c
index 7b5c77b7bd2..21d9c9d815d 100644
--- a/drivers/net/pcmcia/com20020_cs.c
+++ b/drivers/net/pcmcia/com20020_cs.c
@@ -53,11 +53,7 @@
#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
-#ifdef PCMCIA_DEBUG
-
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#ifdef DEBUG
static void regdump(struct net_device *dev)
{
@@ -92,7 +88,6 @@ static void regdump(struct net_device *dev)
#else
-#define DEBUG(n, args...) do { } while (0)
static inline void regdump(struct net_device *dev) { }
#endif
@@ -144,7 +139,7 @@ static int com20020_probe(struct pcmcia_device *p_dev)
struct net_device *dev;
struct arcnet_local *lp;
- DEBUG(0, "com20020_attach()\n");
+ dev_dbg(&p_dev->dev, "com20020_attach()\n");
/* Create new network device */
info = kzalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
@@ -169,11 +164,10 @@ static int com20020_probe(struct pcmcia_device *p_dev)
p_dev->io.NumPorts1 = 16;
p_dev->io.IOAddrLines = 16;
p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
p_dev->conf.Attributes = CONF_ENABLE_IRQ;
p_dev->conf.IntType = INT_MEMORY_AND_IO;
- p_dev->irq.Instance = info->dev = dev;
+ info->dev = dev;
p_dev->priv = info;
return com20020_config(p_dev);
@@ -198,12 +192,12 @@ static void com20020_detach(struct pcmcia_device *link)
struct com20020_dev_t *info = link->priv;
struct net_device *dev = info->dev;
- DEBUG(1,"detach...\n");
+ dev_dbg(&link->dev, "detach...\n");
- DEBUG(0, "com20020_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "com20020_detach\n");
if (link->dev_node) {
- DEBUG(1,"unregister...\n");
+ dev_dbg(&link->dev, "unregister...\n");
unregister_netdev(dev);
@@ -218,16 +212,16 @@ static void com20020_detach(struct pcmcia_device *link)
com20020_release(link);
/* Unlink device structure, free bits */
- DEBUG(1,"unlinking...\n");
+ dev_dbg(&link->dev, "unlinking...\n");
if (link->priv)
{
dev = info->dev;
if (dev)
{
- DEBUG(1,"kfree...\n");
+ dev_dbg(&link->dev, "kfree...\n");
free_netdev(dev);
}
- DEBUG(1,"kfree2...\n");
+ dev_dbg(&link->dev, "kfree2...\n");
kfree(info);
}
@@ -241,25 +235,22 @@ static void com20020_detach(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int com20020_config(struct pcmcia_device *link)
{
struct arcnet_local *lp;
com20020_dev_t *info;
struct net_device *dev;
- int i, last_ret, last_fn;
+ int i, ret;
int ioaddr;
info = link->priv;
dev = info->dev;
- DEBUG(1,"config...\n");
+ dev_dbg(&link->dev, "config...\n");
- DEBUG(0, "com20020_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "com20020_config\n");
- DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
+ dev_dbg(&link->dev, "baseport1 is %Xh\n", link->io.BasePort1);
i = -ENODEV;
if (!link->io.BasePort1)
{
@@ -276,26 +267,27 @@ static int com20020_config(struct pcmcia_device *link)
if (i != 0)
{
- DEBUG(1,"arcnet: requestIO failed totally!\n");
+ dev_dbg(&link->dev, "requestIO failed totally!\n");
goto failed;
}
ioaddr = dev->base_addr = link->io.BasePort1;
- DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
+ dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr);
- DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
- link->irq.AssignedIRQ,
- link->irq.IRQInfo1, link->irq.IRQInfo2);
+ dev_dbg(&link->dev, "request IRQ %d\n",
+ link->irq.AssignedIRQ);
i = pcmcia_request_irq(link, &link->irq);
if (i != 0)
{
- DEBUG(1,"arcnet: requestIRQ failed totally!\n");
+ dev_dbg(&link->dev, "requestIRQ failed totally!\n");
goto failed;
}
dev->irq = link->irq.AssignedIRQ;
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
if (com20020_check(dev))
{
@@ -308,26 +300,25 @@ static int com20020_config(struct pcmcia_device *link)
lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
link->dev_node = &info->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
i = com20020_found(dev, 0); /* calls register_netdev */
if (i != 0) {
- DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
+ dev_printk(KERN_NOTICE, &link->dev,
+ "com20020_cs: com20020_found() failed\n");
link->dev_node = NULL;
goto failed;
}
strcpy(info->node.dev_name, dev->name);
- DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
+ dev_dbg(&link->dev,KERN_INFO "%s: port %#3lx, irq %d\n",
dev->name, dev->base_addr, dev->irq);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
- DEBUG(1,"com20020_config failed...\n");
+ dev_dbg(&link->dev, "com20020_config failed...\n");
com20020_release(link);
return -ENODEV;
} /* com20020_config */
@@ -342,7 +333,7 @@ failed:
static void com20020_release(struct pcmcia_device *link)
{
- DEBUG(0, "com20020_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "com20020_release\n");
pcmcia_disable_device(link);
}
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
index 7e01fbdb87e..8ad8384fc1c 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -72,13 +72,6 @@ MODULE_LICENSE("GPL");
/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */
INT_MODULE_PARM(sram_config, 0);
-#ifdef PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = DRV_NAME ".c " DRV_VERSION " 2002/03/23";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
/*
@@ -245,7 +238,7 @@ static int fmvj18x_probe(struct pcmcia_device *link)
local_info_t *lp;
struct net_device *dev;
- DEBUG(0, "fmvj18x_attach()\n");
+ dev_dbg(&link->dev, "fmvj18x_attach()\n");
/* Make up a FMVJ18x specific data structure */
dev = alloc_etherdev(sizeof(local_info_t));
@@ -262,10 +255,8 @@ static int fmvj18x_probe(struct pcmcia_device *link)
link->io.IOAddrLines = 5;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
- link->irq.Handler = &fjn_interrupt;
- link->irq.Instance = dev;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ link->irq.Handler = fjn_interrupt;
/* General socket configuration */
link->conf.Attributes = CONF_ENABLE_IRQ;
@@ -285,7 +276,7 @@ static void fmvj18x_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "fmvj18x_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "fmvj18x_detach\n");
if (link->dev_node)
unregister_netdev(dev);
@@ -297,9 +288,6 @@ static void fmvj18x_detach(struct pcmcia_device *link)
/*====================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int mfc_try_io_port(struct pcmcia_device *link)
{
int i, ret;
@@ -341,39 +329,44 @@ static int ungermann_try_io_port(struct pcmcia_device *link)
return ret; /* RequestIO failed */
}
+static int fmvj18x_ioprobe(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ return 0; /* strange, but that's what the code did already before... */
+}
+
static int fmvj18x_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
local_info_t *lp = netdev_priv(dev);
- tuple_t tuple;
- cisparse_t parse;
- u_short buf[32];
- int i, last_fn = 0, last_ret = 0, ret;
+ int i, ret;
unsigned int ioaddr;
cardtype_t cardtype;
char *card_name = "unknown";
- u_char *node_id;
+ u8 *buf;
+ size_t len;
+ u_char buggybuf[32];
+
+ dev_dbg(&link->dev, "fmvj18x_config\n");
- DEBUG(0, "fmvj18x_config(0x%p)\n", link);
+ len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf);
+ kfree(buf);
- tuple.TupleData = (u_char *)buf;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_FUNCE;
- tuple.TupleOffset = 0;
- if (pcmcia_get_first_tuple(link, &tuple) == 0) {
+ if (len) {
/* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
- CS_CHECK(ParseTuple, pcmcia_parse_tuple(&tuple, &parse));
- link->conf.ConfigIndex = parse.cftable_entry.index;
+ ret = pcmcia_loop_config(link, fmvj18x_ioprobe, NULL);
+ if (ret != 0)
+ goto failed;
+
switch (link->manf_id) {
case MANFID_TDK:
cardtype = TDK;
- if (link->card_id == PRODID_TDK_GN3410
- || link->card_id == PRODID_TDK_NP9610
- || link->card_id == PRODID_TDK_MN3200) {
+ if (link->card_id == PRODID_TDK_GN3410 ||
+ link->card_id == PRODID_TDK_NP9610 ||
+ link->card_id == PRODID_TDK_MN3200) {
/* MultiFunction Card */
link->conf.ConfigBase = 0x800;
link->conf.ConfigIndex = 0x47;
@@ -433,17 +426,24 @@ static int fmvj18x_config(struct pcmcia_device *link)
if (link->io.NumPorts2 != 0) {
link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+ IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
ret = mfc_try_io_port(link);
- if (ret != 0) goto cs_failed;
+ if (ret != 0) goto failed;
} else if (cardtype == UNGERMANN) {
ret = ungermann_try_io_port(link);
- if (ret != 0) goto cs_failed;
+ if (ret != 0) goto failed;
} else {
- CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
+ ret = pcmcia_request_io(link, &link->io);
+ if (ret)
+ goto failed;
}
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
+
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
@@ -474,21 +474,21 @@ static int fmvj18x_config(struct pcmcia_device *link)
case CONTEC:
case NEC:
case KME:
- tuple.DesiredTuple = CISTPL_FUNCE;
- tuple.TupleOffset = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- tuple.TupleOffset = 0;
- CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
if (cardtype == MBH10304) {
- /* MBH10304's CIS_FUNCE is corrupted */
- node_id = &(tuple.TupleData[5]);
card_name = "FMV-J182";
- } else {
- while (tuple.TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID ) {
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
- CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
+
+ len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf);
+ if (len < 11) {
+ kfree(buf);
+ goto failed;
}
- node_id = &(tuple.TupleData[2]);
+ /* Read MACID from CIS */
+ for (i = 5; i < 11; i++)
+ dev->dev_addr[i] = buf[i];
+ kfree(buf);
+ } else {
+ if (pcmcia_get_mac_from_cis(link, dev))
+ goto failed;
if( cardtype == TDK ) {
card_name = "TDK LAK-CD021";
} else if( cardtype == LA501 ) {
@@ -501,9 +501,6 @@ static int fmvj18x_config(struct pcmcia_device *link)
card_name = "C-NET(PC)C";
}
}
- /* Read MACID from CIS */
- for (i = 0; i < 6; i++)
- dev->dev_addr[i] = node_id[i];
break;
case UNGERMANN:
/* Read MACID from register */
@@ -513,12 +510,12 @@ static int fmvj18x_config(struct pcmcia_device *link)
break;
case XXX10304:
/* Read MACID from Buggy CIS */
- if (fmvj18x_get_hwinfo(link, tuple.TupleData) == -1) {
+ if (fmvj18x_get_hwinfo(link, buggybuf) == -1) {
printk(KERN_NOTICE "fmvj18x_cs: unable to read hardware net address.\n");
goto failed;
}
for (i = 0 ; i < 6; i++) {
- dev->dev_addr[i] = tuple.TupleData[i];
+ dev->dev_addr[i] = buggybuf[i];
}
card_name = "FMV-J182";
break;
@@ -533,7 +530,7 @@ static int fmvj18x_config(struct pcmcia_device *link)
lp->cardtype = cardtype;
link->dev_node = &lp->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n");
@@ -551,9 +548,6 @@ static int fmvj18x_config(struct pcmcia_device *link)
return 0;
-cs_failed:
- /* All Card Services errors end up here */
- cs_error(link, last_fn, last_ret);
failed:
fmvj18x_release(link);
return -ENODEV;
@@ -571,16 +565,14 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id)
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
req.Base = 0; req.Size = 0;
req.AccessSpeed = 0;
- i = pcmcia_request_window(&link, &req, &link->win);
- if (i != 0) {
- cs_error(link, RequestWindow, i);
+ i = pcmcia_request_window(link, &req, &link->win);
+ if (i != 0)
return -1;
- }
base = ioremap(req.Base, req.Size);
mem.Page = 0;
mem.CardOffset = 0;
- pcmcia_map_mem_page(link->win, &mem);
+ pcmcia_map_mem_page(link, link->win, &mem);
/*
* MBH10304 CISTPL_FUNCE_LAN_NODE_ID format
@@ -590,11 +582,11 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id)
*/
for (i = 0; i < 0x200; i++) {
if (readb(base+i*2) == 0x22) {
- if (readb(base+(i-1)*2) == 0xff
- && readb(base+(i+5)*2) == 0x04
- && readb(base+(i+6)*2) == 0x06
- && readb(base+(i+13)*2) == 0xff)
- break;
+ if (readb(base+(i-1)*2) == 0xff &&
+ readb(base+(i+5)*2) == 0x04 &&
+ readb(base+(i+6)*2) == 0x06 &&
+ readb(base+(i+13)*2) == 0xff)
+ break;
}
}
@@ -605,9 +597,7 @@ static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id)
}
iounmap(base);
- j = pcmcia_release_window(link->win);
- if (j != 0)
- cs_error(link, ReleaseWindow, j);
+ j = pcmcia_release_window(link, link->win);
return (i != 0x200) ? 0 : -1;
} /* fmvj18x_get_hwinfo */
@@ -626,11 +616,9 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
req.Base = 0; req.Size = 0;
req.AccessSpeed = 0;
- i = pcmcia_request_window(&link, &req, &link->win);
- if (i != 0) {
- cs_error(link, RequestWindow, i);
+ i = pcmcia_request_window(link, &req, &link->win);
+ if (i != 0)
return -1;
- }
lp->base = ioremap(req.Base, req.Size);
if (lp->base == NULL) {
@@ -640,11 +628,10 @@ static int fmvj18x_setup_mfc(struct pcmcia_device *link)
mem.Page = 0;
mem.CardOffset = 0;
- i = pcmcia_map_mem_page(link->win, &mem);
+ i = pcmcia_map_mem_page(link, link->win, &mem);
if (i != 0) {
iounmap(lp->base);
lp->base = NULL;
- cs_error(link, MapMemPage, i);
return -1;
}
@@ -671,15 +658,13 @@ static void fmvj18x_release(struct pcmcia_device *link)
u_char __iomem *tmp;
int j;
- DEBUG(0, "fmvj18x_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "fmvj18x_release\n");
if (lp->base != NULL) {
tmp = lp->base;
lp->base = NULL; /* set NULL before iounmap */
iounmap(tmp);
- j = pcmcia_release_window(link->win);
- if (j != 0)
- cs_error(link, ReleaseWindow, j);
+ j = pcmcia_release_window(link, link->win);
}
pcmcia_disable_device(link);
@@ -788,8 +773,8 @@ static irqreturn_t fjn_interrupt(int dummy, void *dev_id)
outb(tx_stat, ioaddr + TX_STATUS);
outb(rx_stat, ioaddr + RX_STATUS);
- DEBUG(4, "%s: interrupt, rx_status %02x.\n", dev->name, rx_stat);
- DEBUG(4, " tx_status %02x.\n", tx_stat);
+ pr_debug("%s: interrupt, rx_status %02x.\n", dev->name, rx_stat);
+ pr_debug(" tx_status %02x.\n", tx_stat);
if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
/* there is packet(s) in rx buffer */
@@ -809,8 +794,8 @@ static irqreturn_t fjn_interrupt(int dummy, void *dev_id)
}
netif_wake_queue(dev);
}
- DEBUG(4, "%s: exiting interrupt,\n", dev->name);
- DEBUG(4, " tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat);
+ pr_debug("%s: exiting interrupt,\n", dev->name);
+ pr_debug(" tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat);
outb(D_TX_INTR, ioaddr + TX_INTR);
outb(D_RX_INTR, ioaddr + RX_INTR);
@@ -882,7 +867,7 @@ static netdev_tx_t fjn_start_xmit(struct sk_buff *skb,
return NETDEV_TX_BUSY;
}
- DEBUG(4, "%s: Transmitting a packet of length %lu.\n",
+ pr_debug("%s: Transmitting a packet of length %lu.\n",
dev->name, (unsigned long)skb->len);
dev->stats.tx_bytes += skb->len;
@@ -937,7 +922,7 @@ static void fjn_reset(struct net_device *dev)
unsigned int ioaddr = dev->base_addr;
int i;
- DEBUG(4, "fjn_reset(%s) called.\n",dev->name);
+ pr_debug("fjn_reset(%s) called.\n",dev->name);
/* Reset controller */
if( sram_config == 0 )
@@ -1015,13 +1000,13 @@ static void fjn_rx(struct net_device *dev)
unsigned int ioaddr = dev->base_addr;
int boguscount = 10; /* 5 -> 10: by agy 19940922 */
- DEBUG(4, "%s: in rx_packet(), rx_status %02x.\n",
+ pr_debug("%s: in rx_packet(), rx_status %02x.\n",
dev->name, inb(ioaddr + RX_STATUS));
while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
u_short status = inw(ioaddr + DATAPORT);
- DEBUG(4, "%s: Rxing packet mode %02x status %04x.\n",
+ pr_debug("%s: Rxing packet mode %02x status %04x.\n",
dev->name, inb(ioaddr + RX_MODE), status);
#ifndef final_version
if (status == 0) {
@@ -1061,16 +1046,14 @@ static void fjn_rx(struct net_device *dev)
(pkt_len + 1) >> 1);
skb->protocol = eth_type_trans(skb, dev);
-#ifdef PCMCIA_DEBUG
- if (pc_debug > 5) {
+ {
int i;
- printk(KERN_DEBUG "%s: Rxed packet of length %d: ",
- dev->name, pkt_len);
+ pr_debug("%s: Rxed packet of length %d: ",
+ dev->name, pkt_len);
for (i = 0; i < 14; i++)
- printk(" %02x", skb->data[i]);
- printk(".\n");
+ pr_debug(" %02x", skb->data[i]);
+ pr_debug(".\n");
}
-#endif
netif_rx(skb);
dev->stats.rx_packets++;
@@ -1094,7 +1077,7 @@ static void fjn_rx(struct net_device *dev)
}
if (i > 0)
- DEBUG(5, "%s: Exint Rx packet with mode %02x after "
+ pr_debug("%s: Exint Rx packet with mode %02x after "
"%d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i);
}
*/
@@ -1112,24 +1095,8 @@ static void netdev_get_drvinfo(struct net_device *dev,
sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr);
}
-#ifdef PCMCIA_DEBUG
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
- return pc_debug;
-}
-
-static void netdev_set_msglevel(struct net_device *dev, u32 level)
-{
- pc_debug = level;
-}
-#endif /* PCMCIA_DEBUG */
-
static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
-#ifdef PCMCIA_DEBUG
- .get_msglevel = netdev_get_msglevel,
- .set_msglevel = netdev_set_msglevel,
-#endif /* PCMCIA_DEBUG */
};
static int fjn_config(struct net_device *dev, struct ifmap *map){
@@ -1141,7 +1108,7 @@ static int fjn_open(struct net_device *dev)
struct local_info_t *lp = netdev_priv(dev);
struct pcmcia_device *link = lp->p_dev;
- DEBUG(4, "fjn_open('%s').\n", dev->name);
+ pr_debug("fjn_open('%s').\n", dev->name);
if (!pcmcia_dev_present(link))
return -ENODEV;
@@ -1167,7 +1134,7 @@ static int fjn_close(struct net_device *dev)
struct pcmcia_device *link = lp->p_dev;
unsigned int ioaddr = dev->base_addr;
- DEBUG(4, "fjn_close('%s').\n", dev->name);
+ pr_debug("fjn_close('%s').\n", dev->name);
lp->open_time = 0;
netif_stop_queue(dev);
@@ -1219,8 +1186,8 @@ static void set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) {
memset(mc_filter, 0xff, sizeof(mc_filter));
outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
- } else if (dev->mc_count > MC_FILTERBREAK
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if (dev->mc_count > MC_FILTERBREAK ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter perfectly -- accept all multicasts. */
memset(mc_filter, 0xff, sizeof(mc_filter));
outb(2, ioaddr + RX_MODE); /* Use normal mode. */
diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c
index 06618af1a46..37f4a6fdc3e 100644
--- a/drivers/net/pcmcia/ibmtr_cs.c
+++ b/drivers/net/pcmcia/ibmtr_cs.c
@@ -69,17 +69,6 @@
#define PCMCIA
#include "../tokenring/ibmtr.c"
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"ibmtr_cs.c 1.10 1996/01/06 05:19:00 (Steve Kipisz)\n"
-" 2.2.7 1999/05/03 12:00:00 (Mike Phillips)\n"
-" 2.4.2 2001/30/28 Midnight (Burt Silverman)\n";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -130,6 +119,12 @@ static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
};
+static irqreturn_t ibmtr_interrupt(int irq, void *dev_id) {
+ ibmtr_dev_t *info = dev_id;
+ struct net_device *dev = info->dev;
+ return tok_interrupt(irq, dev);
+};
+
/*======================================================================
ibmtr_attach() creates an "instance" of the driver, allocating
@@ -143,7 +138,7 @@ static int __devinit ibmtr_attach(struct pcmcia_device *link)
ibmtr_dev_t *info;
struct net_device *dev;
- DEBUG(0, "ibmtr_attach()\n");
+ dev_dbg(&link->dev, "ibmtr_attach()\n");
/* Create new token-ring device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -161,14 +156,13 @@ static int __devinit ibmtr_attach(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 4;
link->io.IOAddrLines = 16;
- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
- link->irq.Handler = &tok_interrupt;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.Handler = ibmtr_interrupt;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.Present = PRESENT_OPTION;
- link->irq.Instance = info->dev = dev;
+ info->dev = dev;
SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
@@ -190,7 +184,7 @@ static void ibmtr_detach(struct pcmcia_device *link)
struct net_device *dev = info->dev;
struct tok_info *ti = netdev_priv(dev);
- DEBUG(0, "ibmtr_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "ibmtr_detach\n");
/*
* When the card removal interrupt hits tok_interrupt(),
@@ -217,9 +211,6 @@ static void ibmtr_detach(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int __devinit ibmtr_config(struct pcmcia_device *link)
{
ibmtr_dev_t *info = link->priv;
@@ -227,9 +218,9 @@ static int __devinit ibmtr_config(struct pcmcia_device *link)
struct tok_info *ti = netdev_priv(dev);
win_req_t req;
memreq_t mem;
- int i, last_ret, last_fn;
+ int i, ret;
- DEBUG(0, "ibmtr_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "ibmtr_config\n");
link->conf.ConfigIndex = 0x61;
@@ -241,11 +232,15 @@ static int __devinit ibmtr_config(struct pcmcia_device *link)
if (i != 0) {
/* Couldn't get 0xA20-0xA23. Try ALTERNATE at 0xA24-0xA27. */
link->io.BasePort1 = 0xA24;
- CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
+ ret = pcmcia_request_io(link, &link->io);
+ if (ret)
+ goto failed;
}
dev->base_addr = link->io.BasePort1;
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
dev->irq = link->irq.AssignedIRQ;
ti->irq = link->irq.AssignedIRQ;
ti->global_int_enable=GLOBAL_INT_ENABLE+((dev->irq==9) ? 2 : dev->irq);
@@ -256,11 +251,15 @@ static int __devinit ibmtr_config(struct pcmcia_device *link)
req.Base = 0;
req.Size = 0x2000;
req.AccessSpeed = 250;
- CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
+ ret = pcmcia_request_window(link, &req, &link->win);
+ if (ret)
+ goto failed;
mem.CardOffset = mmiobase;
mem.Page = 0;
- CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
+ ret = pcmcia_map_mem_page(link, link->win, &mem);
+ if (ret)
+ goto failed;
ti->mmio = ioremap(req.Base, req.Size);
/* Allocate the SRAM memory window */
@@ -269,17 +268,23 @@ static int __devinit ibmtr_config(struct pcmcia_device *link)
req.Base = 0;
req.Size = sramsize * 1024;
req.AccessSpeed = 250;
- CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &info->sram_win_handle));
+ ret = pcmcia_request_window(link, &req, &info->sram_win_handle);
+ if (ret)
+ goto failed;
mem.CardOffset = srambase;
mem.Page = 0;
- CS_CHECK(MapMemPage, pcmcia_map_mem_page(info->sram_win_handle, &mem));
+ ret = pcmcia_map_mem_page(link, info->sram_win_handle, &mem);
+ if (ret)
+ goto failed;
ti->sram_base = mem.CardOffset >> 12;
ti->sram_virt = ioremap(req.Base, req.Size);
ti->sram_phys = req.Base;
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/* Set up the Token-Ring Controller Configuration Register and
turn on the card. Check the "Local Area Network Credit Card
@@ -287,7 +292,7 @@ static int __devinit ibmtr_config(struct pcmcia_device *link)
ibmtr_hw_setup(dev, mmiobase);
link->dev_node = &info->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
i = ibmtr_probe_card(dev);
if (i != 0) {
@@ -305,8 +310,6 @@ static int __devinit ibmtr_config(struct pcmcia_device *link)
dev->dev_addr);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
ibmtr_release(link);
return -ENODEV;
@@ -325,12 +328,12 @@ static void ibmtr_release(struct pcmcia_device *link)
ibmtr_dev_t *info = link->priv;
struct net_device *dev = info->dev;
- DEBUG(0, "ibmtr_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "ibmtr_release\n");
if (link->win) {
struct tok_info *ti = netdev_priv(dev);
iounmap(ti->mmio);
- pcmcia_release_window(info->sram_win_handle);
+ pcmcia_release_window(link, info->sram_win_handle);
}
pcmcia_disable_device(link);
}
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
index 5ed6339c52b..8a5ae3b182e 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -381,13 +381,6 @@ typedef struct _mace_private {
Private Global Variables
---------------------------------------------------------------------------- */
-#ifdef PCMCIA_DEBUG
-static char rcsid[] =
-"nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao";
-static char *version =
-DRV_NAME " " DRV_VERSION " (Roger C. Pao)";
-#endif
-
static const char *if_names[]={
"Auto", "10baseT", "BNC",
};
@@ -406,12 +399,6 @@ MODULE_LICENSE("GPL");
/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */
INT_MODULE_PARM(if_port, 0);
-#ifdef PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-#else
-#define DEBUG(n, args...)
-#endif
/* ----------------------------------------------------------------------------
Function Prototypes
@@ -462,8 +449,7 @@ static int nmclan_probe(struct pcmcia_device *link)
mace_private *lp;
struct net_device *dev;
- DEBUG(0, "nmclan_attach()\n");
- DEBUG(1, "%s\n", rcsid);
+ dev_dbg(&link->dev, "nmclan_attach()\n");
/* Create new ethernet device */
dev = alloc_etherdev(sizeof(mace_private));
@@ -477,10 +463,8 @@ static int nmclan_probe(struct pcmcia_device *link)
link->io.NumPorts1 = 32;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
link->io.IOAddrLines = 5;
- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
- link->irq.Handler = &mace_interrupt;
- link->irq.Instance = dev;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.Handler = mace_interrupt;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.ConfigIndex = 1;
@@ -507,7 +491,7 @@ static void nmclan_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "nmclan_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "nmclan_detach\n");
if (link->dev_node)
unregister_netdev(dev);
@@ -654,37 +638,40 @@ nmclan_config
ethernet device available to the system.
---------------------------------------------------------------------------- */
-#define CS_CHECK(fn, ret) \
- do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int nmclan_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
mace_private *lp = netdev_priv(dev);
- tuple_t tuple;
- u_char buf[64];
- int i, last_ret, last_fn;
+ u8 *buf;
+ size_t len;
+ int i, ret;
unsigned int ioaddr;
- DEBUG(0, "nmclan_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "nmclan_config\n");
+
+ ret = pcmcia_request_io(link, &link->io);
+ if (ret)
+ goto failed;
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
- CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
ioaddr = dev->base_addr;
/* Read the ethernet address from the CIS. */
- tuple.DesiredTuple = 0x80 /* CISTPL_CFTABLE_ENTRY_MISC */;
- tuple.TupleData = buf;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
- memcpy(dev->dev_addr, tuple.TupleData, ETHER_ADDR_LEN);
+ len = pcmcia_get_tuple(link, 0x80, &buf);
+ if (!buf || len < ETHER_ADDR_LEN) {
+ kfree(buf);
+ goto failed;
+ }
+ memcpy(dev->dev_addr, buf, ETHER_ADDR_LEN);
+ kfree(buf);
/* Verify configuration by reading the MACE ID. */
{
@@ -693,7 +680,7 @@ static int nmclan_config(struct pcmcia_device *link)
sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
- DEBUG(0, "nmclan_cs configured: mace id=%x %x\n",
+ dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n",
sig[0], sig[1]);
} else {
printk(KERN_NOTICE "nmclan_cs: mace id not found: %x %x should"
@@ -712,7 +699,7 @@ static int nmclan_config(struct pcmcia_device *link)
printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n");
link->dev_node = &lp->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
i = register_netdev(dev);
if (i != 0) {
@@ -729,8 +716,6 @@ static int nmclan_config(struct pcmcia_device *link)
dev->dev_addr);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
nmclan_release(link);
return -ENODEV;
@@ -744,7 +729,7 @@ nmclan_release
---------------------------------------------------------------------------- */
static void nmclan_release(struct pcmcia_device *link)
{
- DEBUG(0, "nmclan_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "nmclan_release\n");
pcmcia_disable_device(link);
}
@@ -795,7 +780,7 @@ static void nmclan_reset(struct net_device *dev)
/* Reset Xilinx */
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
- DEBUG(1, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n",
+ dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n",
OrigCorValue);
reg.Value = COR_SOFT_RESET;
pcmcia_access_configuration_register(link, &reg);
@@ -872,7 +857,7 @@ static int mace_close(struct net_device *dev)
mace_private *lp = netdev_priv(dev);
struct pcmcia_device *link = lp->p_dev;
- DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+ dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
/* Mask off all interrupts from the MACE chip. */
outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
@@ -891,24 +876,8 @@ static void netdev_get_drvinfo(struct net_device *dev,
sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr);
}
-#ifdef PCMCIA_DEBUG
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
- return pc_debug;
-}
-
-static void netdev_set_msglevel(struct net_device *dev, u32 level)
-{
- pc_debug = level;
-}
-#endif /* PCMCIA_DEBUG */
-
static const struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
-#ifdef PCMCIA_DEBUG
- .get_msglevel = netdev_get_msglevel,
- .set_msglevel = netdev_set_msglevel,
-#endif /* PCMCIA_DEBUG */
};
/* ----------------------------------------------------------------------------
@@ -946,7 +915,7 @@ static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
netif_stop_queue(dev);
- DEBUG(3, "%s: mace_start_xmit(length = %ld) called.\n",
+ pr_debug("%s: mace_start_xmit(length = %ld) called.\n",
dev->name, (long)skb->len);
#if (!TX_INTERRUPTABLE)
@@ -1008,7 +977,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
int IntrCnt = MACE_MAX_IR_ITERATIONS;
if (dev == NULL) {
- DEBUG(2, "mace_interrupt(): irq 0x%X for unknown device.\n",
+ pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n",
irq);
return IRQ_NONE;
}
@@ -1031,7 +1000,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
}
if (!netif_device_present(dev)) {
- DEBUG(2, "%s: interrupt from dead card\n", dev->name);
+ pr_debug("%s: interrupt from dead card\n", dev->name);
return IRQ_NONE;
}
@@ -1039,7 +1008,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id)
/* WARNING: MACE_IR is a READ/CLEAR port! */
status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
- DEBUG(3, "mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
+ pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
if (status & MACE_IR_RCVINT) {
mace_rx(dev, MACE_MAX_RX_ITERATIONS);
@@ -1158,7 +1127,7 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt)
) {
rx_status = inw(ioaddr + AM2150_RCV);
- DEBUG(3, "%s: in mace_rx(), framecnt 0x%X, rx_status"
+ pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status"
" 0x%X.\n", dev->name, rx_framecnt, rx_status);
if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
@@ -1185,7 +1154,7 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt)
lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
/* rcv collision count */
- DEBUG(3, " receiving packet size 0x%X rx_status"
+ pr_debug(" receiving packet size 0x%X rx_status"
" 0x%X.\n", pkt_len, rx_status);
skb = dev_alloc_skb(pkt_len+2);
@@ -1204,7 +1173,7 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt)
outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
continue;
} else {
- DEBUG(1, "%s: couldn't allocate a sk_buff of size"
+ pr_debug("%s: couldn't allocate a sk_buff of size"
" %d.\n", dev->name, pkt_len);
lp->linux_stats.rx_dropped++;
}
@@ -1220,28 +1189,28 @@ pr_linux_stats
---------------------------------------------------------------------------- */
static void pr_linux_stats(struct net_device_stats *pstats)
{
- DEBUG(2, "pr_linux_stats\n");
- DEBUG(2, " rx_packets=%-7ld tx_packets=%ld\n",
+ pr_debug("pr_linux_stats\n");
+ pr_debug(" rx_packets=%-7ld tx_packets=%ld\n",
(long)pstats->rx_packets, (long)pstats->tx_packets);
- DEBUG(2, " rx_errors=%-7ld tx_errors=%ld\n",
+ pr_debug(" rx_errors=%-7ld tx_errors=%ld\n",
(long)pstats->rx_errors, (long)pstats->tx_errors);
- DEBUG(2, " rx_dropped=%-7ld tx_dropped=%ld\n",
+ pr_debug(" rx_dropped=%-7ld tx_dropped=%ld\n",
(long)pstats->rx_dropped, (long)pstats->tx_dropped);
- DEBUG(2, " multicast=%-7ld collisions=%ld\n",
+ pr_debug(" multicast=%-7ld collisions=%ld\n",
(long)pstats->multicast, (long)pstats->collisions);
- DEBUG(2, " rx_length_errors=%-7ld rx_over_errors=%ld\n",
+ pr_debug(" rx_length_errors=%-7ld rx_over_errors=%ld\n",
(long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
- DEBUG(2, " rx_crc_errors=%-7ld rx_frame_errors=%ld\n",
+ pr_debug(" rx_crc_errors=%-7ld rx_frame_errors=%ld\n",
(long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
- DEBUG(2, " rx_fifo_errors=%-7ld rx_missed_errors=%ld\n",
+ pr_debug(" rx_fifo_errors=%-7ld rx_missed_errors=%ld\n",
(long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
- DEBUG(2, " tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
+ pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
(long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
- DEBUG(2, " tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n",
+ pr_debug(" tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n",
(long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
- DEBUG(2, " tx_window_errors=%ld\n",
+ pr_debug(" tx_window_errors=%ld\n",
(long)pstats->tx_window_errors);
} /* pr_linux_stats */
@@ -1250,48 +1219,48 @@ pr_mace_stats
---------------------------------------------------------------------------- */
static void pr_mace_stats(mace_statistics *pstats)
{
- DEBUG(2, "pr_mace_stats\n");
+ pr_debug("pr_mace_stats\n");
- DEBUG(2, " xmtsv=%-7d uflo=%d\n",
+ pr_debug(" xmtsv=%-7d uflo=%d\n",
pstats->xmtsv, pstats->uflo);
- DEBUG(2, " lcol=%-7d more=%d\n",
+ pr_debug(" lcol=%-7d more=%d\n",
pstats->lcol, pstats->more);
- DEBUG(2, " one=%-7d defer=%d\n",
+ pr_debug(" one=%-7d defer=%d\n",
pstats->one, pstats->defer);
- DEBUG(2, " lcar=%-7d rtry=%d\n",
+ pr_debug(" lcar=%-7d rtry=%d\n",
pstats->lcar, pstats->rtry);
/* MACE_XMTRC */
- DEBUG(2, " exdef=%-7d xmtrc=%d\n",
+ pr_debug(" exdef=%-7d xmtrc=%d\n",
pstats->exdef, pstats->xmtrc);
/* RFS1--Receive Status (RCVSTS) */
- DEBUG(2, " oflo=%-7d clsn=%d\n",
+ pr_debug(" oflo=%-7d clsn=%d\n",
pstats->oflo, pstats->clsn);
- DEBUG(2, " fram=%-7d fcs=%d\n",
+ pr_debug(" fram=%-7d fcs=%d\n",
pstats->fram, pstats->fcs);
/* RFS2--Runt Packet Count (RNTPC) */
/* RFS3--Receive Collision Count (RCVCC) */
- DEBUG(2, " rfs_rntpc=%-7d rfs_rcvcc=%d\n",
+ pr_debug(" rfs_rntpc=%-7d rfs_rcvcc=%d\n",
pstats->rfs_rntpc, pstats->rfs_rcvcc);
/* MACE_IR */
- DEBUG(2, " jab=%-7d babl=%d\n",
+ pr_debug(" jab=%-7d babl=%d\n",
pstats->jab, pstats->babl);
- DEBUG(2, " cerr=%-7d rcvcco=%d\n",
+ pr_debug(" cerr=%-7d rcvcco=%d\n",
pstats->cerr, pstats->rcvcco);
- DEBUG(2, " rntpco=%-7d mpco=%d\n",
+ pr_debug(" rntpco=%-7d mpco=%d\n",
pstats->rntpco, pstats->mpco);
/* MACE_MPC */
- DEBUG(2, " mpc=%d\n", pstats->mpc);
+ pr_debug(" mpc=%d\n", pstats->mpc);
/* MACE_RNTPC */
- DEBUG(2, " rntpc=%d\n", pstats->rntpc);
+ pr_debug(" rntpc=%d\n", pstats->rntpc);
/* MACE_RCVCC */
- DEBUG(2, " rcvcc=%d\n", pstats->rcvcc);
+ pr_debug(" rcvcc=%d\n", pstats->rcvcc);
} /* pr_mace_stats */
@@ -1360,7 +1329,7 @@ static struct net_device_stats *mace_get_stats(struct net_device *dev)
update_stats(dev->base_addr, dev);
- DEBUG(1, "%s: updating the statistics.\n", dev->name);
+ pr_debug("%s: updating the statistics.\n", dev->name);
pr_linux_stats(&lp->linux_stats);
pr_mace_stats(&lp->mace_stats);
@@ -1427,7 +1396,7 @@ static void BuildLAF(int *ladrf, int *adr)
ladrf[byte] |= (1 << (hashcode & 7));
#ifdef PCMCIA_DEBUG
- if (pc_debug > 2)
+ if (0)
printk(KERN_DEBUG " adr =%pM\n", adr);
printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode);
for (i = 0; i < 8; i++)
@@ -1454,12 +1423,12 @@ static void restore_multicast_list(struct net_device *dev)
unsigned int ioaddr = dev->base_addr;
int i;
- DEBUG(2, "%s: restoring Rx mode to %d addresses.\n",
+ pr_debug("%s: restoring Rx mode to %d addresses.\n",
dev->name, num_addrs);
if (num_addrs > 0) {
- DEBUG(1, "Attempt to restore multicast list detected.\n");
+ pr_debug("Attempt to restore multicast list detected.\n");
mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
/* Poll ADDRCHG bit */
@@ -1511,11 +1480,11 @@ static void set_multicast_list(struct net_device *dev)
struct dev_mc_list *dmi = dev->mc_list;
#ifdef PCMCIA_DEBUG
- if (pc_debug > 1) {
+ {
static int old;
if (dev->mc_count != old) {
old = dev->mc_count;
- DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+ pr_debug("%s: setting Rx mode to %d addresses.\n",
dev->name, old);
}
}
@@ -1546,7 +1515,7 @@ static void restore_multicast_list(struct net_device *dev)
unsigned int ioaddr = dev->base_addr;
mace_private *lp = netdev_priv(dev);
- DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name,
+ pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name,
lp->multicast_num_addrs);
if (dev->flags & IFF_PROMISC) {
@@ -1567,11 +1536,11 @@ static void set_multicast_list(struct net_device *dev)
mace_private *lp = netdev_priv(dev);
#ifdef PCMCIA_DEBUG
- if (pc_debug > 1) {
+ {
static int old;
if (dev->mc_count != old) {
old = dev->mc_count;
- DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+ pr_debug("%s: setting Rx mode to %d addresses.\n",
dev->name, old);
}
}
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
index 94c9ad2746b..2d26b6ca28b 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -71,15 +71,6 @@
static const char *if_names[] = { "auto", "10baseT", "10base2"};
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"pcnet_cs.c 1.153 2003/11/09 18:53:09 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -265,7 +256,7 @@ static int pcnet_probe(struct pcmcia_device *link)
pcnet_dev_t *info;
struct net_device *dev;
- DEBUG(0, "pcnet_attach()\n");
+ dev_dbg(&link->dev, "pcnet_attach()\n");
/* Create new ethernet device */
dev = __alloc_ei_netdev(sizeof(pcnet_dev_t));
@@ -275,7 +266,6 @@ static int pcnet_probe(struct pcmcia_device *link)
link->priv = dev;
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -297,7 +287,7 @@ static void pcnet_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "pcnet_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "pcnet_detach\n");
if (link->dev_node)
unregister_netdev(dev);
@@ -326,17 +316,15 @@ static hw_info_t *get_hwinfo(struct pcmcia_device *link)
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
req.Base = 0; req.Size = 0;
req.AccessSpeed = 0;
- i = pcmcia_request_window(&link, &req, &link->win);
- if (i != 0) {
- cs_error(link, RequestWindow, i);
+ i = pcmcia_request_window(link, &req, &link->win);
+ if (i != 0)
return NULL;
- }
virt = ioremap(req.Base, req.Size);
mem.Page = 0;
for (i = 0; i < NR_INFO; i++) {
mem.CardOffset = hw_info[i].offset & ~(req.Size-1);
- pcmcia_map_mem_page(link->win, &mem);
+ pcmcia_map_mem_page(link, link->win, &mem);
base = &virt[hw_info[i].offset & (req.Size-1)];
if ((readb(base+0) == hw_info[i].a0) &&
(readb(base+2) == hw_info[i].a1) &&
@@ -348,9 +336,7 @@ static hw_info_t *get_hwinfo(struct pcmcia_device *link)
}
iounmap(virt);
- j = pcmcia_release_window(link->win);
- if (j != 0)
- cs_error(link, ReleaseWindow, j);
+ j = pcmcia_release_window(link, link->win);
return (i < NR_INFO) ? hw_info+i : NULL;
} /* get_hwinfo */
@@ -495,9 +481,6 @@ static hw_info_t *get_hwired(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int try_io_port(struct pcmcia_device *link)
{
int j, ret;
@@ -567,19 +550,19 @@ static int pcnet_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
pcnet_dev_t *info = PRIV(dev);
- int last_ret, last_fn, start_pg, stop_pg, cm_offset;
+ int ret, start_pg, stop_pg, cm_offset;
int has_shmem = 0;
hw_info_t *local_hw_info;
- DEBUG(0, "pcnet_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "pcnet_config\n");
- last_ret = pcmcia_loop_config(link, pcnet_confcheck, &has_shmem);
- if (last_ret) {
- cs_error(link, RequestIO, last_ret);
+ ret = pcmcia_loop_config(link, pcnet_confcheck, &has_shmem);
+ if (ret)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
if (link->io.NumPorts2 == 8) {
link->conf.Attributes |= CONF_ENABLE_SPKR;
@@ -589,7 +572,9 @@ static int pcnet_config(struct pcmcia_device *link)
(link->card_id == PRODID_IBM_HOME_AND_AWAY))
link->conf.ConfigIndex |= 0x10;
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
if (info->flags & HAS_MISC_REG) {
@@ -601,8 +586,8 @@ static int pcnet_config(struct pcmcia_device *link)
dev->if_port = 0;
}
- if ((link->conf.ConfigBase == 0x03c0)
- && (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) {
+ if ((link->conf.ConfigBase == 0x03c0) &&
+ (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) {
printk(KERN_INFO "pcnet_cs: this is an AX88190 card!\n");
printk(KERN_INFO "pcnet_cs: use axnet_cs instead.\n");
goto failed;
@@ -660,7 +645,7 @@ static int pcnet_config(struct pcmcia_device *link)
mii_phy_probe(dev);
link->dev_node = &info->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n");
@@ -687,8 +672,6 @@ static int pcnet_config(struct pcmcia_device *link)
printk(" hw_addr %pM\n", dev->dev_addr);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
pcnet_release(link);
return -ENODEV;
@@ -706,7 +689,7 @@ static void pcnet_release(struct pcmcia_device *link)
{
pcnet_dev_t *info = PRIV(link->priv);
- DEBUG(0, "pcnet_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "pcnet_release\n");
if (info->flags & USE_SHMEM)
iounmap(info->base);
@@ -960,7 +943,7 @@ static void mii_phy_probe(struct net_device *dev)
phyid = tmp << 16;
phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
phyid &= MII_PHYID_REV_MASK;
- DEBUG(0, "%s: MII at %d is 0x%08x\n", dev->name, i, phyid);
+ pr_debug("%s: MII at %d is 0x%08x\n", dev->name, i, phyid);
if (phyid == AM79C9XX_HOME_PHY) {
info->pna_phy = i;
} else if (phyid != AM79C9XX_ETH_PHY) {
@@ -976,7 +959,7 @@ static int pcnet_open(struct net_device *dev)
struct pcmcia_device *link = info->p_dev;
unsigned int nic_base = dev->base_addr;
- DEBUG(2, "pcnet_open('%s')\n", dev->name);
+ dev_dbg(&link->dev, "pcnet_open('%s')\n", dev->name);
if (!pcmcia_dev_present(link))
return -ENODEV;
@@ -1008,7 +991,7 @@ static int pcnet_close(struct net_device *dev)
pcnet_dev_t *info = PRIV(dev);
struct pcmcia_device *link = info->p_dev;
- DEBUG(2, "pcnet_close('%s')\n", dev->name);
+ dev_dbg(&link->dev, "pcnet_close('%s')\n", dev->name);
ei_close(dev);
free_irq(dev->irq, dev);
@@ -1251,10 +1234,8 @@ static void dma_block_input(struct net_device *dev, int count,
int xfer_count = count;
char *buf = skb->data;
-#ifdef PCMCIA_DEBUG
if ((ei_debug > 4) && (count != 4))
- printk(KERN_DEBUG "%s: [bi=%d]\n", dev->name, count+4);
-#endif
+ pr_debug("%s: [bi=%d]\n", dev->name, count+4);
if (ei_status.dmaing) {
printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input."
"[DMAstat:%1x][irqlock:%1x]\n",
@@ -1495,7 +1476,7 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
pcnet_dev_t *info = PRIV(dev);
win_req_t req;
memreq_t mem;
- int i, window_size, offset, last_ret, last_fn;
+ int i, window_size, offset, ret;
window_size = (stop_pg - start_pg) << 8;
if (window_size > 32 * 1024)
@@ -1509,13 +1490,17 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
req.Attributes |= WIN_USE_WAIT;
req.Base = 0; req.Size = window_size;
req.AccessSpeed = mem_speed;
- CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
+ ret = pcmcia_request_window(link, &req, &link->win);
+ if (ret)
+ goto failed;
mem.CardOffset = (start_pg << 8) + cm_offset;
offset = mem.CardOffset % window_size;
mem.CardOffset -= offset;
mem.Page = 0;
- CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
+ ret = pcmcia_map_mem_page(link, link->win, &mem);
+ if (ret)
+ goto failed;
/* Try scribbling on the buffer */
info->base = ioremap(req.Base, window_size);
@@ -1527,8 +1512,8 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
pcnet_reset_8390(dev);
if (i != (TX_PAGES<<8)) {
iounmap(info->base);
- pcmcia_release_window(link->win);
- info->base = NULL; link->win = NULL;
+ pcmcia_release_window(link, link->win);
+ info->base = NULL; link->win = 0;
goto failed;
}
@@ -1549,8 +1534,6 @@ static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
info->flags |= USE_SHMEM;
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
return 1;
}
@@ -1768,6 +1751,13 @@ static struct pcmcia_device_id pcnet_ids[] = {
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, pcnet_ids);
+MODULE_FIRMWARE("cis/PCMLM28.cis");
+MODULE_FIRMWARE("cis/DP83903.cis");
+MODULE_FIRMWARE("cis/LA-PCM.cis");
+MODULE_FIRMWARE("PE520.cis");
+MODULE_FIRMWARE("cis/NE2K.cis");
+MODULE_FIRMWARE("cis/PE-200.cis");
+MODULE_FIRMWARE("cis/tamarack.cis");
static struct pcmcia_driver pcnet_driver = {
.drv = {
@@ -1788,7 +1778,6 @@ static int __init init_pcnet_cs(void)
static void __exit exit_pcnet_cs(void)
{
- DEBUG(0, "pcnet_cs: unloading\n");
pcmcia_unregister_driver(&pcnet_driver);
}
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index 7bde2cd34c7..cc4853bc025 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -79,14 +79,6 @@ MODULE_FIRMWARE(FIRMWARE_NAME);
*/
INT_MODULE_PARM(if_port, 0);
-#ifdef PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
-static const char *version =
-"smc91c92_cs.c 1.123 2006/11/09 Donald Becker, becker@scyld.com.\n";
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-#else
-#define DEBUG(n, args...)
-#endif
#define DRV_NAME "smc91c92_cs"
#define DRV_VERSION "1.123"
@@ -126,12 +118,6 @@ struct smc_private {
int rx_ovrn;
};
-struct smc_cfg_mem {
- tuple_t tuple;
- cisparse_t parse;
- u_char buf[255];
-};
-
/* Special definitions for Megahertz multifunction cards */
#define MEGAHERTZ_ISR 0x0380
@@ -329,7 +315,7 @@ static int smc91c92_probe(struct pcmcia_device *link)
struct smc_private *smc;
struct net_device *dev;
- DEBUG(0, "smc91c92_attach()\n");
+ dev_dbg(&link->dev, "smc91c92_attach()\n");
/* Create new ethernet device */
dev = alloc_etherdev(sizeof(struct smc_private));
@@ -343,10 +329,8 @@ static int smc91c92_probe(struct pcmcia_device *link)
link->io.NumPorts1 = 16;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
link->io.IOAddrLines = 4;
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = &smc_interrupt;
- link->irq.Instance = dev;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -377,7 +361,7 @@ static void smc91c92_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "smc91c92_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "smc91c92_detach\n");
if (link->dev_node)
unregister_netdev(dev);
@@ -408,34 +392,7 @@ static int cvt_ascii_address(struct net_device *dev, char *s)
return 0;
}
-/*====================================================================*/
-
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i;
-
- i = pcmcia_get_first_tuple(handle, tuple);
- if (i != 0)
- return i;
- i = pcmcia_get_tuple_data(handle, tuple);
- if (i != 0)
- return i;
- return pcmcia_parse_tuple(tuple, parse);
-}
-
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i;
-
- if ((i = pcmcia_get_next_tuple(handle, tuple)) != 0 ||
- (i = pcmcia_get_tuple_data(handle, tuple)) != 0)
- return i;
- return pcmcia_parse_tuple(tuple, parse);
-}
-
-/*======================================================================
+/*====================================================================
Configuration stuff for Megahertz cards
@@ -490,19 +447,14 @@ static int mhz_mfc_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
struct smc_private *smc = netdev_priv(dev);
- struct smc_cfg_mem *cfg_mem;
win_req_t req;
memreq_t mem;
int i;
- cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- return -ENOMEM;
-
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+ IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
link->io.IOAddrLines = 16;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts2 = 8;
@@ -510,91 +462,80 @@ static int mhz_mfc_config(struct pcmcia_device *link)
/* The Megahertz combo cards have modem-like CIS entries, so
we have to explicitly try a bunch of port combinations. */
if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
- goto free_cfg_mem;
+ return -ENODEV;
+
dev->base_addr = link->io.BasePort1;
/* Allocate a memory window, for accessing the ISR */
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
req.Base = req.Size = 0;
req.AccessSpeed = 0;
- i = pcmcia_request_window(&link, &req, &link->win);
+ i = pcmcia_request_window(link, &req, &link->win);
if (i != 0)
- goto free_cfg_mem;
+ return -ENODEV;
+
smc->base = ioremap(req.Base, req.Size);
mem.CardOffset = mem.Page = 0;
if (smc->manfid == MANFID_MOTOROLA)
mem.CardOffset = link->conf.ConfigBase;
- i = pcmcia_map_mem_page(link->win, &mem);
+ i = pcmcia_map_mem_page(link, link->win, &mem);
- if ((i == 0)
- && (smc->manfid == MANFID_MEGAHERTZ)
- && (smc->cardid == PRODID_MEGAHERTZ_EM3288))
- mhz_3288_power(link);
+ if ((i == 0) &&
+ (smc->manfid == MANFID_MEGAHERTZ) &&
+ (smc->cardid == PRODID_MEGAHERTZ_EM3288))
+ mhz_3288_power(link);
-free_cfg_mem:
- kfree(cfg_mem);
- return -ENODEV;
+ return 0;
}
-static int mhz_setup(struct pcmcia_device *link)
+static int pcmcia_get_versmac(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv)
{
- struct net_device *dev = link->priv;
- struct smc_cfg_mem *cfg_mem;
- tuple_t *tuple;
- cisparse_t *parse;
- u_char *buf, *station_addr;
- int rc;
+ struct net_device *dev = priv;
+ cisparse_t parse;
- cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- return -1;
+ if (pcmcia_parse_tuple(tuple, &parse))
+ return -EINVAL;
- tuple = &cfg_mem->tuple;
- parse = &cfg_mem->parse;
- buf = cfg_mem->buf;
+ if ((parse.version_1.ns > 3) &&
+ (cvt_ascii_address(dev,
+ (parse.version_1.str + parse.version_1.ofs[3]))))
+ return 0;
- tuple->Attributes = tuple->TupleOffset = 0;
- tuple->TupleData = (cisdata_t *)buf;
- tuple->TupleDataMax = 255;
+ return -EINVAL;
+};
+
+static int mhz_setup(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ size_t len;
+ u8 *buf;
+ int rc;
/* Read the station address from the CIS. It is stored as the last
(fourth) string in the Version 1 Version/ID tuple. */
- tuple->DesiredTuple = CISTPL_VERS_1;
- if (first_tuple(link, tuple, parse) != 0) {
- rc = -1;
- goto free_cfg_mem;
- }
+ if ((link->prod_id[3]) &&
+ (cvt_ascii_address(dev, link->prod_id[3]) == 0))
+ return 0;
+
+ /* Workarounds for broken cards start here. */
/* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
- if (next_tuple(link, tuple, parse) != 0)
- first_tuple(link, tuple, parse);
- if (parse->version_1.ns > 3) {
- station_addr = parse->version_1.str + parse->version_1.ofs[3];
- if (cvt_ascii_address(dev, station_addr) == 0) {
- rc = 0;
- goto free_cfg_mem;
- }
- }
+ if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev))
+ return 0;
/* Another possibility: for the EM3288, in a special tuple */
- tuple->DesiredTuple = 0x81;
- if (pcmcia_get_first_tuple(link, tuple) != 0) {
- rc = -1;
- goto free_cfg_mem;
- }
- if (pcmcia_get_tuple_data(link, tuple) != 0) {
- rc = -1;
- goto free_cfg_mem;
- }
- buf[12] = '\0';
- if (cvt_ascii_address(dev, buf) == 0) {
- rc = 0;
- goto free_cfg_mem;
- }
rc = -1;
-free_cfg_mem:
- kfree(cfg_mem);
- return rc;
-}
+ len = pcmcia_get_tuple(link, 0x81, &buf);
+ if (buf && len >= 13) {
+ buf[12] = '\0';
+ if (cvt_ascii_address(dev, buf))
+ rc = 0;
+ }
+ kfree(buf);
+
+ return rc;
+};
/*======================================================================
@@ -684,58 +625,21 @@ static int smc_config(struct pcmcia_device *link)
return i;
}
+
static int smc_setup(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- struct smc_cfg_mem *cfg_mem;
- tuple_t *tuple;
- cisparse_t *parse;
- cistpl_lan_node_id_t *node_id;
- u_char *buf, *station_addr;
- int i, rc;
-
- cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- return -ENOMEM;
-
- tuple = &cfg_mem->tuple;
- parse = &cfg_mem->parse;
- buf = cfg_mem->buf;
-
- tuple->Attributes = tuple->TupleOffset = 0;
- tuple->TupleData = (cisdata_t *)buf;
- tuple->TupleDataMax = 255;
/* Check for a LAN function extension tuple */
- tuple->DesiredTuple = CISTPL_FUNCE;
- i = first_tuple(link, tuple, parse);
- while (i == 0) {
- if (parse->funce.type == CISTPL_FUNCE_LAN_NODE_ID)
- break;
- i = next_tuple(link, tuple, parse);
- }
- if (i == 0) {
- node_id = (cistpl_lan_node_id_t *)parse->funce.data;
- if (node_id->nb == 6) {
- for (i = 0; i < 6; i++)
- dev->dev_addr[i] = node_id->id[i];
- rc = 0;
- goto free_cfg_mem;
- }
- }
+ if (!pcmcia_get_mac_from_cis(link, dev))
+ return 0;
+
/* Try the third string in the Version 1 Version/ID tuple. */
if (link->prod_id[2]) {
- station_addr = link->prod_id[2];
- if (cvt_ascii_address(dev, station_addr) == 0) {
- rc = 0;
- goto free_cfg_mem;
- }
+ if (cvt_ascii_address(dev, link->prod_id[2]) == 0)
+ return 0;
}
-
- rc = -1;
-free_cfg_mem:
- kfree(cfg_mem);
- return rc;
+ return -1;
}
/*====================================================================*/
@@ -749,7 +653,7 @@ static int osi_config(struct pcmcia_device *link)
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
link->irq.Attributes =
- IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+ IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
link->io.NumPorts1 = 64;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts2 = 8;
@@ -794,41 +698,31 @@ static int osi_load_firmware(struct pcmcia_device *link)
return err;
}
-static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
+static int pcmcia_osi_mac(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv)
{
- struct net_device *dev = link->priv;
- struct smc_cfg_mem *cfg_mem;
- tuple_t *tuple;
- u_char *buf;
- int i, rc;
+ struct net_device *dev = priv;
+ int i;
- cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- return -1;
+ if (tuple->TupleDataLen < 8)
+ return -EINVAL;
+ if (tuple->TupleData[0] != 0x04)
+ return -EINVAL;
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = tuple->TupleData[i+2];
+ return 0;
+};
- tuple = &cfg_mem->tuple;
- buf = cfg_mem->buf;
- tuple->Attributes = TUPLE_RETURN_COMMON;
- tuple->TupleData = (cisdata_t *)buf;
- tuple->TupleDataMax = 255;
- tuple->TupleOffset = 0;
+static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
+{
+ struct net_device *dev = link->priv;
+ int rc;
/* Read the station address from tuple 0x90, subtuple 0x04 */
- tuple->DesiredTuple = 0x90;
- i = pcmcia_get_first_tuple(link, tuple);
- while (i == 0) {
- i = pcmcia_get_tuple_data(link, tuple);
- if ((i != 0) || (buf[0] == 0x04))
- break;
- i = pcmcia_get_next_tuple(link, tuple);
- }
- if (i != 0) {
- rc = -1;
- goto free_cfg_mem;
- }
- for (i = 0; i < 6; i++)
- dev->dev_addr[i] = buf[i+2];
+ if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev))
+ return -1;
if (((manfid == MANFID_OSITECH) &&
(cardid == PRODID_OSITECH_SEVEN)) ||
@@ -836,20 +730,17 @@ static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
(cardid == PRODID_PSION_NET100))) {
rc = osi_load_firmware(link);
if (rc)
- goto free_cfg_mem;
+ return rc;
} else if (manfid == MANFID_OSITECH) {
/* Make sure both functions are powered up */
set_bits(0x300, link->io.BasePort1 + OSITECH_AUI_PWR);
/* Now, turn on the interrupt for both card functions */
set_bits(0x300, link->io.BasePort1 + OSITECH_RESET_ISR);
- DEBUG(2, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
+ dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
inw(link->io.BasePort1 + OSITECH_AUI_PWR),
inw(link->io.BasePort1 + OSITECH_RESET_ISR));
}
- rc = 0;
-free_cfg_mem:
- kfree(cfg_mem);
- return rc;
+ return 0;
}
static int smc91c92_suspend(struct pcmcia_device *link)
@@ -959,12 +850,6 @@ static int check_sig(struct pcmcia_device *link)
======================================================================*/
-#define CS_EXIT_TEST(ret, svc, label) \
-if (ret != 0) { \
- cs_error(link, svc, ret); \
- goto label; \
-}
-
static int smc91c92_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
@@ -974,7 +859,7 @@ static int smc91c92_config(struct pcmcia_device *link)
unsigned int ioaddr;
u_long mir;
- DEBUG(0, "smc91c92_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "smc91c92_config\n");
smc->manfid = link->manf_id;
smc->cardid = link->card_id;
@@ -990,12 +875,15 @@ static int smc91c92_config(struct pcmcia_device *link)
} else {
i = smc_config(link);
}
- CS_EXIT_TEST(i, RequestIO, config_failed);
+ if (i)
+ goto config_failed;
i = pcmcia_request_irq(link, &link->irq);
- CS_EXIT_TEST(i, RequestIRQ, config_failed);
+ if (i)
+ goto config_failed;
i = pcmcia_request_configuration(link, &link->conf);
- CS_EXIT_TEST(i, RequestConfiguration, config_failed);
+ if (i)
+ goto config_failed;
if (smc->manfid == MANFID_MOTOROLA)
mot_config(link);
@@ -1074,7 +962,7 @@ static int smc91c92_config(struct pcmcia_device *link)
}
link->dev_node = &smc->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n");
@@ -1100,7 +988,7 @@ static int smc91c92_config(struct pcmcia_device *link)
if (smc->cfg & CFG_MII_SELECT) {
if (smc->mii_if.phy_id != -1) {
- DEBUG(0, " MII transceiver at index %d, status %x.\n",
+ dev_dbg(&link->dev, " MII transceiver at index %d, status %x.\n",
smc->mii_if.phy_id, j);
} else {
printk(KERN_NOTICE " No MII transceivers found!\n");
@@ -1110,7 +998,7 @@ static int smc91c92_config(struct pcmcia_device *link)
config_undo:
unregister_netdev(dev);
-config_failed: /* CS_EXIT_TEST() calls jump to here... */
+config_failed:
smc91c92_release(link);
return -ENODEV;
} /* smc91c92_config */
@@ -1125,7 +1013,7 @@ config_failed: /* CS_EXIT_TEST() calls jump to here... */
static void smc91c92_release(struct pcmcia_device *link)
{
- DEBUG(0, "smc91c92_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "smc91c92_release\n");
if (link->win) {
struct net_device *dev = link->priv;
struct smc_private *smc = netdev_priv(dev);
@@ -1222,10 +1110,10 @@ static int smc_open(struct net_device *dev)
struct smc_private *smc = netdev_priv(dev);
struct pcmcia_device *link = smc->p_dev;
-#ifdef PCMCIA_DEBUG
- DEBUG(0, "%s: smc_open(%p), ID/Window %4.4x.\n",
+ dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n",
dev->name, dev, inw(dev->base_addr + BANK_SELECT));
- if (pc_debug > 1) smc_dump(dev);
+#ifdef PCMCIA_DEBUG
+ smc_dump(dev);
#endif
/* Check that the PCMCIA card is still here. */
@@ -1260,7 +1148,7 @@ static int smc_close(struct net_device *dev)
struct pcmcia_device *link = smc->p_dev;
unsigned int ioaddr = dev->base_addr;
- DEBUG(0, "%s: smc_close(), status %4.4x.\n",
+ dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n",
dev->name, inw(ioaddr + BANK_SELECT));
netif_stop_queue(dev);
@@ -1327,7 +1215,7 @@ static void smc_hardware_send_packet(struct net_device * dev)
u_char *buf = skb->data;
u_int length = skb->len; /* The chip will pad to ethernet min. */
- DEBUG(2, "%s: Trying to xmit packet of length %d.\n",
+ pr_debug("%s: Trying to xmit packet of length %d.\n",
dev->name, length);
/* send the packet length: +6 for status word, length, and ctl */
@@ -1382,7 +1270,7 @@ static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
netif_stop_queue(dev);
- DEBUG(2, "%s: smc_start_xmit(length = %d) called,"
+ pr_debug("%s: smc_start_xmit(length = %d) called,"
" status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2));
if (smc->saved_skb) {
@@ -1429,7 +1317,7 @@ static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
}
/* Otherwise defer until the Tx-space-allocated interrupt. */
- DEBUG(2, "%s: memory allocation deferred.\n", dev->name);
+ pr_debug("%s: memory allocation deferred.\n", dev->name);
outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
spin_unlock_irqrestore(&smc->lock, flags);
@@ -1494,7 +1382,7 @@ static void smc_eph_irq(struct net_device *dev)
SMC_SELECT_BANK(0);
ephs = inw(ioaddr + EPH);
- DEBUG(2, "%s: Ethernet protocol handler interrupt, status"
+ pr_debug("%s: Ethernet protocol handler interrupt, status"
" %4.4x.\n", dev->name, ephs);
/* Could be a counter roll-over warning: update stats. */
card_stats = inw(ioaddr + COUNTER);
@@ -1534,7 +1422,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
ioaddr = dev->base_addr;
- DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
+ pr_debug("%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
irq, ioaddr);
spin_lock(&smc->lock);
@@ -1543,7 +1431,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
if ((saved_bank & 0xff00) != 0x3300) {
/* The device does not exist -- the card could be off-line, or
maybe it has been ejected. */
- DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent"
+ pr_debug("%s: SMC91c92 interrupt %d for non-existent"
"/ejected device.\n", dev->name, irq);
handled = 0;
goto irq_done;
@@ -1557,7 +1445,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
do { /* read the status flag, and mask it */
status = inw(ioaddr + INTERRUPT) & 0xff;
- DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
+ pr_debug("%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
status, mask);
if ((status & mask) == 0) {
if (bogus_cnt == INTR_WORK)
@@ -1602,7 +1490,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
smc_eph_irq(dev);
} while (--bogus_cnt);
- DEBUG(3, " Restoring saved registers mask %2.2x bank %4.4x"
+ pr_debug(" Restoring saved registers mask %2.2x bank %4.4x"
" pointer %4.4x.\n", mask, saved_bank, saved_pointer);
/* restore state register */
@@ -1610,7 +1498,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id)
outw(saved_pointer, ioaddr + POINTER);
SMC_SELECT_BANK(saved_bank);
- DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
+ pr_debug("%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
irq_done:
@@ -1661,7 +1549,7 @@ static void smc_rx(struct net_device *dev)
rx_status = inw(ioaddr + DATA_1);
packet_length = inw(ioaddr + DATA_1) & 0x07ff;
- DEBUG(2, "%s: Receive status %4.4x length %d.\n",
+ pr_debug("%s: Receive status %4.4x length %d.\n",
dev->name, rx_status, packet_length);
if (!(rx_status & RS_ERRORS)) {
@@ -1672,7 +1560,7 @@ static void smc_rx(struct net_device *dev)
skb = dev_alloc_skb(packet_length+2);
if (skb == NULL) {
- DEBUG(1, "%s: Low memory, packet dropped.\n", dev->name);
+ pr_debug("%s: Low memory, packet dropped.\n", dev->name);
dev->stats.rx_dropped++;
outw(MC_RELEASE, ioaddr + MMU_CMD);
return;
@@ -1832,7 +1720,7 @@ static void smc_reset(struct net_device *dev)
struct smc_private *smc = netdev_priv(dev);
int i;
- DEBUG(0, "%s: smc91c92 reset called.\n", dev->name);
+ pr_debug("%s: smc91c92 reset called.\n", dev->name);
/* The first interaction must be a write to bring the chip out
of sleep mode. */
@@ -2149,18 +2037,6 @@ static u32 smc_get_link(struct net_device *dev)
return ret;
}
-#ifdef PCMCIA_DEBUG
-static u32 smc_get_msglevel(struct net_device *dev)
-{
- return pc_debug;
-}
-
-static void smc_set_msglevel(struct net_device *dev, u32 val)
-{
- pc_debug = val;
-}
-#endif
-
static int smc_nway_reset(struct net_device *dev)
{
struct smc_private *smc = netdev_priv(dev);
@@ -2184,10 +2060,6 @@ static const struct ethtool_ops ethtool_ops = {
.get_settings = smc_get_settings,
.set_settings = smc_set_settings,
.get_link = smc_get_link,
-#ifdef PCMCIA_DEBUG
- .get_msglevel = smc_get_msglevel,
- .set_msglevel = smc_set_msglevel,
-#endif
.nway_reset = smc_nway_reset,
};
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index cf842310253..a2eda28f903 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -211,20 +211,6 @@ enum xirc_cmd { /* Commands */
static const char *if_names[] = { "Auto", "10BaseT", "10Base2", "AUI", "100BaseT" };
-/****************
- * All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- * you do not define PCMCIA_DEBUG at all, all the debug code will be
- * left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- * be present but disabled -- but it can then be enabled for specific
- * modules at load time with a 'pc_debug=#' option to insmod.
- */
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KDBG_XIRC args)
-#else
-#define DEBUG(n, args...)
-#endif
#define KDBG_XIRC KERN_DEBUG "xirc2ps_cs: "
#define KERR_XIRC KERN_ERR "xirc2ps_cs: "
@@ -359,7 +345,7 @@ static void xirc_tx_timeout(struct net_device *dev);
static void xirc2ps_tx_timeout_task(struct work_struct *work);
static void set_addresses(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
-static int set_card_type(struct pcmcia_device *link, const void *s);
+static int set_card_type(struct pcmcia_device *link);
static int do_config(struct net_device *dev, struct ifmap *map);
static int do_open(struct net_device *dev);
static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -371,28 +357,6 @@ static void do_powerdown(struct net_device *dev);
static int do_stop(struct net_device *dev);
/*=============== Helper functions =========================*/
-static int
-first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
-{
- int err;
-
- if ((err = pcmcia_get_first_tuple(handle, tuple)) == 0 &&
- (err = pcmcia_get_tuple_data(handle, tuple)) == 0)
- err = pcmcia_parse_tuple(tuple, parse);
- return err;
-}
-
-static int
-next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
-{
- int err;
-
- if ((err = pcmcia_get_next_tuple(handle, tuple)) == 0 &&
- (err = pcmcia_get_tuple_data(handle, tuple)) == 0)
- err = pcmcia_parse_tuple(tuple, parse);
- return err;
-}
-
#define SelectPage(pgnr) outb((pgnr), ioaddr + XIRCREG_PR)
#define GetByte(reg) ((unsigned)inb(ioaddr + (reg)))
#define GetWord(reg) ((unsigned)inw(ioaddr + (reg)))
@@ -400,7 +364,7 @@ next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
#define PutWord(reg,value) outw((value), ioaddr+(reg))
/*====== Functions used for debugging =================================*/
-#if defined(PCMCIA_DEBUG) && 0 /* reading regs may change system status */
+#if 0 /* reading regs may change system status */
static void
PrintRegisters(struct net_device *dev)
{
@@ -421,9 +385,9 @@ PrintRegisters(struct net_device *dev)
printk("\n");
}
for (page=0x40 ; page <= 0x5f; page++) {
- if (page == 0x43 || (page >= 0x46 && page <= 0x4f)
- || (page >= 0x51 && page <=0x5e))
- continue;
+ if (page == 0x43 || (page >= 0x46 && page <= 0x4f) ||
+ (page >= 0x51 && page <=0x5e))
+ continue;
printk(KDBG_XIRC "Register page %2x: ", page);
SelectPage(page);
for (i = 8; i < 16; i++)
@@ -432,7 +396,7 @@ PrintRegisters(struct net_device *dev)
}
}
}
-#endif /* PCMCIA_DEBUG */
+#endif /* 0 */
/*============== MII Management functions ===============*/
@@ -576,7 +540,7 @@ xirc2ps_probe(struct pcmcia_device *link)
struct net_device *dev;
local_info_t *local;
- DEBUG(0, "attach()\n");
+ dev_dbg(&link->dev, "attach()\n");
/* Allocate the device structure */
dev = alloc_etherdev(sizeof(local_info_t));
@@ -592,7 +556,6 @@ xirc2ps_probe(struct pcmcia_device *link)
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.ConfigIndex = 1;
link->irq.Handler = xirc2ps_interrupt;
- link->irq.Instance = dev;
/* Fill in card specific entries */
dev->netdev_ops = &netdev_ops;
@@ -615,7 +578,7 @@ xirc2ps_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "detach\n");
if (link->dev_node)
unregister_netdev(dev);
@@ -644,17 +607,25 @@ xirc2ps_detach(struct pcmcia_device *link)
*
*/
static int
-set_card_type(struct pcmcia_device *link, const void *s)
+set_card_type(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
local_info_t *local = netdev_priv(dev);
- #ifdef PCMCIA_DEBUG
- unsigned cisrev = ((const unsigned char *)s)[2];
- #endif
- unsigned mediaid= ((const unsigned char *)s)[3];
- unsigned prodid = ((const unsigned char *)s)[4];
+ u8 *buf;
+ unsigned int cisrev, mediaid, prodid;
+ size_t len;
+
+ len = pcmcia_get_tuple(link, CISTPL_MANFID, &buf);
+ if (len < 5) {
+ dev_err(&link->dev, "invalid CIS -- sorry\n");
+ return 0;
+ }
- DEBUG(0, "cisrev=%02x mediaid=%02x prodid=%02x\n",
+ cisrev = buf[2];
+ mediaid = buf[3];
+ prodid = buf[4];
+
+ dev_dbg(&link->dev, "cisrev=%02x mediaid=%02x prodid=%02x\n",
cisrev, mediaid, prodid);
local->mohawk = 0;
@@ -761,6 +732,26 @@ xirc2ps_config_check(struct pcmcia_device *p_dev,
}
+
+static int pcmcia_get_mac_ce(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv)
+{
+ struct net_device *dev = priv;
+ int i;
+
+ if (tuple->TupleDataLen != 13)
+ return -EINVAL;
+ if ((tuple->TupleData[0] != 2) || (tuple->TupleData[1] != 1) ||
+ (tuple->TupleData[2] != 6))
+ return -EINVAL;
+ /* another try (James Lehmer's CE2 version 4.1)*/
+ for (i = 2; i < 6; i++)
+ dev->dev_addr[i] = tuple->TupleData[i+2];
+ return 0;
+};
+
+
/****************
* xirc2ps_config() is scheduled to run after a CARD_INSERTION event
* is received, to configure the PCMCIA socket, and to make the
@@ -772,33 +763,21 @@ xirc2ps_config(struct pcmcia_device * link)
struct net_device *dev = link->priv;
local_info_t *local = netdev_priv(dev);
unsigned int ioaddr;
- tuple_t tuple;
- cisparse_t parse;
- int err, i;
- u_char buf[64];
- cistpl_lan_node_id_t *node_id = (cistpl_lan_node_id_t*)parse.funce.data;
+ int err;
+ u8 *buf;
+ size_t len;
local->dingo_ccr = NULL;
- DEBUG(0, "config(0x%p)\n", link);
-
- /*
- * This reads the card's CONFIG tuple to find its configuration
- * registers.
- */
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
+ dev_dbg(&link->dev, "config\n");
/* Is this a valid card */
- tuple.DesiredTuple = CISTPL_MANFID;
- if ((err=first_tuple(link, &tuple, &parse))) {
+ if (link->has_manf_id == 0) {
printk(KNOT_XIRC "manfid not found in CIS\n");
goto failure;
}
- switch(parse.manfid.manf) {
+ switch (link->manf_id) {
case MANFID_XIRCOM:
local->manf_str = "Xircom";
break;
@@ -817,65 +796,44 @@ xirc2ps_config(struct pcmcia_device * link)
break;
default:
printk(KNOT_XIRC "Unknown Card Manufacturer ID: 0x%04x\n",
- (unsigned)parse.manfid.manf);
+ (unsigned)link->manf_id);
goto failure;
}
- DEBUG(0, "found %s card\n", local->manf_str);
+ dev_dbg(&link->dev, "found %s card\n", local->manf_str);
- if (!set_card_type(link, buf)) {
+ if (!set_card_type(link)) {
printk(KNOT_XIRC "this card is not supported\n");
goto failure;
}
/* get the ethernet address from the CIS */
- tuple.DesiredTuple = CISTPL_FUNCE;
- for (err = first_tuple(link, &tuple, &parse); !err;
- err = next_tuple(link, &tuple, &parse)) {
- /* Once I saw two CISTPL_FUNCE_LAN_NODE_ID entries:
- * the first one with a length of zero the second correct -
- * so I skip all entries with length 0 */
- if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID
- && ((cistpl_lan_node_id_t *)parse.funce.data)->nb)
- break;
- }
- if (err) { /* not found: try to get the node-id from tuple 0x89 */
- tuple.DesiredTuple = 0x89; /* data layout looks like tuple 0x22 */
- if ((err = pcmcia_get_first_tuple(link, &tuple)) == 0 &&
- (err = pcmcia_get_tuple_data(link, &tuple)) == 0) {
- if (tuple.TupleDataLen == 8 && *buf == CISTPL_FUNCE_LAN_NODE_ID)
- memcpy(&parse, buf, 8);
- else
- err = -1;
- }
- }
- if (err) { /* another try (James Lehmer's CE2 version 4.1)*/
- tuple.DesiredTuple = CISTPL_FUNCE;
- for (err = first_tuple(link, &tuple, &parse); !err;
- err = next_tuple(link, &tuple, &parse)) {
- if (parse.funce.type == 0x02 && parse.funce.data[0] == 1
- && parse.funce.data[1] == 6 && tuple.TupleDataLen == 13) {
- buf[1] = 4;
- memcpy(&parse, buf+1, 8);
- break;
+ err = pcmcia_get_mac_from_cis(link, dev);
+
+ /* not found: try to get the node-id from tuple 0x89 */
+ if (err) {
+ len = pcmcia_get_tuple(link, 0x89, &buf);
+ /* data layout looks like tuple 0x22 */
+ if (buf && len == 8) {
+ if (*buf == CISTPL_FUNCE_LAN_NODE_ID) {
+ int i;
+ for (i = 2; i < 6; i++)
+ dev->dev_addr[i] = buf[i+2];
+ } else
+ err = -1;
}
- }
+ kfree(buf);
}
+
+ if (err)
+ err = pcmcia_loop_tuple(link, CISTPL_FUNCE, pcmcia_get_mac_ce, dev);
+
if (err) {
printk(KNOT_XIRC "node-id not found in CIS\n");
goto failure;
}
- node_id = (cistpl_lan_node_id_t *)parse.funce.data;
- if (node_id->nb != 6) {
- printk(KNOT_XIRC "malformed node-id in CIS\n");
- goto failure;
- }
- for (i=0; i < 6; i++)
- dev->dev_addr[i] = node_id->id[i];
link->io.IOAddrLines =10;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- link->irq.Attributes = IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
if (local->modem) {
int pass;
@@ -916,10 +874,8 @@ xirc2ps_config(struct pcmcia_device * link)
goto port_found;
}
link->io.BasePort1 = 0; /* let CS decide */
- if ((err=pcmcia_request_io(link, &link->io))) {
- cs_error(link, RequestIO, err);
+ if ((err=pcmcia_request_io(link, &link->io)))
goto config_error;
- }
}
port_found:
if (err)
@@ -929,19 +885,15 @@ xirc2ps_config(struct pcmcia_device * link)
* Now allocate an interrupt line. Note that this does not
* actually assign a handler to the interrupt.
*/
- if ((err=pcmcia_request_irq(link, &link->irq))) {
- cs_error(link, RequestIRQ, err);
+ if ((err=pcmcia_request_irq(link, &link->irq)))
goto config_error;
- }
/****************
* This actually configures the PCMCIA socket -- setting up
* the I/O windows and the interrupt mapping.
*/
- if ((err=pcmcia_request_configuration(link, &link->conf))) {
- cs_error(link, RequestConfiguration, err);
+ if ((err=pcmcia_request_configuration(link, &link->conf)))
goto config_error;
- }
if (local->dingo) {
conf_reg_t reg;
@@ -956,17 +908,13 @@ xirc2ps_config(struct pcmcia_device * link)
reg.Action = CS_WRITE;
reg.Offset = CISREG_IOBASE_0;
reg.Value = link->io.BasePort2 & 0xff;
- if ((err = pcmcia_access_configuration_register(link, &reg))) {
- cs_error(link, AccessConfigurationRegister, err);
+ if ((err = pcmcia_access_configuration_register(link, &reg)))
goto config_error;
- }
reg.Action = CS_WRITE;
reg.Offset = CISREG_IOBASE_1;
reg.Value = (link->io.BasePort2 >> 8) & 0xff;
- if ((err = pcmcia_access_configuration_register(link, &reg))) {
- cs_error(link, AccessConfigurationRegister, err);
+ if ((err = pcmcia_access_configuration_register(link, &reg)))
goto config_error;
- }
/* There is no config entry for the Ethernet part which
* is at 0x0800. So we allocate a window into the attribute
@@ -975,17 +923,14 @@ xirc2ps_config(struct pcmcia_device * link)
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
req.Base = req.Size = 0;
req.AccessSpeed = 0;
- if ((err = pcmcia_request_window(&link, &req, &link->win))) {
- cs_error(link, RequestWindow, err);
+ if ((err = pcmcia_request_window(link, &req, &link->win)))
goto config_error;
- }
+
local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800;
mem.CardOffset = 0x0;
mem.Page = 0;
- if ((err = pcmcia_map_mem_page(link->win, &mem))) {
- cs_error(link, MapMemPage, err);
+ if ((err = pcmcia_map_mem_page(link, link->win, &mem)))
goto config_error;
- }
/* Setup the CCRs; there are no infos in the CIS about the Ethernet
* part.
@@ -1044,7 +989,7 @@ xirc2ps_config(struct pcmcia_device * link)
do_reset(dev, 1); /* a kludge to make the cem56 work */
link->dev_node = &local->node;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if ((err=register_netdev(dev))) {
printk(KNOT_XIRC "register_netdev() failed\n");
@@ -1077,7 +1022,7 @@ xirc2ps_config(struct pcmcia_device * link)
static void
xirc2ps_release(struct pcmcia_device *link)
{
- DEBUG(0, "release(0x%p)\n", link);
+ dev_dbg(&link->dev, "release\n");
if (link->win) {
struct net_device *dev = link->priv;
@@ -1144,7 +1089,7 @@ xirc2ps_interrupt(int irq, void *dev_id)
PutByte(XIRCREG_CR, 0);
}
- DEBUG(6, "%s: interrupt %d at %#x.\n", dev->name, irq, ioaddr);
+ pr_debug("%s: interrupt %d at %#x.\n", dev->name, irq, ioaddr);
saved_page = GetByte(XIRCREG_PR);
/* Read the ISR to see whats the cause for the interrupt.
@@ -1154,7 +1099,7 @@ xirc2ps_interrupt(int irq, void *dev_id)
bytes_rcvd = 0;
loop_entry:
if (int_status == 0xff) { /* card may be ejected */
- DEBUG(3, "%s: interrupt %d for dead card\n", dev->name, irq);
+ pr_debug("%s: interrupt %d for dead card\n", dev->name, irq);
goto leave;
}
eth_status = GetByte(XIRCREG_ESR);
@@ -1167,7 +1112,7 @@ xirc2ps_interrupt(int irq, void *dev_id)
PutByte(XIRCREG40_TXST0, 0);
PutByte(XIRCREG40_TXST1, 0);
- DEBUG(3, "%s: ISR=%#2.2x ESR=%#2.2x RSR=%#2.2x TSR=%#4.4x\n",
+ pr_debug("%s: ISR=%#2.2x ESR=%#2.2x RSR=%#2.2x TSR=%#4.4x\n",
dev->name, int_status, eth_status, rx_status, tx_status);
/***** receive section ******/
@@ -1178,14 +1123,14 @@ xirc2ps_interrupt(int irq, void *dev_id)
/* too many bytes received during this int, drop the rest of the
* packets */
dev->stats.rx_dropped++;
- DEBUG(2, "%s: RX drop, too much done\n", dev->name);
+ pr_debug("%s: RX drop, too much done\n", dev->name);
} else if (rsr & PktRxOk) {
struct sk_buff *skb;
pktlen = GetWord(XIRCREG0_RBC);
bytes_rcvd += pktlen;
- DEBUG(5, "rsr=%#02x packet_length=%u\n", rsr, pktlen);
+ pr_debug("rsr=%#02x packet_length=%u\n", rsr, pktlen);
skb = dev_alloc_skb(pktlen+3); /* 1 extra so we can use insw */
if (!skb) {
@@ -1253,19 +1198,19 @@ xirc2ps_interrupt(int irq, void *dev_id)
dev->stats.multicast++;
}
} else { /* bad packet */
- DEBUG(5, "rsr=%#02x\n", rsr);
+ pr_debug("rsr=%#02x\n", rsr);
}
if (rsr & PktTooLong) {
dev->stats.rx_frame_errors++;
- DEBUG(3, "%s: Packet too long\n", dev->name);
+ pr_debug("%s: Packet too long\n", dev->name);
}
if (rsr & CRCErr) {
dev->stats.rx_crc_errors++;
- DEBUG(3, "%s: CRC error\n", dev->name);
+ pr_debug("%s: CRC error\n", dev->name);
}
if (rsr & AlignErr) {
dev->stats.rx_fifo_errors++; /* okay ? */
- DEBUG(3, "%s: Alignment error\n", dev->name);
+ pr_debug("%s: Alignment error\n", dev->name);
}
/* clear the received/dropped/error packet */
@@ -1277,7 +1222,7 @@ xirc2ps_interrupt(int irq, void *dev_id)
if (rx_status & 0x10) { /* Receive overrun */
dev->stats.rx_over_errors++;
PutByte(XIRCREG_CR, ClearRxOvrun);
- DEBUG(3, "receive overrun cleared\n");
+ pr_debug("receive overrun cleared\n");
}
/***** transmit section ******/
@@ -1290,13 +1235,13 @@ xirc2ps_interrupt(int irq, void *dev_id)
if (nn < n) /* rollover */
dev->stats.tx_packets += 256 - n;
else if (n == nn) { /* happens sometimes - don't know why */
- DEBUG(0, "PTR not changed?\n");
+ pr_debug("PTR not changed?\n");
} else
dev->stats.tx_packets += lp->last_ptr_value - n;
netif_wake_queue(dev);
}
if (tx_status & 0x0002) { /* Execessive collissions */
- DEBUG(0, "tx restarted due to execssive collissions\n");
+ pr_debug("tx restarted due to execssive collissions\n");
PutByte(XIRCREG_CR, RestartTx); /* restart transmitter process */
}
if (tx_status & 0x0040)
@@ -1315,14 +1260,14 @@ xirc2ps_interrupt(int irq, void *dev_id)
maxrx_bytes = 2000;
else if (maxrx_bytes > 22000)
maxrx_bytes = 22000;
- DEBUG(1, "set maxrx=%u (rcvd=%u ticks=%lu)\n",
+ pr_debug("set maxrx=%u (rcvd=%u ticks=%lu)\n",
maxrx_bytes, bytes_rcvd, duration);
} else if (!duration && maxrx_bytes < 22000) {
/* now much faster */
maxrx_bytes += 2000;
if (maxrx_bytes > 22000)
maxrx_bytes = 22000;
- DEBUG(1, "set maxrx=%u\n", maxrx_bytes);
+ pr_debug("set maxrx=%u\n", maxrx_bytes);
}
}
@@ -1372,7 +1317,7 @@ do_start_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned freespace;
unsigned pktlen = skb->len;
- DEBUG(1, "do_start_xmit(skb=%p, dev=%p) len=%u\n",
+ pr_debug("do_start_xmit(skb=%p, dev=%p) len=%u\n",
skb, dev, pktlen);
@@ -1398,7 +1343,7 @@ do_start_xmit(struct sk_buff *skb, struct net_device *dev)
freespace &= 0x7fff;
/* TRS doesn't work - (indeed it is eliminated with sil-rev 1) */
okay = pktlen +2 < freespace;
- DEBUG(2 + (okay ? 2 : 0), "%s: avail. tx space=%u%s\n",
+ pr_debug("%s: avail. tx space=%u%s\n",
dev->name, freespace, okay ? " (okay)":" (not enough)");
if (!okay) { /* not enough space */
return NETDEV_TX_BUSY; /* upper layer may decide to requeue this packet */
@@ -1500,7 +1445,7 @@ do_config(struct net_device *dev, struct ifmap *map)
{
local_info_t *local = netdev_priv(dev);
- DEBUG(0, "do_config(%p)\n", dev);
+ pr_debug("do_config(%p)\n", dev);
if (map->port != 255 && map->port != dev->if_port) {
if (map->port > 4)
return -EINVAL;
@@ -1527,7 +1472,7 @@ do_open(struct net_device *dev)
local_info_t *lp = netdev_priv(dev);
struct pcmcia_device *link = lp->p_dev;
- DEBUG(0, "do_open(%p)\n", dev);
+ dev_dbg(&link->dev, "do_open(%p)\n", dev);
/* Check that the PCMCIA card is still here. */
/* Physical device present signature. */
@@ -1561,7 +1506,7 @@ do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
unsigned int ioaddr = dev->base_addr;
struct mii_ioctl_data *data = if_mii(rq);
- DEBUG(1, "%s: ioctl(%-.6s, %#04x) %04x %04x %04x %04x\n",
+ pr_debug("%s: ioctl(%-.6s, %#04x) %04x %04x %04x %04x\n",
dev->name, rq->ifr_ifrn.ifrn_name, cmd,
data->phy_id, data->reg_num, data->val_in, data->val_out);
@@ -1610,7 +1555,7 @@ do_reset(struct net_device *dev, int full)
unsigned int ioaddr = dev->base_addr;
unsigned value;
- DEBUG(0, "%s: do_reset(%p,%d)\n", dev? dev->name:"eth?", dev, full);
+ pr_debug("%s: do_reset(%p,%d)\n", dev? dev->name:"eth?", dev, full);
hardreset(dev);
PutByte(XIRCREG_CR, SoftReset); /* set */
@@ -1648,8 +1593,8 @@ do_reset(struct net_device *dev, int full)
}
msleep(40); /* wait 40 msec to let it complete */
- #ifdef PCMCIA_DEBUG
- if (pc_debug) {
+ #if 0
+ {
SelectPage(0);
value = GetByte(XIRCREG_ESR); /* read the ESR */
printk(KERN_DEBUG "%s: ESR is: %#02x\n", dev->name, value);
@@ -1666,7 +1611,7 @@ do_reset(struct net_device *dev, int full)
value |= DisableLinkPulse;
PutByte(XIRCREG1_ECR, value);
#endif
- DEBUG(0, "%s: ECR is: %#02x\n", dev->name, value);
+ pr_debug("%s: ECR is: %#02x\n", dev->name, value);
SelectPage(0x42);
PutByte(XIRCREG42_SWC0, 0x20); /* disable source insertion */
@@ -1844,7 +1789,7 @@ do_powerdown(struct net_device *dev)
unsigned int ioaddr = dev->base_addr;
- DEBUG(0, "do_powerdown(%p)\n", dev);
+ pr_debug("do_powerdown(%p)\n", dev);
SelectPage(4);
PutByte(XIRCREG4_GPR1, 0); /* clear bit 0: power down */
@@ -1858,7 +1803,7 @@ do_stop(struct net_device *dev)
local_info_t *lp = netdev_priv(dev);
struct pcmcia_device *link = lp->p_dev;
- DEBUG(0, "do_stop(%p)\n", dev);
+ dev_dbg(&link->dev, "do_stop(%p)\n", dev);
if (!link)
return -ENODEV;
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index c1b3f09f452..dcc67a35e8f 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1515,8 +1515,8 @@ static void __devinit pcnet32_probe_vlbus(unsigned int *pcnet32_portlist)
if (request_region
(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) {
/* check if there is really a pcnet chip on that ioaddr */
- if ((inb(ioaddr + 14) == 0x57)
- && (inb(ioaddr + 15) == 0x57)) {
+ if ((inb(ioaddr + 14) == 0x57) &&
+ (inb(ioaddr + 15) == 0x57)) {
pcnet32_probe1(ioaddr, 0, NULL);
} else {
release_region(ioaddr, PCNET32_TOTAL_SIZE);
@@ -1610,8 +1610,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
a = &pcnet32_wio;
} else {
pcnet32_dwio_reset(ioaddr);
- if (pcnet32_dwio_read_csr(ioaddr, 0) == 4
- && pcnet32_dwio_check(ioaddr)) {
+ if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 &&
+ pcnet32_dwio_check(ioaddr)) {
a = &pcnet32_dwio;
} else {
if (pcnet32_debug & NETIF_MSG_PROBE)
@@ -1750,8 +1750,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
for (i = 0; i < 6; i++)
promaddr[i] = inb(ioaddr + i);
- if (memcmp(promaddr, dev->dev_addr, 6)
- || !is_valid_ether_addr(dev->dev_addr)) {
+ if (memcmp(promaddr, dev->dev_addr, 6) ||
+ !is_valid_ether_addr(dev->dev_addr)) {
if (is_valid_ether_addr(promaddr)) {
if (pcnet32_debug & NETIF_MSG_PROBE) {
printk(" warning: CSR address invalid,\n");
@@ -1840,8 +1840,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
lp->mii = mii;
lp->chip_version = chip_version;
lp->msg_enable = pcnet32_debug;
- if ((cards_found >= MAX_UNITS)
- || (options[cards_found] >= sizeof(options_mapping)))
+ if ((cards_found >= MAX_UNITS) ||
+ (options[cards_found] >= sizeof(options_mapping)))
lp->options = PCNET32_PORT_ASEL;
else
lp->options = options_mapping[options[cards_found]];
@@ -1866,8 +1866,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
goto err_free_ring;
}
/* detect special T1/E1 WAN card by checking for MAC address */
- if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0
- && dev->dev_addr[2] == 0x75)
+ if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 &&
+ dev->dev_addr[2] == 0x75)
lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
lp->init_block->mode = cpu_to_le16(0x0003); /* Disable Rx and Tx. */
@@ -2095,7 +2095,7 @@ static int pcnet32_open(struct net_device *dev)
int rc;
unsigned long flags;
- if (request_irq(dev->irq, &pcnet32_interrupt,
+ if (request_irq(dev->irq, pcnet32_interrupt,
lp->shared_irq ? IRQF_SHARED : 0, dev->name,
(void *)dev)) {
return -EAGAIN;
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index f81e5322223..f63c96a4ecb 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/phy.h>
+#include <linux/brcmphy.h>
#define PHY_ID_BCM50610 0x0143bd60
#define PHY_ID_BCM50610M 0x0143bd70
@@ -24,6 +25,9 @@
#define BRCM_PHY_MODEL(phydev) \
((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
+#define BRCM_PHY_REV(phydev) \
+ ((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
+
#define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */
#define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */
@@ -94,22 +98,35 @@
#define BCM_LED_SRC_OFF 0xe /* Tied high */
#define BCM_LED_SRC_ON 0xf /* Tied low */
+
/*
* BCM5482: Shadow registers
* Shadow values go into bits [14:10] of register 0x1c to select a shadow
* register to access.
*/
+/* 00101: Spare Control Register 3 */
+#define BCM54XX_SHD_SCR3 0x05
+#define BCM54XX_SHD_SCR3_DEF_CLK125 0x0001
+#define BCM54XX_SHD_SCR3_DLLAPD_DIS 0x0002
+#define BCM54XX_SHD_SCR3_TRDDAPD 0x0004
+
+/* 01010: Auto Power-Down */
+#define BCM54XX_SHD_APD 0x0a
+#define BCM54XX_SHD_APD_EN 0x0020
+
#define BCM5482_SHD_LEDS1 0x0d /* 01101: LED Selector 1 */
/* LED3 / ~LINKSPD[2] selector */
#define BCM5482_SHD_LEDS1_LED3(src) ((src & 0xf) << 4)
/* LED1 / ~LINKSPD[1] selector */
#define BCM5482_SHD_LEDS1_LED1(src) ((src & 0xf) << 0)
+#define BCM54XX_SHD_RGMII_MODE 0x0b /* 01011: RGMII Mode Selector */
#define BCM5482_SHD_SSD 0x14 /* 10100: Secondary SerDes control */
#define BCM5482_SHD_SSD_LEDM 0x0008 /* SSD LED Mode enable */
#define BCM5482_SHD_SSD_EN 0x0001 /* SSD enable */
#define BCM5482_SHD_MODE 0x1f /* 11111: Mode Control Register */
#define BCM5482_SHD_MODE_1000BX 0x0001 /* Enable 1000BASE-X registers */
+
/*
* EXPANSION SHADOW ACCESS REGISTERS. (PHY REG 0x15, 0x16, and 0x17)
*/
@@ -138,16 +155,6 @@
#define BCM5482_SSD_SGMII_SLAVE_EN 0x0002 /* Slave mode enable */
#define BCM5482_SSD_SGMII_SLAVE_AD 0x0001 /* Slave auto-detection */
-/*
- * Device flags for PHYs that can be configured for different operating
- * modes.
- */
-#define PHY_BCM_FLAGS_VALID 0x80000000
-#define PHY_BCM_FLAGS_INTF_XAUI 0x00000020
-#define PHY_BCM_FLAGS_INTF_SGMII 0x00000010
-#define PHY_BCM_FLAGS_MODE_1000BX 0x00000002
-#define PHY_BCM_FLAGS_MODE_COPPER 0x00000001
-
/*****************************************************************************/
/* Fast Ethernet Transceiver definitions. */
@@ -237,53 +244,145 @@ static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
}
+/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
static int bcm50610_a0_workaround(struct phy_device *phydev)
{
int err;
- err = bcm54xx_auxctl_write(phydev,
- MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
- MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
- MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
- if (err < 0)
- return err;
-
- err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08,
- MII_BCM54XX_EXP_EXP08_RJCT_2MHZ |
- MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE);
- if (err < 0)
- goto error;
-
err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH0,
MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
if (err < 0)
- goto error;
+ return err;
err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_AADJ1CH3,
MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
if (err < 0)
- goto error;
+ return err;
err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75,
MII_BCM54XX_EXP_EXP75_VDACCTRL);
if (err < 0)
- goto error;
+ return err;
err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP96,
MII_BCM54XX_EXP_EXP96_MYST);
if (err < 0)
- goto error;
+ return err;
err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP97,
MII_BCM54XX_EXP_EXP97_MYST);
+ return err;
+}
+
+static int bcm54xx_phydsp_config(struct phy_device *phydev)
+{
+ int err, err2;
+
+ /* Enable the SMDSP clock */
+ err = bcm54xx_auxctl_write(phydev,
+ MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
+ MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
+ MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
+ if (err < 0)
+ return err;
+
+ if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
+ BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
+ /* Clear bit 9 to fix a phy interop issue. */
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP08,
+ MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
+ if (err < 0)
+ goto error;
+
+ if (phydev->drv->phy_id == PHY_ID_BCM50610) {
+ err = bcm50610_a0_workaround(phydev);
+ if (err < 0)
+ goto error;
+ }
+ }
+
+ if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
+ int val;
+
+ val = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75);
+ if (val < 0)
+ goto error;
+
+ val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
+ err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, val);
+ }
+
error:
- bcm54xx_auxctl_write(phydev,
- MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
- MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
+ /* Disable the SMDSP clock */
+ err2 = bcm54xx_auxctl_write(phydev,
+ MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
+ MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
- return err;
+ /* Return the first error reported. */
+ return err ? err : err2;
+}
+
+static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
+{
+ u32 val, orig;
+ bool clk125en = true;
+
+ /* Abort if we are using an untested phy. */
+ if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 ||
+ BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 ||
+ BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M)
+ return;
+
+ val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_SCR3);
+ if (val < 0)
+ return;
+
+ orig = val;
+
+ if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
+ BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
+ BRCM_PHY_REV(phydev) >= 0x3) {
+ /*
+ * Here, bit 0 _disables_ CLK125 when set.
+ * This bit is set by default.
+ */
+ clk125en = false;
+ } else {
+ if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
+ /* Here, bit 0 _enables_ CLK125 when set */
+ val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
+ clk125en = false;
+ }
+ }
+
+ if (clk125en == false ||
+ (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
+ val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
+ else
+ val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
+
+ if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY)
+ val |= BCM54XX_SHD_SCR3_TRDDAPD;
+
+ if (orig != val)
+ bcm54xx_shadow_write(phydev, BCM54XX_SHD_SCR3, val);
+
+ val = bcm54xx_shadow_read(phydev, BCM54XX_SHD_APD);
+ if (val < 0)
+ return;
+
+ orig = val;
+
+ if (clk125en == false ||
+ (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
+ val |= BCM54XX_SHD_APD_EN;
+ else
+ val &= ~BCM54XX_SHD_APD_EN;
+
+ if (orig != val)
+ bcm54xx_shadow_write(phydev, BCM54XX_SHD_APD, val);
}
static int bcm54xx_config_init(struct phy_device *phydev)
@@ -308,38 +407,17 @@ static int bcm54xx_config_init(struct phy_device *phydev)
if (err < 0)
return err;
- if (phydev->drv->phy_id == PHY_ID_BCM50610) {
- err = bcm50610_a0_workaround(phydev);
- if (err < 0)
- return err;
- }
-
- if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
- int err2;
-
- err = bcm54xx_auxctl_write(phydev,
- MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
- MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
- MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
- if (err < 0)
- return err;
-
- reg = bcm54xx_exp_read(phydev, MII_BCM54XX_EXP_EXP75);
- if (reg < 0)
- goto error;
+ if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
+ BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
+ (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
+ bcm54xx_shadow_write(phydev, BCM54XX_SHD_RGMII_MODE, 0);
- reg |= MII_BCM54XX_EXP_EXP75_CM_OSC;
- err = bcm54xx_exp_write(phydev, MII_BCM54XX_EXP_EXP75, reg);
+ if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
+ (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
+ (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
+ bcm54xx_adjust_rxrefclk(phydev);
-error:
- err2 = bcm54xx_auxctl_write(phydev,
- MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
- MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
- if (err)
- return err;
- if (err2)
- return err2;
- }
+ bcm54xx_phydsp_config(phydev);
return 0;
}
@@ -564,9 +642,11 @@ static int brcm_fet_config_init(struct phy_device *phydev)
if (err < 0)
goto done;
- /* Enable auto power down */
- err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
- MII_BRCM_FET_SHDW_AS2_APDE);
+ if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
+ /* Enable auto power down */
+ err = brcm_phy_setbits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
+ MII_BRCM_FET_SHDW_AS2_APDE);
+ }
done:
/* Disable shadow register access */
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 6b71b003406..b0e9f9c5172 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -254,12 +254,12 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0)
return -EINVAL;
- if (cmd->autoneg == AUTONEG_DISABLE
- && ((cmd->speed != SPEED_1000
- && cmd->speed != SPEED_100
- && cmd->speed != SPEED_10)
- || (cmd->duplex != DUPLEX_HALF
- && cmd->duplex != DUPLEX_FULL)))
+ if (cmd->autoneg == AUTONEG_DISABLE &&
+ ((cmd->speed != SPEED_1000 &&
+ cmd->speed != SPEED_100 &&
+ cmd->speed != SPEED_10) ||
+ (cmd->duplex != DUPLEX_HALF &&
+ cmd->duplex != DUPLEX_FULL)))
return -EINVAL;
phydev->autoneg = cmd->autoneg;
@@ -353,9 +353,9 @@ int phy_mii_ioctl(struct phy_device *phydev,
phy_write(phydev, mii_data->reg_num, val);
- if (mii_data->reg_num == MII_BMCR
- && val & BMCR_RESET
- && phydev->drv->config_init) {
+ if (mii_data->reg_num == MII_BMCR &&
+ val & BMCR_RESET &&
+ phydev->drv->config_init) {
phy_scan_fixups(phydev);
phydev->drv->config_init(phydev);
}
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index 00487f569cf..3327e9fc7b5 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -372,8 +372,8 @@ plip_bh(struct work_struct *work)
nl->is_deferred = 0;
f = connection_state_table[nl->connection];
- if ((r = (*f)(nl->dev, nl, snd, rcv)) != OK
- && (r = plip_bh_timeout_error(nl->dev, nl, snd, rcv, r)) != OK) {
+ if ((r = (*f)(nl->dev, nl, snd, rcv)) != OK &&
+ (r = plip_bh_timeout_error(nl->dev, nl, snd, rcv, r)) != OK) {
nl->is_deferred = 1;
schedule_delayed_work(&nl->deferred, 1);
}
@@ -416,9 +416,8 @@ plip_bh_timeout_error(struct net_device *dev, struct net_local *nl,
if (error != ERROR) { /* Timeout */
nl->timeout_count++;
- if ((error == HS_TIMEOUT
- && nl->timeout_count <= 10)
- || nl->timeout_count <= 3) {
+ if ((error == HS_TIMEOUT && nl->timeout_count <= 10) ||
+ nl->timeout_count <= 3) {
spin_unlock_irq(&nl->lock);
/* Try again later */
return TIMEOUT;
@@ -624,8 +623,8 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl,
if (plip_receive(nibble_timeout, dev,
&rcv->nibble, &rcv->length.b.msb))
return TIMEOUT;
- if (rcv->length.h > dev->mtu + dev->hard_header_len
- || rcv->length.h < 8) {
+ if (rcv->length.h > dev->mtu + dev->hard_header_len ||
+ rcv->length.h < 8) {
printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h);
return ERROR;
}
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 6de8399d6dd..6a375ea4947 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -36,7 +36,7 @@
#define PPP_VERSION "2.4.2"
-#define OBUFSIZE 256
+#define OBUFSIZE 4096
/* Structure for storing local state. */
struct asyncppp {
@@ -337,10 +337,7 @@ ppp_asynctty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
return 0;
}
-/*
- * This can now be called from hard interrupt level as well
- * as soft interrupt level or mainline.
- */
+/* May sleep, don't call from interrupt level or with interrupts disabled */
static void
ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
char *cflags, int count)
@@ -561,8 +558,8 @@ ppp_async_encode(struct asyncppp *ap)
* Start of a new packet - insert the leading FLAG
* character if necessary.
*/
- if (islcp || flag_time == 0
- || time_after_eq(jiffies, ap->last_xmit + flag_time))
+ if (islcp || flag_time == 0 ||
+ time_after_eq(jiffies, ap->last_xmit + flag_time))
*buf++ = PPP_FLAG;
ap->last_xmit = jiffies;
fcs = PPP_INITFCS;
@@ -699,8 +696,8 @@ ppp_async_push(struct asyncppp *ap)
*/
clear_bit(XMIT_BUSY, &ap->xmit_flags);
/* any more work to do? if not, exit the loop */
- if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
- || (!tty_stuffed && ap->tpkt)))
+ if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) ||
+ (!tty_stuffed && ap->tpkt)))
break;
/* more work to do, see if we can do it now */
if (test_and_set_bit(XMIT_BUSY, &ap->xmit_flags))
@@ -757,8 +754,8 @@ scan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count)
for (i = 0; i < count; ++i) {
c = buf[i];
- if (c == PPP_ESCAPE || c == PPP_FLAG
- || (c < 0x20 && (ap->raccm & (1 << c)) != 0))
+ if (c == PPP_ESCAPE || c == PPP_FLAG ||
+ (c < 0x20 && (ap->raccm & (1 << c)) != 0))
break;
}
return i;
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index 034c1c650bc..695bc83e0cf 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -111,11 +111,11 @@ static void *z_comp_alloc(unsigned char *options, int opt_len)
struct ppp_deflate_state *state;
int w_size;
- if (opt_len != CILEN_DEFLATE
- || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
- || options[1] != CILEN_DEFLATE
- || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
- || options[3] != DEFLATE_CHK_SEQUENCE)
+ if (opt_len != CILEN_DEFLATE ||
+ (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+ options[1] != CILEN_DEFLATE ||
+ DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+ options[3] != DEFLATE_CHK_SEQUENCE)
return NULL;
w_size = DEFLATE_SIZE(options[2]);
if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
@@ -163,12 +163,12 @@ static int z_comp_init(void *arg, unsigned char *options, int opt_len,
{
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
- if (opt_len < CILEN_DEFLATE
- || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
- || options[1] != CILEN_DEFLATE
- || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
- || DEFLATE_SIZE(options[2]) != state->w_size
- || options[3] != DEFLATE_CHK_SEQUENCE)
+ if (opt_len < CILEN_DEFLATE ||
+ (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+ options[1] != CILEN_DEFLATE ||
+ DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+ DEFLATE_SIZE(options[2]) != state->w_size ||
+ options[3] != DEFLATE_CHK_SEQUENCE)
return 0;
state->seqno = 0;
@@ -330,11 +330,11 @@ static void *z_decomp_alloc(unsigned char *options, int opt_len)
struct ppp_deflate_state *state;
int w_size;
- if (opt_len != CILEN_DEFLATE
- || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
- || options[1] != CILEN_DEFLATE
- || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
- || options[3] != DEFLATE_CHK_SEQUENCE)
+ if (opt_len != CILEN_DEFLATE ||
+ (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+ options[1] != CILEN_DEFLATE ||
+ DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+ options[3] != DEFLATE_CHK_SEQUENCE)
return NULL;
w_size = DEFLATE_SIZE(options[2]);
if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
@@ -381,12 +381,12 @@ static int z_decomp_init(void *arg, unsigned char *options, int opt_len,
{
struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
- if (opt_len < CILEN_DEFLATE
- || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT)
- || options[1] != CILEN_DEFLATE
- || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
- || DEFLATE_SIZE(options[2]) != state->w_size
- || options[3] != DEFLATE_CHK_SEQUENCE)
+ if (opt_len < CILEN_DEFLATE ||
+ (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+ options[1] != CILEN_DEFLATE ||
+ DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+ DEFLATE_SIZE(options[2]) != state->w_size ||
+ options[3] != DEFLATE_CHK_SEQUENCE)
return 0;
state->seqno = 0;
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 965adb6174c..2282e729edb 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -184,7 +184,7 @@ static atomic_t ppp_unit_count = ATOMIC_INIT(0);
static atomic_t channel_count = ATOMIC_INIT(0);
/* per-net private data for this module */
-static int ppp_net_id;
+static int ppp_net_id __read_mostly;
struct ppp_net {
/* units to ppp mapping */
struct idr units_idr;
@@ -425,8 +425,8 @@ static ssize_t ppp_read(struct file *file, char __user *buf,
* network traffic (demand mode).
*/
struct ppp *ppp = PF_TO_PPP(pf);
- if (ppp->n_channels == 0
- && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+ if (ppp->n_channels == 0 &&
+ (ppp->flags & SC_LOOP_TRAFFIC) == 0)
break;
}
ret = -EAGAIN;
@@ -511,8 +511,8 @@ static unsigned int ppp_poll(struct file *file, poll_table *wait)
else if (pf->kind == INTERFACE) {
/* see comment in ppp_read */
struct ppp *ppp = PF_TO_PPP(pf);
- if (ppp->n_channels == 0
- && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+ if (ppp->n_channels == 0 &&
+ (ppp->flags & SC_LOOP_TRAFFIC) == 0)
mask |= POLLIN | POLLRDNORM;
}
@@ -864,12 +864,7 @@ static const struct file_operations ppp_device_fops = {
static __net_init int ppp_init_net(struct net *net)
{
- struct ppp_net *pn;
- int err;
-
- pn = kzalloc(sizeof(*pn), GFP_KERNEL);
- if (!pn)
- return -ENOMEM;
+ struct ppp_net *pn = net_generic(net, ppp_net_id);
idr_init(&pn->units_idr);
mutex_init(&pn->all_ppp_mutex);
@@ -879,32 +874,21 @@ static __net_init int ppp_init_net(struct net *net)
spin_lock_init(&pn->all_channels_lock);
- err = net_assign_generic(net, ppp_net_id, pn);
- if (err) {
- kfree(pn);
- return err;
- }
-
return 0;
}
static __net_exit void ppp_exit_net(struct net *net)
{
- struct ppp_net *pn;
+ struct ppp_net *pn = net_generic(net, ppp_net_id);
- pn = net_generic(net, ppp_net_id);
idr_destroy(&pn->units_idr);
- /*
- * if someone has cached our net then
- * further net_generic call will return NULL
- */
- net_assign_generic(net, ppp_net_id, NULL);
- kfree(pn);
}
static struct pernet_operations ppp_net_ops = {
.init = ppp_init_net,
.exit = ppp_exit_net,
+ .id = &ppp_net_id,
+ .size = sizeof(struct ppp_net),
};
#define PPP_MAJOR 108
@@ -917,7 +901,7 @@ static int __init ppp_init(void)
printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
- err = register_pernet_gen_device(&ppp_net_id, &ppp_net_ops);
+ err = register_pernet_device(&ppp_net_ops);
if (err) {
printk(KERN_ERR "failed to register PPP pernet device (%d)\n", err);
goto out;
@@ -943,7 +927,7 @@ static int __init ppp_init(void)
out_chrdev:
unregister_chrdev(PPP_MAJOR, "ppp");
out_net:
- unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops);
+ unregister_pernet_device(&ppp_net_ops);
out:
return err;
}
@@ -1073,8 +1057,8 @@ ppp_xmit_process(struct ppp *ppp)
ppp_xmit_lock(ppp);
if (!ppp->closing) {
ppp_push(ppp);
- while (!ppp->xmit_pending
- && (skb = skb_dequeue(&ppp->file.xq)))
+ while (!ppp->xmit_pending &&
+ (skb = skb_dequeue(&ppp->file.xq)))
ppp_send_frame(ppp, skb);
/* If there's no work left to do, tell the core net
code that we can accept some more. */
@@ -1153,18 +1137,18 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
/* the filter instructions are constructed assuming
a four-byte PPP header on each packet */
*skb_push(skb, 2) = 1;
- if (ppp->pass_filter
- && sk_run_filter(skb, ppp->pass_filter,
- ppp->pass_len) == 0) {
+ if (ppp->pass_filter &&
+ sk_run_filter(skb, ppp->pass_filter,
+ ppp->pass_len) == 0) {
if (ppp->debug & 1)
printk(KERN_DEBUG "PPP: outbound frame not passed\n");
kfree_skb(skb);
return;
}
/* if this packet passes the active filter, record the time */
- if (!(ppp->active_filter
- && sk_run_filter(skb, ppp->active_filter,
- ppp->active_len) == 0))
+ if (!(ppp->active_filter &&
+ sk_run_filter(skb, ppp->active_filter,
+ ppp->active_len) == 0))
ppp->last_xmit = jiffies;
skb_pull(skb, 2);
#else
@@ -1218,8 +1202,8 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
}
/* try to do packet compression */
- if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state
- && proto != PPP_LCP && proto != PPP_CCP) {
+ if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state &&
+ proto != PPP_LCP && proto != PPP_CCP) {
if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) {
if (net_ratelimit())
printk(KERN_ERR "ppp: compression required but down - pkt dropped.\n");
@@ -1593,8 +1577,8 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
/* put it on the channel queue */
skb_queue_tail(&pch->file.rq, skb);
/* drop old frames if queue too long */
- while (pch->file.rq.qlen > PPP_MAX_RQLEN
- && (skb = skb_dequeue(&pch->file.rq)))
+ while (pch->file.rq.qlen > PPP_MAX_RQLEN &&
+ (skb = skb_dequeue(&pch->file.rq)))
kfree_skb(skb);
wake_up_interruptible(&pch->file.rwait);
} else {
@@ -1670,8 +1654,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
* Note that some decompressors need to see uncompressed frames
* that come in as well as compressed frames.
*/
- if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)
- && (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
+ if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN) &&
+ (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
skb = ppp_decompress_frame(ppp, skb);
if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
@@ -1742,8 +1726,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
/* control or unknown frame - pass it to pppd */
skb_queue_tail(&ppp->file.rq, skb);
/* limit queue length by dropping old frames */
- while (ppp->file.rq.qlen > PPP_MAX_RQLEN
- && (skb = skb_dequeue(&ppp->file.rq)))
+ while (ppp->file.rq.qlen > PPP_MAX_RQLEN &&
+ (skb = skb_dequeue(&ppp->file.rq)))
kfree_skb(skb);
/* wake up any process polling or blocking on read */
wake_up_interruptible(&ppp->file.rwait);
@@ -1761,26 +1745,26 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
goto err;
*skb_push(skb, 2) = 0;
- if (ppp->pass_filter
- && sk_run_filter(skb, ppp->pass_filter,
- ppp->pass_len) == 0) {
+ if (ppp->pass_filter &&
+ sk_run_filter(skb, ppp->pass_filter,
+ ppp->pass_len) == 0) {
if (ppp->debug & 1)
printk(KERN_DEBUG "PPP: inbound frame "
"not passed\n");
kfree_skb(skb);
return;
}
- if (!(ppp->active_filter
- && sk_run_filter(skb, ppp->active_filter,
- ppp->active_len) == 0))
+ if (!(ppp->active_filter &&
+ sk_run_filter(skb, ppp->active_filter,
+ ppp->active_len) == 0))
ppp->last_recv = jiffies;
__skb_pull(skb, 2);
} else
#endif /* CONFIG_PPP_FILTER */
ppp->last_recv = jiffies;
- if ((ppp->dev->flags & IFF_UP) == 0
- || ppp->npmode[npi] != NPMODE_PASS) {
+ if ((ppp->dev->flags & IFF_UP) == 0 ||
+ ppp->npmode[npi] != NPMODE_PASS) {
kfree_skb(skb);
} else {
/* chop off protocol */
@@ -2244,13 +2228,13 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
err = -EFAULT;
- if (copy_from_user(&data, (void __user *) arg, sizeof(data))
- || (data.length <= CCP_MAX_OPTION_LENGTH
- && copy_from_user(ccp_option, (void __user *) data.ptr, data.length)))
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)) ||
+ (data.length <= CCP_MAX_OPTION_LENGTH &&
+ copy_from_user(ccp_option, (void __user *) data.ptr, data.length)))
goto out;
err = -EINVAL;
- if (data.length > CCP_MAX_OPTION_LENGTH
- || ccp_option[1] < 2 || ccp_option[1] > data.length)
+ if (data.length > CCP_MAX_OPTION_LENGTH ||
+ ccp_option[1] < 2 || ccp_option[1] > data.length)
goto out;
cp = try_then_request_module(
@@ -2835,7 +2819,7 @@ static void __exit ppp_cleanup(void)
unregister_chrdev(PPP_MAJOR, "ppp");
device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
class_destroy(ppp_class);
- unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops);
+ unregister_pernet_device(&ppp_net_ops);
}
/*
diff --git a/drivers/net/ppp_mppe.c b/drivers/net/ppp_mppe.c
index 88f03c9e940..6d1a1b80cc3 100644
--- a/drivers/net/ppp_mppe.c
+++ b/drivers/net/ppp_mppe.c
@@ -195,8 +195,8 @@ static void *mppe_alloc(unsigned char *options, int optlen)
struct ppp_mppe_state *state;
unsigned int digestsize;
- if (optlen != CILEN_MPPE + sizeof(state->master_key)
- || options[0] != CI_MPPE || options[1] != CILEN_MPPE)
+ if (optlen != CILEN_MPPE + sizeof(state->master_key) ||
+ options[0] != CI_MPPE || options[1] != CILEN_MPPE)
goto out;
state = kzalloc(sizeof(*state), GFP_KERNEL);
@@ -276,8 +276,8 @@ mppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug,
struct ppp_mppe_state *state = (struct ppp_mppe_state *) arg;
unsigned char mppe_opts;
- if (optlen != CILEN_MPPE
- || options[0] != CI_MPPE || options[1] != CILEN_MPPE)
+ if (optlen != CILEN_MPPE ||
+ options[0] != CI_MPPE || options[1] != CILEN_MPPE)
return 0;
MPPE_CI_TO_OPTS(&options[2], mppe_opts);
diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c
index d2fa2db1358..3a13cecae3e 100644
--- a/drivers/net/ppp_synctty.c
+++ b/drivers/net/ppp_synctty.c
@@ -378,10 +378,7 @@ ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
return 0;
}
-/*
- * This can now be called from hard interrupt level as well
- * as soft interrupt level or mainline.
- */
+/* May sleep, don't call from interrupt level or with interrupts disabled */
static void
ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
char *cflags, int count)
@@ -665,8 +662,8 @@ ppp_sync_push(struct syncppp *ap)
}
/* haven't made any progress */
spin_unlock_bh(&ap->xmit_lock);
- if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
- || (!tty_stuffed && ap->tpkt)))
+ if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags) ||
+ (!tty_stuffed && ap->tpkt)))
break;
if (!spin_trylock_bh(&ap->xmit_lock))
break;
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index 2559991eea6..cdd11ba100e 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -97,7 +97,7 @@ static const struct proto_ops pppoe_ops;
static struct ppp_channel_ops pppoe_chan_ops;
/* per-net private data for this module */
-static int pppoe_net_id;
+static int pppoe_net_id __read_mostly;
struct pppoe_net {
/*
* we could use _single_ hash table for all
@@ -250,20 +250,19 @@ static inline struct pppox_sock *get_item_by_addr(struct net *net,
{
struct net_device *dev;
struct pppoe_net *pn;
- struct pppox_sock *pppox_sock;
+ struct pppox_sock *pppox_sock = NULL;
int ifindex;
- dev = dev_get_by_name(net, sp->sa_addr.pppoe.dev);
- if (!dev)
- return NULL;
-
- ifindex = dev->ifindex;
- pn = net_generic(net, pppoe_net_id);
- pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid,
+ rcu_read_lock();
+ dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev);
+ if (dev) {
+ ifindex = dev->ifindex;
+ pn = net_generic(net, pppoe_net_id);
+ pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid,
sp->sa_addr.pppoe.remote, ifindex);
- dev_put(dev);
-
+ }
+ rcu_read_unlock();
return pppox_sock;
}
@@ -324,8 +323,8 @@ static void pppoe_flush_dev(struct net_device *dev)
write_unlock_bh(&pn->hash_lock);
lock_sock(sk);
- if (po->pppoe_dev == dev
- && sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+ if (po->pppoe_dev == dev &&
+ sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
pppox_unbind_sock(sk);
sk->sk_state = PPPOX_ZOMBIE;
sk->sk_state_change(sk);
@@ -1140,59 +1139,37 @@ static struct pppox_proto pppoe_proto = {
static __net_init int pppoe_init_net(struct net *net)
{
- struct pppoe_net *pn;
+ struct pppoe_net *pn = pppoe_pernet(net);
struct proc_dir_entry *pde;
- int err;
-
- pn = kzalloc(sizeof(*pn), GFP_KERNEL);
- if (!pn)
- return -ENOMEM;
rwlock_init(&pn->hash_lock);
- err = net_assign_generic(net, pppoe_net_id, pn);
- if (err)
- goto out;
-
pde = proc_net_fops_create(net, "pppoe", S_IRUGO, &pppoe_seq_fops);
#ifdef CONFIG_PROC_FS
- if (!pde) {
- err = -ENOMEM;
- goto out;
- }
+ if (!pde)
+ return -ENOMEM;
#endif
return 0;
-
-out:
- kfree(pn);
- return err;
}
static __net_exit void pppoe_exit_net(struct net *net)
{
- struct pppoe_net *pn;
-
proc_net_remove(net, "pppoe");
- pn = net_generic(net, pppoe_net_id);
- /*
- * if someone has cached our net then
- * further net_generic call will return NULL
- */
- net_assign_generic(net, pppoe_net_id, NULL);
- kfree(pn);
}
static struct pernet_operations pppoe_net_ops = {
.init = pppoe_init_net,
.exit = pppoe_exit_net,
+ .id = &pppoe_net_id,
+ .size = sizeof(struct pppoe_net),
};
static int __init pppoe_init(void)
{
int err;
- err = register_pernet_gen_device(&pppoe_net_id, &pppoe_net_ops);
+ err = register_pernet_device(&pppoe_net_ops);
if (err)
goto out;
@@ -1213,7 +1190,7 @@ static int __init pppoe_init(void)
out_unregister_pppoe_proto:
proto_unregister(&pppoe_sk_proto);
out_unregister_net_ops:
- unregister_pernet_gen_device(pppoe_net_id, &pppoe_net_ops);
+ unregister_pernet_device(&pppoe_net_ops);
out:
return err;
}
@@ -1225,7 +1202,7 @@ static void __exit pppoe_exit(void)
dev_remove_pack(&pppoes_ptype);
unregister_pppox_proto(PX_PROTO_OE);
proto_unregister(&pppoe_sk_proto);
- unregister_pernet_gen_device(pppoe_net_id, &pppoe_net_ops);
+ unregister_pernet_device(&pppoe_net_ops);
}
module_init(pppoe_init);
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c
index 5910df60c93..9fbb2eba9a0 100644
--- a/drivers/net/pppol2tp.c
+++ b/drivers/net/pppol2tp.c
@@ -232,7 +232,7 @@ static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL };
static const struct proto_ops pppol2tp_ops;
/* per-net private data for this module */
-static int pppol2tp_net_id;
+static int pppol2tp_net_id __read_mostly;
struct pppol2tp_net {
struct list_head pppol2tp_tunnel_list;
rwlock_t pppol2tp_tunnel_list_lock;
@@ -516,7 +516,7 @@ static inline int pppol2tp_verify_udp_checksum(struct sock *sk,
return 0;
inet = inet_sk(sk);
- psum = csum_tcpudp_nofold(inet->saddr, inet->daddr, ulen,
+ psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen,
IPPROTO_UDP, 0);
if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
@@ -949,8 +949,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
inet = inet_sk(sk_tun);
udp_len = hdr_len + sizeof(ppph) + total_len;
uh = (struct udphdr *) skb->data;
- uh->source = inet->sport;
- uh->dest = inet->dport;
+ uh->source = inet->inet_sport;
+ uh->dest = inet->inet_dport;
uh->len = htons(udp_len);
uh->check = 0;
skb_put(skb, sizeof(struct udphdr));
@@ -978,7 +978,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
skb->ip_summed = CHECKSUM_COMPLETE;
csum = skb_checksum(skb, 0, udp_len, 0);
- uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, csum);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
@@ -986,7 +987,8 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
- uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, 0);
}
@@ -1136,8 +1138,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
__skb_push(skb, sizeof(*uh));
skb_reset_transport_header(skb);
uh = udp_hdr(skb);
- uh->source = inet->sport;
- uh->dest = inet->dport;
+ uh->source = inet->inet_sport;
+ uh->dest = inet->inet_dport;
uh->len = htons(udp_len);
uh->check = 0;
@@ -1181,7 +1183,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
skb->ip_summed = CHECKSUM_COMPLETE;
csum = skb_checksum(skb, 0, udp_len, 0);
- uh->check = csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, csum);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
@@ -1189,7 +1192,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
- uh->check = ~csum_tcpudp_magic(inet->saddr, inet->daddr,
+ uh->check = ~csum_tcpudp_magic(inet->inet_saddr,
+ inet->inet_daddr,
udp_len, IPPROTO_UDP, 0);
}
@@ -1533,7 +1537,7 @@ static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net,
* if the tunnel socket goes away.
*/
tunnel->old_sk_destruct = sk->sk_destruct;
- sk->sk_destruct = &pppol2tp_tunnel_destruct;
+ sk->sk_destruct = pppol2tp_tunnel_destruct;
tunnel->sock = sk;
sk->sk_allocation = GFP_ATOMIC;
@@ -2601,53 +2605,31 @@ static struct pppox_proto pppol2tp_proto = {
static __net_init int pppol2tp_init_net(struct net *net)
{
- struct pppol2tp_net *pn;
+ struct pppol2tp_net *pn = pppol2tp_pernet(net);
struct proc_dir_entry *pde;
- int err;
-
- pn = kzalloc(sizeof(*pn), GFP_KERNEL);
- if (!pn)
- return -ENOMEM;
INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list);
rwlock_init(&pn->pppol2tp_tunnel_list_lock);
- err = net_assign_generic(net, pppol2tp_net_id, pn);
- if (err)
- goto out;
-
pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops);
#ifdef CONFIG_PROC_FS
- if (!pde) {
- err = -ENOMEM;
- goto out;
- }
+ if (!pde)
+ return -ENOMEM;
#endif
return 0;
-
-out:
- kfree(pn);
- return err;
}
static __net_exit void pppol2tp_exit_net(struct net *net)
{
- struct pppoe_net *pn;
-
proc_net_remove(net, "pppol2tp");
- pn = net_generic(net, pppol2tp_net_id);
- /*
- * if someone has cached our net then
- * further net_generic call will return NULL
- */
- net_assign_generic(net, pppol2tp_net_id, NULL);
- kfree(pn);
}
static struct pernet_operations pppol2tp_net_ops = {
.init = pppol2tp_init_net,
.exit = pppol2tp_exit_net,
+ .id = &pppol2tp_net_id,
+ .size = sizeof(struct pppol2tp_net),
};
static int __init pppol2tp_init(void)
@@ -2661,7 +2643,7 @@ static int __init pppol2tp_init(void)
if (err)
goto out_unregister_pppol2tp_proto;
- err = register_pernet_gen_device(&pppol2tp_net_id, &pppol2tp_net_ops);
+ err = register_pernet_device(&pppol2tp_net_ops);
if (err)
goto out_unregister_pppox_proto;
@@ -2680,7 +2662,7 @@ out_unregister_pppol2tp_proto:
static void __exit pppol2tp_exit(void)
{
unregister_pppox_proto(PX_PROTO_OL2TP);
- unregister_pernet_gen_device(pppol2tp_net_id, &pppol2tp_net_ops);
+ unregister_pernet_device(&pppol2tp_net_ops);
proto_unregister(&pppol2tp_sk_proto);
}
diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c
index 4f6d33fbc67..ac806b27c65 100644
--- a/drivers/net/pppox.c
+++ b/drivers/net/pppox.c
@@ -104,7 +104,8 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
EXPORT_SYMBOL(pppox_ioctl);
-static int pppox_create(struct net *net, struct socket *sock, int protocol)
+static int pppox_create(struct net *net, struct socket *sock, int protocol,
+ int kern)
{
int rc = -EPROTOTYPE;
@@ -125,7 +126,7 @@ out:
return rc;
}
-static struct net_proto_family pppox_proto_family = {
+static const struct net_proto_family pppox_proto_family = {
.family = PF_PPPOX,
.create = pppox_create,
.owner = THIS_MODULE,
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c
index b211613e9db..0c768593aad 100644
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -95,11 +95,11 @@ static void gelic_card_get_ether_port_status(struct gelic_card *card,
lv1_net_control(bus_id(card), dev_id(card),
GELIC_LV1_GET_ETH_PORT_STATUS,
- GELIC_LV1_VLAN_TX_ETHERNET, 0, 0,
+ GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0,
&card->ether_port_status, &v2);
if (inform) {
- ether_netdev = card->netdev[GELIC_PORT_ETHERNET];
+ ether_netdev = card->netdev[GELIC_PORT_ETHERNET_0];
if (card->ether_port_status & GELIC_LV1_ETHER_LINK_UP)
netif_carrier_on(ether_netdev);
else
@@ -107,6 +107,24 @@ static void gelic_card_get_ether_port_status(struct gelic_card *card,
}
}
+static int gelic_card_set_link_mode(struct gelic_card *card, int mode)
+{
+ int status;
+ u64 v1, v2;
+
+ status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_SET_NEGOTIATION_MODE,
+ GELIC_LV1_PHY_ETHERNET_0, mode, 0, &v1, &v2);
+ if (status) {
+ pr_info("%s: failed setting negotiation mode %d\n", __func__,
+ status);
+ return -EBUSY;
+ }
+
+ card->link_mode = mode;
+ return 0;
+}
+
void gelic_card_up(struct gelic_card *card)
{
pr_debug("%s: called\n", __func__);
@@ -296,7 +314,7 @@ static void gelic_card_reset_chain(struct gelic_card *card,
* @card: card structure
* @descr: descriptor to re-init
*
- * return 0 on succes, <0 on failure
+ * return 0 on success, <0 on failure
*
* allocates a new rx skb, iommu-maps it and attaches it to the descriptor.
* Activate the descriptor state-wise
@@ -451,14 +469,14 @@ static void gelic_descr_release_tx(struct gelic_card *card,
static void gelic_card_stop_queues(struct gelic_card *card)
{
- netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET]);
+ netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET_0]);
if (card->netdev[GELIC_PORT_WIRELESS])
netif_stop_queue(card->netdev[GELIC_PORT_WIRELESS]);
}
static void gelic_card_wake_queues(struct gelic_card *card)
{
- netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET]);
+ netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET_0]);
if (card->netdev[GELIC_PORT_WIRELESS])
netif_wake_queue(card->netdev[GELIC_PORT_WIRELESS]);
@@ -999,7 +1017,7 @@ static int gelic_card_decode_one_descr(struct gelic_card *card)
goto refill;
}
} else
- netdev = card->netdev[GELIC_PORT_ETHERNET];
+ netdev = card->netdev[GELIC_PORT_ETHERNET_0];
if ((status == GELIC_DESCR_DMA_RESPONSE_ERROR) ||
(status == GELIC_DESCR_DMA_PROTECTION_ERROR) ||
@@ -1244,14 +1262,58 @@ static int gelic_ether_get_settings(struct net_device *netdev,
cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg |
SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
- SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full;
+ SUPPORTED_1000baseT_Full;
cmd->advertising = cmd->supported;
- cmd->autoneg = AUTONEG_ENABLE; /* always enabled */
+ if (card->link_mode & GELIC_LV1_ETHER_AUTO_NEG) {
+ cmd->autoneg = AUTONEG_ENABLE;
+ } else {
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->advertising &= ~ADVERTISED_Autoneg;
+ }
cmd->port = PORT_TP;
return 0;
}
+static int gelic_ether_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct gelic_card *card = netdev_card(netdev);
+ u64 mode;
+ int ret;
+
+ if (cmd->autoneg == AUTONEG_ENABLE) {
+ mode = GELIC_LV1_ETHER_AUTO_NEG;
+ } else {
+ switch (cmd->speed) {
+ case SPEED_10:
+ mode = GELIC_LV1_ETHER_SPEED_10;
+ break;
+ case SPEED_100:
+ mode = GELIC_LV1_ETHER_SPEED_100;
+ break;
+ case SPEED_1000:
+ mode = GELIC_LV1_ETHER_SPEED_1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (cmd->duplex == DUPLEX_FULL)
+ mode |= GELIC_LV1_ETHER_FULL_DUPLEX;
+ else if (cmd->speed == SPEED_1000) {
+ pr_info("1000 half duplex is not supported.\n");
+ return -EINVAL;
+ }
+ }
+
+ ret = gelic_card_set_link_mode(card, mode);
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
u32 gelic_net_get_rx_csum(struct net_device *netdev)
{
struct gelic_card *card = netdev_card(netdev);
@@ -1349,6 +1411,7 @@ done:
static const struct ethtool_ops gelic_ether_ethtool_ops = {
.get_drvinfo = gelic_net_get_drvinfo,
.get_settings = gelic_ether_get_settings,
+ .set_settings = gelic_ether_set_settings,
.get_link = ethtool_op_get_link,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = ethtool_op_set_tx_csum,
@@ -1369,7 +1432,7 @@ static void gelic_net_tx_timeout_task(struct work_struct *work)
{
struct gelic_card *card =
container_of(work, struct gelic_card, tx_timeout_task);
- struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET];
+ struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET_0];
dev_info(ctodev(card), "%s:Timed out. Restarting... \n", __func__);
@@ -1531,10 +1594,10 @@ static struct gelic_card * __devinit gelic_alloc_card_net(struct net_device **ne
/* gelic_port */
port->netdev = *netdev;
port->card = card;
- port->type = GELIC_PORT_ETHERNET;
+ port->type = GELIC_PORT_ETHERNET_0;
/* gelic_card */
- card->netdev[GELIC_PORT_ETHERNET] = *netdev;
+ card->netdev[GELIC_PORT_ETHERNET_0] = *netdev;
INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task);
init_waitqueue_head(&card->waitq);
@@ -1554,9 +1617,9 @@ static void __devinit gelic_card_get_vlan_info(struct gelic_card *card)
int tx;
int rx;
} vlan_id_ix[2] = {
- [GELIC_PORT_ETHERNET] = {
- .tx = GELIC_LV1_VLAN_TX_ETHERNET,
- .rx = GELIC_LV1_VLAN_RX_ETHERNET
+ [GELIC_PORT_ETHERNET_0] = {
+ .tx = GELIC_LV1_VLAN_TX_ETHERNET_0,
+ .rx = GELIC_LV1_VLAN_RX_ETHERNET_0
},
[GELIC_PORT_WIRELESS] = {
.tx = GELIC_LV1_VLAN_TX_WIRELESS,
@@ -1601,7 +1664,7 @@ static void __devinit gelic_card_get_vlan_info(struct gelic_card *card)
i, card->vlan[i].tx, card->vlan[i].rx);
}
- if (card->vlan[GELIC_PORT_ETHERNET].tx) {
+ if (card->vlan[GELIC_PORT_ETHERNET_0].tx) {
BUG_ON(!card->vlan[GELIC_PORT_WIRELESS].tx);
card->vlan_required = 1;
} else
@@ -1657,6 +1720,8 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
/* get internal vlan info */
gelic_card_get_vlan_info(card);
+ card->link_mode = GELIC_LV1_ETHER_AUTO_NEG;
+
/* setup interrupt */
result = lv1_net_set_interrupt_status_indicator(bus_id(card),
dev_id(card),
@@ -1773,6 +1838,9 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
struct net_device *netdev0;
pr_debug("%s: called\n", __func__);
+ /* set auto-negotiation */
+ gelic_card_set_link_mode(card, GELIC_LV1_ETHER_AUTO_NEG);
+
#ifdef CONFIG_GELIC_WIRELESS
gelic_wl_driver_remove(card);
#endif
@@ -1790,7 +1858,7 @@ static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
gelic_card_free_chain(card, card->tx_top);
gelic_card_free_chain(card, card->rx_top);
- netdev0 = card->netdev[GELIC_PORT_ETHERNET];
+ netdev0 = card->netdev[GELIC_PORT_ETHERNET_0];
/* disconnect event port */
free_irq(card->irq, card);
netdev0->irq = NO_IRQ;
diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ps3_gelic_net.h
index 8b413868bbe..32521ae5e82 100644
--- a/drivers/net/ps3_gelic_net.h
+++ b/drivers/net/ps3_gelic_net.h
@@ -186,7 +186,7 @@ enum gelic_lv1_net_control_code {
GELIC_LV1_GET_CHANNEL = 6,
GELIC_LV1_POST_WLAN_CMD = 9,
GELIC_LV1_GET_WLAN_CMD_RESULT = 10,
- GELIC_LV1_GET_WLAN_EVENT = 11
+ GELIC_LV1_GET_WLAN_EVENT = 11,
};
/* for GELIC_LV1_SET_WOL */
@@ -217,24 +217,29 @@ enum gelic_lv1_ether_port_status {
GELIC_LV1_ETHER_SPEED_10 = 0x0000000000000010L,
GELIC_LV1_ETHER_SPEED_100 = 0x0000000000000020L,
GELIC_LV1_ETHER_SPEED_1000 = 0x0000000000000040L,
- GELIC_LV1_ETHER_SPEED_MASK = 0x0000000000000070L
+ GELIC_LV1_ETHER_SPEED_MASK = 0x0000000000000070L,
};
enum gelic_lv1_vlan_index {
/* for outgoing packets */
- GELIC_LV1_VLAN_TX_ETHERNET = 0x0000000000000002L,
+ GELIC_LV1_VLAN_TX_ETHERNET_0 = 0x0000000000000002L,
GELIC_LV1_VLAN_TX_WIRELESS = 0x0000000000000003L,
+
/* for incoming packets */
- GELIC_LV1_VLAN_RX_ETHERNET = 0x0000000000000012L,
- GELIC_LV1_VLAN_RX_WIRELESS = 0x0000000000000013L
+ GELIC_LV1_VLAN_RX_ETHERNET_0 = 0x0000000000000012L,
+ GELIC_LV1_VLAN_RX_WIRELESS = 0x0000000000000013L,
+};
+
+enum gelic_lv1_phy {
+ GELIC_LV1_PHY_ETHERNET_0 = 0x0000000000000002L,
};
/* size of hardware part of gelic descriptor */
#define GELIC_DESCR_SIZE (32)
enum gelic_port_type {
- GELIC_PORT_ETHERNET = 0,
- GELIC_PORT_WIRELESS = 1,
+ GELIC_PORT_ETHERNET_0 = 0,
+ GELIC_PORT_WIRELESS = 1,
GELIC_PORT_MAX
};
@@ -302,6 +307,8 @@ struct gelic_card {
atomic_t users;
u64 ether_port_status;
+ int link_mode;
+
/* original address returned by kzalloc */
void *unalign;
diff --git a/drivers/net/ps3_gelic_wireless.h b/drivers/net/ps3_gelic_wireless.h
index 5b631c6c977..0a88b535197 100644
--- a/drivers/net/ps3_gelic_wireless.h
+++ b/drivers/net/ps3_gelic_wireless.h
@@ -199,7 +199,7 @@ struct gelic_eurus_rssi_info {
/* for 'stat' member of gelic_wl_info */
enum gelic_wl_info_status_bit {
GELIC_WL_STAT_CONFIGURED,
- GELIC_WL_STAT_CH_INFO, /* ch info aquired */
+ GELIC_WL_STAT_CH_INFO, /* ch info acquired */
GELIC_WL_STAT_ESSID_SET, /* ESSID specified by userspace */
GELIC_WL_STAT_BSSID_SET, /* BSSID specified by userspace */
GELIC_WL_STAT_WPA_PSK_SET, /* PMK specified by userspace */
diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c
index 4c610511eb4..dd35066a7f8 100644
--- a/drivers/net/qla3xxx.c
+++ b/drivers/net/qla3xxx.c
@@ -1969,8 +1969,8 @@ static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev)
struct ql_rcv_buf_cb *lrg_buf_cb;
struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers;
- if ((qdev->lrg_buf_free_count >= 8)
- && (qdev->lrg_buf_release_cnt >= 16)) {
+ if ((qdev->lrg_buf_free_count >= 8) &&
+ (qdev->lrg_buf_release_cnt >= 16)) {
if (qdev->lrg_buf_skb_check)
if (!ql_populate_free_queue(qdev))
@@ -1978,8 +1978,8 @@ static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev)
lrg_buf_q_ele = qdev->lrg_buf_next_free;
- while ((qdev->lrg_buf_release_cnt >= 16)
- && (qdev->lrg_buf_free_count >= 8)) {
+ while ((qdev->lrg_buf_release_cnt >= 16) &&
+ (qdev->lrg_buf_free_count >= 8)) {
for (i = 0; i < 8; i++) {
lrg_buf_cb =
@@ -3651,7 +3651,7 @@ static int ql_adapter_up(struct ql3_adapter *qdev)
ql_sem_unlock(qdev, QL_DRVR_SEM_MASK);
} else {
printk(KERN_ERR PFX
- "%s: Could not aquire driver lock.\n",
+ "%s: Could not acquire driver lock.\n",
ndev->name);
goto err_lock;
}
diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h
index c2383adcd52..862c1aaf386 100644
--- a/drivers/net/qlge/qlge.h
+++ b/drivers/net/qlge/qlge.h
@@ -16,7 +16,7 @@
*/
#define DRV_NAME "qlge"
#define DRV_STRING "QLogic 10 Gigabit PCI-E Ethernet Driver "
-#define DRV_VERSION "v1.00.00-b3"
+#define DRV_VERSION "v1.00.00.23.00.00-01"
#define PFX "qlge: "
#define QPRINTK(qdev, nlevel, klevel, fmt, args...) \
@@ -54,8 +54,10 @@
#define RX_RING_SHADOW_SPACE (sizeof(u64) + \
MAX_DB_PAGES_PER_BQ(NUM_SMALL_BUFFERS) * sizeof(u64) + \
MAX_DB_PAGES_PER_BQ(NUM_LARGE_BUFFERS) * sizeof(u64))
-#define SMALL_BUFFER_SIZE 256
-#define LARGE_BUFFER_SIZE PAGE_SIZE
+#define SMALL_BUFFER_SIZE 512
+#define SMALL_BUF_MAP_SIZE (SMALL_BUFFER_SIZE / 2)
+#define LARGE_BUFFER_MAX_SIZE 8192
+#define LARGE_BUFFER_MIN_SIZE 2048
#define MAX_SPLIT_SIZE 1023
#define QLGE_SB_PAD 32
@@ -795,6 +797,7 @@ enum {
MB_WOL_BCAST = (1 << 5),
MB_WOL_LINK_UP = (1 << 6),
MB_WOL_LINK_DOWN = (1 << 7),
+ MB_WOL_MODE_ON = (1 << 16), /* Wake on Lan Mode on */
MB_CMD_SET_WOL_FLTR = 0x00000111, /* Wake On Lan Filter */
MB_CMD_CLEAR_WOL_FLTR = 0x00000112, /* Wake On Lan Filter */
MB_CMD_SET_WOL_MAGIC = 0x00000113, /* Wake On Lan Magic Packet */
@@ -804,12 +807,27 @@ enum {
MB_CMD_SET_PORT_CFG = 0x00000122,
MB_CMD_GET_PORT_CFG = 0x00000123,
MB_CMD_GET_LINK_STS = 0x00000124,
+ MB_CMD_SET_LED_CFG = 0x00000125, /* Set LED Configuration Register */
+ QL_LED_BLINK = 0x03e803e8,
+ MB_CMD_GET_LED_CFG = 0x00000126, /* Get LED Configuration Register */
MB_CMD_SET_MGMNT_TFK_CTL = 0x00000160, /* Set Mgmnt Traffic Control */
MB_SET_MPI_TFK_STOP = (1 << 0),
MB_SET_MPI_TFK_RESUME = (1 << 1),
MB_CMD_GET_MGMNT_TFK_CTL = 0x00000161, /* Get Mgmnt Traffic Control */
MB_GET_MPI_TFK_STOPPED = (1 << 0),
MB_GET_MPI_TFK_FIFO_EMPTY = (1 << 1),
+ /* Sub-commands for IDC request.
+ * This describes the reason for the
+ * IDC request.
+ */
+ MB_CMD_IOP_NONE = 0x0000,
+ MB_CMD_IOP_PREP_UPDATE_MPI = 0x0001,
+ MB_CMD_IOP_COMP_UPDATE_MPI = 0x0002,
+ MB_CMD_IOP_PREP_LINK_DOWN = 0x0010,
+ MB_CMD_IOP_DVR_START = 0x0100,
+ MB_CMD_IOP_FLASH_ACC = 0x0101,
+ MB_CMD_IOP_RESTART_MPI = 0x0102,
+ MB_CMD_IOP_CORE_DUMP_MPI = 0x0103,
/* Mailbox Command Status. */
MB_CMD_STS_GOOD = 0x00004000, /* Success. */
@@ -1201,9 +1219,17 @@ struct tx_ring_desc {
struct tx_ring_desc *next;
};
+struct page_chunk {
+ struct page *page; /* master page */
+ char *va; /* virt addr for this chunk */
+ u64 map; /* mapping for master */
+ unsigned int offset; /* offset for this chunk */
+ unsigned int last_flag; /* flag set for last chunk in page */
+};
+
struct bq_desc {
union {
- struct page *lbq_page;
+ struct page_chunk pg_chunk;
struct sk_buff *skb;
} p;
__le64 *addr;
@@ -1237,6 +1263,9 @@ struct tx_ring {
atomic_t queue_stopped; /* Turns queue off when full. */
struct delayed_work tx_work;
struct ql_adapter *qdev;
+ u64 tx_packets;
+ u64 tx_bytes;
+ u64 tx_errors;
};
/*
@@ -1272,6 +1301,7 @@ struct rx_ring {
dma_addr_t lbq_base_dma;
void *lbq_base_indirect;
dma_addr_t lbq_base_indirect_dma;
+ struct page_chunk pg_chunk; /* current page for chunks */
struct bq_desc *lbq; /* array of control blocks */
void __iomem *lbq_prod_idx_db_reg; /* PCI doorbell mem area + 0x18 */
u32 lbq_prod_idx; /* current sw prod idx */
@@ -1302,6 +1332,11 @@ struct rx_ring {
struct napi_struct napi;
u8 reserved;
struct ql_adapter *qdev;
+ u64 rx_packets;
+ u64 rx_multicast;
+ u64 rx_bytes;
+ u64 rx_dropped;
+ u64 rx_errors;
};
/*
@@ -1363,6 +1398,174 @@ struct nic_stats {
u64 rx_1024_to_1518_pkts;
u64 rx_1519_to_max_pkts;
u64 rx_len_err_pkts;
+ /*
+ * These stats come from offset 500h to 5C8h
+ * in the XGMAC register.
+ */
+ u64 tx_cbfc_pause_frames0;
+ u64 tx_cbfc_pause_frames1;
+ u64 tx_cbfc_pause_frames2;
+ u64 tx_cbfc_pause_frames3;
+ u64 tx_cbfc_pause_frames4;
+ u64 tx_cbfc_pause_frames5;
+ u64 tx_cbfc_pause_frames6;
+ u64 tx_cbfc_pause_frames7;
+ u64 rx_cbfc_pause_frames0;
+ u64 rx_cbfc_pause_frames1;
+ u64 rx_cbfc_pause_frames2;
+ u64 rx_cbfc_pause_frames3;
+ u64 rx_cbfc_pause_frames4;
+ u64 rx_cbfc_pause_frames5;
+ u64 rx_cbfc_pause_frames6;
+ u64 rx_cbfc_pause_frames7;
+ u64 rx_nic_fifo_drop;
+};
+
+/* Address/Length pairs for the coredump. */
+enum {
+ MPI_CORE_REGS_ADDR = 0x00030000,
+ MPI_CORE_REGS_CNT = 127,
+ MPI_CORE_SH_REGS_CNT = 16,
+ TEST_REGS_ADDR = 0x00001000,
+ TEST_REGS_CNT = 23,
+ RMII_REGS_ADDR = 0x00001040,
+ RMII_REGS_CNT = 64,
+ FCMAC1_REGS_ADDR = 0x00001080,
+ FCMAC2_REGS_ADDR = 0x000010c0,
+ FCMAC_REGS_CNT = 64,
+ FC1_MBX_REGS_ADDR = 0x00001100,
+ FC2_MBX_REGS_ADDR = 0x00001240,
+ FC_MBX_REGS_CNT = 64,
+ IDE_REGS_ADDR = 0x00001140,
+ IDE_REGS_CNT = 64,
+ NIC1_MBX_REGS_ADDR = 0x00001180,
+ NIC2_MBX_REGS_ADDR = 0x00001280,
+ NIC_MBX_REGS_CNT = 64,
+ SMBUS_REGS_ADDR = 0x00001200,
+ SMBUS_REGS_CNT = 64,
+ I2C_REGS_ADDR = 0x00001fc0,
+ I2C_REGS_CNT = 64,
+ MEMC_REGS_ADDR = 0x00003000,
+ MEMC_REGS_CNT = 256,
+ PBUS_REGS_ADDR = 0x00007c00,
+ PBUS_REGS_CNT = 256,
+ MDE_REGS_ADDR = 0x00010000,
+ MDE_REGS_CNT = 6,
+ CODE_RAM_ADDR = 0x00020000,
+ CODE_RAM_CNT = 0x2000,
+ MEMC_RAM_ADDR = 0x00100000,
+ MEMC_RAM_CNT = 0x2000,
+};
+
+#define MPI_COREDUMP_COOKIE 0x5555aaaa
+struct mpi_coredump_global_header {
+ u32 cookie;
+ u8 idString[16];
+ u32 timeLo;
+ u32 timeHi;
+ u32 imageSize;
+ u32 headerSize;
+ u8 info[220];
+};
+
+struct mpi_coredump_segment_header {
+ u32 cookie;
+ u32 segNum;
+ u32 segSize;
+ u32 extra;
+ u8 description[16];
+};
+
+/* Reg dump segment numbers. */
+enum {
+ CORE_SEG_NUM = 1,
+ TEST_LOGIC_SEG_NUM = 2,
+ RMII_SEG_NUM = 3,
+ FCMAC1_SEG_NUM = 4,
+ FCMAC2_SEG_NUM = 5,
+ FC1_MBOX_SEG_NUM = 6,
+ IDE_SEG_NUM = 7,
+ NIC1_MBOX_SEG_NUM = 8,
+ SMBUS_SEG_NUM = 9,
+ FC2_MBOX_SEG_NUM = 10,
+ NIC2_MBOX_SEG_NUM = 11,
+ I2C_SEG_NUM = 12,
+ MEMC_SEG_NUM = 13,
+ PBUS_SEG_NUM = 14,
+ MDE_SEG_NUM = 15,
+ NIC1_CONTROL_SEG_NUM = 16,
+ NIC2_CONTROL_SEG_NUM = 17,
+ NIC1_XGMAC_SEG_NUM = 18,
+ NIC2_XGMAC_SEG_NUM = 19,
+ WCS_RAM_SEG_NUM = 20,
+ MEMC_RAM_SEG_NUM = 21,
+ XAUI_AN_SEG_NUM = 22,
+ XAUI_HSS_PCS_SEG_NUM = 23,
+ XFI_AN_SEG_NUM = 24,
+ XFI_TRAIN_SEG_NUM = 25,
+ XFI_HSS_PCS_SEG_NUM = 26,
+ XFI_HSS_TX_SEG_NUM = 27,
+ XFI_HSS_RX_SEG_NUM = 28,
+ XFI_HSS_PLL_SEG_NUM = 29,
+ MISC_NIC_INFO_SEG_NUM = 30,
+ INTR_STATES_SEG_NUM = 31,
+ CAM_ENTRIES_SEG_NUM = 32,
+ ROUTING_WORDS_SEG_NUM = 33,
+ ETS_SEG_NUM = 34,
+ PROBE_DUMP_SEG_NUM = 35,
+ ROUTING_INDEX_SEG_NUM = 36,
+ MAC_PROTOCOL_SEG_NUM = 37,
+ XAUI2_AN_SEG_NUM = 38,
+ XAUI2_HSS_PCS_SEG_NUM = 39,
+ XFI2_AN_SEG_NUM = 40,
+ XFI2_TRAIN_SEG_NUM = 41,
+ XFI2_HSS_PCS_SEG_NUM = 42,
+ XFI2_HSS_TX_SEG_NUM = 43,
+ XFI2_HSS_RX_SEG_NUM = 44,
+ XFI2_HSS_PLL_SEG_NUM = 45,
+ SEM_REGS_SEG_NUM = 50
+
+};
+
+struct ql_nic_misc {
+ u32 rx_ring_count;
+ u32 tx_ring_count;
+ u32 intr_count;
+ u32 function;
+};
+
+struct ql_reg_dump {
+
+ /* segment 0 */
+ struct mpi_coredump_global_header mpi_global_header;
+
+ /* segment 16 */
+ struct mpi_coredump_segment_header nic_regs_seg_hdr;
+ u32 nic_regs[64];
+
+ /* segment 30 */
+ struct mpi_coredump_segment_header misc_nic_seg_hdr;
+ struct ql_nic_misc misc_nic_info;
+
+ /* segment 31 */
+ /* one interrupt state for each CQ */
+ struct mpi_coredump_segment_header intr_states_seg_hdr;
+ u32 intr_states[MAX_CPUS];
+
+ /* segment 32 */
+ /* 3 cam words each for 16 unicast,
+ * 2 cam words for each of 32 multicast.
+ */
+ struct mpi_coredump_segment_header cam_entries_seg_hdr;
+ u32 cam_entries[(16 * 3) + (32 * 3)];
+
+ /* segment 33 */
+ struct mpi_coredump_segment_header nic_routing_words_seg_hdr;
+ u32 nic_routing_words[16];
+
+ /* segment 34 */
+ struct mpi_coredump_segment_header ets_seg_hdr;
+ u32 ets[8+2];
};
/*
@@ -1398,6 +1601,8 @@ enum {
QL_ALLMULTI = 6,
QL_PORT_CFG = 7,
QL_CAM_RT_SET = 8,
+ QL_SELFTEST = 9,
+ QL_LB_LINK_UP = 10,
};
/* link_status bit definitions */
@@ -1505,6 +1710,7 @@ struct ql_adapter {
struct rx_ring rx_ring[MAX_RX_RINGS];
struct tx_ring tx_ring[MAX_TX_RINGS];
+ unsigned int lbq_buf_order;
int rx_csum;
u32 default_rx_queue;
@@ -1519,11 +1725,11 @@ struct ql_adapter {
u32 port_init;
u32 link_status;
u32 link_config;
+ u32 led_config;
u32 max_frame_size;
union flash_params flash;
- struct net_device_stats stats;
struct workqueue_struct *workqueue;
struct delayed_work asic_reset_work;
struct delayed_work mpi_reset_work;
@@ -1533,6 +1739,7 @@ struct ql_adapter {
struct completion ide_completion;
struct nic_operations *nic_ops;
u16 device_id;
+ atomic_t lb_count;
};
/*
@@ -1611,10 +1818,22 @@ int ql_mb_get_fw_state(struct ql_adapter *qdev);
int ql_cam_route_initialize(struct ql_adapter *qdev);
int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data);
int ql_mb_about_fw(struct ql_adapter *qdev);
+int ql_wol(struct ql_adapter *qdev);
+int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol);
+int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol);
+int ql_mb_set_led_cfg(struct ql_adapter *qdev, u32 led_config);
+int ql_mb_get_led_cfg(struct ql_adapter *qdev);
void ql_link_on(struct ql_adapter *qdev);
void ql_link_off(struct ql_adapter *qdev);
int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control);
+int ql_mb_get_port_cfg(struct ql_adapter *qdev);
+int ql_mb_set_port_cfg(struct ql_adapter *qdev);
int ql_wait_fifo_empty(struct ql_adapter *qdev);
+void ql_gen_reg_dump(struct ql_adapter *qdev,
+ struct ql_reg_dump *mpi_coredump);
+netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev);
+void ql_check_lb_frame(struct ql_adapter *, struct sk_buff *);
+int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget);
#if 1
#define QL_ALL_DUMP
diff --git a/drivers/net/qlge/qlge_dbg.c b/drivers/net/qlge/qlge_dbg.c
index aa88cb3f41c..9f58c471076 100644
--- a/drivers/net/qlge/qlge_dbg.c
+++ b/drivers/net/qlge/qlge_dbg.c
@@ -1,5 +1,185 @@
#include "qlge.h"
+
+static int ql_get_ets_regs(struct ql_adapter *qdev, u32 * buf)
+{
+ int status = 0;
+ int i;
+
+ for (i = 0; i < 8; i++, buf++) {
+ ql_write32(qdev, NIC_ETS, i << 29 | 0x08000000);
+ *buf = ql_read32(qdev, NIC_ETS);
+ }
+
+ for (i = 0; i < 2; i++, buf++) {
+ ql_write32(qdev, CNA_ETS, i << 29 | 0x08000000);
+ *buf = ql_read32(qdev, CNA_ETS);
+ }
+
+ return status;
+}
+
+static void ql_get_intr_states(struct ql_adapter *qdev, u32 * buf)
+{
+ int i;
+
+ for (i = 0; i < qdev->rx_ring_count; i++, buf++) {
+ ql_write32(qdev, INTR_EN,
+ qdev->intr_context[i].intr_read_mask);
+ *buf = ql_read32(qdev, INTR_EN);
+ }
+}
+
+static int ql_get_cam_entries(struct ql_adapter *qdev, u32 * buf)
+{
+ int i, status;
+ u32 value[3];
+
+ status = ql_sem_spinlock(qdev, SEM_MAC_ADDR_MASK);
+ if (status)
+ return status;
+
+ for (i = 0; i < 16; i++) {
+ status = ql_get_mac_addr_reg(qdev,
+ MAC_ADDR_TYPE_CAM_MAC, i, value);
+ if (status) {
+ QPRINTK(qdev, DRV, ERR,
+ "Failed read of mac index register.\n");
+ goto err;
+ }
+ *buf++ = value[0]; /* lower MAC address */
+ *buf++ = value[1]; /* upper MAC address */
+ *buf++ = value[2]; /* output */
+ }
+ for (i = 0; i < 32; i++) {
+ status = ql_get_mac_addr_reg(qdev,
+ MAC_ADDR_TYPE_MULTI_MAC, i, value);
+ if (status) {
+ QPRINTK(qdev, DRV, ERR,
+ "Failed read of mac index register.\n");
+ goto err;
+ }
+ *buf++ = value[0]; /* lower Mcast address */
+ *buf++ = value[1]; /* upper Mcast address */
+ }
+err:
+ ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK);
+ return status;
+}
+
+static int ql_get_routing_entries(struct ql_adapter *qdev, u32 * buf)
+{
+ int status;
+ u32 value, i;
+
+ status = ql_sem_spinlock(qdev, SEM_RT_IDX_MASK);
+ if (status)
+ return status;
+
+ for (i = 0; i < 16; i++) {
+ status = ql_get_routing_reg(qdev, i, &value);
+ if (status) {
+ QPRINTK(qdev, DRV, ERR,
+ "Failed read of routing index register.\n");
+ goto err;
+ } else {
+ *buf++ = value;
+ }
+ }
+err:
+ ql_sem_unlock(qdev, SEM_RT_IDX_MASK);
+ return status;
+}
+
+/* Create a coredump segment header */
+static void ql_build_coredump_seg_header(
+ struct mpi_coredump_segment_header *seg_hdr,
+ u32 seg_number, u32 seg_size, u8 *desc)
+{
+ memset(seg_hdr, 0, sizeof(struct mpi_coredump_segment_header));
+ seg_hdr->cookie = MPI_COREDUMP_COOKIE;
+ seg_hdr->segNum = seg_number;
+ seg_hdr->segSize = seg_size;
+ memcpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1);
+}
+
+void ql_gen_reg_dump(struct ql_adapter *qdev,
+ struct ql_reg_dump *mpi_coredump)
+{
+ int i, status;
+
+
+ memset(&(mpi_coredump->mpi_global_header), 0,
+ sizeof(struct mpi_coredump_global_header));
+ mpi_coredump->mpi_global_header.cookie = MPI_COREDUMP_COOKIE;
+ mpi_coredump->mpi_global_header.headerSize =
+ sizeof(struct mpi_coredump_global_header);
+ mpi_coredump->mpi_global_header.imageSize =
+ sizeof(struct ql_reg_dump);
+ memcpy(mpi_coredump->mpi_global_header.idString, "MPI Coredump",
+ sizeof(mpi_coredump->mpi_global_header.idString));
+
+
+ /* segment 16 */
+ ql_build_coredump_seg_header(&mpi_coredump->misc_nic_seg_hdr,
+ MISC_NIC_INFO_SEG_NUM,
+ sizeof(struct mpi_coredump_segment_header)
+ + sizeof(mpi_coredump->misc_nic_info),
+ "MISC NIC INFO");
+ mpi_coredump->misc_nic_info.rx_ring_count = qdev->rx_ring_count;
+ mpi_coredump->misc_nic_info.tx_ring_count = qdev->tx_ring_count;
+ mpi_coredump->misc_nic_info.intr_count = qdev->intr_count;
+ mpi_coredump->misc_nic_info.function = qdev->func;
+
+ /* Segment 16, Rev C. Step 18 */
+ ql_build_coredump_seg_header(&mpi_coredump->nic_regs_seg_hdr,
+ NIC1_CONTROL_SEG_NUM,
+ sizeof(struct mpi_coredump_segment_header)
+ + sizeof(mpi_coredump->nic_regs),
+ "NIC Registers");
+ /* Get generic reg dump */
+ for (i = 0; i < 64; i++)
+ mpi_coredump->nic_regs[i] = ql_read32(qdev, i * sizeof(u32));
+
+ /* Segment 31 */
+ /* Get indexed register values. */
+ ql_build_coredump_seg_header(&mpi_coredump->intr_states_seg_hdr,
+ INTR_STATES_SEG_NUM,
+ sizeof(struct mpi_coredump_segment_header)
+ + sizeof(mpi_coredump->intr_states),
+ "INTR States");
+ ql_get_intr_states(qdev, &mpi_coredump->intr_states[0]);
+
+ ql_build_coredump_seg_header(&mpi_coredump->cam_entries_seg_hdr,
+ CAM_ENTRIES_SEG_NUM,
+ sizeof(struct mpi_coredump_segment_header)
+ + sizeof(mpi_coredump->cam_entries),
+ "CAM Entries");
+ status = ql_get_cam_entries(qdev, &mpi_coredump->cam_entries[0]);
+ if (status)
+ return;
+
+ ql_build_coredump_seg_header(&mpi_coredump->nic_routing_words_seg_hdr,
+ ROUTING_WORDS_SEG_NUM,
+ sizeof(struct mpi_coredump_segment_header)
+ + sizeof(mpi_coredump->nic_routing_words),
+ "Routing Words");
+ status = ql_get_routing_entries(qdev,
+ &mpi_coredump->nic_routing_words[0]);
+ if (status)
+ return;
+
+ /* Segment 34 (Rev C. step 23) */
+ ql_build_coredump_seg_header(&mpi_coredump->ets_seg_hdr,
+ ETS_SEG_NUM,
+ sizeof(struct mpi_coredump_segment_header)
+ + sizeof(mpi_coredump->ets),
+ "ETS Registers");
+ status = ql_get_ets_regs(qdev, &mpi_coredump->ets[0]);
+ if (status)
+ return;
+}
+
#ifdef QL_REG_DUMP
static void ql_dump_intr_states(struct ql_adapter *qdev)
{
diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c
index 52073946bce..058fa0a48c6 100644
--- a/drivers/net/qlge/qlge_ethtool.c
+++ b/drivers/net/qlge/qlge_ethtool.c
@@ -36,6 +36,11 @@
#include "qlge.h"
+static const char ql_gstrings_test[][ETH_GSTRING_LEN] = {
+ "Loopback test (offline)"
+};
+#define QLGE_TEST_LEN (sizeof(ql_gstrings_test) / ETH_GSTRING_LEN)
+
static int ql_update_ring_coalescing(struct ql_adapter *qdev)
{
int i, status = 0;
@@ -132,6 +137,41 @@ static void ql_update_stats(struct ql_adapter *qdev)
iter++;
}
+ /*
+ * Get Per-priority TX pause frame counter statistics.
+ */
+ for (i = 0x500; i < 0x540; i += 8) {
+ if (ql_read_xgmac_reg64(qdev, i, &data)) {
+ QPRINTK(qdev, DRV, ERR,
+ "Error reading status register 0x%.04x.\n", i);
+ goto end;
+ } else
+ *iter = data;
+ iter++;
+ }
+
+ /*
+ * Get Per-priority RX pause frame counter statistics.
+ */
+ for (i = 0x568; i < 0x5a8; i += 8) {
+ if (ql_read_xgmac_reg64(qdev, i, &data)) {
+ QPRINTK(qdev, DRV, ERR,
+ "Error reading status register 0x%.04x.\n", i);
+ goto end;
+ } else
+ *iter = data;
+ iter++;
+ }
+
+ /*
+ * Get RX NIC FIFO DROP statistics.
+ */
+ if (ql_read_xgmac_reg64(qdev, 0x5b8, &data)) {
+ QPRINTK(qdev, DRV, ERR,
+ "Error reading status register 0x%.04x.\n", i);
+ goto end;
+ } else
+ *iter = data;
end:
ql_sem_unlock(qdev, qdev->xg_sem_mask);
quit:
@@ -185,6 +225,23 @@ static char ql_stats_str_arr[][ETH_GSTRING_LEN] = {
{"rx_1024_to_1518_pkts"},
{"rx_1519_to_max_pkts"},
{"rx_len_err_pkts"},
+ {"tx_cbfc_pause_frames0"},
+ {"tx_cbfc_pause_frames1"},
+ {"tx_cbfc_pause_frames2"},
+ {"tx_cbfc_pause_frames3"},
+ {"tx_cbfc_pause_frames4"},
+ {"tx_cbfc_pause_frames5"},
+ {"tx_cbfc_pause_frames6"},
+ {"tx_cbfc_pause_frames7"},
+ {"rx_cbfc_pause_frames0"},
+ {"rx_cbfc_pause_frames1"},
+ {"rx_cbfc_pause_frames2"},
+ {"rx_cbfc_pause_frames3"},
+ {"rx_cbfc_pause_frames4"},
+ {"rx_cbfc_pause_frames5"},
+ {"rx_cbfc_pause_frames6"},
+ {"rx_cbfc_pause_frames7"},
+ {"rx_nic_fifo_drop"},
};
static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
@@ -199,6 +256,8 @@ static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
static int ql_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
+ case ETH_SS_TEST:
+ return QLGE_TEST_LEN;
case ETH_SS_STATS:
return ARRAY_SIZE(ql_stats_str_arr);
default:
@@ -257,6 +316,23 @@ ql_get_ethtool_stats(struct net_device *ndev,
*data++ = s->rx_1024_to_1518_pkts;
*data++ = s->rx_1519_to_max_pkts;
*data++ = s->rx_len_err_pkts;
+ *data++ = s->tx_cbfc_pause_frames0;
+ *data++ = s->tx_cbfc_pause_frames1;
+ *data++ = s->tx_cbfc_pause_frames2;
+ *data++ = s->tx_cbfc_pause_frames3;
+ *data++ = s->tx_cbfc_pause_frames4;
+ *data++ = s->tx_cbfc_pause_frames5;
+ *data++ = s->tx_cbfc_pause_frames6;
+ *data++ = s->tx_cbfc_pause_frames7;
+ *data++ = s->rx_cbfc_pause_frames0;
+ *data++ = s->rx_cbfc_pause_frames1;
+ *data++ = s->rx_cbfc_pause_frames2;
+ *data++ = s->rx_cbfc_pause_frames3;
+ *data++ = s->rx_cbfc_pause_frames4;
+ *data++ = s->rx_cbfc_pause_frames5;
+ *data++ = s->rx_cbfc_pause_frames6;
+ *data++ = s->rx_cbfc_pause_frames7;
+ *data++ = s->rx_nic_fifo_drop;
}
static int ql_get_settings(struct net_device *ndev,
@@ -302,6 +378,181 @@ static void ql_get_drvinfo(struct net_device *ndev,
drvinfo->eedump_len = 0;
}
+static void ql_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+ struct ql_adapter *qdev = netdev_priv(ndev);
+ /* What we support. */
+ wol->supported = WAKE_MAGIC;
+ /* What we've currently got set. */
+ wol->wolopts = qdev->wol;
+}
+
+static int ql_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+ struct ql_adapter *qdev = netdev_priv(ndev);
+ int status;
+
+ if (wol->wolopts & ~WAKE_MAGIC)
+ return -EINVAL;
+ qdev->wol = wol->wolopts;
+
+ QPRINTK(qdev, DRV, INFO, "Set wol option 0x%x on %s\n",
+ qdev->wol, ndev->name);
+ if (!qdev->wol) {
+ u32 wol = 0;
+ status = ql_mb_wol_mode(qdev, wol);
+ QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n",
+ (status == 0) ? "cleared sucessfully" : "clear failed",
+ wol, qdev->ndev->name);
+ }
+
+ return 0;
+}
+
+static int ql_phys_id(struct net_device *ndev, u32 data)
+{
+ struct ql_adapter *qdev = netdev_priv(ndev);
+ u32 led_reg, i;
+ int status;
+
+ /* Save the current LED settings */
+ status = ql_mb_get_led_cfg(qdev);
+ if (status)
+ return status;
+ led_reg = qdev->led_config;
+
+ /* Start blinking the led */
+ if (!data || data > 300)
+ data = 300;
+
+ for (i = 0; i < (data * 10); i++)
+ ql_mb_set_led_cfg(qdev, QL_LED_BLINK);
+
+ /* Restore LED settings */
+ status = ql_mb_set_led_cfg(qdev, led_reg);
+ if (status)
+ return status;
+
+ return 0;
+}
+
+static int ql_start_loopback(struct ql_adapter *qdev)
+{
+ if (netif_carrier_ok(qdev->ndev)) {
+ set_bit(QL_LB_LINK_UP, &qdev->flags);
+ netif_carrier_off(qdev->ndev);
+ } else
+ clear_bit(QL_LB_LINK_UP, &qdev->flags);
+ qdev->link_config |= CFG_LOOPBACK_PCS;
+ return ql_mb_set_port_cfg(qdev);
+}
+
+static void ql_stop_loopback(struct ql_adapter *qdev)
+{
+ qdev->link_config &= ~CFG_LOOPBACK_PCS;
+ ql_mb_set_port_cfg(qdev);
+ if (test_bit(QL_LB_LINK_UP, &qdev->flags)) {
+ netif_carrier_on(qdev->ndev);
+ clear_bit(QL_LB_LINK_UP, &qdev->flags);
+ }
+}
+
+static void ql_create_lb_frame(struct sk_buff *skb,
+ unsigned int frame_size)
+{
+ memset(skb->data, 0xFF, frame_size);
+ frame_size &= ~1;
+ memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1);
+ memset(&skb->data[frame_size / 2 + 10], 0xBE, 1);
+ memset(&skb->data[frame_size / 2 + 12], 0xAF, 1);
+}
+
+void ql_check_lb_frame(struct ql_adapter *qdev,
+ struct sk_buff *skb)
+{
+ unsigned int frame_size = skb->len;
+
+ if ((*(skb->data + 3) == 0xFF) &&
+ (*(skb->data + frame_size / 2 + 10) == 0xBE) &&
+ (*(skb->data + frame_size / 2 + 12) == 0xAF)) {
+ atomic_dec(&qdev->lb_count);
+ return;
+ }
+}
+
+static int ql_run_loopback_test(struct ql_adapter *qdev)
+{
+ int i;
+ netdev_tx_t rc;
+ struct sk_buff *skb;
+ unsigned int size = SMALL_BUF_MAP_SIZE;
+
+ for (i = 0; i < 64; i++) {
+ skb = netdev_alloc_skb(qdev->ndev, size);
+ if (!skb)
+ return -ENOMEM;
+
+ skb->queue_mapping = 0;
+ skb_put(skb, size);
+ ql_create_lb_frame(skb, size);
+ rc = ql_lb_send(skb, qdev->ndev);
+ if (rc != NETDEV_TX_OK)
+ return -EPIPE;
+ atomic_inc(&qdev->lb_count);
+ }
+
+ ql_clean_lb_rx_ring(&qdev->rx_ring[0], 128);
+ return atomic_read(&qdev->lb_count) ? -EIO : 0;
+}
+
+static int ql_loopback_test(struct ql_adapter *qdev, u64 *data)
+{
+ *data = ql_start_loopback(qdev);
+ if (*data)
+ goto out;
+ *data = ql_run_loopback_test(qdev);
+out:
+ ql_stop_loopback(qdev);
+ return *data;
+}
+
+static void ql_self_test(struct net_device *ndev,
+ struct ethtool_test *eth_test, u64 *data)
+{
+ struct ql_adapter *qdev = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ set_bit(QL_SELFTEST, &qdev->flags);
+ if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
+ /* Offline tests */
+ if (ql_loopback_test(qdev, &data[0]))
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+
+ } else {
+ /* Online tests */
+ data[0] = 0;
+ }
+ clear_bit(QL_SELFTEST, &qdev->flags);
+ } else {
+ QPRINTK(qdev, DRV, ERR,
+ "%s: is down, Loopback test will fail.\n", ndev->name);
+ eth_test->flags |= ETH_TEST_FL_FAILED;
+ }
+}
+
+static int ql_get_regs_len(struct net_device *ndev)
+{
+ return sizeof(struct ql_reg_dump);
+}
+
+static void ql_get_regs(struct net_device *ndev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct ql_adapter *qdev = netdev_priv(ndev);
+
+ ql_gen_reg_dump(qdev, p);
+}
+
static int ql_get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
{
struct ql_adapter *qdev = netdev_priv(dev);
@@ -355,6 +606,37 @@ static int ql_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *c)
return ql_update_ring_coalescing(qdev);
}
+static void ql_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct ql_adapter *qdev = netdev_priv(netdev);
+
+ ql_mb_get_port_cfg(qdev);
+ if (qdev->link_config & CFG_PAUSE_STD) {
+ pause->rx_pause = 1;
+ pause->tx_pause = 1;
+ }
+}
+
+static int ql_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct ql_adapter *qdev = netdev_priv(netdev);
+ int status = 0;
+
+ if ((pause->rx_pause) && (pause->tx_pause))
+ qdev->link_config |= CFG_PAUSE_STD;
+ else if (!pause->rx_pause && !pause->tx_pause)
+ qdev->link_config &= ~CFG_PAUSE_STD;
+ else
+ return -EINVAL;
+
+ status = ql_mb_set_port_cfg(qdev);
+ if (status)
+ return status;
+ return status;
+}
+
static u32 ql_get_rx_csum(struct net_device *netdev)
{
struct ql_adapter *qdev = netdev_priv(netdev);
@@ -396,9 +678,17 @@ static void ql_set_msglevel(struct net_device *ndev, u32 value)
const struct ethtool_ops qlge_ethtool_ops = {
.get_settings = ql_get_settings,
.get_drvinfo = ql_get_drvinfo,
+ .get_wol = ql_get_wol,
+ .set_wol = ql_set_wol,
+ .get_regs_len = ql_get_regs_len,
+ .get_regs = ql_get_regs,
.get_msglevel = ql_get_msglevel,
.set_msglevel = ql_set_msglevel,
.get_link = ethtool_op_get_link,
+ .phys_id = ql_phys_id,
+ .self_test = ql_self_test,
+ .get_pauseparam = ql_get_pauseparam,
+ .set_pauseparam = ql_set_pauseparam,
.get_rx_csum = ql_get_rx_csum,
.set_rx_csum = ql_set_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c
index a2fc70a0d0c..707b391afa0 100644
--- a/drivers/net/qlge/qlge_main.c
+++ b/drivers/net/qlge/qlge_main.c
@@ -69,9 +69,9 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
#define MSIX_IRQ 0
#define MSI_IRQ 1
#define LEG_IRQ 2
-static int irq_type = MSIX_IRQ;
-module_param(irq_type, int, MSIX_IRQ);
-MODULE_PARM_DESC(irq_type, "0 = MSI-X, 1 = MSI, 2 = Legacy.");
+static int qlge_irq_type = MSIX_IRQ;
+module_param(qlge_irq_type, int, MSIX_IRQ);
+MODULE_PARM_DESC(qlge_irq_type, "0 = MSI-X, 1 = MSI, 2 = Legacy.");
static struct pci_device_id qlge_pci_tbl[] __devinitdata = {
{PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, QLGE_DEVICE_ID_8012)},
@@ -1025,6 +1025,11 @@ end:
return status;
}
+static inline unsigned int ql_lbq_block_size(struct ql_adapter *qdev)
+{
+ return PAGE_SIZE << qdev->lbq_buf_order;
+}
+
/* Get the next large buffer. */
static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring)
{
@@ -1036,6 +1041,28 @@ static struct bq_desc *ql_get_curr_lbuf(struct rx_ring *rx_ring)
return lbq_desc;
}
+static struct bq_desc *ql_get_curr_lchunk(struct ql_adapter *qdev,
+ struct rx_ring *rx_ring)
+{
+ struct bq_desc *lbq_desc = ql_get_curr_lbuf(rx_ring);
+
+ pci_dma_sync_single_for_cpu(qdev->pdev,
+ pci_unmap_addr(lbq_desc, mapaddr),
+ rx_ring->lbq_buf_size,
+ PCI_DMA_FROMDEVICE);
+
+ /* If it's the last chunk of our master page then
+ * we unmap it.
+ */
+ if ((lbq_desc->p.pg_chunk.offset + rx_ring->lbq_buf_size)
+ == ql_lbq_block_size(qdev))
+ pci_unmap_page(qdev->pdev,
+ lbq_desc->p.pg_chunk.map,
+ ql_lbq_block_size(qdev),
+ PCI_DMA_FROMDEVICE);
+ return lbq_desc;
+}
+
/* Get the next small buffer. */
static struct bq_desc *ql_get_curr_sbuf(struct rx_ring *rx_ring)
{
@@ -1063,6 +1090,53 @@ static void ql_write_cq_idx(struct rx_ring *rx_ring)
ql_write_db_reg(rx_ring->cnsmr_idx, rx_ring->cnsmr_idx_db_reg);
}
+static int ql_get_next_chunk(struct ql_adapter *qdev, struct rx_ring *rx_ring,
+ struct bq_desc *lbq_desc)
+{
+ if (!rx_ring->pg_chunk.page) {
+ u64 map;
+ rx_ring->pg_chunk.page = alloc_pages(__GFP_COLD | __GFP_COMP |
+ GFP_ATOMIC,
+ qdev->lbq_buf_order);
+ if (unlikely(!rx_ring->pg_chunk.page)) {
+ QPRINTK(qdev, DRV, ERR,
+ "page allocation failed.\n");
+ return -ENOMEM;
+ }
+ rx_ring->pg_chunk.offset = 0;
+ map = pci_map_page(qdev->pdev, rx_ring->pg_chunk.page,
+ 0, ql_lbq_block_size(qdev),
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(qdev->pdev, map)) {
+ __free_pages(rx_ring->pg_chunk.page,
+ qdev->lbq_buf_order);
+ QPRINTK(qdev, DRV, ERR,
+ "PCI mapping failed.\n");
+ return -ENOMEM;
+ }
+ rx_ring->pg_chunk.map = map;
+ rx_ring->pg_chunk.va = page_address(rx_ring->pg_chunk.page);
+ }
+
+ /* Copy the current master pg_chunk info
+ * to the current descriptor.
+ */
+ lbq_desc->p.pg_chunk = rx_ring->pg_chunk;
+
+ /* Adjust the master page chunk for next
+ * buffer get.
+ */
+ rx_ring->pg_chunk.offset += rx_ring->lbq_buf_size;
+ if (rx_ring->pg_chunk.offset == ql_lbq_block_size(qdev)) {
+ rx_ring->pg_chunk.page = NULL;
+ lbq_desc->p.pg_chunk.last_flag = 1;
+ } else {
+ rx_ring->pg_chunk.va += rx_ring->lbq_buf_size;
+ get_page(rx_ring->pg_chunk.page);
+ lbq_desc->p.pg_chunk.last_flag = 0;
+ }
+ return 0;
+}
/* Process (refill) a large buffer queue. */
static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
{
@@ -1072,39 +1146,28 @@ static void ql_update_lbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
u64 map;
int i;
- while (rx_ring->lbq_free_cnt > 16) {
+ while (rx_ring->lbq_free_cnt > 32) {
for (i = 0; i < 16; i++) {
QPRINTK(qdev, RX_STATUS, DEBUG,
"lbq: try cleaning clean_idx = %d.\n",
clean_idx);
lbq_desc = &rx_ring->lbq[clean_idx];
- if (lbq_desc->p.lbq_page == NULL) {
- QPRINTK(qdev, RX_STATUS, DEBUG,
- "lbq: getting new page for index %d.\n",
- lbq_desc->index);
- lbq_desc->p.lbq_page = alloc_page(GFP_ATOMIC);
- if (lbq_desc->p.lbq_page == NULL) {
- rx_ring->lbq_clean_idx = clean_idx;
- QPRINTK(qdev, RX_STATUS, ERR,
- "Couldn't get a page.\n");
- return;
- }
- map = pci_map_page(qdev->pdev,
- lbq_desc->p.lbq_page,
- 0, PAGE_SIZE,
- PCI_DMA_FROMDEVICE);
- if (pci_dma_mapping_error(qdev->pdev, map)) {
- rx_ring->lbq_clean_idx = clean_idx;
- put_page(lbq_desc->p.lbq_page);
- lbq_desc->p.lbq_page = NULL;
- QPRINTK(qdev, RX_STATUS, ERR,
- "PCI mapping failed.\n");
+ if (ql_get_next_chunk(qdev, rx_ring, lbq_desc)) {
+ QPRINTK(qdev, IFUP, ERR,
+ "Could not get a page chunk.\n");
return;
}
+
+ map = lbq_desc->p.pg_chunk.map +
+ lbq_desc->p.pg_chunk.offset;
pci_unmap_addr_set(lbq_desc, mapaddr, map);
- pci_unmap_len_set(lbq_desc, maplen, PAGE_SIZE);
+ pci_unmap_len_set(lbq_desc, maplen,
+ rx_ring->lbq_buf_size);
*lbq_desc->addr = cpu_to_le64(map);
- }
+
+ pci_dma_sync_single_for_device(qdev->pdev, map,
+ rx_ring->lbq_buf_size,
+ PCI_DMA_FROMDEVICE);
clean_idx++;
if (clean_idx == rx_ring->lbq_len)
clean_idx = 0;
@@ -1147,7 +1210,7 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
sbq_desc->index);
sbq_desc->p.skb =
netdev_alloc_skb(qdev->ndev,
- rx_ring->sbq_buf_size);
+ SMALL_BUFFER_SIZE);
if (sbq_desc->p.skb == NULL) {
QPRINTK(qdev, PROBE, ERR,
"Couldn't get an skb.\n");
@@ -1157,8 +1220,8 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
skb_reserve(sbq_desc->p.skb, QLGE_SB_PAD);
map = pci_map_single(qdev->pdev,
sbq_desc->p.skb->data,
- rx_ring->sbq_buf_size /
- 2, PCI_DMA_FROMDEVICE);
+ rx_ring->sbq_buf_size,
+ PCI_DMA_FROMDEVICE);
if (pci_dma_mapping_error(qdev->pdev, map)) {
QPRINTK(qdev, IFUP, ERR, "PCI mapping failed.\n");
rx_ring->sbq_clean_idx = clean_idx;
@@ -1168,7 +1231,7 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)
}
pci_unmap_addr_set(sbq_desc, mapaddr, map);
pci_unmap_len_set(sbq_desc, maplen,
- rx_ring->sbq_buf_size / 2);
+ rx_ring->sbq_buf_size);
*sbq_desc->addr = cpu_to_le64(map);
}
@@ -1480,27 +1543,24 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
* chain it to the header buffer's skb and let
* it rip.
*/
- lbq_desc = ql_get_curr_lbuf(rx_ring);
- pci_unmap_page(qdev->pdev,
- pci_unmap_addr(lbq_desc,
- mapaddr),
- pci_unmap_len(lbq_desc, maplen),
- PCI_DMA_FROMDEVICE);
+ lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
QPRINTK(qdev, RX_STATUS, DEBUG,
- "Chaining page to skb.\n");
- skb_fill_page_desc(skb, 0, lbq_desc->p.lbq_page,
- 0, length);
+ "Chaining page at offset = %d,"
+ "for %d bytes to skb.\n",
+ lbq_desc->p.pg_chunk.offset, length);
+ skb_fill_page_desc(skb, 0, lbq_desc->p.pg_chunk.page,
+ lbq_desc->p.pg_chunk.offset,
+ length);
skb->len += length;
skb->data_len += length;
skb->truesize += length;
- lbq_desc->p.lbq_page = NULL;
} else {
/*
* The headers and data are in a single large buffer. We
* copy it to a new skb and let it go. This can happen with
* jumbo mtu on a non-TCP/UDP frame.
*/
- lbq_desc = ql_get_curr_lbuf(rx_ring);
+ lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
skb = netdev_alloc_skb(qdev->ndev, length);
if (skb == NULL) {
QPRINTK(qdev, PROBE, DEBUG,
@@ -1515,13 +1575,14 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
skb_reserve(skb, NET_IP_ALIGN);
QPRINTK(qdev, RX_STATUS, DEBUG,
"%d bytes of headers and data in large. Chain page to new skb and pull tail.\n", length);
- skb_fill_page_desc(skb, 0, lbq_desc->p.lbq_page,
- 0, length);
+ skb_fill_page_desc(skb, 0,
+ lbq_desc->p.pg_chunk.page,
+ lbq_desc->p.pg_chunk.offset,
+ length);
skb->len += length;
skb->data_len += length;
skb->truesize += length;
length -= length;
- lbq_desc->p.lbq_page = NULL;
__pskb_pull_tail(skb,
(ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ?
VLAN_ETH_HLEN : ETH_HLEN);
@@ -1538,8 +1599,7 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
* frames. If the MTU goes up we could
* eventually be in trouble.
*/
- int size, offset, i = 0;
- __le64 *bq, bq_array[8];
+ int size, i = 0;
sbq_desc = ql_get_curr_sbuf(rx_ring);
pci_unmap_single(qdev->pdev,
pci_unmap_addr(sbq_desc, mapaddr),
@@ -1558,37 +1618,25 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev,
QPRINTK(qdev, RX_STATUS, DEBUG,
"%d bytes of headers & data in chain of large.\n", length);
skb = sbq_desc->p.skb;
- bq = &bq_array[0];
- memcpy(bq, skb->data, sizeof(bq_array));
sbq_desc->p.skb = NULL;
skb_reserve(skb, NET_IP_ALIGN);
- } else {
- QPRINTK(qdev, RX_STATUS, DEBUG,
- "Headers in small, %d bytes of data in chain of large.\n", length);
- bq = (__le64 *)sbq_desc->p.skb->data;
}
while (length > 0) {
- lbq_desc = ql_get_curr_lbuf(rx_ring);
- pci_unmap_page(qdev->pdev,
- pci_unmap_addr(lbq_desc,
- mapaddr),
- pci_unmap_len(lbq_desc,
- maplen),
- PCI_DMA_FROMDEVICE);
- size = (length < PAGE_SIZE) ? length : PAGE_SIZE;
- offset = 0;
+ lbq_desc = ql_get_curr_lchunk(qdev, rx_ring);
+ size = (length < rx_ring->lbq_buf_size) ? length :
+ rx_ring->lbq_buf_size;
QPRINTK(qdev, RX_STATUS, DEBUG,
"Adding page %d to skb for %d bytes.\n",
i, size);
- skb_fill_page_desc(skb, i, lbq_desc->p.lbq_page,
- offset, size);
+ skb_fill_page_desc(skb, i,
+ lbq_desc->p.pg_chunk.page,
+ lbq_desc->p.pg_chunk.offset,
+ size);
skb->len += size;
skb->data_len += size;
skb->truesize += size;
length -= size;
- lbq_desc->p.lbq_page = NULL;
- bq++;
i++;
}
__pskb_pull_tail(skb, (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ?
@@ -1613,6 +1661,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
if (unlikely(!skb)) {
QPRINTK(qdev, RX_STATUS, DEBUG,
"No skb available, drop packet.\n");
+ rx_ring->rx_dropped++;
return;
}
@@ -1621,6 +1670,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
QPRINTK(qdev, DRV, ERR, "Receive error, flags2 = 0x%x\n",
ib_mac_rsp->flags2);
dev_kfree_skb_any(skb);
+ rx_ring->rx_errors++;
return;
}
@@ -1629,6 +1679,14 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
*/
if (skb->len > ndev->mtu + ETH_HLEN) {
dev_kfree_skb_any(skb);
+ rx_ring->rx_dropped++;
+ return;
+ }
+
+ /* loopback self test for ethtool */
+ if (test_bit(QL_SELFTEST, &qdev->flags)) {
+ ql_check_lb_frame(qdev, skb);
+ dev_kfree_skb_any(skb);
return;
}
@@ -1642,6 +1700,7 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
IB_MAC_IOCB_RSP_M_REG ? "Registered" : "",
(ib_mac_rsp->flags1 & IB_MAC_IOCB_RSP_M_MASK) ==
IB_MAC_IOCB_RSP_M_PROM ? "Promiscuous" : "");
+ rx_ring->rx_multicast++;
}
if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_P) {
QPRINTK(qdev, RX_STATUS, DEBUG, "Promiscuous Packet.\n");
@@ -1673,8 +1732,8 @@ static void ql_process_mac_rx_intr(struct ql_adapter *qdev,
}
}
- qdev->stats.rx_packets++;
- qdev->stats.rx_bytes += skb->len;
+ rx_ring->rx_packets++;
+ rx_ring->rx_bytes += skb->len;
skb_record_rx_queue(skb, rx_ring->cq_id);
if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
if (qdev->vlgrp &&
@@ -1705,8 +1764,8 @@ static void ql_process_mac_tx_intr(struct ql_adapter *qdev,
tx_ring = &qdev->tx_ring[mac_rsp->txq_idx];
tx_ring_desc = &tx_ring->q[mac_rsp->tid];
ql_unmap_send(qdev, tx_ring_desc, tx_ring_desc->map_cnt);
- qdev->stats.tx_bytes += (tx_ring_desc->skb)->len;
- qdev->stats.tx_packets++;
+ tx_ring->tx_bytes += (tx_ring_desc->skb)->len;
+ tx_ring->tx_packets++;
dev_kfree_skb(tx_ring_desc->skb);
tx_ring_desc->skb = NULL;
@@ -1929,7 +1988,7 @@ static int ql_napi_poll_msix(struct napi_struct *napi, int budget)
return work_done;
}
-static void ql_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
+static void qlge_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
{
struct ql_adapter *qdev = netdev_priv(ndev);
@@ -1945,7 +2004,7 @@ static void ql_vlan_rx_register(struct net_device *ndev, struct vlan_group *grp)
}
}
-static void ql_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
+static void qlge_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
{
struct ql_adapter *qdev = netdev_priv(ndev);
u32 enable_bit = MAC_ADDR_E;
@@ -1961,7 +2020,7 @@ static void ql_vlan_rx_add_vid(struct net_device *ndev, u16 vid)
ql_sem_unlock(qdev, SEM_MAC_ADDR_MASK);
}
-static void ql_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
+static void qlge_vlan_rx_kill_vid(struct net_device *ndev, u16 vid)
{
struct ql_adapter *qdev = netdev_priv(ndev);
u32 enable_bit = 0;
@@ -2046,12 +2105,12 @@ static irqreturn_t qlge_isr(int irq, void *dev_id)
*/
var = ql_read32(qdev, ISR1);
if (var & intr_context->irq_mask) {
- QPRINTK(qdev, INTR, INFO,
+ QPRINTK(qdev, INTR, INFO,
"Waking handler for rx_ring[0].\n");
ql_disable_completion_interrupt(qdev, intr_context->intr);
- napi_schedule(&rx_ring->napi);
- work_done++;
- }
+ napi_schedule(&rx_ring->napi);
+ work_done++;
+ }
ql_enable_completion_interrupt(qdev, intr_context->intr);
return work_done ? IRQ_HANDLED : IRQ_NONE;
}
@@ -2149,6 +2208,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev)
__func__, tx_ring_idx);
netif_stop_subqueue(ndev, tx_ring->wq_id);
atomic_inc(&tx_ring->queue_stopped);
+ tx_ring->tx_errors++;
return NETDEV_TX_BUSY;
}
tx_ring_desc = &tx_ring->q[tx_ring->prod_idx];
@@ -2183,6 +2243,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev)
NETDEV_TX_OK) {
QPRINTK(qdev, TX_QUEUED, ERR,
"Could not map the segments.\n");
+ tx_ring->tx_errors++;
return NETDEV_TX_BUSY;
}
QL_DUMP_OB_MAC_IOCB(mac_iocb_ptr);
@@ -2199,6 +2260,7 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev)
return NETDEV_TX_OK;
}
+
static void ql_free_shadow_space(struct ql_adapter *qdev)
{
if (qdev->rx_ring_shadow_reg_area) {
@@ -2285,8 +2347,8 @@ static int ql_alloc_tx_resources(struct ql_adapter *qdev,
pci_alloc_consistent(qdev->pdev, tx_ring->wq_size,
&tx_ring->wq_base_dma);
- if ((tx_ring->wq_base == NULL)
- || tx_ring->wq_base_dma & WQ_ADDR_ALIGN) {
+ if ((tx_ring->wq_base == NULL) ||
+ tx_ring->wq_base_dma & WQ_ADDR_ALIGN) {
QPRINTK(qdev, IFUP, ERR, "tx_ring alloc failed.\n");
return -ENOMEM;
}
@@ -2304,20 +2366,29 @@ err:
static void ql_free_lbq_buffers(struct ql_adapter *qdev, struct rx_ring *rx_ring)
{
- int i;
struct bq_desc *lbq_desc;
- for (i = 0; i < rx_ring->lbq_len; i++) {
- lbq_desc = &rx_ring->lbq[i];
- if (lbq_desc->p.lbq_page) {
+ uint32_t curr_idx, clean_idx;
+
+ curr_idx = rx_ring->lbq_curr_idx;
+ clean_idx = rx_ring->lbq_clean_idx;
+ while (curr_idx != clean_idx) {
+ lbq_desc = &rx_ring->lbq[curr_idx];
+
+ if (lbq_desc->p.pg_chunk.last_flag) {
pci_unmap_page(qdev->pdev,
- pci_unmap_addr(lbq_desc, mapaddr),
- pci_unmap_len(lbq_desc, maplen),
+ lbq_desc->p.pg_chunk.map,
+ ql_lbq_block_size(qdev),
PCI_DMA_FROMDEVICE);
-
- put_page(lbq_desc->p.lbq_page);
- lbq_desc->p.lbq_page = NULL;
+ lbq_desc->p.pg_chunk.last_flag = 0;
}
+
+ put_page(lbq_desc->p.pg_chunk.page);
+ lbq_desc->p.pg_chunk.page = NULL;
+
+ if (++curr_idx == rx_ring->lbq_len)
+ curr_idx = 0;
+
}
}
@@ -2615,6 +2686,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
/* Set up the shadow registers for this ring. */
rx_ring->prod_idx_sh_reg = shadow_reg;
rx_ring->prod_idx_sh_reg_dma = shadow_reg_dma;
+ *rx_ring->prod_idx_sh_reg = 0;
shadow_reg += sizeof(u64);
shadow_reg_dma += sizeof(u64);
rx_ring->lbq_base_indirect = shadow_reg;
@@ -2692,7 +2764,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring)
cqicb->sbq_addr =
cpu_to_le64(rx_ring->sbq_base_indirect_dma);
cqicb->sbq_buf_size =
- cpu_to_le16((u16)(rx_ring->sbq_buf_size/2));
+ cpu_to_le16((u16)(rx_ring->sbq_buf_size));
bq_len = (rx_ring->sbq_len == 65536) ? 0 :
(u16) rx_ring->sbq_len;
cqicb->sbq_len = cpu_to_le16(bq_len);
@@ -2798,7 +2870,7 @@ static void ql_enable_msix(struct ql_adapter *qdev)
int i, err;
/* Get the MSIX vectors. */
- if (irq_type == MSIX_IRQ) {
+ if (qlge_irq_type == MSIX_IRQ) {
/* Try to alloc space for the msix struct,
* if it fails then go to MSI/legacy.
*/
@@ -2806,7 +2878,7 @@ static void ql_enable_msix(struct ql_adapter *qdev)
sizeof(struct msix_entry),
GFP_KERNEL);
if (!qdev->msi_x_entry) {
- irq_type = MSI_IRQ;
+ qlge_irq_type = MSI_IRQ;
goto msi;
}
@@ -2829,7 +2901,7 @@ static void ql_enable_msix(struct ql_adapter *qdev)
QPRINTK(qdev, IFUP, WARNING,
"MSI-X Enable failed, trying MSI.\n");
qdev->intr_count = 1;
- irq_type = MSI_IRQ;
+ qlge_irq_type = MSI_IRQ;
} else if (err == 0) {
set_bit(QL_MSIX_ENABLED, &qdev->flags);
QPRINTK(qdev, IFUP, INFO,
@@ -2840,7 +2912,7 @@ static void ql_enable_msix(struct ql_adapter *qdev)
}
msi:
qdev->intr_count = 1;
- if (irq_type == MSI_IRQ) {
+ if (qlge_irq_type == MSI_IRQ) {
if (!pci_enable_msi(qdev->pdev)) {
set_bit(QL_MSI_ENABLED, &qdev->flags);
QPRINTK(qdev, IFUP, INFO,
@@ -2848,7 +2920,7 @@ msi:
return;
}
}
- irq_type = LEG_IRQ;
+ qlge_irq_type = LEG_IRQ;
QPRINTK(qdev, IFUP, DEBUG, "Running with legacy interrupts.\n");
}
@@ -3268,7 +3340,7 @@ static int ql_adapter_initialize(struct ql_adapter *qdev)
ql_write32(qdev, FSC, mask | value);
ql_write32(qdev, SPLT_HDR, SPLT_HDR_EP |
- min(SMALL_BUFFER_SIZE, MAX_SPLIT_SIZE));
+ min(SMALL_BUF_MAP_SIZE, MAX_SPLIT_SIZE));
/* Set RX packet routing to use port/pci function on which the
* packet arrived on in addition to usual frame routing.
@@ -3276,6 +3348,22 @@ static int ql_adapter_initialize(struct ql_adapter *qdev)
* the same MAC address.
*/
ql_write32(qdev, RST_FO, RST_FO_RR_MASK | RST_FO_RR_RCV_FUNC_CQ);
+ /* Reroute all packets to our Interface.
+ * They may have been routed to MPI firmware
+ * due to WOL.
+ */
+ value = ql_read32(qdev, MGMT_RCV_CFG);
+ value &= ~MGMT_RCV_CFG_RM;
+ mask = 0xffff0000;
+
+ /* Sticky reg needs clearing due to WOL. */
+ ql_write32(qdev, MGMT_RCV_CFG, mask);
+ ql_write32(qdev, MGMT_RCV_CFG, mask | value);
+
+ /* Default WOL is enable on Mezz cards */
+ if (qdev->pdev->subsystem_device == 0x0068 ||
+ qdev->pdev->subsystem_device == 0x0180)
+ qdev->wol = WAKE_MAGIC;
/* Start up the rx queues. */
for (i = 0; i < qdev->rx_ring_count; i++) {
@@ -3310,10 +3398,8 @@ static int ql_adapter_initialize(struct ql_adapter *qdev)
/* Initialize the port and set the max framesize. */
status = qdev->nic_ops->port_initialize(qdev);
- if (status) {
- QPRINTK(qdev, IFUP, ERR, "Failed to start port.\n");
- return status;
- }
+ if (status)
+ QPRINTK(qdev, IFUP, ERR, "Failed to start port.\n");
/* Set up the MAC address and frame routing filter. */
status = ql_cam_route_initialize(qdev);
@@ -3392,6 +3478,52 @@ static void ql_display_dev_info(struct net_device *ndev)
QPRINTK(qdev, PROBE, INFO, "MAC address %pM\n", ndev->dev_addr);
}
+int ql_wol(struct ql_adapter *qdev)
+{
+ int status = 0;
+ u32 wol = MB_WOL_DISABLE;
+
+ /* The CAM is still intact after a reset, but if we
+ * are doing WOL, then we may need to program the
+ * routing regs. We would also need to issue the mailbox
+ * commands to instruct the MPI what to do per the ethtool
+ * settings.
+ */
+
+ if (qdev->wol & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_PHY | WAKE_UCAST |
+ WAKE_MCAST | WAKE_BCAST)) {
+ QPRINTK(qdev, IFDOWN, ERR,
+ "Unsupported WOL paramter. qdev->wol = 0x%x.\n",
+ qdev->wol);
+ return -EINVAL;
+ }
+
+ if (qdev->wol & WAKE_MAGIC) {
+ status = ql_mb_wol_set_magic(qdev, 1);
+ if (status) {
+ QPRINTK(qdev, IFDOWN, ERR,
+ "Failed to set magic packet on %s.\n",
+ qdev->ndev->name);
+ return status;
+ } else
+ QPRINTK(qdev, DRV, INFO,
+ "Enabled magic packet successfully on %s.\n",
+ qdev->ndev->name);
+
+ wol |= MB_WOL_MAGIC_PKT;
+ }
+
+ if (qdev->wol) {
+ wol |= MB_WOL_MODE_ON;
+ status = ql_mb_wol_mode(qdev, wol);
+ QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n",
+ (status == 0) ? "Sucessfully set" : "Failed", wol,
+ qdev->ndev->name);
+ }
+
+ return status;
+}
+
static int ql_adapter_down(struct ql_adapter *qdev)
{
int i, status = 0;
@@ -3497,6 +3629,10 @@ static int ql_configure_rings(struct ql_adapter *qdev)
struct rx_ring *rx_ring;
struct tx_ring *tx_ring;
int cpu_cnt = min(MAX_CPUS, (int)num_online_cpus());
+ unsigned int lbq_buf_len = (qdev->ndev->mtu > 1500) ?
+ LARGE_BUFFER_MAX_SIZE : LARGE_BUFFER_MIN_SIZE;
+
+ qdev->lbq_buf_order = get_order(lbq_buf_len);
/* In a perfect world we have one RSS ring for each CPU
* and each has it's own vector. To do that we ask for
@@ -3544,11 +3680,14 @@ static int ql_configure_rings(struct ql_adapter *qdev)
rx_ring->lbq_len = NUM_LARGE_BUFFERS;
rx_ring->lbq_size =
rx_ring->lbq_len * sizeof(__le64);
- rx_ring->lbq_buf_size = LARGE_BUFFER_SIZE;
+ rx_ring->lbq_buf_size = (u16)lbq_buf_len;
+ QPRINTK(qdev, IFUP, DEBUG,
+ "lbq_buf_size %d, order = %d\n",
+ rx_ring->lbq_buf_size, qdev->lbq_buf_order);
rx_ring->sbq_len = NUM_SMALL_BUFFERS;
rx_ring->sbq_size =
rx_ring->sbq_len * sizeof(__le64);
- rx_ring->sbq_buf_size = SMALL_BUFFER_SIZE * 2;
+ rx_ring->sbq_buf_size = SMALL_BUF_MAP_SIZE;
rx_ring->type = RX_Q;
} else {
/*
@@ -3575,6 +3714,10 @@ static int qlge_open(struct net_device *ndev)
int err = 0;
struct ql_adapter *qdev = netdev_priv(ndev);
+ err = ql_adapter_reset(qdev);
+ if (err)
+ return err;
+
err = ql_configure_rings(qdev);
if (err)
return err;
@@ -3594,14 +3737,63 @@ error_up:
return err;
}
+static int ql_change_rx_buffers(struct ql_adapter *qdev)
+{
+ struct rx_ring *rx_ring;
+ int i, status;
+ u32 lbq_buf_len;
+
+ /* Wait for an oustanding reset to complete. */
+ if (!test_bit(QL_ADAPTER_UP, &qdev->flags)) {
+ int i = 3;
+ while (i-- && !test_bit(QL_ADAPTER_UP, &qdev->flags)) {
+ QPRINTK(qdev, IFUP, ERR,
+ "Waiting for adapter UP...\n");
+ ssleep(1);
+ }
+
+ if (!i) {
+ QPRINTK(qdev, IFUP, ERR,
+ "Timed out waiting for adapter UP\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ status = ql_adapter_down(qdev);
+ if (status)
+ goto error;
+
+ /* Get the new rx buffer size. */
+ lbq_buf_len = (qdev->ndev->mtu > 1500) ?
+ LARGE_BUFFER_MAX_SIZE : LARGE_BUFFER_MIN_SIZE;
+ qdev->lbq_buf_order = get_order(lbq_buf_len);
+
+ for (i = 0; i < qdev->rss_ring_count; i++) {
+ rx_ring = &qdev->rx_ring[i];
+ /* Set the new size. */
+ rx_ring->lbq_buf_size = lbq_buf_len;
+ }
+
+ status = ql_adapter_up(qdev);
+ if (status)
+ goto error;
+
+ return status;
+error:
+ QPRINTK(qdev, IFUP, ALERT,
+ "Driver up/down cycle failed, closing device.\n");
+ set_bit(QL_ADAPTER_UP, &qdev->flags);
+ dev_close(qdev->ndev);
+ return status;
+}
+
static int qlge_change_mtu(struct net_device *ndev, int new_mtu)
{
struct ql_adapter *qdev = netdev_priv(ndev);
+ int status;
if (ndev->mtu == 1500 && new_mtu == 9000) {
QPRINTK(qdev, IFUP, ERR, "Changing to jumbo MTU.\n");
- queue_delayed_work(qdev->workqueue,
- &qdev->mpi_port_cfg_work, 0);
} else if (ndev->mtu == 9000 && new_mtu == 1500) {
QPRINTK(qdev, IFUP, ERR, "Changing to normal MTU.\n");
} else if ((ndev->mtu == 1500 && new_mtu == 1500) ||
@@ -3609,15 +3801,60 @@ static int qlge_change_mtu(struct net_device *ndev, int new_mtu)
return 0;
} else
return -EINVAL;
+
+ queue_delayed_work(qdev->workqueue,
+ &qdev->mpi_port_cfg_work, 3*HZ);
+
+ if (!netif_running(qdev->ndev)) {
+ ndev->mtu = new_mtu;
+ return 0;
+ }
+
ndev->mtu = new_mtu;
- return 0;
+ status = ql_change_rx_buffers(qdev);
+ if (status) {
+ QPRINTK(qdev, IFUP, ERR,
+ "Changing MTU failed.\n");
+ }
+
+ return status;
}
static struct net_device_stats *qlge_get_stats(struct net_device
*ndev)
{
struct ql_adapter *qdev = netdev_priv(ndev);
- return &qdev->stats;
+ struct rx_ring *rx_ring = &qdev->rx_ring[0];
+ struct tx_ring *tx_ring = &qdev->tx_ring[0];
+ unsigned long pkts, mcast, dropped, errors, bytes;
+ int i;
+
+ /* Get RX stats. */
+ pkts = mcast = dropped = errors = bytes = 0;
+ for (i = 0; i < qdev->rss_ring_count; i++, rx_ring++) {
+ pkts += rx_ring->rx_packets;
+ bytes += rx_ring->rx_bytes;
+ dropped += rx_ring->rx_dropped;
+ errors += rx_ring->rx_errors;
+ mcast += rx_ring->rx_multicast;
+ }
+ ndev->stats.rx_packets = pkts;
+ ndev->stats.rx_bytes = bytes;
+ ndev->stats.rx_dropped = dropped;
+ ndev->stats.rx_errors = errors;
+ ndev->stats.multicast = mcast;
+
+ /* Get TX stats. */
+ pkts = errors = bytes = 0;
+ for (i = 0; i < qdev->tx_ring_count; i++, tx_ring++) {
+ pkts += tx_ring->tx_packets;
+ bytes += tx_ring->tx_bytes;
+ errors += tx_ring->tx_errors;
+ }
+ ndev->stats.tx_packets = pkts;
+ ndev->stats.tx_bytes = bytes;
+ ndev->stats.tx_errors = errors;
+ return &ndev->stats;
}
static void qlge_set_multicast_list(struct net_device *ndev)
@@ -3714,9 +3951,6 @@ static int qlge_set_mac_address(struct net_device *ndev, void *p)
struct sockaddr *addr = p;
int status;
- if (netif_running(ndev))
- return -EBUSY;
-
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
@@ -3868,8 +4102,7 @@ static int __devinit ql_init_device(struct pci_dev *pdev,
struct net_device *ndev, int cards_found)
{
struct ql_adapter *qdev = netdev_priv(ndev);
- int pos, err = 0;
- u16 val16;
+ int err = 0;
memset((void *)qdev, 0, sizeof(*qdev));
err = pci_enable_device(pdev);
@@ -3881,18 +4114,12 @@ static int __devinit ql_init_device(struct pci_dev *pdev,
qdev->ndev = ndev;
qdev->pdev = pdev;
pci_set_drvdata(pdev, ndev);
- pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
- if (pos <= 0) {
- dev_err(&pdev->dev, PFX "Cannot find PCI Express capability, "
- "aborting.\n");
- return pos;
- } else {
- pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16);
- val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN;
- val16 |= (PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
- pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
+
+ /* Set PCIe read request size */
+ err = pcie_set_readrq(pdev, 4096);
+ if (err) {
+ dev_err(&pdev->dev, "Set readrq failed.\n");
+ goto err_out;
}
err = pci_request_regions(pdev, DRV_NAME);
@@ -3991,7 +4218,6 @@ err_out:
return err;
}
-
static const struct net_device_ops qlge_netdev_ops = {
.ndo_open = qlge_open,
.ndo_stop = qlge_close,
@@ -4002,9 +4228,9 @@ static const struct net_device_ops qlge_netdev_ops = {
.ndo_set_mac_address = qlge_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = qlge_tx_timeout,
- .ndo_vlan_rx_register = ql_vlan_rx_register,
- .ndo_vlan_rx_add_vid = ql_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = ql_vlan_rx_kill_vid,
+ .ndo_vlan_rx_register = qlge_vlan_rx_register,
+ .ndo_vlan_rx_add_vid = qlge_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = qlge_vlan_rx_kill_vid,
};
static int __devinit qlge_probe(struct pci_dev *pdev,
@@ -4060,10 +4286,21 @@ static int __devinit qlge_probe(struct pci_dev *pdev,
}
ql_link_off(qdev);
ql_display_dev_info(ndev);
+ atomic_set(&qdev->lb_count, 0);
cards_found++;
return 0;
}
+netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev)
+{
+ return qlge_send(skb, ndev);
+}
+
+int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget)
+{
+ return ql_clean_inbound_rx_ring(rx_ring, budget);
+}
+
static void __devexit qlge_remove(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
@@ -4193,6 +4430,7 @@ static int qlge_suspend(struct pci_dev *pdev, pm_message_t state)
return err;
}
+ ql_wol(qdev);
err = pci_save_state(pdev);
if (err)
return err;
diff --git a/drivers/net/qlge/qlge_mpi.c b/drivers/net/qlge/qlge_mpi.c
index aec05f26610..e2b2286102d 100644
--- a/drivers/net/qlge/qlge_mpi.c
+++ b/drivers/net/qlge/qlge_mpi.c
@@ -1,25 +1,5 @@
#include "qlge.h"
-static void ql_display_mb_sts(struct ql_adapter *qdev,
- struct mbox_params *mbcp)
-{
- int i;
- static char *err_sts[] = {
- "Command Complete",
- "Command Not Supported",
- "Host Interface Error",
- "Checksum Error",
- "Unused Completion Status",
- "Test Failed",
- "Command Parameter Error"};
-
- QPRINTK(qdev, DRV, DEBUG, "%s.\n",
- err_sts[mbcp->mbox_out[0] & 0x0000000f]);
- for (i = 0; i < mbcp->out_count; i++)
- QPRINTK(qdev, DRV, DEBUG, "mbox_out[%d] = 0x%.08x.\n",
- i, mbcp->mbox_out[i]);
-}
-
int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data)
{
int status;
@@ -317,6 +297,7 @@ static void ql_init_fw_done(struct ql_adapter *qdev, struct mbox_params *mbcp)
} else {
QPRINTK(qdev, DRV, ERR, "Firmware Revision = 0x%.08x.\n",
mbcp->mbox_out[1]);
+ qdev->fw_rev_id = mbcp->mbox_out[1];
status = ql_cam_route_initialize(qdev);
if (status)
QPRINTK(qdev, IFUP, ERR,
@@ -446,6 +427,9 @@ static int ql_mpi_handler(struct ql_adapter *qdev, struct mbox_params *mbcp)
ql_aen_lost(qdev, mbcp);
break;
+ case AEN_DCBX_CHG:
+ /* Need to support AEN 8110 */
+ break;
default:
QPRINTK(qdev, DRV, ERR,
"Unsupported AE %.08x.\n", mbcp->mbox_out[0]);
@@ -537,7 +521,6 @@ done:
MB_CMD_STS_GOOD) &&
((mbcp->mbox_out[0] & 0x0000f000) !=
MB_CMD_STS_INTRMDT)) {
- ql_display_mb_sts(qdev, mbcp);
status = -EIO;
}
end:
@@ -655,7 +638,7 @@ int ql_mb_idc_ack(struct ql_adapter *qdev)
* for the current port.
* Most likely will block.
*/
-static int ql_mb_set_port_cfg(struct ql_adapter *qdev)
+int ql_mb_set_port_cfg(struct ql_adapter *qdev)
{
struct mbox_params mbc;
struct mbox_params *mbcp = &mbc;
@@ -690,7 +673,7 @@ static int ql_mb_set_port_cfg(struct ql_adapter *qdev)
* for the current port.
* Most likely will block.
*/
-static int ql_mb_get_port_cfg(struct ql_adapter *qdev)
+int ql_mb_get_port_cfg(struct ql_adapter *qdev)
{
struct mbox_params mbc;
struct mbox_params *mbcp = &mbc;
@@ -720,6 +703,76 @@ static int ql_mb_get_port_cfg(struct ql_adapter *qdev)
return status;
}
+int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol)
+{
+ struct mbox_params mbc;
+ struct mbox_params *mbcp = &mbc;
+ int status;
+
+ memset(mbcp, 0, sizeof(struct mbox_params));
+
+ mbcp->in_count = 2;
+ mbcp->out_count = 1;
+
+ mbcp->mbox_in[0] = MB_CMD_SET_WOL_MODE;
+ mbcp->mbox_in[1] = wol;
+
+
+ status = ql_mailbox_command(qdev, mbcp);
+ if (status)
+ return status;
+
+ if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) {
+ QPRINTK(qdev, DRV, ERR,
+ "Failed to set WOL mode.\n");
+ status = -EIO;
+ }
+ return status;
+}
+
+int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol)
+{
+ struct mbox_params mbc;
+ struct mbox_params *mbcp = &mbc;
+ int status;
+ u8 *addr = qdev->ndev->dev_addr;
+
+ memset(mbcp, 0, sizeof(struct mbox_params));
+
+ mbcp->in_count = 8;
+ mbcp->out_count = 1;
+
+ mbcp->mbox_in[0] = MB_CMD_SET_WOL_MAGIC;
+ if (enable_wol) {
+ mbcp->mbox_in[1] = (u32)addr[0];
+ mbcp->mbox_in[2] = (u32)addr[1];
+ mbcp->mbox_in[3] = (u32)addr[2];
+ mbcp->mbox_in[4] = (u32)addr[3];
+ mbcp->mbox_in[5] = (u32)addr[4];
+ mbcp->mbox_in[6] = (u32)addr[5];
+ mbcp->mbox_in[7] = 0;
+ } else {
+ mbcp->mbox_in[1] = 0;
+ mbcp->mbox_in[2] = 1;
+ mbcp->mbox_in[3] = 1;
+ mbcp->mbox_in[4] = 1;
+ mbcp->mbox_in[5] = 1;
+ mbcp->mbox_in[6] = 1;
+ mbcp->mbox_in[7] = 0;
+ }
+
+ status = ql_mailbox_command(qdev, mbcp);
+ if (status)
+ return status;
+
+ if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) {
+ QPRINTK(qdev, DRV, ERR,
+ "Failed to set WOL mode.\n");
+ status = -EIO;
+ }
+ return status;
+}
+
/* IDC - Inter Device Communication...
* Some firmware commands require consent of adjacent FCOE
* function. This function waits for the OK, or a
@@ -769,6 +822,61 @@ static int ql_idc_wait(struct ql_adapter *qdev)
return status;
}
+int ql_mb_set_led_cfg(struct ql_adapter *qdev, u32 led_config)
+{
+ struct mbox_params mbc;
+ struct mbox_params *mbcp = &mbc;
+ int status;
+
+ memset(mbcp, 0, sizeof(struct mbox_params));
+
+ mbcp->in_count = 2;
+ mbcp->out_count = 1;
+
+ mbcp->mbox_in[0] = MB_CMD_SET_LED_CFG;
+ mbcp->mbox_in[1] = led_config;
+
+
+ status = ql_mailbox_command(qdev, mbcp);
+ if (status)
+ return status;
+
+ if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) {
+ QPRINTK(qdev, DRV, ERR,
+ "Failed to set LED Configuration.\n");
+ status = -EIO;
+ }
+
+ return status;
+}
+
+int ql_mb_get_led_cfg(struct ql_adapter *qdev)
+{
+ struct mbox_params mbc;
+ struct mbox_params *mbcp = &mbc;
+ int status;
+
+ memset(mbcp, 0, sizeof(struct mbox_params));
+
+ mbcp->in_count = 1;
+ mbcp->out_count = 2;
+
+ mbcp->mbox_in[0] = MB_CMD_GET_LED_CFG;
+
+ status = ql_mailbox_command(qdev, mbcp);
+ if (status)
+ return status;
+
+ if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) {
+ QPRINTK(qdev, DRV, ERR,
+ "Failed to get LED Configuration.\n");
+ status = -EIO;
+ } else
+ qdev->led_config = mbcp->mbox_out[1];
+
+ return status;
+}
+
int ql_mb_set_mgmnt_traffic_ctl(struct ql_adapter *qdev, u32 control)
{
struct mbox_params mbc;
@@ -930,8 +1038,11 @@ void ql_mpi_idc_work(struct work_struct *work)
int status;
struct mbox_params *mbcp = &qdev->idc_mbc;
u32 aen;
+ int timeout;
+ rtnl_lock();
aen = mbcp->mbox_out[1] >> 16;
+ timeout = (mbcp->mbox_out[1] >> 8) & 0xf;
switch (aen) {
default:
@@ -939,22 +1050,61 @@ void ql_mpi_idc_work(struct work_struct *work)
"Bug: Unhandled IDC action.\n");
break;
case MB_CMD_PORT_RESET:
- case MB_CMD_SET_PORT_CFG:
case MB_CMD_STOP_FW:
ql_link_off(qdev);
+ case MB_CMD_SET_PORT_CFG:
/* Signal the resulting link up AEN
* that the frame routing and mac addr
* needs to be set.
* */
set_bit(QL_CAM_RT_SET, &qdev->flags);
- rtnl_lock();
- status = ql_mb_idc_ack(qdev);
- rtnl_unlock();
- if (status) {
- QPRINTK(qdev, DRV, ERR,
- "Bug: No pending IDC!\n");
+ /* Do ACK if required */
+ if (timeout) {
+ status = ql_mb_idc_ack(qdev);
+ if (status)
+ QPRINTK(qdev, DRV, ERR,
+ "Bug: No pending IDC!\n");
+ } else {
+ QPRINTK(qdev, DRV, DEBUG,
+ "IDC ACK not required\n");
+ status = 0; /* success */
}
+ break;
+
+ /* These sub-commands issued by another (FCoE)
+ * function are requesting to do an operation
+ * on the shared resource (MPI environment).
+ * We currently don't issue these so we just
+ * ACK the request.
+ */
+ case MB_CMD_IOP_RESTART_MPI:
+ case MB_CMD_IOP_PREP_LINK_DOWN:
+ /* Drop the link, reload the routing
+ * table when link comes up.
+ */
+ ql_link_off(qdev);
+ set_bit(QL_CAM_RT_SET, &qdev->flags);
+ /* Fall through. */
+ case MB_CMD_IOP_DVR_START:
+ case MB_CMD_IOP_FLASH_ACC:
+ case MB_CMD_IOP_CORE_DUMP_MPI:
+ case MB_CMD_IOP_PREP_UPDATE_MPI:
+ case MB_CMD_IOP_COMP_UPDATE_MPI:
+ case MB_CMD_IOP_NONE: /* an IDC without params */
+ /* Do ACK if required */
+ if (timeout) {
+ status = ql_mb_idc_ack(qdev);
+ if (status)
+ QPRINTK(qdev, DRV, ERR,
+ "Bug: No pending IDC!\n");
+ } else {
+ QPRINTK(qdev, DRV, DEBUG,
+ "IDC ACK not required\n");
+ status = 0; /* success */
+ }
+ break;
}
+ rtnl_unlock();
}
void ql_mpi_work(struct work_struct *work)
diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c
index 8b14c6eda7c..f03e2e4a15a 100644
--- a/drivers/net/r6040.c
+++ b/drivers/net/r6040.c
@@ -842,7 +842,7 @@ static int r6040_open(struct net_device *dev)
int ret;
/* Request IRQ and Register interrupt handler */
- ret = request_irq(dev->irq, &r6040_interrupt,
+ ret = request_irq(dev->irq, r6040_interrupt,
IRQF_SHARED, dev->name, dev);
if (ret)
return ret;
@@ -958,8 +958,7 @@ static void r6040_multicast_list(struct net_device *dev)
}
/* Too many multicast addresses
* accept all traffic */
- else if ((dev->mc_count > MCAST_MAX)
- || (dev->flags & IFF_ALLMULTI))
+ else if ((dev->mc_count > MCAST_MAX) || (dev->flags & IFF_ALLMULTI))
reg |= 0x0020;
iowrite16(reg, ioaddr);
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 0fe2fc90f20..acfc5a3aa49 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -794,7 +794,7 @@ static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct rtl8169_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
unsigned int i;
- static struct {
+ static const struct {
u32 opt;
u16 reg;
u8 mask;
@@ -1277,7 +1277,7 @@ static void rtl8169_get_mac_version(struct rtl8169_private *tp,
*
* (RTL_R32(TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec
*/
- const struct {
+ static const struct {
u32 mask;
u32 val;
int mac_version;
@@ -1351,7 +1351,7 @@ struct phy_reg {
u16 val;
};
-static void rtl_phy_write(void __iomem *ioaddr, struct phy_reg *regs, int len)
+static void rtl_phy_write(void __iomem *ioaddr, const struct phy_reg *regs, int len)
{
while (len-- > 0) {
mdio_write(ioaddr, regs->reg, regs->val);
@@ -1361,7 +1361,7 @@ static void rtl_phy_write(void __iomem *ioaddr, struct phy_reg *regs, int len)
static void rtl8169s_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x06, 0x006e },
{ 0x08, 0x0708 },
@@ -1428,7 +1428,7 @@ static void rtl8169s_hw_phy_config(void __iomem *ioaddr)
static void rtl8169sb_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0002 },
{ 0x01, 0x90d0 },
{ 0x1f, 0x0000 }
@@ -1457,7 +1457,7 @@ static void rtl8169scd_hw_phy_config_quirk(struct rtl8169_private *tp,
static void rtl8169scd_hw_phy_config(struct rtl8169_private *tp,
void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x04, 0x0000 },
{ 0x03, 0x00a1 },
@@ -1504,7 +1504,7 @@ static void rtl8169scd_hw_phy_config(struct rtl8169_private *tp,
static void rtl8169sce_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x04, 0x0000 },
{ 0x03, 0x00a1 },
@@ -1557,7 +1557,7 @@ static void rtl8169sce_hw_phy_config(void __iomem *ioaddr)
static void rtl8168bb_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x10, 0xf41b },
{ 0x1f, 0x0000 }
};
@@ -1570,7 +1570,7 @@ static void rtl8168bb_hw_phy_config(void __iomem *ioaddr)
static void rtl8168bef_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x10, 0xf41b },
{ 0x1f, 0x0000 }
@@ -1581,7 +1581,7 @@ static void rtl8168bef_hw_phy_config(void __iomem *ioaddr)
static void rtl8168cp_1_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0000 },
{ 0x1d, 0x0f00 },
{ 0x1f, 0x0002 },
@@ -1594,7 +1594,7 @@ static void rtl8168cp_1_hw_phy_config(void __iomem *ioaddr)
static void rtl8168cp_2_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x1d, 0x3d98 },
{ 0x1f, 0x0000 }
@@ -1609,7 +1609,7 @@ static void rtl8168cp_2_hw_phy_config(void __iomem *ioaddr)
static void rtl8168c_1_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x12, 0x2300 },
{ 0x1f, 0x0002 },
@@ -1638,7 +1638,7 @@ static void rtl8168c_1_hw_phy_config(void __iomem *ioaddr)
static void rtl8168c_2_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x12, 0x2300 },
{ 0x03, 0x802f },
@@ -1666,7 +1666,7 @@ static void rtl8168c_2_hw_phy_config(void __iomem *ioaddr)
static void rtl8168c_3_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0001 },
{ 0x12, 0x2300 },
{ 0x1d, 0x3d98 },
@@ -1693,7 +1693,7 @@ static void rtl8168c_4_hw_phy_config(void __iomem *ioaddr)
static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr)
{
- static struct phy_reg phy_reg_init_0[] = {
+ static const struct phy_reg phy_reg_init_0[] = {
{ 0x1f, 0x0001 },
{ 0x06, 0x4064 },
{ 0x07, 0x2863 },
@@ -1712,14 +1712,14 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr)
{ 0x1a, 0x05ad },
{ 0x14, 0x94c0 }
};
- static struct phy_reg phy_reg_init_1[] = {
+ static const struct phy_reg phy_reg_init_1[] = {
{ 0x1f, 0x0002 },
{ 0x06, 0x5561 },
{ 0x1f, 0x0005 },
{ 0x05, 0x8332 },
{ 0x06, 0x5561 }
};
- static struct phy_reg phy_reg_init_2[] = {
+ static const struct phy_reg phy_reg_init_2[] = {
{ 0x1f, 0x0005 },
{ 0x05, 0xffc2 },
{ 0x1f, 0x0005 },
@@ -2084,7 +2084,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr)
rtl_phy_write(ioaddr, phy_reg_init_1, ARRAY_SIZE(phy_reg_init_1));
if (rtl8168d_efuse_read(ioaddr, 0x01) == 0xb1) {
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0002 },
{ 0x05, 0x669a },
{ 0x1f, 0x0005 },
@@ -2099,7 +2099,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr)
val = mdio_read(ioaddr, 0x0d);
if ((val & 0x00ff) != 0x006c) {
- u32 set[] = {
+ static const u32 set[] = {
0x0065, 0x0066, 0x0067, 0x0068,
0x0069, 0x006a, 0x006b, 0x006c
};
@@ -2112,7 +2112,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr)
mdio_write(ioaddr, 0x0d, val | set[i]);
}
} else {
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0002 },
{ 0x05, 0x6662 },
{ 0x1f, 0x0005 },
@@ -2136,7 +2136,7 @@ static void rtl8168d_1_hw_phy_config(void __iomem *ioaddr)
static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr)
{
- static struct phy_reg phy_reg_init_0[] = {
+ static const struct phy_reg phy_reg_init_0[] = {
{ 0x1f, 0x0001 },
{ 0x06, 0x4064 },
{ 0x07, 0x2863 },
@@ -2161,7 +2161,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr)
{ 0x05, 0x8332 },
{ 0x06, 0x5561 }
};
- static struct phy_reg phy_reg_init_1[] = {
+ static const struct phy_reg phy_reg_init_1[] = {
{ 0x1f, 0x0005 },
{ 0x05, 0xffc2 },
{ 0x1f, 0x0005 },
@@ -2477,7 +2477,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr)
rtl_phy_write(ioaddr, phy_reg_init_0, ARRAY_SIZE(phy_reg_init_0));
if (rtl8168d_efuse_read(ioaddr, 0x01) == 0xb1) {
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0002 },
{ 0x05, 0x669a },
{ 0x1f, 0x0005 },
@@ -2505,7 +2505,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr)
mdio_write(ioaddr, 0x0d, val | set[i]);
}
} else {
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0002 },
{ 0x05, 0x2642 },
{ 0x1f, 0x0005 },
@@ -2531,7 +2531,7 @@ static void rtl8168d_2_hw_phy_config(void __iomem *ioaddr)
static void rtl8168d_3_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0002 },
{ 0x10, 0x0008 },
{ 0x0d, 0x006c },
@@ -2592,7 +2592,7 @@ static void rtl8168d_3_hw_phy_config(void __iomem *ioaddr)
static void rtl8102e_hw_phy_config(void __iomem *ioaddr)
{
- struct phy_reg phy_reg_init[] = {
+ static const struct phy_reg phy_reg_init[] = {
{ 0x1f, 0x0003 },
{ 0x08, 0x441d },
{ 0x01, 0x9100 },
@@ -3388,7 +3388,7 @@ static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz)
static void rtl8169_set_magic_reg(void __iomem *ioaddr, unsigned mac_version)
{
- struct {
+ static const struct {
u32 mac_version;
u32 clk;
u32 val;
@@ -3512,7 +3512,7 @@ struct ephy_info {
u16 bits;
};
-static void rtl_ephy_init(void __iomem *ioaddr, struct ephy_info *e, int len)
+static void rtl_ephy_init(void __iomem *ioaddr, const struct ephy_info *e, int len)
{
u16 w;
@@ -3583,7 +3583,7 @@ static void __rtl_hw_start_8168cp(void __iomem *ioaddr, struct pci_dev *pdev)
static void rtl_hw_start_8168cp_1(void __iomem *ioaddr, struct pci_dev *pdev)
{
- static struct ephy_info e_info_8168cp[] = {
+ static const struct ephy_info e_info_8168cp[] = {
{ 0x01, 0, 0x0001 },
{ 0x02, 0x0800, 0x1000 },
{ 0x03, 0, 0x0042 },
@@ -3627,7 +3627,7 @@ static void rtl_hw_start_8168cp_3(void __iomem *ioaddr, struct pci_dev *pdev)
static void rtl_hw_start_8168c_1(void __iomem *ioaddr, struct pci_dev *pdev)
{
- static struct ephy_info e_info_8168c_1[] = {
+ static const struct ephy_info e_info_8168c_1[] = {
{ 0x02, 0x0800, 0x1000 },
{ 0x03, 0, 0x0002 },
{ 0x06, 0x0080, 0x0000 }
@@ -3644,7 +3644,7 @@ static void rtl_hw_start_8168c_1(void __iomem *ioaddr, struct pci_dev *pdev)
static void rtl_hw_start_8168c_2(void __iomem *ioaddr, struct pci_dev *pdev)
{
- static struct ephy_info e_info_8168c_2[] = {
+ static const struct ephy_info e_info_8168c_2[] = {
{ 0x01, 0, 0x0001 },
{ 0x03, 0x0400, 0x0220 }
};
@@ -3787,7 +3787,7 @@ static void rtl_hw_start_8168(struct net_device *dev)
static void rtl_hw_start_8102e_1(void __iomem *ioaddr, struct pci_dev *pdev)
{
- static struct ephy_info e_info_8102e_1[] = {
+ static const struct ephy_info e_info_8102e_1[] = {
{ 0x01, 0, 0x6e65 },
{ 0x02, 0, 0x091f },
{ 0x03, 0, 0xc2f9 },
@@ -4447,13 +4447,12 @@ static inline bool rtl8169_try_rx_copy(struct sk_buff **sk_buff,
if (pkt_size >= rx_copybreak)
goto out;
- skb = netdev_alloc_skb(tp->dev, pkt_size + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(tp->dev, pkt_size);
if (!skb)
goto out;
pci_dma_sync_single_for_cpu(tp->pci_dev, addr, pkt_size,
PCI_DMA_FROMDEVICE);
- skb_reserve(skb, NET_IP_ALIGN);
skb_copy_from_linear_data(*sk_buff, skb->data, pkt_size);
*sk_buff = skb;
done = true;
@@ -4764,8 +4763,8 @@ static void rtl_set_rx_mode(struct net_device *dev)
AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
AcceptAllPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter perfectly -- accept all multicasts. */
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
mc_filter[1] = mc_filter[0] = 0xffffffff;
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index 0dd7839322b..cc4218667cb 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -3238,7 +3238,7 @@ static u64 s2io_mdio_read(u32 mmd_type, u64 addr, struct net_device *dev)
/**
* s2io_chk_xpak_counter - Function to check the status of the xpak counters
- * @counter : couter value to be updated
+ * @counter : counter value to be updated
* @flag : flag to indicate the status
* @type : counter type
* Description:
diff --git a/drivers/net/s6gmac.c b/drivers/net/s6gmac.c
index 4525cbe8dd6..45f26344b36 100644
--- a/drivers/net/s6gmac.c
+++ b/drivers/net/s6gmac.c
@@ -373,9 +373,9 @@ struct s6gmac {
static void s6gmac_rx_fillfifo(struct s6gmac *pd)
{
struct sk_buff *skb;
- while ((((u8)(pd->rx_skb_i - pd->rx_skb_o)) < S6_NUM_RX_SKB)
- && (!s6dmac_fifo_full(pd->rx_dma, pd->rx_chan))
- && (skb = dev_alloc_skb(S6_MAX_FRLEN + 2))) {
+ while ((((u8)(pd->rx_skb_i - pd->rx_skb_o)) < S6_NUM_RX_SKB) &&
+ (!s6dmac_fifo_full(pd->rx_dma, pd->rx_chan)) &&
+ (skb = dev_alloc_skb(S6_MAX_FRLEN + 2))) {
pd->rx_skb[(pd->rx_skb_i++) % S6_NUM_RX_SKB] = skb;
s6dmac_put_fifo_cache(pd->rx_dma, pd->rx_chan,
pd->io, (u32)skb->data, S6_MAX_FRLEN);
@@ -984,7 +984,7 @@ static int __devinit s6gmac_probe(struct platform_device *pdev)
pd->rx_dma = DMA_MASK_DMAC(i);
pd->rx_chan = DMA_INDEX_CHNL(i);
pd->io = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
- res = request_irq(dev->irq, &s6gmac_interrupt, 0, dev->name, dev);
+ res = request_irq(dev->irq, s6gmac_interrupt, 0, dev->name, dev);
if (res) {
printk(KERN_ERR DRV_PRMT "irq request failed: %d\n", dev->irq);
goto errirq;
diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c
index c9c70ab0cce..9f83a119737 100644
--- a/drivers/net/sb1000.c
+++ b/drivers/net/sb1000.c
@@ -973,7 +973,7 @@ sb1000_open(struct net_device *dev)
lp->rx_frame_id[1] = 0;
lp->rx_frame_id[2] = 0;
lp->rx_frame_id[3] = 0;
- if (request_irq(dev->irq, &sb1000_interrupt, 0, "sb1000", dev)) {
+ if (request_irq(dev->irq, sb1000_interrupt, 0, "sb1000", dev)) {
return -EAGAIN;
}
diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c
index 508551f1b3f..564d4d7f855 100644
--- a/drivers/net/sb1250-mac.c
+++ b/drivers/net/sb1250-mac.c
@@ -1476,7 +1476,6 @@ static void sbmac_channel_start(struct sbmac_softc *s)
V_MAC_TX_RL_THRSH(4) |
V_MAC_RX_PL_THRSH(4) |
V_MAC_RX_RD_THRSH(4) | /* Must be '4' */
- V_MAC_RX_PL_THRSH(4) |
V_MAC_RX_RL_THRSH(8) |
0;
@@ -2411,7 +2410,7 @@ static int sbmac_open(struct net_device *dev)
*/
__raw_readq(sc->sbm_isr);
- err = request_irq(dev->irq, &sbmac_intr, IRQF_SHARED, dev->name, dev);
+ err = request_irq(dev->irq, sbmac_intr, IRQF_SHARED, dev->name, dev);
if (err) {
printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name,
dev->irq);
diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c
index 8d6030022d1..e35050322f9 100644
--- a/drivers/net/sc92031.c
+++ b/drivers/net/sc92031.c
@@ -428,9 +428,9 @@ static void _sc92031_set_mar(struct net_device *dev)
void __iomem *port_base = priv->port_base;
u32 mar0 = 0, mar1 = 0;
- if ((dev->flags & IFF_PROMISC)
- || dev->mc_count > multicast_filter_limit
- || (dev->flags & IFF_ALLMULTI))
+ if ((dev->flags & IFF_PROMISC) ||
+ dev->mc_count > multicast_filter_limit ||
+ (dev->flags & IFF_ALLMULTI))
mar0 = mar1 = 0xffffffff;
else if (dev->flags & IFF_MULTICAST) {
struct dev_mc_list *mc_list;
@@ -777,10 +777,10 @@ static void _sc92031_rx_tasklet(struct net_device *dev)
rx_ring_offset = (rx_ring_offset + 4) % RX_BUF_LEN;
- if (unlikely(rx_status == 0
- || rx_size > (MAX_ETH_FRAME_SIZE + 4)
- || rx_size < 16
- || !(rx_status & RxStatesOK))) {
+ if (unlikely(rx_status == 0 ||
+ rx_size > (MAX_ETH_FRAME_SIZE + 4) ||
+ rx_size < 16 ||
+ !(rx_status & RxStatesOK))) {
_sc92031_rx_tasklet_error(dev, rx_status, rx_size);
break;
}
@@ -793,7 +793,7 @@ static void _sc92031_rx_tasklet(struct net_device *dev)
rx_len -= rx_size_align + 4;
- skb = netdev_alloc_skb(dev, pkt_size + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(dev, pkt_size);
if (unlikely(!skb)) {
if (printk_ratelimit())
printk(KERN_ERR "%s: Couldn't allocate a skb_buff for a packet of size %u\n",
@@ -801,8 +801,6 @@ static void _sc92031_rx_tasklet(struct net_device *dev)
goto next;
}
- skb_reserve(skb, NET_IP_ALIGN);
-
if ((rx_ring_offset + pkt_size) > RX_BUF_LEN) {
memcpy(skb_put(skb, RX_BUF_LEN - rx_ring_offset),
rx_ring + rx_ring_offset, RX_BUF_LEN - rx_ring_offset);
diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c
index 39246d457ac..fe806bd9b95 100644
--- a/drivers/net/seeq8005.c
+++ b/drivers/net/seeq8005.c
@@ -335,7 +335,7 @@ static int __init seeq8005_probe1(struct net_device *dev, int ioaddr)
#if 0
{
- int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", dev);
+ int irqval = request_irq(dev->irq, seeq8005_interrupt, 0, "seeq8005", dev);
if (irqval) {
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
dev->irq, irqval);
@@ -367,7 +367,7 @@ static int seeq8005_open(struct net_device *dev)
struct net_local *lp = netdev_priv(dev);
{
- int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", dev);
+ int irqval = request_irq(dev->irq, seeq8005_interrupt, 0, "seeq8005", dev);
if (irqval) {
printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
dev->irq, irqval);
diff --git a/drivers/net/sfc/Kconfig b/drivers/net/sfc/Kconfig
index 260aafaac23..a65c9863839 100644
--- a/drivers/net/sfc/Kconfig
+++ b/drivers/net/sfc/Kconfig
@@ -1,5 +1,5 @@
config SFC
- tristate "Solarflare Solarstorm SFC4000 support"
+ tristate "Solarflare Solarstorm SFC4000/SFC9000-family support"
depends on PCI && INET
select MDIO
select CRC32
@@ -7,15 +7,16 @@ config SFC
select I2C_ALGOBIT
help
This driver supports 10-gigabit Ethernet cards based on
- the Solarflare Communications Solarstorm SFC4000 controller.
+ the Solarflare Communications Solarstorm SFC4000 and
+ SFC9000-family controllers.
To compile this driver as a module, choose M here. The module
will be called sfc.
config SFC_MTD
- bool "Solarflare Solarstorm SFC4000 flash MTD support"
+ bool "Solarflare Solarstorm SFC4000/SFC9000-family MTD support"
depends on SFC && MTD && !(SFC=y && MTD=m)
default y
help
- This exposes the on-board flash memory as an MTD device (e.g.
- /dev/mtd1). This makes it possible to upload new boot code
- to the NIC.
+ This exposes the on-board flash memory as MTD devices (e.g.
+ /dev/mtd1). This makes it possible to upload new firmware
+ to the NIC.
diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile
index b89f9be3cb1..1047b19c60a 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/sfc/Makefile
@@ -1,6 +1,7 @@
-sfc-y += efx.o falcon.o tx.o rx.o falcon_gmac.o \
- falcon_xmac.o selftest.o ethtool.o xfp_phy.o \
- mdio_10g.o tenxpress.o boards.o sfe4001.o
+sfc-y += efx.o nic.o falcon.o siena.o tx.o rx.o \
+ falcon_gmac.o falcon_xmac.o mcdi_mac.o \
+ selftest.o ethtool.o qt202x_phy.o mdio_10g.o \
+ tenxpress.o falcon_boards.o mcdi.o mcdi_phy.o
sfc-$(CONFIG_SFC_MTD) += mtd.o
obj-$(CONFIG_SFC) += sfc.o
diff --git a/drivers/net/sfc/bitfield.h b/drivers/net/sfc/bitfield.h
index d54d84c267b..098ac2ad757 100644
--- a/drivers/net/sfc/bitfield.h
+++ b/drivers/net/sfc/bitfield.h
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -37,6 +37,8 @@
#define EFX_DWORD_2_WIDTH 32
#define EFX_DWORD_3_LBN 96
#define EFX_DWORD_3_WIDTH 32
+#define EFX_QWORD_0_LBN 0
+#define EFX_QWORD_0_WIDTH 64
/* Specified attribute (e.g. LBN) of the specified field */
#define EFX_VAL(field, attribute) field ## _ ## attribute
@@ -520,19 +522,6 @@ typedef union efx_oword {
#define EFX_SET_QWORD_FIELD EFX_SET_QWORD_FIELD32
#endif
-#define EFX_SET_OWORD_FIELD_VER(efx, oword, field, value) do { \
- if (falcon_rev(efx) >= FALCON_REV_B0) { \
- EFX_SET_OWORD_FIELD((oword), field##_B0, (value)); \
- } else { \
- EFX_SET_OWORD_FIELD((oword), field##_A1, (value)); \
- } \
-} while (0)
-
-#define EFX_QWORD_FIELD_VER(efx, qword, field) \
- (falcon_rev(efx) >= FALCON_REV_B0 ? \
- EFX_QWORD_FIELD((qword), field##_B0) : \
- EFX_QWORD_FIELD((qword), field##_A1))
-
/* Used to avoid compiler warnings about shift range exceeding width
* of the data types when dma_addr_t is only 32 bits wide.
*/
diff --git a/drivers/net/sfc/boards.c b/drivers/net/sfc/boards.c
deleted file mode 100644
index 4a4c74c891b..00000000000
--- a/drivers/net/sfc/boards.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#include "net_driver.h"
-#include "phy.h"
-#include "boards.h"
-#include "efx.h"
-#include "workarounds.h"
-
-/* Macros for unpacking the board revision */
-/* The revision info is in host byte order. */
-#define BOARD_TYPE(_rev) (_rev >> 8)
-#define BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf)
-#define BOARD_MINOR(_rev) (_rev & 0xf)
-
-/* Blink support. If the PHY has no auto-blink mode so we hang it off a timer */
-#define BLINK_INTERVAL (HZ/2)
-
-static void blink_led_timer(unsigned long context)
-{
- struct efx_nic *efx = (struct efx_nic *)context;
- struct efx_blinker *bl = &efx->board_info.blinker;
- efx->board_info.set_id_led(efx, bl->state);
- bl->state = !bl->state;
- if (bl->resubmit)
- mod_timer(&bl->timer, jiffies + BLINK_INTERVAL);
-}
-
-static void board_blink(struct efx_nic *efx, bool blink)
-{
- struct efx_blinker *blinker = &efx->board_info.blinker;
-
- /* The rtnl mutex serialises all ethtool ioctls, so
- * nothing special needs doing here. */
- if (blink) {
- blinker->resubmit = true;
- blinker->state = false;
- setup_timer(&blinker->timer, blink_led_timer,
- (unsigned long)efx);
- mod_timer(&blinker->timer, jiffies + BLINK_INTERVAL);
- } else {
- blinker->resubmit = false;
- if (blinker->timer.function)
- del_timer_sync(&blinker->timer);
- efx->board_info.init_leds(efx);
- }
-}
-
-/*****************************************************************************
- * Support for LM87 sensor chip used on several boards
- */
-#define LM87_REG_ALARMS1 0x41
-#define LM87_REG_ALARMS2 0x42
-#define LM87_IN_LIMITS(nr, _min, _max) \
- 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min
-#define LM87_AIN_LIMITS(nr, _min, _max) \
- 0x3B + (nr), _max, 0x1A + (nr), _min
-#define LM87_TEMP_INT_LIMITS(_min, _max) \
- 0x39, _max, 0x3A, _min
-#define LM87_TEMP_EXT1_LIMITS(_min, _max) \
- 0x37, _max, 0x38, _min
-
-#define LM87_ALARM_TEMP_INT 0x10
-#define LM87_ALARM_TEMP_EXT1 0x20
-
-#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE)
-
-static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
- const u8 *reg_values)
-{
- struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info);
- int rc;
-
- if (!client)
- return -EIO;
-
- while (*reg_values) {
- u8 reg = *reg_values++;
- u8 value = *reg_values++;
- rc = i2c_smbus_write_byte_data(client, reg, value);
- if (rc)
- goto err;
- }
-
- efx->board_info.hwmon_client = client;
- return 0;
-
-err:
- i2c_unregister_device(client);
- return rc;
-}
-
-static void efx_fini_lm87(struct efx_nic *efx)
-{
- i2c_unregister_device(efx->board_info.hwmon_client);
-}
-
-static int efx_check_lm87(struct efx_nic *efx, unsigned mask)
-{
- struct i2c_client *client = efx->board_info.hwmon_client;
- s32 alarms1, alarms2;
-
- /* If link is up then do not monitor temperature */
- if (EFX_WORKAROUND_7884(efx) && efx->link_up)
- return 0;
-
- alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1);
- alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2);
- if (alarms1 < 0)
- return alarms1;
- if (alarms2 < 0)
- return alarms2;
- alarms1 &= mask;
- alarms2 &= mask >> 8;
- if (alarms1 || alarms2) {
- EFX_ERR(efx,
- "LM87 detected a hardware failure (status %02x:%02x)"
- "%s%s\n",
- alarms1, alarms2,
- (alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "",
- (alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : "");
- return -ERANGE;
- }
-
- return 0;
-}
-
-#else /* !CONFIG_SENSORS_LM87 */
-
-static inline int
-efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
- const u8 *reg_values)
-{
- return 0;
-}
-static inline void efx_fini_lm87(struct efx_nic *efx)
-{
-}
-static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask)
-{
- return 0;
-}
-
-#endif /* CONFIG_SENSORS_LM87 */
-
-/*****************************************************************************
- * Support for the SFE4002
- *
- */
-static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */
-
-static const u8 sfe4002_lm87_regs[] = {
- LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
- LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
- LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
- LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */
- LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
- LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
- LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */
- LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
- LM87_TEMP_INT_LIMITS(10, 60), /* board */
- LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
- 0
-};
-
-static struct i2c_board_info sfe4002_hwmon_info = {
- I2C_BOARD_INFO("lm87", 0x2e),
- .platform_data = &sfe4002_lm87_channel,
-};
-
-/****************************************************************************/
-/* LED allocations. Note that on rev A0 boards the schematic and the reality
- * differ: red and green are swapped. Below is the fixed (A1) layout (there
- * are only 3 A0 boards in existence, so no real reason to make this
- * conditional).
- */
-#define SFE4002_FAULT_LED (2) /* Red */
-#define SFE4002_RX_LED (0) /* Green */
-#define SFE4002_TX_LED (1) /* Amber */
-
-static void sfe4002_init_leds(struct efx_nic *efx)
-{
- /* Set the TX and RX LEDs to reflect status and activity, and the
- * fault LED off */
- xfp_set_led(efx, SFE4002_TX_LED,
- QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT);
- xfp_set_led(efx, SFE4002_RX_LED,
- QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT);
- xfp_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF);
-}
-
-static void sfe4002_set_id_led(struct efx_nic *efx, bool state)
-{
- xfp_set_led(efx, SFE4002_FAULT_LED, state ? QUAKE_LED_ON :
- QUAKE_LED_OFF);
-}
-
-static int sfe4002_check_hw(struct efx_nic *efx)
-{
- /* A0 board rev. 4002s report a temperature fault the whole time
- * (bad sensor) so we mask it out. */
- unsigned alarm_mask =
- (efx->board_info.major == 0 && efx->board_info.minor == 0) ?
- ~LM87_ALARM_TEMP_EXT1 : ~0;
-
- return efx_check_lm87(efx, alarm_mask);
-}
-
-static int sfe4002_init(struct efx_nic *efx)
-{
- int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs);
- if (rc)
- return rc;
- efx->board_info.monitor = sfe4002_check_hw;
- efx->board_info.init_leds = sfe4002_init_leds;
- efx->board_info.set_id_led = sfe4002_set_id_led;
- efx->board_info.blink = board_blink;
- efx->board_info.fini = efx_fini_lm87;
- return 0;
-}
-
-/*****************************************************************************
- * Support for the SFN4112F
- *
- */
-static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */
-
-static const u8 sfn4112f_lm87_regs[] = {
- LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
- LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
- LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
- LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
- LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
- LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
- LM87_TEMP_INT_LIMITS(10, 60), /* board */
- LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
- 0
-};
-
-static struct i2c_board_info sfn4112f_hwmon_info = {
- I2C_BOARD_INFO("lm87", 0x2e),
- .platform_data = &sfn4112f_lm87_channel,
-};
-
-#define SFN4112F_ACT_LED 0
-#define SFN4112F_LINK_LED 1
-
-static void sfn4112f_init_leds(struct efx_nic *efx)
-{
- xfp_set_led(efx, SFN4112F_ACT_LED,
- QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT);
- xfp_set_led(efx, SFN4112F_LINK_LED,
- QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT);
-}
-
-static void sfn4112f_set_id_led(struct efx_nic *efx, bool state)
-{
- xfp_set_led(efx, SFN4112F_LINK_LED,
- state ? QUAKE_LED_ON : QUAKE_LED_OFF);
-}
-
-static int sfn4112f_check_hw(struct efx_nic *efx)
-{
- /* Mask out unused sensors */
- return efx_check_lm87(efx, ~0x48);
-}
-
-static int sfn4112f_init(struct efx_nic *efx)
-{
- int rc = efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs);
- if (rc)
- return rc;
- efx->board_info.monitor = sfn4112f_check_hw;
- efx->board_info.init_leds = sfn4112f_init_leds;
- efx->board_info.set_id_led = sfn4112f_set_id_led;
- efx->board_info.blink = board_blink;
- efx->board_info.fini = efx_fini_lm87;
- return 0;
-}
-
-/* This will get expanded as board-specific details get moved out of the
- * PHY drivers. */
-struct efx_board_data {
- enum efx_board_type type;
- const char *ref_model;
- const char *gen_type;
- int (*init) (struct efx_nic *nic);
-};
-
-
-static struct efx_board_data board_data[] = {
- { EFX_BOARD_SFE4001, "SFE4001", "10GBASE-T adapter", sfe4001_init },
- { EFX_BOARD_SFE4002, "SFE4002", "XFP adapter", sfe4002_init },
- { EFX_BOARD_SFN4111T, "SFN4111T", "100/1000/10GBASE-T adapter",
- sfn4111t_init },
- { EFX_BOARD_SFN4112F, "SFN4112F", "SFP+ adapter",
- sfn4112f_init },
-};
-
-void efx_set_board_info(struct efx_nic *efx, u16 revision_info)
-{
- struct efx_board_data *data = NULL;
- int i;
-
- efx->board_info.type = BOARD_TYPE(revision_info);
- efx->board_info.major = BOARD_MAJOR(revision_info);
- efx->board_info.minor = BOARD_MINOR(revision_info);
-
- for (i = 0; i < ARRAY_SIZE(board_data); i++)
- if (board_data[i].type == efx->board_info.type)
- data = &board_data[i];
-
- if (data) {
- EFX_INFO(efx, "board is %s rev %c%d\n",
- (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
- ? data->ref_model : data->gen_type,
- 'A' + efx->board_info.major, efx->board_info.minor);
- efx->board_info.init = data->init;
- } else {
- EFX_ERR(efx, "unknown board type %d\n", efx->board_info.type);
- }
-}
diff --git a/drivers/net/sfc/boards.h b/drivers/net/sfc/boards.h
deleted file mode 100644
index 44942de0e08..00000000000
--- a/drivers/net/sfc/boards.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_BOARDS_H
-#define EFX_BOARDS_H
-
-/* Board IDs (must fit in 8 bits) */
-enum efx_board_type {
- EFX_BOARD_SFE4001 = 1,
- EFX_BOARD_SFE4002 = 2,
- EFX_BOARD_SFN4111T = 0x51,
- EFX_BOARD_SFN4112F = 0x52,
-};
-
-extern void efx_set_board_info(struct efx_nic *efx, u16 revision_info);
-
-/* SFE4001 (10GBASE-T) */
-extern int sfe4001_init(struct efx_nic *efx);
-/* SFN4111T (100/1000/10GBASE-T) */
-extern int sfn4111t_init(struct efx_nic *efx);
-
-#endif
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index cc4b2f99989..f983e3b507c 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2005-2008 Solarflare Communications Inc.
+ * Copyright 2005-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -21,12 +21,73 @@
#include <linux/ethtool.h>
#include <linux/topology.h>
#include "net_driver.h"
-#include "ethtool.h"
-#include "tx.h"
-#include "rx.h"
#include "efx.h"
#include "mdio_10g.h"
-#include "falcon.h"
+#include "nic.h"
+
+#include "mcdi.h"
+
+/**************************************************************************
+ *
+ * Type name strings
+ *
+ **************************************************************************
+ */
+
+/* Loopback mode names (see LOOPBACK_MODE()) */
+const unsigned int efx_loopback_mode_max = LOOPBACK_MAX;
+const char *efx_loopback_mode_names[] = {
+ [LOOPBACK_NONE] = "NONE",
+ [LOOPBACK_DATA] = "DATAPATH",
+ [LOOPBACK_GMAC] = "GMAC",
+ [LOOPBACK_XGMII] = "XGMII",
+ [LOOPBACK_XGXS] = "XGXS",
+ [LOOPBACK_XAUI] = "XAUI",
+ [LOOPBACK_GMII] = "GMII",
+ [LOOPBACK_SGMII] = "SGMII",
+ [LOOPBACK_XGBR] = "XGBR",
+ [LOOPBACK_XFI] = "XFI",
+ [LOOPBACK_XAUI_FAR] = "XAUI_FAR",
+ [LOOPBACK_GMII_FAR] = "GMII_FAR",
+ [LOOPBACK_SGMII_FAR] = "SGMII_FAR",
+ [LOOPBACK_XFI_FAR] = "XFI_FAR",
+ [LOOPBACK_GPHY] = "GPHY",
+ [LOOPBACK_PHYXS] = "PHYXS",
+ [LOOPBACK_PCS] = "PCS",
+ [LOOPBACK_PMAPMD] = "PMA/PMD",
+ [LOOPBACK_XPORT] = "XPORT",
+ [LOOPBACK_XGMII_WS] = "XGMII_WS",
+ [LOOPBACK_XAUI_WS] = "XAUI_WS",
+ [LOOPBACK_XAUI_WS_FAR] = "XAUI_WS_FAR",
+ [LOOPBACK_XAUI_WS_NEAR] = "XAUI_WS_NEAR",
+ [LOOPBACK_GMII_WS] = "GMII_WS",
+ [LOOPBACK_XFI_WS] = "XFI_WS",
+ [LOOPBACK_XFI_WS_FAR] = "XFI_WS_FAR",
+ [LOOPBACK_PHYXS_WS] = "PHYXS_WS",
+};
+
+/* Interrupt mode names (see INT_MODE())) */
+const unsigned int efx_interrupt_mode_max = EFX_INT_MODE_MAX;
+const char *efx_interrupt_mode_names[] = {
+ [EFX_INT_MODE_MSIX] = "MSI-X",
+ [EFX_INT_MODE_MSI] = "MSI",
+ [EFX_INT_MODE_LEGACY] = "legacy",
+};
+
+const unsigned int efx_reset_type_max = RESET_TYPE_MAX;
+const char *efx_reset_type_names[] = {
+ [RESET_TYPE_INVISIBLE] = "INVISIBLE",
+ [RESET_TYPE_ALL] = "ALL",
+ [RESET_TYPE_WORLD] = "WORLD",
+ [RESET_TYPE_DISABLE] = "DISABLE",
+ [RESET_TYPE_TX_WATCHDOG] = "TX_WATCHDOG",
+ [RESET_TYPE_INT_ERROR] = "INT_ERROR",
+ [RESET_TYPE_RX_RECOVERY] = "RX_RECOVERY",
+ [RESET_TYPE_RX_DESC_FETCH] = "RX_DESC_FETCH",
+ [RESET_TYPE_TX_DESC_FETCH] = "TX_DESC_FETCH",
+ [RESET_TYPE_TX_SKIP] = "TX_SKIP",
+ [RESET_TYPE_MC_FAILURE] = "MC_FAILURE",
+};
#define EFX_MAX_MTU (9 * 1024)
@@ -145,7 +206,8 @@ static void efx_fini_channels(struct efx_nic *efx);
#define EFX_ASSERT_RESET_SERIALISED(efx) \
do { \
- if (efx->state == STATE_RUNNING) \
+ if ((efx->state == STATE_RUNNING) || \
+ (efx->state == STATE_DISABLED)) \
ASSERT_RTNL(); \
} while (0)
@@ -171,7 +233,7 @@ static int efx_process_channel(struct efx_channel *channel, int rx_quota)
!channel->enabled))
return 0;
- rx_packets = falcon_process_eventq(channel, rx_quota);
+ rx_packets = efx_nic_process_eventq(channel, rx_quota);
if (rx_packets == 0)
return 0;
@@ -203,7 +265,7 @@ static inline void efx_channel_processed(struct efx_channel *channel)
channel->work_pending = false;
smp_wmb();
- falcon_eventq_read_ack(channel);
+ efx_nic_eventq_read_ack(channel);
}
/* NAPI poll handler
@@ -228,26 +290,20 @@ static int efx_poll(struct napi_struct *napi, int budget)
if (channel->used_flags & EFX_USED_BY_RX &&
efx->irq_rx_adaptive &&
unlikely(++channel->irq_count == 1000)) {
- unsigned old_irq_moderation = channel->irq_moderation;
-
if (unlikely(channel->irq_mod_score <
irq_adapt_low_thresh)) {
- channel->irq_moderation =
- max_t(int,
- channel->irq_moderation -
- FALCON_IRQ_MOD_RESOLUTION,
- FALCON_IRQ_MOD_RESOLUTION);
+ if (channel->irq_moderation > 1) {
+ channel->irq_moderation -= 1;
+ efx->type->push_irq_moderation(channel);
+ }
} else if (unlikely(channel->irq_mod_score >
irq_adapt_high_thresh)) {
- channel->irq_moderation =
- min(channel->irq_moderation +
- FALCON_IRQ_MOD_RESOLUTION,
- efx->irq_rx_moderation);
+ if (channel->irq_moderation <
+ efx->irq_rx_moderation) {
+ channel->irq_moderation += 1;
+ efx->type->push_irq_moderation(channel);
+ }
}
-
- if (channel->irq_moderation != old_irq_moderation)
- falcon_set_int_moderation(channel);
-
channel->irq_count = 0;
channel->irq_mod_score = 0;
}
@@ -280,7 +336,7 @@ void efx_process_channel_now(struct efx_channel *channel)
BUG_ON(!channel->enabled);
/* Disable interrupts and wait for ISRs to complete */
- falcon_disable_interrupts(efx);
+ efx_nic_disable_interrupts(efx);
if (efx->legacy_irq)
synchronize_irq(efx->legacy_irq);
if (channel->irq)
@@ -290,14 +346,14 @@ void efx_process_channel_now(struct efx_channel *channel)
napi_disable(&channel->napi_str);
/* Poll the channel */
- efx_process_channel(channel, efx->type->evq_size);
+ efx_process_channel(channel, EFX_EVQ_SIZE);
/* Ack the eventq. This may cause an interrupt to be generated
* when they are reenabled */
efx_channel_processed(channel);
napi_enable(&channel->napi_str);
- falcon_enable_interrupts(efx);
+ efx_nic_enable_interrupts(efx);
}
/* Create event queue
@@ -309,7 +365,7 @@ static int efx_probe_eventq(struct efx_channel *channel)
{
EFX_LOG(channel->efx, "chan %d create event queue\n", channel->channel);
- return falcon_probe_eventq(channel);
+ return efx_nic_probe_eventq(channel);
}
/* Prepare channel's event queue */
@@ -319,21 +375,21 @@ static void efx_init_eventq(struct efx_channel *channel)
channel->eventq_read_ptr = 0;
- falcon_init_eventq(channel);
+ efx_nic_init_eventq(channel);
}
static void efx_fini_eventq(struct efx_channel *channel)
{
EFX_LOG(channel->efx, "chan %d fini event queue\n", channel->channel);
- falcon_fini_eventq(channel);
+ efx_nic_fini_eventq(channel);
}
static void efx_remove_eventq(struct efx_channel *channel)
{
EFX_LOG(channel->efx, "chan %d remove event queue\n", channel->channel);
- falcon_remove_eventq(channel);
+ efx_nic_remove_eventq(channel);
}
/**************************************************************************
@@ -499,7 +555,7 @@ static void efx_fini_channels(struct efx_nic *efx)
EFX_ASSERT_RESET_SERIALISED(efx);
BUG_ON(efx->port_enabled);
- rc = falcon_flush_queues(efx);
+ rc = efx_nic_flush_queues(efx);
if (rc)
EFX_ERR(efx, "failed to flush queues\n");
else
@@ -547,8 +603,10 @@ void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay)
* netif_carrier_on/off) of the link status, and also maintains the
* link status's stop on the port's TX queue.
*/
-static void efx_link_status_changed(struct efx_nic *efx)
+void efx_link_status_changed(struct efx_nic *efx)
{
+ struct efx_link_state *link_state = &efx->link_state;
+
/* SFC Bug 5356: A net_dev notifier is registered, so we must ensure
* that no events are triggered between unregister_netdev() and the
* driver unloading. A more general condition is that NETDEV_CHANGE
@@ -561,19 +619,19 @@ static void efx_link_status_changed(struct efx_nic *efx)
return;
}
- if (efx->link_up != netif_carrier_ok(efx->net_dev)) {
+ if (link_state->up != netif_carrier_ok(efx->net_dev)) {
efx->n_link_state_changes++;
- if (efx->link_up)
+ if (link_state->up)
netif_carrier_on(efx->net_dev);
else
netif_carrier_off(efx->net_dev);
}
/* Status message for kernel log */
- if (efx->link_up) {
+ if (link_state->up) {
EFX_INFO(efx, "link up at %uMbps %s-duplex (MTU %d)%s\n",
- efx->link_speed, efx->link_fd ? "full" : "half",
+ link_state->speed, link_state->fd ? "full" : "half",
efx->net_dev->mtu,
(efx->promiscuous ? " [PROMISC]" : ""));
} else {
@@ -582,16 +640,49 @@ static void efx_link_status_changed(struct efx_nic *efx)
}
+void efx_link_set_advertising(struct efx_nic *efx, u32 advertising)
+{
+ efx->link_advertising = advertising;
+ if (advertising) {
+ if (advertising & ADVERTISED_Pause)
+ efx->wanted_fc |= (EFX_FC_TX | EFX_FC_RX);
+ else
+ efx->wanted_fc &= ~(EFX_FC_TX | EFX_FC_RX);
+ if (advertising & ADVERTISED_Asym_Pause)
+ efx->wanted_fc ^= EFX_FC_TX;
+ }
+}
+
+void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type wanted_fc)
+{
+ efx->wanted_fc = wanted_fc;
+ if (efx->link_advertising) {
+ if (wanted_fc & EFX_FC_RX)
+ efx->link_advertising |= (ADVERTISED_Pause |
+ ADVERTISED_Asym_Pause);
+ else
+ efx->link_advertising &= ~(ADVERTISED_Pause |
+ ADVERTISED_Asym_Pause);
+ if (wanted_fc & EFX_FC_TX)
+ efx->link_advertising ^= ADVERTISED_Asym_Pause;
+ }
+}
+
static void efx_fini_port(struct efx_nic *efx);
-/* This call reinitialises the MAC to pick up new PHY settings. The
- * caller must hold the mac_lock */
-void __efx_reconfigure_port(struct efx_nic *efx)
+/* Push loopback/power/transmit disable settings to the PHY, and reconfigure
+ * the MAC appropriately. All other PHY configuration changes are pushed
+ * through phy_op->set_settings(), and pushed asynchronously to the MAC
+ * through efx_monitor().
+ *
+ * Callers must hold the mac_lock
+ */
+int __efx_reconfigure_port(struct efx_nic *efx)
{
- WARN_ON(!mutex_is_locked(&efx->mac_lock));
+ enum efx_phy_mode phy_mode;
+ int rc;
- EFX_LOG(efx, "reconfiguring MAC from PHY settings on CPU %d\n",
- raw_smp_processor_id());
+ WARN_ON(!mutex_is_locked(&efx->mac_lock));
/* Serialise the promiscuous flag with efx_set_multicast_list. */
if (efx_dev_registered(efx)) {
@@ -599,61 +690,48 @@ void __efx_reconfigure_port(struct efx_nic *efx)
netif_addr_unlock_bh(efx->net_dev);
}
- falcon_deconfigure_mac_wrapper(efx);
-
- /* Reconfigure the PHY, disabling transmit in mac level loopback. */
+ /* Disable PHY transmit in mac level loopbacks */
+ phy_mode = efx->phy_mode;
if (LOOPBACK_INTERNAL(efx))
efx->phy_mode |= PHY_MODE_TX_DISABLED;
else
efx->phy_mode &= ~PHY_MODE_TX_DISABLED;
- efx->phy_op->reconfigure(efx);
- if (falcon_switch_mac(efx))
- goto fail;
+ rc = efx->type->reconfigure_port(efx);
- efx->mac_op->reconfigure(efx);
-
- /* Inform kernel of loss/gain of carrier */
- efx_link_status_changed(efx);
- return;
+ if (rc)
+ efx->phy_mode = phy_mode;
-fail:
- EFX_ERR(efx, "failed to reconfigure MAC\n");
- efx->port_enabled = false;
- efx_fini_port(efx);
+ return rc;
}
/* Reinitialise the MAC to pick up new PHY settings, even if the port is
* disabled. */
-void efx_reconfigure_port(struct efx_nic *efx)
+int efx_reconfigure_port(struct efx_nic *efx)
{
+ int rc;
+
EFX_ASSERT_RESET_SERIALISED(efx);
mutex_lock(&efx->mac_lock);
- __efx_reconfigure_port(efx);
+ rc = __efx_reconfigure_port(efx);
mutex_unlock(&efx->mac_lock);
-}
-
-/* Asynchronous efx_reconfigure_port work item. To speed up efx_flush_all()
- * we don't efx_reconfigure_port() if the port is disabled. Care is taken
- * in efx_stop_all() and efx_start_port() to prevent PHY events being lost */
-static void efx_phy_work(struct work_struct *data)
-{
- struct efx_nic *efx = container_of(data, struct efx_nic, phy_work);
- mutex_lock(&efx->mac_lock);
- if (efx->port_enabled)
- __efx_reconfigure_port(efx);
- mutex_unlock(&efx->mac_lock);
+ return rc;
}
+/* Asynchronous work item for changing MAC promiscuity and multicast
+ * hash. Avoid a drain/rx_ingress enable by reconfiguring the current
+ * MAC directly. */
static void efx_mac_work(struct work_struct *data)
{
struct efx_nic *efx = container_of(data, struct efx_nic, mac_work);
mutex_lock(&efx->mac_lock);
- if (efx->port_enabled)
- efx->mac_op->irq(efx);
+ if (efx->port_enabled) {
+ efx->type->push_multicast_hash(efx);
+ efx->mac_op->reconfigure(efx);
+ }
mutex_unlock(&efx->mac_lock);
}
@@ -663,8 +741,8 @@ static int efx_probe_port(struct efx_nic *efx)
EFX_LOG(efx, "create port\n");
- /* Connect up MAC/PHY operations table and read MAC address */
- rc = falcon_probe_port(efx);
+ /* Connect up MAC/PHY operations table */
+ rc = efx->type->probe_port(efx);
if (rc)
goto err;
@@ -699,29 +777,33 @@ static int efx_init_port(struct efx_nic *efx)
EFX_LOG(efx, "init port\n");
- rc = efx->phy_op->init(efx);
- if (rc)
- return rc;
mutex_lock(&efx->mac_lock);
- efx->phy_op->reconfigure(efx);
- rc = falcon_switch_mac(efx);
- mutex_unlock(&efx->mac_lock);
+
+ rc = efx->phy_op->init(efx);
if (rc)
- goto fail;
- efx->mac_op->reconfigure(efx);
+ goto fail1;
efx->port_initialized = true;
- efx_stats_enable(efx);
+
+ /* Reconfigure the MAC before creating dma queues (required for
+ * Falcon/A1 where RX_INGR_EN/TX_DRAIN_EN isn't supported) */
+ efx->mac_op->reconfigure(efx);
+
+ /* Ensure the PHY advertises the correct flow control settings */
+ rc = efx->phy_op->reconfigure(efx);
+ if (rc)
+ goto fail2;
+
+ mutex_unlock(&efx->mac_lock);
return 0;
-fail:
+fail2:
efx->phy_op->fini(efx);
+fail1:
+ mutex_unlock(&efx->mac_lock);
return rc;
}
-/* Allow efx_reconfigure_port() to be scheduled, and close the window
- * between efx_stop_port and efx_flush_all whereby a previously scheduled
- * efx_phy_work()/efx_mac_work() may have been cancelled */
static void efx_start_port(struct efx_nic *efx)
{
EFX_LOG(efx, "start port\n");
@@ -729,15 +811,16 @@ static void efx_start_port(struct efx_nic *efx)
mutex_lock(&efx->mac_lock);
efx->port_enabled = true;
- __efx_reconfigure_port(efx);
- efx->mac_op->irq(efx);
+
+ /* efx_mac_work() might have been scheduled after efx_stop_port(),
+ * and then cancelled by efx_flush_all() */
+ efx->type->push_multicast_hash(efx);
+ efx->mac_op->reconfigure(efx);
+
mutex_unlock(&efx->mac_lock);
}
-/* Prevent efx_phy_work, efx_mac_work, and efx_monitor() from executing,
- * and efx_set_multicast_list() from scheduling efx_phy_work. efx_phy_work
- * and efx_mac_work may still be scheduled via NAPI processing until
- * efx_flush_all() is called */
+/* Prevent efx_mac_work() and efx_monitor() from working */
static void efx_stop_port(struct efx_nic *efx)
{
EFX_LOG(efx, "stop port\n");
@@ -760,11 +843,10 @@ static void efx_fini_port(struct efx_nic *efx)
if (!efx->port_initialized)
return;
- efx_stats_disable(efx);
efx->phy_op->fini(efx);
efx->port_initialized = false;
- efx->link_up = false;
+ efx->link_state.up = false;
efx_link_status_changed(efx);
}
@@ -772,7 +854,7 @@ static void efx_remove_port(struct efx_nic *efx)
{
EFX_LOG(efx, "destroying port\n");
- falcon_remove_port(efx);
+ efx->type->remove_port(efx);
}
/**************************************************************************
@@ -824,9 +906,8 @@ static int efx_init_io(struct efx_nic *efx)
goto fail2;
}
- efx->membase_phys = pci_resource_start(efx->pci_dev,
- efx->type->mem_bar);
- rc = pci_request_region(pci_dev, efx->type->mem_bar, "sfc");
+ efx->membase_phys = pci_resource_start(efx->pci_dev, EFX_MEM_BAR);
+ rc = pci_request_region(pci_dev, EFX_MEM_BAR, "sfc");
if (rc) {
EFX_ERR(efx, "request for memory BAR failed\n");
rc = -EIO;
@@ -835,21 +916,20 @@ static int efx_init_io(struct efx_nic *efx)
efx->membase = ioremap_nocache(efx->membase_phys,
efx->type->mem_map_size);
if (!efx->membase) {
- EFX_ERR(efx, "could not map memory BAR %d at %llx+%x\n",
- efx->type->mem_bar,
+ EFX_ERR(efx, "could not map memory BAR at %llx+%x\n",
(unsigned long long)efx->membase_phys,
efx->type->mem_map_size);
rc = -ENOMEM;
goto fail4;
}
- EFX_LOG(efx, "memory BAR %u at %llx+%x (virtual %p)\n",
- efx->type->mem_bar, (unsigned long long)efx->membase_phys,
+ EFX_LOG(efx, "memory BAR at %llx+%x (virtual %p)\n",
+ (unsigned long long)efx->membase_phys,
efx->type->mem_map_size, efx->membase);
return 0;
fail4:
- pci_release_region(efx->pci_dev, efx->type->mem_bar);
+ pci_release_region(efx->pci_dev, EFX_MEM_BAR);
fail3:
efx->membase_phys = 0;
fail2:
@@ -868,7 +948,7 @@ static void efx_fini_io(struct efx_nic *efx)
}
if (efx->membase_phys) {
- pci_release_region(efx->pci_dev, efx->type->mem_bar);
+ pci_release_region(efx->pci_dev, EFX_MEM_BAR);
efx->membase_phys = 0;
}
@@ -1011,7 +1091,7 @@ static int efx_probe_nic(struct efx_nic *efx)
EFX_LOG(efx, "creating NIC\n");
/* Carry out hardware-type specific initialisation */
- rc = falcon_probe_nic(efx);
+ rc = efx->type->probe(efx);
if (rc)
return rc;
@@ -1032,7 +1112,7 @@ static void efx_remove_nic(struct efx_nic *efx)
EFX_LOG(efx, "destroying NIC\n");
efx_remove_interrupts(efx);
- falcon_remove_nic(efx);
+ efx->type->remove(efx);
}
/**************************************************************************
@@ -1112,12 +1192,31 @@ static void efx_start_all(struct efx_nic *efx)
efx_for_each_channel(channel, efx)
efx_start_channel(channel);
- falcon_enable_interrupts(efx);
-
- /* Start hardware monitor if we're in RUNNING */
- if (efx->state == STATE_RUNNING)
+ efx_nic_enable_interrupts(efx);
+
+ /* Switch to event based MCDI completions after enabling interrupts.
+ * If a reset has been scheduled, then we need to stay in polled mode.
+ * Rather than serialising efx_mcdi_mode_event() [which sleeps] and
+ * reset_pending [modified from an atomic context], we instead guarantee
+ * that efx_mcdi_mode_poll() isn't reverted erroneously */
+ efx_mcdi_mode_event(efx);
+ if (efx->reset_pending != RESET_TYPE_NONE)
+ efx_mcdi_mode_poll(efx);
+
+ /* Start the hardware monitor if there is one. Otherwise (we're link
+ * event driven), we have to poll the PHY because after an event queue
+ * flush, we could have a missed a link state change */
+ if (efx->type->monitor != NULL) {
queue_delayed_work(efx->workqueue, &efx->monitor_work,
efx_monitor_interval);
+ } else {
+ mutex_lock(&efx->mac_lock);
+ if (efx->phy_op->poll(efx))
+ efx_link_status_changed(efx);
+ mutex_unlock(&efx->mac_lock);
+ }
+
+ efx->type->start_stats(efx);
}
/* Flush all delayed work. Should only be called when no more delayed work
@@ -1136,8 +1235,6 @@ static void efx_flush_all(struct efx_nic *efx)
/* Stop scheduled port reconfigurations */
cancel_work_sync(&efx->mac_work);
- cancel_work_sync(&efx->phy_work);
-
}
/* Quiesce hardware and software without bringing the link down.
@@ -1155,8 +1252,13 @@ static void efx_stop_all(struct efx_nic *efx)
if (!efx->port_enabled)
return;
+ efx->type->stop_stats(efx);
+
+ /* Switch to MCDI polling on Siena before disabling interrupts */
+ efx_mcdi_mode_poll(efx);
+
/* Disable interrupts and wait for ISR to complete */
- falcon_disable_interrupts(efx);
+ efx_nic_disable_interrupts(efx);
if (efx->legacy_irq)
synchronize_irq(efx->legacy_irq);
efx_for_each_channel(channel, efx) {
@@ -1173,15 +1275,9 @@ static void efx_stop_all(struct efx_nic *efx)
* window to loose phy events */
efx_stop_port(efx);
- /* Flush efx_phy_work, efx_mac_work, refill_workqueue, monitor_work */
+ /* Flush efx_mac_work(), refill_workqueue, monitor_work */
efx_flush_all(efx);
- /* Isolate the MAC from the TX and RX engines, so that queue
- * flushes will complete in a timely fashion. */
- falcon_deconfigure_mac_wrapper(efx);
- msleep(10); /* Let the Rx FIFO drain */
- falcon_drain_tx_fifo(efx);
-
/* Stop the kernel transmit interface late, so the watchdog
* timer isn't ticking over the flush */
if (efx_dev_registered(efx)) {
@@ -1201,41 +1297,39 @@ static void efx_remove_all(struct efx_nic *efx)
efx_remove_nic(efx);
}
-/* A convinience function to safely flush all the queues */
-void efx_flush_queues(struct efx_nic *efx)
-{
- EFX_ASSERT_RESET_SERIALISED(efx);
-
- efx_stop_all(efx);
-
- efx_fini_channels(efx);
- efx_init_channels(efx);
-
- efx_start_all(efx);
-}
-
/**************************************************************************
*
* Interrupt moderation
*
**************************************************************************/
+static unsigned irq_mod_ticks(int usecs, int resolution)
+{
+ if (usecs <= 0)
+ return 0; /* cannot receive interrupts ahead of time :-) */
+ if (usecs < resolution)
+ return 1; /* never round down to 0 */
+ return usecs / resolution;
+}
+
/* Set interrupt moderation parameters */
void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs,
bool rx_adaptive)
{
struct efx_tx_queue *tx_queue;
struct efx_rx_queue *rx_queue;
+ unsigned tx_ticks = irq_mod_ticks(tx_usecs, EFX_IRQ_MOD_RESOLUTION);
+ unsigned rx_ticks = irq_mod_ticks(rx_usecs, EFX_IRQ_MOD_RESOLUTION);
EFX_ASSERT_RESET_SERIALISED(efx);
efx_for_each_tx_queue(tx_queue, efx)
- tx_queue->channel->irq_moderation = tx_usecs;
+ tx_queue->channel->irq_moderation = tx_ticks;
efx->irq_rx_adaptive = rx_adaptive;
- efx->irq_rx_moderation = rx_usecs;
+ efx->irq_rx_moderation = rx_ticks;
efx_for_each_rx_queue(rx_queue, efx)
- rx_queue->channel->irq_moderation = rx_usecs;
+ rx_queue->channel->irq_moderation = rx_ticks;
}
/**************************************************************************
@@ -1250,10 +1344,10 @@ static void efx_monitor(struct work_struct *data)
{
struct efx_nic *efx = container_of(data, struct efx_nic,
monitor_work.work);
- int rc;
EFX_TRACE(efx, "hardware monitor executing on CPU %d\n",
raw_smp_processor_id());
+ BUG_ON(efx->type->monitor == NULL);
/* If the mac_lock is already held then it is likely a port
* reconfiguration is already in place, which will likely do
@@ -1262,15 +1356,7 @@ static void efx_monitor(struct work_struct *data)
goto out_requeue;
if (!efx->port_enabled)
goto out_unlock;
- rc = efx->board_info.monitor(efx);
- if (rc) {
- EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
- (rc == -ERANGE) ? "reported fault" : "failed");
- efx->phy_mode |= PHY_MODE_LOW_POWER;
- falcon_sim_phy_event(efx);
- }
- efx->phy_op->poll(efx);
- efx->mac_op->poll(efx);
+ efx->type->monitor(efx);
out_unlock:
mutex_unlock(&efx->mac_lock);
@@ -1374,6 +1460,12 @@ static int efx_net_open(struct net_device *net_dev)
return -EIO;
if (efx->phy_mode & PHY_MODE_SPECIAL)
return -EBUSY;
+ if (efx_mcdi_poll_reboot(efx) && efx_reset(efx, RESET_TYPE_ALL))
+ return -EIO;
+
+ /* Notify the kernel of the link state polled during driver load,
+ * before the monitor starts running */
+ efx_link_status_changed(efx);
efx_start_all(efx);
return 0;
@@ -1400,20 +1492,6 @@ static int efx_net_stop(struct net_device *net_dev)
return 0;
}
-void efx_stats_disable(struct efx_nic *efx)
-{
- spin_lock(&efx->stats_lock);
- ++efx->stats_disable_count;
- spin_unlock(&efx->stats_lock);
-}
-
-void efx_stats_enable(struct efx_nic *efx)
-{
- spin_lock(&efx->stats_lock);
- --efx->stats_disable_count;
- spin_unlock(&efx->stats_lock);
-}
-
/* Context: process, dev_base_lock or RTNL held, non-blocking. */
static struct net_device_stats *efx_net_stats(struct net_device *net_dev)
{
@@ -1421,17 +1499,9 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev)
struct efx_mac_stats *mac_stats = &efx->mac_stats;
struct net_device_stats *stats = &net_dev->stats;
- /* Update stats if possible, but do not wait if another thread
- * is updating them or if MAC stats fetches are temporarily
- * disabled; slightly stale stats are acceptable.
- */
- if (!spin_trylock(&efx->stats_lock))
- return stats;
- if (!efx->stats_disable_count) {
- efx->mac_op->update_stats(efx);
- falcon_update_nic_stats(efx);
- }
- spin_unlock(&efx->stats_lock);
+ spin_lock_bh(&efx->stats_lock);
+ efx->type->update_stats(efx);
+ spin_unlock_bh(&efx->stats_lock);
stats->rx_packets = mac_stats->rx_packets;
stats->tx_packets = mac_stats->tx_packets;
@@ -1490,7 +1560,14 @@ static int efx_change_mtu(struct net_device *net_dev, int new_mtu)
EFX_LOG(efx, "changing MTU to %d\n", new_mtu);
efx_fini_channels(efx);
+
+ mutex_lock(&efx->mac_lock);
+ /* Reconfigure the MAC before enabling the dma queues so that
+ * the RX buffers don't overflow */
net_dev->mtu = new_mtu;
+ efx->mac_op->reconfigure(efx);
+ mutex_unlock(&efx->mac_lock);
+
efx_init_channels(efx);
efx_start_all(efx);
@@ -1514,7 +1591,9 @@ static int efx_set_mac_address(struct net_device *net_dev, void *data)
memcpy(net_dev->dev_addr, new_addr, net_dev->addr_len);
/* Reconfigure the MAC */
- efx_reconfigure_port(efx);
+ mutex_lock(&efx->mac_lock);
+ efx->mac_op->reconfigure(efx);
+ mutex_unlock(&efx->mac_lock);
return 0;
}
@@ -1525,16 +1604,14 @@ static void efx_set_multicast_list(struct net_device *net_dev)
struct efx_nic *efx = netdev_priv(net_dev);
struct dev_mc_list *mc_list = net_dev->mc_list;
union efx_multicast_hash *mc_hash = &efx->multicast_hash;
- bool promiscuous = !!(net_dev->flags & IFF_PROMISC);
- bool changed = (efx->promiscuous != promiscuous);
u32 crc;
int bit;
int i;
- efx->promiscuous = promiscuous;
+ efx->promiscuous = !!(net_dev->flags & IFF_PROMISC);
/* Build multicast hash table */
- if (promiscuous || (net_dev->flags & IFF_ALLMULTI)) {
+ if (efx->promiscuous || (net_dev->flags & IFF_ALLMULTI)) {
memset(mc_hash, 0xff, sizeof(*mc_hash));
} else {
memset(mc_hash, 0x00, sizeof(*mc_hash));
@@ -1544,17 +1621,17 @@ static void efx_set_multicast_list(struct net_device *net_dev)
set_bit_le(bit, mc_hash->byte);
mc_list = mc_list->next;
}
- }
-
- if (!efx->port_enabled)
- /* Delay pushing settings until efx_start_port() */
- return;
- if (changed)
- queue_work(efx->workqueue, &efx->phy_work);
+ /* Broadcast packets go through the multicast hash filter.
+ * ether_crc_le() of the broadcast address is 0xbe2612ff
+ * so we always add bit 0xff to the mask.
+ */
+ set_bit_le(0xff, mc_hash->byte);
+ }
- /* Create and activate new global multicast hash table */
- falcon_set_multicast_hash(efx);
+ if (efx->port_enabled)
+ queue_work(efx->workqueue, &efx->mac_work);
+ /* Otherwise efx_start_port() will do this */
}
static const struct net_device_ops efx_netdev_ops = {
@@ -1683,21 +1760,18 @@ static void efx_unregister_netdev(struct efx_nic *efx)
/* Tears down the entire software state and most of the hardware state
* before reset. */
-void efx_reset_down(struct efx_nic *efx, enum reset_type method,
- struct ethtool_cmd *ecmd)
+void efx_reset_down(struct efx_nic *efx, enum reset_type method)
{
EFX_ASSERT_RESET_SERIALISED(efx);
- efx_stats_disable(efx);
efx_stop_all(efx);
mutex_lock(&efx->mac_lock);
mutex_lock(&efx->spi_lock);
- efx->phy_op->get_settings(efx, ecmd);
-
efx_fini_channels(efx);
if (efx->port_initialized && method != RESET_TYPE_INVISIBLE)
efx->phy_op->fini(efx);
+ efx->type->fini(efx);
}
/* This function will always ensure that the locks acquired in
@@ -1705,79 +1779,67 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method,
* that we were unable to reinitialise the hardware, and the
* driver should be disabled. If ok is false, then the rx and tx
* engines are not restarted, pending a RESET_DISABLE. */
-int efx_reset_up(struct efx_nic *efx, enum reset_type method,
- struct ethtool_cmd *ecmd, bool ok)
+int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok)
{
int rc;
EFX_ASSERT_RESET_SERIALISED(efx);
- rc = falcon_init_nic(efx);
+ rc = efx->type->init(efx);
if (rc) {
EFX_ERR(efx, "failed to initialise NIC\n");
- ok = false;
+ goto fail;
}
+ if (!ok)
+ goto fail;
+
if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) {
- if (ok) {
- rc = efx->phy_op->init(efx);
- if (rc)
- ok = false;
- }
- if (!ok)
- efx->port_initialized = false;
+ rc = efx->phy_op->init(efx);
+ if (rc)
+ goto fail;
+ if (efx->phy_op->reconfigure(efx))
+ EFX_ERR(efx, "could not restore PHY settings\n");
}
- if (ok) {
- efx_init_channels(efx);
+ efx->mac_op->reconfigure(efx);
- if (efx->phy_op->set_settings(efx, ecmd))
- EFX_ERR(efx, "could not restore PHY settings\n");
- }
+ efx_init_channels(efx);
+
+ mutex_unlock(&efx->spi_lock);
+ mutex_unlock(&efx->mac_lock);
+
+ efx_start_all(efx);
+
+ return 0;
+
+fail:
+ efx->port_initialized = false;
mutex_unlock(&efx->spi_lock);
mutex_unlock(&efx->mac_lock);
- if (ok) {
- efx_start_all(efx);
- efx_stats_enable(efx);
- }
return rc;
}
-/* Reset the NIC as transparently as possible. Do not reset the PHY
- * Note that the reset may fail, in which case the card will be left
- * in a most-probably-unusable state.
+/* Reset the NIC using the specified method. Note that the reset may
+ * fail, in which case the card will be left in an unusable state.
*
- * This function will sleep. You cannot reset from within an atomic
- * state; use efx_schedule_reset() instead.
- *
- * Grabs the rtnl_lock.
+ * Caller must hold the rtnl_lock.
*/
-static int efx_reset(struct efx_nic *efx)
+int efx_reset(struct efx_nic *efx, enum reset_type method)
{
- struct ethtool_cmd ecmd;
- enum reset_type method = efx->reset_pending;
- int rc = 0;
+ int rc, rc2;
+ bool disabled;
- /* Serialise with kernel interfaces */
- rtnl_lock();
+ EFX_INFO(efx, "resetting (%s)\n", RESET_TYPE(method));
- /* If we're not RUNNING then don't reset. Leave the reset_pending
- * flag set so that efx_pci_probe_main will be retried */
- if (efx->state != STATE_RUNNING) {
- EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n");
- goto out_unlock;
- }
+ efx_reset_down(efx, method);
- EFX_INFO(efx, "resetting (%d)\n", method);
-
- efx_reset_down(efx, method, &ecmd);
-
- rc = falcon_reset_hw(efx, method);
+ rc = efx->type->reset(efx, method);
if (rc) {
EFX_ERR(efx, "failed to reset hardware\n");
- goto out_disable;
+ goto out;
}
/* Allow resets to be rescheduled. */
@@ -1789,25 +1851,22 @@ static int efx_reset(struct efx_nic *efx)
* can respond to requests. */
pci_set_master(efx->pci_dev);
+out:
/* Leave device stopped if necessary */
- if (method == RESET_TYPE_DISABLE) {
- efx_reset_up(efx, method, &ecmd, false);
- rc = -EIO;
- } else {
- rc = efx_reset_up(efx, method, &ecmd, true);
+ disabled = rc || method == RESET_TYPE_DISABLE;
+ rc2 = efx_reset_up(efx, method, !disabled);
+ if (rc2) {
+ disabled = true;
+ if (!rc)
+ rc = rc2;
}
-out_disable:
- if (rc) {
+ if (disabled) {
EFX_ERR(efx, "has been disabled\n");
efx->state = STATE_DISABLED;
- dev_close(efx->net_dev);
} else {
EFX_LOG(efx, "reset complete\n");
}
-
-out_unlock:
- rtnl_unlock();
return rc;
}
@@ -1816,9 +1875,19 @@ out_unlock:
*/
static void efx_reset_work(struct work_struct *data)
{
- struct efx_nic *nic = container_of(data, struct efx_nic, reset_work);
+ struct efx_nic *efx = container_of(data, struct efx_nic, reset_work);
+
+ /* If we're not RUNNING then don't reset. Leave the reset_pending
+ * flag set so that efx_pci_probe_main will be retried */
+ if (efx->state != STATE_RUNNING) {
+ EFX_INFO(efx, "scheduled reset quenched. NIC not RUNNING\n");
+ return;
+ }
- efx_reset(nic);
+ rtnl_lock();
+ if (efx_reset(efx, efx->reset_pending))
+ dev_close(efx->net_dev);
+ rtnl_unlock();
}
void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
@@ -1843,18 +1912,24 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
case RESET_TYPE_TX_SKIP:
method = RESET_TYPE_INVISIBLE;
break;
+ case RESET_TYPE_MC_FAILURE:
default:
method = RESET_TYPE_ALL;
break;
}
if (method != type)
- EFX_LOG(efx, "scheduling reset (%d:%d)\n", type, method);
+ EFX_LOG(efx, "scheduling %s reset for %s\n",
+ RESET_TYPE(method), RESET_TYPE(type));
else
- EFX_LOG(efx, "scheduling reset (%d)\n", method);
+ EFX_LOG(efx, "scheduling %s reset\n", RESET_TYPE(method));
efx->reset_pending = method;
+ /* efx_process_channel() will no longer read events once a
+ * reset is scheduled. So switch back to poll'd MCDI completions. */
+ efx_mcdi_mode_poll(efx);
+
queue_work(reset_workqueue, &efx->reset_work);
}
@@ -1867,15 +1942,19 @@ void efx_schedule_reset(struct efx_nic *efx, enum reset_type type)
/* PCI device ID table */
static struct pci_device_id efx_pci_table[] __devinitdata = {
{PCI_DEVICE(EFX_VENDID_SFC, FALCON_A_P_DEVID),
- .driver_data = (unsigned long) &falcon_a_nic_type},
+ .driver_data = (unsigned long) &falcon_a1_nic_type},
{PCI_DEVICE(EFX_VENDID_SFC, FALCON_B_P_DEVID),
- .driver_data = (unsigned long) &falcon_b_nic_type},
+ .driver_data = (unsigned long) &falcon_b0_nic_type},
+ {PCI_DEVICE(EFX_VENDID_SFC, BETHPAGE_A_P_DEVID),
+ .driver_data = (unsigned long) &siena_a0_nic_type},
+ {PCI_DEVICE(EFX_VENDID_SFC, SIENA_A_P_DEVID),
+ .driver_data = (unsigned long) &siena_a0_nic_type},
{0} /* end of list */
};
/**************************************************************************
*
- * Dummy PHY/MAC/Board operations
+ * Dummy PHY/MAC operations
*
* Can be used for some unimplemented operations
* Needed so all function pointers are valid and do not have to be tested
@@ -1887,29 +1966,19 @@ int efx_port_dummy_op_int(struct efx_nic *efx)
return 0;
}
void efx_port_dummy_op_void(struct efx_nic *efx) {}
-void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink) {}
-
-static struct efx_mac_operations efx_dummy_mac_operations = {
- .reconfigure = efx_port_dummy_op_void,
- .poll = efx_port_dummy_op_void,
- .irq = efx_port_dummy_op_void,
-};
+void efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
+{
+}
+bool efx_port_dummy_op_poll(struct efx_nic *efx)
+{
+ return false;
+}
static struct efx_phy_operations efx_dummy_phy_operations = {
.init = efx_port_dummy_op_int,
- .reconfigure = efx_port_dummy_op_void,
- .poll = efx_port_dummy_op_void,
+ .reconfigure = efx_port_dummy_op_int,
+ .poll = efx_port_dummy_op_poll,
.fini = efx_port_dummy_op_void,
- .clear_interrupt = efx_port_dummy_op_void,
-};
-
-static struct efx_board efx_dummy_board_info = {
- .init = efx_port_dummy_op_int,
- .init_leds = efx_port_dummy_op_void,
- .set_id_led = efx_port_dummy_op_blink,
- .monitor = efx_port_dummy_op_int,
- .blink = efx_port_dummy_op_blink,
- .fini = efx_port_dummy_op_void,
};
/**************************************************************************
@@ -1932,26 +2001,26 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
/* Initialise common structures */
memset(efx, 0, sizeof(*efx));
spin_lock_init(&efx->biu_lock);
- spin_lock_init(&efx->phy_lock);
+ mutex_init(&efx->mdio_lock);
mutex_init(&efx->spi_lock);
+#ifdef CONFIG_SFC_MTD
+ INIT_LIST_HEAD(&efx->mtd_list);
+#endif
INIT_WORK(&efx->reset_work, efx_reset_work);
INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor);
efx->pci_dev = pci_dev;
efx->state = STATE_INIT;
efx->reset_pending = RESET_TYPE_NONE;
strlcpy(efx->name, pci_name(pci_dev), sizeof(efx->name));
- efx->board_info = efx_dummy_board_info;
efx->net_dev = net_dev;
efx->rx_checksum_enabled = true;
spin_lock_init(&efx->netif_stop_lock);
spin_lock_init(&efx->stats_lock);
- efx->stats_disable_count = 1;
mutex_init(&efx->mac_lock);
- efx->mac_op = &efx_dummy_mac_operations;
+ efx->mac_op = type->default_mac_ops;
efx->phy_op = &efx_dummy_phy_operations;
efx->mdio.dev = net_dev;
- INIT_WORK(&efx->phy_work, efx_phy_work);
INIT_WORK(&efx->mac_work, efx_mac_work);
atomic_set(&efx->netif_stop_count, 1);
@@ -1981,17 +2050,9 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
efx->type = type;
- /* Sanity-check NIC type */
- EFX_BUG_ON_PARANOID(efx->type->txd_ring_mask &
- (efx->type->txd_ring_mask + 1));
- EFX_BUG_ON_PARANOID(efx->type->rxd_ring_mask &
- (efx->type->rxd_ring_mask + 1));
- EFX_BUG_ON_PARANOID(efx->type->evq_size &
- (efx->type->evq_size - 1));
/* As close as we can get to guaranteeing that we don't overflow */
- EFX_BUG_ON_PARANOID(efx->type->evq_size <
- (efx->type->txd_ring_mask + 1 +
- efx->type->rxd_ring_mask + 1));
+ BUILD_BUG_ON(EFX_EVQ_SIZE < EFX_TXQ_SIZE + EFX_RXQ_SIZE);
+
EFX_BUG_ON_PARANOID(efx->type->phys_addr_channels > EFX_MAX_CHANNELS);
/* Higher numbered interrupt modes are less capable! */
@@ -2027,19 +2088,10 @@ static void efx_fini_struct(struct efx_nic *efx)
*/
static void efx_pci_remove_main(struct efx_nic *efx)
{
- EFX_ASSERT_RESET_SERIALISED(efx);
-
- /* Skip everything if we never obtained a valid membase */
- if (!efx->membase)
- return;
-
+ efx_nic_fini_interrupt(efx);
efx_fini_channels(efx);
efx_fini_port(efx);
-
- /* Shutdown the board, then the NIC and board state */
- efx->board_info.fini(efx);
- falcon_fini_interrupt(efx);
-
+ efx->type->fini(efx);
efx_fini_napi(efx);
efx_remove_all(efx);
}
@@ -2063,9 +2115,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
/* Allow any queued efx_resets() to complete */
rtnl_unlock();
- if (efx->membase == NULL)
- goto out;
-
efx_unregister_netdev(efx);
efx_mtd_remove(efx);
@@ -2078,7 +2127,6 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
efx_pci_remove_main(efx);
-out:
efx_fini_io(efx);
EFX_LOG(efx, "shutdown successful\n");
@@ -2103,39 +2151,31 @@ static int efx_pci_probe_main(struct efx_nic *efx)
if (rc)
goto fail2;
- /* Initialise the board */
- rc = efx->board_info.init(efx);
- if (rc) {
- EFX_ERR(efx, "failed to initialise board\n");
- goto fail3;
- }
-
- rc = falcon_init_nic(efx);
+ rc = efx->type->init(efx);
if (rc) {
EFX_ERR(efx, "failed to initialise NIC\n");
- goto fail4;
+ goto fail3;
}
rc = efx_init_port(efx);
if (rc) {
EFX_ERR(efx, "failed to initialise port\n");
- goto fail5;
+ goto fail4;
}
efx_init_channels(efx);
- rc = falcon_init_interrupt(efx);
+ rc = efx_nic_init_interrupt(efx);
if (rc)
- goto fail6;
+ goto fail5;
return 0;
- fail6:
+ fail5:
efx_fini_channels(efx);
efx_fini_port(efx);
- fail5:
fail4:
- efx->board_info.fini(efx);
+ efx->type->fini(efx);
fail3:
efx_fini_napi(efx);
fail2:
@@ -2165,9 +2205,11 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
net_dev = alloc_etherdev(sizeof(*efx));
if (!net_dev)
return -ENOMEM;
- net_dev->features |= (NETIF_F_IP_CSUM | NETIF_F_SG |
+ net_dev->features |= (type->offload_features | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO |
NETIF_F_GRO);
+ if (type->offload_features & NETIF_F_V6_CSUM)
+ net_dev->features |= NETIF_F_TSO6;
/* Mask for features that also apply to VLAN devices */
net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO);
@@ -2219,18 +2261,19 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
goto fail4;
}
- /* Switch to the running state before we expose the device to
- * the OS. This is to ensure that the initial gathering of
- * MAC stats succeeds. */
+ /* Switch to the running state before we expose the device to the OS,
+ * so that dev_open()|efx_start_all() will actually start the device */
efx->state = STATE_RUNNING;
- efx_mtd_probe(efx); /* allowed to fail */
-
rc = efx_register_netdev(efx);
if (rc)
goto fail5;
EFX_LOG(efx, "initialisation successful\n");
+
+ rtnl_lock();
+ efx_mtd_probe(efx); /* allowed to fail */
+ rtnl_unlock();
return 0;
fail5:
@@ -2246,11 +2289,107 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
return rc;
}
+static int efx_pm_freeze(struct device *dev)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+
+ efx->state = STATE_FINI;
+
+ netif_device_detach(efx->net_dev);
+
+ efx_stop_all(efx);
+ efx_fini_channels(efx);
+
+ return 0;
+}
+
+static int efx_pm_thaw(struct device *dev)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+
+ efx->state = STATE_INIT;
+
+ efx_init_channels(efx);
+
+ mutex_lock(&efx->mac_lock);
+ efx->phy_op->reconfigure(efx);
+ mutex_unlock(&efx->mac_lock);
+
+ efx_start_all(efx);
+
+ netif_device_attach(efx->net_dev);
+
+ efx->state = STATE_RUNNING;
+
+ efx->type->resume_wol(efx);
+
+ return 0;
+}
+
+static int efx_pm_poweroff(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct efx_nic *efx = pci_get_drvdata(pci_dev);
+
+ efx->type->fini(efx);
+
+ efx->reset_pending = RESET_TYPE_NONE;
+
+ pci_save_state(pci_dev);
+ return pci_set_power_state(pci_dev, PCI_D3hot);
+}
+
+/* Used for both resume and restore */
+static int efx_pm_resume(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct efx_nic *efx = pci_get_drvdata(pci_dev);
+ int rc;
+
+ rc = pci_set_power_state(pci_dev, PCI_D0);
+ if (rc)
+ return rc;
+ pci_restore_state(pci_dev);
+ rc = pci_enable_device(pci_dev);
+ if (rc)
+ return rc;
+ pci_set_master(efx->pci_dev);
+ rc = efx->type->reset(efx, RESET_TYPE_ALL);
+ if (rc)
+ return rc;
+ rc = efx->type->init(efx);
+ if (rc)
+ return rc;
+ efx_pm_thaw(dev);
+ return 0;
+}
+
+static int efx_pm_suspend(struct device *dev)
+{
+ int rc;
+
+ efx_pm_freeze(dev);
+ rc = efx_pm_poweroff(dev);
+ if (rc)
+ efx_pm_resume(dev);
+ return rc;
+}
+
+static struct dev_pm_ops efx_pm_ops = {
+ .suspend = efx_pm_suspend,
+ .resume = efx_pm_resume,
+ .freeze = efx_pm_freeze,
+ .thaw = efx_pm_thaw,
+ .poweroff = efx_pm_poweroff,
+ .restore = efx_pm_resume,
+};
+
static struct pci_driver efx_pci_driver = {
.name = EFX_DRIVER_NAME,
.id_table = efx_pci_table,
.probe = efx_pci_probe,
.remove = efx_pci_remove,
+ .driver.pm = &efx_pm_ops,
};
/**************************************************************************
@@ -2314,8 +2453,8 @@ static void __exit efx_exit_module(void)
module_init(efx_init_module);
module_exit(efx_exit_module);
-MODULE_AUTHOR("Michael Brown <mbrown@fensystems.co.uk> and "
- "Solarflare Communications");
+MODULE_AUTHOR("Solarflare Communications and "
+ "Michael Brown <mbrown@fensystems.co.uk>");
MODULE_DESCRIPTION("Solarflare Communications network driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, efx_pci_table);
diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h
index aecaf62f492..a615ac05153 100644
--- a/drivers/net/sfc/efx.h
+++ b/drivers/net/sfc/efx.h
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -18,35 +18,64 @@
#define FALCON_A_P_DEVID 0x0703
#define FALCON_A_S_DEVID 0x6703
#define FALCON_B_P_DEVID 0x0710
+#define BETHPAGE_A_P_DEVID 0x0803
+#define SIENA_A_P_DEVID 0x0813
+
+/* Solarstorm controllers use BAR 0 for I/O space and BAR 2(&3) for memory */
+#define EFX_MEM_BAR 2
/* TX */
-extern netdev_tx_t efx_xmit(struct efx_nic *efx,
- struct efx_tx_queue *tx_queue,
- struct sk_buff *skb);
+extern int efx_probe_tx_queue(struct efx_tx_queue *tx_queue);
+extern void efx_remove_tx_queue(struct efx_tx_queue *tx_queue);
+extern void efx_init_tx_queue(struct efx_tx_queue *tx_queue);
+extern void efx_fini_tx_queue(struct efx_tx_queue *tx_queue);
+extern void efx_release_tx_buffers(struct efx_tx_queue *tx_queue);
+extern netdev_tx_t
+efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev);
+extern netdev_tx_t
+efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
+extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
extern void efx_stop_queue(struct efx_nic *efx);
extern void efx_wake_queue(struct efx_nic *efx);
+#define EFX_TXQ_SIZE 1024
+#define EFX_TXQ_MASK (EFX_TXQ_SIZE - 1)
/* RX */
-extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
+extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
+extern void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
+extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue);
+extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
+extern void efx_rx_strategy(struct efx_channel *channel);
+extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
+extern void efx_rx_work(struct work_struct *data);
+extern void __efx_rx_packet(struct efx_channel *channel,
+ struct efx_rx_buffer *rx_buf, bool checksummed);
extern void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index,
unsigned int len, bool checksummed, bool discard);
extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue, int delay);
+#define EFX_RXQ_SIZE 1024
+#define EFX_RXQ_MASK (EFX_RXQ_SIZE - 1)
/* Channels */
extern void efx_process_channel_now(struct efx_channel *channel);
-extern void efx_flush_queues(struct efx_nic *efx);
+#define EFX_EVQ_SIZE 4096
+#define EFX_EVQ_MASK (EFX_EVQ_SIZE - 1)
/* Ports */
-extern void efx_stats_disable(struct efx_nic *efx);
-extern void efx_stats_enable(struct efx_nic *efx);
-extern void efx_reconfigure_port(struct efx_nic *efx);
-extern void __efx_reconfigure_port(struct efx_nic *efx);
+extern int efx_reconfigure_port(struct efx_nic *efx);
+extern int __efx_reconfigure_port(struct efx_nic *efx);
+
+/* Ethtool support */
+extern int efx_ethtool_get_settings(struct net_device *net_dev,
+ struct ethtool_cmd *ecmd);
+extern int efx_ethtool_set_settings(struct net_device *net_dev,
+ struct ethtool_cmd *ecmd);
+extern const struct ethtool_ops efx_ethtool_ops;
/* Reset handling */
-extern void efx_reset_down(struct efx_nic *efx, enum reset_type method,
- struct ethtool_cmd *ecmd);
-extern int efx_reset_up(struct efx_nic *efx, enum reset_type method,
- struct ethtool_cmd *ecmd, bool ok);
+extern int efx_reset(struct efx_nic *efx, enum reset_type method);
+extern void efx_reset_down(struct efx_nic *efx, enum reset_type method);
+extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok);
/* Global */
extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
@@ -60,7 +89,9 @@ extern void efx_hex_dump(const u8 *, unsigned int, const char *);
/* Dummy PHY ops for PHY drivers */
extern int efx_port_dummy_op_int(struct efx_nic *efx);
extern void efx_port_dummy_op_void(struct efx_nic *efx);
-extern void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink);
+extern void
+efx_port_dummy_op_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
+extern bool efx_port_dummy_op_poll(struct efx_nic *efx);
/* MTD */
#ifdef CONFIG_SFC_MTD
@@ -84,4 +115,8 @@ static inline void efx_schedule_channel(struct efx_channel *channel)
napi_schedule(&channel->napi_str);
}
+extern void efx_link_status_changed(struct efx_nic *efx);
+extern void efx_link_set_advertising(struct efx_nic *efx, u32);
+extern void efx_link_set_wanted_fc(struct efx_nic *efx, enum efx_fc_type);
+
#endif /* EFX_EFX_H */
diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h
index 60cbc6e1e66..384cfe3b1be 100644
--- a/drivers/net/sfc/enum.h
+++ b/drivers/net/sfc/enum.h
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007-2008 Solarflare Communications Inc.
+ * Copyright 2007-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -13,44 +13,101 @@
/**
* enum efx_loopback_mode - loopback modes
* @LOOPBACK_NONE: no loopback
- * @LOOPBACK_GMAC: loopback within GMAC at unspecified level
- * @LOOPBACK_XGMII: loopback within XMAC at XGMII level
- * @LOOPBACK_XGXS: loopback within XMAC at XGXS level
- * @LOOPBACK_XAUI: loopback within XMAC at XAUI level
+ * @LOOPBACK_DATA: data path loopback
+ * @LOOPBACK_GMAC: loopback within GMAC
+ * @LOOPBACK_XGMII: loopback after XMAC
+ * @LOOPBACK_XGXS: loopback within BPX after XGXS
+ * @LOOPBACK_XAUI: loopback within BPX before XAUI serdes
+ * @LOOPBACK_GMII: loopback within BPX after GMAC
+ * @LOOPBACK_SGMII: loopback within BPX within SGMII
+ * @LOOPBACK_XGBR: loopback within BPX within XGBR
+ * @LOOPBACK_XFI: loopback within BPX before XFI serdes
+ * @LOOPBACK_XAUI_FAR: loopback within BPX after XAUI serdes
+ * @LOOPBACK_GMII_FAR: loopback within BPX before SGMII
+ * @LOOPBACK_SGMII_FAR: loopback within BPX after SGMII
+ * @LOOPBACK_XFI_FAR: loopback after XFI serdes
* @LOOPBACK_GPHY: loopback within 1G PHY at unspecified level
* @LOOPBACK_PHYXS: loopback within 10G PHY at PHYXS level
* @LOOPBACK_PCS: loopback within 10G PHY at PCS level
* @LOOPBACK_PMAPMD: loopback within 10G PHY at PMAPMD level
- * @LOOPBACK_NETWORK: reflecting loopback (even further than furthest!)
+ * @LOOPBACK_XPORT: cross port loopback
+ * @LOOPBACK_XGMII_WS: wireside loopback excluding XMAC
+ * @LOOPBACK_XAUI_WS: wireside loopback within BPX within XAUI serdes
+ * @LOOPBACK_XAUI_WS_FAR: wireside loopback within BPX including XAUI serdes
+ * @LOOPBACK_XAUI_WS_NEAR: wireside loopback within BPX excluding XAUI serdes
+ * @LOOPBACK_GMII_WS: wireside loopback excluding GMAC
+ * @LOOPBACK_XFI_WS: wireside loopback excluding XFI serdes
+ * @LOOPBACK_XFI_WS_FAR: wireside loopback including XFI serdes
+ * @LOOPBACK_PHYXS_WS: wireside loopback within 10G PHY at PHYXS level
*/
-/* Please keep in order and up-to-date w.r.t the following two #defines */
+/* Please keep up-to-date w.r.t the following two #defines */
enum efx_loopback_mode {
LOOPBACK_NONE = 0,
- LOOPBACK_GMAC = 1,
- LOOPBACK_XGMII = 2,
- LOOPBACK_XGXS = 3,
- LOOPBACK_XAUI = 4,
- LOOPBACK_GPHY = 5,
- LOOPBACK_PHYXS = 6,
- LOOPBACK_PCS = 7,
- LOOPBACK_PMAPMD = 8,
- LOOPBACK_NETWORK = 9,
+ LOOPBACK_DATA = 1,
+ LOOPBACK_GMAC = 2,
+ LOOPBACK_XGMII = 3,
+ LOOPBACK_XGXS = 4,
+ LOOPBACK_XAUI = 5,
+ LOOPBACK_GMII = 6,
+ LOOPBACK_SGMII = 7,
+ LOOPBACK_XGBR = 8,
+ LOOPBACK_XFI = 9,
+ LOOPBACK_XAUI_FAR = 10,
+ LOOPBACK_GMII_FAR = 11,
+ LOOPBACK_SGMII_FAR = 12,
+ LOOPBACK_XFI_FAR = 13,
+ LOOPBACK_GPHY = 14,
+ LOOPBACK_PHYXS = 15,
+ LOOPBACK_PCS = 16,
+ LOOPBACK_PMAPMD = 17,
+ LOOPBACK_XPORT = 18,
+ LOOPBACK_XGMII_WS = 19,
+ LOOPBACK_XAUI_WS = 20,
+ LOOPBACK_XAUI_WS_FAR = 21,
+ LOOPBACK_XAUI_WS_NEAR = 22,
+ LOOPBACK_GMII_WS = 23,
+ LOOPBACK_XFI_WS = 24,
+ LOOPBACK_XFI_WS_FAR = 25,
+ LOOPBACK_PHYXS_WS = 26,
LOOPBACK_MAX
};
-
#define LOOPBACK_TEST_MAX LOOPBACK_PMAPMD
-extern const char *efx_loopback_mode_names[];
-#define LOOPBACK_MODE_NAME(mode) \
- STRING_TABLE_LOOKUP(mode, efx_loopback_mode)
-#define LOOPBACK_MODE(efx) \
- LOOPBACK_MODE_NAME(efx->loopback_mode)
-
/* These loopbacks occur within the controller */
-#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_GMAC) | \
- (1 << LOOPBACK_XGMII)| \
- (1 << LOOPBACK_XGXS) | \
- (1 << LOOPBACK_XAUI))
+#define LOOPBACKS_INTERNAL ((1 << LOOPBACK_DATA) | \
+ (1 << LOOPBACK_GMAC) | \
+ (1 << LOOPBACK_XGMII)| \
+ (1 << LOOPBACK_XGXS) | \
+ (1 << LOOPBACK_XAUI) | \
+ (1 << LOOPBACK_GMII) | \
+ (1 << LOOPBACK_SGMII) | \
+ (1 << LOOPBACK_SGMII) | \
+ (1 << LOOPBACK_XGBR) | \
+ (1 << LOOPBACK_XFI) | \
+ (1 << LOOPBACK_XAUI_FAR) | \
+ (1 << LOOPBACK_GMII_FAR) | \
+ (1 << LOOPBACK_SGMII_FAR) | \
+ (1 << LOOPBACK_XFI_FAR) | \
+ (1 << LOOPBACK_XGMII_WS) | \
+ (1 << LOOPBACK_XAUI_WS) | \
+ (1 << LOOPBACK_XAUI_WS_FAR) | \
+ (1 << LOOPBACK_XAUI_WS_NEAR) | \
+ (1 << LOOPBACK_GMII_WS) | \
+ (1 << LOOPBACK_XFI_WS) | \
+ (1 << LOOPBACK_XFI_WS_FAR))
+
+#define LOOPBACKS_WS ((1 << LOOPBACK_XGMII_WS) | \
+ (1 << LOOPBACK_XAUI_WS) | \
+ (1 << LOOPBACK_XAUI_WS_FAR) | \
+ (1 << LOOPBACK_XAUI_WS_NEAR) | \
+ (1 << LOOPBACK_GMII_WS) | \
+ (1 << LOOPBACK_XFI_WS) | \
+ (1 << LOOPBACK_XFI_WS_FAR) | \
+ (1 << LOOPBACK_PHYXS_WS))
+
+#define LOOPBACKS_EXTERNAL(_efx) \
+ ((_efx)->loopback_modes & ~LOOPBACKS_INTERNAL & \
+ ~(1 << LOOPBACK_NONE))
#define LOOPBACK_MASK(_efx) \
(1 << (_efx)->loopback_mode)
@@ -58,6 +115,9 @@ extern const char *efx_loopback_mode_names[];
#define LOOPBACK_INTERNAL(_efx) \
(!!(LOOPBACKS_INTERNAL & LOOPBACK_MASK(_efx)))
+#define LOOPBACK_EXTERNAL(_efx) \
+ (!!(LOOPBACK_MASK(_efx) & LOOPBACKS_EXTERNAL(_efx)))
+
#define LOOPBACK_CHANGED(_from, _to, _mask) \
(!!((LOOPBACK_MASK(_from) ^ LOOPBACK_MASK(_to)) & (_mask)))
@@ -84,6 +144,7 @@ extern const char *efx_loopback_mode_names[];
* @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch
* @RESET_TYPE_TX_DESC_FETCH: pcie error during tx descriptor fetch
* @RESET_TYPE_TX_SKIP: hardware completed empty tx descriptors
+ * @RESET_TYPE_MC_FAILURE: MC reboot/assertion
*/
enum reset_type {
RESET_TYPE_NONE = -1,
@@ -98,6 +159,7 @@ enum reset_type {
RESET_TYPE_RX_DESC_FETCH,
RESET_TYPE_TX_DESC_FETCH,
RESET_TYPE_TX_SKIP,
+ RESET_TYPE_MC_FAILURE,
RESET_TYPE_MAX,
};
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index 45018f283ff..6c0bbed8c47 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -10,30 +10,15 @@
#include <linux/netdevice.h>
#include <linux/ethtool.h>
-#include <linux/mdio.h>
#include <linux/rtnetlink.h>
#include "net_driver.h"
#include "workarounds.h"
#include "selftest.h"
#include "efx.h"
-#include "ethtool.h"
-#include "falcon.h"
+#include "nic.h"
#include "spi.h"
#include "mdio_10g.h"
-const char *efx_loopback_mode_names[] = {
- [LOOPBACK_NONE] = "NONE",
- [LOOPBACK_GMAC] = "GMAC",
- [LOOPBACK_XGMII] = "XGMII",
- [LOOPBACK_XGXS] = "XGXS",
- [LOOPBACK_XAUI] = "XAUI",
- [LOOPBACK_GPHY] = "GPHY",
- [LOOPBACK_PHYXS] = "PHYXS",
- [LOOPBACK_PCS] = "PCS",
- [LOOPBACK_PMAPMD] = "PMA/PMD",
- [LOOPBACK_NETWORK] = "NETWORK",
-};
-
struct ethtool_string {
char name[ETH_GSTRING_LEN];
};
@@ -167,6 +152,7 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc),
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err),
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tcp_udp_chksum_err),
+ EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_mcast_mismatch),
EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_frm_trunc),
};
@@ -187,13 +173,15 @@ static int efx_ethtool_phys_id(struct net_device *net_dev, u32 count)
{
struct efx_nic *efx = netdev_priv(net_dev);
- efx->board_info.blink(efx, 1);
- set_current_state(TASK_INTERRUPTIBLE);
- if (count)
- schedule_timeout(count * HZ);
- else
- schedule();
- efx->board_info.blink(efx, 0);
+ do {
+ efx->type->set_id_led(efx, EFX_LED_ON);
+ schedule_timeout_interruptible(HZ / 2);
+
+ efx->type->set_id_led(efx, EFX_LED_OFF);
+ schedule_timeout_interruptible(HZ / 2);
+ } while (!signal_pending(current) && --count != 0);
+
+ efx->type->set_id_led(efx, EFX_LED_DEFAULT);
return 0;
}
@@ -202,6 +190,7 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
struct ethtool_cmd *ecmd)
{
struct efx_nic *efx = netdev_priv(net_dev);
+ struct efx_link_state *link_state = &efx->link_state;
mutex_lock(&efx->mac_lock);
efx->phy_op->get_settings(efx, ecmd);
@@ -209,6 +198,13 @@ int efx_ethtool_get_settings(struct net_device *net_dev,
/* Falcon GMAC does not support 1000Mbps HD */
ecmd->supported &= ~SUPPORTED_1000baseT_Half;
+ /* Both MACs support pause frames (bidirectional and respond-only) */
+ ecmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+ if (LOOPBACK_INTERNAL(efx)) {
+ ecmd->speed = link_state->speed;
+ ecmd->duplex = link_state->fd ? DUPLEX_FULL : DUPLEX_HALF;
+ }
return 0;
}
@@ -230,9 +226,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev,
mutex_lock(&efx->mac_lock);
rc = efx->phy_op->set_settings(efx, ecmd);
mutex_unlock(&efx->mac_lock);
- if (!rc)
- efx_reconfigure_port(efx);
-
return rc;
}
@@ -243,6 +236,9 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver));
strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version));
+ if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
+ siena_print_fwver(efx, info->fw_version,
+ sizeof(info->fw_version));
strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
}
@@ -289,7 +285,7 @@ static void efx_fill_test(unsigned int test_index,
#define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue
#define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue
#define EFX_LOOPBACK_NAME(_mode, _counter) \
- "loopback.%s." _counter, LOOPBACK_MODE_NAME(mode)
+ "loopback.%s." _counter, STRING_TABLE_LOOKUP(_mode, efx_loopback_mode)
/**
* efx_fill_loopback_test - fill in a block of loopback self-test entries
@@ -372,9 +368,21 @@ static int efx_ethtool_fill_self_tests(struct efx_nic *efx,
efx_fill_test(n++, strings, data, &tests->registers,
"core", 0, "registers", NULL);
- for (i = 0; i < efx->phy_op->num_tests; i++)
- efx_fill_test(n++, strings, data, &tests->phy[i],
- "phy", 0, efx->phy_op->test_names[i], NULL);
+ if (efx->phy_op->run_tests != NULL) {
+ EFX_BUG_ON_PARANOID(efx->phy_op->test_name == NULL);
+
+ for (i = 0; true; ++i) {
+ const char *name;
+
+ EFX_BUG_ON_PARANOID(i >= EFX_MAX_PHY_TESTS);
+ name = efx->phy_op->test_name(efx, i);
+ if (name == NULL)
+ break;
+
+ efx_fill_test(n++, strings, data, &tests->phy[i],
+ "phy", 0, name, NULL);
+ }
+ }
/* Loopback tests */
for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) {
@@ -463,6 +471,36 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
}
}
+static int efx_ethtool_set_tso(struct net_device *net_dev, u32 enable)
+{
+ struct efx_nic *efx __attribute__ ((unused)) = netdev_priv(net_dev);
+ unsigned long features;
+
+ features = NETIF_F_TSO;
+ if (efx->type->offload_features & NETIF_F_V6_CSUM)
+ features |= NETIF_F_TSO6;
+
+ if (enable)
+ net_dev->features |= features;
+ else
+ net_dev->features &= ~features;
+
+ return 0;
+}
+
+static int efx_ethtool_set_tx_csum(struct net_device *net_dev, u32 enable)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ unsigned long features = efx->type->offload_features & NETIF_F_ALL_CSUM;
+
+ if (enable)
+ net_dev->features |= features;
+ else
+ net_dev->features &= ~features;
+
+ return 0;
+}
+
static int efx_ethtool_set_rx_csum(struct net_device *net_dev, u32 enable)
{
struct efx_nic *efx = netdev_priv(net_dev);
@@ -537,7 +575,7 @@ static u32 efx_ethtool_get_link(struct net_device *net_dev)
{
struct efx_nic *efx = netdev_priv(net_dev);
- return efx->link_up;
+ return efx->link_state.up;
}
static int efx_ethtool_get_eeprom_len(struct net_device *net_dev)
@@ -562,7 +600,8 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev,
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
- rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
+ rc = falcon_spi_read(efx, spi,
+ eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
@@ -585,7 +624,8 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev,
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
- rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
+ rc = falcon_spi_write(efx, spi,
+ eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
eeprom->len, &len, buf);
mutex_unlock(&efx->spi_lock);
@@ -618,6 +658,9 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev,
coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive;
coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation;
+ coalesce->tx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION;
+ coalesce->rx_coalesce_usecs_irq *= EFX_IRQ_MOD_RESOLUTION;
+
return 0;
}
@@ -656,13 +699,8 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
}
efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive);
-
- /* Reset channel to pick up new moderation value. Note that
- * this may change the value of the irq_moderation field
- * (e.g. to allow for hardware timer granularity).
- */
efx_for_each_channel(channel, efx)
- falcon_set_int_moderation(channel);
+ efx->type->push_irq_moderation(channel);
return 0;
}
@@ -671,8 +709,12 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
struct ethtool_pauseparam *pause)
{
struct efx_nic *efx = netdev_priv(net_dev);
- enum efx_fc_type wanted_fc;
+ enum efx_fc_type wanted_fc, old_fc;
+ u32 old_adv;
bool reset;
+ int rc = 0;
+
+ mutex_lock(&efx->mac_lock);
wanted_fc = ((pause->rx_pause ? EFX_FC_RX : 0) |
(pause->tx_pause ? EFX_FC_TX : 0) |
@@ -680,14 +722,14 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
- if (!(efx->phy_op->mmds & MDIO_DEVS_AN) &&
- (wanted_fc & EFX_FC_AUTO)) {
- EFX_LOG(efx, "PHY does not support flow control "
- "autonegotiation\n");
- return -EINVAL;
+ if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising) {
+ EFX_LOG(efx, "Autonegotiation is disabled\n");
+ rc = -EINVAL;
+ goto out;
}
/* TX flow control may automatically turn itself off if the
@@ -697,27 +739,40 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
* and fix it be cycling transmit flow control on this end. */
reset = (wanted_fc & EFX_FC_TX) && !(efx->wanted_fc & EFX_FC_TX);
if (EFX_WORKAROUND_11482(efx) && reset) {
- if (falcon_rev(efx) >= FALCON_REV_B0) {
+ if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) {
/* Recover by resetting the EM block */
- if (efx->link_up)
- falcon_drain_tx_fifo(efx);
+ falcon_stop_nic_stats(efx);
+ falcon_drain_tx_fifo(efx);
+ efx->mac_op->reconfigure(efx);
+ falcon_start_nic_stats(efx);
} else {
/* Schedule a reset to recover */
efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
}
}
- /* Try to push the pause parameters */
- mutex_lock(&efx->mac_lock);
+ old_adv = efx->link_advertising;
+ old_fc = efx->wanted_fc;
+ efx_link_set_wanted_fc(efx, wanted_fc);
+ if (efx->link_advertising != old_adv ||
+ (efx->wanted_fc ^ old_fc) & EFX_FC_AUTO) {
+ rc = efx->phy_op->reconfigure(efx);
+ if (rc) {
+ EFX_ERR(efx, "Unable to advertise requested flow "
+ "control setting\n");
+ goto out;
+ }
+ }
- efx->wanted_fc = wanted_fc;
- if (efx->phy_op->mmds & MDIO_DEVS_AN)
- mdio45_ethtool_spauseparam_an(&efx->mdio, pause);
- __efx_reconfigure_port(efx);
+ /* Reconfigure the MAC. The PHY *may* generate a link state change event
+ * if the user just changed the advertised capabilities, but there's no
+ * harm doing this twice */
+ efx->mac_op->reconfigure(efx);
+out:
mutex_unlock(&efx->mac_lock);
- return 0;
+ return rc;
}
static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
@@ -731,6 +786,50 @@ static void efx_ethtool_get_pauseparam(struct net_device *net_dev,
}
+static void efx_ethtool_get_wol(struct net_device *net_dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ return efx->type->get_wol(efx, wol);
+}
+
+
+static int efx_ethtool_set_wol(struct net_device *net_dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ return efx->type->set_wol(efx, wol->wolopts);
+}
+
+extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ enum reset_type method;
+ enum {
+ ETH_RESET_EFX_INVISIBLE = (ETH_RESET_DMA | ETH_RESET_FILTER |
+ ETH_RESET_OFFLOAD | ETH_RESET_MAC)
+ };
+
+ /* Check for minimal reset flags */
+ if ((*flags & ETH_RESET_EFX_INVISIBLE) != ETH_RESET_EFX_INVISIBLE)
+ return -EINVAL;
+ *flags ^= ETH_RESET_EFX_INVISIBLE;
+ method = RESET_TYPE_INVISIBLE;
+
+ if (*flags & ETH_RESET_PHY) {
+ *flags ^= ETH_RESET_PHY;
+ method = RESET_TYPE_ALL;
+ }
+
+ if ((*flags & efx->type->reset_world_flags) ==
+ efx->type->reset_world_flags) {
+ *flags ^= efx->type->reset_world_flags;
+ method = RESET_TYPE_WORLD;
+ }
+
+ return efx_reset(efx, method);
+}
+
const struct ethtool_ops efx_ethtool_ops = {
.get_settings = efx_ethtool_get_settings,
.set_settings = efx_ethtool_set_settings,
@@ -747,11 +846,13 @@ const struct ethtool_ops efx_ethtool_ops = {
.get_rx_csum = efx_ethtool_get_rx_csum,
.set_rx_csum = efx_ethtool_set_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
- .set_tx_csum = ethtool_op_set_tx_csum,
+ /* Need to enable/disable IPv6 too */
+ .set_tx_csum = efx_ethtool_set_tx_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
- .set_tso = ethtool_op_set_tso,
+ /* Need to enable/disable TSO-IPv6 too */
+ .set_tso = efx_ethtool_set_tso,
.get_flags = ethtool_op_get_flags,
.set_flags = ethtool_op_set_flags,
.get_sset_count = efx_ethtool_get_sset_count,
@@ -759,4 +860,7 @@ const struct ethtool_ops efx_ethtool_ops = {
.get_strings = efx_ethtool_get_strings,
.phys_id = efx_ethtool_phys_id,
.get_ethtool_stats = efx_ethtool_get_stats,
+ .get_wol = efx_ethtool_get_wol,
+ .set_wol = efx_ethtool_set_wol,
+ .reset = efx_ethtool_reset,
};
diff --git a/drivers/net/sfc/ethtool.h b/drivers/net/sfc/ethtool.h
deleted file mode 100644
index 295ead40335..00000000000
--- a/drivers/net/sfc/ethtool.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2005 Fen Systems Ltd.
- * Copyright 2006 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_ETHTOOL_H
-#define EFX_ETHTOOL_H
-
-#include "net_driver.h"
-
-/*
- * Ethtool support
- */
-
-extern int efx_ethtool_get_settings(struct net_device *net_dev,
- struct ethtool_cmd *ecmd);
-extern int efx_ethtool_set_settings(struct net_device *net_dev,
- struct ethtool_cmd *ecmd);
-
-extern const struct ethtool_ops efx_ethtool_ops;
-
-#endif /* EFX_ETHTOOL_H */
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index c049364aec4..17afcd26e87 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -14,66 +14,20 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
#include <linux/mii.h>
#include "net_driver.h"
#include "bitfield.h"
#include "efx.h"
#include "mac.h"
#include "spi.h"
-#include "falcon.h"
-#include "falcon_hwdefs.h"
-#include "falcon_io.h"
+#include "nic.h"
+#include "regs.h"
+#include "io.h"
#include "mdio_10g.h"
#include "phy.h"
-#include "boards.h"
#include "workarounds.h"
-/* Falcon hardware control.
- * Falcon is the internal codename for the SFC4000 controller that is
- * present in SFE400X evaluation boards
- */
-
-/**
- * struct falcon_nic_data - Falcon NIC state
- * @next_buffer_table: First available buffer table id
- * @pci_dev2: The secondary PCI device if present
- * @i2c_data: Operations and state for I2C bit-bashing algorithm
- * @int_error_count: Number of internal errors seen recently
- * @int_error_expire: Time at which error count will be expired
- */
-struct falcon_nic_data {
- unsigned next_buffer_table;
- struct pci_dev *pci_dev2;
- struct i2c_algo_bit_data i2c_data;
-
- unsigned int_error_count;
- unsigned long int_error_expire;
-};
-
-/**************************************************************************
- *
- * Configurable values
- *
- **************************************************************************
- */
-
-static int disable_dma_stats;
-
-/* This is set to 16 for a good reason. In summary, if larger than
- * 16, the descriptor cache holds more than a default socket
- * buffer's worth of packets (for UDP we can only have at most one
- * socket buffer's worth outstanding). This combined with the fact
- * that we only get 1 TX event per descriptor cache means the NIC
- * goes idle.
- */
-#define TX_DC_ENTRIES 16
-#define TX_DC_ENTRIES_ORDER 0
-#define TX_DC_BASE 0x130000
-
-#define RX_DC_ENTRIES 64
-#define RX_DC_ENTRIES_ORDER 2
-#define RX_DC_BASE 0x100000
+/* Hardware control for SFC4000 (aka Falcon). */
static const unsigned int
/* "Large" EEPROM device: Atmel AT25640 or similar
@@ -89,104 +43,6 @@ default_flash_type = ((17 << SPI_DEV_TYPE_SIZE_LBN)
| (15 << SPI_DEV_TYPE_ERASE_SIZE_LBN)
| (8 << SPI_DEV_TYPE_BLOCK_SIZE_LBN));
-/* RX FIFO XOFF watermark
- *
- * When the amount of the RX FIFO increases used increases past this
- * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A)
- * This also has an effect on RX/TX arbitration
- */
-static int rx_xoff_thresh_bytes = -1;
-module_param(rx_xoff_thresh_bytes, int, 0644);
-MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold");
-
-/* RX FIFO XON watermark
- *
- * When the amount of the RX FIFO used decreases below this
- * watermark send XON. Only used if TX flow control is enabled (ethtool -A)
- * This also has an effect on RX/TX arbitration
- */
-static int rx_xon_thresh_bytes = -1;
-module_param(rx_xon_thresh_bytes, int, 0644);
-MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold");
-
-/* TX descriptor ring size - min 512 max 4k */
-#define FALCON_TXD_RING_ORDER TX_DESCQ_SIZE_1K
-#define FALCON_TXD_RING_SIZE 1024
-#define FALCON_TXD_RING_MASK (FALCON_TXD_RING_SIZE - 1)
-
-/* RX descriptor ring size - min 512 max 4k */
-#define FALCON_RXD_RING_ORDER RX_DESCQ_SIZE_1K
-#define FALCON_RXD_RING_SIZE 1024
-#define FALCON_RXD_RING_MASK (FALCON_RXD_RING_SIZE - 1)
-
-/* Event queue size - max 32k */
-#define FALCON_EVQ_ORDER EVQ_SIZE_4K
-#define FALCON_EVQ_SIZE 4096
-#define FALCON_EVQ_MASK (FALCON_EVQ_SIZE - 1)
-
-/* If FALCON_MAX_INT_ERRORS internal errors occur within
- * FALCON_INT_ERROR_EXPIRE seconds, we consider the NIC broken and
- * disable it.
- */
-#define FALCON_INT_ERROR_EXPIRE 3600
-#define FALCON_MAX_INT_ERRORS 5
-
-/* We poll for events every FLUSH_INTERVAL ms, and check FLUSH_POLL_COUNT times
- */
-#define FALCON_FLUSH_INTERVAL 10
-#define FALCON_FLUSH_POLL_COUNT 100
-
-/**************************************************************************
- *
- * Falcon constants
- *
- **************************************************************************
- */
-
-/* DMA address mask */
-#define FALCON_DMA_MASK DMA_BIT_MASK(46)
-
-/* TX DMA length mask (13-bit) */
-#define FALCON_TX_DMA_MASK (4096 - 1)
-
-/* Size and alignment of special buffers (4KB) */
-#define FALCON_BUF_SIZE 4096
-
-/* Dummy SRAM size code */
-#define SRM_NB_BSZ_ONCHIP_ONLY (-1)
-
-#define FALCON_IS_DUAL_FUNC(efx) \
- (falcon_rev(efx) < FALCON_REV_B0)
-
-/**************************************************************************
- *
- * Falcon hardware access
- *
- **************************************************************************/
-
-/* Read the current event from the event queue */
-static inline efx_qword_t *falcon_event(struct efx_channel *channel,
- unsigned int index)
-{
- return (((efx_qword_t *) (channel->eventq.addr)) + index);
-}
-
-/* See if an event is present
- *
- * We check both the high and low dword of the event for all ones. We
- * wrote all ones when we cleared the event, and no valid event can
- * have all ones in either its high or low dwords. This approach is
- * robust against reordering.
- *
- * Note that using a single 64-bit comparison is incorrect; even
- * though the CPU read will be atomic, the DMA write may not be.
- */
-static inline int falcon_event_present(efx_qword_t *event)
-{
- return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) |
- EFX_DWORD_IS_ALL_ONES(event->dword[1])));
-}
-
/**************************************************************************
*
* I2C bus - this is a bit-bashing interface using GPIO pins
@@ -200,9 +56,9 @@ static void falcon_setsda(void *data, int state)
struct efx_nic *efx = (struct efx_nic *)data;
efx_oword_t reg;
- falcon_read(efx, &reg, GPIO_CTL_REG_KER);
- EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, !state);
- falcon_write(efx, &reg, GPIO_CTL_REG_KER);
+ efx_reado(efx, &reg, FR_AB_GPIO_CTL);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN, !state);
+ efx_writeo(efx, &reg, FR_AB_GPIO_CTL);
}
static void falcon_setscl(void *data, int state)
@@ -210,9 +66,9 @@ static void falcon_setscl(void *data, int state)
struct efx_nic *efx = (struct efx_nic *)data;
efx_oword_t reg;
- falcon_read(efx, &reg, GPIO_CTL_REG_KER);
- EFX_SET_OWORD_FIELD(reg, GPIO0_OEN, !state);
- falcon_write(efx, &reg, GPIO_CTL_REG_KER);
+ efx_reado(efx, &reg, FR_AB_GPIO_CTL);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO0_OEN, !state);
+ efx_writeo(efx, &reg, FR_AB_GPIO_CTL);
}
static int falcon_getsda(void *data)
@@ -220,8 +76,8 @@ static int falcon_getsda(void *data)
struct efx_nic *efx = (struct efx_nic *)data;
efx_oword_t reg;
- falcon_read(efx, &reg, GPIO_CTL_REG_KER);
- return EFX_OWORD_FIELD(reg, GPIO3_IN);
+ efx_reado(efx, &reg, FR_AB_GPIO_CTL);
+ return EFX_OWORD_FIELD(reg, FRF_AB_GPIO3_IN);
}
static int falcon_getscl(void *data)
@@ -229,8 +85,8 @@ static int falcon_getscl(void *data)
struct efx_nic *efx = (struct efx_nic *)data;
efx_oword_t reg;
- falcon_read(efx, &reg, GPIO_CTL_REG_KER);
- return EFX_OWORD_FIELD(reg, GPIO0_IN);
+ efx_reado(efx, &reg, FR_AB_GPIO_CTL);
+ return EFX_OWORD_FIELD(reg, FRF_AB_GPIO0_IN);
}
static struct i2c_algo_bit_data falcon_i2c_bit_operations = {
@@ -243,1115 +99,39 @@ static struct i2c_algo_bit_data falcon_i2c_bit_operations = {
.timeout = DIV_ROUND_UP(HZ, 20),
};
-/**************************************************************************
- *
- * Falcon special buffer handling
- * Special buffers are used for event queues and the TX and RX
- * descriptor rings.
- *
- *************************************************************************/
-
-/*
- * Initialise a Falcon special buffer
- *
- * This will define a buffer (previously allocated via
- * falcon_alloc_special_buffer()) in Falcon's buffer table, allowing
- * it to be used for event queues, descriptor rings etc.
- */
-static void
-falcon_init_special_buffer(struct efx_nic *efx,
- struct efx_special_buffer *buffer)
-{
- efx_qword_t buf_desc;
- int index;
- dma_addr_t dma_addr;
- int i;
-
- EFX_BUG_ON_PARANOID(!buffer->addr);
-
- /* Write buffer descriptors to NIC */
- for (i = 0; i < buffer->entries; i++) {
- index = buffer->index + i;
- dma_addr = buffer->dma_addr + (i * 4096);
- EFX_LOG(efx, "mapping special buffer %d at %llx\n",
- index, (unsigned long long)dma_addr);
- EFX_POPULATE_QWORD_4(buf_desc,
- IP_DAT_BUF_SIZE, IP_DAT_BUF_SIZE_4K,
- BUF_ADR_REGION, 0,
- BUF_ADR_FBUF, (dma_addr >> 12),
- BUF_OWNER_ID_FBUF, 0);
- falcon_write_sram(efx, &buf_desc, index);
- }
-}
-
-/* Unmaps a buffer from Falcon and clears the buffer table entries */
-static void
-falcon_fini_special_buffer(struct efx_nic *efx,
- struct efx_special_buffer *buffer)
-{
- efx_oword_t buf_tbl_upd;
- unsigned int start = buffer->index;
- unsigned int end = (buffer->index + buffer->entries - 1);
-
- if (!buffer->entries)
- return;
-
- EFX_LOG(efx, "unmapping special buffers %d-%d\n",
- buffer->index, buffer->index + buffer->entries - 1);
-
- EFX_POPULATE_OWORD_4(buf_tbl_upd,
- BUF_UPD_CMD, 0,
- BUF_CLR_CMD, 1,
- BUF_CLR_END_ID, end,
- BUF_CLR_START_ID, start);
- falcon_write(efx, &buf_tbl_upd, BUF_TBL_UPD_REG_KER);
-}
-
-/*
- * Allocate a new Falcon special buffer
- *
- * This allocates memory for a new buffer, clears it and allocates a
- * new buffer ID range. It does not write into Falcon's buffer table.
- *
- * This call will allocate 4KB buffers, since Falcon can't use 8KB
- * buffers for event queues and descriptor rings.
- */
-static int falcon_alloc_special_buffer(struct efx_nic *efx,
- struct efx_special_buffer *buffer,
- unsigned int len)
-{
- struct falcon_nic_data *nic_data = efx->nic_data;
-
- len = ALIGN(len, FALCON_BUF_SIZE);
-
- buffer->addr = pci_alloc_consistent(efx->pci_dev, len,
- &buffer->dma_addr);
- if (!buffer->addr)
- return -ENOMEM;
- buffer->len = len;
- buffer->entries = len / FALCON_BUF_SIZE;
- BUG_ON(buffer->dma_addr & (FALCON_BUF_SIZE - 1));
-
- /* All zeros is a potentially valid event so memset to 0xff */
- memset(buffer->addr, 0xff, len);
-
- /* Select new buffer ID */
- buffer->index = nic_data->next_buffer_table;
- nic_data->next_buffer_table += buffer->entries;
-
- EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x "
- "(virt %p phys %llx)\n", buffer->index,
- buffer->index + buffer->entries - 1,
- (u64)buffer->dma_addr, len,
- buffer->addr, (u64)virt_to_phys(buffer->addr));
-
- return 0;
-}
-
-static void falcon_free_special_buffer(struct efx_nic *efx,
- struct efx_special_buffer *buffer)
-{
- if (!buffer->addr)
- return;
-
- EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x "
- "(virt %p phys %llx)\n", buffer->index,
- buffer->index + buffer->entries - 1,
- (u64)buffer->dma_addr, buffer->len,
- buffer->addr, (u64)virt_to_phys(buffer->addr));
-
- pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr,
- buffer->dma_addr);
- buffer->addr = NULL;
- buffer->entries = 0;
-}
-
-/**************************************************************************
- *
- * Falcon generic buffer handling
- * These buffers are used for interrupt status and MAC stats
- *
- **************************************************************************/
-
-static int falcon_alloc_buffer(struct efx_nic *efx,
- struct efx_buffer *buffer, unsigned int len)
-{
- buffer->addr = pci_alloc_consistent(efx->pci_dev, len,
- &buffer->dma_addr);
- if (!buffer->addr)
- return -ENOMEM;
- buffer->len = len;
- memset(buffer->addr, 0, len);
- return 0;
-}
-
-static void falcon_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer)
-{
- if (buffer->addr) {
- pci_free_consistent(efx->pci_dev, buffer->len,
- buffer->addr, buffer->dma_addr);
- buffer->addr = NULL;
- }
-}
-
-/**************************************************************************
- *
- * Falcon TX path
- *
- **************************************************************************/
-
-/* Returns a pointer to the specified transmit descriptor in the TX
- * descriptor queue belonging to the specified channel.
- */
-static inline efx_qword_t *falcon_tx_desc(struct efx_tx_queue *tx_queue,
- unsigned int index)
-{
- return (((efx_qword_t *) (tx_queue->txd.addr)) + index);
-}
-
-/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */
-static inline void falcon_notify_tx_desc(struct efx_tx_queue *tx_queue)
-{
- unsigned write_ptr;
- efx_dword_t reg;
-
- write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK;
- EFX_POPULATE_DWORD_1(reg, TX_DESC_WPTR_DWORD, write_ptr);
- falcon_writel_page(tx_queue->efx, &reg,
- TX_DESC_UPD_REG_KER_DWORD, tx_queue->queue);
-}
-
-
-/* For each entry inserted into the software descriptor ring, create a
- * descriptor in the hardware TX descriptor ring (in host memory), and
- * write a doorbell.
- */
-void falcon_push_buffers(struct efx_tx_queue *tx_queue)
-{
-
- struct efx_tx_buffer *buffer;
- efx_qword_t *txd;
- unsigned write_ptr;
-
- BUG_ON(tx_queue->write_count == tx_queue->insert_count);
-
- do {
- write_ptr = tx_queue->write_count & FALCON_TXD_RING_MASK;
- buffer = &tx_queue->buffer[write_ptr];
- txd = falcon_tx_desc(tx_queue, write_ptr);
- ++tx_queue->write_count;
-
- /* Create TX descriptor ring entry */
- EFX_POPULATE_QWORD_5(*txd,
- TX_KER_PORT, 0,
- TX_KER_CONT, buffer->continuation,
- TX_KER_BYTE_CNT, buffer->len,
- TX_KER_BUF_REGION, 0,
- TX_KER_BUF_ADR, buffer->dma_addr);
- } while (tx_queue->write_count != tx_queue->insert_count);
-
- wmb(); /* Ensure descriptors are written before they are fetched */
- falcon_notify_tx_desc(tx_queue);
-}
-
-/* Allocate hardware resources for a TX queue */
-int falcon_probe_tx(struct efx_tx_queue *tx_queue)
-{
- struct efx_nic *efx = tx_queue->efx;
- return falcon_alloc_special_buffer(efx, &tx_queue->txd,
- FALCON_TXD_RING_SIZE *
- sizeof(efx_qword_t));
-}
-
-void falcon_init_tx(struct efx_tx_queue *tx_queue)
-{
- efx_oword_t tx_desc_ptr;
- struct efx_nic *efx = tx_queue->efx;
-
- tx_queue->flushed = false;
-
- /* Pin TX descriptor ring */
- falcon_init_special_buffer(efx, &tx_queue->txd);
-
- /* Push TX descriptor ring to card */
- EFX_POPULATE_OWORD_10(tx_desc_ptr,
- TX_DESCQ_EN, 1,
- TX_ISCSI_DDIG_EN, 0,
- TX_ISCSI_HDIG_EN, 0,
- TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index,
- TX_DESCQ_EVQ_ID, tx_queue->channel->channel,
- TX_DESCQ_OWNER_ID, 0,
- TX_DESCQ_LABEL, tx_queue->queue,
- TX_DESCQ_SIZE, FALCON_TXD_RING_ORDER,
- TX_DESCQ_TYPE, 0,
- TX_NON_IP_DROP_DIS_B0, 1);
-
- if (falcon_rev(efx) >= FALCON_REV_B0) {
- int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM;
- EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_IP_CHKSM_DIS_B0, !csum);
- EFX_SET_OWORD_FIELD(tx_desc_ptr, TX_TCP_CHKSM_DIS_B0, !csum);
- }
-
- falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base,
- tx_queue->queue);
-
- if (falcon_rev(efx) < FALCON_REV_B0) {
- efx_oword_t reg;
-
- /* Only 128 bits in this register */
- BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128);
-
- falcon_read(efx, &reg, TX_CHKSM_CFG_REG_KER_A1);
- if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM)
- clear_bit_le(tx_queue->queue, (void *)&reg);
- else
- set_bit_le(tx_queue->queue, (void *)&reg);
- falcon_write(efx, &reg, TX_CHKSM_CFG_REG_KER_A1);
- }
-}
-
-static void falcon_flush_tx_queue(struct efx_tx_queue *tx_queue)
-{
- struct efx_nic *efx = tx_queue->efx;
- efx_oword_t tx_flush_descq;
-
- /* Post a flush command */
- EFX_POPULATE_OWORD_2(tx_flush_descq,
- TX_FLUSH_DESCQ_CMD, 1,
- TX_FLUSH_DESCQ, tx_queue->queue);
- falcon_write(efx, &tx_flush_descq, TX_FLUSH_DESCQ_REG_KER);
-}
-
-void falcon_fini_tx(struct efx_tx_queue *tx_queue)
-{
- struct efx_nic *efx = tx_queue->efx;
- efx_oword_t tx_desc_ptr;
-
- /* The queue should have been flushed */
- WARN_ON(!tx_queue->flushed);
-
- /* Remove TX descriptor ring from card */
- EFX_ZERO_OWORD(tx_desc_ptr);
- falcon_write_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base,
- tx_queue->queue);
-
- /* Unpin TX descriptor ring */
- falcon_fini_special_buffer(efx, &tx_queue->txd);
-}
-
-/* Free buffers backing TX queue */
-void falcon_remove_tx(struct efx_tx_queue *tx_queue)
-{
- falcon_free_special_buffer(tx_queue->efx, &tx_queue->txd);
-}
-
-/**************************************************************************
- *
- * Falcon RX path
- *
- **************************************************************************/
-
-/* Returns a pointer to the specified descriptor in the RX descriptor queue */
-static inline efx_qword_t *falcon_rx_desc(struct efx_rx_queue *rx_queue,
- unsigned int index)
-{
- return (((efx_qword_t *) (rx_queue->rxd.addr)) + index);
-}
-
-/* This creates an entry in the RX descriptor queue */
-static inline void falcon_build_rx_desc(struct efx_rx_queue *rx_queue,
- unsigned index)
-{
- struct efx_rx_buffer *rx_buf;
- efx_qword_t *rxd;
-
- rxd = falcon_rx_desc(rx_queue, index);
- rx_buf = efx_rx_buffer(rx_queue, index);
- EFX_POPULATE_QWORD_3(*rxd,
- RX_KER_BUF_SIZE,
- rx_buf->len -
- rx_queue->efx->type->rx_buffer_padding,
- RX_KER_BUF_REGION, 0,
- RX_KER_BUF_ADR, rx_buf->dma_addr);
-}
-
-/* This writes to the RX_DESC_WPTR register for the specified receive
- * descriptor ring.
- */
-void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue)
-{
- efx_dword_t reg;
- unsigned write_ptr;
-
- while (rx_queue->notified_count != rx_queue->added_count) {
- falcon_build_rx_desc(rx_queue,
- rx_queue->notified_count &
- FALCON_RXD_RING_MASK);
- ++rx_queue->notified_count;
- }
-
- wmb();
- write_ptr = rx_queue->added_count & FALCON_RXD_RING_MASK;
- EFX_POPULATE_DWORD_1(reg, RX_DESC_WPTR_DWORD, write_ptr);
- falcon_writel_page(rx_queue->efx, &reg,
- RX_DESC_UPD_REG_KER_DWORD, rx_queue->queue);
-}
-
-int falcon_probe_rx(struct efx_rx_queue *rx_queue)
-{
- struct efx_nic *efx = rx_queue->efx;
- return falcon_alloc_special_buffer(efx, &rx_queue->rxd,
- FALCON_RXD_RING_SIZE *
- sizeof(efx_qword_t));
-}
-
-void falcon_init_rx(struct efx_rx_queue *rx_queue)
-{
- efx_oword_t rx_desc_ptr;
- struct efx_nic *efx = rx_queue->efx;
- bool is_b0 = falcon_rev(efx) >= FALCON_REV_B0;
- bool iscsi_digest_en = is_b0;
-
- EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n",
- rx_queue->queue, rx_queue->rxd.index,
- rx_queue->rxd.index + rx_queue->rxd.entries - 1);
-
- rx_queue->flushed = false;
-
- /* Pin RX descriptor ring */
- falcon_init_special_buffer(efx, &rx_queue->rxd);
-
- /* Push RX descriptor ring to card */
- EFX_POPULATE_OWORD_10(rx_desc_ptr,
- RX_ISCSI_DDIG_EN, iscsi_digest_en,
- RX_ISCSI_HDIG_EN, iscsi_digest_en,
- RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index,
- RX_DESCQ_EVQ_ID, rx_queue->channel->channel,
- RX_DESCQ_OWNER_ID, 0,
- RX_DESCQ_LABEL, rx_queue->queue,
- RX_DESCQ_SIZE, FALCON_RXD_RING_ORDER,
- RX_DESCQ_TYPE, 0 /* kernel queue */ ,
- /* For >=B0 this is scatter so disable */
- RX_DESCQ_JUMBO, !is_b0,
- RX_DESCQ_EN, 1);
- falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base,
- rx_queue->queue);
-}
-
-static void falcon_flush_rx_queue(struct efx_rx_queue *rx_queue)
-{
- struct efx_nic *efx = rx_queue->efx;
- efx_oword_t rx_flush_descq;
-
- /* Post a flush command */
- EFX_POPULATE_OWORD_2(rx_flush_descq,
- RX_FLUSH_DESCQ_CMD, 1,
- RX_FLUSH_DESCQ, rx_queue->queue);
- falcon_write(efx, &rx_flush_descq, RX_FLUSH_DESCQ_REG_KER);
-}
-
-void falcon_fini_rx(struct efx_rx_queue *rx_queue)
-{
- efx_oword_t rx_desc_ptr;
- struct efx_nic *efx = rx_queue->efx;
-
- /* The queue should already have been flushed */
- WARN_ON(!rx_queue->flushed);
-
- /* Remove RX descriptor ring from card */
- EFX_ZERO_OWORD(rx_desc_ptr);
- falcon_write_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base,
- rx_queue->queue);
-
- /* Unpin RX descriptor ring */
- falcon_fini_special_buffer(efx, &rx_queue->rxd);
-}
-
-/* Free buffers backing RX queue */
-void falcon_remove_rx(struct efx_rx_queue *rx_queue)
-{
- falcon_free_special_buffer(rx_queue->efx, &rx_queue->rxd);
-}
-
-/**************************************************************************
- *
- * Falcon event queue processing
- * Event queues are processed by per-channel tasklets.
- *
- **************************************************************************/
-
-/* Update a channel's event queue's read pointer (RPTR) register
- *
- * This writes the EVQ_RPTR_REG register for the specified channel's
- * event queue.
- *
- * Note that EVQ_RPTR_REG contains the index of the "last read" event,
- * whereas channel->eventq_read_ptr contains the index of the "next to
- * read" event.
- */
-void falcon_eventq_read_ack(struct efx_channel *channel)
-{
- efx_dword_t reg;
- struct efx_nic *efx = channel->efx;
-
- EFX_POPULATE_DWORD_1(reg, EVQ_RPTR_DWORD, channel->eventq_read_ptr);
- falcon_writel_table(efx, &reg, efx->type->evq_rptr_tbl_base,
- channel->channel);
-}
-
-/* Use HW to insert a SW defined event */
-void falcon_generate_event(struct efx_channel *channel, efx_qword_t *event)
-{
- efx_oword_t drv_ev_reg;
-
- EFX_POPULATE_OWORD_2(drv_ev_reg,
- DRV_EV_QID, channel->channel,
- DRV_EV_DATA,
- EFX_QWORD_FIELD64(*event, WHOLE_EVENT));
- falcon_write(channel->efx, &drv_ev_reg, DRV_EV_REG_KER);
-}
-
-/* Handle a transmit completion event
- *
- * Falcon batches TX completion events; the message we receive is of
- * the form "complete all TX events up to this index".
- */
-static void falcon_handle_tx_event(struct efx_channel *channel,
- efx_qword_t *event)
-{
- unsigned int tx_ev_desc_ptr;
- unsigned int tx_ev_q_label;
- struct efx_tx_queue *tx_queue;
- struct efx_nic *efx = channel->efx;
-
- if (likely(EFX_QWORD_FIELD(*event, TX_EV_COMP))) {
- /* Transmit completion */
- tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR);
- tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL);
- tx_queue = &efx->tx_queue[tx_ev_q_label];
- channel->irq_mod_score +=
- (tx_ev_desc_ptr - tx_queue->read_count) &
- efx->type->txd_ring_mask;
- efx_xmit_done(tx_queue, tx_ev_desc_ptr);
- } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) {
- /* Rewrite the FIFO write pointer */
- tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL);
- tx_queue = &efx->tx_queue[tx_ev_q_label];
-
- if (efx_dev_registered(efx))
- netif_tx_lock(efx->net_dev);
- falcon_notify_tx_desc(tx_queue);
- if (efx_dev_registered(efx))
- netif_tx_unlock(efx->net_dev);
- } else if (EFX_QWORD_FIELD(*event, TX_EV_PKT_ERR) &&
- EFX_WORKAROUND_10727(efx)) {
- efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH);
- } else {
- EFX_ERR(efx, "channel %d unexpected TX event "
- EFX_QWORD_FMT"\n", channel->channel,
- EFX_QWORD_VAL(*event));
- }
-}
-
-/* Detect errors included in the rx_evt_pkt_ok bit. */
-static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue,
- const efx_qword_t *event,
- bool *rx_ev_pkt_ok,
- bool *discard)
-{
- struct efx_nic *efx = rx_queue->efx;
- bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err;
- bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err;
- bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc;
- bool rx_ev_other_err, rx_ev_pause_frm;
- bool rx_ev_ip_frag_err, rx_ev_hdr_type, rx_ev_mcast_pkt;
- unsigned rx_ev_pkt_type;
-
- rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE);
- rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT);
- rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, RX_EV_TOBE_DISC);
- rx_ev_pkt_type = EFX_QWORD_FIELD(*event, RX_EV_PKT_TYPE);
- rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event,
- RX_EV_BUF_OWNER_ID_ERR);
- rx_ev_ip_frag_err = EFX_QWORD_FIELD(*event, RX_EV_IF_FRAG_ERR);
- rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event,
- RX_EV_IP_HDR_CHKSUM_ERR);
- rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event,
- RX_EV_TCP_UDP_CHKSUM_ERR);
- rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, RX_EV_ETH_CRC_ERR);
- rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, RX_EV_FRM_TRUNC);
- rx_ev_drib_nib = ((falcon_rev(efx) >= FALCON_REV_B0) ?
- 0 : EFX_QWORD_FIELD(*event, RX_EV_DRIB_NIB));
- rx_ev_pause_frm = EFX_QWORD_FIELD(*event, RX_EV_PAUSE_FRM_ERR);
-
- /* Every error apart from tobe_disc and pause_frm */
- rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err |
- rx_ev_buf_owner_id_err | rx_ev_eth_crc_err |
- rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err);
-
- /* Count errors that are not in MAC stats. Ignore expected
- * checksum errors during self-test. */
- if (rx_ev_frm_trunc)
- ++rx_queue->channel->n_rx_frm_trunc;
- else if (rx_ev_tobe_disc)
- ++rx_queue->channel->n_rx_tobe_disc;
- else if (!efx->loopback_selftest) {
- if (rx_ev_ip_hdr_chksum_err)
- ++rx_queue->channel->n_rx_ip_hdr_chksum_err;
- else if (rx_ev_tcp_udp_chksum_err)
- ++rx_queue->channel->n_rx_tcp_udp_chksum_err;
- }
- if (rx_ev_ip_frag_err)
- ++rx_queue->channel->n_rx_ip_frag_err;
-
- /* The frame must be discarded if any of these are true. */
- *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib |
- rx_ev_tobe_disc | rx_ev_pause_frm);
-
- /* TOBE_DISC is expected on unicast mismatches; don't print out an
- * error message. FRM_TRUNC indicates RXDP dropped the packet due
- * to a FIFO overflow.
- */
-#ifdef EFX_ENABLE_DEBUG
- if (rx_ev_other_err) {
- EFX_INFO_RL(efx, " RX queue %d unexpected RX event "
- EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n",
- rx_queue->queue, EFX_QWORD_VAL(*event),
- rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "",
- rx_ev_ip_hdr_chksum_err ?
- " [IP_HDR_CHKSUM_ERR]" : "",
- rx_ev_tcp_udp_chksum_err ?
- " [TCP_UDP_CHKSUM_ERR]" : "",
- rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "",
- rx_ev_frm_trunc ? " [FRM_TRUNC]" : "",
- rx_ev_drib_nib ? " [DRIB_NIB]" : "",
- rx_ev_tobe_disc ? " [TOBE_DISC]" : "",
- rx_ev_pause_frm ? " [PAUSE]" : "");
- }
-#endif
-}
-
-/* Handle receive events that are not in-order. */
-static void falcon_handle_rx_bad_index(struct efx_rx_queue *rx_queue,
- unsigned index)
-{
- struct efx_nic *efx = rx_queue->efx;
- unsigned expected, dropped;
-
- expected = rx_queue->removed_count & FALCON_RXD_RING_MASK;
- dropped = ((index + FALCON_RXD_RING_SIZE - expected) &
- FALCON_RXD_RING_MASK);
- EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n",
- dropped, index, expected);
-
- efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ?
- RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE);
-}
-
-/* Handle a packet received event
- *
- * Falcon silicon gives a "discard" flag if it's a unicast packet with the
- * wrong destination address
- * Also "is multicast" and "matches multicast filter" flags can be used to
- * discard non-matching multicast packets.
- */
-static void falcon_handle_rx_event(struct efx_channel *channel,
- const efx_qword_t *event)
-{
- unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt;
- unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt;
- unsigned expected_ptr;
- bool rx_ev_pkt_ok, discard = false, checksummed;
- struct efx_rx_queue *rx_queue;
- struct efx_nic *efx = channel->efx;
-
- /* Basic packet information */
- rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, RX_EV_BYTE_CNT);
- rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, RX_EV_PKT_OK);
- rx_ev_hdr_type = EFX_QWORD_FIELD(*event, RX_EV_HDR_TYPE);
- WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_JUMBO_CONT));
- WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_SOP) != 1);
- WARN_ON(EFX_QWORD_FIELD(*event, RX_EV_Q_LABEL) != channel->channel);
-
- rx_queue = &efx->rx_queue[channel->channel];
-
- rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, RX_EV_DESC_PTR);
- expected_ptr = rx_queue->removed_count & FALCON_RXD_RING_MASK;
- if (unlikely(rx_ev_desc_ptr != expected_ptr))
- falcon_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr);
-
- if (likely(rx_ev_pkt_ok)) {
- /* If packet is marked as OK and packet type is TCP/IPv4 or
- * UDP/IPv4, then we can rely on the hardware checksum.
- */
- checksummed = RX_EV_HDR_TYPE_HAS_CHECKSUMS(rx_ev_hdr_type);
- } else {
- falcon_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok,
- &discard);
- checksummed = false;
- }
-
- /* Detect multicast packets that didn't match the filter */
- rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, RX_EV_MCAST_PKT);
- if (rx_ev_mcast_pkt) {
- unsigned int rx_ev_mcast_hash_match =
- EFX_QWORD_FIELD(*event, RX_EV_MCAST_HASH_MATCH);
-
- if (unlikely(!rx_ev_mcast_hash_match))
- discard = true;
- }
-
- channel->irq_mod_score += 2;
-
- /* Handle received packet */
- efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt,
- checksummed, discard);
-}
-
-/* Global events are basically PHY events */
-static void falcon_handle_global_event(struct efx_channel *channel,
- efx_qword_t *event)
-{
- struct efx_nic *efx = channel->efx;
- bool handled = false;
-
- if (EFX_QWORD_FIELD(*event, G_PHY0_INTR) ||
- EFX_QWORD_FIELD(*event, G_PHY1_INTR) ||
- EFX_QWORD_FIELD(*event, XG_PHY_INTR) ||
- EFX_QWORD_FIELD(*event, XFP_PHY_INTR)) {
- efx->phy_op->clear_interrupt(efx);
- queue_work(efx->workqueue, &efx->phy_work);
- handled = true;
- }
-
- if ((falcon_rev(efx) >= FALCON_REV_B0) &&
- EFX_QWORD_FIELD(*event, XG_MNT_INTR_B0)) {
- queue_work(efx->workqueue, &efx->mac_work);
- handled = true;
- }
-
- if (EFX_QWORD_FIELD_VER(efx, *event, RX_RECOVERY)) {
- EFX_ERR(efx, "channel %d seen global RX_RESET "
- "event. Resetting.\n", channel->channel);
-
- atomic_inc(&efx->rx_reset);
- efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ?
- RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE);
- handled = true;
- }
-
- if (!handled)
- EFX_ERR(efx, "channel %d unknown global event "
- EFX_QWORD_FMT "\n", channel->channel,
- EFX_QWORD_VAL(*event));
-}
-
-static void falcon_handle_driver_event(struct efx_channel *channel,
- efx_qword_t *event)
-{
- struct efx_nic *efx = channel->efx;
- unsigned int ev_sub_code;
- unsigned int ev_sub_data;
-
- ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE);
- ev_sub_data = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_DATA);
-
- switch (ev_sub_code) {
- case TX_DESCQ_FLS_DONE_EV_DECODE:
- EFX_TRACE(efx, "channel %d TXQ %d flushed\n",
- channel->channel, ev_sub_data);
- break;
- case RX_DESCQ_FLS_DONE_EV_DECODE:
- EFX_TRACE(efx, "channel %d RXQ %d flushed\n",
- channel->channel, ev_sub_data);
- break;
- case EVQ_INIT_DONE_EV_DECODE:
- EFX_LOG(efx, "channel %d EVQ %d initialised\n",
- channel->channel, ev_sub_data);
- break;
- case SRM_UPD_DONE_EV_DECODE:
- EFX_TRACE(efx, "channel %d SRAM update done\n",
- channel->channel);
- break;
- case WAKE_UP_EV_DECODE:
- EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n",
- channel->channel, ev_sub_data);
- break;
- case TIMER_EV_DECODE:
- EFX_TRACE(efx, "channel %d RX queue %d timer expired\n",
- channel->channel, ev_sub_data);
- break;
- case RX_RECOVERY_EV_DECODE:
- EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. "
- "Resetting.\n", channel->channel);
- atomic_inc(&efx->rx_reset);
- efx_schedule_reset(efx,
- EFX_WORKAROUND_6555(efx) ?
- RESET_TYPE_RX_RECOVERY :
- RESET_TYPE_DISABLE);
- break;
- case RX_DSC_ERROR_EV_DECODE:
- EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error."
- " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data);
- efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH);
- break;
- case TX_DSC_ERROR_EV_DECODE:
- EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error."
- " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data);
- efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH);
- break;
- default:
- EFX_TRACE(efx, "channel %d unknown driver event code %d "
- "data %04x\n", channel->channel, ev_sub_code,
- ev_sub_data);
- break;
- }
-}
-
-int falcon_process_eventq(struct efx_channel *channel, int rx_quota)
-{
- unsigned int read_ptr;
- efx_qword_t event, *p_event;
- int ev_code;
- int rx_packets = 0;
-
- read_ptr = channel->eventq_read_ptr;
-
- do {
- p_event = falcon_event(channel, read_ptr);
- event = *p_event;
-
- if (!falcon_event_present(&event))
- /* End of events */
- break;
-
- EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n",
- channel->channel, EFX_QWORD_VAL(event));
-
- /* Clear this event by marking it all ones */
- EFX_SET_QWORD(*p_event);
-
- ev_code = EFX_QWORD_FIELD(event, EV_CODE);
-
- switch (ev_code) {
- case RX_IP_EV_DECODE:
- falcon_handle_rx_event(channel, &event);
- ++rx_packets;
- break;
- case TX_IP_EV_DECODE:
- falcon_handle_tx_event(channel, &event);
- break;
- case DRV_GEN_EV_DECODE:
- channel->eventq_magic
- = EFX_QWORD_FIELD(event, EVQ_MAGIC);
- EFX_LOG(channel->efx, "channel %d received generated "
- "event "EFX_QWORD_FMT"\n", channel->channel,
- EFX_QWORD_VAL(event));
- break;
- case GLOBAL_EV_DECODE:
- falcon_handle_global_event(channel, &event);
- break;
- case DRIVER_EV_DECODE:
- falcon_handle_driver_event(channel, &event);
- break;
- default:
- EFX_ERR(channel->efx, "channel %d unknown event type %d"
- " (data " EFX_QWORD_FMT ")\n", channel->channel,
- ev_code, EFX_QWORD_VAL(event));
- }
-
- /* Increment read pointer */
- read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK;
-
- } while (rx_packets < rx_quota);
-
- channel->eventq_read_ptr = read_ptr;
- return rx_packets;
-}
-
-void falcon_set_int_moderation(struct efx_channel *channel)
+static void falcon_push_irq_moderation(struct efx_channel *channel)
{
efx_dword_t timer_cmd;
struct efx_nic *efx = channel->efx;
/* Set timer register */
if (channel->irq_moderation) {
- /* Round to resolution supported by hardware. The value we
- * program is based at 0. So actual interrupt moderation
- * achieved is ((x + 1) * res).
- */
- channel->irq_moderation -= (channel->irq_moderation %
- FALCON_IRQ_MOD_RESOLUTION);
- if (channel->irq_moderation < FALCON_IRQ_MOD_RESOLUTION)
- channel->irq_moderation = FALCON_IRQ_MOD_RESOLUTION;
EFX_POPULATE_DWORD_2(timer_cmd,
- TIMER_MODE, TIMER_MODE_INT_HLDOFF,
- TIMER_VAL,
- channel->irq_moderation /
- FALCON_IRQ_MOD_RESOLUTION - 1);
+ FRF_AB_TC_TIMER_MODE,
+ FFE_BB_TIMER_MODE_INT_HLDOFF,
+ FRF_AB_TC_TIMER_VAL,
+ channel->irq_moderation - 1);
} else {
EFX_POPULATE_DWORD_2(timer_cmd,
- TIMER_MODE, TIMER_MODE_DIS,
- TIMER_VAL, 0);
+ FRF_AB_TC_TIMER_MODE,
+ FFE_BB_TIMER_MODE_DIS,
+ FRF_AB_TC_TIMER_VAL, 0);
}
- falcon_writel_page_locked(efx, &timer_cmd, TIMER_CMD_REG_KER,
- channel->channel);
-
+ BUILD_BUG_ON(FR_AA_TIMER_COMMAND_KER != FR_BZ_TIMER_COMMAND_P0);
+ efx_writed_page_locked(efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0,
+ channel->channel);
}
-/* Allocate buffer table entries for event queue */
-int falcon_probe_eventq(struct efx_channel *channel)
-{
- struct efx_nic *efx = channel->efx;
- unsigned int evq_size;
-
- evq_size = FALCON_EVQ_SIZE * sizeof(efx_qword_t);
- return falcon_alloc_special_buffer(efx, &channel->eventq, evq_size);
-}
+static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx);
-void falcon_init_eventq(struct efx_channel *channel)
+static void falcon_prepare_flush(struct efx_nic *efx)
{
- efx_oword_t evq_ptr;
- struct efx_nic *efx = channel->efx;
-
- EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n",
- channel->channel, channel->eventq.index,
- channel->eventq.index + channel->eventq.entries - 1);
-
- /* Pin event queue buffer */
- falcon_init_special_buffer(efx, &channel->eventq);
+ falcon_deconfigure_mac_wrapper(efx);
- /* Fill event queue with all ones (i.e. empty events) */
- memset(channel->eventq.addr, 0xff, channel->eventq.len);
-
- /* Push event queue to card */
- EFX_POPULATE_OWORD_3(evq_ptr,
- EVQ_EN, 1,
- EVQ_SIZE, FALCON_EVQ_ORDER,
- EVQ_BUF_BASE_ID, channel->eventq.index);
- falcon_write_table(efx, &evq_ptr, efx->type->evq_ptr_tbl_base,
- channel->channel);
-
- falcon_set_int_moderation(channel);
-}
-
-void falcon_fini_eventq(struct efx_channel *channel)
-{
- efx_oword_t eventq_ptr;
- struct efx_nic *efx = channel->efx;
-
- /* Remove event queue from card */
- EFX_ZERO_OWORD(eventq_ptr);
- falcon_write_table(efx, &eventq_ptr, efx->type->evq_ptr_tbl_base,
- channel->channel);
-
- /* Unpin event queue */
- falcon_fini_special_buffer(efx, &channel->eventq);
-}
-
-/* Free buffers backing event queue */
-void falcon_remove_eventq(struct efx_channel *channel)
-{
- falcon_free_special_buffer(channel->efx, &channel->eventq);
-}
-
-
-/* Generates a test event on the event queue. A subsequent call to
- * process_eventq() should pick up the event and place the value of
- * "magic" into channel->eventq_magic;
- */
-void falcon_generate_test_event(struct efx_channel *channel, unsigned int magic)
-{
- efx_qword_t test_event;
-
- EFX_POPULATE_QWORD_2(test_event,
- EV_CODE, DRV_GEN_EV_DECODE,
- EVQ_MAGIC, magic);
- falcon_generate_event(channel, &test_event);
-}
-
-void falcon_sim_phy_event(struct efx_nic *efx)
-{
- efx_qword_t phy_event;
-
- EFX_POPULATE_QWORD_1(phy_event, EV_CODE, GLOBAL_EV_DECODE);
- if (EFX_IS10G(efx))
- EFX_SET_QWORD_FIELD(phy_event, XG_PHY_INTR, 1);
- else
- EFX_SET_QWORD_FIELD(phy_event, G_PHY0_INTR, 1);
-
- falcon_generate_event(&efx->channel[0], &phy_event);
-}
-
-/**************************************************************************
- *
- * Flush handling
- *
- **************************************************************************/
-
-
-static void falcon_poll_flush_events(struct efx_nic *efx)
-{
- struct efx_channel *channel = &efx->channel[0];
- struct efx_tx_queue *tx_queue;
- struct efx_rx_queue *rx_queue;
- unsigned int read_ptr = channel->eventq_read_ptr;
- unsigned int end_ptr = (read_ptr - 1) & FALCON_EVQ_MASK;
-
- do {
- efx_qword_t *event = falcon_event(channel, read_ptr);
- int ev_code, ev_sub_code, ev_queue;
- bool ev_failed;
-
- if (!falcon_event_present(event))
- break;
-
- ev_code = EFX_QWORD_FIELD(*event, EV_CODE);
- ev_sub_code = EFX_QWORD_FIELD(*event, DRIVER_EV_SUB_CODE);
- if (ev_code == DRIVER_EV_DECODE &&
- ev_sub_code == TX_DESCQ_FLS_DONE_EV_DECODE) {
- ev_queue = EFX_QWORD_FIELD(*event,
- DRIVER_EV_TX_DESCQ_ID);
- if (ev_queue < EFX_TX_QUEUE_COUNT) {
- tx_queue = efx->tx_queue + ev_queue;
- tx_queue->flushed = true;
- }
- } else if (ev_code == DRIVER_EV_DECODE &&
- ev_sub_code == RX_DESCQ_FLS_DONE_EV_DECODE) {
- ev_queue = EFX_QWORD_FIELD(*event,
- DRIVER_EV_RX_DESCQ_ID);
- ev_failed = EFX_QWORD_FIELD(*event,
- DRIVER_EV_RX_FLUSH_FAIL);
- if (ev_queue < efx->n_rx_queues) {
- rx_queue = efx->rx_queue + ev_queue;
-
- /* retry the rx flush */
- if (ev_failed)
- falcon_flush_rx_queue(rx_queue);
- else
- rx_queue->flushed = true;
- }
- }
-
- read_ptr = (read_ptr + 1) & FALCON_EVQ_MASK;
- } while (read_ptr != end_ptr);
-}
-
-/* Handle tx and rx flushes at the same time, since they run in
- * parallel in the hardware and there's no reason for us to
- * serialise them */
-int falcon_flush_queues(struct efx_nic *efx)
-{
- struct efx_rx_queue *rx_queue;
- struct efx_tx_queue *tx_queue;
- int i;
- bool outstanding;
-
- /* Issue flush requests */
- efx_for_each_tx_queue(tx_queue, efx) {
- tx_queue->flushed = false;
- falcon_flush_tx_queue(tx_queue);
- }
- efx_for_each_rx_queue(rx_queue, efx) {
- rx_queue->flushed = false;
- falcon_flush_rx_queue(rx_queue);
- }
-
- /* Poll the evq looking for flush completions. Since we're not pushing
- * any more rx or tx descriptors at this point, we're in no danger of
- * overflowing the evq whilst we wait */
- for (i = 0; i < FALCON_FLUSH_POLL_COUNT; ++i) {
- msleep(FALCON_FLUSH_INTERVAL);
- falcon_poll_flush_events(efx);
-
- /* Check if every queue has been succesfully flushed */
- outstanding = false;
- efx_for_each_tx_queue(tx_queue, efx)
- outstanding |= !tx_queue->flushed;
- efx_for_each_rx_queue(rx_queue, efx)
- outstanding |= !rx_queue->flushed;
- if (!outstanding)
- return 0;
- }
-
- /* Mark the queues as all flushed. We're going to return failure
- * leading to a reset, or fake up success anyway. "flushed" now
- * indicates that we tried to flush. */
- efx_for_each_tx_queue(tx_queue, efx) {
- if (!tx_queue->flushed)
- EFX_ERR(efx, "tx queue %d flush command timed out\n",
- tx_queue->queue);
- tx_queue->flushed = true;
- }
- efx_for_each_rx_queue(rx_queue, efx) {
- if (!rx_queue->flushed)
- EFX_ERR(efx, "rx queue %d flush command timed out\n",
- rx_queue->queue);
- rx_queue->flushed = true;
- }
-
- if (EFX_WORKAROUND_7803(efx))
- return 0;
-
- return -ETIMEDOUT;
-}
-
-/**************************************************************************
- *
- * Falcon hardware interrupts
- * The hardware interrupt handler does very little work; all the event
- * queue processing is carried out by per-channel tasklets.
- *
- **************************************************************************/
-
-/* Enable/disable/generate Falcon interrupts */
-static inline void falcon_interrupts(struct efx_nic *efx, int enabled,
- int force)
-{
- efx_oword_t int_en_reg_ker;
-
- EFX_POPULATE_OWORD_2(int_en_reg_ker,
- KER_INT_KER, force,
- DRV_INT_EN_KER, enabled);
- falcon_write(efx, &int_en_reg_ker, INT_EN_REG_KER);
-}
-
-void falcon_enable_interrupts(struct efx_nic *efx)
-{
- efx_oword_t int_adr_reg_ker;
- struct efx_channel *channel;
-
- EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr));
- wmb(); /* Ensure interrupt vector is clear before interrupts enabled */
-
- /* Program address */
- EFX_POPULATE_OWORD_2(int_adr_reg_ker,
- NORM_INT_VEC_DIS_KER, EFX_INT_MODE_USE_MSI(efx),
- INT_ADR_KER, efx->irq_status.dma_addr);
- falcon_write(efx, &int_adr_reg_ker, INT_ADR_REG_KER);
-
- /* Enable interrupts */
- falcon_interrupts(efx, 1, 0);
-
- /* Force processing of all the channels to get the EVQ RPTRs up to
- date */
- efx_for_each_channel(channel, efx)
- efx_schedule_channel(channel);
-}
-
-void falcon_disable_interrupts(struct efx_nic *efx)
-{
- /* Disable interrupts */
- falcon_interrupts(efx, 0, 0);
-}
-
-/* Generate a Falcon test interrupt
- * Interrupt must already have been enabled, otherwise nasty things
- * may happen.
- */
-void falcon_generate_interrupt(struct efx_nic *efx)
-{
- falcon_interrupts(efx, 1, 1);
+ /* Wait for the tx and rx fifo's to get to the next packet boundary
+ * (~1ms without back-pressure), then to drain the remainder of the
+ * fifo's at data path speeds (negligible), with a healthy margin. */
+ msleep(10);
}
/* Acknowledge a legacy interrupt from Falcon
@@ -1364,113 +144,17 @@ void falcon_generate_interrupt(struct efx_nic *efx)
*
* NB most hardware supports MSI interrupts
*/
-static inline void falcon_irq_ack_a1(struct efx_nic *efx)
-{
- efx_dword_t reg;
-
- EFX_POPULATE_DWORD_1(reg, INT_ACK_DUMMY_DATA, 0xb7eb7e);
- falcon_writel(efx, &reg, INT_ACK_REG_KER_A1);
- falcon_readl(efx, &reg, WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1);
-}
-
-/* Process a fatal interrupt
- * Disable bus mastering ASAP and schedule a reset
- */
-static irqreturn_t falcon_fatal_interrupt(struct efx_nic *efx)
+inline void falcon_irq_ack_a1(struct efx_nic *efx)
{
- struct falcon_nic_data *nic_data = efx->nic_data;
- efx_oword_t *int_ker = efx->irq_status.addr;
- efx_oword_t fatal_intr;
- int error, mem_perr;
-
- falcon_read(efx, &fatal_intr, FATAL_INTR_REG_KER);
- error = EFX_OWORD_FIELD(fatal_intr, INT_KER_ERROR);
-
- EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status "
- EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker),
- EFX_OWORD_VAL(fatal_intr),
- error ? "disabling bus mastering" : "no recognised error");
- if (error == 0)
- goto out;
-
- /* If this is a memory parity error dump which blocks are offending */
- mem_perr = EFX_OWORD_FIELD(fatal_intr, MEM_PERR_INT_KER);
- if (mem_perr) {
- efx_oword_t reg;
- falcon_read(efx, &reg, MEM_STAT_REG_KER);
- EFX_ERR(efx, "SYSTEM ERROR: memory parity error "
- EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg));
- }
-
- /* Disable both devices */
- pci_clear_master(efx->pci_dev);
- if (FALCON_IS_DUAL_FUNC(efx))
- pci_clear_master(nic_data->pci_dev2);
- falcon_disable_interrupts(efx);
-
- /* Count errors and reset or disable the NIC accordingly */
- if (nic_data->int_error_count == 0 ||
- time_after(jiffies, nic_data->int_error_expire)) {
- nic_data->int_error_count = 0;
- nic_data->int_error_expire =
- jiffies + FALCON_INT_ERROR_EXPIRE * HZ;
- }
- if (++nic_data->int_error_count < FALCON_MAX_INT_ERRORS) {
- EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n");
- efx_schedule_reset(efx, RESET_TYPE_INT_ERROR);
- } else {
- EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen."
- "NIC will be disabled\n");
- efx_schedule_reset(efx, RESET_TYPE_DISABLE);
- }
-out:
- return IRQ_HANDLED;
-}
-
-/* Handle a legacy interrupt from Falcon
- * Acknowledges the interrupt and schedule event queue processing.
- */
-static irqreturn_t falcon_legacy_interrupt_b0(int irq, void *dev_id)
-{
- struct efx_nic *efx = dev_id;
- efx_oword_t *int_ker = efx->irq_status.addr;
- irqreturn_t result = IRQ_NONE;
- struct efx_channel *channel;
efx_dword_t reg;
- u32 queues;
- int syserr;
- /* Read the ISR which also ACKs the interrupts */
- falcon_readl(efx, &reg, INT_ISR0_B0);
- queues = EFX_EXTRACT_DWORD(reg, 0, 31);
-
- /* Check to see if we have a serious error condition */
- syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT);
- if (unlikely(syserr))
- return falcon_fatal_interrupt(efx);
-
- /* Schedule processing of any interrupting queues */
- efx_for_each_channel(channel, efx) {
- if ((queues & 1) ||
- falcon_event_present(
- falcon_event(channel, channel->eventq_read_ptr))) {
- efx_schedule_channel(channel);
- result = IRQ_HANDLED;
- }
- queues >>= 1;
- }
-
- if (result == IRQ_HANDLED) {
- efx->last_irq_cpu = raw_smp_processor_id();
- EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n",
- irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg));
- }
-
- return result;
+ EFX_POPULATE_DWORD_1(reg, FRF_AA_INT_ACK_KER_FIELD, 0xb7eb7e);
+ efx_writed(efx, &reg, FR_AA_INT_ACK_KER);
+ efx_readd(efx, &reg, FR_AA_WORK_AROUND_BROKEN_PCI_READS);
}
-static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id)
+irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id)
{
struct efx_nic *efx = dev_id;
efx_oword_t *int_ker = efx->irq_status.addr;
@@ -1491,15 +175,15 @@ static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id)
irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker));
/* Check to see if we have a serious error condition */
- syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT);
+ syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT);
if (unlikely(syserr))
- return falcon_fatal_interrupt(efx);
+ return efx_nic_fatal_interrupt(efx);
/* Determine interrupting queues, clear interrupt status
* register and acknowledge the device interrupt.
*/
- BUILD_BUG_ON(INT_EVQS_WIDTH > EFX_MAX_CHANNELS);
- queues = EFX_OWORD_FIELD(*int_ker, INT_EVQS);
+ BUILD_BUG_ON(FSF_AZ_NET_IVEC_INT_Q_WIDTH > EFX_MAX_CHANNELS);
+ queues = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_INT_Q);
EFX_ZERO_OWORD(*int_ker);
wmb(); /* Ensure the vector is cleared before interrupt ack */
falcon_irq_ack_a1(efx);
@@ -1515,126 +199,6 @@ static irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id)
return IRQ_HANDLED;
}
-
-/* Handle an MSI interrupt from Falcon
- *
- * Handle an MSI hardware interrupt. This routine schedules event
- * queue processing. No interrupt acknowledgement cycle is necessary.
- * Also, we never need to check that the interrupt is for us, since
- * MSI interrupts cannot be shared.
- */
-static irqreturn_t falcon_msi_interrupt(int irq, void *dev_id)
-{
- struct efx_channel *channel = dev_id;
- struct efx_nic *efx = channel->efx;
- efx_oword_t *int_ker = efx->irq_status.addr;
- int syserr;
-
- efx->last_irq_cpu = raw_smp_processor_id();
- EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n",
- irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker));
-
- /* Check to see if we have a serious error condition */
- syserr = EFX_OWORD_FIELD(*int_ker, FATAL_INT);
- if (unlikely(syserr))
- return falcon_fatal_interrupt(efx);
-
- /* Schedule processing of the channel */
- efx_schedule_channel(channel);
-
- return IRQ_HANDLED;
-}
-
-
-/* Setup RSS indirection table.
- * This maps from the hash value of the packet to RXQ
- */
-static void falcon_setup_rss_indir_table(struct efx_nic *efx)
-{
- int i = 0;
- unsigned long offset;
- efx_dword_t dword;
-
- if (falcon_rev(efx) < FALCON_REV_B0)
- return;
-
- for (offset = RX_RSS_INDIR_TBL_B0;
- offset < RX_RSS_INDIR_TBL_B0 + 0x800;
- offset += 0x10) {
- EFX_POPULATE_DWORD_1(dword, RX_RSS_INDIR_ENT_B0,
- i % efx->n_rx_queues);
- falcon_writel(efx, &dword, offset);
- i++;
- }
-}
-
-/* Hook interrupt handler(s)
- * Try MSI and then legacy interrupts.
- */
-int falcon_init_interrupt(struct efx_nic *efx)
-{
- struct efx_channel *channel;
- int rc;
-
- if (!EFX_INT_MODE_USE_MSI(efx)) {
- irq_handler_t handler;
- if (falcon_rev(efx) >= FALCON_REV_B0)
- handler = falcon_legacy_interrupt_b0;
- else
- handler = falcon_legacy_interrupt_a1;
-
- rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED,
- efx->name, efx);
- if (rc) {
- EFX_ERR(efx, "failed to hook legacy IRQ %d\n",
- efx->pci_dev->irq);
- goto fail1;
- }
- return 0;
- }
-
- /* Hook MSI or MSI-X interrupt */
- efx_for_each_channel(channel, efx) {
- rc = request_irq(channel->irq, falcon_msi_interrupt,
- IRQF_PROBE_SHARED, /* Not shared */
- channel->name, channel);
- if (rc) {
- EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq);
- goto fail2;
- }
- }
-
- return 0;
-
- fail2:
- efx_for_each_channel(channel, efx)
- free_irq(channel->irq, channel);
- fail1:
- return rc;
-}
-
-void falcon_fini_interrupt(struct efx_nic *efx)
-{
- struct efx_channel *channel;
- efx_oword_t reg;
-
- /* Disable MSI/MSI-X interrupts */
- efx_for_each_channel(channel, efx) {
- if (channel->irq)
- free_irq(channel->irq, channel);
- }
-
- /* ACK legacy interrupt */
- if (falcon_rev(efx) >= FALCON_REV_B0)
- falcon_read(efx, &reg, INT_ISR0_B0);
- else
- falcon_irq_ack_a1(efx);
-
- /* Disable legacy interrupt */
- if (efx->legacy_irq)
- free_irq(efx->legacy_irq, efx);
-}
-
/**************************************************************************
*
* EEPROM/flash
@@ -1647,8 +211,8 @@ void falcon_fini_interrupt(struct efx_nic *efx)
static int falcon_spi_poll(struct efx_nic *efx)
{
efx_oword_t reg;
- falcon_read(efx, &reg, EE_SPI_HCMD_REG_KER);
- return EFX_OWORD_FIELD(reg, EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0;
+ efx_reado(efx, &reg, FR_AB_EE_SPI_HCMD);
+ return EFX_OWORD_FIELD(reg, FRF_AB_EE_SPI_HCMD_CMD_EN) ? -EBUSY : 0;
}
/* Wait for SPI command completion */
@@ -1678,11 +242,10 @@ static int falcon_spi_wait(struct efx_nic *efx)
}
}
-int falcon_spi_cmd(const struct efx_spi_device *spi,
+int falcon_spi_cmd(struct efx_nic *efx, const struct efx_spi_device *spi,
unsigned int command, int address,
const void *in, void *out, size_t len)
{
- struct efx_nic *efx = spi->efx;
bool addressed = (address >= 0);
bool reading = (out != NULL);
efx_oword_t reg;
@@ -1700,27 +263,27 @@ int falcon_spi_cmd(const struct efx_spi_device *spi,
/* Program address register, if we have an address */
if (addressed) {
- EFX_POPULATE_OWORD_1(reg, EE_SPI_HADR_ADR, address);
- falcon_write(efx, &reg, EE_SPI_HADR_REG_KER);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_EE_SPI_HADR_ADR, address);
+ efx_writeo(efx, &reg, FR_AB_EE_SPI_HADR);
}
/* Program data register, if we have data */
if (in != NULL) {
memcpy(&reg, in, len);
- falcon_write(efx, &reg, EE_SPI_HDATA_REG_KER);
+ efx_writeo(efx, &reg, FR_AB_EE_SPI_HDATA);
}
/* Issue read/write command */
EFX_POPULATE_OWORD_7(reg,
- EE_SPI_HCMD_CMD_EN, 1,
- EE_SPI_HCMD_SF_SEL, spi->device_id,
- EE_SPI_HCMD_DABCNT, len,
- EE_SPI_HCMD_READ, reading,
- EE_SPI_HCMD_DUBCNT, 0,
- EE_SPI_HCMD_ADBCNT,
+ FRF_AB_EE_SPI_HCMD_CMD_EN, 1,
+ FRF_AB_EE_SPI_HCMD_SF_SEL, spi->device_id,
+ FRF_AB_EE_SPI_HCMD_DABCNT, len,
+ FRF_AB_EE_SPI_HCMD_READ, reading,
+ FRF_AB_EE_SPI_HCMD_DUBCNT, 0,
+ FRF_AB_EE_SPI_HCMD_ADBCNT,
(addressed ? spi->addr_len : 0),
- EE_SPI_HCMD_ENC, command);
- falcon_write(efx, &reg, EE_SPI_HCMD_REG_KER);
+ FRF_AB_EE_SPI_HCMD_ENC, command);
+ efx_writeo(efx, &reg, FR_AB_EE_SPI_HCMD);
/* Wait for read/write to complete */
rc = falcon_spi_wait(efx);
@@ -1729,7 +292,7 @@ int falcon_spi_cmd(const struct efx_spi_device *spi,
/* Read data */
if (out != NULL) {
- falcon_read(efx, &reg, EE_SPI_HDATA_REG_KER);
+ efx_reado(efx, &reg, FR_AB_EE_SPI_HDATA);
memcpy(out, &reg, len);
}
@@ -1751,15 +314,15 @@ efx_spi_munge_command(const struct efx_spi_device *spi,
}
/* Wait up to 10 ms for buffered write completion */
-int falcon_spi_wait_write(const struct efx_spi_device *spi)
+int
+falcon_spi_wait_write(struct efx_nic *efx, const struct efx_spi_device *spi)
{
- struct efx_nic *efx = spi->efx;
unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100);
u8 status;
int rc;
for (;;) {
- rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
+ rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
&status, sizeof(status));
if (rc)
return rc;
@@ -1775,8 +338,8 @@ int falcon_spi_wait_write(const struct efx_spi_device *spi)
}
}
-int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
- size_t len, size_t *retlen, u8 *buffer)
+int falcon_spi_read(struct efx_nic *efx, const struct efx_spi_device *spi,
+ loff_t start, size_t len, size_t *retlen, u8 *buffer)
{
size_t block_len, pos = 0;
unsigned int command;
@@ -1786,7 +349,7 @@ int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
block_len = min(len - pos, FALCON_SPI_MAX_LEN);
command = efx_spi_munge_command(spi, SPI_READ, start + pos);
- rc = falcon_spi_cmd(spi, command, start + pos, NULL,
+ rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL,
buffer + pos, block_len);
if (rc)
break;
@@ -1805,8 +368,9 @@ int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
return rc;
}
-int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
- size_t len, size_t *retlen, const u8 *buffer)
+int
+falcon_spi_write(struct efx_nic *efx, const struct efx_spi_device *spi,
+ loff_t start, size_t len, size_t *retlen, const u8 *buffer)
{
u8 verify_buffer[FALCON_SPI_MAX_LEN];
size_t block_len, pos = 0;
@@ -1814,24 +378,24 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
int rc = 0;
while (pos < len) {
- rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
+ rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0);
if (rc)
break;
block_len = min(len - pos,
falcon_spi_write_limit(spi, start + pos));
command = efx_spi_munge_command(spi, SPI_WRITE, start + pos);
- rc = falcon_spi_cmd(spi, command, start + pos,
+ rc = falcon_spi_cmd(efx, spi, command, start + pos,
buffer + pos, NULL, block_len);
if (rc)
break;
- rc = falcon_spi_wait_write(spi);
+ rc = falcon_spi_wait_write(efx, spi);
if (rc)
break;
command = efx_spi_munge_command(spi, SPI_READ, start + pos);
- rc = falcon_spi_cmd(spi, command, start + pos,
+ rc = falcon_spi_cmd(efx, spi, command, start + pos,
NULL, verify_buffer, block_len);
if (memcmp(verify_buffer, buffer + pos, block_len)) {
rc = -EIO;
@@ -1860,60 +424,70 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
**************************************************************************
*/
-static int falcon_reset_macs(struct efx_nic *efx)
+static void falcon_push_multicast_hash(struct efx_nic *efx)
{
- efx_oword_t reg;
+ union efx_multicast_hash *mc_hash = &efx->multicast_hash;
+
+ WARN_ON(!mutex_is_locked(&efx->mac_lock));
+
+ efx_writeo(efx, &mc_hash->oword[0], FR_AB_MAC_MC_HASH_REG0);
+ efx_writeo(efx, &mc_hash->oword[1], FR_AB_MAC_MC_HASH_REG1);
+}
+
+static void falcon_reset_macs(struct efx_nic *efx)
+{
+ struct falcon_nic_data *nic_data = efx->nic_data;
+ efx_oword_t reg, mac_ctrl;
int count;
- if (falcon_rev(efx) < FALCON_REV_B0) {
+ if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) {
/* It's not safe to use GLB_CTL_REG to reset the
* macs, so instead use the internal MAC resets
*/
if (!EFX_IS10G(efx)) {
- EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 1);
- falcon_write(efx, &reg, GM_CFG1_REG);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 1);
+ efx_writeo(efx, &reg, FR_AB_GM_CFG1);
udelay(1000);
- EFX_POPULATE_OWORD_1(reg, GM_SW_RST, 0);
- falcon_write(efx, &reg, GM_CFG1_REG);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_SW_RST, 0);
+ efx_writeo(efx, &reg, FR_AB_GM_CFG1);
udelay(1000);
- return 0;
+ return;
} else {
- EFX_POPULATE_OWORD_1(reg, XM_CORE_RST, 1);
- falcon_write(efx, &reg, XM_GLB_CFG_REG);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_CORE_RST, 1);
+ efx_writeo(efx, &reg, FR_AB_XM_GLB_CFG);
for (count = 0; count < 10000; count++) {
- falcon_read(efx, &reg, XM_GLB_CFG_REG);
- if (EFX_OWORD_FIELD(reg, XM_CORE_RST) == 0)
- return 0;
+ efx_reado(efx, &reg, FR_AB_XM_GLB_CFG);
+ if (EFX_OWORD_FIELD(reg, FRF_AB_XM_CORE_RST) ==
+ 0)
+ return;
udelay(10);
}
EFX_ERR(efx, "timed out waiting for XMAC core reset\n");
- return -ETIMEDOUT;
}
}
- /* MAC stats will fail whilst the TX fifo is draining. Serialise
- * the drain sequence with the statistics fetch */
- efx_stats_disable(efx);
+ /* Mac stats will fail whist the TX fifo is draining */
+ WARN_ON(nic_data->stats_disable_count == 0);
- falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
- EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1);
- falcon_write(efx, &reg, MAC0_CTRL_REG_KER);
+ efx_reado(efx, &mac_ctrl, FR_AB_MAC_CTRL);
+ EFX_SET_OWORD_FIELD(mac_ctrl, FRF_BB_TXFIFO_DRAIN_EN, 1);
+ efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL);
- falcon_read(efx, &reg, GLB_CTL_REG_KER);
- EFX_SET_OWORD_FIELD(reg, RST_XGTX, 1);
- EFX_SET_OWORD_FIELD(reg, RST_XGRX, 1);
- EFX_SET_OWORD_FIELD(reg, RST_EM, 1);
- falcon_write(efx, &reg, GLB_CTL_REG_KER);
+ efx_reado(efx, &reg, FR_AB_GLB_CTL);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGTX, 1);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_XGRX, 1);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_RST_EM, 1);
+ efx_writeo(efx, &reg, FR_AB_GLB_CTL);
count = 0;
while (1) {
- falcon_read(efx, &reg, GLB_CTL_REG_KER);
- if (!EFX_OWORD_FIELD(reg, RST_XGTX) &&
- !EFX_OWORD_FIELD(reg, RST_XGRX) &&
- !EFX_OWORD_FIELD(reg, RST_EM)) {
+ efx_reado(efx, &reg, FR_AB_GLB_CTL);
+ if (!EFX_OWORD_FIELD(reg, FRF_AB_RST_XGTX) &&
+ !EFX_OWORD_FIELD(reg, FRF_AB_RST_XGRX) &&
+ !EFX_OWORD_FIELD(reg, FRF_AB_RST_EM)) {
EFX_LOG(efx, "Completed MAC reset after %d loops\n",
count);
break;
@@ -1926,55 +500,50 @@ static int falcon_reset_macs(struct efx_nic *efx)
udelay(10);
}
- efx_stats_enable(efx);
-
- /* If we've reset the EM block and the link is up, then
- * we'll have to kick the XAUI link so the PHY can recover */
- if (efx->link_up && EFX_IS10G(efx) && EFX_WORKAROUND_5147(efx))
- falcon_reset_xaui(efx);
-
- return 0;
+ /* Ensure the correct MAC is selected before statistics
+ * are re-enabled by the caller */
+ efx_writeo(efx, &mac_ctrl, FR_AB_MAC_CTRL);
}
void falcon_drain_tx_fifo(struct efx_nic *efx)
{
efx_oword_t reg;
- if ((falcon_rev(efx) < FALCON_REV_B0) ||
+ if ((efx_nic_rev(efx) < EFX_REV_FALCON_B0) ||
(efx->loopback_mode != LOOPBACK_NONE))
return;
- falcon_read(efx, &reg, MAC0_CTRL_REG_KER);
+ efx_reado(efx, &reg, FR_AB_MAC_CTRL);
/* There is no point in draining more than once */
- if (EFX_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0))
+ if (EFX_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN))
return;
falcon_reset_macs(efx);
}
-void falcon_deconfigure_mac_wrapper(struct efx_nic *efx)
+static void falcon_deconfigure_mac_wrapper(struct efx_nic *efx)
{
efx_oword_t reg;
- if (falcon_rev(efx) < FALCON_REV_B0)
+ if (efx_nic_rev(efx) < EFX_REV_FALCON_B0)
return;
/* Isolate the MAC -> RX */
- falcon_read(efx, &reg, RX_CFG_REG_KER);
- EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 0);
- falcon_write(efx, &reg, RX_CFG_REG_KER);
+ efx_reado(efx, &reg, FR_AZ_RX_CFG);
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 0);
+ efx_writeo(efx, &reg, FR_AZ_RX_CFG);
- if (!efx->link_up)
- falcon_drain_tx_fifo(efx);
+ /* Isolate TX -> MAC */
+ falcon_drain_tx_fifo(efx);
}
void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
{
+ struct efx_link_state *link_state = &efx->link_state;
efx_oword_t reg;
int link_speed;
- bool tx_fc;
- switch (efx->link_speed) {
+ switch (link_state->speed) {
case 10000: link_speed = 3; break;
case 1000: link_speed = 2; break;
case 100: link_speed = 1; break;
@@ -1985,75 +554,139 @@ void falcon_reconfigure_mac_wrapper(struct efx_nic *efx)
* indefinitely held and TX queue can be flushed at any point
* while the link is down. */
EFX_POPULATE_OWORD_5(reg,
- MAC_XOFF_VAL, 0xffff /* max pause time */,
- MAC_BCAD_ACPT, 1,
- MAC_UC_PROM, efx->promiscuous,
- MAC_LINK_STATUS, 1, /* always set */
- MAC_SPEED, link_speed);
+ FRF_AB_MAC_XOFF_VAL, 0xffff /* max pause time */,
+ FRF_AB_MAC_BCAD_ACPT, 1,
+ FRF_AB_MAC_UC_PROM, efx->promiscuous,
+ FRF_AB_MAC_LINK_STATUS, 1, /* always set */
+ FRF_AB_MAC_SPEED, link_speed);
/* On B0, MAC backpressure can be disabled and packets get
* discarded. */
- if (falcon_rev(efx) >= FALCON_REV_B0) {
- EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0,
- !efx->link_up);
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) {
+ EFX_SET_OWORD_FIELD(reg, FRF_BB_TXFIFO_DRAIN_EN,
+ !link_state->up);
}
- falcon_write(efx, &reg, MAC0_CTRL_REG_KER);
+ efx_writeo(efx, &reg, FR_AB_MAC_CTRL);
/* Restore the multicast hash registers. */
- falcon_set_multicast_hash(efx);
-
- /* Transmission of pause frames when RX crosses the threshold is
- * covered by RX_XOFF_MAC_EN and XM_TX_CFG_REG:XM_FCNTL.
- * Action on receipt of pause frames is controller by XM_DIS_FCNTL */
- tx_fc = !!(efx->link_fc & EFX_FC_TX);
- falcon_read(efx, &reg, RX_CFG_REG_KER);
- EFX_SET_OWORD_FIELD_VER(efx, reg, RX_XOFF_MAC_EN, tx_fc);
+ falcon_push_multicast_hash(efx);
+ efx_reado(efx, &reg, FR_AZ_RX_CFG);
+ /* Enable XOFF signal from RX FIFO (we enabled it during NIC
+ * initialisation but it may read back as 0) */
+ EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1);
/* Unisolate the MAC -> RX */
- if (falcon_rev(efx) >= FALCON_REV_B0)
- EFX_SET_OWORD_FIELD(reg, RX_INGR_EN_B0, 1);
- falcon_write(efx, &reg, RX_CFG_REG_KER);
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0)
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1);
+ efx_writeo(efx, &reg, FR_AZ_RX_CFG);
}
-int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset)
+static void falcon_stats_request(struct efx_nic *efx)
{
+ struct falcon_nic_data *nic_data = efx->nic_data;
efx_oword_t reg;
- u32 *dma_done;
- int i;
- if (disable_dma_stats)
- return 0;
+ WARN_ON(nic_data->stats_pending);
+ WARN_ON(nic_data->stats_disable_count);
- /* Statistics fetch will fail if the MAC is in TX drain */
- if (falcon_rev(efx) >= FALCON_REV_B0) {
- efx_oword_t temp;
- falcon_read(efx, &temp, MAC0_CTRL_REG_KER);
- if (EFX_OWORD_FIELD(temp, TXFIFO_DRAIN_EN_B0))
- return 0;
- }
+ if (nic_data->stats_dma_done == NULL)
+ return; /* no mac selected */
- dma_done = (efx->stats_buffer.addr + done_offset);
- *dma_done = FALCON_STATS_NOT_DONE;
+ *nic_data->stats_dma_done = FALCON_STATS_NOT_DONE;
+ nic_data->stats_pending = true;
wmb(); /* ensure done flag is clear */
/* Initiate DMA transfer of stats */
EFX_POPULATE_OWORD_2(reg,
- MAC_STAT_DMA_CMD, 1,
- MAC_STAT_DMA_ADR,
+ FRF_AB_MAC_STAT_DMA_CMD, 1,
+ FRF_AB_MAC_STAT_DMA_ADR,
efx->stats_buffer.dma_addr);
- falcon_write(efx, &reg, MAC0_STAT_DMA_REG_KER);
+ efx_writeo(efx, &reg, FR_AB_MAC_STAT_DMA);
- /* Wait for transfer to complete */
- for (i = 0; i < 400; i++) {
- if (*(volatile u32 *)dma_done == FALCON_STATS_DONE) {
- rmb(); /* Ensure the stats are valid. */
- return 0;
- }
- udelay(10);
+ mod_timer(&nic_data->stats_timer, round_jiffies_up(jiffies + HZ / 2));
+}
+
+static void falcon_stats_complete(struct efx_nic *efx)
+{
+ struct falcon_nic_data *nic_data = efx->nic_data;
+
+ if (!nic_data->stats_pending)
+ return;
+
+ nic_data->stats_pending = 0;
+ if (*nic_data->stats_dma_done == FALCON_STATS_DONE) {
+ rmb(); /* read the done flag before the stats */
+ efx->mac_op->update_stats(efx);
+ } else {
+ EFX_ERR(efx, "timed out waiting for statistics\n");
}
+}
- EFX_ERR(efx, "timed out waiting for statistics\n");
- return -ETIMEDOUT;
+static void falcon_stats_timer_func(unsigned long context)
+{
+ struct efx_nic *efx = (struct efx_nic *)context;
+ struct falcon_nic_data *nic_data = efx->nic_data;
+
+ spin_lock(&efx->stats_lock);
+
+ falcon_stats_complete(efx);
+ if (nic_data->stats_disable_count == 0)
+ falcon_stats_request(efx);
+
+ spin_unlock(&efx->stats_lock);
+}
+
+static void falcon_switch_mac(struct efx_nic *efx);
+
+static bool falcon_loopback_link_poll(struct efx_nic *efx)
+{
+ struct efx_link_state old_state = efx->link_state;
+
+ WARN_ON(!mutex_is_locked(&efx->mac_lock));
+ WARN_ON(!LOOPBACK_INTERNAL(efx));
+
+ efx->link_state.fd = true;
+ efx->link_state.fc = efx->wanted_fc;
+ efx->link_state.up = true;
+
+ if (efx->loopback_mode == LOOPBACK_GMAC)
+ efx->link_state.speed = 1000;
+ else
+ efx->link_state.speed = 10000;
+
+ return !efx_link_state_equal(&efx->link_state, &old_state);
+}
+
+static int falcon_reconfigure_port(struct efx_nic *efx)
+{
+ int rc;
+
+ WARN_ON(efx_nic_rev(efx) > EFX_REV_FALCON_B0);
+
+ /* Poll the PHY link state *before* reconfiguring it. This means we
+ * will pick up the correct speed (in loopback) to select the correct
+ * MAC.
+ */
+ if (LOOPBACK_INTERNAL(efx))
+ falcon_loopback_link_poll(efx);
+ else
+ efx->phy_op->poll(efx);
+
+ falcon_stop_nic_stats(efx);
+ falcon_deconfigure_mac_wrapper(efx);
+
+ falcon_switch_mac(efx);
+
+ efx->phy_op->reconfigure(efx);
+ rc = efx->mac_op->reconfigure(efx);
+ BUG_ON(rc);
+
+ falcon_start_nic_stats(efx);
+
+ /* Synchronise efx->link_state with the kernel */
+ efx_link_status_changed(efx);
+
+ return 0;
}
/**************************************************************************
@@ -2066,18 +699,18 @@ int falcon_dma_stats(struct efx_nic *efx, unsigned int done_offset)
/* Wait for GMII access to complete */
static int falcon_gmii_wait(struct efx_nic *efx)
{
- efx_dword_t md_stat;
+ efx_oword_t md_stat;
int count;
/* wait upto 50ms - taken max from datasheet */
for (count = 0; count < 5000; count++) {
- falcon_readl(efx, &md_stat, MD_STAT_REG_KER);
- if (EFX_DWORD_FIELD(md_stat, MD_BSY) == 0) {
- if (EFX_DWORD_FIELD(md_stat, MD_LNFL) != 0 ||
- EFX_DWORD_FIELD(md_stat, MD_BSERR) != 0) {
+ efx_reado(efx, &md_stat, FR_AB_MD_STAT);
+ if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSY) == 0) {
+ if (EFX_OWORD_FIELD(md_stat, FRF_AB_MD_LNFL) != 0 ||
+ EFX_OWORD_FIELD(md_stat, FRF_AB_MD_BSERR) != 0) {
EFX_ERR(efx, "error from GMII access "
- EFX_DWORD_FMT"\n",
- EFX_DWORD_VAL(md_stat));
+ EFX_OWORD_FMT"\n",
+ EFX_OWORD_VAL(md_stat));
return -EIO;
}
return 0;
@@ -2099,7 +732,7 @@ static int falcon_mdio_write(struct net_device *net_dev,
EFX_REGDUMP(efx, "writing MDIO %d register %d.%d with 0x%04x\n",
prtad, devad, addr, value);
- spin_lock_bh(&efx->phy_lock);
+ mutex_lock(&efx->mdio_lock);
/* Check MDIO not currently being accessed */
rc = falcon_gmii_wait(efx);
@@ -2107,34 +740,35 @@ static int falcon_mdio_write(struct net_device *net_dev,
goto out;
/* Write the address/ID register */
- EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr);
- falcon_write(efx, &reg, MD_PHY_ADR_REG_KER);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr);
+ efx_writeo(efx, &reg, FR_AB_MD_PHY_ADR);
- EFX_POPULATE_OWORD_2(reg, MD_PRT_ADR, prtad, MD_DEV_ADR, devad);
- falcon_write(efx, &reg, MD_ID_REG_KER);
+ EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad,
+ FRF_AB_MD_DEV_ADR, devad);
+ efx_writeo(efx, &reg, FR_AB_MD_ID);
/* Write data */
- EFX_POPULATE_OWORD_1(reg, MD_TXD, value);
- falcon_write(efx, &reg, MD_TXD_REG_KER);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_TXD, value);
+ efx_writeo(efx, &reg, FR_AB_MD_TXD);
EFX_POPULATE_OWORD_2(reg,
- MD_WRC, 1,
- MD_GC, 0);
- falcon_write(efx, &reg, MD_CS_REG_KER);
+ FRF_AB_MD_WRC, 1,
+ FRF_AB_MD_GC, 0);
+ efx_writeo(efx, &reg, FR_AB_MD_CS);
/* Wait for data to be written */
rc = falcon_gmii_wait(efx);
if (rc) {
/* Abort the write operation */
EFX_POPULATE_OWORD_2(reg,
- MD_WRC, 0,
- MD_GC, 1);
- falcon_write(efx, &reg, MD_CS_REG_KER);
+ FRF_AB_MD_WRC, 0,
+ FRF_AB_MD_GC, 1);
+ efx_writeo(efx, &reg, FR_AB_MD_CS);
udelay(10);
}
- out:
- spin_unlock_bh(&efx->phy_lock);
+out:
+ mutex_unlock(&efx->mdio_lock);
return rc;
}
@@ -2146,152 +780,139 @@ static int falcon_mdio_read(struct net_device *net_dev,
efx_oword_t reg;
int rc;
- spin_lock_bh(&efx->phy_lock);
+ mutex_lock(&efx->mdio_lock);
/* Check MDIO not currently being accessed */
rc = falcon_gmii_wait(efx);
if (rc)
goto out;
- EFX_POPULATE_OWORD_1(reg, MD_PHY_ADR, addr);
- falcon_write(efx, &reg, MD_PHY_ADR_REG_KER);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_MD_PHY_ADR, addr);
+ efx_writeo(efx, &reg, FR_AB_MD_PHY_ADR);
- EFX_POPULATE_OWORD_2(reg, MD_PRT_ADR, prtad, MD_DEV_ADR, devad);
- falcon_write(efx, &reg, MD_ID_REG_KER);
+ EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_PRT_ADR, prtad,
+ FRF_AB_MD_DEV_ADR, devad);
+ efx_writeo(efx, &reg, FR_AB_MD_ID);
/* Request data to be read */
- EFX_POPULATE_OWORD_2(reg, MD_RDC, 1, MD_GC, 0);
- falcon_write(efx, &reg, MD_CS_REG_KER);
+ EFX_POPULATE_OWORD_2(reg, FRF_AB_MD_RDC, 1, FRF_AB_MD_GC, 0);
+ efx_writeo(efx, &reg, FR_AB_MD_CS);
/* Wait for data to become available */
rc = falcon_gmii_wait(efx);
if (rc == 0) {
- falcon_read(efx, &reg, MD_RXD_REG_KER);
- rc = EFX_OWORD_FIELD(reg, MD_RXD);
+ efx_reado(efx, &reg, FR_AB_MD_RXD);
+ rc = EFX_OWORD_FIELD(reg, FRF_AB_MD_RXD);
EFX_REGDUMP(efx, "read from MDIO %d register %d.%d, got %04x\n",
prtad, devad, addr, rc);
} else {
/* Abort the read operation */
EFX_POPULATE_OWORD_2(reg,
- MD_RIC, 0,
- MD_GC, 1);
- falcon_write(efx, &reg, MD_CS_REG_KER);
+ FRF_AB_MD_RIC, 0,
+ FRF_AB_MD_GC, 1);
+ efx_writeo(efx, &reg, FR_AB_MD_CS);
EFX_LOG(efx, "read from MDIO %d register %d.%d, got error %d\n",
prtad, devad, addr, rc);
}
- out:
- spin_unlock_bh(&efx->phy_lock);
+out:
+ mutex_unlock(&efx->mdio_lock);
return rc;
}
-static int falcon_probe_phy(struct efx_nic *efx)
+static void falcon_clock_mac(struct efx_nic *efx)
{
- switch (efx->phy_type) {
- case PHY_TYPE_SFX7101:
- efx->phy_op = &falcon_sfx7101_phy_ops;
- break;
- case PHY_TYPE_SFT9001A:
- case PHY_TYPE_SFT9001B:
- efx->phy_op = &falcon_sft9001_phy_ops;
- break;
- case PHY_TYPE_QT2022C2:
- case PHY_TYPE_QT2025C:
- efx->phy_op = &falcon_xfp_phy_ops;
- break;
- default:
- EFX_ERR(efx, "Unknown PHY type %d\n",
- efx->phy_type);
- return -1;
- }
-
- if (efx->phy_op->macs & EFX_XMAC)
- efx->loopback_modes |= ((1 << LOOPBACK_XGMII) |
- (1 << LOOPBACK_XGXS) |
- (1 << LOOPBACK_XAUI));
- if (efx->phy_op->macs & EFX_GMAC)
- efx->loopback_modes |= (1 << LOOPBACK_GMAC);
- efx->loopback_modes |= efx->phy_op->loopbacks;
+ unsigned strap_val;
+ efx_oword_t nic_stat;
- return 0;
+ /* Configure the NIC generated MAC clock correctly */
+ efx_reado(efx, &nic_stat, FR_AB_NIC_STAT);
+ strap_val = EFX_IS10G(efx) ? 5 : 3;
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) {
+ EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP_EN, 1);
+ EFX_SET_OWORD_FIELD(nic_stat, FRF_BB_EE_STRAP, strap_val);
+ efx_writeo(efx, &nic_stat, FR_AB_NIC_STAT);
+ } else {
+ /* Falcon A1 does not support 1G/10G speed switching
+ * and must not be used with a PHY that does. */
+ BUG_ON(EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_PINS) !=
+ strap_val);
+ }
}
-int falcon_switch_mac(struct efx_nic *efx)
+static void falcon_switch_mac(struct efx_nic *efx)
{
struct efx_mac_operations *old_mac_op = efx->mac_op;
- efx_oword_t nic_stat;
- unsigned strap_val;
- int rc = 0;
-
- /* Don't try to fetch MAC stats while we're switching MACs */
- efx_stats_disable(efx);
-
- /* Internal loopbacks override the phy speed setting */
- if (efx->loopback_mode == LOOPBACK_GMAC) {
- efx->link_speed = 1000;
- efx->link_fd = true;
- } else if (LOOPBACK_INTERNAL(efx)) {
- efx->link_speed = 10000;
- efx->link_fd = true;
- }
+ struct falcon_nic_data *nic_data = efx->nic_data;
+ unsigned int stats_done_offset;
WARN_ON(!mutex_is_locked(&efx->mac_lock));
+ WARN_ON(nic_data->stats_disable_count == 0);
+
efx->mac_op = (EFX_IS10G(efx) ?
&falcon_xmac_operations : &falcon_gmac_operations);
- /* Always push the NIC_STAT_REG setting even if the mac hasn't
- * changed, because this function is run post online reset */
- falcon_read(efx, &nic_stat, NIC_STAT_REG);
- strap_val = EFX_IS10G(efx) ? 5 : 3;
- if (falcon_rev(efx) >= FALCON_REV_B0) {
- EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_EN, 1);
- EFX_SET_OWORD_FIELD(nic_stat, EE_STRAP_OVR, strap_val);
- falcon_write(efx, &nic_stat, NIC_STAT_REG);
- } else {
- /* Falcon A1 does not support 1G/10G speed switching
- * and must not be used with a PHY that does. */
- BUG_ON(EFX_OWORD_FIELD(nic_stat, STRAP_PINS) != strap_val);
- }
+ if (EFX_IS10G(efx))
+ stats_done_offset = XgDmaDone_offset;
+ else
+ stats_done_offset = GDmaDone_offset;
+ nic_data->stats_dma_done = efx->stats_buffer.addr + stats_done_offset;
if (old_mac_op == efx->mac_op)
- goto out;
+ return;
+
+ falcon_clock_mac(efx);
EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G');
/* Not all macs support a mac-level link state */
- efx->mac_up = true;
-
- rc = falcon_reset_macs(efx);
-out:
- efx_stats_enable(efx);
- return rc;
+ efx->xmac_poll_required = false;
+ falcon_reset_macs(efx);
}
/* This call is responsible for hooking in the MAC and PHY operations */
-int falcon_probe_port(struct efx_nic *efx)
+static int falcon_probe_port(struct efx_nic *efx)
{
int rc;
- /* Hook in PHY operations table */
- rc = falcon_probe_phy(efx);
- if (rc)
- return rc;
+ switch (efx->phy_type) {
+ case PHY_TYPE_SFX7101:
+ efx->phy_op = &falcon_sfx7101_phy_ops;
+ break;
+ case PHY_TYPE_SFT9001A:
+ case PHY_TYPE_SFT9001B:
+ efx->phy_op = &falcon_sft9001_phy_ops;
+ break;
+ case PHY_TYPE_QT2022C2:
+ case PHY_TYPE_QT2025C:
+ efx->phy_op = &falcon_qt202x_phy_ops;
+ break;
+ default:
+ EFX_ERR(efx, "Unknown PHY type %d\n",
+ efx->phy_type);
+ return -ENODEV;
+ }
- /* Set up MDIO structure for PHY */
- efx->mdio.mmds = efx->phy_op->mmds;
- efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
+ /* Fill out MDIO structure and loopback modes */
efx->mdio.mdio_read = falcon_mdio_read;
efx->mdio.mdio_write = falcon_mdio_write;
+ rc = efx->phy_op->probe(efx);
+ if (rc != 0)
+ return rc;
+
+ /* Initial assumption */
+ efx->link_state.speed = 10000;
+ efx->link_state.fd = true;
/* Hardware flow ctrl. FalconA RX FIFO too small for pause generation */
- if (falcon_rev(efx) >= FALCON_REV_B0)
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0)
efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
else
efx->wanted_fc = EFX_FC_RX;
/* Allocate buffer for stats */
- rc = falcon_alloc_buffer(efx, &efx->stats_buffer,
- FALCON_MAC_STATS_SIZE);
+ rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer,
+ FALCON_MAC_STATS_SIZE);
if (rc)
return rc;
EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n",
@@ -2302,40 +923,19 @@ int falcon_probe_port(struct efx_nic *efx)
return 0;
}
-void falcon_remove_port(struct efx_nic *efx)
+static void falcon_remove_port(struct efx_nic *efx)
{
- falcon_free_buffer(efx, &efx->stats_buffer);
+ efx_nic_free_buffer(efx, &efx->stats_buffer);
}
/**************************************************************************
*
- * Multicast filtering
- *
- **************************************************************************
- */
-
-void falcon_set_multicast_hash(struct efx_nic *efx)
-{
- union efx_multicast_hash *mc_hash = &efx->multicast_hash;
-
- /* Broadcast packets go through the multicast hash filter.
- * ether_crc_le() of the broadcast address is 0xbe2612ff
- * so we always add bit 0xff to the mask.
- */
- set_bit_le(0xff, mc_hash->byte);
-
- falcon_write(efx, &mc_hash->oword[0], MAC_MCAST_HASH_REG0_KER);
- falcon_write(efx, &mc_hash->oword[1], MAC_MCAST_HASH_REG1_KER);
-}
-
-
-/**************************************************************************
- *
* Falcon test code
*
**************************************************************************/
-int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
+static int
+falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
{
struct falcon_nvconfig *nvconfig;
struct efx_spi_device *spi;
@@ -2351,10 +951,10 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL);
if (!region)
return -ENOMEM;
- nvconfig = region + NVCONFIG_OFFSET;
+ nvconfig = region + FALCON_NVCONFIG_OFFSET;
mutex_lock(&efx->spi_lock);
- rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region);
+ rc = falcon_spi_read(efx, spi, 0, FALCON_NVCONFIG_END, NULL, region);
mutex_unlock(&efx->spi_lock);
if (rc) {
EFX_ERR(efx, "Failed to read %s\n",
@@ -2367,7 +967,7 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
struct_ver = le16_to_cpu(nvconfig->board_struct_ver);
rc = -EINVAL;
- if (magic_num != NVCONFIG_BOARD_MAGIC_NUM) {
+ if (magic_num != FALCON_NVCONFIG_BOARD_MAGIC_NUM) {
EFX_ERR(efx, "NVRAM bad magic 0x%x\n", magic_num);
goto out;
}
@@ -2398,107 +998,54 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
return rc;
}
-/* Registers tested in the falcon register test */
-static struct {
- unsigned address;
- efx_oword_t mask;
-} efx_test_registers[] = {
- { ADR_REGION_REG_KER,
+static int falcon_test_nvram(struct efx_nic *efx)
+{
+ return falcon_read_nvram(efx, NULL);
+}
+
+static const struct efx_nic_register_test falcon_b0_register_tests[] = {
+ { FR_AZ_ADR_REGION,
EFX_OWORD32(0x0001FFFF, 0x0001FFFF, 0x0001FFFF, 0x0001FFFF) },
- { RX_CFG_REG_KER,
+ { FR_AZ_RX_CFG,
EFX_OWORD32(0xFFFFFFFE, 0x00017FFF, 0x00000000, 0x00000000) },
- { TX_CFG_REG_KER,
+ { FR_AZ_TX_CFG,
EFX_OWORD32(0x7FFF0037, 0x00000000, 0x00000000, 0x00000000) },
- { TX_CFG2_REG_KER,
+ { FR_AZ_TX_RESERVED,
EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) },
- { MAC0_CTRL_REG_KER,
+ { FR_AB_MAC_CTRL,
EFX_OWORD32(0xFFFF0000, 0x00000000, 0x00000000, 0x00000000) },
- { SRM_TX_DC_CFG_REG_KER,
+ { FR_AZ_SRM_TX_DC_CFG,
EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) },
- { RX_DC_CFG_REG_KER,
+ { FR_AZ_RX_DC_CFG,
EFX_OWORD32(0x0000000F, 0x00000000, 0x00000000, 0x00000000) },
- { RX_DC_PF_WM_REG_KER,
+ { FR_AZ_RX_DC_PF_WM,
EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) },
- { DP_CTRL_REG,
+ { FR_BZ_DP_CTRL,
EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) },
- { GM_CFG2_REG,
+ { FR_AB_GM_CFG2,
EFX_OWORD32(0x00007337, 0x00000000, 0x00000000, 0x00000000) },
- { GMF_CFG0_REG,
+ { FR_AB_GMF_CFG0,
EFX_OWORD32(0x00001F1F, 0x00000000, 0x00000000, 0x00000000) },
- { XM_GLB_CFG_REG,
+ { FR_AB_XM_GLB_CFG,
EFX_OWORD32(0x00000C68, 0x00000000, 0x00000000, 0x00000000) },
- { XM_TX_CFG_REG,
+ { FR_AB_XM_TX_CFG,
EFX_OWORD32(0x00080164, 0x00000000, 0x00000000, 0x00000000) },
- { XM_RX_CFG_REG,
+ { FR_AB_XM_RX_CFG,
EFX_OWORD32(0x07100A0C, 0x00000000, 0x00000000, 0x00000000) },
- { XM_RX_PARAM_REG,
+ { FR_AB_XM_RX_PARAM,
EFX_OWORD32(0x00001FF8, 0x00000000, 0x00000000, 0x00000000) },
- { XM_FC_REG,
+ { FR_AB_XM_FC,
EFX_OWORD32(0xFFFF0001, 0x00000000, 0x00000000, 0x00000000) },
- { XM_ADR_LO_REG,
+ { FR_AB_XM_ADR_LO,
EFX_OWORD32(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000) },
- { XX_SD_CTL_REG,
+ { FR_AB_XX_SD_CTL,
EFX_OWORD32(0x0003FF0F, 0x00000000, 0x00000000, 0x00000000) },
};
-static bool efx_masked_compare_oword(const efx_oword_t *a, const efx_oword_t *b,
- const efx_oword_t *mask)
+static int falcon_b0_test_registers(struct efx_nic *efx)
{
- return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) ||
- ((a->u64[1] ^ b->u64[1]) & mask->u64[1]);
-}
-
-int falcon_test_registers(struct efx_nic *efx)
-{
- unsigned address = 0, i, j;
- efx_oword_t mask, imask, original, reg, buf;
-
- /* Falcon should be in loopback to isolate the XMAC from the PHY */
- WARN_ON(!LOOPBACK_INTERNAL(efx));
-
- for (i = 0; i < ARRAY_SIZE(efx_test_registers); ++i) {
- address = efx_test_registers[i].address;
- mask = imask = efx_test_registers[i].mask;
- EFX_INVERT_OWORD(imask);
-
- falcon_read(efx, &original, address);
-
- /* bit sweep on and off */
- for (j = 0; j < 128; j++) {
- if (!EFX_EXTRACT_OWORD32(mask, j, j))
- continue;
-
- /* Test this testable bit can be set in isolation */
- EFX_AND_OWORD(reg, original, mask);
- EFX_SET_OWORD32(reg, j, j, 1);
-
- falcon_write(efx, &reg, address);
- falcon_read(efx, &buf, address);
-
- if (efx_masked_compare_oword(&reg, &buf, &mask))
- goto fail;
-
- /* Test this testable bit can be cleared in isolation */
- EFX_OR_OWORD(reg, original, mask);
- EFX_SET_OWORD32(reg, j, j, 0);
-
- falcon_write(efx, &reg, address);
- falcon_read(efx, &buf, address);
-
- if (efx_masked_compare_oword(&reg, &buf, &mask))
- goto fail;
- }
-
- falcon_write(efx, &original, address);
- }
-
- return 0;
-
-fail:
- EFX_ERR(efx, "wrote "EFX_OWORD_FMT" read "EFX_OWORD_FMT
- " at address 0x%x mask "EFX_OWORD_FMT"\n", EFX_OWORD_VAL(reg),
- EFX_OWORD_VAL(buf), address, EFX_OWORD_VAL(mask));
- return -EIO;
+ return efx_nic_test_registers(efx, falcon_b0_register_tests,
+ ARRAY_SIZE(falcon_b0_register_tests));
}
/**************************************************************************
@@ -2510,13 +1057,13 @@ fail:
/* Resets NIC to known state. This routine must be called in process
* context and is allowed to sleep. */
-int falcon_reset_hw(struct efx_nic *efx, enum reset_type method)
+static int falcon_reset_hw(struct efx_nic *efx, enum reset_type method)
{
struct falcon_nic_data *nic_data = efx->nic_data;
efx_oword_t glb_ctl_reg_ker;
int rc;
- EFX_LOG(efx, "performing hardware reset (%d)\n", method);
+ EFX_LOG(efx, "performing %s hardware reset\n", RESET_TYPE(method));
/* Initiate device reset */
if (method == RESET_TYPE_WORLD) {
@@ -2526,7 +1073,7 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method)
"function prior to hardware reset\n");
goto fail1;
}
- if (FALCON_IS_DUAL_FUNC(efx)) {
+ if (efx_nic_is_dual_func(efx)) {
rc = pci_save_state(nic_data->pci_dev2);
if (rc) {
EFX_ERR(efx, "failed to backup PCI state of "
@@ -2537,29 +1084,31 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method)
}
EFX_POPULATE_OWORD_2(glb_ctl_reg_ker,
- EXT_PHY_RST_DUR, 0x7,
- SWRST, 1);
+ FRF_AB_EXT_PHY_RST_DUR,
+ FFE_AB_EXT_PHY_RST_DUR_10240US,
+ FRF_AB_SWRST, 1);
} else {
- int reset_phy = (method == RESET_TYPE_INVISIBLE ?
- EXCLUDE_FROM_RESET : 0);
-
EFX_POPULATE_OWORD_7(glb_ctl_reg_ker,
- EXT_PHY_RST_CTL, reset_phy,
- PCIE_CORE_RST_CTL, EXCLUDE_FROM_RESET,
- PCIE_NSTCK_RST_CTL, EXCLUDE_FROM_RESET,
- PCIE_SD_RST_CTL, EXCLUDE_FROM_RESET,
- EE_RST_CTL, EXCLUDE_FROM_RESET,
- EXT_PHY_RST_DUR, 0x7 /* 10ms */,
- SWRST, 1);
- }
- falcon_write(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER);
+ /* exclude PHY from "invisible" reset */
+ FRF_AB_EXT_PHY_RST_CTL,
+ method == RESET_TYPE_INVISIBLE,
+ /* exclude EEPROM/flash and PCIe */
+ FRF_AB_PCIE_CORE_RST_CTL, 1,
+ FRF_AB_PCIE_NSTKY_RST_CTL, 1,
+ FRF_AB_PCIE_SD_RST_CTL, 1,
+ FRF_AB_EE_RST_CTL, 1,
+ FRF_AB_EXT_PHY_RST_DUR,
+ FFE_AB_EXT_PHY_RST_DUR_10240US,
+ FRF_AB_SWRST, 1);
+ }
+ efx_writeo(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL);
EFX_LOG(efx, "waiting for hardware reset\n");
schedule_timeout_uninterruptible(HZ / 20);
/* Restore PCI configuration if needed */
if (method == RESET_TYPE_WORLD) {
- if (FALCON_IS_DUAL_FUNC(efx)) {
+ if (efx_nic_is_dual_func(efx)) {
rc = pci_restore_state(nic_data->pci_dev2);
if (rc) {
EFX_ERR(efx, "failed to restore PCI config for "
@@ -2577,8 +1126,8 @@ int falcon_reset_hw(struct efx_nic *efx, enum reset_type method)
}
/* Assert that reset complete */
- falcon_read(efx, &glb_ctl_reg_ker, GLB_CTL_REG_KER);
- if (EFX_OWORD_FIELD(glb_ctl_reg_ker, SWRST) != 0) {
+ efx_reado(efx, &glb_ctl_reg_ker, FR_AB_GLB_CTL);
+ if (EFX_OWORD_FIELD(glb_ctl_reg_ker, FRF_AB_SWRST) != 0) {
rc = -ETIMEDOUT;
EFX_ERR(efx, "timed out waiting for hardware reset\n");
goto fail5;
@@ -2597,6 +1146,44 @@ fail5:
return rc;
}
+static void falcon_monitor(struct efx_nic *efx)
+{
+ bool link_changed;
+ int rc;
+
+ BUG_ON(!mutex_is_locked(&efx->mac_lock));
+
+ rc = falcon_board(efx)->type->monitor(efx);
+ if (rc) {
+ EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
+ (rc == -ERANGE) ? "reported fault" : "failed");
+ efx->phy_mode |= PHY_MODE_LOW_POWER;
+ rc = __efx_reconfigure_port(efx);
+ WARN_ON(rc);
+ }
+
+ if (LOOPBACK_INTERNAL(efx))
+ link_changed = falcon_loopback_link_poll(efx);
+ else
+ link_changed = efx->phy_op->poll(efx);
+
+ if (link_changed) {
+ falcon_stop_nic_stats(efx);
+ falcon_deconfigure_mac_wrapper(efx);
+
+ falcon_switch_mac(efx);
+ rc = efx->mac_op->reconfigure(efx);
+ BUG_ON(rc);
+
+ falcon_start_nic_stats(efx);
+
+ efx_link_status_changed(efx);
+ }
+
+ if (EFX_IS10G(efx))
+ falcon_poll_xmac(efx);
+}
+
/* Zeroes out the SRAM contents. This routine must be called in
* process context and is allowed to sleep.
*/
@@ -2606,16 +1193,16 @@ static int falcon_reset_sram(struct efx_nic *efx)
int count;
/* Set the SRAM wake/sleep GPIO appropriately. */
- falcon_read(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER);
- EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OEN, 1);
- EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, GPIO1_OUT, 1);
- falcon_write(efx, &gpio_cfg_reg_ker, GPIO_CTL_REG_KER);
+ efx_reado(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL);
+ EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OEN, 1);
+ EFX_SET_OWORD_FIELD(gpio_cfg_reg_ker, FRF_AB_GPIO1_OUT, 1);
+ efx_writeo(efx, &gpio_cfg_reg_ker, FR_AB_GPIO_CTL);
/* Initiate SRAM reset */
EFX_POPULATE_OWORD_2(srm_cfg_reg_ker,
- SRAM_OOB_BT_INIT_EN, 1,
- SRM_NUM_BANKS_AND_BANK_SIZE, 0);
- falcon_write(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER);
+ FRF_AZ_SRM_INIT_EN, 1,
+ FRF_AZ_SRM_NB_SZ, 0);
+ efx_writeo(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG);
/* Wait for SRAM reset to complete */
count = 0;
@@ -2626,8 +1213,8 @@ static int falcon_reset_sram(struct efx_nic *efx)
schedule_timeout_uninterruptible(HZ / 50);
/* Check for reset complete */
- falcon_read(efx, &srm_cfg_reg_ker, SRM_CFG_REG_KER);
- if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, SRAM_OOB_BT_INIT_EN)) {
+ efx_reado(efx, &srm_cfg_reg_ker, FR_AZ_SRM_CFG);
+ if (!EFX_OWORD_FIELD(srm_cfg_reg_ker, FRF_AZ_SRM_INIT_EN)) {
EFX_LOG(efx, "SRAM reset complete\n");
return 0;
@@ -2663,8 +1250,6 @@ static int falcon_spi_device_init(struct efx_nic *efx,
spi_device->block_size =
1 << SPI_DEV_TYPE_FIELD(device_type,
SPI_DEV_TYPE_BLOCK_SIZE);
-
- spi_device->efx = efx;
} else {
spi_device = NULL;
}
@@ -2674,7 +1259,6 @@ static int falcon_spi_device_init(struct efx_nic *efx,
return 0;
}
-
static void falcon_remove_spi_devices(struct efx_nic *efx)
{
kfree(efx->spi_eeprom);
@@ -2712,16 +1296,16 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
board_rev = le16_to_cpu(v2->board_revision);
if (le16_to_cpu(nvconfig->board_struct_ver) >= 3) {
- __le32 fl = v3->spi_device_type[EE_SPI_FLASH];
- __le32 ee = v3->spi_device_type[EE_SPI_EEPROM];
- rc = falcon_spi_device_init(efx, &efx->spi_flash,
- EE_SPI_FLASH,
- le32_to_cpu(fl));
+ rc = falcon_spi_device_init(
+ efx, &efx->spi_flash, FFE_AB_SPI_DEVICE_FLASH,
+ le32_to_cpu(v3->spi_device_type
+ [FFE_AB_SPI_DEVICE_FLASH]));
if (rc)
goto fail2;
- rc = falcon_spi_device_init(efx, &efx->spi_eeprom,
- EE_SPI_EEPROM,
- le32_to_cpu(ee));
+ rc = falcon_spi_device_init(
+ efx, &efx->spi_eeprom, FFE_AB_SPI_DEVICE_EEPROM,
+ le32_to_cpu(v3->spi_device_type
+ [FFE_AB_SPI_DEVICE_EEPROM]));
if (rc)
goto fail2;
}
@@ -2732,7 +1316,7 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
EFX_LOG(efx, "PHY is %d phy_id %d\n", efx->phy_type, efx->mdio.prtad);
- efx_set_board_info(efx, board_rev);
+ falcon_probe_board(efx, board_rev);
kfree(nvconfig);
return 0;
@@ -2744,89 +1328,49 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
return rc;
}
-/* Probe the NIC variant (revision, ASIC vs FPGA, function count, port
- * count, port speed). Set workaround and feature flags accordingly.
- */
-static int falcon_probe_nic_variant(struct efx_nic *efx)
-{
- efx_oword_t altera_build;
- efx_oword_t nic_stat;
-
- falcon_read(efx, &altera_build, ALTERA_BUILD_REG_KER);
- if (EFX_OWORD_FIELD(altera_build, VER_ALL)) {
- EFX_ERR(efx, "Falcon FPGA not supported\n");
- return -ENODEV;
- }
-
- falcon_read(efx, &nic_stat, NIC_STAT_REG);
-
- switch (falcon_rev(efx)) {
- case FALCON_REV_A0:
- case 0xff:
- EFX_ERR(efx, "Falcon rev A0 not supported\n");
- return -ENODEV;
-
- case FALCON_REV_A1:
- if (EFX_OWORD_FIELD(nic_stat, STRAP_PCIE) == 0) {
- EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n");
- return -ENODEV;
- }
- break;
-
- case FALCON_REV_B0:
- break;
-
- default:
- EFX_ERR(efx, "Unknown Falcon rev %d\n", falcon_rev(efx));
- return -ENODEV;
- }
-
- /* Initial assumed speed */
- efx->link_speed = EFX_OWORD_FIELD(nic_stat, STRAP_10G) ? 10000 : 1000;
-
- return 0;
-}
-
/* Probe all SPI devices on the NIC */
static void falcon_probe_spi_devices(struct efx_nic *efx)
{
efx_oword_t nic_stat, gpio_ctl, ee_vpd_cfg;
int boot_dev;
- falcon_read(efx, &gpio_ctl, GPIO_CTL_REG_KER);
- falcon_read(efx, &nic_stat, NIC_STAT_REG);
- falcon_read(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
+ efx_reado(efx, &gpio_ctl, FR_AB_GPIO_CTL);
+ efx_reado(efx, &nic_stat, FR_AB_NIC_STAT);
+ efx_reado(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0);
- if (EFX_OWORD_FIELD(gpio_ctl, BOOTED_USING_NVDEVICE)) {
- boot_dev = (EFX_OWORD_FIELD(nic_stat, SF_PRST) ?
- EE_SPI_FLASH : EE_SPI_EEPROM);
+ if (EFX_OWORD_FIELD(gpio_ctl, FRF_AB_GPIO3_PWRUP_VALUE)) {
+ boot_dev = (EFX_OWORD_FIELD(nic_stat, FRF_AB_SF_PRST) ?
+ FFE_AB_SPI_DEVICE_FLASH : FFE_AB_SPI_DEVICE_EEPROM);
EFX_LOG(efx, "Booted from %s\n",
- boot_dev == EE_SPI_FLASH ? "flash" : "EEPROM");
+ boot_dev == FFE_AB_SPI_DEVICE_FLASH ? "flash" : "EEPROM");
} else {
/* Disable VPD and set clock dividers to safe
* values for initial programming. */
boot_dev = -1;
EFX_LOG(efx, "Booted from internal ASIC settings;"
" setting SPI config\n");
- EFX_POPULATE_OWORD_3(ee_vpd_cfg, EE_VPD_EN, 0,
+ EFX_POPULATE_OWORD_3(ee_vpd_cfg, FRF_AB_EE_VPD_EN, 0,
/* 125 MHz / 7 ~= 20 MHz */
- EE_SF_CLOCK_DIV, 7,
+ FRF_AB_EE_SF_CLOCK_DIV, 7,
/* 125 MHz / 63 ~= 2 MHz */
- EE_EE_CLOCK_DIV, 63);
- falcon_write(efx, &ee_vpd_cfg, EE_VPD_CFG_REG_KER);
+ FRF_AB_EE_EE_CLOCK_DIV, 63);
+ efx_writeo(efx, &ee_vpd_cfg, FR_AB_EE_VPD_CFG0);
}
- if (boot_dev == EE_SPI_FLASH)
- falcon_spi_device_init(efx, &efx->spi_flash, EE_SPI_FLASH,
+ if (boot_dev == FFE_AB_SPI_DEVICE_FLASH)
+ falcon_spi_device_init(efx, &efx->spi_flash,
+ FFE_AB_SPI_DEVICE_FLASH,
default_flash_type);
- if (boot_dev == EE_SPI_EEPROM)
- falcon_spi_device_init(efx, &efx->spi_eeprom, EE_SPI_EEPROM,
+ if (boot_dev == FFE_AB_SPI_DEVICE_EEPROM)
+ falcon_spi_device_init(efx, &efx->spi_eeprom,
+ FFE_AB_SPI_DEVICE_EEPROM,
large_eeprom_type);
}
-int falcon_probe_nic(struct efx_nic *efx)
+static int falcon_probe_nic(struct efx_nic *efx)
{
struct falcon_nic_data *nic_data;
+ struct falcon_board *board;
int rc;
/* Allocate storage for hardware specific data */
@@ -2835,15 +1379,33 @@ int falcon_probe_nic(struct efx_nic *efx)
return -ENOMEM;
efx->nic_data = nic_data;
- /* Determine number of ports etc. */
- rc = falcon_probe_nic_variant(efx);
- if (rc)
+ rc = -ENODEV;
+
+ if (efx_nic_fpga_ver(efx) != 0) {
+ EFX_ERR(efx, "Falcon FPGA not supported\n");
goto fail1;
+ }
- /* Probe secondary function if expected */
- if (FALCON_IS_DUAL_FUNC(efx)) {
- struct pci_dev *dev = pci_dev_get(efx->pci_dev);
+ if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) {
+ efx_oword_t nic_stat;
+ struct pci_dev *dev;
+ u8 pci_rev = efx->pci_dev->revision;
+
+ if ((pci_rev == 0xff) || (pci_rev == 0)) {
+ EFX_ERR(efx, "Falcon rev A0 not supported\n");
+ goto fail1;
+ }
+ efx_reado(efx, &nic_stat, FR_AB_NIC_STAT);
+ if (EFX_OWORD_FIELD(nic_stat, FRF_AB_STRAP_10G) == 0) {
+ EFX_ERR(efx, "Falcon rev A1 1G not supported\n");
+ goto fail1;
+ }
+ if (EFX_OWORD_FIELD(nic_stat, FRF_AA_STRAP_PCIE) == 0) {
+ EFX_ERR(efx, "Falcon rev A1 PCI-X not supported\n");
+ goto fail1;
+ }
+ dev = pci_dev_get(efx->pci_dev);
while ((dev = pci_get_device(EFX_VENDID_SFC, FALCON_A_S_DEVID,
dev))) {
if (dev->bus == efx->pci_dev->bus &&
@@ -2867,7 +1429,7 @@ int falcon_probe_nic(struct efx_nic *efx)
}
/* Allocate memory for INT_KER */
- rc = falcon_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t));
+ rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t));
if (rc)
goto fail4;
BUG_ON(efx->irq_status.dma_addr & 0x0f);
@@ -2884,21 +1446,36 @@ int falcon_probe_nic(struct efx_nic *efx)
goto fail5;
/* Initialise I2C adapter */
- efx->i2c_adap.owner = THIS_MODULE;
- nic_data->i2c_data = falcon_i2c_bit_operations;
- nic_data->i2c_data.data = efx;
- efx->i2c_adap.algo_data = &nic_data->i2c_data;
- efx->i2c_adap.dev.parent = &efx->pci_dev->dev;
- strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name));
- rc = i2c_bit_add_bus(&efx->i2c_adap);
+ board = falcon_board(efx);
+ board->i2c_adap.owner = THIS_MODULE;
+ board->i2c_data = falcon_i2c_bit_operations;
+ board->i2c_data.data = efx;
+ board->i2c_adap.algo_data = &board->i2c_data;
+ board->i2c_adap.dev.parent = &efx->pci_dev->dev;
+ strlcpy(board->i2c_adap.name, "SFC4000 GPIO",
+ sizeof(board->i2c_adap.name));
+ rc = i2c_bit_add_bus(&board->i2c_adap);
if (rc)
goto fail5;
+ rc = falcon_board(efx)->type->init(efx);
+ if (rc) {
+ EFX_ERR(efx, "failed to initialise board\n");
+ goto fail6;
+ }
+
+ nic_data->stats_disable_count = 1;
+ setup_timer(&nic_data->stats_timer, &falcon_stats_timer_func,
+ (unsigned long)efx);
+
return 0;
+ fail6:
+ BUG_ON(i2c_del_adapter(&board->i2c_adap));
+ memset(&board->i2c_adap, 0, sizeof(board->i2c_adap));
fail5:
falcon_remove_spi_devices(efx);
- falcon_free_buffer(efx, &efx->irq_status);
+ efx_nic_free_buffer(efx, &efx->irq_status);
fail4:
fail3:
if (nic_data->pci_dev2) {
@@ -2911,166 +1488,147 @@ int falcon_probe_nic(struct efx_nic *efx)
return rc;
}
+static void falcon_init_rx_cfg(struct efx_nic *efx)
+{
+ /* Prior to Siena the RX DMA engine will split each frame at
+ * intervals of RX_USR_BUF_SIZE (32-byte units). We set it to
+ * be so large that that never happens. */
+ const unsigned huge_buf_size = (3 * 4096) >> 5;
+ /* RX control FIFO thresholds (32 entries) */
+ const unsigned ctrl_xon_thr = 20;
+ const unsigned ctrl_xoff_thr = 25;
+ /* RX data FIFO thresholds (256-byte units; size varies) */
+ int data_xon_thr = efx_nic_rx_xon_thresh >> 8;
+ int data_xoff_thr = efx_nic_rx_xoff_thresh >> 8;
+ efx_oword_t reg;
+
+ efx_reado(efx, &reg, FR_AZ_RX_CFG);
+ if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) {
+ /* Data FIFO size is 5.5K */
+ if (data_xon_thr < 0)
+ data_xon_thr = 512 >> 8;
+ if (data_xoff_thr < 0)
+ data_xoff_thr = 2048 >> 8;
+ EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_DESC_PUSH_EN, 0);
+ EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_USR_BUF_SIZE,
+ huge_buf_size);
+ EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, data_xon_thr);
+ EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, data_xoff_thr);
+ EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_TX_TH, ctrl_xon_thr);
+ EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_TX_TH, ctrl_xoff_thr);
+ } else {
+ /* Data FIFO size is 80K; register fields moved */
+ if (data_xon_thr < 0)
+ data_xon_thr = 27648 >> 8; /* ~3*max MTU */
+ if (data_xoff_thr < 0)
+ data_xoff_thr = 54272 >> 8; /* ~80Kb - 3*max MTU */
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_DESC_PUSH_EN, 0);
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_USR_BUF_SIZE,
+ huge_buf_size);
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, data_xon_thr);
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, data_xoff_thr);
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_TX_TH, ctrl_xon_thr);
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_TX_TH, ctrl_xoff_thr);
+ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1);
+ }
+ /* Always enable XOFF signal from RX FIFO. We enable
+ * or disable transmission of pause frames at the MAC. */
+ EFX_SET_OWORD_FIELD(reg, FRF_AZ_RX_XOFF_MAC_EN, 1);
+ efx_writeo(efx, &reg, FR_AZ_RX_CFG);
+}
+
/* This call performs hardware-specific global initialisation, such as
* defining the descriptor cache sizes and number of RSS channels.
* It does not set up any buffers, descriptor rings or event queues.
*/
-int falcon_init_nic(struct efx_nic *efx)
+static int falcon_init_nic(struct efx_nic *efx)
{
efx_oword_t temp;
- unsigned thresh;
int rc;
/* Use on-chip SRAM */
- falcon_read(efx, &temp, NIC_STAT_REG);
- EFX_SET_OWORD_FIELD(temp, ONCHIP_SRAM, 1);
- falcon_write(efx, &temp, NIC_STAT_REG);
+ efx_reado(efx, &temp, FR_AB_NIC_STAT);
+ EFX_SET_OWORD_FIELD(temp, FRF_AB_ONCHIP_SRAM, 1);
+ efx_writeo(efx, &temp, FR_AB_NIC_STAT);
/* Set the source of the GMAC clock */
- if (falcon_rev(efx) == FALCON_REV_B0) {
- falcon_read(efx, &temp, GPIO_CTL_REG_KER);
- EFX_SET_OWORD_FIELD(temp, GPIO_USE_NIC_CLK, true);
- falcon_write(efx, &temp, GPIO_CTL_REG_KER);
+ if (efx_nic_rev(efx) == EFX_REV_FALCON_B0) {
+ efx_reado(efx, &temp, FR_AB_GPIO_CTL);
+ EFX_SET_OWORD_FIELD(temp, FRF_AB_USE_NIC_CLK, true);
+ efx_writeo(efx, &temp, FR_AB_GPIO_CTL);
}
- /* Set buffer table mode */
- EFX_POPULATE_OWORD_1(temp, BUF_TBL_MODE, BUF_TBL_MODE_FULL);
- falcon_write(efx, &temp, BUF_TBL_CFG_REG_KER);
+ /* Select the correct MAC */
+ falcon_clock_mac(efx);
rc = falcon_reset_sram(efx);
if (rc)
return rc;
- /* Set positions of descriptor caches in SRAM. */
- EFX_POPULATE_OWORD_1(temp, SRM_TX_DC_BASE_ADR, TX_DC_BASE / 8);
- falcon_write(efx, &temp, SRM_TX_DC_CFG_REG_KER);
- EFX_POPULATE_OWORD_1(temp, SRM_RX_DC_BASE_ADR, RX_DC_BASE / 8);
- falcon_write(efx, &temp, SRM_RX_DC_CFG_REG_KER);
-
- /* Set TX descriptor cache size. */
- BUILD_BUG_ON(TX_DC_ENTRIES != (16 << TX_DC_ENTRIES_ORDER));
- EFX_POPULATE_OWORD_1(temp, TX_DC_SIZE, TX_DC_ENTRIES_ORDER);
- falcon_write(efx, &temp, TX_DC_CFG_REG_KER);
-
- /* Set RX descriptor cache size. Set low watermark to size-8, as
- * this allows most efficient prefetching.
- */
- BUILD_BUG_ON(RX_DC_ENTRIES != (16 << RX_DC_ENTRIES_ORDER));
- EFX_POPULATE_OWORD_1(temp, RX_DC_SIZE, RX_DC_ENTRIES_ORDER);
- falcon_write(efx, &temp, RX_DC_CFG_REG_KER);
- EFX_POPULATE_OWORD_1(temp, RX_DC_PF_LWM, RX_DC_ENTRIES - 8);
- falcon_write(efx, &temp, RX_DC_PF_WM_REG_KER);
-
/* Clear the parity enables on the TX data fifos as
* they produce false parity errors because of timing issues
*/
if (EFX_WORKAROUND_5129(efx)) {
- falcon_read(efx, &temp, SPARE_REG_KER);
- EFX_SET_OWORD_FIELD(temp, MEM_PERR_EN_TX_DATA, 0);
- falcon_write(efx, &temp, SPARE_REG_KER);
+ efx_reado(efx, &temp, FR_AZ_CSR_SPARE);
+ EFX_SET_OWORD_FIELD(temp, FRF_AB_MEM_PERR_EN_TX_DATA, 0);
+ efx_writeo(efx, &temp, FR_AZ_CSR_SPARE);
}
- /* Enable all the genuinely fatal interrupts. (They are still
- * masked by the overall interrupt mask, controlled by
- * falcon_interrupts()).
- *
- * Note: All other fatal interrupts are enabled
- */
- EFX_POPULATE_OWORD_3(temp,
- ILL_ADR_INT_KER_EN, 1,
- RBUF_OWN_INT_KER_EN, 1,
- TBUF_OWN_INT_KER_EN, 1);
- EFX_INVERT_OWORD(temp);
- falcon_write(efx, &temp, FATAL_INTR_REG_KER);
-
if (EFX_WORKAROUND_7244(efx)) {
- falcon_read(efx, &temp, RX_FILTER_CTL_REG);
- EFX_SET_OWORD_FIELD(temp, UDP_FULL_SRCH_LIMIT, 8);
- EFX_SET_OWORD_FIELD(temp, UDP_WILD_SRCH_LIMIT, 8);
- EFX_SET_OWORD_FIELD(temp, TCP_FULL_SRCH_LIMIT, 8);
- EFX_SET_OWORD_FIELD(temp, TCP_WILD_SRCH_LIMIT, 8);
- falcon_write(efx, &temp, RX_FILTER_CTL_REG);
+ efx_reado(efx, &temp, FR_BZ_RX_FILTER_CTL);
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_FULL_SRCH_LIMIT, 8);
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_UDP_WILD_SRCH_LIMIT, 8);
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_FULL_SRCH_LIMIT, 8);
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_TCP_WILD_SRCH_LIMIT, 8);
+ efx_writeo(efx, &temp, FR_BZ_RX_FILTER_CTL);
}
- falcon_setup_rss_indir_table(efx);
-
+ /* XXX This is documented only for Falcon A0/A1 */
/* Setup RX. Wait for descriptor is broken and must
* be disabled. RXDP recovery shouldn't be needed, but is.
*/
- falcon_read(efx, &temp, RX_SELF_RST_REG_KER);
- EFX_SET_OWORD_FIELD(temp, RX_NODESC_WAIT_DIS, 1);
- EFX_SET_OWORD_FIELD(temp, RX_RECOVERY_EN, 1);
+ efx_reado(efx, &temp, FR_AA_RX_SELF_RST);
+ EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_NODESC_WAIT_DIS, 1);
+ EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_SELF_RST_EN, 1);
if (EFX_WORKAROUND_5583(efx))
- EFX_SET_OWORD_FIELD(temp, RX_ISCSI_DIS, 1);
- falcon_write(efx, &temp, RX_SELF_RST_REG_KER);
-
- /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be
- * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q.
- */
- falcon_read(efx, &temp, TX_CFG2_REG_KER);
- EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER, 0xfe);
- EFX_SET_OWORD_FIELD(temp, TX_RX_SPACER_EN, 1);
- EFX_SET_OWORD_FIELD(temp, TX_ONE_PKT_PER_Q, 1);
- EFX_SET_OWORD_FIELD(temp, TX_CSR_PUSH_EN, 0);
- EFX_SET_OWORD_FIELD(temp, TX_DIS_NON_IP_EV, 1);
- /* Enable SW_EV to inherit in char driver - assume harmless here */
- EFX_SET_OWORD_FIELD(temp, TX_SW_EV_EN, 1);
- /* Prefetch threshold 2 => fetch when descriptor cache half empty */
- EFX_SET_OWORD_FIELD(temp, TX_PREF_THRESHOLD, 2);
- /* Squash TX of packets of 16 bytes or less */
- if (falcon_rev(efx) >= FALCON_REV_B0 && EFX_WORKAROUND_9141(efx))
- EFX_SET_OWORD_FIELD(temp, TX_FLUSH_MIN_LEN_EN_B0, 1);
- falcon_write(efx, &temp, TX_CFG2_REG_KER);
+ EFX_SET_OWORD_FIELD(temp, FRF_AA_RX_ISCSI_DIS, 1);
+ efx_writeo(efx, &temp, FR_AA_RX_SELF_RST);
/* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16
* descriptors (which is bad).
*/
- falcon_read(efx, &temp, TX_CFG_REG_KER);
- EFX_SET_OWORD_FIELD(temp, TX_NO_EOP_DISC_EN, 0);
- falcon_write(efx, &temp, TX_CFG_REG_KER);
-
- /* RX config */
- falcon_read(efx, &temp, RX_CFG_REG_KER);
- EFX_SET_OWORD_FIELD_VER(efx, temp, RX_DESC_PUSH_EN, 0);
- if (EFX_WORKAROUND_7575(efx))
- EFX_SET_OWORD_FIELD_VER(efx, temp, RX_USR_BUF_SIZE,
- (3 * 4096) / 32);
- if (falcon_rev(efx) >= FALCON_REV_B0)
- EFX_SET_OWORD_FIELD(temp, RX_INGR_EN_B0, 1);
-
- /* RX FIFO flow control thresholds */
- thresh = ((rx_xon_thresh_bytes >= 0) ?
- rx_xon_thresh_bytes : efx->type->rx_xon_thresh);
- EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_MAC_TH, thresh / 256);
- thresh = ((rx_xoff_thresh_bytes >= 0) ?
- rx_xoff_thresh_bytes : efx->type->rx_xoff_thresh);
- EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_MAC_TH, thresh / 256);
- /* RX control FIFO thresholds [32 entries] */
- EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XON_TX_TH, 20);
- EFX_SET_OWORD_FIELD_VER(efx, temp, RX_XOFF_TX_TH, 25);
- falcon_write(efx, &temp, RX_CFG_REG_KER);
+ efx_reado(efx, &temp, FR_AZ_TX_CFG);
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0);
+ efx_writeo(efx, &temp, FR_AZ_TX_CFG);
+
+ falcon_init_rx_cfg(efx);
/* Set destination of both TX and RX Flush events */
- if (falcon_rev(efx) >= FALCON_REV_B0) {
- EFX_POPULATE_OWORD_1(temp, FLS_EVQ_ID, 0);
- falcon_write(efx, &temp, DP_CTRL_REG);
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) {
+ EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0);
+ efx_writeo(efx, &temp, FR_BZ_DP_CTRL);
}
+ efx_nic_init_common(efx);
+
return 0;
}
-void falcon_remove_nic(struct efx_nic *efx)
+static void falcon_remove_nic(struct efx_nic *efx)
{
struct falcon_nic_data *nic_data = efx->nic_data;
+ struct falcon_board *board = falcon_board(efx);
int rc;
+ board->type->fini(efx);
+
/* Remove I2C adapter and clear it in preparation for a retry */
- rc = i2c_del_adapter(&efx->i2c_adap);
+ rc = i2c_del_adapter(&board->i2c_adap);
BUG_ON(rc);
- memset(&efx->i2c_adap, 0, sizeof(efx->i2c_adap));
+ memset(&board->i2c_adap, 0, sizeof(board->i2c_adap));
falcon_remove_spi_devices(efx);
- falcon_free_buffer(efx, &efx->irq_status);
+ efx_nic_free_buffer(efx, &efx->irq_status);
falcon_reset_hw(efx, RESET_TYPE_ALL);
@@ -3085,12 +1643,86 @@ void falcon_remove_nic(struct efx_nic *efx)
efx->nic_data = NULL;
}
-void falcon_update_nic_stats(struct efx_nic *efx)
+static void falcon_update_nic_stats(struct efx_nic *efx)
{
+ struct falcon_nic_data *nic_data = efx->nic_data;
efx_oword_t cnt;
- falcon_read(efx, &cnt, RX_NODESC_DROP_REG_KER);
- efx->n_rx_nodesc_drop_cnt += EFX_OWORD_FIELD(cnt, RX_NODESC_DROP_CNT);
+ if (nic_data->stats_disable_count)
+ return;
+
+ efx_reado(efx, &cnt, FR_AZ_RX_NODESC_DROP);
+ efx->n_rx_nodesc_drop_cnt +=
+ EFX_OWORD_FIELD(cnt, FRF_AB_RX_NODESC_DROP_CNT);
+
+ if (nic_data->stats_pending &&
+ *nic_data->stats_dma_done == FALCON_STATS_DONE) {
+ nic_data->stats_pending = false;
+ rmb(); /* read the done flag before the stats */
+ efx->mac_op->update_stats(efx);
+ }
+}
+
+void falcon_start_nic_stats(struct efx_nic *efx)
+{
+ struct falcon_nic_data *nic_data = efx->nic_data;
+
+ spin_lock_bh(&efx->stats_lock);
+ if (--nic_data->stats_disable_count == 0)
+ falcon_stats_request(efx);
+ spin_unlock_bh(&efx->stats_lock);
+}
+
+void falcon_stop_nic_stats(struct efx_nic *efx)
+{
+ struct falcon_nic_data *nic_data = efx->nic_data;
+ int i;
+
+ might_sleep();
+
+ spin_lock_bh(&efx->stats_lock);
+ ++nic_data->stats_disable_count;
+ spin_unlock_bh(&efx->stats_lock);
+
+ del_timer_sync(&nic_data->stats_timer);
+
+ /* Wait enough time for the most recent transfer to
+ * complete. */
+ for (i = 0; i < 4 && nic_data->stats_pending; i++) {
+ if (*nic_data->stats_dma_done == FALCON_STATS_DONE)
+ break;
+ msleep(1);
+ }
+
+ spin_lock_bh(&efx->stats_lock);
+ falcon_stats_complete(efx);
+ spin_unlock_bh(&efx->stats_lock);
+}
+
+static void falcon_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
+{
+ falcon_board(efx)->type->set_id_led(efx, mode);
+}
+
+/**************************************************************************
+ *
+ * Wake on LAN
+ *
+ **************************************************************************
+ */
+
+static void falcon_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol)
+{
+ wol->supported = 0;
+ wol->wolopts = 0;
+ memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+static int falcon_set_wol(struct efx_nic *efx, u32 type)
+{
+ if (type != 0)
+ return -EINVAL;
+ return 0;
}
/**************************************************************************
@@ -3100,50 +1732,91 @@ void falcon_update_nic_stats(struct efx_nic *efx)
**************************************************************************
*/
-struct efx_nic_type falcon_a_nic_type = {
- .mem_bar = 2,
+struct efx_nic_type falcon_a1_nic_type = {
+ .probe = falcon_probe_nic,
+ .remove = falcon_remove_nic,
+ .init = falcon_init_nic,
+ .fini = efx_port_dummy_op_void,
+ .monitor = falcon_monitor,
+ .reset = falcon_reset_hw,
+ .probe_port = falcon_probe_port,
+ .remove_port = falcon_remove_port,
+ .prepare_flush = falcon_prepare_flush,
+ .update_stats = falcon_update_nic_stats,
+ .start_stats = falcon_start_nic_stats,
+ .stop_stats = falcon_stop_nic_stats,
+ .set_id_led = falcon_set_id_led,
+ .push_irq_moderation = falcon_push_irq_moderation,
+ .push_multicast_hash = falcon_push_multicast_hash,
+ .reconfigure_port = falcon_reconfigure_port,
+ .get_wol = falcon_get_wol,
+ .set_wol = falcon_set_wol,
+ .resume_wol = efx_port_dummy_op_void,
+ .test_nvram = falcon_test_nvram,
+ .default_mac_ops = &falcon_xmac_operations,
+
+ .revision = EFX_REV_FALCON_A1,
.mem_map_size = 0x20000,
- .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_A1,
- .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_A1,
- .buf_tbl_base = BUF_TBL_KER_A1,
- .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_A1,
- .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_A1,
- .txd_ring_mask = FALCON_TXD_RING_MASK,
- .rxd_ring_mask = FALCON_RXD_RING_MASK,
- .evq_size = FALCON_EVQ_SIZE,
- .max_dma_mask = FALCON_DMA_MASK,
- .tx_dma_mask = FALCON_TX_DMA_MASK,
- .bug5391_mask = 0xf,
- .rx_xoff_thresh = 2048,
- .rx_xon_thresh = 512,
+ .txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER,
+ .rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER,
+ .buf_tbl_base = FR_AA_BUF_FULL_TBL_KER,
+ .evq_ptr_tbl_base = FR_AA_EVQ_PTR_TBL_KER,
+ .evq_rptr_tbl_base = FR_AA_EVQ_RPTR_KER,
+ .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH),
.rx_buffer_padding = 0x24,
.max_interrupt_mode = EFX_INT_MODE_MSI,
.phys_addr_channels = 4,
+ .tx_dc_base = 0x130000,
+ .rx_dc_base = 0x100000,
+ .offload_features = NETIF_F_IP_CSUM,
+ .reset_world_flags = ETH_RESET_IRQ,
};
-struct efx_nic_type falcon_b_nic_type = {
- .mem_bar = 2,
+struct efx_nic_type falcon_b0_nic_type = {
+ .probe = falcon_probe_nic,
+ .remove = falcon_remove_nic,
+ .init = falcon_init_nic,
+ .fini = efx_port_dummy_op_void,
+ .monitor = falcon_monitor,
+ .reset = falcon_reset_hw,
+ .probe_port = falcon_probe_port,
+ .remove_port = falcon_remove_port,
+ .prepare_flush = falcon_prepare_flush,
+ .update_stats = falcon_update_nic_stats,
+ .start_stats = falcon_start_nic_stats,
+ .stop_stats = falcon_stop_nic_stats,
+ .set_id_led = falcon_set_id_led,
+ .push_irq_moderation = falcon_push_irq_moderation,
+ .push_multicast_hash = falcon_push_multicast_hash,
+ .reconfigure_port = falcon_reconfigure_port,
+ .get_wol = falcon_get_wol,
+ .set_wol = falcon_set_wol,
+ .resume_wol = efx_port_dummy_op_void,
+ .test_registers = falcon_b0_test_registers,
+ .test_nvram = falcon_test_nvram,
+ .default_mac_ops = &falcon_xmac_operations,
+
+ .revision = EFX_REV_FALCON_B0,
/* Map everything up to and including the RSS indirection
* table. Don't map MSI-X table, MSI-X PBA since Linux
* requires that they not be mapped. */
- .mem_map_size = RX_RSS_INDIR_TBL_B0 + 0x800,
- .txd_ptr_tbl_base = TX_DESC_PTR_TBL_KER_B0,
- .rxd_ptr_tbl_base = RX_DESC_PTR_TBL_KER_B0,
- .buf_tbl_base = BUF_TBL_KER_B0,
- .evq_ptr_tbl_base = EVQ_PTR_TBL_KER_B0,
- .evq_rptr_tbl_base = EVQ_RPTR_REG_KER_B0,
- .txd_ring_mask = FALCON_TXD_RING_MASK,
- .rxd_ring_mask = FALCON_RXD_RING_MASK,
- .evq_size = FALCON_EVQ_SIZE,
- .max_dma_mask = FALCON_DMA_MASK,
- .tx_dma_mask = FALCON_TX_DMA_MASK,
- .bug5391_mask = 0,
- .rx_xoff_thresh = 54272, /* ~80Kb - 3*max MTU */
- .rx_xon_thresh = 27648, /* ~3*max MTU */
+ .mem_map_size = (FR_BZ_RX_INDIRECTION_TBL +
+ FR_BZ_RX_INDIRECTION_TBL_STEP *
+ FR_BZ_RX_INDIRECTION_TBL_ROWS),
+ .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
+ .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL,
+ .buf_tbl_base = FR_BZ_BUF_FULL_TBL,
+ .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL,
+ .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR,
+ .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH),
.rx_buffer_padding = 0,
.max_interrupt_mode = EFX_INT_MODE_MSIX,
.phys_addr_channels = 32, /* Hardware limit is 64, but the legacy
* interrupt handler only supports 32
* channels */
+ .tx_dc_base = 0x130000,
+ .rx_dc_base = 0x100000,
+ .offload_features = NETIF_F_IP_CSUM,
+ .reset_world_flags = ETH_RESET_IRQ,
};
diff --git a/drivers/net/sfc/falcon.h b/drivers/net/sfc/falcon.h
deleted file mode 100644
index 77f2e0db7ca..00000000000
--- a/drivers/net/sfc/falcon.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_FALCON_H
-#define EFX_FALCON_H
-
-#include "net_driver.h"
-#include "efx.h"
-
-/*
- * Falcon hardware control
- */
-
-enum falcon_revision {
- FALCON_REV_A0 = 0,
- FALCON_REV_A1 = 1,
- FALCON_REV_B0 = 2,
-};
-
-static inline int falcon_rev(struct efx_nic *efx)
-{
- return efx->pci_dev->revision;
-}
-
-extern struct efx_nic_type falcon_a_nic_type;
-extern struct efx_nic_type falcon_b_nic_type;
-
-/**************************************************************************
- *
- * Externs
- *
- **************************************************************************
- */
-
-/* TX data path */
-extern int falcon_probe_tx(struct efx_tx_queue *tx_queue);
-extern void falcon_init_tx(struct efx_tx_queue *tx_queue);
-extern void falcon_fini_tx(struct efx_tx_queue *tx_queue);
-extern void falcon_remove_tx(struct efx_tx_queue *tx_queue);
-extern void falcon_push_buffers(struct efx_tx_queue *tx_queue);
-
-/* RX data path */
-extern int falcon_probe_rx(struct efx_rx_queue *rx_queue);
-extern void falcon_init_rx(struct efx_rx_queue *rx_queue);
-extern void falcon_fini_rx(struct efx_rx_queue *rx_queue);
-extern void falcon_remove_rx(struct efx_rx_queue *rx_queue);
-extern void falcon_notify_rx_desc(struct efx_rx_queue *rx_queue);
-
-/* Event data path */
-extern int falcon_probe_eventq(struct efx_channel *channel);
-extern void falcon_init_eventq(struct efx_channel *channel);
-extern void falcon_fini_eventq(struct efx_channel *channel);
-extern void falcon_remove_eventq(struct efx_channel *channel);
-extern int falcon_process_eventq(struct efx_channel *channel, int rx_quota);
-extern void falcon_eventq_read_ack(struct efx_channel *channel);
-
-/* Ports */
-extern int falcon_probe_port(struct efx_nic *efx);
-extern void falcon_remove_port(struct efx_nic *efx);
-
-/* MAC/PHY */
-extern int falcon_switch_mac(struct efx_nic *efx);
-extern bool falcon_xaui_link_ok(struct efx_nic *efx);
-extern int falcon_dma_stats(struct efx_nic *efx,
- unsigned int done_offset);
-extern void falcon_drain_tx_fifo(struct efx_nic *efx);
-extern void falcon_deconfigure_mac_wrapper(struct efx_nic *efx);
-extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx);
-
-/* Interrupts and test events */
-extern int falcon_init_interrupt(struct efx_nic *efx);
-extern void falcon_enable_interrupts(struct efx_nic *efx);
-extern void falcon_generate_test_event(struct efx_channel *channel,
- unsigned int magic);
-extern void falcon_sim_phy_event(struct efx_nic *efx);
-extern void falcon_generate_interrupt(struct efx_nic *efx);
-extern void falcon_set_int_moderation(struct efx_channel *channel);
-extern void falcon_disable_interrupts(struct efx_nic *efx);
-extern void falcon_fini_interrupt(struct efx_nic *efx);
-
-#define FALCON_IRQ_MOD_RESOLUTION 5
-
-/* Global Resources */
-extern int falcon_probe_nic(struct efx_nic *efx);
-extern int falcon_probe_resources(struct efx_nic *efx);
-extern int falcon_init_nic(struct efx_nic *efx);
-extern int falcon_flush_queues(struct efx_nic *efx);
-extern int falcon_reset_hw(struct efx_nic *efx, enum reset_type method);
-extern void falcon_remove_resources(struct efx_nic *efx);
-extern void falcon_remove_nic(struct efx_nic *efx);
-extern void falcon_update_nic_stats(struct efx_nic *efx);
-extern void falcon_set_multicast_hash(struct efx_nic *efx);
-extern int falcon_reset_xaui(struct efx_nic *efx);
-
-/* Tests */
-struct falcon_nvconfig;
-extern int falcon_read_nvram(struct efx_nic *efx,
- struct falcon_nvconfig *nvconfig);
-extern int falcon_test_registers(struct efx_nic *efx);
-
-/**************************************************************************
- *
- * Falcon MAC stats
- *
- **************************************************************************
- */
-
-#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset)
-#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH)
-
-/* Retrieve statistic from statistics block */
-#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \
- if (FALCON_STAT_WIDTH(falcon_stat) == 16) \
- (efx)->mac_stats.efx_stat += le16_to_cpu( \
- *((__force __le16 *) \
- (efx->stats_buffer.addr + \
- FALCON_STAT_OFFSET(falcon_stat)))); \
- else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \
- (efx)->mac_stats.efx_stat += le32_to_cpu( \
- *((__force __le32 *) \
- (efx->stats_buffer.addr + \
- FALCON_STAT_OFFSET(falcon_stat)))); \
- else \
- (efx)->mac_stats.efx_stat += le64_to_cpu( \
- *((__force __le64 *) \
- (efx->stats_buffer.addr + \
- FALCON_STAT_OFFSET(falcon_stat)))); \
- } while (0)
-
-#define FALCON_MAC_STATS_SIZE 0x100
-
-#define MAC_DATA_LBN 0
-#define MAC_DATA_WIDTH 32
-
-extern void falcon_generate_event(struct efx_channel *channel,
- efx_qword_t *event);
-
-#endif /* EFX_FALCON_H */
diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/sfc/falcon_boards.c
new file mode 100644
index 00000000000..bf0b96af533
--- /dev/null
+++ b/drivers/net/sfc/falcon_boards.c
@@ -0,0 +1,752 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2007-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/rtnetlink.h>
+
+#include "net_driver.h"
+#include "phy.h"
+#include "efx.h"
+#include "nic.h"
+#include "regs.h"
+#include "io.h"
+#include "workarounds.h"
+
+/* Macros for unpacking the board revision */
+/* The revision info is in host byte order. */
+#define FALCON_BOARD_TYPE(_rev) (_rev >> 8)
+#define FALCON_BOARD_MAJOR(_rev) ((_rev >> 4) & 0xf)
+#define FALCON_BOARD_MINOR(_rev) (_rev & 0xf)
+
+/* Board types */
+#define FALCON_BOARD_SFE4001 0x01
+#define FALCON_BOARD_SFE4002 0x02
+#define FALCON_BOARD_SFN4111T 0x51
+#define FALCON_BOARD_SFN4112F 0x52
+
+/*****************************************************************************
+ * Support for LM87 sensor chip used on several boards
+ */
+#define LM87_REG_ALARMS1 0x41
+#define LM87_REG_ALARMS2 0x42
+#define LM87_IN_LIMITS(nr, _min, _max) \
+ 0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min
+#define LM87_AIN_LIMITS(nr, _min, _max) \
+ 0x3B + (nr), _max, 0x1A + (nr), _min
+#define LM87_TEMP_INT_LIMITS(_min, _max) \
+ 0x39, _max, 0x3A, _min
+#define LM87_TEMP_EXT1_LIMITS(_min, _max) \
+ 0x37, _max, 0x38, _min
+
+#define LM87_ALARM_TEMP_INT 0x10
+#define LM87_ALARM_TEMP_EXT1 0x20
+
+#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE)
+
+static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
+ const u8 *reg_values)
+{
+ struct falcon_board *board = falcon_board(efx);
+ struct i2c_client *client = i2c_new_device(&board->i2c_adap, info);
+ int rc;
+
+ if (!client)
+ return -EIO;
+
+ while (*reg_values) {
+ u8 reg = *reg_values++;
+ u8 value = *reg_values++;
+ rc = i2c_smbus_write_byte_data(client, reg, value);
+ if (rc)
+ goto err;
+ }
+
+ board->hwmon_client = client;
+ return 0;
+
+err:
+ i2c_unregister_device(client);
+ return rc;
+}
+
+static void efx_fini_lm87(struct efx_nic *efx)
+{
+ i2c_unregister_device(falcon_board(efx)->hwmon_client);
+}
+
+static int efx_check_lm87(struct efx_nic *efx, unsigned mask)
+{
+ struct i2c_client *client = falcon_board(efx)->hwmon_client;
+ s32 alarms1, alarms2;
+
+ /* If link is up then do not monitor temperature */
+ if (EFX_WORKAROUND_7884(efx) && efx->link_state.up)
+ return 0;
+
+ alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1);
+ alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2);
+ if (alarms1 < 0)
+ return alarms1;
+ if (alarms2 < 0)
+ return alarms2;
+ alarms1 &= mask;
+ alarms2 &= mask >> 8;
+ if (alarms1 || alarms2) {
+ EFX_ERR(efx,
+ "LM87 detected a hardware failure (status %02x:%02x)"
+ "%s%s\n",
+ alarms1, alarms2,
+ (alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "",
+ (alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : "");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_SENSORS_LM87 */
+
+static inline int
+efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
+ const u8 *reg_values)
+{
+ return 0;
+}
+static inline void efx_fini_lm87(struct efx_nic *efx)
+{
+}
+static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SENSORS_LM87 */
+
+/*****************************************************************************
+ * Support for the SFE4001 and SFN4111T NICs.
+ *
+ * The SFE4001 does not power-up fully at reset due to its high power
+ * consumption. We control its power via a PCA9539 I/O expander.
+ * Both boards have a MAX6647 temperature monitor which we expose to
+ * the lm90 driver.
+ *
+ * This also provides minimal support for reflashing the PHY, which is
+ * initiated by resetting it with the FLASH_CFG_1 pin pulled down.
+ * On SFE4001 rev A2 and later this is connected to the 3V3X output of
+ * the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3.
+ * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually
+ * exclusive with the network device being open.
+ */
+
+/**************************************************************************
+ * Support for I2C IO Expander device on SFE4001
+ */
+#define PCA9539 0x74
+
+#define P0_IN 0x00
+#define P0_OUT 0x02
+#define P0_INVERT 0x04
+#define P0_CONFIG 0x06
+
+#define P0_EN_1V0X_LBN 0
+#define P0_EN_1V0X_WIDTH 1
+#define P0_EN_1V2_LBN 1
+#define P0_EN_1V2_WIDTH 1
+#define P0_EN_2V5_LBN 2
+#define P0_EN_2V5_WIDTH 1
+#define P0_EN_3V3X_LBN 3
+#define P0_EN_3V3X_WIDTH 1
+#define P0_EN_5V_LBN 4
+#define P0_EN_5V_WIDTH 1
+#define P0_SHORTEN_JTAG_LBN 5
+#define P0_SHORTEN_JTAG_WIDTH 1
+#define P0_X_TRST_LBN 6
+#define P0_X_TRST_WIDTH 1
+#define P0_DSP_RESET_LBN 7
+#define P0_DSP_RESET_WIDTH 1
+
+#define P1_IN 0x01
+#define P1_OUT 0x03
+#define P1_INVERT 0x05
+#define P1_CONFIG 0x07
+
+#define P1_AFE_PWD_LBN 0
+#define P1_AFE_PWD_WIDTH 1
+#define P1_DSP_PWD25_LBN 1
+#define P1_DSP_PWD25_WIDTH 1
+#define P1_RESERVED_LBN 2
+#define P1_RESERVED_WIDTH 2
+#define P1_SPARE_LBN 4
+#define P1_SPARE_WIDTH 4
+
+/* Temperature Sensor */
+#define MAX664X_REG_RSL 0x02
+#define MAX664X_REG_WLHO 0x0B
+
+static void sfe4001_poweroff(struct efx_nic *efx)
+{
+ struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client;
+ struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client;
+
+ /* Turn off all power rails and disable outputs */
+ i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff);
+ i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff);
+ i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff);
+
+ /* Clear any over-temperature alert */
+ i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
+}
+
+static int sfe4001_poweron(struct efx_nic *efx)
+{
+ struct i2c_client *ioexp_client = falcon_board(efx)->ioexp_client;
+ struct i2c_client *hwmon_client = falcon_board(efx)->hwmon_client;
+ unsigned int i, j;
+ int rc;
+ u8 out;
+
+ /* Clear any previous over-temperature alert */
+ rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
+ if (rc < 0)
+ return rc;
+
+ /* Enable port 0 and port 1 outputs on IO expander */
+ rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00);
+ if (rc)
+ return rc;
+ rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG,
+ 0xff & ~(1 << P1_SPARE_LBN));
+ if (rc)
+ goto fail_on;
+
+ /* If PHY power is on, turn it all off and wait 1 second to
+ * ensure a full reset.
+ */
+ rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT);
+ if (rc < 0)
+ goto fail_on;
+ out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) |
+ (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) |
+ (0 << P0_EN_1V0X_LBN));
+ if (rc != out) {
+ EFX_INFO(efx, "power-cycling PHY\n");
+ rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
+ if (rc)
+ goto fail_on;
+ schedule_timeout_uninterruptible(HZ);
+ }
+
+ for (i = 0; i < 20; ++i) {
+ /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */
+ out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) |
+ (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) |
+ (1 << P0_X_TRST_LBN));
+ if (efx->phy_mode & PHY_MODE_SPECIAL)
+ out |= 1 << P0_EN_3V3X_LBN;
+
+ rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
+ if (rc)
+ goto fail_on;
+ msleep(10);
+
+ /* Turn on 1V power rail */
+ out &= ~(1 << P0_EN_1V0X_LBN);
+ rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
+ if (rc)
+ goto fail_on;
+
+ EFX_INFO(efx, "waiting for DSP boot (attempt %d)...\n", i);
+
+ /* In flash config mode, DSP does not turn on AFE, so
+ * just wait 1 second.
+ */
+ if (efx->phy_mode & PHY_MODE_SPECIAL) {
+ schedule_timeout_uninterruptible(HZ);
+ return 0;
+ }
+
+ for (j = 0; j < 10; ++j) {
+ msleep(100);
+
+ /* Check DSP has asserted AFE power line */
+ rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN);
+ if (rc < 0)
+ goto fail_on;
+ if (rc & (1 << P1_AFE_PWD_LBN))
+ return 0;
+ }
+ }
+
+ EFX_INFO(efx, "timed out waiting for DSP boot\n");
+ rc = -ETIMEDOUT;
+fail_on:
+ sfe4001_poweroff(efx);
+ return rc;
+}
+
+static int sfn4111t_reset(struct efx_nic *efx)
+{
+ struct falcon_board *board = falcon_board(efx);
+ efx_oword_t reg;
+
+ /* GPIO 3 and the GPIO register are shared with I2C, so block that */
+ i2c_lock_adapter(&board->i2c_adap);
+
+ /* Pull RST_N (GPIO 2) low then let it up again, setting the
+ * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the
+ * output enables; the output levels should always be 0 (low)
+ * and we rely on external pull-ups. */
+ efx_reado(efx, &reg, FR_AB_GPIO_CTL);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO2_OEN, true);
+ efx_writeo(efx, &reg, FR_AB_GPIO_CTL);
+ msleep(1000);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO2_OEN, false);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GPIO3_OEN,
+ !!(efx->phy_mode & PHY_MODE_SPECIAL));
+ efx_writeo(efx, &reg, FR_AB_GPIO_CTL);
+ msleep(1);
+
+ i2c_unlock_adapter(&board->i2c_adap);
+
+ ssleep(1);
+ return 0;
+}
+
+static ssize_t show_phy_flash_cfg(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+ return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL));
+}
+
+static ssize_t set_phy_flash_cfg(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+ enum efx_phy_mode old_mode, new_mode;
+ int err;
+
+ rtnl_lock();
+ old_mode = efx->phy_mode;
+ if (count == 0 || *buf == '0')
+ new_mode = old_mode & ~PHY_MODE_SPECIAL;
+ else
+ new_mode = PHY_MODE_SPECIAL;
+ if (old_mode == new_mode) {
+ err = 0;
+ } else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) {
+ err = -EBUSY;
+ } else {
+ /* Reset the PHY, reconfigure the MAC and enable/disable
+ * MAC stats accordingly. */
+ efx->phy_mode = new_mode;
+ if (new_mode & PHY_MODE_SPECIAL)
+ falcon_stop_nic_stats(efx);
+ if (falcon_board(efx)->type->id == FALCON_BOARD_SFE4001)
+ err = sfe4001_poweron(efx);
+ else
+ err = sfn4111t_reset(efx);
+ if (!err)
+ err = efx_reconfigure_port(efx);
+ if (!(new_mode & PHY_MODE_SPECIAL))
+ falcon_start_nic_stats(efx);
+ }
+ rtnl_unlock();
+
+ return err ? err : count;
+}
+
+static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg);
+
+static void sfe4001_fini(struct efx_nic *efx)
+{
+ struct falcon_board *board = falcon_board(efx);
+
+ EFX_INFO(efx, "%s\n", __func__);
+
+ device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+ sfe4001_poweroff(efx);
+ i2c_unregister_device(board->ioexp_client);
+ i2c_unregister_device(board->hwmon_client);
+}
+
+static int sfe4001_check_hw(struct efx_nic *efx)
+{
+ s32 status;
+
+ /* If XAUI link is up then do not monitor */
+ if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required)
+ return 0;
+
+ /* Check the powered status of the PHY. Lack of power implies that
+ * the MAX6647 has shut down power to it, probably due to a temp.
+ * alarm. Reading the power status rather than the MAX6647 status
+ * directly because the later is read-to-clear and would thus
+ * start to power up the PHY again when polled, causing us to blip
+ * the power undesirably.
+ * We know we can read from the IO expander because we did
+ * it during power-on. Assume failure now is bad news. */
+ status = i2c_smbus_read_byte_data(falcon_board(efx)->ioexp_client, P1_IN);
+ if (status >= 0 &&
+ (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
+ return 0;
+
+ /* Use board power control, not PHY power control */
+ sfe4001_poweroff(efx);
+ efx->phy_mode = PHY_MODE_OFF;
+
+ return (status < 0) ? -EIO : -ERANGE;
+}
+
+static struct i2c_board_info sfe4001_hwmon_info = {
+ I2C_BOARD_INFO("max6647", 0x4e),
+};
+
+/* This board uses an I2C expander to provider power to the PHY, which needs to
+ * be turned on before the PHY can be used.
+ * Context: Process context, rtnl lock held
+ */
+static int sfe4001_init(struct efx_nic *efx)
+{
+ struct falcon_board *board = falcon_board(efx);
+ int rc;
+
+#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE)
+ board->hwmon_client =
+ i2c_new_device(&board->i2c_adap, &sfe4001_hwmon_info);
+#else
+ board->hwmon_client =
+ i2c_new_dummy(&board->i2c_adap, sfe4001_hwmon_info.addr);
+#endif
+ if (!board->hwmon_client)
+ return -EIO;
+
+ /* Raise board/PHY high limit from 85 to 90 degrees Celsius */
+ rc = i2c_smbus_write_byte_data(board->hwmon_client,
+ MAX664X_REG_WLHO, 90);
+ if (rc)
+ goto fail_hwmon;
+
+ board->ioexp_client = i2c_new_dummy(&board->i2c_adap, PCA9539);
+ if (!board->ioexp_client) {
+ rc = -EIO;
+ goto fail_hwmon;
+ }
+
+ if (efx->phy_mode & PHY_MODE_SPECIAL) {
+ /* PHY won't generate a 156.25 MHz clock and MAC stats fetch
+ * will fail. */
+ falcon_stop_nic_stats(efx);
+ }
+ rc = sfe4001_poweron(efx);
+ if (rc)
+ goto fail_ioexp;
+
+ rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+ if (rc)
+ goto fail_on;
+
+ EFX_INFO(efx, "PHY is powered on\n");
+ return 0;
+
+fail_on:
+ sfe4001_poweroff(efx);
+fail_ioexp:
+ i2c_unregister_device(board->ioexp_client);
+fail_hwmon:
+ i2c_unregister_device(board->hwmon_client);
+ return rc;
+}
+
+static int sfn4111t_check_hw(struct efx_nic *efx)
+{
+ s32 status;
+
+ /* If XAUI link is up then do not monitor */
+ if (EFX_WORKAROUND_7884(efx) && !efx->xmac_poll_required)
+ return 0;
+
+ /* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */
+ status = i2c_smbus_read_byte_data(falcon_board(efx)->hwmon_client,
+ MAX664X_REG_RSL);
+ if (status < 0)
+ return -EIO;
+ if (status & 0x57)
+ return -ERANGE;
+ return 0;
+}
+
+static void sfn4111t_fini(struct efx_nic *efx)
+{
+ EFX_INFO(efx, "%s\n", __func__);
+
+ device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+ i2c_unregister_device(falcon_board(efx)->hwmon_client);
+}
+
+static struct i2c_board_info sfn4111t_a0_hwmon_info = {
+ I2C_BOARD_INFO("max6647", 0x4e),
+};
+
+static struct i2c_board_info sfn4111t_r5_hwmon_info = {
+ I2C_BOARD_INFO("max6646", 0x4d),
+};
+
+static void sfn4111t_init_phy(struct efx_nic *efx)
+{
+ if (!(efx->phy_mode & PHY_MODE_SPECIAL)) {
+ if (sft9001_wait_boot(efx) != -EINVAL)
+ return;
+
+ efx->phy_mode = PHY_MODE_SPECIAL;
+ falcon_stop_nic_stats(efx);
+ }
+
+ sfn4111t_reset(efx);
+ sft9001_wait_boot(efx);
+}
+
+static int sfn4111t_init(struct efx_nic *efx)
+{
+ struct falcon_board *board = falcon_board(efx);
+ int rc;
+
+ board->hwmon_client =
+ i2c_new_device(&board->i2c_adap,
+ (board->minor < 5) ?
+ &sfn4111t_a0_hwmon_info :
+ &sfn4111t_r5_hwmon_info);
+ if (!board->hwmon_client)
+ return -EIO;
+
+ rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
+ if (rc)
+ goto fail_hwmon;
+
+ if (efx->phy_mode & PHY_MODE_SPECIAL)
+ /* PHY may not generate a 156.25 MHz clock and MAC
+ * stats fetch will fail. */
+ falcon_stop_nic_stats(efx);
+
+ return 0;
+
+fail_hwmon:
+ i2c_unregister_device(board->hwmon_client);
+ return rc;
+}
+
+/*****************************************************************************
+ * Support for the SFE4002
+ *
+ */
+static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */
+
+static const u8 sfe4002_lm87_regs[] = {
+ LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
+ LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
+ LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
+ LM87_IN_LIMITS(3, 0xb0, 0xc9), /* 5V: 4.6-5.2V */
+ LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
+ LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
+ LM87_AIN_LIMITS(0, 0xa0, 0xb2), /* AIN1: 1.66V +/- 5% */
+ LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
+ LM87_TEMP_INT_LIMITS(10, 60), /* board */
+ LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
+ 0
+};
+
+static struct i2c_board_info sfe4002_hwmon_info = {
+ I2C_BOARD_INFO("lm87", 0x2e),
+ .platform_data = &sfe4002_lm87_channel,
+};
+
+/****************************************************************************/
+/* LED allocations. Note that on rev A0 boards the schematic and the reality
+ * differ: red and green are swapped. Below is the fixed (A1) layout (there
+ * are only 3 A0 boards in existence, so no real reason to make this
+ * conditional).
+ */
+#define SFE4002_FAULT_LED (2) /* Red */
+#define SFE4002_RX_LED (0) /* Green */
+#define SFE4002_TX_LED (1) /* Amber */
+
+static void sfe4002_init_phy(struct efx_nic *efx)
+{
+ /* Set the TX and RX LEDs to reflect status and activity, and the
+ * fault LED off */
+ falcon_qt202x_set_led(efx, SFE4002_TX_LED,
+ QUAKE_LED_TXLINK | QUAKE_LED_LINK_ACTSTAT);
+ falcon_qt202x_set_led(efx, SFE4002_RX_LED,
+ QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACTSTAT);
+ falcon_qt202x_set_led(efx, SFE4002_FAULT_LED, QUAKE_LED_OFF);
+}
+
+static void sfe4002_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
+{
+ falcon_qt202x_set_led(
+ efx, SFE4002_FAULT_LED,
+ (mode == EFX_LED_ON) ? QUAKE_LED_ON : QUAKE_LED_OFF);
+}
+
+static int sfe4002_check_hw(struct efx_nic *efx)
+{
+ struct falcon_board *board = falcon_board(efx);
+
+ /* A0 board rev. 4002s report a temperature fault the whole time
+ * (bad sensor) so we mask it out. */
+ unsigned alarm_mask =
+ (board->major == 0 && board->minor == 0) ?
+ ~LM87_ALARM_TEMP_EXT1 : ~0;
+
+ return efx_check_lm87(efx, alarm_mask);
+}
+
+static int sfe4002_init(struct efx_nic *efx)
+{
+ return efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs);
+}
+
+/*****************************************************************************
+ * Support for the SFN4112F
+ *
+ */
+static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */
+
+static const u8 sfn4112f_lm87_regs[] = {
+ LM87_IN_LIMITS(0, 0x83, 0x91), /* 2.5V: 1.8V +/- 5% */
+ LM87_IN_LIMITS(1, 0x51, 0x5a), /* Vccp1: 1.2V +/- 5% */
+ LM87_IN_LIMITS(2, 0xb6, 0xca), /* 3.3V: 3.3V +/- 5% */
+ LM87_IN_LIMITS(4, 0xb0, 0xe0), /* 12V: 11-14V */
+ LM87_IN_LIMITS(5, 0x44, 0x4b), /* Vccp2: 1.0V +/- 5% */
+ LM87_AIN_LIMITS(1, 0x91, 0xa1), /* AIN2: 1.5V +/- 5% */
+ LM87_TEMP_INT_LIMITS(10, 60), /* board */
+ LM87_TEMP_EXT1_LIMITS(10, 70), /* Falcon */
+ 0
+};
+
+static struct i2c_board_info sfn4112f_hwmon_info = {
+ I2C_BOARD_INFO("lm87", 0x2e),
+ .platform_data = &sfn4112f_lm87_channel,
+};
+
+#define SFN4112F_ACT_LED 0
+#define SFN4112F_LINK_LED 1
+
+static void sfn4112f_init_phy(struct efx_nic *efx)
+{
+ falcon_qt202x_set_led(efx, SFN4112F_ACT_LED,
+ QUAKE_LED_RXLINK | QUAKE_LED_LINK_ACT);
+ falcon_qt202x_set_led(efx, SFN4112F_LINK_LED,
+ QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT);
+}
+
+static void sfn4112f_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
+{
+ int reg;
+
+ switch (mode) {
+ case EFX_LED_OFF:
+ reg = QUAKE_LED_OFF;
+ break;
+ case EFX_LED_ON:
+ reg = QUAKE_LED_ON;
+ break;
+ default:
+ reg = QUAKE_LED_RXLINK | QUAKE_LED_LINK_STAT;
+ break;
+ }
+
+ falcon_qt202x_set_led(efx, SFN4112F_LINK_LED, reg);
+}
+
+static int sfn4112f_check_hw(struct efx_nic *efx)
+{
+ /* Mask out unused sensors */
+ return efx_check_lm87(efx, ~0x48);
+}
+
+static int sfn4112f_init(struct efx_nic *efx)
+{
+ return efx_init_lm87(efx, &sfn4112f_hwmon_info, sfn4112f_lm87_regs);
+}
+
+static const struct falcon_board_type board_types[] = {
+ {
+ .id = FALCON_BOARD_SFE4001,
+ .ref_model = "SFE4001",
+ .gen_type = "10GBASE-T adapter",
+ .init = sfe4001_init,
+ .init_phy = efx_port_dummy_op_void,
+ .fini = sfe4001_fini,
+ .set_id_led = tenxpress_set_id_led,
+ .monitor = sfe4001_check_hw,
+ },
+ {
+ .id = FALCON_BOARD_SFE4002,
+ .ref_model = "SFE4002",
+ .gen_type = "XFP adapter",
+ .init = sfe4002_init,
+ .init_phy = sfe4002_init_phy,
+ .fini = efx_fini_lm87,
+ .set_id_led = sfe4002_set_id_led,
+ .monitor = sfe4002_check_hw,
+ },
+ {
+ .id = FALCON_BOARD_SFN4111T,
+ .ref_model = "SFN4111T",
+ .gen_type = "100/1000/10GBASE-T adapter",
+ .init = sfn4111t_init,
+ .init_phy = sfn4111t_init_phy,
+ .fini = sfn4111t_fini,
+ .set_id_led = tenxpress_set_id_led,
+ .monitor = sfn4111t_check_hw,
+ },
+ {
+ .id = FALCON_BOARD_SFN4112F,
+ .ref_model = "SFN4112F",
+ .gen_type = "SFP+ adapter",
+ .init = sfn4112f_init,
+ .init_phy = sfn4112f_init_phy,
+ .fini = efx_fini_lm87,
+ .set_id_led = sfn4112f_set_id_led,
+ .monitor = sfn4112f_check_hw,
+ },
+};
+
+static const struct falcon_board_type falcon_dummy_board = {
+ .init = efx_port_dummy_op_int,
+ .init_phy = efx_port_dummy_op_void,
+ .fini = efx_port_dummy_op_void,
+ .set_id_led = efx_port_dummy_op_set_id_led,
+ .monitor = efx_port_dummy_op_int,
+};
+
+void falcon_probe_board(struct efx_nic *efx, u16 revision_info)
+{
+ struct falcon_board *board = falcon_board(efx);
+ u8 type_id = FALCON_BOARD_TYPE(revision_info);
+ int i;
+
+ board->major = FALCON_BOARD_MAJOR(revision_info);
+ board->minor = FALCON_BOARD_MINOR(revision_info);
+
+ for (i = 0; i < ARRAY_SIZE(board_types); i++)
+ if (board_types[i].id == type_id)
+ board->type = &board_types[i];
+
+ if (board->type) {
+ EFX_INFO(efx, "board is %s rev %c%d\n",
+ (efx->pci_dev->subsystem_vendor == EFX_VENDID_SFC)
+ ? board->type->ref_model : board->type->gen_type,
+ 'A' + board->major, board->minor);
+ } else {
+ EFX_ERR(efx, "unknown board type %d\n", type_id);
+ board->type = &falcon_dummy_board;
+ }
+}
diff --git a/drivers/net/sfc/falcon_gmac.c b/drivers/net/sfc/falcon_gmac.c
index 8865eae20ac..7dadfcbd6ce 100644
--- a/drivers/net/sfc/falcon_gmac.c
+++ b/drivers/net/sfc/falcon_gmac.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -11,11 +11,10 @@
#include <linux/delay.h>
#include "net_driver.h"
#include "efx.h"
-#include "falcon.h"
+#include "nic.h"
#include "mac.h"
-#include "falcon_hwdefs.h"
-#include "falcon_io.h"
-#include "gmii.h"
+#include "regs.h"
+#include "io.h"
/**************************************************************************
*
@@ -23,106 +22,109 @@
*
*************************************************************************/
-static void falcon_reconfigure_gmac(struct efx_nic *efx)
+static int falcon_reconfigure_gmac(struct efx_nic *efx)
{
+ struct efx_link_state *link_state = &efx->link_state;
bool loopback, tx_fc, rx_fc, bytemode;
int if_mode;
unsigned int max_frame_len;
efx_oword_t reg;
/* Configuration register 1 */
- tx_fc = (efx->link_fc & EFX_FC_TX) || !efx->link_fd;
- rx_fc = !!(efx->link_fc & EFX_FC_RX);
+ tx_fc = (link_state->fc & EFX_FC_TX) || !link_state->fd;
+ rx_fc = !!(link_state->fc & EFX_FC_RX);
loopback = (efx->loopback_mode == LOOPBACK_GMAC);
- bytemode = (efx->link_speed == 1000);
+ bytemode = (link_state->speed == 1000);
EFX_POPULATE_OWORD_5(reg,
- GM_LOOP, loopback,
- GM_TX_EN, 1,
- GM_TX_FC_EN, tx_fc,
- GM_RX_EN, 1,
- GM_RX_FC_EN, rx_fc);
- falcon_write(efx, &reg, GM_CFG1_REG);
+ FRF_AB_GM_LOOP, loopback,
+ FRF_AB_GM_TX_EN, 1,
+ FRF_AB_GM_TX_FC_EN, tx_fc,
+ FRF_AB_GM_RX_EN, 1,
+ FRF_AB_GM_RX_FC_EN, rx_fc);
+ efx_writeo(efx, &reg, FR_AB_GM_CFG1);
udelay(10);
/* Configuration register 2 */
if_mode = (bytemode) ? 2 : 1;
EFX_POPULATE_OWORD_5(reg,
- GM_IF_MODE, if_mode,
- GM_PAD_CRC_EN, 1,
- GM_LEN_CHK, 1,
- GM_FD, efx->link_fd,
- GM_PAMBL_LEN, 0x7/*datasheet recommended */);
+ FRF_AB_GM_IF_MODE, if_mode,
+ FRF_AB_GM_PAD_CRC_EN, 1,
+ FRF_AB_GM_LEN_CHK, 1,
+ FRF_AB_GM_FD, link_state->fd,
+ FRF_AB_GM_PAMBL_LEN, 0x7/*datasheet recommended */);
- falcon_write(efx, &reg, GM_CFG2_REG);
+ efx_writeo(efx, &reg, FR_AB_GM_CFG2);
udelay(10);
/* Max frame len register */
max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu);
- EFX_POPULATE_OWORD_1(reg, GM_MAX_FLEN, max_frame_len);
- falcon_write(efx, &reg, GM_MAX_FLEN_REG);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_GM_MAX_FLEN, max_frame_len);
+ efx_writeo(efx, &reg, FR_AB_GM_MAX_FLEN);
udelay(10);
/* FIFO configuration register 0 */
EFX_POPULATE_OWORD_5(reg,
- GMF_FTFENREQ, 1,
- GMF_STFENREQ, 1,
- GMF_FRFENREQ, 1,
- GMF_SRFENREQ, 1,
- GMF_WTMENREQ, 1);
- falcon_write(efx, &reg, GMF_CFG0_REG);
+ FRF_AB_GMF_FTFENREQ, 1,
+ FRF_AB_GMF_STFENREQ, 1,
+ FRF_AB_GMF_FRFENREQ, 1,
+ FRF_AB_GMF_SRFENREQ, 1,
+ FRF_AB_GMF_WTMENREQ, 1);
+ efx_writeo(efx, &reg, FR_AB_GMF_CFG0);
udelay(10);
/* FIFO configuration register 1 */
EFX_POPULATE_OWORD_2(reg,
- GMF_CFGFRTH, 0x12,
- GMF_CFGXOFFRTX, 0xffff);
- falcon_write(efx, &reg, GMF_CFG1_REG);
+ FRF_AB_GMF_CFGFRTH, 0x12,
+ FRF_AB_GMF_CFGXOFFRTX, 0xffff);
+ efx_writeo(efx, &reg, FR_AB_GMF_CFG1);
udelay(10);
/* FIFO configuration register 2 */
EFX_POPULATE_OWORD_2(reg,
- GMF_CFGHWM, 0x3f,
- GMF_CFGLWM, 0xa);
- falcon_write(efx, &reg, GMF_CFG2_REG);
+ FRF_AB_GMF_CFGHWM, 0x3f,
+ FRF_AB_GMF_CFGLWM, 0xa);
+ efx_writeo(efx, &reg, FR_AB_GMF_CFG2);
udelay(10);
/* FIFO configuration register 3 */
EFX_POPULATE_OWORD_2(reg,
- GMF_CFGHWMFT, 0x1c,
- GMF_CFGFTTH, 0x08);
- falcon_write(efx, &reg, GMF_CFG3_REG);
+ FRF_AB_GMF_CFGHWMFT, 0x1c,
+ FRF_AB_GMF_CFGFTTH, 0x08);
+ efx_writeo(efx, &reg, FR_AB_GMF_CFG3);
udelay(10);
/* FIFO configuration register 4 */
- EFX_POPULATE_OWORD_1(reg, GMF_HSTFLTRFRM_PAUSE, 1);
- falcon_write(efx, &reg, GMF_CFG4_REG);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_GMF_HSTFLTRFRM_PAUSE, 1);
+ efx_writeo(efx, &reg, FR_AB_GMF_CFG4);
udelay(10);
/* FIFO configuration register 5 */
- falcon_read(efx, &reg, GMF_CFG5_REG);
- EFX_SET_OWORD_FIELD(reg, GMF_CFGBYTMODE, bytemode);
- EFX_SET_OWORD_FIELD(reg, GMF_CFGHDPLX, !efx->link_fd);
- EFX_SET_OWORD_FIELD(reg, GMF_HSTDRPLT64, !efx->link_fd);
- EFX_SET_OWORD_FIELD(reg, GMF_HSTFLTRFRMDC_PAUSE, 0);
- falcon_write(efx, &reg, GMF_CFG5_REG);
+ efx_reado(efx, &reg, FR_AB_GMF_CFG5);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_CFGBYTMODE, bytemode);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_CFGHDPLX, !link_state->fd);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_HSTDRPLT64, !link_state->fd);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_GMF_HSTFLTRFRMDC_PAUSE, 0);
+ efx_writeo(efx, &reg, FR_AB_GMF_CFG5);
udelay(10);
/* MAC address */
EFX_POPULATE_OWORD_4(reg,
- GM_HWADDR_5, efx->net_dev->dev_addr[5],
- GM_HWADDR_4, efx->net_dev->dev_addr[4],
- GM_HWADDR_3, efx->net_dev->dev_addr[3],
- GM_HWADDR_2, efx->net_dev->dev_addr[2]);
- falcon_write(efx, &reg, GM_ADR1_REG);
+ FRF_AB_GM_ADR_B0, efx->net_dev->dev_addr[5],
+ FRF_AB_GM_ADR_B1, efx->net_dev->dev_addr[4],
+ FRF_AB_GM_ADR_B2, efx->net_dev->dev_addr[3],
+ FRF_AB_GM_ADR_B3, efx->net_dev->dev_addr[2]);
+ efx_writeo(efx, &reg, FR_AB_GM_ADR1);
udelay(10);
EFX_POPULATE_OWORD_2(reg,
- GM_HWADDR_1, efx->net_dev->dev_addr[1],
- GM_HWADDR_0, efx->net_dev->dev_addr[0]);
- falcon_write(efx, &reg, GM_ADR2_REG);
+ FRF_AB_GM_ADR_B4, efx->net_dev->dev_addr[1],
+ FRF_AB_GM_ADR_B5, efx->net_dev->dev_addr[0]);
+ efx_writeo(efx, &reg, FR_AB_GM_ADR2);
udelay(10);
falcon_reconfigure_mac_wrapper(efx);
+
+ return 0;
}
static void falcon_update_stats_gmac(struct efx_nic *efx)
@@ -130,11 +132,6 @@ static void falcon_update_stats_gmac(struct efx_nic *efx)
struct efx_mac_stats *mac_stats = &efx->mac_stats;
unsigned long old_rx_pause, old_tx_pause;
unsigned long new_rx_pause, new_tx_pause;
- int rc;
-
- rc = falcon_dma_stats(efx, GDmaDone_offset);
- if (rc)
- return;
/* Pause frames are erroneously counted as errors (SFC bug 3269) */
old_rx_pause = mac_stats->rx_pause;
@@ -221,9 +218,13 @@ static void falcon_update_stats_gmac(struct efx_nic *efx)
mac_stats->rx_lt64 = mac_stats->rx_good_lt64 + mac_stats->rx_bad_lt64;
}
+static bool falcon_gmac_check_fault(struct efx_nic *efx)
+{
+ return false;
+}
+
struct efx_mac_operations falcon_gmac_operations = {
.reconfigure = falcon_reconfigure_gmac,
.update_stats = falcon_update_stats_gmac,
- .irq = efx_port_dummy_op_void,
- .poll = efx_port_dummy_op_void,
+ .check_fault = falcon_gmac_check_fault,
};
diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h
deleted file mode 100644
index 2d2261117ac..00000000000
--- a/drivers/net/sfc/falcon_hwdefs.h
+++ /dev/null
@@ -1,1333 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_FALCON_HWDEFS_H
-#define EFX_FALCON_HWDEFS_H
-
-/*
- * Falcon hardware value definitions.
- * Falcon is the internal codename for the SFC4000 controller that is
- * present in SFE400X evaluation boards
- */
-
-/**************************************************************************
- *
- * Falcon registers
- *
- **************************************************************************
- */
-
-/* Address region register */
-#define ADR_REGION_REG_KER 0x00
-#define ADR_REGION0_LBN 0
-#define ADR_REGION0_WIDTH 18
-#define ADR_REGION1_LBN 32
-#define ADR_REGION1_WIDTH 18
-#define ADR_REGION2_LBN 64
-#define ADR_REGION2_WIDTH 18
-#define ADR_REGION3_LBN 96
-#define ADR_REGION3_WIDTH 18
-
-/* Interrupt enable register */
-#define INT_EN_REG_KER 0x0010
-#define KER_INT_KER_LBN 3
-#define KER_INT_KER_WIDTH 1
-#define DRV_INT_EN_KER_LBN 0
-#define DRV_INT_EN_KER_WIDTH 1
-
-/* Interrupt status address register */
-#define INT_ADR_REG_KER 0x0030
-#define NORM_INT_VEC_DIS_KER_LBN 64
-#define NORM_INT_VEC_DIS_KER_WIDTH 1
-#define INT_ADR_KER_LBN 0
-#define INT_ADR_KER_WIDTH EFX_DMA_TYPE_WIDTH(64) /* not 46 for this one */
-
-/* Interrupt status register (B0 only) */
-#define INT_ISR0_B0 0x90
-#define INT_ISR1_B0 0xA0
-
-/* Interrupt acknowledge register (A0/A1 only) */
-#define INT_ACK_REG_KER_A1 0x0050
-#define INT_ACK_DUMMY_DATA_LBN 0
-#define INT_ACK_DUMMY_DATA_WIDTH 32
-
-/* Interrupt acknowledge work-around register (A0/A1 only )*/
-#define WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1 0x0070
-
-/* SPI host command register */
-#define EE_SPI_HCMD_REG_KER 0x0100
-#define EE_SPI_HCMD_CMD_EN_LBN 31
-#define EE_SPI_HCMD_CMD_EN_WIDTH 1
-#define EE_WR_TIMER_ACTIVE_LBN 28
-#define EE_WR_TIMER_ACTIVE_WIDTH 1
-#define EE_SPI_HCMD_SF_SEL_LBN 24
-#define EE_SPI_HCMD_SF_SEL_WIDTH 1
-#define EE_SPI_EEPROM 0
-#define EE_SPI_FLASH 1
-#define EE_SPI_HCMD_DABCNT_LBN 16
-#define EE_SPI_HCMD_DABCNT_WIDTH 5
-#define EE_SPI_HCMD_READ_LBN 15
-#define EE_SPI_HCMD_READ_WIDTH 1
-#define EE_SPI_READ 1
-#define EE_SPI_WRITE 0
-#define EE_SPI_HCMD_DUBCNT_LBN 12
-#define EE_SPI_HCMD_DUBCNT_WIDTH 2
-#define EE_SPI_HCMD_ADBCNT_LBN 8
-#define EE_SPI_HCMD_ADBCNT_WIDTH 2
-#define EE_SPI_HCMD_ENC_LBN 0
-#define EE_SPI_HCMD_ENC_WIDTH 8
-
-/* SPI host address register */
-#define EE_SPI_HADR_REG_KER 0x0110
-#define EE_SPI_HADR_ADR_LBN 0
-#define EE_SPI_HADR_ADR_WIDTH 24
-
-/* SPI host data register */
-#define EE_SPI_HDATA_REG_KER 0x0120
-
-/* SPI/VPD config register */
-#define EE_VPD_CFG_REG_KER 0x0140
-#define EE_VPD_EN_LBN 0
-#define EE_VPD_EN_WIDTH 1
-#define EE_VPD_EN_AD9_MODE_LBN 1
-#define EE_VPD_EN_AD9_MODE_WIDTH 1
-#define EE_EE_CLOCK_DIV_LBN 112
-#define EE_EE_CLOCK_DIV_WIDTH 7
-#define EE_SF_CLOCK_DIV_LBN 120
-#define EE_SF_CLOCK_DIV_WIDTH 7
-
-/* PCIE CORE ACCESS REG */
-#define PCIE_CORE_ADDR_PCIE_DEVICE_CTRL_STAT 0x68
-#define PCIE_CORE_ADDR_PCIE_LINK_CTRL_STAT 0x70
-#define PCIE_CORE_ADDR_ACK_RPL_TIMER 0x700
-#define PCIE_CORE_ADDR_ACK_FREQ 0x70C
-
-/* NIC status register */
-#define NIC_STAT_REG 0x0200
-#define EE_STRAP_EN_LBN 31
-#define EE_STRAP_EN_WIDTH 1
-#define EE_STRAP_OVR_LBN 24
-#define EE_STRAP_OVR_WIDTH 4
-#define ONCHIP_SRAM_LBN 16
-#define ONCHIP_SRAM_WIDTH 1
-#define SF_PRST_LBN 9
-#define SF_PRST_WIDTH 1
-#define EE_PRST_LBN 8
-#define EE_PRST_WIDTH 1
-#define STRAP_PINS_LBN 0
-#define STRAP_PINS_WIDTH 3
-/* These bit definitions are extrapolated from the list of numerical
- * values for STRAP_PINS.
- */
-#define STRAP_10G_LBN 2
-#define STRAP_10G_WIDTH 1
-#define STRAP_PCIE_LBN 0
-#define STRAP_PCIE_WIDTH 1
-
-#define BOOTED_USING_NVDEVICE_LBN 3
-#define BOOTED_USING_NVDEVICE_WIDTH 1
-
-/* GPIO control register */
-#define GPIO_CTL_REG_KER 0x0210
-#define GPIO_USE_NIC_CLK_LBN (30)
-#define GPIO_USE_NIC_CLK_WIDTH (1)
-#define GPIO_OUTPUTS_LBN (16)
-#define GPIO_OUTPUTS_WIDTH (4)
-#define GPIO_INPUTS_LBN (8)
-#define GPIO_DIRECTION_LBN (24)
-#define GPIO_DIRECTION_WIDTH (4)
-#define GPIO_DIRECTION_OUT (1)
-#define GPIO_SRAM_SLEEP (1 << 1)
-
-#define GPIO3_OEN_LBN (GPIO_DIRECTION_LBN + 3)
-#define GPIO3_OEN_WIDTH 1
-#define GPIO2_OEN_LBN (GPIO_DIRECTION_LBN + 2)
-#define GPIO2_OEN_WIDTH 1
-#define GPIO1_OEN_LBN (GPIO_DIRECTION_LBN + 1)
-#define GPIO1_OEN_WIDTH 1
-#define GPIO0_OEN_LBN (GPIO_DIRECTION_LBN + 0)
-#define GPIO0_OEN_WIDTH 1
-
-#define GPIO3_OUT_LBN (GPIO_OUTPUTS_LBN + 3)
-#define GPIO3_OUT_WIDTH 1
-#define GPIO2_OUT_LBN (GPIO_OUTPUTS_LBN + 2)
-#define GPIO2_OUT_WIDTH 1
-#define GPIO1_OUT_LBN (GPIO_OUTPUTS_LBN + 1)
-#define GPIO1_OUT_WIDTH 1
-#define GPIO0_OUT_LBN (GPIO_OUTPUTS_LBN + 0)
-#define GPIO0_OUT_WIDTH 1
-
-#define GPIO3_IN_LBN (GPIO_INPUTS_LBN + 3)
-#define GPIO3_IN_WIDTH 1
-#define GPIO2_IN_WIDTH 1
-#define GPIO1_IN_WIDTH 1
-#define GPIO0_IN_LBN (GPIO_INPUTS_LBN + 0)
-#define GPIO0_IN_WIDTH 1
-
-/* Global control register */
-#define GLB_CTL_REG_KER 0x0220
-#define EXT_PHY_RST_CTL_LBN 63
-#define EXT_PHY_RST_CTL_WIDTH 1
-#define PCIE_SD_RST_CTL_LBN 61
-#define PCIE_SD_RST_CTL_WIDTH 1
-
-#define PCIE_NSTCK_RST_CTL_LBN 58
-#define PCIE_NSTCK_RST_CTL_WIDTH 1
-#define PCIE_CORE_RST_CTL_LBN 57
-#define PCIE_CORE_RST_CTL_WIDTH 1
-#define EE_RST_CTL_LBN 49
-#define EE_RST_CTL_WIDTH 1
-#define RST_XGRX_LBN 24
-#define RST_XGRX_WIDTH 1
-#define RST_XGTX_LBN 23
-#define RST_XGTX_WIDTH 1
-#define RST_EM_LBN 22
-#define RST_EM_WIDTH 1
-#define EXT_PHY_RST_DUR_LBN 1
-#define EXT_PHY_RST_DUR_WIDTH 3
-#define SWRST_LBN 0
-#define SWRST_WIDTH 1
-#define INCLUDE_IN_RESET 0
-#define EXCLUDE_FROM_RESET 1
-
-/* Fatal interrupt register */
-#define FATAL_INTR_REG_KER 0x0230
-#define RBUF_OWN_INT_KER_EN_LBN 39
-#define RBUF_OWN_INT_KER_EN_WIDTH 1
-#define TBUF_OWN_INT_KER_EN_LBN 38
-#define TBUF_OWN_INT_KER_EN_WIDTH 1
-#define ILL_ADR_INT_KER_EN_LBN 33
-#define ILL_ADR_INT_KER_EN_WIDTH 1
-#define MEM_PERR_INT_KER_LBN 8
-#define MEM_PERR_INT_KER_WIDTH 1
-#define INT_KER_ERROR_LBN 0
-#define INT_KER_ERROR_WIDTH 12
-
-#define DP_CTRL_REG 0x250
-#define FLS_EVQ_ID_LBN 0
-#define FLS_EVQ_ID_WIDTH 11
-
-#define MEM_STAT_REG_KER 0x260
-
-/* Debug probe register */
-#define DEBUG_BLK_SEL_MISC 7
-#define DEBUG_BLK_SEL_SERDES 6
-#define DEBUG_BLK_SEL_EM 5
-#define DEBUG_BLK_SEL_SR 4
-#define DEBUG_BLK_SEL_EV 3
-#define DEBUG_BLK_SEL_RX 2
-#define DEBUG_BLK_SEL_TX 1
-#define DEBUG_BLK_SEL_BIU 0
-
-/* FPGA build version */
-#define ALTERA_BUILD_REG_KER 0x0300
-#define VER_ALL_LBN 0
-#define VER_ALL_WIDTH 32
-
-/* Spare EEPROM bits register (flash 0x390) */
-#define SPARE_REG_KER 0x310
-#define MEM_PERR_EN_TX_DATA_LBN 72
-#define MEM_PERR_EN_TX_DATA_WIDTH 2
-
-/* Timer table for kernel access */
-#define TIMER_CMD_REG_KER 0x420
-#define TIMER_MODE_LBN 12
-#define TIMER_MODE_WIDTH 2
-#define TIMER_MODE_DIS 0
-#define TIMER_MODE_INT_HLDOFF 2
-#define TIMER_VAL_LBN 0
-#define TIMER_VAL_WIDTH 12
-
-/* Driver generated event register */
-#define DRV_EV_REG_KER 0x440
-#define DRV_EV_QID_LBN 64
-#define DRV_EV_QID_WIDTH 12
-#define DRV_EV_DATA_LBN 0
-#define DRV_EV_DATA_WIDTH 64
-
-/* Buffer table configuration register */
-#define BUF_TBL_CFG_REG_KER 0x600
-#define BUF_TBL_MODE_LBN 3
-#define BUF_TBL_MODE_WIDTH 1
-#define BUF_TBL_MODE_HALF 0
-#define BUF_TBL_MODE_FULL 1
-
-/* SRAM receive descriptor cache configuration register */
-#define SRM_RX_DC_CFG_REG_KER 0x610
-#define SRM_RX_DC_BASE_ADR_LBN 0
-#define SRM_RX_DC_BASE_ADR_WIDTH 21
-
-/* SRAM transmit descriptor cache configuration register */
-#define SRM_TX_DC_CFG_REG_KER 0x620
-#define SRM_TX_DC_BASE_ADR_LBN 0
-#define SRM_TX_DC_BASE_ADR_WIDTH 21
-
-/* SRAM configuration register */
-#define SRM_CFG_REG_KER 0x630
-#define SRAM_OOB_BT_INIT_EN_LBN 3
-#define SRAM_OOB_BT_INIT_EN_WIDTH 1
-#define SRM_NUM_BANKS_AND_BANK_SIZE_LBN 0
-#define SRM_NUM_BANKS_AND_BANK_SIZE_WIDTH 3
-#define SRM_NB_BSZ_1BANKS_2M 0
-#define SRM_NB_BSZ_1BANKS_4M 1
-#define SRM_NB_BSZ_1BANKS_8M 2
-#define SRM_NB_BSZ_DEFAULT 3 /* char driver will set the default */
-#define SRM_NB_BSZ_2BANKS_4M 4
-#define SRM_NB_BSZ_2BANKS_8M 5
-#define SRM_NB_BSZ_2BANKS_16M 6
-#define SRM_NB_BSZ_RESERVED 7
-
-/* Special buffer table update register */
-#define BUF_TBL_UPD_REG_KER 0x0650
-#define BUF_UPD_CMD_LBN 63
-#define BUF_UPD_CMD_WIDTH 1
-#define BUF_CLR_CMD_LBN 62
-#define BUF_CLR_CMD_WIDTH 1
-#define BUF_CLR_END_ID_LBN 32
-#define BUF_CLR_END_ID_WIDTH 20
-#define BUF_CLR_START_ID_LBN 0
-#define BUF_CLR_START_ID_WIDTH 20
-
-/* Receive configuration register */
-#define RX_CFG_REG_KER 0x800
-
-/* B0 */
-#define RX_INGR_EN_B0_LBN 47
-#define RX_INGR_EN_B0_WIDTH 1
-#define RX_DESC_PUSH_EN_B0_LBN 43
-#define RX_DESC_PUSH_EN_B0_WIDTH 1
-#define RX_XON_TX_TH_B0_LBN 33
-#define RX_XON_TX_TH_B0_WIDTH 5
-#define RX_XOFF_TX_TH_B0_LBN 28
-#define RX_XOFF_TX_TH_B0_WIDTH 5
-#define RX_USR_BUF_SIZE_B0_LBN 19
-#define RX_USR_BUF_SIZE_B0_WIDTH 9
-#define RX_XON_MAC_TH_B0_LBN 10
-#define RX_XON_MAC_TH_B0_WIDTH 9
-#define RX_XOFF_MAC_TH_B0_LBN 1
-#define RX_XOFF_MAC_TH_B0_WIDTH 9
-#define RX_XOFF_MAC_EN_B0_LBN 0
-#define RX_XOFF_MAC_EN_B0_WIDTH 1
-
-/* A1 */
-#define RX_DESC_PUSH_EN_A1_LBN 35
-#define RX_DESC_PUSH_EN_A1_WIDTH 1
-#define RX_XON_TX_TH_A1_LBN 25
-#define RX_XON_TX_TH_A1_WIDTH 5
-#define RX_XOFF_TX_TH_A1_LBN 20
-#define RX_XOFF_TX_TH_A1_WIDTH 5
-#define RX_USR_BUF_SIZE_A1_LBN 11
-#define RX_USR_BUF_SIZE_A1_WIDTH 9
-#define RX_XON_MAC_TH_A1_LBN 6
-#define RX_XON_MAC_TH_A1_WIDTH 5
-#define RX_XOFF_MAC_TH_A1_LBN 1
-#define RX_XOFF_MAC_TH_A1_WIDTH 5
-#define RX_XOFF_MAC_EN_A1_LBN 0
-#define RX_XOFF_MAC_EN_A1_WIDTH 1
-
-/* Receive filter control register */
-#define RX_FILTER_CTL_REG 0x810
-#define UDP_FULL_SRCH_LIMIT_LBN 32
-#define UDP_FULL_SRCH_LIMIT_WIDTH 8
-#define NUM_KER_LBN 24
-#define NUM_KER_WIDTH 2
-#define UDP_WILD_SRCH_LIMIT_LBN 16
-#define UDP_WILD_SRCH_LIMIT_WIDTH 8
-#define TCP_WILD_SRCH_LIMIT_LBN 8
-#define TCP_WILD_SRCH_LIMIT_WIDTH 8
-#define TCP_FULL_SRCH_LIMIT_LBN 0
-#define TCP_FULL_SRCH_LIMIT_WIDTH 8
-
-/* RX queue flush register */
-#define RX_FLUSH_DESCQ_REG_KER 0x0820
-#define RX_FLUSH_DESCQ_CMD_LBN 24
-#define RX_FLUSH_DESCQ_CMD_WIDTH 1
-#define RX_FLUSH_DESCQ_LBN 0
-#define RX_FLUSH_DESCQ_WIDTH 12
-
-/* Receive descriptor update register */
-#define RX_DESC_UPD_REG_KER_DWORD (0x830 + 12)
-#define RX_DESC_WPTR_DWORD_LBN 0
-#define RX_DESC_WPTR_DWORD_WIDTH 12
-
-/* Receive descriptor cache configuration register */
-#define RX_DC_CFG_REG_KER 0x840
-#define RX_DC_SIZE_LBN 0
-#define RX_DC_SIZE_WIDTH 2
-
-#define RX_DC_PF_WM_REG_KER 0x850
-#define RX_DC_PF_LWM_LBN 0
-#define RX_DC_PF_LWM_WIDTH 6
-
-/* RX no descriptor drop counter */
-#define RX_NODESC_DROP_REG_KER 0x880
-#define RX_NODESC_DROP_CNT_LBN 0
-#define RX_NODESC_DROP_CNT_WIDTH 16
-
-/* RX black magic register */
-#define RX_SELF_RST_REG_KER 0x890
-#define RX_ISCSI_DIS_LBN 17
-#define RX_ISCSI_DIS_WIDTH 1
-#define RX_NODESC_WAIT_DIS_LBN 9
-#define RX_NODESC_WAIT_DIS_WIDTH 1
-#define RX_RECOVERY_EN_LBN 8
-#define RX_RECOVERY_EN_WIDTH 1
-
-/* TX queue flush register */
-#define TX_FLUSH_DESCQ_REG_KER 0x0a00
-#define TX_FLUSH_DESCQ_CMD_LBN 12
-#define TX_FLUSH_DESCQ_CMD_WIDTH 1
-#define TX_FLUSH_DESCQ_LBN 0
-#define TX_FLUSH_DESCQ_WIDTH 12
-
-/* Transmit descriptor update register */
-#define TX_DESC_UPD_REG_KER_DWORD (0xa10 + 12)
-#define TX_DESC_WPTR_DWORD_LBN 0
-#define TX_DESC_WPTR_DWORD_WIDTH 12
-
-/* Transmit descriptor cache configuration register */
-#define TX_DC_CFG_REG_KER 0xa20
-#define TX_DC_SIZE_LBN 0
-#define TX_DC_SIZE_WIDTH 2
-
-/* Transmit checksum configuration register (A0/A1 only) */
-#define TX_CHKSM_CFG_REG_KER_A1 0xa30
-
-/* Transmit configuration register */
-#define TX_CFG_REG_KER 0xa50
-#define TX_NO_EOP_DISC_EN_LBN 5
-#define TX_NO_EOP_DISC_EN_WIDTH 1
-
-/* Transmit configuration register 2 */
-#define TX_CFG2_REG_KER 0xa80
-#define TX_CSR_PUSH_EN_LBN 89
-#define TX_CSR_PUSH_EN_WIDTH 1
-#define TX_RX_SPACER_LBN 64
-#define TX_RX_SPACER_WIDTH 8
-#define TX_SW_EV_EN_LBN 59
-#define TX_SW_EV_EN_WIDTH 1
-#define TX_RX_SPACER_EN_LBN 57
-#define TX_RX_SPACER_EN_WIDTH 1
-#define TX_PREF_THRESHOLD_LBN 19
-#define TX_PREF_THRESHOLD_WIDTH 2
-#define TX_ONE_PKT_PER_Q_LBN 18
-#define TX_ONE_PKT_PER_Q_WIDTH 1
-#define TX_DIS_NON_IP_EV_LBN 17
-#define TX_DIS_NON_IP_EV_WIDTH 1
-#define TX_FLUSH_MIN_LEN_EN_B0_LBN 7
-#define TX_FLUSH_MIN_LEN_EN_B0_WIDTH 1
-
-/* PHY management transmit data register */
-#define MD_TXD_REG_KER 0xc00
-#define MD_TXD_LBN 0
-#define MD_TXD_WIDTH 16
-
-/* PHY management receive data register */
-#define MD_RXD_REG_KER 0xc10
-#define MD_RXD_LBN 0
-#define MD_RXD_WIDTH 16
-
-/* PHY management configuration & status register */
-#define MD_CS_REG_KER 0xc20
-#define MD_GC_LBN 4
-#define MD_GC_WIDTH 1
-#define MD_RIC_LBN 2
-#define MD_RIC_WIDTH 1
-#define MD_RDC_LBN 1
-#define MD_RDC_WIDTH 1
-#define MD_WRC_LBN 0
-#define MD_WRC_WIDTH 1
-
-/* PHY management PHY address register */
-#define MD_PHY_ADR_REG_KER 0xc30
-#define MD_PHY_ADR_LBN 0
-#define MD_PHY_ADR_WIDTH 16
-
-/* PHY management ID register */
-#define MD_ID_REG_KER 0xc40
-#define MD_PRT_ADR_LBN 11
-#define MD_PRT_ADR_WIDTH 5
-#define MD_DEV_ADR_LBN 6
-#define MD_DEV_ADR_WIDTH 5
-
-/* PHY management status & mask register (DWORD read only) */
-#define MD_STAT_REG_KER 0xc50
-#define MD_BSERR_LBN 2
-#define MD_BSERR_WIDTH 1
-#define MD_LNFL_LBN 1
-#define MD_LNFL_WIDTH 1
-#define MD_BSY_LBN 0
-#define MD_BSY_WIDTH 1
-
-/* Port 0 and 1 MAC stats registers */
-#define MAC0_STAT_DMA_REG_KER 0xc60
-#define MAC_STAT_DMA_CMD_LBN 48
-#define MAC_STAT_DMA_CMD_WIDTH 1
-#define MAC_STAT_DMA_ADR_LBN 0
-#define MAC_STAT_DMA_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46)
-
-/* Port 0 and 1 MAC control registers */
-#define MAC0_CTRL_REG_KER 0xc80
-#define MAC_XOFF_VAL_LBN 16
-#define MAC_XOFF_VAL_WIDTH 16
-#define TXFIFO_DRAIN_EN_B0_LBN 7
-#define TXFIFO_DRAIN_EN_B0_WIDTH 1
-#define MAC_BCAD_ACPT_LBN 4
-#define MAC_BCAD_ACPT_WIDTH 1
-#define MAC_UC_PROM_LBN 3
-#define MAC_UC_PROM_WIDTH 1
-#define MAC_LINK_STATUS_LBN 2
-#define MAC_LINK_STATUS_WIDTH 1
-#define MAC_SPEED_LBN 0
-#define MAC_SPEED_WIDTH 2
-
-/* 10G XAUI XGXS default values */
-#define XX_TXDRV_DEQ_DEFAULT 0xe /* deq=.6 */
-#define XX_TXDRV_DTX_DEFAULT 0x5 /* 1.25 */
-#define XX_SD_CTL_DRV_DEFAULT 0 /* 20mA */
-
-/* Multicast address hash table */
-#define MAC_MCAST_HASH_REG0_KER 0xca0
-#define MAC_MCAST_HASH_REG1_KER 0xcb0
-
-/* GMAC configuration register 1 */
-#define GM_CFG1_REG 0xe00
-#define GM_SW_RST_LBN 31
-#define GM_SW_RST_WIDTH 1
-#define GM_LOOP_LBN 8
-#define GM_LOOP_WIDTH 1
-#define GM_RX_FC_EN_LBN 5
-#define GM_RX_FC_EN_WIDTH 1
-#define GM_TX_FC_EN_LBN 4
-#define GM_TX_FC_EN_WIDTH 1
-#define GM_RX_EN_LBN 2
-#define GM_RX_EN_WIDTH 1
-#define GM_TX_EN_LBN 0
-#define GM_TX_EN_WIDTH 1
-
-/* GMAC configuration register 2 */
-#define GM_CFG2_REG 0xe10
-#define GM_PAMBL_LEN_LBN 12
-#define GM_PAMBL_LEN_WIDTH 4
-#define GM_IF_MODE_LBN 8
-#define GM_IF_MODE_WIDTH 2
-#define GM_LEN_CHK_LBN 4
-#define GM_LEN_CHK_WIDTH 1
-#define GM_PAD_CRC_EN_LBN 2
-#define GM_PAD_CRC_EN_WIDTH 1
-#define GM_FD_LBN 0
-#define GM_FD_WIDTH 1
-
-/* GMAC maximum frame length register */
-#define GM_MAX_FLEN_REG 0xe40
-#define GM_MAX_FLEN_LBN 0
-#define GM_MAX_FLEN_WIDTH 16
-
-/* GMAC station address register 1 */
-#define GM_ADR1_REG 0xf00
-#define GM_HWADDR_5_LBN 24
-#define GM_HWADDR_5_WIDTH 8
-#define GM_HWADDR_4_LBN 16
-#define GM_HWADDR_4_WIDTH 8
-#define GM_HWADDR_3_LBN 8
-#define GM_HWADDR_3_WIDTH 8
-#define GM_HWADDR_2_LBN 0
-#define GM_HWADDR_2_WIDTH 8
-
-/* GMAC station address register 2 */
-#define GM_ADR2_REG 0xf10
-#define GM_HWADDR_1_LBN 24
-#define GM_HWADDR_1_WIDTH 8
-#define GM_HWADDR_0_LBN 16
-#define GM_HWADDR_0_WIDTH 8
-
-/* GMAC FIFO configuration register 0 */
-#define GMF_CFG0_REG 0xf20
-#define GMF_FTFENREQ_LBN 12
-#define GMF_FTFENREQ_WIDTH 1
-#define GMF_STFENREQ_LBN 11
-#define GMF_STFENREQ_WIDTH 1
-#define GMF_FRFENREQ_LBN 10
-#define GMF_FRFENREQ_WIDTH 1
-#define GMF_SRFENREQ_LBN 9
-#define GMF_SRFENREQ_WIDTH 1
-#define GMF_WTMENREQ_LBN 8
-#define GMF_WTMENREQ_WIDTH 1
-
-/* GMAC FIFO configuration register 1 */
-#define GMF_CFG1_REG 0xf30
-#define GMF_CFGFRTH_LBN 16
-#define GMF_CFGFRTH_WIDTH 5
-#define GMF_CFGXOFFRTX_LBN 0
-#define GMF_CFGXOFFRTX_WIDTH 16
-
-/* GMAC FIFO configuration register 2 */
-#define GMF_CFG2_REG 0xf40
-#define GMF_CFGHWM_LBN 16
-#define GMF_CFGHWM_WIDTH 6
-#define GMF_CFGLWM_LBN 0
-#define GMF_CFGLWM_WIDTH 6
-
-/* GMAC FIFO configuration register 3 */
-#define GMF_CFG3_REG 0xf50
-#define GMF_CFGHWMFT_LBN 16
-#define GMF_CFGHWMFT_WIDTH 6
-#define GMF_CFGFTTH_LBN 0
-#define GMF_CFGFTTH_WIDTH 6
-
-/* GMAC FIFO configuration register 4 */
-#define GMF_CFG4_REG 0xf60
-#define GMF_HSTFLTRFRM_PAUSE_LBN 12
-#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12
-
-/* GMAC FIFO configuration register 5 */
-#define GMF_CFG5_REG 0xf70
-#define GMF_CFGHDPLX_LBN 22
-#define GMF_CFGHDPLX_WIDTH 1
-#define GMF_CFGBYTMODE_LBN 19
-#define GMF_CFGBYTMODE_WIDTH 1
-#define GMF_HSTDRPLT64_LBN 18
-#define GMF_HSTDRPLT64_WIDTH 1
-#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12
-#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1
-
-/* XGMAC address register low */
-#define XM_ADR_LO_REG 0x1200
-#define XM_ADR_3_LBN 24
-#define XM_ADR_3_WIDTH 8
-#define XM_ADR_2_LBN 16
-#define XM_ADR_2_WIDTH 8
-#define XM_ADR_1_LBN 8
-#define XM_ADR_1_WIDTH 8
-#define XM_ADR_0_LBN 0
-#define XM_ADR_0_WIDTH 8
-
-/* XGMAC address register high */
-#define XM_ADR_HI_REG 0x1210
-#define XM_ADR_5_LBN 8
-#define XM_ADR_5_WIDTH 8
-#define XM_ADR_4_LBN 0
-#define XM_ADR_4_WIDTH 8
-
-/* XGMAC global configuration */
-#define XM_GLB_CFG_REG 0x1220
-#define XM_RX_STAT_EN_LBN 11
-#define XM_RX_STAT_EN_WIDTH 1
-#define XM_TX_STAT_EN_LBN 10
-#define XM_TX_STAT_EN_WIDTH 1
-#define XM_RX_JUMBO_MODE_LBN 6
-#define XM_RX_JUMBO_MODE_WIDTH 1
-#define XM_INTCLR_MODE_LBN 3
-#define XM_INTCLR_MODE_WIDTH 1
-#define XM_CORE_RST_LBN 0
-#define XM_CORE_RST_WIDTH 1
-
-/* XGMAC transmit configuration */
-#define XM_TX_CFG_REG 0x1230
-#define XM_IPG_LBN 16
-#define XM_IPG_WIDTH 4
-#define XM_FCNTL_LBN 10
-#define XM_FCNTL_WIDTH 1
-#define XM_TXCRC_LBN 8
-#define XM_TXCRC_WIDTH 1
-#define XM_AUTO_PAD_LBN 5
-#define XM_AUTO_PAD_WIDTH 1
-#define XM_TX_PRMBL_LBN 2
-#define XM_TX_PRMBL_WIDTH 1
-#define XM_TXEN_LBN 1
-#define XM_TXEN_WIDTH 1
-
-/* XGMAC receive configuration */
-#define XM_RX_CFG_REG 0x1240
-#define XM_PASS_CRC_ERR_LBN 25
-#define XM_PASS_CRC_ERR_WIDTH 1
-#define XM_ACPT_ALL_MCAST_LBN 11
-#define XM_ACPT_ALL_MCAST_WIDTH 1
-#define XM_ACPT_ALL_UCAST_LBN 9
-#define XM_ACPT_ALL_UCAST_WIDTH 1
-#define XM_AUTO_DEPAD_LBN 8
-#define XM_AUTO_DEPAD_WIDTH 1
-#define XM_RXEN_LBN 1
-#define XM_RXEN_WIDTH 1
-
-/* XGMAC management interrupt mask register */
-#define XM_MGT_INT_MSK_REG_B0 0x1250
-#define XM_MSK_PRMBLE_ERR_LBN 2
-#define XM_MSK_PRMBLE_ERR_WIDTH 1
-#define XM_MSK_RMTFLT_LBN 1
-#define XM_MSK_RMTFLT_WIDTH 1
-#define XM_MSK_LCLFLT_LBN 0
-#define XM_MSK_LCLFLT_WIDTH 1
-
-/* XGMAC flow control register */
-#define XM_FC_REG 0x1270
-#define XM_PAUSE_TIME_LBN 16
-#define XM_PAUSE_TIME_WIDTH 16
-#define XM_DIS_FCNTL_LBN 0
-#define XM_DIS_FCNTL_WIDTH 1
-
-/* XGMAC pause time count register */
-#define XM_PAUSE_TIME_REG 0x1290
-
-/* XGMAC transmit parameter register */
-#define XM_TX_PARAM_REG 0x012d0
-#define XM_TX_JUMBO_MODE_LBN 31
-#define XM_TX_JUMBO_MODE_WIDTH 1
-#define XM_MAX_TX_FRM_SIZE_LBN 16
-#define XM_MAX_TX_FRM_SIZE_WIDTH 14
-
-/* XGMAC receive parameter register */
-#define XM_RX_PARAM_REG 0x12e0
-#define XM_MAX_RX_FRM_SIZE_LBN 0
-#define XM_MAX_RX_FRM_SIZE_WIDTH 14
-
-/* XGMAC management interrupt status register */
-#define XM_MGT_INT_REG_B0 0x12f0
-#define XM_PRMBLE_ERR 2
-#define XM_PRMBLE_WIDTH 1
-#define XM_RMTFLT_LBN 1
-#define XM_RMTFLT_WIDTH 1
-#define XM_LCLFLT_LBN 0
-#define XM_LCLFLT_WIDTH 1
-
-/* XGXS/XAUI powerdown/reset register */
-#define XX_PWR_RST_REG 0x1300
-
-#define XX_SD_RST_ACT_LBN 16
-#define XX_SD_RST_ACT_WIDTH 1
-#define XX_PWRDND_EN_LBN 15
-#define XX_PWRDND_EN_WIDTH 1
-#define XX_PWRDNC_EN_LBN 14
-#define XX_PWRDNC_EN_WIDTH 1
-#define XX_PWRDNB_EN_LBN 13
-#define XX_PWRDNB_EN_WIDTH 1
-#define XX_PWRDNA_EN_LBN 12
-#define XX_PWRDNA_EN_WIDTH 1
-#define XX_RSTPLLCD_EN_LBN 9
-#define XX_RSTPLLCD_EN_WIDTH 1
-#define XX_RSTPLLAB_EN_LBN 8
-#define XX_RSTPLLAB_EN_WIDTH 1
-#define XX_RESETD_EN_LBN 7
-#define XX_RESETD_EN_WIDTH 1
-#define XX_RESETC_EN_LBN 6
-#define XX_RESETC_EN_WIDTH 1
-#define XX_RESETB_EN_LBN 5
-#define XX_RESETB_EN_WIDTH 1
-#define XX_RESETA_EN_LBN 4
-#define XX_RESETA_EN_WIDTH 1
-#define XX_RSTXGXSRX_EN_LBN 2
-#define XX_RSTXGXSRX_EN_WIDTH 1
-#define XX_RSTXGXSTX_EN_LBN 1
-#define XX_RSTXGXSTX_EN_WIDTH 1
-#define XX_RST_XX_EN_LBN 0
-#define XX_RST_XX_EN_WIDTH 1
-
-/* XGXS/XAUI powerdown/reset control register */
-#define XX_SD_CTL_REG 0x1310
-#define XX_HIDRVD_LBN 15
-#define XX_HIDRVD_WIDTH 1
-#define XX_LODRVD_LBN 14
-#define XX_LODRVD_WIDTH 1
-#define XX_HIDRVC_LBN 13
-#define XX_HIDRVC_WIDTH 1
-#define XX_LODRVC_LBN 12
-#define XX_LODRVC_WIDTH 1
-#define XX_HIDRVB_LBN 11
-#define XX_HIDRVB_WIDTH 1
-#define XX_LODRVB_LBN 10
-#define XX_LODRVB_WIDTH 1
-#define XX_HIDRVA_LBN 9
-#define XX_HIDRVA_WIDTH 1
-#define XX_LODRVA_LBN 8
-#define XX_LODRVA_WIDTH 1
-#define XX_LPBKD_LBN 3
-#define XX_LPBKD_WIDTH 1
-#define XX_LPBKC_LBN 2
-#define XX_LPBKC_WIDTH 1
-#define XX_LPBKB_LBN 1
-#define XX_LPBKB_WIDTH 1
-#define XX_LPBKA_LBN 0
-#define XX_LPBKA_WIDTH 1
-
-#define XX_TXDRV_CTL_REG 0x1320
-#define XX_DEQD_LBN 28
-#define XX_DEQD_WIDTH 4
-#define XX_DEQC_LBN 24
-#define XX_DEQC_WIDTH 4
-#define XX_DEQB_LBN 20
-#define XX_DEQB_WIDTH 4
-#define XX_DEQA_LBN 16
-#define XX_DEQA_WIDTH 4
-#define XX_DTXD_LBN 12
-#define XX_DTXD_WIDTH 4
-#define XX_DTXC_LBN 8
-#define XX_DTXC_WIDTH 4
-#define XX_DTXB_LBN 4
-#define XX_DTXB_WIDTH 4
-#define XX_DTXA_LBN 0
-#define XX_DTXA_WIDTH 4
-
-/* XAUI XGXS core status register */
-#define XX_CORE_STAT_REG 0x1360
-#define XX_FORCE_SIG_LBN 24
-#define XX_FORCE_SIG_WIDTH 8
-#define XX_FORCE_SIG_DECODE_FORCED 0xff
-#define XX_XGXS_LB_EN_LBN 23
-#define XX_XGXS_LB_EN_WIDTH 1
-#define XX_XGMII_LB_EN_LBN 22
-#define XX_XGMII_LB_EN_WIDTH 1
-#define XX_ALIGN_DONE_LBN 20
-#define XX_ALIGN_DONE_WIDTH 1
-#define XX_SYNC_STAT_LBN 16
-#define XX_SYNC_STAT_WIDTH 4
-#define XX_SYNC_STAT_DECODE_SYNCED 0xf
-#define XX_COMMA_DET_LBN 12
-#define XX_COMMA_DET_WIDTH 4
-#define XX_COMMA_DET_DECODE_DETECTED 0xf
-#define XX_COMMA_DET_RESET 0xf
-#define XX_CHARERR_LBN 4
-#define XX_CHARERR_WIDTH 4
-#define XX_CHARERR_RESET 0xf
-#define XX_DISPERR_LBN 0
-#define XX_DISPERR_WIDTH 4
-#define XX_DISPERR_RESET 0xf
-
-/* Receive filter table */
-#define RX_FILTER_TBL0 0xF00000
-
-/* Receive descriptor pointer table */
-#define RX_DESC_PTR_TBL_KER_A1 0x11800
-#define RX_DESC_PTR_TBL_KER_B0 0xF40000
-#define RX_DESC_PTR_TBL_KER_P0 0x900
-#define RX_ISCSI_DDIG_EN_LBN 88
-#define RX_ISCSI_DDIG_EN_WIDTH 1
-#define RX_ISCSI_HDIG_EN_LBN 87
-#define RX_ISCSI_HDIG_EN_WIDTH 1
-#define RX_DESCQ_BUF_BASE_ID_LBN 36
-#define RX_DESCQ_BUF_BASE_ID_WIDTH 20
-#define RX_DESCQ_EVQ_ID_LBN 24
-#define RX_DESCQ_EVQ_ID_WIDTH 12
-#define RX_DESCQ_OWNER_ID_LBN 10
-#define RX_DESCQ_OWNER_ID_WIDTH 14
-#define RX_DESCQ_LABEL_LBN 5
-#define RX_DESCQ_LABEL_WIDTH 5
-#define RX_DESCQ_SIZE_LBN 3
-#define RX_DESCQ_SIZE_WIDTH 2
-#define RX_DESCQ_SIZE_4K 3
-#define RX_DESCQ_SIZE_2K 2
-#define RX_DESCQ_SIZE_1K 1
-#define RX_DESCQ_SIZE_512 0
-#define RX_DESCQ_TYPE_LBN 2
-#define RX_DESCQ_TYPE_WIDTH 1
-#define RX_DESCQ_JUMBO_LBN 1
-#define RX_DESCQ_JUMBO_WIDTH 1
-#define RX_DESCQ_EN_LBN 0
-#define RX_DESCQ_EN_WIDTH 1
-
-/* Transmit descriptor pointer table */
-#define TX_DESC_PTR_TBL_KER_A1 0x11900
-#define TX_DESC_PTR_TBL_KER_B0 0xF50000
-#define TX_DESC_PTR_TBL_KER_P0 0xa40
-#define TX_NON_IP_DROP_DIS_B0_LBN 91
-#define TX_NON_IP_DROP_DIS_B0_WIDTH 1
-#define TX_IP_CHKSM_DIS_B0_LBN 90
-#define TX_IP_CHKSM_DIS_B0_WIDTH 1
-#define TX_TCP_CHKSM_DIS_B0_LBN 89
-#define TX_TCP_CHKSM_DIS_B0_WIDTH 1
-#define TX_DESCQ_EN_LBN 88
-#define TX_DESCQ_EN_WIDTH 1
-#define TX_ISCSI_DDIG_EN_LBN 87
-#define TX_ISCSI_DDIG_EN_WIDTH 1
-#define TX_ISCSI_HDIG_EN_LBN 86
-#define TX_ISCSI_HDIG_EN_WIDTH 1
-#define TX_DESCQ_BUF_BASE_ID_LBN 36
-#define TX_DESCQ_BUF_BASE_ID_WIDTH 20
-#define TX_DESCQ_EVQ_ID_LBN 24
-#define TX_DESCQ_EVQ_ID_WIDTH 12
-#define TX_DESCQ_OWNER_ID_LBN 10
-#define TX_DESCQ_OWNER_ID_WIDTH 14
-#define TX_DESCQ_LABEL_LBN 5
-#define TX_DESCQ_LABEL_WIDTH 5
-#define TX_DESCQ_SIZE_LBN 3
-#define TX_DESCQ_SIZE_WIDTH 2
-#define TX_DESCQ_SIZE_4K 3
-#define TX_DESCQ_SIZE_2K 2
-#define TX_DESCQ_SIZE_1K 1
-#define TX_DESCQ_SIZE_512 0
-#define TX_DESCQ_TYPE_LBN 1
-#define TX_DESCQ_TYPE_WIDTH 2
-
-/* Event queue pointer */
-#define EVQ_PTR_TBL_KER_A1 0x11a00
-#define EVQ_PTR_TBL_KER_B0 0xf60000
-#define EVQ_PTR_TBL_KER_P0 0x500
-#define EVQ_EN_LBN 23
-#define EVQ_EN_WIDTH 1
-#define EVQ_SIZE_LBN 20
-#define EVQ_SIZE_WIDTH 3
-#define EVQ_SIZE_32K 6
-#define EVQ_SIZE_16K 5
-#define EVQ_SIZE_8K 4
-#define EVQ_SIZE_4K 3
-#define EVQ_SIZE_2K 2
-#define EVQ_SIZE_1K 1
-#define EVQ_SIZE_512 0
-#define EVQ_BUF_BASE_ID_LBN 0
-#define EVQ_BUF_BASE_ID_WIDTH 20
-
-/* Event queue read pointer */
-#define EVQ_RPTR_REG_KER_A1 0x11b00
-#define EVQ_RPTR_REG_KER_B0 0xfa0000
-#define EVQ_RPTR_REG_KER_DWORD (EVQ_RPTR_REG_KER + 0)
-#define EVQ_RPTR_DWORD_LBN 0
-#define EVQ_RPTR_DWORD_WIDTH 14
-
-/* RSS indirection table */
-#define RX_RSS_INDIR_TBL_B0 0xFB0000
-#define RX_RSS_INDIR_ENT_B0_LBN 0
-#define RX_RSS_INDIR_ENT_B0_WIDTH 6
-
-/* Special buffer descriptors (full-mode) */
-#define BUF_FULL_TBL_KER_A1 0x8000
-#define BUF_FULL_TBL_KER_B0 0x800000
-#define IP_DAT_BUF_SIZE_LBN 50
-#define IP_DAT_BUF_SIZE_WIDTH 1
-#define IP_DAT_BUF_SIZE_8K 1
-#define IP_DAT_BUF_SIZE_4K 0
-#define BUF_ADR_REGION_LBN 48
-#define BUF_ADR_REGION_WIDTH 2
-#define BUF_ADR_FBUF_LBN 14
-#define BUF_ADR_FBUF_WIDTH 34
-#define BUF_OWNER_ID_FBUF_LBN 0
-#define BUF_OWNER_ID_FBUF_WIDTH 14
-
-/* Transmit descriptor */
-#define TX_KER_PORT_LBN 63
-#define TX_KER_PORT_WIDTH 1
-#define TX_KER_CONT_LBN 62
-#define TX_KER_CONT_WIDTH 1
-#define TX_KER_BYTE_CNT_LBN 48
-#define TX_KER_BYTE_CNT_WIDTH 14
-#define TX_KER_BUF_REGION_LBN 46
-#define TX_KER_BUF_REGION_WIDTH 2
-#define TX_KER_BUF_REGION0_DECODE 0
-#define TX_KER_BUF_REGION1_DECODE 1
-#define TX_KER_BUF_REGION2_DECODE 2
-#define TX_KER_BUF_REGION3_DECODE 3
-#define TX_KER_BUF_ADR_LBN 0
-#define TX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46)
-
-/* Receive descriptor */
-#define RX_KER_BUF_SIZE_LBN 48
-#define RX_KER_BUF_SIZE_WIDTH 14
-#define RX_KER_BUF_REGION_LBN 46
-#define RX_KER_BUF_REGION_WIDTH 2
-#define RX_KER_BUF_REGION0_DECODE 0
-#define RX_KER_BUF_REGION1_DECODE 1
-#define RX_KER_BUF_REGION2_DECODE 2
-#define RX_KER_BUF_REGION3_DECODE 3
-#define RX_KER_BUF_ADR_LBN 0
-#define RX_KER_BUF_ADR_WIDTH EFX_DMA_TYPE_WIDTH(46)
-
-/**************************************************************************
- *
- * Falcon events
- *
- **************************************************************************
- */
-
-/* Event queue entries */
-#define EV_CODE_LBN 60
-#define EV_CODE_WIDTH 4
-#define RX_IP_EV_DECODE 0
-#define TX_IP_EV_DECODE 2
-#define DRIVER_EV_DECODE 5
-#define GLOBAL_EV_DECODE 6
-#define DRV_GEN_EV_DECODE 7
-#define WHOLE_EVENT_LBN 0
-#define WHOLE_EVENT_WIDTH 64
-
-/* Receive events */
-#define RX_EV_PKT_OK_LBN 56
-#define RX_EV_PKT_OK_WIDTH 1
-#define RX_EV_PAUSE_FRM_ERR_LBN 55
-#define RX_EV_PAUSE_FRM_ERR_WIDTH 1
-#define RX_EV_BUF_OWNER_ID_ERR_LBN 54
-#define RX_EV_BUF_OWNER_ID_ERR_WIDTH 1
-#define RX_EV_IF_FRAG_ERR_LBN 53
-#define RX_EV_IF_FRAG_ERR_WIDTH 1
-#define RX_EV_IP_HDR_CHKSUM_ERR_LBN 52
-#define RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1
-#define RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51
-#define RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1
-#define RX_EV_ETH_CRC_ERR_LBN 50
-#define RX_EV_ETH_CRC_ERR_WIDTH 1
-#define RX_EV_FRM_TRUNC_LBN 49
-#define RX_EV_FRM_TRUNC_WIDTH 1
-#define RX_EV_DRIB_NIB_LBN 48
-#define RX_EV_DRIB_NIB_WIDTH 1
-#define RX_EV_TOBE_DISC_LBN 47
-#define RX_EV_TOBE_DISC_WIDTH 1
-#define RX_EV_PKT_TYPE_LBN 44
-#define RX_EV_PKT_TYPE_WIDTH 3
-#define RX_EV_PKT_TYPE_ETH_DECODE 0
-#define RX_EV_PKT_TYPE_LLC_DECODE 1
-#define RX_EV_PKT_TYPE_JUMBO_DECODE 2
-#define RX_EV_PKT_TYPE_VLAN_DECODE 3
-#define RX_EV_PKT_TYPE_VLAN_LLC_DECODE 4
-#define RX_EV_PKT_TYPE_VLAN_JUMBO_DECODE 5
-#define RX_EV_HDR_TYPE_LBN 42
-#define RX_EV_HDR_TYPE_WIDTH 2
-#define RX_EV_HDR_TYPE_TCP_IPV4_DECODE 0
-#define RX_EV_HDR_TYPE_UDP_IPV4_DECODE 1
-#define RX_EV_HDR_TYPE_OTHER_IP_DECODE 2
-#define RX_EV_HDR_TYPE_NON_IP_DECODE 3
-#define RX_EV_HDR_TYPE_HAS_CHECKSUMS(hdr_type) \
- ((hdr_type) <= RX_EV_HDR_TYPE_UDP_IPV4_DECODE)
-#define RX_EV_MCAST_HASH_MATCH_LBN 40
-#define RX_EV_MCAST_HASH_MATCH_WIDTH 1
-#define RX_EV_MCAST_PKT_LBN 39
-#define RX_EV_MCAST_PKT_WIDTH 1
-#define RX_EV_Q_LABEL_LBN 32
-#define RX_EV_Q_LABEL_WIDTH 5
-#define RX_EV_JUMBO_CONT_LBN 31
-#define RX_EV_JUMBO_CONT_WIDTH 1
-#define RX_EV_BYTE_CNT_LBN 16
-#define RX_EV_BYTE_CNT_WIDTH 14
-#define RX_EV_SOP_LBN 15
-#define RX_EV_SOP_WIDTH 1
-#define RX_EV_DESC_PTR_LBN 0
-#define RX_EV_DESC_PTR_WIDTH 12
-
-/* Transmit events */
-#define TX_EV_PKT_ERR_LBN 38
-#define TX_EV_PKT_ERR_WIDTH 1
-#define TX_EV_Q_LABEL_LBN 32
-#define TX_EV_Q_LABEL_WIDTH 5
-#define TX_EV_WQ_FF_FULL_LBN 15
-#define TX_EV_WQ_FF_FULL_WIDTH 1
-#define TX_EV_COMP_LBN 12
-#define TX_EV_COMP_WIDTH 1
-#define TX_EV_DESC_PTR_LBN 0
-#define TX_EV_DESC_PTR_WIDTH 12
-
-/* Driver events */
-#define DRIVER_EV_SUB_CODE_LBN 56
-#define DRIVER_EV_SUB_CODE_WIDTH 4
-#define DRIVER_EV_SUB_DATA_LBN 0
-#define DRIVER_EV_SUB_DATA_WIDTH 14
-#define TX_DESCQ_FLS_DONE_EV_DECODE 0
-#define RX_DESCQ_FLS_DONE_EV_DECODE 1
-#define EVQ_INIT_DONE_EV_DECODE 2
-#define EVQ_NOT_EN_EV_DECODE 3
-#define RX_DESCQ_FLSFF_OVFL_EV_DECODE 4
-#define SRM_UPD_DONE_EV_DECODE 5
-#define WAKE_UP_EV_DECODE 6
-#define TX_PKT_NON_TCP_UDP_DECODE 9
-#define TIMER_EV_DECODE 10
-#define RX_RECOVERY_EV_DECODE 11
-#define RX_DSC_ERROR_EV_DECODE 14
-#define TX_DSC_ERROR_EV_DECODE 15
-#define DRIVER_EV_TX_DESCQ_ID_LBN 0
-#define DRIVER_EV_TX_DESCQ_ID_WIDTH 12
-#define DRIVER_EV_RX_FLUSH_FAIL_LBN 12
-#define DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1
-#define DRIVER_EV_RX_DESCQ_ID_LBN 0
-#define DRIVER_EV_RX_DESCQ_ID_WIDTH 12
-#define SRM_CLR_EV_DECODE 0
-#define SRM_UPD_EV_DECODE 1
-#define SRM_ILLCLR_EV_DECODE 2
-
-/* Global events */
-#define RX_RECOVERY_B0_LBN 12
-#define RX_RECOVERY_B0_WIDTH 1
-#define XG_MNT_INTR_B0_LBN 11
-#define XG_MNT_INTR_B0_WIDTH 1
-#define RX_RECOVERY_A1_LBN 11
-#define RX_RECOVERY_A1_WIDTH 1
-#define XFP_PHY_INTR_LBN 10
-#define XFP_PHY_INTR_WIDTH 1
-#define XG_PHY_INTR_LBN 9
-#define XG_PHY_INTR_WIDTH 1
-#define G_PHY1_INTR_LBN 8
-#define G_PHY1_INTR_WIDTH 1
-#define G_PHY0_INTR_LBN 7
-#define G_PHY0_INTR_WIDTH 1
-
-/* Driver-generated test events */
-#define EVQ_MAGIC_LBN 0
-#define EVQ_MAGIC_WIDTH 32
-
-/**************************************************************************
- *
- * Falcon MAC stats
- *
- **************************************************************************
- *
- */
-
-#define GRxGoodOct_offset 0x0
-#define GRxGoodOct_WIDTH 48
-#define GRxBadOct_offset 0x8
-#define GRxBadOct_WIDTH 48
-#define GRxMissPkt_offset 0x10
-#define GRxMissPkt_WIDTH 32
-#define GRxFalseCRS_offset 0x14
-#define GRxFalseCRS_WIDTH 32
-#define GRxPausePkt_offset 0x18
-#define GRxPausePkt_WIDTH 32
-#define GRxBadPkt_offset 0x1C
-#define GRxBadPkt_WIDTH 32
-#define GRxUcastPkt_offset 0x20
-#define GRxUcastPkt_WIDTH 32
-#define GRxMcastPkt_offset 0x24
-#define GRxMcastPkt_WIDTH 32
-#define GRxBcastPkt_offset 0x28
-#define GRxBcastPkt_WIDTH 32
-#define GRxGoodLt64Pkt_offset 0x2C
-#define GRxGoodLt64Pkt_WIDTH 32
-#define GRxBadLt64Pkt_offset 0x30
-#define GRxBadLt64Pkt_WIDTH 32
-#define GRx64Pkt_offset 0x34
-#define GRx64Pkt_WIDTH 32
-#define GRx65to127Pkt_offset 0x38
-#define GRx65to127Pkt_WIDTH 32
-#define GRx128to255Pkt_offset 0x3C
-#define GRx128to255Pkt_WIDTH 32
-#define GRx256to511Pkt_offset 0x40
-#define GRx256to511Pkt_WIDTH 32
-#define GRx512to1023Pkt_offset 0x44
-#define GRx512to1023Pkt_WIDTH 32
-#define GRx1024to15xxPkt_offset 0x48
-#define GRx1024to15xxPkt_WIDTH 32
-#define GRx15xxtoJumboPkt_offset 0x4C
-#define GRx15xxtoJumboPkt_WIDTH 32
-#define GRxGtJumboPkt_offset 0x50
-#define GRxGtJumboPkt_WIDTH 32
-#define GRxFcsErr64to15xxPkt_offset 0x54
-#define GRxFcsErr64to15xxPkt_WIDTH 32
-#define GRxFcsErr15xxtoJumboPkt_offset 0x58
-#define GRxFcsErr15xxtoJumboPkt_WIDTH 32
-#define GRxFcsErrGtJumboPkt_offset 0x5C
-#define GRxFcsErrGtJumboPkt_WIDTH 32
-#define GTxGoodBadOct_offset 0x80
-#define GTxGoodBadOct_WIDTH 48
-#define GTxGoodOct_offset 0x88
-#define GTxGoodOct_WIDTH 48
-#define GTxSglColPkt_offset 0x90
-#define GTxSglColPkt_WIDTH 32
-#define GTxMultColPkt_offset 0x94
-#define GTxMultColPkt_WIDTH 32
-#define GTxExColPkt_offset 0x98
-#define GTxExColPkt_WIDTH 32
-#define GTxDefPkt_offset 0x9C
-#define GTxDefPkt_WIDTH 32
-#define GTxLateCol_offset 0xA0
-#define GTxLateCol_WIDTH 32
-#define GTxExDefPkt_offset 0xA4
-#define GTxExDefPkt_WIDTH 32
-#define GTxPausePkt_offset 0xA8
-#define GTxPausePkt_WIDTH 32
-#define GTxBadPkt_offset 0xAC
-#define GTxBadPkt_WIDTH 32
-#define GTxUcastPkt_offset 0xB0
-#define GTxUcastPkt_WIDTH 32
-#define GTxMcastPkt_offset 0xB4
-#define GTxMcastPkt_WIDTH 32
-#define GTxBcastPkt_offset 0xB8
-#define GTxBcastPkt_WIDTH 32
-#define GTxLt64Pkt_offset 0xBC
-#define GTxLt64Pkt_WIDTH 32
-#define GTx64Pkt_offset 0xC0
-#define GTx64Pkt_WIDTH 32
-#define GTx65to127Pkt_offset 0xC4
-#define GTx65to127Pkt_WIDTH 32
-#define GTx128to255Pkt_offset 0xC8
-#define GTx128to255Pkt_WIDTH 32
-#define GTx256to511Pkt_offset 0xCC
-#define GTx256to511Pkt_WIDTH 32
-#define GTx512to1023Pkt_offset 0xD0
-#define GTx512to1023Pkt_WIDTH 32
-#define GTx1024to15xxPkt_offset 0xD4
-#define GTx1024to15xxPkt_WIDTH 32
-#define GTx15xxtoJumboPkt_offset 0xD8
-#define GTx15xxtoJumboPkt_WIDTH 32
-#define GTxGtJumboPkt_offset 0xDC
-#define GTxGtJumboPkt_WIDTH 32
-#define GTxNonTcpUdpPkt_offset 0xE0
-#define GTxNonTcpUdpPkt_WIDTH 16
-#define GTxMacSrcErrPkt_offset 0xE4
-#define GTxMacSrcErrPkt_WIDTH 16
-#define GTxIpSrcErrPkt_offset 0xE8
-#define GTxIpSrcErrPkt_WIDTH 16
-#define GDmaDone_offset 0xEC
-#define GDmaDone_WIDTH 32
-
-#define XgRxOctets_offset 0x0
-#define XgRxOctets_WIDTH 48
-#define XgRxOctetsOK_offset 0x8
-#define XgRxOctetsOK_WIDTH 48
-#define XgRxPkts_offset 0x10
-#define XgRxPkts_WIDTH 32
-#define XgRxPktsOK_offset 0x14
-#define XgRxPktsOK_WIDTH 32
-#define XgRxBroadcastPkts_offset 0x18
-#define XgRxBroadcastPkts_WIDTH 32
-#define XgRxMulticastPkts_offset 0x1C
-#define XgRxMulticastPkts_WIDTH 32
-#define XgRxUnicastPkts_offset 0x20
-#define XgRxUnicastPkts_WIDTH 32
-#define XgRxUndersizePkts_offset 0x24
-#define XgRxUndersizePkts_WIDTH 32
-#define XgRxOversizePkts_offset 0x28
-#define XgRxOversizePkts_WIDTH 32
-#define XgRxJabberPkts_offset 0x2C
-#define XgRxJabberPkts_WIDTH 32
-#define XgRxUndersizeFCSerrorPkts_offset 0x30
-#define XgRxUndersizeFCSerrorPkts_WIDTH 32
-#define XgRxDropEvents_offset 0x34
-#define XgRxDropEvents_WIDTH 32
-#define XgRxFCSerrorPkts_offset 0x38
-#define XgRxFCSerrorPkts_WIDTH 32
-#define XgRxAlignError_offset 0x3C
-#define XgRxAlignError_WIDTH 32
-#define XgRxSymbolError_offset 0x40
-#define XgRxSymbolError_WIDTH 32
-#define XgRxInternalMACError_offset 0x44
-#define XgRxInternalMACError_WIDTH 32
-#define XgRxControlPkts_offset 0x48
-#define XgRxControlPkts_WIDTH 32
-#define XgRxPausePkts_offset 0x4C
-#define XgRxPausePkts_WIDTH 32
-#define XgRxPkts64Octets_offset 0x50
-#define XgRxPkts64Octets_WIDTH 32
-#define XgRxPkts65to127Octets_offset 0x54
-#define XgRxPkts65to127Octets_WIDTH 32
-#define XgRxPkts128to255Octets_offset 0x58
-#define XgRxPkts128to255Octets_WIDTH 32
-#define XgRxPkts256to511Octets_offset 0x5C
-#define XgRxPkts256to511Octets_WIDTH 32
-#define XgRxPkts512to1023Octets_offset 0x60
-#define XgRxPkts512to1023Octets_WIDTH 32
-#define XgRxPkts1024to15xxOctets_offset 0x64
-#define XgRxPkts1024to15xxOctets_WIDTH 32
-#define XgRxPkts15xxtoMaxOctets_offset 0x68
-#define XgRxPkts15xxtoMaxOctets_WIDTH 32
-#define XgRxLengthError_offset 0x6C
-#define XgRxLengthError_WIDTH 32
-#define XgTxPkts_offset 0x80
-#define XgTxPkts_WIDTH 32
-#define XgTxOctets_offset 0x88
-#define XgTxOctets_WIDTH 48
-#define XgTxMulticastPkts_offset 0x90
-#define XgTxMulticastPkts_WIDTH 32
-#define XgTxBroadcastPkts_offset 0x94
-#define XgTxBroadcastPkts_WIDTH 32
-#define XgTxUnicastPkts_offset 0x98
-#define XgTxUnicastPkts_WIDTH 32
-#define XgTxControlPkts_offset 0x9C
-#define XgTxControlPkts_WIDTH 32
-#define XgTxPausePkts_offset 0xA0
-#define XgTxPausePkts_WIDTH 32
-#define XgTxPkts64Octets_offset 0xA4
-#define XgTxPkts64Octets_WIDTH 32
-#define XgTxPkts65to127Octets_offset 0xA8
-#define XgTxPkts65to127Octets_WIDTH 32
-#define XgTxPkts128to255Octets_offset 0xAC
-#define XgTxPkts128to255Octets_WIDTH 32
-#define XgTxPkts256to511Octets_offset 0xB0
-#define XgTxPkts256to511Octets_WIDTH 32
-#define XgTxPkts512to1023Octets_offset 0xB4
-#define XgTxPkts512to1023Octets_WIDTH 32
-#define XgTxPkts1024to15xxOctets_offset 0xB8
-#define XgTxPkts1024to15xxOctets_WIDTH 32
-#define XgTxPkts1519toMaxOctets_offset 0xBC
-#define XgTxPkts1519toMaxOctets_WIDTH 32
-#define XgTxUndersizePkts_offset 0xC0
-#define XgTxUndersizePkts_WIDTH 32
-#define XgTxOversizePkts_offset 0xC4
-#define XgTxOversizePkts_WIDTH 32
-#define XgTxNonTcpUdpPkt_offset 0xC8
-#define XgTxNonTcpUdpPkt_WIDTH 16
-#define XgTxMacSrcErrPkt_offset 0xCC
-#define XgTxMacSrcErrPkt_WIDTH 16
-#define XgTxIpSrcErrPkt_offset 0xD0
-#define XgTxIpSrcErrPkt_WIDTH 16
-#define XgDmaDone_offset 0xD4
-
-#define FALCON_STATS_NOT_DONE 0x00000000
-#define FALCON_STATS_DONE 0xffffffff
-
-/* Interrupt status register bits */
-#define FATAL_INT_LBN 64
-#define FATAL_INT_WIDTH 1
-#define INT_EVQS_LBN 40
-#define INT_EVQS_WIDTH 4
-
-/**************************************************************************
- *
- * Falcon non-volatile configuration
- *
- **************************************************************************
- */
-
-/* Board configuration v2 (v1 is obsolete; later versions are compatible) */
-struct falcon_nvconfig_board_v2 {
- __le16 nports;
- u8 port0_phy_addr;
- u8 port0_phy_type;
- u8 port1_phy_addr;
- u8 port1_phy_type;
- __le16 asic_sub_revision;
- __le16 board_revision;
-} __packed;
-
-/* Board configuration v3 extra information */
-struct falcon_nvconfig_board_v3 {
- __le32 spi_device_type[2];
-} __packed;
-
-/* Bit numbers for spi_device_type */
-#define SPI_DEV_TYPE_SIZE_LBN 0
-#define SPI_DEV_TYPE_SIZE_WIDTH 5
-#define SPI_DEV_TYPE_ADDR_LEN_LBN 6
-#define SPI_DEV_TYPE_ADDR_LEN_WIDTH 2
-#define SPI_DEV_TYPE_ERASE_CMD_LBN 8
-#define SPI_DEV_TYPE_ERASE_CMD_WIDTH 8
-#define SPI_DEV_TYPE_ERASE_SIZE_LBN 16
-#define SPI_DEV_TYPE_ERASE_SIZE_WIDTH 5
-#define SPI_DEV_TYPE_BLOCK_SIZE_LBN 24
-#define SPI_DEV_TYPE_BLOCK_SIZE_WIDTH 5
-#define SPI_DEV_TYPE_FIELD(type, field) \
- (((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field)))
-
-#define NVCONFIG_OFFSET 0x300
-
-#define NVCONFIG_BOARD_MAGIC_NUM 0xFA1C
-struct falcon_nvconfig {
- efx_oword_t ee_vpd_cfg_reg; /* 0x300 */
- u8 mac_address[2][8]; /* 0x310 */
- efx_oword_t pcie_sd_ctl0123_reg; /* 0x320 */
- efx_oword_t pcie_sd_ctl45_reg; /* 0x330 */
- efx_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */
- efx_oword_t hw_init_reg; /* 0x350 */
- efx_oword_t nic_stat_reg; /* 0x360 */
- efx_oword_t glb_ctl_reg; /* 0x370 */
- efx_oword_t srm_cfg_reg; /* 0x380 */
- efx_oword_t spare_reg; /* 0x390 */
- __le16 board_magic_num; /* 0x3A0 */
- __le16 board_struct_ver;
- __le16 board_checksum;
- struct falcon_nvconfig_board_v2 board_v2;
- efx_oword_t ee_base_page_reg; /* 0x3B0 */
- struct falcon_nvconfig_board_v3 board_v3;
-} __packed;
-
-#endif /* EFX_FALCON_HWDEFS_H */
diff --git a/drivers/net/sfc/falcon_io.h b/drivers/net/sfc/falcon_io.h
deleted file mode 100644
index 8883092dae9..00000000000
--- a/drivers/net/sfc/falcon_io.h
+++ /dev/null
@@ -1,258 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_FALCON_IO_H
-#define EFX_FALCON_IO_H
-
-#include <linux/io.h>
-#include <linux/spinlock.h>
-
-/**************************************************************************
- *
- * Falcon hardware access
- *
- **************************************************************************
- *
- * Notes on locking strategy:
- *
- * Most Falcon registers require 16-byte (or 8-byte, for SRAM
- * registers) atomic writes which necessitates locking.
- * Under normal operation few writes to the Falcon BAR are made and these
- * registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special
- * cased to allow 4-byte (hence lockless) accesses.
- *
- * It *is* safe to write to these 4-byte registers in the middle of an
- * access to an 8-byte or 16-byte register. We therefore use a
- * spinlock to protect accesses to the larger registers, but no locks
- * for the 4-byte registers.
- *
- * A write barrier is needed to ensure that DW3 is written after DW0/1/2
- * due to the way the 16byte registers are "collected" in the Falcon BIU
- *
- * We also lock when carrying out reads, to ensure consistency of the
- * data (made possible since the BIU reads all 128 bits into a cache).
- * Reads are very rare, so this isn't a significant performance
- * impact. (Most data transferred from NIC to host is DMAed directly
- * into host memory).
- *
- * I/O BAR access uses locks for both reads and writes (but is only provided
- * for testing purposes).
- */
-
-/* Special buffer descriptors (Falcon SRAM) */
-#define BUF_TBL_KER_A1 0x18000
-#define BUF_TBL_KER_B0 0x800000
-
-
-#if BITS_PER_LONG == 64
-#define FALCON_USE_QWORD_IO 1
-#endif
-
-#ifdef FALCON_USE_QWORD_IO
-static inline void _falcon_writeq(struct efx_nic *efx, __le64 value,
- unsigned int reg)
-{
- __raw_writeq((__force u64)value, efx->membase + reg);
-}
-static inline __le64 _falcon_readq(struct efx_nic *efx, unsigned int reg)
-{
- return (__force __le64)__raw_readq(efx->membase + reg);
-}
-#endif
-
-static inline void _falcon_writel(struct efx_nic *efx, __le32 value,
- unsigned int reg)
-{
- __raw_writel((__force u32)value, efx->membase + reg);
-}
-static inline __le32 _falcon_readl(struct efx_nic *efx, unsigned int reg)
-{
- return (__force __le32)__raw_readl(efx->membase + reg);
-}
-
-/* Writes to a normal 16-byte Falcon register, locking as appropriate. */
-static inline void falcon_write(struct efx_nic *efx, efx_oword_t *value,
- unsigned int reg)
-{
- unsigned long flags;
-
- EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg,
- EFX_OWORD_VAL(*value));
-
- spin_lock_irqsave(&efx->biu_lock, flags);
-#ifdef FALCON_USE_QWORD_IO
- _falcon_writeq(efx, value->u64[0], reg + 0);
- wmb();
- _falcon_writeq(efx, value->u64[1], reg + 8);
-#else
- _falcon_writel(efx, value->u32[0], reg + 0);
- _falcon_writel(efx, value->u32[1], reg + 4);
- _falcon_writel(efx, value->u32[2], reg + 8);
- wmb();
- _falcon_writel(efx, value->u32[3], reg + 12);
-#endif
- mmiowb();
- spin_unlock_irqrestore(&efx->biu_lock, flags);
-}
-
-/* Writes to an 8-byte Falcon SRAM register, locking as appropriate. */
-static inline void falcon_write_sram(struct efx_nic *efx, efx_qword_t *value,
- unsigned int index)
-{
- unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value));
- unsigned long flags;
-
- EFX_REGDUMP(efx, "writing SRAM register %x with " EFX_QWORD_FMT "\n",
- reg, EFX_QWORD_VAL(*value));
-
- spin_lock_irqsave(&efx->biu_lock, flags);
-#ifdef FALCON_USE_QWORD_IO
- _falcon_writeq(efx, value->u64[0], reg + 0);
-#else
- _falcon_writel(efx, value->u32[0], reg + 0);
- wmb();
- _falcon_writel(efx, value->u32[1], reg + 4);
-#endif
- mmiowb();
- spin_unlock_irqrestore(&efx->biu_lock, flags);
-}
-
-/* Write dword to Falcon register that allows partial writes
- *
- * Some Falcon registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and
- * TX_DESC_UPD_REG) can be written to as a single dword. This allows
- * for lockless writes.
- */
-static inline void falcon_writel(struct efx_nic *efx, efx_dword_t *value,
- unsigned int reg)
-{
- EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n",
- reg, EFX_DWORD_VAL(*value));
-
- /* No lock required */
- _falcon_writel(efx, value->u32[0], reg);
-}
-
-/* Read from a Falcon register
- *
- * This reads an entire 16-byte Falcon register in one go, locking as
- * appropriate. It is essential to read the first dword first, as this
- * prompts Falcon to load the current value into the shadow register.
- */
-static inline void falcon_read(struct efx_nic *efx, efx_oword_t *value,
- unsigned int reg)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&efx->biu_lock, flags);
- value->u32[0] = _falcon_readl(efx, reg + 0);
- rmb();
- value->u32[1] = _falcon_readl(efx, reg + 4);
- value->u32[2] = _falcon_readl(efx, reg + 8);
- value->u32[3] = _falcon_readl(efx, reg + 12);
- spin_unlock_irqrestore(&efx->biu_lock, flags);
-
- EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg,
- EFX_OWORD_VAL(*value));
-}
-
-/* This reads an 8-byte Falcon SRAM entry in one go. */
-static inline void falcon_read_sram(struct efx_nic *efx, efx_qword_t *value,
- unsigned int index)
-{
- unsigned int reg = efx->type->buf_tbl_base + (index * sizeof(*value));
- unsigned long flags;
-
- spin_lock_irqsave(&efx->biu_lock, flags);
-#ifdef FALCON_USE_QWORD_IO
- value->u64[0] = _falcon_readq(efx, reg + 0);
-#else
- value->u32[0] = _falcon_readl(efx, reg + 0);
- rmb();
- value->u32[1] = _falcon_readl(efx, reg + 4);
-#endif
- spin_unlock_irqrestore(&efx->biu_lock, flags);
-
- EFX_REGDUMP(efx, "read from SRAM register %x, got "EFX_QWORD_FMT"\n",
- reg, EFX_QWORD_VAL(*value));
-}
-
-/* Read dword from Falcon register that allows partial writes (sic) */
-static inline void falcon_readl(struct efx_nic *efx, efx_dword_t *value,
- unsigned int reg)
-{
- value->u32[0] = _falcon_readl(efx, reg);
- EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n",
- reg, EFX_DWORD_VAL(*value));
-}
-
-/* Write to a register forming part of a table */
-static inline void falcon_write_table(struct efx_nic *efx, efx_oword_t *value,
- unsigned int reg, unsigned int index)
-{
- falcon_write(efx, value, reg + index * sizeof(efx_oword_t));
-}
-
-/* Read to a register forming part of a table */
-static inline void falcon_read_table(struct efx_nic *efx, efx_oword_t *value,
- unsigned int reg, unsigned int index)
-{
- falcon_read(efx, value, reg + index * sizeof(efx_oword_t));
-}
-
-/* Write to a dword register forming part of a table */
-static inline void falcon_writel_table(struct efx_nic *efx, efx_dword_t *value,
- unsigned int reg, unsigned int index)
-{
- falcon_writel(efx, value, reg + index * sizeof(efx_oword_t));
-}
-
-/* Page-mapped register block size */
-#define FALCON_PAGE_BLOCK_SIZE 0x2000
-
-/* Calculate offset to page-mapped register block */
-#define FALCON_PAGED_REG(page, reg) \
- ((page) * FALCON_PAGE_BLOCK_SIZE + (reg))
-
-/* As for falcon_write(), but for a page-mapped register. */
-static inline void falcon_write_page(struct efx_nic *efx, efx_oword_t *value,
- unsigned int reg, unsigned int page)
-{
- falcon_write(efx, value, FALCON_PAGED_REG(page, reg));
-}
-
-/* As for falcon_writel(), but for a page-mapped register. */
-static inline void falcon_writel_page(struct efx_nic *efx, efx_dword_t *value,
- unsigned int reg, unsigned int page)
-{
- falcon_writel(efx, value, FALCON_PAGED_REG(page, reg));
-}
-
-/* Write dword to Falcon page-mapped register with an extra lock.
- *
- * As for falcon_writel_page(), but for a register that suffers from
- * SFC bug 3181. If writing to page 0, take out a lock so the BIU
- * collector cannot be confused.
- */
-static inline void falcon_writel_page_locked(struct efx_nic *efx,
- efx_dword_t *value,
- unsigned int reg,
- unsigned int page)
-{
- unsigned long flags = 0;
-
- if (page == 0)
- spin_lock_irqsave(&efx->biu_lock, flags);
- falcon_writel(efx, value, FALCON_PAGED_REG(page, reg));
- if (page == 0)
- spin_unlock_irqrestore(&efx->biu_lock, flags);
-}
-
-#endif /* EFX_FALCON_IO_H */
diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c
index bec52ca37ee..3da933f8f07 100644
--- a/drivers/net/sfc/falcon_xmac.c
+++ b/drivers/net/sfc/falcon_xmac.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -11,13 +11,12 @@
#include <linux/delay.h>
#include "net_driver.h"
#include "efx.h"
-#include "falcon.h"
-#include "falcon_hwdefs.h"
-#include "falcon_io.h"
+#include "nic.h"
+#include "regs.h"
+#include "io.h"
#include "mac.h"
#include "mdio_10g.h"
#include "phy.h"
-#include "boards.h"
#include "workarounds.h"
/**************************************************************************
@@ -36,43 +35,47 @@ static void falcon_setup_xaui(struct efx_nic *efx)
if (efx->phy_type == PHY_TYPE_NONE)
return;
- falcon_read(efx, &sdctl, XX_SD_CTL_REG);
- EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT);
- EFX_SET_OWORD_FIELD(sdctl, XX_LODRVD, XX_SD_CTL_DRV_DEFAULT);
- EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT);
- EFX_SET_OWORD_FIELD(sdctl, XX_LODRVC, XX_SD_CTL_DRV_DEFAULT);
- EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT);
- EFX_SET_OWORD_FIELD(sdctl, XX_LODRVB, XX_SD_CTL_DRV_DEFAULT);
- EFX_SET_OWORD_FIELD(sdctl, XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT);
- EFX_SET_OWORD_FIELD(sdctl, XX_LODRVA, XX_SD_CTL_DRV_DEFAULT);
- falcon_write(efx, &sdctl, XX_SD_CTL_REG);
+ efx_reado(efx, &sdctl, FR_AB_XX_SD_CTL);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVD, FFE_AB_XX_SD_CTL_DRV_DEF);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVD, FFE_AB_XX_SD_CTL_DRV_DEF);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVC, FFE_AB_XX_SD_CTL_DRV_DEF);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVC, FFE_AB_XX_SD_CTL_DRV_DEF);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVB, FFE_AB_XX_SD_CTL_DRV_DEF);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVB, FFE_AB_XX_SD_CTL_DRV_DEF);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_HIDRVA, FFE_AB_XX_SD_CTL_DRV_DEF);
+ EFX_SET_OWORD_FIELD(sdctl, FRF_AB_XX_LODRVA, FFE_AB_XX_SD_CTL_DRV_DEF);
+ efx_writeo(efx, &sdctl, FR_AB_XX_SD_CTL);
EFX_POPULATE_OWORD_8(txdrv,
- XX_DEQD, XX_TXDRV_DEQ_DEFAULT,
- XX_DEQC, XX_TXDRV_DEQ_DEFAULT,
- XX_DEQB, XX_TXDRV_DEQ_DEFAULT,
- XX_DEQA, XX_TXDRV_DEQ_DEFAULT,
- XX_DTXD, XX_TXDRV_DTX_DEFAULT,
- XX_DTXC, XX_TXDRV_DTX_DEFAULT,
- XX_DTXB, XX_TXDRV_DTX_DEFAULT,
- XX_DTXA, XX_TXDRV_DTX_DEFAULT);
- falcon_write(efx, &txdrv, XX_TXDRV_CTL_REG);
+ FRF_AB_XX_DEQD, FFE_AB_XX_TXDRV_DEQ_DEF,
+ FRF_AB_XX_DEQC, FFE_AB_XX_TXDRV_DEQ_DEF,
+ FRF_AB_XX_DEQB, FFE_AB_XX_TXDRV_DEQ_DEF,
+ FRF_AB_XX_DEQA, FFE_AB_XX_TXDRV_DEQ_DEF,
+ FRF_AB_XX_DTXD, FFE_AB_XX_TXDRV_DTX_DEF,
+ FRF_AB_XX_DTXC, FFE_AB_XX_TXDRV_DTX_DEF,
+ FRF_AB_XX_DTXB, FFE_AB_XX_TXDRV_DTX_DEF,
+ FRF_AB_XX_DTXA, FFE_AB_XX_TXDRV_DTX_DEF);
+ efx_writeo(efx, &txdrv, FR_AB_XX_TXDRV_CTL);
}
int falcon_reset_xaui(struct efx_nic *efx)
{
+ struct falcon_nic_data *nic_data = efx->nic_data;
efx_oword_t reg;
int count;
+ /* Don't fetch MAC statistics over an XMAC reset */
+ WARN_ON(nic_data->stats_disable_count == 0);
+
/* Start reset sequence */
- EFX_POPULATE_DWORD_1(reg, XX_RST_XX_EN, 1);
- falcon_write(efx, &reg, XX_PWR_RST_REG);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_XX_RST_XX_EN, 1);
+ efx_writeo(efx, &reg, FR_AB_XX_PWR_RST);
/* Wait up to 10 ms for completion, then reinitialise */
for (count = 0; count < 1000; count++) {
- falcon_read(efx, &reg, XX_PWR_RST_REG);
- if (EFX_OWORD_FIELD(reg, XX_RST_XX_EN) == 0 &&
- EFX_OWORD_FIELD(reg, XX_SD_RST_ACT) == 0) {
+ efx_reado(efx, &reg, FR_AB_XX_PWR_RST);
+ if (EFX_OWORD_FIELD(reg, FRF_AB_XX_RST_XX_EN) == 0 &&
+ EFX_OWORD_FIELD(reg, FRF_AB_XX_SD_RST_ACT) == 0) {
falcon_setup_xaui(efx);
return 0;
}
@@ -86,30 +89,30 @@ static void falcon_mask_status_intr(struct efx_nic *efx, bool enable)
{
efx_oword_t reg;
- if ((falcon_rev(efx) != FALCON_REV_B0) || LOOPBACK_INTERNAL(efx))
+ if ((efx_nic_rev(efx) != EFX_REV_FALCON_B0) || LOOPBACK_INTERNAL(efx))
return;
/* We expect xgmii faults if the wireside link is up */
- if (!EFX_WORKAROUND_5147(efx) || !efx->link_up)
+ if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up)
return;
/* We can only use this interrupt to signal the negative edge of
* xaui_align [we have to poll the positive edge]. */
- if (!efx->mac_up)
+ if (efx->xmac_poll_required)
return;
/* Flush the ISR */
if (enable)
- falcon_read(efx, &reg, XM_MGT_INT_REG_B0);
+ efx_reado(efx, &reg, FR_AB_XM_MGT_INT_MSK);
EFX_POPULATE_OWORD_2(reg,
- XM_MSK_RMTFLT, !enable,
- XM_MSK_LCLFLT, !enable);
- falcon_write(efx, &reg, XM_MGT_INT_MSK_REG_B0);
+ FRF_AB_XM_MSK_RMTFLT, !enable,
+ FRF_AB_XM_MSK_LCLFLT, !enable);
+ efx_writeo(efx, &reg, FR_AB_XM_MGT_INT_MASK);
}
/* Get status of XAUI link */
-bool falcon_xaui_link_ok(struct efx_nic *efx)
+static bool falcon_xaui_link_ok(struct efx_nic *efx)
{
efx_oword_t reg;
bool align_done, link_ok = false;
@@ -119,84 +122,79 @@ bool falcon_xaui_link_ok(struct efx_nic *efx)
return true;
/* Read link status */
- falcon_read(efx, &reg, XX_CORE_STAT_REG);
+ efx_reado(efx, &reg, FR_AB_XX_CORE_STAT);
- align_done = EFX_OWORD_FIELD(reg, XX_ALIGN_DONE);
- sync_status = EFX_OWORD_FIELD(reg, XX_SYNC_STAT);
- if (align_done && (sync_status == XX_SYNC_STAT_DECODE_SYNCED))
+ align_done = EFX_OWORD_FIELD(reg, FRF_AB_XX_ALIGN_DONE);
+ sync_status = EFX_OWORD_FIELD(reg, FRF_AB_XX_SYNC_STAT);
+ if (align_done && (sync_status == FFE_AB_XX_STAT_ALL_LANES))
link_ok = true;
/* Clear link status ready for next read */
- EFX_SET_OWORD_FIELD(reg, XX_COMMA_DET, XX_COMMA_DET_RESET);
- EFX_SET_OWORD_FIELD(reg, XX_CHARERR, XX_CHARERR_RESET);
- EFX_SET_OWORD_FIELD(reg, XX_DISPERR, XX_DISPERR_RESET);
- falcon_write(efx, &reg, XX_CORE_STAT_REG);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_COMMA_DET, FFE_AB_XX_STAT_ALL_LANES);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_CHAR_ERR, FFE_AB_XX_STAT_ALL_LANES);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_DISPERR, FFE_AB_XX_STAT_ALL_LANES);
+ efx_writeo(efx, &reg, FR_AB_XX_CORE_STAT);
/* If the link is up, then check the phy side of the xaui link */
- if (efx->link_up && link_ok)
- if (efx->phy_op->mmds & (1 << MDIO_MMD_PHYXS))
+ if (efx->link_state.up && link_ok)
+ if (efx->mdio.mmds & (1 << MDIO_MMD_PHYXS))
link_ok = efx_mdio_phyxgxs_lane_sync(efx);
return link_ok;
}
-static void falcon_reconfigure_xmac_core(struct efx_nic *efx)
+void falcon_reconfigure_xmac_core(struct efx_nic *efx)
{
unsigned int max_frame_len;
efx_oword_t reg;
- bool rx_fc = !!(efx->link_fc & EFX_FC_RX);
+ bool rx_fc = !!(efx->link_state.fc & EFX_FC_RX);
+ bool tx_fc = !!(efx->link_state.fc & EFX_FC_TX);
/* Configure MAC - cut-thru mode is hard wired on */
- EFX_POPULATE_DWORD_3(reg,
- XM_RX_JUMBO_MODE, 1,
- XM_TX_STAT_EN, 1,
- XM_RX_STAT_EN, 1);
- falcon_write(efx, &reg, XM_GLB_CFG_REG);
+ EFX_POPULATE_OWORD_3(reg,
+ FRF_AB_XM_RX_JUMBO_MODE, 1,
+ FRF_AB_XM_TX_STAT_EN, 1,
+ FRF_AB_XM_RX_STAT_EN, 1);
+ efx_writeo(efx, &reg, FR_AB_XM_GLB_CFG);
/* Configure TX */
- EFX_POPULATE_DWORD_6(reg,
- XM_TXEN, 1,
- XM_TX_PRMBL, 1,
- XM_AUTO_PAD, 1,
- XM_TXCRC, 1,
- XM_FCNTL, 1,
- XM_IPG, 0x3);
- falcon_write(efx, &reg, XM_TX_CFG_REG);
+ EFX_POPULATE_OWORD_6(reg,
+ FRF_AB_XM_TXEN, 1,
+ FRF_AB_XM_TX_PRMBL, 1,
+ FRF_AB_XM_AUTO_PAD, 1,
+ FRF_AB_XM_TXCRC, 1,
+ FRF_AB_XM_FCNTL, tx_fc,
+ FRF_AB_XM_IPG, 0x3);
+ efx_writeo(efx, &reg, FR_AB_XM_TX_CFG);
/* Configure RX */
- EFX_POPULATE_DWORD_5(reg,
- XM_RXEN, 1,
- XM_AUTO_DEPAD, 0,
- XM_ACPT_ALL_MCAST, 1,
- XM_ACPT_ALL_UCAST, efx->promiscuous,
- XM_PASS_CRC_ERR, 1);
- falcon_write(efx, &reg, XM_RX_CFG_REG);
+ EFX_POPULATE_OWORD_5(reg,
+ FRF_AB_XM_RXEN, 1,
+ FRF_AB_XM_AUTO_DEPAD, 0,
+ FRF_AB_XM_ACPT_ALL_MCAST, 1,
+ FRF_AB_XM_ACPT_ALL_UCAST, efx->promiscuous,
+ FRF_AB_XM_PASS_CRC_ERR, 1);
+ efx_writeo(efx, &reg, FR_AB_XM_RX_CFG);
/* Set frame length */
max_frame_len = EFX_MAX_FRAME_LEN(efx->net_dev->mtu);
- EFX_POPULATE_DWORD_1(reg, XM_MAX_RX_FRM_SIZE, max_frame_len);
- falcon_write(efx, &reg, XM_RX_PARAM_REG);
- EFX_POPULATE_DWORD_2(reg,
- XM_MAX_TX_FRM_SIZE, max_frame_len,
- XM_TX_JUMBO_MODE, 1);
- falcon_write(efx, &reg, XM_TX_PARAM_REG);
-
- EFX_POPULATE_DWORD_2(reg,
- XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */
- XM_DIS_FCNTL, !rx_fc);
- falcon_write(efx, &reg, XM_FC_REG);
+ EFX_POPULATE_OWORD_1(reg, FRF_AB_XM_MAX_RX_FRM_SIZE, max_frame_len);
+ efx_writeo(efx, &reg, FR_AB_XM_RX_PARAM);
+ EFX_POPULATE_OWORD_2(reg,
+ FRF_AB_XM_MAX_TX_FRM_SIZE, max_frame_len,
+ FRF_AB_XM_TX_JUMBO_MODE, 1);
+ efx_writeo(efx, &reg, FR_AB_XM_TX_PARAM);
+
+ EFX_POPULATE_OWORD_2(reg,
+ FRF_AB_XM_PAUSE_TIME, 0xfffe, /* MAX PAUSE TIME */
+ FRF_AB_XM_DIS_FCNTL, !rx_fc);
+ efx_writeo(efx, &reg, FR_AB_XM_FC);
/* Set MAC address */
- EFX_POPULATE_DWORD_4(reg,
- XM_ADR_0, efx->net_dev->dev_addr[0],
- XM_ADR_1, efx->net_dev->dev_addr[1],
- XM_ADR_2, efx->net_dev->dev_addr[2],
- XM_ADR_3, efx->net_dev->dev_addr[3]);
- falcon_write(efx, &reg, XM_ADR_LO_REG);
- EFX_POPULATE_DWORD_2(reg,
- XM_ADR_4, efx->net_dev->dev_addr[4],
- XM_ADR_5, efx->net_dev->dev_addr[5]);
- falcon_write(efx, &reg, XM_ADR_HI_REG);
+ memcpy(&reg, &efx->net_dev->dev_addr[0], 4);
+ efx_writeo(efx, &reg, FR_AB_XM_ADR_LO);
+ memcpy(&reg, &efx->net_dev->dev_addr[4], 2);
+ efx_writeo(efx, &reg, FR_AB_XM_ADR_HI);
}
static void falcon_reconfigure_xgxs_core(struct efx_nic *efx)
@@ -212,12 +210,13 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx)
bool old_xgmii_loopback, old_xgxs_loopback, old_xaui_loopback;
bool reset_xgxs;
- falcon_read(efx, &reg, XX_CORE_STAT_REG);
- old_xgxs_loopback = EFX_OWORD_FIELD(reg, XX_XGXS_LB_EN);
- old_xgmii_loopback = EFX_OWORD_FIELD(reg, XX_XGMII_LB_EN);
+ efx_reado(efx, &reg, FR_AB_XX_CORE_STAT);
+ old_xgxs_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN);
+ old_xgmii_loopback =
+ EFX_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN);
- falcon_read(efx, &reg, XX_SD_CTL_REG);
- old_xaui_loopback = EFX_OWORD_FIELD(reg, XX_LPBKA);
+ efx_reado(efx, &reg, FR_AB_XX_SD_CTL);
+ old_xaui_loopback = EFX_OWORD_FIELD(reg, FRF_AB_XX_LPBKA);
/* The PHY driver may have turned XAUI off */
reset_xgxs = ((xgxs_loopback != old_xgxs_loopback) ||
@@ -228,45 +227,55 @@ static void falcon_reconfigure_xgxs_core(struct efx_nic *efx)
falcon_reset_xaui(efx);
}
- falcon_read(efx, &reg, XX_CORE_STAT_REG);
- EFX_SET_OWORD_FIELD(reg, XX_FORCE_SIG,
+ efx_reado(efx, &reg, FR_AB_XX_CORE_STAT);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_FORCE_SIG,
(xgxs_loopback || xaui_loopback) ?
- XX_FORCE_SIG_DECODE_FORCED : 0);
- EFX_SET_OWORD_FIELD(reg, XX_XGXS_LB_EN, xgxs_loopback);
- EFX_SET_OWORD_FIELD(reg, XX_XGMII_LB_EN, xgmii_loopback);
- falcon_write(efx, &reg, XX_CORE_STAT_REG);
-
- falcon_read(efx, &reg, XX_SD_CTL_REG);
- EFX_SET_OWORD_FIELD(reg, XX_LPBKD, xaui_loopback);
- EFX_SET_OWORD_FIELD(reg, XX_LPBKC, xaui_loopback);
- EFX_SET_OWORD_FIELD(reg, XX_LPBKB, xaui_loopback);
- EFX_SET_OWORD_FIELD(reg, XX_LPBKA, xaui_loopback);
- falcon_write(efx, &reg, XX_SD_CTL_REG);
+ FFE_AB_XX_FORCE_SIG_ALL_LANES : 0);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGXS_LB_EN, xgxs_loopback);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_XGMII_LB_EN, xgmii_loopback);
+ efx_writeo(efx, &reg, FR_AB_XX_CORE_STAT);
+
+ efx_reado(efx, &reg, FR_AB_XX_SD_CTL);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKD, xaui_loopback);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKC, xaui_loopback);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKB, xaui_loopback);
+ EFX_SET_OWORD_FIELD(reg, FRF_AB_XX_LPBKA, xaui_loopback);
+ efx_writeo(efx, &reg, FR_AB_XX_SD_CTL);
}
-/* Try and bring the Falcon side of the Falcon-Phy XAUI link fails
- * to come back up. Bash it until it comes back up */
-static void falcon_check_xaui_link_up(struct efx_nic *efx, int tries)
+/* Try to bring up the Falcon side of the Falcon-Phy XAUI link */
+static bool falcon_check_xaui_link_up(struct efx_nic *efx, int tries)
{
- efx->mac_up = falcon_xaui_link_ok(efx);
+ bool mac_up = falcon_xaui_link_ok(efx);
- if ((efx->loopback_mode == LOOPBACK_NETWORK) ||
+ if (LOOPBACK_MASK(efx) & LOOPBACKS_EXTERNAL(efx) & LOOPBACKS_WS ||
efx_phy_mode_disabled(efx->phy_mode))
/* XAUI link is expected to be down */
- return;
+ return mac_up;
- while (!efx->mac_up && tries) {
+ falcon_stop_nic_stats(efx);
+
+ while (!mac_up && tries) {
EFX_LOG(efx, "bashing xaui\n");
falcon_reset_xaui(efx);
udelay(200);
- efx->mac_up = falcon_xaui_link_ok(efx);
+ mac_up = falcon_xaui_link_ok(efx);
--tries;
}
+
+ falcon_start_nic_stats(efx);
+
+ return mac_up;
}
-static void falcon_reconfigure_xmac(struct efx_nic *efx)
+static bool falcon_xmac_check_fault(struct efx_nic *efx)
+{
+ return !falcon_check_xaui_link_up(efx, 5);
+}
+
+static int falcon_reconfigure_xmac(struct efx_nic *efx)
{
falcon_mask_status_intr(efx, false);
@@ -275,18 +284,15 @@ static void falcon_reconfigure_xmac(struct efx_nic *efx)
falcon_reconfigure_mac_wrapper(efx);
- falcon_check_xaui_link_up(efx, 5);
+ efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 5);
falcon_mask_status_intr(efx, true);
+
+ return 0;
}
static void falcon_update_stats_xmac(struct efx_nic *efx)
{
struct efx_mac_stats *mac_stats = &efx->mac_stats;
- int rc;
-
- rc = falcon_dma_stats(efx, XgDmaDone_offset);
- if (rc)
- return;
/* Update MAC stats from DMAed values */
FALCON_STAT(efx, XgRxOctets, rx_bytes);
@@ -344,35 +350,19 @@ static void falcon_update_stats_xmac(struct efx_nic *efx)
mac_stats->rx_control * 64);
}
-static void falcon_xmac_irq(struct efx_nic *efx)
-{
- /* The XGMII link has a transient fault, which indicates either:
- * - there's a transient xgmii fault
- * - falcon's end of the xaui link may need a kick
- * - the wire-side link may have gone down, but the lasi/poll()
- * hasn't noticed yet.
- *
- * We only want to even bother polling XAUI if we're confident it's
- * not (1) or (3). In both cases, the only reliable way to spot this
- * is to wait a bit. We do this here by forcing the mac link state
- * to down, and waiting for the mac poll to come round and check
- */
- efx->mac_up = false;
-}
-
-static void falcon_poll_xmac(struct efx_nic *efx)
+void falcon_poll_xmac(struct efx_nic *efx)
{
- if (!EFX_WORKAROUND_5147(efx) || !efx->link_up || efx->mac_up)
+ if (!EFX_WORKAROUND_5147(efx) || !efx->link_state.up ||
+ !efx->xmac_poll_required)
return;
falcon_mask_status_intr(efx, false);
- falcon_check_xaui_link_up(efx, 1);
+ efx->xmac_poll_required = !falcon_check_xaui_link_up(efx, 1);
falcon_mask_status_intr(efx, true);
}
struct efx_mac_operations falcon_xmac_operations = {
.reconfigure = falcon_reconfigure_xmac,
.update_stats = falcon_update_stats_xmac,
- .irq = falcon_xmac_irq,
- .poll = falcon_poll_xmac,
+ .check_fault = falcon_xmac_check_fault,
};
diff --git a/drivers/net/sfc/gmii.h b/drivers/net/sfc/gmii.h
deleted file mode 100644
index dfccaa7b573..00000000000
--- a/drivers/net/sfc/gmii.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_GMII_H
-#define EFX_GMII_H
-
-/*
- * GMII interface
- */
-
-#include <linux/mii.h>
-
-/* GMII registers, excluding registers already defined as MII
- * registers in mii.h
- */
-#define GMII_IER 0x12 /* Interrupt enable register */
-#define GMII_ISR 0x13 /* Interrupt status register */
-
-/* Interrupt enable register */
-#define IER_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */
-#define IER_SPEED_CHG 0x4000 /* Bit 14 - speed changed */
-#define IER_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */
-#define IER_PAGE_RCVD 0x1000 /* Bit 12 - page received */
-#define IER_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */
-#define IER_LINK_CHG 0x0400 /* Bit 10 - link status changed */
-#define IER_SYM_ERR 0x0200 /* Bit 9 - symbol error */
-#define IER_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */
-#define IER_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */
-#define IER_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */
-#define IER_DOWNSHIFT 0x0020 /* Bit 5 - downshift */
-#define IER_ENERGY 0x0010 /* Bit 4 - energy detect */
-#define IER_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */
-#define IER_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */
-#define IER_JABBER 0x0001 /* Bit 0 - jabber */
-
-/* Interrupt status register */
-#define ISR_ANEG_ERR 0x8000 /* Bit 15 - autonegotiation error */
-#define ISR_SPEED_CHG 0x4000 /* Bit 14 - speed changed */
-#define ISR_DUPLEX_CHG 0x2000 /* Bit 13 - duplex changed */
-#define ISR_PAGE_RCVD 0x1000 /* Bit 12 - page received */
-#define ISR_ANEG_DONE 0x0800 /* Bit 11 - autonegotiation complete */
-#define ISR_LINK_CHG 0x0400 /* Bit 10 - link status changed */
-#define ISR_SYM_ERR 0x0200 /* Bit 9 - symbol error */
-#define ISR_FALSE_CARRIER 0x0100 /* Bit 8 - false carrier */
-#define ISR_FIFO_ERR 0x0080 /* Bit 7 - FIFO over/underflow */
-#define ISR_MDIX_CHG 0x0040 /* Bit 6 - MDI crossover changed */
-#define ISR_DOWNSHIFT 0x0020 /* Bit 5 - downshift */
-#define ISR_ENERGY 0x0010 /* Bit 4 - energy detect */
-#define ISR_DTE_POWER 0x0004 /* Bit 2 - DTE power detect */
-#define ISR_POLARITY_CHG 0x0002 /* Bit 1 - polarity changed */
-#define ISR_JABBER 0x0001 /* Bit 0 - jabber */
-
-#endif /* EFX_GMII_H */
diff --git a/drivers/net/sfc/io.h b/drivers/net/sfc/io.h
new file mode 100644
index 00000000000..b89177c27f4
--- /dev/null
+++ b/drivers/net/sfc/io.h
@@ -0,0 +1,256 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_IO_H
+#define EFX_IO_H
+
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+/**************************************************************************
+ *
+ * NIC register I/O
+ *
+ **************************************************************************
+ *
+ * Notes on locking strategy:
+ *
+ * Most NIC registers require 16-byte (or 8-byte, for SRAM) atomic writes
+ * which necessitates locking.
+ * Under normal operation few writes to NIC registers are made and these
+ * registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and TX_DESC_UPD_REG) are special
+ * cased to allow 4-byte (hence lockless) accesses.
+ *
+ * It *is* safe to write to these 4-byte registers in the middle of an
+ * access to an 8-byte or 16-byte register. We therefore use a
+ * spinlock to protect accesses to the larger registers, but no locks
+ * for the 4-byte registers.
+ *
+ * A write barrier is needed to ensure that DW3 is written after DW0/1/2
+ * due to the way the 16byte registers are "collected" in the BIU.
+ *
+ * We also lock when carrying out reads, to ensure consistency of the
+ * data (made possible since the BIU reads all 128 bits into a cache).
+ * Reads are very rare, so this isn't a significant performance
+ * impact. (Most data transferred from NIC to host is DMAed directly
+ * into host memory).
+ *
+ * I/O BAR access uses locks for both reads and writes (but is only provided
+ * for testing purposes).
+ */
+
+#if BITS_PER_LONG == 64
+#define EFX_USE_QWORD_IO 1
+#endif
+
+#ifdef EFX_USE_QWORD_IO
+static inline void _efx_writeq(struct efx_nic *efx, __le64 value,
+ unsigned int reg)
+{
+ __raw_writeq((__force u64)value, efx->membase + reg);
+}
+static inline __le64 _efx_readq(struct efx_nic *efx, unsigned int reg)
+{
+ return (__force __le64)__raw_readq(efx->membase + reg);
+}
+#endif
+
+static inline void _efx_writed(struct efx_nic *efx, __le32 value,
+ unsigned int reg)
+{
+ __raw_writel((__force u32)value, efx->membase + reg);
+}
+static inline __le32 _efx_readd(struct efx_nic *efx, unsigned int reg)
+{
+ return (__force __le32)__raw_readl(efx->membase + reg);
+}
+
+/* Writes to a normal 16-byte Efx register, locking as appropriate. */
+static inline void efx_writeo(struct efx_nic *efx, efx_oword_t *value,
+ unsigned int reg)
+{
+ unsigned long flags __attribute__ ((unused));
+
+ EFX_REGDUMP(efx, "writing register %x with " EFX_OWORD_FMT "\n", reg,
+ EFX_OWORD_VAL(*value));
+
+ spin_lock_irqsave(&efx->biu_lock, flags);
+#ifdef EFX_USE_QWORD_IO
+ _efx_writeq(efx, value->u64[0], reg + 0);
+ wmb();
+ _efx_writeq(efx, value->u64[1], reg + 8);
+#else
+ _efx_writed(efx, value->u32[0], reg + 0);
+ _efx_writed(efx, value->u32[1], reg + 4);
+ _efx_writed(efx, value->u32[2], reg + 8);
+ wmb();
+ _efx_writed(efx, value->u32[3], reg + 12);
+#endif
+ mmiowb();
+ spin_unlock_irqrestore(&efx->biu_lock, flags);
+}
+
+/* Write an 8-byte NIC SRAM entry through the supplied mapping,
+ * locking as appropriate. */
+static inline void efx_sram_writeq(struct efx_nic *efx, void __iomem *membase,
+ efx_qword_t *value, unsigned int index)
+{
+ unsigned int addr = index * sizeof(*value);
+ unsigned long flags __attribute__ ((unused));
+
+ EFX_REGDUMP(efx, "writing SRAM address %x with " EFX_QWORD_FMT "\n",
+ addr, EFX_QWORD_VAL(*value));
+
+ spin_lock_irqsave(&efx->biu_lock, flags);
+#ifdef EFX_USE_QWORD_IO
+ __raw_writeq((__force u64)value->u64[0], membase + addr);
+#else
+ __raw_writel((__force u32)value->u32[0], membase + addr);
+ wmb();
+ __raw_writel((__force u32)value->u32[1], membase + addr + 4);
+#endif
+ mmiowb();
+ spin_unlock_irqrestore(&efx->biu_lock, flags);
+}
+
+/* Write dword to NIC register that allows partial writes
+ *
+ * Some registers (EVQ_RPTR_REG, RX_DESC_UPD_REG and
+ * TX_DESC_UPD_REG) can be written to as a single dword. This allows
+ * for lockless writes.
+ */
+static inline void efx_writed(struct efx_nic *efx, efx_dword_t *value,
+ unsigned int reg)
+{
+ EFX_REGDUMP(efx, "writing partial register %x with "EFX_DWORD_FMT"\n",
+ reg, EFX_DWORD_VAL(*value));
+
+ /* No lock required */
+ _efx_writed(efx, value->u32[0], reg);
+}
+
+/* Read from a NIC register
+ *
+ * This reads an entire 16-byte register in one go, locking as
+ * appropriate. It is essential to read the first dword first, as this
+ * prompts the NIC to load the current value into the shadow register.
+ */
+static inline void efx_reado(struct efx_nic *efx, efx_oword_t *value,
+ unsigned int reg)
+{
+ unsigned long flags __attribute__ ((unused));
+
+ spin_lock_irqsave(&efx->biu_lock, flags);
+ value->u32[0] = _efx_readd(efx, reg + 0);
+ rmb();
+ value->u32[1] = _efx_readd(efx, reg + 4);
+ value->u32[2] = _efx_readd(efx, reg + 8);
+ value->u32[3] = _efx_readd(efx, reg + 12);
+ spin_unlock_irqrestore(&efx->biu_lock, flags);
+
+ EFX_REGDUMP(efx, "read from register %x, got " EFX_OWORD_FMT "\n", reg,
+ EFX_OWORD_VAL(*value));
+}
+
+/* Read an 8-byte SRAM entry through supplied mapping,
+ * locking as appropriate. */
+static inline void efx_sram_readq(struct efx_nic *efx, void __iomem *membase,
+ efx_qword_t *value, unsigned int index)
+{
+ unsigned int addr = index * sizeof(*value);
+ unsigned long flags __attribute__ ((unused));
+
+ spin_lock_irqsave(&efx->biu_lock, flags);
+#ifdef EFX_USE_QWORD_IO
+ value->u64[0] = (__force __le64)__raw_readq(membase + addr);
+#else
+ value->u32[0] = (__force __le32)__raw_readl(membase + addr);
+ rmb();
+ value->u32[1] = (__force __le32)__raw_readl(membase + addr + 4);
+#endif
+ spin_unlock_irqrestore(&efx->biu_lock, flags);
+
+ EFX_REGDUMP(efx, "read from SRAM address %x, got "EFX_QWORD_FMT"\n",
+ addr, EFX_QWORD_VAL(*value));
+}
+
+/* Read dword from register that allows partial writes (sic) */
+static inline void efx_readd(struct efx_nic *efx, efx_dword_t *value,
+ unsigned int reg)
+{
+ value->u32[0] = _efx_readd(efx, reg);
+ EFX_REGDUMP(efx, "read from register %x, got "EFX_DWORD_FMT"\n",
+ reg, EFX_DWORD_VAL(*value));
+}
+
+/* Write to a register forming part of a table */
+static inline void efx_writeo_table(struct efx_nic *efx, efx_oword_t *value,
+ unsigned int reg, unsigned int index)
+{
+ efx_writeo(efx, value, reg + index * sizeof(efx_oword_t));
+}
+
+/* Read to a register forming part of a table */
+static inline void efx_reado_table(struct efx_nic *efx, efx_oword_t *value,
+ unsigned int reg, unsigned int index)
+{
+ efx_reado(efx, value, reg + index * sizeof(efx_oword_t));
+}
+
+/* Write to a dword register forming part of a table */
+static inline void efx_writed_table(struct efx_nic *efx, efx_dword_t *value,
+ unsigned int reg, unsigned int index)
+{
+ efx_writed(efx, value, reg + index * sizeof(efx_oword_t));
+}
+
+/* Page-mapped register block size */
+#define EFX_PAGE_BLOCK_SIZE 0x2000
+
+/* Calculate offset to page-mapped register block */
+#define EFX_PAGED_REG(page, reg) \
+ ((page) * EFX_PAGE_BLOCK_SIZE + (reg))
+
+/* As for efx_writeo(), but for a page-mapped register. */
+static inline void efx_writeo_page(struct efx_nic *efx, efx_oword_t *value,
+ unsigned int reg, unsigned int page)
+{
+ efx_writeo(efx, value, EFX_PAGED_REG(page, reg));
+}
+
+/* As for efx_writed(), but for a page-mapped register. */
+static inline void efx_writed_page(struct efx_nic *efx, efx_dword_t *value,
+ unsigned int reg, unsigned int page)
+{
+ efx_writed(efx, value, EFX_PAGED_REG(page, reg));
+}
+
+/* Write dword to page-mapped register with an extra lock.
+ *
+ * As for efx_writed_page(), but for a register that suffers from
+ * SFC bug 3181. Take out a lock so the BIU collector cannot be
+ * confused. */
+static inline void efx_writed_page_locked(struct efx_nic *efx,
+ efx_dword_t *value,
+ unsigned int reg,
+ unsigned int page)
+{
+ unsigned long flags __attribute__ ((unused));
+
+ if (page == 0) {
+ spin_lock_irqsave(&efx->biu_lock, flags);
+ efx_writed(efx, value, EFX_PAGED_REG(page, reg));
+ spin_unlock_irqrestore(&efx->biu_lock, flags);
+ } else {
+ efx_writed(efx, value, EFX_PAGED_REG(page, reg));
+ }
+}
+
+#endif /* EFX_IO_H */
diff --git a/drivers/net/sfc/mac.h b/drivers/net/sfc/mac.h
index 4e7074278fe..f1aa5f37489 100644
--- a/drivers/net/sfc/mac.h
+++ b/drivers/net/sfc/mac.h
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -15,5 +15,9 @@
extern struct efx_mac_operations falcon_gmac_operations;
extern struct efx_mac_operations falcon_xmac_operations;
+extern struct efx_mac_operations efx_mcdi_mac_operations;
+extern void falcon_reconfigure_xmac_core(struct efx_nic *efx);
+extern int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr,
+ u32 dma_len, int enable, int clear);
#endif
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c
new file mode 100644
index 00000000000..683353b904c
--- /dev/null
+++ b/drivers/net/sfc/mcdi.c
@@ -0,0 +1,1112 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2008-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/delay.h>
+#include "net_driver.h"
+#include "nic.h"
+#include "io.h"
+#include "regs.h"
+#include "mcdi_pcol.h"
+#include "phy.h"
+
+/**************************************************************************
+ *
+ * Management-Controller-to-Driver Interface
+ *
+ **************************************************************************
+ */
+
+/* Software-defined structure to the shared-memory */
+#define CMD_NOTIFY_PORT0 0
+#define CMD_NOTIFY_PORT1 4
+#define CMD_PDU_PORT0 0x008
+#define CMD_PDU_PORT1 0x108
+#define REBOOT_FLAG_PORT0 0x3f8
+#define REBOOT_FLAG_PORT1 0x3fc
+
+#define MCDI_RPC_TIMEOUT 10 /*seconds */
+
+#define MCDI_PDU(efx) \
+ (efx_port_num(efx) ? CMD_PDU_PORT1 : CMD_PDU_PORT0)
+#define MCDI_DOORBELL(efx) \
+ (efx_port_num(efx) ? CMD_NOTIFY_PORT1 : CMD_NOTIFY_PORT0)
+#define MCDI_REBOOT_FLAG(efx) \
+ (efx_port_num(efx) ? REBOOT_FLAG_PORT1 : REBOOT_FLAG_PORT0)
+
+#define SEQ_MASK \
+ EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ))
+
+static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx)
+{
+ struct siena_nic_data *nic_data;
+ EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
+ nic_data = efx->nic_data;
+ return &nic_data->mcdi;
+}
+
+void efx_mcdi_init(struct efx_nic *efx)
+{
+ struct efx_mcdi_iface *mcdi;
+
+ if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
+ return;
+
+ mcdi = efx_mcdi(efx);
+ init_waitqueue_head(&mcdi->wq);
+ spin_lock_init(&mcdi->iface_lock);
+ atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT);
+ mcdi->mode = MCDI_MODE_POLL;
+
+ (void) efx_mcdi_poll_reboot(efx);
+}
+
+static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
+ const u8 *inbuf, size_t inlen)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+ unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
+ unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
+ unsigned int i;
+ efx_dword_t hdr;
+ u32 xflags, seqno;
+
+ BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
+ BUG_ON(inlen & 3 || inlen >= 0x100);
+
+ seqno = mcdi->seqno & SEQ_MASK;
+ xflags = 0;
+ if (mcdi->mode == MCDI_MODE_EVENTS)
+ xflags |= MCDI_HEADER_XFLAGS_EVREQ;
+
+ EFX_POPULATE_DWORD_6(hdr,
+ MCDI_HEADER_RESPONSE, 0,
+ MCDI_HEADER_RESYNC, 1,
+ MCDI_HEADER_CODE, cmd,
+ MCDI_HEADER_DATALEN, inlen,
+ MCDI_HEADER_SEQ, seqno,
+ MCDI_HEADER_XFLAGS, xflags);
+
+ efx_writed(efx, &hdr, pdu);
+
+ for (i = 0; i < inlen; i += 4)
+ _efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i);
+
+ /* Ensure the payload is written out before the header */
+ wmb();
+
+ /* ring the doorbell with a distinctive value */
+ _efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
+}
+
+static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+ unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
+ int i;
+
+ BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
+ BUG_ON(outlen & 3 || outlen >= 0x100);
+
+ for (i = 0; i < outlen; i += 4)
+ *((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i);
+}
+
+static int efx_mcdi_poll(struct efx_nic *efx)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+ unsigned int time, finish;
+ unsigned int respseq, respcmd, error;
+ unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
+ unsigned int rc, spins;
+ efx_dword_t reg;
+
+ /* Check for a reboot atomically with respect to efx_mcdi_copyout() */
+ rc = efx_mcdi_poll_reboot(efx);
+ if (rc)
+ goto out;
+
+ /* Poll for completion. Poll quickly (once a us) for the 1st jiffy,
+ * because generally mcdi responses are fast. After that, back off
+ * and poll once a jiffy (approximately)
+ */
+ spins = TICK_USEC;
+ finish = get_seconds() + MCDI_RPC_TIMEOUT;
+
+ while (1) {
+ if (spins != 0) {
+ --spins;
+ udelay(1);
+ } else
+ schedule();
+
+ time = get_seconds();
+
+ rmb();
+ efx_readd(efx, &reg, pdu);
+
+ /* All 1's indicates that shared memory is in reset (and is
+ * not a valid header). Wait for it to come out reset before
+ * completing the command */
+ if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff &&
+ EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE))
+ break;
+
+ if (time >= finish)
+ return -ETIMEDOUT;
+ }
+
+ mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN);
+ respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ);
+ respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE);
+ error = EFX_DWORD_FIELD(reg, MCDI_HEADER_ERROR);
+
+ if (error && mcdi->resplen == 0) {
+ EFX_ERR(efx, "MC rebooted\n");
+ rc = EIO;
+ } else if ((respseq ^ mcdi->seqno) & SEQ_MASK) {
+ EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx seq 0x%x\n",
+ respseq, mcdi->seqno);
+ rc = EIO;
+ } else if (error) {
+ efx_readd(efx, &reg, pdu + 4);
+ switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) {
+#define TRANSLATE_ERROR(name) \
+ case MC_CMD_ERR_ ## name: \
+ rc = name; \
+ break
+ TRANSLATE_ERROR(ENOENT);
+ TRANSLATE_ERROR(EINTR);
+ TRANSLATE_ERROR(EACCES);
+ TRANSLATE_ERROR(EBUSY);
+ TRANSLATE_ERROR(EINVAL);
+ TRANSLATE_ERROR(EDEADLK);
+ TRANSLATE_ERROR(ENOSYS);
+ TRANSLATE_ERROR(ETIME);
+#undef TRANSLATE_ERROR
+ default:
+ rc = EIO;
+ break;
+ }
+ } else
+ rc = 0;
+
+out:
+ mcdi->resprc = rc;
+ if (rc)
+ mcdi->resplen = 0;
+
+ /* Return rc=0 like wait_event_timeout() */
+ return 0;
+}
+
+/* Test and clear MC-rebooted flag for this port/function */
+int efx_mcdi_poll_reboot(struct efx_nic *efx)
+{
+ unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx);
+ efx_dword_t reg;
+ uint32_t value;
+
+ if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
+ return false;
+
+ efx_readd(efx, &reg, addr);
+ value = EFX_DWORD_FIELD(reg, EFX_DWORD_0);
+
+ if (value == 0)
+ return 0;
+
+ EFX_ZERO_DWORD(reg);
+ efx_writed(efx, &reg, addr);
+
+ if (value == MC_STATUS_DWORD_ASSERT)
+ return -EINTR;
+ else
+ return -EIO;
+}
+
+static void efx_mcdi_acquire(struct efx_mcdi_iface *mcdi)
+{
+ /* Wait until the interface becomes QUIESCENT and we win the race
+ * to mark it RUNNING. */
+ wait_event(mcdi->wq,
+ atomic_cmpxchg(&mcdi->state,
+ MCDI_STATE_QUIESCENT,
+ MCDI_STATE_RUNNING)
+ == MCDI_STATE_QUIESCENT);
+}
+
+static int efx_mcdi_await_completion(struct efx_nic *efx)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+
+ if (wait_event_timeout(
+ mcdi->wq,
+ atomic_read(&mcdi->state) == MCDI_STATE_COMPLETED,
+ msecs_to_jiffies(MCDI_RPC_TIMEOUT * 1000)) == 0)
+ return -ETIMEDOUT;
+
+ /* Check if efx_mcdi_set_mode() switched us back to polled completions.
+ * In which case, poll for completions directly. If efx_mcdi_ev_cpl()
+ * completed the request first, then we'll just end up completing the
+ * request again, which is safe.
+ *
+ * We need an smp_rmb() to synchronise with efx_mcdi_mode_poll(), which
+ * wait_event_timeout() implicitly provides.
+ */
+ if (mcdi->mode == MCDI_MODE_POLL)
+ return efx_mcdi_poll(efx);
+
+ return 0;
+}
+
+static bool efx_mcdi_complete(struct efx_mcdi_iface *mcdi)
+{
+ /* If the interface is RUNNING, then move to COMPLETED and wake any
+ * waiters. If the interface isn't in RUNNING then we've received a
+ * duplicate completion after we've already transitioned back to
+ * QUIESCENT. [A subsequent invocation would increment seqno, so would
+ * have failed the seqno check].
+ */
+ if (atomic_cmpxchg(&mcdi->state,
+ MCDI_STATE_RUNNING,
+ MCDI_STATE_COMPLETED) == MCDI_STATE_RUNNING) {
+ wake_up(&mcdi->wq);
+ return true;
+ }
+
+ return false;
+}
+
+static void efx_mcdi_release(struct efx_mcdi_iface *mcdi)
+{
+ atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT);
+ wake_up(&mcdi->wq);
+}
+
+static void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno,
+ unsigned int datalen, unsigned int errno)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+ bool wake = false;
+
+ spin_lock(&mcdi->iface_lock);
+
+ if ((seqno ^ mcdi->seqno) & SEQ_MASK) {
+ if (mcdi->credits)
+ /* The request has been cancelled */
+ --mcdi->credits;
+ else
+ EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx "
+ "seq 0x%x\n", seqno, mcdi->seqno);
+ } else {
+ mcdi->resprc = errno;
+ mcdi->resplen = datalen;
+
+ wake = true;
+ }
+
+ spin_unlock(&mcdi->iface_lock);
+
+ if (wake)
+ efx_mcdi_complete(mcdi);
+}
+
+/* Issue the given command by writing the data into the shared memory PDU,
+ * ring the doorbell and wait for completion. Copyout the result. */
+int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
+ const u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen,
+ size_t *outlen_actual)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+ int rc;
+ BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0);
+
+ efx_mcdi_acquire(mcdi);
+
+ /* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */
+ spin_lock_bh(&mcdi->iface_lock);
+ ++mcdi->seqno;
+ spin_unlock_bh(&mcdi->iface_lock);
+
+ efx_mcdi_copyin(efx, cmd, inbuf, inlen);
+
+ if (mcdi->mode == MCDI_MODE_POLL)
+ rc = efx_mcdi_poll(efx);
+ else
+ rc = efx_mcdi_await_completion(efx);
+
+ if (rc != 0) {
+ /* Close the race with efx_mcdi_ev_cpl() executing just too late
+ * and completing a request we've just cancelled, by ensuring
+ * that the seqno check therein fails.
+ */
+ spin_lock_bh(&mcdi->iface_lock);
+ ++mcdi->seqno;
+ ++mcdi->credits;
+ spin_unlock_bh(&mcdi->iface_lock);
+
+ EFX_ERR(efx, "MC command 0x%x inlen %d mode %d timed out\n",
+ cmd, (int)inlen, mcdi->mode);
+ } else {
+ size_t resplen;
+
+ /* At the very least we need a memory barrier here to ensure
+ * we pick up changes from efx_mcdi_ev_cpl(). Protect against
+ * a spurious efx_mcdi_ev_cpl() running concurrently by
+ * acquiring the iface_lock. */
+ spin_lock_bh(&mcdi->iface_lock);
+ rc = -mcdi->resprc;
+ resplen = mcdi->resplen;
+ spin_unlock_bh(&mcdi->iface_lock);
+
+ if (rc == 0) {
+ efx_mcdi_copyout(efx, outbuf,
+ min(outlen, mcdi->resplen + 3) & ~0x3);
+ if (outlen_actual != NULL)
+ *outlen_actual = resplen;
+ } else if (cmd == MC_CMD_REBOOT && rc == -EIO)
+ ; /* Don't reset if MC_CMD_REBOOT returns EIO */
+ else if (rc == -EIO || rc == -EINTR) {
+ EFX_ERR(efx, "MC fatal error %d\n", -rc);
+ efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
+ } else
+ EFX_ERR(efx, "MC command 0x%x inlen %d failed rc=%d\n",
+ cmd, (int)inlen, -rc);
+ }
+
+ efx_mcdi_release(mcdi);
+ return rc;
+}
+
+void efx_mcdi_mode_poll(struct efx_nic *efx)
+{
+ struct efx_mcdi_iface *mcdi;
+
+ if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
+ return;
+
+ mcdi = efx_mcdi(efx);
+ if (mcdi->mode == MCDI_MODE_POLL)
+ return;
+
+ /* We can switch from event completion to polled completion, because
+ * mcdi requests are always completed in shared memory. We do this by
+ * switching the mode to POLL'd then completing the request.
+ * efx_mcdi_await_completion() will then call efx_mcdi_poll().
+ *
+ * We need an smp_wmb() to synchronise with efx_mcdi_await_completion(),
+ * which efx_mcdi_complete() provides for us.
+ */
+ mcdi->mode = MCDI_MODE_POLL;
+
+ efx_mcdi_complete(mcdi);
+}
+
+void efx_mcdi_mode_event(struct efx_nic *efx)
+{
+ struct efx_mcdi_iface *mcdi;
+
+ if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
+ return;
+
+ mcdi = efx_mcdi(efx);
+
+ if (mcdi->mode == MCDI_MODE_EVENTS)
+ return;
+
+ /* We can't switch from polled to event completion in the middle of a
+ * request, because the completion method is specified in the request.
+ * So acquire the interface to serialise the requestors. We don't need
+ * to acquire the iface_lock to change the mode here, but we do need a
+ * write memory barrier ensure that efx_mcdi_rpc() sees it, which
+ * efx_mcdi_acquire() provides.
+ */
+ efx_mcdi_acquire(mcdi);
+ mcdi->mode = MCDI_MODE_EVENTS;
+ efx_mcdi_release(mcdi);
+}
+
+static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+
+ /* If there is an outstanding MCDI request, it has been terminated
+ * either by a BADASSERT or REBOOT event. If the mcdi interface is
+ * in polled mode, then do nothing because the MC reboot handler will
+ * set the header correctly. However, if the mcdi interface is waiting
+ * for a CMDDONE event it won't receive it [and since all MCDI events
+ * are sent to the same queue, we can't be racing with
+ * efx_mcdi_ev_cpl()]
+ *
+ * There's a race here with efx_mcdi_rpc(), because we might receive
+ * a REBOOT event *before* the request has been copied out. In polled
+ * mode (during startup) this is irrelevent, because efx_mcdi_complete()
+ * is ignored. In event mode, this condition is just an edge-case of
+ * receiving a REBOOT event after posting the MCDI request. Did the mc
+ * reboot before or after the copyout? The best we can do always is
+ * just return failure.
+ */
+ spin_lock(&mcdi->iface_lock);
+ if (efx_mcdi_complete(mcdi)) {
+ if (mcdi->mode == MCDI_MODE_EVENTS) {
+ mcdi->resprc = rc;
+ mcdi->resplen = 0;
+ }
+ } else
+ /* Nobody was waiting for an MCDI request, so trigger a reset */
+ efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
+
+ spin_unlock(&mcdi->iface_lock);
+}
+
+static unsigned int efx_mcdi_event_link_speed[] = {
+ [MCDI_EVENT_LINKCHANGE_SPEED_100M] = 100,
+ [MCDI_EVENT_LINKCHANGE_SPEED_1G] = 1000,
+ [MCDI_EVENT_LINKCHANGE_SPEED_10G] = 10000,
+};
+
+
+static void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev)
+{
+ u32 flags, fcntl, speed, lpa;
+
+ speed = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_SPEED);
+ EFX_BUG_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed));
+ speed = efx_mcdi_event_link_speed[speed];
+
+ flags = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LINK_FLAGS);
+ fcntl = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_FCNTL);
+ lpa = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LP_CAP);
+
+ /* efx->link_state is only modified by efx_mcdi_phy_get_link(),
+ * which is only run after flushing the event queues. Therefore, it
+ * is safe to modify the link state outside of the mac_lock here.
+ */
+ efx_mcdi_phy_decode_link(efx, &efx->link_state, speed, flags, fcntl);
+
+ efx_mcdi_phy_check_fcntl(efx, lpa);
+
+ efx_link_status_changed(efx);
+}
+
+static const char *sensor_names[] = {
+ [MC_CMD_SENSOR_CONTROLLER_TEMP] = "Controller temp. sensor",
+ [MC_CMD_SENSOR_PHY_COMMON_TEMP] = "PHY shared temp. sensor",
+ [MC_CMD_SENSOR_CONTROLLER_COOLING] = "Controller cooling",
+ [MC_CMD_SENSOR_PHY0_TEMP] = "PHY 0 temp. sensor",
+ [MC_CMD_SENSOR_PHY0_COOLING] = "PHY 0 cooling",
+ [MC_CMD_SENSOR_PHY1_TEMP] = "PHY 1 temp. sensor",
+ [MC_CMD_SENSOR_PHY1_COOLING] = "PHY 1 cooling",
+ [MC_CMD_SENSOR_IN_1V0] = "1.0V supply sensor",
+ [MC_CMD_SENSOR_IN_1V2] = "1.2V supply sensor",
+ [MC_CMD_SENSOR_IN_1V8] = "1.8V supply sensor",
+ [MC_CMD_SENSOR_IN_2V5] = "2.5V supply sensor",
+ [MC_CMD_SENSOR_IN_3V3] = "3.3V supply sensor",
+ [MC_CMD_SENSOR_IN_12V0] = "12V supply sensor"
+};
+
+static const char *sensor_status_names[] = {
+ [MC_CMD_SENSOR_STATE_OK] = "OK",
+ [MC_CMD_SENSOR_STATE_WARNING] = "Warning",
+ [MC_CMD_SENSOR_STATE_FATAL] = "Fatal",
+ [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure",
+};
+
+static void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev)
+{
+ unsigned int monitor, state, value;
+ const char *name, *state_txt;
+ monitor = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR);
+ state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE);
+ value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE);
+ /* Deal gracefully with the board having more drivers than we
+ * know about, but do not expect new sensor states. */
+ name = (monitor >= ARRAY_SIZE(sensor_names))
+ ? "No sensor name available" :
+ sensor_names[monitor];
+ EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names));
+ state_txt = sensor_status_names[state];
+
+ EFX_ERR(efx, "Sensor %d (%s) reports condition '%s' for raw value %d\n",
+ monitor, name, state_txt, value);
+}
+
+/* Called from falcon_process_eventq for MCDI events */
+void efx_mcdi_process_event(struct efx_channel *channel,
+ efx_qword_t *event)
+{
+ struct efx_nic *efx = channel->efx;
+ int code = EFX_QWORD_FIELD(*event, MCDI_EVENT_CODE);
+ u32 data = EFX_QWORD_FIELD(*event, MCDI_EVENT_DATA);
+
+ switch (code) {
+ case MCDI_EVENT_CODE_BADSSERT:
+ EFX_ERR(efx, "MC watchdog or assertion failure at 0x%x\n", data);
+ efx_mcdi_ev_death(efx, EINTR);
+ break;
+
+ case MCDI_EVENT_CODE_PMNOTICE:
+ EFX_INFO(efx, "MCDI PM event.\n");
+ break;
+
+ case MCDI_EVENT_CODE_CMDDONE:
+ efx_mcdi_ev_cpl(efx,
+ MCDI_EVENT_FIELD(*event, CMDDONE_SEQ),
+ MCDI_EVENT_FIELD(*event, CMDDONE_DATALEN),
+ MCDI_EVENT_FIELD(*event, CMDDONE_ERRNO));
+ break;
+
+ case MCDI_EVENT_CODE_LINKCHANGE:
+ efx_mcdi_process_link_change(efx, event);
+ break;
+ case MCDI_EVENT_CODE_SENSOREVT:
+ efx_mcdi_sensor_event(efx, event);
+ break;
+ case MCDI_EVENT_CODE_SCHEDERR:
+ EFX_INFO(efx, "MC Scheduler error address=0x%x\n", data);
+ break;
+ case MCDI_EVENT_CODE_REBOOT:
+ EFX_INFO(efx, "MC Reboot\n");
+ efx_mcdi_ev_death(efx, EIO);
+ break;
+ case MCDI_EVENT_CODE_MAC_STATS_DMA:
+ /* MAC stats are gather lazily. We can ignore this. */
+ break;
+
+ default:
+ EFX_ERR(efx, "Unknown MCDI event 0x%x\n", code);
+ }
+}
+
+/**************************************************************************
+ *
+ * Specific request functions
+ *
+ **************************************************************************
+ */
+
+int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build)
+{
+ u8 outbuf[ALIGN(MC_CMD_GET_VERSION_V1_OUT_LEN, 4)];
+ size_t outlength;
+ const __le16 *ver_words;
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_GET_VERSION_IN_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, NULL, 0,
+ outbuf, sizeof(outbuf), &outlength);
+ if (rc)
+ goto fail;
+
+ if (outlength == MC_CMD_GET_VERSION_V0_OUT_LEN) {
+ *version = 0;
+ *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE);
+ return 0;
+ }
+
+ if (outlength < MC_CMD_GET_VERSION_V1_OUT_LEN) {
+ rc = -EMSGSIZE;
+ goto fail;
+ }
+
+ ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION);
+ *version = (((u64)le16_to_cpu(ver_words[0]) << 48) |
+ ((u64)le16_to_cpu(ver_words[1]) << 32) |
+ ((u64)le16_to_cpu(ver_words[2]) << 16) |
+ le16_to_cpu(ver_words[3]));
+ *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE);
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
+ bool *was_attached)
+{
+ u8 inbuf[MC_CMD_DRV_ATTACH_IN_LEN];
+ u8 outbuf[MC_CMD_DRV_ATTACH_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_NEW_STATE,
+ driver_operating ? 1 : 0);
+ MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+ if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN)
+ goto fail;
+
+ if (was_attached != NULL)
+ *was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE);
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
+ u16 *fw_subtype_list)
+{
+ uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LEN];
+ size_t outlen;
+ int port_num = efx_port_num(efx);
+ int offset;
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ if (outlen < MC_CMD_GET_BOARD_CFG_OUT_LEN) {
+ rc = -EMSGSIZE;
+ goto fail;
+ }
+
+ offset = (port_num)
+ ? MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST
+ : MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST;
+ if (mac_address)
+ memcpy(mac_address, outbuf + offset, ETH_ALEN);
+ if (fw_subtype_list)
+ memcpy(fw_subtype_list,
+ outbuf + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST,
+ MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN);
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d len=%d\n", __func__, rc, (int)outlen);
+
+ return rc;
+}
+
+int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq)
+{
+ u8 inbuf[MC_CMD_LOG_CTRL_IN_LEN];
+ u32 dest = 0;
+ int rc;
+
+ if (uart)
+ dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_UART;
+ if (evq)
+ dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ;
+
+ MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST, dest);
+ MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST_EVQ, dest_evq);
+
+ BUILD_BUG_ON(MC_CMD_LOG_CTRL_OUT_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_LOG_CTRL, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out)
+{
+ u8 outbuf[MC_CMD_NVRAM_TYPES_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_NVRAM_TYPES_IN_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TYPES, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+ if (outlen < MC_CMD_NVRAM_TYPES_OUT_LEN)
+ goto fail;
+
+ *nvram_types_out = MCDI_DWORD(outbuf, NVRAM_TYPES_OUT_TYPES);
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
+ size_t *size_out, size_t *erase_size_out,
+ bool *protected_out)
+{
+ u8 inbuf[MC_CMD_NVRAM_INFO_IN_LEN];
+ u8 outbuf[MC_CMD_NVRAM_INFO_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, NVRAM_INFO_IN_TYPE, type);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_INFO, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+ if (outlen < MC_CMD_NVRAM_INFO_OUT_LEN)
+ goto fail;
+
+ *size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE);
+ *erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE);
+ *protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) &
+ (1 << MC_CMD_NVRAM_PROTECTED_LBN));
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
+{
+ u8 inbuf[MC_CMD_NVRAM_UPDATE_START_IN_LEN];
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type);
+
+ BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
+ loff_t offset, u8 *buffer, size_t length)
+{
+ u8 inbuf[MC_CMD_NVRAM_READ_IN_LEN];
+ u8 outbuf[MC_CMD_NVRAM_READ_OUT_LEN(length)];
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type);
+ MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset);
+ MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length);
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
+ loff_t offset, const u8 *buffer, size_t length)
+{
+ u8 inbuf[MC_CMD_NVRAM_WRITE_IN_LEN(length)];
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
+ MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
+ MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
+ memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length);
+
+ BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
+ loff_t offset, size_t length)
+{
+ u8 inbuf[MC_CMD_NVRAM_ERASE_IN_LEN];
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type);
+ MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset);
+ MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length);
+
+ BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
+{
+ u8 inbuf[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN];
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
+
+ BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_handle_assertion(struct efx_nic *efx)
+{
+ union {
+ u8 asserts[MC_CMD_GET_ASSERTS_IN_LEN];
+ u8 reboot[MC_CMD_REBOOT_IN_LEN];
+ } inbuf;
+ u8 assertion[MC_CMD_GET_ASSERTS_OUT_LEN];
+ unsigned int flags, index, ofst;
+ const char *reason;
+ size_t outlen;
+ int retry;
+ int rc;
+
+ /* Check if the MC is in the assertion handler, retrying twice. Once
+ * because a boot-time assertion might cause this command to fail
+ * with EINTR. And once again because GET_ASSERTS can race with
+ * MC_CMD_REBOOT running on the other port. */
+ retry = 2;
+ do {
+ MCDI_SET_DWORD(inbuf.asserts, GET_ASSERTS_IN_CLEAR, 0);
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS,
+ inbuf.asserts, MC_CMD_GET_ASSERTS_IN_LEN,
+ assertion, sizeof(assertion), &outlen);
+ } while ((rc == -EINTR || rc == -EIO) && retry-- > 0);
+
+ if (rc)
+ return rc;
+ if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN)
+ return -EINVAL;
+
+ flags = MCDI_DWORD(assertion, GET_ASSERTS_OUT_GLOBAL_FLAGS);
+ if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
+ return 0;
+
+ /* Reset the hardware atomically such that only one port with succeed.
+ * This command will succeed if a reboot is no longer required (because
+ * the other port did it first), but fail with EIO if it succeeds.
+ */
+ BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
+ MCDI_SET_DWORD(inbuf.reboot, REBOOT_IN_FLAGS,
+ MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION);
+ efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf.reboot, MC_CMD_REBOOT_IN_LEN,
+ NULL, 0, NULL);
+
+ /* Print out the assertion */
+ reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
+ ? "system-level assertion"
+ : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
+ ? "thread-level assertion"
+ : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
+ ? "watchdog reset"
+ : "unknown assertion";
+ EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason,
+ MCDI_DWORD(assertion, GET_ASSERTS_OUT_SAVED_PC_OFFS),
+ MCDI_DWORD(assertion, GET_ASSERTS_OUT_THREAD_OFFS));
+
+ /* Print out the registers */
+ ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
+ for (index = 1; index < 32; index++) {
+ EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index,
+ MCDI_DWORD2(assertion, ofst));
+ ofst += sizeof(efx_dword_t);
+ }
+
+ return 0;
+}
+
+void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
+{
+ u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN];
+ int rc;
+
+ BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF);
+ BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON);
+ BUILD_BUG_ON(EFX_LED_DEFAULT != MC_CMD_LED_DEFAULT);
+
+ BUILD_BUG_ON(MC_CMD_SET_ID_LED_OUT_LEN != 0);
+
+ MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+}
+
+int efx_mcdi_reset_port(struct efx_nic *efx)
+{
+ int rc = efx_mcdi_rpc(efx, MC_CMD_PORT_RESET, NULL, 0, NULL, 0, NULL);
+ if (rc)
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_reset_mc(struct efx_nic *efx)
+{
+ u8 inbuf[MC_CMD_REBOOT_IN_LEN];
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
+ MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 0);
+ rc = efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ /* White is black, and up is down */
+ if (rc == -EIO)
+ return 0;
+ if (rc == 0)
+ rc = -EIO;
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type,
+ const u8 *mac, int *id_out)
+{
+ u8 inbuf[MC_CMD_WOL_FILTER_SET_IN_LEN];
+ u8 outbuf[MC_CMD_WOL_FILTER_SET_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type);
+ MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE,
+ MC_CMD_FILTER_MODE_SIMPLE);
+ memcpy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac, ETH_ALEN);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ if (outlen < MC_CMD_WOL_FILTER_SET_OUT_LEN) {
+ rc = -EMSGSIZE;
+ goto fail;
+ }
+
+ *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_SET_OUT_FILTER_ID);
+
+ return 0;
+
+fail:
+ *id_out = -1;
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+
+}
+
+
+int
+efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, const u8 *mac, int *id_out)
+{
+ return efx_mcdi_wol_filter_set(efx, MC_CMD_WOL_TYPE_MAGIC, mac, id_out);
+}
+
+
+int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out)
+{
+ u8 outbuf[MC_CMD_WOL_FILTER_GET_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_GET, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ if (outlen < MC_CMD_WOL_FILTER_GET_OUT_LEN) {
+ rc = -EMSGSIZE;
+ goto fail;
+ }
+
+ *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_GET_OUT_FILTER_ID);
+
+ return 0;
+
+fail:
+ *id_out = -1;
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+
+int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id)
+{
+ u8 inbuf[MC_CMD_WOL_FILTER_REMOVE_IN_LEN];
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, WOL_FILTER_REMOVE_IN_FILTER_ID, (u32)id);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_REMOVE, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+
+int efx_mcdi_wol_filter_reset(struct efx_nic *efx)
+{
+ int rc;
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_RESET, NULL, 0, NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
diff --git a/drivers/net/sfc/mcdi.h b/drivers/net/sfc/mcdi.h
new file mode 100644
index 00000000000..de916728c2e
--- /dev/null
+++ b/drivers/net/sfc/mcdi.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2008-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_MCDI_H
+#define EFX_MCDI_H
+
+/**
+ * enum efx_mcdi_state
+ * @MCDI_STATE_QUIESCENT: No pending MCDI requests. If the caller holds the
+ * mcdi_lock then they are able to move to MCDI_STATE_RUNNING
+ * @MCDI_STATE_RUNNING: There is an MCDI request pending. Only the thread that
+ * moved into this state is allowed to move out of it.
+ * @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread
+ * has not yet consumed the result. For all other threads, equivalent to
+ * MCDI_STATE_RUNNING.
+ */
+enum efx_mcdi_state {
+ MCDI_STATE_QUIESCENT,
+ MCDI_STATE_RUNNING,
+ MCDI_STATE_COMPLETED,
+};
+
+enum efx_mcdi_mode {
+ MCDI_MODE_POLL,
+ MCDI_MODE_EVENTS,
+};
+
+/**
+ * struct efx_mcdi_iface
+ * @state: Interface state. Waited for by mcdi_wq.
+ * @wq: Wait queue for threads waiting for state != STATE_RUNNING
+ * @iface_lock: Protects @credits, @seqno, @resprc, @resplen
+ * @mode: Poll for mcdi completion, or wait for an mcdi_event.
+ * Serialised by @lock
+ * @seqno: The next sequence number to use for mcdi requests.
+ * Serialised by @lock
+ * @credits: Number of spurious MCDI completion events allowed before we
+ * trigger a fatal error. Protected by @lock
+ * @resprc: Returned MCDI completion
+ * @resplen: Returned payload length
+ */
+struct efx_mcdi_iface {
+ atomic_t state;
+ wait_queue_head_t wq;
+ spinlock_t iface_lock;
+ enum efx_mcdi_mode mode;
+ unsigned int credits;
+ unsigned int seqno;
+ unsigned int resprc;
+ size_t resplen;
+};
+
+extern void efx_mcdi_init(struct efx_nic *efx);
+
+extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf,
+ size_t inlen, u8 *outbuf, size_t outlen,
+ size_t *outlen_actual);
+
+extern int efx_mcdi_poll_reboot(struct efx_nic *efx);
+extern void efx_mcdi_mode_poll(struct efx_nic *efx);
+extern void efx_mcdi_mode_event(struct efx_nic *efx);
+
+extern void efx_mcdi_process_event(struct efx_channel *channel,
+ efx_qword_t *event);
+
+#define MCDI_PTR2(_buf, _ofst) \
+ (((u8 *)_buf) + _ofst)
+#define MCDI_SET_DWORD2(_buf, _ofst, _value) \
+ EFX_POPULATE_DWORD_1(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \
+ EFX_DWORD_0, _value)
+#define MCDI_DWORD2(_buf, _ofst) \
+ EFX_DWORD_FIELD(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \
+ EFX_DWORD_0)
+#define MCDI_QWORD2(_buf, _ofst) \
+ EFX_QWORD_FIELD64(*((efx_qword_t *)MCDI_PTR2(_buf, _ofst)), \
+ EFX_QWORD_0)
+
+#define MCDI_PTR(_buf, _ofst) \
+ MCDI_PTR2(_buf, MC_CMD_ ## _ofst ## _OFST)
+#define MCDI_SET_DWORD(_buf, _ofst, _value) \
+ MCDI_SET_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST, _value)
+#define MCDI_DWORD(_buf, _ofst) \
+ MCDI_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST)
+#define MCDI_QWORD(_buf, _ofst) \
+ MCDI_QWORD2(_buf, MC_CMD_ ## _ofst ## _OFST)
+
+#define MCDI_EVENT_FIELD(_ev, _field) \
+ EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field)
+
+extern int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build);
+extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
+ bool *was_attached_out);
+extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
+ u16 *fw_subtype_list);
+extern int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart,
+ u32 dest_evq);
+extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out);
+extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
+ size_t *size_out, size_t *erase_size_out,
+ bool *protected_out);
+extern int efx_mcdi_nvram_update_start(struct efx_nic *efx,
+ unsigned int type);
+extern int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
+ loff_t offset, u8 *buffer, size_t length);
+extern int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
+ loff_t offset, const u8 *buffer,
+ size_t length);
+extern int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
+ loff_t offset, size_t length);
+extern int efx_mcdi_nvram_update_finish(struct efx_nic *efx,
+ unsigned int type);
+extern int efx_mcdi_handle_assertion(struct efx_nic *efx);
+extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
+extern int efx_mcdi_reset_port(struct efx_nic *efx);
+extern int efx_mcdi_reset_mc(struct efx_nic *efx);
+extern int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type,
+ const u8 *mac, int *id_out);
+extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx,
+ const u8 *mac, int *id_out);
+extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out);
+extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id);
+extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx);
+
+#endif /* EFX_MCDI_H */
diff --git a/drivers/net/sfc/mcdi_mac.c b/drivers/net/sfc/mcdi_mac.c
new file mode 100644
index 00000000000..06d24a1e412
--- /dev/null
+++ b/drivers/net/sfc/mcdi_mac.c
@@ -0,0 +1,152 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include "net_driver.h"
+#include "efx.h"
+#include "mac.h"
+#include "mcdi.h"
+#include "mcdi_pcol.h"
+
+static int efx_mcdi_set_mac(struct efx_nic *efx)
+{
+ u32 reject, fcntl;
+ u8 cmdbytes[MC_CMD_SET_MAC_IN_LEN];
+
+ memcpy(cmdbytes + MC_CMD_SET_MAC_IN_ADDR_OFST,
+ efx->net_dev->dev_addr, ETH_ALEN);
+
+ MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_MTU,
+ EFX_MAX_FRAME_LEN(efx->net_dev->mtu));
+ MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_DRAIN, 0);
+
+ /* The MCDI command provides for controlling accept/reject
+ * of broadcast packets too, but the driver doesn't currently
+ * expose this. */
+ reject = (efx->promiscuous) ? 0 :
+ (1 << MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN);
+ MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_REJECT, reject);
+
+ switch (efx->wanted_fc) {
+ case EFX_FC_RX | EFX_FC_TX:
+ fcntl = MC_CMD_FCNTL_BIDIR;
+ break;
+ case EFX_FC_RX:
+ fcntl = MC_CMD_FCNTL_RESPOND;
+ break;
+ default:
+ fcntl = MC_CMD_FCNTL_OFF;
+ break;
+ }
+ if (efx->wanted_fc & EFX_FC_AUTO)
+ fcntl = MC_CMD_FCNTL_AUTO;
+
+ MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_FCNTL, fcntl);
+
+ return efx_mcdi_rpc(efx, MC_CMD_SET_MAC, cmdbytes, sizeof(cmdbytes),
+ NULL, 0, NULL);
+}
+
+static int efx_mcdi_get_mac_faults(struct efx_nic *efx, u32 *faults)
+{
+ u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
+ size_t outlength;
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
+ outbuf, sizeof(outbuf), &outlength);
+ if (rc)
+ goto fail;
+
+ *faults = MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT);
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n",
+ __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr,
+ u32 dma_len, int enable, int clear)
+{
+ u8 inbuf[MC_CMD_MAC_STATS_IN_LEN];
+ int rc;
+ efx_dword_t *cmd_ptr;
+ int period = 1000;
+ u32 addr_hi;
+ u32 addr_lo;
+
+ BUILD_BUG_ON(MC_CMD_MAC_STATS_OUT_LEN != 0);
+
+ addr_lo = ((u64)dma_addr) >> 0;
+ addr_hi = ((u64)dma_addr) >> 32;
+
+ MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_LO, addr_lo);
+ MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_HI, addr_hi);
+ cmd_ptr = (efx_dword_t *)MCDI_PTR(inbuf, MAC_STATS_IN_CMD);
+ if (enable)
+ EFX_POPULATE_DWORD_6(*cmd_ptr,
+ MC_CMD_MAC_STATS_CMD_DMA, 1,
+ MC_CMD_MAC_STATS_CMD_CLEAR, clear,
+ MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1,
+ MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 1,
+ MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0,
+ MC_CMD_MAC_STATS_CMD_PERIOD_MS, period);
+ else
+ EFX_POPULATE_DWORD_5(*cmd_ptr,
+ MC_CMD_MAC_STATS_CMD_DMA, 0,
+ MC_CMD_MAC_STATS_CMD_CLEAR, clear,
+ MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1,
+ MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 0,
+ MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0);
+ MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_LEN, dma_len);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_MAC_STATS, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: %s failed rc=%d\n",
+ __func__, enable ? "enable" : "disable", rc);
+ return rc;
+}
+
+static int efx_mcdi_mac_reconfigure(struct efx_nic *efx)
+{
+ int rc;
+
+ rc = efx_mcdi_set_mac(efx);
+ if (rc != 0)
+ return rc;
+
+ /* Restore the multicast hash registers. */
+ efx->type->push_multicast_hash(efx);
+
+ return 0;
+}
+
+
+static bool efx_mcdi_mac_check_fault(struct efx_nic *efx)
+{
+ u32 faults;
+ int rc = efx_mcdi_get_mac_faults(efx, &faults);
+ return (rc != 0) || (faults != 0);
+}
+
+
+struct efx_mac_operations efx_mcdi_mac_operations = {
+ .reconfigure = efx_mcdi_mac_reconfigure,
+ .update_stats = efx_port_dummy_op_void,
+ .check_fault = efx_mcdi_mac_check_fault,
+};
diff --git a/drivers/net/sfc/mcdi_pcol.h b/drivers/net/sfc/mcdi_pcol.h
new file mode 100644
index 00000000000..2a85360a46f
--- /dev/null
+++ b/drivers/net/sfc/mcdi_pcol.h
@@ -0,0 +1,1578 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+
+#ifndef MCDI_PCOL_H
+#define MCDI_PCOL_H
+
+/* Values to be written into FMCR_CZ_RESET_STATE_REG to control boot. */
+/* Power-on reset state */
+#define MC_FW_STATE_POR (1)
+/* If this is set in MC_RESET_STATE_REG then it should be
+ * possible to jump into IMEM without loading code from flash. */
+#define MC_FW_WARM_BOOT_OK (2)
+/* The MC main image has started to boot. */
+#define MC_FW_STATE_BOOTING (4)
+/* The Scheduler has started. */
+#define MC_FW_STATE_SCHED (8)
+
+/* Values to be written to the per-port status dword in shared
+ * memory on reboot and assert */
+#define MC_STATUS_DWORD_REBOOT (0xb007b007)
+#define MC_STATUS_DWORD_ASSERT (0xdeaddead)
+
+/* The current version of the MCDI protocol.
+ *
+ * Note that the ROM burnt into the card only talks V0, so at the very
+ * least every driver must support version 0 and MCDI_PCOL_VERSION
+ */
+#define MCDI_PCOL_VERSION 1
+
+/**
+ * MCDI version 1
+ *
+ * Each MCDI request starts with an MCDI_HEADER, which is a 32byte
+ * structure, filled in by the client.
+ *
+ * 0 7 8 16 20 22 23 24 31
+ * | CODE | R | LEN | SEQ | Rsvd | E | R | XFLAGS |
+ * | | |
+ * | | \--- Response
+ * | \------- Error
+ * \------------------------------ Resync (always set)
+ *
+ * The client writes it's request into MC shared memory, and rings the
+ * doorbell. Each request is completed by either by the MC writting
+ * back into shared memory, or by writting out an event.
+ *
+ * All MCDI commands support completion by shared memory response. Each
+ * request may also contain additional data (accounted for by HEADER.LEN),
+ * and some response's may also contain additional data (again, accounted
+ * for by HEADER.LEN).
+ *
+ * Some MCDI commands support completion by event, in which any associated
+ * response data is included in the event.
+ *
+ * The protocol requires one response to be delivered for every request, a
+ * request should not be sent unless the response for the previous request
+ * has been received (either by polling shared memory, or by receiving
+ * an event).
+ */
+
+/** Request/Response structure */
+#define MCDI_HEADER_OFST 0
+#define MCDI_HEADER_CODE_LBN 0
+#define MCDI_HEADER_CODE_WIDTH 7
+#define MCDI_HEADER_RESYNC_LBN 7
+#define MCDI_HEADER_RESYNC_WIDTH 1
+#define MCDI_HEADER_DATALEN_LBN 8
+#define MCDI_HEADER_DATALEN_WIDTH 8
+#define MCDI_HEADER_SEQ_LBN 16
+#define MCDI_HEADER_RSVD_LBN 20
+#define MCDI_HEADER_RSVD_WIDTH 2
+#define MCDI_HEADER_SEQ_WIDTH 4
+#define MCDI_HEADER_ERROR_LBN 22
+#define MCDI_HEADER_ERROR_WIDTH 1
+#define MCDI_HEADER_RESPONSE_LBN 23
+#define MCDI_HEADER_RESPONSE_WIDTH 1
+#define MCDI_HEADER_XFLAGS_LBN 24
+#define MCDI_HEADER_XFLAGS_WIDTH 8
+/* Request response using event */
+#define MCDI_HEADER_XFLAGS_EVREQ 0x01
+
+/* Maximum number of payload bytes */
+#define MCDI_CTL_SDU_LEN_MAX 0xfc
+
+/* The MC can generate events for two reasons:
+ * - To complete a shared memory request if XFLAGS_EVREQ was set
+ * - As a notification (link state, i2c event), controlled
+ * via MC_CMD_LOG_CTRL
+ *
+ * Both events share a common structure:
+ *
+ * 0 32 33 36 44 52 60
+ * | Data | Cont | Level | Src | Code | Rsvd |
+ * |
+ * \ There is another event pending in this notification
+ *
+ * If Code==CMDDONE, then the fields are further interpreted as:
+ *
+ * - LEVEL==INFO Command succeded
+ * - LEVEL==ERR Command failed
+ *
+ * 0 8 16 24 32
+ * | Seq | Datalen | Errno | Rsvd |
+ *
+ * These fields are taken directly out of the standard MCDI header, i.e.,
+ * LEVEL==ERR, Datalen == 0 => Reboot
+ *
+ * Events can be squirted out of the UART (using LOG_CTRL) without a
+ * MCDI header. An event can be distinguished from a MCDI response by
+ * examining the first byte which is 0xc0. This corresponds to the
+ * non-existent MCDI command MC_CMD_DEBUG_LOG.
+ *
+ * 0 7 8
+ * | command | Resync | = 0xc0
+ *
+ * Since the event is written in big-endian byte order, this works
+ * providing bits 56-63 of the event are 0xc0.
+ *
+ * 56 60 63
+ * | Rsvd | Code | = 0xc0
+ *
+ * Which means for convenience the event code is 0xc for all MC
+ * generated events.
+ */
+#define FSE_AZ_EV_CODE_MCDI_EVRESPONSE 0xc
+
+#define MCDI_EVENT_DATA_LBN 0
+#define MCDI_EVENT_DATA_WIDTH 32
+#define MCDI_EVENT_CONT_LBN 32
+#define MCDI_EVENT_CONT_WIDTH 1
+#define MCDI_EVENT_LEVEL_LBN 33
+#define MCDI_EVENT_LEVEL_WIDTH 3
+#define MCDI_EVENT_LEVEL_INFO (0)
+#define MCDI_EVENT_LEVEL_WARN (1)
+#define MCDI_EVENT_LEVEL_ERR (2)
+#define MCDI_EVENT_LEVEL_FATAL (3)
+#define MCDI_EVENT_SRC_LBN 36
+#define MCDI_EVENT_SRC_WIDTH 8
+#define MCDI_EVENT_CODE_LBN 44
+#define MCDI_EVENT_CODE_WIDTH 8
+#define MCDI_EVENT_CODE_BADSSERT (1)
+#define MCDI_EVENT_CODE_PMNOTICE (2)
+#define MCDI_EVENT_CODE_CMDDONE (3)
+#define MCDI_EVENT_CMDDONE_SEQ_LBN 0
+#define MCDI_EVENT_CMDDONE_SEQ_WIDTH 8
+#define MCDI_EVENT_CMDDONE_DATALEN_LBN 8
+#define MCDI_EVENT_CMDDONE_DATALEN_WIDTH 8
+#define MCDI_EVENT_CMDDONE_ERRNO_LBN 16
+#define MCDI_EVENT_CMDDONE_ERRNO_WIDTH 8
+#define MCDI_EVENT_CODE_LINKCHANGE (4)
+#define MCDI_EVENT_LINKCHANGE_LP_CAP_LBN 0
+#define MCDI_EVENT_LINKCHANGE_LP_CAP_WIDTH 16
+#define MCDI_EVENT_LINKCHANGE_SPEED_LBN 16
+#define MCDI_EVENT_LINKCHANGE_SPEED_WIDTH 4
+#define MCDI_EVENT_LINKCHANGE_SPEED_100M 1
+#define MCDI_EVENT_LINKCHANGE_SPEED_1G 2
+#define MCDI_EVENT_LINKCHANGE_SPEED_10G 3
+#define MCDI_EVENT_LINKCHANGE_FCNTL_LBN 20
+#define MCDI_EVENT_LINKCHANGE_FCNTL_WIDTH 4
+#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_LBN 24
+#define MCDI_EVENT_LINKCHANGE_LINK_FLAGS_WIDTH 8
+#define MCDI_EVENT_CODE_SENSOREVT (5)
+#define MCDI_EVENT_SENSOREVT_MONITOR_LBN 0
+#define MCDI_EVENT_SENSOREVT_MONITOR_WIDTH 8
+#define MCDI_EVENT_SENSOREVT_STATE_LBN 8
+#define MCDI_EVENT_SENSOREVT_STATE_WIDTH 8
+#define MCDI_EVENT_SENSOREVT_VALUE_LBN 16
+#define MCDI_EVENT_SENSOREVT_VALUE_WIDTH 16
+#define MCDI_EVENT_CODE_SCHEDERR (6)
+#define MCDI_EVENT_CODE_REBOOT (7)
+#define MCDI_EVENT_CODE_MAC_STATS_DMA (8)
+#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_LBN 0
+#define MCDI_EVENT_MAC_STATS_DMA_GENERATION_WIDTH 32
+
+/* Non-existent command target */
+#define MC_CMD_ERR_ENOENT 2
+/* assert() has killed the MC */
+#define MC_CMD_ERR_EINTR 4
+/* Caller does not hold required locks */
+#define MC_CMD_ERR_EACCES 13
+/* Resource is currently unavailable (e.g. lock contention) */
+#define MC_CMD_ERR_EBUSY 16
+/* Invalid argument to target */
+#define MC_CMD_ERR_EINVAL 22
+/* Non-recursive resource is already acquired */
+#define MC_CMD_ERR_EDEADLK 35
+/* Operation not implemented */
+#define MC_CMD_ERR_ENOSYS 38
+/* Operation timed out */
+#define MC_CMD_ERR_ETIME 62
+
+#define MC_CMD_ERR_CODE_OFST 0
+
+
+/* MC_CMD_READ32: (debug, variadic out)
+ * Read multiple 32byte words from MC memory
+ */
+#define MC_CMD_READ32 0x01
+#define MC_CMD_READ32_IN_LEN 8
+#define MC_CMD_READ32_IN_ADDR_OFST 0
+#define MC_CMD_READ32_IN_NUMWORDS_OFST 4
+#define MC_CMD_READ32_OUT_LEN(_numwords) \
+ (4 * (_numwords))
+#define MC_CMD_READ32_OUT_BUFFER_OFST 0
+
+/* MC_CMD_WRITE32: (debug, variadic in)
+ * Write multiple 32byte words to MC memory
+ */
+#define MC_CMD_WRITE32 0x02
+#define MC_CMD_WRITE32_IN_LEN(_numwords) (((_numwords) * 4) + 4)
+#define MC_CMD_WRITE32_IN_ADDR_OFST 0
+#define MC_CMD_WRITE32_IN_BUFFER_OFST 4
+#define MC_CMD_WRITE32_OUT_LEN 0
+
+/* MC_CMD_COPYCODE: (debug)
+ * Copy MC code between two locations and jump
+ */
+#define MC_CMD_COPYCODE 0x03
+#define MC_CMD_COPYCODE_IN_LEN 16
+#define MC_CMD_COPYCODE_IN_SRC_ADDR_OFST 0
+#define MC_CMD_COPYCODE_IN_DEST_ADDR_OFST 4
+#define MC_CMD_COPYCODE_IN_NUMWORDS_OFST 8
+#define MC_CMD_COPYCODE_IN_JUMP_OFST 12
+/* Control should return to the caller rather than jumping */
+#define MC_CMD_COPYCODE_JUMP_NONE 1
+#define MC_CMD_COPYCODE_OUT_LEN 0
+
+/* MC_CMD_SET_FUNC: (debug)
+ * Select function for function-specific commands.
+ */
+#define MC_CMD_SET_FUNC 0x04
+#define MC_CMD_SET_FUNC_IN_LEN 4
+#define MC_CMD_SET_FUNC_IN_FUNC_OFST 0
+#define MC_CMD_SET_FUNC_OUT_LEN 0
+
+/* MC_CMD_GET_BOOT_STATUS:
+ * Get the instruction address from which the MC booted.
+ */
+#define MC_CMD_GET_BOOT_STATUS 0x05
+#define MC_CMD_GET_BOOT_STATUS_IN_LEN 0
+#define MC_CMD_GET_BOOT_STATUS_OUT_LEN 8
+#define MC_CMD_GET_BOOT_STATUS_OUT_BOOT_OFFSET_OFST 0
+#define MC_CMD_GET_BOOT_STATUS_OUT_FLAGS_OFST 4
+/* Reboot caused by watchdog */
+#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_LBN (0)
+#define MC_CMD_GET_BOOT_STATUS_FLAGS_WATCHDOG_WIDTH (1)
+/* MC booted from primary flash partition */
+#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_LBN (1)
+#define MC_CMD_GET_BOOT_STATUS_FLAGS_PRIMARY_WIDTH (1)
+/* MC booted from backup flash partition */
+#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_LBN (2)
+#define MC_CMD_GET_BOOT_STATUS_FLAGS_BACKUP_WIDTH (1)
+
+/* MC_CMD_GET_ASSERTS: (debug, variadic out)
+ * Get (and optionally clear) the current assertion status.
+ *
+ * Only OUT.GLOBAL_FLAGS is guaranteed to exist in the completion
+ * payload. The other fields will only be present if
+ * OUT.GLOBAL_FLAGS != NO_FAILS
+ */
+#define MC_CMD_GET_ASSERTS 0x06
+#define MC_CMD_GET_ASSERTS_IN_LEN 4
+#define MC_CMD_GET_ASSERTS_IN_CLEAR_OFST 0
+#define MC_CMD_GET_ASSERTS_OUT_LEN 140
+/* Assertion status flag */
+#define MC_CMD_GET_ASSERTS_OUT_GLOBAL_FLAGS_OFST 0
+/*! No assertions have failed. */
+#define MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS 1
+/*! A system-level assertion has failed. */
+#define MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL 2
+/*! A thread-level assertion has failed. */
+#define MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL 3
+/*! The system was reset by the watchdog. */
+#define MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED 4
+/* Failing PC value */
+#define MC_CMD_GET_ASSERTS_OUT_SAVED_PC_OFFS_OFST 4
+/* Saved GP regs */
+#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST 8
+#define MC_CMD_GET_ASSERTS_OUT_GP_REGS_LEN 124
+/* Failing thread address */
+#define MC_CMD_GET_ASSERTS_OUT_THREAD_OFFS_OFST 132
+
+/* MC_CMD_LOG_CTRL:
+ * Determine the output stream for various events and messages
+ */
+#define MC_CMD_LOG_CTRL 0x07
+#define MC_CMD_LOG_CTRL_IN_LEN 8
+#define MC_CMD_LOG_CTRL_IN_LOG_DEST_OFST 0
+#define MC_CMD_LOG_CTRL_IN_LOG_DEST_UART (1)
+#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ (2)
+#define MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ_OFST 4
+#define MC_CMD_LOG_CTRL_OUT_LEN 0
+
+/* MC_CMD_GET_VERSION:
+ * Get version information about the MC firmware
+ */
+#define MC_CMD_GET_VERSION 0x08
+#define MC_CMD_GET_VERSION_IN_LEN 0
+#define MC_CMD_GET_VERSION_V0_OUT_LEN 4
+#define MC_CMD_GET_VERSION_V1_OUT_LEN 32
+#define MC_CMD_GET_VERSION_OUT_FIRMWARE_OFST 0
+/* Reserved version number to indicate "any" version. */
+#define MC_CMD_GET_VERSION_OUT_FIRMWARE_ANY 0xffffffff
+/* The version response of a boot ROM awaiting rescue */
+#define MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM 0xb0070000
+#define MC_CMD_GET_VERSION_V1_OUT_PCOL_OFST 4
+/* 128bit mask of functions supported by the current firmware */
+#define MC_CMD_GET_VERSION_V1_OUT_SUPPORTED_FUNCS_OFST 8
+/* The command set exported by the boot ROM (MCDI v0) */
+#define MC_CMD_GET_VERSION_V0_SUPPORTED_FUNCS { \
+ (1 << MC_CMD_READ32) | \
+ (1 << MC_CMD_WRITE32) | \
+ (1 << MC_CMD_COPYCODE) | \
+ (1 << MC_CMD_GET_VERSION), \
+ 0, 0, 0 }
+#define MC_CMD_GET_VERSION_OUT_VERSION_OFST 24
+
+/* Vectors in the boot ROM */
+/* Point to the copycode entry point. */
+#define MC_BOOTROM_COPYCODE_VEC (0x7f4)
+/* Points to the recovery mode entry point. */
+#define MC_BOOTROM_NOFLASH_VEC (0x7f8)
+
+/* Test execution limits */
+#define MC_TESTEXEC_VARIANT_COUNT 16
+#define MC_TESTEXEC_RESULT_COUNT 7
+
+/* MC_CMD_SET_TESTVARS: (debug, variadic in)
+ * Write variant words for test.
+ *
+ * The user supplies a bitmap of the variants they wish to set.
+ * They must ensure that IN.LEN >= 4 + 4 * ffs(BITMAP)
+ */
+#define MC_CMD_SET_TESTVARS 0x09
+#define MC_CMD_SET_TESTVARS_IN_LEN(_numwords) \
+ (4 + 4*(_numwords))
+#define MC_CMD_SET_TESTVARS_IN_ARGS_BITMAP_OFST 0
+/* Up to MC_TESTEXEC_VARIANT_COUNT of 32byte words start here */
+#define MC_CMD_SET_TESTVARS_IN_ARGS_BUFFER_OFST 4
+#define MC_CMD_SET_TESTVARS_OUT_LEN 0
+
+/* MC_CMD_GET_TESTRCS: (debug, variadic out)
+ * Return result words from test.
+ */
+#define MC_CMD_GET_TESTRCS 0x0a
+#define MC_CMD_GET_TESTRCS_IN_LEN 4
+#define MC_CMD_GET_TESTRCS_IN_NUMWORDS_OFST 0
+#define MC_CMD_GET_TESTRCS_OUT_LEN(_numwords) \
+ (4 * (_numwords))
+#define MC_CMD_GET_TESTRCS_OUT_BUFFER_OFST 0
+
+/* MC_CMD_RUN_TEST: (debug)
+ * Run the test exported by this firmware image
+ */
+#define MC_CMD_RUN_TEST 0x0b
+#define MC_CMD_RUN_TEST_IN_LEN 0
+#define MC_CMD_RUN_TEST_OUT_LEN 0
+
+/* MC_CMD_CSR_READ32: (debug, variadic out)
+ * Read 32bit words from the indirect memory map
+ */
+#define MC_CMD_CSR_READ32 0x0c
+#define MC_CMD_CSR_READ32_IN_LEN 12
+#define MC_CMD_CSR_READ32_IN_ADDR_OFST 0
+#define MC_CMD_CSR_READ32_IN_STEP_OFST 4
+#define MC_CMD_CSR_READ32_IN_NUMWORDS_OFST 8
+#define MC_CMD_CSR_READ32_OUT_LEN(_numwords) \
+ (((_numwords) * 4) + 4)
+/* IN.NUMWORDS of 32bit words start here */
+#define MC_CMD_CSR_READ32_OUT_BUFFER_OFST 0
+#define MC_CMD_CSR_READ32_OUT_IREG_STATUS_OFST(_numwords) \
+ ((_numwords) * 4)
+
+/* MC_CMD_CSR_WRITE32: (debug, variadic in)
+ * Write 32bit dwords to the indirect memory map
+ */
+#define MC_CMD_CSR_WRITE32 0x0d
+#define MC_CMD_CSR_WRITE32_IN_LEN(_numwords) \
+ (((_numwords) * 4) + 8)
+#define MC_CMD_CSR_WRITE32_IN_ADDR_OFST 0
+#define MC_CMD_CSR_WRITE32_IN_STEP_OFST 4
+/* Multiple 32bit words of data to write start here */
+#define MC_CMD_CSR_WRITE32_IN_BUFFER_OFST 8
+#define MC_CMD_CSR_WRITE32_OUT_LEN 4
+#define MC_CMD_CSR_WRITE32_OUT_STATUS_OFST 0
+
+/* MC_CMD_JTAG_WORK: (debug, fpga only)
+ * Process JTAG work buffer for RBF acceleration.
+ *
+ * Host: bit count, (up to) 32 words of data to clock out to JTAG
+ * (bits 1,0=TMS,TDO for first bit; bits 3,2=TMS,TDO for second bit, etc.)
+ * MC: bit count, (up to) 32 words of data clocked in from JTAG
+ * (bit 0=TDI for first bit, bit 1=TDI for second bit, etc.; [31:16] unused)
+ */
+#define MC_CMD_JTAG_WORK 0x0e
+
+/* MC_CMD_STACKINFO: (debug, variadic out)
+ * Get stack information
+ *
+ * Host: nothing
+ * MC: (thread ptr, stack size, free space) for each thread in system
+ */
+#define MC_CMD_STACKINFO 0x0f
+
+/* MC_CMD_MDIO_READ:
+ * MDIO register read
+ */
+#define MC_CMD_MDIO_READ 0x10
+#define MC_CMD_MDIO_READ_IN_LEN 16
+#define MC_CMD_MDIO_READ_IN_BUS_OFST 0
+#define MC_CMD_MDIO_READ_IN_PRTAD_OFST 4
+#define MC_CMD_MDIO_READ_IN_DEVAD_OFST 8
+#define MC_CMD_MDIO_READ_IN_ADDR_OFST 12
+#define MC_CMD_MDIO_READ_OUT_LEN 8
+#define MC_CMD_MDIO_READ_OUT_VALUE_OFST 0
+#define MC_CMD_MDIO_READ_OUT_STATUS_OFST 4
+
+/* MC_CMD_MDIO_WRITE:
+ * MDIO register write
+ */
+#define MC_CMD_MDIO_WRITE 0x11
+#define MC_CMD_MDIO_WRITE_IN_LEN 20
+#define MC_CMD_MDIO_WRITE_IN_BUS_OFST 0
+#define MC_CMD_MDIO_WRITE_IN_PRTAD_OFST 4
+#define MC_CMD_MDIO_WRITE_IN_DEVAD_OFST 8
+#define MC_CMD_MDIO_WRITE_IN_ADDR_OFST 12
+#define MC_CMD_MDIO_WRITE_IN_VALUE_OFST 16
+#define MC_CMD_MDIO_WRITE_OUT_LEN 4
+#define MC_CMD_MDIO_WRITE_OUT_STATUS_OFST 0
+
+/* By default all the MCDI MDIO operations perform clause45 mode.
+ * If you want to use clause22 then set DEVAD = MC_CMD_MDIO_CLAUSE22.
+ */
+#define MC_CMD_MDIO_CLAUSE22 32
+
+/* There are two MDIO buses: one for the internal PHY, and one for external
+ * devices.
+ */
+#define MC_CMD_MDIO_BUS_INTERNAL 0
+#define MC_CMD_MDIO_BUS_EXTERNAL 1
+
+/* The MDIO commands return the raw status bits from the MDIO block. A "good"
+ * transaction should have the DONE bit set and all other bits clear.
+ */
+#define MC_CMD_MDIO_STATUS_GOOD 0x08
+
+
+/* MC_CMD_DBI_WRITE: (debug)
+ * Write DBI register(s)
+ *
+ * Host: address, byte-enables (and VF selection, and cs2 flag),
+ * value [,address ...]
+ * MC: nothing
+ */
+#define MC_CMD_DBI_WRITE 0x12
+#define MC_CMD_DBI_WRITE_IN_LEN(_numwords) \
+ (12 * (_numwords))
+#define MC_CMD_DBI_WRITE_IN_ADDRESS_OFST(_word) \
+ (((_word) * 12) + 0)
+#define MC_CMD_DBI_WRITE_IN_BYTE_MASK_OFST(_word) \
+ (((_word) * 12) + 4)
+#define MC_CMD_DBI_WRITE_IN_VALUE_OFST(_word) \
+ (((_word) * 12) + 8)
+#define MC_CMD_DBI_WRITE_OUT_LEN 0
+
+/* MC_CMD_DBI_READ: (debug)
+ * Read DBI register(s)
+ *
+ * Host: address, [,address ...]
+ * MC: value [,value ...]
+ * (note: this does not support reading from VFs, but is retained for backwards
+ * compatibility; see MC_CMD_DBI_READX below)
+ */
+#define MC_CMD_DBI_READ 0x13
+#define MC_CMD_DBI_READ_IN_LEN(_numwords) \
+ (4 * (_numwords))
+#define MC_CMD_DBI_READ_OUT_LEN(_numwords) \
+ (4 * (_numwords))
+
+/* MC_CMD_PORT_READ32: (debug)
+ * Read a 32-bit register from the indirect port register map.
+ *
+ * The port to access is implied by the Shared memory channel used.
+ */
+#define MC_CMD_PORT_READ32 0x14
+#define MC_CMD_PORT_READ32_IN_LEN 4
+#define MC_CMD_PORT_READ32_IN_ADDR_OFST 0
+#define MC_CMD_PORT_READ32_OUT_LEN 8
+#define MC_CMD_PORT_READ32_OUT_VALUE_OFST 0
+#define MC_CMD_PORT_READ32_OUT_STATUS_OFST 4
+
+/* MC_CMD_PORT_WRITE32: (debug)
+ * Write a 32-bit register to the indirect port register map.
+ *
+ * The port to access is implied by the Shared memory channel used.
+ */
+#define MC_CMD_PORT_WRITE32 0x15
+#define MC_CMD_PORT_WRITE32_IN_LEN 8
+#define MC_CMD_PORT_WRITE32_IN_ADDR_OFST 0
+#define MC_CMD_PORT_WRITE32_IN_VALUE_OFST 4
+#define MC_CMD_PORT_WRITE32_OUT_LEN 4
+#define MC_CMD_PORT_WRITE32_OUT_STATUS_OFST 0
+
+/* MC_CMD_PORT_READ128: (debug)
+ * Read a 128-bit register from indirect port register map
+ *
+ * The port to access is implied by the Shared memory channel used.
+ */
+#define MC_CMD_PORT_READ128 0x16
+#define MC_CMD_PORT_READ128_IN_LEN 4
+#define MC_CMD_PORT_READ128_IN_ADDR_OFST 0
+#define MC_CMD_PORT_READ128_OUT_LEN 20
+#define MC_CMD_PORT_READ128_OUT_VALUE_OFST 0
+#define MC_CMD_PORT_READ128_OUT_STATUS_OFST 16
+
+/* MC_CMD_PORT_WRITE128: (debug)
+ * Write a 128-bit register to indirect port register map.
+ *
+ * The port to access is implied by the Shared memory channel used.
+ */
+#define MC_CMD_PORT_WRITE128 0x17
+#define MC_CMD_PORT_WRITE128_IN_LEN 20
+#define MC_CMD_PORT_WRITE128_IN_ADDR_OFST 0
+#define MC_CMD_PORT_WRITE128_IN_VALUE_OFST 4
+#define MC_CMD_PORT_WRITE128_OUT_LEN 4
+#define MC_CMD_PORT_WRITE128_OUT_STATUS_OFST 0
+
+/* MC_CMD_GET_BOARD_CFG:
+ * Returns the MC firmware configuration structure
+ *
+ * The FW_SUBTYPE_LIST contains a 16-bit value for each of the 12 types of
+ * NVRAM area. The values are defined in the firmware/mc/platform/<xxx>.c file
+ * for a specific board type, but otherwise have no meaning to the MC; they
+ * are used by the driver to manage selection of appropriate firmware updates.
+ */
+#define MC_CMD_GET_BOARD_CFG 0x18
+#define MC_CMD_GET_BOARD_CFG_IN_LEN 0
+#define MC_CMD_GET_BOARD_CFG_OUT_LEN 96
+#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_TYPE_OFST 0
+#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_OFST 4
+#define MC_CMD_GET_BOARD_CFG_OUT_BOARD_NAME_LEN 32
+#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT0_OFST 36
+#define MC_CMD_GET_BOARD_CFG_OUT_CAPABILITIES_PORT1_OFST 40
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST 44
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_LEN 6
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST 50
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_LEN 6
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT0_OFST 56
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_COUNT_PORT1_OFST 60
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT0_OFST 64
+#define MC_CMD_GET_BOARD_CFG_OUT_MAC_STRIDE_PORT1_OFST 68
+#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST 72
+#define MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN 24
+
+/* MC_CMD_DBI_READX: (debug)
+ * Read DBI register(s) -- extended functionality
+ *
+ * Host: vf selection, address, [,vf selection ...]
+ * MC: value [,value ...]
+ */
+#define MC_CMD_DBI_READX 0x19
+#define MC_CMD_DBI_READX_IN_LEN(_numwords) \
+ (8*(_numwords))
+#define MC_CMD_DBI_READX_OUT_LEN(_numwords) \
+ (4*(_numwords))
+
+/* MC_CMD_SET_RAND_SEED:
+ * Set the 16byte seed for the MC psuedo-random generator
+ */
+#define MC_CMD_SET_RAND_SEED 0x1a
+#define MC_CMD_SET_RAND_SEED_IN_LEN 16
+#define MC_CMD_SET_RAND_SEED_IN_SEED_OFST 0
+#define MC_CMD_SET_RAND_SEED_OUT_LEN 0
+
+/* MC_CMD_LTSSM_HIST: (debug)
+ * Retrieve the history of the LTSSM, if the build supports it.
+ *
+ * Host: nothing
+ * MC: variable number of LTSSM values, as bytes
+ * The history is read-to-clear.
+ */
+#define MC_CMD_LTSSM_HIST 0x1b
+
+/* MC_CMD_DRV_ATTACH:
+ * Inform MCPU that this port is managed on the host (i.e. driver active)
+ */
+#define MC_CMD_DRV_ATTACH 0x1c
+#define MC_CMD_DRV_ATTACH_IN_LEN 8
+#define MC_CMD_DRV_ATTACH_IN_NEW_STATE_OFST 0
+#define MC_CMD_DRV_ATTACH_IN_UPDATE_OFST 4
+#define MC_CMD_DRV_ATTACH_OUT_LEN 4
+#define MC_CMD_DRV_ATTACH_OUT_OLD_STATE_OFST 0
+
+/* MC_CMD_NCSI_PROD: (debug)
+ * Trigger an NC-SI event (and possibly an AEN in response)
+ */
+#define MC_CMD_NCSI_PROD 0x1d
+#define MC_CMD_NCSI_PROD_IN_LEN 4
+#define MC_CMD_NCSI_PROD_IN_EVENTS_OFST 0
+#define MC_CMD_NCSI_PROD_LINKCHANGE_LBN 0
+#define MC_CMD_NCSI_PROD_LINKCHANGE_WIDTH 1
+#define MC_CMD_NCSI_PROD_RESET_LBN 1
+#define MC_CMD_NCSI_PROD_RESET_WIDTH 1
+#define MC_CMD_NCSI_PROD_DRVATTACH_LBN 2
+#define MC_CMD_NCSI_PROD_DRVATTACH_WIDTH 1
+#define MC_CMD_NCSI_PROD_OUT_LEN 0
+
+/* Enumeration */
+#define MC_CMD_NCSI_PROD_LINKCHANGE 0
+#define MC_CMD_NCSI_PROD_RESET 1
+#define MC_CMD_NCSI_PROD_DRVATTACH 2
+
+/* MC_CMD_DEVEL: (debug)
+ * Reserved for development
+ */
+#define MC_CMD_DEVEL 0x1e
+
+/* MC_CMD_SHMUART: (debug)
+ * Route UART output to circular buffer in shared memory instead.
+ */
+#define MC_CMD_SHMUART 0x1f
+#define MC_CMD_SHMUART_IN_FLAG_OFST 0
+#define MC_CMD_SHMUART_IN_LEN 4
+#define MC_CMD_SHMUART_OUT_LEN 0
+
+/* MC_CMD_PORT_RESET:
+ * Generic per-port reset. There is no equivalent for per-board reset.
+ *
+ * Locks required: None
+ * Return code: 0, ETIME
+ */
+#define MC_CMD_PORT_RESET 0x20
+#define MC_CMD_PORT_RESET_IN_LEN 0
+#define MC_CMD_PORT_RESET_OUT_LEN 0
+
+/* MC_CMD_RESOURCE_LOCK:
+ * Generic resource lock/unlock interface.
+ *
+ * Locks required: None
+ * Return code: 0,
+ * EBUSY (if trylock is contended by other port),
+ * EDEADLK (if trylock is already acquired by this port)
+ * EINVAL (if unlock doesn't own the lock)
+ */
+#define MC_CMD_RESOURCE_LOCK 0x21
+#define MC_CMD_RESOURCE_LOCK_IN_LEN 8
+#define MC_CMD_RESOURCE_LOCK_IN_ACTION_OFST 0
+#define MC_CMD_RESOURCE_LOCK_ACTION_TRYLOCK 1
+#define MC_CMD_RESOURCE_LOCK_ACTION_UNLOCK 0
+#define MC_CMD_RESOURCE_LOCK_IN_RESOURCE_OFST 4
+#define MC_CMD_RESOURCE_LOCK_I2C 2
+#define MC_CMD_RESOURCE_LOCK_PHY 3
+#define MC_CMD_RESOURCE_LOCK_OUT_LEN 0
+
+/* MC_CMD_SPI_COMMAND: (variadic in, variadic out)
+ * Read/Write to/from the SPI device.
+ *
+ * Locks required: SPI_LOCK
+ * Return code: 0, ETIME, EINVAL, EACCES (if SPI_LOCK is not held)
+ */
+#define MC_CMD_SPI_COMMAND 0x22
+#define MC_CMD_SPI_COMMAND_IN_LEN(_write_bytes) (12 + (_write_bytes))
+#define MC_CMD_SPI_COMMAND_IN_ARGS_OFST 0
+#define MC_CMD_SPI_COMMAND_IN_ARGS_ADDRESS_OFST 0
+#define MC_CMD_SPI_COMMAND_IN_ARGS_READ_BYTES_OFST 4
+#define MC_CMD_SPI_COMMAND_IN_ARGS_CHIP_SELECT_OFST 8
+/* Data to write here */
+#define MC_CMD_SPI_COMMAND_IN_WRITE_BUFFER_OFST 12
+#define MC_CMD_SPI_COMMAND_OUT_LEN(_read_bytes) (_read_bytes)
+/* Data read here */
+#define MC_CMD_SPI_COMMAND_OUT_READ_BUFFER_OFST 0
+
+/* MC_CMD_I2C_READ_WRITE: (variadic in, variadic out)
+ * Read/Write to/from the I2C bus.
+ *
+ * Locks required: I2C_LOCK
+ * Return code: 0, ETIME, EINVAL, EACCES (if I2C_LOCK is not held)
+ */
+#define MC_CMD_I2C_RW 0x23
+#define MC_CMD_I2C_RW_IN_LEN(_write_bytes) (8 + (_write_bytes))
+#define MC_CMD_I2C_RW_IN_ARGS_OFST 0
+#define MC_CMD_I2C_RW_IN_ARGS_ADDR_OFST 0
+#define MC_CMD_I2C_RW_IN_ARGS_READ_BYTES_OFST 4
+/* Data to write here */
+#define MC_CMD_I2C_RW_IN_WRITE_BUFFER_OFSET 8
+#define MC_CMD_I2C_RW_OUT_LEN(_read_bytes) (_read_bytes)
+/* Data read here */
+#define MC_CMD_I2C_RW_OUT_READ_BUFFER_OFST 0
+
+/* Generic phy capability bitmask */
+#define MC_CMD_PHY_CAP_10HDX_LBN 1
+#define MC_CMD_PHY_CAP_10HDX_WIDTH 1
+#define MC_CMD_PHY_CAP_10FDX_LBN 2
+#define MC_CMD_PHY_CAP_10FDX_WIDTH 1
+#define MC_CMD_PHY_CAP_100HDX_LBN 3
+#define MC_CMD_PHY_CAP_100HDX_WIDTH 1
+#define MC_CMD_PHY_CAP_100FDX_LBN 4
+#define MC_CMD_PHY_CAP_100FDX_WIDTH 1
+#define MC_CMD_PHY_CAP_1000HDX_LBN 5
+#define MC_CMD_PHY_CAP_1000HDX_WIDTH 1
+#define MC_CMD_PHY_CAP_1000FDX_LBN 6
+#define MC_CMD_PHY_CAP_1000FDX_WIDTH 1
+#define MC_CMD_PHY_CAP_10000FDX_LBN 7
+#define MC_CMD_PHY_CAP_10000FDX_WIDTH 1
+#define MC_CMD_PHY_CAP_PAUSE_LBN 8
+#define MC_CMD_PHY_CAP_PAUSE_WIDTH 1
+#define MC_CMD_PHY_CAP_ASYM_LBN 9
+#define MC_CMD_PHY_CAP_ASYM_WIDTH 1
+#define MC_CMD_PHY_CAP_AN_LBN 10
+#define MC_CMD_PHY_CAP_AN_WIDTH 1
+
+/* Generic loopback enumeration */
+#define MC_CMD_LOOPBACK_NONE 0
+#define MC_CMD_LOOPBACK_DATA 1
+#define MC_CMD_LOOPBACK_GMAC 2
+#define MC_CMD_LOOPBACK_XGMII 3
+#define MC_CMD_LOOPBACK_XGXS 4
+#define MC_CMD_LOOPBACK_XAUI 5
+#define MC_CMD_LOOPBACK_GMII 6
+#define MC_CMD_LOOPBACK_SGMII 7
+#define MC_CMD_LOOPBACK_XGBR 8
+#define MC_CMD_LOOPBACK_XFI 9
+#define MC_CMD_LOOPBACK_XAUI_FAR 10
+#define MC_CMD_LOOPBACK_GMII_FAR 11
+#define MC_CMD_LOOPBACK_SGMII_FAR 12
+#define MC_CMD_LOOPBACK_XFI_FAR 13
+#define MC_CMD_LOOPBACK_GPHY 14
+#define MC_CMD_LOOPBACK_PHYXS 15
+#define MC_CMD_LOOPBACK_PCS 16
+#define MC_CMD_LOOPBACK_PMAPMD 17
+#define MC_CMD_LOOPBACK_XPORT 18
+#define MC_CMD_LOOPBACK_XGMII_WS 19
+#define MC_CMD_LOOPBACK_XAUI_WS 20
+#define MC_CMD_LOOPBACK_XAUI_WS_FAR 21
+#define MC_CMD_LOOPBACK_XAUI_WS_NEAR 22
+#define MC_CMD_LOOPBACK_GMII_WS 23
+#define MC_CMD_LOOPBACK_XFI_WS 24
+#define MC_CMD_LOOPBACK_XFI_WS_FAR 25
+#define MC_CMD_LOOPBACK_PHYXS_WS 26
+
+/* Generic PHY statistics enumeration */
+#define MC_CMD_OUI 0
+#define MC_CMD_PMA_PMD_LINK_UP 1
+#define MC_CMD_PMA_PMD_RX_FAULT 2
+#define MC_CMD_PMA_PMD_TX_FAULT 3
+#define MC_CMD_PMA_PMD_SIGNAL 4
+#define MC_CMD_PMA_PMD_SNR_A 5
+#define MC_CMD_PMA_PMD_SNR_B 6
+#define MC_CMD_PMA_PMD_SNR_C 7
+#define MC_CMD_PMA_PMD_SNR_D 8
+#define MC_CMD_PCS_LINK_UP 9
+#define MC_CMD_PCS_RX_FAULT 10
+#define MC_CMD_PCS_TX_FAULT 11
+#define MC_CMD_PCS_BER 12
+#define MC_CMD_PCS_BLOCK_ERRORS 13
+#define MC_CMD_PHYXS_LINK_UP 14
+#define MC_CMD_PHYXS_RX_FAULT 15
+#define MC_CMD_PHYXS_TX_FAULT 16
+#define MC_CMD_PHYXS_ALIGN 17
+#define MC_CMD_PHYXS_SYNC 18
+#define MC_CMD_AN_LINK_UP 19
+#define MC_CMD_AN_COMPLETE 20
+#define MC_CMD_AN_10GBT_STATUS 21
+#define MC_CMD_CL22_LINK_UP 22
+#define MC_CMD_PHY_NSTATS 23
+
+/* MC_CMD_GET_PHY_CFG:
+ * Report PHY configuration. This guarantees to succeed even if the PHY is in
+ * a "zombie" state.
+ *
+ * Locks required: None
+ * Return code: 0
+ */
+#define MC_CMD_GET_PHY_CFG 0x24
+
+#define MC_CMD_GET_PHY_CFG_IN_LEN 0
+#define MC_CMD_GET_PHY_CFG_OUT_LEN 72
+
+#define MC_CMD_GET_PHY_CFG_OUT_FLAGS_OFST 0
+#define MC_CMD_GET_PHY_CFG_PRESENT_LBN 0
+#define MC_CMD_GET_PHY_CFG_PRESENT_WIDTH 1
+#define MC_CMD_GET_PHY_CFG_SHORTBIST_LBN 1
+#define MC_CMD_GET_PHY_CFG_SHORTBIST_WIDTH 1
+#define MC_CMD_GET_PHY_CFG_LONGBIST_LBN 2
+#define MC_CMD_GET_PHY_CFG_LONGBIST_WIDTH 1
+#define MC_CMD_GET_PHY_CFG_LOWPOWER_LBN 3
+#define MC_CMD_GET_PHY_CFG_LOWPOWER_WIDTH 1
+#define MC_CMD_GET_PHY_CFG_POWEROFF_LBN 4
+#define MC_CMD_GET_PHY_CFG_POWEROFF_WIDTH 1
+#define MC_CMD_GET_PHY_CFG_TXDIS_LBN 5
+#define MC_CMD_GET_PHY_CFG_TXDIS_WIDTH 1
+#define MC_CMD_GET_PHY_CFG_OUT_TYPE_OFST 4
+/* Bitmask of supported capabilities */
+#define MC_CMD_GET_PHY_CFG_OUT_SUPPORTED_CAP_OFST 8
+#define MC_CMD_GET_PHY_CFG_OUT_CHANNEL_OFST 12
+#define MC_CMD_GET_PHY_CFG_OUT_PRT_OFST 16
+/* PHY statistics bitmap */
+#define MC_CMD_GET_PHY_CFG_OUT_STATS_MASK_OFST 20
+/* PHY type/name string */
+#define MC_CMD_GET_PHY_CFG_OUT_NAME_OFST 24
+#define MC_CMD_GET_PHY_CFG_OUT_NAME_LEN 20
+#define MC_CMD_GET_PHY_CFG_OUT_MEDIA_TYPE_OFST 44
+#define MC_CMD_MEDIA_XAUI 1
+#define MC_CMD_MEDIA_CX4 2
+#define MC_CMD_MEDIA_KX4 3
+#define MC_CMD_MEDIA_XFP 4
+#define MC_CMD_MEDIA_SFP_PLUS 5
+#define MC_CMD_MEDIA_BASE_T 6
+/* MDIO "MMDS" supported */
+#define MC_CMD_GET_PHY_CFG_OUT_MMD_MASK_OFST 48
+/* Native clause 22 */
+#define MC_CMD_MMD_CLAUSE22 0
+#define MC_CMD_MMD_CLAUSE45_PMAPMD 1
+#define MC_CMD_MMD_CLAUSE45_WIS 2
+#define MC_CMD_MMD_CLAUSE45_PCS 3
+#define MC_CMD_MMD_CLAUSE45_PHYXS 4
+#define MC_CMD_MMD_CLAUSE45_DTEXS 5
+#define MC_CMD_MMD_CLAUSE45_TC 6
+#define MC_CMD_MMD_CLAUSE45_AN 7
+/* Clause22 proxied over clause45 by PHY */
+#define MC_CMD_MMD_CLAUSE45_C22EXT 29
+#define MC_CMD_MMD_CLAUSE45_VEND1 30
+#define MC_CMD_MMD_CLAUSE45_VEND2 31
+/* PHY stepping version */
+#define MC_CMD_GET_PHY_CFG_OUT_REVISION_OFST 52
+#define MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN 20
+
+/* MC_CMD_START_PHY_BIST:
+ * Start a BIST test on the PHY.
+ *
+ * Locks required: PHY_LOCK if doing a PHY BIST
+ * Return code: 0, EINVAL, EACCES (if PHY_LOCK is not held)
+ */
+#define MC_CMD_START_BIST 0x25
+#define MC_CMD_START_BIST_IN_LEN 4
+#define MC_CMD_START_BIST_TYPE_OFST 0
+
+/* Run the PHY's short BIST */
+#define MC_CMD_PHY_BIST_SHORT 1
+/* Run the PHY's long BIST */
+#define MC_CMD_PHY_BIST_LONG 2
+/* Run BIST on the currently selected BPX Serdes (XAUI or XFI) */
+#define MC_CMD_BPX_SERDES_BIST 3
+
+/* MC_CMD_POLL_PHY_BIST: (variadic output)
+ * Poll for BIST completion
+ *
+ * Returns a single status code, and a binary blob of phy-specific
+ * bist output. If the driver can't succesfully parse the BIST output,
+ * it should still respect the Pass/Fail in OUT.RESULT.
+ *
+ * Locks required: PHY_LOCK if doing a PHY BIST
+ * Return code: 0, EACCES (if PHY_LOCK is not held)
+ */
+#define MC_CMD_POLL_BIST 0x26
+#define MC_CMD_POLL_BIST_IN_LEN 0
+#define MC_CMD_POLL_BIST_OUT_LEN UNKNOWN
+#define MC_CMD_POLL_BIST_OUT_RESULT_OFST 0
+#define MC_CMD_POLL_BIST_RUNNING 1
+#define MC_CMD_POLL_BIST_PASSED 2
+#define MC_CMD_POLL_BIST_FAILED 3
+#define MC_CMD_POLL_BIST_TIMEOUT 4
+#define MC_CMD_POLL_BIST_OUT_PRIVATE_OFST 4
+
+/* MC_CMD_PHY_SPI: (variadic in, variadic out)
+ * Read/Write/Erase the PHY SPI device
+ *
+ * Locks required: PHY_LOCK
+ * Return code: 0, ETIME, EINVAL, EACCES (if PHY_LOCK is not held)
+ */
+#define MC_CMD_PHY_SPI 0x27
+#define MC_CMD_PHY_SPI_IN_LEN(_write_bytes) (12 + (_write_bytes))
+#define MC_CMD_PHY_SPI_IN_ARGS_OFST 0
+#define MC_CMD_PHY_SPI_IN_ARGS_ADDR_OFST 0
+#define MC_CMD_PHY_SPI_IN_ARGS_READ_BYTES_OFST 4
+#define MC_CMD_PHY_SPI_IN_ARGS_ERASE_ALL_OFST 8
+/* Data to write here */
+#define MC_CMD_PHY_SPI_IN_WRITE_BUFFER_OFSET 12
+#define MC_CMD_PHY_SPI_OUT_LEN(_read_bytes) (_read_bytes)
+/* Data read here */
+#define MC_CMD_PHY_SPI_OUT_READ_BUFFER_OFST 0
+
+
+/* MC_CMD_GET_LOOPBACK_MODES:
+ * Returns a bitmask of loopback modes evailable at each speed.
+ *
+ * Locks required: None
+ * Return code: 0
+ */
+#define MC_CMD_GET_LOOPBACK_MODES 0x28
+#define MC_CMD_GET_LOOPBACK_MODES_IN_LEN 0
+#define MC_CMD_GET_LOOPBACK_MODES_OUT_LEN 32
+#define MC_CMD_GET_LOOPBACK_MODES_100M_OFST 0
+#define MC_CMD_GET_LOOPBACK_MODES_1G_OFST 8
+#define MC_CMD_GET_LOOPBACK_MODES_10G_OFST 16
+#define MC_CMD_GET_LOOPBACK_MODES_SUGGESTED_OFST 24
+
+/* Flow control enumeration */
+#define MC_CMD_FCNTL_OFF 0
+#define MC_CMD_FCNTL_RESPOND 1
+#define MC_CMD_FCNTL_BIDIR 2
+/* Auto - Use what the link has autonegotiated
+ * - The driver should modify the advertised capabilities via SET_LINK.CAP
+ * to control the negotiated flow control mode.
+ * - Can only be set if the PHY supports PAUSE+ASYM capabilities
+ * - Never returned by GET_LINK as the value programmed into the MAC
+ */
+#define MC_CMD_FCNTL_AUTO 3
+
+/* Generic mac fault bitmask */
+#define MC_CMD_MAC_FAULT_XGMII_LOCAL_LBN 0
+#define MC_CMD_MAC_FAULT_XGMII_LOCAL_WIDTH 1
+#define MC_CMD_MAC_FAULT_XGMII_REMOTE_LBN 1
+#define MC_CMD_MAC_FAULT_XGMII_REMOTE_WIDTH 1
+#define MC_CMD_MAC_FAULT_SGMII_REMOTE_LBN 2
+#define MC_CMD_MAC_FAULT_SGMII_REMOTE_WIDTH 1
+
+/* MC_CMD_GET_LINK:
+ * Read the unified MAC/PHY link state
+ *
+ * Locks required: None
+ * Return code: 0, ETIME
+ */
+#define MC_CMD_GET_LINK 0x29
+#define MC_CMD_GET_LINK_IN_LEN 0
+#define MC_CMD_GET_LINK_OUT_LEN 28
+/* near-side and link-partner advertised capabilities */
+#define MC_CMD_GET_LINK_OUT_CAP_OFST 0
+#define MC_CMD_GET_LINK_OUT_LP_CAP_OFST 4
+/* Autonegotiated speed in mbit/s. The link may still be down
+ * even if this reads non-zero */
+#define MC_CMD_GET_LINK_OUT_LINK_SPEED_OFST 8
+#define MC_CMD_GET_LINK_OUT_LOOPBACK_MODE_OFST 12
+#define MC_CMD_GET_LINK_OUT_FLAGS_OFST 16
+/* Whether we have overall link up */
+#define MC_CMD_GET_LINK_LINK_UP_LBN 0
+#define MC_CMD_GET_LINK_LINK_UP_WIDTH 1
+#define MC_CMD_GET_LINK_FULL_DUPLEX_LBN 1
+#define MC_CMD_GET_LINK_FULL_DUPLEX_WIDTH 1
+/* Whether we have link at the layers provided by the BPX */
+#define MC_CMD_GET_LINK_BPX_LINK_LBN 2
+#define MC_CMD_GET_LINK_BPX_LINK_WIDTH 1
+/* Whether the PHY has external link */
+#define MC_CMD_GET_LINK_PHY_LINK_LBN 3
+#define MC_CMD_GET_LINK_PHY_LINK_WIDTH 1
+#define MC_CMD_GET_LINK_OUT_FCNTL_OFST 20
+#define MC_CMD_GET_LINK_OUT_MAC_FAULT_OFST 24
+
+/* MC_CMD_SET_LINK:
+ * Write the unified MAC/PHY link configuration
+ *
+ * A loopback speed of "0" is supported, and means
+ * (choose any available speed)
+ *
+ * Locks required: None
+ * Return code: 0, EINVAL, ETIME
+ */
+#define MC_CMD_SET_LINK 0x2a
+#define MC_CMD_SET_LINK_IN_LEN 16
+#define MC_CMD_SET_LINK_IN_CAP_OFST 0
+#define MC_CMD_SET_LINK_IN_FLAGS_OFST 4
+#define MC_CMD_SET_LINK_LOWPOWER_LBN 0
+#define MC_CMD_SET_LINK_LOWPOWER_WIDTH 1
+#define MC_CMD_SET_LINK_POWEROFF_LBN 1
+#define MC_CMD_SET_LINK_POWEROFF_WIDTH 1
+#define MC_CMD_SET_LINK_TXDIS_LBN 2
+#define MC_CMD_SET_LINK_TXDIS_WIDTH 1
+#define MC_CMD_SET_LINK_IN_LOOPBACK_MODE_OFST 8
+#define MC_CMD_SET_LINK_IN_LOOPBACK_SPEED_OFST 12
+#define MC_CMD_SET_LINK_OUT_LEN 0
+
+/* MC_CMD_SET_ID_LED:
+ * Set indentification LED state
+ *
+ * Locks required: None
+ * Return code: 0, EINVAL
+ */
+#define MC_CMD_SET_ID_LED 0x2b
+#define MC_CMD_SET_ID_LED_IN_LEN 4
+#define MC_CMD_SET_ID_LED_IN_STATE_OFST 0
+#define MC_CMD_LED_OFF 0
+#define MC_CMD_LED_ON 1
+#define MC_CMD_LED_DEFAULT 2
+#define MC_CMD_SET_ID_LED_OUT_LEN 0
+
+/* MC_CMD_SET_MAC:
+ * Set MAC configuration
+ *
+ * The MTU is the MTU programmed directly into the XMAC/GMAC
+ * (inclusive of EtherII, VLAN, bug16011 padding)
+ *
+ * Locks required: None
+ * Return code: 0, EINVAL
+ */
+#define MC_CMD_SET_MAC 0x2c
+#define MC_CMD_SET_MAC_IN_LEN 24
+#define MC_CMD_SET_MAC_IN_MTU_OFST 0
+#define MC_CMD_SET_MAC_IN_DRAIN_OFST 4
+#define MC_CMD_SET_MAC_IN_ADDR_OFST 8
+#define MC_CMD_SET_MAC_IN_REJECT_OFST 16
+#define MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN 0
+#define MC_CMD_SET_MAC_IN_REJECT_UNCST_WIDTH 1
+#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_LBN 1
+#define MC_CMD_SET_MAC_IN_REJECT_BRDCST_WIDTH 1
+#define MC_CMD_SET_MAC_IN_FCNTL_OFST 20
+#define MC_CMD_SET_MAC_OUT_LEN 0
+
+/* MC_CMD_PHY_STATS:
+ * Get generic PHY statistics
+ *
+ * This call returns the statistics for a generic PHY, by direct DMA
+ * into host memory, in a sparse array (indexed by the enumerate).
+ * Each value is represented by a 32bit number.
+ *
+ * Locks required: None
+ * Returns: 0, ETIME
+ * Response methods: shared memory, event
+ */
+#define MC_CMD_PHY_STATS 0x2d
+#define MC_CMD_PHY_STATS_IN_LEN 8
+#define MC_CMD_PHY_STATS_IN_DMA_ADDR_LO_OFST 0
+#define MC_CMD_PHY_STATS_IN_DMA_ADDR_HI_OFST 4
+#define MC_CMD_PHY_STATS_OUT_LEN 0
+
+/* Unified MAC statistics enumeration */
+#define MC_CMD_MAC_GENERATION_START 0
+#define MC_CMD_MAC_TX_PKTS 1
+#define MC_CMD_MAC_TX_PAUSE_PKTS 2
+#define MC_CMD_MAC_TX_CONTROL_PKTS 3
+#define MC_CMD_MAC_TX_UNICAST_PKTS 4
+#define MC_CMD_MAC_TX_MULTICAST_PKTS 5
+#define MC_CMD_MAC_TX_BROADCAST_PKTS 6
+#define MC_CMD_MAC_TX_BYTES 7
+#define MC_CMD_MAC_TX_BAD_BYTES 8
+#define MC_CMD_MAC_TX_LT64_PKTS 9
+#define MC_CMD_MAC_TX_64_PKTS 10
+#define MC_CMD_MAC_TX_65_TO_127_PKTS 11
+#define MC_CMD_MAC_TX_128_TO_255_PKTS 12
+#define MC_CMD_MAC_TX_256_TO_511_PKTS 13
+#define MC_CMD_MAC_TX_512_TO_1023_PKTS 14
+#define MC_CMD_MAC_TX_1024_TO_15XX_PKTS 15
+#define MC_CMD_MAC_TX_15XX_TO_JUMBO_PKTS 16
+#define MC_CMD_MAC_TX_GTJUMBO_PKTS 17
+#define MC_CMD_MAC_TX_BAD_FCS_PKTS 18
+#define MC_CMD_MAC_TX_SINGLE_COLLISION_PKTS 19
+#define MC_CMD_MAC_TX_MULTIPLE_COLLISION_PKTS 20
+#define MC_CMD_MAC_TX_EXCESSIVE_COLLISION_PKTS 21
+#define MC_CMD_MAC_TX_LATE_COLLISION_PKTS 22
+#define MC_CMD_MAC_TX_DEFERRED_PKTS 23
+#define MC_CMD_MAC_TX_EXCESSIVE_DEFERRED_PKTS 24
+#define MC_CMD_MAC_TX_NON_TCPUDP_PKTS 25
+#define MC_CMD_MAC_TX_MAC_SRC_ERR_PKTS 26
+#define MC_CMD_MAC_TX_IP_SRC_ERR_PKTS 27
+#define MC_CMD_MAC_RX_PKTS 28
+#define MC_CMD_MAC_RX_PAUSE_PKTS 29
+#define MC_CMD_MAC_RX_GOOD_PKTS 30
+#define MC_CMD_MAC_RX_CONTROL_PKTS 31
+#define MC_CMD_MAC_RX_UNICAST_PKTS 32
+#define MC_CMD_MAC_RX_MULTICAST_PKTS 33
+#define MC_CMD_MAC_RX_BROADCAST_PKTS 34
+#define MC_CMD_MAC_RX_BYTES 35
+#define MC_CMD_MAC_RX_BAD_BYTES 36
+#define MC_CMD_MAC_RX_64_PKTS 37
+#define MC_CMD_MAC_RX_65_TO_127_PKTS 38
+#define MC_CMD_MAC_RX_128_TO_255_PKTS 39
+#define MC_CMD_MAC_RX_256_TO_511_PKTS 40
+#define MC_CMD_MAC_RX_512_TO_1023_PKTS 41
+#define MC_CMD_MAC_RX_1024_TO_15XX_PKTS 42
+#define MC_CMD_MAC_RX_15XX_TO_JUMBO_PKTS 43
+#define MC_CMD_MAC_RX_GTJUMBO_PKTS 44
+#define MC_CMD_MAC_RX_UNDERSIZE_PKTS 45
+#define MC_CMD_MAC_RX_BAD_FCS_PKTS 46
+#define MC_CMD_MAC_RX_OVERFLOW_PKTS 47
+#define MC_CMD_MAC_RX_FALSE_CARRIER_PKTS 48
+#define MC_CMD_MAC_RX_SYMBOL_ERROR_PKTS 49
+#define MC_CMD_MAC_RX_ALIGN_ERROR_PKTS 50
+#define MC_CMD_MAC_RX_LENGTH_ERROR_PKTS 51
+#define MC_CMD_MAC_RX_INTERNAL_ERROR_PKTS 52
+#define MC_CMD_MAC_RX_JABBER_PKTS 53
+#define MC_CMD_MAC_RX_NODESC_DROPS 54
+#define MC_CMD_MAC_RX_LANES01_CHAR_ERR 55
+#define MC_CMD_MAC_RX_LANES23_CHAR_ERR 56
+#define MC_CMD_MAC_RX_LANES01_DISP_ERR 57
+#define MC_CMD_MAC_RX_LANES23_DISP_ERR 58
+#define MC_CMD_MAC_RX_MATCH_FAULT 59
+/* Insert new members here. */
+#define MC_CMD_MAC_GENERATION_END 60
+#define MC_CMD_MAC_NSTATS (MC_CMD_MAC_GENERATION_END+1)
+
+/* MC_CMD_MAC_STATS:
+ * Get unified GMAC/XMAC statistics
+ *
+ * This call returns unified statistics maintained by the MC as it
+ * switches between the GMAC and XMAC. The MC will write out all
+ * supported stats. The driver should zero initialise the buffer to
+ * guarantee consistent results.
+ *
+ * Locks required: None
+ * Returns: 0
+ * Response methods: shared memory, event
+ */
+#define MC_CMD_MAC_STATS 0x2e
+#define MC_CMD_MAC_STATS_IN_LEN 16
+#define MC_CMD_MAC_STATS_IN_DMA_ADDR_LO_OFST 0
+#define MC_CMD_MAC_STATS_IN_DMA_ADDR_HI_OFST 4
+#define MC_CMD_MAC_STATS_IN_CMD_OFST 8
+#define MC_CMD_MAC_STATS_CMD_DMA_LBN 0
+#define MC_CMD_MAC_STATS_CMD_DMA_WIDTH 1
+#define MC_CMD_MAC_STATS_CMD_CLEAR_LBN 1
+#define MC_CMD_MAC_STATS_CMD_CLEAR_WIDTH 1
+#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_LBN 2
+#define MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE_WIDTH 1
+/* Fields only relevent when PERIODIC_CHANGE is set */
+#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_LBN 3
+#define MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE_WIDTH 1
+#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_LBN 4
+#define MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR_WIDTH 1
+#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_LBN 16
+#define MC_CMD_MAC_STATS_CMD_PERIOD_MS_WIDTH 16
+#define MC_CMD_MAC_STATS_IN_DMA_LEN_OFST 12
+
+#define MC_CMD_MAC_STATS_OUT_LEN 0
+
+/* Callisto flags */
+#define MC_CMD_SFT9001_ROBUST_LBN 0
+#define MC_CMD_SFT9001_ROBUST_WIDTH 1
+#define MC_CMD_SFT9001_SHORT_REACH_LBN 1
+#define MC_CMD_SFT9001_SHORT_REACH_WIDTH 1
+
+/* MC_CMD_SFT9001_GET:
+ * Read current callisto specific setting
+ *
+ * Locks required: None
+ * Returns: 0, ETIME
+ */
+#define MC_CMD_SFT9001_GET 0x30
+#define MC_CMD_SFT9001_GET_IN_LEN 0
+#define MC_CMD_SFT9001_GET_OUT_LEN 4
+#define MC_CMD_SFT9001_GET_OUT_FLAGS_OFST 0
+
+/* MC_CMD_SFT9001_SET:
+ * Write current callisto specific setting
+ *
+ * Locks required: None
+ * Returns: 0, ETIME, EINVAL
+ */
+#define MC_CMD_SFT9001_SET 0x31
+#define MC_CMD_SFT9001_SET_IN_LEN 4
+#define MC_CMD_SFT9001_SET_IN_FLAGS_OFST 0
+#define MC_CMD_SFT9001_SET_OUT_LEN 0
+
+
+/* MC_CMD_WOL_FILTER_SET:
+ * Set a WoL filter
+ *
+ * Locks required: None
+ * Returns: 0, EBUSY, EINVAL, ENOSYS
+ */
+#define MC_CMD_WOL_FILTER_SET 0x32
+#define MC_CMD_WOL_FILTER_SET_IN_LEN 192 /* 190 rounded up to a word */
+#define MC_CMD_WOL_FILTER_SET_IN_FILTER_MODE_OFST 0
+#define MC_CMD_WOL_FILTER_SET_IN_WOL_TYPE_OFST 4
+
+/* There is a union at offset 8, following defines overlap due to
+ * this */
+#define MC_CMD_WOL_FILTER_SET_IN_DATA_OFST 8
+
+#define MC_CMD_WOL_FILTER_SET_IN_MAGIC_MAC_OFST \
+ MC_CMD_WOL_FILTER_SET_IN_DATA_OFST
+
+#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_IP_OFST \
+ MC_CMD_WOL_FILTER_SET_IN_DATA_OFST
+#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_IP_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 4)
+#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_SRC_PORT_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 8)
+#define MC_CMD_WOL_FILTER_SET_IN_IPV4_SYN_DST_PORT_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 10)
+
+#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_IP_OFST \
+ MC_CMD_WOL_FILTER_SET_IN_DATA_OFST
+#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_IP_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 16)
+#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_SRC_PORT_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 32)
+#define MC_CMD_WOL_FILTER_SET_IN_IPV6_SYN_DST_PORT_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 34)
+
+#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_MASK_OFST \
+ MC_CMD_WOL_FILTER_SET_IN_DATA_OFST
+#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 48)
+#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LEN_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 176)
+#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER3_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 177)
+#define MC_CMD_WOL_FILTER_SET_IN_BITMAP_LAYER4_OFST \
+ (MC_CMD_WOL_FILTER_SET_IN_DATA_OFST + 178)
+
+#define MC_CMD_WOL_FILTER_SET_OUT_LEN 4
+#define MC_CMD_WOL_FILTER_SET_OUT_FILTER_ID_OFST 0
+
+/* WOL Filter types enumeration */
+#define MC_CMD_WOL_TYPE_MAGIC 0x0
+ /* unused 0x1 */
+#define MC_CMD_WOL_TYPE_WIN_MAGIC 0x2
+#define MC_CMD_WOL_TYPE_IPV4_SYN 0x3
+#define MC_CMD_WOL_TYPE_IPV6_SYN 0x4
+#define MC_CMD_WOL_TYPE_BITMAP 0x5
+#define MC_CMD_WOL_TYPE_MAX 0x6
+
+#define MC_CMD_FILTER_MODE_SIMPLE 0x0
+#define MC_CMD_FILTER_MODE_STRUCTURED 0xffffffff
+
+/* MC_CMD_WOL_FILTER_REMOVE:
+ * Remove a WoL filter
+ *
+ * Locks required: None
+ * Returns: 0, EINVAL, ENOSYS
+ */
+#define MC_CMD_WOL_FILTER_REMOVE 0x33
+#define MC_CMD_WOL_FILTER_REMOVE_IN_LEN 4
+#define MC_CMD_WOL_FILTER_REMOVE_IN_FILTER_ID_OFST 0
+#define MC_CMD_WOL_FILTER_REMOVE_OUT_LEN 0
+
+
+/* MC_CMD_WOL_FILTER_RESET:
+ * Reset (i.e. remove all) WoL filters
+ *
+ * Locks required: None
+ * Returns: 0, ENOSYS
+ */
+#define MC_CMD_WOL_FILTER_RESET 0x34
+#define MC_CMD_WOL_FILTER_RESET_IN_LEN 0
+#define MC_CMD_WOL_FILTER_RESET_OUT_LEN 0
+
+/* MC_CMD_SET_MCAST_HASH:
+ * Set the MCASH hash value without otherwise
+ * reconfiguring the MAC
+ */
+#define MC_CMD_SET_MCAST_HASH 0x35
+#define MC_CMD_SET_MCAST_HASH_IN_LEN 32
+#define MC_CMD_SET_MCAST_HASH_IN_HASH0_OFST 0
+#define MC_CMD_SET_MCAST_HASH_IN_HASH1_OFST 16
+#define MC_CMD_SET_MCAST_HASH_OUT_LEN 0
+
+/* MC_CMD_NVRAM_TYPES:
+ * Return bitfield indicating available types of virtual NVRAM partitions
+ *
+ * Locks required: none
+ * Returns: 0
+ */
+#define MC_CMD_NVRAM_TYPES 0x36
+#define MC_CMD_NVRAM_TYPES_IN_LEN 0
+#define MC_CMD_NVRAM_TYPES_OUT_LEN 4
+#define MC_CMD_NVRAM_TYPES_OUT_TYPES_OFST 0
+
+/* Supported NVRAM types */
+#define MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO 0
+#define MC_CMD_NVRAM_TYPE_MC_FW 1
+#define MC_CMD_NVRAM_TYPE_MC_FW_BACKUP 2
+#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 3
+#define MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1 4
+#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 5
+#define MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1 6
+#define MC_CMD_NVRAM_TYPE_EXP_ROM 7
+#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0 8
+#define MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1 9
+#define MC_CMD_NVRAM_TYPE_PHY_PORT0 10
+#define MC_CMD_NVRAM_TYPE_PHY_PORT1 11
+#define MC_CMD_NVRAM_TYPE_LOG 12
+
+/* MC_CMD_NVRAM_INFO:
+ * Read info about a virtual NVRAM partition
+ *
+ * Locks required: none
+ * Returns: 0, EINVAL (bad type)
+ */
+#define MC_CMD_NVRAM_INFO 0x37
+#define MC_CMD_NVRAM_INFO_IN_LEN 4
+#define MC_CMD_NVRAM_INFO_IN_TYPE_OFST 0
+#define MC_CMD_NVRAM_INFO_OUT_LEN 24
+#define MC_CMD_NVRAM_INFO_OUT_TYPE_OFST 0
+#define MC_CMD_NVRAM_INFO_OUT_SIZE_OFST 4
+#define MC_CMD_NVRAM_INFO_OUT_ERASESIZE_OFST 8
+#define MC_CMD_NVRAM_INFO_OUT_FLAGS_OFST 12
+#define MC_CMD_NVRAM_PROTECTED_LBN 0
+#define MC_CMD_NVRAM_PROTECTED_WIDTH 1
+#define MC_CMD_NVRAM_INFO_OUT_PHYSDEV_OFST 16
+#define MC_CMD_NVRAM_INFO_OUT_PHYSADDR_OFST 20
+
+/* MC_CMD_NVRAM_UPDATE_START:
+ * Start a group of update operations on a virtual NVRAM partition
+ *
+ * Locks required: PHY_LOCK if type==*PHY*
+ * Returns: 0, EINVAL (bad type), EACCES (if PHY_LOCK required and not held)
+ */
+#define MC_CMD_NVRAM_UPDATE_START 0x38
+#define MC_CMD_NVRAM_UPDATE_START_IN_LEN 4
+#define MC_CMD_NVRAM_UPDATE_START_IN_TYPE_OFST 0
+#define MC_CMD_NVRAM_UPDATE_START_OUT_LEN 0
+
+/* MC_CMD_NVRAM_READ:
+ * Read data from a virtual NVRAM partition
+ *
+ * Locks required: PHY_LOCK if type==*PHY*
+ * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held)
+ */
+#define MC_CMD_NVRAM_READ 0x39
+#define MC_CMD_NVRAM_READ_IN_LEN 12
+#define MC_CMD_NVRAM_READ_IN_TYPE_OFST 0
+#define MC_CMD_NVRAM_READ_IN_OFFSET_OFST 4
+#define MC_CMD_NVRAM_READ_IN_LENGTH_OFST 8
+#define MC_CMD_NVRAM_READ_OUT_LEN(_read_bytes) (_read_bytes)
+#define MC_CMD_NVRAM_READ_OUT_READ_BUFFER_OFST 0
+
+/* MC_CMD_NVRAM_WRITE:
+ * Write data to a virtual NVRAM partition
+ *
+ * Locks required: PHY_LOCK if type==*PHY*
+ * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held)
+ */
+#define MC_CMD_NVRAM_WRITE 0x3a
+#define MC_CMD_NVRAM_WRITE_IN_TYPE_OFST 0
+#define MC_CMD_NVRAM_WRITE_IN_OFFSET_OFST 4
+#define MC_CMD_NVRAM_WRITE_IN_LENGTH_OFST 8
+#define MC_CMD_NVRAM_WRITE_IN_WRITE_BUFFER_OFST 12
+#define MC_CMD_NVRAM_WRITE_IN_LEN(_write_bytes) (12 + _write_bytes)
+#define MC_CMD_NVRAM_WRITE_OUT_LEN 0
+
+/* MC_CMD_NVRAM_ERASE:
+ * Erase sector(s) from a virtual NVRAM partition
+ *
+ * Locks required: PHY_LOCK if type==*PHY*
+ * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held)
+ */
+#define MC_CMD_NVRAM_ERASE 0x3b
+#define MC_CMD_NVRAM_ERASE_IN_LEN 12
+#define MC_CMD_NVRAM_ERASE_IN_TYPE_OFST 0
+#define MC_CMD_NVRAM_ERASE_IN_OFFSET_OFST 4
+#define MC_CMD_NVRAM_ERASE_IN_LENGTH_OFST 8
+#define MC_CMD_NVRAM_ERASE_OUT_LEN 0
+
+/* MC_CMD_NVRAM_UPDATE_FINISH:
+ * Finish a group of update operations on a virtual NVRAM partition
+ *
+ * Locks required: PHY_LOCK if type==*PHY*
+ * Returns: 0, EINVAL (bad type/offset/length), EACCES (if PHY_LOCK required and not held)
+ */
+#define MC_CMD_NVRAM_UPDATE_FINISH 0x3c
+#define MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN 4
+#define MC_CMD_NVRAM_UPDATE_FINISH_IN_TYPE_OFST 0
+#define MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN 0
+
+/* MC_CMD_REBOOT:
+ * Reboot the MC. The AFTER_ASSERTION flag is intended to be used
+ * when the driver notices an assertion failure, to allow two ports to
+ * both recover (semi-)gracefully.
+ *
+ * Locks required: NONE
+ * Returns: Nothing. You get back a response with ERR=1, DATALEN=0
+ */
+#define MC_CMD_REBOOT 0x3d
+#define MC_CMD_REBOOT_IN_LEN 4
+#define MC_CMD_REBOOT_IN_FLAGS_OFST 0
+#define MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION 1
+#define MC_CMD_REBOOT_OUT_LEN 0
+
+/* MC_CMD_SCHEDINFO:
+ * Request scheduler info. from the MC.
+ *
+ * Locks required: NONE
+ * Returns: An array of (timeslice,maximum overrun), one for each thread,
+ * in ascending order of thread address.s
+ */
+#define MC_CMD_SCHEDINFO 0x3e
+#define MC_CMD_SCHEDINFO_IN_LEN 0
+
+
+/* MC_CMD_SET_REBOOT_MODE: (debug)
+ * Set the mode for the next MC reboot.
+ *
+ * Locks required: NONE
+ *
+ * Sets the reboot mode to the specified value. Returns the old mode.
+ */
+#define MC_CMD_REBOOT_MODE 0x3f
+#define MC_CMD_REBOOT_MODE_IN_LEN 4
+#define MC_CMD_REBOOT_MODE_IN_VALUE_OFST 0
+#define MC_CMD_REBOOT_MODE_OUT_LEN 4
+#define MC_CMD_REBOOT_MODE_OUT_VALUE_OFST 0
+#define MC_CMD_REBOOT_MODE_NORMAL 0
+#define MC_CMD_REBOOT_MODE_SNAPPER 3
+
+/* MC_CMD_DEBUG_LOG:
+ * Null request/response command (debug)
+ * - sequence number is always zero
+ * - only supported on the UART interface
+ * (the same set of bytes is delivered as an
+ * event over PCI)
+ */
+#define MC_CMD_DEBUG_LOG 0x40
+#define MC_CMD_DEBUG_LOG_IN_LEN 0
+#define MC_CMD_DEBUG_LOG_OUT_LEN 0
+
+/* Generic sensor enumeration. Note that a dual port NIC
+ * will EITHER expose PHY_COMMON_TEMP OR PHY0_TEMP and
+ * PHY1_TEMP depending on whether there is a single sensor
+ * in the vicinity of the two port, or one per port.
+ */
+#define MC_CMD_SENSOR_CONTROLLER_TEMP 0 /* degC */
+#define MC_CMD_SENSOR_PHY_COMMON_TEMP 1 /* degC */
+#define MC_CMD_SENSOR_CONTROLLER_COOLING 2 /* bool */
+#define MC_CMD_SENSOR_PHY0_TEMP 3 /* degC */
+#define MC_CMD_SENSOR_PHY0_COOLING 4 /* bool */
+#define MC_CMD_SENSOR_PHY1_TEMP 5 /* degC */
+#define MC_CMD_SENSOR_PHY1_COOLING 6 /* bool */
+#define MC_CMD_SENSOR_IN_1V0 7 /* mV */
+#define MC_CMD_SENSOR_IN_1V2 8 /* mV */
+#define MC_CMD_SENSOR_IN_1V8 9 /* mV */
+#define MC_CMD_SENSOR_IN_2V5 10 /* mV */
+#define MC_CMD_SENSOR_IN_3V3 11 /* mV */
+#define MC_CMD_SENSOR_IN_12V0 12 /* mV */
+
+
+/* Sensor state */
+#define MC_CMD_SENSOR_STATE_OK 0
+#define MC_CMD_SENSOR_STATE_WARNING 1
+#define MC_CMD_SENSOR_STATE_FATAL 2
+#define MC_CMD_SENSOR_STATE_BROKEN 3
+
+/* MC_CMD_SENSOR_INFO:
+ * Returns information about every available sensor.
+ *
+ * Each sensor has a single (16bit) value, and a corresponding state.
+ * The mapping between value and sensor is nominally determined by the
+ * MC, but in practise is implemented as zero (BROKEN), one (TEMPERATURE),
+ * or two (VOLTAGE) ranges per sensor per state.
+ *
+ * This call returns a mask (32bit) of the sensors that are supported
+ * by this platform, then an array (indexed by MC_CMD_SENSOR) of byte
+ * offsets to the per-sensor arrays. Each sensor array has four 16bit
+ * numbers, min1, max1, min2, max2.
+ *
+ * Locks required: None
+ * Returns: 0
+ */
+#define MC_CMD_SENSOR_INFO 0x41
+#define MC_CMD_SENSOR_INFO_IN_LEN 0
+#define MC_CMD_SENSOR_INFO_OUT_MASK_OFST 0
+#define MC_CMD_SENSOR_INFO_OUT_OFFSET_OFST(_x) \
+ (4 + (_x))
+#define MC_CMD_SENSOR_INFO_OUT_MIN1_OFST(_ofst) \
+ ((_ofst) + 0)
+#define MC_CMD_SENSOR_INFO_OUT_MAX1_OFST(_ofst) \
+ ((_ofst) + 2)
+#define MC_CMD_SENSOR_INFO_OUT_MIN2_OFST(_ofst) \
+ ((_ofst) + 4)
+#define MC_CMD_SENSOR_INFO_OUT_MAX2_OFST(_ofst) \
+ ((_ofst) + 6)
+
+/* MC_CMD_READ_SENSORS
+ * Returns the current (value, state) for each sensor
+ *
+ * Returns the current (value, state) [each 16bit] of each sensor supported by
+ * this board, by DMA'ing a sparse array (indexed by the sensor type) into host
+ * memory.
+ *
+ * The MC will send a SENSOREVT event every time any sensor changes state. The
+ * driver is responsible for ensuring that it doesn't miss any events. The board
+ * will function normally if all sensors are in STATE_OK or state_WARNING.
+ * Otherwise the board should not be expected to function.
+ */
+#define MC_CMD_READ_SENSORS 0x42
+#define MC_CMD_READ_SENSORS_IN_LEN 8
+#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_LO_OFST 0
+#define MC_CMD_READ_SENSORS_IN_DMA_ADDR_HI_OFST 4
+#define MC_CMD_READ_SENSORS_OUT_LEN 0
+
+
+/* MC_CMD_GET_PHY_STATE:
+ * Report current state of PHY. A "zombie" PHY is a PHY that has failed to
+ * boot (e.g. due to missing or corrupted firmware).
+ *
+ * Locks required: None
+ * Return code: 0
+ */
+#define MC_CMD_GET_PHY_STATE 0x43
+
+#define MC_CMD_GET_PHY_STATE_IN_LEN 0
+#define MC_CMD_GET_PHY_STATE_OUT_LEN 4
+#define MC_CMD_GET_PHY_STATE_STATE_OFST 0
+/* PHY state enumeration: */
+#define MC_CMD_PHY_STATE_OK 1
+#define MC_CMD_PHY_STATE_ZOMBIE 2
+
+
+/* 802.1Qbb control. 8 Tx queues that map to priorities 0 - 7. Use all 1s to
+ * disable 802.Qbb for a given priority. */
+#define MC_CMD_SETUP_8021QBB 0x44
+#define MC_CMD_SETUP_8021QBB_IN_LEN 32
+#define MC_CMD_SETUP_8021QBB_OUT_LEN 0
+#define MC_CMD_SETUP_8021QBB_IN_TXQS_OFFST 0
+
+
+/* MC_CMD_WOL_FILTER_GET:
+ * Retrieve ID of any WoL filters
+ *
+ * Locks required: None
+ * Returns: 0, ENOSYS
+ */
+#define MC_CMD_WOL_FILTER_GET 0x45
+#define MC_CMD_WOL_FILTER_GET_IN_LEN 0
+#define MC_CMD_WOL_FILTER_GET_OUT_LEN 4
+#define MC_CMD_WOL_FILTER_GET_OUT_FILTER_ID_OFST 0
+
+
+/* MC_CMD_ADD_LIGHTSOUT_OFFLOAD:
+ * Offload a protocol to NIC for lights-out state
+ *
+ * Locks required: None
+ * Returns: 0, ENOSYS
+ */
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD 0x46
+
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_LEN 16
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0
+
+/* There is a union at offset 4, following defines overlap due to
+ * this */
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_DATA_OFST 4
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPMAC_OFST 4
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARPIP_OFST 10
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSMAC_OFST 4
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSSNIPV6_OFST 10
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NSIPV6_OFST 26
+
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN 4
+#define MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID_OFST 0
+
+
+/* MC_CMD_REMOVE_LIGHTSOUT_PROTOCOL_OFFLOAD:
+ * Offload a protocol to NIC for lights-out state
+ *
+ * Locks required: None
+ * Returns: 0, ENOSYS
+ */
+#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD 0x47
+#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN 8
+#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN 0
+
+#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL_OFST 0
+#define MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID_OFST 4
+
+/* Lights-out offload protocols enumeration */
+#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP 0x1
+#define MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS 0x2
+
+
+/* MC_CMD_MAC_RESET_RESTORE:
+ * Restore MAC after block reset
+ *
+ * Locks required: None
+ * Returns: 0
+ */
+
+#define MC_CMD_MAC_RESET_RESTORE 0x48
+#define MC_CMD_MAC_RESET_RESTORE_IN_LEN 0
+#define MC_CMD_MAC_RESET_RESTORE_OUT_LEN 0
+
+#endif /* MCDI_PCOL_H */
diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c
new file mode 100644
index 00000000000..0e1bcc5a0d5
--- /dev/null
+++ b/drivers/net/sfc/mcdi_phy.c
@@ -0,0 +1,597 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+/*
+ * Driver for PHY related operations via MCDI.
+ */
+
+#include "efx.h"
+#include "phy.h"
+#include "mcdi.h"
+#include "mcdi_pcol.h"
+#include "mdio_10g.h"
+
+struct efx_mcdi_phy_cfg {
+ u32 flags;
+ u32 type;
+ u32 supported_cap;
+ u32 channel;
+ u32 port;
+ u32 stats_mask;
+ u8 name[20];
+ u32 media;
+ u32 mmd_mask;
+ u8 revision[20];
+ u32 forced_cap;
+};
+
+static int
+efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_cfg *cfg)
+{
+ u8 outbuf[MC_CMD_GET_PHY_CFG_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_IN_LEN != 0);
+ BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_OUT_NAME_LEN != sizeof(cfg->name));
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) {
+ rc = -EMSGSIZE;
+ goto fail;
+ }
+
+ cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS);
+ cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE);
+ cfg->supported_cap =
+ MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP);
+ cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL);
+ cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT);
+ cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK);
+ memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME),
+ sizeof(cfg->name));
+ cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE);
+ cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK);
+ memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION),
+ sizeof(cfg->revision));
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+static int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities,
+ u32 flags, u32 loopback_mode,
+ u32 loopback_speed)
+{
+ u8 inbuf[MC_CMD_SET_LINK_IN_LEN];
+ int rc;
+
+ BUILD_BUG_ON(MC_CMD_SET_LINK_OUT_LEN != 0);
+
+ MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities);
+ MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags);
+ MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode);
+ MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, sizeof(inbuf),
+ NULL, 0, NULL);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+static int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes)
+{
+ u8 outbuf[MC_CMD_GET_LOOPBACK_MODES_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_LOOPBACK_MODES, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ if (outlen < MC_CMD_GET_LOOPBACK_MODES_OUT_LEN) {
+ rc = -EMSGSIZE;
+ goto fail;
+ }
+
+ *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_SUGGESTED);
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus,
+ unsigned int prtad, unsigned int devad, u16 addr,
+ u16 *value_out, u32 *status_out)
+{
+ u8 inbuf[MC_CMD_MDIO_READ_IN_LEN];
+ u8 outbuf[MC_CMD_MDIO_READ_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, bus);
+ MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad);
+ MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad);
+ MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ *value_out = (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE);
+ *status_out = MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS);
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus,
+ unsigned int prtad, unsigned int devad, u16 addr,
+ u16 value, u32 *status_out)
+{
+ u8 inbuf[MC_CMD_MDIO_WRITE_IN_LEN];
+ u8 outbuf[MC_CMD_MDIO_WRITE_OUT_LEN];
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, bus);
+ MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad);
+ MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad);
+ MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr);
+ MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf),
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ goto fail;
+
+ *status_out = MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS);
+ return 0;
+
+fail:
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return rc;
+}
+
+static u32 mcdi_to_ethtool_cap(u32 media, u32 cap)
+{
+ u32 result = 0;
+
+ switch (media) {
+ case MC_CMD_MEDIA_KX4:
+ result |= SUPPORTED_Backplane;
+ if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
+ result |= SUPPORTED_1000baseKX_Full;
+ if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
+ result |= SUPPORTED_10000baseKX4_Full;
+ break;
+
+ case MC_CMD_MEDIA_XFP:
+ case MC_CMD_MEDIA_SFP_PLUS:
+ result |= SUPPORTED_FIBRE;
+ break;
+
+ case MC_CMD_MEDIA_BASE_T:
+ result |= SUPPORTED_TP;
+ if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN))
+ result |= SUPPORTED_10baseT_Half;
+ if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN))
+ result |= SUPPORTED_10baseT_Full;
+ if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN))
+ result |= SUPPORTED_100baseT_Half;
+ if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN))
+ result |= SUPPORTED_100baseT_Full;
+ if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN))
+ result |= SUPPORTED_1000baseT_Half;
+ if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
+ result |= SUPPORTED_1000baseT_Full;
+ if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
+ result |= SUPPORTED_10000baseT_Full;
+ break;
+ }
+
+ if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
+ result |= SUPPORTED_Pause;
+ if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
+ result |= SUPPORTED_Asym_Pause;
+ if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
+ result |= SUPPORTED_Autoneg;
+
+ return result;
+}
+
+static u32 ethtool_to_mcdi_cap(u32 cap)
+{
+ u32 result = 0;
+
+ if (cap & SUPPORTED_10baseT_Half)
+ result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN);
+ if (cap & SUPPORTED_10baseT_Full)
+ result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN);
+ if (cap & SUPPORTED_100baseT_Half)
+ result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN);
+ if (cap & SUPPORTED_100baseT_Full)
+ result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN);
+ if (cap & SUPPORTED_1000baseT_Half)
+ result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN);
+ if (cap & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseKX_Full))
+ result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN);
+ if (cap & (SUPPORTED_10000baseT_Full | SUPPORTED_10000baseKX4_Full))
+ result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN);
+ if (cap & SUPPORTED_Pause)
+ result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN);
+ if (cap & SUPPORTED_Asym_Pause)
+ result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN);
+ if (cap & SUPPORTED_Autoneg)
+ result |= (1 << MC_CMD_PHY_CAP_AN_LBN);
+
+ return result;
+}
+
+static u32 efx_get_mcdi_phy_flags(struct efx_nic *efx)
+{
+ struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data;
+ enum efx_phy_mode mode, supported;
+ u32 flags;
+
+ /* TODO: Advertise the capabilities supported by this PHY */
+ supported = 0;
+ if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_TXDIS_LBN))
+ supported |= PHY_MODE_TX_DISABLED;
+ if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_LOWPOWER_LBN))
+ supported |= PHY_MODE_LOW_POWER;
+ if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_POWEROFF_LBN))
+ supported |= PHY_MODE_OFF;
+
+ mode = efx->phy_mode & supported;
+
+ flags = 0;
+ if (mode & PHY_MODE_TX_DISABLED)
+ flags |= (1 << MC_CMD_SET_LINK_TXDIS_LBN);
+ if (mode & PHY_MODE_LOW_POWER)
+ flags |= (1 << MC_CMD_SET_LINK_LOWPOWER_LBN);
+ if (mode & PHY_MODE_OFF)
+ flags |= (1 << MC_CMD_SET_LINK_POWEROFF_LBN);
+
+ return flags;
+}
+
+static u32 mcdi_to_ethtool_media(u32 media)
+{
+ switch (media) {
+ case MC_CMD_MEDIA_XAUI:
+ case MC_CMD_MEDIA_CX4:
+ case MC_CMD_MEDIA_KX4:
+ return PORT_OTHER;
+
+ case MC_CMD_MEDIA_XFP:
+ case MC_CMD_MEDIA_SFP_PLUS:
+ return PORT_FIBRE;
+
+ case MC_CMD_MEDIA_BASE_T:
+ return PORT_TP;
+
+ default:
+ return PORT_OTHER;
+ }
+}
+
+static int efx_mcdi_phy_probe(struct efx_nic *efx)
+{
+ struct efx_mcdi_phy_cfg *phy_cfg;
+ int rc;
+
+ /* TODO: Move phy_data initialisation to
+ * phy_op->probe/remove, rather than init/fini */
+ phy_cfg = kzalloc(sizeof(*phy_cfg), GFP_KERNEL);
+ if (phy_cfg == NULL) {
+ rc = -ENOMEM;
+ goto fail_alloc;
+ }
+ rc = efx_mcdi_get_phy_cfg(efx, phy_cfg);
+ if (rc != 0)
+ goto fail;
+
+ efx->phy_type = phy_cfg->type;
+
+ efx->mdio_bus = phy_cfg->channel;
+ efx->mdio.prtad = phy_cfg->port;
+ efx->mdio.mmds = phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22);
+ efx->mdio.mode_support = 0;
+ if (phy_cfg->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22))
+ efx->mdio.mode_support |= MDIO_SUPPORTS_C22;
+ if (phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22))
+ efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
+
+ /* Assert that we can map efx -> mcdi loopback modes */
+ BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE);
+ BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA);
+ BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC);
+ BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII);
+ BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS);
+ BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI);
+ BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII);
+ BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII);
+ BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR);
+ BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI);
+ BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR);
+ BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR);
+ BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR);
+ BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR);
+ BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY);
+ BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS);
+ BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS);
+ BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD);
+ BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT);
+ BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS);
+ BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS);
+ BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR);
+ BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR);
+ BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS);
+ BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS);
+ BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR);
+ BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS);
+
+ rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes);
+ if (rc != 0)
+ goto fail;
+ /* The MC indicates that LOOPBACK_NONE is a valid loopback mode,
+ * but by convention we don't */
+ efx->loopback_modes &= ~(1 << LOOPBACK_NONE);
+
+ kfree(phy_cfg);
+
+ return 0;
+
+fail:
+ kfree(phy_cfg);
+fail_alloc:
+ return rc;
+}
+
+static int efx_mcdi_phy_init(struct efx_nic *efx)
+{
+ struct efx_mcdi_phy_cfg *phy_data;
+ u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
+ u32 caps;
+ int rc;
+
+ phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
+ if (phy_data == NULL)
+ return -ENOMEM;
+
+ rc = efx_mcdi_get_phy_cfg(efx, phy_data);
+ if (rc != 0)
+ goto fail;
+
+ efx->phy_data = phy_data;
+
+ BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
+ outbuf, sizeof(outbuf), NULL);
+ if (rc)
+ goto fail;
+
+ caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP);
+ if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN))
+ efx->link_advertising =
+ mcdi_to_ethtool_cap(phy_data->media, caps);
+ else
+ phy_data->forced_cap = caps;
+
+ return 0;
+
+fail:
+ kfree(phy_data);
+ return rc;
+}
+
+int efx_mcdi_phy_reconfigure(struct efx_nic *efx)
+{
+ struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data;
+ u32 caps = (efx->link_advertising ?
+ ethtool_to_mcdi_cap(efx->link_advertising) :
+ phy_cfg->forced_cap);
+
+ return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
+ efx->loopback_mode, 0);
+}
+
+void efx_mcdi_phy_decode_link(struct efx_nic *efx,
+ struct efx_link_state *link_state,
+ u32 speed, u32 flags, u32 fcntl)
+{
+ switch (fcntl) {
+ case MC_CMD_FCNTL_AUTO:
+ WARN_ON(1); /* This is not a link mode */
+ link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX;
+ break;
+ case MC_CMD_FCNTL_BIDIR:
+ link_state->fc = EFX_FC_TX | EFX_FC_RX;
+ break;
+ case MC_CMD_FCNTL_RESPOND:
+ link_state->fc = EFX_FC_RX;
+ break;
+ default:
+ WARN_ON(1);
+ case MC_CMD_FCNTL_OFF:
+ link_state->fc = 0;
+ break;
+ }
+
+ link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_LINK_UP_LBN));
+ link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_FULL_DUPLEX_LBN));
+ link_state->speed = speed;
+}
+
+/* Verify that the forced flow control settings (!EFX_FC_AUTO) are
+ * supported by the link partner. Warn the user if this isn't the case
+ */
+void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa)
+{
+ struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data;
+ u32 rmtadv;
+
+ /* The link partner capabilities are only relevent if the
+ * link supports flow control autonegotiation */
+ if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
+ return;
+
+ /* If flow control autoneg is supported and enabled, then fine */
+ if (efx->wanted_fc & EFX_FC_AUTO)
+ return;
+
+ rmtadv = 0;
+ if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
+ rmtadv |= ADVERTISED_Pause;
+ if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
+ rmtadv |= ADVERTISED_Asym_Pause;
+
+ if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause)
+ EFX_ERR(efx, "warning: link partner doesn't support "
+ "pause frames");
+}
+
+static bool efx_mcdi_phy_poll(struct efx_nic *efx)
+{
+ struct efx_link_state old_state = efx->link_state;
+ u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
+ int rc;
+
+ WARN_ON(!mutex_is_locked(&efx->mac_lock));
+
+ BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
+
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
+ outbuf, sizeof(outbuf), NULL);
+ if (rc) {
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ efx->link_state.up = false;
+ } else {
+ efx_mcdi_phy_decode_link(
+ efx, &efx->link_state,
+ MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED),
+ MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
+ MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
+ }
+
+ return !efx_link_state_equal(&efx->link_state, &old_state);
+}
+
+static void efx_mcdi_phy_fini(struct efx_nic *efx)
+{
+ struct efx_mcdi_phy_data *phy_data = efx->phy_data;
+
+ efx->phy_data = NULL;
+ kfree(phy_data);
+}
+
+static void efx_mcdi_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+ struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data;
+ u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
+ int rc;
+
+ ecmd->supported =
+ mcdi_to_ethtool_cap(phy_cfg->media, phy_cfg->supported_cap);
+ ecmd->advertising = efx->link_advertising;
+ ecmd->speed = efx->link_state.speed;
+ ecmd->duplex = efx->link_state.fd;
+ ecmd->port = mcdi_to_ethtool_media(phy_cfg->media);
+ ecmd->phy_address = phy_cfg->port;
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->autoneg = !!(efx->link_advertising & ADVERTISED_Autoneg);
+ ecmd->mdio_support = (efx->mdio.mode_support &
+ (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22));
+
+ BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
+ rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
+ outbuf, sizeof(outbuf), NULL);
+ if (rc) {
+ EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc);
+ return;
+ }
+ ecmd->lp_advertising =
+ mcdi_to_ethtool_cap(phy_cfg->media,
+ MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP));
+}
+
+static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+{
+ struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data;
+ u32 caps;
+ int rc;
+
+ if (ecmd->autoneg) {
+ caps = (ethtool_to_mcdi_cap(ecmd->advertising) |
+ 1 << MC_CMD_PHY_CAP_AN_LBN);
+ } else if (ecmd->duplex) {
+ switch (ecmd->speed) {
+ case 10: caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN; break;
+ case 100: caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN; break;
+ case 1000: caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN; break;
+ case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break;
+ default: return -EINVAL;
+ }
+ } else {
+ switch (ecmd->speed) {
+ case 10: caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN; break;
+ case 100: caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN; break;
+ case 1000: caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN; break;
+ default: return -EINVAL;
+ }
+ }
+
+ rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
+ efx->loopback_mode, 0);
+ if (rc)
+ return rc;
+
+ if (ecmd->autoneg) {
+ efx_link_set_advertising(
+ efx, ecmd->advertising | ADVERTISED_Autoneg);
+ phy_cfg->forced_cap = 0;
+ } else {
+ efx_link_set_advertising(efx, 0);
+ phy_cfg->forced_cap = caps;
+ }
+ return 0;
+}
+
+struct efx_phy_operations efx_mcdi_phy_ops = {
+ .probe = efx_mcdi_phy_probe,
+ .init = efx_mcdi_phy_init,
+ .reconfigure = efx_mcdi_phy_reconfigure,
+ .poll = efx_mcdi_phy_poll,
+ .fini = efx_mcdi_phy_fini,
+ .get_settings = efx_mcdi_phy_get_settings,
+ .set_settings = efx_mcdi_phy_set_settings,
+ .run_tests = NULL,
+ .test_name = NULL,
+};
diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c
index 6c33459f9ea..1574e52f059 100644
--- a/drivers/net/sfc/mdio_10g.c
+++ b/drivers/net/sfc/mdio_10g.c
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -14,8 +14,8 @@
#include <linux/delay.h>
#include "net_driver.h"
#include "mdio_10g.h"
-#include "boards.h"
#include "workarounds.h"
+#include "nic.h"
unsigned efx_mdio_id_oui(u32 id)
{
@@ -174,7 +174,7 @@ bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask)
* of mmd's */
if (LOOPBACK_INTERNAL(efx))
return true;
- else if (efx->loopback_mode == LOOPBACK_NETWORK)
+ else if (LOOPBACK_MASK(efx) & LOOPBACKS_WS)
return false;
else if (efx_phy_mode_disabled(efx->phy_mode))
return false;
@@ -211,7 +211,7 @@ void efx_mdio_phy_reconfigure(struct efx_nic *efx)
efx->loopback_mode == LOOPBACK_PCS);
efx_mdio_set_flag(efx, MDIO_MMD_PHYXS,
MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK,
- efx->loopback_mode == LOOPBACK_NETWORK);
+ efx->loopback_mode == LOOPBACK_PHYXS_WS);
}
static void efx_mdio_set_mmd_lpower(struct efx_nic *efx,
@@ -249,8 +249,6 @@ void efx_mdio_set_mmds_lpower(struct efx_nic *efx,
int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
struct ethtool_cmd prev;
- u32 required;
- int reg;
efx->phy_op->get_settings(efx, &prev);
@@ -266,86 +264,74 @@ int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
return -EINVAL;
/* Check that PHY supports these settings */
- if (ecmd->autoneg) {
- required = SUPPORTED_Autoneg;
- } else if (ecmd->duplex) {
- switch (ecmd->speed) {
- case SPEED_10: required = SUPPORTED_10baseT_Full; break;
- case SPEED_100: required = SUPPORTED_100baseT_Full; break;
- default: return -EINVAL;
- }
- } else {
- switch (ecmd->speed) {
- case SPEED_10: required = SUPPORTED_10baseT_Half; break;
- case SPEED_100: required = SUPPORTED_100baseT_Half; break;
- default: return -EINVAL;
- }
- }
- required |= ecmd->advertising;
- if (required & ~prev.supported)
+ if (!ecmd->autoneg ||
+ (ecmd->advertising | SUPPORTED_Autoneg) & ~prev.supported)
return -EINVAL;
- if (ecmd->autoneg) {
- bool xnp = (ecmd->advertising & ADVERTISED_10000baseT_Full
- || EFX_WORKAROUND_13204(efx));
-
- /* Set up the base page */
- reg = ADVERTISE_CSMA;
- if (ecmd->advertising & ADVERTISED_10baseT_Half)
- reg |= ADVERTISE_10HALF;
- if (ecmd->advertising & ADVERTISED_10baseT_Full)
- reg |= ADVERTISE_10FULL;
- if (ecmd->advertising & ADVERTISED_100baseT_Half)
- reg |= ADVERTISE_100HALF;
- if (ecmd->advertising & ADVERTISED_100baseT_Full)
- reg |= ADVERTISE_100FULL;
- if (xnp)
- reg |= ADVERTISE_RESV;
- else if (ecmd->advertising & (ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full))
- reg |= ADVERTISE_NPAGE;
- reg |= mii_advertise_flowctrl(efx->wanted_fc);
- efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg);
-
- /* Set up the (extended) next page if necessary */
- if (efx->phy_op->set_npage_adv)
- efx->phy_op->set_npage_adv(efx, ecmd->advertising);
-
- /* Enable and restart AN */
- reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1);
- reg |= MDIO_AN_CTRL1_ENABLE;
- if (!(EFX_WORKAROUND_15195(efx) &&
- LOOPBACK_MASK(efx) & efx->phy_op->loopbacks))
- reg |= MDIO_AN_CTRL1_RESTART;
- if (xnp)
- reg |= MDIO_AN_CTRL1_XNP;
- else
- reg &= ~MDIO_AN_CTRL1_XNP;
- efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg);
- } else {
- /* Disable AN */
- efx_mdio_set_flag(efx, MDIO_MMD_AN, MDIO_CTRL1,
- MDIO_AN_CTRL1_ENABLE, false);
-
- /* Set the basic control bits */
- reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1);
- reg &= ~(MDIO_CTRL1_SPEEDSEL | MDIO_CTRL1_FULLDPLX);
- if (ecmd->speed == SPEED_100)
- reg |= MDIO_PMA_CTRL1_SPEED100;
- if (ecmd->duplex)
- reg |= MDIO_CTRL1_FULLDPLX;
- efx_mdio_write(efx, MDIO_MMD_PMAPMD, MDIO_CTRL1, reg);
- }
-
+ efx_link_set_advertising(efx, ecmd->advertising | ADVERTISED_Autoneg);
+ efx_mdio_an_reconfigure(efx);
return 0;
}
+/**
+ * efx_mdio_an_reconfigure - Push advertising flags and restart autonegotiation
+ * @efx: Efx NIC
+ */
+void efx_mdio_an_reconfigure(struct efx_nic *efx)
+{
+ bool xnp = (efx->link_advertising & ADVERTISED_10000baseT_Full
+ || EFX_WORKAROUND_13204(efx));
+ int reg;
+
+ WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN));
+
+ /* Set up the base page */
+ reg = ADVERTISE_CSMA;
+ if (efx->link_advertising & ADVERTISED_10baseT_Half)
+ reg |= ADVERTISE_10HALF;
+ if (efx->link_advertising & ADVERTISED_10baseT_Full)
+ reg |= ADVERTISE_10FULL;
+ if (efx->link_advertising & ADVERTISED_100baseT_Half)
+ reg |= ADVERTISE_100HALF;
+ if (efx->link_advertising & ADVERTISED_100baseT_Full)
+ reg |= ADVERTISE_100FULL;
+ if (xnp)
+ reg |= ADVERTISE_RESV;
+ else if (efx->link_advertising & (ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full))
+ reg |= ADVERTISE_NPAGE;
+ if (efx->link_advertising & ADVERTISED_Pause)
+ reg |= ADVERTISE_PAUSE_CAP;
+ if (efx->link_advertising & ADVERTISED_Asym_Pause)
+ reg |= ADVERTISE_PAUSE_ASYM;
+ efx_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, reg);
+
+ /* Set up the (extended) next page if necessary */
+ if (efx->phy_op->set_npage_adv)
+ efx->phy_op->set_npage_adv(efx, efx->link_advertising);
+
+ /* Enable and restart AN */
+ reg = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1);
+ reg |= MDIO_AN_CTRL1_ENABLE;
+ if (!(EFX_WORKAROUND_15195(efx) && LOOPBACK_EXTERNAL(efx)))
+ reg |= MDIO_AN_CTRL1_RESTART;
+ if (xnp)
+ reg |= MDIO_AN_CTRL1_XNP;
+ else
+ reg &= ~MDIO_AN_CTRL1_XNP;
+ efx_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, reg);
+}
+
enum efx_fc_type efx_mdio_get_pause(struct efx_nic *efx)
{
- int lpa;
+ BUILD_BUG_ON(EFX_FC_AUTO & (EFX_FC_RX | EFX_FC_TX));
- if (!(efx->phy_op->mmds & MDIO_DEVS_AN))
+ if (!(efx->wanted_fc & EFX_FC_AUTO))
return efx->wanted_fc;
- lpa = efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA);
- return efx_fc_resolve(efx->wanted_fc, lpa);
+
+ WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN));
+
+ return mii_resolve_flowctrl_fdx(
+ mii_advertise_flowctrl(efx->wanted_fc),
+ efx_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA));
}
diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h
index 6b14421a744..f6ac9503339 100644
--- a/drivers/net/sfc/mdio_10g.h
+++ b/drivers/net/sfc/mdio_10g.h
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -17,7 +17,6 @@
*/
#include "efx.h"
-#include "boards.h"
static inline unsigned efx_mdio_id_rev(u32 id) { return id & 0xf; }
static inline unsigned efx_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; }
@@ -87,6 +86,9 @@ extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx,
/* Set (some of) the PHY settings over MDIO */
extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd);
+/* Push advertising flags and restart autonegotiation */
+extern void efx_mdio_an_reconfigure(struct efx_nic *efx);
+
/* Get pause parameters from AN if available (otherwise return
* requested pause parameters)
*/
diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c
index 820c233c3ea..3a464529a46 100644
--- a/drivers/net/sfc/mtd.c
+++ b/drivers/net/sfc/mtd.c
@@ -1,36 +1,80 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
+#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/delay.h>
+#include <linux/rtnetlink.h>
#define EFX_DRIVER_NAME "sfc_mtd"
#include "net_driver.h"
#include "spi.h"
#include "efx.h"
+#include "nic.h"
+#include "mcdi.h"
+#include "mcdi_pcol.h"
#define EFX_SPI_VERIFY_BUF_LEN 16
+#define EFX_MCDI_CHUNK_LEN 128
-struct efx_mtd {
- const struct efx_spi_device *spi;
+struct efx_mtd_partition {
struct mtd_info mtd;
+ union {
+ struct {
+ bool updating;
+ u8 nvram_type;
+ u16 fw_subtype;
+ } mcdi;
+ size_t offset;
+ };
+ const char *type_name;
char name[IFNAMSIZ + 20];
};
+struct efx_mtd_ops {
+ int (*read)(struct mtd_info *mtd, loff_t start, size_t len,
+ size_t *retlen, u8 *buffer);
+ int (*erase)(struct mtd_info *mtd, loff_t start, size_t len);
+ int (*write)(struct mtd_info *mtd, loff_t start, size_t len,
+ size_t *retlen, const u8 *buffer);
+ int (*sync)(struct mtd_info *mtd);
+};
+
+struct efx_mtd {
+ struct list_head node;
+ struct efx_nic *efx;
+ const struct efx_spi_device *spi;
+ const char *name;
+ const struct efx_mtd_ops *ops;
+ size_t n_parts;
+ struct efx_mtd_partition part[0];
+};
+
+#define efx_for_each_partition(part, efx_mtd) \
+ for ((part) = &(efx_mtd)->part[0]; \
+ (part) != &(efx_mtd)->part[(efx_mtd)->n_parts]; \
+ (part)++)
+
+#define to_efx_mtd_partition(mtd) \
+ container_of(mtd, struct efx_mtd_partition, mtd)
+
+static int falcon_mtd_probe(struct efx_nic *efx);
+static int siena_mtd_probe(struct efx_nic *efx);
+
/* SPI utilities */
static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible)
{
const struct efx_spi_device *spi = efx_mtd->spi;
- struct efx_nic *efx = spi->efx;
+ struct efx_nic *efx = efx_mtd->efx;
u8 status;
int rc, i;
@@ -39,7 +83,7 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible)
__set_current_state(uninterruptible ?
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 10);
- rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
+ rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
&status, sizeof(status));
if (rc)
return rc;
@@ -52,32 +96,35 @@ static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible)
return -ETIMEDOUT;
}
-static int efx_spi_unlock(const struct efx_spi_device *spi)
+static int
+efx_spi_unlock(struct efx_nic *efx, const struct efx_spi_device *spi)
{
const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 |
SPI_STATUS_BP0);
u8 status;
int rc;
- rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status));
+ rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL,
+ &status, sizeof(status));
if (rc)
return rc;
if (!(status & unlock_mask))
return 0; /* already unlocked */
- rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
+ rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0);
if (rc)
return rc;
- rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
+ rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
if (rc)
return rc;
status &= ~unlock_mask;
- rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status));
+ rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status,
+ NULL, sizeof(status));
if (rc)
return rc;
- rc = falcon_spi_wait_write(spi);
+ rc = falcon_spi_wait_write(efx, spi);
if (rc)
return rc;
@@ -87,6 +134,7 @@ static int efx_spi_unlock(const struct efx_spi_device *spi)
static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
{
const struct efx_spi_device *spi = efx_mtd->spi;
+ struct efx_nic *efx = efx_mtd->efx;
unsigned pos, block_len;
u8 empty[EFX_SPI_VERIFY_BUF_LEN];
u8 buffer[EFX_SPI_VERIFY_BUF_LEN];
@@ -98,13 +146,14 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
if (spi->erase_command == 0)
return -EOPNOTSUPP;
- rc = efx_spi_unlock(spi);
+ rc = efx_spi_unlock(efx, spi);
if (rc)
return rc;
- rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
+ rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0);
if (rc)
return rc;
- rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0);
+ rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL,
+ NULL, 0);
if (rc)
return rc;
rc = efx_spi_slow_wait(efx_mtd, false);
@@ -113,7 +162,8 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
memset(empty, 0xff, sizeof(empty));
for (pos = 0; pos < len; pos += block_len) {
block_len = min(len - pos, sizeof(buffer));
- rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer);
+ rc = falcon_spi_read(efx, spi, start + pos, block_len,
+ NULL, buffer);
if (rc)
return rc;
if (memcmp(empty, buffer, block_len))
@@ -130,140 +180,473 @@ static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
/* MTD interface */
-static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
- size_t *retlen, u8 *buffer)
+static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
{
struct efx_mtd *efx_mtd = mtd->priv;
+ int rc;
+
+ rc = efx_mtd->ops->erase(mtd, erase->addr, erase->len);
+ if (rc == 0) {
+ erase->state = MTD_ERASE_DONE;
+ } else {
+ erase->state = MTD_ERASE_FAILED;
+ erase->fail_addr = 0xffffffff;
+ }
+ mtd_erase_callback(erase);
+ return rc;
+}
+
+static void efx_mtd_sync(struct mtd_info *mtd)
+{
+ struct efx_mtd *efx_mtd = mtd->priv;
+ struct efx_nic *efx = efx_mtd->efx;
+ int rc;
+
+ rc = efx_mtd->ops->sync(mtd);
+ if (rc)
+ EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc);
+}
+
+static void efx_mtd_remove_partition(struct efx_mtd_partition *part)
+{
+ int rc;
+
+ for (;;) {
+ rc = del_mtd_device(&part->mtd);
+ if (rc != -EBUSY)
+ break;
+ ssleep(1);
+ }
+ WARN_ON(rc);
+}
+
+static void efx_mtd_remove_device(struct efx_mtd *efx_mtd)
+{
+ struct efx_mtd_partition *part;
+
+ efx_for_each_partition(part, efx_mtd)
+ efx_mtd_remove_partition(part);
+ list_del(&efx_mtd->node);
+ kfree(efx_mtd);
+}
+
+static void efx_mtd_rename_device(struct efx_mtd *efx_mtd)
+{
+ struct efx_mtd_partition *part;
+
+ efx_for_each_partition(part, efx_mtd)
+ if (efx_nic_rev(efx_mtd->efx) >= EFX_REV_SIENA_A0)
+ snprintf(part->name, sizeof(part->name),
+ "%s %s:%02x", efx_mtd->efx->name,
+ part->type_name, part->mcdi.fw_subtype);
+ else
+ snprintf(part->name, sizeof(part->name),
+ "%s %s", efx_mtd->efx->name,
+ part->type_name);
+}
+
+static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd)
+{
+ struct efx_mtd_partition *part;
+
+ efx_mtd->efx = efx;
+
+ efx_mtd_rename_device(efx_mtd);
+
+ efx_for_each_partition(part, efx_mtd) {
+ part->mtd.writesize = 1;
+
+ part->mtd.owner = THIS_MODULE;
+ part->mtd.priv = efx_mtd;
+ part->mtd.name = part->name;
+ part->mtd.erase = efx_mtd_erase;
+ part->mtd.read = efx_mtd->ops->read;
+ part->mtd.write = efx_mtd->ops->write;
+ part->mtd.sync = efx_mtd_sync;
+
+ if (add_mtd_device(&part->mtd))
+ goto fail;
+ }
+
+ list_add(&efx_mtd->node, &efx->mtd_list);
+ return 0;
+
+fail:
+ while (part != &efx_mtd->part[0]) {
+ --part;
+ efx_mtd_remove_partition(part);
+ }
+ /* add_mtd_device() returns 1 if the MTD table is full */
+ return -ENOMEM;
+}
+
+void efx_mtd_remove(struct efx_nic *efx)
+{
+ struct efx_mtd *efx_mtd, *next;
+
+ WARN_ON(efx_dev_registered(efx));
+
+ list_for_each_entry_safe(efx_mtd, next, &efx->mtd_list, node)
+ efx_mtd_remove_device(efx_mtd);
+}
+
+void efx_mtd_rename(struct efx_nic *efx)
+{
+ struct efx_mtd *efx_mtd;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(efx_mtd, &efx->mtd_list, node)
+ efx_mtd_rename_device(efx_mtd);
+}
+
+int efx_mtd_probe(struct efx_nic *efx)
+{
+ if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
+ return siena_mtd_probe(efx);
+ else
+ return falcon_mtd_probe(efx);
+}
+
+/* Implementation of MTD operations for Falcon */
+
+static int falcon_mtd_read(struct mtd_info *mtd, loff_t start,
+ size_t len, size_t *retlen, u8 *buffer)
+{
+ struct efx_mtd_partition *part = to_efx_mtd_partition(mtd);
+ struct efx_mtd *efx_mtd = mtd->priv;
const struct efx_spi_device *spi = efx_mtd->spi;
- struct efx_nic *efx = spi->efx;
+ struct efx_nic *efx = efx_mtd->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
- rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start,
- len, retlen, buffer);
+ rc = falcon_spi_read(efx, spi, part->offset + start, len,
+ retlen, buffer);
mutex_unlock(&efx->spi_lock);
return rc;
}
-static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
+static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
{
+ struct efx_mtd_partition *part = to_efx_mtd_partition(mtd);
struct efx_mtd *efx_mtd = mtd->priv;
- struct efx_nic *efx = efx_mtd->spi->efx;
+ struct efx_nic *efx = efx_mtd->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
- rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr,
- erase->len);
+ rc = efx_spi_erase(efx_mtd, part->offset + start, len);
mutex_unlock(&efx->spi_lock);
-
- if (rc == 0) {
- erase->state = MTD_ERASE_DONE;
- } else {
- erase->state = MTD_ERASE_FAILED;
- erase->fail_addr = 0xffffffff;
- }
- mtd_erase_callback(erase);
return rc;
}
-static int efx_mtd_write(struct mtd_info *mtd, loff_t start,
- size_t len, size_t *retlen, const u8 *buffer)
+static int falcon_mtd_write(struct mtd_info *mtd, loff_t start,
+ size_t len, size_t *retlen, const u8 *buffer)
{
+ struct efx_mtd_partition *part = to_efx_mtd_partition(mtd);
struct efx_mtd *efx_mtd = mtd->priv;
const struct efx_spi_device *spi = efx_mtd->spi;
- struct efx_nic *efx = spi->efx;
+ struct efx_nic *efx = efx_mtd->efx;
int rc;
rc = mutex_lock_interruptible(&efx->spi_lock);
if (rc)
return rc;
- rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start,
- len, retlen, buffer);
+ rc = falcon_spi_write(efx, spi, part->offset + start, len,
+ retlen, buffer);
mutex_unlock(&efx->spi_lock);
return rc;
}
-static void efx_mtd_sync(struct mtd_info *mtd)
+static int falcon_mtd_sync(struct mtd_info *mtd)
{
struct efx_mtd *efx_mtd = mtd->priv;
- struct efx_nic *efx = efx_mtd->spi->efx;
+ struct efx_nic *efx = efx_mtd->efx;
int rc;
mutex_lock(&efx->spi_lock);
rc = efx_spi_slow_wait(efx_mtd, true);
mutex_unlock(&efx->spi_lock);
+ return rc;
+}
+
+static struct efx_mtd_ops falcon_mtd_ops = {
+ .read = falcon_mtd_read,
+ .erase = falcon_mtd_erase,
+ .write = falcon_mtd_write,
+ .sync = falcon_mtd_sync,
+};
+
+static int falcon_mtd_probe(struct efx_nic *efx)
+{
+ struct efx_spi_device *spi = efx->spi_flash;
+ struct efx_mtd *efx_mtd;
+ int rc;
+
+ ASSERT_RTNL();
+ if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START)
+ return -ENODEV;
+
+ efx_mtd = kzalloc(sizeof(*efx_mtd) + sizeof(efx_mtd->part[0]),
+ GFP_KERNEL);
+ if (!efx_mtd)
+ return -ENOMEM;
+
+ efx_mtd->spi = spi;
+ efx_mtd->name = "flash";
+ efx_mtd->ops = &falcon_mtd_ops;
+
+ efx_mtd->n_parts = 1;
+ efx_mtd->part[0].mtd.type = MTD_NORFLASH;
+ efx_mtd->part[0].mtd.flags = MTD_CAP_NORFLASH;
+ efx_mtd->part[0].mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
+ efx_mtd->part[0].mtd.erasesize = spi->erase_size;
+ efx_mtd->part[0].offset = FALCON_FLASH_BOOTCODE_START;
+ efx_mtd->part[0].type_name = "sfc_flash_bootrom";
+
+ rc = efx_mtd_probe_device(efx, efx_mtd);
if (rc)
- EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc);
- return;
+ kfree(efx_mtd);
+ return rc;
}
-void efx_mtd_remove(struct efx_nic *efx)
+/* Implementation of MTD operations for Siena */
+
+static int siena_mtd_read(struct mtd_info *mtd, loff_t start,
+ size_t len, size_t *retlen, u8 *buffer)
{
- if (efx->spi_flash && efx->spi_flash->mtd) {
- struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
- int rc;
-
- for (;;) {
- rc = del_mtd_device(&efx_mtd->mtd);
- if (rc != -EBUSY)
- break;
- ssleep(1);
- }
- WARN_ON(rc);
- kfree(efx_mtd);
+ struct efx_mtd_partition *part = to_efx_mtd_partition(mtd);
+ struct efx_mtd *efx_mtd = mtd->priv;
+ struct efx_nic *efx = efx_mtd->efx;
+ loff_t offset = start;
+ loff_t end = min_t(loff_t, start + len, mtd->size);
+ size_t chunk;
+ int rc = 0;
+
+ while (offset < end) {
+ chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN);
+ rc = efx_mcdi_nvram_read(efx, part->mcdi.nvram_type, offset,
+ buffer, chunk);
+ if (rc)
+ goto out;
+ offset += chunk;
+ buffer += chunk;
}
+out:
+ *retlen = offset - start;
+ return rc;
}
-void efx_mtd_rename(struct efx_nic *efx)
+static int siena_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
{
- if (efx->spi_flash && efx->spi_flash->mtd) {
- struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
- snprintf(efx_mtd->name, sizeof(efx_mtd->name),
- "%s sfc_flash_bootrom", efx->name);
+ struct efx_mtd_partition *part = to_efx_mtd_partition(mtd);
+ struct efx_mtd *efx_mtd = mtd->priv;
+ struct efx_nic *efx = efx_mtd->efx;
+ loff_t offset = start & ~((loff_t)(mtd->erasesize - 1));
+ loff_t end = min_t(loff_t, start + len, mtd->size);
+ size_t chunk = part->mtd.erasesize;
+ int rc = 0;
+
+ if (!part->mcdi.updating) {
+ rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type);
+ if (rc)
+ goto out;
+ part->mcdi.updating = 1;
+ }
+
+ /* The MCDI interface can in fact do multiple erase blocks at once;
+ * but erasing may be slow, so we make multiple calls here to avoid
+ * tripping the MCDI RPC timeout. */
+ while (offset < end) {
+ rc = efx_mcdi_nvram_erase(efx, part->mcdi.nvram_type, offset,
+ chunk);
+ if (rc)
+ goto out;
+ offset += chunk;
}
+out:
+ return rc;
}
-int efx_mtd_probe(struct efx_nic *efx)
+static int siena_mtd_write(struct mtd_info *mtd, loff_t start,
+ size_t len, size_t *retlen, const u8 *buffer)
{
- struct efx_spi_device *spi = efx->spi_flash;
- struct efx_mtd *efx_mtd;
+ struct efx_mtd_partition *part = to_efx_mtd_partition(mtd);
+ struct efx_mtd *efx_mtd = mtd->priv;
+ struct efx_nic *efx = efx_mtd->efx;
+ loff_t offset = start;
+ loff_t end = min_t(loff_t, start + len, mtd->size);
+ size_t chunk;
+ int rc = 0;
+
+ if (!part->mcdi.updating) {
+ rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type);
+ if (rc)
+ goto out;
+ part->mcdi.updating = 1;
+ }
- if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START)
+ while (offset < end) {
+ chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN);
+ rc = efx_mcdi_nvram_write(efx, part->mcdi.nvram_type, offset,
+ buffer, chunk);
+ if (rc)
+ goto out;
+ offset += chunk;
+ buffer += chunk;
+ }
+out:
+ *retlen = offset - start;
+ return rc;
+}
+
+static int siena_mtd_sync(struct mtd_info *mtd)
+{
+ struct efx_mtd_partition *part = to_efx_mtd_partition(mtd);
+ struct efx_mtd *efx_mtd = mtd->priv;
+ struct efx_nic *efx = efx_mtd->efx;
+ int rc = 0;
+
+ if (part->mcdi.updating) {
+ part->mcdi.updating = 0;
+ rc = efx_mcdi_nvram_update_finish(efx, part->mcdi.nvram_type);
+ }
+
+ return rc;
+}
+
+static struct efx_mtd_ops siena_mtd_ops = {
+ .read = siena_mtd_read,
+ .erase = siena_mtd_erase,
+ .write = siena_mtd_write,
+ .sync = siena_mtd_sync,
+};
+
+struct siena_nvram_type_info {
+ int port;
+ const char *name;
+};
+
+static struct siena_nvram_type_info siena_nvram_types[] = {
+ [MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO] = { 0, "sfc_dummy_phy" },
+ [MC_CMD_NVRAM_TYPE_MC_FW] = { 0, "sfc_mcfw" },
+ [MC_CMD_NVRAM_TYPE_MC_FW_BACKUP] = { 0, "sfc_mcfw_backup" },
+ [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0] = { 0, "sfc_static_cfg" },
+ [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1] = { 1, "sfc_static_cfg" },
+ [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0] = { 0, "sfc_dynamic_cfg" },
+ [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1] = { 1, "sfc_dynamic_cfg" },
+ [MC_CMD_NVRAM_TYPE_EXP_ROM] = { 0, "sfc_exp_rom" },
+ [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0] = { 0, "sfc_exp_rom_cfg" },
+ [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1] = { 1, "sfc_exp_rom_cfg" },
+ [MC_CMD_NVRAM_TYPE_PHY_PORT0] = { 0, "sfc_phy_fw" },
+ [MC_CMD_NVRAM_TYPE_PHY_PORT1] = { 1, "sfc_phy_fw" },
+};
+
+static int siena_mtd_probe_partition(struct efx_nic *efx,
+ struct efx_mtd *efx_mtd,
+ unsigned int part_id,
+ unsigned int type)
+{
+ struct efx_mtd_partition *part = &efx_mtd->part[part_id];
+ struct siena_nvram_type_info *info;
+ size_t size, erase_size;
+ bool protected;
+ int rc;
+
+ if (type >= ARRAY_SIZE(siena_nvram_types))
return -ENODEV;
- efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL);
+ info = &siena_nvram_types[type];
+
+ if (info->port != efx_port_num(efx))
+ return -ENODEV;
+
+ rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected);
+ if (rc)
+ return rc;
+ if (protected)
+ return -ENODEV; /* hide it */
+
+ part->mcdi.nvram_type = type;
+ part->type_name = info->name;
+
+ part->mtd.type = MTD_NORFLASH;
+ part->mtd.flags = MTD_CAP_NORFLASH;
+ part->mtd.size = size;
+ part->mtd.erasesize = erase_size;
+
+ return 0;
+}
+
+static int siena_mtd_get_fw_subtypes(struct efx_nic *efx,
+ struct efx_mtd *efx_mtd)
+{
+ struct efx_mtd_partition *part;
+ uint16_t fw_subtype_list[MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN /
+ sizeof(uint16_t)];
+ int rc;
+
+ rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list);
+ if (rc)
+ return rc;
+
+ efx_for_each_partition(part, efx_mtd)
+ part->mcdi.fw_subtype = fw_subtype_list[part->mcdi.nvram_type];
+
+ return 0;
+}
+
+static int siena_mtd_probe(struct efx_nic *efx)
+{
+ struct efx_mtd *efx_mtd;
+ int rc = -ENODEV;
+ u32 nvram_types;
+ unsigned int type;
+
+ ASSERT_RTNL();
+
+ rc = efx_mcdi_nvram_types(efx, &nvram_types);
+ if (rc)
+ return rc;
+
+ efx_mtd = kzalloc(sizeof(*efx_mtd) +
+ hweight32(nvram_types) * sizeof(efx_mtd->part[0]),
+ GFP_KERNEL);
if (!efx_mtd)
return -ENOMEM;
- efx_mtd->spi = spi;
- spi->mtd = efx_mtd;
-
- efx_mtd->mtd.type = MTD_NORFLASH;
- efx_mtd->mtd.flags = MTD_CAP_NORFLASH;
- efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
- efx_mtd->mtd.erasesize = spi->erase_size;
- efx_mtd->mtd.writesize = 1;
- efx_mtd_rename(efx);
-
- efx_mtd->mtd.owner = THIS_MODULE;
- efx_mtd->mtd.priv = efx_mtd;
- efx_mtd->mtd.name = efx_mtd->name;
- efx_mtd->mtd.erase = efx_mtd_erase;
- efx_mtd->mtd.read = efx_mtd_read;
- efx_mtd->mtd.write = efx_mtd_write;
- efx_mtd->mtd.sync = efx_mtd_sync;
-
- if (add_mtd_device(&efx_mtd->mtd)) {
- kfree(efx_mtd);
- spi->mtd = NULL;
- /* add_mtd_device() returns 1 if the MTD table is full */
- return -ENOMEM;
+ efx_mtd->name = "Siena NVRAM manager";
+
+ efx_mtd->ops = &siena_mtd_ops;
+
+ type = 0;
+ efx_mtd->n_parts = 0;
+
+ while (nvram_types != 0) {
+ if (nvram_types & 1) {
+ rc = siena_mtd_probe_partition(efx, efx_mtd,
+ efx_mtd->n_parts, type);
+ if (rc == 0)
+ efx_mtd->n_parts++;
+ else if (rc != -ENODEV)
+ goto fail;
+ }
+ type++;
+ nvram_types >>= 1;
}
- return 0;
+ rc = siena_mtd_get_fw_subtypes(efx, efx_mtd);
+ if (rc)
+ goto fail;
+
+ rc = efx_mtd_probe_device(efx, efx_mtd);
+fail:
+ if (rc)
+ kfree(efx_mtd);
+ return rc;
}
+
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index 298566da638..34c381f009b 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2005-2008 Solarflare Communications Inc.
+ * Copyright 2005-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -38,7 +38,7 @@
#ifndef EFX_DRIVER_NAME
#define EFX_DRIVER_NAME "sfc"
#endif
-#define EFX_DRIVER_VERSION "2.3"
+#define EFX_DRIVER_VERSION "3.0"
#ifdef EFX_ENABLE_DEBUG
#define EFX_BUG_ON_PARANOID(x) BUG_ON(x)
@@ -113,6 +113,13 @@ struct efx_special_buffer {
int entries;
};
+enum efx_flush_state {
+ FLUSH_NONE,
+ FLUSH_PENDING,
+ FLUSH_FAILED,
+ FLUSH_DONE,
+};
+
/**
* struct efx_tx_buffer - An Efx TX buffer
* @skb: The associated socket buffer.
@@ -189,7 +196,7 @@ struct efx_tx_queue {
struct efx_nic *nic;
struct efx_tx_buffer *buffer;
struct efx_special_buffer txd;
- bool flushed;
+ enum efx_flush_state flushed;
/* Members used mainly on the completion path */
unsigned int read_count ____cacheline_aligned_in_smp;
@@ -284,7 +291,7 @@ struct efx_rx_queue {
struct page *buf_page;
dma_addr_t buf_dma_addr;
char *buf_data;
- bool flushed;
+ enum efx_flush_state flushed;
};
/**
@@ -327,7 +334,7 @@ enum efx_rx_alloc_method {
* @used_flags: Channel is used by net driver
* @enabled: Channel enabled indicator
* @irq: IRQ number (MSI and MSI-X only)
- * @irq_moderation: IRQ moderation value (in us)
+ * @irq_moderation: IRQ moderation value (in hardware ticks)
* @napi_dev: Net device used with NAPI
* @napi_str: NAPI control structure
* @reset_work: Scheduled reset work thread
@@ -343,9 +350,9 @@ enum efx_rx_alloc_method {
* @rx_alloc_push_pages: RX allocation method currently in use for pushing
* descriptors
* @n_rx_tobe_disc: Count of RX_TOBE_DISC errors
- * @n_rx_ip_frag_err: Count of RX IP fragment errors
* @n_rx_ip_hdr_chksum_err: Count of RX IP header checksum errors
* @n_rx_tcp_udp_chksum_err: Count of RX TCP and UDP checksum errors
+ * @n_rx_mcast_mismatch: Count of unmatched multicast frames
* @n_rx_frm_trunc: Count of RX_FRM_TRUNC errors
* @n_rx_overlength: Count of RX_OVERLENGTH errors
* @n_skbuff_leaks: Count of skbuffs leaked due to RX overrun
@@ -373,9 +380,9 @@ struct efx_channel {
int rx_alloc_push_pages;
unsigned n_rx_tobe_disc;
- unsigned n_rx_ip_frag_err;
unsigned n_rx_ip_hdr_chksum_err;
unsigned n_rx_tcp_udp_chksum_err;
+ unsigned n_rx_mcast_mismatch;
unsigned n_rx_frm_trunc;
unsigned n_rx_overlength;
unsigned n_skbuff_leaks;
@@ -388,53 +395,29 @@ struct efx_channel {
};
-/**
- * struct efx_blinker - S/W LED blinking context
- * @state: Current state - on or off
- * @resubmit: Timer resubmission flag
- * @timer: Control timer for blinking
- */
-struct efx_blinker {
- bool state;
- bool resubmit;
- struct timer_list timer;
+enum efx_led_mode {
+ EFX_LED_OFF = 0,
+ EFX_LED_ON = 1,
+ EFX_LED_DEFAULT = 2
};
+#define STRING_TABLE_LOOKUP(val, member) \
+ ((val) < member ## _max) ? member ## _names[val] : "(invalid)"
-/**
- * struct efx_board - board information
- * @type: Board model type
- * @major: Major rev. ('A', 'B' ...)
- * @minor: Minor rev. (0, 1, ...)
- * @init: Initialisation function
- * @init_leds: Sets up board LEDs. May be called repeatedly.
- * @set_id_led: Turns the identification LED on or off
- * @blink: Starts/stops blinking
- * @monitor: Board-specific health check function
- * @fini: Cleanup function
- * @blinker: used to blink LEDs in software
- * @hwmon_client: I2C client for hardware monitor
- * @ioexp_client: I2C client for power/port control
- */
-struct efx_board {
- int type;
- int major;
- int minor;
- int (*init) (struct efx_nic *nic);
- /* As the LEDs are typically attached to the PHY, LEDs
- * have a separate init callback that happens later than
- * board init. */
- void (*init_leds)(struct efx_nic *efx);
- void (*set_id_led) (struct efx_nic *efx, bool state);
- int (*monitor) (struct efx_nic *nic);
- void (*blink) (struct efx_nic *efx, bool start);
- void (*fini) (struct efx_nic *nic);
- struct efx_blinker blinker;
- struct i2c_client *hwmon_client, *ioexp_client;
-};
+extern const char *efx_loopback_mode_names[];
+extern const unsigned int efx_loopback_mode_max;
+#define LOOPBACK_MODE(efx) \
+ STRING_TABLE_LOOKUP((efx)->loopback_mode, efx_loopback_mode)
+
+extern const char *efx_interrupt_mode_names[];
+extern const unsigned int efx_interrupt_mode_max;
+#define INT_MODE(efx) \
+ STRING_TABLE_LOOKUP(efx->interrupt_mode, efx_interrupt_mode)
-#define STRING_TABLE_LOOKUP(val, member) \
- member ## _names[val]
+extern const char *efx_reset_type_names[];
+extern const unsigned int efx_reset_type_max;
+#define RESET_TYPE(type) \
+ STRING_TABLE_LOOKUP(type, efx_reset_type)
enum efx_int_mode {
/* Be careful if altering to correct macro below */
@@ -445,20 +428,7 @@ enum efx_int_mode {
};
#define EFX_INT_MODE_USE_MSI(x) (((x)->interrupt_mode) <= EFX_INT_MODE_MSI)
-enum phy_type {
- PHY_TYPE_NONE = 0,
- PHY_TYPE_TXC43128 = 1,
- PHY_TYPE_88E1111 = 2,
- PHY_TYPE_SFX7101 = 3,
- PHY_TYPE_QT2022C2 = 4,
- PHY_TYPE_PM8358 = 6,
- PHY_TYPE_SFT9001A = 8,
- PHY_TYPE_QT2025C = 9,
- PHY_TYPE_SFT9001B = 10,
- PHY_TYPE_MAX /* Insert any new items before this */
-};
-
-#define EFX_IS10G(efx) ((efx)->link_speed == 10000)
+#define EFX_IS10G(efx) ((efx)->link_state.speed == 10000)
enum nic_state {
STATE_INIT = 0,
@@ -500,73 +470,69 @@ enum efx_fc_type {
EFX_FC_AUTO = 4,
};
-/* Supported MAC bit-mask */
-enum efx_mac_type {
- EFX_GMAC = 1,
- EFX_XMAC = 2,
+/**
+ * struct efx_link_state - Current state of the link
+ * @up: Link is up
+ * @fd: Link is full-duplex
+ * @fc: Actual flow control flags
+ * @speed: Link speed (Mbps)
+ */
+struct efx_link_state {
+ bool up;
+ bool fd;
+ enum efx_fc_type fc;
+ unsigned int speed;
};
-static inline enum efx_fc_type efx_fc_resolve(enum efx_fc_type wanted_fc,
- unsigned int lpa)
+static inline bool efx_link_state_equal(const struct efx_link_state *left,
+ const struct efx_link_state *right)
{
- BUILD_BUG_ON(EFX_FC_AUTO & (EFX_FC_RX | EFX_FC_TX));
-
- if (!(wanted_fc & EFX_FC_AUTO))
- return wanted_fc;
-
- return mii_resolve_flowctrl_fdx(mii_advertise_flowctrl(wanted_fc), lpa);
+ return left->up == right->up && left->fd == right->fd &&
+ left->fc == right->fc && left->speed == right->speed;
}
/**
* struct efx_mac_operations - Efx MAC operations table
* @reconfigure: Reconfigure MAC. Serialised by the mac_lock
* @update_stats: Update statistics
- * @irq: Hardware MAC event callback. Serialised by the mac_lock
- * @poll: Poll for hardware state. Serialised by the mac_lock
+ * @check_fault: Check fault state. True if fault present.
*/
struct efx_mac_operations {
- void (*reconfigure) (struct efx_nic *efx);
+ int (*reconfigure) (struct efx_nic *efx);
void (*update_stats) (struct efx_nic *efx);
- void (*irq) (struct efx_nic *efx);
- void (*poll) (struct efx_nic *efx);
+ bool (*check_fault)(struct efx_nic *efx);
};
/**
* struct efx_phy_operations - Efx PHY operations table
+ * @probe: Probe PHY and initialise efx->mdio.mode_support, efx->mdio.mmds,
+ * efx->loopback_modes.
* @init: Initialise PHY
* @fini: Shut down PHY
* @reconfigure: Reconfigure PHY (e.g. for new link parameters)
- * @clear_interrupt: Clear down interrupt
- * @blink: Blink LEDs
- * @poll: Poll for hardware state. Serialised by the mac_lock.
+ * @poll: Update @link_state and report whether it changed.
+ * Serialised by the mac_lock.
* @get_settings: Get ethtool settings. Serialised by the mac_lock.
* @set_settings: Set ethtool settings. Serialised by the mac_lock.
* @set_npage_adv: Set abilities advertised in (Extended) Next Page
* (only needed where AN bit is set in mmds)
- * @num_tests: Number of PHY-specific tests/results
- * @test_names: Names of the tests/results
+ * @test_name: Get the name of a PHY-specific test/result
* @run_tests: Run tests and record results as appropriate.
* Flags are the ethtool tests flags.
- * @mmds: MMD presence mask
- * @loopbacks: Supported loopback modes mask
*/
struct efx_phy_operations {
- enum efx_mac_type macs;
+ int (*probe) (struct efx_nic *efx);
int (*init) (struct efx_nic *efx);
void (*fini) (struct efx_nic *efx);
- void (*reconfigure) (struct efx_nic *efx);
- void (*clear_interrupt) (struct efx_nic *efx);
- void (*poll) (struct efx_nic *efx);
+ int (*reconfigure) (struct efx_nic *efx);
+ bool (*poll) (struct efx_nic *efx);
void (*get_settings) (struct efx_nic *efx,
struct ethtool_cmd *ecmd);
int (*set_settings) (struct efx_nic *efx,
struct ethtool_cmd *ecmd);
void (*set_npage_adv) (struct efx_nic *efx, u32);
- u32 num_tests;
- const char *const *test_names;
+ const char *(*test_name) (struct efx_nic *efx, unsigned int index);
int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags);
- int mmds;
- unsigned loopbacks;
};
/**
@@ -690,36 +656,38 @@ union efx_multicast_hash {
* @interrupt_mode: Interrupt mode
* @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues
* @irq_rx_moderation: IRQ moderation time for RX event queues
- * @i2c_adap: I2C adapter
- * @board_info: Board-level information
* @state: Device state flag. Serialised by the rtnl_lock.
* @reset_pending: Pending reset method (normally RESET_TYPE_NONE)
* @tx_queue: TX DMA queues
* @rx_queue: RX DMA queues
* @channel: Channels
+ * @next_buffer_table: First available buffer table id
* @n_rx_queues: Number of RX queues
* @n_channels: Number of channels in use
* @rx_buffer_len: RX buffer length
* @rx_buffer_order: Order (log2) of number of pages for each RX buffer
+ * @int_error_count: Number of internal errors seen recently
+ * @int_error_expire: Time at which error count will be expired
* @irq_status: Interrupt status buffer
* @last_irq_cpu: Last CPU to handle interrupt.
* This register is written with the SMP processor ID whenever an
* interrupt is handled. It is used by falcon_test_interrupt()
* to verify that an interrupt has occurred.
* @spi_flash: SPI flash device
- * This field will be %NULL if no flash device is present.
+ * This field will be %NULL if no flash device is present (or for Siena).
* @spi_eeprom: SPI EEPROM device
- * This field will be %NULL if no EEPROM device is present.
+ * This field will be %NULL if no EEPROM device is present (or for Siena).
* @spi_lock: SPI bus lock
+ * @mtd_list: List of MTDs attached to the NIC
* @n_rx_nodesc_drop_cnt: RX no descriptor drop count
* @nic_data: Hardware dependant state
* @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
* @port_inhibited, efx_monitor() and efx_reconfigure_port()
* @port_enabled: Port enabled indicator.
- * Serialises efx_stop_all(), efx_start_all(), efx_monitor(),
- * efx_phy_work(), and efx_mac_work() with kernel interfaces. Safe to read
- * under any one of the rtnl_lock, mac_lock, or netif_tx_lock, but all
- * three must be held to modify it.
+ * Serialises efx_stop_all(), efx_start_all(), efx_monitor() and
+ * efx_mac_work() with kernel interfaces. Safe to read under any
+ * one of the rtnl_lock, mac_lock, or netif_tx_lock, but all three must
+ * be held to modify it.
* @port_inhibited: If set, the netif_carrier is always off. Hold the mac_lock
* @port_initialized: Port initialized?
* @net_dev: Operating system network device. Consider holding the rtnl lock
@@ -731,26 +699,23 @@ union efx_multicast_hash {
* &struct net_device_stats.
* @stats_buffer: DMA buffer for statistics
* @stats_lock: Statistics update lock. Serialises statistics fetches
- * @stats_disable_count: Nest count for disabling statistics fetches
* @mac_op: MAC interface
* @mac_address: Permanent MAC address
* @phy_type: PHY type
- * @phy_lock: PHY access lock
+ * @mdio_lock: MDIO lock
* @phy_op: PHY interface
* @phy_data: PHY private data (including PHY-specific stats)
* @mdio: PHY MDIO interface
+ * @mdio_bus: PHY MDIO bus ID (only used by Siena)
* @phy_mode: PHY operating mode. Serialised by @mac_lock.
- * @mac_up: MAC link state
- * @link_up: Link status
- * @link_fd: Link is full duplex
- * @link_fc: Actualy flow control flags
- * @link_speed: Link speed (Mbps)
+ * @xmac_poll_required: XMAC link state needs polling
+ * @link_advertising: Autonegotiation advertising flags
+ * @link_state: Current state of the link
* @n_link_state_changes: Number of times the link has changed state
* @promiscuous: Promiscuous flag. Protected by netif_tx_lock.
* @multicast_hash: Multicast hash table
* @wanted_fc: Wanted flow control flags
- * @phy_work: work item for dealing with PHY events
- * @mac_work: work item for dealing with MAC events
+ * @mac_work: Work item for changing MAC promiscuity and multicast hash
* @loopback_mode: Loopback status
* @loopback_modes: Supported loopback mode bitmask
* @loopback_selftest: Offline self-test private state
@@ -774,9 +739,6 @@ struct efx_nic {
bool irq_rx_adaptive;
unsigned int irq_rx_moderation;
- struct i2c_adapter i2c_adap;
- struct efx_board board_info;
-
enum nic_state state;
enum reset_type reset_pending;
@@ -784,21 +746,29 @@ struct efx_nic {
struct efx_rx_queue rx_queue[EFX_MAX_RX_QUEUES];
struct efx_channel channel[EFX_MAX_CHANNELS];
+ unsigned next_buffer_table;
int n_rx_queues;
int n_channels;
unsigned int rx_buffer_len;
unsigned int rx_buffer_order;
+ unsigned int_error_count;
+ unsigned long int_error_expire;
+
struct efx_buffer irq_status;
volatile signed int last_irq_cpu;
+ unsigned long irq_zero_count;
struct efx_spi_device *spi_flash;
struct efx_spi_device *spi_eeprom;
struct mutex spi_lock;
+#ifdef CONFIG_SFC_MTD
+ struct list_head mtd_list;
+#endif
unsigned n_rx_nodesc_drop_cnt;
- struct falcon_nic_data *nic_data;
+ void *nic_data;
struct mutex mac_lock;
struct work_struct mac_work;
@@ -815,24 +785,21 @@ struct efx_nic {
struct efx_mac_stats mac_stats;
struct efx_buffer stats_buffer;
spinlock_t stats_lock;
- unsigned int stats_disable_count;
struct efx_mac_operations *mac_op;
unsigned char mac_address[ETH_ALEN];
- enum phy_type phy_type;
- spinlock_t phy_lock;
- struct work_struct phy_work;
+ unsigned int phy_type;
+ struct mutex mdio_lock;
struct efx_phy_operations *phy_op;
void *phy_data;
struct mdio_if_info mdio;
+ unsigned int mdio_bus;
enum efx_phy_mode phy_mode;
- bool mac_up;
- bool link_up;
- bool link_fd;
- enum efx_fc_type link_fc;
- unsigned int link_speed;
+ bool xmac_poll_required;
+ u32 link_advertising;
+ struct efx_link_state link_state;
unsigned int n_link_state_changes;
bool promiscuous;
@@ -841,7 +808,7 @@ struct efx_nic {
atomic_t rx_reset;
enum efx_loopback_mode loopback_mode;
- unsigned int loopback_modes;
+ u64 loopback_modes;
void *loopback_selftest;
};
@@ -860,50 +827,95 @@ static inline const char *efx_dev_name(struct efx_nic *efx)
return efx_dev_registered(efx) ? efx->name : "";
}
+static inline unsigned int efx_port_num(struct efx_nic *efx)
+{
+ return PCI_FUNC(efx->pci_dev->devfn);
+}
+
/**
* struct efx_nic_type - Efx device type definition
- * @mem_bar: Memory BAR number
+ * @probe: Probe the controller
+ * @remove: Free resources allocated by probe()
+ * @init: Initialise the controller
+ * @fini: Shut down the controller
+ * @monitor: Periodic function for polling link state and hardware monitor
+ * @reset: Reset the controller hardware and possibly the PHY. This will
+ * be called while the controller is uninitialised.
+ * @probe_port: Probe the MAC and PHY
+ * @remove_port: Free resources allocated by probe_port()
+ * @prepare_flush: Prepare the hardware for flushing the DMA queues
+ * @update_stats: Update statistics not provided by event handling
+ * @start_stats: Start the regular fetching of statistics
+ * @stop_stats: Stop the regular fetching of statistics
+ * @set_id_led: Set state of identifying LED or revert to automatic function
+ * @push_irq_moderation: Apply interrupt moderation value
+ * @push_multicast_hash: Apply multicast hash table
+ * @reconfigure_port: Push loopback/power/txdis changes to the MAC and PHY
+ * @get_wol: Get WoL configuration from driver state
+ * @set_wol: Push WoL configuration to the NIC
+ * @resume_wol: Synchronise WoL state between driver and MC (e.g. after resume)
+ * @test_registers: Test read/write functionality of control registers
+ * @test_nvram: Test validity of NVRAM contents
+ * @default_mac_ops: efx_mac_operations to set at startup
+ * @revision: Hardware architecture revision
* @mem_map_size: Memory BAR mapped size
* @txd_ptr_tbl_base: TX descriptor ring base address
* @rxd_ptr_tbl_base: RX descriptor ring base address
* @buf_tbl_base: Buffer table base address
* @evq_ptr_tbl_base: Event queue pointer table base address
* @evq_rptr_tbl_base: Event queue read-pointer table base address
- * @txd_ring_mask: TX descriptor ring size - 1 (must be a power of two - 1)
- * @rxd_ring_mask: RX descriptor ring size - 1 (must be a power of two - 1)
- * @evq_size: Event queue size (must be a power of two)
* @max_dma_mask: Maximum possible DMA mask
- * @tx_dma_mask: TX DMA mask
- * @bug5391_mask: Address mask for bug 5391 workaround
- * @rx_xoff_thresh: RX FIFO XOFF watermark (bytes)
- * @rx_xon_thresh: RX FIFO XON watermark (bytes)
* @rx_buffer_padding: Padding added to each RX buffer
* @max_interrupt_mode: Highest capability interrupt mode supported
* from &enum efx_init_mode.
* @phys_addr_channels: Number of channels with physically addressed
* descriptors
+ * @tx_dc_base: Base address in SRAM of TX queue descriptor caches
+ * @rx_dc_base: Base address in SRAM of RX queue descriptor caches
+ * @offload_features: net_device feature flags for protocol offload
+ * features implemented in hardware
+ * @reset_world_flags: Flags for additional components covered by
+ * reset method RESET_TYPE_WORLD
*/
struct efx_nic_type {
- unsigned int mem_bar;
+ int (*probe)(struct efx_nic *efx);
+ void (*remove)(struct efx_nic *efx);
+ int (*init)(struct efx_nic *efx);
+ void (*fini)(struct efx_nic *efx);
+ void (*monitor)(struct efx_nic *efx);
+ int (*reset)(struct efx_nic *efx, enum reset_type method);
+ int (*probe_port)(struct efx_nic *efx);
+ void (*remove_port)(struct efx_nic *efx);
+ void (*prepare_flush)(struct efx_nic *efx);
+ void (*update_stats)(struct efx_nic *efx);
+ void (*start_stats)(struct efx_nic *efx);
+ void (*stop_stats)(struct efx_nic *efx);
+ void (*set_id_led)(struct efx_nic *efx, enum efx_led_mode mode);
+ void (*push_irq_moderation)(struct efx_channel *channel);
+ void (*push_multicast_hash)(struct efx_nic *efx);
+ int (*reconfigure_port)(struct efx_nic *efx);
+ void (*get_wol)(struct efx_nic *efx, struct ethtool_wolinfo *wol);
+ int (*set_wol)(struct efx_nic *efx, u32 type);
+ void (*resume_wol)(struct efx_nic *efx);
+ int (*test_registers)(struct efx_nic *efx);
+ int (*test_nvram)(struct efx_nic *efx);
+ struct efx_mac_operations *default_mac_ops;
+
+ int revision;
unsigned int mem_map_size;
unsigned int txd_ptr_tbl_base;
unsigned int rxd_ptr_tbl_base;
unsigned int buf_tbl_base;
unsigned int evq_ptr_tbl_base;
unsigned int evq_rptr_tbl_base;
-
- unsigned int txd_ring_mask;
- unsigned int rxd_ring_mask;
- unsigned int evq_size;
u64 max_dma_mask;
- unsigned int tx_dma_mask;
- unsigned bug5391_mask;
-
- int rx_xoff_thresh;
- int rx_xon_thresh;
unsigned int rx_buffer_padding;
unsigned int max_interrupt_mode;
unsigned int phys_addr_channels;
+ unsigned int tx_dc_base;
+ unsigned int rx_dc_base;
+ unsigned long offload_features;
+ u32 reset_world_flags;
};
/**************************************************************************
diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c
new file mode 100644
index 00000000000..a577be22786
--- /dev/null
+++ b/drivers/net/sfc/nic.c
@@ -0,0 +1,1583 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include "net_driver.h"
+#include "bitfield.h"
+#include "efx.h"
+#include "nic.h"
+#include "regs.h"
+#include "io.h"
+#include "workarounds.h"
+
+/**************************************************************************
+ *
+ * Configurable values
+ *
+ **************************************************************************
+ */
+
+/* This is set to 16 for a good reason. In summary, if larger than
+ * 16, the descriptor cache holds more than a default socket
+ * buffer's worth of packets (for UDP we can only have at most one
+ * socket buffer's worth outstanding). This combined with the fact
+ * that we only get 1 TX event per descriptor cache means the NIC
+ * goes idle.
+ */
+#define TX_DC_ENTRIES 16
+#define TX_DC_ENTRIES_ORDER 1
+
+#define RX_DC_ENTRIES 64
+#define RX_DC_ENTRIES_ORDER 3
+
+/* RX FIFO XOFF watermark
+ *
+ * When the amount of the RX FIFO increases used increases past this
+ * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A)
+ * This also has an effect on RX/TX arbitration
+ */
+int efx_nic_rx_xoff_thresh = -1;
+module_param_named(rx_xoff_thresh_bytes, efx_nic_rx_xoff_thresh, int, 0644);
+MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold");
+
+/* RX FIFO XON watermark
+ *
+ * When the amount of the RX FIFO used decreases below this
+ * watermark send XON. Only used if TX flow control is enabled (ethtool -A)
+ * This also has an effect on RX/TX arbitration
+ */
+int efx_nic_rx_xon_thresh = -1;
+module_param_named(rx_xon_thresh_bytes, efx_nic_rx_xon_thresh, int, 0644);
+MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold");
+
+/* If EFX_MAX_INT_ERRORS internal errors occur within
+ * EFX_INT_ERROR_EXPIRE seconds, we consider the NIC broken and
+ * disable it.
+ */
+#define EFX_INT_ERROR_EXPIRE 3600
+#define EFX_MAX_INT_ERRORS 5
+
+/* We poll for events every FLUSH_INTERVAL ms, and check FLUSH_POLL_COUNT times
+ */
+#define EFX_FLUSH_INTERVAL 10
+#define EFX_FLUSH_POLL_COUNT 100
+
+/* Size and alignment of special buffers (4KB) */
+#define EFX_BUF_SIZE 4096
+
+/* Depth of RX flush request fifo */
+#define EFX_RX_FLUSH_COUNT 4
+
+/**************************************************************************
+ *
+ * Solarstorm hardware access
+ *
+ **************************************************************************/
+
+static inline void efx_write_buf_tbl(struct efx_nic *efx, efx_qword_t *value,
+ unsigned int index)
+{
+ efx_sram_writeq(efx, efx->membase + efx->type->buf_tbl_base,
+ value, index);
+}
+
+/* Read the current event from the event queue */
+static inline efx_qword_t *efx_event(struct efx_channel *channel,
+ unsigned int index)
+{
+ return (((efx_qword_t *) (channel->eventq.addr)) + index);
+}
+
+/* See if an event is present
+ *
+ * We check both the high and low dword of the event for all ones. We
+ * wrote all ones when we cleared the event, and no valid event can
+ * have all ones in either its high or low dwords. This approach is
+ * robust against reordering.
+ *
+ * Note that using a single 64-bit comparison is incorrect; even
+ * though the CPU read will be atomic, the DMA write may not be.
+ */
+static inline int efx_event_present(efx_qword_t *event)
+{
+ return (!(EFX_DWORD_IS_ALL_ONES(event->dword[0]) |
+ EFX_DWORD_IS_ALL_ONES(event->dword[1])));
+}
+
+static bool efx_masked_compare_oword(const efx_oword_t *a, const efx_oword_t *b,
+ const efx_oword_t *mask)
+{
+ return ((a->u64[0] ^ b->u64[0]) & mask->u64[0]) ||
+ ((a->u64[1] ^ b->u64[1]) & mask->u64[1]);
+}
+
+int efx_nic_test_registers(struct efx_nic *efx,
+ const struct efx_nic_register_test *regs,
+ size_t n_regs)
+{
+ unsigned address = 0, i, j;
+ efx_oword_t mask, imask, original, reg, buf;
+
+ /* Falcon should be in loopback to isolate the XMAC from the PHY */
+ WARN_ON(!LOOPBACK_INTERNAL(efx));
+
+ for (i = 0; i < n_regs; ++i) {
+ address = regs[i].address;
+ mask = imask = regs[i].mask;
+ EFX_INVERT_OWORD(imask);
+
+ efx_reado(efx, &original, address);
+
+ /* bit sweep on and off */
+ for (j = 0; j < 128; j++) {
+ if (!EFX_EXTRACT_OWORD32(mask, j, j))
+ continue;
+
+ /* Test this testable bit can be set in isolation */
+ EFX_AND_OWORD(reg, original, mask);
+ EFX_SET_OWORD32(reg, j, j, 1);
+
+ efx_writeo(efx, &reg, address);
+ efx_reado(efx, &buf, address);
+
+ if (efx_masked_compare_oword(&reg, &buf, &mask))
+ goto fail;
+
+ /* Test this testable bit can be cleared in isolation */
+ EFX_OR_OWORD(reg, original, mask);
+ EFX_SET_OWORD32(reg, j, j, 0);
+
+ efx_writeo(efx, &reg, address);
+ efx_reado(efx, &buf, address);
+
+ if (efx_masked_compare_oword(&reg, &buf, &mask))
+ goto fail;
+ }
+
+ efx_writeo(efx, &original, address);
+ }
+
+ return 0;
+
+fail:
+ EFX_ERR(efx, "wrote "EFX_OWORD_FMT" read "EFX_OWORD_FMT
+ " at address 0x%x mask "EFX_OWORD_FMT"\n", EFX_OWORD_VAL(reg),
+ EFX_OWORD_VAL(buf), address, EFX_OWORD_VAL(mask));
+ return -EIO;
+}
+
+/**************************************************************************
+ *
+ * Special buffer handling
+ * Special buffers are used for event queues and the TX and RX
+ * descriptor rings.
+ *
+ *************************************************************************/
+
+/*
+ * Initialise a special buffer
+ *
+ * This will define a buffer (previously allocated via
+ * efx_alloc_special_buffer()) in the buffer table, allowing
+ * it to be used for event queues, descriptor rings etc.
+ */
+static void
+efx_init_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer)
+{
+ efx_qword_t buf_desc;
+ int index;
+ dma_addr_t dma_addr;
+ int i;
+
+ EFX_BUG_ON_PARANOID(!buffer->addr);
+
+ /* Write buffer descriptors to NIC */
+ for (i = 0; i < buffer->entries; i++) {
+ index = buffer->index + i;
+ dma_addr = buffer->dma_addr + (i * 4096);
+ EFX_LOG(efx, "mapping special buffer %d at %llx\n",
+ index, (unsigned long long)dma_addr);
+ EFX_POPULATE_QWORD_3(buf_desc,
+ FRF_AZ_BUF_ADR_REGION, 0,
+ FRF_AZ_BUF_ADR_FBUF, dma_addr >> 12,
+ FRF_AZ_BUF_OWNER_ID_FBUF, 0);
+ efx_write_buf_tbl(efx, &buf_desc, index);
+ }
+}
+
+/* Unmaps a buffer and clears the buffer table entries */
+static void
+efx_fini_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer)
+{
+ efx_oword_t buf_tbl_upd;
+ unsigned int start = buffer->index;
+ unsigned int end = (buffer->index + buffer->entries - 1);
+
+ if (!buffer->entries)
+ return;
+
+ EFX_LOG(efx, "unmapping special buffers %d-%d\n",
+ buffer->index, buffer->index + buffer->entries - 1);
+
+ EFX_POPULATE_OWORD_4(buf_tbl_upd,
+ FRF_AZ_BUF_UPD_CMD, 0,
+ FRF_AZ_BUF_CLR_CMD, 1,
+ FRF_AZ_BUF_CLR_END_ID, end,
+ FRF_AZ_BUF_CLR_START_ID, start);
+ efx_writeo(efx, &buf_tbl_upd, FR_AZ_BUF_TBL_UPD);
+}
+
+/*
+ * Allocate a new special buffer
+ *
+ * This allocates memory for a new buffer, clears it and allocates a
+ * new buffer ID range. It does not write into the buffer table.
+ *
+ * This call will allocate 4KB buffers, since 8KB buffers can't be
+ * used for event queues and descriptor rings.
+ */
+static int efx_alloc_special_buffer(struct efx_nic *efx,
+ struct efx_special_buffer *buffer,
+ unsigned int len)
+{
+ len = ALIGN(len, EFX_BUF_SIZE);
+
+ buffer->addr = pci_alloc_consistent(efx->pci_dev, len,
+ &buffer->dma_addr);
+ if (!buffer->addr)
+ return -ENOMEM;
+ buffer->len = len;
+ buffer->entries = len / EFX_BUF_SIZE;
+ BUG_ON(buffer->dma_addr & (EFX_BUF_SIZE - 1));
+
+ /* All zeros is a potentially valid event so memset to 0xff */
+ memset(buffer->addr, 0xff, len);
+
+ /* Select new buffer ID */
+ buffer->index = efx->next_buffer_table;
+ efx->next_buffer_table += buffer->entries;
+
+ EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x "
+ "(virt %p phys %llx)\n", buffer->index,
+ buffer->index + buffer->entries - 1,
+ (u64)buffer->dma_addr, len,
+ buffer->addr, (u64)virt_to_phys(buffer->addr));
+
+ return 0;
+}
+
+static void
+efx_free_special_buffer(struct efx_nic *efx, struct efx_special_buffer *buffer)
+{
+ if (!buffer->addr)
+ return;
+
+ EFX_LOG(efx, "deallocating special buffers %d-%d at %llx+%x "
+ "(virt %p phys %llx)\n", buffer->index,
+ buffer->index + buffer->entries - 1,
+ (u64)buffer->dma_addr, buffer->len,
+ buffer->addr, (u64)virt_to_phys(buffer->addr));
+
+ pci_free_consistent(efx->pci_dev, buffer->len, buffer->addr,
+ buffer->dma_addr);
+ buffer->addr = NULL;
+ buffer->entries = 0;
+}
+
+/**************************************************************************
+ *
+ * Generic buffer handling
+ * These buffers are used for interrupt status and MAC stats
+ *
+ **************************************************************************/
+
+int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer,
+ unsigned int len)
+{
+ buffer->addr = pci_alloc_consistent(efx->pci_dev, len,
+ &buffer->dma_addr);
+ if (!buffer->addr)
+ return -ENOMEM;
+ buffer->len = len;
+ memset(buffer->addr, 0, len);
+ return 0;
+}
+
+void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer)
+{
+ if (buffer->addr) {
+ pci_free_consistent(efx->pci_dev, buffer->len,
+ buffer->addr, buffer->dma_addr);
+ buffer->addr = NULL;
+ }
+}
+
+/**************************************************************************
+ *
+ * TX path
+ *
+ **************************************************************************/
+
+/* Returns a pointer to the specified transmit descriptor in the TX
+ * descriptor queue belonging to the specified channel.
+ */
+static inline efx_qword_t *
+efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index)
+{
+ return (((efx_qword_t *) (tx_queue->txd.addr)) + index);
+}
+
+/* This writes to the TX_DESC_WPTR; write pointer for TX descriptor ring */
+static inline void efx_notify_tx_desc(struct efx_tx_queue *tx_queue)
+{
+ unsigned write_ptr;
+ efx_dword_t reg;
+
+ write_ptr = tx_queue->write_count & EFX_TXQ_MASK;
+ EFX_POPULATE_DWORD_1(reg, FRF_AZ_TX_DESC_WPTR_DWORD, write_ptr);
+ efx_writed_page(tx_queue->efx, &reg,
+ FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue);
+}
+
+
+/* For each entry inserted into the software descriptor ring, create a
+ * descriptor in the hardware TX descriptor ring (in host memory), and
+ * write a doorbell.
+ */
+void efx_nic_push_buffers(struct efx_tx_queue *tx_queue)
+{
+
+ struct efx_tx_buffer *buffer;
+ efx_qword_t *txd;
+ unsigned write_ptr;
+
+ BUG_ON(tx_queue->write_count == tx_queue->insert_count);
+
+ do {
+ write_ptr = tx_queue->write_count & EFX_TXQ_MASK;
+ buffer = &tx_queue->buffer[write_ptr];
+ txd = efx_tx_desc(tx_queue, write_ptr);
+ ++tx_queue->write_count;
+
+ /* Create TX descriptor ring entry */
+ EFX_POPULATE_QWORD_4(*txd,
+ FSF_AZ_TX_KER_CONT, buffer->continuation,
+ FSF_AZ_TX_KER_BYTE_COUNT, buffer->len,
+ FSF_AZ_TX_KER_BUF_REGION, 0,
+ FSF_AZ_TX_KER_BUF_ADDR, buffer->dma_addr);
+ } while (tx_queue->write_count != tx_queue->insert_count);
+
+ wmb(); /* Ensure descriptors are written before they are fetched */
+ efx_notify_tx_desc(tx_queue);
+}
+
+/* Allocate hardware resources for a TX queue */
+int efx_nic_probe_tx(struct efx_tx_queue *tx_queue)
+{
+ struct efx_nic *efx = tx_queue->efx;
+ BUILD_BUG_ON(EFX_TXQ_SIZE < 512 || EFX_TXQ_SIZE > 4096 ||
+ EFX_TXQ_SIZE & EFX_TXQ_MASK);
+ return efx_alloc_special_buffer(efx, &tx_queue->txd,
+ EFX_TXQ_SIZE * sizeof(efx_qword_t));
+}
+
+void efx_nic_init_tx(struct efx_tx_queue *tx_queue)
+{
+ efx_oword_t tx_desc_ptr;
+ struct efx_nic *efx = tx_queue->efx;
+
+ tx_queue->flushed = FLUSH_NONE;
+
+ /* Pin TX descriptor ring */
+ efx_init_special_buffer(efx, &tx_queue->txd);
+
+ /* Push TX descriptor ring to card */
+ EFX_POPULATE_OWORD_10(tx_desc_ptr,
+ FRF_AZ_TX_DESCQ_EN, 1,
+ FRF_AZ_TX_ISCSI_DDIG_EN, 0,
+ FRF_AZ_TX_ISCSI_HDIG_EN, 0,
+ FRF_AZ_TX_DESCQ_BUF_BASE_ID, tx_queue->txd.index,
+ FRF_AZ_TX_DESCQ_EVQ_ID,
+ tx_queue->channel->channel,
+ FRF_AZ_TX_DESCQ_OWNER_ID, 0,
+ FRF_AZ_TX_DESCQ_LABEL, tx_queue->queue,
+ FRF_AZ_TX_DESCQ_SIZE,
+ __ffs(tx_queue->txd.entries),
+ FRF_AZ_TX_DESCQ_TYPE, 0,
+ FRF_BZ_TX_NON_IP_DROP_DIS, 1);
+
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0) {
+ int csum = tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM;
+ EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_IP_CHKSM_DIS, !csum);
+ EFX_SET_OWORD_FIELD(tx_desc_ptr, FRF_BZ_TX_TCP_CHKSM_DIS,
+ !csum);
+ }
+
+ efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base,
+ tx_queue->queue);
+
+ if (efx_nic_rev(efx) < EFX_REV_FALCON_B0) {
+ efx_oword_t reg;
+
+ /* Only 128 bits in this register */
+ BUILD_BUG_ON(EFX_TX_QUEUE_COUNT >= 128);
+
+ efx_reado(efx, &reg, FR_AA_TX_CHKSM_CFG);
+ if (tx_queue->queue == EFX_TX_QUEUE_OFFLOAD_CSUM)
+ clear_bit_le(tx_queue->queue, (void *)&reg);
+ else
+ set_bit_le(tx_queue->queue, (void *)&reg);
+ efx_writeo(efx, &reg, FR_AA_TX_CHKSM_CFG);
+ }
+}
+
+static void efx_flush_tx_queue(struct efx_tx_queue *tx_queue)
+{
+ struct efx_nic *efx = tx_queue->efx;
+ efx_oword_t tx_flush_descq;
+
+ tx_queue->flushed = FLUSH_PENDING;
+
+ /* Post a flush command */
+ EFX_POPULATE_OWORD_2(tx_flush_descq,
+ FRF_AZ_TX_FLUSH_DESCQ_CMD, 1,
+ FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue);
+ efx_writeo(efx, &tx_flush_descq, FR_AZ_TX_FLUSH_DESCQ);
+}
+
+void efx_nic_fini_tx(struct efx_tx_queue *tx_queue)
+{
+ struct efx_nic *efx = tx_queue->efx;
+ efx_oword_t tx_desc_ptr;
+
+ /* The queue should have been flushed */
+ WARN_ON(tx_queue->flushed != FLUSH_DONE);
+
+ /* Remove TX descriptor ring from card */
+ EFX_ZERO_OWORD(tx_desc_ptr);
+ efx_writeo_table(efx, &tx_desc_ptr, efx->type->txd_ptr_tbl_base,
+ tx_queue->queue);
+
+ /* Unpin TX descriptor ring */
+ efx_fini_special_buffer(efx, &tx_queue->txd);
+}
+
+/* Free buffers backing TX queue */
+void efx_nic_remove_tx(struct efx_tx_queue *tx_queue)
+{
+ efx_free_special_buffer(tx_queue->efx, &tx_queue->txd);
+}
+
+/**************************************************************************
+ *
+ * RX path
+ *
+ **************************************************************************/
+
+/* Returns a pointer to the specified descriptor in the RX descriptor queue */
+static inline efx_qword_t *
+efx_rx_desc(struct efx_rx_queue *rx_queue, unsigned int index)
+{
+ return (((efx_qword_t *) (rx_queue->rxd.addr)) + index);
+}
+
+/* This creates an entry in the RX descriptor queue */
+static inline void
+efx_build_rx_desc(struct efx_rx_queue *rx_queue, unsigned index)
+{
+ struct efx_rx_buffer *rx_buf;
+ efx_qword_t *rxd;
+
+ rxd = efx_rx_desc(rx_queue, index);
+ rx_buf = efx_rx_buffer(rx_queue, index);
+ EFX_POPULATE_QWORD_3(*rxd,
+ FSF_AZ_RX_KER_BUF_SIZE,
+ rx_buf->len -
+ rx_queue->efx->type->rx_buffer_padding,
+ FSF_AZ_RX_KER_BUF_REGION, 0,
+ FSF_AZ_RX_KER_BUF_ADDR, rx_buf->dma_addr);
+}
+
+/* This writes to the RX_DESC_WPTR register for the specified receive
+ * descriptor ring.
+ */
+void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue)
+{
+ efx_dword_t reg;
+ unsigned write_ptr;
+
+ while (rx_queue->notified_count != rx_queue->added_count) {
+ efx_build_rx_desc(rx_queue,
+ rx_queue->notified_count &
+ EFX_RXQ_MASK);
+ ++rx_queue->notified_count;
+ }
+
+ wmb();
+ write_ptr = rx_queue->added_count & EFX_RXQ_MASK;
+ EFX_POPULATE_DWORD_1(reg, FRF_AZ_RX_DESC_WPTR_DWORD, write_ptr);
+ efx_writed_page(rx_queue->efx, &reg,
+ FR_AZ_RX_DESC_UPD_DWORD_P0, rx_queue->queue);
+}
+
+int efx_nic_probe_rx(struct efx_rx_queue *rx_queue)
+{
+ struct efx_nic *efx = rx_queue->efx;
+ BUILD_BUG_ON(EFX_RXQ_SIZE < 512 || EFX_RXQ_SIZE > 4096 ||
+ EFX_RXQ_SIZE & EFX_RXQ_MASK);
+ return efx_alloc_special_buffer(efx, &rx_queue->rxd,
+ EFX_RXQ_SIZE * sizeof(efx_qword_t));
+}
+
+void efx_nic_init_rx(struct efx_rx_queue *rx_queue)
+{
+ efx_oword_t rx_desc_ptr;
+ struct efx_nic *efx = rx_queue->efx;
+ bool is_b0 = efx_nic_rev(efx) >= EFX_REV_FALCON_B0;
+ bool iscsi_digest_en = is_b0;
+
+ EFX_LOG(efx, "RX queue %d ring in special buffers %d-%d\n",
+ rx_queue->queue, rx_queue->rxd.index,
+ rx_queue->rxd.index + rx_queue->rxd.entries - 1);
+
+ rx_queue->flushed = FLUSH_NONE;
+
+ /* Pin RX descriptor ring */
+ efx_init_special_buffer(efx, &rx_queue->rxd);
+
+ /* Push RX descriptor ring to card */
+ EFX_POPULATE_OWORD_10(rx_desc_ptr,
+ FRF_AZ_RX_ISCSI_DDIG_EN, iscsi_digest_en,
+ FRF_AZ_RX_ISCSI_HDIG_EN, iscsi_digest_en,
+ FRF_AZ_RX_DESCQ_BUF_BASE_ID, rx_queue->rxd.index,
+ FRF_AZ_RX_DESCQ_EVQ_ID,
+ rx_queue->channel->channel,
+ FRF_AZ_RX_DESCQ_OWNER_ID, 0,
+ FRF_AZ_RX_DESCQ_LABEL, rx_queue->queue,
+ FRF_AZ_RX_DESCQ_SIZE,
+ __ffs(rx_queue->rxd.entries),
+ FRF_AZ_RX_DESCQ_TYPE, 0 /* kernel queue */ ,
+ /* For >=B0 this is scatter so disable */
+ FRF_AZ_RX_DESCQ_JUMBO, !is_b0,
+ FRF_AZ_RX_DESCQ_EN, 1);
+ efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base,
+ rx_queue->queue);
+}
+
+static void efx_flush_rx_queue(struct efx_rx_queue *rx_queue)
+{
+ struct efx_nic *efx = rx_queue->efx;
+ efx_oword_t rx_flush_descq;
+
+ rx_queue->flushed = FLUSH_PENDING;
+
+ /* Post a flush command */
+ EFX_POPULATE_OWORD_2(rx_flush_descq,
+ FRF_AZ_RX_FLUSH_DESCQ_CMD, 1,
+ FRF_AZ_RX_FLUSH_DESCQ, rx_queue->queue);
+ efx_writeo(efx, &rx_flush_descq, FR_AZ_RX_FLUSH_DESCQ);
+}
+
+void efx_nic_fini_rx(struct efx_rx_queue *rx_queue)
+{
+ efx_oword_t rx_desc_ptr;
+ struct efx_nic *efx = rx_queue->efx;
+
+ /* The queue should already have been flushed */
+ WARN_ON(rx_queue->flushed != FLUSH_DONE);
+
+ /* Remove RX descriptor ring from card */
+ EFX_ZERO_OWORD(rx_desc_ptr);
+ efx_writeo_table(efx, &rx_desc_ptr, efx->type->rxd_ptr_tbl_base,
+ rx_queue->queue);
+
+ /* Unpin RX descriptor ring */
+ efx_fini_special_buffer(efx, &rx_queue->rxd);
+}
+
+/* Free buffers backing RX queue */
+void efx_nic_remove_rx(struct efx_rx_queue *rx_queue)
+{
+ efx_free_special_buffer(rx_queue->efx, &rx_queue->rxd);
+}
+
+/**************************************************************************
+ *
+ * Event queue processing
+ * Event queues are processed by per-channel tasklets.
+ *
+ **************************************************************************/
+
+/* Update a channel's event queue's read pointer (RPTR) register
+ *
+ * This writes the EVQ_RPTR_REG register for the specified channel's
+ * event queue.
+ *
+ * Note that EVQ_RPTR_REG contains the index of the "last read" event,
+ * whereas channel->eventq_read_ptr contains the index of the "next to
+ * read" event.
+ */
+void efx_nic_eventq_read_ack(struct efx_channel *channel)
+{
+ efx_dword_t reg;
+ struct efx_nic *efx = channel->efx;
+
+ EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, channel->eventq_read_ptr);
+ efx_writed_table(efx, &reg, efx->type->evq_rptr_tbl_base,
+ channel->channel);
+}
+
+/* Use HW to insert a SW defined event */
+void efx_generate_event(struct efx_channel *channel, efx_qword_t *event)
+{
+ efx_oword_t drv_ev_reg;
+
+ BUILD_BUG_ON(FRF_AZ_DRV_EV_DATA_LBN != 0 ||
+ FRF_AZ_DRV_EV_DATA_WIDTH != 64);
+ drv_ev_reg.u32[0] = event->u32[0];
+ drv_ev_reg.u32[1] = event->u32[1];
+ drv_ev_reg.u32[2] = 0;
+ drv_ev_reg.u32[3] = 0;
+ EFX_SET_OWORD_FIELD(drv_ev_reg, FRF_AZ_DRV_EV_QID, channel->channel);
+ efx_writeo(channel->efx, &drv_ev_reg, FR_AZ_DRV_EV);
+}
+
+/* Handle a transmit completion event
+ *
+ * The NIC batches TX completion events; the message we receive is of
+ * the form "complete all TX events up to this index".
+ */
+static void
+efx_handle_tx_event(struct efx_channel *channel, efx_qword_t *event)
+{
+ unsigned int tx_ev_desc_ptr;
+ unsigned int tx_ev_q_label;
+ struct efx_tx_queue *tx_queue;
+ struct efx_nic *efx = channel->efx;
+
+ if (likely(EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_COMP))) {
+ /* Transmit completion */
+ tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_DESC_PTR);
+ tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL);
+ tx_queue = &efx->tx_queue[tx_ev_q_label];
+ channel->irq_mod_score +=
+ (tx_ev_desc_ptr - tx_queue->read_count) &
+ EFX_TXQ_MASK;
+ efx_xmit_done(tx_queue, tx_ev_desc_ptr);
+ } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_WQ_FF_FULL)) {
+ /* Rewrite the FIFO write pointer */
+ tx_ev_q_label = EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_Q_LABEL);
+ tx_queue = &efx->tx_queue[tx_ev_q_label];
+
+ if (efx_dev_registered(efx))
+ netif_tx_lock(efx->net_dev);
+ efx_notify_tx_desc(tx_queue);
+ if (efx_dev_registered(efx))
+ netif_tx_unlock(efx->net_dev);
+ } else if (EFX_QWORD_FIELD(*event, FSF_AZ_TX_EV_PKT_ERR) &&
+ EFX_WORKAROUND_10727(efx)) {
+ efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH);
+ } else {
+ EFX_ERR(efx, "channel %d unexpected TX event "
+ EFX_QWORD_FMT"\n", channel->channel,
+ EFX_QWORD_VAL(*event));
+ }
+}
+
+/* Detect errors included in the rx_evt_pkt_ok bit. */
+static void efx_handle_rx_not_ok(struct efx_rx_queue *rx_queue,
+ const efx_qword_t *event,
+ bool *rx_ev_pkt_ok,
+ bool *discard)
+{
+ struct efx_nic *efx = rx_queue->efx;
+ bool rx_ev_buf_owner_id_err, rx_ev_ip_hdr_chksum_err;
+ bool rx_ev_tcp_udp_chksum_err, rx_ev_eth_crc_err;
+ bool rx_ev_frm_trunc, rx_ev_drib_nib, rx_ev_tobe_disc;
+ bool rx_ev_other_err, rx_ev_pause_frm;
+ bool rx_ev_hdr_type, rx_ev_mcast_pkt;
+ unsigned rx_ev_pkt_type;
+
+ rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE);
+ rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT);
+ rx_ev_tobe_disc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_TOBE_DISC);
+ rx_ev_pkt_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_TYPE);
+ rx_ev_buf_owner_id_err = EFX_QWORD_FIELD(*event,
+ FSF_AZ_RX_EV_BUF_OWNER_ID_ERR);
+ rx_ev_ip_hdr_chksum_err = EFX_QWORD_FIELD(*event,
+ FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR);
+ rx_ev_tcp_udp_chksum_err = EFX_QWORD_FIELD(*event,
+ FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR);
+ rx_ev_eth_crc_err = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_ETH_CRC_ERR);
+ rx_ev_frm_trunc = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_FRM_TRUNC);
+ rx_ev_drib_nib = ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) ?
+ 0 : EFX_QWORD_FIELD(*event, FSF_AA_RX_EV_DRIB_NIB));
+ rx_ev_pause_frm = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PAUSE_FRM_ERR);
+
+ /* Every error apart from tobe_disc and pause_frm */
+ rx_ev_other_err = (rx_ev_drib_nib | rx_ev_tcp_udp_chksum_err |
+ rx_ev_buf_owner_id_err | rx_ev_eth_crc_err |
+ rx_ev_frm_trunc | rx_ev_ip_hdr_chksum_err);
+
+ /* Count errors that are not in MAC stats. Ignore expected
+ * checksum errors during self-test. */
+ if (rx_ev_frm_trunc)
+ ++rx_queue->channel->n_rx_frm_trunc;
+ else if (rx_ev_tobe_disc)
+ ++rx_queue->channel->n_rx_tobe_disc;
+ else if (!efx->loopback_selftest) {
+ if (rx_ev_ip_hdr_chksum_err)
+ ++rx_queue->channel->n_rx_ip_hdr_chksum_err;
+ else if (rx_ev_tcp_udp_chksum_err)
+ ++rx_queue->channel->n_rx_tcp_udp_chksum_err;
+ }
+
+ /* The frame must be discarded if any of these are true. */
+ *discard = (rx_ev_eth_crc_err | rx_ev_frm_trunc | rx_ev_drib_nib |
+ rx_ev_tobe_disc | rx_ev_pause_frm);
+
+ /* TOBE_DISC is expected on unicast mismatches; don't print out an
+ * error message. FRM_TRUNC indicates RXDP dropped the packet due
+ * to a FIFO overflow.
+ */
+#ifdef EFX_ENABLE_DEBUG
+ if (rx_ev_other_err) {
+ EFX_INFO_RL(efx, " RX queue %d unexpected RX event "
+ EFX_QWORD_FMT "%s%s%s%s%s%s%s%s\n",
+ rx_queue->queue, EFX_QWORD_VAL(*event),
+ rx_ev_buf_owner_id_err ? " [OWNER_ID_ERR]" : "",
+ rx_ev_ip_hdr_chksum_err ?
+ " [IP_HDR_CHKSUM_ERR]" : "",
+ rx_ev_tcp_udp_chksum_err ?
+ " [TCP_UDP_CHKSUM_ERR]" : "",
+ rx_ev_eth_crc_err ? " [ETH_CRC_ERR]" : "",
+ rx_ev_frm_trunc ? " [FRM_TRUNC]" : "",
+ rx_ev_drib_nib ? " [DRIB_NIB]" : "",
+ rx_ev_tobe_disc ? " [TOBE_DISC]" : "",
+ rx_ev_pause_frm ? " [PAUSE]" : "");
+ }
+#endif
+}
+
+/* Handle receive events that are not in-order. */
+static void
+efx_handle_rx_bad_index(struct efx_rx_queue *rx_queue, unsigned index)
+{
+ struct efx_nic *efx = rx_queue->efx;
+ unsigned expected, dropped;
+
+ expected = rx_queue->removed_count & EFX_RXQ_MASK;
+ dropped = (index - expected) & EFX_RXQ_MASK;
+ EFX_INFO(efx, "dropped %d events (index=%d expected=%d)\n",
+ dropped, index, expected);
+
+ efx_schedule_reset(efx, EFX_WORKAROUND_5676(efx) ?
+ RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE);
+}
+
+/* Handle a packet received event
+ *
+ * The NIC gives a "discard" flag if it's a unicast packet with the
+ * wrong destination address
+ * Also "is multicast" and "matches multicast filter" flags can be used to
+ * discard non-matching multicast packets.
+ */
+static void
+efx_handle_rx_event(struct efx_channel *channel, const efx_qword_t *event)
+{
+ unsigned int rx_ev_desc_ptr, rx_ev_byte_cnt;
+ unsigned int rx_ev_hdr_type, rx_ev_mcast_pkt;
+ unsigned expected_ptr;
+ bool rx_ev_pkt_ok, discard = false, checksummed;
+ struct efx_rx_queue *rx_queue;
+ struct efx_nic *efx = channel->efx;
+
+ /* Basic packet information */
+ rx_ev_byte_cnt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_BYTE_CNT);
+ rx_ev_pkt_ok = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_PKT_OK);
+ rx_ev_hdr_type = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_HDR_TYPE);
+ WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_JUMBO_CONT));
+ WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_SOP) != 1);
+ WARN_ON(EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_Q_LABEL) !=
+ channel->channel);
+
+ rx_queue = &efx->rx_queue[channel->channel];
+
+ rx_ev_desc_ptr = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_DESC_PTR);
+ expected_ptr = rx_queue->removed_count & EFX_RXQ_MASK;
+ if (unlikely(rx_ev_desc_ptr != expected_ptr))
+ efx_handle_rx_bad_index(rx_queue, rx_ev_desc_ptr);
+
+ if (likely(rx_ev_pkt_ok)) {
+ /* If packet is marked as OK and packet type is TCP/IP or
+ * UDP/IP, then we can rely on the hardware checksum.
+ */
+ checksummed =
+ likely(efx->rx_checksum_enabled) &&
+ (rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP ||
+ rx_ev_hdr_type == FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP);
+ } else {
+ efx_handle_rx_not_ok(rx_queue, event, &rx_ev_pkt_ok, &discard);
+ checksummed = false;
+ }
+
+ /* Detect multicast packets that didn't match the filter */
+ rx_ev_mcast_pkt = EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_PKT);
+ if (rx_ev_mcast_pkt) {
+ unsigned int rx_ev_mcast_hash_match =
+ EFX_QWORD_FIELD(*event, FSF_AZ_RX_EV_MCAST_HASH_MATCH);
+
+ if (unlikely(!rx_ev_mcast_hash_match)) {
+ ++channel->n_rx_mcast_mismatch;
+ discard = true;
+ }
+ }
+
+ channel->irq_mod_score += 2;
+
+ /* Handle received packet */
+ efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt,
+ checksummed, discard);
+}
+
+/* Global events are basically PHY events */
+static void
+efx_handle_global_event(struct efx_channel *channel, efx_qword_t *event)
+{
+ struct efx_nic *efx = channel->efx;
+ bool handled = false;
+
+ if (EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_G_PHY0_INTR) ||
+ EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XG_PHY0_INTR) ||
+ EFX_QWORD_FIELD(*event, FSF_AB_GLB_EV_XFP_PHY0_INTR)) {
+ /* Ignored */
+ handled = true;
+ }
+
+ if ((efx_nic_rev(efx) >= EFX_REV_FALCON_B0) &&
+ EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_XG_MGT_INTR)) {
+ efx->xmac_poll_required = true;
+ handled = true;
+ }
+
+ if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1 ?
+ EFX_QWORD_FIELD(*event, FSF_AA_GLB_EV_RX_RECOVERY) :
+ EFX_QWORD_FIELD(*event, FSF_BB_GLB_EV_RX_RECOVERY)) {
+ EFX_ERR(efx, "channel %d seen global RX_RESET "
+ "event. Resetting.\n", channel->channel);
+
+ atomic_inc(&efx->rx_reset);
+ efx_schedule_reset(efx, EFX_WORKAROUND_6555(efx) ?
+ RESET_TYPE_RX_RECOVERY : RESET_TYPE_DISABLE);
+ handled = true;
+ }
+
+ if (!handled)
+ EFX_ERR(efx, "channel %d unknown global event "
+ EFX_QWORD_FMT "\n", channel->channel,
+ EFX_QWORD_VAL(*event));
+}
+
+static void
+efx_handle_driver_event(struct efx_channel *channel, efx_qword_t *event)
+{
+ struct efx_nic *efx = channel->efx;
+ unsigned int ev_sub_code;
+ unsigned int ev_sub_data;
+
+ ev_sub_code = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBCODE);
+ ev_sub_data = EFX_QWORD_FIELD(*event, FSF_AZ_DRIVER_EV_SUBDATA);
+
+ switch (ev_sub_code) {
+ case FSE_AZ_TX_DESCQ_FLS_DONE_EV:
+ EFX_TRACE(efx, "channel %d TXQ %d flushed\n",
+ channel->channel, ev_sub_data);
+ break;
+ case FSE_AZ_RX_DESCQ_FLS_DONE_EV:
+ EFX_TRACE(efx, "channel %d RXQ %d flushed\n",
+ channel->channel, ev_sub_data);
+ break;
+ case FSE_AZ_EVQ_INIT_DONE_EV:
+ EFX_LOG(efx, "channel %d EVQ %d initialised\n",
+ channel->channel, ev_sub_data);
+ break;
+ case FSE_AZ_SRM_UPD_DONE_EV:
+ EFX_TRACE(efx, "channel %d SRAM update done\n",
+ channel->channel);
+ break;
+ case FSE_AZ_WAKE_UP_EV:
+ EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n",
+ channel->channel, ev_sub_data);
+ break;
+ case FSE_AZ_TIMER_EV:
+ EFX_TRACE(efx, "channel %d RX queue %d timer expired\n",
+ channel->channel, ev_sub_data);
+ break;
+ case FSE_AA_RX_RECOVER_EV:
+ EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. "
+ "Resetting.\n", channel->channel);
+ atomic_inc(&efx->rx_reset);
+ efx_schedule_reset(efx,
+ EFX_WORKAROUND_6555(efx) ?
+ RESET_TYPE_RX_RECOVERY :
+ RESET_TYPE_DISABLE);
+ break;
+ case FSE_BZ_RX_DSC_ERROR_EV:
+ EFX_ERR(efx, "RX DMA Q %d reports descriptor fetch error."
+ " RX Q %d is disabled.\n", ev_sub_data, ev_sub_data);
+ efx_schedule_reset(efx, RESET_TYPE_RX_DESC_FETCH);
+ break;
+ case FSE_BZ_TX_DSC_ERROR_EV:
+ EFX_ERR(efx, "TX DMA Q %d reports descriptor fetch error."
+ " TX Q %d is disabled.\n", ev_sub_data, ev_sub_data);
+ efx_schedule_reset(efx, RESET_TYPE_TX_DESC_FETCH);
+ break;
+ default:
+ EFX_TRACE(efx, "channel %d unknown driver event code %d "
+ "data %04x\n", channel->channel, ev_sub_code,
+ ev_sub_data);
+ break;
+ }
+}
+
+int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota)
+{
+ unsigned int read_ptr;
+ efx_qword_t event, *p_event;
+ int ev_code;
+ int rx_packets = 0;
+
+ read_ptr = channel->eventq_read_ptr;
+
+ do {
+ p_event = efx_event(channel, read_ptr);
+ event = *p_event;
+
+ if (!efx_event_present(&event))
+ /* End of events */
+ break;
+
+ EFX_TRACE(channel->efx, "channel %d event is "EFX_QWORD_FMT"\n",
+ channel->channel, EFX_QWORD_VAL(event));
+
+ /* Clear this event by marking it all ones */
+ EFX_SET_QWORD(*p_event);
+
+ ev_code = EFX_QWORD_FIELD(event, FSF_AZ_EV_CODE);
+
+ switch (ev_code) {
+ case FSE_AZ_EV_CODE_RX_EV:
+ efx_handle_rx_event(channel, &event);
+ ++rx_packets;
+ break;
+ case FSE_AZ_EV_CODE_TX_EV:
+ efx_handle_tx_event(channel, &event);
+ break;
+ case FSE_AZ_EV_CODE_DRV_GEN_EV:
+ channel->eventq_magic = EFX_QWORD_FIELD(
+ event, FSF_AZ_DRV_GEN_EV_MAGIC);
+ EFX_LOG(channel->efx, "channel %d received generated "
+ "event "EFX_QWORD_FMT"\n", channel->channel,
+ EFX_QWORD_VAL(event));
+ break;
+ case FSE_AZ_EV_CODE_GLOBAL_EV:
+ efx_handle_global_event(channel, &event);
+ break;
+ case FSE_AZ_EV_CODE_DRIVER_EV:
+ efx_handle_driver_event(channel, &event);
+ break;
+ case FSE_CZ_EV_CODE_MCDI_EV:
+ efx_mcdi_process_event(channel, &event);
+ break;
+ default:
+ EFX_ERR(channel->efx, "channel %d unknown event type %d"
+ " (data " EFX_QWORD_FMT ")\n", channel->channel,
+ ev_code, EFX_QWORD_VAL(event));
+ }
+
+ /* Increment read pointer */
+ read_ptr = (read_ptr + 1) & EFX_EVQ_MASK;
+
+ } while (rx_packets < rx_quota);
+
+ channel->eventq_read_ptr = read_ptr;
+ return rx_packets;
+}
+
+
+/* Allocate buffer table entries for event queue */
+int efx_nic_probe_eventq(struct efx_channel *channel)
+{
+ struct efx_nic *efx = channel->efx;
+ BUILD_BUG_ON(EFX_EVQ_SIZE < 512 || EFX_EVQ_SIZE > 32768 ||
+ EFX_EVQ_SIZE & EFX_EVQ_MASK);
+ return efx_alloc_special_buffer(efx, &channel->eventq,
+ EFX_EVQ_SIZE * sizeof(efx_qword_t));
+}
+
+void efx_nic_init_eventq(struct efx_channel *channel)
+{
+ efx_oword_t reg;
+ struct efx_nic *efx = channel->efx;
+
+ EFX_LOG(efx, "channel %d event queue in special buffers %d-%d\n",
+ channel->channel, channel->eventq.index,
+ channel->eventq.index + channel->eventq.entries - 1);
+
+ if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) {
+ EFX_POPULATE_OWORD_3(reg,
+ FRF_CZ_TIMER_Q_EN, 1,
+ FRF_CZ_HOST_NOTIFY_MODE, 0,
+ FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS);
+ efx_writeo_table(efx, &reg, FR_BZ_TIMER_TBL, channel->channel);
+ }
+
+ /* Pin event queue buffer */
+ efx_init_special_buffer(efx, &channel->eventq);
+
+ /* Fill event queue with all ones (i.e. empty events) */
+ memset(channel->eventq.addr, 0xff, channel->eventq.len);
+
+ /* Push event queue to card */
+ EFX_POPULATE_OWORD_3(reg,
+ FRF_AZ_EVQ_EN, 1,
+ FRF_AZ_EVQ_SIZE, __ffs(channel->eventq.entries),
+ FRF_AZ_EVQ_BUF_BASE_ID, channel->eventq.index);
+ efx_writeo_table(efx, &reg, efx->type->evq_ptr_tbl_base,
+ channel->channel);
+
+ efx->type->push_irq_moderation(channel);
+}
+
+void efx_nic_fini_eventq(struct efx_channel *channel)
+{
+ efx_oword_t reg;
+ struct efx_nic *efx = channel->efx;
+
+ /* Remove event queue from card */
+ EFX_ZERO_OWORD(reg);
+ efx_writeo_table(efx, &reg, efx->type->evq_ptr_tbl_base,
+ channel->channel);
+ if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
+ efx_writeo_table(efx, &reg, FR_BZ_TIMER_TBL, channel->channel);
+
+ /* Unpin event queue */
+ efx_fini_special_buffer(efx, &channel->eventq);
+}
+
+/* Free buffers backing event queue */
+void efx_nic_remove_eventq(struct efx_channel *channel)
+{
+ efx_free_special_buffer(channel->efx, &channel->eventq);
+}
+
+
+/* Generates a test event on the event queue. A subsequent call to
+ * process_eventq() should pick up the event and place the value of
+ * "magic" into channel->eventq_magic;
+ */
+void efx_nic_generate_test_event(struct efx_channel *channel, unsigned int magic)
+{
+ efx_qword_t test_event;
+
+ EFX_POPULATE_QWORD_2(test_event, FSF_AZ_EV_CODE,
+ FSE_AZ_EV_CODE_DRV_GEN_EV,
+ FSF_AZ_DRV_GEN_EV_MAGIC, magic);
+ efx_generate_event(channel, &test_event);
+}
+
+/**************************************************************************
+ *
+ * Flush handling
+ *
+ **************************************************************************/
+
+
+static void efx_poll_flush_events(struct efx_nic *efx)
+{
+ struct efx_channel *channel = &efx->channel[0];
+ struct efx_tx_queue *tx_queue;
+ struct efx_rx_queue *rx_queue;
+ unsigned int read_ptr = channel->eventq_read_ptr;
+ unsigned int end_ptr = (read_ptr - 1) & EFX_EVQ_MASK;
+
+ do {
+ efx_qword_t *event = efx_event(channel, read_ptr);
+ int ev_code, ev_sub_code, ev_queue;
+ bool ev_failed;
+
+ if (!efx_event_present(event))
+ break;
+
+ ev_code = EFX_QWORD_FIELD(*event, FSF_AZ_EV_CODE);
+ ev_sub_code = EFX_QWORD_FIELD(*event,
+ FSF_AZ_DRIVER_EV_SUBCODE);
+ if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV &&
+ ev_sub_code == FSE_AZ_TX_DESCQ_FLS_DONE_EV) {
+ ev_queue = EFX_QWORD_FIELD(*event,
+ FSF_AZ_DRIVER_EV_SUBDATA);
+ if (ev_queue < EFX_TX_QUEUE_COUNT) {
+ tx_queue = efx->tx_queue + ev_queue;
+ tx_queue->flushed = FLUSH_DONE;
+ }
+ } else if (ev_code == FSE_AZ_EV_CODE_DRIVER_EV &&
+ ev_sub_code == FSE_AZ_RX_DESCQ_FLS_DONE_EV) {
+ ev_queue = EFX_QWORD_FIELD(
+ *event, FSF_AZ_DRIVER_EV_RX_DESCQ_ID);
+ ev_failed = EFX_QWORD_FIELD(
+ *event, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL);
+ if (ev_queue < efx->n_rx_queues) {
+ rx_queue = efx->rx_queue + ev_queue;
+ rx_queue->flushed =
+ ev_failed ? FLUSH_FAILED : FLUSH_DONE;
+ }
+ }
+
+ /* We're about to destroy the queue anyway, so
+ * it's ok to throw away every non-flush event */
+ EFX_SET_QWORD(*event);
+
+ read_ptr = (read_ptr + 1) & EFX_EVQ_MASK;
+ } while (read_ptr != end_ptr);
+
+ channel->eventq_read_ptr = read_ptr;
+}
+
+/* Handle tx and rx flushes at the same time, since they run in
+ * parallel in the hardware and there's no reason for us to
+ * serialise them */
+int efx_nic_flush_queues(struct efx_nic *efx)
+{
+ struct efx_rx_queue *rx_queue;
+ struct efx_tx_queue *tx_queue;
+ int i, tx_pending, rx_pending;
+
+ /* If necessary prepare the hardware for flushing */
+ efx->type->prepare_flush(efx);
+
+ /* Flush all tx queues in parallel */
+ efx_for_each_tx_queue(tx_queue, efx)
+ efx_flush_tx_queue(tx_queue);
+
+ /* The hardware supports four concurrent rx flushes, each of which may
+ * need to be retried if there is an outstanding descriptor fetch */
+ for (i = 0; i < EFX_FLUSH_POLL_COUNT; ++i) {
+ rx_pending = tx_pending = 0;
+ efx_for_each_rx_queue(rx_queue, efx) {
+ if (rx_queue->flushed == FLUSH_PENDING)
+ ++rx_pending;
+ }
+ efx_for_each_rx_queue(rx_queue, efx) {
+ if (rx_pending == EFX_RX_FLUSH_COUNT)
+ break;
+ if (rx_queue->flushed == FLUSH_FAILED ||
+ rx_queue->flushed == FLUSH_NONE) {
+ efx_flush_rx_queue(rx_queue);
+ ++rx_pending;
+ }
+ }
+ efx_for_each_tx_queue(tx_queue, efx) {
+ if (tx_queue->flushed != FLUSH_DONE)
+ ++tx_pending;
+ }
+
+ if (rx_pending == 0 && tx_pending == 0)
+ return 0;
+
+ msleep(EFX_FLUSH_INTERVAL);
+ efx_poll_flush_events(efx);
+ }
+
+ /* Mark the queues as all flushed. We're going to return failure
+ * leading to a reset, or fake up success anyway */
+ efx_for_each_tx_queue(tx_queue, efx) {
+ if (tx_queue->flushed != FLUSH_DONE)
+ EFX_ERR(efx, "tx queue %d flush command timed out\n",
+ tx_queue->queue);
+ tx_queue->flushed = FLUSH_DONE;
+ }
+ efx_for_each_rx_queue(rx_queue, efx) {
+ if (rx_queue->flushed != FLUSH_DONE)
+ EFX_ERR(efx, "rx queue %d flush command timed out\n",
+ rx_queue->queue);
+ rx_queue->flushed = FLUSH_DONE;
+ }
+
+ if (EFX_WORKAROUND_7803(efx))
+ return 0;
+
+ return -ETIMEDOUT;
+}
+
+/**************************************************************************
+ *
+ * Hardware interrupts
+ * The hardware interrupt handler does very little work; all the event
+ * queue processing is carried out by per-channel tasklets.
+ *
+ **************************************************************************/
+
+/* Enable/disable/generate interrupts */
+static inline void efx_nic_interrupts(struct efx_nic *efx,
+ bool enabled, bool force)
+{
+ efx_oword_t int_en_reg_ker;
+ unsigned int level = 0;
+
+ if (EFX_WORKAROUND_17213(efx) && !EFX_INT_MODE_USE_MSI(efx))
+ /* Set the level always even if we're generating a test
+ * interrupt, because our legacy interrupt handler is safe */
+ level = 0x1f;
+
+ EFX_POPULATE_OWORD_3(int_en_reg_ker,
+ FRF_AZ_KER_INT_LEVE_SEL, level,
+ FRF_AZ_KER_INT_KER, force,
+ FRF_AZ_DRV_INT_EN_KER, enabled);
+ efx_writeo(efx, &int_en_reg_ker, FR_AZ_INT_EN_KER);
+}
+
+void efx_nic_enable_interrupts(struct efx_nic *efx)
+{
+ struct efx_channel *channel;
+
+ EFX_ZERO_OWORD(*((efx_oword_t *) efx->irq_status.addr));
+ wmb(); /* Ensure interrupt vector is clear before interrupts enabled */
+
+ /* Enable interrupts */
+ efx_nic_interrupts(efx, true, false);
+
+ /* Force processing of all the channels to get the EVQ RPTRs up to
+ date */
+ efx_for_each_channel(channel, efx)
+ efx_schedule_channel(channel);
+}
+
+void efx_nic_disable_interrupts(struct efx_nic *efx)
+{
+ /* Disable interrupts */
+ efx_nic_interrupts(efx, false, false);
+}
+
+/* Generate a test interrupt
+ * Interrupt must already have been enabled, otherwise nasty things
+ * may happen.
+ */
+void efx_nic_generate_interrupt(struct efx_nic *efx)
+{
+ efx_nic_interrupts(efx, true, true);
+}
+
+/* Process a fatal interrupt
+ * Disable bus mastering ASAP and schedule a reset
+ */
+irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx)
+{
+ struct falcon_nic_data *nic_data = efx->nic_data;
+ efx_oword_t *int_ker = efx->irq_status.addr;
+ efx_oword_t fatal_intr;
+ int error, mem_perr;
+
+ efx_reado(efx, &fatal_intr, FR_AZ_FATAL_INTR_KER);
+ error = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_FATAL_INTR);
+
+ EFX_ERR(efx, "SYSTEM ERROR " EFX_OWORD_FMT " status "
+ EFX_OWORD_FMT ": %s\n", EFX_OWORD_VAL(*int_ker),
+ EFX_OWORD_VAL(fatal_intr),
+ error ? "disabling bus mastering" : "no recognised error");
+ if (error == 0)
+ goto out;
+
+ /* If this is a memory parity error dump which blocks are offending */
+ mem_perr = EFX_OWORD_FIELD(fatal_intr, FRF_AZ_MEM_PERR_INT_KER);
+ if (mem_perr) {
+ efx_oword_t reg;
+ efx_reado(efx, &reg, FR_AZ_MEM_STAT);
+ EFX_ERR(efx, "SYSTEM ERROR: memory parity error "
+ EFX_OWORD_FMT "\n", EFX_OWORD_VAL(reg));
+ }
+
+ /* Disable both devices */
+ pci_clear_master(efx->pci_dev);
+ if (efx_nic_is_dual_func(efx))
+ pci_clear_master(nic_data->pci_dev2);
+ efx_nic_disable_interrupts(efx);
+
+ /* Count errors and reset or disable the NIC accordingly */
+ if (efx->int_error_count == 0 ||
+ time_after(jiffies, efx->int_error_expire)) {
+ efx->int_error_count = 0;
+ efx->int_error_expire =
+ jiffies + EFX_INT_ERROR_EXPIRE * HZ;
+ }
+ if (++efx->int_error_count < EFX_MAX_INT_ERRORS) {
+ EFX_ERR(efx, "SYSTEM ERROR - reset scheduled\n");
+ efx_schedule_reset(efx, RESET_TYPE_INT_ERROR);
+ } else {
+ EFX_ERR(efx, "SYSTEM ERROR - max number of errors seen."
+ "NIC will be disabled\n");
+ efx_schedule_reset(efx, RESET_TYPE_DISABLE);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+/* Handle a legacy interrupt
+ * Acknowledges the interrupt and schedule event queue processing.
+ */
+static irqreturn_t efx_legacy_interrupt(int irq, void *dev_id)
+{
+ struct efx_nic *efx = dev_id;
+ efx_oword_t *int_ker = efx->irq_status.addr;
+ irqreturn_t result = IRQ_NONE;
+ struct efx_channel *channel;
+ efx_dword_t reg;
+ u32 queues;
+ int syserr;
+
+ /* Read the ISR which also ACKs the interrupts */
+ efx_readd(efx, &reg, FR_BZ_INT_ISR0);
+ queues = EFX_EXTRACT_DWORD(reg, 0, 31);
+
+ /* Check to see if we have a serious error condition */
+ syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT);
+ if (unlikely(syserr))
+ return efx_nic_fatal_interrupt(efx);
+
+ if (queues != 0) {
+ if (EFX_WORKAROUND_15783(efx))
+ efx->irq_zero_count = 0;
+
+ /* Schedule processing of any interrupting queues */
+ efx_for_each_channel(channel, efx) {
+ if (queues & 1)
+ efx_schedule_channel(channel);
+ queues >>= 1;
+ }
+ result = IRQ_HANDLED;
+
+ } else if (EFX_WORKAROUND_15783(efx) &&
+ efx->irq_zero_count++ == 0) {
+ efx_qword_t *event;
+
+ /* Ensure we rearm all event queues */
+ efx_for_each_channel(channel, efx) {
+ event = efx_event(channel, channel->eventq_read_ptr);
+ if (efx_event_present(event))
+ efx_schedule_channel(channel);
+ }
+
+ result = IRQ_HANDLED;
+ }
+
+ if (result == IRQ_HANDLED) {
+ efx->last_irq_cpu = raw_smp_processor_id();
+ EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_DWORD_FMT "\n",
+ irq, raw_smp_processor_id(), EFX_DWORD_VAL(reg));
+ }
+
+ return result;
+}
+
+/* Handle an MSI interrupt
+ *
+ * Handle an MSI hardware interrupt. This routine schedules event
+ * queue processing. No interrupt acknowledgement cycle is necessary.
+ * Also, we never need to check that the interrupt is for us, since
+ * MSI interrupts cannot be shared.
+ */
+static irqreturn_t efx_msi_interrupt(int irq, void *dev_id)
+{
+ struct efx_channel *channel = dev_id;
+ struct efx_nic *efx = channel->efx;
+ efx_oword_t *int_ker = efx->irq_status.addr;
+ int syserr;
+
+ efx->last_irq_cpu = raw_smp_processor_id();
+ EFX_TRACE(efx, "IRQ %d on CPU %d status " EFX_OWORD_FMT "\n",
+ irq, raw_smp_processor_id(), EFX_OWORD_VAL(*int_ker));
+
+ /* Check to see if we have a serious error condition */
+ syserr = EFX_OWORD_FIELD(*int_ker, FSF_AZ_NET_IVEC_FATAL_INT);
+ if (unlikely(syserr))
+ return efx_nic_fatal_interrupt(efx);
+
+ /* Schedule processing of the channel */
+ efx_schedule_channel(channel);
+
+ return IRQ_HANDLED;
+}
+
+
+/* Setup RSS indirection table.
+ * This maps from the hash value of the packet to RXQ
+ */
+static void efx_setup_rss_indir_table(struct efx_nic *efx)
+{
+ int i = 0;
+ unsigned long offset;
+ efx_dword_t dword;
+
+ if (efx_nic_rev(efx) < EFX_REV_FALCON_B0)
+ return;
+
+ for (offset = FR_BZ_RX_INDIRECTION_TBL;
+ offset < FR_BZ_RX_INDIRECTION_TBL + 0x800;
+ offset += 0x10) {
+ EFX_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE,
+ i % efx->n_rx_queues);
+ efx_writed(efx, &dword, offset);
+ i++;
+ }
+}
+
+/* Hook interrupt handler(s)
+ * Try MSI and then legacy interrupts.
+ */
+int efx_nic_init_interrupt(struct efx_nic *efx)
+{
+ struct efx_channel *channel;
+ int rc;
+
+ if (!EFX_INT_MODE_USE_MSI(efx)) {
+ irq_handler_t handler;
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0)
+ handler = efx_legacy_interrupt;
+ else
+ handler = falcon_legacy_interrupt_a1;
+
+ rc = request_irq(efx->legacy_irq, handler, IRQF_SHARED,
+ efx->name, efx);
+ if (rc) {
+ EFX_ERR(efx, "failed to hook legacy IRQ %d\n",
+ efx->pci_dev->irq);
+ goto fail1;
+ }
+ return 0;
+ }
+
+ /* Hook MSI or MSI-X interrupt */
+ efx_for_each_channel(channel, efx) {
+ rc = request_irq(channel->irq, efx_msi_interrupt,
+ IRQF_PROBE_SHARED, /* Not shared */
+ channel->name, channel);
+ if (rc) {
+ EFX_ERR(efx, "failed to hook IRQ %d\n", channel->irq);
+ goto fail2;
+ }
+ }
+
+ return 0;
+
+ fail2:
+ efx_for_each_channel(channel, efx)
+ free_irq(channel->irq, channel);
+ fail1:
+ return rc;
+}
+
+void efx_nic_fini_interrupt(struct efx_nic *efx)
+{
+ struct efx_channel *channel;
+ efx_oword_t reg;
+
+ /* Disable MSI/MSI-X interrupts */
+ efx_for_each_channel(channel, efx) {
+ if (channel->irq)
+ free_irq(channel->irq, channel);
+ }
+
+ /* ACK legacy interrupt */
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0)
+ efx_reado(efx, &reg, FR_BZ_INT_ISR0);
+ else
+ falcon_irq_ack_a1(efx);
+
+ /* Disable legacy interrupt */
+ if (efx->legacy_irq)
+ free_irq(efx->legacy_irq, efx);
+}
+
+u32 efx_nic_fpga_ver(struct efx_nic *efx)
+{
+ efx_oword_t altera_build;
+ efx_reado(efx, &altera_build, FR_AZ_ALTERA_BUILD);
+ return EFX_OWORD_FIELD(altera_build, FRF_AZ_ALTERA_BUILD_VER);
+}
+
+void efx_nic_init_common(struct efx_nic *efx)
+{
+ efx_oword_t temp;
+
+ /* Set positions of descriptor caches in SRAM. */
+ EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_TX_DC_BASE_ADR,
+ efx->type->tx_dc_base / 8);
+ efx_writeo(efx, &temp, FR_AZ_SRM_TX_DC_CFG);
+ EFX_POPULATE_OWORD_1(temp, FRF_AZ_SRM_RX_DC_BASE_ADR,
+ efx->type->rx_dc_base / 8);
+ efx_writeo(efx, &temp, FR_AZ_SRM_RX_DC_CFG);
+
+ /* Set TX descriptor cache size. */
+ BUILD_BUG_ON(TX_DC_ENTRIES != (8 << TX_DC_ENTRIES_ORDER));
+ EFX_POPULATE_OWORD_1(temp, FRF_AZ_TX_DC_SIZE, TX_DC_ENTRIES_ORDER);
+ efx_writeo(efx, &temp, FR_AZ_TX_DC_CFG);
+
+ /* Set RX descriptor cache size. Set low watermark to size-8, as
+ * this allows most efficient prefetching.
+ */
+ BUILD_BUG_ON(RX_DC_ENTRIES != (8 << RX_DC_ENTRIES_ORDER));
+ EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_SIZE, RX_DC_ENTRIES_ORDER);
+ efx_writeo(efx, &temp, FR_AZ_RX_DC_CFG);
+ EFX_POPULATE_OWORD_1(temp, FRF_AZ_RX_DC_PF_LWM, RX_DC_ENTRIES - 8);
+ efx_writeo(efx, &temp, FR_AZ_RX_DC_PF_WM);
+
+ /* Program INT_KER address */
+ EFX_POPULATE_OWORD_2(temp,
+ FRF_AZ_NORM_INT_VEC_DIS_KER,
+ EFX_INT_MODE_USE_MSI(efx),
+ FRF_AZ_INT_ADR_KER, efx->irq_status.dma_addr);
+ efx_writeo(efx, &temp, FR_AZ_INT_ADR_KER);
+
+ /* Enable all the genuinely fatal interrupts. (They are still
+ * masked by the overall interrupt mask, controlled by
+ * falcon_interrupts()).
+ *
+ * Note: All other fatal interrupts are enabled
+ */
+ EFX_POPULATE_OWORD_3(temp,
+ FRF_AZ_ILL_ADR_INT_KER_EN, 1,
+ FRF_AZ_RBUF_OWN_INT_KER_EN, 1,
+ FRF_AZ_TBUF_OWN_INT_KER_EN, 1);
+ EFX_INVERT_OWORD(temp);
+ efx_writeo(efx, &temp, FR_AZ_FATAL_INTR_KER);
+
+ efx_setup_rss_indir_table(efx);
+
+ /* Disable the ugly timer-based TX DMA backoff and allow TX DMA to be
+ * controlled by the RX FIFO fill level. Set arbitration to one pkt/Q.
+ */
+ efx_reado(efx, &temp, FR_AZ_TX_RESERVED);
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe);
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1);
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1);
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 0);
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1);
+ /* Enable SW_EV to inherit in char driver - assume harmless here */
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1);
+ /* Prefetch threshold 2 => fetch when descriptor cache half empty */
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PREF_THRESHOLD, 2);
+ /* Squash TX of packets of 16 bytes or less */
+ if (efx_nic_rev(efx) >= EFX_REV_FALCON_B0)
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1);
+ efx_writeo(efx, &temp, FR_AZ_TX_RESERVED);
+}
diff --git a/drivers/net/sfc/nic.h b/drivers/net/sfc/nic.h
new file mode 100644
index 00000000000..9351c0331a4
--- /dev/null
+++ b/drivers/net/sfc/nic.h
@@ -0,0 +1,261 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_NIC_H
+#define EFX_NIC_H
+
+#include <linux/i2c-algo-bit.h>
+#include "net_driver.h"
+#include "efx.h"
+#include "mcdi.h"
+
+/*
+ * Falcon hardware control
+ */
+
+enum {
+ EFX_REV_FALCON_A0 = 0,
+ EFX_REV_FALCON_A1 = 1,
+ EFX_REV_FALCON_B0 = 2,
+ EFX_REV_SIENA_A0 = 3,
+};
+
+static inline int efx_nic_rev(struct efx_nic *efx)
+{
+ return efx->type->revision;
+}
+
+extern u32 efx_nic_fpga_ver(struct efx_nic *efx);
+
+static inline bool efx_nic_has_mc(struct efx_nic *efx)
+{
+ return efx_nic_rev(efx) >= EFX_REV_SIENA_A0;
+}
+/* NIC has two interlinked PCI functions for the same port. */
+static inline bool efx_nic_is_dual_func(struct efx_nic *efx)
+{
+ return efx_nic_rev(efx) < EFX_REV_FALCON_B0;
+}
+
+enum {
+ PHY_TYPE_NONE = 0,
+ PHY_TYPE_TXC43128 = 1,
+ PHY_TYPE_88E1111 = 2,
+ PHY_TYPE_SFX7101 = 3,
+ PHY_TYPE_QT2022C2 = 4,
+ PHY_TYPE_PM8358 = 6,
+ PHY_TYPE_SFT9001A = 8,
+ PHY_TYPE_QT2025C = 9,
+ PHY_TYPE_SFT9001B = 10,
+};
+
+#define FALCON_XMAC_LOOPBACKS \
+ ((1 << LOOPBACK_XGMII) | \
+ (1 << LOOPBACK_XGXS) | \
+ (1 << LOOPBACK_XAUI))
+
+#define FALCON_GMAC_LOOPBACKS \
+ (1 << LOOPBACK_GMAC)
+
+/**
+ * struct falcon_board_type - board operations and type information
+ * @id: Board type id, as found in NVRAM
+ * @ref_model: Model number of Solarflare reference design
+ * @gen_type: Generic board type description
+ * @init: Allocate resources and initialise peripheral hardware
+ * @init_phy: Do board-specific PHY initialisation
+ * @fini: Shut down hardware and free resources
+ * @set_id_led: Set state of identifying LED or revert to automatic function
+ * @monitor: Board-specific health check function
+ */
+struct falcon_board_type {
+ u8 id;
+ const char *ref_model;
+ const char *gen_type;
+ int (*init) (struct efx_nic *nic);
+ void (*init_phy) (struct efx_nic *efx);
+ void (*fini) (struct efx_nic *nic);
+ void (*set_id_led) (struct efx_nic *efx, enum efx_led_mode mode);
+ int (*monitor) (struct efx_nic *nic);
+};
+
+/**
+ * struct falcon_board - board information
+ * @type: Type of board
+ * @major: Major rev. ('A', 'B' ...)
+ * @minor: Minor rev. (0, 1, ...)
+ * @i2c_adap: I2C adapter for on-board peripherals
+ * @i2c_data: Data for bit-banging algorithm
+ * @hwmon_client: I2C client for hardware monitor
+ * @ioexp_client: I2C client for power/port control
+ */
+struct falcon_board {
+ const struct falcon_board_type *type;
+ int major;
+ int minor;
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_data;
+ struct i2c_client *hwmon_client, *ioexp_client;
+};
+
+/**
+ * struct falcon_nic_data - Falcon NIC state
+ * @pci_dev2: Secondary function of Falcon A
+ * @board: Board state and functions
+ * @stats_disable_count: Nest count for disabling statistics fetches
+ * @stats_pending: Is there a pending DMA of MAC statistics.
+ * @stats_timer: A timer for regularly fetching MAC statistics.
+ * @stats_dma_done: Pointer to the flag which indicates DMA completion.
+ */
+struct falcon_nic_data {
+ struct pci_dev *pci_dev2;
+ struct falcon_board board;
+ unsigned int stats_disable_count;
+ bool stats_pending;
+ struct timer_list stats_timer;
+ u32 *stats_dma_done;
+};
+
+static inline struct falcon_board *falcon_board(struct efx_nic *efx)
+{
+ struct falcon_nic_data *data = efx->nic_data;
+ return &data->board;
+}
+
+/**
+ * struct siena_nic_data - Siena NIC state
+ * @fw_version: Management controller firmware version
+ * @fw_build: Firmware build number
+ * @mcdi: Management-Controller-to-Driver Interface
+ * @wol_filter_id: Wake-on-LAN packet filter id
+ */
+struct siena_nic_data {
+ u64 fw_version;
+ u32 fw_build;
+ struct efx_mcdi_iface mcdi;
+ int wol_filter_id;
+};
+
+extern void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len);
+
+extern struct efx_nic_type falcon_a1_nic_type;
+extern struct efx_nic_type falcon_b0_nic_type;
+extern struct efx_nic_type siena_a0_nic_type;
+
+/**************************************************************************
+ *
+ * Externs
+ *
+ **************************************************************************
+ */
+
+extern void falcon_probe_board(struct efx_nic *efx, u16 revision_info);
+
+/* TX data path */
+extern int efx_nic_probe_tx(struct efx_tx_queue *tx_queue);
+extern void efx_nic_init_tx(struct efx_tx_queue *tx_queue);
+extern void efx_nic_fini_tx(struct efx_tx_queue *tx_queue);
+extern void efx_nic_remove_tx(struct efx_tx_queue *tx_queue);
+extern void efx_nic_push_buffers(struct efx_tx_queue *tx_queue);
+
+/* RX data path */
+extern int efx_nic_probe_rx(struct efx_rx_queue *rx_queue);
+extern void efx_nic_init_rx(struct efx_rx_queue *rx_queue);
+extern void efx_nic_fini_rx(struct efx_rx_queue *rx_queue);
+extern void efx_nic_remove_rx(struct efx_rx_queue *rx_queue);
+extern void efx_nic_notify_rx_desc(struct efx_rx_queue *rx_queue);
+
+/* Event data path */
+extern int efx_nic_probe_eventq(struct efx_channel *channel);
+extern void efx_nic_init_eventq(struct efx_channel *channel);
+extern void efx_nic_fini_eventq(struct efx_channel *channel);
+extern void efx_nic_remove_eventq(struct efx_channel *channel);
+extern int efx_nic_process_eventq(struct efx_channel *channel, int rx_quota);
+extern void efx_nic_eventq_read_ack(struct efx_channel *channel);
+
+/* MAC/PHY */
+extern void falcon_drain_tx_fifo(struct efx_nic *efx);
+extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx);
+extern int efx_nic_rx_xoff_thresh, efx_nic_rx_xon_thresh;
+
+/* Interrupts and test events */
+extern int efx_nic_init_interrupt(struct efx_nic *efx);
+extern void efx_nic_enable_interrupts(struct efx_nic *efx);
+extern void efx_nic_generate_test_event(struct efx_channel *channel,
+ unsigned int magic);
+extern void efx_nic_generate_interrupt(struct efx_nic *efx);
+extern void efx_nic_disable_interrupts(struct efx_nic *efx);
+extern void efx_nic_fini_interrupt(struct efx_nic *efx);
+extern irqreturn_t efx_nic_fatal_interrupt(struct efx_nic *efx);
+extern irqreturn_t falcon_legacy_interrupt_a1(int irq, void *dev_id);
+extern void falcon_irq_ack_a1(struct efx_nic *efx);
+
+#define EFX_IRQ_MOD_RESOLUTION 5
+
+/* Global Resources */
+extern int efx_nic_flush_queues(struct efx_nic *efx);
+extern void falcon_start_nic_stats(struct efx_nic *efx);
+extern void falcon_stop_nic_stats(struct efx_nic *efx);
+extern int falcon_reset_xaui(struct efx_nic *efx);
+extern void efx_nic_init_common(struct efx_nic *efx);
+
+int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer,
+ unsigned int len);
+void efx_nic_free_buffer(struct efx_nic *efx, struct efx_buffer *buffer);
+
+/* Tests */
+struct efx_nic_register_test {
+ unsigned address;
+ efx_oword_t mask;
+};
+extern int efx_nic_test_registers(struct efx_nic *efx,
+ const struct efx_nic_register_test *regs,
+ size_t n_regs);
+
+/**************************************************************************
+ *
+ * Falcon MAC stats
+ *
+ **************************************************************************
+ */
+
+#define FALCON_STAT_OFFSET(falcon_stat) EFX_VAL(falcon_stat, offset)
+#define FALCON_STAT_WIDTH(falcon_stat) EFX_VAL(falcon_stat, WIDTH)
+
+/* Retrieve statistic from statistics block */
+#define FALCON_STAT(efx, falcon_stat, efx_stat) do { \
+ if (FALCON_STAT_WIDTH(falcon_stat) == 16) \
+ (efx)->mac_stats.efx_stat += le16_to_cpu( \
+ *((__force __le16 *) \
+ (efx->stats_buffer.addr + \
+ FALCON_STAT_OFFSET(falcon_stat)))); \
+ else if (FALCON_STAT_WIDTH(falcon_stat) == 32) \
+ (efx)->mac_stats.efx_stat += le32_to_cpu( \
+ *((__force __le32 *) \
+ (efx->stats_buffer.addr + \
+ FALCON_STAT_OFFSET(falcon_stat)))); \
+ else \
+ (efx)->mac_stats.efx_stat += le64_to_cpu( \
+ *((__force __le64 *) \
+ (efx->stats_buffer.addr + \
+ FALCON_STAT_OFFSET(falcon_stat)))); \
+ } while (0)
+
+#define FALCON_MAC_STATS_SIZE 0x100
+
+#define MAC_DATA_LBN 0
+#define MAC_DATA_WIDTH 32
+
+extern void efx_nic_generate_event(struct efx_channel *channel,
+ efx_qword_t *event);
+
+extern void falcon_poll_xmac(struct efx_nic *efx);
+
+#endif /* EFX_NIC_H */
diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h
index c1cff9c0c17..5bc26137257 100644
--- a/drivers/net/sfc/phy.h
+++ b/drivers/net/sfc/phy.h
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007-2008 Solarflare Communications Inc.
+ * Copyright 2007-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -16,16 +16,16 @@
extern struct efx_phy_operations falcon_sfx7101_phy_ops;
extern struct efx_phy_operations falcon_sft9001_phy_ops;
-extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink);
+extern void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
/* Wait for the PHY to boot. Return 0 on success, -EINVAL if the PHY failed
* to boot due to corrupt flash, or some other negative error code. */
extern int sft9001_wait_boot(struct efx_nic *efx);
/****************************************************************************
- * AMCC/Quake QT20xx PHYs
+ * AMCC/Quake QT202x PHYs
*/
-extern struct efx_phy_operations falcon_xfp_phy_ops;
+extern struct efx_phy_operations falcon_qt202x_phy_ops;
/* These PHYs provide various H/W control states for LEDs */
#define QUAKE_LED_LINK_INVAL (0)
@@ -39,6 +39,23 @@ extern struct efx_phy_operations falcon_xfp_phy_ops;
#define QUAKE_LED_TXLINK (0)
#define QUAKE_LED_RXLINK (8)
-extern void xfp_set_led(struct efx_nic *p, int led, int state);
+extern void falcon_qt202x_set_led(struct efx_nic *p, int led, int state);
+
+/****************************************************************************
+ * Siena managed PHYs
+ */
+extern struct efx_phy_operations efx_mcdi_phy_ops;
+
+extern int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus,
+ unsigned int prtad, unsigned int devad,
+ u16 addr, u16 *value_out, u32 *status_out);
+extern int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus,
+ unsigned int prtad, unsigned int devad,
+ u16 addr, u16 value, u32 *status_out);
+extern void efx_mcdi_phy_decode_link(struct efx_nic *efx,
+ struct efx_link_state *link_state,
+ u32 speed, u32 flags, u32 fcntl);
+extern int efx_mcdi_phy_reconfigure(struct efx_nic *efx);
+extern void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa);
#endif
diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/qt202x_phy.c
index e6b3d5eaddb..3800fc791b2 100644
--- a/drivers/net/sfc/xfp_phy.c
+++ b/drivers/net/sfc/qt202x_phy.c
@@ -1,14 +1,13 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
/*
- * Driver for SFP+ and XFP optical PHYs plus some support specific to the
- * AMCC QT20xx adapters; see www.amcc.com for details
+ * Driver for AMCC QT202x SFP+ and XFP adapters; see www.amcc.com for details
*/
#include <linux/timer.h>
@@ -16,15 +15,15 @@
#include "efx.h"
#include "mdio_10g.h"
#include "phy.h"
-#include "falcon.h"
+#include "nic.h"
-#define XFP_REQUIRED_DEVS (MDIO_DEVS_PCS | \
- MDIO_DEVS_PMAPMD | \
- MDIO_DEVS_PHYXS)
+#define QT202X_REQUIRED_DEVS (MDIO_DEVS_PCS | \
+ MDIO_DEVS_PMAPMD | \
+ MDIO_DEVS_PHYXS)
-#define XFP_LOOPBACKS ((1 << LOOPBACK_PCS) | \
- (1 << LOOPBACK_PMAPMD) | \
- (1 << LOOPBACK_NETWORK))
+#define QT202X_LOOPBACKS ((1 << LOOPBACK_PCS) | \
+ (1 << LOOPBACK_PMAPMD) | \
+ (1 << LOOPBACK_PHYXS_WS))
/****************************************************************************/
/* Quake-specific MDIO registers */
@@ -45,18 +44,18 @@
#define PCS_VEND1_REG 0xc000
#define PCS_VEND1_LBTXD_LBN 5
-void xfp_set_led(struct efx_nic *p, int led, int mode)
+void falcon_qt202x_set_led(struct efx_nic *p, int led, int mode)
{
int addr = MDIO_QUAKE_LED0_REG + led;
efx_mdio_write(p, MDIO_MMD_PMAPMD, addr, mode);
}
-struct xfp_phy_data {
+struct qt202x_phy_data {
enum efx_phy_mode phy_mode;
};
-#define XFP_MAX_RESET_TIME 500
-#define XFP_RESET_WAIT 10
+#define QT2022C2_MAX_RESET_TIME 500
+#define QT2022C2_RESET_WAIT 10
static int qt2025c_wait_reset(struct efx_nic *efx)
{
@@ -97,7 +96,7 @@ static int qt2025c_wait_reset(struct efx_nic *efx)
return 0;
}
-static int xfp_reset_phy(struct efx_nic *efx)
+static int qt202x_reset_phy(struct efx_nic *efx)
{
int rc;
@@ -111,8 +110,9 @@ static int xfp_reset_phy(struct efx_nic *efx)
/* Reset the PHYXS MMD. This is documented as doing
* a complete soft reset. */
rc = efx_mdio_reset_mmd(efx, MDIO_MMD_PHYXS,
- XFP_MAX_RESET_TIME / XFP_RESET_WAIT,
- XFP_RESET_WAIT);
+ QT2022C2_MAX_RESET_TIME /
+ QT2022C2_RESET_WAIT,
+ QT2022C2_RESET_WAIT);
if (rc < 0)
goto fail;
}
@@ -122,11 +122,11 @@ static int xfp_reset_phy(struct efx_nic *efx)
/* Check that all the MMDs we expect are present and responding. We
* expect faults on some if the link is down, but not on the PHY XS */
- rc = efx_mdio_check_mmds(efx, XFP_REQUIRED_DEVS, MDIO_DEVS_PHYXS);
+ rc = efx_mdio_check_mmds(efx, QT202X_REQUIRED_DEVS, MDIO_DEVS_PHYXS);
if (rc < 0)
goto fail;
- efx->board_info.init_leds(efx);
+ falcon_board(efx)->type->init_phy(efx);
return rc;
@@ -135,60 +135,60 @@ static int xfp_reset_phy(struct efx_nic *efx)
return rc;
}
-static int xfp_phy_init(struct efx_nic *efx)
+static int qt202x_phy_probe(struct efx_nic *efx)
{
- struct xfp_phy_data *phy_data;
- u32 devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS);
+ efx->mdio.mmds = QT202X_REQUIRED_DEVS;
+ efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
+ efx->loopback_modes = QT202X_LOOPBACKS | FALCON_XMAC_LOOPBACKS;
+ return 0;
+}
+
+static int qt202x_phy_init(struct efx_nic *efx)
+{
+ struct qt202x_phy_data *phy_data;
+ u32 devid;
int rc;
- phy_data = kzalloc(sizeof(struct xfp_phy_data), GFP_KERNEL);
+ rc = qt202x_reset_phy(efx);
+ if (rc) {
+ EFX_ERR(efx, "PHY init failed\n");
+ return rc;
+ }
+
+ phy_data = kzalloc(sizeof(struct qt202x_phy_data), GFP_KERNEL);
if (!phy_data)
return -ENOMEM;
efx->phy_data = phy_data;
+ devid = efx_mdio_read_id(efx, MDIO_MMD_PHYXS);
EFX_INFO(efx, "PHY ID reg %x (OUI %06x model %02x revision %x)\n",
devid, efx_mdio_id_oui(devid), efx_mdio_id_model(devid),
efx_mdio_id_rev(devid));
phy_data->phy_mode = efx->phy_mode;
-
- rc = xfp_reset_phy(efx);
-
- EFX_INFO(efx, "PHY init %s.\n",
- rc ? "failed" : "successful");
- if (rc < 0)
- goto fail;
-
return 0;
-
- fail:
- kfree(efx->phy_data);
- efx->phy_data = NULL;
- return rc;
}
-static void xfp_phy_clear_interrupt(struct efx_nic *efx)
+static int qt202x_link_ok(struct efx_nic *efx)
{
- /* Read to clear link status alarm */
- efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT);
+ return efx_mdio_links_ok(efx, QT202X_REQUIRED_DEVS);
}
-static int xfp_link_ok(struct efx_nic *efx)
+static bool qt202x_phy_poll(struct efx_nic *efx)
{
- return efx_mdio_links_ok(efx, XFP_REQUIRED_DEVS);
-}
+ bool was_up = efx->link_state.up;
-static void xfp_phy_poll(struct efx_nic *efx)
-{
- int link_up = xfp_link_ok(efx);
- /* Simulate a PHY event if link state has changed */
- if (link_up != efx->link_up)
- falcon_sim_phy_event(efx);
+ efx->link_state.up = qt202x_link_ok(efx);
+ efx->link_state.speed = 10000;
+ efx->link_state.fd = true;
+ efx->link_state.fc = efx->wanted_fc;
+
+ return efx->link_state.up != was_up;
}
-static void xfp_phy_reconfigure(struct efx_nic *efx)
+static int qt202x_phy_reconfigure(struct efx_nic *efx)
{
- struct xfp_phy_data *phy_data = efx->phy_data;
+ struct qt202x_phy_data *phy_data = efx->phy_data;
if (efx->phy_type == PHY_TYPE_QT2025C) {
/* There are several different register bits which can
@@ -207,7 +207,7 @@ static void xfp_phy_reconfigure(struct efx_nic *efx)
/* Reset the PHY when moving from tx off to tx on */
if (!(efx->phy_mode & PHY_MODE_TX_DISABLED) &&
(phy_data->phy_mode & PHY_MODE_TX_DISABLED))
- xfp_reset_phy(efx);
+ qt202x_reset_phy(efx);
efx_mdio_transmit_disable(efx);
}
@@ -215,36 +215,28 @@ static void xfp_phy_reconfigure(struct efx_nic *efx)
efx_mdio_phy_reconfigure(efx);
phy_data->phy_mode = efx->phy_mode;
- efx->link_up = xfp_link_ok(efx);
- efx->link_speed = 10000;
- efx->link_fd = true;
- efx->link_fc = efx->wanted_fc;
+
+ return 0;
}
-static void xfp_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
+static void qt202x_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
{
mdio45_ethtool_gset(&efx->mdio, ecmd);
}
-static void xfp_phy_fini(struct efx_nic *efx)
+static void qt202x_phy_fini(struct efx_nic *efx)
{
- /* Clobber the LED if it was blinking */
- efx->board_info.blink(efx, false);
-
/* Free the context block */
kfree(efx->phy_data);
efx->phy_data = NULL;
}
-struct efx_phy_operations falcon_xfp_phy_ops = {
- .macs = EFX_XMAC,
- .init = xfp_phy_init,
- .reconfigure = xfp_phy_reconfigure,
- .poll = xfp_phy_poll,
- .fini = xfp_phy_fini,
- .clear_interrupt = xfp_phy_clear_interrupt,
- .get_settings = xfp_phy_get_settings,
+struct efx_phy_operations falcon_qt202x_phy_ops = {
+ .probe = qt202x_phy_probe,
+ .init = qt202x_phy_init,
+ .reconfigure = qt202x_phy_reconfigure,
+ .poll = qt202x_phy_poll,
+ .fini = qt202x_phy_fini,
+ .get_settings = qt202x_phy_get_settings,
.set_settings = efx_mdio_set_settings,
- .mmds = XFP_REQUIRED_DEVS,
- .loopbacks = XFP_LOOPBACKS,
};
diff --git a/drivers/net/sfc/regs.h b/drivers/net/sfc/regs.h
new file mode 100644
index 00000000000..89d606fe924
--- /dev/null
+++ b/drivers/net/sfc/regs.h
@@ -0,0 +1,3168 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_REGS_H
+#define EFX_REGS_H
+
+/*
+ * Falcon hardware architecture definitions have a name prefix following
+ * the format:
+ *
+ * F<type>_<min-rev><max-rev>_
+ *
+ * The following <type> strings are used:
+ *
+ * MMIO register MC register Host memory structure
+ * -------------------------------------------------------------
+ * Address R MCR
+ * Bitfield RF MCRF SF
+ * Enumerator FE MCFE SE
+ *
+ * <min-rev> is the first revision to which the definition applies:
+ *
+ * A: Falcon A1 (SFC4000AB)
+ * B: Falcon B0 (SFC4000BA)
+ * C: Siena A0 (SFL9021AA)
+ *
+ * If the definition has been changed or removed in later revisions
+ * then <max-rev> is the last revision to which the definition applies;
+ * otherwise it is "Z".
+ */
+
+/**************************************************************************
+ *
+ * Falcon/Siena registers and descriptors
+ *
+ **************************************************************************
+ */
+
+/* ADR_REGION_REG: Address region register */
+#define FR_AZ_ADR_REGION 0x00000000
+#define FRF_AZ_ADR_REGION3_LBN 96
+#define FRF_AZ_ADR_REGION3_WIDTH 18
+#define FRF_AZ_ADR_REGION2_LBN 64
+#define FRF_AZ_ADR_REGION2_WIDTH 18
+#define FRF_AZ_ADR_REGION1_LBN 32
+#define FRF_AZ_ADR_REGION1_WIDTH 18
+#define FRF_AZ_ADR_REGION0_LBN 0
+#define FRF_AZ_ADR_REGION0_WIDTH 18
+
+/* INT_EN_REG_KER: Kernel driver Interrupt enable register */
+#define FR_AZ_INT_EN_KER 0x00000010
+#define FRF_AZ_KER_INT_LEVE_SEL_LBN 8
+#define FRF_AZ_KER_INT_LEVE_SEL_WIDTH 6
+#define FRF_AZ_KER_INT_CHAR_LBN 4
+#define FRF_AZ_KER_INT_CHAR_WIDTH 1
+#define FRF_AZ_KER_INT_KER_LBN 3
+#define FRF_AZ_KER_INT_KER_WIDTH 1
+#define FRF_AZ_DRV_INT_EN_KER_LBN 0
+#define FRF_AZ_DRV_INT_EN_KER_WIDTH 1
+
+/* INT_EN_REG_CHAR: Char Driver interrupt enable register */
+#define FR_BZ_INT_EN_CHAR 0x00000020
+#define FRF_BZ_CHAR_INT_LEVE_SEL_LBN 8
+#define FRF_BZ_CHAR_INT_LEVE_SEL_WIDTH 6
+#define FRF_BZ_CHAR_INT_CHAR_LBN 4
+#define FRF_BZ_CHAR_INT_CHAR_WIDTH 1
+#define FRF_BZ_CHAR_INT_KER_LBN 3
+#define FRF_BZ_CHAR_INT_KER_WIDTH 1
+#define FRF_BZ_DRV_INT_EN_CHAR_LBN 0
+#define FRF_BZ_DRV_INT_EN_CHAR_WIDTH 1
+
+/* INT_ADR_REG_KER: Interrupt host address for Kernel driver */
+#define FR_AZ_INT_ADR_KER 0x00000030
+#define FRF_AZ_NORM_INT_VEC_DIS_KER_LBN 64
+#define FRF_AZ_NORM_INT_VEC_DIS_KER_WIDTH 1
+#define FRF_AZ_INT_ADR_KER_LBN 0
+#define FRF_AZ_INT_ADR_KER_WIDTH 64
+
+/* INT_ADR_REG_CHAR: Interrupt host address for Char driver */
+#define FR_BZ_INT_ADR_CHAR 0x00000040
+#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_LBN 64
+#define FRF_BZ_NORM_INT_VEC_DIS_CHAR_WIDTH 1
+#define FRF_BZ_INT_ADR_CHAR_LBN 0
+#define FRF_BZ_INT_ADR_CHAR_WIDTH 64
+
+/* INT_ACK_KER: Kernel interrupt acknowledge register */
+#define FR_AA_INT_ACK_KER 0x00000050
+#define FRF_AA_INT_ACK_KER_FIELD_LBN 0
+#define FRF_AA_INT_ACK_KER_FIELD_WIDTH 32
+
+/* INT_ISR0_REG: Function 0 Interrupt Acknowlege Status register */
+#define FR_BZ_INT_ISR0 0x00000090
+#define FRF_BZ_INT_ISR_REG_LBN 0
+#define FRF_BZ_INT_ISR_REG_WIDTH 64
+
+/* HW_INIT_REG: Hardware initialization register */
+#define FR_AZ_HW_INIT 0x000000c0
+#define FRF_BB_BDMRD_CPLF_FULL_LBN 124
+#define FRF_BB_BDMRD_CPLF_FULL_WIDTH 1
+#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_LBN 121
+#define FRF_BB_PCIE_CPL_TIMEOUT_CTRL_WIDTH 3
+#define FRF_CZ_TX_MRG_TAGS_LBN 120
+#define FRF_CZ_TX_MRG_TAGS_WIDTH 1
+#define FRF_AB_TRGT_MASK_ALL_LBN 100
+#define FRF_AB_TRGT_MASK_ALL_WIDTH 1
+#define FRF_AZ_DOORBELL_DROP_LBN 92
+#define FRF_AZ_DOORBELL_DROP_WIDTH 8
+#define FRF_AB_TX_RREQ_MASK_EN_LBN 76
+#define FRF_AB_TX_RREQ_MASK_EN_WIDTH 1
+#define FRF_AB_PE_EIDLE_DIS_LBN 75
+#define FRF_AB_PE_EIDLE_DIS_WIDTH 1
+#define FRF_AA_FC_BLOCKING_EN_LBN 45
+#define FRF_AA_FC_BLOCKING_EN_WIDTH 1
+#define FRF_BZ_B2B_REQ_EN_LBN 45
+#define FRF_BZ_B2B_REQ_EN_WIDTH 1
+#define FRF_AA_B2B_REQ_EN_LBN 44
+#define FRF_AA_B2B_REQ_EN_WIDTH 1
+#define FRF_BB_FC_BLOCKING_EN_LBN 44
+#define FRF_BB_FC_BLOCKING_EN_WIDTH 1
+#define FRF_AZ_POST_WR_MASK_LBN 40
+#define FRF_AZ_POST_WR_MASK_WIDTH 4
+#define FRF_AZ_TLP_TC_LBN 34
+#define FRF_AZ_TLP_TC_WIDTH 3
+#define FRF_AZ_TLP_ATTR_LBN 32
+#define FRF_AZ_TLP_ATTR_WIDTH 2
+#define FRF_AB_INTB_VEC_LBN 24
+#define FRF_AB_INTB_VEC_WIDTH 5
+#define FRF_AB_INTA_VEC_LBN 16
+#define FRF_AB_INTA_VEC_WIDTH 5
+#define FRF_AZ_WD_TIMER_LBN 8
+#define FRF_AZ_WD_TIMER_WIDTH 8
+#define FRF_AZ_US_DISABLE_LBN 5
+#define FRF_AZ_US_DISABLE_WIDTH 1
+#define FRF_AZ_TLP_EP_LBN 4
+#define FRF_AZ_TLP_EP_WIDTH 1
+#define FRF_AZ_ATTR_SEL_LBN 3
+#define FRF_AZ_ATTR_SEL_WIDTH 1
+#define FRF_AZ_TD_SEL_LBN 1
+#define FRF_AZ_TD_SEL_WIDTH 1
+#define FRF_AZ_TLP_TD_LBN 0
+#define FRF_AZ_TLP_TD_WIDTH 1
+
+/* EE_SPI_HCMD_REG: SPI host command register */
+#define FR_AB_EE_SPI_HCMD 0x00000100
+#define FRF_AB_EE_SPI_HCMD_CMD_EN_LBN 31
+#define FRF_AB_EE_SPI_HCMD_CMD_EN_WIDTH 1
+#define FRF_AB_EE_WR_TIMER_ACTIVE_LBN 28
+#define FRF_AB_EE_WR_TIMER_ACTIVE_WIDTH 1
+#define FRF_AB_EE_SPI_HCMD_SF_SEL_LBN 24
+#define FRF_AB_EE_SPI_HCMD_SF_SEL_WIDTH 1
+#define FRF_AB_EE_SPI_HCMD_DABCNT_LBN 16
+#define FRF_AB_EE_SPI_HCMD_DABCNT_WIDTH 5
+#define FRF_AB_EE_SPI_HCMD_READ_LBN 15
+#define FRF_AB_EE_SPI_HCMD_READ_WIDTH 1
+#define FRF_AB_EE_SPI_HCMD_DUBCNT_LBN 12
+#define FRF_AB_EE_SPI_HCMD_DUBCNT_WIDTH 2
+#define FRF_AB_EE_SPI_HCMD_ADBCNT_LBN 8
+#define FRF_AB_EE_SPI_HCMD_ADBCNT_WIDTH 2
+#define FRF_AB_EE_SPI_HCMD_ENC_LBN 0
+#define FRF_AB_EE_SPI_HCMD_ENC_WIDTH 8
+
+/* USR_EV_CFG: User Level Event Configuration register */
+#define FR_CZ_USR_EV_CFG 0x00000100
+#define FRF_CZ_USREV_DIS_LBN 16
+#define FRF_CZ_USREV_DIS_WIDTH 1
+#define FRF_CZ_DFLT_EVQ_LBN 0
+#define FRF_CZ_DFLT_EVQ_WIDTH 10
+
+/* EE_SPI_HADR_REG: SPI host address register */
+#define FR_AB_EE_SPI_HADR 0x00000110
+#define FRF_AB_EE_SPI_HADR_DUBYTE_LBN 24
+#define FRF_AB_EE_SPI_HADR_DUBYTE_WIDTH 8
+#define FRF_AB_EE_SPI_HADR_ADR_LBN 0
+#define FRF_AB_EE_SPI_HADR_ADR_WIDTH 24
+
+/* EE_SPI_HDATA_REG: SPI host data register */
+#define FR_AB_EE_SPI_HDATA 0x00000120
+#define FRF_AB_EE_SPI_HDATA3_LBN 96
+#define FRF_AB_EE_SPI_HDATA3_WIDTH 32
+#define FRF_AB_EE_SPI_HDATA2_LBN 64
+#define FRF_AB_EE_SPI_HDATA2_WIDTH 32
+#define FRF_AB_EE_SPI_HDATA1_LBN 32
+#define FRF_AB_EE_SPI_HDATA1_WIDTH 32
+#define FRF_AB_EE_SPI_HDATA0_LBN 0
+#define FRF_AB_EE_SPI_HDATA0_WIDTH 32
+
+/* EE_BASE_PAGE_REG: Expansion ROM base mirror register */
+#define FR_AB_EE_BASE_PAGE 0x00000130
+#define FRF_AB_EE_EXPROM_MASK_LBN 16
+#define FRF_AB_EE_EXPROM_MASK_WIDTH 13
+#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_LBN 0
+#define FRF_AB_EE_EXP_ROM_WINDOW_BASE_WIDTH 13
+
+/* EE_VPD_CFG0_REG: SPI/VPD configuration register 0 */
+#define FR_AB_EE_VPD_CFG0 0x00000140
+#define FRF_AB_EE_SF_FASTRD_EN_LBN 127
+#define FRF_AB_EE_SF_FASTRD_EN_WIDTH 1
+#define FRF_AB_EE_SF_CLOCK_DIV_LBN 120
+#define FRF_AB_EE_SF_CLOCK_DIV_WIDTH 7
+#define FRF_AB_EE_VPD_WIP_POLL_LBN 119
+#define FRF_AB_EE_VPD_WIP_POLL_WIDTH 1
+#define FRF_AB_EE_EE_CLOCK_DIV_LBN 112
+#define FRF_AB_EE_EE_CLOCK_DIV_WIDTH 7
+#define FRF_AB_EE_EE_WR_TMR_VALUE_LBN 96
+#define FRF_AB_EE_EE_WR_TMR_VALUE_WIDTH 16
+#define FRF_AB_EE_VPDW_LENGTH_LBN 80
+#define FRF_AB_EE_VPDW_LENGTH_WIDTH 15
+#define FRF_AB_EE_VPDW_BASE_LBN 64
+#define FRF_AB_EE_VPDW_BASE_WIDTH 15
+#define FRF_AB_EE_VPD_WR_CMD_EN_LBN 56
+#define FRF_AB_EE_VPD_WR_CMD_EN_WIDTH 8
+#define FRF_AB_EE_VPD_BASE_LBN 32
+#define FRF_AB_EE_VPD_BASE_WIDTH 24
+#define FRF_AB_EE_VPD_LENGTH_LBN 16
+#define FRF_AB_EE_VPD_LENGTH_WIDTH 15
+#define FRF_AB_EE_VPD_AD_SIZE_LBN 8
+#define FRF_AB_EE_VPD_AD_SIZE_WIDTH 5
+#define FRF_AB_EE_VPD_ACCESS_ON_LBN 5
+#define FRF_AB_EE_VPD_ACCESS_ON_WIDTH 1
+#define FRF_AB_EE_VPD_ACCESS_BLOCK_LBN 4
+#define FRF_AB_EE_VPD_ACCESS_BLOCK_WIDTH 1
+#define FRF_AB_EE_VPD_DEV_SF_SEL_LBN 2
+#define FRF_AB_EE_VPD_DEV_SF_SEL_WIDTH 1
+#define FRF_AB_EE_VPD_EN_AD9_MODE_LBN 1
+#define FRF_AB_EE_VPD_EN_AD9_MODE_WIDTH 1
+#define FRF_AB_EE_VPD_EN_LBN 0
+#define FRF_AB_EE_VPD_EN_WIDTH 1
+
+/* EE_VPD_SW_CNTL_REG: VPD access SW control register */
+#define FR_AB_EE_VPD_SW_CNTL 0x00000150
+#define FRF_AB_EE_VPD_CYCLE_PENDING_LBN 31
+#define FRF_AB_EE_VPD_CYCLE_PENDING_WIDTH 1
+#define FRF_AB_EE_VPD_CYC_WRITE_LBN 28
+#define FRF_AB_EE_VPD_CYC_WRITE_WIDTH 1
+#define FRF_AB_EE_VPD_CYC_ADR_LBN 0
+#define FRF_AB_EE_VPD_CYC_ADR_WIDTH 15
+
+/* EE_VPD_SW_DATA_REG: VPD access SW data register */
+#define FR_AB_EE_VPD_SW_DATA 0x00000160
+#define FRF_AB_EE_VPD_CYC_DAT_LBN 0
+#define FRF_AB_EE_VPD_CYC_DAT_WIDTH 32
+
+/* PBMX_DBG_IADDR_REG: Capture Module address register */
+#define FR_CZ_PBMX_DBG_IADDR 0x000001f0
+#define FRF_CZ_PBMX_DBG_IADDR_LBN 0
+#define FRF_CZ_PBMX_DBG_IADDR_WIDTH 32
+
+/* PCIE_CORE_INDIRECT_REG: Indirect Access to PCIE Core registers */
+#define FR_BB_PCIE_CORE_INDIRECT 0x000001f0
+#define FRF_BB_PCIE_CORE_TARGET_DATA_LBN 32
+#define FRF_BB_PCIE_CORE_TARGET_DATA_WIDTH 32
+#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_LBN 15
+#define FRF_BB_PCIE_CORE_INDIRECT_ACCESS_DIR_WIDTH 1
+#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_LBN 0
+#define FRF_BB_PCIE_CORE_TARGET_REG_ADRS_WIDTH 12
+
+/* PBMX_DBG_IDATA_REG: Capture Module data register */
+#define FR_CZ_PBMX_DBG_IDATA 0x000001f8
+#define FRF_CZ_PBMX_DBG_IDATA_LBN 0
+#define FRF_CZ_PBMX_DBG_IDATA_WIDTH 64
+
+/* NIC_STAT_REG: NIC status register */
+#define FR_AB_NIC_STAT 0x00000200
+#define FRF_BB_AER_DIS_LBN 34
+#define FRF_BB_AER_DIS_WIDTH 1
+#define FRF_BB_EE_STRAP_EN_LBN 31
+#define FRF_BB_EE_STRAP_EN_WIDTH 1
+#define FRF_BB_EE_STRAP_LBN 24
+#define FRF_BB_EE_STRAP_WIDTH 4
+#define FRF_BB_REVISION_ID_LBN 17
+#define FRF_BB_REVISION_ID_WIDTH 7
+#define FRF_AB_ONCHIP_SRAM_LBN 16
+#define FRF_AB_ONCHIP_SRAM_WIDTH 1
+#define FRF_AB_SF_PRST_LBN 9
+#define FRF_AB_SF_PRST_WIDTH 1
+#define FRF_AB_EE_PRST_LBN 8
+#define FRF_AB_EE_PRST_WIDTH 1
+#define FRF_AB_ATE_MODE_LBN 3
+#define FRF_AB_ATE_MODE_WIDTH 1
+#define FRF_AB_STRAP_PINS_LBN 0
+#define FRF_AB_STRAP_PINS_WIDTH 3
+
+/* GPIO_CTL_REG: GPIO control register */
+#define FR_AB_GPIO_CTL 0x00000210
+#define FRF_AB_GPIO_OUT3_LBN 112
+#define FRF_AB_GPIO_OUT3_WIDTH 16
+#define FRF_AB_GPIO_IN3_LBN 104
+#define FRF_AB_GPIO_IN3_WIDTH 8
+#define FRF_AB_GPIO_PWRUP_VALUE3_LBN 96
+#define FRF_AB_GPIO_PWRUP_VALUE3_WIDTH 8
+#define FRF_AB_GPIO_OUT2_LBN 80
+#define FRF_AB_GPIO_OUT2_WIDTH 16
+#define FRF_AB_GPIO_IN2_LBN 72
+#define FRF_AB_GPIO_IN2_WIDTH 8
+#define FRF_AB_GPIO_PWRUP_VALUE2_LBN 64
+#define FRF_AB_GPIO_PWRUP_VALUE2_WIDTH 8
+#define FRF_AB_GPIO15_OEN_LBN 63
+#define FRF_AB_GPIO15_OEN_WIDTH 1
+#define FRF_AB_GPIO14_OEN_LBN 62
+#define FRF_AB_GPIO14_OEN_WIDTH 1
+#define FRF_AB_GPIO13_OEN_LBN 61
+#define FRF_AB_GPIO13_OEN_WIDTH 1
+#define FRF_AB_GPIO12_OEN_LBN 60
+#define FRF_AB_GPIO12_OEN_WIDTH 1
+#define FRF_AB_GPIO11_OEN_LBN 59
+#define FRF_AB_GPIO11_OEN_WIDTH 1
+#define FRF_AB_GPIO10_OEN_LBN 58
+#define FRF_AB_GPIO10_OEN_WIDTH 1
+#define FRF_AB_GPIO9_OEN_LBN 57
+#define FRF_AB_GPIO9_OEN_WIDTH 1
+#define FRF_AB_GPIO8_OEN_LBN 56
+#define FRF_AB_GPIO8_OEN_WIDTH 1
+#define FRF_AB_GPIO15_OUT_LBN 55
+#define FRF_AB_GPIO15_OUT_WIDTH 1
+#define FRF_AB_GPIO14_OUT_LBN 54
+#define FRF_AB_GPIO14_OUT_WIDTH 1
+#define FRF_AB_GPIO13_OUT_LBN 53
+#define FRF_AB_GPIO13_OUT_WIDTH 1
+#define FRF_AB_GPIO12_OUT_LBN 52
+#define FRF_AB_GPIO12_OUT_WIDTH 1
+#define FRF_AB_GPIO11_OUT_LBN 51
+#define FRF_AB_GPIO11_OUT_WIDTH 1
+#define FRF_AB_GPIO10_OUT_LBN 50
+#define FRF_AB_GPIO10_OUT_WIDTH 1
+#define FRF_AB_GPIO9_OUT_LBN 49
+#define FRF_AB_GPIO9_OUT_WIDTH 1
+#define FRF_AB_GPIO8_OUT_LBN 48
+#define FRF_AB_GPIO8_OUT_WIDTH 1
+#define FRF_AB_GPIO15_IN_LBN 47
+#define FRF_AB_GPIO15_IN_WIDTH 1
+#define FRF_AB_GPIO14_IN_LBN 46
+#define FRF_AB_GPIO14_IN_WIDTH 1
+#define FRF_AB_GPIO13_IN_LBN 45
+#define FRF_AB_GPIO13_IN_WIDTH 1
+#define FRF_AB_GPIO12_IN_LBN 44
+#define FRF_AB_GPIO12_IN_WIDTH 1
+#define FRF_AB_GPIO11_IN_LBN 43
+#define FRF_AB_GPIO11_IN_WIDTH 1
+#define FRF_AB_GPIO10_IN_LBN 42
+#define FRF_AB_GPIO10_IN_WIDTH 1
+#define FRF_AB_GPIO9_IN_LBN 41
+#define FRF_AB_GPIO9_IN_WIDTH 1
+#define FRF_AB_GPIO8_IN_LBN 40
+#define FRF_AB_GPIO8_IN_WIDTH 1
+#define FRF_AB_GPIO15_PWRUP_VALUE_LBN 39
+#define FRF_AB_GPIO15_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO14_PWRUP_VALUE_LBN 38
+#define FRF_AB_GPIO14_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO13_PWRUP_VALUE_LBN 37
+#define FRF_AB_GPIO13_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO12_PWRUP_VALUE_LBN 36
+#define FRF_AB_GPIO12_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO11_PWRUP_VALUE_LBN 35
+#define FRF_AB_GPIO11_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO10_PWRUP_VALUE_LBN 34
+#define FRF_AB_GPIO10_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO9_PWRUP_VALUE_LBN 33
+#define FRF_AB_GPIO9_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO8_PWRUP_VALUE_LBN 32
+#define FRF_AB_GPIO8_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_CLK156_OUT_EN_LBN 31
+#define FRF_AB_CLK156_OUT_EN_WIDTH 1
+#define FRF_AB_USE_NIC_CLK_LBN 30
+#define FRF_AB_USE_NIC_CLK_WIDTH 1
+#define FRF_AB_GPIO5_OEN_LBN 29
+#define FRF_AB_GPIO5_OEN_WIDTH 1
+#define FRF_AB_GPIO4_OEN_LBN 28
+#define FRF_AB_GPIO4_OEN_WIDTH 1
+#define FRF_AB_GPIO3_OEN_LBN 27
+#define FRF_AB_GPIO3_OEN_WIDTH 1
+#define FRF_AB_GPIO2_OEN_LBN 26
+#define FRF_AB_GPIO2_OEN_WIDTH 1
+#define FRF_AB_GPIO1_OEN_LBN 25
+#define FRF_AB_GPIO1_OEN_WIDTH 1
+#define FRF_AB_GPIO0_OEN_LBN 24
+#define FRF_AB_GPIO0_OEN_WIDTH 1
+#define FRF_AB_GPIO7_OUT_LBN 23
+#define FRF_AB_GPIO7_OUT_WIDTH 1
+#define FRF_AB_GPIO6_OUT_LBN 22
+#define FRF_AB_GPIO6_OUT_WIDTH 1
+#define FRF_AB_GPIO5_OUT_LBN 21
+#define FRF_AB_GPIO5_OUT_WIDTH 1
+#define FRF_AB_GPIO4_OUT_LBN 20
+#define FRF_AB_GPIO4_OUT_WIDTH 1
+#define FRF_AB_GPIO3_OUT_LBN 19
+#define FRF_AB_GPIO3_OUT_WIDTH 1
+#define FRF_AB_GPIO2_OUT_LBN 18
+#define FRF_AB_GPIO2_OUT_WIDTH 1
+#define FRF_AB_GPIO1_OUT_LBN 17
+#define FRF_AB_GPIO1_OUT_WIDTH 1
+#define FRF_AB_GPIO0_OUT_LBN 16
+#define FRF_AB_GPIO0_OUT_WIDTH 1
+#define FRF_AB_GPIO7_IN_LBN 15
+#define FRF_AB_GPIO7_IN_WIDTH 1
+#define FRF_AB_GPIO6_IN_LBN 14
+#define FRF_AB_GPIO6_IN_WIDTH 1
+#define FRF_AB_GPIO5_IN_LBN 13
+#define FRF_AB_GPIO5_IN_WIDTH 1
+#define FRF_AB_GPIO4_IN_LBN 12
+#define FRF_AB_GPIO4_IN_WIDTH 1
+#define FRF_AB_GPIO3_IN_LBN 11
+#define FRF_AB_GPIO3_IN_WIDTH 1
+#define FRF_AB_GPIO2_IN_LBN 10
+#define FRF_AB_GPIO2_IN_WIDTH 1
+#define FRF_AB_GPIO1_IN_LBN 9
+#define FRF_AB_GPIO1_IN_WIDTH 1
+#define FRF_AB_GPIO0_IN_LBN 8
+#define FRF_AB_GPIO0_IN_WIDTH 1
+#define FRF_AB_GPIO7_PWRUP_VALUE_LBN 7
+#define FRF_AB_GPIO7_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO6_PWRUP_VALUE_LBN 6
+#define FRF_AB_GPIO6_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO5_PWRUP_VALUE_LBN 5
+#define FRF_AB_GPIO5_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO4_PWRUP_VALUE_LBN 4
+#define FRF_AB_GPIO4_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO3_PWRUP_VALUE_LBN 3
+#define FRF_AB_GPIO3_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO2_PWRUP_VALUE_LBN 2
+#define FRF_AB_GPIO2_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO1_PWRUP_VALUE_LBN 1
+#define FRF_AB_GPIO1_PWRUP_VALUE_WIDTH 1
+#define FRF_AB_GPIO0_PWRUP_VALUE_LBN 0
+#define FRF_AB_GPIO0_PWRUP_VALUE_WIDTH 1
+
+/* GLB_CTL_REG: Global control register */
+#define FR_AB_GLB_CTL 0x00000220
+#define FRF_AB_EXT_PHY_RST_CTL_LBN 63
+#define FRF_AB_EXT_PHY_RST_CTL_WIDTH 1
+#define FRF_AB_XAUI_SD_RST_CTL_LBN 62
+#define FRF_AB_XAUI_SD_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_SD_RST_CTL_LBN 61
+#define FRF_AB_PCIE_SD_RST_CTL_WIDTH 1
+#define FRF_AA_PCIX_RST_CTL_LBN 60
+#define FRF_AA_PCIX_RST_CTL_WIDTH 1
+#define FRF_BB_BIU_RST_CTL_LBN 60
+#define FRF_BB_BIU_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_STKY_RST_CTL_LBN 59
+#define FRF_AB_PCIE_STKY_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_NSTKY_RST_CTL_LBN 58
+#define FRF_AB_PCIE_NSTKY_RST_CTL_WIDTH 1
+#define FRF_AB_PCIE_CORE_RST_CTL_LBN 57
+#define FRF_AB_PCIE_CORE_RST_CTL_WIDTH 1
+#define FRF_AB_XGRX_RST_CTL_LBN 56
+#define FRF_AB_XGRX_RST_CTL_WIDTH 1
+#define FRF_AB_XGTX_RST_CTL_LBN 55
+#define FRF_AB_XGTX_RST_CTL_WIDTH 1
+#define FRF_AB_EM_RST_CTL_LBN 54
+#define FRF_AB_EM_RST_CTL_WIDTH 1
+#define FRF_AB_EV_RST_CTL_LBN 53
+#define FRF_AB_EV_RST_CTL_WIDTH 1
+#define FRF_AB_SR_RST_CTL_LBN 52
+#define FRF_AB_SR_RST_CTL_WIDTH 1
+#define FRF_AB_RX_RST_CTL_LBN 51
+#define FRF_AB_RX_RST_CTL_WIDTH 1
+#define FRF_AB_TX_RST_CTL_LBN 50
+#define FRF_AB_TX_RST_CTL_WIDTH 1
+#define FRF_AB_EE_RST_CTL_LBN 49
+#define FRF_AB_EE_RST_CTL_WIDTH 1
+#define FRF_AB_CS_RST_CTL_LBN 48
+#define FRF_AB_CS_RST_CTL_WIDTH 1
+#define FRF_AB_HOT_RST_CTL_LBN 40
+#define FRF_AB_HOT_RST_CTL_WIDTH 2
+#define FRF_AB_RST_EXT_PHY_LBN 31
+#define FRF_AB_RST_EXT_PHY_WIDTH 1
+#define FRF_AB_RST_XAUI_SD_LBN 30
+#define FRF_AB_RST_XAUI_SD_WIDTH 1
+#define FRF_AB_RST_PCIE_SD_LBN 29
+#define FRF_AB_RST_PCIE_SD_WIDTH 1
+#define FRF_AA_RST_PCIX_LBN 28
+#define FRF_AA_RST_PCIX_WIDTH 1
+#define FRF_BB_RST_BIU_LBN 28
+#define FRF_BB_RST_BIU_WIDTH 1
+#define FRF_AB_RST_PCIE_STKY_LBN 27
+#define FRF_AB_RST_PCIE_STKY_WIDTH 1
+#define FRF_AB_RST_PCIE_NSTKY_LBN 26
+#define FRF_AB_RST_PCIE_NSTKY_WIDTH 1
+#define FRF_AB_RST_PCIE_CORE_LBN 25
+#define FRF_AB_RST_PCIE_CORE_WIDTH 1
+#define FRF_AB_RST_XGRX_LBN 24
+#define FRF_AB_RST_XGRX_WIDTH 1
+#define FRF_AB_RST_XGTX_LBN 23
+#define FRF_AB_RST_XGTX_WIDTH 1
+#define FRF_AB_RST_EM_LBN 22
+#define FRF_AB_RST_EM_WIDTH 1
+#define FRF_AB_RST_EV_LBN 21
+#define FRF_AB_RST_EV_WIDTH 1
+#define FRF_AB_RST_SR_LBN 20
+#define FRF_AB_RST_SR_WIDTH 1
+#define FRF_AB_RST_RX_LBN 19
+#define FRF_AB_RST_RX_WIDTH 1
+#define FRF_AB_RST_TX_LBN 18
+#define FRF_AB_RST_TX_WIDTH 1
+#define FRF_AB_RST_SF_LBN 17
+#define FRF_AB_RST_SF_WIDTH 1
+#define FRF_AB_RST_CS_LBN 16
+#define FRF_AB_RST_CS_WIDTH 1
+#define FRF_AB_INT_RST_DUR_LBN 4
+#define FRF_AB_INT_RST_DUR_WIDTH 3
+#define FRF_AB_EXT_PHY_RST_DUR_LBN 1
+#define FRF_AB_EXT_PHY_RST_DUR_WIDTH 3
+#define FFE_AB_EXT_PHY_RST_DUR_10240US 7
+#define FFE_AB_EXT_PHY_RST_DUR_5120US 6
+#define FFE_AB_EXT_PHY_RST_DUR_2560US 5
+#define FFE_AB_EXT_PHY_RST_DUR_1280US 4
+#define FFE_AB_EXT_PHY_RST_DUR_640US 3
+#define FFE_AB_EXT_PHY_RST_DUR_320US 2
+#define FFE_AB_EXT_PHY_RST_DUR_160US 1
+#define FFE_AB_EXT_PHY_RST_DUR_80US 0
+#define FRF_AB_SWRST_LBN 0
+#define FRF_AB_SWRST_WIDTH 1
+
+/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */
+#define FR_AZ_FATAL_INTR_KER 0x00000230
+#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_LBN 44
+#define FRF_CZ_SRAM_PERR_INT_P_KER_EN_WIDTH 1
+#define FRF_AB_PCI_BUSERR_INT_KER_EN_LBN 43
+#define FRF_AB_PCI_BUSERR_INT_KER_EN_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_KER_EN_LBN 43
+#define FRF_CZ_MBU_PERR_INT_KER_EN_WIDTH 1
+#define FRF_AZ_SRAM_OOB_INT_KER_EN_LBN 42
+#define FRF_AZ_SRAM_OOB_INT_KER_EN_WIDTH 1
+#define FRF_AZ_BUFID_OOB_INT_KER_EN_LBN 41
+#define FRF_AZ_BUFID_OOB_INT_KER_EN_WIDTH 1
+#define FRF_AZ_MEM_PERR_INT_KER_EN_LBN 40
+#define FRF_AZ_MEM_PERR_INT_KER_EN_WIDTH 1
+#define FRF_AZ_RBUF_OWN_INT_KER_EN_LBN 39
+#define FRF_AZ_RBUF_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_TBUF_OWN_INT_KER_EN_LBN 38
+#define FRF_AZ_TBUF_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_LBN 37
+#define FRF_AZ_RDESCQ_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_LBN 36
+#define FRF_AZ_TDESCQ_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_EVQ_OWN_INT_KER_EN_LBN 35
+#define FRF_AZ_EVQ_OWN_INT_KER_EN_WIDTH 1
+#define FRF_AZ_EVF_OFLO_INT_KER_EN_LBN 34
+#define FRF_AZ_EVF_OFLO_INT_KER_EN_WIDTH 1
+#define FRF_AZ_ILL_ADR_INT_KER_EN_LBN 33
+#define FRF_AZ_ILL_ADR_INT_KER_EN_WIDTH 1
+#define FRF_AZ_SRM_PERR_INT_KER_EN_LBN 32
+#define FRF_AZ_SRM_PERR_INT_KER_EN_WIDTH 1
+#define FRF_CZ_SRAM_PERR_INT_P_KER_LBN 12
+#define FRF_CZ_SRAM_PERR_INT_P_KER_WIDTH 1
+#define FRF_AB_PCI_BUSERR_INT_KER_LBN 11
+#define FRF_AB_PCI_BUSERR_INT_KER_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_KER_LBN 11
+#define FRF_CZ_MBU_PERR_INT_KER_WIDTH 1
+#define FRF_AZ_SRAM_OOB_INT_KER_LBN 10
+#define FRF_AZ_SRAM_OOB_INT_KER_WIDTH 1
+#define FRF_AZ_BUFID_DC_OOB_INT_KER_LBN 9
+#define FRF_AZ_BUFID_DC_OOB_INT_KER_WIDTH 1
+#define FRF_AZ_MEM_PERR_INT_KER_LBN 8
+#define FRF_AZ_MEM_PERR_INT_KER_WIDTH 1
+#define FRF_AZ_RBUF_OWN_INT_KER_LBN 7
+#define FRF_AZ_RBUF_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_TBUF_OWN_INT_KER_LBN 6
+#define FRF_AZ_TBUF_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_RDESCQ_OWN_INT_KER_LBN 5
+#define FRF_AZ_RDESCQ_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_TDESCQ_OWN_INT_KER_LBN 4
+#define FRF_AZ_TDESCQ_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_EVQ_OWN_INT_KER_LBN 3
+#define FRF_AZ_EVQ_OWN_INT_KER_WIDTH 1
+#define FRF_AZ_EVF_OFLO_INT_KER_LBN 2
+#define FRF_AZ_EVF_OFLO_INT_KER_WIDTH 1
+#define FRF_AZ_ILL_ADR_INT_KER_LBN 1
+#define FRF_AZ_ILL_ADR_INT_KER_WIDTH 1
+#define FRF_AZ_SRM_PERR_INT_KER_LBN 0
+#define FRF_AZ_SRM_PERR_INT_KER_WIDTH 1
+
+/* FATAL_INTR_REG_CHAR: Fatal interrupt register for Char */
+#define FR_BZ_FATAL_INTR_CHAR 0x00000240
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_LBN 44
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_EN_WIDTH 1
+#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_LBN 43
+#define FRF_BB_PCI_BUSERR_INT_CHAR_EN_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_CHAR_EN_LBN 43
+#define FRF_CZ_MBU_PERR_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_LBN 42
+#define FRF_BZ_SRAM_OOB_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_LBN 41
+#define FRF_BZ_BUFID_OOB_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_MEM_PERR_INT_CHAR_EN_LBN 40
+#define FRF_BZ_MEM_PERR_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_LBN 39
+#define FRF_BZ_RBUF_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_LBN 38
+#define FRF_BZ_TBUF_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_LBN 37
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_LBN 36
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_LBN 35
+#define FRF_BZ_EVQ_OWN_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_LBN 34
+#define FRF_BZ_EVF_OFLO_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_ILL_ADR_INT_CHAR_EN_LBN 33
+#define FRF_BZ_ILL_ADR_INT_CHAR_EN_WIDTH 1
+#define FRF_BZ_SRM_PERR_INT_CHAR_EN_LBN 32
+#define FRF_BZ_SRM_PERR_INT_CHAR_EN_WIDTH 1
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_LBN 12
+#define FRF_CZ_SRAM_PERR_INT_P_CHAR_WIDTH 1
+#define FRF_BB_PCI_BUSERR_INT_CHAR_LBN 11
+#define FRF_BB_PCI_BUSERR_INT_CHAR_WIDTH 1
+#define FRF_CZ_MBU_PERR_INT_CHAR_LBN 11
+#define FRF_CZ_MBU_PERR_INT_CHAR_WIDTH 1
+#define FRF_BZ_SRAM_OOB_INT_CHAR_LBN 10
+#define FRF_BZ_SRAM_OOB_INT_CHAR_WIDTH 1
+#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_LBN 9
+#define FRF_BZ_BUFID_DC_OOB_INT_CHAR_WIDTH 1
+#define FRF_BZ_MEM_PERR_INT_CHAR_LBN 8
+#define FRF_BZ_MEM_PERR_INT_CHAR_WIDTH 1
+#define FRF_BZ_RBUF_OWN_INT_CHAR_LBN 7
+#define FRF_BZ_RBUF_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_TBUF_OWN_INT_CHAR_LBN 6
+#define FRF_BZ_TBUF_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_LBN 5
+#define FRF_BZ_RDESCQ_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_LBN 4
+#define FRF_BZ_TDESCQ_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_EVQ_OWN_INT_CHAR_LBN 3
+#define FRF_BZ_EVQ_OWN_INT_CHAR_WIDTH 1
+#define FRF_BZ_EVF_OFLO_INT_CHAR_LBN 2
+#define FRF_BZ_EVF_OFLO_INT_CHAR_WIDTH 1
+#define FRF_BZ_ILL_ADR_INT_CHAR_LBN 1
+#define FRF_BZ_ILL_ADR_INT_CHAR_WIDTH 1
+#define FRF_BZ_SRM_PERR_INT_CHAR_LBN 0
+#define FRF_BZ_SRM_PERR_INT_CHAR_WIDTH 1
+
+/* DP_CTRL_REG: Datapath control register */
+#define FR_BZ_DP_CTRL 0x00000250
+#define FRF_BZ_FLS_EVQ_ID_LBN 0
+#define FRF_BZ_FLS_EVQ_ID_WIDTH 12
+
+/* MEM_STAT_REG: Memory status register */
+#define FR_AZ_MEM_STAT 0x00000260
+#define FRF_AB_MEM_PERR_VEC_LBN 53
+#define FRF_AB_MEM_PERR_VEC_WIDTH 38
+#define FRF_AB_MBIST_CORR_LBN 38
+#define FRF_AB_MBIST_CORR_WIDTH 15
+#define FRF_AB_MBIST_ERR_LBN 0
+#define FRF_AB_MBIST_ERR_WIDTH 40
+#define FRF_CZ_MEM_PERR_VEC_LBN 0
+#define FRF_CZ_MEM_PERR_VEC_WIDTH 35
+
+/* CS_DEBUG_REG: Debug register */
+#define FR_AZ_CS_DEBUG 0x00000270
+#define FRF_AB_GLB_DEBUG2_SEL_LBN 50
+#define FRF_AB_GLB_DEBUG2_SEL_WIDTH 3
+#define FRF_AB_DEBUG_BLK_SEL2_LBN 47
+#define FRF_AB_DEBUG_BLK_SEL2_WIDTH 3
+#define FRF_AB_DEBUG_BLK_SEL1_LBN 44
+#define FRF_AB_DEBUG_BLK_SEL1_WIDTH 3
+#define FRF_AB_DEBUG_BLK_SEL0_LBN 41
+#define FRF_AB_DEBUG_BLK_SEL0_WIDTH 3
+#define FRF_CZ_CS_PORT_NUM_LBN 40
+#define FRF_CZ_CS_PORT_NUM_WIDTH 2
+#define FRF_AB_MISC_DEBUG_ADDR_LBN 36
+#define FRF_AB_MISC_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_SERDES_DEBUG_ADDR_LBN 31
+#define FRF_AB_SERDES_DEBUG_ADDR_WIDTH 5
+#define FRF_CZ_CS_PORT_FPE_LBN 1
+#define FRF_CZ_CS_PORT_FPE_WIDTH 35
+#define FRF_AB_EM_DEBUG_ADDR_LBN 26
+#define FRF_AB_EM_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_SR_DEBUG_ADDR_LBN 21
+#define FRF_AB_SR_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_EV_DEBUG_ADDR_LBN 16
+#define FRF_AB_EV_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_RX_DEBUG_ADDR_LBN 11
+#define FRF_AB_RX_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_TX_DEBUG_ADDR_LBN 6
+#define FRF_AB_TX_DEBUG_ADDR_WIDTH 5
+#define FRF_AB_CS_BIU_DEBUG_ADDR_LBN 1
+#define FRF_AB_CS_BIU_DEBUG_ADDR_WIDTH 5
+#define FRF_AZ_CS_DEBUG_EN_LBN 0
+#define FRF_AZ_CS_DEBUG_EN_WIDTH 1
+
+/* DRIVER_REG: Driver scratch register [0-7] */
+#define FR_AZ_DRIVER 0x00000280
+#define FR_AZ_DRIVER_STEP 16
+#define FR_AZ_DRIVER_ROWS 8
+#define FRF_AZ_DRIVER_DW0_LBN 0
+#define FRF_AZ_DRIVER_DW0_WIDTH 32
+
+/* ALTERA_BUILD_REG: Altera build register */
+#define FR_AZ_ALTERA_BUILD 0x00000300
+#define FRF_AZ_ALTERA_BUILD_VER_LBN 0
+#define FRF_AZ_ALTERA_BUILD_VER_WIDTH 32
+
+/* CSR_SPARE_REG: Spare register */
+#define FR_AZ_CSR_SPARE 0x00000310
+#define FRF_AB_MEM_PERR_EN_LBN 64
+#define FRF_AB_MEM_PERR_EN_WIDTH 38
+#define FRF_CZ_MEM_PERR_EN_LBN 64
+#define FRF_CZ_MEM_PERR_EN_WIDTH 35
+#define FRF_AB_MEM_PERR_EN_TX_DATA_LBN 72
+#define FRF_AB_MEM_PERR_EN_TX_DATA_WIDTH 2
+#define FRF_AZ_CSR_SPARE_BITS_LBN 0
+#define FRF_AZ_CSR_SPARE_BITS_WIDTH 32
+
+/* PCIE_SD_CTL0123_REG: PCIE SerDes control register 0 to 3 */
+#define FR_AB_PCIE_SD_CTL0123 0x00000320
+#define FRF_AB_PCIE_TESTSIG_H_LBN 96
+#define FRF_AB_PCIE_TESTSIG_H_WIDTH 19
+#define FRF_AB_PCIE_TESTSIG_L_LBN 64
+#define FRF_AB_PCIE_TESTSIG_L_WIDTH 19
+#define FRF_AB_PCIE_OFFSET_LBN 56
+#define FRF_AB_PCIE_OFFSET_WIDTH 8
+#define FRF_AB_PCIE_OFFSETEN_H_LBN 55
+#define FRF_AB_PCIE_OFFSETEN_H_WIDTH 1
+#define FRF_AB_PCIE_OFFSETEN_L_LBN 54
+#define FRF_AB_PCIE_OFFSETEN_L_WIDTH 1
+#define FRF_AB_PCIE_HIVMODE_H_LBN 53
+#define FRF_AB_PCIE_HIVMODE_H_WIDTH 1
+#define FRF_AB_PCIE_HIVMODE_L_LBN 52
+#define FRF_AB_PCIE_HIVMODE_L_WIDTH 1
+#define FRF_AB_PCIE_PARRESET_H_LBN 51
+#define FRF_AB_PCIE_PARRESET_H_WIDTH 1
+#define FRF_AB_PCIE_PARRESET_L_LBN 50
+#define FRF_AB_PCIE_PARRESET_L_WIDTH 1
+#define FRF_AB_PCIE_LPBKWDRV_H_LBN 49
+#define FRF_AB_PCIE_LPBKWDRV_H_WIDTH 1
+#define FRF_AB_PCIE_LPBKWDRV_L_LBN 48
+#define FRF_AB_PCIE_LPBKWDRV_L_WIDTH 1
+#define FRF_AB_PCIE_LPBK_LBN 40
+#define FRF_AB_PCIE_LPBK_WIDTH 8
+#define FRF_AB_PCIE_PARLPBK_LBN 32
+#define FRF_AB_PCIE_PARLPBK_WIDTH 8
+#define FRF_AB_PCIE_RXTERMADJ_H_LBN 30
+#define FRF_AB_PCIE_RXTERMADJ_H_WIDTH 2
+#define FRF_AB_PCIE_RXTERMADJ_L_LBN 28
+#define FRF_AB_PCIE_RXTERMADJ_L_WIDTH 2
+#define FFE_AB_PCIE_RXTERMADJ_MIN15PCNT 3
+#define FFE_AB_PCIE_RXTERMADJ_PL10PCNT 2
+#define FFE_AB_PCIE_RXTERMADJ_MIN17PCNT 1
+#define FFE_AB_PCIE_RXTERMADJ_NOMNL 0
+#define FRF_AB_PCIE_TXTERMADJ_H_LBN 26
+#define FRF_AB_PCIE_TXTERMADJ_H_WIDTH 2
+#define FRF_AB_PCIE_TXTERMADJ_L_LBN 24
+#define FRF_AB_PCIE_TXTERMADJ_L_WIDTH 2
+#define FFE_AB_PCIE_TXTERMADJ_MIN15PCNT 3
+#define FFE_AB_PCIE_TXTERMADJ_PL10PCNT 2
+#define FFE_AB_PCIE_TXTERMADJ_MIN17PCNT 1
+#define FFE_AB_PCIE_TXTERMADJ_NOMNL 0
+#define FRF_AB_PCIE_RXEQCTL_H_LBN 18
+#define FRF_AB_PCIE_RXEQCTL_H_WIDTH 2
+#define FRF_AB_PCIE_RXEQCTL_L_LBN 16
+#define FRF_AB_PCIE_RXEQCTL_L_WIDTH 2
+#define FFE_AB_PCIE_RXEQCTL_OFF_ALT 3
+#define FFE_AB_PCIE_RXEQCTL_OFF 2
+#define FFE_AB_PCIE_RXEQCTL_MIN 1
+#define FFE_AB_PCIE_RXEQCTL_MAX 0
+#define FRF_AB_PCIE_HIDRV_LBN 8
+#define FRF_AB_PCIE_HIDRV_WIDTH 8
+#define FRF_AB_PCIE_LODRV_LBN 0
+#define FRF_AB_PCIE_LODRV_WIDTH 8
+
+/* PCIE_SD_CTL45_REG: PCIE SerDes control register 4 and 5 */
+#define FR_AB_PCIE_SD_CTL45 0x00000330
+#define FRF_AB_PCIE_DTX7_LBN 60
+#define FRF_AB_PCIE_DTX7_WIDTH 4
+#define FRF_AB_PCIE_DTX6_LBN 56
+#define FRF_AB_PCIE_DTX6_WIDTH 4
+#define FRF_AB_PCIE_DTX5_LBN 52
+#define FRF_AB_PCIE_DTX5_WIDTH 4
+#define FRF_AB_PCIE_DTX4_LBN 48
+#define FRF_AB_PCIE_DTX4_WIDTH 4
+#define FRF_AB_PCIE_DTX3_LBN 44
+#define FRF_AB_PCIE_DTX3_WIDTH 4
+#define FRF_AB_PCIE_DTX2_LBN 40
+#define FRF_AB_PCIE_DTX2_WIDTH 4
+#define FRF_AB_PCIE_DTX1_LBN 36
+#define FRF_AB_PCIE_DTX1_WIDTH 4
+#define FRF_AB_PCIE_DTX0_LBN 32
+#define FRF_AB_PCIE_DTX0_WIDTH 4
+#define FRF_AB_PCIE_DEQ7_LBN 28
+#define FRF_AB_PCIE_DEQ7_WIDTH 4
+#define FRF_AB_PCIE_DEQ6_LBN 24
+#define FRF_AB_PCIE_DEQ6_WIDTH 4
+#define FRF_AB_PCIE_DEQ5_LBN 20
+#define FRF_AB_PCIE_DEQ5_WIDTH 4
+#define FRF_AB_PCIE_DEQ4_LBN 16
+#define FRF_AB_PCIE_DEQ4_WIDTH 4
+#define FRF_AB_PCIE_DEQ3_LBN 12
+#define FRF_AB_PCIE_DEQ3_WIDTH 4
+#define FRF_AB_PCIE_DEQ2_LBN 8
+#define FRF_AB_PCIE_DEQ2_WIDTH 4
+#define FRF_AB_PCIE_DEQ1_LBN 4
+#define FRF_AB_PCIE_DEQ1_WIDTH 4
+#define FRF_AB_PCIE_DEQ0_LBN 0
+#define FRF_AB_PCIE_DEQ0_WIDTH 4
+
+/* PCIE_PCS_CTL_STAT_REG: PCIE PCS control and status register */
+#define FR_AB_PCIE_PCS_CTL_STAT 0x00000340
+#define FRF_AB_PCIE_PRBSERRCOUNT0_H_LBN 52
+#define FRF_AB_PCIE_PRBSERRCOUNT0_H_WIDTH 4
+#define FRF_AB_PCIE_PRBSERRCOUNT0_L_LBN 48
+#define FRF_AB_PCIE_PRBSERRCOUNT0_L_WIDTH 4
+#define FRF_AB_PCIE_PRBSERR_LBN 40
+#define FRF_AB_PCIE_PRBSERR_WIDTH 8
+#define FRF_AB_PCIE_PRBSERRH0_LBN 32
+#define FRF_AB_PCIE_PRBSERRH0_WIDTH 8
+#define FRF_AB_PCIE_FASTINIT_H_LBN 15
+#define FRF_AB_PCIE_FASTINIT_H_WIDTH 1
+#define FRF_AB_PCIE_FASTINIT_L_LBN 14
+#define FRF_AB_PCIE_FASTINIT_L_WIDTH 1
+#define FRF_AB_PCIE_CTCDISABLE_H_LBN 13
+#define FRF_AB_PCIE_CTCDISABLE_H_WIDTH 1
+#define FRF_AB_PCIE_CTCDISABLE_L_LBN 12
+#define FRF_AB_PCIE_CTCDISABLE_L_WIDTH 1
+#define FRF_AB_PCIE_PRBSSYNC_H_LBN 11
+#define FRF_AB_PCIE_PRBSSYNC_H_WIDTH 1
+#define FRF_AB_PCIE_PRBSSYNC_L_LBN 10
+#define FRF_AB_PCIE_PRBSSYNC_L_WIDTH 1
+#define FRF_AB_PCIE_PRBSERRACK_H_LBN 9
+#define FRF_AB_PCIE_PRBSERRACK_H_WIDTH 1
+#define FRF_AB_PCIE_PRBSERRACK_L_LBN 8
+#define FRF_AB_PCIE_PRBSERRACK_L_WIDTH 1
+#define FRF_AB_PCIE_PRBSSEL_LBN 0
+#define FRF_AB_PCIE_PRBSSEL_WIDTH 8
+
+/* DEBUG_DATA_OUT_REG: Live Debug and Debug 2 out ports */
+#define FR_BB_DEBUG_DATA_OUT 0x00000350
+#define FRF_BB_DEBUG2_PORT_LBN 25
+#define FRF_BB_DEBUG2_PORT_WIDTH 15
+#define FRF_BB_DEBUG1_PORT_LBN 0
+#define FRF_BB_DEBUG1_PORT_WIDTH 25
+
+/* EVQ_RPTR_REGP0: Event queue read pointer register */
+#define FR_BZ_EVQ_RPTR_P0 0x00000400
+#define FR_BZ_EVQ_RPTR_P0_STEP 8192
+#define FR_BZ_EVQ_RPTR_P0_ROWS 1024
+/* EVQ_RPTR_REG_KER: Event queue read pointer register */
+#define FR_AA_EVQ_RPTR_KER 0x00011b00
+#define FR_AA_EVQ_RPTR_KER_STEP 4
+#define FR_AA_EVQ_RPTR_KER_ROWS 4
+/* EVQ_RPTR_REG: Event queue read pointer register */
+#define FR_BZ_EVQ_RPTR 0x00fa0000
+#define FR_BZ_EVQ_RPTR_STEP 16
+#define FR_BB_EVQ_RPTR_ROWS 4096
+#define FR_CZ_EVQ_RPTR_ROWS 1024
+/* EVQ_RPTR_REGP123: Event queue read pointer register */
+#define FR_BB_EVQ_RPTR_P123 0x01000400
+#define FR_BB_EVQ_RPTR_P123_STEP 8192
+#define FR_BB_EVQ_RPTR_P123_ROWS 3072
+#define FRF_AZ_EVQ_RPTR_VLD_LBN 15
+#define FRF_AZ_EVQ_RPTR_VLD_WIDTH 1
+#define FRF_AZ_EVQ_RPTR_LBN 0
+#define FRF_AZ_EVQ_RPTR_WIDTH 15
+
+/* TIMER_COMMAND_REGP0: Timer Command Registers */
+#define FR_BZ_TIMER_COMMAND_P0 0x00000420
+#define FR_BZ_TIMER_COMMAND_P0_STEP 8192
+#define FR_BZ_TIMER_COMMAND_P0_ROWS 1024
+/* TIMER_COMMAND_REG_KER: Timer Command Registers */
+#define FR_AA_TIMER_COMMAND_KER 0x00000420
+#define FR_AA_TIMER_COMMAND_KER_STEP 8192
+#define FR_AA_TIMER_COMMAND_KER_ROWS 4
+/* TIMER_COMMAND_REGP123: Timer Command Registers */
+#define FR_BB_TIMER_COMMAND_P123 0x01000420
+#define FR_BB_TIMER_COMMAND_P123_STEP 8192
+#define FR_BB_TIMER_COMMAND_P123_ROWS 3072
+#define FRF_CZ_TC_TIMER_MODE_LBN 14
+#define FRF_CZ_TC_TIMER_MODE_WIDTH 2
+#define FRF_AB_TC_TIMER_MODE_LBN 12
+#define FRF_AB_TC_TIMER_MODE_WIDTH 2
+#define FRF_CZ_TC_TIMER_VAL_LBN 0
+#define FRF_CZ_TC_TIMER_VAL_WIDTH 14
+#define FRF_AB_TC_TIMER_VAL_LBN 0
+#define FRF_AB_TC_TIMER_VAL_WIDTH 12
+
+/* DRV_EV_REG: Driver generated event register */
+#define FR_AZ_DRV_EV 0x00000440
+#define FRF_AZ_DRV_EV_QID_LBN 64
+#define FRF_AZ_DRV_EV_QID_WIDTH 12
+#define FRF_AZ_DRV_EV_DATA_LBN 0
+#define FRF_AZ_DRV_EV_DATA_WIDTH 64
+
+/* EVQ_CTL_REG: Event queue control register */
+#define FR_AZ_EVQ_CTL 0x00000450
+#define FRF_CZ_RX_EVQ_WAKEUP_MASK_LBN 15
+#define FRF_CZ_RX_EVQ_WAKEUP_MASK_WIDTH 10
+#define FRF_BB_RX_EVQ_WAKEUP_MASK_LBN 15
+#define FRF_BB_RX_EVQ_WAKEUP_MASK_WIDTH 6
+#define FRF_AZ_EVQ_OWNERR_CTL_LBN 14
+#define FRF_AZ_EVQ_OWNERR_CTL_WIDTH 1
+#define FRF_AZ_EVQ_FIFO_AF_TH_LBN 7
+#define FRF_AZ_EVQ_FIFO_AF_TH_WIDTH 7
+#define FRF_AZ_EVQ_FIFO_NOTAF_TH_LBN 0
+#define FRF_AZ_EVQ_FIFO_NOTAF_TH_WIDTH 7
+
+/* EVQ_CNT1_REG: Event counter 1 register */
+#define FR_AZ_EVQ_CNT1 0x00000460
+#define FRF_AZ_EVQ_CNT_PRE_FIFO_LBN 120
+#define FRF_AZ_EVQ_CNT_PRE_FIFO_WIDTH 7
+#define FRF_AZ_EVQ_CNT_TOBIU_LBN 100
+#define FRF_AZ_EVQ_CNT_TOBIU_WIDTH 20
+#define FRF_AZ_EVQ_TX_REQ_CNT_LBN 80
+#define FRF_AZ_EVQ_TX_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_RX_REQ_CNT_LBN 60
+#define FRF_AZ_EVQ_RX_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_EM_REQ_CNT_LBN 40
+#define FRF_AZ_EVQ_EM_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_CSR_REQ_CNT_LBN 20
+#define FRF_AZ_EVQ_CSR_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_ERR_REQ_CNT_LBN 0
+#define FRF_AZ_EVQ_ERR_REQ_CNT_WIDTH 20
+
+/* EVQ_CNT2_REG: Event counter 2 register */
+#define FR_AZ_EVQ_CNT2 0x00000470
+#define FRF_AZ_EVQ_UPD_REQ_CNT_LBN 104
+#define FRF_AZ_EVQ_UPD_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_CLR_REQ_CNT_LBN 84
+#define FRF_AZ_EVQ_CLR_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_RDY_CNT_LBN 80
+#define FRF_AZ_EVQ_RDY_CNT_WIDTH 4
+#define FRF_AZ_EVQ_WU_REQ_CNT_LBN 60
+#define FRF_AZ_EVQ_WU_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_WET_REQ_CNT_LBN 40
+#define FRF_AZ_EVQ_WET_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_INIT_REQ_CNT_LBN 20
+#define FRF_AZ_EVQ_INIT_REQ_CNT_WIDTH 20
+#define FRF_AZ_EVQ_TM_REQ_CNT_LBN 0
+#define FRF_AZ_EVQ_TM_REQ_CNT_WIDTH 20
+
+/* USR_EV_REG: Event mailbox register */
+#define FR_CZ_USR_EV 0x00000540
+#define FR_CZ_USR_EV_STEP 8192
+#define FR_CZ_USR_EV_ROWS 1024
+#define FRF_CZ_USR_EV_DATA_LBN 0
+#define FRF_CZ_USR_EV_DATA_WIDTH 32
+
+/* BUF_TBL_CFG_REG: Buffer table configuration register */
+#define FR_AZ_BUF_TBL_CFG 0x00000600
+#define FRF_AZ_BUF_TBL_MODE_LBN 3
+#define FRF_AZ_BUF_TBL_MODE_WIDTH 1
+
+/* SRM_RX_DC_CFG_REG: SRAM receive descriptor cache configuration register */
+#define FR_AZ_SRM_RX_DC_CFG 0x00000610
+#define FRF_AZ_SRM_CLK_TMP_EN_LBN 21
+#define FRF_AZ_SRM_CLK_TMP_EN_WIDTH 1
+#define FRF_AZ_SRM_RX_DC_BASE_ADR_LBN 0
+#define FRF_AZ_SRM_RX_DC_BASE_ADR_WIDTH 21
+
+/* SRM_TX_DC_CFG_REG: SRAM transmit descriptor cache configuration register */
+#define FR_AZ_SRM_TX_DC_CFG 0x00000620
+#define FRF_AZ_SRM_TX_DC_BASE_ADR_LBN 0
+#define FRF_AZ_SRM_TX_DC_BASE_ADR_WIDTH 21
+
+/* SRM_CFG_REG: SRAM configuration register */
+#define FR_AZ_SRM_CFG 0x00000630
+#define FRF_AZ_SRM_OOB_ADR_INTEN_LBN 5
+#define FRF_AZ_SRM_OOB_ADR_INTEN_WIDTH 1
+#define FRF_AZ_SRM_OOB_BUF_INTEN_LBN 4
+#define FRF_AZ_SRM_OOB_BUF_INTEN_WIDTH 1
+#define FRF_AZ_SRM_INIT_EN_LBN 3
+#define FRF_AZ_SRM_INIT_EN_WIDTH 1
+#define FRF_AZ_SRM_NUM_BANK_LBN 2
+#define FRF_AZ_SRM_NUM_BANK_WIDTH 1
+#define FRF_AZ_SRM_BANK_SIZE_LBN 0
+#define FRF_AZ_SRM_BANK_SIZE_WIDTH 2
+
+/* BUF_TBL_UPD_REG: Buffer table update register */
+#define FR_AZ_BUF_TBL_UPD 0x00000650
+#define FRF_AZ_BUF_UPD_CMD_LBN 63
+#define FRF_AZ_BUF_UPD_CMD_WIDTH 1
+#define FRF_AZ_BUF_CLR_CMD_LBN 62
+#define FRF_AZ_BUF_CLR_CMD_WIDTH 1
+#define FRF_AZ_BUF_CLR_END_ID_LBN 32
+#define FRF_AZ_BUF_CLR_END_ID_WIDTH 20
+#define FRF_AZ_BUF_CLR_START_ID_LBN 0
+#define FRF_AZ_BUF_CLR_START_ID_WIDTH 20
+
+/* SRM_UPD_EVQ_REG: Buffer table update register */
+#define FR_AZ_SRM_UPD_EVQ 0x00000660
+#define FRF_AZ_SRM_UPD_EVQ_ID_LBN 0
+#define FRF_AZ_SRM_UPD_EVQ_ID_WIDTH 12
+
+/* SRAM_PARITY_REG: SRAM parity register. */
+#define FR_AZ_SRAM_PARITY 0x00000670
+#define FRF_CZ_BYPASS_ECC_LBN 3
+#define FRF_CZ_BYPASS_ECC_WIDTH 1
+#define FRF_CZ_SEC_INT_LBN 2
+#define FRF_CZ_SEC_INT_WIDTH 1
+#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_LBN 1
+#define FRF_CZ_FORCE_SRAM_DOUBLE_ERR_WIDTH 1
+#define FRF_AB_FORCE_SRAM_PERR_LBN 0
+#define FRF_AB_FORCE_SRAM_PERR_WIDTH 1
+#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_LBN 0
+#define FRF_CZ_FORCE_SRAM_SINGLE_ERR_WIDTH 1
+
+/* RX_CFG_REG: Receive configuration register */
+#define FR_AZ_RX_CFG 0x00000800
+#define FRF_CZ_RX_MIN_KBUF_SIZE_LBN 72
+#define FRF_CZ_RX_MIN_KBUF_SIZE_WIDTH 14
+#define FRF_CZ_RX_HDR_SPLIT_EN_LBN 71
+#define FRF_CZ_RX_HDR_SPLIT_EN_WIDTH 1
+#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_LBN 62
+#define FRF_CZ_RX_HDR_SPLIT_PLD_BUF_SIZE_WIDTH 9
+#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_LBN 53
+#define FRF_CZ_RX_HDR_SPLIT_HDR_BUF_SIZE_WIDTH 9
+#define FRF_CZ_RX_PRE_RFF_IPG_LBN 49
+#define FRF_CZ_RX_PRE_RFF_IPG_WIDTH 4
+#define FRF_BZ_RX_TCP_SUP_LBN 48
+#define FRF_BZ_RX_TCP_SUP_WIDTH 1
+#define FRF_BZ_RX_INGR_EN_LBN 47
+#define FRF_BZ_RX_INGR_EN_WIDTH 1
+#define FRF_BZ_RX_IP_HASH_LBN 46
+#define FRF_BZ_RX_IP_HASH_WIDTH 1
+#define FRF_BZ_RX_HASH_ALG_LBN 45
+#define FRF_BZ_RX_HASH_ALG_WIDTH 1
+#define FRF_BZ_RX_HASH_INSRT_HDR_LBN 44
+#define FRF_BZ_RX_HASH_INSRT_HDR_WIDTH 1
+#define FRF_BZ_RX_DESC_PUSH_EN_LBN 43
+#define FRF_BZ_RX_DESC_PUSH_EN_WIDTH 1
+#define FRF_BZ_RX_RDW_PATCH_EN_LBN 42
+#define FRF_BZ_RX_RDW_PATCH_EN_WIDTH 1
+#define FRF_BB_RX_PCI_BURST_SIZE_LBN 39
+#define FRF_BB_RX_PCI_BURST_SIZE_WIDTH 3
+#define FRF_BZ_RX_OWNERR_CTL_LBN 38
+#define FRF_BZ_RX_OWNERR_CTL_WIDTH 1
+#define FRF_BZ_RX_XON_TX_TH_LBN 33
+#define FRF_BZ_RX_XON_TX_TH_WIDTH 5
+#define FRF_AA_RX_DESC_PUSH_EN_LBN 35
+#define FRF_AA_RX_DESC_PUSH_EN_WIDTH 1
+#define FRF_AA_RX_RDW_PATCH_EN_LBN 34
+#define FRF_AA_RX_RDW_PATCH_EN_WIDTH 1
+#define FRF_AA_RX_PCI_BURST_SIZE_LBN 31
+#define FRF_AA_RX_PCI_BURST_SIZE_WIDTH 3
+#define FRF_BZ_RX_XOFF_TX_TH_LBN 28
+#define FRF_BZ_RX_XOFF_TX_TH_WIDTH 5
+#define FRF_AA_RX_OWNERR_CTL_LBN 30
+#define FRF_AA_RX_OWNERR_CTL_WIDTH 1
+#define FRF_AA_RX_XON_TX_TH_LBN 25
+#define FRF_AA_RX_XON_TX_TH_WIDTH 5
+#define FRF_BZ_RX_USR_BUF_SIZE_LBN 19
+#define FRF_BZ_RX_USR_BUF_SIZE_WIDTH 9
+#define FRF_AA_RX_XOFF_TX_TH_LBN 20
+#define FRF_AA_RX_XOFF_TX_TH_WIDTH 5
+#define FRF_AA_RX_USR_BUF_SIZE_LBN 11
+#define FRF_AA_RX_USR_BUF_SIZE_WIDTH 9
+#define FRF_BZ_RX_XON_MAC_TH_LBN 10
+#define FRF_BZ_RX_XON_MAC_TH_WIDTH 9
+#define FRF_AA_RX_XON_MAC_TH_LBN 6
+#define FRF_AA_RX_XON_MAC_TH_WIDTH 5
+#define FRF_BZ_RX_XOFF_MAC_TH_LBN 1
+#define FRF_BZ_RX_XOFF_MAC_TH_WIDTH 9
+#define FRF_AA_RX_XOFF_MAC_TH_LBN 1
+#define FRF_AA_RX_XOFF_MAC_TH_WIDTH 5
+#define FRF_AZ_RX_XOFF_MAC_EN_LBN 0
+#define FRF_AZ_RX_XOFF_MAC_EN_WIDTH 1
+
+/* RX_FILTER_CTL_REG: Receive filter control registers */
+#define FR_BZ_RX_FILTER_CTL 0x00000810
+#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_LBN 94
+#define FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT_WIDTH 8
+#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_LBN 86
+#define FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT_WIDTH 8
+#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_LBN 85
+#define FRF_CZ_RX_FILTER_ALL_VLAN_ETHERTYPES_WIDTH 1
+#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_LBN 69
+#define FRF_CZ_RX_VLAN_MATCH_ETHERTYPE_WIDTH 16
+#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_LBN 57
+#define FRF_CZ_MULTICAST_NOMATCH_Q_ID_WIDTH 12
+#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_LBN 56
+#define FRF_CZ_MULTICAST_NOMATCH_RSS_ENABLED_WIDTH 1
+#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_LBN 55
+#define FRF_CZ_MULTICAST_NOMATCH_IP_OVERRIDE_WIDTH 1
+#define FRF_CZ_UNICAST_NOMATCH_Q_ID_LBN 43
+#define FRF_CZ_UNICAST_NOMATCH_Q_ID_WIDTH 12
+#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_LBN 42
+#define FRF_CZ_UNICAST_NOMATCH_RSS_ENABLED_WIDTH 1
+#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_LBN 41
+#define FRF_CZ_UNICAST_NOMATCH_IP_OVERRIDE_WIDTH 1
+#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_LBN 40
+#define FRF_BZ_SCATTER_ENBL_NO_MATCH_Q_WIDTH 1
+#define FRF_BZ_UDP_FULL_SRCH_LIMIT_LBN 32
+#define FRF_BZ_UDP_FULL_SRCH_LIMIT_WIDTH 8
+#define FRF_BZ_NUM_KER_LBN 24
+#define FRF_BZ_NUM_KER_WIDTH 2
+#define FRF_BZ_UDP_WILD_SRCH_LIMIT_LBN 16
+#define FRF_BZ_UDP_WILD_SRCH_LIMIT_WIDTH 8
+#define FRF_BZ_TCP_WILD_SRCH_LIMIT_LBN 8
+#define FRF_BZ_TCP_WILD_SRCH_LIMIT_WIDTH 8
+#define FRF_BZ_TCP_FULL_SRCH_LIMIT_LBN 0
+#define FRF_BZ_TCP_FULL_SRCH_LIMIT_WIDTH 8
+
+/* RX_FLUSH_DESCQ_REG: Receive flush descriptor queue register */
+#define FR_AZ_RX_FLUSH_DESCQ 0x00000820
+#define FRF_AZ_RX_FLUSH_DESCQ_CMD_LBN 24
+#define FRF_AZ_RX_FLUSH_DESCQ_CMD_WIDTH 1
+#define FRF_AZ_RX_FLUSH_DESCQ_LBN 0
+#define FRF_AZ_RX_FLUSH_DESCQ_WIDTH 12
+
+/* RX_DESC_UPD_REGP0: Receive descriptor update register. */
+#define FR_BZ_RX_DESC_UPD_P0 0x00000830
+#define FR_BZ_RX_DESC_UPD_P0_STEP 8192
+#define FR_BZ_RX_DESC_UPD_P0_ROWS 1024
+/* RX_DESC_UPD_REG_KER: Receive descriptor update register. */
+#define FR_AA_RX_DESC_UPD_KER 0x00000830
+#define FR_AA_RX_DESC_UPD_KER_STEP 8192
+#define FR_AA_RX_DESC_UPD_KER_ROWS 4
+/* RX_DESC_UPD_REGP123: Receive descriptor update register. */
+#define FR_BB_RX_DESC_UPD_P123 0x01000830
+#define FR_BB_RX_DESC_UPD_P123_STEP 8192
+#define FR_BB_RX_DESC_UPD_P123_ROWS 3072
+#define FRF_AZ_RX_DESC_WPTR_LBN 96
+#define FRF_AZ_RX_DESC_WPTR_WIDTH 12
+#define FRF_AZ_RX_DESC_PUSH_CMD_LBN 95
+#define FRF_AZ_RX_DESC_PUSH_CMD_WIDTH 1
+#define FRF_AZ_RX_DESC_LBN 0
+#define FRF_AZ_RX_DESC_WIDTH 64
+
+/* RX_DC_CFG_REG: Receive descriptor cache configuration register */
+#define FR_AZ_RX_DC_CFG 0x00000840
+#define FRF_AB_RX_MAX_PF_LBN 2
+#define FRF_AB_RX_MAX_PF_WIDTH 2
+#define FRF_AZ_RX_DC_SIZE_LBN 0
+#define FRF_AZ_RX_DC_SIZE_WIDTH 2
+#define FFE_AZ_RX_DC_SIZE_64 3
+#define FFE_AZ_RX_DC_SIZE_32 2
+#define FFE_AZ_RX_DC_SIZE_16 1
+#define FFE_AZ_RX_DC_SIZE_8 0
+
+/* RX_DC_PF_WM_REG: Receive descriptor cache pre-fetch watermark register */
+#define FR_AZ_RX_DC_PF_WM 0x00000850
+#define FRF_AZ_RX_DC_PF_HWM_LBN 6
+#define FRF_AZ_RX_DC_PF_HWM_WIDTH 6
+#define FRF_AZ_RX_DC_PF_LWM_LBN 0
+#define FRF_AZ_RX_DC_PF_LWM_WIDTH 6
+
+/* RX_RSS_TKEY_REG: RSS Toeplitz hash key */
+#define FR_BZ_RX_RSS_TKEY 0x00000860
+#define FRF_BZ_RX_RSS_TKEY_HI_LBN 64
+#define FRF_BZ_RX_RSS_TKEY_HI_WIDTH 64
+#define FRF_BZ_RX_RSS_TKEY_LO_LBN 0
+#define FRF_BZ_RX_RSS_TKEY_LO_WIDTH 64
+
+/* RX_NODESC_DROP_REG: Receive dropped packet counter register */
+#define FR_AZ_RX_NODESC_DROP 0x00000880
+#define FRF_CZ_RX_NODESC_DROP_CNT_LBN 0
+#define FRF_CZ_RX_NODESC_DROP_CNT_WIDTH 32
+#define FRF_AB_RX_NODESC_DROP_CNT_LBN 0
+#define FRF_AB_RX_NODESC_DROP_CNT_WIDTH 16
+
+/* RX_SELF_RST_REG: Receive self reset register */
+#define FR_AA_RX_SELF_RST 0x00000890
+#define FRF_AA_RX_ISCSI_DIS_LBN 17
+#define FRF_AA_RX_ISCSI_DIS_WIDTH 1
+#define FRF_AA_RX_SW_RST_REG_LBN 16
+#define FRF_AA_RX_SW_RST_REG_WIDTH 1
+#define FRF_AA_RX_NODESC_WAIT_DIS_LBN 9
+#define FRF_AA_RX_NODESC_WAIT_DIS_WIDTH 1
+#define FRF_AA_RX_SELF_RST_EN_LBN 8
+#define FRF_AA_RX_SELF_RST_EN_WIDTH 1
+#define FRF_AA_RX_MAX_PF_LAT_LBN 4
+#define FRF_AA_RX_MAX_PF_LAT_WIDTH 4
+#define FRF_AA_RX_MAX_LU_LAT_LBN 0
+#define FRF_AA_RX_MAX_LU_LAT_WIDTH 4
+
+/* RX_DEBUG_REG: undocumented register */
+#define FR_AZ_RX_DEBUG 0x000008a0
+#define FRF_AZ_RX_DEBUG_LBN 0
+#define FRF_AZ_RX_DEBUG_WIDTH 64
+
+/* RX_PUSH_DROP_REG: Receive descriptor push dropped counter register */
+#define FR_AZ_RX_PUSH_DROP 0x000008b0
+#define FRF_AZ_RX_PUSH_DROP_CNT_LBN 0
+#define FRF_AZ_RX_PUSH_DROP_CNT_WIDTH 32
+
+/* RX_RSS_IPV6_REG1: IPv6 RSS Toeplitz hash key low bytes */
+#define FR_CZ_RX_RSS_IPV6_REG1 0x000008d0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_LBN 0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_LO_WIDTH 128
+
+/* RX_RSS_IPV6_REG2: IPv6 RSS Toeplitz hash key middle bytes */
+#define FR_CZ_RX_RSS_IPV6_REG2 0x000008e0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_LBN 0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_MID_WIDTH 128
+
+/* RX_RSS_IPV6_REG3: IPv6 RSS Toeplitz hash key upper bytes and IPv6 RSS settings */
+#define FR_CZ_RX_RSS_IPV6_REG3 0x000008f0
+#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_LBN 66
+#define FRF_CZ_RX_RSS_IPV6_THASH_ENABLE_WIDTH 1
+#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_LBN 65
+#define FRF_CZ_RX_RSS_IPV6_IP_THASH_ENABLE_WIDTH 1
+#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_LBN 64
+#define FRF_CZ_RX_RSS_IPV6_TCP_SUPPRESS_WIDTH 1
+#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_LBN 0
+#define FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH 64
+
+/* TX_FLUSH_DESCQ_REG: Transmit flush descriptor queue register */
+#define FR_AZ_TX_FLUSH_DESCQ 0x00000a00
+#define FRF_AZ_TX_FLUSH_DESCQ_CMD_LBN 12
+#define FRF_AZ_TX_FLUSH_DESCQ_CMD_WIDTH 1
+#define FRF_AZ_TX_FLUSH_DESCQ_LBN 0
+#define FRF_AZ_TX_FLUSH_DESCQ_WIDTH 12
+
+/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */
+#define FR_BZ_TX_DESC_UPD_P0 0x00000a10
+#define FR_BZ_TX_DESC_UPD_P0_STEP 8192
+#define FR_BZ_TX_DESC_UPD_P0_ROWS 1024
+/* TX_DESC_UPD_REG_KER: Transmit descriptor update register. */
+#define FR_AA_TX_DESC_UPD_KER 0x00000a10
+#define FR_AA_TX_DESC_UPD_KER_STEP 8192
+#define FR_AA_TX_DESC_UPD_KER_ROWS 8
+/* TX_DESC_UPD_REGP123: Transmit descriptor update register. */
+#define FR_BB_TX_DESC_UPD_P123 0x01000a10
+#define FR_BB_TX_DESC_UPD_P123_STEP 8192
+#define FR_BB_TX_DESC_UPD_P123_ROWS 3072
+#define FRF_AZ_TX_DESC_WPTR_LBN 96
+#define FRF_AZ_TX_DESC_WPTR_WIDTH 12
+#define FRF_AZ_TX_DESC_PUSH_CMD_LBN 95
+#define FRF_AZ_TX_DESC_PUSH_CMD_WIDTH 1
+#define FRF_AZ_TX_DESC_LBN 0
+#define FRF_AZ_TX_DESC_WIDTH 95
+
+/* TX_DC_CFG_REG: Transmit descriptor cache configuration register */
+#define FR_AZ_TX_DC_CFG 0x00000a20
+#define FRF_AZ_TX_DC_SIZE_LBN 0
+#define FRF_AZ_TX_DC_SIZE_WIDTH 2
+#define FFE_AZ_TX_DC_SIZE_32 2
+#define FFE_AZ_TX_DC_SIZE_16 1
+#define FFE_AZ_TX_DC_SIZE_8 0
+
+/* TX_CHKSM_CFG_REG: Transmit checksum configuration register */
+#define FR_AA_TX_CHKSM_CFG 0x00000a30
+#define FRF_AA_TX_Q_CHKSM_DIS_96_127_LBN 96
+#define FRF_AA_TX_Q_CHKSM_DIS_96_127_WIDTH 32
+#define FRF_AA_TX_Q_CHKSM_DIS_64_95_LBN 64
+#define FRF_AA_TX_Q_CHKSM_DIS_64_95_WIDTH 32
+#define FRF_AA_TX_Q_CHKSM_DIS_32_63_LBN 32
+#define FRF_AA_TX_Q_CHKSM_DIS_32_63_WIDTH 32
+#define FRF_AA_TX_Q_CHKSM_DIS_0_31_LBN 0
+#define FRF_AA_TX_Q_CHKSM_DIS_0_31_WIDTH 32
+
+/* TX_CFG_REG: Transmit configuration register */
+#define FR_AZ_TX_CFG 0x00000a50
+#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_LBN 114
+#define FRF_CZ_TX_CONT_LOOKUP_THRESH_RANGE_WIDTH 8
+#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_LBN 113
+#define FRF_CZ_TX_FILTER_TEST_MODE_BIT_WIDTH 1
+#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_LBN 105
+#define FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_LBN 97
+#define FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_LBN 89
+#define FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_LBN 81
+#define FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_LBN 73
+#define FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_LBN 65
+#define FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE_WIDTH 8
+#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_LBN 64
+#define FRF_CZ_TX_FILTER_ALL_VLAN_ETHERTYPES_BIT_WIDTH 1
+#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_LBN 48
+#define FRF_CZ_TX_VLAN_MATCH_ETHERTYPE_RANGE_WIDTH 16
+#define FRF_CZ_TX_FILTER_EN_BIT_LBN 47
+#define FRF_CZ_TX_FILTER_EN_BIT_WIDTH 1
+#define FRF_AZ_TX_IP_ID_P0_OFS_LBN 16
+#define FRF_AZ_TX_IP_ID_P0_OFS_WIDTH 15
+#define FRF_AZ_TX_NO_EOP_DISC_EN_LBN 5
+#define FRF_AZ_TX_NO_EOP_DISC_EN_WIDTH 1
+#define FRF_AZ_TX_P1_PRI_EN_LBN 4
+#define FRF_AZ_TX_P1_PRI_EN_WIDTH 1
+#define FRF_AZ_TX_OWNERR_CTL_LBN 2
+#define FRF_AZ_TX_OWNERR_CTL_WIDTH 1
+#define FRF_AA_TX_NON_IP_DROP_DIS_LBN 1
+#define FRF_AA_TX_NON_IP_DROP_DIS_WIDTH 1
+#define FRF_AZ_TX_IP_ID_REP_EN_LBN 0
+#define FRF_AZ_TX_IP_ID_REP_EN_WIDTH 1
+
+/* TX_PUSH_DROP_REG: Transmit push dropped register */
+#define FR_AZ_TX_PUSH_DROP 0x00000a60
+#define FRF_AZ_TX_PUSH_DROP_CNT_LBN 0
+#define FRF_AZ_TX_PUSH_DROP_CNT_WIDTH 32
+
+/* TX_RESERVED_REG: Transmit configuration register */
+#define FR_AZ_TX_RESERVED 0x00000a80
+#define FRF_AZ_TX_EVT_CNT_LBN 121
+#define FRF_AZ_TX_EVT_CNT_WIDTH 7
+#define FRF_AZ_TX_PREF_AGE_CNT_LBN 119
+#define FRF_AZ_TX_PREF_AGE_CNT_WIDTH 2
+#define FRF_AZ_TX_RD_COMP_TMR_LBN 96
+#define FRF_AZ_TX_RD_COMP_TMR_WIDTH 23
+#define FRF_AZ_TX_PUSH_EN_LBN 89
+#define FRF_AZ_TX_PUSH_EN_WIDTH 1
+#define FRF_AZ_TX_PUSH_CHK_DIS_LBN 88
+#define FRF_AZ_TX_PUSH_CHK_DIS_WIDTH 1
+#define FRF_AZ_TX_D_FF_FULL_P0_LBN 85
+#define FRF_AZ_TX_D_FF_FULL_P0_WIDTH 1
+#define FRF_AZ_TX_DMAR_ST_P0_LBN 81
+#define FRF_AZ_TX_DMAR_ST_P0_WIDTH 1
+#define FRF_AZ_TX_DMAQ_ST_LBN 78
+#define FRF_AZ_TX_DMAQ_ST_WIDTH 1
+#define FRF_AZ_TX_RX_SPACER_LBN 64
+#define FRF_AZ_TX_RX_SPACER_WIDTH 8
+#define FRF_AZ_TX_DROP_ABORT_EN_LBN 60
+#define FRF_AZ_TX_DROP_ABORT_EN_WIDTH 1
+#define FRF_AZ_TX_SOFT_EVT_EN_LBN 59
+#define FRF_AZ_TX_SOFT_EVT_EN_WIDTH 1
+#define FRF_AZ_TX_PS_EVT_DIS_LBN 58
+#define FRF_AZ_TX_PS_EVT_DIS_WIDTH 1
+#define FRF_AZ_TX_RX_SPACER_EN_LBN 57
+#define FRF_AZ_TX_RX_SPACER_EN_WIDTH 1
+#define FRF_AZ_TX_XP_TIMER_LBN 52
+#define FRF_AZ_TX_XP_TIMER_WIDTH 5
+#define FRF_AZ_TX_PREF_SPACER_LBN 44
+#define FRF_AZ_TX_PREF_SPACER_WIDTH 8
+#define FRF_AZ_TX_PREF_WD_TMR_LBN 22
+#define FRF_AZ_TX_PREF_WD_TMR_WIDTH 22
+#define FRF_AZ_TX_ONLY1TAG_LBN 21
+#define FRF_AZ_TX_ONLY1TAG_WIDTH 1
+#define FRF_AZ_TX_PREF_THRESHOLD_LBN 19
+#define FRF_AZ_TX_PREF_THRESHOLD_WIDTH 2
+#define FRF_AZ_TX_ONE_PKT_PER_Q_LBN 18
+#define FRF_AZ_TX_ONE_PKT_PER_Q_WIDTH 1
+#define FRF_AZ_TX_DIS_NON_IP_EV_LBN 17
+#define FRF_AZ_TX_DIS_NON_IP_EV_WIDTH 1
+#define FRF_AA_TX_DMA_FF_THR_LBN 16
+#define FRF_AA_TX_DMA_FF_THR_WIDTH 1
+#define FRF_AZ_TX_DMA_SPACER_LBN 8
+#define FRF_AZ_TX_DMA_SPACER_WIDTH 8
+#define FRF_AA_TX_TCP_DIS_LBN 7
+#define FRF_AA_TX_TCP_DIS_WIDTH 1
+#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_LBN 7
+#define FRF_BZ_TX_FLUSH_MIN_LEN_EN_WIDTH 1
+#define FRF_AA_TX_IP_DIS_LBN 6
+#define FRF_AA_TX_IP_DIS_WIDTH 1
+#define FRF_AZ_TX_MAX_CPL_LBN 2
+#define FRF_AZ_TX_MAX_CPL_WIDTH 2
+#define FFE_AZ_TX_MAX_CPL_16 3
+#define FFE_AZ_TX_MAX_CPL_8 2
+#define FFE_AZ_TX_MAX_CPL_4 1
+#define FFE_AZ_TX_MAX_CPL_NOLIMIT 0
+#define FRF_AZ_TX_MAX_PREF_LBN 0
+#define FRF_AZ_TX_MAX_PREF_WIDTH 2
+#define FFE_AZ_TX_MAX_PREF_32 3
+#define FFE_AZ_TX_MAX_PREF_16 2
+#define FFE_AZ_TX_MAX_PREF_8 1
+#define FFE_AZ_TX_MAX_PREF_OFF 0
+
+/* TX_PACE_REG: Transmit pace control register */
+#define FR_BZ_TX_PACE 0x00000a90
+#define FRF_BZ_TX_PACE_SB_NOT_AF_LBN 19
+#define FRF_BZ_TX_PACE_SB_NOT_AF_WIDTH 10
+#define FRF_BZ_TX_PACE_SB_AF_LBN 9
+#define FRF_BZ_TX_PACE_SB_AF_WIDTH 10
+#define FRF_BZ_TX_PACE_FB_BASE_LBN 5
+#define FRF_BZ_TX_PACE_FB_BASE_WIDTH 4
+#define FRF_BZ_TX_PACE_BIN_TH_LBN 0
+#define FRF_BZ_TX_PACE_BIN_TH_WIDTH 5
+
+/* TX_PACE_DROP_QID_REG: PACE Drop QID Counter */
+#define FR_BZ_TX_PACE_DROP_QID 0x00000aa0
+#define FRF_BZ_TX_PACE_QID_DRP_CNT_LBN 0
+#define FRF_BZ_TX_PACE_QID_DRP_CNT_WIDTH 16
+
+/* TX_VLAN_REG: Transmit VLAN tag register */
+#define FR_BB_TX_VLAN 0x00000ae0
+#define FRF_BB_TX_VLAN_EN_LBN 127
+#define FRF_BB_TX_VLAN_EN_WIDTH 1
+#define FRF_BB_TX_VLAN7_PORT1_EN_LBN 125
+#define FRF_BB_TX_VLAN7_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN7_PORT0_EN_LBN 124
+#define FRF_BB_TX_VLAN7_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN7_LBN 112
+#define FRF_BB_TX_VLAN7_WIDTH 12
+#define FRF_BB_TX_VLAN6_PORT1_EN_LBN 109
+#define FRF_BB_TX_VLAN6_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN6_PORT0_EN_LBN 108
+#define FRF_BB_TX_VLAN6_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN6_LBN 96
+#define FRF_BB_TX_VLAN6_WIDTH 12
+#define FRF_BB_TX_VLAN5_PORT1_EN_LBN 93
+#define FRF_BB_TX_VLAN5_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN5_PORT0_EN_LBN 92
+#define FRF_BB_TX_VLAN5_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN5_LBN 80
+#define FRF_BB_TX_VLAN5_WIDTH 12
+#define FRF_BB_TX_VLAN4_PORT1_EN_LBN 77
+#define FRF_BB_TX_VLAN4_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN4_PORT0_EN_LBN 76
+#define FRF_BB_TX_VLAN4_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN4_LBN 64
+#define FRF_BB_TX_VLAN4_WIDTH 12
+#define FRF_BB_TX_VLAN3_PORT1_EN_LBN 61
+#define FRF_BB_TX_VLAN3_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN3_PORT0_EN_LBN 60
+#define FRF_BB_TX_VLAN3_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN3_LBN 48
+#define FRF_BB_TX_VLAN3_WIDTH 12
+#define FRF_BB_TX_VLAN2_PORT1_EN_LBN 45
+#define FRF_BB_TX_VLAN2_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN2_PORT0_EN_LBN 44
+#define FRF_BB_TX_VLAN2_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN2_LBN 32
+#define FRF_BB_TX_VLAN2_WIDTH 12
+#define FRF_BB_TX_VLAN1_PORT1_EN_LBN 29
+#define FRF_BB_TX_VLAN1_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN1_PORT0_EN_LBN 28
+#define FRF_BB_TX_VLAN1_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN1_LBN 16
+#define FRF_BB_TX_VLAN1_WIDTH 12
+#define FRF_BB_TX_VLAN0_PORT1_EN_LBN 13
+#define FRF_BB_TX_VLAN0_PORT1_EN_WIDTH 1
+#define FRF_BB_TX_VLAN0_PORT0_EN_LBN 12
+#define FRF_BB_TX_VLAN0_PORT0_EN_WIDTH 1
+#define FRF_BB_TX_VLAN0_LBN 0
+#define FRF_BB_TX_VLAN0_WIDTH 12
+
+/* TX_IPFIL_PORTEN_REG: Transmit filter control register */
+#define FR_BZ_TX_IPFIL_PORTEN 0x00000af0
+#define FRF_BZ_TX_MADR0_FIL_EN_LBN 64
+#define FRF_BZ_TX_MADR0_FIL_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL31_PORT_EN_LBN 62
+#define FRF_BB_TX_IPFIL31_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL30_PORT_EN_LBN 60
+#define FRF_BB_TX_IPFIL30_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL29_PORT_EN_LBN 58
+#define FRF_BB_TX_IPFIL29_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL28_PORT_EN_LBN 56
+#define FRF_BB_TX_IPFIL28_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL27_PORT_EN_LBN 54
+#define FRF_BB_TX_IPFIL27_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL26_PORT_EN_LBN 52
+#define FRF_BB_TX_IPFIL26_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL25_PORT_EN_LBN 50
+#define FRF_BB_TX_IPFIL25_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL24_PORT_EN_LBN 48
+#define FRF_BB_TX_IPFIL24_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL23_PORT_EN_LBN 46
+#define FRF_BB_TX_IPFIL23_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL22_PORT_EN_LBN 44
+#define FRF_BB_TX_IPFIL22_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL21_PORT_EN_LBN 42
+#define FRF_BB_TX_IPFIL21_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL20_PORT_EN_LBN 40
+#define FRF_BB_TX_IPFIL20_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL19_PORT_EN_LBN 38
+#define FRF_BB_TX_IPFIL19_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL18_PORT_EN_LBN 36
+#define FRF_BB_TX_IPFIL18_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL17_PORT_EN_LBN 34
+#define FRF_BB_TX_IPFIL17_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL16_PORT_EN_LBN 32
+#define FRF_BB_TX_IPFIL16_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL15_PORT_EN_LBN 30
+#define FRF_BB_TX_IPFIL15_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL14_PORT_EN_LBN 28
+#define FRF_BB_TX_IPFIL14_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL13_PORT_EN_LBN 26
+#define FRF_BB_TX_IPFIL13_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL12_PORT_EN_LBN 24
+#define FRF_BB_TX_IPFIL12_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL11_PORT_EN_LBN 22
+#define FRF_BB_TX_IPFIL11_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL10_PORT_EN_LBN 20
+#define FRF_BB_TX_IPFIL10_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL9_PORT_EN_LBN 18
+#define FRF_BB_TX_IPFIL9_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL8_PORT_EN_LBN 16
+#define FRF_BB_TX_IPFIL8_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL7_PORT_EN_LBN 14
+#define FRF_BB_TX_IPFIL7_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL6_PORT_EN_LBN 12
+#define FRF_BB_TX_IPFIL6_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL5_PORT_EN_LBN 10
+#define FRF_BB_TX_IPFIL5_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL4_PORT_EN_LBN 8
+#define FRF_BB_TX_IPFIL4_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL3_PORT_EN_LBN 6
+#define FRF_BB_TX_IPFIL3_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL2_PORT_EN_LBN 4
+#define FRF_BB_TX_IPFIL2_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL1_PORT_EN_LBN 2
+#define FRF_BB_TX_IPFIL1_PORT_EN_WIDTH 1
+#define FRF_BB_TX_IPFIL0_PORT_EN_LBN 0
+#define FRF_BB_TX_IPFIL0_PORT_EN_WIDTH 1
+
+/* TX_IPFIL_TBL: Transmit IP source address filter table */
+#define FR_BB_TX_IPFIL_TBL 0x00000b00
+#define FR_BB_TX_IPFIL_TBL_STEP 16
+#define FR_BB_TX_IPFIL_TBL_ROWS 16
+#define FRF_BB_TX_IPFIL_MASK_1_LBN 96
+#define FRF_BB_TX_IPFIL_MASK_1_WIDTH 32
+#define FRF_BB_TX_IP_SRC_ADR_1_LBN 64
+#define FRF_BB_TX_IP_SRC_ADR_1_WIDTH 32
+#define FRF_BB_TX_IPFIL_MASK_0_LBN 32
+#define FRF_BB_TX_IPFIL_MASK_0_WIDTH 32
+#define FRF_BB_TX_IP_SRC_ADR_0_LBN 0
+#define FRF_BB_TX_IP_SRC_ADR_0_WIDTH 32
+
+/* MD_TXD_REG: PHY management transmit data register */
+#define FR_AB_MD_TXD 0x00000c00
+#define FRF_AB_MD_TXD_LBN 0
+#define FRF_AB_MD_TXD_WIDTH 16
+
+/* MD_RXD_REG: PHY management receive data register */
+#define FR_AB_MD_RXD 0x00000c10
+#define FRF_AB_MD_RXD_LBN 0
+#define FRF_AB_MD_RXD_WIDTH 16
+
+/* MD_CS_REG: PHY management configuration & status register */
+#define FR_AB_MD_CS 0x00000c20
+#define FRF_AB_MD_RD_EN_CMD_LBN 15
+#define FRF_AB_MD_RD_EN_CMD_WIDTH 1
+#define FRF_AB_MD_WR_EN_CMD_LBN 14
+#define FRF_AB_MD_WR_EN_CMD_WIDTH 1
+#define FRF_AB_MD_ADDR_CMD_LBN 13
+#define FRF_AB_MD_ADDR_CMD_WIDTH 1
+#define FRF_AB_MD_PT_LBN 7
+#define FRF_AB_MD_PT_WIDTH 3
+#define FRF_AB_MD_PL_LBN 6
+#define FRF_AB_MD_PL_WIDTH 1
+#define FRF_AB_MD_INT_CLR_LBN 5
+#define FRF_AB_MD_INT_CLR_WIDTH 1
+#define FRF_AB_MD_GC_LBN 4
+#define FRF_AB_MD_GC_WIDTH 1
+#define FRF_AB_MD_PRSP_LBN 3
+#define FRF_AB_MD_PRSP_WIDTH 1
+#define FRF_AB_MD_RIC_LBN 2
+#define FRF_AB_MD_RIC_WIDTH 1
+#define FRF_AB_MD_RDC_LBN 1
+#define FRF_AB_MD_RDC_WIDTH 1
+#define FRF_AB_MD_WRC_LBN 0
+#define FRF_AB_MD_WRC_WIDTH 1
+
+/* MD_PHY_ADR_REG: PHY management PHY address register */
+#define FR_AB_MD_PHY_ADR 0x00000c30
+#define FRF_AB_MD_PHY_ADR_LBN 0
+#define FRF_AB_MD_PHY_ADR_WIDTH 16
+
+/* MD_ID_REG: PHY management ID register */
+#define FR_AB_MD_ID 0x00000c40
+#define FRF_AB_MD_PRT_ADR_LBN 11
+#define FRF_AB_MD_PRT_ADR_WIDTH 5
+#define FRF_AB_MD_DEV_ADR_LBN 6
+#define FRF_AB_MD_DEV_ADR_WIDTH 5
+
+/* MD_STAT_REG: PHY management status & mask register */
+#define FR_AB_MD_STAT 0x00000c50
+#define FRF_AB_MD_PINT_LBN 4
+#define FRF_AB_MD_PINT_WIDTH 1
+#define FRF_AB_MD_DONE_LBN 3
+#define FRF_AB_MD_DONE_WIDTH 1
+#define FRF_AB_MD_BSERR_LBN 2
+#define FRF_AB_MD_BSERR_WIDTH 1
+#define FRF_AB_MD_LNFL_LBN 1
+#define FRF_AB_MD_LNFL_WIDTH 1
+#define FRF_AB_MD_BSY_LBN 0
+#define FRF_AB_MD_BSY_WIDTH 1
+
+/* MAC_STAT_DMA_REG: Port MAC statistical counter DMA register */
+#define FR_AB_MAC_STAT_DMA 0x00000c60
+#define FRF_AB_MAC_STAT_DMA_CMD_LBN 48
+#define FRF_AB_MAC_STAT_DMA_CMD_WIDTH 1
+#define FRF_AB_MAC_STAT_DMA_ADR_LBN 0
+#define FRF_AB_MAC_STAT_DMA_ADR_WIDTH 48
+
+/* MAC_CTRL_REG: Port MAC control register */
+#define FR_AB_MAC_CTRL 0x00000c80
+#define FRF_AB_MAC_XOFF_VAL_LBN 16
+#define FRF_AB_MAC_XOFF_VAL_WIDTH 16
+#define FRF_BB_TXFIFO_DRAIN_EN_LBN 7
+#define FRF_BB_TXFIFO_DRAIN_EN_WIDTH 1
+#define FRF_AB_MAC_XG_DISTXCRC_LBN 5
+#define FRF_AB_MAC_XG_DISTXCRC_WIDTH 1
+#define FRF_AB_MAC_BCAD_ACPT_LBN 4
+#define FRF_AB_MAC_BCAD_ACPT_WIDTH 1
+#define FRF_AB_MAC_UC_PROM_LBN 3
+#define FRF_AB_MAC_UC_PROM_WIDTH 1
+#define FRF_AB_MAC_LINK_STATUS_LBN 2
+#define FRF_AB_MAC_LINK_STATUS_WIDTH 1
+#define FRF_AB_MAC_SPEED_LBN 0
+#define FRF_AB_MAC_SPEED_WIDTH 2
+#define FFE_AB_MAC_SPEED_10G 3
+#define FFE_AB_MAC_SPEED_1G 2
+#define FFE_AB_MAC_SPEED_100M 1
+#define FFE_AB_MAC_SPEED_10M 0
+
+/* GEN_MODE_REG: General Purpose mode register (external interrupt mask) */
+#define FR_BB_GEN_MODE 0x00000c90
+#define FRF_BB_XFP_PHY_INT_POL_SEL_LBN 3
+#define FRF_BB_XFP_PHY_INT_POL_SEL_WIDTH 1
+#define FRF_BB_XG_PHY_INT_POL_SEL_LBN 2
+#define FRF_BB_XG_PHY_INT_POL_SEL_WIDTH 1
+#define FRF_BB_XFP_PHY_INT_MASK_LBN 1
+#define FRF_BB_XFP_PHY_INT_MASK_WIDTH 1
+#define FRF_BB_XG_PHY_INT_MASK_LBN 0
+#define FRF_BB_XG_PHY_INT_MASK_WIDTH 1
+
+/* MAC_MC_HASH_REG0: Multicast address hash table */
+#define FR_AB_MAC_MC_HASH_REG0 0x00000ca0
+#define FRF_AB_MAC_MCAST_HASH0_LBN 0
+#define FRF_AB_MAC_MCAST_HASH0_WIDTH 128
+
+/* MAC_MC_HASH_REG1: Multicast address hash table */
+#define FR_AB_MAC_MC_HASH_REG1 0x00000cb0
+#define FRF_AB_MAC_MCAST_HASH1_LBN 0
+#define FRF_AB_MAC_MCAST_HASH1_WIDTH 128
+
+/* GM_CFG1_REG: GMAC configuration register 1 */
+#define FR_AB_GM_CFG1 0x00000e00
+#define FRF_AB_GM_SW_RST_LBN 31
+#define FRF_AB_GM_SW_RST_WIDTH 1
+#define FRF_AB_GM_SIM_RST_LBN 30
+#define FRF_AB_GM_SIM_RST_WIDTH 1
+#define FRF_AB_GM_RST_RX_MAC_CTL_LBN 19
+#define FRF_AB_GM_RST_RX_MAC_CTL_WIDTH 1
+#define FRF_AB_GM_RST_TX_MAC_CTL_LBN 18
+#define FRF_AB_GM_RST_TX_MAC_CTL_WIDTH 1
+#define FRF_AB_GM_RST_RX_FUNC_LBN 17
+#define FRF_AB_GM_RST_RX_FUNC_WIDTH 1
+#define FRF_AB_GM_RST_TX_FUNC_LBN 16
+#define FRF_AB_GM_RST_TX_FUNC_WIDTH 1
+#define FRF_AB_GM_LOOP_LBN 8
+#define FRF_AB_GM_LOOP_WIDTH 1
+#define FRF_AB_GM_RX_FC_EN_LBN 5
+#define FRF_AB_GM_RX_FC_EN_WIDTH 1
+#define FRF_AB_GM_TX_FC_EN_LBN 4
+#define FRF_AB_GM_TX_FC_EN_WIDTH 1
+#define FRF_AB_GM_SYNC_RXEN_LBN 3
+#define FRF_AB_GM_SYNC_RXEN_WIDTH 1
+#define FRF_AB_GM_RX_EN_LBN 2
+#define FRF_AB_GM_RX_EN_WIDTH 1
+#define FRF_AB_GM_SYNC_TXEN_LBN 1
+#define FRF_AB_GM_SYNC_TXEN_WIDTH 1
+#define FRF_AB_GM_TX_EN_LBN 0
+#define FRF_AB_GM_TX_EN_WIDTH 1
+
+/* GM_CFG2_REG: GMAC configuration register 2 */
+#define FR_AB_GM_CFG2 0x00000e10
+#define FRF_AB_GM_PAMBL_LEN_LBN 12
+#define FRF_AB_GM_PAMBL_LEN_WIDTH 4
+#define FRF_AB_GM_IF_MODE_LBN 8
+#define FRF_AB_GM_IF_MODE_WIDTH 2
+#define FFE_AB_IF_MODE_BYTE_MODE 2
+#define FFE_AB_IF_MODE_NIBBLE_MODE 1
+#define FRF_AB_GM_HUGE_FRM_EN_LBN 5
+#define FRF_AB_GM_HUGE_FRM_EN_WIDTH 1
+#define FRF_AB_GM_LEN_CHK_LBN 4
+#define FRF_AB_GM_LEN_CHK_WIDTH 1
+#define FRF_AB_GM_PAD_CRC_EN_LBN 2
+#define FRF_AB_GM_PAD_CRC_EN_WIDTH 1
+#define FRF_AB_GM_CRC_EN_LBN 1
+#define FRF_AB_GM_CRC_EN_WIDTH 1
+#define FRF_AB_GM_FD_LBN 0
+#define FRF_AB_GM_FD_WIDTH 1
+
+/* GM_IPG_REG: GMAC IPG register */
+#define FR_AB_GM_IPG 0x00000e20
+#define FRF_AB_GM_NONB2B_IPG1_LBN 24
+#define FRF_AB_GM_NONB2B_IPG1_WIDTH 7
+#define FRF_AB_GM_NONB2B_IPG2_LBN 16
+#define FRF_AB_GM_NONB2B_IPG2_WIDTH 7
+#define FRF_AB_GM_MIN_IPG_ENF_LBN 8
+#define FRF_AB_GM_MIN_IPG_ENF_WIDTH 8
+#define FRF_AB_GM_B2B_IPG_LBN 0
+#define FRF_AB_GM_B2B_IPG_WIDTH 7
+
+/* GM_HD_REG: GMAC half duplex register */
+#define FR_AB_GM_HD 0x00000e30
+#define FRF_AB_GM_ALT_BOFF_VAL_LBN 20
+#define FRF_AB_GM_ALT_BOFF_VAL_WIDTH 4
+#define FRF_AB_GM_ALT_BOFF_EN_LBN 19
+#define FRF_AB_GM_ALT_BOFF_EN_WIDTH 1
+#define FRF_AB_GM_BP_NO_BOFF_LBN 18
+#define FRF_AB_GM_BP_NO_BOFF_WIDTH 1
+#define FRF_AB_GM_DIS_BOFF_LBN 17
+#define FRF_AB_GM_DIS_BOFF_WIDTH 1
+#define FRF_AB_GM_EXDEF_TX_EN_LBN 16
+#define FRF_AB_GM_EXDEF_TX_EN_WIDTH 1
+#define FRF_AB_GM_RTRY_LIMIT_LBN 12
+#define FRF_AB_GM_RTRY_LIMIT_WIDTH 4
+#define FRF_AB_GM_COL_WIN_LBN 0
+#define FRF_AB_GM_COL_WIN_WIDTH 10
+
+/* GM_MAX_FLEN_REG: GMAC maximum frame length register */
+#define FR_AB_GM_MAX_FLEN 0x00000e40
+#define FRF_AB_GM_MAX_FLEN_LBN 0
+#define FRF_AB_GM_MAX_FLEN_WIDTH 16
+
+/* GM_TEST_REG: GMAC test register */
+#define FR_AB_GM_TEST 0x00000e70
+#define FRF_AB_GM_MAX_BOFF_LBN 3
+#define FRF_AB_GM_MAX_BOFF_WIDTH 1
+#define FRF_AB_GM_REG_TX_FLOW_EN_LBN 2
+#define FRF_AB_GM_REG_TX_FLOW_EN_WIDTH 1
+#define FRF_AB_GM_TEST_PAUSE_LBN 1
+#define FRF_AB_GM_TEST_PAUSE_WIDTH 1
+#define FRF_AB_GM_SHORT_SLOT_LBN 0
+#define FRF_AB_GM_SHORT_SLOT_WIDTH 1
+
+/* GM_ADR1_REG: GMAC station address register 1 */
+#define FR_AB_GM_ADR1 0x00000f00
+#define FRF_AB_GM_ADR_B0_LBN 24
+#define FRF_AB_GM_ADR_B0_WIDTH 8
+#define FRF_AB_GM_ADR_B1_LBN 16
+#define FRF_AB_GM_ADR_B1_WIDTH 8
+#define FRF_AB_GM_ADR_B2_LBN 8
+#define FRF_AB_GM_ADR_B2_WIDTH 8
+#define FRF_AB_GM_ADR_B3_LBN 0
+#define FRF_AB_GM_ADR_B3_WIDTH 8
+
+/* GM_ADR2_REG: GMAC station address register 2 */
+#define FR_AB_GM_ADR2 0x00000f10
+#define FRF_AB_GM_ADR_B4_LBN 24
+#define FRF_AB_GM_ADR_B4_WIDTH 8
+#define FRF_AB_GM_ADR_B5_LBN 16
+#define FRF_AB_GM_ADR_B5_WIDTH 8
+
+/* GMF_CFG0_REG: GMAC FIFO configuration register 0 */
+#define FR_AB_GMF_CFG0 0x00000f20
+#define FRF_AB_GMF_FTFENRPLY_LBN 20
+#define FRF_AB_GMF_FTFENRPLY_WIDTH 1
+#define FRF_AB_GMF_STFENRPLY_LBN 19
+#define FRF_AB_GMF_STFENRPLY_WIDTH 1
+#define FRF_AB_GMF_FRFENRPLY_LBN 18
+#define FRF_AB_GMF_FRFENRPLY_WIDTH 1
+#define FRF_AB_GMF_SRFENRPLY_LBN 17
+#define FRF_AB_GMF_SRFENRPLY_WIDTH 1
+#define FRF_AB_GMF_WTMENRPLY_LBN 16
+#define FRF_AB_GMF_WTMENRPLY_WIDTH 1
+#define FRF_AB_GMF_FTFENREQ_LBN 12
+#define FRF_AB_GMF_FTFENREQ_WIDTH 1
+#define FRF_AB_GMF_STFENREQ_LBN 11
+#define FRF_AB_GMF_STFENREQ_WIDTH 1
+#define FRF_AB_GMF_FRFENREQ_LBN 10
+#define FRF_AB_GMF_FRFENREQ_WIDTH 1
+#define FRF_AB_GMF_SRFENREQ_LBN 9
+#define FRF_AB_GMF_SRFENREQ_WIDTH 1
+#define FRF_AB_GMF_WTMENREQ_LBN 8
+#define FRF_AB_GMF_WTMENREQ_WIDTH 1
+#define FRF_AB_GMF_HSTRSTFT_LBN 4
+#define FRF_AB_GMF_HSTRSTFT_WIDTH 1
+#define FRF_AB_GMF_HSTRSTST_LBN 3
+#define FRF_AB_GMF_HSTRSTST_WIDTH 1
+#define FRF_AB_GMF_HSTRSTFR_LBN 2
+#define FRF_AB_GMF_HSTRSTFR_WIDTH 1
+#define FRF_AB_GMF_HSTRSTSR_LBN 1
+#define FRF_AB_GMF_HSTRSTSR_WIDTH 1
+#define FRF_AB_GMF_HSTRSTWT_LBN 0
+#define FRF_AB_GMF_HSTRSTWT_WIDTH 1
+
+/* GMF_CFG1_REG: GMAC FIFO configuration register 1 */
+#define FR_AB_GMF_CFG1 0x00000f30
+#define FRF_AB_GMF_CFGFRTH_LBN 16
+#define FRF_AB_GMF_CFGFRTH_WIDTH 5
+#define FRF_AB_GMF_CFGXOFFRTX_LBN 0
+#define FRF_AB_GMF_CFGXOFFRTX_WIDTH 16
+
+/* GMF_CFG2_REG: GMAC FIFO configuration register 2 */
+#define FR_AB_GMF_CFG2 0x00000f40
+#define FRF_AB_GMF_CFGHWM_LBN 16
+#define FRF_AB_GMF_CFGHWM_WIDTH 6
+#define FRF_AB_GMF_CFGLWM_LBN 0
+#define FRF_AB_GMF_CFGLWM_WIDTH 6
+
+/* GMF_CFG3_REG: GMAC FIFO configuration register 3 */
+#define FR_AB_GMF_CFG3 0x00000f50
+#define FRF_AB_GMF_CFGHWMFT_LBN 16
+#define FRF_AB_GMF_CFGHWMFT_WIDTH 6
+#define FRF_AB_GMF_CFGFTTH_LBN 0
+#define FRF_AB_GMF_CFGFTTH_WIDTH 6
+
+/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */
+#define FR_AB_GMF_CFG4 0x00000f60
+#define FRF_AB_GMF_HSTFLTRFRM_LBN 0
+#define FRF_AB_GMF_HSTFLTRFRM_WIDTH 18
+
+/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */
+#define FR_AB_GMF_CFG5 0x00000f70
+#define FRF_AB_GMF_CFGHDPLX_LBN 22
+#define FRF_AB_GMF_CFGHDPLX_WIDTH 1
+#define FRF_AB_GMF_SRFULL_LBN 21
+#define FRF_AB_GMF_SRFULL_WIDTH 1
+#define FRF_AB_GMF_HSTSRFULLCLR_LBN 20
+#define FRF_AB_GMF_HSTSRFULLCLR_WIDTH 1
+#define FRF_AB_GMF_CFGBYTMODE_LBN 19
+#define FRF_AB_GMF_CFGBYTMODE_WIDTH 1
+#define FRF_AB_GMF_HSTDRPLT64_LBN 18
+#define FRF_AB_GMF_HSTDRPLT64_WIDTH 1
+#define FRF_AB_GMF_HSTFLTRFRMDC_LBN 0
+#define FRF_AB_GMF_HSTFLTRFRMDC_WIDTH 18
+
+/* TX_SRC_MAC_TBL: Transmit IP source address filter table */
+#define FR_BB_TX_SRC_MAC_TBL 0x00001000
+#define FR_BB_TX_SRC_MAC_TBL_STEP 16
+#define FR_BB_TX_SRC_MAC_TBL_ROWS 16
+#define FRF_BB_TX_SRC_MAC_ADR_1_LBN 64
+#define FRF_BB_TX_SRC_MAC_ADR_1_WIDTH 48
+#define FRF_BB_TX_SRC_MAC_ADR_0_LBN 0
+#define FRF_BB_TX_SRC_MAC_ADR_0_WIDTH 48
+
+/* TX_SRC_MAC_CTL_REG: Transmit MAC source address filter control */
+#define FR_BB_TX_SRC_MAC_CTL 0x00001100
+#define FRF_BB_TX_SRC_DROP_CTR_LBN 16
+#define FRF_BB_TX_SRC_DROP_CTR_WIDTH 16
+#define FRF_BB_TX_SRC_FLTR_EN_LBN 15
+#define FRF_BB_TX_SRC_FLTR_EN_WIDTH 1
+#define FRF_BB_TX_DROP_CTR_CLR_LBN 12
+#define FRF_BB_TX_DROP_CTR_CLR_WIDTH 1
+#define FRF_BB_TX_MAC_QID_SEL_LBN 0
+#define FRF_BB_TX_MAC_QID_SEL_WIDTH 3
+
+/* XM_ADR_LO_REG: XGMAC address register low */
+#define FR_AB_XM_ADR_LO 0x00001200
+#define FRF_AB_XM_ADR_LO_LBN 0
+#define FRF_AB_XM_ADR_LO_WIDTH 32
+
+/* XM_ADR_HI_REG: XGMAC address register high */
+#define FR_AB_XM_ADR_HI 0x00001210
+#define FRF_AB_XM_ADR_HI_LBN 0
+#define FRF_AB_XM_ADR_HI_WIDTH 16
+
+/* XM_GLB_CFG_REG: XGMAC global configuration */
+#define FR_AB_XM_GLB_CFG 0x00001220
+#define FRF_AB_XM_RMTFLT_GEN_LBN 17
+#define FRF_AB_XM_RMTFLT_GEN_WIDTH 1
+#define FRF_AB_XM_DEBUG_MODE_LBN 16
+#define FRF_AB_XM_DEBUG_MODE_WIDTH 1
+#define FRF_AB_XM_RX_STAT_EN_LBN 11
+#define FRF_AB_XM_RX_STAT_EN_WIDTH 1
+#define FRF_AB_XM_TX_STAT_EN_LBN 10
+#define FRF_AB_XM_TX_STAT_EN_WIDTH 1
+#define FRF_AB_XM_RX_JUMBO_MODE_LBN 6
+#define FRF_AB_XM_RX_JUMBO_MODE_WIDTH 1
+#define FRF_AB_XM_WAN_MODE_LBN 5
+#define FRF_AB_XM_WAN_MODE_WIDTH 1
+#define FRF_AB_XM_INTCLR_MODE_LBN 3
+#define FRF_AB_XM_INTCLR_MODE_WIDTH 1
+#define FRF_AB_XM_CORE_RST_LBN 0
+#define FRF_AB_XM_CORE_RST_WIDTH 1
+
+/* XM_TX_CFG_REG: XGMAC transmit configuration */
+#define FR_AB_XM_TX_CFG 0x00001230
+#define FRF_AB_XM_TX_PROG_LBN 24
+#define FRF_AB_XM_TX_PROG_WIDTH 1
+#define FRF_AB_XM_IPG_LBN 16
+#define FRF_AB_XM_IPG_WIDTH 4
+#define FRF_AB_XM_FCNTL_LBN 10
+#define FRF_AB_XM_FCNTL_WIDTH 1
+#define FRF_AB_XM_TXCRC_LBN 8
+#define FRF_AB_XM_TXCRC_WIDTH 1
+#define FRF_AB_XM_EDRC_LBN 6
+#define FRF_AB_XM_EDRC_WIDTH 1
+#define FRF_AB_XM_AUTO_PAD_LBN 5
+#define FRF_AB_XM_AUTO_PAD_WIDTH 1
+#define FRF_AB_XM_TX_PRMBL_LBN 2
+#define FRF_AB_XM_TX_PRMBL_WIDTH 1
+#define FRF_AB_XM_TXEN_LBN 1
+#define FRF_AB_XM_TXEN_WIDTH 1
+#define FRF_AB_XM_TX_RST_LBN 0
+#define FRF_AB_XM_TX_RST_WIDTH 1
+
+/* XM_RX_CFG_REG: XGMAC receive configuration */
+#define FR_AB_XM_RX_CFG 0x00001240
+#define FRF_AB_XM_PASS_LENERR_LBN 26
+#define FRF_AB_XM_PASS_LENERR_WIDTH 1
+#define FRF_AB_XM_PASS_CRC_ERR_LBN 25
+#define FRF_AB_XM_PASS_CRC_ERR_WIDTH 1
+#define FRF_AB_XM_PASS_PRMBLE_ERR_LBN 24
+#define FRF_AB_XM_PASS_PRMBLE_ERR_WIDTH 1
+#define FRF_AB_XM_REJ_BCAST_LBN 20
+#define FRF_AB_XM_REJ_BCAST_WIDTH 1
+#define FRF_AB_XM_ACPT_ALL_MCAST_LBN 11
+#define FRF_AB_XM_ACPT_ALL_MCAST_WIDTH 1
+#define FRF_AB_XM_ACPT_ALL_UCAST_LBN 9
+#define FRF_AB_XM_ACPT_ALL_UCAST_WIDTH 1
+#define FRF_AB_XM_AUTO_DEPAD_LBN 8
+#define FRF_AB_XM_AUTO_DEPAD_WIDTH 1
+#define FRF_AB_XM_RXCRC_LBN 3
+#define FRF_AB_XM_RXCRC_WIDTH 1
+#define FRF_AB_XM_RX_PRMBL_LBN 2
+#define FRF_AB_XM_RX_PRMBL_WIDTH 1
+#define FRF_AB_XM_RXEN_LBN 1
+#define FRF_AB_XM_RXEN_WIDTH 1
+#define FRF_AB_XM_RX_RST_LBN 0
+#define FRF_AB_XM_RX_RST_WIDTH 1
+
+/* XM_MGT_INT_MASK: documentation to be written for sum_XM_MGT_INT_MASK */
+#define FR_AB_XM_MGT_INT_MASK 0x00001250
+#define FRF_AB_XM_MSK_STA_INTR_LBN 16
+#define FRF_AB_XM_MSK_STA_INTR_WIDTH 1
+#define FRF_AB_XM_MSK_STAT_CNTR_HF_LBN 9
+#define FRF_AB_XM_MSK_STAT_CNTR_HF_WIDTH 1
+#define FRF_AB_XM_MSK_STAT_CNTR_OF_LBN 8
+#define FRF_AB_XM_MSK_STAT_CNTR_OF_WIDTH 1
+#define FRF_AB_XM_MSK_PRMBLE_ERR_LBN 2
+#define FRF_AB_XM_MSK_PRMBLE_ERR_WIDTH 1
+#define FRF_AB_XM_MSK_RMTFLT_LBN 1
+#define FRF_AB_XM_MSK_RMTFLT_WIDTH 1
+#define FRF_AB_XM_MSK_LCLFLT_LBN 0
+#define FRF_AB_XM_MSK_LCLFLT_WIDTH 1
+
+/* XM_FC_REG: XGMAC flow control register */
+#define FR_AB_XM_FC 0x00001270
+#define FRF_AB_XM_PAUSE_TIME_LBN 16
+#define FRF_AB_XM_PAUSE_TIME_WIDTH 16
+#define FRF_AB_XM_RX_MAC_STAT_LBN 11
+#define FRF_AB_XM_RX_MAC_STAT_WIDTH 1
+#define FRF_AB_XM_TX_MAC_STAT_LBN 10
+#define FRF_AB_XM_TX_MAC_STAT_WIDTH 1
+#define FRF_AB_XM_MCNTL_PASS_LBN 8
+#define FRF_AB_XM_MCNTL_PASS_WIDTH 2
+#define FRF_AB_XM_REJ_CNTL_UCAST_LBN 6
+#define FRF_AB_XM_REJ_CNTL_UCAST_WIDTH 1
+#define FRF_AB_XM_REJ_CNTL_MCAST_LBN 5
+#define FRF_AB_XM_REJ_CNTL_MCAST_WIDTH 1
+#define FRF_AB_XM_ZPAUSE_LBN 2
+#define FRF_AB_XM_ZPAUSE_WIDTH 1
+#define FRF_AB_XM_XMIT_PAUSE_LBN 1
+#define FRF_AB_XM_XMIT_PAUSE_WIDTH 1
+#define FRF_AB_XM_DIS_FCNTL_LBN 0
+#define FRF_AB_XM_DIS_FCNTL_WIDTH 1
+
+/* XM_PAUSE_TIME_REG: XGMAC pause time register */
+#define FR_AB_XM_PAUSE_TIME 0x00001290
+#define FRF_AB_XM_TX_PAUSE_CNT_LBN 16
+#define FRF_AB_XM_TX_PAUSE_CNT_WIDTH 16
+#define FRF_AB_XM_RX_PAUSE_CNT_LBN 0
+#define FRF_AB_XM_RX_PAUSE_CNT_WIDTH 16
+
+/* XM_TX_PARAM_REG: XGMAC transmit parameter register */
+#define FR_AB_XM_TX_PARAM 0x000012d0
+#define FRF_AB_XM_TX_JUMBO_MODE_LBN 31
+#define FRF_AB_XM_TX_JUMBO_MODE_WIDTH 1
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_LBN 19
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH 11
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN 16
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH 3
+#define FRF_AB_XM_PAD_CHAR_LBN 0
+#define FRF_AB_XM_PAD_CHAR_WIDTH 8
+
+/* XM_RX_PARAM_REG: XGMAC receive parameter register */
+#define FR_AB_XM_RX_PARAM 0x000012e0
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_LBN 3
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH 11
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN 0
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH 3
+
+/* XM_MGT_INT_MSK_REG: XGMAC management interrupt mask register */
+#define FR_AB_XM_MGT_INT_MSK 0x000012f0
+#define FRF_AB_XM_STAT_CNTR_OF_LBN 9
+#define FRF_AB_XM_STAT_CNTR_OF_WIDTH 1
+#define FRF_AB_XM_STAT_CNTR_HF_LBN 8
+#define FRF_AB_XM_STAT_CNTR_HF_WIDTH 1
+#define FRF_AB_XM_PRMBLE_ERR_LBN 2
+#define FRF_AB_XM_PRMBLE_ERR_WIDTH 1
+#define FRF_AB_XM_RMTFLT_LBN 1
+#define FRF_AB_XM_RMTFLT_WIDTH 1
+#define FRF_AB_XM_LCLFLT_LBN 0
+#define FRF_AB_XM_LCLFLT_WIDTH 1
+
+/* XX_PWR_RST_REG: XGXS/XAUI powerdown/reset register */
+#define FR_AB_XX_PWR_RST 0x00001300
+#define FRF_AB_XX_PWRDND_SIG_LBN 31
+#define FRF_AB_XX_PWRDND_SIG_WIDTH 1
+#define FRF_AB_XX_PWRDNC_SIG_LBN 30
+#define FRF_AB_XX_PWRDNC_SIG_WIDTH 1
+#define FRF_AB_XX_PWRDNB_SIG_LBN 29
+#define FRF_AB_XX_PWRDNB_SIG_WIDTH 1
+#define FRF_AB_XX_PWRDNA_SIG_LBN 28
+#define FRF_AB_XX_PWRDNA_SIG_WIDTH 1
+#define FRF_AB_XX_SIM_MODE_LBN 27
+#define FRF_AB_XX_SIM_MODE_WIDTH 1
+#define FRF_AB_XX_RSTPLLCD_SIG_LBN 25
+#define FRF_AB_XX_RSTPLLCD_SIG_WIDTH 1
+#define FRF_AB_XX_RSTPLLAB_SIG_LBN 24
+#define FRF_AB_XX_RSTPLLAB_SIG_WIDTH 1
+#define FRF_AB_XX_RESETD_SIG_LBN 23
+#define FRF_AB_XX_RESETD_SIG_WIDTH 1
+#define FRF_AB_XX_RESETC_SIG_LBN 22
+#define FRF_AB_XX_RESETC_SIG_WIDTH 1
+#define FRF_AB_XX_RESETB_SIG_LBN 21
+#define FRF_AB_XX_RESETB_SIG_WIDTH 1
+#define FRF_AB_XX_RESETA_SIG_LBN 20
+#define FRF_AB_XX_RESETA_SIG_WIDTH 1
+#define FRF_AB_XX_RSTXGXSRX_SIG_LBN 18
+#define FRF_AB_XX_RSTXGXSRX_SIG_WIDTH 1
+#define FRF_AB_XX_RSTXGXSTX_SIG_LBN 17
+#define FRF_AB_XX_RSTXGXSTX_SIG_WIDTH 1
+#define FRF_AB_XX_SD_RST_ACT_LBN 16
+#define FRF_AB_XX_SD_RST_ACT_WIDTH 1
+#define FRF_AB_XX_PWRDND_EN_LBN 15
+#define FRF_AB_XX_PWRDND_EN_WIDTH 1
+#define FRF_AB_XX_PWRDNC_EN_LBN 14
+#define FRF_AB_XX_PWRDNC_EN_WIDTH 1
+#define FRF_AB_XX_PWRDNB_EN_LBN 13
+#define FRF_AB_XX_PWRDNB_EN_WIDTH 1
+#define FRF_AB_XX_PWRDNA_EN_LBN 12
+#define FRF_AB_XX_PWRDNA_EN_WIDTH 1
+#define FRF_AB_XX_RSTPLLCD_EN_LBN 9
+#define FRF_AB_XX_RSTPLLCD_EN_WIDTH 1
+#define FRF_AB_XX_RSTPLLAB_EN_LBN 8
+#define FRF_AB_XX_RSTPLLAB_EN_WIDTH 1
+#define FRF_AB_XX_RESETD_EN_LBN 7
+#define FRF_AB_XX_RESETD_EN_WIDTH 1
+#define FRF_AB_XX_RESETC_EN_LBN 6
+#define FRF_AB_XX_RESETC_EN_WIDTH 1
+#define FRF_AB_XX_RESETB_EN_LBN 5
+#define FRF_AB_XX_RESETB_EN_WIDTH 1
+#define FRF_AB_XX_RESETA_EN_LBN 4
+#define FRF_AB_XX_RESETA_EN_WIDTH 1
+#define FRF_AB_XX_RSTXGXSRX_EN_LBN 2
+#define FRF_AB_XX_RSTXGXSRX_EN_WIDTH 1
+#define FRF_AB_XX_RSTXGXSTX_EN_LBN 1
+#define FRF_AB_XX_RSTXGXSTX_EN_WIDTH 1
+#define FRF_AB_XX_RST_XX_EN_LBN 0
+#define FRF_AB_XX_RST_XX_EN_WIDTH 1
+
+/* XX_SD_CTL_REG: XGXS/XAUI powerdown/reset control register */
+#define FR_AB_XX_SD_CTL 0x00001310
+#define FRF_AB_XX_TERMADJ1_LBN 17
+#define FRF_AB_XX_TERMADJ1_WIDTH 1
+#define FRF_AB_XX_TERMADJ0_LBN 16
+#define FRF_AB_XX_TERMADJ0_WIDTH 1
+#define FRF_AB_XX_HIDRVD_LBN 15
+#define FRF_AB_XX_HIDRVD_WIDTH 1
+#define FRF_AB_XX_LODRVD_LBN 14
+#define FRF_AB_XX_LODRVD_WIDTH 1
+#define FRF_AB_XX_HIDRVC_LBN 13
+#define FRF_AB_XX_HIDRVC_WIDTH 1
+#define FRF_AB_XX_LODRVC_LBN 12
+#define FRF_AB_XX_LODRVC_WIDTH 1
+#define FRF_AB_XX_HIDRVB_LBN 11
+#define FRF_AB_XX_HIDRVB_WIDTH 1
+#define FRF_AB_XX_LODRVB_LBN 10
+#define FRF_AB_XX_LODRVB_WIDTH 1
+#define FRF_AB_XX_HIDRVA_LBN 9
+#define FRF_AB_XX_HIDRVA_WIDTH 1
+#define FRF_AB_XX_LODRVA_LBN 8
+#define FRF_AB_XX_LODRVA_WIDTH 1
+#define FRF_AB_XX_LPBKD_LBN 3
+#define FRF_AB_XX_LPBKD_WIDTH 1
+#define FRF_AB_XX_LPBKC_LBN 2
+#define FRF_AB_XX_LPBKC_WIDTH 1
+#define FRF_AB_XX_LPBKB_LBN 1
+#define FRF_AB_XX_LPBKB_WIDTH 1
+#define FRF_AB_XX_LPBKA_LBN 0
+#define FRF_AB_XX_LPBKA_WIDTH 1
+
+/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */
+#define FR_AB_XX_TXDRV_CTL 0x00001320
+#define FRF_AB_XX_DEQD_LBN 28
+#define FRF_AB_XX_DEQD_WIDTH 4
+#define FRF_AB_XX_DEQC_LBN 24
+#define FRF_AB_XX_DEQC_WIDTH 4
+#define FRF_AB_XX_DEQB_LBN 20
+#define FRF_AB_XX_DEQB_WIDTH 4
+#define FRF_AB_XX_DEQA_LBN 16
+#define FRF_AB_XX_DEQA_WIDTH 4
+#define FRF_AB_XX_DTXD_LBN 12
+#define FRF_AB_XX_DTXD_WIDTH 4
+#define FRF_AB_XX_DTXC_LBN 8
+#define FRF_AB_XX_DTXC_WIDTH 4
+#define FRF_AB_XX_DTXB_LBN 4
+#define FRF_AB_XX_DTXB_WIDTH 4
+#define FRF_AB_XX_DTXA_LBN 0
+#define FRF_AB_XX_DTXA_WIDTH 4
+
+/* XX_PRBS_CTL_REG: documentation to be written for sum_XX_PRBS_CTL_REG */
+#define FR_AB_XX_PRBS_CTL 0x00001330
+#define FRF_AB_XX_CH3_RX_PRBS_SEL_LBN 30
+#define FRF_AB_XX_CH3_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH3_RX_PRBS_INV_LBN 29
+#define FRF_AB_XX_CH3_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_LBN 28
+#define FRF_AB_XX_CH3_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH2_RX_PRBS_SEL_LBN 26
+#define FRF_AB_XX_CH2_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH2_RX_PRBS_INV_LBN 25
+#define FRF_AB_XX_CH2_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_LBN 24
+#define FRF_AB_XX_CH2_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH1_RX_PRBS_SEL_LBN 22
+#define FRF_AB_XX_CH1_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH1_RX_PRBS_INV_LBN 21
+#define FRF_AB_XX_CH1_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_LBN 20
+#define FRF_AB_XX_CH1_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH0_RX_PRBS_SEL_LBN 18
+#define FRF_AB_XX_CH0_RX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH0_RX_PRBS_INV_LBN 17
+#define FRF_AB_XX_CH0_RX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_LBN 16
+#define FRF_AB_XX_CH0_RX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH3_TX_PRBS_SEL_LBN 14
+#define FRF_AB_XX_CH3_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH3_TX_PRBS_INV_LBN 13
+#define FRF_AB_XX_CH3_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_LBN 12
+#define FRF_AB_XX_CH3_TX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH2_TX_PRBS_SEL_LBN 10
+#define FRF_AB_XX_CH2_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH2_TX_PRBS_INV_LBN 9
+#define FRF_AB_XX_CH2_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_LBN 8
+#define FRF_AB_XX_CH2_TX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH1_TX_PRBS_SEL_LBN 6
+#define FRF_AB_XX_CH1_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH1_TX_PRBS_INV_LBN 5
+#define FRF_AB_XX_CH1_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_LBN 4
+#define FRF_AB_XX_CH1_TX_PRBS_CHKEN_WIDTH 1
+#define FRF_AB_XX_CH0_TX_PRBS_SEL_LBN 2
+#define FRF_AB_XX_CH0_TX_PRBS_SEL_WIDTH 2
+#define FRF_AB_XX_CH0_TX_PRBS_INV_LBN 1
+#define FRF_AB_XX_CH0_TX_PRBS_INV_WIDTH 1
+#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_LBN 0
+#define FRF_AB_XX_CH0_TX_PRBS_CHKEN_WIDTH 1
+
+/* XX_PRBS_CHK_REG: documentation to be written for sum_XX_PRBS_CHK_REG */
+#define FR_AB_XX_PRBS_CHK 0x00001340
+#define FRF_AB_XX_REV_LB_EN_LBN 16
+#define FRF_AB_XX_REV_LB_EN_WIDTH 1
+#define FRF_AB_XX_CH3_DEG_DET_LBN 15
+#define FRF_AB_XX_CH3_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH3_LFSR_LOCK_IND_LBN 14
+#define FRF_AB_XX_CH3_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH3_PRBS_FRUN_LBN 13
+#define FRF_AB_XX_CH3_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH3_ERR_CHK_LBN 12
+#define FRF_AB_XX_CH3_ERR_CHK_WIDTH 1
+#define FRF_AB_XX_CH2_DEG_DET_LBN 11
+#define FRF_AB_XX_CH2_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH2_LFSR_LOCK_IND_LBN 10
+#define FRF_AB_XX_CH2_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH2_PRBS_FRUN_LBN 9
+#define FRF_AB_XX_CH2_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH2_ERR_CHK_LBN 8
+#define FRF_AB_XX_CH2_ERR_CHK_WIDTH 1
+#define FRF_AB_XX_CH1_DEG_DET_LBN 7
+#define FRF_AB_XX_CH1_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH1_LFSR_LOCK_IND_LBN 6
+#define FRF_AB_XX_CH1_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH1_PRBS_FRUN_LBN 5
+#define FRF_AB_XX_CH1_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH1_ERR_CHK_LBN 4
+#define FRF_AB_XX_CH1_ERR_CHK_WIDTH 1
+#define FRF_AB_XX_CH0_DEG_DET_LBN 3
+#define FRF_AB_XX_CH0_DEG_DET_WIDTH 1
+#define FRF_AB_XX_CH0_LFSR_LOCK_IND_LBN 2
+#define FRF_AB_XX_CH0_LFSR_LOCK_IND_WIDTH 1
+#define FRF_AB_XX_CH0_PRBS_FRUN_LBN 1
+#define FRF_AB_XX_CH0_PRBS_FRUN_WIDTH 1
+#define FRF_AB_XX_CH0_ERR_CHK_LBN 0
+#define FRF_AB_XX_CH0_ERR_CHK_WIDTH 1
+
+/* XX_PRBS_ERR_REG: documentation to be written for sum_XX_PRBS_ERR_REG */
+#define FR_AB_XX_PRBS_ERR 0x00001350
+#define FRF_AB_XX_CH3_PRBS_ERR_CNT_LBN 24
+#define FRF_AB_XX_CH3_PRBS_ERR_CNT_WIDTH 8
+#define FRF_AB_XX_CH2_PRBS_ERR_CNT_LBN 16
+#define FRF_AB_XX_CH2_PRBS_ERR_CNT_WIDTH 8
+#define FRF_AB_XX_CH1_PRBS_ERR_CNT_LBN 8
+#define FRF_AB_XX_CH1_PRBS_ERR_CNT_WIDTH 8
+#define FRF_AB_XX_CH0_PRBS_ERR_CNT_LBN 0
+#define FRF_AB_XX_CH0_PRBS_ERR_CNT_WIDTH 8
+
+/* XX_CORE_STAT_REG: XAUI XGXS core status register */
+#define FR_AB_XX_CORE_STAT 0x00001360
+#define FRF_AB_XX_FORCE_SIG3_LBN 31
+#define FRF_AB_XX_FORCE_SIG3_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG3_VAL_LBN 30
+#define FRF_AB_XX_FORCE_SIG3_VAL_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG2_LBN 29
+#define FRF_AB_XX_FORCE_SIG2_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG2_VAL_LBN 28
+#define FRF_AB_XX_FORCE_SIG2_VAL_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG1_LBN 27
+#define FRF_AB_XX_FORCE_SIG1_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG1_VAL_LBN 26
+#define FRF_AB_XX_FORCE_SIG1_VAL_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG0_LBN 25
+#define FRF_AB_XX_FORCE_SIG0_WIDTH 1
+#define FRF_AB_XX_FORCE_SIG0_VAL_LBN 24
+#define FRF_AB_XX_FORCE_SIG0_VAL_WIDTH 1
+#define FRF_AB_XX_XGXS_LB_EN_LBN 23
+#define FRF_AB_XX_XGXS_LB_EN_WIDTH 1
+#define FRF_AB_XX_XGMII_LB_EN_LBN 22
+#define FRF_AB_XX_XGMII_LB_EN_WIDTH 1
+#define FRF_AB_XX_MATCH_FAULT_LBN 21
+#define FRF_AB_XX_MATCH_FAULT_WIDTH 1
+#define FRF_AB_XX_ALIGN_DONE_LBN 20
+#define FRF_AB_XX_ALIGN_DONE_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT3_LBN 19
+#define FRF_AB_XX_SYNC_STAT3_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT2_LBN 18
+#define FRF_AB_XX_SYNC_STAT2_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT1_LBN 17
+#define FRF_AB_XX_SYNC_STAT1_WIDTH 1
+#define FRF_AB_XX_SYNC_STAT0_LBN 16
+#define FRF_AB_XX_SYNC_STAT0_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH3_LBN 15
+#define FRF_AB_XX_COMMA_DET_CH3_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH2_LBN 14
+#define FRF_AB_XX_COMMA_DET_CH2_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH1_LBN 13
+#define FRF_AB_XX_COMMA_DET_CH1_WIDTH 1
+#define FRF_AB_XX_COMMA_DET_CH0_LBN 12
+#define FRF_AB_XX_COMMA_DET_CH0_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH3_LBN 11
+#define FRF_AB_XX_CGRP_ALIGN_CH3_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH2_LBN 10
+#define FRF_AB_XX_CGRP_ALIGN_CH2_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH1_LBN 9
+#define FRF_AB_XX_CGRP_ALIGN_CH1_WIDTH 1
+#define FRF_AB_XX_CGRP_ALIGN_CH0_LBN 8
+#define FRF_AB_XX_CGRP_ALIGN_CH0_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH3_LBN 7
+#define FRF_AB_XX_CHAR_ERR_CH3_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH2_LBN 6
+#define FRF_AB_XX_CHAR_ERR_CH2_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH1_LBN 5
+#define FRF_AB_XX_CHAR_ERR_CH1_WIDTH 1
+#define FRF_AB_XX_CHAR_ERR_CH0_LBN 4
+#define FRF_AB_XX_CHAR_ERR_CH0_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH3_LBN 3
+#define FRF_AB_XX_DISPERR_CH3_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH2_LBN 2
+#define FRF_AB_XX_DISPERR_CH2_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH1_LBN 1
+#define FRF_AB_XX_DISPERR_CH1_WIDTH 1
+#define FRF_AB_XX_DISPERR_CH0_LBN 0
+#define FRF_AB_XX_DISPERR_CH0_WIDTH 1
+
+/* RX_DESC_PTR_TBL_KER: Receive descriptor pointer table */
+#define FR_AA_RX_DESC_PTR_TBL_KER 0x00011800
+#define FR_AA_RX_DESC_PTR_TBL_KER_STEP 16
+#define FR_AA_RX_DESC_PTR_TBL_KER_ROWS 4
+/* RX_DESC_PTR_TBL: Receive descriptor pointer table */
+#define FR_BZ_RX_DESC_PTR_TBL 0x00f40000
+#define FR_BZ_RX_DESC_PTR_TBL_STEP 16
+#define FR_BB_RX_DESC_PTR_TBL_ROWS 4096
+#define FR_CZ_RX_DESC_PTR_TBL_ROWS 1024
+#define FRF_CZ_RX_HDR_SPLIT_LBN 90
+#define FRF_CZ_RX_HDR_SPLIT_WIDTH 1
+#define FRF_AA_RX_RESET_LBN 89
+#define FRF_AA_RX_RESET_WIDTH 1
+#define FRF_AZ_RX_ISCSI_DDIG_EN_LBN 88
+#define FRF_AZ_RX_ISCSI_DDIG_EN_WIDTH 1
+#define FRF_AZ_RX_ISCSI_HDIG_EN_LBN 87
+#define FRF_AZ_RX_ISCSI_HDIG_EN_WIDTH 1
+#define FRF_AZ_RX_DESC_PREF_ACT_LBN 86
+#define FRF_AZ_RX_DESC_PREF_ACT_WIDTH 1
+#define FRF_AZ_RX_DC_HW_RPTR_LBN 80
+#define FRF_AZ_RX_DC_HW_RPTR_WIDTH 6
+#define FRF_AZ_RX_DESCQ_HW_RPTR_LBN 68
+#define FRF_AZ_RX_DESCQ_HW_RPTR_WIDTH 12
+#define FRF_AZ_RX_DESCQ_SW_WPTR_LBN 56
+#define FRF_AZ_RX_DESCQ_SW_WPTR_WIDTH 12
+#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_LBN 36
+#define FRF_AZ_RX_DESCQ_BUF_BASE_ID_WIDTH 20
+#define FRF_AZ_RX_DESCQ_EVQ_ID_LBN 24
+#define FRF_AZ_RX_DESCQ_EVQ_ID_WIDTH 12
+#define FRF_AZ_RX_DESCQ_OWNER_ID_LBN 10
+#define FRF_AZ_RX_DESCQ_OWNER_ID_WIDTH 14
+#define FRF_AZ_RX_DESCQ_LABEL_LBN 5
+#define FRF_AZ_RX_DESCQ_LABEL_WIDTH 5
+#define FRF_AZ_RX_DESCQ_SIZE_LBN 3
+#define FRF_AZ_RX_DESCQ_SIZE_WIDTH 2
+#define FFE_AZ_RX_DESCQ_SIZE_4K 3
+#define FFE_AZ_RX_DESCQ_SIZE_2K 2
+#define FFE_AZ_RX_DESCQ_SIZE_1K 1
+#define FFE_AZ_RX_DESCQ_SIZE_512 0
+#define FRF_AZ_RX_DESCQ_TYPE_LBN 2
+#define FRF_AZ_RX_DESCQ_TYPE_WIDTH 1
+#define FRF_AZ_RX_DESCQ_JUMBO_LBN 1
+#define FRF_AZ_RX_DESCQ_JUMBO_WIDTH 1
+#define FRF_AZ_RX_DESCQ_EN_LBN 0
+#define FRF_AZ_RX_DESCQ_EN_WIDTH 1
+
+/* TX_DESC_PTR_TBL_KER: Transmit descriptor pointer */
+#define FR_AA_TX_DESC_PTR_TBL_KER 0x00011900
+#define FR_AA_TX_DESC_PTR_TBL_KER_STEP 16
+#define FR_AA_TX_DESC_PTR_TBL_KER_ROWS 8
+/* TX_DESC_PTR_TBL: Transmit descriptor pointer */
+#define FR_BZ_TX_DESC_PTR_TBL 0x00f50000
+#define FR_BZ_TX_DESC_PTR_TBL_STEP 16
+#define FR_BB_TX_DESC_PTR_TBL_ROWS 4096
+#define FR_CZ_TX_DESC_PTR_TBL_ROWS 1024
+#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_LBN 94
+#define FRF_CZ_TX_DPT_Q_MASK_WIDTH_WIDTH 2
+#define FRF_CZ_TX_DPT_ETH_FILT_EN_LBN 93
+#define FRF_CZ_TX_DPT_ETH_FILT_EN_WIDTH 1
+#define FRF_CZ_TX_DPT_IP_FILT_EN_LBN 92
+#define FRF_CZ_TX_DPT_IP_FILT_EN_WIDTH 1
+#define FRF_BZ_TX_NON_IP_DROP_DIS_LBN 91
+#define FRF_BZ_TX_NON_IP_DROP_DIS_WIDTH 1
+#define FRF_BZ_TX_IP_CHKSM_DIS_LBN 90
+#define FRF_BZ_TX_IP_CHKSM_DIS_WIDTH 1
+#define FRF_BZ_TX_TCP_CHKSM_DIS_LBN 89
+#define FRF_BZ_TX_TCP_CHKSM_DIS_WIDTH 1
+#define FRF_AZ_TX_DESCQ_EN_LBN 88
+#define FRF_AZ_TX_DESCQ_EN_WIDTH 1
+#define FRF_AZ_TX_ISCSI_DDIG_EN_LBN 87
+#define FRF_AZ_TX_ISCSI_DDIG_EN_WIDTH 1
+#define FRF_AZ_TX_ISCSI_HDIG_EN_LBN 86
+#define FRF_AZ_TX_ISCSI_HDIG_EN_WIDTH 1
+#define FRF_AZ_TX_DC_HW_RPTR_LBN 80
+#define FRF_AZ_TX_DC_HW_RPTR_WIDTH 6
+#define FRF_AZ_TX_DESCQ_HW_RPTR_LBN 68
+#define FRF_AZ_TX_DESCQ_HW_RPTR_WIDTH 12
+#define FRF_AZ_TX_DESCQ_SW_WPTR_LBN 56
+#define FRF_AZ_TX_DESCQ_SW_WPTR_WIDTH 12
+#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_LBN 36
+#define FRF_AZ_TX_DESCQ_BUF_BASE_ID_WIDTH 20
+#define FRF_AZ_TX_DESCQ_EVQ_ID_LBN 24
+#define FRF_AZ_TX_DESCQ_EVQ_ID_WIDTH 12
+#define FRF_AZ_TX_DESCQ_OWNER_ID_LBN 10
+#define FRF_AZ_TX_DESCQ_OWNER_ID_WIDTH 14
+#define FRF_AZ_TX_DESCQ_LABEL_LBN 5
+#define FRF_AZ_TX_DESCQ_LABEL_WIDTH 5
+#define FRF_AZ_TX_DESCQ_SIZE_LBN 3
+#define FRF_AZ_TX_DESCQ_SIZE_WIDTH 2
+#define FFE_AZ_TX_DESCQ_SIZE_4K 3
+#define FFE_AZ_TX_DESCQ_SIZE_2K 2
+#define FFE_AZ_TX_DESCQ_SIZE_1K 1
+#define FFE_AZ_TX_DESCQ_SIZE_512 0
+#define FRF_AZ_TX_DESCQ_TYPE_LBN 1
+#define FRF_AZ_TX_DESCQ_TYPE_WIDTH 2
+#define FRF_AZ_TX_DESCQ_FLUSH_LBN 0
+#define FRF_AZ_TX_DESCQ_FLUSH_WIDTH 1
+
+/* EVQ_PTR_TBL_KER: Event queue pointer table */
+#define FR_AA_EVQ_PTR_TBL_KER 0x00011a00
+#define FR_AA_EVQ_PTR_TBL_KER_STEP 16
+#define FR_AA_EVQ_PTR_TBL_KER_ROWS 4
+/* EVQ_PTR_TBL: Event queue pointer table */
+#define FR_BZ_EVQ_PTR_TBL 0x00f60000
+#define FR_BZ_EVQ_PTR_TBL_STEP 16
+#define FR_CZ_EVQ_PTR_TBL_ROWS 1024
+#define FR_BB_EVQ_PTR_TBL_ROWS 4096
+#define FRF_BZ_EVQ_RPTR_IGN_LBN 40
+#define FRF_BZ_EVQ_RPTR_IGN_WIDTH 1
+#define FRF_AB_EVQ_WKUP_OR_INT_EN_LBN 39
+#define FRF_AB_EVQ_WKUP_OR_INT_EN_WIDTH 1
+#define FRF_CZ_EVQ_DOS_PROTECT_EN_LBN 39
+#define FRF_CZ_EVQ_DOS_PROTECT_EN_WIDTH 1
+#define FRF_AZ_EVQ_NXT_WPTR_LBN 24
+#define FRF_AZ_EVQ_NXT_WPTR_WIDTH 15
+#define FRF_AZ_EVQ_EN_LBN 23
+#define FRF_AZ_EVQ_EN_WIDTH 1
+#define FRF_AZ_EVQ_SIZE_LBN 20
+#define FRF_AZ_EVQ_SIZE_WIDTH 3
+#define FFE_AZ_EVQ_SIZE_32K 6
+#define FFE_AZ_EVQ_SIZE_16K 5
+#define FFE_AZ_EVQ_SIZE_8K 4
+#define FFE_AZ_EVQ_SIZE_4K 3
+#define FFE_AZ_EVQ_SIZE_2K 2
+#define FFE_AZ_EVQ_SIZE_1K 1
+#define FFE_AZ_EVQ_SIZE_512 0
+#define FRF_AZ_EVQ_BUF_BASE_ID_LBN 0
+#define FRF_AZ_EVQ_BUF_BASE_ID_WIDTH 20
+
+/* BUF_HALF_TBL_KER: Buffer table in half buffer table mode direct access by driver */
+#define FR_AA_BUF_HALF_TBL_KER 0x00018000
+#define FR_AA_BUF_HALF_TBL_KER_STEP 8
+#define FR_AA_BUF_HALF_TBL_KER_ROWS 4096
+/* BUF_HALF_TBL: Buffer table in half buffer table mode direct access by driver */
+#define FR_BZ_BUF_HALF_TBL 0x00800000
+#define FR_BZ_BUF_HALF_TBL_STEP 8
+#define FR_CZ_BUF_HALF_TBL_ROWS 147456
+#define FR_BB_BUF_HALF_TBL_ROWS 524288
+#define FRF_AZ_BUF_ADR_HBUF_ODD_LBN 44
+#define FRF_AZ_BUF_ADR_HBUF_ODD_WIDTH 20
+#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_LBN 32
+#define FRF_AZ_BUF_OWNER_ID_HBUF_ODD_WIDTH 12
+#define FRF_AZ_BUF_ADR_HBUF_EVEN_LBN 12
+#define FRF_AZ_BUF_ADR_HBUF_EVEN_WIDTH 20
+#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_LBN 0
+#define FRF_AZ_BUF_OWNER_ID_HBUF_EVEN_WIDTH 12
+
+/* BUF_FULL_TBL_KER: Buffer table in full buffer table mode direct access by driver */
+#define FR_AA_BUF_FULL_TBL_KER 0x00018000
+#define FR_AA_BUF_FULL_TBL_KER_STEP 8
+#define FR_AA_BUF_FULL_TBL_KER_ROWS 4096
+/* BUF_FULL_TBL: Buffer table in full buffer table mode direct access by driver */
+#define FR_BZ_BUF_FULL_TBL 0x00800000
+#define FR_BZ_BUF_FULL_TBL_STEP 8
+#define FR_CZ_BUF_FULL_TBL_ROWS 147456
+#define FR_BB_BUF_FULL_TBL_ROWS 917504
+#define FRF_AZ_BUF_FULL_UNUSED_LBN 51
+#define FRF_AZ_BUF_FULL_UNUSED_WIDTH 13
+#define FRF_AZ_IP_DAT_BUF_SIZE_LBN 50
+#define FRF_AZ_IP_DAT_BUF_SIZE_WIDTH 1
+#define FRF_AZ_BUF_ADR_REGION_LBN 48
+#define FRF_AZ_BUF_ADR_REGION_WIDTH 2
+#define FFE_AZ_BUF_ADR_REGN3 3
+#define FFE_AZ_BUF_ADR_REGN2 2
+#define FFE_AZ_BUF_ADR_REGN1 1
+#define FFE_AZ_BUF_ADR_REGN0 0
+#define FRF_AZ_BUF_ADR_FBUF_LBN 14
+#define FRF_AZ_BUF_ADR_FBUF_WIDTH 34
+#define FRF_AZ_BUF_OWNER_ID_FBUF_LBN 0
+#define FRF_AZ_BUF_OWNER_ID_FBUF_WIDTH 14
+
+/* RX_FILTER_TBL0: TCP/IPv4 Receive filter table */
+#define FR_BZ_RX_FILTER_TBL0 0x00f00000
+#define FR_BZ_RX_FILTER_TBL0_STEP 32
+#define FR_BZ_RX_FILTER_TBL0_ROWS 8192
+/* RX_FILTER_TBL1: TCP/IPv4 Receive filter table */
+#define FR_BB_RX_FILTER_TBL1 0x00f00010
+#define FR_BB_RX_FILTER_TBL1_STEP 32
+#define FR_BB_RX_FILTER_TBL1_ROWS 8192
+#define FRF_BZ_RSS_EN_LBN 110
+#define FRF_BZ_RSS_EN_WIDTH 1
+#define FRF_BZ_SCATTER_EN_LBN 109
+#define FRF_BZ_SCATTER_EN_WIDTH 1
+#define FRF_BZ_TCP_UDP_LBN 108
+#define FRF_BZ_TCP_UDP_WIDTH 1
+#define FRF_BZ_RXQ_ID_LBN 96
+#define FRF_BZ_RXQ_ID_WIDTH 12
+#define FRF_BZ_DEST_IP_LBN 64
+#define FRF_BZ_DEST_IP_WIDTH 32
+#define FRF_BZ_DEST_PORT_TCP_LBN 48
+#define FRF_BZ_DEST_PORT_TCP_WIDTH 16
+#define FRF_BZ_SRC_IP_LBN 16
+#define FRF_BZ_SRC_IP_WIDTH 32
+#define FRF_BZ_SRC_TCP_DEST_UDP_LBN 0
+#define FRF_BZ_SRC_TCP_DEST_UDP_WIDTH 16
+
+/* RX_MAC_FILTER_TBL0: Receive Ethernet filter table */
+#define FR_CZ_RX_MAC_FILTER_TBL0 0x00f00010
+#define FR_CZ_RX_MAC_FILTER_TBL0_STEP 32
+#define FR_CZ_RX_MAC_FILTER_TBL0_ROWS 512
+#define FRF_CZ_RMFT_RSS_EN_LBN 75
+#define FRF_CZ_RMFT_RSS_EN_WIDTH 1
+#define FRF_CZ_RMFT_SCATTER_EN_LBN 74
+#define FRF_CZ_RMFT_SCATTER_EN_WIDTH 1
+#define FRF_CZ_RMFT_IP_OVERRIDE_LBN 73
+#define FRF_CZ_RMFT_IP_OVERRIDE_WIDTH 1
+#define FRF_CZ_RMFT_RXQ_ID_LBN 61
+#define FRF_CZ_RMFT_RXQ_ID_WIDTH 12
+#define FRF_CZ_RMFT_WILDCARD_MATCH_LBN 60
+#define FRF_CZ_RMFT_WILDCARD_MATCH_WIDTH 1
+#define FRF_CZ_RMFT_DEST_MAC_LBN 16
+#define FRF_CZ_RMFT_DEST_MAC_WIDTH 44
+#define FRF_CZ_RMFT_VLAN_ID_LBN 0
+#define FRF_CZ_RMFT_VLAN_ID_WIDTH 12
+
+/* TIMER_TBL: Timer table */
+#define FR_BZ_TIMER_TBL 0x00f70000
+#define FR_BZ_TIMER_TBL_STEP 16
+#define FR_CZ_TIMER_TBL_ROWS 1024
+#define FR_BB_TIMER_TBL_ROWS 4096
+#define FRF_CZ_TIMER_Q_EN_LBN 33
+#define FRF_CZ_TIMER_Q_EN_WIDTH 1
+#define FRF_CZ_INT_ARMD_LBN 32
+#define FRF_CZ_INT_ARMD_WIDTH 1
+#define FRF_CZ_INT_PEND_LBN 31
+#define FRF_CZ_INT_PEND_WIDTH 1
+#define FRF_CZ_HOST_NOTIFY_MODE_LBN 30
+#define FRF_CZ_HOST_NOTIFY_MODE_WIDTH 1
+#define FRF_CZ_RELOAD_TIMER_VAL_LBN 16
+#define FRF_CZ_RELOAD_TIMER_VAL_WIDTH 14
+#define FRF_CZ_TIMER_MODE_LBN 14
+#define FRF_CZ_TIMER_MODE_WIDTH 2
+#define FFE_CZ_TIMER_MODE_INT_HLDOFF 3
+#define FFE_CZ_TIMER_MODE_TRIG_START 2
+#define FFE_CZ_TIMER_MODE_IMMED_START 1
+#define FFE_CZ_TIMER_MODE_DIS 0
+#define FRF_BB_TIMER_MODE_LBN 12
+#define FRF_BB_TIMER_MODE_WIDTH 2
+#define FFE_BB_TIMER_MODE_INT_HLDOFF 2
+#define FFE_BB_TIMER_MODE_TRIG_START 2
+#define FFE_BB_TIMER_MODE_IMMED_START 1
+#define FFE_BB_TIMER_MODE_DIS 0
+#define FRF_CZ_TIMER_VAL_LBN 0
+#define FRF_CZ_TIMER_VAL_WIDTH 14
+#define FRF_BB_TIMER_VAL_LBN 0
+#define FRF_BB_TIMER_VAL_WIDTH 12
+
+/* TX_PACE_TBL: Transmit pacing table */
+#define FR_BZ_TX_PACE_TBL 0x00f80000
+#define FR_BZ_TX_PACE_TBL_STEP 16
+#define FR_CZ_TX_PACE_TBL_ROWS 1024
+#define FR_BB_TX_PACE_TBL_ROWS 4096
+#define FRF_BZ_TX_PACE_LBN 0
+#define FRF_BZ_TX_PACE_WIDTH 5
+
+/* RX_INDIRECTION_TBL: RX Indirection Table */
+#define FR_BZ_RX_INDIRECTION_TBL 0x00fb0000
+#define FR_BZ_RX_INDIRECTION_TBL_STEP 16
+#define FR_BZ_RX_INDIRECTION_TBL_ROWS 128
+#define FRF_BZ_IT_QUEUE_LBN 0
+#define FRF_BZ_IT_QUEUE_WIDTH 6
+
+/* TX_FILTER_TBL0: TCP/IPv4 Transmit filter table */
+#define FR_CZ_TX_FILTER_TBL0 0x00fc0000
+#define FR_CZ_TX_FILTER_TBL0_STEP 16
+#define FR_CZ_TX_FILTER_TBL0_ROWS 8192
+#define FRF_CZ_TIFT_TCP_UDP_LBN 108
+#define FRF_CZ_TIFT_TCP_UDP_WIDTH 1
+#define FRF_CZ_TIFT_TXQ_ID_LBN 96
+#define FRF_CZ_TIFT_TXQ_ID_WIDTH 12
+#define FRF_CZ_TIFT_DEST_IP_LBN 64
+#define FRF_CZ_TIFT_DEST_IP_WIDTH 32
+#define FRF_CZ_TIFT_DEST_PORT_TCP_LBN 48
+#define FRF_CZ_TIFT_DEST_PORT_TCP_WIDTH 16
+#define FRF_CZ_TIFT_SRC_IP_LBN 16
+#define FRF_CZ_TIFT_SRC_IP_WIDTH 32
+#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_LBN 0
+#define FRF_CZ_TIFT_SRC_TCP_DEST_UDP_WIDTH 16
+
+/* TX_MAC_FILTER_TBL0: Transmit Ethernet filter table */
+#define FR_CZ_TX_MAC_FILTER_TBL0 0x00fe0000
+#define FR_CZ_TX_MAC_FILTER_TBL0_STEP 16
+#define FR_CZ_TX_MAC_FILTER_TBL0_ROWS 512
+#define FRF_CZ_TMFT_TXQ_ID_LBN 61
+#define FRF_CZ_TMFT_TXQ_ID_WIDTH 12
+#define FRF_CZ_TMFT_WILDCARD_MATCH_LBN 60
+#define FRF_CZ_TMFT_WILDCARD_MATCH_WIDTH 1
+#define FRF_CZ_TMFT_SRC_MAC_LBN 16
+#define FRF_CZ_TMFT_SRC_MAC_WIDTH 44
+#define FRF_CZ_TMFT_VLAN_ID_LBN 0
+#define FRF_CZ_TMFT_VLAN_ID_WIDTH 12
+
+/* MC_TREG_SMEM: MC Shared Memory */
+#define FR_CZ_MC_TREG_SMEM 0x00ff0000
+#define FR_CZ_MC_TREG_SMEM_STEP 4
+#define FR_CZ_MC_TREG_SMEM_ROWS 512
+#define FRF_CZ_MC_TREG_SMEM_ROW_LBN 0
+#define FRF_CZ_MC_TREG_SMEM_ROW_WIDTH 32
+
+/* MSIX_VECTOR_TABLE: MSIX Vector Table */
+#define FR_BB_MSIX_VECTOR_TABLE 0x00ff0000
+#define FR_BZ_MSIX_VECTOR_TABLE_STEP 16
+#define FR_BB_MSIX_VECTOR_TABLE_ROWS 64
+/* MSIX_VECTOR_TABLE: MSIX Vector Table */
+#define FR_CZ_MSIX_VECTOR_TABLE 0x00000000
+/* FR_BZ_MSIX_VECTOR_TABLE_STEP 16 */
+#define FR_CZ_MSIX_VECTOR_TABLE_ROWS 1024
+#define FRF_BZ_MSIX_VECTOR_RESERVED_LBN 97
+#define FRF_BZ_MSIX_VECTOR_RESERVED_WIDTH 31
+#define FRF_BZ_MSIX_VECTOR_MASK_LBN 96
+#define FRF_BZ_MSIX_VECTOR_MASK_WIDTH 1
+#define FRF_BZ_MSIX_MESSAGE_DATA_LBN 64
+#define FRF_BZ_MSIX_MESSAGE_DATA_WIDTH 32
+#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_LBN 32
+#define FRF_BZ_MSIX_MESSAGE_ADDRESS_HI_WIDTH 32
+#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_LBN 0
+#define FRF_BZ_MSIX_MESSAGE_ADDRESS_LO_WIDTH 32
+
+/* MSIX_PBA_TABLE: MSIX Pending Bit Array */
+#define FR_BB_MSIX_PBA_TABLE 0x00ff2000
+#define FR_BZ_MSIX_PBA_TABLE_STEP 4
+#define FR_BB_MSIX_PBA_TABLE_ROWS 2
+/* MSIX_PBA_TABLE: MSIX Pending Bit Array */
+#define FR_CZ_MSIX_PBA_TABLE 0x00008000
+/* FR_BZ_MSIX_PBA_TABLE_STEP 4 */
+#define FR_CZ_MSIX_PBA_TABLE_ROWS 32
+#define FRF_BZ_MSIX_PBA_PEND_DWORD_LBN 0
+#define FRF_BZ_MSIX_PBA_PEND_DWORD_WIDTH 32
+
+/* SRM_DBG_REG: SRAM debug access */
+#define FR_BZ_SRM_DBG 0x03000000
+#define FR_BZ_SRM_DBG_STEP 8
+#define FR_CZ_SRM_DBG_ROWS 262144
+#define FR_BB_SRM_DBG_ROWS 2097152
+#define FRF_BZ_SRM_DBG_LBN 0
+#define FRF_BZ_SRM_DBG_WIDTH 64
+
+/* TB_MSIX_PBA_TABLE: MSIX Pending Bit Array */
+#define FR_CZ_TB_MSIX_PBA_TABLE 0x00008000
+#define FR_CZ_TB_MSIX_PBA_TABLE_STEP 4
+#define FR_CZ_TB_MSIX_PBA_TABLE_ROWS 1024
+#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_LBN 0
+#define FRF_CZ_TB_MSIX_PBA_PEND_DWORD_WIDTH 32
+
+/* DRIVER_EV */
+#define FSF_AZ_DRIVER_EV_SUBCODE_LBN 56
+#define FSF_AZ_DRIVER_EV_SUBCODE_WIDTH 4
+#define FSE_BZ_TX_DSC_ERROR_EV 15
+#define FSE_BZ_RX_DSC_ERROR_EV 14
+#define FSE_AA_RX_RECOVER_EV 11
+#define FSE_AZ_TIMER_EV 10
+#define FSE_AZ_TX_PKT_NON_TCP_UDP 9
+#define FSE_AZ_WAKE_UP_EV 6
+#define FSE_AZ_SRM_UPD_DONE_EV 5
+#define FSE_AB_EVQ_NOT_EN_EV 3
+#define FSE_AZ_EVQ_INIT_DONE_EV 2
+#define FSE_AZ_RX_DESCQ_FLS_DONE_EV 1
+#define FSE_AZ_TX_DESCQ_FLS_DONE_EV 0
+#define FSF_AZ_DRIVER_EV_SUBDATA_LBN 0
+#define FSF_AZ_DRIVER_EV_SUBDATA_WIDTH 14
+
+/* EVENT_ENTRY */
+#define FSF_AZ_EV_CODE_LBN 60
+#define FSF_AZ_EV_CODE_WIDTH 4
+#define FSE_CZ_EV_CODE_MCDI_EV 12
+#define FSE_CZ_EV_CODE_USER_EV 8
+#define FSE_AZ_EV_CODE_DRV_GEN_EV 7
+#define FSE_AZ_EV_CODE_GLOBAL_EV 6
+#define FSE_AZ_EV_CODE_DRIVER_EV 5
+#define FSE_AZ_EV_CODE_TX_EV 2
+#define FSE_AZ_EV_CODE_RX_EV 0
+#define FSF_AZ_EV_DATA_LBN 0
+#define FSF_AZ_EV_DATA_WIDTH 60
+
+/* GLOBAL_EV */
+#define FSF_BB_GLB_EV_RX_RECOVERY_LBN 12
+#define FSF_BB_GLB_EV_RX_RECOVERY_WIDTH 1
+#define FSF_AA_GLB_EV_RX_RECOVERY_LBN 11
+#define FSF_AA_GLB_EV_RX_RECOVERY_WIDTH 1
+#define FSF_BB_GLB_EV_XG_MGT_INTR_LBN 11
+#define FSF_BB_GLB_EV_XG_MGT_INTR_WIDTH 1
+#define FSF_AB_GLB_EV_XFP_PHY0_INTR_LBN 10
+#define FSF_AB_GLB_EV_XFP_PHY0_INTR_WIDTH 1
+#define FSF_AB_GLB_EV_XG_PHY0_INTR_LBN 9
+#define FSF_AB_GLB_EV_XG_PHY0_INTR_WIDTH 1
+#define FSF_AB_GLB_EV_G_PHY0_INTR_LBN 7
+#define FSF_AB_GLB_EV_G_PHY0_INTR_WIDTH 1
+
+/* LEGACY_INT_VEC */
+#define FSF_AZ_NET_IVEC_FATAL_INT_LBN 64
+#define FSF_AZ_NET_IVEC_FATAL_INT_WIDTH 1
+#define FSF_AZ_NET_IVEC_INT_Q_LBN 40
+#define FSF_AZ_NET_IVEC_INT_Q_WIDTH 4
+#define FSF_AZ_NET_IVEC_INT_FLAG_LBN 32
+#define FSF_AZ_NET_IVEC_INT_FLAG_WIDTH 1
+#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_LBN 1
+#define FSF_AZ_NET_IVEC_EVQ_FIFO_HF_WIDTH 1
+#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_LBN 0
+#define FSF_AZ_NET_IVEC_EVQ_FIFO_AF_WIDTH 1
+
+/* MC_XGMAC_FLTR_RULE_DEF */
+#define FSF_CZ_MC_XFRC_MODE_LBN 416
+#define FSF_CZ_MC_XFRC_MODE_WIDTH 1
+#define FSE_CZ_MC_XFRC_MODE_LAYERED 1
+#define FSE_CZ_MC_XFRC_MODE_SIMPLE 0
+#define FSF_CZ_MC_XFRC_HASH_LBN 384
+#define FSF_CZ_MC_XFRC_HASH_WIDTH 32
+#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_LBN 256
+#define FSF_CZ_MC_XFRC_LAYER4_BYTE_MASK_WIDTH 128
+#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_LBN 128
+#define FSF_CZ_MC_XFRC_LAYER3_BYTE_MASK_WIDTH 128
+#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_LBN 0
+#define FSF_CZ_MC_XFRC_LAYER2_OR_SIMPLE_BYTE_MASK_WIDTH 128
+
+/* RX_EV */
+#define FSF_CZ_RX_EV_PKT_NOT_PARSED_LBN 58
+#define FSF_CZ_RX_EV_PKT_NOT_PARSED_WIDTH 1
+#define FSF_CZ_RX_EV_IPV6_PKT_LBN 57
+#define FSF_CZ_RX_EV_IPV6_PKT_WIDTH 1
+#define FSF_AZ_RX_EV_PKT_OK_LBN 56
+#define FSF_AZ_RX_EV_PKT_OK_WIDTH 1
+#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_LBN 55
+#define FSF_AZ_RX_EV_PAUSE_FRM_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_LBN 54
+#define FSF_AZ_RX_EV_BUF_OWNER_ID_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_IP_FRAG_ERR_LBN 53
+#define FSF_AZ_RX_EV_IP_FRAG_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_LBN 52
+#define FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_LBN 51
+#define FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_ETH_CRC_ERR_LBN 50
+#define FSF_AZ_RX_EV_ETH_CRC_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_FRM_TRUNC_LBN 49
+#define FSF_AZ_RX_EV_FRM_TRUNC_WIDTH 1
+#define FSF_AA_RX_EV_DRIB_NIB_LBN 49
+#define FSF_AA_RX_EV_DRIB_NIB_WIDTH 1
+#define FSF_AZ_RX_EV_TOBE_DISC_LBN 47
+#define FSF_AZ_RX_EV_TOBE_DISC_WIDTH 1
+#define FSF_AZ_RX_EV_PKT_TYPE_LBN 44
+#define FSF_AZ_RX_EV_PKT_TYPE_WIDTH 3
+#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_JUMBO 5
+#define FSE_AZ_RX_EV_PKT_TYPE_VLAN_LLC 4
+#define FSE_AZ_RX_EV_PKT_TYPE_VLAN 3
+#define FSE_AZ_RX_EV_PKT_TYPE_JUMBO 2
+#define FSE_AZ_RX_EV_PKT_TYPE_LLC 1
+#define FSE_AZ_RX_EV_PKT_TYPE_ETH 0
+#define FSF_AZ_RX_EV_HDR_TYPE_LBN 42
+#define FSF_AZ_RX_EV_HDR_TYPE_WIDTH 2
+#define FSE_AZ_RX_EV_HDR_TYPE_OTHER 3
+#define FSE_AB_RX_EV_HDR_TYPE_IPV4_OTHER 2
+#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_OTHER 2
+#define FSE_AB_RX_EV_HDR_TYPE_IPV4_UDP 1
+#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_UDP 1
+#define FSE_AB_RX_EV_HDR_TYPE_IPV4_TCP 0
+#define FSE_CZ_RX_EV_HDR_TYPE_IPV4V6_TCP 0
+#define FSF_AZ_RX_EV_DESC_Q_EMPTY_LBN 41
+#define FSF_AZ_RX_EV_DESC_Q_EMPTY_WIDTH 1
+#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_LBN 40
+#define FSF_AZ_RX_EV_MCAST_HASH_MATCH_WIDTH 1
+#define FSF_AZ_RX_EV_MCAST_PKT_LBN 39
+#define FSF_AZ_RX_EV_MCAST_PKT_WIDTH 1
+#define FSF_AA_RX_EV_RECOVERY_FLAG_LBN 37
+#define FSF_AA_RX_EV_RECOVERY_FLAG_WIDTH 1
+#define FSF_AZ_RX_EV_Q_LABEL_LBN 32
+#define FSF_AZ_RX_EV_Q_LABEL_WIDTH 5
+#define FSF_AZ_RX_EV_JUMBO_CONT_LBN 31
+#define FSF_AZ_RX_EV_JUMBO_CONT_WIDTH 1
+#define FSF_AZ_RX_EV_PORT_LBN 30
+#define FSF_AZ_RX_EV_PORT_WIDTH 1
+#define FSF_AZ_RX_EV_BYTE_CNT_LBN 16
+#define FSF_AZ_RX_EV_BYTE_CNT_WIDTH 14
+#define FSF_AZ_RX_EV_SOP_LBN 15
+#define FSF_AZ_RX_EV_SOP_WIDTH 1
+#define FSF_AZ_RX_EV_ISCSI_PKT_OK_LBN 14
+#define FSF_AZ_RX_EV_ISCSI_PKT_OK_WIDTH 1
+#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_LBN 13
+#define FSF_AZ_RX_EV_ISCSI_DDIG_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_LBN 12
+#define FSF_AZ_RX_EV_ISCSI_HDIG_ERR_WIDTH 1
+#define FSF_AZ_RX_EV_DESC_PTR_LBN 0
+#define FSF_AZ_RX_EV_DESC_PTR_WIDTH 12
+
+/* RX_KER_DESC */
+#define FSF_AZ_RX_KER_BUF_SIZE_LBN 48
+#define FSF_AZ_RX_KER_BUF_SIZE_WIDTH 14
+#define FSF_AZ_RX_KER_BUF_REGION_LBN 46
+#define FSF_AZ_RX_KER_BUF_REGION_WIDTH 2
+#define FSF_AZ_RX_KER_BUF_ADDR_LBN 0
+#define FSF_AZ_RX_KER_BUF_ADDR_WIDTH 46
+
+/* RX_USER_DESC */
+#define FSF_AZ_RX_USER_2BYTE_OFFSET_LBN 20
+#define FSF_AZ_RX_USER_2BYTE_OFFSET_WIDTH 12
+#define FSF_AZ_RX_USER_BUF_ID_LBN 0
+#define FSF_AZ_RX_USER_BUF_ID_WIDTH 20
+
+/* TX_EV */
+#define FSF_AZ_TX_EV_PKT_ERR_LBN 38
+#define FSF_AZ_TX_EV_PKT_ERR_WIDTH 1
+#define FSF_AZ_TX_EV_PKT_TOO_BIG_LBN 37
+#define FSF_AZ_TX_EV_PKT_TOO_BIG_WIDTH 1
+#define FSF_AZ_TX_EV_Q_LABEL_LBN 32
+#define FSF_AZ_TX_EV_Q_LABEL_WIDTH 5
+#define FSF_AZ_TX_EV_PORT_LBN 16
+#define FSF_AZ_TX_EV_PORT_WIDTH 1
+#define FSF_AZ_TX_EV_WQ_FF_FULL_LBN 15
+#define FSF_AZ_TX_EV_WQ_FF_FULL_WIDTH 1
+#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_LBN 14
+#define FSF_AZ_TX_EV_BUF_OWNER_ID_ERR_WIDTH 1
+#define FSF_AZ_TX_EV_COMP_LBN 12
+#define FSF_AZ_TX_EV_COMP_WIDTH 1
+#define FSF_AZ_TX_EV_DESC_PTR_LBN 0
+#define FSF_AZ_TX_EV_DESC_PTR_WIDTH 12
+
+/* TX_KER_DESC */
+#define FSF_AZ_TX_KER_CONT_LBN 62
+#define FSF_AZ_TX_KER_CONT_WIDTH 1
+#define FSF_AZ_TX_KER_BYTE_COUNT_LBN 48
+#define FSF_AZ_TX_KER_BYTE_COUNT_WIDTH 14
+#define FSF_AZ_TX_KER_BUF_REGION_LBN 46
+#define FSF_AZ_TX_KER_BUF_REGION_WIDTH 2
+#define FSF_AZ_TX_KER_BUF_ADDR_LBN 0
+#define FSF_AZ_TX_KER_BUF_ADDR_WIDTH 46
+
+/* TX_USER_DESC */
+#define FSF_AZ_TX_USER_SW_EV_EN_LBN 48
+#define FSF_AZ_TX_USER_SW_EV_EN_WIDTH 1
+#define FSF_AZ_TX_USER_CONT_LBN 46
+#define FSF_AZ_TX_USER_CONT_WIDTH 1
+#define FSF_AZ_TX_USER_BYTE_CNT_LBN 33
+#define FSF_AZ_TX_USER_BYTE_CNT_WIDTH 13
+#define FSF_AZ_TX_USER_BUF_ID_LBN 13
+#define FSF_AZ_TX_USER_BUF_ID_WIDTH 20
+#define FSF_AZ_TX_USER_BYTE_OFS_LBN 0
+#define FSF_AZ_TX_USER_BYTE_OFS_WIDTH 13
+
+/* USER_EV */
+#define FSF_CZ_USER_QID_LBN 32
+#define FSF_CZ_USER_QID_WIDTH 10
+#define FSF_CZ_USER_EV_REG_VALUE_LBN 0
+#define FSF_CZ_USER_EV_REG_VALUE_WIDTH 32
+
+/**************************************************************************
+ *
+ * Falcon B0 PCIe core indirect registers
+ *
+ **************************************************************************
+ */
+
+#define FPCR_BB_PCIE_DEVICE_CTRL_STAT 0x68
+
+#define FPCR_BB_PCIE_LINK_CTRL_STAT 0x70
+
+#define FPCR_BB_ACK_RPL_TIMER 0x700
+#define FPCRF_BB_ACK_TL_LBN 0
+#define FPCRF_BB_ACK_TL_WIDTH 16
+#define FPCRF_BB_RPL_TL_LBN 16
+#define FPCRF_BB_RPL_TL_WIDTH 16
+
+#define FPCR_BB_ACK_FREQ 0x70C
+#define FPCRF_BB_ACK_FREQ_LBN 0
+#define FPCRF_BB_ACK_FREQ_WIDTH 7
+
+/**************************************************************************
+ *
+ * Pseudo-registers and fields
+ *
+ **************************************************************************
+ */
+
+/* Interrupt acknowledge work-around register (A0/A1 only) */
+#define FR_AA_WORK_AROUND_BROKEN_PCI_READS 0x0070
+
+/* EE_SPI_HCMD_REG: SPI host command register */
+/* Values for the EE_SPI_HCMD_SF_SEL register field */
+#define FFE_AB_SPI_DEVICE_EEPROM 0
+#define FFE_AB_SPI_DEVICE_FLASH 1
+
+/* NIC_STAT_REG: NIC status register */
+#define FRF_AB_STRAP_10G_LBN 2
+#define FRF_AB_STRAP_10G_WIDTH 1
+#define FRF_AA_STRAP_PCIE_LBN 0
+#define FRF_AA_STRAP_PCIE_WIDTH 1
+
+/* FATAL_INTR_REG_KER: Fatal interrupt register for Kernel */
+#define FRF_AZ_FATAL_INTR_LBN 0
+#define FRF_AZ_FATAL_INTR_WIDTH 12
+
+/* SRM_CFG_REG: SRAM configuration register */
+/* We treat the number of SRAM banks and bank size as a single field */
+#define FRF_AZ_SRM_NB_SZ_LBN FRF_AZ_SRM_BANK_SIZE_LBN
+#define FRF_AZ_SRM_NB_SZ_WIDTH \
+ (FRF_AZ_SRM_BANK_SIZE_WIDTH + FRF_AZ_SRM_NUM_BANK_WIDTH)
+#define FFE_AB_SRM_NB1_SZ2M 0
+#define FFE_AB_SRM_NB1_SZ4M 1
+#define FFE_AB_SRM_NB1_SZ8M 2
+#define FFE_AB_SRM_NB_SZ_DEF 3
+#define FFE_AB_SRM_NB2_SZ4M 4
+#define FFE_AB_SRM_NB2_SZ8M 5
+#define FFE_AB_SRM_NB2_SZ16M 6
+#define FFE_AB_SRM_NB_SZ_RES 7
+
+/* RX_DESC_UPD_REGP0: Receive descriptor update register. */
+/* We write just the last dword of these registers */
+#define FR_AZ_RX_DESC_UPD_DWORD_P0 \
+ (BUILD_BUG_ON_ZERO(FR_AA_RX_DESC_UPD_KER != FR_BZ_RX_DESC_UPD_P0) + \
+ FR_BZ_RX_DESC_UPD_P0 + 3 * 4)
+#define FRF_AZ_RX_DESC_WPTR_DWORD_LBN (FRF_AZ_RX_DESC_WPTR_LBN - 3 * 32)
+#define FRF_AZ_RX_DESC_WPTR_DWORD_WIDTH FRF_AZ_RX_DESC_WPTR_WIDTH
+
+/* TX_DESC_UPD_REGP0: Transmit descriptor update register. */
+#define FR_AZ_TX_DESC_UPD_DWORD_P0 \
+ (BUILD_BUG_ON_ZERO(FR_AA_TX_DESC_UPD_KER != FR_BZ_TX_DESC_UPD_P0) + \
+ FR_BZ_TX_DESC_UPD_P0 + 3 * 4)
+#define FRF_AZ_TX_DESC_WPTR_DWORD_LBN (FRF_AZ_TX_DESC_WPTR_LBN - 3 * 32)
+#define FRF_AZ_TX_DESC_WPTR_DWORD_WIDTH FRF_AZ_TX_DESC_WPTR_WIDTH
+
+/* GMF_CFG4_REG: GMAC FIFO configuration register 4 */
+#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_LBN 12
+#define FRF_AB_GMF_HSTFLTRFRM_PAUSE_WIDTH 1
+
+/* GMF_CFG5_REG: GMAC FIFO configuration register 5 */
+#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_LBN 12
+#define FRF_AB_GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1
+
+/* XM_TX_PARAM_REG: XGMAC transmit parameter register */
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_LBN FRF_AB_XM_MAX_TX_FRM_SIZE_LO_LBN
+#define FRF_AB_XM_MAX_TX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_TX_FRM_SIZE_HI_WIDTH + \
+ FRF_AB_XM_MAX_TX_FRM_SIZE_LO_WIDTH)
+
+/* XM_RX_PARAM_REG: XGMAC receive parameter register */
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_LBN FRF_AB_XM_MAX_RX_FRM_SIZE_LO_LBN
+#define FRF_AB_XM_MAX_RX_FRM_SIZE_WIDTH (FRF_AB_XM_MAX_RX_FRM_SIZE_HI_WIDTH + \
+ FRF_AB_XM_MAX_RX_FRM_SIZE_LO_WIDTH)
+
+/* XX_TXDRV_CTL_REG: XAUI SerDes transmit drive control register */
+/* Default values */
+#define FFE_AB_XX_TXDRV_DEQ_DEF 0xe /* deq=.6 */
+#define FFE_AB_XX_TXDRV_DTX_DEF 0x5 /* 1.25 */
+#define FFE_AB_XX_SD_CTL_DRV_DEF 0 /* 20mA */
+
+/* XX_CORE_STAT_REG: XAUI XGXS core status register */
+/* XGXS all-lanes status fields */
+#define FRF_AB_XX_SYNC_STAT_LBN FRF_AB_XX_SYNC_STAT0_LBN
+#define FRF_AB_XX_SYNC_STAT_WIDTH 4
+#define FRF_AB_XX_COMMA_DET_LBN FRF_AB_XX_COMMA_DET_CH0_LBN
+#define FRF_AB_XX_COMMA_DET_WIDTH 4
+#define FRF_AB_XX_CHAR_ERR_LBN FRF_AB_XX_CHAR_ERR_CH0_LBN
+#define FRF_AB_XX_CHAR_ERR_WIDTH 4
+#define FRF_AB_XX_DISPERR_LBN FRF_AB_XX_DISPERR_CH0_LBN
+#define FRF_AB_XX_DISPERR_WIDTH 4
+#define FFE_AB_XX_STAT_ALL_LANES 0xf
+#define FRF_AB_XX_FORCE_SIG_LBN FRF_AB_XX_FORCE_SIG0_VAL_LBN
+#define FRF_AB_XX_FORCE_SIG_WIDTH 8
+#define FFE_AB_XX_FORCE_SIG_ALL_LANES 0xff
+
+/* DRIVER_EV */
+/* Sub-fields of an RX flush completion event */
+#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_LBN 12
+#define FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL_WIDTH 1
+#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_LBN 0
+#define FSF_AZ_DRIVER_EV_RX_DESCQ_ID_WIDTH 12
+
+/* EVENT_ENTRY */
+/* Magic number field for event test */
+#define FSF_AZ_DRV_GEN_EV_MAGIC_LBN 0
+#define FSF_AZ_DRV_GEN_EV_MAGIC_WIDTH 32
+
+/**************************************************************************
+ *
+ * Falcon MAC stats
+ *
+ **************************************************************************
+ *
+ */
+
+#define GRxGoodOct_offset 0x0
+#define GRxGoodOct_WIDTH 48
+#define GRxBadOct_offset 0x8
+#define GRxBadOct_WIDTH 48
+#define GRxMissPkt_offset 0x10
+#define GRxMissPkt_WIDTH 32
+#define GRxFalseCRS_offset 0x14
+#define GRxFalseCRS_WIDTH 32
+#define GRxPausePkt_offset 0x18
+#define GRxPausePkt_WIDTH 32
+#define GRxBadPkt_offset 0x1C
+#define GRxBadPkt_WIDTH 32
+#define GRxUcastPkt_offset 0x20
+#define GRxUcastPkt_WIDTH 32
+#define GRxMcastPkt_offset 0x24
+#define GRxMcastPkt_WIDTH 32
+#define GRxBcastPkt_offset 0x28
+#define GRxBcastPkt_WIDTH 32
+#define GRxGoodLt64Pkt_offset 0x2C
+#define GRxGoodLt64Pkt_WIDTH 32
+#define GRxBadLt64Pkt_offset 0x30
+#define GRxBadLt64Pkt_WIDTH 32
+#define GRx64Pkt_offset 0x34
+#define GRx64Pkt_WIDTH 32
+#define GRx65to127Pkt_offset 0x38
+#define GRx65to127Pkt_WIDTH 32
+#define GRx128to255Pkt_offset 0x3C
+#define GRx128to255Pkt_WIDTH 32
+#define GRx256to511Pkt_offset 0x40
+#define GRx256to511Pkt_WIDTH 32
+#define GRx512to1023Pkt_offset 0x44
+#define GRx512to1023Pkt_WIDTH 32
+#define GRx1024to15xxPkt_offset 0x48
+#define GRx1024to15xxPkt_WIDTH 32
+#define GRx15xxtoJumboPkt_offset 0x4C
+#define GRx15xxtoJumboPkt_WIDTH 32
+#define GRxGtJumboPkt_offset 0x50
+#define GRxGtJumboPkt_WIDTH 32
+#define GRxFcsErr64to15xxPkt_offset 0x54
+#define GRxFcsErr64to15xxPkt_WIDTH 32
+#define GRxFcsErr15xxtoJumboPkt_offset 0x58
+#define GRxFcsErr15xxtoJumboPkt_WIDTH 32
+#define GRxFcsErrGtJumboPkt_offset 0x5C
+#define GRxFcsErrGtJumboPkt_WIDTH 32
+#define GTxGoodBadOct_offset 0x80
+#define GTxGoodBadOct_WIDTH 48
+#define GTxGoodOct_offset 0x88
+#define GTxGoodOct_WIDTH 48
+#define GTxSglColPkt_offset 0x90
+#define GTxSglColPkt_WIDTH 32
+#define GTxMultColPkt_offset 0x94
+#define GTxMultColPkt_WIDTH 32
+#define GTxExColPkt_offset 0x98
+#define GTxExColPkt_WIDTH 32
+#define GTxDefPkt_offset 0x9C
+#define GTxDefPkt_WIDTH 32
+#define GTxLateCol_offset 0xA0
+#define GTxLateCol_WIDTH 32
+#define GTxExDefPkt_offset 0xA4
+#define GTxExDefPkt_WIDTH 32
+#define GTxPausePkt_offset 0xA8
+#define GTxPausePkt_WIDTH 32
+#define GTxBadPkt_offset 0xAC
+#define GTxBadPkt_WIDTH 32
+#define GTxUcastPkt_offset 0xB0
+#define GTxUcastPkt_WIDTH 32
+#define GTxMcastPkt_offset 0xB4
+#define GTxMcastPkt_WIDTH 32
+#define GTxBcastPkt_offset 0xB8
+#define GTxBcastPkt_WIDTH 32
+#define GTxLt64Pkt_offset 0xBC
+#define GTxLt64Pkt_WIDTH 32
+#define GTx64Pkt_offset 0xC0
+#define GTx64Pkt_WIDTH 32
+#define GTx65to127Pkt_offset 0xC4
+#define GTx65to127Pkt_WIDTH 32
+#define GTx128to255Pkt_offset 0xC8
+#define GTx128to255Pkt_WIDTH 32
+#define GTx256to511Pkt_offset 0xCC
+#define GTx256to511Pkt_WIDTH 32
+#define GTx512to1023Pkt_offset 0xD0
+#define GTx512to1023Pkt_WIDTH 32
+#define GTx1024to15xxPkt_offset 0xD4
+#define GTx1024to15xxPkt_WIDTH 32
+#define GTx15xxtoJumboPkt_offset 0xD8
+#define GTx15xxtoJumboPkt_WIDTH 32
+#define GTxGtJumboPkt_offset 0xDC
+#define GTxGtJumboPkt_WIDTH 32
+#define GTxNonTcpUdpPkt_offset 0xE0
+#define GTxNonTcpUdpPkt_WIDTH 16
+#define GTxMacSrcErrPkt_offset 0xE4
+#define GTxMacSrcErrPkt_WIDTH 16
+#define GTxIpSrcErrPkt_offset 0xE8
+#define GTxIpSrcErrPkt_WIDTH 16
+#define GDmaDone_offset 0xEC
+#define GDmaDone_WIDTH 32
+
+#define XgRxOctets_offset 0x0
+#define XgRxOctets_WIDTH 48
+#define XgRxOctetsOK_offset 0x8
+#define XgRxOctetsOK_WIDTH 48
+#define XgRxPkts_offset 0x10
+#define XgRxPkts_WIDTH 32
+#define XgRxPktsOK_offset 0x14
+#define XgRxPktsOK_WIDTH 32
+#define XgRxBroadcastPkts_offset 0x18
+#define XgRxBroadcastPkts_WIDTH 32
+#define XgRxMulticastPkts_offset 0x1C
+#define XgRxMulticastPkts_WIDTH 32
+#define XgRxUnicastPkts_offset 0x20
+#define XgRxUnicastPkts_WIDTH 32
+#define XgRxUndersizePkts_offset 0x24
+#define XgRxUndersizePkts_WIDTH 32
+#define XgRxOversizePkts_offset 0x28
+#define XgRxOversizePkts_WIDTH 32
+#define XgRxJabberPkts_offset 0x2C
+#define XgRxJabberPkts_WIDTH 32
+#define XgRxUndersizeFCSerrorPkts_offset 0x30
+#define XgRxUndersizeFCSerrorPkts_WIDTH 32
+#define XgRxDropEvents_offset 0x34
+#define XgRxDropEvents_WIDTH 32
+#define XgRxFCSerrorPkts_offset 0x38
+#define XgRxFCSerrorPkts_WIDTH 32
+#define XgRxAlignError_offset 0x3C
+#define XgRxAlignError_WIDTH 32
+#define XgRxSymbolError_offset 0x40
+#define XgRxSymbolError_WIDTH 32
+#define XgRxInternalMACError_offset 0x44
+#define XgRxInternalMACError_WIDTH 32
+#define XgRxControlPkts_offset 0x48
+#define XgRxControlPkts_WIDTH 32
+#define XgRxPausePkts_offset 0x4C
+#define XgRxPausePkts_WIDTH 32
+#define XgRxPkts64Octets_offset 0x50
+#define XgRxPkts64Octets_WIDTH 32
+#define XgRxPkts65to127Octets_offset 0x54
+#define XgRxPkts65to127Octets_WIDTH 32
+#define XgRxPkts128to255Octets_offset 0x58
+#define XgRxPkts128to255Octets_WIDTH 32
+#define XgRxPkts256to511Octets_offset 0x5C
+#define XgRxPkts256to511Octets_WIDTH 32
+#define XgRxPkts512to1023Octets_offset 0x60
+#define XgRxPkts512to1023Octets_WIDTH 32
+#define XgRxPkts1024to15xxOctets_offset 0x64
+#define XgRxPkts1024to15xxOctets_WIDTH 32
+#define XgRxPkts15xxtoMaxOctets_offset 0x68
+#define XgRxPkts15xxtoMaxOctets_WIDTH 32
+#define XgRxLengthError_offset 0x6C
+#define XgRxLengthError_WIDTH 32
+#define XgTxPkts_offset 0x80
+#define XgTxPkts_WIDTH 32
+#define XgTxOctets_offset 0x88
+#define XgTxOctets_WIDTH 48
+#define XgTxMulticastPkts_offset 0x90
+#define XgTxMulticastPkts_WIDTH 32
+#define XgTxBroadcastPkts_offset 0x94
+#define XgTxBroadcastPkts_WIDTH 32
+#define XgTxUnicastPkts_offset 0x98
+#define XgTxUnicastPkts_WIDTH 32
+#define XgTxControlPkts_offset 0x9C
+#define XgTxControlPkts_WIDTH 32
+#define XgTxPausePkts_offset 0xA0
+#define XgTxPausePkts_WIDTH 32
+#define XgTxPkts64Octets_offset 0xA4
+#define XgTxPkts64Octets_WIDTH 32
+#define XgTxPkts65to127Octets_offset 0xA8
+#define XgTxPkts65to127Octets_WIDTH 32
+#define XgTxPkts128to255Octets_offset 0xAC
+#define XgTxPkts128to255Octets_WIDTH 32
+#define XgTxPkts256to511Octets_offset 0xB0
+#define XgTxPkts256to511Octets_WIDTH 32
+#define XgTxPkts512to1023Octets_offset 0xB4
+#define XgTxPkts512to1023Octets_WIDTH 32
+#define XgTxPkts1024to15xxOctets_offset 0xB8
+#define XgTxPkts1024to15xxOctets_WIDTH 32
+#define XgTxPkts1519toMaxOctets_offset 0xBC
+#define XgTxPkts1519toMaxOctets_WIDTH 32
+#define XgTxUndersizePkts_offset 0xC0
+#define XgTxUndersizePkts_WIDTH 32
+#define XgTxOversizePkts_offset 0xC4
+#define XgTxOversizePkts_WIDTH 32
+#define XgTxNonTcpUdpPkt_offset 0xC8
+#define XgTxNonTcpUdpPkt_WIDTH 16
+#define XgTxMacSrcErrPkt_offset 0xCC
+#define XgTxMacSrcErrPkt_WIDTH 16
+#define XgTxIpSrcErrPkt_offset 0xD0
+#define XgTxIpSrcErrPkt_WIDTH 16
+#define XgDmaDone_offset 0xD4
+#define XgDmaDone_WIDTH 32
+
+#define FALCON_STATS_NOT_DONE 0x00000000
+#define FALCON_STATS_DONE 0xffffffff
+
+/**************************************************************************
+ *
+ * Falcon non-volatile configuration
+ *
+ **************************************************************************
+ */
+
+/* Board configuration v2 (v1 is obsolete; later versions are compatible) */
+struct falcon_nvconfig_board_v2 {
+ __le16 nports;
+ u8 port0_phy_addr;
+ u8 port0_phy_type;
+ u8 port1_phy_addr;
+ u8 port1_phy_type;
+ __le16 asic_sub_revision;
+ __le16 board_revision;
+} __packed;
+
+/* Board configuration v3 extra information */
+struct falcon_nvconfig_board_v3 {
+ __le32 spi_device_type[2];
+} __packed;
+
+/* Bit numbers for spi_device_type */
+#define SPI_DEV_TYPE_SIZE_LBN 0
+#define SPI_DEV_TYPE_SIZE_WIDTH 5
+#define SPI_DEV_TYPE_ADDR_LEN_LBN 6
+#define SPI_DEV_TYPE_ADDR_LEN_WIDTH 2
+#define SPI_DEV_TYPE_ERASE_CMD_LBN 8
+#define SPI_DEV_TYPE_ERASE_CMD_WIDTH 8
+#define SPI_DEV_TYPE_ERASE_SIZE_LBN 16
+#define SPI_DEV_TYPE_ERASE_SIZE_WIDTH 5
+#define SPI_DEV_TYPE_BLOCK_SIZE_LBN 24
+#define SPI_DEV_TYPE_BLOCK_SIZE_WIDTH 5
+#define SPI_DEV_TYPE_FIELD(type, field) \
+ (((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field)))
+
+#define FALCON_NVCONFIG_OFFSET 0x300
+
+#define FALCON_NVCONFIG_BOARD_MAGIC_NUM 0xFA1C
+struct falcon_nvconfig {
+ efx_oword_t ee_vpd_cfg_reg; /* 0x300 */
+ u8 mac_address[2][8]; /* 0x310 */
+ efx_oword_t pcie_sd_ctl0123_reg; /* 0x320 */
+ efx_oword_t pcie_sd_ctl45_reg; /* 0x330 */
+ efx_oword_t pcie_pcs_ctl_stat_reg; /* 0x340 */
+ efx_oword_t hw_init_reg; /* 0x350 */
+ efx_oword_t nic_stat_reg; /* 0x360 */
+ efx_oword_t glb_ctl_reg; /* 0x370 */
+ efx_oword_t srm_cfg_reg; /* 0x380 */
+ efx_oword_t spare_reg; /* 0x390 */
+ __le16 board_magic_num; /* 0x3A0 */
+ __le16 board_struct_ver;
+ __le16 board_checksum;
+ struct falcon_nvconfig_board_v2 board_v2;
+ efx_oword_t ee_base_page_reg; /* 0x3B0 */
+ struct falcon_nvconfig_board_v3 board_v3; /* 0x3C0 */
+} __packed;
+
+#endif /* EFX_REGS_H */
diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c
index 98bff5ada09..a97c923b560 100644
--- a/drivers/net/sfc/rx.c
+++ b/drivers/net/sfc/rx.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2005-2008 Solarflare Communications Inc.
+ * Copyright 2005-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -16,9 +16,8 @@
#include <net/ip.h>
#include <net/checksum.h>
#include "net_driver.h"
-#include "rx.h"
#include "efx.h"
-#include "falcon.h"
+#include "nic.h"
#include "selftest.h"
#include "workarounds.h"
@@ -61,7 +60,7 @@
* rx_alloc_method = (rx_alloc_level > RX_ALLOC_LEVEL_LRO ?
* RX_ALLOC_METHOD_PAGE : RX_ALLOC_METHOD_SKB)
*/
-static int rx_alloc_method = RX_ALLOC_METHOD_PAGE;
+static int rx_alloc_method = RX_ALLOC_METHOD_AUTO;
#define RX_ALLOC_LEVEL_LRO 0x2000
#define RX_ALLOC_LEVEL_MAX 0x3000
@@ -293,8 +292,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue,
* fill anyway.
*/
fill_level = (rx_queue->added_count - rx_queue->removed_count);
- EFX_BUG_ON_PARANOID(fill_level >
- rx_queue->efx->type->rxd_ring_mask + 1);
+ EFX_BUG_ON_PARANOID(fill_level > EFX_RXQ_SIZE);
/* Don't fill if we don't need to */
if (fill_level >= rx_queue->fast_fill_trigger)
@@ -316,8 +314,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue,
retry:
/* Recalculate current fill level now that we have the lock */
fill_level = (rx_queue->added_count - rx_queue->removed_count);
- EFX_BUG_ON_PARANOID(fill_level >
- rx_queue->efx->type->rxd_ring_mask + 1);
+ EFX_BUG_ON_PARANOID(fill_level > EFX_RXQ_SIZE);
space = rx_queue->fast_fill_limit - fill_level;
if (space < EFX_RX_BATCH)
goto out_unlock;
@@ -329,8 +326,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue,
do {
for (i = 0; i < EFX_RX_BATCH; ++i) {
- index = (rx_queue->added_count &
- rx_queue->efx->type->rxd_ring_mask);
+ index = rx_queue->added_count & EFX_RXQ_MASK;
rx_buf = efx_rx_buffer(rx_queue, index);
rc = efx_init_rx_buffer(rx_queue, rx_buf);
if (unlikely(rc))
@@ -345,7 +341,7 @@ static int __efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue,
out:
/* Send write pointer to card. */
- falcon_notify_rx_desc(rx_queue);
+ efx_nic_notify_rx_desc(rx_queue);
/* If the fast fill is running inside from the refill tasklet, then
* for SMP systems it may be running on a different CPU to
@@ -448,17 +444,23 @@ static void efx_rx_packet_lro(struct efx_channel *channel,
bool checksummed)
{
struct napi_struct *napi = &channel->napi_str;
+ gro_result_t gro_result;
/* Pass the skb/page into the LRO engine */
if (rx_buf->page) {
- struct sk_buff *skb = napi_get_frags(napi);
+ struct page *page = rx_buf->page;
+ struct sk_buff *skb;
+ EFX_BUG_ON_PARANOID(rx_buf->skb);
+ rx_buf->page = NULL;
+
+ skb = napi_get_frags(napi);
if (!skb) {
- put_page(rx_buf->page);
- goto out;
+ put_page(page);
+ return;
}
- skb_shinfo(skb)->frags[0].page = rx_buf->page;
+ skb_shinfo(skb)->frags[0].page = page;
skb_shinfo(skb)->frags[0].page_offset =
efx_rx_buf_offset(rx_buf);
skb_shinfo(skb)->frags[0].size = rx_buf->len;
@@ -470,17 +472,24 @@ static void efx_rx_packet_lro(struct efx_channel *channel,
skb->ip_summed =
checksummed ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
- napi_gro_frags(napi);
+ skb_record_rx_queue(skb, channel->channel);
-out:
- EFX_BUG_ON_PARANOID(rx_buf->skb);
- rx_buf->page = NULL;
+ gro_result = napi_gro_frags(napi);
} else {
- EFX_BUG_ON_PARANOID(!rx_buf->skb);
- EFX_BUG_ON_PARANOID(!checksummed);
+ struct sk_buff *skb = rx_buf->skb;
- napi_gro_receive(napi, rx_buf->skb);
+ EFX_BUG_ON_PARANOID(!skb);
+ EFX_BUG_ON_PARANOID(!checksummed);
rx_buf->skb = NULL;
+
+ gro_result = napi_gro_receive(napi, skb);
+ }
+
+ if (gro_result == GRO_NORMAL) {
+ channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
+ } else if (gro_result != GRO_DROP) {
+ channel->rx_alloc_level += RX_ALLOC_FACTOR_LRO;
+ channel->irq_mod_score += 2;
}
}
@@ -558,7 +567,7 @@ void __efx_rx_packet(struct efx_channel *channel,
if (unlikely(efx->loopback_selftest)) {
efx_loopback_rx_packet(efx, rx_buf->data, rx_buf->len);
efx_free_rx_buffer(efx, rx_buf);
- goto done;
+ return;
}
if (rx_buf->skb) {
@@ -570,34 +579,28 @@ void __efx_rx_packet(struct efx_channel *channel,
* at the ethernet header */
rx_buf->skb->protocol = eth_type_trans(rx_buf->skb,
efx->net_dev);
+
+ skb_record_rx_queue(rx_buf->skb, channel->channel);
}
if (likely(checksummed || rx_buf->page)) {
efx_rx_packet_lro(channel, rx_buf, checksummed);
- goto done;
+ return;
}
/* We now own the SKB */
skb = rx_buf->skb;
rx_buf->skb = NULL;
-
- EFX_BUG_ON_PARANOID(rx_buf->page);
- EFX_BUG_ON_PARANOID(rx_buf->skb);
EFX_BUG_ON_PARANOID(!skb);
/* Set the SKB flags */
skb->ip_summed = CHECKSUM_NONE;
- skb_record_rx_queue(skb, channel->channel);
-
/* Pass the packet up */
netif_receive_skb(skb);
/* Update allocation strategy method */
channel->rx_alloc_level += RX_ALLOC_FACTOR_SKB;
-
-done:
- ;
}
void efx_rx_strategy(struct efx_channel *channel)
@@ -632,12 +635,12 @@ int efx_probe_rx_queue(struct efx_rx_queue *rx_queue)
EFX_LOG(efx, "creating RX queue %d\n", rx_queue->queue);
/* Allocate RX buffers */
- rxq_size = (efx->type->rxd_ring_mask + 1) * sizeof(*rx_queue->buffer);
+ rxq_size = EFX_RXQ_SIZE * sizeof(*rx_queue->buffer);
rx_queue->buffer = kzalloc(rxq_size, GFP_KERNEL);
if (!rx_queue->buffer)
return -ENOMEM;
- rc = falcon_probe_rx(rx_queue);
+ rc = efx_nic_probe_rx(rx_queue);
if (rc) {
kfree(rx_queue->buffer);
rx_queue->buffer = NULL;
@@ -647,7 +650,6 @@ int efx_probe_rx_queue(struct efx_rx_queue *rx_queue)
void efx_init_rx_queue(struct efx_rx_queue *rx_queue)
{
- struct efx_nic *efx = rx_queue->efx;
unsigned int max_fill, trigger, limit;
EFX_LOG(rx_queue->efx, "initialising RX queue %d\n", rx_queue->queue);
@@ -660,7 +662,7 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue)
rx_queue->min_overfill = -1U;
/* Initialise limit fields */
- max_fill = efx->type->rxd_ring_mask + 1 - EFX_RXD_HEAD_ROOM;
+ max_fill = EFX_RXQ_SIZE - EFX_RXD_HEAD_ROOM;
trigger = max_fill * min(rx_refill_threshold, 100U) / 100U;
limit = max_fill * min(rx_refill_limit, 100U) / 100U;
@@ -669,7 +671,7 @@ void efx_init_rx_queue(struct efx_rx_queue *rx_queue)
rx_queue->fast_fill_limit = limit;
/* Set up RX descriptor ring */
- falcon_init_rx(rx_queue);
+ efx_nic_init_rx(rx_queue);
}
void efx_fini_rx_queue(struct efx_rx_queue *rx_queue)
@@ -679,11 +681,11 @@ void efx_fini_rx_queue(struct efx_rx_queue *rx_queue)
EFX_LOG(rx_queue->efx, "shutting down RX queue %d\n", rx_queue->queue);
- falcon_fini_rx(rx_queue);
+ efx_nic_fini_rx(rx_queue);
/* Release RX buffers NB start at index 0 not current HW ptr */
if (rx_queue->buffer) {
- for (i = 0; i <= rx_queue->efx->type->rxd_ring_mask; i++) {
+ for (i = 0; i <= EFX_RXQ_MASK; i++) {
rx_buf = efx_rx_buffer(rx_queue, i);
efx_fini_rx_buffer(rx_queue, rx_buf);
}
@@ -704,7 +706,7 @@ void efx_remove_rx_queue(struct efx_rx_queue *rx_queue)
{
EFX_LOG(rx_queue->efx, "destroying RX queue %d\n", rx_queue->queue);
- falcon_remove_rx(rx_queue);
+ efx_nic_remove_rx(rx_queue);
kfree(rx_queue->buffer);
rx_queue->buffer = NULL;
diff --git a/drivers/net/sfc/rx.h b/drivers/net/sfc/rx.h
deleted file mode 100644
index 42ee7555a80..00000000000
--- a/drivers/net/sfc/rx.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2006 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_RX_H
-#define EFX_RX_H
-
-#include "net_driver.h"
-
-int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
-void efx_remove_rx_queue(struct efx_rx_queue *rx_queue);
-void efx_init_rx_queue(struct efx_rx_queue *rx_queue);
-void efx_fini_rx_queue(struct efx_rx_queue *rx_queue);
-
-void efx_rx_strategy(struct efx_channel *channel);
-void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue);
-void efx_rx_work(struct work_struct *data);
-void __efx_rx_packet(struct efx_channel *channel,
- struct efx_rx_buffer *rx_buf, bool checksummed);
-
-#endif /* EFX_RX_H */
diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c
index 817c7efc11e..14949bb303a 100644
--- a/drivers/net/sfc/selftest.c
+++ b/drivers/net/sfc/selftest.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -20,14 +20,12 @@
#include <linux/rtnetlink.h>
#include <asm/io.h>
#include "net_driver.h"
-#include "ethtool.h"
#include "efx.h"
-#include "falcon.h"
+#include "nic.h"
#include "selftest.h"
-#include "boards.h"
#include "workarounds.h"
#include "spi.h"
-#include "falcon_io.h"
+#include "io.h"
#include "mdio_10g.h"
/*
@@ -57,6 +55,7 @@ static const char *payload_msg =
* @flush: Drop all packets in efx_loopback_rx_packet
* @packet_count: Number of packets being used in this test
* @skbs: An array of skbs transmitted
+ * @offload_csum: Checksums are being offloaded
* @rx_good: RX good packet count
* @rx_bad: RX bad packet count
* @payload: Payload used in tests
@@ -65,10 +64,7 @@ struct efx_loopback_state {
bool flush;
int packet_count;
struct sk_buff **skbs;
-
- /* Checksums are being offloaded */
bool offload_csum;
-
atomic_t rx_good;
atomic_t rx_bad;
struct efx_loopback_payload payload;
@@ -104,7 +100,7 @@ static int efx_test_mdio(struct efx_nic *efx, struct efx_self_tests *tests)
}
if (EFX_IS10G(efx)) {
- rc = efx_mdio_check_mmds(efx, efx->phy_op->mmds, 0);
+ rc = efx_mdio_check_mmds(efx, efx->mdio.mmds, 0);
if (rc)
goto out;
}
@@ -117,23 +113,26 @@ out:
static int efx_test_nvram(struct efx_nic *efx, struct efx_self_tests *tests)
{
- int rc;
+ int rc = 0;
+
+ if (efx->type->test_nvram) {
+ rc = efx->type->test_nvram(efx);
+ tests->nvram = rc ? -1 : 1;
+ }
- rc = falcon_read_nvram(efx, NULL);
- tests->nvram = rc ? -1 : 1;
return rc;
}
static int efx_test_chip(struct efx_nic *efx, struct efx_self_tests *tests)
{
- int rc;
+ int rc = 0;
- /* Not supported on A-series silicon */
- if (falcon_rev(efx) < FALCON_REV_B0)
- return 0;
+ /* Test register access */
+ if (efx->type->test_registers) {
+ rc = efx->type->test_registers(efx);
+ tests->registers = rc ? -1 : 1;
+ }
- rc = falcon_test_registers(efx);
- tests->registers = rc ? -1 : 1;
return rc;
}
@@ -165,7 +164,7 @@ static int efx_test_interrupts(struct efx_nic *efx,
goto success;
}
- falcon_generate_interrupt(efx);
+ efx_nic_generate_interrupt(efx);
/* Wait for arrival of test interrupt. */
EFX_LOG(efx, "waiting for test interrupt\n");
@@ -177,8 +176,8 @@ static int efx_test_interrupts(struct efx_nic *efx,
return -ETIMEDOUT;
success:
- EFX_LOG(efx, "test interrupt (mode %d) seen on CPU%d\n",
- efx->interrupt_mode, efx->last_irq_cpu);
+ EFX_LOG(efx, "%s test interrupt seen on CPU%d\n", INT_MODE(efx),
+ efx->last_irq_cpu);
tests->interrupt = 1;
return 0;
}
@@ -203,7 +202,7 @@ static int efx_test_eventq_irq(struct efx_channel *channel,
channel->eventq_magic = 0;
smp_wmb();
- falcon_generate_test_event(channel, magic);
+ efx_nic_generate_test_event(channel, magic);
/* Wait for arrival of interrupt */
count = 0;
@@ -254,9 +253,6 @@ static int efx_test_phy(struct efx_nic *efx, struct efx_self_tests *tests,
if (!efx->phy_op->run_tests)
return 0;
- EFX_BUG_ON_PARANOID(efx->phy_op->num_tests == 0 ||
- efx->phy_op->num_tests > EFX_MAX_PHY_TESTS);
-
mutex_lock(&efx->mac_lock);
rc = efx->phy_op->run_tests(efx, tests->phy, flags);
mutex_unlock(&efx->mac_lock);
@@ -426,7 +422,7 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue)
if (efx_dev_registered(efx))
netif_tx_lock_bh(efx->net_dev);
- rc = efx_xmit(efx, tx_queue, skb);
+ rc = efx_enqueue_skb(tx_queue, skb);
if (efx_dev_registered(efx))
netif_tx_unlock_bh(efx->net_dev);
@@ -439,7 +435,6 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue)
kfree_skb(skb);
return -EPIPE;
}
- efx->net_dev->trans_start = jiffies;
}
return 0;
@@ -527,7 +522,7 @@ efx_test_loopback(struct efx_tx_queue *tx_queue,
for (i = 0; i < 3; i++) {
/* Determine how many packets to send */
- state->packet_count = (efx->type->txd_ring_mask + 1) / 3;
+ state->packet_count = EFX_TXQ_SIZE / 3;
state->packet_count = min(1 << (i << 2), state->packet_count);
state->skbs = kzalloc(sizeof(state->skbs[0]) *
state->packet_count, GFP_KERNEL);
@@ -568,14 +563,49 @@ efx_test_loopback(struct efx_tx_queue *tx_queue,
return 0;
}
+/* Wait for link up. On Falcon, we would prefer to rely on efx_monitor, but
+ * any contention on the mac lock (via e.g. efx_mac_mcast_work) causes it
+ * to delay and retry. Therefore, it's safer to just poll directly. Wait
+ * for link up and any faults to dissipate. */
+static int efx_wait_for_link(struct efx_nic *efx)
+{
+ struct efx_link_state *link_state = &efx->link_state;
+ int count;
+ bool link_up;
+
+ for (count = 0; count < 40; count++) {
+ schedule_timeout_uninterruptible(HZ / 10);
+
+ if (efx->type->monitor != NULL) {
+ mutex_lock(&efx->mac_lock);
+ efx->type->monitor(efx);
+ mutex_unlock(&efx->mac_lock);
+ } else {
+ struct efx_channel *channel = &efx->channel[0];
+ if (channel->work_pending)
+ efx_process_channel_now(channel);
+ }
+
+ mutex_lock(&efx->mac_lock);
+ link_up = link_state->up;
+ if (link_up)
+ link_up = !efx->mac_op->check_fault(efx);
+ mutex_unlock(&efx->mac_lock);
+
+ if (link_up)
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests,
unsigned int loopback_modes)
{
enum efx_loopback_mode mode;
struct efx_loopback_state *state;
struct efx_tx_queue *tx_queue;
- bool link_up;
- int count, rc = 0;
+ int rc = 0;
/* Set the port loopback_selftest member. From this point on
* all received packets will be dropped. Mark the state as
@@ -594,46 +624,23 @@ static int efx_test_loopbacks(struct efx_nic *efx, struct efx_self_tests *tests,
/* Move the port into the specified loopback mode. */
state->flush = true;
+ mutex_lock(&efx->mac_lock);
efx->loopback_mode = mode;
- efx_reconfigure_port(efx);
-
- /* Wait for the PHY to signal the link is up. Interrupts
- * are enabled for PHY's using LASI, otherwise we poll()
- * quickly */
- count = 0;
- do {
- struct efx_channel *channel = &efx->channel[0];
+ rc = __efx_reconfigure_port(efx);
+ mutex_unlock(&efx->mac_lock);
+ if (rc) {
+ EFX_ERR(efx, "unable to move into %s loopback\n",
+ LOOPBACK_MODE(efx));
+ goto out;
+ }
- efx->phy_op->poll(efx);
- schedule_timeout_uninterruptible(HZ / 10);
- if (channel->work_pending)
- efx_process_channel_now(channel);
- /* Wait for PHY events to be processed */
- flush_workqueue(efx->workqueue);
- rmb();
-
- /* We need both the phy and xaui links to be ok.
- * rather than relying on the falcon_xmac irq/poll
- * regime, just poll xaui directly */
- link_up = efx->link_up;
- if (link_up && EFX_IS10G(efx) &&
- !falcon_xaui_link_ok(efx))
- link_up = false;
-
- } while ((++count < 20) && !link_up);
-
- /* The link should now be up. If it isn't, there is no point
- * in attempting a loopback test */
- if (!link_up) {
+ rc = efx_wait_for_link(efx);
+ if (rc) {
EFX_ERR(efx, "loopback %s never came up\n",
LOOPBACK_MODE(efx));
- rc = -EIO;
goto out;
}
- EFX_LOG(efx, "link came up in %s loopback in %d iterations\n",
- LOOPBACK_MODE(efx), count);
-
/* Test every TX queue */
efx_for_each_tx_queue(tx_queue, efx) {
state->offload_csum = (tx_queue->queue ==
@@ -667,7 +674,6 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
enum efx_loopback_mode loopback_mode = efx->loopback_mode;
int phy_mode = efx->phy_mode;
enum reset_type reset_method = RESET_TYPE_INVISIBLE;
- struct ethtool_cmd ecmd;
struct efx_channel *channel;
int rc_test = 0, rc_reset = 0, rc;
@@ -720,21 +726,21 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
mutex_unlock(&efx->mac_lock);
/* free up all consumers of SRAM (including all the queues) */
- efx_reset_down(efx, reset_method, &ecmd);
+ efx_reset_down(efx, reset_method);
rc = efx_test_chip(efx, tests);
if (rc && !rc_test)
rc_test = rc;
/* reset the chip to recover from the register test */
- rc_reset = falcon_reset_hw(efx, reset_method);
+ rc_reset = efx->type->reset(efx, reset_method);
/* Ensure that the phy is powered and out of loopback
* for the bist and loopback tests */
efx->phy_mode &= ~PHY_MODE_LOW_POWER;
efx->loopback_mode = LOOPBACK_NONE;
- rc = efx_reset_up(efx, reset_method, &ecmd, rc_reset == 0);
+ rc = efx_reset_up(efx, reset_method, rc_reset == 0);
if (rc && !rc_reset)
rc_reset = rc;
@@ -753,10 +759,12 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests,
rc_test = rc;
/* restore the PHY to the previous state */
- efx->loopback_mode = loopback_mode;
+ mutex_lock(&efx->mac_lock);
efx->phy_mode = phy_mode;
efx->port_inhibited = false;
- efx_ethtool_set_settings(efx->net_dev, &ecmd);
+ efx->loopback_mode = loopback_mode;
+ __efx_reconfigure_port(efx);
+ mutex_unlock(&efx->mac_lock);
return rc_test;
}
diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c
deleted file mode 100644
index 49eb91b5f50..00000000000
--- a/drivers/net/sfc/sfe4001.c
+++ /dev/null
@@ -1,435 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-/*****************************************************************************
- * Support for the SFE4001 and SFN4111T NICs.
- *
- * The SFE4001 does not power-up fully at reset due to its high power
- * consumption. We control its power via a PCA9539 I/O expander.
- * Both boards have a MAX6647 temperature monitor which we expose to
- * the lm90 driver.
- *
- * This also provides minimal support for reflashing the PHY, which is
- * initiated by resetting it with the FLASH_CFG_1 pin pulled down.
- * On SFE4001 rev A2 and later this is connected to the 3V3X output of
- * the IO-expander; on the SFN4111T it is connected to Falcon's GPIO3.
- * We represent reflash mode as PHY_MODE_SPECIAL and make it mutually
- * exclusive with the network device being open.
- */
-
-#include <linux/delay.h>
-#include <linux/rtnetlink.h>
-#include "net_driver.h"
-#include "efx.h"
-#include "phy.h"
-#include "boards.h"
-#include "falcon.h"
-#include "falcon_hwdefs.h"
-#include "falcon_io.h"
-#include "mac.h"
-#include "workarounds.h"
-
-/**************************************************************************
- *
- * I2C IO Expander device
- *
- **************************************************************************/
-#define PCA9539 0x74
-
-#define P0_IN 0x00
-#define P0_OUT 0x02
-#define P0_INVERT 0x04
-#define P0_CONFIG 0x06
-
-#define P0_EN_1V0X_LBN 0
-#define P0_EN_1V0X_WIDTH 1
-#define P0_EN_1V2_LBN 1
-#define P0_EN_1V2_WIDTH 1
-#define P0_EN_2V5_LBN 2
-#define P0_EN_2V5_WIDTH 1
-#define P0_EN_3V3X_LBN 3
-#define P0_EN_3V3X_WIDTH 1
-#define P0_EN_5V_LBN 4
-#define P0_EN_5V_WIDTH 1
-#define P0_SHORTEN_JTAG_LBN 5
-#define P0_SHORTEN_JTAG_WIDTH 1
-#define P0_X_TRST_LBN 6
-#define P0_X_TRST_WIDTH 1
-#define P0_DSP_RESET_LBN 7
-#define P0_DSP_RESET_WIDTH 1
-
-#define P1_IN 0x01
-#define P1_OUT 0x03
-#define P1_INVERT 0x05
-#define P1_CONFIG 0x07
-
-#define P1_AFE_PWD_LBN 0
-#define P1_AFE_PWD_WIDTH 1
-#define P1_DSP_PWD25_LBN 1
-#define P1_DSP_PWD25_WIDTH 1
-#define P1_RESERVED_LBN 2
-#define P1_RESERVED_WIDTH 2
-#define P1_SPARE_LBN 4
-#define P1_SPARE_WIDTH 4
-
-/* Temperature Sensor */
-#define MAX664X_REG_RSL 0x02
-#define MAX664X_REG_WLHO 0x0B
-
-static void sfe4001_poweroff(struct efx_nic *efx)
-{
- struct i2c_client *ioexp_client = efx->board_info.ioexp_client;
- struct i2c_client *hwmon_client = efx->board_info.hwmon_client;
-
- /* Turn off all power rails and disable outputs */
- i2c_smbus_write_byte_data(ioexp_client, P0_OUT, 0xff);
- i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG, 0xff);
- i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff);
-
- /* Clear any over-temperature alert */
- i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
-}
-
-static int sfe4001_poweron(struct efx_nic *efx)
-{
- struct i2c_client *hwmon_client = efx->board_info.hwmon_client;
- struct i2c_client *ioexp_client = efx->board_info.ioexp_client;
- unsigned int i, j;
- int rc;
- u8 out;
-
- /* Clear any previous over-temperature alert */
- rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
- if (rc < 0)
- return rc;
-
- /* Enable port 0 and port 1 outputs on IO expander */
- rc = i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0x00);
- if (rc)
- return rc;
- rc = i2c_smbus_write_byte_data(ioexp_client, P1_CONFIG,
- 0xff & ~(1 << P1_SPARE_LBN));
- if (rc)
- goto fail_on;
-
- /* If PHY power is on, turn it all off and wait 1 second to
- * ensure a full reset.
- */
- rc = i2c_smbus_read_byte_data(ioexp_client, P0_OUT);
- if (rc < 0)
- goto fail_on;
- out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) |
- (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) |
- (0 << P0_EN_1V0X_LBN));
- if (rc != out) {
- EFX_INFO(efx, "power-cycling PHY\n");
- rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
- if (rc)
- goto fail_on;
- schedule_timeout_uninterruptible(HZ);
- }
-
- for (i = 0; i < 20; ++i) {
- /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */
- out = 0xff & ~((1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) |
- (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) |
- (1 << P0_X_TRST_LBN));
- if (efx->phy_mode & PHY_MODE_SPECIAL)
- out |= 1 << P0_EN_3V3X_LBN;
-
- rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
- if (rc)
- goto fail_on;
- msleep(10);
-
- /* Turn on 1V power rail */
- out &= ~(1 << P0_EN_1V0X_LBN);
- rc = i2c_smbus_write_byte_data(ioexp_client, P0_OUT, out);
- if (rc)
- goto fail_on;
-
- EFX_INFO(efx, "waiting for DSP boot (attempt %d)...\n", i);
-
- /* In flash config mode, DSP does not turn on AFE, so
- * just wait 1 second.
- */
- if (efx->phy_mode & PHY_MODE_SPECIAL) {
- schedule_timeout_uninterruptible(HZ);
- return 0;
- }
-
- for (j = 0; j < 10; ++j) {
- msleep(100);
-
- /* Check DSP has asserted AFE power line */
- rc = i2c_smbus_read_byte_data(ioexp_client, P1_IN);
- if (rc < 0)
- goto fail_on;
- if (rc & (1 << P1_AFE_PWD_LBN))
- return 0;
- }
- }
-
- EFX_INFO(efx, "timed out waiting for DSP boot\n");
- rc = -ETIMEDOUT;
-fail_on:
- sfe4001_poweroff(efx);
- return rc;
-}
-
-static int sfn4111t_reset(struct efx_nic *efx)
-{
- efx_oword_t reg;
-
- /* GPIO 3 and the GPIO register are shared with I2C, so block that */
- i2c_lock_adapter(&efx->i2c_adap);
-
- /* Pull RST_N (GPIO 2) low then let it up again, setting the
- * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the
- * output enables; the output levels should always be 0 (low)
- * and we rely on external pull-ups. */
- falcon_read(efx, &reg, GPIO_CTL_REG_KER);
- EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, true);
- falcon_write(efx, &reg, GPIO_CTL_REG_KER);
- msleep(1000);
- EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, false);
- EFX_SET_OWORD_FIELD(reg, GPIO3_OEN,
- !!(efx->phy_mode & PHY_MODE_SPECIAL));
- falcon_write(efx, &reg, GPIO_CTL_REG_KER);
- msleep(1);
-
- i2c_unlock_adapter(&efx->i2c_adap);
-
- ssleep(1);
- return 0;
-}
-
-static ssize_t show_phy_flash_cfg(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
- return sprintf(buf, "%d\n", !!(efx->phy_mode & PHY_MODE_SPECIAL));
-}
-
-static ssize_t set_phy_flash_cfg(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
- enum efx_phy_mode old_mode, new_mode;
- int err;
-
- rtnl_lock();
- old_mode = efx->phy_mode;
- if (count == 0 || *buf == '0')
- new_mode = old_mode & ~PHY_MODE_SPECIAL;
- else
- new_mode = PHY_MODE_SPECIAL;
- if (old_mode == new_mode) {
- err = 0;
- } else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) {
- err = -EBUSY;
- } else {
- /* Reset the PHY, reconfigure the MAC and enable/disable
- * MAC stats accordingly. */
- efx->phy_mode = new_mode;
- if (new_mode & PHY_MODE_SPECIAL)
- efx_stats_disable(efx);
- if (efx->board_info.type == EFX_BOARD_SFE4001)
- err = sfe4001_poweron(efx);
- else
- err = sfn4111t_reset(efx);
- efx_reconfigure_port(efx);
- if (!(new_mode & PHY_MODE_SPECIAL))
- efx_stats_enable(efx);
- }
- rtnl_unlock();
-
- return err ? err : count;
-}
-
-static DEVICE_ATTR(phy_flash_cfg, 0644, show_phy_flash_cfg, set_phy_flash_cfg);
-
-static void sfe4001_fini(struct efx_nic *efx)
-{
- EFX_INFO(efx, "%s\n", __func__);
-
- device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
- sfe4001_poweroff(efx);
- i2c_unregister_device(efx->board_info.ioexp_client);
- i2c_unregister_device(efx->board_info.hwmon_client);
-}
-
-static int sfe4001_check_hw(struct efx_nic *efx)
-{
- s32 status;
-
- /* If XAUI link is up then do not monitor */
- if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
- return 0;
-
- /* Check the powered status of the PHY. Lack of power implies that
- * the MAX6647 has shut down power to it, probably due to a temp.
- * alarm. Reading the power status rather than the MAX6647 status
- * directly because the later is read-to-clear and would thus
- * start to power up the PHY again when polled, causing us to blip
- * the power undesirably.
- * We know we can read from the IO expander because we did
- * it during power-on. Assume failure now is bad news. */
- status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN);
- if (status >= 0 &&
- (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
- return 0;
-
- /* Use board power control, not PHY power control */
- sfe4001_poweroff(efx);
- efx->phy_mode = PHY_MODE_OFF;
-
- return (status < 0) ? -EIO : -ERANGE;
-}
-
-static struct i2c_board_info sfe4001_hwmon_info = {
- I2C_BOARD_INFO("max6647", 0x4e),
-};
-
-/* This board uses an I2C expander to provider power to the PHY, which needs to
- * be turned on before the PHY can be used.
- * Context: Process context, rtnl lock held
- */
-int sfe4001_init(struct efx_nic *efx)
-{
- int rc;
-
-#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE)
- efx->board_info.hwmon_client =
- i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
-#else
- efx->board_info.hwmon_client =
- i2c_new_dummy(&efx->i2c_adap, sfe4001_hwmon_info.addr);
-#endif
- if (!efx->board_info.hwmon_client)
- return -EIO;
-
- /* Raise board/PHY high limit from 85 to 90 degrees Celsius */
- rc = i2c_smbus_write_byte_data(efx->board_info.hwmon_client,
- MAX664X_REG_WLHO, 90);
- if (rc)
- goto fail_hwmon;
-
- efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539);
- if (!efx->board_info.ioexp_client) {
- rc = -EIO;
- goto fail_hwmon;
- }
-
- /* 10Xpress has fixed-function LED pins, so there is no board-specific
- * blink code. */
- efx->board_info.blink = tenxpress_phy_blink;
-
- efx->board_info.monitor = sfe4001_check_hw;
- efx->board_info.fini = sfe4001_fini;
-
- if (efx->phy_mode & PHY_MODE_SPECIAL) {
- /* PHY won't generate a 156.25 MHz clock and MAC stats fetch
- * will fail. */
- efx_stats_disable(efx);
- }
- rc = sfe4001_poweron(efx);
- if (rc)
- goto fail_ioexp;
-
- rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
- if (rc)
- goto fail_on;
-
- EFX_INFO(efx, "PHY is powered on\n");
- return 0;
-
-fail_on:
- sfe4001_poweroff(efx);
-fail_ioexp:
- i2c_unregister_device(efx->board_info.ioexp_client);
-fail_hwmon:
- i2c_unregister_device(efx->board_info.hwmon_client);
- return rc;
-}
-
-static int sfn4111t_check_hw(struct efx_nic *efx)
-{
- s32 status;
-
- /* If XAUI link is up then do not monitor */
- if (EFX_WORKAROUND_7884(efx) && efx->mac_up)
- return 0;
-
- /* Test LHIGH, RHIGH, FAULT, EOT and IOT alarms */
- status = i2c_smbus_read_byte_data(efx->board_info.hwmon_client,
- MAX664X_REG_RSL);
- if (status < 0)
- return -EIO;
- if (status & 0x57)
- return -ERANGE;
- return 0;
-}
-
-static void sfn4111t_fini(struct efx_nic *efx)
-{
- EFX_INFO(efx, "%s\n", __func__);
-
- device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
- i2c_unregister_device(efx->board_info.hwmon_client);
-}
-
-static struct i2c_board_info sfn4111t_a0_hwmon_info = {
- I2C_BOARD_INFO("max6647", 0x4e),
-};
-
-static struct i2c_board_info sfn4111t_r5_hwmon_info = {
- I2C_BOARD_INFO("max6646", 0x4d),
-};
-
-int sfn4111t_init(struct efx_nic *efx)
-{
- int i = 0;
- int rc;
-
- efx->board_info.hwmon_client =
- i2c_new_device(&efx->i2c_adap,
- (efx->board_info.minor < 5) ?
- &sfn4111t_a0_hwmon_info :
- &sfn4111t_r5_hwmon_info);
- if (!efx->board_info.hwmon_client)
- return -EIO;
-
- efx->board_info.blink = tenxpress_phy_blink;
- efx->board_info.monitor = sfn4111t_check_hw;
- efx->board_info.fini = sfn4111t_fini;
-
- rc = device_create_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
- if (rc)
- goto fail_hwmon;
-
- do {
- if (efx->phy_mode & PHY_MODE_SPECIAL) {
- /* PHY may not generate a 156.25 MHz clock and MAC
- * stats fetch will fail. */
- efx_stats_disable(efx);
- sfn4111t_reset(efx);
- }
- rc = sft9001_wait_boot(efx);
- if (rc == 0)
- return 0;
- efx->phy_mode = PHY_MODE_SPECIAL;
- } while (rc == -EINVAL && ++i < 2);
-
- device_remove_file(&efx->pci_dev->dev, &dev_attr_phy_flash_cfg);
-fail_hwmon:
- i2c_unregister_device(efx->board_info.hwmon_client);
- return rc;
-}
diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c
new file mode 100644
index 00000000000..de07a4f031b
--- /dev/null
+++ b/drivers/net/sfc/siena.c
@@ -0,0 +1,604 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2009 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include "net_driver.h"
+#include "bitfield.h"
+#include "efx.h"
+#include "nic.h"
+#include "mac.h"
+#include "spi.h"
+#include "regs.h"
+#include "io.h"
+#include "phy.h"
+#include "workarounds.h"
+#include "mcdi.h"
+#include "mcdi_pcol.h"
+
+/* Hardware control for SFC9000 family including SFL9021 (aka Siena). */
+
+static void siena_init_wol(struct efx_nic *efx);
+
+
+static void siena_push_irq_moderation(struct efx_channel *channel)
+{
+ efx_dword_t timer_cmd;
+
+ if (channel->irq_moderation)
+ EFX_POPULATE_DWORD_2(timer_cmd,
+ FRF_CZ_TC_TIMER_MODE,
+ FFE_CZ_TIMER_MODE_INT_HLDOFF,
+ FRF_CZ_TC_TIMER_VAL,
+ channel->irq_moderation - 1);
+ else
+ EFX_POPULATE_DWORD_2(timer_cmd,
+ FRF_CZ_TC_TIMER_MODE,
+ FFE_CZ_TIMER_MODE_DIS,
+ FRF_CZ_TC_TIMER_VAL, 0);
+ efx_writed_page_locked(channel->efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0,
+ channel->channel);
+}
+
+static void siena_push_multicast_hash(struct efx_nic *efx)
+{
+ WARN_ON(!mutex_is_locked(&efx->mac_lock));
+
+ efx_mcdi_rpc(efx, MC_CMD_SET_MCAST_HASH,
+ efx->multicast_hash.byte, sizeof(efx->multicast_hash),
+ NULL, 0, NULL);
+}
+
+static int siena_mdio_write(struct net_device *net_dev,
+ int prtad, int devad, u16 addr, u16 value)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ uint32_t status;
+ int rc;
+
+ rc = efx_mcdi_mdio_write(efx, efx->mdio_bus, prtad, devad,
+ addr, value, &status);
+ if (rc)
+ return rc;
+ if (status != MC_CMD_MDIO_STATUS_GOOD)
+ return -EIO;
+
+ return 0;
+}
+
+static int siena_mdio_read(struct net_device *net_dev,
+ int prtad, int devad, u16 addr)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ uint16_t value;
+ uint32_t status;
+ int rc;
+
+ rc = efx_mcdi_mdio_read(efx, efx->mdio_bus, prtad, devad,
+ addr, &value, &status);
+ if (rc)
+ return rc;
+ if (status != MC_CMD_MDIO_STATUS_GOOD)
+ return -EIO;
+
+ return (int)value;
+}
+
+/* This call is responsible for hooking in the MAC and PHY operations */
+static int siena_probe_port(struct efx_nic *efx)
+{
+ int rc;
+
+ /* Hook in PHY operations table */
+ efx->phy_op = &efx_mcdi_phy_ops;
+
+ /* Set up MDIO structure for PHY */
+ efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
+ efx->mdio.mdio_read = siena_mdio_read;
+ efx->mdio.mdio_write = siena_mdio_write;
+
+ /* Fill out MDIO structure and loopback modes */
+ rc = efx->phy_op->probe(efx);
+ if (rc != 0)
+ return rc;
+
+ /* Initial assumption */
+ efx->link_state.speed = 10000;
+ efx->link_state.fd = true;
+ efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
+
+ /* Allocate buffer for stats */
+ rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer,
+ MC_CMD_MAC_NSTATS * sizeof(u64));
+ if (rc)
+ return rc;
+ EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n",
+ (u64)efx->stats_buffer.dma_addr,
+ efx->stats_buffer.addr,
+ (u64)virt_to_phys(efx->stats_buffer.addr));
+
+ efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 1);
+
+ return 0;
+}
+
+void siena_remove_port(struct efx_nic *efx)
+{
+ efx_nic_free_buffer(efx, &efx->stats_buffer);
+}
+
+static const struct efx_nic_register_test siena_register_tests[] = {
+ { FR_AZ_ADR_REGION,
+ EFX_OWORD32(0x0001FFFF, 0x0001FFFF, 0x0001FFFF, 0x0001FFFF) },
+ { FR_CZ_USR_EV_CFG,
+ EFX_OWORD32(0x000103FF, 0x00000000, 0x00000000, 0x00000000) },
+ { FR_AZ_RX_CFG,
+ EFX_OWORD32(0xFFFFFFFE, 0xFFFFFFFF, 0x0003FFFF, 0x00000000) },
+ { FR_AZ_TX_CFG,
+ EFX_OWORD32(0x7FFF0037, 0xFFFF8000, 0xFFFFFFFF, 0x03FFFFFF) },
+ { FR_AZ_TX_RESERVED,
+ EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) },
+ { FR_AZ_SRM_TX_DC_CFG,
+ EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) },
+ { FR_AZ_RX_DC_CFG,
+ EFX_OWORD32(0x00000003, 0x00000000, 0x00000000, 0x00000000) },
+ { FR_AZ_RX_DC_PF_WM,
+ EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) },
+ { FR_BZ_DP_CTRL,
+ EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) },
+ { FR_BZ_RX_RSS_TKEY,
+ EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) },
+ { FR_CZ_RX_RSS_IPV6_REG1,
+ EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) },
+ { FR_CZ_RX_RSS_IPV6_REG2,
+ EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) },
+ { FR_CZ_RX_RSS_IPV6_REG3,
+ EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000) },
+};
+
+static int siena_test_registers(struct efx_nic *efx)
+{
+ return efx_nic_test_registers(efx, siena_register_tests,
+ ARRAY_SIZE(siena_register_tests));
+}
+
+/**************************************************************************
+ *
+ * Device reset
+ *
+ **************************************************************************
+ */
+
+static int siena_reset_hw(struct efx_nic *efx, enum reset_type method)
+{
+
+ if (method == RESET_TYPE_WORLD)
+ return efx_mcdi_reset_mc(efx);
+ else
+ return efx_mcdi_reset_port(efx);
+}
+
+static int siena_probe_nvconfig(struct efx_nic *efx)
+{
+ int rc;
+
+ rc = efx_mcdi_get_board_cfg(efx, efx->mac_address, NULL);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int siena_probe_nic(struct efx_nic *efx)
+{
+ struct siena_nic_data *nic_data;
+ bool already_attached = 0;
+ int rc;
+
+ /* Allocate storage for hardware specific data */
+ nic_data = kzalloc(sizeof(struct siena_nic_data), GFP_KERNEL);
+ if (!nic_data)
+ return -ENOMEM;
+ efx->nic_data = nic_data;
+
+ if (efx_nic_fpga_ver(efx) != 0) {
+ EFX_ERR(efx, "Siena FPGA not supported\n");
+ rc = -ENODEV;
+ goto fail1;
+ }
+
+ efx_mcdi_init(efx);
+
+ /* Recover from a failed assertion before probing */
+ rc = efx_mcdi_handle_assertion(efx);
+ if (rc)
+ goto fail1;
+
+ rc = efx_mcdi_fwver(efx, &nic_data->fw_version, &nic_data->fw_build);
+ if (rc) {
+ EFX_ERR(efx, "Failed to read MCPU firmware version - "
+ "rc %d\n", rc);
+ goto fail1; /* MCPU absent? */
+ }
+
+ /* Let the BMC know that the driver is now in charge of link and
+ * filter settings. We must do this before we reset the NIC */
+ rc = efx_mcdi_drv_attach(efx, true, &already_attached);
+ if (rc) {
+ EFX_ERR(efx, "Unable to register driver with MCPU\n");
+ goto fail2;
+ }
+ if (already_attached)
+ /* Not a fatal error */
+ EFX_ERR(efx, "Host already registered with MCPU\n");
+
+ /* Now we can reset the NIC */
+ rc = siena_reset_hw(efx, RESET_TYPE_ALL);
+ if (rc) {
+ EFX_ERR(efx, "failed to reset NIC\n");
+ goto fail3;
+ }
+
+ siena_init_wol(efx);
+
+ /* Allocate memory for INT_KER */
+ rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t));
+ if (rc)
+ goto fail4;
+ BUG_ON(efx->irq_status.dma_addr & 0x0f);
+
+ EFX_LOG(efx, "INT_KER at %llx (virt %p phys %llx)\n",
+ (unsigned long long)efx->irq_status.dma_addr,
+ efx->irq_status.addr,
+ (unsigned long long)virt_to_phys(efx->irq_status.addr));
+
+ /* Read in the non-volatile configuration */
+ rc = siena_probe_nvconfig(efx);
+ if (rc == -EINVAL) {
+ EFX_ERR(efx, "NVRAM is invalid therefore using defaults\n");
+ efx->phy_type = PHY_TYPE_NONE;
+ efx->mdio.prtad = MDIO_PRTAD_NONE;
+ } else if (rc) {
+ goto fail5;
+ }
+
+ return 0;
+
+fail5:
+ efx_nic_free_buffer(efx, &efx->irq_status);
+fail4:
+fail3:
+ efx_mcdi_drv_attach(efx, false, NULL);
+fail2:
+fail1:
+ kfree(efx->nic_data);
+ return rc;
+}
+
+/* This call performs hardware-specific global initialisation, such as
+ * defining the descriptor cache sizes and number of RSS channels.
+ * It does not set up any buffers, descriptor rings or event queues.
+ */
+static int siena_init_nic(struct efx_nic *efx)
+{
+ efx_oword_t temp;
+ int rc;
+
+ /* Recover from a failed assertion post-reset */
+ rc = efx_mcdi_handle_assertion(efx);
+ if (rc)
+ return rc;
+
+ /* Squash TX of packets of 16 bytes or less */
+ efx_reado(efx, &temp, FR_AZ_TX_RESERVED);
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1);
+ efx_writeo(efx, &temp, FR_AZ_TX_RESERVED);
+
+ /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16
+ * descriptors (which is bad).
+ */
+ efx_reado(efx, &temp, FR_AZ_TX_CFG);
+ EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0);
+ EFX_SET_OWORD_FIELD(temp, FRF_CZ_TX_FILTER_EN_BIT, 1);
+ efx_writeo(efx, &temp, FR_AZ_TX_CFG);
+
+ efx_reado(efx, &temp, FR_AZ_RX_CFG);
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_DESC_PUSH_EN, 0);
+ EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_INGR_EN, 1);
+ efx_writeo(efx, &temp, FR_AZ_RX_CFG);
+
+ if (efx_nic_rx_xoff_thresh >= 0 || efx_nic_rx_xon_thresh >= 0)
+ /* No MCDI operation has been defined to set thresholds */
+ EFX_ERR(efx, "ignoring RX flow control thresholds\n");
+
+ /* Enable event logging */
+ rc = efx_mcdi_log_ctrl(efx, true, false, 0);
+ if (rc)
+ return rc;
+
+ /* Set destination of both TX and RX Flush events */
+ EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0);
+ efx_writeo(efx, &temp, FR_BZ_DP_CTRL);
+
+ EFX_POPULATE_OWORD_1(temp, FRF_CZ_USREV_DIS, 1);
+ efx_writeo(efx, &temp, FR_CZ_USR_EV_CFG);
+
+ efx_nic_init_common(efx);
+ return 0;
+}
+
+static void siena_remove_nic(struct efx_nic *efx)
+{
+ efx_nic_free_buffer(efx, &efx->irq_status);
+
+ siena_reset_hw(efx, RESET_TYPE_ALL);
+
+ /* Relinquish the device back to the BMC */
+ if (efx_nic_has_mc(efx))
+ efx_mcdi_drv_attach(efx, false, NULL);
+
+ /* Tear down the private nic state */
+ kfree(efx->nic_data);
+ efx->nic_data = NULL;
+}
+
+#define STATS_GENERATION_INVALID ((u64)(-1))
+
+static int siena_try_update_nic_stats(struct efx_nic *efx)
+{
+ u64 *dma_stats;
+ struct efx_mac_stats *mac_stats;
+ u64 generation_start;
+ u64 generation_end;
+
+ mac_stats = &efx->mac_stats;
+ dma_stats = (u64 *)efx->stats_buffer.addr;
+
+ generation_end = dma_stats[MC_CMD_MAC_GENERATION_END];
+ if (generation_end == STATS_GENERATION_INVALID)
+ return 0;
+ rmb();
+
+#define MAC_STAT(M, D) \
+ mac_stats->M = dma_stats[MC_CMD_MAC_ ## D]
+
+ MAC_STAT(tx_bytes, TX_BYTES);
+ MAC_STAT(tx_bad_bytes, TX_BAD_BYTES);
+ mac_stats->tx_good_bytes = (mac_stats->tx_bytes -
+ mac_stats->tx_bad_bytes);
+ MAC_STAT(tx_packets, TX_PKTS);
+ MAC_STAT(tx_bad, TX_BAD_FCS_PKTS);
+ MAC_STAT(tx_pause, TX_PAUSE_PKTS);
+ MAC_STAT(tx_control, TX_CONTROL_PKTS);
+ MAC_STAT(tx_unicast, TX_UNICAST_PKTS);
+ MAC_STAT(tx_multicast, TX_MULTICAST_PKTS);
+ MAC_STAT(tx_broadcast, TX_BROADCAST_PKTS);
+ MAC_STAT(tx_lt64, TX_LT64_PKTS);
+ MAC_STAT(tx_64, TX_64_PKTS);
+ MAC_STAT(tx_65_to_127, TX_65_TO_127_PKTS);
+ MAC_STAT(tx_128_to_255, TX_128_TO_255_PKTS);
+ MAC_STAT(tx_256_to_511, TX_256_TO_511_PKTS);
+ MAC_STAT(tx_512_to_1023, TX_512_TO_1023_PKTS);
+ MAC_STAT(tx_1024_to_15xx, TX_1024_TO_15XX_PKTS);
+ MAC_STAT(tx_15xx_to_jumbo, TX_15XX_TO_JUMBO_PKTS);
+ MAC_STAT(tx_gtjumbo, TX_GTJUMBO_PKTS);
+ mac_stats->tx_collision = 0;
+ MAC_STAT(tx_single_collision, TX_SINGLE_COLLISION_PKTS);
+ MAC_STAT(tx_multiple_collision, TX_MULTIPLE_COLLISION_PKTS);
+ MAC_STAT(tx_excessive_collision, TX_EXCESSIVE_COLLISION_PKTS);
+ MAC_STAT(tx_deferred, TX_DEFERRED_PKTS);
+ MAC_STAT(tx_late_collision, TX_LATE_COLLISION_PKTS);
+ mac_stats->tx_collision = (mac_stats->tx_single_collision +
+ mac_stats->tx_multiple_collision +
+ mac_stats->tx_excessive_collision +
+ mac_stats->tx_late_collision);
+ MAC_STAT(tx_excessive_deferred, TX_EXCESSIVE_DEFERRED_PKTS);
+ MAC_STAT(tx_non_tcpudp, TX_NON_TCPUDP_PKTS);
+ MAC_STAT(tx_mac_src_error, TX_MAC_SRC_ERR_PKTS);
+ MAC_STAT(tx_ip_src_error, TX_IP_SRC_ERR_PKTS);
+ MAC_STAT(rx_bytes, RX_BYTES);
+ MAC_STAT(rx_bad_bytes, RX_BAD_BYTES);
+ mac_stats->rx_good_bytes = (mac_stats->rx_bytes -
+ mac_stats->rx_bad_bytes);
+ MAC_STAT(rx_packets, RX_PKTS);
+ MAC_STAT(rx_good, RX_GOOD_PKTS);
+ mac_stats->rx_bad = mac_stats->rx_packets - mac_stats->rx_good;
+ MAC_STAT(rx_pause, RX_PAUSE_PKTS);
+ MAC_STAT(rx_control, RX_CONTROL_PKTS);
+ MAC_STAT(rx_unicast, RX_UNICAST_PKTS);
+ MAC_STAT(rx_multicast, RX_MULTICAST_PKTS);
+ MAC_STAT(rx_broadcast, RX_BROADCAST_PKTS);
+ MAC_STAT(rx_lt64, RX_UNDERSIZE_PKTS);
+ MAC_STAT(rx_64, RX_64_PKTS);
+ MAC_STAT(rx_65_to_127, RX_65_TO_127_PKTS);
+ MAC_STAT(rx_128_to_255, RX_128_TO_255_PKTS);
+ MAC_STAT(rx_256_to_511, RX_256_TO_511_PKTS);
+ MAC_STAT(rx_512_to_1023, RX_512_TO_1023_PKTS);
+ MAC_STAT(rx_1024_to_15xx, RX_1024_TO_15XX_PKTS);
+ MAC_STAT(rx_15xx_to_jumbo, RX_15XX_TO_JUMBO_PKTS);
+ MAC_STAT(rx_gtjumbo, RX_GTJUMBO_PKTS);
+ mac_stats->rx_bad_lt64 = 0;
+ mac_stats->rx_bad_64_to_15xx = 0;
+ mac_stats->rx_bad_15xx_to_jumbo = 0;
+ MAC_STAT(rx_bad_gtjumbo, RX_JABBER_PKTS);
+ MAC_STAT(rx_overflow, RX_OVERFLOW_PKTS);
+ mac_stats->rx_missed = 0;
+ MAC_STAT(rx_false_carrier, RX_FALSE_CARRIER_PKTS);
+ MAC_STAT(rx_symbol_error, RX_SYMBOL_ERROR_PKTS);
+ MAC_STAT(rx_align_error, RX_ALIGN_ERROR_PKTS);
+ MAC_STAT(rx_length_error, RX_LENGTH_ERROR_PKTS);
+ MAC_STAT(rx_internal_error, RX_INTERNAL_ERROR_PKTS);
+ mac_stats->rx_good_lt64 = 0;
+
+ efx->n_rx_nodesc_drop_cnt = dma_stats[MC_CMD_MAC_RX_NODESC_DROPS];
+
+#undef MAC_STAT
+
+ rmb();
+ generation_start = dma_stats[MC_CMD_MAC_GENERATION_START];
+ if (generation_end != generation_start)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static void siena_update_nic_stats(struct efx_nic *efx)
+{
+ while (siena_try_update_nic_stats(efx) == -EAGAIN)
+ cpu_relax();
+}
+
+static void siena_start_nic_stats(struct efx_nic *efx)
+{
+ u64 *dma_stats = (u64 *)efx->stats_buffer.addr;
+
+ dma_stats[MC_CMD_MAC_GENERATION_END] = STATS_GENERATION_INVALID;
+
+ efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr,
+ MC_CMD_MAC_NSTATS * sizeof(u64), 1, 0);
+}
+
+static void siena_stop_nic_stats(struct efx_nic *efx)
+{
+ efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 0);
+}
+
+void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len)
+{
+ struct siena_nic_data *nic_data = efx->nic_data;
+ snprintf(buf, len, "%u.%u.%u.%u",
+ (unsigned int)(nic_data->fw_version >> 48),
+ (unsigned int)(nic_data->fw_version >> 32 & 0xffff),
+ (unsigned int)(nic_data->fw_version >> 16 & 0xffff),
+ (unsigned int)(nic_data->fw_version & 0xffff));
+}
+
+/**************************************************************************
+ *
+ * Wake on LAN
+ *
+ **************************************************************************
+ */
+
+static void siena_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol)
+{
+ struct siena_nic_data *nic_data = efx->nic_data;
+
+ wol->supported = WAKE_MAGIC;
+ if (nic_data->wol_filter_id != -1)
+ wol->wolopts = WAKE_MAGIC;
+ else
+ wol->wolopts = 0;
+ memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+
+static int siena_set_wol(struct efx_nic *efx, u32 type)
+{
+ struct siena_nic_data *nic_data = efx->nic_data;
+ int rc;
+
+ if (type & ~WAKE_MAGIC)
+ return -EINVAL;
+
+ if (type & WAKE_MAGIC) {
+ if (nic_data->wol_filter_id != -1)
+ efx_mcdi_wol_filter_remove(efx,
+ nic_data->wol_filter_id);
+ rc = efx_mcdi_wol_filter_set_magic(efx, efx->mac_address,
+ &nic_data->wol_filter_id);
+ if (rc)
+ goto fail;
+
+ pci_wake_from_d3(efx->pci_dev, true);
+ } else {
+ rc = efx_mcdi_wol_filter_reset(efx);
+ nic_data->wol_filter_id = -1;
+ pci_wake_from_d3(efx->pci_dev, false);
+ if (rc)
+ goto fail;
+ }
+
+ return 0;
+ fail:
+ EFX_ERR(efx, "%s failed: type=%d rc=%d\n", __func__, type, rc);
+ return rc;
+}
+
+
+static void siena_init_wol(struct efx_nic *efx)
+{
+ struct siena_nic_data *nic_data = efx->nic_data;
+ int rc;
+
+ rc = efx_mcdi_wol_filter_get_magic(efx, &nic_data->wol_filter_id);
+
+ if (rc != 0) {
+ /* If it failed, attempt to get into a synchronised
+ * state with MC by resetting any set WoL filters */
+ efx_mcdi_wol_filter_reset(efx);
+ nic_data->wol_filter_id = -1;
+ } else if (nic_data->wol_filter_id != -1) {
+ pci_wake_from_d3(efx->pci_dev, true);
+ }
+}
+
+
+/**************************************************************************
+ *
+ * Revision-dependent attributes used by efx.c and nic.c
+ *
+ **************************************************************************
+ */
+
+struct efx_nic_type siena_a0_nic_type = {
+ .probe = siena_probe_nic,
+ .remove = siena_remove_nic,
+ .init = siena_init_nic,
+ .fini = efx_port_dummy_op_void,
+ .monitor = NULL,
+ .reset = siena_reset_hw,
+ .probe_port = siena_probe_port,
+ .remove_port = siena_remove_port,
+ .prepare_flush = efx_port_dummy_op_void,
+ .update_stats = siena_update_nic_stats,
+ .start_stats = siena_start_nic_stats,
+ .stop_stats = siena_stop_nic_stats,
+ .set_id_led = efx_mcdi_set_id_led,
+ .push_irq_moderation = siena_push_irq_moderation,
+ .push_multicast_hash = siena_push_multicast_hash,
+ .reconfigure_port = efx_mcdi_phy_reconfigure,
+ .get_wol = siena_get_wol,
+ .set_wol = siena_set_wol,
+ .resume_wol = siena_init_wol,
+ .test_registers = siena_test_registers,
+ .default_mac_ops = &efx_mcdi_mac_operations,
+
+ .revision = EFX_REV_SIENA_A0,
+ .mem_map_size = (FR_CZ_MC_TREG_SMEM +
+ FR_CZ_MC_TREG_SMEM_STEP * FR_CZ_MC_TREG_SMEM_ROWS),
+ .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
+ .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL,
+ .buf_tbl_base = FR_BZ_BUF_FULL_TBL,
+ .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL,
+ .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR,
+ .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH),
+ .rx_buffer_padding = 0,
+ .max_interrupt_mode = EFX_INT_MODE_MSIX,
+ .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy
+ * interrupt handler only supports 32
+ * channels */
+ .tx_dc_base = 0x88000,
+ .rx_dc_base = 0x68000,
+ .offload_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM,
+ .reset_world_flags = ETH_RESET_MGMT << ETH_RESET_SHARED_SHIFT,
+};
diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h
index 1b1ceb41167..8bf4fce0813 100644
--- a/drivers/net/sfc/spi.h
+++ b/drivers/net/sfc/spi.h
@@ -36,8 +36,6 @@
/**
* struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device
- * @efx: The Efx controller that owns this device
- * @mtd: MTD state
* @device_id: Controller's id for the device
* @size: Size (in bytes)
* @addr_len: Number of address bytes in read/write commands
@@ -54,10 +52,6 @@
* Write commands are limited to blocks with this size and alignment.
*/
struct efx_spi_device {
- struct efx_nic *efx;
-#ifdef CONFIG_SFC_MTD
- void *mtd;
-#endif
int device_id;
unsigned int size;
unsigned int addr_len;
@@ -67,12 +61,16 @@ struct efx_spi_device {
unsigned int block_size;
};
-int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command,
+int falcon_spi_cmd(struct efx_nic *efx,
+ const struct efx_spi_device *spi, unsigned int command,
int address, const void* in, void *out, size_t len);
-int falcon_spi_wait_write(const struct efx_spi_device *spi);
-int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
+int falcon_spi_wait_write(struct efx_nic *efx,
+ const struct efx_spi_device *spi);
+int falcon_spi_read(struct efx_nic *efx,
+ const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, u8 *buffer);
-int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
+int falcon_spi_write(struct efx_nic *efx,
+ const struct efx_spi_device *spi, loff_t start,
size_t len, size_t *retlen, const u8 *buffer);
/*
diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c
index f4d509015f7..ca11572a49a 100644
--- a/drivers/net/sfc/tenxpress.c
+++ b/drivers/net/sfc/tenxpress.c
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2007-2008 Solarflare Communications Inc.
+ * Copyright 2007-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -12,10 +12,9 @@
#include <linux/seq_file.h>
#include "efx.h"
#include "mdio_10g.h"
-#include "falcon.h"
+#include "nic.h"
#include "phy.h"
-#include "falcon_hwdefs.h"
-#include "boards.h"
+#include "regs.h"
#include "workarounds.h"
#include "selftest.h"
@@ -31,13 +30,13 @@
#define SFX7101_LOOPBACKS ((1 << LOOPBACK_PHYXS) | \
(1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
- (1 << LOOPBACK_NETWORK))
+ (1 << LOOPBACK_PHYXS_WS))
#define SFT9001_LOOPBACKS ((1 << LOOPBACK_GPHY) | \
(1 << LOOPBACK_PHYXS) | \
(1 << LOOPBACK_PCS) | \
(1 << LOOPBACK_PMAPMD) | \
- (1 << LOOPBACK_NETWORK))
+ (1 << LOOPBACK_PHYXS_WS))
/* We complain if we fail to see the link partner as 10G capable this many
* times in a row (must be > 1 as sampling the autoneg. registers is racy)
@@ -84,9 +83,9 @@
#define PMA_PMD_LED_FLASH (3)
#define PMA_PMD_LED_MASK 3
/* All LEDs under hardware control */
-#define PMA_PMD_LED_FULL_AUTO (0)
+#define SFT9001_PMA_PMD_LED_DEFAULT 0
/* Green and Amber under hardware control, Red off */
-#define PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN)
+#define SFX7101_PMA_PMD_LED_DEFAULT (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN)
#define PMA_PMD_SPEED_ENABLE_REG 49192
#define PMA_PMD_100TX_ADV_LBN 1
@@ -200,15 +199,16 @@ static ssize_t set_phy_short_reach(struct device *dev,
const char *buf, size_t count)
{
struct efx_nic *efx = pci_get_drvdata(to_pci_dev(dev));
+ int rc;
rtnl_lock();
efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_TXPWR,
MDIO_PMA_10GBT_TXPWR_SHORT,
count != 0 && *buf != '0');
- efx_reconfigure_port(efx);
+ rc = efx_reconfigure_port(efx);
rtnl_unlock();
- return count;
+ return rc < 0 ? rc : (ssize_t)count;
}
static DEVICE_ATTR(phy_short_reach, 0644, show_phy_short_reach,
@@ -292,17 +292,36 @@ static int tenxpress_init(struct efx_nic *efx)
efx_mdio_set_flag(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_CTRL_REG,
1 << PMA_PMA_LED_ACTIVITY_LBN, true);
efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG,
- PMA_PMD_LED_DEFAULT);
+ SFX7101_PMA_PMD_LED_DEFAULT);
}
return 0;
}
+static int sfx7101_phy_probe(struct efx_nic *efx)
+{
+ efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS;
+ efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
+ efx->loopback_modes = SFX7101_LOOPBACKS | FALCON_XMAC_LOOPBACKS;
+ return 0;
+}
+
+static int sft9001_phy_probe(struct efx_nic *efx)
+{
+ efx->mdio.mmds = TENXPRESS_REQUIRED_DEVS;
+ efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
+ efx->loopback_modes = (SFT9001_LOOPBACKS | FALCON_XMAC_LOOPBACKS |
+ FALCON_GMAC_LOOPBACKS);
+ return 0;
+}
+
static int tenxpress_phy_init(struct efx_nic *efx)
{
struct tenxpress_phy_data *phy_data;
int rc = 0;
+ falcon_board(efx)->type->init_phy(efx);
+
phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
if (!phy_data)
return -ENOMEM;
@@ -333,6 +352,15 @@ static int tenxpress_phy_init(struct efx_nic *efx)
if (rc < 0)
goto fail;
+ /* Initialise advertising flags */
+ efx->link_advertising = (ADVERTISED_TP | ADVERTISED_Autoneg |
+ ADVERTISED_10000baseT_Full);
+ if (efx->phy_type != PHY_TYPE_SFX7101)
+ efx->link_advertising |= (ADVERTISED_1000baseT_Full |
+ ADVERTISED_100baseT_Full);
+ efx_link_set_wanted_fc(efx, efx->wanted_fc);
+ efx_mdio_an_reconfigure(efx);
+
if (efx->phy_type == PHY_TYPE_SFT9001B) {
rc = device_create_file(&efx->pci_dev->dev,
&dev_attr_phy_short_reach);
@@ -363,7 +391,7 @@ static int tenxpress_special_reset(struct efx_nic *efx)
/* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so
* a special software reset can glitch the XGMAC sufficiently for stats
* requests to fail. */
- efx_stats_disable(efx);
+ falcon_stop_nic_stats(efx);
/* Initiate reset */
reg = efx_mdio_read(efx, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG);
@@ -385,7 +413,7 @@ static int tenxpress_special_reset(struct efx_nic *efx)
/* Wait for the XGXS state machine to churn */
mdelay(10);
out:
- efx_stats_enable(efx);
+ falcon_start_nic_stats(efx);
return rc;
}
@@ -489,95 +517,76 @@ static void tenxpress_low_power(struct efx_nic *efx)
!!(efx->phy_mode & PHY_MODE_LOW_POWER));
}
-static void tenxpress_phy_reconfigure(struct efx_nic *efx)
+static int tenxpress_phy_reconfigure(struct efx_nic *efx)
{
struct tenxpress_phy_data *phy_data = efx->phy_data;
- struct ethtool_cmd ecmd;
bool phy_mode_change, loop_reset;
if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) {
phy_data->phy_mode = efx->phy_mode;
- return;
+ return 0;
}
- tenxpress_low_power(efx);
-
phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL &&
phy_data->phy_mode != PHY_MODE_NORMAL);
- loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) ||
+ loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, LOOPBACKS_EXTERNAL(efx)) ||
LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY));
if (loop_reset || phy_mode_change) {
- int rc;
-
- efx->phy_op->get_settings(efx, &ecmd);
-
- if (loop_reset || phy_mode_change) {
- tenxpress_special_reset(efx);
-
- /* Reset XAUI if we were in 10G, and are staying
- * in 10G. If we're moving into and out of 10G
- * then xaui will be reset anyway */
- if (EFX_IS10G(efx))
- falcon_reset_xaui(efx);
- }
+ tenxpress_special_reset(efx);
- rc = efx->phy_op->set_settings(efx, &ecmd);
- WARN_ON(rc);
+ /* Reset XAUI if we were in 10G, and are staying
+ * in 10G. If we're moving into and out of 10G
+ * then xaui will be reset anyway */
+ if (EFX_IS10G(efx))
+ falcon_reset_xaui(efx);
}
+ tenxpress_low_power(efx);
efx_mdio_transmit_disable(efx);
efx_mdio_phy_reconfigure(efx);
tenxpress_ext_loopback(efx);
+ efx_mdio_an_reconfigure(efx);
phy_data->loopback_mode = efx->loopback_mode;
phy_data->phy_mode = efx->phy_mode;
- if (efx->phy_type == PHY_TYPE_SFX7101) {
- efx->link_speed = 10000;
- efx->link_fd = true;
- efx->link_up = sfx7101_link_ok(efx);
- } else {
- efx->phy_op->get_settings(efx, &ecmd);
- efx->link_speed = ecmd.speed;
- efx->link_fd = ecmd.duplex == DUPLEX_FULL;
- efx->link_up = sft9001_link_ok(efx, &ecmd);
- }
- efx->link_fc = efx_mdio_get_pause(efx);
+ return 0;
}
-/* Poll PHY for interrupt */
-static void tenxpress_phy_poll(struct efx_nic *efx)
+static void
+tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd);
+
+/* Poll for link state changes */
+static bool tenxpress_phy_poll(struct efx_nic *efx)
{
- struct tenxpress_phy_data *phy_data = efx->phy_data;
- bool change = false;
+ struct efx_link_state old_state = efx->link_state;
if (efx->phy_type == PHY_TYPE_SFX7101) {
- bool link_ok = sfx7101_link_ok(efx);
- if (link_ok != efx->link_up) {
- change = true;
- } else {
- unsigned int link_fc = efx_mdio_get_pause(efx);
- if (link_fc != efx->link_fc)
- change = true;
- }
- sfx7101_check_bad_lp(efx, link_ok);
- } else if (efx->loopback_mode) {
- bool link_ok = sft9001_link_ok(efx, NULL);
- if (link_ok != efx->link_up)
- change = true;
+ efx->link_state.up = sfx7101_link_ok(efx);
+ efx->link_state.speed = 10000;
+ efx->link_state.fd = true;
+ efx->link_state.fc = efx_mdio_get_pause(efx);
+
+ sfx7101_check_bad_lp(efx, efx->link_state.up);
} else {
- int status = efx_mdio_read(efx, MDIO_MMD_PMAPMD,
- MDIO_PMA_LASI_STAT);
- if (status & MDIO_PMA_LASI_LSALARM)
- change = true;
- }
+ struct ethtool_cmd ecmd;
- if (change)
- falcon_sim_phy_event(efx);
+ /* Check the LASI alarm first */
+ if (efx->loopback_mode == LOOPBACK_NONE &&
+ !(efx_mdio_read(efx, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT) &
+ MDIO_PMA_LASI_LSALARM))
+ return false;
- if (phy_data->phy_mode != PHY_MODE_NORMAL)
- return;
+ tenxpress_get_settings(efx, &ecmd);
+
+ efx->link_state.up = sft9001_link_ok(efx, &ecmd);
+ efx->link_state.speed = ecmd.speed;
+ efx->link_state.fd = (ecmd.duplex == DUPLEX_FULL);
+ efx->link_state.fc = efx_mdio_get_pause(efx);
+ }
+
+ return !efx_link_state_equal(&efx->link_state, &old_state);
}
static void tenxpress_phy_fini(struct efx_nic *efx)
@@ -604,18 +613,29 @@ static void tenxpress_phy_fini(struct efx_nic *efx)
}
-/* Set the RX and TX LEDs and Link LED flashing. The other LEDs
- * (which probably aren't wired anyway) are left in AUTO mode */
-void tenxpress_phy_blink(struct efx_nic *efx, bool blink)
+/* Override the RX, TX and link LEDs */
+void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
{
int reg;
- if (blink)
- reg = (PMA_PMD_LED_FLASH << PMA_PMD_LED_TX_LBN) |
- (PMA_PMD_LED_FLASH << PMA_PMD_LED_RX_LBN) |
- (PMA_PMD_LED_FLASH << PMA_PMD_LED_LINK_LBN);
- else
- reg = PMA_PMD_LED_DEFAULT;
+ switch (mode) {
+ case EFX_LED_OFF:
+ reg = (PMA_PMD_LED_OFF << PMA_PMD_LED_TX_LBN) |
+ (PMA_PMD_LED_OFF << PMA_PMD_LED_RX_LBN) |
+ (PMA_PMD_LED_OFF << PMA_PMD_LED_LINK_LBN);
+ break;
+ case EFX_LED_ON:
+ reg = (PMA_PMD_LED_ON << PMA_PMD_LED_TX_LBN) |
+ (PMA_PMD_LED_ON << PMA_PMD_LED_RX_LBN) |
+ (PMA_PMD_LED_ON << PMA_PMD_LED_LINK_LBN);
+ break;
+ default:
+ if (efx->phy_type == PHY_TYPE_SFX7101)
+ reg = SFX7101_PMA_PMD_LED_DEFAULT;
+ else
+ reg = SFT9001_PMA_PMD_LED_DEFAULT;
+ break;
+ }
efx_mdio_write(efx, MDIO_MMD_PMAPMD, PMA_PMD_LED_OVERR_REG, reg);
}
@@ -624,6 +644,13 @@ static const char *const sfx7101_test_names[] = {
"bist"
};
+static const char *sfx7101_test_name(struct efx_nic *efx, unsigned int index)
+{
+ if (index < ARRAY_SIZE(sfx7101_test_names))
+ return sfx7101_test_names[index];
+ return NULL;
+}
+
static int
sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{
@@ -635,6 +662,9 @@ sfx7101_run_tests(struct efx_nic *efx, int *results, unsigned flags)
/* BIST is automatically run after a special software reset */
rc = tenxpress_special_reset(efx);
results[0] = rc ? -1 : 1;
+
+ efx_mdio_an_reconfigure(efx);
+
return rc;
}
@@ -650,14 +680,17 @@ static const char *const sft9001_test_names[] = {
"cable.pairD.length",
};
+static const char *sft9001_test_name(struct efx_nic *efx, unsigned int index)
+{
+ if (index < ARRAY_SIZE(sft9001_test_names))
+ return sft9001_test_names[index];
+ return NULL;
+}
+
static int sft9001_run_tests(struct efx_nic *efx, int *results, unsigned flags)
{
- struct ethtool_cmd ecmd;
int rc = 0, rc2, i, ctrl_reg, res_reg;
- if (flags & ETH_TEST_FL_OFFLINE)
- efx->phy_op->get_settings(efx, &ecmd);
-
/* Initialise cable diagnostic results to unknown failure */
for (i = 1; i < 9; ++i)
results[i] = -1;
@@ -709,9 +742,7 @@ out:
if (!rc)
rc = rc2;
- rc2 = efx->phy_op->set_settings(efx, &ecmd);
- if (!rc)
- rc = rc2;
+ efx_mdio_an_reconfigure(efx);
}
return rc;
@@ -758,7 +789,7 @@ tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
* but doesn't advertise the correct speed. So override it */
if (efx->loopback_mode == LOOPBACK_GPHY)
ecmd->speed = SPEED_1000;
- else if (LOOPBACK_MASK(efx) & efx->phy_op->loopbacks)
+ else if (LOOPBACK_EXTERNAL(efx))
ecmd->speed = SPEED_10000;
}
@@ -788,35 +819,27 @@ static void sft9001_set_npage_adv(struct efx_nic *efx, u32 advertising)
}
struct efx_phy_operations falcon_sfx7101_phy_ops = {
- .macs = EFX_XMAC,
+ .probe = sfx7101_phy_probe,
.init = tenxpress_phy_init,
.reconfigure = tenxpress_phy_reconfigure,
.poll = tenxpress_phy_poll,
.fini = tenxpress_phy_fini,
- .clear_interrupt = efx_port_dummy_op_void,
.get_settings = tenxpress_get_settings,
.set_settings = tenxpress_set_settings,
.set_npage_adv = sfx7101_set_npage_adv,
- .num_tests = ARRAY_SIZE(sfx7101_test_names),
- .test_names = sfx7101_test_names,
+ .test_name = sfx7101_test_name,
.run_tests = sfx7101_run_tests,
- .mmds = TENXPRESS_REQUIRED_DEVS,
- .loopbacks = SFX7101_LOOPBACKS,
};
struct efx_phy_operations falcon_sft9001_phy_ops = {
- .macs = EFX_GMAC | EFX_XMAC,
+ .probe = sft9001_phy_probe,
.init = tenxpress_phy_init,
.reconfigure = tenxpress_phy_reconfigure,
.poll = tenxpress_phy_poll,
.fini = tenxpress_phy_fini,
- .clear_interrupt = efx_port_dummy_op_void,
.get_settings = tenxpress_get_settings,
.set_settings = tenxpress_set_settings,
.set_npage_adv = sft9001_set_npage_adv,
- .num_tests = ARRAY_SIZE(sft9001_test_names),
- .test_names = sft9001_test_names,
+ .test_name = sft9001_test_name,
.run_tests = sft9001_run_tests,
- .mmds = TENXPRESS_REQUIRED_DEVS,
- .loopbacks = SFT9001_LOOPBACKS,
};
diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c
index 489c4de3144..e669f94e821 100644
--- a/drivers/net/sfc/tx.c
+++ b/drivers/net/sfc/tx.c
@@ -1,7 +1,7 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005-2006 Fen Systems Ltd.
- * Copyright 2005-2008 Solarflare Communications Inc.
+ * Copyright 2005-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -12,12 +12,13 @@
#include <linux/tcp.h>
#include <linux/ip.h>
#include <linux/in.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
#include <linux/if_ether.h>
#include <linux/highmem.h>
#include "net_driver.h"
-#include "tx.h"
#include "efx.h"
-#include "falcon.h"
+#include "nic.h"
#include "workarounds.h"
/*
@@ -26,8 +27,7 @@
* The tx_queue descriptor ring fill-level must fall below this value
* before we restart the netif queue
*/
-#define EFX_NETDEV_TX_THRESHOLD(_tx_queue) \
- (_tx_queue->efx->type->txd_ring_mask / 2u)
+#define EFX_TXQ_THRESHOLD (EFX_TXQ_MASK / 2u)
/* We want to be able to nest calls to netif_stop_queue(), since each
* channel can have an individual stop on the queue.
@@ -125,6 +125,24 @@ static void efx_tsoh_free(struct efx_tx_queue *tx_queue,
}
+static inline unsigned
+efx_max_tx_len(struct efx_nic *efx, dma_addr_t dma_addr)
+{
+ /* Depending on the NIC revision, we can use descriptor
+ * lengths up to 8K or 8K-1. However, since PCI Express
+ * devices must split read requests at 4K boundaries, there is
+ * little benefit from using descriptors that cross those
+ * boundaries and we keep things simple by not doing so.
+ */
+ unsigned len = (~dma_addr & 0xfff) + 1;
+
+ /* Work around hardware bug for unaligned buffers. */
+ if (EFX_WORKAROUND_5391(efx) && (dma_addr & 0xf))
+ len = min_t(unsigned, len, 512 - (dma_addr & 0xf));
+
+ return len;
+}
+
/*
* Add a socket buffer to a TX queue
*
@@ -135,11 +153,13 @@ static void efx_tsoh_free(struct efx_tx_queue *tx_queue,
* If any DMA mapping fails, any mapped fragments will be unmapped,
* the queue's insert pointer will be restored to its original value.
*
+ * This function is split out from efx_hard_start_xmit to allow the
+ * loopback test to direct packets via specific TX queues.
+ *
* Returns NETDEV_TX_OK or NETDEV_TX_BUSY
* You must hold netif_tx_lock() to call this function.
*/
-static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
- struct sk_buff *skb)
+netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb)
{
struct efx_nic *efx = tx_queue->efx;
struct pci_dev *pci_dev = efx->pci_dev;
@@ -147,7 +167,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
skb_frag_t *fragment;
struct page *page;
int page_offset;
- unsigned int len, unmap_len = 0, fill_level, insert_ptr, misalign;
+ unsigned int len, unmap_len = 0, fill_level, insert_ptr;
dma_addr_t dma_addr, unmap_addr = 0;
unsigned int dma_len;
bool unmap_single;
@@ -156,7 +176,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count);
- if (skb_shinfo((struct sk_buff *)skb)->gso_size)
+ if (skb_shinfo(skb)->gso_size)
return efx_enqueue_skb_tso(tx_queue, skb);
/* Get size of the initial fragment */
@@ -171,7 +191,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
}
fill_level = tx_queue->insert_count - tx_queue->old_read_count;
- q_space = efx->type->txd_ring_mask - 1 - fill_level;
+ q_space = EFX_TXQ_MASK - 1 - fill_level;
/* Map for DMA. Use pci_map_single rather than pci_map_page
* since this is more efficient on machines with sparse
@@ -208,16 +228,14 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
&tx_queue->read_count;
fill_level = (tx_queue->insert_count
- tx_queue->old_read_count);
- q_space = (efx->type->txd_ring_mask - 1 -
- fill_level);
+ q_space = EFX_TXQ_MASK - 1 - fill_level;
if (unlikely(q_space-- <= 0))
goto stop;
smp_mb();
--tx_queue->stopped;
}
- insert_ptr = (tx_queue->insert_count &
- efx->type->txd_ring_mask);
+ insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK;
buffer = &tx_queue->buffer[insert_ptr];
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->tsoh);
@@ -226,14 +244,10 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
EFX_BUG_ON_PARANOID(!buffer->continuation);
EFX_BUG_ON_PARANOID(buffer->unmap_len);
- dma_len = (((~dma_addr) & efx->type->tx_dma_mask) + 1);
- if (likely(dma_len > len))
+ dma_len = efx_max_tx_len(efx, dma_addr);
+ if (likely(dma_len >= len))
dma_len = len;
- misalign = (unsigned)dma_addr & efx->type->bug5391_mask;
- if (misalign && dma_len + misalign > 512)
- dma_len = 512 - misalign;
-
/* Fill out per descriptor fields */
buffer->len = dma_len;
buffer->dma_addr = dma_addr;
@@ -266,7 +280,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
buffer->continuation = false;
/* Pass off to hardware */
- falcon_push_buffers(tx_queue);
+ efx_nic_push_buffers(tx_queue);
return NETDEV_TX_OK;
@@ -276,7 +290,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
skb_shinfo(skb)->nr_frags + 1);
/* Mark the packet as transmitted, and free the SKB ourselves */
- dev_kfree_skb_any((struct sk_buff *)skb);
+ dev_kfree_skb_any(skb);
goto unwind;
stop:
@@ -289,7 +303,7 @@ static netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue,
/* Work backwards until we hit the original insert pointer value */
while (tx_queue->insert_count != tx_queue->write_count) {
--tx_queue->insert_count;
- insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask;
+ insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK;
buffer = &tx_queue->buffer[insert_ptr];
efx_dequeue_buffer(tx_queue, buffer);
buffer->len = 0;
@@ -318,10 +332,9 @@ static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue,
{
struct efx_nic *efx = tx_queue->efx;
unsigned int stop_index, read_ptr;
- unsigned int mask = tx_queue->efx->type->txd_ring_mask;
- stop_index = (index + 1) & mask;
- read_ptr = tx_queue->read_count & mask;
+ stop_index = (index + 1) & EFX_TXQ_MASK;
+ read_ptr = tx_queue->read_count & EFX_TXQ_MASK;
while (read_ptr != stop_index) {
struct efx_tx_buffer *buffer = &tx_queue->buffer[read_ptr];
@@ -338,28 +351,10 @@ static void efx_dequeue_buffers(struct efx_tx_queue *tx_queue,
buffer->len = 0;
++tx_queue->read_count;
- read_ptr = tx_queue->read_count & mask;
+ read_ptr = tx_queue->read_count & EFX_TXQ_MASK;
}
}
-/* Initiate a packet transmission on the specified TX queue.
- * Note that returning anything other than NETDEV_TX_OK will cause the
- * OS to free the skb.
- *
- * This function is split out from efx_hard_start_xmit to allow the
- * loopback test to direct packets via specific TX queues. It is
- * therefore a non-static inline, so as not to penalise performance
- * for non-loopback transmissions.
- *
- * Context: netif_tx_lock held
- */
-inline netdev_tx_t efx_xmit(struct efx_nic *efx,
- struct efx_tx_queue *tx_queue, struct sk_buff *skb)
-{
- /* Map fragments for DMA and add to TX queue */
- return efx_enqueue_skb(tx_queue, skb);
-}
-
/* Initiate a packet transmission. We use one channel per CPU
* (sharing when we have more CPUs than channels). On Falcon, the TX
* completion events will be directed back to the CPU that transmitted
@@ -383,7 +378,7 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb,
else
tx_queue = &efx->tx_queue[EFX_TX_QUEUE_NO_CSUM];
- return efx_xmit(efx, tx_queue, skb);
+ return efx_enqueue_skb(tx_queue, skb);
}
void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
@@ -391,7 +386,7 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
unsigned fill_level;
struct efx_nic *efx = tx_queue->efx;
- EFX_BUG_ON_PARANOID(index > efx->type->txd_ring_mask);
+ EFX_BUG_ON_PARANOID(index > EFX_TXQ_MASK);
efx_dequeue_buffers(tx_queue, index);
@@ -401,7 +396,7 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
smp_mb();
if (unlikely(tx_queue->stopped) && likely(efx->port_enabled)) {
fill_level = tx_queue->insert_count - tx_queue->read_count;
- if (fill_level < EFX_NETDEV_TX_THRESHOLD(tx_queue)) {
+ if (fill_level < EFX_TXQ_THRESHOLD) {
EFX_BUG_ON_PARANOID(!efx_dev_registered(efx));
/* Do this under netif_tx_lock(), to avoid racing
@@ -425,15 +420,15 @@ int efx_probe_tx_queue(struct efx_tx_queue *tx_queue)
EFX_LOG(efx, "creating TX queue %d\n", tx_queue->queue);
/* Allocate software ring */
- txq_size = (efx->type->txd_ring_mask + 1) * sizeof(*tx_queue->buffer);
+ txq_size = EFX_TXQ_SIZE * sizeof(*tx_queue->buffer);
tx_queue->buffer = kzalloc(txq_size, GFP_KERNEL);
if (!tx_queue->buffer)
return -ENOMEM;
- for (i = 0; i <= efx->type->txd_ring_mask; ++i)
+ for (i = 0; i <= EFX_TXQ_MASK; ++i)
tx_queue->buffer[i].continuation = true;
/* Allocate hardware ring */
- rc = falcon_probe_tx(tx_queue);
+ rc = efx_nic_probe_tx(tx_queue);
if (rc)
goto fail;
@@ -456,7 +451,7 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue)
BUG_ON(tx_queue->stopped);
/* Set up TX descriptor ring */
- falcon_init_tx(tx_queue);
+ efx_nic_init_tx(tx_queue);
}
void efx_release_tx_buffers(struct efx_tx_queue *tx_queue)
@@ -468,8 +463,7 @@ void efx_release_tx_buffers(struct efx_tx_queue *tx_queue)
/* Free any buffers left in the ring */
while (tx_queue->read_count != tx_queue->write_count) {
- buffer = &tx_queue->buffer[tx_queue->read_count &
- tx_queue->efx->type->txd_ring_mask];
+ buffer = &tx_queue->buffer[tx_queue->read_count & EFX_TXQ_MASK];
efx_dequeue_buffer(tx_queue, buffer);
buffer->continuation = true;
buffer->len = 0;
@@ -483,7 +477,7 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue)
EFX_LOG(tx_queue->efx, "shutting down TX queue %d\n", tx_queue->queue);
/* Flush TX queue, remove descriptor ring */
- falcon_fini_tx(tx_queue);
+ efx_nic_fini_tx(tx_queue);
efx_release_tx_buffers(tx_queue);
@@ -500,7 +494,7 @@ void efx_fini_tx_queue(struct efx_tx_queue *tx_queue)
void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
{
EFX_LOG(tx_queue->efx, "destroying TX queue %d\n", tx_queue->queue);
- falcon_remove_tx(tx_queue);
+ efx_nic_remove_tx(tx_queue);
kfree(tx_queue->buffer);
tx_queue->buffer = NULL;
@@ -539,6 +533,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
#define ETH_HDR_LEN(skb) (skb_network_header(skb) - (skb)->data)
#define SKB_TCP_OFF(skb) PTR_DIFF(tcp_hdr(skb), (skb)->data)
#define SKB_IPV4_OFF(skb) PTR_DIFF(ip_hdr(skb), (skb)->data)
+#define SKB_IPV6_OFF(skb) PTR_DIFF(ipv6_hdr(skb), (skb)->data)
/**
* struct tso_state - TSO state for an SKB
@@ -551,6 +546,7 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue)
* @unmap_len: Length of SKB fragment
* @unmap_addr: DMA address of SKB fragment
* @unmap_single: DMA single vs page mapping flag
+ * @protocol: Network protocol (after any VLAN header)
* @header_len: Number of bytes of header
* @full_packet_size: Number of bytes to put in each outgoing segment
*
@@ -571,6 +567,7 @@ struct tso_state {
dma_addr_t unmap_addr;
bool unmap_single;
+ __be16 protocol;
unsigned header_len;
int full_packet_size;
};
@@ -578,9 +575,9 @@ struct tso_state {
/*
* Verify that our various assumptions about sk_buffs and the conditions
- * under which TSO will be attempted hold true.
+ * under which TSO will be attempted hold true. Return the protocol number.
*/
-static void efx_tso_check_safe(struct sk_buff *skb)
+static __be16 efx_tso_check_protocol(struct sk_buff *skb)
{
__be16 protocol = skb->protocol;
@@ -595,13 +592,22 @@ static void efx_tso_check_safe(struct sk_buff *skb)
if (protocol == htons(ETH_P_IP))
skb_set_transport_header(skb, sizeof(*veh) +
4 * ip_hdr(skb)->ihl);
+ else if (protocol == htons(ETH_P_IPV6))
+ skb_set_transport_header(skb, sizeof(*veh) +
+ sizeof(struct ipv6hdr));
}
- EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IP));
- EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP);
+ if (protocol == htons(ETH_P_IP)) {
+ EFX_BUG_ON_PARANOID(ip_hdr(skb)->protocol != IPPROTO_TCP);
+ } else {
+ EFX_BUG_ON_PARANOID(protocol != htons(ETH_P_IPV6));
+ EFX_BUG_ON_PARANOID(ipv6_hdr(skb)->nexthdr != NEXTHDR_TCP);
+ }
EFX_BUG_ON_PARANOID((PTR_DIFF(tcp_hdr(skb), skb->data)
+ (tcp_hdr(skb)->doff << 2u)) >
skb_headlen(skb));
+
+ return protocol;
}
@@ -708,14 +714,14 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue,
{
struct efx_tx_buffer *buffer;
struct efx_nic *efx = tx_queue->efx;
- unsigned dma_len, fill_level, insert_ptr, misalign;
+ unsigned dma_len, fill_level, insert_ptr;
int q_space;
EFX_BUG_ON_PARANOID(len <= 0);
fill_level = tx_queue->insert_count - tx_queue->old_read_count;
/* -1 as there is no way to represent all descriptors used */
- q_space = efx->type->txd_ring_mask - 1 - fill_level;
+ q_space = EFX_TXQ_MASK - 1 - fill_level;
while (1) {
if (unlikely(q_space-- <= 0)) {
@@ -731,7 +737,7 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue,
*(volatile unsigned *)&tx_queue->read_count;
fill_level = (tx_queue->insert_count
- tx_queue->old_read_count);
- q_space = efx->type->txd_ring_mask - 1 - fill_level;
+ q_space = EFX_TXQ_MASK - 1 - fill_level;
if (unlikely(q_space-- <= 0)) {
*final_buffer = NULL;
return 1;
@@ -740,13 +746,13 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue,
--tx_queue->stopped;
}
- insert_ptr = tx_queue->insert_count & efx->type->txd_ring_mask;
+ insert_ptr = tx_queue->insert_count & EFX_TXQ_MASK;
buffer = &tx_queue->buffer[insert_ptr];
++tx_queue->insert_count;
EFX_BUG_ON_PARANOID(tx_queue->insert_count -
tx_queue->read_count >
- efx->type->txd_ring_mask);
+ EFX_TXQ_MASK);
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->len);
@@ -757,12 +763,7 @@ static int efx_tx_queue_insert(struct efx_tx_queue *tx_queue,
buffer->dma_addr = dma_addr;
- /* Ensure we do not cross a boundary unsupported by H/W */
- dma_len = (~dma_addr & efx->type->tx_dma_mask) + 1;
-
- misalign = (unsigned)dma_addr & efx->type->bug5391_mask;
- if (misalign && dma_len + misalign > 512)
- dma_len = 512 - misalign;
+ dma_len = efx_max_tx_len(efx, dma_addr);
/* If there is enough space to send then do so */
if (dma_len >= len)
@@ -792,8 +793,7 @@ static void efx_tso_put_header(struct efx_tx_queue *tx_queue,
{
struct efx_tx_buffer *buffer;
- buffer = &tx_queue->buffer[tx_queue->insert_count &
- tx_queue->efx->type->txd_ring_mask];
+ buffer = &tx_queue->buffer[tx_queue->insert_count & EFX_TXQ_MASK];
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->len);
EFX_BUG_ON_PARANOID(buffer->unmap_len);
@@ -818,7 +818,7 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue)
while (tx_queue->insert_count != tx_queue->write_count) {
--tx_queue->insert_count;
buffer = &tx_queue->buffer[tx_queue->insert_count &
- tx_queue->efx->type->txd_ring_mask];
+ EFX_TXQ_MASK];
efx_tsoh_free(tx_queue, buffer);
EFX_BUG_ON_PARANOID(buffer->skb);
buffer->len = 0;
@@ -850,7 +850,10 @@ static void tso_start(struct tso_state *st, const struct sk_buff *skb)
+ PTR_DIFF(tcp_hdr(skb), skb->data));
st->full_packet_size = st->header_len + skb_shinfo(skb)->gso_size;
- st->ipv4_id = ntohs(ip_hdr(skb)->id);
+ if (st->protocol == htons(ETH_P_IP))
+ st->ipv4_id = ntohs(ip_hdr(skb)->id);
+ else
+ st->ipv4_id = 0;
st->seqnum = ntohl(tcp_hdr(skb)->seq);
EFX_BUG_ON_PARANOID(tcp_hdr(skb)->urg);
@@ -965,7 +968,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue,
struct tso_state *st)
{
struct efx_tso_header *tsoh;
- struct iphdr *tsoh_iph;
struct tcphdr *tsoh_th;
unsigned ip_length;
u8 *header;
@@ -989,7 +991,6 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue,
header = TSOH_BUFFER(tsoh);
tsoh_th = (struct tcphdr *)(header + SKB_TCP_OFF(skb));
- tsoh_iph = (struct iphdr *)(header + SKB_IPV4_OFF(skb));
/* Copy and update the headers. */
memcpy(header, skb->data, st->header_len);
@@ -1007,11 +1008,22 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue,
tsoh_th->fin = tcp_hdr(skb)->fin;
tsoh_th->psh = tcp_hdr(skb)->psh;
}
- tsoh_iph->tot_len = htons(ip_length);
- /* Linux leaves suitable gaps in the IP ID space for us to fill. */
- tsoh_iph->id = htons(st->ipv4_id);
- st->ipv4_id++;
+ if (st->protocol == htons(ETH_P_IP)) {
+ struct iphdr *tsoh_iph =
+ (struct iphdr *)(header + SKB_IPV4_OFF(skb));
+
+ tsoh_iph->tot_len = htons(ip_length);
+
+ /* Linux leaves suitable gaps in the IP ID space for us to fill. */
+ tsoh_iph->id = htons(st->ipv4_id);
+ st->ipv4_id++;
+ } else {
+ struct ipv6hdr *tsoh_iph =
+ (struct ipv6hdr *)(header + SKB_IPV6_OFF(skb));
+
+ tsoh_iph->payload_len = htons(ip_length - sizeof(*tsoh_iph));
+ }
st->packet_space = skb_shinfo(skb)->gso_size;
++tx_queue->tso_packets;
@@ -1041,8 +1053,8 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
int frag_i, rc, rc2 = NETDEV_TX_OK;
struct tso_state state;
- /* Verify TSO is safe - these checks should never fail. */
- efx_tso_check_safe(skb);
+ /* Find the packet protocol and sanity-check it */
+ state.protocol = efx_tso_check_protocol(skb);
EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count);
@@ -1092,14 +1104,14 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
}
/* Pass off to hardware */
- falcon_push_buffers(tx_queue);
+ efx_nic_push_buffers(tx_queue);
tx_queue->tso_bursts++;
return NETDEV_TX_OK;
mem_err:
EFX_ERR(efx, "Out of memory for TSO headers, or PCI mapping error\n");
- dev_kfree_skb_any((struct sk_buff *)skb);
+ dev_kfree_skb_any(skb);
goto unwind;
stop:
@@ -1135,7 +1147,7 @@ static void efx_fini_tso(struct efx_tx_queue *tx_queue)
unsigned i;
if (tx_queue->buffer) {
- for (i = 0; i <= tx_queue->efx->type->txd_ring_mask; ++i)
+ for (i = 0; i <= EFX_TXQ_MASK; ++i)
efx_tsoh_free(tx_queue, &tx_queue->buffer[i]);
}
diff --git a/drivers/net/sfc/tx.h b/drivers/net/sfc/tx.h
deleted file mode 100644
index e3678962a5b..00000000000
--- a/drivers/net/sfc/tx.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/****************************************************************************
- * Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2006 Fen Systems Ltd.
- * Copyright 2006-2008 Solarflare Communications Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation, incorporated herein by reference.
- */
-
-#ifndef EFX_TX_H
-#define EFX_TX_H
-
-#include "net_driver.h"
-
-int efx_probe_tx_queue(struct efx_tx_queue *tx_queue);
-void efx_remove_tx_queue(struct efx_tx_queue *tx_queue);
-void efx_init_tx_queue(struct efx_tx_queue *tx_queue);
-void efx_fini_tx_queue(struct efx_tx_queue *tx_queue);
-
-netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb,
- struct net_device *net_dev);
-void efx_release_tx_buffers(struct efx_tx_queue *tx_queue);
-
-#endif /* EFX_TX_H */
diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h
index c821c15445a..acd9c734e48 100644
--- a/drivers/net/sfc/workarounds.h
+++ b/drivers/net/sfc/workarounds.h
@@ -1,6 +1,6 @@
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
- * Copyright 2006-2008 Solarflare Communications Inc.
+ * Copyright 2006-2009 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
@@ -16,7 +16,9 @@
*/
#define EFX_WORKAROUND_ALWAYS(efx) 1
-#define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1)
+#define EFX_WORKAROUND_FALCON_A(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_A1)
+#define EFX_WORKAROUND_FALCON_AB(efx) (efx_nic_rev(efx) <= EFX_REV_FALCON_B0)
+#define EFX_WORKAROUND_SIENA(efx) (efx_nic_rev(efx) == EFX_REV_SIENA_A0)
#define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx)
#define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \
(efx)->phy_type == PHY_TYPE_SFT9001B)
@@ -27,20 +29,22 @@
#define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
/* Bit-bashed I2C reads cause performance drop */
#define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G
-/* TX pkt parser problem with <= 16 byte TXes */
-#define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS
/* TX_EV_PKT_ERR can be caused by a dangling TX descriptor
* or a PCIe error (bug 11028) */
#define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS
/* Transmit flow control may get disabled */
-#define EFX_WORKAROUND_11482 EFX_WORKAROUND_ALWAYS
-/* Flush events can take a very long time to appear */
-#define EFX_WORKAROUND_11557 EFX_WORKAROUND_ALWAYS
+#define EFX_WORKAROUND_11482 EFX_WORKAROUND_FALCON_AB
/* Truncated IPv4 packets can confuse the TX packet parser */
-#define EFX_WORKAROUND_15592 EFX_WORKAROUND_ALWAYS
+#define EFX_WORKAROUND_15592 EFX_WORKAROUND_FALCON_AB
+/* Legacy ISR read can return zero once */
+#define EFX_WORKAROUND_15783 EFX_WORKAROUND_SIENA
+/* Legacy interrupt storm when interrupt fifo fills */
+#define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA
/* Spurious parity errors in TSORT buffers */
#define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A
+/* Unaligned read request >512 bytes after aligning may break TSORT */
+#define EFX_WORKAROUND_5391 EFX_WORKAROUND_FALCON_A
/* iSCSI parsing errors */
#define EFX_WORKAROUND_5583 EFX_WORKAROUND_FALCON_A
/* RX events go missing */
diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c
index f4dfd1f679a..6b364a6c6c6 100644
--- a/drivers/net/sgiseeq.c
+++ b/drivers/net/sgiseeq.c
@@ -365,11 +365,10 @@ static inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp
}
skb_reserve(newskb, 2);
} else {
- skb = netdev_alloc_skb(dev, len + 2);
- if (skb) {
- skb_reserve(skb, 2);
+ skb = netdev_alloc_skb_ip_align(dev, len);
+ if (skb)
skb_copy_to_linear_data(skb, rd->skb->data, len);
- }
+
newskb = rd->skb;
}
memory_squeeze:
diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c
index 528b912a4b0..c88bc101304 100644
--- a/drivers/net/sh_eth.c
+++ b/drivers/net/sh_eth.c
@@ -30,6 +30,7 @@
#include <linux/phy.h>
#include <linux/cache.h>
#include <linux/io.h>
+#include <linux/pm_runtime.h>
#include <asm/cacheflush.h>
#include "sh_eth.h"
@@ -299,16 +300,20 @@ static void update_mac_address(struct net_device *ndev)
* When you want use this device, you must set MAC address in bootloader.
*
*/
-static void read_mac_address(struct net_device *ndev)
+static void read_mac_address(struct net_device *ndev, unsigned char *mac)
{
u32 ioaddr = ndev->base_addr;
- ndev->dev_addr[0] = (ctrl_inl(ioaddr + MAHR) >> 24);
- ndev->dev_addr[1] = (ctrl_inl(ioaddr + MAHR) >> 16) & 0xFF;
- ndev->dev_addr[2] = (ctrl_inl(ioaddr + MAHR) >> 8) & 0xFF;
- ndev->dev_addr[3] = (ctrl_inl(ioaddr + MAHR) & 0xFF);
- ndev->dev_addr[4] = (ctrl_inl(ioaddr + MALR) >> 8) & 0xFF;
- ndev->dev_addr[5] = (ctrl_inl(ioaddr + MALR) & 0xFF);
+ if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5]) {
+ memcpy(ndev->dev_addr, mac, 6);
+ } else {
+ ndev->dev_addr[0] = (ctrl_inl(ioaddr + MAHR) >> 24);
+ ndev->dev_addr[1] = (ctrl_inl(ioaddr + MAHR) >> 16) & 0xFF;
+ ndev->dev_addr[2] = (ctrl_inl(ioaddr + MAHR) >> 8) & 0xFF;
+ ndev->dev_addr[3] = (ctrl_inl(ioaddr + MAHR) & 0xFF);
+ ndev->dev_addr[4] = (ctrl_inl(ioaddr + MALR) >> 8) & 0xFF;
+ ndev->dev_addr[5] = (ctrl_inl(ioaddr + MALR) & 0xFF);
+ }
}
struct bb_info {
@@ -1009,7 +1014,9 @@ static int sh_eth_open(struct net_device *ndev)
int ret = 0;
struct sh_eth_private *mdp = netdev_priv(ndev);
- ret = request_irq(ndev->irq, &sh_eth_interrupt,
+ pm_runtime_get_sync(&mdp->pdev->dev);
+
+ ret = request_irq(ndev->irq, sh_eth_interrupt,
#if defined(CONFIG_CPU_SUBTYPE_SH7763) || defined(CONFIG_CPU_SUBTYPE_SH7764)
IRQF_SHARED,
#else
@@ -1045,6 +1052,7 @@ static int sh_eth_open(struct net_device *ndev)
out_free_irq:
free_irq(ndev->irq, ndev);
+ pm_runtime_put_sync(&mdp->pdev->dev);
return ret;
}
@@ -1176,6 +1184,8 @@ static int sh_eth_close(struct net_device *ndev)
ringsize = sizeof(struct sh_eth_txdesc) * TX_RING_SIZE;
dma_free_coherent(NULL, ringsize, mdp->tx_ring, mdp->tx_desc_dma);
+ pm_runtime_put_sync(&mdp->pdev->dev);
+
return 0;
}
@@ -1184,6 +1194,8 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
struct sh_eth_private *mdp = netdev_priv(ndev);
u32 ioaddr = ndev->base_addr;
+ pm_runtime_get_sync(&mdp->pdev->dev);
+
mdp->stats.tx_dropped += ctrl_inl(ioaddr + TROCR);
ctrl_outl(0, ioaddr + TROCR); /* (write clear) */
mdp->stats.collisions += ctrl_inl(ioaddr + CDCR);
@@ -1199,6 +1211,8 @@ static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
mdp->stats.tx_carrier_errors += ctrl_inl(ioaddr + CNDCR);
ctrl_outl(0, ioaddr + CNDCR); /* (write clear) */
#endif
+ pm_runtime_put_sync(&mdp->pdev->dev);
+
return &mdp->stats;
}
@@ -1407,6 +1421,9 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
mdp = netdev_priv(ndev);
spin_lock_init(&mdp->lock);
+ mdp->pdev = pdev;
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
pd = (struct sh_eth_plat_data *)(pdev->dev.platform_data);
/* get PHY ID */
@@ -1428,7 +1445,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
mdp->post_fw = POST_FW >> (devno << 1);
/* read and set MAC address */
- read_mac_address(ndev);
+ read_mac_address(ndev, pd->mac_addr);
/* First device only init */
if (!devno) {
@@ -1482,18 +1499,37 @@ static int sh_eth_drv_remove(struct platform_device *pdev)
sh_mdio_release(ndev);
unregister_netdev(ndev);
flush_scheduled_work();
-
+ pm_runtime_disable(&pdev->dev);
free_netdev(ndev);
platform_set_drvdata(pdev, NULL);
return 0;
}
+static int sh_eth_runtime_nop(struct device *dev)
+{
+ /*
+ * Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static struct dev_pm_ops sh_eth_dev_pm_ops = {
+ .runtime_suspend = sh_eth_runtime_nop,
+ .runtime_resume = sh_eth_runtime_nop,
+};
+
static struct platform_driver sh_eth_driver = {
.probe = sh_eth_drv_probe,
.remove = sh_eth_drv_remove,
.driver = {
.name = CARDNAME,
+ .pm = &sh_eth_dev_pm_ops,
},
};
diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h
index ba151f86ae7..8b47763958f 100644
--- a/drivers/net/sh_eth.h
+++ b/drivers/net/sh_eth.h
@@ -703,6 +703,7 @@ struct sh_eth_cpu_data {
};
struct sh_eth_private {
+ struct platform_device *pdev;
struct sh_eth_cpu_data *cd;
dma_addr_t rx_desc_dma;
dma_addr_t tx_desc_dma;
diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c
index 7cc9898f4e0..31233b4c44a 100644
--- a/drivers/net/sis190.c
+++ b/drivers/net/sis190.c
@@ -536,13 +536,12 @@ static bool sis190_try_rx_copy(struct sis190_private *tp,
if (pkt_size >= rx_copybreak)
goto out;
- skb = netdev_alloc_skb(tp->dev, pkt_size + 2);
+ skb = netdev_alloc_skb_ip_align(tp->dev, pkt_size);
if (!skb)
goto out;
pci_dma_sync_single_for_cpu(tp->pci_dev, addr, tp->rx_buf_sz,
PCI_DMA_FROMDEVICE);
- skb_reserve(skb, 2);
skb_copy_to_linear_data(skb, sk_buff[0]->data, pkt_size);
*sk_buff = skb;
done = true;
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index c072f7f36ac..7360d4bbf75 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -1016,7 +1016,7 @@ sis900_open(struct net_device *net_dev)
/* Equalizer workaround Rule */
sis630_set_eq(net_dev, sis_priv->chipset_rev);
- ret = request_irq(net_dev->irq, &sis900_interrupt, IRQF_SHARED,
+ ret = request_irq(net_dev->irq, sis900_interrupt, IRQF_SHARED,
net_dev->name, net_dev);
if (ret)
return ret;
@@ -1760,7 +1760,7 @@ static int sis900_rx(struct net_device *net_dev)
sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE,
PCI_DMA_FROMDEVICE);
- /* refill the Rx buffer, what if there is not enought
+ /* refill the Rx buffer, what if there is not enough
* memory for new socket buffer ?? */
if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
/*
@@ -1775,7 +1775,7 @@ static int sis900_rx(struct net_device *net_dev)
}
/* This situation should never happen, but due to
- some unknow bugs, it is possible that
+ some unknown bugs, it is possible that
we are working on NULL sk_buff :-( */
if (sis_priv->rx_skbuff[entry] == NULL) {
if (netif_msg_rx_err(sis_priv))
diff --git a/drivers/net/skfp/h/smc.h b/drivers/net/skfp/h/smc.h
index 1758d954836..026a83b9f74 100644
--- a/drivers/net/skfp/h/smc.h
+++ b/drivers/net/skfp/h/smc.h
@@ -393,10 +393,10 @@ struct smt_config {
*/
u_long mac_d_max ; /* MAC : D_Max timer value */
- u_long lct_short ; /* LCT : error threshhold */
- u_long lct_medium ; /* LCT : error threshhold */
- u_long lct_long ; /* LCT : error threshhold */
- u_long lct_extended ; /* LCT : error threshhold */
+ u_long lct_short ; /* LCT : error threshold */
+ u_long lct_medium ; /* LCT : error threshold */
+ u_long lct_long ; /* LCT : error threshold */
+ u_long lct_extended ; /* LCT : error threshold */
} ;
#ifdef DEBUG
diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c
index b27156eaf26..db216a72850 100644
--- a/drivers/net/skfp/skfddi.c
+++ b/drivers/net/skfp/skfddi.c
@@ -1002,7 +1002,7 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
break;
default:
- printk("ioctl for %s: unknow cmd: %04x\n", dev->name, ioc.cmd);
+ printk("ioctl for %s: unknown cmd: %04x\n", dev->name, ioc.cmd);
status = -EOPNOTSUPP;
} // switch
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 8f5414348e8..379a3dc0016 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -238,8 +238,8 @@ static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct skge_port *skge = netdev_priv(dev);
struct skge_hw *hw = skge->hw;
- if ((wol->wolopts & ~wol_supported(hw))
- || !device_can_wakeup(&hw->pdev->dev))
+ if ((wol->wolopts & ~wol_supported(hw)) ||
+ !device_can_wakeup(&hw->pdev->dev))
return -EOPNOTSUPP;
skge->wol = wol->wolopts;
@@ -576,9 +576,10 @@ static void skge_get_pauseparam(struct net_device *dev,
{
struct skge_port *skge = netdev_priv(dev);
- ecmd->rx_pause = (skge->flow_control == FLOW_MODE_SYMMETRIC)
- || (skge->flow_control == FLOW_MODE_SYM_OR_REM);
- ecmd->tx_pause = ecmd->rx_pause || (skge->flow_control == FLOW_MODE_LOC_SEND);
+ ecmd->rx_pause = ((skge->flow_control == FLOW_MODE_SYMMETRIC) ||
+ (skge->flow_control == FLOW_MODE_SYM_OR_REM));
+ ecmd->tx_pause = (ecmd->rx_pause ||
+ (skge->flow_control == FLOW_MODE_LOC_SEND));
ecmd->autoneg = ecmd->rx_pause || ecmd->tx_pause;
}
@@ -2779,8 +2780,8 @@ static netdev_tx_t skge_xmit_frame(struct sk_buff *skb,
/* This seems backwards, but it is what the sk98lin
* does. Looks like hardware is wrong?
*/
- if (ipip_hdr(skb)->protocol == IPPROTO_UDP
- && hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON)
+ if (ipip_hdr(skb)->protocol == IPPROTO_UDP &&
+ hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON)
control = BMU_TCP_CHECK;
else
control = BMU_UDP_CHECK;
@@ -2948,8 +2949,8 @@ static void genesis_set_multicast(struct net_device *dev)
else {
memset(filter, 0, sizeof(filter));
- if (skge->flow_status == FLOW_STAT_REM_SEND
- || skge->flow_status == FLOW_STAT_SYMMETRIC)
+ if (skge->flow_status == FLOW_STAT_REM_SEND ||
+ skge->flow_status == FLOW_STAT_SYMMETRIC)
genesis_add_filter(filter, pause_mc_addr);
for (i = 0; list && i < count; i++, list = list->next)
@@ -2972,8 +2973,8 @@ static void yukon_set_multicast(struct net_device *dev)
struct skge_hw *hw = skge->hw;
int port = skge->port;
struct dev_mc_list *list = dev->mc_list;
- int rx_pause = (skge->flow_status == FLOW_STAT_REM_SEND
- || skge->flow_status == FLOW_STAT_SYMMETRIC);
+ int rx_pause = (skge->flow_status == FLOW_STAT_REM_SEND ||
+ skge->flow_status == FLOW_STAT_SYMMETRIC);
u16 reg;
u8 filter[8];
@@ -3071,11 +3072,10 @@ static struct sk_buff *skge_rx_get(struct net_device *dev,
goto error;
if (len < RX_COPY_THRESHOLD) {
- skb = netdev_alloc_skb(dev, len + 2);
+ skb = netdev_alloc_skb_ip_align(dev, len);
if (!skb)
goto resubmit;
- skb_reserve(skb, 2);
pci_dma_sync_single_for_cpu(skge->hw->pdev,
pci_unmap_addr(e, mapaddr),
len, PCI_DMA_FROMDEVICE);
@@ -3086,11 +3086,11 @@ static struct sk_buff *skge_rx_get(struct net_device *dev,
skge_rx_reuse(e, skge->rx_buf_size);
} else {
struct sk_buff *nskb;
- nskb = netdev_alloc_skb(dev, skge->rx_buf_size + NET_IP_ALIGN);
+
+ nskb = netdev_alloc_skb_ip_align(dev, skge->rx_buf_size);
if (!nskb)
goto resubmit;
- skb_reserve(nskb, NET_IP_ALIGN);
pci_unmap_single(skge->hw->pdev,
pci_unmap_addr(e, mapaddr),
pci_unmap_len(e, maplen),
@@ -3948,7 +3948,7 @@ static int __devinit skge_probe(struct pci_dev *pdev,
hw->pdev = pdev;
spin_lock_init(&hw->hw_lock);
spin_lock_init(&hw->phy_lock);
- tasklet_init(&hw->phy_task, &skge_extirq, (unsigned long) hw);
+ tasklet_init(&hw->phy_task, skge_extirq, (unsigned long) hw);
hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
if (!hw->regs) {
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 6a10d7ba587..044e6817986 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -50,7 +50,7 @@
#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.25"
+#define DRV_VERSION "1.26"
#define PFX DRV_NAME " "
/*
@@ -102,6 +102,7 @@ MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */
+ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E01) }, /* SK-9E21M */
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) }, /* DGE-560T */
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4001) }, /* DGE-550SX */
{ PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4B02) }, /* DGE-560SX */
@@ -139,6 +140,7 @@ static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436D) }, /* 88E8055 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */
{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4381) }, /* 88E8059 */
{ 0 }
};
@@ -372,8 +374,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO);
/* downshift on PHY 88E1112 and 88E1149 is changed */
- if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED)
- && (hw->flags & SKY2_HW_NEWER_PHY)) {
+ if ( (sky2->flags & SKY2_FLAG_AUTO_SPEED) &&
+ (hw->flags & SKY2_HW_NEWER_PHY)) {
/* set downshift counter to 3x and enable downshift */
ctrl &= ~PHY_M_PC_DSC_MSK;
ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA;
@@ -602,13 +604,23 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
/* apply workaround for integrated resistors calibration */
gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17);
gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60);
+ } else if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) {
+ /* apply fixes in PHY AFE */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff);
+
+ /* apply RDAC termination workaround */
+ gm_phy_write(hw, port, 24, 0x2800);
+ gm_phy_write(hw, port, 23, 0x2001);
+
+ /* set page register back to 0 */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
} else if (hw->chip_id != CHIP_ID_YUKON_EX &&
hw->chip_id < CHIP_ID_YUKON_SUPR) {
/* no effect on Yukon-XL */
gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
- if ( !(sky2->flags & SKY2_FLAG_AUTO_SPEED)
- || sky2->speed == SPEED_100) {
+ if (!(sky2->flags & SKY2_FLAG_AUTO_SPEED) ||
+ sky2->speed == SPEED_100) {
/* turn on 100 Mbps LED (LED_LINK100) */
ledover |= PHY_M_LED_MO_100(MO_LED_ON);
}
@@ -786,8 +798,7 @@ static void sky2_set_tx_stfwd(struct sky2_hw *hw, unsigned port)
if ( (hw->chip_id == CHIP_ID_YUKON_EX &&
hw->chip_rev != CHIP_REV_YU_EX_A0) ||
- hw->chip_id == CHIP_ID_YUKON_FE_P ||
- hw->chip_id == CHIP_ID_YUKON_SUPR) {
+ hw->chip_id >= CHIP_ID_YUKON_FE_P) {
/* Yukon-Extreme B0 and further Extreme devices */
/* enable Store & Forward mode for TX */
@@ -925,8 +936,14 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
/* On chips without ram buffer, pause is controled by MAC level */
if (!(hw->flags & SKY2_HW_RAM_BUFFER)) {
- sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8);
- sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8);
+ /* Pause threshold is scaled by 8 in bytes */
+ if (hw->chip_id == CHIP_ID_YUKON_FE_P &&
+ hw->chip_rev == CHIP_REV_YU_FE2_A0)
+ reg = 1568 / 8;
+ else
+ reg = 1024 / 8;
+ sky2_write16(hw, SK_REG(port, RX_GMF_UP_THR), reg);
+ sky2_write16(hw, SK_REG(port, RX_GMF_LP_THR), 768 / 8);
sky2_set_tx_stfwd(hw, port);
}
@@ -1336,8 +1353,8 @@ static int sky2_rx_start(struct sky2_port *sky2)
/* These chips have no ram buffer?
* MAC Rx RAM Read is controlled by hardware */
if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
- (hw->chip_rev == CHIP_REV_YU_EC_U_A1
- || hw->chip_rev == CHIP_REV_YU_EC_U_B0))
+ (hw->chip_rev == CHIP_REV_YU_EC_U_A1 ||
+ hw->chip_rev == CHIP_REV_YU_EC_U_B0))
sky2_write32(hw, Q_ADDR(rxq, Q_TEST), F_M_RX_RAM_DIS);
sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1);
@@ -1397,6 +1414,31 @@ static int sky2_rx_start(struct sky2_port *sky2)
/* Tell chip about available buffers */
sky2_rx_update(sky2, rxq);
+
+ if (hw->chip_id == CHIP_ID_YUKON_EX ||
+ hw->chip_id == CHIP_ID_YUKON_SUPR) {
+ /*
+ * Disable flushing of non ASF packets;
+ * must be done after initializing the BMUs;
+ * drivers without ASF support should do this too, otherwise
+ * it may happen that they cannot run on ASF devices;
+ * remember that the MAC FIFO isn't reset during initialization.
+ */
+ sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_MACSEC_FLUSH_OFF);
+ }
+
+ if (hw->chip_id >= CHIP_ID_YUKON_SUPR) {
+ /* Enable RX Home Address & Routing Header checksum fix */
+ sky2_write16(hw, SK_REG(sky2->port, RX_GMF_FL_CTRL),
+ RX_IPV6_SA_MOB_ENA | RX_IPV6_DA_MOB_ENA);
+
+ /* Enable TX Home Address & Routing Header checksum fix */
+ sky2_write32(hw, Q_ADDR(txqaddr[sky2->port], Q_TEST),
+ TBMU_TEST_HOME_ADD_FIX_EN | TBMU_TEST_ROUTING_ADD_FIX_EN);
+ }
+
+
+
return 0;
nomem:
sky2_rx_clean(sky2);
@@ -1518,8 +1560,8 @@ static int sky2_up(struct net_device *dev)
sky2_write32(hw, Q_ADDR(txqaddr[port], Q_TEST), F_TX_CHK_AUTO_OFF);
/* Set almost empty threshold */
- if (hw->chip_id == CHIP_ID_YUKON_EC_U
- && hw->chip_rev == CHIP_REV_YU_EC_U_A0)
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U &&
+ hw->chip_rev == CHIP_REV_YU_EC_U_A0)
sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV);
sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
@@ -1865,8 +1907,8 @@ static int sky2_down(struct net_device *dev)
sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
/* Workaround shared GMAC reset */
- if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0
- && port == 0 && hw->dev[1] && netif_running(hw->dev[1])))
+ if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 &&
+ port == 0 && hw->dev[1] && netif_running(hw->dev[1])))
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
@@ -1975,7 +2017,7 @@ static void sky2_link_down(struct sky2_port *sky2)
netif_carrier_off(sky2->netdev);
- /* Turn on link LED */
+ /* Turn off link LED */
sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
if (netif_msg_link(sky2))
@@ -2043,8 +2085,8 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
sky2->flow_status = FC_TX;
}
- if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000
- && !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX))
+ if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 &&
+ !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX))
sky2->flow_status = FC_NONE;
if (sky2->flow_status & FC_TX)
@@ -2096,6 +2138,25 @@ out:
spin_unlock(&sky2->phy_lock);
}
+/* Special quick link interrupt (Yukon-2 Optima only) */
+static void sky2_qlink_intr(struct sky2_hw *hw)
+{
+ struct sky2_port *sky2 = netdev_priv(hw->dev[0]);
+ u32 imask;
+ u16 phy;
+
+ /* disable irq */
+ imask = sky2_read32(hw, B0_IMSK);
+ imask &= ~Y2_IS_PHY_QLNK;
+ sky2_write32(hw, B0_IMSK, imask);
+
+ /* reset PHY Link Detect */
+ phy = sky2_pci_read16(hw, PSM_CONFIG_REG4);
+ sky2_pci_write16(hw, PSM_CONFIG_REG4, phy | 1);
+
+ sky2_link_up(sky2);
+}
+
/* Transmit timeout is only called if we are running, carrier is up
* and tx queue is full (stopped).
*/
@@ -2191,9 +2252,8 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2,
{
struct sk_buff *skb;
- skb = netdev_alloc_skb(sky2->netdev, length + 2);
+ skb = netdev_alloc_skb_ip_align(sky2->netdev, length);
if (likely(skb)) {
- skb_reserve(skb, 2);
pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr,
length, PCI_DMA_FROMDEVICE);
skb_copy_from_linear_data(re->skb, skb->data, length);
@@ -2766,6 +2826,9 @@ static int sky2_poll(struct napi_struct *napi, int work_limit)
if (status & Y2_IS_IRQ_PHY2)
sky2_phy_intr(hw, 1);
+ if (status & Y2_IS_PHY_QLNK)
+ sky2_qlink_intr(hw);
+
while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) {
work_done += sky2_status_intr(hw, work_limit - work_done, idx);
@@ -2815,6 +2878,7 @@ static u32 sky2_mhz(const struct sky2_hw *hw)
case CHIP_ID_YUKON_EX:
case CHIP_ID_YUKON_SUPR:
case CHIP_ID_YUKON_UL_2:
+ case CHIP_ID_YUKON_OPT:
return 125;
case CHIP_ID_YUKON_FE:
@@ -2904,6 +2968,7 @@ static int __devinit sky2_init(struct sky2_hw *hw)
break;
case CHIP_ID_YUKON_UL_2:
+ case CHIP_ID_YUKON_OPT:
hw->flags = SKY2_HW_GIGABIT
| SKY2_HW_ADV_POWER_CTL;
break;
@@ -2986,6 +3051,52 @@ static void sky2_reset(struct sky2_hw *hw)
sky2_write16(hw, SK_REG(i, GMAC_CTRL),
GMC_BYP_MACSECRX_ON | GMC_BYP_MACSECTX_ON
| GMC_BYP_RETR_ON);
+
+ }
+
+ if (hw->chip_id == CHIP_ID_YUKON_SUPR && hw->chip_rev > CHIP_REV_YU_SU_B0) {
+ /* enable MACSec clock gating */
+ sky2_pci_write32(hw, PCI_DEV_REG3, P_CLK_MACSEC_DIS);
+ }
+
+ if (hw->chip_id == CHIP_ID_YUKON_OPT) {
+ u16 reg;
+ u32 msk;
+
+ if (hw->chip_rev == 0) {
+ /* disable PCI-E PHY power down (set PHY reg 0x80, bit 7 */
+ sky2_write32(hw, Y2_PEX_PHY_DATA, (0x80UL << 16) | (1 << 7));
+
+ /* set PHY Link Detect Timer to 1.1 second (11x 100ms) */
+ reg = 10;
+ } else {
+ /* set PHY Link Detect Timer to 0.4 second (4x 100ms) */
+ reg = 3;
+ }
+
+ reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE;
+
+ /* reset PHY Link Detect */
+ sky2_pci_write16(hw, PSM_CONFIG_REG4,
+ reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT);
+ sky2_pci_write16(hw, PSM_CONFIG_REG4, reg);
+
+
+ /* enable PHY Quick Link */
+ msk = sky2_read32(hw, B0_IMSK);
+ msk |= Y2_IS_PHY_QLNK;
+ sky2_write32(hw, B0_IMSK, msk);
+
+ /* check if PSMv2 was running before */
+ reg = sky2_pci_read16(hw, PSM_CONFIG_REG3);
+ if (reg & PCI_EXP_LNKCTL_ASPMC) {
+ int cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ /* restore the PCIe Link Control register */
+ sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg);
+ }
+
+ /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
+ sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
}
/* Clear I2C IRQ noise */
@@ -3133,8 +3244,8 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct sky2_port *sky2 = netdev_priv(dev);
struct sky2_hw *hw = sky2->hw;
- if ((wol->wolopts & ~sky2_wol_supported(sky2->hw))
- || !device_can_wakeup(&hw->pdev->dev))
+ if ((wol->wolopts & ~sky2_wol_supported(sky2->hw)) ||
+ !device_can_wakeup(&hw->pdev->dev))
return -EOPNOTSUPP;
sky2->wol = wol->wolopts;
@@ -4406,9 +4517,11 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
"FE+", /* 0xb8 */
"Supreme", /* 0xb9 */
"UL 2", /* 0xba */
+ "Unknown", /* 0xbb */
+ "Optima", /* 0xbc */
};
- if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_UL_2)
+ if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_OPT)
strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
else
snprintf(buf, sz, "(chip %#x)", chipid);
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h
index ed54129698b..365d79c7d83 100644
--- a/drivers/net/sky2.h
+++ b/drivers/net/sky2.h
@@ -16,6 +16,13 @@ enum {
PCI_DEV_REG5 = 0x88,
PCI_CFG_REG_0 = 0x90,
PCI_CFG_REG_1 = 0x94,
+
+ PSM_CONFIG_REG0 = 0x98,
+ PSM_CONFIG_REG1 = 0x9C,
+ PSM_CONFIG_REG2 = 0x160,
+ PSM_CONFIG_REG3 = 0x164,
+ PSM_CONFIG_REG4 = 0x168,
+
};
/* Yukon-2 */
@@ -48,6 +55,37 @@ enum pci_dev_reg_2 {
PCI_USEDATA64 = 1<<0, /* Use 64Bit Data bus ext */
};
+/* PCI_OUR_REG_3 32 bit Our Register 3 (Yukon-ECU only) */
+enum pci_dev_reg_3 {
+ P_CLK_ASF_REGS_DIS = 1<<18,/* Disable Clock ASF (Yukon-Ext.) */
+ P_CLK_COR_REGS_D0_DIS = 1<<17,/* Disable Clock Core Regs D0 */
+ P_CLK_MACSEC_DIS = 1<<17,/* Disable Clock MACSec (Yukon-Ext.) */
+ P_CLK_PCI_REGS_D0_DIS = 1<<16,/* Disable Clock PCI Regs D0 */
+ P_CLK_COR_YTB_ARB_DIS = 1<<15,/* Disable Clock YTB Arbiter */
+ P_CLK_MAC_LNK1_D3_DIS = 1<<14,/* Disable Clock MAC Link1 D3 */
+ P_CLK_COR_LNK1_D0_DIS = 1<<13,/* Disable Clock Core Link1 D0 */
+ P_CLK_MAC_LNK1_D0_DIS = 1<<12,/* Disable Clock MAC Link1 D0 */
+ P_CLK_COR_LNK1_D3_DIS = 1<<11,/* Disable Clock Core Link1 D3 */
+ P_CLK_PCI_MST_ARB_DIS = 1<<10,/* Disable Clock PCI Master Arb. */
+ P_CLK_COR_REGS_D3_DIS = 1<<9, /* Disable Clock Core Regs D3 */
+ P_CLK_PCI_REGS_D3_DIS = 1<<8, /* Disable Clock PCI Regs D3 */
+ P_CLK_REF_LNK1_GM_DIS = 1<<7, /* Disable Clock Ref. Link1 GMAC */
+ P_CLK_COR_LNK1_GM_DIS = 1<<6, /* Disable Clock Core Link1 GMAC */
+ P_CLK_PCI_COMMON_DIS = 1<<5, /* Disable Clock PCI Common */
+ P_CLK_COR_COMMON_DIS = 1<<4, /* Disable Clock Core Common */
+ P_CLK_PCI_LNK1_BMU_DIS = 1<<3, /* Disable Clock PCI Link1 BMU */
+ P_CLK_COR_LNK1_BMU_DIS = 1<<2, /* Disable Clock Core Link1 BMU */
+ P_CLK_PCI_LNK1_BIU_DIS = 1<<1, /* Disable Clock PCI Link1 BIU */
+ P_CLK_COR_LNK1_BIU_DIS = 1<<0, /* Disable Clock Core Link1 BIU */
+ PCIE_OUR3_WOL_D3_COLD_SET = P_CLK_ASF_REGS_DIS |
+ P_CLK_COR_REGS_D0_DIS |
+ P_CLK_COR_LNK1_D0_DIS |
+ P_CLK_MAC_LNK1_D0_DIS |
+ P_CLK_PCI_MST_ARB_DIS |
+ P_CLK_COR_COMMON_DIS |
+ P_CLK_COR_LNK1_BMU_DIS,
+};
+
/* PCI_OUR_REG_4 32 bit Our Register 4 (Yukon-ECU only) */
enum pci_dev_reg_4 {
/* (Link Training & Status State Machine) */
@@ -114,7 +152,7 @@ enum pci_dev_reg_5 {
P_GAT_PCIE_RX_EL_IDLE,
};
-#/* PCI_CFG_REG_1 32 bit Config Register 1 (Yukon-Ext only) */
+/* PCI_CFG_REG_1 32 bit Config Register 1 (Yukon-Ext only) */
enum pci_cfg_reg1 {
P_CF1_DIS_REL_EVT_RST = 1<<24, /* Dis. Rel. Event during PCIE reset */
/* Bit 23..21: Release Clock on Event */
@@ -145,6 +183,72 @@ enum pci_cfg_reg1 {
P_CF1_ENA_TXBMU_WR_IDLE,
};
+/* Yukon-Optima */
+enum {
+ PSM_CONFIG_REG1_AC_PRESENT_STATUS = 1<<31, /* AC Present Status */
+
+ PSM_CONFIG_REG1_PTP_CLK_SEL = 1<<29, /* PTP Clock Select */
+ PSM_CONFIG_REG1_PTP_MODE = 1<<28, /* PTP Mode */
+
+ PSM_CONFIG_REG1_MUX_PHY_LINK = 1<<27, /* PHY Energy Detect Event */
+
+ PSM_CONFIG_REG1_EN_PIN63_AC_PRESENT = 1<<26, /* Enable LED_DUPLEX for ac_present */
+ PSM_CONFIG_REG1_EN_PCIE_TIMER = 1<<25, /* Enable PCIe Timer */
+ PSM_CONFIG_REG1_EN_SPU_TIMER = 1<<24, /* Enable SPU Timer */
+ PSM_CONFIG_REG1_POLARITY_AC_PRESENT = 1<<23, /* AC Present Polarity */
+
+ PSM_CONFIG_REG1_EN_AC_PRESENT = 1<<21, /* Enable AC Present */
+
+ PSM_CONFIG_REG1_EN_GPHY_INT_PSM = 1<<20, /* Enable GPHY INT for PSM */
+ PSM_CONFIG_REG1_DIS_PSM_TIMER = 1<<19, /* Disable PSM Timer */
+};
+
+/* Yukon-Supreme */
+enum {
+ PSM_CONFIG_REG1_GPHY_ENERGY_STS = 1<<31, /* GPHY Energy Detect Status */
+
+ PSM_CONFIG_REG1_UART_MODE_MSK = 3<<29, /* UART_Mode */
+ PSM_CONFIG_REG1_CLK_RUN_ASF = 1<<28, /* Enable Clock Free Running for ASF Subsystem */
+ PSM_CONFIG_REG1_UART_CLK_DISABLE= 1<<27, /* Disable UART clock */
+ PSM_CONFIG_REG1_VAUX_ONE = 1<<26, /* Tie internal Vaux to 1'b1 */
+ PSM_CONFIG_REG1_UART_FC_RI_VAL = 1<<25, /* Default value for UART_RI_n */
+ PSM_CONFIG_REG1_UART_FC_DCD_VAL = 1<<24, /* Default value for UART_DCD_n */
+ PSM_CONFIG_REG1_UART_FC_DSR_VAL = 1<<23, /* Default value for UART_DSR_n */
+ PSM_CONFIG_REG1_UART_FC_CTS_VAL = 1<<22, /* Default value for UART_CTS_n */
+ PSM_CONFIG_REG1_LATCH_VAUX = 1<<21, /* Enable Latch current Vaux_avlbl */
+ PSM_CONFIG_REG1_FORCE_TESTMODE_INPUT= 1<<20, /* Force Testmode pin as input PAD */
+ PSM_CONFIG_REG1_UART_RST = 1<<19, /* UART_RST */
+ PSM_CONFIG_REG1_PSM_PCIE_L1_POL = 1<<18, /* PCIE L1 Event Polarity for PSM */
+ PSM_CONFIG_REG1_TIMER_STAT = 1<<17, /* PSM Timer Status */
+ PSM_CONFIG_REG1_GPHY_INT = 1<<16, /* GPHY INT Status */
+ PSM_CONFIG_REG1_FORCE_TESTMODE_ZERO= 1<<15, /* Force internal Testmode as 1'b0 */
+ PSM_CONFIG_REG1_EN_INT_ASPM_CLKREQ = 1<<14, /* ENABLE INT for CLKRUN on ASPM and CLKREQ */
+ PSM_CONFIG_REG1_EN_SND_TASK_ASPM_CLKREQ = 1<<13, /* ENABLE Snd_task for CLKRUN on ASPM and CLKREQ */
+ PSM_CONFIG_REG1_DIS_CLK_GATE_SND_TASK = 1<<12, /* Disable CLK_GATE control snd_task */
+ PSM_CONFIG_REG1_DIS_FF_CHIAN_SND_INTA = 1<<11, /* Disable flip-flop chain for sndmsg_inta */
+
+ PSM_CONFIG_REG1_DIS_LOADER = 1<<9, /* Disable Loader SM after PSM Goes back to IDLE */
+ PSM_CONFIG_REG1_DO_PWDN = 1<<8, /* Do Power Down, Start PSM Scheme */
+ PSM_CONFIG_REG1_DIS_PIG = 1<<7, /* Disable Plug-in-Go SM after PSM Goes back to IDLE */
+ PSM_CONFIG_REG1_DIS_PERST = 1<<6, /* Disable Internal PCIe Reset after PSM Goes back to IDLE */
+ PSM_CONFIG_REG1_EN_REG18_PD = 1<<5, /* Enable REG18 Power Down for PSM */
+ PSM_CONFIG_REG1_EN_PSM_LOAD = 1<<4, /* Disable EEPROM Loader after PSM Goes back to IDLE */
+ PSM_CONFIG_REG1_EN_PSM_HOT_RST = 1<<3, /* Enable PCIe Hot Reset for PSM */
+ PSM_CONFIG_REG1_EN_PSM_PERST = 1<<2, /* Enable PCIe Reset Event for PSM */
+ PSM_CONFIG_REG1_EN_PSM_PCIE_L1 = 1<<1, /* Enable PCIe L1 Event for PSM */
+ PSM_CONFIG_REG1_EN_PSM = 1<<0, /* Enable PSM Scheme */
+};
+
+/* PSM_CONFIG_REG4 0x0168 PSM Config Register 4 */
+enum {
+ /* PHY Link Detect Timer */
+ PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_MSK = 0xf<<4,
+ PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE = 4,
+
+ PSM_CONFIG_REG4_DEBUG_TIMER = 1<<1, /* Debug Timer */
+ PSM_CONFIG_REG4_RST_PHY_LINK_DETECT = 1<<0, /* Reset GPHY Link Detect */
+};
+
#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \
PCI_STATUS_SIG_SYSTEM_ERROR | \
@@ -197,6 +301,9 @@ enum csr_regs {
B2_I2C_IRQ = 0x0168,
B2_I2C_SW = 0x016c,
+ Y2_PEX_PHY_DATA = 0x0170,
+ Y2_PEX_PHY_ADDR = 0x0172,
+
B3_RAM_ADDR = 0x0180,
B3_RAM_DATA_LO = 0x0184,
B3_RAM_DATA_HI = 0x0188,
@@ -317,6 +424,10 @@ enum {
Y2_IS_CHK_TXS2 = 1<<9, /* Descriptor error TXS 2 */
Y2_IS_CHK_TXA2 = 1<<8, /* Descriptor error TXA 2 */
+ Y2_IS_PSM_ACK = 1<<7, /* PSM Acknowledge (Yukon-Optima only) */
+ Y2_IS_PTP_TIST = 1<<6, /* PTP Time Stamp (Yukon-Optima only) */
+ Y2_IS_PHY_QLNK = 1<<5, /* PHY Quick Link (Yukon-Optima only) */
+
Y2_IS_IRQ_PHY1 = 1<<4, /* Interrupt from PHY 1 */
Y2_IS_IRQ_MAC1 = 1<<3, /* Interrupt from MAC 1 */
Y2_IS_CHK_RX1 = 1<<2, /* Descriptor error Rx 1 */
@@ -435,6 +546,7 @@ enum {
CHIP_ID_YUKON_FE_P = 0xb8, /* YUKON-2 FE+ */
CHIP_ID_YUKON_SUPR = 0xb9, /* YUKON-2 Supreme */
CHIP_ID_YUKON_UL_2 = 0xba, /* YUKON-2 Ultra 2 */
+ CHIP_ID_YUKON_OPT = 0xbc, /* YUKON-2 Optima */
};
enum yukon_ec_rev {
CHIP_REV_YU_EC_A1 = 0, /* Chip Rev. for Yukon-EC A1/A0 */
@@ -459,6 +571,8 @@ enum yukon_ex_rev {
};
enum yukon_supr_rev {
CHIP_REV_YU_SU_A0 = 0,
+ CHIP_REV_YU_SU_B0 = 1,
+ CHIP_REV_YU_SU_B1 = 3,
};
@@ -513,6 +627,12 @@ enum {
TIM_T_STEP = 1<<0, /* Test step */
};
+/* Y2_PEX_PHY_ADDR/DATA PEX PHY address and data reg (Yukon-2 only) */
+enum {
+ PEX_RD_ACCESS = 1<<31, /* Access Mode Read = 1, Write = 0 */
+ PEX_DB_ACCESS = 1<<30, /* Access to debug register */
+};
+
/* B3_RAM_ADDR 32 bit RAM Address, to read or write */
/* Bit 31..19: reserved */
#define RAM_ADR_RAN 0x0007ffffL /* Bit 18.. 0: RAM Address Range */
@@ -688,10 +808,11 @@ enum {
RX_GMF_AF_THR = 0x0c44,/* 32 bit Rx GMAC FIFO Almost Full Thresh. */
RX_GMF_CTRL_T = 0x0c48,/* 32 bit Rx GMAC FIFO Control/Test */
RX_GMF_FL_MSK = 0x0c4c,/* 32 bit Rx GMAC FIFO Flush Mask */
- RX_GMF_FL_THR = 0x0c50,/* 32 bit Rx GMAC FIFO Flush Threshold */
+ RX_GMF_FL_THR = 0x0c50,/* 16 bit Rx GMAC FIFO Flush Threshold */
+ RX_GMF_FL_CTRL = 0x0c52,/* 16 bit Rx GMAC FIFO Flush Control */
RX_GMF_TR_THR = 0x0c54,/* 32 bit Rx Truncation Threshold (Yukon-2) */
- RX_GMF_UP_THR = 0x0c58,/* 8 bit Rx Upper Pause Thr (Yukon-EC_U) */
- RX_GMF_LP_THR = 0x0c5a,/* 8 bit Rx Lower Pause Thr (Yukon-EC_U) */
+ RX_GMF_UP_THR = 0x0c58,/* 16 bit Rx Upper Pause Thr (Yukon-EC_U) */
+ RX_GMF_LP_THR = 0x0c5a,/* 16 bit Rx Lower Pause Thr (Yukon-EC_U) */
RX_GMF_VLAN = 0x0c5c,/* 32 bit Rx VLAN Type Register (Yukon-2) */
RX_GMF_WP = 0x0c60,/* 32 bit Rx GMAC FIFO Write Pointer */
@@ -754,6 +875,42 @@ enum {
BMU_TX_CLR_IRQ_TCP = 1<<11, /* Clear IRQ on TCP segment length mismatch */
};
+/* TBMU_TEST 0x06B8 Transmit BMU Test Register */
+enum {
+ TBMU_TEST_BMU_TX_CHK_AUTO_OFF = 1<<31, /* BMU Tx Checksum Auto Calculation Disable */
+ TBMU_TEST_BMU_TX_CHK_AUTO_ON = 1<<30, /* BMU Tx Checksum Auto Calculation Enable */
+ TBMU_TEST_HOME_ADD_PAD_FIX1_EN = 1<<29, /* Home Address Paddiing FIX1 Enable */
+ TBMU_TEST_HOME_ADD_PAD_FIX1_DIS = 1<<28, /* Home Address Paddiing FIX1 Disable */
+ TBMU_TEST_ROUTING_ADD_FIX_EN = 1<<27, /* Routing Address Fix Enable */
+ TBMU_TEST_ROUTING_ADD_FIX_DIS = 1<<26, /* Routing Address Fix Disable */
+ TBMU_TEST_HOME_ADD_FIX_EN = 1<<25, /* Home address checksum fix enable */
+ TBMU_TEST_HOME_ADD_FIX_DIS = 1<<24, /* Home address checksum fix disable */
+
+ TBMU_TEST_TEST_RSPTR_ON = 1<<22, /* Testmode Shadow Read Ptr On */
+ TBMU_TEST_TEST_RSPTR_OFF = 1<<21, /* Testmode Shadow Read Ptr Off */
+ TBMU_TEST_TESTSTEP_RSPTR = 1<<20, /* Teststep Shadow Read Ptr */
+
+ TBMU_TEST_TEST_RPTR_ON = 1<<18, /* Testmode Read Ptr On */
+ TBMU_TEST_TEST_RPTR_OFF = 1<<17, /* Testmode Read Ptr Off */
+ TBMU_TEST_TESTSTEP_RPTR = 1<<16, /* Teststep Read Ptr */
+
+ TBMU_TEST_TEST_WSPTR_ON = 1<<14, /* Testmode Shadow Write Ptr On */
+ TBMU_TEST_TEST_WSPTR_OFF = 1<<13, /* Testmode Shadow Write Ptr Off */
+ TBMU_TEST_TESTSTEP_WSPTR = 1<<12, /* Teststep Shadow Write Ptr */
+
+ TBMU_TEST_TEST_WPTR_ON = 1<<10, /* Testmode Write Ptr On */
+ TBMU_TEST_TEST_WPTR_OFF = 1<<9, /* Testmode Write Ptr Off */
+ TBMU_TEST_TESTSTEP_WPTR = 1<<8, /* Teststep Write Ptr */
+
+ TBMU_TEST_TEST_REQ_NB_ON = 1<<6, /* Testmode Req Nbytes/Addr On */
+ TBMU_TEST_TEST_REQ_NB_OFF = 1<<5, /* Testmode Req Nbytes/Addr Off */
+ TBMU_TEST_TESTSTEP_REQ_NB = 1<<4, /* Teststep Req Nbytes/Addr */
+
+ TBMU_TEST_TEST_DONE_IDX_ON = 1<<2, /* Testmode Done Index On */
+ TBMU_TEST_TEST_DONE_IDX_OFF = 1<<1, /* Testmode Done Index Off */
+ TBMU_TEST_TESTSTEP_DONE_IDX = 1<<0, /* Teststep Done Index */
+};
+
/* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/
/* PREF_UNIT_CTRL 32 bit Prefetch Control register */
enum {
@@ -1674,6 +1831,12 @@ enum {
/* RX_GMF_CTRL_T 32 bit Rx GMAC FIFO Control/Test */
enum {
+ RX_GCLKMAC_ENA = 1<<31, /* RX MAC Clock Gating Enable */
+ RX_GCLKMAC_OFF = 1<<30,
+
+ RX_STFW_DIS = 1<<29, /* RX Store and Forward Enable */
+ RX_STFW_ENA = 1<<28,
+
RX_TRUNC_ON = 1<<27, /* enable packet truncation */
RX_TRUNC_OFF = 1<<26, /* disable packet truncation */
RX_VLAN_STRIP_ON = 1<<25, /* enable VLAN stripping */
@@ -1711,6 +1874,20 @@ enum {
GMF_RX_CTRL_DEF = GMF_OPER_ON | GMF_RX_F_FL_ON,
};
+/* RX_GMF_FL_CTRL 16 bit Rx GMAC FIFO Flush Control (Yukon-Supreme) */
+enum {
+ RX_IPV6_SA_MOB_ENA = 1<<9, /* IPv6 SA Mobility Support Enable */
+ RX_IPV6_SA_MOB_DIS = 1<<8, /* IPv6 SA Mobility Support Disable */
+ RX_IPV6_DA_MOB_ENA = 1<<7, /* IPv6 DA Mobility Support Enable */
+ RX_IPV6_DA_MOB_DIS = 1<<6, /* IPv6 DA Mobility Support Disable */
+ RX_PTR_SYNCDLY_ENA = 1<<5, /* Pointers Delay Synch Enable */
+ RX_PTR_SYNCDLY_DIS = 1<<4, /* Pointers Delay Synch Disable */
+ RX_ASF_NEWFLAG_ENA = 1<<3, /* RX ASF Flag New Logic Enable */
+ RX_ASF_NEWFLAG_DIS = 1<<2, /* RX ASF Flag New Logic Disable */
+ RX_FLSH_MISSPKT_ENA = 1<<1, /* RX Flush Miss-Packet Enable */
+ RX_FLSH_MISSPKT_DIS = 1<<0, /* RX Flush Miss-Packet Disable */
+};
+
/* TX_GMF_EA 32 bit Tx GMAC FIFO End Address */
enum {
TX_DYN_WM_ENA = 3, /* Yukon-FE+ specific */
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index fe3cebb984d..ba5bbc50344 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -80,6 +80,7 @@
#include <linux/rtnetlink.h>
#include <linux/if_arp.h>
#include <linux/if_slip.h>
+#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/init.h>
#include "slip.h"
@@ -955,8 +956,8 @@ static void slip_unesc(struct slip *sl, unsigned char s)
clear_bit(SLF_KEEPTEST, &sl->flags);
#endif
- if (!test_and_clear_bit(SLF_ERROR, &sl->flags)
- && (sl->rcount > 2))
+ if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
+ (sl->rcount > 2))
sl_bump(sl);
clear_bit(SLF_ESCAPE, &sl->flags);
sl->rcount = 0;
@@ -1038,8 +1039,8 @@ static void slip_unesc6(struct slip *sl, unsigned char s)
clear_bit(SLF_KEEPTEST, &sl->flags);
#endif
- if (!test_and_clear_bit(SLF_ERROR, &sl->flags)
- && (sl->rcount > 2))
+ if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
+ (sl->rcount > 2))
sl_bump(sl);
sl->rcount = 0;
sl->xbits = 0;
@@ -1169,6 +1170,27 @@ static int slip_ioctl(struct tty_struct *tty, struct file *file,
}
}
+#ifdef CONFIG_COMPAT
+static long slip_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case SIOCGIFNAME:
+ case SIOCGIFENCAP:
+ case SIOCSIFENCAP:
+ case SIOCSIFHWADDR:
+ case SIOCSKEEPALIVE:
+ case SIOCGKEEPALIVE:
+ case SIOCSOUTFILL:
+ case SIOCGOUTFILL:
+ return slip_ioctl(tty, file, cmd,
+ (unsigned long)compat_ptr(arg));
+ }
+
+ return -ENOIOCTLCMD;
+}
+#endif
+
/* VSV changes start here */
#ifdef CONFIG_SLIP_SMART
/* function do_ioctl called from net/core/dev.c
@@ -1261,6 +1283,9 @@ static struct tty_ldisc_ops sl_ldisc = {
.close = slip_close,
.hangup = slip_hangup,
.ioctl = slip_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = slip_compat_ioctl,
+#endif
.receive_buf = slip_receive_buf,
.write_wakeup = slip_write_wakeup,
};
diff --git a/drivers/net/smc-mca.c b/drivers/net/smc-mca.c
index c791ef76c1d..a93f122e9a9 100644
--- a/drivers/net/smc-mca.c
+++ b/drivers/net/smc-mca.c
@@ -268,9 +268,9 @@ static int __init ultramca_probe(struct device *gen_dev)
}
}
- if(!tirq || !tbase
- || (irq && irq != tirq)
- || (base_addr && tbase != base_addr))
+ if(!tirq || !tbase ||
+ (irq && irq != tirq) ||
+ (base_addr && tbase != base_addr))
/* FIXME: we're trying to force the ordering of the
* devices here, there should be a way of getting this
* to happen */
diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c
index 2a6b6de9533..44ebbaa7457 100644
--- a/drivers/net/smc911x.c
+++ b/drivers/net/smc911x.c
@@ -1984,7 +1984,7 @@ static int __devinit smc911x_probe(struct net_device *dev)
#endif
/* Grab the IRQ */
- retval = request_irq(dev->irq, &smc911x_interrupt,
+ retval = request_irq(dev->irq, smc911x_interrupt,
irq_flags, dev->name, dev);
if (retval)
goto err_out;
diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c
index 934a1201282..8371b82323a 100644
--- a/drivers/net/smc9194.c
+++ b/drivers/net/smc9194.c
@@ -1050,7 +1050,7 @@ static int __init smc_probe(struct net_device *dev, int ioaddr)
memset(netdev_priv(dev), 0, sizeof(struct smc_local));
/* Grab the IRQ */
- retval = request_irq(dev->irq, &smc_interrupt, 0, DRV_NAME, dev);
+ retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
if (retval) {
printk("%s: unable to get IRQ %d (irqval=%d).\n", DRV_NAME,
dev->irq, retval);
diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c
index f12206bdbb7..ae4983a5127 100644
--- a/drivers/net/smc91x.c
+++ b/drivers/net/smc91x.c
@@ -2031,7 +2031,7 @@ static int __devinit smc_probe(struct net_device *dev, void __iomem *ioaddr,
}
/* Grab the IRQ */
- retval = request_irq(dev->irq, &smc_interrupt, irq_flags, dev->name, dev);
+ retval = request_irq(dev->irq, smc_interrupt, irq_flags, dev->name, dev);
if (retval)
goto err_out;
@@ -2365,9 +2365,10 @@ static int __devexit smc_drv_remove(struct platform_device *pdev)
return 0;
}
-static int smc_drv_suspend(struct platform_device *dev, pm_message_t state)
+static int smc_drv_suspend(struct device *dev)
{
- struct net_device *ndev = platform_get_drvdata(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
if (ndev) {
if (netif_running(ndev)) {
@@ -2379,9 +2380,10 @@ static int smc_drv_suspend(struct platform_device *dev, pm_message_t state)
return 0;
}
-static int smc_drv_resume(struct platform_device *dev)
+static int smc_drv_resume(struct device *dev)
{
- struct net_device *ndev = platform_get_drvdata(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
if (ndev) {
struct smc_local *lp = netdev_priv(ndev);
@@ -2397,14 +2399,18 @@ static int smc_drv_resume(struct platform_device *dev)
return 0;
}
+static struct dev_pm_ops smc_drv_pm_ops = {
+ .suspend = smc_drv_suspend,
+ .resume = smc_drv_resume,
+};
+
static struct platform_driver smc_driver = {
.probe = smc_drv_probe,
.remove = __devexit_p(smc_drv_remove),
- .suspend = smc_drv_suspend,
- .resume = smc_drv_resume,
.driver = {
.name = CARDNAME,
.owner = THIS_MODULE,
+ .pm = &smc_drv_pm_ops,
},
};
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 3911be7c0cb..7815bfc300f 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -158,8 +158,8 @@ static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
#define SMC_IRQ_FLAGS (-1) /* from resource */
-#elif defined(CONFIG_MACH_LOGICPD_PXA270) \
- || defined(CONFIG_MACH_NOMADIK_8815NHK)
+#elif defined(CONFIG_MACH_LOGICPD_PXA270) || \
+ defined(CONFIG_MACH_NOMADIK_8815NHK)
#define SMC_CAN_USE_8BIT 0
#define SMC_CAN_USE_16BIT 1
@@ -258,9 +258,9 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg)
#define RPC_LSA_DEFAULT RPC_LED_TX_RX
#define RPC_LSB_DEFAULT RPC_LED_100_10
-#elif defined(CONFIG_MACH_LPD79520) \
- || defined(CONFIG_MACH_LPD7A400) \
- || defined(CONFIG_MACH_LPD7A404)
+#elif defined(CONFIG_MACH_LPD79520) || \
+ defined(CONFIG_MACH_LPD7A400) || \
+ defined(CONFIG_MACH_LPD7A404)
/* The LPD7X_IOBARRIER is necessary to overcome a mismatch between the
* way that the CPU handles chip selects and the way that the SMC chip
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
index f9cdcbcb77d..20d6095cf41 100644
--- a/drivers/net/smsc911x.c
+++ b/drivers/net/smsc911x.c
@@ -748,8 +748,8 @@ static void smsc911x_phy_adjust_link(struct net_device *dev)
* usage is 10/100 indicator */
pdata->gpio_setting = smsc911x_reg_read(pdata,
GPIO_CFG);
- if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_)
- && (!pdata->using_extphy)) {
+ if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
/* Force 10/100 LED off, after saving
* orginal GPIO configuration */
pdata->gpio_orig_setting = pdata->gpio_setting;
@@ -816,7 +816,7 @@ static int smsc911x_mii_probe(struct net_device *dev)
SMSC_TRACE(HW, "Passed Loop Back Test");
#endif /* USE_PHY_WORK_AROUND */
- SMSC_TRACE(HW, "phy initialised succesfully");
+ SMSC_TRACE(HW, "phy initialised successfully");
return 0;
}
@@ -2071,6 +2071,9 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
if (is_valid_ether_addr(dev->dev_addr)) {
smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
SMSC_TRACE(PROBE, "MAC Address is specified by configuration");
+ } else if (is_valid_ether_addr(pdata->config.mac)) {
+ memcpy(dev->dev_addr, pdata->config.mac, 6);
+ SMSC_TRACE(PROBE, "MAC Address specified by platform data");
} else {
/* Try reading mac address from device. if EEPROM is present
* it will already have been set */
diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h
index b5716bd8a59..016360c65ce 100644
--- a/drivers/net/smsc911x.h
+++ b/drivers/net/smsc911x.h
@@ -30,7 +30,7 @@
#define SMSC_NAPI_WEIGHT 16
/* implements a PHY loopback test at initialisation time, to ensure a packet
- * can be succesfully looped back */
+ * can be successfully looped back */
#define USE_PHY_WORK_AROUND
#define DPRINTK(nlevel, klevel, fmt, args...) \
diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c
index 0f790927623..12f0f5d74e3 100644
--- a/drivers/net/smsc9420.c
+++ b/drivers/net/smsc9420.c
@@ -1175,7 +1175,7 @@ static int smsc9420_mii_probe(struct net_device *dev)
phydev->phy_id);
phydev = phy_connect(dev, dev_name(&phydev->dev),
- &smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+ smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
pr_err("%s: Could not attach to PHY\n", dev->name);
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index 90e663f4515..218524857bf 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -57,6 +57,7 @@ MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com> and Jens Osterkamp " \
MODULE_DESCRIPTION("Spider Southbridge Gigabit Ethernet driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
+MODULE_FIRMWARE(SPIDER_NET_FIRMWARE_NAME);
static int rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_DEFAULT;
static int tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_DEFAULT;
@@ -409,7 +410,7 @@ spider_net_free_rx_chain_contents(struct spider_net_card *card)
* @card: card structure
* @descr: descriptor to re-init
*
- * Return 0 on succes, <0 on failure.
+ * Return 0 on success, <0 on failure.
*
* Allocates a new rx skb, iommu-maps it and attaches it to the
* descriptor. Mark the descriptor as activated, ready-to-use.
diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c
index a36e2b51e88..95db60adde4 100644
--- a/drivers/net/starfire.c
+++ b/drivers/net/starfire.c
@@ -928,7 +928,7 @@ static int netdev_open(struct net_device *dev)
/* Do we ever need to reset the chip??? */
- retval = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
+ retval = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev);
if (retval)
return retval;
@@ -1482,8 +1482,8 @@ static int __netdev_rx(struct net_device *dev, int *quota)
printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d, quota %d.\n", pkt_len, *quota);
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(np->pci_dev,
np->rx_info[entry].mapping,
@@ -1793,8 +1793,8 @@ static void set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
rx_mode |= AcceptAll;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to match, or accept all multicasts. */
rx_mode |= AcceptBroadcast|AcceptAllMulticast|PerfectFilter;
} else if (dev->mc_count <= 14) {
diff --git a/drivers/net/stmmac/gmac.c b/drivers/net/stmmac/gmac.c
index b624bb5bae0..52586ee6895 100644
--- a/drivers/net/stmmac/gmac.c
+++ b/drivers/net/stmmac/gmac.c
@@ -112,7 +112,7 @@ static void gmac_dma_operation_mode(unsigned long ioaddr, int txmode,
" (threshold = %d)\n", txmode);
csr6 &= ~DMA_CONTROL_TSF;
csr6 &= DMA_CONTROL_TC_TX_MASK;
- /* Set the transmit threashold */
+ /* Set the transmit threshold */
if (txmode <= 32)
csr6 |= DMA_CONTROL_TTC_32;
else if (txmode <= 64)
diff --git a/drivers/net/stmmac/gmac.h b/drivers/net/stmmac/gmac.h
index 684a363120a..2e82d6c9a14 100644
--- a/drivers/net/stmmac/gmac.h
+++ b/drivers/net/stmmac/gmac.h
@@ -154,14 +154,14 @@ enum rx_tx_priority_ratio {
#define DMA_CONTROL_DT 0x04000000 /* Disable Drop TCP/IP csum error */
#define DMA_CONTROL_RSF 0x02000000 /* Receive Store and Forward */
#define DMA_CONTROL_DFF 0x01000000 /* Disaable flushing */
-/* Theshold for Activating the FC */
+/* Threshold for Activating the FC */
enum rfa {
act_full_minus_1 = 0x00800000,
act_full_minus_2 = 0x00800200,
act_full_minus_3 = 0x00800400,
act_full_minus_4 = 0x00800600,
};
-/* Theshold for Deactivating the FC */
+/* Threshold for Deactivating the FC */
enum rfd {
deac_full_minus_1 = 0x00400000,
deac_full_minus_2 = 0x00400800,
diff --git a/drivers/net/stmmac/stmmac_main.c b/drivers/net/stmmac/stmmac_main.c
index 9542995ba66..508fba8fa07 100644
--- a/drivers/net/stmmac/stmmac_main.c
+++ b/drivers/net/stmmac/stmmac_main.c
@@ -922,8 +922,7 @@ static void stmmac_dma_interrupt(struct net_device *dev)
DBG(intr, INFO, "CSR5[15] DMA ABNORMAL IRQ: ");
if (unlikely(intr_status & DMA_STATUS_UNF)) {
DBG(intr, INFO, "transmit underflow\n");
- if (unlikely(tc != SF_DMA_MODE)
- && (tc <= 256)) {
+ if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) {
/* Try to bump up the threshold */
tc += 64;
priv->mac_type->ops->dma_mode(ioaddr, tc,
@@ -1024,7 +1023,7 @@ static int stmmac_open(struct net_device *dev)
}
/* Request the IRQ lines */
- ret = request_irq(dev->irq, &stmmac_interrupt,
+ ret = request_irq(dev->irq, stmmac_interrupt,
IRQF_SHARED, dev->name, dev);
if (unlikely(ret < 0)) {
pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c
index 2f1eaaf7a72..b447a871942 100644
--- a/drivers/net/sun3_82586.c
+++ b/drivers/net/sun3_82586.c
@@ -191,7 +191,7 @@ static int sun3_82586_open(struct net_device *dev)
startrecv586(dev);
sun3_enaint();
- ret = request_irq(dev->irq, &sun3_82586_interrupt,0,dev->name,dev);
+ ret = request_irq(dev->irq, sun3_82586_interrupt,0,dev->name,dev);
if (ret)
{
sun3_reset586();
diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c
index 536cf7e06bf..25e81ebd9cd 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/sunbmac.c
@@ -919,7 +919,7 @@ static int bigmac_open(struct net_device *dev)
struct bigmac *bp = netdev_priv(dev);
int ret;
- ret = request_irq(dev->irq, &bigmac_interrupt, IRQF_SHARED, dev->name, bp);
+ ret = request_irq(dev->irq, bigmac_interrupt, IRQF_SHARED, dev->name, bp);
if (ret) {
printk(KERN_ERR "BIGMAC: Can't order irq %d to go.\n", dev->irq);
return ret;
diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c
index e13685a570f..d58e1891ca6 100644
--- a/drivers/net/sundance.c
+++ b/drivers/net/sundance.c
@@ -603,8 +603,8 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev,
strcmp (media[card_idx], "4") == 0) {
np->speed = 100;
np->mii_if.full_duplex = 1;
- } else if (strcmp (media[card_idx], "100mbps_hd") == 0
- || strcmp (media[card_idx], "3") == 0) {
+ } else if (strcmp (media[card_idx], "100mbps_hd") == 0 ||
+ strcmp (media[card_idx], "3") == 0) {
np->speed = 100;
np->mii_if.full_duplex = 0;
} else if (strcmp (media[card_idx], "10mbps_fd") == 0 ||
@@ -819,7 +819,7 @@ static int netdev_open(struct net_device *dev)
/* Do we need to reset the chip??? */
- i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
+ i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev);
if (i)
return i;
@@ -1079,8 +1079,8 @@ start_tx (struct sk_buff *skb, struct net_device *dev)
tasklet_schedule(&np->tx_tasklet);
/* On some architectures: explicitly flush cache lines here. */
- if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1
- && !netif_queue_stopped(dev)) {
+ if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1 &&
+ !netif_queue_stopped(dev)) {
/* do nothing */
} else {
netif_stop_queue (dev);
@@ -1336,8 +1336,8 @@ static void rx_poll(unsigned long data)
#endif
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(np->pci_dev,
le32_to_cpu(desc->frag[0].addr),
@@ -1517,8 +1517,8 @@ static void set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
memset(mc_filter, 0xff, sizeof(mc_filter));
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to match, or accept all multicasts. */
memset(mc_filter, 0xff, sizeof(mc_filter));
rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index 61640b99b70..b571a1babab 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -1034,10 +1034,8 @@ static netdev_tx_t gem_start_xmit(struct sk_buff *skb,
(csum_stuff_off << 21));
}
- local_irq_save(flags);
- if (!spin_trylock(&gp->tx_lock)) {
+ if (!spin_trylock_irqsave(&gp->tx_lock, flags)) {
/* Tell upper layer to requeue */
- local_irq_restore(flags);
return NETDEV_TX_LOCKED;
}
/* We raced with gem_do_stop() */
diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h
index f7a02917ce5..19905460def 100644
--- a/drivers/net/sungem.h
+++ b/drivers/net/sungem.h
@@ -1031,8 +1031,8 @@ struct gem {
#endif
};
-#define found_mii_phy(gp) ((gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) \
- && gp->phy_mii.def && gp->phy_mii.def->ops)
+#define found_mii_phy(gp) ((gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) && \
+ gp->phy_mii.def && gp->phy_mii.def->ops)
#define ALIGNED_RX_SKB_ADDR(addr) \
((((unsigned long)(addr) + (64UL - 1UL)) & ~(64UL - 1UL)) - (unsigned long)(addr))
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 37d721bbdb3..6762f1c6ec8 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -1226,10 +1226,16 @@ static void happy_meal_clean_rings(struct happy_meal *hp)
for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
txd = &hp->happy_block->happy_meal_txd[i];
dma_addr = hme_read_desc32(hp, &txd->tx_addr);
- dma_unmap_single(hp->dma_dev, dma_addr,
- (hme_read_desc32(hp, &txd->tx_flags)
- & TXFLAG_SIZE),
- DMA_TO_DEVICE);
+ if (!frag)
+ dma_unmap_single(hp->dma_dev, dma_addr,
+ (hme_read_desc32(hp, &txd->tx_flags)
+ & TXFLAG_SIZE),
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_page(hp->dma_dev, dma_addr,
+ (hme_read_desc32(hp, &txd->tx_flags)
+ & TXFLAG_SIZE),
+ DMA_TO_DEVICE);
if (frag != skb_shinfo(skb)->nr_frags)
i++;
@@ -1953,7 +1959,10 @@ static void happy_meal_tx(struct happy_meal *hp)
dma_len = hme_read_desc32(hp, &this->tx_flags);
dma_len &= TXFLAG_SIZE;
- dma_unmap_single(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE);
+ if (!frag)
+ dma_unmap_single(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE);
+ else
+ dma_unmap_page(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE);
elem = NEXT_TX(elem);
this = &txbase[elem];
@@ -2184,7 +2193,7 @@ static int happy_meal_open(struct net_device *dev)
* into a single source which we register handling at probe time.
*/
if ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO) {
- if (request_irq(dev->irq, &happy_meal_interrupt,
+ if (request_irq(dev->irq, happy_meal_interrupt,
IRQF_SHARED, dev->name, (void *)dev)) {
HMD(("EAGAIN\n"));
printk(KERN_ERR "happy_meal(SBUS): Can't order irq %d to go.\n",
@@ -3047,9 +3056,9 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev,
int len;
if (qfe_slot != -1 &&
- (addr = of_get_property(dp,
- "local-mac-address", &len)) != NULL
- && len == 6) {
+ (addr = of_get_property(dp, "local-mac-address", &len))
+ != NULL &&
+ len == 6) {
memcpy(dev->dev_addr, addr, 6);
} else {
memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index 9d6fd4760ea..64e7d08c878 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -923,7 +923,7 @@ static int lance_open(struct net_device *dev)
STOP_LANCE(lp);
- if (request_irq(dev->irq, &lance_interrupt, IRQF_SHARED,
+ if (request_irq(dev->irq, lance_interrupt, IRQF_SHARED,
lancestr, (void *) dev)) {
printk(KERN_ERR "Lance: Can't get irq %d\n", dev->irq);
return -EAGAIN;
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index dcefb608a9f..45c383f285e 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -807,7 +807,7 @@ static struct sunqec * __devinit get_qec(struct of_device *child)
qec_init_once(qecp, op);
- if (request_irq(op->irqs[0], &qec_interrupt,
+ if (request_irq(op->irqs[0], qec_interrupt,
IRQF_SHARED, "qec", (void *) qecp)) {
printk(KERN_ERR "qec: Can't register irq.\n");
goto fail;
diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c
index d1298e5b72c..75a669d48e5 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/tc35815.c
@@ -22,11 +22,7 @@
* All Rights Reserved.
*/
-#ifdef TC35815_NAPI
-#define DRV_VERSION "1.38-NAPI"
-#else
-#define DRV_VERSION "1.38"
-#endif
+#define DRV_VERSION "1.39"
static const char *version = "tc35815.c:v" DRV_VERSION "\n";
#define MODNAME "tc35815"
@@ -54,13 +50,6 @@ static const char *version = "tc35815.c:v" DRV_VERSION "\n";
#include <asm/io.h>
#include <asm/byteorder.h>
-/* First, a few definitions that the brave might change. */
-
-#define GATHER_TXINT /* On-Demand Tx Interrupt */
-#define WORKAROUND_LOSTCAR
-#define WORKAROUND_100HALF_PROMISC
-/* #define TC35815_USE_PACKEDBUFFER */
-
enum tc35815_chiptype {
TC35815CF = 0,
TC35815_NWU,
@@ -330,17 +319,10 @@ struct BDesc {
/* Some useful constants. */
-#undef NO_CHECK_CARRIER /* Does not check No-Carrier with TP */
-#ifdef NO_CHECK_CARRIER
-#define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \
- Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \
- Tx_En) /* maybe 0x7b01 */
-#else
-#define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \
+#define TX_CTL_CMD (Tx_EnTxPar | Tx_EnLateColl | \
Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \
Tx_En) /* maybe 0x7b01 */
-#endif
/* Do not use Rx_StripCRC -- it causes trouble on BLEx/FDAEx condition */
#define RX_CTL_CMD (Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \
| Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn) /* maybe 0x6f01 */
@@ -361,13 +343,6 @@ struct BDesc {
#define TX_THRESHOLD_KEEP_LIMIT 10
/* 16 + RX_BUF_NUM * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*FD_PAGE_NUM */
-#ifdef TC35815_USE_PACKEDBUFFER
-#define FD_PAGE_NUM 2
-#define RX_BUF_NUM 8 /* >= 2 */
-#define RX_FD_NUM 250 /* >= 32 */
-#define TX_FD_NUM 128
-#define RX_BUF_SIZE PAGE_SIZE
-#else /* TC35815_USE_PACKEDBUFFER */
#define FD_PAGE_NUM 4
#define RX_BUF_NUM 128 /* < 256 */
#define RX_FD_NUM 256 /* >= 32 */
@@ -381,7 +356,6 @@ struct BDesc {
#define RX_BUF_SIZE \
L1_CACHE_ALIGN(ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN + NET_IP_ALIGN)
#endif
-#endif /* TC35815_USE_PACKEDBUFFER */
#define RX_FD_RESERVE (2 / 2) /* max 2 BD per RxFD */
#define NAPI_WEIGHT 16
@@ -439,11 +413,7 @@ struct tc35815_local {
/*
* Transmitting: Batch Mode.
* 1 BD in 1 TxFD.
- * Receiving: Packing Mode. (TC35815_USE_PACKEDBUFFER)
- * 1 circular FD for Free Buffer List.
- * RX_BUF_NUM BD in Free Buffer FD.
- * One Free Buffer BD has PAGE_SIZE data buffer.
- * Or Non-Packing Mode.
+ * Receiving: Non-Packing Mode.
* 1 circular FD for Free Buffer List.
* RX_BUF_NUM BD in Free Buffer FD.
* One Free Buffer BD has ETH_FRAME_LEN data buffer.
@@ -457,21 +427,11 @@ struct tc35815_local {
struct RxFD *rfd_limit;
struct RxFD *rfd_cur;
struct FrFD *fbl_ptr;
-#ifdef TC35815_USE_PACKEDBUFFER
- unsigned char fbl_curid;
- void *data_buf[RX_BUF_NUM]; /* packing */
- dma_addr_t data_buf_dma[RX_BUF_NUM];
- struct {
- struct sk_buff *skb;
- dma_addr_t skb_dma;
- } tx_skbs[TX_FD_NUM];
-#else
unsigned int fbl_count;
struct {
struct sk_buff *skb;
dma_addr_t skb_dma;
} tx_skbs[TX_FD_NUM], rx_skbs[RX_BUF_NUM];
-#endif
u32 msg_enable;
enum tc35815_chiptype chiptype;
};
@@ -486,51 +446,6 @@ static inline void *fd_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus)
return (void *)((u8 *)lp->fd_buf + (bus - lp->fd_buf_dma));
}
#endif
-#ifdef TC35815_USE_PACKEDBUFFER
-static inline void *rxbuf_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus)
-{
- int i;
- for (i = 0; i < RX_BUF_NUM; i++) {
- if (bus >= lp->data_buf_dma[i] &&
- bus < lp->data_buf_dma[i] + PAGE_SIZE)
- return (void *)((u8 *)lp->data_buf[i] +
- (bus - lp->data_buf_dma[i]));
- }
- return NULL;
-}
-
-#define TC35815_DMA_SYNC_ONDEMAND
-static void *alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle)
-{
-#ifdef TC35815_DMA_SYNC_ONDEMAND
- void *buf;
- /* pci_map + pci_dma_sync will be more effective than
- * pci_alloc_consistent on some archs. */
- buf = (void *)__get_free_page(GFP_ATOMIC);
- if (!buf)
- return NULL;
- *dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE,
- PCI_DMA_FROMDEVICE);
- if (pci_dma_mapping_error(hwdev, *dma_handle)) {
- free_page((unsigned long)buf);
- return NULL;
- }
- return buf;
-#else
- return pci_alloc_consistent(hwdev, PAGE_SIZE, dma_handle);
-#endif
-}
-
-static void free_rxbuf_page(struct pci_dev *hwdev, void *buf, dma_addr_t dma_handle)
-{
-#ifdef TC35815_DMA_SYNC_ONDEMAND
- pci_unmap_single(hwdev, dma_handle, PAGE_SIZE, PCI_DMA_FROMDEVICE);
- free_page((unsigned long)buf);
-#else
- pci_free_consistent(hwdev, PAGE_SIZE, buf, dma_handle);
-#endif
-}
-#else /* TC35815_USE_PACKEDBUFFER */
static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev,
struct pci_dev *hwdev,
dma_addr_t *dma_handle)
@@ -555,19 +470,14 @@ static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_
PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
}
-#endif /* TC35815_USE_PACKEDBUFFER */
/* Index to functions, as function prototypes. */
static int tc35815_open(struct net_device *dev);
static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t tc35815_interrupt(int irq, void *dev_id);
-#ifdef TC35815_NAPI
static int tc35815_rx(struct net_device *dev, int limit);
static int tc35815_poll(struct napi_struct *napi, int budget);
-#else
-static void tc35815_rx(struct net_device *dev);
-#endif
static void tc35815_txdone(struct net_device *dev);
static int tc35815_close(struct net_device *dev);
static struct net_device_stats *tc35815_get_stats(struct net_device *dev);
@@ -654,8 +564,6 @@ static void tc_handle_link_change(struct net_device *dev)
* TX4939 PCFG.SPEEDn bit will be changed on
* NETDEV_CHANGE event.
*/
-
-#if !defined(NO_CHECK_CARRIER) && defined(WORKAROUND_LOSTCAR)
/*
* WORKAROUND: enable LostCrS only if half duplex
* operation.
@@ -665,7 +573,6 @@ static void tc_handle_link_change(struct net_device *dev)
lp->chiptype != TC35815_TX4939)
tc_writel(tc_readl(&tr->Tx_Ctl) | Tx_EnLCarr,
&tr->Tx_Ctl);
-#endif
lp->speed = phydev->speed;
lp->duplex = phydev->duplex;
@@ -674,11 +581,9 @@ static void tc_handle_link_change(struct net_device *dev)
if (phydev->link != lp->link) {
if (phydev->link) {
-#ifdef WORKAROUND_100HALF_PROMISC
/* delayed promiscuous enabling */
if (dev->flags & IFF_PROMISC)
tc35815_set_multicast_list(dev);
-#endif
} else {
lp->speed = 0;
lp->duplex = -1;
@@ -923,9 +828,7 @@ static int __devinit tc35815_init_one(struct pci_dev *pdev,
dev->netdev_ops = &tc35815_netdev_ops;
dev->ethtool_ops = &tc35815_ethtool_ops;
dev->watchdog_timeo = TC35815_TX_TIMEOUT;
-#ifdef TC35815_NAPI
netif_napi_add(dev, &lp->napi, tc35815_poll, NAPI_WEIGHT);
-#endif
dev->irq = pdev->irq;
dev->base_addr = (unsigned long)ioaddr;
@@ -1007,25 +910,6 @@ tc35815_init_queues(struct net_device *dev)
if (!lp->fd_buf)
return -ENOMEM;
for (i = 0; i < RX_BUF_NUM; i++) {
-#ifdef TC35815_USE_PACKEDBUFFER
- lp->data_buf[i] =
- alloc_rxbuf_page(lp->pci_dev,
- &lp->data_buf_dma[i]);
- if (!lp->data_buf[i]) {
- while (--i >= 0) {
- free_rxbuf_page(lp->pci_dev,
- lp->data_buf[i],
- lp->data_buf_dma[i]);
- lp->data_buf[i] = NULL;
- }
- pci_free_consistent(lp->pci_dev,
- PAGE_SIZE * FD_PAGE_NUM,
- lp->fd_buf,
- lp->fd_buf_dma);
- lp->fd_buf = NULL;
- return -ENOMEM;
- }
-#else
lp->rx_skbs[i].skb =
alloc_rxbuf_skb(dev, lp->pci_dev,
&lp->rx_skbs[i].skb_dma);
@@ -1043,15 +927,9 @@ tc35815_init_queues(struct net_device *dev)
lp->fd_buf = NULL;
return -ENOMEM;
}
-#endif
}
printk(KERN_DEBUG "%s: FD buf %p DataBuf",
dev->name, lp->fd_buf);
-#ifdef TC35815_USE_PACKEDBUFFER
- printk(" DataBuf");
- for (i = 0; i < RX_BUF_NUM; i++)
- printk(" %p", lp->data_buf[i]);
-#endif
printk("\n");
} else {
for (i = 0; i < FD_PAGE_NUM; i++)
@@ -1084,7 +962,6 @@ tc35815_init_queues(struct net_device *dev)
lp->fbl_ptr = (struct FrFD *)fd_addr;
lp->fbl_ptr->fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, lp->fbl_ptr));
lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_NUM | FD_CownsFD);
-#ifndef TC35815_USE_PACKEDBUFFER
/*
* move all allocated skbs to head of rx_skbs[] array.
* fbl_count mighe not be RX_BUF_NUM if alloc_rxbuf_skb() in
@@ -1102,11 +979,7 @@ tc35815_init_queues(struct net_device *dev)
lp->fbl_count++;
}
}
-#endif
for (i = 0; i < RX_BUF_NUM; i++) {
-#ifdef TC35815_USE_PACKEDBUFFER
- lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(lp->data_buf_dma[i]);
-#else
if (i >= lp->fbl_count) {
lp->fbl_ptr->bd[i].BuffData = 0;
lp->fbl_ptr->bd[i].BDCtl = 0;
@@ -1114,15 +987,11 @@ tc35815_init_queues(struct net_device *dev)
}
lp->fbl_ptr->bd[i].BuffData =
cpu_to_le32(lp->rx_skbs[i].skb_dma);
-#endif
/* BDID is index of FrFD.bd[] */
lp->fbl_ptr->bd[i].BDCtl =
cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) |
RX_BUF_SIZE);
}
-#ifdef TC35815_USE_PACKEDBUFFER
- lp->fbl_curid = 0;
-#endif
printk(KERN_DEBUG "%s: TxFD %p RxFD %p FrFD %p\n",
dev->name, lp->tfd_base, lp->rfd_base, lp->fbl_ptr);
@@ -1196,19 +1065,11 @@ tc35815_free_queues(struct net_device *dev)
lp->fbl_ptr = NULL;
for (i = 0; i < RX_BUF_NUM; i++) {
-#ifdef TC35815_USE_PACKEDBUFFER
- if (lp->data_buf[i]) {
- free_rxbuf_page(lp->pci_dev,
- lp->data_buf[i], lp->data_buf_dma[i]);
- lp->data_buf[i] = NULL;
- }
-#else
if (lp->rx_skbs[i].skb) {
free_rxbuf_skb(lp->pci_dev, lp->rx_skbs[i].skb,
lp->rx_skbs[i].skb_dma);
lp->rx_skbs[i].skb = NULL;
}
-#endif
}
if (lp->fd_buf) {
pci_free_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM,
@@ -1254,7 +1115,7 @@ dump_rxfd(struct RxFD *fd)
return bd_count;
}
-#if defined(DEBUG) || defined(TC35815_USE_PACKEDBUFFER)
+#ifdef DEBUG
static void
dump_frfd(struct FrFD *fd)
{
@@ -1271,9 +1132,7 @@ dump_frfd(struct FrFD *fd)
le32_to_cpu(fd->bd[i].BDCtl));
printk("\n");
}
-#endif
-#ifdef DEBUG
static void
panic_queues(struct net_device *dev)
{
@@ -1389,7 +1248,7 @@ tc35815_open(struct net_device *dev)
* This is used if the interrupt line can turned off (shared).
* See 3c503.c for an example of selecting the IRQ at config-time.
*/
- if (request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED,
+ if (request_irq(dev->irq, tc35815_interrupt, IRQF_SHARED,
dev->name, dev))
return -EAGAIN;
@@ -1400,9 +1259,7 @@ tc35815_open(struct net_device *dev)
return -EAGAIN;
}
-#ifdef TC35815_NAPI
napi_enable(&lp->napi);
-#endif
/* Reset the hardware here. Don't forget to set the station address. */
spin_lock_irq(&lp->lock);
@@ -1478,9 +1335,7 @@ static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev)
(struct tc35815_regs __iomem *)dev->base_addr;
/* Start DMA Transmitter. */
txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL);
-#ifdef GATHER_TXINT
txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx);
-#endif
if (netif_msg_tx_queued(lp)) {
printk("%s: starting TxFD.\n", dev->name);
dump_txfd(txfd);
@@ -1536,11 +1391,7 @@ static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status)
tc35815_schedule_restart(dev);
}
-#ifdef TC35815_NAPI
static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit)
-#else
-static int tc35815_do_interrupt(struct net_device *dev, u32 status)
-#endif
{
struct tc35815_local *lp = netdev_priv(dev);
int ret = -1;
@@ -1579,12 +1430,7 @@ static int tc35815_do_interrupt(struct net_device *dev, u32 status)
/* normal notification */
if (status & Int_IntMacRx) {
/* Got a packet(s). */
-#ifdef TC35815_NAPI
ret = tc35815_rx(dev, limit);
-#else
- tc35815_rx(dev);
- ret = 0;
-#endif
lp->lstats.rx_ints++;
}
if (status & Int_IntMacTx) {
@@ -1592,7 +1438,8 @@ static int tc35815_do_interrupt(struct net_device *dev, u32 status)
lp->lstats.tx_ints++;
tc35815_txdone(dev);
netif_wake_queue(dev);
- ret = 0;
+ if (ret < 0)
+ ret = 0;
}
return ret;
}
@@ -1607,7 +1454,6 @@ static irqreturn_t tc35815_interrupt(int irq, void *dev_id)
struct tc35815_local *lp = netdev_priv(dev);
struct tc35815_regs __iomem *tr =
(struct tc35815_regs __iomem *)dev->base_addr;
-#ifdef TC35815_NAPI
u32 dmactl = tc_readl(&tr->DMA_Ctl);
if (!(dmactl & DMA_IntMask)) {
@@ -1624,22 +1470,6 @@ static irqreturn_t tc35815_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
return IRQ_NONE;
-#else
- int handled;
- u32 status;
-
- spin_lock(&lp->lock);
- status = tc_readl(&tr->Int_Src);
- /* BLEx, FDAEx will be cleared later */
- tc_writel(status & ~(Int_BLEx | Int_FDAEx),
- &tr->Int_Src); /* write to clear */
- handled = tc35815_do_interrupt(dev, status);
- if (status & (Int_BLEx | Int_FDAEx))
- tc_writel(status & (Int_BLEx | Int_FDAEx), &tr->Int_Src);
- (void)tc_readl(&tr->Int_Src); /* flush */
- spin_unlock(&lp->lock);
- return IRQ_RETVAL(handled >= 0);
-#endif /* TC35815_NAPI */
}
#ifdef CONFIG_NET_POLL_CONTROLLER
@@ -1652,20 +1482,13 @@ static void tc35815_poll_controller(struct net_device *dev)
#endif
/* We have a good packet(s), get it/them out of the buffers. */
-#ifdef TC35815_NAPI
static int
tc35815_rx(struct net_device *dev, int limit)
-#else
-static void
-tc35815_rx(struct net_device *dev)
-#endif
{
struct tc35815_local *lp = netdev_priv(dev);
unsigned int fdctl;
int i;
-#ifdef TC35815_NAPI
int received = 0;
-#endif
while (!((fdctl = le32_to_cpu(lp->rfd_cur->fd.FDCtl)) & FD_CownsFD)) {
int status = le32_to_cpu(lp->rfd_cur->fd.FDStat);
@@ -1684,52 +1507,9 @@ tc35815_rx(struct net_device *dev)
struct sk_buff *skb;
unsigned char *data;
int cur_bd;
-#ifdef TC35815_USE_PACKEDBUFFER
- int offset;
-#endif
-#ifdef TC35815_NAPI
if (--limit < 0)
break;
-#endif
-#ifdef TC35815_USE_PACKEDBUFFER
- BUG_ON(bd_count > 2);
- skb = dev_alloc_skb(pkt_len + NET_IP_ALIGN);
- if (skb == NULL) {
- printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
- dev->name);
- dev->stats.rx_dropped++;
- break;
- }
- skb_reserve(skb, NET_IP_ALIGN);
-
- data = skb_put(skb, pkt_len);
-
- /* copy from receive buffer */
- cur_bd = 0;
- offset = 0;
- while (offset < pkt_len && cur_bd < bd_count) {
- int len = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BDCtl) &
- BD_BuffLength_MASK;
- dma_addr_t dma = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData);
- void *rxbuf = rxbuf_bus_to_virt(lp, dma);
- if (offset + len > pkt_len)
- len = pkt_len - offset;
-#ifdef TC35815_DMA_SYNC_ONDEMAND
- pci_dma_sync_single_for_cpu(lp->pci_dev,
- dma, len,
- PCI_DMA_FROMDEVICE);
-#endif
- memcpy(data + offset, rxbuf, len);
-#ifdef TC35815_DMA_SYNC_ONDEMAND
- pci_dma_sync_single_for_device(lp->pci_dev,
- dma, len,
- PCI_DMA_FROMDEVICE);
-#endif
- offset += len;
- cur_bd++;
- }
-#else /* TC35815_USE_PACKEDBUFFER */
BUG_ON(bd_count > 1);
cur_bd = (le32_to_cpu(lp->rfd_cur->bd[0].BDCtl)
& BD_RxBDID_MASK) >> BD_RxBDID_SHIFT;
@@ -1757,16 +1537,11 @@ tc35815_rx(struct net_device *dev)
memmove(skb->data, skb->data - NET_IP_ALIGN,
pkt_len);
data = skb_put(skb, pkt_len);
-#endif /* TC35815_USE_PACKEDBUFFER */
if (netif_msg_pktdata(lp))
print_eth(data);
skb->protocol = eth_type_trans(skb, dev);
-#ifdef TC35815_NAPI
netif_receive_skb(skb);
received++;
-#else
- netif_rx(skb);
-#endif
dev->stats.rx_packets++;
dev->stats.rx_bytes += pkt_len;
} else {
@@ -1803,19 +1578,11 @@ tc35815_rx(struct net_device *dev)
BUG_ON(id >= RX_BUF_NUM);
#endif
/* free old buffers */
-#ifdef TC35815_USE_PACKEDBUFFER
- while (lp->fbl_curid != id)
-#else
lp->fbl_count--;
while (lp->fbl_count < RX_BUF_NUM)
-#endif
{
-#ifdef TC35815_USE_PACKEDBUFFER
- unsigned char curid = lp->fbl_curid;
-#else
unsigned char curid =
(id + 1 + lp->fbl_count) % RX_BUF_NUM;
-#endif
struct BDesc *bd = &lp->fbl_ptr->bd[curid];
#ifdef DEBUG
bdctl = le32_to_cpu(bd->BDCtl);
@@ -1826,7 +1593,6 @@ tc35815_rx(struct net_device *dev)
}
#endif
/* pass BD to controller */
-#ifndef TC35815_USE_PACKEDBUFFER
if (!lp->rx_skbs[curid].skb) {
lp->rx_skbs[curid].skb =
alloc_rxbuf_skb(dev,
@@ -1836,21 +1602,11 @@ tc35815_rx(struct net_device *dev)
break; /* try on next reception */
bd->BuffData = cpu_to_le32(lp->rx_skbs[curid].skb_dma);
}
-#endif /* TC35815_USE_PACKEDBUFFER */
/* Note: BDLength was modified by chip. */
bd->BDCtl = cpu_to_le32(BD_CownsBD |
(curid << BD_RxBDID_SHIFT) |
RX_BUF_SIZE);
-#ifdef TC35815_USE_PACKEDBUFFER
- lp->fbl_curid = (curid + 1) % RX_BUF_NUM;
- if (netif_msg_rx_status(lp)) {
- printk("%s: Entering new FBD %d\n",
- dev->name, lp->fbl_curid);
- dump_frfd(lp->fbl_ptr);
- }
-#else
lp->fbl_count++;
-#endif
}
}
@@ -1882,12 +1638,9 @@ tc35815_rx(struct net_device *dev)
#endif
}
-#ifdef TC35815_NAPI
return received;
-#endif
}
-#ifdef TC35815_NAPI
static int tc35815_poll(struct napi_struct *napi, int budget)
{
struct tc35815_local *lp = container_of(napi, struct tc35815_local, napi);
@@ -1924,13 +1677,8 @@ static int tc35815_poll(struct napi_struct *napi, int budget)
}
return received;
}
-#endif
-#ifdef NO_CHECK_CARRIER
-#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_LateColl|Tx_TxPar|Tx_SQErr)
-#else
#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_NCarr|Tx_LateColl|Tx_TxPar|Tx_SQErr)
-#endif
static void
tc35815_check_tx_stat(struct net_device *dev, int status)
@@ -1944,16 +1692,12 @@ tc35815_check_tx_stat(struct net_device *dev, int status)
if (status & Tx_TxColl_MASK)
dev->stats.collisions += status & Tx_TxColl_MASK;
-#ifndef NO_CHECK_CARRIER
/* TX4939 does not have NCarr */
if (lp->chiptype == TC35815_TX4939)
status &= ~Tx_NCarr;
-#ifdef WORKAROUND_LOSTCAR
/* WORKAROUND: ignore LostCrS in full duplex operation */
if (!lp->link || lp->duplex == DUPLEX_FULL)
status &= ~Tx_NCarr;
-#endif
-#endif
if (!(status & TX_STA_ERR)) {
/* no error. */
@@ -1983,12 +1727,10 @@ tc35815_check_tx_stat(struct net_device *dev, int status)
dev->stats.tx_fifo_errors++;
msg = "Excessive Deferral.";
}
-#ifndef NO_CHECK_CARRIER
if (status & Tx_NCarr) {
dev->stats.tx_carrier_errors++;
msg = "Lost Carrier Sense.";
}
-#endif
if (status & Tx_LateColl) {
dev->stats.tx_aborted_errors++;
msg = "Late Collision.";
@@ -2044,11 +1786,7 @@ tc35815_txdone(struct net_device *dev)
pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE);
lp->tx_skbs[lp->tfd_end].skb = NULL;
lp->tx_skbs[lp->tfd_end].skb_dma = 0;
-#ifdef TC35815_NAPI
dev_kfree_skb_any(skb);
-#else
- dev_kfree_skb_irq(skb);
-#endif
}
txfd->fd.FDSystem = cpu_to_le32(0xffffffff);
@@ -2083,9 +1821,7 @@ tc35815_txdone(struct net_device *dev)
/* start DMA Transmitter again */
txhead->fd.FDNext |= cpu_to_le32(FD_Next_EOL);
-#ifdef GATHER_TXINT
txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx);
-#endif
if (netif_msg_tx_queued(lp)) {
printk("%s: start TxFD on queue.\n",
dev->name);
@@ -2112,9 +1848,7 @@ tc35815_close(struct net_device *dev)
struct tc35815_local *lp = netdev_priv(dev);
netif_stop_queue(dev);
-#ifdef TC35815_NAPI
napi_disable(&lp->napi);
-#endif
if (lp->phy_dev)
phy_stop(lp->phy_dev);
cancel_work_sync(&lp->restart_work);
@@ -2198,14 +1932,12 @@ tc35815_set_multicast_list(struct net_device *dev)
(struct tc35815_regs __iomem *)dev->base_addr;
if (dev->flags & IFF_PROMISC) {
-#ifdef WORKAROUND_100HALF_PROMISC
/* With some (all?) 100MHalf HUB, controller will hang
* if we enabled promiscuous mode before linkup... */
struct tc35815_local *lp = netdev_priv(dev);
if (!lp->link)
return;
-#endif
/* Enable promiscuous mode */
tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl);
} else if ((dev->flags & IFF_ALLMULTI) ||
@@ -2392,9 +2124,6 @@ static void tc35815_chip_init(struct net_device *dev)
tc_writel(DMA_BURST_SIZE | DMA_RxAlign_2, &tr->DMA_Ctl);
else
tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl);
-#ifdef TC35815_USE_PACKEDBUFFER
- tc_writel(RxFrag_EnPack | ETH_ZLEN, &tr->RxFragSize); /* Packing */
-#endif
tc_writel(0, &tr->TxPollCtr); /* Batch mode */
tc_writel(TX_THRESHOLD, &tr->TxThrsh);
tc_writel(INT_EN_CMD, &tr->Int_En);
@@ -2412,19 +2141,12 @@ static void tc35815_chip_init(struct net_device *dev)
tc_writel(RX_CTL_CMD, &tr->Rx_Ctl); /* start MAC receiver */
/* start MAC transmitter */
-#ifndef NO_CHECK_CARRIER
/* TX4939 does not have EnLCarr */
if (lp->chiptype == TC35815_TX4939)
txctl &= ~Tx_EnLCarr;
-#ifdef WORKAROUND_LOSTCAR
/* WORKAROUND: ignore LostCrS in full duplex operation */
if (!lp->phy_dev || !lp->link || lp->duplex == DUPLEX_FULL)
txctl &= ~Tx_EnLCarr;
-#endif
-#endif /* !NO_CHECK_CARRIER */
-#ifdef GATHER_TXINT
- txctl &= ~Tx_EnComp; /* disable global tx completion int. */
-#endif
tc_writel(txctl, &tr->Tx_Ctl);
}
diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c
index ec9dfb251f3..80b404f2b93 100644
--- a/drivers/net/tehuti.c
+++ b/drivers/net/tehuti.c
@@ -420,7 +420,7 @@ static int bdx_hw_start(struct bdx_priv *priv)
GMAC_RX_FILTER_AM | GMAC_RX_FILTER_AB);
#define BDX_IRQ_TYPE ((priv->nic->irq_type == IRQ_MSI)?0:IRQF_SHARED)
- if ((rc = request_irq(priv->pdev->irq, &bdx_isr_napi, BDX_IRQ_TYPE,
+ if ((rc = request_irq(priv->pdev->irq, bdx_isr_napi, BDX_IRQ_TYPE,
ndev->name, ndev)))
goto err_irq;
bdx_enable_interrupts(priv);
@@ -1784,9 +1784,9 @@ static void bdx_tx_cleanup(struct bdx_priv *priv)
}
#endif
- if (unlikely(netif_queue_stopped(priv->ndev)
- && netif_carrier_ok(priv->ndev)
- && (priv->tx_level >= BDX_MIN_TX_LEVEL))) {
+ if (unlikely(netif_queue_stopped(priv->ndev) &&
+ netif_carrier_ok(priv->ndev) &&
+ (priv->tx_level >= BDX_MIN_TX_LEVEL))) {
DBG("%s: %s: TX Q WAKE level %d\n",
BDX_DRV_NAME, priv->ndev->name, priv->tx_level);
netif_wake_queue(priv->ndev);
@@ -1878,7 +1878,7 @@ static void bdx_tx_push_desc_safe(struct bdx_priv *priv, void *data, int size)
udelay(50); /* give hw a chance to clean fifo */
continue;
}
- avail = MIN(avail, size);
+ avail = min(avail, size);
DBG("about to push %d bytes starting %p size %d\n", avail,
data, size);
bdx_tx_push_desc(priv, data, avail);
@@ -2105,12 +2105,6 @@ err_pci:
}
/****************** Ethtool interface *********************/
-/* get strings for tests */
-static const char
- bdx_test_names[][ETH_GSTRING_LEN] = {
- "No tests defined"
-};
-
/* get strings for statistics counters */
static const char
bdx_stat_names[][ETH_GSTRING_LEN] = {
@@ -2279,8 +2273,8 @@ bdx_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal)
(((tx_max_coal * BDX_TXF_DESC_SZ) + PCK_TH_MULT - 1)
/ PCK_TH_MULT);
- if ((rx_coal > 0x7FFF) || (tx_coal > 0x7FFF)
- || (rx_max_coal > 0xF) || (tx_max_coal > 0xF))
+ if ((rx_coal > 0x7FFF) || (tx_coal > 0x7FFF) ||
+ (rx_max_coal > 0xF) || (tx_max_coal > 0xF))
return -EINVAL;
rdintcm = INT_REG_VAL(rx_coal, GET_INT_COAL_RC(priv->rdintcm),
@@ -2353,8 +2347,8 @@ bdx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
tx_size = 3;
/*Is there anything to do? */
- if ((rx_size == priv->rxf_size)
- && (tx_size == priv->txd_size))
+ if ((rx_size == priv->rxf_size) &&
+ (tx_size == priv->txd_size))
return 0;
priv->rxf_size = rx_size;
@@ -2380,9 +2374,6 @@ bdx_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
static void bdx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
{
switch (stringset) {
- case ETH_SS_TEST:
- memcpy(data, *bdx_test_names, sizeof(bdx_test_names));
- break;
case ETH_SS_STATS:
memcpy(data, *bdx_stat_names, sizeof(bdx_stat_names));
break;
@@ -2390,15 +2381,21 @@ static void bdx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
}
/*
- * bdx_get_stats_count - return number of 64bit statistics counters
+ * bdx_get_sset_count - return number of statistics or tests
* @netdev
*/
-static int bdx_get_stats_count(struct net_device *netdev)
+static int bdx_get_sset_count(struct net_device *netdev, int stringset)
{
struct bdx_priv *priv = netdev_priv(netdev);
- BDX_ASSERT(ARRAY_SIZE(bdx_stat_names)
- != sizeof(struct bdx_stats) / sizeof(u64));
- return ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0);
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ BDX_ASSERT(ARRAY_SIZE(bdx_stat_names)
+ != sizeof(struct bdx_stats) / sizeof(u64));
+ return ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0);
+ default:
+ return -EINVAL;
+ }
}
/*
@@ -2441,7 +2438,7 @@ static void bdx_ethtool_ops(struct net_device *netdev)
.get_sg = ethtool_op_get_sg,
.get_tso = ethtool_op_get_tso,
.get_strings = bdx_get_strings,
- .get_stats_count = bdx_get_stats_count,
+ .get_sset_count = bdx_get_sset_count,
.get_ethtool_stats = bdx_get_ethtool_stats,
};
diff --git a/drivers/net/tehuti.h b/drivers/net/tehuti.h
index 4fc875e5dcd..124141909e4 100644
--- a/drivers/net/tehuti.h
+++ b/drivers/net/tehuti.h
@@ -76,8 +76,6 @@
#define FIFO_SIZE 4096
#define FIFO_EXTRA_SPACE 1024
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
-
#if BITS_PER_LONG == 64
# define H32_64(x) (u32) ((u64)(x) >> 32)
# define L32_64(x) (u32) ((u64)(x) & 0xffffffff)
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index ba5d3fe753b..3a74d216859 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -68,8 +68,8 @@
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "3.102"
-#define DRV_MODULE_RELDATE "September 1, 2009"
+#define DRV_MODULE_VERSION "3.105"
+#define DRV_MODULE_RELDATE "December 2, 2009"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -137,6 +137,12 @@
#define TG3_RX_STD_MAP_SZ TG3_RX_DMA_TO_MAP_SZ(TG3_RX_STD_DMA_SZ)
#define TG3_RX_JMB_MAP_SZ TG3_RX_DMA_TO_MAP_SZ(TG3_RX_JMB_DMA_SZ)
+#define TG3_RX_STD_BUFF_RING_SIZE \
+ (sizeof(struct ring_info) * TG3_RX_RING_SIZE)
+
+#define TG3_RX_JMB_BUFF_RING_SIZE \
+ (sizeof(struct ring_info) * TG3_RX_JUMBO_RING_SIZE)
+
/* minimum number of free TX descriptors required to wake up TX process */
#define TG3_TX_WAKEUP_THRESH(tnapi) ((tnapi)->tx_pending / 4)
@@ -235,6 +241,9 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57760)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57790)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57788)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5717)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5718)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5724)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)},
{PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)},
@@ -396,7 +405,7 @@ static void tg3_write_indirect_mbox(struct tg3 *tp, u32 off, u32 val)
TG3_64BIT_REG_LOW, val);
return;
}
- if (off == (MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW)) {
+ if (off == TG3_RX_STD_PROD_IDX_REG) {
pci_write_config_dword(tp->pdev, TG3PCI_STD_RING_PROD_IDX +
TG3_64BIT_REG_LOW, val);
return;
@@ -937,9 +946,10 @@ static void tg3_mdio_config_5785(struct tg3 *tp)
u32 val;
struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
case TG3_PHY_ID_BCM50610:
+ case TG3_PHY_ID_BCM50610M:
val = MAC_PHYCFG2_50610_LED_MODES;
break;
case TG3_PHY_ID_BCMAC131:
@@ -1031,7 +1041,7 @@ static void tg3_mdio_start(struct tg3 *tp)
if (is_serdes)
tp->phy_addr += 7;
} else
- tp->phy_addr = PHY_ADDR;
+ tp->phy_addr = TG3_PHY_MII_ADDR;
if ((tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) &&
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
@@ -1062,7 +1072,7 @@ static int tg3_mdio_init(struct tg3 *tp)
tp->mdio_bus->read = &tg3_mdio_read;
tp->mdio_bus->write = &tg3_mdio_write;
tp->mdio_bus->reset = &tg3_mdio_reset;
- tp->mdio_bus->phy_mask = ~(1 << PHY_ADDR);
+ tp->mdio_bus->phy_mask = ~(1 << TG3_PHY_MII_ADDR);
tp->mdio_bus->irq = &tp->mdio_irq[0];
for (i = 0; i < PHY_MAX_ADDR; i++)
@@ -1084,7 +1094,7 @@ static int tg3_mdio_init(struct tg3 *tp)
return i;
}
- phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
if (!phydev || !phydev->drv) {
printk(KERN_WARNING "%s: No PHY devices\n", tp->dev->name);
@@ -1096,8 +1106,14 @@ static int tg3_mdio_init(struct tg3 *tp)
switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
case TG3_PHY_ID_BCM57780:
phydev->interface = PHY_INTERFACE_MODE_GMII;
+ phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE;
break;
case TG3_PHY_ID_BCM50610:
+ case TG3_PHY_ID_BCM50610M:
+ phydev->dev_flags |= PHY_BRCM_CLEAR_RGMII_MODE |
+ PHY_BRCM_RX_REFCLK_UNUSED |
+ PHY_BRCM_DIS_TXCRXC_NOENRGY |
+ PHY_BRCM_AUTO_PWRDWN_ENABLE;
if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE)
phydev->dev_flags |= PHY_BRCM_STD_IBND_DISABLE;
if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN)
@@ -1111,6 +1127,7 @@ static int tg3_mdio_init(struct tg3 *tp)
case TG3_PHY_ID_RTL8201E:
case TG3_PHY_ID_BCMAC131:
phydev->interface = PHY_INTERFACE_MODE_MII;
+ phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE;
tp->tg3_flags3 |= TG3_FLG3_PHY_IS_FET;
break;
}
@@ -1311,7 +1328,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
u32 old_tx_mode = tp->tx_mode;
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB)
- autoneg = tp->mdio_bus->phy_map[PHY_ADDR]->autoneg;
+ autoneg = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]->autoneg;
else
autoneg = tp->link_config.autoneg;
@@ -1348,7 +1365,7 @@ static void tg3_adjust_link(struct net_device *dev)
u8 oldflowctrl, linkmesg = 0;
u32 mac_mode, lcl_adv, rmt_adv;
struct tg3 *tp = netdev_priv(dev);
- struct phy_device *phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ struct phy_device *phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
spin_lock_bh(&tp->lock);
@@ -1363,8 +1380,11 @@ static void tg3_adjust_link(struct net_device *dev)
if (phydev->speed == SPEED_100 || phydev->speed == SPEED_10)
mac_mode |= MAC_MODE_PORT_MODE_MII;
- else
+ else if (phydev->speed == SPEED_1000 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785)
mac_mode |= MAC_MODE_PORT_MODE_GMII;
+ else
+ mac_mode |= MAC_MODE_PORT_MODE_MII;
if (phydev->duplex == DUPLEX_HALF)
mac_mode |= MAC_MODE_HALF_DUPLEX;
@@ -1434,7 +1454,7 @@ static int tg3_phy_init(struct tg3 *tp)
/* Bring the PHY back to a known state. */
tg3_bmcr_reset(tp);
- phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
/* Attach the MAC to the PHY. */
phydev = phy_connect(tp->dev, dev_name(&phydev->dev), tg3_adjust_link,
@@ -1461,7 +1481,7 @@ static int tg3_phy_init(struct tg3 *tp)
SUPPORTED_Asym_Pause);
break;
default:
- phy_disconnect(tp->mdio_bus->phy_map[PHY_ADDR]);
+ phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
return -EINVAL;
}
@@ -1479,7 +1499,7 @@ static void tg3_phy_start(struct tg3 *tp)
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return;
- phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
if (tp->link_config.phy_is_low_power) {
tp->link_config.phy_is_low_power = 0;
@@ -1499,13 +1519,13 @@ static void tg3_phy_stop(struct tg3 *tp)
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return;
- phy_stop(tp->mdio_bus->phy_map[PHY_ADDR]);
+ phy_stop(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
}
static void tg3_phy_fini(struct tg3 *tp)
{
if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) {
- phy_disconnect(tp->mdio_bus->phy_map[PHY_ADDR]);
+ phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
tp->tg3_flags3 &= ~TG3_FLG3_PHY_CONNECTED;
}
}
@@ -2149,6 +2169,26 @@ static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power)
tw32_f(GRC_MISC_CFG, val | GRC_MISC_CFG_EPHY_IDDQ);
udelay(40);
return;
+ } else if (tp->tg3_flags3 & TG3_FLG3_PHY_IS_FET) {
+ u32 phytest;
+ if (!tg3_readphy(tp, MII_TG3_FET_TEST, &phytest)) {
+ u32 phy;
+
+ tg3_writephy(tp, MII_ADVERTISE, 0);
+ tg3_writephy(tp, MII_BMCR,
+ BMCR_ANENABLE | BMCR_ANRESTART);
+
+ tg3_writephy(tp, MII_TG3_FET_TEST,
+ phytest | MII_TG3_FET_SHADOW_EN);
+ if (!tg3_readphy(tp, MII_TG3_FET_SHDW_AUXMODE4, &phy)) {
+ phy |= MII_TG3_FET_SHDW_AUXMODE4_SBPD;
+ tg3_writephy(tp,
+ MII_TG3_FET_SHDW_AUXMODE4,
+ phy);
+ }
+ tg3_writephy(tp, MII_TG3_FET_TEST, phytest);
+ }
+ return;
} else if (do_low_power) {
tg3_writephy(tp, MII_TG3_EXT_CTRL,
MII_TG3_EXT_CTRL_FORCE_LED_OFF);
@@ -2218,7 +2258,7 @@ static void tg3_nvram_unlock(struct tg3 *tp)
static void tg3_enable_nvram_access(struct tg3 *tp)
{
if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) &&
- !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) {
+ !(tp->tg3_flags3 & TG3_FLG3_PROTECTED_NVRAM)) {
u32 nvaccess = tr32(NVRAM_ACCESS);
tw32(NVRAM_ACCESS, nvaccess | ACCESS_ENABLE);
@@ -2229,7 +2269,7 @@ static void tg3_enable_nvram_access(struct tg3 *tp)
static void tg3_disable_nvram_access(struct tg3 *tp)
{
if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) &&
- !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM)) {
+ !(tp->tg3_flags3 & TG3_FLG3_PROTECTED_NVRAM)) {
u32 nvaccess = tr32(NVRAM_ACCESS);
tw32(NVRAM_ACCESS, nvaccess & ~ACCESS_ENABLE);
@@ -2474,7 +2514,7 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
struct phy_device *phydev;
u32 phyid, advertising;
- phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
tp->link_config.phy_is_low_power = 1;
@@ -3243,15 +3283,6 @@ relink:
pci_write_config_word(tp->pdev,
tp->pcie_cap + PCI_EXP_LNKCTL,
newlnkctl);
- } else if (tp->tg3_flags3 & TG3_FLG3_TOGGLE_10_100_L1PLLPD) {
- u32 newreg, oldreg = tr32(TG3_PCIE_LNKCTL);
- if (tp->link_config.active_speed == SPEED_100 ||
- tp->link_config.active_speed == SPEED_10)
- newreg = oldreg & ~TG3_PCIE_LNKCTL_L1_PLL_PD_EN;
- else
- newreg = oldreg | TG3_PCIE_LNKCTL_L1_PLL_PD_EN;
- if (newreg != oldreg)
- tw32(TG3_PCIE_LNKCTL, newreg);
}
if (current_link_up != netif_carrier_ok(tp->dev)) {
@@ -4320,13 +4351,13 @@ static void tg3_tx(struct tg3_napi *tnapi)
struct netdev_queue *txq;
int index = tnapi - tp->napi;
- if (tp->tg3_flags2 & TG3_FLG2_USING_MSIX)
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)
index--;
txq = netdev_get_tx_queue(tp->dev, index);
while (sw_idx != hw_idx) {
- struct tx_ring_info *ri = &tnapi->tx_buffers[sw_idx];
+ struct ring_info *ri = &tnapi->tx_buffers[sw_idx];
struct sk_buff *skb = ri->skb;
int i, tx_bug = 0;
@@ -4335,7 +4366,10 @@ static void tg3_tx(struct tg3_napi *tnapi)
return;
}
- skb_dma_unmap(&tp->pdev->dev, skb, DMA_TO_DEVICE);
+ pci_unmap_single(tp->pdev,
+ pci_unmap_addr(ri, mapping),
+ skb_headlen(skb),
+ PCI_DMA_TODEVICE);
ri->skb = NULL;
@@ -4345,6 +4379,11 @@ static void tg3_tx(struct tg3_napi *tnapi)
ri = &tnapi->tx_buffers[sw_idx];
if (unlikely(ri->skb != NULL || sw_idx == hw_idx))
tx_bug = 1;
+
+ pci_unmap_page(tp->pdev,
+ pci_unmap_addr(ri, mapping),
+ skb_shinfo(skb)->frags[i].size,
+ PCI_DMA_TODEVICE);
sw_idx = NEXT_TX(sw_idx);
}
@@ -4375,6 +4414,17 @@ static void tg3_tx(struct tg3_napi *tnapi)
}
}
+static void tg3_rx_skb_free(struct tg3 *tp, struct ring_info *ri, u32 map_sz)
+{
+ if (!ri->skb)
+ return;
+
+ pci_unmap_single(tp->pdev, pci_unmap_addr(ri, mapping),
+ map_sz, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(ri->skb);
+ ri->skb = NULL;
+}
+
/* Returns size of skb allocated or < 0 on error.
*
* We only need to fill in the address because the other members
@@ -4386,16 +4436,14 @@ static void tg3_tx(struct tg3_napi *tnapi)
* buffers the cpu only reads the last cacheline of the RX descriptor
* (to fetch the error flags, vlan tag, checksum, and opaque cookie).
*/
-static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key,
- int src_idx, u32 dest_idx_unmasked)
+static int tg3_alloc_rx_skb(struct tg3 *tp, struct tg3_rx_prodring_set *tpr,
+ u32 opaque_key, u32 dest_idx_unmasked)
{
- struct tg3 *tp = tnapi->tp;
struct tg3_rx_buffer_desc *desc;
struct ring_info *map, *src_map;
struct sk_buff *skb;
dma_addr_t mapping;
int skb_size, dest_idx;
- struct tg3_rx_prodring_set *tpr = &tp->prodring[0];
src_map = NULL;
switch (opaque_key) {
@@ -4403,8 +4451,6 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key,
dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE;
desc = &tpr->rx_std[dest_idx];
map = &tpr->rx_std_buffers[dest_idx];
- if (src_idx >= 0)
- src_map = &tpr->rx_std_buffers[src_idx];
skb_size = tp->rx_pkt_map_sz;
break;
@@ -4412,8 +4458,6 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key,
dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE;
desc = &tpr->rx_jmb[dest_idx].std;
map = &tpr->rx_jmb_buffers[dest_idx];
- if (src_idx >= 0)
- src_map = &tpr->rx_jmb_buffers[src_idx];
skb_size = TG3_RX_JMB_MAP_SZ;
break;
@@ -4435,13 +4479,14 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key,
mapping = pci_map_single(tp->pdev, skb->data, skb_size,
PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(tp->pdev, mapping)) {
+ dev_kfree_skb(skb);
+ return -EIO;
+ }
map->skb = skb;
pci_unmap_addr_set(map, mapping, mapping);
- if (src_map != NULL)
- src_map->skb = NULL;
-
desc->addr_hi = ((u64)mapping >> 32);
desc->addr_lo = ((u64)mapping & 0xffffffff);
@@ -4452,30 +4497,32 @@ static int tg3_alloc_rx_skb(struct tg3_napi *tnapi, u32 opaque_key,
* members of the RX descriptor are invariant. See notes above
* tg3_alloc_rx_skb for full details.
*/
-static void tg3_recycle_rx(struct tg3_napi *tnapi, u32 opaque_key,
- int src_idx, u32 dest_idx_unmasked)
+static void tg3_recycle_rx(struct tg3_napi *tnapi,
+ struct tg3_rx_prodring_set *dpr,
+ u32 opaque_key, int src_idx,
+ u32 dest_idx_unmasked)
{
struct tg3 *tp = tnapi->tp;
struct tg3_rx_buffer_desc *src_desc, *dest_desc;
struct ring_info *src_map, *dest_map;
int dest_idx;
- struct tg3_rx_prodring_set *tpr = &tp->prodring[0];
+ struct tg3_rx_prodring_set *spr = &tp->prodring[0];
switch (opaque_key) {
case RXD_OPAQUE_RING_STD:
dest_idx = dest_idx_unmasked % TG3_RX_RING_SIZE;
- dest_desc = &tpr->rx_std[dest_idx];
- dest_map = &tpr->rx_std_buffers[dest_idx];
- src_desc = &tpr->rx_std[src_idx];
- src_map = &tpr->rx_std_buffers[src_idx];
+ dest_desc = &dpr->rx_std[dest_idx];
+ dest_map = &dpr->rx_std_buffers[dest_idx];
+ src_desc = &spr->rx_std[src_idx];
+ src_map = &spr->rx_std_buffers[src_idx];
break;
case RXD_OPAQUE_RING_JUMBO:
dest_idx = dest_idx_unmasked % TG3_RX_JUMBO_RING_SIZE;
- dest_desc = &tpr->rx_jmb[dest_idx].std;
- dest_map = &tpr->rx_jmb_buffers[dest_idx];
- src_desc = &tpr->rx_jmb[src_idx].std;
- src_map = &tpr->rx_jmb_buffers[src_idx];
+ dest_desc = &dpr->rx_jmb[dest_idx].std;
+ dest_map = &dpr->rx_jmb_buffers[dest_idx];
+ src_desc = &spr->rx_jmb[src_idx].std;
+ src_map = &spr->rx_jmb_buffers[src_idx];
break;
default:
@@ -4487,7 +4534,6 @@ static void tg3_recycle_rx(struct tg3_napi *tnapi, u32 opaque_key,
pci_unmap_addr(src_map, mapping));
dest_desc->addr_hi = src_desc->addr_hi;
dest_desc->addr_lo = src_desc->addr_lo;
-
src_map->skb = NULL;
}
@@ -4519,10 +4565,11 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
{
struct tg3 *tp = tnapi->tp;
u32 work_mask, rx_std_posted = 0;
+ u32 std_prod_idx, jmb_prod_idx;
u32 sw_idx = tnapi->rx_rcb_ptr;
u16 hw_idx;
int received;
- struct tg3_rx_prodring_set *tpr = &tp->prodring[0];
+ struct tg3_rx_prodring_set *tpr = tnapi->prodring;
hw_idx = *(tnapi->rx_rcb_prod_idx);
/*
@@ -4532,7 +4579,10 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
rmb();
work_mask = 0;
received = 0;
+ std_prod_idx = tpr->rx_std_prod_idx;
+ jmb_prod_idx = tpr->rx_jmb_prod_idx;
while (sw_idx != hw_idx && budget > 0) {
+ struct ring_info *ri;
struct tg3_rx_buffer_desc *desc = &tnapi->rx_rcb[sw_idx];
unsigned int len;
struct sk_buff *skb;
@@ -4542,16 +4592,16 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
desc_idx = desc->opaque & RXD_OPAQUE_INDEX_MASK;
opaque_key = desc->opaque & RXD_OPAQUE_RING_MASK;
if (opaque_key == RXD_OPAQUE_RING_STD) {
- struct ring_info *ri = &tpr->rx_std_buffers[desc_idx];
+ ri = &tp->prodring[0].rx_std_buffers[desc_idx];
dma_addr = pci_unmap_addr(ri, mapping);
skb = ri->skb;
- post_ptr = &tpr->rx_std_ptr;
+ post_ptr = &std_prod_idx;
rx_std_posted++;
} else if (opaque_key == RXD_OPAQUE_RING_JUMBO) {
- struct ring_info *ri = &tpr->rx_jmb_buffers[desc_idx];
+ ri = &tp->prodring[0].rx_jmb_buffers[desc_idx];
dma_addr = pci_unmap_addr(ri, mapping);
skb = ri->skb;
- post_ptr = &tpr->rx_jmb_ptr;
+ post_ptr = &jmb_prod_idx;
} else
goto next_pkt_nopost;
@@ -4560,7 +4610,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
if ((desc->err_vlan & RXD_ERR_MASK) != 0 &&
(desc->err_vlan != RXD_ERR_ODD_NIBBLE_RCVD_MII)) {
drop_it:
- tg3_recycle_rx(tnapi, opaque_key,
+ tg3_recycle_rx(tnapi, tpr, opaque_key,
desc_idx, *post_ptr);
drop_it_no_recycle:
/* Other statistics kept track of by card. */
@@ -4571,20 +4621,21 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) -
ETH_FCS_LEN;
- if (len > RX_COPY_THRESHOLD
- && tp->rx_offset == NET_IP_ALIGN
- /* rx_offset will likely not equal NET_IP_ALIGN
- * if this is a 5701 card running in PCI-X mode
- * [see tg3_get_invariants()]
- */
- ) {
+ if (len > RX_COPY_THRESHOLD &&
+ tp->rx_offset == NET_IP_ALIGN) {
+ /* rx_offset will likely not equal NET_IP_ALIGN
+ * if this is a 5701 card running in PCI-X mode
+ * [see tg3_get_invariants()]
+ */
int skb_size;
- skb_size = tg3_alloc_rx_skb(tnapi, opaque_key,
- desc_idx, *post_ptr);
+ skb_size = tg3_alloc_rx_skb(tp, tpr, opaque_key,
+ *post_ptr);
if (skb_size < 0)
goto drop_it;
+ ri->skb = NULL;
+
pci_unmap_single(tp->pdev, dma_addr, skb_size,
PCI_DMA_FROMDEVICE);
@@ -4592,7 +4643,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget)
} else {
struct sk_buff *copy_skb;
- tg3_recycle_rx(tnapi, opaque_key,
+ tg3_recycle_rx(tnapi, tpr, opaque_key,
desc_idx, *post_ptr);
copy_skb = netdev_alloc_skb(tp->dev,
@@ -4643,9 +4694,7 @@ next_pkt:
if (unlikely(rx_std_posted >= tp->rx_std_max_post)) {
u32 idx = *post_ptr % TG3_RX_RING_SIZE;
-
- tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX +
- TG3_64BIT_REG_LOW, idx);
+ tw32_rx_mbox(TG3_RX_STD_PROD_IDX_REG, idx);
work_mask &= ~RXD_OPAQUE_RING_STD;
rx_std_posted = 0;
}
@@ -4665,33 +4714,45 @@ next_pkt_nopost:
tw32_rx_mbox(tnapi->consmbox, sw_idx);
/* Refill RX ring(s). */
- if (work_mask & RXD_OPAQUE_RING_STD) {
- sw_idx = tpr->rx_std_ptr % TG3_RX_RING_SIZE;
- tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW,
- sw_idx);
- }
- if (work_mask & RXD_OPAQUE_RING_JUMBO) {
- sw_idx = tpr->rx_jmb_ptr % TG3_RX_JUMBO_RING_SIZE;
- tw32_rx_mbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW,
- sw_idx);
+ if (!(tp->tg3_flags3 & TG3_FLG3_ENABLE_RSS) || tnapi == &tp->napi[1]) {
+ if (work_mask & RXD_OPAQUE_RING_STD) {
+ tpr->rx_std_prod_idx = std_prod_idx % TG3_RX_RING_SIZE;
+ tw32_rx_mbox(TG3_RX_STD_PROD_IDX_REG,
+ tpr->rx_std_prod_idx);
+ }
+ if (work_mask & RXD_OPAQUE_RING_JUMBO) {
+ tpr->rx_jmb_prod_idx = jmb_prod_idx %
+ TG3_RX_JUMBO_RING_SIZE;
+ tw32_rx_mbox(TG3_RX_JMB_PROD_IDX_REG,
+ tpr->rx_jmb_prod_idx);
+ }
+ mmiowb();
+ } else if (work_mask) {
+ /* rx_std_buffers[] and rx_jmb_buffers[] entries must be
+ * updated before the producer indices can be updated.
+ */
+ smp_wmb();
+
+ tpr->rx_std_prod_idx = std_prod_idx % TG3_RX_RING_SIZE;
+ tpr->rx_jmb_prod_idx = jmb_prod_idx % TG3_RX_JUMBO_RING_SIZE;
+
+ napi_schedule(&tp->napi[1].napi);
}
- mmiowb();
return received;
}
-static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget)
+static void tg3_poll_link(struct tg3 *tp)
{
- struct tg3 *tp = tnapi->tp;
- struct tg3_hw_status *sblk = tnapi->hw_status;
-
/* handle link change and other phy events */
if (!(tp->tg3_flags &
(TG3_FLAG_USE_LINKCHG_REG |
TG3_FLAG_POLL_SERDES))) {
+ struct tg3_hw_status *sblk = tp->napi[0].hw_status;
+
if (sblk->status & SD_STATUS_LINK_CHG) {
sblk->status = SD_STATUS_UPDATED |
- (sblk->status & ~SD_STATUS_LINK_CHG);
+ (sblk->status & ~SD_STATUS_LINK_CHG);
spin_lock(&tp->lock);
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
tw32_f(MAC_STATUS,
@@ -4705,6 +4766,98 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget)
spin_unlock(&tp->lock);
}
}
+}
+
+static void tg3_rx_prodring_xfer(struct tg3 *tp,
+ struct tg3_rx_prodring_set *dpr,
+ struct tg3_rx_prodring_set *spr)
+{
+ u32 si, di, cpycnt, src_prod_idx;
+ int i;
+
+ while (1) {
+ src_prod_idx = spr->rx_std_prod_idx;
+
+ /* Make sure updates to the rx_std_buffers[] entries and the
+ * standard producer index are seen in the correct order.
+ */
+ smp_rmb();
+
+ if (spr->rx_std_cons_idx == src_prod_idx)
+ break;
+
+ if (spr->rx_std_cons_idx < src_prod_idx)
+ cpycnt = src_prod_idx - spr->rx_std_cons_idx;
+ else
+ cpycnt = TG3_RX_RING_SIZE - spr->rx_std_cons_idx;
+
+ cpycnt = min(cpycnt, TG3_RX_RING_SIZE - dpr->rx_std_prod_idx);
+
+ si = spr->rx_std_cons_idx;
+ di = dpr->rx_std_prod_idx;
+
+ memcpy(&dpr->rx_std_buffers[di],
+ &spr->rx_std_buffers[si],
+ cpycnt * sizeof(struct ring_info));
+
+ for (i = 0; i < cpycnt; i++, di++, si++) {
+ struct tg3_rx_buffer_desc *sbd, *dbd;
+ sbd = &spr->rx_std[si];
+ dbd = &dpr->rx_std[di];
+ dbd->addr_hi = sbd->addr_hi;
+ dbd->addr_lo = sbd->addr_lo;
+ }
+
+ spr->rx_std_cons_idx = (spr->rx_std_cons_idx + cpycnt) %
+ TG3_RX_RING_SIZE;
+ dpr->rx_std_prod_idx = (dpr->rx_std_prod_idx + cpycnt) %
+ TG3_RX_RING_SIZE;
+ }
+
+ while (1) {
+ src_prod_idx = spr->rx_jmb_prod_idx;
+
+ /* Make sure updates to the rx_jmb_buffers[] entries and
+ * the jumbo producer index are seen in the correct order.
+ */
+ smp_rmb();
+
+ if (spr->rx_jmb_cons_idx == src_prod_idx)
+ break;
+
+ if (spr->rx_jmb_cons_idx < src_prod_idx)
+ cpycnt = src_prod_idx - spr->rx_jmb_cons_idx;
+ else
+ cpycnt = TG3_RX_JUMBO_RING_SIZE - spr->rx_jmb_cons_idx;
+
+ cpycnt = min(cpycnt,
+ TG3_RX_JUMBO_RING_SIZE - dpr->rx_jmb_prod_idx);
+
+ si = spr->rx_jmb_cons_idx;
+ di = dpr->rx_jmb_prod_idx;
+
+ memcpy(&dpr->rx_jmb_buffers[di],
+ &spr->rx_jmb_buffers[si],
+ cpycnt * sizeof(struct ring_info));
+
+ for (i = 0; i < cpycnt; i++, di++, si++) {
+ struct tg3_rx_buffer_desc *sbd, *dbd;
+ sbd = &spr->rx_jmb[si].std;
+ dbd = &dpr->rx_jmb[di].std;
+ dbd->addr_hi = sbd->addr_hi;
+ dbd->addr_lo = sbd->addr_lo;
+ }
+
+ spr->rx_jmb_cons_idx = (spr->rx_jmb_cons_idx + cpycnt) %
+ TG3_RX_JUMBO_RING_SIZE;
+ dpr->rx_jmb_prod_idx = (dpr->rx_jmb_prod_idx + cpycnt) %
+ TG3_RX_JUMBO_RING_SIZE;
+ }
+}
+
+static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget)
+{
+ struct tg3 *tp = tnapi->tp;
/* run TX completion thread */
if (tnapi->hw_status->idx[0].tx_consumer != tnapi->tx_cons) {
@@ -4720,6 +4873,74 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget)
if (*(tnapi->rx_rcb_prod_idx) != tnapi->rx_rcb_ptr)
work_done += tg3_rx(tnapi, budget - work_done);
+ if ((tp->tg3_flags3 & TG3_FLG3_ENABLE_RSS) && tnapi == &tp->napi[1]) {
+ int i;
+ u32 std_prod_idx = tp->prodring[0].rx_std_prod_idx;
+ u32 jmb_prod_idx = tp->prodring[0].rx_jmb_prod_idx;
+
+ for (i = 2; i < tp->irq_cnt; i++)
+ tg3_rx_prodring_xfer(tp, tnapi->prodring,
+ tp->napi[i].prodring);
+
+ wmb();
+
+ if (std_prod_idx != tp->prodring[0].rx_std_prod_idx) {
+ u32 mbox = TG3_RX_STD_PROD_IDX_REG;
+ tw32_rx_mbox(mbox, tp->prodring[0].rx_std_prod_idx);
+ }
+
+ if (jmb_prod_idx != tp->prodring[0].rx_jmb_prod_idx) {
+ u32 mbox = TG3_RX_JMB_PROD_IDX_REG;
+ tw32_rx_mbox(mbox, tp->prodring[0].rx_jmb_prod_idx);
+ }
+
+ mmiowb();
+ }
+
+ return work_done;
+}
+
+static int tg3_poll_msix(struct napi_struct *napi, int budget)
+{
+ struct tg3_napi *tnapi = container_of(napi, struct tg3_napi, napi);
+ struct tg3 *tp = tnapi->tp;
+ int work_done = 0;
+ struct tg3_hw_status *sblk = tnapi->hw_status;
+
+ while (1) {
+ work_done = tg3_poll_work(tnapi, work_done, budget);
+
+ if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING))
+ goto tx_recovery;
+
+ if (unlikely(work_done >= budget))
+ break;
+
+ /* tp->last_tag is used in tg3_restart_ints() below
+ * to tell the hw how much work has been processed,
+ * so we must read it before checking for more work.
+ */
+ tnapi->last_tag = sblk->status_tag;
+ tnapi->last_irq_tag = tnapi->last_tag;
+ rmb();
+
+ /* check for RX/TX work to do */
+ if (sblk->idx[0].tx_consumer == tnapi->tx_cons &&
+ *(tnapi->rx_rcb_prod_idx) == tnapi->rx_rcb_ptr) {
+ napi_complete(napi);
+ /* Reenable interrupts. */
+ tw32_mailbox(tnapi->int_mbox, tnapi->last_tag << 24);
+ mmiowb();
+ break;
+ }
+ }
+
+ return work_done;
+
+tx_recovery:
+ /* work_done is guaranteed to be less than budget. */
+ napi_complete(napi);
+ schedule_work(&tp->reset_task);
return work_done;
}
@@ -4731,6 +4952,8 @@ static int tg3_poll(struct napi_struct *napi, int budget)
struct tg3_hw_status *sblk = tnapi->hw_status;
while (1) {
+ tg3_poll_link(tp);
+
work_done = tg3_poll_work(tnapi, work_done, budget);
if (unlikely(tp->tg3_flags & TG3_FLAG_TX_RECOVERY_PENDING))
@@ -5093,11 +5316,11 @@ static inline int tg3_40bit_overflow_test(struct tg3 *tp, dma_addr_t mapping,
static void tg3_set_txd(struct tg3_napi *, int, dma_addr_t, int, u32, u32);
/* Workaround 4GB and 40-bit hardware DMA bugs. */
-static int tigon3_dma_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
- u32 last_plus_one, u32 *start,
- u32 base_flags, u32 mss)
+static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi,
+ struct sk_buff *skb, u32 last_plus_one,
+ u32 *start, u32 base_flags, u32 mss)
{
- struct tg3_napi *tnapi = &tp->napi[0];
+ struct tg3 *tp = tnapi->tp;
struct sk_buff *new_skb;
dma_addr_t new_addr = 0;
u32 entry = *start;
@@ -5118,16 +5341,21 @@ static int tigon3_dma_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
} else {
/* New SKB is guaranteed to be linear. */
entry = *start;
- ret = skb_dma_map(&tp->pdev->dev, new_skb, DMA_TO_DEVICE);
- new_addr = skb_shinfo(new_skb)->dma_head;
+ new_addr = pci_map_single(tp->pdev, new_skb->data, new_skb->len,
+ PCI_DMA_TODEVICE);
+ /* Make sure the mapping succeeded */
+ if (pci_dma_mapping_error(tp->pdev, new_addr)) {
+ ret = -1;
+ dev_kfree_skb(new_skb);
+ new_skb = NULL;
/* Make sure new skb does not cross any 4G boundaries.
* Drop the packet if it does.
*/
- if (ret || tg3_4g_overflow_test(new_addr, new_skb->len)) {
- if (!ret)
- skb_dma_unmap(&tp->pdev->dev, new_skb,
- DMA_TO_DEVICE);
+ } else if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) &&
+ tg3_4g_overflow_test(new_addr, new_skb->len)) {
+ pci_unmap_single(tp->pdev, new_addr, new_skb->len,
+ PCI_DMA_TODEVICE);
ret = -1;
dev_kfree_skb(new_skb);
new_skb = NULL;
@@ -5141,15 +5369,28 @@ static int tigon3_dma_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
/* Now clean up the sw ring entries. */
i = 0;
while (entry != last_plus_one) {
+ int len;
+
if (i == 0)
- tnapi->tx_buffers[entry].skb = new_skb;
+ len = skb_headlen(skb);
else
+ len = skb_shinfo(skb)->frags[i-1].size;
+
+ pci_unmap_single(tp->pdev,
+ pci_unmap_addr(&tnapi->tx_buffers[entry],
+ mapping),
+ len, PCI_DMA_TODEVICE);
+ if (i == 0) {
+ tnapi->tx_buffers[entry].skb = new_skb;
+ pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping,
+ new_addr);
+ } else {
tnapi->tx_buffers[entry].skb = NULL;
+ }
entry = NEXT_TX(entry);
i++;
}
- skb_dma_unmap(&tp->pdev->dev, skb, DMA_TO_DEVICE);
dev_kfree_skb(skb);
return ret;
@@ -5179,21 +5420,22 @@ static void tg3_set_txd(struct tg3_napi *tnapi, int entry,
}
/* hard_start_xmit for devices that don't have any bugs and
- * support TG3_FLG2_HW_TSO_2 only.
+ * support TG3_FLG2_HW_TSO_2 and TG3_FLG2_HW_TSO_3 only.
*/
static netdev_tx_t tg3_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct tg3 *tp = netdev_priv(dev);
u32 len, entry, base_flags, mss;
- struct skb_shared_info *sp;
dma_addr_t mapping;
struct tg3_napi *tnapi;
struct netdev_queue *txq;
+ unsigned int i, last;
+
txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
tnapi = &tp->napi[skb_get_queue_mapping(skb)];
- if (tp->tg3_flags2 & TG3_FLG2_USING_MSIX)
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)
tnapi++;
/* We are running in BH disabled context with netif_tx_lock
@@ -5238,7 +5480,7 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb,
hdrlen = ip_tcp_len + tcp_opt_len;
}
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) {
+ if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) {
mss |= (hdrlen & 0xc) << 12;
if (hdrlen & 0x10)
base_flags |= 0x00000010;
@@ -5260,20 +5502,19 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb,
(vlan_tx_tag_get(skb) << 16));
#endif
- if (skb_dma_map(&tp->pdev->dev, skb, DMA_TO_DEVICE)) {
+ len = skb_headlen(skb);
+
+ /* Queue skb data, a.k.a. the main skb fragment. */
+ mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(tp->pdev, mapping)) {
dev_kfree_skb(skb);
goto out_unlock;
}
- sp = skb_shinfo(skb);
-
- mapping = sp->dma_head;
-
tnapi->tx_buffers[entry].skb = skb;
+ pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, mapping);
- len = skb_headlen(skb);
-
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 &&
+ if ((tp->tg3_flags3 & TG3_FLG3_USE_JUMBO_BDFLAG) &&
!mss && skb->len > ETH_DATA_LEN)
base_flags |= TXD_FLAG_JMB_PKT;
@@ -5284,15 +5525,21 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb,
/* Now loop through additional data fragments, and queue them. */
if (skb_shinfo(skb)->nr_frags > 0) {
- unsigned int i, last;
-
last = skb_shinfo(skb)->nr_frags - 1;
for (i = 0; i <= last; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
len = frag->size;
- mapping = sp->dma_maps[i];
+ mapping = pci_map_page(tp->pdev,
+ frag->page,
+ frag->page_offset,
+ len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(tp->pdev, mapping))
+ goto dma_error;
+
tnapi->tx_buffers[entry].skb = NULL;
+ pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping,
+ mapping);
tg3_set_txd(tnapi, entry, mapping, len,
base_flags, (i == last) | (mss << 1));
@@ -5315,6 +5562,27 @@ out_unlock:
mmiowb();
return NETDEV_TX_OK;
+
+dma_error:
+ last = i;
+ entry = tnapi->tx_prod;
+ tnapi->tx_buffers[entry].skb = NULL;
+ pci_unmap_single(tp->pdev,
+ pci_unmap_addr(&tnapi->tx_buffers[entry], mapping),
+ skb_headlen(skb),
+ PCI_DMA_TODEVICE);
+ for (i = 0; i <= last; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ entry = NEXT_TX(entry);
+
+ pci_unmap_page(tp->pdev,
+ pci_unmap_addr(&tnapi->tx_buffers[entry],
+ mapping),
+ frag->size, PCI_DMA_TODEVICE);
+ }
+
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
}
static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *,
@@ -5362,12 +5630,17 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
{
struct tg3 *tp = netdev_priv(dev);
u32 len, entry, base_flags, mss;
- struct skb_shared_info *sp;
int would_hit_hwbug;
dma_addr_t mapping;
- struct tg3_napi *tnapi = &tp->napi[0];
+ struct tg3_napi *tnapi;
+ struct netdev_queue *txq;
+ unsigned int i, last;
- len = skb_headlen(skb);
+
+ txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
+ tnapi = &tp->napi[skb_get_queue_mapping(skb)];
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)
+ tnapi++;
/* We are running in BH disabled context with netif_tx_lock
* and TX reclaim runs via tp->napi.poll inside of a software
@@ -5375,8 +5648,8 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
* no IRQ context deadlocks to worry about either. Rejoice!
*/
if (unlikely(tg3_tx_avail(tnapi) <= (skb_shinfo(skb)->nr_frags + 1))) {
- if (!netif_queue_stopped(dev)) {
- netif_stop_queue(dev);
+ if (!netif_tx_queue_stopped(txq)) {
+ netif_tx_stop_queue(txq);
/* This is a hard error, log it. */
printk(KERN_ERR PFX "%s: BUG! Tx Ring full when "
@@ -5389,10 +5662,10 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
base_flags = 0;
if (skb->ip_summed == CHECKSUM_PARTIAL)
base_flags |= TXD_FLAG_TCPUDP_CSUM;
- mss = 0;
+
if ((mss = skb_shinfo(skb)->gso_size) != 0) {
struct iphdr *iph;
- int tcp_opt_len, ip_tcp_len, hdr_len;
+ u32 tcp_opt_len, ip_tcp_len, hdr_len;
if (skb_header_cloned(skb) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
@@ -5423,8 +5696,15 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
IPPROTO_TCP,
0);
- if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) ||
- (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
+ if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) {
+ mss |= (hdr_len & 0xc) << 12;
+ if (hdr_len & 0x10)
+ base_flags |= 0x00000010;
+ base_flags |= (hdr_len & 0x3e0) << 5;
+ } else if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2)
+ mss |= hdr_len << 9;
+ else if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_1) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
if (tcp_opt_len || iph->ihl > 5) {
int tsflags;
@@ -5446,22 +5726,35 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
(vlan_tx_tag_get(skb) << 16));
#endif
- if (skb_dma_map(&tp->pdev->dev, skb, DMA_TO_DEVICE)) {
+ if ((tp->tg3_flags3 & TG3_FLG3_USE_JUMBO_BDFLAG) &&
+ !mss && skb->len > ETH_DATA_LEN)
+ base_flags |= TXD_FLAG_JMB_PKT;
+
+ len = skb_headlen(skb);
+
+ mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(tp->pdev, mapping)) {
dev_kfree_skb(skb);
goto out_unlock;
}
- sp = skb_shinfo(skb);
-
- mapping = sp->dma_head;
-
tnapi->tx_buffers[entry].skb = skb;
+ pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping, mapping);
would_hit_hwbug = 0;
- if (tp->tg3_flags3 & TG3_FLG3_5701_DMA_BUG)
+ if ((tp->tg3_flags3 & TG3_FLG3_SHORT_DMA_BUG) && len <= 8)
would_hit_hwbug = 1;
- else if (tg3_4g_overflow_test(mapping, len))
+
+ if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) &&
+ tg3_4g_overflow_test(mapping, len))
+ would_hit_hwbug = 1;
+
+ if ((tp->tg3_flags3 & TG3_FLG3_40BIT_DMA_LIMIT_BUG) &&
+ tg3_40bit_overflow_test(tp, mapping, len))
+ would_hit_hwbug = 1;
+
+ if (tp->tg3_flags3 & TG3_FLG3_5701_DMA_BUG)
would_hit_hwbug = 1;
tg3_set_txd(tnapi, entry, mapping, len, base_flags,
@@ -5471,21 +5764,32 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
/* Now loop through additional data fragments, and queue them. */
if (skb_shinfo(skb)->nr_frags > 0) {
- unsigned int i, last;
-
last = skb_shinfo(skb)->nr_frags - 1;
for (i = 0; i <= last; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
len = frag->size;
- mapping = sp->dma_maps[i];
+ mapping = pci_map_page(tp->pdev,
+ frag->page,
+ frag->page_offset,
+ len, PCI_DMA_TODEVICE);
tnapi->tx_buffers[entry].skb = NULL;
+ pci_unmap_addr_set(&tnapi->tx_buffers[entry], mapping,
+ mapping);
+ if (pci_dma_mapping_error(tp->pdev, mapping))
+ goto dma_error;
- if (tg3_4g_overflow_test(mapping, len))
+ if ((tp->tg3_flags3 & TG3_FLG3_SHORT_DMA_BUG) &&
+ len <= 8)
would_hit_hwbug = 1;
- if (tg3_40bit_overflow_test(tp, mapping, len))
+ if ((tp->tg3_flags3 & TG3_FLG3_4G_DMA_BNDRY_BUG) &&
+ tg3_4g_overflow_test(mapping, len))
+ would_hit_hwbug = 1;
+
+ if ((tp->tg3_flags3 & TG3_FLG3_40BIT_DMA_LIMIT_BUG) &&
+ tg3_40bit_overflow_test(tp, mapping, len))
would_hit_hwbug = 1;
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
@@ -5509,7 +5813,7 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
/* If the workaround fails due to memory/mapping
* failure, silently drop this packet.
*/
- if (tigon3_dma_hwbug_workaround(tp, skb, last_plus_one,
+ if (tigon3_dma_hwbug_workaround(tnapi, skb, last_plus_one,
&start, base_flags, mss))
goto out_unlock;
@@ -5517,19 +5821,40 @@ static netdev_tx_t tg3_start_xmit_dma_bug(struct sk_buff *skb,
}
/* Packets are ready, update Tx producer idx local and on card. */
- tw32_tx_mbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, entry);
+ tw32_tx_mbox(tnapi->prodmbox, entry);
tnapi->tx_prod = entry;
if (unlikely(tg3_tx_avail(tnapi) <= (MAX_SKB_FRAGS + 1))) {
- netif_stop_queue(dev);
+ netif_tx_stop_queue(txq);
if (tg3_tx_avail(tnapi) > TG3_TX_WAKEUP_THRESH(tnapi))
- netif_wake_queue(tp->dev);
+ netif_tx_wake_queue(txq);
}
out_unlock:
mmiowb();
return NETDEV_TX_OK;
+
+dma_error:
+ last = i;
+ entry = tnapi->tx_prod;
+ tnapi->tx_buffers[entry].skb = NULL;
+ pci_unmap_single(tp->pdev,
+ pci_unmap_addr(&tnapi->tx_buffers[entry], mapping),
+ skb_headlen(skb),
+ PCI_DMA_TODEVICE);
+ for (i = 0; i <= last; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ entry = NEXT_TX(entry);
+
+ pci_unmap_page(tp->pdev,
+ pci_unmap_addr(&tnapi->tx_buffers[entry],
+ mapping),
+ frag->size, PCI_DMA_TODEVICE);
+ }
+
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
}
static inline void tg3_set_mtu(struct net_device *dev, struct tg3 *tp,
@@ -5594,36 +5919,33 @@ static void tg3_rx_prodring_free(struct tg3 *tp,
struct tg3_rx_prodring_set *tpr)
{
int i;
- struct ring_info *rxp;
-
- for (i = 0; i < TG3_RX_RING_SIZE; i++) {
- rxp = &tpr->rx_std_buffers[i];
- if (rxp->skb == NULL)
- continue;
+ if (tpr != &tp->prodring[0]) {
+ for (i = tpr->rx_std_cons_idx; i != tpr->rx_std_prod_idx;
+ i = (i + 1) % TG3_RX_RING_SIZE)
+ tg3_rx_skb_free(tp, &tpr->rx_std_buffers[i],
+ tp->rx_pkt_map_sz);
+
+ if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) {
+ for (i = tpr->rx_jmb_cons_idx;
+ i != tpr->rx_jmb_prod_idx;
+ i = (i + 1) % TG3_RX_JUMBO_RING_SIZE) {
+ tg3_rx_skb_free(tp, &tpr->rx_jmb_buffers[i],
+ TG3_RX_JMB_MAP_SZ);
+ }
+ }
- pci_unmap_single(tp->pdev,
- pci_unmap_addr(rxp, mapping),
- tp->rx_pkt_map_sz,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb_any(rxp->skb);
- rxp->skb = NULL;
+ return;
}
- if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) {
- for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++) {
- rxp = &tpr->rx_jmb_buffers[i];
-
- if (rxp->skb == NULL)
- continue;
+ for (i = 0; i < TG3_RX_RING_SIZE; i++)
+ tg3_rx_skb_free(tp, &tpr->rx_std_buffers[i],
+ tp->rx_pkt_map_sz);
- pci_unmap_single(tp->pdev,
- pci_unmap_addr(rxp, mapping),
- TG3_RX_JMB_MAP_SZ,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb_any(rxp->skb);
- rxp->skb = NULL;
- }
+ if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) {
+ for (i = 0; i < TG3_RX_JUMBO_RING_SIZE; i++)
+ tg3_rx_skb_free(tp, &tpr->rx_jmb_buffers[i],
+ TG3_RX_JMB_MAP_SZ);
}
}
@@ -5638,7 +5960,19 @@ static int tg3_rx_prodring_alloc(struct tg3 *tp,
struct tg3_rx_prodring_set *tpr)
{
u32 i, rx_pkt_dma_sz;
- struct tg3_napi *tnapi = &tp->napi[0];
+
+ tpr->rx_std_cons_idx = 0;
+ tpr->rx_std_prod_idx = 0;
+ tpr->rx_jmb_cons_idx = 0;
+ tpr->rx_jmb_prod_idx = 0;
+
+ if (tpr != &tp->prodring[0]) {
+ memset(&tpr->rx_std_buffers[0], 0, TG3_RX_STD_BUFF_RING_SIZE);
+ if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE)
+ memset(&tpr->rx_jmb_buffers[0], 0,
+ TG3_RX_JMB_BUFF_RING_SIZE);
+ goto done;
+ }
/* Zero out all descriptors. */
memset(tpr->rx_std, 0, TG3_RX_RING_BYTES);
@@ -5665,7 +5999,7 @@ static int tg3_rx_prodring_alloc(struct tg3 *tp,
/* Now allocate fresh SKBs for each rx ring. */
for (i = 0; i < tp->rx_pending; i++) {
- if (tg3_alloc_rx_skb(tnapi, RXD_OPAQUE_RING_STD, -1, i) < 0) {
+ if (tg3_alloc_rx_skb(tp, tpr, RXD_OPAQUE_RING_STD, i) < 0) {
printk(KERN_WARNING PFX
"%s: Using a smaller RX standard ring, "
"only %d out of %d buffers were allocated "
@@ -5696,8 +6030,8 @@ static int tg3_rx_prodring_alloc(struct tg3 *tp,
}
for (i = 0; i < tp->rx_jumbo_pending; i++) {
- if (tg3_alloc_rx_skb(tnapi, RXD_OPAQUE_RING_JUMBO,
- -1, i) < 0) {
+ if (tg3_alloc_rx_skb(tp, tpr, RXD_OPAQUE_RING_JUMBO,
+ i) < 0) {
printk(KERN_WARNING PFX
"%s: Using a smaller RX jumbo ring, "
"only %d out of %d buffers were "
@@ -5741,8 +6075,7 @@ static void tg3_rx_prodring_fini(struct tg3 *tp,
static int tg3_rx_prodring_init(struct tg3 *tp,
struct tg3_rx_prodring_set *tpr)
{
- tpr->rx_std_buffers = kzalloc(sizeof(struct ring_info) *
- TG3_RX_RING_SIZE, GFP_KERNEL);
+ tpr->rx_std_buffers = kzalloc(TG3_RX_STD_BUFF_RING_SIZE, GFP_KERNEL);
if (!tpr->rx_std_buffers)
return -ENOMEM;
@@ -5752,8 +6085,7 @@ static int tg3_rx_prodring_init(struct tg3 *tp,
goto err_out;
if (tp->tg3_flags & TG3_FLAG_JUMBO_CAPABLE) {
- tpr->rx_jmb_buffers = kzalloc(sizeof(struct ring_info) *
- TG3_RX_JUMBO_RING_SIZE,
+ tpr->rx_jmb_buffers = kzalloc(TG3_RX_JMB_BUFF_RING_SIZE,
GFP_KERNEL);
if (!tpr->rx_jmb_buffers)
goto err_out;
@@ -5790,8 +6122,9 @@ static void tg3_free_rings(struct tg3 *tp)
continue;
for (i = 0; i < TG3_TX_RING_SIZE; ) {
- struct tx_ring_info *txp;
+ struct ring_info *txp;
struct sk_buff *skb;
+ unsigned int k;
txp = &tnapi->tx_buffers[i];
skb = txp->skb;
@@ -5801,17 +6134,29 @@ static void tg3_free_rings(struct tg3 *tp)
continue;
}
- skb_dma_unmap(&tp->pdev->dev, skb, DMA_TO_DEVICE);
-
+ pci_unmap_single(tp->pdev,
+ pci_unmap_addr(txp, mapping),
+ skb_headlen(skb),
+ PCI_DMA_TODEVICE);
txp->skb = NULL;
- i += skb_shinfo(skb)->nr_frags + 1;
+ i++;
+
+ for (k = 0; k < skb_shinfo(skb)->nr_frags; k++) {
+ txp = &tnapi->tx_buffers[i & (TG3_TX_RING_SIZE - 1)];
+ pci_unmap_page(tp->pdev,
+ pci_unmap_addr(txp, mapping),
+ skb_shinfo(skb)->frags[k].size,
+ PCI_DMA_TODEVICE);
+ i++;
+ }
dev_kfree_skb_any(skb);
}
- }
- tg3_rx_prodring_free(tp, &tp->prodring[0]);
+ if (tp->irq_cnt == 1 || j != tp->irq_cnt - 1)
+ tg3_rx_prodring_free(tp, &tp->prodring[j]);
+ }
}
/* Initialize tx/rx rings for packet processing.
@@ -5845,9 +6190,13 @@ static int tg3_init_rings(struct tg3 *tp)
tnapi->rx_rcb_ptr = 0;
if (tnapi->rx_rcb)
memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp));
+
+ if ((tp->irq_cnt == 1 || i != tp->irq_cnt - 1) &&
+ tg3_rx_prodring_alloc(tp, &tp->prodring[i]))
+ return -ENOMEM;
}
- return tg3_rx_prodring_alloc(tp, &tp->prodring[0]);
+ return 0;
}
/*
@@ -5891,7 +6240,8 @@ static void tg3_free_consistent(struct tg3 *tp)
tp->hw_stats = NULL;
}
- tg3_rx_prodring_fini(tp, &tp->prodring[0]);
+ for (i = 0; i < (tp->irq_cnt == 1 ? 1 : tp->irq_cnt - 1); i++)
+ tg3_rx_prodring_fini(tp, &tp->prodring[i]);
}
/*
@@ -5902,8 +6252,10 @@ static int tg3_alloc_consistent(struct tg3 *tp)
{
int i;
- if (tg3_rx_prodring_init(tp, &tp->prodring[0]))
- return -ENOMEM;
+ for (i = 0; i < (tp->irq_cnt == 1 ? 1 : tp->irq_cnt - 1); i++) {
+ if (tg3_rx_prodring_init(tp, &tp->prodring[i]))
+ goto err_out;
+ }
tp->hw_stats = pci_alloc_consistent(tp->pdev,
sizeof(struct tg3_hw_stats),
@@ -5926,6 +6278,24 @@ static int tg3_alloc_consistent(struct tg3 *tp)
memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE);
sblk = tnapi->hw_status;
+ /* If multivector TSS is enabled, vector 0 does not handle
+ * tx interrupts. Don't allocate any resources for it.
+ */
+ if ((!i && !(tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)) ||
+ (i && (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS))) {
+ tnapi->tx_buffers = kzalloc(sizeof(struct ring_info) *
+ TG3_TX_RING_SIZE,
+ GFP_KERNEL);
+ if (!tnapi->tx_buffers)
+ goto err_out;
+
+ tnapi->tx_ring = pci_alloc_consistent(tp->pdev,
+ TG3_TX_RING_BYTES,
+ &tnapi->tx_desc_mapping);
+ if (!tnapi->tx_ring)
+ goto err_out;
+ }
+
/*
* When RSS is enabled, the status block format changes
* slightly. The "rx_jumbo_consumer", "reserved",
@@ -5947,6 +6317,11 @@ static int tg3_alloc_consistent(struct tg3 *tp)
break;
}
+ if (tp->irq_cnt == 1)
+ tnapi->prodring = &tp->prodring[0];
+ else if (i)
+ tnapi->prodring = &tp->prodring[i - 1];
+
/*
* If multivector RSS is enabled, vector 0 does not handle
* rx or tx interrupts. Don't allocate any resources for it.
@@ -5961,17 +6336,6 @@ static int tg3_alloc_consistent(struct tg3 *tp)
goto err_out;
memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp));
-
- tnapi->tx_buffers = kzalloc(sizeof(struct tx_ring_info) *
- TG3_TX_RING_SIZE, GFP_KERNEL);
- if (!tnapi->tx_buffers)
- goto err_out;
-
- tnapi->tx_ring = pci_alloc_consistent(tp->pdev,
- TG3_TX_RING_BYTES,
- &tnapi->tx_desc_mapping);
- if (!tnapi->tx_ring)
- goto err_out;
}
return 0;
@@ -6580,10 +6944,35 @@ static int tg3_chip_reset(struct tg3 *tp)
tg3_mdio_start(tp);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780) {
+ u8 phy_addr;
+
+ phy_addr = tp->phy_addr;
+ tp->phy_addr = TG3_PHY_PCIE_ADDR;
+
+ tg3_writephy(tp, TG3_PCIEPHY_BLOCK_ADDR,
+ TG3_PCIEPHY_TXB_BLK << TG3_PCIEPHY_BLOCK_SHIFT);
+ val = TG3_PCIEPHY_TX0CTRL1_TXOCM | TG3_PCIEPHY_TX0CTRL1_RDCTL |
+ TG3_PCIEPHY_TX0CTRL1_TXCMV | TG3_PCIEPHY_TX0CTRL1_TKSEL |
+ TG3_PCIEPHY_TX0CTRL1_NB_EN;
+ tg3_writephy(tp, TG3_PCIEPHY_TX0CTRL1, val);
+ udelay(10);
+
+ tg3_writephy(tp, TG3_PCIEPHY_BLOCK_ADDR,
+ TG3_PCIEPHY_XGXS_BLK1 << TG3_PCIEPHY_BLOCK_SHIFT);
+ val = TG3_PCIEPHY_PWRMGMT4_LOWPWR_EN |
+ TG3_PCIEPHY_PWRMGMT4_L1PLLPD_EN;
+ tg3_writephy(tp, TG3_PCIEPHY_PWRMGMT4, val);
+ udelay(10);
+
+ tp->phy_addr = phy_addr;
+ }
+
if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) &&
tp->pci_chip_rev_id != CHIPREV_ID_5750_A0 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) {
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765) {
val = tr32(0x7c00);
tw32(0x7c00, val | (1 << 25));
@@ -6935,19 +7324,21 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec)
{
int i;
- if (!(tp->tg3_flags2 & TG3_FLG2_USING_MSIX)) {
+ if (!(tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)) {
tw32(HOSTCC_TXCOL_TICKS, ec->tx_coalesce_usecs);
tw32(HOSTCC_TXMAX_FRAMES, ec->tx_max_coalesced_frames);
tw32(HOSTCC_TXCOAL_MAXF_INT, ec->tx_max_coalesced_frames_irq);
-
- tw32(HOSTCC_RXCOL_TICKS, ec->rx_coalesce_usecs);
- tw32(HOSTCC_RXMAX_FRAMES, ec->rx_max_coalesced_frames);
- tw32(HOSTCC_RXCOAL_MAXF_INT, ec->rx_max_coalesced_frames_irq);
} else {
tw32(HOSTCC_TXCOL_TICKS, 0);
tw32(HOSTCC_TXMAX_FRAMES, 0);
tw32(HOSTCC_TXCOAL_MAXF_INT, 0);
+ }
+ if (!(tp->tg3_flags2 & TG3_FLG2_USING_MSIX)) {
+ tw32(HOSTCC_RXCOL_TICKS, ec->rx_coalesce_usecs);
+ tw32(HOSTCC_RXMAX_FRAMES, ec->rx_max_coalesced_frames);
+ tw32(HOSTCC_RXCOAL_MAXF_INT, ec->rx_max_coalesced_frames_irq);
+ } else {
tw32(HOSTCC_RXCOL_TICKS, 0);
tw32(HOSTCC_RXMAX_FRAMES, 0);
tw32(HOSTCC_RXCOAL_MAXF_INT, 0);
@@ -6970,25 +7361,31 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec)
reg = HOSTCC_RXCOL_TICKS_VEC1 + i * 0x18;
tw32(reg, ec->rx_coalesce_usecs);
- reg = HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18;
- tw32(reg, ec->tx_coalesce_usecs);
reg = HOSTCC_RXMAX_FRAMES_VEC1 + i * 0x18;
tw32(reg, ec->rx_max_coalesced_frames);
- reg = HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18;
- tw32(reg, ec->tx_max_coalesced_frames);
reg = HOSTCC_RXCOAL_MAXF_INT_VEC1 + i * 0x18;
tw32(reg, ec->rx_max_coalesced_frames_irq);
- reg = HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18;
- tw32(reg, ec->tx_max_coalesced_frames_irq);
+
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) {
+ reg = HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18;
+ tw32(reg, ec->tx_coalesce_usecs);
+ reg = HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18;
+ tw32(reg, ec->tx_max_coalesced_frames);
+ reg = HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18;
+ tw32(reg, ec->tx_max_coalesced_frames_irq);
+ }
}
for (; i < tp->irq_max - 1; i++) {
tw32(HOSTCC_RXCOL_TICKS_VEC1 + i * 0x18, 0);
- tw32(HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18, 0);
tw32(HOSTCC_RXMAX_FRAMES_VEC1 + i * 0x18, 0);
- tw32(HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18, 0);
tw32(HOSTCC_RXCOAL_MAXF_INT_VEC1 + i * 0x18, 0);
- tw32(HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18, 0);
+
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS) {
+ tw32(HOSTCC_TXCOL_TICKS_VEC1 + i * 0x18, 0);
+ tw32(HOSTCC_TXMAX_FRAMES_VEC1 + i * 0x18, 0);
+ tw32(HOSTCC_TXCOAL_MAXF_INT_VEC1 + i * 0x18, 0);
+ }
}
}
@@ -7002,6 +7399,8 @@ static void tg3_rings_reset(struct tg3 *tp)
/* Disable all transmit rings but the first. */
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 16;
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
+ limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 2;
else
limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE;
@@ -7016,7 +7415,8 @@ static void tg3_rings_reset(struct tg3 *tp)
limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 17;
else if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 16;
- else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755)
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 4;
else
limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE;
@@ -7089,17 +7489,19 @@ static void tg3_rings_reset(struct tg3 *tp)
/* Clear status block in ram. */
memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE);
- tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
- (TG3_TX_RING_SIZE <<
- BDINFO_FLAGS_MAXLEN_SHIFT),
- NIC_SRAM_TX_BUFFER_DESC);
+ if (tnapi->tx_ring) {
+ tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping,
+ (TG3_TX_RING_SIZE <<
+ BDINFO_FLAGS_MAXLEN_SHIFT),
+ NIC_SRAM_TX_BUFFER_DESC);
+ txrcb += TG3_BDINFO_SIZE;
+ }
tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping,
(TG3_RX_RCB_RING_SIZE(tp) <<
BDINFO_FLAGS_MAXLEN_SHIFT), 0);
stblk += 8;
- txrcb += TG3_BDINFO_SIZE;
rxrcb += TG3_BDINFO_SIZE;
}
}
@@ -7162,15 +7564,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
tw32(TG3_PCIE_EIDLE_DELAY, val | TG3_PCIE_EIDLE_DELAY_13_CLKS);
tw32(TG3_CORR_ERR_STAT, TG3_CORR_ERR_STAT_CLEAR);
- }
- if (tp->tg3_flags3 & TG3_FLG3_TOGGLE_10_100_L1PLLPD) {
- val = tr32(TG3_PCIE_LNKCTL);
- if (tp->tg3_flags3 & TG3_FLG3_CLKREQ_BUG)
- val |= TG3_PCIE_LNKCTL_L1_PLL_PD_DIS;
- else
- val &= ~TG3_PCIE_LNKCTL_L1_PLL_PD_DIS;
- tw32(TG3_PCIE_LNKCTL, val);
+ val = tr32(TG3_PCIE_LNKCTL) & ~TG3_PCIE_LNKCTL_L1_PLL_PD_EN;
+ tw32(TG3_PCIE_LNKCTL, val | TG3_PCIE_LNKCTL_L1_PLL_PD_DIS);
}
/* This works around an issue with Athlon chipsets on
@@ -7217,9 +7613,13 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
if (err)
return err;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) {
+ val = tr32(TG3PCI_DMA_RW_CTRL) &
+ ~DMA_RWCTRL_DIS_CACHE_ALIGNMENT;
+ tw32(TG3PCI_DMA_RW_CTRL, val | tp->dma_rwctrl);
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) {
/* This value is determined during the probe time DMA
* engine test, tg3_test_dma.
*/
@@ -7342,8 +7742,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
((u64) tpr->rx_std_mapping >> 32));
tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW,
((u64) tpr->rx_std_mapping & 0xffffffff));
- tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR,
- NIC_SRAM_RX_BUFFER_DESC);
+ if (!(tp->tg3_flags3 & TG3_FLG3_5755_PLUS))
+ tw32(RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR,
+ NIC_SRAM_RX_BUFFER_DESC);
/* Disable the mini ring */
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
@@ -7366,14 +7767,16 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS,
(RX_JUMBO_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT) |
BDINFO_FLAGS_USE_EXT_RECV);
- tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR,
- NIC_SRAM_RX_JUMBO_BUFFER_DESC);
+ if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS))
+ tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_NIC_ADDR,
+ NIC_SRAM_RX_JUMBO_BUFFER_DESC);
} else {
tw32(RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS,
BDINFO_FLAGS_DISABLED);
}
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
val = (RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT) |
(RX_STD_MAX_SIZE << 2);
else
@@ -7383,16 +7786,15 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, val);
- tpr->rx_std_ptr = tp->rx_pending;
- tw32_rx_mbox(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW,
- tpr->rx_std_ptr);
+ tpr->rx_std_prod_idx = tp->rx_pending;
+ tw32_rx_mbox(TG3_RX_STD_PROD_IDX_REG, tpr->rx_std_prod_idx);
- tpr->rx_jmb_ptr = (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) ?
+ tpr->rx_jmb_prod_idx = (tp->tg3_flags & TG3_FLAG_JUMBO_RING_ENABLE) ?
tp->rx_jumbo_pending : 0;
- tw32_rx_mbox(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW,
- tpr->rx_jmb_ptr);
+ tw32_rx_mbox(TG3_RX_JMB_PROD_IDX_REG, tpr->rx_jmb_prod_idx);
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) {
tw32(STD_REPLENISH_LWM, 32);
tw32(JMB_REPLENISH_LWM, 16);
}
@@ -7453,7 +7855,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
rdmac_mode |= RDMAC_MODE_IPV4_LSO_EN;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
rdmac_mode |= RDMAC_MODE_IPV6_LSO_EN;
@@ -7602,6 +8005,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
if (tp->tg3_flags3 & TG3_FLG3_5755_PLUS)
val |= WDMAC_MODE_STATUS_TAG_FIX;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)
+ val |= WDMAC_MODE_BURST_ALL_DATA;
+
tw32_f(WDMAC_MODE, val);
udelay(40);
@@ -7641,7 +8047,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE | 0x8);
val = SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE;
- if (tp->tg3_flags2 & TG3_FLG2_USING_MSIX)
+ if (tp->tg3_flags3 & TG3_FLG3_ENABLE_TSS)
val |= SNDBDI_MODE_MULTI_TXQ_EN;
tw32(SNDBDI_MODE, val);
tw32(SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE);
@@ -8065,7 +8471,8 @@ static int tg3_test_interrupt(struct tg3 *tp)
* Turn off MSI one shot mode. Otherwise this test has no
* observable way to know whether the interrupt was delivered.
*/
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 &&
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) &&
(tp->tg3_flags2 & TG3_FLG2_USING_MSI)) {
val = tr32(MSGINT_MODE) | MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, val);
@@ -8108,7 +8515,8 @@ static int tg3_test_interrupt(struct tg3 *tp)
if (intr_ok) {
/* Reenable MSI one shot mode. */
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 &&
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) &&
(tp->tg3_flags2 & TG3_FLG2_USING_MSI)) {
val = tr32(MSGINT_MODE) & ~MSGINT_MODE_ONE_SHOT_DISABLE;
tw32(MSGINT_MODE, val);
@@ -8249,7 +8657,11 @@ static bool tg3_enable_msix(struct tg3 *tp)
for (i = 0; i < tp->irq_max; i++)
tp->napi[i].irq_vec = msix_ent[i].vector;
- tp->dev->real_num_tx_queues = tp->irq_cnt - 1;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) {
+ tp->tg3_flags3 |= TG3_FLG3_ENABLE_TSS;
+ tp->dev->real_num_tx_queues = tp->irq_cnt - 1;
+ } else
+ tp->dev->real_num_tx_queues = 1;
return true;
}
@@ -8400,6 +8812,7 @@ static int tg3_open(struct net_device *dev)
}
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765 &&
(tp->tg3_flags2 & TG3_FLG2_USING_MSI) &&
(tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI)) {
u32 val = tr32(PCIE_TRANSACTION_CFG);
@@ -9240,9 +9653,11 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
struct tg3 *tp = netdev_priv(dev);
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
+ struct phy_device *phydev;
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
- return phy_ethtool_gset(tp->mdio_bus->phy_map[PHY_ADDR], cmd);
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+ return phy_ethtool_gset(phydev, cmd);
}
cmd->supported = (SUPPORTED_Autoneg);
@@ -9281,9 +9696,11 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
struct tg3 *tp = netdev_priv(dev);
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
+ struct phy_device *phydev;
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
- return phy_ethtool_sset(tp->mdio_bus->phy_map[PHY_ADDR], cmd);
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+ return phy_ethtool_sset(phydev, cmd);
}
if (cmd->autoneg != AUTONEG_ENABLE &&
@@ -9436,15 +9853,16 @@ static int tg3_set_tso(struct net_device *dev, u32 value)
return 0;
}
if ((dev->features & NETIF_F_IPV6_CSUM) &&
- (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2)) {
+ ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) ||
+ (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3))) {
if (value) {
dev->features |= NETIF_F_TSO6;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 &&
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
dev->features |= NETIF_F_TSO_ECN;
} else
dev->features &= ~(NETIF_F_TSO6 | NETIF_F_TSO_ECN);
@@ -9466,7 +9884,7 @@ static int tg3_nway_reset(struct net_device *dev)
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
- r = phy_start_aneg(tp->mdio_bus->phy_map[PHY_ADDR]);
+ r = phy_start_aneg(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]);
} else {
u32 bmcr;
@@ -9585,7 +10003,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
u32 newadv;
struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[PHY_ADDR];
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
if (epause->rx_pause) {
if (epause->tx_pause)
@@ -10339,6 +10757,10 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
tx_data[i] = (u8) (i & 0xff);
map = pci_map_single(tp->pdev, skb->data, tx_len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(tp->pdev, map)) {
+ dev_kfree_skb(skb);
+ return -EIO;
+ }
tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE |
rnapi->coal_now);
@@ -10359,8 +10781,8 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
udelay(10);
- /* 250 usec to allow enough time on some 10/100 Mbps devices. */
- for (i = 0; i < 25; i++) {
+ /* 350 usec to allow enough time on some 10/100 Mbps devices. */
+ for (i = 0; i < 35; i++) {
tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE |
coal_now);
@@ -10565,9 +10987,11 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
int err;
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) {
+ struct phy_device *phydev;
if (!(tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED))
return -EAGAIN;
- return phy_mii_ioctl(tp->mdio_bus->phy_map[PHY_ADDR], data, cmd);
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
+ return phy_mii_ioctl(phydev, data, cmd);
}
switch(cmd) {
@@ -10887,7 +11311,7 @@ static void __devinit tg3_get_5752_nvram_info(struct tg3 *tp)
/* NVRAM protection for TPM */
if (nvcfg1 & (1 << 27))
- tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM;
+ tp->tg3_flags3 |= TG3_FLG3_PROTECTED_NVRAM;
switch (nvcfg1 & NVRAM_CFG1_5752VENDOR_MASK) {
case FLASH_5752VENDOR_ATMEL_EEPROM_64KHZ:
@@ -10928,7 +11352,7 @@ static void __devinit tg3_get_5755_nvram_info(struct tg3 *tp)
/* NVRAM protection for TPM */
if (nvcfg1 & (1 << 27)) {
- tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM;
+ tp->tg3_flags3 |= TG3_FLG3_PROTECTED_NVRAM;
protect = 1;
}
@@ -11022,7 +11446,7 @@ static void __devinit tg3_get_5761_nvram_info(struct tg3 *tp)
/* NVRAM protection for TPM */
if (nvcfg1 & (1 << 27)) {
- tp->tg3_flags2 |= TG3_FLG2_PROTECTED_NVRAM;
+ tp->tg3_flags3 |= TG3_FLG3_PROTECTED_NVRAM;
protect = 1;
}
@@ -11283,7 +11707,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp)
tg3_get_5761_nvram_info(tp);
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tg3_get_5906_nvram_info(tp);
- else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
tg3_get_57780_nvram_info(tp);
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
tg3_get_5717_nvram_info(tp);
@@ -11524,7 +11949,7 @@ static int tg3_nvram_write_block(struct tg3 *tp, u32 offset, u32 len, u8 *buf)
tg3_enable_nvram_access(tp);
if ((tp->tg3_flags2 & TG3_FLG2_5750_PLUS) &&
- !(tp->tg3_flags2 & TG3_FLG2_PROTECTED_NVRAM))
+ !(tp->tg3_flags3 & TG3_FLG3_PROTECTED_NVRAM))
tw32(NVRAM_WRITE1, 0x406);
grc_mode = tr32(GRC_MODE);
@@ -12008,7 +12433,7 @@ skip_phy_reset:
static void __devinit tg3_read_partno(struct tg3 *tp)
{
- unsigned char vpd_data[256]; /* in little-endian format */
+ unsigned char vpd_data[TG3_NVM_VPD_LEN]; /* in little-endian format */
unsigned int i;
u32 magic;
@@ -12017,48 +12442,37 @@ static void __devinit tg3_read_partno(struct tg3 *tp)
goto out_not_found;
if (magic == TG3_EEPROM_MAGIC) {
- for (i = 0; i < 256; i += 4) {
+ for (i = 0; i < TG3_NVM_VPD_LEN; i += 4) {
u32 tmp;
/* The data is in little-endian format in NVRAM.
* Use the big-endian read routines to preserve
* the byte order as it exists in NVRAM.
*/
- if (tg3_nvram_read_be32(tp, 0x100 + i, &tmp))
+ if (tg3_nvram_read_be32(tp, TG3_NVM_VPD_OFF + i, &tmp))
goto out_not_found;
memcpy(&vpd_data[i], &tmp, sizeof(tmp));
}
} else {
- int vpd_cap;
-
- vpd_cap = pci_find_capability(tp->pdev, PCI_CAP_ID_VPD);
- for (i = 0; i < 256; i += 4) {
- u32 tmp, j = 0;
- __le32 v;
- u16 tmp16;
-
- pci_write_config_word(tp->pdev, vpd_cap + PCI_VPD_ADDR,
- i);
- while (j++ < 100) {
- pci_read_config_word(tp->pdev, vpd_cap +
- PCI_VPD_ADDR, &tmp16);
- if (tmp16 & 0x8000)
- break;
- msleep(1);
- }
- if (!(tmp16 & 0x8000))
+ ssize_t cnt;
+ unsigned int pos = 0, i = 0;
+
+ for (; pos < TG3_NVM_VPD_LEN && i < 3; i++, pos += cnt) {
+ cnt = pci_read_vpd(tp->pdev, pos,
+ TG3_NVM_VPD_LEN - pos,
+ &vpd_data[pos]);
+ if (cnt == -ETIMEDOUT || -EINTR)
+ cnt = 0;
+ else if (cnt < 0)
goto out_not_found;
-
- pci_read_config_dword(tp->pdev, vpd_cap + PCI_VPD_DATA,
- &tmp);
- v = cpu_to_le32(tmp);
- memcpy(&vpd_data[i], &v, sizeof(v));
}
+ if (pos != TG3_NVM_VPD_LEN)
+ goto out_not_found;
}
/* Now parse and find the part number. */
- for (i = 0; i < 254; ) {
+ for (i = 0; i < TG3_NVM_VPD_LEN - 2; ) {
unsigned char val = vpd_data[i];
unsigned int block_end;
@@ -12077,7 +12491,7 @@ static void __devinit tg3_read_partno(struct tg3 *tp)
(vpd_data[i + 2] << 8)));
i += 3;
- if (block_end > 256)
+ if (block_end > TG3_NVM_VPD_LEN)
goto out_not_found;
while (i < (block_end - 2)) {
@@ -12086,7 +12500,8 @@ static void __devinit tg3_read_partno(struct tg3 *tp)
int partno_len = vpd_data[i + 2];
i += 3;
- if (partno_len > 24 || (partno_len + i) > 256)
+ if (partno_len > TG3_BPN_SIZE ||
+ (partno_len + i) > TG3_NVM_VPD_LEN)
goto out_not_found;
memcpy(tp->board_part_number,
@@ -12117,6 +12532,8 @@ out_not_found:
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 &&
tp->pdev->device == TG3PCI_DEVICE_TIGON3_57788)
strcpy(tp->board_part_number, "BCM57788");
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
+ strcpy(tp->board_part_number, "BCM57765");
else
strcpy(tp->board_part_number, "none");
}
@@ -12400,13 +12817,21 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_USE_PROD_ID_REG) {
u32 prod_id_asic_rev;
- if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717C ||
- tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717S ||
- tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718C ||
- tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718S)
+ if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_5717 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_5724)
pci_read_config_dword(tp->pdev,
TG3PCI_GEN2_PRODID_ASICREV,
&prod_id_asic_rev);
+ else if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_57781 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_57785 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_57761 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_57765 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_57791 ||
+ tp->pdev->device == TG3PCI_DEVICE_TIGON3_57795)
+ pci_read_config_dword(tp->pdev,
+ TG3PCI_GEN15_PRODID_ASICREV,
+ &prod_id_asic_rev);
else
pci_read_config_dword(tp->pdev, TG3PCI_PRODID_ASICREV,
&prod_id_asic_rev);
@@ -12560,7 +12985,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
tp->tg3_flags3 |= TG3_FLG3_5755_PLUS;
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 ||
@@ -12586,6 +13012,30 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->dev->features |= NETIF_F_IPV6_CSUM;
}
+ /* Determine TSO capabilities */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
+ tp->tg3_flags2 |= TG3_FLG2_HW_TSO_3;
+ else if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2;
+ else if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) {
+ tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | TG3_FLG2_TSO_BUG;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 &&
+ tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2)
+ tp->tg3_flags2 &= ~TG3_FLG2_TSO_BUG;
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701 &&
+ tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) {
+ tp->tg3_flags2 |= TG3_FLG2_TSO_BUG;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)
+ tp->fw_needed = FIRMWARE_TG3TSO5;
+ else
+ tp->fw_needed = FIRMWARE_TG3TSO;
+ }
+
+ tp->irq_max = 1;
+
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) {
tp->tg3_flags |= TG3_FLAG_SUPPORT_MSI;
if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5750_AX ||
@@ -12597,29 +13047,31 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
- tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2;
tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI;
- } else {
- tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | TG3_FLG2_TSO_BUG;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) ==
- ASIC_REV_5750 &&
- tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2)
- tp->tg3_flags2 &= ~TG3_FLG2_TSO_BUG;
}
- }
- tp->irq_max = 1;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) {
+ tp->tg3_flags |= TG3_FLAG_SUPPORT_MSIX;
+ tp->irq_max = TG3_IRQ_MAX_VECS;
+ }
+ }
-#ifdef TG3_NAPI
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717) {
- tp->tg3_flags |= TG3_FLAG_SUPPORT_MSIX;
- tp->irq_max = TG3_IRQ_MAX_VECS;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ tp->tg3_flags3 |= TG3_FLG3_SHORT_DMA_BUG;
+ else if (!(tp->tg3_flags3 & TG3_FLG3_5755_PLUS)) {
+ tp->tg3_flags3 |= TG3_FLG3_4G_DMA_BNDRY_BUG;
+ tp->tg3_flags3 |= TG3_FLG3_40BIT_DMA_LIMIT_BUG;
}
-#endif
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
+ tp->tg3_flags3 |= TG3_FLG3_USE_JUMBO_BDFLAG;
if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS) ||
(tp->tg3_flags2 & TG3_FLG2_5780_CLASS) ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
+ (tp->tg3_flags3 & TG3_FLG3_USE_JUMBO_BDFLAG))
tp->tg3_flags |= TG3_FLAG_JUMBO_CAPABLE;
pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE,
@@ -12812,7 +13264,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
tp->tg3_flags |= TG3_FLAG_CPMU_PRESENT;
/* Set up tp->grc_local_ctrl before calling tg3_set_power_state().
@@ -12891,7 +13344,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
!(tp->tg3_flags3 & TG3_FLG3_PHY_IS_FET) &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57780 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) {
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 ||
@@ -12926,11 +13380,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
tp->tg3_flags3 |= TG3_FLG3_USE_PHYLIB;
- if ((tp->pci_chip_rev_id == CHIPREV_ID_57780_A1 &&
- tr32(RCVLPC_STATS_ENABLE) & RCVLPC_STATSENAB_ASF_FIX) ||
- tp->pci_chip_rev_id == CHIPREV_ID_57780_A0)
- tp->tg3_flags3 |= TG3_FLG3_TOGGLE_10_100_L1PLLPD;
-
err = tg3_mdio_init(tp);
if (err)
return err;
@@ -13220,6 +13669,12 @@ static u32 __devinit tg3_calc_dma_bndry(struct tg3 *tp, u32 val)
#endif
#endif
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765) {
+ val = goal ? 0 : DMA_RWCTRL_DIS_CACHE_ALIGNMENT;
+ goto out;
+ }
+
if (!goal)
goto out;
@@ -13414,7 +13869,7 @@ static int __devinit tg3_test_dma(struct tg3 *tp)
{
dma_addr_t buf_dma;
u32 *buf, saved_dma_rwctrl;
- int ret;
+ int ret = 0;
buf = pci_alloc_consistent(tp->pdev, TEST_BUFFER_SIZE, &buf_dma);
if (!buf) {
@@ -13427,6 +13882,10 @@ static int __devinit tg3_test_dma(struct tg3 *tp)
tp->dma_rwctrl = tg3_calc_dma_bndry(tp, tp->dma_rwctrl);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57765)
+ goto out;
+
if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) {
/* DMA read watermark not used on PCIE */
tp->dma_rwctrl |= 0x00180000;
@@ -13499,7 +13958,6 @@ static int __devinit tg3_test_dma(struct tg3 *tp)
tg3_switch_clocks(tp);
#endif
- ret = 0;
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701)
goto out;
@@ -13618,7 +14076,8 @@ static void __devinit tg3_init_link_config(struct tg3 *tp)
static void __devinit tg3_init_bufmgr_config(struct tg3 *tp)
{
if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717) {
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5717 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_57765) {
tp->bufmgr_config.mbuf_read_dma_low_water =
DEFAULT_MB_RDMA_LOW_WATER_5705;
tp->bufmgr_config.mbuf_mac_rx_low_water =
@@ -13678,6 +14137,7 @@ static char * __devinit tg3_phy_string(struct tg3 *tp)
case PHY_ID_BCM5756: return "5722/5756";
case PHY_ID_BCM5906: return "5906";
case PHY_ID_BCM5761: return "5761";
+ case PHY_ID_BCM5717: return "5717";
case PHY_ID_BCM8002: return "8002/serdes";
case 0: return "serdes";
default: return "unknown";
@@ -13919,51 +14379,6 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
tp->rx_pending = TG3_DEF_RX_RING_PENDING;
tp->rx_jumbo_pending = TG3_DEF_RX_JUMBO_RING_PENDING;
- intmbx = MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW;
- rcvmbx = MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW;
- sndmbx = MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW;
- for (i = 0; i < TG3_IRQ_MAX_VECS; i++) {
- struct tg3_napi *tnapi = &tp->napi[i];
-
- tnapi->tp = tp;
- tnapi->tx_pending = TG3_DEF_TX_RING_PENDING;
-
- tnapi->int_mbox = intmbx;
- if (i < 4)
- intmbx += 0x8;
- else
- intmbx += 0x4;
-
- tnapi->consmbox = rcvmbx;
- tnapi->prodmbox = sndmbx;
-
- if (i)
- tnapi->coal_now = HOSTCC_MODE_COAL_VEC1_NOW << (i - 1);
- else
- tnapi->coal_now = HOSTCC_MODE_NOW;
-
- if (!(tp->tg3_flags & TG3_FLAG_SUPPORT_MSIX))
- break;
-
- /*
- * If we support MSIX, we'll be using RSS. If we're using
- * RSS, the first vector only handles link interrupts and the
- * remaining vectors handle rx and tx interrupts. Reuse the
- * mailbox values for the next iteration. The values we setup
- * above are still useful for the single vectored mode.
- */
- if (!i)
- continue;
-
- rcvmbx += 0x8;
-
- if (sndmbx & 0x4)
- sndmbx -= 0x4;
- else
- sndmbx += 0xc;
- }
-
- netif_napi_add(dev, &tp->napi[0].napi, tg3_poll, 64);
dev->ethtool_ops = &tg3_ethtool_ops;
dev->watchdog_timeo = TG3_TX_TIMEOUT;
dev->irq = pdev->irq;
@@ -13975,8 +14390,8 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
goto err_out_iounmap;
}
- if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ if ((tp->tg3_flags3 & TG3_FLG3_5755_PLUS) &&
+ tp->pci_chip_rev_id != CHIPREV_ID_5717_A0)
dev->netdev_ops = &tg3_netdev_ops;
else
dev->netdev_ops = &tg3_netdev_ops_dma_bug;
@@ -14023,46 +14438,39 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
tg3_init_bufmgr_config(tp);
- if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0)
- tp->fw_needed = FIRMWARE_TG3;
-
- if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
+ /* Selectively allow TSO based on operating conditions */
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) ||
+ (tp->fw_needed && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)))
tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE;
+ else {
+ tp->tg3_flags2 &= ~(TG3_FLG2_TSO_CAPABLE | TG3_FLG2_TSO_BUG);
+ tp->fw_needed = NULL;
}
- else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 ||
- tp->pci_chip_rev_id == CHIPREV_ID_5705_A0 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 ||
- (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0) {
- tp->tg3_flags2 &= ~TG3_FLG2_TSO_CAPABLE;
- } else {
- tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE | TG3_FLG2_TSO_BUG;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)
- tp->fw_needed = FIRMWARE_TG3TSO5;
- else
- tp->fw_needed = FIRMWARE_TG3TSO;
- }
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0)
+ tp->fw_needed = FIRMWARE_TG3;
/* TSO is on by default on chips that support hardware TSO.
* Firmware TSO on older chips gives lower performance, so it
* is off by default, but can be enabled using ethtool.
*/
- if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
- if (dev->features & NETIF_F_IP_CSUM)
- dev->features |= NETIF_F_TSO;
- if ((dev->features & NETIF_F_IPV6_CSUM) &&
- (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2))
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) &&
+ (dev->features & NETIF_F_IP_CSUM))
+ dev->features |= NETIF_F_TSO;
+
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) ||
+ (tp->tg3_flags2 & TG3_FLG2_HW_TSO_3)) {
+ if (dev->features & NETIF_F_IPV6_CSUM)
dev->features |= NETIF_F_TSO6;
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_3) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 ||
(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 &&
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5717)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780)
dev->features |= NETIF_F_TSO_ECN;
}
-
if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 &&
!(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) &&
!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH)) {
@@ -14074,7 +14482,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
if (err) {
printk(KERN_ERR PFX "Could not obtain valid ethernet address, "
"aborting.\n");
- goto err_out_fw;
+ goto err_out_iounmap;
}
if (tp->tg3_flags3 & TG3_FLG3_ENABLE_APE) {
@@ -14083,7 +14491,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
printk(KERN_ERR PFX "Cannot map APE registers, "
"aborting.\n");
err = -ENOMEM;
- goto err_out_fw;
+ goto err_out_iounmap;
}
tg3_ape_lock_init(tp);
@@ -14113,6 +14521,53 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
tp->tg3_flags |= TG3_FLAG_PAUSE_AUTONEG;
tp->link_config.flowctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
+ intmbx = MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW;
+ rcvmbx = MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW;
+ sndmbx = MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW;
+ for (i = 0; i < TG3_IRQ_MAX_VECS; i++) {
+ struct tg3_napi *tnapi = &tp->napi[i];
+
+ tnapi->tp = tp;
+ tnapi->tx_pending = TG3_DEF_TX_RING_PENDING;
+
+ tnapi->int_mbox = intmbx;
+ if (i < 4)
+ intmbx += 0x8;
+ else
+ intmbx += 0x4;
+
+ tnapi->consmbox = rcvmbx;
+ tnapi->prodmbox = sndmbx;
+
+ if (i) {
+ tnapi->coal_now = HOSTCC_MODE_COAL_VEC1_NOW << (i - 1);
+ netif_napi_add(dev, &tnapi->napi, tg3_poll_msix, 64);
+ } else {
+ tnapi->coal_now = HOSTCC_MODE_NOW;
+ netif_napi_add(dev, &tnapi->napi, tg3_poll, 64);
+ }
+
+ if (!(tp->tg3_flags & TG3_FLAG_SUPPORT_MSIX))
+ break;
+
+ /*
+ * If we support MSIX, we'll be using RSS. If we're using
+ * RSS, the first vector only handles link interrupts and the
+ * remaining vectors handle rx and tx interrupts. Reuse the
+ * mailbox values for the next iteration. The values we setup
+ * above are still useful for the single vectored mode.
+ */
+ if (!i)
+ continue;
+
+ rcvmbx += 0x8;
+
+ if (sndmbx & 0x4)
+ sndmbx -= 0x4;
+ else
+ sndmbx += 0xc;
+ }
+
tg3_init_coal(tp);
pci_set_drvdata(pdev, dev);
@@ -14131,13 +14586,14 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
tg3_bus_string(tp, str),
dev->dev_addr);
- if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED)
+ if (tp->tg3_flags3 & TG3_FLG3_PHY_CONNECTED) {
+ struct phy_device *phydev;
+ phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR];
printk(KERN_INFO
"%s: attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- tp->dev->name,
- tp->mdio_bus->phy_map[PHY_ADDR]->drv->name,
- dev_name(&tp->mdio_bus->phy_map[PHY_ADDR]->dev));
- else
+ tp->dev->name, phydev->drv->name,
+ dev_name(&phydev->dev));
+ } else
printk(KERN_INFO
"%s: attached PHY is %s (%s Ethernet) (WireSpeed[%d])\n",
tp->dev->name, tg3_phy_string(tp),
@@ -14166,10 +14622,6 @@ err_out_apeunmap:
tp->aperegs = NULL;
}
-err_out_fw:
- if (tp->fw)
- release_firmware(tp->fw);
-
err_out_iounmap:
if (tp->regs) {
iounmap(tp->regs);
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index bab7940158e..cd30889650f 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -46,10 +46,15 @@
#define TG3PCI_DEVICE_TIGON3_57788 0x1691
#define TG3PCI_DEVICE_TIGON3_5785_G 0x1699 /* GPHY */
#define TG3PCI_DEVICE_TIGON3_5785_F 0x16a0 /* 10/100 only */
-#define TG3PCI_DEVICE_TIGON3_5717C 0x1655
-#define TG3PCI_DEVICE_TIGON3_5717S 0x1656
-#define TG3PCI_DEVICE_TIGON3_5718C 0x1665
-#define TG3PCI_DEVICE_TIGON3_5718S 0x1666
+#define TG3PCI_DEVICE_TIGON3_5717 0x1655
+#define TG3PCI_DEVICE_TIGON3_5718 0x1656
+#define TG3PCI_DEVICE_TIGON3_5724 0x165c
+#define TG3PCI_DEVICE_TIGON3_57781 0x16b1
+#define TG3PCI_DEVICE_TIGON3_57785 0x16b5
+#define TG3PCI_DEVICE_TIGON3_57761 0x16b0
+#define TG3PCI_DEVICE_TIGON3_57765 0x16b4
+#define TG3PCI_DEVICE_TIGON3_57791 0x16b2
+#define TG3PCI_DEVICE_TIGON3_57795 0x16b6
/* 0x04 --> 0x64 unused */
#define TG3PCI_MSI_DATA 0x00000064
/* 0x66 --> 0x68 unused */
@@ -103,6 +108,7 @@
#define CHIPREV_ID_5906_A1 0xc001
#define CHIPREV_ID_57780_A0 0x57780000
#define CHIPREV_ID_57780_A1 0x57780001
+#define CHIPREV_ID_5717_A0 0x05717000
#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12)
#define ASIC_REV_5700 0x07
#define ASIC_REV_5701 0x00
@@ -122,6 +128,7 @@
#define ASIC_REV_5785 0x5785
#define ASIC_REV_57780 0x57780
#define ASIC_REV_5717 0x5717
+#define ASIC_REV_57765 0x57785
#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8)
#define CHIPREV_5700_AX 0x70
#define CHIPREV_5700_BX 0x71
@@ -141,8 +148,7 @@
#define METAL_REV_B1 0x01
#define METAL_REV_B2 0x02
#define TG3PCI_DMA_RW_CTRL 0x0000006c
-#define DMA_RWCTRL_MIN_DMA 0x000000ff
-#define DMA_RWCTRL_MIN_DMA_SHIFT 0
+#define DMA_RWCTRL_DIS_CACHE_ALIGNMENT 0x00000001
#define DMA_RWCTRL_READ_BNDRY_MASK 0x00000700
#define DMA_RWCTRL_READ_BNDRY_DISAB 0x00000000
#define DMA_RWCTRL_READ_BNDRY_16 0x00000100
@@ -221,6 +227,7 @@
/* 0xc0 --> 0xf4 unused */
#define TG3PCI_GEN2_PRODID_ASICREV 0x000000f4
+#define TG3PCI_GEN15_PRODID_ASICREV 0x000000fc
/* 0xf8 --> 0x200 unused */
#define TG3_CORR_ERR_STAT 0x00000110
@@ -242,7 +249,11 @@
#define MAILBOX_GENERAL_7 0x00000258 /* 64-bit */
#define MAILBOX_RELOAD_STAT 0x00000260 /* 64-bit */
#define MAILBOX_RCV_STD_PROD_IDX 0x00000268 /* 64-bit */
+#define TG3_RX_STD_PROD_IDX_REG (MAILBOX_RCV_STD_PROD_IDX + \
+ TG3_64BIT_REG_LOW)
#define MAILBOX_RCV_JUMBO_PROD_IDX 0x00000270 /* 64-bit */
+#define TG3_RX_JMB_PROD_IDX_REG (MAILBOX_RCV_JUMBO_PROD_IDX + \
+ TG3_64BIT_REG_LOW)
#define MAILBOX_RCV_MINI_PROD_IDX 0x00000278 /* 64-bit */
#define MAILBOX_RCVRET_CON_IDX_0 0x00000280 /* 64-bit */
#define MAILBOX_RCVRET_CON_IDX_1 0x00000288 /* 64-bit */
@@ -1264,8 +1275,9 @@
#define WDMAC_MODE_FIFOURUN_ENAB 0x00000080
#define WDMAC_MODE_FIFOOREAD_ENAB 0x00000100
#define WDMAC_MODE_LNGREAD_ENAB 0x00000200
-#define WDMAC_MODE_RX_ACCEL 0x00000400
+#define WDMAC_MODE_RX_ACCEL 0x00000400
#define WDMAC_MODE_STATUS_TAG_FIX 0x20000000
+#define WDMAC_MODE_BURST_ALL_DATA 0xc0000000
#define WDMAC_STATUS 0x00004c04
#define WDMAC_STATUS_TGTABORT 0x00000004
#define WDMAC_STATUS_MSTABORT 0x00000008
@@ -1809,6 +1821,11 @@
#define TG3_OTP_DEFAULT 0x286c1640
+
+/* Hardware Legacy NVRAM layout */
+#define TG3_NVM_VPD_OFF 0x100
+#define TG3_NVM_VPD_LEN 256
+
/* Hardware Selfboot NVRAM layout */
#define TG3_NVM_HWSB_CFG1 0x00000004
#define TG3_NVM_HWSB_CFG1_MAJMSK 0xf8000000
@@ -1953,10 +1970,34 @@
#define NIC_SRAM_MBUF_POOL_BASE5705 0x00010000
#define NIC_SRAM_MBUF_POOL_SIZE5705 0x0000e000
+
/* Currently this is fixed. */
-#define PHY_ADDR 0x01
+#define TG3_PHY_PCIE_ADDR 0x00
+#define TG3_PHY_MII_ADDR 0x01
+
+
+/*** Tigon3 specific PHY PCIE registers. ***/
+
+#define TG3_PCIEPHY_BLOCK_ADDR 0x1f
+#define TG3_PCIEPHY_XGXS_BLK1 0x0801
+#define TG3_PCIEPHY_TXB_BLK 0x0861
+#define TG3_PCIEPHY_BLOCK_SHIFT 4
+
+/* TG3_PCIEPHY_TXB_BLK */
+#define TG3_PCIEPHY_TX0CTRL1 0x15
+#define TG3_PCIEPHY_TX0CTRL1_TXOCM 0x0003
+#define TG3_PCIEPHY_TX0CTRL1_RDCTL 0x0008
+#define TG3_PCIEPHY_TX0CTRL1_TXCMV 0x0030
+#define TG3_PCIEPHY_TX0CTRL1_TKSEL 0x0040
+#define TG3_PCIEPHY_TX0CTRL1_NB_EN 0x0400
-/* Tigon3 specific PHY MII registers. */
+/* TG3_PCIEPHY_XGXS_BLK1 */
+#define TG3_PCIEPHY_PWRMGMT4 0x1a
+#define TG3_PCIEPHY_PWRMGMT4_L1PLLPD_EN 0x0038
+#define TG3_PCIEPHY_PWRMGMT4_LOWPWR_EN 0x4000
+
+
+/*** Tigon3 specific PHY MII registers. ***/
#define TG3_BMCR_SPEED1000 0x0040
#define MII_TG3_CTRL 0x09 /* 1000-baseT control register */
@@ -2055,6 +2096,9 @@
#define MII_TG3_FET_SHDW_MISCCTRL 0x10
#define MII_TG3_FET_SHDW_MISCCTRL_MDIX 0x4000
+#define MII_TG3_FET_SHDW_AUXMODE4 0x1a
+#define MII_TG3_FET_SHDW_AUXMODE4_SBPD 0x0008
+
#define MII_TG3_FET_SHDW_AUXSTAT2 0x1b
#define MII_TG3_FET_SHDW_AUXSTAT2_APD 0x0020
@@ -2410,10 +2454,6 @@ struct ring_info {
DECLARE_PCI_UNMAP_ADDR(mapping)
};
-struct tx_ring_info {
- struct sk_buff *skb;
-};
-
struct tg3_config_info {
u32 flags;
};
@@ -2542,8 +2582,10 @@ struct tg3_ethtool_stats {
};
struct tg3_rx_prodring_set {
- u32 rx_std_ptr;
- u32 rx_jmb_ptr;
+ u32 rx_std_prod_idx;
+ u32 rx_std_cons_idx;
+ u32 rx_jmb_prod_idx;
+ u32 rx_jmb_cons_idx;
struct tg3_rx_buffer_desc *rx_std;
struct tg3_ext_rx_buffer_desc *rx_jmb;
struct ring_info *rx_std_buffers;
@@ -2571,10 +2613,11 @@ struct tg3_napi {
u32 consmbox;
u32 rx_rcb_ptr;
u16 *rx_rcb_prod_idx;
+ struct tg3_rx_prodring_set *prodring;
struct tg3_rx_buffer_desc *rx_rcb;
struct tg3_tx_buffer_desc *tx_ring;
- struct tx_ring_info *tx_buffers;
+ struct ring_info *tx_buffers;
dma_addr_t status_mapping;
dma_addr_t rx_rcb_mapping;
@@ -2654,7 +2697,7 @@ struct tg3 {
struct vlan_group *vlgrp;
#endif
- struct tg3_rx_prodring_set prodring[1];
+ struct tg3_rx_prodring_set prodring[TG3_IRQ_MAX_VECS - 1];
/* begin "everything else" cacheline(s) section */
@@ -2725,7 +2768,7 @@ struct tg3 {
#define TG3_FLG2_SERDES_PREEMPHASIS 0x00020000
#define TG3_FLG2_5705_PLUS 0x00040000
#define TG3_FLG2_5750_PLUS 0x00080000
-#define TG3_FLG2_PROTECTED_NVRAM 0x00100000
+#define TG3_FLG2_HW_TSO_3 0x00100000
#define TG3_FLG2_USING_MSI 0x00200000
#define TG3_FLG2_USING_MSIX 0x00400000
#define TG3_FLG2_USING_MSI_OR_MSIX (TG3_FLG2_USING_MSI | \
@@ -2737,7 +2780,9 @@ struct tg3 {
#define TG3_FLG2_ICH_WORKAROUND 0x02000000
#define TG3_FLG2_5780_CLASS 0x04000000
#define TG3_FLG2_HW_TSO_2 0x08000000
-#define TG3_FLG2_HW_TSO (TG3_FLG2_HW_TSO_1 | TG3_FLG2_HW_TSO_2)
+#define TG3_FLG2_HW_TSO (TG3_FLG2_HW_TSO_1 | \
+ TG3_FLG2_HW_TSO_2 | \
+ TG3_FLG2_HW_TSO_3)
#define TG3_FLG2_1SHOT_MSI 0x10000000
#define TG3_FLG2_PHY_JITTER_BUG 0x20000000
#define TG3_FLG2_NO_FWARE_REPORTED 0x40000000
@@ -2745,6 +2790,7 @@ struct tg3 {
u32 tg3_flags3;
#define TG3_FLG3_NO_NVRAM_ADDR_TRANS 0x00000001
#define TG3_FLG3_ENABLE_APE 0x00000002
+#define TG3_FLG3_PROTECTED_NVRAM 0x00000004
#define TG3_FLG3_5701_DMA_BUG 0x00000008
#define TG3_FLG3_USE_PHYLIB 0x00000010
#define TG3_FLG3_MDIOBUS_INITED 0x00000020
@@ -2756,9 +2802,13 @@ struct tg3 {
#define TG3_FLG3_PHY_ENABLE_APD 0x00001000
#define TG3_FLG3_5755_PLUS 0x00002000
#define TG3_FLG3_NO_NVRAM 0x00004000
-#define TG3_FLG3_TOGGLE_10_100_L1PLLPD 0x00008000
#define TG3_FLG3_PHY_IS_FET 0x00010000
#define TG3_FLG3_ENABLE_RSS 0x00020000
+#define TG3_FLG3_ENABLE_TSS 0x00040000
+#define TG3_FLG3_4G_DMA_BNDRY_BUG 0x00080000
+#define TG3_FLG3_40BIT_DMA_LIMIT_BUG 0x00100000
+#define TG3_FLG3_SHORT_DMA_BUG 0x00200000
+#define TG3_FLG3_USE_JUMBO_BDFLAG 0x00400000
struct timer_list timer;
u16 timer_counter;
@@ -2825,6 +2875,7 @@ struct tg3 {
#define PHY_ID_BCM5756 0xbc050ed0
#define PHY_ID_BCM5784 0xbc050fa0
#define PHY_ID_BCM5761 0xbc050fd0
+#define PHY_ID_BCM5717 0x5c0d8a00
#define PHY_ID_BCM5906 0xdc00ac40
#define PHY_ID_BCM8002 0x60010140
#define PHY_ID_INVALID 0xffffffff
@@ -2834,6 +2885,7 @@ struct tg3 {
#define PHY_REV_BCM5401_C0 0x6
#define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */
#define TG3_PHY_ID_BCM50610 0x143bd60
+#define TG3_PHY_ID_BCM50610M 0x143bd70
#define TG3_PHY_ID_BCMAC131 0x143bc70
#define TG3_PHY_ID_RTL8211C 0x001cc910
#define TG3_PHY_ID_RTL8201E 0x00008200
@@ -2846,8 +2898,9 @@ struct tg3 {
u32 led_ctrl;
u32 phy_otp;
- char board_part_number[24];
-#define TG3_VER_SIZE 32
+#define TG3_BPN_SIZE 24
+ char board_part_number[TG3_BPN_SIZE];
+#define TG3_VER_SIZE ETHTOOL_FWVERS_LEN
char fw_ver[TG3_VER_SIZE];
u32 nic_sram_data_cfg;
u32 pci_clock_ctrl;
@@ -2865,7 +2918,7 @@ struct tg3 {
(X) == PHY_ID_BCM5780 || (X) == PHY_ID_BCM5787 || \
(X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM5756 || \
(X) == PHY_ID_BCM5906 || (X) == PHY_ID_BCM5761 || \
- (X) == PHY_ID_BCM8002)
+ (X) == PHY_ID_BCM5717 || (X) == PHY_ID_BCM8002)
struct tg3_hw_stats *hw_stats;
dma_addr_t stats_mapping;
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index 3d31b47332b..fabaeffb315 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -1549,7 +1549,8 @@ static u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int )
if (tmpCStat & TLAN_CSTAT_EOC)
eoc = 1;
- new_skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 );
+ new_skb = netdev_alloc_skb_ip_align(dev,
+ TLAN_MAX_FRAME_SIZE + 5);
if ( !new_skb )
goto drop_and_reuse;
@@ -1563,7 +1564,6 @@ static u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int )
skb->protocol = eth_type_trans( skb, dev );
netif_rx( skb );
- skb_reserve( new_skb, NET_IP_ALIGN );
head_list->buffer[0].address = pci_map_single(priv->pciDev,
new_skb->data,
TLAN_MAX_FRAME_SIZE,
@@ -1755,8 +1755,8 @@ static u32 TLan_HandleStatusCheck( struct net_device *dev, u16 host_int )
! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
tlphy_ctl |= TLAN_TC_SWAPOL;
TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
- } else if ( ( tlphy_sts & TLAN_TS_POLOK )
- && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+ } else if ( ( tlphy_sts & TLAN_TS_POLOK ) &&
+ ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
tlphy_ctl &= ~TLAN_TC_SWAPOL;
TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
}
@@ -1967,13 +1967,12 @@ static void TLan_ResetLists( struct net_device *dev )
list->cStat = TLAN_CSTAT_READY;
list->frameSize = TLAN_MAX_FRAME_SIZE;
list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
- skb = netdev_alloc_skb(dev, TLAN_MAX_FRAME_SIZE + 7 );
+ skb = netdev_alloc_skb_ip_align(dev, TLAN_MAX_FRAME_SIZE + 5);
if ( !skb ) {
pr_err("TLAN: out of memory for received data.\n" );
break;
}
- skb_reserve( skb, NET_IP_ALIGN );
list->buffer[0].address = pci_map_single(priv->pciDev,
skb->data,
TLAN_MAX_FRAME_SIZE,
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index 724158966ec..cf552d1d962 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -610,9 +610,8 @@ static int xl_open(struct net_device *dev)
u16 switchsettings, switchsettings_eeprom ;
- if(request_irq(dev->irq, &xl_interrupt, IRQF_SHARED , "3c359", dev)) {
+ if (request_irq(dev->irq, xl_interrupt, IRQF_SHARED , "3c359", dev))
return -EAGAIN;
- }
/*
* Read the information from the EEPROM that we need.
diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c
index 75fa32e34fd..66272f2a075 100644
--- a/drivers/net/tokenring/ibmtr.c
+++ b/drivers/net/tokenring/ibmtr.c
@@ -96,7 +96,7 @@
*
* Change by Mike Sullivan et al.:
* + added turbo card support. No need to use lanaid to configure
- * the adapter into isa compatiblity mode.
+ * the adapter into isa compatibility mode.
*
* Changes by Burt Silverman to allow the computer to behave nicely when
* a cable is pulled or not in place, or a PCMCIA card is removed hot.
@@ -680,7 +680,7 @@ static int __devinit ibmtr_probe1(struct net_device *dev, int PIOaddr)
/* The PCMCIA has already got the interrupt line and the io port,
so no chance of anybody else getting it - MLP */
- if (request_irq(dev->irq = irq, &tok_interrupt, 0, "ibmtr", dev) != 0) {
+ if (request_irq(dev->irq = irq, tok_interrupt, 0, "ibmtr", dev) != 0) {
DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n",
irq);
iounmap(t_mmio);
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index 26dca2b2bdb..d6ccd59c7d0 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -596,7 +596,7 @@ static int streamer_open(struct net_device *dev)
rc=streamer_reset(dev);
}
- if (request_irq(dev->irq, &streamer_interrupt, IRQF_SHARED, "lanstreamer", dev)) {
+ if (request_irq(dev->irq, streamer_interrupt, IRQF_SHARED, "lanstreamer", dev)) {
return -EAGAIN;
}
#if STREAMER_DEBUG
@@ -712,8 +712,8 @@ static int streamer_open(struct net_device *dev)
strcat(open_error, " - ");
strcat(open_error, open_min_error[(error_code & 0x0f)]);
- if (!streamer_priv->streamer_ring_speed
- && ((error_code & 0x0f) == 0x0d))
+ if (!streamer_priv->streamer_ring_speed &&
+ ((error_code & 0x0f) == 0x0d))
{
printk(KERN_WARNING "%s: Tried to autosense ring speed with no monitors present\n", dev->name);
printk(KERN_WARNING "%s: Please try again with a specified ring speed \n", dev->name);
@@ -1032,8 +1032,8 @@ static irqreturn_t streamer_interrupt(int irq, void *dev_id)
sisr = readw(streamer_mmio + SISR);
while((sisr & (SISR_MI | SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE |
- SISR_ARB_CMD | SISR_TRB_REPLY | SISR_PAR_ERR | SISR_SERR_ERR))
- && (max_intr > 0)) {
+ SISR_ARB_CMD | SISR_TRB_REPLY | SISR_PAR_ERR | SISR_SERR_ERR)) &&
+ (max_intr > 0)) {
if(sisr & SISR_PAR_ERR) {
writew(~SISR_PAR_ERR, streamer_mmio + SISR_RUM);
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index d9ec7f0bbd0..df32025c513 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -445,9 +445,9 @@ static int olympic_open(struct net_device *dev)
olympic_init(dev);
- if(request_irq(dev->irq, &olympic_interrupt, IRQF_SHARED , "olympic", dev)) {
+ if (request_irq(dev->irq, olympic_interrupt, IRQF_SHARED , "olympic",
+ dev))
return -EAGAIN;
- }
#if OLYMPIC_DEBUG
printk("BMCTL: %x\n",readl(olympic_mmio+BMCTL_SUM));
diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c
index ebda61bc4c2..5401d86a7be 100644
--- a/drivers/net/tokenring/smctr.c
+++ b/drivers/net/tokenring/smctr.c
@@ -426,7 +426,7 @@ static int smctr_alloc_shared_memory(struct net_device *dev)
smctr_malloc(dev, 1L);
/* Allocate Non-MAC receive data buffers.
- * To guarantee a minimum of 256 contigous memory to
+ * To guarantee a minimum of 256 contiguous memory to
* UM_Receive_Packet's lookahead pointer, before a page
* change or ring end is encountered, place each rx buffer on
* a 256 byte boundary.
@@ -2309,9 +2309,9 @@ static irqreturn_t smctr_interrupt(int irq, void *dev_id)
else
{
if((tp->acb_head->cmd
- == ACB_CMD_READ_TRC_STATUS)
- && (tp->acb_head->subcmd
- == RW_TRC_STATUS_BLOCK))
+ == ACB_CMD_READ_TRC_STATUS) &&
+ (tp->acb_head->subcmd
+ == RW_TRC_STATUS_BLOCK))
{
if(tp->ptr_bcn_type)
{
@@ -2331,8 +2331,8 @@ static irqreturn_t smctr_interrupt(int irq, void *dev_id)
smctr_disable_16bit(dev);
err = smctr_ring_status_chg(dev);
smctr_enable_16bit(dev);
- if((tp->ring_status & REMOVE_RECEIVED)
- && (tp->config_word0 & NO_AUTOREMOVE))
+ if((tp->ring_status & REMOVE_RECEIVED) &&
+ (tp->config_word0 & NO_AUTOREMOVE))
{
smctr_issue_remove_cmd(dev);
}
@@ -2511,9 +2511,9 @@ static int smctr_issue_init_timers_cmd(struct net_device *dev)
tp->config_word0 = THDREN | DMA_TRIGGER | USETPT | NO_AUTOREMOVE;
tp->config_word1 = 0;
- if((tp->media_type == MEDIA_STP_16)
- || (tp->media_type == MEDIA_UTP_16)
- || (tp->media_type == MEDIA_STP_16_UTP_16))
+ if((tp->media_type == MEDIA_STP_16) ||
+ (tp->media_type == MEDIA_UTP_16) ||
+ (tp->media_type == MEDIA_STP_16_UTP_16))
{
tp->config_word0 |= FREQ_16MB_BIT;
}
@@ -2556,9 +2556,9 @@ static int smctr_issue_init_timers_cmd(struct net_device *dev)
tp->config_word1 &= ~SOURCE_ROUTING_SPANNING_BITS;
}
- if((tp->media_type == MEDIA_STP_16)
- || (tp->media_type == MEDIA_UTP_16)
- || (tp->media_type == MEDIA_STP_16_UTP_16))
+ if((tp->media_type == MEDIA_STP_16) ||
+ (tp->media_type == MEDIA_UTP_16) ||
+ (tp->media_type == MEDIA_STP_16_UTP_16))
{
tp->config_word1 |= INTERFRAME_SPACING_16;
}
@@ -2568,9 +2568,9 @@ static int smctr_issue_init_timers_cmd(struct net_device *dev)
*pTimer_Struc++ = tp->config_word0;
*pTimer_Struc++ = tp->config_word1;
- if((tp->media_type == MEDIA_STP_4)
- || (tp->media_type == MEDIA_UTP_4)
- || (tp->media_type == MEDIA_STP_4_UTP_4))
+ if((tp->media_type == MEDIA_STP_4) ||
+ (tp->media_type == MEDIA_UTP_4) ||
+ (tp->media_type == MEDIA_STP_4_UTP_4))
{
*pTimer_Struc++ = 0x00FA; /* prescale */
*pTimer_Struc++ = 0x2710; /* TPT_limit */
@@ -2990,8 +2990,8 @@ static int smctr_load_firmware(struct net_device *dev)
}
/* Verify the firmware exists and is there in the right amount. */
- if (!fw->data
- || (*(fw->data + UCODE_VERSION_OFFSET) < UCODE_VERSION))
+ if (!fw->data ||
+ (*(fw->data + UCODE_VERSION_OFFSET) < UCODE_VERSION))
{
err = (UCODE_NOT_PRESENT);
goto out;
@@ -3010,9 +3010,8 @@ static int smctr_load_firmware(struct net_device *dev)
smctr_enable_16bit(dev);
smctr_set_page(dev, (__u8 *)tp->ram_access);
- if((smctr_checksum_firmware(dev))
- || (*(fw->data + UCODE_VERSION_OFFSET)
- > tp->microcode_version))
+ if((smctr_checksum_firmware(dev)) ||
+ (*(fw->data + UCODE_VERSION_OFFSET) > tp->microcode_version))
{
smctr_enable_adapter_ctrl_store(dev);
@@ -3117,9 +3116,9 @@ static int smctr_lobe_media_test(struct net_device *dev)
}
/* Check if any frames received during test. */
- if((tp->rx_fcb_curr[MAC_QUEUE]->frame_status)
- || (tp->rx_fcb_curr[NON_MAC_QUEUE]->frame_status))
- goto err;
+ if((tp->rx_fcb_curr[MAC_QUEUE]->frame_status) ||
+ (tp->rx_fcb_curr[NON_MAC_QUEUE]->frame_status))
+ goto err;
/* Set receive mask to "Promisc" mode. */
tp->receive_mask = saved_rcv_mask;
@@ -3303,8 +3302,8 @@ static int smctr_make_group_addr(struct net_device *dev, MAC_SUB_VECTOR *tsv)
/* Set Group Address Sub-vector to all zeros if only the
* Group Address/Functional Address Indicator is set.
*/
- if(tsv->svv[0] == 0x80 && tsv->svv[1] == 0x00
- && tsv->svv[2] == 0x00 && tsv->svv[3] == 0x00)
+ if(tsv->svv[0] == 0x80 && tsv->svv[1] == 0x00 &&
+ tsv->svv[2] == 0x00 && tsv->svv[3] == 0x00)
tsv->svv[0] = 0x00;
return (0);
@@ -3876,10 +3875,10 @@ static int smctr_process_rx_packet(MAC_HEADER *rmf, __u16 size,
/* NOTE: UNKNOWN MAC frames will NOT be passed up unless
* ACCEPT_ATT_MAC_FRAMES is set.
*/
- if(((tp->receive_mask & ACCEPT_ATT_MAC_FRAMES)
- && (xframe == (__u8)0))
- || ((tp->receive_mask & ACCEPT_EXT_MAC_FRAMES)
- && (xframe == (__u8)1)))
+ if(((tp->receive_mask & ACCEPT_ATT_MAC_FRAMES) &&
+ (xframe == (__u8)0)) ||
+ ((tp->receive_mask & ACCEPT_EXT_MAC_FRAMES) &&
+ (xframe == (__u8)1)))
{
rmf->vl = SWAP_BYTES(rmf->vl);
@@ -3934,8 +3933,8 @@ static int smctr_ram_memory_test(struct net_device *dev)
word_pattern = start_pattern;
- for(j = 1; j < (__u32)(tp->ram_usable * 1024) - 1
- && (~err); j += 2, word_pattern++)
+ for(j = 1; j < (__u32)(tp->ram_usable * 1024) - 1 && (~err);
+ j += 2, word_pattern++)
{
word_read = *(__u16 *)(pword + j);
if(word_read != word_pattern)
@@ -3959,8 +3958,7 @@ static int smctr_ram_memory_test(struct net_device *dev)
for(j = 0; j < (__u32)tp->ram_usable * 1024; j +=2)
*(__u16 *)(pword + j) = word_pattern;
- for(j =0; j < (__u32)tp->ram_usable * 1024
- && (~err); j += 2)
+ for(j =0; j < (__u32)tp->ram_usable * 1024 && (~err); j += 2)
{
word_read = *(__u16 *)(pword + j);
if(word_read != word_pattern)
@@ -4325,8 +4323,8 @@ static int smctr_restart_tx_chain(struct net_device *dev, short queue)
if(smctr_debug > 10)
printk(KERN_DEBUG "%s: smctr_restart_tx_chain\n", dev->name);
- if(tp->num_tx_fcbs_used[queue] != 0
- && tp->tx_queue_status[queue] == NOT_TRANSMITING)
+ if(tp->num_tx_fcbs_used[queue] != 0 &&
+ tp->tx_queue_status[queue] == NOT_TRANSMITING)
{
tp->tx_queue_status[queue] = TRANSMITING;
err = smctr_issue_resume_tx_fcb_cmd(dev, queue);
@@ -4349,8 +4347,8 @@ static int smctr_ring_status_chg(struct net_device *dev)
*/
if(tp->ring_status_flags == MONITOR_STATE_CHANGED)
{
- if((tp->monitor_state == MS_ACTIVE_MONITOR_STATE)
- || (tp->monitor_state == MS_STANDBY_MONITOR_STATE))
+ if((tp->monitor_state == MS_ACTIVE_MONITOR_STATE) ||
+ (tp->monitor_state == MS_STANDBY_MONITOR_STATE))
{
tp->monitor_state_ready = 1;
}
@@ -4363,8 +4361,8 @@ static int smctr_ring_status_chg(struct net_device *dev)
tp->monitor_state_ready = 0;
/* Ring speed problem, switching to auto mode. */
- if(tp->monitor_state == MS_MONITOR_FSM_INACTIVE
- && !tp->cleanup)
+ if(tp->monitor_state == MS_MONITOR_FSM_INACTIVE &&
+ !tp->cleanup)
{
printk(KERN_INFO "%s: Incorrect ring speed switching.\n",
dev->name);
@@ -4442,8 +4440,8 @@ static int smctr_rx_frame(struct net_device *dev)
{
err = HARDWARE_FAILED;
- if(((status & 0x007f) == 0)
- || ((tp->receive_mask & ACCEPT_ERR_PACKETS) != 0))
+ if(((status & 0x007f) == 0) ||
+ ((tp->receive_mask & ACCEPT_ERR_PACKETS) != 0))
{
/* frame length less the CRC (4 bytes) + FS (1 byte) */
rx_size = tp->rx_fcb_curr[queue]->frame_length - 5;
@@ -4538,8 +4536,8 @@ static int smctr_send_dat(struct net_device *dev)
}
/* Check if GOOD frame Tx'ed. */
- if(!(fcb->frame_status & FCB_COMMAND_DONE)
- || fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS))
+ if(!(fcb->frame_status & FCB_COMMAND_DONE) ||
+ fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS))
{
return (INITIALIZE_FAILED);
}
@@ -4653,8 +4651,8 @@ static int smctr_send_lobe_media_test(struct net_device *dev)
}
/* Check if GOOD frame Tx'ed */
- if(!(fcb->frame_status & FCB_COMMAND_DONE)
- || fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS))
+ if(!(fcb->frame_status & FCB_COMMAND_DONE) ||
+ fcb->frame_status & (FCB_TX_STATUS_E | FCB_TX_AC_BITS))
{
return (LOBE_MEDIA_TEST_FAILED);
}
diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c
index a7b6888829b..e3c42f5ac4a 100644
--- a/drivers/net/tokenring/tms380tr.c
+++ b/drivers/net/tokenring/tms380tr.c
@@ -729,8 +729,8 @@ static void tms380tr_timer_chk(unsigned long data)
return;
tms380tr_chk_outstanding_cmds(dev);
- if(time_before(tp->LastSendTime + SEND_TIMEOUT, jiffies)
- && (tp->TplFree != tp->TplBusy))
+ if(time_before(tp->LastSendTime + SEND_TIMEOUT, jiffies) &&
+ (tp->TplFree != tp->TplBusy))
{
/* Anything to send, but stalled too long */
tp->LastSendTime = jiffies;
@@ -830,8 +830,8 @@ irqreturn_t tms380tr_interrupt(int irq, void *dev_id)
}
/* Reset system interrupt if not already done. */
- if(irq_type != STS_IRQ_TRANSMIT_STATUS
- && irq_type != STS_IRQ_RECEIVE_STATUS) {
+ if(irq_type != STS_IRQ_TRANSMIT_STATUS &&
+ irq_type != STS_IRQ_RECEIVE_STATUS) {
tms380tr_reset_interrupt(dev);
}
@@ -895,10 +895,10 @@ static unsigned char tms380tr_chk_ssb(struct net_local *tp, unsigned short IrqTy
/* Check if this interrupt does use the SSB. */
- if(IrqType != STS_IRQ_TRANSMIT_STATUS
- && IrqType != STS_IRQ_RECEIVE_STATUS
- && IrqType != STS_IRQ_COMMAND_STATUS
- && IrqType != STS_IRQ_RING_STATUS)
+ if(IrqType != STS_IRQ_TRANSMIT_STATUS &&
+ IrqType != STS_IRQ_RECEIVE_STATUS &&
+ IrqType != STS_IRQ_COMMAND_STATUS &&
+ IrqType != STS_IRQ_RING_STATUS)
{
return (1); /* SSB not involved. */
}
@@ -1364,6 +1364,8 @@ static int tms380tr_reset_adapter(struct net_device *dev)
return (-1);
}
+MODULE_FIRMWARE("tms380tr.bin");
+
/*
* Starts bring up diagnostics of token ring adapter and evaluates
* diagnostic results.
@@ -1483,8 +1485,8 @@ static int tms380tr_init_adapter(struct net_device *dev)
/* Mask interesting status bits */
Status = SIFREADW(SIFSTS);
Status &= STS_MASK;
- } while(((Status &(STS_INITIALIZE | STS_ERROR | STS_TEST)) != 0)
- && ((Status & STS_ERROR) == 0) && (loop_cnt != 0));
+ } while(((Status &(STS_INITIALIZE | STS_ERROR | STS_TEST)) != 0) &&
+ ((Status & STS_ERROR) == 0) && (loop_cnt != 0));
if((Status & (STS_INITIALIZE | STS_ERROR | STS_TEST)) == 0)
{
@@ -2181,8 +2183,8 @@ static void tms380tr_rcv_status_irq(struct net_device *dev)
}
}
- if(skb && (rpl->SkbStat == SKB_DATA_COPY
- || rpl->SkbStat == SKB_DMA_DIRECT))
+ if(skb && (rpl->SkbStat == SKB_DATA_COPY ||
+ rpl->SkbStat == SKB_DMA_DIRECT))
{
if(rpl->SkbStat == SKB_DATA_COPY)
skb_copy_to_linear_data(skb, ReceiveDataPtr,
diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c
index 7030bd5e984..a69c4a48bab 100644
--- a/drivers/net/tsi108_eth.c
+++ b/drivers/net/tsi108_eth.c
@@ -802,13 +802,11 @@ static int tsi108_refill_rx(struct net_device *dev, int budget)
int rx = data->rxhead;
struct sk_buff *skb;
- data->rxskbs[rx] = skb = netdev_alloc_skb(dev,
- TSI108_RXBUF_SIZE + 2);
+ skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE);
+ data->rxskbs[rx] = skb;
if (!skb)
break;
- skb_reserve(skb, 2); /* Align the data on a 4-byte boundary. */
-
data->rxring[rx].buf0 = dma_map_single(NULL, skb->data,
TSI108_RX_SKB_SIZE,
DMA_FROM_DEVICE);
@@ -1356,7 +1354,7 @@ static int tsi108_open(struct net_device *dev)
for (i = 0; i < TSI108_RXRING_LEN; i++) {
struct sk_buff *skb;
- skb = netdev_alloc_skb(dev, TSI108_RXBUF_SIZE + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(dev, TSI108_RXBUF_SIZE);
if (!skb) {
/* Bah. No memory for now, but maybe we'll get
* some more later.
@@ -1370,8 +1368,6 @@ static int tsi108_open(struct net_device *dev)
}
data->rxskbs[i] = skb;
- /* Align the payload on a 4-byte boundary */
- skb_reserve(skb, 2);
data->rxskbs[i] = skb;
data->rxring[i].buf0 = virt_to_phys(data->rxskbs[i]->data);
data->rxring[i].misc = TSI108_RX_OWN | TSI108_RX_INT;
diff --git a/drivers/net/tulip/21142.c b/drivers/net/tulip/21142.c
index db7d5e11855..9f6742fad6c 100644
--- a/drivers/net/tulip/21142.c
+++ b/drivers/net/tulip/21142.c
@@ -209,10 +209,10 @@ void t21142_lnk_change(struct net_device *dev, int csr5)
printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 %8.8x.\n",
dev->name, tp->csr6, ioread32(ioaddr + CSR6),
ioread32(ioaddr + CSR12));
- } else if ((tp->nwayset && (csr5 & 0x08000000)
- && (dev->if_port == 3 || dev->if_port == 5)
- && (csr12 & 2) == 2) ||
- (tp->nway && (csr5 & (TPLnkFail)))) {
+ } else if ((tp->nwayset && (csr5 & 0x08000000) &&
+ (dev->if_port == 3 || dev->if_port == 5) &&
+ (csr12 & 2) == 2) ||
+ (tp->nway && (csr5 & (TPLnkFail)))) {
/* Link blew? Maybe restart NWay. */
del_timer_sync(&tp->timer);
t21142_start_nway(dev);
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c
index 74e5ba42d38..d4255d44cb7 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/tulip/de2104x.c
@@ -62,9 +62,9 @@ module_param (debug, int, 0);
MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number");
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
- || defined(CONFIG_SPARC) || defined(__ia64__) \
- || defined(__sh__) || defined(__mips__)
+#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \
+ defined(CONFIG_SPARC) || defined(__ia64__) || \
+ defined(__sh__) || defined(__mips__)
static int rx_copybreak = 1518;
#else
static int rx_copybreak = 100;
diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c
index a45ded0538b..ad63621913c 100644
--- a/drivers/net/tulip/dmfe.c
+++ b/drivers/net/tulip/dmfe.c
@@ -543,7 +543,7 @@ static int dmfe_open(struct DEVICE *dev)
DMFE_DBUG(0, "dmfe_open", 0);
- ret = request_irq(dev->irq, &dmfe_interrupt,
+ ret = request_irq(dev->irq, dmfe_interrupt,
IRQF_SHARED, dev->name, dev);
if (ret)
return ret;
diff --git a/drivers/net/tulip/eeprom.c b/drivers/net/tulip/eeprom.c
index 391acd32a6a..889f57aae89 100644
--- a/drivers/net/tulip/eeprom.c
+++ b/drivers/net/tulip/eeprom.c
@@ -174,10 +174,10 @@ void __devinit tulip_parse_eeprom(struct net_device *dev)
}
/* Do a fix-up based on the vendor half of the station address prefix. */
for (i = 0; eeprom_fixups[i].name; i++) {
- if (dev->dev_addr[0] == eeprom_fixups[i].addr0
- && dev->dev_addr[1] == eeprom_fixups[i].addr1
- && dev->dev_addr[2] == eeprom_fixups[i].addr2) {
- if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
+ if (dev->dev_addr[0] == eeprom_fixups[i].addr0 &&
+ dev->dev_addr[1] == eeprom_fixups[i].addr1 &&
+ dev->dev_addr[2] == eeprom_fixups[i].addr2) {
+ if (dev->dev_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
i++; /* An Accton EN1207, not an outlaw Maxtech. */
memcpy(ee_data + 26, eeprom_fixups[i].newtable,
sizeof(eeprom_fixups[i].newtable));
diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c
index c8d220cf2cc..2e8e8ee893c 100644
--- a/drivers/net/tulip/interrupt.c
+++ b/drivers/net/tulip/interrupt.c
@@ -170,8 +170,8 @@ int tulip_poll(struct napi_struct *napi, int budget)
RxDescCollisionSeen |
RxDescRunt |
RxDescDescErr |
- RxWholePkt)) != RxWholePkt
- || pkt_len > 1518) {
+ RxWholePkt)) != RxWholePkt ||
+ pkt_len > 1518) {
if ((status & (RxLengthOver2047 |
RxWholePkt)) != RxWholePkt) {
/* Ingore earlier buffers. */
@@ -201,8 +201,8 @@ int tulip_poll(struct napi_struct *napi, int budget)
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < tulip_rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < tulip_rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(tp->pdev,
tp->rx_buffers[entry].mapping,
@@ -395,8 +395,8 @@ static int tulip_rx(struct net_device *dev)
RxDescCollisionSeen |
RxDescRunt |
RxDescDescErr |
- RxWholePkt)) != RxWholePkt
- || pkt_len > 1518) {
+ RxWholePkt)) != RxWholePkt ||
+ pkt_len > 1518) {
if ((status & (RxLengthOver2047 |
RxWholePkt)) != RxWholePkt) {
/* Ingore earlier buffers. */
@@ -425,8 +425,8 @@ static int tulip_rx(struct net_device *dev)
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < tulip_rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < tulip_rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(tp->pdev,
tp->rx_buffers[entry].mapping,
diff --git a/drivers/net/tulip/media.c b/drivers/net/tulip/media.c
index daddfa51853..d8fda83705b 100644
--- a/drivers/net/tulip/media.c
+++ b/drivers/net/tulip/media.c
@@ -468,8 +468,8 @@ void __devinit tulip_find_mii (struct net_device *dev, int board_idx)
int phy = phyn & 0x1f;
int mii_status = tulip_mdio_read (dev, phy, MII_BMSR);
if ((mii_status & 0x8301) == 0x8001 ||
- ((mii_status & BMSR_100BASE4) == 0
- && (mii_status & 0x7800) != 0)) {
+ ((mii_status & BMSR_100BASE4) == 0 &&
+ (mii_status & 0x7800) != 0)) {
/* preserve Becker logic, gain indentation level */
} else {
continue;
diff --git a/drivers/net/tulip/pnic2.c b/drivers/net/tulip/pnic2.c
index f49579128fb..d8418694bf4 100644
--- a/drivers/net/tulip/pnic2.c
+++ b/drivers/net/tulip/pnic2.c
@@ -316,9 +316,9 @@ void pnic2_lnk_change(struct net_device *dev, int csr5)
}
}
- if ((tp->nwayset && (csr5 & 0x08000000)
- && (dev->if_port == 3 || dev->if_port == 5)
- && (csr12 & 2) == 2) || (tp->nway && (csr5 & (TPLnkFail)))) {
+ if ((tp->nwayset && (csr5 & 0x08000000) &&
+ (dev->if_port == 3 || dev->if_port == 5) &&
+ (csr12 & 2) == 2) || (tp->nway && (csr5 & (TPLnkFail)))) {
/* Link blew? Maybe restart NWay. */
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index 6b2330e4206..0fa3140d65b 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -64,9 +64,9 @@ const char * const medianame[32] = {
};
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
- || defined(CONFIG_SPARC) || defined(__ia64__) \
- || defined(__sh__) || defined(__mips__)
+#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \
+ defined(CONFIG_SPARC) || defined(__ia64__) || \
+ defined(__sh__) || defined(__mips__)
static int rx_copybreak = 1518;
#else
static int rx_copybreak = 100;
@@ -449,8 +449,8 @@ media_picked:
iowrite32(0x0201B078, ioaddr + 0xB8);
next_tick = 1*HZ;
}
- } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881)
- && ! tp->medialock) {
+ } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) &&
+ ! tp->medialock) {
dev->if_port = 0;
tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
iowrite32(0x0f370000 | ioread16(ioaddr + 0x80), ioaddr + 0x80);
@@ -506,7 +506,7 @@ tulip_open(struct net_device *dev)
tulip_init_ring (dev);
- retval = request_irq(dev->irq, &tulip_interrupt, IRQF_SHARED, dev->name, dev);
+ retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev);
if (retval)
goto free_ring;
@@ -535,9 +535,9 @@ static void tulip_tx_timeout(struct net_device *dev)
if (tulip_debug > 1)
printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
dev->name);
- } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
- || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881
- || tp->chip_id == DM910X) {
+ } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 ||
+ tp->chip_id == MX98713 || tp->chip_id == COMPEX9881 ||
+ tp->chip_id == DM910X) {
printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
"SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
dev->name, ioread32(ioaddr + CSR5), ioread32(ioaddr + CSR12),
@@ -1538,8 +1538,10 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
}
}
/* Lite-On boards have the address byte-swapped. */
- if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0 || dev->dev_addr[0] == 0x02)
- && dev->dev_addr[1] == 0x00)
+ if ((dev->dev_addr[0] == 0xA0 ||
+ dev->dev_addr[0] == 0xC0 ||
+ dev->dev_addr[0] == 0x02) &&
+ dev->dev_addr[1] == 0x00)
for (i = 0; i < 6; i+=2) {
char tmp = dev->dev_addr[i];
dev->dev_addr[i] = dev->dev_addr[i+1];
@@ -1782,7 +1784,7 @@ static int tulip_resume(struct pci_dev *pdev)
return retval;
}
- if ((retval = request_irq(dev->irq, &tulip_interrupt, IRQF_SHARED, dev->name, dev))) {
+ if ((retval = request_irq(dev->irq, tulip_interrupt, IRQF_SHARED, dev->name, dev))) {
printk (KERN_ERR "tulip: request_irq failed in resume\n");
return retval;
}
diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c
index c457a0ca55a..fa019cabc35 100644
--- a/drivers/net/tulip/uli526x.c
+++ b/drivers/net/tulip/uli526x.c
@@ -461,7 +461,7 @@ static int uli526x_open(struct net_device *dev)
/* Initialize ULI526X board */
uli526x_init(dev);
- ret = request_irq(dev->irq, &uli526x_interrupt, IRQF_SHARED, dev->name, dev);
+ ret = request_irq(dev->irq, uli526x_interrupt, IRQF_SHARED, dev->name, dev);
if (ret)
return ret;
diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c
index b38d3b7f6e3..869a7a0005f 100644
--- a/drivers/net/tulip/winbond-840.c
+++ b/drivers/net/tulip/winbond-840.c
@@ -639,7 +639,7 @@ static int netdev_open(struct net_device *dev)
iowrite32(0x00000001, ioaddr + PCIBusCfg); /* Reset */
netif_device_detach(dev);
- i = request_irq(dev->irq, &intr_handler, IRQF_SHARED, dev->name, dev);
+ i = request_irq(dev->irq, intr_handler, IRQF_SHARED, dev->name, dev);
if (i)
goto out_err;
@@ -1230,8 +1230,8 @@ static int netdev_rx(struct net_device *dev)
#endif
/* Check if the packet is long enough to accept without copying
to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak
- && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb_reserve(skb, 2); /* 16 byte align the IP header */
pci_dma_sync_single_for_cpu(np->pci_dev,np->rx_addr[entry],
np->rx_skbuff[entry]->len,
@@ -1357,8 +1357,8 @@ static u32 __set_rx_mode(struct net_device *dev)
memset(mc_filter, 0xff, sizeof(mc_filter));
rx_mode = RxAcceptBroadcast | AcceptMulticast | RxAcceptAllPhys
| AcceptMyPhys;
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to match, or accept all multicasts. */
memset(mc_filter, 0xff, sizeof(mc_filter));
rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys;
diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c
index 0f2ca5980c3..9924c4c7e2d 100644
--- a/drivers/net/tulip/xircom_cb.c
+++ b/drivers/net/tulip/xircom_cb.c
@@ -458,7 +458,7 @@ static int xircom_open(struct net_device *dev)
int retval;
enter("xircom_open");
printk(KERN_INFO "xircom cardbus adaptor found, registering as %s, using irq %i \n",dev->name,dev->irq);
- retval = request_irq(dev->irq, &xircom_interrupt, IRQF_SHARED, dev->name, dev);
+ retval = request_irq(dev->irq, xircom_interrupt, IRQF_SHARED, dev->name, dev);
if (retval) {
leave("xircom_open - No IRQ");
return retval;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 4fdfa2ae541..01e99f22210 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -44,7 +44,6 @@
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/init.h>
@@ -54,6 +53,7 @@
#include <linux/miscdevice.h>
#include <linux/ethtool.h>
#include <linux/rtnetlink.h>
+#include <linux/compat.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
@@ -1110,8 +1110,8 @@ static int set_offload(struct net_device *dev, unsigned long arg)
return 0;
}
-static long tun_chr_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg, int ifreq_len)
{
struct tun_file *tfile = file->private_data;
struct tun_struct *tun;
@@ -1121,7 +1121,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd,
int ret;
if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
- if (copy_from_user(&ifr, argp, sizeof ifr))
+ if (copy_from_user(&ifr, argp, ifreq_len))
return -EFAULT;
if (cmd == TUNGETFEATURES) {
@@ -1144,7 +1144,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd,
if (ret)
goto unlock;
- if (copy_to_user(argp, &ifr, sizeof(ifr)))
+ if (copy_to_user(argp, &ifr, ifreq_len))
ret = -EFAULT;
goto unlock;
}
@@ -1162,7 +1162,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd,
if (ret)
break;
- if (copy_to_user(argp, &ifr, sizeof(ifr)))
+ if (copy_to_user(argp, &ifr, ifreq_len))
ret = -EFAULT;
break;
@@ -1236,7 +1236,7 @@ static long tun_chr_ioctl(struct file *file, unsigned int cmd,
/* Get hw addres */
memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
ifr.ifr_hwaddr.sa_family = tun->dev->type;
- if (copy_to_user(argp, &ifr, sizeof ifr))
+ if (copy_to_user(argp, &ifr, ifreq_len))
ret = -EFAULT;
break;
@@ -1275,6 +1275,41 @@ unlock:
return ret;
}
+static long tun_chr_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return __tun_chr_ioctl(file, cmd, arg, sizeof (struct ifreq));
+}
+
+#ifdef CONFIG_COMPAT
+static long tun_chr_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case TUNSETIFF:
+ case TUNGETIFF:
+ case TUNSETTXFILTER:
+ case TUNGETSNDBUF:
+ case TUNSETSNDBUF:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ arg = (unsigned long)compat_ptr(arg);
+ break;
+ default:
+ arg = (compat_ulong_t)arg;
+ break;
+ }
+
+ /*
+ * compat_ifreq is shorter than ifreq, so we must not access beyond
+ * the end of that structure. All fields that are used in this
+ * driver are compatible though, we don't need to convert the
+ * contents.
+ */
+ return __tun_chr_ioctl(file, cmd, arg, sizeof(struct compat_ifreq));
+}
+#endif /* CONFIG_COMPAT */
+
static int tun_chr_fasync(int fd, struct file *file, int on)
{
struct tun_struct *tun = tun_get(file);
@@ -1285,7 +1320,6 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->dev->name, on);
- lock_kernel();
if ((ret = fasync_helper(fd, file, on, &tun->fasync)) < 0)
goto out;
@@ -1298,7 +1332,6 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
tun->flags &= ~TUN_FASYNC;
ret = 0;
out:
- unlock_kernel();
tun_put(tun);
return ret;
}
@@ -1306,7 +1339,7 @@ out:
static int tun_chr_open(struct inode *inode, struct file * file)
{
struct tun_file *tfile;
- cycle_kernel_lock();
+
DBG1(KERN_INFO "tunX: tun_chr_open\n");
tfile = kmalloc(sizeof(*tfile), GFP_KERNEL);
@@ -1359,7 +1392,10 @@ static const struct file_operations tun_fops = {
.write = do_sync_write,
.aio_write = tun_chr_aio_write,
.poll = tun_chr_poll,
- .unlocked_ioctl = tun_chr_ioctl,
+ .unlocked_ioctl = tun_chr_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = tun_chr_compat_ioctl,
+#endif
.open = tun_chr_open,
.release = tun_chr_close,
.fasync = tun_chr_fasync
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index 5921f5bdd76..39f1fc650be 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -1769,8 +1769,8 @@ typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile __le32 * read
csum_bits = rx->rxStatus & (TYPHOON_RX_IP_CHK_GOOD |
TYPHOON_RX_UDP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD);
if(csum_bits ==
- (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD)
- || csum_bits ==
+ (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD) ||
+ csum_bits ==
(TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_UDP_CHK_GOOD)) {
new_skb->ip_summed = CHECKSUM_UNNECESSARY;
} else
@@ -2151,7 +2151,7 @@ typhoon_open(struct net_device *dev)
goto out_sleep;
}
- err = request_irq(dev->irq, &typhoon_interrupt, IRQF_SHARED,
+ err = request_irq(dev->irq, typhoon_interrupt, IRQF_SHARED,
dev->name, dev);
if(err < 0)
goto out_sleep;
diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c
index 4469f2451a6..afaf088b72e 100644
--- a/drivers/net/ucc_geth.c
+++ b/drivers/net/ucc_geth.c
@@ -1306,8 +1306,8 @@ static int init_max_rx_buff_len(u16 max_rx_buf_len,
u16 __iomem *mrblr_register)
{
/* max_rx_buf_len value must be a multiple of 128 */
- if ((max_rx_buf_len == 0)
- || (max_rx_buf_len % UCC_GETH_MRBLR_ALIGNMENT))
+ if ((max_rx_buf_len == 0) ||
+ (max_rx_buf_len % UCC_GETH_MRBLR_ALIGNMENT))
return -EINVAL;
out_be16(mrblr_register, max_rx_buf_len);
@@ -2159,8 +2159,8 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
}
if ((ug_info->numStationAddresses !=
- UCC_GETH_NUM_OF_STATION_ADDRESSES_1)
- && ug_info->rxExtendedFiltering) {
+ UCC_GETH_NUM_OF_STATION_ADDRESSES_1) &&
+ ug_info->rxExtendedFiltering) {
if (netif_msg_probe(ugeth))
ugeth_err("%s: Number of station addresses greater than 1 "
"not allowed in extended parsing mode.",
@@ -2284,9 +2284,9 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
UCC_GETH_NUM_OF_STATION_ADDRESSES_1);
ugeth->rx_extended_features = ugeth->rx_non_dynamic_extended_features ||
- (ug_info->vlanOperationTagged != UCC_GETH_VLAN_OPERATION_TAGGED_NOP)
- || (ug_info->vlanOperationNonTagged !=
- UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP);
+ (ug_info->vlanOperationTagged != UCC_GETH_VLAN_OPERATION_TAGGED_NOP) ||
+ (ug_info->vlanOperationNonTagged !=
+ UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP);
init_default_reg_vals(&uf_regs->upsmr,
&ug_regs->maccfg1, &ug_regs->maccfg2);
@@ -2987,11 +2987,11 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
ugeth->p_init_enet_param_shadow->rgftgfrxglobal |=
ugeth->rx_glbl_pram_offset | ug_info->riscRx;
if ((ug_info->largestexternallookupkeysize !=
- QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE)
- && (ug_info->largestexternallookupkeysize !=
- QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES)
- && (ug_info->largestexternallookupkeysize !=
- QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) {
+ QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE) &&
+ (ug_info->largestexternallookupkeysize !=
+ QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES) &&
+ (ug_info->largestexternallookupkeysize !=
+ QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) {
if (netif_msg_ifup(ugeth))
ugeth_err("%s: Invalid largest External Lookup Key Size.",
__func__);
@@ -3798,7 +3798,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
prop = of_get_property(np, "tx-clock", NULL);
if (!prop) {
printk(KERN_ERR
- "ucc_geth: mising tx-clock-name property\n");
+ "ucc_geth: missing tx-clock-name property\n");
return -EINVAL;
}
if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) {
diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h
index 03a6ca016d5..a007e2acf65 100644
--- a/drivers/net/ucc_geth.h
+++ b/drivers/net/ucc_geth.h
@@ -80,16 +80,16 @@ struct ucc_geth {
frames) received that were between 128
(Including FCS length==4) and 255 octets */
u32 txok; /* Total number of octets residing in frames
- that where involved in succesfull
+ that where involved in successfull
transmission */
u16 txcf; /* Total number of PAUSE control frames
transmitted by this MAC */
u8 res4[0x2];
u32 tmca; /* Total number of frames that were transmitted
- succesfully with the group address bit set
+ successfully with the group address bit set
that are not broadcast frames */
u32 tbca; /* Total number of frames transmitted
- succesfully that had destination address
+ successfully that had destination address
field equal to the broadcast address */
u32 rxfok; /* Total number of frames received OK */
u32 rxbok; /* Total number of octets received OK */
@@ -98,9 +98,9 @@ struct ucc_geth {
HW because it includes octets in frames that
never even reach the UCC */
u32 rmca; /* Total number of frames that were received
- succesfully with the group address bit set
+ successfully with the group address bit set
that are not broadcast frames */
- u32 rbca; /* Total number of frames received succesfully
+ u32 rbca; /* Total number of frames received successfully
that had destination address equal to the
broadcast address */
u32 scar; /* Statistics carry register */
@@ -759,15 +759,15 @@ struct ucc_geth_hardware_statistics {
frames) received that were between 128
(Including FCS length==4) and 255 octets */
u32 txok; /* Total number of octets residing in frames
- that where involved in succesfull
+ that where involved in successfull
transmission */
u16 txcf; /* Total number of PAUSE control frames
transmitted by this MAC */
u32 tmca; /* Total number of frames that were transmitted
- succesfully with the group address bit set
+ successfully with the group address bit set
that are not broadcast frames */
u32 tbca; /* Total number of frames transmitted
- succesfully that had destination address
+ successfully that had destination address
field equal to the broadcast address */
u32 rxfok; /* Total number of frames received OK */
u32 rxbok; /* Total number of octets received OK */
@@ -776,9 +776,9 @@ struct ucc_geth_hardware_statistics {
HW because it includes octets in frames that
never even reach the UCC */
u32 rmca; /* Total number of frames that were received
- succesfully with the group address bit set
+ successfully with the group address bit set
that are not broadcast frames */
- u32 rbca; /* Total number of frames received succesfully
+ u32 rbca; /* Total number of frames received successfully
that had destination address equal to the
broadcast address */
} __attribute__ ((packed));
diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c
index 6ce7f775bb7..a516185cbc9 100644
--- a/drivers/net/usb/asix.c
+++ b/drivers/net/usb/asix.c
@@ -365,8 +365,8 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
padlen = ((skb->len + 4) % 512) ? 0 : 4;
- if ((!skb_cloned(skb))
- && ((headroom + tailroom) >= (4 + padlen))) {
+ if ((!skb_cloned(skb)) &&
+ ((headroom + tailroom) >= (4 + padlen))) {
if ((headroom < 4) || (tailroom < padlen)) {
skb->data = memmove(skb->head + 4, skb->data, skb->len);
skb_set_tail_pointer(skb, skb->len);
@@ -541,8 +541,8 @@ static void asix_set_multicast(struct net_device *net)
if (net->flags & IFF_PROMISC) {
rx_ctl |= AX_RX_CTL_PRO;
- } else if (net->flags & IFF_ALLMULTI
- || net->mc_count > AX_MAX_MCAST) {
+ } else if (net->flags & IFF_ALLMULTI ||
+ net->mc_count > AX_MAX_MCAST) {
rx_ctl |= AX_RX_CTL_AMALL;
} else if (net->mc_count == 0) {
/* just broadcast and directed */
@@ -753,8 +753,8 @@ static void ax88172_set_multicast(struct net_device *net)
if (net->flags & IFF_PROMISC) {
rx_ctl |= 0x01;
- } else if (net->flags & IFF_ALLMULTI
- || net->mc_count > AX_MAX_MCAST) {
+ } else if (net->flags & IFF_ALLMULTI ||
+ net->mc_count > AX_MAX_MCAST) {
rx_ctl |= 0x02;
} else if (net->mc_count == 0) {
/* just broadcast and directed */
@@ -1327,7 +1327,7 @@ static const struct driver_info ax8817x_info = {
.status = asix_status,
.link_reset = ax88172_link_reset,
.reset = ax88172_link_reset,
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_LINK_INTR,
.data = 0x00130103,
};
@@ -1337,7 +1337,7 @@ static const struct driver_info dlink_dub_e100_info = {
.status = asix_status,
.link_reset = ax88172_link_reset,
.reset = ax88172_link_reset,
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_LINK_INTR,
.data = 0x009f9d9f,
};
@@ -1347,7 +1347,7 @@ static const struct driver_info netgear_fa120_info = {
.status = asix_status,
.link_reset = ax88172_link_reset,
.reset = ax88172_link_reset,
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_LINK_INTR,
.data = 0x00130103,
};
@@ -1357,7 +1357,7 @@ static const struct driver_info hawking_uf200_info = {
.status = asix_status,
.link_reset = ax88172_link_reset,
.reset = ax88172_link_reset,
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_LINK_INTR,
.data = 0x001f1d1f,
};
@@ -1367,7 +1367,7 @@ static const struct driver_info ax88772_info = {
.status = asix_status,
.link_reset = ax88772_link_reset,
.reset = ax88772_link_reset,
- .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
.rx_fixup = asix_rx_fixup,
.tx_fixup = asix_tx_fixup,
};
@@ -1378,7 +1378,7 @@ static const struct driver_info ax88178_info = {
.status = asix_status,
.link_reset = ax88178_link_reset,
.reset = ax88178_link_reset,
- .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR,
.rx_fixup = asix_rx_fixup,
.tx_fixup = asix_tx_fixup,
};
diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c
index 2bed6b087d1..22b87e64a81 100644
--- a/drivers/net/usb/catc.c
+++ b/drivers/net/usb/catc.c
@@ -436,8 +436,8 @@ static netdev_tx_t catc_start_xmit(struct sk_buff *skb,
clear_bit(TX_RUNNING, &catc->flags);
}
- if ((catc->is_f5u011 && catc->tx_ptr)
- || (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2))))
+ if ((catc->is_f5u011 && catc->tx_ptr) ||
+ (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2))))
netif_stop_queue(netdev);
spin_unlock_irqrestore(&catc->tx_lock, flags);
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index 33d5c579c5a..6491c9c00c8 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -372,12 +372,12 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* Data interface has one inactive and one active setting */
if (data_intf->num_altsetting != 2)
return -EINVAL;
- if (data_intf->altsetting[0].desc.bNumEndpoints == 0
- && data_intf->altsetting[1].desc.bNumEndpoints == 2)
+ if (data_intf->altsetting[0].desc.bNumEndpoints == 0 &&
+ data_intf->altsetting[1].desc.bNumEndpoints == 2)
data_desc = data_intf->altsetting + 1;
else
- if (data_intf->altsetting[0].desc.bNumEndpoints == 2
- && data_intf->altsetting[1].desc.bNumEndpoints == 0)
+ if (data_intf->altsetting[0].desc.bNumEndpoints == 2 &&
+ data_intf->altsetting[1].desc.bNumEndpoints == 0)
data_desc = data_intf->altsetting;
else
return -EINVAL;
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
index 23300656c26..c337ffc3304 100644
--- a/drivers/net/usb/cdc_eem.c
+++ b/drivers/net/usb/cdc_eem.c
@@ -121,8 +121,8 @@ static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
int headroom = skb_headroom(skb);
int tailroom = skb_tailroom(skb);
- if ((tailroom >= ETH_FCS_LEN + padlen)
- && (headroom >= EEM_HEAD))
+ if ((tailroom >= ETH_FCS_LEN + padlen) &&
+ (headroom >= EEM_HEAD))
goto done;
if ((headroom + tailroom)
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 21e1ba16000..21e183a83b9 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -37,23 +37,23 @@
static int is_rndis(struct usb_interface_descriptor *desc)
{
- return desc->bInterfaceClass == USB_CLASS_COMM
- && desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff;
+ return (desc->bInterfaceClass == USB_CLASS_COMM &&
+ desc->bInterfaceSubClass == 2 &&
+ desc->bInterfaceProtocol == 0xff);
}
static int is_activesync(struct usb_interface_descriptor *desc)
{
- return desc->bInterfaceClass == USB_CLASS_MISC
- && desc->bInterfaceSubClass == 1
- && desc->bInterfaceProtocol == 1;
+ return (desc->bInterfaceClass == USB_CLASS_MISC &&
+ desc->bInterfaceSubClass == 1 &&
+ desc->bInterfaceProtocol == 1);
}
static int is_wireless_rndis(struct usb_interface_descriptor *desc)
{
- return desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER
- && desc->bInterfaceSubClass == 1
- && desc->bInterfaceProtocol == 3;
+ return (desc->bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER &&
+ desc->bInterfaceSubClass == 1 &&
+ desc->bInterfaceProtocol == 3);
}
#else
@@ -116,9 +116,9 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
/* this assumes that if there's a non-RNDIS vendor variant
* of cdc-acm, it'll fail RNDIS requests cleanly.
*/
- rndis = is_rndis(&intf->cur_altsetting->desc)
- || is_activesync(&intf->cur_altsetting->desc)
- || is_wireless_rndis(&intf->cur_altsetting->desc);
+ rndis = (is_rndis(&intf->cur_altsetting->desc) ||
+ is_activesync(&intf->cur_altsetting->desc) ||
+ is_wireless_rndis(&intf->cur_altsetting->desc));
memset(info, 0, sizeof *info);
info->control = intf;
@@ -279,10 +279,10 @@ next_desc:
dev->status = &info->control->cur_altsetting->endpoint [0];
desc = &dev->status->desc;
- if (!usb_endpoint_is_int_in(desc)
- || (le16_to_cpu(desc->wMaxPacketSize)
- < sizeof(struct usb_cdc_notification))
- || !desc->bInterval) {
+ if (!usb_endpoint_is_int_in(desc) ||
+ (le16_to_cpu(desc->wMaxPacketSize)
+ < sizeof(struct usb_cdc_notification)) ||
+ !desc->bInterval) {
dev_dbg(&intf->dev, "bad notification endpoint\n");
dev->status = NULL;
}
@@ -411,13 +411,28 @@ static int cdc_bind(struct usbnet *dev, struct usb_interface *intf)
return 0;
}
+static int cdc_manage_power(struct usbnet *dev, int on)
+{
+ dev->intf->needs_remote_wakeup = on;
+ return 0;
+}
+
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_LINK_INTR,
// .check_connect = cdc_check_connect,
.bind = cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = cdc_status,
+ .manage_power = cdc_manage_power,
+};
+
+static const struct driver_info mbm_info = {
+ .description = "Mobile Broadband Network Device",
+ .flags = FLAG_WWAN,
+ .bind = cdc_bind,
+ .unbind = usbnet_cdc_unbind,
+ .status = cdc_status,
};
/*-------------------------------------------------------------------------*/
@@ -532,72 +547,72 @@ static const struct usb_device_id products [] = {
/* Ericsson F3507g */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1900, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Ericsson F3507g ver. 2 */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1902, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Ericsson F3607gw */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1904, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Ericsson F3607gw ver 2 */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1905, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Ericsson F3607gw ver 3 */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1906, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Ericsson F3307 */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x190a, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Ericsson F3307 ver 2 */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1909, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Ericsson C3607w */
USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1049, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Toshiba F3507g */
USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130b, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Toshiba F3607gw */
USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130c, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Toshiba F3607gw ver 2 */
USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x1311, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Dell F3507g */
USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8147, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Dell F3607gw */
USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8183, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
}, {
/* Dell F3607gw ver 2 */
USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x8184, USB_CLASS_COMM,
USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
- .driver_info = (unsigned long) &cdc_info,
+ .driver_info = (unsigned long) &mbm_info,
},
{ }, // END
};
@@ -610,6 +625,8 @@ static struct usb_driver cdc_driver = {
.disconnect = usbnet_disconnect,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
+ .supports_autosuspend = 1,
};
diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c
index a2b30a10064..3d406f9b2f2 100644
--- a/drivers/net/usb/dm9601.c
+++ b/drivers/net/usb/dm9601.c
@@ -611,7 +611,7 @@ static int dm9601_link_reset(struct usbnet *dev)
static const struct driver_info dm9601_info = {
.description = "Davicom DM9601 USB Ethernet",
- .flags = FLAG_ETHER,
+ .flags = FLAG_ETHER | FLAG_LINK_INTR,
.bind = dm9601_bind,
.rx_fixup = dm9601_rx_fixup,
.tx_fixup = dm9601_tx_fixup,
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 43bc3fcc0d8..f78f0903b07 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -602,9 +602,9 @@ static struct hso_serial *get_serial_by_shared_int_and_type(
port = hso_mux_to_port(mux);
for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
- if (serial_table[i]
- && (dev2ser(serial_table[i])->shared_int == shared_int)
- && ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) {
+ if (serial_table[i] &&
+ (dev2ser(serial_table[i])->shared_int == shared_int) &&
+ ((serial_table[i]->port_spec & HSO_PORT_MASK) == port)) {
return dev2ser(serial_table[i]);
}
}
@@ -846,8 +846,8 @@ static void hso_net_tx_timeout(struct net_device *net)
dev_warn(&net->dev, "Tx timed out.\n");
/* Tear the waiting frame off the list */
- if (odev->mux_bulk_tx_urb
- && (odev->mux_bulk_tx_urb->status == -EINPROGRESS))
+ if (odev->mux_bulk_tx_urb &&
+ (odev->mux_bulk_tx_urb->status == -EINPROGRESS))
usb_unlink_urb(odev->mux_bulk_tx_urb);
/* Update statistics */
@@ -1020,9 +1020,9 @@ static void read_bulk_callback(struct urb *urb)
u32 rest;
u8 crc_check[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
rest = urb->actual_length % odev->in_endp->wMaxPacketSize;
- if (((rest == 5) || (rest == 6))
- && !memcmp(((u8 *) urb->transfer_buffer) +
- urb->actual_length - 4, crc_check, 4)) {
+ if (((rest == 5) || (rest == 6)) &&
+ !memcmp(((u8 *) urb->transfer_buffer) +
+ urb->actual_length - 4, crc_check, 4)) {
urb->actual_length -= 4;
}
}
@@ -1226,9 +1226,9 @@ static void hso_std_serial_read_bulk_callback(struct urb *urb)
rest =
urb->actual_length %
serial->in_endp->wMaxPacketSize;
- if (((rest == 5) || (rest == 6))
- && !memcmp(((u8 *) urb->transfer_buffer) +
- urb->actual_length - 4, crc_check, 4)) {
+ if (((rest == 5) || (rest == 6)) &&
+ !memcmp(((u8 *) urb->transfer_buffer) +
+ urb->actual_length - 4, crc_check, 4)) {
urb->actual_length -= 4;
}
}
@@ -2982,8 +2982,8 @@ static int hso_probe(struct usb_interface *interface,
case HSO_INTF_BULK:
/* It's a regular bulk interface */
- if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK)
- && !disable_net)
+ if (((port_spec & HSO_PORT_MASK) == HSO_PORT_NETWORK) &&
+ !disable_net)
hso_dev = hso_create_net_device(interface, port_spec);
else
hso_dev =
@@ -3146,8 +3146,8 @@ static void hso_free_interface(struct usb_interface *interface)
int i;
for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
- if (serial_table[i]
- && (serial_table[i]->interface == interface)) {
+ if (serial_table[i] &&
+ (serial_table[i]->interface == interface)) {
hso_dev = dev2ser(serial_table[i]);
spin_lock_irq(&hso_dev->serial_lock);
tty = tty_kref_get(hso_dev->tty);
@@ -3163,8 +3163,8 @@ static void hso_free_interface(struct usb_interface *interface)
}
for (i = 0; i < HSO_MAX_NET_DEVICES; i++) {
- if (network_table[i]
- && (network_table[i]->interface == interface)) {
+ if (network_table[i] &&
+ (network_table[i]->interface == interface)) {
struct rfkill *rfk = dev2net(network_table[i])->rfkill;
/* hso_stop_net_device doesn't stop the net queue since
* traffic needs to start it again when suspended */
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index e391ef969c2..3b80e8d2d62 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -471,16 +471,7 @@ static int kaweth_reset(struct kaweth_device *kaweth)
int result;
dbg("kaweth_reset(%p)", kaweth);
- result = kaweth_control(kaweth,
- usb_sndctrlpipe(kaweth->dev, 0),
- USB_REQ_SET_CONFIGURATION,
- 0,
- kaweth->dev->config[0].desc.bConfigurationValue,
- 0,
- NULL,
- 0,
- KAWETH_CONTROL_TIMEOUT);
-
+ result = usb_reset_configuration(kaweth->dev);
mdelay(10);
dbg("kaweth_reset() returns %d.",result);
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index 10873d96b2d..87374317f48 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -391,8 +391,8 @@ static void mcs7830_set_multicast(struct net_device *net)
if (net->flags & IFF_PROMISC) {
data->config |= HIF_REG_CONFIG_PROMISCIOUS;
- } else if (net->flags & IFF_ALLMULTI
- || net->mc_count > MCS7830_MAX_MCAST) {
+ } else if (net->flags & IFF_ALLMULTI ||
+ net->mc_count > MCS7830_MAX_MCAST) {
data->config |= HIF_REG_CONFIG_ALLMULTICAST;
} else if (net->mc_count == 0) {
/* just broadcast and directed */
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index f56dec6119c..490fa8f5542 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -114,8 +114,8 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)
*/
/* Issue the request; xid is unique, don't bother byteswapping it */
- if (likely(buf->msg_type != RNDIS_MSG_HALT
- && buf->msg_type != RNDIS_MSG_RESET)) {
+ if (likely(buf->msg_type != RNDIS_MSG_HALT &&
+ buf->msg_type != RNDIS_MSG_RESET)) {
xid = dev->xid++;
if (!xid)
xid = dev->xid++;
@@ -493,9 +493,9 @@ int rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
data_len = le32_to_cpu(hdr->data_len);
/* don't choke if we see oob, per-packet data, etc */
- if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET
- || skb->len < msg_len
- || (data_offset + data_len + 8) > msg_len)) {
+ if (unlikely(hdr->msg_type != RNDIS_MSG_PACKET ||
+ skb->len < msg_len ||
+ (data_offset + data_len + 8) > msg_len)) {
dev->net->stats.rx_frame_errors++;
devdbg(dev, "bad rndis message %d/%d/%d/%d, len %d",
le32_to_cpu(hdr->msg_type),
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index c6c922247d0..0c3c738d741 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -748,7 +748,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)
mii_nway_restart(&dev->mii);
if (netif_msg_ifup(dev))
- devdbg(dev, "phy initialised succesfully");
+ devdbg(dev, "phy initialised successfully");
return 0;
}
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index ca5ca5ae061..035fab04c0a 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -140,8 +140,8 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
if (!alt || !in || !out)
return -EINVAL;
- if (alt->desc.bAlternateSetting != 0
- || !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+ if (alt->desc.bAlternateSetting != 0 ||
+ !(dev->driver_info->flags & FLAG_NO_SETINT)) {
tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
alt->desc.bAlternateSetting);
if (tmp < 0)
@@ -351,9 +351,10 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
spin_lock_irqsave (&dev->rxq.lock, lockflags);
- if (netif_running (dev->net)
- && netif_device_present (dev->net)
- && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ if (netif_running (dev->net) &&
+ netif_device_present (dev->net) &&
+ !test_bit (EVENT_RX_HALT, &dev->flags) &&
+ !test_bit (EVENT_DEV_ASLEEP, &dev->flags)) {
switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
case -EPIPE:
usbnet_defer_kevent (dev, EVENT_RX_HALT);
@@ -391,8 +392,8 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
{
- if (dev->driver_info->rx_fixup
- && !dev->driver_info->rx_fixup (dev, skb))
+ if (dev->driver_info->rx_fixup &&
+ !dev->driver_info->rx_fixup (dev, skb))
goto error;
// else network stack removes extra byte if we forced a short packet
@@ -484,8 +485,8 @@ block:
defer_bh(dev, skb, &dev->rxq);
if (urb) {
- if (netif_running (dev->net)
- && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ if (netif_running (dev->net) &&
+ !test_bit (EVENT_RX_HALT, &dev->flags)) {
rx_submit (dev, urb, GFP_ATOMIC);
return;
}
@@ -611,15 +612,39 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
/*-------------------------------------------------------------------------*/
// precondition: never called in_interrupt
+static void usbnet_terminate_urbs(struct usbnet *dev)
+{
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
+ DECLARE_WAITQUEUE(wait, current);
+ int temp;
+
+ /* ensure there are no more active urbs */
+ add_wait_queue(&unlink_wakeup, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ dev->wait = &unlink_wakeup;
+ temp = unlink_urbs(dev, &dev->txq) +
+ unlink_urbs(dev, &dev->rxq);
+
+ /* maybe wait for deletions to finish. */
+ while (!skb_queue_empty(&dev->rxq)
+ && !skb_queue_empty(&dev->txq)
+ && !skb_queue_empty(&dev->done)) {
+ schedule_timeout(UNLINK_TIMEOUT_MS);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (netif_msg_ifdown(dev))
+ devdbg(dev, "waited for %d urb completions",
+ temp);
+ }
+ set_current_state(TASK_RUNNING);
+ dev->wait = NULL;
+ remove_wait_queue(&unlink_wakeup, &wait);
+}
int usbnet_stop (struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
struct driver_info *info = dev->driver_info;
- int temp;
int retval;
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK (unlink_wakeup);
- DECLARE_WAITQUEUE (wait, current);
netif_stop_queue (net);
@@ -641,25 +666,8 @@ int usbnet_stop (struct net_device *net)
info->description);
}
- if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
- /* ensure there are no more active urbs */
- add_wait_queue(&unlink_wakeup, &wait);
- dev->wait = &unlink_wakeup;
- temp = unlink_urbs(dev, &dev->txq) +
- unlink_urbs(dev, &dev->rxq);
-
- /* maybe wait for deletions to finish. */
- while (!skb_queue_empty(&dev->rxq)
- && !skb_queue_empty(&dev->txq)
- && !skb_queue_empty(&dev->done)) {
- msleep(UNLINK_TIMEOUT_MS);
- if (netif_msg_ifdown(dev))
- devdbg(dev, "waited for %d urb completions",
- temp);
- }
- dev->wait = NULL;
- remove_wait_queue(&unlink_wakeup, &wait);
- }
+ if (!(info->flags & FLAG_AVOID_UNLINK_URBS))
+ usbnet_terminate_urbs(dev);
usb_kill_urb(dev->interrupt);
@@ -672,7 +680,10 @@ int usbnet_stop (struct net_device *net)
dev->flags = 0;
del_timer_sync (&dev->delay);
tasklet_kill (&dev->bh);
- usb_autopm_put_interface(dev->intf);
+ if (info->manage_power)
+ info->manage_power(dev, 0);
+ else
+ usb_autopm_put_interface(dev->intf);
return 0;
}
@@ -753,6 +764,12 @@ int usbnet_open (struct net_device *net)
// delay posting reads until we're fully open
tasklet_schedule (&dev->bh);
+ if (info->manage_power) {
+ retval = info->manage_power(dev, 1);
+ if (retval < 0)
+ goto done;
+ usb_autopm_put_interface(dev->intf);
+ }
return retval;
done:
usb_autopm_put_interface(dev->intf);
@@ -881,11 +898,16 @@ kevent (struct work_struct *work)
/* usb_clear_halt() needs a thread context */
if (test_bit (EVENT_TX_HALT, &dev->flags)) {
unlink_urbs (dev, &dev->txq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_pipe;
status = usb_clear_halt (dev->udev, dev->out);
- if (status < 0
- && status != -EPIPE
- && status != -ESHUTDOWN) {
+ usb_autopm_put_interface(dev->intf);
+ if (status < 0 &&
+ status != -EPIPE &&
+ status != -ESHUTDOWN) {
if (netif_msg_tx_err (dev))
+fail_pipe:
deverr (dev, "can't clear tx halt, status %d",
status);
} else {
@@ -896,11 +918,16 @@ kevent (struct work_struct *work)
}
if (test_bit (EVENT_RX_HALT, &dev->flags)) {
unlink_urbs (dev, &dev->rxq);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_halt;
status = usb_clear_halt (dev->udev, dev->in);
- if (status < 0
- && status != -EPIPE
- && status != -ESHUTDOWN) {
+ usb_autopm_put_interface(dev->intf);
+ if (status < 0 &&
+ status != -EPIPE &&
+ status != -ESHUTDOWN) {
if (netif_msg_rx_err (dev))
+fail_halt:
deverr (dev, "can't clear rx halt, status %d",
status);
} else {
@@ -919,7 +946,12 @@ kevent (struct work_struct *work)
clear_bit (EVENT_RX_MEMORY, &dev->flags);
if (urb != NULL) {
clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto fail_lowmem;
rx_submit (dev, urb, GFP_KERNEL);
+ usb_autopm_put_interface(dev->intf);
+fail_lowmem:
tasklet_schedule (&dev->bh);
}
}
@@ -929,11 +961,18 @@ kevent (struct work_struct *work)
int retval = 0;
clear_bit (EVENT_LINK_RESET, &dev->flags);
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0)
+ goto skip_reset;
if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
+ usb_autopm_put_interface(dev->intf);
+skip_reset:
devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s",
retval,
dev->udev->bus->bus_name, dev->udev->devpath,
info->description);
+ } else {
+ usb_autopm_put_interface(dev->intf);
}
}
@@ -971,6 +1010,7 @@ static void tx_complete (struct urb *urb)
case -EPROTO:
case -ETIME:
case -EILSEQ:
+ usb_mark_last_busy(dev->udev);
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay,
jiffies + THROTTLE_JIFFIES);
@@ -987,6 +1027,7 @@ static void tx_complete (struct urb *urb)
}
}
+ usb_autopm_put_interface_async(dev->intf);
urb->dev = NULL;
entry->state = tx_done;
defer_bh(dev, skb, &dev->txq);
@@ -1057,14 +1098,34 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
}
}
- spin_lock_irqsave (&dev->txq.lock, flags);
+ spin_lock_irqsave(&dev->txq.lock, flags);
+ retval = usb_autopm_get_interface_async(dev->intf);
+ if (retval < 0) {
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ goto drop;
+ }
+
+#ifdef CONFIG_PM
+ /* if this triggers the device is still a sleep */
+ if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) {
+ /* transmission will be done in resume */
+ usb_anchor_urb(urb, &dev->deferred);
+ /* no use to process more packets */
+ netif_stop_queue(net);
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
+ devdbg(dev, "Delaying transmission for resumption");
+ goto deferred;
+ }
+#endif
switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
case -EPIPE:
netif_stop_queue (net);
usbnet_defer_kevent (dev, EVENT_TX_HALT);
+ usb_autopm_put_interface_async(dev->intf);
break;
default:
+ usb_autopm_put_interface_async(dev->intf);
if (netif_msg_tx_err (dev))
devdbg (dev, "tx: submit urb err %d", retval);
break;
@@ -1088,6 +1149,9 @@ drop:
devdbg (dev, "> tx, len %d, type 0x%x",
length, skb->protocol);
}
+#ifdef CONFIG_PM
+deferred:
+#endif
return NETDEV_TX_OK;
}
EXPORT_SYMBOL_GPL(usbnet_start_xmit);
@@ -1126,10 +1190,10 @@ static void usbnet_bh (unsigned long param)
}
// or are we maybe short a few urbs?
- } else if (netif_running (dev->net)
- && netif_device_present (dev->net)
- && !timer_pending (&dev->delay)
- && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ } else if (netif_running (dev->net) &&
+ netif_device_present (dev->net) &&
+ !timer_pending (&dev->delay) &&
+ !test_bit (EVENT_RX_HALT, &dev->flags)) {
int temp = dev->rxq.qlen;
int qlen = RX_QLEN (dev);
@@ -1210,6 +1274,14 @@ static const struct net_device_ops usbnet_netdev_ops = {
// precondition: never called in_interrupt
+static struct device_type wlan_type = {
+ .name = "wlan",
+};
+
+static struct device_type wwan_type = {
+ .name = "wwan",
+};
+
int
usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{
@@ -1255,6 +1327,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev->bh.func = usbnet_bh;
dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent);
+ init_usb_anchor(&dev->deferred);
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
init_timer (&dev->delay);
@@ -1289,12 +1362,15 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
// heuristic: "usb%d" for links we know are two-host,
// else "eth%d" when there's reasonable doubt. userspace
// can rename the link if it knows better.
- if ((dev->driver_info->flags & FLAG_ETHER) != 0
- && (net->dev_addr [0] & 0x02) == 0)
+ if ((dev->driver_info->flags & FLAG_ETHER) != 0 &&
+ (net->dev_addr [0] & 0x02) == 0)
strcpy (net->name, "eth%d");
/* WLAN devices should always be named "wlan%d" */
if ((dev->driver_info->flags & FLAG_WLAN) != 0)
strcpy(net->name, "wlan%d");
+ /* WWAN devices should always be named "wwan%d" */
+ if ((dev->driver_info->flags & FLAG_WWAN) != 0)
+ strcpy(net->name, "wwan%d");
/* maybe the remote can't receive an Ethernet MTU */
if (net->mtu > (dev->hard_mtu - net->hard_header_len))
@@ -1322,6 +1398,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
SET_NETDEV_DEV(net, &udev->dev);
+
+ if ((dev->driver_info->flags & FLAG_WLAN) != 0)
+ SET_NETDEV_DEVTYPE(net, &wlan_type);
+ if ((dev->driver_info->flags & FLAG_WWAN) != 0)
+ SET_NETDEV_DEVTYPE(net, &wwan_type);
+
status = register_netdev (net);
if (status)
goto out3;
@@ -1335,9 +1417,11 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
// ok, it's ready to go.
usb_set_intfdata (udev, dev);
- // start as if the link is up
netif_device_attach (net);
+ if (dev->driver_info->flags & FLAG_LINK_INTR)
+ netif_carrier_off(net);
+
return 0;
out3:
@@ -1363,13 +1447,23 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
struct usbnet *dev = usb_get_intfdata(intf);
if (!dev->suspend_count++) {
+ spin_lock_irq(&dev->txq.lock);
+ /* don't autosuspend while transmitting */
+ if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
+ spin_unlock_irq(&dev->txq.lock);
+ return -EBUSY;
+ } else {
+ set_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+ }
/*
* accelerate emptying of the rx and queues, to avoid
* having everything error out.
*/
netif_device_detach (dev->net);
- (void) unlink_urbs (dev, &dev->rxq);
- (void) unlink_urbs (dev, &dev->txq);
+ usbnet_terminate_urbs(dev);
+ usb_kill_urb(dev->interrupt);
+
/*
* reattach so runtime management can use and
* wake the device
@@ -1383,10 +1477,34 @@ EXPORT_SYMBOL_GPL(usbnet_suspend);
int usbnet_resume (struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
+ struct sk_buff *skb;
+ struct urb *res;
+ int retval;
+
+ if (!--dev->suspend_count) {
+ spin_lock_irq(&dev->txq.lock);
+ while ((res = usb_get_from_anchor(&dev->deferred))) {
+
+ printk(KERN_INFO"%s has delayed data\n", __func__);
+ skb = (struct sk_buff *)res->context;
+ retval = usb_submit_urb(res, GFP_ATOMIC);
+ if (retval < 0) {
+ dev_kfree_skb_any(skb);
+ usb_free_urb(res);
+ usb_autopm_put_interface_async(dev->intf);
+ } else {
+ dev->net->trans_start = jiffies;
+ __skb_queue_tail(&dev->txq, skb);
+ }
+ }
- if (!--dev->suspend_count)
+ smp_mb();
+ clear_bit(EVENT_DEV_ASLEEP, &dev->flags);
+ spin_unlock_irq(&dev->txq.lock);
+ if (!(dev->txq.qlen >= TX_QLEN(dev)))
+ netif_start_queue(dev->net);
tasklet_schedule (&dev->bh);
-
+ }
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_resume);
diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c
index 04882c8f9bf..3eb0b167b5b 100644
--- a/drivers/net/usb/zaurus.c
+++ b/drivers/net/usb/zaurus.c
@@ -174,8 +174,8 @@ static int blan_mdlm_bind(struct usbnet *dev, struct usb_interface *intf)
goto bad_desc;
}
/* expect bcdVersion 1.0, ignore */
- if (memcmp(&desc->bGUID, blan_guid, 16)
- && memcmp(&desc->bGUID, safe_guid, 16) ) {
+ if (memcmp(&desc->bGUID, blan_guid, 16) &&
+ memcmp(&desc->bGUID, safe_guid, 16)) {
/* hey, this one might _really_ be MDLM! */
dev_dbg(&intf->dev, "MDLM guid\n");
goto bad_desc;
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 52af5017c46..63099c58a6d 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -155,8 +155,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
struct veth_net_stats *stats, *rcv_stats;
int length, cpu;
- skb_orphan(skb);
-
priv = netdev_priv(dev);
rcv = priv->peer;
rcv_priv = netdev_priv(rcv);
@@ -168,20 +166,12 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
if (!(rcv->flags & IFF_UP))
goto tx_drop;
- if (skb->len > (rcv->mtu + MTU_PAD))
- goto rx_drop;
-
- skb->tstamp.tv64 = 0;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = eth_type_trans(skb, rcv);
if (dev->features & NETIF_F_NO_CSUM)
skb->ip_summed = rcv_priv->ip_summed;
- skb->mark = 0;
- secpath_reset(skb);
- nf_reset(skb);
-
- length = skb->len;
+ length = skb->len + ETH_HLEN;
+ if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS)
+ goto rx_drop;
stats->tx_bytes += length;
stats->tx_packets++;
@@ -189,7 +179,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
rcv_stats->rx_bytes += length;
rcv_stats->rx_packets++;
- netif_rx(skb);
return NETDEV_TX_OK;
tx_drop:
@@ -337,7 +326,7 @@ static int veth_validate(struct nlattr *tb[], struct nlattr *data[])
static struct rtnl_link_ops veth_link_ops;
-static int veth_newlink(struct net_device *dev,
+static int veth_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
int err;
@@ -345,6 +334,7 @@ static int veth_newlink(struct net_device *dev,
struct veth_priv *priv;
char ifname[IFNAMSIZ];
struct nlattr *peer_tb[IFLA_MAX + 1], **tbp;
+ struct net *net;
/*
* create and register peer first
@@ -377,14 +367,22 @@ static int veth_newlink(struct net_device *dev,
else
snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d");
- peer = rtnl_create_link(dev_net(dev), ifname, &veth_link_ops, tbp);
- if (IS_ERR(peer))
+ net = rtnl_link_get_net(src_net, tbp);
+ if (IS_ERR(net))
+ return PTR_ERR(net);
+
+ peer = rtnl_create_link(src_net, net, ifname, &veth_link_ops, tbp);
+ if (IS_ERR(peer)) {
+ put_net(net);
return PTR_ERR(peer);
+ }
if (tbp[IFLA_ADDRESS] == NULL)
random_ether_addr(peer->dev_addr);
err = register_netdevice(peer);
+ put_net(net);
+ net = NULL;
if (err < 0)
goto err_register_peer;
@@ -439,7 +437,7 @@ err_register_peer:
return err;
}
-static void veth_dellink(struct net_device *dev)
+static void veth_dellink(struct net_device *dev, struct list_head *head)
{
struct veth_priv *priv;
struct net_device *peer;
@@ -447,8 +445,8 @@ static void veth_dellink(struct net_device *dev)
priv = netdev_priv(dev);
peer = priv->peer;
- unregister_netdevice(dev);
- unregister_netdevice(peer);
+ unregister_netdevice_queue(dev, head);
+ unregister_netdevice_queue(peer, head);
}
static const struct nla_policy veth_policy[VETH_INFO_MAX + 1];
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index 1fd70583be4..593e01f64e9 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -42,9 +42,9 @@ static int max_interrupt_work = 20;
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1518 effectively disables this feature. */
-#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
- || defined(CONFIG_SPARC) || defined(__ia64__) \
- || defined(__sh__) || defined(__mips__)
+#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) || \
+ defined(CONFIG_SPARC) || defined(__ia64__) || \
+ defined(__sh__) || defined(__mips__)
static int rx_copybreak = 1518;
#else
static int rx_copybreak;
@@ -1150,7 +1150,7 @@ static int rhine_open(struct net_device *dev)
void __iomem *ioaddr = rp->base;
int rc;
- rc = request_irq(rp->pdev->irq, &rhine_interrupt, IRQF_SHARED, dev->name,
+ rc = request_irq(rp->pdev->irq, rhine_interrupt, IRQF_SHARED, dev->name,
dev);
if (rc)
return rc;
@@ -1484,15 +1484,15 @@ static int rhine_rx(struct net_device *dev, int limit)
}
}
} else {
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
/* Length should omit the CRC */
int pkt_len = data_size - 4;
/* Check if the packet is long enough to accept without
copying to a minimally-sized skbuff. */
- if (pkt_len < rx_copybreak &&
- (skb = netdev_alloc_skb(dev, pkt_len + NET_IP_ALIGN)) != NULL) {
- skb_reserve(skb, NET_IP_ALIGN); /* 16 byte align the IP header */
+ if (pkt_len < rx_copybreak)
+ skb = netdev_alloc_skb_ip_align(dev, pkt_len);
+ if (skb) {
pci_dma_sync_single_for_cpu(rp->pdev,
rp->rx_skbuff_dma[entry],
rp->rx_buf_sz,
@@ -1683,8 +1683,8 @@ static void rhine_set_rx_mode(struct net_device *dev)
rx_mode = 0x1C;
iowrite32(0xffffffff, ioaddr + MulticastFilter0);
iowrite32(0xffffffff, ioaddr + MulticastFilter1);
- } else if ((dev->mc_count > multicast_filter_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > multicast_filter_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
/* Too many to match, or accept all multicasts. */
iowrite32(0xffffffff, ioaddr + MulticastFilter0);
iowrite32(0xffffffff, ioaddr + MulticastFilter1);
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index e04e5bee005..4ceb441f268 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -9,7 +9,6 @@
*
* TODO
* rx_copybreak/alignment
- * Scatter gather
* More testing
*
* The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk>
@@ -275,7 +274,7 @@ VELOCITY_PARAM(rx_thresh, "Receive fifo threshold");
#define DMA_LENGTH_MIN 0
#define DMA_LENGTH_MAX 7
-#define DMA_LENGTH_DEF 0
+#define DMA_LENGTH_DEF 6
/* DMA_length[] is used for controlling the DMA length
0: 8 DWORDs
@@ -298,14 +297,6 @@ VELOCITY_PARAM(DMA_length, "DMA length");
*/
VELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned");
-#define TX_CSUM_DEF 1
-/* txcsum_offload[] is used for setting the checksum offload ability of NIC.
- (We only support RX checksum offload now)
- 0: disable csum_offload[checksum offload
- 1: enable checksum offload. (Default)
-*/
-VELOCITY_PARAM(txcsum_offload, "Enable transmit packet checksum offload");
-
#define FLOW_CNTL_DEF 1
#define FLOW_CNTL_MIN 1
#define FLOW_CNTL_MAX 5
@@ -354,21 +345,10 @@ VELOCITY_PARAM(ValPktLen, "Receiving or Drop invalid 802.3 frame");
*/
VELOCITY_PARAM(wol_opts, "Wake On Lan options");
-#define INT_WORKS_DEF 20
-#define INT_WORKS_MIN 10
-#define INT_WORKS_MAX 64
-
-VELOCITY_PARAM(int_works, "Number of packets per interrupt services");
-
static int rx_copybreak = 200;
module_param(rx_copybreak, int, 0644);
MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");
-#ifdef CONFIG_PM
-static DEFINE_SPINLOCK(velocity_dev_list_lock);
-static LIST_HEAD(velocity_dev_list);
-#endif
-
/*
* Internal board variants. At the moment we have only one
*/
@@ -417,14 +397,6 @@ static void __devexit velocity_remove1(struct pci_dev *pdev)
struct net_device *dev = pci_get_drvdata(pdev);
struct velocity_info *vptr = netdev_priv(dev);
-#ifdef CONFIG_PM
- unsigned long flags;
-
- spin_lock_irqsave(&velocity_dev_list_lock, flags);
- if (!list_empty(&velocity_dev_list))
- list_del(&vptr->list);
- spin_unlock_irqrestore(&velocity_dev_list_lock, flags);
-#endif
unregister_netdev(dev);
iounmap(vptr->mac_regs);
pci_release_regions(pdev);
@@ -510,13 +482,11 @@ static void __devinit velocity_get_options(struct velocity_opt *opts, int index,
velocity_set_int_opt(&opts->numrx, RxDescriptors[index], RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, "RxDescriptors", devname);
velocity_set_int_opt(&opts->numtx, TxDescriptors[index], TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, "TxDescriptors", devname);
- velocity_set_bool_opt(&opts->flags, txcsum_offload[index], TX_CSUM_DEF, VELOCITY_FLAGS_TX_CSUM, "txcsum_offload", devname);
velocity_set_int_opt(&opts->flow_cntl, flow_control[index], FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, "flow_control", devname);
velocity_set_bool_opt(&opts->flags, IP_byte_align[index], IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, "IP_byte_align", devname);
velocity_set_bool_opt(&opts->flags, ValPktLen[index], VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN, "ValPktLen", devname);
velocity_set_int_opt((int *) &opts->spd_dpx, speed_duplex[index], MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, "Media link mode", devname);
velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index], WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, "Wake On Lan options", devname);
- velocity_set_int_opt((int *) &opts->int_works, int_works[index], INT_WORKS_MIN, INT_WORKS_MAX, INT_WORKS_DEF, "Interrupt service works", devname);
opts->numrx = (opts->numrx & ~3);
}
@@ -925,8 +895,8 @@ static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status)
/*
Check if new status is consisent with current status
- if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE)
- || (mii_status==curr_status)) {
+ if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE) ||
+ (mii_status==curr_status)) {
vptr->mii_status=mii_check_media_mode(vptr->mac_regs);
vptr->mii_status=check_connection_type(vptr->mac_regs);
VELOCITY_PRT(MSG_LEVEL_INFO, "Velocity link no change\n");
@@ -1162,8 +1132,8 @@ static void velocity_set_multi(struct net_device *dev)
writel(0xffffffff, &regs->MARCAM[0]);
writel(0xffffffff, &regs->MARCAM[4]);
rx_mode = (RCR_AM | RCR_AB | RCR_PROM);
- } else if ((dev->mc_count > vptr->multicast_limit)
- || (dev->flags & IFF_ALLMULTI)) {
+ } else if ((dev->mc_count > vptr->multicast_limit) ||
+ (dev->flags & IFF_ALLMULTI)) {
writel(0xffffffff, &regs->MARCAM[0]);
writel(0xffffffff, &regs->MARCAM[4]);
rx_mode = (RCR_AM | RCR_AB);
@@ -1259,6 +1229,66 @@ static void mii_init(struct velocity_info *vptr, u32 mii_status)
}
}
+/**
+ * setup_queue_timers - Setup interrupt timers
+ *
+ * Setup interrupt frequency during suppression (timeout if the frame
+ * count isn't filled).
+ */
+static void setup_queue_timers(struct velocity_info *vptr)
+{
+ /* Only for newer revisions */
+ if (vptr->rev_id >= REV_ID_VT3216_A0) {
+ u8 txqueue_timer = 0;
+ u8 rxqueue_timer = 0;
+
+ if (vptr->mii_status & (VELOCITY_SPEED_1000 |
+ VELOCITY_SPEED_100)) {
+ txqueue_timer = vptr->options.txqueue_timer;
+ rxqueue_timer = vptr->options.rxqueue_timer;
+ }
+
+ writeb(txqueue_timer, &vptr->mac_regs->TQETMR);
+ writeb(rxqueue_timer, &vptr->mac_regs->RQETMR);
+ }
+}
+/**
+ * setup_adaptive_interrupts - Setup interrupt suppression
+ *
+ * @vptr velocity adapter
+ *
+ * The velocity is able to suppress interrupt during high interrupt load.
+ * This function turns on that feature.
+ */
+static void setup_adaptive_interrupts(struct velocity_info *vptr)
+{
+ struct mac_regs __iomem *regs = vptr->mac_regs;
+ u16 tx_intsup = vptr->options.tx_intsup;
+ u16 rx_intsup = vptr->options.rx_intsup;
+
+ /* Setup default interrupt mask (will be changed below) */
+ vptr->int_mask = INT_MASK_DEF;
+
+ /* Set Tx Interrupt Suppression Threshold */
+ writeb(CAMCR_PS0, &regs->CAMCR);
+ if (tx_intsup != 0) {
+ vptr->int_mask &= ~(ISR_PTXI | ISR_PTX0I | ISR_PTX1I |
+ ISR_PTX2I | ISR_PTX3I);
+ writew(tx_intsup, &regs->ISRCTL);
+ } else
+ writew(ISRCTL_TSUPDIS, &regs->ISRCTL);
+
+ /* Set Rx Interrupt Suppression Threshold */
+ writeb(CAMCR_PS1, &regs->CAMCR);
+ if (rx_intsup != 0) {
+ vptr->int_mask &= ~ISR_PRXI;
+ writew(rx_intsup, &regs->ISRCTL);
+ } else
+ writew(ISRCTL_RSUPDIS, &regs->ISRCTL);
+
+ /* Select page to interrupt hold timer */
+ writeb(0, &regs->CAMCR);
+}
/**
* velocity_init_registers - initialise MAC registers
@@ -1345,7 +1375,7 @@ static void velocity_init_registers(struct velocity_info *vptr,
*/
enable_mii_autopoll(regs);
- vptr->int_mask = INT_MASK_DEF;
+ setup_adaptive_interrupts(vptr);
writel(vptr->rx.pool_dma, &regs->RDBaseLo);
writew(vptr->options.numrx - 1, &regs->RDCSize);
@@ -1483,7 +1513,8 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx)
* Do the gymnastics to get the buffer head for data at
* 64byte alignment.
*/
- skb_reserve(rd_info->skb, (unsigned long) rd_info->skb->data & 63);
+ skb_reserve(rd_info->skb,
+ 64 - ((unsigned long) rd_info->skb->data & 63));
rd_info->skb_dma = pci_map_single(vptr->pdev, rd_info->skb->data,
vptr->rx.buf_sz, PCI_DMA_FROMDEVICE);
@@ -1602,12 +1633,10 @@ out:
*/
static int velocity_init_td_ring(struct velocity_info *vptr)
{
- dma_addr_t curr;
int j;
/* Init the TD ring entries */
for (j = 0; j < vptr->tx.numq; j++) {
- curr = vptr->tx.pool_dma[j];
vptr->tx.infos[j] = kcalloc(vptr->options.numtx,
sizeof(struct velocity_td_info),
@@ -1673,21 +1702,27 @@ err_free_dma_rings_0:
* Release an transmit buffer. If the buffer was preallocated then
* recycle it, if not then unmap the buffer.
*/
-static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_info *tdinfo)
+static void velocity_free_tx_buf(struct velocity_info *vptr,
+ struct velocity_td_info *tdinfo, struct tx_desc *td)
{
struct sk_buff *skb = tdinfo->skb;
- int i;
- int pktlen;
/*
* Don't unmap the pre-allocated tx_bufs
*/
if (tdinfo->skb_dma) {
+ int i;
- pktlen = max_t(unsigned int, skb->len, ETH_ZLEN);
for (i = 0; i < tdinfo->nskb_dma; i++) {
- pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], pktlen, PCI_DMA_TODEVICE);
- tdinfo->skb_dma[i] = 0;
+ size_t pktlen = max_t(size_t, skb->len, ETH_ZLEN);
+
+ /* For scatter-gather */
+ if (skb_shinfo(skb)->nr_frags > 0)
+ pktlen = max_t(size_t, pktlen,
+ td->td_buf[i].size & ~TD_QUEUE);
+
+ pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i],
+ le16_to_cpu(pktlen), PCI_DMA_TODEVICE);
}
}
dev_kfree_skb_irq(skb);
@@ -1801,6 +1836,8 @@ static void velocity_error(struct velocity_info *vptr, int status)
BYTE_REG_BITS_OFF(TESTCFG_HBDIS, &regs->TESTCFG);
else
BYTE_REG_BITS_ON(TESTCFG_HBDIS, &regs->TESTCFG);
+
+ setup_queue_timers(vptr);
}
/*
* Get link status from PHYSR0
@@ -1887,7 +1924,7 @@ static int velocity_tx_srv(struct velocity_info *vptr, u32 status)
stats->tx_packets++;
stats->tx_bytes += tdinfo->skb->len;
}
- velocity_free_tx_buf(vptr, tdinfo);
+ velocity_free_tx_buf(vptr, tdinfo, td);
vptr->tx.used[qnum]--;
}
vptr->tx.tail[qnum] = idx;
@@ -1899,8 +1936,8 @@ static int velocity_tx_srv(struct velocity_info *vptr, u32 status)
* Look to see if we should kick the transmit network
* layer for more work.
*/
- if (netif_queue_stopped(vptr->dev) && (full == 0)
- && (!(vptr->mii_status & VELOCITY_LINK_FAIL))) {
+ if (netif_queue_stopped(vptr->dev) && (full == 0) &&
+ (!(vptr->mii_status & VELOCITY_LINK_FAIL))) {
netif_wake_queue(vptr->dev);
}
return works;
@@ -1949,10 +1986,9 @@ static int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size,
if (pkt_size < rx_copybreak) {
struct sk_buff *new_skb;
- new_skb = netdev_alloc_skb(vptr->dev, pkt_size + 2);
+ new_skb = netdev_alloc_skb_ip_align(vptr->dev, pkt_size);
if (new_skb) {
new_skb->ip_summed = rx_skb[0]->ip_summed;
- skb_reserve(new_skb, 2);
skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size);
*rx_skb = new_skb;
ret = 0;
@@ -2060,13 +2096,14 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx)
* any received packets from the receive queue. Hand the ring
* slots back to the adapter for reuse.
*/
-static int velocity_rx_srv(struct velocity_info *vptr, int status)
+static int velocity_rx_srv(struct velocity_info *vptr, int status,
+ int budget_left)
{
struct net_device_stats *stats = &vptr->dev->stats;
int rd_curr = vptr->rx.curr;
int works = 0;
- do {
+ while (works < budget_left) {
struct rx_desc *rd = vptr->rx.ring + rd_curr;
if (!vptr->rx.info[rd_curr].skb)
@@ -2097,7 +2134,8 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status)
rd_curr++;
if (rd_curr >= vptr->options.numrx)
rd_curr = 0;
- } while (++works <= 15);
+ works++;
+ }
vptr->rx.curr = rd_curr;
@@ -2108,6 +2146,40 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status)
return works;
}
+static int velocity_poll(struct napi_struct *napi, int budget)
+{
+ struct velocity_info *vptr = container_of(napi,
+ struct velocity_info, napi);
+ unsigned int rx_done;
+ u32 isr_status;
+
+ spin_lock(&vptr->lock);
+ isr_status = mac_read_isr(vptr->mac_regs);
+
+ /* Ack the interrupt */
+ mac_write_isr(vptr->mac_regs, isr_status);
+ if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI)))
+ velocity_error(vptr, isr_status);
+
+ /*
+ * Do rx and tx twice for performance (taken from the VIA
+ * out-of-tree driver).
+ */
+ rx_done = velocity_rx_srv(vptr, isr_status, budget / 2);
+ velocity_tx_srv(vptr, isr_status);
+ rx_done += velocity_rx_srv(vptr, isr_status, budget - rx_done);
+ velocity_tx_srv(vptr, isr_status);
+
+ spin_unlock(&vptr->lock);
+
+ /* If budget not fully consumed, exit the polling mode */
+ if (rx_done < budget) {
+ napi_complete(napi);
+ mac_enable_int(vptr->mac_regs);
+ }
+
+ return rx_done;
+}
/**
* velocity_intr - interrupt callback
@@ -2124,8 +2196,6 @@ static irqreturn_t velocity_intr(int irq, void *dev_instance)
struct net_device *dev = dev_instance;
struct velocity_info *vptr = netdev_priv(dev);
u32 isr_status;
- int max_count = 0;
-
spin_lock(&vptr->lock);
isr_status = mac_read_isr(vptr->mac_regs);
@@ -2136,32 +2206,13 @@ static irqreturn_t velocity_intr(int irq, void *dev_instance)
return IRQ_NONE;
}
- mac_disable_int(vptr->mac_regs);
-
- /*
- * Keep processing the ISR until we have completed
- * processing and the isr_status becomes zero
- */
-
- while (isr_status != 0) {
- mac_write_isr(vptr->mac_regs, isr_status);
- if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI)))
- velocity_error(vptr, isr_status);
- if (isr_status & (ISR_PRXI | ISR_PPRXI))
- max_count += velocity_rx_srv(vptr, isr_status);
- if (isr_status & (ISR_PTXI | ISR_PPTXI))
- max_count += velocity_tx_srv(vptr, isr_status);
- isr_status = mac_read_isr(vptr->mac_regs);
- if (max_count > vptr->options.int_works) {
- printk(KERN_WARNING "%s: excessive work at interrupt.\n",
- dev->name);
- max_count = 0;
- }
+ if (likely(napi_schedule_prep(&vptr->napi))) {
+ mac_disable_int(vptr->mac_regs);
+ __napi_schedule(&vptr->napi);
}
spin_unlock(&vptr->lock);
- mac_enable_int(vptr->mac_regs);
- return IRQ_HANDLED;
+ return IRQ_HANDLED;
}
/**
@@ -2190,7 +2241,7 @@ static int velocity_open(struct net_device *dev)
velocity_init_registers(vptr, VELOCITY_INIT_COLD);
- ret = request_irq(vptr->pdev->irq, &velocity_intr, IRQF_SHARED,
+ ret = request_irq(vptr->pdev->irq, velocity_intr, IRQF_SHARED,
dev->name, dev);
if (ret < 0) {
/* Power down the chip */
@@ -2201,6 +2252,7 @@ static int velocity_open(struct net_device *dev)
mac_enable_int(vptr->mac_regs);
netif_start_queue(dev);
+ napi_enable(&vptr->napi);
vptr->flags |= VELOCITY_FLAGS_OPENED;
out:
return ret;
@@ -2436,6 +2488,7 @@ static int velocity_close(struct net_device *dev)
{
struct velocity_info *vptr = netdev_priv(dev);
+ napi_disable(&vptr->napi);
netif_stop_queue(dev);
velocity_shutdown(vptr);
@@ -2470,14 +2523,22 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
struct velocity_td_info *tdinfo;
unsigned long flags;
int pktlen;
- __le16 len;
- int index;
+ int index, prev;
+ int i = 0;
if (skb_padto(skb, ETH_ZLEN))
goto out;
- pktlen = max_t(unsigned int, skb->len, ETH_ZLEN);
- len = cpu_to_le16(pktlen);
+ /* The hardware can handle at most 7 memory segments, so merge
+ * the skb if there are more */
+ if (skb_shinfo(skb)->nr_frags > 6 && __skb_linearize(skb)) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ pktlen = skb_shinfo(skb)->nr_frags == 0 ?
+ max_t(unsigned int, skb->len, ETH_ZLEN) :
+ skb_headlen(skb);
spin_lock_irqsave(&vptr->lock, flags);
@@ -2494,11 +2555,24 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
*/
tdinfo->skb = skb;
tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE);
- td_ptr->tdesc0.len = len;
+ td_ptr->tdesc0.len = cpu_to_le16(pktlen);
td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]);
td_ptr->td_buf[0].pa_high = 0;
- td_ptr->td_buf[0].size = len;
- tdinfo->nskb_dma = 1;
+ td_ptr->td_buf[0].size = cpu_to_le16(pktlen);
+
+ /* Handle fragments */
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ tdinfo->skb_dma[i + 1] = pci_map_page(vptr->pdev, frag->page,
+ frag->page_offset, frag->size,
+ PCI_DMA_TODEVICE);
+
+ td_ptr->td_buf[i + 1].pa_low = cpu_to_le32(tdinfo->skb_dma[i + 1]);
+ td_ptr->td_buf[i + 1].pa_high = 0;
+ td_ptr->td_buf[i + 1].size = cpu_to_le16(frag->size);
+ }
+ tdinfo->nskb_dma = i + 1;
td_ptr->tdesc1.cmd = TCPLS_NORMAL + (tdinfo->nskb_dma + 1) * 16;
@@ -2510,8 +2584,8 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
/*
* Handle hardware checksum
*/
- if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM)
- && (skb->ip_summed == CHECKSUM_PARTIAL)) {
+ if ((dev->features & NETIF_F_IP_CSUM) &&
+ (skb->ip_summed == CHECKSUM_PARTIAL)) {
const struct iphdr *ip = ip_hdr(skb);
if (ip->protocol == IPPROTO_TCP)
td_ptr->tdesc1.TCR |= TCR0_TCPCK;
@@ -2519,23 +2593,21 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb,
td_ptr->tdesc1.TCR |= (TCR0_UDPCK);
td_ptr->tdesc1.TCR |= TCR0_IPCK;
}
- {
- int prev = index - 1;
+ prev = index - 1;
+ if (prev < 0)
+ prev = vptr->options.numtx - 1;
+ td_ptr->tdesc0.len |= OWNED_BY_NIC;
+ vptr->tx.used[qnum]++;
+ vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx;
- if (prev < 0)
- prev = vptr->options.numtx - 1;
- td_ptr->tdesc0.len |= OWNED_BY_NIC;
- vptr->tx.used[qnum]++;
- vptr->tx.curr[qnum] = (index + 1) % vptr->options.numtx;
+ if (AVAIL_TD(vptr, qnum) < 1)
+ netif_stop_queue(dev);
- if (AVAIL_TD(vptr, qnum) < 1)
- netif_stop_queue(dev);
+ td_ptr = &(vptr->tx.rings[qnum][prev]);
+ td_ptr->td_buf[0].size |= TD_QUEUE;
+ mac_tx_queue_wake(vptr->mac_regs, qnum);
- td_ptr = &(vptr->tx.rings[qnum][prev]);
- td_ptr->td_buf[0].size |= TD_QUEUE;
- mac_tx_queue_wake(vptr->mac_regs, qnum);
- }
dev->trans_start = jiffies;
spin_unlock_irqrestore(&vptr->lock, flags);
out:
@@ -2578,7 +2650,6 @@ static void __devinit velocity_init_info(struct pci_dev *pdev,
vptr->tx.numq = info->txqueue;
vptr->multicast_limit = MCAM_SIZE;
spin_lock_init(&vptr->lock);
- INIT_LIST_HEAD(&vptr->list);
}
/**
@@ -2755,12 +2826,10 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi
dev->irq = pdev->irq;
dev->netdev_ops = &velocity_netdev_ops;
dev->ethtool_ops = &velocity_ethtool_ops;
+ netif_napi_add(dev, &vptr->napi, velocity_poll, VELOCITY_NAPI_WEIGHT);
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER |
- NETIF_F_HW_VLAN_RX;
-
- if (vptr->flags & VELOCITY_FLAGS_TX_CSUM)
- dev->features |= NETIF_F_IP_CSUM;
+ NETIF_F_HW_VLAN_RX | NETIF_F_IP_CSUM;
ret = register_netdev(dev);
if (ret < 0)
@@ -2777,15 +2846,6 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi
/* and leave the chip powered down */
pci_set_power_state(pdev, PCI_D3hot);
-#ifdef CONFIG_PM
- {
- unsigned long flags;
-
- spin_lock_irqsave(&velocity_dev_list_lock, flags);
- list_add(&vptr->list, &velocity_dev_list);
- spin_unlock_irqrestore(&velocity_dev_list_lock, flags);
- }
-#endif
velocity_nics++;
out:
return ret;
@@ -3222,15 +3282,114 @@ static void velocity_set_msglevel(struct net_device *dev, u32 value)
msglevel = value;
}
+static int get_pending_timer_val(int val)
+{
+ int mult_bits = val >> 6;
+ int mult = 1;
+
+ switch (mult_bits)
+ {
+ case 1:
+ mult = 4; break;
+ case 2:
+ mult = 16; break;
+ case 3:
+ mult = 64; break;
+ case 0:
+ default:
+ break;
+ }
+
+ return (val & 0x3f) * mult;
+}
+
+static void set_pending_timer_val(int *val, u32 us)
+{
+ u8 mult = 0;
+ u8 shift = 0;
+
+ if (us >= 0x3f) {
+ mult = 1; /* mult with 4 */
+ shift = 2;
+ }
+ if (us >= 0x3f * 4) {
+ mult = 2; /* mult with 16 */
+ shift = 4;
+ }
+ if (us >= 0x3f * 16) {
+ mult = 3; /* mult with 64 */
+ shift = 6;
+ }
+
+ *val = (mult << 6) | ((us >> shift) & 0x3f);
+}
+
+
+static int velocity_get_coalesce(struct net_device *dev,
+ struct ethtool_coalesce *ecmd)
+{
+ struct velocity_info *vptr = netdev_priv(dev);
+
+ ecmd->tx_max_coalesced_frames = vptr->options.tx_intsup;
+ ecmd->rx_max_coalesced_frames = vptr->options.rx_intsup;
+
+ ecmd->rx_coalesce_usecs = get_pending_timer_val(vptr->options.rxqueue_timer);
+ ecmd->tx_coalesce_usecs = get_pending_timer_val(vptr->options.txqueue_timer);
+
+ return 0;
+}
+
+static int velocity_set_coalesce(struct net_device *dev,
+ struct ethtool_coalesce *ecmd)
+{
+ struct velocity_info *vptr = netdev_priv(dev);
+ int max_us = 0x3f * 64;
+
+ /* 6 bits of */
+ if (ecmd->tx_coalesce_usecs > max_us)
+ return -EINVAL;
+ if (ecmd->rx_coalesce_usecs > max_us)
+ return -EINVAL;
+
+ if (ecmd->tx_max_coalesced_frames > 0xff)
+ return -EINVAL;
+ if (ecmd->rx_max_coalesced_frames > 0xff)
+ return -EINVAL;
+
+ vptr->options.rx_intsup = ecmd->rx_max_coalesced_frames;
+ vptr->options.tx_intsup = ecmd->tx_max_coalesced_frames;
+
+ set_pending_timer_val(&vptr->options.rxqueue_timer,
+ ecmd->rx_coalesce_usecs);
+ set_pending_timer_val(&vptr->options.txqueue_timer,
+ ecmd->tx_coalesce_usecs);
+
+ /* Setup the interrupt suppression and queue timers */
+ mac_disable_int(vptr->mac_regs);
+ setup_adaptive_interrupts(vptr);
+ setup_queue_timers(vptr);
+
+ mac_write_int_mask(vptr->int_mask, vptr->mac_regs);
+ mac_clear_isr(vptr->mac_regs);
+ mac_enable_int(vptr->mac_regs);
+
+ return 0;
+}
+
static const struct ethtool_ops velocity_ethtool_ops = {
.get_settings = velocity_get_settings,
.set_settings = velocity_set_settings,
.get_drvinfo = velocity_get_drvinfo,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .get_tx_csum = ethtool_op_get_tx_csum,
.get_wol = velocity_ethtool_get_wol,
.set_wol = velocity_ethtool_set_wol,
.get_msglevel = velocity_get_msglevel,
.set_msglevel = velocity_set_msglevel,
+ .set_sg = ethtool_op_set_sg,
.get_link = velocity_get_link,
+ .get_coalesce = velocity_get_coalesce,
+ .set_coalesce = velocity_set_coalesce,
.begin = velocity_ethtool_up,
.complete = velocity_ethtool_down
};
@@ -3241,20 +3400,10 @@ static int velocity_netdev_event(struct notifier_block *nb, unsigned long notifi
{
struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
struct net_device *dev = ifa->ifa_dev->dev;
- struct velocity_info *vptr;
- unsigned long flags;
- if (dev_net(dev) != &init_net)
- return NOTIFY_DONE;
-
- spin_lock_irqsave(&velocity_dev_list_lock, flags);
- list_for_each_entry(vptr, &velocity_dev_list, list) {
- if (vptr->dev == dev) {
- velocity_get_ip(vptr);
- break;
- }
- }
- spin_unlock_irqrestore(&velocity_dev_list_lock, flags);
+ if (dev_net(dev) == &init_net &&
+ dev->netdev_ops == &velocity_netdev_ops)
+ velocity_get_ip(netdev_priv(dev));
return NOTIFY_DONE;
}
diff --git a/drivers/net/via-velocity.h b/drivers/net/via-velocity.h
index 2f00c13ab50..ef4a0f64ba1 100644
--- a/drivers/net/via-velocity.h
+++ b/drivers/net/via-velocity.h
@@ -29,9 +29,10 @@
#define VELOCITY_NAME "via-velocity"
#define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver"
-#define VELOCITY_VERSION "1.14"
+#define VELOCITY_VERSION "1.15"
#define VELOCITY_IO_SIZE 256
+#define VELOCITY_NAPI_WEIGHT 64
#define PKT_BUF_SZ 1540
@@ -1005,7 +1006,8 @@ struct mac_regs {
volatile __le32 RDBaseLo; /* 0x38 */
volatile __le16 RDIdx; /* 0x3C */
- volatile __le16 reserved_3E;
+ volatile u8 TQETMR; /* 0x3E, VT3216 and above only */
+ volatile u8 RQETMR; /* 0x3F, VT3216 and above only */
volatile __le32 TDBaseLo[4]; /* 0x40 */
@@ -1421,7 +1423,6 @@ enum velocity_msg_level {
*/
#define VELOCITY_FLAGS_TAGGING 0x00000001UL
-#define VELOCITY_FLAGS_TX_CSUM 0x00000002UL
#define VELOCITY_FLAGS_RX_CSUM 0x00000004UL
#define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL
#define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL
@@ -1491,6 +1492,10 @@ struct velocity_opt {
int rx_bandwidth_hi;
int rx_bandwidth_lo;
int rx_bandwidth_en;
+ int rxqueue_timer;
+ int txqueue_timer;
+ int tx_intsup;
+ int rx_intsup;
u32 flags;
};
@@ -1499,8 +1504,6 @@ struct velocity_opt {
#define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx])
struct velocity_info {
- struct list_head list;
-
struct pci_dev *pdev;
struct net_device *dev;
@@ -1559,6 +1562,8 @@ struct velocity_info {
u32 ticks;
u8 rev_id;
+
+ struct napi_struct napi;
};
/**
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index b9e002fccbc..c708ecc3cb2 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -282,13 +282,12 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp)
do {
struct skb_vnet_hdr *hdr;
- skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(vi->dev, MAX_PACKET_LEN);
if (unlikely(!skb)) {
oom = true;
break;
}
- skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, MAX_PACKET_LEN);
hdr = skb_vnet_hdr(skb);
@@ -343,14 +342,12 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp)
do {
skb_frag_t *f;
- skb = netdev_alloc_skb(vi->dev, GOOD_COPY_LEN + NET_IP_ALIGN);
+ skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN);
if (unlikely(!skb)) {
oom = true;
break;
}
- skb_reserve(skb, NET_IP_ALIGN);
-
f = &skb_shinfo(skb)->frags[0];
f->page = get_a_page(vi, gfp);
if (!f->page) {
@@ -431,8 +428,8 @@ again:
/* Out of packets? */
if (received < budget) {
napi_complete(napi);
- if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq))
- && napi_schedule_prep(napi)) {
+ if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq)) &&
+ napi_schedule_prep(napi)) {
vi->rvq->vq_ops->disable_cb(vi->rvq);
__napi_schedule(napi);
goto again;
@@ -893,9 +890,9 @@ static int virtnet_probe(struct virtio_device *vdev)
INIT_DELAYED_WORK(&vi->refill, refill_work);
/* If we can receive ANY GSO packets, we must allocate large ones. */
- if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4)
- || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6)
- || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) ||
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) ||
+ virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
vi->big_packets = true;
if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
diff --git a/drivers/net/vmxnet3/vmxnet3_defs.h b/drivers/net/vmxnet3/vmxnet3_defs.h
index dc8ee4438a4..b4889e6c4a5 100644
--- a/drivers/net/vmxnet3/vmxnet3_defs.h
+++ b/drivers/net/vmxnet3/vmxnet3_defs.h
@@ -90,23 +90,60 @@ enum {
VMXNET3_CMD_GET_CONF_INTR
};
-struct Vmxnet3_TxDesc {
- u64 addr;
+/*
+ * Little Endian layout of bitfields -
+ * Byte 0 : 7.....len.....0
+ * Byte 1 : rsvd gen 13.len.8
+ * Byte 2 : 5.msscof.0 ext1 dtype
+ * Byte 3 : 13...msscof...6
+ *
+ * Big Endian layout of bitfields -
+ * Byte 0: 13...msscof...6
+ * Byte 1 : 5.msscof.0 ext1 dtype
+ * Byte 2 : rsvd gen 13.len.8
+ * Byte 3 : 7.....len.....0
+ *
+ * Thus, le32_to_cpu on the dword will allow the big endian driver to read
+ * the bit fields correctly. And cpu_to_le32 will convert bitfields
+ * bit fields written by big endian driver to format required by device.
+ */
- u32 len:14;
- u32 gen:1; /* generation bit */
- u32 rsvd:1;
- u32 dtype:1; /* descriptor type */
- u32 ext1:1;
- u32 msscof:14; /* MSS, checksum offset, flags */
-
- u32 hlen:10; /* header len */
- u32 om:2; /* offload mode */
- u32 eop:1; /* End Of Packet */
- u32 cq:1; /* completion request */
- u32 ext2:1;
- u32 ti:1; /* VLAN Tag Insertion */
- u32 tci:16; /* Tag to Insert */
+struct Vmxnet3_TxDesc {
+ __le64 addr;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 msscof:14; /* MSS, checksum offset, flags */
+ u32 ext1:1;
+ u32 dtype:1; /* descriptor type */
+ u32 rsvd:1;
+ u32 gen:1; /* generation bit */
+ u32 len:14;
+#else
+ u32 len:14;
+ u32 gen:1; /* generation bit */
+ u32 rsvd:1;
+ u32 dtype:1; /* descriptor type */
+ u32 ext1:1;
+ u32 msscof:14; /* MSS, checksum offset, flags */
+#endif /* __BIG_ENDIAN_BITFIELD */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 tci:16; /* Tag to Insert */
+ u32 ti:1; /* VLAN Tag Insertion */
+ u32 ext2:1;
+ u32 cq:1; /* completion request */
+ u32 eop:1; /* End Of Packet */
+ u32 om:2; /* offload mode */
+ u32 hlen:10; /* header len */
+#else
+ u32 hlen:10; /* header len */
+ u32 om:2; /* offload mode */
+ u32 eop:1; /* End Of Packet */
+ u32 cq:1; /* completion request */
+ u32 ext2:1;
+ u32 ti:1; /* VLAN Tag Insertion */
+ u32 tci:16; /* Tag to Insert */
+#endif /* __BIG_ENDIAN_BITFIELD */
};
/* TxDesc.OM values */
@@ -118,6 +155,8 @@ struct Vmxnet3_TxDesc {
#define VMXNET3_TXD_EOP_SHIFT 12
#define VMXNET3_TXD_CQ_SHIFT 13
#define VMXNET3_TXD_GEN_SHIFT 14
+#define VMXNET3_TXD_EOP_DWORD_SHIFT 3
+#define VMXNET3_TXD_GEN_DWORD_SHIFT 2
#define VMXNET3_TXD_CQ (1 << VMXNET3_TXD_CQ_SHIFT)
#define VMXNET3_TXD_EOP (1 << VMXNET3_TXD_EOP_SHIFT)
@@ -130,29 +169,40 @@ struct Vmxnet3_TxDataDesc {
u8 data[VMXNET3_HDR_COPY_SIZE];
};
+#define VMXNET3_TCD_GEN_SHIFT 31
+#define VMXNET3_TCD_GEN_SIZE 1
+#define VMXNET3_TCD_TXIDX_SHIFT 0
+#define VMXNET3_TCD_TXIDX_SIZE 12
+#define VMXNET3_TCD_GEN_DWORD_SHIFT 3
struct Vmxnet3_TxCompDesc {
u32 txdIdx:12; /* Index of the EOP TxDesc */
u32 ext1:20;
- u32 ext2;
- u32 ext3;
+ __le32 ext2;
+ __le32 ext3;
u32 rsvd:24;
u32 type:7; /* completion type */
u32 gen:1; /* generation bit */
};
-
struct Vmxnet3_RxDesc {
- u64 addr;
+ __le64 addr;
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 gen:1; /* Generation bit */
+ u32 rsvd:15;
+ u32 dtype:1; /* Descriptor type */
+ u32 btype:1; /* Buffer Type */
+ u32 len:14;
+#else
u32 len:14;
u32 btype:1; /* Buffer Type */
u32 dtype:1; /* Descriptor type */
u32 rsvd:15;
u32 gen:1; /* Generation bit */
-
+#endif
u32 ext1;
};
@@ -164,8 +214,17 @@ struct Vmxnet3_RxDesc {
#define VMXNET3_RXD_BTYPE_SHIFT 14
#define VMXNET3_RXD_GEN_SHIFT 31
-
struct Vmxnet3_RxCompDesc {
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 ext2:1;
+ u32 cnc:1; /* Checksum Not Calculated */
+ u32 rssType:4; /* RSS hash type used */
+ u32 rqID:10; /* rx queue/ring ID */
+ u32 sop:1; /* Start of Packet */
+ u32 eop:1; /* End of Packet */
+ u32 ext1:2;
+ u32 rxdIdx:12; /* Index of the RxDesc */
+#else
u32 rxdIdx:12; /* Index of the RxDesc */
u32 ext1:2;
u32 eop:1; /* End of Packet */
@@ -174,14 +233,36 @@ struct Vmxnet3_RxCompDesc {
u32 rssType:4; /* RSS hash type used */
u32 cnc:1; /* Checksum Not Calculated */
u32 ext2:1;
+#endif /* __BIG_ENDIAN_BITFIELD */
- u32 rssHash; /* RSS hash value */
+ __le32 rssHash; /* RSS hash value */
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 tci:16; /* Tag stripped */
+ u32 ts:1; /* Tag is stripped */
+ u32 err:1; /* Error */
+ u32 len:14; /* data length */
+#else
u32 len:14; /* data length */
u32 err:1; /* Error */
u32 ts:1; /* Tag is stripped */
u32 tci:16; /* Tag stripped */
+#endif /* __BIG_ENDIAN_BITFIELD */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 gen:1; /* generation bit */
+ u32 type:7; /* completion type */
+ u32 fcs:1; /* Frame CRC correct */
+ u32 frg:1; /* IP Fragment */
+ u32 v4:1; /* IPv4 */
+ u32 v6:1; /* IPv6 */
+ u32 ipc:1; /* IP Checksum Correct */
+ u32 tcp:1; /* TCP packet */
+ u32 udp:1; /* UDP packet */
+ u32 tuc:1; /* TCP/UDP Checksum Correct */
+ u32 csum:16;
+#else
u32 csum:16;
u32 tuc:1; /* TCP/UDP Checksum Correct */
u32 udp:1; /* UDP packet */
@@ -193,6 +274,7 @@ struct Vmxnet3_RxCompDesc {
u32 fcs:1; /* Frame CRC correct */
u32 type:7; /* completion type */
u32 gen:1; /* generation bit */
+#endif /* __BIG_ENDIAN_BITFIELD */
};
/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */
@@ -206,6 +288,8 @@ struct Vmxnet3_RxCompDesc {
/* csum OK for TCP/UDP pkts over IP */
#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \
1 << VMXNET3_RCD_IPC_SHIFT)
+#define VMXNET3_TXD_GEN_SIZE 1
+#define VMXNET3_TXD_EOP_SIZE 1
/* value of RxCompDesc.rssType */
enum {
@@ -219,9 +303,9 @@ enum {
/* a union for accessing all cmd/completion descriptors */
union Vmxnet3_GenericDesc {
- u64 qword[2];
- u32 dword[4];
- u16 word[8];
+ __le64 qword[2];
+ __le32 dword[4];
+ __le16 word[8];
struct Vmxnet3_TxDesc txd;
struct Vmxnet3_RxDesc rxd;
struct Vmxnet3_TxCompDesc tcd;
@@ -287,18 +371,24 @@ enum {
struct Vmxnet3_GOSInfo {
- u32 gosBits:2; /* 32-bit or 64-bit? */
- u32 gosType:4; /* which guest */
- u32 gosVer:16; /* gos version */
- u32 gosMisc:10; /* other info about gos */
+#ifdef __BIG_ENDIAN_BITFIELD
+ u32 gosMisc:10; /* other info about gos */
+ u32 gosVer:16; /* gos version */
+ u32 gosType:4; /* which guest */
+ u32 gosBits:2; /* 32-bit or 64-bit? */
+#else
+ u32 gosBits:2; /* 32-bit or 64-bit? */
+ u32 gosType:4; /* which guest */
+ u32 gosVer:16; /* gos version */
+ u32 gosMisc:10; /* other info about gos */
+#endif /* __BIG_ENDIAN_BITFIELD */
};
-
struct Vmxnet3_DriverInfo {
- u32 version;
+ __le32 version;
struct Vmxnet3_GOSInfo gos;
- u32 vmxnet3RevSpt;
- u32 uptVerSpt;
+ __le32 vmxnet3RevSpt;
+ __le32 uptVerSpt;
};
@@ -315,42 +405,42 @@ struct Vmxnet3_DriverInfo {
struct Vmxnet3_MiscConf {
struct Vmxnet3_DriverInfo driverInfo;
- u64 uptFeatures;
- u64 ddPA; /* driver data PA */
- u64 queueDescPA; /* queue descriptor table PA */
- u32 ddLen; /* driver data len */
- u32 queueDescLen; /* queue desc. table len in bytes */
- u32 mtu;
- u16 maxNumRxSG;
+ __le64 uptFeatures;
+ __le64 ddPA; /* driver data PA */
+ __le64 queueDescPA; /* queue descriptor table PA */
+ __le32 ddLen; /* driver data len */
+ __le32 queueDescLen; /* queue desc. table len in bytes */
+ __le32 mtu;
+ __le16 maxNumRxSG;
u8 numTxQueues;
u8 numRxQueues;
- u32 reserved[4];
+ __le32 reserved[4];
};
struct Vmxnet3_TxQueueConf {
- u64 txRingBasePA;
- u64 dataRingBasePA;
- u64 compRingBasePA;
- u64 ddPA; /* driver data */
- u64 reserved;
- u32 txRingSize; /* # of tx desc */
- u32 dataRingSize; /* # of data desc */
- u32 compRingSize; /* # of comp desc */
- u32 ddLen; /* size of driver data */
+ __le64 txRingBasePA;
+ __le64 dataRingBasePA;
+ __le64 compRingBasePA;
+ __le64 ddPA; /* driver data */
+ __le64 reserved;
+ __le32 txRingSize; /* # of tx desc */
+ __le32 dataRingSize; /* # of data desc */
+ __le32 compRingSize; /* # of comp desc */
+ __le32 ddLen; /* size of driver data */
u8 intrIdx;
u8 _pad[7];
};
struct Vmxnet3_RxQueueConf {
- u64 rxRingBasePA[2];
- u64 compRingBasePA;
- u64 ddPA; /* driver data */
- u64 reserved;
- u32 rxRingSize[2]; /* # of rx desc */
- u32 compRingSize; /* # of rx comp desc */
- u32 ddLen; /* size of driver data */
+ __le64 rxRingBasePA[2];
+ __le64 compRingBasePA;
+ __le64 ddPA; /* driver data */
+ __le64 reserved;
+ __le32 rxRingSize[2]; /* # of rx desc */
+ __le32 compRingSize; /* # of rx comp desc */
+ __le32 ddLen; /* size of driver data */
u8 intrIdx;
u8 _pad[7];
};
@@ -381,7 +471,7 @@ struct Vmxnet3_IntrConf {
u8 eventIntrIdx;
u8 modLevels[VMXNET3_MAX_INTRS]; /* moderation level for
* each intr */
- u32 reserved[3];
+ __le32 reserved[3];
};
/* one bit per VLAN ID, the size is in the units of u32 */
@@ -391,21 +481,21 @@ struct Vmxnet3_IntrConf {
struct Vmxnet3_QueueStatus {
bool stopped;
u8 _pad[3];
- u32 error;
+ __le32 error;
};
struct Vmxnet3_TxQueueCtrl {
- u32 txNumDeferred;
- u32 txThreshold;
- u64 reserved;
+ __le32 txNumDeferred;
+ __le32 txThreshold;
+ __le64 reserved;
};
struct Vmxnet3_RxQueueCtrl {
bool updateRxProd;
u8 _pad[7];
- u64 reserved;
+ __le64 reserved;
};
enum {
@@ -417,11 +507,11 @@ enum {
};
struct Vmxnet3_RxFilterConf {
- u32 rxMode; /* VMXNET3_RXM_xxx */
- u16 mfTableLen; /* size of the multicast filter table */
- u16 _pad1;
- u64 mfTablePA; /* PA of the multicast filters table */
- u32 vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */
+ __le32 rxMode; /* VMXNET3_RXM_xxx */
+ __le16 mfTableLen; /* size of the multicast filter table */
+ __le16 _pad1;
+ __le64 mfTablePA; /* PA of the multicast filters table */
+ __le32 vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */
};
@@ -444,7 +534,7 @@ struct Vmxnet3_PM_PktFilter {
struct Vmxnet3_PMConf {
- u16 wakeUpEvents; /* VMXNET3_PM_WAKEUP_xxx */
+ __le16 wakeUpEvents; /* VMXNET3_PM_WAKEUP_xxx */
u8 numFilters;
u8 pad[5];
struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS];
@@ -452,9 +542,9 @@ struct Vmxnet3_PMConf {
struct Vmxnet3_VariableLenConfDesc {
- u32 confVer;
- u32 confLen;
- u64 confPA;
+ __le32 confVer;
+ __le32 confLen;
+ __le64 confPA;
};
@@ -491,12 +581,12 @@ struct Vmxnet3_DSDevRead {
/* All structures in DriverShared are padded to multiples of 8 bytes */
struct Vmxnet3_DriverShared {
- u32 magic;
+ __le32 magic;
/* make devRead start at 64bit boundaries */
- u32 pad;
- struct Vmxnet3_DSDevRead devRead;
- u32 ecr;
- u32 reserved[5];
+ __le32 pad;
+ struct Vmxnet3_DSDevRead devRead;
+ __le32 ecr;
+ __le32 reserved[5];
};
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 004353a46af..1ceb9d0f8b9 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -24,12 +24,13 @@
*
*/
+#include <net/ip6_checksum.h>
+
#include "vmxnet3_int.h"
char vmxnet3_driver_name[] = "vmxnet3";
#define VMXNET3_DRIVER_DESC "VMware vmxnet3 virtual NIC driver"
-
/*
* PCI Device ID Table
* Last entry must be all 0s
@@ -151,11 +152,10 @@ vmxnet3_check_link(struct vmxnet3_adapter *adapter)
}
}
-
static void
vmxnet3_process_events(struct vmxnet3_adapter *adapter)
{
- u32 events = adapter->shared->ecr;
+ u32 events = le32_to_cpu(adapter->shared->ecr);
if (!events)
return;
@@ -173,7 +173,7 @@ vmxnet3_process_events(struct vmxnet3_adapter *adapter)
if (adapter->tqd_start->status.stopped) {
printk(KERN_ERR "%s: tq error 0x%x\n",
adapter->netdev->name,
- adapter->tqd_start->status.error);
+ le32_to_cpu(adapter->tqd_start->status.error));
}
if (adapter->rqd_start->status.stopped) {
printk(KERN_ERR "%s: rq error 0x%x\n",
@@ -185,6 +185,106 @@ vmxnet3_process_events(struct vmxnet3_adapter *adapter)
}
}
+#ifdef __BIG_ENDIAN_BITFIELD
+/*
+ * The device expects the bitfields in shared structures to be written in
+ * little endian. When CPU is big endian, the following routines are used to
+ * correctly read and write into ABI.
+ * The general technique used here is : double word bitfields are defined in
+ * opposite order for big endian architecture. Then before reading them in
+ * driver the complete double word is translated using le32_to_cpu. Similarly
+ * After the driver writes into bitfields, cpu_to_le32 is used to translate the
+ * double words into required format.
+ * In order to avoid touching bits in shared structure more than once, temporary
+ * descriptors are used. These are passed as srcDesc to following functions.
+ */
+static void vmxnet3_RxDescToCPU(const struct Vmxnet3_RxDesc *srcDesc,
+ struct Vmxnet3_RxDesc *dstDesc)
+{
+ u32 *src = (u32 *)srcDesc + 2;
+ u32 *dst = (u32 *)dstDesc + 2;
+ dstDesc->addr = le64_to_cpu(srcDesc->addr);
+ *dst = le32_to_cpu(*src);
+ dstDesc->ext1 = le32_to_cpu(srcDesc->ext1);
+}
+
+static void vmxnet3_TxDescToLe(const struct Vmxnet3_TxDesc *srcDesc,
+ struct Vmxnet3_TxDesc *dstDesc)
+{
+ int i;
+ u32 *src = (u32 *)(srcDesc + 1);
+ u32 *dst = (u32 *)(dstDesc + 1);
+
+ /* Working backwards so that the gen bit is set at the end. */
+ for (i = 2; i > 0; i--) {
+ src--;
+ dst--;
+ *dst = cpu_to_le32(*src);
+ }
+}
+
+
+static void vmxnet3_RxCompToCPU(const struct Vmxnet3_RxCompDesc *srcDesc,
+ struct Vmxnet3_RxCompDesc *dstDesc)
+{
+ int i = 0;
+ u32 *src = (u32 *)srcDesc;
+ u32 *dst = (u32 *)dstDesc;
+ for (i = 0; i < sizeof(struct Vmxnet3_RxCompDesc) / sizeof(u32); i++) {
+ *dst = le32_to_cpu(*src);
+ src++;
+ dst++;
+ }
+}
+
+
+/* Used to read bitfield values from double words. */
+static u32 get_bitfield32(const __le32 *bitfield, u32 pos, u32 size)
+{
+ u32 temp = le32_to_cpu(*bitfield);
+ u32 mask = ((1 << size) - 1) << pos;
+ temp &= mask;
+ temp >>= pos;
+ return temp;
+}
+
+
+
+#endif /* __BIG_ENDIAN_BITFIELD */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+
+# define VMXNET3_TXDESC_GET_GEN(txdesc) get_bitfield32(((const __le32 *) \
+ txdesc) + VMXNET3_TXD_GEN_DWORD_SHIFT, \
+ VMXNET3_TXD_GEN_SHIFT, VMXNET3_TXD_GEN_SIZE)
+# define VMXNET3_TXDESC_GET_EOP(txdesc) get_bitfield32(((const __le32 *) \
+ txdesc) + VMXNET3_TXD_EOP_DWORD_SHIFT, \
+ VMXNET3_TXD_EOP_SHIFT, VMXNET3_TXD_EOP_SIZE)
+# define VMXNET3_TCD_GET_GEN(tcd) get_bitfield32(((const __le32 *)tcd) + \
+ VMXNET3_TCD_GEN_DWORD_SHIFT, VMXNET3_TCD_GEN_SHIFT, \
+ VMXNET3_TCD_GEN_SIZE)
+# define VMXNET3_TCD_GET_TXIDX(tcd) get_bitfield32((const __le32 *)tcd, \
+ VMXNET3_TCD_TXIDX_SHIFT, VMXNET3_TCD_TXIDX_SIZE)
+# define vmxnet3_getRxComp(dstrcd, rcd, tmp) do { \
+ (dstrcd) = (tmp); \
+ vmxnet3_RxCompToCPU((rcd), (tmp)); \
+ } while (0)
+# define vmxnet3_getRxDesc(dstrxd, rxd, tmp) do { \
+ (dstrxd) = (tmp); \
+ vmxnet3_RxDescToCPU((rxd), (tmp)); \
+ } while (0)
+
+#else
+
+# define VMXNET3_TXDESC_GET_GEN(txdesc) ((txdesc)->gen)
+# define VMXNET3_TXDESC_GET_EOP(txdesc) ((txdesc)->eop)
+# define VMXNET3_TCD_GET_GEN(tcd) ((tcd)->gen)
+# define VMXNET3_TCD_GET_TXIDX(tcd) ((tcd)->txdIdx)
+# define vmxnet3_getRxComp(dstrcd, rcd, tmp) (dstrcd) = (rcd)
+# define vmxnet3_getRxDesc(dstrxd, rxd, tmp) (dstrxd) = (rxd)
+
+#endif /* __BIG_ENDIAN_BITFIELD */
+
static void
vmxnet3_unmap_tx_buf(struct vmxnet3_tx_buf_info *tbi,
@@ -212,7 +312,7 @@ vmxnet3_unmap_pkt(u32 eop_idx, struct vmxnet3_tx_queue *tq,
/* no out of order completion */
BUG_ON(tq->buf_info[eop_idx].sop_idx != tq->tx_ring.next2comp);
- BUG_ON(tq->tx_ring.base[eop_idx].txd.eop != 1);
+ BUG_ON(VMXNET3_TXDESC_GET_EOP(&(tq->tx_ring.base[eop_idx].txd)) != 1);
skb = tq->buf_info[eop_idx].skb;
BUG_ON(skb == NULL);
@@ -246,9 +346,10 @@ vmxnet3_tq_tx_complete(struct vmxnet3_tx_queue *tq,
union Vmxnet3_GenericDesc *gdesc;
gdesc = tq->comp_ring.base + tq->comp_ring.next2proc;
- while (gdesc->tcd.gen == tq->comp_ring.gen) {
- completed += vmxnet3_unmap_pkt(gdesc->tcd.txdIdx, tq,
- adapter->pdev, adapter);
+ while (VMXNET3_TCD_GET_GEN(&gdesc->tcd) == tq->comp_ring.gen) {
+ completed += vmxnet3_unmap_pkt(VMXNET3_TCD_GET_TXIDX(
+ &gdesc->tcd), tq, adapter->pdev,
+ adapter);
vmxnet3_comp_ring_adv_next2proc(&tq->comp_ring);
gdesc = tq->comp_ring.base + tq->comp_ring.next2proc;
@@ -472,9 +573,9 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx,
}
BUG_ON(rbi->dma_addr == 0);
- gd->rxd.addr = rbi->dma_addr;
- gd->dword[2] = (ring->gen << VMXNET3_RXD_GEN_SHIFT) | val |
- rbi->len;
+ gd->rxd.addr = cpu_to_le64(rbi->dma_addr);
+ gd->dword[2] = cpu_to_le32((ring->gen << VMXNET3_RXD_GEN_SHIFT)
+ | val | rbi->len);
num_allocated++;
vmxnet3_cmd_ring_adv_next2fill(ring);
@@ -531,10 +632,10 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
/* no need to map the buffer if headers are copied */
if (ctx->copy_size) {
- ctx->sop_txd->txd.addr = tq->data_ring.basePA +
+ ctx->sop_txd->txd.addr = cpu_to_le64(tq->data_ring.basePA +
tq->tx_ring.next2fill *
- sizeof(struct Vmxnet3_TxDataDesc);
- ctx->sop_txd->dword[2] = dw2 | ctx->copy_size;
+ sizeof(struct Vmxnet3_TxDataDesc));
+ ctx->sop_txd->dword[2] = cpu_to_le32(dw2 | ctx->copy_size);
ctx->sop_txd->dword[3] = 0;
tbi = tq->buf_info + tq->tx_ring.next2fill;
@@ -542,7 +643,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
dev_dbg(&adapter->netdev->dev,
"txd[%u]: 0x%Lx 0x%x 0x%x\n",
- tq->tx_ring.next2fill, ctx->sop_txd->txd.addr,
+ tq->tx_ring.next2fill,
+ le64_to_cpu(ctx->sop_txd->txd.addr),
ctx->sop_txd->dword[2], ctx->sop_txd->dword[3]);
vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring);
@@ -570,14 +672,14 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
gdesc = tq->tx_ring.base + tq->tx_ring.next2fill;
BUG_ON(gdesc->txd.gen == tq->tx_ring.gen);
- gdesc->txd.addr = tbi->dma_addr;
- gdesc->dword[2] = dw2 | buf_size;
+ gdesc->txd.addr = cpu_to_le64(tbi->dma_addr);
+ gdesc->dword[2] = cpu_to_le32(dw2 | buf_size);
gdesc->dword[3] = 0;
dev_dbg(&adapter->netdev->dev,
"txd[%u]: 0x%Lx 0x%x 0x%x\n",
- tq->tx_ring.next2fill, gdesc->txd.addr,
- gdesc->dword[2], gdesc->dword[3]);
+ tq->tx_ring.next2fill, le64_to_cpu(gdesc->txd.addr),
+ le32_to_cpu(gdesc->dword[2]), gdesc->dword[3]);
vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring);
dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT;
@@ -599,14 +701,14 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
gdesc = tq->tx_ring.base + tq->tx_ring.next2fill;
BUG_ON(gdesc->txd.gen == tq->tx_ring.gen);
- gdesc->txd.addr = tbi->dma_addr;
- gdesc->dword[2] = dw2 | frag->size;
+ gdesc->txd.addr = cpu_to_le64(tbi->dma_addr);
+ gdesc->dword[2] = cpu_to_le32(dw2 | frag->size);
gdesc->dword[3] = 0;
dev_dbg(&adapter->netdev->dev,
"txd[%u]: 0x%llu %u %u\n",
- tq->tx_ring.next2fill, gdesc->txd.addr,
- gdesc->dword[2], gdesc->dword[3]);
+ tq->tx_ring.next2fill, le64_to_cpu(gdesc->txd.addr),
+ le32_to_cpu(gdesc->dword[2]), gdesc->dword[3]);
vmxnet3_cmd_ring_adv_next2fill(&tq->tx_ring);
dw2 = tq->tx_ring.gen << VMXNET3_TXD_GEN_SHIFT;
}
@@ -751,6 +853,10 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
unsigned long flags;
struct vmxnet3_tx_ctx ctx;
union Vmxnet3_GenericDesc *gdesc;
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* Use temporary descriptor to avoid touching bits multiple times */
+ union Vmxnet3_GenericDesc tempTxDesc;
+#endif
/* conservatively estimate # of descriptors to use */
count = VMXNET3_TXD_NEEDED(skb_headlen(skb)) +
@@ -827,16 +933,22 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter);
/* setup the EOP desc */
- ctx.eop_txd->dword[3] = VMXNET3_TXD_CQ | VMXNET3_TXD_EOP;
+ ctx.eop_txd->dword[3] = cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP);
/* setup the SOP desc */
+#ifdef __BIG_ENDIAN_BITFIELD
+ gdesc = &tempTxDesc;
+ gdesc->dword[2] = ctx.sop_txd->dword[2];
+ gdesc->dword[3] = ctx.sop_txd->dword[3];
+#else
gdesc = ctx.sop_txd;
+#endif
if (ctx.mss) {
gdesc->txd.hlen = ctx.eth_ip_hdr_size + ctx.l4_hdr_size;
gdesc->txd.om = VMXNET3_OM_TSO;
gdesc->txd.msscof = ctx.mss;
- tq->shared->txNumDeferred += (skb->len - gdesc->txd.hlen +
- ctx.mss - 1) / ctx.mss;
+ le32_add_cpu(&tq->shared->txNumDeferred, (skb->len -
+ gdesc->txd.hlen + ctx.mss - 1) / ctx.mss);
} else {
if (skb->ip_summed == CHECKSUM_PARTIAL) {
gdesc->txd.hlen = ctx.eth_ip_hdr_size;
@@ -847,7 +959,7 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
gdesc->txd.om = 0;
gdesc->txd.msscof = 0;
}
- tq->shared->txNumDeferred++;
+ le32_add_cpu(&tq->shared->txNumDeferred, 1);
}
if (vlan_tx_tag_present(skb)) {
@@ -855,19 +967,27 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
gdesc->txd.tci = vlan_tx_tag_get(skb);
}
- wmb();
-
- /* finally flips the GEN bit of the SOP desc */
- gdesc->dword[2] ^= VMXNET3_TXD_GEN;
+ /* finally flips the GEN bit of the SOP desc. */
+ gdesc->dword[2] = cpu_to_le32(le32_to_cpu(gdesc->dword[2]) ^
+ VMXNET3_TXD_GEN);
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* Finished updating in bitfields of Tx Desc, so write them in original
+ * place.
+ */
+ vmxnet3_TxDescToLe((struct Vmxnet3_TxDesc *)gdesc,
+ (struct Vmxnet3_TxDesc *)ctx.sop_txd);
+ gdesc = ctx.sop_txd;
+#endif
dev_dbg(&adapter->netdev->dev,
"txd[%u]: SOP 0x%Lx 0x%x 0x%x\n",
(u32)((union Vmxnet3_GenericDesc *)ctx.sop_txd -
- tq->tx_ring.base), gdesc->txd.addr, gdesc->dword[2],
- gdesc->dword[3]);
+ tq->tx_ring.base), le64_to_cpu(gdesc->txd.addr),
+ le32_to_cpu(gdesc->dword[2]), le32_to_cpu(gdesc->dword[3]));
spin_unlock_irqrestore(&tq->tx_lock, flags);
- if (tq->shared->txNumDeferred >= tq->shared->txThreshold) {
+ if (le32_to_cpu(tq->shared->txNumDeferred) >=
+ le32_to_cpu(tq->shared->txThreshold)) {
tq->shared->txNumDeferred = 0;
VMXNET3_WRITE_BAR0_REG(adapter, VMXNET3_REG_TXPROD,
tq->tx_ring.next2fill);
@@ -889,9 +1009,8 @@ static netdev_tx_t
vmxnet3_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
- struct vmxnet3_tx_queue *tq = &adapter->tx_queue;
- return vmxnet3_tq_xmit(skb, tq, adapter, netdev);
+ return vmxnet3_tq_xmit(skb, &adapter->tx_queue, adapter, netdev);
}
@@ -902,7 +1021,7 @@ vmxnet3_rx_csum(struct vmxnet3_adapter *adapter,
{
if (!gdesc->rcd.cnc && adapter->rxcsum) {
/* typical case: TCP/UDP over IP and both csums are correct */
- if ((gdesc->dword[3] & VMXNET3_RCD_CSUM_OK) ==
+ if ((le32_to_cpu(gdesc->dword[3]) & VMXNET3_RCD_CSUM_OK) ==
VMXNET3_RCD_CSUM_OK) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
BUG_ON(!(gdesc->rcd.tcp || gdesc->rcd.udp));
@@ -957,8 +1076,12 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
u32 num_rxd = 0;
struct Vmxnet3_RxCompDesc *rcd;
struct vmxnet3_rx_ctx *ctx = &rq->rx_ctx;
-
- rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd;
+#ifdef __BIG_ENDIAN_BITFIELD
+ struct Vmxnet3_RxDesc rxCmdDesc;
+ struct Vmxnet3_RxCompDesc rxComp;
+#endif
+ vmxnet3_getRxComp(rcd, &rq->comp_ring.base[rq->comp_ring.next2proc].rcd,
+ &rxComp);
while (rcd->gen == rq->comp_ring.gen) {
struct vmxnet3_rx_buf_info *rbi;
struct sk_buff *skb;
@@ -976,11 +1099,12 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
idx = rcd->rxdIdx;
ring_idx = rcd->rqID == rq->qid ? 0 : 1;
-
- rxd = &rq->rx_ring[ring_idx].base[idx].rxd;
+ vmxnet3_getRxDesc(rxd, &rq->rx_ring[ring_idx].base[idx].rxd,
+ &rxCmdDesc);
rbi = rq->buf_info[ring_idx] + idx;
- BUG_ON(rxd->addr != rbi->dma_addr || rxd->len != rbi->len);
+ BUG_ON(rxd->addr != rbi->dma_addr ||
+ rxd->len != rbi->len);
if (unlikely(rcd->eop && rcd->err)) {
vmxnet3_rx_error(rq, rcd, ctx, adapter);
@@ -1078,7 +1202,8 @@ rcd_done:
}
vmxnet3_comp_ring_adv_next2proc(&rq->comp_ring);
- rcd = &rq->comp_ring.base[rq->comp_ring.next2proc].rcd;
+ vmxnet3_getRxComp(rcd,
+ &rq->comp_ring.base[rq->comp_ring.next2proc].rcd, &rxComp);
}
return num_rxd;
@@ -1094,7 +1219,11 @@ vmxnet3_rq_cleanup(struct vmxnet3_rx_queue *rq,
for (ring_idx = 0; ring_idx < 2; ring_idx++) {
for (i = 0; i < rq->rx_ring[ring_idx].size; i++) {
- rxd = &rq->rx_ring[ring_idx].base[i].rxd;
+#ifdef __BIG_ENDIAN_BITFIELD
+ struct Vmxnet3_RxDesc rxDesc;
+#endif
+ vmxnet3_getRxDesc(rxd,
+ &rq->rx_ring[ring_idx].base[i].rxd, &rxDesc);
if (rxd->btype == VMXNET3_RXD_BTYPE_HEAD &&
rq->buf_info[ring_idx][i].skb) {
@@ -1346,12 +1475,12 @@ vmxnet3_request_irqs(struct vmxnet3_adapter *adapter)
err = request_irq(adapter->intr.msix_entries[0].vector,
vmxnet3_intr, 0, adapter->netdev->name,
adapter->netdev);
- } else
-#endif
- if (adapter->intr.type == VMXNET3_IT_MSI) {
+ } else if (adapter->intr.type == VMXNET3_IT_MSI) {
err = request_irq(adapter->pdev->irq, vmxnet3_intr, 0,
adapter->netdev->name, adapter->netdev);
- } else {
+ } else
+#endif
+ {
err = request_irq(adapter->pdev->irq, vmxnet3_intr,
IRQF_SHARED, adapter->netdev->name,
adapter->netdev);
@@ -1412,6 +1541,22 @@ vmxnet3_free_irqs(struct vmxnet3_adapter *adapter)
}
+inline void set_flag_le16(__le16 *data, u16 flag)
+{
+ *data = cpu_to_le16(le16_to_cpu(*data) | flag);
+}
+
+inline void set_flag_le64(__le64 *data, u64 flag)
+{
+ *data = cpu_to_le64(le64_to_cpu(*data) | flag);
+}
+
+inline void reset_flag_le64(__le64 *data, u64 flag)
+{
+ *data = cpu_to_le64(le64_to_cpu(*data) & ~flag);
+}
+
+
static void
vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
{
@@ -1427,7 +1572,8 @@ vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
adapter->vlan_grp = grp;
/* update FEATURES to device */
- devRead->misc.uptFeatures |= UPT1_F_RXVLAN;
+ set_flag_le64(&devRead->misc.uptFeatures,
+ UPT1_F_RXVLAN);
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_UPDATE_FEATURE);
/*
@@ -1450,7 +1596,7 @@ vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
struct Vmxnet3_DSDevRead *devRead = &shared->devRead;
adapter->vlan_grp = NULL;
- if (devRead->misc.uptFeatures & UPT1_F_RXVLAN) {
+ if (le64_to_cpu(devRead->misc.uptFeatures) & UPT1_F_RXVLAN) {
int i;
for (i = 0; i < VMXNET3_VFT_SIZE; i++) {
@@ -1463,7 +1609,8 @@ vmxnet3_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
VMXNET3_CMD_UPDATE_VLAN_FILTERS);
/* update FEATURES to device */
- devRead->misc.uptFeatures &= ~UPT1_F_RXVLAN;
+ reset_flag_le64(&devRead->misc.uptFeatures,
+ UPT1_F_RXVLAN);
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_UPDATE_FEATURE);
}
@@ -1565,9 +1712,10 @@ vmxnet3_set_mc(struct net_device *netdev)
new_table = vmxnet3_copy_mc(netdev);
if (new_table) {
new_mode |= VMXNET3_RXM_MCAST;
- rxConf->mfTableLen = netdev->mc_count *
- ETH_ALEN;
- rxConf->mfTablePA = virt_to_phys(new_table);
+ rxConf->mfTableLen = cpu_to_le16(
+ netdev->mc_count * ETH_ALEN);
+ rxConf->mfTablePA = cpu_to_le64(virt_to_phys(
+ new_table));
} else {
printk(KERN_INFO "%s: failed to copy mcast list"
", setting ALL_MULTI\n", netdev->name);
@@ -1582,7 +1730,7 @@ vmxnet3_set_mc(struct net_device *netdev)
}
if (new_mode != rxConf->rxMode) {
- rxConf->rxMode = new_mode;
+ rxConf->rxMode = cpu_to_le32(new_mode);
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_UPDATE_RX_MODE);
}
@@ -1610,63 +1758,69 @@ vmxnet3_setup_driver_shared(struct vmxnet3_adapter *adapter)
memset(shared, 0, sizeof(*shared));
/* driver settings */
- shared->magic = VMXNET3_REV1_MAGIC;
- devRead->misc.driverInfo.version = VMXNET3_DRIVER_VERSION_NUM;
+ shared->magic = cpu_to_le32(VMXNET3_REV1_MAGIC);
+ devRead->misc.driverInfo.version = cpu_to_le32(
+ VMXNET3_DRIVER_VERSION_NUM);
devRead->misc.driverInfo.gos.gosBits = (sizeof(void *) == 4 ?
VMXNET3_GOS_BITS_32 : VMXNET3_GOS_BITS_64);
devRead->misc.driverInfo.gos.gosType = VMXNET3_GOS_TYPE_LINUX;
- devRead->misc.driverInfo.vmxnet3RevSpt = 1;
- devRead->misc.driverInfo.uptVerSpt = 1;
+ *((u32 *)&devRead->misc.driverInfo.gos) = cpu_to_le32(
+ *((u32 *)&devRead->misc.driverInfo.gos));
+ devRead->misc.driverInfo.vmxnet3RevSpt = cpu_to_le32(1);
+ devRead->misc.driverInfo.uptVerSpt = cpu_to_le32(1);
- devRead->misc.ddPA = virt_to_phys(adapter);
- devRead->misc.ddLen = sizeof(struct vmxnet3_adapter);
+ devRead->misc.ddPA = cpu_to_le64(virt_to_phys(adapter));
+ devRead->misc.ddLen = cpu_to_le32(sizeof(struct vmxnet3_adapter));
/* set up feature flags */
if (adapter->rxcsum)
- devRead->misc.uptFeatures |= UPT1_F_RXCSUM;
+ set_flag_le64(&devRead->misc.uptFeatures, UPT1_F_RXCSUM);
if (adapter->lro) {
- devRead->misc.uptFeatures |= UPT1_F_LRO;
- devRead->misc.maxNumRxSG = 1 + MAX_SKB_FRAGS;
+ set_flag_le64(&devRead->misc.uptFeatures, UPT1_F_LRO);
+ devRead->misc.maxNumRxSG = cpu_to_le16(1 + MAX_SKB_FRAGS);
}
- if ((adapter->netdev->features & NETIF_F_HW_VLAN_RX)
- && adapter->vlan_grp) {
- devRead->misc.uptFeatures |= UPT1_F_RXVLAN;
+ if ((adapter->netdev->features & NETIF_F_HW_VLAN_RX) &&
+ adapter->vlan_grp) {
+ set_flag_le64(&devRead->misc.uptFeatures, UPT1_F_RXVLAN);
}
- devRead->misc.mtu = adapter->netdev->mtu;
- devRead->misc.queueDescPA = adapter->queue_desc_pa;
- devRead->misc.queueDescLen = sizeof(struct Vmxnet3_TxQueueDesc) +
- sizeof(struct Vmxnet3_RxQueueDesc);
+ devRead->misc.mtu = cpu_to_le32(adapter->netdev->mtu);
+ devRead->misc.queueDescPA = cpu_to_le64(adapter->queue_desc_pa);
+ devRead->misc.queueDescLen = cpu_to_le32(
+ sizeof(struct Vmxnet3_TxQueueDesc) +
+ sizeof(struct Vmxnet3_RxQueueDesc));
/* tx queue settings */
BUG_ON(adapter->tx_queue.tx_ring.base == NULL);
devRead->misc.numTxQueues = 1;
tqc = &adapter->tqd_start->conf;
- tqc->txRingBasePA = adapter->tx_queue.tx_ring.basePA;
- tqc->dataRingBasePA = adapter->tx_queue.data_ring.basePA;
- tqc->compRingBasePA = adapter->tx_queue.comp_ring.basePA;
- tqc->ddPA = virt_to_phys(adapter->tx_queue.buf_info);
- tqc->txRingSize = adapter->tx_queue.tx_ring.size;
- tqc->dataRingSize = adapter->tx_queue.data_ring.size;
- tqc->compRingSize = adapter->tx_queue.comp_ring.size;
- tqc->ddLen = sizeof(struct vmxnet3_tx_buf_info) *
- tqc->txRingSize;
+ tqc->txRingBasePA = cpu_to_le64(adapter->tx_queue.tx_ring.basePA);
+ tqc->dataRingBasePA = cpu_to_le64(adapter->tx_queue.data_ring.basePA);
+ tqc->compRingBasePA = cpu_to_le64(adapter->tx_queue.comp_ring.basePA);
+ tqc->ddPA = cpu_to_le64(virt_to_phys(
+ adapter->tx_queue.buf_info));
+ tqc->txRingSize = cpu_to_le32(adapter->tx_queue.tx_ring.size);
+ tqc->dataRingSize = cpu_to_le32(adapter->tx_queue.data_ring.size);
+ tqc->compRingSize = cpu_to_le32(adapter->tx_queue.comp_ring.size);
+ tqc->ddLen = cpu_to_le32(sizeof(struct vmxnet3_tx_buf_info) *
+ tqc->txRingSize);
tqc->intrIdx = adapter->tx_queue.comp_ring.intr_idx;
/* rx queue settings */
devRead->misc.numRxQueues = 1;
rqc = &adapter->rqd_start->conf;
- rqc->rxRingBasePA[0] = adapter->rx_queue.rx_ring[0].basePA;
- rqc->rxRingBasePA[1] = adapter->rx_queue.rx_ring[1].basePA;
- rqc->compRingBasePA = adapter->rx_queue.comp_ring.basePA;
- rqc->ddPA = virt_to_phys(adapter->rx_queue.buf_info);
- rqc->rxRingSize[0] = adapter->rx_queue.rx_ring[0].size;
- rqc->rxRingSize[1] = adapter->rx_queue.rx_ring[1].size;
- rqc->compRingSize = adapter->rx_queue.comp_ring.size;
- rqc->ddLen = sizeof(struct vmxnet3_rx_buf_info) *
- (rqc->rxRingSize[0] + rqc->rxRingSize[1]);
+ rqc->rxRingBasePA[0] = cpu_to_le64(adapter->rx_queue.rx_ring[0].basePA);
+ rqc->rxRingBasePA[1] = cpu_to_le64(adapter->rx_queue.rx_ring[1].basePA);
+ rqc->compRingBasePA = cpu_to_le64(adapter->rx_queue.comp_ring.basePA);
+ rqc->ddPA = cpu_to_le64(virt_to_phys(
+ adapter->rx_queue.buf_info));
+ rqc->rxRingSize[0] = cpu_to_le32(adapter->rx_queue.rx_ring[0].size);
+ rqc->rxRingSize[1] = cpu_to_le32(adapter->rx_queue.rx_ring[1].size);
+ rqc->compRingSize = cpu_to_le32(adapter->rx_queue.comp_ring.size);
+ rqc->ddLen = cpu_to_le32(sizeof(struct vmxnet3_rx_buf_info) *
+ (rqc->rxRingSize[0] + rqc->rxRingSize[1]));
rqc->intrIdx = adapter->rx_queue.comp_ring.intr_idx;
/* intr settings */
@@ -1715,11 +1869,10 @@ vmxnet3_activate_dev(struct vmxnet3_adapter *adapter)
vmxnet3_setup_driver_shared(adapter);
- VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL,
- VMXNET3_GET_ADDR_LO(adapter->shared_pa));
- VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH,
- VMXNET3_GET_ADDR_HI(adapter->shared_pa));
-
+ VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAL, VMXNET3_GET_ADDR_LO(
+ adapter->shared_pa));
+ VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_DSAH, VMXNET3_GET_ADDR_HI(
+ adapter->shared_pa));
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_ACTIVATE_DEV);
ret = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD);
@@ -2425,7 +2578,7 @@ vmxnet3_suspend(struct device *device)
memcpy(pmConf->filters[i].pattern, netdev->dev_addr, ETH_ALEN);
pmConf->filters[i].mask[0] = 0x3F; /* LSB ETH_ALEN bits */
- pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER;
+ set_flag_le16(&pmConf->wakeUpEvents, VMXNET3_PM_WAKEUP_FILTER);
i++;
}
@@ -2467,19 +2620,21 @@ vmxnet3_suspend(struct device *device)
pmConf->filters[i].mask[5] = 0x03; /* IPv4 TIP */
in_dev_put(in_dev);
- pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_FILTER;
+ set_flag_le16(&pmConf->wakeUpEvents, VMXNET3_PM_WAKEUP_FILTER);
i++;
}
skip_arp:
if (adapter->wol & WAKE_MAGIC)
- pmConf->wakeUpEvents |= VMXNET3_PM_WAKEUP_MAGIC;
+ set_flag_le16(&pmConf->wakeUpEvents, VMXNET3_PM_WAKEUP_MAGIC);
pmConf->numFilters = i;
- adapter->shared->devRead.pmConfDesc.confVer = 1;
- adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf);
- adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf);
+ adapter->shared->devRead.pmConfDesc.confVer = cpu_to_le32(1);
+ adapter->shared->devRead.pmConfDesc.confLen = cpu_to_le32(sizeof(
+ *pmConf));
+ adapter->shared->devRead.pmConfDesc.confPA = cpu_to_le64(virt_to_phys(
+ pmConf));
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_UPDATE_PMCFG);
@@ -2510,9 +2665,11 @@ vmxnet3_resume(struct device *device)
pmConf = adapter->pm_conf;
memset(pmConf, 0, sizeof(*pmConf));
- adapter->shared->devRead.pmConfDesc.confVer = 1;
- adapter->shared->devRead.pmConfDesc.confLen = sizeof(*pmConf);
- adapter->shared->devRead.pmConfDesc.confPA = virt_to_phys(pmConf);
+ adapter->shared->devRead.pmConfDesc.confVer = cpu_to_le32(1);
+ adapter->shared->devRead.pmConfDesc.confLen = cpu_to_le32(sizeof(
+ *pmConf));
+ adapter->shared->devRead.pmConfDesc.confPA = cpu_to_le32(virt_to_phys(
+ pmConf));
netif_device_attach(netdev);
pci_set_power_state(pdev, PCI_D0);
diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c
index c2c15e4cafc..3935c4493fb 100644
--- a/drivers/net/vmxnet3/vmxnet3_ethtool.c
+++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c
@@ -50,11 +50,13 @@ vmxnet3_set_rx_csum(struct net_device *netdev, u32 val)
adapter->rxcsum = val;
if (netif_running(netdev)) {
if (val)
- adapter->shared->devRead.misc.uptFeatures |=
- UPT1_F_RXCSUM;
+ set_flag_le64(
+ &adapter->shared->devRead.misc.uptFeatures,
+ UPT1_F_RXCSUM);
else
- adapter->shared->devRead.misc.uptFeatures &=
- ~UPT1_F_RXCSUM;
+ reset_flag_le64(
+ &adapter->shared->devRead.misc.uptFeatures,
+ UPT1_F_RXCSUM);
VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD,
VMXNET3_CMD_UPDATE_FEATURE);
diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h
index 3c0d70d5811..34f392f46fb 100644
--- a/drivers/net/vmxnet3/vmxnet3_int.h
+++ b/drivers/net/vmxnet3/vmxnet3_int.h
@@ -27,16 +27,11 @@
#ifndef _VMXNET3_INT_H
#define _VMXNET3_INT_H
-#include <linux/types.h>
#include <linux/ethtool.h>
#include <linux/delay.h>
-#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
-#include <linux/ethtool.h>
#include <linux/compiler.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/ioport.h>
@@ -335,14 +330,14 @@ struct vmxnet3_adapter {
};
#define VMXNET3_WRITE_BAR0_REG(adapter, reg, val) \
- writel((val), (adapter)->hw_addr0 + (reg))
+ writel(cpu_to_le32(val), (adapter)->hw_addr0 + (reg))
#define VMXNET3_READ_BAR0_REG(adapter, reg) \
- readl((adapter)->hw_addr0 + (reg))
+ le32_to_cpu(readl((adapter)->hw_addr0 + (reg)))
#define VMXNET3_WRITE_BAR1_REG(adapter, reg, val) \
- writel((val), (adapter)->hw_addr1 + (reg))
+ writel(cpu_to_le32(val), (adapter)->hw_addr1 + (reg))
#define VMXNET3_READ_BAR1_REG(adapter, reg) \
- readl((adapter)->hw_addr1 + (reg))
+ le32_to_cpu(readl((adapter)->hw_addr1 + (reg)))
#define VMXNET3_WAKE_QUEUE_THRESHOLD(tq) (5)
#define VMXNET3_RX_ALLOC_THRESHOLD(rq, ring_idx, adapter) \
@@ -358,6 +353,10 @@ struct vmxnet3_adapter {
#define VMXNET3_MAX_ETH_HDR_SIZE 22
#define VMXNET3_MAX_SKB_BUF_SIZE (3*1024)
+void set_flag_le16(__le16 *data, u16 flag);
+void set_flag_le64(__le64 *data, u64 flag);
+void reset_flag_le64(__le64 *data, u64 flag);
+
int
vmxnet3_quiesce_dev(struct vmxnet3_adapter *adapter);
diff --git a/drivers/net/vxge/vxge-config.c b/drivers/net/vxge/vxge-config.c
index 9e94c4b0fb1..32a75fa935e 100644
--- a/drivers/net/vxge/vxge-config.c
+++ b/drivers/net/vxge/vxge-config.c
@@ -356,10 +356,8 @@ __vxge_hw_device_access_rights_get(u32 host_type, u32 func_id)
switch (host_type) {
case VXGE_HW_NO_MR_NO_SR_NORMAL_FUNCTION:
- if (func_id == 0) {
- access_rights |= VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM |
- VXGE_HW_DEVICE_ACCESS_RIGHT_SRPCIM;
- }
+ access_rights |= VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM |
+ VXGE_HW_DEVICE_ACCESS_RIGHT_SRPCIM;
break;
case VXGE_HW_MR_NO_SR_VH0_BASE_FUNCTION:
access_rights |= VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM |
@@ -382,6 +380,22 @@ __vxge_hw_device_access_rights_get(u32 host_type, u32 func_id)
return access_rights;
}
/*
+ * __vxge_hw_device_is_privilaged
+ * This routine checks if the device function is privilaged or not
+ */
+
+enum vxge_hw_status
+__vxge_hw_device_is_privilaged(u32 host_type, u32 func_id)
+{
+ if (__vxge_hw_device_access_rights_get(host_type,
+ func_id) &
+ VXGE_HW_DEVICE_ACCESS_RIGHT_MRPCIM)
+ return VXGE_HW_OK;
+ else
+ return VXGE_HW_ERR_PRIVILAGED_OPEARATION;
+}
+
+/*
* __vxge_hw_device_host_info_get
* This routine returns the host type assignments
*/
@@ -446,220 +460,6 @@ __vxge_hw_verify_pci_e_info(struct __vxge_hw_device *hldev)
return VXGE_HW_OK;
}
-enum vxge_hw_status
-__vxge_hw_device_is_privilaged(struct __vxge_hw_device *hldev)
-{
- if ((hldev->host_type == VXGE_HW_NO_MR_NO_SR_NORMAL_FUNCTION ||
- hldev->host_type == VXGE_HW_MR_NO_SR_VH0_BASE_FUNCTION ||
- hldev->host_type == VXGE_HW_NO_MR_SR_VH0_FUNCTION0) &&
- (hldev->func_id == 0))
- return VXGE_HW_OK;
- else
- return VXGE_HW_ERR_PRIVILAGED_OPEARATION;
-}
-
-/*
- * vxge_hw_wrr_rebalance - Rebalance the RX_WRR and KDFC_WRR calandars.
- * Rebalance the RX_WRR and KDFC_WRR calandars.
- */
-static enum
-vxge_hw_status vxge_hw_wrr_rebalance(struct __vxge_hw_device *hldev)
-{
- u64 val64;
- u32 wrr_states[VXGE_HW_WEIGHTED_RR_SERVICE_STATES];
- u32 i, j, how_often = 1;
- enum vxge_hw_status status = VXGE_HW_OK;
-
- status = __vxge_hw_device_is_privilaged(hldev);
- if (status != VXGE_HW_OK)
- goto exit;
-
- /* Reset the priorities assigned to the WRR arbitration
- phases for the receive traffic */
- for (i = 0; i < VXGE_HW_WRR_RING_COUNT; i++)
- writeq(0, ((&hldev->mrpcim_reg->rx_w_round_robin_0) + i));
-
- /* Reset the transmit FIFO servicing calendar for FIFOs */
- for (i = 0; i < VXGE_HW_WRR_FIFO_COUNT; i++) {
- writeq(0, ((&hldev->mrpcim_reg->kdfc_w_round_robin_0) + i));
- writeq(0, ((&hldev->mrpcim_reg->kdfc_w_round_robin_20) + i));
- }
-
- /* Assign WRR priority 0 for all FIFOs */
- for (i = 1; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
- writeq(VXGE_HW_KDFC_FIFO_0_CTRL_WRR_NUMBER(0),
- ((&hldev->mrpcim_reg->kdfc_fifo_0_ctrl) + i));
-
- writeq(VXGE_HW_KDFC_FIFO_17_CTRL_WRR_NUMBER(0),
- ((&hldev->mrpcim_reg->kdfc_fifo_17_ctrl) + i));
- }
-
- /* Reset to service non-offload doorbells */
- writeq(0, &hldev->mrpcim_reg->kdfc_entry_type_sel_0);
- writeq(0, &hldev->mrpcim_reg->kdfc_entry_type_sel_1);
-
- /* Set priority 0 to all receive queues */
- writeq(0, &hldev->mrpcim_reg->rx_queue_priority_0);
- writeq(0, &hldev->mrpcim_reg->rx_queue_priority_1);
- writeq(0, &hldev->mrpcim_reg->rx_queue_priority_2);
-
- /* Initialize all the slots as unused */
- for (i = 0; i < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; i++)
- wrr_states[i] = -1;
-
- /* Prepare the Fifo service states */
- for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
-
- if (!hldev->config.vp_config[i].min_bandwidth)
- continue;
-
- how_often = VXGE_HW_VPATH_BANDWIDTH_MAX /
- hldev->config.vp_config[i].min_bandwidth;
- if (how_often) {
-
- for (j = 0; j < VXGE_HW_WRR_FIFO_SERVICE_STATES;) {
- if (wrr_states[j] == -1) {
- wrr_states[j] = i;
- /* Make sure each fifo is serviced
- * atleast once */
- if (i == j)
- j += VXGE_HW_MAX_VIRTUAL_PATHS;
- else
- j += how_often;
- } else
- j++;
- }
- }
- }
-
- /* Fill the unused slots with 0 */
- for (j = 0; j < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; j++) {
- if (wrr_states[j] == -1)
- wrr_states[j] = 0;
- }
-
- /* Assign WRR priority number for FIFOs */
- for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
- writeq(VXGE_HW_KDFC_FIFO_0_CTRL_WRR_NUMBER(i),
- ((&hldev->mrpcim_reg->kdfc_fifo_0_ctrl) + i));
-
- writeq(VXGE_HW_KDFC_FIFO_17_CTRL_WRR_NUMBER(i),
- ((&hldev->mrpcim_reg->kdfc_fifo_17_ctrl) + i));
- }
-
- /* Modify the servicing algorithm applied to the 3 types of doorbells.
- i.e, none-offload, message and offload */
- writeq(VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_0(0) |
- VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_1(0) |
- VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_2(0) |
- VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_3(0) |
- VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_4(1) |
- VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_5(0) |
- VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_6(0) |
- VXGE_HW_KDFC_ENTRY_TYPE_SEL_0_NUMBER_7(0),
- &hldev->mrpcim_reg->kdfc_entry_type_sel_0);
-
- writeq(VXGE_HW_KDFC_ENTRY_TYPE_SEL_1_NUMBER_8(1),
- &hldev->mrpcim_reg->kdfc_entry_type_sel_1);
-
- for (i = 0, j = 0; i < VXGE_HW_WRR_FIFO_COUNT; i++) {
-
- val64 = VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_0(wrr_states[j++]);
- val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_1(wrr_states[j++]);
- val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_2(wrr_states[j++]);
- val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_3(wrr_states[j++]);
- val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_4(wrr_states[j++]);
- val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_5(wrr_states[j++]);
- val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_6(wrr_states[j++]);
- val64 |= VXGE_HW_KDFC_W_ROUND_ROBIN_0_NUMBER_7(wrr_states[j++]);
-
- writeq(val64, (&hldev->mrpcim_reg->kdfc_w_round_robin_0 + i));
- writeq(val64, (&hldev->mrpcim_reg->kdfc_w_round_robin_20 + i));
- }
-
- /* Set up the priorities assigned to receive queues */
- writeq(VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_0(0) |
- VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_1(1) |
- VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_2(2) |
- VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_3(3) |
- VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_4(4) |
- VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_5(5) |
- VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_6(6) |
- VXGE_HW_RX_QUEUE_PRIORITY_0_RX_Q_NUMBER_7(7),
- &hldev->mrpcim_reg->rx_queue_priority_0);
-
- writeq(VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_8(8) |
- VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_9(9) |
- VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_10(10) |
- VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_11(11) |
- VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_12(12) |
- VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_13(13) |
- VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_14(14) |
- VXGE_HW_RX_QUEUE_PRIORITY_1_RX_Q_NUMBER_15(15),
- &hldev->mrpcim_reg->rx_queue_priority_1);
-
- writeq(VXGE_HW_RX_QUEUE_PRIORITY_2_RX_Q_NUMBER_16(16),
- &hldev->mrpcim_reg->rx_queue_priority_2);
-
- /* Initialize all the slots as unused */
- for (i = 0; i < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; i++)
- wrr_states[i] = -1;
-
- /* Prepare the Ring service states */
- for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
-
- if (!hldev->config.vp_config[i].min_bandwidth)
- continue;
-
- how_often = VXGE_HW_VPATH_BANDWIDTH_MAX /
- hldev->config.vp_config[i].min_bandwidth;
-
- if (how_often) {
- for (j = 0; j < VXGE_HW_WRR_RING_SERVICE_STATES;) {
- if (wrr_states[j] == -1) {
- wrr_states[j] = i;
- /* Make sure each ring is
- * serviced atleast once */
- if (i == j)
- j += VXGE_HW_MAX_VIRTUAL_PATHS;
- else
- j += how_often;
- } else
- j++;
- }
- }
- }
-
- /* Fill the unused slots with 0 */
- for (j = 0; j < VXGE_HW_WEIGHTED_RR_SERVICE_STATES; j++) {
- if (wrr_states[j] == -1)
- wrr_states[j] = 0;
- }
-
- for (i = 0, j = 0; i < VXGE_HW_WRR_RING_COUNT; i++) {
- val64 = VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_0(
- wrr_states[j++]);
- val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_1(
- wrr_states[j++]);
- val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_2(
- wrr_states[j++]);
- val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_3(
- wrr_states[j++]);
- val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_4(
- wrr_states[j++]);
- val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_5(
- wrr_states[j++]);
- val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_6(
- wrr_states[j++]);
- val64 |= VXGE_HW_RX_W_ROUND_ROBIN_0_RX_W_PRIORITY_SS_7(
- wrr_states[j++]);
-
- writeq(val64, ((&hldev->mrpcim_reg->rx_w_round_robin_0) + i));
- }
-exit:
- return status;
-}
-
/*
* __vxge_hw_device_initialize
* Initialize Titan-V hardware.
@@ -668,14 +468,14 @@ enum vxge_hw_status __vxge_hw_device_initialize(struct __vxge_hw_device *hldev)
{
enum vxge_hw_status status = VXGE_HW_OK;
- if (VXGE_HW_OK == __vxge_hw_device_is_privilaged(hldev)) {
+ if (VXGE_HW_OK == __vxge_hw_device_is_privilaged(hldev->host_type,
+ hldev->func_id)) {
/* Validate the pci-e link width and speed */
status = __vxge_hw_verify_pci_e_info(hldev);
if (status != VXGE_HW_OK)
goto exit;
}
- vxge_hw_wrr_rebalance(hldev);
exit:
return status;
}
@@ -953,7 +753,8 @@ vxge_hw_mrpcim_stats_access(struct __vxge_hw_device *hldev,
u64 val64;
enum vxge_hw_status status = VXGE_HW_OK;
- status = __vxge_hw_device_is_privilaged(hldev);
+ status = __vxge_hw_device_is_privilaged(hldev->host_type,
+ hldev->func_id);
if (status != VXGE_HW_OK)
goto exit;
@@ -990,7 +791,8 @@ vxge_hw_device_xmac_aggr_stats_get(struct __vxge_hw_device *hldev, u32 port,
val64 = (u64 *)aggr_stats;
- status = __vxge_hw_device_is_privilaged(hldev);
+ status = __vxge_hw_device_is_privilaged(hldev->host_type,
+ hldev->func_id);
if (status != VXGE_HW_OK)
goto exit;
@@ -1023,7 +825,8 @@ vxge_hw_device_xmac_port_stats_get(struct __vxge_hw_device *hldev, u32 port,
u32 offset = 0x0;
val64 = (u64 *) port_stats;
- status = __vxge_hw_device_is_privilaged(hldev);
+ status = __vxge_hw_device_is_privilaged(hldev->host_type,
+ hldev->func_id);
if (status != VXGE_HW_OK)
goto exit;
@@ -1221,7 +1024,8 @@ enum vxge_hw_status vxge_hw_device_setpause_data(struct __vxge_hw_device *hldev,
goto exit;
}
- status = __vxge_hw_device_is_privilaged(hldev);
+ status = __vxge_hw_device_is_privilaged(hldev->host_type,
+ hldev->func_id);
if (status != VXGE_HW_OK)
goto exit;
@@ -2353,6 +2157,28 @@ exit:
}
/*
+ * vxge_hw_vpath_strip_fcs_check - Check for FCS strip.
+ */
+enum vxge_hw_status
+vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask)
+{
+ struct vxge_hw_vpmgmt_reg __iomem *vpmgmt_reg;
+ enum vxge_hw_status status = VXGE_HW_OK;
+ int i = 0, j = 0;
+
+ for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
+ if (!((vpath_mask) & vxge_mBIT(i)))
+ continue;
+ vpmgmt_reg = hldev->vpmgmt_reg[i];
+ for (j = 0; j < VXGE_HW_MAC_MAX_MAC_PORT_ID; j++) {
+ if (readq(&vpmgmt_reg->rxmac_cfg0_port_vpmgmt_clone[j])
+ & VXGE_HW_RXMAC_CFG0_PORT_VPMGMT_CLONE_STRIP_FCS)
+ return VXGE_HW_FAIL;
+ }
+ }
+ return status;
+}
+/*
* vxge_hw_mgmt_reg_Write - Write Titan register.
*/
enum vxge_hw_status
@@ -4056,6 +3882,30 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id)
return status;
}
+void
+vxge_hw_vpath_tti_ci_set(struct __vxge_hw_device *hldev, u32 vp_id)
+{
+ struct __vxge_hw_virtualpath *vpath;
+ struct vxge_hw_vpath_reg __iomem *vp_reg;
+ struct vxge_hw_vp_config *config;
+ u64 val64;
+
+ vpath = &hldev->virtual_paths[vp_id];
+ vp_reg = vpath->vp_reg;
+ config = vpath->vp_config;
+
+ if (config->fifo.enable == VXGE_HW_FIFO_ENABLE) {
+ val64 = readq(&vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]);
+
+ if (config->tti.timer_ci_en != VXGE_HW_TIM_TIMER_CI_ENABLE) {
+ config->tti.timer_ci_en = VXGE_HW_TIM_TIMER_CI_ENABLE;
+ val64 |= VXGE_HW_TIM_CFG1_INT_NUM_TIMER_CI;
+ writeq(val64,
+ &vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]);
+ }
+ }
+ return;
+}
/*
* __vxge_hw_vpath_initialize
* This routine is the final phase of init which initializes the
@@ -4098,8 +3948,6 @@ __vxge_hw_vpath_initialize(struct __vxge_hw_device *hldev, u32 vp_id)
if (status != VXGE_HW_OK)
goto exit;
- writeq(0, &vp_reg->gendma_int);
-
val64 = readq(&vp_reg->rtdma_rd_optimization_ctrl);
/* Get MRRS value from device control */
diff --git a/drivers/net/vxge/vxge-config.h b/drivers/net/vxge/vxge-config.h
index 3e94f0ce090..e7877df092f 100644
--- a/drivers/net/vxge/vxge-config.h
+++ b/drivers/net/vxge/vxge-config.h
@@ -2201,6 +2201,8 @@ __vxge_hw_vpath_func_id_get(
enum vxge_hw_status
__vxge_hw_vpath_reset_check(struct __vxge_hw_virtualpath *vpath);
+enum vxge_hw_status
+vxge_hw_vpath_strip_fcs_check(struct __vxge_hw_device *hldev, u64 vpath_mask);
/**
* vxge_debug
* @level: level of debug verbosity.
diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c
index 068d7a9d3e3..f1c4b2a1e86 100644
--- a/drivers/net/vxge/vxge-main.c
+++ b/drivers/net/vxge/vxge-main.c
@@ -2435,7 +2435,6 @@ static int vxge_add_isr(struct vxgedev *vdev)
int ret = 0;
#ifdef CONFIG_PCI_MSI
int vp_idx = 0, intr_idx = 0, intr_cnt = 0, msix_idx = 0, irq_req = 0;
- u64 function_mode = vdev->config.device_hw_info.function_mode;
int pci_fun = PCI_FUNC(vdev->pdev->devfn);
if (vdev->config.intr_type == MSI_X)
@@ -2444,20 +2443,9 @@ static int vxge_add_isr(struct vxgedev *vdev)
if (ret) {
vxge_debug_init(VXGE_ERR,
"%s: Enabling MSI-X Failed", VXGE_DRIVER_NAME);
- if ((function_mode == VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) &&
- test_and_set_bit(__VXGE_STATE_CARD_UP,
- &driver_config->inta_dev_open))
- return VXGE_HW_FAIL;
- else {
- vxge_debug_init(VXGE_ERR,
- "%s: Defaulting to INTA", VXGE_DRIVER_NAME);
- vdev->config.intr_type = INTA;
- vxge_hw_device_set_intr_type(vdev->devh,
- VXGE_HW_INTR_MODE_IRQLINE);
- vxge_close_vpaths(vdev, 1);
- vdev->no_of_vpath = 1;
- vdev->stats.vpaths_open = 1;
- }
+ vxge_debug_init(VXGE_ERR,
+ "%s: Defaulting to INTA", VXGE_DRIVER_NAME);
+ vdev->config.intr_type = INTA;
}
if (vdev->config.intr_type == MSI_X) {
@@ -2505,24 +2493,11 @@ static int vxge_add_isr(struct vxgedev *vdev)
"%s: MSIX - %d Registration failed",
vdev->ndev->name, intr_cnt);
vxge_rem_msix_isr(vdev);
- if ((function_mode ==
- VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) &&
- test_and_set_bit(__VXGE_STATE_CARD_UP,
- &driver_config->inta_dev_open))
- return VXGE_HW_FAIL;
- else {
- vxge_hw_device_set_intr_type(
- vdev->devh,
- VXGE_HW_INTR_MODE_IRQLINE);
- vdev->config.intr_type = INTA;
- vxge_debug_init(VXGE_ERR,
- "%s: Defaulting to INTA"
- , vdev->ndev->name);
- vxge_close_vpaths(vdev, 1);
- vdev->no_of_vpath = 1;
- vdev->stats.vpaths_open = 1;
+ vdev->config.intr_type = INTA;
+ vxge_debug_init(VXGE_ERR,
+ "%s: Defaulting to INTA"
+ , vdev->ndev->name);
goto INTA_MODE;
- }
}
if (irq_req) {
@@ -2535,9 +2510,9 @@ static int vxge_add_isr(struct vxgedev *vdev)
}
/* Point to next vpath handler */
- if (((intr_idx + 1) % VXGE_HW_VPATH_MSIX_ACTIVE == 0)
- && (vp_idx < (vdev->no_of_vpath - 1)))
- vp_idx++;
+ if (((intr_idx + 1) % VXGE_HW_VPATH_MSIX_ACTIVE == 0) &&
+ (vp_idx < (vdev->no_of_vpath - 1)))
+ vp_idx++;
}
intr_cnt = vdev->max_vpath_supported * 2;
@@ -2555,23 +2530,11 @@ static int vxge_add_isr(struct vxgedev *vdev)
"%s: MSIX - %d Registration failed",
vdev->ndev->name, intr_cnt);
vxge_rem_msix_isr(vdev);
- if ((function_mode ==
- VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) &&
- test_and_set_bit(__VXGE_STATE_CARD_UP,
- &driver_config->inta_dev_open))
- return VXGE_HW_FAIL;
- else {
- vxge_hw_device_set_intr_type(vdev->devh,
- VXGE_HW_INTR_MODE_IRQLINE);
- vdev->config.intr_type = INTA;
- vxge_debug_init(VXGE_ERR,
- "%s: Defaulting to INTA",
- vdev->ndev->name);
- vxge_close_vpaths(vdev, 1);
- vdev->no_of_vpath = 1;
- vdev->stats.vpaths_open = 1;
+ vdev->config.intr_type = INTA;
+ vxge_debug_init(VXGE_ERR,
+ "%s: Defaulting to INTA",
+ vdev->ndev->name);
goto INTA_MODE;
- }
}
vxge_hw_vpath_msix_unmask(vdev->vpaths[vp_idx].handle,
@@ -2584,6 +2547,10 @@ INTA_MODE:
snprintf(vdev->desc[0], VXGE_INTR_STRLEN, "%s:vxge", vdev->ndev->name);
if (vdev->config.intr_type == INTA) {
+ vxge_hw_device_set_intr_type(vdev->devh,
+ VXGE_HW_INTR_MODE_IRQLINE);
+ vxge_hw_vpath_tti_ci_set(vdev->devh,
+ vdev->vpaths[0].device_id);
ret = request_irq((int) vdev->pdev->irq,
vxge_isr_napi,
IRQF_SHARED, vdev->desc[0], vdev);
@@ -2688,13 +2655,6 @@ vxge_open(struct net_device *dev)
* initialized */
netif_carrier_off(dev);
- /* Check for another device already opn with INTA */
- if ((function_mode == VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION) &&
- test_bit(__VXGE_STATE_CARD_UP, &driver_config->inta_dev_open)) {
- ret = -EPERM;
- goto out0;
- }
-
/* Open VPATHs */
status = vxge_open_vpaths(vdev);
if (status != VXGE_HW_OK) {
@@ -2983,7 +2943,6 @@ int do_vxge_close(struct net_device *dev, int do_io)
vxge_debug_entryexit(VXGE_TRACE,
"%s: %s:%d Exiting...", dev->name, __func__, __LINE__);
- clear_bit(__VXGE_STATE_CARD_UP, &driver_config->inta_dev_open);
clear_bit(__VXGE_STATE_RESET_CARD, &vdev->state);
return 0;
@@ -3653,11 +3612,12 @@ static int __devinit vxge_config_vpaths(
device_config->vp_config[i].fifo.enable =
VXGE_HW_FIFO_ENABLE;
device_config->vp_config[i].fifo.max_frags =
- MAX_SKB_FRAGS;
+ MAX_SKB_FRAGS + 1;
device_config->vp_config[i].fifo.memblock_size =
VXGE_HW_MIN_FIFO_MEMBLOCK_SIZE;
- txdl_size = MAX_SKB_FRAGS * sizeof(struct vxge_hw_fifo_txd);
+ txdl_size = device_config->vp_config[i].fifo.max_frags *
+ sizeof(struct vxge_hw_fifo_txd);
txdl_per_memblock = VXGE_HW_MIN_FIFO_MEMBLOCK_SIZE / txdl_size;
device_config->vp_config[i].fifo.fifo_blocks =
@@ -4088,9 +4048,10 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
driver_config->config_dev_cnt = 0;
driver_config->total_dev_cnt = 0;
driver_config->g_no_cpus = 0;
- driver_config->vpath_per_dev = max_config_vpath;
}
+ driver_config->vpath_per_dev = max_config_vpath;
+
driver_config->total_dev_cnt++;
if (++driver_config->config_dev_cnt > max_config_dev) {
ret = 0;
@@ -4243,6 +4204,15 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
goto _exit3;
}
+ /* if FCS stripping is not disabled in MAC fail driver load */
+ if (vxge_hw_vpath_strip_fcs_check(hldev, vpath_mask) != VXGE_HW_OK) {
+ vxge_debug_init(VXGE_ERR,
+ "%s: FCS stripping is not disabled in MAC"
+ " failing driver load", VXGE_DRIVER_NAME);
+ ret = -EINVAL;
+ goto _exit4;
+ }
+
vxge_hw_device_debug_set(hldev, VXGE_ERR, VXGE_COMPONENT_LL);
/* set private device info */
@@ -4387,6 +4357,27 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
}
kfree(device_config);
+
+ /*
+ * INTA is shared in multi-function mode. This is unlike the INTA
+ * implementation in MR mode, where each VH has its own INTA message.
+ * - INTA is masked (disabled) as long as at least one function sets
+ * its TITAN_MASK_ALL_INT.ALARM bit.
+ * - INTA is unmasked (enabled) when all enabled functions have cleared
+ * their own TITAN_MASK_ALL_INT.ALARM bit.
+ * The TITAN_MASK_ALL_INT ALARM & TRAFFIC bits are cleared on power up.
+ * Though this driver leaves the top level interrupts unmasked while
+ * leaving the required module interrupt bits masked on exit, there
+ * could be a rougue driver around that does not follow this procedure
+ * resulting in a failure to generate interrupts. The following code is
+ * present to prevent such a failure.
+ */
+
+ if (ll_config.device_hw_info.function_mode ==
+ VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION)
+ if (vdev->config.intr_type == INTA)
+ vxge_hw_device_unmask_all(hldev);
+
vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d Exiting...",
vdev->ndev->name, __func__, __LINE__);
diff --git a/drivers/net/vxge/vxge-main.h b/drivers/net/vxge/vxge-main.h
index 9c36b3a9a63..7c83ba4be9d 100644
--- a/drivers/net/vxge/vxge-main.h
+++ b/drivers/net/vxge/vxge-main.h
@@ -112,7 +112,6 @@ enum vxge_mac_addr_state {
struct vxge_drv_config {
int config_dev_cnt;
int total_dev_cnt;
- unsigned long inta_dev_open;
int g_no_cpus;
unsigned int vpath_per_dev;
};
diff --git a/drivers/net/vxge/vxge-reg.h b/drivers/net/vxge/vxge-reg.h
index 9a3b823e08d..9a0cf8eaa32 100644
--- a/drivers/net/vxge/vxge-reg.h
+++ b/drivers/net/vxge/vxge-reg.h
@@ -4326,10 +4326,6 @@ struct vxge_hw_vpath_reg {
/*0x011e0*/ u64 umq_bwr_init_byte;
#define VXGE_HW_UMQ_BWR_INIT_BYTE_COUNT(val) vxge_vBIT(val, 0, 32)
/*0x011e8*/ u64 gendma_int;
-#define VXGE_HW_GENDMA_INT_IMMED_ENABLE vxge_mBIT(6)
-#define VXGE_HW_GENDMA_INT_EVENT_ENABLE vxge_mBIT(7)
-#define VXGE_HW_GENDMA_INT_NUMBER(val) vxge_vBIT(val, 9, 7)
-#define VXGE_HW_GENDMA_INT_BITMAP(val) vxge_vBIT(val, 16, 16)
/*0x011f0*/ u64 umqdmq_ir_init_notify;
#define VXGE_HW_UMQDMQ_IR_INIT_NOTIFY_PULSE vxge_mBIT(3)
/*0x011f8*/ u64 dmq_init_notify;
diff --git a/drivers/net/vxge/vxge-traffic.c b/drivers/net/vxge/vxge-traffic.c
index fe3ae518c69..2c012f4ce46 100644
--- a/drivers/net/vxge/vxge-traffic.c
+++ b/drivers/net/vxge/vxge-traffic.c
@@ -295,6 +295,8 @@ void vxge_hw_device_intr_enable(struct __vxge_hw_device *hldev)
u64 val64;
u32 val32;
+ vxge_hw_device_mask_all(hldev);
+
for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++) {
if (!(hldev->vpaths_deployed & vxge_mBIT(i)))
@@ -1232,7 +1234,7 @@ void vxge_hw_fifo_txdl_post(struct __vxge_hw_fifo *fifo, void *txdlh)
vxge_hw_channel_dtr_post(&fifo->channel, txdlh);
__vxge_hw_non_offload_db_post(fifo,
- (u64)(size_t)txdl_priv->dma_addr,
+ (u64)txdl_priv->dma_addr,
txdl_priv->frags - 1,
fifo->no_snoop_bits);
@@ -1961,14 +1963,14 @@ enum vxge_hw_status __vxge_hw_vpath_alarm_process(
val64 = readq(&vp_reg->asic_ntwk_vp_err_reg);
if (((val64 &
- VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT) &&
- (!(val64 &
+ VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT) &&
+ (!(val64 &
VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK))) ||
((val64 &
- VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR)
- && (!(val64 &
+ VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR) &&
+ (!(val64 &
VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR)
- ))) {
+ ))) {
sw_stats->error_stats.network_sustained_fault++;
writeq(
@@ -1981,14 +1983,14 @@ enum vxge_hw_status __vxge_hw_vpath_alarm_process(
}
if (((val64 &
- VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK) &&
- (!(val64 &
+ VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK) &&
+ (!(val64 &
VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT))) ||
((val64 &
- VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR)
- && (!(val64 &
+ VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_OK_OCCURR) &&
+ (!(val64 &
VXGE_HW_ASIC_NW_VP_ERR_REG_XMACJ_STN_FLT_OCCURR)
- ))) {
+ ))) {
sw_stats->error_stats.network_sustained_ok++;
diff --git a/drivers/net/vxge/vxge-traffic.h b/drivers/net/vxge/vxge-traffic.h
index 461742b4442..861c853e3e8 100644
--- a/drivers/net/vxge/vxge-traffic.h
+++ b/drivers/net/vxge/vxge-traffic.h
@@ -2389,6 +2389,8 @@ vxge_hw_channel_dtr_free(struct __vxge_hw_channel *channel, void *dtrh);
int
vxge_hw_channel_dtr_count(struct __vxge_hw_channel *channel);
+void
+vxge_hw_vpath_tti_ci_set(struct __vxge_hw_device *hldev, u32 vp_id);
/* ========================== PRIVATE API ================================= */
diff --git a/drivers/net/vxge/vxge-version.h b/drivers/net/vxge/vxge-version.h
index 8fbce755203..77c2a754b7b 100644
--- a/drivers/net/vxge/vxge-version.h
+++ b/drivers/net/vxge/vxge-version.h
@@ -17,7 +17,7 @@
#define VXGE_VERSION_MAJOR "2"
#define VXGE_VERSION_MINOR "0"
-#define VXGE_VERSION_FIX "5"
-#define VXGE_VERSION_BUILD "18053"
+#define VXGE_VERSION_FIX "6"
+#define VXGE_VERSION_BUILD "18937"
#define VXGE_VERSION_FOR "k"
#endif
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index 8e25ca7080c..b36bf96eb50 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -297,8 +297,8 @@ static ssize_t cosa_write(struct file *file,
static unsigned int cosa_poll(struct file *file, poll_table *poll);
static int cosa_open(struct inode *inode, struct file *file);
static int cosa_release(struct inode *inode, struct file *file);
-static int cosa_chardev_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
+static long cosa_chardev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
#ifdef COSA_FASYNC_WORKING
static int cosa_fasync(struct inode *inode, struct file *file, int on);
#endif
@@ -309,7 +309,7 @@ static const struct file_operations cosa_fops = {
.read = cosa_read,
.write = cosa_write,
.poll = cosa_poll,
- .ioctl = cosa_chardev_ioctl,
+ .unlocked_ioctl = cosa_chardev_ioctl,
.open = cosa_open,
.release = cosa_release,
#ifdef COSA_FASYNC_WORKING
@@ -1205,12 +1205,18 @@ static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return hdlc_ioctl(dev, ifr, cmd);
}
-static int cosa_chardev_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long cosa_chardev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct channel_data *channel = file->private_data;
- struct cosa_data *cosa = channel->cosa;
- return cosa_ioctl_common(cosa, channel, cmd, arg);
+ struct cosa_data *cosa;
+ long ret;
+
+ lock_kernel();
+ cosa = channel->cosa;
+ ret = cosa_ioctl_common(cosa, channel, cmd, arg);
+ unlock_kernel();
+ return ret;
}
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index 15d353f268b..421d0715310 100644
--- a/drivers/net/wan/dlci.c
+++ b/drivers/net/wan/dlci.c
@@ -77,7 +77,7 @@ static int dlci_header(struct sk_buff *skb, struct net_device *dev,
dlp = netdev_priv(dev);
hdr.control = FRAD_I_UI;
- switch(type)
+ switch (type)
{
case ETH_P_IP:
hdr.IP_NLPID = FRAD_P_IP;
@@ -130,7 +130,7 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev)
dev->stats.rx_errors++;
}
else
- switch(hdr->IP_NLPID)
+ switch (hdr->IP_NLPID)
{
case FRAD_P_PADDING:
if (hdr->NLPID != FRAD_P_SNAP)
@@ -208,7 +208,7 @@ static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, in
if (!get)
{
- if(copy_from_user(&config, conf, sizeof(struct dlci_conf)))
+ if (copy_from_user(&config, conf, sizeof(struct dlci_conf)))
return -EFAULT;
if (config.flags & ~DLCI_VALID_FLAGS)
return(-EINVAL);
@@ -222,7 +222,7 @@ static int dlci_config(struct net_device *dev, struct dlci_conf __user *conf, in
if (get)
{
- if(copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf)))
+ if (copy_to_user(conf, &dlp->config, sizeof(struct dlci_conf)))
return -EFAULT;
}
@@ -238,7 +238,7 @@ static int dlci_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
dlp = netdev_priv(dev);
- switch(cmd)
+ switch (cmd)
{
case DLCI_GET_SLAVE:
if (!*(short *)(dev->dev_addr))
@@ -417,7 +417,7 @@ static int dlci_ioctl(unsigned int cmd, void __user *arg)
if (!capable(CAP_NET_ADMIN))
return(-EPERM);
- if(copy_from_user(&add, arg, sizeof(struct dlci_add)))
+ if (copy_from_user(&add, arg, sizeof(struct dlci_add)))
return -EFAULT;
switch (cmd)
@@ -426,7 +426,7 @@ static int dlci_ioctl(unsigned int cmd, void __user *arg)
err = dlci_add(&add);
if (!err)
- if(copy_to_user(arg, &add, sizeof(struct dlci_add)))
+ if (copy_to_user(arg, &add, sizeof(struct dlci_add)))
return -EFAULT;
break;
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 07d00b4cf48..3f759daf3ca 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -1128,7 +1128,7 @@ done:
init_timer(&dpriv->timer);
dpriv->timer.expires = jiffies + 10*HZ;
dpriv->timer.data = (unsigned long)dev;
- dpriv->timer.function = &dscc4_timer;
+ dpriv->timer.function = dscc4_timer;
add_timer(&dpriv->timer);
netif_carrier_on(dev);
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index beda387f2fc..9bc2e364915 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -1346,8 +1346,8 @@ do_bottom_half_tx(struct fst_card_info *card)
dev = port_to_dev(port);
while (!(FST_RDB(card, txDescrRing[pi][port->txpos].bits) &
- DMA_OWN)
- && !(card->dmatx_in_progress)) {
+ DMA_OWN) &&
+ !(card->dmatx_in_progress)) {
/*
* There doesn't seem to be a txdone event per-se
* We seem to have to deduce it, by checking the DMA_OWN
@@ -1379,8 +1379,8 @@ do_bottom_half_tx(struct fst_card_info *card)
*/
FST_WRW(card, txDescrRing[pi][port->txpos].bcnt,
cnv_bcnt(skb->len));
- if ((skb->len < FST_MIN_DMA_LEN)
- || (card->family == FST_FAMILY_TXP)) {
+ if ((skb->len < FST_MIN_DMA_LEN) ||
+ (card->family == FST_FAMILY_TXP)) {
/* Enqueue the packet with normal io */
memcpy_toio(card->mem +
BUF_OFFSET(txBuffer[pi]
@@ -2030,8 +2030,8 @@ fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
/* Sanity check the parameters. We don't support partial writes
* when going over the top
*/
- if (wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE
- || wrthdr.size + wrthdr.offset > FST_MEMSIZE) {
+ if (wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE ||
+ wrthdr.size + wrthdr.offset > FST_MEMSIZE) {
return -ENXIO;
}
diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c
index cc07236ea73..9937bbab938 100644
--- a/drivers/net/wan/hdlc.c
+++ b/drivers/net/wan/hdlc.c
@@ -57,7 +57,7 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct hdlc_device *hdlc = dev_to_hdlc(dev);
- if (dev_net(dev) != &init_net) {
+ if (!net_eq(dev_net(dev), &init_net)) {
kfree_skb(skb);
return 0;
}
@@ -102,7 +102,7 @@ static int hdlc_device_event(struct notifier_block *this, unsigned long event,
unsigned long flags;
int on;
- if (dev_net(dev) != &init_net)
+ if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (!(dev->priv_flags & IFF_WAN_HDLC))
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index 840cff72a0f..0e52993e207 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -182,7 +182,7 @@ static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
if (pvc->dlci == dlci)
return pvc;
if (pvc->dlci > dlci)
- return NULL; /* the listed is sorted */
+ return NULL; /* the list is sorted */
pvc = pvc->next;
}
@@ -1214,10 +1214,10 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
return 0;
case IF_PROTO_FR:
- if(!capable(CAP_NET_ADMIN))
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if(dev->flags & IFF_UP)
+ if (dev->flags & IFF_UP)
return -EBUSY;
if (copy_from_user(&new_settings, fr_s, size))
@@ -1263,7 +1263,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */
return -EINVAL;
- if(!capable(CAP_NET_ADMIN))
+ if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&pvc, ifr->ifr_settings.ifs_ifsu.fr_pvc,
diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c
index 15002c3d0d9..74164d29524 100644
--- a/drivers/net/wan/hostess_sv11.c
+++ b/drivers/net/wan/hostess_sv11.c
@@ -218,7 +218,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq)
/* We want a fast IRQ for this device. Actually we'd like an even faster
IRQ ;) - This is one driver RtLinux is made for */
- if (request_irq(irq, &z8530_interrupt, IRQF_DISABLED,
+ if (request_irq(irq, z8530_interrupt, IRQF_DISABLED,
"Hostess SV11", sv) < 0) {
printk(KERN_WARNING "hostess: IRQ %d already in use.\n", irq);
goto err_irq;
diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c
index 7ea71b33d2e..4b6f27e7c82 100644
--- a/drivers/net/wan/lmc/lmc_main.c
+++ b/drivers/net/wan/lmc/lmc_main.c
@@ -927,7 +927,7 @@ static int __devinit lmc_init_one(struct pci_dev *pdev,
sc->lmc_media = &lmc_t1_media;
break;
default:
- printk(KERN_WARNING "%s: LMC UNKOWN CARD!\n", dev->name);
+ printk(KERN_WARNING "%s: LMC UNKNOWN CARD!\n", dev->name);
break;
}
@@ -1028,7 +1028,7 @@ static int lmc_open(struct net_device *dev)
lmc_softreset (sc);
/* Since we have to use PCI bus, this should work on x86,alpha,ppc */
- if (request_irq (dev->irq, &lmc_interrupt, IRQF_SHARED, dev->name, dev)){
+ if (request_irq (dev->irq, lmc_interrupt, IRQF_SHARED, dev->name, dev)){
printk(KERN_WARNING "%s: could not get irq: %d\n", dev->name, dev->irq);
lmc_trace(dev, "lmc_open irq failed out");
return -EAGAIN;
diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c
index 58c66819f39..5394b51bdb2 100644
--- a/drivers/net/wan/n2.c
+++ b/drivers/net/wan/n2.c
@@ -376,7 +376,7 @@ static int __init n2_run(unsigned long io, unsigned long irq,
}
card->io = io;
- if (request_irq(irq, &sca_intr, 0, devname, card)) {
+ if (request_irq(irq, sca_intr, 0, devname, card)) {
printk(KERN_ERR "n2: could not allocate IRQ\n");
n2_destroy_card(card);
return(-EBUSY);
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 79dabc557bd..aec4d395542 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -514,8 +514,8 @@ static int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb)
RX_BD_ADDR(ch, chan->rx_first_bd));
while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
nchar = cpc_readw(&ptdescr->len);
- if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT))
- || (nchar > BD_DEF_LEN)) {
+ if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) ||
+ (nchar > BD_DEF_LEN)) {
if (nchar > BD_DEF_LEN)
status |= DST_RBIT;
@@ -1428,8 +1428,7 @@ static void falc_update_stats(pc300_t * card, int ch)
if (((conf->media == IF_IFACE_T1) &&
(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_LLBAD) &&
- (!(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN)))
- ||
+ (!(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN))) ||
((conf->media == IF_IFACE_E1) &&
(cpc_readb(falcbase + F_REG(RSP, ch)) & RSP_LLBAD))) {
pfalc->prbs = 2;
@@ -2285,8 +2284,8 @@ static void falc_e1_intr(pc300_t * card, int ch)
if (gis & GIS_ISR1) {
isr1 = cpc_readb(falcbase + F_REG(FISR1, ch));
if (isr1 & FISR1_XMB) {
- if ((pfalc->xmb_cause & 2)
- && pfalc->multiframe_mode) {
+ if ((pfalc->xmb_cause & 2) &&
+ pfalc->multiframe_mode) {
if (cpc_readb (falcbase + F_REG(FRS0, ch)) &
(FRS0_LOS | FRS0_AIS | FRS0_LFA)) {
cpc_writeb(falcbase + F_REG(XSP, ch),
@@ -2639,9 +2638,9 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
!(cpc_readb (scabase + M_REG(CTL, ch)) & CTL_DTR);
/* There is no DSR in HD64572 */
}
- if (!arg
- || copy_to_user(arg, &pc300status, sizeof(pc300status_t)))
- return -EINVAL;
+ if (!arg ||
+ copy_to_user(arg, &pc300status, sizeof(pc300status_t)))
+ return -EINVAL;
return 0;
}
diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c
index 1cc24a45f00..25477b5cde4 100644
--- a/drivers/net/wan/sbni.c
+++ b/drivers/net/wan/sbni.c
@@ -195,9 +195,9 @@ static unsigned int netcard_portlist[ ] __initdata = {
static inline int __init
sbni_isa_probe( struct net_device *dev )
{
- if( dev->base_addr > 0x1ff
- && request_region( dev->base_addr, SBNI_IO_EXTENT, dev->name )
- && sbni_probe1( dev, dev->base_addr, dev->irq ) )
+ if( dev->base_addr > 0x1ff &&
+ request_region( dev->base_addr, SBNI_IO_EXTENT, dev->name ) &&
+ sbni_probe1( dev, dev->base_addr, dev->irq ) )
return 0;
else {
@@ -286,8 +286,8 @@ static int __init sbni_init(struct net_device *dev)
for( i = 0; netcard_portlist[ i ]; ++i ) {
int ioaddr = netcard_portlist[ i ];
- if( request_region( ioaddr, SBNI_IO_EXTENT, dev->name )
- && sbni_probe1( dev, ioaddr, 0 ))
+ if( request_region( ioaddr, SBNI_IO_EXTENT, dev->name ) &&
+ sbni_probe1( dev, ioaddr, 0 ))
return 0;
}
@@ -306,9 +306,9 @@ sbni_pci_probe( struct net_device *dev )
unsigned long pci_ioaddr;
u16 subsys;
- if( pdev->vendor != SBNI_PCI_VENDOR
- && pdev->device != SBNI_PCI_DEVICE )
- continue;
+ if( pdev->vendor != SBNI_PCI_VENDOR &&
+ pdev->device != SBNI_PCI_DEVICE )
+ continue;
pci_ioaddr = pci_resource_start( pdev, 0 );
pci_irq_line = pdev->irq;
@@ -977,8 +977,8 @@ check_fhdr( u32 ioaddr, u32 *framelen, u32 *frameno, u32 *ack,
*ack = *framelen & FRAME_ACK_MASK;
*is_first = (*framelen & FRAME_FIRST) != 0;
- if( (*framelen &= FRAME_LEN_MASK) < 6
- || *framelen > SBNI_MAX_FRAME - 3 )
+ if( (*framelen &= FRAME_LEN_MASK) < 6 ||
+ *framelen > SBNI_MAX_FRAME - 3 )
return 0;
value = inb( ioaddr + DAT );
@@ -1173,10 +1173,10 @@ sbni_open( struct net_device *dev )
if( dev->base_addr < 0x400 ) { /* ISA only */
struct net_device **p = sbni_cards;
for( ; *p && p < sbni_cards + SBNI_MAX_NUM_CARDS; ++p )
- if( (*p)->irq == dev->irq
- && ((*p)->base_addr == dev->base_addr + 4
- || (*p)->base_addr == dev->base_addr - 4)
- && (*p)->flags & IFF_UP ) {
+ if( (*p)->irq == dev->irq &&
+ ((*p)->base_addr == dev->base_addr + 4 ||
+ (*p)->base_addr == dev->base_addr - 4) &&
+ (*p)->flags & IFF_UP ) {
((struct net_local *) (netdev_priv(*p)))
->second = dev;
diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c
index 2b15a7e40d5..31c41af2246 100644
--- a/drivers/net/wan/sdla.c
+++ b/drivers/net/wan/sdla.c
@@ -1457,7 +1457,7 @@ got_type:
}
err = -EAGAIN;
- if (request_irq(dev->irq, &sdla_isr, 0, dev->name, dev))
+ if (request_irq(dev->irq, sdla_isr, 0, dev->name, dev))
goto fail;
if (flp->type == SDLA_S507) {
diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c
index 0c525e24b24..61249f489e3 100644
--- a/drivers/net/wan/sealevel.c
+++ b/drivers/net/wan/sealevel.c
@@ -84,8 +84,7 @@ static int sealevel_open(struct net_device *d)
* Link layer up.
*/
- switch (unit)
- {
+ switch (unit) {
case 0:
err = z8530_sync_dma_open(d, slvl->chan);
break;
@@ -133,8 +132,7 @@ static int sealevel_close(struct net_device *d)
hdlc_close(d);
netif_stop_queue(d);
- switch (unit)
- {
+ switch (unit) {
case 0:
z8530_sync_dma_close(d, slvl->chan);
break;
@@ -266,7 +264,7 @@ static __init struct slvl_board *slvl_init(int iobase, int irq,
/* We want a fast IRQ for this device. Actually we'd like an even faster
IRQ ;) - This is one driver RtLinux is made for */
- if (request_irq(irq, &z8530_interrupt, IRQF_DISABLED,
+ if (request_irq(irq, z8530_interrupt, IRQF_DISABLED,
"SeaLevel", dev) < 0) {
printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq);
goto err_request_irq;
@@ -342,8 +340,7 @@ static void __exit slvl_shutdown(struct slvl_board *b)
z8530_shutdown(&b->board);
- for (u = 0; u < 2; u++)
- {
+ for (u = 0; u < 2; u++) {
struct net_device *d = b->dev[u].chan->netdevice;
unregister_hdlc_device(d);
free_netdev(d);
@@ -391,7 +388,7 @@ static int __init slvl_init_module(void)
static void __exit slvl_cleanup_module(void)
{
- if(slvl_unit)
+ if (slvl_unit)
slvl_shutdown(slvl_unit);
}
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c
index 27945049c9e..b9f520b7db6 100644
--- a/drivers/net/wan/x25_asy.c
+++ b/drivers/net/wan/x25_asy.c
@@ -33,6 +33,7 @@
#include <linux/lapb.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
+#include <linux/compat.h>
#include "x25_asy.h"
#include <net/x25device.h>
@@ -656,8 +657,8 @@ static void x25_asy_unesc(struct x25_asy *sl, unsigned char s)
switch (s) {
case X25_END:
- if (!test_and_clear_bit(SLF_ERROR, &sl->flags)
- && sl->rcount > 2)
+ if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
+ sl->rcount > 2)
x25_asy_bump(sl);
clear_bit(SLF_ESCAPE, &sl->flags);
sl->rcount = 0;
@@ -705,6 +706,21 @@ static int x25_asy_ioctl(struct tty_struct *tty, struct file *file,
}
}
+#ifdef CONFIG_COMPAT
+static long x25_asy_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case SIOCGIFNAME:
+ case SIOCSIFHWADDR:
+ return x25_asy_ioctl(tty, file, cmd,
+ (unsigned long)compat_ptr(arg));
+ }
+
+ return -ENOIOCTLCMD;
+}
+#endif
+
static int x25_asy_open_dev(struct net_device *dev)
{
struct x25_asy *sl = netdev_priv(dev);
@@ -754,6 +770,9 @@ static struct tty_ldisc_ops x25_ldisc = {
.open = x25_asy_open_tty,
.close = x25_asy_close_tty,
.ioctl = x25_asy_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = x25_asy_compat_ioctl,
+#endif
.receive_buf = x25_asy_receive_buf,
.write_wakeup = x25_asy_write_wakeup,
};
diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig
index d623b3d99a4..3f703384295 100644
--- a/drivers/net/wimax/i2400m/Kconfig
+++ b/drivers/net/wimax/i2400m/Kconfig
@@ -31,6 +31,14 @@ config WIMAX_I2400M_SDIO
If unsure, it is safe to select M (module).
+config WIMAX_IWMC3200_SDIO
+ bool "Intel Wireless Multicom WiMAX Connection 3200 over SDIO"
+ depends on WIMAX_I2400M_SDIO
+ select IWMC3200TOP
+ help
+ Select if you have a device based on the Intel Multicom WiMAX
+ Connection 3200 over SDIO.
+
config WIMAX_I2400M_DEBUG_LEVEL
int "WiMAX i2400m debug level"
depends on WIMAX_I2400M
diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c
index 07308686dbc..94494554039 100644
--- a/drivers/net/wimax/i2400m/control.c
+++ b/drivers/net/wimax/i2400m/control.c
@@ -54,7 +54,7 @@
* i2400m_set_init_config()
* i2400m_cmd_get_state()
* i2400m_dev_shutdown() Called by i2400m_dev_stop()
- * i2400m->bus_reset()
+ * i2400m_reset()
*
* i2400m_{cmd,get,set}_*()
* i2400m_msg_to_dev()
@@ -82,6 +82,13 @@
#define D_SUBMODULE control
#include "debug-levels.h"
+int i2400m_passive_mode; /* 0 (passive mode disabled) by default */
+module_param_named(passive_mode, i2400m_passive_mode, int, 0644);
+MODULE_PARM_DESC(passive_mode,
+ "If true, the driver will not do any device setup "
+ "and leave it up to user space, who must be properly "
+ "setup.");
+
/*
* Return if a TLV is of a give type and size
@@ -263,7 +270,7 @@ int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *l3l4_hdr,
if (status == 0)
return 0;
- if (status > ARRAY_SIZE(ms_to_errno)) {
+ if (status >= ARRAY_SIZE(ms_to_errno)) {
str = "unknown status code";
result = -EBADR;
} else {
@@ -336,7 +343,7 @@ void i2400m_report_tlv_system_state(struct i2400m *i2400m,
/* Huh? just in case, shut it down */
dev_err(dev, "HW BUG? unknown state %u: shutting down\n",
i2400m_state);
- i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+ i2400m_reset(i2400m, I2400M_RT_WARM);
break;
};
d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n",
@@ -1335,6 +1342,8 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
unsigned argc = 0;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
+ if (i2400m_passive_mode)
+ goto out_passive;
/* Disable idle mode? (enabled by default) */
if (i2400m_idle_mode_disabled) {
if (i2400m_le_v1_3(i2400m)) {
@@ -1377,6 +1386,7 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
result = i2400m_set_init_config(i2400m, args, argc);
if (result < 0)
goto error;
+out_passive:
/*
* Update state: Here it just calls a get state; parsing the
* result (System State TLV and RF Status TLV [done in the rx
diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c
index 9b81af3f80a..b1aec3e1892 100644
--- a/drivers/net/wimax/i2400m/debugfs.c
+++ b/drivers/net/wimax/i2400m/debugfs.c
@@ -214,7 +214,7 @@ int debugfs_i2400m_reset_set(void *data, u64 val)
case I2400M_RT_WARM:
case I2400M_RT_COLD:
case I2400M_RT_BUS:
- result = i2400m->bus_reset(i2400m, rt);
+ result = i2400m_reset(i2400m, rt);
if (result >= 0)
result = 0;
default:
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c
index 304f0443ca4..96a615fe09d 100644
--- a/drivers/net/wimax/i2400m/driver.c
+++ b/drivers/net/wimax/i2400m/driver.c
@@ -41,8 +41,10 @@
* __i2400m_dev_start()
*
* i2400m_setup()
+ * i2400m->bus_setup()
* i2400m_bootrom_init()
* register_netdev()
+ * wimax_dev_add()
* i2400m_dev_start()
* __i2400m_dev_start()
* i2400m_dev_bootstrap()
@@ -50,15 +52,15 @@
* i2400m->bus_dev_start()
* i2400m_firmware_check()
* i2400m_check_mac_addr()
- * wimax_dev_add()
*
* i2400m_release()
- * wimax_dev_rm()
* i2400m_dev_stop()
* __i2400m_dev_stop()
* i2400m_dev_shutdown()
* i2400m->bus_dev_stop()
* i2400m_tx_release()
+ * i2400m->bus_release()
+ * wimax_dev_rm()
* unregister_netdev()
*/
#include "i2400m.h"
@@ -66,6 +68,7 @@
#include <linux/wimax/i2400m.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/suspend.h>
#define D_SUBMODULE driver
#include "debug-levels.h"
@@ -90,76 +93,39 @@ MODULE_PARM_DESC(power_save_disabled,
"False by default (so the device is told to do power "
"saving).");
-/**
- * i2400m_queue_work - schedule work on a i2400m's queue
- *
- * @i2400m: device descriptor
- *
- * @fn: function to run to execute work. It gets passed a 'struct
- * work_struct' that is wrapped in a 'struct i2400m_work'. Once
- * done, you have to (1) i2400m_put(i2400m_work->i2400m) and then
- * (2) kfree(i2400m_work).
- *
- * @gfp_flags: GFP flags for memory allocation.
- *
- * @pl: pointer to a payload buffer that you want to pass to the _work
- * function. Use this to pack (for example) a struct with extra
- * arguments.
- *
- * @pl_size: size of the payload buffer.
- *
- * We do this quite often, so this just saves typing; allocate a
- * wrapper for a i2400m, get a ref to it, pack arguments and launch
- * the work.
- *
- * A usual workflow is:
- *
- * struct my_work_args {
- * void *something;
- * int whatever;
- * };
- * ...
- *
- * struct my_work_args my_args = {
- * .something = FOO,
- * .whaetever = BLAH
- * };
- * i2400m_queue_work(i2400m, 1, my_work_function, GFP_KERNEL,
- * &args, sizeof(args))
- *
- * And now the work function can unpack the arguments and call the
- * real function (or do the job itself):
- *
- * static
- * void my_work_fn((struct work_struct *ws)
- * {
- * struct i2400m_work *iw =
- * container_of(ws, struct i2400m_work, ws);
- * struct my_work_args *my_args = (void *) iw->pl;
- *
- * my_work(iw->i2400m, my_args->something, my_args->whatevert);
- * }
- */
-int i2400m_queue_work(struct i2400m *i2400m,
- void (*fn)(struct work_struct *), gfp_t gfp_flags,
- const void *pl, size_t pl_size)
+static char i2400m_debug_params[128];
+module_param_string(debug, i2400m_debug_params, sizeof(i2400m_debug_params),
+ 0644);
+MODULE_PARM_DESC(debug,
+ "String of space-separated NAME:VALUE pairs, where NAMEs "
+ "are the different debug submodules and VALUE are the "
+ "initial debug value to set.");
+
+static char i2400m_barkers_params[128];
+module_param_string(barkers, i2400m_barkers_params,
+ sizeof(i2400m_barkers_params), 0644);
+MODULE_PARM_DESC(barkers,
+ "String of comma-separated 32-bit values; each is "
+ "recognized as the value the device sends as a reboot "
+ "signal; values are appended to a list--setting one value "
+ "as zero cleans the existing list and starts a new one.");
+
+static
+struct i2400m_work *__i2400m_work_setup(
+ struct i2400m *i2400m, void (*fn)(struct work_struct *),
+ gfp_t gfp_flags, const void *pl, size_t pl_size)
{
- int result;
struct i2400m_work *iw;
- BUG_ON(i2400m->work_queue == NULL);
- result = -ENOMEM;
iw = kzalloc(sizeof(*iw) + pl_size, gfp_flags);
if (iw == NULL)
- goto error_kzalloc;
+ return NULL;
iw->i2400m = i2400m_get(i2400m);
+ iw->pl_size = pl_size;
memcpy(iw->pl, pl, pl_size);
INIT_WORK(&iw->ws, fn);
- result = queue_work(i2400m->work_queue, &iw->ws);
-error_kzalloc:
- return result;
+ return iw;
}
-EXPORT_SYMBOL_GPL(i2400m_queue_work);
/*
@@ -175,21 +141,19 @@ EXPORT_SYMBOL_GPL(i2400m_queue_work);
* it should not happen.
*/
int i2400m_schedule_work(struct i2400m *i2400m,
- void (*fn)(struct work_struct *), gfp_t gfp_flags)
+ void (*fn)(struct work_struct *), gfp_t gfp_flags,
+ const void *pl, size_t pl_size)
{
int result;
struct i2400m_work *iw;
result = -ENOMEM;
- iw = kzalloc(sizeof(*iw), gfp_flags);
- if (iw == NULL)
- goto error_kzalloc;
- iw->i2400m = i2400m_get(i2400m);
- INIT_WORK(&iw->ws, fn);
- result = schedule_work(&iw->ws);
- if (result == 0)
- result = -ENXIO;
-error_kzalloc:
+ iw = __i2400m_work_setup(i2400m, fn, gfp_flags, pl, pl_size);
+ if (iw != NULL) {
+ result = schedule_work(&iw->ws);
+ if (WARN_ON(result == 0))
+ result = -ENXIO;
+ }
return result;
}
@@ -291,7 +255,7 @@ int i2400m_op_reset(struct wimax_dev *wimax_dev)
mutex_lock(&i2400m->init_mutex);
i2400m->reset_ctx = &ctx;
mutex_unlock(&i2400m->init_mutex);
- result = i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+ result = i2400m_reset(i2400m, I2400M_RT_WARM);
if (result < 0)
goto out;
result = wait_for_completion_timeout(&ctx.completion, 4*HZ);
@@ -420,9 +384,15 @@ retry:
dev_err(dev, "cannot create workqueue\n");
goto error_create_workqueue;
}
- result = i2400m->bus_dev_start(i2400m);
- if (result < 0)
- goto error_bus_dev_start;
+ if (i2400m->bus_dev_start) {
+ result = i2400m->bus_dev_start(i2400m);
+ if (result < 0)
+ goto error_bus_dev_start;
+ }
+ i2400m->ready = 1;
+ wmb(); /* see i2400m->ready's documentation */
+ /* process pending reports from the device */
+ queue_work(i2400m->work_queue, &i2400m->rx_report_ws);
result = i2400m_firmware_check(i2400m); /* fw versions ok? */
if (result < 0)
goto error_fw_check;
@@ -430,8 +400,6 @@ retry:
result = i2400m_check_mac_addr(i2400m);
if (result < 0)
goto error_check_mac_addr;
- i2400m->ready = 1;
- wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED);
result = i2400m_dev_initialize(i2400m);
if (result < 0)
goto error_dev_initialize;
@@ -443,8 +411,12 @@ retry:
error_dev_initialize:
error_check_mac_addr:
+ i2400m->ready = 0;
+ wmb(); /* see i2400m->ready's documentation */
+ flush_workqueue(i2400m->work_queue);
error_fw_check:
- i2400m->bus_dev_stop(i2400m);
+ if (i2400m->bus_dev_stop)
+ i2400m->bus_dev_stop(i2400m);
error_bus_dev_start:
destroy_workqueue(i2400m->work_queue);
error_create_workqueue:
@@ -466,11 +438,15 @@ error_bootstrap:
static
int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags)
{
- int result;
+ int result = 0;
mutex_lock(&i2400m->init_mutex); /* Well, start the device */
- result = __i2400m_dev_start(i2400m, bm_flags);
- if (result >= 0)
- i2400m->updown = 1;
+ if (i2400m->updown == 0) {
+ result = __i2400m_dev_start(i2400m, bm_flags);
+ if (result >= 0) {
+ i2400m->updown = 1;
+ wmb(); /* see i2400m->updown's documentation */
+ }
+ }
mutex_unlock(&i2400m->init_mutex);
return result;
}
@@ -495,9 +471,20 @@ void __i2400m_dev_stop(struct i2400m *i2400m)
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING);
+ i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST);
+ complete(&i2400m->msg_completion);
+ i2400m_net_wake_stop(i2400m);
i2400m_dev_shutdown(i2400m);
- i2400m->ready = 0;
- i2400m->bus_dev_stop(i2400m);
+ /*
+ * Make sure no report hooks are running *before* we stop the
+ * communication infrastructure with the device.
+ */
+ i2400m->ready = 0; /* nobody can queue work anymore */
+ wmb(); /* see i2400m->ready's documentation */
+ flush_workqueue(i2400m->work_queue);
+
+ if (i2400m->bus_dev_stop)
+ i2400m->bus_dev_stop(i2400m);
destroy_workqueue(i2400m->work_queue);
i2400m_rx_release(i2400m);
i2400m_tx_release(i2400m);
@@ -518,12 +505,139 @@ void i2400m_dev_stop(struct i2400m *i2400m)
if (i2400m->updown) {
__i2400m_dev_stop(i2400m);
i2400m->updown = 0;
+ wmb(); /* see i2400m->updown's documentation */
}
mutex_unlock(&i2400m->init_mutex);
}
/*
+ * Listen to PM events to cache the firmware before suspend/hibernation
+ *
+ * When the device comes out of suspend, it might go into reset and
+ * firmware has to be uploaded again. At resume, most of the times, we
+ * can't load firmware images from disk, so we need to cache it.
+ *
+ * i2400m_fw_cache() will allocate a kobject and attach the firmware
+ * to it; that way we don't have to worry too much about the fw loader
+ * hitting a race condition.
+ *
+ * Note: modus operandi stolen from the Orinoco driver; thx.
+ */
+static
+int i2400m_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event,
+ void *unused)
+{
+ struct i2400m *i2400m =
+ container_of(notifier, struct i2400m, pm_notifier);
+ struct device *dev = i2400m_dev(i2400m);
+
+ d_fnstart(3, dev, "(i2400m %p pm_event %lx)\n", i2400m, pm_event);
+ switch (pm_event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ i2400m_fw_cache(i2400m);
+ break;
+ case PM_POST_RESTORE:
+ /* Restore from hibernation failed. We need to clean
+ * up in exactly the same way, so fall through. */
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ i2400m_fw_uncache(i2400m);
+ break;
+
+ case PM_RESTORE_PREPARE:
+ default:
+ break;
+ }
+ d_fnend(3, dev, "(i2400m %p pm_event %lx) = void\n", i2400m, pm_event);
+ return NOTIFY_DONE;
+}
+
+
+/*
+ * pre-reset is called before a device is going on reset
+ *
+ * This has to be followed by a call to i2400m_post_reset(), otherwise
+ * bad things might happen.
+ */
+int i2400m_pre_reset(struct i2400m *i2400m)
+{
+ int result;
+ struct device *dev = i2400m_dev(i2400m);
+
+ d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
+ d_printf(1, dev, "pre-reset shut down\n");
+
+ result = 0;
+ mutex_lock(&i2400m->init_mutex);
+ if (i2400m->updown) {
+ netif_tx_disable(i2400m->wimax_dev.net_dev);
+ __i2400m_dev_stop(i2400m);
+ result = 0;
+ /* down't set updown to zero -- this way
+ * post_reset can restore properly */
+ }
+ mutex_unlock(&i2400m->init_mutex);
+ if (i2400m->bus_release)
+ i2400m->bus_release(i2400m);
+ d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(i2400m_pre_reset);
+
+
+/*
+ * Restore device state after a reset
+ *
+ * Do the work needed after a device reset to bring it up to the same
+ * state as it was before the reset.
+ *
+ * NOTE: this requires i2400m->init_mutex taken
+ */
+int i2400m_post_reset(struct i2400m *i2400m)
+{
+ int result = 0;
+ struct device *dev = i2400m_dev(i2400m);
+
+ d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
+ d_printf(1, dev, "post-reset start\n");
+ if (i2400m->bus_setup) {
+ result = i2400m->bus_setup(i2400m);
+ if (result < 0) {
+ dev_err(dev, "bus-specific setup failed: %d\n",
+ result);
+ goto error_bus_setup;
+ }
+ }
+ mutex_lock(&i2400m->init_mutex);
+ if (i2400m->updown) {
+ result = __i2400m_dev_start(
+ i2400m, I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT);
+ if (result < 0)
+ goto error_dev_start;
+ }
+ mutex_unlock(&i2400m->init_mutex);
+ d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
+ return result;
+
+error_dev_start:
+ if (i2400m->bus_release)
+ i2400m->bus_release(i2400m);
+error_bus_setup:
+ /* even if the device was up, it could not be recovered, so we
+ * mark it as down. */
+ i2400m->updown = 0;
+ wmb(); /* see i2400m->updown's documentation */
+ mutex_unlock(&i2400m->init_mutex);
+ d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(i2400m_post_reset);
+
+
+/*
* The device has rebooted; fix up the device and the driver
*
* Tear down the driver communication with the device, reload the
@@ -542,56 +656,69 @@ void i2400m_dev_stop(struct i2400m *i2400m)
* _stop()], don't do anything, let it fail and handle it.
*
* This function is ran always in a thread context
+ *
+ * This function gets passed, as payload to i2400m_work() a 'const
+ * char *' ptr with a "reason" why the reset happened (for messages).
*/
static
void __i2400m_dev_reset_handle(struct work_struct *ws)
{
int result;
struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws);
+ const char *reason;
struct i2400m *i2400m = iw->i2400m;
struct device *dev = i2400m_dev(i2400m);
- enum wimax_st wimax_state;
struct i2400m_reset_ctx *ctx = i2400m->reset_ctx;
- d_fnstart(3, dev, "(ws %p i2400m %p)\n", ws, i2400m);
+ if (WARN_ON(iw->pl_size != sizeof(reason)))
+ reason = "SW BUG: reason n/a";
+ else
+ memcpy(&reason, iw->pl, sizeof(reason));
+
+ d_fnstart(3, dev, "(ws %p i2400m %p reason %s)\n", ws, i2400m, reason);
+
result = 0;
if (mutex_trylock(&i2400m->init_mutex) == 0) {
/* We are still in i2400m_dev_start() [let it fail] or
* i2400m_dev_stop() [we are shutting down anyway, so
* ignore it] or we are resetting somewhere else. */
- dev_err(dev, "device rebooted\n");
+ dev_err(dev, "device rebooted somewhere else?\n");
i2400m_msg_to_dev_cancel_wait(i2400m, -EL3RST);
complete(&i2400m->msg_completion);
goto out;
}
- wimax_state = wimax_state_get(&i2400m->wimax_dev);
- if (wimax_state < WIMAX_ST_UNINITIALIZED) {
- dev_info(dev, "device rebooted: it is down, ignoring\n");
- goto out_unlock; /* ifconfig up/down wasn't called */
+ if (i2400m->updown == 0) {
+ dev_info(dev, "%s: device is down, doing nothing\n", reason);
+ goto out_unlock;
}
- dev_err(dev, "device rebooted: reinitializing driver\n");
+ dev_err(dev, "%s: reinitializing driver\n", reason);
__i2400m_dev_stop(i2400m);
- i2400m->updown = 0;
result = __i2400m_dev_start(i2400m,
I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT);
if (result < 0) {
- dev_err(dev, "device reboot: cannot start the device: %d\n",
- result);
- result = i2400m->bus_reset(i2400m, I2400M_RT_BUS);
- if (result >= 0)
- result = -ENODEV;
- } else
- i2400m->updown = 1;
+ i2400m->updown = 0;
+ wmb(); /* see i2400m->updown's documentation */
+ dev_err(dev, "%s: cannot start the device: %d\n",
+ reason, result);
+ result = -EUCLEAN;
+ }
out_unlock:
if (i2400m->reset_ctx) {
ctx->result = result;
complete(&ctx->completion);
}
mutex_unlock(&i2400m->init_mutex);
+ if (result == -EUCLEAN) {
+ /* ops, need to clean up [w/ init_mutex not held] */
+ result = i2400m_reset(i2400m, I2400M_RT_BUS);
+ if (result >= 0)
+ result = -ENODEV;
+ }
out:
i2400m_put(i2400m);
kfree(iw);
- d_fnend(3, dev, "(ws %p i2400m %p) = void\n", ws, i2400m);
+ d_fnend(3, dev, "(ws %p i2400m %p reason %s) = void\n",
+ ws, i2400m, reason);
return;
}
@@ -608,16 +735,104 @@ out:
* reinitializing the driver to handle the reset, calling into the
* bus-specific functions ops as needed.
*/
-int i2400m_dev_reset_handle(struct i2400m *i2400m)
+int i2400m_dev_reset_handle(struct i2400m *i2400m, const char *reason)
{
i2400m->boot_mode = 1;
wmb(); /* Make sure i2400m_msg_to_dev() sees boot_mode */
return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle,
- GFP_ATOMIC);
+ GFP_ATOMIC, &reason, sizeof(reason));
}
EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle);
+/*
+ * Alloc the command and ack buffers for boot mode
+ *
+ * Get the buffers needed to deal with boot mode messages. These
+ * buffers need to be allocated before the sdio recieve irq is setup.
+ */
+static
+int i2400m_bm_buf_alloc(struct i2400m *i2400m)
+{
+ int result;
+
+ result = -ENOMEM;
+ i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL);
+ if (i2400m->bm_cmd_buf == NULL)
+ goto error_bm_cmd_kzalloc;
+ i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL);
+ if (i2400m->bm_ack_buf == NULL)
+ goto error_bm_ack_buf_kzalloc;
+ return 0;
+
+error_bm_ack_buf_kzalloc:
+ kfree(i2400m->bm_cmd_buf);
+error_bm_cmd_kzalloc:
+ return result;
+}
+
+
+/*
+ * Free boot mode command and ack buffers.
+ */
+static
+void i2400m_bm_buf_free(struct i2400m *i2400m)
+{
+ kfree(i2400m->bm_ack_buf);
+ kfree(i2400m->bm_cmd_buf);
+}
+
+
+/**
+ * i2400m_init - Initialize a 'struct i2400m' from all zeroes
+ *
+ * This is a bus-generic API call.
+ */
+void i2400m_init(struct i2400m *i2400m)
+{
+ wimax_dev_init(&i2400m->wimax_dev);
+
+ i2400m->boot_mode = 1;
+ i2400m->rx_reorder = 1;
+ init_waitqueue_head(&i2400m->state_wq);
+
+ spin_lock_init(&i2400m->tx_lock);
+ i2400m->tx_pl_min = UINT_MAX;
+ i2400m->tx_size_min = UINT_MAX;
+
+ spin_lock_init(&i2400m->rx_lock);
+ i2400m->rx_pl_min = UINT_MAX;
+ i2400m->rx_size_min = UINT_MAX;
+ INIT_LIST_HEAD(&i2400m->rx_reports);
+ INIT_WORK(&i2400m->rx_report_ws, i2400m_report_hook_work);
+
+ mutex_init(&i2400m->msg_mutex);
+ init_completion(&i2400m->msg_completion);
+
+ mutex_init(&i2400m->init_mutex);
+ /* wake_tx_ws is initialized in i2400m_tx_setup() */
+}
+EXPORT_SYMBOL_GPL(i2400m_init);
+
+
+int i2400m_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
+{
+ struct net_device *net_dev = i2400m->wimax_dev.net_dev;
+
+ /*
+ * Make sure we stop TXs and down the carrier before
+ * resetting; this is needed to avoid things like
+ * i2400m_wake_tx() scheduling stuff in parallel.
+ */
+ if (net_dev->reg_state == NETREG_REGISTERED) {
+ netif_tx_disable(net_dev);
+ netif_carrier_off(net_dev);
+ }
+ return i2400m->bus_reset(i2400m, rt);
+}
+EXPORT_SYMBOL_GPL(i2400m_reset);
+
+
/**
* i2400m_setup - bus-generic setup function for the i2400m device
*
@@ -625,13 +840,9 @@ EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle);
*
* Returns: 0 if ok, < 0 errno code on error.
*
- * Initializes the bus-generic parts of the i2400m driver; the
- * bus-specific parts have been initialized, function pointers filled
- * out by the bus-specific probe function.
- *
- * As well, this registers the WiMAX and net device nodes. Once this
- * function returns, the device is operative and has to be ready to
- * receive and send network traffic and WiMAX control operations.
+ * Sets up basic device comunication infrastructure, boots the ROM to
+ * read the MAC address, registers with the WiMAX and network stacks
+ * and then brings up the device.
*/
int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
{
@@ -645,16 +856,21 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
snprintf(wimax_dev->name, sizeof(wimax_dev->name),
"i2400m-%s:%s", dev->bus->name, dev_name(dev));
- i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL);
- if (i2400m->bm_cmd_buf == NULL) {
- dev_err(dev, "cannot allocate USB command buffer\n");
- goto error_bm_cmd_kzalloc;
+ result = i2400m_bm_buf_alloc(i2400m);
+ if (result < 0) {
+ dev_err(dev, "cannot allocate bootmode scratch buffers\n");
+ goto error_bm_buf_alloc;
}
- i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL);
- if (i2400m->bm_ack_buf == NULL) {
- dev_err(dev, "cannot allocate USB ack buffer\n");
- goto error_bm_ack_buf_kzalloc;
+
+ if (i2400m->bus_setup) {
+ result = i2400m->bus_setup(i2400m);
+ if (result < 0) {
+ dev_err(dev, "bus-specific setup failed: %d\n",
+ result);
+ goto error_bus_setup;
+ }
}
+
result = i2400m_bootrom_init(i2400m, bm_flags);
if (result < 0) {
dev_err(dev, "read mac addr: bootrom init "
@@ -666,6 +882,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
goto error_read_mac_addr;
random_ether_addr(i2400m->src_mac_addr);
+ i2400m->pm_notifier.notifier_call = i2400m_pm_notifier;
+ register_pm_notifier(&i2400m->pm_notifier);
+
result = register_netdev(net_dev); /* Okey dokey, bring it up */
if (result < 0) {
dev_err(dev, "cannot register i2400m network device: %d\n",
@@ -674,18 +893,13 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
}
netif_carrier_off(net_dev);
- result = i2400m_dev_start(i2400m, bm_flags);
- if (result < 0)
- goto error_dev_start;
-
i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user;
i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle;
i2400m->wimax_dev.op_reset = i2400m_op_reset;
+
result = wimax_dev_add(&i2400m->wimax_dev, net_dev);
if (result < 0)
goto error_wimax_dev_add;
- /* User space needs to do some init stuff */
- wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED);
/* Now setup all that requires a registered net and wimax device. */
result = sysfs_create_group(&net_dev->dev.kobj, &i2400m_dev_attr_group);
@@ -693,30 +907,37 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
dev_err(dev, "cannot setup i2400m's sysfs: %d\n", result);
goto error_sysfs_setup;
}
+
result = i2400m_debugfs_add(i2400m);
if (result < 0) {
dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result);
goto error_debugfs_setup;
}
+
+ result = i2400m_dev_start(i2400m, bm_flags);
+ if (result < 0)
+ goto error_dev_start;
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
+error_dev_start:
+ i2400m_debugfs_rm(i2400m);
error_debugfs_setup:
sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj,
&i2400m_dev_attr_group);
error_sysfs_setup:
wimax_dev_rm(&i2400m->wimax_dev);
error_wimax_dev_add:
- i2400m_dev_stop(i2400m);
-error_dev_start:
unregister_netdev(net_dev);
error_register_netdev:
+ unregister_pm_notifier(&i2400m->pm_notifier);
error_read_mac_addr:
error_bootrom_init:
- kfree(i2400m->bm_ack_buf);
-error_bm_ack_buf_kzalloc:
- kfree(i2400m->bm_cmd_buf);
-error_bm_cmd_kzalloc:
+ if (i2400m->bus_release)
+ i2400m->bus_release(i2400m);
+error_bus_setup:
+ i2400m_bm_buf_free(i2400m);
+error_bm_buf_alloc:
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
}
@@ -735,14 +956,17 @@ void i2400m_release(struct i2400m *i2400m)
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
netif_stop_queue(i2400m->wimax_dev.net_dev);
+ i2400m_dev_stop(i2400m);
+
i2400m_debugfs_rm(i2400m);
sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj,
&i2400m_dev_attr_group);
wimax_dev_rm(&i2400m->wimax_dev);
- i2400m_dev_stop(i2400m);
unregister_netdev(i2400m->wimax_dev.net_dev);
- kfree(i2400m->bm_ack_buf);
- kfree(i2400m->bm_cmd_buf);
+ unregister_pm_notifier(&i2400m->pm_notifier);
+ if (i2400m->bus_release)
+ i2400m->bus_release(i2400m);
+ i2400m_bm_buf_free(i2400m);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
EXPORT_SYMBOL_GPL(i2400m_release);
@@ -759,6 +983,7 @@ struct d_level D_LEVEL[] = {
D_SUBMODULE_DEFINE(netdev),
D_SUBMODULE_DEFINE(rfkill),
D_SUBMODULE_DEFINE(rx),
+ D_SUBMODULE_DEFINE(sysfs),
D_SUBMODULE_DEFINE(tx),
};
size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
@@ -767,7 +992,9 @@ size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
static
int __init i2400m_driver_init(void)
{
- return 0;
+ d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400m_debug_params,
+ "i2400m.debug");
+ return i2400m_barker_db_init(i2400m_barkers_params);
}
module_init(i2400m_driver_init);
@@ -776,6 +1003,7 @@ void __exit i2400m_driver_exit(void)
{
/* for scheds i2400m_dev_reset_handle() */
flush_scheduled_work();
+ i2400m_barker_db_exit();
return;
}
module_exit(i2400m_driver_exit);
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index e81750e5445..64cdfeb299c 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -40,11 +40,9 @@
*
* THE PROCEDURE
*
- * (this is decribed for USB, but for SDIO is similar)
- *
- * The 2400m works in two modes: boot-mode or normal mode. In boot
- * mode we can execute only a handful of commands targeted at
- * uploading the firmware and launching it.
+ * The 2400m and derived devices work in two modes: boot-mode or
+ * normal mode. In boot mode we can execute only a handful of commands
+ * targeted at uploading the firmware and launching it.
*
* The 2400m enters boot mode when it is first connected to the
* system, when it crashes and when you ask it to reboot. There are
@@ -52,18 +50,26 @@
* firmwares signed with a certain private key, non-signed takes any
* firmware. Normal hardware takes only signed firmware.
*
- * Upon entrance to boot mode, the device sends a few zero length
- * packets (ZLPs) on the notification endpoint, then a reboot barker
- * (4 le32 words with value I2400M_{S,N}BOOT_BARKER). We ack it by
- * sending the same barker on the bulk out endpoint. The device acks
- * with a reboot ack barker (4 le32 words with value 0xfeedbabe) and
- * then the device is fully rebooted. At this point we can upload the
- * firmware.
+ * On boot mode, in USB, we write to the device using the bulk out
+ * endpoint and read from it in the notification endpoint. In SDIO we
+ * talk to it via the write address and read from the read address.
+ *
+ * Upon entrance to boot mode, the device sends (preceeded with a few
+ * zero length packets (ZLPs) on the notification endpoint in USB) a
+ * reboot barker (4 le32 words with the same value). We ack it by
+ * sending the same barker to the device. The device acks with a
+ * reboot ack barker (4 le32 words with value I2400M_ACK_BARKER) and
+ * then is fully booted. At this point we can upload the firmware.
+ *
+ * Note that different iterations of the device and EEPROM
+ * configurations will send different [re]boot barkers; these are
+ * collected in i2400m_barker_db along with the firmware
+ * characteristics they require.
*
* This process is accomplished by the i2400m_bootrom_init()
* function. All the device interaction happens through the
* i2400m_bm_cmd() [boot mode command]. Special return values will
- * indicate if the device resets.
+ * indicate if the device did reset during the process.
*
* After this, we read the MAC address and then (if needed)
* reinitialize the device. We need to read it ahead of time because
@@ -72,11 +78,11 @@
*
* We can then upload the firmware file. The file is composed of a BCF
* header (basic data, keys and signatures) and a list of write
- * commands and payloads. We first upload the header
- * [i2400m_dnload_init()] and then pass the commands and payloads
- * verbatim to the i2400m_bm_cmd() function
- * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new
- * firmware [i2400m_dnload_finalize()].
+ * commands and payloads. Optionally more BCF headers might follow the
+ * main payload. We first upload the header [i2400m_dnload_init()] and
+ * then pass the commands and payloads verbatim to the i2400m_bm_cmd()
+ * function [i2400m_dnload_bcf()]. Then we tell the device to jump to
+ * the new firmware [i2400m_dnload_finalize()].
*
* Once firmware is uploaded, we are good to go :)
*
@@ -99,18 +105,32 @@
* read an acknolwedgement from it (or an asynchronous notification)
* from it.
*
+ * FIRMWARE LOADING
+ *
+ * Note that in some cases, we can't just load a firmware file (for
+ * example, when resuming). For that, we might cache the firmware
+ * file. Thus, when doing the bootstrap, if there is a cache firmware
+ * file, it is used; if not, loading from disk is attempted.
+ *
* ROADMAP
*
+ * i2400m_barker_db_init Called by i2400m_driver_init()
+ * i2400m_barker_db_add
+ *
+ * i2400m_barker_db_exit Called by i2400m_driver_exit()
+ *
* i2400m_dev_bootstrap Called by __i2400m_dev_start()
* request_firmware
- * i2400m_fw_check
- * i2400m_fw_dnload
+ * i2400m_fw_bootstrap
+ * i2400m_fw_check
+ * i2400m_fw_hdr_check
+ * i2400m_fw_dnload
* release_firmware
*
* i2400m_fw_dnload
* i2400m_bootrom_init
* i2400m_bm_cmd
- * i2400m->bus_reset
+ * i2400m_reset
* i2400m_dnload_init
* i2400m_dnload_init_signed
* i2400m_dnload_init_nonsigned
@@ -125,9 +145,14 @@
* i2400m->bus_bm_cmd_send()
* i2400m->bus_bm_wait_for_ack
* __i2400m_bm_ack_verify
+ * i2400m_is_boot_barker
*
* i2400m_bm_cmd_prepare Used by bus-drivers to prep
* commands before sending
+ *
+ * i2400m_pm_notifier Called on Power Management events
+ * i2400m_fw_cache
+ * i2400m_fw_uncache
*/
#include <linux/firmware.h>
#include <linux/sched.h>
@@ -175,6 +200,240 @@ EXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare);
/*
+ * Database of known barkers.
+ *
+ * A barker is what the device sends indicating he is ready to be
+ * bootloaded. Different versions of the device will send different
+ * barkers. Depending on the barker, it might mean the device wants
+ * some kind of firmware or the other.
+ */
+static struct i2400m_barker_db {
+ __le32 data[4];
+} *i2400m_barker_db;
+static size_t i2400m_barker_db_used, i2400m_barker_db_size;
+
+
+static
+int i2400m_zrealloc_2x(void **ptr, size_t *_count, size_t el_size,
+ gfp_t gfp_flags)
+{
+ size_t old_count = *_count,
+ new_count = old_count ? 2 * old_count : 2,
+ old_size = el_size * old_count,
+ new_size = el_size * new_count;
+ void *nptr = krealloc(*ptr, new_size, gfp_flags);
+ if (nptr) {
+ /* zero the other half or the whole thing if old_count
+ * was zero */
+ if (old_size == 0)
+ memset(nptr, 0, new_size);
+ else
+ memset(nptr + old_size, 0, old_size);
+ *_count = new_count;
+ *ptr = nptr;
+ return 0;
+ } else
+ return -ENOMEM;
+}
+
+
+/*
+ * Add a barker to the database
+ *
+ * This cannot used outside of this module and only at at module_init
+ * time. This is to avoid the need to do locking.
+ */
+static
+int i2400m_barker_db_add(u32 barker_id)
+{
+ int result;
+
+ struct i2400m_barker_db *barker;
+ if (i2400m_barker_db_used >= i2400m_barker_db_size) {
+ result = i2400m_zrealloc_2x(
+ (void **) &i2400m_barker_db, &i2400m_barker_db_size,
+ sizeof(i2400m_barker_db[0]), GFP_KERNEL);
+ if (result < 0)
+ return result;
+ }
+ barker = i2400m_barker_db + i2400m_barker_db_used++;
+ barker->data[0] = le32_to_cpu(barker_id);
+ barker->data[1] = le32_to_cpu(barker_id);
+ barker->data[2] = le32_to_cpu(barker_id);
+ barker->data[3] = le32_to_cpu(barker_id);
+ return 0;
+}
+
+
+void i2400m_barker_db_exit(void)
+{
+ kfree(i2400m_barker_db);
+ i2400m_barker_db = NULL;
+ i2400m_barker_db_size = 0;
+ i2400m_barker_db_used = 0;
+}
+
+
+/*
+ * Helper function to add all the known stable barkers to the barker
+ * database.
+ */
+static
+int i2400m_barker_db_known_barkers(void)
+{
+ int result;
+
+ result = i2400m_barker_db_add(I2400M_NBOOT_BARKER);
+ if (result < 0)
+ goto error_add;
+ result = i2400m_barker_db_add(I2400M_SBOOT_BARKER);
+ if (result < 0)
+ goto error_add;
+ result = i2400m_barker_db_add(I2400M_SBOOT_BARKER_6050);
+ if (result < 0)
+ goto error_add;
+error_add:
+ return result;
+}
+
+
+/*
+ * Initialize the barker database
+ *
+ * This can only be used from the module_init function for this
+ * module; this is to avoid the need to do locking.
+ *
+ * @options: command line argument with extra barkers to
+ * recognize. This is a comma-separated list of 32-bit hex
+ * numbers. They are appended to the existing list. Setting 0
+ * cleans the existing list and starts a new one.
+ */
+int i2400m_barker_db_init(const char *_options)
+{
+ int result;
+ char *options = NULL, *options_orig, *token;
+
+ i2400m_barker_db = NULL;
+ i2400m_barker_db_size = 0;
+ i2400m_barker_db_used = 0;
+
+ result = i2400m_barker_db_known_barkers();
+ if (result < 0)
+ goto error_add;
+ /* parse command line options from i2400m.barkers */
+ if (_options != NULL) {
+ unsigned barker;
+
+ options_orig = kstrdup(_options, GFP_KERNEL);
+ if (options_orig == NULL)
+ goto error_parse;
+ options = options_orig;
+
+ while ((token = strsep(&options, ",")) != NULL) {
+ if (*token == '\0') /* eat joint commas */
+ continue;
+ if (sscanf(token, "%x", &barker) != 1
+ || barker > 0xffffffff) {
+ printk(KERN_ERR "%s: can't recognize "
+ "i2400m.barkers value '%s' as "
+ "a 32-bit number\n",
+ __func__, token);
+ result = -EINVAL;
+ goto error_parse;
+ }
+ if (barker == 0) {
+ /* clean list and start new */
+ i2400m_barker_db_exit();
+ continue;
+ }
+ result = i2400m_barker_db_add(barker);
+ if (result < 0)
+ goto error_add;
+ }
+ kfree(options_orig);
+ }
+ return 0;
+
+error_parse:
+error_add:
+ kfree(i2400m_barker_db);
+ return result;
+}
+
+
+/*
+ * Recognize a boot barker
+ *
+ * @buf: buffer where the boot barker.
+ * @buf_size: size of the buffer (has to be 16 bytes). It is passed
+ * here so the function can check it for the caller.
+ *
+ * Note that as a side effect, upon identifying the obtained boot
+ * barker, this function will set i2400m->barker to point to the right
+ * barker database entry. Subsequent calls to the function will result
+ * in verifying that the same type of boot barker is returned when the
+ * device [re]boots (as long as the same device instance is used).
+ *
+ * Return: 0 if @buf matches a known boot barker. -ENOENT if the
+ * buffer in @buf doesn't match any boot barker in the database or
+ * -EILSEQ if the buffer doesn't have the right size.
+ */
+int i2400m_is_boot_barker(struct i2400m *i2400m,
+ const void *buf, size_t buf_size)
+{
+ int result;
+ struct device *dev = i2400m_dev(i2400m);
+ struct i2400m_barker_db *barker;
+ int i;
+
+ result = -ENOENT;
+ if (buf_size != sizeof(i2400m_barker_db[i].data))
+ return result;
+
+ /* Short circuit if we have already discovered the barker
+ * associated with the device. */
+ if (i2400m->barker
+ && !memcmp(buf, i2400m->barker, sizeof(i2400m->barker->data))) {
+ unsigned index = (i2400m->barker - i2400m_barker_db)
+ / sizeof(*i2400m->barker);
+ d_printf(2, dev, "boot barker cache-confirmed #%u/%08x\n",
+ index, le32_to_cpu(i2400m->barker->data[0]));
+ return 0;
+ }
+
+ for (i = 0; i < i2400m_barker_db_used; i++) {
+ barker = &i2400m_barker_db[i];
+ BUILD_BUG_ON(sizeof(barker->data) != 16);
+ if (memcmp(buf, barker->data, sizeof(barker->data)))
+ continue;
+
+ if (i2400m->barker == NULL) {
+ i2400m->barker = barker;
+ d_printf(1, dev, "boot barker set to #%u/%08x\n",
+ i, le32_to_cpu(barker->data[0]));
+ if (barker->data[0] == le32_to_cpu(I2400M_NBOOT_BARKER))
+ i2400m->sboot = 0;
+ else
+ i2400m->sboot = 1;
+ } else if (i2400m->barker != barker) {
+ dev_err(dev, "HW inconsistency: device "
+ "reports a different boot barker "
+ "than set (from %08x to %08x)\n",
+ le32_to_cpu(i2400m->barker->data[0]),
+ le32_to_cpu(barker->data[0]));
+ result = -EIO;
+ } else
+ d_printf(2, dev, "boot barker confirmed #%u/%08x\n",
+ i, le32_to_cpu(barker->data[0]));
+ result = 0;
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(i2400m_is_boot_barker);
+
+
+/*
* Verify the ack data received
*
* Given a reply to a boot mode command, chew it and verify everything
@@ -204,20 +463,10 @@ ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode,
opcode, ack_size, sizeof(*ack));
goto error_ack_short;
}
- if (ack_size == sizeof(i2400m_NBOOT_BARKER)
- && memcmp(ack, i2400m_NBOOT_BARKER, sizeof(*ack)) == 0) {
+ result = i2400m_is_boot_barker(i2400m, ack, ack_size);
+ if (result >= 0) {
result = -ERESTARTSYS;
- i2400m->sboot = 0;
- d_printf(6, dev, "boot-mode cmd %d: "
- "HW non-signed boot barker\n", opcode);
- goto error_reboot;
- }
- if (ack_size == sizeof(i2400m_SBOOT_BARKER)
- && memcmp(ack, i2400m_SBOOT_BARKER, sizeof(*ack)) == 0) {
- result = -ERESTARTSYS;
- i2400m->sboot = 1;
- d_printf(6, dev, "boot-mode cmd %d: HW signed reboot barker\n",
- opcode);
+ d_printf(6, dev, "boot-mode cmd %d: HW boot barker\n", opcode);
goto error_reboot;
}
if (ack_size == sizeof(i2400m_ACK_BARKER)
@@ -343,7 +592,6 @@ ssize_t i2400m_bm_cmd(struct i2400m *i2400m,
BUG_ON(i2400m->boot_mode == 0);
if (cmd != NULL) { /* send the command */
- memcpy(i2400m->bm_cmd_buf, cmd, cmd_size);
result = i2400m->bus_bm_cmd_send(i2400m, cmd, cmd_size, flags);
if (result < 0)
goto error_cmd_send;
@@ -432,8 +680,8 @@ static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk,
* Download a BCF file's sections to the device
*
* @i2400m: device descriptor
- * @bcf: pointer to firmware data (followed by the payloads). Assumed
- * verified and consistent.
+ * @bcf: pointer to firmware data (first header followed by the
+ * payloads). Assumed verified and consistent.
* @bcf_len: length (in bytes) of the @bcf buffer.
*
* Returns: < 0 errno code on error or the offset to the jump instruction.
@@ -472,14 +720,17 @@ ssize_t i2400m_dnload_bcf(struct i2400m *i2400m,
"downloading section #%zu (@%zu %zu B) to 0x%08x\n",
section, offset, sizeof(*bh) + data_size,
le32_to_cpu(bh->target_addr));
- if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP) {
- /* Secure boot needs to stop here */
- d_printf(5, dev, "signed jump found @%zu\n", offset);
+ /*
+ * We look for JUMP cmd from the bootmode header,
+ * either I2400M_BRH_SIGNED_JUMP for secure boot
+ * or I2400M_BRH_JUMP for unsecure boot, the last chunk
+ * should be the bootmode header with JUMP cmd.
+ */
+ if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP ||
+ i2400m_brh_get_opcode(bh) == I2400M_BRH_JUMP) {
+ d_printf(5, dev, "jump found @%zu\n", offset);
break;
}
- if (offset + section_size == bcf_len)
- /* Non-secure boot stops here */
- break;
if (offset + section_size > bcf_len) {
dev_err(dev, "fw %s: bad section #%zu, "
"end (@%zu) beyond EOF (@%zu)\n",
@@ -510,13 +761,30 @@ error_send:
/*
+ * Indicate if the device emitted a reboot barker that indicates
+ * "signed boot"
+ */
+static
+unsigned i2400m_boot_is_signed(struct i2400m *i2400m)
+{
+ return likely(i2400m->sboot);
+}
+
+
+/*
* Do the final steps of uploading firmware
*
+ * @bcf_hdr: BCF header we are actually using
+ * @bcf: pointer to the firmware image (which matches the first header
+ * that is followed by the actual payloads).
+ * @offset: [byte] offset into @bcf for the command we need to send.
+ *
* Depending on the boot mode (signed vs non-signed), different
* actions need to be taken.
*/
static
int i2400m_dnload_finalize(struct i2400m *i2400m,
+ const struct i2400m_bcf_hdr *bcf_hdr,
const struct i2400m_bcf_hdr *bcf, size_t offset)
{
int ret = 0;
@@ -530,10 +798,14 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
d_fnstart(3, dev, "offset %zu\n", offset);
cmd = (void *) bcf + offset;
- if (i2400m->sboot == 0) {
+ if (i2400m_boot_is_signed(i2400m) == 0) {
struct i2400m_bootrom_header jump_ack;
d_printf(1, dev, "unsecure boot, jumping to 0x%08x\n",
le32_to_cpu(cmd->target_addr));
+ cmd_buf = i2400m->bm_cmd_buf;
+ memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd));
+ cmd = &cmd_buf->cmd;
+ /* now cmd points to the actual bootrom_header in cmd_buf */
i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP);
cmd->data_size = 0;
ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
@@ -544,12 +816,13 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
cmd_buf = i2400m->bm_cmd_buf;
memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd));
signature_block_offset =
- sizeof(*bcf)
- + le32_to_cpu(bcf->key_size) * sizeof(u32)
- + le32_to_cpu(bcf->exponent_size) * sizeof(u32);
+ sizeof(*bcf_hdr)
+ + le32_to_cpu(bcf_hdr->key_size) * sizeof(u32)
+ + le32_to_cpu(bcf_hdr->exponent_size) * sizeof(u32);
signature_block_size =
- le32_to_cpu(bcf->modulus_size) * sizeof(u32);
- memcpy(cmd_buf->cmd_pl, (void *) bcf + signature_block_offset,
+ le32_to_cpu(bcf_hdr->modulus_size) * sizeof(u32);
+ memcpy(cmd_buf->cmd_pl,
+ (void *) bcf_hdr + signature_block_offset,
signature_block_size);
ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd,
sizeof(cmd_buf->cmd) + signature_block_size,
@@ -565,7 +838,7 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
*
* @i2400m: device descriptor
* @flags:
- * I2400M_BRI_SOFT: a reboot notification has been seen
+ * I2400M_BRI_SOFT: a reboot barker has been seen
* already, so don't wait for it.
*
* I2400M_BRI_NO_REBOOT: Don't send a reboot command, but wait
@@ -576,17 +849,15 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
*
* < 0 errno code on error, 0 if ok.
*
- * i2400m->sboot set to 0 for unsecure boot process, 1 for secure
- * boot process.
- *
* Description:
*
* Tries hard enough to put the device in boot-mode. There are two
* main phases to this:
*
* a. (1) send a reboot command and (2) get a reboot barker
- * b. (1) ack the reboot sending a reboot barker and (2) getting an
- * ack barker in return
+ *
+ * b. (1) echo/ack the reboot sending the reboot barker back and (2)
+ * getting an ack barker in return
*
* We want to skip (a) in some cases [soft]. The state machine is
* horrible, but it is basically: on each phase, send what has to be
@@ -594,6 +865,16 @@ int i2400m_dnload_finalize(struct i2400m *i2400m,
* have to backtrack and retry, so we keep a max tries counter for
* that.
*
+ * It sucks because we don't know ahead of time which is going to be
+ * the reboot barker (the device might send different ones depending
+ * on its EEPROM config) and once the device reboots and waits for the
+ * echo/ack reboot barker being sent back, it doesn't understand
+ * anything else. So we can be left at the point where we don't know
+ * what to send to it -- cold reset and bus reset seem to have little
+ * effect. So the function iterates (in this case) through all the
+ * known barkers and tries them all until an ACK is
+ * received. Otherwise, it gives up.
+ *
* If we get a timeout after sending a warm reset, we do it again.
*/
int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)
@@ -602,10 +883,11 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)
struct device *dev = i2400m_dev(i2400m);
struct i2400m_bootrom_header *cmd;
struct i2400m_bootrom_header ack;
- int count = I2400M_BOOT_RETRIES;
+ int count = i2400m->bus_bm_retries;
int ack_timeout_cnt = 1;
+ unsigned i;
- BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_NBOOT_BARKER));
+ BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_barker_db[0].data));
BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER));
d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags);
@@ -614,27 +896,59 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags)
if (flags & I2400M_BRI_SOFT)
goto do_reboot_ack;
do_reboot:
+ ack_timeout_cnt = 1;
if (--count < 0)
goto error_timeout;
d_printf(4, dev, "device reboot: reboot command [%d # left]\n",
count);
if ((flags & I2400M_BRI_NO_REBOOT) == 0)
- i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+ i2400m_reset(i2400m, I2400M_RT_WARM);
result = i2400m_bm_cmd(i2400m, NULL, 0, &ack, sizeof(ack),
I2400M_BM_CMD_RAW);
flags &= ~I2400M_BRI_NO_REBOOT;
switch (result) {
case -ERESTARTSYS:
+ /*
+ * at this point, i2400m_bm_cmd(), through
+ * __i2400m_bm_ack_process(), has updated
+ * i2400m->barker and we are good to go.
+ */
d_printf(4, dev, "device reboot: got reboot barker\n");
break;
case -EISCONN: /* we don't know how it got here...but we follow it */
d_printf(4, dev, "device reboot: got ack barker - whatever\n");
goto do_reboot;
- case -ETIMEDOUT: /* device has timed out, we might be in boot
- * mode already and expecting an ack, let's try
- * that */
- dev_info(dev, "warm reset timed out, trying an ack\n");
- goto do_reboot_ack;
+ case -ETIMEDOUT:
+ /*
+ * Device has timed out, we might be in boot mode
+ * already and expecting an ack; if we don't know what
+ * the barker is, we just send them all. Cold reset
+ * and bus reset don't work. Beats me.
+ */
+ if (i2400m->barker != NULL) {
+ dev_err(dev, "device boot: reboot barker timed out, "
+ "trying (set) %08x echo/ack\n",
+ le32_to_cpu(i2400m->barker->data[0]));
+ goto do_reboot_ack;
+ }
+ for (i = 0; i < i2400m_barker_db_used; i++) {
+ struct i2400m_barker_db *barker = &i2400m_barker_db[i];
+ memcpy(cmd, barker->data, sizeof(barker->data));
+ result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
+ &ack, sizeof(ack),
+ I2400M_BM_CMD_RAW);
+ if (result == -EISCONN) {
+ dev_warn(dev, "device boot: got ack barker "
+ "after sending echo/ack barker "
+ "#%d/%08x; rebooting j.i.c.\n",
+ i, le32_to_cpu(barker->data[0]));
+ flags &= ~I2400M_BRI_NO_REBOOT;
+ goto do_reboot;
+ }
+ }
+ dev_err(dev, "device boot: tried all the echo/acks, could "
+ "not get device to respond; giving up");
+ result = -ESHUTDOWN;
case -EPROTO:
case -ESHUTDOWN: /* dev is gone */
case -EINTR: /* user cancelled */
@@ -642,6 +956,7 @@ do_reboot:
default:
dev_err(dev, "device reboot: error %d while waiting "
"for reboot barker - rebooting\n", result);
+ d_dump(1, dev, &ack, result);
goto do_reboot;
}
/* At this point we ack back with 4 REBOOT barkers and expect
@@ -650,12 +965,7 @@ do_reboot:
* notification and report it as -EISCONN. */
do_reboot_ack:
d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count);
- if (i2400m->sboot == 0)
- memcpy(cmd, i2400m_NBOOT_BARKER,
- sizeof(i2400m_NBOOT_BARKER));
- else
- memcpy(cmd, i2400m_SBOOT_BARKER,
- sizeof(i2400m_SBOOT_BARKER));
+ memcpy(cmd, i2400m->barker->data, sizeof(i2400m->barker->data));
result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd),
&ack, sizeof(ack), I2400M_BM_CMD_RAW);
switch (result) {
@@ -668,10 +978,8 @@ do_reboot_ack:
d_printf(4, dev, "reboot ack: got ack barker - good\n");
break;
case -ETIMEDOUT: /* no response, maybe it is the other type? */
- if (ack_timeout_cnt-- >= 0) {
- d_printf(4, dev, "reboot ack timedout: "
- "trying the other type?\n");
- i2400m->sboot = !i2400m->sboot;
+ if (ack_timeout_cnt-- < 0) {
+ d_printf(4, dev, "reboot ack timedout: retrying\n");
goto do_reboot_ack;
} else {
dev_err(dev, "reboot ack timedout too long: "
@@ -839,32 +1147,29 @@ int i2400m_dnload_init_signed(struct i2400m *i2400m,
* (signed or non-signed).
*/
static
-int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf)
+int i2400m_dnload_init(struct i2400m *i2400m,
+ const struct i2400m_bcf_hdr *bcf_hdr)
{
int result;
struct device *dev = i2400m_dev(i2400m);
- u32 module_id = le32_to_cpu(bcf->module_id);
- if (i2400m->sboot == 0
- && (module_id & I2400M_BCF_MOD_ID_POKES) == 0) {
- /* non-signed boot process without pokes */
- result = i2400m_dnload_init_nonsigned(i2400m);
+ if (i2400m_boot_is_signed(i2400m)) {
+ d_printf(1, dev, "signed boot\n");
+ result = i2400m_dnload_init_signed(i2400m, bcf_hdr);
if (result == -ERESTARTSYS)
return result;
if (result < 0)
- dev_err(dev, "fw %s: non-signed download "
+ dev_err(dev, "firmware %s: signed boot download "
"initialization failed: %d\n",
i2400m->fw_name, result);
- } else if (i2400m->sboot == 0
- && (module_id & I2400M_BCF_MOD_ID_POKES)) {
- /* non-signed boot process with pokes, nothing to do */
- result = 0;
- } else { /* signed boot process */
- result = i2400m_dnload_init_signed(i2400m, bcf);
+ } else {
+ /* non-signed boot process without pokes */
+ d_printf(1, dev, "non-signed boot\n");
+ result = i2400m_dnload_init_nonsigned(i2400m);
if (result == -ERESTARTSYS)
return result;
if (result < 0)
- dev_err(dev, "fw %s: signed boot download "
+ dev_err(dev, "firmware %s: non-signed download "
"initialization failed: %d\n",
i2400m->fw_name, result);
}
@@ -873,74 +1178,201 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf)
/*
- * Run quick consistency tests on the firmware file
+ * Run consistency tests on the firmware file and load up headers
*
* Check for the firmware being made for the i2400m device,
* etc...These checks are mostly informative, as the device will make
* them too; but the driver's response is more informative on what
* went wrong.
+ *
+ * This will also look at all the headers present on the firmware
+ * file, and update i2400m->fw_bcf_hdr to point to them.
*/
static
-int i2400m_fw_check(struct i2400m *i2400m,
- const struct i2400m_bcf_hdr *bcf,
- size_t bcf_size)
+int i2400m_fw_hdr_check(struct i2400m *i2400m,
+ const struct i2400m_bcf_hdr *bcf_hdr,
+ size_t index, size_t offset)
{
- int result;
struct device *dev = i2400m_dev(i2400m);
+
unsigned module_type, header_len, major_version, minor_version,
module_id, module_vendor, date, size;
- /* Check hard errors */
- result = -EINVAL;
- if (bcf_size < sizeof(*bcf)) { /* big enough header? */
- dev_err(dev, "firmware %s too short: "
- "%zu B vs %zu (at least) expected\n",
- i2400m->fw_name, bcf_size, sizeof(*bcf));
- goto error;
- }
+ module_type = bcf_hdr->module_type;
+ header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len);
+ major_version = (le32_to_cpu(bcf_hdr->header_version) & 0xffff0000)
+ >> 16;
+ minor_version = le32_to_cpu(bcf_hdr->header_version) & 0x0000ffff;
+ module_id = le32_to_cpu(bcf_hdr->module_id);
+ module_vendor = le32_to_cpu(bcf_hdr->module_vendor);
+ date = le32_to_cpu(bcf_hdr->date);
+ size = sizeof(u32) * le32_to_cpu(bcf_hdr->size);
- module_type = bcf->module_type;
- header_len = sizeof(u32) * le32_to_cpu(bcf->header_len);
- major_version = le32_to_cpu(bcf->header_version) & 0xffff0000 >> 16;
- minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff;
- module_id = le32_to_cpu(bcf->module_id);
- module_vendor = le32_to_cpu(bcf->module_vendor);
- date = le32_to_cpu(bcf->date);
- size = sizeof(u32) * le32_to_cpu(bcf->size);
-
- if (bcf_size != size) { /* annoyingly paranoid */
- dev_err(dev, "firmware %s: bad size, got "
- "%zu B vs %u expected\n",
- i2400m->fw_name, bcf_size, size);
- goto error;
- }
+ d_printf(1, dev, "firmware %s #%zd@%08zx: BCF header "
+ "type:vendor:id 0x%x:%x:%x v%u.%u (%u/%u B) built %08x\n",
+ i2400m->fw_name, index, offset,
+ module_type, module_vendor, module_id,
+ major_version, minor_version, header_len, size, date);
- d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) "
- "date %08x (%zu B)\n",
- module_type, module_id, module_vendor,
- major_version, minor_version, (size_t) header_len,
- date, (size_t) size);
+ /* Hard errors */
+ if (major_version != 1) {
+ dev_err(dev, "firmware %s #%zd@%08zx: major header version "
+ "v%u.%u not supported\n",
+ i2400m->fw_name, index, offset,
+ major_version, minor_version);
+ return -EBADF;
+ }
if (module_type != 6) { /* built for the right hardware? */
- dev_err(dev, "bad fw %s: unexpected module type 0x%x; "
- "aborting\n", i2400m->fw_name, module_type);
- goto error;
+ dev_err(dev, "firmware %s #%zd@%08zx: unexpected module "
+ "type 0x%x; aborting\n",
+ i2400m->fw_name, index, offset,
+ module_type);
+ return -EBADF;
+ }
+
+ if (module_vendor != 0x8086) {
+ dev_err(dev, "firmware %s #%zd@%08zx: unexpected module "
+ "vendor 0x%x; aborting\n",
+ i2400m->fw_name, index, offset, module_vendor);
+ return -EBADF;
}
- /* Check soft-er errors */
- result = 0;
- if (module_vendor != 0x8086)
- dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n",
- i2400m->fw_name, module_vendor);
if (date < 0x20080300)
- dev_err(dev, "bad fw %s? build date too old %08x\n",
- i2400m->fw_name, date);
-error:
+ dev_warn(dev, "firmware %s #%zd@%08zx: build date %08x "
+ "too old; unsupported\n",
+ i2400m->fw_name, index, offset, date);
+ return 0;
+}
+
+
+/*
+ * Run consistency tests on the firmware file and load up headers
+ *
+ * Check for the firmware being made for the i2400m device,
+ * etc...These checks are mostly informative, as the device will make
+ * them too; but the driver's response is more informative on what
+ * went wrong.
+ *
+ * This will also look at all the headers present on the firmware
+ * file, and update i2400m->fw_hdrs to point to them.
+ */
+static
+int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size)
+{
+ int result;
+ struct device *dev = i2400m_dev(i2400m);
+ size_t headers = 0;
+ const struct i2400m_bcf_hdr *bcf_hdr;
+ const void *itr, *next, *top;
+ size_t slots = 0, used_slots = 0;
+
+ for (itr = bcf, top = itr + bcf_size;
+ itr < top;
+ headers++, itr = next) {
+ size_t leftover, offset, header_len, size;
+
+ leftover = top - itr;
+ offset = itr - (const void *) bcf;
+ if (leftover <= sizeof(*bcf_hdr)) {
+ dev_err(dev, "firmware %s: %zu B left at @%zx, "
+ "not enough for BCF header\n",
+ i2400m->fw_name, leftover, offset);
+ break;
+ }
+ bcf_hdr = itr;
+ /* Only the first header is supposed to be followed by
+ * payload */
+ header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len);
+ size = sizeof(u32) * le32_to_cpu(bcf_hdr->size);
+ if (headers == 0)
+ next = itr + size;
+ else
+ next = itr + header_len;
+
+ result = i2400m_fw_hdr_check(i2400m, bcf_hdr, headers, offset);
+ if (result < 0)
+ continue;
+ if (used_slots + 1 >= slots) {
+ /* +1 -> we need to account for the one we'll
+ * occupy and at least an extra one for
+ * always being NULL */
+ result = i2400m_zrealloc_2x(
+ (void **) &i2400m->fw_hdrs, &slots,
+ sizeof(i2400m->fw_hdrs[0]),
+ GFP_KERNEL);
+ if (result < 0)
+ goto error_zrealloc;
+ }
+ i2400m->fw_hdrs[used_slots] = bcf_hdr;
+ used_slots++;
+ }
+ if (headers == 0) {
+ dev_err(dev, "firmware %s: no usable headers found\n",
+ i2400m->fw_name);
+ result = -EBADF;
+ } else
+ result = 0;
+error_zrealloc:
return result;
}
/*
+ * Match a barker to a BCF header module ID
+ *
+ * The device sends a barker which tells the firmware loader which
+ * header in the BCF file has to be used. This does the matching.
+ */
+static
+unsigned i2400m_bcf_hdr_match(struct i2400m *i2400m,
+ const struct i2400m_bcf_hdr *bcf_hdr)
+{
+ u32 barker = le32_to_cpu(i2400m->barker->data[0])
+ & 0x7fffffff;
+ u32 module_id = le32_to_cpu(bcf_hdr->module_id)
+ & 0x7fffffff; /* high bit used for something else */
+
+ /* special case for 5x50 */
+ if (barker == I2400M_SBOOT_BARKER && module_id == 0)
+ return 1;
+ if (module_id == barker)
+ return 1;
+ return 0;
+}
+
+static
+const struct i2400m_bcf_hdr *i2400m_bcf_hdr_find(struct i2400m *i2400m)
+{
+ struct device *dev = i2400m_dev(i2400m);
+ const struct i2400m_bcf_hdr **bcf_itr, *bcf_hdr;
+ unsigned i = 0;
+ u32 barker = le32_to_cpu(i2400m->barker->data[0]);
+
+ d_printf(2, dev, "finding BCF header for barker %08x\n", barker);
+ if (barker == I2400M_NBOOT_BARKER) {
+ bcf_hdr = i2400m->fw_hdrs[0];
+ d_printf(1, dev, "using BCF header #%u/%08x for non-signed "
+ "barker\n", 0, le32_to_cpu(bcf_hdr->module_id));
+ return bcf_hdr;
+ }
+ for (bcf_itr = i2400m->fw_hdrs; *bcf_itr != NULL; bcf_itr++, i++) {
+ bcf_hdr = *bcf_itr;
+ if (i2400m_bcf_hdr_match(i2400m, bcf_hdr)) {
+ d_printf(1, dev, "hit on BCF hdr #%u/%08x\n",
+ i, le32_to_cpu(bcf_hdr->module_id));
+ return bcf_hdr;
+ } else
+ d_printf(1, dev, "miss on BCF hdr #%u/%08x\n",
+ i, le32_to_cpu(bcf_hdr->module_id));
+ }
+ dev_err(dev, "cannot find a matching BCF header for barker %08x\n",
+ barker);
+ return NULL;
+}
+
+
+/*
* Download the firmware to the device
*
* @i2400m: device descriptor
@@ -956,14 +1388,16 @@ error:
*/
static
int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf,
- size_t bcf_size, enum i2400m_bri flags)
+ size_t fw_size, enum i2400m_bri flags)
{
int ret = 0;
struct device *dev = i2400m_dev(i2400m);
int count = i2400m->bus_bm_retries;
+ const struct i2400m_bcf_hdr *bcf_hdr;
+ size_t bcf_size;
- d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n",
- i2400m, bcf, bcf_size);
+ d_fnstart(5, dev, "(i2400m %p bcf %p fw size %zu)\n",
+ i2400m, bcf, fw_size);
i2400m->boot_mode = 1;
wmb(); /* Make sure other readers see it */
hw_reboot:
@@ -985,13 +1419,28 @@ hw_reboot:
* Initialize the download, push the bytes to the device and
* then jump to the new firmware. Note @ret is passed with the
* offset of the jump instruction to _dnload_finalize()
+ *
+ * Note we need to use the BCF header in the firmware image
+ * that matches the barker that the device sent when it
+ * rebooted, so it has to be passed along.
*/
- ret = i2400m_dnload_init(i2400m, bcf); /* Init device's dnload */
+ ret = -EBADF;
+ bcf_hdr = i2400m_bcf_hdr_find(i2400m);
+ if (bcf_hdr == NULL)
+ goto error_bcf_hdr_find;
+
+ ret = i2400m_dnload_init(i2400m, bcf_hdr);
if (ret == -ERESTARTSYS)
goto error_dev_rebooted;
if (ret < 0)
goto error_dnload_init;
+ /*
+ * bcf_size refers to one header size plus the fw sections size
+ * indicated by the header,ie. if there are other extended headers
+ * at the tail, they are not counted
+ */
+ bcf_size = sizeof(u32) * le32_to_cpu(bcf_hdr->size);
ret = i2400m_dnload_bcf(i2400m, bcf, bcf_size);
if (ret == -ERESTARTSYS)
goto error_dev_rebooted;
@@ -1001,7 +1450,7 @@ hw_reboot:
goto error_dnload_bcf;
}
- ret = i2400m_dnload_finalize(i2400m, bcf, ret);
+ ret = i2400m_dnload_finalize(i2400m, bcf_hdr, bcf, ret);
if (ret == -ERESTARTSYS)
goto error_dev_rebooted;
if (ret < 0) {
@@ -1018,10 +1467,11 @@ hw_reboot:
error_dnload_finalize:
error_dnload_bcf:
error_dnload_init:
+error_bcf_hdr_find:
error_bootrom_init:
error_too_many_reboots:
d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n",
- i2400m, bcf, bcf_size, ret);
+ i2400m, bcf, fw_size, ret);
return ret;
error_dev_rebooted:
@@ -1031,6 +1481,61 @@ error_dev_rebooted:
goto hw_reboot;
}
+static
+int i2400m_fw_bootstrap(struct i2400m *i2400m, const struct firmware *fw,
+ enum i2400m_bri flags)
+{
+ int ret;
+ struct device *dev = i2400m_dev(i2400m);
+ const struct i2400m_bcf_hdr *bcf; /* Firmware data */
+
+ d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
+ bcf = (void *) fw->data;
+ ret = i2400m_fw_check(i2400m, bcf, fw->size);
+ if (ret >= 0)
+ ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
+ if (ret < 0)
+ dev_err(dev, "%s: cannot use: %d, skipping\n",
+ i2400m->fw_name, ret);
+ kfree(i2400m->fw_hdrs);
+ i2400m->fw_hdrs = NULL;
+ d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
+ return ret;
+}
+
+
+/* Refcounted container for firmware data */
+struct i2400m_fw {
+ struct kref kref;
+ const struct firmware *fw;
+};
+
+
+static
+void i2400m_fw_destroy(struct kref *kref)
+{
+ struct i2400m_fw *i2400m_fw =
+ container_of(kref, struct i2400m_fw, kref);
+ release_firmware(i2400m_fw->fw);
+ kfree(i2400m_fw);
+}
+
+
+static
+struct i2400m_fw *i2400m_fw_get(struct i2400m_fw *i2400m_fw)
+{
+ if (i2400m_fw != NULL && i2400m_fw != (void *) ~0)
+ kref_get(&i2400m_fw->kref);
+ return i2400m_fw;
+}
+
+
+static
+void i2400m_fw_put(struct i2400m_fw *i2400m_fw)
+{
+ kref_put(&i2400m_fw->kref, i2400m_fw_destroy);
+}
+
/**
* i2400m_dev_bootstrap - Bring the device to a known state and upload firmware
@@ -1049,42 +1554,109 @@ error_dev_rebooted:
*/
int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
{
- int ret = 0, itr = 0;
+ int ret, itr;
struct device *dev = i2400m_dev(i2400m);
- const struct firmware *fw;
+ struct i2400m_fw *i2400m_fw;
const struct i2400m_bcf_hdr *bcf; /* Firmware data */
+ const struct firmware *fw;
const char *fw_name;
d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
+ ret = -ENODEV;
+ spin_lock(&i2400m->rx_lock);
+ i2400m_fw = i2400m_fw_get(i2400m->fw_cached);
+ spin_unlock(&i2400m->rx_lock);
+ if (i2400m_fw == (void *) ~0) {
+ dev_err(dev, "can't load firmware now!");
+ goto out;
+ } else if (i2400m_fw != NULL) {
+ dev_info(dev, "firmware %s: loading from cache\n",
+ i2400m->fw_name);
+ ret = i2400m_fw_bootstrap(i2400m, i2400m_fw->fw, flags);
+ i2400m_fw_put(i2400m_fw);
+ goto out;
+ }
+
/* Load firmware files to memory. */
- itr = 0;
- while(1) {
+ for (itr = 0, bcf = NULL, ret = -ENOENT; ; itr++) {
fw_name = i2400m->bus_fw_names[itr];
if (fw_name == NULL) {
dev_err(dev, "Could not find a usable firmware image\n");
- ret = -ENOENT;
- goto error_no_fw;
+ break;
}
+ d_printf(1, dev, "trying firmware %s (%d)\n", fw_name, itr);
ret = request_firmware(&fw, fw_name, dev);
- if (ret == 0)
- break; /* got it */
- if (ret < 0)
+ if (ret < 0) {
dev_err(dev, "fw %s: cannot load file: %d\n",
fw_name, ret);
- itr++;
+ continue;
+ }
+ i2400m->fw_name = fw_name;
+ ret = i2400m_fw_bootstrap(i2400m, fw, flags);
+ release_firmware(fw);
+ if (ret >= 0) /* firmware loaded succesfully */
+ break;
+ i2400m->fw_name = NULL;
}
-
- bcf = (void *) fw->data;
- i2400m->fw_name = fw_name;
- ret = i2400m_fw_check(i2400m, bcf, fw->size);
- if (ret < 0)
- goto error_fw_bad;
- ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
-error_fw_bad:
- release_firmware(fw);
-error_no_fw:
+out:
d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
return ret;
}
EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap);
+
+
+void i2400m_fw_cache(struct i2400m *i2400m)
+{
+ int result;
+ struct i2400m_fw *i2400m_fw;
+ struct device *dev = i2400m_dev(i2400m);
+
+ /* if there is anything there, free it -- now, this'd be weird */
+ spin_lock(&i2400m->rx_lock);
+ i2400m_fw = i2400m->fw_cached;
+ spin_unlock(&i2400m->rx_lock);
+ if (i2400m_fw != NULL && i2400m_fw != (void *) ~0) {
+ i2400m_fw_put(i2400m_fw);
+ WARN(1, "%s:%u: still cached fw still present?\n",
+ __func__, __LINE__);
+ }
+
+ if (i2400m->fw_name == NULL) {
+ dev_err(dev, "firmware n/a: can't cache\n");
+ i2400m_fw = (void *) ~0;
+ goto out;
+ }
+
+ i2400m_fw = kzalloc(sizeof(*i2400m_fw), GFP_ATOMIC);
+ if (i2400m_fw == NULL)
+ goto out;
+ kref_init(&i2400m_fw->kref);
+ result = request_firmware(&i2400m_fw->fw, i2400m->fw_name, dev);
+ if (result < 0) {
+ dev_err(dev, "firmware %s: failed to cache: %d\n",
+ i2400m->fw_name, result);
+ kfree(i2400m_fw);
+ i2400m_fw = (void *) ~0;
+ } else
+ dev_info(dev, "firmware %s: cached\n", i2400m->fw_name);
+out:
+ spin_lock(&i2400m->rx_lock);
+ i2400m->fw_cached = i2400m_fw;
+ spin_unlock(&i2400m->rx_lock);
+}
+
+
+void i2400m_fw_uncache(struct i2400m *i2400m)
+{
+ struct i2400m_fw *i2400m_fw;
+
+ spin_lock(&i2400m->rx_lock);
+ i2400m_fw = i2400m->fw_cached;
+ i2400m->fw_cached = NULL;
+ spin_unlock(&i2400m->rx_lock);
+
+ if (i2400m_fw != NULL && i2400m_fw != (void *) ~0)
+ i2400m_fw_put(i2400m_fw);
+}
+
diff --git a/drivers/net/wimax/i2400m/i2400m-sdio.h b/drivers/net/wimax/i2400m/i2400m-sdio.h
index 9c4e3189f7b..b9c4bed3b45 100644
--- a/drivers/net/wimax/i2400m/i2400m-sdio.h
+++ b/drivers/net/wimax/i2400m/i2400m-sdio.h
@@ -67,6 +67,7 @@
/* Host-Device interface for SDIO */
enum {
+ I2400M_SDIO_BOOT_RETRIES = 3,
I2400MS_BLK_SIZE = 256,
I2400MS_PL_SIZE_MAX = 0x3E00,
@@ -77,9 +78,11 @@ enum {
I2400MS_INTR_GET_SIZE_ADDR = 0x2C,
/* The number of ticks to wait for the device to signal that
* it is ready */
- I2400MS_INIT_SLEEP_INTERVAL = 10,
+ I2400MS_INIT_SLEEP_INTERVAL = 100,
/* How long to wait for the device to settle after reset */
I2400MS_SETTLE_TIME = 40,
+ /* The number of msec to wait for IOR after sending IOE */
+ IWMC3200_IOR_TIMEOUT = 10,
};
@@ -97,6 +100,14 @@ enum {
* @tx_workqueue: workqeueue used for data TX; we don't use the
* system's workqueue as that might cause deadlocks with code in
* the bus-generic driver.
+ *
+ * @debugfs_dentry: dentry for the SDIO specific debugfs files
+ *
+ * Note this value is set to NULL upon destruction; this is
+ * because some routinges use it to determine if we are inside the
+ * probe() path or some other path. When debugfs is disabled,
+ * creation sets the dentry to '(void*) -ENODEV', which is valid
+ * for the test.
*/
struct i2400ms {
struct i2400m i2400m; /* FIRST! See doc */
@@ -111,6 +122,9 @@ struct i2400ms {
wait_queue_head_t bm_wfa_wq;
int bm_wait_result;
size_t bm_ack_size;
+
+ /* Device is any of the iwmc3200 SKUs */
+ unsigned iwmc3200:1;
};
diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h
index 6f76558b170..5cc0f279417 100644
--- a/drivers/net/wimax/i2400m/i2400m-usb.h
+++ b/drivers/net/wimax/i2400m/i2400m-usb.h
@@ -88,6 +88,13 @@ struct edc {
u16 errorcount;
};
+struct i2400m_endpoint_cfg {
+ unsigned char bulk_out;
+ unsigned char notification;
+ unsigned char reset_cold;
+ unsigned char bulk_in;
+};
+
static inline void edc_init(struct edc *edc)
{
edc->timestart = jiffies;
@@ -137,15 +144,13 @@ static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe)
/* Host-Device interface for USB */
enum {
+ I2400M_USB_BOOT_RETRIES = 3,
I2400MU_MAX_NOTIFICATION_LEN = 256,
I2400MU_BLK_SIZE = 16,
I2400MU_PL_SIZE_MAX = 0x3EFF,
- /* Endpoints */
- I2400MU_EP_BULK_OUT = 0,
- I2400MU_EP_NOTIFICATION,
- I2400MU_EP_RESET_COLD,
- I2400MU_EP_BULK_IN,
+ /* Device IDs */
+ USB_DEVICE_ID_I6050 = 0x0186,
};
@@ -215,6 +220,7 @@ struct i2400mu {
struct usb_device *usb_dev;
struct usb_interface *usb_iface;
struct edc urb_edc; /* Error density counter */
+ struct i2400m_endpoint_cfg endpoint_cfg;
struct urb *notif_urb;
struct task_struct *tx_kthread;
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index 60330f313f2..04df9bbe340 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -117,16 +117,30 @@
* well as i2400m->wimax_dev.net_dev and call i2400m_setup(). The
* i2400m driver will only register with the WiMAX and network stacks;
* the only access done to the device is to read the MAC address so we
- * can register a network device. This calls i2400m_dev_start() to
- * load firmware, setup communication with the device and configure it
- * for operation.
+ * can register a network device.
*
- * At this point, control and data communications are possible.
+ * The high-level call flow is:
+ *
+ * bus_probe()
+ * i2400m_setup()
+ * i2400m->bus_setup()
+ * boot rom initialization / read mac addr
+ * network / WiMAX stacks registration
+ * i2400m_dev_start()
+ * i2400m->bus_dev_start()
+ * i2400m_dev_initialize()
*
- * On disconnect/driver unload, the bus-specific disconnect function
- * calls i2400m_release() to undo i2400m_setup(). i2400m_dev_stop()
- * shuts the firmware down and releases resources uses to communicate
- * with the device.
+ * The reverse applies for a disconnect() call:
+ *
+ * bus_disconnect()
+ * i2400m_release()
+ * i2400m_dev_stop()
+ * i2400m_dev_shutdown()
+ * i2400m->bus_dev_stop()
+ * network / WiMAX stack unregistration
+ * i2400m->bus_release()
+ *
+ * At this point, control and data communications are possible.
*
* While the device is up, it might reset. The bus-specific driver has
* to catch that situation and call i2400m_dev_reset_handle() to deal
@@ -148,9 +162,6 @@
/* Misc constants */
enum {
- /* Firmware uploading */
- I2400M_BOOT_RETRIES = 3,
- I3200_BOOT_RETRIES = 3,
/* Size of the Boot Mode Command buffer */
I2400M_BM_CMD_BUF_SIZE = 16 * 1024,
I2400M_BM_ACK_BUF_SIZE = 256,
@@ -197,6 +208,7 @@ enum i2400m_reset_type {
struct i2400m_reset_ctx;
struct i2400m_roq;
+struct i2400m_barker_db;
/**
* struct i2400m - descriptor for an Intel 2400m
@@ -204,27 +216,50 @@ struct i2400m_roq;
* Members marked with [fill] must be filled out/initialized before
* calling i2400m_setup().
*
+ * Note the @bus_setup/@bus_release, @bus_dev_start/@bus_dev_release
+ * call pairs are very much doing almost the same, and depending on
+ * the underlying bus, some stuff has to be put in one or the
+ * other. The idea of setup/release is that they setup the minimal
+ * amount needed for loading firmware, where us dev_start/stop setup
+ * the rest needed to do full data/control traffic.
+ *
* @bus_tx_block_size: [fill] SDIO imposes a 256 block size, USB 16,
* so we have a tx_blk_size variable that the bus layer sets to
* tell the engine how much of that we need.
*
* @bus_pl_size_max: [fill] Maximum payload size.
*
- * @bus_dev_start: [fill] Function called by the bus-generic code
- * [i2400m_dev_start()] to setup the bus-specific communications
- * to the the device. See LIFE CYCLE above.
+ * @bus_setup: [optional fill] Function called by the bus-generic code
+ * [i2400m_setup()] to setup the basic bus-specific communications
+ * to the the device needed to load firmware. See LIFE CYCLE above.
*
* NOTE: Doesn't need to upload the firmware, as that is taken
* care of by the bus-generic code.
*
- * @bus_dev_stop: [fill] Function called by the bus-generic code
- * [i2400m_dev_stop()] to shutdown the bus-specific communications
- * to the the device. See LIFE CYCLE above.
+ * @bus_release: [optional fill] Function called by the bus-generic
+ * code [i2400m_release()] to shutdown the basic bus-specific
+ * communications to the the device needed to load firmware. See
+ * LIFE CYCLE above.
*
* This function does not need to reset the device, just tear down
* all the host resources created to handle communication with
* the device.
*
+ * @bus_dev_start: [optional fill] Function called by the bus-generic
+ * code [i2400m_dev_start()] to do things needed to start the
+ * device. See LIFE CYCLE above.
+ *
+ * NOTE: Doesn't need to upload the firmware, as that is taken
+ * care of by the bus-generic code.
+ *
+ * @bus_dev_stop: [optional fill] Function called by the bus-generic
+ * code [i2400m_dev_stop()] to do things needed for stopping the
+ * device. See LIFE CYCLE above.
+ *
+ * This function does not need to reset the device, just tear down
+ * all the host resources created to handle communication with
+ * the device.
+ *
* @bus_tx_kick: [fill] Function called by the bus-generic code to let
* the bus-specific code know that there is data available in the
* TX FIFO for transmission to the device.
@@ -246,6 +281,9 @@ struct i2400m_roq;
* process, so it cannot rely on common infrastructure being laid
* out.
*
+ * IMPORTANT: don't call reset on RT_BUS with i2400m->init_mutex
+ * held, as the .pre/.post reset handlers will deadlock.
+ *
* @bus_bm_retries: [fill] How many times shall a firmware upload /
* device initialization be retried? Different models of the same
* device might need different values, hence it is set by the
@@ -297,6 +335,27 @@ struct i2400m_roq;
* force this to be the first field so that we can get from
* netdev_priv() the right pointer.
*
+ * @updown: the device is up and ready for transmitting control and
+ * data packets. This implies @ready (communication infrastructure
+ * with the device is ready) and the device's firmware has been
+ * loaded and the device initialized.
+ *
+ * Write to it only inside a i2400m->init_mutex protected area
+ * followed with a wmb(); rmb() before accesing (unless locked
+ * inside i2400m->init_mutex). Read access can be loose like that
+ * [just using rmb()] because the paths that use this also do
+ * other error checks later on.
+ *
+ * @ready: Communication infrastructure with the device is ready, data
+ * frames can start to be passed around (this is lighter than
+ * using the WiMAX state for certain hot paths).
+ *
+ * Write to it only inside a i2400m->init_mutex protected area
+ * followed with a wmb(); rmb() before accesing (unless locked
+ * inside i2400m->init_mutex). Read access can be loose like that
+ * [just using rmb()] because the paths that use this also do
+ * other error checks later on.
+ *
* @rx_reorder: 1 if RX reordering is enabled; this can only be
* set at probe time.
*
@@ -362,6 +421,13 @@ struct i2400m_roq;
* delivered. Then the driver can release them to the host. See
* drivers/net/i2400m/rx.c for details.
*
+ * @rx_reports: reports received from the device that couldn't be
+ * processed because the driver wasn't still ready; when ready,
+ * they are pulled from here and chewed.
+ *
+ * @rx_reports_ws: Work struct used to kick a scan of the RX reports
+ * list and to process each.
+ *
* @src_mac_addr: MAC address used to make ethernet packets be coming
* from. This is generated at i2400m_setup() time and used during
* the life cycle of the instance. See i2400m_fake_eth_header().
@@ -422,6 +488,25 @@ struct i2400m_roq;
*
* @fw_version: version of the firmware interface, Major.minor,
* encoded in the high word and low word (major << 16 | minor).
+ *
+ * @fw_hdrs: NULL terminated array of pointers to the firmware
+ * headers. This is only available during firmware load time.
+ *
+ * @fw_cached: Used to cache firmware when the system goes to
+ * suspend/standby/hibernation (as on resume we can't read it). If
+ * NULL, no firmware was cached, read it. If ~0, you can't read
+ * any firmware files (the system still didn't come out of suspend
+ * and failed to cache one), so abort; otherwise, a valid cached
+ * firmware to be used. Access to this variable is protected by
+ * the spinlock i2400m->rx_lock.
+ *
+ * @barker: barker type that the device uses; this is initialized by
+ * i2400m_is_boot_barker() the first time it is called. Then it
+ * won't change during the life cycle of the device and everytime
+ * a boot barker is received, it is just verified for it being the
+ * same.
+ *
+ * @pm_notifier: used to register for PM events
*/
struct i2400m {
struct wimax_dev wimax_dev; /* FIRST! See doc */
@@ -429,7 +514,7 @@ struct i2400m {
unsigned updown:1; /* Network device is up or down */
unsigned boot_mode:1; /* is the device in boot mode? */
unsigned sboot:1; /* signed or unsigned fw boot */
- unsigned ready:1; /* all probing steps done */
+ unsigned ready:1; /* Device comm infrastructure ready */
unsigned rx_reorder:1; /* RX reorder is enabled */
u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */
/* typed u8 so /sys/kernel/debug/u8 can tweak */
@@ -440,8 +525,10 @@ struct i2400m {
size_t bus_pl_size_max;
unsigned bus_bm_retries;
+ int (*bus_setup)(struct i2400m *);
int (*bus_dev_start)(struct i2400m *);
void (*bus_dev_stop)(struct i2400m *);
+ void (*bus_release)(struct i2400m *);
void (*bus_tx_kick)(struct i2400m *);
int (*bus_reset)(struct i2400m *, enum i2400m_reset_type);
ssize_t (*bus_bm_cmd_send)(struct i2400m *,
@@ -468,6 +555,8 @@ struct i2400m {
rx_num, rx_size_acc, rx_size_min, rx_size_max;
struct i2400m_roq *rx_roq; /* not under rx_lock! */
u8 src_mac_addr[ETH_HLEN];
+ struct list_head rx_reports; /* under rx_lock! */
+ struct work_struct rx_report_ws;
struct mutex msg_mutex; /* serialize command execution */
struct completion msg_completion;
@@ -487,37 +576,12 @@ struct i2400m {
struct dentry *debugfs_dentry;
const char *fw_name; /* name of the current firmware image */
unsigned long fw_version; /* version of the firmware interface */
-};
-
+ const struct i2400m_bcf_hdr **fw_hdrs;
+ struct i2400m_fw *fw_cached; /* protected by rx_lock */
+ struct i2400m_barker_db *barker;
-/*
- * Initialize a 'struct i2400m' from all zeroes
- *
- * This is a bus-generic API call.
- */
-static inline
-void i2400m_init(struct i2400m *i2400m)
-{
- wimax_dev_init(&i2400m->wimax_dev);
-
- i2400m->boot_mode = 1;
- i2400m->rx_reorder = 1;
- init_waitqueue_head(&i2400m->state_wq);
-
- spin_lock_init(&i2400m->tx_lock);
- i2400m->tx_pl_min = UINT_MAX;
- i2400m->tx_size_min = UINT_MAX;
-
- spin_lock_init(&i2400m->rx_lock);
- i2400m->rx_pl_min = UINT_MAX;
- i2400m->rx_size_min = UINT_MAX;
-
- mutex_init(&i2400m->msg_mutex);
- init_completion(&i2400m->msg_completion);
-
- mutex_init(&i2400m->init_mutex);
- /* wake_tx_ws is initialized in i2400m_tx_setup() */
-}
+ struct notifier_block pm_notifier;
+};
/*
@@ -577,6 +641,14 @@ extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *);
extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri);
extern int i2400m_read_mac_addr(struct i2400m *);
extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri);
+extern int i2400m_is_boot_barker(struct i2400m *, const void *, size_t);
+static inline
+int i2400m_is_d2h_barker(const void *buf)
+{
+ const __le32 *barker = buf;
+ return le32_to_cpu(*barker) == I2400M_D2H_MSG_BARKER;
+}
+extern void i2400m_unknown_barker(struct i2400m *, const void *, size_t);
/* Make/grok boot-rom header commands */
@@ -644,6 +716,8 @@ unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr)
/*
* Driver / device setup and internal functions
*/
+extern void i2400m_init(struct i2400m *);
+extern int i2400m_reset(struct i2400m *, enum i2400m_reset_type);
extern void i2400m_netdev_setup(struct net_device *net_dev);
extern int i2400m_sysfs_setup(struct device_driver *);
extern void i2400m_sysfs_release(struct device_driver *);
@@ -654,10 +728,14 @@ extern void i2400m_tx_release(struct i2400m *);
extern int i2400m_rx_setup(struct i2400m *);
extern void i2400m_rx_release(struct i2400m *);
+extern void i2400m_fw_cache(struct i2400m *);
+extern void i2400m_fw_uncache(struct i2400m *);
+
extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned,
const void *, int);
extern void i2400m_net_erx(struct i2400m *, struct sk_buff *,
enum i2400m_cs);
+extern void i2400m_net_wake_stop(struct i2400m *);
enum i2400m_pt;
extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt);
@@ -672,14 +750,12 @@ static inline int i2400m_debugfs_add(struct i2400m *i2400m)
static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {}
#endif
-/* Called by _dev_start()/_dev_stop() to initialize the device itself */
+/* Initialize/shutdown the device */
extern int i2400m_dev_initialize(struct i2400m *);
extern void i2400m_dev_shutdown(struct i2400m *);
extern struct attribute_group i2400m_dev_attr_group;
-extern int i2400m_schedule_work(struct i2400m *,
- void (*)(struct work_struct *), gfp_t);
/* HDI message's payload description handling */
@@ -724,7 +800,9 @@ void i2400m_put(struct i2400m *i2400m)
dev_put(i2400m->wimax_dev.net_dev);
}
-extern int i2400m_dev_reset_handle(struct i2400m *);
+extern int i2400m_dev_reset_handle(struct i2400m *, const char *);
+extern int i2400m_pre_reset(struct i2400m *);
+extern int i2400m_post_reset(struct i2400m *);
/*
* _setup()/_release() are called by the probe/disconnect functions of
@@ -737,20 +815,6 @@ extern int i2400m_rx(struct i2400m *, struct sk_buff *);
extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *);
extern void i2400m_tx_msg_sent(struct i2400m *);
-static const __le32 i2400m_NBOOT_BARKER[4] = {
- cpu_to_le32(I2400M_NBOOT_BARKER),
- cpu_to_le32(I2400M_NBOOT_BARKER),
- cpu_to_le32(I2400M_NBOOT_BARKER),
- cpu_to_le32(I2400M_NBOOT_BARKER)
-};
-
-static const __le32 i2400m_SBOOT_BARKER[4] = {
- cpu_to_le32(I2400M_SBOOT_BARKER),
- cpu_to_le32(I2400M_SBOOT_BARKER),
- cpu_to_le32(I2400M_SBOOT_BARKER),
- cpu_to_le32(I2400M_SBOOT_BARKER)
-};
-
extern int i2400m_power_save_disabled;
/*
@@ -773,10 +837,12 @@ struct device *i2400m_dev(struct i2400m *i2400m)
struct i2400m_work {
struct work_struct ws;
struct i2400m *i2400m;
+ size_t pl_size;
u8 pl[0];
};
-extern int i2400m_queue_work(struct i2400m *,
- void (*)(struct work_struct *), gfp_t,
+
+extern int i2400m_schedule_work(struct i2400m *,
+ void (*)(struct work_struct *), gfp_t,
const void *, size_t);
extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *,
@@ -789,6 +855,7 @@ extern void i2400m_msg_ack_hook(struct i2400m *,
const struct i2400m_l3l4_hdr *, size_t);
extern void i2400m_report_hook(struct i2400m *,
const struct i2400m_l3l4_hdr *, size_t);
+extern void i2400m_report_hook_work(struct work_struct *);
extern int i2400m_cmd_enter_powersave(struct i2400m *);
extern int i2400m_cmd_get_state(struct i2400m *);
extern int i2400m_cmd_exit_idle(struct i2400m *);
@@ -849,6 +916,12 @@ void __i2400m_msleep(unsigned ms)
#endif
}
+
+/* module initialization helpers */
+extern int i2400m_barker_db_init(const char *);
+extern void i2400m_barker_db_exit(void);
+
+
/* Module parameters */
extern int i2400m_idle_mode_disabled;
diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c
index 796396cb4c8..599aa4eb9ba 100644
--- a/drivers/net/wimax/i2400m/netdev.c
+++ b/drivers/net/wimax/i2400m/netdev.c
@@ -74,6 +74,7 @@
*/
#include <linux/if_arp.h>
#include <linux/netdevice.h>
+#include <linux/ethtool.h>
#include "i2400m.h"
@@ -88,7 +89,10 @@ enum {
* The MTU is 1400 or less
*/
I2400M_MAX_MTU = 1400,
- I2400M_TX_TIMEOUT = HZ,
+ /* 20 secs? yep, this is the maximum timeout that the device
+ * might take to get out of IDLE / negotiate it with the base
+ * station. We add 1sec for good measure. */
+ I2400M_TX_TIMEOUT = 21 * HZ,
I2400M_TX_QLEN = 5,
};
@@ -101,22 +105,19 @@ int i2400m_open(struct net_device *net_dev)
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m);
- if (i2400m->ready == 0) {
- dev_err(dev, "Device is still initializing\n");
- result = -EBUSY;
- } else
+ /* Make sure we wait until init is complete... */
+ mutex_lock(&i2400m->init_mutex);
+ if (i2400m->updown)
result = 0;
+ else
+ result = -EBUSY;
+ mutex_unlock(&i2400m->init_mutex);
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
net_dev, i2400m, result);
return result;
}
-/*
- *
- * On kernel versions where cancel_work_sync() didn't return anything,
- * we rely on wake_tx_skb() being non-NULL.
- */
static
int i2400m_stop(struct net_device *net_dev)
{
@@ -124,21 +125,7 @@ int i2400m_stop(struct net_device *net_dev)
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m);
- /* See i2400m_hard_start_xmit(), references are taken there
- * and here we release them if the work was still
- * pending. Note we can't differentiate work not pending vs
- * never scheduled, so the NULL check does that. */
- if (cancel_work_sync(&i2400m->wake_tx_ws) == 0
- && i2400m->wake_tx_skb != NULL) {
- unsigned long flags;
- struct sk_buff *wake_tx_skb;
- spin_lock_irqsave(&i2400m->tx_lock, flags);
- wake_tx_skb = i2400m->wake_tx_skb; /* compat help */
- i2400m->wake_tx_skb = NULL; /* compat help */
- spin_unlock_irqrestore(&i2400m->tx_lock, flags);
- i2400m_put(i2400m);
- kfree_skb(wake_tx_skb);
- }
+ i2400m_net_wake_stop(i2400m);
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = 0\n", net_dev, i2400m);
return 0;
}
@@ -167,6 +154,7 @@ void i2400m_wake_tx_work(struct work_struct *ws)
{
int result;
struct i2400m *i2400m = container_of(ws, struct i2400m, wake_tx_ws);
+ struct net_device *net_dev = i2400m->wimax_dev.net_dev;
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *skb = i2400m->wake_tx_skb;
unsigned long flags;
@@ -182,27 +170,36 @@ void i2400m_wake_tx_work(struct work_struct *ws)
dev_err(dev, "WAKE&TX: skb dissapeared!\n");
goto out_put;
}
+ /* If we have, somehow, lost the connection after this was
+ * queued, don't do anything; this might be the device got
+ * reset or just disconnected. */
+ if (unlikely(!netif_carrier_ok(net_dev)))
+ goto out_kfree;
result = i2400m_cmd_exit_idle(i2400m);
if (result == -EILSEQ)
result = 0;
if (result < 0) {
dev_err(dev, "WAKE&TX: device didn't get out of idle: "
- "%d\n", result);
- goto error;
+ "%d - resetting\n", result);
+ i2400m_reset(i2400m, I2400M_RT_BUS);
+ goto error;
}
result = wait_event_timeout(i2400m->state_wq,
- i2400m->state != I2400M_SS_IDLE, 5 * HZ);
+ i2400m->state != I2400M_SS_IDLE,
+ net_dev->watchdog_timeo - HZ/2);
if (result == 0)
result = -ETIMEDOUT;
if (result < 0) {
dev_err(dev, "WAKE&TX: error waiting for device to exit IDLE: "
- "%d\n", result);
+ "%d - resetting\n", result);
+ i2400m_reset(i2400m, I2400M_RT_BUS);
goto error;
}
msleep(20); /* device still needs some time or it drops it */
result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA);
- netif_wake_queue(i2400m->wimax_dev.net_dev);
error:
+ netif_wake_queue(net_dev);
+out_kfree:
kfree_skb(skb); /* refcount transferred by _hard_start_xmit() */
out_put:
i2400m_put(i2400m);
@@ -229,6 +226,38 @@ void i2400m_tx_prep_header(struct sk_buff *skb)
}
+
+/*
+ * Cleanup resources acquired during i2400m_net_wake_tx()
+ *
+ * This is called by __i2400m_dev_stop and means we have to make sure
+ * the workqueue is flushed from any pending work.
+ */
+void i2400m_net_wake_stop(struct i2400m *i2400m)
+{
+ struct device *dev = i2400m_dev(i2400m);
+
+ d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
+ /* See i2400m_hard_start_xmit(), references are taken there
+ * and here we release them if the work was still
+ * pending. Note we can't differentiate work not pending vs
+ * never scheduled, so the NULL check does that. */
+ if (cancel_work_sync(&i2400m->wake_tx_ws) == 0
+ && i2400m->wake_tx_skb != NULL) {
+ unsigned long flags;
+ struct sk_buff *wake_tx_skb;
+ spin_lock_irqsave(&i2400m->tx_lock, flags);
+ wake_tx_skb = i2400m->wake_tx_skb; /* compat help */
+ i2400m->wake_tx_skb = NULL; /* compat help */
+ spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+ i2400m_put(i2400m);
+ kfree_skb(wake_tx_skb);
+ }
+ d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
+ return;
+}
+
+
/*
* TX an skb to an idle device
*
@@ -342,6 +371,20 @@ netdev_tx_t i2400m_hard_start_xmit(struct sk_buff *skb,
int result;
d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev);
+ if (skb_header_cloned(skb)) {
+ /*
+ * Make tcpdump/wireshark happy -- if they are
+ * running, the skb is cloned and we will overwrite
+ * the mac fields in i2400m_tx_prep_header. Expand
+ * seems to fix this...
+ */
+ result = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (result) {
+ result = NETDEV_TX_BUSY;
+ goto error_expand;
+ }
+ }
+
if (i2400m->state == I2400M_SS_IDLE)
result = i2400m_net_wake_tx(i2400m, net_dev, skb);
else
@@ -352,10 +395,11 @@ netdev_tx_t i2400m_hard_start_xmit(struct sk_buff *skb,
net_dev->stats.tx_packets++;
net_dev->stats.tx_bytes += skb->len;
}
+ result = NETDEV_TX_OK;
+error_expand:
kfree_skb(skb);
-
- d_fnend(3, dev, "(skb %p net_dev %p)\n", skb, net_dev);
- return NETDEV_TX_OK;
+ d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result);
+ return result;
}
@@ -559,6 +603,22 @@ static const struct net_device_ops i2400m_netdev_ops = {
.ndo_change_mtu = i2400m_change_mtu,
};
+static void i2400m_get_drvinfo(struct net_device *net_dev,
+ struct ethtool_drvinfo *info)
+{
+ struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
+
+ strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver) - 1);
+ strncpy(info->fw_version, i2400m->fw_name, sizeof(info->fw_version) - 1);
+ if (net_dev->dev.parent)
+ strncpy(info->bus_info, dev_name(net_dev->dev.parent),
+ sizeof(info->bus_info) - 1);
+}
+
+static const struct ethtool_ops i2400m_ethtool_ops = {
+ .get_drvinfo = i2400m_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
/**
* i2400m_netdev_setup - Setup setup @net_dev's i2400m private data
@@ -580,6 +640,7 @@ void i2400m_netdev_setup(struct net_device *net_dev)
& ~IFF_MULTICAST);
net_dev->watchdog_timeo = I2400M_TX_TIMEOUT;
net_dev->netdev_ops = &i2400m_netdev_ops;
+ net_dev->ethtool_ops = &i2400m_ethtool_ops;
d_fnend(3, NULL, "(net_dev %p) = void\n", net_dev);
}
EXPORT_SYMBOL_GPL(i2400m_netdev_setup);
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c
index 07c32e68909..7ddb173fd4a 100644
--- a/drivers/net/wimax/i2400m/rx.c
+++ b/drivers/net/wimax/i2400m/rx.c
@@ -158,30 +158,104 @@ struct i2400m_report_hook_args {
struct sk_buff *skb_rx;
const struct i2400m_l3l4_hdr *l3l4_hdr;
size_t size;
+ struct list_head list_node;
};
/*
* Execute i2400m_report_hook in a workqueue
*
- * Unpacks arguments from the deferred call, executes it and then
- * drops the references.
+ * Goes over the list of queued reports in i2400m->rx_reports and
+ * processes them.
*
- * Obvious NOTE: References are needed because we are a separate
- * thread; otherwise the buffer changes under us because it is
- * released by the original caller.
+ * NOTE: refcounts on i2400m are not needed because we flush the
+ * workqueue this runs on (i2400m->work_queue) before destroying
+ * i2400m.
*/
-static
void i2400m_report_hook_work(struct work_struct *ws)
{
- struct i2400m_work *iw =
- container_of(ws, struct i2400m_work, ws);
- struct i2400m_report_hook_args *args = (void *) iw->pl;
- if (iw->i2400m->ready)
- i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size);
- kfree_skb(args->skb_rx);
- i2400m_put(iw->i2400m);
- kfree(iw);
+ struct i2400m *i2400m = container_of(ws, struct i2400m, rx_report_ws);
+ struct device *dev = i2400m_dev(i2400m);
+ struct i2400m_report_hook_args *args, *args_next;
+ LIST_HEAD(list);
+ unsigned long flags;
+
+ while (1) {
+ spin_lock_irqsave(&i2400m->rx_lock, flags);
+ list_splice_init(&i2400m->rx_reports, &list);
+ spin_unlock_irqrestore(&i2400m->rx_lock, flags);
+ if (list_empty(&list))
+ break;
+ else
+ d_printf(1, dev, "processing queued reports\n");
+ list_for_each_entry_safe(args, args_next, &list, list_node) {
+ d_printf(2, dev, "processing queued report %p\n", args);
+ i2400m_report_hook(i2400m, args->l3l4_hdr, args->size);
+ kfree_skb(args->skb_rx);
+ list_del(&args->list_node);
+ kfree(args);
+ }
+ }
+}
+
+
+/*
+ * Flush the list of queued reports
+ */
+static
+void i2400m_report_hook_flush(struct i2400m *i2400m)
+{
+ struct device *dev = i2400m_dev(i2400m);
+ struct i2400m_report_hook_args *args, *args_next;
+ LIST_HEAD(list);
+ unsigned long flags;
+
+ d_printf(1, dev, "flushing queued reports\n");
+ spin_lock_irqsave(&i2400m->rx_lock, flags);
+ list_splice_init(&i2400m->rx_reports, &list);
+ spin_unlock_irqrestore(&i2400m->rx_lock, flags);
+ list_for_each_entry_safe(args, args_next, &list, list_node) {
+ d_printf(2, dev, "flushing queued report %p\n", args);
+ kfree_skb(args->skb_rx);
+ list_del(&args->list_node);
+ kfree(args);
+ }
+}
+
+
+/*
+ * Queue a report for later processing
+ *
+ * @i2400m: device descriptor
+ * @skb_rx: skb that contains the payload (for reference counting)
+ * @l3l4_hdr: pointer to the control
+ * @size: size of the message
+ */
+static
+void i2400m_report_hook_queue(struct i2400m *i2400m, struct sk_buff *skb_rx,
+ const void *l3l4_hdr, size_t size)
+{
+ struct device *dev = i2400m_dev(i2400m);
+ unsigned long flags;
+ struct i2400m_report_hook_args *args;
+
+ args = kzalloc(sizeof(*args), GFP_NOIO);
+ if (args) {
+ args->skb_rx = skb_get(skb_rx);
+ args->l3l4_hdr = l3l4_hdr;
+ args->size = size;
+ spin_lock_irqsave(&i2400m->rx_lock, flags);
+ list_add_tail(&args->list_node, &i2400m->rx_reports);
+ spin_unlock_irqrestore(&i2400m->rx_lock, flags);
+ d_printf(2, dev, "queued report %p\n", args);
+ rmb(); /* see i2400m->ready's documentation */
+ if (likely(i2400m->ready)) /* only send if up */
+ queue_work(i2400m->work_queue, &i2400m->rx_report_ws);
+ } else {
+ if (printk_ratelimit())
+ dev_err(dev, "%s:%u: Can't allocate %zu B\n",
+ __func__, __LINE__, sizeof(*args));
+ }
}
@@ -295,21 +369,29 @@ void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx,
msg_type, size);
d_dump(2, dev, l3l4_hdr, size);
if (msg_type & I2400M_MT_REPORT_MASK) {
- /* These hooks have to be ran serialized; as well, the
- * handling might force the execution of commands, and
- * that might cause reentrancy issues with
- * bus-specific subdrivers and workqueues. So we run
- * it in a separate workqueue. */
- struct i2400m_report_hook_args args = {
- .skb_rx = skb_rx,
- .l3l4_hdr = l3l4_hdr,
- .size = size
- };
- if (unlikely(i2400m->ready == 0)) /* only send if up */
- return;
- skb_get(skb_rx);
- i2400m_queue_work(i2400m, i2400m_report_hook_work,
- GFP_KERNEL, &args, sizeof(args));
+ /*
+ * Process each report
+ *
+ * - has to be ran serialized as well
+ *
+ * - the handling might force the execution of
+ * commands. That might cause reentrancy issues with
+ * bus-specific subdrivers and workqueues, so the we
+ * run it in a separate workqueue.
+ *
+ * - when the driver is not yet ready to handle them,
+ * they are queued and at some point the queue is
+ * restarted [NOTE: we can't queue SKBs directly, as
+ * this might be a piece of a SKB, not the whole
+ * thing, and this is cheaper than cloning the
+ * SKB].
+ *
+ * Note we don't do refcounting for the device
+ * structure; this is because before destroying
+ * 'i2400m', we make sure to flush the
+ * i2400m->work_queue, so there are no issues.
+ */
+ i2400m_report_hook_queue(i2400m, skb_rx, l3l4_hdr, size);
if (unlikely(i2400m->trace_msg_from_user))
wimax_msg(&i2400m->wimax_dev, "echo",
l3l4_hdr, size, GFP_KERNEL);
@@ -363,8 +445,6 @@ void i2400m_rx_trace(struct i2400m *i2400m,
msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET",
msg_type, size);
d_dump(2, dev, l3l4_hdr, size);
- if (unlikely(i2400m->ready == 0)) /* only send if up */
- return;
result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL);
if (result < 0)
dev_err(dev, "error sending trace to userspace: %d\n",
@@ -748,7 +828,7 @@ void i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq,
dev_err(dev, "SW BUG? queue nsn %d (lbn %u ws %u)\n",
nsn, lbn, roq->ws);
i2400m_roq_log_dump(i2400m, roq);
- i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+ i2400m_reset(i2400m, I2400M_RT_WARM);
} else {
__i2400m_roq_queue(i2400m, roq, skb, lbn, nsn);
i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET,
@@ -814,7 +894,7 @@ void i2400m_roq_queue_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq,
dev_err(dev, "SW BUG? queue_update_ws nsn %u (sn %u ws %u)\n",
nsn, sn, roq->ws);
i2400m_roq_log_dump(i2400m, roq);
- i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+ i2400m_reset(i2400m, I2400M_RT_WARM);
} else {
/* if the queue is empty, don't bother as we'd queue
* it and inmediately unqueue it -- just deliver it */
@@ -1114,7 +1194,7 @@ error:
* device. See the file header for the format. Run all checks on the
* buffer header, then run over each payload's descriptors, verify
* their consistency and act on each payload's contents. If
- * everything is succesful, update the device's statistics.
+ * everything is successful, update the device's statistics.
*
* Note: You need to set the skb to contain only the length of the
* received buffer; for that, use skb_trim(skb, RECEIVED_SIZE).
@@ -1194,6 +1274,28 @@ error_msg_hdr_check:
EXPORT_SYMBOL_GPL(i2400m_rx);
+void i2400m_unknown_barker(struct i2400m *i2400m,
+ const void *buf, size_t size)
+{
+ struct device *dev = i2400m_dev(i2400m);
+ char prefix[64];
+ const __le32 *barker = buf;
+ dev_err(dev, "RX: HW BUG? unknown barker %08x, "
+ "dropping %zu bytes\n", le32_to_cpu(*barker), size);
+ snprintf(prefix, sizeof(prefix), "%s %s: ",
+ dev_driver_string(dev), dev_name(dev));
+ if (size > 64) {
+ print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
+ 8, 4, buf, 64, 0);
+ printk(KERN_ERR "%s... (only first 64 bytes "
+ "dumped)\n", prefix);
+ } else
+ print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
+ 8, 4, buf, size, 0);
+}
+EXPORT_SYMBOL(i2400m_unknown_barker);
+
+
/*
* Initialize the RX queue and infrastructure
*
@@ -1261,4 +1363,6 @@ void i2400m_rx_release(struct i2400m *i2400m)
kfree(i2400m->rx_roq[0].log);
kfree(i2400m->rx_roq);
}
+ /* at this point, nothing can be received... */
+ i2400m_report_hook_flush(i2400m);
}
diff --git a/drivers/net/wimax/i2400m/sdio-fw.c b/drivers/net/wimax/i2400m/sdio-fw.c
index 7d6ec0f475f..8e025418f5b 100644
--- a/drivers/net/wimax/i2400m/sdio-fw.c
+++ b/drivers/net/wimax/i2400m/sdio-fw.c
@@ -118,7 +118,8 @@ ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m,
if (cmd_size > I2400M_BM_CMD_BUF_SIZE)
goto error_too_big;
- memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); /* Prep command */
+ if (_cmd != i2400m->bm_cmd_buf)
+ memmove(i2400m->bm_cmd_buf, _cmd, cmd_size);
cmd = i2400m->bm_cmd_buf;
if (cmd_size_a > cmd_size) /* Zero pad space */
memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size);
@@ -177,10 +178,6 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n",
i2400m, ack, ack_size);
- spin_lock(&i2400m->rx_lock);
- i2400ms->bm_ack_size = -EINPROGRESS;
- spin_unlock(&i2400m->rx_lock);
-
result = wait_event_timeout(i2400ms->bm_wfa_wq,
i2400ms->bm_ack_size != -EINPROGRESS,
2 * HZ);
@@ -199,6 +196,10 @@ ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
size = min(ack_size, i2400ms->bm_ack_size);
memcpy(ack, i2400m->bm_ack_buf, size);
}
+ /*
+ * Remember always to clear the bm_ack_size to -EINPROGRESS
+ * after the RX data is processed
+ */
i2400ms->bm_ack_size = -EINPROGRESS;
spin_unlock(&i2400m->rx_lock);
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c
index 321beadf6e4..8adf6c9b6f8 100644
--- a/drivers/net/wimax/i2400m/sdio-rx.c
+++ b/drivers/net/wimax/i2400m/sdio-rx.c
@@ -53,6 +53,7 @@
* i2400ms_irq()
* i2400ms_rx()
* __i2400ms_rx_get_size()
+ * i2400m_is_boot_barker()
* i2400m_rx()
*
* i2400ms_rx_setup()
@@ -138,6 +139,11 @@ void i2400ms_rx(struct i2400ms *i2400ms)
ret = rx_size;
goto error_get_size;
}
+ /*
+ * Hardware quirk: make sure to clear the INTR status register
+ * AFTER getting the data transfer size.
+ */
+ sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret);
ret = -ENOMEM;
skb = alloc_skb(rx_size, GFP_ATOMIC);
@@ -153,25 +159,34 @@ void i2400ms_rx(struct i2400ms *i2400ms)
}
rmb(); /* make sure we get boot_mode from dev_reset_handle */
- if (i2400m->boot_mode == 1) {
+ if (unlikely(i2400m->boot_mode == 1)) {
spin_lock(&i2400m->rx_lock);
i2400ms->bm_ack_size = rx_size;
spin_unlock(&i2400m->rx_lock);
memcpy(i2400m->bm_ack_buf, skb->data, rx_size);
wake_up(&i2400ms->bm_wfa_wq);
- dev_err(dev, "RX: SDIO boot mode message\n");
+ d_printf(5, dev, "RX: SDIO boot mode message\n");
kfree_skb(skb);
- } else if (unlikely(!memcmp(skb->data, i2400m_NBOOT_BARKER,
- sizeof(i2400m_NBOOT_BARKER))
- || !memcmp(skb->data, i2400m_SBOOT_BARKER,
- sizeof(i2400m_SBOOT_BARKER)))) {
- ret = i2400m_dev_reset_handle(i2400m);
+ goto out;
+ }
+ ret = -EIO;
+ if (unlikely(rx_size < sizeof(__le32))) {
+ dev_err(dev, "HW BUG? only %zu bytes received\n", rx_size);
+ goto error_bad_size;
+ }
+ if (likely(i2400m_is_d2h_barker(skb->data))) {
+ skb_put(skb, rx_size);
+ i2400m_rx(i2400m, skb);
+ } else if (unlikely(i2400m_is_boot_barker(i2400m,
+ skb->data, rx_size))) {
+ ret = i2400m_dev_reset_handle(i2400m, "device rebooted");
dev_err(dev, "RX: SDIO reboot barker\n");
kfree_skb(skb);
} else {
- skb_put(skb, rx_size);
- i2400m_rx(i2400m, skb);
+ i2400m_unknown_barker(i2400m, skb->data, rx_size);
+ kfree_skb(skb);
}
+out:
d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms);
return;
@@ -179,6 +194,7 @@ error_memcpy_fromio:
kfree_skb(skb);
error_alloc_skb:
error_get_size:
+error_bad_size:
d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret);
return;
}
@@ -209,7 +225,6 @@ void i2400ms_irq(struct sdio_func *func)
dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n");
goto error_no_irq;
}
- sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret);
i2400ms_rx(i2400ms);
error_no_irq:
d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms);
@@ -234,6 +249,13 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms)
init_waitqueue_head(&i2400ms->bm_wfa_wq);
spin_lock(&i2400m->rx_lock);
i2400ms->bm_wait_result = -EINPROGRESS;
+ /*
+ * Before we are about to enable the RX interrupt, make sure
+ * bm_ack_size is cleared to -EINPROGRESS which indicates
+ * no RX interrupt happened yet or the previous interrupt
+ * has been handled, we are ready to take the new interrupt
+ */
+ i2400ms->bm_ack_size = -EINPROGRESS;
spin_unlock(&i2400m->rx_lock);
sdio_claim_host(func);
diff --git a/drivers/net/wimax/i2400m/sdio-tx.c b/drivers/net/wimax/i2400m/sdio-tx.c
index 5105a5ebc44..de66d068c9c 100644
--- a/drivers/net/wimax/i2400m/sdio-tx.c
+++ b/drivers/net/wimax/i2400m/sdio-tx.c
@@ -149,5 +149,8 @@ int i2400ms_tx_setup(struct i2400ms *i2400ms)
void i2400ms_tx_release(struct i2400ms *i2400ms)
{
- destroy_workqueue(i2400ms->tx_workqueue);
+ if (i2400ms->tx_workqueue) {
+ destroy_workqueue(i2400ms->tx_workqueue);
+ i2400ms->tx_workqueue = NULL;
+ }
}
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index 2981e211e04..76a50ac02eb 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -43,18 +43,9 @@
* i2400m_release()
* free_netdev(net_dev)
*
- * i2400ms_bus_reset() Called by i2400m->bus_reset
+ * i2400ms_bus_reset() Called by i2400m_reset
* __i2400ms_reset()
* __i2400ms_send_barker()
- *
- * i2400ms_bus_dev_start() Called by i2400m_dev_start() [who is
- * i2400ms_tx_setup() called by i2400m_setup()]
- * i2400ms_rx_setup()
- *
- * i2400ms_bus_dev_stop() Called by i2400m_dev_stop() [who is
- * i2400ms_rx_release() is called by i2400m_release()]
- * i2400ms_tx_release()
- *
*/
#include <linux/debugfs.h>
@@ -71,6 +62,14 @@
static int ioe_timeout = 2;
module_param(ioe_timeout, int, 0);
+static char i2400ms_debug_params[128];
+module_param_string(debug, i2400ms_debug_params, sizeof(i2400ms_debug_params),
+ 0644);
+MODULE_PARM_DESC(debug,
+ "String of space-separated NAME:VALUE pairs, where NAMEs "
+ "are the different debug submodules and VALUE are the "
+ "initial debug value to set.");
+
/* Our firmware file name list */
static const char *i2400ms_bus_fw_names[] = {
#define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-1.3.sbcf"
@@ -95,17 +94,24 @@ static const struct i2400m_poke_table i2400ms_pokes[] = {
* when we ask it to explicitly doing). Tries until a timeout is
* reached.
*
+ * The @maxtries argument indicates how many times (at most) it should
+ * be tried to enable the function. 0 means forever. This acts along
+ * with the timeout (ie: it'll stop trying as soon as the maximum
+ * number of tries is reached _or_ as soon as the timeout is reached).
+ *
* The reverse of this is...sdio_disable_function()
*
* Returns: 0 if the SDIO function was enabled, < 0 errno code on
* error (-ENODEV when it was unable to enable the function).
*/
static
-int i2400ms_enable_function(struct sdio_func *func)
+int i2400ms_enable_function(struct i2400ms *i2400ms, unsigned maxtries)
{
+ struct sdio_func *func = i2400ms->func;
u64 timeout;
int err;
struct device *dev = &func->dev;
+ unsigned tries = 0;
d_fnstart(3, dev, "(func %p)\n", func);
/* Setup timeout (FIXME: This needs to read the CIS table to
@@ -115,6 +121,14 @@ int i2400ms_enable_function(struct sdio_func *func)
err = -ENODEV;
while (err != 0 && time_before64(get_jiffies_64(), timeout)) {
sdio_claim_host(func);
+ /*
+ * There is a sillicon bug on the IWMC3200, where the
+ * IOE timeout will cause problems on Moorestown
+ * platforms (system hang). We explicitly overwrite
+ * func->enable_timeout here to work around the issue.
+ */
+ if (i2400ms->iwmc3200)
+ func->enable_timeout = IWMC3200_IOR_TIMEOUT;
err = sdio_enable_func(func);
if (0 == err) {
sdio_release_host(func);
@@ -122,8 +136,11 @@ int i2400ms_enable_function(struct sdio_func *func)
goto function_enabled;
}
d_printf(2, dev, "SDIO function failed to enable: %d\n", err);
- sdio_disable_func(func);
sdio_release_host(func);
+ if (maxtries > 0 && ++tries >= maxtries) {
+ err = -ETIME;
+ break;
+ }
msleep(I2400MS_INIT_SLEEP_INTERVAL);
}
/* If timed out, device is not there yet -- get -ENODEV so
@@ -140,46 +157,99 @@ function_enabled:
/*
- * Setup driver resources needed to communicate with the device
+ * Setup minimal device communication infrastructure needed to at
+ * least be able to update the firmware.
*
- * The fw needs some time to settle, and it was just uploaded,
- * so give it a break first. I'd prefer to just wait for the device to
- * send something, but seems the poking we do to enable SDIO stuff
- * interferes with it, so just give it a break before starting...
+ * Note the ugly trick: if we are in the probe path
+ * (i2400ms->debugfs_dentry == NULL), we only retry function
+ * enablement one, to avoid racing with the iwmc3200 top controller.
*/
static
-int i2400ms_bus_dev_start(struct i2400m *i2400m)
+int i2400ms_bus_setup(struct i2400m *i2400m)
{
int result;
- struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
+ struct i2400ms *i2400ms =
+ container_of(i2400m, struct i2400ms, i2400m);
+ struct device *dev = i2400m_dev(i2400m);
struct sdio_func *func = i2400ms->func;
- struct device *dev = &func->dev;
+ int retries;
+
+ sdio_claim_host(func);
+ result = sdio_set_block_size(func, I2400MS_BLK_SIZE);
+ sdio_release_host(func);
+ if (result < 0) {
+ dev_err(dev, "Failed to set block size: %d\n", result);
+ goto error_set_blk_size;
+ }
+
+ if (i2400ms->iwmc3200 && i2400ms->debugfs_dentry == NULL)
+ retries = 1;
+ else
+ retries = 0;
+ result = i2400ms_enable_function(i2400ms, retries);
+ if (result < 0) {
+ dev_err(dev, "Cannot enable SDIO function: %d\n", result);
+ goto error_func_enable;
+ }
- d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
- msleep(200);
result = i2400ms_tx_setup(i2400ms);
if (result < 0)
goto error_tx_setup;
- d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
- return result;
+ result = i2400ms_rx_setup(i2400ms);
+ if (result < 0)
+ goto error_rx_setup;
+ return 0;
-error_tx_setup:
+error_rx_setup:
i2400ms_tx_release(i2400ms);
- d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
+error_tx_setup:
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+error_func_enable:
+error_set_blk_size:
return result;
}
+/*
+ * Tear down minimal device communication infrastructure needed to at
+ * least be able to update the firmware.
+ */
+static
+void i2400ms_bus_release(struct i2400m *i2400m)
+{
+ struct i2400ms *i2400ms =
+ container_of(i2400m, struct i2400ms, i2400m);
+ struct sdio_func *func = i2400ms->func;
+
+ i2400ms_rx_release(i2400ms);
+ i2400ms_tx_release(i2400ms);
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+}
+
+
+/*
+ * Setup driver resources needed to communicate with the device
+ *
+ * The fw needs some time to settle, and it was just uploaded,
+ * so give it a break first. I'd prefer to just wait for the device to
+ * send something, but seems the poking we do to enable SDIO stuff
+ * interferes with it, so just give it a break before starting...
+ */
static
-void i2400ms_bus_dev_stop(struct i2400m *i2400m)
+int i2400ms_bus_dev_start(struct i2400m *i2400m)
{
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
- i2400ms_tx_release(i2400ms);
- d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
+ msleep(200);
+ d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, 0);
+ return 0;
}
@@ -233,18 +303,17 @@ error_kzalloc:
* Warm reset:
*
* The device will be fully reset internally, but won't be
- * disconnected from the USB bus (so no reenumeration will
+ * disconnected from the bus (so no reenumeration will
* happen). Firmware upload will be neccessary.
*
- * The device will send a reboot barker in the notification endpoint
- * that will trigger the driver to reinitialize the state
- * automatically from notif.c:i2400m_notification_grok() into
- * i2400m_dev_bootstrap_delayed().
+ * The device will send a reboot barker that will trigger the driver
+ * to reinitialize the state via __i2400m_dev_reset_handle.
*
- * Cold and bus (USB) reset:
+ *
+ * Cold and bus reset:
*
* The device will be fully reset internally, disconnected from the
- * USB bus an a reenumeration will happen. Firmware upload will be
+ * bus an a reenumeration will happen. Firmware upload will be
* neccessary. Thus, we don't do any locking or struct
* reinitialization, as we are going to be fully disconnected and
* reenumerated.
@@ -283,25 +352,13 @@ int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
sizeof(i2400m_COLD_BOOT_BARKER));
else if (rt == I2400M_RT_BUS) {
do_bus_reset:
- /* call netif_tx_disable() before sending IOE disable,
- * so that all the tx from network layer are stopped
- * while IOE is being reset. Make sure it is called
- * only after register_netdev() was issued.
- */
- if (i2400m->wimax_dev.net_dev->reg_state == NETREG_REGISTERED)
- netif_tx_disable(i2400m->wimax_dev.net_dev);
- i2400ms_rx_release(i2400ms);
- sdio_claim_host(i2400ms->func);
- sdio_disable_func(i2400ms->func);
- sdio_release_host(i2400ms->func);
+ i2400ms_bus_release(i2400m);
/* Wait for the device to settle */
msleep(40);
- result = i2400ms_enable_function(i2400ms->func);
- if (result >= 0)
- i2400ms_rx_setup(i2400ms);
+ result = i2400ms_bus_setup(i2400m);
} else
BUG();
if (result < 0 && rt != I2400M_RT_BUS) {
@@ -350,7 +407,7 @@ int i2400ms_debugfs_add(struct i2400ms *i2400ms)
int result;
struct dentry *dentry = i2400ms->i2400m.wimax_dev.debugfs_dentry;
- dentry = debugfs_create_dir("i2400m-usb", dentry);
+ dentry = debugfs_create_dir("i2400m-sdio", dentry);
result = PTR_ERR(dentry);
if (IS_ERR(dentry)) {
if (result == -ENODEV)
@@ -367,6 +424,7 @@ int i2400ms_debugfs_add(struct i2400ms *i2400ms)
error:
debugfs_remove_recursive(i2400ms->debugfs_dentry);
+ i2400ms->debugfs_dentry = NULL;
return result;
}
@@ -425,37 +483,30 @@ int i2400ms_probe(struct sdio_func *func,
i2400m->bus_tx_block_size = I2400MS_BLK_SIZE;
i2400m->bus_pl_size_max = I2400MS_PL_SIZE_MAX;
+ i2400m->bus_setup = i2400ms_bus_setup;
i2400m->bus_dev_start = i2400ms_bus_dev_start;
- i2400m->bus_dev_stop = i2400ms_bus_dev_stop;
+ i2400m->bus_dev_stop = NULL;
+ i2400m->bus_release = i2400ms_bus_release;
i2400m->bus_tx_kick = i2400ms_bus_tx_kick;
i2400m->bus_reset = i2400ms_bus_reset;
/* The iwmc3200-wimax sometimes requires the driver to try
* hard when we paint it into a corner. */
- i2400m->bus_bm_retries = I3200_BOOT_RETRIES;
+ i2400m->bus_bm_retries = I2400M_SDIO_BOOT_RETRIES;
i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send;
i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack;
i2400m->bus_fw_names = i2400ms_bus_fw_names;
i2400m->bus_bm_mac_addr_impaired = 1;
i2400m->bus_bm_pokes_table = &i2400ms_pokes[0];
- sdio_claim_host(func);
- result = sdio_set_block_size(func, I2400MS_BLK_SIZE);
- sdio_release_host(func);
- if (result < 0) {
- dev_err(dev, "Failed to set block size: %d\n", result);
- goto error_set_blk_size;
- }
-
- result = i2400ms_enable_function(i2400ms->func);
- if (result < 0) {
- dev_err(dev, "Cannot enable SDIO function: %d\n", result);
- goto error_func_enable;
+ switch (func->device) {
+ case SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX:
+ case SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5:
+ i2400ms->iwmc3200 = 1;
+ break;
+ default:
+ i2400ms->iwmc3200 = 0;
}
- result = i2400ms_rx_setup(i2400ms);
- if (result < 0)
- goto error_rx_setup;
-
result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT);
if (result < 0) {
dev_err(dev, "cannot setup device: %d\n", result);
@@ -473,13 +524,6 @@ int i2400ms_probe(struct sdio_func *func,
error_debugfs_add:
i2400m_release(i2400m);
error_setup:
- i2400ms_rx_release(i2400ms);
-error_rx_setup:
- sdio_claim_host(func);
- sdio_disable_func(func);
- sdio_release_host(func);
-error_func_enable:
-error_set_blk_size:
sdio_set_drvdata(func, NULL);
free_netdev(net_dev);
error_alloc_netdev:
@@ -497,12 +541,9 @@ void i2400ms_remove(struct sdio_func *func)
d_fnstart(3, dev, "SDIO func %p\n", func);
debugfs_remove_recursive(i2400ms->debugfs_dentry);
- i2400ms_rx_release(i2400ms);
+ i2400ms->debugfs_dentry = NULL;
i2400m_release(i2400m);
sdio_set_drvdata(func, NULL);
- sdio_claim_host(func);
- sdio_disable_func(func);
- sdio_release_host(func);
free_netdev(net_dev);
d_fnend(3, dev, "SDIO func %p\n", func);
}
@@ -512,6 +553,8 @@ const struct sdio_device_id i2400ms_sdio_ids[] = {
/* Intel: i2400m WiMAX (iwmc3200) over SDIO */
{ SDIO_DEVICE(SDIO_VENDOR_ID_INTEL,
SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL,
+ SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5) },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, i2400ms_sdio_ids);
@@ -529,6 +572,8 @@ struct sdio_driver i2400m_sdio_driver = {
static
int __init i2400ms_driver_init(void)
{
+ d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400ms_debug_params,
+ "i2400m_sdio.debug");
return sdio_register_driver(&i2400m_sdio_driver);
}
module_init(i2400ms_driver_init);
diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c
index fa16ccf8e26..54480e8947f 100644
--- a/drivers/net/wimax/i2400m/tx.c
+++ b/drivers/net/wimax/i2400m/tx.c
@@ -310,7 +310,7 @@ size_t __i2400m_tx_tail_room(struct i2400m *i2400m)
size_t tail_room;
size_t tx_in;
- if (unlikely(i2400m->tx_in) == 0)
+ if (unlikely(i2400m->tx_in == 0))
return I2400M_TX_BUF_SIZE;
tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE;
tail_room = I2400M_TX_BUF_SIZE - tx_in;
@@ -642,6 +642,9 @@ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len,
* current one is out of payload slots or we have a singleton,
* close it and start a new one */
spin_lock_irqsave(&i2400m->tx_lock, flags);
+ result = -ESHUTDOWN;
+ if (i2400m->tx_buf == NULL)
+ goto error_tx_new;
try_new:
if (unlikely(i2400m->tx_msg == NULL))
i2400m_tx_new(i2400m);
@@ -697,7 +700,10 @@ try_new:
}
error_tx_new:
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
- i2400m->bus_tx_kick(i2400m); /* always kick, might free up space */
+ /* kick in most cases, except when the TX subsys is down, as
+ * it might free space */
+ if (likely(result != -ESHUTDOWN))
+ i2400m->bus_tx_kick(i2400m);
d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n",
i2400m, buf, buf_len, pl_type, result);
return result;
@@ -740,6 +746,9 @@ struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *i2400m,
d_fnstart(3, dev, "(i2400m %p bus_size %p)\n", i2400m, bus_size);
spin_lock_irqsave(&i2400m->tx_lock, flags);
+ tx_msg_moved = NULL;
+ if (i2400m->tx_buf == NULL)
+ goto out_unlock;
skip:
tx_msg_moved = NULL;
if (i2400m->tx_in == i2400m->tx_out) { /* Empty FIFO? */
@@ -829,6 +838,8 @@ void i2400m_tx_msg_sent(struct i2400m *i2400m)
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
spin_lock_irqsave(&i2400m->tx_lock, flags);
+ if (i2400m->tx_buf == NULL)
+ goto out_unlock;
i2400m->tx_out += i2400m->tx_msg_size;
d_printf(2, dev, "TX: sent %zu b\n", (size_t) i2400m->tx_msg_size);
i2400m->tx_msg_size = 0;
@@ -837,6 +848,7 @@ void i2400m_tx_msg_sent(struct i2400m *i2400m)
n = i2400m->tx_out / I2400M_TX_BUF_SIZE;
i2400m->tx_out %= I2400M_TX_BUF_SIZE;
i2400m->tx_in -= n * I2400M_TX_BUF_SIZE;
+out_unlock:
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
@@ -876,5 +888,9 @@ int i2400m_tx_setup(struct i2400m *i2400m)
*/
void i2400m_tx_release(struct i2400m *i2400m)
{
+ unsigned long flags;
+ spin_lock_irqsave(&i2400m->tx_lock, flags);
kfree(i2400m->tx_buf);
+ i2400m->tx_buf = NULL;
+ spin_unlock_irqrestore(&i2400m->tx_lock, flags);
}
diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c
index 5ad287c228b..ce6b9938fde 100644
--- a/drivers/net/wimax/i2400m/usb-fw.c
+++ b/drivers/net/wimax/i2400m/usb-fw.c
@@ -99,10 +99,10 @@ ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)
dev_err(dev, "BM-CMD: can't get autopm: %d\n", result);
do_autopm = 0;
}
- epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT);
+ epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
- result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, HZ);
+ result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, 200);
switch (result) {
case 0:
if (len != buf_size) {
@@ -113,6 +113,28 @@ retry:
}
result = len;
break;
+ case -EPIPE:
+ /*
+ * Stall -- maybe the device is choking with our
+ * requests. Clear it and give it some time. If they
+ * happen to often, it might be another symptom, so we
+ * reset.
+ *
+ * No error handling for usb_clear_halt(0; if it
+ * works, the retry works; if it fails, this switch
+ * does the error handling for us.
+ */
+ if (edc_inc(&i2400mu->urb_edc,
+ 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "BM-CMD: too many stalls in "
+ "URB; resetting device\n");
+ usb_queue_reset_device(i2400mu->usb_iface);
+ /* fallthrough */
+ } else {
+ usb_clear_halt(i2400mu->usb_dev, pipe);
+ msleep(10); /* give the device some time */
+ goto retry;
+ }
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
@@ -135,7 +157,6 @@ retry:
result);
goto retry;
}
- result = len;
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
return result;
@@ -172,7 +193,8 @@ ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m,
result = -E2BIG;
if (cmd_size > I2400M_BM_CMD_BUF_SIZE)
goto error_too_big;
- memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size);
+ if (_cmd != i2400m->bm_cmd_buf)
+ memmove(i2400m->bm_cmd_buf, _cmd, cmd_size);
cmd = i2400m->bm_cmd_buf;
if (cmd_size_a > cmd_size) /* Zero pad space */
memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size);
@@ -226,7 +248,8 @@ int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb,
struct usb_endpoint_descriptor *epd;
int pipe;
- epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION);
+ epd = usb_get_epd(i2400mu->usb_iface,
+ i2400mu->endpoint_cfg.notification);
pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
usb_fill_int_urb(urb, i2400mu->usb_dev, pipe,
i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE,
@@ -328,8 +351,8 @@ error_dev_gone:
out:
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
- d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %zd\n",
- i2400m, ack, ack_size, result);
+ d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %ld\n",
+ i2400m, ack, ack_size, (long) result);
return result;
error_exceeded:
diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c
index 6add27c3f35..f88d1c6e35c 100644
--- a/drivers/net/wimax/i2400m/usb-notif.c
+++ b/drivers/net/wimax/i2400m/usb-notif.c
@@ -51,6 +51,7 @@
*
* i2400mu_usb_notification_cb() Called when a URB is ready
* i2400mu_notif_grok()
+ * i2400m_is_boot_barker()
* i2400m_dev_reset_handle()
* i2400mu_rx_kick()
*/
@@ -87,32 +88,21 @@ int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf,
d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n",
i2400mu, buf, buf_len);
ret = -EIO;
- if (buf_len < sizeof(i2400m_NBOOT_BARKER))
+ if (buf_len < sizeof(i2400m_ZERO_BARKER))
/* Not a bug, just ignore */
goto error_bad_size;
- if (!memcmp(i2400m_NBOOT_BARKER, buf, sizeof(i2400m_NBOOT_BARKER))
- || !memcmp(i2400m_SBOOT_BARKER, buf, sizeof(i2400m_SBOOT_BARKER)))
- ret = i2400m_dev_reset_handle(i2400m);
- else if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) {
+ ret = 0;
+ if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) {
i2400mu_rx_kick(i2400mu);
- ret = 0;
- } else { /* Unknown or unexpected data in the notif message */
- char prefix[64];
- ret = -EIO;
- dev_err(dev, "HW BUG? Unknown/unexpected data in notification "
- "message (%zu bytes)\n", buf_len);
- snprintf(prefix, sizeof(prefix), "%s %s: ",
- dev_driver_string(dev), dev_name(dev));
- if (buf_len > 64) {
- print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
- 8, 4, buf, 64, 0);
- printk(KERN_ERR "%s... (only first 64 bytes "
- "dumped)\n", prefix);
- } else
- print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
- 8, 4, buf, buf_len, 0);
+ goto out;
}
+ ret = i2400m_is_boot_barker(i2400m, buf, buf_len);
+ if (unlikely(ret >= 0))
+ ret = i2400m_dev_reset_handle(i2400m, "device rebooted");
+ else /* Unknown or unexpected data in the notif message */
+ i2400m_unknown_barker(i2400m, buf, buf_len);
error_bad_size:
+out:
d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n",
i2400mu, buf, buf_len, ret);
return ret;
@@ -220,7 +210,8 @@ int i2400mu_notification_setup(struct i2400mu *i2400mu)
dev_err(dev, "notification: cannot allocate URB\n");
goto error_alloc_urb;
}
- epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION);
+ epd = usb_get_epd(i2400mu->usb_iface,
+ i2400mu->endpoint_cfg.notification);
usb_pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe,
buf, I2400MU_MAX_NOTIFICATION_LEN,
diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c
index a314799967c..ba1b02362df 100644
--- a/drivers/net/wimax/i2400m/usb-rx.c
+++ b/drivers/net/wimax/i2400m/usb-rx.c
@@ -204,7 +204,7 @@ struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb)
dev_err(dev, "RX: can't get autopm: %d\n", result);
do_autopm = 0;
}
- epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_IN);
+ epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_in);
usb_pipe = usb_rcvbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
rx_size = skb_end_pointer(rx_skb) - rx_skb->data - rx_skb->len;
@@ -214,7 +214,7 @@ retry:
}
result = usb_bulk_msg(
i2400mu->usb_dev, usb_pipe, rx_skb->data + rx_skb->len,
- rx_size, &read_size, HZ);
+ rx_size, &read_size, 200);
usb_mark_last_busy(i2400mu->usb_dev);
switch (result) {
case 0:
@@ -222,6 +222,26 @@ retry:
goto retry; /* ZLP, just resubmit */
skb_put(rx_skb, read_size);
break;
+ case -EPIPE:
+ /*
+ * Stall -- maybe the device is choking with our
+ * requests. Clear it and give it some time. If they
+ * happen to often, it might be another symptom, so we
+ * reset.
+ *
+ * No error handling for usb_clear_halt(0; if it
+ * works, the retry works; if it fails, this switch
+ * does the error handling for us.
+ */
+ if (edc_inc(&i2400mu->urb_edc,
+ 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "BM-CMD: too many stalls in "
+ "URB; resetting device\n");
+ goto do_reset;
+ }
+ usb_clear_halt(i2400mu->usb_dev, usb_pipe);
+ msleep(10); /* give the device some time */
+ goto retry;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
@@ -283,6 +303,7 @@ out:
error_reset:
dev_err(dev, "RX: maximum errors in URB exceeded; "
"resetting device\n");
+do_reset:
usb_queue_reset_device(i2400mu->usb_iface);
rx_skb = ERR_PTR(result);
goto out;
@@ -316,10 +337,15 @@ int i2400mu_rxd(void *_i2400mu)
size_t pending;
int rx_size;
struct sk_buff *rx_skb;
+ unsigned long flags;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
+ spin_lock_irqsave(&i2400m->rx_lock, flags);
+ BUG_ON(i2400mu->rx_kthread != NULL);
+ i2400mu->rx_kthread = current;
+ spin_unlock_irqrestore(&i2400m->rx_lock, flags);
while (1) {
- d_printf(2, dev, "TX: waiting for messages\n");
+ d_printf(2, dev, "RX: waiting for messages\n");
pending = 0;
wait_event_interruptible(
i2400mu->rx_wq,
@@ -367,6 +393,9 @@ int i2400mu_rxd(void *_i2400mu)
}
result = 0;
out:
+ spin_lock_irqsave(&i2400m->rx_lock, flags);
+ i2400mu->rx_kthread = NULL;
+ spin_unlock_irqrestore(&i2400m->rx_lock, flags);
d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
return result;
@@ -403,18 +432,33 @@ int i2400mu_rx_setup(struct i2400mu *i2400mu)
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
+ struct task_struct *kthread;
- i2400mu->rx_kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx",
- wimax_dev->name);
- if (IS_ERR(i2400mu->rx_kthread)) {
- result = PTR_ERR(i2400mu->rx_kthread);
+ kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx",
+ wimax_dev->name);
+ /* the kthread function sets i2400mu->rx_thread */
+ if (IS_ERR(kthread)) {
+ result = PTR_ERR(kthread);
dev_err(dev, "RX: cannot start thread: %d\n", result);
}
return result;
}
+
void i2400mu_rx_release(struct i2400mu *i2400mu)
{
- kthread_stop(i2400mu->rx_kthread);
+ unsigned long flags;
+ struct i2400m *i2400m = &i2400mu->i2400m;
+ struct device *dev = i2400m_dev(i2400m);
+ struct task_struct *kthread;
+
+ spin_lock_irqsave(&i2400m->rx_lock, flags);
+ kthread = i2400mu->rx_kthread;
+ i2400mu->rx_kthread = NULL;
+ spin_unlock_irqrestore(&i2400m->rx_lock, flags);
+ if (kthread)
+ kthread_stop(kthread);
+ else
+ d_printf(1, dev, "RX: kthread had already exited\n");
}
diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c
index dfd893356f4..c65b9979f87 100644
--- a/drivers/net/wimax/i2400m/usb-tx.c
+++ b/drivers/net/wimax/i2400m/usb-tx.c
@@ -101,11 +101,11 @@ int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
dev_err(dev, "TX: can't get autopm: %d\n", result);
do_autopm = 0;
}
- epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT);
+ epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
- tx_msg, tx_msg_size, &sent_size, HZ);
+ tx_msg, tx_msg_size, &sent_size, 200);
usb_mark_last_busy(i2400mu->usb_dev);
switch (result) {
case 0:
@@ -115,6 +115,28 @@ retry:
result = -EIO;
}
break;
+ case -EPIPE:
+ /*
+ * Stall -- maybe the device is choking with our
+ * requests. Clear it and give it some time. If they
+ * happen to often, it might be another symptom, so we
+ * reset.
+ *
+ * No error handling for usb_clear_halt(0; if it
+ * works, the retry works; if it fails, this switch
+ * does the error handling for us.
+ */
+ if (edc_inc(&i2400mu->urb_edc,
+ 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "BM-CMD: too many stalls in "
+ "URB; resetting device\n");
+ usb_queue_reset_device(i2400mu->usb_iface);
+ /* fallthrough */
+ } else {
+ usb_clear_halt(i2400mu->usb_dev, usb_pipe);
+ msleep(10); /* give the device some time */
+ goto retry;
+ }
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
@@ -161,9 +183,15 @@ int i2400mu_txd(void *_i2400mu)
struct device *dev = &i2400mu->usb_iface->dev;
struct i2400m_msg_hdr *tx_msg;
size_t tx_msg_size;
+ unsigned long flags;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
+ spin_lock_irqsave(&i2400m->tx_lock, flags);
+ BUG_ON(i2400mu->tx_kthread != NULL);
+ i2400mu->tx_kthread = current;
+ spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+
while (1) {
d_printf(2, dev, "TX: waiting for messages\n");
tx_msg = NULL;
@@ -183,6 +211,11 @@ int i2400mu_txd(void *_i2400mu)
if (result < 0)
break;
}
+
+ spin_lock_irqsave(&i2400m->tx_lock, flags);
+ i2400mu->tx_kthread = NULL;
+ spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+
d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
return result;
}
@@ -213,11 +246,13 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu)
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
+ struct task_struct *kthread;
- i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
- wimax_dev->name);
- if (IS_ERR(i2400mu->tx_kthread)) {
- result = PTR_ERR(i2400mu->tx_kthread);
+ kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
+ wimax_dev->name);
+ /* the kthread function sets i2400mu->tx_thread */
+ if (IS_ERR(kthread)) {
+ result = PTR_ERR(kthread);
dev_err(dev, "TX: cannot start thread: %d\n", result);
}
return result;
@@ -225,5 +260,17 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu)
void i2400mu_tx_release(struct i2400mu *i2400mu)
{
- kthread_stop(i2400mu->tx_kthread);
+ unsigned long flags;
+ struct i2400m *i2400m = &i2400mu->i2400m;
+ struct device *dev = i2400m_dev(i2400m);
+ struct task_struct *kthread;
+
+ spin_lock_irqsave(&i2400m->tx_lock, flags);
+ kthread = i2400mu->tx_kthread;
+ i2400mu->tx_kthread = NULL;
+ spin_unlock_irqrestore(&i2400m->tx_lock, flags);
+ if (kthread)
+ kthread_stop(kthread);
+ else
+ d_printf(1, dev, "TX: kthread had already exited\n");
}
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 7eadd11c815..47e84ef355c 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -58,7 +58,7 @@
* i2400mu_rx_release()
* i2400mu_tx_release()
*
- * i2400mu_bus_reset() Called by i2400m->bus_reset
+ * i2400mu_bus_reset() Called by i2400m_reset
* __i2400mu_reset()
* __i2400mu_send_barker()
* usb_reset_device()
@@ -71,13 +71,25 @@
#define D_SUBMODULE usb
#include "usb-debug-levels.h"
+static char i2400mu_debug_params[128];
+module_param_string(debug, i2400mu_debug_params, sizeof(i2400mu_debug_params),
+ 0644);
+MODULE_PARM_DESC(debug,
+ "String of space-separated NAME:VALUE pairs, where NAMEs "
+ "are the different debug submodules and VALUE are the "
+ "initial debug value to set.");
/* Our firmware file name */
-static const char *i2400mu_bus_fw_names[] = {
+static const char *i2400mu_bus_fw_names_5x50[] = {
#define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf"
I2400MU_FW_FILE_NAME_v1_4,
-#define I2400MU_FW_FILE_NAME_v1_3 "i2400m-fw-usb-1.3.sbcf"
- I2400MU_FW_FILE_NAME_v1_3,
+ NULL,
+};
+
+
+static const char *i2400mu_bus_fw_names_6050[] = {
+#define I6050U_FW_FILE_NAME_v1_5 "i6050-fw-usb-1.5.sbcf"
+ I6050U_FW_FILE_NAME_v1_5,
NULL,
};
@@ -160,14 +172,59 @@ int __i2400mu_send_barker(struct i2400mu *i2400mu,
epd = usb_get_epd(i2400mu->usb_iface, endpoint);
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
memcpy(buffer, barker, barker_size);
+retry:
ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size,
- &actual_len, HZ);
- if (ret < 0) {
- if (ret != -EINVAL)
- dev_err(dev, "E: barker error: %d\n", ret);
- } else if (actual_len != barker_size) {
- dev_err(dev, "E: only %d bytes transmitted\n", actual_len);
- ret = -EIO;
+ &actual_len, 200);
+ switch (ret) {
+ case 0:
+ if (actual_len != barker_size) { /* Too short? drop it */
+ dev_err(dev, "E: %s: short write (%d B vs %zu "
+ "expected)\n",
+ __func__, actual_len, barker_size);
+ ret = -EIO;
+ }
+ break;
+ case -EPIPE:
+ /*
+ * Stall -- maybe the device is choking with our
+ * requests. Clear it and give it some time. If they
+ * happen to often, it might be another symptom, so we
+ * reset.
+ *
+ * No error handling for usb_clear_halt(0; if it
+ * works, the retry works; if it fails, this switch
+ * does the error handling for us.
+ */
+ if (edc_inc(&i2400mu->urb_edc,
+ 10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "E: %s: too many stalls in "
+ "URB; resetting device\n", __func__);
+ usb_queue_reset_device(i2400mu->usb_iface);
+ /* fallthrough */
+ } else {
+ usb_clear_halt(i2400mu->usb_dev, pipe);
+ msleep(10); /* give the device some time */
+ goto retry;
+ }
+ case -EINVAL: /* while removing driver */
+ case -ENODEV: /* dev disconnect ... */
+ case -ENOENT: /* just ignore it */
+ case -ESHUTDOWN: /* and exit */
+ case -ECONNRESET:
+ ret = -ESHUTDOWN;
+ break;
+ default: /* Some error? */
+ if (edc_inc(&i2400mu->urb_edc,
+ EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "E: %s: maximum errors in URB "
+ "exceeded; resetting device\n",
+ __func__);
+ usb_queue_reset_device(i2400mu->usb_iface);
+ } else {
+ dev_warn(dev, "W: %s: cannot send URB: %d\n",
+ __func__, ret);
+ goto retry;
+ }
}
kfree(buffer);
error_kzalloc:
@@ -232,15 +289,16 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt);
if (rt == I2400M_RT_WARM)
- result = __i2400mu_send_barker(i2400mu, i2400m_WARM_BOOT_BARKER,
- sizeof(i2400m_WARM_BOOT_BARKER),
- I2400MU_EP_BULK_OUT);
+ result = __i2400mu_send_barker(
+ i2400mu, i2400m_WARM_BOOT_BARKER,
+ sizeof(i2400m_WARM_BOOT_BARKER),
+ i2400mu->endpoint_cfg.bulk_out);
else if (rt == I2400M_RT_COLD)
- result = __i2400mu_send_barker(i2400mu, i2400m_COLD_BOOT_BARKER,
- sizeof(i2400m_COLD_BOOT_BARKER),
- I2400MU_EP_RESET_COLD);
+ result = __i2400mu_send_barker(
+ i2400mu, i2400m_COLD_BOOT_BARKER,
+ sizeof(i2400m_COLD_BOOT_BARKER),
+ i2400mu->endpoint_cfg.reset_cold);
else if (rt == I2400M_RT_BUS) {
-do_bus_reset:
result = usb_reset_device(i2400mu->usb_dev);
switch (result) {
case 0:
@@ -248,7 +306,7 @@ do_bus_reset:
case -ENODEV:
case -ENOENT:
case -ESHUTDOWN:
- result = rt == I2400M_RT_WARM ? -ENODEV : 0;
+ result = 0;
break; /* We assume the device is disconnected */
default:
dev_err(dev, "USB reset failed (%d), giving up!\n",
@@ -261,10 +319,17 @@ do_bus_reset:
if (result < 0
&& result != -EINVAL /* device is gone */
&& rt != I2400M_RT_BUS) {
+ /*
+ * Things failed -- resort to lower level reset, that
+ * we queue in another context; the reason for this is
+ * that the pre and post reset functionality requires
+ * the i2400m->init_mutex; RT_WARM and RT_COLD can
+ * come from areas where i2400m->init_mutex is taken.
+ */
dev_err(dev, "%s reset failed (%d); trying USB reset\n",
rt == I2400M_RT_WARM ? "warm" : "cold", result);
- rt = I2400M_RT_BUS;
- goto do_bus_reset;
+ usb_queue_reset_device(i2400mu->usb_iface);
+ result = -ENODEV;
}
d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result);
return result;
@@ -402,20 +467,33 @@ int i2400mu_probe(struct usb_interface *iface,
i2400m->bus_tx_block_size = I2400MU_BLK_SIZE;
i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX;
+ i2400m->bus_setup = NULL;
i2400m->bus_dev_start = i2400mu_bus_dev_start;
i2400m->bus_dev_stop = i2400mu_bus_dev_stop;
+ i2400m->bus_release = NULL;
i2400m->bus_tx_kick = i2400mu_bus_tx_kick;
i2400m->bus_reset = i2400mu_bus_reset;
- i2400m->bus_bm_retries = I2400M_BOOT_RETRIES;
+ i2400m->bus_bm_retries = I2400M_USB_BOOT_RETRIES;
i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send;
i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack;
- i2400m->bus_fw_names = i2400mu_bus_fw_names;
i2400m->bus_bm_mac_addr_impaired = 0;
+ if (id->idProduct == USB_DEVICE_ID_I6050) {
+ i2400m->bus_fw_names = i2400mu_bus_fw_names_6050;
+ i2400mu->endpoint_cfg.bulk_out = 0;
+ i2400mu->endpoint_cfg.notification = 3;
+ i2400mu->endpoint_cfg.reset_cold = 2;
+ i2400mu->endpoint_cfg.bulk_in = 1;
+ } else {
+ i2400m->bus_fw_names = i2400mu_bus_fw_names_5x50;
+ i2400mu->endpoint_cfg.bulk_out = 0;
+ i2400mu->endpoint_cfg.notification = 1;
+ i2400mu->endpoint_cfg.reset_cold = 2;
+ i2400mu->endpoint_cfg.bulk_in = 3;
+ }
#ifdef CONFIG_PM
iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */
device_init_wakeup(dev, 1);
- usb_autopm_enable(i2400mu->usb_iface);
usb_dev->autosuspend_delay = 15 * HZ;
usb_dev->autosuspend_disabled = 0;
#endif
@@ -483,7 +561,10 @@ void i2400mu_disconnect(struct usb_interface *iface)
* So at the end, the three cases require common handling.
*
* If at the time of this call the device's firmware is not loaded,
- * nothing has to be done.
+ * nothing has to be done. Note we can be "loose" about not reading
+ * i2400m->updown under i2400m->init_mutex. If it happens to change
+ * inmediately, other parts of the call flow will fail and effectively
+ * catch it.
*
* If the firmware is loaded, we need to:
*
@@ -522,6 +603,7 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
#endif
d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event);
+ rmb(); /* see i2400m->updown's documentation */
if (i2400m->updown == 0)
goto no_firmware;
if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) {
@@ -575,6 +657,7 @@ int i2400mu_resume(struct usb_interface *iface)
struct i2400m *i2400m = &i2400mu->i2400m;
d_fnstart(3, dev, "(iface %p)\n", iface);
+ rmb(); /* see i2400m->updown's documentation */
if (i2400m->updown == 0) {
d_printf(1, dev, "fw was down, no resume neeed\n");
goto out;
@@ -591,7 +674,54 @@ out:
static
+int i2400mu_reset_resume(struct usb_interface *iface)
+{
+ int result;
+ struct device *dev = &iface->dev;
+ struct i2400mu *i2400mu = usb_get_intfdata(iface);
+ struct i2400m *i2400m = &i2400mu->i2400m;
+
+ d_fnstart(3, dev, "(iface %p)\n", iface);
+ result = i2400m_dev_reset_handle(i2400m, "device reset on resume");
+ d_fnend(3, dev, "(iface %p) = %d\n", iface, result);
+ return result < 0 ? result : 0;
+}
+
+
+/*
+ * Another driver or user space is triggering a reset on the device
+ * which contains the interface passed as an argument. Cease IO and
+ * save any device state you need to restore.
+ *
+ * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if
+ * you are in atomic context.
+ */
+static
+int i2400mu_pre_reset(struct usb_interface *iface)
+{
+ struct i2400mu *i2400mu = usb_get_intfdata(iface);
+ return i2400m_pre_reset(&i2400mu->i2400m);
+}
+
+
+/*
+ * The reset has completed. Restore any saved device state and begin
+ * using the device again.
+ *
+ * If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if
+ * you are in atomic context.
+ */
+static
+int i2400mu_post_reset(struct usb_interface *iface)
+{
+ struct i2400mu *i2400mu = usb_get_intfdata(iface);
+ return i2400m_post_reset(&i2400mu->i2400m);
+}
+
+
+static
struct usb_device_id i2400mu_id_table[] = {
+ { USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) },
{ USB_DEVICE(0x8086, 0x0181) },
{ USB_DEVICE(0x8086, 0x1403) },
{ USB_DEVICE(0x8086, 0x1405) },
@@ -609,8 +739,11 @@ struct usb_driver i2400mu_driver = {
.name = KBUILD_MODNAME,
.suspend = i2400mu_suspend,
.resume = i2400mu_resume,
+ .reset_resume = i2400mu_reset_resume,
.probe = i2400mu_probe,
.disconnect = i2400mu_disconnect,
+ .pre_reset = i2400mu_pre_reset,
+ .post_reset = i2400mu_post_reset,
.id_table = i2400mu_id_table,
.supports_autosuspend = 1,
};
@@ -618,6 +751,8 @@ struct usb_driver i2400mu_driver = {
static
int __init i2400mu_driver_init(void)
{
+ d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400mu_debug_params,
+ "i2400m_usb.debug");
return usb_register(&i2400mu_driver);
}
module_init(i2400mu_driver_init);
@@ -632,7 +767,7 @@ void __exit i2400mu_driver_exit(void)
module_exit(i2400mu_driver_exit);
MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
-MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB");
+MODULE_DESCRIPTION("Driver for USB based Intel Wireless WiMAX Connection 2400M "
+ "(5x50 & 6050)");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_4);
-MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_3);
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index d7a764a2fc1..56dd6650c97 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -5,6 +5,7 @@
menuconfig WLAN
bool "Wireless LAN"
depends on !S390
+ select WIRELESS
default y
---help---
This section contains all the pre 802.11 and 802.11 wireless
@@ -15,114 +16,12 @@ menuconfig WLAN
if WLAN
-menuconfig WLAN_PRE80211
- bool "Wireless LAN (pre-802.11)"
- depends on NETDEVICES
- ---help---
- Say Y if you have any pre-802.11 wireless LAN hardware.
-
- This option does not affect the kernel build, it only
- lets you choose drivers.
-
-config STRIP
- tristate "STRIP (Metricom starmode radio IP)"
- depends on INET && WLAN_PRE80211
- select WIRELESS_EXT
- ---help---
- Say Y if you have a Metricom radio and intend to use Starmode Radio
- IP. STRIP is a radio protocol developed for the MosquitoNet project
- to send Internet traffic using Metricom radios. Metricom radios are
- small, battery powered, 100kbit/sec packet radio transceivers, about
- the size and weight of a cellular telephone. (You may also have heard
- them called "Metricom modems" but we avoid the term "modem" because
- it misleads many people into thinking that you can plug a Metricom
- modem into a phone line and use it as a modem.)
-
- You can use STRIP on any Linux machine with a serial port, although
- it is obviously most useful for people with laptop computers. If you
- think you might get a Metricom radio in the future, there is no harm
- in saying Y to STRIP now, except that it makes the kernel a bit
- bigger.
-
- To compile this as a module, choose M here: the module will be
- called strip.
-
-config ARLAN
- tristate "Aironet Arlan 655 & IC2200 DS support"
- depends on ISA && !64BIT && WLAN_PRE80211
- select WIRELESS_EXT
- ---help---
- Aironet makes Arlan, a class of wireless LAN adapters. These use the
- www.Telxon.com chip, which is also used on several similar cards.
- This driver is tested on the 655 and IC2200 series cards. Look at
- <http://www.ylenurme.ee/~elmer/655/> for the latest information.
-
- The driver is built as two modules, arlan and arlan-proc. The latter
- is the /proc interface and is not needed most of time.
-
- On some computers the card ends up in non-valid state after some
- time. Use a ping-reset script to clear it.
-
-config WAVELAN
- tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support"
- depends on ISA && WLAN_PRE80211
- select WIRELESS_EXT
- ---help---
- The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is
- a Radio LAN (wireless Ethernet-like Local Area Network) using the
- radio frequencies 900 MHz and 2.4 GHz.
-
- If you want to use an ISA WaveLAN card under Linux, say Y and read
- the Ethernet-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. Some more specific
- information is contained in
- <file:Documentation/networking/wavelan.txt> and in the source code
- <file:drivers/net/wireless/wavelan.p.h>.
-
- You will also need the wireless tools package available from
- <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
- Please read the man pages contained therein.
-
- To compile this driver as a module, choose M here: the module will be
- called wavelan.
-
-config PCMCIA_WAVELAN
- tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support"
- depends on PCMCIA && WLAN_PRE80211
- select WIRELESS_EXT
- help
- Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA
- (PC-card) wireless Ethernet networking card to your computer. This
- driver is for the non-IEEE-802.11 Wavelan cards.
-
- To compile this driver as a module, choose M here: the module will be
- called wavelan_cs. If unsure, say N.
-
-config PCMCIA_NETWAVE
- tristate "Xircom Netwave AirSurfer Pcmcia wireless support"
- depends on PCMCIA && WLAN_PRE80211
- select WIRELESS_EXT
- help
- Say Y here if you intend to attach this type of PCMCIA (PC-card)
- wireless Ethernet networking card to your computer.
-
- To compile this driver as a module, choose M here: the module will be
- called netwave_cs. If unsure, say N.
-
-
-menuconfig WLAN_80211
- bool "Wireless LAN (IEEE 802.11)"
- depends on NETDEVICES
- ---help---
- Say Y if you have any 802.11 wireless LAN hardware.
-
- This option does not affect the kernel build, it only
- lets you choose drivers.
-
config PCMCIA_RAYCS
tristate "Aviator/Raytheon 2.4GHz wireless support"
- depends on PCMCIA && WLAN_80211
+ depends on PCMCIA
select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
---help---
Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
(PC-card) wireless Ethernet networking card to your computer.
@@ -132,49 +31,9 @@ config PCMCIA_RAYCS
To compile this driver as a module, choose M here: the module will be
called ray_cs. If unsure, say N.
-config LIBERTAS
- tristate "Marvell 8xxx Libertas WLAN driver support"
- depends on WLAN_80211
- select WIRELESS_EXT
- select LIB80211
- select FW_LOADER
- ---help---
- A library for Marvell Libertas 8xxx devices.
-
-config LIBERTAS_USB
- tristate "Marvell Libertas 8388 USB 802.11b/g cards"
- depends on LIBERTAS && USB
- ---help---
- A driver for Marvell Libertas 8388 USB devices.
-
-config LIBERTAS_CS
- tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards"
- depends on LIBERTAS && PCMCIA
- select FW_LOADER
- ---help---
- A driver for Marvell Libertas 8385 CompactFlash devices.
-
-config LIBERTAS_SDIO
- tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards"
- depends on LIBERTAS && MMC
- ---help---
- A driver for Marvell Libertas 8385/8686/8688 SDIO devices.
-
-config LIBERTAS_SPI
- tristate "Marvell Libertas 8686 SPI 802.11b/g cards"
- depends on LIBERTAS && SPI
- ---help---
- A driver for Marvell Libertas 8686 SPI devices.
-
-config LIBERTAS_DEBUG
- bool "Enable full debugging output in the Libertas module."
- depends on LIBERTAS
- ---help---
- Debugging support.
-
config LIBERTAS_THINFIRM
tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware"
- depends on WLAN_80211 && MAC80211
+ depends on MAC80211
select FW_LOADER
---help---
A library for Marvell Libertas 8xxx devices using thinfirm.
@@ -187,9 +46,11 @@ config LIBERTAS_THINFIRM_USB
config AIRO
tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
- depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN)
+ depends on ISA_DMA_API && (PCI || BROKEN)
select WIRELESS_EXT
select CRYPTO
+ select WEXT_SPY
+ select WEXT_PRIV
---help---
This is the standard Linux driver to support Cisco/Aironet ISA and
PCI 802.11 wireless cards.
@@ -205,8 +66,9 @@ config AIRO
config ATMEL
tristate "Atmel at76c50x chipset 802.11b support"
- depends on (PCI || PCMCIA) && WLAN_80211
+ depends on (PCI || PCMCIA)
select WIRELESS_EXT
+ select WEXT_PRIV
select FW_LOADER
select CRC32
---help---
@@ -239,7 +101,7 @@ config PCMCIA_ATMEL
config AT76C50X_USB
tristate "Atmel at76c503/at76c505/at76c505a USB cards"
- depends on MAC80211 && WLAN_80211 && USB
+ depends on MAC80211 && USB
select FW_LOADER
---help---
Enable support for USB Wireless devices using Atmel at76c503,
@@ -247,8 +109,9 @@ config AT76C50X_USB
config AIRO_CS
tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
- depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211
+ depends on PCMCIA && (BROKEN || !M32R)
select WIRELESS_EXT
+ select WEXT_SPY
select CRYPTO
select CRYPTO_AES
---help---
@@ -266,18 +129,21 @@ config AIRO_CS
Cisco Linux utilities can be used to configure the card.
config PCMCIA_WL3501
- tristate "Planet WL3501 PCMCIA cards"
- depends on EXPERIMENTAL && PCMCIA && WLAN_80211
- select WIRELESS_EXT
- ---help---
- A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
- It has basic support for Linux wireless extensions and initial
- micro support for ethtool.
+ tristate "Planet WL3501 PCMCIA cards"
+ depends on EXPERIMENTAL && PCMCIA
+ select WIRELESS_EXT
+ select WEXT_SPY
+ help
+ A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
+ It has basic support for Linux wireless extensions and initial
+ micro support for ethtool.
config PRISM54
tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)'
- depends on PCI && EXPERIMENTAL && WLAN_80211
+ depends on PCI && EXPERIMENTAL
select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
select FW_LOADER
---help---
This enables support for FullMAC PCI/Cardbus prism54 devices. This
@@ -298,8 +164,9 @@ config PRISM54
config USB_ZD1201
tristate "USB ZD1201 based Wireless device support"
- depends on USB && WLAN_80211
+ depends on USB
select WIRELESS_EXT
+ select WEXT_PRIV
select FW_LOADER
---help---
Say Y if you want to use wireless LAN adapters based on the ZyDAS
@@ -316,7 +183,7 @@ config USB_ZD1201
config USB_NET_RNDIS_WLAN
tristate "Wireless RNDIS USB support"
- depends on USB && WLAN_80211 && EXPERIMENTAL
+ depends on USB && EXPERIMENTAL
depends on CFG80211
select USB_USBNET
select USB_NET_CDCETHER
@@ -344,7 +211,7 @@ config USB_NET_RNDIS_WLAN
config RTL8180
tristate "Realtek 8180/8185 PCI support"
- depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL
+ depends on MAC80211 && PCI && EXPERIMENTAL
select EEPROM_93CX6
---help---
This is a driver for RTL8180 and RTL8185 based cards.
@@ -400,7 +267,7 @@ config RTL8180
config RTL8187
tristate "Realtek 8187 and 8187B USB support"
- depends on MAC80211 && USB && WLAN_80211
+ depends on MAC80211 && USB
select EEPROM_93CX6
---help---
This is a driver for RTL8187 and RTL8187B based cards.
@@ -429,7 +296,7 @@ config RTL8187_LEDS
config ADM8211
tristate "ADMtek ADM8211 support"
- depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL
+ depends on MAC80211 && PCI && EXPERIMENTAL
select CRC32
select EEPROM_93CX6
---help---
@@ -456,7 +323,7 @@ config ADM8211
config MAC80211_HWSIM
tristate "Simulated radio testing tool for mac80211"
- depends on MAC80211 && WLAN_80211
+ depends on MAC80211
---help---
This driver is a developer testing tool that can be used to test
IEEE 802.11 networking stack (mac80211) functionality. This is not
@@ -469,24 +336,25 @@ config MAC80211_HWSIM
config MWL8K
tristate "Marvell 88W8xxx PCI/PCIe Wireless support"
- depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL
+ depends on MAC80211 && PCI && EXPERIMENTAL
---help---
This driver supports Marvell TOPDOG 802.11 wireless cards.
To compile this driver as a module, choose M here: the module
will be called mwl8k. If unsure, say N.
-source "drivers/net/wireless/p54/Kconfig"
source "drivers/net/wireless/ath/Kconfig"
-source "drivers/net/wireless/ipw2x00/Kconfig"
-source "drivers/net/wireless/iwlwifi/Kconfig"
-source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"
-source "drivers/net/wireless/zd1211rw/Kconfig"
-source "drivers/net/wireless/rt2x00/Kconfig"
+source "drivers/net/wireless/hostap/Kconfig"
+source "drivers/net/wireless/ipw2x00/Kconfig"
+source "drivers/net/wireless/iwlwifi/Kconfig"
+source "drivers/net/wireless/iwmc3200wifi/Kconfig"
+source "drivers/net/wireless/libertas/Kconfig"
source "drivers/net/wireless/orinoco/Kconfig"
+source "drivers/net/wireless/p54/Kconfig"
+source "drivers/net/wireless/rt2x00/Kconfig"
source "drivers/net/wireless/wl12xx/Kconfig"
-source "drivers/net/wireless/iwmc3200wifi/Kconfig"
+source "drivers/net/wireless/zd1211rw/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 7a4647e78fd..5d4ce4d2b32 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -5,16 +5,6 @@
obj-$(CONFIG_IPW2100) += ipw2x00/
obj-$(CONFIG_IPW2200) += ipw2x00/
-obj-$(CONFIG_STRIP) += strip.o
-obj-$(CONFIG_ARLAN) += arlan.o
-
-arlan-objs := arlan-main.o arlan-proc.o
-
-# Obsolete cards
-obj-$(CONFIG_WAVELAN) += wavelan.o
-obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o
-obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o
-
obj-$(CONFIG_HERMES) += orinoco/
obj-$(CONFIG_AIRO) += airo.o
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index b80f514877d..39410016b4f 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1538,7 +1538,7 @@ static int adm8211_start(struct ieee80211_hw *dev)
adm8211_hw_init(dev);
adm8211_rf_set_channel(dev, priv->channel);
- retval = request_irq(priv->pdev->irq, &adm8211_interrupt,
+ retval = request_irq(priv->pdev->irq, adm8211_interrupt,
IRQF_SHARED, "adm8211", dev);
if (retval) {
printk(KERN_ERR "%s: failed to register IRQ handler\n",
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index abf896a7390..4331d675fcc 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -4806,7 +4806,7 @@ static int airo_config_commit(struct net_device *dev,
static inline int sniffing_mode(struct airo_info *ai)
{
- return le16_to_cpu(ai->config.rmode & RXMODE_MASK) >=
+ return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >=
le16_to_cpu(RXMODE_RFMON);
}
@@ -5659,7 +5659,8 @@ static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state)
pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
pci_save_state(pdev);
- return pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
}
static int airo_pci_resume(struct pci_dev *pdev)
diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c
index d0593ed9170..f6036fb4231 100644
--- a/drivers/net/wireless/airo_cs.c
+++ b/drivers/net/wireless/airo_cs.c
@@ -43,21 +43,6 @@
#include "airo.h"
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-static char *version = "$Revision: 1.2 $";
-#define DEBUG(n, args...) if (pc_debug > (n)) printk(KERN_DEBUG args);
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -145,11 +130,10 @@ static int airo_probe(struct pcmcia_device *p_dev)
{
local_info_t *local;
- DEBUG(0, "airo_attach()\n");
+ dev_dbg(&p_dev->dev, "airo_attach()\n");
/* Interrupt setup */
p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
p_dev->irq.Handler = NULL;
/*
@@ -184,7 +168,7 @@ static int airo_probe(struct pcmcia_device *p_dev)
static void airo_detach(struct pcmcia_device *link)
{
- DEBUG(0, "airo_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "airo_detach\n");
airo_release(link);
@@ -204,9 +188,6 @@ static void airo_detach(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int airo_cs_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -275,11 +256,11 @@ static int airo_cs_config_check(struct pcmcia_device *p_dev,
req->Base = mem->win[0].host_addr;
req->Size = mem->win[0].len;
req->AccessSpeed = 0;
- if (pcmcia_request_window(&p_dev, req, &p_dev->win) != 0)
+ if (pcmcia_request_window(p_dev, req, &p_dev->win) != 0)
return -ENODEV;
map.Page = 0;
map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(p_dev->win, &map) != 0)
+ if (pcmcia_map_mem_page(p_dev, p_dev->win, &map) != 0)
return -ENODEV;
}
/* If we got this far, we're cool! */
@@ -291,11 +272,11 @@ static int airo_config(struct pcmcia_device *link)
{
local_info_t *dev;
win_req_t *req;
- int last_fn, last_ret;
+ int ret;
dev = link->priv;
- DEBUG(0, "airo_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "airo_config\n");
req = kzalloc(sizeof(win_req_t), GFP_KERNEL);
if (!req)
@@ -315,8 +296,8 @@ static int airo_config(struct pcmcia_device *link)
* and most client drivers will only use the CIS to fill in
* implementation-defined details.
*/
- last_ret = pcmcia_loop_config(link, airo_cs_config_check, req);
- if (last_ret)
+ ret = pcmcia_loop_config(link, airo_cs_config_check, req);
+ if (ret)
goto failed;
/*
@@ -324,21 +305,25 @@ static int airo_config(struct pcmcia_device *link)
handler to the interrupt, unless the 'Handler' member of the
irq structure is initialized.
*/
- if (link->conf.Attributes & CONF_ENABLE_IRQ)
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ }
/*
This actually configures the PCMCIA socket -- setting up
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
((local_info_t *)link->priv)->eth_dev =
init_airo_card(link->irq.AssignedIRQ,
- link->io.BasePort1, 1, &handle_to_dev(link));
+ link->io.BasePort1, 1, &link->dev);
if (!((local_info_t *)link->priv)->eth_dev)
- goto cs_failed;
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -368,8 +353,6 @@ static int airo_config(struct pcmcia_device *link)
kfree(req);
return 0;
- cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
airo_release(link);
kfree(req);
@@ -386,7 +369,7 @@ static int airo_config(struct pcmcia_device *link)
static void airo_release(struct pcmcia_device *link)
{
- DEBUG(0, "airo_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "airo_release\n");
pcmcia_disable_device(link);
}
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 8e1a55dec35..2517364d3eb 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -121,6 +121,14 @@ static struct fwentry firmwares[] = {
[BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" },
[BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" },
};
+MODULE_FIRMWARE("atmel_at76c503-i3861.bin");
+MODULE_FIRMWARE("atmel_at76c503-i3863.bin");
+MODULE_FIRMWARE("atmel_at76c503-rfmd.bin");
+MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin");
+MODULE_FIRMWARE("atmel_at76c505-rfmd.bin");
+MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin");
+MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin");
+MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin");
#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops)
@@ -524,20 +532,6 @@ static char *hex2str(void *buf, int len)
return ret;
}
-#define MAC2STR_BUFFERS 4
-
-static inline char *mac2str(u8 *mac)
-{
- static atomic_t a = ATOMIC_INIT(0);
- static char bufs[MAC2STR_BUFFERS][6 * 3];
- char *str;
-
- str = bufs[atomic_inc_return(&a) & (MAC2STR_BUFFERS - 1)];
- sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
- mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
- return str;
-}
-
/* LED trigger */
static int tx_activity;
static void at76_ledtrig_tx_timerfunc(unsigned long data);
@@ -973,13 +967,13 @@ static void at76_dump_mib_mac_addr(struct at76_priv *priv)
goto exit;
}
- at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %s res 0x%x 0x%x",
+ at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x",
wiphy_name(priv->hw->wiphy),
- mac2str(m->mac_addr), m->res[0], m->res[1]);
+ m->mac_addr, m->res[0], m->res[1]);
for (i = 0; i < ARRAY_SIZE(m->group_addr); i++)
- at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %s, "
+ at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, "
"status %d", wiphy_name(priv->hw->wiphy), i,
- mac2str(m->group_addr[i]), m->group_addr_status[i]);
+ m->group_addr[i], m->group_addr_status[i]);
exit:
kfree(m);
}
@@ -1042,7 +1036,7 @@ static void at76_dump_mib_mac_mgmt(struct at76_priv *priv)
at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration "
"%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d "
"CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d "
- "current_bssid %s current_essid %s current_bss_type %d "
+ "current_bssid %pM current_essid %s current_bss_type %d "
"pm_mode %d ibss_change %d res %d "
"multi_domain_capability_implemented %d "
"international_roaming %d country_string %.3s",
@@ -1051,7 +1045,7 @@ static void at76_dump_mib_mac_mgmt(struct at76_priv *priv)
le16_to_cpu(m->medium_occupancy_limit),
le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window),
m->CFP_mode, m->privacy_option_implemented, m->DTIM_period,
- m->CFP_period, mac2str(m->current_bssid),
+ m->CFP_period, m->current_bssid,
hex2str(m->current_essid, IW_ESSID_MAX_SIZE),
m->current_bss_type, m->power_mgmt_mode, m->ibss_change,
m->res, m->multi_domain_capability_implemented,
@@ -1080,7 +1074,7 @@ static void at76_dump_mib_mac(struct at76_priv *priv)
"cwmin %d cwmax %d short_retry_time %d long_retry_time %d "
"scan_type %d scan_channel %d probe_delay %u "
"min_channel_time %d max_channel_time %d listen_int %d "
- "desired_ssid %s desired_bssid %s desired_bsstype %d",
+ "desired_ssid %s desired_bssid %pM desired_bsstype %d",
wiphy_name(priv->hw->wiphy),
le32_to_cpu(m->max_tx_msdu_lifetime),
le32_to_cpu(m->max_rx_lifetime),
@@ -1092,7 +1086,7 @@ static void at76_dump_mib_mac(struct at76_priv *priv)
le16_to_cpu(m->max_channel_time),
le16_to_cpu(m->listen_interval),
hex2str(m->desired_ssid, IW_ESSID_MAX_SIZE),
- mac2str(m->desired_bssid), m->desired_bsstype);
+ m->desired_bssid, m->desired_bsstype);
exit:
kfree(m);
}
@@ -1194,6 +1188,9 @@ static int at76_start_monitor(struct at76_priv *priv)
scan.channel = priv->channel;
scan.scan_type = SCAN_TYPE_PASSIVE;
scan.international_scan = 0;
+ scan.min_channel_time = cpu_to_le16(priv->scan_min_time);
+ scan.max_channel_time = cpu_to_le16(priv->scan_max_time);
+ scan.probe_delay = cpu_to_le16(0);
ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan));
if (ret >= 0)
@@ -2217,6 +2214,8 @@ static struct ieee80211_supported_band at76_supported_band = {
static int at76_init_new_device(struct at76_priv *priv,
struct usb_interface *interface)
{
+ struct wiphy *wiphy;
+ size_t len;
int ret;
/* set up the endpoint information */
@@ -2254,6 +2253,7 @@ static int at76_init_new_device(struct at76_priv *priv,
priv->device_unplugged = 0;
/* mac80211 initialisation */
+ wiphy = priv->hw->wiphy;
priv->hw->wiphy->max_scan_ssids = 1;
priv->hw->wiphy->max_scan_ie_len = 0;
priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
@@ -2265,6 +2265,13 @@ static int at76_init_new_device(struct at76_priv *priv,
SET_IEEE80211_DEV(priv->hw, &interface->dev);
SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
+ len = sizeof(wiphy->fw_version);
+ snprintf(wiphy->fw_version, len, "%d.%d.%d-%d",
+ priv->fw_version.major, priv->fw_version.minor,
+ priv->fw_version.patch, priv->fw_version.build);
+
+ wiphy->hw_version = priv->board_type;
+
ret = ieee80211_register_hw(priv->hw);
if (ret) {
printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n",
@@ -2274,9 +2281,9 @@ static int at76_init_new_device(struct at76_priv *priv,
priv->mac80211_registered = 1;
- printk(KERN_INFO "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n",
+ printk(KERN_INFO "%s: USB %s, MAC %pM, firmware %d.%d.%d-%d\n",
wiphy_name(priv->hw->wiphy),
- dev_name(&interface->dev), mac2str(priv->mac_addr),
+ dev_name(&interface->dev), priv->mac_addr,
priv->fw_version.major, priv->fw_version.minor,
priv->fw_version.patch, priv->fw_version.build);
printk(KERN_INFO "%s: regulatory domain 0x%02x: %s\n",
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index 11ded150b93..4e7a7fd695c 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -1,6 +1,5 @@
menuconfig ATH_COMMON
tristate "Atheros Wireless Cards"
- depends on WLAN_80211
depends on CFG80211
---help---
This will enable the support for the Atheros wireless drivers.
@@ -16,7 +15,15 @@ menuconfig ATH_COMMON
http://wireless.kernel.org/en/users/Drivers/Atheros
if ATH_COMMON
+
+config ATH_DEBUG
+ bool "Atheros wireless debugging"
+ ---help---
+ Say Y, if you want to debug atheros wireless drivers.
+ Right now only ath9k makes use of this.
+
source "drivers/net/wireless/ath/ath5k/Kconfig"
source "drivers/net/wireless/ath/ath9k/Kconfig"
source "drivers/net/wireless/ath/ar9170/Kconfig"
+
endif
diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile
index 4bb0132ada3..8113a5042af 100644
--- a/drivers/net/wireless/ath/Makefile
+++ b/drivers/net/wireless/ath/Makefile
@@ -1,6 +1,11 @@
obj-$(CONFIG_ATH5K) += ath5k/
-obj-$(CONFIG_ATH9K) += ath9k/
+obj-$(CONFIG_ATH9K_HW) += ath9k/
obj-$(CONFIG_AR9170_USB) += ar9170/
obj-$(CONFIG_ATH_COMMON) += ath.o
-ath-objs := main.o regd.o
+
+ath-objs := main.o \
+ regd.o \
+ hw.o
+
+ath-$(CONFIG_ATH_DEBUG) += debug.o
diff --git a/drivers/net/wireless/ath/ar9170/Kconfig b/drivers/net/wireless/ath/ar9170/Kconfig
index 05918f1e685..d7a4799d20f 100644
--- a/drivers/net/wireless/ath/ar9170/Kconfig
+++ b/drivers/net/wireless/ath/ar9170/Kconfig
@@ -1,6 +1,6 @@
config AR9170_USB
tristate "Atheros AR9170 802.11n USB support"
- depends on USB && MAC80211 && WLAN_80211
+ depends on USB && MAC80211
select FW_LOADER
help
This is a driver for the Atheros "otus" 802.11n USB devices.
diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h
index 914e4718a9a..9f9459860d8 100644
--- a/drivers/net/wireless/ath/ar9170/ar9170.h
+++ b/drivers/net/wireless/ath/ar9170/ar9170.h
@@ -172,8 +172,6 @@ struct ar9170 {
/* interface mode settings */
struct ieee80211_vif *vif;
- u8 mac_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
/* beaconing */
struct sk_buff *beacon;
@@ -204,6 +202,8 @@ struct ar9170 {
u8 power_2G_ht20[8];
u8 power_2G_ht40[8];
+ u8 phy_heavy_clip;
+
#ifdef CONFIG_AR9170_LEDS
struct delayed_work led_work;
struct ar9170_led leds[AR9170_NUM_LEDS];
@@ -231,7 +231,7 @@ struct ar9170 {
struct sk_buff_head tx_status_ampdu;
spinlock_t tx_ampdu_list_lock;
struct list_head tx_ampdu_list;
- unsigned int tx_ampdu_pending;
+ atomic_t tx_ampdu_pending;
/* rxstream mpdu merge */
struct ar9170_rxstream_mpdu_merge rx_mpdu;
diff --git a/drivers/net/wireless/ath/ar9170/cmd.c b/drivers/net/wireless/ath/ar9170/cmd.c
index f57a6200167..cf6f5c4174a 100644
--- a/drivers/net/wireless/ath/ar9170/cmd.c
+++ b/drivers/net/wireless/ath/ar9170/cmd.c
@@ -72,8 +72,7 @@ int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
return err;
}
-static int ar9170_read_mreg(struct ar9170 *ar, int nregs,
- const u32 *regs, u32 *out)
+int ar9170_read_mreg(struct ar9170 *ar, int nregs, const u32 *regs, u32 *out)
{
int i, err;
__le32 *offs, *res;
diff --git a/drivers/net/wireless/ath/ar9170/cmd.h b/drivers/net/wireless/ath/ar9170/cmd.h
index a4f0e50e52b..826c45e6b27 100644
--- a/drivers/net/wireless/ath/ar9170/cmd.h
+++ b/drivers/net/wireless/ath/ar9170/cmd.h
@@ -44,6 +44,7 @@
int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len);
int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val);
int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val);
+int ar9170_read_mreg(struct ar9170 *ar, int nregs, const u32 *regs, u32 *out);
int ar9170_echo_test(struct ar9170 *ar, u32 v);
/*
diff --git a/drivers/net/wireless/ath/ar9170/hw.h b/drivers/net/wireless/ath/ar9170/hw.h
index 6cbfb2f8339..701ddb7d840 100644
--- a/drivers/net/wireless/ath/ar9170/hw.h
+++ b/drivers/net/wireless/ath/ar9170/hw.h
@@ -152,14 +152,14 @@ enum ar9170_cmd {
#define AR9170_MAC_REG_FTF_BIT14 BIT(14)
#define AR9170_MAC_REG_FTF_BIT15 BIT(15)
#define AR9170_MAC_REG_FTF_BAR BIT(24)
-#define AR9170_MAC_REG_FTF_BIT25 BIT(25)
+#define AR9170_MAC_REG_FTF_BA BIT(25)
#define AR9170_MAC_REG_FTF_PSPOLL BIT(26)
#define AR9170_MAC_REG_FTF_RTS BIT(27)
#define AR9170_MAC_REG_FTF_CTS BIT(28)
#define AR9170_MAC_REG_FTF_ACK BIT(29)
#define AR9170_MAC_REG_FTF_CFE BIT(30)
#define AR9170_MAC_REG_FTF_CFE_ACK BIT(31)
-#define AR9170_MAC_REG_FTF_DEFAULTS 0x0500ffff
+#define AR9170_MAC_REG_FTF_DEFAULTS 0x0700ffff
#define AR9170_MAC_REG_FTF_MONITOR 0xfd00ffff
#define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6A0)
@@ -311,6 +311,8 @@ struct ar9170_tx_control {
#define AR9170_TX_PHY_SHORT_GI 0x80000000
+#define AR5416_MAX_RATE_POWER 63
+
struct ar9170_rx_head {
u8 plcp[12];
} __packed;
diff --git a/drivers/net/wireless/ath/ar9170/mac.c b/drivers/net/wireless/ath/ar9170/mac.c
index 614e3218a2b..ddc8c09dc79 100644
--- a/drivers/net/wireless/ath/ar9170/mac.c
+++ b/drivers/net/wireless/ath/ar9170/mac.c
@@ -35,6 +35,9 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+
+#include <asm/unaligned.h>
+
#include "ar9170.h"
#include "cmd.h"
@@ -227,11 +230,8 @@ static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac)
ar9170_regwrite_begin(ar);
- ar9170_regwrite(reg,
- (mac[3] << 24) | (mac[2] << 16) |
- (mac[1] << 8) | mac[0]);
-
- ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]);
+ ar9170_regwrite(reg, get_unaligned_le32(mac));
+ ar9170_regwrite(reg + 4, get_unaligned_le16(mac + 4));
ar9170_regwrite_finish();
@@ -311,13 +311,14 @@ static int ar9170_set_promiscouous(struct ar9170 *ar)
int ar9170_set_operating_mode(struct ar9170 *ar)
{
+ struct ath_common *common = &ar->common;
u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS;
u8 *mac_addr, *bssid;
int err;
if (ar->vif) {
- mac_addr = ar->mac_addr;
- bssid = ar->bssid;
+ mac_addr = common->macaddr;
+ bssid = common->curbssid;
switch (ar->vif->type) {
case NL80211_IFTYPE_MESH_POINT:
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index c1f8c69db16..f9d6db8d013 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -414,9 +414,9 @@ static void ar9170_tx_ampdu_callback(struct ar9170 *ar, struct sk_buff *skb)
skb_queue_tail(&ar->tx_status_ampdu, skb);
ar9170_tx_fake_ampdu_status(ar);
- ar->tx_ampdu_pending--;
- if (!list_empty(&ar->tx_ampdu_list) && !ar->tx_ampdu_pending)
+ if (atomic_dec_and_test(&ar->tx_ampdu_pending) &&
+ !list_empty(&ar->tx_ampdu_list))
ar9170_tx_ampdu(ar);
}
@@ -850,6 +850,7 @@ static int ar9170_rx_mac_status(struct ar9170 *ar,
}
break;
+ case AR9170_RX_STATUS_MODULATION_DUPOFDM:
case AR9170_RX_STATUS_MODULATION_OFDM:
switch (head->plcp[0] & 0xf) {
case 0xb:
@@ -897,8 +898,7 @@ static int ar9170_rx_mac_status(struct ar9170 *ar,
status->flag |= RX_FLAG_HT;
break;
- case AR9170_RX_STATUS_MODULATION_DUPOFDM:
- /* XXX */
+ default:
if (ar9170_nag_limiter(ar))
printk(KERN_ERR "%s: invalid modulation\n",
wiphy_name(ar->hw->wiphy));
@@ -1248,6 +1248,7 @@ static int ar9170_op_start(struct ieee80211_hw *hw)
ar->global_ampdu_density = 6;
ar->global_ampdu_factor = 3;
+ atomic_set(&ar->tx_ampdu_pending, 0);
ar->bad_hw_nagger = jiffies;
err = ar->open(ar);
@@ -1773,7 +1774,7 @@ static void ar9170_tx(struct ar9170 *ar)
msecs_to_jiffies(AR9170_TX_TIMEOUT);
if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK)
- ar->tx_ampdu_pending++;
+ atomic_inc(&ar->tx_ampdu_pending);
#ifdef AR9170_QUEUE_DEBUG
printk(KERN_DEBUG "%s: send frame q:%d =>\n",
@@ -1784,7 +1785,7 @@ static void ar9170_tx(struct ar9170 *ar)
err = ar->tx(ar, skb);
if (unlikely(err)) {
if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK)
- ar->tx_ampdu_pending--;
+ atomic_dec(&ar->tx_ampdu_pending);
frames_failed++;
dev_kfree_skb_any(skb);
@@ -1931,7 +1932,7 @@ int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (info->flags & IEEE80211_TX_CTL_AMPDU) {
bool run = ar9170_tx_ampdu_queue(ar, skb);
- if (run || !ar->tx_ampdu_pending)
+ if (run || !atomic_read(&ar->tx_ampdu_pending))
ar9170_tx_ampdu(ar);
} else {
unsigned int queue = skb_get_queue_mapping(skb);
@@ -1952,6 +1953,7 @@ static int ar9170_op_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct ar9170 *ar = hw->priv;
+ struct ath_common *common = &ar->common;
int err = 0;
mutex_lock(&ar->mutex);
@@ -1962,7 +1964,7 @@ static int ar9170_op_add_interface(struct ieee80211_hw *hw,
}
ar->vif = conf->vif;
- memcpy(ar->mac_addr, conf->mac_addr, ETH_ALEN);
+ memcpy(common->macaddr, conf->mac_addr, ETH_ALEN);
if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) {
ar->rx_software_decryption = true;
@@ -2131,12 +2133,13 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw,
u32 changed)
{
struct ar9170 *ar = hw->priv;
+ struct ath_common *common = &ar->common;
int err = 0;
mutex_lock(&ar->mutex);
if (changed & BSS_CHANGED_BSSID) {
- memcpy(ar->bssid, bss_conf->bssid, ETH_ALEN);
+ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
err = ar9170_set_operating_mode(ar);
if (err)
goto out;
@@ -2190,22 +2193,30 @@ static u64 ar9170_op_get_tsf(struct ieee80211_hw *hw)
{
struct ar9170 *ar = hw->priv;
int err;
- u32 tsf_low;
- u32 tsf_high;
u64 tsf;
+#define NR 3
+ static const u32 addr[NR] = { AR9170_MAC_REG_TSF_H,
+ AR9170_MAC_REG_TSF_L,
+ AR9170_MAC_REG_TSF_H };
+ u32 val[NR];
+ int loops = 0;
mutex_lock(&ar->mutex);
- err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_L, &tsf_low);
- if (!err)
- err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_H, &tsf_high);
+
+ while (loops++ < 10) {
+ err = ar9170_read_mreg(ar, NR, addr, val);
+ if (err || val[0] == val[2])
+ break;
+ }
+
mutex_unlock(&ar->mutex);
if (WARN_ON(err))
return 0;
-
- tsf = tsf_high;
- tsf = (tsf << 32) | tsf_low;
+ tsf = val[0];
+ tsf = (tsf << 32) | val[1];
return tsf;
+#undef NR
}
static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -2430,6 +2441,7 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
}
static int ar9170_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
@@ -2459,7 +2471,7 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw,
tid_info->state = AR9170_TID_STATE_PROGRESS;
tid_info->active = false;
spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
- ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid);
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
@@ -2469,7 +2481,7 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw,
tid_info->active = false;
skb_queue_purge(&tid_info->queue);
spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags);
- ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid);
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
diff --git a/drivers/net/wireless/ath/ar9170/phy.c b/drivers/net/wireless/ath/ar9170/phy.c
index dbd488da18b..45a415ea809 100644
--- a/drivers/net/wireless/ath/ar9170/phy.c
+++ b/drivers/net/wireless/ath/ar9170/phy.c
@@ -1239,9 +1239,6 @@ static u8 ar9170_get_max_edge_power(struct ar9170 *ar,
struct ar9170_calctl_edges edges[],
u32 freq)
{
-/* TODO: move somewhere else */
-#define AR5416_MAX_RATE_POWER 63
-
int i;
u8 rc = AR5416_MAX_RATE_POWER;
u8 f;
@@ -1259,10 +1256,11 @@ static u8 ar9170_get_max_edge_power(struct ar9170 *ar,
break;
}
if (i > 0 && f < edges[i].channel) {
- if (f > edges[i-1].channel &&
- edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) {
+ if (f > edges[i - 1].channel &&
+ edges[i - 1].power_flags &
+ AR9170_CALCTL_EDGE_FLAGS) {
/* lower channel has the inband flag set */
- rc = edges[i-1].power_flags &
+ rc = edges[i - 1].power_flags &
~AR9170_CALCTL_EDGE_FLAGS;
}
break;
@@ -1270,18 +1268,48 @@ static u8 ar9170_get_max_edge_power(struct ar9170 *ar,
}
if (i == AR5416_NUM_BAND_EDGES) {
- if (f > edges[i-1].channel &&
- edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) {
+ if (f > edges[i - 1].channel &&
+ edges[i - 1].power_flags & AR9170_CALCTL_EDGE_FLAGS) {
/* lower channel has the inband flag set */
- rc = edges[i-1].power_flags &
+ rc = edges[i - 1].power_flags &
~AR9170_CALCTL_EDGE_FLAGS;
}
}
return rc;
}
-/* calculate the conformance test limits and apply them to ar->power*
- * (derived from otus hal/hpmain.c, line 3706 ff.)
+static u8 ar9170_get_heavy_clip(struct ar9170 *ar,
+ struct ar9170_calctl_edges edges[],
+ u32 freq, enum ar9170_bw bw)
+{
+ u8 f;
+ int i;
+ u8 rc = 0;
+
+ if (freq < 3000)
+ f = freq - 2300;
+ else
+ f = (freq - 4800) / 5;
+
+ if (bw == AR9170_BW_40_BELOW || bw == AR9170_BW_40_ABOVE)
+ rc |= 0xf0;
+
+ for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) {
+ if (edges[i].channel == 0xff)
+ break;
+ if (f == edges[i].channel) {
+ if (!(edges[i].power_flags & AR9170_CALCTL_EDGE_FLAGS))
+ rc |= 0x0f;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * calculate the conformance test limits and the heavy clip parameter
+ * and apply them to ar->power* (derived from otus hal/hpmain.c, line 3706)
*/
static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
{
@@ -1295,7 +1323,8 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
int pwr_cal_len;
} *modes;
- /* order is relevant in the mode_list_*: we fall back to the
+ /*
+ * order is relevant in the mode_list_*: we fall back to the
* lower indices if any mode is missed in the EEPROM.
*/
struct ctl_modes mode_list_2ghz[] = {
@@ -1313,7 +1342,10 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
#define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n])
- /* TODO: investigate the differences between OTUS'
+ ar->phy_heavy_clip = 0;
+
+ /*
+ * TODO: investigate the differences between OTUS'
* hpreg.c::zfHpGetRegulatoryDomain() and
* ath/regd.c::ath_regd_get_band_ctl() -
* e.g. for FCC3_WORLD the OTUS procedure
@@ -1347,6 +1379,15 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
if (ctl_idx < AR5416_NUM_CTLS) {
int f_off = 0;
+ /* determine heav clip parameter from
+ the 11G edges array */
+ if (modes[i].ctl_mode == CTL_11G) {
+ ar->phy_heavy_clip =
+ ar9170_get_heavy_clip(ar,
+ EDGES(ctl_idx, 1),
+ freq, bw);
+ }
+
/* adjust freq for 40MHz */
if (modes[i].ctl_mode == CTL_2GHT40 ||
modes[i].ctl_mode == CTL_5GHT40) {
@@ -1360,13 +1401,15 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
ar9170_get_max_edge_power(ar, EDGES(ctl_idx, 1),
freq+f_off);
- /* TODO: check if the regulatory max. power is
+ /*
+ * TODO: check if the regulatory max. power is
* controlled by cfg80211 for DFS
* (hpmain applies it to max_power itself for DFS freq)
*/
} else {
- /* Workaround in otus driver, hpmain.c, line 3906:
+ /*
+ * Workaround in otus driver, hpmain.c, line 3906:
* if no data for 5GHT20 are found, take the
* legacy 5G value.
* We extend this here to fallback from any other *HT or
@@ -1390,6 +1433,19 @@ static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
modes[i].max_power);
}
}
+
+ if (ar->phy_heavy_clip & 0xf0) {
+ ar->power_2G_ht40[0]--;
+ ar->power_2G_ht40[1]--;
+ ar->power_2G_ht40[2]--;
+ }
+ if (ar->phy_heavy_clip & 0xf) {
+ ar->power_2G_ht20[0]++;
+ ar->power_2G_ht20[1]++;
+ ar->power_2G_ht20[2]++;
+ }
+
+
#undef EDGES
}
@@ -1499,8 +1555,6 @@ static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
/* calc. conformance test limits and apply to ar->power*[] */
ar9170_calc_ctl(ar, freq, bw);
- /* TODO: (heavy clip) regulatory domain power level fine-tuning. */
-
/* set ACK/CTS TX power */
ar9170_regwrite_begin(ar);
@@ -1643,6 +1697,17 @@ int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
if (err)
return err;
+ if (ar->phy_heavy_clip) {
+ err = ar9170_write_reg(ar, 0x1c59e0,
+ 0x200 | ar->phy_heavy_clip);
+ if (err) {
+ if (ar9170_nag_limiter(ar))
+ printk(KERN_ERR "%s: failed to set "
+ "heavy clip\n",
+ wiphy_name(ar->hw->wiphy));
+ }
+ }
+
for (i = 0; i < 2; i++) {
ar->noise[i] = ar9170_calc_noise_dbm(
(le32_to_cpu(vals[2 + i]) >> 19) & 0x1ff);
diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c
index e974e5829e1..e0799d92405 100644
--- a/drivers/net/wireless/ath/ar9170/usb.c
+++ b/drivers/net/wireless/ath/ar9170/usb.c
@@ -68,8 +68,10 @@ static struct usb_device_id ar9170_usb_ids[] = {
{ USB_DEVICE(0x0cf3, 0x1002) },
/* Cace Airpcap NX */
{ USB_DEVICE(0xcace, 0x0300) },
- /* D-Link DWA 160A */
+ /* D-Link DWA 160 A1 */
{ USB_DEVICE(0x07d1, 0x3c10) },
+ /* D-Link DWA 160 A2 */
+ { USB_DEVICE(0x07d1, 0x3a09) },
/* Netgear WNDA3100 */
{ USB_DEVICE(0x0846, 0x9010) },
/* Netgear WN111 v2 */
@@ -108,15 +110,15 @@ static void ar9170_usb_submit_urb(struct ar9170_usb *aru)
return ;
spin_lock_irqsave(&aru->tx_urb_lock, flags);
- if (aru->tx_submitted_urbs >= AR9170_NUM_TX_URBS) {
+ if (atomic_read(&aru->tx_submitted_urbs) >= AR9170_NUM_TX_URBS) {
spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
return ;
}
- aru->tx_submitted_urbs++;
+ atomic_inc(&aru->tx_submitted_urbs);
urb = usb_get_from_anchor(&aru->tx_pending);
if (!urb) {
- aru->tx_submitted_urbs--;
+ atomic_dec(&aru->tx_submitted_urbs);
spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
return ;
@@ -133,7 +135,7 @@ static void ar9170_usb_submit_urb(struct ar9170_usb *aru)
err);
usb_unanchor_urb(urb);
- aru->tx_submitted_urbs--;
+ atomic_dec(&aru->tx_submitted_urbs);
ar9170_tx_callback(&aru->common, urb->context);
}
@@ -151,7 +153,7 @@ static void ar9170_usb_tx_urb_complete_frame(struct urb *urb)
return ;
}
- aru->tx_submitted_urbs--;
+ atomic_dec(&aru->tx_submitted_urbs);
ar9170_tx_callback(&aru->common, skb);
@@ -794,7 +796,7 @@ static int ar9170_usb_probe(struct usb_interface *intf,
spin_lock_init(&aru->tx_urb_lock);
aru->tx_pending_urbs = 0;
- aru->tx_submitted_urbs = 0;
+ atomic_set(&aru->tx_submitted_urbs, 0);
aru->common.stop = ar9170_usb_stop;
aru->common.flush = ar9170_usb_flush;
diff --git a/drivers/net/wireless/ath/ar9170/usb.h b/drivers/net/wireless/ath/ar9170/usb.h
index d098f4d5d2f..a2ce3b169ce 100644
--- a/drivers/net/wireless/ath/ar9170/usb.h
+++ b/drivers/net/wireless/ath/ar9170/usb.h
@@ -67,7 +67,7 @@ struct ar9170_usb {
bool req_one_stage_fw;
spinlock_t tx_urb_lock;
- unsigned int tx_submitted_urbs;
+ atomic_t tx_submitted_urbs;
unsigned int tx_pending_urbs;
struct completion cmd_wait;
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index a63e90cbf9e..9e05648356f 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -18,6 +18,35 @@
#define ATH_H
#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <net/mac80211.h>
+
+/*
+ * The key cache is used for h/w cipher state and also for
+ * tracking station state such as the current tx antenna.
+ * We also setup a mapping table between key cache slot indices
+ * and station state to short-circuit node lookups on rx.
+ * Different parts have different size key caches. We handle
+ * up to ATH_KEYMAX entries (could dynamically allocate state).
+ */
+#define ATH_KEYMAX 128 /* max key cache size we handle */
+
+static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+struct ath_ani {
+ bool caldone;
+ int16_t noise_floor;
+ unsigned int longcal_timer;
+ unsigned int shortcal_timer;
+ unsigned int resetcal_timer;
+ unsigned int checkani_timer;
+ struct timer_list timer;
+};
+
+enum ath_device_state {
+ ATH_HW_UNAVAILABLE,
+ ATH_HW_INITIALIZED,
+};
struct reg_dmn_pair_mapping {
u16 regDmnEnum;
@@ -36,13 +65,53 @@ struct ath_regulatory {
struct reg_dmn_pair_mapping *regpair;
};
+struct ath_ops {
+ unsigned int (*read)(void *, u32 reg_offset);
+ void (*write)(void *, u32 val, u32 reg_offset);
+};
+
+struct ath_common;
+
+struct ath_bus_ops {
+ void (*read_cachesize)(struct ath_common *common, int *csz);
+ void (*cleanup)(struct ath_common *common);
+ bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data);
+ void (*bt_coex_prep)(struct ath_common *common);
+};
+
struct ath_common {
+ void *ah;
+ void *priv;
+ struct ieee80211_hw *hw;
+ int debug_mask;
+ enum ath_device_state state;
+
+ struct ath_ani ani;
+
u16 cachelsz;
+ u16 curaid;
+ u8 macaddr[ETH_ALEN];
+ u8 curbssid[ETH_ALEN];
+ u8 bssidmask[ETH_ALEN];
+
+ u8 tx_chainmask;
+ u8 rx_chainmask;
+
+ u32 rx_bufsize;
+
+ u32 keymax;
+ DECLARE_BITMAP(keymap, ATH_KEYMAX);
+ u8 splitmic;
+
struct ath_regulatory regulatory;
+ const struct ath_ops *ops;
+ const struct ath_bus_ops *bus_ops;
};
struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
u32 len,
gfp_t gfp_mask);
+void ath_hw_setbssidmask(struct ath_common *common);
+
#endif /* ATH_H */
diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
index 06d006675d7..eb83b7b4d0e 100644
--- a/drivers/net/wireless/ath/ath5k/Kconfig
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -1,6 +1,6 @@
config ATH5K
tristate "Atheros 5xxx wireless cards support"
- depends on PCI && MAC80211 && WLAN_80211
+ depends on PCI && MAC80211
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 6cd5efcec41..6a2a9676111 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -35,6 +35,7 @@
* TODO: Make a more generic struct (eg. add more stuff to ath5k_capabilities)
* and clean up common bits, then introduce set/get functions in eeprom.c */
#include "eeprom.h"
+#include "../ath.h"
/* PCI IDs */
#define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */
@@ -165,13 +166,6 @@
#define AR5K_INI_VAL_XR 0
#define AR5K_INI_VAL_MAX 5
-/* Used for BSSID etc manipulation */
-#define AR5K_LOW_ID(_a)( \
-(_a)[0] | (_a)[1] << 8 | (_a)[2] << 16 | (_a)[3] << 24 \
-)
-
-#define AR5K_HIGH_ID(_a) ((_a)[4] | (_a)[5] << 8)
-
/*
* Some tuneable values (these should be changeable by the user)
* TODO: Make use of them and add more options OR use debug/configfs
@@ -204,6 +198,7 @@
#define AR5K_TUNE_CWMAX_11B 1023
#define AR5K_TUNE_CWMAX_XR 7
#define AR5K_TUNE_NOISE_FLOOR -72
+#define AR5K_TUNE_CCA_MAX_GOOD_VALUE -95
#define AR5K_TUNE_MAX_TXPOWER 63
#define AR5K_TUNE_DEFAULT_TXPOWER 25
#define AR5K_TUNE_TPC_TXPOWER false
@@ -1012,6 +1007,14 @@ struct ath5k_capabilities {
} cap_queues;
};
+/* size of noise floor history (keep it a power of two) */
+#define ATH5K_NF_CAL_HIST_MAX 8
+struct ath5k_nfcal_hist
+{
+ s16 index; /* current index into nfval */
+ s16 nfval[ATH5K_NF_CAL_HIST_MAX]; /* last few noise floors */
+};
+
/***************************************\
HARDWARE ABSTRACTION LAYER STRUCTURE
@@ -1027,6 +1030,7 @@ struct ath5k_capabilities {
/* TODO: Clean up and merge with ath5k_softc */
struct ath5k_hw {
u32 ah_magic;
+ struct ath_common common;
struct ath5k_softc *ah_sc;
void __iomem *ah_iobase;
@@ -1067,14 +1071,6 @@ struct ath5k_hw {
u8 ah_def_ant;
bool ah_software_retry;
- u8 ah_sta_id[ETH_ALEN];
-
- /* Current BSSID we are trying to assoc to / create.
- * This is passed by mac80211 on config_interface() and cached here for
- * use in resets */
- u8 ah_bssid[ETH_ALEN];
- u8 ah_bssid_mask[ETH_ALEN];
-
int ah_gpio_npins;
struct ath5k_capabilities ah_capabilities;
@@ -1125,6 +1121,8 @@ struct ath5k_hw {
struct ieee80211_channel r_last_channel;
} ah_radar;
+ struct ath5k_nfcal_hist ah_nfcal_hist;
+
/* noise floor from last periodic calibration */
s32 ah_noise_floor;
@@ -1160,7 +1158,7 @@ struct ath5k_hw {
*/
/* Attach/Detach Functions */
-extern struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc);
+extern int ath5k_hw_attach(struct ath5k_softc *sc);
extern void ath5k_hw_detach(struct ath5k_hw *ah);
/* LED functions */
@@ -1203,10 +1201,9 @@ extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah);
/* Protocol Control Unit Functions */
extern int ath5k_hw_set_opmode(struct ath5k_hw *ah);
/* BSSID Functions */
-extern void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac);
extern int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac);
-extern void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id);
-extern int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask);
+extern void ath5k_hw_set_associd(struct ath5k_hw *ah);
+extern void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask);
/* Receive start/stop functions */
extern void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah);
extern void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah);
@@ -1288,8 +1285,10 @@ extern int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah);
extern bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags);
extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel);
/* PHY calibration */
+void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah);
extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel);
extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq);
+extern s16 ath5k_hw_get_noise_floor(struct ath5k_hw *ah);
extern void ath5k_hw_calibration_poll(struct ath5k_hw *ah);
/* Spur mitigation */
bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
@@ -1329,17 +1328,21 @@ static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo)
return turbo ? (clock / 80) : (clock / 40);
}
-/*
- * Read from a register
- */
+static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah)
+{
+ return &ah->common;
+}
+
+static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah)
+{
+ return &(ath5k_hw_common(ah)->regulatory);
+}
+
static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
{
return ioread32(ah->ah_iobase + reg);
}
-/*
- * Write to a register
- */
static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg)
{
iowrite32(val, ah->ah_iobase + reg);
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c
index 71a1bd25451..42284445b75 100644
--- a/drivers/net/wireless/ath/ath5k/attach.c
+++ b/drivers/net/wireless/ath/ath5k/attach.c
@@ -101,25 +101,15 @@ static int ath5k_hw_post(struct ath5k_hw *ah)
* -ENODEV if the device is not supported or prints an error msg if something
* else went wrong.
*/
-struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc)
+int ath5k_hw_attach(struct ath5k_softc *sc)
{
- struct ath5k_hw *ah;
+ struct ath5k_hw *ah = sc->ah;
+ struct ath_common *common = ath5k_hw_common(ah);
struct pci_dev *pdev = sc->pdev;
struct ath5k_eeprom_info *ee;
int ret;
u32 srev;
- /*If we passed the test malloc a ath5k_hw struct*/
- ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
- if (ah == NULL) {
- ret = -ENOMEM;
- ATH5K_ERR(sc, "out of memory\n");
- goto err;
- }
-
- ah->ah_sc = sc;
- ah->ah_iobase = sc->iobase;
-
/*
* HW information
*/
@@ -278,12 +268,12 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc)
goto err_free;
}
+ ee = &ah->ah_capabilities.cap_eeprom;
+
/*
* Write PCI-E power save settings
*/
if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) {
- struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
-
ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES);
@@ -321,7 +311,6 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc)
}
/* Crypto settings */
- ee = &ah->ah_capabilities.cap_eeprom;
ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 &&
(ee->ee_version >= AR5K_EEPROM_VERSION_5_0 &&
!AR5K_EEPROM_AES_DIS(ee->ee_misc5));
@@ -336,20 +325,21 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc)
ath5k_hw_set_lladdr(ah, (u8[ETH_ALEN]){});
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
- memset(ah->ah_bssid, 0xff, ETH_ALEN);
- ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
+ memcpy(common->curbssid, ath_bcast_mac, ETH_ALEN);
+ ath5k_hw_set_associd(ah);
ath5k_hw_set_opmode(ah);
ath5k_hw_rfgain_opt_init(ah);
+ ath5k_hw_init_nfcal_hist(ah);
+
/* turn on HW LEDs */
ath5k_hw_set_ledstate(ah, AR5K_LED_INIT);
- return ah;
+ return 0;
err_free:
kfree(ah);
-err:
- return ERR_PTR(ret);
+ return ret;
}
/**
@@ -369,5 +359,4 @@ void ath5k_hw_detach(struct ath5k_hw *ah)
ath5k_eeprom_detach(ah);
/* assume interrupts are down */
- kfree(ah);
}
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 95a8e232b58..a4c086f069b 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -195,12 +195,13 @@ static int __devinit ath5k_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id);
static void __devexit ath5k_pci_remove(struct pci_dev *pdev);
#ifdef CONFIG_PM
-static int ath5k_pci_suspend(struct pci_dev *pdev,
- pm_message_t state);
-static int ath5k_pci_resume(struct pci_dev *pdev);
+static int ath5k_pci_suspend(struct device *dev);
+static int ath5k_pci_resume(struct device *dev);
+
+SIMPLE_DEV_PM_OPS(ath5k_pm_ops, ath5k_pci_suspend, ath5k_pci_resume);
+#define ATH5K_PM_OPS (&ath5k_pm_ops)
#else
-#define ath5k_pci_suspend NULL
-#define ath5k_pci_resume NULL
+#define ATH5K_PM_OPS NULL
#endif /* CONFIG_PM */
static struct pci_driver ath5k_pci_driver = {
@@ -208,8 +209,7 @@ static struct pci_driver ath5k_pci_driver = {
.id_table = ath5k_pci_id_table,
.probe = ath5k_pci_probe,
.remove = __devexit_p(ath5k_pci_remove),
- .suspend = ath5k_pci_suspend,
- .resume = ath5k_pci_resume,
+ .driver.pm = ATH5K_PM_OPS,
};
@@ -323,10 +323,13 @@ static inline void ath5k_txbuf_free(struct ath5k_softc *sc,
static inline void ath5k_rxbuf_free(struct ath5k_softc *sc,
struct ath5k_buf *bf)
{
+ struct ath5k_hw *ah = sc->ah;
+ struct ath_common *common = ath5k_hw_common(ah);
+
BUG_ON(!bf);
if (!bf->skb)
return;
- pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
+ pci_unmap_single(sc->pdev, bf->skbaddr, common->rx_bufsize,
PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(bf->skb);
bf->skb = NULL;
@@ -437,6 +440,22 @@ ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val)
return name;
}
+static unsigned int ath5k_ioread32(void *hw_priv, u32 reg_offset)
+{
+ struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv;
+ return ath5k_hw_reg_read(ah, reg_offset);
+}
+
+static void ath5k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
+{
+ struct ath5k_hw *ah = (struct ath5k_hw *) hw_priv;
+ ath5k_hw_reg_write(ah, val, reg_offset);
+}
+
+static const struct ath_ops ath5k_common_ops = {
+ .read = ath5k_ioread32,
+ .write = ath5k_iowrite32,
+};
static int __devinit
ath5k_pci_probe(struct pci_dev *pdev,
@@ -444,6 +463,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
{
void __iomem *mem;
struct ath5k_softc *sc;
+ struct ath_common *common;
struct ieee80211_hw *hw;
int ret;
u8 csz;
@@ -547,7 +567,6 @@ ath5k_pci_probe(struct pci_dev *pdev,
__set_bit(ATH_STAT_INVALID, sc->status);
sc->iobase = mem; /* So we can unmap it on detach */
- sc->common.cachelsz = csz << 2; /* convert to bytes */
sc->opmode = NL80211_IFTYPE_STATION;
sc->bintval = 1000;
mutex_init(&sc->lock);
@@ -565,13 +584,28 @@ ath5k_pci_probe(struct pci_dev *pdev,
goto err_free;
}
- /* Initialize device */
- sc->ah = ath5k_hw_attach(sc);
- if (IS_ERR(sc->ah)) {
- ret = PTR_ERR(sc->ah);
+ /*If we passed the test malloc a ath5k_hw struct*/
+ sc->ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
+ if (!sc->ah) {
+ ret = -ENOMEM;
+ ATH5K_ERR(sc, "out of memory\n");
goto err_irq;
}
+ sc->ah->ah_sc = sc;
+ sc->ah->ah_iobase = sc->iobase;
+ common = ath5k_hw_common(sc->ah);
+ common->ops = &ath5k_common_ops;
+ common->ah = sc->ah;
+ common->hw = hw;
+ common->cachelsz = csz << 2; /* convert to bytes */
+
+ /* Initialize device */
+ ret = ath5k_hw_attach(sc);
+ if (ret) {
+ goto err_free_ah;
+ }
+
/* set up multi-rate retry capabilities */
if (sc->ah->ah_version == AR5K_AR5212) {
hw->max_rates = 4;
@@ -640,6 +674,8 @@ err_ah:
ath5k_hw_detach(sc->ah);
err_irq:
free_irq(pdev->irq, sc);
+err_free_ah:
+ kfree(sc->ah);
err_free:
ieee80211_free_hw(hw);
err_map:
@@ -661,6 +697,7 @@ ath5k_pci_remove(struct pci_dev *pdev)
ath5k_debug_finish_device(sc);
ath5k_detach(pdev, hw);
ath5k_hw_detach(sc->ah);
+ kfree(sc->ah);
free_irq(pdev->irq, sc);
pci_iounmap(pdev, sc->iobase);
pci_release_region(pdev, 0);
@@ -669,33 +706,20 @@ ath5k_pci_remove(struct pci_dev *pdev)
}
#ifdef CONFIG_PM
-static int
-ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+static int ath5k_pci_suspend(struct device *dev)
{
- struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+ struct ieee80211_hw *hw = pci_get_drvdata(to_pci_dev(dev));
struct ath5k_softc *sc = hw->priv;
ath5k_led_off(sc);
-
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
-
return 0;
}
-static int
-ath5k_pci_resume(struct pci_dev *pdev)
+static int ath5k_pci_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath5k_softc *sc = hw->priv;
- int err;
-
- pci_restore_state(pdev);
-
- err = pci_enable_device(pdev);
- if (err)
- return err;
/*
* Suspend/Resume resets the PCI configuration space, so we have to
@@ -718,7 +742,7 @@ static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *re
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct ath5k_softc *sc = hw->priv;
- struct ath_regulatory *regulatory = &sc->common.regulatory;
+ struct ath_regulatory *regulatory = ath5k_hw_regulatory(sc->ah);
return ath_reg_notifier_apply(wiphy, request, regulatory);
}
@@ -728,7 +752,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
- struct ath_regulatory *regulatory = &sc->common.regulatory;
+ struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah);
u8 mac[ETH_ALEN] = {};
int ret;
@@ -815,7 +839,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
SET_IEEE80211_PERM_ADDR(hw, mac);
/* All MAC address bits matter for ACKs */
- memset(sc->bssidmask, 0xff, ETH_ALEN);
+ memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain;
@@ -1152,24 +1176,26 @@ ath5k_hw_to_driver_rix(struct ath5k_softc *sc, int hw_rix)
static
struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr)
{
+ struct ath_common *common = ath5k_hw_common(sc->ah);
struct sk_buff *skb;
/*
* Allocate buffer with headroom_needed space for the
* fake physical layer header at the start.
*/
- skb = ath_rxbuf_alloc(&sc->common,
- sc->rxbufsize + sc->common.cachelsz - 1,
+ skb = ath_rxbuf_alloc(common,
+ common->rx_bufsize,
GFP_ATOMIC);
if (!skb) {
ATH5K_ERR(sc, "can't alloc skbuff of size %u\n",
- sc->rxbufsize + sc->common.cachelsz - 1);
+ common->rx_bufsize);
return NULL;
}
*skb_addr = pci_map_single(sc->pdev,
- skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE);
+ skb->data, common->rx_bufsize,
+ PCI_DMA_FROMDEVICE);
if (unlikely(pci_dma_mapping_error(sc->pdev, *skb_addr))) {
ATH5K_ERR(sc, "%s: DMA mapping failed\n", __func__);
dev_kfree_skb(skb);
@@ -1605,13 +1631,14 @@ static int
ath5k_rx_start(struct ath5k_softc *sc)
{
struct ath5k_hw *ah = sc->ah;
+ struct ath_common *common = ath5k_hw_common(ah);
struct ath5k_buf *bf;
int ret;
- sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->common.cachelsz);
+ common->rx_bufsize = roundup(IEEE80211_MAX_LEN, common->cachelsz);
- ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rxbufsize %u\n",
- sc->common.cachelsz, sc->rxbufsize);
+ ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rx_bufsize %u\n",
+ common->cachelsz, common->rx_bufsize);
spin_lock_bh(&sc->rxbuflock);
sc->rxlink = NULL;
@@ -1656,6 +1683,8 @@ static unsigned int
ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
struct sk_buff *skb, struct ath5k_rx_status *rs)
{
+ struct ath5k_hw *ah = sc->ah;
+ struct ath_common *common = ath5k_hw_common(ah);
struct ieee80211_hdr *hdr = (void *)skb->data;
unsigned int keyix, hlen;
@@ -1672,7 +1701,7 @@ ath5k_rx_decrypted(struct ath5k_softc *sc, struct ath5k_desc *ds,
skb->len >= hlen + 4) {
keyix = skb->data[hlen + 3] >> 6;
- if (test_bit(keyix, sc->keymap))
+ if (test_bit(keyix, common->keymap))
return RX_FLAG_DECRYPTED;
}
@@ -1684,13 +1713,14 @@ static void
ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
struct ieee80211_rx_status *rxs)
{
+ struct ath_common *common = ath5k_hw_common(sc->ah);
u64 tsf, bc_tstamp;
u32 hw_tu;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
if (ieee80211_is_beacon(mgmt->frame_control) &&
le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
- memcmp(mgmt->bssid, sc->ah->ah_bssid, ETH_ALEN) == 0) {
+ memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) {
/*
* Received an IBSS beacon with the same BSSID. Hardware *must*
* have updated the local TSF. We have to work around various
@@ -1745,6 +1775,8 @@ ath5k_tasklet_rx(unsigned long data)
struct sk_buff *skb, *next_skb;
dma_addr_t next_skb_addr;
struct ath5k_softc *sc = (void *)data;
+ struct ath5k_hw *ah = sc->ah;
+ struct ath_common *common = ath5k_hw_common(ah);
struct ath5k_buf *bf;
struct ath5k_desc *ds;
int ret;
@@ -1822,7 +1854,7 @@ accept:
if (!next_skb)
goto next;
- pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
+ pci_unmap_single(sc->pdev, bf->skbaddr, common->rx_bufsize,
PCI_DMA_FROMDEVICE);
skb_put(skb, rs.rs_datalen);
@@ -3008,6 +3040,8 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_key_conf *key)
{
struct ath5k_softc *sc = hw->priv;
+ struct ath5k_hw *ah = sc->ah;
+ struct ath_common *common = ath5k_hw_common(ah);
int ret = 0;
if (modparam_nohwcrypt)
@@ -3040,14 +3074,14 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ATH5K_ERR(sc, "can't set the key\n");
goto unlock;
}
- __set_bit(key->keyidx, sc->keymap);
+ __set_bit(key->keyidx, common->keymap);
key->hw_key_idx = key->keyidx;
key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV |
IEEE80211_KEY_FLAG_GENERATE_MMIC);
break;
case DISABLE_KEY:
ath5k_hw_reset_key(sc->ah, key->keyidx);
- __clear_bit(key->keyidx, sc->keymap);
+ __clear_bit(key->keyidx, common->keymap);
break;
default:
ret = -EINVAL;
@@ -3176,6 +3210,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
{
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
+ struct ath_common *common = ath5k_hw_common(ah);
unsigned long flags;
mutex_lock(&sc->lock);
@@ -3184,10 +3219,9 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
if (changes & BSS_CHANGED_BSSID) {
/* Cache for later use during resets */
- memcpy(ah->ah_bssid, bss_conf->bssid, ETH_ALEN);
- /* XXX: assoc id is set to 0 for now, mac80211 doesn't have
- * a clean way of letting us retrieve this yet. */
- ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
+ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+ common->curaid = 0;
+ ath5k_hw_set_associd(ah);
mmiowb();
}
@@ -3200,6 +3234,14 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
set_beacon_filter(hw, sc->assoc);
ath5k_hw_set_ledstate(sc->ah, sc->assoc ?
AR5K_LED_ASSOC : AR5K_LED_INIT);
+ if (bss_conf->assoc) {
+ ATH5K_DBG(sc, ATH5K_DEBUG_ANY,
+ "Bss Info ASSOC %d, bssid: %pM\n",
+ bss_conf->aid, common->curbssid);
+ common->curaid = bss_conf->aid;
+ ath5k_hw_set_associd(ah);
+ /* Once ANI is available you would start it here */
+ }
}
if (changes & BSS_CHANGED_BEACON) {
diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h
index a28c42f32c9..952b3a21bbc 100644
--- a/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -36,7 +36,7 @@
*/
/*
- * Defintions for the Atheros Wireless LAN controller driver.
+ * Definitions for the Atheros Wireless LAN controller driver.
*/
#ifndef _DEV_ATH_ATHVAR_H
#define _DEV_ATH_ATHVAR_H
@@ -115,7 +115,6 @@ struct ath5k_rfkill {
* associated with an instance of a device */
struct ath5k_softc {
struct pci_dev *pdev; /* for dma mapping */
- struct ath_common common;
void __iomem *iobase; /* address of the device */
struct mutex lock; /* dev-level lock */
struct ieee80211_tx_queue_stats tx_stats[AR5K_NUM_TX_QUEUES];
@@ -154,8 +153,6 @@ struct ath5k_softc {
enum ath5k_int imask; /* interrupt mask copy */
- DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */
-
u8 bssidmask[ETH_ALEN];
unsigned int led_pin, /* GPIO pin for driving LED */
@@ -193,7 +190,7 @@ struct ath5k_softc {
struct ath5k_txq *cabq; /* content after beacon */
int power_level; /* Requested tx power in dbm */
- bool assoc; /* assocate state */
+ bool assoc; /* associate state */
bool enable_beacon; /* true if beacons are on */
};
@@ -202,15 +199,4 @@ struct ath5k_softc {
#define ath5k_hw_hasveol(_ah) \
(ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0)
-static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah)
-{
- return &ah->ah_sc->common;
-}
-
-static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah)
-{
- return &(ath5k_hw_common(ah)->regulatory);
-
-}
-
#endif
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
index 644962adda9..81ea52c4faf 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -1492,7 +1492,7 @@ ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
* This info is used to calibrate the baseband power table. Imagine
* that for each channel there is a power curve that's hw specific
* (depends on amplifier etc) and we try to "correct" this curve using
- * offests we pass on to phy chip (baseband -> before amplifier) so that
+ * offsets we pass on to phy chip (baseband -> before amplifier) so that
* it can use accurate power values when setting tx power (takes amplifier's
* performance on each channel into account).
*
diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c
index 18eb5190ce4..8fa43930882 100644
--- a/drivers/net/wireless/ath/ath5k/initvals.c
+++ b/drivers/net/wireless/ath/ath5k/initvals.c
@@ -560,8 +560,8 @@ static const struct ath5k_ini ar5212_ini_common_start[] = {
{ AR5K_SLEEP0, 0x0002aaaa },
{ AR5K_SLEEP1, 0x02005555 },
{ AR5K_SLEEP2, 0x00000000 },
- { AR5K_BSS_IDM0, 0xffffffff },
- { AR5K_BSS_IDM1, 0x0000ffff },
+ { AR_BSSMSKL, 0xffffffff },
+ { AR_BSSMSKU, 0x0000ffff },
{ AR5K_TXPC, 0x00000000 },
{ AR5K_PROFCNT_TX, 0x00000000 },
{ AR5K_PROFCNT_RX, 0x00000000 },
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index b548c8eaaae..d495890355d 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -59,6 +59,8 @@ static const struct pci_device_id ath5k_led_devices[] = {
{ ATH_SDEVICE(PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID), ATH_LED(1, 1) },
/* Acer Aspire One A150 (maximlevitsky@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe008), ATH_LED(3, 0) },
+ /* Acer Aspire One AO531h AO751h (keng-yu.lin@canonical.com) */
+ { ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe00d), ATH_LED(3, 0) },
/* Acer Ferrari 5000 (russ.dill@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) },
/* E-machines E510 (tuliom@gmail.com) */
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 2942f13c9c4..64fc1eb9b6d 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -24,6 +24,8 @@
* Protocol Control Unit Functions *
\*********************************/
+#include <asm/unaligned.h>
+
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
@@ -44,6 +46,7 @@
*/
int ath5k_hw_set_opmode(struct ath5k_hw *ah)
{
+ struct ath_common *common = ath5k_hw_common(ah);
u32 pcu_reg, beacon_reg, low_id, high_id;
@@ -95,8 +98,8 @@ int ath5k_hw_set_opmode(struct ath5k_hw *ah)
/*
* Set PCU registers
*/
- low_id = AR5K_LOW_ID(ah->ah_sta_id);
- high_id = AR5K_HIGH_ID(ah->ah_sta_id);
+ low_id = get_unaligned_le32(common->macaddr);
+ high_id = get_unaligned_le16(common->macaddr + 4);
ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
@@ -238,28 +241,6 @@ int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout)
return 0;
}
-
-/****************\
-* BSSID handling *
-\****************/
-
-/**
- * ath5k_hw_get_lladdr - Get station id
- *
- * @ah: The &struct ath5k_hw
- * @mac: The card's mac address
- *
- * Initialize ah->ah_sta_id using the mac address provided
- * (just a memcpy).
- *
- * TODO: Remove it once we merge ath5k_softc and ath5k_hw
- */
-void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac)
-{
- ATH5K_TRACE(ah->ah_sc);
- memcpy(mac, ah->ah_sta_id, ETH_ALEN);
-}
-
/**
* ath5k_hw_set_lladdr - Set station id
*
@@ -270,17 +251,18 @@ void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac)
*/
int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac)
{
+ struct ath_common *common = ath5k_hw_common(ah);
u32 low_id, high_id;
u32 pcu_reg;
ATH5K_TRACE(ah->ah_sc);
/* Set new station ID */
- memcpy(ah->ah_sta_id, mac, ETH_ALEN);
+ memcpy(common->macaddr, mac, ETH_ALEN);
pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000;
- low_id = AR5K_LOW_ID(mac);
- high_id = AR5K_HIGH_ID(mac);
+ low_id = get_unaligned_le32(mac);
+ high_id = get_unaligned_le16(mac + 4);
ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0);
ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1);
@@ -297,159 +279,51 @@ int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac)
*
* Sets the BSSID which trigers the "SME Join" operation
*/
-void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id)
+void ath5k_hw_set_associd(struct ath5k_hw *ah)
{
- u32 low_id, high_id;
+ struct ath_common *common = ath5k_hw_common(ah);
u16 tim_offset = 0;
/*
* Set simple BSSID mask on 5212
*/
- if (ah->ah_version == AR5K_AR5212) {
- ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask),
- AR5K_BSS_IDM0);
- ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask),
- AR5K_BSS_IDM1);
- }
+ if (ah->ah_version == AR5K_AR5212)
+ ath_hw_setbssidmask(common);
/*
* Set BSSID which triggers the "SME Join" operation
*/
- low_id = AR5K_LOW_ID(bssid);
- high_id = AR5K_HIGH_ID(bssid);
- ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0);
- ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) <<
- AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1);
-
- if (assoc_id == 0) {
+ ath5k_hw_reg_write(ah,
+ get_unaligned_le32(common->curbssid),
+ AR5K_BSS_ID0);
+ ath5k_hw_reg_write(ah,
+ get_unaligned_le16(common->curbssid + 4) |
+ ((common->curaid & 0x3fff) << AR5K_BSS_ID1_AID_S),
+ AR5K_BSS_ID1);
+
+ if (common->curaid == 0) {
ath5k_hw_disable_pspoll(ah);
return;
}
AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM,
- tim_offset ? tim_offset + 4 : 0);
+ tim_offset ? tim_offset + 4 : 0);
ath5k_hw_enable_pspoll(ah, NULL, 0);
}
-/**
- * ath5k_hw_set_bssid_mask - filter out bssids we listen
- *
- * @ah: the &struct ath5k_hw
- * @mask: the bssid_mask, a u8 array of size ETH_ALEN
- *
- * BSSID masking is a method used by AR5212 and newer hardware to inform PCU
- * which bits of the interface's MAC address should be looked at when trying
- * to decide which packets to ACK. In station mode and AP mode with a single
- * BSS every bit matters since we lock to only one BSS. In AP mode with
- * multiple BSSes (virtual interfaces) not every bit matters because hw must
- * accept frames for all BSSes and so we tweak some bits of our mac address
- * in order to have multiple BSSes.
- *
- * NOTE: This is a simple filter and does *not* filter out all
- * relevant frames. Some frames that are not for us might get ACKed from us
- * by PCU because they just match the mask.
- *
- * When handling multiple BSSes you can get the BSSID mask by computing the
- * set of ~ ( MAC XOR BSSID ) for all bssids we handle.
- *
- * When you do this you are essentially computing the common bits of all your
- * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with
- * the MAC address to obtain the relevant bits and compare the result with
- * (frame's BSSID & mask) to see if they match.
- */
-/*
- * Simple example: on your card you have have two BSSes you have created with
- * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address.
- * There is another BSSID-03 but you are not part of it. For simplicity's sake,
- * assuming only 4 bits for a mac address and for BSSIDs you can then have:
- *
- * \
- * MAC: 0001 |
- * BSSID-01: 0100 | --> Belongs to us
- * BSSID-02: 1001 |
- * /
- * -------------------
- * BSSID-03: 0110 | --> External
- * -------------------
- *
- * Our bssid_mask would then be:
- *
- * On loop iteration for BSSID-01:
- * ~(0001 ^ 0100) -> ~(0101)
- * -> 1010
- * bssid_mask = 1010
- *
- * On loop iteration for BSSID-02:
- * bssid_mask &= ~(0001 ^ 1001)
- * bssid_mask = (1010) & ~(0001 ^ 1001)
- * bssid_mask = (1010) & ~(1001)
- * bssid_mask = (1010) & (0110)
- * bssid_mask = 0010
- *
- * A bssid_mask of 0010 means "only pay attention to the second least
- * significant bit". This is because its the only bit common
- * amongst the MAC and all BSSIDs we support. To findout what the real
- * common bit is we can simply "&" the bssid_mask now with any BSSID we have
- * or our MAC address (we assume the hardware uses the MAC address).
- *
- * Now, suppose there's an incoming frame for BSSID-03:
- *
- * IFRAME-01: 0110
- *
- * An easy eye-inspeciton of this already should tell you that this frame
- * will not pass our check. This is beacuse the bssid_mask tells the
- * hardware to only look at the second least significant bit and the
- * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB
- * as 1, which does not match 0.
- *
- * So with IFRAME-01 we *assume* the hardware will do:
- *
- * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
- * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0;
- * --> allow = (0010) == 0000 ? 1 : 0;
- * --> allow = 0
- *
- * Lets now test a frame that should work:
- *
- * IFRAME-02: 0001 (we should allow)
- *
- * allow = (0001 & 1010) == 1010
- *
- * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
- * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0;
- * --> allow = (0010) == (0010)
- * --> allow = 1
- *
- * Other examples:
- *
- * IFRAME-03: 0100 --> allowed
- * IFRAME-04: 1001 --> allowed
- * IFRAME-05: 1101 --> allowed but its not for us!!!
- *
- */
-int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask)
+void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask)
{
- u32 low_id, high_id;
+ struct ath_common *common = ath5k_hw_common(ah);
ATH5K_TRACE(ah->ah_sc);
/* Cache bssid mask so that we can restore it
* on reset */
- memcpy(ah->ah_bssid_mask, mask, ETH_ALEN);
- if (ah->ah_version == AR5K_AR5212) {
- low_id = AR5K_LOW_ID(mask);
- high_id = AR5K_HIGH_ID(mask);
-
- ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0);
- ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1);
-
- return 0;
- }
-
- return -EIO;
+ memcpy(common->bssidmask, mask, ETH_ALEN);
+ if (ah->ah_version == AR5K_AR5212)
+ ath_hw_setbssidmask(common);
}
-
/************\
* RX Control *
\************/
@@ -1157,14 +1031,17 @@ int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac)
/* Invalid entry (key table overflow) */
AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE);
- /* MAC may be NULL if it's a broadcast key. In this case no need to
- * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */
+ /*
+ * MAC may be NULL if it's a broadcast key. In this case no need to
+ * to compute get_unaligned_le32 and get_unaligned_le16 as we
+ * already know it.
+ */
if (!mac) {
low_id = 0xffffffff;
high_id = 0xffff | AR5K_KEYTABLE_VALID;
} else {
- low_id = AR5K_LOW_ID(mac);
- high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID;
+ low_id = get_unaligned_le32(mac);
+ high_id = get_unaligned_le16(mac + 4) | AR5K_KEYTABLE_VALID;
}
ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry));
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index 1a039f2bd73..72474c0ccaf 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -117,7 +117,7 @@ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah,
/*
* This code is used to optimize rf gain on different environments
- * (temprature mostly) based on feedback from a power detector.
+ * (temperature mostly) based on feedback from a power detector.
*
* It's only used on RF5111 and RF5112, later RF chips seem to have
* auto adjustment on hw -notice they have a much smaller BANK 7 and
@@ -1124,77 +1124,148 @@ ath5k_hw_calibration_poll(struct ath5k_hw *ah)
ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION;
AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI);
}
+}
+static int sign_extend(int val, const int nbits)
+{
+ int order = BIT(nbits-1);
+ return (val ^ order) - order;
}
-/**
- * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration
- *
- * @ah: struct ath5k_hw pointer we are operating on
- * @freq: the channel frequency, just used for error logging
- *
- * This function performs a noise floor calibration of the PHY and waits for
- * it to complete. Then the noise floor value is compared to some maximum
- * noise floor we consider valid.
- *
- * Note that this is different from what the madwifi HAL does: it reads the
- * noise floor and afterwards initiates the calibration. Since the noise floor
- * calibration can take some time to finish, depending on the current channel
- * use, that avoids the occasional timeout warnings we are seeing now.
- *
- * See the following link for an Atheros patent on noise floor calibration:
- * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \
- * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7
+static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah)
+{
+ s32 val;
+
+ val = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
+ return sign_extend(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 9);
+}
+
+void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah)
+{
+ int i;
+
+ ah->ah_nfcal_hist.index = 0;
+ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++)
+ ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
+}
+
+static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor)
+{
+ struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist;
+ hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX-1);
+ hist->nfval[hist->index] = noise_floor;
+}
+
+static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah)
+{
+ s16 sort[ATH5K_NF_CAL_HIST_MAX];
+ s16 tmp;
+ int i, j;
+
+ memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort));
+ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) {
+ for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) {
+ if (sort[j] > sort[j-1]) {
+ tmp = sort[j];
+ sort[j] = sort[j-1];
+ sort[j-1] = tmp;
+ }
+ }
+ }
+ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) {
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ "cal %d:%d\n", i, sort[i]);
+ }
+ return sort[(ATH5K_NF_CAL_HIST_MAX-1) / 2];
+}
+
+/*
+ * When we tell the hardware to perform a noise floor calibration
+ * by setting the AR5K_PHY_AGCCTL_NF bit, it will periodically
+ * sample-and-hold the minimum noise level seen at the antennas.
+ * This value is then stored in a ring buffer of recently measured
+ * noise floor values so we have a moving window of the last few
+ * samples.
*
- * XXX: Since during noise floor calibration antennas are detached according to
- * the patent, we should stop tx queues here.
+ * The median of the values in the history is then loaded into the
+ * hardware for its own use for RSSI and CCA measurements.
*/
-int
-ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq)
+void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
{
- int ret;
- unsigned int i;
- s32 noise_floor;
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ u32 val;
+ s16 nf, threshold;
+ u8 ee_mode;
- /*
- * Enable noise floor calibration
- */
- AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
- AR5K_PHY_AGCCTL_NF);
+ /* keep last value if calibration hasn't completed */
+ if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ "NF did not complete in calibration window\n");
- ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL,
- AR5K_PHY_AGCCTL_NF, 0, false);
- if (ret) {
- ATH5K_ERR(ah->ah_sc,
- "noise floor calibration timeout (%uMHz)\n", freq);
- return -EAGAIN;
+ return;
}
- /* Wait until the noise floor is calibrated and read the value */
- for (i = 20; i > 0; i--) {
- mdelay(1);
- noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
- noise_floor = AR5K_PHY_NF_RVAL(noise_floor);
- if (noise_floor & AR5K_PHY_NF_ACTIVE) {
- noise_floor = AR5K_PHY_NF_AVAL(noise_floor);
-
- if (noise_floor <= AR5K_TUNE_NOISE_FLOOR)
- break;
- }
+ switch (ah->ah_current_channel->hw_value & CHANNEL_MODES) {
+ case CHANNEL_A:
+ case CHANNEL_T:
+ case CHANNEL_XR:
+ ee_mode = AR5K_EEPROM_MODE_11A;
+ break;
+ case CHANNEL_G:
+ case CHANNEL_TG:
+ ee_mode = AR5K_EEPROM_MODE_11G;
+ break;
+ default:
+ case CHANNEL_B:
+ ee_mode = AR5K_EEPROM_MODE_11B;
+ break;
}
- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
- "noise floor %d\n", noise_floor);
- if (noise_floor > AR5K_TUNE_NOISE_FLOOR) {
- ATH5K_ERR(ah->ah_sc,
- "noise floor calibration failed (%uMHz)\n", freq);
- return -EAGAIN;
+ /* completed NF calibration, test threshold */
+ nf = ath5k_hw_read_measured_noise_floor(ah);
+ threshold = ee->ee_noise_floor_thr[ee_mode];
+
+ if (nf > threshold) {
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ "noise floor failure detected; "
+ "read %d, threshold %d\n",
+ nf, threshold);
+
+ nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
}
- ah->ah_noise_floor = noise_floor;
+ ath5k_hw_update_nfcal_hist(ah, nf);
+ nf = ath5k_hw_get_median_noise_floor(ah);
- return 0;
+ /* load noise floor (in .5 dBm) so the hardware will use it */
+ val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M;
+ val |= (nf * 2) & AR5K_PHY_NF_M;
+ ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
+
+ AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
+ ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE));
+
+ ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF,
+ 0, false);
+
+ /*
+ * Load a high max CCA Power value (-50 dBm in .5 dBm units)
+ * so that we're not capped by the median we just loaded.
+ * This will be used as the initial value for the next noise
+ * floor calibration.
+ */
+ val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M);
+ ath5k_hw_reg_write(ah, val, AR5K_PHY_NF);
+ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
+ AR5K_PHY_AGCCTL_NF_EN |
+ AR5K_PHY_AGCCTL_NF_NOUPDATE |
+ AR5K_PHY_AGCCTL_NF);
+
+ ah->ah_noise_floor = nf;
+
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
+ "noise floor calibrated: %d\n", nf);
}
/*
@@ -1287,7 +1358,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
return ret;
}
- ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
+ ath5k_hw_update_noise_floor(ah);
/*
* Re-enable RX/TX and beacons
@@ -1328,7 +1399,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah,
if (i_coffd == 0 || q_coffd == 0)
goto done;
- i_coff = ((-iq_corr) / i_coffd) & 0x3f;
+ i_coff = ((-iq_corr) / i_coffd);
/* Boundary check */
if (i_coff > 31)
@@ -1336,7 +1407,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah,
if (i_coff < -32)
i_coff = -32;
- q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f;
+ q_coff = (((s32)i_pwr / q_coffd) - 128);
/* Boundary check */
if (q_coff > 15)
@@ -1360,7 +1431,7 @@ done:
* since noise floor calibration interrupts rx path while I/Q
* calibration doesn't. We don't need to run noise floor calibration
* as often as I/Q calibration.*/
- ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
+ ath5k_hw_update_noise_floor(ah);
/* Initiate a gain_F calibration */
ath5k_hw_request_rfgain_probe(ah);
@@ -2675,7 +2746,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
/* Fill curves in reverse order
* from lower power (max gain)
* to higher power. Use curve -> idx
- * backmaping we did on eeprom init */
+ * backmapping we did on eeprom init */
u8 idx = pdg_curve_to_idx[pdg];
/* Grab the needed curves by index */
@@ -2777,7 +2848,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
/* Now we have a set of curves for this
* channel on tmpL (x range is table_max - table_min
* and y values are tmpL[pdg][]) sorted in the same
- * order as EEPROM (because we've used the backmaping).
+ * order as EEPROM (because we've used the backmapping).
* So for RF5112 it's from higher power to lower power
* and for RF2413 it's from lower power to higher power.
* For RF5111 we only have one curve. */
@@ -2954,8 +3025,6 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
return -EINVAL;
}
- if (txpower == 0)
- txpower = AR5K_TUNE_DEFAULT_TXPOWER;
/* Reset TX power values */
memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h
index c63ea6afd96..4cb9c5df9f4 100644
--- a/drivers/net/wireless/ath/ath5k/reg.h
+++ b/drivers/net/wireless/ath/ath5k/reg.h
@@ -35,7 +35,7 @@
* released by Atheros and on various debug messages found on the net.
*/
-
+#include "../reg.h"
/*====MAC DMA REGISTERS====*/
@@ -1650,12 +1650,6 @@
#define AR5K_SLEEP2_DTIM_PER_S 16
/*
- * BSSID mask registers
- */
-#define AR5K_BSS_IDM0 0x80e0 /* Upper bits */
-#define AR5K_BSS_IDM1 0x80e4 /* Lower bits */
-
-/*
* TX power control (TPC) register
*
* XXX: PCDAC steps (0.5dbm) or DBM ?
@@ -2039,17 +2033,14 @@
#define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automaticaly */
/*
- * PHY noise floor status register
+ * PHY noise floor status register (CCA = Clear Channel Assessment)
*/
#define AR5K_PHY_NF 0x9864 /* Register address */
-#define AR5K_PHY_NF_M 0x000001ff /* Noise floor mask */
-#define AR5K_PHY_NF_ACTIVE 0x00000100 /* Noise floor calibration still active */
-#define AR5K_PHY_NF_RVAL(_n) (((_n) >> 19) & AR5K_PHY_NF_M)
-#define AR5K_PHY_NF_AVAL(_n) (-((_n) ^ AR5K_PHY_NF_M) + 1)
-#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9))
+#define AR5K_PHY_NF_M 0x000001ff /* Noise floor, written to hardware in 1/2 dBm units */
+#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9))
#define AR5K_PHY_NF_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */
#define AR5K_PHY_NF_THRESH62_S 12
-#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* ??? */
+#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* Minimum measured noise level, read from hardware in 1 dBm units */
#define AR5K_PHY_NF_MINCCA_PWR_S 19
/*
diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
index 34e13c70084..62954fc7786 100644
--- a/drivers/net/wireless/ath/ath5k/reset.c
+++ b/drivers/net/wireless/ath/ath5k/reset.c
@@ -25,6 +25,8 @@
Reset functions and helpers
\*****************************/
+#include <asm/unaligned.h>
+
#include <linux/pci.h> /* To determine if a card is pci-e */
#include <linux/log2.h>
#include "ath5k.h"
@@ -870,6 +872,7 @@ static void ath5k_hw_commit_eeprom_settings(struct ath5k_hw *ah,
int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
struct ieee80211_channel *channel, bool change_channel)
{
+ struct ath_common *common = ath5k_hw_common(ah);
u32 s_seq[10], s_ant, s_led[3], staid1_flags, tsf_up, tsf_lo;
u32 phy_tst1;
u8 mode, freq, ee_mode, ant[2];
@@ -1171,10 +1174,12 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
ath5k_hw_reg_write(ah, s_led[2], AR5K_GPIODO);
/* Restore sta_id flags and preserve our mac address*/
- ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_sta_id),
- AR5K_STA_ID0);
- ath5k_hw_reg_write(ah, staid1_flags | AR5K_HIGH_ID(ah->ah_sta_id),
- AR5K_STA_ID1);
+ ath5k_hw_reg_write(ah,
+ get_unaligned_le32(common->macaddr),
+ AR5K_STA_ID0);
+ ath5k_hw_reg_write(ah,
+ staid1_flags | get_unaligned_le16(common->macaddr + 4),
+ AR5K_STA_ID1);
/*
@@ -1182,8 +1187,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
*/
/* Restore bssid and bssid mask */
- /* XXX: add ah->aid once mac80211 gives this to us */
- ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
+ ath5k_hw_set_associd(ah);
/* Set PCU config */
ath5k_hw_set_opmode(ah);
@@ -1289,7 +1293,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
* out and/or noise floor calibration might timeout.
*/
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
- AR5K_PHY_AGCCTL_CAL);
+ AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF);
/* At the same time start I/Q calibration for QAM constellation
* -no need for CCK- */
@@ -1310,21 +1314,6 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
channel->center_freq);
}
- /*
- * If we run NF calibration before AGC, it always times out.
- * Binary HAL starts NF and AGC calibration at the same time
- * and only waits for AGC to finish. Also if AGC or NF cal.
- * times out, reset doesn't fail on binary HAL. I believe
- * that's wrong because since rx path is routed to a detector,
- * if cal. doesn't finish we won't have RX. Sam's HAL for AR5210/5211
- * enables noise floor calibration after offset calibration and if noise
- * floor calibration fails, reset fails. I believe that's
- * a better approach, we just need to find a polling interval
- * that suits best, even if reset continues we need to make
- * sure that rx path is ready.
- */
- ath5k_hw_noise_floor_calibration(ah, channel->center_freq);
-
/* Restore antenna mode */
ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index ef5f59c4dd8..03a1106ad72 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -1,9 +1,16 @@
+config ATH9K_HW
+ tristate
+config ATH9K_COMMON
+ tristate
+
config ATH9K
tristate "Atheros 802.11n wireless cards support"
- depends on PCI && MAC80211 && WLAN_80211
+ depends on PCI && MAC80211
+ select ATH9K_HW
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
+ select ATH9K_COMMON
---help---
This module adds support for wireless adapters based on
Atheros IEEE 802.11n AR5008, AR9001 and AR9002 family
@@ -16,13 +23,12 @@ config ATH9K
If you choose to build a module, it'll be called ath9k.
-config ATH9K_DEBUG
+config ATH9K_DEBUGFS
bool "Atheros ath9k debugging"
depends on ATH9K
---help---
- Say Y, if you need ath9k to display debug messages.
- Pass the debug mask as a module parameter:
+ Say Y, if you need access to ath9k's statistics for
+ interrupts, rate control, etc.
- modprobe ath9k debug=0x00000200
+ Also required for changing debug message flags at run time.
- Look in ath9k/debug.h for possible debug masks
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index ff2c9a26c10..4985b2b1b0a 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -1,22 +1,28 @@
-ath9k-y += hw.o \
- eeprom.o \
- eeprom_def.o \
- eeprom_4k.o \
- eeprom_9287.o \
- mac.o \
- calib.o \
- ani.o \
- phy.o \
- beacon.o \
+ath9k-y += beacon.o \
main.o \
recv.o \
xmit.o \
virtual.o \
- rc.o \
- btcoex.o
+ rc.o
ath9k-$(CONFIG_PCI) += pci.o
ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
-ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
+ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
obj-$(CONFIG_ATH9K) += ath9k.o
+
+ath9k_hw-y:= hw.o \
+ eeprom.o \
+ eeprom_def.o \
+ eeprom_4k.o \
+ eeprom_9287.o \
+ calib.o \
+ ani.o \
+ phy.o \
+ btcoex.o \
+ mac.o \
+
+obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
+
+obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
+ath9k_common-y:= common.o
diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
index 2ad7d0280f7..329e6bc137a 100644
--- a/drivers/net/wireless/ath/ath9k/ahb.c
+++ b/drivers/net/wireless/ath/ath9k/ahb.c
@@ -22,27 +22,29 @@
#include "ath9k.h"
/* return bus cachesize in 4B word units */
-static void ath_ahb_read_cachesize(struct ath_softc *sc, int *csz)
+static void ath_ahb_read_cachesize(struct ath_common *common, int *csz)
{
*csz = L1_CACHE_BYTES >> 2;
}
-static void ath_ahb_cleanup(struct ath_softc *sc)
+static void ath_ahb_cleanup(struct ath_common *common)
{
+ struct ath_softc *sc = (struct ath_softc *)common->priv;
iounmap(sc->mem);
}
-static bool ath_ahb_eeprom_read(struct ath_hw *ah, u32 off, u16 *data)
+static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
{
- struct ath_softc *sc = ah->ah_sc;
+ struct ath_softc *sc = (struct ath_softc *)common->priv;
struct platform_device *pdev = to_platform_device(sc->dev);
struct ath9k_platform_data *pdata;
pdata = (struct ath9k_platform_data *) pdev->dev.platform_data;
if (off >= (ARRAY_SIZE(pdata->eeprom_data))) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "%s: flash read failed, offset %08x is out of range\n",
- __func__, off);
+ ath_print(common, ATH_DBG_FATAL,
+ "%s: flash read failed, offset %08x "
+ "is out of range\n",
+ __func__, off);
return false;
}
@@ -67,6 +69,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
int irq;
int ret = 0;
struct ath_hw *ah;
+ char hw_name[64];
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data specified\n");
@@ -116,10 +119,9 @@ static int ath_ahb_probe(struct platform_device *pdev)
sc->hw = hw;
sc->dev = &pdev->dev;
sc->mem = mem;
- sc->bus_ops = &ath_ahb_bus_ops;
sc->irq = irq;
- ret = ath_init_device(AR5416_AR9100_DEVID, sc, 0x0);
+ ret = ath_init_device(AR5416_AR9100_DEVID, sc, 0x0, &ath_ahb_bus_ops);
if (ret) {
dev_err(&pdev->dev, "failed to initialize device\n");
goto err_free_hw;
@@ -132,14 +134,11 @@ static int ath_ahb_probe(struct platform_device *pdev)
}
ah = sc->sc_ah;
+ ath9k_hw_name(ah, hw_name, sizeof(hw_name));
printk(KERN_INFO
- "%s: Atheros AR%s MAC/BB Rev:%x, "
- "AR%s RF Rev:%x, mem=0x%lx, irq=%d\n",
+ "%s: %s mem=0x%lx, irq=%d\n",
wiphy_name(hw->wiphy),
- ath_mac_bb_name(ah->hw_version.macVersion),
- ah->hw_version.macRev,
- ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)),
- ah->hw_version.phyRev,
+ hw_name,
(unsigned long)mem, irq);
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index 2b493742ef1..2a0cd64c2bf 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
struct ath9k_channel *chan)
@@ -31,8 +31,8 @@ static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
}
}
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "No more channel states left. Using channel 0\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_ANI,
+ "No more channel states left. Using channel 0\n");
return 0;
}
@@ -41,16 +41,17 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah,
enum ath9k_ani_cmd cmd, int param)
{
struct ar5416AniState *aniState = ah->curani;
+ struct ath_common *common = ath9k_hw_common(ah);
switch (cmd & ah->ani_function) {
case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{
u32 level = param;
if (level >= ARRAY_SIZE(ah->totalSizeDesired)) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "level out of range (%u > %u)\n",
- level,
- (unsigned)ARRAY_SIZE(ah->totalSizeDesired));
+ ath_print(common, ATH_DBG_ANI,
+ "level out of range (%u > %u)\n",
+ level,
+ (unsigned)ARRAY_SIZE(ah->totalSizeDesired));
return false;
}
@@ -152,10 +153,10 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah,
u32 level = param;
if (level >= ARRAY_SIZE(firstep)) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "level out of range (%u > %u)\n",
- level,
- (unsigned) ARRAY_SIZE(firstep));
+ ath_print(common, ATH_DBG_ANI,
+ "level out of range (%u > %u)\n",
+ level,
+ (unsigned) ARRAY_SIZE(firstep));
return false;
}
REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
@@ -174,11 +175,10 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah,
u32 level = param;
if (level >= ARRAY_SIZE(cycpwrThr1)) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "level out of range (%u > %u)\n",
- level,
- (unsigned)
- ARRAY_SIZE(cycpwrThr1));
+ ath_print(common, ATH_DBG_ANI,
+ "level out of range (%u > %u)\n",
+ level,
+ (unsigned) ARRAY_SIZE(cycpwrThr1));
return false;
}
REG_RMW_FIELD(ah, AR_PHY_TIMING5,
@@ -194,25 +194,28 @@ static bool ath9k_hw_ani_control(struct ath_hw *ah,
case ATH9K_ANI_PRESENT:
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "invalid cmd %u\n", cmd);
+ ath_print(common, ATH_DBG_ANI,
+ "invalid cmd %u\n", cmd);
return false;
}
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "ANI parameters:\n");
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "noiseImmunityLevel=%d, spurImmunityLevel=%d, "
- "ofdmWeakSigDetectOff=%d\n",
- aniState->noiseImmunityLevel, aniState->spurImmunityLevel,
- !aniState->ofdmWeakSigDetectOff);
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "cckWeakSigThreshold=%d, "
- "firstepLevel=%d, listenTime=%d\n",
- aniState->cckWeakSigThreshold, aniState->firstepLevel,
- aniState->listenTime);
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+ ath_print(common, ATH_DBG_ANI, "ANI parameters:\n");
+ ath_print(common, ATH_DBG_ANI,
+ "noiseImmunityLevel=%d, spurImmunityLevel=%d, "
+ "ofdmWeakSigDetectOff=%d\n",
+ aniState->noiseImmunityLevel,
+ aniState->spurImmunityLevel,
+ !aniState->ofdmWeakSigDetectOff);
+ ath_print(common, ATH_DBG_ANI,
+ "cckWeakSigThreshold=%d, "
+ "firstepLevel=%d, listenTime=%d\n",
+ aniState->cckWeakSigThreshold,
+ aniState->firstepLevel,
+ aniState->listenTime);
+ ath_print(common, ATH_DBG_ANI,
"cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n",
- aniState->cycleCount, aniState->ofdmPhyErrCount,
+ aniState->cycleCount,
+ aniState->ofdmPhyErrCount,
aniState->cckPhyErrCount);
return true;
@@ -231,6 +234,7 @@ static void ath9k_hw_update_mibstats(struct ath_hw *ah,
static void ath9k_ani_restart(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
+ struct ath_common *common = ath9k_hw_common(ah);
if (!DO_ANI(ah))
return;
@@ -240,24 +244,24 @@ static void ath9k_ani_restart(struct ath_hw *ah)
if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
aniState->ofdmPhyErrBase = 0;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "OFDM Trigger is too high for hw counters\n");
+ ath_print(common, ATH_DBG_ANI,
+ "OFDM Trigger is too high for hw counters\n");
} else {
aniState->ofdmPhyErrBase =
AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
}
if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
aniState->cckPhyErrBase = 0;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "CCK Trigger is too high for hw counters\n");
+ ath_print(common, ATH_DBG_ANI,
+ "CCK Trigger is too high for hw counters\n");
} else {
aniState->cckPhyErrBase =
AR_PHY_COUNTMAX - aniState->cckTrigHigh;
}
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Writing ofdmbase=%u cckbase=%u\n",
- aniState->ofdmPhyErrBase,
- aniState->cckPhyErrBase);
+ ath_print(common, ATH_DBG_ANI,
+ "Writing ofdmbase=%u cckbase=%u\n",
+ aniState->ofdmPhyErrBase,
+ aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
@@ -271,7 +275,7 @@ static void ath9k_ani_restart(struct ath_hw *ah)
static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
struct ar5416AniState *aniState;
int32_t rssi;
@@ -343,7 +347,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
struct ar5416AniState *aniState;
int32_t rssi;
@@ -464,6 +468,7 @@ void ath9k_ani_reset(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
struct ath9k_channel *chan = ah->curchan;
+ struct ath_common *common = ath9k_hw_common(ah);
int index;
if (!DO_ANI(ah))
@@ -475,8 +480,8 @@ void ath9k_ani_reset(struct ath_hw *ah)
if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION
&& ah->opmode != NL80211_IFTYPE_ADHOC) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Reset ANI state opmode %u\n", ah->opmode);
+ ath_print(common, ATH_DBG_ANI,
+ "Reset ANI state opmode %u\n", ah->opmode);
ah->stats.ast_ani_reset++;
if (ah->opmode == NL80211_IFTYPE_AP) {
@@ -543,6 +548,7 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah,
struct ath9k_channel *chan)
{
struct ar5416AniState *aniState;
+ struct ath_common *common = ath9k_hw_common(ah);
int32_t listenTime;
u32 phyCnt1, phyCnt2;
u32 ofdmPhyErrCnt, cckPhyErrCnt;
@@ -569,20 +575,22 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah,
if (phyCnt1 < aniState->ofdmPhyErrBase ||
phyCnt2 < aniState->cckPhyErrBase) {
if (phyCnt1 < aniState->ofdmPhyErrBase) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "phyCnt1 0x%x, resetting "
- "counter value to 0x%x\n",
- phyCnt1, aniState->ofdmPhyErrBase);
+ ath_print(common, ATH_DBG_ANI,
+ "phyCnt1 0x%x, resetting "
+ "counter value to 0x%x\n",
+ phyCnt1,
+ aniState->ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_1,
aniState->ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_MASK_1,
AR_PHY_ERR_OFDM_TIMING);
}
if (phyCnt2 < aniState->cckPhyErrBase) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "phyCnt2 0x%x, resetting "
- "counter value to 0x%x\n",
- phyCnt2, aniState->cckPhyErrBase);
+ ath_print(common, ATH_DBG_ANI,
+ "phyCnt2 0x%x, resetting "
+ "counter value to 0x%x\n",
+ phyCnt2,
+ aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_2,
aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_MASK_2,
@@ -621,10 +629,13 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah,
}
}
}
+EXPORT_SYMBOL(ath9k_hw_ani_monitor);
void ath9k_enable_mib_counters(struct ath_hw *ah)
{
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable MIB counters\n");
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ ath_print(common, ATH_DBG_ANI, "Enable MIB counters\n");
ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
@@ -640,7 +651,10 @@ void ath9k_enable_mib_counters(struct ath_hw *ah)
/* Freeze the MIB counters, get the stats and then clear them */
void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
{
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disable MIB counters\n");
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ ath_print(common, ATH_DBG_ANI, "Disable MIB counters\n");
+
REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
@@ -653,6 +667,7 @@ u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah,
u32 *rxf_pcnt,
u32 *txf_pcnt)
{
+ struct ath_common *common = ath9k_hw_common(ah);
static u32 cycles, rx_clear, rx_frame, tx_frame;
u32 good = 1;
@@ -662,8 +677,8 @@ u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah,
u32 cc = REG_READ(ah, AR_CCCNT);
if (cycles == 0 || cycles > cc) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "cycle counter wrap. ExtBusy = 0\n");
+ ath_print(common, ATH_DBG_ANI,
+ "cycle counter wrap. ExtBusy = 0\n");
good = 0;
} else {
u32 cc_d = cc - cycles;
@@ -742,6 +757,7 @@ void ath9k_hw_procmibevent(struct ath_hw *ah)
ath9k_ani_restart(ah);
}
}
+EXPORT_SYMBOL(ath9k_hw_procmibevent);
void ath9k_hw_ani_setup(struct ath_hw *ah)
{
@@ -762,9 +778,10 @@ void ath9k_hw_ani_setup(struct ath_hw *ah)
void ath9k_hw_ani_init(struct ath_hw *ah)
{
+ struct ath_common *common = ath9k_hw_common(ah);
int i;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Initialize ANI\n");
+ ath_print(common, ATH_DBG_ANI, "Initialize ANI\n");
memset(ah->ani, 0, sizeof(ah->ani));
for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
@@ -786,11 +803,11 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
}
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Setting OfdmErrBase = 0x%08x\n",
- ah->ani[0].ofdmPhyErrBase);
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
- ah->ani[0].cckPhyErrBase);
+ ath_print(common, ATH_DBG_ANI,
+ "Setting OfdmErrBase = 0x%08x\n",
+ ah->ani[0].ofdmPhyErrBase);
+ ath_print(common, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
+ ah->ani[0].cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase);
@@ -803,7 +820,7 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
void ath9k_hw_ani_disable(struct ath_hw *ah)
{
- DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disabling ANI\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_ANI, "Disabling ANI\n");
ath9k_hw_disable_mib_counters(ah);
REG_WRITE(ah, AR_PHY_ERR_1, 0);
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 1d59f10f68d..e2cef2ff5d8 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -19,14 +19,15 @@
#include <linux/etherdevice.h>
#include <linux/device.h>
-#include <net/mac80211.h>
#include <linux/leds.h>
-#include "hw.h"
-#include "rc.h"
#include "debug.h"
-#include "../ath.h"
-#include "btcoex.h"
+#include "common.h"
+
+/*
+ * Header for the ath9k.ko driver core *only* -- hw code nor any other driver
+ * should rely on this file or its contents.
+ */
struct ath_node;
@@ -54,15 +55,11 @@ struct ath_node;
#define A_MAX(a, b) ((a) > (b) ? (a) : (b))
-#define ASSERT(exp) BUG_ON(!(exp))
-
#define TSF_TO_TU(_h,_l) \
((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i))
-static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
struct ath_config {
u32 ath_aggr_prot;
u16 txpowlimit;
@@ -103,18 +100,6 @@ enum buffer_type {
BUF_XRETRY = BIT(5),
};
-struct ath_buf_state {
- int bfs_nframes;
- u16 bfs_al;
- u16 bfs_frmlen;
- int bfs_seqno;
- int bfs_tidno;
- int bfs_retries;
- u8 bf_type;
- u32 bfs_keyix;
- enum ath9k_key_type bfs_keytype;
-};
-
#define bf_nframes bf_state.bfs_nframes
#define bf_al bf_state.bfs_al
#define bf_frmlen bf_state.bfs_frmlen
@@ -129,21 +114,6 @@ struct ath_buf_state {
#define bf_isretried(bf) (bf->bf_state.bf_type & BUF_RETRY)
#define bf_isxretried(bf) (bf->bf_state.bf_type & BUF_XRETRY)
-struct ath_buf {
- struct list_head list;
- struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or
- an aggregate) */
- struct ath_buf *bf_next; /* next subframe in the aggregate */
- struct sk_buff *bf_mpdu; /* enclosing frame structure */
- struct ath_desc *bf_desc; /* virtual addr of desc */
- dma_addr_t bf_daddr; /* physical addr of desc */
- dma_addr_t bf_buf_addr; /* physical addr of data buffer */
- bool bf_stale;
- u16 bf_flags;
- struct ath_buf_state bf_state;
- dma_addr_t bf_dmacontext;
-};
-
struct ath_descdma {
struct ath_desc *dd_desc;
dma_addr_t dd_desc_paddr;
@@ -163,13 +133,9 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
#define ATH_MAX_ANTENNA 3
#define ATH_RXBUF 512
-#define WME_NUM_TID 16
#define ATH_TXBUF 512
#define ATH_TXMAXTRY 13
#define ATH_MGT_TXMAXTRY 4
-#define WME_BA_BMP_SIZE 64
-#define WME_MAX_BA WME_BA_BMP_SIZE
-#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA)
#define TID_TO_WME_AC(_tid) \
((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \
@@ -177,12 +143,6 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
(((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \
WME_AC_VO)
-#define WME_AC_BE 0
-#define WME_AC_BK 1
-#define WME_AC_VI 2
-#define WME_AC_VO 3
-#define WME_NUM_AC 4
-
#define ADDBA_EXCHANGE_ATTEMPTS 10
#define ATH_AGGR_DELIM_SZ 4
#define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */
@@ -191,7 +151,6 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
/* minimum h/w qdepth to be sustained to maximize aggregation */
#define ATH_AGGR_MIN_QDEPTH 2
#define ATH_AMPDU_SUBFRAME_DEFAULT 32
-#define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1)
#define IEEE80211_SEQ_SEQ_SHIFT 4
#define IEEE80211_SEQ_MAX 4096
@@ -238,18 +197,8 @@ struct ath_txq {
struct list_head axq_q;
spinlock_t axq_lock;
u32 axq_depth;
- u8 axq_aggr_depth;
bool stopped;
bool axq_tx_inprogress;
- struct ath_buf *axq_linkbuf;
-
- /* first desc of the last descriptor that contains CTS */
- struct ath_desc *axq_lastdsWithCTS;
-
- /* final desc of the gating desc that determines whether
- lastdsWithCTS has been DMA'ed or not */
- struct ath_desc *axq_gatingds;
-
struct list_head axq_acq;
};
@@ -257,30 +206,6 @@ struct ath_txq {
#define AGGR_ADDBA_COMPLETE BIT(2)
#define AGGR_ADDBA_PROGRESS BIT(3)
-struct ath_atx_tid {
- struct list_head list;
- struct list_head buf_q;
- struct ath_node *an;
- struct ath_atx_ac *ac;
- struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];
- u16 seq_start;
- u16 seq_next;
- u16 baw_size;
- int tidno;
- int baw_head; /* first un-acked tx buffer */
- int baw_tail; /* next unused tx buffer slot */
- int sched;
- int paused;
- u8 state;
-};
-
-struct ath_atx_ac {
- int sched;
- int qnum;
- struct list_head list;
- struct list_head tid_q;
-};
-
struct ath_tx_control {
struct ath_txq *txq;
int if_id;
@@ -291,30 +216,6 @@ struct ath_tx_control {
#define ATH_TX_XRETRY 0x02
#define ATH_TX_BAR 0x04
-#define ATH_RSSI_LPF_LEN 10
-#define RSSI_LPF_THRESHOLD -20
-#define ATH9K_RSSI_BAD 0x80
-#define ATH_RSSI_EP_MULTIPLIER (1<<7)
-#define ATH_EP_MUL(x, mul) ((x) * (mul))
-#define ATH_RSSI_IN(x) (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER))
-#define ATH_LPF_RSSI(x, y, len) \
- ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
-#define ATH_RSSI_LPF(x, y) do { \
- if ((y) >= RSSI_LPF_THRESHOLD) \
- x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \
-} while (0)
-#define ATH_EP_RND(x, mul) \
- ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
-
-struct ath_node {
- struct ath_softc *an_sc;
- struct ath_atx_tid tid[WME_NUM_TID];
- struct ath_atx_ac ac[WME_NUM_AC];
- u16 maxampdu;
- u8 mpdudensity;
- int last_rssi;
-};
-
struct ath_tx {
u16 seq_no;
u32 txqsetup;
@@ -329,7 +230,6 @@ struct ath_rx {
u8 defant;
u8 rxotherant;
u32 *rxlink;
- int bufsize;
unsigned int rxfilter;
spinlock_t rxflushlock;
spinlock_t rxbuflock;
@@ -427,9 +327,9 @@ struct ath_beacon {
void ath_beacon_tasklet(unsigned long data);
void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif);
-int ath_beaconq_setup(struct ath_hw *ah);
int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif);
void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp);
+int ath_beaconq_config(struct ath_softc *sc);
/*******/
/* ANI */
@@ -441,14 +341,24 @@ void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp);
#define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */
#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */
-struct ath_ani {
- bool caldone;
- int16_t noise_floor;
- unsigned int longcal_timer;
- unsigned int shortcal_timer;
- unsigned int resetcal_timer;
- unsigned int checkani_timer;
- struct timer_list timer;
+/* Defines the BT AR_BT_COEX_WGHT used */
+enum ath_stomp_type {
+ ATH_BTCOEX_NO_STOMP,
+ ATH_BTCOEX_STOMP_ALL,
+ ATH_BTCOEX_STOMP_LOW,
+ ATH_BTCOEX_STOMP_NONE
+};
+
+struct ath_btcoex {
+ bool hw_timer_enabled;
+ spinlock_t btcoex_lock;
+ struct timer_list period_timer; /* Timer for BT period */
+ u32 bt_priority_cnt;
+ unsigned long bt_priority_time;
+ int bt_stomp_type; /* Types of BT stomping */
+ u32 btcoex_no_stomp; /* in usec */
+ u32 btcoex_period; /* in usec */
+ struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */
};
/********************/
@@ -484,25 +394,13 @@ struct ath_led {
* Used when PCI device not fully initialized by bootrom/BIOS
*/
#define DEFAULT_CACHELINE 32
-#define ATH_DEFAULT_NOISE_FLOOR -95
#define ATH_REGCLASSIDS_MAX 10
#define ATH_CABQ_READY_TIME 80 /* % of beacon interval */
#define ATH_MAX_SW_RETRIES 10
#define ATH_CHAN_MAX 255
#define IEEE80211_WEP_NKID 4 /* number of key ids */
-/*
- * The key cache is used for h/w cipher state and also for
- * tracking station state such as the current tx antenna.
- * We also setup a mapping table between key cache slot indices
- * and station state to short-circuit node lookups on rx.
- * Different parts have different size key caches. We handle
- * up to ATH_KEYMAX entries (could dynamically allocate state).
- */
-#define ATH_KEYMAX 128 /* max key cache size we handle */
-
#define ATH_TXPOWER_MAX 100 /* .5 dBm units */
-#define ATH_RSSI_DUMMY_MARKER 0x127
#define ATH_RATE_DUMMY_MARKER 0
#define SC_OP_INVALID BIT(0)
@@ -522,23 +420,17 @@ struct ath_led {
#define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17)
#define SC_OP_WAIT_FOR_TX_ACK BIT(18)
#define SC_OP_BEACON_SYNC BIT(19)
-#define SC_OP_BTCOEX_ENABLED BIT(20)
#define SC_OP_BT_PRIORITY_DETECTED BIT(21)
-
-struct ath_bus_ops {
- void (*read_cachesize)(struct ath_softc *sc, int *csz);
- void (*cleanup)(struct ath_softc *sc);
- bool (*eeprom_read)(struct ath_hw *ah, u32 off, u16 *data);
-};
+#define SC_OP_NULLFUNC_COMPLETED BIT(22)
+#define SC_OP_PS_ENABLED BIT(23)
struct ath_wiphy;
+struct ath_rate_table;
struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
- struct ath_common common;
-
spinlock_t wiphy_lock; /* spinlock to protect ath_wiphy data */
struct ath_wiphy *pri_wiphy;
struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may
@@ -565,32 +457,21 @@ struct ath_softc {
spinlock_t sc_pm_lock;
struct mutex mutex;
- u8 curbssid[ETH_ALEN];
- u8 bssidmask[ETH_ALEN];
u32 intrstatus;
u32 sc_flags; /* SC_OP_* */
u16 curtxpow;
- u16 curaid;
u8 nbcnvifs;
u16 nvifs;
- u8 tx_chainmask;
- u8 rx_chainmask;
- u32 keymax;
- DECLARE_BITMAP(keymap, ATH_KEYMAX);
- u8 splitmic;
bool ps_enabled;
unsigned long ps_usecount;
enum ath9k_int imask;
- enum ath9k_ht_extprotspacing ht_extprotspacing;
- enum ath9k_ht_macmode tx_chan_width;
struct ath_config config;
struct ath_rx rx;
struct ath_tx tx;
struct ath_beacon beacon;
- struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX];
- const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX];
const struct ath_rate_table *cur_rate_table;
+ enum wireless_mode cur_rate_mode;
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
struct ath_led radio_led;
@@ -605,14 +486,12 @@ struct ath_softc {
int beacon_interval;
- struct ath_ani ani;
-#ifdef CONFIG_ATH9K_DEBUG
+#ifdef CONFIG_ATH9K_DEBUGFS
struct ath9k_debug debug;
#endif
- struct ath_bus_ops *bus_ops;
struct ath_beacon_config cur_beacon_conf;
struct delayed_work tx_complete_work;
- struct ath_btcoex_info btcoex_info;
+ struct ath_btcoex btcoex;
};
struct ath_wiphy {
@@ -625,6 +504,7 @@ struct ath_wiphy {
ATH_WIPHY_PAUSED,
ATH_WIPHY_SCAN,
} state;
+ bool idle;
int chan_idx;
int chan_is_ht;
};
@@ -634,31 +514,22 @@ int ath_get_hal_qnum(u16 queue, struct ath_softc *sc);
int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc);
int ath_cabq_update(struct ath_softc *);
-static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
-{
- return &ah->ah_sc->common;
-}
-
-static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah)
+static inline void ath_read_cachesize(struct ath_common *common, int *csz)
{
- return &(ath9k_hw_common(ah)->regulatory);
+ common->bus_ops->read_cachesize(common, csz);
}
-static inline void ath_read_cachesize(struct ath_softc *sc, int *csz)
+static inline void ath_bus_cleanup(struct ath_common *common)
{
- sc->bus_ops->read_cachesize(sc, csz);
-}
-
-static inline void ath_bus_cleanup(struct ath_softc *sc)
-{
- sc->bus_ops->cleanup(sc);
+ common->bus_ops->cleanup(common);
}
extern struct ieee80211_ops ath9k_ops;
irqreturn_t ath_isr(int irq, void *dev);
void ath_cleanup(struct ath_softc *sc);
-int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid);
+int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
+ const struct ath_bus_ops *bus_ops);
void ath_detach(struct ath_softc *sc);
const char *ath_mac_bb_name(u32 mac_bb_version);
const char *ath_rf_name(u16 rf_version);
@@ -668,8 +539,9 @@ void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
void ath_update_chainmask(struct ath_softc *sc, int is_ht);
int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
struct ath9k_channel *hchan);
-void ath_radio_enable(struct ath_softc *sc);
-void ath_radio_disable(struct ath_softc *sc);
+
+void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw);
+void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw);
#ifdef CONFIG_PCI
int ath_pci_init(void);
@@ -705,9 +577,10 @@ void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
bool ath9k_wiphy_scanning(struct ath_softc *sc);
void ath9k_wiphy_work(struct work_struct *work);
bool ath9k_all_wiphys_idle(struct ath_softc *sc);
+void ath9k_set_wiphy_idle(struct ath_wiphy *aphy, bool idle);
-void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val);
-unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset);
+void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue);
+void ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue);
int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype);
#endif /* ATH9K_H */
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 45c4ea57616..1660ef17aaf 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -23,10 +23,12 @@
* the operating mode of the station (AP or AdHoc). Parameters are AIFS
* settings and channel width min/max
*/
-static int ath_beaconq_config(struct ath_softc *sc)
+int ath_beaconq_config(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
- struct ath9k_tx_queue_info qi;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_tx_queue_info qi, qi_be;
+ int qnum;
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
@@ -36,14 +38,17 @@ static int ath_beaconq_config(struct ath_softc *sc)
qi.tqi_cwmax = 0;
} else {
/* Adhoc mode; important thing is to use 2x cwmin. */
- qi.tqi_aifs = sc->beacon.beacon_qi.tqi_aifs;
- qi.tqi_cwmin = 2*sc->beacon.beacon_qi.tqi_cwmin;
- qi.tqi_cwmax = sc->beacon.beacon_qi.tqi_cwmax;
+ qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA,
+ ATH9K_WME_AC_BE);
+ ath9k_hw_get_txq_props(ah, qnum, &qi_be);
+ qi.tqi_aifs = qi_be.tqi_aifs;
+ qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
+ qi.tqi_cwmax = qi_be.tqi_cwmax;
}
if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to update h/w beacon queue parameters\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to update h/w beacon queue parameters\n");
return 0;
} else {
ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
@@ -61,11 +66,12 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
{
struct sk_buff *skb = bf->bf_mpdu;
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_desc *ds;
struct ath9k_11n_rate_series series[4];
- const struct ath_rate_table *rt;
int flags, antenna, ctsrate = 0, ctsduration = 0;
- u8 rate;
+ struct ieee80211_supported_band *sband;
+ u8 rate = 0;
ds = bf->bf_desc;
flags = ATH9K_TXDESC_NOACK;
@@ -89,10 +95,10 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
ds->ds_data = bf->bf_buf_addr;
- rt = sc->cur_rate_table;
- rate = rt->info[0].ratecode;
+ sband = &sc->sbands[common->hw->conf.channel->band];
+ rate = sband->bitrates[0].hw_value;
if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
- rate |= rt->info[0].short_preamble;
+ rate |= sband->bitrates[0].hw_value_short;
ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN,
ATH9K_PKT_TYPE_BEACON,
@@ -108,7 +114,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp,
memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
series[0].Tries = 1;
series[0].Rate = rate;
- series[0].ChSel = sc->tx_chainmask;
+ series[0].ChSel = common->tx_chainmask;
series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration,
series, 4, 0);
@@ -119,6 +125,7 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_buf *bf;
struct ath_vif *avp;
struct sk_buff *skb;
@@ -172,7 +179,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
- DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error on beaconing\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "dma_mapping_error on beaconing\n");
return NULL;
}
@@ -192,8 +200,8 @@ static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
if (skb && cabq_depth) {
if (sc->nvifs > 1) {
- DPRINTF(sc, ATH_DBG_BEACON,
- "Flushing previous cabq traffic\n");
+ ath_print(common, ATH_DBG_BEACON,
+ "Flushing previous cabq traffic\n");
ath_draintxq(sc, cabq, false);
}
}
@@ -216,6 +224,7 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc,
struct ieee80211_vif *vif)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf;
struct ath_vif *avp;
struct sk_buff *skb;
@@ -233,25 +242,14 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc,
/* NB: caller is known to have already stopped tx dma */
ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
ath9k_hw_txstart(ah, sc->beacon.beaconq);
- DPRINTF(sc, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n",
- sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc);
-}
-
-int ath_beaconq_setup(struct ath_hw *ah)
-{
- struct ath9k_tx_queue_info qi;
-
- memset(&qi, 0, sizeof(qi));
- qi.tqi_aifs = 1;
- qi.tqi_cwmin = 0;
- qi.tqi_cwmax = 0;
- /* NB: don't enable any interrupts */
- return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi);
+ ath_print(common, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n",
+ sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc);
}
int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
{
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp;
struct ath_buf *bf;
struct sk_buff *skb;
@@ -309,7 +307,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
/* NB: the beacon data buffer must be 32-bit aligned. */
skb = ieee80211_beacon_get(sc->hw, vif);
if (skb == NULL) {
- DPRINTF(sc, ATH_DBG_BEACON, "cannot get skb\n");
+ ath_print(common, ATH_DBG_BEACON, "cannot get skb\n");
return -ENOMEM;
}
@@ -333,9 +331,10 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
tsfadjust = intval * avp->av_bslot / ATH_BCBUF;
avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
- DPRINTF(sc, ATH_DBG_BEACON,
- "stagger beacons, bslot %d intval %u tsfadjust %llu\n",
- avp->av_bslot, intval, (unsigned long long)tsfadjust);
+ ath_print(common, ATH_DBG_BEACON,
+ "stagger beacons, bslot %d intval "
+ "%u tsfadjust %llu\n",
+ avp->av_bslot, intval, (unsigned long long)tsfadjust);
((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
avp->tsf_adjust;
@@ -349,8 +348,8 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
- DPRINTF(sc, ATH_DBG_FATAL,
- "dma_mapping_error on beacon alloc\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "dma_mapping_error on beacon alloc\n");
return -ENOMEM;
}
@@ -386,6 +385,7 @@ void ath_beacon_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf = NULL;
struct ieee80211_vif *vif;
struct ath_wiphy *aphy;
@@ -405,12 +405,12 @@ void ath_beacon_tasklet(unsigned long data)
sc->beacon.bmisscnt++;
if (sc->beacon.bmisscnt < BSTUCK_THRESH) {
- DPRINTF(sc, ATH_DBG_BEACON,
- "missed %u consecutive beacons\n",
- sc->beacon.bmisscnt);
+ ath_print(common, ATH_DBG_BEACON,
+ "missed %u consecutive beacons\n",
+ sc->beacon.bmisscnt);
} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
- DPRINTF(sc, ATH_DBG_BEACON,
- "beacon is officially stuck\n");
+ ath_print(common, ATH_DBG_BEACON,
+ "beacon is officially stuck\n");
sc->sc_flags |= SC_OP_TSF_RESET;
ath_reset(sc, false);
}
@@ -419,9 +419,9 @@ void ath_beacon_tasklet(unsigned long data)
}
if (sc->beacon.bmisscnt != 0) {
- DPRINTF(sc, ATH_DBG_BEACON,
- "resume beacon xmit after %u misses\n",
- sc->beacon.bmisscnt);
+ ath_print(common, ATH_DBG_BEACON,
+ "resume beacon xmit after %u misses\n",
+ sc->beacon.bmisscnt);
sc->beacon.bmisscnt = 0;
}
@@ -447,9 +447,9 @@ void ath_beacon_tasklet(unsigned long data)
vif = sc->beacon.bslot[slot];
aphy = sc->beacon.bslot_aphy[slot];
- DPRINTF(sc, ATH_DBG_BEACON,
- "slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
- slot, tsf, tsftu, intval, vif);
+ ath_print(common, ATH_DBG_BEACON,
+ "slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
+ slot, tsf, tsftu, intval, vif);
bfaddr = 0;
if (vif) {
@@ -490,7 +490,7 @@ void ath_beacon_tasklet(unsigned long data)
* are still pending on the queue.
*/
if (!ath9k_hw_stoptxdma(ah, sc->beacon.beaconq)) {
- DPRINTF(sc, ATH_DBG_FATAL,
+ ath_print(common, ATH_DBG_FATAL,
"beacon queue %u did not stop?\n", sc->beacon.beaconq);
}
@@ -502,6 +502,19 @@ void ath_beacon_tasklet(unsigned long data)
}
}
+static void ath9k_beacon_init(struct ath_softc *sc,
+ u32 next_beacon,
+ u32 beacon_period)
+{
+ if (beacon_period & ATH9K_BEACON_RESET_TSF)
+ ath9k_ps_wakeup(sc);
+
+ ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period);
+
+ if (beacon_period & ATH9K_BEACON_RESET_TSF)
+ ath9k_ps_restore(sc);
+}
+
/*
* For multi-bss ap support beacons are either staggered evenly over N slots or
* burst together. For the former arrange for the SWBA to be delivered for each
@@ -534,7 +547,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
/* Set the computed AP beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
- ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval);
+ ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
@@ -555,6 +568,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
static void ath_beacon_config_sta(struct ath_softc *sc,
struct ath_beacon_config *conf)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath9k_beacon_state bs;
int dtimperiod, dtimcount, sleepduration;
int cfpperiod, cfpcount;
@@ -651,11 +665,11 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
/* TSF out of range threshold fixed at 1 second */
bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
- DPRINTF(sc, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
- DPRINTF(sc, ATH_DBG_BEACON,
- "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
- bs.bs_bmissthreshold, bs.bs_sleepduration,
- bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
+ ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
+ ath_print(common, ATH_DBG_BEACON,
+ "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
+ bs.bs_bmissthreshold, bs.bs_sleepduration,
+ bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
/* Set the computed STA beacon timers */
@@ -669,6 +683,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
struct ath_beacon_config *conf,
struct ieee80211_vif *vif)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u64 tsf;
u32 tsftu, intval, nexttbtt;
@@ -689,9 +704,9 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
nexttbtt += intval;
} while (nexttbtt < tsftu);
- DPRINTF(sc, ATH_DBG_BEACON,
- "IBSS nexttbtt %u intval %u (%u)\n",
- nexttbtt, intval, conf->beacon_interval);
+ ath_print(common, ATH_DBG_BEACON,
+ "IBSS nexttbtt %u intval %u (%u)\n",
+ nexttbtt, intval, conf->beacon_interval);
/*
* In IBSS mode enable the beacon timers but only enable SWBA interrupts
@@ -707,7 +722,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
/* Set the computed ADHOC beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
- ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval);
+ ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
@@ -719,6 +734,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
{
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
enum nl80211_iftype iftype;
/* Setup the beacon configuration parameters */
@@ -759,8 +775,8 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
ath_beacon_config_sta(sc, cur_conf);
break;
default:
- DPRINTF(sc, ATH_DBG_CONFIG,
- "Unsupported beaconing mode\n");
+ ath_print(common, ATH_DBG_CONFIG,
+ "Unsupported beaconing mode\n");
return;
}
diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c
index 55f607b7699..fb4ac15f3b9 100644
--- a/drivers/net/wireless/ath/ath9k/btcoex.c
+++ b/drivers/net/wireless/ath/ath9k/btcoex.c
@@ -14,10 +14,26 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
-static const struct ath_btcoex_config ath_bt_config = { 0, true, true,
- ATH_BT_COEX_MODE_SLOTTED, true, true, 2, 5, true };
+enum ath_bt_mode {
+ ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */
+ ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */
+ ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */
+ ATH_BT_COEX_MODE_DISALBED, /* coexistence disabled */
+};
+
+struct ath_btcoex_config {
+ u8 bt_time_extend;
+ bool bt_txstate_extend;
+ bool bt_txframe_extend;
+ enum ath_bt_mode bt_mode; /* coexistence mode */
+ bool bt_quiet_collision;
+ bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/
+ u8 bt_priority_time;
+ u8 bt_first_slot_time;
+ bool bt_hold_rx_clear;
+};
static const u16 ath_subsysid_tbl[] = {
AR9280_COEX2WIRE_SUBSYSID,
@@ -29,141 +45,38 @@ static const u16 ath_subsysid_tbl[] = {
* Checks the subsystem id of the device to see if it
* supports btcoex
*/
-bool ath_btcoex_supported(u16 subsysid)
+bool ath9k_hw_btcoex_supported(struct ath_hw *ah)
{
int i;
- if (!subsysid)
+ if (!ah->hw_version.subsysid)
return false;
for (i = 0; i < ARRAY_SIZE(ath_subsysid_tbl); i++)
- if (subsysid == ath_subsysid_tbl[i])
+ if (ah->hw_version.subsysid == ath_subsysid_tbl[i])
return true;
return false;
}
-/*
- * Detects if there is any priority bt traffic
- */
-static void ath_detect_bt_priority(struct ath_softc *sc)
-{
- struct ath_btcoex_info *btinfo = &sc->btcoex_info;
-
- if (ath9k_hw_gpio_get(sc->sc_ah, btinfo->btpriority_gpio))
- btinfo->bt_priority_cnt++;
-
- if (time_after(jiffies, btinfo->bt_priority_time +
- msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
- if (btinfo->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
- DPRINTF(sc, ATH_DBG_BTCOEX,
- "BT priority traffic detected");
- sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
- } else {
- sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
- }
-
- btinfo->bt_priority_cnt = 0;
- btinfo->bt_priority_time = jiffies;
- }
-}
-
-/*
- * Configures appropriate weight based on stomp type.
- */
-static void ath_btcoex_bt_stomp(struct ath_softc *sc,
- struct ath_btcoex_info *btinfo,
- int stomp_type)
-{
-
- switch (stomp_type) {
- case ATH_BTCOEX_STOMP_ALL:
- ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
- AR_STOMP_ALL_WLAN_WGHT);
- break;
- case ATH_BTCOEX_STOMP_LOW:
- ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
- AR_STOMP_LOW_WLAN_WGHT);
- break;
- case ATH_BTCOEX_STOMP_NONE:
- ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT,
- AR_STOMP_NONE_WLAN_WGHT);
- break;
- default:
- DPRINTF(sc, ATH_DBG_BTCOEX, "Invalid Stomptype\n");
- break;
- }
-
- ath9k_hw_btcoex_enable(sc->sc_ah);
-}
-
-/*
- * This is the master bt coex timer which runs for every
- * 45ms, bt traffic will be given priority during 55% of this
- * period while wlan gets remaining 45%
- */
-
-static void ath_btcoex_period_timer(unsigned long data)
-{
- struct ath_softc *sc = (struct ath_softc *) data;
- struct ath_btcoex_info *btinfo = &sc->btcoex_info;
-
- ath_detect_bt_priority(sc);
-
- spin_lock_bh(&btinfo->btcoex_lock);
-
- ath_btcoex_bt_stomp(sc, btinfo, btinfo->bt_stomp_type);
-
- spin_unlock_bh(&btinfo->btcoex_lock);
-
- if (btinfo->btcoex_period != btinfo->btcoex_no_stomp) {
- if (btinfo->hw_timer_enabled)
- ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
-
- ath_gen_timer_start(sc->sc_ah,
- btinfo->no_stomp_timer,
- (ath9k_hw_gettsf32(sc->sc_ah) +
- btinfo->btcoex_no_stomp),
- btinfo->btcoex_no_stomp * 10);
- btinfo->hw_timer_enabled = true;
- }
-
- mod_timer(&btinfo->period_timer, jiffies +
- msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
-}
-
-/*
- * Generic tsf based hw timer which configures weight
- * registers to time slice between wlan and bt traffic
- */
-
-static void ath_btcoex_no_stomp_timer(void *arg)
-{
- struct ath_softc *sc = (struct ath_softc *)arg;
- struct ath_btcoex_info *btinfo = &sc->btcoex_info;
-
- DPRINTF(sc, ATH_DBG_BTCOEX, "no stomp timer running \n");
-
- spin_lock_bh(&btinfo->btcoex_lock);
-
- if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_LOW)
- ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_NONE);
- else if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
- ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_LOW);
-
- spin_unlock_bh(&btinfo->btcoex_lock);
-}
-
-static int ath_init_btcoex_info(struct ath_hw *hw,
- struct ath_btcoex_info *btcoex_info)
+void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
{
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
+ const struct ath_btcoex_config ath_bt_config = {
+ .bt_time_extend = 0,
+ .bt_txstate_extend = true,
+ .bt_txframe_extend = true,
+ .bt_mode = ATH_BT_COEX_MODE_SLOTTED,
+ .bt_quiet_collision = true,
+ .bt_rxclear_polarity = true,
+ .bt_priority_time = 2,
+ .bt_first_slot_time = 5,
+ .bt_hold_rx_clear = true,
+ };
u32 i;
- int qnum;
- qnum = ath_tx_get_qnum(hw->ah_sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
-
- btcoex_info->bt_coex_mode =
- (btcoex_info->bt_coex_mode & AR_BT_QCU_THRESH) |
+ btcoex_hw->bt_coex_mode =
+ (btcoex_hw->bt_coex_mode & AR_BT_QCU_THRESH) |
SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) |
SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) |
SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) |
@@ -174,167 +87,141 @@ static int ath_init_btcoex_info(struct ath_hw *hw,
SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) |
SM(qnum, AR_BT_QCU_THRESH);
- btcoex_info->bt_coex_mode2 =
+ btcoex_hw->bt_coex_mode2 =
SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) |
SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) |
AR_BT_DISABLE_BT_ANT;
- btcoex_info->bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
+ for (i = 0; i < 32; i++)
+ ah->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i;
+}
+EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw);
- btcoex_info->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
+void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah)
+{
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
- btcoex_info->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
- btcoex_info->btcoex_period / 100;
+ /* connect bt_active to baseband */
+ REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+ (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF |
+ AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF));
- for (i = 0; i < 32; i++)
- hw->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i;
+ REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+ AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB);
- setup_timer(&btcoex_info->period_timer, ath_btcoex_period_timer,
- (unsigned long) hw->ah_sc);
+ /* Set input mux for bt_active to gpio pin */
+ REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+ AR_GPIO_INPUT_MUX1_BT_ACTIVE,
+ btcoex_hw->btactive_gpio);
- btcoex_info->no_stomp_timer = ath_gen_timer_alloc(hw,
- ath_btcoex_no_stomp_timer,
- ath_btcoex_no_stomp_timer,
- (void *)hw->ah_sc, AR_FIRST_NDP_TIMER);
+ /* Configure the desired gpio port for input */
+ ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio);
+}
+EXPORT_SYMBOL(ath9k_hw_btcoex_init_2wire);
+
+void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah)
+{
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
- if (btcoex_info->no_stomp_timer == NULL)
- return -ENOMEM;
+ /* btcoex 3-wire */
+ REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
+ (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB |
+ AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB));
- spin_lock_init(&btcoex_info->btcoex_lock);
+ /* Set input mux for bt_prority_async and
+ * bt_active_async to GPIO pins */
+ REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+ AR_GPIO_INPUT_MUX1_BT_ACTIVE,
+ btcoex_hw->btactive_gpio);
- return 0;
+ REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
+ AR_GPIO_INPUT_MUX1_BT_PRIORITY,
+ btcoex_hw->btpriority_gpio);
+
+ /* Configure the desired GPIO ports for input */
+
+ ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio);
+ ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btpriority_gpio);
}
+EXPORT_SYMBOL(ath9k_hw_btcoex_init_3wire);
-int ath9k_hw_btcoex_init(struct ath_hw *ah)
+static void ath9k_hw_btcoex_enable_2wire(struct ath_hw *ah)
{
- struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
- int ret = 0;
-
- if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) {
- /* connect bt_active to baseband */
- REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL,
- (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF |
- AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF));
-
- REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
- AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB);
-
- /* Set input mux for bt_active to gpio pin */
- REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
- AR_GPIO_INPUT_MUX1_BT_ACTIVE,
- btcoex_info->btactive_gpio);
-
- /* Configure the desired gpio port for input */
- ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio);
- } else {
- /* btcoex 3-wire */
- REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
- (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB |
- AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB));
-
- /* Set input mux for bt_prority_async and
- * bt_active_async to GPIO pins */
- REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
- AR_GPIO_INPUT_MUX1_BT_ACTIVE,
- btcoex_info->btactive_gpio);
-
- REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
- AR_GPIO_INPUT_MUX1_BT_PRIORITY,
- btcoex_info->btpriority_gpio);
-
- /* Configure the desired GPIO ports for input */
-
- ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio);
- ath9k_hw_cfg_gpio_input(ah, btcoex_info->btpriority_gpio);
-
- ret = ath_init_btcoex_info(ah, btcoex_info);
- }
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
- return ret;
+ /* Configure the desired GPIO port for TX_FRAME output */
+ ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio,
+ AR_GPIO_OUTPUT_MUX_AS_TX_FRAME);
}
-void ath9k_hw_btcoex_enable(struct ath_hw *ah)
+void ath9k_hw_btcoex_set_weight(struct ath_hw *ah,
+ u32 bt_weight,
+ u32 wlan_weight)
{
- struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
-
- if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) {
- /* Configure the desired GPIO port for TX_FRAME output */
- ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
- AR_GPIO_OUTPUT_MUX_AS_TX_FRAME);
- } else {
- /*
- * Program coex mode and weight registers to
- * enable coex 3-wire
- */
- REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_info->bt_coex_mode);
- REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_info->bt_coex_weights);
- REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_info->bt_coex_mode2);
-
- REG_RMW_FIELD(ah, AR_QUIET1,
- AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
- REG_RMW_FIELD(ah, AR_PCU_MISC,
- AR_PCU_BT_ANT_PREVENT_RX, 0);
-
- ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
- AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL);
- }
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
- REG_RMW(ah, AR_GPIO_PDPU,
- (0x2 << (btcoex_info->btactive_gpio * 2)),
- (0x3 << (btcoex_info->btactive_gpio * 2)));
-
- ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED;
+ btcoex_hw->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) |
+ SM(wlan_weight, AR_BTCOEX_WL_WGHT);
}
+EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight);
-void ath9k_hw_btcoex_disable(struct ath_hw *ah)
+static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah)
{
- struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
- ath9k_hw_set_gpio(ah, btcoex_info->wlanactive_gpio, 0);
+ /*
+ * Program coex mode and weight registers to
+ * enable coex 3-wire
+ */
+ REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_hw->bt_coex_mode);
+ REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_hw->bt_coex_weights);
+ REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_hw->bt_coex_mode2);
- ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio,
- AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+ REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
+ REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0);
- if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) {
- REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE);
- REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0);
- REG_WRITE(ah, AR_BT_COEX_MODE2, 0);
- }
-
- ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED;
+ ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio,
+ AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL);
}
-/*
- * Pause btcoex timer and bt duty cycle timer
- */
-void ath_btcoex_timer_pause(struct ath_softc *sc,
- struct ath_btcoex_info *btinfo)
+void ath9k_hw_btcoex_enable(struct ath_hw *ah)
{
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
- del_timer_sync(&btinfo->period_timer);
+ switch (btcoex_hw->scheme) {
+ case ATH_BTCOEX_CFG_NONE:
+ break;
+ case ATH_BTCOEX_CFG_2WIRE:
+ ath9k_hw_btcoex_enable_2wire(ah);
+ break;
+ case ATH_BTCOEX_CFG_3WIRE:
+ ath9k_hw_btcoex_enable_3wire(ah);
+ break;
+ }
- if (btinfo->hw_timer_enabled)
- ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
+ REG_RMW(ah, AR_GPIO_PDPU,
+ (0x2 << (btcoex_hw->btactive_gpio * 2)),
+ (0x3 << (btcoex_hw->btactive_gpio * 2)));
- btinfo->hw_timer_enabled = false;
+ ah->btcoex_hw.enabled = true;
}
+EXPORT_SYMBOL(ath9k_hw_btcoex_enable);
-/*
- * (Re)start btcoex timers
- */
-void ath_btcoex_timer_resume(struct ath_softc *sc,
- struct ath_btcoex_info *btinfo)
+void ath9k_hw_btcoex_disable(struct ath_hw *ah)
{
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
- DPRINTF(sc, ATH_DBG_BTCOEX, "Starting btcoex timers");
+ ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0);
- /* make sure duty cycle timer is also stopped when resuming */
- if (btinfo->hw_timer_enabled)
- ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer);
+ ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio,
+ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- btinfo->bt_priority_cnt = 0;
- btinfo->bt_priority_time = jiffies;
- sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
+ if (btcoex_hw->scheme == ATH_BTCOEX_CFG_3WIRE) {
+ REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE);
+ REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0);
+ REG_WRITE(ah, AR_BT_COEX_MODE2, 0);
+ }
- mod_timer(&btinfo->period_timer, jiffies);
+ ah->btcoex_hw.enabled = false;
}
+EXPORT_SYMBOL(ath9k_hw_btcoex_disable);
diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h
index 297b027fd3c..1ba31a73317 100644
--- a/drivers/net/wireless/ath/ath9k/btcoex.h
+++ b/drivers/net/wireless/ath/ath9k/btcoex.h
@@ -17,6 +17,8 @@
#ifndef BTCOEX_H
#define BTCOEX_H
+#include "hw.h"
+
#define ATH_WLANACTIVE_GPIO 5
#define ATH_BTACTIVE_GPIO 6
#define ATH_BTPRIORITY_GPIO 7
@@ -34,67 +36,25 @@ enum ath_btcoex_scheme {
ATH_BTCOEX_CFG_3WIRE,
};
-enum ath_stomp_type {
- ATH_BTCOEX_NO_STOMP,
- ATH_BTCOEX_STOMP_ALL,
- ATH_BTCOEX_STOMP_LOW,
- ATH_BTCOEX_STOMP_NONE
-};
-
-enum ath_bt_mode {
- ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */
- ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */
- ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */
- ATH_BT_COEX_MODE_DISALBED, /* coexistence disabled */
-};
-
-struct ath_btcoex_config {
- u8 bt_time_extend;
- bool bt_txstate_extend;
- bool bt_txframe_extend;
- enum ath_bt_mode bt_mode; /* coexistence mode */
- bool bt_quiet_collision;
- bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/
- u8 bt_priority_time;
- u8 bt_first_slot_time;
- bool bt_hold_rx_clear;
-};
-
-struct ath_btcoex_info {
- enum ath_btcoex_scheme btcoex_scheme;
+struct ath_btcoex_hw {
+ enum ath_btcoex_scheme scheme;
+ bool enabled;
u8 wlanactive_gpio;
u8 btactive_gpio;
u8 btpriority_gpio;
- u8 bt_duty_cycle; /* BT duty cycle in percentage */
- int bt_stomp_type; /* Types of BT stomping */
u32 bt_coex_mode; /* Register setting for AR_BT_COEX_MODE */
u32 bt_coex_weights; /* Register setting for AR_BT_COEX_WEIGHT */
u32 bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */
- u32 btcoex_no_stomp; /* in usec */
- u32 btcoex_period; /* in usec */
- u32 bt_priority_cnt;
- unsigned long bt_priority_time;
- bool hw_timer_enabled;
- spinlock_t btcoex_lock;
- struct timer_list period_timer; /* Timer for BT period */
- struct ath_gen_timer *no_stomp_timer; /*Timer for no BT stomping*/
};
-bool ath_btcoex_supported(u16 subsysid);
-int ath9k_hw_btcoex_init(struct ath_hw *ah);
+bool ath9k_hw_btcoex_supported(struct ath_hw *ah);
+void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah);
+void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah);
+void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum);
+void ath9k_hw_btcoex_set_weight(struct ath_hw *ah,
+ u32 bt_weight,
+ u32 wlan_weight);
void ath9k_hw_btcoex_enable(struct ath_hw *ah);
void ath9k_hw_btcoex_disable(struct ath_hw *ah);
-void ath_btcoex_timer_resume(struct ath_softc *sc,
- struct ath_btcoex_info *btinfo);
-void ath_btcoex_timer_pause(struct ath_softc *sc,
- struct ath_btcoex_info *btinfo);
-
-static inline void ath_btcoex_set_weight(struct ath_btcoex_info *btcoex_info,
- u32 bt_weight,
- u32 wlan_weight)
-{
- btcoex_info->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) |
- SM(wlan_weight, AR_BTCOEX_WL_WGHT);
-}
#endif
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 0ad6d0b76e9..238a5744d8e 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
/* We can tune this as we go by monitoring really low values */
#define ATH9K_NF_TOO_LOW -60
@@ -26,11 +26,11 @@
static bool ath9k_hw_nf_in_range(struct ath_hw *ah, s16 nf)
{
if (nf > ATH9K_NF_TOO_LOW) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "noise floor value detected (%d) is "
- "lower than what we think is a "
- "reasonable value (%d)\n",
- nf, ATH9K_NF_TOO_LOW);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
+ "noise floor value detected (%d) is "
+ "lower than what we think is a "
+ "reasonable value (%d)\n",
+ nf, ATH9K_NF_TOO_LOW);
return false;
}
return true;
@@ -89,6 +89,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
static void ath9k_hw_do_getnf(struct ath_hw *ah,
int16_t nfarray[NUM_NF_READINGS])
{
+ struct ath_common *common = ath9k_hw_common(ah);
int16_t nf;
if (AR_SREV_9280_10_OR_LATER(ah))
@@ -98,8 +99,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ctl] [chain 0] is %d\n", nf);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "NF calibrated [ctl] [chain 0] is %d\n", nf);
nfarray[0] = nf;
if (!AR_SREV_9285(ah)) {
@@ -112,8 +113,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ctl] [chain 1] is %d\n", nf);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "NF calibrated [ctl] [chain 1] is %d\n", nf);
nfarray[1] = nf;
if (!AR_SREV_9280(ah) && !AR_SREV_9287(ah)) {
@@ -121,8 +122,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
AR_PHY_CH2_MINCCA_PWR);
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ctl] [chain 2] is %d\n", nf);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "NF calibrated [ctl] [chain 2] is %d\n", nf);
nfarray[2] = nf;
}
}
@@ -136,8 +137,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ext] [chain 0] is %d\n", nf);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "NF calibrated [ext] [chain 0] is %d\n", nf);
nfarray[3] = nf;
if (!AR_SREV_9285(ah)) {
@@ -150,8 +151,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ext] [chain 1] is %d\n", nf);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "NF calibrated [ext] [chain 1] is %d\n", nf);
nfarray[4] = nf;
if (!AR_SREV_9280(ah) && !AR_SREV_9287(ah)) {
@@ -159,8 +160,8 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
AR_PHY_CH2_EXT_MINCCA_PWR);
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF calibrated [ext] [chain 2] is %d\n", nf);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "NF calibrated [ext] [chain 2] is %d\n", nf);
nfarray[5] = nf;
}
}
@@ -188,6 +189,8 @@ static bool getNoiseFloorThresh(struct ath_hw *ah,
static void ath9k_hw_setup_calibration(struct ath_hw *ah,
struct ath9k_cal_list *currCal)
{
+ struct ath_common *common = ath9k_hw_common(ah);
+
REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0),
AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
currCal->calData->calCountMax);
@@ -195,23 +198,23 @@ static void ath9k_hw_setup_calibration(struct ath_hw *ah,
switch (currCal->calData->calType) {
case IQ_MISMATCH_CAL:
REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "starting IQ Mismatch Calibration\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "starting IQ Mismatch Calibration\n");
break;
case ADC_GAIN_CAL:
REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "starting ADC Gain Calibration\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "starting ADC Gain Calibration\n");
break;
case ADC_DC_CAL:
REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "starting ADC DC Calibration\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "starting ADC DC Calibration\n");
break;
case ADC_DC_INIT_CAL:
REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "starting Init ADC DC Calibration\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "starting Init ADC DC Calibration\n");
break;
}
@@ -278,7 +281,7 @@ static bool ath9k_hw_per_calibration(struct ath_hw *ah,
static bool ath9k_hw_iscal_supported(struct ath_hw *ah,
enum ath9k_cal_types calType)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
switch (calType & ah->supp_cals) {
case IQ_MISMATCH_CAL: /* Both 2 GHz and 5 GHz support OFDM */
@@ -304,11 +307,11 @@ static void ath9k_hw_iqcal_collect(struct ath_hw *ah)
REG_READ(ah, AR_PHY_CAL_MEAS_1(i));
ah->totalIqCorrMeas[i] +=
(int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i));
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
- ah->cal_samples, i, ah->totalPowerMeasI[i],
- ah->totalPowerMeasQ[i],
- ah->totalIqCorrMeas[i]);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
+ "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n",
+ ah->cal_samples, i, ah->totalPowerMeasI[i],
+ ah->totalPowerMeasQ[i],
+ ah->totalIqCorrMeas[i]);
}
}
@@ -326,14 +329,14 @@ static void ath9k_hw_adc_gaincal_collect(struct ath_hw *ah)
ah->totalAdcQEvenPhase[i] +=
REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
- "oddq=0x%08x; evenq=0x%08x;\n",
- ah->cal_samples, i,
- ah->totalAdcIOddPhase[i],
- ah->totalAdcIEvenPhase[i],
- ah->totalAdcQOddPhase[i],
- ah->totalAdcQEvenPhase[i]);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
+ "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
+ "oddq=0x%08x; evenq=0x%08x;\n",
+ ah->cal_samples, i,
+ ah->totalAdcIOddPhase[i],
+ ah->totalAdcIEvenPhase[i],
+ ah->totalAdcQOddPhase[i],
+ ah->totalAdcQEvenPhase[i]);
}
}
@@ -351,19 +354,20 @@ static void ath9k_hw_adc_dccal_collect(struct ath_hw *ah)
ah->totalAdcDcOffsetQEvenPhase[i] +=
(int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i));
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
- "oddq=0x%08x; evenq=0x%08x;\n",
- ah->cal_samples, i,
- ah->totalAdcDcOffsetIOddPhase[i],
- ah->totalAdcDcOffsetIEvenPhase[i],
- ah->totalAdcDcOffsetQOddPhase[i],
- ah->totalAdcDcOffsetQEvenPhase[i]);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
+ "%d: Chn %d oddi=0x%08x; eveni=0x%08x; "
+ "oddq=0x%08x; evenq=0x%08x;\n",
+ ah->cal_samples, i,
+ ah->totalAdcDcOffsetIOddPhase[i],
+ ah->totalAdcDcOffsetIEvenPhase[i],
+ ah->totalAdcDcOffsetQOddPhase[i],
+ ah->totalAdcDcOffsetQEvenPhase[i]);
}
}
static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
{
+ struct ath_common *common = ath9k_hw_common(ah);
u32 powerMeasQ, powerMeasI, iqCorrMeas;
u32 qCoffDenom, iCoffDenom;
int32_t qCoff, iCoff;
@@ -374,13 +378,13 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
powerMeasQ = ah->totalPowerMeasQ[i];
iqCorrMeas = ah->totalIqCorrMeas[i];
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Starting IQ Cal and Correction for Chain %d\n",
- i);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Starting IQ Cal and Correction for Chain %d\n",
+ i);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Orignal: Chn %diq_corr_meas = 0x%08x\n",
- i, ah->totalIqCorrMeas[i]);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Orignal: Chn %diq_corr_meas = 0x%08x\n",
+ i, ah->totalIqCorrMeas[i]);
iqCorrNeg = 0;
@@ -389,27 +393,28 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
iqCorrNeg = 1;
}
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
- iqCorrNeg);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ);
+ ath_print(common, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n",
+ iqCorrNeg);
iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128;
qCoffDenom = powerMeasQ / 64;
- if (powerMeasQ != 0) {
+ if ((powerMeasQ != 0) && (iCoffDenom != 0) &&
+ (qCoffDenom != 0)) {
iCoff = iqCorrMeas / iCoffDenom;
qCoff = powerMeasI / qCoffDenom - 64;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d iCoff = 0x%08x\n", i, iCoff);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d qCoff = 0x%08x\n", i, qCoff);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d iCoff = 0x%08x\n", i, iCoff);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d qCoff = 0x%08x\n", i, qCoff);
iCoff = iCoff & 0x3f;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "New: Chn %d iCoff = 0x%08x\n", i, iCoff);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "New: Chn %d iCoff = 0x%08x\n", i, iCoff);
if (iqCorrNeg == 0x0)
iCoff = 0x40 - iCoff;
@@ -418,9 +423,9 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
else if (qCoff <= -16)
qCoff = 16;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d : iCoff = 0x%x qCoff = 0x%x\n",
- i, iCoff, qCoff);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d : iCoff = 0x%x qCoff = 0x%x\n",
+ i, iCoff, qCoff);
REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF,
@@ -428,9 +433,9 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i),
AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF,
qCoff);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "IQ Cal and Correction done for Chain %d\n",
- i);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "IQ Cal and Correction done for Chain %d\n",
+ i);
}
}
@@ -440,6 +445,7 @@ static void ath9k_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains)
{
+ struct ath_common *common = ath9k_hw_common(ah);
u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset, qEvenMeasOffset;
u32 qGainMismatch, iGainMismatch, val, i;
@@ -449,21 +455,21 @@ static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains)
qOddMeasOffset = ah->totalAdcQOddPhase[i];
qEvenMeasOffset = ah->totalAdcQEvenPhase[i];
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Starting ADC Gain Cal for Chain %d\n", i);
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_i = 0x%08x\n", i,
- iOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_i = 0x%08x\n", i,
- iEvenMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_q = 0x%08x\n", i,
- qOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_q = 0x%08x\n", i,
- qEvenMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Starting ADC Gain Cal for Chain %d\n", i);
+
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_i = 0x%08x\n", i,
+ iOddMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_i = 0x%08x\n", i,
+ iEvenMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_q = 0x%08x\n", i,
+ qOddMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_q = 0x%08x\n", i,
+ qEvenMeasOffset);
if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) {
iGainMismatch =
@@ -473,20 +479,20 @@ static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains)
((qOddMeasOffset * 32) /
qEvenMeasOffset) & 0x3f;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d gain_mismatch_i = 0x%08x\n", i,
- iGainMismatch);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d gain_mismatch_q = 0x%08x\n", i,
- qGainMismatch);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d gain_mismatch_i = 0x%08x\n", i,
+ iGainMismatch);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d gain_mismatch_q = 0x%08x\n", i,
+ qGainMismatch);
val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
val &= 0xfffff000;
val |= (qGainMismatch) | (iGainMismatch << 6);
REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "ADC Gain Cal done for Chain %d\n", i);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "ADC Gain Cal done for Chain %d\n", i);
}
}
@@ -497,6 +503,7 @@ static void ath9k_hw_adc_gaincal_calibrate(struct ath_hw *ah, u8 numChains)
static void ath9k_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains)
{
+ struct ath_common *common = ath9k_hw_common(ah);
u32 iOddMeasOffset, iEvenMeasOffset, val, i;
int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch;
const struct ath9k_percal_data *calData =
@@ -510,41 +517,41 @@ static void ath9k_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains)
qOddMeasOffset = ah->totalAdcDcOffsetQOddPhase[i];
qEvenMeasOffset = ah->totalAdcDcOffsetQEvenPhase[i];
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Starting ADC DC Offset Cal for Chain %d\n", i);
-
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_i = %d\n", i,
- iOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_i = %d\n", i,
- iEvenMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_odd_q = %d\n", i,
- qOddMeasOffset);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d pwr_meas_even_q = %d\n", i,
- qEvenMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Starting ADC DC Offset Cal for Chain %d\n", i);
+
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_i = %d\n", i,
+ iOddMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_i = %d\n", i,
+ iEvenMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_odd_q = %d\n", i,
+ qOddMeasOffset);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d pwr_meas_even_q = %d\n", i,
+ qEvenMeasOffset);
iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) /
numSamples) & 0x1ff;
qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) /
numSamples) & 0x1ff;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d dc_offset_mismatch_i = 0x%08x\n", i,
- iDcMismatch);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Chn %d dc_offset_mismatch_q = 0x%08x\n", i,
- qDcMismatch);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d dc_offset_mismatch_i = 0x%08x\n", i,
+ iDcMismatch);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Chn %d dc_offset_mismatch_q = 0x%08x\n", i,
+ qDcMismatch);
val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
val &= 0xc0000fff;
val |= (qDcMismatch << 12) | (iDcMismatch << 21);
REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "ADC DC Offset Cal done for Chain %d\n", i);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "ADC DC Offset Cal done for Chain %d\n", i);
}
REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
@@ -555,7 +562,8 @@ static void ath9k_hw_adc_dccal_calibrate(struct ath_hw *ah, u8 numChains)
/* This is done for the currently configured channel */
bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_conf *conf = &common->hw->conf;
struct ath9k_cal_list *currCal = ah->cal_list_curr;
if (!ah->curchan)
@@ -568,24 +576,25 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
return true;
if (currCal->calState != CAL_DONE) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Calibration state incorrect, %d\n",
- currCal->calState);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Calibration state incorrect, %d\n",
+ currCal->calState);
return true;
}
if (!ath9k_hw_iscal_supported(ah, currCal->calData->calType))
return true;
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "Resetting Cal %d state for channel %u\n",
- currCal->calData->calType, conf->channel->center_freq);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "Resetting Cal %d state for channel %u\n",
+ currCal->calData->calType, conf->channel->center_freq);
ah->curchan->CalValid &= ~currCal->calData->calType;
currCal->calState = CAL_WAITING;
return false;
}
+EXPORT_SYMBOL(ath9k_hw_reset_calvalid);
void ath9k_hw_start_nfcal(struct ath_hw *ah)
{
@@ -645,11 +654,11 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
- for (j = 0; j < 1000; j++) {
+ for (j = 0; j < 5; j++) {
if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
AR_PHY_AGC_CONTROL_NF) == 0)
break;
- udelay(10);
+ udelay(50);
}
for (i = 0; i < NUM_NF_READINGS; i++) {
@@ -665,6 +674,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
int16_t ath9k_hw_getnf(struct ath_hw *ah,
struct ath9k_channel *chan)
{
+ struct ath_common *common = ath9k_hw_common(ah);
int16_t nf, nfThresh;
int16_t nfarray[NUM_NF_READINGS] = { 0 };
struct ath9k_nfcal_hist *h;
@@ -672,8 +682,8 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
chan->channelFlags &= (~CHANNEL_CW_INT);
if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "NF did not complete in calibration window\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "NF did not complete in calibration window\n");
nf = 0;
chan->rawNoiseFloor = nf;
return chan->rawNoiseFloor;
@@ -682,10 +692,10 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
nf = nfarray[0];
if (getNoiseFloorThresh(ah, c->band, &nfThresh)
&& nf > nfThresh) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "noise floor failed detected; "
- "detected %d, threshold %d\n",
- nf, nfThresh);
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "noise floor failed detected; "
+ "detected %d, threshold %d\n",
+ nf, nfThresh);
chan->channelFlags |= CHANNEL_CW_INT;
}
}
@@ -737,51 +747,73 @@ s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan)
return nf;
}
+EXPORT_SYMBOL(ath9k_hw_getchan_noise);
-static void ath9k_olc_temp_compensation(struct ath_hw *ah)
+static void ath9k_olc_temp_compensation_9287(struct ath_hw *ah)
{
- u32 rddata, i;
- int delta, currPDADC, regval, slope;
+ u32 rddata;
+ int32_t delta, currPDADC, slope;
rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4);
currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
+ if (ah->initPDADC == 0 || currPDADC == 0) {
+ /*
+ * Zero value indicates that no frames have been transmitted yet,
+ * can't do temperature compensation until frames are transmitted.
+ */
+ return;
+ } else {
+ slope = ah->eep_ops->get_eeprom(ah, EEP_TEMPSENSE_SLOPE);
+
+ if (slope == 0) { /* to avoid divide by zero case */
+ delta = 0;
+ } else {
+ delta = ((currPDADC - ah->initPDADC)*4) / slope;
+ }
+ REG_RMW_FIELD(ah, AR_PHY_CH0_TX_PWRCTRL11,
+ AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta);
+ REG_RMW_FIELD(ah, AR_PHY_CH1_TX_PWRCTRL11,
+ AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta);
+ }
+}
+
+static void ath9k_olc_temp_compensation(struct ath_hw *ah)
+{
+ u32 rddata, i;
+ int delta, currPDADC, regval;
if (OLC_FOR_AR9287_10_LATER) {
+ ath9k_olc_temp_compensation_9287(ah);
+ } else {
+ rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4);
+ currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
+
if (ah->initPDADC == 0 || currPDADC == 0) {
return;
} else {
- slope = ah->eep_ops->get_eeprom(ah, EEP_TEMPSENSE_SLOPE);
- if (slope == 0)
- delta = 0;
+ if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G))
+ delta = (currPDADC - ah->initPDADC + 4) / 8;
else
- delta = ((currPDADC - ah->initPDADC)*4) / slope;
- REG_RMW_FIELD(ah, AR_PHY_CH0_TX_PWRCTRL11,
- AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta);
- REG_RMW_FIELD(ah, AR_PHY_CH1_TX_PWRCTRL11,
- AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta);
- }
- } else {
- if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G))
- delta = (currPDADC - ah->initPDADC + 4) / 8;
- else
- delta = (currPDADC - ah->initPDADC + 5) / 10;
-
- if (delta != ah->PDADCdelta) {
- ah->PDADCdelta = delta;
- for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
- regval = ah->originalGain[i] - delta;
- if (regval < 0)
- regval = 0;
-
- REG_RMW_FIELD(ah, AR_PHY_TX_GAIN_TBL1 + i * 4,
- AR_PHY_TX_GAIN, regval);
+ delta = (currPDADC - ah->initPDADC + 5) / 10;
+
+ if (delta != ah->PDADCdelta) {
+ ah->PDADCdelta = delta;
+ for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
+ regval = ah->originalGain[i] - delta;
+ if (regval < 0)
+ regval = 0;
+
+ REG_RMW_FIELD(ah,
+ AR_PHY_TX_GAIN_TBL1 + i * 4,
+ AR_PHY_TX_GAIN, regval);
+ }
}
}
}
}
-static void ath9k_hw_9271_pa_cal(struct ath_hw *ah)
+static void ath9k_hw_9271_pa_cal(struct ath_hw *ah, bool is_reset)
{
u32 regVal;
unsigned int i;
@@ -845,7 +877,7 @@ static void ath9k_hw_9271_pa_cal(struct ath_hw *ah)
REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0);
/* find off_6_1; */
- for (i = 6; i >= 0; i--) {
+ for (i = 6; i > 0; i--) {
regVal = REG_READ(ah, 0x7834);
regVal |= (1 << (20 + i));
REG_WRITE(ah, 0x7834, regVal);
@@ -857,10 +889,19 @@ static void ath9k_hw_9271_pa_cal(struct ath_hw *ah)
REG_WRITE(ah, 0x7834, regVal);
}
- /* Empirical offset correction */
-#if 0
- REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0x20);
-#endif
+ regVal = (regVal >>20) & 0x7f;
+
+ /* Update PA cal info */
+ if ((!is_reset) && (ah->pacal_info.prev_offset == regVal)) {
+ if (ah->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT)
+ ah->pacal_info.max_skipcount =
+ 2 * ah->pacal_info.max_skipcount;
+ ah->pacal_info.skipcount = ah->pacal_info.max_skipcount;
+ } else {
+ ah->pacal_info.max_skipcount = 1;
+ ah->pacal_info.skipcount = 0;
+ ah->pacal_info.prev_offset = regVal;
+ }
regVal = REG_READ(ah, 0x7834);
regVal |= 0x1;
@@ -875,7 +916,7 @@ static void ath9k_hw_9271_pa_cal(struct ath_hw *ah)
static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah, bool is_reset)
{
-
+ struct ath_common *common = ath9k_hw_common(ah);
u32 regVal;
int i, offset, offs_6_1, offs_0;
u32 ccomp_org, reg_field;
@@ -889,7 +930,7 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah, bool is_reset)
{ 0x7838, 0 },
};
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "Running PA Calibration\n");
+ ath_print(common, ATH_DBG_CALIBRATE, "Running PA Calibration\n");
/* PA CAL is not needed for high power solution */
if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) ==
@@ -1011,7 +1052,7 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
if (longcal) {
/* Do periodic PAOffset Cal */
if (AR_SREV_9271(ah))
- ath9k_hw_9271_pa_cal(ah);
+ ath9k_hw_9271_pa_cal(ah, false);
else if (AR_SREV_9285_11_OR_LATER(ah)) {
if (!ah->pacal_info.skipcount)
ath9k_hw_9285_pa_cal(ah, false);
@@ -1036,9 +1077,13 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
return iscaldone;
}
+EXPORT_SYMBOL(ath9k_hw_calibrate);
+/* Carrier leakage Calibration fix */
static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan)
{
+ struct ath_common *common = ath9k_hw_common(ah);
+
REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE);
if (IS_CHAN_HT20(chan)) {
REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE);
@@ -1049,9 +1094,9 @@ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan)
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "offset "
- "calibration failed to complete in "
- "1ms; noisy ??\n");
+ ath_print(common, ATH_DBG_CALIBRATE, "offset "
+ "calibration failed to complete in "
+ "1ms; noisy ??\n");
return false;
}
REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN);
@@ -1064,8 +1109,8 @@ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan)
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL,
0, AH_WAIT_TIMEOUT)) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "offset calibration "
- "failed to complete in 1ms; noisy ??\n");
+ ath_print(common, ATH_DBG_CALIBRATE, "offset calibration "
+ "failed to complete in 1ms; noisy ??\n");
return false;
}
@@ -1078,7 +1123,9 @@ static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan)
bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
{
- if (AR_SREV_9285_12_OR_LATER(ah)) {
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (AR_SREV_9271(ah) || AR_SREV_9285_12_OR_LATER(ah)) {
if (!ar9285_clc(ah, chan))
return false;
} else {
@@ -1098,9 +1145,9 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
/* Poll for offset calibration complete */
if (!ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL,
0, AH_WAIT_TIMEOUT)) {
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "offset calibration failed to complete in 1ms; "
- "noisy environment?\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "offset calibration failed to "
+ "complete in 1ms; noisy environment?\n");
return false;
}
@@ -1114,7 +1161,9 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
}
/* Do PA Calibration */
- if (AR_SREV_9285_11_OR_LATER(ah))
+ if (AR_SREV_9271(ah))
+ ath9k_hw_9271_pa_cal(ah, true);
+ else if (AR_SREV_9285_11_OR_LATER(ah))
ath9k_hw_9285_pa_cal(ah, true);
/* Do NF Calibration after DC offset and other calibrations */
@@ -1128,20 +1177,20 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan)
if (ath9k_hw_iscal_supported(ah, ADC_GAIN_CAL)) {
INIT_CAL(&ah->adcgain_caldata);
INSERT_CAL(ah, &ah->adcgain_caldata);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "enabling ADC Gain Calibration.\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "enabling ADC Gain Calibration.\n");
}
if (ath9k_hw_iscal_supported(ah, ADC_DC_CAL)) {
INIT_CAL(&ah->adcdc_caldata);
INSERT_CAL(ah, &ah->adcdc_caldata);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "enabling ADC DC Calibration.\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "enabling ADC DC Calibration.\n");
}
if (ath9k_hw_iscal_supported(ah, IQ_MISMATCH_CAL)) {
INIT_CAL(&ah->iq_caldata);
INSERT_CAL(ah, &ah->iq_caldata);
- DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
- "enabling IQ Calibration.\n");
+ ath_print(common, ATH_DBG_CALIBRATE,
+ "enabling IQ Calibration.\n");
}
ah->cal_list_curr = ah->cal_list;
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h
index 9028ab193e4..b2c873e9748 100644
--- a/drivers/net/wireless/ath/ath9k/calib.h
+++ b/drivers/net/wireless/ath/ath9k/calib.h
@@ -17,6 +17,8 @@
#ifndef CALIB_H
#define CALIB_H
+#include "hw.h"
+
extern const struct ath9k_percal_data iq_cal_multi_sample;
extern const struct ath9k_percal_data iq_cal_single_sample;
extern const struct ath9k_percal_data adc_gain_cal_multi_sample;
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
new file mode 100644
index 00000000000..4d775ae141d
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Module for common driver code between ath9k and ath9k_htc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "common.h"
+
+MODULE_AUTHOR("Atheros Communications");
+MODULE_DESCRIPTION("Shared library for Atheros wireless 802.11n LAN cards.");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* Common RX processing */
+
+/* Assumes you've already done the endian to CPU conversion */
+static bool ath9k_rx_accept(struct ath_common *common,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rxs,
+ struct ath_rx_status *rx_stats,
+ bool *decrypt_error)
+{
+ struct ath_hw *ah = common->ah;
+ struct ieee80211_hdr *hdr;
+ __le16 fc;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+ fc = hdr->frame_control;
+
+ if (!rx_stats->rs_datalen)
+ return false;
+ /*
+ * rs_status follows rs_datalen so if rs_datalen is too large
+ * we can take a hint that hardware corrupted it, so ignore
+ * those frames.
+ */
+ if (rx_stats->rs_datalen > common->rx_bufsize)
+ return false;
+
+ /*
+ * rs_more indicates chained descriptors which can be used
+ * to link buffers together for a sort of scatter-gather
+ * operation.
+ *
+ * The rx_stats->rs_status will not be set until the end of the
+ * chained descriptors so it can be ignored if rs_more is set. The
+ * rs_more will be false at the last element of the chained
+ * descriptors.
+ */
+ if (!rx_stats->rs_more && rx_stats->rs_status != 0) {
+ if (rx_stats->rs_status & ATH9K_RXERR_CRC)
+ rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
+ if (rx_stats->rs_status & ATH9K_RXERR_PHY)
+ return false;
+
+ if (rx_stats->rs_status & ATH9K_RXERR_DECRYPT) {
+ *decrypt_error = true;
+ } else if (rx_stats->rs_status & ATH9K_RXERR_MIC) {
+ if (ieee80211_is_ctl(fc))
+ /*
+ * Sometimes, we get invalid
+ * MIC failures on valid control frames.
+ * Remove these mic errors.
+ */
+ rx_stats->rs_status &= ~ATH9K_RXERR_MIC;
+ else
+ rxs->flag |= RX_FLAG_MMIC_ERROR;
+ }
+ /*
+ * Reject error frames with the exception of
+ * decryption and MIC failures. For monitor mode,
+ * we also ignore the CRC error.
+ */
+ if (ah->opmode == NL80211_IFTYPE_MONITOR) {
+ if (rx_stats->rs_status &
+ ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
+ ATH9K_RXERR_CRC))
+ return false;
+ } else {
+ if (rx_stats->rs_status &
+ ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static u8 ath9k_process_rate(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs,
+ struct sk_buff *skb)
+{
+ struct ieee80211_supported_band *sband;
+ enum ieee80211_band band;
+ unsigned int i = 0;
+
+ band = hw->conf.channel->band;
+ sband = hw->wiphy->bands[band];
+
+ if (rx_stats->rs_rate & 0x80) {
+ /* HT rate */
+ rxs->flag |= RX_FLAG_HT;
+ if (rx_stats->rs_flags & ATH9K_RX_2040)
+ rxs->flag |= RX_FLAG_40MHZ;
+ if (rx_stats->rs_flags & ATH9K_RX_GI)
+ rxs->flag |= RX_FLAG_SHORT_GI;
+ return rx_stats->rs_rate & 0x7f;
+ }
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].hw_value == rx_stats->rs_rate)
+ return i;
+ if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) {
+ rxs->flag |= RX_FLAG_SHORTPRE;
+ return i;
+ }
+ }
+
+ /* No valid hardware bitrate found -- we should not get here */
+ ath_print(common, ATH_DBG_XMIT, "unsupported hw bitrate detected "
+ "0x%02x using 1 Mbit\n", rx_stats->rs_rate);
+ if ((common->debug_mask & ATH_DBG_XMIT))
+ print_hex_dump_bytes("", DUMP_PREFIX_NONE, skb->data, skb->len);
+
+ return 0;
+}
+
+static void ath9k_process_rssi(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats)
+{
+ struct ath_hw *ah = common->ah;
+ struct ieee80211_sta *sta;
+ struct ieee80211_hdr *hdr;
+ struct ath_node *an;
+ int last_rssi = ATH_RSSI_DUMMY_MARKER;
+ __le16 fc;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ fc = hdr->frame_control;
+
+ rcu_read_lock();
+ /*
+ * XXX: use ieee80211_find_sta! This requires quite a bit of work
+ * under the current ath9k virtual wiphy implementation as we have
+ * no way of tying a vif to wiphy. Typically vifs are attached to
+ * at least one sdata of a wiphy on mac80211 but with ath9k virtual
+ * wiphy you'd have to iterate over every wiphy and each sdata.
+ */
+ sta = ieee80211_find_sta_by_hw(hw, hdr->addr2);
+ if (sta) {
+ an = (struct ath_node *) sta->drv_priv;
+ if (rx_stats->rs_rssi != ATH9K_RSSI_BAD &&
+ !rx_stats->rs_moreaggr)
+ ATH_RSSI_LPF(an->last_rssi, rx_stats->rs_rssi);
+ last_rssi = an->last_rssi;
+ }
+ rcu_read_unlock();
+
+ if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+ rx_stats->rs_rssi = ATH_EP_RND(last_rssi,
+ ATH_RSSI_EP_MULTIPLIER);
+ if (rx_stats->rs_rssi < 0)
+ rx_stats->rs_rssi = 0;
+
+ /* Update Beacon RSSI, this is used by ANI. */
+ if (ieee80211_is_beacon(fc))
+ ah->stats.avgbrssi = rx_stats->rs_rssi;
+}
+
+/*
+ * For Decrypt or Demic errors, we only mark packet status here and always push
+ * up the frame up to let mac80211 handle the actual error case, be it no
+ * decryption key or real decryption error. This let us keep statistics there.
+ */
+int ath9k_cmn_rx_skb_preprocess(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rx_status,
+ bool *decrypt_error)
+{
+ struct ath_hw *ah = common->ah;
+
+ memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
+ if (!ath9k_rx_accept(common, skb, rx_status, rx_stats, decrypt_error))
+ return -EINVAL;
+
+ ath9k_process_rssi(common, hw, skb, rx_stats);
+
+ rx_status->rate_idx = ath9k_process_rate(common, hw,
+ rx_stats, rx_status, skb);
+ rx_status->mactime = ath9k_hw_extend_tsf(ah, rx_stats->rs_tstamp);
+ rx_status->band = hw->conf.channel->band;
+ rx_status->freq = hw->conf.channel->center_freq;
+ rx_status->noise = common->ani.noise_floor;
+ rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + rx_stats->rs_rssi;
+ rx_status->antenna = rx_stats->rs_antenna;
+ rx_status->flag |= RX_FLAG_TSFT;
+
+ return 0;
+}
+EXPORT_SYMBOL(ath9k_cmn_rx_skb_preprocess);
+
+void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs,
+ bool decrypt_error)
+{
+ struct ath_hw *ah = common->ah;
+ struct ieee80211_hdr *hdr;
+ int hdrlen, padpos, padsize;
+ u8 keyix;
+ __le16 fc;
+
+ /* see if any padding is done by the hw and remove it */
+ hdr = (struct ieee80211_hdr *) skb->data;
+ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+ fc = hdr->frame_control;
+ padpos = ath9k_cmn_padpos(hdr->frame_control);
+
+ /* The MAC header is padded to have 32-bit boundary if the
+ * packet payload is non-zero. The general calculation for
+ * padsize would take into account odd header lengths:
+ * padsize = (4 - padpos % 4) % 4; However, since only
+ * even-length headers are used, padding can only be 0 or 2
+ * bytes and we can optimize this a bit. In addition, we must
+ * not try to remove padding from short control frames that do
+ * not have payload. */
+ padsize = padpos & 3;
+ if (padsize && skb->len>=padpos+padsize+FCS_LEN) {
+ memmove(skb->data + padsize, skb->data, padpos);
+ skb_pull(skb, padsize);
+ }
+
+ keyix = rx_stats->rs_keyix;
+
+ if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) {
+ rxs->flag |= RX_FLAG_DECRYPTED;
+ } else if (ieee80211_has_protected(fc)
+ && !decrypt_error && skb->len >= hdrlen + 4) {
+ keyix = skb->data[hdrlen + 3] >> 6;
+
+ if (test_bit(keyix, common->keymap))
+ rxs->flag |= RX_FLAG_DECRYPTED;
+ }
+ if (ah->sw_mgmt_crypto &&
+ (rxs->flag & RX_FLAG_DECRYPTED) &&
+ ieee80211_is_mgmt(fc))
+ /* Use software decrypt for management frames. */
+ rxs->flag &= ~RX_FLAG_DECRYPTED;
+}
+EXPORT_SYMBOL(ath9k_cmn_rx_skb_postprocess);
+
+int ath9k_cmn_padpos(__le16 frame_control)
+{
+ int padpos = 24;
+ if (ieee80211_has_a4(frame_control)) {
+ padpos += ETH_ALEN;
+ }
+ if (ieee80211_is_data_qos(frame_control)) {
+ padpos += IEEE80211_QOS_CTL_LEN;
+ }
+
+ return padpos;
+}
+EXPORT_SYMBOL(ath9k_cmn_padpos);
+
+static int __init ath9k_cmn_init(void)
+{
+ return 0;
+}
+module_init(ath9k_cmn_init);
+
+static void __exit ath9k_cmn_exit(void)
+{
+ return;
+}
+module_exit(ath9k_cmn_exit);
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
new file mode 100644
index 00000000000..042999c2fe9
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <net/mac80211.h>
+
+#include "../ath.h"
+#include "../debug.h"
+
+#include "hw.h"
+
+/* Common header for Atheros 802.11n base driver cores */
+
+#define WME_NUM_TID 16
+#define WME_BA_BMP_SIZE 64
+#define WME_MAX_BA WME_BA_BMP_SIZE
+#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA)
+
+#define WME_AC_BE 0
+#define WME_AC_BK 1
+#define WME_AC_VI 2
+#define WME_AC_VO 3
+#define WME_NUM_AC 4
+
+#define ATH_RSSI_DUMMY_MARKER 0x127
+#define ATH_RSSI_LPF_LEN 10
+#define RSSI_LPF_THRESHOLD -20
+#define ATH_RSSI_EP_MULTIPLIER (1<<7)
+#define ATH_EP_MUL(x, mul) ((x) * (mul))
+#define ATH_RSSI_IN(x) (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER))
+#define ATH_LPF_RSSI(x, y, len) \
+ ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define ATH_RSSI_LPF(x, y) do { \
+ if ((y) >= RSSI_LPF_THRESHOLD) \
+ x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \
+} while (0)
+#define ATH_EP_RND(x, mul) \
+ ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+
+struct ath_atx_ac {
+ int sched;
+ int qnum;
+ struct list_head list;
+ struct list_head tid_q;
+};
+
+struct ath_buf_state {
+ int bfs_nframes;
+ u16 bfs_al;
+ u16 bfs_frmlen;
+ int bfs_seqno;
+ int bfs_tidno;
+ int bfs_retries;
+ u8 bf_type;
+ u32 bfs_keyix;
+ enum ath9k_key_type bfs_keytype;
+};
+
+struct ath_buf {
+ struct list_head list;
+ struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or
+ an aggregate) */
+ struct ath_buf *bf_next; /* next subframe in the aggregate */
+ struct sk_buff *bf_mpdu; /* enclosing frame structure */
+ struct ath_desc *bf_desc; /* virtual addr of desc */
+ dma_addr_t bf_daddr; /* physical addr of desc */
+ dma_addr_t bf_buf_addr; /* physical addr of data buffer */
+ bool bf_stale;
+ bool bf_isnullfunc;
+ u16 bf_flags;
+ struct ath_buf_state bf_state;
+ dma_addr_t bf_dmacontext;
+ struct ath_wiphy *aphy;
+};
+
+struct ath_atx_tid {
+ struct list_head list;
+ struct list_head buf_q;
+ struct ath_node *an;
+ struct ath_atx_ac *ac;
+ struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];
+ u16 seq_start;
+ u16 seq_next;
+ u16 baw_size;
+ int tidno;
+ int baw_head; /* first un-acked tx buffer */
+ int baw_tail; /* next unused tx buffer slot */
+ int sched;
+ int paused;
+ u8 state;
+};
+
+struct ath_node {
+ struct ath_common *common;
+ struct ath_atx_tid tid[WME_NUM_TID];
+ struct ath_atx_ac ac[WME_NUM_AC];
+ u16 maxampdu;
+ u8 mpdudensity;
+ int last_rssi;
+};
+
+int ath9k_cmn_rx_skb_preprocess(struct ath_common *common,
+ struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rx_status,
+ bool *decrypt_error);
+
+void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rxs,
+ bool decrypt_error);
+
+int ath9k_cmn_padpos(__le16 frame_control);
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 2be4c225204..b66f72dbf7b 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -18,40 +18,30 @@
#include "ath9k.h"
-static unsigned int ath9k_debug = DBG_DEFAULT;
-module_param_named(debug, ath9k_debug, uint, 0);
+#define REG_WRITE_D(_ah, _reg, _val) \
+ ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg))
+#define REG_READ_D(_ah, _reg) \
+ ath9k_hw_common(_ah)->ops->read((_ah), (_reg))
static struct dentry *ath9k_debugfs_root;
-void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...)
-{
- if (!sc)
- return;
-
- if (sc->debug.debug_mask & dbg_mask) {
- va_list args;
-
- va_start(args, fmt);
- printk(KERN_DEBUG "ath9k: ");
- vprintk(fmt, args);
- va_end(args);
- }
-}
-
static int ath9k_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
+#ifdef CONFIG_ATH_DEBUG
+
static ssize_t read_file_debug(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
char buf[32];
unsigned int len;
- len = snprintf(buf, sizeof(buf), "0x%08x\n", sc->debug.debug_mask);
+ len = snprintf(buf, sizeof(buf), "0x%08x\n", common->debug_mask);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -59,6 +49,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
unsigned long mask;
char buf[32];
ssize_t len;
@@ -71,7 +62,7 @@ static ssize_t write_file_debug(struct file *file, const char __user *user_buf,
if (strict_strtoul(buf, 0, &mask))
return -EINVAL;
- sc->debug.debug_mask = mask;
+ common->debug_mask = mask;
return count;
}
@@ -82,6 +73,8 @@ static const struct file_operations fops_debug = {
.owner = THIS_MODULE
};
+#endif
+
static ssize_t read_file_dma(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -95,7 +88,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf,
ath9k_ps_wakeup(sc);
- REG_WRITE(ah, AR_MACMISC,
+ REG_WRITE_D(ah, AR_MACMISC,
((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
(AR_MACMISC_MISC_OBS_BUS_1 <<
AR_MACMISC_MISC_OBS_BUS_MSB_S)));
@@ -107,7 +100,7 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf,
if (i % 4 == 0)
len += snprintf(buf + len, sizeof(buf) - len, "\n");
- val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32)));
+ val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32)));
len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ",
i, val[i]);
}
@@ -157,9 +150,9 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf,
(val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n",
- REG_READ(ah, AR_OBS_BUS_1));
+ REG_READ_D(ah, AR_OBS_BUS_1));
len += snprintf(buf + len, sizeof(buf) - len,
- "AR_CR: 0x%x \n", REG_READ(ah, AR_CR));
+ "AR_CR: 0x%x \n", REG_READ_D(ah, AR_CR));
ath9k_ps_restore(sc);
@@ -266,18 +259,11 @@ static const struct file_operations fops_interrupt = {
.owner = THIS_MODULE
};
-void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb)
+void ath_debug_stat_rc(struct ath_softc *sc, int final_rate)
{
- struct ath_tx_info_priv *tx_info_priv = NULL;
- struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ieee80211_tx_rate *rates = tx_info->status.rates;
- int final_ts_idx, idx;
struct ath_rc_stats *stats;
- tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
- final_ts_idx = tx_info_priv->tx.ts_rateindex;
- idx = rates[final_ts_idx].idx;
- stats = &sc->debug.stats.rcstats[idx];
+ stats = &sc->debug.stats.rcstats[final_rate];
stats->success++;
}
@@ -376,12 +362,12 @@ static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
aphy->chan_idx, aphy->chan_is_ht);
}
- put_unaligned_le32(REG_READ(sc->sc_ah, AR_STA_ID0), addr);
- put_unaligned_le16(REG_READ(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
+ put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_STA_ID0), addr);
+ put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
len += snprintf(buf + len, sizeof(buf) - len,
"addr: %pM\n", addr);
- put_unaligned_le32(REG_READ(sc->sc_ah, AR_BSSMSKL), addr);
- put_unaligned_le16(REG_READ(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
+ put_unaligned_le32(REG_READ_D(sc->sc_ah, AR_BSSMSKL), addr);
+ put_unaligned_le16(REG_READ_D(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
len += snprintf(buf + len, sizeof(buf) - len,
"addrmask: %pM\n", addr);
@@ -568,9 +554,10 @@ static const struct file_operations fops_xmit = {
.owner = THIS_MODULE
};
-int ath9k_init_debug(struct ath_softc *sc)
+int ath9k_init_debug(struct ath_hw *ah)
{
- sc->debug.debug_mask = ath9k_debug;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
if (!ath9k_debugfs_root)
return -ENOENT;
@@ -580,10 +567,12 @@ int ath9k_init_debug(struct ath_softc *sc)
if (!sc->debug.debugfs_phy)
goto err;
+#ifdef CONFIG_ATH_DEBUG
sc->debug.debugfs_debug = debugfs_create_file("debug",
S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug);
if (!sc->debug.debugfs_debug)
goto err;
+#endif
sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUSR,
sc->debug.debugfs_phy, sc, &fops_dma);
@@ -619,12 +608,15 @@ int ath9k_init_debug(struct ath_softc *sc)
return 0;
err:
- ath9k_exit_debug(sc);
+ ath9k_exit_debug(ah);
return -ENOMEM;
}
-void ath9k_exit_debug(struct ath_softc *sc)
+void ath9k_exit_debug(struct ath_hw *ah)
{
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+
debugfs_remove(sc->debug.debugfs_xmit);
debugfs_remove(sc->debug.debugfs_wiphy);
debugfs_remove(sc->debug.debugfs_rcstat);
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 7241f474833..536663e3ee1 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -17,36 +17,19 @@
#ifndef DEBUG_H
#define DEBUG_H
-enum ATH_DEBUG {
- ATH_DBG_RESET = 0x00000001,
- ATH_DBG_QUEUE = 0x00000002,
- ATH_DBG_EEPROM = 0x00000004,
- ATH_DBG_CALIBRATE = 0x00000008,
- ATH_DBG_INTERRUPT = 0x00000010,
- ATH_DBG_REGULATORY = 0x00000020,
- ATH_DBG_ANI = 0x00000040,
- ATH_DBG_XMIT = 0x00000080,
- ATH_DBG_BEACON = 0x00000100,
- ATH_DBG_CONFIG = 0x00000200,
- ATH_DBG_FATAL = 0x00000400,
- ATH_DBG_PS = 0x00000800,
- ATH_DBG_HWTIMER = 0x00001000,
- ATH_DBG_BTCOEX = 0x00002000,
- ATH_DBG_ANY = 0xffffffff
-};
-
-#define DBG_DEFAULT (ATH_DBG_FATAL)
+#include "hw.h"
+#include "rc.h"
struct ath_txq;
struct ath_buf;
-#ifdef CONFIG_ATH9K_DEBUG
+#ifdef CONFIG_ATH9K_DEBUGFS
#define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++
#else
#define TX_STAT_INC(q, c) do { } while (0)
#endif
-#ifdef CONFIG_ATH9K_DEBUG
+#ifdef CONFIG_ATH9K_DEBUGFS
/**
* struct ath_interrupt_stats - Contains statistics about interrupts
@@ -140,7 +123,6 @@ struct ath_stats {
};
struct ath9k_debug {
- int debug_mask;
struct dentry *debugfs_phy;
struct dentry *debugfs_debug;
struct dentry *debugfs_dma;
@@ -151,13 +133,13 @@ struct ath9k_debug {
struct ath_stats stats;
};
-void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...);
-int ath9k_init_debug(struct ath_softc *sc);
-void ath9k_exit_debug(struct ath_softc *sc);
+int ath9k_init_debug(struct ath_hw *ah);
+void ath9k_exit_debug(struct ath_hw *ah);
+
int ath9k_debug_create_root(void);
void ath9k_debug_remove_root(void);
void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status);
-void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb);
+void ath_debug_stat_rc(struct ath_softc *sc, int final_rate);
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf);
void ath_debug_stat_retries(struct ath_softc *sc, int rix,
@@ -165,17 +147,12 @@ void ath_debug_stat_retries(struct ath_softc *sc, int rix,
#else
-static inline void DPRINTF(struct ath_softc *sc, int dbg_mask,
- const char *fmt, ...)
-{
-}
-
-static inline int ath9k_init_debug(struct ath_softc *sc)
+static inline int ath9k_init_debug(struct ath_hw *ah)
{
return 0;
}
-static inline void ath9k_exit_debug(struct ath_softc *sc)
+static inline void ath9k_exit_debug(struct ath_hw *ah)
{
}
@@ -194,7 +171,7 @@ static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
}
static inline void ath_debug_stat_rc(struct ath_softc *sc,
- struct sk_buff *skb)
+ int final_rate)
{
}
@@ -209,6 +186,6 @@ static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix,
{
}
-#endif /* CONFIG_ATH9K_DEBUG */
+#endif /* CONFIG_ATH9K_DEBUGFS */
#endif /* DEBUG_H */
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index b6e52d0f8c4..dacaae93414 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
static inline u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz)
{
@@ -83,11 +83,9 @@ bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize,
return false;
}
-bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data)
+bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data)
{
- struct ath_softc *sc = ah->ah_sc;
-
- return sc->bus_ops->eeprom_read(ah, off, data);
+ return common->bus_ops->eeprom_read(common, off, data);
}
void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index 4fe33f7eee9..2f2993b50e2 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -17,6 +17,7 @@
#ifndef EEPROM_H
#define EEPROM_H
+#include "../ath.h"
#include <net/cfg80211.h>
#define AH_USE_EEPROM 0x1
@@ -133,6 +134,7 @@
#define AR5416_EEP_MINOR_VER_17 0x11
#define AR5416_EEP_MINOR_VER_19 0x13
#define AR5416_EEP_MINOR_VER_20 0x14
+#define AR5416_EEP_MINOR_VER_21 0x15
#define AR5416_EEP_MINOR_VER_22 0x16
#define AR5416_NUM_5G_CAL_PIERS 8
@@ -153,7 +155,7 @@
#define AR5416_BCHAN_UNUSED 0xFF
#define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64
#define AR5416_MAX_CHAINS 3
-#define AR5416_PWR_TABLE_OFFSET -5
+#define AR5416_PWR_TABLE_OFFSET_DB -5
/* Rx gain type values */
#define AR5416_EEP_RXGAIN_23DB_BACKOFF 0
@@ -301,7 +303,7 @@ struct base_eep_header {
u8 txGainType;
u8 rcChainMask;
u8 desiredScaleCCK;
- u8 power_table_offset;
+ u8 pwr_table_offset;
u8 frac_n_5g;
u8 futureBase_3[21];
} __packed;
@@ -638,6 +640,7 @@ struct ar9287_eeprom {
} __packed;
enum reg_ext_bitmap {
+ REG_EXT_FCC_MIDBAND = 0,
REG_EXT_JAPAN_MIDBAND = 1,
REG_EXT_FCC_DFS_HT40 = 2,
REG_EXT_JAPAN_NONDFS_HT40 = 3,
@@ -684,7 +687,7 @@ int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight,
int16_t targetRight);
bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize,
u16 *indexL, u16 *indexR);
-bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data);
+bool ath9k_hw_nvram_read(struct ath_common *common, u32 off, u16 *data);
void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
u8 *pVpdList, u16 numIntercepts,
u8 *pRetVpdList);
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
index b8eca7be5f3..68db16690ab 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
static int ath9k_hw_4k_get_eeprom_ver(struct ath_hw *ah)
{
@@ -29,20 +29,21 @@ static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah)
static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
{
#define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
+ struct ath_common *common = ath9k_hw_common(ah);
u16 *eep_data = (u16 *)&ah->eeprom.map4k;
int addr, eep_start_loc = 0;
eep_start_loc = 64;
if (!ath9k_hw_use_flash(ah)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Reading from EEPROM, not flash\n");
+ ath_print(common, ATH_DBG_EEPROM,
+ "Reading from EEPROM, not flash\n");
}
for (addr = 0; addr < SIZE_EEPROM_4K; addr++) {
- if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Unable to read eeprom region \n");
+ if (!ath9k_hw_nvram_read(common, addr + eep_start_loc, eep_data)) {
+ ath_print(common, ATH_DBG_EEPROM,
+ "Unable to read eeprom region \n");
return false;
}
eep_data++;
@@ -55,6 +56,7 @@ static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah)
static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
{
#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
+ struct ath_common *common = ath9k_hw_common(ah);
struct ar5416_eeprom_4k *eep =
(struct ar5416_eeprom_4k *) &ah->eeprom.map4k;
u16 *eepdata, temp, magic, magic2;
@@ -64,15 +66,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
if (!ath9k_hw_use_flash(ah)) {
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
+ if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET,
&magic)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Reading Magic # failed\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Reading Magic # failed\n");
return false;
}
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Read Magic = 0x%04X\n", magic);
+ ath_print(common, ATH_DBG_EEPROM,
+ "Read Magic = 0x%04X\n", magic);
if (magic != AR5416_EEPROM_MAGIC) {
magic2 = swab16(magic);
@@ -87,16 +89,16 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
eepdata++;
}
} else {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Invalid EEPROM Magic. "
- "endianness mismatch.\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Invalid EEPROM Magic. "
+ "endianness mismatch.\n");
return -EINVAL;
}
}
}
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n",
- need_swap ? "True" : "False");
+ ath_print(common, ATH_DBG_EEPROM, "need_swap = %s.\n",
+ need_swap ? "True" : "False");
if (need_swap)
el = swab16(ah->eeprom.map4k.baseEepHeader.length);
@@ -117,8 +119,8 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
u32 integer;
u16 word;
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "EEPROM Endianness is not native.. Changing\n");
+ ath_print(common, ATH_DBG_EEPROM,
+ "EEPROM Endianness is not native.. Changing\n");
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -160,9 +162,9 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ ath_print(common, ATH_DBG_FATAL,
+ "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
+ sum, ah->eep_ops->get_eeprom_ver(ah));
return -EINVAL;
}
@@ -208,6 +210,8 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
return pBase->rxMask;
case EEP_FRAC_N_5G:
return 0;
+ case EEP_PWR_TABLE_OFFSET:
+ return AR5416_PWR_TABLE_OFFSET_DB;
default:
return 0;
}
@@ -385,6 +389,7 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
struct ath9k_channel *chan,
int16_t *pTxPowerIndexOffset)
{
+ struct ath_common *common = ath9k_hw_common(ah);
struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
struct cal_data_per_freq_4k *pRawDataset;
u8 *pCalBChans = NULL;
@@ -470,21 +475,21 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
((pdadcValues[4 * j + 3] & 0xFF) << 24);
REG_WRITE(ah, regOffset, reg32);
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PDADC (%d,%4x): %4.4x %8.8x\n",
- i, regChainOffset, regOffset,
- reg32);
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PDADC: Chain %d | "
- "PDADC %3d Value %3d | "
- "PDADC %3d Value %3d | "
- "PDADC %3d Value %3d | "
- "PDADC %3d Value %3d |\n",
- i, 4 * j, pdadcValues[4 * j],
- 4 * j + 1, pdadcValues[4 * j + 1],
- 4 * j + 2, pdadcValues[4 * j + 2],
- 4 * j + 3,
- pdadcValues[4 * j + 3]);
+ ath_print(common, ATH_DBG_EEPROM,
+ "PDADC (%d,%4x): %4.4x %8.8x\n",
+ i, regChainOffset, regOffset,
+ reg32);
+ ath_print(common, ATH_DBG_EEPROM,
+ "PDADC: Chain %d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d |\n",
+ i, 4 * j, pdadcValues[4 * j],
+ 4 * j + 1, pdadcValues[4 * j + 1],
+ 4 * j + 2, pdadcValues[4 * j + 2],
+ 4 * j + 3,
+ pdadcValues[4 * j + 3]);
regOffset += 4;
}
@@ -750,7 +755,7 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
if (AR_SREV_9280_10_OR_LATER(ah)) {
for (i = 0; i < Ar5416RateSize; i++)
- ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2;
+ ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
}
/* OFDM power per rate */
@@ -1107,6 +1112,10 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
pModal->txEndToRxOn);
+
+ if (AR_SREV_9271_10(ah))
+ REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
+ pModal->txEndToRxOn);
REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62,
pModal->thresh62);
REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62,
@@ -1148,20 +1157,21 @@ static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz)
{
#define EEP_MAP4K_SPURCHAN \
(ah->eeprom.map4k.modalHeader.spurChans[i].spurChan)
+ struct ath_common *common = ath9k_hw_common(ah);
u16 spur_val = AR_NO_SPUR;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur idx %d is2Ghz. %d val %x\n",
- i, is2GHz, ah->config.spurchans[i][is2GHz]);
+ ath_print(common, ATH_DBG_ANI,
+ "Getting spur idx %d is2Ghz. %d val %x\n",
+ i, is2GHz, ah->config.spurchans[i][is2GHz]);
switch (ah->config.spurmode) {
case SPUR_DISABLE:
break;
case SPUR_ENABLE_IOCTL:
spur_val = ah->config.spurchans[i][is2GHz];
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur val from new loc. %d\n", spur_val);
+ ath_print(common, ATH_DBG_ANI,
+ "Getting spur val from new loc. %d\n", spur_val);
break;
case SPUR_ENABLE_EEPROM:
spur_val = EEP_MAP4K_SPURCHAN;
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index c20c21a79b2..839d05a1df2 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
static int ath9k_hw_AR9287_get_eeprom_ver(struct ath_hw *ah)
{
@@ -29,20 +29,22 @@ static int ath9k_hw_AR9287_get_eeprom_rev(struct ath_hw *ah)
static bool ath9k_hw_AR9287_fill_eeprom(struct ath_hw *ah)
{
struct ar9287_eeprom *eep = &ah->eeprom.map9287;
+ struct ath_common *common = ath9k_hw_common(ah);
u16 *eep_data;
int addr, eep_start_loc = AR9287_EEP_START_LOC;
eep_data = (u16 *)eep;
if (!ath9k_hw_use_flash(ah)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Reading from EEPROM, not flash\n");
+ ath_print(common, ATH_DBG_EEPROM,
+ "Reading from EEPROM, not flash\n");
}
for (addr = 0; addr < sizeof(struct ar9287_eeprom) / sizeof(u16);
addr++) {
- if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Unable to read eeprom region \n");
+ if (!ath9k_hw_nvram_read(common,
+ addr + eep_start_loc, eep_data)) {
+ ath_print(common, ATH_DBG_EEPROM,
+ "Unable to read eeprom region \n");
return false;
}
eep_data++;
@@ -57,17 +59,18 @@ static int ath9k_hw_AR9287_check_eeprom(struct ath_hw *ah)
int i, addr;
bool need_swap = false;
struct ar9287_eeprom *eep = &ah->eeprom.map9287;
+ struct ath_common *common = ath9k_hw_common(ah);
if (!ath9k_hw_use_flash(ah)) {
- if (!ath9k_hw_nvram_read
- (ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Reading Magic # failed\n");
+ if (!ath9k_hw_nvram_read(common,
+ AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
+ ath_print(common, ATH_DBG_FATAL,
+ "Reading Magic # failed\n");
return false;
}
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Read Magic = 0x%04X\n", magic);
+ ath_print(common, ATH_DBG_EEPROM,
+ "Read Magic = 0x%04X\n", magic);
if (magic != AR5416_EEPROM_MAGIC) {
magic2 = swab16(magic);
@@ -83,15 +86,15 @@ static int ath9k_hw_AR9287_check_eeprom(struct ath_hw *ah)
eepdata++;
}
} else {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Invalid EEPROM Magic. "
- "endianness mismatch.\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Invalid EEPROM Magic. "
+ "endianness mismatch.\n");
return -EINVAL;
}
}
}
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ?
- "True" : "False");
+ ath_print(common, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ?
+ "True" : "False");
if (need_swap)
el = swab16(ah->eeprom.map9287.baseEepHeader.length);
@@ -148,9 +151,9 @@ static int ath9k_hw_AR9287_check_eeprom(struct ath_hw *ah)
if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER
|| ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ ath_print(common, ATH_DBG_FATAL,
+ "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
+ sum, ah->eep_ops->get_eeprom_ver(ah));
return -EINVAL;
}
@@ -436,6 +439,7 @@ static void ath9k_hw_set_AR9287_power_cal_table(struct ath_hw *ah,
struct ath9k_channel *chan,
int16_t *pTxPowerIndexOffset)
{
+ struct ath_common *common = ath9k_hw_common(ah);
struct cal_data_per_freq_ar9287 *pRawDataset;
struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop;
u8 *pCalBChans = NULL;
@@ -564,24 +568,25 @@ static void ath9k_hw_set_AR9287_power_cal_table(struct ath_hw *ah,
& 0xFF) << 24) ;
REG_WRITE(ah, regOffset, reg32);
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PDADC (%d,%4x): %4.4x %8.8x\n",
- i, regChainOffset, regOffset,
- reg32);
-
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PDADC: Chain %d | "
- "PDADC %3d Value %3d | "
- "PDADC %3d Value %3d | "
- "PDADC %3d Value %3d | "
- "PDADC %3d Value %3d |\n",
- i, 4 * j, pdadcValues[4 * j],
- 4 * j + 1,
- pdadcValues[4 * j + 1],
- 4 * j + 2,
- pdadcValues[4 * j + 2],
- 4 * j + 3,
- pdadcValues[4 * j + 3]);
+ ath_print(common, ATH_DBG_EEPROM,
+ "PDADC (%d,%4x): %4.4x "
+ "%8.8x\n",
+ i, regChainOffset, regOffset,
+ reg32);
+
+ ath_print(common, ATH_DBG_EEPROM,
+ "PDADC: Chain %d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d |\n",
+ i, 4 * j, pdadcValues[4 * j],
+ 4 * j + 1,
+ pdadcValues[4 * j + 1],
+ 4 * j + 2,
+ pdadcValues[4 * j + 2],
+ 4 * j + 3,
+ pdadcValues[4 * j + 3]);
regOffset += 4;
}
@@ -831,6 +836,7 @@ static void ath9k_hw_AR9287_set_txpower(struct ath_hw *ah,
{
#define INCREASE_MAXPOW_BY_TWO_CHAIN 6
#define INCREASE_MAXPOW_BY_THREE_CHAIN 10
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ar9287_eeprom *pEepData = &ah->eeprom.map9287;
struct modal_eep_ar9287_header *pModal = &pEepData->modalHeader;
@@ -966,8 +972,8 @@ static void ath9k_hw_AR9287_set_txpower(struct ath_hw *ah,
INCREASE_MAXPOW_BY_THREE_CHAIN;
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Invalid chainmask configuration\n");
+ ath_print(common, ATH_DBG_EEPROM,
+ "Invalid chainmask configuration\n");
break;
}
}
@@ -1138,19 +1144,20 @@ static u16 ath9k_hw_AR9287_get_spur_channel(struct ath_hw *ah,
{
#define EEP_MAP9287_SPURCHAN \
(ah->eeprom.map9287.modalHeader.spurChans[i].spurChan)
+ struct ath_common *common = ath9k_hw_common(ah);
u16 spur_val = AR_NO_SPUR;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur idx %d is2Ghz. %d val %x\n",
- i, is2GHz, ah->config.spurchans[i][is2GHz]);
+ ath_print(common, ATH_DBG_ANI,
+ "Getting spur idx %d is2Ghz. %d val %x\n",
+ i, is2GHz, ah->config.spurchans[i][is2GHz]);
switch (ah->config.spurmode) {
case SPUR_DISABLE:
break;
case SPUR_ENABLE_IOCTL:
spur_val = ah->config.spurchans[i][is2GHz];
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur val from new loc. %d\n", spur_val);
+ ath_print(common, ATH_DBG_ANI,
+ "Getting spur val from new loc. %d\n", spur_val);
break;
case SPUR_ENABLE_EEPROM:
spur_val = EEP_MAP9287_SPURCHAN;
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 4071fc91da0..404a0341242 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
static void ath9k_get_txgain_index(struct ath_hw *ah,
struct ath9k_channel *chan,
@@ -89,14 +89,15 @@ static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah)
static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah)
{
#define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16))
+ struct ath_common *common = ath9k_hw_common(ah);
u16 *eep_data = (u16 *)&ah->eeprom.def;
int addr, ar5416_eep_start_loc = 0x100;
for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) {
- if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc,
+ if (!ath9k_hw_nvram_read(common, addr + ar5416_eep_start_loc,
eep_data)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Unable to read eeprom region\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "Unable to read eeprom region\n");
return false;
}
eep_data++;
@@ -109,19 +110,20 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
{
struct ar5416_eeprom_def *eep =
(struct ar5416_eeprom_def *) &ah->eeprom.def;
+ struct ath_common *common = ath9k_hw_common(ah);
u16 *eepdata, temp, magic, magic2;
u32 sum = 0, el;
bool need_swap = false;
int i, addr, size;
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n");
+ if (!ath9k_hw_nvram_read(common, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
+ ath_print(common, ATH_DBG_FATAL, "Reading Magic # failed\n");
return false;
}
if (!ath9k_hw_use_flash(ah)) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Read Magic = 0x%04X\n", magic);
+ ath_print(common, ATH_DBG_EEPROM,
+ "Read Magic = 0x%04X\n", magic);
if (magic != AR5416_EEPROM_MAGIC) {
magic2 = swab16(magic);
@@ -137,16 +139,16 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
eepdata++;
}
} else {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Invalid EEPROM Magic. "
- "Endianness mismatch.\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Invalid EEPROM Magic. "
+ "Endianness mismatch.\n");
return -EINVAL;
}
}
}
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n",
- need_swap ? "True" : "False");
+ ath_print(common, ATH_DBG_EEPROM, "need_swap = %s.\n",
+ need_swap ? "True" : "False");
if (need_swap)
el = swab16(ah->eeprom.def.baseEepHeader.length);
@@ -167,8 +169,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
u32 integer, j;
u16 word;
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "EEPROM Endianness is not native.. Changing.\n");
+ ath_print(common, ATH_DBG_EEPROM,
+ "EEPROM Endianness is not native.. Changing.\n");
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -214,8 +216,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
+ ath_print(common, ATH_DBG_FATAL,
+ "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
sum, ah->eep_ops->get_eeprom_ver(ah));
return -EINVAL;
}
@@ -289,6 +291,11 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
return pBase->frac_n_5g;
else
return 0;
+ case EEP_PWR_TABLE_OFFSET:
+ if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_21)
+ return pBase->pwr_table_offset;
+ else
+ return AR5416_PWR_TABLE_OFFSET_DB;
default:
return 0;
}
@@ -739,6 +746,76 @@ static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *ah,
return;
}
+static int16_t ath9k_change_gain_boundary_setting(struct ath_hw *ah,
+ u16 *gb,
+ u16 numXpdGain,
+ u16 pdGainOverlap_t2,
+ int8_t pwr_table_offset,
+ int16_t *diff)
+
+{
+ u16 k;
+
+ /* Prior to writing the boundaries or the pdadc vs. power table
+ * into the chip registers the default starting point on the pdadc
+ * vs. power table needs to be checked and the curve boundaries
+ * adjusted accordingly
+ */
+ if (AR_SREV_9280_20_OR_LATER(ah)) {
+ u16 gb_limit;
+
+ if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
+ /* get the difference in dB */
+ *diff = (u16)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
+ /* get the number of half dB steps */
+ *diff *= 2;
+ /* change the original gain boundary settings
+ * by the number of half dB steps
+ */
+ for (k = 0; k < numXpdGain; k++)
+ gb[k] = (u16)(gb[k] - *diff);
+ }
+ /* Because of a hardware limitation, ensure the gain boundary
+ * is not larger than (63 - overlap)
+ */
+ gb_limit = (u16)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
+
+ for (k = 0; k < numXpdGain; k++)
+ gb[k] = (u16)min(gb_limit, gb[k]);
+ }
+
+ return *diff;
+}
+
+static void ath9k_adjust_pdadc_values(struct ath_hw *ah,
+ int8_t pwr_table_offset,
+ int16_t diff,
+ u8 *pdadcValues)
+{
+#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
+ u16 k;
+
+ /* If this is a board that has a pwrTableOffset that differs from
+ * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
+ * pdadc vs pwr table needs to be adjusted prior to writing to the
+ * chip.
+ */
+ if (AR_SREV_9280_20_OR_LATER(ah)) {
+ if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
+ /* shift the table to start at the new offset */
+ for (k = 0; k < (u16)NUM_PDADC(diff); k++ ) {
+ pdadcValues[k] = pdadcValues[k + diff];
+ }
+
+ /* fill the back of the table */
+ for (k = (u16)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
+ pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
+ }
+ }
+ }
+#undef NUM_PDADC
+}
+
static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
struct ath9k_channel *chan,
int16_t *pTxPowerIndexOffset)
@@ -746,7 +823,7 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
#define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x)
#define SM_PDGAIN_B(x, y) \
SM((gainBoundaries[x]), AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##y)
-
+ struct ath_common *common = ath9k_hw_common(ah);
struct ar5416_eeprom_def *pEepData = &ah->eeprom.def;
struct cal_data_per_freq *pRawDataset;
u8 *pCalBChans = NULL;
@@ -754,15 +831,18 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
static u8 pdadcValues[AR5416_NUM_PDADC_VALUES];
u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK];
u16 numPiers, i, j;
- int16_t tMinCalPower;
+ int16_t tMinCalPower, diff = 0;
u16 numXpdGain, xpdMask;
u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 };
u32 reg32, regOffset, regChainOffset;
int16_t modalIdx;
+ int8_t pwr_table_offset;
modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0;
xpdMask = pEepData->modalHeader[modalIdx].xpdGain;
+ pwr_table_offset = ah->eep_ops->get_eeprom(ah, EEP_PWR_TABLE_OFFSET);
+
if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
AR5416_EEP_MINOR_VER_2) {
pdGainOverlap_t2 =
@@ -842,6 +922,13 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
numXpdGain);
}
+ diff = ath9k_change_gain_boundary_setting(ah,
+ gainBoundaries,
+ numXpdGain,
+ pdGainOverlap_t2,
+ pwr_table_offset,
+ &diff);
+
if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) {
if (OLC_FOR_AR9280_20_LATER) {
REG_WRITE(ah,
@@ -862,6 +949,10 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
}
}
+
+ ath9k_adjust_pdadc_values(ah, pwr_table_offset,
+ diff, pdadcValues);
+
regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset;
for (j = 0; j < 32; j++) {
reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) |
@@ -870,20 +961,20 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
((pdadcValues[4 * j + 3] & 0xFF) << 24);
REG_WRITE(ah, regOffset, reg32);
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PDADC (%d,%4x): %4.4x %8.8x\n",
- i, regChainOffset, regOffset,
- reg32);
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PDADC: Chain %d | PDADC %3d "
- "Value %3d | PDADC %3d Value %3d | "
- "PDADC %3d Value %3d | PDADC %3d "
- "Value %3d |\n",
- i, 4 * j, pdadcValues[4 * j],
- 4 * j + 1, pdadcValues[4 * j + 1],
- 4 * j + 2, pdadcValues[4 * j + 2],
- 4 * j + 3,
- pdadcValues[4 * j + 3]);
+ ath_print(common, ATH_DBG_EEPROM,
+ "PDADC (%d,%4x): %4.4x %8.8x\n",
+ i, regChainOffset, regOffset,
+ reg32);
+ ath_print(common, ATH_DBG_EEPROM,
+ "PDADC: Chain %d | PDADC %3d "
+ "Value %3d | PDADC %3d Value %3d | "
+ "PDADC %3d Value %3d | PDADC %3d "
+ "Value %3d |\n",
+ i, 4 * j, pdadcValues[4 * j],
+ 4 * j + 1, pdadcValues[4 * j + 1],
+ 4 * j + 2, pdadcValues[4 * j + 2],
+ 4 * j + 3,
+ pdadcValues[4 * j + 3]);
regOffset += 4;
}
@@ -1197,8 +1288,13 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
}
if (AR_SREV_9280_10_OR_LATER(ah)) {
- for (i = 0; i < Ar5416RateSize; i++)
- ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2;
+ for (i = 0; i < Ar5416RateSize; i++) {
+ int8_t pwr_table_offset;
+
+ pwr_table_offset = ah->eep_ops->get_eeprom(ah,
+ EEP_PWR_TABLE_OFFSET);
+ ratesArray[i] -= pwr_table_offset * 2;
+ }
}
REG_WRITE(ah, AR_PHY_POWER_TX_RATE1,
@@ -1297,7 +1393,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
if (AR_SREV_9280_10_OR_LATER(ah))
regulatory->max_power_level =
- ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2;
+ ratesArray[i] + AR5416_PWR_TABLE_OFFSET_DB * 2;
else
regulatory->max_power_level = ratesArray[i];
@@ -1311,8 +1407,8 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
regulatory->max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN;
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "Invalid chainmask configuration\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_EEPROM,
+ "Invalid chainmask configuration\n");
break;
}
}
@@ -1349,20 +1445,21 @@ static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz)
{
#define EEP_DEF_SPURCHAN \
(ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan)
+ struct ath_common *common = ath9k_hw_common(ah);
u16 spur_val = AR_NO_SPUR;
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur idx %d is2Ghz. %d val %x\n",
- i, is2GHz, ah->config.spurchans[i][is2GHz]);
+ ath_print(common, ATH_DBG_ANI,
+ "Getting spur idx %d is2Ghz. %d val %x\n",
+ i, is2GHz, ah->config.spurchans[i][is2GHz]);
switch (ah->config.spurmode) {
case SPUR_DISABLE:
break;
case SPUR_ENABLE_IOCTL:
spur_val = ah->config.spurchans[i][is2GHz];
- DPRINTF(ah->ah_sc, ATH_DBG_ANI,
- "Getting spur val from new loc. %d\n", spur_val);
+ ath_print(common, ATH_DBG_ANI,
+ "Getting spur val from new loc. %d\n", spur_val);
break;
case SPUR_ENABLE_EEPROM:
spur_val = EEP_DEF_SPURCHAN;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index ca7694caf36..2ec61f08cfd 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -16,9 +16,9 @@
#include <linux/io.h>
#include <asm/unaligned.h>
-#include <linux/pci.h>
-#include "ath9k.h"
+#include "hw.h"
+#include "rc.h"
#include "initvals.h"
#define ATH9K_CLOCK_RATE_CCK 22
@@ -26,13 +26,27 @@
#define ATH9K_CLOCK_RATE_2GHZ_OFDM 44
static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type);
-static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan,
- enum ath9k_ht_macmode macmode);
+static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan);
static u32 ath9k_hw_ini_fixup(struct ath_hw *ah,
struct ar5416_eeprom_def *pEepData,
u32 reg, u32 value);
-static void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan);
-static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan);
+
+MODULE_AUTHOR("Atheros Communications");
+MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
+MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+static int __init ath9k_init(void)
+{
+ return 0;
+}
+module_init(ath9k_init);
+
+static void __exit ath9k_exit(void)
+{
+ return;
+}
+module_exit(ath9k_exit);
/********************/
/* Helper Functions */
@@ -40,7 +54,7 @@ static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan
static u32 ath9k_hw_mac_usec(struct ath_hw *ah, u32 clks)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
if (!ah->curchan) /* should really check for CCK instead */
return clks / ATH9K_CLOCK_RATE_CCK;
@@ -52,7 +66,7 @@ static u32 ath9k_hw_mac_usec(struct ath_hw *ah, u32 clks)
static u32 ath9k_hw_mac_to_usec(struct ath_hw *ah, u32 clks)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
if (conf_is_ht40(conf))
return ath9k_hw_mac_usec(ah, clks) / 2;
@@ -62,7 +76,7 @@ static u32 ath9k_hw_mac_to_usec(struct ath_hw *ah, u32 clks)
static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
if (!ah->curchan) /* should really check for CCK instead */
return usecs *ATH9K_CLOCK_RATE_CCK;
@@ -73,7 +87,7 @@ static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs)
static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs)
{
- struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
if (conf_is_ht40(conf))
return ath9k_hw_mac_clks(ah, usecs) * 2;
@@ -81,38 +95,6 @@ static u32 ath9k_hw_mac_to_clks(struct ath_hw *ah, u32 usecs)
return ath9k_hw_mac_clks(ah, usecs);
}
-/*
- * Read and write, they both share the same lock. We do this to serialize
- * reads and writes on Atheros 802.11n PCI devices only. This is required
- * as the FIFO on these devices can only accept sanely 2 requests. After
- * that the device goes bananas. Serializing the reads/writes prevents this
- * from happening.
- */
-
-void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val)
-{
- if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
- unsigned long flags;
- spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags);
- iowrite32(val, ah->ah_sc->mem + reg_offset);
- spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags);
- } else
- iowrite32(val, ah->ah_sc->mem + reg_offset);
-}
-
-unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset)
-{
- u32 val;
- if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
- unsigned long flags;
- spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags);
- val = ioread32(ah->ah_sc->mem + reg_offset);
- spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags);
- } else
- val = ioread32(ah->ah_sc->mem + reg_offset);
- return val;
-}
-
bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout)
{
int i;
@@ -126,12 +108,13 @@ bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout)
udelay(AH_TIME_QUANTUM);
}
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
- timeout, reg, REG_READ(ah, reg), mask, val);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_ANY,
+ "timeout (%d us) on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
+ timeout, reg, REG_READ(ah, reg), mask, val);
return false;
}
+EXPORT_SYMBOL(ath9k_hw_wait);
u32 ath9k_hw_reverse_bits(u32 val, u32 n)
{
@@ -165,22 +148,19 @@ bool ath9k_get_channel_edges(struct ath_hw *ah,
}
u16 ath9k_hw_computetxtime(struct ath_hw *ah,
- const struct ath_rate_table *rates,
+ u8 phy, int kbps,
u32 frameLen, u16 rateix,
bool shortPreamble)
{
u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
- u32 kbps;
-
- kbps = rates->info[rateix].ratekbps;
if (kbps == 0)
return 0;
- switch (rates->info[rateix].phy) {
+ switch (phy) {
case WLAN_RC_PHY_CCK:
phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
- if (shortPreamble && rates->info[rateix].short_preamble)
+ if (shortPreamble)
phyTime >>= 1;
numBits = frameLen << 3;
txTime = CCK_SIFS_TIME + phyTime + ((numBits * 1000) / kbps);
@@ -210,15 +190,15 @@ u16 ath9k_hw_computetxtime(struct ath_hw *ah,
}
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Unknown phy %u (rate ix %u)\n",
- rates->info[rateix].phy, rateix);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "Unknown phy %u (rate ix %u)\n", phy, rateix);
txTime = 0;
break;
}
return txTime;
}
+EXPORT_SYMBOL(ath9k_hw_computetxtime);
void ath9k_hw_get_channel_centers(struct ath_hw *ah,
struct ath9k_channel *chan,
@@ -245,10 +225,9 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
centers->ctl_center =
centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT);
+ /* 25 MHz spacing is supported by hw but not on upper layers */
centers->ext_center =
- centers->synth_center + (extoff *
- ((ah->extprotspacing == ATH9K_HT_EXTPROTSPACING_20) ?
- HT40_CHANNEL_CENTER_SHIFT : 15));
+ centers->synth_center + (extoff * HT40_CHANNEL_CENTER_SHIFT);
}
/******************/
@@ -317,6 +296,7 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah)
static bool ath9k_hw_chip_test(struct ath_hw *ah)
{
+ struct ath_common *common = ath9k_hw_common(ah);
u32 regAddr[2] = { AR_STA_ID0, AR_PHY_BASE + (8 << 2) };
u32 regHold[2];
u32 patternData[4] = { 0x55555555,
@@ -335,10 +315,11 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah)
REG_WRITE(ah, addr, wrData);
rdData = REG_READ(ah, addr);
if (rdData != wrData) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "address test failed "
- "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
- addr, wrData, rdData);
+ ath_print(common, ATH_DBG_FATAL,
+ "address test failed "
+ "addr: 0x%08x - wr:0x%08x != "
+ "rd:0x%08x\n",
+ addr, wrData, rdData);
return false;
}
}
@@ -347,10 +328,11 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah)
REG_WRITE(ah, addr, wrData);
rdData = REG_READ(ah, addr);
if (wrData != rdData) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "address test failed "
- "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
- addr, wrData, rdData);
+ ath_print(common, ATH_DBG_FATAL,
+ "address test failed "
+ "addr: 0x%08x - wr:0x%08x != "
+ "rd:0x%08x\n",
+ addr, wrData, rdData);
return false;
}
}
@@ -404,8 +386,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
ah->config.cck_trig_high = 200;
ah->config.cck_trig_low = 100;
ah->config.enable_ani = 1;
- ah->config.diversity_control = ATH9K_ANT_VARIABLE;
- ah->config.antenna_switch_swap = 0;
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
ah->config.spurchans[i][0] = AR_NO_SPUR;
@@ -433,6 +413,7 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
if (num_possible_cpus() > 1)
ah->config.serialize_regmode = SER_REG_MODE_AUTO;
}
+EXPORT_SYMBOL(ath9k_hw_init);
static void ath9k_hw_init_defaults(struct ath_hw *ah)
{
@@ -459,27 +440,9 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah)
ah->acktimeout = (u32) -1;
ah->ctstimeout = (u32) -1;
ah->globaltxtimeout = (u32) -1;
-
- ah->gbeacon_rate = 0;
-
ah->power_mode = ATH9K_PM_UNDEFINED;
}
-static int ath9k_hw_rfattach(struct ath_hw *ah)
-{
- bool rfStatus = false;
- int ecode = 0;
-
- rfStatus = ath9k_hw_init_rf(ah, &ecode);
- if (!rfStatus) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "RF setup failed, status: %u\n", ecode);
- return ecode;
- }
-
- return 0;
-}
-
static int ath9k_hw_rf_claim(struct ath_hw *ah)
{
u32 val;
@@ -497,9 +460,9 @@ static int ath9k_hw_rf_claim(struct ath_hw *ah)
case AR_RAD2122_SREV_MAJOR:
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Radio Chip Rev 0x%02X not supported\n",
- val & AR_RADIO_SREV_MAJOR);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "Radio Chip Rev 0x%02X not supported\n",
+ val & AR_RADIO_SREV_MAJOR);
return -EOPNOTSUPP;
}
@@ -510,6 +473,7 @@ static int ath9k_hw_rf_claim(struct ath_hw *ah)
static int ath9k_hw_init_macaddr(struct ath_hw *ah)
{
+ struct ath_common *common = ath9k_hw_common(ah);
u32 sum;
int i;
u16 eeval;
@@ -518,8 +482,8 @@ static int ath9k_hw_init_macaddr(struct ath_hw *ah)
for (i = 0; i < 3; i++) {
eeval = ah->eep_ops->get_eeprom(ah, AR_EEPROM_MAC(i));
sum += eeval;
- ah->macaddr[2 * i] = eeval >> 8;
- ah->macaddr[2 * i + 1] = eeval & 0xff;
+ common->macaddr[2 * i] = eeval >> 8;
+ common->macaddr[2 * i + 1] = eeval & 0xff;
}
if (sum == 0 || sum == 0xffff * 3)
return -EADDRNOTAVAIL;
@@ -590,12 +554,20 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
if (ecode != 0)
return ecode;
- DPRINTF(ah->ah_sc, ATH_DBG_CONFIG, "Eeprom VER: %d, REV: %d\n",
- ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah));
-
- ecode = ath9k_hw_rfattach(ah);
- if (ecode != 0)
- return ecode;
+ ath_print(ath9k_hw_common(ah), ATH_DBG_CONFIG,
+ "Eeprom VER: %d, REV: %d\n",
+ ah->eep_ops->get_eeprom_ver(ah),
+ ah->eep_ops->get_eeprom_rev(ah));
+
+ if (!AR_SREV_9280_10_OR_LATER(ah)) {
+ ecode = ath9k_hw_rf_alloc_ext_banks(ah);
+ if (ecode) {
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "Failed allocating banks for "
+ "external radio\n");
+ return ecode;
+ }
+ }
if (!AR_SREV_9100(ah)) {
ath9k_hw_ani_setup(ah);
@@ -617,6 +589,7 @@ static bool ath9k_hw_devid_supported(u16 devid)
case AR9285_DEVID_PCIE:
case AR5416_DEVID_AR9287_PCI:
case AR5416_DEVID_AR9287_PCIE:
+ case AR9271_USB:
return true;
default:
break;
@@ -634,9 +607,8 @@ static bool ath9k_hw_macversion_supported(u32 macversion)
case AR_SREV_VERSION_9280:
case AR_SREV_VERSION_9285:
case AR_SREV_VERSION_9287:
- return true;
- /* Not yet */
case AR_SREV_VERSION_9271:
+ return true;
default:
break;
}
@@ -670,10 +642,13 @@ static void ath9k_hw_init_cal_settings(struct ath_hw *ah)
static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
{
if (AR_SREV_9271(ah)) {
- INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271_1_0,
- ARRAY_SIZE(ar9271Modes_9271_1_0), 6);
- INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271_1_0,
- ARRAY_SIZE(ar9271Common_9271_1_0), 2);
+ INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271,
+ ARRAY_SIZE(ar9271Modes_9271), 6);
+ INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271,
+ ARRAY_SIZE(ar9271Common_9271), 2);
+ INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only,
+ ar9271Modes_9271_1_0_only,
+ ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6);
return;
}
@@ -905,21 +880,27 @@ static void ath9k_hw_init_11a_eeprom_fix(struct ath_hw *ah)
int ath9k_hw_init(struct ath_hw *ah)
{
+ struct ath_common *common = ath9k_hw_common(ah);
int r = 0;
- if (!ath9k_hw_devid_supported(ah->hw_version.devid))
+ if (!ath9k_hw_devid_supported(ah->hw_version.devid)) {
+ ath_print(common, ATH_DBG_FATAL,
+ "Unsupported device ID: 0x%0x\n",
+ ah->hw_version.devid);
return -EOPNOTSUPP;
+ }
ath9k_hw_init_defaults(ah);
ath9k_hw_init_config(ah);
if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Couldn't reset chip\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Couldn't reset chip\n");
return -EIO;
}
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Couldn't wakeup chip\n");
+ ath_print(common, ATH_DBG_FATAL, "Couldn't wakeup chip\n");
return -EIO;
}
@@ -934,14 +915,19 @@ int ath9k_hw_init(struct ath_hw *ah)
}
}
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "serialize_regmode is %d\n",
+ ath_print(common, ATH_DBG_RESET, "serialize_regmode is %d\n",
ah->config.serialize_regmode);
+ if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+ ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1;
+ else
+ ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD;
+
if (!ath9k_hw_macversion_supported(ah->hw_version.macVersion)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Mac Chip Rev 0x%02x.%x is not supported by "
- "this driver\n", ah->hw_version.macVersion,
- ah->hw_version.macRev);
+ ath_print(common, ATH_DBG_FATAL,
+ "Mac Chip Rev 0x%02x.%x is not supported by "
+ "this driver\n", ah->hw_version.macVersion,
+ ah->hw_version.macRev);
return -EOPNOTSUPP;
}
@@ -959,8 +945,14 @@ int ath9k_hw_init(struct ath_hw *ah)
ath9k_hw_init_cal_settings(ah);
ah->ani_function = ATH9K_ANI_ALL;
- if (AR_SREV_9280_10_OR_LATER(ah))
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL;
+ ah->ath9k_hw_rf_set_freq = &ath9k_hw_ar9280_set_channel;
+ ah->ath9k_hw_spur_mitigate_freq = &ath9k_hw_9280_spur_mitigate;
+ } else {
+ ah->ath9k_hw_rf_set_freq = &ath9k_hw_set_channel;
+ ah->ath9k_hw_spur_mitigate_freq = &ath9k_hw_spur_mitigate;
+ }
ath9k_hw_init_mode_regs(ah);
@@ -969,18 +961,31 @@ int ath9k_hw_init(struct ath_hw *ah)
else
ath9k_hw_disablepcie(ah);
+ /* Support for Japan ch.14 (2484) spread */
+ if (AR_SREV_9287_11_OR_LATER(ah)) {
+ INIT_INI_ARRAY(&ah->iniCckfirNormal,
+ ar9287Common_normal_cck_fir_coeff_92871_1,
+ ARRAY_SIZE(ar9287Common_normal_cck_fir_coeff_92871_1), 2);
+ INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
+ ar9287Common_japan_2484_cck_fir_coeff_92871_1,
+ ARRAY_SIZE(ar9287Common_japan_2484_cck_fir_coeff_92871_1), 2);
+ }
+
r = ath9k_hw_post_init(ah);
if (r)
return r;
ath9k_hw_init_mode_gain_regs(ah);
- ath9k_hw_fill_cap_info(ah);
+ r = ath9k_hw_fill_cap_info(ah);
+ if (r)
+ return r;
+
ath9k_hw_init_11a_eeprom_fix(ah);
r = ath9k_hw_init_macaddr(ah);
if (r) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Failed to initialize MAC address\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Failed to initialize MAC address\n");
return r;
}
@@ -991,6 +996,8 @@ int ath9k_hw_init(struct ath_hw *ah)
ath9k_init_nfcal_hist_buffer(ah);
+ common->state = ATH_HW_INITIALIZED;
+
return 0;
}
@@ -1027,6 +1034,22 @@ static void ath9k_hw_init_qos(struct ath_hw *ah)
REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
}
+static void ath9k_hw_change_target_baud(struct ath_hw *ah, u32 freq, u32 baud)
+{
+ u32 lcr;
+ u32 baud_divider = freq * 1000 * 1000 / 16 / baud;
+
+ lcr = REG_READ(ah , 0x5100c);
+ lcr |= 0x80;
+
+ REG_WRITE(ah, 0x5100c, lcr);
+ REG_WRITE(ah, 0x51004, (baud_divider >> 8));
+ REG_WRITE(ah, 0x51000, (baud_divider & 0xff));
+
+ lcr &= ~0x80;
+ REG_WRITE(ah, 0x5100c, lcr);
+}
+
static void ath9k_hw_init_pll(struct ath_hw *ah,
struct ath9k_channel *chan)
{
@@ -1090,6 +1113,26 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
}
REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll);
+ /* Switch the core clock for ar9271 to 117Mhz */
+ if (AR_SREV_9271(ah)) {
+ if ((pll == 0x142c) || (pll == 0x2850) ) {
+ udelay(500);
+ /* set CLKOBS to output AHB clock */
+ REG_WRITE(ah, 0x7020, 0xe);
+ /*
+ * 0x304: 117Mhz, ahb_ratio: 1x1
+ * 0x306: 40Mhz, ahb_ratio: 1x1
+ */
+ REG_WRITE(ah, 0x50040, 0x304);
+ /*
+ * makes adjustments for the baud dividor to keep the
+ * targetted baud rate based on the used core clock.
+ */
+ ath9k_hw_change_target_baud(ah, AR9271_CORE_CLOCK,
+ AR9271_TARGET_BAUD_RATE);
+ }
+ }
+
udelay(RTC_PLL_SETTLE_DELAY);
REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
@@ -1107,7 +1150,7 @@ static void ath9k_hw_init_chain_masks(struct ath_hw *ah)
REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
AR_PHY_SWAP_ALT_CHAIN);
case 0x3:
- if (((ah)->hw_version.macVersion <= AR_SREV_VERSION_9160)) {
+ if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) {
REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7);
REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7);
break;
@@ -1164,7 +1207,8 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
static bool ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us)
{
if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad ack timeout %u\n", us);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_RESET,
+ "bad ack timeout %u\n", us);
ah->acktimeout = (u32) -1;
return false;
} else {
@@ -1178,7 +1222,8 @@ static bool ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us)
static bool ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us)
{
if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad cts timeout %u\n", us);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_RESET,
+ "bad cts timeout %u\n", us);
ah->ctstimeout = (u32) -1;
return false;
} else {
@@ -1192,8 +1237,8 @@ static bool ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us)
static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu)
{
if (tu > 0xFFFF) {
- DPRINTF(ah->ah_sc, ATH_DBG_XMIT,
- "bad global tx timeout %u\n", tu);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_XMIT,
+ "bad global tx timeout %u\n", tu);
ah->globaltxtimeout = (u32) -1;
return false;
} else {
@@ -1205,8 +1250,8 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu)
static void ath9k_hw_init_user_settings(struct ath_hw *ah)
{
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "ah->misc_mode 0x%x\n",
- ah->misc_mode);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, "ah->misc_mode 0x%x\n",
+ ah->misc_mode);
if (ah->misc_mode != 0)
REG_WRITE(ah, AR_PCU_MISC,
@@ -1229,14 +1274,23 @@ const char *ath9k_hw_probe(u16 vendorid, u16 devid)
void ath9k_hw_detach(struct ath_hw *ah)
{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (common->state <= ATH_HW_INITIALIZED)
+ goto free_hw;
+
if (!AR_SREV_9100(ah))
ath9k_hw_ani_disable(ah);
- ath9k_hw_rf_free(ah);
ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
+
+free_hw:
+ if (!AR_SREV_9280_10_OR_LATER(ah))
+ ath9k_hw_rf_free_ext_banks(ah);
kfree(ah);
ah = NULL;
}
+EXPORT_SYMBOL(ath9k_hw_detach);
/*******/
/* INI */
@@ -1254,7 +1308,8 @@ static void ath9k_hw_override_ini(struct ath_hw *ah,
* AR9271 1.1
*/
if (AR_SREV_9271_10(ah)) {
- val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) | AR_PHY_SPECTRAL_SCAN_ENABLE;
+ val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) |
+ AR_PHY_SPECTRAL_SCAN_ENABLE;
REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val);
}
else if (AR_SREV_9271_11(ah))
@@ -1298,28 +1353,29 @@ static u32 ath9k_hw_def_ini_fixup(struct ath_hw *ah,
u32 reg, u32 value)
{
struct base_eep_header *pBase = &(pEepData->baseEepHeader);
+ struct ath_common *common = ath9k_hw_common(ah);
switch (ah->hw_version.devid) {
case AR9280_DEVID_PCI:
if (reg == 0x7894) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+ ath_print(common, ATH_DBG_EEPROM,
"ini VAL: %x EEPROM: %x\n", value,
(pBase->version & 0xff));
if ((pBase->version & 0xff) > 0x0a) {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PWDCLKIND: %d\n",
- pBase->pwdclkind);
+ ath_print(common, ATH_DBG_EEPROM,
+ "PWDCLKIND: %d\n",
+ pBase->pwdclkind);
value &= ~AR_AN_TOP2_PWDCLKIND;
value |= AR_AN_TOP2_PWDCLKIND &
(pBase->pwdclkind << AR_AN_TOP2_PWDCLKIND_S);
} else {
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "PWDCLKIND Earlier Rev\n");
+ ath_print(common, ATH_DBG_EEPROM,
+ "PWDCLKIND Earlier Rev\n");
}
- DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
- "final ini VAL: %x\n", value);
+ ath_print(common, ATH_DBG_EEPROM,
+ "final ini VAL: %x\n", value);
}
break;
}
@@ -1374,8 +1430,7 @@ static u32 ath9k_regd_get_ctl(struct ath_regulatory *reg,
}
static int ath9k_hw_process_ini(struct ath_hw *ah,
- struct ath9k_channel *chan,
- enum ath9k_ht_macmode macmode)
+ struct ath9k_channel *chan)
{
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
int i, regWrites = 0;
@@ -1469,7 +1524,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
DO_DELAY(regWrites);
}
- ath9k_hw_write_regs(ah, modesIndex, freqIndex, regWrites);
+ ath9k_hw_write_regs(ah, freqIndex, regWrites);
+
+ if (AR_SREV_9271_10(ah))
+ REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only,
+ modesIndex, regWrites);
if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex,
@@ -1477,7 +1536,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
}
ath9k_hw_override_ini(ah, chan);
- ath9k_hw_set_regs(ah, chan, macmode);
+ ath9k_hw_set_regs(ah, chan);
ath9k_hw_init_chain_masks(ah);
if (OLC_FOR_AR9280_20_LATER)
@@ -1491,8 +1550,8 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
(u32) regulatory->power_limit));
if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "ar5416SetRfRegs failed\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "ar5416SetRfRegs failed\n");
return -EIO;
}
@@ -1697,16 +1756,14 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
REG_WRITE(ah, AR_RTC_RC, 0);
if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "RTC stuck in MAC reset\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_RESET,
+ "RTC stuck in MAC reset\n");
return false;
}
if (!AR_SREV_9100(ah))
REG_WRITE(ah, AR_RC, 0);
- ath9k_hw_init_pll(ah, NULL);
-
if (AR_SREV_9100(ah))
udelay(50);
@@ -1734,7 +1791,8 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah)
AR_RTC_STATUS_M,
AR_RTC_STATUS_ON,
AH_WAIT_TIMEOUT)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "RTC not waking up\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_RESET,
+ "RTC not waking up\n");
return false;
}
@@ -1759,8 +1817,7 @@ static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type)
}
}
-static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan,
- enum ath9k_ht_macmode macmode)
+static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan)
{
u32 phymode;
u32 enableDacFifo = 0;
@@ -1779,12 +1836,10 @@ static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan,
(chan->chanmode == CHANNEL_G_HT40PLUS))
phymode |= AR_PHY_FC_DYN2040_PRI_CH;
- if (ah->extprotspacing == ATH9K_HT_EXTPROTSPACING_25)
- phymode |= AR_PHY_FC_DYN2040_EXT_CH;
}
REG_WRITE(ah, AR_PHY_TURBO, phymode);
- ath9k_hw_set11nmac2040(ah, macmode);
+ ath9k_hw_set11nmac2040(ah);
REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S);
@@ -1810,17 +1865,19 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
}
static bool ath9k_hw_channel_change(struct ath_hw *ah,
- struct ath9k_channel *chan,
- enum ath9k_ht_macmode macmode)
+ struct ath9k_channel *chan)
{
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
+ struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_channel *channel = chan->chan;
u32 synthDelay, qnum;
+ int r;
for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
if (ath9k_hw_numtxpending(ah, qnum)) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "Transmit frames pending on queue %d\n", qnum);
+ ath_print(common, ATH_DBG_QUEUE,
+ "Transmit frames pending on "
+ "queue %d\n", qnum);
return false;
}
}
@@ -1828,21 +1885,18 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN);
if (!ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN,
AR_PHY_RFBUS_GRANT_EN, AH_WAIT_TIMEOUT)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Could not kill baseband RX\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Could not kill baseband RX\n");
return false;
}
- ath9k_hw_set_regs(ah, chan, macmode);
+ ath9k_hw_set_regs(ah, chan);
- if (AR_SREV_9280_10_OR_LATER(ah)) {
- ath9k_hw_ar9280_set_channel(ah, chan);
- } else {
- if (!(ath9k_hw_set_channel(ah, chan))) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Failed to set channel\n");
- return false;
- }
+ r = ah->ath9k_hw_rf_set_freq(ah, chan);
+ if (r) {
+ ath_print(common, ATH_DBG_FATAL,
+ "Failed to set channel\n");
+ return false;
}
ah->eep_ops->set_txpower(ah, chan,
@@ -1865,10 +1919,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
ath9k_hw_set_delta_slope(ah, chan);
- if (AR_SREV_9280_10_OR_LATER(ah))
- ath9k_hw_9280_spur_mitigate(ah, chan);
- else
- ath9k_hw_spur_mitigate(ah, chan);
+ ah->ath9k_hw_spur_mitigate_freq(ah, chan);
if (!chan->oneTimeCalsDone)
chan->oneTimeCalsDone = true;
@@ -1876,457 +1927,6 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
return true;
}
-static void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan)
-{
- int bb_spur = AR_NO_SPUR;
- int freq;
- int bin, cur_bin;
- int bb_spur_off, spur_subchannel_sd;
- int spur_freq_sd;
- int spur_delta_phase;
- int denominator;
- int upper, lower, cur_vit_mask;
- int tmp, newVal;
- int i;
- int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8,
- AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60
- };
- int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10,
- AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60
- };
- int inc[4] = { 0, 100, 0, 0 };
- struct chan_centers centers;
-
- int8_t mask_m[123];
- int8_t mask_p[123];
- int8_t mask_amt;
- int tmp_mask;
- int cur_bb_spur;
- bool is2GHz = IS_CHAN_2GHZ(chan);
-
- memset(&mask_m, 0, sizeof(int8_t) * 123);
- memset(&mask_p, 0, sizeof(int8_t) * 123);
-
- ath9k_hw_get_channel_centers(ah, chan, &centers);
- freq = centers.synth_center;
-
- ah->config.spurmode = SPUR_ENABLE_EEPROM;
- for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
- cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz);
-
- if (is2GHz)
- cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ;
- else
- cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ;
-
- if (AR_NO_SPUR == cur_bb_spur)
- break;
- cur_bb_spur = cur_bb_spur - freq;
-
- if (IS_CHAN_HT40(chan)) {
- if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) &&
- (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) {
- bb_spur = cur_bb_spur;
- break;
- }
- } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) &&
- (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) {
- bb_spur = cur_bb_spur;
- break;
- }
- }
-
- if (AR_NO_SPUR == bb_spur) {
- REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK,
- AR_PHY_FORCE_CLKEN_CCK_MRC_MUX);
- return;
- } else {
- REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK,
- AR_PHY_FORCE_CLKEN_CCK_MRC_MUX);
- }
-
- bin = bb_spur * 320;
-
- tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0));
-
- newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI |
- AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER |
- AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
- AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
- REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), newVal);
-
- newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL |
- AR_PHY_SPUR_REG_ENABLE_MASK_PPM |
- AR_PHY_SPUR_REG_MASK_RATE_SELECT |
- AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI |
- SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH));
- REG_WRITE(ah, AR_PHY_SPUR_REG, newVal);
-
- if (IS_CHAN_HT40(chan)) {
- if (bb_spur < 0) {
- spur_subchannel_sd = 1;
- bb_spur_off = bb_spur + 10;
- } else {
- spur_subchannel_sd = 0;
- bb_spur_off = bb_spur - 10;
- }
- } else {
- spur_subchannel_sd = 0;
- bb_spur_off = bb_spur;
- }
-
- if (IS_CHAN_HT40(chan))
- spur_delta_phase =
- ((bb_spur * 262144) /
- 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE;
- else
- spur_delta_phase =
- ((bb_spur * 524288) /
- 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE;
-
- denominator = IS_CHAN_2GHZ(chan) ? 44 : 40;
- spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff;
-
- newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC |
- SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) |
- SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE));
- REG_WRITE(ah, AR_PHY_TIMING11, newVal);
-
- newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S;
- REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal);
-
- cur_bin = -6000;
- upper = bin + 100;
- lower = bin - 100;
-
- for (i = 0; i < 4; i++) {
- int pilot_mask = 0;
- int chan_mask = 0;
- int bp = 0;
- for (bp = 0; bp < 30; bp++) {
- if ((cur_bin > lower) && (cur_bin < upper)) {
- pilot_mask = pilot_mask | 0x1 << bp;
- chan_mask = chan_mask | 0x1 << bp;
- }
- cur_bin += 100;
- }
- cur_bin += inc[i];
- REG_WRITE(ah, pilot_mask_reg[i], pilot_mask);
- REG_WRITE(ah, chan_mask_reg[i], chan_mask);
- }
-
- cur_vit_mask = 6100;
- upper = bin + 120;
- lower = bin - 120;
-
- for (i = 0; i < 123; i++) {
- if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) {
-
- /* workaround for gcc bug #37014 */
- volatile int tmp_v = abs(cur_vit_mask - bin);
-
- if (tmp_v < 75)
- mask_amt = 1;
- else
- mask_amt = 0;
- if (cur_vit_mask < 0)
- mask_m[abs(cur_vit_mask / 100)] = mask_amt;
- else
- mask_p[cur_vit_mask / 100] = mask_amt;
- }
- cur_vit_mask -= 100;
- }
-
- tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28)
- | (mask_m[48] << 26) | (mask_m[49] << 24)
- | (mask_m[50] << 22) | (mask_m[51] << 20)
- | (mask_m[52] << 18) | (mask_m[53] << 16)
- | (mask_m[54] << 14) | (mask_m[55] << 12)
- | (mask_m[56] << 10) | (mask_m[57] << 8)
- | (mask_m[58] << 6) | (mask_m[59] << 4)
- | (mask_m[60] << 2) | (mask_m[61] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask);
- REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask);
-
- tmp_mask = (mask_m[31] << 28)
- | (mask_m[32] << 26) | (mask_m[33] << 24)
- | (mask_m[34] << 22) | (mask_m[35] << 20)
- | (mask_m[36] << 18) | (mask_m[37] << 16)
- | (mask_m[48] << 14) | (mask_m[39] << 12)
- | (mask_m[40] << 10) | (mask_m[41] << 8)
- | (mask_m[42] << 6) | (mask_m[43] << 4)
- | (mask_m[44] << 2) | (mask_m[45] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask);
-
- tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28)
- | (mask_m[18] << 26) | (mask_m[18] << 24)
- | (mask_m[20] << 22) | (mask_m[20] << 20)
- | (mask_m[22] << 18) | (mask_m[22] << 16)
- | (mask_m[24] << 14) | (mask_m[24] << 12)
- | (mask_m[25] << 10) | (mask_m[26] << 8)
- | (mask_m[27] << 6) | (mask_m[28] << 4)
- | (mask_m[29] << 2) | (mask_m[30] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask);
-
- tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28)
- | (mask_m[2] << 26) | (mask_m[3] << 24)
- | (mask_m[4] << 22) | (mask_m[5] << 20)
- | (mask_m[6] << 18) | (mask_m[7] << 16)
- | (mask_m[8] << 14) | (mask_m[9] << 12)
- | (mask_m[10] << 10) | (mask_m[11] << 8)
- | (mask_m[12] << 6) | (mask_m[13] << 4)
- | (mask_m[14] << 2) | (mask_m[15] << 0);
- REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask);
-
- tmp_mask = (mask_p[15] << 28)
- | (mask_p[14] << 26) | (mask_p[13] << 24)
- | (mask_p[12] << 22) | (mask_p[11] << 20)
- | (mask_p[10] << 18) | (mask_p[9] << 16)
- | (mask_p[8] << 14) | (mask_p[7] << 12)
- | (mask_p[6] << 10) | (mask_p[5] << 8)
- | (mask_p[4] << 6) | (mask_p[3] << 4)
- | (mask_p[2] << 2) | (mask_p[1] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask);
-
- tmp_mask = (mask_p[30] << 28)
- | (mask_p[29] << 26) | (mask_p[28] << 24)
- | (mask_p[27] << 22) | (mask_p[26] << 20)
- | (mask_p[25] << 18) | (mask_p[24] << 16)
- | (mask_p[23] << 14) | (mask_p[22] << 12)
- | (mask_p[21] << 10) | (mask_p[20] << 8)
- | (mask_p[19] << 6) | (mask_p[18] << 4)
- | (mask_p[17] << 2) | (mask_p[16] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask);
-
- tmp_mask = (mask_p[45] << 28)
- | (mask_p[44] << 26) | (mask_p[43] << 24)
- | (mask_p[42] << 22) | (mask_p[41] << 20)
- | (mask_p[40] << 18) | (mask_p[39] << 16)
- | (mask_p[38] << 14) | (mask_p[37] << 12)
- | (mask_p[36] << 10) | (mask_p[35] << 8)
- | (mask_p[34] << 6) | (mask_p[33] << 4)
- | (mask_p[32] << 2) | (mask_p[31] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask);
-
- tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28)
- | (mask_p[59] << 26) | (mask_p[58] << 24)
- | (mask_p[57] << 22) | (mask_p[56] << 20)
- | (mask_p[55] << 18) | (mask_p[54] << 16)
- | (mask_p[53] << 14) | (mask_p[52] << 12)
- | (mask_p[51] << 10) | (mask_p[50] << 8)
- | (mask_p[49] << 6) | (mask_p[48] << 4)
- | (mask_p[47] << 2) | (mask_p[46] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask);
-}
-
-static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan)
-{
- int bb_spur = AR_NO_SPUR;
- int bin, cur_bin;
- int spur_freq_sd;
- int spur_delta_phase;
- int denominator;
- int upper, lower, cur_vit_mask;
- int tmp, new;
- int i;
- int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8,
- AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60
- };
- int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10,
- AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60
- };
- int inc[4] = { 0, 100, 0, 0 };
-
- int8_t mask_m[123];
- int8_t mask_p[123];
- int8_t mask_amt;
- int tmp_mask;
- int cur_bb_spur;
- bool is2GHz = IS_CHAN_2GHZ(chan);
-
- memset(&mask_m, 0, sizeof(int8_t) * 123);
- memset(&mask_p, 0, sizeof(int8_t) * 123);
-
- for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
- cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz);
- if (AR_NO_SPUR == cur_bb_spur)
- break;
- cur_bb_spur = cur_bb_spur - (chan->channel * 10);
- if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) {
- bb_spur = cur_bb_spur;
- break;
- }
- }
-
- if (AR_NO_SPUR == bb_spur)
- return;
-
- bin = bb_spur * 32;
-
- tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0));
- new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI |
- AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER |
- AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
- AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
-
- REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), new);
-
- new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL |
- AR_PHY_SPUR_REG_ENABLE_MASK_PPM |
- AR_PHY_SPUR_REG_MASK_RATE_SELECT |
- AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI |
- SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH));
- REG_WRITE(ah, AR_PHY_SPUR_REG, new);
-
- spur_delta_phase = ((bb_spur * 524288) / 100) &
- AR_PHY_TIMING11_SPUR_DELTA_PHASE;
-
- denominator = IS_CHAN_2GHZ(chan) ? 440 : 400;
- spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff;
-
- new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC |
- SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) |
- SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE));
- REG_WRITE(ah, AR_PHY_TIMING11, new);
-
- cur_bin = -6000;
- upper = bin + 100;
- lower = bin - 100;
-
- for (i = 0; i < 4; i++) {
- int pilot_mask = 0;
- int chan_mask = 0;
- int bp = 0;
- for (bp = 0; bp < 30; bp++) {
- if ((cur_bin > lower) && (cur_bin < upper)) {
- pilot_mask = pilot_mask | 0x1 << bp;
- chan_mask = chan_mask | 0x1 << bp;
- }
- cur_bin += 100;
- }
- cur_bin += inc[i];
- REG_WRITE(ah, pilot_mask_reg[i], pilot_mask);
- REG_WRITE(ah, chan_mask_reg[i], chan_mask);
- }
-
- cur_vit_mask = 6100;
- upper = bin + 120;
- lower = bin - 120;
-
- for (i = 0; i < 123; i++) {
- if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) {
-
- /* workaround for gcc bug #37014 */
- volatile int tmp_v = abs(cur_vit_mask - bin);
-
- if (tmp_v < 75)
- mask_amt = 1;
- else
- mask_amt = 0;
- if (cur_vit_mask < 0)
- mask_m[abs(cur_vit_mask / 100)] = mask_amt;
- else
- mask_p[cur_vit_mask / 100] = mask_amt;
- }
- cur_vit_mask -= 100;
- }
-
- tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28)
- | (mask_m[48] << 26) | (mask_m[49] << 24)
- | (mask_m[50] << 22) | (mask_m[51] << 20)
- | (mask_m[52] << 18) | (mask_m[53] << 16)
- | (mask_m[54] << 14) | (mask_m[55] << 12)
- | (mask_m[56] << 10) | (mask_m[57] << 8)
- | (mask_m[58] << 6) | (mask_m[59] << 4)
- | (mask_m[60] << 2) | (mask_m[61] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask);
- REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask);
-
- tmp_mask = (mask_m[31] << 28)
- | (mask_m[32] << 26) | (mask_m[33] << 24)
- | (mask_m[34] << 22) | (mask_m[35] << 20)
- | (mask_m[36] << 18) | (mask_m[37] << 16)
- | (mask_m[48] << 14) | (mask_m[39] << 12)
- | (mask_m[40] << 10) | (mask_m[41] << 8)
- | (mask_m[42] << 6) | (mask_m[43] << 4)
- | (mask_m[44] << 2) | (mask_m[45] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask);
-
- tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28)
- | (mask_m[18] << 26) | (mask_m[18] << 24)
- | (mask_m[20] << 22) | (mask_m[20] << 20)
- | (mask_m[22] << 18) | (mask_m[22] << 16)
- | (mask_m[24] << 14) | (mask_m[24] << 12)
- | (mask_m[25] << 10) | (mask_m[26] << 8)
- | (mask_m[27] << 6) | (mask_m[28] << 4)
- | (mask_m[29] << 2) | (mask_m[30] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask);
-
- tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28)
- | (mask_m[2] << 26) | (mask_m[3] << 24)
- | (mask_m[4] << 22) | (mask_m[5] << 20)
- | (mask_m[6] << 18) | (mask_m[7] << 16)
- | (mask_m[8] << 14) | (mask_m[9] << 12)
- | (mask_m[10] << 10) | (mask_m[11] << 8)
- | (mask_m[12] << 6) | (mask_m[13] << 4)
- | (mask_m[14] << 2) | (mask_m[15] << 0);
- REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask);
-
- tmp_mask = (mask_p[15] << 28)
- | (mask_p[14] << 26) | (mask_p[13] << 24)
- | (mask_p[12] << 22) | (mask_p[11] << 20)
- | (mask_p[10] << 18) | (mask_p[9] << 16)
- | (mask_p[8] << 14) | (mask_p[7] << 12)
- | (mask_p[6] << 10) | (mask_p[5] << 8)
- | (mask_p[4] << 6) | (mask_p[3] << 4)
- | (mask_p[2] << 2) | (mask_p[1] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask);
-
- tmp_mask = (mask_p[30] << 28)
- | (mask_p[29] << 26) | (mask_p[28] << 24)
- | (mask_p[27] << 22) | (mask_p[26] << 20)
- | (mask_p[25] << 18) | (mask_p[24] << 16)
- | (mask_p[23] << 14) | (mask_p[22] << 12)
- | (mask_p[21] << 10) | (mask_p[20] << 8)
- | (mask_p[19] << 6) | (mask_p[18] << 4)
- | (mask_p[17] << 2) | (mask_p[16] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask);
-
- tmp_mask = (mask_p[45] << 28)
- | (mask_p[44] << 26) | (mask_p[43] << 24)
- | (mask_p[42] << 22) | (mask_p[41] << 20)
- | (mask_p[40] << 18) | (mask_p[39] << 16)
- | (mask_p[38] << 14) | (mask_p[37] << 12)
- | (mask_p[36] << 10) | (mask_p[35] << 8)
- | (mask_p[34] << 6) | (mask_p[33] << 4)
- | (mask_p[32] << 2) | (mask_p[31] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask);
-
- tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28)
- | (mask_p[59] << 26) | (mask_p[58] << 24)
- | (mask_p[57] << 22) | (mask_p[56] << 20)
- | (mask_p[55] << 18) | (mask_p[54] << 16)
- | (mask_p[53] << 14) | (mask_p[52] << 12)
- | (mask_p[51] << 10) | (mask_p[50] << 8)
- | (mask_p[49] << 6) | (mask_p[48] << 4)
- | (mask_p[47] << 2) | (mask_p[46] << 0);
- REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask);
- REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask);
-}
-
static void ath9k_enable_rfkill(struct ath_hw *ah)
{
REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL,
@@ -2342,17 +1942,16 @@ static void ath9k_enable_rfkill(struct ath_hw *ah)
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
bool bChannelChange)
{
+ struct ath_common *common = ath9k_hw_common(ah);
u32 saveLedState;
- struct ath_softc *sc = ah->ah_sc;
struct ath9k_channel *curchan = ah->curchan;
u32 saveDefAntenna;
u32 macStaId1;
u64 tsf = 0;
int i, rx_chainmask, r;
- ah->extprotspacing = sc->ht_extprotspacing;
- ah->txchainmask = sc->tx_chainmask;
- ah->rxchainmask = sc->rx_chainmask;
+ ah->txchainmask = common->tx_chainmask;
+ ah->rxchainmask = common->rx_chainmask;
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
return -EIO;
@@ -2369,7 +1968,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
!(AR_SREV_9280(ah) || IS_CHAN_A_5MHZ_SPACED(chan) ||
IS_CHAN_A_5MHZ_SPACED(ah->curchan))) {
- if (ath9k_hw_channel_change(ah, chan, sc->tx_chan_width)) {
+ if (ath9k_hw_channel_change(ah, chan)) {
ath9k_hw_loadnf(ah, ah->curchan);
ath9k_hw_start_nfcal(ah);
return 0;
@@ -2400,7 +1999,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
}
if (!ath9k_hw_chip_reset(ah, chan)) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Chip reset failed\n");
+ ath_print(common, ATH_DBG_FATAL, "Chip reset failed\n");
return -EINVAL;
}
@@ -2429,7 +2028,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3,
AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET);
}
- r = ath9k_hw_process_ini(ah, chan, sc->tx_chan_width);
+ r = ath9k_hw_process_ini(ah, chan);
if (r)
return r;
@@ -2453,17 +2052,11 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
ath9k_hw_set_delta_slope(ah, chan);
- if (AR_SREV_9280_10_OR_LATER(ah))
- ath9k_hw_9280_spur_mitigate(ah, chan);
- else
- ath9k_hw_spur_mitigate(ah, chan);
-
+ ah->ath9k_hw_spur_mitigate_freq(ah, chan);
ah->eep_ops->set_board_values(ah, chan);
- ath9k_hw_decrease_chain_power(ah, chan);
-
- REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(ah->macaddr));
- REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(ah->macaddr + 4)
+ REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(common->macaddr));
+ REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(common->macaddr + 4)
| macStaId1
| AR_STA_ID1_RTS_USE_DEF
| (ah->config.
@@ -2471,24 +2064,19 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
| ah->sta_id1_defaults);
ath9k_hw_set_operating_mode(ah, ah->opmode);
- REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(sc->bssidmask));
- REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(sc->bssidmask + 4));
+ ath_hw_setbssidmask(common);
REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna);
- REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(sc->curbssid));
- REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(sc->curbssid + 4) |
- ((sc->curaid & 0x3fff) << AR_BSS_ID1_AID_S));
+ ath9k_hw_write_associd(ah);
REG_WRITE(ah, AR_ISR, ~0);
REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
- if (AR_SREV_9280_10_OR_LATER(ah))
- ath9k_hw_ar9280_set_channel(ah, chan);
- else
- if (!(ath9k_hw_set_channel(ah, chan)))
- return -EIO;
+ r = ah->ath9k_hw_rf_set_freq(ah, chan);
+ if (r)
+ return r;
for (i = 0; i < AR_NUM_DCU; i++)
REG_WRITE(ah, AR_DQCUMASK(i), 1 << i);
@@ -2558,13 +2146,13 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
u32 mask;
mask = REG_READ(ah, AR_CFG);
if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET,
+ ath_print(common, ATH_DBG_RESET,
"CFG Byte Swap Set 0x%x\n", mask);
} else {
mask =
INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB;
REG_WRITE(ah, AR_CFG, mask);
- DPRINTF(ah->ah_sc, ATH_DBG_RESET,
+ ath_print(common, ATH_DBG_RESET,
"Setting CFG 0x%x\n", REG_READ(ah, AR_CFG));
}
} else {
@@ -2577,11 +2165,12 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
#endif
}
- if (ah->ah_sc->sc_flags & SC_OP_BTCOEX_ENABLED)
+ if (ah->btcoex_hw.enabled)
ath9k_hw_btcoex_enable(ah);
return 0;
}
+EXPORT_SYMBOL(ath9k_hw_reset);
/************************/
/* Key Cache Management */
@@ -2592,8 +2181,8 @@ bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry)
u32 keyType;
if (entry >= ah->caps.keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "keychache entry %u out of range\n", entry);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "keychache entry %u out of range\n", entry);
return false;
}
@@ -2620,14 +2209,15 @@ bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry)
return true;
}
+EXPORT_SYMBOL(ath9k_hw_keyreset);
bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac)
{
u32 macHi, macLo;
if (entry >= ah->caps.keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "keychache entry %u out of range\n", entry);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "keychache entry %u out of range\n", entry);
return false;
}
@@ -2648,18 +2238,20 @@ bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac)
return true;
}
+EXPORT_SYMBOL(ath9k_hw_keysetmac);
bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
const struct ath9k_keyval *k,
const u8 *mac)
{
const struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct ath_common *common = ath9k_hw_common(ah);
u32 key0, key1, key2, key3, key4;
u32 keyType;
if (entry >= pCap->keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "keycache entry %u out of range\n", entry);
+ ath_print(common, ATH_DBG_FATAL,
+ "keycache entry %u out of range\n", entry);
return false;
}
@@ -2669,9 +2261,9 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
break;
case ATH9K_CIPHER_AES_CCM:
if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "AES-CCM not supported by mac rev 0x%x\n",
- ah->hw_version.macRev);
+ ath_print(common, ATH_DBG_ANY,
+ "AES-CCM not supported by mac rev 0x%x\n",
+ ah->hw_version.macRev);
return false;
}
keyType = AR_KEYTABLE_TYPE_CCM;
@@ -2680,15 +2272,15 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
keyType = AR_KEYTABLE_TYPE_TKIP;
if (ATH9K_IS_MIC_ENABLED(ah)
&& entry + 64 >= pCap->keycache_size) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "entry %u inappropriate for TKIP\n", entry);
+ ath_print(common, ATH_DBG_ANY,
+ "entry %u inappropriate for TKIP\n", entry);
return false;
}
break;
case ATH9K_CIPHER_WEP:
if (k->kv_len < WLAN_KEY_LEN_WEP40) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "WEP key length %u too small\n", k->kv_len);
+ ath_print(common, ATH_DBG_ANY,
+ "WEP key length %u too small\n", k->kv_len);
return false;
}
if (k->kv_len <= WLAN_KEY_LEN_WEP40)
@@ -2702,8 +2294,8 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
keyType = AR_KEYTABLE_TYPE_CLR;
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "cipher %u not supported\n", k->kv_type);
+ ath_print(common, ATH_DBG_FATAL,
+ "cipher %u not supported\n", k->kv_type);
return false;
}
@@ -2845,6 +2437,7 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
return true;
}
+EXPORT_SYMBOL(ath9k_hw_set_keycache_entry);
bool ath9k_hw_keyisvalid(struct ath_hw *ah, u16 entry)
{
@@ -2855,6 +2448,7 @@ bool ath9k_hw_keyisvalid(struct ath_hw *ah, u16 entry)
}
return false;
}
+EXPORT_SYMBOL(ath9k_hw_keyisvalid);
/******************************/
/* Power Management (Chipset) */
@@ -2869,8 +2463,9 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
if (!AR_SREV_9100(ah))
REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
- REG_CLR_BIT(ah, (AR_RTC_RESET),
- AR_RTC_RESET_EN);
+ if(!AR_SREV_5416(ah))
+ REG_CLR_BIT(ah, (AR_RTC_RESET),
+ AR_RTC_RESET_EN);
}
}
@@ -2902,6 +2497,7 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
ATH9K_RESET_POWER_ON) != true) {
return false;
}
+ ath9k_hw_init_pll(ah, NULL);
}
if (AR_SREV_9100(ah))
REG_SET_BIT(ah, AR_RTC_RESET,
@@ -2920,8 +2516,9 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
AR_RTC_FORCE_WAKE_EN);
}
if (i == 0) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Failed to wakeup in %uus\n", POWER_UP_TIME / 20);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "Failed to wakeup in %uus\n",
+ POWER_UP_TIME / 20);
return false;
}
}
@@ -2931,9 +2528,9 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
return true;
}
-static bool ath9k_hw_setpower_nolock(struct ath_hw *ah,
- enum ath9k_power_mode mode)
+bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
{
+ struct ath_common *common = ath9k_hw_common(ah);
int status = true, setChip = true;
static const char *modes[] = {
"AWAKE",
@@ -2945,8 +2542,8 @@ static bool ath9k_hw_setpower_nolock(struct ath_hw *ah,
if (ah->power_mode == mode)
return status;
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s -> %s\n",
- modes[ah->power_mode], modes[mode]);
+ ath_print(common, ATH_DBG_RESET, "%s -> %s\n",
+ modes[ah->power_mode], modes[mode]);
switch (mode) {
case ATH9K_PM_AWAKE:
@@ -2960,59 +2557,15 @@ static bool ath9k_hw_setpower_nolock(struct ath_hw *ah,
ath9k_set_power_network_sleep(ah, setChip);
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Unknown power mode %u\n", mode);
+ ath_print(common, ATH_DBG_FATAL,
+ "Unknown power mode %u\n", mode);
return false;
}
ah->power_mode = mode;
return status;
}
-
-bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
-{
- unsigned long flags;
- bool ret;
-
- spin_lock_irqsave(&ah->ah_sc->sc_pm_lock, flags);
- ret = ath9k_hw_setpower_nolock(ah, mode);
- spin_unlock_irqrestore(&ah->ah_sc->sc_pm_lock, flags);
-
- return ret;
-}
-
-void ath9k_ps_wakeup(struct ath_softc *sc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sc->sc_pm_lock, flags);
- if (++sc->ps_usecount != 1)
- goto unlock;
-
- ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_AWAKE);
-
- unlock:
- spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
-}
-
-void ath9k_ps_restore(struct ath_softc *sc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sc->sc_pm_lock, flags);
- if (--sc->ps_usecount != 0)
- goto unlock;
-
- if (sc->ps_enabled &&
- !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
- SC_OP_WAIT_FOR_CAB |
- SC_OP_WAIT_FOR_PSPOLL_DATA |
- SC_OP_WAIT_FOR_TX_ACK)))
- ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
-
- unlock:
- spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
-}
+EXPORT_SYMBOL(ath9k_hw_setpower);
/*
* Helper for ASPM support.
@@ -3145,6 +2698,7 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off)
}
}
}
+EXPORT_SYMBOL(ath9k_hw_configpcipowersave);
/**********************/
/* Interrupt Handling */
@@ -3168,6 +2722,7 @@ bool ath9k_hw_intrpend(struct ath_hw *ah)
return false;
}
+EXPORT_SYMBOL(ath9k_hw_intrpend);
bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
{
@@ -3176,6 +2731,7 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
struct ath9k_hw_capabilities *pCap = &ah->caps;
u32 sync_cause = 0;
bool fatal_int = false;
+ struct ath_common *common = ath9k_hw_common(ah);
if (!AR_SREV_9100(ah)) {
if (REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) {
@@ -3249,8 +2805,8 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
}
if (isr & AR_ISR_RXORN) {
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "receive FIFO overrun interrupt\n");
+ ath_print(common, ATH_DBG_INTERRUPT,
+ "receive FIFO overrun interrupt\n");
}
if (!AR_SREV_9100(ah)) {
@@ -3292,25 +2848,25 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
if (fatal_int) {
if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "received PCI FATAL interrupt\n");
+ ath_print(common, ATH_DBG_ANY,
+ "received PCI FATAL interrupt\n");
}
if (sync_cause & AR_INTR_SYNC_HOST1_PERR) {
- DPRINTF(ah->ah_sc, ATH_DBG_ANY,
- "received PCI PERR interrupt\n");
+ ath_print(common, ATH_DBG_ANY,
+ "received PCI PERR interrupt\n");
}
*masked |= ATH9K_INT_FATAL;
}
if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) {
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n");
+ ath_print(common, ATH_DBG_INTERRUPT,
+ "AR_INTR_SYNC_RADM_CPL_TIMEOUT\n");
REG_WRITE(ah, AR_RC, AR_RC_HOSTIF);
REG_WRITE(ah, AR_RC, 0);
*masked |= ATH9K_INT_FATAL;
}
if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) {
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "AR_INTR_SYNC_LOCAL_TIMEOUT\n");
+ ath_print(common, ATH_DBG_INTERRUPT,
+ "AR_INTR_SYNC_LOCAL_TIMEOUT\n");
}
REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause);
@@ -3319,17 +2875,19 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
return true;
}
+EXPORT_SYMBOL(ath9k_hw_getisr);
enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
{
u32 omask = ah->mask_reg;
u32 mask, mask2;
struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct ath_common *common = ath9k_hw_common(ah);
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
+ ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
if (omask & ATH9K_INT_GLOBAL) {
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "disable IER\n");
+ ath_print(common, ATH_DBG_INTERRUPT, "disable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
(void) REG_READ(ah, AR_IER);
if (!AR_SREV_9100(ah)) {
@@ -3386,7 +2944,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
mask2 |= AR_IMR_S2_CST;
}
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask);
+ ath_print(common, ATH_DBG_INTERRUPT, "new IMR 0x%x\n", mask);
REG_WRITE(ah, AR_IMR, mask);
mask = REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM |
AR_IMR_S2_DTIM |
@@ -3406,7 +2964,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
}
if (ints & ATH9K_INT_GLOBAL) {
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "enable IER\n");
+ ath_print(common, ATH_DBG_INTERRUPT, "enable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
if (!AR_SREV_9100(ah)) {
REG_WRITE(ah, AR_INTR_ASYNC_ENABLE,
@@ -3419,12 +2977,13 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
REG_WRITE(ah, AR_INTR_SYNC_MASK,
AR_INTR_SYNC_DEFAULT);
}
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
- REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
+ ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
+ REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
}
return omask;
}
+EXPORT_SYMBOL(ath9k_hw_set_interrupts);
/*******************/
/* Beacon Handling */
@@ -3467,9 +3026,9 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN;
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON,
- "%s: unsupported opmode: %d\n",
- __func__, ah->opmode);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_BEACON,
+ "%s: unsupported opmode: %d\n",
+ __func__, ah->opmode);
return;
break;
}
@@ -3481,18 +3040,19 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
beacon_period &= ~ATH9K_BEACON_ENA;
if (beacon_period & ATH9K_BEACON_RESET_TSF) {
- beacon_period &= ~ATH9K_BEACON_RESET_TSF;
ath9k_hw_reset_tsf(ah);
}
REG_SET_BIT(ah, AR_TIMER_MODE, flags);
}
+EXPORT_SYMBOL(ath9k_hw_beaconinit);
void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
const struct ath9k_beacon_state *bs)
{
u32 nextTbtt, beaconintval, dtimperiod, beacontimeout;
struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct ath_common *common = ath9k_hw_common(ah);
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt));
@@ -3518,10 +3078,10 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
else
nextTbtt = bs->bs_nexttbtt;
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "next DTIM %d\n", bs->bs_nextdtim);
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt);
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "beacon period %d\n", beaconintval);
- DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "DTIM period %d\n", dtimperiod);
+ ath_print(common, ATH_DBG_BEACON, "next DTIM %d\n", bs->bs_nextdtim);
+ ath_print(common, ATH_DBG_BEACON, "next beacon %d\n", nextTbtt);
+ ath_print(common, ATH_DBG_BEACON, "beacon period %d\n", beaconintval);
+ ath_print(common, ATH_DBG_BEACON, "DTIM period %d\n", dtimperiod);
REG_WRITE(ah, AR_NEXT_DTIM,
TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP));
@@ -3549,16 +3109,18 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
/* TSF Out of Range Threshold */
REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold);
}
+EXPORT_SYMBOL(ath9k_hw_set_sta_beacon_timers);
/*******************/
/* HW Capabilities */
/*******************/
-void ath9k_hw_fill_cap_info(struct ath_hw *ah)
+int ath9k_hw_fill_cap_info(struct ath_hw *ah)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
- struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
u16 capField = 0, eeval;
@@ -3579,11 +3141,17 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah)
regulatory->current_rd += 5;
else if (regulatory->current_rd == 0x41)
regulatory->current_rd = 0x43;
- DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
- "regdomain mapped to 0x%x\n", regulatory->current_rd);
+ ath_print(common, ATH_DBG_REGULATORY,
+ "regdomain mapped to 0x%x\n", regulatory->current_rd);
}
eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE);
+ if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) {
+ ath_print(common, ATH_DBG_FATAL,
+ "no band has been marked as supported in EEPROM.\n");
+ return -EINVAL;
+ }
+
bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX);
if (eeval & AR5416_OPFLAGS_11A) {
@@ -3670,7 +3238,11 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->keycache_size = AR_KEYTABLE_SIZE;
pCap->hw_caps |= ATH9K_HW_CAP_FASTCC;
- pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
+
+ if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+ pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD >> 1;
+ else
+ pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
if (AR_SREV_9285_10_OR_LATER(ah))
pCap->num_gpio_pins = AR9285_NUM_GPIO;
@@ -3719,7 +3291,10 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah)
AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN;
}
- pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND;
+ /* Advertise midband for AR5416 with FCC midband set in eeprom */
+ if (regulatory->current_rd_ext & (1 << REG_EXT_FCC_MIDBAND) &&
+ AR_SREV_5416(ah))
+ pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND;
pCap->num_antcfg_5ghz =
ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_5GHZ);
@@ -3727,19 +3302,21 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah)
ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_2GHZ);
if (AR_SREV_9280_10_OR_LATER(ah) &&
- ath_btcoex_supported(ah->hw_version.subsysid)) {
- btcoex_info->btactive_gpio = ATH_BTACTIVE_GPIO;
- btcoex_info->wlanactive_gpio = ATH_WLANACTIVE_GPIO;
+ ath9k_hw_btcoex_supported(ah)) {
+ btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO;
+ btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO;
if (AR_SREV_9285(ah)) {
- btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_3WIRE;
- btcoex_info->btpriority_gpio = ATH_BTPRIORITY_GPIO;
+ btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE;
+ btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO;
} else {
- btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_2WIRE;
+ btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE;
}
} else {
- btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_NONE;
+ btcoex_hw->scheme = ATH_BTCOEX_CFG_NONE;
}
+
+ return 0;
}
bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type,
@@ -3812,6 +3389,7 @@ bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type,
return false;
}
}
+EXPORT_SYMBOL(ath9k_hw_getcapability);
bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type,
u32 capability, u32 setting, int *status)
@@ -3845,6 +3423,7 @@ bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type,
return false;
}
}
+EXPORT_SYMBOL(ath9k_hw_setcapability);
/****************************/
/* GPIO / RFKILL / Antennae */
@@ -3882,7 +3461,7 @@ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio)
{
u32 gpio_shift;
- ASSERT(gpio < ah->caps.num_gpio_pins);
+ BUG_ON(gpio >= ah->caps.num_gpio_pins);
gpio_shift = gpio << 1;
@@ -3891,6 +3470,7 @@ void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio)
(AR_GPIO_OE_OUT_DRV_NO << gpio_shift),
(AR_GPIO_OE_OUT_DRV << gpio_shift));
}
+EXPORT_SYMBOL(ath9k_hw_cfg_gpio_input);
u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
{
@@ -3909,6 +3489,7 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
else
return MS_REG_READ(AR, gpio) != 0;
}
+EXPORT_SYMBOL(ath9k_hw_gpio_get);
void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
u32 ah_signal_type)
@@ -3924,67 +3505,26 @@ void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
(AR_GPIO_OE_OUT_DRV_ALL << gpio_shift),
(AR_GPIO_OE_OUT_DRV << gpio_shift));
}
+EXPORT_SYMBOL(ath9k_hw_cfg_output);
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
{
REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
AR_GPIO_BIT(gpio));
}
+EXPORT_SYMBOL(ath9k_hw_set_gpio);
u32 ath9k_hw_getdefantenna(struct ath_hw *ah)
{
return REG_READ(ah, AR_DEF_ANTENNA) & 0x7;
}
+EXPORT_SYMBOL(ath9k_hw_getdefantenna);
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna)
{
REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
}
-
-bool ath9k_hw_setantennaswitch(struct ath_hw *ah,
- enum ath9k_ant_setting settings,
- struct ath9k_channel *chan,
- u8 *tx_chainmask,
- u8 *rx_chainmask,
- u8 *antenna_cfgd)
-{
- static u8 tx_chainmask_cfg, rx_chainmask_cfg;
-
- if (AR_SREV_9280(ah)) {
- if (!tx_chainmask_cfg) {
-
- tx_chainmask_cfg = *tx_chainmask;
- rx_chainmask_cfg = *rx_chainmask;
- }
-
- switch (settings) {
- case ATH9K_ANT_FIXED_A:
- *tx_chainmask = ATH9K_ANTENNA0_CHAINMASK;
- *rx_chainmask = ATH9K_ANTENNA0_CHAINMASK;
- *antenna_cfgd = true;
- break;
- case ATH9K_ANT_FIXED_B:
- if (ah->caps.tx_chainmask >
- ATH9K_ANTENNA1_CHAINMASK) {
- *tx_chainmask = ATH9K_ANTENNA1_CHAINMASK;
- }
- *rx_chainmask = ATH9K_ANTENNA1_CHAINMASK;
- *antenna_cfgd = true;
- break;
- case ATH9K_ANT_VARIABLE:
- *tx_chainmask = tx_chainmask_cfg;
- *rx_chainmask = rx_chainmask_cfg;
- *antenna_cfgd = true;
- break;
- default:
- break;
- }
- } else {
- ah->config.diversity_control = settings;
- }
-
- return true;
-}
+EXPORT_SYMBOL(ath9k_hw_setantenna);
/*********************/
/* General Operation */
@@ -4002,6 +3542,7 @@ u32 ath9k_hw_getrxfilter(struct ath_hw *ah)
return bits;
}
+EXPORT_SYMBOL(ath9k_hw_getrxfilter);
void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits)
{
@@ -4023,19 +3564,30 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits)
REG_WRITE(ah, AR_RXCFG,
REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA);
}
+EXPORT_SYMBOL(ath9k_hw_setrxfilter);
bool ath9k_hw_phy_disable(struct ath_hw *ah)
{
- return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM);
+ if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
+ return false;
+
+ ath9k_hw_init_pll(ah, NULL);
+ return true;
}
+EXPORT_SYMBOL(ath9k_hw_phy_disable);
bool ath9k_hw_disable(struct ath_hw *ah)
{
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
return false;
- return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD);
+ if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD))
+ return false;
+
+ ath9k_hw_init_pll(ah, NULL);
+ return true;
}
+EXPORT_SYMBOL(ath9k_hw_disable);
void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit)
{
@@ -4052,35 +3604,36 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit)
min((u32) MAX_RATE_POWER,
(u32) regulatory->power_limit));
}
+EXPORT_SYMBOL(ath9k_hw_set_txpowerlimit);
void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac)
{
- memcpy(ah->macaddr, mac, ETH_ALEN);
+ memcpy(ath9k_hw_common(ah)->macaddr, mac, ETH_ALEN);
}
+EXPORT_SYMBOL(ath9k_hw_setmac);
void ath9k_hw_setopmode(struct ath_hw *ah)
{
ath9k_hw_set_operating_mode(ah, ah->opmode);
}
+EXPORT_SYMBOL(ath9k_hw_setopmode);
void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1)
{
REG_WRITE(ah, AR_MCAST_FIL0, filter0);
REG_WRITE(ah, AR_MCAST_FIL1, filter1);
}
+EXPORT_SYMBOL(ath9k_hw_setmcastfilter);
-void ath9k_hw_setbssidmask(struct ath_softc *sc)
+void ath9k_hw_write_associd(struct ath_hw *ah)
{
- REG_WRITE(sc->sc_ah, AR_BSSMSKL, get_unaligned_le32(sc->bssidmask));
- REG_WRITE(sc->sc_ah, AR_BSSMSKU, get_unaligned_le16(sc->bssidmask + 4));
-}
+ struct ath_common *common = ath9k_hw_common(ah);
-void ath9k_hw_write_associd(struct ath_softc *sc)
-{
- REG_WRITE(sc->sc_ah, AR_BSS_ID0, get_unaligned_le32(sc->curbssid));
- REG_WRITE(sc->sc_ah, AR_BSS_ID1, get_unaligned_le16(sc->curbssid + 4) |
- ((sc->curaid & 0x3fff) << AR_BSS_ID1_AID_S));
+ REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(common->curbssid));
+ REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(common->curbssid + 4) |
+ ((common->curaid & 0x3fff) << AR_BSS_ID1_AID_S));
}
+EXPORT_SYMBOL(ath9k_hw_write_associd);
u64 ath9k_hw_gettsf64(struct ath_hw *ah)
{
@@ -4091,24 +3644,25 @@ u64 ath9k_hw_gettsf64(struct ath_hw *ah)
return tsf;
}
+EXPORT_SYMBOL(ath9k_hw_gettsf64);
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64)
{
REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff);
REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff);
}
+EXPORT_SYMBOL(ath9k_hw_settsf64);
void ath9k_hw_reset_tsf(struct ath_hw *ah)
{
- ath9k_ps_wakeup(ah->ah_sc);
if (!ath9k_hw_wait(ah, AR_SLP32_MODE, AR_SLP32_TSF_WRITE_STATUS, 0,
AH_TSF_WRITE_TIMEOUT))
- DPRINTF(ah->ah_sc, ATH_DBG_RESET,
- "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n");
+ ath_print(ath9k_hw_common(ah), ATH_DBG_RESET,
+ "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n");
REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);
- ath9k_ps_restore(ah->ah_sc);
}
+EXPORT_SYMBOL(ath9k_hw_reset_tsf);
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting)
{
@@ -4117,11 +3671,28 @@ void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting)
else
ah->misc_mode &= ~AR_PCU_TX_ADD_TSF;
}
+EXPORT_SYMBOL(ath9k_hw_set_tsfadjust);
+
+/*
+ * Extend 15-bit time stamp from rx descriptor to
+ * a full 64-bit TSF using the current h/w TSF.
+*/
+u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp)
+{
+ u64 tsf;
+
+ tsf = ath9k_hw_gettsf64(ah);
+ if ((tsf & 0x7fff) < rstamp)
+ tsf -= 0x8000;
+ return (tsf & ~0x7fff) | rstamp;
+}
+EXPORT_SYMBOL(ath9k_hw_extend_tsf);
bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us)
{
if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) {
- DPRINTF(ah->ah_sc, ATH_DBG_RESET, "bad slot time %u\n", us);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_RESET,
+ "bad slot time %u\n", us);
ah->slottime = (u32) -1;
return false;
} else {
@@ -4130,13 +3701,14 @@ bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us)
return true;
}
}
+EXPORT_SYMBOL(ath9k_hw_setslottime);
-void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode)
+void ath9k_hw_set11nmac2040(struct ath_hw *ah)
{
+ struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
u32 macmode;
- if (mode == ATH9K_HT_MACMODE_2040 &&
- !ah->config.cwm_ignore_extcca)
+ if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca)
macmode = AR_2040_JOINED_RX_CLEAR;
else
macmode = 0;
@@ -4193,6 +3765,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah)
{
return REG_READ(ah, AR_TSF_L32);
}
+EXPORT_SYMBOL(ath9k_hw_gettsf32);
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
void (*trigger)(void *),
@@ -4206,8 +3779,9 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
if (timer == NULL) {
- printk(KERN_DEBUG "Failed to allocate memory"
- "for hw timer[%d]\n", timer_index);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "Failed to allocate memory"
+ "for hw timer[%d]\n", timer_index);
return NULL;
}
@@ -4220,10 +3794,12 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
return timer;
}
+EXPORT_SYMBOL(ath_gen_timer_alloc);
-void ath_gen_timer_start(struct ath_hw *ah,
- struct ath_gen_timer *timer,
- u32 timer_next, u32 timer_period)
+void ath9k_hw_gen_timer_start(struct ath_hw *ah,
+ struct ath_gen_timer *timer,
+ u32 timer_next,
+ u32 timer_period)
{
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
u32 tsf;
@@ -4234,8 +3810,9 @@ void ath_gen_timer_start(struct ath_hw *ah,
tsf = ath9k_hw_gettsf32(ah);
- DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, "curent tsf %x period %x"
- "timer_next %x\n", tsf, timer_period, timer_next);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_HWTIMER,
+ "curent tsf %x period %x"
+ "timer_next %x\n", tsf, timer_period, timer_next);
/*
* Pull timer_next forward if the current TSF already passed it
@@ -4258,15 +3835,10 @@ void ath_gen_timer_start(struct ath_hw *ah,
REG_SET_BIT(ah, AR_IMR_S5,
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
-
- if ((ah->ah_sc->imask & ATH9K_INT_GENTIMER) == 0) {
- ath9k_hw_set_interrupts(ah, 0);
- ah->ah_sc->imask |= ATH9K_INT_GENTIMER;
- ath9k_hw_set_interrupts(ah, ah->ah_sc->imask);
- }
}
+EXPORT_SYMBOL(ath9k_hw_gen_timer_start);
-void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
+void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
{
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
@@ -4285,14 +3857,8 @@ void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
clear_bit(timer->index, &timer_table->timer_mask.timer_bits);
-
- /* if no timer is enabled, turn off interrupt mask */
- if (timer_table->timer_mask.val == 0) {
- ath9k_hw_set_interrupts(ah, 0);
- ah->ah_sc->imask &= ~ATH9K_INT_GENTIMER;
- ath9k_hw_set_interrupts(ah, ah->ah_sc->imask);
- }
}
+EXPORT_SYMBOL(ath9k_hw_gen_timer_stop);
void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer)
{
@@ -4302,6 +3868,7 @@ void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer)
timer_table->timers[timer->index] = NULL;
kfree(timer);
}
+EXPORT_SYMBOL(ath_gen_timer_free);
/*
* Generic Timer Interrupts handling
@@ -4310,6 +3877,7 @@ void ath_gen_timer_isr(struct ath_hw *ah)
{
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
struct ath_gen_timer *timer;
+ struct ath_common *common = ath9k_hw_common(ah);
u32 trigger_mask, thresh_mask, index;
/* get hardware generic timer interrupt status */
@@ -4324,8 +3892,8 @@ void ath_gen_timer_isr(struct ath_hw *ah)
index = rightmost_index(timer_table, &thresh_mask);
timer = timer_table->timers[index];
BUG_ON(!timer);
- DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER,
- "TSF overflow for Gen timer %d\n", index);
+ ath_print(common, ATH_DBG_HWTIMER,
+ "TSF overflow for Gen timer %d\n", index);
timer->overflow(timer->arg);
}
@@ -4333,21 +3901,95 @@ void ath_gen_timer_isr(struct ath_hw *ah)
index = rightmost_index(timer_table, &trigger_mask);
timer = timer_table->timers[index];
BUG_ON(!timer);
- DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER,
- "Gen timer[%d] trigger\n", index);
+ ath_print(common, ATH_DBG_HWTIMER,
+ "Gen timer[%d] trigger\n", index);
timer->trigger(timer->arg);
}
}
+EXPORT_SYMBOL(ath_gen_timer_isr);
+
+static struct {
+ u32 version;
+ const char * name;
+} ath_mac_bb_names[] = {
+ /* Devices with external radios */
+ { AR_SREV_VERSION_5416_PCI, "5416" },
+ { AR_SREV_VERSION_5416_PCIE, "5418" },
+ { AR_SREV_VERSION_9100, "9100" },
+ { AR_SREV_VERSION_9160, "9160" },
+ /* Single-chip solutions */
+ { AR_SREV_VERSION_9280, "9280" },
+ { AR_SREV_VERSION_9285, "9285" },
+ { AR_SREV_VERSION_9287, "9287" },
+ { AR_SREV_VERSION_9271, "9271" },
+};
+
+/* For devices with external radios */
+static struct {
+ u16 version;
+ const char * name;
+} ath_rf_names[] = {
+ { 0, "5133" },
+ { AR_RAD5133_SREV_MAJOR, "5133" },
+ { AR_RAD5122_SREV_MAJOR, "5122" },
+ { AR_RAD2133_SREV_MAJOR, "2133" },
+ { AR_RAD2122_SREV_MAJOR, "2122" }
+};
+
+/*
+ * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
+ */
+static const char *ath9k_hw_mac_bb_name(u32 mac_bb_version)
+{
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(ath_mac_bb_names); i++) {
+ if (ath_mac_bb_names[i].version == mac_bb_version) {
+ return ath_mac_bb_names[i].name;
+ }
+ }
+
+ return "????";
+}
/*
- * Primitive to disable ASPM
+ * Return the RF name. "????" is returned if the RF is unknown.
+ * Used for devices with external radios.
*/
-void ath_pcie_aspm_disable(struct ath_softc *sc)
+static const char *ath9k_hw_rf_name(u16 rf_version)
+{
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(ath_rf_names); i++) {
+ if (ath_rf_names[i].version == rf_version) {
+ return ath_rf_names[i].name;
+ }
+ }
+
+ return "????";
+}
+
+void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len)
{
- struct pci_dev *pdev = to_pci_dev(sc->dev);
- u8 aspm;
+ int used;
+
+ /* chipsets >= AR9280 are single-chip */
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ used = snprintf(hw_name, len,
+ "Atheros AR%s Rev:%x",
+ ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
+ ah->hw_version.macRev);
+ }
+ else {
+ used = snprintf(hw_name, len,
+ "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x",
+ ath9k_hw_mac_bb_name(ah->hw_version.macVersion),
+ ah->hw_version.macRev,
+ ath9k_hw_rf_name((ah->hw_version.analog5GhzRev &
+ AR_RADIO_SREV_MAJOR)),
+ ah->hw_version.phyRev);
+ }
- pci_read_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, &aspm);
- aspm &= ~(ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1);
- pci_write_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, aspm);
+ hw_name[used] = '\0';
}
+EXPORT_SYMBOL(ath9k_hw_name);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index b8923457182..e2b0c73a616 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -27,17 +27,24 @@
#include "calib.h"
#include "reg.h"
#include "phy.h"
+#include "btcoex.h"
#include "../regd.h"
+#include "../debug.h"
#define ATHEROS_VENDOR_ID 0x168c
+
#define AR5416_DEVID_PCI 0x0023
#define AR5416_DEVID_PCIE 0x0024
#define AR9160_DEVID_PCI 0x0027
#define AR9280_DEVID_PCI 0x0029
#define AR9280_DEVID_PCIE 0x002a
#define AR9285_DEVID_PCIE 0x002b
+
#define AR5416_AR9100_DEVID 0x000b
+
+#define AR9271_USB 0x9271
+
#define AR_SUBVENDOR_ID_NOG 0x0e11
#define AR_SUBVENDOR_ID_NEW_A 0x7065
#define AR5416_MAGIC 0x19641014
@@ -49,9 +56,18 @@
#define AT9285_COEX3WIRE_SA_SUBSYSID 0x30aa
#define AT9285_COEX3WIRE_DA_SUBSYSID 0x30ab
+#define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1)
+
+#define ATH_DEFAULT_NOISE_FLOOR -95
+
+#define ATH9K_RSSI_BAD -128
+
/* Register read/write primitives */
-#define REG_WRITE(_ah, _reg, _val) ath9k_iowrite32((_ah), (_reg), (_val))
-#define REG_READ(_ah, _reg) ath9k_ioread32((_ah), (_reg))
+#define REG_WRITE(_ah, _reg, _val) \
+ ath9k_hw_common(_ah)->ops->write((_ah), (_val), (_reg))
+
+#define REG_READ(_ah, _reg) \
+ ath9k_hw_common(_ah)->ops->read((_ah), (_reg))
#define SM(_v, _f) (((_v) << _f##_S) & _f)
#define MS(_v, _f) (((_v) & _f) >> _f##_S)
@@ -91,7 +107,7 @@
#define AR_GPIO_BIT(_gpio) (1 << (_gpio))
#define BASE_ACTIVATE_DELAY 100
-#define RTC_PLL_SETTLE_DELAY 1000
+#define RTC_PLL_SETTLE_DELAY 100
#define COEF_SCALE_S 24
#define HT40_CHANNEL_CENTER_SHIFT 10
@@ -132,12 +148,6 @@ enum wireless_mode {
ATH9K_MODE_MAX,
};
-enum ath9k_ant_setting {
- ATH9K_ANT_VARIABLE = 0,
- ATH9K_ANT_FIXED_A,
- ATH9K_ANT_FIXED_B
-};
-
enum ath9k_hw_caps {
ATH9K_HW_CAP_MIC_AESCCM = BIT(0),
ATH9K_HW_CAP_MIC_CKIP = BIT(1),
@@ -201,8 +211,6 @@ struct ath9k_ops_config {
u32 cck_trig_high;
u32 cck_trig_low;
u32 enable_ani;
- enum ath9k_ant_setting diversity_control;
- u16 antenna_switch_swap;
int serialize_regmode;
bool intr_mitigation;
#define SPUR_DISABLE 0
@@ -218,6 +226,7 @@ struct ath9k_ops_config {
#define AR_SPUR_FEEQ_BOUND_HT20 10
int spurmode;
u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
+ u8 max_txtrig_level;
};
enum ath9k_int {
@@ -407,7 +416,7 @@ struct ath9k_hw_version {
* Using de Bruijin sequence to to look up 1's index in a 32 bit number
* debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001
*/
-#define debruijn32 0x077CB531UL
+#define debruijn32 0x077CB531U
struct ath_gen_timer_configuration {
u32 next_addr;
@@ -433,7 +442,8 @@ struct ath_gen_timer_table {
};
struct ath_hw {
- struct ath_softc *ah_sc;
+ struct ieee80211_hw *hw;
+ struct ath_common common;
struct ath9k_hw_version hw_version;
struct ath9k_ops_config config;
struct ath9k_hw_capabilities caps;
@@ -450,7 +460,6 @@ struct ath_hw {
bool sw_mgmt_crypto;
bool is_pciexpress;
- u8 macaddr[ETH_ALEN];
u16 tx_trig_level;
u16 rfsilent;
u32 rfkill_gpio;
@@ -523,7 +532,14 @@ struct ath_hw {
DONT_USE_32KHZ,
} enable_32kHz_clock;
- /* RF */
+ /* Callback for radio frequency change */
+ int (*ath9k_hw_rf_set_freq)(struct ath_hw *ah, struct ath9k_channel *chan);
+
+ /* Callback for baseband spur frequency */
+ void (*ath9k_hw_spur_mitigate_freq)(struct ath_hw *ah,
+ struct ath9k_channel *chan);
+
+ /* Used to program the radio on non single-chip devices */
u32 *analogBank0Data;
u32 *analogBank1Data;
u32 *analogBank2Data;
@@ -540,7 +556,6 @@ struct ath_hw {
u32 acktimeout;
u32 ctstimeout;
u32 globaltxtimeout;
- u8 gbeacon_rate;
/* ANI */
u32 proc_phyerr;
@@ -553,8 +568,10 @@ struct ath_hw {
int firpwr[5];
enum ath9k_ani_cmd ani_function;
+ /* Bluetooth coexistance */
+ struct ath_btcoex_hw btcoex_hw;
+
u32 intr_txqs;
- enum ath9k_ht_extprotspacing extprotspacing;
u8 txchainmask;
u8 rxchainmask;
@@ -578,20 +595,32 @@ struct ath_hw {
struct ar5416IniArray iniModesAdditional;
struct ar5416IniArray iniModesRxGain;
struct ar5416IniArray iniModesTxGain;
+ struct ar5416IniArray iniModes_9271_1_0_only;
+ struct ar5416IniArray iniCckfirNormal;
+ struct ar5416IniArray iniCckfirJapan2484;
u32 intr_gen_timer_trigger;
u32 intr_gen_timer_thresh;
struct ath_gen_timer_table hw_gen_timers;
};
+static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
+{
+ return &ah->common;
+}
+
+static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah)
+{
+ return &(ath9k_hw_common(ah)->regulatory);
+}
+
/* Initialization, Detach, Reset */
const char *ath9k_hw_probe(u16 vendorid, u16 devid);
void ath9k_hw_detach(struct ath_hw *ah);
int ath9k_hw_init(struct ath_hw *ah);
-void ath9k_hw_rf_free(struct ath_hw *ah);
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
bool bChannelChange);
-void ath9k_hw_fill_cap_info(struct ath_hw *ah);
+int ath9k_hw_fill_cap_info(struct ath_hw *ah);
bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type,
u32 capability, u32 *result);
bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type,
@@ -613,18 +642,13 @@ void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
u32 ath9k_hw_getdefantenna(struct ath_hw *ah);
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
-bool ath9k_hw_setantennaswitch(struct ath_hw *ah,
- enum ath9k_ant_setting settings,
- struct ath9k_channel *chan,
- u8 *tx_chainmask, u8 *rx_chainmask,
- u8 *antenna_cfgd);
/* General Operation */
bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
u32 ath9k_hw_reverse_bits(u32 val, u32 n);
bool ath9k_get_channel_edges(struct ath_hw *ah, u16 flags, u16 *low, u16 *high);
u16 ath9k_hw_computetxtime(struct ath_hw *ah,
- const struct ath_rate_table *rates,
+ u8 phy, int kbps,
u32 frameLen, u16 rateix, bool shortPreamble);
void ath9k_hw_get_channel_centers(struct ath_hw *ah,
struct ath9k_channel *chan,
@@ -637,19 +661,21 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit);
void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac);
void ath9k_hw_setopmode(struct ath_hw *ah);
void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1);
-void ath9k_hw_setbssidmask(struct ath_softc *sc);
-void ath9k_hw_write_associd(struct ath_softc *sc);
+void ath9k_hw_setbssidmask(struct ath_hw *ah);
+void ath9k_hw_write_associd(struct ath_hw *ah);
u64 ath9k_hw_gettsf64(struct ath_hw *ah);
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
void ath9k_hw_reset_tsf(struct ath_hw *ah);
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting);
+u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp);
bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us);
-void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode);
+void ath9k_hw_set11nmac2040(struct ath_hw *ah);
void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period);
void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
const struct ath9k_beacon_state *bs);
-bool ath9k_hw_setpower(struct ath_hw *ah,
- enum ath9k_power_mode mode);
+
+bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
+
void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off);
/* Interrupt Handling */
@@ -663,16 +689,20 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
void (*overflow)(void *),
void *arg,
u8 timer_index);
-void ath_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer,
- u32 timer_next, u32 timer_period);
-void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer);
+void ath9k_hw_gen_timer_start(struct ath_hw *ah,
+ struct ath_gen_timer *timer,
+ u32 timer_next,
+ u32 timer_period);
+void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer);
+
void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
void ath_gen_timer_isr(struct ath_hw *hw);
u32 ath9k_hw_gettsf32(struct ath_hw *ah);
+void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len);
+
#define ATH_PCIE_CAP_LINK_CTRL 0x70
#define ATH_PCIE_CAP_LINK_L0S 1
#define ATH_PCIE_CAP_LINK_L1 2
-void ath_pcie_aspm_disable(struct ath_softc *sc);
#endif
diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h
index 8622265a030..8a3bf3ab998 100644
--- a/drivers/net/wireless/ath/ath9k/initvals.h
+++ b/drivers/net/wireless/ath/ath9k/initvals.h
@@ -21,6 +21,8 @@ static const u32 ar5416Modes[][6] = {
{ 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 },
{ 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 },
{ 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf },
+ { 0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810 },
+ { 0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a, 0x0000320a },
{ 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 },
{ 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 },
{ 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
@@ -31,11 +33,11 @@ static const u32 ar5416Modes[][6] = {
{ 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
{ 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
{ 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 },
- { 0x00009850, 0x6c48b4e0, 0x6c48b4e0, 0x6c48b0de, 0x6c48b0de, 0x6c48b0de },
+ { 0x00009850, 0x6c48b4e0, 0x6d48b4e0, 0x6d48b0de, 0x6c48b0de, 0x6c48b0de },
{ 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e },
- { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e, 0x31395d5e },
+ { 0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e },
{ 0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18 },
- { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
+ { 0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
{ 0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 },
{ 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 },
{ 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
@@ -46,10 +48,10 @@ static const u32 ar5416Modes[][6] = {
{ 0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 },
{ 0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 },
{ 0x00009964, 0x00000000, 0x00000000, 0x00001120, 0x00001120, 0x00001120 },
- { 0x0000c9bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00 },
+ { 0x000099bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00 },
{ 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be },
{ 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 },
- { 0x000099c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c },
+ { 0x000099c8, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c, 0x6af6532c },
{ 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 },
{ 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 },
{ 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
@@ -199,7 +201,6 @@ static const u32 ar5416Common[][2] = {
{ 0x00008110, 0x00000168 },
{ 0x00008118, 0x000100aa },
{ 0x0000811c, 0x00003210 },
- { 0x00008120, 0x08f04800 },
{ 0x00008124, 0x00000000 },
{ 0x00008128, 0x00000000 },
{ 0x0000812c, 0x00000000 },
@@ -215,7 +216,6 @@ static const u32 ar5416Common[][2] = {
{ 0x00008178, 0x00000100 },
{ 0x0000817c, 0x00000000 },
{ 0x000081c4, 0x00000000 },
- { 0x000081d0, 0x00003210 },
{ 0x000081ec, 0x00000000 },
{ 0x000081f0, 0x00000000 },
{ 0x000081f4, 0x00000000 },
@@ -246,6 +246,7 @@ static const u32 ar5416Common[][2] = {
{ 0x00008258, 0x00000000 },
{ 0x0000825c, 0x400000ff },
{ 0x00008260, 0x00080922 },
+ { 0x00008264, 0xa8000010 },
{ 0x00008270, 0x00000000 },
{ 0x00008274, 0x40000000 },
{ 0x00008278, 0x003e4180 },
@@ -406,9 +407,9 @@ static const u32 ar5416Common[][2] = {
{ 0x0000a25c, 0x0f0f0f01 },
{ 0x0000a260, 0xdfa91f01 },
{ 0x0000a268, 0x00000000 },
- { 0x0000a26c, 0x0ebae9c6 },
- { 0x0000b26c, 0x0ebae9c6 },
- { 0x0000c26c, 0x0ebae9c6 },
+ { 0x0000a26c, 0x0e79e5c6 },
+ { 0x0000b26c, 0x0e79e5c6 },
+ { 0x0000c26c, 0x0e79e5c6 },
{ 0x0000d270, 0x00820820 },
{ 0x0000a278, 0x1ce739ce },
{ 0x0000a27c, 0x051701ce },
@@ -2551,26 +2552,27 @@ static const u32 ar9280Modes_9280_2[][6] = {
{ 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 },
{ 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 },
{ 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 },
- { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
+ { 0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e },
{ 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
{ 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
{ 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 },
{ 0x00009840, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e, 0x206a012e },
{ 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 },
- { 0x00009850, 0x6c4000e2, 0x6c4000e2, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 },
+ { 0x00009850, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 },
{ 0x00009858, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e },
- { 0x0000985c, 0x31395d5e, 0x31395d5e, 0x3139605e, 0x31395d5e, 0x31395d5e },
+ { 0x0000985c, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e, 0x31395d5e },
{ 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 },
{ 0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
{ 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 },
{ 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 },
{ 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
- { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 },
+ { 0x00009918, 0x0000000a, 0x00000014, 0x00000268, 0x0000000b, 0x00000016 },
{ 0x00009924, 0xd00a8a0b, 0xd00a8a0b, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d },
{ 0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010, 0xffbc1010 },
{ 0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 },
{ 0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 },
{ 0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210 },
+ { 0x0000c968, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce, 0x000003ce },
{ 0x000099b8, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c },
{ 0x000099bc, 0x00000a00, 0x00000a00, 0x00000c00, 0x00000c00, 0x00000c00 },
{ 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 },
@@ -2585,8 +2587,10 @@ static const u32 ar9280Modes_9280_2[][6] = {
{ 0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 },
{ 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
{ 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
+ { 0x0000a23c, 0x13c88000, 0x13c88000, 0x13c88001, 0x13c88000, 0x13c88000 },
{ 0x0000a250, 0x001ff000, 0x001ff000, 0x0004a000, 0x0004a000, 0x0004a000 },
{ 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
+ { 0x0000a388, 0x0c000000, 0x0c000000, 0x08000000, 0x0c000000, 0x0c000000 },
{ 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000 },
};
@@ -2813,7 +2817,6 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x00009958, 0x2108ecff },
{ 0x00009940, 0x14750604 },
{ 0x0000c95c, 0x004b6a8e },
- { 0x0000c968, 0x000003ce },
{ 0x00009970, 0x190fb515 },
{ 0x00009974, 0x00000000 },
{ 0x00009978, 0x00000001 },
@@ -2849,7 +2852,6 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x0000a22c, 0x233f7180 },
{ 0x0000a234, 0x20202020 },
{ 0x0000a238, 0x20202020 },
- { 0x0000a23c, 0x13c88000 },
{ 0x0000a240, 0x38490a20 },
{ 0x0000a244, 0x00007bb6 },
{ 0x0000a248, 0x0fff3ffc },
@@ -2859,8 +2861,8 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x0000a25c, 0x0f0f0f01 },
{ 0x0000a260, 0xdfa91f01 },
{ 0x0000a268, 0x00000000 },
- { 0x0000a26c, 0x0ebae9c6 },
- { 0x0000b26c, 0x0ebae9c6 },
+ { 0x0000a26c, 0x0e79e5c6 },
+ { 0x0000b26c, 0x0e79e5c6 },
{ 0x0000d270, 0x00820820 },
{ 0x0000a278, 0x1ce739ce },
{ 0x0000d35c, 0x07ffffef },
@@ -2874,7 +2876,6 @@ static const u32 ar9280Common_9280_2[][2] = {
{ 0x0000d37c, 0x7fffffe2 },
{ 0x0000d380, 0x7f3c7bba },
{ 0x0000d384, 0xf3307ff0 },
- { 0x0000a388, 0x0c000000 },
{ 0x0000a38c, 0x20202020 },
{ 0x0000a390, 0x20202020 },
{ 0x0000a394, 0x1ce739ce },
@@ -2940,7 +2941,7 @@ static const u32 ar9280Modes_fast_clock_9280_2[][3] = {
{ 0x0000801c, 0x148ec02b, 0x148ec057 },
{ 0x00008318, 0x000044c0, 0x00008980 },
{ 0x00009820, 0x02020200, 0x02020200 },
- { 0x00009824, 0x00000f0f, 0x00000f0f },
+ { 0x00009824, 0x01000f0f, 0x01000f0f },
{ 0x00009828, 0x0b020001, 0x0b020001 },
{ 0x00009834, 0x00000f0f, 0x00000f0f },
{ 0x00009844, 0x03721821, 0x03721821 },
@@ -3348,6 +3349,8 @@ static const u32 ar9280Modes_backoff_13db_rxgain_9280_2[][6] = {
};
static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = {
+ { 0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 },
+ { 0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce },
{ 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x0000a304, 0x00003002, 0x00003002, 0x00004002, 0x00004002, 0x00004002 },
{ 0x0000a308, 0x00006004, 0x00006004, 0x00007008, 0x00007008, 0x00007008 },
@@ -3376,11 +3379,11 @@ static const u32 ar9280Modes_high_power_tx_gain_9280_2[][6] = {
{ 0x00007840, 0x00172000, 0x00172000, 0x00172000, 0x00172000, 0x00172000 },
{ 0x00007820, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480 },
{ 0x00007844, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480, 0xf258a480 },
- { 0x0000a274, 0x0a19e652, 0x0a19e652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 },
- { 0x0000a27c, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce, 0x050739ce },
};
static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = {
+ { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 },
+ { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce },
{ 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
{ 0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002, 0x00003002 },
{ 0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009, 0x00008009 },
@@ -3409,8 +3412,6 @@ static const u32 ar9280Modes_original_tx_gain_9280_2[][6] = {
{ 0x00007840, 0x00392000, 0x00392000, 0x00392000, 0x00392000, 0x00392000 },
{ 0x00007820, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480 },
{ 0x00007844, 0x92592480, 0x92592480, 0x92592480, 0x92592480, 0x92592480 },
- { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 },
- { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce },
};
static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = {
@@ -5918,9 +5919,6 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = {
{ 0x000099ec, 0x0cc80caa },
{ 0x000099f0, 0x00000000 },
{ 0x000099fc, 0x00001042 },
- { 0x0000a1f4, 0x00fffeff },
- { 0x0000a1f8, 0x00f5f9ff },
- { 0x0000a1fc, 0xb79f6427 },
{ 0x0000a208, 0x803e4788 },
{ 0x0000a210, 0x4080a333 },
{ 0x0000a214, 0x40206c10 },
@@ -5980,7 +5978,7 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = {
{ 0x0000b3f4, 0x00000000 },
{ 0x0000a7d8, 0x000003f1 },
{ 0x00007800, 0x00000800 },
- { 0x00007804, 0x6c35ffc2 },
+ { 0x00007804, 0x6c35ffd2 },
{ 0x00007808, 0x6db6c000 },
{ 0x0000780c, 0x6db6cb30 },
{ 0x00007810, 0x6db6cb6c },
@@ -6000,7 +5998,7 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = {
{ 0x00007848, 0x934934a8 },
{ 0x00007850, 0x00000000 },
{ 0x00007854, 0x00000800 },
- { 0x00007858, 0x6c35ffc2 },
+ { 0x00007858, 0x6c35ffd2 },
{ 0x0000785c, 0x6db6c000 },
{ 0x00007860, 0x6db6cb30 },
{ 0x00007864, 0x6db6cb6c },
@@ -6027,6 +6025,22 @@ static const u_int32_t ar9287Common_9287_1_1[][2] = {
{ 0x000078b8, 0x2a850160 },
};
+/*
+ * For Japanese regulatory requirements, 2484 MHz requires the following three
+ * registers be programmed differently from the channel between 2412 and 2472 MHz.
+ */
+static const u_int32_t ar9287Common_normal_cck_fir_coeff_92871_1[][2] = {
+ { 0x0000a1f4, 0x00fffeff },
+ { 0x0000a1f8, 0x00f5f9ff },
+ { 0x0000a1fc, 0xb79f6427 },
+};
+
+static const u_int32_t ar9287Common_japan_2484_cck_fir_coeff_92871_1[][2] = {
+ { 0x0000a1f4, 0x00000000 },
+ { 0x0000a1f8, 0xefff0301 },
+ { 0x0000a1fc, 0xca9228ee },
+};
+
static const u_int32_t ar9287Modes_tx_gain_9287_1_1[][6] = {
/* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */
{ 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
@@ -6365,8 +6379,8 @@ static const u_int32_t ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = {
};
-/* AR9271 initialization values automaticaly created: 03/23/09 */
-static const u_int32_t ar9271Modes_9271_1_0[][6] = {
+/* AR9271 initialization values automaticaly created: 06/04/09 */
+static const u_int32_t ar9271Modes_9271[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
{ 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
{ 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 },
@@ -6376,8 +6390,8 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = {
{ 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 },
{ 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 },
{ 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 },
- { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
- { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
+ { 0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e },
+ { 0x00009828, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001 },
{ 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e },
{ 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 },
{ 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e },
@@ -6391,6 +6405,7 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = {
{ 0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 },
{ 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 },
{ 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 },
+ { 0x00009910, 0x30002310, 0x30002310, 0x30002310, 0x30002310, 0x30002310 },
{ 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 },
{ 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 },
{ 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d },
@@ -6401,7 +6416,7 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = {
{ 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 },
{ 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 },
{ 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 },
- { 0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329 },
+ { 0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f },
{ 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 },
{ 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 },
{ 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
@@ -6690,7 +6705,7 @@ static const u_int32_t ar9271Modes_9271_1_0[][6] = {
{ 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
};
-static const u_int32_t ar9271Common_9271_1_0[][2] = {
+static const u_int32_t ar9271Common_9271[][2] = {
{ 0x0000000c, 0x00000000 },
{ 0x00000030, 0x00020045 },
{ 0x00000034, 0x00000005 },
@@ -6786,7 +6801,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = {
{ 0x0000803c, 0x00000000 },
{ 0x00008048, 0x00000000 },
{ 0x00008054, 0x00000000 },
- { 0x00008058, 0x02000000 },
+ { 0x00008058, 0x00000000 },
{ 0x0000805c, 0x000fc78f },
{ 0x00008060, 0x0000000f },
{ 0x00008064, 0x00000000 },
@@ -6817,7 +6832,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = {
{ 0x00008110, 0x00000168 },
{ 0x00008118, 0x000100aa },
{ 0x0000811c, 0x00003210 },
- { 0x00008120, 0x08f04814 },
+ { 0x00008120, 0x08f04810 },
{ 0x00008124, 0x00000000 },
{ 0x00008128, 0x00000000 },
{ 0x0000812c, 0x00000000 },
@@ -6864,7 +6879,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = {
{ 0x00008258, 0x00000000 },
{ 0x0000825c, 0x400000ff },
{ 0x00008260, 0x00080922 },
- { 0x00008264, 0xa8a00010 },
+ { 0x00008264, 0x88a00010 },
{ 0x00008270, 0x00000000 },
{ 0x00008274, 0x40000000 },
{ 0x00008278, 0x003e4180 },
@@ -6896,7 +6911,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = {
{ 0x00007814, 0x924934a8 },
{ 0x0000781c, 0x00000000 },
{ 0x00007820, 0x00000c04 },
- { 0x00007824, 0x00d86bff },
+ { 0x00007824, 0x00d8abff },
{ 0x00007828, 0x66964300 },
{ 0x0000782c, 0x8db6d961 },
{ 0x00007830, 0x8db6d96c },
@@ -6930,7 +6945,6 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = {
{ 0x00009904, 0x00000000 },
{ 0x00009908, 0x00000000 },
{ 0x0000990c, 0x00000000 },
- { 0x00009910, 0x30002310 },
{ 0x0000991c, 0x10000fff },
{ 0x00009920, 0x04900000 },
{ 0x00009928, 0x00000001 },
@@ -6944,7 +6958,7 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = {
{ 0x00009954, 0x5f3ca3de },
{ 0x00009958, 0x0108ecff },
{ 0x00009968, 0x000003ce },
- { 0x00009970, 0x192bb515 },
+ { 0x00009970, 0x192bb514 },
{ 0x00009974, 0x00000000 },
{ 0x00009978, 0x00000001 },
{ 0x0000997c, 0x00000000 },
@@ -7031,3 +7045,8 @@ static const u_int32_t ar9271Common_9271_1_0[][2] = {
{ 0x0000d380, 0x7f3c7bba },
{ 0x0000d384, 0xf3307ff0 },
};
+
+static const u_int32_t ar9271Modes_9271_1_0_only[][6] = {
+ { 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 },
+ { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
+};
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 800bfab9463..71b84d91dcf 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -14,16 +14,16 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+#include "hw.h"
static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
struct ath9k_tx_queue_info *qi)
{
- DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
- "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
- ah->txok_interrupt_mask, ah->txerr_interrupt_mask,
- ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask,
- ah->txurn_interrupt_mask);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_INTERRUPT,
+ "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
+ ah->txok_interrupt_mask, ah->txerr_interrupt_mask,
+ ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask,
+ ah->txurn_interrupt_mask);
REG_WRITE(ah, AR_IMR_S0,
SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK)
@@ -39,17 +39,21 @@ u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q)
{
return REG_READ(ah, AR_QTXDP(q));
}
+EXPORT_SYMBOL(ath9k_hw_gettxbuf);
void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp)
{
REG_WRITE(ah, AR_QTXDP(q), txdp);
}
+EXPORT_SYMBOL(ath9k_hw_puttxbuf);
void ath9k_hw_txstart(struct ath_hw *ah, u32 q)
{
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Enable TXE on queue: %u\n", q);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_QUEUE,
+ "Enable TXE on queue: %u\n", q);
REG_WRITE(ah, AR_Q_TXE, 1 << q);
}
+EXPORT_SYMBOL(ath9k_hw_txstart);
u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q)
{
@@ -64,13 +68,39 @@ u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q)
return npend;
}
+EXPORT_SYMBOL(ath9k_hw_numtxpending);
+/**
+ * ath9k_hw_updatetxtriglevel - adjusts the frame trigger level
+ *
+ * @ah: atheros hardware struct
+ * @bIncTrigLevel: whether or not the frame trigger level should be updated
+ *
+ * The frame trigger level specifies the minimum number of bytes,
+ * in units of 64 bytes, that must be DMA'ed into the PCU TX FIFO
+ * before the PCU will initiate sending the frame on the air. This can
+ * mean we initiate transmit before a full frame is on the PCU TX FIFO.
+ * Resets to 0x1 (meaning 64 bytes or a full frame, whichever occurs
+ * first)
+ *
+ * Caution must be taken to ensure to set the frame trigger level based
+ * on the DMA request size. For example if the DMA request size is set to
+ * 128 bytes the trigger level cannot exceed 6 * 64 = 384. This is because
+ * there need to be enough space in the tx FIFO for the requested transfer
+ * size. Hence the tx FIFO will stop with 512 - 128 = 384 bytes. If we set
+ * the threshold to a value beyond 6, then the transmit will hang.
+ *
+ * Current dual stream devices have a PCU TX FIFO size of 8 KB.
+ * Current single stream devices have a PCU TX FIFO size of 4 KB, however,
+ * there is a hardware issue which forces us to use 2 KB instead so the
+ * frame trigger level must not exceed 2 KB for these chipsets.
+ */
bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
{
u32 txcfg, curLevel, newLevel;
enum ath9k_int omask;
- if (ah->tx_trig_level >= MAX_TX_FIFO_THRESHOLD)
+ if (ah->tx_trig_level >= ah->config.max_txtrig_level)
return false;
omask = ath9k_hw_set_interrupts(ah, ah->mask_reg & ~ATH9K_INT_GLOBAL);
@@ -79,7 +109,7 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
curLevel = MS(txcfg, AR_FTRIG);
newLevel = curLevel;
if (bIncTrigLevel) {
- if (curLevel < MAX_TX_FIFO_THRESHOLD)
+ if (curLevel < ah->config.max_txtrig_level)
newLevel++;
} else if (curLevel > MIN_TX_FIFO_THRESHOLD)
newLevel--;
@@ -93,27 +123,28 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
return newLevel != curLevel;
}
+EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel);
bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
{
#define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */
#define ATH9K_TIME_QUANTUM 100 /* usec */
-
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_tx_queue_info *qi;
u32 tsfLow, j, wait;
u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM;
if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, "
- "invalid queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, "
+ "invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, "
- "inactive queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, "
+ "inactive queue: %u\n", q);
return false;
}
@@ -126,9 +157,9 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
}
if (ath9k_hw_numtxpending(ah, q)) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "%s: Num of pending TX Frames %d on Q %d\n",
- __func__, ath9k_hw_numtxpending(ah, q), q);
+ ath_print(common, ATH_DBG_QUEUE,
+ "%s: Num of pending TX Frames %d on Q %d\n",
+ __func__, ath9k_hw_numtxpending(ah, q), q);
for (j = 0; j < 2; j++) {
tsfLow = REG_READ(ah, AR_TSF_L32);
@@ -142,9 +173,9 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10))
break;
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "TSF has moved while trying to set "
- "quiet time TSF: 0x%08x\n", tsfLow);
+ ath_print(common, ATH_DBG_QUEUE,
+ "TSF has moved while trying to set "
+ "quiet time TSF: 0x%08x\n", tsfLow);
}
REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
@@ -155,9 +186,9 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
wait = wait_time;
while (ath9k_hw_numtxpending(ah, q)) {
if ((--wait) == 0) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
- "Failed to stop TX DMA in 100 "
- "msec after killing last frame\n");
+ ath_print(common, ATH_DBG_QUEUE,
+ "Failed to stop TX DMA in 100 "
+ "msec after killing last frame\n");
break;
}
udelay(ATH9K_TIME_QUANTUM);
@@ -172,6 +203,7 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
#undef ATH9K_TX_STOP_DMA_TIMEOUT
#undef ATH9K_TIME_QUANTUM
}
+EXPORT_SYMBOL(ath9k_hw_stoptxdma);
void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 segLen, bool firstSeg,
@@ -198,6 +230,7 @@ void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds,
ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
}
+EXPORT_SYMBOL(ath9k_hw_filltxdesc);
void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds)
{
@@ -209,6 +242,7 @@ void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds)
ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
}
+EXPORT_SYMBOL(ath9k_hw_cleartxdesc);
int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds)
{
@@ -222,6 +256,8 @@ int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds)
ds->ds_txstat.ts_status = 0;
ds->ds_txstat.ts_flags = 0;
+ if (ads->ds_txstatus1 & AR_FrmXmitOK)
+ ds->ds_txstat.ts_status |= ATH9K_TX_ACKED;
if (ads->ds_txstatus1 & AR_ExcessiveRetries)
ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY;
if (ads->ds_txstatus1 & AR_Filtered)
@@ -284,6 +320,7 @@ int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds)
return 0;
}
+EXPORT_SYMBOL(ath9k_hw_txprocdesc);
void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 pktLen, enum ath9k_pkt_type type, u32 txPower,
@@ -319,6 +356,7 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
ads->ds_ctl11 = 0;
}
}
+EXPORT_SYMBOL(ath9k_hw_set11n_txdesc);
void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, struct ath_desc *ds,
struct ath_desc *lastds,
@@ -374,6 +412,7 @@ void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, struct ath_desc *ds,
last_ads->ds_ctl2 = ads->ds_ctl2;
last_ads->ds_ctl3 = ads->ds_ctl3;
}
+EXPORT_SYMBOL(ath9k_hw_set11n_ratescenario);
void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds,
u32 aggrLen)
@@ -384,6 +423,7 @@ void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds,
ads->ds_ctl6 &= ~AR_AggrLen;
ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen);
}
+EXPORT_SYMBOL(ath9k_hw_set11n_aggr_first);
void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds,
u32 numDelims)
@@ -398,6 +438,7 @@ void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds,
ctl6 |= SM(numDelims, AR_PadDelim);
ads->ds_ctl6 = ctl6;
}
+EXPORT_SYMBOL(ath9k_hw_set11n_aggr_middle);
void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds)
{
@@ -407,6 +448,7 @@ void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds)
ads->ds_ctl1 &= ~AR_MoreAggr;
ads->ds_ctl6 &= ~AR_PadDelim;
}
+EXPORT_SYMBOL(ath9k_hw_set11n_aggr_last);
void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds)
{
@@ -414,6 +456,7 @@ void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds)
ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr);
}
+EXPORT_SYMBOL(ath9k_hw_clr11n_aggr);
void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds,
u32 burstDuration)
@@ -423,6 +466,7 @@ void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds,
ads->ds_ctl2 &= ~AR_BurstDur;
ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur);
}
+EXPORT_SYMBOL(ath9k_hw_set11n_burstduration);
void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds,
u32 vmf)
@@ -440,28 +484,30 @@ void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs)
*txqs &= ah->intr_txqs;
ah->intr_txqs &= ~(*txqs);
}
+EXPORT_SYMBOL(ath9k_hw_gettxintrtxqs);
bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q,
const struct ath9k_tx_queue_info *qinfo)
{
u32 cw;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_tx_queue_info *qi;
if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, "
- "invalid queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Set TXQ properties, "
+ "invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, "
- "inactive queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Set TXQ properties, "
+ "inactive queue: %u\n", q);
return false;
}
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q);
qi->tqi_ver = qinfo->tqi_ver;
qi->tqi_subtype = qinfo->tqi_subtype;
@@ -510,23 +556,25 @@ bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q,
return true;
}
+EXPORT_SYMBOL(ath9k_hw_set_txq_props);
bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q,
struct ath9k_tx_queue_info *qinfo)
{
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_tx_queue_info *qi;
if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, "
- "invalid queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Get TXQ properties, "
+ "invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, "
- "inactive queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Get TXQ properties, "
+ "inactive queue: %u\n", q);
return false;
}
@@ -547,10 +595,12 @@ bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q,
return true;
}
+EXPORT_SYMBOL(ath9k_hw_get_txq_props);
int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
const struct ath9k_tx_queue_info *qinfo)
{
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_tx_queue_info *qi;
struct ath9k_hw_capabilities *pCap = &ah->caps;
int q;
@@ -574,23 +624,23 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
ATH9K_TX_QUEUE_INACTIVE)
break;
if (q == pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "No available TX queue\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "No available TX queue\n");
return -1;
}
break;
default:
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Invalid TX queue type: %u\n",
- type);
+ ath_print(common, ATH_DBG_FATAL,
+ "Invalid TX queue type: %u\n", type);
return -1;
}
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q);
qi = &ah->txq[q];
if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "TX queue: %u already active\n", q);
+ ath_print(common, ATH_DBG_FATAL,
+ "TX queue: %u already active\n", q);
return -1;
}
memset(qi, 0, sizeof(struct ath9k_tx_queue_info));
@@ -613,25 +663,27 @@ int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
return q;
}
+EXPORT_SYMBOL(ath9k_hw_setuptxqueue);
bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_tx_queue_info *qi;
if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, "
- "invalid queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Release TXQ, "
+ "invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, "
- "inactive queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Release TXQ, "
+ "inactive queue: %u\n", q);
return false;
}
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TX queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Release TX queue: %u\n", q);
qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE;
ah->txok_interrupt_mask &= ~(1 << q);
@@ -643,28 +695,30 @@ bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q)
return true;
}
+EXPORT_SYMBOL(ath9k_hw_releasetxqueue);
bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
struct ath9k_tx_queue_info *qi;
u32 cwMin, chanCwMin, value;
if (q >= pCap->total_queues) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, "
- "invalid queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Reset TXQ, "
+ "invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, "
- "inactive queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Reset TXQ, "
+ "inactive queue: %u\n", q);
return true;
}
- DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TX queue: %u\n", q);
+ ath_print(common, ATH_DBG_QUEUE, "Reset TX queue: %u\n", q);
if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) {
if (chan && IS_CHAN_B(chan))
@@ -799,6 +853,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
return true;
}
+EXPORT_SYMBOL(ath9k_hw_resettxqueue);
int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 pa, struct ath_desc *nds, u64 tsf)
@@ -880,6 +935,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
return 0;
}
+EXPORT_SYMBOL(ath9k_hw_rxprocdesc);
void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 size, u32 flags)
@@ -895,7 +951,15 @@ void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds,
if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
memset(&(ads->u), 0, sizeof(ads->u));
}
+EXPORT_SYMBOL(ath9k_hw_setuprxdesc);
+/*
+ * This can stop or re-enables RX.
+ *
+ * If bool is set this will kill any frame which is currently being
+ * transferred between the MAC and baseband and also prevent any new
+ * frames from getting started.
+ */
bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set)
{
u32 reg;
@@ -911,8 +975,9 @@ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set)
AR_DIAG_RX_ABORT));
reg = REG_READ(ah, AR_OBS_BUS_1);
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "RX failed to go idle in 10 ms RXSM=0x%x\n", reg);
+ ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
+ "RX failed to go idle in 10 ms RXSM=0x%x\n",
+ reg);
return false;
}
@@ -923,16 +988,19 @@ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set)
return true;
}
+EXPORT_SYMBOL(ath9k_hw_setrxabort);
void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp)
{
REG_WRITE(ah, AR_RXDP, rxdp);
}
+EXPORT_SYMBOL(ath9k_hw_putrxbuf);
void ath9k_hw_rxena(struct ath_hw *ah)
{
REG_WRITE(ah, AR_CR, AR_CR_RXE);
}
+EXPORT_SYMBOL(ath9k_hw_rxena);
void ath9k_hw_startpcureceive(struct ath_hw *ah)
{
@@ -942,6 +1010,7 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah)
REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
}
+EXPORT_SYMBOL(ath9k_hw_startpcureceive);
void ath9k_hw_stoppcurecv(struct ath_hw *ah)
{
@@ -949,12 +1018,13 @@ void ath9k_hw_stoppcurecv(struct ath_hw *ah)
ath9k_hw_disable_mib_counters(ah);
}
+EXPORT_SYMBOL(ath9k_hw_stoppcurecv);
bool ath9k_hw_stopdmarecv(struct ath_hw *ah)
{
#define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */
#define AH_RX_TIME_QUANTUM 100 /* usec */
-
+ struct ath_common *common = ath9k_hw_common(ah);
int i;
REG_WRITE(ah, AR_CR, AR_CR_RXD);
@@ -967,12 +1037,12 @@ bool ath9k_hw_stopdmarecv(struct ath_hw *ah)
}
if (i == 0) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "DMA failed to stop in %d ms "
- "AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
- AH_RX_STOP_DMA_TIMEOUT / 1000,
- REG_READ(ah, AR_CR),
- REG_READ(ah, AR_DIAG_SW));
+ ath_print(common, ATH_DBG_FATAL,
+ "DMA failed to stop in %d ms "
+ "AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
+ AH_RX_STOP_DMA_TIMEOUT / 1000,
+ REG_READ(ah, AR_CR),
+ REG_READ(ah, AR_DIAG_SW));
return false;
} else {
return true;
@@ -981,3 +1051,17 @@ bool ath9k_hw_stopdmarecv(struct ath_hw *ah)
#undef AH_RX_TIME_QUANTUM
#undef AH_RX_STOP_DMA_TIMEOUT
}
+EXPORT_SYMBOL(ath9k_hw_stopdmarecv);
+
+int ath9k_hw_beaconq_setup(struct ath_hw *ah)
+{
+ struct ath9k_tx_queue_info qi;
+
+ memset(&qi, 0, sizeof(qi));
+ qi.tqi_aifs = 1;
+ qi.tqi_cwmin = 0;
+ qi.tqi_cwmax = 0;
+ /* NB: don't enable any interrupts */
+ return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi);
+}
+EXPORT_SYMBOL(ath9k_hw_beaconq_setup);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index f56e77da6c3..0c87771383f 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -76,6 +76,7 @@
#define ATH9K_TXERR_FIFO 0x04
#define ATH9K_TXERR_XTXOP 0x08
#define ATH9K_TXERR_TIMER_EXPIRED 0x10
+#define ATH9K_TX_ACKED 0x20
#define ATH9K_TX_BA 0x01
#define ATH9K_TX_PWRMGMT 0x02
@@ -85,9 +86,15 @@
#define ATH9K_TX_SW_ABORTED 0x40
#define ATH9K_TX_SW_FILTERED 0x80
+/* 64 bytes */
#define MIN_TX_FIFO_THRESHOLD 0x1
+
+/*
+ * Single stream device AR9285 and AR9271 require 2 KB
+ * to work around a hardware issue, all other devices
+ * have can use the max 4 KB limit.
+ */
#define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1)
-#define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD
struct ath_tx_status {
u32 ts_tstamp;
@@ -380,6 +387,11 @@ struct ar5416_desc {
#define AR_TxBaStatus 0x40000000
#define AR_TxStatusRsvd01 0x80000000
+/*
+ * AR_FrmXmitOK - Frame transmission success flag. If set, the frame was
+ * transmitted successfully. If clear, no ACK or BA was received to indicate
+ * successful transmission when we were expecting an ACK or BA.
+ */
#define AR_FrmXmitOK 0x00000001
#define AR_ExcessiveRetries 0x00000002
#define AR_FIFOUnderrun 0x00000004
@@ -614,19 +626,8 @@ enum ath9k_cipher {
ATH9K_CIPHER_MIC = 127
};
-enum ath9k_ht_macmode {
- ATH9K_HT_MACMODE_20 = 0,
- ATH9K_HT_MACMODE_2040 = 1,
-};
-
-enum ath9k_ht_extprotspacing {
- ATH9K_HT_EXTPROTSPACING_20 = 0,
- ATH9K_HT_EXTPROTSPACING_25 = 1,
-};
-
struct ath_hw;
struct ath9k_channel;
-struct ath_rate_table;
u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q);
void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp);
@@ -677,5 +678,6 @@ void ath9k_hw_rxena(struct ath_hw *ah);
void ath9k_hw_startpcureceive(struct ath_hw *ah);
void ath9k_hw_stoppcurecv(struct ath_hw *ah);
bool ath9k_hw_stopdmarecv(struct ath_hw *ah);
+int ath9k_hw_beaconq_setup(struct ath_hw *ah);
#endif /* MAC_H */
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 43d2be9867f..c4874345251 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -16,6 +16,7 @@
#include <linux/nl80211.h>
#include "ath9k.h"
+#include "btcoex.h"
static char *dev_info = "ath9k";
@@ -28,6 +29,10 @@ static int modparam_nohwcrypt;
module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
+static unsigned int ath9k_debug = ATH_DBG_DEFAULT;
+module_param_named(debug, ath9k_debug, uint, 0);
+MODULE_PARM_DESC(debug, "Debugging mask");
+
/* We use the hw_value as an index into our private channel structure */
#define CHAN2G(_freq, _idx) { \
@@ -99,37 +104,55 @@ static struct ieee80211_channel ath9k_5ghz_chantable[] = {
CHAN5G(5825, 37), /* Channel 165 */
};
+/* Atheros hardware rate code addition for short premble */
+#define SHPCHECK(__hw_rate, __flags) \
+ ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0)
+
+#define RATE(_bitrate, _hw_rate, _flags) { \
+ .bitrate = (_bitrate), \
+ .flags = (_flags), \
+ .hw_value = (_hw_rate), \
+ .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \
+}
+
+static struct ieee80211_rate ath9k_legacy_rates[] = {
+ RATE(10, 0x1b, 0),
+ RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0x0b, 0),
+ RATE(90, 0x0f, 0),
+ RATE(120, 0x0a, 0),
+ RATE(180, 0x0e, 0),
+ RATE(240, 0x09, 0),
+ RATE(360, 0x0d, 0),
+ RATE(480, 0x08, 0),
+ RATE(540, 0x0c, 0),
+};
+
static void ath_cache_conf_rate(struct ath_softc *sc,
struct ieee80211_conf *conf)
{
switch (conf->channel->band) {
case IEEE80211_BAND_2GHZ:
if (conf_is_ht20(conf))
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
+ sc->cur_rate_mode = ATH9K_MODE_11NG_HT20;
else if (conf_is_ht40_minus(conf))
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
+ sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS;
else if (conf_is_ht40_plus(conf))
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
+ sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS;
else
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11G];
+ sc->cur_rate_mode = ATH9K_MODE_11G;
break;
case IEEE80211_BAND_5GHZ:
if (conf_is_ht20(conf))
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
+ sc->cur_rate_mode = ATH9K_MODE_11NA_HT20;
else if (conf_is_ht40_minus(conf))
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
+ sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS;
else if (conf_is_ht40_plus(conf))
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
+ sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS;
else
- sc->cur_rate_table =
- sc->hw_rate_table[ATH9K_MODE_11A];
+ sc->cur_rate_mode = ATH9K_MODE_11A;
break;
default:
BUG_ON(1);
@@ -185,50 +208,6 @@ static u8 parse_mpdudensity(u8 mpdudensity)
}
}
-static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
-{
- const struct ath_rate_table *rate_table = NULL;
- struct ieee80211_supported_band *sband;
- struct ieee80211_rate *rate;
- int i, maxrates;
-
- switch (band) {
- case IEEE80211_BAND_2GHZ:
- rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
- break;
- case IEEE80211_BAND_5GHZ:
- rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
- break;
- default:
- break;
- }
-
- if (rate_table == NULL)
- return;
-
- sband = &sc->sbands[band];
- rate = sc->rates[band];
-
- if (rate_table->rate_cnt > ATH_RATE_MAX)
- maxrates = ATH_RATE_MAX;
- else
- maxrates = rate_table->rate_cnt;
-
- for (i = 0; i < maxrates; i++) {
- rate[i].bitrate = rate_table->info[i].ratekbps / 100;
- rate[i].hw_value = rate_table->info[i].ratecode;
- if (rate_table->info[i].short_preamble) {
- rate[i].hw_value_short = rate_table->info[i].ratecode |
- rate_table->info[i].short_preamble;
- rate[i].flags = IEEE80211_RATE_SHORT_PREAMBLE;
- }
- sband->n_bitrates++;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "Rate: %2dMbps, ratecode: %2d\n",
- rate[i].bitrate / 10, rate[i].hw_value);
- }
-}
-
static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
struct ieee80211_hw *hw)
{
@@ -242,6 +221,51 @@ static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
return channel;
}
+static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
+ ret = ath9k_hw_setpower(sc->sc_ah, mode);
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+
+ return ret;
+}
+
+void ath9k_ps_wakeup(struct ath_softc *sc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
+ if (++sc->ps_usecount != 1)
+ goto unlock;
+
+ ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
+
+ unlock:
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+}
+
+void ath9k_ps_restore(struct ath_softc *sc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
+ if (--sc->ps_usecount != 0)
+ goto unlock;
+
+ if (sc->ps_enabled &&
+ !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
+ SC_OP_WAIT_FOR_CAB |
+ SC_OP_WAIT_FOR_PSPOLL_DATA |
+ SC_OP_WAIT_FOR_TX_ACK)))
+ ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
+
+ unlock:
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+}
+
/*
* Set/change channels. If the channel is really being changed, it's done
* by reseting the chip. To accomplish this we must first cleanup any pending
@@ -251,6 +275,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
struct ath9k_channel *hchan)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_conf *conf = &common->hw->conf;
bool fastcc = true, stopped;
struct ieee80211_channel *channel = hw->conf.channel;
int r;
@@ -280,19 +306,19 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
fastcc = false;
- DPRINTF(sc, ATH_DBG_CONFIG,
- "(%u MHz) -> (%u MHz), chanwidth: %d\n",
- sc->sc_ah->curchan->channel,
- channel->center_freq, sc->tx_chan_width);
+ ath_print(common, ATH_DBG_CONFIG,
+ "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n",
+ sc->sc_ah->curchan->channel,
+ channel->center_freq, conf_is_ht40(conf));
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, hchan, fastcc);
if (r) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to reset channel (%u Mhz) "
- "reset status %d\n",
- channel->center_freq, r);
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to reset channel (%u Mhz) "
+ "reset status %d\n",
+ channel->center_freq, r);
spin_unlock_bh(&sc->sc_resetlock);
goto ps_restore;
}
@@ -301,8 +327,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
sc->sc_flags &= ~SC_OP_FULL_RESET;
if (ath_startrecv(sc) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to restart recv logic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to restart recv logic\n");
r = -EIO;
goto ps_restore;
}
@@ -327,6 +353,7 @@ static void ath_ani_calibrate(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
bool longcal = false;
bool shortcal = false;
bool aniflag = false;
@@ -351,33 +378,34 @@ static void ath_ani_calibrate(unsigned long data)
ath9k_ps_wakeup(sc);
/* Long calibration runs independently of short calibration. */
- if ((timestamp - sc->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
+ if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
longcal = true;
- DPRINTF(sc, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
- sc->ani.longcal_timer = timestamp;
+ ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
+ common->ani.longcal_timer = timestamp;
}
/* Short calibration applies only while caldone is false */
- if (!sc->ani.caldone) {
- if ((timestamp - sc->ani.shortcal_timer) >= short_cal_interval) {
+ if (!common->ani.caldone) {
+ if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
shortcal = true;
- DPRINTF(sc, ATH_DBG_ANI, "shortcal @%lu\n", jiffies);
- sc->ani.shortcal_timer = timestamp;
- sc->ani.resetcal_timer = timestamp;
+ ath_print(common, ATH_DBG_ANI,
+ "shortcal @%lu\n", jiffies);
+ common->ani.shortcal_timer = timestamp;
+ common->ani.resetcal_timer = timestamp;
}
} else {
- if ((timestamp - sc->ani.resetcal_timer) >=
+ if ((timestamp - common->ani.resetcal_timer) >=
ATH_RESTART_CALINTERVAL) {
- sc->ani.caldone = ath9k_hw_reset_calvalid(ah);
- if (sc->ani.caldone)
- sc->ani.resetcal_timer = timestamp;
+ common->ani.caldone = ath9k_hw_reset_calvalid(ah);
+ if (common->ani.caldone)
+ common->ani.resetcal_timer = timestamp;
}
}
/* Verify whether we must check ANI */
- if ((timestamp - sc->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
+ if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
aniflag = true;
- sc->ani.checkani_timer = timestamp;
+ common->ani.checkani_timer = timestamp;
}
/* Skip all processing if there's nothing to do. */
@@ -388,16 +416,21 @@ static void ath_ani_calibrate(unsigned long data)
/* Perform calibration if necessary */
if (longcal || shortcal) {
- sc->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan,
- sc->rx_chainmask, longcal);
+ common->ani.caldone =
+ ath9k_hw_calibrate(ah,
+ ah->curchan,
+ common->rx_chainmask,
+ longcal);
if (longcal)
- sc->ani.noise_floor = ath9k_hw_getchan_noise(ah,
+ common->ani.noise_floor = ath9k_hw_getchan_noise(ah,
ah->curchan);
- DPRINTF(sc, ATH_DBG_ANI," calibrate chan %u/%x nf: %d\n",
- ah->curchan->channel, ah->curchan->channelFlags,
- sc->ani.noise_floor);
+ ath_print(common, ATH_DBG_ANI,
+ " calibrate chan %u/%x nf: %d\n",
+ ah->curchan->channel,
+ ah->curchan->channelFlags,
+ common->ani.noise_floor);
}
}
@@ -413,21 +446,21 @@ set_timer:
cal_interval = ATH_LONG_CALINTERVAL;
if (sc->sc_ah->config.enable_ani)
cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
- if (!sc->ani.caldone)
+ if (!common->ani.caldone)
cal_interval = min(cal_interval, (u32)short_cal_interval);
- mod_timer(&sc->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
+ mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
}
-static void ath_start_ani(struct ath_softc *sc)
+static void ath_start_ani(struct ath_common *common)
{
unsigned long timestamp = jiffies_to_msecs(jiffies);
- sc->ani.longcal_timer = timestamp;
- sc->ani.shortcal_timer = timestamp;
- sc->ani.checkani_timer = timestamp;
+ common->ani.longcal_timer = timestamp;
+ common->ani.shortcal_timer = timestamp;
+ common->ani.checkani_timer = timestamp;
- mod_timer(&sc->ani.timer,
+ mod_timer(&common->ani.timer,
jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
}
@@ -439,17 +472,22 @@ static void ath_start_ani(struct ath_softc *sc)
*/
void ath_update_chainmask(struct ath_softc *sc, int is_ht)
{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+
if ((sc->sc_flags & SC_OP_SCANNING) || is_ht ||
- (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE)) {
- sc->tx_chainmask = sc->sc_ah->caps.tx_chainmask;
- sc->rx_chainmask = sc->sc_ah->caps.rx_chainmask;
+ (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE)) {
+ common->tx_chainmask = ah->caps.tx_chainmask;
+ common->rx_chainmask = ah->caps.rx_chainmask;
} else {
- sc->tx_chainmask = 1;
- sc->rx_chainmask = 1;
+ common->tx_chainmask = 1;
+ common->rx_chainmask = 1;
}
- DPRINTF(sc, ATH_DBG_CONFIG, "tx chmask: %d, rx chmask: %d\n",
- sc->tx_chainmask, sc->rx_chainmask);
+ ath_print(common, ATH_DBG_CONFIG,
+ "tx chmask: %d, rx chmask: %d\n",
+ common->tx_chainmask,
+ common->rx_chainmask);
}
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
@@ -478,6 +516,9 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
static void ath9k_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+
u32 status = sc->intrstatus;
ath9k_ps_wakeup(sc);
@@ -502,16 +543,17 @@ static void ath9k_tasklet(unsigned long data)
* TSF sync does not look correct; remain awake to sync with
* the next Beacon.
*/
- DPRINTF(sc, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n");
+ ath_print(common, ATH_DBG_PS,
+ "TSFOOR - Sync with next Beacon\n");
sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC;
}
- if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+ if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
if (status & ATH9K_INT_GENTIMER)
ath_gen_timer_isr(sc->sc_ah);
/* re-enable hardware interrupt */
- ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
+ ath9k_hw_set_interrupts(ah, sc->imask);
ath9k_ps_restore(sc);
}
@@ -602,7 +644,7 @@ irqreturn_t ath_isr(int irq, void *dev)
if (status & ATH9K_INT_TIM_TIMER) {
/* Clear RxAbort bit so that we can
* receive frames */
- ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
+ ath9k_setpower(sc, ATH9K_PM_AWAKE);
ath9k_hw_setrxabort(sc->sc_ah, 0);
sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
}
@@ -664,10 +706,11 @@ static u32 ath_get_extchanmode(struct ath_softc *sc,
return chanmode;
}
-static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key,
+static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
struct ath9k_keyval *hk, const u8 *addr,
bool authenticator)
{
+ struct ath_hw *ah = common->ah;
const u8 *key_rxmic;
const u8 *key_txmic;
@@ -687,42 +730,42 @@ static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key,
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
}
- return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr);
+ return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
}
- if (!sc->splitmic) {
+ if (!common->splitmic) {
/* TX and RX keys share the same key cache entry. */
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
- return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr);
+ return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
}
/* Separate key cache entries for TX and RX */
/* TX key goes at first index, RX key at +32. */
memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
- if (!ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, NULL)) {
+ if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) {
/* TX MIC entry failed. No need to proceed further */
- DPRINTF(sc, ATH_DBG_FATAL,
- "Setting TX MIC Key Failed\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Setting TX MIC Key Failed\n");
return 0;
}
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
/* XXX delete tx key on failure? */
- return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix + 32, hk, addr);
+ return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr);
}
-static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc)
+static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
{
int i;
- for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) {
- if (test_bit(i, sc->keymap) ||
- test_bit(i + 64, sc->keymap))
+ for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+ if (test_bit(i, common->keymap) ||
+ test_bit(i + 64, common->keymap))
continue; /* At least one part of TKIP key allocated */
- if (sc->splitmic &&
- (test_bit(i + 32, sc->keymap) ||
- test_bit(i + 64 + 32, sc->keymap)))
+ if (common->splitmic &&
+ (test_bit(i + 32, common->keymap) ||
+ test_bit(i + 64 + 32, common->keymap)))
continue; /* At least one part of TKIP key allocated */
/* Found a free slot for a TKIP key */
@@ -731,60 +774,60 @@ static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc)
return -1;
}
-static int ath_reserve_key_cache_slot(struct ath_softc *sc)
+static int ath_reserve_key_cache_slot(struct ath_common *common)
{
int i;
/* First, try to find slots that would not be available for TKIP. */
- if (sc->splitmic) {
- for (i = IEEE80211_WEP_NKID; i < sc->keymax / 4; i++) {
- if (!test_bit(i, sc->keymap) &&
- (test_bit(i + 32, sc->keymap) ||
- test_bit(i + 64, sc->keymap) ||
- test_bit(i + 64 + 32, sc->keymap)))
+ if (common->splitmic) {
+ for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
+ if (!test_bit(i, common->keymap) &&
+ (test_bit(i + 32, common->keymap) ||
+ test_bit(i + 64, common->keymap) ||
+ test_bit(i + 64 + 32, common->keymap)))
return i;
- if (!test_bit(i + 32, sc->keymap) &&
- (test_bit(i, sc->keymap) ||
- test_bit(i + 64, sc->keymap) ||
- test_bit(i + 64 + 32, sc->keymap)))
+ if (!test_bit(i + 32, common->keymap) &&
+ (test_bit(i, common->keymap) ||
+ test_bit(i + 64, common->keymap) ||
+ test_bit(i + 64 + 32, common->keymap)))
return i + 32;
- if (!test_bit(i + 64, sc->keymap) &&
- (test_bit(i , sc->keymap) ||
- test_bit(i + 32, sc->keymap) ||
- test_bit(i + 64 + 32, sc->keymap)))
+ if (!test_bit(i + 64, common->keymap) &&
+ (test_bit(i , common->keymap) ||
+ test_bit(i + 32, common->keymap) ||
+ test_bit(i + 64 + 32, common->keymap)))
return i + 64;
- if (!test_bit(i + 64 + 32, sc->keymap) &&
- (test_bit(i, sc->keymap) ||
- test_bit(i + 32, sc->keymap) ||
- test_bit(i + 64, sc->keymap)))
+ if (!test_bit(i + 64 + 32, common->keymap) &&
+ (test_bit(i, common->keymap) ||
+ test_bit(i + 32, common->keymap) ||
+ test_bit(i + 64, common->keymap)))
return i + 64 + 32;
}
} else {
- for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) {
- if (!test_bit(i, sc->keymap) &&
- test_bit(i + 64, sc->keymap))
+ for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
+ if (!test_bit(i, common->keymap) &&
+ test_bit(i + 64, common->keymap))
return i;
- if (test_bit(i, sc->keymap) &&
- !test_bit(i + 64, sc->keymap))
+ if (test_bit(i, common->keymap) &&
+ !test_bit(i + 64, common->keymap))
return i + 64;
}
}
/* No partially used TKIP slots, pick any available slot */
- for (i = IEEE80211_WEP_NKID; i < sc->keymax; i++) {
+ for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
/* Do not allow slots that could be needed for TKIP group keys
* to be used. This limitation could be removed if we know that
* TKIP will not be used. */
if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
continue;
- if (sc->splitmic) {
+ if (common->splitmic) {
if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
continue;
if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
continue;
}
- if (!test_bit(i, sc->keymap))
+ if (!test_bit(i, common->keymap))
return i; /* Found a free slot for a key */
}
@@ -792,11 +835,12 @@ static int ath_reserve_key_cache_slot(struct ath_softc *sc)
return -1;
}
-static int ath_key_config(struct ath_softc *sc,
+static int ath_key_config(struct ath_common *common,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
+ struct ath_hw *ah = common->ah;
struct ath9k_keyval hk;
const u8 *mac = NULL;
int ret = 0;
@@ -842,54 +886,57 @@ static int ath_key_config(struct ath_softc *sc,
mac = sta->addr;
if (key->alg == ALG_TKIP)
- idx = ath_reserve_key_cache_slot_tkip(sc);
+ idx = ath_reserve_key_cache_slot_tkip(common);
else
- idx = ath_reserve_key_cache_slot(sc);
+ idx = ath_reserve_key_cache_slot(common);
if (idx < 0)
return -ENOSPC; /* no free key cache entries */
}
if (key->alg == ALG_TKIP)
- ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac,
+ ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
vif->type == NL80211_IFTYPE_AP);
else
- ret = ath9k_hw_set_keycache_entry(sc->sc_ah, idx, &hk, mac);
+ ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac);
if (!ret)
return -EIO;
- set_bit(idx, sc->keymap);
+ set_bit(idx, common->keymap);
if (key->alg == ALG_TKIP) {
- set_bit(idx + 64, sc->keymap);
- if (sc->splitmic) {
- set_bit(idx + 32, sc->keymap);
- set_bit(idx + 64 + 32, sc->keymap);
+ set_bit(idx + 64, common->keymap);
+ if (common->splitmic) {
+ set_bit(idx + 32, common->keymap);
+ set_bit(idx + 64 + 32, common->keymap);
}
}
return idx;
}
-static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key)
+static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key)
{
- ath9k_hw_keyreset(sc->sc_ah, key->hw_key_idx);
+ struct ath_hw *ah = common->ah;
+
+ ath9k_hw_keyreset(ah, key->hw_key_idx);
if (key->hw_key_idx < IEEE80211_WEP_NKID)
return;
- clear_bit(key->hw_key_idx, sc->keymap);
+ clear_bit(key->hw_key_idx, common->keymap);
if (key->alg != ALG_TKIP)
return;
- clear_bit(key->hw_key_idx + 64, sc->keymap);
- if (sc->splitmic) {
- clear_bit(key->hw_key_idx + 32, sc->keymap);
- clear_bit(key->hw_key_idx + 64 + 32, sc->keymap);
+ clear_bit(key->hw_key_idx + 64, common->keymap);
+ if (common->splitmic) {
+ clear_bit(key->hw_key_idx + 32, common->keymap);
+ clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
}
}
static void setup_ht_cap(struct ath_softc *sc,
struct ieee80211_sta_ht_cap *ht_info)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u8 tx_streams, rx_streams;
ht_info->ht_supported = true;
@@ -903,12 +950,15 @@ static void setup_ht_cap(struct ath_softc *sc,
/* set up supported mcs set */
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
- tx_streams = !(sc->tx_chainmask & (sc->tx_chainmask - 1)) ? 1 : 2;
- rx_streams = !(sc->rx_chainmask & (sc->rx_chainmask - 1)) ? 1 : 2;
+ tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ?
+ 1 : 2;
+ rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ?
+ 1 : 2;
if (tx_streams != rx_streams) {
- DPRINTF(sc, ATH_DBG_CONFIG, "TX streams %d, RX streams: %d\n",
- tx_streams, rx_streams);
+ ath_print(common, ATH_DBG_CONFIG,
+ "TX streams %d, RX streams: %d\n",
+ tx_streams, rx_streams);
ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
ht_info->mcs.tx_params |= ((tx_streams - 1) <<
IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
@@ -925,14 +975,17 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf)
{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
if (bss_conf->assoc) {
- DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n",
- bss_conf->aid, sc->curbssid);
+ ath_print(common, ATH_DBG_CONFIG,
+ "Bss Info ASSOC %d, bssid: %pM\n",
+ bss_conf->aid, common->curbssid);
/* New association, store aid */
- sc->curaid = bss_conf->aid;
- ath9k_hw_write_associd(sc);
+ common->curaid = bss_conf->aid;
+ ath9k_hw_write_associd(ah);
/*
* Request a re-configuration of Beacon related timers
@@ -947,12 +1000,12 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
/* Reset rssi stats */
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
- ath_start_ani(sc);
+ ath_start_ani(common);
} else {
- DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISASSOC\n");
- sc->curaid = 0;
+ ath_print(common, ATH_DBG_CONFIG, "Bss Info DISASSOC\n");
+ common->curaid = 0;
/* Stop ANI */
- del_timer_sync(&sc->ani.timer);
+ del_timer_sync(&common->ani.timer);
}
}
@@ -1042,8 +1095,8 @@ static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev);
if (ret)
- DPRINTF(sc, ATH_DBG_FATAL,
- "Failed to register led:%s", led->name);
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "Failed to register led:%s", led->name);
else
led->registered = 1;
return ret;
@@ -1124,10 +1177,11 @@ fail:
ath_deinit_leds(sc);
}
-void ath_radio_enable(struct ath_softc *sc)
+void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
{
struct ath_hw *ah = sc->sc_ah;
- struct ieee80211_channel *channel = sc->hw->conf.channel;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_channel *channel = hw->conf.channel;
int r;
ath9k_ps_wakeup(sc);
@@ -1139,17 +1193,17 @@ void ath_radio_enable(struct ath_softc *sc)
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, ah->curchan, false);
if (r) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to reset channel %u (%uMhz) ",
- "reset status %d\n",
- channel->center_freq, r);
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to reset channel %u (%uMhz) ",
+ "reset status %d\n",
+ channel->center_freq, r);
}
spin_unlock_bh(&sc->sc_resetlock);
ath_update_txpow(sc);
if (ath_startrecv(sc) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to restart recv logic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to restart recv logic\n");
return;
}
@@ -1164,18 +1218,18 @@ void ath_radio_enable(struct ath_softc *sc)
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(ah, ah->led_pin, 0);
- ieee80211_wake_queues(sc->hw);
+ ieee80211_wake_queues(hw);
ath9k_ps_restore(sc);
}
-void ath_radio_disable(struct ath_softc *sc)
+void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
{
struct ath_hw *ah = sc->sc_ah;
- struct ieee80211_channel *channel = sc->hw->conf.channel;
+ struct ieee80211_channel *channel = hw->conf.channel;
int r;
ath9k_ps_wakeup(sc);
- ieee80211_stop_queues(sc->hw);
+ ieee80211_stop_queues(hw);
/* Disable LED */
ath9k_hw_set_gpio(ah, ah->led_pin, 1);
@@ -1189,22 +1243,22 @@ void ath_radio_disable(struct ath_softc *sc)
ath_flushrecv(sc); /* flush recv queue */
if (!ah->curchan)
- ah->curchan = ath_get_curchannel(sc, sc->hw);
+ ah->curchan = ath_get_curchannel(sc, hw);
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, ah->curchan, false);
if (r) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to reset channel %u (%uMhz) "
- "reset status %d\n",
- channel->center_freq, r);
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "Unable to reset channel %u (%uMhz) "
+ "reset status %d\n",
+ channel->center_freq, r);
}
spin_unlock_bh(&sc->sc_resetlock);
ath9k_hw_phy_disable(ah);
ath9k_hw_configpcipowersave(ah, 1, 1);
ath9k_ps_restore(sc);
- ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
+ ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
}
/*******************/
@@ -1236,23 +1290,26 @@ static void ath_start_rfkill_poll(struct ath_softc *sc)
wiphy_rfkill_start_polling(sc->hw->wiphy);
}
-void ath_cleanup(struct ath_softc *sc)
+static void ath9k_uninit_hw(struct ath_softc *sc)
{
- ath_detach(sc);
- free_irq(sc->irq, sc);
- ath_bus_cleanup(sc);
- kfree(sc->sec_wiphy);
- ieee80211_free_hw(sc->hw);
+ struct ath_hw *ah = sc->sc_ah;
+
+ BUG_ON(!ah);
+
+ ath9k_exit_debug(ah);
+ ath9k_hw_detach(ah);
+ sc->sc_ah = NULL;
}
-void ath_detach(struct ath_softc *sc)
+static void ath_clean_core(struct ath_softc *sc)
{
struct ieee80211_hw *hw = sc->hw;
+ struct ath_hw *ah = sc->sc_ah;
int i = 0;
ath9k_ps_wakeup(sc);
- DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n");
+ dev_dbg(sc->dev, "Detach ATH hw\n");
ath_deinit_leds(sc);
wiphy_rfkill_stop_polling(sc->hw->wiphy);
@@ -1273,20 +1330,36 @@ void ath_detach(struct ath_softc *sc)
tasklet_kill(&sc->bcon_tasklet);
if (!(sc->sc_flags & SC_OP_INVALID))
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
+ ath9k_setpower(sc, ATH9K_PM_AWAKE);
/* cleanup tx queues */
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
- if ((sc->btcoex_info.no_stomp_timer) &&
- sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
- ath_gen_timer_free(sc->sc_ah, sc->btcoex_info.no_stomp_timer);
+ if ((sc->btcoex.no_stomp_timer) &&
+ ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
+ ath_gen_timer_free(ah, sc->btcoex.no_stomp_timer);
+}
- ath9k_hw_detach(sc->sc_ah);
- sc->sc_ah = NULL;
- ath9k_exit_debug(sc);
+void ath_detach(struct ath_softc *sc)
+{
+ ath_clean_core(sc);
+ ath9k_uninit_hw(sc);
+}
+
+void ath_cleanup(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ ath_clean_core(sc);
+ free_irq(sc->irq, sc);
+ ath_bus_cleanup(common);
+ kfree(sc->sec_wiphy);
+ ieee80211_free_hw(sc->hw);
+
+ ath9k_uninit_hw(sc);
}
static int ath9k_reg_notifier(struct wiphy *wiphy,
@@ -1295,29 +1368,245 @@ static int ath9k_reg_notifier(struct wiphy *wiphy,
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
- struct ath_regulatory *reg = &sc->common.regulatory;
+ struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah);
return ath_reg_notifier_apply(wiphy, request, reg);
}
/*
+ * Detects if there is any priority bt traffic
+ */
+static void ath_detect_bt_priority(struct ath_softc *sc)
+{
+ struct ath_btcoex *btcoex = &sc->btcoex;
+ struct ath_hw *ah = sc->sc_ah;
+
+ if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
+ btcoex->bt_priority_cnt++;
+
+ if (time_after(jiffies, btcoex->bt_priority_time +
+ msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
+ if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
+ "BT priority traffic detected");
+ sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
+ } else {
+ sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
+ }
+
+ btcoex->bt_priority_cnt = 0;
+ btcoex->bt_priority_time = jiffies;
+ }
+}
+
+/*
+ * Configures appropriate weight based on stomp type.
+ */
+static void ath9k_btcoex_bt_stomp(struct ath_softc *sc,
+ enum ath_stomp_type stomp_type)
+{
+ struct ath_hw *ah = sc->sc_ah;
+
+ switch (stomp_type) {
+ case ATH_BTCOEX_STOMP_ALL:
+ ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
+ AR_STOMP_ALL_WLAN_WGHT);
+ break;
+ case ATH_BTCOEX_STOMP_LOW:
+ ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
+ AR_STOMP_LOW_WLAN_WGHT);
+ break;
+ case ATH_BTCOEX_STOMP_NONE:
+ ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
+ AR_STOMP_NONE_WLAN_WGHT);
+ break;
+ default:
+ ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
+ "Invalid Stomptype\n");
+ break;
+ }
+
+ ath9k_hw_btcoex_enable(ah);
+}
+
+static void ath9k_gen_timer_start(struct ath_hw *ah,
+ struct ath_gen_timer *timer,
+ u32 timer_next,
+ u32 timer_period)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+
+ ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);
+
+ if ((sc->imask & ATH9K_INT_GENTIMER) == 0) {
+ ath9k_hw_set_interrupts(ah, 0);
+ sc->imask |= ATH9K_INT_GENTIMER;
+ ath9k_hw_set_interrupts(ah, sc->imask);
+ }
+}
+
+static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+ struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
+
+ ath9k_hw_gen_timer_stop(ah, timer);
+
+ /* if no timer is enabled, turn off interrupt mask */
+ if (timer_table->timer_mask.val == 0) {
+ ath9k_hw_set_interrupts(ah, 0);
+ sc->imask &= ~ATH9K_INT_GENTIMER;
+ ath9k_hw_set_interrupts(ah, sc->imask);
+ }
+}
+
+/*
+ * This is the master bt coex timer which runs for every
+ * 45ms, bt traffic will be given priority during 55% of this
+ * period while wlan gets remaining 45%
+ */
+static void ath_btcoex_period_timer(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *) data;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_btcoex *btcoex = &sc->btcoex;
+
+ ath_detect_bt_priority(sc);
+
+ spin_lock_bh(&btcoex->btcoex_lock);
+
+ ath9k_btcoex_bt_stomp(sc, btcoex->bt_stomp_type);
+
+ spin_unlock_bh(&btcoex->btcoex_lock);
+
+ if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
+ if (btcoex->hw_timer_enabled)
+ ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
+
+ ath9k_gen_timer_start(ah,
+ btcoex->no_stomp_timer,
+ (ath9k_hw_gettsf32(ah) +
+ btcoex->btcoex_no_stomp),
+ btcoex->btcoex_no_stomp * 10);
+ btcoex->hw_timer_enabled = true;
+ }
+
+ mod_timer(&btcoex->period_timer, jiffies +
+ msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
+}
+
+/*
+ * Generic tsf based hw timer which configures weight
+ * registers to time slice between wlan and bt traffic
+ */
+static void ath_btcoex_no_stomp_timer(void *arg)
+{
+ struct ath_softc *sc = (struct ath_softc *)arg;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_btcoex *btcoex = &sc->btcoex;
+
+ ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
+ "no stomp timer running \n");
+
+ spin_lock_bh(&btcoex->btcoex_lock);
+
+ if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW)
+ ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE);
+ else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
+ ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW);
+
+ spin_unlock_bh(&btcoex->btcoex_lock);
+}
+
+static int ath_init_btcoex_timer(struct ath_softc *sc)
+{
+ struct ath_btcoex *btcoex = &sc->btcoex;
+
+ btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
+ btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
+ btcoex->btcoex_period / 100;
+
+ setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
+ (unsigned long) sc);
+
+ spin_lock_init(&btcoex->btcoex_lock);
+
+ btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
+ ath_btcoex_no_stomp_timer,
+ ath_btcoex_no_stomp_timer,
+ (void *) sc, AR_FIRST_NDP_TIMER);
+
+ if (!btcoex->no_stomp_timer)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * Read and write, they both share the same lock. We do this to serialize
+ * reads and writes on Atheros 802.11n PCI devices only. This is required
+ * as the FIFO on these devices can only accept sanely 2 requests. After
+ * that the device goes bananas. Serializing the reads/writes prevents this
+ * from happening.
+ */
+
+static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+
+ if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
+ unsigned long flags;
+ spin_lock_irqsave(&sc->sc_serial_rw, flags);
+ iowrite32(val, sc->mem + reg_offset);
+ spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
+ } else
+ iowrite32(val, sc->mem + reg_offset);
+}
+
+static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+ u32 val;
+
+ if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
+ unsigned long flags;
+ spin_lock_irqsave(&sc->sc_serial_rw, flags);
+ val = ioread32(sc->mem + reg_offset);
+ spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
+ } else
+ val = ioread32(sc->mem + reg_offset);
+ return val;
+}
+
+static const struct ath_ops ath9k_common_ops = {
+ .read = ath9k_ioread32,
+ .write = ath9k_iowrite32,
+};
+
+/*
* Initialize and fill ath_softc, ath_sofct is the
* "Software Carrier" struct. Historically it has existed
* to allow the separation between hardware specific
* variables (now in ath_hw) and driver specific variables.
*/
-static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
+static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
+ const struct ath_bus_ops *bus_ops)
{
struct ath_hw *ah = NULL;
+ struct ath_common *common;
int r = 0, i;
int csz = 0;
+ int qnum;
/* XXX: hardware will not be ready until ath_open() being called */
sc->sc_flags |= SC_OP_INVALID;
- if (ath9k_init_debug(sc) < 0)
- printk(KERN_ERR "Unable to create debugfs files\n");
-
spin_lock_init(&sc->wiphy_lock);
spin_lock_init(&sc->sc_resetlock);
spin_lock_init(&sc->sc_serial_rw);
@@ -1328,75 +1617,80 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
(unsigned long)sc);
- /*
- * Cache line size is used to size and align various
- * structures used to communicate with the hardware.
- */
- ath_read_cachesize(sc, &csz);
- /* XXX assert csz is non-zero */
- sc->common.cachelsz = csz << 2; /* convert to bytes */
-
ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
- if (!ah) {
- r = -ENOMEM;
- goto bad_no_ah;
- }
+ if (!ah)
+ return -ENOMEM;
- ah->ah_sc = sc;
ah->hw_version.devid = devid;
ah->hw_version.subsysid = subsysid;
sc->sc_ah = ah;
+ common = ath9k_hw_common(ah);
+ common->ops = &ath9k_common_ops;
+ common->bus_ops = bus_ops;
+ common->ah = ah;
+ common->hw = sc->hw;
+ common->priv = sc;
+ common->debug_mask = ath9k_debug;
+
+ /*
+ * Cache line size is used to size and align various
+ * structures used to communicate with the hardware.
+ */
+ ath_read_cachesize(common, &csz);
+ /* XXX assert csz is non-zero */
+ common->cachelsz = csz << 2; /* convert to bytes */
+
r = ath9k_hw_init(ah);
if (r) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to initialize hardware; "
- "initialization status: %d\n", r);
- goto bad;
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to initialize hardware; "
+ "initialization status: %d\n", r);
+ goto bad_free_hw;
+ }
+
+ if (ath9k_init_debug(ah) < 0) {
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to create debugfs files\n");
+ goto bad_free_hw;
}
/* Get the hardware key cache size. */
- sc->keymax = ah->caps.keycache_size;
- if (sc->keymax > ATH_KEYMAX) {
- DPRINTF(sc, ATH_DBG_ANY,
- "Warning, using only %u entries in %u key cache\n",
- ATH_KEYMAX, sc->keymax);
- sc->keymax = ATH_KEYMAX;
+ common->keymax = ah->caps.keycache_size;
+ if (common->keymax > ATH_KEYMAX) {
+ ath_print(common, ATH_DBG_ANY,
+ "Warning, using only %u entries in %u key cache\n",
+ ATH_KEYMAX, common->keymax);
+ common->keymax = ATH_KEYMAX;
}
/*
* Reset the key cache since some parts do not
* reset the contents on initial power up.
*/
- for (i = 0; i < sc->keymax; i++)
+ for (i = 0; i < common->keymax; i++)
ath9k_hw_keyreset(ah, (u16) i);
/* default to MONITOR mode */
sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR;
- /* Setup rate tables */
-
- ath_rate_attach(sc);
- ath_setup_rates(sc, IEEE80211_BAND_2GHZ);
- ath_setup_rates(sc, IEEE80211_BAND_5GHZ);
-
/*
* Allocate hardware transmit queues: one queue for
* beacon frames and one data queue for each QoS
* priority. Note that the hal handles reseting
* these queues at the needed time.
*/
- sc->beacon.beaconq = ath_beaconq_setup(ah);
+ sc->beacon.beaconq = ath9k_hw_beaconq_setup(ah);
if (sc->beacon.beaconq == -1) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to setup a beacon xmit queue\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to setup a beacon xmit queue\n");
r = -EIO;
goto bad2;
}
sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
if (sc->beacon.cabq == NULL) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to setup CAB xmit queue\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to setup CAB xmit queue\n");
r = -EIO;
goto bad2;
}
@@ -1410,27 +1704,27 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
/* Setup data queues */
/* NB: ensure BK queue is the lowest priority h/w queue */
if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to setup xmit queue for BK traffic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for BK traffic\n");
r = -EIO;
goto bad2;
}
if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to setup xmit queue for BE traffic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for BE traffic\n");
r = -EIO;
goto bad2;
}
if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to setup xmit queue for VI traffic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for VI traffic\n");
r = -EIO;
goto bad2;
}
if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to setup xmit queue for VO traffic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to setup xmit queue for VO traffic\n");
r = -EIO;
goto bad2;
}
@@ -1438,8 +1732,8 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
/* Initializes the noise floor to a reasonable default value.
* Later on this will be updated during ANI processing. */
- sc->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR;
- setup_timer(&sc->ani.timer, ath_ani_calibrate, (unsigned long)sc);
+ common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR;
+ setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
ATH9K_CIPHER_TKIP, NULL)) {
@@ -1465,7 +1759,7 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
ATH9K_CIPHER_MIC, NULL)
&& ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
0, NULL))
- sc->splitmic = 1;
+ common->splitmic = 1;
/* turn on mcast key search if possible */
if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
@@ -1480,14 +1774,14 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
sc->sc_flags |= SC_OP_RXAGGR;
}
- sc->tx_chainmask = ah->caps.tx_chainmask;
- sc->rx_chainmask = ah->caps.rx_chainmask;
+ common->tx_chainmask = ah->caps.tx_chainmask;
+ common->rx_chainmask = ah->caps.rx_chainmask;
ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
sc->rx.defant = ath9k_hw_getdefantenna(ah);
if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
- memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
+ memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */
@@ -1499,26 +1793,45 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
/* setup channels and rates */
- sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable;
- sc->sbands[IEEE80211_BAND_2GHZ].bitrates =
- sc->rates[IEEE80211_BAND_2GHZ];
- sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
- sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
- ARRAY_SIZE(ath9k_2ghz_chantable);
+ if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) {
+ sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable;
+ sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
+ sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
+ ARRAY_SIZE(ath9k_2ghz_chantable);
+ sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
+ sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
+ ARRAY_SIZE(ath9k_legacy_rates);
+ }
if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) {
sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable;
- sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
- sc->rates[IEEE80211_BAND_5GHZ];
sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
ARRAY_SIZE(ath9k_5ghz_chantable);
+ sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
+ ath9k_legacy_rates + 4;
+ sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
+ ARRAY_SIZE(ath9k_legacy_rates) - 4;
}
- if (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) {
- r = ath9k_hw_btcoex_init(ah);
+ switch (ah->btcoex_hw.scheme) {
+ case ATH_BTCOEX_CFG_NONE:
+ break;
+ case ATH_BTCOEX_CFG_2WIRE:
+ ath9k_hw_btcoex_init_2wire(ah);
+ break;
+ case ATH_BTCOEX_CFG_3WIRE:
+ ath9k_hw_btcoex_init_3wire(ah);
+ r = ath_init_btcoex_timer(sc);
if (r)
goto bad2;
+ qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
+ ath9k_hw_init_btcoex_hw(ah, qnum);
+ sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
+ break;
+ default:
+ WARN_ON(1);
+ break;
}
return 0;
@@ -1527,12 +1840,9 @@ bad2:
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
-bad:
- ath9k_hw_detach(ah);
- sc->sc_ah = NULL;
-bad_no_ah:
- ath9k_exit_debug(sc);
+bad_free_hw:
+ ath9k_uninit_hw(sc);
return r;
}
@@ -1555,7 +1865,7 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT);
- hw->wiphy->ps_default = false;
+ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
hw->queues = 4;
hw->max_rates = 4;
@@ -1568,43 +1878,53 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->rate_control_algorithm = "ath9k_rate_control";
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &sc->sbands[IEEE80211_BAND_2GHZ];
+ if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &sc->sbands[IEEE80211_BAND_2GHZ];
if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
&sc->sbands[IEEE80211_BAND_5GHZ];
}
/* Device driver core initialization */
-int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid)
+int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
+ const struct ath_bus_ops *bus_ops)
{
struct ieee80211_hw *hw = sc->hw;
+ struct ath_common *common;
+ struct ath_hw *ah;
int error = 0, i;
struct ath_regulatory *reg;
- DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n");
+ dev_dbg(sc->dev, "Attach ATH hw\n");
- error = ath_init_softc(devid, sc, subsysid);
+ error = ath_init_softc(devid, sc, subsysid, bus_ops);
if (error != 0)
return error;
+ ah = sc->sc_ah;
+ common = ath9k_hw_common(ah);
+
/* get mac address from hardware and set in mac80211 */
- SET_IEEE80211_PERM_ADDR(hw, sc->sc_ah->macaddr);
+ SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
ath_set_hw_capab(sc, hw);
- error = ath_regd_init(&sc->common.regulatory, sc->hw->wiphy,
+ error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
ath9k_reg_notifier);
if (error)
return error;
- reg = &sc->common.regulatory;
+ reg = &common->regulatory;
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
- if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
- setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+ if (test_bit(ATH9K_MODE_11G, ah->caps.wireless_modes))
+ setup_ht_cap(sc,
+ &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+ if (test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes))
+ setup_ht_cap(sc,
+ &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
}
/* initialize tx/rx engine */
@@ -1641,9 +1961,7 @@ error_attach:
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
- ath9k_hw_detach(sc->sc_ah);
- sc->sc_ah = NULL;
- ath9k_exit_debug(sc);
+ ath9k_uninit_hw(sc);
return error;
}
@@ -1651,6 +1969,7 @@ error_attach:
int ath_reset(struct ath_softc *sc, bool retry_tx)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_hw *hw = sc->hw;
int r;
@@ -1662,12 +1981,13 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
if (r)
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to reset hardware; reset status %d\n", r);
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to reset hardware; reset status %d\n", r);
spin_unlock_bh(&sc->sc_resetlock);
if (ath_startrecv(sc) != 0)
- DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to start recv logic\n");
/*
* We may be doing a reset in response to a request
@@ -1710,19 +2030,20 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
-
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_desc *ds;
struct ath_buf *bf;
int i, bsize, error;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n",
- name, nbuf, ndesc);
+ ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n",
+ name, nbuf, ndesc);
INIT_LIST_HEAD(head);
/* ath_desc must be a multiple of DWORDs */
if ((sizeof(struct ath_desc) % 4) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL, "ath_desc not DWORD aligned\n");
- ASSERT((sizeof(struct ath_desc) % 4) == 0);
+ ath_print(common, ATH_DBG_FATAL,
+ "ath_desc not DWORD aligned\n");
+ BUG_ON((sizeof(struct ath_desc) % 4) != 0);
error = -ENOMEM;
goto fail;
}
@@ -1755,9 +2076,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
goto fail;
}
ds = dd->dd_desc;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
- name, ds, (u32) dd->dd_desc_len,
- ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
+ ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
+ name, ds, (u32) dd->dd_desc_len,
+ ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
/* allocate buffers */
bsize = sizeof(struct ath_buf) * nbuf;
@@ -1780,7 +2101,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
* descriptor fetch.
*/
while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
- ASSERT((caddr_t) bf->bf_desc <
+ BUG_ON((caddr_t) bf->bf_desc >=
((caddr_t) dd->dd_desc +
dd->dd_desc_len));
@@ -1884,31 +2205,50 @@ void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
}
- sc->tx_chan_width = ATH9K_HT_MACMODE_20;
-
- if (conf_is_ht(conf)) {
- if (conf_is_ht40(conf))
- sc->tx_chan_width = ATH9K_HT_MACMODE_2040;
-
+ if (conf_is_ht(conf))
ichan->chanmode = ath_get_extchanmode(sc, chan,
conf->channel_type);
- }
}
/**********************/
/* mac80211 callbacks */
/**********************/
+/*
+ * (Re)start btcoex timers
+ */
+static void ath9k_btcoex_timer_resume(struct ath_softc *sc)
+{
+ struct ath_btcoex *btcoex = &sc->btcoex;
+ struct ath_hw *ah = sc->sc_ah;
+
+ ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
+ "Starting btcoex timers");
+
+ /* make sure duty cycle timer is also stopped when resuming */
+ if (btcoex->hw_timer_enabled)
+ ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
+
+ btcoex->bt_priority_cnt = 0;
+ btcoex->bt_priority_time = jiffies;
+ sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
+
+ mod_timer(&btcoex->period_timer, jiffies);
+}
+
static int ath9k_start(struct ieee80211_hw *hw)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_channel *curchan = hw->conf.channel;
struct ath9k_channel *init_channel;
int r;
- DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with "
- "initial channel: %d MHz\n", curchan->center_freq);
+ ath_print(common, ATH_DBG_CONFIG,
+ "Starting driver with initial channel: %d MHz\n",
+ curchan->center_freq);
mutex_lock(&sc->mutex);
@@ -1940,7 +2280,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
init_channel = ath_get_curchannel(sc, hw);
/* Reset SERDES registers */
- ath9k_hw_configpcipowersave(sc->sc_ah, 0, 0);
+ ath9k_hw_configpcipowersave(ah, 0, 0);
/*
* The basic interface to setting the hardware in a good
@@ -1950,12 +2290,12 @@ static int ath9k_start(struct ieee80211_hw *hw)
* and then setup of the interrupt mask.
*/
spin_lock_bh(&sc->sc_resetlock);
- r = ath9k_hw_reset(sc->sc_ah, init_channel, false);
+ r = ath9k_hw_reset(ah, init_channel, false);
if (r) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to reset hardware; reset status %d "
- "(freq %u MHz)\n", r,
- curchan->center_freq);
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to reset hardware; reset status %d "
+ "(freq %u MHz)\n", r,
+ curchan->center_freq);
spin_unlock_bh(&sc->sc_resetlock);
goto mutex_unlock;
}
@@ -1975,7 +2315,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
* here except setup the interrupt mask.
*/
if (ath_startrecv(sc) != 0) {
- DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to start recv logic\n");
r = -EIO;
goto mutex_unlock;
}
@@ -1985,10 +2326,10 @@ static int ath9k_start(struct ieee80211_hw *hw)
| ATH9K_INT_RXEOL | ATH9K_INT_RXORN
| ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
sc->imask |= ATH9K_INT_GTT;
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
sc->imask |= ATH9K_INT_CST;
ath_cache_conf_rate(sc, &hw->conf);
@@ -1997,21 +2338,22 @@ static int ath9k_start(struct ieee80211_hw *hw)
/* Disable BMISS interrupt when we're not associated */
sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
- ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
+ ath9k_hw_set_interrupts(ah, sc->imask);
ieee80211_wake_queues(hw);
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
- if ((sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) &&
- !(sc->sc_flags & SC_OP_BTCOEX_ENABLED)) {
- ath_btcoex_set_weight(&sc->btcoex_info, AR_BT_COEX_WGHT,
- AR_STOMP_LOW_WLAN_WGHT);
- ath9k_hw_btcoex_enable(sc->sc_ah);
+ if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
+ !ah->btcoex_hw.enabled) {
+ ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
+ AR_STOMP_LOW_WLAN_WGHT);
+ ath9k_hw_btcoex_enable(ah);
- ath_pcie_aspm_disable(sc);
- if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
- ath_btcoex_timer_resume(sc, &sc->btcoex_info);
+ if (common->bus_ops->bt_coex_prep)
+ common->bus_ops->bt_coex_prep(common);
+ if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
+ ath9k_btcoex_timer_resume(sc);
}
mutex_unlock:
@@ -2026,17 +2368,19 @@ static int ath9k_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_tx_control txctl;
- int hdrlen, padsize;
+ int padpos, padsize;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) {
- printk(KERN_DEBUG "ath9k: %s: TX in unexpected wiphy state "
- "%d\n", wiphy_name(hw->wiphy), aphy->state);
+ ath_print(common, ATH_DBG_XMIT,
+ "ath9k: %s: TX in unexpected wiphy state "
+ "%d\n", wiphy_name(hw->wiphy), aphy->state);
goto exit;
}
if (sc->ps_enabled) {
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
/*
* mac80211 does not set PM field for normal data frames, so we
* need to update that based on the current PS mode.
@@ -2044,8 +2388,8 @@ static int ath9k_tx(struct ieee80211_hw *hw,
if (ieee80211_is_data(hdr->frame_control) &&
!ieee80211_is_nullfunc(hdr->frame_control) &&
!ieee80211_has_pm(hdr->frame_control)) {
- DPRINTF(sc, ATH_DBG_PS, "Add PM=1 for a TX frame "
- "while in PS mode\n");
+ ath_print(common, ATH_DBG_PS, "Add PM=1 for a TX frame "
+ "while in PS mode\n");
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
}
}
@@ -2056,15 +2400,15 @@ static int ath9k_tx(struct ieee80211_hw *hw,
* power save mode. Need to wake up hardware for the TX to be
* completed and if needed, also for RX of buffered frames.
*/
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
ath9k_ps_wakeup(sc);
ath9k_hw_setrxabort(sc->sc_ah, 0);
if (ieee80211_is_pspoll(hdr->frame_control)) {
- DPRINTF(sc, ATH_DBG_PS, "Sending PS-Poll to pick a "
- "buffered frame\n");
+ ath_print(common, ATH_DBG_PS,
+ "Sending PS-Poll to pick a buffered frame\n");
sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA;
} else {
- DPRINTF(sc, ATH_DBG_PS, "Wake up to complete TX\n");
+ ath_print(common, ATH_DBG_PS,
+ "Wake up to complete TX\n");
sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK;
}
/*
@@ -2083,7 +2427,6 @@ static int ath9k_tx(struct ieee80211_hw *hw,
* BSSes.
*/
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
sc->tx.seq_no += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
@@ -2091,13 +2434,13 @@ static int ath9k_tx(struct ieee80211_hw *hw,
}
/* Add the padding after the header if this is not already done */
- hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- if (hdrlen & 3) {
- padsize = hdrlen % 4;
+ padpos = ath9k_cmn_padpos(hdr->frame_control);
+ padsize = padpos & 3;
+ if (padsize && skb->len>padpos) {
if (skb_headroom(skb) < padsize)
return -1;
skb_push(skb, padsize);
- memmove(skb->data, skb->data + padsize, hdrlen);
+ memmove(skb->data, skb->data + padsize, padpos);
}
/* Check if a tx queue is available */
@@ -2106,10 +2449,10 @@ static int ath9k_tx(struct ieee80211_hw *hw,
if (!txctl.txq)
goto exit;
- DPRINTF(sc, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
+ ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
if (ath_tx_start(hw, skb, &txctl) != 0) {
- DPRINTF(sc, ATH_DBG_XMIT, "TX failed\n");
+ ath_print(common, ATH_DBG_XMIT, "TX failed\n");
goto exit;
}
@@ -2119,10 +2462,28 @@ exit:
return 0;
}
+/*
+ * Pause btcoex timer and bt duty cycle timer
+ */
+static void ath9k_btcoex_timer_pause(struct ath_softc *sc)
+{
+ struct ath_btcoex *btcoex = &sc->btcoex;
+ struct ath_hw *ah = sc->sc_ah;
+
+ del_timer_sync(&btcoex->period_timer);
+
+ if (btcoex->hw_timer_enabled)
+ ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
+
+ btcoex->hw_timer_enabled = false;
+}
+
static void ath9k_stop(struct ieee80211_hw *hw)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
mutex_lock(&sc->mutex);
@@ -2137,7 +2498,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
}
if (sc->sc_flags & SC_OP_INVALID) {
- DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
+ ath_print(common, ATH_DBG_ANY, "Device not present\n");
mutex_unlock(&sc->mutex);
return;
}
@@ -2147,33 +2508,33 @@ static void ath9k_stop(struct ieee80211_hw *hw)
return; /* another wiphy still in use */
}
- if (sc->sc_flags & SC_OP_BTCOEX_ENABLED) {
- ath9k_hw_btcoex_disable(sc->sc_ah);
- if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
- ath_btcoex_timer_pause(sc, &sc->btcoex_info);
+ if (ah->btcoex_hw.enabled) {
+ ath9k_hw_btcoex_disable(ah);
+ if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
+ ath9k_btcoex_timer_pause(sc);
}
/* make sure h/w will not generate any interrupt
* before setting the invalid flag. */
- ath9k_hw_set_interrupts(sc->sc_ah, 0);
+ ath9k_hw_set_interrupts(ah, 0);
if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_drain_all_txq(sc, false);
ath_stoprecv(sc);
- ath9k_hw_phy_disable(sc->sc_ah);
+ ath9k_hw_phy_disable(ah);
} else
sc->rx.rxlink = NULL;
/* disable HAL and put h/w to sleep */
- ath9k_hw_disable(sc->sc_ah);
- ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1);
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
+ ath9k_hw_disable(ah);
+ ath9k_hw_configpcipowersave(ah, 1, 1);
+ ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
sc->sc_flags |= SC_OP_INVALID;
mutex_unlock(&sc->mutex);
- DPRINTF(sc, ATH_DBG_CONFIG, "Driver halt\n");
+ ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
}
static int ath9k_add_interface(struct ieee80211_hw *hw,
@@ -2181,6 +2542,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)conf->vif->drv_priv;
enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
int ret = 0;
@@ -2207,13 +2569,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
ic_opmode = conf->type;
break;
default:
- DPRINTF(sc, ATH_DBG_FATAL,
+ ath_print(common, ATH_DBG_FATAL,
"Interface type %d not yet supported\n", conf->type);
ret = -EOPNOTSUPP;
goto out;
}
- DPRINTF(sc, ATH_DBG_CONFIG, "Attach a VIF of type: %d\n", ic_opmode);
+ ath_print(common, ATH_DBG_CONFIG,
+ "Attach a VIF of type: %d\n", ic_opmode);
/* Set the VIF opmode */
avp->av_opmode = ic_opmode;
@@ -2251,7 +2614,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
if (conf->type == NL80211_IFTYPE_AP ||
conf->type == NL80211_IFTYPE_ADHOC ||
conf->type == NL80211_IFTYPE_MONITOR)
- ath_start_ani(sc);
+ ath_start_ani(common);
out:
mutex_unlock(&sc->mutex);
@@ -2263,15 +2626,16 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)conf->vif->drv_priv;
int i;
- DPRINTF(sc, ATH_DBG_CONFIG, "Detach Interface\n");
+ ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
mutex_lock(&sc->mutex);
/* Stop ANI */
- del_timer_sync(&sc->ani.timer);
+ del_timer_sync(&common->ani.timer);
/* Reclaim beacon resources */
if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
@@ -2301,32 +2665,55 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ieee80211_conf *conf = &hw->conf;
struct ath_hw *ah = sc->sc_ah;
- bool all_wiphys_idle = false, disable_radio = false;
+ bool disable_radio;
mutex_lock(&sc->mutex);
- /* Leave this as the first check */
+ /*
+ * Leave this as the first check because we need to turn on the
+ * radio if it was disabled before prior to processing the rest
+ * of the changes. Likewise we must only disable the radio towards
+ * the end.
+ */
if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ bool enable_radio;
+ bool all_wiphys_idle;
+ bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
spin_lock_bh(&sc->wiphy_lock);
all_wiphys_idle = ath9k_all_wiphys_idle(sc);
+ ath9k_set_wiphy_idle(aphy, idle);
+
+ if (!idle && all_wiphys_idle)
+ enable_radio = true;
+
+ /*
+ * After we unlock here its possible another wiphy
+ * can be re-renabled so to account for that we will
+ * only disable the radio toward the end of this routine
+ * if by then all wiphys are still idle.
+ */
spin_unlock_bh(&sc->wiphy_lock);
- if (conf->flags & IEEE80211_CONF_IDLE){
- if (all_wiphys_idle)
- disable_radio = true;
- }
- else if (all_wiphys_idle) {
- ath_radio_enable(sc);
- DPRINTF(sc, ATH_DBG_CONFIG,
- "not-idle: enabling radio\n");
+ if (enable_radio) {
+ ath_radio_enable(sc, hw);
+ ath_print(common, ATH_DBG_CONFIG,
+ "not-idle: enabling radio\n");
}
}
+ /*
+ * We just prepare to enable PS. We have to wait until our AP has
+ * ACK'd our null data frame to disable RX otherwise we'll ignore
+ * those ACKs and end up retransmitting the same null data frames.
+ * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
+ */
if (changed & IEEE80211_CONF_CHANGE_PS) {
if (conf->flags & IEEE80211_CONF_PS) {
+ sc->sc_flags |= SC_OP_PS_ENABLED;
if (!(ah->caps.hw_caps &
ATH9K_HW_CAP_AUTOSLEEP)) {
if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
@@ -2334,12 +2721,21 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
ath9k_hw_set_interrupts(sc->sc_ah,
sc->imask);
}
- ath9k_hw_setrxabort(sc->sc_ah, 1);
}
- sc->ps_enabled = true;
+ /*
+ * At this point we know hardware has received an ACK
+ * of a previously sent null data frame.
+ */
+ if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) {
+ sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
+ sc->ps_enabled = true;
+ ath9k_hw_setrxabort(sc->sc_ah, 1);
+ }
} else {
sc->ps_enabled = false;
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
+ sc->sc_flags &= ~(SC_OP_PS_ENABLED |
+ SC_OP_NULLFUNC_COMPLETED);
+ ath9k_setpower(sc, ATH9K_PM_AWAKE);
if (!(ah->caps.hw_caps &
ATH9K_HW_CAP_AUTOSLEEP)) {
ath9k_hw_setrxabort(sc->sc_ah, 0);
@@ -2374,8 +2770,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
goto skip_chan_change;
}
- DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
- curchan->center_freq);
+ ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
+ curchan->center_freq);
/* XXX: remove me eventualy */
ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
@@ -2383,7 +2779,8 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
ath_update_chainmask(sc, conf_is_ht(conf));
if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
- DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to set channel\n");
mutex_unlock(&sc->mutex);
return -EINVAL;
}
@@ -2393,9 +2790,13 @@ skip_chan_change:
if (changed & IEEE80211_CONF_CHANGE_POWER)
sc->config.txpowlimit = 2 * conf->power_level;
+ spin_lock_bh(&sc->wiphy_lock);
+ disable_radio = ath9k_all_wiphys_idle(sc);
+ spin_unlock_bh(&sc->wiphy_lock);
+
if (disable_radio) {
- DPRINTF(sc, ATH_DBG_CONFIG, "idle: disabling radio\n");
- ath_radio_disable(sc);
+ ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
+ ath_radio_disable(sc, hw);
}
mutex_unlock(&sc->mutex);
@@ -2431,7 +2832,8 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw,
ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
ath9k_ps_restore(sc);
- DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", rfilt);
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
+ "Set HW RX filter: 0x%x\n", rfilt);
}
static void ath9k_sta_notify(struct ieee80211_hw *hw,
@@ -2459,6 +2861,7 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath9k_tx_queue_info qi;
int ret = 0, qnum;
@@ -2475,15 +2878,19 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
qi.tqi_burstTime = params->txop;
qnum = ath_get_hal_qnum(queue, sc);
- DPRINTF(sc, ATH_DBG_CONFIG,
- "Configure tx [queue/halq] [%d/%d], "
- "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
- queue, qnum, params->aifs, params->cw_min,
- params->cw_max, params->txop);
+ ath_print(common, ATH_DBG_CONFIG,
+ "Configure tx [queue/halq] [%d/%d], "
+ "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
+ queue, qnum, params->aifs, params->cw_min,
+ params->cw_max, params->txop);
ret = ath_txq_update(sc, qnum, &qi);
if (ret)
- DPRINTF(sc, ATH_DBG_FATAL, "TXQ Update failed\n");
+ ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
+
+ if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)
+ if ((qnum == sc->tx.hwq_map[ATH9K_WME_AC_BE]) && !ret)
+ ath_beaconq_config(sc);
mutex_unlock(&sc->mutex);
@@ -2498,6 +2905,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int ret = 0;
if (modparam_nohwcrypt)
@@ -2505,11 +2913,11 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
mutex_lock(&sc->mutex);
ath9k_ps_wakeup(sc);
- DPRINTF(sc, ATH_DBG_CONFIG, "Set HW Key\n");
+ ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
switch (cmd) {
case SET_KEY:
- ret = ath_key_config(sc, vif, sta, key);
+ ret = ath_key_config(common, vif, sta, key);
if (ret >= 0) {
key->hw_key_idx = ret;
/* push IV and Michael MIC generation to stack */
@@ -2522,7 +2930,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
}
break;
case DISABLE_KEY:
- ath_key_delete(sc, key);
+ ath_key_delete(common, key);
break;
default:
ret = -EINVAL;
@@ -2542,94 +2950,67 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_vif *avp = (void *)vif->drv_priv;
- u32 rfilt = 0;
- int error, i;
+ int error;
mutex_lock(&sc->mutex);
- /*
- * TODO: Need to decide which hw opmode to use for
- * multi-interface cases
- * XXX: This belongs into add_interface!
- */
- if (vif->type == NL80211_IFTYPE_AP &&
- ah->opmode != NL80211_IFTYPE_AP) {
- ah->opmode = NL80211_IFTYPE_STATION;
- ath9k_hw_setopmode(ah);
- memcpy(sc->curbssid, sc->sc_ah->macaddr, ETH_ALEN);
- sc->curaid = 0;
- ath9k_hw_write_associd(sc);
- /* Request full reset to get hw opmode changed properly */
- sc->sc_flags |= SC_OP_FULL_RESET;
- }
-
- if ((changed & BSS_CHANGED_BSSID) &&
- !is_zero_ether_addr(bss_conf->bssid)) {
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- /* Set BSSID */
- memcpy(sc->curbssid, bss_conf->bssid, ETH_ALEN);
- memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
- sc->curaid = 0;
- ath9k_hw_write_associd(sc);
-
- /* Set aggregation protection mode parameters */
- sc->config.ath_aggr_prot = 0;
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "RX filter 0x%x bssid %pM aid 0x%x\n",
- rfilt, sc->curbssid, sc->curaid);
-
- /* need to reconfigure the beacon */
- sc->sc_flags &= ~SC_OP_BEACONS ;
+ if (changed & BSS_CHANGED_BSSID) {
+ /* Set BSSID */
+ memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
+ memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
+ common->curaid = 0;
+ ath9k_hw_write_associd(ah);
- break;
- default:
- break;
- }
+ /* Set aggregation protection mode parameters */
+ sc->config.ath_aggr_prot = 0;
+
+ /* Only legacy IBSS for now */
+ if (vif->type == NL80211_IFTYPE_ADHOC)
+ ath_update_chainmask(sc, 0);
+
+ ath_print(common, ATH_DBG_CONFIG,
+ "BSSID: %pM aid: 0x%x\n",
+ common->curbssid, common->curaid);
+
+ /* need to reconfigure the beacon */
+ sc->sc_flags &= ~SC_OP_BEACONS ;
}
- if ((vif->type == NL80211_IFTYPE_ADHOC) ||
- (vif->type == NL80211_IFTYPE_AP) ||
- (vif->type == NL80211_IFTYPE_MESH_POINT)) {
- if ((changed & BSS_CHANGED_BEACON) ||
- (changed & BSS_CHANGED_BEACON_ENABLED &&
- bss_conf->enable_beacon)) {
- /*
- * Allocate and setup the beacon frame.
- *
- * Stop any previous beacon DMA. This may be
- * necessary, for example, when an ibss merge
- * causes reconfiguration; we may be called
- * with beacon transmission active.
- */
- ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+ /* Enable transmission of beacons (AP, IBSS, MESH) */
+ if ((changed & BSS_CHANGED_BEACON) ||
+ ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) {
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+ error = ath_beacon_alloc(aphy, vif);
+ if (!error)
+ ath_beacon_config(sc, vif);
+ }
+ /* Disable transmission of beacons */
+ if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon)
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ sc->beacon_interval = bss_conf->beacon_int;
+ /*
+ * In case of AP mode, the HW TSF has to be reset
+ * when the beacon interval changes.
+ */
+ if (vif->type == NL80211_IFTYPE_AP) {
+ sc->sc_flags |= SC_OP_TSF_RESET;
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
error = ath_beacon_alloc(aphy, vif);
if (!error)
ath_beacon_config(sc, vif);
+ } else {
+ ath_beacon_config(sc, vif);
}
}
- /* Check for WLAN_CAPABILITY_PRIVACY ? */
- if ((avp->av_opmode != NL80211_IFTYPE_STATION)) {
- for (i = 0; i < IEEE80211_WEP_NKID; i++)
- if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
- ath9k_hw_keysetmac(sc->sc_ah,
- (u16)i,
- sc->curbssid);
- }
-
- /* Only legacy IBSS for now */
- if (vif->type == NL80211_IFTYPE_ADHOC)
- ath_update_chainmask(sc, 0);
-
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
- DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
- bss_conf->use_short_preamble);
+ ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
+ bss_conf->use_short_preamble);
if (bss_conf->use_short_preamble)
sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
else
@@ -2637,8 +3018,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_ERP_CTS_PROT) {
- DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
- bss_conf->use_cts_prot);
+ ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
+ bss_conf->use_cts_prot);
if (bss_conf->use_cts_prot &&
hw->conf.channel->band != IEEE80211_BAND_5GHZ)
sc->sc_flags |= SC_OP_PROTECT_ENABLE;
@@ -2647,23 +3028,11 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
}
if (changed & BSS_CHANGED_ASSOC) {
- DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
+ ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
bss_conf->assoc);
ath9k_bss_assoc_info(sc, vif, bss_conf);
}
- /*
- * The HW TSF has to be reset when the beacon interval changes.
- * We set the flag here, and ath_beacon_config_ap() would take this
- * into account when it gets called through the subsequent
- * config_interface() call - with IFCC_BEACON in the changed field.
- */
-
- if (changed & BSS_CHANGED_BEACON_INT) {
- sc->sc_flags |= SC_OP_TSF_RESET;
- sc->beacon_interval = bss_conf->beacon_int;
- }
-
mutex_unlock(&sc->mutex);
}
@@ -2696,11 +3065,16 @@ static void ath9k_reset_tsf(struct ieee80211_hw *hw)
struct ath_softc *sc = aphy->sc;
mutex_lock(&sc->mutex);
+
+ ath9k_ps_wakeup(sc);
ath9k_hw_reset_tsf(sc->sc_ah);
+ ath9k_ps_restore(sc);
+
mutex_unlock(&sc->mutex);
}
static int ath9k_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta,
u16 tid, u16 *ssn)
@@ -2718,17 +3092,18 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
break;
case IEEE80211_AMPDU_TX_START:
ath_tx_aggr_start(sc, sta, tid, ssn);
- ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid);
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
ath_tx_aggr_stop(sc, sta, tid);
- ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid);
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
ath_tx_aggr_resume(sc, sta, tid);
break;
default:
- DPRINTF(sc, ATH_DBG_FATAL, "Unknown AMPDU action\n");
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "Unknown AMPDU action\n");
}
return ret;
@@ -2796,64 +3171,6 @@ struct ieee80211_ops ath9k_ops = {
.rfkill_poll = ath9k_rfkill_poll_state,
};
-static struct {
- u32 version;
- const char * name;
-} ath_mac_bb_names[] = {
- { AR_SREV_VERSION_5416_PCI, "5416" },
- { AR_SREV_VERSION_5416_PCIE, "5418" },
- { AR_SREV_VERSION_9100, "9100" },
- { AR_SREV_VERSION_9160, "9160" },
- { AR_SREV_VERSION_9280, "9280" },
- { AR_SREV_VERSION_9285, "9285" },
- { AR_SREV_VERSION_9287, "9287" }
-};
-
-static struct {
- u16 version;
- const char * name;
-} ath_rf_names[] = {
- { 0, "5133" },
- { AR_RAD5133_SREV_MAJOR, "5133" },
- { AR_RAD5122_SREV_MAJOR, "5122" },
- { AR_RAD2133_SREV_MAJOR, "2133" },
- { AR_RAD2122_SREV_MAJOR, "2122" }
-};
-
-/*
- * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
- */
-const char *
-ath_mac_bb_name(u32 mac_bb_version)
-{
- int i;
-
- for (i=0; i<ARRAY_SIZE(ath_mac_bb_names); i++) {
- if (ath_mac_bb_names[i].version == mac_bb_version) {
- return ath_mac_bb_names[i].name;
- }
- }
-
- return "????";
-}
-
-/*
- * Return the RF name. "????" is returned if the RF is unknown.
- */
-const char *
-ath_rf_name(u16 rf_version)
-{
- int i;
-
- for (i=0; i<ARRAY_SIZE(ath_rf_names); i++) {
- if (ath_rf_names[i].version == rf_version) {
- return ath_rf_names[i].name;
- }
- }
-
- return "????";
-}
-
static int __init ath9k_init(void)
{
int error;
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 903dd8ad9d4..5321f735e5a 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -31,8 +31,9 @@ static struct pci_device_id ath_pci_id_table[] __devinitdata = {
};
/* return bus cachesize in 4B word units */
-static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz)
+static void ath_pci_read_cachesize(struct ath_common *common, int *csz)
{
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
u8 u8tmp;
pci_read_config_byte(to_pci_dev(sc->dev), PCI_CACHE_LINE_SIZE, &u8tmp);
@@ -48,8 +49,9 @@ static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz)
*csz = DEFAULT_CACHELINE >> 2; /* Use the default size */
}
-static void ath_pci_cleanup(struct ath_softc *sc)
+static void ath_pci_cleanup(struct ath_common *common)
{
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
struct pci_dev *pdev = to_pci_dev(sc->dev);
pci_iounmap(pdev, sc->mem);
@@ -57,9 +59,11 @@ static void ath_pci_cleanup(struct ath_softc *sc)
pci_release_region(pdev, 0);
}
-static bool ath_pci_eeprom_read(struct ath_hw *ah, u32 off, u16 *data)
+static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data)
{
- (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
+ struct ath_hw *ah = (struct ath_hw *) common->ah;
+
+ common->ops->read(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
if (!ath9k_hw_wait(ah,
AR_EEPROM_STATUS_DATA,
@@ -69,16 +73,34 @@ static bool ath_pci_eeprom_read(struct ath_hw *ah, u32 off, u16 *data)
return false;
}
- *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA),
+ *data = MS(common->ops->read(ah, AR_EEPROM_STATUS_DATA),
AR_EEPROM_STATUS_DATA_VAL);
return true;
}
-static struct ath_bus_ops ath_pci_bus_ops = {
+/*
+ * Bluetooth coexistance requires disabling ASPM.
+ */
+static void ath_pci_bt_coex_prep(struct ath_common *common)
+{
+ struct ath_softc *sc = (struct ath_softc *) common->priv;
+ struct pci_dev *pdev = to_pci_dev(sc->dev);
+ u8 aspm;
+
+ if (!pdev->is_pcie)
+ return;
+
+ pci_read_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, &aspm);
+ aspm &= ~(ATH_PCIE_CAP_LINK_L0S | ATH_PCIE_CAP_LINK_L1);
+ pci_write_config_byte(pdev, ATH_PCIE_CAP_LINK_CTRL, aspm);
+}
+
+const static struct ath_bus_ops ath_pci_bus_ops = {
.read_cachesize = ath_pci_read_cachesize,
.cleanup = ath_pci_cleanup,
.eeprom_read = ath_pci_eeprom_read,
+ .bt_coex_prep = ath_pci_bt_coex_prep,
};
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -92,6 +114,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
u32 val;
int ret = 0;
struct ath_hw *ah;
+ char hw_name[64];
if (pci_enable_device(pdev))
return -EIO;
@@ -177,10 +200,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sc->hw = hw;
sc->dev = &pdev->dev;
sc->mem = mem;
- sc->bus_ops = &ath_pci_bus_ops;
pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid);
- ret = ath_init_device(id->device, sc, subsysid);
+ ret = ath_init_device(id->device, sc, subsysid, &ath_pci_bus_ops);
if (ret) {
dev_err(&pdev->dev, "failed to initialize device\n");
goto bad3;
@@ -197,14 +219,11 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sc->irq = pdev->irq;
ah = sc->sc_ah;
+ ath9k_hw_name(ah, hw_name, sizeof(hw_name));
printk(KERN_INFO
- "%s: Atheros AR%s MAC/BB Rev:%x "
- "AR%s RF Rev:%x: mem=0x%lx, irq=%d\n",
+ "%s: %s mem=0x%lx, irq=%d\n",
wiphy_name(hw->wiphy),
- ath_mac_bb_name(ah->hw_version.macVersion),
- ah->hw_version.macRev,
- ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)),
- ah->hw_version.phyRev,
+ hw_name,
(unsigned long)mem, pdev->irq);
return 0;
diff --git a/drivers/net/wireless/ath/ath9k/phy.c b/drivers/net/wireless/ath/ath9k/phy.c
index 63bf9a307c6..c3b59390fe3 100644
--- a/drivers/net/wireless/ath/ath9k/phy.c
+++ b/drivers/net/wireless/ath/ath9k/phy.c
@@ -14,90 +14,70 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include "ath9k.h"
+/**
+ * DOC: Programming Atheros 802.11n analog front end radios
+ *
+ * AR5416 MAC based PCI devices and AR518 MAC based PCI-Express
+ * devices have either an external AR2133 analog front end radio for single
+ * band 2.4 GHz communication or an AR5133 analog front end radio for dual
+ * band 2.4 GHz / 5 GHz communication.
+ *
+ * All devices after the AR5416 and AR5418 family starting with the AR9280
+ * have their analog front radios, MAC/BB and host PCIe/USB interface embedded
+ * into a single-chip and require less programming.
+ *
+ * The following single-chips exist with a respective embedded radio:
+ *
+ * AR9280 - 11n dual-band 2x2 MIMO for PCIe
+ * AR9281 - 11n single-band 1x2 MIMO for PCIe
+ * AR9285 - 11n single-band 1x1 for PCIe
+ * AR9287 - 11n single-band 2x2 MIMO for PCIe
+ *
+ * AR9220 - 11n dual-band 2x2 MIMO for PCI
+ * AR9223 - 11n single-band 2x2 MIMO for PCI
+ *
+ * AR9287 - 11n single-band 1x1 MIMO for USB
+ */
-void
-ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex, u32 freqIndex,
- int regWrites)
-{
- REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites);
-}
+#include "hw.h"
-bool
-ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
+/**
+ * ath9k_hw_write_regs - ??
+ *
+ * @ah: atheros hardware structure
+ * @freqIndex:
+ * @regWrites:
+ *
+ * Used for both the chipsets with an external AR2133/AR5133 radios and
+ * single-chip devices.
+ */
+void ath9k_hw_write_regs(struct ath_hw *ah, u32 freqIndex, int regWrites)
{
- u32 channelSel = 0;
- u32 bModeSynth = 0;
- u32 aModeRefSel = 0;
- u32 reg32 = 0;
- u16 freq;
- struct chan_centers centers;
-
- ath9k_hw_get_channel_centers(ah, chan, &centers);
- freq = centers.synth_center;
-
- if (freq < 4800) {
- u32 txctl;
-
- if (((freq - 2192) % 5) == 0) {
- channelSel = ((freq - 672) * 2 - 3040) / 10;
- bModeSynth = 0;
- } else if (((freq - 2224) % 5) == 0) {
- channelSel = ((freq - 704) * 2 - 3040) / 10;
- bModeSynth = 1;
- } else {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Invalid channel %u MHz\n", freq);
- return false;
- }
-
- channelSel = (channelSel << 2) & 0xff;
- channelSel = ath9k_hw_reverse_bits(channelSel, 8);
-
- txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL);
- if (freq == 2484) {
-
- REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
- txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
- } else {
- REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
- txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN);
- }
-
- } else if ((freq % 20) == 0 && freq >= 5120) {
- channelSel =
- ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8);
- aModeRefSel = ath9k_hw_reverse_bits(1, 2);
- } else if ((freq % 10) == 0) {
- channelSel =
- ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8);
- if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah))
- aModeRefSel = ath9k_hw_reverse_bits(2, 2);
- else
- aModeRefSel = ath9k_hw_reverse_bits(1, 2);
- } else if ((freq % 5) == 0) {
- channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8);
- aModeRefSel = ath9k_hw_reverse_bits(1, 2);
- } else {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Invalid channel %u MHz\n", freq);
- return false;
- }
-
- reg32 =
- (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) |
- (1 << 5) | 0x1;
-
- REG_WRITE(ah, AR_PHY(0x37), reg32);
-
- ah->curchan = chan;
- ah->curchan_rad_index = -1;
-
- return true;
+ REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites);
}
-void ath9k_hw_ar9280_set_channel(struct ath_hw *ah,
- struct ath9k_channel *chan)
+/**
+ * ath9k_hw_ar9280_set_channel - set channel on single-chip device
+ * @ah: atheros hardware structure
+ * @chan:
+ *
+ * This is the function to change channel on single-chip devices, that is
+ * all devices after ar9280.
+ *
+ * This function takes the channel value in MHz and sets
+ * hardware channel value. Assumes writes have been enabled to analog bus.
+ *
+ * Actual Expression,
+ *
+ * For 2GHz channel,
+ * Channel Frequency = (3/4) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17)
+ * (freq_ref = 40MHz)
+ *
+ * For 5GHz channel,
+ * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^10)
+ * (freq_ref = 40MHz/(24>>amodeRefSel))
+ */
+int ath9k_hw_ar9280_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
{
u16 bMode, fracMode, aModeRefSel = 0;
u32 freq, ndiv, channelSel = 0, channelFrac = 0, reg32 = 0;
@@ -110,22 +90,34 @@ void ath9k_hw_ar9280_set_channel(struct ath_hw *ah,
reg32 = REG_READ(ah, AR_PHY_SYNTH_CONTROL);
reg32 &= 0xc0000000;
- if (freq < 4800) {
+ if (freq < 4800) { /* 2 GHz, fractional mode */
u32 txctl;
+ int regWrites = 0;
bMode = 1;
fracMode = 1;
aModeRefSel = 0;
channelSel = (freq * 0x10000) / 15;
- txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL);
- if (freq == 2484) {
-
- REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
- txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
+ if (AR_SREV_9287_11_OR_LATER(ah)) {
+ if (freq == 2484) {
+ /* Enable channel spreading for channel 14 */
+ REG_WRITE_ARRAY(&ah->iniCckfirJapan2484,
+ 1, regWrites);
+ } else {
+ REG_WRITE_ARRAY(&ah->iniCckfirNormal,
+ 1, regWrites);
+ }
} else {
- REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
- txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN);
+ txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL);
+ if (freq == 2484) {
+ /* Enable channel spreading for channel 14 */
+ REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
+ txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
+ } else {
+ REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
+ txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN);
+ }
}
} else {
bMode = 0;
@@ -143,10 +135,15 @@ void ath9k_hw_ar9280_set_channel(struct ath_hw *ah,
case 1:
default:
aModeRefSel = 0;
+ /*
+ * Enable 2G (fractional) mode for channels
+ * which are 5MHz spaced.
+ */
fracMode = 1;
refDivA = 1;
channelSel = (freq * 0x8000) / 15;
+ /* RefDivA setting */
REG_RMW_FIELD(ah, AR_AN_SYNTH9,
AR_AN_SYNTH9_REFDIVA, refDivA);
@@ -168,12 +165,284 @@ void ath9k_hw_ar9280_set_channel(struct ath_hw *ah,
ah->curchan = chan;
ah->curchan_rad_index = -1;
+
+ return 0;
+}
+
+/**
+ * ath9k_hw_9280_spur_mitigate - convert baseband spur frequency
+ * @ah: atheros hardware structure
+ * @chan:
+ *
+ * For single-chip solutions. Converts to baseband spur frequency given the
+ * input channel frequency and compute register settings below.
+ */
+void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ int bb_spur = AR_NO_SPUR;
+ int freq;
+ int bin, cur_bin;
+ int bb_spur_off, spur_subchannel_sd;
+ int spur_freq_sd;
+ int spur_delta_phase;
+ int denominator;
+ int upper, lower, cur_vit_mask;
+ int tmp, newVal;
+ int i;
+ int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8,
+ AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60
+ };
+ int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10,
+ AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60
+ };
+ int inc[4] = { 0, 100, 0, 0 };
+ struct chan_centers centers;
+
+ int8_t mask_m[123];
+ int8_t mask_p[123];
+ int8_t mask_amt;
+ int tmp_mask;
+ int cur_bb_spur;
+ bool is2GHz = IS_CHAN_2GHZ(chan);
+
+ memset(&mask_m, 0, sizeof(int8_t) * 123);
+ memset(&mask_p, 0, sizeof(int8_t) * 123);
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+ freq = centers.synth_center;
+
+ ah->config.spurmode = SPUR_ENABLE_EEPROM;
+ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
+ cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz);
+
+ if (is2GHz)
+ cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ;
+ else
+ cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ;
+
+ if (AR_NO_SPUR == cur_bb_spur)
+ break;
+ cur_bb_spur = cur_bb_spur - freq;
+
+ if (IS_CHAN_HT40(chan)) {
+ if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) &&
+ (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) {
+ bb_spur = cur_bb_spur;
+ break;
+ }
+ } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) &&
+ (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) {
+ bb_spur = cur_bb_spur;
+ break;
+ }
+ }
+
+ if (AR_NO_SPUR == bb_spur) {
+ REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK,
+ AR_PHY_FORCE_CLKEN_CCK_MRC_MUX);
+ return;
+ } else {
+ REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK,
+ AR_PHY_FORCE_CLKEN_CCK_MRC_MUX);
+ }
+
+ bin = bb_spur * 320;
+
+ tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0));
+
+ newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI |
+ AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER |
+ AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
+ AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
+ REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), newVal);
+
+ newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL |
+ AR_PHY_SPUR_REG_ENABLE_MASK_PPM |
+ AR_PHY_SPUR_REG_MASK_RATE_SELECT |
+ AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI |
+ SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH));
+ REG_WRITE(ah, AR_PHY_SPUR_REG, newVal);
+
+ if (IS_CHAN_HT40(chan)) {
+ if (bb_spur < 0) {
+ spur_subchannel_sd = 1;
+ bb_spur_off = bb_spur + 10;
+ } else {
+ spur_subchannel_sd = 0;
+ bb_spur_off = bb_spur - 10;
+ }
+ } else {
+ spur_subchannel_sd = 0;
+ bb_spur_off = bb_spur;
+ }
+
+ if (IS_CHAN_HT40(chan))
+ spur_delta_phase =
+ ((bb_spur * 262144) /
+ 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE;
+ else
+ spur_delta_phase =
+ ((bb_spur * 524288) /
+ 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE;
+
+ denominator = IS_CHAN_2GHZ(chan) ? 44 : 40;
+ spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff;
+
+ newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC |
+ SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) |
+ SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE));
+ REG_WRITE(ah, AR_PHY_TIMING11, newVal);
+
+ newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S;
+ REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal);
+
+ cur_bin = -6000;
+ upper = bin + 100;
+ lower = bin - 100;
+
+ for (i = 0; i < 4; i++) {
+ int pilot_mask = 0;
+ int chan_mask = 0;
+ int bp = 0;
+ for (bp = 0; bp < 30; bp++) {
+ if ((cur_bin > lower) && (cur_bin < upper)) {
+ pilot_mask = pilot_mask | 0x1 << bp;
+ chan_mask = chan_mask | 0x1 << bp;
+ }
+ cur_bin += 100;
+ }
+ cur_bin += inc[i];
+ REG_WRITE(ah, pilot_mask_reg[i], pilot_mask);
+ REG_WRITE(ah, chan_mask_reg[i], chan_mask);
+ }
+
+ cur_vit_mask = 6100;
+ upper = bin + 120;
+ lower = bin - 120;
+
+ for (i = 0; i < 123; i++) {
+ if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) {
+
+ /* workaround for gcc bug #37014 */
+ volatile int tmp_v = abs(cur_vit_mask - bin);
+
+ if (tmp_v < 75)
+ mask_amt = 1;
+ else
+ mask_amt = 0;
+ if (cur_vit_mask < 0)
+ mask_m[abs(cur_vit_mask / 100)] = mask_amt;
+ else
+ mask_p[cur_vit_mask / 100] = mask_amt;
+ }
+ cur_vit_mask -= 100;
+ }
+
+ tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28)
+ | (mask_m[48] << 26) | (mask_m[49] << 24)
+ | (mask_m[50] << 22) | (mask_m[51] << 20)
+ | (mask_m[52] << 18) | (mask_m[53] << 16)
+ | (mask_m[54] << 14) | (mask_m[55] << 12)
+ | (mask_m[56] << 10) | (mask_m[57] << 8)
+ | (mask_m[58] << 6) | (mask_m[59] << 4)
+ | (mask_m[60] << 2) | (mask_m[61] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask);
+ REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask);
+
+ tmp_mask = (mask_m[31] << 28)
+ | (mask_m[32] << 26) | (mask_m[33] << 24)
+ | (mask_m[34] << 22) | (mask_m[35] << 20)
+ | (mask_m[36] << 18) | (mask_m[37] << 16)
+ | (mask_m[48] << 14) | (mask_m[39] << 12)
+ | (mask_m[40] << 10) | (mask_m[41] << 8)
+ | (mask_m[42] << 6) | (mask_m[43] << 4)
+ | (mask_m[44] << 2) | (mask_m[45] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask);
+
+ tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28)
+ | (mask_m[18] << 26) | (mask_m[18] << 24)
+ | (mask_m[20] << 22) | (mask_m[20] << 20)
+ | (mask_m[22] << 18) | (mask_m[22] << 16)
+ | (mask_m[24] << 14) | (mask_m[24] << 12)
+ | (mask_m[25] << 10) | (mask_m[26] << 8)
+ | (mask_m[27] << 6) | (mask_m[28] << 4)
+ | (mask_m[29] << 2) | (mask_m[30] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask);
+
+ tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28)
+ | (mask_m[2] << 26) | (mask_m[3] << 24)
+ | (mask_m[4] << 22) | (mask_m[5] << 20)
+ | (mask_m[6] << 18) | (mask_m[7] << 16)
+ | (mask_m[8] << 14) | (mask_m[9] << 12)
+ | (mask_m[10] << 10) | (mask_m[11] << 8)
+ | (mask_m[12] << 6) | (mask_m[13] << 4)
+ | (mask_m[14] << 2) | (mask_m[15] << 0);
+ REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask);
+
+ tmp_mask = (mask_p[15] << 28)
+ | (mask_p[14] << 26) | (mask_p[13] << 24)
+ | (mask_p[12] << 22) | (mask_p[11] << 20)
+ | (mask_p[10] << 18) | (mask_p[9] << 16)
+ | (mask_p[8] << 14) | (mask_p[7] << 12)
+ | (mask_p[6] << 10) | (mask_p[5] << 8)
+ | (mask_p[4] << 6) | (mask_p[3] << 4)
+ | (mask_p[2] << 2) | (mask_p[1] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask);
+
+ tmp_mask = (mask_p[30] << 28)
+ | (mask_p[29] << 26) | (mask_p[28] << 24)
+ | (mask_p[27] << 22) | (mask_p[26] << 20)
+ | (mask_p[25] << 18) | (mask_p[24] << 16)
+ | (mask_p[23] << 14) | (mask_p[22] << 12)
+ | (mask_p[21] << 10) | (mask_p[20] << 8)
+ | (mask_p[19] << 6) | (mask_p[18] << 4)
+ | (mask_p[17] << 2) | (mask_p[16] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask);
+
+ tmp_mask = (mask_p[45] << 28)
+ | (mask_p[44] << 26) | (mask_p[43] << 24)
+ | (mask_p[42] << 22) | (mask_p[41] << 20)
+ | (mask_p[40] << 18) | (mask_p[39] << 16)
+ | (mask_p[38] << 14) | (mask_p[37] << 12)
+ | (mask_p[36] << 10) | (mask_p[35] << 8)
+ | (mask_p[34] << 6) | (mask_p[33] << 4)
+ | (mask_p[32] << 2) | (mask_p[31] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask);
+
+ tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28)
+ | (mask_p[59] << 26) | (mask_p[58] << 24)
+ | (mask_p[57] << 22) | (mask_p[56] << 20)
+ | (mask_p[55] << 18) | (mask_p[54] << 16)
+ | (mask_p[53] << 14) | (mask_p[52] << 12)
+ | (mask_p[51] << 10) | (mask_p[50] << 8)
+ | (mask_p[49] << 6) | (mask_p[48] << 4)
+ | (mask_p[47] << 2) | (mask_p[46] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask);
}
-static void
-ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32,
- u32 numBits, u32 firstBit,
- u32 column)
+/* All code below is for non single-chip solutions */
+
+/**
+ * ath9k_phy_modify_rx_buffer() - perform analog swizzling of parameters
+ * @rfbuf:
+ * @reg32:
+ * @numBits:
+ * @firstBit:
+ * @column:
+ *
+ * Performs analog "swizzling" of parameters into their location.
+ * Used on external AR2133/AR5133 radios.
+ */
+static void ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32,
+ u32 numBits, u32 firstBit,
+ u32 column)
{
u32 tmp32, mask, arrayEntry, lastBit;
int32_t bitPosition, bitsLeft;
@@ -197,26 +466,466 @@ ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32,
}
}
-bool
-ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan,
- u16 modesIndex)
+/*
+ * Fix on 2.4 GHz band for orientation sensitivity issue by increasing
+ * rf_pwd_icsyndiv.
+ *
+ * Theoretical Rules:
+ * if 2 GHz band
+ * if forceBiasAuto
+ * if synth_freq < 2412
+ * bias = 0
+ * else if 2412 <= synth_freq <= 2422
+ * bias = 1
+ * else // synth_freq > 2422
+ * bias = 2
+ * else if forceBias > 0
+ * bias = forceBias & 7
+ * else
+ * no change, use value from ini file
+ * else
+ * no change, invalid band
+ *
+ * 1st Mod:
+ * 2422 also uses value of 2
+ * <approved>
+ *
+ * 2nd Mod:
+ * Less than 2412 uses value of 0, 2412 and above uses value of 2
+ */
+static void ath9k_hw_force_bias(struct ath_hw *ah, u16 synth_freq)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ u32 tmp_reg;
+ int reg_writes = 0;
+ u32 new_bias = 0;
+
+ if (!AR_SREV_5416(ah) || synth_freq >= 3000) {
+ return;
+ }
+
+ BUG_ON(AR_SREV_9280_10_OR_LATER(ah));
+
+ if (synth_freq < 2412)
+ new_bias = 0;
+ else if (synth_freq < 2422)
+ new_bias = 1;
+ else
+ new_bias = 2;
+
+ /* pre-reverse this field */
+ tmp_reg = ath9k_hw_reverse_bits(new_bias, 3);
+
+ ath_print(common, ATH_DBG_CONFIG,
+ "Force rf_pwd_icsyndiv to %1d on %4d\n",
+ new_bias, synth_freq);
+
+ /* swizzle rf_pwd_icsyndiv */
+ ath9k_phy_modify_rx_buffer(ah->analogBank6Data, tmp_reg, 3, 181, 3);
+
+ /* write Bank 6 with new params */
+ REG_WRITE_RF_ARRAY(&ah->iniBank6, ah->analogBank6Data, reg_writes);
+}
+
+/**
+ * ath9k_hw_set_channel - tune to a channel on the external AR2133/AR5133 radios
+ * @ah: atheros hardware stucture
+ * @chan:
+ *
+ * For the external AR2133/AR5133 radios, takes the MHz channel value and set
+ * the channel value. Assumes writes enabled to analog bus and bank6 register
+ * cache in ah->analogBank6Data.
+ */
+int ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ u32 channelSel = 0;
+ u32 bModeSynth = 0;
+ u32 aModeRefSel = 0;
+ u32 reg32 = 0;
+ u16 freq;
+ struct chan_centers centers;
+
+ ath9k_hw_get_channel_centers(ah, chan, &centers);
+ freq = centers.synth_center;
+
+ if (freq < 4800) {
+ u32 txctl;
+
+ if (((freq - 2192) % 5) == 0) {
+ channelSel = ((freq - 672) * 2 - 3040) / 10;
+ bModeSynth = 0;
+ } else if (((freq - 2224) % 5) == 0) {
+ channelSel = ((freq - 704) * 2 - 3040) / 10;
+ bModeSynth = 1;
+ } else {
+ ath_print(common, ATH_DBG_FATAL,
+ "Invalid channel %u MHz\n", freq);
+ return -EINVAL;
+ }
+
+ channelSel = (channelSel << 2) & 0xff;
+ channelSel = ath9k_hw_reverse_bits(channelSel, 8);
+
+ txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL);
+ if (freq == 2484) {
+
+ REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
+ txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
+ } else {
+ REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
+ txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN);
+ }
+
+ } else if ((freq % 20) == 0 && freq >= 5120) {
+ channelSel =
+ ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8);
+ aModeRefSel = ath9k_hw_reverse_bits(1, 2);
+ } else if ((freq % 10) == 0) {
+ channelSel =
+ ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8);
+ if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah))
+ aModeRefSel = ath9k_hw_reverse_bits(2, 2);
+ else
+ aModeRefSel = ath9k_hw_reverse_bits(1, 2);
+ } else if ((freq % 5) == 0) {
+ channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8);
+ aModeRefSel = ath9k_hw_reverse_bits(1, 2);
+ } else {
+ ath_print(common, ATH_DBG_FATAL,
+ "Invalid channel %u MHz\n", freq);
+ return -EINVAL;
+ }
+
+ ath9k_hw_force_bias(ah, freq);
+
+ reg32 =
+ (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) |
+ (1 << 5) | 0x1;
+
+ REG_WRITE(ah, AR_PHY(0x37), reg32);
+
+ ah->curchan = chan;
+ ah->curchan_rad_index = -1;
+
+ return 0;
+}
+
+/**
+ * ath9k_hw_spur_mitigate - convert baseband spur frequency for external radios
+ * @ah: atheros hardware structure
+ * @chan:
+ *
+ * For non single-chip solutions. Converts to baseband spur frequency given the
+ * input channel frequency and compute register settings below.
+ */
+void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ int bb_spur = AR_NO_SPUR;
+ int bin, cur_bin;
+ int spur_freq_sd;
+ int spur_delta_phase;
+ int denominator;
+ int upper, lower, cur_vit_mask;
+ int tmp, new;
+ int i;
+ int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8,
+ AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60
+ };
+ int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10,
+ AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60
+ };
+ int inc[4] = { 0, 100, 0, 0 };
+
+ int8_t mask_m[123];
+ int8_t mask_p[123];
+ int8_t mask_amt;
+ int tmp_mask;
+ int cur_bb_spur;
+ bool is2GHz = IS_CHAN_2GHZ(chan);
+
+ memset(&mask_m, 0, sizeof(int8_t) * 123);
+ memset(&mask_p, 0, sizeof(int8_t) * 123);
+
+ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
+ cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz);
+ if (AR_NO_SPUR == cur_bb_spur)
+ break;
+ cur_bb_spur = cur_bb_spur - (chan->channel * 10);
+ if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) {
+ bb_spur = cur_bb_spur;
+ break;
+ }
+ }
+
+ if (AR_NO_SPUR == bb_spur)
+ return;
+
+ bin = bb_spur * 32;
+
+ tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0));
+ new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI |
+ AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER |
+ AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
+ AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
+
+ REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), new);
+
+ new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL |
+ AR_PHY_SPUR_REG_ENABLE_MASK_PPM |
+ AR_PHY_SPUR_REG_MASK_RATE_SELECT |
+ AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI |
+ SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH));
+ REG_WRITE(ah, AR_PHY_SPUR_REG, new);
+
+ spur_delta_phase = ((bb_spur * 524288) / 100) &
+ AR_PHY_TIMING11_SPUR_DELTA_PHASE;
+
+ denominator = IS_CHAN_2GHZ(chan) ? 440 : 400;
+ spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff;
+
+ new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC |
+ SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) |
+ SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE));
+ REG_WRITE(ah, AR_PHY_TIMING11, new);
+
+ cur_bin = -6000;
+ upper = bin + 100;
+ lower = bin - 100;
+
+ for (i = 0; i < 4; i++) {
+ int pilot_mask = 0;
+ int chan_mask = 0;
+ int bp = 0;
+ for (bp = 0; bp < 30; bp++) {
+ if ((cur_bin > lower) && (cur_bin < upper)) {
+ pilot_mask = pilot_mask | 0x1 << bp;
+ chan_mask = chan_mask | 0x1 << bp;
+ }
+ cur_bin += 100;
+ }
+ cur_bin += inc[i];
+ REG_WRITE(ah, pilot_mask_reg[i], pilot_mask);
+ REG_WRITE(ah, chan_mask_reg[i], chan_mask);
+ }
+
+ cur_vit_mask = 6100;
+ upper = bin + 120;
+ lower = bin - 120;
+
+ for (i = 0; i < 123; i++) {
+ if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) {
+
+ /* workaround for gcc bug #37014 */
+ volatile int tmp_v = abs(cur_vit_mask - bin);
+
+ if (tmp_v < 75)
+ mask_amt = 1;
+ else
+ mask_amt = 0;
+ if (cur_vit_mask < 0)
+ mask_m[abs(cur_vit_mask / 100)] = mask_amt;
+ else
+ mask_p[cur_vit_mask / 100] = mask_amt;
+ }
+ cur_vit_mask -= 100;
+ }
+
+ tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28)
+ | (mask_m[48] << 26) | (mask_m[49] << 24)
+ | (mask_m[50] << 22) | (mask_m[51] << 20)
+ | (mask_m[52] << 18) | (mask_m[53] << 16)
+ | (mask_m[54] << 14) | (mask_m[55] << 12)
+ | (mask_m[56] << 10) | (mask_m[57] << 8)
+ | (mask_m[58] << 6) | (mask_m[59] << 4)
+ | (mask_m[60] << 2) | (mask_m[61] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask);
+ REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask);
+
+ tmp_mask = (mask_m[31] << 28)
+ | (mask_m[32] << 26) | (mask_m[33] << 24)
+ | (mask_m[34] << 22) | (mask_m[35] << 20)
+ | (mask_m[36] << 18) | (mask_m[37] << 16)
+ | (mask_m[48] << 14) | (mask_m[39] << 12)
+ | (mask_m[40] << 10) | (mask_m[41] << 8)
+ | (mask_m[42] << 6) | (mask_m[43] << 4)
+ | (mask_m[44] << 2) | (mask_m[45] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask);
+
+ tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28)
+ | (mask_m[18] << 26) | (mask_m[18] << 24)
+ | (mask_m[20] << 22) | (mask_m[20] << 20)
+ | (mask_m[22] << 18) | (mask_m[22] << 16)
+ | (mask_m[24] << 14) | (mask_m[24] << 12)
+ | (mask_m[25] << 10) | (mask_m[26] << 8)
+ | (mask_m[27] << 6) | (mask_m[28] << 4)
+ | (mask_m[29] << 2) | (mask_m[30] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask);
+
+ tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28)
+ | (mask_m[2] << 26) | (mask_m[3] << 24)
+ | (mask_m[4] << 22) | (mask_m[5] << 20)
+ | (mask_m[6] << 18) | (mask_m[7] << 16)
+ | (mask_m[8] << 14) | (mask_m[9] << 12)
+ | (mask_m[10] << 10) | (mask_m[11] << 8)
+ | (mask_m[12] << 6) | (mask_m[13] << 4)
+ | (mask_m[14] << 2) | (mask_m[15] << 0);
+ REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask);
+
+ tmp_mask = (mask_p[15] << 28)
+ | (mask_p[14] << 26) | (mask_p[13] << 24)
+ | (mask_p[12] << 22) | (mask_p[11] << 20)
+ | (mask_p[10] << 18) | (mask_p[9] << 16)
+ | (mask_p[8] << 14) | (mask_p[7] << 12)
+ | (mask_p[6] << 10) | (mask_p[5] << 8)
+ | (mask_p[4] << 6) | (mask_p[3] << 4)
+ | (mask_p[2] << 2) | (mask_p[1] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask);
+
+ tmp_mask = (mask_p[30] << 28)
+ | (mask_p[29] << 26) | (mask_p[28] << 24)
+ | (mask_p[27] << 22) | (mask_p[26] << 20)
+ | (mask_p[25] << 18) | (mask_p[24] << 16)
+ | (mask_p[23] << 14) | (mask_p[22] << 12)
+ | (mask_p[21] << 10) | (mask_p[20] << 8)
+ | (mask_p[19] << 6) | (mask_p[18] << 4)
+ | (mask_p[17] << 2) | (mask_p[16] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask);
+
+ tmp_mask = (mask_p[45] << 28)
+ | (mask_p[44] << 26) | (mask_p[43] << 24)
+ | (mask_p[42] << 22) | (mask_p[41] << 20)
+ | (mask_p[40] << 18) | (mask_p[39] << 16)
+ | (mask_p[38] << 14) | (mask_p[37] << 12)
+ | (mask_p[36] << 10) | (mask_p[35] << 8)
+ | (mask_p[34] << 6) | (mask_p[33] << 4)
+ | (mask_p[32] << 2) | (mask_p[31] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask);
+
+ tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28)
+ | (mask_p[59] << 26) | (mask_p[58] << 24)
+ | (mask_p[57] << 22) | (mask_p[56] << 20)
+ | (mask_p[55] << 18) | (mask_p[54] << 16)
+ | (mask_p[53] << 14) | (mask_p[52] << 12)
+ | (mask_p[51] << 10) | (mask_p[50] << 8)
+ | (mask_p[49] << 6) | (mask_p[48] << 4)
+ | (mask_p[47] << 2) | (mask_p[46] << 0);
+ REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask);
+ REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask);
+}
+
+/**
+ * ath9k_hw_rf_alloc_ext_banks - allocates banks for external radio programming
+ * @ah: atheros hardware structure
+ *
+ * Only required for older devices with external AR2133/AR5133 radios.
+ */
+int ath9k_hw_rf_alloc_ext_banks(struct ath_hw *ah)
+{
+#define ATH_ALLOC_BANK(bank, size) do { \
+ bank = kzalloc((sizeof(u32) * size), GFP_KERNEL); \
+ if (!bank) { \
+ ath_print(common, ATH_DBG_FATAL, \
+ "Cannot allocate RF banks\n"); \
+ return -ENOMEM; \
+ } \
+ } while (0);
+
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ BUG_ON(AR_SREV_9280_10_OR_LATER(ah));
+
+ ATH_ALLOC_BANK(ah->analogBank0Data, ah->iniBank0.ia_rows);
+ ATH_ALLOC_BANK(ah->analogBank1Data, ah->iniBank1.ia_rows);
+ ATH_ALLOC_BANK(ah->analogBank2Data, ah->iniBank2.ia_rows);
+ ATH_ALLOC_BANK(ah->analogBank3Data, ah->iniBank3.ia_rows);
+ ATH_ALLOC_BANK(ah->analogBank6Data, ah->iniBank6.ia_rows);
+ ATH_ALLOC_BANK(ah->analogBank6TPCData, ah->iniBank6TPC.ia_rows);
+ ATH_ALLOC_BANK(ah->analogBank7Data, ah->iniBank7.ia_rows);
+ ATH_ALLOC_BANK(ah->addac5416_21,
+ ah->iniAddac.ia_rows * ah->iniAddac.ia_columns);
+ ATH_ALLOC_BANK(ah->bank6Temp, ah->iniBank6.ia_rows);
+
+ return 0;
+#undef ATH_ALLOC_BANK
+}
+
+
+/**
+ * ath9k_hw_rf_free_ext_banks - Free memory for analog bank scratch buffers
+ * @ah: atheros hardware struture
+ * For the external AR2133/AR5133 radios banks.
+ */
+void
+ath9k_hw_rf_free_ext_banks(struct ath_hw *ah)
+{
+#define ATH_FREE_BANK(bank) do { \
+ kfree(bank); \
+ bank = NULL; \
+ } while (0);
+
+ BUG_ON(AR_SREV_9280_10_OR_LATER(ah));
+
+ ATH_FREE_BANK(ah->analogBank0Data);
+ ATH_FREE_BANK(ah->analogBank1Data);
+ ATH_FREE_BANK(ah->analogBank2Data);
+ ATH_FREE_BANK(ah->analogBank3Data);
+ ATH_FREE_BANK(ah->analogBank6Data);
+ ATH_FREE_BANK(ah->analogBank6TPCData);
+ ATH_FREE_BANK(ah->analogBank7Data);
+ ATH_FREE_BANK(ah->addac5416_21);
+ ATH_FREE_BANK(ah->bank6Temp);
+
+#undef ATH_FREE_BANK
+}
+
+/* *
+ * ath9k_hw_set_rf_regs - programs rf registers based on EEPROM
+ * @ah: atheros hardware structure
+ * @chan:
+ * @modesIndex:
+ *
+ * Used for the external AR2133/AR5133 radios.
+ *
+ * Reads the EEPROM header info from the device structure and programs
+ * all rf registers. This routine requires access to the analog
+ * rf device. This is not required for single-chip devices.
+ */
+bool ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan,
+ u16 modesIndex)
{
u32 eepMinorRev;
u32 ob5GHz = 0, db5GHz = 0;
u32 ob2GHz = 0, db2GHz = 0;
int regWrites = 0;
+ /*
+ * Software does not need to program bank data
+ * for single chip devices, that is AR9280 or anything
+ * after that.
+ */
if (AR_SREV_9280_10_OR_LATER(ah))
return true;
+ /* Setup rf parameters */
eepMinorRev = ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV);
+ /* Setup Bank 0 Write */
RF_BANK_SETUP(ah->analogBank0Data, &ah->iniBank0, 1);
+ /* Setup Bank 1 Write */
RF_BANK_SETUP(ah->analogBank1Data, &ah->iniBank1, 1);
+ /* Setup Bank 2 Write */
RF_BANK_SETUP(ah->analogBank2Data, &ah->iniBank2, 1);
+ /* Setup Bank 6 Write */
RF_BANK_SETUP(ah->analogBank3Data, &ah->iniBank3,
modesIndex);
{
@@ -227,6 +936,7 @@ ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan,
}
}
+ /* Only the 5 or 2 GHz OB/DB need to be set for a mode */
if (eepMinorRev >= 2) {
if (IS_CHAN_2GHZ(chan)) {
ob2GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_2);
@@ -245,8 +955,10 @@ ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan,
}
}
+ /* Setup Bank 7 Setup */
RF_BANK_SETUP(ah->analogBank7Data, &ah->iniBank7, 1);
+ /* Write Analog registers */
REG_WRITE_RF_ARRAY(&ah->iniBank0, ah->analogBank0Data,
regWrites);
REG_WRITE_RF_ARRAY(&ah->iniBank1, ah->analogBank1Data,
@@ -262,137 +974,3 @@ ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan,
return true;
}
-
-void
-ath9k_hw_rf_free(struct ath_hw *ah)
-{
-#define ATH_FREE_BANK(bank) do { \
- kfree(bank); \
- bank = NULL; \
- } while (0);
-
- ATH_FREE_BANK(ah->analogBank0Data);
- ATH_FREE_BANK(ah->analogBank1Data);
- ATH_FREE_BANK(ah->analogBank2Data);
- ATH_FREE_BANK(ah->analogBank3Data);
- ATH_FREE_BANK(ah->analogBank6Data);
- ATH_FREE_BANK(ah->analogBank6TPCData);
- ATH_FREE_BANK(ah->analogBank7Data);
- ATH_FREE_BANK(ah->addac5416_21);
- ATH_FREE_BANK(ah->bank6Temp);
-#undef ATH_FREE_BANK
-}
-
-bool ath9k_hw_init_rf(struct ath_hw *ah, int *status)
-{
- if (!AR_SREV_9280_10_OR_LATER(ah)) {
- ah->analogBank0Data =
- kzalloc((sizeof(u32) *
- ah->iniBank0.ia_rows), GFP_KERNEL);
- ah->analogBank1Data =
- kzalloc((sizeof(u32) *
- ah->iniBank1.ia_rows), GFP_KERNEL);
- ah->analogBank2Data =
- kzalloc((sizeof(u32) *
- ah->iniBank2.ia_rows), GFP_KERNEL);
- ah->analogBank3Data =
- kzalloc((sizeof(u32) *
- ah->iniBank3.ia_rows), GFP_KERNEL);
- ah->analogBank6Data =
- kzalloc((sizeof(u32) *
- ah->iniBank6.ia_rows), GFP_KERNEL);
- ah->analogBank6TPCData =
- kzalloc((sizeof(u32) *
- ah->iniBank6TPC.ia_rows), GFP_KERNEL);
- ah->analogBank7Data =
- kzalloc((sizeof(u32) *
- ah->iniBank7.ia_rows), GFP_KERNEL);
-
- if (ah->analogBank0Data == NULL
- || ah->analogBank1Data == NULL
- || ah->analogBank2Data == NULL
- || ah->analogBank3Data == NULL
- || ah->analogBank6Data == NULL
- || ah->analogBank6TPCData == NULL
- || ah->analogBank7Data == NULL) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Cannot allocate RF banks\n");
- *status = -ENOMEM;
- return false;
- }
-
- ah->addac5416_21 =
- kzalloc((sizeof(u32) *
- ah->iniAddac.ia_rows *
- ah->iniAddac.ia_columns), GFP_KERNEL);
- if (ah->addac5416_21 == NULL) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Cannot allocate addac5416_21\n");
- *status = -ENOMEM;
- return false;
- }
-
- ah->bank6Temp =
- kzalloc((sizeof(u32) *
- ah->iniBank6.ia_rows), GFP_KERNEL);
- if (ah->bank6Temp == NULL) {
- DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
- "Cannot allocate bank6Temp\n");
- *status = -ENOMEM;
- return false;
- }
- }
-
- return true;
-}
-
-void
-ath9k_hw_decrease_chain_power(struct ath_hw *ah, struct ath9k_channel *chan)
-{
- int i, regWrites = 0;
- u32 bank6SelMask;
- u32 *bank6Temp = ah->bank6Temp;
-
- switch (ah->config.diversity_control) {
- case ATH9K_ANT_FIXED_A:
- bank6SelMask =
- (ah->config.antenna_switch_swap & ANTSWAP_AB) ?
- REDUCE_CHAIN_0 : REDUCE_CHAIN_1;
- break;
- case ATH9K_ANT_FIXED_B:
- bank6SelMask =
- (ah->config.antenna_switch_swap & ANTSWAP_AB) ?
- REDUCE_CHAIN_1 : REDUCE_CHAIN_0;
- break;
- case ATH9K_ANT_VARIABLE:
- return;
- break;
- default:
- return;
- break;
- }
-
- for (i = 0; i < ah->iniBank6.ia_rows; i++)
- bank6Temp[i] = ah->analogBank6Data[i];
-
- REG_WRITE(ah, AR_PHY_BASE + 0xD8, bank6SelMask);
-
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 189, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 190, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 191, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 192, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 193, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 222, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 245, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 246, 0);
- ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 247, 0);
-
- REG_WRITE_RF_ARRAY(&ah->iniBank6, bank6Temp, regWrites);
-
- REG_WRITE(ah, AR_PHY_BASE + 0xD8, 0x00000053);
-#ifdef ALTER_SWITCH
- REG_WRITE(ah, PHY_SWITCH_CHAIN_0,
- (REG_READ(ah, PHY_SWITCH_CHAIN_0) & ~0x38)
- | ((REG_READ(ah, PHY_SWITCH_CHAIN_0) >> 3) & 0x38));
-#endif
-}
diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h
index dfda6f44464..31de27dc0c4 100644
--- a/drivers/net/wireless/ath/ath9k/phy.h
+++ b/drivers/net/wireless/ath/ath9k/phy.h
@@ -17,20 +17,23 @@
#ifndef PHY_H
#define PHY_H
-void ath9k_hw_ar9280_set_channel(struct ath_hw *ah,
- struct ath9k_channel
- *chan);
-bool ath9k_hw_set_channel(struct ath_hw *ah,
- struct ath9k_channel *chan);
-void ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex,
- u32 freqIndex, int regWrites);
+/* Common between single chip and non single-chip solutions */
+void ath9k_hw_write_regs(struct ath_hw *ah, u32 freqIndex, int regWrites);
+
+/* Single chip radio settings */
+int ath9k_hw_ar9280_set_channel(struct ath_hw *ah, struct ath9k_channel *chan);
+void ath9k_hw_9280_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan);
+
+/* Routines below are for non single-chip solutions */
+int ath9k_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan);
+void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan);
+
+int ath9k_hw_rf_alloc_ext_banks(struct ath_hw *ah);
+void ath9k_hw_rf_free_ext_banks(struct ath_hw *ah);
+
bool ath9k_hw_set_rf_regs(struct ath_hw *ah,
struct ath9k_channel *chan,
u16 modesIndex);
-void ath9k_hw_decrease_chain_power(struct ath_hw *ah,
- struct ath9k_channel *chan);
-bool ath9k_hw_init_rf(struct ath_hw *ah,
- int *status);
#define AR_PHY_BASE 0x9800
#define AR_PHY(_n) (AR_PHY_BASE + ((_n)<<2))
@@ -45,6 +48,7 @@ bool ath9k_hw_init_rf(struct ath_hw *ah,
#define AR_PHY_FC_DYN2040_EN 0x00000004
#define AR_PHY_FC_DYN2040_PRI_ONLY 0x00000008
#define AR_PHY_FC_DYN2040_PRI_CH 0x00000010
+/* For 25 MHz channel spacing -- not used but supported by hw */
#define AR_PHY_FC_DYN2040_EXT_CH 0x00000020
#define AR_PHY_FC_HT_EN 0x00000040
#define AR_PHY_FC_SHORT_GI_40 0x00000080
@@ -185,8 +189,20 @@ bool ath9k_hw_init_rf(struct ath_hw *ah,
#define AR_PHY_PLL_CTL_44_2133 0xeb
#define AR_PHY_PLL_CTL_40_2133 0xea
-#define AR_PHY_SPECTRAL_SCAN 0x9912
-#define AR_PHY_SPECTRAL_SCAN_ENABLE 0x1
+#define AR_PHY_SPECTRAL_SCAN 0x9910 /* AR9280 spectral scan configuration register */
+#define AR_PHY_SPECTRAL_SCAN_ENABLE 0x1
+#define AR_PHY_SPECTRAL_SCAN_ENA 0x00000001 /* Enable spectral scan, reg 68, bit 0 */
+#define AR_PHY_SPECTRAL_SCAN_ENA_S 0 /* Enable spectral scan, reg 68, bit 0 */
+#define AR_PHY_SPECTRAL_SCAN_ACTIVE 0x00000002 /* Activate spectral scan reg 68, bit 1*/
+#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S 1 /* Activate spectral scan reg 68, bit 1*/
+#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD 0x000000F0 /* Interval for FFT reports, reg 68, bits 4-7*/
+#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S 4
+#define AR_PHY_SPECTRAL_SCAN_PERIOD 0x0000FF00 /* Interval for FFT reports, reg 68, bits 8-15*/
+#define AR_PHY_SPECTRAL_SCAN_PERIOD_S 8
+#define AR_PHY_SPECTRAL_SCAN_COUNT 0x00FF0000 /* Number of reports, reg 68, bits 16-23*/
+#define AR_PHY_SPECTRAL_SCAN_COUNT_S 16
+#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x01000000 /* Short repeat, reg 68, bit 24*/
+#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24 /* Short repeat, reg 68, bit 24*/
#define AR_PHY_RX_DELAY 0x9914
#define AR_PHY_SEARCH_START_DELAY 0x9918
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 1895d63aad0..70fdb9d8db8 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -19,133 +19,92 @@
static const struct ath_rate_table ar5416_11na_ratetable = {
42,
+ 8, /* MCS start */
{
{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
- 5400, 0x0b, 0x00, 12,
- 0, 0, 0, 0, 0, 0 },
+ 5400, 0, 12, 0, 0, 0, 0, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
- 7800, 0x0f, 0x00, 18,
- 0, 1, 1, 1, 1, 0 },
+ 7800, 1, 18, 0, 1, 1, 1, 1 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
- 10000, 0x0a, 0x00, 24,
- 2, 2, 2, 2, 2, 0 },
+ 10000, 2, 24, 2, 2, 2, 2, 2 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
- 13900, 0x0e, 0x00, 36,
- 2, 3, 3, 3, 3, 0 },
+ 13900, 3, 36, 2, 3, 3, 3, 3 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
- 17300, 0x09, 0x00, 48,
- 4, 4, 4, 4, 4, 0 },
+ 17300, 4, 48, 4, 4, 4, 4, 4 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
- 23000, 0x0d, 0x00, 72,
- 4, 5, 5, 5, 5, 0 },
+ 23000, 5, 72, 4, 5, 5, 5, 5 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
- 27400, 0x08, 0x00, 96,
- 4, 6, 6, 6, 6, 0 },
+ 27400, 6, 96, 4, 6, 6, 6, 6 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
- 29300, 0x0c, 0x00, 108,
- 4, 7, 7, 7, 7, 0 },
+ 29300, 7, 108, 4, 7, 7, 7, 7 },
{ VALID_2040, VALID_2040, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
- 6400, 0x80, 0x00, 0,
- 0, 8, 24, 8, 24, 3216 },
+ 6400, 0, 0, 0, 8, 24, 8, 24 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
- 12700, 0x81, 0x00, 1,
- 2, 9, 25, 9, 25, 6434 },
+ 12700, 1, 1, 2, 9, 25, 9, 25 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
- 18800, 0x82, 0x00, 2,
- 2, 10, 26, 10, 26, 9650 },
+ 18800, 2, 2, 2, 10, 26, 10, 26 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
- 25000, 0x83, 0x00, 3,
- 4, 11, 27, 11, 27, 12868 },
+ 25000, 3, 3, 4, 11, 27, 11, 27 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
- 36700, 0x84, 0x00, 4,
- 4, 12, 28, 12, 28, 19304 },
+ 36700, 4, 4, 4, 12, 28, 12, 28 },
{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
- 48100, 0x85, 0x00, 5,
- 4, 13, 29, 13, 29, 25740 },
+ 48100, 5, 5, 4, 13, 29, 13, 29 },
{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
- 53500, 0x86, 0x00, 6,
- 4, 14, 30, 14, 30, 28956 },
+ 53500, 6, 6, 4, 14, 30, 14, 30 },
{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
- 59000, 0x87, 0x00, 7,
- 4, 15, 31, 15, 32, 32180 },
+ 59000, 7, 7, 4, 15, 31, 15, 32 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
- 12700, 0x88, 0x00,
- 8, 3, 16, 33, 16, 33, 6430 },
+ 12700, 8, 8, 3, 16, 33, 16, 33 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
- 24800, 0x89, 0x00, 9,
- 2, 17, 34, 17, 34, 12860 },
+ 24800, 9, 9, 2, 17, 34, 17, 34 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
- 36600, 0x8a, 0x00, 10,
- 2, 18, 35, 18, 35, 19300 },
+ 36600, 10, 10, 2, 18, 35, 18, 35 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
- 48100, 0x8b, 0x00, 11,
- 4, 19, 36, 19, 36, 25736 },
+ 48100, 11, 11, 4, 19, 36, 19, 36 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
- 69500, 0x8c, 0x00, 12,
- 4, 20, 37, 20, 37, 38600 },
+ 69500, 12, 12, 4, 20, 37, 20, 37 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
- 89500, 0x8d, 0x00, 13,
- 4, 21, 38, 21, 38, 51472 },
+ 89500, 13, 13, 4, 21, 38, 21, 38 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
- 98900, 0x8e, 0x00, 14,
- 4, 22, 39, 22, 39, 57890 },
+ 98900, 14, 14, 4, 22, 39, 22, 39 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
- 108300, 0x8f, 0x00, 15,
- 4, 23, 40, 23, 41, 64320 },
+ 108300, 15, 15, 4, 23, 40, 23, 41 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
- 13200, 0x80, 0x00, 0,
- 0, 8, 24, 24, 24, 6684 },
+ 13200, 0, 0, 0, 8, 24, 24, 24 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
- 25900, 0x81, 0x00, 1,
- 2, 9, 25, 25, 25, 13368 },
+ 25900, 1, 1, 2, 9, 25, 25, 25 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
- 38600, 0x82, 0x00, 2,
- 2, 10, 26, 26, 26, 20052 },
+ 38600, 2, 2, 2, 10, 26, 26, 26 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
- 49800, 0x83, 0x00, 3,
- 4, 11, 27, 27, 27, 26738 },
+ 49800, 3, 3, 4, 11, 27, 27, 27 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
- 72200, 0x84, 0x00, 4,
- 4, 12, 28, 28, 28, 40104 },
+ 72200, 4, 4, 4, 12, 28, 28, 28 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
- 92900, 0x85, 0x00, 5,
- 4, 13, 29, 29, 29, 53476 },
+ 92900, 5, 5, 4, 13, 29, 29, 29 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */
- 102700, 0x86, 0x00, 6,
- 4, 14, 30, 30, 30, 60156 },
+ 102700, 6, 6, 4, 14, 30, 30, 30 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
- 112000, 0x87, 0x00, 7,
- 4, 15, 31, 32, 32, 66840 },
+ 112000, 7, 7, 4, 15, 31, 32, 32 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
- 122000, 0x87, 0x00, 7,
- 4, 15, 31, 32, 32, 74200 },
+ 122000, 7, 7, 4, 15, 31, 32, 32 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
- 25800, 0x88, 0x00, 8,
- 0, 16, 33, 33, 33, 13360 },
+ 25800, 8, 8, 0, 16, 33, 33, 33 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
- 49800, 0x89, 0x00, 9,
- 2, 17, 34, 34, 34, 26720 },
+ 49800, 9, 9, 2, 17, 34, 34, 34 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
- 71900, 0x8a, 0x00, 10,
- 2, 18, 35, 35, 35, 40080 },
+ 71900, 10, 10, 2, 18, 35, 35, 35 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
- 92500, 0x8b, 0x00, 11,
- 4, 19, 36, 36, 36, 53440 },
+ 92500, 11, 11, 4, 19, 36, 36, 36 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
- 130300, 0x8c, 0x00, 12,
- 4, 20, 37, 37, 37, 80160 },
+ 130300, 12, 12, 4, 20, 37, 37, 37 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
- 162800, 0x8d, 0x00, 13,
- 4, 21, 38, 38, 38, 106880 },
+ 162800, 13, 13, 4, 21, 38, 38, 38 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
- 178200, 0x8e, 0x00, 14,
- 4, 22, 39, 39, 39, 120240 },
+ 178200, 14, 14, 4, 22, 39, 39, 39 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
- 192100, 0x8f, 0x00, 15,
- 4, 23, 40, 41, 41, 133600 },
+ 192100, 15, 15, 4, 23, 40, 41, 41 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
- 207000, 0x8f, 0x00, 15,
- 4, 23, 40, 41, 41, 148400 },
+ 207000, 15, 15, 4, 23, 40, 41, 41 },
},
50, /* probe interval */
WLAN_RC_HT_FLAG, /* Phy rates allowed initially */
@@ -156,177 +115,125 @@ static const struct ath_rate_table ar5416_11na_ratetable = {
static const struct ath_rate_table ar5416_11ng_ratetable = {
46,
+ 12, /* MCS start */
{
{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
- 900, 0x1b, 0x00, 2,
- 0, 0, 0, 0, 0, 0 },
+ 900, 0, 2, 0, 0, 0, 0, 0 },
{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
- 1900, 0x1a, 0x04, 4,
- 1, 1, 1, 1, 1, 0 },
+ 1900, 1, 4, 1, 1, 1, 1, 1 },
{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
- 4900, 0x19, 0x04, 11,
- 2, 2, 2, 2, 2, 0 },
+ 4900, 2, 11, 2, 2, 2, 2, 2 },
{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
- 8100, 0x18, 0x04, 22,
- 3, 3, 3, 3, 3, 0 },
+ 8100, 3, 22, 3, 3, 3, 3, 3 },
{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
- 5400, 0x0b, 0x00, 12,
- 4, 4, 4, 4, 4, 0 },
+ 5400, 4, 12, 4, 4, 4, 4, 4 },
{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
- 7800, 0x0f, 0x00, 18,
- 4, 5, 5, 5, 5, 0 },
+ 7800, 5, 18, 4, 5, 5, 5, 5 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
- 10100, 0x0a, 0x00, 24,
- 6, 6, 6, 6, 6, 0 },
+ 10100, 6, 24, 6, 6, 6, 6, 6 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
- 14100, 0x0e, 0x00, 36,
- 6, 7, 7, 7, 7, 0 },
+ 14100, 7, 36, 6, 7, 7, 7, 7 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
- 17700, 0x09, 0x00, 48,
- 8, 8, 8, 8, 8, 0 },
+ 17700, 8, 48, 8, 8, 8, 8, 8 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
- 23700, 0x0d, 0x00, 72,
- 8, 9, 9, 9, 9, 0 },
+ 23700, 9, 72, 8, 9, 9, 9, 9 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
- 27400, 0x08, 0x00, 96,
- 8, 10, 10, 10, 10, 0 },
+ 27400, 10, 96, 8, 10, 10, 10, 10 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
- 30900, 0x0c, 0x00, 108,
- 8, 11, 11, 11, 11, 0 },
+ 30900, 11, 108, 8, 11, 11, 11, 11 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
- 6400, 0x80, 0x00, 0,
- 4, 12, 28, 12, 28, 3216 },
+ 6400, 0, 0, 4, 12, 28, 12, 28 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
- 12700, 0x81, 0x00, 1,
- 6, 13, 29, 13, 29, 6434 },
+ 12700, 1, 1, 6, 13, 29, 13, 29 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
- 18800, 0x82, 0x00, 2,
- 6, 14, 30, 14, 30, 9650 },
+ 18800, 2, 2, 6, 14, 30, 14, 30 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
- 25000, 0x83, 0x00, 3,
- 8, 15, 31, 15, 31, 12868 },
+ 25000, 3, 3, 8, 15, 31, 15, 31 },
{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
- 36700, 0x84, 0x00, 4,
- 8, 16, 32, 16, 32, 19304 },
+ 36700, 4, 4, 8, 16, 32, 16, 32 },
{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
- 48100, 0x85, 0x00, 5,
- 8, 17, 33, 17, 33, 25740 },
+ 48100, 5, 5, 8, 17, 33, 17, 33 },
{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
- 53500, 0x86, 0x00, 6,
- 8, 18, 34, 18, 34, 28956 },
+ 53500, 6, 6, 8, 18, 34, 18, 34 },
{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
- 59000, 0x87, 0x00, 7,
- 8, 19, 35, 19, 36, 32180 },
+ 59000, 7, 7, 8, 19, 35, 19, 36 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
- 12700, 0x88, 0x00, 8,
- 4, 20, 37, 20, 37, 6430 },
+ 12700, 8, 8, 4, 20, 37, 20, 37 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
- 24800, 0x89, 0x00, 9,
- 6, 21, 38, 21, 38, 12860 },
+ 24800, 9, 9, 6, 21, 38, 21, 38 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
- 36600, 0x8a, 0x00, 10,
- 6, 22, 39, 22, 39, 19300 },
+ 36600, 10, 10, 6, 22, 39, 22, 39 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
- 48100, 0x8b, 0x00, 11,
- 8, 23, 40, 23, 40, 25736 },
+ 48100, 11, 11, 8, 23, 40, 23, 40 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
- 69500, 0x8c, 0x00, 12,
- 8, 24, 41, 24, 41, 38600 },
+ 69500, 12, 12, 8, 24, 41, 24, 41 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
- 89500, 0x8d, 0x00, 13,
- 8, 25, 42, 25, 42, 51472 },
+ 89500, 13, 13, 8, 25, 42, 25, 42 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
- 98900, 0x8e, 0x00, 14,
- 8, 26, 43, 26, 44, 57890 },
+ 98900, 14, 14, 8, 26, 43, 26, 44 },
{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
- 108300, 0x8f, 0x00, 15,
- 8, 27, 44, 27, 45, 64320 },
+ 108300, 15, 15, 8, 27, 44, 27, 45 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
- 13200, 0x80, 0x00, 0,
- 8, 12, 28, 28, 28, 6684 },
+ 13200, 0, 0, 8, 12, 28, 28, 28 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
- 25900, 0x81, 0x00, 1,
- 8, 13, 29, 29, 29, 13368 },
+ 25900, 1, 1, 8, 13, 29, 29, 29 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
- 38600, 0x82, 0x00, 2,
- 8, 14, 30, 30, 30, 20052 },
+ 38600, 2, 2, 8, 14, 30, 30, 30 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
- 49800, 0x83, 0x00, 3,
- 8, 15, 31, 31, 31, 26738 },
+ 49800, 3, 3, 8, 15, 31, 31, 31 },
{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
- 72200, 0x84, 0x00, 4,
- 8, 16, 32, 32, 32, 40104 },
+ 72200, 4, 4, 8, 16, 32, 32, 32 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
- 92900, 0x85, 0x00, 5,
- 8, 17, 33, 33, 33, 53476 },
+ 92900, 5, 5, 8, 17, 33, 33, 33 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */
- 102700, 0x86, 0x00, 6,
- 8, 18, 34, 34, 34, 60156 },
+ 102700, 6, 6, 8, 18, 34, 34, 34 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
- 112000, 0x87, 0x00, 7,
- 8, 19, 35, 36, 36, 66840 },
+ 112000, 7, 7, 8, 19, 35, 36, 36 },
{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
- 122000, 0x87, 0x00, 7,
- 8, 19, 35, 36, 36, 74200 },
+ 122000, 7, 7, 8, 19, 35, 36, 36 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
- 25800, 0x88, 0x00, 8,
- 8, 20, 37, 37, 37, 13360 },
+ 25800, 8, 8, 8, 20, 37, 37, 37 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
- 49800, 0x89, 0x00, 9,
- 8, 21, 38, 38, 38, 26720 },
+ 49800, 9, 9, 8, 21, 38, 38, 38 },
{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
- 71900, 0x8a, 0x00, 10,
- 8, 22, 39, 39, 39, 40080 },
+ 71900, 10, 10, 8, 22, 39, 39, 39 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
- 92500, 0x8b, 0x00, 11,
- 8, 23, 40, 40, 40, 53440 },
+ 92500, 11, 11, 8, 23, 40, 40, 40 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
- 130300, 0x8c, 0x00, 12,
- 8, 24, 41, 41, 41, 80160 },
+ 130300, 12, 12, 8, 24, 41, 41, 41 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
- 162800, 0x8d, 0x00, 13,
- 8, 25, 42, 42, 42, 106880 },
+ 162800, 13, 13, 8, 25, 42, 42, 42 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
- 178200, 0x8e, 0x00, 14,
- 8, 26, 43, 43, 43, 120240 },
+ 178200, 14, 14, 8, 26, 43, 43, 43 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
- 192100, 0x8f, 0x00, 15,
- 8, 27, 44, 45, 45, 133600 },
+ 192100, 15, 15, 8, 27, 44, 45, 45 },
{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
- 207000, 0x8f, 0x00, 15,
- 8, 27, 44, 45, 45, 148400 },
- },
+ 207000, 15, 15, 8, 27, 44, 45, 45 },
+ },
50, /* probe interval */
WLAN_RC_HT_FLAG, /* Phy rates allowed initially */
};
static const struct ath_rate_table ar5416_11a_ratetable = {
8,
+ 0,
{
{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
- 5400, 0x0b, 0x00, (0x80|12),
- 0, 0, 0 },
+ 5400, 0, 12, 0, 0, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
- 7800, 0x0f, 0x00, 18,
- 0, 1, 0 },
+ 7800, 1, 18, 0, 1, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
- 10000, 0x0a, 0x00, (0x80|24),
- 2, 2, 0 },
+ 10000, 2, 24, 2, 2, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
- 13900, 0x0e, 0x00, 36,
- 2, 3, 0 },
+ 13900, 3, 36, 2, 3, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
- 17300, 0x09, 0x00, (0x80|48),
- 4, 4, 0 },
+ 17300, 4, 48, 4, 4, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
- 23000, 0x0d, 0x00, 72,
- 4, 5, 0 },
+ 23000, 5, 72, 4, 5, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
- 27400, 0x08, 0x00, 96,
- 4, 6, 0 },
+ 27400, 6, 96, 4, 6, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
- 29300, 0x0c, 0x00, 108,
- 4, 7, 0 },
+ 29300, 7, 108, 4, 7, 0 },
},
50, /* probe interval */
0, /* Phy rates allowed initially */
@@ -334,48 +241,51 @@ static const struct ath_rate_table ar5416_11a_ratetable = {
static const struct ath_rate_table ar5416_11g_ratetable = {
12,
+ 0,
{
{ VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
- 900, 0x1b, 0x00, 2,
- 0, 0, 0 },
+ 900, 0, 2, 0, 0, 0 },
{ VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
- 1900, 0x1a, 0x04, 4,
- 1, 1, 0 },
+ 1900, 1, 4, 1, 1, 0 },
{ VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
- 4900, 0x19, 0x04, 11,
- 2, 2, 0 },
+ 4900, 2, 11, 2, 2, 0 },
{ VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
- 8100, 0x18, 0x04, 22,
- 3, 3, 0 },
+ 8100, 3, 22, 3, 3, 0 },
{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
- 5400, 0x0b, 0x00, 12,
- 4, 4, 0 },
+ 5400, 4, 12, 4, 4, 0 },
{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
- 7800, 0x0f, 0x00, 18,
- 4, 5, 0 },
+ 7800, 5, 18, 4, 5, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
- 10000, 0x0a, 0x00, 24,
- 6, 6, 0 },
+ 10000, 6, 24, 6, 6, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
- 13900, 0x0e, 0x00, 36,
- 6, 7, 0 },
+ 13900, 7, 36, 6, 7, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
- 17300, 0x09, 0x00, 48,
- 8, 8, 0 },
+ 17300, 8, 48, 8, 8, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
- 23000, 0x0d, 0x00, 72,
- 8, 9, 0 },
+ 23000, 9, 72, 8, 9, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
- 27400, 0x08, 0x00, 96,
- 8, 10, 0 },
+ 27400, 10, 96, 8, 10, 0 },
{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
- 29300, 0x0c, 0x00, 108,
- 8, 11, 0 },
+ 29300, 11, 108, 8, 11, 0 },
},
50, /* probe interval */
0, /* Phy rates allowed initially */
};
+static const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX] = {
+ [ATH9K_MODE_11A] = &ar5416_11a_ratetable,
+ [ATH9K_MODE_11G] = &ar5416_11g_ratetable,
+ [ATH9K_MODE_11NA_HT20] = &ar5416_11na_ratetable,
+ [ATH9K_MODE_11NG_HT20] = &ar5416_11ng_ratetable,
+ [ATH9K_MODE_11NA_HT40PLUS] = &ar5416_11na_ratetable,
+ [ATH9K_MODE_11NA_HT40MINUS] = &ar5416_11na_ratetable,
+ [ATH9K_MODE_11NG_HT40PLUS] = &ar5416_11ng_ratetable,
+ [ATH9K_MODE_11NG_HT40MINUS] = &ar5416_11ng_ratetable,
+};
+
+static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
+ struct ieee80211_tx_rate *rate);
+
static inline int8_t median(int8_t a, int8_t b, int8_t c)
{
if (a >= b) {
@@ -425,7 +335,7 @@ static void ath_rc_init_valid_txmask(struct ath_rate_priv *ath_rc_priv)
static inline void ath_rc_set_valid_txmask(struct ath_rate_priv *ath_rc_priv,
u8 index, int valid_tx_rate)
{
- ASSERT(index <= ath_rc_priv->rate_table_size);
+ BUG_ON(index > ath_rc_priv->rate_table_size);
ath_rc_priv->valid_rate_index[index] = valid_tx_rate ? 1 : 0;
}
@@ -534,7 +444,7 @@ static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv,
* capflag matches one of the validity
* (VALID/VALID_20/VALID_40) flags */
- if (((rate & 0x7F) == (dot11rate & 0x7F)) &&
+ if ((rate == dot11rate) &&
((valid & WLAN_RC_CAP_MODE(capflag)) ==
WLAN_RC_CAP_MODE(capflag)) &&
!WLAN_RC_PHY_HT(phy)) {
@@ -576,8 +486,7 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv,
u8 rate = rateset->rs_rates[i];
u8 dot11rate = rate_table->info[j].dot11rate;
- if (((rate & 0x7F) != (dot11rate & 0x7F)) ||
- !WLAN_RC_PHY_HT(phy) ||
+ if ((rate != dot11rate) || !WLAN_RC_PHY_HT(phy) ||
!WLAN_RC_PHY_HT_VALID(valid, capflag))
continue;
@@ -696,18 +605,20 @@ static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table,
u8 tries, u8 rix, int rtsctsenable)
{
rate->count = tries;
- rate->idx = rix;
+ rate->idx = rate_table->info[rix].ratecode;
if (txrc->short_preamble)
rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
if (txrc->rts || rtsctsenable)
rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
- if (WLAN_RC_PHY_40(rate_table->info[rix].phy))
- rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
- if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy))
- rate->flags |= IEEE80211_TX_RC_SHORT_GI;
- if (WLAN_RC_PHY_HT(rate_table->info[rix].phy))
+
+ if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) {
rate->flags |= IEEE80211_TX_RC_MCS;
+ if (WLAN_RC_PHY_40(rate_table->info[rix].phy))
+ rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy))
+ rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+ }
}
static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
@@ -720,7 +631,7 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
/* get the cix for the lowest valid rix */
for (i = 3; i >= 0; i--) {
if (rates[i].count && (rates[i].idx >= 0)) {
- rix = rates[i].idx;
+ rix = ath_rc_get_rateindex(rate_table, &rates[i]);
break;
}
}
@@ -859,12 +770,12 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
static bool ath_rc_update_per(struct ath_softc *sc,
const struct ath_rate_table *rate_table,
struct ath_rate_priv *ath_rc_priv,
- struct ath_tx_info_priv *tx_info_priv,
+ struct ieee80211_tx_info *tx_info,
int tx_rate, int xretries, int retries,
u32 now_msec)
{
bool state_change = false;
- int count;
+ int count, n_bad_frames;
u8 last_per;
static u32 nretry_to_per_lookup[10] = {
100 * 0 / 1,
@@ -880,6 +791,7 @@ static bool ath_rc_update_per(struct ath_softc *sc,
};
last_per = ath_rc_priv->per[tx_rate];
+ n_bad_frames = tx_info->status.ampdu_len - tx_info->status.ampdu_ack_len;
if (xretries) {
if (xretries == 1) {
@@ -907,7 +819,7 @@ static bool ath_rc_update_per(struct ath_softc *sc,
if (retries >= count)
retries = count - 1;
- if (tx_info_priv->n_bad_frames) {
+ if (n_bad_frames) {
/* new_PER = 7/8*old_PER + 1/8*(currentPER)
* Assuming that n_frames is not 0. The current PER
* from the retries is 100 * retries / (retries+1),
@@ -920,14 +832,14 @@ static bool ath_rc_update_per(struct ath_softc *sc,
* the above PER. The expression below is a
* simplified version of the sum of these two terms.
*/
- if (tx_info_priv->n_frames > 0) {
- int n_frames, n_bad_frames;
+ if (tx_info->status.ampdu_len > 0) {
+ int n_frames, n_bad_tries;
u8 cur_per, new_per;
- n_bad_frames = retries * tx_info_priv->n_frames +
- tx_info_priv->n_bad_frames;
- n_frames = tx_info_priv->n_frames * (retries + 1);
- cur_per = (100 * n_bad_frames / n_frames) >> 3;
+ n_bad_tries = retries * tx_info->status.ampdu_len +
+ n_bad_frames;
+ n_frames = tx_info->status.ampdu_len * (retries + 1);
+ cur_per = (100 * n_bad_tries / n_frames) >> 3;
new_per = (u8)(last_per - (last_per >> 3) + cur_per);
ath_rc_priv->per[tx_rate] = new_per;
}
@@ -943,8 +855,7 @@ static bool ath_rc_update_per(struct ath_softc *sc,
* this was a probe. Otherwise, ignore the probe.
*/
if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) {
- if (retries > 0 || 2 * tx_info_priv->n_bad_frames >
- tx_info_priv->n_frames) {
+ if (retries > 0 || 2 * n_bad_frames > tx_info->status.ampdu_len) {
/*
* Since we probed with just a single attempt,
* any retries means the probe failed. Also,
@@ -969,7 +880,7 @@ static bool ath_rc_update_per(struct ath_softc *sc,
* Since this probe succeeded, we allow the next
* probe twice as soon. This allows the maxRate
* to move up faster if the probes are
- * succesful.
+ * successful.
*/
ath_rc_priv->probe_time =
now_msec - rate_table->probe_interval / 2;
@@ -1003,7 +914,7 @@ static bool ath_rc_update_per(struct ath_softc *sc,
static void ath_rc_update_ht(struct ath_softc *sc,
struct ath_rate_priv *ath_rc_priv,
- struct ath_tx_info_priv *tx_info_priv,
+ struct ieee80211_tx_info *tx_info,
int tx_rate, int xretries, int retries)
{
u32 now_msec = jiffies_to_msecs(jiffies);
@@ -1020,7 +931,7 @@ static void ath_rc_update_ht(struct ath_softc *sc,
/* Update PER first */
state_change = ath_rc_update_per(sc, rate_table, ath_rc_priv,
- tx_info_priv, tx_rate, xretries,
+ tx_info, tx_rate, xretries,
retries, now_msec);
/*
@@ -1080,15 +991,19 @@ static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table,
{
int rix;
+ if (!(rate->flags & IEEE80211_TX_RC_MCS))
+ return rate->idx;
+
+ rix = rate->idx + rate_table->mcs_start;
if ((rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) &&
(rate->flags & IEEE80211_TX_RC_SHORT_GI))
- rix = rate_table->info[rate->idx].ht_index;
+ rix = rate_table->info[rix].ht_index;
else if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
- rix = rate_table->info[rate->idx].sgi_index;
+ rix = rate_table->info[rix].sgi_index;
else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
- rix = rate_table->info[rate->idx].cw40index;
+ rix = rate_table->info[rix].cw40index;
else
- rix = rate_table->info[rate->idx].base_index;
+ rix = rate_table->info[rix].base_index;
return rix;
}
@@ -1098,7 +1013,6 @@ static void ath_rc_tx_status(struct ath_softc *sc,
struct ieee80211_tx_info *tx_info,
int final_ts_idx, int xretries, int long_retry)
{
- struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
const struct ath_rate_table *rate_table;
struct ieee80211_tx_rate *rates = tx_info->status.rates;
u8 flags;
@@ -1124,9 +1038,8 @@ static void ath_rc_tx_status(struct ath_softc *sc,
return;
rix = ath_rc_get_rateindex(rate_table, &rates[i]);
- ath_rc_update_ht(sc, ath_rc_priv,
- tx_info_priv, rix,
- xretries ? 1 : 2,
+ ath_rc_update_ht(sc, ath_rc_priv, tx_info,
+ rix, xretries ? 1 : 2,
rates[i].count);
}
}
@@ -1149,8 +1062,7 @@ static void ath_rc_tx_status(struct ath_softc *sc,
return;
rix = ath_rc_get_rateindex(rate_table, &rates[i]);
- ath_rc_update_ht(sc, ath_rc_priv, tx_info_priv, rix,
- xretries, long_retry);
+ ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry);
}
static const
@@ -1160,6 +1072,7 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc,
bool is_cw_40)
{
int mode = 0;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
switch(band) {
case IEEE80211_BAND_2GHZ:
@@ -1177,14 +1090,17 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc,
mode = ATH9K_MODE_11NA_HT40PLUS;
break;
default:
- DPRINTF(sc, ATH_DBG_CONFIG, "Invalid band\n");
+ ath_print(common, ATH_DBG_CONFIG, "Invalid band\n");
return NULL;
}
BUG_ON(mode >= ATH9K_MODE_MAX);
- DPRINTF(sc, ATH_DBG_CONFIG, "Choosing rate table for mode: %d\n", mode);
- return sc->hw_rate_table[mode];
+ ath_print(common, ATH_DBG_CONFIG,
+ "Choosing rate table for mode: %d\n", mode);
+
+ sc->cur_rate_mode = mode;
+ return hw_rate_table[mode];
}
static void ath_rc_init(struct ath_softc *sc,
@@ -1194,14 +1110,10 @@ static void ath_rc_init(struct ath_softc *sc,
const struct ath_rate_table *rate_table)
{
struct ath_rateset *rateset = &ath_rc_priv->neg_rates;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates;
u8 i, j, k, hi = 0, hthi = 0;
- if (!rate_table) {
- DPRINTF(sc, ATH_DBG_FATAL, "Rate table not initialized\n");
- return;
- }
-
/* Initial rate table size. Will change depending
* on the working rate set */
ath_rc_priv->rate_table_size = RATE_TABLE_SIZE;
@@ -1239,7 +1151,7 @@ static void ath_rc_init(struct ath_softc *sc,
ath_rc_priv->rate_table_size = hi + 1;
ath_rc_priv->rate_max_phy = 0;
- ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE);
+ BUG_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE);
for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) {
for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) {
@@ -1253,16 +1165,17 @@ static void ath_rc_init(struct ath_softc *sc,
ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1];
}
- ASSERT(ath_rc_priv->rate_table_size <= RATE_TABLE_SIZE);
- ASSERT(k <= RATE_TABLE_SIZE);
+ BUG_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE);
+ BUG_ON(k > RATE_TABLE_SIZE);
ath_rc_priv->max_valid_rate = k;
ath_rc_sort_validrates(rate_table, ath_rc_priv);
ath_rc_priv->rate_max_phy = ath_rc_priv->valid_rate_index[k-4];
sc->cur_rate_table = rate_table;
- DPRINTF(sc, ATH_DBG_CONFIG, "RC Initialized with capabilities: 0x%x\n",
- ath_rc_priv->ht_cap);
+ ath_print(common, ATH_DBG_CONFIG,
+ "RC Initialized with capabilities: 0x%x\n",
+ ath_rc_priv->ht_cap);
}
static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -1296,44 +1209,52 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
{
struct ath_softc *sc = priv;
struct ath_rate_priv *ath_rc_priv = priv_sta;
- struct ath_tx_info_priv *tx_info_priv = NULL;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr;
- int final_ts_idx, tx_status = 0, is_underrun = 0;
+ int final_ts_idx = 0, tx_status = 0, is_underrun = 0;
+ int long_retry = 0;
__le16 fc;
+ int i;
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
- tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
- final_ts_idx = tx_info_priv->tx.ts_rateindex;
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ struct ieee80211_tx_rate *rate = &tx_info->status.rates[i];
+ if (!rate->count)
+ break;
+
+ final_ts_idx = i;
+ long_retry = rate->count - 1;
+ }
if (!priv_sta || !ieee80211_is_data(fc) ||
- !tx_info_priv->update_rc)
- goto exit;
+ !(tx_info->pad[0] & ATH_TX_INFO_UPDATE_RC))
+ return;
- if (tx_info_priv->tx.ts_status & ATH9K_TXERR_FILT)
- goto exit;
+ if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED)
+ return;
/*
- * If underrun error is seen assume it as an excessive retry only
- * if prefetch trigger level have reached the max (0x3f for 5416)
- * Adjust the long retry as if the frame was tried hw->max_rate_tries
- * times. This affects how ratectrl updates PER for the failed rate.
+ * If an underrun error is seen assume it as an excessive retry only
+ * if max frame trigger level has been reached (2 KB for singel stream,
+ * and 4 KB for dual stream). Adjust the long retry as if the frame was
+ * tried hw->max_rate_tries times to affect how ratectrl updates PER for
+ * the failed rate. In case of congestion on the bus penalizing these
+ * type of underruns should help hardware actually transmit new frames
+ * successfully by eventually preferring slower rates. This itself
+ * should also alleviate congestion on the bus.
*/
- if (tx_info_priv->tx.ts_flags &
- (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) &&
- ((sc->sc_ah->tx_trig_level) >= ath_rc_priv->tx_triglevel_max)) {
+ if ((tx_info->pad[0] & ATH_TX_INFO_UNDERRUN) &&
+ (sc->sc_ah->tx_trig_level >= ath_rc_priv->tx_triglevel_max)) {
tx_status = 1;
is_underrun = 1;
}
- if ((tx_info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) ||
- (tx_info_priv->tx.ts_status & ATH9K_TXERR_FIFO))
+ if (tx_info->pad[0] & ATH_TX_INFO_XRETRY)
tx_status = 1;
ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status,
- (is_underrun) ? sc->hw->max_rate_tries :
- tx_info_priv->tx.ts_longretry);
+ (is_underrun) ? sc->hw->max_rate_tries : long_retry);
/* Check if aggregation has to be enabled for this tid */
if (conf_is_ht(&sc->hw->conf) &&
@@ -1347,13 +1268,12 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
an = (struct ath_node *)sta->drv_priv;
if(ath_tx_aggr_check(sc, an, tid))
- ieee80211_start_tx_ba_session(sc->hw, hdr->addr1, tid);
+ ieee80211_start_tx_ba_session(sta, tid);
}
}
- ath_debug_stat_rc(sc, skb);
-exit:
- kfree(tx_info_priv);
+ ath_debug_stat_rc(sc, ath_rc_get_rateindex(sc->cur_rate_table,
+ &tx_info->status.rates[final_ts_idx]));
}
static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
@@ -1361,7 +1281,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
{
struct ath_softc *sc = priv;
struct ath_rate_priv *ath_rc_priv = priv_sta;
- const struct ath_rate_table *rate_table = NULL;
+ const struct ath_rate_table *rate_table;
bool is_cw40, is_sgi40;
int i, j = 0;
@@ -1393,11 +1313,9 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
(sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) ||
(sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)) {
rate_table = ath_choose_rate_table(sc, sband->band,
- sta->ht_cap.ht_supported,
- is_cw40);
- } else if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
- /* cur_rate_table would be set on init through config() */
- rate_table = sc->cur_rate_table;
+ sta->ht_cap.ht_supported, is_cw40);
+ } else {
+ rate_table = hw_rate_table[sc->cur_rate_mode];
}
ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi40);
@@ -1438,9 +1356,10 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband,
oper_cw40, oper_sgi40);
ath_rc_init(sc, priv_sta, sband, sta, rate_table);
- DPRINTF(sc, ATH_DBG_CONFIG,
- "Operating HT Bandwidth changed to: %d\n",
- sc->hw->conf.channel_type);
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
+ "Operating HT Bandwidth changed to: %d\n",
+ sc->hw->conf.channel_type);
+ sc->cur_rate_table = hw_rate_table[sc->cur_rate_mode];
}
}
}
@@ -1463,8 +1382,8 @@ static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp
rate_priv = kzalloc(sizeof(struct ath_rate_priv), gfp);
if (!rate_priv) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to allocate private rc structure\n");
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "Unable to allocate private rc structure\n");
return NULL;
}
@@ -1493,26 +1412,6 @@ static struct rate_control_ops ath_rate_ops = {
.free_sta = ath_rate_free_sta,
};
-void ath_rate_attach(struct ath_softc *sc)
-{
- sc->hw_rate_table[ATH9K_MODE_11A] =
- &ar5416_11a_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11G] =
- &ar5416_11g_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NA_HT20] =
- &ar5416_11na_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NG_HT20] =
- &ar5416_11ng_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] =
- &ar5416_11na_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] =
- &ar5416_11na_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] =
- &ar5416_11ng_ratetable;
- sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] =
- &ar5416_11ng_ratetable;
-}
-
int ath_rate_control_register(void)
{
return ieee80211_rate_control_register(&ath_rate_ops);
diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h
index fa21a628ddd..9eb96f50699 100644
--- a/drivers/net/wireless/ath/ath9k/rc.h
+++ b/drivers/net/wireless/ath/ath9k/rc.h
@@ -19,6 +19,8 @@
#ifndef RC_H
#define RC_H
+#include "hw.h"
+
struct ath_softc;
#define ATH_RATE_MAX 30
@@ -102,6 +104,7 @@ enum {
*/
struct ath_rate_table {
int rate_cnt;
+ int mcs_start;
struct {
int valid;
int valid_single_stream;
@@ -109,14 +112,12 @@ struct ath_rate_table {
u32 ratekbps;
u32 user_ratekbps;
u8 ratecode;
- u8 short_preamble;
u8 dot11rate;
u8 ctrl_rate;
u8 base_index;
u8 cw40index;
u8 sgi_index;
u8 ht_index;
- u32 max_4ms_framelen;
} info[RATE_TABLE_SIZE];
u32 probe_interval;
u8 initial_ratemax;
@@ -165,26 +166,18 @@ struct ath_rate_priv {
struct ath_rate_softc *asc;
};
+#define ATH_TX_INFO_FRAME_TYPE_INTERNAL (1 << 0)
+#define ATH_TX_INFO_FRAME_TYPE_PAUSE (1 << 1)
+#define ATH_TX_INFO_UPDATE_RC (1 << 2)
+#define ATH_TX_INFO_XRETRY (1 << 3)
+#define ATH_TX_INFO_UNDERRUN (1 << 4)
+
enum ath9k_internal_frame_type {
ATH9K_NOT_INTERNAL,
ATH9K_INT_PAUSE,
ATH9K_INT_UNPAUSE
};
-struct ath_tx_info_priv {
- struct ath_wiphy *aphy;
- struct ath_tx_status tx;
- int n_frames;
- int n_bad_frames;
- bool update_rc;
- enum ath9k_internal_frame_type frame_type;
-};
-
-#define ATH_TX_INFO_PRIV(tx_info) \
- ((struct ath_tx_info_priv *)((tx_info)->rate_driver_data[0]))
-
-void ath_rate_attach(struct ath_softc *sc);
-u8 ath_rate_findrateix(struct ath_softc *sc, u8 dot11_rate);
int ath_rate_control_register(void);
void ath_rate_control_unregister(void);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index ec0abf82399..477365e5ae6 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -48,6 +48,7 @@ static struct ieee80211_hw * ath_get_virt_hw(struct ath_softc *sc,
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_desc *ds;
struct sk_buff *skb;
@@ -59,14 +60,16 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
/* virtual addr of the beginning of the buffer. */
skb = bf->bf_mpdu;
- ASSERT(skb != NULL);
+ BUG_ON(skb == NULL);
ds->ds_vdata = skb->data;
- /* setup rx descriptors. The rx.bufsize here tells the harware
+ /*
+ * setup rx descriptors. The rx_bufsize here tells the hardware
* how much data it can DMA to us and that we are prepared
- * to process */
+ * to process
+ */
ath9k_hw_setuprxdesc(ah, ds,
- sc->rx.bufsize,
+ common->rx_bufsize,
0);
if (sc->rx.rxlink == NULL)
@@ -86,192 +89,11 @@ static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
sc->rx.rxotherant = 0;
}
-/*
- * Extend 15-bit time stamp from rx descriptor to
- * a full 64-bit TSF using the current h/w TSF.
-*/
-static u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp)
-{
- u64 tsf;
-
- tsf = ath9k_hw_gettsf64(sc->sc_ah);
- if ((tsf & 0x7fff) < rstamp)
- tsf -= 0x8000;
- return (tsf & ~0x7fff) | rstamp;
-}
-
-/*
- * For Decrypt or Demic errors, we only mark packet status here and always push
- * up the frame up to let mac80211 handle the actual error case, be it no
- * decryption key or real decryption error. This let us keep statistics there.
- */
-static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
- struct ieee80211_rx_status *rx_status, bool *decrypt_error,
- struct ath_softc *sc)
-{
- struct ieee80211_hdr *hdr;
- u8 ratecode;
- __le16 fc;
- struct ieee80211_hw *hw;
- struct ieee80211_sta *sta;
- struct ath_node *an;
- int last_rssi = ATH_RSSI_DUMMY_MARKER;
-
-
- hdr = (struct ieee80211_hdr *)skb->data;
- fc = hdr->frame_control;
- memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
- hw = ath_get_virt_hw(sc, hdr);
-
- if (ds->ds_rxstat.rs_more) {
- /*
- * Frame spans multiple descriptors; this cannot happen yet
- * as we don't support jumbograms. If not in monitor mode,
- * discard the frame. Enable this if you want to see
- * error frames in Monitor mode.
- */
- if (sc->sc_ah->opmode != NL80211_IFTYPE_MONITOR)
- goto rx_next;
- } else if (ds->ds_rxstat.rs_status != 0) {
- if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC)
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
- if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY)
- goto rx_next;
-
- if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) {
- *decrypt_error = true;
- } else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) {
- if (ieee80211_is_ctl(fc))
- /*
- * Sometimes, we get invalid
- * MIC failures on valid control frames.
- * Remove these mic errors.
- */
- ds->ds_rxstat.rs_status &= ~ATH9K_RXERR_MIC;
- else
- rx_status->flag |= RX_FLAG_MMIC_ERROR;
- }
- /*
- * Reject error frames with the exception of
- * decryption and MIC failures. For monitor mode,
- * we also ignore the CRC error.
- */
- if (sc->sc_ah->opmode == NL80211_IFTYPE_MONITOR) {
- if (ds->ds_rxstat.rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
- ATH9K_RXERR_CRC))
- goto rx_next;
- } else {
- if (ds->ds_rxstat.rs_status &
- ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
- goto rx_next;
- }
- }
- }
-
- ratecode = ds->ds_rxstat.rs_rate;
-
- if (ratecode & 0x80) {
- /* HT rate */
- rx_status->flag |= RX_FLAG_HT;
- if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040)
- rx_status->flag |= RX_FLAG_40MHZ;
- if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI)
- rx_status->flag |= RX_FLAG_SHORT_GI;
- rx_status->rate_idx = ratecode & 0x7f;
- } else {
- int i = 0, cur_band, n_rates;
-
- cur_band = hw->conf.channel->band;
- n_rates = sc->sbands[cur_band].n_bitrates;
-
- for (i = 0; i < n_rates; i++) {
- if (sc->sbands[cur_band].bitrates[i].hw_value ==
- ratecode) {
- rx_status->rate_idx = i;
- break;
- }
-
- if (sc->sbands[cur_band].bitrates[i].hw_value_short ==
- ratecode) {
- rx_status->rate_idx = i;
- rx_status->flag |= RX_FLAG_SHORTPRE;
- break;
- }
- }
- }
-
- rcu_read_lock();
- sta = ieee80211_find_sta(sc->hw, hdr->addr2);
- if (sta) {
- an = (struct ath_node *) sta->drv_priv;
- if (ds->ds_rxstat.rs_rssi != ATH9K_RSSI_BAD &&
- !ds->ds_rxstat.rs_moreaggr)
- ATH_RSSI_LPF(an->last_rssi, ds->ds_rxstat.rs_rssi);
- last_rssi = an->last_rssi;
- }
- rcu_read_unlock();
-
- if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
- ds->ds_rxstat.rs_rssi = ATH_EP_RND(last_rssi,
- ATH_RSSI_EP_MULTIPLIER);
- if (ds->ds_rxstat.rs_rssi < 0)
- ds->ds_rxstat.rs_rssi = 0;
- else if (ds->ds_rxstat.rs_rssi > 127)
- ds->ds_rxstat.rs_rssi = 127;
-
- /* Update Beacon RSSI, this is used by ANI. */
- if (ieee80211_is_beacon(fc))
- sc->sc_ah->stats.avgbrssi = ds->ds_rxstat.rs_rssi;
-
- rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
- rx_status->band = hw->conf.channel->band;
- rx_status->freq = hw->conf.channel->center_freq;
- rx_status->noise = sc->ani.noise_floor;
- rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + ds->ds_rxstat.rs_rssi;
- rx_status->antenna = ds->ds_rxstat.rs_antenna;
-
- /*
- * Theory for reporting quality:
- *
- * At a hardware RSSI of 45 you will be able to use MCS 7 reliably.
- * At a hardware RSSI of 45 you will be able to use MCS 15 reliably.
- * At a hardware RSSI of 35 you should be able use 54 Mbps reliably.
- *
- * MCS 7 is the highets MCS index usable by a 1-stream device.
- * MCS 15 is the highest MCS index usable by a 2-stream device.
- *
- * All ath9k devices are either 1-stream or 2-stream.
- *
- * How many bars you see is derived from the qual reporting.
- *
- * A more elaborate scheme can be used here but it requires tables
- * of SNR/throughput for each possible mode used. For the MCS table
- * you can refer to the wireless wiki:
- *
- * http://wireless.kernel.org/en/developers/Documentation/ieee80211/802.11n
- *
- */
- if (conf_is_ht(&hw->conf))
- rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 45;
- else
- rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 35;
-
- /* rssi can be more than 45 though, anything above that
- * should be considered at 100% */
- if (rx_status->qual > 100)
- rx_status->qual = 100;
-
- rx_status->flag |= RX_FLAG_TSFT;
-
- return 1;
-rx_next:
- return 0;
-}
-
static void ath_opmode_init(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+
u32 rfilt, mfilt[2];
/* configure rx filter */
@@ -280,13 +102,13 @@ static void ath_opmode_init(struct ath_softc *sc)
/* configure bssid mask */
if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
- ath9k_hw_setbssidmask(sc);
+ ath_hw_setbssidmask(common);
/* configure operational mode */
ath9k_hw_setopmode(ah);
/* Handle any link-level address change. */
- ath9k_hw_setmac(ah, sc->sc_ah->macaddr);
+ ath9k_hw_setmac(ah, common->macaddr);
/* calculate and install multicast filter */
mfilt[0] = mfilt[1] = ~0;
@@ -295,6 +117,7 @@ static void ath_opmode_init(struct ath_softc *sc)
int ath_rx_init(struct ath_softc *sc, int nbufs)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct sk_buff *skb;
struct ath_buf *bf;
int error = 0;
@@ -303,24 +126,24 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
sc->sc_flags &= ~SC_OP_RXFLUSH;
spin_lock_init(&sc->rx.rxbuflock);
- sc->rx.bufsize = roundup(IEEE80211_MAX_MPDU_LEN,
- min(sc->common.cachelsz, (u16)64));
+ common->rx_bufsize = roundup(IEEE80211_MAX_MPDU_LEN,
+ min(common->cachelsz, (u16)64));
- DPRINTF(sc, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n",
- sc->common.cachelsz, sc->rx.bufsize);
+ ath_print(common, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n",
+ common->cachelsz, common->rx_bufsize);
/* Initialize rx descriptors */
error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf,
"rx", nbufs, 1);
if (error != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "failed to allocate rx descriptors: %d\n", error);
+ ath_print(common, ATH_DBG_FATAL,
+ "failed to allocate rx descriptors: %d\n", error);
goto err;
}
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
- skb = ath_rxbuf_alloc(&sc->common, sc->rx.bufsize, GFP_KERNEL);
+ skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_KERNEL);
if (skb == NULL) {
error = -ENOMEM;
goto err;
@@ -328,14 +151,14 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
bf->bf_mpdu = skb;
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
- sc->rx.bufsize,
+ common->rx_bufsize,
DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(sc->dev,
bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
- DPRINTF(sc, ATH_DBG_FATAL,
- "dma_mapping_error() on RX init\n");
+ ath_print(common, ATH_DBG_FATAL,
+ "dma_mapping_error() on RX init\n");
error = -ENOMEM;
goto err;
}
@@ -352,6 +175,8 @@ err:
void ath_rx_cleanup(struct ath_softc *sc)
{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb;
struct ath_buf *bf;
@@ -359,7 +184,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
skb = bf->bf_mpdu;
if (skb) {
dma_unmap_single(sc->dev, bf->bf_buf_addr,
- sc->rx.bufsize, DMA_FROM_DEVICE);
+ common->rx_bufsize, DMA_FROM_DEVICE);
dev_kfree_skb(skb);
}
}
@@ -420,7 +245,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
else
rfilt |= ATH9K_RX_FILTER_BEACON;
- if (sc->rx.rxfilter & FIF_PSPOLL)
+ if ((AR_SREV_9280_10_OR_LATER(sc->sc_ah) ||
+ AR_SREV_9285_10_OR_LATER(sc->sc_ah)) &&
+ (sc->sc_ah->opmode == NL80211_IFTYPE_AP) &&
+ (sc->rx.rxfilter & FIF_PSPOLL))
rfilt |= ATH9K_RX_FILTER_PSPOLL;
if (conf_is_ht(&sc->hw->conf))
@@ -527,20 +355,22 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
{
struct ieee80211_mgmt *mgmt;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
if (skb->len < 24 + 8 + 2 + 2)
return;
mgmt = (struct ieee80211_mgmt *)skb->data;
- if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0)
+ if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0)
return; /* not from our current AP */
sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
if (sc->sc_flags & SC_OP_BEACON_SYNC) {
sc->sc_flags &= ~SC_OP_BEACON_SYNC;
- DPRINTF(sc, ATH_DBG_PS, "Reconfigure Beacon timers based on "
- "timestamp from the AP\n");
+ ath_print(common, ATH_DBG_PS,
+ "Reconfigure Beacon timers based on "
+ "timestamp from the AP\n");
ath_beacon_config(sc, NULL);
}
@@ -552,8 +382,8 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
* a backup trigger for returning into NETWORK SLEEP state,
* so we are waiting for it as well.
*/
- DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating "
- "buffered broadcast/multicast frame(s)\n");
+ ath_print(common, ATH_DBG_PS, "Received DTIM beacon indicating "
+ "buffered broadcast/multicast frame(s)\n");
sc->sc_flags |= SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_BEACON;
return;
}
@@ -565,13 +395,15 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
* been delivered.
*/
sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB;
- DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n");
+ ath_print(common, ATH_DBG_PS,
+ "PS wait for CAB frames timed out\n");
}
}
static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
hdr = (struct ieee80211_hdr *)skb->data;
@@ -589,14 +421,15 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
* point.
*/
sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB;
- DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to "
- "sleep\n");
+ ath_print(common, ATH_DBG_PS,
+ "All PS CAB frames received, back to sleep\n");
} else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) &&
!is_multicast_ether_addr(hdr->addr1) &&
!ieee80211_has_morefrags(hdr->frame_control)) {
sc->sc_flags &= ~SC_OP_WAIT_FOR_PSPOLL_DATA;
- DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having "
- "received PS-Poll data (0x%x)\n",
+ ath_print(common, ATH_DBG_PS,
+ "Going back to sleep after having received "
+ "PS-Poll data (0x%x)\n",
sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
SC_OP_WAIT_FOR_CAB |
SC_OP_WAIT_FOR_PSPOLL_DATA |
@@ -604,8 +437,9 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
}
}
-static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
+static void ath_rx_send_to_mac80211(struct ieee80211_hw *hw,
+ struct ath_softc *sc, struct sk_buff *skb,
+ struct ieee80211_rx_status *rxs)
{
struct ieee80211_hdr *hdr;
@@ -625,19 +459,14 @@ static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb,
if (aphy == NULL)
continue;
nskb = skb_copy(skb, GFP_ATOMIC);
- if (nskb) {
- memcpy(IEEE80211_SKB_RXCB(nskb), rx_status,
- sizeof(*rx_status));
- ieee80211_rx(aphy->hw, nskb);
- }
+ if (!nskb)
+ continue;
+ ieee80211_rx(aphy->hw, nskb);
}
- memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status));
ieee80211_rx(sc->hw, skb);
- } else {
+ } else
/* Deliver unicast frames based on receiver address */
- memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status));
- ieee80211_rx(ath_get_virt_hw(sc, hdr), skb);
- }
+ ieee80211_rx(hw, skb);
}
int ath_rx_tasklet(struct ath_softc *sc, int flush)
@@ -648,14 +477,20 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
struct ath_buf *bf;
struct ath_desc *ds;
+ struct ath_rx_status *rx_stats;
struct sk_buff *skb = NULL, *requeue_skb;
- struct ieee80211_rx_status rx_status;
+ struct ieee80211_rx_status *rxs;
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ /*
+ * The hw can techncically differ from common->hw when using ath9k
+ * virtual wiphy so to account for that we iterate over the active
+ * wiphys and find the appropriate wiphy and therefore hw.
+ */
+ struct ieee80211_hw *hw = NULL;
struct ieee80211_hdr *hdr;
- int hdrlen, padsize, retval;
+ int retval;
bool decrypt_error = false;
- u8 keyix;
- __le16 fc;
spin_lock_bh(&sc->rx.rxbuflock);
@@ -727,9 +562,15 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
* 2. requeueing the same buffer to h/w
*/
dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr,
- sc->rx.bufsize,
+ common->rx_bufsize,
DMA_FROM_DEVICE);
+ hdr = (struct ieee80211_hdr *) skb->data;
+ rxs = IEEE80211_SKB_RXCB(skb);
+
+ hw = ath_get_virt_hw(sc, hdr);
+ rx_stats = &ds->ds_rxstat;
+
/*
* If we're asked to flush receive queue, directly
* chain it back at the queue without processing it.
@@ -737,19 +578,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
if (flush)
goto requeue;
- if (!ds->ds_rxstat.rs_datalen)
- goto requeue;
-
- /* The status portion of the descriptor could get corrupted. */
- if (sc->rx.bufsize < ds->ds_rxstat.rs_datalen)
- goto requeue;
-
- if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc))
+ retval = ath9k_cmn_rx_skb_preprocess(common, hw, skb, rx_stats,
+ rxs, &decrypt_error);
+ if (retval)
goto requeue;
/* Ensure we always have an skb to requeue once we are done
* processing the current buffer's skb */
- requeue_skb = ath_rxbuf_alloc(&sc->common, sc->rx.bufsize, GFP_ATOMIC);
+ requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
/* If there is no memory we ignore the current RX'd frame,
* tell hardware it can give us a new frame using the old
@@ -760,60 +596,26 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
/* Unmap the frame */
dma_unmap_single(sc->dev, bf->bf_buf_addr,
- sc->rx.bufsize,
+ common->rx_bufsize,
DMA_FROM_DEVICE);
- skb_put(skb, ds->ds_rxstat.rs_datalen);
-
- /* see if any padding is done by the hw and remove it */
- hdr = (struct ieee80211_hdr *)skb->data;
- hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- fc = hdr->frame_control;
-
- /* The MAC header is padded to have 32-bit boundary if the
- * packet payload is non-zero. The general calculation for
- * padsize would take into account odd header lengths:
- * padsize = (4 - hdrlen % 4) % 4; However, since only
- * even-length headers are used, padding can only be 0 or 2
- * bytes and we can optimize this a bit. In addition, we must
- * not try to remove padding from short control frames that do
- * not have payload. */
- padsize = hdrlen & 3;
- if (padsize && hdrlen >= 24) {
- memmove(skb->data + padsize, skb->data, hdrlen);
- skb_pull(skb, padsize);
- }
-
- keyix = ds->ds_rxstat.rs_keyix;
+ skb_put(skb, rx_stats->rs_datalen);
- if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) {
- rx_status.flag |= RX_FLAG_DECRYPTED;
- } else if (ieee80211_has_protected(fc)
- && !decrypt_error && skb->len >= hdrlen + 4) {
- keyix = skb->data[hdrlen + 3] >> 6;
-
- if (test_bit(keyix, sc->keymap))
- rx_status.flag |= RX_FLAG_DECRYPTED;
- }
- if (ah->sw_mgmt_crypto &&
- (rx_status.flag & RX_FLAG_DECRYPTED) &&
- ieee80211_is_mgmt(fc)) {
- /* Use software decrypt for management frames. */
- rx_status.flag &= ~RX_FLAG_DECRYPTED;
- }
+ ath9k_cmn_rx_skb_postprocess(common, skb, rx_stats,
+ rxs, decrypt_error);
/* We will now give hardware our shiny new allocated skb */
bf->bf_mpdu = requeue_skb;
bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
- sc->rx.bufsize,
- DMA_FROM_DEVICE);
+ common->rx_bufsize,
+ DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(sc->dev,
bf->bf_buf_addr))) {
dev_kfree_skb_any(requeue_skb);
bf->bf_mpdu = NULL;
- DPRINTF(sc, ATH_DBG_FATAL,
- "dma_mapping_error() on RX\n");
- ath_rx_send_to_mac80211(sc, skb, &rx_status);
+ ath_print(common, ATH_DBG_FATAL,
+ "dma_mapping_error() on RX\n");
+ ath_rx_send_to_mac80211(hw, sc, skb, rxs);
break;
}
bf->bf_dmacontext = bf->bf_buf_addr;
@@ -824,7 +626,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
*/
if (sc->rx.defant != ds->ds_rxstat.rs_antenna) {
if (++sc->rx.rxotherant >= 3)
- ath_setdefantenna(sc, ds->ds_rxstat.rs_antenna);
+ ath_setdefantenna(sc, rx_stats->rs_antenna);
} else {
sc->rx.rxotherant = 0;
}
@@ -834,7 +636,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
SC_OP_WAIT_FOR_PSPOLL_DATA)))
ath_rx_ps(sc, skb);
- ath_rx_send_to_mac80211(sc, skb, &rx_status);
+ ath_rx_send_to_mac80211(hw, sc, skb, rxs);
requeue:
list_move_tail(&bf->list, &sc->rx.rxbuf);
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index d83b77f821e..8e653fb937a 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -17,6 +17,8 @@
#ifndef REG_H
#define REG_H
+#include "../reg.h"
+
#define AR_CR 0x0008
#define AR_CR_RXE 0x00000004
#define AR_CR_RXD 0x00000020
@@ -969,10 +971,10 @@ enum {
#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_S 4
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7
+#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00000400
+#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 10
#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB 0x00001000
#define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S 12
-#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00001000
-#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 1
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15
#define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000
@@ -1330,13 +1332,22 @@ enum {
#define AR_MCAST_FIL0 0x8040
#define AR_MCAST_FIL1 0x8044
+/*
+ * AR_DIAG_SW - Register which can be used for diagnostics and testing purposes.
+ *
+ * The force RX abort (AR_DIAG_RX_ABORT, bit 25) can be used in conjunction with
+ * RX block (AR_DIAG_RX_DIS, bit 5) to help fast channel change to shut down
+ * receive. The force RX abort bit will kill any frame which is currently being
+ * transferred between the MAC and baseband. The RX block bit (AR_DIAG_RX_DIS)
+ * will prevent any new frames from getting started.
+ */
#define AR_DIAG_SW 0x8048
#define AR_DIAG_CACHE_ACK 0x00000001
#define AR_DIAG_ACK_DIS 0x00000002
#define AR_DIAG_CTS_DIS 0x00000004
#define AR_DIAG_ENCRYPT_DIS 0x00000008
#define AR_DIAG_DECRYPT_DIS 0x00000010
-#define AR_DIAG_RX_DIS 0x00000020
+#define AR_DIAG_RX_DIS 0x00000020 /* RX block */
#define AR_DIAG_LOOP_BACK 0x00000040
#define AR_DIAG_CORR_FCS 0x00000080
#define AR_DIAG_CHAN_INFO 0x00000100
@@ -1345,12 +1356,12 @@ enum {
#define AR_DIAG_FRAME_NV0 0x00020000
#define AR_DIAG_OBS_PT_SEL1 0x000C0000
#define AR_DIAG_OBS_PT_SEL1_S 18
-#define AR_DIAG_FORCE_RX_CLEAR 0x00100000
+#define AR_DIAG_FORCE_RX_CLEAR 0x00100000 /* force rx_clear high */
#define AR_DIAG_IGNORE_VIRT_CS 0x00200000
#define AR_DIAG_FORCE_CH_IDLE_HIGH 0x00400000
#define AR_DIAG_EIFS_CTRL_ENA 0x00800000
#define AR_DIAG_DUAL_CHAIN_INFO 0x01000000
-#define AR_DIAG_RX_ABORT 0x02000000
+#define AR_DIAG_RX_ABORT 0x02000000 /* Force RX abort */
#define AR_DIAG_SATURATE_CYCLE_CNT 0x04000000
#define AR_DIAG_OBS_PT_SEL2 0x08000000
#define AR_DIAG_RX_CLEAR_CTL_LOW 0x10000000
@@ -1421,9 +1432,6 @@ enum {
#define AR_SLEEP2_BEACON_TIMEOUT 0xFFE00000
#define AR_SLEEP2_BEACON_TIMEOUT_S 21
-#define AR_BSSMSKL 0x80e0
-#define AR_BSSMSKU 0x80e4
-
#define AR_TPC 0x80e8
#define AR_TPC_ACK 0x0000003f
#define AR_TPC_ACK_S 0x00
@@ -1705,4 +1713,7 @@ enum {
#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24)
#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28)
+#define AR9271_CORE_CLOCK 117 /* clock to 117Mhz */
+#define AR9271_TARGET_BAUD_RATE 19200 /* 115200 */
+
#endif
diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c
index 19b88f8177f..cd26caaf44e 100644
--- a/drivers/net/wireless/ath/ath9k/virtual.c
+++ b/drivers/net/wireless/ath/ath9k/virtual.c
@@ -40,6 +40,7 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath9k_vif_iter_data iter_data;
int i, j;
u8 mask[ETH_ALEN];
@@ -51,7 +52,7 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
*/
iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC);
if (iter_data.addr) {
- memcpy(iter_data.addr, sc->sc_ah->macaddr, ETH_ALEN);
+ memcpy(iter_data.addr, common->macaddr, ETH_ALEN);
iter_data.count = 1;
} else
iter_data.count = 0;
@@ -86,20 +87,21 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
kfree(iter_data.addr);
/* Invert the mask and configure hardware */
- sc->bssidmask[0] = ~mask[0];
- sc->bssidmask[1] = ~mask[1];
- sc->bssidmask[2] = ~mask[2];
- sc->bssidmask[3] = ~mask[3];
- sc->bssidmask[4] = ~mask[4];
- sc->bssidmask[5] = ~mask[5];
-
- ath9k_hw_setbssidmask(sc);
+ common->bssidmask[0] = ~mask[0];
+ common->bssidmask[1] = ~mask[1];
+ common->bssidmask[2] = ~mask[2];
+ common->bssidmask[3] = ~mask[3];
+ common->bssidmask[4] = ~mask[4];
+ common->bssidmask[5] = ~mask[5];
+
+ ath_hw_setbssidmask(common);
}
int ath9k_wiphy_add(struct ath_softc *sc)
{
int i, error;
struct ath_wiphy *aphy;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ieee80211_hw *hw;
u8 addr[ETH_ALEN];
@@ -138,7 +140,7 @@ int ath9k_wiphy_add(struct ath_softc *sc)
sc->sec_wiphy[i] = aphy;
spin_unlock_bh(&sc->wiphy_lock);
- memcpy(addr, sc->sc_ah->macaddr, ETH_ALEN);
+ memcpy(addr, common->macaddr, ETH_ALEN);
addr[0] |= 0x02; /* Locally managed address */
/*
* XOR virtual wiphy index into the least significant bits to generate
@@ -296,6 +298,7 @@ static void ath9k_wiphy_unpause_channel(struct ath_softc *sc)
void ath9k_wiphy_chan_work(struct work_struct *work)
{
struct ath_softc *sc = container_of(work, struct ath_softc, chan_work);
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_wiphy *aphy = sc->next_wiphy;
if (aphy == NULL)
@@ -311,6 +314,10 @@ void ath9k_wiphy_chan_work(struct work_struct *work)
/* XXX: remove me eventually */
ath9k_update_ichannel(sc, aphy->hw,
&sc->sc_ah->channels[sc->chan_idx]);
+
+ /* sync hw configuration for hw code */
+ common->hw = aphy->hw;
+
ath_update_chainmask(sc, sc->chan_is_ht);
if (ath_set_channel(sc, aphy->hw,
&sc->sc_ah->channels[sc->chan_idx]) < 0) {
@@ -331,13 +338,11 @@ void ath9k_wiphy_chan_work(struct work_struct *work)
void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ath_wiphy *aphy = hw->priv;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
- if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE &&
+ if ((tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_PAUSE) &&
aphy->state == ATH_WIPHY_PAUSING) {
- if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
+ if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) {
printk(KERN_DEBUG "ath9k: %s: no ACK for pause "
"frame\n", wiphy_name(hw->wiphy));
/*
@@ -356,9 +361,6 @@ void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
}
}
- kfree(tx_info_priv);
- tx_info->rate_driver_data[0] = NULL;
-
dev_kfree_skb(skb);
}
@@ -519,8 +521,9 @@ int ath9k_wiphy_select(struct ath_wiphy *aphy)
* frame being completed)
*/
spin_unlock_bh(&sc->wiphy_lock);
- ath_radio_disable(sc);
- ath_radio_enable(sc);
+ ath_radio_disable(sc, aphy->hw);
+ ath_radio_enable(sc, aphy->hw);
+ /* Only the primary wiphy hw is used for queuing work */
ieee80211_queue_work(aphy->sc->hw,
&aphy->sc->chan_work);
return -EBUSY; /* previous select still in progress */
@@ -666,15 +669,78 @@ void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int)
bool ath9k_all_wiphys_idle(struct ath_softc *sc)
{
unsigned int i;
- if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) {
+ if (!sc->pri_wiphy->idle)
return false;
- }
for (i = 0; i < sc->num_sec_wiphy; i++) {
struct ath_wiphy *aphy = sc->sec_wiphy[i];
if (!aphy)
continue;
- if (aphy->state != ATH_WIPHY_INACTIVE)
+ if (!aphy->idle)
return false;
}
return true;
}
+
+/* caller must hold wiphy_lock */
+void ath9k_set_wiphy_idle(struct ath_wiphy *aphy, bool idle)
+{
+ struct ath_softc *sc = aphy->sc;
+
+ aphy->idle = idle;
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
+ "Marking %s as %s\n",
+ wiphy_name(aphy->hw->wiphy),
+ idle ? "idle" : "not-idle");
+}
+/* Only bother starting a queue on an active virtual wiphy */
+void ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue)
+{
+ struct ieee80211_hw *hw = sc->pri_wiphy->hw;
+ unsigned int i;
+
+ spin_lock_bh(&sc->wiphy_lock);
+
+ /* Start the primary wiphy */
+ if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) {
+ ieee80211_wake_queue(hw, skb_queue);
+ goto unlock;
+ }
+
+ /* Now start the secondary wiphy queues */
+ for (i = 0; i < sc->num_sec_wiphy; i++) {
+ struct ath_wiphy *aphy = sc->sec_wiphy[i];
+ if (!aphy)
+ continue;
+ if (aphy->state != ATH_WIPHY_ACTIVE)
+ continue;
+
+ hw = aphy->hw;
+ ieee80211_wake_queue(hw, skb_queue);
+ break;
+ }
+
+unlock:
+ spin_unlock_bh(&sc->wiphy_lock);
+}
+
+/* Go ahead and propagate information to all virtual wiphys, it won't hurt */
+void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue)
+{
+ struct ieee80211_hw *hw = sc->pri_wiphy->hw;
+ unsigned int i;
+
+ spin_lock_bh(&sc->wiphy_lock);
+
+ /* Stop the primary wiphy */
+ ieee80211_stop_queue(hw, skb_queue);
+
+ /* Now stop the secondary wiphy queues */
+ for (i = 0; i < sc->num_sec_wiphy; i++) {
+ struct ath_wiphy *aphy = sc->sec_wiphy[i];
+ if (!aphy)
+ continue;
+ hw = aphy->hw;
+ ieee80211_stop_queue(hw, skb_queue);
+ }
+ spin_unlock_bh(&sc->wiphy_lock);
+}
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 42551a48c8a..564c6cb1c2b 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -70,6 +70,29 @@ static int ath_tx_num_badfrms(struct ath_softc *sc, struct ath_buf *bf,
static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
int nbad, int txok, bool update_rc);
+enum {
+ MCS_DEFAULT,
+ MCS_HT40,
+ MCS_HT40_SGI,
+};
+
+static int ath_max_4ms_framelen[3][16] = {
+ [MCS_DEFAULT] = {
+ 3216, 6434, 9650, 12868, 19304, 25740, 28956, 32180,
+ 6430, 12860, 19300, 25736, 38600, 51472, 57890, 64320,
+ },
+ [MCS_HT40] = {
+ 6684, 13368, 20052, 26738, 40104, 53476, 60156, 66840,
+ 13360, 26720, 40080, 53440, 80160, 106880, 120240, 133600,
+ },
+ [MCS_HT40_SGI] = {
+ /* TODO: Only MCS 7 and 15 updated, recalculate the rest */
+ 6684, 13368, 20052, 26738, 40104, 53476, 60156, 74200,
+ 13360, 26720, 40080, 53440, 80160, 106880, 120240, 148400,
+ }
+};
+
+
/*********************/
/* Aggregation logic */
/*********************/
@@ -107,7 +130,7 @@ static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
- ASSERT(tid->paused > 0);
+ BUG_ON(tid->paused <= 0);
spin_lock_bh(&txq->axq_lock);
tid->paused--;
@@ -131,7 +154,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
- ASSERT(tid->paused > 0);
+ BUG_ON(tid->paused <= 0);
spin_lock_bh(&txq->axq_lock);
tid->paused--;
@@ -143,7 +166,7 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
while (!list_empty(&tid->buf_q)) {
bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
- ASSERT(!bf_isretried(bf));
+ BUG_ON(bf_isretried(bf));
list_move_tail(&bf->list, &bf_head);
ath_tx_send_ht_normal(sc, txq, tid, &bf_head);
}
@@ -178,7 +201,7 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
index = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno);
cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
- ASSERT(tid->tx_buf[cindex] == NULL);
+ BUG_ON(tid->tx_buf[cindex] != NULL);
tid->tx_buf[cindex] = bf;
if (index >= ((tid->baw_tail - tid->baw_head) &
@@ -251,6 +274,7 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf)
ATH_TXBUF_RESET(tbf);
+ tbf->aphy = bf->aphy;
tbf->bf_mpdu = bf->bf_mpdu;
tbf->bf_buf_addr = bf->bf_buf_addr;
*(tbf->bf_desc) = *(bf->bf_desc);
@@ -267,7 +291,9 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
struct ath_node *an = NULL;
struct sk_buff *skb;
struct ieee80211_sta *sta;
+ struct ieee80211_hw *hw;
struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_info *tx_info;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf_next, *bf_last = bf->bf_lastbf;
struct ath_desc *ds = bf_last->bf_desc;
@@ -280,9 +306,13 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
skb = bf->bf_mpdu;
hdr = (struct ieee80211_hdr *)skb->data;
+ tx_info = IEEE80211_SKB_CB(skb);
+ hw = bf->aphy->hw;
+
rcu_read_lock();
- sta = ieee80211_find_sta(sc->hw, hdr->addr1);
+ /* XXX: use ieee80211_find_sta! */
+ sta = ieee80211_find_sta_by_hw(hw, hdr->addr1);
if (!sta) {
rcu_read_unlock();
return;
@@ -358,7 +388,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
else
INIT_LIST_HEAD(&bf_head);
} else {
- ASSERT(!list_empty(bf_q));
+ BUG_ON(list_empty(bf_q));
list_move_tail(&bf->list, &bf_head);
}
@@ -452,11 +482,9 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
struct ath_atx_tid *tid)
{
- const struct ath_rate_table *rate_table = sc->cur_rate_table;
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
struct ieee80211_tx_rate *rates;
- struct ath_tx_info_priv *tx_info_priv;
u32 max_4ms_framelen, frmlen;
u16 aggr_limit, legacy = 0;
int i;
@@ -464,7 +492,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
skb = bf->bf_mpdu;
tx_info = IEEE80211_SKB_CB(skb);
rates = tx_info->control.rates;
- tx_info_priv = (struct ath_tx_info_priv *)tx_info->rate_driver_data[0];
/*
* Find the lowest frame length among the rate series that will have a
@@ -475,12 +502,20 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
for (i = 0; i < 4; i++) {
if (rates[i].count) {
- if (!WLAN_RC_PHY_HT(rate_table->info[rates[i].idx].phy)) {
+ int modeidx;
+ if (!(rates[i].flags & IEEE80211_TX_RC_MCS)) {
legacy = 1;
break;
}
- frmlen = rate_table->info[rates[i].idx].max_4ms_framelen;
+ if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI)
+ modeidx = MCS_HT40_SGI;
+ else if (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ modeidx = MCS_HT40;
+ else
+ modeidx = MCS_DEFAULT;
+
+ frmlen = ath_max_4ms_framelen[modeidx][rates[i].idx];
max_4ms_framelen = min(max_4ms_framelen, frmlen);
}
}
@@ -518,12 +553,11 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
struct ath_buf *bf, u16 frmlen)
{
- const struct ath_rate_table *rt = sc->cur_rate_table;
struct sk_buff *skb = bf->bf_mpdu;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
u32 nsymbits, nsymbols;
u16 minlen;
- u8 rc, flags, rix;
+ u8 flags, rix;
int width, half_gi, ndelim, mindelim;
/* Select standard number of delimiters based on frame length alone */
@@ -553,7 +587,6 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
rix = tx_info->control.rates[0].idx;
flags = tx_info->control.rates[0].flags;
- rc = rt->info[rix].ratecode;
width = (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ? 1 : 0;
half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0;
@@ -565,7 +598,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
if (nsymbols == 0)
nsymbols = 1;
- nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
+ nsymbits = bits_per_symbol[rix][width];
minlen = (nsymbols * nsymbits) / BITS_PER_BYTE;
if (frmlen < minlen) {
@@ -694,7 +727,6 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
/* anchor last desc of aggregate */
ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc);
- txq->axq_aggr_depth++;
ath_tx_txqaddbuf(sc, txq, &bf_q);
TX_STAT_INC(txq->axq_qnum, a_aggr);
@@ -815,6 +847,7 @@ static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_tx_queue_info qi;
int qnum;
@@ -854,9 +887,9 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
return NULL;
}
if (qnum >= ARRAY_SIZE(sc->tx.txq)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "qnum %u out of range, max %u!\n",
- qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq));
+ ath_print(common, ATH_DBG_FATAL,
+ "qnum %u out of range, max %u!\n",
+ qnum, (unsigned int)ARRAY_SIZE(sc->tx.txq));
ath9k_hw_releasetxqueue(ah, qnum);
return NULL;
}
@@ -869,8 +902,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
INIT_LIST_HEAD(&txq->axq_acq);
spin_lock_init(&txq->axq_lock);
txq->axq_depth = 0;
- txq->axq_aggr_depth = 0;
- txq->axq_linkbuf = NULL;
txq->axq_tx_inprogress = false;
sc->tx.txqsetup |= 1<<qnum;
}
@@ -884,9 +915,9 @@ int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
switch (qtype) {
case ATH9K_TX_QUEUE_DATA:
if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "HAL AC %u out of range, max %zu!\n",
- haltype, ARRAY_SIZE(sc->tx.hwq_map));
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "HAL AC %u out of range, max %zu!\n",
+ haltype, ARRAY_SIZE(sc->tx.hwq_map));
return -1;
}
qnum = sc->tx.hwq_map[haltype];
@@ -906,18 +937,19 @@ int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb)
{
struct ath_txq *txq = NULL;
+ u16 skb_queue = skb_get_queue_mapping(skb);
int qnum;
- qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc);
+ qnum = ath_get_hal_qnum(skb_queue, sc);
txq = &sc->tx.txq[qnum];
spin_lock_bh(&txq->axq_lock);
if (txq->axq_depth >= (ATH_TXBUF - 20)) {
- DPRINTF(sc, ATH_DBG_XMIT,
- "TX queue: %d is full, depth: %d\n",
- qnum, txq->axq_depth);
- ieee80211_stop_queue(sc->hw, skb_get_queue_mapping(skb));
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_XMIT,
+ "TX queue: %d is full, depth: %d\n",
+ qnum, txq->axq_depth);
+ ath_mac80211_stop_queue(sc, skb_queue);
txq->stopped = 1;
spin_unlock_bh(&txq->axq_lock);
return NULL;
@@ -945,7 +977,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
return 0;
}
- ASSERT(sc->tx.txq[qnum].axq_qnum == qnum);
+ BUG_ON(sc->tx.txq[qnum].axq_qnum != qnum);
ath9k_hw_get_txq_props(ah, qnum, &qi);
qi.tqi_aifs = qinfo->tqi_aifs;
@@ -955,8 +987,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
qi.tqi_readyTime = qinfo->tqi_readyTime;
if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to update hardware queue %u!\n", qnum);
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "Unable to update hardware queue %u!\n", qnum);
error = -EIO;
} else {
ath9k_hw_resettxqueue(ah, qnum);
@@ -1004,7 +1036,6 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
- txq->axq_linkbuf = NULL;
spin_unlock_bh(&txq->axq_lock);
break;
}
@@ -1055,6 +1086,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_txq *txq;
int i, npend = 0;
@@ -1076,14 +1108,15 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
if (npend) {
int r;
- DPRINTF(sc, ATH_DBG_XMIT, "Unable to stop TxDMA. Reset HAL!\n");
+ ath_print(common, ATH_DBG_XMIT,
+ "Unable to stop TxDMA. Reset HAL!\n");
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, sc->sc_ah->curchan, true);
if (r)
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to reset hardware; reset status %d\n",
- r);
+ ath_print(common, ATH_DBG_FATAL,
+ "Unable to reset hardware; reset status %d\n",
+ r);
spin_unlock_bh(&sc->sc_resetlock);
}
@@ -1147,8 +1180,8 @@ int ath_tx_setup(struct ath_softc *sc, int haltype)
struct ath_txq *txq;
if (haltype >= ARRAY_SIZE(sc->tx.hwq_map)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "HAL AC %u out of range, max %zu!\n",
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "HAL AC %u out of range, max %zu!\n",
haltype, ARRAY_SIZE(sc->tx.hwq_map));
return 0;
}
@@ -1172,6 +1205,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
struct list_head *head)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf;
/*
@@ -1186,21 +1220,20 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
list_splice_tail_init(head, &txq->axq_q);
txq->axq_depth++;
- txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list);
- DPRINTF(sc, ATH_DBG_QUEUE,
- "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth);
+ ath_print(common, ATH_DBG_QUEUE,
+ "qnum: %d, txq depth: %d\n", txq->axq_qnum, txq->axq_depth);
if (txq->axq_link == NULL) {
ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
- DPRINTF(sc, ATH_DBG_XMIT,
- "TXDP[%u] = %llx (%p)\n",
- txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
+ ath_print(common, ATH_DBG_XMIT,
+ "TXDP[%u] = %llx (%p)\n",
+ txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
} else {
*txq->axq_link = bf->bf_daddr;
- DPRINTF(sc, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n",
- txq->axq_qnum, txq->axq_link,
- ito64(bf->bf_daddr), bf->bf_desc);
+ ath_print(common, ATH_DBG_XMIT, "link[%u] (%p)=%llx (%p)\n",
+ txq->axq_qnum, txq->axq_link,
+ ito64(bf->bf_daddr), bf->bf_desc);
}
txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link);
ath9k_hw_txstart(ah, txq->axq_qnum);
@@ -1420,22 +1453,14 @@ static int setup_tx_flags(struct ath_softc *sc, struct sk_buff *skb,
static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf,
int width, int half_gi, bool shortPreamble)
{
- const struct ath_rate_table *rate_table = sc->cur_rate_table;
u32 nbits, nsymbits, duration, nsymbols;
- u8 rc;
int streams, pktlen;
pktlen = bf_isaggr(bf) ? bf->bf_al : bf->bf_frmlen;
- rc = rate_table->info[rix].ratecode;
-
- /* for legacy rates, use old function to compute packet duration */
- if (!IS_HT_RATE(rc))
- return ath9k_hw_computetxtime(sc->sc_ah, rate_table, pktlen,
- rix, shortPreamble);
/* find number of symbols: PLCP + data */
nbits = (pktlen << 3) + OFDM_PLCP_BITS;
- nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
+ nsymbits = bits_per_symbol[rix][width];
nsymbols = (nbits + nsymbits - 1) / nsymbits;
if (!half_gi)
@@ -1444,7 +1469,7 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf,
duration = SYMBOL_TIME_HALFGI(nsymbols);
/* addup duration for legacy/ht training and signal fields */
- streams = HT_RC_2_STREAMS(rc);
+ streams = HT_RC_2_STREAMS(rix);
duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
return duration;
@@ -1452,11 +1477,12 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf,
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
{
- const struct ath_rate_table *rt = sc->cur_rate_table;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath9k_11n_rate_series series[4];
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
struct ieee80211_tx_rate *rates;
+ const struct ieee80211_rate *rate;
struct ieee80211_hdr *hdr;
int i, flags = 0;
u8 rix = 0, ctsrate = 0;
@@ -1475,11 +1501,10 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
* checking the BSS's global flag.
* But for the rate series, IEEE80211_TX_RC_USE_SHORT_PREAMBLE is used.
*/
+ rate = ieee80211_get_rts_cts_rate(sc->hw, tx_info);
+ ctsrate = rate->hw_value;
if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
- ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode |
- rt->info[tx_info->control.rts_cts_rate_idx].short_preamble;
- else
- ctsrate = rt->info[tx_info->control.rts_cts_rate_idx].ratecode;
+ ctsrate |= rate->hw_value_short;
/*
* ATH9K_TXDESC_RTSENA and ATH9K_TXDESC_CTSENA are mutually exclusive.
@@ -1502,18 +1527,15 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
flags &= ~(ATH9K_TXDESC_RTSENA);
for (i = 0; i < 4; i++) {
+ bool is_40, is_sgi, is_sp;
+ int phy;
+
if (!rates[i].count || (rates[i].idx < 0))
continue;
rix = rates[i].idx;
series[i].Tries = rates[i].count;
- series[i].ChSel = sc->tx_chainmask;
-
- if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
- series[i].Rate = rt->info[rix].ratecode |
- rt->info[rix].short_preamble;
- else
- series[i].Rate = rt->info[rix].ratecode;
+ series[i].ChSel = common->tx_chainmask;
if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS)
series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
@@ -1522,10 +1544,36 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
if (rates[i].flags & IEEE80211_TX_RC_SHORT_GI)
series[i].RateFlags |= ATH9K_RATESERIES_HALFGI;
- series[i].PktDuration = ath_pkt_duration(sc, rix, bf,
- (rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) != 0,
- (rates[i].flags & IEEE80211_TX_RC_SHORT_GI),
- (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE));
+ is_sgi = !!(rates[i].flags & IEEE80211_TX_RC_SHORT_GI);
+ is_40 = !!(rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH);
+ is_sp = !!(rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
+
+ if (rates[i].flags & IEEE80211_TX_RC_MCS) {
+ /* MCS rates */
+ series[i].Rate = rix | 0x80;
+ series[i].PktDuration = ath_pkt_duration(sc, rix, bf,
+ is_40, is_sgi, is_sp);
+ continue;
+ }
+
+ /* legcay rates */
+ if ((tx_info->band == IEEE80211_BAND_2GHZ) &&
+ !(rate->flags & IEEE80211_RATE_ERP_G))
+ phy = WLAN_RC_PHY_CCK;
+ else
+ phy = WLAN_RC_PHY_OFDM;
+
+ rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx];
+ series[i].Rate = rate->hw_value;
+ if (rate->hw_value_short) {
+ if (rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+ series[i].Rate |= rate->hw_value_short;
+ } else {
+ is_sp = false;
+ }
+
+ series[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
+ phy, rate->bitrate * 100, bf->bf_frmlen, rix, is_sp);
}
/* set dur_update_en for l-sig computation except for PS-Poll frames */
@@ -1546,24 +1594,36 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
struct ath_softc *sc = aphy->sc;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ath_tx_info_priv *tx_info_priv;
int hdrlen;
__le16 fc;
+ int padpos, padsize;
- tx_info_priv = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC);
- if (unlikely(!tx_info_priv))
- return -ENOMEM;
- tx_info->rate_driver_data[0] = tx_info_priv;
- tx_info_priv->aphy = aphy;
- tx_info_priv->frame_type = txctl->frame_type;
+ tx_info->pad[0] = 0;
+ switch (txctl->frame_type) {
+ case ATH9K_NOT_INTERNAL:
+ break;
+ case ATH9K_INT_PAUSE:
+ tx_info->pad[0] |= ATH_TX_INFO_FRAME_TYPE_PAUSE;
+ /* fall through */
+ case ATH9K_INT_UNPAUSE:
+ tx_info->pad[0] |= ATH_TX_INFO_FRAME_TYPE_INTERNAL;
+ break;
+ }
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
fc = hdr->frame_control;
ATH_TXBUF_RESET(bf);
- bf->bf_frmlen = skb->len + FCS_LEN - (hdrlen & 3);
+ bf->aphy = aphy;
+ bf->bf_frmlen = skb->len + FCS_LEN;
+ /* Remove the padding size from bf_frmlen, if any */
+ padpos = ath9k_cmn_padpos(hdr->frame_control);
+ padsize = padpos & 3;
+ if (padsize && skb->len>padpos+padsize) {
+ bf->bf_frmlen -= padsize;
+ }
- if (conf_is_ht(&sc->hw->conf) && !is_pae(skb))
+ if (conf_is_ht(&hw->conf) && !is_pae(skb))
bf->bf_state.bf_type |= BUF_HT;
bf->bf_flags = setup_tx_flags(sc, skb, txctl->txq);
@@ -1585,13 +1645,20 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
skb->len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(sc->dev, bf->bf_dmacontext))) {
bf->bf_mpdu = NULL;
- kfree(tx_info_priv);
- tx_info->rate_driver_data[0] = NULL;
- DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error() on TX\n");
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
+ "dma_mapping_error() on TX\n");
return -ENOMEM;
}
bf->bf_buf_addr = bf->bf_dmacontext;
+
+ /* tag if this is a nullfunc frame to enable PS when AP acks it */
+ if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc)) {
+ bf->bf_isnullfunc = true;
+ sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
+ } else
+ bf->bf_isnullfunc = false;
+
return 0;
}
@@ -1669,12 +1736,13 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_buf *bf;
int r;
bf = ath_tx_get_buffer(sc);
if (!bf) {
- DPRINTF(sc, ATH_DBG_XMIT, "TX buffers are full\n");
+ ath_print(common, ATH_DBG_XMIT, "TX buffers are full\n");
return -1;
}
@@ -1682,7 +1750,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
if (unlikely(r)) {
struct ath_txq *txq = txctl->txq;
- DPRINTF(sc, ATH_DBG_FATAL, "TX mem alloc failure\n");
+ ath_print(common, ATH_DBG_FATAL, "TX mem alloc failure\n");
/* upon ath_tx_processq() this TX queue will be resumed, we
* guarantee this will happen by knowing beforehand that
@@ -1690,8 +1758,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
* on the queue */
spin_lock_bh(&txq->axq_lock);
if (sc->tx.txq[txq->axq_qnum].axq_depth > 1) {
- ieee80211_stop_queue(sc->hw,
- skb_get_queue_mapping(skb));
+ ath_mac80211_stop_queue(sc, skb_get_queue_mapping(skb));
txq->stopped = 1;
}
spin_unlock_bh(&txq->axq_lock);
@@ -1712,6 +1779,7 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int hdrlen, padsize;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ath_tx_control txctl;
@@ -1736,7 +1804,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
if (hdrlen & 3) {
padsize = hdrlen % 4;
if (skb_headroom(skb) < padsize) {
- DPRINTF(sc, ATH_DBG_XMIT, "TX CABQ padding failed\n");
+ ath_print(common, ATH_DBG_XMIT,
+ "TX CABQ padding failed\n");
dev_kfree_skb_any(skb);
return;
}
@@ -1746,10 +1815,11 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb)
txctl.txq = sc->beacon.cabq;
- DPRINTF(sc, ATH_DBG_XMIT, "transmitting CABQ packet, skb: %p\n", skb);
+ ath_print(common, ATH_DBG_XMIT,
+ "transmitting CABQ packet, skb: %p\n", skb);
if (ath_tx_start(hw, skb, &txctl) != 0) {
- DPRINTF(sc, ATH_DBG_XMIT, "CABQ TX failed\n");
+ ath_print(common, ATH_DBG_XMIT, "CABQ TX failed\n");
goto exit;
}
@@ -1763,26 +1833,17 @@ exit:
/*****************/
static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
- int tx_flags)
+ struct ath_wiphy *aphy, int tx_flags)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int hdrlen, padsize;
- int frame_type = ATH9K_NOT_INTERNAL;
- DPRINTF(sc, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
+ ath_print(common, ATH_DBG_XMIT, "TX complete: skb: %p\n", skb);
- if (tx_info_priv) {
- hw = tx_info_priv->aphy->hw;
- frame_type = tx_info_priv->frame_type;
- }
-
- if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
- tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
- kfree(tx_info_priv);
- tx_info->rate_driver_data[0] = NULL;
- }
+ if (aphy)
+ hw = aphy->hw;
if (tx_flags & ATH_TX_BAR)
tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
@@ -1805,18 +1866,19 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) {
sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK;
- DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having "
- "received TX status (0x%x)\n",
+ ath_print(common, ATH_DBG_PS,
+ "Going back to sleep after having "
+ "received TX status (0x%x)\n",
sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
SC_OP_WAIT_FOR_CAB |
SC_OP_WAIT_FOR_PSPOLL_DATA |
SC_OP_WAIT_FOR_TX_ACK));
}
- if (frame_type == ATH9K_NOT_INTERNAL)
- ieee80211_tx_status(hw, skb);
- else
+ if (unlikely(tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_INTERNAL))
ath9k_tx_status(hw, skb);
+ else
+ ieee80211_tx_status(hw, skb);
}
static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
@@ -1839,7 +1901,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
}
dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE);
- ath_tx_complete(sc, skb, tx_flags);
+ ath_tx_complete(sc, skb, bf->aphy, tx_flags);
ath_debug_stat_tx(sc, txq, bf);
/*
@@ -1887,8 +1949,7 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
struct sk_buff *skb = bf->bf_mpdu;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
- struct ieee80211_hw *hw = tx_info_priv->aphy->hw;
+ struct ieee80211_hw *hw = bf->aphy->hw;
u8 i, tx_rateindex;
if (txok)
@@ -1897,22 +1958,29 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds,
tx_rateindex = ds->ds_txstat.ts_rateindex;
WARN_ON(tx_rateindex >= hw->max_rates);
- tx_info_priv->update_rc = update_rc;
+ if (update_rc)
+ tx_info->pad[0] |= ATH_TX_INFO_UPDATE_RC;
if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT)
tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
(bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) {
if (ieee80211_is_data(hdr->frame_control)) {
- memcpy(&tx_info_priv->tx, &ds->ds_txstat,
- sizeof(tx_info_priv->tx));
- tx_info_priv->n_frames = bf->bf_nframes;
- tx_info_priv->n_bad_frames = nbad;
+ if (ds->ds_txstat.ts_flags &
+ (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN))
+ tx_info->pad[0] |= ATH_TX_INFO_UNDERRUN;
+ if ((ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY) ||
+ (ds->ds_txstat.ts_status & ATH9K_TXERR_FIFO))
+ tx_info->pad[0] |= ATH_TX_INFO_XRETRY;
+ tx_info->status.ampdu_len = bf->bf_nframes;
+ tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad;
}
}
- for (i = tx_rateindex + 1; i < hw->max_rates; i++)
+ for (i = tx_rateindex + 1; i < hw->max_rates; i++) {
tx_info->status.rates[i].count = 0;
+ tx_info->status.rates[i].idx = -1;
+ }
tx_info->status.rates[tx_rateindex].count = bf->bf_retries + 1;
}
@@ -1926,7 +1994,7 @@ static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
sc->tx.txq[txq->axq_qnum].axq_depth <= (ATH_TXBUF - 20)) {
qnum = ath_get_mac80211_qnum(txq->axq_qnum, sc);
if (qnum != -1) {
- ieee80211_wake_queue(sc->hw, qnum);
+ ath_mac80211_start_queue(sc, qnum);
txq->stopped = 0;
}
}
@@ -1936,21 +2004,21 @@ static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf, *lastbf, *bf_held = NULL;
struct list_head bf_head;
struct ath_desc *ds;
int txok;
int status;
- DPRINTF(sc, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
- txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum),
- txq->axq_link);
+ ath_print(common, ATH_DBG_QUEUE, "tx queue %d (%x), link %p\n",
+ txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum),
+ txq->axq_link);
for (;;) {
spin_lock_bh(&txq->axq_lock);
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
- txq->axq_linkbuf = NULL;
spin_unlock_bh(&txq->axq_lock);
break;
}
@@ -1984,10 +2052,19 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
spin_unlock_bh(&txq->axq_lock);
break;
}
- if (bf->bf_desc == txq->axq_lastdsWithCTS)
- txq->axq_lastdsWithCTS = NULL;
- if (ds == txq->axq_gatingds)
- txq->axq_gatingds = NULL;
+
+ /*
+ * We now know the nullfunc frame has been ACKed so we
+ * can disable RX.
+ */
+ if (bf->bf_isnullfunc &&
+ (ds->ds_txstat.ts_status & ATH9K_TX_ACKED)) {
+ if ((sc->sc_flags & SC_OP_PS_ENABLED)) {
+ sc->ps_enabled = true;
+ ath9k_hw_setrxabort(sc->sc_ah, 1);
+ } else
+ sc->sc_flags |= SC_OP_NULLFUNC_COMPLETED;
+ }
/*
* Remove ath_buf's of the same transmit unit from txq,
@@ -2001,9 +2078,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
&txq->axq_q, lastbf->list.prev);
txq->axq_depth--;
- if (bf_isaggr(bf))
- txq->axq_aggr_depth--;
-
txok = (ds->ds_txstat.ts_status == 0);
txq->axq_tx_inprogress = false;
spin_unlock_bh(&txq->axq_lock);
@@ -2064,8 +2138,11 @@ static void ath_tx_complete_poll_work(struct work_struct *work)
}
if (needreset) {
- DPRINTF(sc, ATH_DBG_RESET, "tx hung, resetting the chip\n");
+ ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET,
+ "tx hung, resetting the chip\n");
+ ath9k_ps_wakeup(sc);
ath_reset(sc, false);
+ ath9k_ps_restore(sc);
}
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
@@ -2093,6 +2170,7 @@ void ath_tx_tasklet(struct ath_softc *sc)
int ath_tx_init(struct ath_softc *sc, int nbufs)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int error = 0;
spin_lock_init(&sc->tx.txbuflock);
@@ -2100,16 +2178,16 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
error = ath_descdma_setup(sc, &sc->tx.txdma, &sc->tx.txbuf,
"tx", nbufs, 1);
if (error != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Failed to allocate tx descriptors: %d\n", error);
+ ath_print(common, ATH_DBG_FATAL,
+ "Failed to allocate tx descriptors: %d\n", error);
goto err;
}
error = ath_descdma_setup(sc, &sc->beacon.bdma, &sc->beacon.bbuf,
"beacon", ATH_BCBUF, 1);
if (error != 0) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Failed to allocate beacon descriptors: %d\n", error);
+ ath_print(common, ATH_DBG_FATAL,
+ "Failed to allocate beacon descriptors: %d\n", error);
goto err;
}
diff --git a/drivers/net/wireless/ath/debug.c b/drivers/net/wireless/ath/debug.c
new file mode 100644
index 00000000000..53e77bd131b
--- /dev/null
+++ b/drivers/net/wireless/ath/debug.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath.h"
+#include "debug.h"
+
+void ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...)
+{
+ va_list args;
+
+ if (likely(!(common->debug_mask & dbg_mask)))
+ return;
+
+ va_start(args, fmt);
+ printk(KERN_DEBUG "ath: ");
+ vprintk(fmt, args);
+ va_end(args);
+}
+EXPORT_SYMBOL(ath_print);
diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h
new file mode 100644
index 00000000000..d6b685a06c5
--- /dev/null
+++ b/drivers/net/wireless/ath/debug.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ATH_DEBUG_H
+#define ATH_DEBUG_H
+
+#include "ath.h"
+
+/**
+ * enum ath_debug_level - atheros wireless debug level
+ *
+ * @ATH_DBG_RESET: reset processing
+ * @ATH_DBG_QUEUE: hardware queue management
+ * @ATH_DBG_EEPROM: eeprom processing
+ * @ATH_DBG_CALIBRATE: periodic calibration
+ * @ATH_DBG_INTERRUPT: interrupt processing
+ * @ATH_DBG_REGULATORY: regulatory processing
+ * @ATH_DBG_ANI: adaptive noise immunitive processing
+ * @ATH_DBG_XMIT: basic xmit operation
+ * @ATH_DBG_BEACON: beacon handling
+ * @ATH_DBG_CONFIG: configuration of the hardware
+ * @ATH_DBG_FATAL: fatal errors, this is the default, DBG_DEFAULT
+ * @ATH_DBG_PS: power save processing
+ * @ATH_DBG_HWTIMER: hardware timer handling
+ * @ATH_DBG_BTCOEX: bluetooth coexistance
+ * @ATH_DBG_ANY: enable all debugging
+ *
+ * The debug level is used to control the amount and type of debugging output
+ * we want to see. Each driver has its own method for enabling debugging and
+ * modifying debug level states -- but this is typically done through a
+ * module parameter 'debug' along with a respective 'debug' debugfs file
+ * entry.
+ */
+enum ATH_DEBUG {
+ ATH_DBG_RESET = 0x00000001,
+ ATH_DBG_QUEUE = 0x00000002,
+ ATH_DBG_EEPROM = 0x00000004,
+ ATH_DBG_CALIBRATE = 0x00000008,
+ ATH_DBG_INTERRUPT = 0x00000010,
+ ATH_DBG_REGULATORY = 0x00000020,
+ ATH_DBG_ANI = 0x00000040,
+ ATH_DBG_XMIT = 0x00000080,
+ ATH_DBG_BEACON = 0x00000100,
+ ATH_DBG_CONFIG = 0x00000200,
+ ATH_DBG_FATAL = 0x00000400,
+ ATH_DBG_PS = 0x00000800,
+ ATH_DBG_HWTIMER = 0x00001000,
+ ATH_DBG_BTCOEX = 0x00002000,
+ ATH_DBG_ANY = 0xffffffff
+};
+
+#define ATH_DBG_DEFAULT (ATH_DBG_FATAL)
+
+#ifdef CONFIG_ATH_DEBUG
+void ath_print(struct ath_common *common, int dbg_mask, const char *fmt, ...);
+#else
+static inline void ath_print(struct ath_common *common,
+ int dbg_mask,
+ const char *fmt, ...)
+{
+}
+#endif /* CONFIG_ATH_DEBUG */
+
+#endif /* ATH_DEBUG_H */
diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c
new file mode 100644
index 00000000000..ecc9eb01f4f
--- /dev/null
+++ b/drivers/net/wireless/ath/hw.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+
+#include "ath.h"
+#include "reg.h"
+
+#define REG_READ common->ops->read
+#define REG_WRITE common->ops->write
+
+/**
+ * ath_hw_set_bssid_mask - filter out bssids we listen
+ *
+ * @common: the ath_common struct for the device.
+ *
+ * BSSID masking is a method used by AR5212 and newer hardware to inform PCU
+ * which bits of the interface's MAC address should be looked at when trying
+ * to decide which packets to ACK. In station mode and AP mode with a single
+ * BSS every bit matters since we lock to only one BSS. In AP mode with
+ * multiple BSSes (virtual interfaces) not every bit matters because hw must
+ * accept frames for all BSSes and so we tweak some bits of our mac address
+ * in order to have multiple BSSes.
+ *
+ * NOTE: This is a simple filter and does *not* filter out all
+ * relevant frames. Some frames that are not for us might get ACKed from us
+ * by PCU because they just match the mask.
+ *
+ * When handling multiple BSSes you can get the BSSID mask by computing the
+ * set of ~ ( MAC XOR BSSID ) for all bssids we handle.
+ *
+ * When you do this you are essentially computing the common bits of all your
+ * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with
+ * the MAC address to obtain the relevant bits and compare the result with
+ * (frame's BSSID & mask) to see if they match.
+ *
+ * Simple example: on your card you have have two BSSes you have created with
+ * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address.
+ * There is another BSSID-03 but you are not part of it. For simplicity's sake,
+ * assuming only 4 bits for a mac address and for BSSIDs you can then have:
+ *
+ * \
+ * MAC: 0001 |
+ * BSSID-01: 0100 | --> Belongs to us
+ * BSSID-02: 1001 |
+ * /
+ * -------------------
+ * BSSID-03: 0110 | --> External
+ * -------------------
+ *
+ * Our bssid_mask would then be:
+ *
+ * On loop iteration for BSSID-01:
+ * ~(0001 ^ 0100) -> ~(0101)
+ * -> 1010
+ * bssid_mask = 1010
+ *
+ * On loop iteration for BSSID-02:
+ * bssid_mask &= ~(0001 ^ 1001)
+ * bssid_mask = (1010) & ~(0001 ^ 1001)
+ * bssid_mask = (1010) & ~(1001)
+ * bssid_mask = (1010) & (0110)
+ * bssid_mask = 0010
+ *
+ * A bssid_mask of 0010 means "only pay attention to the second least
+ * significant bit". This is because its the only bit common
+ * amongst the MAC and all BSSIDs we support. To findout what the real
+ * common bit is we can simply "&" the bssid_mask now with any BSSID we have
+ * or our MAC address (we assume the hardware uses the MAC address).
+ *
+ * Now, suppose there's an incoming frame for BSSID-03:
+ *
+ * IFRAME-01: 0110
+ *
+ * An easy eye-inspeciton of this already should tell you that this frame
+ * will not pass our check. This is beacuse the bssid_mask tells the
+ * hardware to only look at the second least significant bit and the
+ * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB
+ * as 1, which does not match 0.
+ *
+ * So with IFRAME-01 we *assume* the hardware will do:
+ *
+ * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
+ * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0;
+ * --> allow = (0010) == 0000 ? 1 : 0;
+ * --> allow = 0
+ *
+ * Lets now test a frame that should work:
+ *
+ * IFRAME-02: 0001 (we should allow)
+ *
+ * allow = (0001 & 1010) == 1010
+ *
+ * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0;
+ * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0;
+ * --> allow = (0010) == (0010)
+ * --> allow = 1
+ *
+ * Other examples:
+ *
+ * IFRAME-03: 0100 --> allowed
+ * IFRAME-04: 1001 --> allowed
+ * IFRAME-05: 1101 --> allowed but its not for us!!!
+ *
+ */
+void ath_hw_setbssidmask(struct ath_common *common)
+{
+ void *ah = common->ah;
+
+ REG_WRITE(ah, get_unaligned_le32(common->bssidmask), AR_BSSMSKL);
+ REG_WRITE(ah, get_unaligned_le16(common->bssidmask + 4), AR_BSSMSKU);
+}
+EXPORT_SYMBOL(ath_hw_setbssidmask);
diff --git a/drivers/net/wireless/ath/reg.h b/drivers/net/wireless/ath/reg.h
new file mode 100644
index 00000000000..dfe1fbec24f
--- /dev/null
+++ b/drivers/net/wireless/ath/reg.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ATH_REGISTERS_H
+#define ATH_REGISTERS_H
+
+/*
+ * BSSID mask registers. See ath_hw_set_bssid_mask()
+ * for detailed documentation about these registers.
+ */
+#define AR_BSSMSKL 0x80e0
+#define AR_BSSMSKU 0x80e4
+
+#endif /* ATH_REGISTERS_H */
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index 077bcc142cd..039ac490465 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -450,7 +450,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg,
const struct ieee80211_regdomain *regd;
wiphy->reg_notifier = reg_notifier;
- wiphy->strict_regulatory = true;
+ wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
if (ath_is_world_regd(reg)) {
/*
@@ -458,8 +458,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg,
* saved on the wiphy orig_* parameters
*/
regd = ath_world_regdomain(reg);
- wiphy->custom_regulatory = true;
- wiphy->strict_regulatory = false;
+ wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
} else {
/*
* This gets applied in the case of the absense of CRDA,
diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h
index c1dd857697a..a1c39526161 100644
--- a/drivers/net/wireless/ath/regd.h
+++ b/drivers/net/wireless/ath/regd.h
@@ -65,10 +65,13 @@ enum CountryCode {
CTRY_ALGERIA = 12,
CTRY_ARGENTINA = 32,
CTRY_ARMENIA = 51,
+ CTRY_ARUBA = 533,
CTRY_AUSTRALIA = 36,
CTRY_AUSTRIA = 40,
CTRY_AZERBAIJAN = 31,
CTRY_BAHRAIN = 48,
+ CTRY_BANGLADESH = 50,
+ CTRY_BARBADOS = 52,
CTRY_BELARUS = 112,
CTRY_BELGIUM = 56,
CTRY_BELIZE = 84,
@@ -77,6 +80,7 @@ enum CountryCode {
CTRY_BRAZIL = 76,
CTRY_BRUNEI_DARUSSALAM = 96,
CTRY_BULGARIA = 100,
+ CTRY_CAMBODIA = 116,
CTRY_CANADA = 124,
CTRY_CHILE = 152,
CTRY_CHINA = 156,
@@ -97,7 +101,11 @@ enum CountryCode {
CTRY_GEORGIA = 268,
CTRY_GERMANY = 276,
CTRY_GREECE = 300,
+ CTRY_GREENLAND = 304,
+ CTRY_GRENEDA = 308,
+ CTRY_GUAM = 316,
CTRY_GUATEMALA = 320,
+ CTRY_HAITI = 332,
CTRY_HONDURAS = 340,
CTRY_HONG_KONG = 344,
CTRY_HUNGARY = 348,
diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h
index 9847af72208..248c670fdfb 100644
--- a/drivers/net/wireless/ath/regd_common.h
+++ b/drivers/net/wireless/ath/regd_common.h
@@ -288,13 +288,16 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_DEFAULT, FCC1_FCCA, "CO"},
{CTRY_ALBANIA, NULL1_WORLD, "AL"},
{CTRY_ALGERIA, NULL1_WORLD, "DZ"},
- {CTRY_ARGENTINA, APL3_WORLD, "AR"},
+ {CTRY_ARGENTINA, FCC3_WORLD, "AR"},
{CTRY_ARMENIA, ETSI4_WORLD, "AM"},
+ {CTRY_ARUBA, ETSI1_WORLD, "AW"},
{CTRY_AUSTRALIA, FCC2_WORLD, "AU"},
{CTRY_AUSTRALIA2, FCC6_WORLD, "AU"},
{CTRY_AUSTRIA, ETSI1_WORLD, "AT"},
{CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ"},
{CTRY_BAHRAIN, APL6_WORLD, "BH"},
+ {CTRY_BANGLADESH, NULL1_WORLD, "BD"},
+ {CTRY_BARBADOS, FCC2_WORLD, "BB"},
{CTRY_BELARUS, ETSI1_WORLD, "BY"},
{CTRY_BELGIUM, ETSI1_WORLD, "BE"},
{CTRY_BELGIUM2, ETSI4_WORLD, "BL"},
@@ -304,13 +307,14 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_BRAZIL, FCC3_WORLD, "BR"},
{CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN"},
{CTRY_BULGARIA, ETSI6_WORLD, "BG"},
- {CTRY_CANADA, FCC2_FCCA, "CA"},
+ {CTRY_CAMBODIA, ETSI1_WORLD, "KH"},
+ {CTRY_CANADA, FCC3_FCCA, "CA"},
{CTRY_CANADA2, FCC6_FCCA, "CA"},
{CTRY_CHILE, APL6_WORLD, "CL"},
{CTRY_CHINA, APL1_WORLD, "CN"},
{CTRY_COLOMBIA, FCC1_FCCA, "CO"},
{CTRY_COSTA_RICA, FCC1_WORLD, "CR"},
- {CTRY_CROATIA, ETSI3_WORLD, "HR"},
+ {CTRY_CROATIA, ETSI1_WORLD, "HR"},
{CTRY_CYPRUS, ETSI1_WORLD, "CY"},
{CTRY_CZECH, ETSI3_WORLD, "CZ"},
{CTRY_DENMARK, ETSI1_WORLD, "DK"},
@@ -324,18 +328,22 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_GEORGIA, ETSI4_WORLD, "GE"},
{CTRY_GERMANY, ETSI1_WORLD, "DE"},
{CTRY_GREECE, ETSI1_WORLD, "GR"},
+ {CTRY_GREENLAND, ETSI1_WORLD, "GL"},
+ {CTRY_GRENEDA, FCC3_FCCA, "GD"},
+ {CTRY_GUAM, FCC1_FCCA, "GU"},
{CTRY_GUATEMALA, FCC1_FCCA, "GT"},
+ {CTRY_HAITI, ETSI1_WORLD, "HT"},
{CTRY_HONDURAS, NULL1_WORLD, "HN"},
- {CTRY_HONG_KONG, FCC2_WORLD, "HK"},
+ {CTRY_HONG_KONG, FCC3_WORLD, "HK"},
{CTRY_HUNGARY, ETSI1_WORLD, "HU"},
{CTRY_ICELAND, ETSI1_WORLD, "IS"},
{CTRY_INDIA, APL6_WORLD, "IN"},
- {CTRY_INDONESIA, APL1_WORLD, "ID"},
+ {CTRY_INDONESIA, NULL1_WORLD, "ID"},
{CTRY_IRAN, APL1_WORLD, "IR"},
{CTRY_IRELAND, ETSI1_WORLD, "IE"},
{CTRY_ISRAEL, NULL1_WORLD, "IL"},
{CTRY_ITALY, ETSI1_WORLD, "IT"},
- {CTRY_JAMAICA, ETSI1_WORLD, "JM"},
+ {CTRY_JAMAICA, FCC3_WORLD, "JM"},
{CTRY_JAPAN, MKK1_MKKA, "JP"},
{CTRY_JAPAN1, MKK1_MKKB, "JP"},
@@ -402,7 +410,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_KOREA_ROC, APL9_WORLD, "KR"},
{CTRY_KOREA_ROC2, APL2_WORLD, "K2"},
{CTRY_KOREA_ROC3, APL9_WORLD, "K3"},
- {CTRY_KUWAIT, NULL1_WORLD, "KW"},
+ {CTRY_KUWAIT, ETSI3_WORLD, "KW"},
{CTRY_LATVIA, ETSI1_WORLD, "LV"},
{CTRY_LEBANON, NULL1_WORLD, "LB"},
{CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI"},
@@ -414,13 +422,13 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_MALTA, ETSI1_WORLD, "MT"},
{CTRY_MEXICO, FCC1_FCCA, "MX"},
{CTRY_MONACO, ETSI4_WORLD, "MC"},
- {CTRY_MOROCCO, NULL1_WORLD, "MA"},
+ {CTRY_MOROCCO, APL4_WORLD, "MA"},
{CTRY_NEPAL, APL1_WORLD, "NP"},
{CTRY_NETHERLANDS, ETSI1_WORLD, "NL"},
{CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN"},
{CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ"},
{CTRY_NORWAY, ETSI1_WORLD, "NO"},
- {CTRY_OMAN, APL6_WORLD, "OM"},
+ {CTRY_OMAN, FCC3_WORLD, "OM"},
{CTRY_PAKISTAN, NULL1_WORLD, "PK"},
{CTRY_PANAMA, FCC1_FCCA, "PA"},
{CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG"},
@@ -429,7 +437,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_POLAND, ETSI1_WORLD, "PL"},
{CTRY_PORTUGAL, ETSI1_WORLD, "PT"},
{CTRY_PUERTO_RICO, FCC1_FCCA, "PR"},
- {CTRY_QATAR, NULL1_WORLD, "QA"},
+ {CTRY_QATAR, APL1_WORLD, "QA"},
{CTRY_ROMANIA, NULL1_WORLD, "RO"},
{CTRY_RUSSIA, NULL1_WORLD, "RU"},
{CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA"},
@@ -445,7 +453,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_SYRIA, NULL1_WORLD, "SY"},
{CTRY_TAIWAN, APL3_FCCA, "TW"},
{CTRY_THAILAND, FCC3_WORLD, "TH"},
- {CTRY_TRINIDAD_Y_TOBAGO, ETSI4_WORLD, "TT"},
+ {CTRY_TRINIDAD_Y_TOBAGO, FCC3_WORLD, "TT"},
{CTRY_TUNISIA, ETSI3_WORLD, "TN"},
{CTRY_TURKEY, ETSI3_WORLD, "TR"},
{CTRY_UKRAINE, NULL1_WORLD, "UA"},
@@ -456,7 +464,7 @@ static struct country_code_to_enum_rd allCountries[] = {
* would need to assign new special alpha2 to CRDA db as with the world
* regdomain and use another alpha2 */
{CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS"},
- {CTRY_URUGUAY, APL2_WORLD, "UY"},
+ {CTRY_URUGUAY, FCC3_WORLD, "UY"},
{CTRY_UZBEKISTAN, FCC3_FCCA, "UZ"},
{CTRY_VENEZUELA, APL2_ETSIC, "VE"},
{CTRY_VIET_NAM, NULL1_WORLD, "VN"},
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index cce188837d1..3edbbcf0f54 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -99,6 +99,22 @@ static struct {
{ ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" },
{ ATMEL_FW_TYPE_NONE, NULL, NULL }
};
+MODULE_FIRMWARE("atmel_at76c502-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502.bin");
+MODULE_FIRMWARE("atmel_at76c502d-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502d.bin");
+MODULE_FIRMWARE("atmel_at76c502e-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502e.bin");
+MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c502_3com.bin");
+MODULE_FIRMWARE("atmel_at76c504-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c504.bin");
+MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c504_2958.bin");
+MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c504a_2958.bin");
+MODULE_FIRMWARE("atmel_at76c506-wpa.bin");
+MODULE_FIRMWARE("atmel_at76c506.bin");
#define MAX_SSID_LENGTH 32
#define MGMT_JIFFIES (256 * HZ / 100)
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c
index ddaa859c349..32407911842 100644
--- a/drivers/net/wireless/atmel_cs.c
+++ b/drivers/net/wireless/atmel_cs.c
@@ -55,22 +55,6 @@
#include "atmel.h"
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-static char *version = "$Revision: 1.2 $";
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -155,11 +139,10 @@ static int atmel_probe(struct pcmcia_device *p_dev)
{
local_info_t *local;
- DEBUG(0, "atmel_attach()\n");
+ dev_dbg(&p_dev->dev, "atmel_attach()\n");
/* Interrupt setup */
p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
p_dev->irq.Handler = NULL;
/*
@@ -194,7 +177,7 @@ static int atmel_probe(struct pcmcia_device *p_dev)
static void atmel_detach(struct pcmcia_device *link)
{
- DEBUG(0, "atmel_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "atmel_detach\n");
atmel_release(link);
@@ -209,9 +192,6 @@ static void atmel_detach(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
/* Call-back function to interrogate PCMCIA-specific information
about the current existance of the card */
static int card_present(void *arg)
@@ -275,13 +255,13 @@ static int atmel_config_check(struct pcmcia_device *p_dev,
static int atmel_config(struct pcmcia_device *link)
{
local_info_t *dev;
- int last_fn, last_ret;
+ int ret;
struct pcmcia_device_id *did;
dev = link->priv;
- did = dev_get_drvdata(&handle_to_dev(link));
+ did = dev_get_drvdata(&link->dev);
- DEBUG(0, "atmel_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "atmel_config\n");
/*
In this loop, we scan the CIS for configuration table entries,
@@ -303,31 +283,36 @@ static int atmel_config(struct pcmcia_device *link)
handler to the interrupt, unless the 'Handler' member of the
irq structure is initialized.
*/
- if (link->conf.Attributes & CONF_ENABLE_IRQ)
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ }
/*
This actually configures the PCMCIA socket -- setting up
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
if (link->irq.AssignedIRQ == 0) {
printk(KERN_ALERT
"atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config.");
- goto cs_failed;
+ goto failed;
}
((local_info_t*)link->priv)->eth_dev =
init_atmel_card(link->irq.AssignedIRQ,
link->io.BasePort1,
did ? did->driver_info : ATMEL_FW_TYPE_NONE,
- &handle_to_dev(link),
+ &link->dev,
card_present,
link);
if (!((local_info_t*)link->priv)->eth_dev)
- goto cs_failed;
+ goto failed;
/*
@@ -340,8 +325,6 @@ static int atmel_config(struct pcmcia_device *link)
return 0;
- cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
atmel_release(link);
return -ENODEV;
@@ -359,7 +342,7 @@ static void atmel_release(struct pcmcia_device *link)
{
struct net_device *dev = ((local_info_t*)link->priv)->eth_dev;
- DEBUG(0, "atmel_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "atmel_release\n");
if (dev)
stop_atmel_card(dev);
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 54ea61c15d8..64c12e1bced 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -1,6 +1,6 @@
config B43
tristate "Broadcom 43xx wireless support (mac80211 stack)"
- depends on SSB_POSSIBLE && MAC80211 && WLAN_80211 && HAS_DMA
+ depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
select SSB
select FW_LOADER
---help---
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index 660716214d4..fe3bf949199 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -26,8 +26,6 @@
# define B43_DEBUG 0
#endif
-#define B43_RX_MAX_SSI 60
-
/* MMIO offsets */
#define B43_MMIO_DMA0_REASON 0x20
#define B43_MMIO_DMA0_IRQ_MASK 0x24
@@ -749,12 +747,6 @@ struct b43_wldev {
#endif
};
-/*
- * Include goes here to avoid a dependency problem.
- * A better fix would be to integrate xmit.h into b43.h.
- */
-#include "xmit.h"
-
/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */
struct b43_wl {
/* Pointer to the active wireless device on this chip */
@@ -830,13 +822,9 @@ struct b43_wl {
struct b43_leds leds;
#ifdef CONFIG_B43_PIO
- /*
- * RX/TX header/tail buffers used by the frame transmit functions.
- */
- struct b43_rxhdr_fw4 rxhdr;
- struct b43_txhdr txhdr;
- u8 rx_tail[4];
- u8 tx_tail[4];
+ /* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */
+ u8 pio_scratchspace[110] __attribute__((__aligned__(8)));
+ u8 pio_tailspace[4] __attribute__((__aligned__(8)));
#endif /* CONFIG_B43_PIO */
};
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index de4e804bedf..027be275e03 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -383,44 +383,160 @@ static inline
}
}
+/* Check if a DMA region fits the device constraints.
+ * Returns true, if the region is OK for usage with this device. */
+static inline bool b43_dma_address_ok(struct b43_dmaring *ring,
+ dma_addr_t addr, size_t size)
+{
+ switch (ring->type) {
+ case B43_DMA_30BIT:
+ if ((u64)addr + size > (1ULL << 30))
+ return 0;
+ break;
+ case B43_DMA_32BIT:
+ if ((u64)addr + size > (1ULL << 32))
+ return 0;
+ break;
+ case B43_DMA_64BIT:
+ /* Currently we can't have addresses beyond
+ * 64bit in the kernel. */
+ break;
+ }
+ return 1;
+}
+
+#define is_4k_aligned(addr) (((u64)(addr) & 0x0FFFull) == 0)
+#define is_8k_aligned(addr) (((u64)(addr) & 0x1FFFull) == 0)
+
+static void b43_unmap_and_free_ringmem(struct b43_dmaring *ring, void *base,
+ dma_addr_t dmaaddr, size_t size)
+{
+ ssb_dma_unmap_single(ring->dev->dev, dmaaddr, size, DMA_TO_DEVICE);
+ free_pages((unsigned long)base, get_order(size));
+}
+
+static void * __b43_get_and_map_ringmem(struct b43_dmaring *ring,
+ dma_addr_t *dmaaddr, size_t size,
+ gfp_t gfp_flags)
+{
+ void *base;
+
+ base = (void *)__get_free_pages(gfp_flags, get_order(size));
+ if (!base)
+ return NULL;
+ memset(base, 0, size);
+ *dmaaddr = ssb_dma_map_single(ring->dev->dev, base, size,
+ DMA_TO_DEVICE);
+ if (ssb_dma_mapping_error(ring->dev->dev, *dmaaddr)) {
+ free_pages((unsigned long)base, get_order(size));
+ return NULL;
+ }
+
+ return base;
+}
+
+static void * b43_get_and_map_ringmem(struct b43_dmaring *ring,
+ dma_addr_t *dmaaddr, size_t size)
+{
+ void *base;
+
+ base = __b43_get_and_map_ringmem(ring, dmaaddr, size,
+ GFP_KERNEL);
+ if (!base) {
+ b43err(ring->dev->wl, "Failed to allocate or map pages "
+ "for DMA ringmemory\n");
+ return NULL;
+ }
+ if (!b43_dma_address_ok(ring, *dmaaddr, size)) {
+ /* The memory does not fit our device constraints.
+ * Retry with GFP_DMA set to get lower memory. */
+ b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size);
+ base = __b43_get_and_map_ringmem(ring, dmaaddr, size,
+ GFP_KERNEL | GFP_DMA);
+ if (!base) {
+ b43err(ring->dev->wl, "Failed to allocate or map pages "
+ "in the GFP_DMA region for DMA ringmemory\n");
+ return NULL;
+ }
+ if (!b43_dma_address_ok(ring, *dmaaddr, size)) {
+ b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size);
+ b43err(ring->dev->wl, "Failed to allocate DMA "
+ "ringmemory that fits device constraints\n");
+ return NULL;
+ }
+ }
+ /* We expect the memory to be 4k aligned, at least. */
+ if (B43_WARN_ON(!is_4k_aligned(*dmaaddr))) {
+ b43_unmap_and_free_ringmem(ring, base, *dmaaddr, size);
+ return NULL;
+ }
+
+ return base;
+}
+
static int alloc_ringmemory(struct b43_dmaring *ring)
{
- gfp_t flags = GFP_KERNEL;
-
- /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
- * alignment and 8K buffers for 64-bit DMA with 8K alignment. Testing
- * has shown that 4K is sufficient for the latter as long as the buffer
- * does not cross an 8K boundary.
- *
- * For unknown reasons - possibly a hardware error - the BCM4311 rev
- * 02, which uses 64-bit DMA, needs the ring buffer in very low memory,
- * which accounts for the GFP_DMA flag below.
- *
- * The flags here must match the flags in free_ringmemory below!
+ unsigned int required;
+ void *base;
+ dma_addr_t dmaaddr;
+
+ /* There are several requirements to the descriptor ring memory:
+ * - The memory region needs to fit the address constraints for the
+ * device (same as for frame buffers).
+ * - For 30/32bit DMA devices, the descriptor ring must be 4k aligned.
+ * - For 64bit DMA devices, the descriptor ring must be 8k aligned.
*/
+
if (ring->type == B43_DMA_64BIT)
- flags |= GFP_DMA;
- ring->descbase = ssb_dma_alloc_consistent(ring->dev->dev,
- B43_DMA_RINGMEMSIZE,
- &(ring->dmabase), flags);
- if (!ring->descbase) {
- b43err(ring->dev->wl, "DMA ringmemory allocation failed\n");
+ required = ring->nr_slots * sizeof(struct b43_dmadesc64);
+ else
+ required = ring->nr_slots * sizeof(struct b43_dmadesc32);
+ if (B43_WARN_ON(required > 0x1000))
+ return -ENOMEM;
+
+ ring->alloc_descsize = 0x1000;
+ base = b43_get_and_map_ringmem(ring, &dmaaddr, ring->alloc_descsize);
+ if (!base)
+ return -ENOMEM;
+ ring->alloc_descbase = base;
+ ring->alloc_dmabase = dmaaddr;
+
+ if ((ring->type != B43_DMA_64BIT) || is_8k_aligned(dmaaddr)) {
+ /* We're on <=32bit DMA, or we already got 8k aligned memory.
+ * That's all we need, so we're fine. */
+ ring->descbase = base;
+ ring->dmabase = dmaaddr;
+ return 0;
+ }
+ b43_unmap_and_free_ringmem(ring, base, dmaaddr, ring->alloc_descsize);
+
+ /* Ok, we failed at the 8k alignment requirement.
+ * Try to force-align the memory region now. */
+ ring->alloc_descsize = 0x2000;
+ base = b43_get_and_map_ringmem(ring, &dmaaddr, ring->alloc_descsize);
+ if (!base)
return -ENOMEM;
+ ring->alloc_descbase = base;
+ ring->alloc_dmabase = dmaaddr;
+
+ if (is_8k_aligned(dmaaddr)) {
+ /* We're already 8k aligned. That Ok, too. */
+ ring->descbase = base;
+ ring->dmabase = dmaaddr;
+ return 0;
}
- memset(ring->descbase, 0, B43_DMA_RINGMEMSIZE);
+ /* Force-align it to 8k */
+ ring->descbase = (void *)((u8 *)base + 0x1000);
+ ring->dmabase = dmaaddr + 0x1000;
+ B43_WARN_ON(!is_8k_aligned(ring->dmabase));
return 0;
}
static void free_ringmemory(struct b43_dmaring *ring)
{
- gfp_t flags = GFP_KERNEL;
-
- if (ring->type == B43_DMA_64BIT)
- flags |= GFP_DMA;
-
- ssb_dma_free_consistent(ring->dev->dev, B43_DMA_RINGMEMSIZE,
- ring->descbase, ring->dmabase, flags);
+ b43_unmap_and_free_ringmem(ring, ring->alloc_descbase,
+ ring->alloc_dmabase, ring->alloc_descsize);
}
/* Reset the RX DMA channel */
@@ -530,29 +646,14 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring,
if (unlikely(ssb_dma_mapping_error(ring->dev->dev, addr)))
return 1;
- switch (ring->type) {
- case B43_DMA_30BIT:
- if ((u64)addr + buffersize > (1ULL << 30))
- goto address_error;
- break;
- case B43_DMA_32BIT:
- if ((u64)addr + buffersize > (1ULL << 32))
- goto address_error;
- break;
- case B43_DMA_64BIT:
- /* Currently we can't have addresses beyond
- * 64bit in the kernel. */
- break;
+ if (!b43_dma_address_ok(ring, addr, buffersize)) {
+ /* We can't support this address. Unmap it again. */
+ unmap_descbuffer(ring, addr, buffersize, dma_to_device);
+ return 1;
}
/* The address is OK. */
return 0;
-
-address_error:
- /* We can't support this address. Unmap it again. */
- unmap_descbuffer(ring, addr, buffersize, dma_to_device);
-
- return 1;
}
static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb)
@@ -614,6 +715,9 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring,
meta->dmaaddr = dmaaddr;
ring->ops->fill_descriptor(ring, desc, dmaaddr,
ring->rx_buffersize, 0, 0, 0);
+ ssb_dma_sync_single_for_device(ring->dev->dev,
+ ring->alloc_dmabase,
+ ring->alloc_descsize, DMA_TO_DEVICE);
return 0;
}
@@ -770,7 +874,7 @@ static void free_all_descbuffers(struct b43_dmaring *ring)
for (i = 0; i < ring->nr_slots; i++) {
desc = ring->ops->idx2desc(ring, i, &meta);
- if (!meta->skb) {
+ if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) {
B43_WARN_ON(!ring->tx);
continue;
}
@@ -822,7 +926,7 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
enum b43_dmatype type)
{
struct b43_dmaring *ring;
- int err;
+ int i, err;
dma_addr_t dma_test;
ring = kzalloc(sizeof(*ring), GFP_KERNEL);
@@ -837,6 +941,8 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
GFP_KERNEL);
if (!ring->meta)
goto err_kfree_ring;
+ for (i = 0; i < ring->nr_slots; i++)
+ ring->meta->skb = B43_DMA_PTR_POISON;
ring->type = type;
ring->dev = dev;
@@ -1147,28 +1253,29 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
case 0x5000:
ring = dma->tx_ring_mcast;
break;
- default:
- B43_WARN_ON(1);
}
*slot = (cookie & 0x0FFF);
- B43_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots));
+ if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) {
+ b43dbg(dev->wl, "TX-status contains "
+ "invalid cookie: 0x%04X\n", cookie);
+ return NULL;
+ }
return ring;
}
static int dma_tx_fragment(struct b43_dmaring *ring,
- struct sk_buff **in_skb)
+ struct sk_buff *skb)
{
- struct sk_buff *skb = *in_skb;
const struct b43_dma_ops *ops = ring->ops;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(info);
u8 *header;
int slot, old_top_slot, old_used_slots;
int err;
struct b43_dmadesc_generic *desc;
struct b43_dmadesc_meta *meta;
struct b43_dmadesc_meta *meta_hdr;
- struct sk_buff *bounce_skb;
u16 cookie;
size_t hdrsize = b43_txhdr_size(ring->dev);
@@ -1212,34 +1319,28 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
meta->skb = skb;
meta->is_last_fragment = 1;
+ priv_info->bouncebuffer = NULL;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
/* create a bounce buffer in zone_dma on mapping failure. */
if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
- bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
- if (!bounce_skb) {
+ priv_info->bouncebuffer = kmalloc(skb->len, GFP_ATOMIC | GFP_DMA);
+ if (!priv_info->bouncebuffer) {
ring->current_slot = old_top_slot;
ring->used_slots = old_used_slots;
err = -ENOMEM;
goto out_unmap_hdr;
}
+ memcpy(priv_info->bouncebuffer, skb->data, skb->len);
- memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
- memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb));
- bounce_skb->dev = skb->dev;
- skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb));
- info = IEEE80211_SKB_CB(bounce_skb);
-
- dev_kfree_skb_any(skb);
- skb = bounce_skb;
- *in_skb = bounce_skb;
- meta->skb = skb;
- meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
+ meta->dmaaddr = map_descbuffer(ring, priv_info->bouncebuffer, skb->len, 1);
if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
+ kfree(priv_info->bouncebuffer);
+ priv_info->bouncebuffer = NULL;
ring->current_slot = old_top_slot;
ring->used_slots = old_used_slots;
err = -EIO;
- goto out_free_bounce;
+ goto out_unmap_hdr;
}
}
@@ -1253,11 +1354,12 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
}
/* Now transfer the whole frame. */
wmb();
+ ssb_dma_sync_single_for_device(ring->dev->dev,
+ ring->alloc_dmabase,
+ ring->alloc_descsize, DMA_TO_DEVICE);
ops->poke_tx(ring, next_slot(ring, slot));
return 0;
-out_free_bounce:
- dev_kfree_skb_any(skb);
out_unmap_hdr:
unmap_descbuffer(ring, meta_hdr->dmaaddr,
hdrsize, 1);
@@ -1362,11 +1464,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
* static, so we don't need to store it per frame. */
ring->queue_prio = skb_get_queue_mapping(skb);
- /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing
- * into the skb data or cb now. */
- hdr = NULL;
- info = NULL;
- err = dma_tx_fragment(ring, &skb);
+ err = dma_tx_fragment(ring, skb);
if (unlikely(err == -ENOKEY)) {
/* Drop this packet, as we don't have the encryption key
* anymore and must not transmit it unencrypted. */
@@ -1400,30 +1498,63 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
struct b43_dmaring *ring;
struct b43_dmadesc_generic *desc;
struct b43_dmadesc_meta *meta;
- int slot;
+ int slot, firstused;
bool frame_succeed;
ring = parse_cookie(dev, status->cookie, &slot);
if (unlikely(!ring))
return;
-
B43_WARN_ON(!ring->tx);
+
+ /* Sanity check: TX packets are processed in-order on one ring.
+ * Check if the slot deduced from the cookie really is the first
+ * used slot. */
+ firstused = ring->current_slot - ring->used_slots + 1;
+ if (firstused < 0)
+ firstused = ring->nr_slots + firstused;
+ if (unlikely(slot != firstused)) {
+ /* This possibly is a firmware bug and will result in
+ * malfunction, memory leaks and/or stall of DMA functionality. */
+ b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. "
+ "Expected %d, but got %d\n",
+ ring->index, firstused, slot);
+ return;
+ }
+
ops = ring->ops;
while (1) {
- B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
+ B43_WARN_ON(slot < 0 || slot >= ring->nr_slots);
desc = ops->idx2desc(ring, slot, &meta);
- if (meta->skb)
- unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len,
- 1);
- else
+ if (b43_dma_ptr_is_poisoned(meta->skb)) {
+ b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) "
+ "on ring %d\n",
+ slot, firstused, ring->index);
+ break;
+ }
+ if (meta->skb) {
+ struct b43_private_tx_info *priv_info =
+ b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb));
+
+ unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1);
+ kfree(priv_info->bouncebuffer);
+ priv_info->bouncebuffer = NULL;
+ } else {
unmap_descbuffer(ring, meta->dmaaddr,
b43_txhdr_size(dev), 1);
+ }
if (meta->is_last_fragment) {
struct ieee80211_tx_info *info;
- BUG_ON(!meta->skb);
+ if (unlikely(!meta->skb)) {
+ /* This is a scatter-gather fragment of a frame, so
+ * the skb pointer must not be NULL. */
+ b43dbg(dev->wl, "TX status unexpected NULL skb "
+ "at slot %d (first=%d) on ring %d\n",
+ slot, firstused, ring->index);
+ break;
+ }
info = IEEE80211_SKB_CB(meta->skb);
@@ -1441,20 +1572,29 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
#endif /* DEBUG */
ieee80211_tx_status(dev->wl->hw, meta->skb);
- /* skb is freed by ieee80211_tx_status() */
- meta->skb = NULL;
+ /* skb will be freed by ieee80211_tx_status().
+ * Poison our pointer. */
+ meta->skb = B43_DMA_PTR_POISON;
} else {
/* No need to call free_descriptor_buffer here, as
* this is only the txhdr, which is not allocated.
*/
- B43_WARN_ON(meta->skb);
+ if (unlikely(meta->skb)) {
+ b43dbg(dev->wl, "TX status unexpected non-NULL skb "
+ "at slot %d (first=%d) on ring %d\n",
+ slot, firstused, ring->index);
+ break;
+ }
}
/* Everything unmapped and free'd. So it's not used anymore. */
ring->used_slots--;
- if (meta->is_last_fragment)
+ if (meta->is_last_fragment) {
+ /* This is the last scatter-gather
+ * fragment of the frame. We are done. */
break;
+ }
slot = next_slot(ring, slot);
}
if (ring->stopped) {
diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h
index f0b0838fb5b..e607b392314 100644
--- a/drivers/net/wireless/b43/dma.h
+++ b/drivers/net/wireless/b43/dma.h
@@ -1,7 +1,7 @@
#ifndef B43_DMA_H_
#define B43_DMA_H_
-#include <linux/ieee80211.h>
+#include <linux/err.h>
#include "b43.h"
@@ -157,7 +157,6 @@ struct b43_dmadesc_generic {
} __attribute__ ((__packed__));
/* Misc DMA constants */
-#define B43_DMA_RINGMEMSIZE PAGE_SIZE
#define B43_DMA0_RX_FRAMEOFFSET 30
/* DMA engine tuning knobs */
@@ -165,6 +164,10 @@ struct b43_dmadesc_generic {
#define B43_RXRING_SLOTS 64
#define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN
+/* Pointer poison */
+#define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM))
+#define b43_dma_ptr_is_poisoned(ptr) (unlikely((ptr) == B43_DMA_PTR_POISON))
+
struct sk_buff;
struct b43_private;
@@ -243,6 +246,12 @@ struct b43_dmaring {
/* The QOS priority assigned to this ring. Only used for TX rings.
* This is the mac80211 "queue" value. */
u8 queue_prio;
+ /* Pointers and size of the originally allocated and mapped memory
+ * region for the descriptor ring. */
+ void *alloc_descbase;
+ dma_addr_t alloc_dmabase;
+ unsigned int alloc_descsize;
+ /* Pointer to our wireless device. */
struct b43_wldev *dev;
#ifdef CONFIG_B43_DEBUG
/* Maximum number of used slots. */
diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c
index 1e8dba48800..c587115dd2b 100644
--- a/drivers/net/wireless/b43/leds.c
+++ b/drivers/net/wireless/b43/leds.c
@@ -246,6 +246,7 @@ static void b43_led_get_sprominfo(struct b43_wldev *dev,
*behaviour = B43_LED_OFF;
break;
default:
+ *behaviour = B43_LED_OFF;
B43_WARN_ON(1);
return;
}
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 098dda1a67c..71e5c996bd0 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -2955,7 +2955,7 @@ static void do_periodic_work(struct b43_wldev *dev)
/* Periodic work locking policy:
* The whole periodic work handler is protected by
* wl->mutex. If another lock is needed somewhere in the
- * pwork callchain, it's aquired in-place, where it's needed.
+ * pwork callchain, it's acquired in-place, where it's needed.
*/
static void b43_periodic_work_handler(struct work_struct *work)
{
@@ -3573,7 +3573,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
if (conf->channel->hw_value != phy->channel)
b43_switch_channel(dev, conf->channel->hw_value);
- dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_RADIOTAP);
+ dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR);
/* Adjust the desired TX power level. */
if (conf->power_level != 0) {
@@ -4669,7 +4669,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
struct ssb_bus *bus = dev->dev->bus;
- struct pci_dev *pdev = bus->host_pci;
+ struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL;
int err;
bool have_2ghz_phy = 0, have_5ghz_phy = 0;
u32 tmp;
@@ -4802,7 +4802,7 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl)
if (!list_empty(&wl->devlist)) {
/* We are not the first core on this chip. */
- pdev = dev->bus->host_pci;
+ pdev = (dev->bus->bustype == SSB_BUSTYPE_PCI) ? dev->bus->host_pci : NULL;
/* Only special chips support more than one wireless
* core, although some of the other chips have more than
* one wireless core as well. Check for this and
diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c
index 6c3a74964ab..984174bc7b0 100644
--- a/drivers/net/wireless/b43/pcmcia.c
+++ b/drivers/net/wireless/b43/pcmcia.c
@@ -65,35 +65,15 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev)
struct ssb_bus *ssb;
win_req_t win;
memreq_t mem;
- tuple_t tuple;
- cisparse_t parse;
int err = -ENOMEM;
int res = 0;
- unsigned char buf[64];
ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
if (!ssb)
goto out_error;
err = -ENODEV;
- tuple.DesiredTuple = CISTPL_CONFIG;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- res = pcmcia_get_first_tuple(dev, &tuple);
- if (res != 0)
- goto err_kfree_ssb;
- res = pcmcia_get_tuple_data(dev, &tuple);
- if (res != 0)
- goto err_kfree_ssb;
- res = pcmcia_parse_tuple(&tuple, &parse);
- if (res != 0)
- goto err_kfree_ssb;
-
- dev->conf.ConfigBase = parse.config.base;
- dev->conf.Present = parse.config.rmask[0];
dev->conf.Attributes = CONF_ENABLE_IRQ;
dev->conf.IntType = INT_MEMORY_AND_IO;
@@ -107,20 +87,18 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev)
win.Base = 0;
win.Size = SSB_CORE_SIZE;
win.AccessSpeed = 250;
- res = pcmcia_request_window(&dev, &win, &dev->win);
+ res = pcmcia_request_window(dev, &win, &dev->win);
if (res != 0)
goto err_kfree_ssb;
mem.CardOffset = 0;
mem.Page = 0;
- res = pcmcia_map_mem_page(dev->win, &mem);
+ res = pcmcia_map_mem_page(dev, dev->win, &mem);
if (res != 0)
goto err_disable;
dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
dev->irq.Handler = NULL; /* The handler is registered later. */
- dev->irq.Instance = NULL;
res = pcmcia_request_irq(dev, &dev->irq);
if (res != 0)
goto err_disable;
diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c
index 1e318d815a5..3e046ec1ff8 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/b43/phy_lp.c
@@ -67,6 +67,7 @@ static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev)
struct b43_phy_lp *lpphy = phy->lp;
memset(lpphy, 0, sizeof(*lpphy));
+ lpphy->antenna = B43_ANTENNA_DEFAULT;
//TODO
}
@@ -751,11 +752,17 @@ static void lpphy_clear_deaf(struct b43_wldev *dev, bool user)
}
}
+static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx)
+{
+ u16 trsw = (tx << 1) | rx;
+ b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
+}
+
static void lpphy_disable_crs(struct b43_wldev *dev, bool user)
{
lpphy_set_deaf(dev, user);
- b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x1);
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
+ lpphy_set_trsw_over(dev, false, true);
b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB);
b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4);
b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7);
@@ -790,6 +797,60 @@ static void lpphy_restore_crs(struct b43_wldev *dev, bool user)
struct lpphy_tx_gains { u16 gm, pga, pad, dac; };
+static void lpphy_disable_rx_gain_override(struct b43_wldev *dev)
+{
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF);
+ if (dev->phy.rev >= 2) {
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF);
+ b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7);
+ }
+ } else {
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF);
+ }
+}
+
+static void lpphy_enable_rx_gain_override(struct b43_wldev *dev)
+{
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
+ if (dev->phy.rev >= 2) {
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400);
+ b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8);
+ }
+ } else {
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200);
+ }
+}
+
+static void lpphy_disable_tx_gain_override(struct b43_wldev *dev)
+{
+ if (dev->phy.rev < 2)
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
+ else {
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF);
+ }
+ b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF);
+}
+
+static void lpphy_enable_tx_gain_override(struct b43_wldev *dev)
+{
+ if (dev->phy.rev < 2)
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
+ else {
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000);
+ }
+ b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40);
+}
+
static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev)
{
struct lpphy_tx_gains gains;
@@ -819,6 +880,17 @@ static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac)
b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl);
}
+static u16 lpphy_get_pa_gain(struct b43_wldev *dev)
+{
+ return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F;
+}
+
+static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain)
+{
+ b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6);
+ b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8);
+}
+
static void lpphy_set_tx_gains(struct b43_wldev *dev,
struct lpphy_tx_gains gains)
{
@@ -829,25 +901,22 @@ static void lpphy_set_tx_gains(struct b43_wldev *dev,
b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
0xF800, rf_gain);
} else {
- pa_gain = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x1FC0;
- pa_gain <<= 2;
+ pa_gain = lpphy_get_pa_gain(dev);
b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
(gains.pga << 8) | gains.gm);
+ /*
+ * SPEC FIXME The spec calls for (pa_gain << 8) here, but that
+ * conflicts with the spec for set_pa_gain! Vendor driver bug?
+ */
b43_phy_maskset(dev, B43_PHY_OFDM(0xFB),
- 0x8000, gains.pad | pa_gain);
+ 0x8000, gains.pad | (pa_gain << 6));
b43_phy_write(dev, B43_PHY_OFDM(0xFC),
(gains.pga << 8) | gains.gm);
b43_phy_maskset(dev, B43_PHY_OFDM(0xFD),
- 0x8000, gains.pad | pa_gain);
+ 0x8000, gains.pad | (pa_gain << 8));
}
lpphy_set_dac_gain(dev, gains.dac);
- if (dev->phy.rev < 2) {
- b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF, 1 << 8);
- } else {
- b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F, 1 << 7);
- b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF, 1 << 14);
- }
- b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF, 1 << 6);
+ lpphy_enable_tx_gain_override(dev);
}
static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain)
@@ -887,38 +956,6 @@ static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain)
}
}
-static void lpphy_disable_rx_gain_override(struct b43_wldev *dev)
-{
- b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE);
- b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF);
- b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF);
- if (dev->phy.rev >= 2) {
- b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
- if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
- b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF);
- b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7);
- }
- } else {
- b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF);
- }
-}
-
-static void lpphy_enable_rx_gain_override(struct b43_wldev *dev)
-{
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1);
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
- if (dev->phy.rev >= 2) {
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
- if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400);
- b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8);
- }
- } else {
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200);
- }
-}
-
static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain)
{
if (dev->phy.rev < 2)
@@ -1003,8 +1040,7 @@ static int lpphy_loopback(struct b43_wldev *dev)
memset(&iq_est, 0, sizeof(iq_est));
- b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x3);
- b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
+ lpphy_set_trsw_over(dev, true, true);
b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1);
b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
@@ -1126,7 +1162,7 @@ static void lpphy_set_tx_power_control(struct b43_wldev *dev,
b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM,
0x8FFF, ((u16)lpphy->tssi_npt << 16));
//TODO Set "TSSI Transmit Count" variable to total transmitted frame count
- //TODO Disable TX gain override
+ lpphy_disable_tx_gain_override(dev);
lpphy->tx_pwr_idx_over = -1;
}
}
@@ -1312,15 +1348,73 @@ static void lpphy_calibrate_rc(struct b43_wldev *dev)
}
}
+static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
+{
+ if (dev->phy.rev >= 2)
+ return; // rev2+ doesn't support antenna diversity
+
+ if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1))
+ return;
+
+ b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP);
+
+ b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2);
+ b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1);
+
+ b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP);
+
+ dev->phy.lp->antenna = antenna;
+}
+
+static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b)
+{
+ u16 tmp[2];
+
+ tmp[0] = a;
+ tmp[1] = b;
+ b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp);
+}
+
static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index)
{
struct b43_phy_lp *lpphy = dev->phy.lp;
+ struct lpphy_tx_gains gains;
+ u32 iq_comp, tx_gain, coeff, rf_power;
lpphy->tx_pwr_idx_over = index;
+ lpphy_read_tx_pctl_mode_from_hardware(dev);
if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF)
lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW);
-
- //TODO
+ if (dev->phy.rev >= 2) {
+ iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320));
+ tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192));
+ gains.pad = (tx_gain >> 16) & 0xFF;
+ gains.gm = tx_gain & 0xFF;
+ gains.pga = (tx_gain >> 8) & 0xFF;
+ gains.dac = (iq_comp >> 28) & 0xFF;
+ lpphy_set_tx_gains(dev, gains);
+ } else {
+ iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320));
+ tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192));
+ b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
+ 0xF800, (tx_gain >> 4) & 0x7FFF);
+ lpphy_set_dac_gain(dev, tx_gain & 0x7);
+ lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F);
+ }
+ lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF);
+ lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF);
+ if (dev->phy.rev >= 2) {
+ coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448));
+ } else {
+ coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448));
+ }
+ b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF);
+ if (dev->phy.rev >= 2) {
+ rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576));
+ b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00,
+ rf_power & 0xFFFF);//SPEC FIXME mask & set != 0
+ }
+ lpphy_enable_tx_gain_override(dev);
}
static void lpphy_btcoex_override(struct b43_wldev *dev)
@@ -1329,58 +1423,45 @@ static void lpphy_btcoex_override(struct b43_wldev *dev)
b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF);
}
-static void lpphy_pr41573_workaround(struct b43_wldev *dev)
+static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
+ bool blocked)
{
- struct b43_phy_lp *lpphy = dev->phy.lp;
- u32 *saved_tab;
- const unsigned int saved_tab_size = 256;
- enum b43_lpphy_txpctl_mode txpctl_mode;
- s8 tx_pwr_idx_over;
- u16 tssi_npt, tssi_idx;
-
- saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL);
- if (!saved_tab) {
- b43err(dev->wl, "PR41573 failed. Out of memory!\n");
- return;
- }
-
- lpphy_read_tx_pctl_mode_from_hardware(dev);
- txpctl_mode = lpphy->txpctl_mode;
- tx_pwr_idx_over = lpphy->tx_pwr_idx_over;
- tssi_npt = lpphy->tssi_npt;
- tssi_idx = lpphy->tssi_idx;
-
- if (dev->phy.rev < 2) {
- b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140),
- saved_tab_size, saved_tab);
+ //TODO check MAC control register
+ if (blocked) {
+ if (dev->phy.rev >= 2) {
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
+ b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808);
+ } else {
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018);
+ }
} else {
- b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
- saved_tab_size, saved_tab);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF);
+ if (dev->phy.rev >= 2)
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7);
+ else
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7);
}
- //TODO
-
- kfree(saved_tab);
}
-static void lpphy_calibration(struct b43_wldev *dev)
+/* This was previously called lpphy_japan_filter */
+static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel)
{
struct b43_phy_lp *lpphy = dev->phy.lp;
- enum b43_lpphy_txpctl_mode saved_pctl_mode;
-
- b43_mac_suspend(dev);
-
- lpphy_btcoex_override(dev);
- lpphy_read_tx_pctl_mode_from_hardware(dev);
- saved_pctl_mode = lpphy->txpctl_mode;
- lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
- //TODO Perform transmit power table I/Q LO calibration
- if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF))
- lpphy_pr41573_workaround(dev);
- //TODO If a full calibration has not been performed on this channel yet, perform PAPD TX-power calibration
- lpphy_set_tx_power_control(dev, saved_pctl_mode);
- //TODO Perform I/Q calibration with a single control value set
+ u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter!
- b43_mac_enable(dev);
+ if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific?
+ b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9);
+ if ((dev->phy.rev == 1) && (lpphy->rc_cap))
+ lpphy_set_rc_cap(dev);
+ } else {
+ b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F);
+ }
}
static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode)
@@ -1489,6 +1570,473 @@ static void lpphy_tx_pctl_init(struct b43_wldev *dev)
}
}
+static void lpphy_pr41573_workaround(struct b43_wldev *dev)
+{
+ struct b43_phy_lp *lpphy = dev->phy.lp;
+ u32 *saved_tab;
+ const unsigned int saved_tab_size = 256;
+ enum b43_lpphy_txpctl_mode txpctl_mode;
+ s8 tx_pwr_idx_over;
+ u16 tssi_npt, tssi_idx;
+
+ saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL);
+ if (!saved_tab) {
+ b43err(dev->wl, "PR41573 failed. Out of memory!\n");
+ return;
+ }
+
+ lpphy_read_tx_pctl_mode_from_hardware(dev);
+ txpctl_mode = lpphy->txpctl_mode;
+ tx_pwr_idx_over = lpphy->tx_pwr_idx_over;
+ tssi_npt = lpphy->tssi_npt;
+ tssi_idx = lpphy->tssi_idx;
+
+ if (dev->phy.rev < 2) {
+ b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140),
+ saved_tab_size, saved_tab);
+ } else {
+ b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
+ saved_tab_size, saved_tab);
+ }
+ //FIXME PHY reset
+ lpphy_table_init(dev); //FIXME is table init needed?
+ lpphy_baseband_init(dev);
+ lpphy_tx_pctl_init(dev);
+ b43_lpphy_op_software_rfkill(dev, false);
+ lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
+ if (dev->phy.rev < 2) {
+ b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140),
+ saved_tab_size, saved_tab);
+ } else {
+ b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140),
+ saved_tab_size, saved_tab);
+ }
+ b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel);
+ lpphy->tssi_npt = tssi_npt;
+ lpphy->tssi_idx = tssi_idx;
+ lpphy_set_analog_filter(dev, lpphy->channel);
+ if (tx_pwr_idx_over != -1)
+ lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over);
+ if (lpphy->rc_cap)
+ lpphy_set_rc_cap(dev);
+ b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna);
+ lpphy_set_tx_power_control(dev, txpctl_mode);
+ kfree(saved_tab);
+}
+
+struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; };
+
+static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = {
+ { .chan = 1, .c1 = -66, .c0 = 15, },
+ { .chan = 2, .c1 = -66, .c0 = 15, },
+ { .chan = 3, .c1 = -66, .c0 = 15, },
+ { .chan = 4, .c1 = -66, .c0 = 15, },
+ { .chan = 5, .c1 = -66, .c0 = 15, },
+ { .chan = 6, .c1 = -66, .c0 = 15, },
+ { .chan = 7, .c1 = -66, .c0 = 14, },
+ { .chan = 8, .c1 = -66, .c0 = 14, },
+ { .chan = 9, .c1 = -66, .c0 = 14, },
+ { .chan = 10, .c1 = -66, .c0 = 14, },
+ { .chan = 11, .c1 = -66, .c0 = 14, },
+ { .chan = 12, .c1 = -66, .c0 = 13, },
+ { .chan = 13, .c1 = -66, .c0 = 13, },
+ { .chan = 14, .c1 = -66, .c0 = 13, },
+};
+
+static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = {
+ { .chan = 1, .c1 = -64, .c0 = 13, },
+ { .chan = 2, .c1 = -64, .c0 = 13, },
+ { .chan = 3, .c1 = -64, .c0 = 13, },
+ { .chan = 4, .c1 = -64, .c0 = 13, },
+ { .chan = 5, .c1 = -64, .c0 = 12, },
+ { .chan = 6, .c1 = -64, .c0 = 12, },
+ { .chan = 7, .c1 = -64, .c0 = 12, },
+ { .chan = 8, .c1 = -64, .c0 = 12, },
+ { .chan = 9, .c1 = -64, .c0 = 12, },
+ { .chan = 10, .c1 = -64, .c0 = 11, },
+ { .chan = 11, .c1 = -64, .c0 = 11, },
+ { .chan = 12, .c1 = -64, .c0 = 11, },
+ { .chan = 13, .c1 = -64, .c0 = 11, },
+ { .chan = 14, .c1 = -64, .c0 = 10, },
+ { .chan = 34, .c1 = -62, .c0 = 24, },
+ { .chan = 38, .c1 = -62, .c0 = 24, },
+ { .chan = 42, .c1 = -62, .c0 = 24, },
+ { .chan = 46, .c1 = -62, .c0 = 23, },
+ { .chan = 36, .c1 = -62, .c0 = 24, },
+ { .chan = 40, .c1 = -62, .c0 = 24, },
+ { .chan = 44, .c1 = -62, .c0 = 23, },
+ { .chan = 48, .c1 = -62, .c0 = 23, },
+ { .chan = 52, .c1 = -62, .c0 = 23, },
+ { .chan = 56, .c1 = -62, .c0 = 22, },
+ { .chan = 60, .c1 = -62, .c0 = 22, },
+ { .chan = 64, .c1 = -62, .c0 = 22, },
+ { .chan = 100, .c1 = -62, .c0 = 16, },
+ { .chan = 104, .c1 = -62, .c0 = 16, },
+ { .chan = 108, .c1 = -62, .c0 = 15, },
+ { .chan = 112, .c1 = -62, .c0 = 14, },
+ { .chan = 116, .c1 = -62, .c0 = 14, },
+ { .chan = 120, .c1 = -62, .c0 = 13, },
+ { .chan = 124, .c1 = -62, .c0 = 12, },
+ { .chan = 128, .c1 = -62, .c0 = 12, },
+ { .chan = 132, .c1 = -62, .c0 = 12, },
+ { .chan = 136, .c1 = -62, .c0 = 11, },
+ { .chan = 140, .c1 = -62, .c0 = 10, },
+ { .chan = 149, .c1 = -61, .c0 = 9, },
+ { .chan = 153, .c1 = -61, .c0 = 9, },
+ { .chan = 157, .c1 = -61, .c0 = 9, },
+ { .chan = 161, .c1 = -61, .c0 = 8, },
+ { .chan = 165, .c1 = -61, .c0 = 8, },
+ { .chan = 184, .c1 = -62, .c0 = 25, },
+ { .chan = 188, .c1 = -62, .c0 = 25, },
+ { .chan = 192, .c1 = -62, .c0 = 25, },
+ { .chan = 196, .c1 = -62, .c0 = 25, },
+ { .chan = 200, .c1 = -62, .c0 = 25, },
+ { .chan = 204, .c1 = -62, .c0 = 25, },
+ { .chan = 208, .c1 = -62, .c0 = 25, },
+ { .chan = 212, .c1 = -62, .c0 = 25, },
+ { .chan = 216, .c1 = -62, .c0 = 26, },
+};
+
+static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = {
+ .chan = 0,
+ .c1 = -64,
+ .c0 = 0,
+};
+
+static u8 lpphy_nbits(s32 val)
+{
+ u32 tmp = abs(val);
+ u8 nbits = 0;
+
+ while (tmp != 0) {
+ nbits++;
+ tmp >>= 1;
+ }
+
+ return nbits;
+}
+
+static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples)
+{
+ struct lpphy_iq_est iq_est;
+ u16 c0, c1;
+ int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret;
+
+ c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S);
+ c0 = c1 >> 8;
+ c1 |= 0xFF;
+
+ b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0);
+ b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF);
+
+ ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est);
+ if (!ret)
+ goto out;
+
+ prod = iq_est.iq_prod;
+ ipwr = iq_est.i_pwr;
+ qpwr = iq_est.q_pwr;
+
+ if (ipwr + qpwr < 2) {
+ ret = 0;
+ goto out;
+ }
+
+ prod_msb = lpphy_nbits(prod);
+ q_msb = lpphy_nbits(qpwr);
+ tmp1 = prod_msb - 20;
+
+ if (tmp1 >= 0) {
+ tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) /
+ (ipwr >> tmp1);
+ } else {
+ tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) /
+ (ipwr << -tmp1);
+ }
+
+ tmp2 = q_msb - 11;
+
+ if (tmp2 >= 0)
+ tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2);
+ else
+ tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2);
+
+ tmp4 -= tmp3 * tmp3;
+ tmp4 = -int_sqrt(tmp4);
+
+ c0 = tmp3 >> 3;
+ c1 = tmp4 >> 4;
+
+out:
+ b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1);
+ b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8);
+ return ret;
+}
+
+/* Complex number using 2 32-bit signed integers */
+typedef struct {s32 i, q;} lpphy_c32;
+
+static lpphy_c32 lpphy_cordic(int theta)
+{
+ u32 arctg[] = { 2949120, 1740967, 919879, 466945, 234379, 117304,
+ 58666, 29335, 14668, 7334, 3667, 1833, 917, 458,
+ 229, 115, 57, 29, };
+ int i, tmp, signx = 1, angle = 0;
+ lpphy_c32 ret = { .i = 39797, .q = 0, };
+
+ theta = clamp_t(int, theta, -180, 180);
+
+ if (theta > 90) {
+ theta -= 180;
+ signx = -1;
+ } else if (theta < -90) {
+ theta += 180;
+ signx = -1;
+ }
+
+ for (i = 0; i <= 17; i++) {
+ if (theta > angle) {
+ tmp = ret.i - (ret.q >> i);
+ ret.q += ret.i >> i;
+ ret.i = tmp;
+ angle += arctg[i];
+ } else {
+ tmp = ret.i + (ret.q >> i);
+ ret.q -= ret.i >> i;
+ ret.i = tmp;
+ angle -= arctg[i];
+ }
+ }
+
+ ret.i *= signx;
+ ret.q *= signx;
+
+ return ret;
+}
+
+static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops,
+ u16 wait)
+{
+ b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL,
+ 0xFFC0, samples - 1);
+ if (loops != 0xFFFF)
+ loops--;
+ b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops);
+ b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6);
+ b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1);
+}
+
+//SPEC FIXME what does a negative freq mean?
+static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
+{
+ struct b43_phy_lp *lpphy = dev->phy.lp;
+ u16 buf[64];
+ int i, samples = 0, angle = 0, rotation = (9 * freq) / 500;
+ lpphy_c32 sample;
+
+ lpphy->tx_tone_freq = freq;
+
+ if (freq) {
+ /* Find i for which abs(freq) integrally divides 20000 * i */
+ for (i = 1; samples * abs(freq) != 20000 * i; i++) {
+ samples = (20000 * i) / abs(freq);
+ if(B43_WARN_ON(samples > 63))
+ return;
+ }
+ } else {
+ samples = 2;
+ }
+
+ for (i = 0; i < samples; i++) {
+ sample = lpphy_cordic(angle);
+ angle += rotation;
+ buf[i] = ((sample.i * max) & 0xFF) << 8;
+ buf[i] |= (sample.q * max) & 0xFF;
+ }
+
+ b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf);
+
+ lpphy_run_samples(dev, samples, 0xFFFF, 0);
+}
+
+static void lpphy_stop_tx_tone(struct b43_wldev *dev)
+{
+ struct b43_phy_lp *lpphy = dev->phy.lp;
+ int i;
+
+ lpphy->tx_tone_freq = 0;
+
+ b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000);
+ for (i = 0; i < 31; i++) {
+ if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1))
+ break;
+ udelay(100);
+ }
+}
+
+
+static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains,
+ int mode, bool useindex, u8 index)
+{
+ //TODO
+}
+
+static void lpphy_papd_cal_txpwr(struct b43_wldev *dev)
+{
+ struct b43_phy_lp *lpphy = dev->phy.lp;
+ struct ssb_bus *bus = dev->dev->bus;
+ struct lpphy_tx_gains gains, oldgains;
+ int old_txpctl, old_afe_ovr, old_rf, old_bbmult;
+
+ lpphy_read_tx_pctl_mode_from_hardware(dev);
+ old_txpctl = lpphy->txpctl_mode;
+ old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;
+ if (old_afe_ovr)
+ oldgains = lpphy_get_tx_gains(dev);
+ old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF;
+ old_bbmult = lpphy_get_bb_mult(dev);
+
+ lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
+
+ if (bus->chip_id == 0x4325 && bus->chip_rev == 0)
+ lpphy_papd_cal(dev, gains, 0, 1, 30);
+ else
+ lpphy_papd_cal(dev, gains, 0, 1, 65);
+
+ if (old_afe_ovr)
+ lpphy_set_tx_gains(dev, oldgains);
+ lpphy_set_bb_mult(dev, old_bbmult);
+ lpphy_set_tx_power_control(dev, old_txpctl);
+ b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf);
+}
+
+static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx,
+ bool rx, bool pa, struct lpphy_tx_gains *gains)
+{
+ struct b43_phy_lp *lpphy = dev->phy.lp;
+ struct ssb_bus *bus = dev->dev->bus;
+ const struct lpphy_rx_iq_comp *iqcomp = NULL;
+ struct lpphy_tx_gains nogains, oldgains;
+ u16 tmp;
+ int i, ret;
+
+ memset(&nogains, 0, sizeof(nogains));
+ memset(&oldgains, 0, sizeof(oldgains));
+
+ if (bus->chip_id == 0x5354) {
+ for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) {
+ if (lpphy_5354_iq_table[i].chan == lpphy->channel) {
+ iqcomp = &lpphy_5354_iq_table[i];
+ }
+ }
+ } else if (dev->phy.rev >= 2) {
+ iqcomp = &lpphy_rev2plus_iq_comp;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) {
+ if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) {
+ iqcomp = &lpphy_rev0_1_iq_table[i];
+ }
+ }
+ }
+
+ if (B43_WARN_ON(!iqcomp))
+ return 0;
+
+ b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1);
+ b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S,
+ 0x00FF, iqcomp->c0 << 8);
+
+ if (noise) {
+ tx = true;
+ rx = false;
+ pa = false;
+ }
+
+ lpphy_set_trsw_over(dev, tx, rx);
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
+ b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
+ 0xFFF7, pa << 3);
+ } else {
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20);
+ b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
+ 0xFFDF, pa << 5);
+ }
+
+ tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;
+
+ if (noise)
+ lpphy_set_rx_gain(dev, 0x2D5D);
+ else {
+ if (tmp)
+ oldgains = lpphy_get_tx_gains(dev);
+ if (!gains)
+ gains = &nogains;
+ lpphy_set_tx_gains(dev, *gains);
+ }
+
+ b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
+ b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
+ b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800);
+ lpphy_set_deaf(dev, false);
+ if (noise)
+ ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0);
+ else {
+ lpphy_start_tx_tone(dev, 4000, 100);
+ ret = lpphy_calc_rx_iq_comp(dev, 0x4000);
+ lpphy_stop_tx_tone(dev);
+ }
+ lpphy_clear_deaf(dev, false);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7);
+ b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF);
+ if (!noise) {
+ if (tmp)
+ lpphy_set_tx_gains(dev, oldgains);
+ else
+ lpphy_disable_tx_gain_override(dev);
+ }
+ lpphy_disable_rx_gain_override(dev);
+ b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
+ b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF);
+ return ret;
+}
+
+static void lpphy_calibration(struct b43_wldev *dev)
+{
+ struct b43_phy_lp *lpphy = dev->phy.lp;
+ enum b43_lpphy_txpctl_mode saved_pctl_mode;
+ bool full_cal = false;
+
+ if (lpphy->full_calib_chan != lpphy->channel) {
+ full_cal = true;
+ lpphy->full_calib_chan = lpphy->channel;
+ }
+
+ b43_mac_suspend(dev);
+
+ lpphy_btcoex_override(dev);
+ if (dev->phy.rev >= 2)
+ lpphy_save_dig_flt_state(dev);
+ lpphy_read_tx_pctl_mode_from_hardware(dev);
+ saved_pctl_mode = lpphy->txpctl_mode;
+ lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
+ //TODO Perform transmit power table I/Q LO calibration
+ if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF))
+ lpphy_pr41573_workaround(dev);
+ if ((dev->phy.rev >= 2) && full_cal) {
+ lpphy_papd_cal_txpwr(dev);
+ }
+ lpphy_set_tx_power_control(dev, saved_pctl_mode);
+ if (dev->phy.rev >= 2)
+ lpphy_restore_dig_flt_state(dev);
+ lpphy_rx_iq_cal(dev, true, true, false, false, NULL);
+
+ b43_mac_enable(dev);
+}
+
static u16 b43_lpphy_op_read(struct b43_wldev *dev, u16 reg)
{
b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
@@ -1533,12 +2081,6 @@ static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
}
-static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
- bool blocked)
-{
- //TODO
-}
-
struct b206x_channel {
u8 channel;
u16 freq;
@@ -2004,22 +2546,6 @@ static int lpphy_b2062_tune(struct b43_wldev *dev,
return err;
}
-
-/* This was previously called lpphy_japan_filter */
-static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel)
-{
- struct b43_phy_lp *lpphy = dev->phy.lp;
- u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter!
-
- if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific?
- b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9);
- if ((dev->phy.rev == 1) && (lpphy->rc_cap))
- lpphy_set_rc_cap(dev);
- } else {
- b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F);
- }
-}
-
static void lpphy_b2063_vco_calib(struct b43_wldev *dev)
{
u16 tmp;
@@ -2204,18 +2730,6 @@ static int b43_lpphy_op_init(struct b43_wldev *dev)
return 0;
}
-static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
-{
- if (dev->phy.rev >= 2)
- return; // rev2+ doesn't support antenna diversity
-
- if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1))
- return;
-
- b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2);
- b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1);
-}
-
static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev)
{
//TODO
@@ -2238,6 +2752,11 @@ void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on)
}
}
+static void b43_lpphy_op_pwork_15sec(struct b43_wldev *dev)
+{
+ //TODO
+}
+
const struct b43_phy_operations b43_phyops_lp = {
.allocate = b43_lpphy_op_allocate,
.free = b43_lpphy_op_free,
@@ -2255,4 +2774,6 @@ const struct b43_phy_operations b43_phyops_lp = {
.set_rx_antenna = b43_lpphy_op_set_rx_antenna,
.recalc_txpower = b43_lpphy_op_recalc_txpower,
.adjust_txpower = b43_lpphy_op_adjust_txpower,
+ .pwork_15sec = b43_lpphy_op_pwork_15sec,
+ .pwork_60sec = lpphy_calibration,
};
diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/b43/phy_lp.h
index c3232c17b60..62737f700cb 100644
--- a/drivers/net/wireless/b43/phy_lp.h
+++ b/drivers/net/wireless/b43/phy_lp.h
@@ -286,6 +286,7 @@
#define B43_LPPHY_TR_LOOKUP_6 B43_PHY_OFDM(0xC8) /* TR Lookup 6 */
#define B43_LPPHY_TR_LOOKUP_7 B43_PHY_OFDM(0xC9) /* TR Lookup 7 */
#define B43_LPPHY_TR_LOOKUP_8 B43_PHY_OFDM(0xCA) /* TR Lookup 8 */
+#define B43_LPPHY_RF_PWR_OVERRIDE B43_PHY_OFDM(0xD3) /* RF power override */
@@ -871,12 +872,12 @@ struct b43_phy_lp {
u8 rssi_gs;
/* RC cap */
- u8 rc_cap; /* FIXME initial value? */
+ u8 rc_cap;
/* BX arch */
u8 bx_arch;
/* Full calibration channel */
- u8 full_calib_chan; /* FIXME initial value? */
+ u8 full_calib_chan;
/* Transmit iqlocal best coeffs */
bool tx_iqloc_best_coeffs_valid;
@@ -891,6 +892,12 @@ struct b43_phy_lp {
/* The channel we are tuned to */
u8 channel;
+
+ /* The active antenna diversity mode */
+ int antenna;
+
+ /* Frequency of the active TX tone */
+ int tx_tone_freq;
};
enum tssi_mux_mode {
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c
index 9b904440021..c01b8e02412 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/b43/pio.c
@@ -342,12 +342,15 @@ static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q,
q->mmio_base + B43_PIO_TXDATA,
sizeof(u16));
if (data_len & 1) {
+ u8 *tail = wl->pio_tailspace;
+ BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2);
+
/* Write the last byte. */
ctl &= ~B43_PIO_TXCTL_WRITEHI;
b43_piotx_write16(q, B43_PIO_TXCTL, ctl);
- wl->tx_tail[0] = data[data_len - 1];
- wl->tx_tail[1] = 0;
- ssb_block_write(dev->dev, wl->tx_tail, 2,
+ tail[0] = data[data_len - 1];
+ tail[1] = 0;
+ ssb_block_write(dev->dev, tail, 2,
q->mmio_base + B43_PIO_TXDATA,
sizeof(u16));
}
@@ -393,31 +396,31 @@ static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q,
q->mmio_base + B43_PIO8_TXDATA,
sizeof(u32));
if (data_len & 3) {
- wl->tx_tail[3] = 0;
+ u8 *tail = wl->pio_tailspace;
+ BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4);
+
+ memset(tail, 0, 4);
/* Write the last few bytes. */
ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 |
B43_PIO8_TXCTL_24_31);
switch (data_len & 3) {
case 3:
ctl |= B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_8_15;
- wl->tx_tail[0] = data[data_len - 3];
- wl->tx_tail[1] = data[data_len - 2];
- wl->tx_tail[2] = data[data_len - 1];
+ tail[0] = data[data_len - 3];
+ tail[1] = data[data_len - 2];
+ tail[2] = data[data_len - 1];
break;
case 2:
ctl |= B43_PIO8_TXCTL_8_15;
- wl->tx_tail[0] = data[data_len - 2];
- wl->tx_tail[1] = data[data_len - 1];
- wl->tx_tail[2] = 0;
+ tail[0] = data[data_len - 2];
+ tail[1] = data[data_len - 1];
break;
case 1:
- wl->tx_tail[0] = data[data_len - 1];
- wl->tx_tail[1] = 0;
- wl->tx_tail[2] = 0;
+ tail[0] = data[data_len - 1];
break;
}
b43_piotx_write32(q, B43_PIO8_TXCTL, ctl);
- ssb_block_write(dev->dev, wl->tx_tail, 4,
+ ssb_block_write(dev->dev, tail, 4,
q->mmio_base + B43_PIO8_TXDATA,
sizeof(u32));
}
@@ -456,6 +459,7 @@ static int pio_tx_frame(struct b43_pio_txqueue *q,
int err;
unsigned int hdrlen;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct b43_txhdr *txhdr = (struct b43_txhdr *)wl->pio_scratchspace;
B43_WARN_ON(list_empty(&q->packets_list));
pack = list_entry(q->packets_list.next,
@@ -463,7 +467,9 @@ static int pio_tx_frame(struct b43_pio_txqueue *q,
cookie = generate_cookie(q, pack);
hdrlen = b43_txhdr_size(dev);
- err = b43_generate_txhdr(dev, (u8 *)&wl->txhdr, skb,
+ BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(struct b43_txhdr));
+ B43_WARN_ON(sizeof(wl->pio_scratchspace) < hdrlen);
+ err = b43_generate_txhdr(dev, (u8 *)txhdr, skb,
info, cookie);
if (err)
return err;
@@ -477,9 +483,9 @@ static int pio_tx_frame(struct b43_pio_txqueue *q,
pack->skb = skb;
if (q->rev >= 8)
- pio_tx_frame_4byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen);
+ pio_tx_frame_4byte_queue(pack, (const u8 *)txhdr, hdrlen);
else
- pio_tx_frame_2byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen);
+ pio_tx_frame_2byte_queue(pack, (const u8 *)txhdr, hdrlen);
/* Remove it from the list of available packet slots.
* It will be put back when we receive the status report. */
@@ -625,8 +631,11 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q)
unsigned int i, padding;
struct sk_buff *skb;
const char *err_msg = NULL;
+ struct b43_rxhdr_fw4 *rxhdr =
+ (struct b43_rxhdr_fw4 *)wl->pio_scratchspace;
- memset(&wl->rxhdr, 0, sizeof(wl->rxhdr));
+ BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(*rxhdr));
+ memset(rxhdr, 0, sizeof(*rxhdr));
/* Check if we have data and wait for it to get ready. */
if (q->rev >= 8) {
@@ -664,16 +673,16 @@ data_ready:
/* Get the preamble (RX header) */
if (q->rev >= 8) {
- ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr),
+ ssb_block_read(dev->dev, rxhdr, sizeof(*rxhdr),
q->mmio_base + B43_PIO8_RXDATA,
sizeof(u32));
} else {
- ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr),
+ ssb_block_read(dev->dev, rxhdr, sizeof(*rxhdr),
q->mmio_base + B43_PIO_RXDATA,
sizeof(u16));
}
/* Sanity checks. */
- len = le16_to_cpu(wl->rxhdr.frame_len);
+ len = le16_to_cpu(rxhdr->frame_len);
if (unlikely(len > 0x700)) {
err_msg = "len > 0x700";
goto rx_error;
@@ -683,7 +692,7 @@ data_ready:
goto rx_error;
}
- macstat = le32_to_cpu(wl->rxhdr.mac_status);
+ macstat = le32_to_cpu(rxhdr->mac_status);
if (macstat & B43_RX_MAC_FCSERR) {
if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) {
/* Drop frames with failed FCS. */
@@ -708,22 +717,25 @@ data_ready:
q->mmio_base + B43_PIO8_RXDATA,
sizeof(u32));
if (len & 3) {
+ u8 *tail = wl->pio_tailspace;
+ BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4);
+
/* Read the last few bytes. */
- ssb_block_read(dev->dev, wl->rx_tail, 4,
+ ssb_block_read(dev->dev, tail, 4,
q->mmio_base + B43_PIO8_RXDATA,
sizeof(u32));
switch (len & 3) {
case 3:
- skb->data[len + padding - 3] = wl->rx_tail[0];
- skb->data[len + padding - 2] = wl->rx_tail[1];
- skb->data[len + padding - 1] = wl->rx_tail[2];
+ skb->data[len + padding - 3] = tail[0];
+ skb->data[len + padding - 2] = tail[1];
+ skb->data[len + padding - 1] = tail[2];
break;
case 2:
- skb->data[len + padding - 2] = wl->rx_tail[0];
- skb->data[len + padding - 1] = wl->rx_tail[1];
+ skb->data[len + padding - 2] = tail[0];
+ skb->data[len + padding - 1] = tail[1];
break;
case 1:
- skb->data[len + padding - 1] = wl->rx_tail[0];
+ skb->data[len + padding - 1] = tail[0];
break;
}
}
@@ -732,22 +744,29 @@ data_ready:
q->mmio_base + B43_PIO_RXDATA,
sizeof(u16));
if (len & 1) {
+ u8 *tail = wl->pio_tailspace;
+ BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2);
+
/* Read the last byte. */
- ssb_block_read(dev->dev, wl->rx_tail, 2,
+ ssb_block_read(dev->dev, tail, 2,
q->mmio_base + B43_PIO_RXDATA,
sizeof(u16));
- skb->data[len + padding - 1] = wl->rx_tail[0];
+ skb->data[len + padding - 1] = tail[0];
}
}
- b43_rx(q->dev, skb, &wl->rxhdr);
+ b43_rx(q->dev, skb, rxhdr);
return 1;
rx_error:
if (err_msg)
b43dbg(q->dev->wl, "PIO RX error: %s\n", err_msg);
- b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY);
+ if (q->rev >= 8)
+ b43_piorx_write32(q, B43_PIO8_RXCTL, B43_PIO8_RXCTL_DATARDY);
+ else
+ b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY);
+
return 1;
}
diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c
index ffdce6f3c90..78016ae21c5 100644
--- a/drivers/net/wireless/b43/rfkill.c
+++ b/drivers/net/wireless/b43/rfkill.c
@@ -33,8 +33,14 @@ bool b43_is_hw_radio_enabled(struct b43_wldev *dev)
& B43_MMIO_RADIO_HWENABLED_HI_MASK))
return 1;
} else {
- if (b43_status(dev) >= B43_STAT_STARTED &&
- b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO)
+ /* To prevent CPU fault on PPC, do not read a register
+ * unless the interface is started; however, on resume
+ * for hibernation, this routine is entered early. When
+ * that happens, unconditionally return TRUE.
+ */
+ if (b43_status(dev) < B43_STAT_STARTED)
+ return 1;
+ if (b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO)
& B43_MMIO_RADIO_HWENABLED_LO_MASK)
return 1;
}
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index f4e9695ec18..eda06529ef5 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -27,7 +27,7 @@
*/
-#include "b43.h"
+#include "xmit.h"
#include "phy_common.h"
#include "dma.h"
#include "pio.h"
@@ -621,7 +621,6 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
(phystat0 & B43_RX_PHYST0_OFDM),
(phystat0 & B43_RX_PHYST0_GAINCTL),
(phystat3 & B43_RX_PHYST3_TRSTATE));
- status.qual = (rxhdr->jssi * 100) / B43_RX_MAX_SSI;
}
if (phystat0 & B43_RX_PHYST0_OFDM)
@@ -690,10 +689,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
}
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
-
- local_bh_disable();
- ieee80211_rx(dev->wl->hw, skb);
- local_bh_enable();
+ ieee80211_rx_ni(dev->wl->hw, skb);
#if B43_DEBUG
dev->rx_count++;
diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h
index 3530de87187..d23ff9fe0c9 100644
--- a/drivers/net/wireless/b43/xmit.h
+++ b/drivers/net/wireless/b43/xmit.h
@@ -2,6 +2,8 @@
#define B43_XMIT_H_
#include "main.h"
+#include <net/mac80211.h>
+
#define _b43_declare_plcp_hdr(size) \
struct b43_plcp_hdr##size { \
@@ -332,4 +334,21 @@ static inline u8 b43_kidx_to_raw(struct b43_wldev *dev, u8 firmware_kidx)
return raw_kidx;
}
+/* struct b43_private_tx_info - TX info private to b43.
+ * The structure is placed in (struct ieee80211_tx_info *)->rate_driver_data
+ *
+ * @bouncebuffer: DMA Bouncebuffer (if used)
+ */
+struct b43_private_tx_info {
+ void *bouncebuffer;
+};
+
+static inline struct b43_private_tx_info *
+b43_get_priv_tx_info(struct ieee80211_tx_info *info)
+{
+ BUILD_BUG_ON(sizeof(struct b43_private_tx_info) >
+ sizeof(info->rate_driver_data));
+ return (struct b43_private_tx_info *)info->rate_driver_data;
+}
+
#endif /* B43_XMIT_H_ */
diff --git a/drivers/net/wireless/b43legacy/Kconfig b/drivers/net/wireless/b43legacy/Kconfig
index 94a46347805..1ffa28835c5 100644
--- a/drivers/net/wireless/b43legacy/Kconfig
+++ b/drivers/net/wireless/b43legacy/Kconfig
@@ -1,6 +1,6 @@
config B43LEGACY
tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)"
- depends on SSB_POSSIBLE && MAC80211 && WLAN_80211 && HAS_DMA
+ depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
select SSB
select FW_LOADER
---help---
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index 038baa8869e..89fe2f972c7 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -29,8 +29,6 @@
#define B43legacy_IRQWAIT_MAX_RETRIES 20
-#define B43legacy_RX_MAX_SSI 60 /* best guess at max ssi */
-
/* MMIO offsets */
#define B43legacy_MMIO_DMA0_REASON 0x20
#define B43legacy_MMIO_DMA0_IRQ_MASK 0x24
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c
index 86640341581..0a86bdf5315 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/b43legacy/dma.c
@@ -1240,8 +1240,9 @@ struct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev,
}
static int dma_tx_fragment(struct b43legacy_dmaring *ring,
- struct sk_buff *skb)
+ struct sk_buff **in_skb)
{
+ struct sk_buff *skb = *in_skb;
const struct b43legacy_dma_ops *ops = ring->ops;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
u8 *header;
@@ -1305,8 +1306,14 @@ static int dma_tx_fragment(struct b43legacy_dmaring *ring,
}
memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len);
+ memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb));
+ bounce_skb->dev = skb->dev;
+ skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb));
+ info = IEEE80211_SKB_CB(bounce_skb);
+
dev_kfree_skb_any(skb);
skb = bounce_skb;
+ *in_skb = bounce_skb;
meta->skb = skb;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
@@ -1360,8 +1367,10 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
struct sk_buff *skb)
{
struct b43legacy_dmaring *ring;
+ struct ieee80211_hdr *hdr;
int err = 0;
unsigned long flags;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
ring = priority_to_txring(dev, skb_get_queue_mapping(skb));
spin_lock_irqsave(&ring->lock, flags);
@@ -1386,7 +1395,11 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
goto out_unlock;
}
- err = dma_tx_fragment(ring, skb);
+ /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing
+ * into the skb data or cb now. */
+ hdr = NULL;
+ info = NULL;
+ err = dma_tx_fragment(ring, &skb);
if (unlikely(err == -ENOKEY)) {
/* Drop this packet, as we don't have the encryption key
* anymore and must not transmit it unencrypted. */
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 4b60148a5e6..4a905b6a886 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -2277,7 +2277,7 @@ static void do_periodic_work(struct b43legacy_wldev *dev)
/* Periodic work locking policy:
* The whole periodic work handler is protected by
* wl->mutex. If another lock is needed somewhere in the
- * pwork callchain, it's aquired in-place, where it's needed.
+ * pwork callchain, it's acquired in-place, where it's needed.
*/
static void b43legacy_periodic_work_handler(struct work_struct *work)
{
@@ -2677,7 +2677,7 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
if (conf->channel->hw_value != phy->channel)
b43legacy_radio_selectchannel(dev, conf->channel->hw_value, 0);
- dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_RADIOTAP);
+ dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR);
/* Adjust the desired TX power level. */
if (conf->power_level != 0) {
@@ -3593,7 +3593,7 @@ static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev)
{
struct b43legacy_wl *wl = dev->wl;
struct ssb_bus *bus = dev->dev->bus;
- struct pci_dev *pdev = bus->host_pci;
+ struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL;
int err;
int have_bphy = 0;
int have_gphy = 0;
@@ -3707,7 +3707,7 @@ static int b43legacy_one_core_attach(struct ssb_device *dev,
if (!list_empty(&wl->devlist)) {
/* We are not the first core on this chip. */
- pdev = dev->bus->host_pci;
+ pdev = (dev->bus->bustype == SSB_BUSTYPE_PCI) ? dev->bus->host_pci : NULL;
/* Only special chips support more than one wireless
* core, although some of the other chips have more than
* one wireless core as well. Check for this and
diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/b43legacy/rfkill.c
index 8783022db11..d579df72b78 100644
--- a/drivers/net/wireless/b43legacy/rfkill.c
+++ b/drivers/net/wireless/b43legacy/rfkill.c
@@ -34,6 +34,13 @@ bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
& B43legacy_MMIO_RADIO_HWENABLED_HI_MASK))
return 1;
} else {
+ /* To prevent CPU fault on PPC, do not read a register
+ * unless the interface is started; however, on resume
+ * for hibernation, this routine is entered early. When
+ * that happens, unconditionally return TRUE.
+ */
+ if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
+ return 1;
if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO)
& B43legacy_MMIO_RADIO_HWENABLED_LO_MASK)
return 1;
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c
index 103f3c9e7f5..9c8882d9275 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/b43legacy/xmit.c
@@ -549,7 +549,6 @@ void b43legacy_rx(struct b43legacy_wldev *dev,
(phystat0 & B43legacy_RX_PHYST0_GAINCTL),
(phystat3 & B43legacy_RX_PHYST3_TRSTATE));
status.noise = dev->stats.link_noise;
- status.qual = (jssi * 100) / B43legacy_RX_MAX_SSI;
/* change to support A PHY */
if (phystat0 & B43legacy_RX_PHYST0_OFDM)
status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false);
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig
index c15db229351..287d82728bc 100644
--- a/drivers/net/wireless/hostap/Kconfig
+++ b/drivers/net/wireless/hostap/Kconfig
@@ -1,7 +1,8 @@
config HOSTAP
tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
- depends on WLAN_80211
select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
select CRYPTO
select CRYPTO_ARC4
select CRYPTO_ECB
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index ad8eab4a639..c9640a3e02c 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -274,9 +274,6 @@ static int sandisk_enable_wireless(struct net_device *dev)
conf_reg_t reg;
struct hostap_interface *iface = netdev_priv(dev);
local_info_t *local = iface->local;
- tuple_t tuple;
- cisparse_t *parse = NULL;
- u_char buf[64];
struct hostap_cs_priv *hw_priv = local->hw_priv;
if (hw_priv->link->io.NumPorts1 < 0x42) {
@@ -285,28 +282,13 @@ static int sandisk_enable_wireless(struct net_device *dev)
goto done;
}
- parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
- if (parse == NULL) {
- ret = -ENOMEM;
- goto done;
- }
-
- tuple.Attributes = TUPLE_RETURN_COMMON;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
if (hw_priv->link->manf_id != 0xd601 || hw_priv->link->card_id != 0x0101) {
/* No SanDisk manfid found */
ret = -ENODEV;
goto done;
}
- tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
- if (pcmcia_get_first_tuple(hw_priv->link, &tuple) ||
- pcmcia_get_tuple_data(hw_priv->link, &tuple) ||
- pcmcia_parse_tuple(&tuple, parse) ||
- parse->longlink_mfc.nfn < 2) {
+ if (hw_priv->link->socket->functions < 2) {
/* No multi-function links found */
ret = -ENODEV;
goto done;
@@ -354,7 +336,6 @@ static int sandisk_enable_wireless(struct net_device *dev)
udelay(10);
done:
- kfree(parse);
return ret;
}
@@ -529,10 +510,6 @@ static void prism2_detach(struct pcmcia_device *link)
}
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
-
/* run after a CARD_INSERTION event is received to configure the PCMCIA
* socket and make the device available to the system */
@@ -624,7 +601,6 @@ static int prism2_config(struct pcmcia_device *link)
struct hostap_interface *iface;
local_info_t *local;
int ret = 1;
- int last_fn, last_ret;
struct hostap_cs_priv *hw_priv;
PDEBUG(DEBUG_FLOW, "prism2_config()\n");
@@ -636,19 +612,18 @@ static int prism2_config(struct pcmcia_device *link)
}
/* Look for an appropriate configuration table entry in the CIS */
- last_ret = pcmcia_loop_config(link, prism2_config_check, NULL);
- if (last_ret) {
+ ret = pcmcia_loop_config(link, prism2_config_check, NULL);
+ if (ret) {
if (!ignore_cis_vcc)
printk(KERN_ERR "GetNextTuple(): No matching "
"CIS configuration. Maybe you need the "
"ignore_cis_vcc=1 parameter.\n");
- cs_error(link, RequestIO, last_ret);
goto failed;
}
/* Need to allocate net_device before requesting IRQ handler */
dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
- &handle_to_dev(link));
+ &link->dev);
if (dev == NULL)
goto failed;
link->priv = dev;
@@ -666,13 +641,11 @@ static int prism2_config(struct pcmcia_device *link)
* irq structure is initialized.
*/
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING |
- IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = prism2_interrupt;
- link->irq.Instance = dev;
- CS_CHECK(RequestIRQ,
- pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
}
/*
@@ -680,8 +653,9 @@ static int prism2_config(struct pcmcia_device *link)
* the I/O windows and the interrupt mapping, and putting the
* card and host interface into "Memory and IO" mode.
*/
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
@@ -714,9 +688,6 @@ static int prism2_config(struct pcmcia_device *link)
}
return ret;
- cs_failed:
- cs_error(link, last_fn, last_ret);
-
failed:
kfree(hw_priv);
prism2_release((u_long)link);
diff --git a/drivers/net/wireless/i82593.h b/drivers/net/wireless/i82593.h
deleted file mode 100644
index afac5c7a323..00000000000
--- a/drivers/net/wireless/i82593.h
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Definitions for Intel 82593 CSMA/CD Core LAN Controller
- * The definitions are taken from the 1992 users manual with Intel
- * order number 297125-001.
- *
- * /usr/src/pc/RCS/i82593.h,v 1.1 1996/07/17 15:23:12 root Exp
- *
- * Copyright 1994, Anders Klemets <klemets@it.kth.se>
- *
- * HISTORY
- * i82593.h,v
- * Revision 1.4 2005/11/4 09:15:00 baroniunas
- * Modified copyright with permission of author as follows:
- *
- * "If I82539.H is the only file with my copyright statement
- * that is included in the Source Forge project, then you have
- * my approval to change the copyright statement to be a GPL
- * license, in the way you proposed on October 10."
- *
- * Revision 1.1 1996/07/17 15:23:12 root
- * Initial revision
- *
- * Revision 1.3 1995/04/05 15:13:58 adj
- * Initial alpha release
- *
- * Revision 1.2 1994/06/16 23:57:31 klemets
- * Mirrored all the fields in the configuration block.
- *
- * Revision 1.1 1994/06/02 20:25:34 klemets
- * Initial revision
- *
- *
- */
-#ifndef _I82593_H
-#define _I82593_H
-
-/* Intel 82593 CSMA/CD Core LAN Controller */
-
-/* Port 0 Command Register definitions */
-
-/* Execution operations */
-#define OP0_NOP 0 /* CHNL = 0 */
-#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */
-#define OP0_IA_SETUP 1
-#define OP0_CONFIGURE 2
-#define OP0_MC_SETUP 3
-#define OP0_TRANSMIT 4
-#define OP0_TDR 5
-#define OP0_DUMP 6
-#define OP0_DIAGNOSE 7
-#define OP0_TRANSMIT_NO_CRC 9
-#define OP0_RETRANSMIT 12
-#define OP0_ABORT 13
-/* Reception operations */
-#define OP0_RCV_ENABLE 8
-#define OP0_RCV_DISABLE 10
-#define OP0_STOP_RCV 11
-/* Status pointer control operations */
-#define OP0_FIX_PTR 15 /* CHNL = 1 */
-#define OP0_RLS_PTR 15 /* CHNL = 0 */
-#define OP0_RESET 14
-
-#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */
-#define CR0_STATUS_0 0x00
-#define CR0_STATUS_1 0x20
-#define CR0_STATUS_2 0x40
-#define CR0_STATUS_3 0x60
-#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */
-
-/* Port 0 Status Register definitions */
-
-#define SR0_NO_RESULT 0 /* dummy */
-#define SR0_EVENT_MASK 0x0f
-#define SR0_IA_SETUP_DONE 1
-#define SR0_CONFIGURE_DONE 2
-#define SR0_MC_SETUP_DONE 3
-#define SR0_TRANSMIT_DONE 4
-#define SR0_TDR_DONE 5
-#define SR0_DUMP_DONE 6
-#define SR0_DIAGNOSE_PASSED 7
-#define SR0_TRANSMIT_NO_CRC_DONE 9
-#define SR0_RETRANSMIT_DONE 12
-#define SR0_EXECUTION_ABORTED 13
-#define SR0_END_OF_FRAME 8
-#define SR0_RECEPTION_ABORTED 10
-#define SR0_DIAGNOSE_FAILED 15
-#define SR0_STOP_REG_HIT 11
-
-#define SR0_CHNL (1 << 4)
-#define SR0_EXECUTION (1 << 5)
-#define SR0_RECEPTION (1 << 6)
-#define SR0_INTERRUPT (1 << 7)
-#define SR0_BOTH_RX_TX (SR0_EXECUTION | SR0_RECEPTION)
-
-#define SR3_EXEC_STATE_MASK 0x03
-#define SR3_EXEC_IDLE 0
-#define SR3_TX_ABORT_IN_PROGRESS 1
-#define SR3_EXEC_ACTIVE 2
-#define SR3_ABORT_IN_PROGRESS 3
-#define SR3_EXEC_CHNL (1 << 2)
-#define SR3_STP_ON_NO_RSRC (1 << 3)
-#define SR3_RCVING_NO_RSRC (1 << 4)
-#define SR3_RCV_STATE_MASK 0x60
-#define SR3_RCV_IDLE 0x00
-#define SR3_RCV_READY 0x20
-#define SR3_RCV_ACTIVE 0x40
-#define SR3_RCV_STOP_IN_PROG 0x60
-#define SR3_RCV_CHNL (1 << 7)
-
-/* Port 1 Command Register definitions */
-
-#define OP1_NOP 0
-#define OP1_SWIT_TO_PORT_0 1
-#define OP1_INT_DISABLE 2
-#define OP1_INT_ENABLE 3
-#define OP1_SET_TS 5
-#define OP1_RST_TS 7
-#define OP1_POWER_DOWN 8
-#define OP1_RESET_RING_MNGMT 11
-#define OP1_RESET 14
-#define OP1_SEL_RST 15
-
-#define CR1_STATUS_4 0x00
-#define CR1_STATUS_5 0x20
-#define CR1_STATUS_6 0x40
-#define CR1_STOP_REG_UPDATE (1 << 7)
-
-/* Receive frame status bits */
-
-#define RX_RCLD (1 << 0)
-#define RX_IA_MATCH (1 << 1)
-#define RX_NO_AD_MATCH (1 << 2)
-#define RX_NO_SFD (1 << 3)
-#define RX_SRT_FRM (1 << 7)
-#define RX_OVRRUN (1 << 8)
-#define RX_ALG_ERR (1 << 10)
-#define RX_CRC_ERR (1 << 11)
-#define RX_LEN_ERR (1 << 12)
-#define RX_RCV_OK (1 << 13)
-#define RX_TYP_LEN (1 << 15)
-
-/* Transmit status bits */
-
-#define TX_NCOL_MASK 0x0f
-#define TX_FRTL (1 << 4)
-#define TX_MAX_COL (1 << 5)
-#define TX_HRT_BEAT (1 << 6)
-#define TX_DEFER (1 << 7)
-#define TX_UND_RUN (1 << 8)
-#define TX_LOST_CTS (1 << 9)
-#define TX_LOST_CRS (1 << 10)
-#define TX_LTCOL (1 << 11)
-#define TX_OK (1 << 13)
-#define TX_COLL (1 << 15)
-
-struct i82593_conf_block {
- u_char fifo_limit : 4,
- forgnesi : 1,
- fifo_32 : 1,
- d6mod : 1,
- throttle_enb : 1;
- u_char throttle : 6,
- cntrxint : 1,
- contin : 1;
- u_char addr_len : 3,
- acloc : 1,
- preamb_len : 2,
- loopback : 2;
- u_char lin_prio : 3,
- tbofstop : 1,
- exp_prio : 3,
- bof_met : 1;
- u_char : 4,
- ifrm_spc : 4;
- u_char : 5,
- slottim_low : 3;
- u_char slottim_hi : 3,
- : 1,
- max_retr : 4;
- u_char prmisc : 1,
- bc_dis : 1,
- : 1,
- crs_1 : 1,
- nocrc_ins : 1,
- crc_1632 : 1,
- : 1,
- crs_cdt : 1;
- u_char cs_filter : 3,
- crs_src : 1,
- cd_filter : 3,
- : 1;
- u_char : 2,
- min_fr_len : 6;
- u_char lng_typ : 1,
- lng_fld : 1,
- rxcrc_xf : 1,
- artx : 1,
- sarec : 1,
- tx_jabber : 1, /* why is this called max_len in the manual? */
- hash_1 : 1,
- lbpkpol : 1;
- u_char : 6,
- fdx : 1,
- : 1;
- u_char dummy_6 : 6, /* supposed to be ones */
- mult_ia : 1,
- dis_bof : 1;
- u_char dummy_1 : 1, /* supposed to be one */
- tx_ifs_retrig : 2,
- mc_all : 1,
- rcv_mon : 2,
- frag_acpt : 1,
- tstrttrs : 1;
- u_char fretx : 1,
- runt_eop : 1,
- hw_sw_pin : 1,
- big_endn : 1,
- syncrqs : 1,
- sttlen : 1,
- tx_eop : 1,
- rx_eop : 1;
- u_char rbuf_size : 5,
- rcvstop : 1,
- : 2;
-};
-
-#define I82593_MAX_MULTICAST_ADDRESSES 128 /* Hardware hashed filter */
-
-#endif /* _I82593_H */
diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig
index a8131384c6b..2715b101ade 100644
--- a/drivers/net/wireless/ipw2x00/Kconfig
+++ b/drivers/net/wireless/ipw2x00/Kconfig
@@ -4,8 +4,10 @@
config IPW2100
tristate "Intel PRO/Wireless 2100 Network Connection"
- depends on PCI && WLAN_80211 && CFG80211
+ depends on PCI && CFG80211
select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
select FW_LOADER
select LIB80211
select LIBIPW
@@ -63,8 +65,10 @@ config IPW2100_DEBUG
config IPW2200
tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection"
- depends on PCI && WLAN_80211 && CFG80211
+ depends on PCI && CFG80211 && CFG80211_WEXT
select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
select FW_LOADER
select LIB80211
select LIBIPW
@@ -150,8 +154,9 @@ config IPW2200_DEBUG
config LIBIPW
tristate
- depends on PCI && WLAN_80211 && CFG80211
+ depends on PCI && CFG80211
select WIRELESS_EXT
+ select WEXT_SPY
select CRYPTO
select CRYPTO_ARC4
select CRYPTO_ECB
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 6e2fc0cb6f8..56afcf041f8 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -296,6 +296,33 @@ static const char *command_types[] = {
};
#endif
+#define WEXT_USECHANNELS 1
+
+static const long ipw2100_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+
+#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies)
+
+static const long ipw2100_rates_11b[] = {
+ 1000000,
+ 2000000,
+ 5500000,
+ 11000000
+};
+
+static struct ieee80211_rate ipw2100_bg_rates[] = {
+ { .bitrate = 10 },
+ { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+};
+
+#define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b)
+
/* Pre-decl until we get the code solid and then we can clean it up */
static void ipw2100_tx_send_commands(struct ipw2100_priv *priv);
static void ipw2100_tx_send_data(struct ipw2100_priv *priv);
@@ -551,7 +578,7 @@ static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
/* get number of entries */
field_count = *(((u16 *) & field_info) + 1);
- /* abort if no enought memory */
+ /* abort if no enough memory */
total_length = field_len * field_count;
if (total_length > *len) {
*len = total_length;
@@ -1141,6 +1168,7 @@ static int rf_kill_active(struct ipw2100_priv *priv)
int i;
if (!(priv->hw_features & HW_FEATURE_RFKILL)) {
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
priv->status &= ~STATUS_RF_KILL_HW;
return 0;
}
@@ -1151,10 +1179,13 @@ static int rf_kill_active(struct ipw2100_priv *priv)
value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1);
}
- if (value == 0)
+ if (value == 0) {
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
priv->status |= STATUS_RF_KILL_HW;
- else
+ } else {
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
priv->status &= ~STATUS_RF_KILL_HW;
+ }
return (value == 0);
}
@@ -1814,13 +1845,6 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
return rc;
}
-/* Called by register_netdev() */
-static int ipw2100_net_init(struct net_device *dev)
-{
- struct ipw2100_priv *priv = libipw_priv(dev);
- return ipw2100_up(priv, 1);
-}
-
static void ipw2100_down(struct ipw2100_priv *priv)
{
unsigned long flags;
@@ -1875,6 +1899,64 @@ static void ipw2100_down(struct ipw2100_priv *priv)
netif_stop_queue(priv->net_dev);
}
+/* Called by register_netdev() */
+static int ipw2100_net_init(struct net_device *dev)
+{
+ struct ipw2100_priv *priv = libipw_priv(dev);
+ const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
+ struct wireless_dev *wdev = &priv->ieee->wdev;
+ int ret;
+ int i;
+
+ ret = ipw2100_up(priv, 1);
+ if (ret)
+ return ret;
+
+ memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
+
+ /* fill-out priv->ieee->bg_band */
+ if (geo->bg_channels) {
+ struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band;
+
+ bg_band->band = IEEE80211_BAND_2GHZ;
+ bg_band->n_channels = geo->bg_channels;
+ bg_band->channels =
+ kzalloc(geo->bg_channels *
+ sizeof(struct ieee80211_channel), GFP_KERNEL);
+ /* translate geo->bg to bg_band.channels */
+ for (i = 0; i < geo->bg_channels; i++) {
+ bg_band->channels[i].band = IEEE80211_BAND_2GHZ;
+ bg_band->channels[i].center_freq = geo->bg[i].freq;
+ bg_band->channels[i].hw_value = geo->bg[i].channel;
+ bg_band->channels[i].max_power = geo->bg[i].max_power;
+ if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_PASSIVE_SCAN;
+ if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_NO_IBSS;
+ if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_RADAR;
+ /* No equivalent for LIBIPW_CH_80211H_RULES,
+ LIBIPW_CH_UNIFORM_SPREADING, or
+ LIBIPW_CH_B_ONLY... */
+ }
+ /* point at bitrate info */
+ bg_band->bitrates = ipw2100_bg_rates;
+ bg_band->n_bitrates = RATE_COUNT;
+
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band;
+ }
+
+ set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
+ if (wiphy_register(wdev->wiphy)) {
+ ipw2100_down(priv);
+ return -EIO;
+ }
+ return 0;
+}
+
static void ipw2100_reset_adapter(struct work_struct *work)
{
struct ipw2100_priv *priv =
@@ -2090,6 +2172,7 @@ static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
priv->net_dev->name);
/* RF_KILL is now enabled (else we wouldn't be here) */
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
priv->status |= STATUS_RF_KILL_HW;
/* Make sure the RF Kill check timer is running */
@@ -3044,7 +3127,7 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
IPW_MAX_BDS)) {
/* TODO: Support merging buffers if more than
* IPW_MAX_BDS are used */
- IPW_DEBUG_INFO("%s: Maximum BD theshold exceeded. "
+ IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded. "
"Increase fragmentation level.\n",
priv->net_dev->name);
}
@@ -6029,7 +6112,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev,
struct ipw2100_priv *priv;
struct net_device *dev;
- dev = alloc_ieee80211(sizeof(struct ipw2100_priv));
+ dev = alloc_ieee80211(sizeof(struct ipw2100_priv), 0);
if (!dev)
return NULL;
priv = libipw_priv(dev);
@@ -6342,7 +6425,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
sysfs_remove_group(&pci_dev->dev.kobj,
&ipw2100_attribute_group);
- free_ieee80211(dev);
+ free_ieee80211(dev, 0);
pci_set_drvdata(pci_dev, NULL);
}
@@ -6400,7 +6483,10 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev)
if (dev->base_addr)
iounmap((void __iomem *)dev->base_addr);
- free_ieee80211(dev);
+ /* wiphy_unregister needs to be here, before free_ieee80211 */
+ wiphy_unregister(priv->ieee->wdev.wiphy);
+ kfree(priv->ieee->bg_band.channels);
+ free_ieee80211(dev, 0);
}
pci_release_regions(pci_dev);
@@ -6487,6 +6573,16 @@ static int ipw2100_resume(struct pci_dev *pci_dev)
}
#endif
+static void ipw2100_shutdown(struct pci_dev *pci_dev)
+{
+ struct ipw2100_priv *priv = pci_get_drvdata(pci_dev);
+
+ /* Take down the device; powers it off, etc. */
+ ipw2100_down(priv);
+
+ pci_disable_device(pci_dev);
+}
+
#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x }
static struct pci_device_id ipw2100_pci_id_table[] __devinitdata = {
@@ -6550,6 +6646,7 @@ static struct pci_driver ipw2100_pci_driver = {
.suspend = ipw2100_suspend,
.resume = ipw2100_resume,
#endif
+ .shutdown = ipw2100_shutdown,
};
/**
@@ -6601,26 +6698,6 @@ static void __exit ipw2100_exit(void)
module_init(ipw2100_init);
module_exit(ipw2100_exit);
-#define WEXT_USECHANNELS 1
-
-static const long ipw2100_frequencies[] = {
- 2412, 2417, 2422, 2427,
- 2432, 2437, 2442, 2447,
- 2452, 2457, 2462, 2467,
- 2472, 2484
-};
-
-#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies)
-
-static const long ipw2100_rates_11b[] = {
- 1000000,
- 2000000,
- 5500000,
- 11000000
-};
-
-#define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b)
-
static int ipw2100_wx_get_name(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
@@ -6820,7 +6897,7 @@ static int ipw2100_wx_get_range(struct net_device *dev,
range->max_qual.updated = 7; /* Updated all three */
range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
- /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
range->avg_qual.noise = 0;
range->avg_qual.updated = 7; /* Updated all three */
@@ -8462,6 +8539,12 @@ static int ipw2100_get_firmware(struct ipw2100_priv *priv,
return 0;
}
+MODULE_FIRMWARE(IPW2100_FW_NAME("-i"));
+#ifdef CONFIG_IPW2100_MONITOR
+MODULE_FIRMWARE(IPW2100_FW_NAME("-p"));
+#endif
+MODULE_FIRMWARE(IPW2100_FW_NAME(""));
+
static void ipw2100_release_firmware(struct ipw2100_priv *priv,
struct ipw2100_fw *fw)
{
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index a6ca536e44f..09ddd3e6bed 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -81,6 +81,11 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("ipw2200-ibss.fw");
+#ifdef CONFIG_IPW2200_MONITOR
+MODULE_FIRMWARE("ipw2200-sniffer.fw");
+#endif
+MODULE_FIRMWARE("ipw2200-bss.fw");
static int cmdlog = 0;
static int debug = 0;
@@ -104,6 +109,25 @@ static int antenna = CFG_SYS_ANTENNA_BOTH;
static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */
#endif
+static struct ieee80211_rate ipw2200_rates[] = {
+ { .bitrate = 10 },
+ { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 60 },
+ { .bitrate = 90 },
+ { .bitrate = 120 },
+ { .bitrate = 180 },
+ { .bitrate = 240 },
+ { .bitrate = 360 },
+ { .bitrate = 480 },
+ { .bitrate = 540 }
+};
+
+#define ipw2200_a_rates (ipw2200_rates + 4)
+#define ipw2200_num_a_rates 8
+#define ipw2200_bg_rates (ipw2200_rates + 0)
+#define ipw2200_num_bg_rates 12
#ifdef CONFIG_IPW2200_QOS
static int qos_enable = 0;
@@ -768,7 +792,7 @@ static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
/* get number of entries */
field_count = *(((u16 *) & field_info) + 1);
- /* abort if not enought memory */
+ /* abort if not enough memory */
total_len = field_len * field_count;
if (total_len > *len) {
*len = total_len;
@@ -1734,10 +1758,13 @@ static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO,
static int rf_kill_active(struct ipw_priv *priv)
{
- if (0 == (ipw_read32(priv, 0x30) & 0x10000))
+ if (0 == (ipw_read32(priv, 0x30) & 0x10000)) {
priv->status |= STATUS_RF_KILL_HW;
- else
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
+ } else {
priv->status &= ~STATUS_RF_KILL_HW;
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false);
+ }
return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0;
}
@@ -2020,6 +2047,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
if (inta & IPW_INTA_BIT_RF_KILL_DONE) {
IPW_DEBUG_RF_KILL("RF_KILL_DONE\n");
priv->status |= STATUS_RF_KILL_HW;
+ wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true);
wake_up_interruptible(&priv->wait_command_queue);
priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
cancel_delayed_work(&priv->request_scan);
@@ -7732,7 +7760,7 @@ static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
case SEC_LEVEL_0:
break;
default:
- printk(KERN_ERR "Unknow security level %d\n",
+ printk(KERN_ERR "Unknown security level %d\n",
priv->ieee->sec.level);
break;
}
@@ -8655,24 +8683,6 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option)
*
*/
-static int ipw_wx_get_name(struct net_device *dev,
- struct iw_request_info *info,
- union iwreq_data *wrqu, char *extra)
-{
- struct ipw_priv *priv = libipw_priv(dev);
- mutex_lock(&priv->mutex);
- if (priv->status & STATUS_RF_KILL_MASK)
- strcpy(wrqu->name, "radio off");
- else if (!(priv->status & STATUS_ASSOCIATED))
- strcpy(wrqu->name, "unassociated");
- else
- snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c",
- ipw_modes[priv->assoc_request.ieee_mode]);
- IPW_DEBUG_WX("Name: %s\n", wrqu->name);
- mutex_unlock(&priv->mutex);
- return 0;
-}
-
static int ipw_set_channel(struct ipw_priv *priv, u8 channel)
{
if (channel == 0) {
@@ -8916,7 +8926,7 @@ static int ipw_wx_get_range(struct net_device *dev,
range->max_qual.updated = 7; /* Updated all three */
range->avg_qual.qual = 70;
- /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
range->avg_qual.level = 0; /* FIXME to real average level */
range->avg_qual.noise = 0;
range->avg_qual.updated = 7; /* Updated all three */
@@ -9972,7 +9982,7 @@ static int ipw_wx_sw_reset(struct net_device *dev,
/* Rebase the WE IOCTLs to zero for the handler array */
#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
static iw_handler ipw_wx_handlers[] = {
- IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name,
+ IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname,
IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
@@ -10289,7 +10299,7 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb,
case SEC_LEVEL_0:
break;
default:
- printk(KERN_ERR "Unknow security level %d\n",
+ printk(KERN_ERR "Unknown security level %d\n",
priv->ieee->sec.level);
break;
}
@@ -11275,6 +11285,7 @@ static int ipw_up(struct ipw_priv *priv)
if (!(priv->config & CFG_CUSTOM_MAC))
eeprom_parse_mac(priv, priv->mac_addr);
memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN);
+ memcpy(priv->net_dev->perm_addr, priv->mac_addr, ETH_ALEN);
for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) {
if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE],
@@ -11416,16 +11427,100 @@ static void ipw_bg_down(struct work_struct *work)
/* Called by register_netdev() */
static int ipw_net_init(struct net_device *dev)
{
+ int i, rc = 0;
struct ipw_priv *priv = libipw_priv(dev);
+ const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
+ struct wireless_dev *wdev = &priv->ieee->wdev;
mutex_lock(&priv->mutex);
if (ipw_up(priv)) {
- mutex_unlock(&priv->mutex);
- return -EIO;
+ rc = -EIO;
+ goto out;
+ }
+
+ memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
+
+ /* fill-out priv->ieee->bg_band */
+ if (geo->bg_channels) {
+ struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band;
+
+ bg_band->band = IEEE80211_BAND_2GHZ;
+ bg_band->n_channels = geo->bg_channels;
+ bg_band->channels =
+ kzalloc(geo->bg_channels *
+ sizeof(struct ieee80211_channel), GFP_KERNEL);
+ /* translate geo->bg to bg_band.channels */
+ for (i = 0; i < geo->bg_channels; i++) {
+ bg_band->channels[i].band = IEEE80211_BAND_2GHZ;
+ bg_band->channels[i].center_freq = geo->bg[i].freq;
+ bg_band->channels[i].hw_value = geo->bg[i].channel;
+ bg_band->channels[i].max_power = geo->bg[i].max_power;
+ if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_PASSIVE_SCAN;
+ if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_NO_IBSS;
+ if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)
+ bg_band->channels[i].flags |=
+ IEEE80211_CHAN_RADAR;
+ /* No equivalent for LIBIPW_CH_80211H_RULES,
+ LIBIPW_CH_UNIFORM_SPREADING, or
+ LIBIPW_CH_B_ONLY... */
+ }
+ /* point at bitrate info */
+ bg_band->bitrates = ipw2200_bg_rates;
+ bg_band->n_bitrates = ipw2200_num_bg_rates;
+
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band;
+ }
+
+ /* fill-out priv->ieee->a_band */
+ if (geo->a_channels) {
+ struct ieee80211_supported_band *a_band = &priv->ieee->a_band;
+
+ a_band->band = IEEE80211_BAND_5GHZ;
+ a_band->n_channels = geo->a_channels;
+ a_band->channels =
+ kzalloc(geo->a_channels *
+ sizeof(struct ieee80211_channel), GFP_KERNEL);
+ /* translate geo->bg to a_band.channels */
+ for (i = 0; i < geo->a_channels; i++) {
+ a_band->channels[i].band = IEEE80211_BAND_2GHZ;
+ a_band->channels[i].center_freq = geo->a[i].freq;
+ a_band->channels[i].hw_value = geo->a[i].channel;
+ a_band->channels[i].max_power = geo->a[i].max_power;
+ if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY)
+ a_band->channels[i].flags |=
+ IEEE80211_CHAN_PASSIVE_SCAN;
+ if (geo->a[i].flags & LIBIPW_CH_NO_IBSS)
+ a_band->channels[i].flags |=
+ IEEE80211_CHAN_NO_IBSS;
+ if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT)
+ a_band->channels[i].flags |=
+ IEEE80211_CHAN_RADAR;
+ /* No equivalent for LIBIPW_CH_80211H_RULES,
+ LIBIPW_CH_UNIFORM_SPREADING, or
+ LIBIPW_CH_B_ONLY... */
+ }
+ /* point at bitrate info */
+ a_band->bitrates = ipw2200_a_rates;
+ a_band->n_bitrates = ipw2200_num_a_rates;
+
+ wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band;
+ }
+
+ set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
+
+ /* With that information in place, we can now register the wiphy... */
+ if (wiphy_register(wdev->wiphy)) {
+ rc = -EIO;
+ goto out;
}
+out:
mutex_unlock(&priv->mutex);
- return 0;
+ return rc;
}
/* PCI driver stuff */
@@ -11556,7 +11651,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv)
if (priv->prom_net_dev)
return -EPERM;
- priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv));
+ priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv), 1);
if (priv->prom_net_dev == NULL)
return -ENOMEM;
@@ -11575,7 +11670,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv)
rc = register_netdev(priv->prom_net_dev);
if (rc) {
- free_ieee80211(priv->prom_net_dev);
+ free_ieee80211(priv->prom_net_dev, 1);
priv->prom_net_dev = NULL;
return rc;
}
@@ -11589,7 +11684,7 @@ static void ipw_prom_free(struct ipw_priv *priv)
return;
unregister_netdev(priv->prom_net_dev);
- free_ieee80211(priv->prom_net_dev);
+ free_ieee80211(priv->prom_net_dev, 1);
priv->prom_net_dev = NULL;
}
@@ -11617,7 +11712,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev,
struct ipw_priv *priv;
int i;
- net_dev = alloc_ieee80211(sizeof(struct ipw_priv));
+ net_dev = alloc_ieee80211(sizeof(struct ipw_priv), 0);
if (net_dev == NULL) {
err = -ENOMEM;
goto out;
@@ -11765,7 +11860,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev,
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
out_free_ieee80211:
- free_ieee80211(priv->net_dev);
+ free_ieee80211(priv->net_dev, 0);
out:
return err;
}
@@ -11832,7 +11927,11 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- free_ieee80211(priv->net_dev);
+ /* wiphy_unregister needs to be here, before free_ieee80211 */
+ wiphy_unregister(priv->ieee->wdev.wiphy);
+ kfree(priv->ieee->a_band.channels);
+ kfree(priv->ieee->bg_band.channels);
+ free_ieee80211(priv->net_dev, 0);
free_firmware();
}
diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h
index 1e334ff6bd5..bf45391172f 100644
--- a/drivers/net/wireless/ipw2x00/libipw.h
+++ b/drivers/net/wireless/ipw2x00/libipw.h
@@ -31,6 +31,7 @@
#include <linux/ieee80211.h>
#include <net/lib80211.h>
+#include <net/cfg80211.h>
#define LIBIPW_VERSION "git-1.1.13"
@@ -783,12 +784,15 @@ struct libipw_geo {
struct libipw_device {
struct net_device *dev;
+ struct wireless_dev wdev;
struct libipw_security sec;
/* Bookkeeping structures */
struct libipw_stats ieee_stats;
struct libipw_geo geo;
+ struct ieee80211_supported_band bg_band;
+ struct ieee80211_supported_band a_band;
/* Probe / Beacon management */
struct list_head network_free_list;
@@ -1014,8 +1018,8 @@ static inline int libipw_is_cck_rate(u8 rate)
}
/* ieee80211.c */
-extern void free_ieee80211(struct net_device *dev);
-extern struct net_device *alloc_ieee80211(int sizeof_priv);
+extern void free_ieee80211(struct net_device *dev, int monitor);
+extern struct net_device *alloc_ieee80211(int sizeof_priv, int monitor);
extern int libipw_change_mtu(struct net_device *dev, int new_mtu);
extern void libipw_networks_age(struct libipw_device *ieee,
diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c
index eb2b60834c1..1ae0b2b02c3 100644
--- a/drivers/net/wireless/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/ipw2x00/libipw_module.c
@@ -62,6 +62,9 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_LICENSE("GPL");
+struct cfg80211_ops libipw_config_ops = { };
+void *libipw_wiphy_privid = &libipw_wiphy_privid;
+
static int libipw_networks_allocate(struct libipw_device *ieee)
{
if (ieee->networks)
@@ -140,7 +143,7 @@ int libipw_change_mtu(struct net_device *dev, int new_mtu)
}
EXPORT_SYMBOL(libipw_change_mtu);
-struct net_device *alloc_ieee80211(int sizeof_priv)
+struct net_device *alloc_ieee80211(int sizeof_priv, int monitor)
{
struct libipw_device *ieee;
struct net_device *dev;
@@ -157,10 +160,31 @@ struct net_device *alloc_ieee80211(int sizeof_priv)
ieee->dev = dev;
+ if (!monitor) {
+ ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0);
+ if (!ieee->wdev.wiphy) {
+ LIBIPW_ERROR("Unable to allocate wiphy.\n");
+ goto failed_free_netdev;
+ }
+
+ ieee->dev->ieee80211_ptr = &ieee->wdev;
+ ieee->wdev.iftype = NL80211_IFTYPE_STATION;
+
+ /* Fill-out wiphy structure bits we know... Not enough info
+ here to call set_wiphy_dev or set MAC address or channel info
+ -- have to do that in ->ndo_init... */
+ ieee->wdev.wiphy->privid = libipw_wiphy_privid;
+
+ ieee->wdev.wiphy->max_scan_ssids = 1;
+ ieee->wdev.wiphy->max_scan_ie_len = 0;
+ ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
+ | BIT(NL80211_IFTYPE_ADHOC);
+ }
+
err = libipw_networks_allocate(ieee);
if (err) {
LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err);
- goto failed_free_netdev;
+ goto failed_free_wiphy;
}
libipw_networks_initialize(ieee);
@@ -175,7 +199,7 @@ struct net_device *alloc_ieee80211(int sizeof_priv)
ieee->host_decrypt = 1;
ieee->host_mc_decrypt = 1;
- /* Host fragementation in Open mode. Default is enabled.
+ /* Host fragmentation in Open mode. Default is enabled.
* Note: host fragmentation is always enabled if host encryption
* is enabled. For cards can do hardware encryption, they must do
* hardware fragmentation as well. So we don't need a variable
@@ -193,19 +217,27 @@ struct net_device *alloc_ieee80211(int sizeof_priv)
return dev;
+failed_free_wiphy:
+ if (!monitor)
+ wiphy_free(ieee->wdev.wiphy);
failed_free_netdev:
free_netdev(dev);
failed:
return NULL;
}
-void free_ieee80211(struct net_device *dev)
+void free_ieee80211(struct net_device *dev, int monitor)
{
struct libipw_device *ieee = netdev_priv(dev);
lib80211_crypt_info_free(&ieee->crypt_info);
libipw_networks_free(ieee);
+
+ /* free cfg80211 resources */
+ if (!monitor)
+ wiphy_free(ieee->wdev.wiphy);
+
free_netdev(dev);
}
@@ -216,17 +248,22 @@ u32 libipw_debug_level = 0;
EXPORT_SYMBOL_GPL(libipw_debug_level);
static struct proc_dir_entry *libipw_proc = NULL;
-static int show_debug_level(char *page, char **start, off_t offset,
- int count, int *eof, void *data)
+static int debug_level_proc_show(struct seq_file *m, void *v)
{
- return snprintf(page, count, "0x%08X\n", libipw_debug_level);
+ seq_printf(m, "0x%08X\n", libipw_debug_level);
+ return 0;
}
-static int store_debug_level(struct file *file, const char __user * buffer,
- unsigned long count, void *data)
+static int debug_level_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_level_proc_show, NULL);
+}
+
+static ssize_t debug_level_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *pos)
{
char buf[] = "0x00000000\n";
- unsigned long len = min((unsigned long)sizeof(buf) - 1, count);
+ size_t len = min(sizeof(buf) - 1, count);
unsigned long val;
if (copy_from_user(buf, buffer, len))
@@ -240,6 +277,15 @@ static int store_debug_level(struct file *file, const char __user * buffer,
return strnlen(buf, len);
}
+
+static const struct file_operations debug_level_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_level_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = debug_level_proc_write,
+};
#endif /* CONFIG_LIBIPW_DEBUG */
static int __init libipw_init(void)
@@ -254,16 +300,13 @@ static int __init libipw_init(void)
" proc directory\n");
return -EIO;
}
- e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR,
- libipw_proc);
+ e = proc_create("debug_level", S_IRUGO | S_IWUSR, libipw_proc,
+ &debug_level_proc_fops);
if (!e) {
remove_proc_entry(DRV_NAME, init_net.proc_net);
libipw_proc = NULL;
return -EIO;
}
- e->read_proc = show_debug_level;
- e->write_proc = store_debug_level;
- e->data = NULL;
#endif /* CONFIG_LIBIPW_DEBUG */
printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n");
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index 99310c03325..b16b06c2031 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -1,17 +1,7 @@
config IWLWIFI
tristate "Intel Wireless Wifi"
- depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
- select LIB80211
+ depends on PCI && MAC80211 && EXPERIMENTAL
select FW_LOADER
- select MAC80211_LEDS if IWLWIFI_LEDS
- select LEDS_CLASS if IWLWIFI_LEDS
-
-config IWLWIFI_LEDS
- bool "Enable LED support in iwlagn and iwl3945 drivers"
- depends on IWLWIFI
- default y
- ---help---
- Select this if you want LED support.
config IWLWIFI_SPECTRUM_MEASUREMENT
bool "Enable Spectrum Measurement in iwlagn driver"
@@ -50,6 +40,24 @@ config IWLWIFI_DEBUGFS
---help---
Enable creation of debugfs files for the iwlwifi drivers.
+config IWLWIFI_DEVICE_TRACING
+ bool "iwlwifi device access tracing"
+ depends on IWLWIFI
+ depends on EVENT_TRACING
+ help
+ Say Y here to trace all commands, including TX frames and IO
+ accesses, sent to the device. If you say yes, iwlwifi will
+ register with the ftrace framework for event tracing and dump
+ all this information to the ringbuffer, you may need to
+ increase the ringbuffer size. See the ftrace documentation
+ for more information.
+
+ When tracing is not enabled, this option still has some
+ (though rather small) overhead.
+
+ If unsure, say Y so we can help you better when problems
+ occur.
+
config IWLAGN
tristate "Intel Wireless WiFi Next Gen AGN (iwlagn)"
depends on IWLWIFI
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 1d4e0a226fd..7f82044af24 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -1,20 +1,22 @@
obj-$(CONFIG_IWLWIFI) += iwlcore.o
iwlcore-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o
iwlcore-objs += iwl-rx.o iwl-tx.o iwl-sta.o iwl-calib.o
-iwlcore-objs += iwl-scan.o
+iwlcore-objs += iwl-scan.o iwl-led.o
iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o
-iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o
iwlcore-$(CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT) += iwl-spectrum.o
+iwlcore-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
+CFLAGS_iwl-devtrace.o := -I$(src)
+
+# AGN
obj-$(CONFIG_IWLAGN) += iwlagn.o
-iwlagn-objs := iwl-agn.o iwl-agn-rs.o
+iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o
iwlagn-$(CONFIG_IWL4965) += iwl-4965.o
iwlagn-$(CONFIG_IWL5000) += iwl-5000.o
iwlagn-$(CONFIG_IWL5000) += iwl-6000.o
iwlagn-$(CONFIG_IWL5000) += iwl-1000.o
+# 3945
obj-$(CONFIG_IWL3945) += iwl3945.o
iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl-3945-led.o
-
-
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 950267ab556..8414178bcff 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -44,6 +44,7 @@
#include "iwl-sta.h"
#include "iwl-helpers.h"
#include "iwl-5000-hw.h"
+#include "iwl-agn-led.h"
/* Highest firmware API version supported */
#define IWL1000_UCODE_API_MAX 3
@@ -76,7 +77,10 @@ static void iwl1000_set_ct_threshold(struct iwl_priv *priv)
/* NIC configuration for 1000 series */
static void iwl1000_nic_config(struct iwl_priv *priv)
{
- iwl5000_nic_config(priv);
+ /* set CSR_HW_CONFIG_REG for uCode use */
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+ CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
/* Setting digital SVR for 1000 card to 1.32V */
/* locking is acquired in iwl_set_bits_mask_prph() function */
@@ -106,9 +110,8 @@ static struct iwl_lib_ops iwl1000_lib = {
.send_tx_power = iwl5000_send_tx_power,
.update_chain_flags = iwl_update_chain_flags,
.apm_ops = {
- .init = iwl5000_apm_init,
- .reset = iwl5000_apm_reset,
- .stop = iwl5000_apm_stop,
+ .init = iwl_apm_init,
+ .stop = iwl_apm_stop,
.config = iwl1000_nic_config,
.set_pwr_src = iwl_set_pwr_src,
},
@@ -142,6 +145,7 @@ static struct iwl_ops iwl1000_ops = {
.lib = &iwl1000_lib,
.hcmd = &iwl5000_hcmd,
.utils = &iwl5000_hcmd_utils,
+ .led = &iwlagn_led_ops,
};
struct iwl_cfg iwl1000_bgn_cfg = {
@@ -152,15 +156,50 @@ struct iwl_cfg iwl1000_bgn_cfg = {
.sku = IWL_SKU_G|IWL_SKU_N,
.ops = &iwl1000_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
- .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_ver = EEPROM_1000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_A,
.valid_rx_ant = ANT_AB,
- .need_pll_cfg = true,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
.max_ll_items = OTP_MAX_LL_ITEMS_1000,
.shadow_ram_support = false,
.ht_greenfield_support = true,
+ .led_compensation = 51,
.use_rts_for_ht = true, /* use rts/cts protection */
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .support_ct_kill_exit = true,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
};
+struct iwl_cfg iwl1000_bg_cfg = {
+ .name = "1000 Series BG",
+ .fw_name_pre = IWL1000_FW_PRE,
+ .ucode_api_max = IWL1000_UCODE_API_MAX,
+ .ucode_api_min = IWL1000_UCODE_API_MIN,
+ .sku = IWL_SKU_G,
+ .ops = &iwl1000_ops,
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .eeprom_ver = EEPROM_1000_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
+ .mod_params = &iwl50_mod_params,
+ .valid_tx_ant = ANT_A,
+ .valid_rx_ant = ANT_AB,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
+ .max_ll_items = OTP_MAX_LL_ITEMS_1000,
+ .shadow_ram_support = false,
+ .ht_greenfield_support = true,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .support_ct_kill_exit = true,
+};
+
+MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
index 16772780c5b..6fd10d443ba 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
@@ -71,12 +71,6 @@
#include "iwl-eeprom.h"
-/*
- * uCode queue management definitions ...
- * Queue #4 is the command queue for 3945 and 4965.
- */
-#define IWL_CMD_QUEUE_NUM 4
-
/* Time constants */
#define SHORT_SLOT_TIME 9
#define LONG_SLOT_TIME 20
@@ -254,12 +248,6 @@ struct iwl3945_eeprom {
#define TFD_CTL_PAD_SET(n) (n << 28)
#define TFD_CTL_PAD_GET(ctl) (ctl >> 28)
-/*
- * RX related structures and functions
- */
-#define RX_FREE_BUFFERS 64
-#define RX_LOW_WATERMARK 8
-
/* Sizes and addresses for instruction and data memory (SRAM) in
* 3945's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */
#define IWL39_RTC_INST_LOWER_BOUND (0x000000)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
index 8c29ded7d02..a871d09d598 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
@@ -24,8 +24,6 @@
*
*****************************************************************************/
-#ifdef CONFIG_IWLWIFI_LEDS
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -43,388 +41,51 @@
#include "iwl-3945.h"
#include "iwl-core.h"
#include "iwl-dev.h"
+#include "iwl-3945-led.h"
-#ifdef CONFIG_IWLWIFI_DEBUG
-static const char *led_type_str[] = {
- __stringify(IWL_LED_TRG_TX),
- __stringify(IWL_LED_TRG_RX),
- __stringify(IWL_LED_TRG_ASSOC),
- __stringify(IWL_LED_TRG_RADIO),
- NULL
-};
-#endif /* CONFIG_IWLWIFI_DEBUG */
-
-static const struct {
- u16 brightness;
- u8 on_time;
- u8 off_time;
-} blink_tbl[] =
-{
- {300, 25, 25},
- {200, 40, 40},
- {100, 55, 55},
- {70, 65, 65},
- {50, 75, 75},
- {20, 85, 85},
- {15, 95, 95 },
- {10, 110, 110},
- {5, 130, 130},
- {0, 167, 167},
- /* SOLID_ON */
- {-1, IWL_LED_SOLID, 0}
-};
-
-#define IWL_1MB_RATE (128 * 1024)
-#define IWL_LED_THRESHOLD (16)
-#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /*Exclude Solid on*/
-#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
-
-static void iwl3945_led_cmd_callback(struct iwl_priv *priv,
- struct iwl_device_cmd *cmd,
- struct sk_buff *skb)
-{
-}
-
-static inline int iwl3945_brightness_to_idx(enum led_brightness brightness)
-{
- return fls(0x000000FF & (u32)brightness);
-}
/* Send led command */
-static int iwl_send_led_cmd(struct iwl_priv *priv,
- struct iwl_led_cmd *led_cmd)
+static int iwl3945_send_led_cmd(struct iwl_priv *priv,
+ struct iwl_led_cmd *led_cmd)
{
struct iwl_host_cmd cmd = {
.id = REPLY_LEDS_CMD,
.len = sizeof(struct iwl_led_cmd),
.data = led_cmd,
.flags = CMD_ASYNC,
- .callback = iwl3945_led_cmd_callback,
+ .callback = NULL,
};
return iwl_send_cmd(priv, &cmd);
}
-
-
-/* Set led on command */
-static int iwl3945_led_pattern(struct iwl_priv *priv, int led_id,
- unsigned int idx)
-{
- struct iwl_led_cmd led_cmd = {
- .id = led_id,
- .interval = IWL_DEF_LED_INTRVL
- };
-
- BUG_ON(idx > IWL_MAX_BLINK_TBL);
-
- led_cmd.on = blink_tbl[idx].on_time;
- led_cmd.off = blink_tbl[idx].off_time;
-
- return iwl_send_led_cmd(priv, &led_cmd);
-}
-
-
/* Set led on command */
-static int iwl3945_led_on(struct iwl_priv *priv, int led_id)
+static int iwl3945_led_on(struct iwl_priv *priv)
{
struct iwl_led_cmd led_cmd = {
- .id = led_id,
+ .id = IWL_LED_LINK,
.on = IWL_LED_SOLID,
.off = 0,
.interval = IWL_DEF_LED_INTRVL
};
- return iwl_send_led_cmd(priv, &led_cmd);
+ return iwl3945_send_led_cmd(priv, &led_cmd);
}
/* Set led off command */
-static int iwl3945_led_off(struct iwl_priv *priv, int led_id)
+static int iwl3945_led_off(struct iwl_priv *priv)
{
struct iwl_led_cmd led_cmd = {
- .id = led_id,
+ .id = IWL_LED_LINK,
.on = 0,
.off = 0,
.interval = IWL_DEF_LED_INTRVL
};
- IWL_DEBUG_LED(priv, "led off %d\n", led_id);
- return iwl_send_led_cmd(priv, &led_cmd);
+ IWL_DEBUG_LED(priv, "led off\n");
+ return iwl3945_send_led_cmd(priv, &led_cmd);
}
-/*
- * Set led on in case of association
- * */
-static int iwl3945_led_associate(struct iwl_priv *priv, int led_id)
-{
- IWL_DEBUG_LED(priv, "Associated\n");
-
- priv->allow_blinking = 1;
- return iwl3945_led_on(priv, led_id);
-}
-/* Set Led off in case of disassociation */
-static int iwl3945_led_disassociate(struct iwl_priv *priv, int led_id)
-{
- IWL_DEBUG_LED(priv, "Disassociated\n");
-
- priv->allow_blinking = 0;
-
- return 0;
-}
-
-/*
- * brightness call back function for Tx/Rx LED
- */
-static int iwl3945_led_associated(struct iwl_priv *priv, int led_id)
-{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
- !test_bit(STATUS_READY, &priv->status))
- return 0;
-
-
- /* start counting Tx/Rx bytes */
- if (!priv->last_blink_time && priv->allow_blinking)
- priv->last_blink_time = jiffies;
- return 0;
-}
-
-/*
- * brightness call back for association and radio
- */
-static void iwl3945_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct iwl_led *led = container_of(led_cdev,
- struct iwl_led, led_dev);
- struct iwl_priv *priv = led->priv;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
- IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
- led_type_str[led->type], brightness);
-
- switch (brightness) {
- case LED_FULL:
- if (led->led_on)
- led->led_on(priv, IWL_LED_LINK);
- break;
- case LED_OFF:
- if (led->led_off)
- led->led_off(priv, IWL_LED_LINK);
- break;
- default:
- if (led->led_pattern) {
- int idx = iwl3945_brightness_to_idx(brightness);
- led->led_pattern(priv, IWL_LED_LINK, idx);
- }
- break;
- }
-}
-
-/*
- * Register led class with the system
- */
-static int iwl3945_led_register_led(struct iwl_priv *priv,
- struct iwl_led *led,
- enum led_type type, u8 set_led,
- char *trigger)
-{
- struct device *device = wiphy_dev(priv->hw->wiphy);
- int ret;
-
- led->led_dev.name = led->name;
- led->led_dev.brightness_set = iwl3945_led_brightness_set;
- led->led_dev.default_trigger = trigger;
-
- led->priv = priv;
- led->type = type;
-
- ret = led_classdev_register(device, &led->led_dev);
- if (ret) {
- IWL_ERR(priv, "Error: failed to register led handler.\n");
- return ret;
- }
-
- led->registered = 1;
-
- if (set_led && led->led_on)
- led->led_on(priv, IWL_LED_LINK);
- return 0;
-}
-
-
-/*
- * calculate blink rate according to last 2 sec Tx/Rx activities
- */
-static inline u8 get_blink_rate(struct iwl_priv *priv)
-{
- int index;
- s64 tpt = priv->rxtxpackets;
-
- if (tpt < 0)
- tpt = -tpt;
-
- IWL_DEBUG_LED(priv, "tpt %lld \n", (long long)tpt);
-
- if (!priv->allow_blinking)
- index = IWL_MAX_BLINK_TBL;
- else
- for (index = 0; index < IWL_MAX_BLINK_TBL; index++)
- if (tpt > (blink_tbl[index].brightness * IWL_1MB_RATE))
- break;
-
- IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", index);
- return index;
-}
-
-/*
- * this function called from handler. Since setting Led command can
- * happen very frequent we postpone led command to be called from
- * REPLY handler so we know ucode is up
- */
-void iwl3945_led_background(struct iwl_priv *priv)
-{
- u8 blink_idx;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
- priv->last_blink_time = 0;
- return;
- }
- if (iwl_is_rfkill(priv)) {
- priv->last_blink_time = 0;
- return;
- }
-
- if (!priv->allow_blinking) {
- priv->last_blink_time = 0;
- if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
- priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
- iwl3945_led_pattern(priv, IWL_LED_LINK,
- IWL_SOLID_BLINK_IDX);
- }
- return;
- }
- if (!priv->last_blink_time ||
- !time_after(jiffies, priv->last_blink_time +
- msecs_to_jiffies(1000)))
- return;
-
- blink_idx = get_blink_rate(priv);
-
- /* call only if blink rate change */
- if (blink_idx != priv->last_blink_rate)
- iwl3945_led_pattern(priv, IWL_LED_LINK, blink_idx);
-
- priv->last_blink_time = jiffies;
- priv->last_blink_rate = blink_idx;
- priv->rxtxpackets = 0;
-}
-
-
-/* Register all led handler */
-int iwl3945_led_register(struct iwl_priv *priv)
-{
- char *trigger;
- int ret;
-
- priv->last_blink_rate = 0;
- priv->rxtxpackets = 0;
- priv->led_tpt = 0;
- priv->last_blink_time = 0;
- priv->allow_blinking = 0;
-
- trigger = ieee80211_get_radio_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_RADIO].name,
- sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
- wiphy_name(priv->hw->wiphy));
-
- priv->led[IWL_LED_TRG_RADIO].led_on = iwl3945_led_on;
- priv->led[IWL_LED_TRG_RADIO].led_off = iwl3945_led_off;
- priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL;
-
- ret = iwl3945_led_register_led(priv,
- &priv->led[IWL_LED_TRG_RADIO],
- IWL_LED_TRG_RADIO, 1, trigger);
-
- if (ret)
- goto exit_fail;
-
- trigger = ieee80211_get_assoc_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
- sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
- wiphy_name(priv->hw->wiphy));
-
- ret = iwl3945_led_register_led(priv,
- &priv->led[IWL_LED_TRG_ASSOC],
- IWL_LED_TRG_ASSOC, 0, trigger);
-
- /* for assoc always turn led on */
- priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_associate;
- priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_disassociate;
- priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
-
- if (ret)
- goto exit_fail;
-
- trigger = ieee80211_get_rx_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_RX].name,
- sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
- wiphy_name(priv->hw->wiphy));
-
- ret = iwl3945_led_register_led(priv,
- &priv->led[IWL_LED_TRG_RX],
- IWL_LED_TRG_RX, 0, trigger);
-
- priv->led[IWL_LED_TRG_RX].led_on = iwl3945_led_associated;
- priv->led[IWL_LED_TRG_RX].led_off = iwl3945_led_associated;
- priv->led[IWL_LED_TRG_RX].led_pattern = iwl3945_led_pattern;
-
- if (ret)
- goto exit_fail;
-
- trigger = ieee80211_get_tx_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_TX].name,
- sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
- wiphy_name(priv->hw->wiphy));
-
- ret = iwl3945_led_register_led(priv,
- &priv->led[IWL_LED_TRG_TX],
- IWL_LED_TRG_TX, 0, trigger);
-
- priv->led[IWL_LED_TRG_TX].led_on = iwl3945_led_associated;
- priv->led[IWL_LED_TRG_TX].led_off = iwl3945_led_associated;
- priv->led[IWL_LED_TRG_TX].led_pattern = iwl3945_led_pattern;
-
- if (ret)
- goto exit_fail;
-
- return 0;
-
-exit_fail:
- iwl3945_led_unregister(priv);
- return ret;
-}
-
-
-/* unregister led class */
-static void iwl3945_led_unregister_led(struct iwl_led *led, u8 set_led)
-{
- if (!led->registered)
- return;
-
- led_classdev_unregister(&led->led_dev);
-
- if (set_led)
- led->led_dev.brightness_set(&led->led_dev, LED_OFF);
- led->registered = 0;
-}
-
-/* Unregister all led handlers */
-void iwl3945_led_unregister(struct iwl_priv *priv)
-{
- iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0);
- iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RX], 0);
- iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_TX], 0);
- iwl3945_led_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1);
-}
-
-#endif
+const struct iwl_led_ops iwl3945_led_ops = {
+ .cmd = iwl3945_send_led_cmd,
+ .on = iwl3945_led_on,
+ .off = iwl3945_led_off,
+};
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.h b/drivers/net/wireless/iwlwifi/iwl-3945-led.h
index 3b65642258c..5a1033ca7aa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-led.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.h
@@ -24,23 +24,9 @@
*
*****************************************************************************/
-#ifndef IWL3945_LEDS_H
-#define IWL3945_LEDS_H
+#ifndef __iwl_3945_led_h__
+#define __iwl_3945_led_h__
-struct iwl_priv;
+extern const struct iwl_led_ops iwl3945_led_ops;
-#ifdef CONFIG_IWLWIFI_LEDS
-
-#include "iwl-led.h"
-
-extern int iwl3945_led_register(struct iwl_priv *priv);
-extern void iwl3945_led_unregister(struct iwl_priv *priv);
-extern void iwl3945_led_background(struct iwl_priv *priv);
-
-#else
-static inline int iwl3945_led_register(struct iwl_priv *priv) { return 0; }
-static inline void iwl3945_led_unregister(struct iwl_priv *priv) {}
-static inline void iwl3945_led_background(struct iwl_priv *priv) {}
-
-#endif /* IWLWIFI_LEDS*/
-#endif /* IWL3945_LEDS_H */
+#endif /* __iwl_3945_led_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
index cbb0585083a..d4b49883b30 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
@@ -42,38 +42,6 @@
#define RS_NAME "iwl-3945-rs"
-struct iwl3945_rate_scale_data {
- u64 data;
- s32 success_counter;
- s32 success_ratio;
- s32 counter;
- s32 average_tpt;
- unsigned long stamp;
-};
-
-struct iwl3945_rs_sta {
- spinlock_t lock;
- struct iwl_priv *priv;
- s32 *expected_tpt;
- unsigned long last_partial_flush;
- unsigned long last_flush;
- u32 flush_time;
- u32 last_tx_packets;
- u32 tx_packets;
- u8 tgg;
- u8 flush_pending;
- u8 start_rate;
- u8 ibss_sta_added;
- struct timer_list rate_scale_flush;
- struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945];
-#ifdef CONFIG_MAC80211_DEBUGFS
- struct dentry *rs_sta_dbgfs_stats_table_file;
-#endif
-
- /* used to be in sta_info */
- int last_txrate_idx;
-};
-
static s32 iwl3945_expected_tpt_g[IWL_RATE_COUNT_3945] = {
7, 13, 35, 58, 0, 0, 76, 104, 130, 168, 191, 202
};
@@ -370,6 +338,28 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
IWL_DEBUG_RATE(priv, "enter\n");
+ spin_lock_init(&rs_sta->lock);
+
+ rs_sta->priv = priv;
+
+ rs_sta->start_rate = IWL_RATE_INVALID;
+
+ /* default to just 802.11b */
+ rs_sta->expected_tpt = iwl3945_expected_tpt_b;
+
+ rs_sta->last_partial_flush = jiffies;
+ rs_sta->last_flush = jiffies;
+ rs_sta->flush_time = IWL_RATE_FLUSH;
+ rs_sta->last_tx_packets = 0;
+ rs_sta->ibss_sta_added = 0;
+
+ init_timer(&rs_sta->rate_scale_flush);
+ rs_sta->rate_scale_flush.data = (unsigned long)rs_sta;
+ rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush;
+
+ for (i = 0; i < IWL_RATE_COUNT_3945; i++)
+ iwl3945_clear_window(&rs_sta->win[i]);
+
/* TODO: what is a good starting rate for STA? About middle? Maybe not
* the lowest or the highest rate.. Could consider using RSSI from
* previous packets? Need to have IEEE 802.1X auth succeed immediately
@@ -409,45 +399,11 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp)
{
struct iwl3945_rs_sta *rs_sta;
struct iwl3945_sta_priv *psta = (void *) sta->drv_priv;
- struct iwl_priv *priv = iwl_priv;
- int i;
-
- /*
- * XXX: If it's using sta->drv_priv anyway, it might
- * as well just put all the information there.
- */
+ struct iwl_priv *priv __maybe_unused = iwl_priv;
IWL_DEBUG_RATE(priv, "enter\n");
- rs_sta = kzalloc(sizeof(struct iwl3945_rs_sta), gfp);
- if (!rs_sta) {
- IWL_DEBUG_RATE(priv, "leave: ENOMEM\n");
- return NULL;
- }
-
- psta->rs_sta = rs_sta;
-
- spin_lock_init(&rs_sta->lock);
-
- rs_sta->priv = priv;
-
- rs_sta->start_rate = IWL_RATE_INVALID;
-
- /* default to just 802.11b */
- rs_sta->expected_tpt = iwl3945_expected_tpt_b;
-
- rs_sta->last_partial_flush = jiffies;
- rs_sta->last_flush = jiffies;
- rs_sta->flush_time = IWL_RATE_FLUSH;
- rs_sta->last_tx_packets = 0;
- rs_sta->ibss_sta_added = 0;
-
- init_timer(&rs_sta->rate_scale_flush);
- rs_sta->rate_scale_flush.data = (unsigned long)rs_sta;
- rs_sta->rate_scale_flush.function = &iwl3945_bg_rate_scale_flush;
-
- for (i = 0; i < IWL_RATE_COUNT_3945; i++)
- iwl3945_clear_window(&rs_sta->win[i]);
+ rs_sta = &psta->rs_sta;
IWL_DEBUG_RATE(priv, "leave\n");
@@ -458,14 +414,11 @@ static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta,
void *priv_sta)
{
struct iwl3945_sta_priv *psta = (void *) sta->drv_priv;
- struct iwl3945_rs_sta *rs_sta = priv_sta;
+ struct iwl3945_rs_sta *rs_sta = &psta->rs_sta;
struct iwl_priv *priv __maybe_unused = rs_sta->priv;
- psta->rs_sta = NULL;
-
IWL_DEBUG_RATE(priv, "enter\n");
del_timer_sync(&rs_sta->rate_scale_flush);
- kfree(rs_sta);
IWL_DEBUG_RATE(priv, "leave\n");
}
@@ -960,14 +913,15 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
rcu_read_lock();
- sta = ieee80211_find_sta(hw, priv->stations[sta_id].sta.sta.addr);
+ sta = ieee80211_find_sta(priv->vif,
+ priv->stations[sta_id].sta.sta.addr);
if (!sta) {
rcu_read_unlock();
return;
}
psta = (void *) sta->drv_priv;
- rs_sta = psta->rs_sta;
+ rs_sta = &psta->rs_sta;
spin_lock_irqsave(&rs_sta->lock, flags);
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index f059b49dc69..7da1dab933d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -47,7 +47,8 @@
#include "iwl-eeprom.h"
#include "iwl-helpers.h"
#include "iwl-core.h"
-#include "iwl-agn-rs.h"
+#include "iwl-led.h"
+#include "iwl-3945-led.h"
#define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \
[IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
@@ -293,7 +294,7 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv,
static void iwl3945_rx_reply_tx(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
@@ -353,16 +354,12 @@ static void iwl3945_rx_reply_tx(struct iwl_priv *priv,
void iwl3945_hw_rx_statistics(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
(int)sizeof(struct iwl3945_notif_statistics),
le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
memcpy(&priv->statistics_39, pkt->u.raw, sizeof(priv->statistics_39));
-
- iwl3945_led_background(priv);
-
- priv->last_statistics_time = jiffies;
}
/******************************************************************************
@@ -545,14 +542,18 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb,
struct ieee80211_rx_status *stats)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IWL_RX_DATA(pkt);
struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
- short len = le16_to_cpu(rx_hdr->len);
+ u16 len = le16_to_cpu(rx_hdr->len);
+ struct sk_buff *skb;
+ int ret;
+ __le16 fc = hdr->frame_control;
/* We received data from the HW, so stop the watchdog */
- if (unlikely((len + IWL39_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) {
+ if (unlikely(len + IWL39_RX_FRAME_SIZE >
+ PAGE_SIZE << priv->hw_params.rx_page_order)) {
IWL_DEBUG_DROP(priv, "Corruption detected!\n");
return;
}
@@ -564,24 +565,50 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
return;
}
- skb_reserve(rxb->skb, (void *)rx_hdr->payload - (void *)pkt);
- /* Set the size of the skb to the size of the frame */
- skb_put(rxb->skb, le16_to_cpu(rx_hdr->len));
+ skb = alloc_skb(IWL_LINK_HDR_MAX * 2, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(priv, "alloc_skb failed\n");
+ return;
+ }
if (!iwl3945_mod_params.sw_crypto)
iwl_set_decrypted_flag(priv,
- (struct ieee80211_hdr *)rxb->skb->data,
+ (struct ieee80211_hdr *)rxb_addr(rxb),
le32_to_cpu(rx_end->status), stats);
-#ifdef CONFIG_IWLWIFI_LEDS
- if (ieee80211_is_data(hdr->frame_control))
- priv->rxtxpackets += len;
-#endif
- iwl_update_stats(priv, false, hdr->frame_control, len);
+ skb_reserve(skb, IWL_LINK_HDR_MAX);
+ skb_add_rx_frag(skb, 0, rxb->page,
+ (void *)rx_hdr->payload - (void *)pkt, len);
+
+ /* mac80211 currently doesn't support paged SKB. Convert it to
+ * linear SKB for management frame and data frame requires
+ * software decryption or software defragementation. */
+ if (ieee80211_is_mgmt(fc) ||
+ ieee80211_has_protected(fc) ||
+ ieee80211_has_morefrags(fc) ||
+ le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
+ ret = skb_linearize(skb);
+ else
+ ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ?
+ 0 : -ENOMEM;
+
+ if (ret) {
+ kfree_skb(skb);
+ goto out;
+ }
- memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats));
- ieee80211_rx_irqsafe(priv->hw, rxb->skb);
- rxb->skb = NULL;
+ /*
+ * XXX: We cannot touch the page and its virtual memory (pkt) after
+ * here. It might have already been freed by the above skb change.
+ */
+
+ iwl_update_stats(priv, false, fc, len);
+ memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
+
+ ieee80211_rx(priv->hw, skb);
+ out:
+ priv->alloc_rxb_page--;
+ rxb->page = NULL;
}
#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
@@ -591,7 +618,7 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv,
{
struct ieee80211_hdr *header;
struct ieee80211_rx_status rx_status;
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl3945_rx_frame_stats *rx_stats = IWL_RX_STATS(pkt);
struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt);
struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt);
@@ -791,29 +818,31 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv,
u8 data_retry_limit;
__le32 tx_flags;
__le16 fc = hdr->frame_control;
- struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
+ struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
rate = iwl3945_rates[rate_index].plcp;
- tx_flags = tx->tx_flags;
+ tx_flags = tx_cmd->tx_flags;
/* We need to figure out how to get the sta->supp_rates while
* in this running context */
rate_mask = IWL_RATES_MASK;
+
+ /* Set retry limit on DATA packets and Probe Responses*/
+ if (ieee80211_is_probe_resp(fc))
+ data_retry_limit = 3;
+ else
+ data_retry_limit = IWL_DEFAULT_TX_RETRY;
+ tx_cmd->data_retry_limit = data_retry_limit;
+
if (tx_id >= IWL_CMD_QUEUE_NUM)
rts_retry_limit = 3;
else
rts_retry_limit = 7;
- if (ieee80211_is_probe_resp(fc)) {
- data_retry_limit = 3;
- if (data_retry_limit < rts_retry_limit)
- rts_retry_limit = data_retry_limit;
- } else
- data_retry_limit = IWL_DEFAULT_TX_RETRY;
-
- if (priv->data_retry_limit != -1)
- data_retry_limit = priv->data_retry_limit;
+ if (data_retry_limit < rts_retry_limit)
+ rts_retry_limit = data_retry_limit;
+ tx_cmd->rts_retry_limit = rts_retry_limit;
if (ieee80211_is_mgmt(fc)) {
switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) {
@@ -831,22 +860,20 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv,
}
}
- tx->rts_retry_limit = rts_retry_limit;
- tx->data_retry_limit = data_retry_limit;
- tx->rate = rate;
- tx->tx_flags = tx_flags;
+ tx_cmd->rate = rate;
+ tx_cmd->tx_flags = tx_flags;
/* OFDM */
- tx->supp_rates[0] =
+ tx_cmd->supp_rates[0] =
((rate_mask & IWL_OFDM_RATES_MASK) >> IWL_FIRST_OFDM_RATE) & 0xFF;
/* CCK */
- tx->supp_rates[1] = (rate_mask & 0xF);
+ tx_cmd->supp_rates[1] = (rate_mask & 0xF);
IWL_DEBUG_RATE(priv, "Tx sta id: %d, rate: %d (plcp), flags: 0x%4X "
"cck/ofdm mask: 0x%x/0x%x\n", sta_id,
- tx->rate, le32_to_cpu(tx->tx_flags),
- tx->supp_rates[1], tx->supp_rates[0]);
+ tx_cmd->rate, le32_to_cpu(tx_cmd->tx_flags),
+ tx_cmd->supp_rates[1], tx_cmd->supp_rates[0]);
}
u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id, u16 tx_rate, u8 flags)
@@ -962,6 +989,11 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv)
iwl3945_hw_txq_ctx_free(priv);
+ /* allocate tx queue structure */
+ rc = iwl_alloc_txq_mem(priv);
+ if (rc)
+ return rc;
+
/* Tx CMD queue */
rc = iwl3945_tx_reset(priv);
if (rc)
@@ -986,41 +1018,25 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv)
return rc;
}
+
+/*
+ * Start up 3945's basic functionality after it has been reset
+ * (e.g. after platform boot, or shutdown via iwl_apm_stop())
+ * NOTE: This does not load uCode nor start the embedded processor
+ */
static int iwl3945_apm_init(struct iwl_priv *priv)
{
- int ret;
-
- iwl_power_initialize(priv);
-
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+ int ret = iwl_apm_init(priv);
- /* disable L0s without affecting L1 :don't wait for ICH L0s bug W/A) */
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
-
- /* set "initialization complete" bit to move adapter
- * D0U* --> D0A* state */
- iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (ret < 0) {
- IWL_DEBUG_INFO(priv, "Failed to init the card\n");
- goto out;
- }
-
- /* enable DMA */
- iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT |
- APMG_CLK_VAL_BSM_CLK_RQT);
-
- udelay(20);
+ /* Clear APMG (NIC's internal power management) interrupts */
+ iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0);
+ iwl_write_prph(priv, APMG_RTC_INT_STT_REG, 0xFFFFFFFF);
- /* disable L1-Active */
- iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+ /* Reset radio chip */
+ iwl_set_bits_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ);
+ udelay(5);
+ iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ);
-out:
return ret;
}
@@ -1145,12 +1161,16 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv)
int txq_id;
/* Tx queues */
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
- if (txq_id == IWL_CMD_QUEUE_NUM)
- iwl_cmd_queue_free(priv);
- else
- iwl_tx_queue_free(priv, txq_id);
+ if (priv->txq)
+ for (txq_id = 0; txq_id < priv->hw_params.max_txq_num;
+ txq_id++)
+ if (txq_id == IWL_CMD_QUEUE_NUM)
+ iwl_cmd_queue_free(priv);
+ else
+ iwl_tx_queue_free(priv, txq_id);
+ /* free tx queue structure */
+ iwl_free_txq_mem(priv);
}
void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
@@ -1159,6 +1179,7 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
/* stop SCD */
iwl_write_prph(priv, ALM_SCD_MODE_REG, 0);
+ iwl_write_prph(priv, ALM_SCD_TXFACT_REG, 0);
/* reset TFD queues */
for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) {
@@ -1171,85 +1192,6 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
iwl3945_hw_txq_ctx_free(priv);
}
-static int iwl3945_apm_stop_master(struct iwl_priv *priv)
-{
- int ret = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* set stop master bit */
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
-
- iwl_poll_direct_bit(priv, CSR_RESET,
- CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
-
- if (ret < 0)
- goto out;
-
-out:
- spin_unlock_irqrestore(&priv->lock, flags);
- IWL_DEBUG_INFO(priv, "stop master\n");
-
- return ret;
-}
-
-static void iwl3945_apm_stop(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- iwl3945_apm_stop_master(priv);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
- /* clear "init complete" move adapter D0A* --> D0U state */
- iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static int iwl3945_apm_reset(struct iwl_priv *priv)
-{
- iwl3945_apm_stop_master(priv);
-
-
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
- udelay(10);
-
- iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
-
- iwl_write_prph(priv, APMG_CLK_CTRL_REG,
- APMG_CLK_VAL_BSM_CLK_RQT);
-
- iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0);
- iwl_write_prph(priv, APMG_RTC_INT_STT_REG,
- 0xFFFFFFFF);
-
- /* enable DMA */
- iwl_write_prph(priv, APMG_CLK_EN_REG,
- APMG_CLK_VAL_DMA_CLK_RQT |
- APMG_CLK_VAL_BSM_CLK_RQT);
- udelay(10);
-
- iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_RESET_REQ);
- udelay(5);
- iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
- APMG_PS_CTRL_VAL_RESET_REQ);
-
- /* Clear the 'host command active' bit... */
- clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
-
- wake_up_interruptible(&priv->wait_command_queue);
-
- return 0;
-}
-
/**
* iwl3945_hw_reg_adjust_power_by_temp
* return index delta into power gain settings table
@@ -1858,7 +1800,7 @@ int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power)
static int iwl3945_send_rxon_assoc(struct iwl_priv *priv)
{
int rc = 0;
- struct iwl_rx_packet *res = NULL;
+ struct iwl_rx_packet *pkt;
struct iwl3945_rxon_assoc_cmd rxon_assoc;
struct iwl_host_cmd cmd = {
.id = REPLY_RXON_ASSOC,
@@ -1887,14 +1829,14 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv)
if (rc)
return rc;
- res = (struct iwl_rx_packet *)cmd.reply_skb->data;
- if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+ pkt = (struct iwl_rx_packet *)cmd.reply_page;
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from REPLY_RXON_ASSOC command\n");
rc = -EIO;
}
- priv->alloc_rxb_skb--;
- dev_kfree_skb_any(cmd.reply_skb);
+ priv->alloc_rxb_page--;
+ free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
return rc;
}
@@ -2042,12 +1984,6 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
return 0;
}
-/* will add 3945 channel switch cmd handling later */
-int iwl3945_hw_channel_switch(struct iwl_priv *priv, u16 channel)
-{
- return 0;
-}
-
/**
* iwl3945_reg_txpower_periodic - called when time to check our temperature.
*
@@ -2557,11 +2493,10 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv)
}
/* Assign number of Usable TX queues */
- priv->hw_params.max_txq_num = IWL39_NUM_QUEUES;
+ priv->hw_params.max_txq_num = priv->cfg->num_of_queues;
priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd);
- priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K;
- priv->hw_params.max_pkt_size = 2342;
+ priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_3K);
priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
priv->hw_params.max_stations = IWL3945_STATION_COUNT;
@@ -2844,8 +2779,7 @@ static struct iwl_lib_ops iwl3945_lib = {
.dump_nic_error_log = iwl3945_dump_nic_error_log,
.apm_ops = {
.init = iwl3945_apm_init,
- .reset = iwl3945_apm_reset,
- .stop = iwl3945_apm_stop,
+ .stop = iwl_apm_stop,
.config = iwl3945_nic_config,
.set_pwr_src = iwl3945_set_pwr_src,
},
@@ -2874,6 +2808,7 @@ static struct iwl_lib_ops iwl3945_lib = {
static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = {
.get_hcmd_size = iwl3945_get_hcmd_size,
.build_addsta_hcmd = iwl3945_build_addsta_hcmd,
+ .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag,
};
static struct iwl_ops iwl3945_ops = {
@@ -2881,6 +2816,7 @@ static struct iwl_ops iwl3945_ops = {
.lib = &iwl3945_lib,
.hcmd = &iwl3945_hcmd,
.utils = &iwl3945_hcmd_utils,
+ .led = &iwl3945_led_ops,
};
static struct iwl_cfg iwl3945_bg_cfg = {
@@ -2892,9 +2828,14 @@ static struct iwl_cfg iwl3945_bg_cfg = {
.eeprom_size = IWL3945_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_3945_EEPROM_VERSION,
.ops = &iwl3945_ops,
+ .num_of_queues = IWL39_NUM_QUEUES,
.mod_params = &iwl3945_mod_params,
+ .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL,
+ .set_l0s = false,
+ .use_bsm = true,
.use_isr_legacy = true,
.ht_greenfield_support = false,
+ .led_compensation = 64,
};
static struct iwl_cfg iwl3945_abg_cfg = {
@@ -2906,9 +2847,11 @@ static struct iwl_cfg iwl3945_abg_cfg = {
.eeprom_size = IWL3945_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_3945_EEPROM_VERSION,
.ops = &iwl3945_ops,
+ .num_of_queues = IWL39_NUM_QUEUES,
.mod_params = &iwl3945_mod_params,
.use_isr_legacy = true,
.ht_greenfield_support = false,
+ .led_compensation = 64,
};
struct pci_device_id iwl3945_hw_card_ids[] = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index 21679bf3a1a..ecc23ec1f6a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -46,7 +46,7 @@ extern struct pci_device_id iwl3945_hw_card_ids[];
#include "iwl-debug.h"
#include "iwl-power.h"
#include "iwl-dev.h"
-#include "iwl-3945-led.h"
+#include "iwl-led.h"
/* Highest firmware API version supported */
#define IWL3945_UCODE_API_MAX 2
@@ -74,8 +74,41 @@ extern struct pci_device_id iwl3945_hw_card_ids[];
/* Module parameters accessible from iwl-*.c */
extern struct iwl_mod_params iwl3945_mod_params;
+struct iwl3945_rate_scale_data {
+ u64 data;
+ s32 success_counter;
+ s32 success_ratio;
+ s32 counter;
+ s32 average_tpt;
+ unsigned long stamp;
+};
+
+struct iwl3945_rs_sta {
+ spinlock_t lock;
+ struct iwl_priv *priv;
+ s32 *expected_tpt;
+ unsigned long last_partial_flush;
+ unsigned long last_flush;
+ u32 flush_time;
+ u32 last_tx_packets;
+ u32 tx_packets;
+ u8 tgg;
+ u8 flush_pending;
+ u8 start_rate;
+ u8 ibss_sta_added;
+ struct timer_list rate_scale_flush;
+ struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945];
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct dentry *rs_sta_dbgfs_stats_table_file;
+#endif
+
+ /* used to be in sta_info */
+ int last_txrate_idx;
+};
+
+
struct iwl3945_sta_priv {
- struct iwl3945_rs_sta *rs_sta;
+ struct iwl3945_rs_sta rs_sta;
};
enum iwl3945_antenna {
@@ -130,12 +163,6 @@ struct iwl3945_frame {
#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ)
#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4)
-/*
- * RX related structures and functions
- */
-#define RX_FREE_BUFFERS 64
-#define RX_LOW_WATERMARK 8
-
#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
@@ -194,22 +221,13 @@ struct iwl3945_ibss_seq {
* for use by iwl-*.c
*
*****************************************************************************/
-extern int iwl3945_power_init_handle(struct iwl_priv *priv);
-extern int iwl3945_eeprom_init(struct iwl_priv *priv);
extern int iwl3945_calc_db_from_ratio(int sig_ratio);
extern int iwl3945_calc_sig_qual(int rssi_dbm, int noise_dbm);
-extern int iwl3945_tx_queue_init(struct iwl_priv *priv,
- struct iwl_tx_queue *txq, int count, u32 id);
extern void iwl3945_rx_replenish(void *data);
extern void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
-extern void iwl3945_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq);
-extern int iwl3945_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len,
- const void *data);
-extern int __must_check iwl3945_send_cmd(struct iwl_priv *priv,
- struct iwl_host_cmd *cmd);
extern unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,int left);
-extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv);
+extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log);
extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv);
/*
@@ -280,8 +298,6 @@ extern void iwl3945_config_ap(struct iwl_priv *priv);
*/
extern u8 iwl3945_hw_find_station(struct iwl_priv *priv, const u8 *bssid);
-extern int iwl3945_hw_channel_switch(struct iwl_priv *priv, u16 channel);
-
/*
* Forward declare iwl-3945.c functions for iwl-base.c
*/
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
index b34322a3245..c606366b582 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
@@ -76,12 +76,9 @@
/*
* uCode queue management definitions ...
- * Queue #4 is the command queue for 3945 and 4965; map it to Tx FIFO chnl 4.
* The first queue used for block-ack aggregation is #7 (4965 only).
* All block-ack aggregation queues should map to Tx DMA/FIFO channel 7.
*/
-#define IWL_CMD_QUEUE_NUM 4
-#define IWL_CMD_FIFO_NUM 4
#define IWL49_FIRST_AMPDU_QUEUE 7
/* Time constants */
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 6f703a04184..386513b601f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -45,6 +45,7 @@
#include "iwl-helpers.h"
#include "iwl-calib.h"
#include "iwl-sta.h"
+#include "iwl-agn-led.h"
static int iwl4965_send_tx_power(struct iwl_priv *priv);
static int iwl4965_hw_get_temperature(struct iwl_priv *priv);
@@ -62,8 +63,6 @@ static int iwl4965_hw_get_temperature(struct iwl_priv *priv);
/* module parameters */
static struct iwl_mod_params iwl4965_mod_params = {
- .num_of_queues = IWL49_NUM_QUEUES,
- .num_of_ampdu_queues = IWL49_NUM_AMPDU_QUEUES,
.amsdu_size_8K = 1,
.restart_fw = 1,
/* the rest are 0 by default */
@@ -319,63 +318,13 @@ static void iwl4965_txq_set_sched(struct iwl_priv *priv, u32 mask)
iwl_write_prph(priv, IWL49_SCD_TXFACT, mask);
}
-static int iwl4965_apm_init(struct iwl_priv *priv)
-{
- int ret = 0;
-
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
-
- /* disable L0s without affecting L1 :don't wait for ICH L0s bug W/A) */
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
-
- /* set "initialization complete" bit to move adapter
- * D0U* --> D0A* state */
- iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /* wait for clock stabilization */
- ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (ret < 0) {
- IWL_DEBUG_INFO(priv, "Failed to init the card\n");
- goto out;
- }
-
- /* enable DMA */
- iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT |
- APMG_CLK_VAL_BSM_CLK_RQT);
-
- udelay(20);
-
- /* disable L1-Active */
- iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-
-out:
- return ret;
-}
-
-
static void iwl4965_nic_config(struct iwl_priv *priv)
{
unsigned long flags;
u16 radio_cfg;
- u16 lctl;
spin_lock_irqsave(&priv->lock, flags);
- lctl = iwl_pcie_link_ctl(priv);
-
- /* HW bug W/A - negligible power consumption */
- /* L1-ASPM is enabled by BIOS */
- if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN)
- /* L1-ASPM enabled: disable L0S */
- iwl_set_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- else
- /* L1-ASPM disabled: enable L0S */
- iwl_clear_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
-
radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
/* write radio config values to register */
@@ -396,79 +345,6 @@ static void iwl4965_nic_config(struct iwl_priv *priv)
spin_unlock_irqrestore(&priv->lock, flags);
}
-static int iwl4965_apm_stop_master(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* set stop master bit */
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
-
- iwl_poll_direct_bit(priv, CSR_RESET,
- CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- IWL_DEBUG_INFO(priv, "stop master\n");
-
- return 0;
-}
-
-static void iwl4965_apm_stop(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- iwl4965_apm_stop_master(priv);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
- /* clear "init complete" move adapter D0A* --> D0U state */
- iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static int iwl4965_apm_reset(struct iwl_priv *priv)
-{
- int ret = 0;
-
- iwl4965_apm_stop_master(priv);
-
-
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
-
- /* FIXME: put here L1A -L0S w/a */
-
- iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (ret < 0)
- goto out;
-
- udelay(10);
-
- /* Enable DMA and BSM Clock */
- iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT |
- APMG_CLK_VAL_BSM_CLK_RQT);
-
- udelay(10);
-
- /* disable L1A */
- iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-
- clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
- wake_up_interruptible(&priv->wait_command_queue);
-
-out:
- return ret;
-}
-
/* Reset differential Rx gains in NIC to prepare for chain noise calibration.
* Called after every association, but this runs only once!
* ... once chain noise is calibrated the first time, it's good forever. */
@@ -496,14 +372,15 @@ static void iwl4965_chain_noise_reset(struct iwl_priv *priv)
static void iwl4965_gain_computation(struct iwl_priv *priv,
u32 *average_noise,
u16 min_average_noise_antenna_i,
- u32 min_average_noise)
+ u32 min_average_noise,
+ u8 default_chain)
{
int i, ret;
struct iwl_chain_noise_data *data = &priv->chain_noise_data;
data->delta_gain_code[min_average_noise_antenna_i] = 0;
- for (i = 0; i < NUM_RX_CHAINS; i++) {
+ for (i = default_chain; i < NUM_RX_CHAINS; i++) {
s32 delta_g = 0;
if (!(data->disconn_array[i]) &&
@@ -557,18 +434,6 @@ static void iwl4965_gain_computation(struct iwl_priv *priv,
data->beacon_count = 0;
}
-static void iwl4965_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
- __le32 *tx_flags)
-{
- if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
- *tx_flags |= TX_CMD_FLG_RTS_MSK;
- *tx_flags &= ~TX_CMD_FLG_CTS_MSK;
- } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
- *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
- *tx_flags |= TX_CMD_FLG_CTS_MSK;
- }
-}
-
static void iwl4965_bg_txpower_work(struct work_struct *work)
{
struct iwl_priv *priv = container_of(work, struct iwl_priv,
@@ -663,7 +528,8 @@ static int iwl4965_alive_notify(struct iwl_priv *priv)
iwl_write_targ_mem(priv, a, 0);
for (; a < priv->scd_base_addr + IWL49_SCD_TRANSLATE_TBL_OFFSET; a += 4)
iwl_write_targ_mem(priv, a, 0);
- for (; a < sizeof(u16) * priv->hw_params.max_txq_num; a += 4)
+ for (; a < priv->scd_base_addr +
+ IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(priv->hw_params.max_txq_num); a += 4)
iwl_write_targ_mem(priv, a, 0);
/* Tel 4965 where to find Tx byte count tables */
@@ -748,6 +614,10 @@ static struct iwl_sensitivity_ranges iwl4965_sensitivity = {
.nrg_th_cck = 100,
.nrg_th_ofdm = 100,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
};
static void iwl4965_set_ct_threshold(struct iwl_priv *priv)
@@ -764,19 +634,16 @@ static void iwl4965_set_ct_threshold(struct iwl_priv *priv)
*/
static int iwl4965_hw_set_hw_params(struct iwl_priv *priv)
{
+ if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES &&
+ priv->cfg->mod_params->num_of_queues <= IWL49_NUM_QUEUES)
+ priv->cfg->num_of_queues =
+ priv->cfg->mod_params->num_of_queues;
- if ((priv->cfg->mod_params->num_of_queues > IWL49_NUM_QUEUES) ||
- (priv->cfg->mod_params->num_of_queues < IWL_MIN_NUM_QUEUES)) {
- IWL_ERR(priv,
- "invalid queues_num, should be between %d and %d\n",
- IWL_MIN_NUM_QUEUES, IWL49_NUM_QUEUES);
- return -EINVAL;
- }
-
- priv->hw_params.max_txq_num = priv->cfg->mod_params->num_of_queues;
+ priv->hw_params.max_txq_num = priv->cfg->num_of_queues;
priv->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM;
priv->hw_params.scd_bc_tbls_size =
- IWL49_NUM_QUEUES * sizeof(struct iwl4965_scd_bc_tbl);
+ priv->cfg->num_of_queues *
+ sizeof(struct iwl4965_scd_bc_tbl);
priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
priv->hw_params.max_stations = IWL4965_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL4965_BROADCAST_ID;
@@ -787,10 +654,10 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv)
priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
- priv->hw_params.tx_chains_num = 2;
- priv->hw_params.rx_chains_num = 2;
- priv->hw_params.valid_tx_ant = ANT_A | ANT_B;
- priv->hw_params.valid_rx_ant = ANT_A | ANT_B;
+ priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+ priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
+ priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
+ priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
@@ -1567,14 +1434,13 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv)
return ret;
}
-#ifdef IEEE80211_CONF_CHANNEL_SWITCH
static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
{
int rc;
u8 band = 0;
bool is_ht40 = false;
u8 ctrl_chan_high = 0;
- struct iwl4965_channel_switch_cmd cmd = { 0 };
+ struct iwl4965_channel_switch_cmd cmd;
const struct iwl_channel_info *ch_info;
band = priv->band == IEEE80211_BAND_2GHZ;
@@ -1584,19 +1450,22 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
is_ht40 = is_ht40_channel(priv->staging_rxon.flags);
if (is_ht40 &&
- (priv->active_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK))
+ (priv->staging_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK))
ctrl_chan_high = 1;
cmd.band = band;
cmd.expect_beacon = 0;
cmd.channel = cpu_to_le16(channel);
- cmd.rxon_flags = priv->active_rxon.flags;
- cmd.rxon_filter_flags = priv->active_rxon.filter_flags;
+ cmd.rxon_flags = priv->staging_rxon.flags;
+ cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
if (ch_info)
cmd.expect_beacon = is_channel_radar(ch_info);
- else
- cmd.expect_beacon = 1;
+ else {
+ IWL_ERR(priv, "invalid channel switch from %u to %u\n",
+ priv->active_rxon.channel, channel);
+ return -EFAULT;
+ }
rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_ht40,
ctrl_chan_high, &cmd.tx_power);
@@ -1605,10 +1474,11 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
return rc;
}
- rc = iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
- return rc;
+ priv->switch_rxon.channel = cpu_to_le16(channel);
+ priv->switch_rxon.switch_in_progress = true;
+
+ return iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd);
}
-#endif
/**
* iwl4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
@@ -1805,11 +1675,13 @@ static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
u16 ssn_idx, u8 tx_fifo)
{
if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES <= txq_id)) {
+ (IWL49_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
+ <= txq_id)) {
IWL_WARN(priv,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWL49_FIRST_AMPDU_QUEUE,
- IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES - 1);
+ IWL49_FIRST_AMPDU_QUEUE +
+ priv->cfg->num_of_ampdu_queues - 1);
return -EINVAL;
}
@@ -1870,11 +1742,13 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id,
u16 ra_tid;
if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES <= txq_id)) {
+ (IWL49_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
+ <= txq_id)) {
IWL_WARN(priv,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWL49_FIRST_AMPDU_QUEUE,
- IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES - 1);
+ IWL49_FIRST_AMPDU_QUEUE +
+ priv->cfg->num_of_ampdu_queues - 1);
return -EINVAL;
}
@@ -1944,8 +1818,9 @@ static u16 iwl4965_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data)
addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid;
addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid;
addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn;
+ addsta->sleep_tx_count = cmd->sleep_tx_count;
addsta->reserved1 = cpu_to_le16(0);
- addsta->reserved2 = cpu_to_le32(0);
+ addsta->reserved2 = cpu_to_le16(0);
return (u16)sizeof(struct iwl4965_addsta_cmd);
}
@@ -1991,8 +1866,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]);
info->status.rates[0].count = tx_resp->failure_frame + 1;
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- info->flags |= iwl_is_tx_success(status) ?
- IEEE80211_TX_STAT_ACK : 0;
+ info->flags |= iwl_tx_status_to_mac80211(status);
iwl_hwrate_to_tx_control(priv, rate_n_flags, info);
/* FIXME: code repetition end */
@@ -2078,7 +1952,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv,
static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
@@ -2147,8 +2021,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv,
}
} else {
info->status.rates[0].count = tx_resp->failure_frame + 1;
- info->flags |= iwl_is_tx_success(status) ?
- IEEE80211_TX_STAT_ACK : 0;
+ info->flags |= iwl_tx_status_to_mac80211(status);
iwl_hwrate_to_tx_control(priv,
le32_to_cpu(tx_resp->rate_n_flags),
info);
@@ -2279,7 +2152,7 @@ static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = {
.build_addsta_hcmd = iwl4965_build_addsta_hcmd,
.chain_noise_reset = iwl4965_chain_noise_reset,
.gain_computation = iwl4965_gain_computation,
- .rts_tx_cmd_flag = iwl4965_rts_tx_cmd_flag,
+ .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag,
.calc_rssi = iwl4965_calc_rssi,
};
@@ -2301,10 +2174,10 @@ static struct iwl_lib_ops iwl4965_lib = {
.load_ucode = iwl4965_load_bsm,
.dump_nic_event_log = iwl_dump_nic_event_log,
.dump_nic_error_log = iwl_dump_nic_error_log,
+ .set_channel_switch = iwl4965_hw_channel_switch,
.apm_ops = {
- .init = iwl4965_apm_init,
- .reset = iwl4965_apm_reset,
- .stop = iwl4965_apm_stop,
+ .init = iwl_apm_init,
+ .stop = iwl_apm_stop,
.config = iwl4965_nic_config,
.set_pwr_src = iwl_set_pwr_src,
},
@@ -2340,6 +2213,7 @@ static struct iwl_ops iwl4965_ops = {
.lib = &iwl4965_lib,
.hcmd = &iwl4965_hcmd,
.utils = &iwl4965_hcmd_utils,
+ .led = &iwlagn_led_ops,
};
struct iwl_cfg iwl4965_agn_cfg = {
@@ -2352,30 +2226,41 @@ struct iwl_cfg iwl4965_agn_cfg = {
.eeprom_ver = EEPROM_4965_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION,
.ops = &iwl4965_ops,
+ .num_of_queues = IWL49_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL49_NUM_AMPDU_QUEUES,
.mod_params = &iwl4965_mod_params,
+ .valid_tx_ant = ANT_AB,
+ .valid_rx_ant = ANT_ABC,
+ .pll_cfg_val = 0,
+ .set_l0s = true,
+ .use_bsm = true,
.use_isr_legacy = true,
.ht_greenfield_support = false,
.broken_powersave = true,
+ .led_compensation = 61,
+ .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
};
/* Module firmware */
MODULE_FIRMWARE(IWL4965_MODULE_FIRMWARE(IWL4965_UCODE_API_MAX));
-module_param_named(antenna, iwl4965_mod_params.antenna, int, 0444);
+module_param_named(antenna, iwl4965_mod_params.antenna, int, S_IRUGO);
MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])");
-module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, 0444);
+module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, S_IRUGO);
MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
module_param_named(
- disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, 0444);
+ disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, S_IRUGO);
MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
-module_param_named(queues_num, iwl4965_mod_params.num_of_queues, int, 0444);
+module_param_named(queues_num, iwl4965_mod_params.num_of_queues, int, S_IRUGO);
MODULE_PARM_DESC(queues_num, "number of hw queues.");
/* 11n */
-module_param_named(11n_disable, iwl4965_mod_params.disable_11n, int, 0444);
+module_param_named(11n_disable, iwl4965_mod_params.disable_11n, int, S_IRUGO);
MODULE_PARM_DESC(11n_disable, "disable 11n functionality");
-module_param_named(amsdu_size_8K, iwl4965_mod_params.amsdu_size_8K, int, 0444);
+module_param_named(amsdu_size_8K, iwl4965_mod_params.amsdu_size_8K,
+ int, S_IRUGO);
MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size");
-module_param_named(fw_restart4965, iwl4965_mod_params.restart_fw, int, 0444);
+module_param_named(fw_restart4965, iwl4965_mod_params.restart_fw, int, S_IRUGO);
MODULE_PARM_DESC(fw_restart4965, "restart firmware in case of error");
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 6e6f516ba40..e2f8615c8c9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -43,6 +43,7 @@
#include "iwl-io.h"
#include "iwl-sta.h"
#include "iwl-helpers.h"
+#include "iwl-agn-led.h"
#include "iwl-5000-hw.h"
#include "iwl-6000-hw.h"
@@ -72,157 +73,18 @@ static const u16 iwl5000_default_queue_to_tx_fifo[] = {
IWL_TX_FIFO_HCCA_2
};
-/* FIXME: same implementation as 4965 */
-static int iwl5000_apm_stop_master(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- /* set stop master bit */
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
-
- iwl_poll_direct_bit(priv, CSR_RESET,
- CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- IWL_DEBUG_INFO(priv, "stop master\n");
-
- return 0;
-}
-
-
-int iwl5000_apm_init(struct iwl_priv *priv)
-{
- int ret = 0;
-
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
-
- /* disable L0s without affecting L1 :don't wait for ICH L0s bug W/A) */
- iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
- CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
-
- /* Set FH wait threshold to maximum (HW error during stress W/A) */
- iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
-
- /* enable HAP INTA to move device L1a -> L0s */
- iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
-
- if (priv->cfg->need_pll_cfg)
- iwl_set_bit(priv, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL);
-
- /* set "initialization complete" bit to move adapter
- * D0U* --> D0A* state */
- iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /* wait for clock stabilization */
- ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (ret < 0) {
- IWL_DEBUG_INFO(priv, "Failed to init the card\n");
- return ret;
- }
-
- /* enable DMA */
- iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
-
- udelay(20);
-
- /* disable L1-Active */
- iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-
- return ret;
-}
-
-/* FIXME: this is identical to 4965 */
-void iwl5000_apm_stop(struct iwl_priv *priv)
-{
- unsigned long flags;
-
- iwl5000_apm_stop_master(priv);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
-
- /* clear "init complete" move adapter D0A* --> D0U state */
- iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-
-int iwl5000_apm_reset(struct iwl_priv *priv)
-{
- int ret = 0;
-
- iwl5000_apm_stop_master(priv);
-
- iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
-
- udelay(10);
-
-
- /* FIXME: put here L1A -L0S w/a */
-
- if (priv->cfg->need_pll_cfg)
- iwl_set_bit(priv, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL);
-
- /* set "initialization complete" bit to move adapter
- * D0U* --> D0A* state */
- iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
-
- /* wait for clock stabilization */
- ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
- if (ret < 0) {
- IWL_DEBUG_INFO(priv, "Failed to init the card\n");
- goto out;
- }
-
- /* enable DMA */
- iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
-
- udelay(20);
-
- /* disable L1-Active */
- iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
- APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
-out:
-
- return ret;
-}
-
-
-/* NIC configuration for 5000 series and up */
+/* NIC configuration for 5000 series */
void iwl5000_nic_config(struct iwl_priv *priv)
{
unsigned long flags;
u16 radio_cfg;
- u16 lctl;
spin_lock_irqsave(&priv->lock, flags);
- lctl = iwl_pcie_link_ctl(priv);
-
- /* HW bug W/A */
- /* L1-ASPM is enabled by BIOS */
- if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN)
- /* L1-APSM enabled: disable L0S */
- iwl_set_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- else
- /* L1-ASPM disabled: enable L0S */
- iwl_clear_bit(priv, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
-
radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
/* write radio config values to register */
- if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) < EEPROM_5000_RF_CFG_TYPE_MAX)
+ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) < EEPROM_RF_CONFIG_TYPE_MAX)
iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
EEPROM_RF_CFG_TYPE_MSK(radio_cfg) |
EEPROM_RF_CFG_STEP_MSK(radio_cfg) |
@@ -302,19 +164,22 @@ u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv)
static void iwl5000_gain_computation(struct iwl_priv *priv,
u32 average_noise[NUM_RX_CHAINS],
u16 min_average_noise_antenna_i,
- u32 min_average_noise)
+ u32 min_average_noise,
+ u8 default_chain)
{
int i;
s32 delta_g;
struct iwl_chain_noise_data *data = &priv->chain_noise_data;
- /* Find Gain Code for the antennas B and C */
- for (i = 1; i < NUM_RX_CHAINS; i++) {
+ /*
+ * Find Gain Code for the chains based on "default chain"
+ */
+ for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) {
if ((data->disconn_array[i])) {
data->delta_gain_code[i] = 0;
continue;
}
- delta_g = (1000 * ((s32)average_noise[0] -
+ delta_g = (1000 * ((s32)average_noise[default_chain] -
(s32)average_noise[i])) / 1500;
/* bound gain by 2 bits value max, 3rd bit is sign */
data->delta_gain_code[i] =
@@ -407,6 +272,10 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = {
.auto_corr_max_cck_mrc = 400,
.nrg_th_cck = 95,
.nrg_th_ofdm = 95,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
};
static struct iwl_sensitivity_ranges iwl5150_sensitivity = {
@@ -429,6 +298,10 @@ static struct iwl_sensitivity_ranges iwl5150_sensitivity = {
.auto_corr_max_cck_mrc = 400,
.nrg_th_cck = 95,
.nrg_th_ofdm = 95,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
};
const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
@@ -493,7 +366,7 @@ static int iwl5000_send_calib_cfg(struct iwl_priv *priv)
static void iwl5000_rx_calib_result(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw;
int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
int index;
@@ -719,16 +592,6 @@ static void iwl5000_tx_queue_set_status(struct iwl_priv *priv,
scd_retry ? "BA" : "AC", txq_id, tx_fifo_id);
}
-static int iwl5000_send_wimax_coex(struct iwl_priv *priv)
-{
- struct iwl_wimax_coex_cmd coex_cmd;
-
- memset(&coex_cmd, 0, sizeof(coex_cmd));
-
- return iwl_send_cmd_pdu(priv, COEX_PRIORITY_TABLE_CMD,
- sizeof(coex_cmd), &coex_cmd);
-}
-
int iwl5000_alive_notify(struct iwl_priv *priv)
{
u32 a;
@@ -746,7 +609,8 @@ int iwl5000_alive_notify(struct iwl_priv *priv)
for (; a < priv->scd_base_addr + IWL50_SCD_TRANSLATE_TBL_OFFSET;
a += 4)
iwl_write_targ_mem(priv, a, 0);
- for (; a < sizeof(u16) * priv->hw_params.max_txq_num; a += 4)
+ for (; a < priv->scd_base_addr +
+ IWL50_SCD_TRANSLATE_TBL_OFFSET_QUEUE(priv->hw_params.max_txq_num); a += 4)
iwl_write_targ_mem(priv, a, 0);
iwl_write_prph(priv, IWL50_SCD_DRAM_BASE_ADDR,
@@ -798,9 +662,13 @@ int iwl5000_alive_notify(struct iwl_priv *priv)
iwl_txq_ctx_activate(priv, i);
iwl5000_tx_queue_set_status(priv, &priv->txq[i], ac, 0);
}
- /* TODO - need to initialize those FIFOs inside the loop above,
- * not only mark them as active */
- iwl_txq_ctx_activate(priv, 4);
+
+ /*
+ * TODO - need to initialize these queues and map them to FIFOs
+ * in the loop above, not only mark them as active. We do this
+ * because we want the first aggregation queue to be queue #10,
+ * but do not use 8 or 9 otherwise yet.
+ */
iwl_txq_ctx_activate(priv, 7);
iwl_txq_ctx_activate(priv, 8);
iwl_txq_ctx_activate(priv, 9);
@@ -808,7 +676,7 @@ int iwl5000_alive_notify(struct iwl_priv *priv)
spin_unlock_irqrestore(&priv->lock, flags);
- iwl5000_send_wimax_coex(priv);
+ iwl_send_wimax_coex(priv);
iwl5000_set_Xtal_calib(priv);
iwl_send_calib_results(priv);
@@ -818,32 +686,22 @@ int iwl5000_alive_notify(struct iwl_priv *priv)
int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
{
- if ((priv->cfg->mod_params->num_of_queues > IWL50_NUM_QUEUES) ||
- (priv->cfg->mod_params->num_of_queues < IWL_MIN_NUM_QUEUES)) {
- IWL_ERR(priv,
- "invalid queues_num, should be between %d and %d\n",
- IWL_MIN_NUM_QUEUES, IWL50_NUM_QUEUES);
- return -EINVAL;
- }
+ if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES &&
+ priv->cfg->mod_params->num_of_queues <= IWL50_NUM_QUEUES)
+ priv->cfg->num_of_queues =
+ priv->cfg->mod_params->num_of_queues;
- priv->hw_params.max_txq_num = priv->cfg->mod_params->num_of_queues;
+ priv->hw_params.max_txq_num = priv->cfg->num_of_queues;
priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
priv->hw_params.scd_bc_tbls_size =
- IWL50_NUM_QUEUES * sizeof(struct iwl5000_scd_bc_tbl);
+ priv->cfg->num_of_queues *
+ sizeof(struct iwl5000_scd_bc_tbl);
priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
priv->hw_params.max_stations = IWL5000_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID;
- switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
- case CSR_HW_REV_TYPE_6x00:
- case CSR_HW_REV_TYPE_6x50:
- priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE;
- priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE;
- break;
- default:
- priv->hw_params.max_data_size = IWL50_RTC_DATA_SIZE;
- priv->hw_params.max_inst_size = IWL50_RTC_INST_SIZE;
- }
+ priv->hw_params.max_data_size = IWL50_RTC_DATA_SIZE;
+ priv->hw_params.max_inst_size = IWL50_RTC_INST_SIZE;
priv->hw_params.max_bsm_size = 0;
priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
@@ -989,11 +847,13 @@ int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id,
u16 ra_tid;
if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) {
+ (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
+ <= txq_id)) {
IWL_WARN(priv,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWL50_FIRST_AMPDU_QUEUE,
- IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES - 1);
+ IWL50_FIRST_AMPDU_QUEUE +
+ priv->cfg->num_of_ampdu_queues - 1);
return -EINVAL;
}
@@ -1047,11 +907,13 @@ int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
u16 ssn_idx, u8 tx_fifo)
{
if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
- (IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) {
+ (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
+ <= txq_id)) {
IWL_ERR(priv,
"queue number out of range: %d, must be %d to %d\n",
txq_id, IWL50_FIRST_AMPDU_QUEUE,
- IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES - 1);
+ IWL50_FIRST_AMPDU_QUEUE +
+ priv->cfg->num_of_ampdu_queues - 1);
return -EINVAL;
}
@@ -1132,8 +994,7 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv,
info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb[0]);
info->status.rates[0].count = tx_resp->failure_frame + 1;
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- info->flags |= iwl_is_tx_success(status) ?
- IEEE80211_TX_STAT_ACK : 0;
+ info->flags |= iwl_tx_status_to_mac80211(status);
iwl_hwrate_to_tx_control(priv, rate_n_flags, info);
/* FIXME: code repetition end */
@@ -1218,7 +1079,7 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv,
static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
@@ -1278,8 +1139,7 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv,
BUG_ON(txq_id != txq->swq_id);
info->status.rates[0].count = tx_resp->failure_frame + 1;
- info->flags |= iwl_is_tx_success(status) ?
- IEEE80211_TX_STAT_ACK : 0;
+ info->flags |= iwl_tx_status_to_mac80211(status);
iwl_hwrate_to_tx_control(priv,
le32_to_cpu(tx_resp->rate_n_flags),
info);
@@ -1389,6 +1249,22 @@ int iwl5000_send_tx_power(struct iwl_priv *priv)
/* half dBm need to multiply */
tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt);
+
+ if (priv->tx_power_lmt_in_half_dbm &&
+ priv->tx_power_lmt_in_half_dbm < tx_power_cmd.global_lmt) {
+ /*
+ * For the newer devices which using enhanced/extend tx power
+ * table in EEPROM, the format is in half dBm. driver need to
+ * convert to dBm format before report to mac80211.
+ * By doing so, there is a possibility of 1/2 dBm resolution
+ * lost. driver will perform "round-up" operation before
+ * reporting, but it will cause 1/2 dBm tx power over the
+ * regulatory limit. Perform the checking here, if the
+ * "tx_power_user_lmt" is higher than EEPROM value (in
+ * half-dBm format), lower the tx power based on EEPROM
+ */
+ tx_power_cmd.global_lmt = priv->tx_power_lmt_in_half_dbm;
+ }
tx_power_cmd.flags = IWL50_TX_POWER_NO_CLOSED;
tx_power_cmd.srv_chan_lmt = IWL50_TX_POWER_AUTO;
@@ -1459,6 +1335,24 @@ int iwl5000_calc_rssi(struct iwl_priv *priv,
return max_rssi - agc - IWL49_RSSI_OFFSET;
}
+static int iwl5000_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant)
+{
+ struct iwl_tx_ant_config_cmd tx_ant_cmd = {
+ .valid = cpu_to_le32(valid_tx_ant),
+ };
+
+ if (IWL_UCODE_API(priv->ucode_ver) > 1) {
+ IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant);
+ return iwl_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD,
+ sizeof(struct iwl_tx_ant_config_cmd),
+ &tx_ant_cmd);
+ } else {
+ IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n");
+ return -EOPNOTSUPP;
+ }
+}
+
+
#define IWL5000_UCODE_GET(item) \
static u32 iwl5000_ucode_get_##item(const struct iwl_ucode_header *ucode,\
u32 api_ver) \
@@ -1497,10 +1391,43 @@ IWL5000_UCODE_GET(init_size);
IWL5000_UCODE_GET(init_data_size);
IWL5000_UCODE_GET(boot_size);
+static int iwl5000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
+{
+ struct iwl5000_channel_switch_cmd cmd;
+ const struct iwl_channel_info *ch_info;
+ struct iwl_host_cmd hcmd = {
+ .id = REPLY_CHANNEL_SWITCH,
+ .len = sizeof(cmd),
+ .flags = CMD_SIZE_HUGE,
+ .data = &cmd,
+ };
+
+ IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
+ priv->active_rxon.channel, channel);
+ cmd.band = priv->band == IEEE80211_BAND_2GHZ;
+ cmd.channel = cpu_to_le16(channel);
+ cmd.rxon_flags = priv->staging_rxon.flags;
+ cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
+ cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
+ ch_info = iwl_get_channel_info(priv, priv->band, channel);
+ if (ch_info)
+ cmd.expect_beacon = is_channel_radar(ch_info);
+ else {
+ IWL_ERR(priv, "invalid channel switch from %u to %u\n",
+ priv->active_rxon.channel, channel);
+ return -EFAULT;
+ }
+ priv->switch_rxon.channel = cpu_to_le16(channel);
+ priv->switch_rxon.switch_in_progress = true;
+
+ return iwl_send_cmd_sync(priv, &hcmd);
+}
+
struct iwl_hcmd_ops iwl5000_hcmd = {
.rxon_assoc = iwl5000_send_rxon_assoc,
.commit_rxon = iwl_commit_rxon,
.set_rxon_chain = iwl_set_rxon_chain,
+ .set_tx_ant = iwl5000_send_tx_ant_config,
};
struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = {
@@ -1543,10 +1470,10 @@ struct iwl_lib_ops iwl5000_lib = {
.alive_notify = iwl5000_alive_notify,
.send_tx_power = iwl5000_send_tx_power,
.update_chain_flags = iwl_update_chain_flags,
+ .set_channel_switch = iwl5000_hw_channel_switch,
.apm_ops = {
- .init = iwl5000_apm_init,
- .reset = iwl5000_apm_reset,
- .stop = iwl5000_apm_stop,
+ .init = iwl_apm_init,
+ .stop = iwl_apm_stop,
.config = iwl5000_nic_config,
.set_pwr_src = iwl_set_pwr_src,
},
@@ -1595,10 +1522,10 @@ static struct iwl_lib_ops iwl5150_lib = {
.alive_notify = iwl5000_alive_notify,
.send_tx_power = iwl5000_send_tx_power,
.update_chain_flags = iwl_update_chain_flags,
+ .set_channel_switch = iwl5000_hw_channel_switch,
.apm_ops = {
- .init = iwl5000_apm_init,
- .reset = iwl5000_apm_reset,
- .stop = iwl5000_apm_stop,
+ .init = iwl_apm_init,
+ .stop = iwl_apm_stop,
.config = iwl5000_nic_config,
.set_pwr_src = iwl_set_pwr_src,
},
@@ -1627,11 +1554,12 @@ static struct iwl_lib_ops iwl5150_lib = {
},
};
-struct iwl_ops iwl5000_ops = {
+static struct iwl_ops iwl5000_ops = {
.ucode = &iwl5000_ucode,
.lib = &iwl5000_lib,
.hcmd = &iwl5000_hcmd,
.utils = &iwl5000_hcmd_utils,
+ .led = &iwlagn_led_ops,
};
static struct iwl_ops iwl5150_ops = {
@@ -1639,11 +1567,10 @@ static struct iwl_ops iwl5150_ops = {
.lib = &iwl5150_lib,
.hcmd = &iwl5000_hcmd,
.utils = &iwl5000_hcmd_utils,
+ .led = &iwlagn_led_ops,
};
struct iwl_mod_params iwl50_mod_params = {
- .num_of_queues = IWL50_NUM_QUEUES,
- .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.amsdu_size_8K = 1,
.restart_fw = 1,
/* the rest are 0 by default */
@@ -1660,28 +1587,41 @@ struct iwl_cfg iwl5300_agn_cfg = {
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_5000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_ABC,
.valid_rx_ant = ANT_ABC,
- .need_pll_cfg = true,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
.ht_greenfield_support = true,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
};
-struct iwl_cfg iwl5100_bg_cfg = {
- .name = "5100BG",
+struct iwl_cfg iwl5100_bgn_cfg = {
+ .name = "5100BGN",
.fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN,
- .sku = IWL_SKU_G,
+ .sku = IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_5000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_B,
.valid_rx_ant = ANT_AB,
- .need_pll_cfg = true,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
.ht_greenfield_support = true,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
};
struct iwl_cfg iwl5100_abg_cfg = {
@@ -1694,11 +1634,16 @@ struct iwl_cfg iwl5100_abg_cfg = {
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_5000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_B,
.valid_rx_ant = ANT_AB,
- .need_pll_cfg = true,
- .ht_greenfield_support = true,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
};
struct iwl_cfg iwl5100_agn_cfg = {
@@ -1711,11 +1656,18 @@ struct iwl_cfg iwl5100_agn_cfg = {
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_5000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_B,
.valid_rx_ant = ANT_AB,
- .need_pll_cfg = true,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
.ht_greenfield_support = true,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
};
struct iwl_cfg iwl5350_agn_cfg = {
@@ -1728,11 +1680,18 @@ struct iwl_cfg iwl5350_agn_cfg = {
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_5050_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_ABC,
.valid_rx_ant = ANT_ABC,
- .need_pll_cfg = true,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
.ht_greenfield_support = true,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
};
struct iwl_cfg iwl5150_agn_cfg = {
@@ -1745,24 +1704,54 @@ struct iwl_cfg iwl5150_agn_cfg = {
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_5050_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_A,
.valid_rx_ant = ANT_AB,
- .need_pll_cfg = true,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
.ht_greenfield_support = true,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
+};
+
+struct iwl_cfg iwl5150_abg_cfg = {
+ .name = "5150ABG",
+ .fw_name_pre = IWL5150_FW_PRE,
+ .ucode_api_max = IWL5150_UCODE_API_MAX,
+ .ucode_api_min = IWL5150_UCODE_API_MIN,
+ .sku = IWL_SKU_A|IWL_SKU_G,
+ .ops = &iwl5150_ops,
+ .eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
+ .eeprom_ver = EEPROM_5050_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
+ .mod_params = &iwl50_mod_params,
+ .valid_tx_ant = ANT_A,
+ .valid_rx_ant = ANT_AB,
+ .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
+ .set_l0s = true,
+ .use_bsm = false,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
};
MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX));
-module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, 0444);
+module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, S_IRUGO);
MODULE_PARM_DESC(swcrypto50,
"using software crypto engine (default 0 [hardware])\n");
-module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, 0444);
+module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, S_IRUGO);
MODULE_PARM_DESC(queues_num50, "number of hw queues in 50xx series");
-module_param_named(11n_disable50, iwl50_mod_params.disable_11n, int, 0444);
+module_param_named(11n_disable50, iwl50_mod_params.disable_11n, int, S_IRUGO);
MODULE_PARM_DESC(11n_disable50, "disable 50XX 11n functionality");
-module_param_named(amsdu_size_8K50, iwl50_mod_params.amsdu_size_8K, int, 0444);
+module_param_named(amsdu_size_8K50, iwl50_mod_params.amsdu_size_8K,
+ int, S_IRUGO);
MODULE_PARM_DESC(amsdu_size_8K50, "enable 8K amsdu size in 50XX series");
-module_param_named(fw_restart50, iwl50_mod_params.restart_fw, int, 0444);
+module_param_named(fw_restart50, iwl50_mod_params.restart_fw, int, S_IRUGO);
MODULE_PARM_DESC(fw_restart50, "restart firmware in case of error");
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index 1473452ba22..74e57104927 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -44,14 +44,16 @@
#include "iwl-sta.h"
#include "iwl-helpers.h"
#include "iwl-5000-hw.h"
+#include "iwl-6000-hw.h"
+#include "iwl-agn-led.h"
/* Highest firmware API version supported */
#define IWL6000_UCODE_API_MAX 4
#define IWL6050_UCODE_API_MAX 4
/* Lowest firmware API version supported */
-#define IWL6000_UCODE_API_MIN 1
-#define IWL6050_UCODE_API_MIN 1
+#define IWL6000_UCODE_API_MIN 4
+#define IWL6050_UCODE_API_MIN 4
#define IWL6000_FW_PRE "iwlwifi-6000-"
#define _IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE #api ".ucode"
@@ -71,14 +73,24 @@ static void iwl6000_set_ct_threshold(struct iwl_priv *priv)
/* NIC configuration for 6000 series */
static void iwl6000_nic_config(struct iwl_priv *priv)
{
- iwl5000_nic_config(priv);
+ u16 radio_cfg;
+
+ radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
+
+ /* write radio config values to register */
+ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX)
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+ EEPROM_RF_CFG_TYPE_MSK(radio_cfg) |
+ EEPROM_RF_CFG_STEP_MSK(radio_cfg) |
+ EEPROM_RF_CFG_DASH_MSK(radio_cfg));
+
+ /* set CSR_HW_CONFIG_REG for uCode use */
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+ CSR_HW_IF_CONFIG_REG_BIT_MAC_SI);
/* no locking required for register write */
- if (priv->cfg->pa_type == IWL_PA_HYBRID) {
- /* 2x2 hybrid phy type */
- iwl_write32(priv, CSR_GP_DRIVER_REG,
- CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB);
- } else if (priv->cfg->pa_type == IWL_PA_INTERNAL) {
+ if (priv->cfg->pa_type == IWL_PA_INTERNAL) {
/* 2x2 IPA phy type */
iwl_write32(priv, CSR_GP_DRIVER_REG,
CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA);
@@ -86,8 +98,109 @@ static void iwl6000_nic_config(struct iwl_priv *priv)
/* else do nothing, uCode configured */
}
+static struct iwl_sensitivity_ranges iwl6000_sensitivity = {
+ .min_nrg_cck = 97,
+ .max_nrg_cck = 0, /* not used, set to 0 */
+ .auto_corr_min_ofdm = 80,
+ .auto_corr_min_ofdm_mrc = 128,
+ .auto_corr_min_ofdm_x1 = 105,
+ .auto_corr_min_ofdm_mrc_x1 = 192,
+
+ .auto_corr_max_ofdm = 145,
+ .auto_corr_max_ofdm_mrc = 232,
+ .auto_corr_max_ofdm_x1 = 145,
+ .auto_corr_max_ofdm_mrc_x1 = 232,
+
+ .auto_corr_min_cck = 125,
+ .auto_corr_max_cck = 175,
+ .auto_corr_min_cck_mrc = 160,
+ .auto_corr_max_cck_mrc = 310,
+ .nrg_th_cck = 97,
+ .nrg_th_ofdm = 100,
+
+ .barker_corr_th_min = 190,
+ .barker_corr_th_min_mrc = 390,
+ .nrg_th_cca = 62,
+};
+
+static int iwl6000_hw_set_hw_params(struct iwl_priv *priv)
+{
+ if (priv->cfg->mod_params->num_of_queues >= IWL_MIN_NUM_QUEUES &&
+ priv->cfg->mod_params->num_of_queues <= IWL50_NUM_QUEUES)
+ priv->cfg->num_of_queues =
+ priv->cfg->mod_params->num_of_queues;
+
+ priv->hw_params.max_txq_num = priv->cfg->num_of_queues;
+ priv->hw_params.dma_chnl_num = FH50_TCSR_CHNL_NUM;
+ priv->hw_params.scd_bc_tbls_size =
+ priv->cfg->num_of_queues *
+ sizeof(struct iwl5000_scd_bc_tbl);
+ priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
+ priv->hw_params.max_stations = IWL5000_STATION_COUNT;
+ priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID;
+
+ priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE;
+ priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE;
+
+ priv->hw_params.max_bsm_size = 0;
+ priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) |
+ BIT(IEEE80211_BAND_5GHZ);
+ priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR;
+
+ priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant);
+ priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant);
+ priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
+ priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
+
+ if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
+ priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
+
+ /* Set initial sensitivity parameters */
+ /* Set initial calibration set */
+ priv->hw_params.sens = &iwl6000_sensitivity;
+ priv->hw_params.calib_init_cfg =
+ BIT(IWL_CALIB_XTAL) |
+ BIT(IWL_CALIB_LO) |
+ BIT(IWL_CALIB_TX_IQ) |
+ BIT(IWL_CALIB_BASE_BAND);
+ return 0;
+}
+
+static int iwl6000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
+{
+ struct iwl6000_channel_switch_cmd cmd;
+ const struct iwl_channel_info *ch_info;
+ struct iwl_host_cmd hcmd = {
+ .id = REPLY_CHANNEL_SWITCH,
+ .len = sizeof(cmd),
+ .flags = CMD_SIZE_HUGE,
+ .data = &cmd,
+ };
+
+ IWL_DEBUG_11H(priv, "channel switch from %d to %d\n",
+ priv->active_rxon.channel, channel);
+
+ cmd.band = priv->band == IEEE80211_BAND_2GHZ;
+ cmd.channel = cpu_to_le16(channel);
+ cmd.rxon_flags = priv->staging_rxon.flags;
+ cmd.rxon_filter_flags = priv->staging_rxon.filter_flags;
+ cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time);
+ ch_info = iwl_get_channel_info(priv, priv->band, channel);
+ if (ch_info)
+ cmd.expect_beacon = is_channel_radar(ch_info);
+ else {
+ IWL_ERR(priv, "invalid channel switch from %u to %u\n",
+ priv->active_rxon.channel, channel);
+ return -EFAULT;
+ }
+ priv->switch_rxon.channel = cpu_to_le16(channel);
+ priv->switch_rxon.switch_in_progress = true;
+
+ return iwl_send_cmd_sync(priv, &hcmd);
+}
+
static struct iwl_lib_ops iwl6000_lib = {
- .set_hw_params = iwl5000_hw_set_hw_params,
+ .set_hw_params = iwl6000_hw_set_hw_params,
.txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl,
.txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl,
.txq_set_sched = iwl5000_txq_set_sched,
@@ -106,10 +219,10 @@ static struct iwl_lib_ops iwl6000_lib = {
.alive_notify = iwl5000_alive_notify,
.send_tx_power = iwl5000_send_tx_power,
.update_chain_flags = iwl_update_chain_flags,
+ .set_channel_switch = iwl6000_hw_channel_switch,
.apm_ops = {
- .init = iwl5000_apm_init,
- .reset = iwl5000_apm_reset,
- .stop = iwl5000_apm_stop,
+ .init = iwl_apm_init,
+ .stop = iwl_apm_stop,
.config = iwl6000_nic_config,
.set_pwr_src = iwl_set_pwr_src,
},
@@ -139,25 +252,33 @@ static struct iwl_lib_ops iwl6000_lib = {
},
};
-static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = {
+static struct iwl_ops iwl6000_ops = {
+ .ucode = &iwl5000_ucode,
+ .lib = &iwl6000_lib,
+ .hcmd = &iwl5000_hcmd,
+ .utils = &iwl5000_hcmd_utils,
+ .led = &iwlagn_led_ops,
+};
+
+static struct iwl_hcmd_utils_ops iwl6050_hcmd_utils = {
.get_hcmd_size = iwl5000_get_hcmd_size,
.build_addsta_hcmd = iwl5000_build_addsta_hcmd,
.rts_tx_cmd_flag = iwl5000_rts_tx_cmd_flag,
.calc_rssi = iwl5000_calc_rssi,
};
-static struct iwl_ops iwl6000_ops = {
+static struct iwl_ops iwl6050_ops = {
.ucode = &iwl5000_ucode,
.lib = &iwl6000_lib,
.hcmd = &iwl5000_hcmd,
- .utils = &iwl6000_hcmd_utils,
+ .utils = &iwl6050_hcmd_utils,
+ .led = &iwlagn_led_ops,
};
-
/*
- * "h": Hybrid configuration, use both internal and external Power Amplifier
+ * "i": Internal configuration, use internal Power Amplifier
*/
-struct iwl_cfg iwl6000h_2agn_cfg = {
+struct iwl_cfg iwl6000i_2agn_cfg = {
.name = "6000 Series 2x2 AGN",
.fw_name_pre = IWL6000_FW_PRE,
.ucode_api_max = IWL6000_UCODE_API_MAX,
@@ -165,41 +286,85 @@ struct iwl_cfg iwl6000h_2agn_cfg = {
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl6000_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
- .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_ver = EEPROM_6000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
- .valid_tx_ant = ANT_AB,
- .valid_rx_ant = ANT_AB,
- .need_pll_cfg = false,
- .pa_type = IWL_PA_HYBRID,
+ .valid_tx_ant = ANT_BC,
+ .valid_rx_ant = ANT_BC,
+ .pll_cfg_val = 0,
+ .set_l0s = true,
+ .use_bsm = false,
+ .pa_type = IWL_PA_INTERNAL,
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
.ht_greenfield_support = true,
+ .led_compensation = 51,
.use_rts_for_ht = true, /* use rts/cts protection */
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .supports_idle = true,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
};
-/*
- * "i": Internal configuration, use internal Power Amplifier
- */
-struct iwl_cfg iwl6000i_2agn_cfg = {
- .name = "6000 Series 2x2 AGN",
+struct iwl_cfg iwl6000i_2abg_cfg = {
+ .name = "6000 Series 2x2 ABG",
.fw_name_pre = IWL6000_FW_PRE,
.ucode_api_max = IWL6000_UCODE_API_MAX,
.ucode_api_min = IWL6000_UCODE_API_MIN,
- .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
+ .sku = IWL_SKU_A|IWL_SKU_G,
.ops = &iwl6000_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
- .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_ver = EEPROM_6000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_BC,
.valid_rx_ant = ANT_BC,
- .need_pll_cfg = false,
+ .pll_cfg_val = 0,
+ .set_l0s = true,
+ .use_bsm = false,
.pa_type = IWL_PA_INTERNAL,
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
.ht_greenfield_support = true,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .supports_idle = true,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+};
+
+struct iwl_cfg iwl6000i_2bg_cfg = {
+ .name = "6000 Series 2x2 BG",
+ .fw_name_pre = IWL6000_FW_PRE,
+ .ucode_api_max = IWL6000_UCODE_API_MAX,
+ .ucode_api_min = IWL6000_UCODE_API_MIN,
+ .sku = IWL_SKU_G,
+ .ops = &iwl6000_ops,
+ .eeprom_size = OTP_LOW_IMAGE_SIZE,
+ .eeprom_ver = EEPROM_6000_EEPROM_VERSION,
+ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
+ .mod_params = &iwl50_mod_params,
+ .valid_tx_ant = ANT_BC,
+ .valid_rx_ant = ANT_BC,
+ .pll_cfg_val = 0,
+ .set_l0s = true,
+ .use_bsm = false,
+ .pa_type = IWL_PA_INTERNAL,
+ .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
+ .shadow_ram_support = true,
+ .ht_greenfield_support = true,
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .supports_idle = true,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
};
struct iwl_cfg iwl6050_2agn_cfg = {
@@ -208,61 +373,89 @@ struct iwl_cfg iwl6050_2agn_cfg = {
.ucode_api_max = IWL6050_UCODE_API_MAX,
.ucode_api_min = IWL6050_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
- .ops = &iwl6000_ops,
+ .ops = &iwl6050_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
- .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_ver = EEPROM_6050_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_AB,
.valid_rx_ant = ANT_AB,
- .need_pll_cfg = false,
+ .pll_cfg_val = 0,
+ .set_l0s = true,
+ .use_bsm = false,
.pa_type = IWL_PA_SYSTEM,
- .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
+ .max_ll_items = OTP_MAX_LL_ITEMS_6x50,
.shadow_ram_support = true,
.ht_greenfield_support = true,
+ .led_compensation = 51,
.use_rts_for_ht = true, /* use rts/cts protection */
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .supports_idle = true,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DYNAMIC,
};
-struct iwl_cfg iwl6000_3agn_cfg = {
- .name = "6000 Series 3x3 AGN",
- .fw_name_pre = IWL6000_FW_PRE,
- .ucode_api_max = IWL6000_UCODE_API_MAX,
- .ucode_api_min = IWL6000_UCODE_API_MIN,
- .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
- .ops = &iwl6000_ops,
+struct iwl_cfg iwl6050_2abg_cfg = {
+ .name = "6050 Series 2x2 ABG",
+ .fw_name_pre = IWL6050_FW_PRE,
+ .ucode_api_max = IWL6050_UCODE_API_MAX,
+ .ucode_api_min = IWL6050_UCODE_API_MIN,
+ .sku = IWL_SKU_A|IWL_SKU_G,
+ .ops = &iwl6050_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
- .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_ver = EEPROM_6050_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
- .valid_tx_ant = ANT_ABC,
- .valid_rx_ant = ANT_ABC,
- .need_pll_cfg = false,
+ .valid_tx_ant = ANT_AB,
+ .valid_rx_ant = ANT_AB,
+ .pll_cfg_val = 0,
+ .set_l0s = true,
+ .use_bsm = false,
.pa_type = IWL_PA_SYSTEM,
- .max_ll_items = OTP_MAX_LL_ITEMS_6x00,
+ .max_ll_items = OTP_MAX_LL_ITEMS_6x50,
.shadow_ram_support = true,
.ht_greenfield_support = true,
- .use_rts_for_ht = true, /* use rts/cts protection */
+ .led_compensation = 51,
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .supports_idle = true,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
};
-struct iwl_cfg iwl6050_3agn_cfg = {
- .name = "6050 Series 3x3 AGN",
- .fw_name_pre = IWL6050_FW_PRE,
- .ucode_api_max = IWL6050_UCODE_API_MAX,
- .ucode_api_min = IWL6050_UCODE_API_MIN,
+struct iwl_cfg iwl6000_3agn_cfg = {
+ .name = "6000 Series 3x3 AGN",
+ .fw_name_pre = IWL6000_FW_PRE,
+ .ucode_api_max = IWL6000_UCODE_API_MAX,
+ .ucode_api_min = IWL6000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl6000_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
- .eeprom_ver = EEPROM_5000_EEPROM_VERSION,
+ .eeprom_ver = EEPROM_6000_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION,
+ .num_of_queues = IWL50_NUM_QUEUES,
+ .num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
.mod_params = &iwl50_mod_params,
.valid_tx_ant = ANT_ABC,
.valid_rx_ant = ANT_ABC,
- .need_pll_cfg = false,
+ .pll_cfg_val = 0,
+ .set_l0s = true,
+ .use_bsm = false,
.pa_type = IWL_PA_SYSTEM,
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
.shadow_ram_support = true,
.ht_greenfield_support = true,
+ .led_compensation = 51,
.use_rts_for_ht = true, /* use rts/cts protection */
+ .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
+ .supports_idle = true,
+ .adv_thermal_throttle = true,
+ .support_ct_kill_exit = true,
+ .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
};
MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-led.c b/drivers/net/wireless/iwlwifi/iwl-agn-led.c
new file mode 100644
index 00000000000..3bccba20f6d
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-led.c
@@ -0,0 +1,85 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <asm/unaligned.h>
+
+#include "iwl-commands.h"
+#include "iwl-dev.h"
+#include "iwl-core.h"
+#include "iwl-io.h"
+#include "iwl-agn-led.h"
+
+/* Send led command */
+static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd)
+{
+ struct iwl_host_cmd cmd = {
+ .id = REPLY_LEDS_CMD,
+ .len = sizeof(struct iwl_led_cmd),
+ .data = led_cmd,
+ .flags = CMD_ASYNC,
+ .callback = NULL,
+ };
+ u32 reg;
+
+ reg = iwl_read32(priv, CSR_LED_REG);
+ if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
+ iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK);
+
+ return iwl_send_cmd(priv, &cmd);
+}
+
+/* Set led register off */
+static int iwl_led_on_reg(struct iwl_priv *priv)
+{
+ IWL_DEBUG_LED(priv, "led on\n");
+ iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON);
+ return 0;
+}
+
+/* Set led register off */
+static int iwl_led_off_reg(struct iwl_priv *priv)
+{
+ IWL_DEBUG_LED(priv, "LED Reg off\n");
+ iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF);
+ return 0;
+}
+
+const struct iwl_led_ops iwlagn_led_ops = {
+ .cmd = iwl_send_led_cmd,
+ .on = iwl_led_on_reg,
+ .off = iwl_led_off_reg,
+};
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-led.h b/drivers/net/wireless/iwlwifi/iwl-agn-led.h
new file mode 100644
index 00000000000..ab55f92a161
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-led.h
@@ -0,0 +1,32 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __iwl_agn_led_h__
+#define __iwl_agn_led_h__
+
+extern const struct iwl_led_ops iwlagn_led_ops;
+
+#endif /* __iwl_agn_led_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 81726ee3285..fe511cbf012 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -75,104 +75,6 @@ static const u8 ant_toggle_lookup[] = {
/*ANT_ABC -> */ ANT_ABC,
};
-/**
- * struct iwl_rate_scale_data -- tx success history for one rate
- */
-struct iwl_rate_scale_data {
- u64 data; /* bitmap of successful frames */
- s32 success_counter; /* number of frames successful */
- s32 success_ratio; /* per-cent * 128 */
- s32 counter; /* number of frames attempted */
- s32 average_tpt; /* success ratio * expected throughput */
- unsigned long stamp;
-};
-
-/**
- * struct iwl_scale_tbl_info -- tx params and success history for all rates
- *
- * There are two of these in struct iwl_lq_sta,
- * one for "active", and one for "search".
- */
-struct iwl_scale_tbl_info {
- enum iwl_table_type lq_type;
- u8 ant_type;
- u8 is_SGI; /* 1 = short guard interval */
- u8 is_ht40; /* 1 = 40 MHz channel width */
- u8 is_dup; /* 1 = duplicated data streams */
- u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
- u8 max_search; /* maximun number of tables we can search */
- s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
- u32 current_rate; /* rate_n_flags, uCode API format */
- struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
-};
-
-struct iwl_traffic_load {
- unsigned long time_stamp; /* age of the oldest statistics */
- u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time
- * slice */
- u32 total; /* total num of packets during the
- * last TID_MAX_TIME_DIFF */
- u8 queue_count; /* number of queues that has
- * been used since the last cleanup */
- u8 head; /* start of the circular buffer */
-};
-
-/**
- * struct iwl_lq_sta -- driver's rate scaling private structure
- *
- * Pointer to this gets passed back and forth between driver and mac80211.
- */
-struct iwl_lq_sta {
- u8 active_tbl; /* index of active table, range 0-1 */
- u8 enable_counter; /* indicates HT mode */
- u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */
- u8 search_better_tbl; /* 1: currently trying alternate mode */
- s32 last_tpt;
-
- /* The following determine when to search for a new mode */
- u32 table_count_limit;
- u32 max_failure_limit; /* # failed frames before new search */
- u32 max_success_limit; /* # successful frames before new search */
- u32 table_count;
- u32 total_failed; /* total failed frames, any/all rates */
- u32 total_success; /* total successful frames, any/all rates */
- u64 flush_timer; /* time staying in mode before new search */
-
- u8 action_counter; /* # mode-switch actions tried */
- u8 is_green;
- u8 is_dup;
- enum ieee80211_band band;
- u8 ibss_sta_added;
-
- /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
- u32 supp_rates;
- u16 active_legacy_rate;
- u16 active_siso_rate;
- u16 active_mimo2_rate;
- u16 active_mimo3_rate;
- u16 active_rate_basic;
- s8 max_rate_idx; /* Max rate set by user */
- u8 missed_rate_counter;
-
- struct iwl_link_quality_cmd lq;
- struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
- struct iwl_traffic_load load[TID_MAX_LOAD_COUNT];
- u8 tx_agg_tid_en;
-#ifdef CONFIG_MAC80211_DEBUGFS
- struct dentry *rs_sta_dbgfs_scale_table_file;
- struct dentry *rs_sta_dbgfs_stats_table_file;
- struct dentry *rs_sta_dbgfs_rate_scale_data_file;
- struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
- u32 dbg_fixed_rate;
-#endif
- struct iwl_priv *drv;
-
- /* used to be in sta_info */
- int last_txrate_idx;
- /* last tx rate_n_flags */
- u32 last_rate_n_flags;
-};
-
static void rs_rate_scale_perform(struct iwl_priv *priv,
struct sk_buff *skb,
struct ieee80211_sta *sta,
@@ -190,84 +92,78 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
{}
#endif
-/*
- * Expected throughput metrics for following rates:
- * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
- * "G" is the only table that supports CCK (the first 4 rates).
+/**
+ * The following tables contain the expected throughput metrics for all rates
+ *
+ * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
+ *
+ * where invalid entries are zeros.
+ *
+ * CCK rates are only valid in legacy table and will only be used in G
+ * (2.4 GHz) band.
*/
-static s32 expected_tpt_A[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186
-};
-
-static s32 expected_tpt_G[IWL_RATE_COUNT] = {
- 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 186
-};
-
-static s32 expected_tpt_siso20MHz[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 42, 42, 76, 102, 124, 159, 183, 193, 202
-};
-
-static s32 expected_tpt_siso20MHzSGI[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 46, 46, 82, 110, 132, 168, 192, 202, 211
-};
-
-static s32 expected_tpt_mimo2_20MHz[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 74, 74, 123, 155, 179, 214, 236, 244, 251
-};
-
-static s32 expected_tpt_mimo2_20MHzSGI[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 81, 81, 131, 164, 188, 222, 243, 251, 257
+static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
+ 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
};
-static s32 expected_tpt_siso40MHz[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 77, 77, 127, 160, 184, 220, 242, 250, 257
+static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 42, 0, 76, 102, 124, 158, 183, 193, 202}, /* Norm */
+ {0, 0, 0, 0, 46, 0, 82, 110, 132, 167, 192, 202, 210}, /* SGI */
+ {0, 0, 0, 0, 48, 0, 93, 135, 176, 251, 319, 351, 381}, /* AGG */
+ {0, 0, 0, 0, 53, 0, 102, 149, 193, 275, 348, 381, 413}, /* AGG+SGI */
};
-static s32 expected_tpt_siso40MHzSGI[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 83, 83, 135, 169, 193, 229, 250, 257, 264
+static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
+ {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
+ {0, 0, 0, 0, 96, 0, 182, 259, 328, 451, 553, 598, 640}, /* AGG */
+ {0, 0, 0, 0, 106, 0, 199, 282, 357, 487, 593, 640, 683}, /* AGG+SGI */
};
-static s32 expected_tpt_mimo2_40MHz[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 123, 123, 182, 214, 235, 264, 279, 285, 289
+static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250}, /* Norm */
+ {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256}, /* SGI */
+ {0, 0, 0, 0, 92, 0, 175, 250, 317, 436, 534, 578, 619}, /* AGG */
+ {0, 0, 0, 0, 102, 0, 192, 273, 344, 470, 573, 619, 660}, /* AGG+SGI*/
};
-static s32 expected_tpt_mimo2_40MHzSGI[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 131, 131, 191, 222, 242, 270, 284, 289, 293
+static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
+ {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
+ {0, 0, 0, 0, 180, 0, 327, 446, 545, 708, 828, 878, 922}, /* AGG */
+ {0, 0, 0, 0, 197, 0, 355, 481, 584, 752, 872, 922, 966}, /* AGG+SGI */
};
-/* Expected throughput metric MIMO3 */
-static s32 expected_tpt_mimo3_20MHz[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 99, 99, 153, 186, 208, 239, 256, 263, 268
+static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
+ {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
+ {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
+ {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
};
-static s32 expected_tpt_mimo3_20MHzSGI[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 106, 106, 162, 194, 215, 246, 262, 268, 273
-};
-
-static s32 expected_tpt_mimo3_40MHz[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 152, 152, 211, 239, 255, 279, 290, 294, 297
-};
-
-static s32 expected_tpt_mimo3_40MHzSGI[IWL_RATE_COUNT] = {
- 0, 0, 0, 0, 160, 160, 219, 245, 261, 284, 294, 297, 300
+static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
+ {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */
+ {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */
+ {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */
+ {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */
};
/* mbps, mcs */
const static struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
- {"1", ""},
- {"2", ""},
- {"5.5", ""},
- {"11", ""},
- {"6", "BPSK 1/2"},
- {"9", "BPSK 1/2"},
- {"12", "QPSK 1/2"},
- {"18", "QPSK 3/4"},
- {"24", "16QAM 1/2"},
- {"36", "16QAM 3/4"},
- {"48", "64QAM 2/3"},
- {"54", "64QAM 3/4"},
- {"60", "64QAM 5/6"}
+ { "1", "BPSK DSSS"},
+ { "2", "QPSK DSSS"},
+ {"5.5", "BPSK CCK"},
+ { "11", "QPSK CCK"},
+ { "6", "BPSK 1/2"},
+ { "9", "BPSK 1/2"},
+ { "12", "QPSK 1/2"},
+ { "18", "QPSK 3/4"},
+ { "24", "16QAM 1/2"},
+ { "36", "16QAM 3/4"},
+ { "48", "64QAM 2/3"},
+ { "54", "64QAM 3/4"},
+ { "60", "64QAM 5/6"},
};
#define MCS_INDEX_PER_STREAM (8)
@@ -405,7 +301,7 @@ static void rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
if (rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) {
IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n",
sta->addr, tid);
- ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid);
+ ieee80211_start_tx_ba_session(sta, tid);
}
}
@@ -444,7 +340,7 @@ static inline int get_num_of_ant_from_rate(u32 rate_n_flags)
* packets.
*/
static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
- int scale_index, s32 tpt, int retries,
+ int scale_index, s32 tpt, int attempts,
int successes)
{
struct iwl_rate_scale_data *window = NULL;
@@ -454,7 +350,7 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
return -EINVAL;
- /* Select data for current tx bit rate */
+ /* Select window for current tx bit rate */
window = &(windows[scale_index]);
/*
@@ -465,7 +361,7 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
* subtract "1" from the success counter (this is the main reason
* we keep these bitmaps!).
*/
- while (retries > 0) {
+ while (attempts > 0) {
if (window->counter >= IWL_RATE_MAX_WINDOW) {
/* remove earliest */
@@ -480,17 +376,17 @@ static int rs_collect_tx_data(struct iwl_rate_scale_data *windows,
/* Increment frames-attempted counter */
window->counter++;
- /* Shift bitmap by one frame (throw away oldest history),
- * OR in "1", and increment "success" if this
- * frame was successful. */
+ /* Shift bitmap by one frame to throw away oldest history */
window->data <<= 1;
+
+ /* Mark the most recent #successes attempts as successful */
if (successes > 0) {
window->success_counter++;
window->data |= 0x1;
successes--;
}
- retries--;
+ attempts--;
}
/* Calculate current success ratio, avoid divide-by-0! */
@@ -671,7 +567,7 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
* there are no non-GF stations present in the BSS.
*/
static inline u8 rs_use_green(struct ieee80211_sta *sta,
- struct iwl_ht_info *ht_conf)
+ struct iwl_ht_config *ht_conf)
{
return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
!(ht_conf->non_GF_STA_present);
@@ -821,27 +717,45 @@ out:
}
/*
+ * Simple function to compare two rate scale table types
+ */
+static bool table_type_matches(struct iwl_scale_tbl_info *a,
+ struct iwl_scale_tbl_info *b)
+{
+ return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) &&
+ (a->is_SGI == b->is_SGI);
+}
+/*
+ * Static function to get the expected throughput from an iwl_scale_tbl_info
+ * that wraps a NULL pointer check
+ */
+static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
+{
+ if (tbl->expected_tpt)
+ return tbl->expected_tpt[rs_index];
+ return 0;
+}
+
+/*
* mac80211 sends us Tx status
*/
static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
{
- int status;
- u8 retries;
- int rs_index, mac_index, index = 0;
+ int legacy_success;
+ int retries;
+ int rs_index, mac_index, i;
struct iwl_lq_sta *lq_sta = priv_sta;
struct iwl_link_quality_cmd *table;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_rate_scale_data *window = NULL;
- struct iwl_rate_scale_data *search_win = NULL;
enum mac80211_rate_control_flags mac_flags;
u32 tx_rate;
struct iwl_scale_tbl_info tbl_type;
- struct iwl_scale_tbl_info *curr_tbl, *search_tbl;
- u8 active_index = 0;
+ struct iwl_scale_tbl_info *curr_tbl, *other_tbl;
s32 tpt = 0;
IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
@@ -850,30 +764,14 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
info->flags & IEEE80211_TX_CTL_NO_ACK)
return;
- /* This packet was aggregated but doesn't carry rate scale info */
+ /* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(info->flags & IEEE80211_TX_STAT_AMPDU))
return;
- if (info->flags & IEEE80211_TX_STAT_AMPDU)
- retries = 0;
- else
- retries = info->status.rates[0].count - 1;
-
- if (retries > 15)
- retries = 15;
-
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
!lq_sta->ibss_sta_added)
- goto out;
-
- table = &lq_sta->lq;
- active_index = lq_sta->active_tbl;
-
- curr_tbl = &(lq_sta->lq_info[active_index]);
- search_tbl = &(lq_sta->lq_info[(1 - active_index)]);
- window = (struct iwl_rate_scale_data *)&(curr_tbl->win[0]);
- search_win = (struct iwl_rate_scale_data *)&(search_tbl->win[0]);
+ return;
/*
* Ignore this Tx frame response if its initial rate doesn't match
@@ -883,6 +781,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
* to check "search" mode, or a prior "search" mode after we've moved
* to a new "search" mode (which might become the new "active" mode).
*/
+ table = &lq_sta->lq;
tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index);
if (priv->band == IEEE80211_BAND_5GHZ)
@@ -901,7 +800,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
if (priv->band == IEEE80211_BAND_2GHZ)
mac_index += IWL_FIRST_OFDM_RATE;
}
-
+ /* Here we actually compare this rate to the latest LQ command */
if ((mac_index < 0) ||
(tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
(tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
@@ -911,124 +810,106 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
(!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
(rs_index != mac_index)) {
IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate);
- /* the last LQ command could failed so the LQ in ucode not
- * the same in driver sync up
+ /*
+ * Since rates mis-match, the last LQ command may have failed.
+ * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
+ * ... driver.
*/
lq_sta->missed_rate_counter++;
if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
lq_sta->missed_rate_counter = 0;
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
}
- goto out;
+ /* Regardless, ignore this status info for outdated rate */
+ return;
+ } else
+ /* Rate did match, so reset the missed_rate_counter */
+ lq_sta->missed_rate_counter = 0;
+
+ /* Figure out if rate scale algorithm is in active or search table */
+ if (table_type_matches(&tbl_type,
+ &(lq_sta->lq_info[lq_sta->active_tbl]))) {
+ curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ } else if (table_type_matches(&tbl_type,
+ &lq_sta->lq_info[1 - lq_sta->active_tbl])) {
+ curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+ other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+ } else {
+ IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n");
+ return;
}
+ window = (struct iwl_rate_scale_data *)&(curr_tbl->win[0]);
- lq_sta->missed_rate_counter = 0;
- /* Update frame history window with "failure" for each Tx retry. */
- while (retries) {
- /* Look up the rate and other info used for each tx attempt.
- * Each tx attempt steps one entry deeper in the rate table. */
- tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags);
- rs_get_tbl_info_from_mcs(tx_rate, priv->band,
- &tbl_type, &rs_index);
-
- /* If type matches "search" table,
- * add failure to "search" history */
- if ((tbl_type.lq_type == search_tbl->lq_type) &&
- (tbl_type.ant_type == search_tbl->ant_type) &&
- (tbl_type.is_SGI == search_tbl->is_SGI)) {
- if (search_tbl->expected_tpt)
- tpt = search_tbl->expected_tpt[rs_index];
- else
- tpt = 0;
- rs_collect_tx_data(search_win, rs_index, tpt, 1, 0);
-
- /* Else if type matches "current/active" table,
- * add failure to "current/active" history */
- } else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
- (tbl_type.ant_type == curr_tbl->ant_type) &&
- (tbl_type.is_SGI == curr_tbl->is_SGI)) {
- if (curr_tbl->expected_tpt)
- tpt = curr_tbl->expected_tpt[rs_index];
- else
- tpt = 0;
- rs_collect_tx_data(window, rs_index, tpt, 1, 0);
+ /*
+ * Updating the frame history depends on whether packets were
+ * aggregated.
+ *
+ * For aggregation, all packets were transmitted at the same rate, the
+ * first index into rate scale table.
+ */
+ if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+ tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
+ rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type,
+ &rs_index);
+ tpt = get_expected_tpt(curr_tbl, rs_index);
+ rs_collect_tx_data(window, rs_index, tpt,
+ info->status.ampdu_ack_len,
+ info->status.ampdu_ack_map);
+
+ /* Update success/fail counts if not searching for new mode */
+ if (lq_sta->stay_in_tbl) {
+ lq_sta->total_success += info->status.ampdu_ack_map;
+ lq_sta->total_failed += (info->status.ampdu_ack_len -
+ info->status.ampdu_ack_map);
}
-
- /* If not searching for a new mode, increment failed counter
- * ... this helps determine when to start searching again */
- if (lq_sta->stay_in_tbl)
- lq_sta->total_failed++;
- --retries;
- index++;
-
- }
-
+ } else {
/*
- * Find (by rate) the history window to update with final Tx attempt;
- * if Tx was successful first try, use original rate,
- * else look up the rate that was, finally, successful.
+ * For legacy, update frame history with for each Tx retry.
*/
- tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags);
- lq_sta->last_rate_n_flags = tx_rate;
- rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index);
-
- /* Update frame history window with "success" if Tx got ACKed ... */
- status = !!(info->flags & IEEE80211_TX_STAT_ACK);
-
- /* If type matches "search" table,
- * add final tx status to "search" history */
- if ((tbl_type.lq_type == search_tbl->lq_type) &&
- (tbl_type.ant_type == search_tbl->ant_type) &&
- (tbl_type.is_SGI == search_tbl->is_SGI)) {
- if (search_tbl->expected_tpt)
- tpt = search_tbl->expected_tpt[rs_index];
- else
- tpt = 0;
- if (info->flags & IEEE80211_TX_STAT_AMPDU)
- rs_collect_tx_data(search_win, rs_index, tpt,
- info->status.ampdu_ack_len,
- info->status.ampdu_ack_map);
- else
- rs_collect_tx_data(search_win, rs_index, tpt,
- 1, status);
- /* Else if type matches "current/active" table,
- * add final tx status to "current/active" history */
- } else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
- (tbl_type.ant_type == curr_tbl->ant_type) &&
- (tbl_type.is_SGI == curr_tbl->is_SGI)) {
- if (curr_tbl->expected_tpt)
- tpt = curr_tbl->expected_tpt[rs_index];
- else
- tpt = 0;
- if (info->flags & IEEE80211_TX_STAT_AMPDU)
- rs_collect_tx_data(window, rs_index, tpt,
- info->status.ampdu_ack_len,
- info->status.ampdu_ack_map);
- else
- rs_collect_tx_data(window, rs_index, tpt,
- 1, status);
- }
+ retries = info->status.rates[0].count - 1;
+ /* HW doesn't send more than 15 retries */
+ retries = min(retries, 15);
+
+ /* The last transmission may have been successful */
+ legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ /* Collect data for each rate used during failed TX attempts */
+ for (i = 0; i <= retries; ++i) {
+ tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags);
+ rs_get_tbl_info_from_mcs(tx_rate, priv->band,
+ &tbl_type, &rs_index);
+ /*
+ * Only collect stats if retried rate is in the same RS
+ * table as active/search.
+ */
+ if (table_type_matches(&tbl_type, curr_tbl))
+ tpt = get_expected_tpt(curr_tbl, rs_index);
+ else if (table_type_matches(&tbl_type, other_tbl))
+ tpt = get_expected_tpt(other_tbl, rs_index);
+ else
+ continue;
- /* If not searching for new mode, increment success/failed counter
- * ... these help determine when to start searching again */
- if (lq_sta->stay_in_tbl) {
- if (info->flags & IEEE80211_TX_STAT_AMPDU) {
- lq_sta->total_success += info->status.ampdu_ack_map;
- lq_sta->total_failed +=
- (info->status.ampdu_ack_len - info->status.ampdu_ack_map);
- } else {
- if (status)
- lq_sta->total_success++;
+ /* Constants mean 1 transmission, 0 successes */
+ if (i < retries)
+ rs_collect_tx_data(window, rs_index, tpt, 1,
+ 0);
else
- lq_sta->total_failed++;
+ rs_collect_tx_data(window, rs_index, tpt, 1,
+ legacy_success);
+ }
+
+ /* Update success/fail counts if not searching for new mode */
+ if (lq_sta->stay_in_tbl) {
+ lq_sta->total_success += legacy_success;
+ lq_sta->total_failed += retries + (1 - legacy_success);
}
}
+ /* The last TX rate is cached in lq_sta; it's set in if/else above */
+ lq_sta->last_rate_n_flags = tx_rate;
/* See if there's a better rate or modulation mode to try. */
if (sta && sta->supp_rates[sband->band])
rs_rate_scale_perform(priv, skb, sta, lq_sta);
-out:
- return;
}
/*
@@ -1066,43 +947,45 @@ static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy,
static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
struct iwl_scale_tbl_info *tbl)
{
+ /* Used to choose among HT tables */
+ s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+
+ /* Check for invalid LQ type */
+ if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
+ tbl->expected_tpt = expected_tpt_legacy;
+ return;
+ }
+
+ /* Legacy rates have only one table */
if (is_legacy(tbl->lq_type)) {
- if (!is_a_band(tbl->lq_type))
- tbl->expected_tpt = expected_tpt_G;
- else
- tbl->expected_tpt = expected_tpt_A;
- } else if (is_siso(tbl->lq_type)) {
- if (tbl->is_ht40 && !lq_sta->is_dup)
- if (tbl->is_SGI)
- tbl->expected_tpt = expected_tpt_siso40MHzSGI;
- else
- tbl->expected_tpt = expected_tpt_siso40MHz;
- else if (tbl->is_SGI)
- tbl->expected_tpt = expected_tpt_siso20MHzSGI;
- else
- tbl->expected_tpt = expected_tpt_siso20MHz;
- } else if (is_mimo2(tbl->lq_type)) {
- if (tbl->is_ht40 && !lq_sta->is_dup)
- if (tbl->is_SGI)
- tbl->expected_tpt = expected_tpt_mimo2_40MHzSGI;
- else
- tbl->expected_tpt = expected_tpt_mimo2_40MHz;
- else if (tbl->is_SGI)
- tbl->expected_tpt = expected_tpt_mimo2_20MHzSGI;
- else
- tbl->expected_tpt = expected_tpt_mimo2_20MHz;
- } else if (is_mimo3(tbl->lq_type)) {
- if (tbl->is_ht40 && !lq_sta->is_dup)
- if (tbl->is_SGI)
- tbl->expected_tpt = expected_tpt_mimo3_40MHzSGI;
- else
- tbl->expected_tpt = expected_tpt_mimo3_40MHz;
- else if (tbl->is_SGI)
- tbl->expected_tpt = expected_tpt_mimo3_20MHzSGI;
- else
- tbl->expected_tpt = expected_tpt_mimo3_20MHz;
- } else
- tbl->expected_tpt = expected_tpt_G;
+ tbl->expected_tpt = expected_tpt_legacy;
+ return;
+ }
+
+ /* Choose among many HT tables depending on number of streams
+ * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation
+ * status */
+ if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
+ ht_tbl_pointer = expected_tpt_siso20MHz;
+ else if (is_siso(tbl->lq_type))
+ ht_tbl_pointer = expected_tpt_siso40MHz;
+ else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
+ ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+ else if (is_mimo2(tbl->lq_type))
+ ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+ else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup))
+ ht_tbl_pointer = expected_tpt_mimo3_20MHz;
+ else /* if (is_mimo3(tbl->lq_type)) <-- must be true */
+ ht_tbl_pointer = expected_tpt_mimo3_40MHz;
+
+ if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */
+ tbl->expected_tpt = ht_tbl_pointer[0];
+ else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */
+ tbl->expected_tpt = ht_tbl_pointer[1];
+ else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */
+ tbl->expected_tpt = ht_tbl_pointer[2];
+ else /* AGG+SGI */
+ tbl->expected_tpt = ht_tbl_pointer[3];
}
/*
@@ -2077,6 +1960,14 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
tid = rs_tl_add_packet(lq_sta, hdr);
+ if ((tid != MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) {
+ tid_data = &priv->stations[lq_sta->lq.sta_id].tid[tid];
+ if (tid_data->agg.state == IWL_AGG_OFF)
+ lq_sta->is_agg = 0;
+ else
+ lq_sta->is_agg = 1;
+ } else
+ lq_sta->is_agg = 0;
/*
* Select rate-scale / modulation-mode table to work with in
@@ -2177,10 +2068,10 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
goto out;
}
-
/* Else we have enough samples; calculate estimate of
* actual average throughput */
+ /* Sanity-check TPT calculations */
BUG_ON(window->average_tpt != ((window->success_ratio *
tbl->expected_tpt[index] + 64) / 128));
@@ -2584,22 +2475,13 @@ static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
gfp_t gfp)
{
struct iwl_lq_sta *lq_sta;
+ struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv;
struct iwl_priv *priv;
- int i, j;
priv = (struct iwl_priv *)priv_rate;
IWL_DEBUG_RATE(priv, "create station rate scale window\n");
- lq_sta = kzalloc(sizeof(struct iwl_lq_sta), gfp);
-
- if (lq_sta == NULL)
- return NULL;
- lq_sta->lq.sta_id = 0xff;
-
-
- for (j = 0; j < LQ_SIZE; j++)
- for (i = 0; i < IWL_RATE_COUNT; i++)
- rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+ lq_sta = &sta_priv->lq_sta;
return lq_sta;
}
@@ -2613,6 +2495,12 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct iwl_lq_sta *lq_sta = priv_sta;
+ lq_sta->lq.sta_id = 0xff;
+
+ for (j = 0; j < LQ_SIZE; j++)
+ for (i = 0; i < IWL_RATE_COUNT; i++)
+ rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+
lq_sta->flush_timer = 0;
lq_sta->supp_rates = sta->supp_rates[sband->band];
for (j = 0; j < LQ_SIZE; j++)
@@ -2690,6 +2578,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
lq_sta->last_txrate_idx = rate_lowest_index(sband, sta);
if (sband->band == IEEE80211_BAND_5GHZ)
lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
+ lq_sta->is_agg = 0;
rs_initialize_lq(priv, conf, sta, lq_sta);
}
@@ -2808,7 +2697,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
repeat_rate--;
}
- lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_MAX;
+ lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
lq_cmd->agg_params.agg_time_limit =
cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
@@ -2827,11 +2716,9 @@ static void rs_free(void *priv_rate)
static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta,
void *priv_sta)
{
- struct iwl_lq_sta *lq_sta = priv_sta;
struct iwl_priv *priv __maybe_unused = priv_r;
IWL_DEBUG_RATE(priv, "enter\n");
- kfree(lq_sta);
IWL_DEBUG_RATE(priv, "leave\n");
}
@@ -2942,8 +2829,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
desc += sprintf(buff+desc, " %s",
(tbl->is_ht40) ? "40MHz" : "20MHz");
- desc += sprintf(buff+desc, " %s %s\n", (tbl->is_SGI) ? "SGI" : "",
- (lq_sta->is_green) ? "GF enabled" : "");
+ desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "",
+ (lq_sta->is_green) ? "GF enabled" : "",
+ (lq_sta->is_agg) ? "AGG on" : "");
}
desc += sprintf(buff+desc, "last tx rate=0x%X\n",
lq_sta->last_rate_n_flags);
@@ -3076,16 +2964,16 @@ static void rs_add_debugfs(void *priv, void *priv_sta,
{
struct iwl_lq_sta *lq_sta = priv_sta;
lq_sta->rs_sta_dbgfs_scale_table_file =
- debugfs_create_file("rate_scale_table", 0600, dir,
+ debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
lq_sta, &rs_sta_dbgfs_scale_table_ops);
lq_sta->rs_sta_dbgfs_stats_table_file =
- debugfs_create_file("rate_stats_table", 0600, dir,
+ debugfs_create_file("rate_stats_table", S_IRUSR, dir,
lq_sta, &rs_sta_dbgfs_stats_table_ops);
lq_sta->rs_sta_dbgfs_rate_scale_data_file =
- debugfs_create_file("rate_scale_data", 0600, dir,
+ debugfs_create_file("rate_scale_data", S_IRUSR, dir,
lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
- debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
+ debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
&lq_sta->tx_agg_tid_en);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
index 9fac530cfb7..affc0c5a2f2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h
@@ -54,6 +54,7 @@ struct iwl3945_rate_info {
u8 prev_table_rs; /* prev in rate table cmd */
};
+
/*
* These serve as indexes into
* struct iwl_rate_info iwl_rates[IWL_RATE_COUNT];
@@ -335,6 +336,106 @@ struct iwl_rate_mcs_info {
char mcs[IWL_MAX_MCS_DISPLAY_SIZE];
};
+/**
+ * struct iwl_rate_scale_data -- tx success history for one rate
+ */
+struct iwl_rate_scale_data {
+ u64 data; /* bitmap of successful frames */
+ s32 success_counter; /* number of frames successful */
+ s32 success_ratio; /* per-cent * 128 */
+ s32 counter; /* number of frames attempted */
+ s32 average_tpt; /* success ratio * expected throughput */
+ unsigned long stamp;
+};
+
+/**
+ * struct iwl_scale_tbl_info -- tx params and success history for all rates
+ *
+ * There are two of these in struct iwl_lq_sta,
+ * one for "active", and one for "search".
+ */
+struct iwl_scale_tbl_info {
+ enum iwl_table_type lq_type;
+ u8 ant_type;
+ u8 is_SGI; /* 1 = short guard interval */
+ u8 is_ht40; /* 1 = 40 MHz channel width */
+ u8 is_dup; /* 1 = duplicated data streams */
+ u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
+ u8 max_search; /* maximun number of tables we can search */
+ s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */
+ u32 current_rate; /* rate_n_flags, uCode API format */
+ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
+};
+
+struct iwl_traffic_load {
+ unsigned long time_stamp; /* age of the oldest statistics */
+ u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time
+ * slice */
+ u32 total; /* total num of packets during the
+ * last TID_MAX_TIME_DIFF */
+ u8 queue_count; /* number of queues that has
+ * been used since the last cleanup */
+ u8 head; /* start of the circular buffer */
+};
+
+/**
+ * struct iwl_lq_sta -- driver's rate scaling private structure
+ *
+ * Pointer to this gets passed back and forth between driver and mac80211.
+ */
+struct iwl_lq_sta {
+ u8 active_tbl; /* index of active table, range 0-1 */
+ u8 enable_counter; /* indicates HT mode */
+ u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */
+ u8 search_better_tbl; /* 1: currently trying alternate mode */
+ s32 last_tpt;
+
+ /* The following determine when to search for a new mode */
+ u32 table_count_limit;
+ u32 max_failure_limit; /* # failed frames before new search */
+ u32 max_success_limit; /* # successful frames before new search */
+ u32 table_count;
+ u32 total_failed; /* total failed frames, any/all rates */
+ u32 total_success; /* total successful frames, any/all rates */
+ u64 flush_timer; /* time staying in mode before new search */
+
+ u8 action_counter; /* # mode-switch actions tried */
+ u8 is_green;
+ u8 is_dup;
+ enum ieee80211_band band;
+ u8 ibss_sta_added;
+
+ /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
+ u32 supp_rates;
+ u16 active_legacy_rate;
+ u16 active_siso_rate;
+ u16 active_mimo2_rate;
+ u16 active_mimo3_rate;
+ u16 active_rate_basic;
+ s8 max_rate_idx; /* Max rate set by user */
+ u8 missed_rate_counter;
+
+ struct iwl_link_quality_cmd lq;
+ struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
+ struct iwl_traffic_load load[TID_MAX_LOAD_COUNT];
+ u8 tx_agg_tid_en;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct dentry *rs_sta_dbgfs_scale_table_file;
+ struct dentry *rs_sta_dbgfs_stats_table_file;
+ struct dentry *rs_sta_dbgfs_rate_scale_data_file;
+ struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
+ u32 dbg_fixed_rate;
+#endif
+ struct iwl_priv *drv;
+
+ /* used to be in sta_info */
+ int last_txrate_idx;
+ /* last tx rate_n_flags */
+ u32 last_rate_n_flags;
+ /* packets destined for this STA are aggregated */
+ u8 is_agg;
+};
+
static inline u8 num_of_ant(u8 mask)
{
return !!((mask) & ANT_A) +
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 921dc4a26fe..b8377efb3ba 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -123,6 +123,17 @@ int iwl_commit_rxon(struct iwl_priv *priv)
return -EINVAL;
}
+ /*
+ * receive commit_rxon request
+ * abort any previous channel switch if still in process
+ */
+ if (priv->switch_rxon.switch_in_progress &&
+ (priv->switch_rxon.channel != priv->staging_rxon.channel)) {
+ IWL_DEBUG_11H(priv, "abort channel switch on %d\n",
+ le16_to_cpu(priv->switch_rxon.channel));
+ priv->switch_rxon.switch_in_progress = false;
+ }
+
/* If we don't need to send a full RXON, we can use
* iwl_rxon_assoc_cmd which is used to reconfigure filter
* and other flags for the current radio configuration. */
@@ -134,6 +145,7 @@ int iwl_commit_rxon(struct iwl_priv *priv)
}
memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
+ iwl_print_rx_config_cmd(priv);
return 0;
}
@@ -191,11 +203,7 @@ int iwl_commit_rxon(struct iwl_priv *priv)
priv->start_calib = 0;
/* Add the broadcast address so we can send broadcast frames */
- if (iwl_rxon_add_station(priv, iwl_bcast_addr, 0) ==
- IWL_INVALID_STATION) {
- IWL_ERR(priv, "Error adding BROADCAST address for transmit.\n");
- return -EIO;
- }
+ iwl_add_bcast_station(priv);
/* If we have set the ASSOC_MSK and we are in BSS mode then
* add the IWL_AP_ID to the station rate table */
@@ -233,6 +241,7 @@ int iwl_commit_rxon(struct iwl_priv *priv)
}
memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
}
+ iwl_print_rx_config_cmd(priv);
iwl_init_sensitivity(priv);
@@ -302,7 +311,7 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame)
list_add(&frame->list, &priv->free_frames);
}
-static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
+static u32 iwl_fill_beacon_frame(struct iwl_priv *priv,
struct ieee80211_hdr *hdr,
int left)
{
@@ -319,34 +328,74 @@ static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv,
return priv->ibss_beacon->len;
}
+/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */
+static void iwl_set_beacon_tim(struct iwl_priv *priv,
+ struct iwl_tx_beacon_cmd *tx_beacon_cmd,
+ u8 *beacon, u32 frame_size)
+{
+ u16 tim_idx;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
+
+ /*
+ * The index is relative to frame start but we start looking at the
+ * variable-length part of the beacon.
+ */
+ tim_idx = mgmt->u.beacon.variable - beacon;
+
+ /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+ while ((tim_idx < (frame_size - 2)) &&
+ (beacon[tim_idx] != WLAN_EID_TIM))
+ tim_idx += beacon[tim_idx+1] + 2;
+
+ /* If TIM field was found, set variables */
+ if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
+ tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx);
+ tx_beacon_cmd->tim_size = beacon[tim_idx+1];
+ } else
+ IWL_WARN(priv, "Unable to find TIM Element in beacon\n");
+}
+
static unsigned int iwl_hw_get_beacon_cmd(struct iwl_priv *priv,
- struct iwl_frame *frame, u8 rate)
+ struct iwl_frame *frame)
{
struct iwl_tx_beacon_cmd *tx_beacon_cmd;
- unsigned int frame_size;
+ u32 frame_size;
+ u32 rate_flags;
+ u32 rate;
+ /*
+ * We have to set up the TX command, the TX Beacon command, and the
+ * beacon contents.
+ */
+ /* Initialize memory */
tx_beacon_cmd = &frame->u.beacon;
memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd));
- tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id;
- tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
-
+ /* Set up TX beacon contents */
frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame,
sizeof(frame->u) - sizeof(*tx_beacon_cmd));
+ if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE))
+ return 0;
- BUG_ON(frame_size > MAX_MPDU_SIZE);
+ /* Set up TX command fields */
tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size);
+ tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id;
+ tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+ tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
+ TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK;
- if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP))
- tx_beacon_cmd->tx.rate_n_flags =
- iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK);
- else
- tx_beacon_cmd->tx.rate_n_flags =
- iwl_hw_set_rate_n_flags(rate, 0);
+ /* Set up TX beacon command fields */
+ iwl_set_beacon_tim(priv, tx_beacon_cmd, (u8 *)tx_beacon_cmd->frame,
+ frame_size);
- tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK |
- TX_CMD_FLG_TSF_MSK |
- TX_CMD_FLG_STA_RATE_MSK;
+ /* Set up packet rate and flags */
+ rate = iwl_rate_get_lowest_plcp(priv);
+ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant);
+ rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant);
+ if ((rate >= IWL_FIRST_CCK_RATE) && (rate <= IWL_LAST_CCK_RATE))
+ rate_flags |= RATE_MCS_CCK_MSK;
+ tx_beacon_cmd->tx.rate_n_flags = iwl_hw_set_rate_n_flags(rate,
+ rate_flags);
return sizeof(*tx_beacon_cmd) + frame_size;
}
@@ -355,19 +404,20 @@ static int iwl_send_beacon_cmd(struct iwl_priv *priv)
struct iwl_frame *frame;
unsigned int frame_size;
int rc;
- u8 rate;
frame = iwl_get_free_frame(priv);
-
if (!frame) {
IWL_ERR(priv, "Could not obtain free frame buffer for beacon "
"command.\n");
return -ENOMEM;
}
- rate = iwl_rate_get_lowest_plcp(priv);
-
- frame_size = iwl_hw_get_beacon_cmd(priv, frame, rate);
+ frame_size = iwl_hw_get_beacon_cmd(priv, frame);
+ if (!frame_size) {
+ IWL_ERR(priv, "Error configuring the beacon command\n");
+ iwl_free_frame(priv, frame);
+ return -EINVAL;
+ }
rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size,
&frame->u.cmd[0]);
@@ -525,7 +575,7 @@ int iwl_hw_tx_queue_init(struct iwl_priv *priv,
static void iwl_rx_reply_alive(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_alive_resp *palive;
struct delayed_work *pwork;
@@ -604,14 +654,14 @@ static void iwl_bg_statistics_periodic(unsigned long data)
if (!iwl_is_ready_rf(priv))
return;
- iwl_send_statistics_request(priv, CMD_ASYNC);
+ iwl_send_statistics_request(priv, CMD_ASYNC, false);
}
static void iwl_rx_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl4965_beacon_notif *beacon =
(struct iwl4965_beacon_notif *)pkt->u.raw;
u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags);
@@ -635,7 +685,7 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv,
static void iwl_rx_card_state_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
unsigned long status = priv->status;
@@ -721,7 +771,7 @@ static void iwl_setup_rx_handlers(struct iwl_priv *priv)
* statistics request from the host as well as for the periodic
* statistics notifications (after received beacons) from the uCode.
*/
- priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_rx_statistics;
+ priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_reply_statistics;
priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics;
iwl_setup_spectrum_handlers(priv);
@@ -770,7 +820,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
IWL_DEBUG_RX(priv, "r = %d, i = %d\n", r, i);
/* calculate total frames need to be restock after handling RX */
- total_empty = r - priv->rxq.write_actual;
+ total_empty = r - rxq->write_actual;
if (total_empty < 0)
total_empty += RX_QUEUE_SIZE;
@@ -787,10 +837,13 @@ void iwl_rx_handle(struct iwl_priv *priv)
rxq->queue[i] = NULL;
- pci_unmap_single(priv->pci_dev, rxb->real_dma_addr,
- priv->hw_params.rx_buf_size + 256,
- PCI_DMA_FROMDEVICE);
- pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ pci_unmap_page(priv->pci_dev, rxb->page_dma,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ pkt = rxb_addr(rxb);
+
+ trace_iwlwifi_dev_rx(priv, pkt,
+ le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
/* Reclaim a command buffer only if this packet is a response
* to a (driver-originated) command.
@@ -812,8 +865,8 @@ void iwl_rx_handle(struct iwl_priv *priv)
if (priv->rx_handlers[pkt->hdr.cmd]) {
IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r,
i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
+ priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
} else {
/* No handling needed */
IWL_DEBUG_RX(priv,
@@ -822,35 +875,45 @@ void iwl_rx_handle(struct iwl_priv *priv)
pkt->hdr.cmd);
}
+ /*
+ * XXX: After here, we should always check rxb->page
+ * against NULL before touching it or its virtual
+ * memory (pkt). Because some rx_handler might have
+ * already taken or freed the pages.
+ */
+
if (reclaim) {
- /* Invoke any callbacks, transfer the skb to caller, and
- * fire off the (possibly) blocking iwl_send_cmd()
+ /* Invoke any callbacks, transfer the buffer to caller,
+ * and fire off the (possibly) blocking iwl_send_cmd()
* as we reclaim the driver command queue */
- if (rxb && rxb->skb)
+ if (rxb->page)
iwl_tx_cmd_complete(priv, rxb);
else
IWL_WARN(priv, "Claim null rxb?\n");
}
- /* For now we just don't re-use anything. We can tweak this
- * later to try and re-use notification packets and SKBs that
- * fail to Rx correctly */
- if (rxb->skb != NULL) {
- priv->alloc_rxb_skb--;
- dev_kfree_skb_any(rxb->skb);
- rxb->skb = NULL;
- }
-
+ /* Reuse the page if possible. For notification packets and
+ * SKBs that fail to Rx correctly, add them back into the
+ * rx_free list for reuse later. */
spin_lock_irqsave(&rxq->lock, flags);
- list_add_tail(&rxb->list, &priv->rxq.rx_used);
+ if (rxb->page != NULL) {
+ rxb->page_dma = pci_map_page(priv->pci_dev, rxb->page,
+ 0, PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ } else
+ list_add_tail(&rxb->list, &rxq->rx_used);
+
spin_unlock_irqrestore(&rxq->lock, flags);
+
i = (i + 1) & RX_QUEUE_MASK;
/* If there are a lot of unused frames,
* restock the Rx queue so ucode wont assert. */
if (fill_rx) {
count++;
if (count >= 8) {
- priv->rxq.read = i;
+ rxq->read = i;
iwl_rx_replenish_now(priv);
count = 0;
}
@@ -858,7 +921,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
}
/* Backtrack one entry */
- priv->rxq.read = i;
+ rxq->read = i;
if (fill_rx)
iwl_rx_replenish_now(priv);
else
@@ -878,6 +941,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
u32 inta, handled = 0;
u32 inta_fh;
unsigned long flags;
+ u32 i;
#ifdef CONFIG_IWLWIFI_DEBUG
u32 inta_mask;
#endif
@@ -905,6 +969,8 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
}
#endif
+ spin_unlock_irqrestore(&priv->lock, flags);
+
/* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
* atomic, make sure that inta covers all the interrupts that
* we've discovered, even if FH interrupt came in just after
@@ -926,8 +992,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
handled |= CSR_INT_BIT_HW_ERR;
- spin_unlock_irqrestore(&priv->lock, flags);
-
return;
}
@@ -995,19 +1059,17 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
handled |= CSR_INT_BIT_SW_ERR;
}
- /* uCode wakes up after power-down sleep */
+ /*
+ * uCode wakes up after power-down sleep.
+ * Tell device about any new tx or host commands enqueued,
+ * and about any Rx buffers made available while asleep.
+ */
if (inta & CSR_INT_BIT_WAKEUP) {
IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
- iwl_txq_update_write_ptr(priv, &priv->txq[0]);
- iwl_txq_update_write_ptr(priv, &priv->txq[1]);
- iwl_txq_update_write_ptr(priv, &priv->txq[2]);
- iwl_txq_update_write_ptr(priv, &priv->txq[3]);
- iwl_txq_update_write_ptr(priv, &priv->txq[4]);
- iwl_txq_update_write_ptr(priv, &priv->txq[5]);
-
+ for (i = 0; i < priv->hw_params.max_txq_num; i++)
+ iwl_txq_update_write_ptr(priv, &priv->txq[i]);
priv->isr_stats.wakeup++;
-
handled |= CSR_INT_BIT_WAKEUP;
}
@@ -1020,11 +1082,12 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
}
+ /* This "Tx" DMA channel is used only for loading uCode */
if (inta & CSR_INT_BIT_FH_TX) {
- IWL_DEBUG_ISR(priv, "Tx interrupt\n");
+ IWL_DEBUG_ISR(priv, "uCode load interrupt\n");
priv->isr_stats.tx++;
handled |= CSR_INT_BIT_FH_TX;
- /* FH finished to write, send event */
+ /* Wake up uCode load routine, now that load is complete */
priv->ucode_write_complete = 1;
wake_up_interruptible(&priv->wait_command_queue);
}
@@ -1054,7 +1117,6 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
"flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
}
#endif
- spin_unlock_irqrestore(&priv->lock, flags);
}
/* tasklet for iwlagn interrupt */
@@ -1063,6 +1125,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
u32 inta = 0;
u32 handled = 0;
unsigned long flags;
+ u32 i;
#ifdef CONFIG_IWLWIFI_DEBUG
u32 inta_mask;
#endif
@@ -1084,6 +1147,9 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
inta, inta_mask);
}
#endif
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
/* saved interrupt in inta variable now we can reset priv->inta */
priv->inta = 0;
@@ -1099,8 +1165,6 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
handled |= CSR_INT_BIT_HW_ERR;
- spin_unlock_irqrestore(&priv->lock, flags);
-
return;
}
@@ -1172,12 +1236,8 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
if (inta & CSR_INT_BIT_WAKEUP) {
IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
- iwl_txq_update_write_ptr(priv, &priv->txq[0]);
- iwl_txq_update_write_ptr(priv, &priv->txq[1]);
- iwl_txq_update_write_ptr(priv, &priv->txq[2]);
- iwl_txq_update_write_ptr(priv, &priv->txq[3]);
- iwl_txq_update_write_ptr(priv, &priv->txq[4]);
- iwl_txq_update_write_ptr(priv, &priv->txq[5]);
+ for (i = 0; i < priv->hw_params.max_txq_num; i++)
+ iwl_txq_update_write_ptr(priv, &priv->txq[i]);
priv->isr_stats.wakeup++;
@@ -1206,26 +1266,36 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
* 3- update RX shared data to indicate last write index.
* 4- send interrupt.
* This could lead to RX race, driver could receive RX interrupt
- * but the shared data changes does not reflect this.
- * this could lead to RX race, RX periodic will solve this race
+ * but the shared data changes does not reflect this;
+ * periodic interrupt will detect any dangling Rx activity.
*/
- iwl_write32(priv, CSR_INT_PERIODIC_REG,
+
+ /* Disable periodic interrupt; we use it as just a one-shot. */
+ iwl_write8(priv, CSR_INT_PERIODIC_REG,
CSR_INT_PERIODIC_DIS);
iwl_rx_handle(priv);
- /* Only set RX periodic if real RX is received. */
+
+ /*
+ * Enable periodic interrupt in 8 msec only if we received
+ * real RX interrupt (instead of just periodic int), to catch
+ * any dangling Rx interrupt. If it was just the periodic
+ * interrupt, there was no dangling Rx activity, and no need
+ * to extend the periodic interrupt; one-shot is enough.
+ */
if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
- iwl_write32(priv, CSR_INT_PERIODIC_REG,
+ iwl_write8(priv, CSR_INT_PERIODIC_REG,
CSR_INT_PERIODIC_ENA);
priv->isr_stats.rx++;
}
+ /* This "Tx" DMA channel is used only for loading uCode */
if (inta & CSR_INT_BIT_FH_TX) {
iwl_write32(priv, CSR_FH_INT_STATUS, CSR49_FH_INT_TX_MASK);
- IWL_DEBUG_ISR(priv, "Tx interrupt\n");
+ IWL_DEBUG_ISR(priv, "uCode load interrupt\n");
priv->isr_stats.tx++;
handled |= CSR_INT_BIT_FH_TX;
- /* FH finished to write, send event */
+ /* Wake up uCode load routine, now that load is complete */
priv->ucode_write_complete = 1;
wake_up_interruptible(&priv->wait_command_queue);
}
@@ -1240,14 +1310,10 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
inta & ~priv->inta_mask);
}
-
/* Re-enable all interrupts */
/* only Re-enable if diabled by irq */
if (test_bit(STATUS_INT_ENABLED, &priv->status))
iwl_enable_interrupts(priv);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
}
@@ -1367,6 +1433,14 @@ static int iwl_read_ucode(struct iwl_priv *priv)
IWL_UCODE_API(priv->ucode_ver),
IWL_UCODE_SERIAL(priv->ucode_ver));
+ snprintf(priv->hw->wiphy->fw_version,
+ sizeof(priv->hw->wiphy->fw_version),
+ "%u.%u.%u.%u",
+ IWL_UCODE_MAJOR(priv->ucode_ver),
+ IWL_UCODE_MINOR(priv->ucode_ver),
+ IWL_UCODE_API(priv->ucode_ver),
+ IWL_UCODE_SERIAL(priv->ucode_ver));
+
if (build)
IWL_DEBUG_INFO(priv, "Build %u\n", build);
@@ -1531,7 +1605,6 @@ static int iwl_read_ucode(struct iwl_priv *priv)
return ret;
}
-#ifdef CONFIG_IWLWIFI_DEBUG
static const char *desc_lookup_text[] = {
"OK",
"FAIL",
@@ -1589,7 +1662,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
base = le32_to_cpu(priv->card_alive.error_event_table_ptr);
if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base);
+ IWL_ERR(priv,
+ "Not valid error log pointer 0x%08X for %s uCode\n",
+ base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT");
return;
}
@@ -1611,6 +1686,9 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32));
time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32));
+ trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, data2, line,
+ blink1, blink2, ilink1, ilink2);
+
IWL_ERR(priv, "Desc Time "
"data1 data2 line\n");
IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n",
@@ -1635,6 +1713,7 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
u32 ptr; /* SRAM byte address of log data */
u32 ev, time, data; /* event log data */
+ unsigned long reg_flags;
if (num_events == 0)
return;
@@ -1650,26 +1729,72 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
+ /* Make sure device is powered up for SRAM reads */
+ spin_lock_irqsave(&priv->reg_lock, reg_flags);
+ iwl_grab_nic_access(priv);
+
+ /* Set starting address; reads will auto-increment */
+ _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
+ rmb();
+
/* "time" is actually "data" for mode 0 (no timestamp).
* place event id # at far right for easier visual parsing. */
for (i = 0; i < num_events; i++) {
- ev = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
- time = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
+ ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+ time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
if (mode == 0) {
/* data, ev */
+ trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev);
} else {
- data = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
+ data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
time, data, ev);
+ trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
}
}
+
+ /* Allow device to power down */
+ iwl_release_nic_access(priv);
+ spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+}
+
+/**
+ * iwl_print_last_event_logs - Dump the newest # of event log to syslog
+ */
+static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+ u32 num_wraps, u32 next_entry,
+ u32 size, u32 mode)
+{
+ /*
+ * display the newest DEFAULT_LOG_ENTRIES entries
+ * i.e the entries just before the next ont that uCode would fill.
+ */
+ if (num_wraps) {
+ if (next_entry < size) {
+ iwl_print_event_log(priv,
+ capacity - (size - next_entry),
+ size - next_entry, mode);
+ iwl_print_event_log(priv, 0,
+ next_entry, mode);
+ } else
+ iwl_print_event_log(priv, next_entry - size,
+ size, mode);
+ } else {
+ if (next_entry < size)
+ iwl_print_event_log(priv, 0, next_entry, mode);
+ else
+ iwl_print_event_log(priv, next_entry - size,
+ size, mode);
+ }
}
-void iwl_dump_nic_event_log(struct iwl_priv *priv)
+/* For sanity check only. Actual size is determined by uCode, typ. 512 */
+#define MAX_EVENT_LOG_SIZE (512)
+
+#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
+
+void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
{
u32 base; /* SRAM byte address of event log header */
u32 capacity; /* event log capacity in # entries */
@@ -1684,7 +1809,9 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
- IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base);
+ IWL_ERR(priv,
+ "Invalid event log pointer 0x%08X for %s uCode\n",
+ base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT");
return;
}
@@ -1694,6 +1821,18 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
+ if (capacity > MAX_EVENT_LOG_SIZE) {
+ IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n",
+ capacity, MAX_EVENT_LOG_SIZE);
+ capacity = MAX_EVENT_LOG_SIZE;
+ }
+
+ if (next_entry > MAX_EVENT_LOG_SIZE) {
+ IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
+ next_entry, MAX_EVENT_LOG_SIZE);
+ next_entry = MAX_EVENT_LOG_SIZE;
+ }
+
size = num_wraps ? capacity : next_entry;
/* bail out if nothing in log */
@@ -1702,19 +1841,37 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
return;
}
- IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n",
- size, num_wraps);
-
- /* if uCode has wrapped back to top of log, start at the oldest entry,
- * i.e the next one that uCode would fill. */
- if (num_wraps)
- iwl_print_event_log(priv, next_entry,
- capacity - next_entry, mode);
- /* (then/else) start at top of log */
- iwl_print_event_log(priv, 0, next_entry, mode);
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS))
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#else
+ size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
+#endif
+ IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n",
+ size);
-}
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
+ /*
+ * if uCode has wrapped back to top of log,
+ * start at the oldest entry,
+ * i.e the next one that uCode would fill.
+ */
+ if (num_wraps)
+ iwl_print_event_log(priv, next_entry,
+ capacity - next_entry, mode);
+ /* (then/else) start at top of log */
+ iwl_print_event_log(priv, 0, next_entry, mode);
+ } else
+ iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode);
+#else
+ iwl_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode);
#endif
+}
/**
* iwl_alive_start - called after REPLY_ALIVE notification received
@@ -1763,6 +1920,10 @@ static void iwl_alive_start(struct iwl_priv *priv)
priv->active_rate = priv->rates_mask;
priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
+ /* Configure Tx antenna selection based on H/W config */
+ if (priv->cfg->ops->hcmd->set_tx_ant)
+ priv->cfg->ops->hcmd->set_tx_ant(priv, priv->cfg->valid_tx_ant);
+
if (iwl_is_associated(priv)) {
struct iwl_rxon_cmd *active_rxon =
(struct iwl_rxon_cmd *)&priv->active_rxon;
@@ -1790,7 +1951,7 @@ static void iwl_alive_start(struct iwl_priv *priv)
/* At this point, the NIC is initialized and operational */
iwl_rf_kill_ct_config(priv);
- iwl_leds_register(priv);
+ iwl_leds_init(priv);
IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
set_bit(STATUS_READY, &priv->status);
@@ -1828,8 +1989,6 @@ static void __iwl_down(struct iwl_priv *priv)
if (!exit_pending)
set_bit(STATUS_EXIT_PENDING, &priv->status);
- iwl_leds_unregister(priv);
-
iwl_clear_stations_table(priv);
/* Unblock any waiting calls */
@@ -1877,24 +2036,20 @@ static void __iwl_down(struct iwl_priv *priv)
/* device going down, Stop using ICT table */
iwl_disable_ict(priv);
- spin_lock_irqsave(&priv->lock, flags);
- iwl_clear_bit(priv, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- spin_unlock_irqrestore(&priv->lock, flags);
iwl_txq_ctx_stop(priv);
iwl_rxq_stop(priv);
- iwl_write_prph(priv, APMG_CLK_DIS_REG,
- APMG_CLK_VAL_DMA_CLK_RQT);
-
+ /* Power-down device's busmaster DMA clocks */
+ iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
udelay(5);
- /* FIXME: apm_ops.suspend(priv) */
- if (exit_pending)
- priv->cfg->ops->lib->apm_ops.stop(priv);
- else
- priv->cfg->ops->lib->apm_ops.reset(priv);
+ /* Make sure (redundant) we've released our request to stay awake */
+ iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ /* Stop the device, and put it in low power state */
+ priv->cfg->ops->lib->apm_ops.stop(priv);
+
exit:
memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp));
@@ -2281,6 +2436,67 @@ void iwl_post_associate(struct iwl_priv *priv)
#define UCODE_READY_TIMEOUT (4 * HZ)
+/*
+ * Not a mac80211 entry point function, but it fits in with all the
+ * other mac80211 functions grouped here.
+ */
+static int iwl_setup_mac(struct iwl_priv *priv)
+{
+ int ret;
+ struct ieee80211_hw *hw = priv->hw;
+ hw->rate_control_algorithm = "iwl-agn-rs";
+
+ /* Tell mac80211 our characteristics */
+ hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_NOISE_DBM |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_SPECTRUM_MGMT;
+
+ if (!priv->cfg->broken_powersave)
+ hw->flags |= IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
+
+ hw->sta_data_size = sizeof(struct iwl_station_priv);
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC);
+
+ hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY |
+ WIPHY_FLAG_DISABLE_BEACON_HINTS;
+
+ /*
+ * For now, disable PS by default because it affects
+ * RX performance significantly.
+ */
+ hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+ /* we create the 802.11 header and a zero-length SSID element */
+ hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
+
+ /* Default value; 4 EDCA QOS priorities */
+ hw->queues = 4;
+
+ hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+
+ if (priv->bands[IEEE80211_BAND_2GHZ].n_channels)
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &priv->bands[IEEE80211_BAND_2GHZ];
+ if (priv->bands[IEEE80211_BAND_5GHZ].n_channels)
+ priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &priv->bands[IEEE80211_BAND_5GHZ];
+
+ ret = ieee80211_register_hw(priv->hw);
+ if (ret) {
+ IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
+ return ret;
+ }
+ priv->mac80211_registered = 1;
+
+ return 0;
+}
+
+
static int iwl_mac_start(struct ieee80211_hw *hw)
{
struct iwl_priv *priv = hw->priv;
@@ -2328,6 +2544,8 @@ static int iwl_mac_start(struct ieee80211_hw *hw)
}
}
+ iwl_led_start(priv);
+
out:
priv->is_open = 1;
IWL_DEBUG_MAC80211(priv, "leave\n");
@@ -2404,6 +2622,10 @@ void iwl_config_ap(struct iwl_priv *priv)
IWL_WARN(priv, "REPLY_RXON_TIMING failed - "
"Attempting to continue.\n");
+ /* AP has all antennas */
+ priv->chain_noise_data.active_chains =
+ priv->hw_params.valid_rx_ant;
+ iwl_set_rxon_ht(priv, &priv->current_ht_config);
if (priv->cfg->ops->hcmd->set_rxon_chain)
priv->cfg->ops->hcmd->set_rxon_chain(priv);
@@ -2432,10 +2654,11 @@ void iwl_config_ap(struct iwl_priv *priv)
/* restore RXON assoc */
priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
iwlcore_commit_rxon(priv);
+ iwl_reset_qos(priv);
spin_lock_irqsave(&priv->lock, flags);
iwl_activate_qos(priv, 1);
spin_unlock_irqrestore(&priv->lock, flags);
- iwl_rxon_add_station(priv, iwl_bcast_addr, 0);
+ iwl_add_bcast_station(priv);
}
iwl_send_beacon_cmd(priv);
@@ -2527,6 +2750,7 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn)
{
@@ -2580,6 +2804,45 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw,
return 0;
}
+static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_priv *priv = hw->priv;
+ struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
+ int sta_id;
+
+ /*
+ * TODO: We really should use this callback to
+ * actually maintain the station table in
+ * the device.
+ */
+
+ switch (cmd) {
+ case STA_NOTIFY_ADD:
+ atomic_set(&sta_priv->pending_frames, 0);
+ if (vif->type == NL80211_IFTYPE_AP)
+ sta_priv->client = true;
+ break;
+ case STA_NOTIFY_SLEEP:
+ WARN_ON(!sta_priv->client);
+ sta_priv->asleep = true;
+ if (atomic_read(&sta_priv->pending_frames) > 0)
+ ieee80211_sta_block_awake(hw, sta, true);
+ break;
+ case STA_NOTIFY_AWAKE:
+ WARN_ON(!sta_priv->client);
+ sta_priv->asleep = false;
+ sta_id = iwl_find_station(priv, sta->addr);
+ if (sta_id != IWL_INVALID_STATION)
+ iwl_sta_modify_ps_wake(priv, sta_id);
+ break;
+ default:
+ break;
+ }
+}
+
/*****************************************************************************
*
* sysfs attributes
@@ -2774,7 +3037,7 @@ static ssize_t show_statistics(struct device *d,
return -EAGAIN;
mutex_lock(&priv->mutex);
- rc = iwl_send_statistics_request(priv, 0);
+ rc = iwl_send_statistics_request(priv, CMD_SYNC, false);
mutex_unlock(&priv->mutex);
if (rc) {
@@ -2799,6 +3062,40 @@ static ssize_t show_statistics(struct device *d,
static DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL);
+static ssize_t show_rts_ht_protection(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct iwl_priv *priv = dev_get_drvdata(d);
+
+ return sprintf(buf, "%s\n",
+ priv->cfg->use_rts_for_ht ? "RTS/CTS" : "CTS-to-self");
+}
+
+static ssize_t store_rts_ht_protection(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iwl_priv *priv = dev_get_drvdata(d);
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ IWL_INFO(priv, "Input is not in decimal form.\n");
+ else {
+ if (!iwl_is_associated(priv))
+ priv->cfg->use_rts_for_ht = val ? true : false;
+ else
+ IWL_ERR(priv, "Sta associated with AP - "
+ "Change protection mechanism is not allowed\n");
+ ret = count;
+ }
+ return ret;
+}
+
+static DEVICE_ATTR(rts_ht_protection, S_IWUSR | S_IRUGO,
+ show_rts_ht_protection, store_rts_ht_protection);
+
/*****************************************************************************
*
@@ -2849,12 +3146,103 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
del_timer_sync(&priv->statistics_periodic);
}
+static void iwl_init_hw_rates(struct iwl_priv *priv,
+ struct ieee80211_rate *rates)
+{
+ int i;
+
+ for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) {
+ rates[i].bitrate = iwl_rates[i].ieee * 5;
+ rates[i].hw_value = i; /* Rate scaling will work on indexes */
+ rates[i].hw_value_short = i;
+ rates[i].flags = 0;
+ if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) {
+ /*
+ * If CCK != 1M then set short preamble rate flag.
+ */
+ rates[i].flags |=
+ (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ?
+ 0 : IEEE80211_RATE_SHORT_PREAMBLE;
+ }
+ }
+}
+
+static int iwl_init_drv(struct iwl_priv *priv)
+{
+ int ret;
+
+ priv->ibss_beacon = NULL;
+
+ spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->sta_lock);
+ spin_lock_init(&priv->hcmd_lock);
+
+ INIT_LIST_HEAD(&priv->free_frames);
+
+ mutex_init(&priv->mutex);
+
+ /* Clear the driver's (not device's) station table */
+ iwl_clear_stations_table(priv);
+
+ priv->ieee_channels = NULL;
+ priv->ieee_rates = NULL;
+ priv->band = IEEE80211_BAND_2GHZ;
+
+ priv->iw_mode = NL80211_IFTYPE_STATION;
+
+ /* Choose which receivers/antennas to use */
+ if (priv->cfg->ops->hcmd->set_rxon_chain)
+ priv->cfg->ops->hcmd->set_rxon_chain(priv);
+
+ iwl_init_scan_params(priv);
+
+ iwl_reset_qos(priv);
+
+ priv->qos_data.qos_active = 0;
+ priv->qos_data.qos_cap.val = 0;
+
+ priv->rates_mask = IWL_RATES_MASK;
+ /* Set the tx_power_user_lmt to the lowest power level
+ * this value will get overwritten by channel max power avg
+ * from eeprom */
+ priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN;
+
+ ret = iwl_init_channel_map(priv);
+ if (ret) {
+ IWL_ERR(priv, "initializing regulatory failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = iwlcore_init_geos(priv);
+ if (ret) {
+ IWL_ERR(priv, "initializing geos failed: %d\n", ret);
+ goto err_free_channel_map;
+ }
+ iwl_init_hw_rates(priv, priv->ieee_rates);
+
+ return 0;
+
+err_free_channel_map:
+ iwl_free_channel_map(priv);
+err:
+ return ret;
+}
+
+static void iwl_uninit_drv(struct iwl_priv *priv)
+{
+ iwl_calib_free_results(priv);
+ iwlcore_free_geos(priv);
+ iwl_free_channel_map(priv);
+ kfree(priv->scan);
+}
+
static struct attribute *iwl_sysfs_entries[] = {
&dev_attr_flags.attr,
&dev_attr_filter_flags.attr,
&dev_attr_statistics.attr,
&dev_attr_temperature.attr,
&dev_attr_tx_power.attr,
+ &dev_attr_rts_ht_protection.attr,
#ifdef CONFIG_IWLWIFI_DEBUG
&dev_attr_debug_level.attr,
#endif
@@ -2882,7 +3270,8 @@ static struct ieee80211_ops iwl_hw_ops = {
.reset_tsf = iwl_mac_reset_tsf,
.bss_info_changed = iwl_bss_info_changed,
.ampdu_action = iwl_mac_ampdu_action,
- .hw_scan = iwl_mac_hw_scan
+ .hw_scan = iwl_mac_hw_scan,
+ .sta_notify = iwl_mac_sta_notify,
};
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -2990,12 +3379,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_iounmap;
}
- /* amp init */
- err = priv->cfg->ops->lib->apm_ops.init(priv);
- if (err < 0) {
- IWL_ERR(priv, "Failed to init APMG\n");
- goto out_iounmap;
- }
/*****************
* 4. Read EEPROM
*****************/
@@ -3141,6 +3524,15 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
iwl_down(priv);
}
+ /*
+ * Make sure device is reset to low power before unloading driver.
+ * This may be redundant with iwl_down(), but there are paths to
+ * run iwl_down() without calling apm_ops.stop(), and there are
+ * paths to avoid running iwl_down() at all before leaving driver.
+ * This (inexpensive) call *makes sure* device is reset.
+ */
+ priv->cfg->ops->lib->apm_ops.stop(priv);
+
iwl_tt_exit(priv);
/* make sure we flush any pending irq or
@@ -3203,37 +3595,97 @@ static struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)},
#endif /* CONFIG_IWL4965 */
#ifdef CONFIG_IWL5000
- {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)},
- {IWL_PCI_DEVICE(0x4232, PCI_ANY_ID, iwl5100_agn_cfg)},
- {IWL_PCI_DEVICE(0x4235, PCI_ANY_ID, iwl5300_agn_cfg)},
- {IWL_PCI_DEVICE(0x4236, PCI_ANY_ID, iwl5300_agn_cfg)},
- {IWL_PCI_DEVICE(0x4237, PCI_ANY_ID, iwl5100_agn_cfg)},
-/* 5350 WiFi/WiMax */
- {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)},
- {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)},
- {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)},
-/* 5150 Wifi/WiMax */
- {IWL_PCI_DEVICE(0x423C, PCI_ANY_ID, iwl5150_agn_cfg)},
- {IWL_PCI_DEVICE(0x423D, PCI_ANY_ID, iwl5150_agn_cfg)},
-/* 6000/6050 Series */
- {IWL_PCI_DEVICE(0x008D, PCI_ANY_ID, iwl6000h_2agn_cfg)},
- {IWL_PCI_DEVICE(0x008E, PCI_ANY_ID, iwl6000h_2agn_cfg)},
- {IWL_PCI_DEVICE(0x422B, PCI_ANY_ID, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x4238, PCI_ANY_ID, iwl6000_3agn_cfg)},
- {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000i_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0086, PCI_ANY_ID, iwl6050_3agn_cfg)},
- {IWL_PCI_DEVICE(0x0087, PCI_ANY_ID, iwl6050_2agn_cfg)},
- {IWL_PCI_DEVICE(0x0088, PCI_ANY_ID, iwl6050_3agn_cfg)},
- {IWL_PCI_DEVICE(0x0089, PCI_ANY_ID, iwl6050_2agn_cfg)},
+/* 5100 Series WiFi */
+ {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */
+
+/* 5300 Series WiFi */
+ {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */
+
+/* 5350 Series WiFi/WiMax */
+ {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */
+
+/* 5150 Series Wifi/WiMax */
+ {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
+
+ {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */
+ {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */
+
+/* 6x00 Series */
+ {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)},
+
+/* 6x50 WiFi/WiMax Series */
+ {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
+ {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
+
/* 1000 Series WiFi */
- {IWL_PCI_DEVICE(0x0083, PCI_ANY_ID, iwl1000_bgn_cfg)},
- {IWL_PCI_DEVICE(0x0084, PCI_ANY_ID, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)},
+ {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)},
#endif /* CONFIG_IWL5000 */
{0}
@@ -3288,9 +3740,9 @@ module_exit(iwl_exit);
module_init(iwl_init);
#ifdef CONFIG_IWLWIFI_DEBUG
-module_param_named(debug50, iwl_debug_level, uint, 0444);
+module_param_named(debug50, iwl_debug_level, uint, S_IRUGO);
MODULE_PARM_DESC(debug50, "50XX debug output mask (deprecated)");
-module_param_named(debug, iwl_debug_level, uint, 0644);
+module_param_named(debug, iwl_debug_level, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "debug output mask");
#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c
index c4b565a2de9..95a57b36a7e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-calib.c
+++ b/drivers/net/wireless/iwlwifi/iwl-calib.c
@@ -132,6 +132,7 @@ void iwl_calib_free_results(struct iwl_priv *priv)
priv->calib_results[i].buf_len = 0;
}
}
+EXPORT_SYMBOL(iwl_calib_free_results);
/*****************************************************************************
* RUNTIME calibrations framework
@@ -447,11 +448,11 @@ static int iwl_sensitivity_write(struct iwl_priv *priv)
cpu_to_le16((u16)data->nrg_th_ofdm);
cmd.table[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
- cpu_to_le16(190);
+ cpu_to_le16(data->barker_corr_th_min);
cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
- cpu_to_le16(390);
+ cpu_to_le16(data->barker_corr_th_min_mrc);
cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] =
- cpu_to_le16(62);
+ cpu_to_le16(data->nrg_th_cca);
IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
@@ -516,7 +517,7 @@ void iwl_init_sensitivity(struct iwl_priv *priv)
for (i = 0; i < NRG_NUM_PREV_STAT_L; i++)
data->nrg_silence_rssi[i] = 0;
- data->auto_corr_ofdm = 90;
+ data->auto_corr_ofdm = ranges->auto_corr_min_ofdm;
data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc;
data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1;
data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1;
@@ -524,6 +525,9 @@ void iwl_init_sensitivity(struct iwl_priv *priv)
data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc;
data->nrg_th_cck = ranges->nrg_th_cck;
data->nrg_th_ofdm = ranges->nrg_th_ofdm;
+ data->barker_corr_th_min = ranges->barker_corr_th_min;
+ data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc;
+ data->nrg_th_cca = ranges->nrg_th_cca;
data->last_bad_plcp_cnt_ofdm = 0;
data->last_fa_cnt_ofdm = 0;
@@ -643,6 +647,15 @@ void iwl_sensitivity_calibration(struct iwl_priv *priv,
}
EXPORT_SYMBOL(iwl_sensitivity_calibration);
+static inline u8 find_first_chain(u8 mask)
+{
+ if (mask & ANT_A)
+ return CHAIN_A;
+ if (mask & ANT_B)
+ return CHAIN_B;
+ return CHAIN_C;
+}
+
/*
* Accumulate 20 beacons of signal and noise statistics for each of
* 3 receivers/antennas/rx-chains, then figure out:
@@ -675,14 +688,17 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
u8 num_tx_chains;
unsigned long flags;
struct statistics_rx_non_phy *rx_info = &(stat_resp->rx.general);
+ u8 first_chain;
if (priv->disable_chain_noise_cal)
return;
data = &(priv->chain_noise_data);
- /* Accumulate just the first 20 beacons after the first association,
- * then we're done forever. */
+ /*
+ * Accumulate just the first "chain_noise_num_beacons" after
+ * the first association, then we're done forever.
+ */
if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) {
if (data->state == IWL_CHAIN_NOISE_ALIVE)
IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n");
@@ -710,7 +726,10 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
return;
}
- /* Accumulate beacon statistics values across 20 beacons */
+ /*
+ * Accumulate beacon statistics values across
+ * "chain_noise_num_beacons"
+ */
chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) &
IN_BAND_FILTER;
chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) &
@@ -741,16 +760,19 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n",
chain_noise_a, chain_noise_b, chain_noise_c);
- /* If this is the 20th beacon, determine:
+ /* If this is the "chain_noise_num_beacons", determine:
* 1) Disconnected antennas (using signal strengths)
* 2) Differential gain (using silence noise) to balance receivers */
- if (data->beacon_count != CAL_NUM_OF_BEACONS)
+ if (data->beacon_count != priv->cfg->chain_noise_num_beacons)
return;
/* Analyze signal for disconnected antenna */
- average_sig[0] = (data->chain_signal_a) / CAL_NUM_OF_BEACONS;
- average_sig[1] = (data->chain_signal_b) / CAL_NUM_OF_BEACONS;
- average_sig[2] = (data->chain_signal_c) / CAL_NUM_OF_BEACONS;
+ average_sig[0] =
+ (data->chain_signal_a) / priv->cfg->chain_noise_num_beacons;
+ average_sig[1] =
+ (data->chain_signal_b) / priv->cfg->chain_noise_num_beacons;
+ average_sig[2] =
+ (data->chain_signal_c) / priv->cfg->chain_noise_num_beacons;
if (average_sig[0] >= average_sig[1]) {
max_average_sig = average_sig[0];
@@ -803,13 +825,17 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
/* there is a Tx antenna connected */
break;
if (num_tx_chains == priv->hw_params.tx_chains_num &&
- data->disconn_array[i]) {
- /* This is the last TX antenna and is also
- * disconnected connect it anyway */
- data->disconn_array[i] = 0;
- active_chains |= ant_msk;
- IWL_DEBUG_CALIB(priv, "All Tx chains are disconnected W/A - "
- "declare %d as connected\n", i);
+ data->disconn_array[i]) {
+ /*
+ * If all chains are disconnected
+ * connect the first valid tx chain
+ */
+ first_chain =
+ find_first_chain(priv->cfg->valid_tx_ant);
+ data->disconn_array[first_chain] = 0;
+ active_chains |= BIT(first_chain);
+ IWL_DEBUG_CALIB(priv, "All Tx chains are disconnected W/A - declare %d as connected\n",
+ first_chain);
break;
}
}
@@ -820,9 +846,12 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
active_chains);
/* Analyze noise for rx balance */
- average_noise[0] = ((data->chain_noise_a)/CAL_NUM_OF_BEACONS);
- average_noise[1] = ((data->chain_noise_b)/CAL_NUM_OF_BEACONS);
- average_noise[2] = ((data->chain_noise_c)/CAL_NUM_OF_BEACONS);
+ average_noise[0] =
+ ((data->chain_noise_a) / priv->cfg->chain_noise_num_beacons);
+ average_noise[1] =
+ ((data->chain_noise_b) / priv->cfg->chain_noise_num_beacons);
+ average_noise[2] =
+ ((data->chain_noise_c) / priv->cfg->chain_noise_num_beacons);
for (i = 0; i < NUM_RX_CHAINS; i++) {
if (!(data->disconn_array[i]) &&
@@ -843,7 +872,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv,
if (priv->cfg->ops->utils->gain_computation)
priv->cfg->ops->utils->gain_computation(priv, average_noise,
- min_average_noise_antenna_i, min_average_noise);
+ min_average_noise_antenna_i, min_average_noise,
+ find_first_chain(priv->cfg->valid_rx_ant));
/* Some power changes may have been made during the calibration.
* Update and commit the RXON
@@ -870,7 +900,7 @@ void iwl_reset_run_time_calib(struct iwl_priv *priv)
/* Ask for statistics now, the uCode will send notification
* periodically after association */
- iwl_send_statistics_request(priv, CMD_ASYNC);
+ iwl_send_statistics_request(priv, CMD_ASYNC, true);
}
EXPORT_SYMBOL(iwl_reset_run_time_calib);
diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h
index 4afaf773aea..e9150753192 100644
--- a/drivers/net/wireless/iwlwifi/iwl-commands.h
+++ b/drivers/net/wireless/iwlwifi/iwl-commands.h
@@ -109,11 +109,12 @@ enum {
REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* 4965 only */
/* WiMAX coexistence */
- COEX_PRIORITY_TABLE_CMD = 0x5a, /*5000 only */
+ COEX_PRIORITY_TABLE_CMD = 0x5a, /* for 5000 series and up */
COEX_MEDIUM_NOTIFICATION = 0x5b,
COEX_EVENT_CMD = 0x5c,
/* Calibration */
+ TEMPERATURE_NOTIFICATION = 0x62,
CALIBRATION_CFG_CMD = 0x65,
CALIBRATION_RES_NOTIFICATION = 0x66,
CALIBRATION_COMPLETE_NOTIFICATION = 0x67,
@@ -148,7 +149,7 @@ enum {
QUIET_NOTIFICATION = 0x96, /* not used */
REPLY_TX_PWR_TABLE_CMD = 0x97,
REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */
- TX_ANT_CONFIGURATION_CMD = 0x98, /* not used */
+ TX_ANT_CONFIGURATION_CMD = 0x98,
MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */
/* Bluetooth device coexistence config command */
@@ -353,6 +354,9 @@ struct iwl3945_power_per_rate {
#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32
#define POWER_TABLE_CCK_ENTRY 32
+#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24
+#define IWL_PWR_CCK_ENTRIES 2
+
/**
* union iwl4965_tx_power_dual_stream
*
@@ -411,6 +415,16 @@ struct iwl5000_tx_power_dbm_cmd {
u8 reserved;
} __attribute__ ((packed));
+/**
+ * Command TX_ANT_CONFIGURATION_CMD = 0x98
+ * This command is used to configure valid Tx antenna.
+ * By default uCode concludes the valid antenna according to the radio flavor.
+ * This command enables the driver to override/modify this conclusion.
+ */
+struct iwl_tx_ant_config_cmd {
+ __le32 valid;
+} __attribute__ ((packed));
+
/******************************************************************************
* (0a)
* Alive and Error Commands & Responses:
@@ -793,7 +807,7 @@ struct iwl3945_channel_switch_cmd {
struct iwl3945_power_per_rate power[IWL_MAX_RATES];
} __attribute__ ((packed));
-struct iwl_channel_switch_cmd {
+struct iwl4965_channel_switch_cmd {
u8 band;
u8 expect_beacon;
__le16 channel;
@@ -803,6 +817,48 @@ struct iwl_channel_switch_cmd {
struct iwl4965_tx_power_db tx_power;
} __attribute__ ((packed));
+/**
+ * struct iwl5000_channel_switch_cmd
+ * @band: 0- 5.2GHz, 1- 2.4GHz
+ * @expect_beacon: 0- resume transmits after channel switch
+ * 1- wait for beacon to resume transmits
+ * @channel: new channel number
+ * @rxon_flags: Rx on flags
+ * @rxon_filter_flags: filtering parameters
+ * @switch_time: switch time in extended beacon format
+ * @reserved: reserved bytes
+ */
+struct iwl5000_channel_switch_cmd {
+ u8 band;
+ u8 expect_beacon;
+ __le16 channel;
+ __le32 rxon_flags;
+ __le32 rxon_filter_flags;
+ __le32 switch_time;
+ __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES];
+} __attribute__ ((packed));
+
+/**
+ * struct iwl6000_channel_switch_cmd
+ * @band: 0- 5.2GHz, 1- 2.4GHz
+ * @expect_beacon: 0- resume transmits after channel switch
+ * 1- wait for beacon to resume transmits
+ * @channel: new channel number
+ * @rxon_flags: Rx on flags
+ * @rxon_filter_flags: filtering parameters
+ * @switch_time: switch time in extended beacon format
+ * @reserved: reserved bytes
+ */
+struct iwl6000_channel_switch_cmd {
+ u8 band;
+ u8 expect_beacon;
+ __le16 channel;
+ __le32 rxon_flags;
+ __le32 rxon_filter_flags;
+ __le32 switch_time;
+ __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES];
+} __attribute__ ((packed));
+
/*
* CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command)
*/
@@ -921,6 +977,7 @@ struct iwl_qosparam_cmd {
#define STA_MODIFY_TX_RATE_MSK 0x04
#define STA_MODIFY_ADDBA_TID_MSK 0x08
#define STA_MODIFY_DELBA_TID_MSK 0x10
+#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20
/* Receiver address (actually, Rx station's index into station table),
* combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
@@ -1051,7 +1108,14 @@ struct iwl4965_addsta_cmd {
* Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */
__le16 add_immediate_ba_ssn;
- __le32 reserved2;
+ /*
+ * Number of packets OK to transmit to station even though
+ * it is asleep -- used to synchronise PS-poll and u-APSD
+ * responses while ucode keeps track of STA sleep state.
+ */
+ __le16 sleep_tx_count;
+
+ __le16 reserved2;
} __attribute__ ((packed));
/* 5000 */
@@ -1082,7 +1146,14 @@ struct iwl_addsta_cmd {
* Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */
__le16 add_immediate_ba_ssn;
- __le32 reserved2;
+ /*
+ * Number of packets OK to transmit to station even though
+ * it is asleep -- used to synchronise PS-poll and u-APSD
+ * responses while ucode keeps track of STA sleep state.
+ */
+ __le16 sleep_tx_count;
+
+ __le16 reserved2;
} __attribute__ ((packed));
@@ -1634,6 +1705,21 @@ enum {
TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */
};
+static inline u32 iwl_tx_status_to_mac80211(u32 status)
+{
+ status &= TX_STATUS_MSK;
+
+ switch (status) {
+ case TX_STATUS_SUCCESS:
+ case TX_STATUS_DIRECT_DONE:
+ return IEEE80211_TX_STAT_ACK;
+ case TX_STATUS_FAIL_DEST_PS:
+ return IEEE80211_TX_STAT_TX_FILTERED;
+ default:
+ return 0;
+ }
+}
+
static inline bool iwl_is_tx_success(u32 status)
{
status &= TX_STATUS_MSK;
@@ -2162,6 +2248,19 @@ struct iwl_link_quality_cmd {
__le32 reserved2;
} __attribute__ ((packed));
+#define BT_COEX_DISABLE (0x0)
+#define BT_COEX_MODE_2W (0x1)
+#define BT_COEX_MODE_3W (0x2)
+#define BT_COEX_MODE_4W (0x3)
+
+#define BT_LEAD_TIME_MIN (0x0)
+#define BT_LEAD_TIME_DEF (0x1E)
+#define BT_LEAD_TIME_MAX (0xFF)
+
+#define BT_MAX_KILL_MIN (0x1)
+#define BT_MAX_KILL_DEF (0x5)
+#define BT_MAX_KILL_MAX (0xFF)
+
/*
* REPLY_BT_CONFIG = 0x9b (command, has simple generic response)
*
@@ -2497,9 +2596,10 @@ struct iwl_scan_channel {
/**
* struct iwl_ssid_ie - directed scan network information element
*
- * Up to 4 of these may appear in REPLY_SCAN_CMD, selected by "type" field
- * in struct iwl_scan_channel; each channel may select different ssids from
- * among the 4 entries. SSID IEs get transmitted in reverse order of entry.
+ * Up to 20 of these may appear in REPLY_SCAN_CMD (Note: Only 4 are in
+ * 3945 SCAN api), selected by "type" bit field in struct iwl_scan_channel;
+ * each channel may select different ssids from among the 20 (4) entries.
+ * SSID IEs get transmitted in reverse order of entry.
*/
struct iwl_ssid_ie {
u8 id;
@@ -3001,6 +3101,10 @@ struct statistics_general {
__le32 reserved3;
} __attribute__ ((packed));
+#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0)
+#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1)
+#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2)
+
/*
* REPLY_STATISTICS_CMD = 0x9c,
* 3945 and 4965 identical.
@@ -3237,12 +3341,6 @@ struct iwl_missed_beacon_notif {
* Lower values mean higher energy; this means making sure that the value
* in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy".
*
- * Driver should set the following entries to fixed values:
- *
- * HD_MIN_ENERGY_OFDM_DET_INDEX 100
- * HD_BARKER_CORR_TH_ADD_MIN_INDEX 190
- * HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX 390
- * HD_OFDM_ENERGY_TH_IN_INDEX 62
*/
/*
@@ -3440,30 +3538,134 @@ struct iwl_led_cmd {
} __attribute__ ((packed));
/*
- * Coexistence WIFI/WIMAX Command
- * COEX_PRIORITY_TABLE_CMD = 0x5a
- *
+ * station priority table entries
+ * also used as potential "events" value for both
+ * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD
+ */
+
+/*
+ * COEX events entry flag masks
+ * RP - Requested Priority
+ * WP - Win Medium Priority: priority assigned when the contention has been won
+ */
+#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1)
+#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2)
+#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4)
+
+#define COEX_CU_UNASSOC_IDLE_RP 4
+#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4
+#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4
+#define COEX_CU_CALIBRATION_RP 4
+#define COEX_CU_PERIODIC_CALIBRATION_RP 4
+#define COEX_CU_CONNECTION_ESTAB_RP 4
+#define COEX_CU_ASSOCIATED_IDLE_RP 4
+#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4
+#define COEX_CU_ASSOC_AUTO_SCAN_RP 4
+#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4
+#define COEX_CU_RF_ON_RP 6
+#define COEX_CU_RF_OFF_RP 4
+#define COEX_CU_STAND_ALONE_DEBUG_RP 6
+#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4
+#define COEX_CU_RSRVD1_RP 4
+#define COEX_CU_RSRVD2_RP 4
+
+#define COEX_CU_UNASSOC_IDLE_WP 3
+#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3
+#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3
+#define COEX_CU_CALIBRATION_WP 3
+#define COEX_CU_PERIODIC_CALIBRATION_WP 3
+#define COEX_CU_CONNECTION_ESTAB_WP 3
+#define COEX_CU_ASSOCIATED_IDLE_WP 3
+#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3
+#define COEX_CU_ASSOC_AUTO_SCAN_WP 3
+#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3
+#define COEX_CU_RF_ON_WP 3
+#define COEX_CU_RF_OFF_WP 3
+#define COEX_CU_STAND_ALONE_DEBUG_WP 6
+#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3
+#define COEX_CU_RSRVD1_WP 3
+#define COEX_CU_RSRVD2_WP 3
+
+#define COEX_UNASSOC_IDLE_FLAGS 0
+#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_UNASSOC_AUTO_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_CALIBRATION_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_PERIODIC_CALIBRATION_FLAGS 0
+/*
+ * COEX_CONNECTION_ESTAB:
+ * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network.
+ */
+#define COEX_CONNECTION_ESTAB_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
+ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
+#define COEX_ASSOCIATED_IDLE_FLAGS 0
+#define COEX_ASSOC_MANUAL_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_ASSOC_AUTO_SCAN_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0
+#define COEX_RF_ON_FLAGS 0
+#define COEX_RF_OFF_FLAGS 0
+#define COEX_STAND_ALONE_DEBUG_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG)
+#define COEX_IPAN_ASSOC_LEVEL_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
+ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
+#define COEX_RSRVD1_FLAGS 0
+#define COEX_RSRVD2_FLAGS 0
+/*
+ * COEX_CU_RF_ON is the event wrapping all radio ownership.
+ * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network.
*/
+#define COEX_CU_RF_ON_FLAGS \
+ (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \
+ COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \
+ COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG)
+
+
enum {
+ /* un-association part */
COEX_UNASSOC_IDLE = 0,
COEX_UNASSOC_MANUAL_SCAN = 1,
COEX_UNASSOC_AUTO_SCAN = 2,
+ /* calibration */
COEX_CALIBRATION = 3,
COEX_PERIODIC_CALIBRATION = 4,
+ /* connection */
COEX_CONNECTION_ESTAB = 5,
+ /* association part */
COEX_ASSOCIATED_IDLE = 6,
COEX_ASSOC_MANUAL_SCAN = 7,
COEX_ASSOC_AUTO_SCAN = 8,
COEX_ASSOC_ACTIVE_LEVEL = 9,
+ /* RF ON/OFF */
COEX_RF_ON = 10,
COEX_RF_OFF = 11,
COEX_STAND_ALONE_DEBUG = 12,
+ /* IPAN */
COEX_IPAN_ASSOC_LEVEL = 13,
+ /* reserved */
COEX_RSRVD1 = 14,
COEX_RSRVD2 = 15,
COEX_NUM_OF_EVENTS = 16
};
+/*
+ * Coexistence WIFI/WIMAX Command
+ * COEX_PRIORITY_TABLE_CMD = 0x5a
+ *
+ */
struct iwl_wimax_coex_event_entry {
u8 request_prio;
u8 win_medium_prio;
@@ -3488,6 +3690,55 @@ struct iwl_wimax_coex_cmd {
struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS];
} __attribute__ ((packed));
+/*
+ * Coexistence MEDIUM NOTIFICATION
+ * COEX_MEDIUM_NOTIFICATION = 0x5b
+ *
+ * notification from uCode to host to indicate medium changes
+ *
+ */
+/*
+ * status field
+ * bit 0 - 2: medium status
+ * bit 3: medium change indication
+ * bit 4 - 31: reserved
+ */
+/* status option values, (0 - 2 bits) */
+#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */
+#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */
+#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */
+#define COEX_MEDIUM_MSK (0x7)
+
+/* send notification status (1 bit) */
+#define COEX_MEDIUM_CHANGED (0x8)
+#define COEX_MEDIUM_CHANGED_MSK (0x8)
+#define COEX_MEDIUM_SHIFT (3)
+
+struct iwl_coex_medium_notification {
+ __le32 status;
+ __le32 events;
+} __attribute__ ((packed));
+
+/*
+ * Coexistence EVENT Command
+ * COEX_EVENT_CMD = 0x5c
+ *
+ * send from host to uCode for coex event request.
+ */
+/* flags options */
+#define COEX_EVENT_REQUEST_MSK (0x1)
+
+struct iwl_coex_event_cmd {
+ u8 flags;
+ u8 event;
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct iwl_coex_event_resp {
+ __le32 status;
+} __attribute__ ((packed));
+
+
/******************************************************************************
* (13)
* Union of all expected notifications/responses:
@@ -3495,6 +3746,16 @@ struct iwl_wimax_coex_cmd {
*****************************************************************************/
struct iwl_rx_packet {
+ /*
+ * The first 4 bytes of the RX frame header contain both the RX frame
+ * size and some flags.
+ * Bit fields:
+ * 31: flag flush RB request
+ * 30: flag ignore TC (terminal counter) request
+ * 29: flag fast IRQ request
+ * 28-14: Reserved
+ * 13-00: RX frame size
+ */
__le32 len_n_flags;
struct iwl_cmd_header hdr;
union {
@@ -3514,6 +3775,8 @@ struct iwl_rx_packet {
struct iwl_notif_statistics stats;
struct iwl_compressed_ba_resp compressed_ba;
struct iwl_missed_beacon_notif missed_beacon;
+ struct iwl_coex_medium_notification coex_medium_notif;
+ struct iwl_coex_event_resp coex_event;
__le32 status;
u8 raw[0];
} u;
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 2dc92875545..574d3665870 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -47,6 +47,37 @@ MODULE_VERSION(IWLWIFI_VERSION);
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
+static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = {
+ {COEX_CU_UNASSOC_IDLE_RP, COEX_CU_UNASSOC_IDLE_WP,
+ 0, COEX_UNASSOC_IDLE_FLAGS},
+ {COEX_CU_UNASSOC_MANUAL_SCAN_RP, COEX_CU_UNASSOC_MANUAL_SCAN_WP,
+ 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
+ {COEX_CU_UNASSOC_AUTO_SCAN_RP, COEX_CU_UNASSOC_AUTO_SCAN_WP,
+ 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
+ {COEX_CU_CALIBRATION_RP, COEX_CU_CALIBRATION_WP,
+ 0, COEX_CALIBRATION_FLAGS},
+ {COEX_CU_PERIODIC_CALIBRATION_RP, COEX_CU_PERIODIC_CALIBRATION_WP,
+ 0, COEX_PERIODIC_CALIBRATION_FLAGS},
+ {COEX_CU_CONNECTION_ESTAB_RP, COEX_CU_CONNECTION_ESTAB_WP,
+ 0, COEX_CONNECTION_ESTAB_FLAGS},
+ {COEX_CU_ASSOCIATED_IDLE_RP, COEX_CU_ASSOCIATED_IDLE_WP,
+ 0, COEX_ASSOCIATED_IDLE_FLAGS},
+ {COEX_CU_ASSOC_MANUAL_SCAN_RP, COEX_CU_ASSOC_MANUAL_SCAN_WP,
+ 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
+ {COEX_CU_ASSOC_AUTO_SCAN_RP, COEX_CU_ASSOC_AUTO_SCAN_WP,
+ 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
+ {COEX_CU_ASSOC_ACTIVE_LEVEL_RP, COEX_CU_ASSOC_ACTIVE_LEVEL_WP,
+ 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
+ {COEX_CU_RF_ON_RP, COEX_CU_RF_ON_WP, 0, COEX_CU_RF_ON_FLAGS},
+ {COEX_CU_RF_OFF_RP, COEX_CU_RF_OFF_WP, 0, COEX_RF_OFF_FLAGS},
+ {COEX_CU_STAND_ALONE_DEBUG_RP, COEX_CU_STAND_ALONE_DEBUG_WP,
+ 0, COEX_STAND_ALONE_DEBUG_FLAGS},
+ {COEX_CU_IPAN_ASSOC_LEVEL_RP, COEX_CU_IPAN_ASSOC_LEVEL_WP,
+ 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
+ {COEX_CU_RSRVD1_RP, COEX_CU_RSRVD1_WP, 0, COEX_RSRVD1_FLAGS},
+ {COEX_CU_RSRVD2_RP, COEX_CU_RSRVD2_WP, 0, COEX_RSRVD2_FLAGS}
+};
+
#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \
[IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \
IWL_RATE_SISO_##s##M_PLCP, \
@@ -178,6 +209,7 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant)
}
return ant;
}
+EXPORT_SYMBOL(iwl_toggle_tx_ant);
const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
EXPORT_SYMBOL(iwl_bcast_addr);
@@ -224,7 +256,10 @@ int iwl_hw_nic_init(struct iwl_priv *priv)
/* nic_init */
spin_lock_irqsave(&priv->lock, flags);
priv->cfg->ops->lib->apm_ops.init(priv);
- iwl_write32(priv, CSR_INT_COALESCING, 512 / 32);
+
+ /* Set interrupt coalescing timer to 512 usecs */
+ iwl_write8(priv, CSR_INT_COALESCING, 512 / 32);
+
spin_unlock_irqrestore(&priv->lock, flags);
ret = priv->cfg->ops->lib->apm_ops.set_pwr_src(priv, IWL_PWR_SRC_VMAIN);
@@ -416,8 +451,7 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
ht_info->cap |= (IEEE80211_HT_CAP_SM_PS &
- (WLAN_HT_CAP_SM_PS_DISABLED << 2));
-
+ (priv->cfg->sm_ps_mode << 2));
max_bit_rate = MAX_BIT_RATE_20_MHZ;
if (priv->hw_params.ht40_channel & BIT(band)) {
ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -452,28 +486,6 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
}
}
-static void iwlcore_init_hw_rates(struct iwl_priv *priv,
- struct ieee80211_rate *rates)
-{
- int i;
-
- for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) {
- rates[i].bitrate = iwl_rates[i].ieee * 5;
- rates[i].hw_value = i; /* Rate scaling will work on indexes */
- rates[i].hw_value_short = i;
- rates[i].flags = 0;
- if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) {
- /*
- * If CCK != 1M then set short preamble rate flag.
- */
- rates[i].flags |=
- (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ?
- 0 : IEEE80211_RATE_SHORT_PREAMBLE;
- }
- }
-}
-
-
/**
* iwlcore_init_geos - Initialize mac80211's geo/channel info based from eeprom
*/
@@ -605,11 +617,27 @@ void iwlcore_free_geos(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwlcore_free_geos);
+/*
+ * iwlcore_rts_tx_cmd_flag: Set rts/cts. 3945 and 4965 only share this
+ * function.
+ */
+void iwlcore_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
+ __le32 *tx_flags)
+{
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ *tx_flags |= TX_CMD_FLG_RTS_MSK;
+ *tx_flags &= ~TX_CMD_FLG_CTS_MSK;
+ } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
+ *tx_flags &= ~TX_CMD_FLG_RTS_MSK;
+ *tx_flags |= TX_CMD_FLG_CTS_MSK;
+ }
+}
+EXPORT_SYMBOL(iwlcore_rts_tx_cmd_flag);
+
static bool is_single_rx_stream(struct iwl_priv *priv)
{
return !priv->current_ht_config.is_ht ||
- ((priv->current_ht_config.mcs.rx_mask[1] == 0) &&
- (priv->current_ht_config.mcs.rx_mask[2] == 0));
+ priv->current_ht_config.single_chain_sufficient;
}
static u8 iwl_is_channel_extension(struct iwl_priv *priv,
@@ -635,10 +663,9 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv,
u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
struct ieee80211_sta_ht_cap *sta_ht_inf)
{
- struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;
+ struct iwl_ht_config *ht_conf = &priv->current_ht_config;
- if ((!iwl_ht_conf->is_ht) ||
- (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ))
+ if (!ht_conf->is_ht || !ht_conf->is_40mhz)
return 0;
/* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
@@ -654,7 +681,7 @@ u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
#endif
return iwl_is_channel_extension(priv, priv->band,
le16_to_cpu(priv->staging_rxon.channel),
- iwl_ht_conf->extension_chan_offset);
+ ht_conf->extension_chan_offset);
}
EXPORT_SYMBOL(iwl_is_ht40_tx_allowed);
@@ -878,11 +905,11 @@ u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_rate_get_lowest_plcp);
-void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
+void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf)
{
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
- if (!ht_info->is_ht) {
+ if (!ht_conf->is_ht) {
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
RXON_FLG_HT40_PROT_MSK |
@@ -893,7 +920,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
/* FIXME: if the definition of ht_protection changed, the "translation"
* will be needed for rxon->flags
*/
- rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);
+ rxon->flags |= cpu_to_le32(ht_conf->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);
/* Set up channel bandwidth:
* 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */
@@ -902,10 +929,10 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
if (iwl_is_ht40_tx_allowed(priv, NULL)) {
/* pure ht40 */
- if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
+ if (ht_conf->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) {
rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
/* Note: control channel is opposite of extension channel */
- switch (ht_info->extension_chan_offset) {
+ switch (ht_conf->extension_chan_offset) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
rxon->flags &= ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
break;
@@ -915,7 +942,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
}
} else {
/* Note: control channel is opposite of extension channel */
- switch (ht_info->extension_chan_offset) {
+ switch (ht_conf->extension_chan_offset) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
@@ -938,14 +965,10 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
if (priv->cfg->ops->hcmd->set_rxon_chain)
priv->cfg->ops->hcmd->set_rxon_chain(priv);
- IWL_DEBUG_ASSOC(priv, "supported HT rate 0x%X 0x%X 0x%X "
- "rxon flags 0x%X operation mode :0x%X "
+ IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X "
"extension channel offset 0x%x\n",
- ht_info->mcs.rx_mask[0],
- ht_info->mcs.rx_mask[1],
- ht_info->mcs.rx_mask[2],
- le32_to_cpu(rxon->flags), ht_info->ht_protection,
- ht_info->extension_chan_offset);
+ le32_to_cpu(rxon->flags), ht_conf->ht_protection,
+ ht_conf->extension_chan_offset);
return;
}
EXPORT_SYMBOL(iwl_set_rxon_ht);
@@ -955,44 +978,50 @@ EXPORT_SYMBOL(iwl_set_rxon_ht);
#define IWL_NUM_IDLE_CHAINS_DUAL 2
#define IWL_NUM_IDLE_CHAINS_SINGLE 1
-/* Determine how many receiver/antenna chains to use.
- * More provides better reception via diversity. Fewer saves power.
+/*
+ * Determine how many receiver/antenna chains to use.
+ *
+ * More provides better reception via diversity. Fewer saves power
+ * at the expense of throughput, but only when not in powersave to
+ * start with.
+ *
* MIMO (dual stream) requires at least 2, but works better with 3.
* This does not determine *which* chains to use, just how many.
*/
static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
{
- bool is_single = is_single_rx_stream(priv);
- bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
-
/* # of Rx chains to use when expecting MIMO. */
- if (is_single || (!is_cam && (priv->current_ht_config.sm_ps ==
- WLAN_HT_CAP_SM_PS_STATIC)))
+ if (is_single_rx_stream(priv))
return IWL_NUM_RX_CHAINS_SINGLE;
else
return IWL_NUM_RX_CHAINS_MULTIPLE;
}
+/*
+ * When we are in power saving mode, unless device support spatial
+ * multiplexing power save, use the active count for rx chain count.
+ */
static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
{
- int idle_cnt;
+ int idle_cnt = active_cnt;
bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
+
/* # Rx chains when idling and maybe trying to save power */
- switch (priv->current_ht_config.sm_ps) {
+ switch (priv->cfg->sm_ps_mode) {
case WLAN_HT_CAP_SM_PS_STATIC:
+ idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE;
+ break;
case WLAN_HT_CAP_SM_PS_DYNAMIC:
idle_cnt = (is_cam) ? IWL_NUM_IDLE_CHAINS_DUAL :
- IWL_NUM_IDLE_CHAINS_SINGLE;
+ IWL_NUM_IDLE_CHAINS_SINGLE;
break;
case WLAN_HT_CAP_SM_PS_DISABLED:
- idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE;
break;
case WLAN_HT_CAP_SM_PS_INVALID:
default:
- IWL_ERR(priv, "invalid mimo ps mode %d\n",
- priv->current_ht_config.sm_ps);
+ IWL_ERR(priv, "invalid sm_ps mode %u\n",
+ priv->cfg->sm_ps_mode);
WARN_ON(1);
- idle_cnt = -1;
break;
}
return idle_cnt;
@@ -1005,7 +1034,7 @@ static u8 iwl_count_chain_bitmap(u32 chain_bitmap)
res = (chain_bitmap & BIT(0)) >> 0;
res += (chain_bitmap & BIT(1)) >> 1;
res += (chain_bitmap & BIT(2)) >> 2;
- res += (chain_bitmap & BIT(4)) >> 4;
+ res += (chain_bitmap & BIT(3)) >> 3;
return res;
}
@@ -1281,18 +1310,28 @@ static void iwl_set_rate(struct iwl_priv *priv)
void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon;
struct iwl_csa_notification *csa = &(pkt->u.csa_notif);
- IWL_DEBUG_11H(priv, "CSA notif: channel %d, status %d\n",
- le16_to_cpu(csa->channel), le32_to_cpu(csa->status));
- rxon->channel = csa->channel;
- priv->staging_rxon.channel = csa->channel;
+
+ if (priv->switch_rxon.switch_in_progress) {
+ if (!le32_to_cpu(csa->status) &&
+ (csa->channel == priv->switch_rxon.channel)) {
+ rxon->channel = csa->channel;
+ priv->staging_rxon.channel = csa->channel;
+ IWL_DEBUG_11H(priv, "CSA notif: channel %d\n",
+ le16_to_cpu(csa->channel));
+ } else
+ IWL_ERR(priv, "CSA notif (fail) : channel %d\n",
+ le16_to_cpu(csa->channel));
+
+ priv->switch_rxon.switch_in_progress = false;
+ }
}
EXPORT_SYMBOL(iwl_rx_csa);
#ifdef CONFIG_IWLWIFI_DEBUG
-static void iwl_print_rx_config_cmd(struct iwl_priv *priv)
+void iwl_print_rx_config_cmd(struct iwl_priv *priv)
{
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
@@ -1310,6 +1349,7 @@ static void iwl_print_rx_config_cmd(struct iwl_priv *priv)
IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr);
IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id));
}
+EXPORT_SYMBOL(iwl_print_rx_config_cmd);
#endif
/**
* iwl_irq_handle_error - called for HW or SW error interrupt from card
@@ -1322,12 +1362,11 @@ void iwl_irq_handle_error(struct iwl_priv *priv)
/* Cancel currently queued command. */
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
+ priv->cfg->ops->lib->dump_nic_error_log(priv);
+ priv->cfg->ops->lib->dump_nic_event_log(priv, false);
#ifdef CONFIG_IWLWIFI_DEBUG
- if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) {
- priv->cfg->ops->lib->dump_nic_error_log(priv);
- priv->cfg->ops->lib->dump_nic_event_log(priv);
+ if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)
iwl_print_rx_config_cmd(priv);
- }
#endif
wake_up_interruptible(&priv->wait_command_queue);
@@ -1346,6 +1385,160 @@ void iwl_irq_handle_error(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_irq_handle_error);
+int iwl_apm_stop_master(struct iwl_priv *priv)
+{
+ int ret = 0;
+
+ /* stop device's busmaster DMA activity */
+ iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER);
+
+ ret = iwl_poll_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED,
+ CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
+ if (ret)
+ IWL_WARN(priv, "Master Disable Timed Out, 100 usec\n");
+
+ IWL_DEBUG_INFO(priv, "stop master\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(iwl_apm_stop_master);
+
+void iwl_apm_stop(struct iwl_priv *priv)
+{
+ IWL_DEBUG_INFO(priv, "Stop card, put in low power state\n");
+
+ /* Stop device's DMA activity */
+ iwl_apm_stop_master(priv);
+
+ /* Reset the entire device */
+ iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+
+ udelay(10);
+
+ /*
+ * Clear "initialization complete" bit to move adapter from
+ * D0A* (powered-up Active) --> D0U* (Uninitialized) state.
+ */
+ iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+}
+EXPORT_SYMBOL(iwl_apm_stop);
+
+
+/*
+ * Start up NIC's basic functionality after it has been reset
+ * (e.g. after platform boot, or shutdown via iwl_apm_stop())
+ * NOTE: This does not load uCode nor start the embedded processor
+ */
+int iwl_apm_init(struct iwl_priv *priv)
+{
+ int ret = 0;
+ u16 lctl;
+
+ IWL_DEBUG_INFO(priv, "Init card's basic functions\n");
+
+ /*
+ * Use "set_bit" below rather than "write", to preserve any hardware
+ * bits already set by default after reset.
+ */
+
+ /* Disable L0S exit timer (platform NMI Work/Around) */
+ iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+
+ /*
+ * Disable L0s without affecting L1;
+ * don't wait for ICH L0s (ICH bug W/A)
+ */
+ iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+ CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX);
+
+ /* Set FH wait threshold to maximum (HW error during stress W/A) */
+ iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL);
+
+ /*
+ * Enable HAP INTA (interrupt from management bus) to
+ * wake device's PCI Express link L1a -> L0s
+ * NOTE: This is no-op for 3945 (non-existant bit)
+ */
+ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A);
+
+ /*
+ * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition.
+ * Check if BIOS (or OS) enabled L1-ASPM on this device.
+ * If so (likely), disable L0S, so device moves directly L0->L1;
+ * costs negligible amount of power savings.
+ * If not (unlikely), enable L0S, so there is at least some
+ * power savings, even without L1.
+ */
+ if (priv->cfg->set_l0s) {
+ lctl = iwl_pcie_link_ctl(priv);
+ if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) ==
+ PCI_CFG_LINK_CTRL_VAL_L1_EN) {
+ /* L1-ASPM enabled; disable(!) L0S */
+ iwl_set_bit(priv, CSR_GIO_REG,
+ CSR_GIO_REG_VAL_L0S_ENABLED);
+ IWL_DEBUG_POWER(priv, "L1 Enabled; Disabling L0S\n");
+ } else {
+ /* L1-ASPM disabled; enable(!) L0S */
+ iwl_clear_bit(priv, CSR_GIO_REG,
+ CSR_GIO_REG_VAL_L0S_ENABLED);
+ IWL_DEBUG_POWER(priv, "L1 Disabled; Enabling L0S\n");
+ }
+ }
+
+ /* Configure analog phase-lock-loop before activating to D0A */
+ if (priv->cfg->pll_cfg_val)
+ iwl_set_bit(priv, CSR_ANA_PLL_CFG, priv->cfg->pll_cfg_val);
+
+ /*
+ * Set "initialization complete" bit to move adapter from
+ * D0U* --> D0A* (powered-up active) state.
+ */
+ iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+
+ /*
+ * Wait for clock stabilization; once stabilized, access to
+ * device-internal resources is supported, e.g. iwl_write_prph()
+ * and accesses to uCode SRAM.
+ */
+ ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+ if (ret < 0) {
+ IWL_DEBUG_INFO(priv, "Failed to init the card\n");
+ goto out;
+ }
+
+ /*
+ * Enable DMA and BSM (if used) clocks, wait for them to stabilize.
+ * BSM (Boostrap State Machine) is only in 3945 and 4965;
+ * later devices (i.e. 5000 and later) have non-volatile SRAM,
+ * and don't need BSM to restore data after power-saving sleep.
+ *
+ * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits
+ * do not disable clocks. This preserves any hardware bits already
+ * set by default in "CLK_CTRL_REG" after reset.
+ */
+ if (priv->cfg->use_bsm)
+ iwl_write_prph(priv, APMG_CLK_EN_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT);
+ else
+ iwl_write_prph(priv, APMG_CLK_EN_REG,
+ APMG_CLK_VAL_DMA_CLK_RQT);
+ udelay(20);
+
+ /* Disable L1-Active */
+ iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
+ APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(iwl_apm_init);
+
+
+
void iwl_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
@@ -1393,73 +1586,14 @@ void iwl_configure_filter(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(iwl_configure_filter);
-int iwl_setup_mac(struct iwl_priv *priv)
-{
- int ret;
- struct ieee80211_hw *hw = priv->hw;
- hw->rate_control_algorithm = "iwl-agn-rs";
-
- /* Tell mac80211 our characteristics */
- hw->flags = IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_NOISE_DBM |
- IEEE80211_HW_AMPDU_AGGREGATION |
- IEEE80211_HW_SPECTRUM_MGMT;
-
- if (!priv->cfg->broken_powersave)
- hw->flags |= IEEE80211_HW_SUPPORTS_PS |
- IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
-
- hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
-
- hw->wiphy->custom_regulatory = true;
-
- /* Firmware does not support this */
- hw->wiphy->disable_beacon_hints = true;
-
- /*
- * For now, disable PS by default because it affects
- * RX performance significantly.
- */
- hw->wiphy->ps_default = false;
-
- hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
- /* we create the 802.11 header and a zero-length SSID element */
- hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2;
-
- /* Default value; 4 EDCA QOS priorities */
- hw->queues = 4;
-
- hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
-
- if (priv->bands[IEEE80211_BAND_2GHZ].n_channels)
- priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &priv->bands[IEEE80211_BAND_2GHZ];
- if (priv->bands[IEEE80211_BAND_5GHZ].n_channels)
- priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &priv->bands[IEEE80211_BAND_5GHZ];
-
- ret = ieee80211_register_hw(priv->hw);
- if (ret) {
- IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
- return ret;
- }
- priv->mac80211_registered = 1;
-
- return 0;
-}
-EXPORT_SYMBOL(iwl_setup_mac);
-
int iwl_set_hw_params(struct iwl_priv *priv)
{
priv->hw_params.max_rxq_size = RX_QUEUE_SIZE;
priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG;
if (priv->cfg->mod_params->amsdu_size_8K)
- priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_8K;
+ priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_8K);
else
- priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_4K;
- priv->hw_params.max_pkt_size = priv->hw_params.rx_buf_size - 256;
+ priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_4K);
priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL;
@@ -1471,71 +1605,6 @@ int iwl_set_hw_params(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_set_hw_params);
-int iwl_init_drv(struct iwl_priv *priv)
-{
- int ret;
-
- priv->ibss_beacon = NULL;
-
- spin_lock_init(&priv->lock);
- spin_lock_init(&priv->sta_lock);
- spin_lock_init(&priv->hcmd_lock);
-
- INIT_LIST_HEAD(&priv->free_frames);
-
- mutex_init(&priv->mutex);
-
- /* Clear the driver's (not device's) station table */
- iwl_clear_stations_table(priv);
-
- priv->data_retry_limit = -1;
- priv->ieee_channels = NULL;
- priv->ieee_rates = NULL;
- priv->band = IEEE80211_BAND_2GHZ;
-
- priv->iw_mode = NL80211_IFTYPE_STATION;
-
- priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
-
- /* Choose which receivers/antennas to use */
- if (priv->cfg->ops->hcmd->set_rxon_chain)
- priv->cfg->ops->hcmd->set_rxon_chain(priv);
-
- iwl_init_scan_params(priv);
-
- iwl_reset_qos(priv);
-
- priv->qos_data.qos_active = 0;
- priv->qos_data.qos_cap.val = 0;
-
- priv->rates_mask = IWL_RATES_MASK;
- /* Set the tx_power_user_lmt to the lowest power level
- * this value will get overwritten by channel max power avg
- * from eeprom */
- priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN;
-
- ret = iwl_init_channel_map(priv);
- if (ret) {
- IWL_ERR(priv, "initializing regulatory failed: %d\n", ret);
- goto err;
- }
-
- ret = iwlcore_init_geos(priv);
- if (ret) {
- IWL_ERR(priv, "initializing geos failed: %d\n", ret);
- goto err_free_channel_map;
- }
- iwlcore_init_hw_rates(priv, priv->ieee_rates);
-
- return 0;
-
-err_free_channel_map:
- iwl_free_channel_map(priv);
-err:
- return ret;
-}
-EXPORT_SYMBOL(iwl_init_drv);
-
int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
{
int ret = 0;
@@ -1583,15 +1652,6 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force)
}
EXPORT_SYMBOL(iwl_set_tx_power);
-void iwl_uninit_drv(struct iwl_priv *priv)
-{
- iwl_calib_free_results(priv);
- iwlcore_free_geos(priv);
- iwl_free_channel_map(priv);
- kfree(priv->scan);
-}
-EXPORT_SYMBOL(iwl_uninit_drv);
-
#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
/* Free dram table */
@@ -1915,9 +1975,9 @@ EXPORT_SYMBOL(iwl_isr_legacy);
int iwl_send_bt_config(struct iwl_priv *priv)
{
struct iwl_bt_cmd bt_cmd = {
- .flags = 3,
- .lead_time = 0xAA,
- .max_kill = 1,
+ .flags = BT_COEX_MODE_4W,
+ .lead_time = BT_LEAD_TIME_DEF,
+ .max_kill = BT_MAX_KILL_DEF,
.kill_ack_mask = 0,
.kill_cts_mask = 0,
};
@@ -1927,16 +1987,21 @@ int iwl_send_bt_config(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_send_bt_config);
-int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags)
+int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear)
{
- u32 stat_flags = 0;
- struct iwl_host_cmd cmd = {
- .id = REPLY_STATISTICS_CMD,
- .flags = flags,
- .len = sizeof(stat_flags),
- .data = (u8 *) &stat_flags,
+ struct iwl_statistics_cmd statistics_cmd = {
+ .configuration_flags =
+ clear ? IWL_STATS_CONF_CLEAR_STATS : 0,
};
- return iwl_send_cmd(priv, &cmd);
+
+ if (flags & CMD_ASYNC)
+ return iwl_send_cmd_pdu_async(priv, REPLY_STATISTICS_CMD,
+ sizeof(struct iwl_statistics_cmd),
+ &statistics_cmd, NULL);
+ else
+ return iwl_send_cmd_pdu(priv, REPLY_STATISTICS_CMD,
+ sizeof(struct iwl_statistics_cmd),
+ &statistics_cmd);
}
EXPORT_SYMBOL(iwl_send_statistics_request);
@@ -2077,10 +2142,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
spin_unlock_irqrestore(&priv->lock, flags);
priv->thermal_throttle.ct_kill_toggle = false;
- switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
- case CSR_HW_REV_TYPE_1000:
- case CSR_HW_REV_TYPE_6x00:
- case CSR_HW_REV_TYPE_6x50:
+ if (priv->cfg->support_ct_kill_exit) {
adv_cmd.critical_temperature_enter =
cpu_to_le32(priv->hw_params.ct_kill_threshold);
adv_cmd.critical_temperature_exit =
@@ -2097,8 +2159,7 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
"exit is %d\n",
priv->hw_params.ct_kill_threshold,
priv->hw_params.ct_kill_exit_threshold);
- break;
- default:
+ } else {
cmd.critical_temperature_R =
cpu_to_le32(priv->hw_params.ct_kill_threshold);
@@ -2111,7 +2172,6 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv)
"succeeded, "
"critical temperature is %d\n",
priv->hw_params.ct_kill_threshold);
- break;
}
}
EXPORT_SYMBOL(iwl_rf_kill_ct_config);
@@ -2143,7 +2203,7 @@ void iwl_rx_pm_sleep_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_sleep_notification *sleep = &(pkt->u.sleep_notif);
IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n",
sleep->pm_sleep_mode, sleep->pm_wakeup_src);
@@ -2154,7 +2214,7 @@ EXPORT_SYMBOL(iwl_rx_pm_sleep_notif);
void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled "
"notification for %s:\n", len,
@@ -2166,7 +2226,7 @@ EXPORT_SYMBOL(iwl_rx_pm_debug_statistics_notif);
void iwl_rx_reply_error(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
IWL_ERR(priv, "Error Reply type 0x%08X cmd %s (0x%02X) "
"seq 0x%04X ser 0x%08X\n",
@@ -2228,42 +2288,58 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
EXPORT_SYMBOL(iwl_mac_conf_tx);
static void iwl_ht_conf(struct iwl_priv *priv,
- struct ieee80211_bss_conf *bss_conf)
+ struct ieee80211_bss_conf *bss_conf)
{
- struct ieee80211_sta_ht_cap *ht_conf;
- struct iwl_ht_info *iwl_conf = &priv->current_ht_config;
+ struct iwl_ht_config *ht_conf = &priv->current_ht_config;
struct ieee80211_sta *sta;
IWL_DEBUG_MAC80211(priv, "enter: \n");
- if (!iwl_conf->is_ht)
+ if (!ht_conf->is_ht)
return;
+ ht_conf->ht_protection =
+ bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
+ ht_conf->non_GF_STA_present =
+ !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
- /*
- * It is totally wrong to base global information on something
- * that is valid only when associated, alas, this driver works
- * that way and I don't know how to fix it.
- */
+ ht_conf->single_chain_sufficient = false;
- rcu_read_lock();
- sta = ieee80211_find_sta(priv->hw, priv->bssid);
- if (!sta) {
+ switch (priv->iw_mode) {
+ case NL80211_IFTYPE_STATION:
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, priv->bssid);
+ if (sta) {
+ struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+ int maxstreams;
+
+ maxstreams = (ht_cap->mcs.tx_params &
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+ >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
+ maxstreams += 1;
+
+ if ((ht_cap->mcs.rx_mask[1] == 0) &&
+ (ht_cap->mcs.rx_mask[2] == 0))
+ ht_conf->single_chain_sufficient = true;
+ if (maxstreams <= 1)
+ ht_conf->single_chain_sufficient = true;
+ } else {
+ /*
+ * If at all, this can only happen through a race
+ * when the AP disconnects us while we're still
+ * setting up the connection, in that case mac80211
+ * will soon tell us about that.
+ */
+ ht_conf->single_chain_sufficient = true;
+ }
rcu_read_unlock();
- return;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ ht_conf->single_chain_sufficient = true;
+ break;
+ default:
+ break;
}
- ht_conf = &sta->ht_cap;
-
- iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2);
-
- memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16);
-
- iwl_conf->ht_protection =
- bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION;
- iwl_conf->non_GF_STA_present =
- !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
-
- rcu_read_unlock();
IWL_DEBUG_MAC80211(priv, "leave\n");
}
@@ -2387,6 +2463,8 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
priv->timestamp = bss_conf->timestamp;
priv->assoc_capability = bss_conf->assoc_capability;
+ iwl_led_associate(priv);
+
/*
* We have just associated, don't start scan too early
* leave time for EAPOL exchange to complete.
@@ -2397,9 +2475,20 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
IWL_DELAY_NEXT_SCAN_AFTER_ASSOC;
if (!iwl_is_rfkill(priv))
priv->cfg->ops->lib->post_associate(priv);
- } else
+ } else {
priv->assoc_id = 0;
+ iwl_led_disassociate(priv);
+ /*
+ * inform the ucode that there is no longer an
+ * association and that no more packets should be
+ * send
+ */
+ priv->staging_rxon.filter_flags &=
+ ~RXON_FILTER_ASSOC_MSK;
+ priv->staging_rxon.assoc_id = 0;
+ iwlcore_commit_rxon(priv);
+ }
}
if (changes && iwl_is_associated(priv) && priv->assoc_id) {
@@ -2414,6 +2503,14 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw,
}
}
+ if ((changes & BSS_CHANGED_BEACON_ENABLED) &&
+ vif->bss_conf.enable_beacon) {
+ memcpy(priv->staging_rxon.bssid_addr,
+ bss_conf->bssid, ETH_ALEN);
+ memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN);
+ iwlcore_config_ap(priv);
+ }
+
mutex_unlock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
@@ -2570,7 +2667,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
struct iwl_priv *priv = hw->priv;
const struct iwl_channel_info *ch_info;
struct ieee80211_conf *conf = &hw->conf;
- struct iwl_ht_info *ht_conf = &priv->current_ht_config;
+ struct iwl_ht_config *ht_conf = &priv->current_ht_config;
unsigned long flags = 0;
int ret = 0;
u16 ch;
@@ -2620,21 +2717,18 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
if (conf_is_ht40_minus(conf)) {
ht_conf->extension_chan_offset =
IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- ht_conf->supported_chan_width =
- IWL_CHANNEL_WIDTH_40MHZ;
+ ht_conf->is_40mhz = true;
} else if (conf_is_ht40_plus(conf)) {
ht_conf->extension_chan_offset =
IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
- ht_conf->supported_chan_width =
- IWL_CHANNEL_WIDTH_40MHZ;
+ ht_conf->is_40mhz = true;
} else {
ht_conf->extension_chan_offset =
IEEE80211_HT_PARAM_CHA_SEC_NONE;
- ht_conf->supported_chan_width =
- IWL_CHANNEL_WIDTH_20MHZ;
+ ht_conf->is_40mhz = false;
}
} else
- ht_conf->supported_chan_width = IWL_CHANNEL_WIDTH_20MHZ;
+ ht_conf->is_40mhz = false;
/* Default to no protection. Protection mode will later be set
* from BSS config in iwl_ht_conf */
ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
@@ -2649,6 +2743,22 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
iwl_set_flags_for_band(priv, conf->channel->band);
spin_unlock_irqrestore(&priv->lock, flags);
+ if (iwl_is_associated(priv) &&
+ (le16_to_cpu(priv->active_rxon.channel) != ch) &&
+ priv->cfg->ops->lib->set_channel_switch) {
+ iwl_set_rate(priv);
+ /*
+ * at this point, staging_rxon has the
+ * configuration for channel switch
+ */
+ ret = priv->cfg->ops->lib->set_channel_switch(priv,
+ ch);
+ if (!ret) {
+ iwl_print_rx_config_cmd(priv);
+ goto out;
+ }
+ priv->switch_rxon.switch_in_progress = false;
+ }
set_ch_out:
/* The list of supported rates and rate mask can be different
* for each band; since the band may have changed, reset
@@ -2656,7 +2766,8 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
iwl_set_rate(priv);
}
- if (changed & IEEE80211_CONF_CHANGE_PS) {
+ if (changed & (IEEE80211_CONF_CHANGE_PS |
+ IEEE80211_CONF_CHANGE_IDLE)) {
ret = iwl_power_update_mode(priv, false);
if (ret)
IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n");
@@ -2740,7 +2851,7 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
IWL_DEBUG_MAC80211(priv, "enter\n");
spin_lock_irqsave(&priv->lock, flags);
- memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info));
+ memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_config));
spin_unlock_irqrestore(&priv->lock, flags);
iwl_reset_qos(priv);
@@ -2792,6 +2903,55 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(iwl_mac_reset_tsf);
+int iwl_alloc_txq_mem(struct iwl_priv *priv)
+{
+ if (!priv->txq)
+ priv->txq = kzalloc(
+ sizeof(struct iwl_tx_queue) * priv->cfg->num_of_queues,
+ GFP_KERNEL);
+ if (!priv->txq) {
+ IWL_ERR(priv, "Not enough memory for txq \n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(iwl_alloc_txq_mem);
+
+void iwl_free_txq_mem(struct iwl_priv *priv)
+{
+ kfree(priv->txq);
+ priv->txq = NULL;
+}
+EXPORT_SYMBOL(iwl_free_txq_mem);
+
+int iwl_send_wimax_coex(struct iwl_priv *priv)
+{
+ struct iwl_wimax_coex_cmd uninitialized_var(coex_cmd);
+
+ if (priv->cfg->support_wimax_coexist) {
+ /* UnMask wake up src at associated sleep */
+ coex_cmd.flags |= COEX_FLAGS_ASSOC_WA_UNMASK_MSK;
+
+ /* UnMask wake up src at unassociated sleep */
+ coex_cmd.flags |= COEX_FLAGS_UNASSOC_WA_UNMASK_MSK;
+ memcpy(coex_cmd.sta_prio, cu_priorities,
+ sizeof(struct iwl_wimax_coex_event_entry) *
+ COEX_NUM_OF_EVENTS);
+
+ /* enabling the coexistence feature */
+ coex_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK;
+
+ /* enabling the priorities tables */
+ coex_cmd.flags |= COEX_FLAGS_STA_TABLE_VALID_MSK;
+ } else {
+ /* coexistence is disabled */
+ memset(&coex_cmd, 0, sizeof(coex_cmd));
+ }
+ return iwl_send_cmd_pdu(priv, COEX_PRIORITY_TABLE_CMD,
+ sizeof(coex_cmd), &coex_cmd);
+}
+EXPORT_SYMBOL(iwl_send_wimax_coex);
+
#ifdef CONFIG_IWLWIFI_DEBUGFS
#define IWL_TRAFFIC_DUMP_SIZE (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES)
@@ -2929,15 +3089,11 @@ const char *get_ctrl_string(int cmd)
}
}
-void iwl_clear_tx_stats(struct iwl_priv *priv)
+void iwl_clear_traffic_stats(struct iwl_priv *priv)
{
memset(&priv->tx_stats, 0, sizeof(struct traffic_stats));
-
-}
-
-void iwl_clear_rx_stats(struct iwl_priv *priv)
-{
memset(&priv->rx_stats, 0, sizeof(struct traffic_stats));
+ priv->led_tpt = 0;
}
/*
@@ -3030,6 +3186,7 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len)
stats->data_cnt++;
stats->data_bytes += len;
}
+ iwl_leds_background(priv);
}
EXPORT_SYMBOL(iwl_update_stats);
#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 7754538c219..675b7df632f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -63,6 +63,8 @@
#ifndef __iwl_core_h__
#define __iwl_core_h__
+#include <linux/utsrelease.h>
+
/************************
* forward declarations *
************************/
@@ -70,7 +72,7 @@ struct iwl_host_cmd;
struct iwl_cmd;
-#define IWLWIFI_VERSION "1.3.27k"
+#define IWLWIFI_VERSION UTS_RELEASE "-k"
#define DRV_COPYRIGHT "Copyright(c) 2003-2009 Intel Corporation"
#define DRV_AUTHOR "<ilw@linux.intel.com>"
@@ -89,6 +91,7 @@ struct iwl_hcmd_ops {
int (*rxon_assoc)(struct iwl_priv *priv);
int (*commit_rxon)(struct iwl_priv *priv);
void (*set_rxon_chain)(struct iwl_priv *priv);
+ int (*set_tx_ant)(struct iwl_priv *priv, u8 valid_tx_ant);
};
struct iwl_hcmd_utils_ops {
@@ -97,7 +100,8 @@ struct iwl_hcmd_utils_ops {
void (*gain_computation)(struct iwl_priv *priv,
u32 *average_noise,
u16 min_average_noise_antennat_i,
- u32 min_average_noise);
+ u32 min_average_noise,
+ u8 default_chain);
void (*chain_noise_reset)(struct iwl_priv *priv);
void (*rts_tx_cmd_flag)(struct ieee80211_tx_info *info,
__le32 *tx_flags);
@@ -107,7 +111,6 @@ struct iwl_hcmd_utils_ops {
struct iwl_apm_ops {
int (*init)(struct iwl_priv *priv);
- int (*reset)(struct iwl_priv *priv);
void (*stop)(struct iwl_priv *priv);
void (*config)(struct iwl_priv *priv);
int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src);
@@ -166,8 +169,9 @@ struct iwl_lib_ops {
int (*is_valid_rtc_data_addr)(u32 addr);
/* 1st ucode load */
int (*load_ucode)(struct iwl_priv *priv);
- void (*dump_nic_event_log)(struct iwl_priv *priv);
+ void (*dump_nic_event_log)(struct iwl_priv *priv, bool full_log);
void (*dump_nic_error_log)(struct iwl_priv *priv);
+ int (*set_channel_switch)(struct iwl_priv *priv, u16 channel);
/* power management */
struct iwl_apm_ops apm_ops;
@@ -185,18 +189,24 @@ struct iwl_lib_ops {
struct iwl_temp_ops temp_ops;
};
+struct iwl_led_ops {
+ int (*cmd)(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd);
+ int (*on)(struct iwl_priv *priv);
+ int (*off)(struct iwl_priv *priv);
+};
+
struct iwl_ops {
const struct iwl_ucode_ops *ucode;
const struct iwl_lib_ops *lib;
const struct iwl_hcmd_ops *hcmd;
const struct iwl_hcmd_utils_ops *utils;
+ const struct iwl_led_ops *led;
};
struct iwl_mod_params {
int sw_crypto; /* def: 0 = using hardware encryption */
int disable_hw_scan; /* def: 0 = use h/w scan */
int num_of_queues; /* def: HW dependent */
- int num_of_ampdu_queues;/* def: HW dependent */
int disable_11n; /* def: 0 = 11n capabilities enabled */
int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */
int antenna; /* def: 0 = both antennas (use diversity) */
@@ -213,7 +223,15 @@ struct iwl_mod_params {
* @pa_type: used by 6000 series only to identify the type of Power Amplifier
* @max_ll_items: max number of OTP blocks
* @shadow_ram_support: shadow support for OTP memory
+ * @led_compensation: compensate on the led on/off time per HW according
+ * to the deviation to achieve the desired led frequency.
+ * The detail algorithm is described in iwl-led.c
* @use_rts_for_ht: use rts/cts protection for HT traffic
+ * @chain_noise_num_beacons: number of beacons used to compute chain noise
+ * @adv_thermal_throttle: support advance thermal throttle
+ * @support_ct_kill_exit: support ct kill exit condition
+ * @sm_ps_mode: spatial multiplexing power save mode
+ * @support_wimax_coexist: support wimax/wifi co-exist
*
* We enable the driver to be backward compatible wrt API version. The
* driver specifies which APIs it supports (with @ucode_api_max being the
@@ -245,18 +263,32 @@ struct iwl_cfg {
int eeprom_size;
u16 eeprom_ver;
u16 eeprom_calib_ver;
+ int num_of_queues; /* def: HW dependent */
+ int num_of_ampdu_queues;/* def: HW dependent */
const struct iwl_ops *ops;
const struct iwl_mod_params *mod_params;
u8 valid_tx_ant;
u8 valid_rx_ant;
- bool need_pll_cfg;
+
+ /* for iwl_apm_init() */
+ u32 pll_cfg_val;
+ bool set_l0s;
+ bool use_bsm;
+
bool use_isr_legacy;
enum iwl_pa_type pa_type;
const u16 max_ll_items;
const bool shadow_ram_support;
const bool ht_greenfield_support;
+ u16 led_compensation;
const bool broken_powersave;
bool use_rts_for_ht;
+ int chain_noise_num_beacons;
+ const bool supports_idle;
+ bool adv_thermal_throttle;
+ bool support_ct_kill_exit;
+ u8 sm_ps_mode;
+ const bool support_wimax_coexist;
};
/***************************
@@ -275,7 +307,7 @@ int iwl_check_rxon_cmd(struct iwl_priv *priv);
int iwl_full_rxon_required(struct iwl_priv *priv);
void iwl_set_rxon_chain(struct iwl_priv *priv);
int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch);
-void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info);
+void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf);
u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv,
struct ieee80211_sta_ht_cap *sta_ht_inf);
void iwl_set_flags_for_band(struct iwl_priv *priv, enum ieee80211_band band);
@@ -289,10 +321,7 @@ void iwl_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags, u64 multicast);
int iwl_hw_nic_init(struct iwl_priv *priv);
-int iwl_setup_mac(struct iwl_priv *priv);
int iwl_set_hw_params(struct iwl_priv *priv);
-int iwl_init_drv(struct iwl_priv *priv);
-void iwl_uninit_drv(struct iwl_priv *priv);
bool iwl_is_monitor_mode(struct iwl_priv *priv);
void iwl_post_associate(struct iwl_priv *priv);
void iwl_bss_info_changed(struct ieee80211_hw *hw,
@@ -311,6 +340,11 @@ void iwl_config_ap(struct iwl_priv *priv);
int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats);
void iwl_mac_reset_tsf(struct ieee80211_hw *hw);
+int iwl_alloc_txq_mem(struct iwl_priv *priv);
+void iwl_free_txq_mem(struct iwl_priv *priv);
+void iwlcore_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
+ __le32 *tx_flags);
+int iwl_send_wimax_coex(struct iwl_priv *priv);
#ifdef CONFIG_IWLWIFI_DEBUGFS
int iwl_alloc_traffic_mem(struct iwl_priv *priv);
void iwl_free_traffic_mem(struct iwl_priv *priv);
@@ -321,8 +355,7 @@ void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv,
u16 length, struct ieee80211_hdr *header);
const char *get_mgmt_string(int cmd);
const char *get_ctrl_string(int cmd);
-void iwl_clear_tx_stats(struct iwl_priv *priv);
-void iwl_clear_rx_stats(struct iwl_priv *priv);
+void iwl_clear_traffic_stats(struct iwl_priv *priv);
void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc,
u16 len);
#else
@@ -358,6 +391,7 @@ static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx,
/* data */
stats->data_bytes += len;
}
+ iwl_leds_background(priv);
}
#endif
/*****************************************************
@@ -393,6 +427,8 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
void iwl_rx_statistics(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
+void iwl_reply_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb);
void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb);
/* TX helpers */
@@ -511,7 +547,7 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len,
const void *data,
void (*callback)(struct iwl_priv *priv,
struct iwl_device_cmd *cmd,
- struct sk_buff *skb));
+ struct iwl_rx_packet *pkt));
int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
@@ -544,15 +580,12 @@ int iwl_pci_resume(struct pci_dev *pdev);
/*****************************************************
* Error Handling Debugging
******************************************************/
-#ifdef CONFIG_IWLWIFI_DEBUG
-void iwl_dump_nic_event_log(struct iwl_priv *priv);
void iwl_dump_nic_error_log(struct iwl_priv *priv);
+void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log);
+#ifdef CONFIG_IWLWIFI_DEBUG
+void iwl_print_rx_config_cmd(struct iwl_priv *priv);
#else
-static inline void iwl_dump_nic_event_log(struct iwl_priv *priv)
-{
-}
-
-static inline void iwl_dump_nic_error_log(struct iwl_priv *priv)
+static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv)
{
}
#endif
@@ -571,6 +604,7 @@ void iwlcore_free_geos(struct iwl_priv *priv);
#define STATUS_HCMD_SYNC_ACTIVE 1 /* sync host command in progress */
#define STATUS_INT_ENABLED 2
#define STATUS_RF_KILL_HW 3
+#define STATUS_CT_KILL 4
#define STATUS_INIT 5
#define STATUS_ALIVE 6
#define STATUS_READY 7
@@ -615,6 +649,11 @@ static inline int iwl_is_rfkill(struct iwl_priv *priv)
return iwl_is_rfkill_hw(priv);
}
+static inline int iwl_is_ctkill(struct iwl_priv *priv)
+{
+ return test_bit(STATUS_CT_KILL, &priv->status);
+}
+
static inline int iwl_is_ready_rf(struct iwl_priv *priv)
{
@@ -626,7 +665,8 @@ static inline int iwl_is_ready_rf(struct iwl_priv *priv)
extern void iwl_rf_kill_ct_config(struct iwl_priv *priv);
extern int iwl_send_bt_config(struct iwl_priv *priv);
-extern int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags);
+extern int iwl_send_statistics_request(struct iwl_priv *priv,
+ u8 flags, bool clear);
extern int iwl_verify_ucode(struct iwl_priv *priv);
extern int iwl_send_lq_cmd(struct iwl_priv *priv,
struct iwl_link_quality_cmd *lq, u8 flags);
@@ -636,6 +676,9 @@ extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
+void iwl_apm_stop(struct iwl_priv *priv);
+int iwl_apm_stop_master(struct iwl_priv *priv);
+int iwl_apm_init(struct iwl_priv *priv);
void iwl_setup_rxon_timing(struct iwl_priv *priv);
static inline int iwl_send_rxon_assoc(struct iwl_priv *priv)
@@ -655,5 +698,4 @@ static inline const struct ieee80211_supported_band *iwl_get_hw_mode(
{
return priv->hw->wiphy->bands[band];
}
-
#endif /* __iwl_core_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 06437d13e73..a7bfae01f19 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -62,11 +62,29 @@
*****************************************************************************/
#ifndef __iwl_csr_h__
#define __iwl_csr_h__
-/*=== CSR (control and status registers) ===*/
+/*
+ * CSR (control and status registers)
+ *
+ * CSR registers are mapped directly into PCI bus space, and are accessible
+ * whenever platform supplies power to device, even when device is in
+ * low power states due to driver-invoked device resets
+ * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes.
+ *
+ * Use iwl_write32() and iwl_read32() family to access these registers;
+ * these provide simple PCI bus access, without waking up the MAC.
+ * Do not use iwl_write_direct32() family for these registers;
+ * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ.
+ * The MAC (uCode processor, etc.) does not need to be powered up for accessing
+ * the CSR registers.
+ *
+ * NOTE: Newer devices using one-time-programmable (OTP) memory
+ * require device to be awake in order to read this memory
+ * via CSR_EEPROM and CSR_OTP registers
+ */
#define CSR_BASE (0x000)
#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */
-#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
+#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */
#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */
#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */
#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/
@@ -74,43 +92,67 @@
#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/
#define CSR_GP_CNTRL (CSR_BASE+0x024)
+/* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */
+#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005)
+
/*
* Hardware revision info
* Bit fields:
* 31-8: Reserved
- * 7-4: Type of device: 0x0 = 4965, 0xd = 3945
+ * 7-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions
* 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D
- * 1-0: "Dash" value, as in A-1, etc.
+ * 1-0: "Dash" (-) value, as in A-1, etc.
*
* NOTE: Revision step affects calculation of CCK txpower for 4965.
+ * NOTE: See also CSR_HW_REV_WA_REG (work-around for bug in 4965).
*/
#define CSR_HW_REV (CSR_BASE+0x028)
-/* EEPROM reads */
+/*
+ * EEPROM and OTP (one-time-programmable) memory reads
+ *
+ * NOTE: For (newer) devices using OTP, device must be awake, initialized via
+ * apm_ops.init() in order to read. Older devices (3945/4965/5000)
+ * use EEPROM and do not require this.
+ */
#define CSR_EEPROM_REG (CSR_BASE+0x02c)
#define CSR_EEPROM_GP (CSR_BASE+0x030)
#define CSR_OTP_GP_REG (CSR_BASE+0x034)
+
#define CSR_GIO_REG (CSR_BASE+0x03C)
#define CSR_GP_UCODE_REG (CSR_BASE+0x048)
#define CSR_GP_DRIVER_REG (CSR_BASE+0x050)
+
+/*
+ * UCODE-DRIVER GP (general purpose) mailbox registers.
+ * SET/CLR registers set/clear bit(s) if "1" is written.
+ */
#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058)
#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
+
#define CSR_LED_REG (CSR_BASE+0x094)
#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0)
+
+/* GIO Chicken Bits (PCI Express bus link power management) */
#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
-#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005)
/* Analog phase-lock-loop configuration */
#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
+
/*
- * Indicates hardware rev, to determine CCK backoff for txpower calculation.
+ * CSR Hardware Revision Workaround Register. Indicates hardware rev;
+ * "step" determines CCK backoff for txpower calculation. Used for 4965 only.
+ * See also CSR_HW_REV register.
* Bit fields:
* 3-2: 0 = A, 1 = B, 2 = C, 3 = D step
+ * 1-0: "Dash" (-) value, as in C-1, etc.
*/
-#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C)
-#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240)
+#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C)
+
+#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240)
+#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250)
/* Bits for CSR_HW_IF_CONFIG_REG */
#define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010)
@@ -125,14 +167,14 @@
#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000)
#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000)
-#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000)
-#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
-#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000)
-#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000)
-#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000)
+#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000)
+#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
+#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */
+#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */
+#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */
-#define CSR_INT_PERIODIC_DIS (0x00)
-#define CSR_INT_PERIODIC_ENA (0xFF)
+#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/
+#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/
/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
* acknowledged (reset) by host writing "1" to flagged bits. */
@@ -195,8 +237,46 @@
#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080)
#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100)
#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200)
+#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000)
-/* GP (general purpose) CONTROL */
+/*
+ * GP (general purpose) CONTROL REGISTER
+ * Bit fields:
+ * 27: HW_RF_KILL_SW
+ * Indicates state of (platform's) hardware RF-Kill switch
+ * 26-24: POWER_SAVE_TYPE
+ * Indicates current power-saving mode:
+ * 000 -- No power saving
+ * 001 -- MAC power-down
+ * 010 -- PHY (radio) power-down
+ * 011 -- Error
+ * 9-6: SYS_CONFIG
+ * Indicates current system configuration, reflecting pins on chip
+ * as forced high/low by device circuit board.
+ * 4: GOING_TO_SLEEP
+ * Indicates MAC is entering a power-saving sleep power-down.
+ * Not a good time to access device-internal resources.
+ * 3: MAC_ACCESS_REQ
+ * Host sets this to request and maintain MAC wakeup, to allow host
+ * access to device-internal resources. Host must wait for
+ * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR
+ * device registers.
+ * 2: INIT_DONE
+ * Host sets this to put device into fully operational D0 power mode.
+ * Host resets this after SW_RESET to put device into low power mode.
+ * 0: MAC_CLOCK_READY
+ * Indicates MAC (ucode processor, etc.) is powered up and can run.
+ * Internal resources are accessible.
+ * NOTE: This does not indicate that the processor is actually running.
+ * NOTE: This does not indicate that 4965 or 3945 has completed
+ * init or post-power-down restore of internal SRAM memory.
+ * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that
+ * SRAM is restored and uCode is in normal operation mode.
+ * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and
+ * do not need to save/restore it.
+ * NOTE: After device reset, this bit remains "0" until host sets
+ * INIT_DONE
+ */
#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001)
#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004)
#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008)
@@ -229,18 +309,58 @@
#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000)
/* EEPROM GP */
-#define CSR_EEPROM_GP_VALID_MSK (0x00000007)
-#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000)
+#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */
#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180)
+#define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000)
+#define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001)
+#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002)
+#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004)
+
+/* One-time-programmable memory general purpose reg */
#define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */
#define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */
#define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */
#define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */
+/* GP REG */
+#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */
+#define CSR_GP_REG_NO_POWER_SAVE (0x00000000)
+#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000)
+#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000)
+#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000)
+
+
/* CSR GIO */
#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002)
-/* UCODE DRV GP */
+/*
+ * UCODE-DRIVER GP (general purpose) mailbox register 1
+ * Host driver and uCode write and/or read this register to communicate with
+ * each other.
+ * Bit fields:
+ * 4: UCODE_DISABLE
+ * Host sets this to request permanent halt of uCode, same as
+ * sending CARD_STATE command with "halt" bit set.
+ * 3: CT_KILL_EXIT
+ * Host sets this to request exit from CT_KILL state, i.e. host thinks
+ * device temperature is low enough to continue normal operation.
+ * 2: CMD_BLOCKED
+ * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL)
+ * to release uCode to clear all Tx and command queues, enter
+ * unassociated mode, and power down.
+ * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit.
+ * 1: SW_BIT_RFKILL
+ * Host sets this when issuing CARD_STATE command to request
+ * device sleep.
+ * 0: MAC_SLEEP
+ * uCode sets this when preparing a power-saving power-down.
+ * uCode resets this when power-up is complete and SRAM is sane.
+ * NOTE: 3945/4965 saves internal SRAM data to host when powering down,
+ * and must restore this data after powering back up.
+ * MAC_SLEEP is the best indication that restore is complete.
+ * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and
+ * do not need to save/restore it.
+ */
#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001)
#define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
@@ -253,7 +373,7 @@
#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002)
-/* GI Chicken Bits */
+/* GIO Chicken Bits (PCI Express bus link power management) */
#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000)
#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000)
@@ -273,8 +393,23 @@
#define CSR_DRAM_INT_TBL_ENABLE (1 << 31)
#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27)
-/*=== HBUS (Host-side Bus) ===*/
+/*
+ * HBUS (Host-side Bus)
+ *
+ * HBUS registers are mapped directly into PCI bus space, but are used
+ * to indirectly access device's internal memory or registers that
+ * may be powered-down.
+ *
+ * Use iwl_write_direct32()/iwl_read_direct32() family for these registers;
+ * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ
+ * to make sure the MAC (uCode processor, etc.) is powered up for accessing
+ * internal resources.
+ *
+ * Do not use iwl_write32()/iwl_read32() family to access these registers;
+ * these provide only simple PCI bus access, without waking up the MAC.
+ */
#define HBUS_BASE (0x400)
+
/*
* Registers for accessing device's internal SRAM memory (e.g. SCD SRAM
* structures, error log, event log, verifying uCode load).
@@ -289,6 +424,10 @@
#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018)
#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c)
+/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */
+#define HBUS_TARG_MBX_C (HBUS_BASE+0x030)
+#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)
+
/*
* Registers for accessing device's internal peripheral registers
* (e.g. SCD, BSM, etc.). First write to address register,
@@ -303,16 +442,12 @@
#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050)
/*
- * Per-Tx-queue write pointer (index, really!) (3945 and 4965).
+ * Per-Tx-queue write pointer (index, really!)
* Indicates index to next TFD that driver will fill (1 past latest filled).
* Bit usage:
* 0-7: queue write index
* 11-8: queue selector
*/
#define HBUS_TARG_WRPTR (HBUS_BASE+0x060)
-#define HBUS_TARG_MBX_C (HBUS_BASE+0x030)
-
-#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004)
-
#endif /* !__iwl_csr_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index cbc62904655..d61293ab67c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -84,9 +84,7 @@ struct iwl_debugfs {
struct dentry *file_interrupt;
struct dentry *file_qos;
struct dentry *file_thermal_throttling;
-#ifdef CONFIG_IWLWIFI_LEDS
struct dentry *file_led;
-#endif
struct dentry *file_disable_ht40;
struct dentry *file_sleep_level_override;
struct dentry *file_current_sleep_command;
@@ -108,6 +106,9 @@ struct iwl_debugfs {
struct dentry *file_sensitivity;
struct dentry *file_chain_noise;
struct dentry *file_tx_power;
+ struct dentry *file_power_save_status;
+ struct dentry *file_clear_ucode_statistics;
+ struct dentry *file_clear_traffic_statistics;
} dbgfs_debug_files;
u32 sram_offset;
u32 sram_len;
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
index a198bcf6102..21e0f6699da 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c
@@ -47,9 +47,9 @@
goto err; \
} while (0)
-#define DEBUGFS_ADD_FILE(name, parent) do { \
+#define DEBUGFS_ADD_FILE(name, parent, mode) do { \
dbgfs->dbgfs_##parent##_files.file_##name = \
- debugfs_create_file(#name, S_IWUSR | S_IRUSR, \
+ debugfs_create_file(#name, mode, \
dbgfs->dir_##parent, priv, \
&iwl_dbgfs_##name##_ops); \
if (!(dbgfs->dbgfs_##parent##_files.file_##name)) \
@@ -131,21 +131,22 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file,
int cnt;
ssize_t ret;
- const size_t bufsz = 100 + sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX);
+ const size_t bufsz = 100 +
+ sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX);
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
pos += scnprintf(buf + pos, bufsz - pos, "Management:\n");
for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) {
pos += scnprintf(buf + pos, bufsz - pos,
- "\t%s\t\t: %u\n",
+ "\t%25s\t\t: %u\n",
get_mgmt_string(cnt),
priv->tx_stats.mgmt[cnt]);
}
pos += scnprintf(buf + pos, bufsz - pos, "Control\n");
for (cnt = 0; cnt < CONTROL_MAX; cnt++) {
pos += scnprintf(buf + pos, bufsz - pos,
- "\t%s\t\t: %u\n",
+ "\t%25s\t\t: %u\n",
get_ctrl_string(cnt),
priv->tx_stats.ctrl[cnt]);
}
@@ -159,7 +160,7 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file,
return ret;
}
-static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file,
+static ssize_t iwl_dbgfs_clear_traffic_statistics_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -174,8 +175,7 @@ static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file,
return -EFAULT;
if (sscanf(buf, "%x", &clear_flag) != 1)
return -EFAULT;
- if (clear_flag == 1)
- iwl_clear_tx_stats(priv);
+ iwl_clear_traffic_stats(priv);
return count;
}
@@ -190,7 +190,7 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file,
int cnt;
ssize_t ret;
const size_t bufsz = 100 +
- sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX);
+ sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX);
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -198,14 +198,14 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file,
pos += scnprintf(buf + pos, bufsz - pos, "Management:\n");
for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) {
pos += scnprintf(buf + pos, bufsz - pos,
- "\t%s\t\t: %u\n",
+ "\t%25s\t\t: %u\n",
get_mgmt_string(cnt),
priv->rx_stats.mgmt[cnt]);
}
pos += scnprintf(buf + pos, bufsz - pos, "Control:\n");
for (cnt = 0; cnt < CONTROL_MAX; cnt++) {
pos += scnprintf(buf + pos, bufsz - pos,
- "\t%s\t\t: %u\n",
+ "\t%25s\t\t: %u\n",
get_ctrl_string(cnt),
priv->rx_stats.ctrl[cnt]);
}
@@ -220,26 +220,6 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file,
return ret;
}
-static ssize_t iwl_dbgfs_rx_statistics_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_priv *priv = file->private_data;
- u32 clear_flag;
- char buf[8];
- int buf_size;
-
- memset(buf, 0, sizeof(buf));
- buf_size = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- if (sscanf(buf, "%x", &clear_flag) != 1)
- return -EFAULT;
- if (clear_flag == 1)
- iwl_clear_rx_stats(priv);
- return count;
-}
-
#define BYTE1_MASK 0x000000ff;
#define BYTE2_MASK 0x0000ffff;
#define BYTE3_MASK 0x00ffffff;
@@ -248,13 +228,29 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
size_t count, loff_t *ppos)
{
u32 val;
- char buf[1024];
+ char *buf;
ssize_t ret;
int i;
int pos = 0;
struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
- const size_t bufsz = sizeof(buf);
-
+ size_t bufsz;
+
+ /* default is to dump the entire data segment */
+ if (!priv->dbgfs->sram_offset && !priv->dbgfs->sram_len) {
+ priv->dbgfs->sram_offset = 0x800000;
+ if (priv->ucode_type == UCODE_INIT)
+ priv->dbgfs->sram_len = priv->ucode_init_data.len;
+ else
+ priv->dbgfs->sram_len = priv->ucode_data.len;
+ }
+ bufsz = 30 + priv->dbgfs->sram_len * sizeof(char) * 10;
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n",
+ priv->dbgfs->sram_len);
+ pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
+ priv->dbgfs->sram_offset);
for (i = priv->dbgfs->sram_len; i > 0; i -= 4) {
val = iwl_read_targ_mem(priv, priv->dbgfs->sram_offset + \
priv->dbgfs->sram_len - i);
@@ -271,11 +267,14 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
break;
}
}
+ if (!(i % 16))
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val);
}
pos += scnprintf(buf + pos, bufsz - pos, "\n");
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
return ret;
}
@@ -335,8 +334,6 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
pos += scnprintf(buf + pos, bufsz - pos,
"flags: 0x%x\n",
station->sta.station_flags_msk);
- pos += scnprintf(buf + pos, bufsz - pos,
- "ps_status: %u\n", station->ps_status);
pos += scnprintf(buf + pos, bufsz - pos, "tid data:\n");
pos += scnprintf(buf + pos, bufsz - pos,
"seq_num\t\ttxq_id");
@@ -383,6 +380,7 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file,
int pos = 0, ofs = 0, buf_size = 0;
const u8 *ptr;
char *buf;
+ u16 eeprom_ver;
size_t eeprom_len = priv->cfg->eeprom_size;
buf_size = 4 * eeprom_len + 256;
@@ -403,9 +401,11 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file,
IWL_ERR(priv, "Can not allocate Buffer\n");
return -ENOMEM;
}
- pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s\n",
+ eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
+ pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s, "
+ "version: 0x%x\n",
(priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
- ? "OTP" : "EEPROM");
+ ? "OTP" : "EEPROM", eeprom_ver);
for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) {
pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs);
hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos,
@@ -436,7 +436,7 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file,
if (sscanf(buf, "%d", &event_log_flag) != 1)
return -EFAULT;
if (event_log_flag == 1)
- priv->cfg->ops->lib->dump_nic_event_log(priv);
+ priv->cfg->ops->lib->dump_nic_event_log(priv, true);
return count;
}
@@ -532,6 +532,8 @@ static ssize_t iwl_dbgfs_status_read(struct file *file,
test_bit(STATUS_INT_ENABLED, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n",
test_bit(STATUS_RF_KILL_HW, &priv->status));
+ pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n",
+ test_bit(STATUS_CT_KILL, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n",
test_bit(STATUS_INIT, &priv->status));
pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n",
@@ -672,7 +674,6 @@ static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf,
return ret;
}
-#ifdef CONFIG_IWLWIFI_LEDS
static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -697,7 +698,6 @@ static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf,
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
return ret;
}
-#endif
static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file,
char __user *user_buf,
@@ -798,15 +798,20 @@ static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file,
* valid here. However, let's not confuse them and present
* IWL_POWER_INDEX_1 as "1", not "0".
*/
- if (value > 0)
+ if (value == 0)
+ return -EINVAL;
+ else if (value > 0)
value -= 1;
if (value != -1 && (value < 0 || value >= IWL_POWER_NUM))
return -EINVAL;
+ if (!iwl_is_ready_rf(priv))
+ return -EAGAIN;
+
priv->power_data.debug_sleep_level_override = value;
- iwl_power_update_mode(priv, false);
+ iwl_power_update_mode(priv, true);
return count;
}
@@ -861,9 +866,7 @@ DEBUGFS_READ_FILE_OPS(channels);
DEBUGFS_READ_FILE_OPS(status);
DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
DEBUGFS_READ_FILE_OPS(qos);
-#ifdef CONFIG_IWLWIFI_LEDS
DEBUGFS_READ_FILE_OPS(led);
-#endif
DEBUGFS_READ_FILE_OPS(thermal_throttling);
DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40);
DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override);
@@ -881,10 +884,14 @@ static ssize_t iwl_dbgfs_traffic_log_read(struct file *file,
struct iwl_rx_queue *rxq = &priv->rxq;
char *buf;
int bufsz = ((IWL_TRAFFIC_ENTRIES * IWL_TRAFFIC_ENTRY_SIZE * 64) * 2) +
- (IWL_MAX_NUM_QUEUES * 32 * 8) + 400;
+ (priv->cfg->num_of_queues * 32 * 8) + 400;
const u8 *ptr;
ssize_t ret;
+ if (!priv->txq) {
+ IWL_ERR(priv, "txq not ready\n");
+ return -EAGAIN;
+ }
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf) {
IWL_ERR(priv, "Can not allocate buffer\n");
@@ -976,8 +983,12 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
int pos = 0;
int cnt;
int ret;
- const size_t bufsz = sizeof(char) * 60 * IWL_MAX_NUM_QUEUES;
+ const size_t bufsz = sizeof(char) * 64 * priv->cfg->num_of_queues;
+ if (!priv->txq) {
+ IWL_ERR(priv, "txq not ready\n");
+ return -EAGAIN;
+ }
buf = kzalloc(bufsz, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -1028,10 +1039,6 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
-#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0)
-#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1)
-#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2)
-
static int iwl_dbgfs_statistics_flag(struct iwl_priv *priv, char *buf,
int bufsz)
{
@@ -1068,17 +1075,17 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file,
sizeof(struct statistics_rx_non_phy) * 20 +
sizeof(struct statistics_rx_ht_phy) * 20 + 400;
ssize_t ret;
- struct statistics_rx_phy *ofdm;
- struct statistics_rx_phy *cck;
- struct statistics_rx_non_phy *general;
- struct statistics_rx_ht_phy *ht;
+ struct statistics_rx_phy *ofdm, *accum_ofdm;
+ struct statistics_rx_phy *cck, *accum_cck;
+ struct statistics_rx_non_phy *general, *accum_general;
+ struct statistics_rx_ht_phy *ht, *accum_ht;
if (!iwl_is_alive(priv))
return -EAGAIN;
/* make request to uCode to retrieve statistics information */
mutex_lock(&priv->mutex);
- ret = iwl_send_statistics_request(priv, 0);
+ ret = iwl_send_statistics_request(priv, CMD_SYNC, false);
mutex_unlock(&priv->mutex);
if (ret) {
@@ -1100,155 +1107,268 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file,
cck = &priv->statistics.rx.cck;
general = &priv->statistics.rx.general;
ht = &priv->statistics.rx.ofdm_ht;
+ accum_ofdm = &priv->accum_statistics.rx.ofdm;
+ accum_cck = &priv->accum_statistics.rx.cck;
+ accum_general = &priv->accum_statistics.rx.general;
+ accum_ht = &priv->accum_statistics.rx.ofdm_ht;
pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz);
pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM:\n");
- pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n",
- le32_to_cpu(ofdm->ina_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n",
- le32_to_cpu(ofdm->fina_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n",
- le32_to_cpu(ofdm->plcp_err));
- pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n",
- le32_to_cpu(ofdm->crc32_err));
- pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n",
- le32_to_cpu(ofdm->overrun_err));
- pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n",
- le32_to_cpu(ofdm->early_overrun_err));
- pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n",
- le32_to_cpu(ofdm->crc32_good));
- pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n",
- le32_to_cpu(ofdm->false_alarm_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n",
- le32_to_cpu(ofdm->fina_sync_err_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n",
- le32_to_cpu(ofdm->sfd_timeout));
- pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n",
- le32_to_cpu(ofdm->fina_timeout));
- pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n",
- le32_to_cpu(ofdm->unresponded_rts));
- pos += scnprintf(buf + pos, bufsz - pos,
- "rxe_frame_limit_overrun: %u\n",
- le32_to_cpu(ofdm->rxe_frame_limit_overrun));
- pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n",
- le32_to_cpu(ofdm->sent_ack_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n",
- le32_to_cpu(ofdm->sent_cts_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n",
- le32_to_cpu(ofdm->sent_ba_rsp_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n",
- le32_to_cpu(ofdm->dsp_self_kill));
- pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n",
- le32_to_cpu(ofdm->mh_format_err));
- pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n",
- le32_to_cpu(ofdm->re_acq_main_rssi_sum));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t\t\tcurrent\t\t\taccumulative\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err);
+ pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "overrun_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->overrun_err),
+ accum_ofdm->overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "early_overrun_err:\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->early_overrun_err),
+ accum_ofdm->early_overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->crc32_good),
+ accum_ofdm->crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "false_alarm_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->false_alarm_cnt),
+ accum_ofdm->false_alarm_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "fina_sync_err_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->fina_sync_err_cnt),
+ accum_ofdm->fina_sync_err_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sfd_timeout:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->sfd_timeout),
+ accum_ofdm->sfd_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "fina_timeout:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->fina_timeout),
+ accum_ofdm->fina_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "unresponded_rts:\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->unresponded_rts),
+ accum_ofdm->unresponded_rts);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "rxe_frame_lmt_ovrun:\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->rxe_frame_limit_overrun),
+ accum_ofdm->rxe_frame_limit_overrun);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sent_ack_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->sent_ack_cnt),
+ accum_ofdm->sent_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sent_cts_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->sent_cts_cnt),
+ accum_ofdm->sent_cts_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sent_ba_rsp_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->sent_ba_rsp_cnt),
+ accum_ofdm->sent_ba_rsp_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "dsp_self_kill:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->dsp_self_kill),
+ accum_ofdm->dsp_self_kill);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "mh_format_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->mh_format_err),
+ accum_ofdm->mh_format_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "re_acq_main_rssi_sum:\t%u\t\t\t%u\n",
+ le32_to_cpu(ofdm->re_acq_main_rssi_sum),
+ accum_ofdm->re_acq_main_rssi_sum);
pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - CCK:\n");
- pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n",
- le32_to_cpu(cck->ina_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n",
- le32_to_cpu(cck->fina_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n",
- le32_to_cpu(cck->plcp_err));
- pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n",
- le32_to_cpu(cck->crc32_err));
- pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n",
- le32_to_cpu(cck->overrun_err));
- pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n",
- le32_to_cpu(cck->early_overrun_err));
- pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n",
- le32_to_cpu(cck->crc32_good));
- pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n",
- le32_to_cpu(cck->false_alarm_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n",
- le32_to_cpu(cck->fina_sync_err_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n",
- le32_to_cpu(cck->sfd_timeout));
- pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n",
- le32_to_cpu(cck->fina_timeout));
- pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n",
- le32_to_cpu(cck->unresponded_rts));
- pos += scnprintf(buf + pos, bufsz - pos,
- "rxe_frame_limit_overrun: %u\n",
- le32_to_cpu(cck->rxe_frame_limit_overrun));
- pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n",
- le32_to_cpu(cck->sent_ack_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n",
- le32_to_cpu(cck->sent_cts_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n",
- le32_to_cpu(cck->sent_ba_rsp_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n",
- le32_to_cpu(cck->dsp_self_kill));
- pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n",
- le32_to_cpu(cck->mh_format_err));
- pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n",
- le32_to_cpu(cck->re_acq_main_rssi_sum));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t\t\tcurrent\t\t\taccumulative\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->plcp_err), accum_cck->plcp_err);
+ pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->crc32_err), accum_cck->crc32_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "overrun_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->overrun_err),
+ accum_cck->overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "early_overrun_err:\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->early_overrun_err),
+ accum_cck->early_overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->crc32_good), accum_cck->crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "false_alarm_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->false_alarm_cnt),
+ accum_cck->false_alarm_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "fina_sync_err_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->fina_sync_err_cnt),
+ accum_cck->fina_sync_err_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sfd_timeout:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->sfd_timeout),
+ accum_cck->sfd_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "fina_timeout:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->fina_timeout),
+ accum_cck->fina_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "unresponded_rts:\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->unresponded_rts),
+ accum_cck->unresponded_rts);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "rxe_frame_lmt_ovrun:\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->rxe_frame_limit_overrun),
+ accum_cck->rxe_frame_limit_overrun);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sent_ack_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->sent_ack_cnt),
+ accum_cck->sent_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sent_cts_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->sent_cts_cnt),
+ accum_cck->sent_cts_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sent_ba_rsp_cnt:\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->sent_ba_rsp_cnt),
+ accum_cck->sent_ba_rsp_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "dsp_self_kill:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->dsp_self_kill),
+ accum_cck->dsp_self_kill);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "mh_format_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->mh_format_err),
+ accum_cck->mh_format_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "re_acq_main_rssi_sum:\t%u\t\t\t%u\n",
+ le32_to_cpu(cck->re_acq_main_rssi_sum),
+ accum_cck->re_acq_main_rssi_sum);
pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - GENERAL:\n");
- pos += scnprintf(buf + pos, bufsz - pos, "bogus_cts: %u\n",
- le32_to_cpu(general->bogus_cts));
- pos += scnprintf(buf + pos, bufsz - pos, "bogus_ack: %u\n",
- le32_to_cpu(general->bogus_ack));
- pos += scnprintf(buf + pos, bufsz - pos, "non_bssid_frames: %u\n",
- le32_to_cpu(general->non_bssid_frames));
- pos += scnprintf(buf + pos, bufsz - pos, "filtered_frames: %u\n",
- le32_to_cpu(general->filtered_frames));
- pos += scnprintf(buf + pos, bufsz - pos, "non_channel_beacons: %u\n",
- le32_to_cpu(general->non_channel_beacons));
- pos += scnprintf(buf + pos, bufsz - pos, "channel_beacons: %u\n",
- le32_to_cpu(general->channel_beacons));
- pos += scnprintf(buf + pos, bufsz - pos, "num_missed_bcon: %u\n",
- le32_to_cpu(general->num_missed_bcon));
- pos += scnprintf(buf + pos, bufsz - pos,
- "adc_rx_saturation_time: %u\n",
- le32_to_cpu(general->adc_rx_saturation_time));
- pos += scnprintf(buf + pos, bufsz - pos,
- "ina_detection_search_time: %u\n",
- le32_to_cpu(general->ina_detection_search_time));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_a: %u\n",
- le32_to_cpu(general->beacon_silence_rssi_a));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_b: %u\n",
- le32_to_cpu(general->beacon_silence_rssi_b));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_c: %u\n",
- le32_to_cpu(general->beacon_silence_rssi_c));
- pos += scnprintf(buf + pos, bufsz - pos,
- "interference_data_flag: %u\n",
- le32_to_cpu(general->interference_data_flag));
- pos += scnprintf(buf + pos, bufsz - pos, "channel_load: %u\n",
- le32_to_cpu(general->channel_load));
- pos += scnprintf(buf + pos, bufsz - pos, "dsp_false_alarms: %u\n",
- le32_to_cpu(general->dsp_false_alarms));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_a: %u\n",
- le32_to_cpu(general->beacon_rssi_a));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_b: %u\n",
- le32_to_cpu(general->beacon_rssi_b));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_c: %u\n",
- le32_to_cpu(general->beacon_rssi_c));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_a: %u\n",
- le32_to_cpu(general->beacon_energy_a));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_b: %u\n",
- le32_to_cpu(general->beacon_energy_b));
- pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_c: %u\n",
- le32_to_cpu(general->beacon_energy_c));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t\t\tcurrent\t\t\taccumulative\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "bogus_cts:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->bogus_cts),
+ accum_general->bogus_cts);
+ pos += scnprintf(buf + pos, bufsz - pos, "bogus_ack:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->bogus_ack),
+ accum_general->bogus_ack);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "non_bssid_frames:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->non_bssid_frames),
+ accum_general->non_bssid_frames);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "filtered_frames:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->filtered_frames),
+ accum_general->filtered_frames);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "non_channel_beacons:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->non_channel_beacons),
+ accum_general->non_channel_beacons);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "channel_beacons:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->channel_beacons),
+ accum_general->channel_beacons);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "num_missed_bcon:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->num_missed_bcon),
+ accum_general->num_missed_bcon);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "adc_rx_saturation_time:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->adc_rx_saturation_time),
+ accum_general->adc_rx_saturation_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ina_detect_search_tm:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->ina_detection_search_time),
+ accum_general->ina_detection_search_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_silence_rssi_a:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_silence_rssi_a),
+ accum_general->beacon_silence_rssi_a);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_silence_rssi_b:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_silence_rssi_b),
+ accum_general->beacon_silence_rssi_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_silence_rssi_c:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_silence_rssi_c),
+ accum_general->beacon_silence_rssi_c);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "interference_data_flag:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->interference_data_flag),
+ accum_general->interference_data_flag);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "channel_load:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->channel_load),
+ accum_general->channel_load);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "dsp_false_alarms:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->dsp_false_alarms),
+ accum_general->dsp_false_alarms);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_rssi_a:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_rssi_a),
+ accum_general->beacon_rssi_a);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_rssi_b:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_rssi_b),
+ accum_general->beacon_rssi_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_rssi_c:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_rssi_c),
+ accum_general->beacon_rssi_c);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_energy_a:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_energy_a),
+ accum_general->beacon_energy_a);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_energy_b:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_energy_b),
+ accum_general->beacon_energy_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "beacon_energy_c:\t%u\t\t\t%u\n",
+ le32_to_cpu(general->beacon_energy_c),
+ accum_general->beacon_energy_c);
pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM_HT:\n");
- pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n",
- le32_to_cpu(ht->plcp_err));
- pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n",
- le32_to_cpu(ht->overrun_err));
- pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n",
- le32_to_cpu(ht->early_overrun_err));
- pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n",
- le32_to_cpu(ht->crc32_good));
- pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n",
- le32_to_cpu(ht->crc32_err));
- pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n",
- le32_to_cpu(ht->mh_format_err));
- pos += scnprintf(buf + pos, bufsz - pos, "agg_crc32_good: %u\n",
- le32_to_cpu(ht->agg_crc32_good));
- pos += scnprintf(buf + pos, bufsz - pos, "agg_mpdu_cnt: %u\n",
- le32_to_cpu(ht->agg_mpdu_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt: %u\n",
- le32_to_cpu(ht->agg_cnt));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t\t\tcurrent\t\t\taccumulative\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "plcp_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->plcp_err), accum_ht->plcp_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "overrun_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->overrun_err), accum_ht->overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "early_overrun_err:\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->early_overrun_err),
+ accum_ht->early_overrun_err);
+ pos += scnprintf(buf + pos, bufsz - pos, "crc32_good:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->crc32_good), accum_ht->crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos, "crc32_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->crc32_err), accum_ht->crc32_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "mh_format_err:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->mh_format_err),
+ accum_ht->mh_format_err);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg_crc32_good:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->agg_crc32_good),
+ accum_ht->agg_crc32_good);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg_mpdu_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->agg_mpdu_cnt),
+ accum_ht->agg_mpdu_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
@@ -1264,14 +1384,14 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file,
char *buf;
int bufsz = (sizeof(struct statistics_tx) * 24) + 250;
ssize_t ret;
- struct statistics_tx *tx;
+ struct statistics_tx *tx, *accum_tx;
if (!iwl_is_alive(priv))
return -EAGAIN;
/* make request to uCode to retrieve statistics information */
mutex_lock(&priv->mutex);
- ret = iwl_send_statistics_request(priv, 0);
+ ret = iwl_send_statistics_request(priv, CMD_SYNC, false);
mutex_unlock(&priv->mutex);
if (ret) {
@@ -1290,62 +1410,107 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file,
* might not reflect the current uCode activity
*/
tx = &priv->statistics.tx;
+ accum_tx = &priv->accum_statistics.tx;
pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz);
pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Tx:\n");
- pos += scnprintf(buf + pos, bufsz - pos, "preamble: %u\n",
- le32_to_cpu(tx->preamble_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "rx_detected_cnt: %u\n",
- le32_to_cpu(tx->rx_detected_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_defer_cnt: %u\n",
- le32_to_cpu(tx->bt_prio_defer_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_kill_cnt: %u\n",
- le32_to_cpu(tx->bt_prio_kill_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "few_bytes_cnt: %u\n",
- le32_to_cpu(tx->few_bytes_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout: %u\n",
- le32_to_cpu(tx->cts_timeout));
- pos += scnprintf(buf + pos, bufsz - pos, "ack_timeout: %u\n",
- le32_to_cpu(tx->ack_timeout));
- pos += scnprintf(buf + pos, bufsz - pos, "expected_ack_cnt: %u\n",
- le32_to_cpu(tx->expected_ack_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "actual_ack_cnt: %u\n",
- le32_to_cpu(tx->actual_ack_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "dump_msdu_cnt: %u\n",
- le32_to_cpu(tx->dump_msdu_cnt));
- pos += scnprintf(buf + pos, bufsz - pos,
- "burst_abort_next_frame_mismatch_cnt: %u\n",
- le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt));
- pos += scnprintf(buf + pos, bufsz - pos,
- "burst_abort_missing_next_frame_cnt: %u\n",
- le32_to_cpu(tx->burst_abort_missing_next_frame_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout_collision: %u\n",
- le32_to_cpu(tx->cts_timeout_collision));
- pos += scnprintf(buf + pos, bufsz - pos,
- "ack_or_ba_timeout_collision: %u\n",
- le32_to_cpu(tx->ack_or_ba_timeout_collision));
- pos += scnprintf(buf + pos, bufsz - pos, "agg ba_timeout: %u\n",
- le32_to_cpu(tx->agg.ba_timeout));
- pos += scnprintf(buf + pos, bufsz - pos,
- "agg ba_reschedule_frames: %u\n",
- le32_to_cpu(tx->agg.ba_reschedule_frames));
- pos += scnprintf(buf + pos, bufsz - pos,
- "agg scd_query_agg_frame_cnt: %u\n",
- le32_to_cpu(tx->agg.scd_query_agg_frame_cnt));
- pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_no_agg: %u\n",
- le32_to_cpu(tx->agg.scd_query_no_agg));
- pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_agg: %u\n",
- le32_to_cpu(tx->agg.scd_query_agg));
- pos += scnprintf(buf + pos, bufsz - pos,
- "agg scd_query_mismatch: %u\n",
- le32_to_cpu(tx->agg.scd_query_mismatch));
- pos += scnprintf(buf + pos, bufsz - pos, "agg frame_not_ready: %u\n",
- le32_to_cpu(tx->agg.frame_not_ready));
- pos += scnprintf(buf + pos, bufsz - pos, "agg underrun: %u\n",
- le32_to_cpu(tx->agg.underrun));
- pos += scnprintf(buf + pos, bufsz - pos, "agg bt_prio_kill: %u\n",
- le32_to_cpu(tx->agg.bt_prio_kill));
- pos += scnprintf(buf + pos, bufsz - pos, "agg rx_ba_rsp_cnt: %u\n",
- le32_to_cpu(tx->agg.rx_ba_rsp_cnt));
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t\t\tcurrent\t\t\taccumulative\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "preamble:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->preamble_cnt),
+ accum_tx->preamble_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "rx_detected_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->rx_detected_cnt),
+ accum_tx->rx_detected_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "bt_prio_defer_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->bt_prio_defer_cnt),
+ accum_tx->bt_prio_defer_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "bt_prio_kill_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->bt_prio_kill_cnt),
+ accum_tx->bt_prio_kill_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "few_bytes_cnt:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->few_bytes_cnt),
+ accum_tx->few_bytes_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "cts_timeout:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ack_timeout:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->ack_timeout),
+ accum_tx->ack_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "expected_ack_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->expected_ack_cnt),
+ accum_tx->expected_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "actual_ack_cnt:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->actual_ack_cnt),
+ accum_tx->actual_ack_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "dump_msdu_cnt:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->dump_msdu_cnt),
+ accum_tx->dump_msdu_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "abort_nxt_frame_mismatch:"
+ "\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt),
+ accum_tx->burst_abort_next_frame_mismatch_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "abort_missing_nxt_frame:"
+ "\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->burst_abort_missing_next_frame_cnt),
+ accum_tx->burst_abort_missing_next_frame_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "cts_timeout_collision:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->cts_timeout_collision),
+ accum_tx->cts_timeout_collision);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "ack_ba_timeout_collision:\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->ack_or_ba_timeout_collision),
+ accum_tx->ack_or_ba_timeout_collision);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg ba_timeout:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.ba_timeout),
+ accum_tx->agg.ba_timeout);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg ba_resched_frames:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.ba_reschedule_frames),
+ accum_tx->agg.ba_reschedule_frames);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg scd_query_agg_frame:\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.scd_query_agg_frame_cnt),
+ accum_tx->agg.scd_query_agg_frame_cnt);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg scd_query_no_agg:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.scd_query_no_agg),
+ accum_tx->agg.scd_query_no_agg);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg scd_query_agg:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.scd_query_agg),
+ accum_tx->agg.scd_query_agg);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg scd_query_mismatch:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.scd_query_mismatch),
+ accum_tx->agg.scd_query_mismatch);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg frame_not_ready:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.frame_not_ready),
+ accum_tx->agg.frame_not_ready);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg underrun:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.underrun),
+ accum_tx->agg.underrun);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg bt_prio_kill:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.bt_prio_kill),
+ accum_tx->agg.bt_prio_kill);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "agg rx_ba_rsp_cnt:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(tx->agg.rx_ba_rsp_cnt),
+ accum_tx->agg.rx_ba_rsp_cnt);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
@@ -1361,16 +1526,16 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file,
char *buf;
int bufsz = sizeof(struct statistics_general) * 4 + 250;
ssize_t ret;
- struct statistics_general *general;
- struct statistics_dbg *dbg;
- struct statistics_div *div;
+ struct statistics_general *general, *accum_general;
+ struct statistics_dbg *dbg, *accum_dbg;
+ struct statistics_div *div, *accum_div;
if (!iwl_is_alive(priv))
return -EAGAIN;
/* make request to uCode to retrieve statistics information */
mutex_lock(&priv->mutex);
- ret = iwl_send_statistics_request(priv, 0);
+ ret = iwl_send_statistics_request(priv, CMD_SYNC, false);
mutex_unlock(&priv->mutex);
if (ret) {
@@ -1391,34 +1556,53 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file,
general = &priv->statistics.general;
dbg = &priv->statistics.general.dbg;
div = &priv->statistics.general.div;
+ accum_general = &priv->accum_statistics.general;
+ accum_dbg = &priv->accum_statistics.general.dbg;
+ accum_div = &priv->accum_statistics.general.div;
pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz);
pos += scnprintf(buf + pos, bufsz - pos, "Statistics_General:\n");
- pos += scnprintf(buf + pos, bufsz - pos, "temperature: %u\n",
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t\t\tcurrent\t\t\taccumulative\n");
+ pos += scnprintf(buf + pos, bufsz - pos, "temperature:\t\t\t%u\n",
le32_to_cpu(general->temperature));
- pos += scnprintf(buf + pos, bufsz - pos, "temperature_m: %u\n",
+ pos += scnprintf(buf + pos, bufsz - pos, "temperature_m:\t\t\t%u\n",
le32_to_cpu(general->temperature_m));
- pos += scnprintf(buf + pos, bufsz - pos, "burst_check: %u\n",
- le32_to_cpu(dbg->burst_check));
- pos += scnprintf(buf + pos, bufsz - pos, "burst_count: %u\n",
- le32_to_cpu(dbg->burst_count));
- pos += scnprintf(buf + pos, bufsz - pos, "sleep_time: %u\n",
- le32_to_cpu(general->sleep_time));
- pos += scnprintf(buf + pos, bufsz - pos, "slots_out: %u\n",
- le32_to_cpu(general->slots_out));
- pos += scnprintf(buf + pos, bufsz - pos, "slots_idle: %u\n",
- le32_to_cpu(general->slots_idle));
- pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp: %u\n",
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "burst_check:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(dbg->burst_check),
+ accum_dbg->burst_check);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "burst_count:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(dbg->burst_count),
+ accum_dbg->burst_count);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "sleep_time:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->sleep_time),
+ accum_general->sleep_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "slots_out:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->slots_out),
+ accum_general->slots_out);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "slots_idle:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->slots_idle),
+ accum_general->slots_idle);
+ pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n",
le32_to_cpu(general->ttl_timestamp));
- pos += scnprintf(buf + pos, bufsz - pos, "tx_on_a: %u\n",
- le32_to_cpu(div->tx_on_a));
- pos += scnprintf(buf + pos, bufsz - pos, "tx_on_b: %u\n",
- le32_to_cpu(div->tx_on_b));
- pos += scnprintf(buf + pos, bufsz - pos, "exec_time: %u\n",
- le32_to_cpu(div->exec_time));
- pos += scnprintf(buf + pos, bufsz - pos, "probe_time: %u\n",
- le32_to_cpu(div->probe_time));
- pos += scnprintf(buf + pos, bufsz - pos, "rx_enable_counter: %u\n",
- le32_to_cpu(general->rx_enable_counter));
+ pos += scnprintf(buf + pos, bufsz - pos, "tx_on_a:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(div->tx_on_a), accum_div->tx_on_a);
+ pos += scnprintf(buf + pos, bufsz - pos, "tx_on_b:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(div->tx_on_b), accum_div->tx_on_b);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "exec_time:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(div->exec_time), accum_div->exec_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "probe_time:\t\t\t%u\t\t\t%u\n",
+ le32_to_cpu(div->probe_time), accum_div->probe_time);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "rx_enable_counter:\t\t%u\t\t\t%u\n",
+ le32_to_cpu(general->rx_enable_counter),
+ accum_general->rx_enable_counter);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
return ret;
@@ -1579,7 +1763,7 @@ static ssize_t iwl_dbgfs_tx_power_read(struct file *file,
else {
/* make request to uCode to retrieve statistics information */
mutex_lock(&priv->mutex);
- ret = iwl_send_statistics_request(priv, 0);
+ ret = iwl_send_statistics_request(priv, CMD_SYNC, false);
mutex_unlock(&priv->mutex);
if (ret) {
@@ -1614,8 +1798,55 @@ static ssize_t iwl_dbgfs_tx_power_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
-DEBUGFS_READ_WRITE_FILE_OPS(rx_statistics);
-DEBUGFS_READ_WRITE_FILE_OPS(tx_statistics);
+static ssize_t iwl_dbgfs_power_save_status_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+ char buf[60];
+ int pos = 0;
+ const size_t bufsz = sizeof(buf);
+ u32 pwrsave_status;
+
+ pwrsave_status = iwl_read32(priv, CSR_GP_CNTRL) &
+ CSR_GP_REG_POWER_SAVE_STATUS_MSK;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: ");
+ pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
+ (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" :
+ (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" :
+ (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" :
+ "error");
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_priv *priv = file->private_data;
+ char buf[8];
+ int buf_size;
+ int clear;
+
+ memset(buf, 0, sizeof(buf));
+ buf_size = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ if (sscanf(buf, "%d", &clear) != 1)
+ return -EFAULT;
+
+ /* make request to uCode to retrieve statistics information */
+ mutex_lock(&priv->mutex);
+ iwl_send_statistics_request(priv, CMD_SYNC, true);
+ mutex_unlock(&priv->mutex);
+
+ return count;
+}
+
+DEBUGFS_READ_FILE_OPS(rx_statistics);
+DEBUGFS_READ_FILE_OPS(tx_statistics);
DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
DEBUGFS_READ_FILE_OPS(rx_queue);
DEBUGFS_READ_FILE_OPS(tx_queue);
@@ -1625,6 +1856,9 @@ DEBUGFS_READ_FILE_OPS(ucode_general_stats);
DEBUGFS_READ_FILE_OPS(sensitivity);
DEBUGFS_READ_FILE_OPS(chain_noise);
DEBUGFS_READ_FILE_OPS(tx_power);
+DEBUGFS_READ_FILE_OPS(power_save_status);
+DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics);
+DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics);
/*
* Create the debugfs files and directories
@@ -1653,33 +1887,34 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_DIR(data, dbgfs->dir_drv);
DEBUGFS_ADD_DIR(rf, dbgfs->dir_drv);
DEBUGFS_ADD_DIR(debug, dbgfs->dir_drv);
- DEBUGFS_ADD_FILE(nvm, data);
- DEBUGFS_ADD_FILE(sram, data);
- DEBUGFS_ADD_FILE(log_event, data);
- DEBUGFS_ADD_FILE(stations, data);
- DEBUGFS_ADD_FILE(channels, data);
- DEBUGFS_ADD_FILE(status, data);
- DEBUGFS_ADD_FILE(interrupt, data);
- DEBUGFS_ADD_FILE(qos, data);
-#ifdef CONFIG_IWLWIFI_LEDS
- DEBUGFS_ADD_FILE(led, data);
-#endif
- DEBUGFS_ADD_FILE(sleep_level_override, data);
- DEBUGFS_ADD_FILE(current_sleep_command, data);
- DEBUGFS_ADD_FILE(thermal_throttling, data);
- DEBUGFS_ADD_FILE(disable_ht40, data);
- DEBUGFS_ADD_FILE(rx_statistics, debug);
- DEBUGFS_ADD_FILE(tx_statistics, debug);
- DEBUGFS_ADD_FILE(traffic_log, debug);
- DEBUGFS_ADD_FILE(rx_queue, debug);
- DEBUGFS_ADD_FILE(tx_queue, debug);
- DEBUGFS_ADD_FILE(tx_power, debug);
+ DEBUGFS_ADD_FILE(nvm, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(sram, data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(log_event, data, S_IWUSR);
+ DEBUGFS_ADD_FILE(stations, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(channels, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(status, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(interrupt, data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(qos, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(led, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(sleep_level_override, data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(current_sleep_command, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(thermal_throttling, data, S_IRUSR);
+ DEBUGFS_ADD_FILE(disable_ht40, data, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(rx_statistics, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(tx_statistics, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(traffic_log, debug, S_IWUSR | S_IRUSR);
+ DEBUGFS_ADD_FILE(rx_queue, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(tx_queue, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(tx_power, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(power_save_status, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(clear_ucode_statistics, debug, S_IWUSR);
+ DEBUGFS_ADD_FILE(clear_traffic_statistics, debug, S_IWUSR);
if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
- DEBUGFS_ADD_FILE(ucode_rx_stats, debug);
- DEBUGFS_ADD_FILE(ucode_tx_stats, debug);
- DEBUGFS_ADD_FILE(ucode_general_stats, debug);
- DEBUGFS_ADD_FILE(sensitivity, debug);
- DEBUGFS_ADD_FILE(chain_noise, debug);
+ DEBUGFS_ADD_FILE(ucode_rx_stats, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(ucode_tx_stats, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(ucode_general_stats, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(sensitivity, debug, S_IRUSR);
+ DEBUGFS_ADD_FILE(chain_noise, debug, S_IRUSR);
}
DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal);
DEBUGFS_ADD_BOOL(disable_chain_noise, rf,
@@ -1716,9 +1951,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_interrupt);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_qos);
-#ifdef CONFIG_IWLWIFI_LEDS
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_led);
-#endif
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_thermal_throttling);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_disable_ht40);
DEBUGFS_REMOVE(priv->dbgfs->dir_data);
@@ -1728,6 +1961,11 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_queue);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue);
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_power_save_status);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
+ file_clear_ucode_statistics);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
+ file_clear_traffic_statistics);
if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
file_ucode_rx_stats);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 028d5059955..2673e9a4db9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -43,7 +43,6 @@
#include "iwl-debug.h"
#include "iwl-4965-hw.h"
#include "iwl-3945-hw.h"
-#include "iwl-3945-led.h"
#include "iwl-led.h"
#include "iwl-power.h"
#include "iwl-agn-rs.h"
@@ -53,21 +52,23 @@ extern struct iwl_cfg iwl4965_agn_cfg;
extern struct iwl_cfg iwl5300_agn_cfg;
extern struct iwl_cfg iwl5100_agn_cfg;
extern struct iwl_cfg iwl5350_agn_cfg;
-extern struct iwl_cfg iwl5100_bg_cfg;
+extern struct iwl_cfg iwl5100_bgn_cfg;
extern struct iwl_cfg iwl5100_abg_cfg;
extern struct iwl_cfg iwl5150_agn_cfg;
-extern struct iwl_cfg iwl6000h_2agn_cfg;
+extern struct iwl_cfg iwl5150_abg_cfg;
extern struct iwl_cfg iwl6000i_2agn_cfg;
+extern struct iwl_cfg iwl6000i_2abg_cfg;
+extern struct iwl_cfg iwl6000i_2bg_cfg;
extern struct iwl_cfg iwl6000_3agn_cfg;
extern struct iwl_cfg iwl6050_2agn_cfg;
-extern struct iwl_cfg iwl6050_3agn_cfg;
+extern struct iwl_cfg iwl6050_2abg_cfg;
extern struct iwl_cfg iwl1000_bgn_cfg;
+extern struct iwl_cfg iwl1000_bg_cfg;
struct iwl_tx_queue;
/* shared structures from iwl-5000.c */
extern struct iwl_mod_params iwl50_mod_params;
-extern struct iwl_ops iwl5000_ops;
extern struct iwl_ucode_ops iwl5000_ucode;
extern struct iwl_lib_ops iwl5000_lib;
extern struct iwl_hcmd_ops iwl5000_hcmd;
@@ -81,9 +82,6 @@ extern void iwl5000_rts_tx_cmd_flag(struct ieee80211_tx_info *info,
__le32 *tx_flags);
extern int iwl5000_calc_rssi(struct iwl_priv *priv,
struct iwl_rx_phy_res *rx_resp);
-extern int iwl5000_apm_init(struct iwl_priv *priv);
-extern void iwl5000_apm_stop(struct iwl_priv *priv);
-extern int iwl5000_apm_reset(struct iwl_priv *priv);
extern void iwl5000_nic_config(struct iwl_priv *priv);
extern u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv);
extern const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
@@ -144,12 +142,13 @@ extern void iwl5000_temperature(struct iwl_priv *priv);
#define DEFAULT_LONG_RETRY_LIMIT 4U
struct iwl_rx_mem_buffer {
- dma_addr_t real_dma_addr;
- dma_addr_t aligned_dma_addr;
- struct sk_buff *skb;
+ dma_addr_t page_dma;
+ struct page *page;
struct list_head list;
};
+#define rxb_addr(r) page_address(r->page)
+
/* defined below */
struct iwl_device_cmd;
@@ -165,7 +164,7 @@ struct iwl_cmd_meta {
*/
void (*callback)(struct iwl_priv *priv,
struct iwl_device_cmd *cmd,
- struct sk_buff *skb);
+ struct iwl_rx_packet *pkt);
/* The CMD_SIZE_HUGE flag bit indicates that the command
* structure is stored at the end of the shared queue memory. */
@@ -293,9 +292,6 @@ struct iwl_channel_info {
/* HT40 channel info */
s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */
- s8 ht40_curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */
- s8 ht40_min_power; /* always 0 */
- s8 ht40_scan_power; /* (dBm) eeprom, direct scans, any rate */
u8 ht40_flags; /* flags copied from EEPROM */
u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */
@@ -321,6 +317,13 @@ struct iwl_channel_info {
* queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */
#define IWL_MIN_NUM_QUEUES 10
+/*
+ * Queue #4 is the command queue for 3945/4965/5x00/1000/6x00,
+ * the driver maps it into the appropriate device FIFO for the
+ * uCode.
+ */
+#define IWL_CMD_QUEUE_NUM 4
+
/* Power management (not Tx power) structures */
enum iwl_pwr_src {
@@ -356,7 +359,14 @@ enum {
CMD_WANT_SKB = (1 << 2),
};
-#define IWL_CMD_MAX_PAYLOAD 320
+#define DEF_CMD_PAYLOAD_SIZE 320
+
+/*
+ * IWL_LINK_HDR_MAX should include ieee80211_hdr, radiotap header,
+ * SNAP header and alignment. It should also be big enough for 802.11
+ * control frames.
+ */
+#define IWL_LINK_HDR_MAX 64
/**
* struct iwl_device_cmd
@@ -373,7 +383,8 @@ struct iwl_device_cmd {
u16 val16;
u32 val32;
struct iwl_tx_cmd tx;
- u8 payload[IWL_CMD_MAX_PAYLOAD];
+ struct iwl6000_channel_switch_cmd chswitch;
+ u8 payload[DEF_CMD_PAYLOAD_SIZE];
} __attribute__ ((packed)) cmd;
} __attribute__ ((packed));
@@ -382,21 +393,15 @@ struct iwl_device_cmd {
struct iwl_host_cmd {
const void *data;
- struct sk_buff *reply_skb;
+ unsigned long reply_page;
void (*callback)(struct iwl_priv *priv,
struct iwl_device_cmd *cmd,
- struct sk_buff *skb);
+ struct iwl_rx_packet *pkt);
u32 flags;
u16 len;
u8 id;
};
-/*
- * RX related structures and functions
- */
-#define RX_FREE_BUFFERS 64
-#define RX_LOW_WATERMARK 8
-
#define SUP_RATE_11A_MAX_NUM_CHANNELS 8
#define SUP_RATE_11B_MAX_NUM_CHANNELS 4
#define SUP_RATE_11G_MAX_NUM_CHANNELS 12
@@ -502,12 +507,11 @@ union iwl_ht_rate_supp {
#define CFG_HT_MPDU_DENSITY_4USEC (0x5)
#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC
-struct iwl_ht_info {
+struct iwl_ht_config {
/* self configuration data */
- u8 is_ht;
- u8 supported_chan_width;
- u8 sm_ps;
- struct ieee80211_mcs_info mcs;
+ bool is_ht;
+ bool is_40mhz;
+ bool single_chain_sufficient;
/* BSS related data */
u8 extension_chan_offset;
u8 ht_protection;
@@ -541,26 +545,27 @@ struct iwl_qos_info {
struct iwl_qosparam_cmd def_qos_parm;
};
-#define STA_PS_STATUS_WAKE 0
-#define STA_PS_STATUS_SLEEP 1
-
-
-struct iwl3945_station_entry {
- struct iwl3945_addsta_cmd sta;
- struct iwl_tid_data tid[MAX_TID_COUNT];
- u8 used;
- u8 ps_status;
- struct iwl_hw_key keyinfo;
-};
-
struct iwl_station_entry {
struct iwl_addsta_cmd sta;
struct iwl_tid_data tid[MAX_TID_COUNT];
u8 used;
- u8 ps_status;
struct iwl_hw_key keyinfo;
};
+/*
+ * iwl_station_priv: Driver's private station information
+ *
+ * When mac80211 creates a station it reserves some space (hw->sta_data_size)
+ * in the structure for use by driver. This structure is places in that
+ * space.
+ */
+struct iwl_station_priv {
+ struct iwl_lq_sta lq_sta;
+ atomic_t pending_frames;
+ bool client;
+ bool asleep;
+};
+
/* one for each uCode image (inst/data, boot/init/runtime) */
struct fw_desc {
void *v_addr; /* access by driver */
@@ -622,6 +627,10 @@ struct iwl_sensitivity_ranges {
u16 auto_corr_max_cck_mrc;
u16 auto_corr_min_cck;
u16 auto_corr_min_cck_mrc;
+
+ u16 barker_corr_th_min;
+ u16 barker_corr_th_min_mrc;
+ u16 nrg_th_cca;
};
@@ -639,7 +648,7 @@ struct iwl_sensitivity_ranges {
* @valid_tx/rx_ant: usable antennas
* @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2)
* @max_rxq_log: Log-base-2 of max_rxq_size
- * @rx_buf_size: Rx buffer size
+ * @rx_page_order: Rx buffer page order
* @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR
* @max_stations:
* @bcast_sta_id:
@@ -662,9 +671,8 @@ struct iwl_hw_params {
u8 valid_rx_ant;
u16 max_rxq_size;
u16 max_rxq_log;
- u32 rx_buf_size;
+ u32 rx_page_order;
u32 rx_wrt_ptr_reg;
- u32 max_pkt_size;
u8 max_stations;
u8 bcast_sta_id;
u8 ht40_channel;
@@ -711,7 +719,11 @@ static inline int iwl_queue_used(const struct iwl_queue *q, int i)
static inline u8 get_cmd_index(struct iwl_queue *q, u32 index, int is_huge)
{
- /* This is for scan command, the big buffer at end of command array */
+ /*
+ * This is for init calibration result and scan command which
+ * required buffer > TFD_MAX_PAYLOAD_SIZE,
+ * the big buffer at end of command array
+ */
if (is_huge)
return q->n_window; /* must be power of 2 */
@@ -726,9 +738,6 @@ struct iwl_dma_ptr {
size_t size;
};
-#define IWL_CHANNEL_WIDTH_20MHZ 0
-#define IWL_CHANNEL_WIDTH_40MHZ 1
-
#define IWL_OPERATION_MODE_AUTO 0
#define IWL_OPERATION_MODE_HT_ONLY 1
#define IWL_OPERATION_MODE_MIXED 2
@@ -741,7 +750,8 @@ struct iwl_dma_ptr {
/* Sensitivity and chain noise calibration */
#define INITIALIZATION_VALUE 0xFFFF
-#define CAL_NUM_OF_BEACONS 20
+#define IWL4965_CAL_NUM_BEACONS 20
+#define IWL_CAL_NUM_BEACONS 16
#define MAXIMUM_ALLOWED_PATHLOSS 15
#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3
@@ -845,6 +855,10 @@ struct iwl_sensitivity_data {
s32 nrg_auto_corr_silence_diff;
u32 num_in_cck_no_fa;
u32 nrg_th_ofdm;
+
+ u16 barker_corr_th_min;
+ u16 barker_corr_th_min_mrc;
+ u16 nrg_th_cca;
};
/* Chain noise (differential Rx gain) calib data */
@@ -894,13 +908,11 @@ enum iwl_access_mode {
/**
* enum iwl_pa_type - Power Amplifier type
* @IWL_PA_SYSTEM: based on uCode configuration
- * @IWL_PA_HYBRID: use both Internal and external PA
* @IWL_PA_INTERNAL: use Internal only
*/
enum iwl_pa_type {
IWL_PA_SYSTEM = 0,
- IWL_PA_HYBRID = 1,
- IWL_PA_INTERNAL = 2,
+ IWL_PA_INTERNAL = 1,
};
/* interrupt statistics */
@@ -961,7 +973,16 @@ struct traffic_stats {
};
#endif
-#define IWL_MAX_NUM_QUEUES 20 /* FIXME: do dynamic allocation */
+/*
+ * iwl_switch_rxon: "channel switch" structure
+ *
+ * @ switch_in_progress: channel switch in progress
+ * @ channel: new channel
+ */
+struct iwl_switch_rxon {
+ bool switch_in_progress;
+ __le16 channel;
+};
struct iwl_priv {
@@ -976,7 +997,7 @@ struct iwl_priv {
int frames_count;
enum ieee80211_band band;
- int alloc_rxb_skb;
+ int alloc_rxb_page;
void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb);
@@ -1056,21 +1077,18 @@ struct iwl_priv {
const struct iwl_rxon_cmd active_rxon;
struct iwl_rxon_cmd staging_rxon;
- struct iwl_rxon_cmd recovery_rxon;
+ struct iwl_switch_rxon switch_rxon;
/* 1st responses from initialize and runtime uCode images.
* 4965's initialize alive response contains some calibration data. */
struct iwl_init_alive_resp card_alive_init;
struct iwl_alive_resp card_alive;
-#ifdef CONFIG_IWLWIFI_LEDS
unsigned long last_blink_time;
u8 last_blink_rate;
u8 allow_blinking;
u64 led_tpt;
- struct iwl_led led[IWL_LED_TRG_MAX];
- unsigned int rxtxpackets;
-#endif
+
u16 active_rate;
u16 active_rate_basic;
@@ -1080,11 +1098,10 @@ struct iwl_priv {
struct iwl_chain_noise_data chain_noise_data;
__le16 sensitivity_tbl[HD_TABLE_SIZE];
- struct iwl_ht_info current_ht_config;
+ struct iwl_ht_config current_ht_config;
u8 last_phy_res[100];
/* Rate scaling data */
- s8 data_retry_limit;
u8 retry_rate;
wait_queue_head_t wait_command_queue;
@@ -1093,7 +1110,7 @@ struct iwl_priv {
/* Rx and Tx DMA processing queues */
struct iwl_rx_queue rxq;
- struct iwl_tx_queue txq[IWL_MAX_NUM_QUEUES];
+ struct iwl_tx_queue *txq;
unsigned long txq_ctx_active_msk;
struct iwl_dma_ptr kw; /* keep warm address */
struct iwl_dma_ptr scd_bc_tbls;
@@ -1116,7 +1133,9 @@ struct iwl_priv {
struct iwl_tt_mgmt thermal_throttle;
struct iwl_notif_statistics statistics;
- unsigned long last_statistics_time;
+#ifdef CONFIG_IWLWIFI_DEBUG
+ struct iwl_notif_statistics accum_statistics;
+#endif
/* context information */
u16 rates_mask;
@@ -1216,6 +1235,7 @@ struct iwl_priv {
/* TX Power */
s8 tx_power_user_lmt;
s8 tx_power_device_lmt;
+ s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */
#ifdef CONFIG_IWLWIFI_DEBUG
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c
new file mode 100644
index 00000000000..e7d88d1da15
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c
@@ -0,0 +1,14 @@
+#include <linux/module.h>
+
+/* sparse doesn't like tracepoint macros */
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "iwl-devtrace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error);
+#endif
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
new file mode 100644
index 00000000000..21361968ab7
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
@@ -0,0 +1,197 @@
+#if !defined(__IWLWIFI_DEVICE_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __IWLWIFI_DEVICE_TRACE
+
+#include <linux/tracepoint.h>
+#include "iwl-dev.h"
+
+#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif
+
+#define PRIV_ENTRY __field(struct iwl_priv *, priv)
+#define PRIV_ASSIGN __entry->priv = priv
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_io
+
+TRACE_EVENT(iwlwifi_dev_ioread32,
+ TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val),
+ TP_ARGS(priv, offs, val),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%p] read io[%#x] = %#x", __entry->priv, __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite8,
+ TP_PROTO(struct iwl_priv *priv, u32 offs, u8 val),
+ TP_ARGS(priv, offs, val),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+ __field(u32, offs)
+ __field(u8, val)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(iwlwifi_dev_iowrite32,
+ TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val),
+ TP_ARGS(priv, offs, val),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+ __field(u32, offs)
+ __field(u32, val)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ __entry->offs = offs;
+ __entry->val = val;
+ ),
+ TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val)
+);
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi
+
+TRACE_EVENT(iwlwifi_dev_hcmd,
+ TP_PROTO(struct iwl_priv *priv, void *hcmd, size_t len, u32 flags),
+ TP_ARGS(priv, hcmd, len, flags),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+ __dynamic_array(u8, hcmd, len)
+ __field(u32, flags)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ memcpy(__get_dynamic_array(hcmd), hcmd, len);
+ __entry->flags = flags;
+ ),
+ TP_printk("[%p] hcmd %#.2x (%ssync)",
+ __entry->priv, ((u8 *)__get_dynamic_array(hcmd))[0],
+ __entry->flags & CMD_ASYNC ? "a" : "")
+);
+
+TRACE_EVENT(iwlwifi_dev_rx,
+ TP_PROTO(struct iwl_priv *priv, void *rxbuf, size_t len),
+ TP_ARGS(priv, rxbuf, len),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+ __dynamic_array(u8, rxbuf, len)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ memcpy(__get_dynamic_array(rxbuf), rxbuf, len);
+ ),
+ TP_printk("[%p] RX cmd %#.2x",
+ __entry->priv, ((u8 *)__get_dynamic_array(rxbuf))[4])
+);
+
+TRACE_EVENT(iwlwifi_dev_tx,
+ TP_PROTO(struct iwl_priv *priv, void *tfd, size_t tfdlen,
+ void *buf0, size_t buf0_len,
+ void *buf1, size_t buf1_len),
+ TP_ARGS(priv, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+
+ __field(size_t, framelen)
+ __dynamic_array(u8, tfd, tfdlen)
+
+ /*
+ * Do not insert between or below these items,
+ * we want to keep the frame together (except
+ * for the possible padding).
+ */
+ __dynamic_array(u8, buf0, buf0_len)
+ __dynamic_array(u8, buf1, buf1_len)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ __entry->framelen = buf0_len + buf1_len;
+ memcpy(__get_dynamic_array(tfd), tfd, tfdlen);
+ memcpy(__get_dynamic_array(buf0), buf0, buf0_len);
+ memcpy(__get_dynamic_array(buf1), buf1, buf0_len);
+ ),
+ TP_printk("[%p] TX %.2x (%zu bytes)",
+ __entry->priv,
+ ((u8 *)__get_dynamic_array(buf0))[0],
+ __entry->framelen)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_error,
+ TP_PROTO(struct iwl_priv *priv, u32 desc, u32 time,
+ u32 data1, u32 data2, u32 line, u32 blink1,
+ u32 blink2, u32 ilink1, u32 ilink2),
+ TP_ARGS(priv, desc, time, data1, data2, line,
+ blink1, blink2, ilink1, ilink2),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+ __field(u32, desc)
+ __field(u32, time)
+ __field(u32, data1)
+ __field(u32, data2)
+ __field(u32, line)
+ __field(u32, blink1)
+ __field(u32, blink2)
+ __field(u32, ilink1)
+ __field(u32, ilink2)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ __entry->desc = desc;
+ __entry->time = time;
+ __entry->data1 = data1;
+ __entry->data2 = data2;
+ __entry->line = line;
+ __entry->blink1 = blink1;
+ __entry->blink2 = blink2;
+ __entry->ilink1 = ilink1;
+ __entry->ilink2 = ilink2;
+ ),
+ TP_printk("[%p] #%02d %010u data 0x%08X 0x%08X line %u, "
+ "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X",
+ __entry->priv, __entry->desc, __entry->time, __entry->data1,
+ __entry->data2, __entry->line, __entry->blink1,
+ __entry->blink2, __entry->ilink1, __entry->ilink2)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_event,
+ TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev),
+ TP_ARGS(priv, time, data, ev),
+ TP_STRUCT__entry(
+ PRIV_ENTRY
+
+ __field(u32, time)
+ __field(u32, data)
+ __field(u32, ev)
+ ),
+ TP_fast_assign(
+ PRIV_ASSIGN;
+ __entry->time = time;
+ __entry->data = data;
+ __entry->ev = ev;
+ ),
+ TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u",
+ __entry->priv, __entry->time, __entry->data, __entry->ev)
+);
+#endif /* __IWLWIFI_DEVICE_TRACE */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE iwl-devtrace
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
index e14c9952a93..3946e5c03f8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
@@ -215,12 +215,35 @@ static const struct iwl_txpwr_section enhinfo[] = {
int iwlcore_eeprom_verify_signature(struct iwl_priv *priv)
{
- u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
- if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) {
- IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp);
- return -ENOENT;
+ u32 gp = iwl_read32(priv, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
+ int ret = 0;
+
+ IWL_DEBUG_INFO(priv, "EEPROM signature=0x%08x\n", gp);
+ switch (gp) {
+ case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP:
+ if (priv->nvm_device_type != NVM_DEVICE_TYPE_OTP) {
+ IWL_ERR(priv, "EEPROM with bad signature: 0x%08x\n",
+ gp);
+ ret = -ENOENT;
+ }
+ break;
+ case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
+ case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
+ if (priv->nvm_device_type != NVM_DEVICE_TYPE_EEPROM) {
+ IWL_ERR(priv, "OTP with bad signature: 0x%08x\n", gp);
+ ret = -ENOENT;
+ }
+ break;
+ case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP:
+ default:
+ IWL_ERR(priv, "bad EEPROM/OTP signature, type=%s, "
+ "EEPROM_GP=0x%08x\n",
+ (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
+ ? "OTP" : "EEPROM", gp);
+ ret = -ENOENT;
+ break;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL(iwlcore_eeprom_verify_signature);
@@ -283,7 +306,8 @@ int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv)
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
/* See if we got it */
- ret = iwl_poll_direct_bit(priv, CSR_HW_IF_CONFIG_REG,
+ ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
EEPROM_SEM_TIMEOUT);
if (ret >= 0) {
@@ -322,7 +346,8 @@ static int iwl_init_otp_access(struct iwl_priv *priv)
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
/* wait for clock to be ready */
- ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
+ ret = iwl_poll_bit(priv, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
25000);
if (ret < 0)
@@ -333,6 +358,14 @@ static int iwl_init_otp_access(struct iwl_priv *priv)
udelay(5);
iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
APMG_PS_CTRL_VAL_RESET_REQ);
+
+ /*
+ * CSR auto clock gate disable bit -
+ * this is only applicable for HW with OTP shadow RAM
+ */
+ if (priv->cfg->shadow_ram_support)
+ iwl_set_bit(priv, CSR_DBG_LINK_PWR_MGMT_REG,
+ CSR_RESET_LINK_PWR_MGMT_DISABLED);
}
return ret;
}
@@ -345,7 +378,8 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data)
_iwl_write32(priv, CSR_EEPROM_REG,
CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
- ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
+ ret = iwl_poll_bit(priv, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_READ_VALID_MSK,
CSR_EEPROM_REG_READ_VALID_MSK,
IWL_EEPROM_ACCESS_TIMEOUT);
if (ret < 0) {
@@ -484,6 +518,11 @@ int iwl_eeprom_init(struct iwl_priv *priv)
}
e = (u16 *)priv->eeprom;
+ if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) {
+ /* OTP reads require powered-up chip */
+ priv->cfg->ops->lib->apm_ops.init(priv);
+ }
+
ret = priv->cfg->ops->lib->eeprom_ops.verify_signature(priv);
if (ret < 0) {
IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp);
@@ -498,7 +537,9 @@ int iwl_eeprom_init(struct iwl_priv *priv)
ret = -ENOENT;
goto err;
}
+
if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) {
+
ret = iwl_init_otp_access(priv);
if (ret) {
IWL_ERR(priv, "Failed to initialize OTP access.\n");
@@ -529,6 +570,13 @@ int iwl_eeprom_init(struct iwl_priv *priv)
e[cache_addr / 2] = eeprom_data;
cache_addr += sizeof(u16);
}
+
+ /*
+ * Now that OTP reads are complete, reset chip to save
+ * power until we load uCode during "up".
+ */
+ priv->cfg->ops->lib->apm_ops.stop(priv);
+
} else {
/* eeprom is an array of 16bit values */
for (addr = 0; addr < sz; addr += sizeof(u16)) {
@@ -537,7 +585,8 @@ int iwl_eeprom_init(struct iwl_priv *priv)
_iwl_write32(priv, CSR_EEPROM_REG,
CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
- ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
+ ret = iwl_poll_bit(priv, CSR_EEPROM_REG,
+ CSR_EEPROM_REG_READ_VALID_MSK,
CSR_EEPROM_REG_READ_VALID_MSK,
IWL_EEPROM_ACCESS_TIMEOUT);
if (ret < 0) {
@@ -705,9 +754,6 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv,
ch_info->ht40_eeprom = *eeprom_ch;
ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg;
- ch_info->ht40_curr_txpow = eeprom_ch->max_power_avg;
- ch_info->ht40_min_power = 0;
- ch_info->ht40_scan_power = eeprom_ch->max_power_avg;
ch_info->ht40_flags = eeprom_ch->flags;
ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel;
@@ -719,7 +765,8 @@ static int iwl_mod_ht40_chan_info(struct iwl_priv *priv,
* find the highest tx power from all chains for the channel
*/
static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
- struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, int element)
+ struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
+ int element, s8 *max_txpower_in_half_dbm)
{
s8 max_txpower_avg = 0; /* (dBm) */
@@ -751,10 +798,14 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
(enhanced_txpower[element].mimo3_max > max_txpower_avg))
max_txpower_avg = enhanced_txpower[element].mimo3_max;
- /* max. tx power in EEPROM is in 1/2 dBm format
- * convert from 1/2 dBm to dBm
+ /*
+ * max. tx power in EEPROM is in 1/2 dBm format
+ * convert from 1/2 dBm to dBm (round-up convert)
+ * but we also do not want to loss 1/2 dBm resolution which
+ * will impact performance
*/
- return max_txpower_avg >> 1;
+ *max_txpower_in_half_dbm = max_txpower_avg;
+ return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1);
}
/**
@@ -763,7 +814,7 @@ static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
*/
static s8 iwl_update_common_txpower(struct iwl_priv *priv,
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
- int section, int element)
+ int section, int element, s8 *max_txpower_in_half_dbm)
{
struct iwl_channel_info *ch_info;
int ch;
@@ -777,25 +828,25 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv,
if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX)
is_ht40 = true;
max_txpower_avg =
- iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
+ iwl_get_max_txpower_avg(priv, enhanced_txpower,
+ element, max_txpower_in_half_dbm);
+
ch_info = priv->channel_info;
for (ch = 0; ch < priv->channel_count; ch++) {
/* find matching band and update tx power if needed */
if ((ch_info->band == enhinfo[section].band) &&
- (ch_info->max_power_avg < max_txpower_avg) && (!is_ht40)) {
+ (ch_info->max_power_avg < max_txpower_avg) &&
+ (!is_ht40)) {
/* Update regulatory-based run-time data */
ch_info->max_power_avg = ch_info->curr_txpow =
- max_txpower_avg;
+ max_txpower_avg;
ch_info->scan_power = max_txpower_avg;
}
if ((ch_info->band == enhinfo[section].band) && is_ht40 &&
- ch_info->ht40_max_power_avg &&
(ch_info->ht40_max_power_avg < max_txpower_avg)) {
/* Update regulatory-based run-time data */
ch_info->ht40_max_power_avg = max_txpower_avg;
- ch_info->ht40_curr_txpow = max_txpower_avg;
- ch_info->ht40_scan_power = max_txpower_avg;
}
ch_info++;
}
@@ -808,7 +859,7 @@ static s8 iwl_update_common_txpower(struct iwl_priv *priv,
*/
static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
- int section, int element)
+ int section, int element, s8 *max_txpower_in_half_dbm)
{
struct iwl_channel_info *ch_info;
int ch;
@@ -817,7 +868,8 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
channel = enhinfo[section].iwl_eeprom_section_channel[element];
max_txpower_avg =
- iwl_get_max_txpower_avg(priv, enhanced_txpower, element);
+ iwl_get_max_txpower_avg(priv, enhanced_txpower,
+ element, max_txpower_in_half_dbm);
ch_info = priv->channel_info;
for (ch = 0; ch < priv->channel_count; ch++) {
@@ -831,12 +883,9 @@ static s8 iwl_update_channel_txpower(struct iwl_priv *priv,
ch_info->scan_power = max_txpower_avg;
}
if ((enhinfo[section].is_ht40) &&
- (ch_info->ht40_max_power_avg) &&
(ch_info->ht40_max_power_avg < max_txpower_avg)) {
/* Update regulatory-based run-time data */
ch_info->ht40_max_power_avg = max_txpower_avg;
- ch_info->ht40_curr_txpow = max_txpower_avg;
- ch_info->ht40_scan_power = max_txpower_avg;
}
break;
}
@@ -855,6 +904,7 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
struct iwl_eeprom_enhanced_txpwr *enhanced_txpower;
u32 offset;
s8 max_txpower_avg; /* (dBm) */
+ s8 max_txpower_in_half_dbm; /* (half-dBm) */
/* Loop through all the sections
* adjust bands and channel's max tx power
@@ -867,20 +917,43 @@ void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
enhanced_txpower = (struct iwl_eeprom_enhanced_txpwr *)
iwl_eeprom_query_addr(priv, offset);
+ /*
+ * check for valid entry -
+ * different version of EEPROM might contain different set
+ * of enhanced tx power table
+ * always check for valid entry before process
+ * the information
+ */
+ if (!enhanced_txpower->common || enhanced_txpower->reserved)
+ continue;
+
for (element = 0; element < eeprom_section_count; element++) {
if (enhinfo[section].is_common)
max_txpower_avg =
iwl_update_common_txpower(priv,
- enhanced_txpower, section, element);
+ enhanced_txpower, section,
+ element,
+ &max_txpower_in_half_dbm);
else
max_txpower_avg =
iwl_update_channel_txpower(priv,
- enhanced_txpower, section, element);
+ enhanced_txpower, section,
+ element,
+ &max_txpower_in_half_dbm);
/* Update the tx_power_user_lmt to the highest power
* supported by any channel */
if (max_txpower_avg > priv->tx_power_user_lmt)
priv->tx_power_user_lmt = max_txpower_avg;
+
+ /*
+ * Update the tx_power_lmt_in_half_dbm to
+ * the highest power supported by any channel
+ */
+ if (max_txpower_in_half_dbm >
+ priv->tx_power_lmt_in_half_dbm)
+ priv->tx_power_lmt_in_half_dbm =
+ max_txpower_in_half_dbm;
}
}
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
index 80b9e45d9b9..5cd2b66bbe4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h
@@ -63,6 +63,8 @@
#ifndef __iwl_eeprom_h__
#define __iwl_eeprom_h__
+#include <net/mac80211.h>
+
struct iwl_priv;
/*
@@ -125,19 +127,21 @@ struct iwl_eeprom_channel {
* Enhanced regulatory tx power portion of eeprom image can be broken down
* into individual structures; each one is 8 bytes in size and contain the
* following information
+ * @common: (desc + channel) not used by driver, should _NOT_ be "zero"
* @chain_a_max_pwr: chain a max power in 1/2 dBm
* @chain_b_max_pwr: chain b max power in 1/2 dBm
* @chain_c_max_pwr: chain c max power in 1/2 dBm
+ * @reserved: not used, should be "zero"
* @mimo2_max_pwr: mimo2 max power in 1/2 dBm
* @mimo3_max_pwr: mimo3 max power in 1/2 dBm
*
*/
struct iwl_eeprom_enhanced_txpwr {
- u16 reserved;
+ u16 common;
s8 chain_a_max;
s8 chain_b_max;
s8 chain_c_max;
- s8 reserved1;
+ s8 reserved;
s8 mimo2_max;
s8 mimo3_max;
} __attribute__ ((packed));
@@ -256,6 +260,15 @@ struct iwl_eeprom_enhanced_txpwr {
#define EEPROM_5050_TX_POWER_VERSION (4)
#define EEPROM_5050_EEPROM_VERSION (0x21E)
+/* 1000 Specific */
+#define EEPROM_1000_EEPROM_VERSION (0x15C)
+
+/* 6x00 Specific */
+#define EEPROM_6000_EEPROM_VERSION (0x434)
+
+/* 6x50 Specific */
+#define EEPROM_6050_EEPROM_VERSION (0x532)
+
/* OTP */
/* lower blocks contain EEPROM image and calibration data */
#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
@@ -370,12 +383,10 @@ struct iwl_eeprom_calib_info {
#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */
#define EEPROM_VERSION (2*0x44) /* 2 bytes */
#define EEPROM_SKU_CAP (2*0x45) /* 1 bytes */
-#define EEPROM_LEDS_MODE (2*0x45+1) /* 1 bytes */
#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */
#define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */
#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */
#define EEPROM_3945_M_VERSION (2*0x4A) /* 1 bytes */
-#define EEPROM_ANTENNA_SWITCH_TYPE (2*0x4A+1) /* 1 bytes */
/* The following masks are to be applied on EEPROM_RADIO_CONFIG */
#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */
@@ -387,7 +398,12 @@ struct iwl_eeprom_calib_info {
#define EEPROM_3945_RF_CFG_TYPE_MAX 0x0
#define EEPROM_4965_RF_CFG_TYPE_MAX 0x1
-#define EEPROM_5000_RF_CFG_TYPE_MAX 0x3
+
+/* Radio Config for 5000 and up */
+#define EEPROM_RF_CONFIG_TYPE_R3x3 0x0
+#define EEPROM_RF_CONFIG_TYPE_R2x2 0x1
+#define EEPROM_RF_CONFIG_TYPE_R1x2 0x2
+#define EEPROM_RF_CONFIG_TYPE_MAX 0x3
/*
* Per-channel regulatory data.
diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c
index a6856daf14c..a2316594820 100644
--- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c
+++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c
@@ -56,6 +56,8 @@ const char *get_cmd_string(u8 cmd)
IWL_CMD(REPLY_LEDS_CMD);
IWL_CMD(REPLY_TX_LINK_QUALITY_CMD);
IWL_CMD(COEX_PRIORITY_TABLE_CMD);
+ IWL_CMD(COEX_MEDIUM_NOTIFICATION);
+ IWL_CMD(COEX_EVENT_CMD);
IWL_CMD(RADAR_NOTIFICATION);
IWL_CMD(REPLY_QUIET_CMD);
IWL_CMD(REPLY_CHANNEL_SWITCH);
@@ -93,6 +95,8 @@ const char *get_cmd_string(u8 cmd)
IWL_CMD(CALIBRATION_RES_NOTIFICATION);
IWL_CMD(CALIBRATION_COMPLETE_NOTIFICATION);
IWL_CMD(REPLY_TX_POWER_DBM_CMD);
+ IWL_CMD(TEMPERATURE_NOTIFICATION);
+ IWL_CMD(TX_ANT_CONFIGURATION_CMD);
default:
return "UNKNOWN";
@@ -104,17 +108,8 @@ EXPORT_SYMBOL(get_cmd_string);
static void iwl_generic_cmd_callback(struct iwl_priv *priv,
struct iwl_device_cmd *cmd,
- struct sk_buff *skb)
+ struct iwl_rx_packet *pkt)
{
- struct iwl_rx_packet *pkt = NULL;
-
- if (!skb) {
- IWL_ERR(priv, "Error: Response NULL in %s.\n",
- get_cmd_string(cmd->hdr.cmd));
- return;
- }
-
- pkt = (struct iwl_rx_packet *)skb->data;
if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from %s (0x%08X)\n",
get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
@@ -205,18 +200,18 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
}
if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
- IWL_DEBUG_INFO(priv, "Command %s aborted: RF KILL Switch\n",
+ IWL_ERR(priv, "Command %s aborted: RF KILL Switch\n",
get_cmd_string(cmd->id));
ret = -ECANCELED;
goto fail;
}
if (test_bit(STATUS_FW_ERROR, &priv->status)) {
- IWL_DEBUG_INFO(priv, "Command %s failed: FW Error\n",
+ IWL_ERR(priv, "Command %s failed: FW Error\n",
get_cmd_string(cmd->id));
ret = -EIO;
goto fail;
}
- if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_skb) {
+ if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
IWL_ERR(priv, "Error: Response NULL in '%s'\n",
get_cmd_string(cmd->id));
ret = -EIO;
@@ -238,9 +233,9 @@ cancel:
~CMD_WANT_SKB;
}
fail:
- if (cmd->reply_skb) {
- dev_kfree_skb_any(cmd->reply_skb);
- cmd->reply_skb = NULL;
+ if (cmd->reply_page) {
+ free_pages(cmd->reply_page, priv->hw_params.rx_page_order);
+ cmd->reply_page = 0;
}
out:
clear_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status);
@@ -273,7 +268,7 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv,
u8 id, u16 len, const void *data,
void (*callback)(struct iwl_priv *priv,
struct iwl_device_cmd *cmd,
- struct sk_buff *skb))
+ struct iwl_rx_packet *pkt))
{
struct iwl_host_cmd cmd = {
.id = id,
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index d30cb0275d1..e552d4c4bdb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -32,6 +32,7 @@
#include <linux/io.h>
#include "iwl-debug.h"
+#include "iwl-devtrace.h"
/*
* IO, register, and NIC memory access functions
@@ -61,7 +62,32 @@
*
*/
-#define _iwl_write32(priv, ofs, val) iowrite32((val), (priv)->hw_base + (ofs))
+static inline void _iwl_write8(struct iwl_priv *priv, u32 ofs, u8 val)
+{
+ trace_iwlwifi_dev_iowrite8(priv, ofs, val);
+ iowrite8(val, priv->hw_base + ofs);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+static inline void __iwl_write8(const char *f, u32 l, struct iwl_priv *priv,
+ u32 ofs, u8 val)
+{
+ IWL_DEBUG_IO(priv, "write8(0x%08X, 0x%02X) - %s %d\n", ofs, val, f, l);
+ _iwl_write8(priv, ofs, val);
+}
+#define iwl_write8(priv, ofs, val) \
+ __iwl_write8(__FILE__, __LINE__, priv, ofs, val)
+#else
+#define iwl_write8(priv, ofs, val) _iwl_write8(priv, ofs, val)
+#endif
+
+
+static inline void _iwl_write32(struct iwl_priv *priv, u32 ofs, u32 val)
+{
+ trace_iwlwifi_dev_iowrite32(priv, ofs, val);
+ iowrite32(val, priv->hw_base + ofs);
+}
+
#ifdef CONFIG_IWLWIFI_DEBUG
static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *priv,
u32 ofs, u32 val)
@@ -75,7 +101,13 @@ static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *priv,
#define iwl_write32(priv, ofs, val) _iwl_write32(priv, ofs, val)
#endif
-#define _iwl_read32(priv, ofs) ioread32((priv)->hw_base + (ofs))
+static inline u32 _iwl_read32(struct iwl_priv *priv, u32 ofs)
+{
+ u32 val = ioread32(priv->hw_base + ofs);
+ trace_iwlwifi_dev_ioread32(priv, ofs, val);
+ return val;
+}
+
#ifdef CONFIG_IWLWIFI_DEBUG
static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *priv, u32 ofs)
{
@@ -188,6 +220,26 @@ static inline int _iwl_grab_nic_access(struct iwl_priv *priv)
/* this bit wakes up the NIC */
_iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ /*
+ * These bits say the device is running, and should keep running for
+ * at least a short while (at least as long as MAC_ACCESS_REQ stays 1),
+ * but they do not indicate that embedded SRAM is restored yet;
+ * 3945 and 4965 have volatile SRAM, and must save/restore contents
+ * to/from host DRAM when sleeping/waking for power-saving.
+ * Each direction takes approximately 1/4 millisecond; with this
+ * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a
+ * series of register accesses are expected (e.g. reading Event Log),
+ * to keep device from sleeping.
+ *
+ * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that
+ * SRAM is okay/restored. We don't check that here because this call
+ * is just for hardware register access; but GP1 MAC_SLEEP check is a
+ * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log).
+ *
+ * 5000 series and later (including 1000 series) have non-volatile SRAM,
+ * and do not save/restore SRAM when power cycling.
+ */
ret = _iwl_poll_bit(priv, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
(CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c
index f420c99e724..46c7a95b88f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-led.c
@@ -42,15 +42,11 @@
#include "iwl-core.h"
#include "iwl-io.h"
-#ifdef CONFIG_IWLWIFI_DEBUG
-static const char *led_type_str[] = {
- __stringify(IWL_LED_TRG_TX),
- __stringify(IWL_LED_TRG_RX),
- __stringify(IWL_LED_TRG_ASSOC),
- __stringify(IWL_LED_TRG_RADIO),
- NULL
-};
-#endif /* CONFIG_IWLWIFI_DEBUG */
+/* default: IWL_LED_BLINK(0) using blinking index table */
+static int led_mode;
+module_param(led_mode, int, S_IRUGO);
+MODULE_PARM_DESC(led_mode, "led mode: 0=blinking, 1=On(RF On)/Off(RF Off), "
+ "(default 0)\n");
static const struct {
@@ -65,11 +61,11 @@ static const struct {
{70, 65, 65},
{50, 75, 75},
{20, 85, 85},
- {15, 95, 95 },
- {10, 110, 110},
- {5, 130, 130},
+ {10, 95, 95},
+ {5, 110, 110},
+ {1, 130, 130},
{0, 167, 167},
-/* SOLID_ON */
+ /* SOLID_ON */
{-1, IWL_LED_SOLID, 0}
};
@@ -78,191 +74,74 @@ static const struct {
#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
-/* [0-256] -> [0..8] FIXME: we need [0..10] */
-static inline int iwl_brightness_to_idx(enum led_brightness brightness)
-{
- return fls(0x000000FF & (u32)brightness);
-}
-
-/* Send led command */
-static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd)
+/*
+ * Adjust led blink rate to compensate on a MAC Clock difference on every HW
+ * Led blink rate analysis showed an average deviation of 0% on 3945,
+ * 5% on 4965 HW and 20% on 5000 series and up.
+ * Need to compensate on the led on/off time per HW according to the deviation
+ * to achieve the desired led frequency
+ * The calculation is: (100-averageDeviation)/100 * blinkTime
+ * For code efficiency the calculation will be:
+ * compensation = (100 - averageDeviation) * 64 / 100
+ * NewBlinkTime = (compensation * BlinkTime) / 64
+ */
+static inline u8 iwl_blink_compensation(struct iwl_priv *priv,
+ u8 time, u16 compensation)
{
- struct iwl_host_cmd cmd = {
- .id = REPLY_LEDS_CMD,
- .len = sizeof(struct iwl_led_cmd),
- .data = led_cmd,
- .flags = CMD_ASYNC,
- .callback = NULL,
- };
- u32 reg;
-
- reg = iwl_read32(priv, CSR_LED_REG);
- if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
- iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK);
+ if (!compensation) {
+ IWL_ERR(priv, "undefined blink compensation: "
+ "use pre-defined blinking time\n");
+ return time;
+ }
- return iwl_send_cmd(priv, &cmd);
+ return (u8)((time * compensation) >> 6);
}
/* Set led pattern command */
-static int iwl_led_pattern(struct iwl_priv *priv, int led_id,
- unsigned int idx)
+static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx)
{
struct iwl_led_cmd led_cmd = {
- .id = led_id,
+ .id = IWL_LED_LINK,
.interval = IWL_DEF_LED_INTRVL
};
BUG_ON(idx > IWL_MAX_BLINK_TBL);
- led_cmd.on = blink_tbl[idx].on_time;
- led_cmd.off = blink_tbl[idx].off_time;
-
- return iwl_send_led_cmd(priv, &led_cmd);
-}
-
-/* Set led register off */
-static int iwl_led_on_reg(struct iwl_priv *priv, int led_id)
-{
- IWL_DEBUG_LED(priv, "led on %d\n", led_id);
- iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON);
- return 0;
-}
+ IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n",
+ priv->cfg->led_compensation);
+ led_cmd.on =
+ iwl_blink_compensation(priv, blink_tbl[idx].on_time,
+ priv->cfg->led_compensation);
+ led_cmd.off =
+ iwl_blink_compensation(priv, blink_tbl[idx].off_time,
+ priv->cfg->led_compensation);
-#if 0
-/* Set led on command */
-static int iwl_led_on(struct iwl_priv *priv, int led_id)
-{
- struct iwl_led_cmd led_cmd = {
- .id = led_id,
- .on = IWL_LED_SOLID,
- .off = 0,
- .interval = IWL_DEF_LED_INTRVL
- };
- return iwl_send_led_cmd(priv, &led_cmd);
+ return priv->cfg->ops->led->cmd(priv, &led_cmd);
}
-/* Set led off command */
-int iwl_led_off(struct iwl_priv *priv, int led_id)
+int iwl_led_start(struct iwl_priv *priv)
{
- struct iwl_led_cmd led_cmd = {
- .id = led_id,
- .on = 0,
- .off = 0,
- .interval = IWL_DEF_LED_INTRVL
- };
- IWL_DEBUG_LED(priv, "led off %d\n", led_id);
- return iwl_send_led_cmd(priv, &led_cmd);
+ return priv->cfg->ops->led->on(priv);
}
-#endif
-
+EXPORT_SYMBOL(iwl_led_start);
-/* Set led register off */
-static int iwl_led_off_reg(struct iwl_priv *priv, int led_id)
-{
- IWL_DEBUG_LED(priv, "LED Reg off\n");
- iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF);
- return 0;
-}
-
-/*
- * Set led register in case of disassociation according to rfkill state
- */
-static int iwl_led_associate(struct iwl_priv *priv, int led_id)
+int iwl_led_associate(struct iwl_priv *priv)
{
IWL_DEBUG_LED(priv, "Associated\n");
- priv->allow_blinking = 1;
- return iwl_led_on_reg(priv, led_id);
-}
-static int iwl_led_disassociate(struct iwl_priv *priv, int led_id)
-{
- priv->allow_blinking = 0;
-
- return 0;
-}
-
-/*
- * brightness call back function for Tx/Rx LED
- */
-static int iwl_led_associated(struct iwl_priv *priv, int led_id)
-{
- if (test_bit(STATUS_EXIT_PENDING, &priv->status) ||
- !test_bit(STATUS_READY, &priv->status))
- return 0;
-
+ if (led_mode == IWL_LED_BLINK)
+ priv->allow_blinking = 1;
+ priv->last_blink_time = jiffies;
- /* start counting Tx/Rx bytes */
- if (!priv->last_blink_time && priv->allow_blinking)
- priv->last_blink_time = jiffies;
return 0;
}
-/*
- * brightness call back for association and radio
- */
-static void iwl_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
+int iwl_led_disassociate(struct iwl_priv *priv)
{
- struct iwl_led *led = container_of(led_cdev, struct iwl_led, led_dev);
- struct iwl_priv *priv = led->priv;
-
- if (test_bit(STATUS_EXIT_PENDING, &priv->status))
- return;
-
-
- IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
- led_type_str[led->type], brightness);
- switch (brightness) {
- case LED_FULL:
- if (led->led_on)
- led->led_on(priv, IWL_LED_LINK);
- break;
- case LED_OFF:
- if (led->led_off)
- led->led_off(priv, IWL_LED_LINK);
- break;
- default:
- if (led->led_pattern) {
- int idx = iwl_brightness_to_idx(brightness);
- led->led_pattern(priv, IWL_LED_LINK, idx);
- }
- break;
- }
-}
-
-
-
-/*
- * Register led class with the system
- */
-static int iwl_leds_register_led(struct iwl_priv *priv, struct iwl_led *led,
- enum led_type type, u8 set_led,
- char *trigger)
-{
- struct device *device = wiphy_dev(priv->hw->wiphy);
- int ret;
-
- led->led_dev.name = led->name;
- led->led_dev.brightness_set = iwl_led_brightness_set;
- led->led_dev.default_trigger = trigger;
-
- led->priv = priv;
- led->type = type;
-
- ret = led_classdev_register(device, &led->led_dev);
- if (ret) {
- IWL_ERR(priv, "Error: failed to register led handler.\n");
- return ret;
- }
-
- led->registered = 1;
-
- if (set_led && led->led_on)
- led->led_on(priv, IWL_LED_LINK);
+ priv->allow_blinking = 0;
return 0;
}
-
/*
* calculate blink rate according to last second Tx/Rx activities
*/
@@ -288,7 +167,7 @@ static int iwl_get_blink_rate(struct iwl_priv *priv)
i = IWL_MAX_BLINK_TBL;
else
for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
- if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
+ if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
break;
IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
@@ -317,8 +196,7 @@ void iwl_leds_background(struct iwl_priv *priv)
priv->last_blink_time = 0;
if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
- iwl_led_pattern(priv, IWL_LED_LINK,
- IWL_SOLID_BLINK_IDX);
+ iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX);
}
return;
}
@@ -331,111 +209,17 @@ void iwl_leds_background(struct iwl_priv *priv)
/* call only if blink rate change */
if (blink_idx != priv->last_blink_rate)
- iwl_led_pattern(priv, IWL_LED_LINK, blink_idx);
+ iwl_led_pattern(priv, blink_idx);
priv->last_blink_time = jiffies;
priv->last_blink_rate = blink_idx;
}
+EXPORT_SYMBOL(iwl_leds_background);
-/* Register all led handler */
-int iwl_leds_register(struct iwl_priv *priv)
+void iwl_leds_init(struct iwl_priv *priv)
{
- char *trigger;
- int ret;
-
priv->last_blink_rate = 0;
- priv->led_tpt = 0;
priv->last_blink_time = 0;
priv->allow_blinking = 0;
-
- trigger = ieee80211_get_radio_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_RADIO].name,
- sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
- wiphy_name(priv->hw->wiphy));
-
- priv->led[IWL_LED_TRG_RADIO].led_on = iwl_led_on_reg;
- priv->led[IWL_LED_TRG_RADIO].led_off = iwl_led_off_reg;
- priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL;
-
- ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RADIO],
- IWL_LED_TRG_RADIO, 1, trigger);
- if (ret)
- goto exit_fail;
-
- trigger = ieee80211_get_assoc_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
- sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
- wiphy_name(priv->hw->wiphy));
-
- ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_ASSOC],
- IWL_LED_TRG_ASSOC, 0, trigger);
-
- /* for assoc always turn led on */
- priv->led[IWL_LED_TRG_ASSOC].led_on = iwl_led_associate;
- priv->led[IWL_LED_TRG_ASSOC].led_off = iwl_led_disassociate;
- priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
-
- if (ret)
- goto exit_fail;
-
- trigger = ieee80211_get_rx_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_RX].name,
- sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
- wiphy_name(priv->hw->wiphy));
-
- ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RX],
- IWL_LED_TRG_RX, 0, trigger);
-
- priv->led[IWL_LED_TRG_RX].led_on = iwl_led_associated;
- priv->led[IWL_LED_TRG_RX].led_off = iwl_led_associated;
- priv->led[IWL_LED_TRG_RX].led_pattern = iwl_led_pattern;
-
- if (ret)
- goto exit_fail;
-
- trigger = ieee80211_get_tx_led_name(priv->hw);
- snprintf(priv->led[IWL_LED_TRG_TX].name,
- sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
- wiphy_name(priv->hw->wiphy));
-
- ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_TX],
- IWL_LED_TRG_TX, 0, trigger);
-
- priv->led[IWL_LED_TRG_TX].led_on = iwl_led_associated;
- priv->led[IWL_LED_TRG_TX].led_off = iwl_led_associated;
- priv->led[IWL_LED_TRG_TX].led_pattern = iwl_led_pattern;
-
- if (ret)
- goto exit_fail;
-
- return 0;
-
-exit_fail:
- iwl_leds_unregister(priv);
- return ret;
}
-EXPORT_SYMBOL(iwl_leds_register);
-
-/* unregister led class */
-static void iwl_leds_unregister_led(struct iwl_led *led, u8 set_led)
-{
- if (!led->registered)
- return;
-
- led_classdev_unregister(&led->led_dev);
-
- if (set_led)
- led->led_dev.brightness_set(&led->led_dev, LED_OFF);
- led->registered = 0;
-}
-
-/* Unregister all led handlers */
-void iwl_leds_unregister(struct iwl_priv *priv)
-{
- iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_ASSOC], 0);
- iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RX], 0);
- iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_TX], 0);
- iwl_leds_unregister_led(&priv->led[IWL_LED_TRG_RADIO], 1);
-}
-EXPORT_SYMBOL(iwl_leds_unregister);
-
+EXPORT_SYMBOL(iwl_leds_init);
diff --git a/drivers/net/wireless/iwlwifi/iwl-led.h b/drivers/net/wireless/iwlwifi/iwl-led.h
index ef9b174c37f..f47f053f02e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-led.h
+++ b/drivers/net/wireless/iwlwifi/iwl-led.h
@@ -30,9 +30,6 @@
struct iwl_priv;
-#ifdef CONFIG_IWLWIFI_LEDS
-#include <linux/leds.h>
-
#define IWL_LED_SOLID 11
#define IWL_LED_NAME_LEN 31
#define IWL_DEF_LED_INTRVL cpu_to_le32(1000)
@@ -47,38 +44,23 @@ enum led_type {
IWL_LED_TRG_RADIO,
IWL_LED_TRG_MAX,
};
-#endif
-
-#ifdef CONFIG_IWLWIFI_LEDS
-
-struct iwl_led {
- struct iwl_priv *priv;
- struct led_classdev led_dev;
- char name[32];
- int (*led_on) (struct iwl_priv *priv, int led_id);
- int (*led_off) (struct iwl_priv *priv, int led_id);
- int (*led_pattern) (struct iwl_priv *priv, int led_id, unsigned int idx);
-
- enum led_type type;
- unsigned int registered;
+/*
+ * LED mode
+ * IWL_LED_BLINK: adjust led blink rate based on blink table
+ * IWL_LED_RF_STATE: turn LED on/off based on RF state
+ * LED ON = RF ON
+ * LED OFF = RF OFF
+ */
+enum iwl_led_mode {
+ IWL_LED_BLINK,
+ IWL_LED_RF_STATE,
};
-int iwl_leds_register(struct iwl_priv *priv);
-void iwl_leds_unregister(struct iwl_priv *priv);
+void iwl_leds_init(struct iwl_priv *priv);
void iwl_leds_background(struct iwl_priv *priv);
+int iwl_led_start(struct iwl_priv *priv);
+int iwl_led_associate(struct iwl_priv *priv);
+int iwl_led_disassociate(struct iwl_priv *priv);
-#else
-static inline int iwl_leds_register(struct iwl_priv *priv)
-{
- return 0;
-}
-static inline void iwl_leds_unregister(struct iwl_priv *priv)
-{
-}
-static inline void iwl_leds_background(struct iwl_priv *priv)
-{
-}
-
-#endif /* CONFIG_IWLWIFI_LEDS */
#endif /* __iwl_leds_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c
index 60be976afff..8ccc0bb1d9e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.c
+++ b/drivers/net/wireless/iwlwifi/iwl-power.c
@@ -66,7 +66,7 @@ MODULE_PARM_DESC(no_sleep_autoadjust,
struct iwl_power_vec_entry {
struct iwl_powertable_cmd cmd;
- u8 no_dtim;
+ u8 no_dtim; /* number of skip dtim */
};
#define IWL_DTIM_RANGE_0_MAX 2
@@ -83,8 +83,9 @@ struct iwl_power_vec_entry {
cpu_to_le32(X4)}
/* default power management (not Tx power) table values */
/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */
+/* DTIM 0 - 2 */
static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = {
- {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},
+ {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0},
{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0},
{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0},
{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1},
@@ -93,15 +94,17 @@ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = {
/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */
+/* DTIM 3 - 10 */
static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = {
{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0},
{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0},
{{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0},
{{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1},
- {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 7, 10, 10)}, 2}
+ {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2}
};
/* for DTIM period > IWL_DTIM_RANGE_1_MAX */
+/* DTIM 11 - */
static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = {
{{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0},
{{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0},
@@ -115,13 +118,15 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
enum iwl_power_level lvl, int period)
{
const struct iwl_power_vec_entry *table;
- int max_sleep, i;
- bool skip;
+ int max_sleep[IWL_POWER_VEC_SIZE] = { 0 };
+ int i;
+ u8 skip;
+ u32 slp_itrvl;
table = range_2;
- if (period < IWL_DTIM_RANGE_1_MAX)
+ if (period <= IWL_DTIM_RANGE_1_MAX)
table = range_1;
- if (period < IWL_DTIM_RANGE_0_MAX)
+ if (period <= IWL_DTIM_RANGE_0_MAX)
table = range_0;
BUG_ON(lvl < 0 || lvl >= IWL_POWER_NUM);
@@ -129,34 +134,60 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
*cmd = table[lvl].cmd;
if (period == 0) {
- skip = false;
+ skip = 0;
period = 1;
+ for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
+ max_sleep[i] = 1;
+
} else {
- skip = !!table[lvl].no_dtim;
+ skip = table[lvl].no_dtim;
+ for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
+ max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]);
+ max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1;
}
- if (skip) {
- __le32 slp_itrvl = cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1];
- max_sleep = le32_to_cpu(slp_itrvl);
- if (max_sleep == 0xFF)
- max_sleep = period * (skip + 1);
- else if (max_sleep > period)
- max_sleep = (le32_to_cpu(slp_itrvl) / period) * period;
+ slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
+ /* figure out the listen interval based on dtim period and skip */
+ if (slp_itrvl == 0xFF)
+ cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
+ cpu_to_le32(period * (skip + 1));
+
+ slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
+ if (slp_itrvl > period)
+ cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
+ cpu_to_le32((slp_itrvl / period) * period);
+
+ if (skip)
cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK;
- } else {
- max_sleep = period;
+ else
cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK;
- }
- for (i = 0; i < IWL_POWER_VEC_SIZE; i++)
- if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep)
- cmd->sleep_interval[i] = cpu_to_le32(max_sleep);
+ slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]);
+ if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL)
+ cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] =
+ cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL);
+
+ /* enforce max sleep interval */
+ for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) {
+ if (le32_to_cpu(cmd->sleep_interval[i]) >
+ (max_sleep[i] * period))
+ cmd->sleep_interval[i] =
+ cpu_to_le32(max_sleep[i] * period);
+ if (i != (IWL_POWER_VEC_SIZE - 1)) {
+ if (le32_to_cpu(cmd->sleep_interval[i]) >
+ le32_to_cpu(cmd->sleep_interval[i+1]))
+ cmd->sleep_interval[i] =
+ cmd->sleep_interval[i+1];
+ }
+ }
if (priv->power_data.pci_pm)
cmd->flags |= IWL_POWER_PCI_PM_MSK;
else
cmd->flags &= ~IWL_POWER_PCI_PM_MSK;
+ IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n",
+ skip, period);
IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1);
}
@@ -165,26 +196,26 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
*=============================================================================
* Condition Nxt State Condition Nxt State Condition Nxt State
*-----------------------------------------------------------------------------
- * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A
- * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0
- * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1
+ * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
+ * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
+ * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
* IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
*=============================================================================
*/
static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = {
{IWL_TI_0, IWL_ABSOLUTE_ZERO, 104},
- {IWL_TI_1, 105, CT_KILL_THRESHOLD},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}
+ {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1},
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
};
static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = {
{IWL_TI_0, IWL_ABSOLUTE_ZERO, 95},
- {IWL_TI_2, 110, CT_KILL_THRESHOLD},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}
+ {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1},
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
};
static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = {
{IWL_TI_1, IWL_ABSOLUTE_ZERO, 100},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX},
- {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX},
+ {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}
};
static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = {
{IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD},
@@ -294,6 +325,9 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force)
if (priv->cfg->broken_powersave)
iwl_power_sleep_cam_cmd(priv, &cmd);
+ else if (priv->cfg->supports_idle &&
+ priv->hw->conf.flags & IEEE80211_CONF_IDLE)
+ iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_5, 20);
else if (tt->state >= IWL_TI_1)
iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper);
else if (!enabled)
@@ -348,6 +382,23 @@ bool iwl_ht_enabled(struct iwl_priv *priv)
}
EXPORT_SYMBOL(iwl_ht_enabled);
+bool iwl_within_ct_kill_margin(struct iwl_priv *priv)
+{
+ s32 temp = priv->temperature; /* degrees CELSIUS except 4965 */
+ bool within_margin = false;
+
+ if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965)
+ temp = KELVIN_TO_CELSIUS(priv->temperature);
+
+ if (!priv->thermal_throttle.advanced_tt)
+ within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
+ CT_KILL_THRESHOLD_LEGACY) ? true : false;
+ else
+ within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >=
+ CT_KILL_THRESHOLD) ? true : false;
+ return within_margin;
+}
+
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv)
{
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
@@ -372,6 +423,7 @@ enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv)
}
#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */
+#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */
/*
* toggle the bit to wake up uCode and check the temperature
@@ -409,6 +461,7 @@ static void iwl_tt_check_exit_ct_kill(unsigned long data)
/* Reschedule the ct_kill timer to occur in
* CT_KILL_EXIT_DURATION seconds to ensure we get a
* thermal update */
+ IWL_DEBUG_POWER(priv, "schedule ct_kill exit timer\n");
mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies +
CT_KILL_EXIT_DURATION * HZ);
}
@@ -432,6 +485,33 @@ static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
}
}
+static void iwl_tt_ready_for_ct_kill(unsigned long data)
+{
+ struct iwl_priv *priv = (struct iwl_priv *)data;
+ struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ /* temperature timer expired, ready to go into CT_KILL state */
+ if (tt->state != IWL_TI_CT_KILL) {
+ IWL_DEBUG_POWER(priv, "entering CT_KILL state when temperature timer expired\n");
+ tt->state = IWL_TI_CT_KILL;
+ set_bit(STATUS_CT_KILL, &priv->status);
+ iwl_perform_ct_kill_task(priv, true);
+ }
+}
+
+static void iwl_prepare_ct_kill_task(struct iwl_priv *priv)
+{
+ IWL_DEBUG_POWER(priv, "Prepare to enter IWL_TI_CT_KILL\n");
+ /* make request to retrieve statistics information */
+ iwl_send_statistics_request(priv, CMD_SYNC, false);
+ /* Reschedule the ct_kill wait timer */
+ mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm,
+ jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION));
+}
+
#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY)
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100)
#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90)
@@ -445,7 +525,7 @@ static void iwl_perform_ct_kill_task(struct iwl_priv *priv,
* Throttle early enough to lower the power consumption before
* drastic steps are needed
*/
-static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp)
+static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
{
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
enum iwl_tt_state old_state;
@@ -474,6 +554,8 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp)
#ifdef CONFIG_IWLWIFI_DEBUG
tt->tt_previous_temp = temp;
#endif
+ /* stop ct_kill_waiting_tm timer */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
if (tt->state != old_state) {
switch (tt->state) {
case IWL_TI_0:
@@ -494,17 +576,28 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp)
break;
}
mutex_lock(&priv->mutex);
- if (iwl_power_update_mode(priv, true)) {
+ if (old_state == IWL_TI_CT_KILL)
+ clear_bit(STATUS_CT_KILL, &priv->status);
+ if (tt->state != IWL_TI_CT_KILL &&
+ iwl_power_update_mode(priv, true)) {
/* TT state not updated
* try again during next temperature read
*/
+ if (old_state == IWL_TI_CT_KILL)
+ set_bit(STATUS_CT_KILL, &priv->status);
tt->state = old_state;
IWL_ERR(priv, "Cannot update power mode, "
"TT state not updated\n");
} else {
- if (tt->state == IWL_TI_CT_KILL)
- iwl_perform_ct_kill_task(priv, true);
- else if (old_state == IWL_TI_CT_KILL &&
+ if (tt->state == IWL_TI_CT_KILL) {
+ if (force) {
+ set_bit(STATUS_CT_KILL, &priv->status);
+ iwl_perform_ct_kill_task(priv, true);
+ } else {
+ iwl_prepare_ct_kill_task(priv);
+ tt->state = old_state;
+ }
+ } else if (old_state == IWL_TI_CT_KILL &&
tt->state != IWL_TI_CT_KILL)
iwl_perform_ct_kill_task(priv, false);
IWL_DEBUG_POWER(priv, "Temperature state changed %u\n",
@@ -531,13 +624,13 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp)
*=============================================================================
* Condition Nxt State Condition Nxt State Condition Nxt State
*-----------------------------------------------------------------------------
- * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A
- * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0
- * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1
+ * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A
+ * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0
+ * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1
* IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0
*=============================================================================
*/
-static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp)
+static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force)
{
struct iwl_tt_mgmt *tt = &priv->thermal_throttle;
int i;
@@ -582,6 +675,8 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp)
break;
}
}
+ /* stop ct_kill_waiting_tm timer */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
if (changed) {
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
@@ -613,12 +708,17 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp)
iwl_set_rxon_ht(priv, &priv->current_ht_config);
}
mutex_lock(&priv->mutex);
- if (iwl_power_update_mode(priv, true)) {
+ if (old_state == IWL_TI_CT_KILL)
+ clear_bit(STATUS_CT_KILL, &priv->status);
+ if (tt->state != IWL_TI_CT_KILL &&
+ iwl_power_update_mode(priv, true)) {
/* TT state not updated
* try again during next temperature read
*/
IWL_ERR(priv, "Cannot update power mode, "
"TT state not updated\n");
+ if (old_state == IWL_TI_CT_KILL)
+ set_bit(STATUS_CT_KILL, &priv->status);
tt->state = old_state;
} else {
IWL_DEBUG_POWER(priv,
@@ -626,9 +726,15 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp)
tt->state);
if (old_state != IWL_TI_CT_KILL &&
tt->state == IWL_TI_CT_KILL) {
- IWL_DEBUG_POWER(priv, "Enter IWL_TI_CT_KILL\n");
- iwl_perform_ct_kill_task(priv, true);
-
+ if (force) {
+ IWL_DEBUG_POWER(priv,
+ "Enter IWL_TI_CT_KILL\n");
+ set_bit(STATUS_CT_KILL, &priv->status);
+ iwl_perform_ct_kill_task(priv, true);
+ } else {
+ iwl_prepare_ct_kill_task(priv);
+ tt->state = old_state;
+ }
} else if (old_state == IWL_TI_CT_KILL &&
tt->state != IWL_TI_CT_KILL) {
IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n");
@@ -665,10 +771,11 @@ static void iwl_bg_ct_enter(struct work_struct *work)
"- ucode going to sleep!\n");
if (!priv->thermal_throttle.advanced_tt)
iwl_legacy_tt_handler(priv,
- IWL_MINIMAL_POWER_THRESHOLD);
+ IWL_MINIMAL_POWER_THRESHOLD,
+ true);
else
iwl_advance_tt_handler(priv,
- CT_KILL_THRESHOLD + 1);
+ CT_KILL_THRESHOLD + 1, true);
}
}
@@ -695,11 +802,18 @@ static void iwl_bg_ct_exit(struct work_struct *work)
IWL_ERR(priv,
"Device temperature below critical"
"- ucode awake!\n");
+ /*
+ * exit from CT_KILL state
+ * reset the current temperature reading
+ */
+ priv->temperature = 0;
if (!priv->thermal_throttle.advanced_tt)
iwl_legacy_tt_handler(priv,
- IWL_REDUCED_PERFORMANCE_THRESHOLD_2);
+ IWL_REDUCED_PERFORMANCE_THRESHOLD_2,
+ true);
else
- iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD);
+ iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD,
+ true);
}
}
@@ -735,9 +849,9 @@ static void iwl_bg_tt_work(struct work_struct *work)
temp = KELVIN_TO_CELSIUS(priv->temperature);
if (!priv->thermal_throttle.advanced_tt)
- iwl_legacy_tt_handler(priv, temp);
+ iwl_legacy_tt_handler(priv, temp, false);
else
- iwl_advance_tt_handler(priv, temp);
+ iwl_advance_tt_handler(priv, temp, false);
}
void iwl_tt_handler(struct iwl_priv *priv)
@@ -768,16 +882,18 @@ void iwl_tt_initialize(struct iwl_priv *priv)
tt->state = IWL_TI_0;
init_timer(&priv->thermal_throttle.ct_kill_exit_tm);
priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv;
- priv->thermal_throttle.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill;
-
+ priv->thermal_throttle.ct_kill_exit_tm.function =
+ iwl_tt_check_exit_ct_kill;
+ init_timer(&priv->thermal_throttle.ct_kill_waiting_tm);
+ priv->thermal_throttle.ct_kill_waiting_tm.data = (unsigned long)priv;
+ priv->thermal_throttle.ct_kill_waiting_tm.function =
+ iwl_tt_ready_for_ct_kill;
/* setup deferred ct kill work */
INIT_WORK(&priv->tt_work, iwl_bg_tt_work);
INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
- switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
- case CSR_HW_REV_TYPE_6x00:
- case CSR_HW_REV_TYPE_6x50:
+ if (priv->cfg->adv_thermal_throttle) {
IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n");
tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) *
IWL_TI_STATE_MAX, GFP_KERNEL);
@@ -810,11 +926,9 @@ void iwl_tt_initialize(struct iwl_priv *priv)
&restriction_range[0], size);
priv->thermal_throttle.advanced_tt = true;
}
- break;
- default:
+ } else {
IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n");
priv->thermal_throttle.advanced_tt = false;
- break;
}
}
EXPORT_SYMBOL(iwl_tt_initialize);
@@ -826,6 +940,8 @@ void iwl_tt_exit(struct iwl_priv *priv)
/* stop ct_kill_exit_tm timer if activated */
del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm);
+ /* stop ct_kill_waiting_tm timer if activated */
+ del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm);
cancel_work_sync(&priv->tt_work);
cancel_work_sync(&priv->ct_enter);
cancel_work_sync(&priv->ct_exit);
diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h
index df6f6a49712..310c32e8f69 100644
--- a/drivers/net/wireless/iwlwifi/iwl-power.h
+++ b/drivers/net/wireless/iwlwifi/iwl-power.h
@@ -33,6 +33,7 @@
#define IWL_ABSOLUTE_ZERO 0
#define IWL_ABSOLUTE_MAX 0xFFFFFFFF
#define IWL_TT_INCREASE_MARGIN 5
+#define IWL_TT_CT_KILL_MARGIN 3
enum iwl_antenna_ok {
IWL_ANT_OK_NONE,
@@ -110,6 +111,7 @@ struct iwl_tt_mgmt {
struct iwl_tt_restriction *restriction;
struct iwl_tt_trans *transaction;
struct timer_list ct_kill_exit_tm;
+ struct timer_list ct_kill_waiting_tm;
};
enum iwl_power_level {
@@ -129,6 +131,7 @@ struct iwl_power_mgr {
int iwl_power_update_mode(struct iwl_priv *priv, bool force);
bool iwl_ht_enabled(struct iwl_priv *priv);
+bool iwl_within_ct_kill_margin(struct iwl_priv *priv);
enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv);
enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv);
void iwl_tt_enter_ct_kill(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index d393e8f0210..6d95832db06 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -254,7 +254,8 @@
* device. A queue maps to only one (selectable by driver) Tx DMA channel,
* but one DMA channel may take input from several queues.
*
- * Tx DMA channels have dedicated purposes. For 4965, they are used as follows:
+ * Tx DMA channels have dedicated purposes. For 4965, they are used as follows
+ * (cf. default_queue_to_tx_fifo in iwl-4965.c):
*
* 0 -- EDCA BK (background) frames, lowest priority
* 1 -- EDCA BE (best effort) frames, normal priority
@@ -265,9 +266,21 @@
* 6 -- HCCA long frames
* 7 -- not used by driver (device-internal only)
*
+ * For 5000 series and up, they are used slightly differently
+ * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c):
+ *
+ * 0 -- EDCA BK (background) frames, lowest priority
+ * 1 -- EDCA BE (best effort) frames, normal priority
+ * 2 -- EDCA VI (video) frames, higher priority
+ * 3 -- EDCA VO (voice) and management frames, highest priority
+ * 4 -- (TBD)
+ * 5 -- HCCA short frames
+ * 6 -- HCCA long frames
+ * 7 -- Commands
+ *
* Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6.
- * In addition, driver can map queues 7-15 to Tx DMA/FIFO channels 0-3 to
- * support 11n aggregation via EDCA DMA channels.
+ * In addition, driver can map the remaining queues to Tx DMA/FIFO
+ * channels 0-3 to support 11n aggregation via EDCA DMA channels.
*
* The driver sets up each queue to work in one of two modes:
*
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 493626bcd3e..6090bc15a6d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -140,6 +140,8 @@ int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
+ IWL_DEBUG_INFO(priv, "Rx queue requesting wakeup, GP1 = 0x%x\n",
+ reg);
iwl_set_bit(priv, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
goto exit_unlock;
@@ -200,7 +202,7 @@ int iwl_rx_queue_restock(struct iwl_priv *priv)
list_del(element);
/* Point to Rx buffer via next RBD in circular buffer */
- rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->aligned_dma_addr);
+ rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->page_dma);
rxq->queue[rxq->write] = rxb;
rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
rxq->free_count--;
@@ -239,8 +241,9 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
struct iwl_rx_queue *rxq = &priv->rxq;
struct list_head *element;
struct iwl_rx_mem_buffer *rxb;
- struct sk_buff *skb;
+ struct page *page;
unsigned long flags;
+ gfp_t gfp_mask = priority;
while (1) {
spin_lock_irqsave(&rxq->lock, flags);
@@ -251,30 +254,35 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
spin_unlock_irqrestore(&rxq->lock, flags);
if (rxq->free_count > RX_LOW_WATERMARK)
- priority |= __GFP_NOWARN;
- /* Alloc a new receive buffer */
- skb = alloc_skb(priv->hw_params.rx_buf_size + 256,
- priority);
+ gfp_mask |= __GFP_NOWARN;
+
+ if (priv->hw_params.rx_page_order > 0)
+ gfp_mask |= __GFP_COMP;
- if (!skb) {
+ /* Alloc a new receive buffer */
+ page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order);
+ if (!page) {
if (net_ratelimit())
- IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n");
+ IWL_DEBUG_INFO(priv, "alloc_pages failed, "
+ "order: %d\n",
+ priv->hw_params.rx_page_order);
+
if ((rxq->free_count <= RX_LOW_WATERMARK) &&
net_ratelimit())
- IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n",
+ IWL_CRIT(priv, "Failed to alloc_pages with %s. Only %u free buffers remaining.\n",
priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL",
rxq->free_count);
/* We don't reschedule replenish work here -- we will
* call the restock method and if it still needs
* more buffers it will schedule replenish */
- break;
+ return;
}
spin_lock_irqsave(&rxq->lock, flags);
if (list_empty(&rxq->rx_used)) {
spin_unlock_irqrestore(&rxq->lock, flags);
- dev_kfree_skb_any(skb);
+ __free_pages(page, priv->hw_params.rx_page_order);
return;
}
element = rxq->rx_used.next;
@@ -283,24 +291,21 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
spin_unlock_irqrestore(&rxq->lock, flags);
- rxb->skb = skb;
- /* Get physical address of RB/SKB */
- rxb->real_dma_addr = pci_map_single(
- priv->pci_dev,
- rxb->skb->data,
- priv->hw_params.rx_buf_size + 256,
- PCI_DMA_FROMDEVICE);
+ rxb->page = page;
+ /* Get physical address of the RB */
+ rxb->page_dma = pci_map_page(priv->pci_dev, page, 0,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
/* dma address must be no more than 36 bits */
- BUG_ON(rxb->real_dma_addr & ~DMA_BIT_MASK(36));
+ BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
/* and also 256 byte aligned! */
- rxb->aligned_dma_addr = ALIGN(rxb->real_dma_addr, 256);
- skb_reserve(rxb->skb, rxb->aligned_dma_addr - rxb->real_dma_addr);
+ BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
spin_lock_irqsave(&rxq->lock, flags);
list_add_tail(&rxb->list, &rxq->rx_free);
rxq->free_count++;
- priv->alloc_rxb_skb++;
+ priv->alloc_rxb_page++;
spin_unlock_irqrestore(&rxq->lock, flags);
}
@@ -336,12 +341,14 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
{
int i;
for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
- if (rxq->pool[i].skb != NULL) {
- pci_unmap_single(priv->pci_dev,
- rxq->pool[i].real_dma_addr,
- priv->hw_params.rx_buf_size + 256,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb(rxq->pool[i].skb);
+ if (rxq->pool[i].page != NULL) {
+ pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ __free_pages(rxq->pool[i].page,
+ priv->hw_params.rx_page_order);
+ rxq->pool[i].page = NULL;
+ priv->alloc_rxb_page--;
}
}
@@ -405,14 +412,14 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
/* In the reset function, these buffers may have been allocated
* to an SKB, so we need to unmap and free potential storage */
- if (rxq->pool[i].skb != NULL) {
- pci_unmap_single(priv->pci_dev,
- rxq->pool[i].real_dma_addr,
- priv->hw_params.rx_buf_size + 256,
- PCI_DMA_FROMDEVICE);
- priv->alloc_rxb_skb--;
- dev_kfree_skb(rxq->pool[i].skb);
- rxq->pool[i].skb = NULL;
+ if (rxq->pool[i].page != NULL) {
+ pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ priv->alloc_rxb_page--;
+ __free_pages(rxq->pool[i].page,
+ priv->hw_params.rx_page_order);
+ rxq->pool[i].page = NULL;
}
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
}
@@ -470,7 +477,8 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
(rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
(rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
- iwl_write32(priv, CSR_INT_COALESCING, 0x40);
+ /* Set interrupt coalescing timer to 64 x 32 = 2048 usecs */
+ iwl_write8(priv, CSR_INT_COALESCING, 0x40);
return 0;
}
@@ -491,7 +499,7 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_missed_beacon_notif *missed_beacon;
missed_beacon = &pkt->u.missed_beacon;
@@ -548,13 +556,51 @@ static void iwl_rx_calc_noise(struct iwl_priv *priv)
priv->last_rx_noise);
}
+#ifdef CONFIG_IWLWIFI_DEBUG
+/*
+ * based on the assumption of all statistics counter are in DWORD
+ * FIXME: This function is for debugging, do not deal with
+ * the case of counters roll-over.
+ */
+static void iwl_accumulative_statistics(struct iwl_priv *priv,
+ __le32 *stats)
+{
+ int i;
+ __le32 *prev_stats;
+ u32 *accum_stats;
+
+ prev_stats = (__le32 *)&priv->statistics;
+ accum_stats = (u32 *)&priv->accum_statistics;
+
+ for (i = sizeof(__le32); i < sizeof(struct iwl_notif_statistics);
+ i += sizeof(__le32), stats++, prev_stats++, accum_stats++)
+ if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats))
+ *accum_stats += (le32_to_cpu(*stats) -
+ le32_to_cpu(*prev_stats));
+
+ /* reset accumulative statistics for "no-counter" type statistics */
+ priv->accum_statistics.general.temperature =
+ priv->statistics.general.temperature;
+ priv->accum_statistics.general.temperature_m =
+ priv->statistics.general.temperature_m;
+ priv->accum_statistics.general.ttl_timestamp =
+ priv->statistics.general.ttl_timestamp;
+ priv->accum_statistics.tx.tx_power.ant_a =
+ priv->statistics.tx.tx_power.ant_a;
+ priv->accum_statistics.tx.tx_power.ant_b =
+ priv->statistics.tx.tx_power.ant_b;
+ priv->accum_statistics.tx.tx_power.ant_c =
+ priv->statistics.tx.tx_power.ant_c;
+}
+#endif
+
#define REG_RECALIB_PERIOD (60)
void iwl_rx_statistics(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
int change;
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
(int)sizeof(priv->statistics),
@@ -566,6 +612,9 @@ void iwl_rx_statistics(struct iwl_priv *priv,
STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
(pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
+#ifdef CONFIG_IWLWIFI_DEBUG
+ iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats);
+#endif
memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
set_bit(STATUS_STATISTICS, &priv->status);
@@ -582,14 +631,29 @@ void iwl_rx_statistics(struct iwl_priv *priv,
iwl_rx_calc_noise(priv);
queue_work(priv->workqueue, &priv->run_time_calib_work);
}
-
- iwl_leds_background(priv);
-
if (priv->cfg->ops->lib->temp_ops.temperature && change)
priv->cfg->ops->lib->temp_ops.temperature(priv);
}
EXPORT_SYMBOL(iwl_rx_statistics);
+void iwl_reply_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+ if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) {
+ memset(&priv->statistics, 0,
+ sizeof(struct iwl_notif_statistics));
+#ifdef CONFIG_IWLWIFI_DEBUG
+ memset(&priv->accum_statistics, 0,
+ sizeof(struct iwl_notif_statistics));
+#endif
+ IWL_DEBUG_RX(priv, "Statistics have been cleared\n");
+ }
+ iwl_rx_statistics(priv, rxb);
+}
+EXPORT_SYMBOL(iwl_reply_statistics);
+
#define PERFECT_RSSI (-20) /* dBm */
#define WORST_RSSI (-95) /* dBm */
#define RSSI_RANGE (PERFECT_RSSI - WORST_RSSI)
@@ -878,6 +942,10 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb,
struct ieee80211_rx_status *stats)
{
+ struct sk_buff *skb;
+ int ret = 0;
+ __le16 fc = hdr->frame_control;
+
/* We only process data packets if the interface is open */
if (unlikely(!priv->is_open)) {
IWL_DEBUG_DROP_LIMIT(priv,
@@ -890,15 +958,44 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
iwl_set_decrypted_flag(priv, hdr, ampdu_status, stats))
return;
- /* Resize SKB from mac header to end of packet */
- skb_reserve(rxb->skb, (void *)hdr - (void *)rxb->skb->data);
- skb_put(rxb->skb, len);
+ skb = alloc_skb(IWL_LINK_HDR_MAX * 2, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(priv, "alloc_skb failed\n");
+ return;
+ }
+
+ skb_reserve(skb, IWL_LINK_HDR_MAX);
+ skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len);
+
+ /* mac80211 currently doesn't support paged SKB. Convert it to
+ * linear SKB for management frame and data frame requires
+ * software decryption or software defragementation. */
+ if (ieee80211_is_mgmt(fc) ||
+ ieee80211_has_protected(fc) ||
+ ieee80211_has_morefrags(fc) ||
+ le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
+ ret = skb_linearize(skb);
+ else
+ ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ?
+ 0 : -ENOMEM;
+
+ if (ret) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ /*
+ * XXX: We cannot touch the page and its virtual memory (hdr) after
+ * here. It might have already been freed by the above skb change.
+ */
- iwl_update_stats(priv, false, hdr->frame_control, len);
- memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats));
- ieee80211_rx_irqsafe(priv->hw, rxb->skb);
- priv->alloc_rxb_skb--;
- rxb->skb = NULL;
+ iwl_update_stats(priv, false, fc, len);
+ memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
+
+ ieee80211_rx(priv->hw, skb);
+ out:
+ priv->alloc_rxb_page--;
+ rxb->page = NULL;
}
/* This is necessary only for a number of statistics, see the caller. */
@@ -926,13 +1023,12 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
{
struct ieee80211_hdr *header;
struct ieee80211_rx_status rx_status;
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_rx_phy_res *phy_res;
__le32 rx_pkt_status;
struct iwl4965_rx_mpdu_res_start *amsdu;
u32 len;
u32 ampdu_status;
- u16 fc;
u32 rate_n_flags;
/**
@@ -1065,20 +1161,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv,
priv->last_tsf = le64_to_cpu(phy_res->timestamp);
}
- fc = le16_to_cpu(header->frame_control);
- switch (fc & IEEE80211_FCTL_FTYPE) {
- case IEEE80211_FTYPE_MGMT:
- case IEEE80211_FTYPE_DATA:
- if (priv->iw_mode == NL80211_IFTYPE_AP)
- iwl_update_ps_mode(priv, fc & IEEE80211_FCTL_PM,
- header->addr2);
- /* fall through */
- default:
- iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status,
- rxb, &rx_status);
- break;
-
- }
+ iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status,
+ rxb, &rx_status);
}
EXPORT_SYMBOL(iwl_rx_reply_rx);
@@ -1087,7 +1171,7 @@ EXPORT_SYMBOL(iwl_rx_reply_rx);
void iwl_rx_reply_rx_phy(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
priv->last_phy_res[0] = 1;
memcpy(&priv->last_phy_res[1], &(pkt->u.raw[0]),
sizeof(struct iwl_rx_phy_res));
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index 4f3a108fa99..a2b2b8315ff 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -27,7 +27,6 @@
*****************************************************************************/
#include <linux/types.h>
#include <linux/etherdevice.h>
-#include <net/lib80211.h>
#include <net/mac80211.h>
#include "iwl-eeprom.h"
@@ -112,7 +111,7 @@ EXPORT_SYMBOL(iwl_scan_cancel_timeout);
static int iwl_send_scan_abort(struct iwl_priv *priv)
{
int ret = 0;
- struct iwl_rx_packet *res;
+ struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = REPLY_SCAN_ABORT_CMD,
.flags = CMD_WANT_SKB,
@@ -132,21 +131,21 @@ static int iwl_send_scan_abort(struct iwl_priv *priv)
return ret;
}
- res = (struct iwl_rx_packet *)cmd.reply_skb->data;
- if (res->u.status != CAN_ABORT_STATUS) {
+ pkt = (struct iwl_rx_packet *)cmd.reply_page;
+ if (pkt->u.status != CAN_ABORT_STATUS) {
/* The scan abort will return 1 for success or
* 2 for "failure". A failure condition can be
* due to simply not being in an active scan which
* can occur if we send the scan abort before we
* the microcode has notified us that a scan is
* completed. */
- IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", res->u.status);
+ IWL_DEBUG_INFO(priv, "SCAN_ABORT returned %d.\n", pkt->u.status);
clear_bit(STATUS_SCAN_ABORTING, &priv->status);
clear_bit(STATUS_SCAN_HW, &priv->status);
}
- priv->alloc_rxb_skb--;
- dev_kfree_skb_any(cmd.reply_skb);
+ priv->alloc_rxb_page--;
+ free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
return ret;
}
@@ -156,7 +155,7 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scanreq_notification *notif =
(struct iwl_scanreq_notification *)pkt->u.raw;
@@ -168,7 +167,7 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv,
static void iwl_rx_scan_start_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scanstart_notification *notif =
(struct iwl_scanstart_notification *)pkt->u.raw;
priv->scan_start_tsf = le32_to_cpu(notif->tsf_low);
@@ -187,7 +186,7 @@ static void iwl_rx_scan_results_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scanresults_notification *notif =
(struct iwl_scanresults_notification *)pkt->u.raw;
@@ -214,7 +213,7 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw;
IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n",
@@ -402,6 +401,7 @@ void iwl_init_scan_params(struct iwl_priv *priv)
if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ])
priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx;
}
+EXPORT_SYMBOL(iwl_init_scan_params);
static int iwl_scan_initiate(struct iwl_priv *priv)
{
@@ -581,6 +581,7 @@ static void iwl_bg_request_scan(struct work_struct *data)
u8 rate;
bool is_active = false;
int chan_mod;
+ u8 active_chains;
conf = ieee80211_get_hw_conf(priv->hw);
@@ -734,9 +735,22 @@ static void iwl_bg_request_scan(struct work_struct *data)
rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]);
scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags);
+ /* In power save mode use one chain, otherwise use all chains */
+ if (test_bit(STATUS_POWER_PMI, &priv->status)) {
+ /* rx_ant has been set to all valid chains previously */
+ active_chains = rx_ant &
+ ((u8)(priv->chain_noise_data.active_chains));
+ if (!active_chains)
+ active_chains = rx_ant;
+
+ IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n",
+ priv->chain_noise_data.active_chains);
+
+ rx_ant = first_antenna(active_chains);
+ }
/* MIMO is not used here, but value is required */
- rx_chain |= ANT_ABC << RXON_RX_CHAIN_VALID_POS;
- rx_chain |= ANT_ABC << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
+ rx_chain |= priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS;
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS;
rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
scan->rx_chain = cpu_to_le16(rx_chain);
diff --git a/drivers/net/wireless/iwlwifi/iwl-spectrum.c b/drivers/net/wireless/iwlwifi/iwl-spectrum.c
index 022bcf11573..1ea5cd345fe 100644
--- a/drivers/net/wireless/iwlwifi/iwl-spectrum.c
+++ b/drivers/net/wireless/iwlwifi/iwl-spectrum.c
@@ -177,7 +177,7 @@ static int iwl_get_measurement(struct iwl_priv *priv,
static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif);
if (!report->state) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index c6633fec821..cd6a6901216 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -99,32 +99,25 @@ static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
static void iwl_add_sta_callback(struct iwl_priv *priv,
struct iwl_device_cmd *cmd,
- struct sk_buff *skb)
+ struct iwl_rx_packet *pkt)
{
- struct iwl_rx_packet *res = NULL;
struct iwl_addsta_cmd *addsta =
(struct iwl_addsta_cmd *)cmd->cmd.payload;
u8 sta_id = addsta->sta.sta_id;
- if (!skb) {
- IWL_ERR(priv, "Error: Response NULL in REPLY_ADD_STA.\n");
- return;
- }
-
- res = (struct iwl_rx_packet *)skb->data;
- if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n",
- res->hdr.flags);
+ pkt->hdr.flags);
return;
}
- switch (res->u.add_sta.status) {
+ switch (pkt->u.add_sta.status) {
case ADD_STA_SUCCESS_MSK:
iwl_sta_ucode_activate(priv, sta_id);
/* fall through */
default:
IWL_DEBUG_HC(priv, "Received REPLY_ADD_STA:(0x%08X)\n",
- res->u.add_sta.status);
+ pkt->u.add_sta.status);
break;
}
}
@@ -132,7 +125,7 @@ static void iwl_add_sta_callback(struct iwl_priv *priv,
int iwl_send_add_sta(struct iwl_priv *priv,
struct iwl_addsta_cmd *sta, u8 flags)
{
- struct iwl_rx_packet *res = NULL;
+ struct iwl_rx_packet *pkt = NULL;
int ret = 0;
u8 data[sizeof(*sta)];
struct iwl_host_cmd cmd = {
@@ -152,15 +145,15 @@ int iwl_send_add_sta(struct iwl_priv *priv,
if (ret || (flags & CMD_ASYNC))
return ret;
- res = (struct iwl_rx_packet *)cmd.reply_skb->data;
- if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+ pkt = (struct iwl_rx_packet *)cmd.reply_page;
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n",
- res->hdr.flags);
+ pkt->hdr.flags);
ret = -EIO;
}
if (ret == 0) {
- switch (res->u.add_sta.status) {
+ switch (pkt->u.add_sta.status) {
case ADD_STA_SUCCESS_MSK:
iwl_sta_ucode_activate(priv, sta->sta.sta_id);
IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n");
@@ -172,8 +165,8 @@ int iwl_send_add_sta(struct iwl_priv *priv,
}
}
- priv->alloc_rxb_skb--;
- dev_kfree_skb_any(cmd.reply_skb);
+ priv->alloc_rxb_page--;
+ free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
return ret;
}
@@ -189,6 +182,11 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
goto done;
mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2;
+ IWL_DEBUG_ASSOC(priv, "spatial multiplexing power save mode: %s\n",
+ (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ?
+ "static" :
+ (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ?
+ "dynamic" : "disabled");
sta_flags = priv->stations[index].sta.station_flags;
@@ -324,26 +322,19 @@ static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const char *addr)
static void iwl_remove_sta_callback(struct iwl_priv *priv,
struct iwl_device_cmd *cmd,
- struct sk_buff *skb)
+ struct iwl_rx_packet *pkt)
{
- struct iwl_rx_packet *res = NULL;
struct iwl_rem_sta_cmd *rm_sta =
- (struct iwl_rem_sta_cmd *)cmd->cmd.payload;
+ (struct iwl_rem_sta_cmd *)cmd->cmd.payload;
const char *addr = rm_sta->addr;
- if (!skb) {
- IWL_ERR(priv, "Error: Response NULL in REPLY_REMOVE_STA.\n");
- return;
- }
-
- res = (struct iwl_rx_packet *)skb->data;
- if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n",
- res->hdr.flags);
+ pkt->hdr.flags);
return;
}
- switch (res->u.rem_sta.status) {
+ switch (pkt->u.rem_sta.status) {
case REM_STA_SUCCESS_MSK:
iwl_sta_ucode_deactivate(priv, addr);
break;
@@ -356,7 +347,7 @@ static void iwl_remove_sta_callback(struct iwl_priv *priv,
static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
u8 flags)
{
- struct iwl_rx_packet *res = NULL;
+ struct iwl_rx_packet *pkt;
int ret;
struct iwl_rem_sta_cmd rm_sta_cmd;
@@ -381,15 +372,15 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
if (ret || (flags & CMD_ASYNC))
return ret;
- res = (struct iwl_rx_packet *)cmd.reply_skb->data;
- if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+ pkt = (struct iwl_rx_packet *)cmd.reply_page;
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n",
- res->hdr.flags);
+ pkt->hdr.flags);
ret = -EIO;
}
if (!ret) {
- switch (res->u.rem_sta.status) {
+ switch (pkt->u.rem_sta.status) {
case REM_STA_SUCCESS_MSK:
iwl_sta_ucode_deactivate(priv, addr);
IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
@@ -401,8 +392,8 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
}
}
- priv->alloc_rxb_skb--;
- dev_kfree_skb_any(cmd.reply_skb);
+ priv->alloc_rxb_page--;
+ free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
return ret;
}
@@ -1026,7 +1017,7 @@ int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
*/
if (priv->current_ht_config.is_ht) {
rcu_read_lock();
- sta = ieee80211_find_sta(priv->hw, addr);
+ sta = ieee80211_find_sta(priv->vif, addr);
if (sta) {
memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config));
cur_ht_config = &ht_config;
@@ -1044,6 +1035,68 @@ int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
EXPORT_SYMBOL(iwl_rxon_add_station);
/**
+ * iwl_sta_init_bcast_lq - Initialize a bcast station's hardware rate table
+ *
+ * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
+ * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
+ * which requires station table entry to exist).
+ */
+static void iwl_sta_init_bcast_lq(struct iwl_priv *priv)
+{
+ int i, r;
+ struct iwl_link_quality_cmd link_cmd = {
+ .reserved1 = 0,
+ };
+ u32 rate_flags;
+
+ /* Set up the rate scaling to start at selected rate, fall back
+ * all the way down to 1M in IEEE order, and then spin on 1M */
+ if (priv->band == IEEE80211_BAND_5GHZ)
+ r = IWL_RATE_6M_INDEX;
+ else
+ r = IWL_RATE_1M_INDEX;
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ rate_flags = 0;
+ if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
+ rate_flags |= RATE_MCS_CCK_MSK;
+
+ rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
+ RATE_MCS_ANT_POS;
+
+ link_cmd.rs_table[i].rate_n_flags =
+ iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
+ r = iwl_get_prev_ieee_rate(r);
+ }
+
+ link_cmd.general_params.single_stream_ant_msk =
+ first_antenna(priv->hw_params.valid_tx_ant);
+ link_cmd.general_params.dual_stream_ant_msk = 3;
+ link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+ link_cmd.agg_params.agg_time_limit =
+ cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+
+ /* Update the rate scaling for control frame Tx to AP */
+ link_cmd.sta_id = priv->hw_params.bcast_sta_id;
+
+ iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD,
+ sizeof(link_cmd), &link_cmd, NULL);
+}
+
+
+/**
+ * iwl_add_bcast_station - add broadcast station into station table.
+ */
+void iwl_add_bcast_station(struct iwl_priv *priv)
+{
+ iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL);
+
+ /* Set up default rate scaling table in device's station table */
+ iwl_sta_init_bcast_lq(priv);
+}
+EXPORT_SYMBOL(iwl_add_bcast_station);
+
+/**
* iwl_get_sta_id - Find station's index within station table
*
* If new IBSS station, create new entry in station table
@@ -1163,7 +1216,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid)
}
EXPORT_SYMBOL(iwl_sta_rx_agg_stop);
-static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
+void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
{
unsigned long flags;
@@ -1171,27 +1224,26 @@ static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id)
priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK;
priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
priv->stations[sta_id].sta.sta.modify_mask = 0;
+ priv->stations[sta_id].sta.sleep_tx_count = 0;
priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
spin_unlock_irqrestore(&priv->sta_lock, flags);
iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
}
+EXPORT_SYMBOL(iwl_sta_modify_ps_wake);
-void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr)
+void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
{
- /* FIXME: need locking over ps_status ??? */
- u8 sta_id = iwl_find_station(priv, addr);
+ unsigned long flags;
- if (sta_id != IWL_INVALID_STATION) {
- u8 sta_awake = priv->stations[sta_id].
- ps_status == STA_PS_STATUS_WAKE;
+ spin_lock_irqsave(&priv->sta_lock, flags);
+ priv->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK;
+ priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK;
+ priv->stations[sta_id].sta.sta.modify_mask =
+ STA_MODIFY_SLEEP_TX_COUNT_MSK;
+ priv->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt);
+ priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
+ spin_unlock_irqrestore(&priv->sta_lock, flags);
- if (sta_awake && ps_bit)
- priv->stations[sta_id].ps_status = STA_PS_STATUS_SLEEP;
- else if (!sta_awake && !ps_bit) {
- iwl_sta_modify_ps_wake(priv, sta_id);
- priv->stations[sta_id].ps_status = STA_PS_STATUS_WAKE;
- }
- }
+ iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
}
-
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h
index 6deebade636..8d052de2d40 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.h
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.h
@@ -52,6 +52,7 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
const u8 *addr, u32 iv32, u16 *phase1key);
int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap);
+void iwl_add_bcast_station(struct iwl_priv *priv);
int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap);
void iwl_clear_stations_table(struct iwl_priv *priv);
int iwl_get_free_ucode_key_index(struct iwl_priv *priv);
@@ -65,5 +66,6 @@ void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
int iwl_sta_rx_agg_start(struct iwl_priv *priv,
const u8 *addr, int tid, u16 ssn);
int iwl_sta_rx_agg_stop(struct iwl_priv *priv, const u8 *addr, int tid);
-void iwl_update_ps_mode(struct iwl_priv *priv, u16 ps_bit, u8 *addr);
+void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id);
+void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt);
#endif /* __iwl_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index b7e196e3c8d..58b132f9cf2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -97,7 +97,8 @@ int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
reg = iwl_read32(priv, CSR_UCODE_DRV_GP1);
if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
- IWL_DEBUG_INFO(priv, "Requesting wakeup, GP1 = 0x%x\n", reg);
+ IWL_DEBUG_INFO(priv, "Tx queue %d requesting wakeup, GP1 = 0x%x\n",
+ txq_id, reg);
iwl_set_bit(priv, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
return ret;
@@ -132,7 +133,7 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
struct iwl_tx_queue *txq = &priv->txq[txq_id];
struct iwl_queue *q = &txq->q;
struct pci_dev *dev = priv->pci_dev;
- int i, len;
+ int i;
if (q->n_bd == 0)
return;
@@ -142,8 +143,6 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd))
priv->cfg->ops->lib->txq_free_tfd(priv, txq);
- len = sizeof(struct iwl_device_cmd) * q->n_window;
-
/* De-alloc array of command/tx buffers */
for (i = 0; i < TFD_TX_CMD_SLOTS; i++)
kfree(txq->cmd[i]);
@@ -181,14 +180,11 @@ void iwl_cmd_queue_free(struct iwl_priv *priv)
struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM];
struct iwl_queue *q = &txq->q;
struct pci_dev *dev = priv->pci_dev;
- int i, len;
+ int i;
if (q->n_bd == 0)
return;
- len = sizeof(struct iwl_device_cmd) * q->n_window;
- len += IWL_MAX_SCAN_SIZE;
-
/* De-alloc array of command/tx buffers */
for (i = 0; i <= TFD_CMD_SLOTS; i++)
kfree(txq->cmd[i]);
@@ -370,8 +366,13 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
txq->need_update = 0;
- /* aggregation TX queues will get their ID when aggregation begins */
- if (txq_id <= IWL_TX_FIFO_AC3)
+ /*
+ * Aggregation TX queues will get their ID when aggregation begins;
+ * they overwrite the setting done here. The command FIFO doesn't
+ * need an swq_id so don't set one to catch errors, all others can
+ * be set up to the identity mapping.
+ */
+ if (txq_id != IWL_CMD_QUEUE_NUM)
txq->swq_id = txq_id;
/* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise
@@ -406,15 +407,19 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv)
int txq_id;
/* Tx queues */
- for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
- if (txq_id == IWL_CMD_QUEUE_NUM)
- iwl_cmd_queue_free(priv);
- else
- iwl_tx_queue_free(priv, txq_id);
-
+ if (priv->txq)
+ for (txq_id = 0; txq_id < priv->hw_params.max_txq_num;
+ txq_id++)
+ if (txq_id == IWL_CMD_QUEUE_NUM)
+ iwl_cmd_queue_free(priv);
+ else
+ iwl_tx_queue_free(priv, txq_id);
iwl_free_dma_ptr(priv, &priv->kw);
iwl_free_dma_ptr(priv, &priv->scd_bc_tbls);
+
+ /* free tx queue structure */
+ iwl_free_txq_mem(priv);
}
EXPORT_SYMBOL(iwl_hw_txq_ctx_free);
@@ -446,6 +451,12 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
IWL_ERR(priv, "Keep Warm allocation failed\n");
goto error_kw;
}
+
+ /* allocate tx queue structure */
+ ret = iwl_alloc_txq_mem(priv);
+ if (ret)
+ goto error;
+
spin_lock_irqsave(&priv->lock, flags);
/* Turn off all Tx DMA fifos */
@@ -582,9 +593,7 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv,
u8 rate_plcp;
/* Set retry limit on DATA packets and Probe Responses*/
- if (priv->data_retry_limit != -1)
- data_retry_limit = priv->data_retry_limit;
- else if (ieee80211_is_probe_resp(fc))
+ if (ieee80211_is_probe_resp(fc))
data_retry_limit = 3;
else
data_retry_limit = IWL_DEFAULT_TX_RETRY;
@@ -701,6 +710,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sta *sta = info->control.sta;
+ struct iwl_station_priv *sta_priv = NULL;
struct iwl_tx_queue *txq;
struct iwl_queue *q;
struct iwl_device_cmd *out_cmd;
@@ -710,7 +721,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
dma_addr_t phys_addr;
dma_addr_t txcmd_phys;
dma_addr_t scratch_phys;
- u16 len, len_org;
+ u16 len, len_org, firstlen, secondlen;
u16 seq_number = 0;
__le16 fc;
u8 hdr_len;
@@ -763,6 +774,24 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
IWL_DEBUG_TX(priv, "station Id %d\n", sta_id);
+ if (sta)
+ sta_priv = (void *)sta->drv_priv;
+
+ if (sta_priv && sta_id != priv->hw_params.bcast_sta_id &&
+ sta_priv->asleep) {
+ WARN_ON(!(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE));
+ /*
+ * This sends an asynchronous command to the device,
+ * but we can rely on it being processed before the
+ * next frame is processed -- and the next frame to
+ * this station is the one that will consume this
+ * counter.
+ * For now set the counter to just 1 since we do not
+ * support uAPSD yet.
+ */
+ iwl_sta_modify_sleep_tx_count(priv, sta_id, 1);
+ }
+
txq_id = skb_get_queue_mapping(skb);
if (ieee80211_is_data_qos(fc)) {
qc = ieee80211_get_qos_ctl(hdr);
@@ -843,7 +872,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
sizeof(struct iwl_cmd_header) + hdr_len;
len_org = len;
- len = (len + 3) & ~3;
+ firstlen = len = (len + 3) & ~3;
if (len_org != len)
len_org = 1;
@@ -877,7 +906,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
/* Set up TFD's 2nd entry to point directly to remainder of skb,
* if any (802.11 null frames have no payload). */
- len = skb->len - hdr_len;
+ secondlen = len = skb->len - hdr_len;
if (len) {
phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len,
len, PCI_DMA_TODEVICE);
@@ -911,11 +940,28 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys,
len, PCI_DMA_BIDIRECTIONAL);
+ trace_iwlwifi_dev_tx(priv,
+ &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr],
+ sizeof(struct iwl_tfd),
+ &out_cmd->hdr, firstlen,
+ skb->data + hdr_len, secondlen);
+
/* Tell device the write index *just past* this latest filled TFD */
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
ret = iwl_txq_update_write_ptr(priv, txq);
spin_unlock_irqrestore(&priv->lock, flags);
+ /*
+ * At this point the frame is "transmitted" successfully
+ * and we will get a TX status notification eventually,
+ * regardless of the value of ret. "ret" only indicates
+ * whether or not we should update the write pointer.
+ */
+
+ /* avoid atomic ops if it isn't an associated client */
+ if (sta_priv && sta_priv->client)
+ atomic_inc(&sta_priv->pending_frames);
+
if (ret)
return ret;
@@ -970,13 +1016,20 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) &&
!(cmd->flags & CMD_SIZE_HUGE));
- if (iwl_is_rfkill(priv)) {
- IWL_DEBUG_INFO(priv, "Not sending command - RF KILL\n");
+ if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) {
+ IWL_WARN(priv, "Not sending command - %s KILL\n",
+ iwl_is_rfkill(priv) ? "RF" : "CT");
return -EIO;
}
if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) {
- IWL_ERR(priv, "No space for Tx\n");
+ IWL_ERR(priv, "No space in command queue\n");
+ if (iwl_within_ct_kill_margin(priv))
+ iwl_tt_enter_ct_kill(priv);
+ else {
+ IWL_ERR(priv, "Restarting adapter due to queue full\n");
+ queue_work(priv->workqueue, &priv->restart);
+ }
return -ENOSPC;
}
@@ -1039,6 +1092,8 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
pci_unmap_addr_set(out_meta, mapping, phys_addr);
pci_unmap_len_set(out_meta, len, fix_size);
+ trace_iwlwifi_dev_hcmd(priv, &out_cmd->hdr, fix_size, cmd->flags);
+
priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq,
phys_addr, fix_size, 1,
U32_PAD(cmd->len));
@@ -1051,6 +1106,24 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
return ret ? ret : idx;
}
+static void iwl_tx_status(struct iwl_priv *priv, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_sta *sta;
+ struct iwl_station_priv *sta_priv;
+
+ sta = ieee80211_find_sta(priv->vif, hdr->addr1);
+ if (sta) {
+ sta_priv = (void *)sta->drv_priv;
+ /* avoid atomic ops if this isn't a client */
+ if (sta_priv->client &&
+ atomic_dec_return(&sta_priv->pending_frames) == 0)
+ ieee80211_sta_block_awake(priv->hw, sta, false);
+ }
+
+ ieee80211_tx_status_irqsafe(priv->hw, skb);
+}
+
int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
{
struct iwl_tx_queue *txq = &priv->txq[txq_id];
@@ -1070,7 +1143,7 @@ int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index)
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
tx_info = &txq->txb[txq->q.read_ptr];
- ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb[0]);
+ iwl_tx_status(priv, tx_info->skb[0]);
tx_info->skb[0] = NULL;
if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl)
@@ -1105,11 +1178,6 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id,
return;
}
- pci_unmap_single(priv->pci_dev,
- pci_unmap_addr(&txq->meta[cmd_idx], mapping),
- pci_unmap_len(&txq->meta[cmd_idx], len),
- PCI_DMA_BIDIRECTIONAL);
-
for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx;
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
@@ -1132,7 +1200,7 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id,
*/
void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
@@ -1157,12 +1225,17 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index];
meta = &priv->txq[IWL_CMD_QUEUE_NUM].meta[cmd_index];
+ pci_unmap_single(priv->pci_dev,
+ pci_unmap_addr(meta, mapping),
+ pci_unmap_len(meta, len),
+ PCI_DMA_BIDIRECTIONAL);
+
/* Input error checking is done when commands are added to queue. */
if (meta->flags & CMD_WANT_SKB) {
- meta->source->reply_skb = rxb->skb;
- rxb->skb = NULL;
+ meta->source->reply_page = (unsigned long)rxb_addr(rxb);
+ rxb->page = NULL;
} else if (meta->callback)
- meta->callback(priv, cmd, rxb->skb);
+ meta->callback(priv, cmd, pkt);
iwl_hcmd_queue_reclaim(priv, txq_id, index, cmd_index);
@@ -1240,7 +1313,7 @@ int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn)
if (tid_data->tfds_in_queue == 0) {
IWL_DEBUG_HT(priv, "HW queue is empty\n");
tid_data->agg.state = IWL_AGG_ON;
- ieee80211_start_tx_ba_cb_irqsafe(priv->hw, ra, tid);
+ ieee80211_start_tx_ba_cb_irqsafe(priv->vif, ra, tid);
} else {
IWL_DEBUG_HT(priv, "HW queue is NOT empty: %d packets in HW queue\n",
tid_data->tfds_in_queue);
@@ -1313,7 +1386,7 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid)
if (ret)
return ret;
- ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, ra, tid);
+ ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, ra, tid);
return 0;
}
@@ -1337,7 +1410,7 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id)
priv->cfg->ops->lib->txq_agg_disable(priv, txq_id,
ssn, tx_fifo);
tid_data->agg.state = IWL_AGG_OFF;
- ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, addr, tid);
+ ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, addr, tid);
}
break;
case IWL_EMPTYING_HW_QUEUE_ADDBA:
@@ -1345,7 +1418,7 @@ int iwl_txq_check_empty(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id)
if (tid_data->tfds_in_queue == 0) {
IWL_DEBUG_HT(priv, "HW queue empty: continue ADDBA flow\n");
tid_data->agg.state = IWL_AGG_ON;
- ieee80211_start_tx_ba_cb_irqsafe(priv->hw, addr, tid);
+ ieee80211_start_tx_ba_cb_irqsafe(priv->vif, addr, tid);
}
break;
}
@@ -1409,7 +1482,7 @@ static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv,
info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb[0]);
memset(&info->status, 0, sizeof(info->status));
- info->flags = IEEE80211_TX_STAT_ACK;
+ info->flags |= IEEE80211_TX_STAT_ACK;
info->flags |= IEEE80211_TX_STAT_AMPDU;
info->status.ampdu_ack_map = successes;
info->status.ampdu_ack_len = agg->frame_count;
@@ -1429,7 +1502,7 @@ static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv,
void iwl_rx_reply_compressed_ba(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba;
struct iwl_tx_queue *txq = NULL;
struct iwl_ht_agg *agg;
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index d00a8033409..2a28a1f8b1f 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -42,7 +42,6 @@
#include <linux/if_arp.h>
#include <net/ieee80211_radiotap.h>
-#include <net/lib80211.h>
#include <net/mac80211.h>
#include <asm/div64.h>
@@ -77,11 +76,9 @@
#define VS
#endif
-#define IWL39_VERSION "1.2.26k" VD VS
+#define DRV_VERSION IWLWIFI_VERSION VD VS
#define DRV_COPYRIGHT "Copyright(c) 2003-2009 Intel Corporation"
#define DRV_AUTHOR "<ilw@linux.intel.com>"
-#define DRV_VERSION IWL39_VERSION
-
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_VERSION(DRV_VERSION);
@@ -90,7 +87,6 @@ MODULE_LICENSE("GPL");
/* module parameters */
struct iwl_mod_params iwl3945_mod_params = {
- .num_of_queues = IWL39_NUM_QUEUES, /* Not used */
.sw_crypto = 1,
.restart_fw = 1,
/* the rest are 0 by default */
@@ -368,13 +364,13 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
struct sk_buff *skb_frag,
int sta_id)
{
- struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
+ struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
struct iwl_hw_key *keyinfo = &priv->stations[sta_id].keyinfo;
switch (keyinfo->alg) {
case ALG_CCMP:
- tx->sec_ctl = TX_CMD_SEC_CCM;
- memcpy(tx->key, keyinfo->key, keyinfo->keylen);
+ tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
+ memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen);
IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n");
break;
@@ -382,13 +378,13 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
break;
case ALG_WEP:
- tx->sec_ctl = TX_CMD_SEC_WEP |
+ tx_cmd->sec_ctl = TX_CMD_SEC_WEP |
(info->control.hw_key->hw_key_idx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT;
if (keyinfo->keylen == 13)
- tx->sec_ctl |= TX_CMD_SEC_KEY128;
+ tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
- memcpy(&tx->key[3], keyinfo->key, keyinfo->keylen);
+ memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen);
IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption "
"with key %d\n", info->control.hw_key->hw_key_idx);
@@ -408,12 +404,11 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv,
struct ieee80211_tx_info *info,
struct ieee80211_hdr *hdr, u8 std_id)
{
- struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
- __le32 tx_flags = tx->tx_flags;
+ struct iwl3945_tx_cmd *tx_cmd = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
+ __le32 tx_flags = tx_cmd->tx_flags;
__le16 fc = hdr->frame_control;
- u8 rc_flags = info->control.rates[0].flags;
- tx->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
+ tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
tx_flags |= TX_CMD_FLG_ACK_MSK;
if (ieee80211_is_mgmt(fc))
@@ -426,25 +421,19 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv,
tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
}
- tx->sta_id = std_id;
+ tx_cmd->sta_id = std_id;
if (ieee80211_has_morefrags(fc))
tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK;
if (ieee80211_is_data_qos(fc)) {
u8 *qc = ieee80211_get_qos_ctl(hdr);
- tx->tid_tspec = qc[0] & 0xf;
+ tx_cmd->tid_tspec = qc[0] & 0xf;
tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK;
} else {
tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK;
}
- if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
- tx_flags |= TX_CMD_FLG_RTS_MSK;
- tx_flags &= ~TX_CMD_FLG_CTS_MSK;
- } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
- tx_flags &= ~TX_CMD_FLG_RTS_MSK;
- tx_flags |= TX_CMD_FLG_CTS_MSK;
- }
+ priv->cfg->ops->utils->rts_tx_cmd_flag(info, &tx_flags);
if ((tx_flags & TX_CMD_FLG_RTS_MSK) || (tx_flags & TX_CMD_FLG_CTS_MSK))
tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK;
@@ -452,19 +441,16 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv,
tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK);
if (ieee80211_is_mgmt(fc)) {
if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
- tx->timeout.pm_frame_timeout = cpu_to_le16(3);
+ tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3);
else
- tx->timeout.pm_frame_timeout = cpu_to_le16(2);
+ tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2);
} else {
- tx->timeout.pm_frame_timeout = 0;
-#ifdef CONFIG_IWLWIFI_LEDS
- priv->rxtxpackets += le16_to_cpu(cmd->cmd.tx.len);
-#endif
+ tx_cmd->timeout.pm_frame_timeout = 0;
}
- tx->driver_txop = 0;
- tx->tx_flags = tx_flags;
- tx->next_frame_len = 0;
+ tx_cmd->driver_txop = 0;
+ tx_cmd->tx_flags = tx_flags;
+ tx_cmd->next_frame_len = 0;
}
/*
@@ -474,7 +460,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct iwl3945_tx_cmd *tx;
+ struct iwl3945_tx_cmd *tx_cmd;
struct iwl_tx_queue *txq = NULL;
struct iwl_queue *q = NULL;
struct iwl_device_cmd *out_cmd;
@@ -573,9 +559,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
/* Init first empty entry in queue's array of Tx/cmd buffers */
out_cmd = txq->cmd[idx];
out_meta = &txq->meta[idx];
- tx = (struct iwl3945_tx_cmd *)out_cmd->cmd.payload;
+ tx_cmd = (struct iwl3945_tx_cmd *)out_cmd->cmd.payload;
memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr));
- memset(tx, 0, sizeof(*tx));
+ memset(tx_cmd, 0, sizeof(*tx_cmd));
/*
* Set up the Tx-command (not MAC!) header.
@@ -588,7 +574,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
INDEX_TO_SEQ(q->write_ptr)));
/* Copy MAC header from skb into command buffer */
- memcpy(tx->hdr, hdr, hdr_len);
+ memcpy(tx_cmd->hdr, hdr, hdr_len);
if (info->control.hw_key)
@@ -602,12 +588,12 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
/* Total # bytes to be transmitted */
len = (u16)skb->len;
- tx->len = cpu_to_le16(len);
+ tx_cmd->len = cpu_to_le16(len);
iwl_dbg_log_tx_data_frame(priv, len, hdr);
iwl_update_stats(priv, true, fc, len);
- tx->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK;
- tx->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK;
+ tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK;
+ tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK;
if (!ieee80211_has_morefrags(hdr->frame_control)) {
txq->need_update = 1;
@@ -620,9 +606,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
IWL_DEBUG_TX(priv, "sequence nr = 0X%x \n",
le16_to_cpu(out_cmd->hdr.sequence));
- IWL_DEBUG_TX(priv, "tx_flags = 0X%x \n", le32_to_cpu(tx->tx_flags));
- iwl_print_hex_dump(priv, IWL_DL_TX, tx, sizeof(*tx));
- iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx->hdr,
+ IWL_DEBUG_TX(priv, "tx_flags = 0X%x \n", le32_to_cpu(tx_cmd->tx_flags));
+ iwl_print_hex_dump(priv, IWL_DL_TX, tx_cmd, sizeof(*tx_cmd));
+ iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr,
ieee80211_hdrlen(fc));
/*
@@ -758,7 +744,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv,
u8 type)
{
struct iwl_spectrum_cmd spectrum;
- struct iwl_rx_packet *res;
+ struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = REPLY_SPECTRUM_MEASUREMENT_CMD,
.data = (void *)&spectrum,
@@ -803,18 +789,18 @@ static int iwl3945_get_measurement(struct iwl_priv *priv,
if (rc)
return rc;
- res = (struct iwl_rx_packet *)cmd.reply_skb->data;
- if (res->hdr.flags & IWL_CMD_FAILED_MSK) {
+ pkt = (struct iwl_rx_packet *)cmd.reply_page;
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from REPLY_RX_ON_ASSOC command\n");
rc = -EIO;
}
- spectrum_resp_status = le16_to_cpu(res->u.spectrum.status);
+ spectrum_resp_status = le16_to_cpu(pkt->u.spectrum.status);
switch (spectrum_resp_status) {
case 0: /* Command will be handled */
- if (res->u.spectrum.id != 0xff) {
+ if (pkt->u.spectrum.id != 0xff) {
IWL_DEBUG_INFO(priv, "Replaced existing measurement: %d\n",
- res->u.spectrum.id);
+ pkt->u.spectrum.id);
priv->measurement_status &= ~MEASUREMENT_READY;
}
priv->measurement_status |= MEASUREMENT_ACTIVE;
@@ -826,7 +812,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv,
break;
}
- dev_kfree_skb_any(cmd.reply_skb);
+ free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
return rc;
}
@@ -835,7 +821,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv,
static void iwl3945_rx_reply_alive(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_alive_resp *palive;
struct delayed_work *pwork;
@@ -872,7 +858,7 @@ static void iwl3945_rx_reply_add_sta(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
#endif
IWL_DEBUG_RX(priv, "Received REPLY_ADD_STA: 0x%02X\n", pkt->u.status);
@@ -908,7 +894,7 @@ static void iwl3945_rx_beacon_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
#ifdef CONFIG_IWLWIFI_DEBUG
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl3945_beacon_notif *beacon = &(pkt->u.beacon_status);
u8 rate = beacon->beacon_notify_hdr.rate;
@@ -931,7 +917,7 @@ static void iwl3945_rx_beacon_notif(struct iwl_priv *priv,
static void iwl3945_rx_card_state_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb)
{
- struct iwl_rx_packet *pkt = (void *)rxb->skb->data;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
unsigned long status = priv->status;
@@ -1095,7 +1081,7 @@ static int iwl3945_rx_queue_restock(struct iwl_priv *priv)
list_del(element);
/* Point to Rx buffer via next RBD in circular buffer */
- rxq->bd[rxq->write] = iwl3945_dma_addr2rbd_ptr(priv, rxb->real_dma_addr);
+ rxq->bd[rxq->write] = iwl3945_dma_addr2rbd_ptr(priv, rxb->page_dma);
rxq->queue[rxq->write] = rxb;
rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
rxq->free_count--;
@@ -1135,8 +1121,9 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
struct iwl_rx_queue *rxq = &priv->rxq;
struct list_head *element;
struct iwl_rx_mem_buffer *rxb;
- struct sk_buff *skb;
+ struct page *page;
unsigned long flags;
+ gfp_t gfp_mask = priority;
while (1) {
spin_lock_irqsave(&rxq->lock, flags);
@@ -1148,10 +1135,14 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
spin_unlock_irqrestore(&rxq->lock, flags);
if (rxq->free_count > RX_LOW_WATERMARK)
- priority |= __GFP_NOWARN;
+ gfp_mask |= __GFP_NOWARN;
+
+ if (priv->hw_params.rx_page_order > 0)
+ gfp_mask |= __GFP_COMP;
+
/* Alloc a new receive buffer */
- skb = alloc_skb(priv->hw_params.rx_buf_size, priority);
- if (!skb) {
+ page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order);
+ if (!page) {
if (net_ratelimit())
IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n");
if ((rxq->free_count <= RX_LOW_WATERMARK) &&
@@ -1168,7 +1159,7 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
spin_lock_irqsave(&rxq->lock, flags);
if (list_empty(&rxq->rx_used)) {
spin_unlock_irqrestore(&rxq->lock, flags);
- dev_kfree_skb_any(skb);
+ __free_pages(page, priv->hw_params.rx_page_order);
return;
}
element = rxq->rx_used.next;
@@ -1176,26 +1167,18 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
list_del(element);
spin_unlock_irqrestore(&rxq->lock, flags);
- rxb->skb = skb;
-
- /* If radiotap head is required, reserve some headroom here.
- * The physical head count is a variable rx_stats->phy_count.
- * We reserve 4 bytes here. Plus these extra bytes, the
- * headroom of the physical head should be enough for the
- * radiotap head that iwl3945 supported. See iwl3945_rt.
- */
- skb_reserve(rxb->skb, 4);
-
+ rxb->page = page;
/* Get physical address of RB/SKB */
- rxb->real_dma_addr = pci_map_single(priv->pci_dev,
- rxb->skb->data,
- priv->hw_params.rx_buf_size,
- PCI_DMA_FROMDEVICE);
+ rxb->page_dma = pci_map_page(priv->pci_dev, page, 0,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
spin_lock_irqsave(&rxq->lock, flags);
+
list_add_tail(&rxb->list, &rxq->rx_free);
- priv->alloc_rxb_skb++;
rxq->free_count++;
+ priv->alloc_rxb_page++;
+
spin_unlock_irqrestore(&rxq->lock, flags);
}
}
@@ -1211,14 +1194,14 @@ void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) {
/* In the reset function, these buffers may have been allocated
* to an SKB, so we need to unmap and free potential storage */
- if (rxq->pool[i].skb != NULL) {
- pci_unmap_single(priv->pci_dev,
- rxq->pool[i].real_dma_addr,
- priv->hw_params.rx_buf_size,
- PCI_DMA_FROMDEVICE);
- priv->alloc_rxb_skb--;
- dev_kfree_skb(rxq->pool[i].skb);
- rxq->pool[i].skb = NULL;
+ if (rxq->pool[i].page != NULL) {
+ pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ priv->alloc_rxb_page--;
+ __free_pages(rxq->pool[i].page,
+ priv->hw_params.rx_page_order);
+ rxq->pool[i].page = NULL;
}
list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
}
@@ -1226,8 +1209,8 @@ void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
/* Set us so that we have processed and used all buffers, but have
* not restocked the Rx queue with fresh buffers */
rxq->read = rxq->write = 0;
- rxq->free_count = 0;
rxq->write_actual = 0;
+ rxq->free_count = 0;
spin_unlock_irqrestore(&rxq->lock, flags);
}
@@ -1260,12 +1243,14 @@ static void iwl3945_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rx
{
int i;
for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
- if (rxq->pool[i].skb != NULL) {
- pci_unmap_single(priv->pci_dev,
- rxq->pool[i].real_dma_addr,
- priv->hw_params.rx_buf_size,
- PCI_DMA_FROMDEVICE);
- dev_kfree_skb(rxq->pool[i].skb);
+ if (rxq->pool[i].page != NULL) {
+ pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ __free_pages(rxq->pool[i].page,
+ priv->hw_params.rx_page_order);
+ rxq->pool[i].page = NULL;
+ priv->alloc_rxb_page--;
}
}
@@ -1381,7 +1366,7 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
i = rxq->read;
/* calculate total frames need to be restock after handling RX */
- total_empty = r - priv->rxq.write_actual;
+ total_empty = r - rxq->write_actual;
if (total_empty < 0)
total_empty += RX_QUEUE_SIZE;
@@ -1401,10 +1386,13 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
rxq->queue[i] = NULL;
- pci_unmap_single(priv->pci_dev, rxb->real_dma_addr,
- priv->hw_params.rx_buf_size,
- PCI_DMA_FROMDEVICE);
- pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ pci_unmap_page(priv->pci_dev, rxb->page_dma,
+ PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ pkt = rxb_addr(rxb);
+
+ trace_iwlwifi_dev_rx(priv, pkt,
+ le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
/* Reclaim a command buffer only if this packet is a response
* to a (driver-originated) command.
@@ -1422,44 +1410,55 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
if (priv->rx_handlers[pkt->hdr.cmd]) {
IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, i,
get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
+ priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
} else {
/* No handling needed */
- IWL_DEBUG_RX(priv, "r %d i %d No handler needed for %s, 0x%02x\n",
+ IWL_DEBUG_RX(priv,
+ "r %d i %d No handler needed for %s, 0x%02x\n",
r, i, get_cmd_string(pkt->hdr.cmd),
pkt->hdr.cmd);
}
+ /*
+ * XXX: After here, we should always check rxb->page
+ * against NULL before touching it or its virtual
+ * memory (pkt). Because some rx_handler might have
+ * already taken or freed the pages.
+ */
+
if (reclaim) {
- /* Invoke any callbacks, transfer the skb to caller, and
- * fire off the (possibly) blocking iwl_send_cmd()
+ /* Invoke any callbacks, transfer the buffer to caller,
+ * and fire off the (possibly) blocking iwl_send_cmd()
* as we reclaim the driver command queue */
- if (rxb && rxb->skb)
+ if (rxb->page)
iwl_tx_cmd_complete(priv, rxb);
else
IWL_WARN(priv, "Claim null rxb?\n");
}
- /* For now we just don't re-use anything. We can tweak this
- * later to try and re-use notification packets and SKBs that
- * fail to Rx correctly */
- if (rxb->skb != NULL) {
- priv->alloc_rxb_skb--;
- dev_kfree_skb_any(rxb->skb);
- rxb->skb = NULL;
- }
-
+ /* Reuse the page if possible. For notification packets and
+ * SKBs that fail to Rx correctly, add them back into the
+ * rx_free list for reuse later. */
spin_lock_irqsave(&rxq->lock, flags);
- list_add_tail(&rxb->list, &priv->rxq.rx_used);
+ if (rxb->page != NULL) {
+ rxb->page_dma = pci_map_page(priv->pci_dev, rxb->page,
+ 0, PAGE_SIZE << priv->hw_params.rx_page_order,
+ PCI_DMA_FROMDEVICE);
+ list_add_tail(&rxb->list, &rxq->rx_free);
+ rxq->free_count++;
+ } else
+ list_add_tail(&rxb->list, &rxq->rx_used);
+
spin_unlock_irqrestore(&rxq->lock, flags);
+
i = (i + 1) & RX_QUEUE_MASK;
/* If there are a lot of unused frames,
* restock the Rx queue so ucode won't assert. */
if (fill_rx) {
count++;
if (count >= 8) {
- priv->rxq.read = i;
+ rxq->read = i;
iwl3945_rx_replenish_now(priv);
count = 0;
}
@@ -1467,7 +1466,7 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
}
/* Backtrack one entry */
- priv->rxq.read = i;
+ rxq->read = i;
if (fill_rx)
iwl3945_rx_replenish_now(priv);
else
@@ -1482,7 +1481,6 @@ static inline void iwl_synchronize_irq(struct iwl_priv *priv)
tasklet_kill(&priv->irq_tasklet);
}
-#ifdef CONFIG_IWLWIFI_DEBUG
static const char *desc_lookup(int i)
{
switch (i) {
@@ -1551,8 +1549,9 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
"%-13s (#%d) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n",
desc_lookup(desc), desc, time, blink1, blink2,
ilink1, ilink2, data1);
+ trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, 0,
+ 0, blink1, blink2, ilink1, ilink2);
}
-
}
#define EVENT_START_OFFSET (6 * sizeof(u32))
@@ -1569,6 +1568,7 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */
u32 ptr; /* SRAM byte address of log data */
u32 ev, time, data; /* event log data */
+ unsigned long reg_flags;
if (num_events == 0)
return;
@@ -1582,25 +1582,71 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
+ /* Make sure device is powered up for SRAM reads */
+ spin_lock_irqsave(&priv->reg_lock, reg_flags);
+ iwl_grab_nic_access(priv);
+
+ /* Set starting address; reads will auto-increment */
+ _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
+ rmb();
+
/* "time" is actually "data" for mode 0 (no timestamp).
* place event id # at far right for easier visual parsing. */
for (i = 0; i < num_events; i++) {
- ev = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
- time = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
+ ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+ time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
if (mode == 0) {
/* data, ev */
IWL_ERR(priv, "0x%08x\t%04u\n", time, ev);
+ trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
} else {
- data = iwl_read_targ_mem(priv, ptr);
- ptr += sizeof(u32);
+ data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", time, data, ev);
+ trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
}
}
+
+ /* Allow device to power down */
+ iwl_release_nic_access(priv);
+ spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
}
-void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
+/**
+ * iwl3945_print_last_event_logs - Dump the newest # of event log to syslog
+ */
+static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+ u32 num_wraps, u32 next_entry,
+ u32 size, u32 mode)
+{
+ /*
+ * display the newest DEFAULT_LOG_ENTRIES entries
+ * i.e the entries just before the next ont that uCode would fill.
+ */
+ if (num_wraps) {
+ if (next_entry < size) {
+ iwl3945_print_event_log(priv,
+ capacity - (size - next_entry),
+ size - next_entry, mode);
+ iwl3945_print_event_log(priv, 0,
+ next_entry, mode);
+ } else
+ iwl3945_print_event_log(priv, next_entry - size,
+ size, mode);
+ } else {
+ if (next_entry < size)
+ iwl3945_print_event_log(priv, 0, next_entry, mode);
+ else
+ iwl3945_print_event_log(priv, next_entry - size,
+ size, mode);
+ }
+}
+
+/* For sanity check only. Actual size is determined by uCode, typ. 512 */
+#define IWL3945_MAX_EVENT_LOG_SIZE (512)
+
+#define DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES (20)
+
+void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
{
u32 base; /* SRAM byte address of event log header */
u32 capacity; /* event log capacity in # entries */
@@ -1621,6 +1667,18 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
+ if (capacity > IWL3945_MAX_EVENT_LOG_SIZE) {
+ IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n",
+ capacity, IWL3945_MAX_EVENT_LOG_SIZE);
+ capacity = IWL3945_MAX_EVENT_LOG_SIZE;
+ }
+
+ if (next_entry > IWL3945_MAX_EVENT_LOG_SIZE) {
+ IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n",
+ next_entry, IWL3945_MAX_EVENT_LOG_SIZE);
+ next_entry = IWL3945_MAX_EVENT_LOG_SIZE;
+ }
+
size = num_wraps ? capacity : next_entry;
/* bail out if nothing in log */
@@ -1629,30 +1687,40 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
return;
}
- IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n",
- size, num_wraps);
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS))
+ size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size;
+#else
+ size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES)
+ ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size;
+#endif
+
+ IWL_ERR(priv, "Start IWL Event Log Dump: display last %d count\n",
+ size);
- /* if uCode has wrapped back to top of log, start at the oldest entry,
- * i.e the next one that uCode would fill. */
- if (num_wraps)
- iwl3945_print_event_log(priv, next_entry,
+#ifdef CONFIG_IWLWIFI_DEBUG
+ if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
+ /* if uCode has wrapped back to top of log,
+ * start at the oldest entry,
+ * i.e the next one that uCode would fill.
+ */
+ if (num_wraps)
+ iwl3945_print_event_log(priv, next_entry,
capacity - next_entry, mode);
- /* (then/else) start at top of log */
- iwl3945_print_event_log(priv, 0, next_entry, mode);
-
-}
+ /* (then/else) start at top of log */
+ iwl3945_print_event_log(priv, 0, next_entry, mode);
+ } else
+ iwl3945_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode);
#else
-void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
-{
-}
+ iwl3945_print_last_event_logs(priv, capacity, num_wraps,
+ next_entry, size, mode);
+#endif
-void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
-{
}
-#endif
-
static void iwl3945_irq_tasklet(struct iwl_priv *priv)
{
u32 inta, handled = 0;
@@ -1685,6 +1753,8 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv)
}
#endif
+ spin_unlock_irqrestore(&priv->lock, flags);
+
/* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not
* atomic, make sure that inta covers all the interrupts that
* we've discovered, even if FH interrupt came in just after
@@ -1706,8 +1776,6 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv)
handled |= CSR_INT_BIT_HW_ERR;
- spin_unlock_irqrestore(&priv->lock, flags);
-
return;
}
@@ -1799,7 +1867,6 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv)
"flags 0x%08lx\n", inta, inta_mask, inta_fh, flags);
}
#endif
- spin_unlock_irqrestore(&priv->lock, flags);
}
static int iwl3945_get_channels_for_scan(struct iwl_priv *priv,
@@ -2158,6 +2225,14 @@ static int iwl3945_read_ucode(struct iwl_priv *priv)
IWL_UCODE_API(priv->ucode_ver),
IWL_UCODE_SERIAL(priv->ucode_ver));
+ snprintf(priv->hw->wiphy->fw_version,
+ sizeof(priv->hw->wiphy->fw_version),
+ "%u.%u.%u.%u",
+ IWL_UCODE_MAJOR(priv->ucode_ver),
+ IWL_UCODE_MINOR(priv->ucode_ver),
+ IWL_UCODE_API(priv->ucode_ver),
+ IWL_UCODE_SERIAL(priv->ucode_ver));
+
IWL_DEBUG_INFO(priv, "f/w package hdr ucode version raw = 0x%x\n",
priv->ucode_ver);
IWL_DEBUG_INFO(priv, "f/w package hdr runtime inst size = %u\n",
@@ -2458,7 +2533,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
priv->active_rate = priv->rates_mask;
priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
- iwl_power_update_mode(priv, false);
+ iwl_power_update_mode(priv, true);
if (iwl_is_associated(priv)) {
struct iwl3945_rxon_cmd *active_rxon =
@@ -2479,7 +2554,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
iwl3945_reg_txpower_periodic(priv);
- iwl3945_led_register(priv);
+ iwl_leds_init(priv);
IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n");
set_bit(STATUS_READY, &priv->status);
@@ -2517,7 +2592,6 @@ static void __iwl3945_down(struct iwl_priv *priv)
if (!exit_pending)
set_bit(STATUS_EXIT_PENDING, &priv->status);
- iwl3945_led_unregister(priv);
iwl_clear_stations_table(priv);
/* Unblock any waiting calls */
@@ -2563,23 +2637,15 @@ static void __iwl3945_down(struct iwl_priv *priv)
test_bit(STATUS_EXIT_PENDING, &priv->status) <<
STATUS_EXIT_PENDING;
- priv->cfg->ops->lib->apm_ops.reset(priv);
- spin_lock_irqsave(&priv->lock, flags);
- iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- spin_unlock_irqrestore(&priv->lock, flags);
-
iwl3945_hw_txq_ctx_stop(priv);
iwl3945_hw_rxq_stop(priv);
- iwl_write_prph(priv, APMG_CLK_DIS_REG,
- APMG_CLK_VAL_DMA_CLK_RQT);
-
+ /* Power-down device's busmaster DMA clocks */
+ iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT);
udelay(5);
- if (exit_pending)
- priv->cfg->ops->lib->apm_ops.stop(priv);
- else
- priv->cfg->ops->lib->apm_ops.reset(priv);
+ /* Stop the device, and put it in low power state */
+ priv->cfg->ops->lib->apm_ops.stop(priv);
exit:
memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp));
@@ -2724,19 +2790,34 @@ static void iwl3945_bg_alive_start(struct work_struct *data)
mutex_unlock(&priv->mutex);
}
+/*
+ * 3945 cannot interrupt driver when hardware rf kill switch toggles;
+ * driver must poll CSR_GP_CNTRL_REG register for change. This register
+ * *is* readable even when device has been SW_RESET into low power mode
+ * (e.g. during RF KILL).
+ */
static void iwl3945_rfkill_poll(struct work_struct *data)
{
struct iwl_priv *priv =
container_of(data, struct iwl_priv, rfkill_poll.work);
+ bool old_rfkill = test_bit(STATUS_RF_KILL_HW, &priv->status);
+ bool new_rfkill = !(iwl_read32(priv, CSR_GP_CNTRL)
+ & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
- if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
- clear_bit(STATUS_RF_KILL_HW, &priv->status);
- else
- set_bit(STATUS_RF_KILL_HW, &priv->status);
+ if (new_rfkill != old_rfkill) {
+ if (new_rfkill)
+ set_bit(STATUS_RF_KILL_HW, &priv->status);
+ else
+ clear_bit(STATUS_RF_KILL_HW, &priv->status);
+
+ wiphy_rfkill_set_hw_state(priv->hw->wiphy, new_rfkill);
- wiphy_rfkill_set_hw_state(priv->hw->wiphy,
- test_bit(STATUS_RF_KILL_HW, &priv->status));
+ IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n",
+ new_rfkill ? "disable radio" : "enable radio");
+ }
+ /* Keep this running, even if radio now enabled. This will be
+ * cancelled in mac_start() if system decides to start again */
queue_delayed_work(priv->workqueue, &priv->rfkill_poll,
round_jiffies_relative(2 * HZ));
@@ -3152,6 +3233,8 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw)
* no need to poll the killswitch state anymore */
cancel_delayed_work(&priv->rfkill_poll);
+ iwl_led_start(priv);
+
priv->is_open = 1;
IWL_DEBUG_MAC80211(priv, "leave\n");
return 0;
@@ -3606,7 +3689,7 @@ static ssize_t show_statistics(struct device *d,
return -EAGAIN;
mutex_lock(&priv->mutex);
- rc = iwl_send_statistics_request(priv, 0);
+ rc = iwl_send_statistics_request(priv, CMD_SYNC, false);
mutex_unlock(&priv->mutex);
if (rc) {
@@ -3795,7 +3878,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv)
/* Clear the driver's (not device's) station table */
iwl_clear_stations_table(priv);
- priv->data_retry_limit = -1;
priv->ieee_channels = NULL;
priv->ieee_rates = NULL;
priv->band = IEEE80211_BAND_2GHZ;
@@ -3862,10 +3944,8 @@ static int iwl3945_setup_mac(struct iwl_priv *priv)
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
- hw->wiphy->custom_regulatory = true;
-
- /* Firmware does not support this */
- hw->wiphy->disable_beacon_hints = true;
+ hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY |
+ WIPHY_FLAG_DISABLE_BEACON_HINTS;
hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945;
/* we create the 802.11 header and a zero-length SSID element */
@@ -3982,13 +4062,6 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
*/
spin_lock_init(&priv->reg_lock);
- /* amp init */
- err = priv->cfg->ops->lib->apm_ops.init(priv);
- if (err < 0) {
- IWL_DEBUG_INFO(priv, "Failed to init the card\n");
- goto out_iounmap;
- }
-
/***********************
* 4. Read EEPROM
* ********************/
@@ -4054,6 +4127,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
&priv->bands[IEEE80211_BAND_2GHZ].channels[5]);
iwl3945_setup_deferred_work(priv);
iwl3945_setup_rx_handlers(priv);
+ iwl_power_initialize(priv);
/*********************************
* 8. Setup and Register mac80211
@@ -4124,6 +4198,15 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev)
iwl3945_down(priv);
}
+ /*
+ * Make sure device is reset to low power before unloading driver.
+ * This may be redundant with iwl_down(), but there are paths to
+ * run iwl_down() without calling apm_ops.stop(), and there are
+ * paths to avoid running iwl_down() at all before leaving driver.
+ * This (inexpensive) call *makes sure* device is reset.
+ */
+ priv->cfg->ops->lib->apm_ops.stop(priv);
+
/* make sure we flush any pending irq or
* tasklet for the driver
*/
@@ -4226,18 +4309,19 @@ static void __exit iwl3945_exit(void)
MODULE_FIRMWARE(IWL3945_MODULE_FIRMWARE(IWL3945_UCODE_API_MAX));
-module_param_named(antenna, iwl3945_mod_params.antenna, int, 0444);
+module_param_named(antenna, iwl3945_mod_params.antenna, int, S_IRUGO);
MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])");
-module_param_named(swcrypto, iwl3945_mod_params.sw_crypto, int, 0444);
+module_param_named(swcrypto, iwl3945_mod_params.sw_crypto, int, S_IRUGO);
MODULE_PARM_DESC(swcrypto,
"using software crypto (default 1 [software])\n");
#ifdef CONFIG_IWLWIFI_DEBUG
-module_param_named(debug, iwl_debug_level, uint, 0644);
+module_param_named(debug, iwl_debug_level, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "debug output mask");
#endif
-module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, 0444);
+module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan,
+ int, S_IRUGO);
MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)");
-module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, 0444);
+module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO);
MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error");
module_exit(iwl3945_exit);
diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig
index c25a04371ca..b9d34a76696 100644
--- a/drivers/net/wireless/iwmc3200wifi/Kconfig
+++ b/drivers/net/wireless/iwmc3200wifi/Kconfig
@@ -1,8 +1,9 @@
config IWM
tristate "Intel Wireless Multicomm 3200 WiFi driver"
- depends on MMC && WLAN_80211 && EXPERIMENTAL
+ depends on MMC && EXPERIMENTAL
depends on CFG80211
select FW_LOADER
+ select IWMC3200TOP
help
The Intel Wireless Multicomm 3200 hardware is a combo
card with GPS, Bluetooth, WiMax and 802.11 radios. It
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
index f3c55658225..7c4f44a9c3e 100644
--- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c
+++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c
@@ -405,39 +405,21 @@ static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
struct ieee80211_channel *chan = params->channel;
- struct cfg80211_bss *bss;
if (!test_bit(IWM_STATUS_READY, &iwm->status))
return -EIO;
- /* UMAC doesn't support creating IBSS network with specified bssid.
- * This should be removed after we have join only mode supported. */
+ /* UMAC doesn't support creating or joining an IBSS network
+ * with specified bssid. */
if (params->bssid)
return -EOPNOTSUPP;
- bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
- params->ssid, params->ssid_len);
- if (!bss) {
- iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
- schedule_timeout_interruptible(2 * HZ);
- bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
- params->ssid, params->ssid_len);
- }
- /* IBSS join only mode is not supported by UMAC ATM */
- if (bss) {
- cfg80211_put_bss(bss);
- return -EOPNOTSUPP;
- }
-
iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
iwm->umac_profile->ibss.band = chan->band;
iwm->umac_profile->ibss.channel = iwm->channel;
iwm->umac_profile->ssid.ssid_len = params->ssid_len;
memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
- if (params->bssid)
- memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
-
return iwm_send_mlme_profile(iwm);
}
@@ -490,12 +472,12 @@ static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version)
return 0;
}
+ if (wpa_version & NL80211_WPA_VERSION_1)
+ iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
+
if (wpa_version & NL80211_WPA_VERSION_2)
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
- if (wpa_version & NL80211_WPA_VERSION_1)
- iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK;
-
return 0;
}
@@ -646,6 +628,13 @@ static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
iwm->default_key = sme->key_idx;
}
+ /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */
+ if ((iwm->umac_profile->sec.flags &
+ (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) &&
+ iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) {
+ iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK;
+ }
+
ret = iwm_send_mlme_profile(iwm);
if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK ||
@@ -682,10 +671,24 @@ static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
static int iwm_cfg80211_set_txpower(struct wiphy *wiphy,
enum tx_power_setting type, int dbm)
{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+ int ret;
+
switch (type) {
case TX_POWER_AUTOMATIC:
return 0;
+ case TX_POWER_FIXED:
+ if (!test_bit(IWM_STATUS_READY, &iwm->status))
+ return 0;
+
+ ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
+ CFG_TX_PWR_LIMIT_USR, dbm * 2);
+ if (ret < 0)
+ return ret;
+
+ return iwm_tx_power_trigger(iwm);
default:
+ IWM_ERR(iwm, "Unsupported power type: %d\n", type);
return -EOPNOTSUPP;
}
@@ -696,7 +699,7 @@ static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm)
{
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
- *dbm = iwm->txpower;
+ *dbm = iwm->txpower >> 1;
return 0;
}
@@ -722,6 +725,33 @@ static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy,
CFG_POWER_INDEX, iwm->conf.power_index);
}
+int iwm_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+ return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD);
+}
+
+int iwm_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+
+ return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL);
+}
+
+int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
+{
+ struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
+ struct cfg80211_pmksa pmksa;
+
+ memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+ return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH);
+}
+
+
static struct cfg80211_ops iwm_cfg80211_ops = {
.change_virtual_intf = iwm_cfg80211_change_iface,
.add_key = iwm_cfg80211_add_key,
@@ -738,6 +768,9 @@ static struct cfg80211_ops iwm_cfg80211_ops = {
.set_tx_power = iwm_cfg80211_set_txpower,
.get_tx_power = iwm_cfg80211_get_txpower,
.set_power_mgmt = iwm_cfg80211_set_power_mgmt,
+ .set_pmksa = iwm_cfg80211_set_pmksa,
+ .del_pmksa = iwm_cfg80211_del_pmksa,
+ .flush_pmksa = iwm_cfg80211_flush_pmksa,
};
static const u32 cipher_suites[] = {
@@ -783,6 +816,7 @@ struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
set_wiphy_dev(wdev->wiphy, dev);
wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
+ wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS;
wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 84158b6d35d..777584d76a8 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -77,6 +77,11 @@ int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
int ret;
u8 oid = hdr->oid;
+ if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
+ IWM_ERR(iwm, "Interface is not ready yet");
+ return -EAGAIN;
+ }
+
umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER;
umac_cmd.resp = resp;
@@ -94,6 +99,10 @@ int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
return ret;
}
+static int modparam_wiwi = COEX_MODE_CM;
+module_param_named(wiwi, modparam_wiwi, int, 0644);
+MODULE_PARM_DESC(wiwi, "Wifi-WiMAX coexistence: 1=SA, 2=XOR, 3=CM (default)");
+
static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] =
{
{4, 3, 0, COEX_UNASSOC_IDLE_FLAGS},
@@ -117,18 +126,18 @@ static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] =
static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] =
{
{1, 1, 0, COEX_UNASSOC_IDLE_FLAGS},
- {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
+ {4, 4, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
{3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
- {5, 5, 0, COEX_CALIBRATION_FLAGS},
+ {6, 6, 0, COEX_CALIBRATION_FLAGS},
{3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
- {5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS},
+ {6, 5, 0, COEX_CONNECTION_ESTAB_FLAGS},
{4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS},
{4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
{4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
{4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
{1, 1, 0, COEX_RF_ON_FLAGS},
{1, 1, 0, COEX_RF_OFF_FLAGS},
- {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
+ {7, 7, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
{5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
{1, 1, 0, COEX_RSRVD1_FLAGS},
{1, 1, 0, COEX_RSRVD2_FLAGS}
@@ -143,7 +152,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm)
coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK;
- switch (iwm->conf.coexist_mode) {
+ switch (modparam_wiwi) {
case COEX_MODE_XOR:
case COEX_MODE_CM:
coex_enabled = 1;
@@ -168,7 +177,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm)
COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK |
COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK;
- switch (iwm->conf.coexist_mode) {
+ switch (modparam_wiwi) {
case COEX_MODE_XOR:
memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl,
sizeof(iwm_sta_xor_prio_tbl));
@@ -179,7 +188,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm)
break;
default:
IWM_ERR(iwm, "Invalid coex_mode 0x%x\n",
- iwm->conf.coexist_mode);
+ modparam_wiwi);
break;
}
} else
@@ -187,7 +196,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm)
return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD,
&coex_table_cmd,
- sizeof(struct iwm_coex_prio_table_cmd), 1);
+ sizeof(struct iwm_coex_prio_table_cmd), 0);
}
int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
@@ -275,6 +284,17 @@ int iwm_send_calib_results(struct iwm_priv *iwm)
return ret;
}
+int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit)
+{
+ struct iwm_ct_kill_cfg_cmd cmd;
+
+ cmd.entry_threshold = entry;
+ cmd.exit_threshold = exit;
+
+ return iwm_send_lmac_ptrough_cmd(iwm, REPLY_CT_KILL_CONFIG_CMD, &cmd,
+ sizeof(struct iwm_ct_kill_cfg_cmd), 0);
+}
+
int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp)
{
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
@@ -380,7 +400,7 @@ int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags)
return ret;
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
- CFG_COEX_MODE, iwm->conf.coexist_mode);
+ CFG_COEX_MODE, modparam_wiwi);
if (ret < 0)
return ret;
@@ -778,11 +798,24 @@ int iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
return ret;
ret = wait_event_interruptible_timeout(iwm->mlme_queue,
- (iwm->umac_profile_active == 0), 2 * HZ);
+ (iwm->umac_profile_active == 0), 5 * HZ);
return ret ? 0 : -EBUSY;
}
+int iwm_tx_power_trigger(struct iwm_priv *iwm)
+{
+ struct iwm_umac_pwr_trigger pwr_trigger;
+
+ pwr_trigger.hdr.oid = UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER;
+ pwr_trigger.hdr.buf_size =
+ cpu_to_le16(sizeof(struct iwm_umac_pwr_trigger) -
+ sizeof(struct iwm_umac_wifi_if));
+
+
+ return iwm_send_wifi_if_cmd(iwm, &pwr_trigger, sizeof(pwr_trigger), 1);
+}
+
int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags)
{
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
@@ -900,3 +933,58 @@ int iwm_target_reset(struct iwm_priv *iwm)
return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
}
+
+int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
+ struct iwm_umac_notif_stop_resume_tx *ntf)
+{
+ struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
+ struct iwm_umac_cmd umac_cmd;
+ struct iwm_umac_cmd_stop_resume_tx stp_res_cmd;
+ struct iwm_sta_info *sta_info;
+ u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id);
+ int i;
+
+ sta_info = &iwm->sta_table[sta_id];
+ if (!sta_info->valid) {
+ IWM_ERR(iwm, "Invalid STA: %d\n", sta_id);
+ return -EINVAL;
+ }
+
+ umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX;
+ umac_cmd.resp = 0;
+
+ stp_res_cmd.flags = ntf->flags;
+ stp_res_cmd.sta_id = ntf->sta_id;
+ stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk;
+ for (i = 0; i < IWM_UMAC_TID_NR; i++)
+ stp_res_cmd.last_seq_num[i] =
+ sta_info->tid_info[i].last_seq_num;
+
+ return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd,
+ sizeof(struct iwm_umac_cmd_stop_resume_tx));
+
+}
+
+int iwm_send_pmkid_update(struct iwm_priv *iwm,
+ struct cfg80211_pmksa *pmksa, u32 command)
+{
+ struct iwm_umac_pmkid_update update;
+ int ret;
+
+ memset(&update, 0, sizeof(struct iwm_umac_pmkid_update));
+
+ update.command = cpu_to_le32(command);
+ if (pmksa->bssid)
+ memcpy(&update.bssid, pmksa->bssid, ETH_ALEN);
+ if (pmksa->pmkid)
+ memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
+
+ ret = iwm_send_wifi_if_cmd(iwm, &update,
+ sizeof(struct iwm_umac_pmkid_update), 0);
+ if (ret) {
+ IWM_ERR(iwm, "PMKID update command failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
index e24d5b63399..06af0552cd7 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.h
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -102,7 +102,6 @@ enum {
CFG_SCAN_NUM_PASSIVE_CHAN_PER_PARTIAL_SCAN,
CFG_TLC_SUPPORTED_TX_HT_RATES,
CFG_TLC_SUPPORTED_TX_RATES,
- CFG_TLC_VALID_ANTENNA,
CFG_TLC_SPATIAL_STREAM_SUPPORTED,
CFG_TLC_RETRY_PER_RATE,
CFG_TLC_RETRY_PER_HT_RATE,
@@ -136,6 +135,10 @@ enum {
CFG_TLC_RENEW_ADDBA_DELAY,
CFG_TLC_NUM_OF_MULTISEC_TO_COUN_LOAD,
CFG_TLC_IS_STABLE_IN_HT,
+ CFG_TLC_SR_SIC_1ST_FAIL,
+ CFG_TLC_SR_SIC_1ST_PASS,
+ CFG_TLC_SR_SIC_TOTAL_FAIL,
+ CFG_TLC_SR_SIC_TOTAL_PASS,
CFG_RLC_CHAIN_CTRL,
CFG_TRK_TABLE_OP_MODE,
CFG_TRK_TABLE_RSSI_THRESHOLD,
@@ -147,6 +150,58 @@ enum {
CFG_MLME_DBG_NOTIF_BLOCK,
CFG_BT_OFF_BECONS_INTERVALS,
CFG_BT_FRAG_DURATION,
+ CFG_ACTIVE_CHAINS,
+ CFG_CALIB_CTRL,
+ CFG_CAPABILITY_SUPPORTED_HT_RATES,
+ CFG_HT_MAC_PARAM_INFO,
+ CFG_MIMO_PS_MODE,
+ CFG_HT_DEFAULT_CAPABILIES_INFO,
+ CFG_LED_SC_RESOLUTION_FACTOR,
+ CFG_PTAM_ENERGY_CCK_DET_DEFAULT,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_DEFAULT,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_DEFAULT,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_DEFAULT,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_DEFAULT,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_DEFAULT,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_DEFAULT,
+ CFG_PTAM_ENERGY_CCK_DET_MIN_VAL,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MIN_VAL,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_MIN_VAL,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MIN_VAL,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_MIN_VAL,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MIN_VAL,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_MIN_VAL,
+ CFG_PTAM_ENERGY_CCK_DET_MAX_VAL,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MAX_VAL,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_MAX_VAL,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MAX_VAL,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_MAX_VAL,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MAX_VAL,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_MAX_VAL,
+ CFG_PTAM_ENERGY_CCK_DET_STEP_VAL,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_STEP_VAL,
+ CFG_PTAM_CORR40_4_TH_ADD_MIN_STEP_VAL,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_STEP_VAL,
+ CFG_PTAM_CORR32_4_TH_ADD_MIN_STEP_VAL,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_STEP_VAL,
+ CFG_PTAM_CORR32_1_TH_ADD_MIN_STEP_VAL,
+ CFG_PTAM_LINK_SENS_FA_OFDM_MAX,
+ CFG_PTAM_LINK_SENS_FA_OFDM_MIN,
+ CFG_PTAM_LINK_SENS_FA_CCK_MAX,
+ CFG_PTAM_LINK_SENS_FA_CCK_MIN,
+ CFG_PTAM_LINK_SENS_NRG_DIFF,
+ CFG_PTAM_LINK_SENS_NRG_MARGIN,
+ CFG_PTAM_LINK_SENS_MAX_NUMBER_OF_TIMES_IN_CCK_NO_FA,
+ CFG_PTAM_LINK_SENS_AUTO_CORR_MAX_TH_CCK,
+ CFG_AGG_MGG_TID_LOAD_ADDBA_THRESHOLD,
+ CFG_AGG_MGG_TID_LOAD_DELBA_THRESHOLD,
+ CFG_AGG_MGG_ADDBA_BUF_SIZE,
+ CFG_AGG_MGG_ADDBA_INACTIVE_TIMEOUT,
+ CFG_AGG_MGG_ADDBA_DEBUG_FLAGS,
+ CFG_SCAN_PERIODIC_RSSI_HIGH_THRESHOLD,
+ CFG_SCAN_PERIODIC_COEF_RSSI_HIGH,
+ CFG_11D_ENABLED,
+ CFG_11H_FEATURE_FLAGS,
/* <-- LAST --> */
CFG_TBL_FIX_LAST
@@ -155,7 +210,8 @@ enum {
/* variable size table */
enum {
CFG_NET_ADDR = 0,
- CFG_PROFILE,
+ CFG_LED_PATTERN_TABLE,
+
/* <-- LAST --> */
CFG_TBL_VAR_LAST
};
@@ -288,6 +344,9 @@ struct iwm_umac_cmd_scan_request {
/* iwm_umac_security.flag is WSC mode on -- bits [2:2] */
#define UMAC_SEC_FLG_WSC_ON_POS 2
#define UMAC_SEC_FLG_WSC_ON_SEED 1
+#define UMAC_SEC_FLG_WSC_ON_MSK (UMAC_SEC_FLG_WSC_ON_SEED << \
+ UMAC_SEC_FLG_WSC_ON_POS)
+
/* Legacy profile can use only WEP40 and WEP104 for encryption and
* OPEN or PSK for authentication */
@@ -382,10 +441,34 @@ struct iwm_umac_tx_key_id {
u8 reserved[3];
} __attribute__ ((packed));
+struct iwm_umac_pwr_trigger {
+ struct iwm_umac_wifi_if hdr;
+ __le32 reseved;
+} __attribute__ ((packed));
+
struct iwm_umac_cmd_stats_req {
__le32 flags;
} __attribute__ ((packed));
+struct iwm_umac_cmd_stop_resume_tx {
+ u8 flags;
+ u8 sta_id;
+ __le16 stop_resume_tid_msk;
+ __le16 last_seq_num[IWM_UMAC_TID_NR];
+ u16 reserved;
+} __attribute__ ((packed));
+
+#define IWM_CMD_PMKID_ADD 1
+#define IWM_CMD_PMKID_DEL 2
+#define IWM_CMD_PMKID_FLUSH 3
+
+struct iwm_umac_pmkid_update {
+ __le32 command;
+ u8 bssid[ETH_ALEN];
+ __le16 reserved;
+ u8 pmkid[WLAN_PMKID_LEN];
+} __attribute__ ((packed));
+
/* LMAC commands */
int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
int iwm_send_prio_table(struct iwm_priv *iwm);
@@ -393,6 +476,7 @@ int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
int iwm_send_calib_results(struct iwm_priv *iwm);
int iwm_store_rxiq_calib_result(struct iwm_priv *iwm);
+int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit);
/* UMAC commands */
int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
@@ -407,11 +491,16 @@ int iwm_invalidate_mlme_profile(struct iwm_priv *iwm);
int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id);
int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx);
int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key);
+int iwm_tx_power_trigger(struct iwm_priv *iwm);
int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags);
int iwm_send_umac_channel_list(struct iwm_priv *iwm);
int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
int ssid_num);
int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
+int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
+ struct iwm_umac_notif_stop_resume_tx *ntf);
+int iwm_send_pmkid_update(struct iwm_priv *iwm,
+ struct cfg80211_pmksa *pmksa, u32 command);
/* UDMA commands */
int iwm_target_reset(struct iwm_priv *iwm);
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c
index 1465379f900..be992ca41cf 100644
--- a/drivers/net/wireless/iwmc3200wifi/debugfs.c
+++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c
@@ -158,6 +158,29 @@ static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer,
}
spin_unlock_irqrestore(&txq->queue.lock, flags);
+
+ spin_lock_irqsave(&txq->stopped_queue.lock, flags);
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tStopped Queue len: %d\n",
+ skb_queue_len(&txq->stopped_queue));
+ for (j = 0; j < skb_queue_len(&txq->stopped_queue); j++) {
+ struct iwm_tx_info *tx_info;
+
+ skb = skb->next;
+ tx_info = skb_to_tx_info(skb);
+
+ len += snprintf(buf + len, buf_len - len,
+ "\tSKB #%d\n", j);
+ len += snprintf(buf + len, buf_len - len,
+ "\t\tsta: %d\n", tx_info->sta);
+ len += snprintf(buf + len, buf_len - len,
+ "\t\tcolor: %d\n", tx_info->color);
+ len += snprintf(buf + len, buf_len - len,
+ "\t\ttid: %d\n", tx_info->tid);
+ }
+
+ spin_unlock_irqrestore(&txq->stopped_queue.lock, flags);
}
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.c b/drivers/net/wireless/iwmc3200wifi/eeprom.c
index 365910fbe01..8091421ee5e 100644
--- a/drivers/net/wireless/iwmc3200wifi/eeprom.c
+++ b/drivers/net/wireless/iwmc3200wifi/eeprom.c
@@ -66,6 +66,10 @@ static struct iwm_eeprom_entry eeprom_map[] = {
[IWM_EEPROM_SKU_CAP] =
{"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN},
+ [IWM_EEPROM_FAT_CHANNELS_CAP] =
+ {"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF,
+ IWM_EEPROM_FAT_CHANNELS_CAP_LEN},
+
[IWM_EEPROM_CALIB_RXIQ_OFFSET] =
{"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN},
@@ -146,6 +150,52 @@ u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id)
return iwm->eeprom + eeprom_map[eeprom_id].offset;
}
+int iwm_eeprom_fat_channels(struct iwm_priv *iwm)
+{
+ struct wiphy *wiphy = iwm_to_wiphy(iwm);
+ struct ieee80211_supported_band *band;
+ u16 *channels, i;
+
+ channels = (u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_FAT_CHANNELS_CAP);
+ if (IS_ERR(channels))
+ return PTR_ERR(channels);
+
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
+ band->ht_cap.ht_supported = true;
+
+ for (i = 0; i < IWM_EEPROM_FAT_CHANNELS_24; i++)
+ if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED))
+ band->ht_cap.ht_supported = false;
+
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
+ band->ht_cap.ht_supported = true;
+ for (i = IWM_EEPROM_FAT_CHANNELS_24; i < IWM_EEPROM_FAT_CHANNELS; i++)
+ if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED))
+ band->ht_cap.ht_supported = false;
+
+ return 0;
+}
+
+u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm)
+{
+ u16 sku_cap;
+ u32 wireless_mode = 0;
+
+ sku_cap = *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP));
+
+ if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_24GHZ)
+ wireless_mode |= WIRELESS_MODE_11G;
+
+ if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_52GHZ)
+ wireless_mode |= WIRELESS_MODE_11A;
+
+ if (sku_cap & IWM_EEPROM_SKU_CAP_11N_ENABLE)
+ wireless_mode |= WIRELESS_MODE_11N;
+
+ return wireless_mode;
+}
+
+
int iwm_eeprom_init(struct iwm_priv *iwm)
{
int i, ret = 0;
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.h b/drivers/net/wireless/iwmc3200wifi/eeprom.h
index cdb31a6a1f5..4e3a3fdab0d 100644
--- a/drivers/net/wireless/iwmc3200wifi/eeprom.h
+++ b/drivers/net/wireless/iwmc3200wifi/eeprom.h
@@ -48,6 +48,7 @@ enum {
IWM_EEPROM_CARD_ID,
IWM_EEPROM_RADIO_CONF,
IWM_EEPROM_SKU_CAP,
+ IWM_EEPROM_FAT_CHANNELS_CAP,
IWM_EEPROM_INDIRECT_OFFSET,
IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET,
@@ -58,14 +59,15 @@ enum {
IWM_EEPROM_LAST,
};
-#define IWM_EEPROM_SIG_OFF 0x00
-#define IWM_EEPROM_VERSION_OFF (0x54 << 1)
-#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1)
-#define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1)
-#define IWM_EEPROM_CARD_ID_OFF (0x5d << 1)
-#define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1)
-#define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1)
-#define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1)
+#define IWM_EEPROM_SIG_OFF 0x00
+#define IWM_EEPROM_VERSION_OFF (0x54 << 1)
+#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1)
+#define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1)
+#define IWM_EEPROM_CARD_ID_OFF (0x5d << 1)
+#define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1)
+#define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1)
+#define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1)
+#define IWM_EEPROM_FAT_CHANNELS_CAP_OFF (0xde << 1)
#define IWM_EEPROM_SIG_LEN 4
#define IWM_EEPROM_VERSION_LEN 2
@@ -74,6 +76,7 @@ enum {
#define IWM_EEPROM_CARD_ID_LEN 2
#define IWM_EEPROM_RADIO_CONF_LEN 2
#define IWM_EEPROM_SKU_CAP_LEN 2
+#define IWM_EEPROM_FAT_CHANNELS_CAP_LEN 40
#define IWM_EEPROM_INDIRECT_LEN 2
#define IWM_MAX_EEPROM_DATA_LEN 240
@@ -87,6 +90,14 @@ enum {
#define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5)
#define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6)
+#define IWM_EEPROM_FAT_CHANNELS 20
+/* 2.4 gHz FAT primary channels: 1, 2, 3, 4, 5, 6, 7, 8, 9 */
+#define IWM_EEPROM_FAT_CHANNELS_24 9
+/* 5.2 gHz FAT primary channels: 36,44,52,60,100,108,116,124,132,149,157 */
+#define IWM_EEPROM_FAT_CHANNELS_52 11
+
+#define IWM_EEPROM_FAT_CHANNEL_ENABLED (1 << 0)
+
enum {
IWM_EEPROM_CALIB_CAL_HDR,
IWM_EEPROM_CALIB_TX_POWER,
@@ -110,5 +121,7 @@ struct iwm_eeprom_entry {
int iwm_eeprom_init(struct iwm_priv *iwm);
void iwm_eeprom_exit(struct iwm_priv *iwm);
u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id);
+int iwm_eeprom_fat_channels(struct iwm_priv *iwm);
+u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm);
#endif
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c
index 6b0bcad758c..49067092d33 100644
--- a/drivers/net/wireless/iwmc3200wifi/fw.c
+++ b/drivers/net/wireless/iwmc3200wifi/fw.c
@@ -217,6 +217,13 @@ static int iwm_load_img(struct iwm_priv *iwm, const char *img_name)
IWM_BUILD_YEAR(build_date), IWM_BUILD_MONTH(build_date),
IWM_BUILD_DAY(build_date));
+ if (!strcmp(img_name, iwm->bus_ops->umac_name))
+ sprintf(iwm->umac_version, "%02X.%02X",
+ ver->major, ver->minor);
+
+ if (!strcmp(img_name, iwm->bus_ops->lmac_name))
+ sprintf(iwm->lmac_version, "%02X.%02X",
+ ver->major, ver->minor);
err_release_fw:
release_firmware(fw);
@@ -398,6 +405,8 @@ int iwm_load_fw(struct iwm_priv *iwm)
iwm_send_prio_table(iwm);
iwm_send_calib_results(iwm);
iwm_send_periodic_calib_cfg(iwm, periodic_calib_map);
+ iwm_send_ct_kill_cfg(iwm, iwm->conf.ct_kill_entry,
+ iwm->conf.ct_kill_exit);
return 0;
diff --git a/drivers/net/wireless/iwmc3200wifi/hal.c b/drivers/net/wireless/iwmc3200wifi/hal.c
index c430418248b..d13c8853ee8 100644
--- a/drivers/net/wireless/iwmc3200wifi/hal.c
+++ b/drivers/net/wireless/iwmc3200wifi/hal.c
@@ -411,7 +411,7 @@ static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
/*
* iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
* Sending command to the LMAC is equivalent to sending a
- * regular UMAC command with the LMAC passtrough or the LMAC
+ * regular UMAC command with the LMAC passthrough or the LMAC
* wrapper UMAC command IDs.
*/
int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 1b02a4e2a1a..5a26bb05a33 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -65,6 +65,8 @@ struct iwm_conf {
u32 sdio_ior_timeout;
unsigned long calib_map;
unsigned long expected_calib_map;
+ u8 ct_kill_entry;
+ u8 ct_kill_exit;
bool reset_on_fatal_err;
bool auto_connect;
bool wimax_not_present;
@@ -79,7 +81,6 @@ struct iwm_conf {
u32 assoc_timeout;
u32 roam_timeout;
u32 wireless_mode;
- u32 coexist_mode;
u8 ibss_band;
u8 ibss_channel;
@@ -129,11 +130,18 @@ struct iwm_notif {
unsigned long buf_size;
};
+struct iwm_tid_info {
+ __le16 last_seq_num;
+ bool stopped;
+ struct mutex mutex;
+};
+
struct iwm_sta_info {
u8 addr[ETH_ALEN];
bool valid;
bool qos;
u8 color;
+ struct iwm_tid_info tid_info[IWM_UMAC_TID_NR];
};
struct iwm_tx_info {
@@ -183,6 +191,8 @@ struct iwm_key {
struct iwm_tx_queue {
int id;
struct sk_buff_head queue;
+ struct sk_buff_head stopped_queue;
+ spinlock_t lock;
struct workqueue_struct *wq;
struct work_struct worker;
u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE];
@@ -276,12 +286,14 @@ struct iwm_priv {
struct iw_statistics wstats;
struct delayed_work stats_request;
struct delayed_work disconnect;
+ struct delayed_work ct_kill_delay;
struct iwm_debugfs dbg;
u8 *eeprom;
struct timer_list watchdog;
struct work_struct reset_worker;
+ struct work_struct auth_retry_worker;
struct mutex mutex;
u8 *req_ie;
@@ -290,6 +302,8 @@ struct iwm_priv {
int resp_ie_len;
struct iwm_fw_error_hdr *last_fw_err;
+ char umac_version[8];
+ char lmac_version[8];
char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
};
@@ -335,6 +349,7 @@ int iwm_up(struct iwm_priv *iwm);
int iwm_down(struct iwm_priv *iwm);
/* TX API */
+u16 iwm_tid_to_queue(u16 tid);
void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages);
void iwm_tx_worker(struct work_struct *work);
int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
diff --git a/drivers/net/wireless/iwmc3200wifi/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h
index 6c1a14c4480..a3a79b5e289 100644
--- a/drivers/net/wireless/iwmc3200wifi/lmac.h
+++ b/drivers/net/wireless/iwmc3200wifi/lmac.h
@@ -187,6 +187,14 @@ struct iwm_coex_prio_table_cmd {
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
+/* CT kill config command */
+struct iwm_ct_kill_cfg_cmd {
+ u32 exit_threshold;
+ u32 reserved;
+ u32 entry_threshold;
+} __attribute__ ((packed));
+
+
/* LMAC OP CODES */
#define REPLY_PAD 0x0
#define REPLY_ALIVE 0x1
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 222eb2cf1b3..7f34d6dd3c4 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -64,9 +64,10 @@ static struct iwm_conf def_iwm_conf = {
BIT(PHY_CALIBRATE_TX_IQ_CMD) |
BIT(PHY_CALIBRATE_RX_IQ_CMD) |
BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD),
+ .ct_kill_entry = 110,
+ .ct_kill_exit = 110,
.reset_on_fatal_err = 1,
.auto_connect = 1,
- .wimax_not_present = 0,
.enable_qos = 1,
.mode = UMAC_MODE_BSS,
@@ -78,8 +79,8 @@ static struct iwm_conf def_iwm_conf = {
.assoc_timeout = 2,
.roam_timeout = 10,
- .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G,
- .coexist_mode = COEX_MODE_CM,
+ .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G |
+ WIRELESS_MODE_11N,
/* IBSS */
.ibss_band = UMAC_BAND_2GHZ,
@@ -92,6 +93,10 @@ static int modparam_reset;
module_param_named(reset, modparam_reset, bool, 0644);
MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])");
+static int modparam_wimax_enable = 1;
+module_param_named(wimax_enable, modparam_wimax_enable, bool, 0644);
+MODULE_PARM_DESC(wimax_enable, "Enable wimax core (default 1 [wimax enabled])");
+
int iwm_mode_to_nl80211_iftype(int mode)
{
switch (mode) {
@@ -134,6 +139,17 @@ static void iwm_disconnect_work(struct work_struct *work)
cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL);
}
+static void iwm_ct_kill_work(struct work_struct *work)
+{
+ struct iwm_priv *iwm =
+ container_of(work, struct iwm_priv, ct_kill_delay.work);
+ struct wiphy *wiphy = iwm_to_wiphy(iwm);
+
+ IWM_INFO(iwm, "CT kill delay timeout\n");
+
+ wiphy_rfkill_set_hw_state(wiphy, false);
+}
+
static int __iwm_up(struct iwm_priv *iwm);
static int __iwm_down(struct iwm_priv *iwm);
@@ -195,6 +211,33 @@ static void iwm_reset_worker(struct work_struct *work)
mutex_unlock(&iwm->mutex);
}
+static void iwm_auth_retry_worker(struct work_struct *work)
+{
+ struct iwm_priv *iwm;
+ int i, ret;
+
+ iwm = container_of(work, struct iwm_priv, auth_retry_worker);
+ if (iwm->umac_profile_active) {
+ ret = iwm_invalidate_mlme_profile(iwm);
+ if (ret < 0)
+ return;
+ }
+
+ iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
+
+ ret = iwm_send_mlme_profile(iwm);
+ if (ret < 0)
+ return;
+
+ for (i = 0; i < IWM_NUM_KEYS; i++)
+ if (iwm->keys[i].key_len)
+ iwm_set_key(iwm, 0, &iwm->keys[i]);
+
+ iwm_set_tx_key(iwm, iwm->default_key);
+}
+
+
+
static void iwm_watchdog(unsigned long data)
{
struct iwm_priv *iwm = (struct iwm_priv *)data;
@@ -207,7 +250,7 @@ static void iwm_watchdog(unsigned long data)
int iwm_priv_init(struct iwm_priv *iwm)
{
- int i;
+ int i, j;
char name[32];
iwm->status = 0;
@@ -226,7 +269,9 @@ int iwm_priv_init(struct iwm_priv *iwm)
iwm->scan_id = 1;
INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request);
INIT_DELAYED_WORK(&iwm->disconnect, iwm_disconnect_work);
+ INIT_DELAYED_WORK(&iwm->ct_kill_delay, iwm_ct_kill_work);
INIT_WORK(&iwm->reset_worker, iwm_reset_worker);
+ INIT_WORK(&iwm->auth_retry_worker, iwm_auth_retry_worker);
INIT_LIST_HEAD(&iwm->bss_list);
skb_queue_head_init(&iwm->rx_list);
@@ -249,6 +294,8 @@ int iwm_priv_init(struct iwm_priv *iwm)
return -EAGAIN;
skb_queue_head_init(&iwm->txq[i].queue);
+ skb_queue_head_init(&iwm->txq[i].stopped_queue);
+ spin_lock_init(&iwm->txq[i].lock);
}
for (i = 0; i < IWM_NUM_KEYS; i++)
@@ -256,6 +303,12 @@ int iwm_priv_init(struct iwm_priv *iwm)
iwm->default_key = -1;
+ for (i = 0; i < IWM_STA_TABLE_NUM; i++)
+ for (j = 0; j < IWM_UMAC_TID_NR; j++) {
+ mutex_init(&iwm->sta_table[i].tid_info[j].mutex);
+ iwm->sta_table[i].tid_info[j].stopped = false;
+ }
+
init_timer(&iwm->watchdog);
iwm->watchdog.function = iwm_watchdog;
iwm->watchdog.data = (unsigned long)iwm;
@@ -436,7 +489,7 @@ static int iwm_config_boot_params(struct iwm_priv *iwm)
int ret;
/* check Wimax is off and config debug monitor */
- if (iwm->conf.wimax_not_present) {
+ if (!modparam_wimax_enable) {
u32 data1 = 0x1f;
u32 addr1 = 0x606BE258;
@@ -529,6 +582,7 @@ void iwm_link_off(struct iwm_priv *iwm)
for (i = 0; i < IWM_TX_QUEUES; i++) {
skb_queue_purge(&iwm->txq[i].queue);
+ skb_queue_purge(&iwm->txq[i].stopped_queue);
iwm->txq[i].concat_count = 0;
iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf;
@@ -587,6 +641,8 @@ static int __iwm_up(struct iwm_priv *iwm)
{
int ret;
struct iwm_notif *notif_reboot, *notif_ack = NULL;
+ struct wiphy *wiphy = iwm_to_wiphy(iwm);
+ u32 wireless_mode;
ret = iwm_bus_enable(iwm);
if (ret) {
@@ -638,6 +694,8 @@ static int __iwm_up(struct iwm_priv *iwm)
IWM_ERR(iwm, "MAC reading failed\n");
goto err_disable;
}
+ memcpy(iwm_to_ndev(iwm)->perm_addr, iwm_to_ndev(iwm)->dev_addr,
+ ETH_ALEN);
/* We can load the FWs */
ret = iwm_load_fw(iwm);
@@ -646,6 +704,30 @@ static int __iwm_up(struct iwm_priv *iwm)
goto err_disable;
}
+ ret = iwm_eeprom_fat_channels(iwm);
+ if (ret) {
+ IWM_ERR(iwm, "Couldnt read HT channels EEPROM entries\n");
+ goto err_fw;
+ }
+
+ /*
+ * Read our SKU capabilities.
+ * If it's valid, we AND the configured wireless mode with the
+ * device EEPROM value as the current profile wireless mode.
+ */
+ wireless_mode = iwm_eeprom_wireless_mode(iwm);
+ if (wireless_mode) {
+ iwm->conf.wireless_mode &= wireless_mode;
+ if (iwm->umac_profile)
+ iwm->umac_profile->wireless_mode =
+ iwm->conf.wireless_mode;
+ } else
+ IWM_ERR(iwm, "Wrong SKU capabilities: 0x%x\n",
+ *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP)));
+
+ snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "L%s_U%s",
+ iwm->lmac_version, iwm->umac_version);
+
/* We configure the UMAC and enable the wifi module */
ret = iwm_send_umac_config(iwm,
cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) |
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index 35ec006c2d2..e4f0f8705f6 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -76,6 +76,14 @@ static int iwm_stop(struct net_device *ndev)
*/
static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
+u16 iwm_tid_to_queue(u16 tid)
+{
+ if (tid > IWM_UMAC_TID_NR - 2)
+ return -EINVAL;
+
+ return iwm_1d_to_queue[tid];
+}
+
static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb)
{
skb->priority = cfg80211_classify8021d(skb);
@@ -152,6 +160,7 @@ void iwm_if_free(struct iwm_priv *iwm)
if (!iwm_to_ndev(iwm))
return;
+ cancel_delayed_work_sync(&iwm->ct_kill_delay);
free_netdev(iwm_to_ndev(iwm));
iwm_priv_deinit(iwm);
kfree(iwm->umac_profile);
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 771a301003c..1c57c1f72cb 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -423,7 +423,9 @@ static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf,
if (IS_ERR(ticket_node))
return PTR_ERR(ticket_node);
- IWM_DBG_RX(iwm, DBG, "TICKET RELEASE(%d)\n",
+ IWM_DBG_RX(iwm, DBG, "TICKET %s(%d)\n",
+ ticket->action == IWM_RX_TICKET_RELEASE ?
+ "RELEASE" : "DROP",
ticket->id);
list_add_tail(&ticket_node->node, &iwm->rx_tickets);
@@ -500,6 +502,18 @@ static int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf,
return 0;
}
+static u8 iwm_is_open_wep_profile(struct iwm_priv *iwm)
+{
+ if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 ||
+ iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) &&
+ (iwm->umac_profile->sec.ucast_cipher ==
+ iwm->umac_profile->sec.mcast_cipher) &&
+ (iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN))
+ return 1;
+
+ return 0;
+}
+
static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
unsigned long buf_size,
struct iwm_wifi_cmd *cmd)
@@ -565,11 +579,17 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf,
goto ibss;
if (!test_bit(IWM_STATUS_RESETTING, &iwm->status))
- cfg80211_connect_result(iwm_to_ndev(iwm),
- complete->bssid,
- NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
+ if (!iwm_is_open_wep_profile(iwm)) {
+ cfg80211_connect_result(iwm_to_ndev(iwm),
+ complete->bssid,
+ NULL, 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_KERNEL);
+ } else {
+ /* Let's try shared WEP auth */
+ IWM_ERR(iwm, "Trying WEP shared auth\n");
+ schedule_work(&iwm->auth_retry_worker);
+ }
else
cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0,
GFP_KERNEL);
@@ -713,6 +733,19 @@ static int iwm_mlme_update_sta_table(struct iwm_priv *iwm, u8 *buf,
return 0;
}
+static int iwm_mlme_medium_lost(struct iwm_priv *iwm, u8 *buf,
+ unsigned long buf_size,
+ struct iwm_wifi_cmd *cmd)
+{
+ struct wiphy *wiphy = iwm_to_wiphy(iwm);
+
+ IWM_DBG_NTF(iwm, DBG, "WiFi/WiMax coexistence radio is OFF\n");
+
+ wiphy_rfkill_set_hw_state(wiphy, true);
+
+ return 0;
+}
+
static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf,
unsigned long buf_size,
struct iwm_wifi_cmd *cmd)
@@ -899,6 +932,8 @@ static int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf,
case WIFI_IF_NTFY_EXTENDED_IE_REQUIRED:
IWM_DBG_MLME(iwm, DBG, "Extended IE required\n");
break;
+ case WIFI_IF_NTFY_RADIO_PREEMPTION:
+ return iwm_mlme_medium_lost(iwm, buf, buf_size, cmd);
case WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED:
return iwm_mlme_update_bss_table(iwm, buf, buf_size, cmd);
case WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED:
@@ -1052,12 +1087,83 @@ static int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf,
return 0;
}
+static int iwm_ntf_stop_resume_tx(struct iwm_priv *iwm, u8 *buf,
+ unsigned long buf_size,
+ struct iwm_wifi_cmd *cmd)
+{
+ struct iwm_umac_notif_stop_resume_tx *stp_res_tx =
+ (struct iwm_umac_notif_stop_resume_tx *)buf;
+ struct iwm_sta_info *sta_info;
+ struct iwm_tid_info *tid_info;
+ u8 sta_id = STA_ID_N_COLOR_ID(stp_res_tx->sta_id);
+ u16 tid_msk = le16_to_cpu(stp_res_tx->stop_resume_tid_msk);
+ int bit, ret = 0;
+ bool stop = false;
+
+ IWM_DBG_NTF(iwm, DBG, "stop/resume notification:\n"
+ "\tflags: 0x%x\n"
+ "\tSTA id: %d\n"
+ "\tTID bitmask: 0x%x\n",
+ stp_res_tx->flags, stp_res_tx->sta_id,
+ stp_res_tx->stop_resume_tid_msk);
+
+ if (stp_res_tx->flags & UMAC_STOP_TX_FLAG)
+ stop = true;
+
+ sta_info = &iwm->sta_table[sta_id];
+ if (!sta_info->valid) {
+ IWM_ERR(iwm, "Stoping an invalid STA: %d %d\n",
+ sta_id, stp_res_tx->sta_id);
+ return -EINVAL;
+ }
+
+ for_each_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) {
+ tid_info = &sta_info->tid_info[bit];
+
+ mutex_lock(&tid_info->mutex);
+ tid_info->stopped = stop;
+ mutex_unlock(&tid_info->mutex);
+
+ if (!stop) {
+ struct iwm_tx_queue *txq;
+ u16 queue = iwm_tid_to_queue(bit);
+
+ if (queue < 0)
+ continue;
+
+ txq = &iwm->txq[queue];
+ /*
+ * If we resume, we have to move our SKBs
+ * back to the tx queue and queue some work.
+ */
+ spin_lock_bh(&txq->lock);
+ skb_queue_splice_init(&txq->queue, &txq->stopped_queue);
+ spin_unlock_bh(&txq->lock);
+
+ queue_work(txq->wq, &txq->worker);
+ }
+
+ }
+
+ /* We send an ACK only for the stop case */
+ if (stop)
+ ret = iwm_send_umac_stop_resume_tx(iwm, stp_res_tx);
+
+ return ret;
+}
+
static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
unsigned long buf_size,
struct iwm_wifi_cmd *cmd)
{
- struct iwm_umac_wifi_if *hdr =
- (struct iwm_umac_wifi_if *)cmd->buf.payload;
+ struct iwm_umac_wifi_if *hdr;
+
+ if (cmd == NULL) {
+ IWM_ERR(iwm, "Couldn't find expected wifi command\n");
+ return -EINVAL;
+ }
+
+ hdr = (struct iwm_umac_wifi_if *)cmd->buf.payload;
IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: "
"oid is 0x%x\n", hdr->oid);
@@ -1079,6 +1185,7 @@ static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
return 0;
}
+#define CT_KILL_DELAY (30 * HZ)
static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf,
unsigned long buf_size, struct iwm_wifi_cmd *cmd)
{
@@ -1091,7 +1198,20 @@ static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf,
flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF",
flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF");
- wiphy_rfkill_set_hw_state(wiphy, flags & IWM_CARD_STATE_HW_DISABLED);
+ if (flags & IWM_CARD_STATE_CTKILL_DISABLED) {
+ /*
+ * We got a CTKILL event: We bring the interface down in
+ * oder to cool the device down, and try to bring it up
+ * 30 seconds later. If it's still too hot, we'll go through
+ * this code path again.
+ */
+ cancel_delayed_work_sync(&iwm->ct_kill_delay);
+ schedule_delayed_work(&iwm->ct_kill_delay, CT_KILL_DELAY);
+ }
+
+ wiphy_rfkill_set_hw_state(wiphy, flags &
+ (IWM_CARD_STATE_HW_DISABLED |
+ IWM_CARD_STATE_CTKILL_DISABLED));
return 0;
}
@@ -1282,6 +1402,14 @@ int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size)
switch (le32_to_cpu(hdr->cmd)) {
case UMAC_REBOOT_BARKER:
+ if (test_bit(IWM_STATUS_READY, &iwm->status)) {
+ IWM_ERR(iwm, "Unexpected BARKER\n");
+
+ schedule_work(&iwm->reset_worker);
+
+ return 0;
+ }
+
return iwm_notif_send(iwm, NULL, IWM_BARKER_REBOOT_NOTIFICATION,
IWM_SRC_UDMA, buf, buf_size);
case UMAC_ACK_BARKER:
@@ -1308,6 +1436,7 @@ static const iwm_handler iwm_umac_handlers[] =
[UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics,
[UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy,
[UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list,
+ [UMAC_CMD_OPCODE_STOP_RESUME_STA_TX] = iwm_ntf_stop_resume_tx,
[REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet,
[UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper,
};
@@ -1444,11 +1573,12 @@ static void iwm_rx_process_packet(struct iwm_priv *iwm,
}
break;
case IWM_RX_TICKET_DROP:
- IWM_DBG_RX(iwm, DBG, "DROP packet\n");
+ IWM_DBG_RX(iwm, DBG, "DROP packet: 0x%x\n",
+ le16_to_cpu(ticket_node->ticket->flags));
kfree_skb(packet->skb);
break;
default:
- IWM_ERR(iwm, "Unknow ticket action: %d\n",
+ IWM_ERR(iwm, "Unknown ticket action: %d\n",
le16_to_cpu(ticket_node->ticket->action));
kfree_skb(packet->skb);
}
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index 8b1de84003c..a7ec7eac913 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -224,8 +224,6 @@ static int if_sdio_disable(struct iwm_priv *iwm)
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
int ret;
- iwm_reset(iwm);
-
sdio_claim_host(hw->func);
sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
if (ret < 0)
@@ -237,6 +235,8 @@ static int if_sdio_disable(struct iwm_priv *iwm)
iwm_sdio_rx_free(hw);
+ iwm_reset(iwm);
+
IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n");
return 0;
@@ -399,6 +399,9 @@ static struct iwm_if_ops if_sdio_ops = {
.calib_lmac_name = "iwmc3200wifi-calib-sdio.bin",
.lmac_name = "iwmc3200wifi-lmac-sdio.bin",
};
+MODULE_FIRMWARE("iwmc3200wifi-umac-sdio.bin");
+MODULE_FIRMWARE("iwmc3200wifi-calib-sdio.bin");
+MODULE_FIRMWARE("iwmc3200wifi-lmac-sdio.bin");
static int iwm_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
@@ -493,8 +496,10 @@ static void iwm_sdio_remove(struct sdio_func *func)
}
static const struct sdio_device_id iwm_sdio_ids[] = {
- { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL,
- SDIO_DEVICE_ID_INTEL_IWMC3200WIFI) },
+ /* Global/AGN SKU */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1403) },
+ /* BGN SKU */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1408) },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids);
diff --git a/drivers/net/wireless/iwmc3200wifi/tx.c b/drivers/net/wireless/iwmc3200wifi/tx.c
index e3b4f7902da..55905f02309 100644
--- a/drivers/net/wireless/iwmc3200wifi/tx.c
+++ b/drivers/net/wireless/iwmc3200wifi/tx.c
@@ -329,7 +329,7 @@ static int iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb,
memcpy(buf + sizeof(*hdr), skb->data, skb->len);
- return 0;
+ return umac_cmd.seq_num;
}
static int iwm_tx_send_concat_packets(struct iwm_priv *iwm,
@@ -354,16 +354,15 @@ static int iwm_tx_send_concat_packets(struct iwm_priv *iwm,
return ret;
}
-#define CONFIG_IWM_TX_CONCATENATED 1
-
void iwm_tx_worker(struct work_struct *work)
{
struct iwm_priv *iwm;
struct iwm_tx_info *tx_info = NULL;
struct sk_buff *skb;
- int cmdlen, ret;
struct iwm_tx_queue *txq;
- int pool_id;
+ struct iwm_sta_info *sta_info;
+ struct iwm_tid_info *tid_info;
+ int cmdlen, ret, pool_id;
txq = container_of(work, struct iwm_tx_queue, worker);
iwm = container_of(txq, struct iwm_priv, txq[txq->id]);
@@ -373,19 +372,46 @@ void iwm_tx_worker(struct work_struct *work)
while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
!skb_queue_empty(&txq->queue)) {
+ spin_lock_bh(&txq->lock);
skb = skb_dequeue(&txq->queue);
+ spin_unlock_bh(&txq->lock);
+
tx_info = skb_to_tx_info(skb);
+ sta_info = &iwm->sta_table[tx_info->sta];
+ if (!sta_info->valid) {
+ IWM_ERR(iwm, "Trying to send a frame to unknown STA\n");
+ kfree_skb(skb);
+ continue;
+ }
+
+ tid_info = &sta_info->tid_info[tx_info->tid];
+
+ mutex_lock(&tid_info->mutex);
+
+ /*
+ * If the RAxTID is stopped, we queue the skb to the stopped
+ * queue.
+ * Whenever we'll get a UMAC notification to resume the tx flow
+ * for this RAxTID, we'll merge back the stopped queue into the
+ * regular queue. See iwm_ntf_stop_resume_tx() from rx.c.
+ */
+ if (tid_info->stopped) {
+ IWM_DBG_TX(iwm, DBG, "%dx%d stopped\n",
+ tx_info->sta, tx_info->tid);
+ spin_lock_bh(&txq->lock);
+ skb_queue_tail(&txq->stopped_queue, skb);
+ spin_unlock_bh(&txq->lock);
+
+ mutex_unlock(&tid_info->mutex);
+ continue;
+ }
+
cmdlen = IWM_UDMA_HDR_LEN + skb->len;
IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: "
"%d, color: %d\n", txq->id, skb, tx_info->sta,
tx_info->color);
-#if !CONFIG_IWM_TX_CONCATENATED
- /* temporarily keep this to comparing the performance */
- ret = iwm_send_packet(iwm, skb, pool_id);
-#else
-
if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE)
iwm_tx_send_concat_packets(iwm, txq);
@@ -393,14 +419,21 @@ void iwm_tx_worker(struct work_struct *work)
if (ret) {
IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue "
"%d, Tx worker stopped\n", txq->id);
+ spin_lock_bh(&txq->lock);
skb_queue_head(&txq->queue, skb);
+ spin_unlock_bh(&txq->lock);
+
+ mutex_unlock(&tid_info->mutex);
break;
}
txq->concat_ptr = txq->concat_buf + txq->concat_count;
- iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr);
+ tid_info->last_seq_num =
+ iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr);
txq->concat_count += ALIGN(cmdlen, 16);
-#endif
+
+ mutex_unlock(&tid_info->mutex);
+
kfree_skb(skb);
}
@@ -419,14 +452,14 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
struct iwm_priv *iwm = ndev_to_iwm(netdev);
struct net_device *ndev = iwm_to_ndev(iwm);
struct wireless_dev *wdev = iwm_to_wdev(iwm);
- u8 *dst_addr;
struct iwm_tx_info *tx_info;
struct iwm_tx_queue *txq;
struct iwm_sta_info *sta_info;
- u8 sta_id;
+ u8 *dst_addr, sta_id;
u16 queue;
int ret;
+
if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: "
"not associated\n");
@@ -440,7 +473,8 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
txq = &iwm->txq[queue];
/* No free space for Tx, tx_worker is too slow */
- if (skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) {
+ if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) ||
+ (skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) {
IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue);
netif_stop_subqueue(netdev, queue);
return NETDEV_TX_BUSY;
@@ -477,7 +511,9 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
else
tx_info->tid = IWM_UMAC_MGMT_TID;
+ spin_lock_bh(&iwm->txq[queue].lock);
skb_queue_tail(&iwm->txq[queue].queue, skb);
+ spin_unlock_bh(&iwm->txq[queue].lock);
queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h
index c5a14ae3160..7f54a145ca6 100644
--- a/drivers/net/wireless/iwmc3200wifi/umac.h
+++ b/drivers/net/wireless/iwmc3200wifi/umac.h
@@ -83,6 +83,20 @@ struct iwm_udma_out_wifi_hdr {
((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
(UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
+/* STA ID and color */
+#define STA_ID_SEED (0x0f)
+#define STA_ID_POS (0)
+#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS)
+
+#define STA_COLOR_SEED (0x7)
+#define STA_COLOR_POS (4)
+#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS)
+
+#define STA_ID_N_COLOR_COLOR(id_n_color) \
+ (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
+#define STA_ID_N_COLOR_ID(id_n_color) \
+ (((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
+
/* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */
#define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0
#define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF
@@ -260,6 +274,9 @@ struct iwm_udma_out_wifi_hdr {
#define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16
#define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17
#define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18
+#define UMAC_CMD_OPCODE_STOP_RESUME_STA_TX 0x19
+#define UMAC_CMD_OPCODE_TEST_BLOCK_ACK 0x1A
+
#define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA
#define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB
#define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC
@@ -281,6 +298,7 @@ struct iwm_udma_out_wifi_hdr {
#define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B
#define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C
#define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E
+#define UMAC_WIFI_IF_CMD_PMKID_UPDATE 0x1F
#define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20
/* UMAC WiFi interface ports */
@@ -687,19 +705,24 @@ struct iwm_umac_notif_rx_ticket {
/* Tx/Rx rates window (number of max of last update window per second) */
#define UMAC_NTF_RATE_SAMPLE_NR 4
+/* Max numbers of bits required to go through all antennae in bitmasks */
+#define UMAC_PHY_NUM_CHAINS 3
+
#define IWM_UMAC_MGMT_TID 8
-#define IWM_UMAC_TID_NR 8
+#define IWM_UMAC_TID_NR 9 /* 8 TIDs + MGMT */
struct iwm_umac_notif_stats {
struct iwm_umac_wifi_in_hdr hdr;
__le32 flags;
__le32 timestamp;
- __le16 tid_load[IWM_UMAC_TID_NR + 2]; /* 1 non-QoS + 1 dword align */
+ __le16 tid_load[IWM_UMAC_TID_NR + 1]; /* 1 non-QoS + 1 dword align */
__le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR];
__le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR];
+ __le32 chain_energy[UMAC_PHY_NUM_CHAINS];
s32 rssi_dbm;
s32 noise_dbm;
__le32 supp_rates;
+ __le32 supp_ht_rates;
__le32 missed_beacons;
__le32 rx_beacons;
__le32 rx_dir_pkts;
@@ -737,6 +760,20 @@ struct iwm_umac_notif_stats {
__le32 roam_ap_loadblance;
} __attribute__ ((packed));
+#define UMAC_STOP_TX_FLAG 0x1
+#define UMAC_RESUME_TX_FLAG 0x2
+
+#define LAST_SEQ_NUM_INVALID 0xFFFF
+
+struct iwm_umac_notif_stop_resume_tx {
+ struct iwm_umac_wifi_in_hdr hdr;
+ u8 flags; /* UMAC_*_TX_FLAG_* */
+ u8 sta_id;
+ __le16 stop_resume_tid_msk; /* tid bitmask */
+} __attribute__ ((packed));
+
+#define UMAC_MAX_NUM_PMKIDS 4
+
/* WiFi interface wrapper header */
struct iwm_umac_wifi_if {
u8 oid;
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c
deleted file mode 100644
index 5c6968101f0..00000000000
--- a/drivers/net/wireless/libertas/11d.c
+++ /dev/null
@@ -1,696 +0,0 @@
-/**
- * This file contains functions for 802.11D.
- */
-#include <linux/ctype.h>
-#include <linux/kernel.h>
-#include <linux/wireless.h>
-
-#include "host.h"
-#include "decl.h"
-#include "11d.h"
-#include "dev.h"
-#include "wext.h"
-
-#define TX_PWR_DEFAULT 10
-
-static struct region_code_mapping region_code_mapping[] = {
- {"US ", 0x10}, /* US FCC */
- {"CA ", 0x10}, /* IC Canada */
- {"SG ", 0x10}, /* Singapore */
- {"EU ", 0x30}, /* ETSI */
- {"AU ", 0x30}, /* Australia */
- {"KR ", 0x30}, /* Republic Of Korea */
- {"ES ", 0x31}, /* Spain */
- {"FR ", 0x32}, /* France */
- {"JP ", 0x40}, /* Japan */
-};
-
-/* Following 2 structure defines the supported channels */
-static struct chan_freq_power channel_freq_power_UN_BG[] = {
- {1, 2412, TX_PWR_DEFAULT},
- {2, 2417, TX_PWR_DEFAULT},
- {3, 2422, TX_PWR_DEFAULT},
- {4, 2427, TX_PWR_DEFAULT},
- {5, 2432, TX_PWR_DEFAULT},
- {6, 2437, TX_PWR_DEFAULT},
- {7, 2442, TX_PWR_DEFAULT},
- {8, 2447, TX_PWR_DEFAULT},
- {9, 2452, TX_PWR_DEFAULT},
- {10, 2457, TX_PWR_DEFAULT},
- {11, 2462, TX_PWR_DEFAULT},
- {12, 2467, TX_PWR_DEFAULT},
- {13, 2472, TX_PWR_DEFAULT},
- {14, 2484, TX_PWR_DEFAULT}
-};
-
-static u8 lbs_region_2_code(u8 *region)
-{
- u8 i;
-
- for (i = 0; i < COUNTRY_CODE_LEN && region[i]; i++)
- region[i] = toupper(region[i]);
-
- for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) {
- if (!memcmp(region, region_code_mapping[i].region,
- COUNTRY_CODE_LEN))
- return (region_code_mapping[i].code);
- }
-
- /* default is US */
- return (region_code_mapping[0].code);
-}
-
-static u8 *lbs_code_2_region(u8 code)
-{
- u8 i;
-
- for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) {
- if (region_code_mapping[i].code == code)
- return (region_code_mapping[i].region);
- }
- /* default is US */
- return (region_code_mapping[0].region);
-}
-
-/**
- * @brief This function finds the nrchan-th chan after the firstchan
- * @param band band
- * @param firstchan first channel number
- * @param nrchan number of channels
- * @return the nrchan-th chan number
-*/
-static u8 lbs_get_chan_11d(u8 firstchan, u8 nrchan, u8 *chan)
-/*find the nrchan-th chan after the firstchan*/
-{
- u8 i;
- struct chan_freq_power *cfp;
- u8 cfp_no;
-
- cfp = channel_freq_power_UN_BG;
- cfp_no = ARRAY_SIZE(channel_freq_power_UN_BG);
-
- for (i = 0; i < cfp_no; i++) {
- if ((cfp + i)->channel == firstchan) {
- lbs_deb_11d("firstchan found\n");
- break;
- }
- }
-
- if (i < cfp_no) {
- /*if beyond the boundary */
- if (i + nrchan < cfp_no) {
- *chan = (cfp + i + nrchan)->channel;
- return 1;
- }
- }
-
- return 0;
-}
-
-/**
- * @brief This function Checks if chan txpwr is learned from AP/IBSS
- * @param chan chan number
- * @param parsed_region_chan pointer to parsed_region_chan_11d
- * @return TRUE; FALSE
-*/
-static u8 lbs_channel_known_11d(u8 chan,
- struct parsed_region_chan_11d * parsed_region_chan)
-{
- struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr;
- u8 nr_chan = parsed_region_chan->nr_chan;
- u8 i = 0;
-
- lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)chanpwr,
- sizeof(struct chan_power_11d) * nr_chan);
-
- for (i = 0; i < nr_chan; i++) {
- if (chan == chanpwr[i].chan) {
- lbs_deb_11d("found chan %d\n", chan);
- return 1;
- }
- }
-
- lbs_deb_11d("chan %d not found\n", chan);
- return 0;
-}
-
-u32 lbs_chan_2_freq(u8 chan)
-{
- struct chan_freq_power *cf;
- u16 i;
- u32 freq = 0;
-
- cf = channel_freq_power_UN_BG;
-
- for (i = 0; i < ARRAY_SIZE(channel_freq_power_UN_BG); i++) {
- if (chan == cf[i].channel)
- freq = cf[i].freq;
- }
-
- return freq;
-}
-
-static int generate_domain_info_11d(struct parsed_region_chan_11d
- *parsed_region_chan,
- struct lbs_802_11d_domain_reg *domaininfo)
-{
- u8 nr_subband = 0;
-
- u8 nr_chan = parsed_region_chan->nr_chan;
- u8 nr_parsedchan = 0;
-
- u8 firstchan = 0, nextchan = 0, maxpwr = 0;
-
- u8 i, flag = 0;
-
- memcpy(domaininfo->countrycode, parsed_region_chan->countrycode,
- COUNTRY_CODE_LEN);
-
- lbs_deb_11d("nrchan %d\n", nr_chan);
- lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)parsed_region_chan,
- sizeof(struct parsed_region_chan_11d));
-
- for (i = 0; i < nr_chan; i++) {
- if (!flag) {
- flag = 1;
- nextchan = firstchan =
- parsed_region_chan->chanpwr[i].chan;
- maxpwr = parsed_region_chan->chanpwr[i].pwr;
- nr_parsedchan = 1;
- continue;
- }
-
- if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 &&
- parsed_region_chan->chanpwr[i].pwr == maxpwr) {
- nextchan++;
- nr_parsedchan++;
- } else {
- domaininfo->subband[nr_subband].firstchan = firstchan;
- domaininfo->subband[nr_subband].nrchan =
- nr_parsedchan;
- domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
- nr_subband++;
- nextchan = firstchan =
- parsed_region_chan->chanpwr[i].chan;
- maxpwr = parsed_region_chan->chanpwr[i].pwr;
- }
- }
-
- if (flag) {
- domaininfo->subband[nr_subband].firstchan = firstchan;
- domaininfo->subband[nr_subband].nrchan = nr_parsedchan;
- domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
- nr_subband++;
- }
- domaininfo->nr_subband = nr_subband;
-
- lbs_deb_11d("nr_subband=%x\n", domaininfo->nr_subband);
- lbs_deb_hex(LBS_DEB_11D, "domaininfo", (char *)domaininfo,
- COUNTRY_CODE_LEN + 1 +
- sizeof(struct ieee_subbandset) * nr_subband);
- return 0;
-}
-
-/**
- * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS
- * @param region_chan pointer to struct region_channel
- * @param *parsed_region_chan pointer to parsed_region_chan_11d
- * @return N/A
-*/
-static void lbs_generate_parsed_region_chan_11d(struct region_channel *region_chan,
- struct parsed_region_chan_11d *
- parsed_region_chan)
-{
- u8 i;
- struct chan_freq_power *cfp;
-
- if (region_chan == NULL) {
- lbs_deb_11d("region_chan is NULL\n");
- return;
- }
-
- cfp = region_chan->CFP;
- if (cfp == NULL) {
- lbs_deb_11d("cfp is NULL \n");
- return;
- }
-
- parsed_region_chan->band = region_chan->band;
- parsed_region_chan->region = region_chan->region;
- memcpy(parsed_region_chan->countrycode,
- lbs_code_2_region(region_chan->region), COUNTRY_CODE_LEN);
-
- lbs_deb_11d("region 0x%x, band %d\n", parsed_region_chan->region,
- parsed_region_chan->band);
-
- for (i = 0; i < region_chan->nrcfp; i++, cfp++) {
- parsed_region_chan->chanpwr[i].chan = cfp->channel;
- parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower;
- lbs_deb_11d("chan %d, pwr %d\n",
- parsed_region_chan->chanpwr[i].chan,
- parsed_region_chan->chanpwr[i].pwr);
- }
- parsed_region_chan->nr_chan = region_chan->nrcfp;
-
- lbs_deb_11d("nrchan %d\n", parsed_region_chan->nr_chan);
-
- return;
-}
-
-/**
- * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS
- * @param region region ID
- * @param band band
- * @param chan chan
- * @return TRUE;FALSE
-*/
-static u8 lbs_region_chan_supported_11d(u8 region, u8 chan)
-{
- struct chan_freq_power *cfp;
- int cfp_no;
- u8 idx;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- cfp = lbs_get_region_cfp_table(region, &cfp_no);
- if (cfp == NULL)
- return 0;
-
- for (idx = 0; idx < cfp_no; idx++) {
- if (chan == (cfp + idx)->channel) {
- /* If Mrvl Chip Supported? */
- if ((cfp + idx)->unsupported) {
- ret = 0;
- } else {
- ret = 1;
- }
- goto done;
- }
- }
-
- /*chan is not in the region table */
-
-done:
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief This function checks if chan txpwr is learned from AP/IBSS
- * @param chan chan number
- * @param parsed_region_chan pointer to parsed_region_chan_11d
- * @return 0
-*/
-static int parse_domain_info_11d(struct ieee_ie_country_info_full_set *countryinfo,
- u8 band,
- struct parsed_region_chan_11d *parsed_region_chan)
-{
- u8 nr_subband, nrchan;
- u8 lastchan, firstchan;
- u8 region;
- u8 curchan = 0;
-
- u8 idx = 0; /*chan index in parsed_region_chan */
-
- u8 j, i;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- /*validation Rules:
- 1. valid region Code
- 2. First Chan increment
- 3. channel range no overlap
- 4. channel is valid?
- 5. channel is supported by region?
- 6. Others
- */
-
- lbs_deb_hex(LBS_DEB_11D, "countryinfo", (u8 *) countryinfo, 30);
-
- if ((*(countryinfo->countrycode)) == 0
- || (countryinfo->header.len <= COUNTRY_CODE_LEN)) {
- /* No region Info or Wrong region info: treat as No 11D info */
- goto done;
- }
-
- /*Step1: check region_code */
- parsed_region_chan->region = region =
- lbs_region_2_code(countryinfo->countrycode);
-
- lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region);
- lbs_deb_hex(LBS_DEB_11D, "countrycode", (char *)countryinfo->countrycode,
- COUNTRY_CODE_LEN);
-
- parsed_region_chan->band = band;
-
- memcpy(parsed_region_chan->countrycode, countryinfo->countrycode,
- COUNTRY_CODE_LEN);
-
- nr_subband = (countryinfo->header.len - COUNTRY_CODE_LEN) /
- sizeof(struct ieee_subbandset);
-
- for (j = 0, lastchan = 0; j < nr_subband; j++) {
-
- if (countryinfo->subband[j].firstchan <= lastchan) {
- /*Step2&3. Check First Chan Num increment and no overlap */
- lbs_deb_11d("chan %d>%d, overlap\n",
- countryinfo->subband[j].firstchan, lastchan);
- continue;
- }
-
- firstchan = countryinfo->subband[j].firstchan;
- nrchan = countryinfo->subband[j].nrchan;
-
- for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) {
- /*step4: channel is supported? */
-
- if (!lbs_get_chan_11d(firstchan, i, &curchan)) {
- /* Chan is not found in UN table */
- lbs_deb_11d("chan is not supported: %d \n", i);
- break;
- }
-
- lastchan = curchan;
-
- if (lbs_region_chan_supported_11d(region, curchan)) {
- /*step5: Check if curchan is supported by mrvl in region */
- parsed_region_chan->chanpwr[idx].chan = curchan;
- parsed_region_chan->chanpwr[idx].pwr =
- countryinfo->subband[j].maxtxpwr;
- idx++;
- } else {
- /*not supported and ignore the chan */
- lbs_deb_11d(
- "i %d, chan %d unsupported in region %x, band %d\n",
- i, curchan, region, band);
- }
- }
-
- /*Step6: Add other checking if any */
-
- }
-
- parsed_region_chan->nr_chan = idx;
-
- lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan);
- lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (u8 *) parsed_region_chan,
- 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx);
-
-done:
- lbs_deb_enter(LBS_DEB_11D);
- return 0;
-}
-
-/**
- * @brief This function calculates the scan type for channels
- * @param chan chan number
- * @param parsed_region_chan pointer to parsed_region_chan_11d
- * @return PASSIVE if chan is unknown; ACTIVE if chan is known
-*/
-u8 lbs_get_scan_type_11d(u8 chan,
- struct parsed_region_chan_11d * parsed_region_chan)
-{
- u8 scan_type = CMD_SCAN_TYPE_PASSIVE;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- if (lbs_channel_known_11d(chan, parsed_region_chan)) {
- lbs_deb_11d("found, do active scan\n");
- scan_type = CMD_SCAN_TYPE_ACTIVE;
- } else {
- lbs_deb_11d("not found, do passive scan\n");
- }
-
- lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type);
- return scan_type;
-
-}
-
-void lbs_init_11d(struct lbs_private *priv)
-{
- priv->enable11d = 0;
- memset(&(priv->parsed_region_chan), 0,
- sizeof(struct parsed_region_chan_11d));
- return;
-}
-
-/**
- * @brief This function sets DOMAIN INFO to FW
- * @param priv pointer to struct lbs_private
- * @return 0; -1
-*/
-static int set_domain_info_11d(struct lbs_private *priv)
-{
- int ret;
-
- if (!priv->enable11d) {
- lbs_deb_11d("dnld domain Info with 11d disabled\n");
- return 0;
- }
-
- ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO,
- CMD_ACT_SET,
- CMD_OPTION_WAITFORRSP, 0, NULL);
- if (ret)
- lbs_deb_11d("fail to dnld domain info\n");
-
- return ret;
-}
-
-/**
- * @brief This function setups scan channels
- * @param priv pointer to struct lbs_private
- * @param band band
- * @return 0
-*/
-int lbs_set_universaltable(struct lbs_private *priv, u8 band)
-{
- u16 size = sizeof(struct chan_freq_power);
- u16 i = 0;
-
- memset(priv->universal_channel, 0,
- sizeof(priv->universal_channel));
-
- priv->universal_channel[i].nrcfp =
- sizeof(channel_freq_power_UN_BG) / size;
- lbs_deb_11d("BG-band nrcfp %d\n",
- priv->universal_channel[i].nrcfp);
-
- priv->universal_channel[i].CFP = channel_freq_power_UN_BG;
- priv->universal_channel[i].valid = 1;
- priv->universal_channel[i].region = UNIVERSAL_REGION_CODE;
- priv->universal_channel[i].band = band;
- i++;
-
- return 0;
-}
-
-/**
- * @brief This function implements command CMD_802_11D_DOMAIN_INFO
- * @param priv pointer to struct lbs_private
- * @param cmd pointer to cmd buffer
- * @param cmdno cmd ID
- * @param cmdOption cmd action
- * @return 0
-*/
-int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
- struct cmd_ds_command *cmd, u16 cmdno,
- u16 cmdoption)
-{
- struct cmd_ds_802_11d_domain_info *pdomaininfo =
- &cmd->params.domaininfo;
- struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain;
- u8 nr_subband = priv->domainreg.nr_subband;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- lbs_deb_11d("nr_subband=%x\n", nr_subband);
-
- cmd->command = cpu_to_le16(cmdno);
- pdomaininfo->action = cpu_to_le16(cmdoption);
- if (cmdoption == CMD_ACT_GET) {
- cmd->size =
- cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
- lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
- le16_to_cpu(cmd->size));
- goto done;
- }
-
- domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
- memcpy(domain->countrycode, priv->domainreg.countrycode,
- sizeof(domain->countrycode));
-
- domain->header.len =
- cpu_to_le16(nr_subband * sizeof(struct ieee_subbandset) +
- sizeof(domain->countrycode));
-
- if (nr_subband) {
- memcpy(domain->subband, priv->domainreg.subband,
- nr_subband * sizeof(struct ieee_subbandset));
-
- cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
- le16_to_cpu(domain->header.len) +
- sizeof(struct mrvl_ie_header) +
- S_DS_GEN);
- } else {
- cmd->size =
- cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
- }
-
- lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size));
-
-done:
- lbs_deb_enter(LBS_DEB_11D);
- return 0;
-}
-
-/**
- * @brief This function parses countryinfo from AP and download country info to FW
- * @param priv pointer to struct lbs_private
- * @param resp pointer to command response buffer
- * @return 0; -1
- */
-int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp)
-{
- struct cmd_ds_802_11d_domain_info *domaininfo = &resp->params.domaininforesp;
- struct mrvl_ie_domain_param_set *domain = &domaininfo->domain;
- u16 action = le16_to_cpu(domaininfo->action);
- s16 ret = 0;
- u8 nr_subband = 0;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp,
- (int)le16_to_cpu(resp->size));
-
- nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) /
- sizeof(struct ieee_subbandset);
-
- lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband);
-
- if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) {
- lbs_deb_11d("Invalid Numrer of Subband returned!!\n");
- return -1;
- }
-
- switch (action) {
- case CMD_ACT_SET: /*Proc Set action */
- break;
-
- case CMD_ACT_GET:
- break;
- default:
- lbs_deb_11d("Invalid action:%d\n", domaininfo->action);
- ret = -1;
- break;
- }
-
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief This function parses countryinfo from AP and download country info to FW
- * @param priv pointer to struct lbs_private
- * @return 0; -1
- */
-int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv,
- struct bss_descriptor * bss)
-{
- int ret;
-
- lbs_deb_enter(LBS_DEB_11D);
- if (priv->enable11d) {
- memset(&priv->parsed_region_chan, 0,
- sizeof(struct parsed_region_chan_11d));
- ret = parse_domain_info_11d(&bss->countryinfo, 0,
- &priv->parsed_region_chan);
-
- if (ret == -1) {
- lbs_deb_11d("error parsing domain_info from AP\n");
- goto done;
- }
-
- memset(&priv->domainreg, 0,
- sizeof(struct lbs_802_11d_domain_reg));
- generate_domain_info_11d(&priv->parsed_region_chan,
- &priv->domainreg);
-
- ret = set_domain_info_11d(priv);
-
- if (ret) {
- lbs_deb_11d("error setting domain info\n");
- goto done;
- }
- }
- ret = 0;
-
-done:
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
-
-/**
- * @brief This function generates 11D info from user specified regioncode and download to FW
- * @param priv pointer to struct lbs_private
- * @return 0; -1
- */
-int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv)
-{
- int ret;
- struct region_channel *region_chan;
- u8 j;
-
- lbs_deb_enter(LBS_DEB_11D);
- lbs_deb_11d("curbssparams.band %d\n", priv->curbssparams.band);
-
- if (priv->enable11d) {
- /* update parsed_region_chan_11; dnld domaininf to FW */
-
- for (j = 0; j < ARRAY_SIZE(priv->region_channel); j++) {
- region_chan = &priv->region_channel[j];
-
- lbs_deb_11d("%d region_chan->band %d\n", j,
- region_chan->band);
-
- if (!region_chan || !region_chan->valid
- || !region_chan->CFP)
- continue;
- if (region_chan->band != priv->curbssparams.band)
- continue;
- break;
- }
-
- if (j >= ARRAY_SIZE(priv->region_channel)) {
- lbs_deb_11d("region_chan not found, band %d\n",
- priv->curbssparams.band);
- ret = -1;
- goto done;
- }
-
- memset(&priv->parsed_region_chan, 0,
- sizeof(struct parsed_region_chan_11d));
- lbs_generate_parsed_region_chan_11d(region_chan,
- &priv->
- parsed_region_chan);
-
- memset(&priv->domainreg, 0,
- sizeof(struct lbs_802_11d_domain_reg));
- generate_domain_info_11d(&priv->parsed_region_chan,
- &priv->domainreg);
-
- ret = set_domain_info_11d(priv);
-
- if (ret) {
- lbs_deb_11d("error setting domain info\n");
- goto done;
- }
-
- }
- ret = 0;
-
-done:
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h
deleted file mode 100644
index fb75d3e321a..00000000000
--- a/drivers/net/wireless/libertas/11d.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * This header file contains data structures and
- * function declarations of 802.11d
- */
-#ifndef _LBS_11D_
-#define _LBS_11D_
-
-#include "types.h"
-#include "defs.h"
-
-#define UNIVERSAL_REGION_CODE 0xff
-
-/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr)
- */
-#define MRVDRV_MAX_SUBBAND_802_11D 83
-
-#define COUNTRY_CODE_LEN 3
-#define MAX_NO_OF_CHAN 40
-
-struct cmd_ds_command;
-
-/** Data structure for Country IE*/
-struct ieee_subbandset {
- u8 firstchan;
- u8 nrchan;
- u8 maxtxpwr;
-} __attribute__ ((packed));
-
-struct ieee_ie_country_info_set {
- struct ieee_ie_header header;
-
- u8 countrycode[COUNTRY_CODE_LEN];
- struct ieee_subbandset subband[1];
-};
-
-struct ieee_ie_country_info_full_set {
- struct ieee_ie_header header;
-
- u8 countrycode[COUNTRY_CODE_LEN];
- struct ieee_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D];
-} __attribute__ ((packed));
-
-struct mrvl_ie_domain_param_set {
- struct mrvl_ie_header header;
-
- u8 countrycode[COUNTRY_CODE_LEN];
- struct ieee_subbandset subband[1];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11d_domain_info {
- __le16 action;
- struct mrvl_ie_domain_param_set domain;
-} __attribute__ ((packed));
-
-/** domain regulatory information */
-struct lbs_802_11d_domain_reg {
- /** country Code*/
- u8 countrycode[COUNTRY_CODE_LEN];
- /** No. of subband*/
- u8 nr_subband;
- struct ieee_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D];
-};
-
-struct chan_power_11d {
- u8 chan;
- u8 pwr;
-} __attribute__ ((packed));
-
-struct parsed_region_chan_11d {
- u8 band;
- u8 region;
- s8 countrycode[COUNTRY_CODE_LEN];
- struct chan_power_11d chanpwr[MAX_NO_OF_CHAN];
- u8 nr_chan;
-} __attribute__ ((packed));
-
-struct region_code_mapping {
- u8 region[COUNTRY_CODE_LEN];
- u8 code;
-};
-
-struct lbs_private;
-
-u8 lbs_get_scan_type_11d(u8 chan,
- struct parsed_region_chan_11d *parsed_region_chan);
-
-u32 lbs_chan_2_freq(u8 chan);
-
-void lbs_init_11d(struct lbs_private *priv);
-
-int lbs_set_universaltable(struct lbs_private *priv, u8 band);
-
-int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
- struct cmd_ds_command *cmd, u16 cmdno,
- u16 cmdOption);
-
-int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp);
-
-struct bss_descriptor;
-int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv,
- struct bss_descriptor * bss);
-
-int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv);
-
-#endif
diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig
new file mode 100644
index 00000000000..30aa9d48d67
--- /dev/null
+++ b/drivers/net/wireless/libertas/Kconfig
@@ -0,0 +1,39 @@
+config LIBERTAS
+ tristate "Marvell 8xxx Libertas WLAN driver support"
+ depends on CFG80211
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select LIB80211
+ select FW_LOADER
+ ---help---
+ A library for Marvell Libertas 8xxx devices.
+
+config LIBERTAS_USB
+ tristate "Marvell Libertas 8388 USB 802.11b/g cards"
+ depends on LIBERTAS && USB
+ ---help---
+ A driver for Marvell Libertas 8388 USB devices.
+
+config LIBERTAS_CS
+ tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards"
+ depends on LIBERTAS && PCMCIA
+ ---help---
+ A driver for Marvell Libertas 8385 CompactFlash devices.
+
+config LIBERTAS_SDIO
+ tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards"
+ depends on LIBERTAS && MMC
+ ---help---
+ A driver for Marvell Libertas 8385/8686/8688 SDIO devices.
+
+config LIBERTAS_SPI
+ tristate "Marvell Libertas 8686 SPI 802.11b/g cards"
+ depends on LIBERTAS && SPI
+ ---help---
+ A driver for Marvell Libertas 8686 SPI devices.
+
+config LIBERTAS_DEBUG
+ bool "Enable full debugging output in the Libertas module."
+ depends on LIBERTAS
+ ---help---
+ Debugging support.
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
index 0b691858450..b188cd97a05 100644
--- a/drivers/net/wireless/libertas/Makefile
+++ b/drivers/net/wireless/libertas/Makefile
@@ -1,5 +1,15 @@
-libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o \
- debugfs.o persistcfg.o ethtool.o assoc.o
+libertas-y += assoc.o
+libertas-y += cfg.o
+libertas-y += cmd.o
+libertas-y += cmdresp.o
+libertas-y += debugfs.o
+libertas-y += ethtool.o
+libertas-y += main.o
+libertas-y += mesh.o
+libertas-y += rx.o
+libertas-y += scan.o
+libertas-y += tx.o
+libertas-y += wext.o
usb8xxx-objs += if_usb.o
libertas_cs-objs += if_cs.o
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
index ab6a2d518af..2726c044430 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/libertas/README
@@ -1,5 +1,5 @@
================================================================================
- README for USB8388
+ README for Libertas
(c) Copyright © 2003-2006, Marvell International Ltd.
All Rights Reserved
@@ -226,4 +226,28 @@ setuserscan
All entries in the scan table (not just the new scan data when keep=1)
will be displayed upon completion by use of the getscantable ioctl.
+========================
+IWCONFIG COMMANDS
+========================
+power period
+
+ This command is used to configure the station in deep sleep mode /
+ auto deep sleep mode.
+
+ The timer is implemented to monitor the activities (command, event,
+ etc.). When an activity is detected station will exit from deep
+ sleep mode automatically and restart the timer. At timer expiry
+ (no activity for defined time period) the deep sleep mode is entered
+ automatically.
+
+ Note: this command is for SDIO interface only.
+
+ Usage:
+ To enable deep sleep mode do:
+ iwconfig wlan0 power period 0
+ To enable auto deep sleep mode with idle time period 5 seconds do:
+ iwconfig wlan0 power period 5
+ To disable deep sleep/auto deep sleep mode do:
+ iwconfig wlan0 power period -1
+
==============================================================================
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
index dd8732611ba..751067369ba 100644
--- a/drivers/net/wireless/libertas/assoc.c
+++ b/drivers/net/wireless/libertas/assoc.c
@@ -23,6 +23,13 @@ static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) =
*/
#define CAPINFO_MASK (~(0xda00))
+/**
+ * 802.11b/g supported bitrates (in 500Kb/s units)
+ */
+u8 lbs_bg_rates[MAX_RATES] =
+ { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
+0x00, 0x00 };
+
/**
* @brief This function finds common rates between rates and card rates.
@@ -147,6 +154,397 @@ static int lbs_set_authentication(struct lbs_private *priv, u8 bssid[6], u8 auth
}
+int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
+ struct assoc_request *assoc)
+{
+ struct cmd_ds_802_11_set_wep cmd;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.command = cpu_to_le16(CMD_802_11_SET_WEP);
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ cmd.action = cpu_to_le16(cmd_action);
+
+ if (cmd_action == CMD_ACT_ADD) {
+ int i;
+
+ /* default tx key index */
+ cmd.keyindex = cpu_to_le16(assoc->wep_tx_keyidx &
+ CMD_WEP_KEY_INDEX_MASK);
+
+ /* Copy key types and material to host command structure */
+ for (i = 0; i < 4; i++) {
+ struct enc_key *pkey = &assoc->wep_keys[i];
+
+ switch (pkey->len) {
+ case KEY_LEN_WEP_40:
+ cmd.keytype[i] = CMD_TYPE_WEP_40_BIT;
+ memmove(cmd.keymaterial[i], pkey->key, pkey->len);
+ lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i);
+ break;
+ case KEY_LEN_WEP_104:
+ cmd.keytype[i] = CMD_TYPE_WEP_104_BIT;
+ memmove(cmd.keymaterial[i], pkey->key, pkey->len);
+ lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i);
+ break;
+ case 0:
+ break;
+ default:
+ lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n",
+ i, pkey->len);
+ ret = -1;
+ goto done;
+ break;
+ }
+ }
+ } else if (cmd_action == CMD_ACT_REMOVE) {
+ /* ACT_REMOVE clears _all_ WEP keys */
+
+ /* default tx key index */
+ cmd.keyindex = cpu_to_le16(priv->wep_tx_keyidx &
+ CMD_WEP_KEY_INDEX_MASK);
+ lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx);
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
+done:
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
+ uint16_t *enable)
+{
+ struct cmd_ds_802_11_enable_rsn cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(cmd_action);
+
+ if (cmd_action == CMD_ACT_GET)
+ cmd.enable = 0;
+ else {
+ if (*enable)
+ cmd.enable = cpu_to_le16(CMD_ENABLE_RSN);
+ else
+ cmd.enable = cpu_to_le16(CMD_DISABLE_RSN);
+ lbs_deb_cmd("ENABLE_RSN: %d\n", *enable);
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
+ if (!ret && cmd_action == CMD_ACT_GET)
+ *enable = le16_to_cpu(cmd.enable);
+
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam,
+ struct enc_key *key)
+{
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (key->flags & KEY_INFO_WPA_ENABLED)
+ keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
+ if (key->flags & KEY_INFO_WPA_UNICAST)
+ keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
+ if (key->flags & KEY_INFO_WPA_MCAST)
+ keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
+
+ keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
+ keyparam->keytypeid = cpu_to_le16(key->type);
+ keyparam->keylen = cpu_to_le16(key->len);
+ memcpy(keyparam->key, key->key, key->len);
+
+ /* Length field doesn't include the {type,length} header */
+ keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4);
+ lbs_deb_leave(LBS_DEB_CMD);
+}
+
+int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
+ struct assoc_request *assoc)
+{
+ struct cmd_ds_802_11_key_material cmd;
+ int ret = 0;
+ int index = 0;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ cmd.action = cpu_to_le16(cmd_action);
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ if (cmd_action == CMD_ACT_GET) {
+ cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_header) + 2);
+ } else {
+ memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet));
+
+ if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) {
+ set_one_wpa_key(&cmd.keyParamSet[index],
+ &assoc->wpa_unicast_key);
+ index++;
+ }
+
+ if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) {
+ set_one_wpa_key(&cmd.keyParamSet[index],
+ &assoc->wpa_mcast_key);
+ index++;
+ }
+
+ /* The common header and as many keys as we included */
+ cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd),
+ keyParamSet[index]));
+ }
+ ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
+ /* Copy the returned key to driver private data */
+ if (!ret && cmd_action == CMD_ACT_GET) {
+ void *buf_ptr = cmd.keyParamSet;
+ void *resp_end = &(&cmd)[1];
+
+ while (buf_ptr < resp_end) {
+ struct MrvlIEtype_keyParamSet *keyparam = buf_ptr;
+ struct enc_key *key;
+ uint16_t param_set_len = le16_to_cpu(keyparam->length);
+ uint16_t key_len = le16_to_cpu(keyparam->keylen);
+ uint16_t key_flags = le16_to_cpu(keyparam->keyinfo);
+ uint16_t key_type = le16_to_cpu(keyparam->keytypeid);
+ void *end;
+
+ end = (void *)keyparam + sizeof(keyparam->type)
+ + sizeof(keyparam->length) + param_set_len;
+
+ /* Make sure we don't access past the end of the IEs */
+ if (end > resp_end)
+ break;
+
+ if (key_flags & KEY_INFO_WPA_UNICAST)
+ key = &priv->wpa_unicast_key;
+ else if (key_flags & KEY_INFO_WPA_MCAST)
+ key = &priv->wpa_mcast_key;
+ else
+ break;
+
+ /* Copy returned key into driver */
+ memset(key, 0, sizeof(struct enc_key));
+ if (key_len > sizeof(key->key))
+ break;
+ key->type = key_type;
+ key->flags = key_flags;
+ key->len = key_len;
+ memcpy(key->key, keyparam->key, key->len);
+
+ buf_ptr = end + 1;
+ }
+ }
+
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+static __le16 lbs_rate_to_fw_bitmap(int rate, int lower_rates_ok)
+{
+/* Bit Rate
+* 15:13 Reserved
+* 12 54 Mbps
+* 11 48 Mbps
+* 10 36 Mbps
+* 9 24 Mbps
+* 8 18 Mbps
+* 7 12 Mbps
+* 6 9 Mbps
+* 5 6 Mbps
+* 4 Reserved
+* 3 11 Mbps
+* 2 5.5 Mbps
+* 1 2 Mbps
+* 0 1 Mbps
+**/
+
+ uint16_t ratemask;
+ int i = lbs_data_rate_to_fw_index(rate);
+ if (lower_rates_ok)
+ ratemask = (0x1fef >> (12 - i));
+ else
+ ratemask = (1 << i);
+ return cpu_to_le16(ratemask);
+}
+
+int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
+ uint16_t cmd_action)
+{
+ struct cmd_ds_802_11_rate_adapt_rateset cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (!priv->cur_rate && !priv->enablehwauto)
+ return -EINVAL;
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ cmd.action = cpu_to_le16(cmd_action);
+ cmd.enablehwauto = cpu_to_le16(priv->enablehwauto);
+ cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto);
+ ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd);
+ if (!ret && cmd_action == CMD_ACT_GET) {
+ priv->ratebitmap = le16_to_cpu(cmd.bitmap);
+ priv->enablehwauto = le16_to_cpu(cmd.enablehwauto);
+ }
+
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * @brief Set the data rate
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param rate The desired data rate, or 0 to clear a locked rate
+ *
+ * @return 0 on success, error on failure
+ */
+int lbs_set_data_rate(struct lbs_private *priv, u8 rate)
+{
+ struct cmd_ds_802_11_data_rate cmd;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ if (rate > 0) {
+ cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE);
+ cmd.rates[0] = lbs_data_rate_to_fw_index(rate);
+ if (cmd.rates[0] == 0) {
+ lbs_deb_cmd("DATA_RATE: invalid requested rate of"
+ " 0x%02X\n", rate);
+ ret = 0;
+ goto out;
+ }
+ lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]);
+ } else {
+ cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO);
+ lbs_deb_cmd("DATA_RATE: setting auto\n");
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, &cmd);
+ if (ret)
+ goto out;
+
+ lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof(cmd));
+
+ /* FIXME: get actual rates FW can do if this command actually returns
+ * all data rates supported.
+ */
+ priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]);
+ lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate);
+
+out:
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+
+int lbs_cmd_802_11_rssi(struct lbs_private *priv,
+ struct cmd_ds_command *cmd)
+{
+
+ lbs_deb_enter(LBS_DEB_CMD);
+ cmd->command = cpu_to_le16(CMD_802_11_RSSI);
+ cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) +
+ sizeof(struct cmd_header));
+ cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR);
+
+ /* reset Beacon SNR/NF/RSSI values */
+ priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
+ priv->SNR[TYPE_BEACON][TYPE_AVG] = 0;
+ priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
+ priv->NF[TYPE_BEACON][TYPE_AVG] = 0;
+ priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
+ priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+int lbs_ret_802_11_rssi(struct lbs_private *priv,
+ struct cmd_ds_command *resp)
+{
+ struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ /* store the non average value */
+ priv->SNR[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->SNR);
+ priv->NF[TYPE_BEACON][TYPE_NOAVG] =
+ get_unaligned_le16(&rssirsp->noisefloor);
+
+ priv->SNR[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgSNR);
+ priv->NF[TYPE_BEACON][TYPE_AVG] =
+ get_unaligned_le16(&rssirsp->avgnoisefloor);
+
+ priv->RSSI[TYPE_BEACON][TYPE_NOAVG] =
+ CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
+ priv->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+ priv->RSSI[TYPE_BEACON][TYPE_AVG] =
+ CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
+ priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
+
+ lbs_deb_cmd("RSSI: beacon %d, avg %d\n",
+ priv->RSSI[TYPE_BEACON][TYPE_NOAVG],
+ priv->RSSI[TYPE_BEACON][TYPE_AVG]);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+
+int lbs_cmd_bcn_ctrl(struct lbs_private *priv,
+ struct cmd_ds_command *cmd,
+ u16 cmd_action)
+{
+ struct cmd_ds_802_11_beacon_control
+ *bcn_ctrl = &cmd->params.bcn_ctrl;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+ cmd->size =
+ cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control)
+ + sizeof(struct cmd_header));
+ cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL);
+
+ bcn_ctrl->action = cpu_to_le16(cmd_action);
+ bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable);
+ bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+int lbs_ret_802_11_bcn_ctrl(struct lbs_private *priv,
+ struct cmd_ds_command *resp)
+{
+ struct cmd_ds_802_11_beacon_control *bcn_ctrl =
+ &resp->params.bcn_ctrl;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (bcn_ctrl->action == CMD_ACT_GET) {
+ priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable);
+ priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period);
+ }
+
+ lbs_deb_enter(LBS_DEB_CMD);
+ return 0;
+}
+
+
+
static int lbs_assoc_post(struct lbs_private *priv,
struct cmd_ds_802_11_associate_response *resp)
{
@@ -226,7 +624,7 @@ static int lbs_assoc_post(struct lbs_private *priv,
priv->connect_status = LBS_CONNECTED;
/* Update current SSID and BSSID */
- memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
+ memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN);
priv->curbssparams.ssid_len = bss->ssid_len;
memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
@@ -369,12 +767,7 @@ static int lbs_associate(struct lbs_private *priv,
(u16)(pos - (u8 *) &cmd.iebuf));
/* update curbssparams */
- priv->curbssparams.channel = bss->phy.ds.channel;
-
- if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
- ret = -1;
- goto done;
- }
+ priv->channel = bss->phy.ds.channel;
ret = lbs_cmd_with_response(priv, command, &cmd);
if (ret == 0) {
@@ -472,7 +865,7 @@ static int lbs_adhoc_post(struct lbs_private *priv,
memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
/* Set the new SSID to current SSID */
- memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
+ memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN);
priv->curbssparams.ssid_len = bss->ssid_len;
netif_carrier_on(priv->dev);
@@ -487,7 +880,7 @@ static int lbs_adhoc_post(struct lbs_private *priv,
lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %pM, channel %d\n",
print_ssid(ssid, bss->ssid, bss->ssid_len),
priv->curbssparams.bssid,
- priv->curbssparams.channel);
+ priv->channel);
done:
lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
@@ -560,7 +953,7 @@ static int lbs_adhoc_join(struct lbs_private *priv,
lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band);
priv->adhoccreate = 0;
- priv->curbssparams.channel = bss->channel;
+ priv->channel = bss->channel;
/* Build the join command */
memset(&cmd, 0, sizeof(cmd));
@@ -633,11 +1026,6 @@ static int lbs_adhoc_join(struct lbs_private *priv,
}
}
- if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
- ret = -1;
- goto out;
- }
-
ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd);
if (ret == 0) {
ret = lbs_adhoc_post(priv,
@@ -737,12 +1125,6 @@ static int lbs_adhoc_start(struct lbs_private *priv,
lbs_deb_join("ADHOC_START: rates=%02x %02x %02x %02x\n",
cmd.rates[0], cmd.rates[1], cmd.rates[2], cmd.rates[3]);
- if (lbs_create_dnld_countryinfo_11d(priv)) {
- lbs_deb_join("ADHOC_START: dnld_countryinfo_11d failed\n");
- ret = -1;
- goto out;
- }
-
lbs_deb_join("ADHOC_START: Starting Ad-Hoc BSS on channel %d, band %d\n",
assoc_req->channel, assoc_req->band);
@@ -1099,7 +1481,7 @@ static int assoc_helper_essid(struct lbs_private *priv,
/* else send START command */
lbs_deb_assoc("SSID not found, creating adhoc network\n");
memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
- IW_ESSID_MAX_SIZE);
+ IEEE80211_MAX_SSID_LEN);
assoc_req->bss.ssid_len = assoc_req->ssid_len;
lbs_adhoc_start(priv, assoc_req);
}
@@ -1185,7 +1567,8 @@ static int assoc_helper_mode(struct lbs_private *priv,
}
priv->mode = assoc_req->mode;
- ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, assoc_req->mode);
+ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE,
+ assoc_req->mode == IW_MODE_ADHOC ? 2 : 1);
done:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
@@ -1205,7 +1588,7 @@ static int assoc_helper_channel(struct lbs_private *priv,
goto done;
}
- if (assoc_req->channel == priv->curbssparams.channel)
+ if (assoc_req->channel == priv->channel)
goto done;
if (priv->mesh_dev) {
@@ -1217,7 +1600,7 @@ static int assoc_helper_channel(struct lbs_private *priv,
}
lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
- priv->curbssparams.channel, assoc_req->channel);
+ priv->channel, assoc_req->channel);
ret = lbs_set_channel(priv, assoc_req->channel);
if (ret < 0)
@@ -1232,7 +1615,7 @@ static int assoc_helper_channel(struct lbs_private *priv,
goto done;
}
- if (assoc_req->channel != priv->curbssparams.channel) {
+ if (assoc_req->channel != priv->channel) {
lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
assoc_req->channel);
goto restore_mesh;
@@ -1253,7 +1636,7 @@ static int assoc_helper_channel(struct lbs_private *priv,
restore_mesh:
if (priv->mesh_dev)
lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel);
+ priv->channel);
done:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
@@ -1475,7 +1858,7 @@ static int should_stop_adhoc(struct lbs_private *priv,
}
if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
- if (assoc_req->channel != priv->curbssparams.channel)
+ if (assoc_req->channel != priv->channel)
return 1;
}
@@ -1557,7 +1940,7 @@ static int lbs_find_best_network_ssid(struct lbs_private *priv,
found = lbs_find_best_ssid_in_list(priv, preferred_mode);
if (found && (found->ssid_len > 0)) {
- memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE);
+ memcpy(out_ssid, &found->ssid, IEEE80211_MAX_SSID_LEN);
*out_ssid_len = found->ssid_len;
*out_mode = found->mode;
ret = 0;
@@ -1775,12 +2158,12 @@ struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
assoc_req = priv->pending_assoc_req;
if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
- IW_ESSID_MAX_SIZE);
+ IEEE80211_MAX_SSID_LEN);
assoc_req->ssid_len = priv->curbssparams.ssid_len;
}
if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
- assoc_req->channel = priv->curbssparams.channel;
+ assoc_req->channel = priv->channel;
if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
assoc_req->band = priv->curbssparams.band;
diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h
index 6e765e9f91a..40621b789fc 100644
--- a/drivers/net/wireless/libertas/assoc.h
+++ b/drivers/net/wireless/libertas/assoc.h
@@ -3,7 +3,126 @@
#ifndef _LBS_ASSOC_H_
#define _LBS_ASSOC_H_
-#include "dev.h"
+
+#include "defs.h"
+#include "host.h"
+
+
+struct lbs_private;
+
+/*
+ * In theory, the IE is limited to the IE length, 255,
+ * but in practice 64 bytes are enough.
+ */
+#define MAX_WPA_IE_LEN 64
+
+
+
+struct lbs_802_11_security {
+ u8 WPAenabled;
+ u8 WPA2enabled;
+ u8 wep_enabled;
+ u8 auth_mode;
+ u32 key_mgmt;
+};
+
+/** Current Basic Service Set State Structure */
+struct current_bss_params {
+ /** bssid */
+ u8 bssid[ETH_ALEN];
+ /** ssid */
+ u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+ u8 ssid_len;
+
+ /** band */
+ u8 band;
+ /** channel is directly in priv->channel */
+ /** zero-terminated array of supported data rates */
+ u8 rates[MAX_RATES + 1];
+};
+
+/**
+ * @brief Structure used to store information for each beacon/probe response
+ */
+struct bss_descriptor {
+ u8 bssid[ETH_ALEN];
+
+ u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+ u8 ssid_len;
+
+ u16 capability;
+ u32 rssi;
+ u32 channel;
+ u16 beaconperiod;
+ __le16 atimwindow;
+
+ /* IW_MODE_AUTO, IW_MODE_ADHOC, IW_MODE_INFRA */
+ u8 mode;
+
+ /* zero-terminated array of supported data rates */
+ u8 rates[MAX_RATES + 1];
+
+ unsigned long last_scanned;
+
+ union ieee_phy_param_set phy;
+ union ieee_ss_param_set ss;
+
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ size_t wpa_ie_len;
+ u8 rsn_ie[MAX_WPA_IE_LEN];
+ size_t rsn_ie_len;
+
+ u8 mesh;
+
+ struct list_head list;
+};
+
+/** Association request
+ *
+ * Encapsulates all the options that describe a specific assocation request
+ * or configuration of the wireless card's radio, mode, and security settings.
+ */
+struct assoc_request {
+#define ASSOC_FLAG_SSID 1
+#define ASSOC_FLAG_CHANNEL 2
+#define ASSOC_FLAG_BAND 3
+#define ASSOC_FLAG_MODE 4
+#define ASSOC_FLAG_BSSID 5
+#define ASSOC_FLAG_WEP_KEYS 6
+#define ASSOC_FLAG_WEP_TX_KEYIDX 7
+#define ASSOC_FLAG_WPA_MCAST_KEY 8
+#define ASSOC_FLAG_WPA_UCAST_KEY 9
+#define ASSOC_FLAG_SECINFO 10
+#define ASSOC_FLAG_WPA_IE 11
+ unsigned long flags;
+
+ u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+ u8 ssid_len;
+ u8 channel;
+ u8 band;
+ u8 mode;
+ u8 bssid[ETH_ALEN] __attribute__ ((aligned (2)));
+
+ /** WEP keys */
+ struct enc_key wep_keys[4];
+ u16 wep_tx_keyidx;
+
+ /** WPA keys */
+ struct enc_key wpa_mcast_key;
+ struct enc_key wpa_unicast_key;
+
+ struct lbs_802_11_security secinfo;
+
+ /** WPA Information Elements*/
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ u8 wpa_ie_len;
+
+ /* BSS to associate with for infrastructure of Ad-Hoc join */
+ struct bss_descriptor bss;
+};
+
+
+extern u8 lbs_bg_rates[MAX_RATES];
void lbs_association_worker(struct work_struct *work);
struct assoc_request *lbs_get_association_request(struct lbs_private *priv);
@@ -13,4 +132,24 @@ int lbs_adhoc_stop(struct lbs_private *priv);
int lbs_cmd_80211_deauthenticate(struct lbs_private *priv,
u8 bssid[ETH_ALEN], u16 reason);
+int lbs_cmd_802_11_rssi(struct lbs_private *priv,
+ struct cmd_ds_command *cmd);
+int lbs_ret_802_11_rssi(struct lbs_private *priv,
+ struct cmd_ds_command *resp);
+
+int lbs_cmd_bcn_ctrl(struct lbs_private *priv,
+ struct cmd_ds_command *cmd,
+ u16 cmd_action);
+int lbs_ret_802_11_bcn_ctrl(struct lbs_private *priv,
+ struct cmd_ds_command *resp);
+
+int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
+ struct assoc_request *assoc);
+
+int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
+ uint16_t *enable);
+
+int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
+ struct assoc_request *assoc);
+
#endif /* _LBS_ASSOC_H */
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
new file mode 100644
index 00000000000..4396dccd12a
--- /dev/null
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -0,0 +1,198 @@
+/*
+ * Implement cfg80211 ("iw") support.
+ *
+ * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany
+ * Holger Schurig <hs4233@mail.mn-solutions.de>
+ *
+ */
+
+#include <net/cfg80211.h>
+
+#include "cfg.h"
+#include "cmd.h"
+
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel lbs_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+ .bitrate = (_rate), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+}
+
+
+static struct ieee80211_rate lbs_rates[] = {
+ RATETAB_ENT(10, 0x1, 0),
+ RATETAB_ENT(20, 0x2, 0),
+ RATETAB_ENT(55, 0x4, 0),
+ RATETAB_ENT(110, 0x8, 0),
+ RATETAB_ENT(60, 0x10, 0),
+ RATETAB_ENT(90, 0x20, 0),
+ RATETAB_ENT(120, 0x40, 0),
+ RATETAB_ENT(180, 0x80, 0),
+ RATETAB_ENT(240, 0x100, 0),
+ RATETAB_ENT(360, 0x200, 0),
+ RATETAB_ENT(480, 0x400, 0),
+ RATETAB_ENT(540, 0x800, 0),
+};
+
+static struct ieee80211_supported_band lbs_band_2ghz = {
+ .channels = lbs_2ghz_channels,
+ .n_channels = ARRAY_SIZE(lbs_2ghz_channels),
+ .bitrates = lbs_rates,
+ .n_bitrates = ARRAY_SIZE(lbs_rates),
+};
+
+
+static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+};
+
+
+
+static int lbs_cfg_set_channel(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = -ENOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", chan->center_freq, channel_type);
+
+ if (channel_type != NL80211_CHAN_NO_HT)
+ goto out;
+
+ ret = lbs_set_channel(priv, chan->hw_value);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+
+static struct cfg80211_ops lbs_cfg80211_ops = {
+ .set_channel = lbs_cfg_set_channel,
+};
+
+
+/*
+ * At this time lbs_private *priv doesn't even exist, so we just allocate
+ * memory and don't initialize the wiphy further. This is postponed until we
+ * can talk to the firmware and happens at registration time in
+ * lbs_cfg_wiphy_register().
+ */
+struct wireless_dev *lbs_cfg_alloc(struct device *dev)
+{
+ int ret = 0;
+ struct wireless_dev *wdev;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!wdev) {
+ dev_err(dev, "cannot allocate wireless device\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private));
+ if (!wdev->wiphy) {
+ dev_err(dev, "cannot allocate wiphy\n");
+ ret = -ENOMEM;
+ goto err_wiphy_new;
+ }
+
+ lbs_deb_leave(LBS_DEB_CFG80211);
+ return wdev;
+
+ err_wiphy_new:
+ kfree(wdev);
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ERR_PTR(ret);
+}
+
+
+/*
+ * This function get's called after lbs_setup_firmware() determined the
+ * firmware capabities. So we can setup the wiphy according to our
+ * hardware/firmware.
+ */
+int lbs_cfg_register(struct lbs_private *priv)
+{
+ struct wireless_dev *wdev = priv->wdev;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ wdev->wiphy->max_scan_ssids = 1;
+ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ /* TODO: BIT(NL80211_IFTYPE_ADHOC); */
+ wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+ /* TODO: honor priv->regioncode */
+ wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;
+
+ /*
+ * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have
+ * never seen a firmware without WPA
+ */
+ wdev->wiphy->cipher_suites = cipher_suites;
+ wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+ ret = wiphy_register(wdev->wiphy);
+ if (ret < 0)
+ lbs_pr_err("cannot register wiphy device\n");
+
+ ret = register_netdev(priv->dev);
+ if (ret)
+ lbs_pr_err("cannot register network device\n");
+
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+void lbs_cfg_free(struct lbs_private *priv)
+{
+ struct wireless_dev *wdev = priv->wdev;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ if (!wdev)
+ return;
+
+ if (wdev->wiphy) {
+ wiphy_unregister(wdev->wiphy);
+ wiphy_free(wdev->wiphy);
+ }
+ kfree(wdev);
+}
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
new file mode 100644
index 00000000000..e09a193a34d
--- /dev/null
+++ b/drivers/net/wireless/libertas/cfg.h
@@ -0,0 +1,16 @@
+#ifndef __LBS_CFG80211_H__
+#define __LBS_CFG80211_H__
+
+#include "dev.h"
+
+struct wireless_dev *lbs_cfg_alloc(struct device *dev);
+int lbs_cfg_register(struct lbs_private *priv);
+void lbs_cfg_free(struct lbs_private *priv);
+
+int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid,
+ u8 ssid_len);
+int lbs_scan_networks(struct lbs_private *priv, int full_scan);
+void lbs_cfg_scan_worker(struct work_struct *work);
+
+
+#endif
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 0a324dcd264..b9b371bfa30 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -3,21 +3,20 @@
* It prepares command and sends it to firmware when it is ready.
*/
-#include <net/iw_handler.h>
-#include <net/lib80211.h>
#include <linux/kfifo.h>
#include <linux/sched.h>
+
#include "host.h"
-#include "hostcmd.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
#include "assoc.h"
#include "wext.h"
+#include "scan.h"
#include "cmd.h"
-static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
+static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
/**
* @brief Simple callback that copies response back into command
@@ -77,6 +76,30 @@ static u8 is_command_allowed_in_ps(u16 cmd)
}
/**
+ * @brief This function checks if the command is allowed.
+ *
+ * @param priv A pointer to lbs_private structure
+ * @return allowed or not allowed.
+ */
+
+static int lbs_is_cmd_allowed(struct lbs_private *priv)
+{
+ int ret = 1;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (!priv->is_auto_deep_sleep_enabled) {
+ if (priv->is_deep_sleep) {
+ lbs_deb_cmd("command not allowed in deep sleep\n");
+ ret = 0;
+ }
+ }
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return ret;
+}
+
+/**
* @brief Updates the hardware details like MAC address and regulatory region
*
* @param priv A pointer to struct lbs_private structure
@@ -169,11 +192,6 @@ int lbs_update_hw_spec(struct lbs_private *priv)
goto out;
}
- if (lbs_set_universaltable(priv, 0)) {
- ret = -1;
- goto out;
- }
-
out:
lbs_deb_leave(LBS_DEB_CMD);
return ret;
@@ -222,7 +240,7 @@ static int lbs_cmd_802_11_ps_mode(struct cmd_ds_command *cmd,
cmd->command = cpu_to_le16(CMD_802_11_PS_MODE);
cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) +
- S_DS_GEN);
+ sizeof(struct cmd_header));
psm->action = cpu_to_le16(cmd_action);
psm->multipledtim = 0;
switch (cmd_action) {
@@ -251,33 +269,6 @@ static int lbs_cmd_802_11_ps_mode(struct cmd_ds_command *cmd,
return 0;
}
-int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv,
- uint16_t cmd_action, uint16_t *timeout)
-{
- struct cmd_ds_802_11_inactivity_timeout cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- cmd.hdr.command = cpu_to_le16(CMD_802_11_INACTIVITY_TIMEOUT);
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- cmd.action = cpu_to_le16(cmd_action);
-
- if (cmd_action == CMD_ACT_SET)
- cmd.timeout = cpu_to_le16(*timeout);
- else
- cmd.timeout = 0;
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_INACTIVITY_TIMEOUT, &cmd);
-
- if (!ret)
- *timeout = le16_to_cpu(cmd.timeout);
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return 0;
-}
-
int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
struct sleep_params *sp)
{
@@ -320,190 +311,53 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
return 0;
}
-int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc)
+static int lbs_wait_for_ds_awake(struct lbs_private *priv)
{
- struct cmd_ds_802_11_set_wep cmd;
int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.command = cpu_to_le16(CMD_802_11_SET_WEP);
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- cmd.action = cpu_to_le16(cmd_action);
-
- if (cmd_action == CMD_ACT_ADD) {
- int i;
-
- /* default tx key index */
- cmd.keyindex = cpu_to_le16(assoc->wep_tx_keyidx &
- CMD_WEP_KEY_INDEX_MASK);
-
- /* Copy key types and material to host command structure */
- for (i = 0; i < 4; i++) {
- struct enc_key *pkey = &assoc->wep_keys[i];
-
- switch (pkey->len) {
- case KEY_LEN_WEP_40:
- cmd.keytype[i] = CMD_TYPE_WEP_40_BIT;
- memmove(cmd.keymaterial[i], pkey->key, pkey->len);
- lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i);
- break;
- case KEY_LEN_WEP_104:
- cmd.keytype[i] = CMD_TYPE_WEP_104_BIT;
- memmove(cmd.keymaterial[i], pkey->key, pkey->len);
- lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i);
- break;
- case 0:
- break;
- default:
- lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n",
- i, pkey->len);
- ret = -1;
- goto done;
- break;
- }
+ if (priv->is_deep_sleep) {
+ if (!wait_event_interruptible_timeout(priv->ds_awake_q,
+ !priv->is_deep_sleep, (10 * HZ))) {
+ lbs_pr_err("ds_awake_q: timer expired\n");
+ ret = -1;
}
- } else if (cmd_action == CMD_ACT_REMOVE) {
- /* ACT_REMOVE clears _all_ WEP keys */
-
- /* default tx key index */
- cmd.keyindex = cpu_to_le16(priv->wep_tx_keyidx &
- CMD_WEP_KEY_INDEX_MASK);
- lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx);
- }
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
-done:
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
-int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
- uint16_t *enable)
-{
- struct cmd_ds_802_11_enable_rsn cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
- cmd.action = cpu_to_le16(cmd_action);
-
- if (cmd_action == CMD_ACT_GET)
- cmd.enable = 0;
- else {
- if (*enable)
- cmd.enable = cpu_to_le16(CMD_ENABLE_RSN);
- else
- cmd.enable = cpu_to_le16(CMD_DISABLE_RSN);
- lbs_deb_cmd("ENABLE_RSN: %d\n", *enable);
}
- ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
- if (!ret && cmd_action == CMD_ACT_GET)
- *enable = le16_to_cpu(cmd.enable);
-
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
-static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam,
- struct enc_key *key)
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
{
- lbs_deb_enter(LBS_DEB_CMD);
-
- if (key->flags & KEY_INFO_WPA_ENABLED)
- keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
- if (key->flags & KEY_INFO_WPA_UNICAST)
- keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
- if (key->flags & KEY_INFO_WPA_MCAST)
- keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
-
- keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
- keyparam->keytypeid = cpu_to_le16(key->type);
- keyparam->keylen = cpu_to_le16(key->len);
- memcpy(keyparam->key, key->key, key->len);
-
- /* Length field doesn't include the {type,length} header */
- keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4);
- lbs_deb_leave(LBS_DEB_CMD);
-}
-
-int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc)
-{
- struct cmd_ds_802_11_key_material cmd;
- int ret = 0;
- int index = 0;
+ int ret = 0;
lbs_deb_enter(LBS_DEB_CMD);
- cmd.action = cpu_to_le16(cmd_action);
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- if (cmd_action == CMD_ACT_GET) {
- cmd.hdr.size = cpu_to_le16(S_DS_GEN + 2);
- } else {
- memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet));
-
- if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) {
- set_one_wpa_key(&cmd.keyParamSet[index],
- &assoc->wpa_unicast_key);
- index++;
- }
-
- if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) {
- set_one_wpa_key(&cmd.keyParamSet[index],
- &assoc->wpa_mcast_key);
- index++;
+ if (deep_sleep) {
+ if (priv->is_deep_sleep != 1) {
+ lbs_deb_cmd("deep sleep: sleep\n");
+ BUG_ON(!priv->enter_deep_sleep);
+ ret = priv->enter_deep_sleep(priv);
+ if (!ret) {
+ netif_stop_queue(priv->dev);
+ netif_carrier_off(priv->dev);
+ }
+ } else {
+ lbs_pr_err("deep sleep: already enabled\n");
}
-
- /* The common header and as many keys as we included */
- cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd),
- keyParamSet[index]));
- }
- ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
- /* Copy the returned key to driver private data */
- if (!ret && cmd_action == CMD_ACT_GET) {
- void *buf_ptr = cmd.keyParamSet;
- void *resp_end = &(&cmd)[1];
-
- while (buf_ptr < resp_end) {
- struct MrvlIEtype_keyParamSet *keyparam = buf_ptr;
- struct enc_key *key;
- uint16_t param_set_len = le16_to_cpu(keyparam->length);
- uint16_t key_len = le16_to_cpu(keyparam->keylen);
- uint16_t key_flags = le16_to_cpu(keyparam->keyinfo);
- uint16_t key_type = le16_to_cpu(keyparam->keytypeid);
- void *end;
-
- end = (void *)keyparam + sizeof(keyparam->type)
- + sizeof(keyparam->length) + param_set_len;
-
- /* Make sure we don't access past the end of the IEs */
- if (end > resp_end)
- break;
-
- if (key_flags & KEY_INFO_WPA_UNICAST)
- key = &priv->wpa_unicast_key;
- else if (key_flags & KEY_INFO_WPA_MCAST)
- key = &priv->wpa_mcast_key;
- else
- break;
-
- /* Copy returned key into driver */
- memset(key, 0, sizeof(struct enc_key));
- if (key_len > sizeof(key->key))
- break;
- key->type = key_type;
- key->flags = key_flags;
- key->len = key_len;
- memcpy(key->key, keyparam->key, key->len);
-
- buf_ptr = end + 1;
+ } else {
+ if (priv->is_deep_sleep) {
+ lbs_deb_cmd("deep sleep: wakeup\n");
+ BUG_ON(!priv->exit_deep_sleep);
+ ret = priv->exit_deep_sleep(priv);
+ if (!ret) {
+ ret = lbs_wait_for_ds_awake(priv);
+ if (ret)
+ lbs_pr_err("deep sleep: wakeup"
+ "failed\n");
+ }
}
}
@@ -535,7 +389,7 @@ int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val)
switch (oid) {
case SNMP_MIB_OID_BSS_TYPE:
cmd.bufsize = cpu_to_le16(sizeof(u8));
- cmd.value[0] = (val == IW_MODE_ADHOC) ? 2 : 1;
+ cmd.value[0] = val;
break;
case SNMP_MIB_OID_11D_ENABLE:
case SNMP_MIB_OID_FRAG_THRESHOLD:
@@ -588,13 +442,7 @@ int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
switch (le16_to_cpu(cmd.bufsize)) {
case sizeof(u8):
- if (oid == SNMP_MIB_OID_BSS_TYPE) {
- if (cmd.value[0] == 2)
- *out_val = IW_MODE_ADHOC;
- else
- *out_val = IW_MODE_INFRA;
- } else
- *out_val = cmd.value[0];
+ *out_val = cmd.value[0];
break;
case sizeof(u16):
*out_val = le16_to_cpu(*((__le16 *)(&cmd.value)));
@@ -681,7 +529,7 @@ static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd,
cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE);
cmd->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) +
- S_DS_GEN);
+ sizeof(struct cmd_header));
monitor->action = cpu_to_le16(cmd_action);
if (cmd_action == CMD_ACT_SET) {
@@ -692,111 +540,6 @@ static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd,
return 0;
}
-static __le16 lbs_rate_to_fw_bitmap(int rate, int lower_rates_ok)
-{
-/* Bit Rate
-* 15:13 Reserved
-* 12 54 Mbps
-* 11 48 Mbps
-* 10 36 Mbps
-* 9 24 Mbps
-* 8 18 Mbps
-* 7 12 Mbps
-* 6 9 Mbps
-* 5 6 Mbps
-* 4 Reserved
-* 3 11 Mbps
-* 2 5.5 Mbps
-* 1 2 Mbps
-* 0 1 Mbps
-**/
-
- uint16_t ratemask;
- int i = lbs_data_rate_to_fw_index(rate);
- if (lower_rates_ok)
- ratemask = (0x1fef >> (12 - i));
- else
- ratemask = (1 << i);
- return cpu_to_le16(ratemask);
-}
-
-int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
- uint16_t cmd_action)
-{
- struct cmd_ds_802_11_rate_adapt_rateset cmd;
- int ret;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- if (!priv->cur_rate && !priv->enablehwauto)
- return -EINVAL;
-
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- cmd.action = cpu_to_le16(cmd_action);
- cmd.enablehwauto = cpu_to_le16(priv->enablehwauto);
- cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto);
- ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd);
- if (!ret && cmd_action == CMD_ACT_GET) {
- priv->ratebitmap = le16_to_cpu(cmd.bitmap);
- priv->enablehwauto = le16_to_cpu(cmd.enablehwauto);
- }
-
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-EXPORT_SYMBOL_GPL(lbs_cmd_802_11_rate_adapt_rateset);
-
-/**
- * @brief Set the data rate
- *
- * @param priv A pointer to struct lbs_private structure
- * @param rate The desired data rate, or 0 to clear a locked rate
- *
- * @return 0 on success, error on failure
- */
-int lbs_set_data_rate(struct lbs_private *priv, u8 rate)
-{
- struct cmd_ds_802_11_data_rate cmd;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-
- if (rate > 0) {
- cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE);
- cmd.rates[0] = lbs_data_rate_to_fw_index(rate);
- if (cmd.rates[0] == 0) {
- lbs_deb_cmd("DATA_RATE: invalid requested rate of"
- " 0x%02X\n", rate);
- ret = 0;
- goto out;
- }
- lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]);
- } else {
- cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO);
- lbs_deb_cmd("DATA_RATE: setting auto\n");
- }
-
- ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, &cmd);
- if (ret)
- goto out;
-
- lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof (cmd));
-
- /* FIXME: get actual rates FW can do if this command actually returns
- * all data rates supported.
- */
- priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]);
- lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate);
-
-out:
- lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
- return ret;
-}
-
/**
* @brief Get the radio channel
*
@@ -804,7 +547,7 @@ out:
*
* @return The channel on success, error on failure
*/
-int lbs_get_channel(struct lbs_private *priv)
+static int lbs_get_channel(struct lbs_private *priv)
{
struct cmd_ds_802_11_rf_channel cmd;
int ret = 0;
@@ -836,7 +579,7 @@ int lbs_update_channel(struct lbs_private *priv)
ret = lbs_get_channel(priv);
if (ret > 0) {
- priv->curbssparams.channel = ret;
+ priv->channel = ret;
ret = 0;
}
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
@@ -855,7 +598,7 @@ int lbs_set_channel(struct lbs_private *priv, u8 channel)
{
struct cmd_ds_802_11_rf_channel cmd;
#ifdef DEBUG
- u8 old_channel = priv->curbssparams.channel;
+ u8 old_channel = priv->channel;
#endif
int ret = 0;
@@ -870,36 +613,15 @@ int lbs_set_channel(struct lbs_private *priv, u8 channel)
if (ret)
goto out;
- priv->curbssparams.channel = (uint8_t) le16_to_cpu(cmd.channel);
+ priv->channel = (uint8_t) le16_to_cpu(cmd.channel);
lbs_deb_cmd("channel switch from %d to %d\n", old_channel,
- priv->curbssparams.channel);
+ priv->channel);
out:
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
-static int lbs_cmd_802_11_rssi(struct lbs_private *priv,
- struct cmd_ds_command *cmd)
-{
-
- lbs_deb_enter(LBS_DEB_CMD);
- cmd->command = cpu_to_le16(CMD_802_11_RSSI);
- cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN);
- cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR);
-
- /* reset Beacon SNR/NF/RSSI values */
- priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
- priv->SNR[TYPE_BEACON][TYPE_AVG] = 0;
- priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
- priv->NF[TYPE_BEACON][TYPE_AVG] = 0;
- priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
- priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
-
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
u8 cmd_action, void *pdata_buf)
{
@@ -916,7 +638,7 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
cmdptr->size =
cpu_to_le16(sizeof (struct cmd_ds_mac_reg_access)
- + S_DS_GEN);
+ + sizeof(struct cmd_header));
macreg =
(struct cmd_ds_mac_reg_access *)&cmdptr->params.
macreg;
@@ -935,7 +657,7 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
cmdptr->size =
cpu_to_le16(sizeof
(struct cmd_ds_bbp_reg_access)
- + S_DS_GEN);
+ + sizeof(struct cmd_header));
bbpreg =
(struct cmd_ds_bbp_reg_access *)&cmdptr->params.
bbpreg;
@@ -954,7 +676,7 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
cmdptr->size =
cpu_to_le16(sizeof
(struct cmd_ds_rf_reg_access) +
- S_DS_GEN);
+ sizeof(struct cmd_header));
rfreg =
(struct cmd_ds_rf_reg_access *)&cmdptr->params.
rfreg;
@@ -974,192 +696,6 @@ static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
return 0;
}
-static int lbs_cmd_bt_access(struct cmd_ds_command *cmd,
- u16 cmd_action, void *pdata_buf)
-{
- struct cmd_ds_bt_access *bt_access = &cmd->params.bt;
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
-
- cmd->command = cpu_to_le16(CMD_BT_ACCESS);
- cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + S_DS_GEN);
- cmd->result = 0;
- bt_access->action = cpu_to_le16(cmd_action);
-
- switch (cmd_action) {
- case CMD_ACT_BT_ACCESS_ADD:
- memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN);
- lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr", bt_access->addr1, 6);
- break;
- case CMD_ACT_BT_ACCESS_DEL:
- memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN);
- lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr", bt_access->addr1, 6);
- break;
- case CMD_ACT_BT_ACCESS_LIST:
- bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
- break;
- case CMD_ACT_BT_ACCESS_RESET:
- break;
- case CMD_ACT_BT_ACCESS_SET_INVERT:
- bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
- break;
- case CMD_ACT_BT_ACCESS_GET_INVERT:
- break;
- default:
- break;
- }
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
-static int lbs_cmd_fwt_access(struct cmd_ds_command *cmd,
- u16 cmd_action, void *pdata_buf)
-{
- struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt;
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
-
- cmd->command = cpu_to_le16(CMD_FWT_ACCESS);
- cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + S_DS_GEN);
- cmd->result = 0;
-
- if (pdata_buf)
- memcpy(fwt_access, pdata_buf, sizeof(*fwt_access));
- else
- memset(fwt_access, 0, sizeof(*fwt_access));
-
- fwt_access->action = cpu_to_le16(cmd_action);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
-int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
- struct cmd_ds_mesh_access *cmd)
-{
- int ret;
-
- lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
-
- cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
- cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
- cmd->hdr.result = 0;
-
- cmd->action = cpu_to_le16(cmd_action);
-
- ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return ret;
-}
-
-static int __lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type)
-{
- int ret;
- u16 command = CMD_MESH_CONFIG_OLD;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- /*
- * Command id is 0xac for v10 FW along with mesh interface
- * id in bits 14-13-12.
- */
- if (priv->mesh_fw_ver == MESH_FW_NEW)
- command = CMD_MESH_CONFIG |
- (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
-
- cmd->hdr.command = cpu_to_le16(command);
- cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
- cmd->hdr.result = 0;
-
- cmd->type = cpu_to_le16(type);
- cmd->action = cpu_to_le16(action);
-
- ret = lbs_cmd_with_response(priv, command, cmd);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return ret;
-}
-
-int lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type)
-{
- int ret;
-
- if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
- return -EOPNOTSUPP;
-
- ret = __lbs_mesh_config_send(priv, cmd, action, type);
- return ret;
-}
-
-/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
- * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
- * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
- * lbs_mesh_config_send.
- */
-int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_meshie *ie;
- DECLARE_SSID_BUF(ssid);
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.channel = cpu_to_le16(chan);
- ie = (struct mrvl_meshie *)cmd.data;
-
- switch (action) {
- case CMD_ACT_MESH_CONFIG_START:
- ie->id = WLAN_EID_GENERIC;
- ie->val.oui[0] = 0x00;
- ie->val.oui[1] = 0x50;
- ie->val.oui[2] = 0x43;
- ie->val.type = MARVELL_MESH_IE_TYPE;
- ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
- ie->val.version = MARVELL_MESH_IE_VERSION;
- ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
- ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
- ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
- ie->val.mesh_id_len = priv->mesh_ssid_len;
- memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
- ie->len = sizeof(struct mrvl_meshie_val) -
- IW_ESSID_MAX_SIZE + priv->mesh_ssid_len;
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
- break;
- case CMD_ACT_MESH_CONFIG_STOP:
- break;
- default:
- return -1;
- }
- lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
- action, priv->mesh_tlv, chan,
- print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
-
- return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
-}
-
-static int lbs_cmd_bcn_ctrl(struct lbs_private * priv,
- struct cmd_ds_command *cmd,
- u16 cmd_action)
-{
- struct cmd_ds_802_11_beacon_control
- *bcn_ctrl = &cmd->params.bcn_ctrl;
-
- lbs_deb_enter(LBS_DEB_CMD);
- cmd->size =
- cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control)
- + S_DS_GEN);
- cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL);
-
- bcn_ctrl->action = cpu_to_le16(cmd_action);
- bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable);
- bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
static void lbs_queue_cmd(struct lbs_private *priv,
struct cmd_ctrl_node *cmdnode)
{
@@ -1243,8 +779,17 @@ static void lbs_submit_command(struct lbs_private *priv,
timeo = HZ/4;
}
- /* Setup the timer after transmit command */
- mod_timer(&priv->command_timer, jiffies + timeo);
+ if (command == CMD_802_11_DEEP_SLEEP) {
+ if (priv->is_auto_deep_sleep_enabled) {
+ priv->wakeup_dev_required = 1;
+ priv->dnld_sent = 0;
+ }
+ priv->is_deep_sleep = 1;
+ lbs_complete_command(priv, cmdnode, 0);
+ } else {
+ /* Setup the timer after transmit command */
+ mod_timer(&priv->command_timer, jiffies + timeo);
+ }
lbs_deb_leave(LBS_DEB_HOST);
}
@@ -1391,6 +936,11 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
goto done;
}
+ if (!lbs_is_cmd_allowed(priv)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
cmdnode = lbs_get_cmd_ctrl_node(priv);
if (cmdnode == NULL) {
@@ -1441,7 +991,7 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
cmdptr->command = cpu_to_le16(cmd_no);
cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) +
- S_DS_GEN);
+ sizeof(struct cmd_header));
memmove(&cmdptr->params.afc,
pdata_buf, sizeof(struct cmd_ds_802_11_afc));
@@ -1449,45 +999,17 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
ret = 0;
goto done;
- case CMD_802_11D_DOMAIN_INFO:
- ret = lbs_cmd_802_11d_domain_info(priv, cmdptr,
- cmd_no, cmd_action);
- break;
-
case CMD_802_11_TPC_CFG:
cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG);
cmdptr->size =
cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) +
- S_DS_GEN);
+ sizeof(struct cmd_header));
memmove(&cmdptr->params.tpccfg,
pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg));
ret = 0;
break;
- case CMD_802_11_LED_GPIO_CTRL:
- {
- struct mrvl_ie_ledgpio *gpio =
- (struct mrvl_ie_ledgpio*)
- cmdptr->params.ledgpio.data;
-
- memmove(&cmdptr->params.ledgpio,
- pdata_buf,
- sizeof(struct cmd_ds_802_11_led_ctrl));
-
- cmdptr->command =
- cpu_to_le16(CMD_802_11_LED_GPIO_CTRL);
-
-#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8
- cmdptr->size =
- cpu_to_le16(le16_to_cpu(gpio->header.len)
- + S_DS_GEN
- + ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN);
- gpio->header.len = gpio->header.len;
-
- ret = 0;
- break;
- }
case CMD_BT_ACCESS:
ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf);
@@ -1497,15 +1019,13 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
ret = lbs_cmd_fwt_access(cmdptr, cmd_action, pdata_buf);
break;
- case CMD_GET_TSF:
- cmdptr->command = cpu_to_le16(CMD_GET_TSF);
- cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_get_tsf) +
- S_DS_GEN);
- ret = 0;
- break;
case CMD_802_11_BEACON_CTRL:
ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
break;
+ case CMD_802_11_DEEP_SLEEP:
+ cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP);
+ cmdptr->size = cpu_to_le16(sizeof(struct cmd_header));
+ break;
default:
lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
ret = -1;
@@ -1823,30 +1343,6 @@ done:
return ret;
}
-void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
-{
- union iwreq_data iwrq;
- u8 buf[50];
-
- lbs_deb_enter(LBS_DEB_WEXT);
-
- memset(&iwrq, 0, sizeof(union iwreq_data));
- memset(buf, 0, sizeof(buf));
-
- snprintf(buf, sizeof(buf) - 1, "%s", str);
-
- iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;
-
- /* Send Event to upper layer */
- lbs_deb_wext("event indication string %s\n", (char *)buf);
- lbs_deb_wext("event indication length %d\n", iwrq.data.length);
- lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str);
-
- wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf);
-
- lbs_deb_leave(LBS_DEB_WEXT);
-}
-
static void lbs_send_confirmsleep(struct lbs_private *priv)
{
unsigned long flags;
@@ -2024,7 +1520,7 @@ int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
}
-static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
+struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
unsigned long callback_arg)
@@ -2039,6 +1535,11 @@ static struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
goto done;
}
+ if (!lbs_is_cmd_allowed(priv)) {
+ cmdnode = ERR_PTR(-EBUSY);
+ goto done;
+ }
+
cmdnode = lbs_get_cmd_ctrl_node(priv);
if (cmdnode == NULL) {
lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
@@ -2117,5 +1618,3 @@ done:
return ret;
}
EXPORT_SYMBOL_GPL(__lbs_cmd);
-
-
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h
index 392e578ca09..2862748aef7 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/libertas/cmd.h
@@ -3,11 +3,30 @@
#ifndef _LBS_CMD_H_
#define _LBS_CMD_H_
-#include "hostcmd.h"
+#include "host.h"
#include "dev.h"
+
+/* Command & response transfer between host and card */
+
+struct cmd_ctrl_node {
+ struct list_head list;
+ int result;
+ /* command response */
+ int (*callback)(struct lbs_private *,
+ unsigned long,
+ struct cmd_header *);
+ unsigned long callback_arg;
+ /* command data */
+ struct cmd_header *cmdbuf;
+ /* wait queue */
+ u16 cmdwaitqwoken;
+ wait_queue_head_t cmdwait_q;
+};
+
+
/* lbs_cmd() infers the size of the buffer to copy data back into, from
- the size of the target of the pointer. Since the command to be sent
+ the size of the target of the pointer. Since the command to be sent
may often be smaller, that size is set in cmd->size by the caller.*/
#define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \
uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \
@@ -18,6 +37,11 @@
#define lbs_cmd_with_response(priv, cmdnr, cmd) \
lbs_cmd(priv, cmdnr, cmd, lbs_cmd_copyback, (unsigned long) (cmd))
+int lbs_prepare_and_send_command(struct lbs_private *priv,
+ u16 cmd_no,
+ u16 cmd_action,
+ u16 wait_option, u32 cmd_oid, void *pdata_buf);
+
void lbs_cmd_async(struct lbs_private *priv, uint16_t command,
struct cmd_header *in_cmd, int in_cmd_size);
@@ -26,62 +50,93 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
unsigned long callback_arg);
-int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
- int8_t p1, int8_t p2);
+struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
+ uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
+ int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
+ unsigned long callback_arg);
-int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
- int8_t p2, int usesnr);
+int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
+ struct cmd_header *resp);
-int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
- int8_t p1, int8_t p2);
+int lbs_allocate_cmd_buffer(struct lbs_private *priv);
+int lbs_free_cmd_buffer(struct lbs_private *priv);
-int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
- int8_t p2, int usesnr);
+int lbs_execute_next_command(struct lbs_private *priv);
+void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
+ int result);
+int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len);
-int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
- struct cmd_header *resp);
-int lbs_update_hw_spec(struct lbs_private *priv);
+/* From cmdresp.c */
-int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
- struct cmd_ds_mesh_access *cmd);
+void lbs_mac_event_disconnected(struct lbs_private *priv);
-int lbs_set_data_rate(struct lbs_private *priv, u8 rate);
-int lbs_get_channel(struct lbs_private *priv);
+
+/* Events */
+
+int lbs_process_event(struct lbs_private *priv, u32 event);
+
+
+/* Actual commands */
+
+int lbs_update_hw_spec(struct lbs_private *priv);
+
int lbs_set_channel(struct lbs_private *priv, u8 channel);
-int lbs_mesh_config_send(struct lbs_private *priv,
- struct cmd_ds_mesh_config *cmd,
- uint16_t action, uint16_t type);
-int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
+int lbs_update_channel(struct lbs_private *priv);
int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
struct wol_config *p_wol_config);
-int lbs_suspend(struct lbs_private *priv);
-void lbs_resume(struct lbs_private *priv);
-int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
- uint16_t cmd_action);
-int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv,
- uint16_t cmd_action, uint16_t *timeout);
int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
struct sleep_params *sp);
-int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc);
-int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
- uint16_t *enable);
-int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
- struct assoc_request *assoc);
-int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
- s16 *maxlevel);
-int lbs_set_tx_power(struct lbs_private *priv, s16 dbm);
+void lbs_ps_sleep(struct lbs_private *priv, int wait_option);
+
+void lbs_ps_wakeup(struct lbs_private *priv, int wait_option);
+
+void lbs_ps_confirm_sleep(struct lbs_private *priv);
int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on);
+void lbs_set_mac_control(struct lbs_private *priv);
+
+int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
+ s16 *maxlevel);
+
int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val);
int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val);
+
+/* Mesh related */
+
+int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
+ struct cmd_ds_mesh_access *cmd);
+
+int lbs_mesh_config_send(struct lbs_private *priv,
+ struct cmd_ds_mesh_config *cmd,
+ uint16_t action, uint16_t type);
+
+int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
+
+
+/* Commands only used in wext.c, assoc. and scan.c */
+
+int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
+ int8_t p1, int8_t p2);
+
+int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
+ int8_t p2, int usesnr);
+
+int lbs_set_data_rate(struct lbs_private *priv, u8 rate);
+
+int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
+ uint16_t cmd_action);
+
+int lbs_set_tx_power(struct lbs_private *priv, s16 dbm);
+
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
+
#endif /* _LBS_CMD_H */
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index 23f684337fd..21d57690c20 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -11,6 +11,7 @@
#include "host.h"
#include "decl.h"
+#include "cmd.h"
#include "defs.h"
#include "dev.h"
#include "assoc.h"
@@ -26,23 +27,17 @@
*/
void lbs_mac_event_disconnected(struct lbs_private *priv)
{
- union iwreq_data wrqu;
-
if (priv->connect_status != LBS_CONNECTED)
return;
lbs_deb_enter(LBS_DEB_ASSOC);
- memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-
/*
* Cisco AP sends EAP failure and de-auth in less than 0.5 ms.
* It causes problem in the Supplicant
*/
-
msleep_interruptible(1000);
- wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+ lbs_send_disconnect_notification(priv);
/* report disconnect to upper layer */
netif_stop_queue(priv->dev);
@@ -67,7 +62,7 @@ void lbs_mac_event_disconnected(struct lbs_private *priv)
* no longer valid.
*/
memset(&priv->curbssparams.bssid, 0, ETH_ALEN);
- memset(&priv->curbssparams.ssid, 0, IW_ESSID_MAX_SIZE);
+ memset(&priv->curbssparams.ssid, 0, IEEE80211_MAX_SSID_LEN);
priv->curbssparams.ssid_len = 0;
if (priv->psstate != PS_STATE_FULL_POWER) {
@@ -78,32 +73,6 @@ void lbs_mac_event_disconnected(struct lbs_private *priv)
lbs_deb_leave(LBS_DEB_ASSOC);
}
-/**
- * @brief This function handles MIC failure event.
- *
- * @param priv A pointer to struct lbs_private structure
- * @para event the event id
- * @return n/a
- */
-static void handle_mic_failureevent(struct lbs_private *priv, u32 event)
-{
- char buf[50];
-
- lbs_deb_enter(LBS_DEB_CMD);
- memset(buf, 0, sizeof(buf));
-
- sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication ");
-
- if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) {
- strcat(buf, "unicast ");
- } else {
- strcat(buf, "multicast ");
- }
-
- lbs_send_iwevcustom_event(priv, buf);
- lbs_deb_leave(LBS_DEB_CMD);
-}
-
static int lbs_ret_reg_access(struct lbs_private *priv,
u16 type, struct cmd_ds_command *resp)
{
@@ -147,53 +116,6 @@ static int lbs_ret_reg_access(struct lbs_private *priv,
return ret;
}
-static int lbs_ret_802_11_rssi(struct lbs_private *priv,
- struct cmd_ds_command *resp)
-{
- struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- /* store the non average value */
- priv->SNR[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->SNR);
- priv->NF[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->noisefloor);
-
- priv->SNR[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgSNR);
- priv->NF[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgnoisefloor);
-
- priv->RSSI[TYPE_BEACON][TYPE_NOAVG] =
- CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
- priv->NF[TYPE_BEACON][TYPE_NOAVG]);
-
- priv->RSSI[TYPE_BEACON][TYPE_AVG] =
- CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
- priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
-
- lbs_deb_cmd("RSSI: beacon %d, avg %d\n",
- priv->RSSI[TYPE_BEACON][TYPE_NOAVG],
- priv->RSSI[TYPE_BEACON][TYPE_AVG]);
-
- lbs_deb_leave(LBS_DEB_CMD);
- return 0;
-}
-
-static int lbs_ret_802_11_bcn_ctrl(struct lbs_private * priv,
- struct cmd_ds_command *resp)
-{
- struct cmd_ds_802_11_beacon_control *bcn_ctrl =
- &resp->params.bcn_ctrl;
-
- lbs_deb_enter(LBS_DEB_CMD);
-
- if (bcn_ctrl->action == CMD_ACT_GET) {
- priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable);
- priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period);
- }
-
- lbs_deb_enter(LBS_DEB_CMD);
- return 0;
-}
-
static inline int handle_cmd_response(struct lbs_private *priv,
struct cmd_header *cmd_response)
{
@@ -227,29 +149,13 @@ static inline int handle_cmd_response(struct lbs_private *priv,
ret = lbs_ret_802_11_rssi(priv, resp);
break;
- case CMD_RET(CMD_802_11D_DOMAIN_INFO):
- ret = lbs_ret_802_11d_domain_info(resp);
- break;
-
case CMD_RET(CMD_802_11_TPC_CFG):
spin_lock_irqsave(&priv->driver_lock, flags);
memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg,
sizeof(struct cmd_ds_802_11_tpc_cfg));
spin_unlock_irqrestore(&priv->driver_lock, flags);
break;
- case CMD_RET(CMD_802_11_LED_GPIO_CTRL):
- spin_lock_irqsave(&priv->driver_lock, flags);
- memmove((void *)priv->cur_cmd->callback_arg, &resp->params.ledgpio,
- sizeof(struct cmd_ds_802_11_led_ctrl));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- break;
- case CMD_RET(CMD_GET_TSF):
- spin_lock_irqsave(&priv->driver_lock, flags);
- memcpy((void *)priv->cur_cmd->callback_arg,
- &resp->params.gettsf.tsfvalue, sizeof(u64));
- spin_unlock_irqrestore(&priv->driver_lock, flags);
- break;
case CMD_RET(CMD_BT_ACCESS):
spin_lock_irqsave(&priv->driver_lock, flags);
if (priv->cur_cmd->callback_arg)
@@ -505,9 +411,21 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
case MACREG_INT_CODE_HOST_AWAKE:
lbs_deb_cmd("EVENT: host awake\n");
+ if (priv->reset_deep_sleep_wakeup)
+ priv->reset_deep_sleep_wakeup(priv);
+ priv->is_deep_sleep = 0;
lbs_send_confirmwake(priv);
break;
+ case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
+ if (priv->reset_deep_sleep_wakeup)
+ priv->reset_deep_sleep_wakeup(priv);
+ lbs_deb_cmd("EVENT: ds awake\n");
+ priv->is_deep_sleep = 0;
+ priv->wakeup_dev_required = 0;
+ wake_up_interruptible(&priv->ds_awake_q);
+ break;
+
case MACREG_INT_CODE_PS_AWAKE:
lbs_deb_cmd("EVENT: ps awake\n");
/* handle unexpected PS AWAKE event */
@@ -533,12 +451,12 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
case MACREG_INT_CODE_MIC_ERR_UNICAST:
lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
- handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST);
+ lbs_send_mic_failureevent(priv, event);
break;
case MACREG_INT_CODE_MIC_ERR_MULTICAST:
lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
- handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST);
+ lbs_send_mic_failureevent(priv, event);
break;
case MACREG_INT_CODE_MIB_CHANGED:
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index 893a55ca344..587b0cb0088 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -451,10 +451,12 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
CMD_MAC_REG_ACCESS, 0,
CMD_OPTION_WAITFORRSP, 0, &offval);
mdelay(10);
- pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n",
+ if (!ret) {
+ pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n",
priv->mac_offset, priv->offsetvalue.value);
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ }
free_page(addr);
return ret;
}
@@ -514,7 +516,8 @@ static ssize_t lbs_wrmac_write(struct file *file,
CMD_OPTION_WAITFORRSP, 0, &offval);
mdelay(10);
- res = count;
+ if (!res)
+ res = count;
out_unlock:
free_page(addr);
return res;
@@ -539,10 +542,12 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
CMD_BBP_REG_ACCESS, 0,
CMD_OPTION_WAITFORRSP, 0, &offval);
mdelay(10);
- pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n",
+ if (!ret) {
+ pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n",
priv->bbp_offset, priv->offsetvalue.value);
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ }
free_page(addr);
return ret;
@@ -603,7 +608,8 @@ static ssize_t lbs_wrbbp_write(struct file *file,
CMD_OPTION_WAITFORRSP, 0, &offval);
mdelay(10);
- res = count;
+ if (!res)
+ res = count;
out_unlock:
free_page(addr);
return res;
@@ -628,10 +634,12 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
CMD_RF_REG_ACCESS, 0,
CMD_OPTION_WAITFORRSP, 0, &offval);
mdelay(10);
- pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n",
+ if (!ret) {
+ pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n",
priv->rf_offset, priv->offsetvalue.value);
- ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ }
free_page(addr);
return ret;
@@ -692,7 +700,8 @@ static ssize_t lbs_wrrf_write(struct file *file,
CMD_OPTION_WAITFORRSP, 0, &offval);
mdelay(10);
- res = count;
+ if (!res)
+ res = count;
out_unlock:
free_page(addr);
return res;
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
index 8b15380ae6e..709ffcad22a 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/libertas/decl.h
@@ -8,71 +8,46 @@
#include <linux/netdevice.h>
-#include "defs.h"
-/** Function Prototype Declaration */
struct lbs_private;
struct sk_buff;
struct net_device;
-struct cmd_ctrl_node;
-struct cmd_ds_command;
-void lbs_set_mac_control(struct lbs_private *priv);
-void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count);
-
-int lbs_free_cmd_buffer(struct lbs_private *priv);
-
-int lbs_prepare_and_send_command(struct lbs_private *priv,
- u16 cmd_no,
- u16 cmd_action,
- u16 wait_option, u32 cmd_oid, void *pdata_buf);
+/* ethtool.c */
+extern const struct ethtool_ops lbs_ethtool_ops;
-int lbs_allocate_cmd_buffer(struct lbs_private *priv);
-int lbs_execute_next_command(struct lbs_private *priv);
-int lbs_process_event(struct lbs_private *priv, u32 event);
-void lbs_queue_event(struct lbs_private *priv, u32 event);
-void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
-u32 lbs_fw_index_to_data_rate(u8 index);
-u8 lbs_data_rate_to_fw_index(u32 rate);
-
-/** The proc fs interface */
-int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len);
-void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
- int result);
+/* tx.c */
+void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count);
netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev);
-int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band);
+/* rx.c */
int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *);
-void lbs_ps_sleep(struct lbs_private *priv, int wait_option);
-void lbs_ps_confirm_sleep(struct lbs_private *priv);
-void lbs_ps_wakeup(struct lbs_private *priv, int wait_option);
-
-struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
- struct lbs_private *priv,
- u8 band,
- u16 channel);
-
-void lbs_mac_event_disconnected(struct lbs_private *priv);
-
-void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str);
-
-/* persistcfg.c */
-void lbs_persist_config_init(struct net_device *net);
-void lbs_persist_config_remove(struct net_device *net);
/* main.c */
-struct chan_freq_power *lbs_get_region_cfp_table(u8 region,
- int *cfp_no);
struct lbs_private *lbs_add_card(void *card, struct device *dmdev);
void lbs_remove_card(struct lbs_private *priv);
int lbs_start_card(struct lbs_private *priv);
void lbs_stop_card(struct lbs_private *priv);
void lbs_host_to_card_done(struct lbs_private *priv);
-int lbs_update_channel(struct lbs_private *priv);
+int lbs_set_mac_address(struct net_device *dev, void *addr);
+void lbs_set_multicast_list(struct net_device *dev);
+
+int lbs_suspend(struct lbs_private *priv);
+void lbs_resume(struct lbs_private *priv);
+
+void lbs_queue_event(struct lbs_private *priv, u32 event);
+void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
+
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
+
+u32 lbs_fw_index_to_data_rate(u8 index);
+u8 lbs_data_rate_to_fw_index(u32 rate);
+
#endif
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h
index 72f3479a4d7..6b6ea9f7bf5 100644
--- a/drivers/net/wireless/libertas/defs.h
+++ b/drivers/net/wireless/libertas/defs.h
@@ -42,6 +42,7 @@
#define LBS_DEB_SDIO 0x00400000
#define LBS_DEB_SYSFS 0x00800000
#define LBS_DEB_SPI 0x01000000
+#define LBS_DEB_CFG80211 0x02000000
extern unsigned int lbs_debug;
@@ -86,6 +87,7 @@ do { if ((lbs_debug & (grp)) == (grp)) \
#define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args)
#define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args)
#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args)
+#define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args)
#define lbs_pr_info(format, args...) \
printk(KERN_INFO DRV_NAME": " format, ## args)
@@ -320,7 +322,6 @@ static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, in
extern const char lbs_driver_version[];
extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE];
-extern u8 lbs_bg_rates[MAX_RATES];
/** ENUM definition*/
/** SNRNF_TYPE */
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index d3b69a4b4b5..6a8d2b291d8 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -6,75 +6,11 @@
#ifndef _LBS_DEV_H_
#define _LBS_DEV_H_
-#include <linux/netdevice.h>
-#include <linux/wireless.h>
-#include <linux/ethtool.h>
-#include <linux/debugfs.h>
+#include "mesh.h"
+#include "scan.h"
+#include "assoc.h"
-#include "defs.h"
-#include "hostcmd.h"
-extern const struct ethtool_ops lbs_ethtool_ops;
-
-#define MAX_BSSID_PER_CHANNEL 16
-
-#define NR_TX_QUEUE 3
-
-/* For the extended Scan */
-#define MAX_EXTENDED_SCAN_BSSID_LIST MAX_BSSID_PER_CHANNEL * \
- MRVDRV_MAX_CHANNEL_SIZE + 1
-
-#define MAX_REGION_CHANNEL_NUM 2
-
-/** Chan-freq-TxPower mapping table*/
-struct chan_freq_power {
- /** channel Number */
- u16 channel;
- /** frequency of this channel */
- u32 freq;
- /** Max allowed Tx power level */
- u16 maxtxpower;
- /** TRUE:channel unsupported; FLASE:supported*/
- u8 unsupported;
-};
-
-/** region-band mapping table*/
-struct region_channel {
- /** TRUE if this entry is valid */
- u8 valid;
- /** region code for US, Japan ... */
- u8 region;
- /** band B/G/A, used for BAND_CONFIG cmd */
- u8 band;
- /** Actual No. of elements in the array below */
- u8 nrcfp;
- /** chan-freq-txpower mapping table*/
- struct chan_freq_power *CFP;
-};
-
-struct lbs_802_11_security {
- u8 WPAenabled;
- u8 WPA2enabled;
- u8 wep_enabled;
- u8 auth_mode;
- u32 key_mgmt;
-};
-
-/** Current Basic Service Set State Structure */
-struct current_bss_params {
- /** bssid */
- u8 bssid[ETH_ALEN];
- /** ssid */
- u8 ssid[IW_ESSID_MAX_SIZE + 1];
- u8 ssid_len;
-
- /** band */
- u8 band;
- /** channel */
- u8 channel;
- /** zero-terminated array of supported data rates */
- u8 rates[MAX_RATES + 1];
-};
/** sleep_params */
struct sleep_params {
@@ -86,109 +22,99 @@ struct sleep_params {
uint16_t sp_reserved;
};
-/* Mesh statistics */
-struct lbs_mesh_stats {
- u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
- u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
- u32 fwd_drop_ttl; /* Fwd: TTL zero */
- u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
- u32 fwd_drop_noroute; /* Fwd: No route to Destination */
- u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
- u32 drop_blind; /* Rx: Dropped by blinding table */
- u32 tx_failed_cnt; /* Tx: Failed transmissions */
-};
/** Private structure for the MV device */
struct lbs_private {
- int mesh_open;
- int mesh_fw_ver;
- int infra_open;
- int mesh_autostart_enabled;
- char name[DEV_NAME_LEN];
-
- void *card;
+ /* Basic networking */
struct net_device *dev;
+ u32 connect_status;
+ int infra_open;
+ struct work_struct mcast_work;
+ u32 nr_of_multicastmacaddr;
+ u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
+
+ /* CFG80211 */
+ struct wireless_dev *wdev;
+ /* Mesh */
struct net_device *mesh_dev; /* Virtual device */
+ u32 mesh_connect_status;
+ struct lbs_mesh_stats mstats;
+ int mesh_open;
+ int mesh_fw_ver;
+ int mesh_autostart_enabled;
+ uint16_t mesh_tlv;
+ u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1];
+ u8 mesh_ssid_len;
+ struct work_struct sync_channel;
+
+ /* Monitor mode */
struct net_device *rtap_net_dev;
+ u32 monitormode;
- struct iw_statistics wstats;
- struct lbs_mesh_stats mstats;
+ /* Debugfs */
struct dentry *debugfs_dir;
struct dentry *debugfs_debug;
struct dentry *debugfs_files[6];
-
struct dentry *events_dir;
struct dentry *debugfs_events_files[6];
-
struct dentry *regs_dir;
struct dentry *debugfs_regs_files[6];
+ /* Hardware debugging */
u32 mac_offset;
u32 bbp_offset;
u32 rf_offset;
+ struct lbs_offset_value offsetvalue;
- /* Download sent:
- bit0 1/0=data_sent/data_tx_done,
- bit1 1/0=cmd_sent/cmd_tx_done,
- all other bits reserved 0 */
- u8 dnld_sent;
-
- /** thread to service interrupts */
- struct task_struct *main_thread;
- wait_queue_head_t waitq;
- struct workqueue_struct *work_thread;
-
- struct work_struct mcast_work;
+ /* Power management */
+ u16 psmode;
+ u32 psstate;
+ u8 needtowakeup;
- /** Scanning */
- struct delayed_work scan_work;
- struct delayed_work assoc_work;
- struct work_struct sync_channel;
- /* remember which channel was scanned last, != 0 if currently scanning */
- int scan_channel;
- u8 scan_ssid[IW_ESSID_MAX_SIZE + 1];
- u8 scan_ssid_len;
+ /* Deep sleep */
+ int is_deep_sleep;
+ int is_auto_deep_sleep_enabled;
+ int wakeup_dev_required;
+ int is_activity_detected;
+ int auto_deep_sleep_timeout; /* in ms */
+ wait_queue_head_t ds_awake_q;
+ struct timer_list auto_deepsleep_timer;
- /** Hardware access */
+ /* Hardware access */
+ void *card;
+ u8 fw_ready;
+ u8 surpriseremoved;
int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
void (*reset_card) (struct lbs_private *priv);
+ int (*enter_deep_sleep) (struct lbs_private *priv);
+ int (*exit_deep_sleep) (struct lbs_private *priv);
+ int (*reset_deep_sleep_wakeup) (struct lbs_private *priv);
- /* Wake On LAN */
- uint32_t wol_criteria;
- uint8_t wol_gpio;
- uint8_t wol_gap;
-
- /** Wlan adapter data structure*/
- /** STATUS variables */
+ /* Adapter info (from EEPROM) */
u32 fwrelease;
u32 fwcapinfo;
+ u16 regioncode;
+ u8 current_addr[ETH_ALEN];
- struct mutex lock;
-
- /* TX packet ready to be sent... */
- int tx_pending_len; /* -1 while building packet */
-
- u8 tx_pending_buf[LBS_UPLD_SIZE];
- /* protected by hard_start_xmit serialization */
-
- /** command-related variables */
+ /* Command download */
+ u8 dnld_sent;
+ /* bit0 1/0=data_sent/data_tx_done,
+ bit1 1/0=cmd_sent/cmd_tx_done,
+ all other bits reserved 0 */
u16 seqnum;
-
struct cmd_ctrl_node *cmd_array;
- /** Current command */
struct cmd_ctrl_node *cur_cmd;
- int cur_cmd_retcode;
- /** command Queues */
- /** Free command buffers */
- struct list_head cmdfreeq;
- /** Pending command buffers */
- struct list_head cmdpendingq;
-
+ struct list_head cmdfreeq; /* free command buffers */
+ struct list_head cmdpendingq; /* pending command buffers */
wait_queue_head_t cmd_pending;
+ struct timer_list command_timer;
+ int nr_retries;
+ int cmd_timed_out;
/* Command responses sent from the hardware to the driver */
+ int cur_cmd_retcode;
u8 resp_idx;
u8 resp_buf[2][LBS_UPLD_SIZE];
u32 resp_len[2];
@@ -196,95 +122,76 @@ struct lbs_private {
/* Events sent from hardware to driver */
struct kfifo *event_fifo;
- /* nickname */
- u8 nodename[16];
-
- /** spin locks */
- spinlock_t driver_lock;
-
- /** Timers */
- struct timer_list command_timer;
- int nr_retries;
- int cmd_timed_out;
-
- /** current ssid/bssid related parameters*/
- struct current_bss_params curbssparams;
-
- uint16_t mesh_tlv;
- u8 mesh_ssid[IW_ESSID_MAX_SIZE + 1];
- u8 mesh_ssid_len;
-
- /* IW_MODE_* */
- u8 mode;
-
- /* Scan results list */
- struct list_head network_list;
- struct list_head network_free_list;
- struct bss_descriptor *networks;
-
- u16 beacon_period;
- u8 beacon_enable;
- u8 adhoccreate;
-
- /** capability Info used in Association, start, join */
- u16 capability;
-
- /** MAC address information */
- u8 current_addr[ETH_ALEN];
- u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
- u32 nr_of_multicastmacaddr;
+ /** thread to service interrupts */
+ struct task_struct *main_thread;
+ wait_queue_head_t waitq;
+ struct workqueue_struct *work_thread;
- /** 802.11 statistics */
-// struct cmd_DS_802_11_GET_STAT wlan802_11Stat;
+ /** Encryption stuff */
+ struct lbs_802_11_security secinfo;
+ struct enc_key wpa_mcast_key;
+ struct enc_key wpa_unicast_key;
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ u8 wpa_ie_len;
+ u16 wep_tx_keyidx;
+ struct enc_key wep_keys[4];
- uint16_t enablehwauto;
- uint16_t ratebitmap;
+ /* Wake On LAN */
+ uint32_t wol_criteria;
+ uint8_t wol_gpio;
+ uint8_t wol_gap;
+ /* Transmitting */
+ int tx_pending_len; /* -1 while building packet */
+ u8 tx_pending_buf[LBS_UPLD_SIZE];
+ /* protected by hard_start_xmit serialization */
u8 txretrycount;
-
- /** Tx-related variables (for single packet tx) */
struct sk_buff *currenttxskb;
- /** NIC Operation characteristics */
+ /* Locks */
+ struct mutex lock;
+ spinlock_t driver_lock;
+
+ /* NIC/link operation characteristics */
u16 mac_control;
- u32 connect_status;
- u32 mesh_connect_status;
- u16 regioncode;
+ u8 radio_on;
+ u8 channel;
s16 txpower_cur;
s16 txpower_min;
s16 txpower_max;
- /** POWER MANAGEMENT AND PnP SUPPORT */
- u8 surpriseremoved;
-
- u16 psmode; /* Wlan802_11PowermodeCAM=disable
- Wlan802_11PowermodeMAX_PSP=enable */
- u32 psstate;
- u8 needtowakeup;
+ /** Scanning */
+ struct delayed_work scan_work;
+ int scan_channel;
+ /* remember which channel was scanned last, != 0 if currently scanning */
+ u8 scan_ssid[IEEE80211_MAX_SSID_LEN + 1];
+ u8 scan_ssid_len;
+ /* Associating */
+ struct delayed_work assoc_work;
+ struct current_bss_params curbssparams;
+ u8 mode;
+ struct list_head network_list;
+ struct list_head network_free_list;
+ struct bss_descriptor *networks;
struct assoc_request * pending_assoc_req;
struct assoc_request * in_progress_assoc_req;
+ u16 capability;
+ uint16_t enablehwauto;
+ uint16_t ratebitmap;
- /** Encryption parameter */
- struct lbs_802_11_security secinfo;
-
- /** WEP keys */
- struct enc_key wep_keys[4];
- u16 wep_tx_keyidx;
-
- /** WPA keys */
- struct enc_key wpa_mcast_key;
- struct enc_key wpa_unicast_key;
-
-/*
- * In theory, the IE is limited to the IE length, 255,
- * but in practice 64 bytes are enough.
- */
-#define MAX_WPA_IE_LEN 64
+ /* ADHOC */
+ u16 beacon_period;
+ u8 beacon_enable;
+ u8 adhoccreate;
- /** WPA Information Elements*/
- u8 wpa_ie[MAX_WPA_IE_LEN];
- u8 wpa_ie_len;
+ /* WEXT */
+ char name[DEV_NAME_LEN];
+ u8 nodename[16];
+ struct iw_statistics wstats;
+ u8 cur_rate;
+#define MAX_REGION_CHANNEL_NUM 2
+ struct region_channel region_channel[MAX_REGION_CHANNEL_NUM];
/** Requested Signal Strength*/
u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG];
@@ -294,116 +201,8 @@ struct lbs_private {
u8 rawNF[DEFAULT_DATA_AVG_FACTOR];
u16 nextSNRNF;
u16 numSNRNF;
-
- u8 radio_on;
-
- /** data rate stuff */
- u8 cur_rate;
-
- /** RF calibration data */
-
-#define MAX_REGION_CHANNEL_NUM 2
- /** region channel data */
- struct region_channel region_channel[MAX_REGION_CHANNEL_NUM];
-
- struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM];
-
- /** 11D and Domain Regulatory Data */
- struct lbs_802_11d_domain_reg domainreg;
- struct parsed_region_chan_11d parsed_region_chan;
-
- /** FSM variable for 11d support */
- u32 enable11d;
-
- /** MISCELLANEOUS */
- struct lbs_offset_value offsetvalue;
-
- u32 monitormode;
- u8 fw_ready;
};
extern struct cmd_confirm_sleep confirm_sleep;
-/**
- * @brief Structure used to store information for each beacon/probe response
- */
-struct bss_descriptor {
- u8 bssid[ETH_ALEN];
-
- u8 ssid[IW_ESSID_MAX_SIZE + 1];
- u8 ssid_len;
-
- u16 capability;
- u32 rssi;
- u32 channel;
- u16 beaconperiod;
- __le16 atimwindow;
-
- /* IW_MODE_AUTO, IW_MODE_ADHOC, IW_MODE_INFRA */
- u8 mode;
-
- /* zero-terminated array of supported data rates */
- u8 rates[MAX_RATES + 1];
-
- unsigned long last_scanned;
-
- union ieee_phy_param_set phy;
- union ieee_ss_param_set ss;
-
- struct ieee_ie_country_info_full_set countryinfo;
-
- u8 wpa_ie[MAX_WPA_IE_LEN];
- size_t wpa_ie_len;
- u8 rsn_ie[MAX_WPA_IE_LEN];
- size_t rsn_ie_len;
-
- u8 mesh;
-
- struct list_head list;
-};
-
-/** Association request
- *
- * Encapsulates all the options that describe a specific assocation request
- * or configuration of the wireless card's radio, mode, and security settings.
- */
-struct assoc_request {
-#define ASSOC_FLAG_SSID 1
-#define ASSOC_FLAG_CHANNEL 2
-#define ASSOC_FLAG_BAND 3
-#define ASSOC_FLAG_MODE 4
-#define ASSOC_FLAG_BSSID 5
-#define ASSOC_FLAG_WEP_KEYS 6
-#define ASSOC_FLAG_WEP_TX_KEYIDX 7
-#define ASSOC_FLAG_WPA_MCAST_KEY 8
-#define ASSOC_FLAG_WPA_UCAST_KEY 9
-#define ASSOC_FLAG_SECINFO 10
-#define ASSOC_FLAG_WPA_IE 11
- unsigned long flags;
-
- u8 ssid[IW_ESSID_MAX_SIZE + 1];
- u8 ssid_len;
- u8 channel;
- u8 band;
- u8 mode;
- u8 bssid[ETH_ALEN] __attribute__ ((aligned (2)));
-
- /** WEP keys */
- struct enc_key wep_keys[4];
- u16 wep_tx_keyidx;
-
- /** WPA keys */
- struct enc_key wpa_mcast_key;
- struct enc_key wpa_unicast_key;
-
- struct lbs_802_11_security secinfo;
-
- /** WPA Information Elements*/
- u8 wpa_ie[MAX_WPA_IE_LEN];
- u8 wpa_ie_len;
-
- /* BSS to associate with for infrastructure of Ad-Hoc join */
- struct bss_descriptor bss;
-};
-
#endif
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
index 53d56ab83c0..63d020374c2 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -8,17 +8,8 @@
#include "dev.h"
#include "wext.h"
#include "cmd.h"
+#include "mesh.h"
-static const char * mesh_stat_strings[]= {
- "drop_duplicate_bcast",
- "drop_ttl_zero",
- "drop_no_fwd_route",
- "drop_no_buffers",
- "fwded_unicast_cnt",
- "fwded_bcast_cnt",
- "drop_blind_table",
- "tx_failed_cnt"
-};
static void lbs_ethtool_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
@@ -73,73 +64,6 @@ out:
return ret;
}
-static void lbs_ethtool_get_stats(struct net_device *dev,
- struct ethtool_stats *stats, uint64_t *data)
-{
- struct lbs_private *priv = dev->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- int ret;
-
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-
- /* Get Mesh Statistics */
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
-
- if (ret) {
- memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
- return;
- }
-
- priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
- priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
- priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
- priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
- priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
- priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
- priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
- priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
-
- data[0] = priv->mstats.fwd_drop_rbt;
- data[1] = priv->mstats.fwd_drop_ttl;
- data[2] = priv->mstats.fwd_drop_noroute;
- data[3] = priv->mstats.fwd_drop_nobuf;
- data[4] = priv->mstats.fwd_unicast_cnt;
- data[5] = priv->mstats.fwd_bcast_cnt;
- data[6] = priv->mstats.drop_blind;
- data[7] = priv->mstats.tx_failed_cnt;
-
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-}
-
-static int lbs_ethtool_get_sset_count(struct net_device *dev, int sset)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
- return MESH_STATS_NUM;
-
- return -EOPNOTSUPP;
-}
-
-static void lbs_ethtool_get_strings(struct net_device *dev,
- uint32_t stringset, uint8_t *s)
-{
- int i;
-
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-
- switch (stringset) {
- case ETH_SS_STATS:
- for (i=0; i < MESH_STATS_NUM; i++) {
- memcpy(s + i * ETH_GSTRING_LEN,
- mesh_stat_strings[i],
- ETH_GSTRING_LEN);
- }
- break;
- }
- lbs_deb_enter(LBS_DEB_ETHTOOL);
-}
-
static void lbs_ethtool_get_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
@@ -190,9 +114,9 @@ const struct ethtool_ops lbs_ethtool_ops = {
.get_drvinfo = lbs_ethtool_get_drvinfo,
.get_eeprom = lbs_ethtool_get_eeprom,
.get_eeprom_len = lbs_ethtool_get_eeprom_len,
- .get_sset_count = lbs_ethtool_get_sset_count,
- .get_ethtool_stats = lbs_ethtool_get_stats,
- .get_strings = lbs_ethtool_get_strings,
+ .get_sset_count = lbs_mesh_ethtool_get_sset_count,
+ .get_ethtool_stats = lbs_mesh_ethtool_get_stats,
+ .get_strings = lbs_mesh_ethtool_get_strings,
.get_wol = lbs_ethtool_get_wol,
.set_wol = lbs_ethtool_set_wol,
};
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
index fe8f0cb737b..3809c0b4946 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/libertas/host.h
@@ -1,201 +1,190 @@
/**
- * This file contains definitions of WLAN commands.
+ * This file function prototypes, data structure
+ * and definitions for all the host/station commands
*/
#ifndef _LBS_HOST_H_
#define _LBS_HOST_H_
-/** PUBLIC DEFINITIONS */
-#define DEFAULT_AD_HOC_CHANNEL 6
-#define DEFAULT_AD_HOC_CHANNEL_A 36
+#include "types.h"
+#include "defs.h"
-#define CMD_OPTION_WAITFORRSP 0x0002
+#define DEFAULT_AD_HOC_CHANNEL 6
+
+#define CMD_OPTION_WAITFORRSP 0x0002
/** Host command IDs */
/* Return command are almost always the same as the host command, but with
* bit 15 set high. There are a few exceptions, though...
*/
-#define CMD_RET(cmd) (0x8000 | cmd)
+#define CMD_RET(cmd) (0x8000 | cmd)
/* Return command convention exceptions: */
-#define CMD_RET_802_11_ASSOCIATE 0x8012
+#define CMD_RET_802_11_ASSOCIATE 0x8012
/* Command codes */
-#define CMD_GET_HW_SPEC 0x0003
-#define CMD_EEPROM_UPDATE 0x0004
-#define CMD_802_11_RESET 0x0005
-#define CMD_802_11_SCAN 0x0006
-#define CMD_802_11_GET_LOG 0x000b
-#define CMD_MAC_MULTICAST_ADR 0x0010
-#define CMD_802_11_AUTHENTICATE 0x0011
-#define CMD_802_11_EEPROM_ACCESS 0x0059
-#define CMD_802_11_ASSOCIATE 0x0050
-#define CMD_802_11_SET_WEP 0x0013
-#define CMD_802_11_GET_STAT 0x0014
-#define CMD_802_3_GET_STAT 0x0015
-#define CMD_802_11_SNMP_MIB 0x0016
-#define CMD_MAC_REG_MAP 0x0017
-#define CMD_BBP_REG_MAP 0x0018
-#define CMD_MAC_REG_ACCESS 0x0019
-#define CMD_BBP_REG_ACCESS 0x001a
-#define CMD_RF_REG_ACCESS 0x001b
-#define CMD_802_11_RADIO_CONTROL 0x001c
-#define CMD_802_11_RF_CHANNEL 0x001d
-#define CMD_802_11_RF_TX_POWER 0x001e
-#define CMD_802_11_RSSI 0x001f
-#define CMD_802_11_RF_ANTENNA 0x0020
-#define CMD_802_11_PS_MODE 0x0021
-#define CMD_802_11_DATA_RATE 0x0022
-#define CMD_RF_REG_MAP 0x0023
-#define CMD_802_11_DEAUTHENTICATE 0x0024
-#define CMD_802_11_REASSOCIATE 0x0025
-#define CMD_MAC_CONTROL 0x0028
-#define CMD_802_11_AD_HOC_START 0x002b
-#define CMD_802_11_AD_HOC_JOIN 0x002c
-#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e
-#define CMD_802_11_ENABLE_RSN 0x002f
-#define CMD_802_11_SET_AFC 0x003c
-#define CMD_802_11_GET_AFC 0x003d
-#define CMD_802_11_AD_HOC_STOP 0x0040
-#define CMD_802_11_HOST_SLEEP_CFG 0x0043
-#define CMD_802_11_WAKEUP_CONFIRM 0x0044
-#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045
-#define CMD_802_11_BEACON_STOP 0x0049
-#define CMD_802_11_MAC_ADDRESS 0x004d
-#define CMD_802_11_LED_GPIO_CTRL 0x004e
-#define CMD_802_11_EEPROM_ACCESS 0x0059
-#define CMD_802_11_BAND_CONFIG 0x0058
-#define CMD_GSPI_BUS_CONFIG 0x005a
-#define CMD_802_11D_DOMAIN_INFO 0x005b
-#define CMD_802_11_KEY_MATERIAL 0x005e
-#define CMD_802_11_SLEEP_PARAMS 0x0066
-#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067
-#define CMD_802_11_SLEEP_PERIOD 0x0068
-#define CMD_802_11_TPC_CFG 0x0072
-#define CMD_802_11_PA_CFG 0x0073
-#define CMD_802_11_FW_WAKE_METHOD 0x0074
-#define CMD_802_11_SUBSCRIBE_EVENT 0x0075
-#define CMD_802_11_RATE_ADAPT_RATESET 0x0076
-#define CMD_802_11_TX_RATE_QUERY 0x007f
-#define CMD_GET_TSF 0x0080
-#define CMD_BT_ACCESS 0x0087
-#define CMD_FWT_ACCESS 0x0095
-#define CMD_802_11_MONITOR_MODE 0x0098
-#define CMD_MESH_ACCESS 0x009b
-#define CMD_MESH_CONFIG_OLD 0x00a3
-#define CMD_MESH_CONFIG 0x00ac
-#define CMD_SET_BOOT2_VER 0x00a5
-#define CMD_FUNC_INIT 0x00a9
-#define CMD_FUNC_SHUTDOWN 0x00aa
-#define CMD_802_11_BEACON_CTRL 0x00b0
+#define CMD_GET_HW_SPEC 0x0003
+#define CMD_EEPROM_UPDATE 0x0004
+#define CMD_802_11_RESET 0x0005
+#define CMD_802_11_SCAN 0x0006
+#define CMD_802_11_GET_LOG 0x000b
+#define CMD_MAC_MULTICAST_ADR 0x0010
+#define CMD_802_11_AUTHENTICATE 0x0011
+#define CMD_802_11_EEPROM_ACCESS 0x0059
+#define CMD_802_11_ASSOCIATE 0x0050
+#define CMD_802_11_SET_WEP 0x0013
+#define CMD_802_11_GET_STAT 0x0014
+#define CMD_802_3_GET_STAT 0x0015
+#define CMD_802_11_SNMP_MIB 0x0016
+#define CMD_MAC_REG_MAP 0x0017
+#define CMD_BBP_REG_MAP 0x0018
+#define CMD_MAC_REG_ACCESS 0x0019
+#define CMD_BBP_REG_ACCESS 0x001a
+#define CMD_RF_REG_ACCESS 0x001b
+#define CMD_802_11_RADIO_CONTROL 0x001c
+#define CMD_802_11_RF_CHANNEL 0x001d
+#define CMD_802_11_RF_TX_POWER 0x001e
+#define CMD_802_11_RSSI 0x001f
+#define CMD_802_11_RF_ANTENNA 0x0020
+#define CMD_802_11_PS_MODE 0x0021
+#define CMD_802_11_DATA_RATE 0x0022
+#define CMD_RF_REG_MAP 0x0023
+#define CMD_802_11_DEAUTHENTICATE 0x0024
+#define CMD_802_11_REASSOCIATE 0x0025
+#define CMD_MAC_CONTROL 0x0028
+#define CMD_802_11_AD_HOC_START 0x002b
+#define CMD_802_11_AD_HOC_JOIN 0x002c
+#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e
+#define CMD_802_11_ENABLE_RSN 0x002f
+#define CMD_802_11_SET_AFC 0x003c
+#define CMD_802_11_GET_AFC 0x003d
+#define CMD_802_11_DEEP_SLEEP 0x003e
+#define CMD_802_11_AD_HOC_STOP 0x0040
+#define CMD_802_11_HOST_SLEEP_CFG 0x0043
+#define CMD_802_11_WAKEUP_CONFIRM 0x0044
+#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045
+#define CMD_802_11_BEACON_STOP 0x0049
+#define CMD_802_11_MAC_ADDRESS 0x004d
+#define CMD_802_11_LED_GPIO_CTRL 0x004e
+#define CMD_802_11_EEPROM_ACCESS 0x0059
+#define CMD_802_11_BAND_CONFIG 0x0058
+#define CMD_GSPI_BUS_CONFIG 0x005a
+#define CMD_802_11D_DOMAIN_INFO 0x005b
+#define CMD_802_11_KEY_MATERIAL 0x005e
+#define CMD_802_11_SLEEP_PARAMS 0x0066
+#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067
+#define CMD_802_11_SLEEP_PERIOD 0x0068
+#define CMD_802_11_TPC_CFG 0x0072
+#define CMD_802_11_PA_CFG 0x0073
+#define CMD_802_11_FW_WAKE_METHOD 0x0074
+#define CMD_802_11_SUBSCRIBE_EVENT 0x0075
+#define CMD_802_11_RATE_ADAPT_RATESET 0x0076
+#define CMD_802_11_TX_RATE_QUERY 0x007f
+#define CMD_GET_TSF 0x0080
+#define CMD_BT_ACCESS 0x0087
+#define CMD_FWT_ACCESS 0x0095
+#define CMD_802_11_MONITOR_MODE 0x0098
+#define CMD_MESH_ACCESS 0x009b
+#define CMD_MESH_CONFIG_OLD 0x00a3
+#define CMD_MESH_CONFIG 0x00ac
+#define CMD_SET_BOOT2_VER 0x00a5
+#define CMD_FUNC_INIT 0x00a9
+#define CMD_FUNC_SHUTDOWN 0x00aa
+#define CMD_802_11_BEACON_CTRL 0x00b0
/* For the IEEE Power Save */
-#define CMD_SUBCMD_ENTER_PS 0x0030
-#define CMD_SUBCMD_EXIT_PS 0x0031
-#define CMD_SUBCMD_SLEEP_CONFIRMED 0x0034
-#define CMD_SUBCMD_FULL_POWERDOWN 0x0035
-#define CMD_SUBCMD_FULL_POWERUP 0x0036
+#define CMD_SUBCMD_ENTER_PS 0x0030
+#define CMD_SUBCMD_EXIT_PS 0x0031
+#define CMD_SUBCMD_SLEEP_CONFIRMED 0x0034
+#define CMD_SUBCMD_FULL_POWERDOWN 0x0035
+#define CMD_SUBCMD_FULL_POWERUP 0x0036
-#define CMD_ENABLE_RSN 0x0001
-#define CMD_DISABLE_RSN 0x0000
+#define CMD_ENABLE_RSN 0x0001
+#define CMD_DISABLE_RSN 0x0000
-#define CMD_ACT_GET 0x0000
-#define CMD_ACT_SET 0x0001
-#define CMD_ACT_GET_AES 0x0002
-#define CMD_ACT_SET_AES 0x0003
-#define CMD_ACT_REMOVE_AES 0x0004
+#define CMD_ACT_GET 0x0000
+#define CMD_ACT_SET 0x0001
/* Define action or option for CMD_802_11_SET_WEP */
-#define CMD_ACT_ADD 0x0002
-#define CMD_ACT_REMOVE 0x0004
-#define CMD_ACT_USE_DEFAULT 0x0008
-
-#define CMD_TYPE_WEP_40_BIT 0x01
-#define CMD_TYPE_WEP_104_BIT 0x02
+#define CMD_ACT_ADD 0x0002
+#define CMD_ACT_REMOVE 0x0004
-#define CMD_NUM_OF_WEP_KEYS 4
+#define CMD_TYPE_WEP_40_BIT 0x01
+#define CMD_TYPE_WEP_104_BIT 0x02
-#define CMD_WEP_KEY_INDEX_MASK 0x3fff
+#define CMD_NUM_OF_WEP_KEYS 4
-/* Define action or option for CMD_802_11_RESET */
-#define CMD_ACT_HALT 0x0003
+#define CMD_WEP_KEY_INDEX_MASK 0x3fff
/* Define action or option for CMD_802_11_SCAN */
-#define CMD_BSS_TYPE_BSS 0x0001
-#define CMD_BSS_TYPE_IBSS 0x0002
-#define CMD_BSS_TYPE_ANY 0x0003
+#define CMD_BSS_TYPE_BSS 0x0001
+#define CMD_BSS_TYPE_IBSS 0x0002
+#define CMD_BSS_TYPE_ANY 0x0003
/* Define action or option for CMD_802_11_SCAN */
-#define CMD_SCAN_TYPE_ACTIVE 0x0000
-#define CMD_SCAN_TYPE_PASSIVE 0x0001
+#define CMD_SCAN_TYPE_ACTIVE 0x0000
+#define CMD_SCAN_TYPE_PASSIVE 0x0001
-#define CMD_SCAN_RADIO_TYPE_BG 0
+#define CMD_SCAN_RADIO_TYPE_BG 0
-#define CMD_SCAN_PROBE_DELAY_TIME 0
+#define CMD_SCAN_PROBE_DELAY_TIME 0
/* Define action or option for CMD_MAC_CONTROL */
-#define CMD_ACT_MAC_RX_ON 0x0001
-#define CMD_ACT_MAC_TX_ON 0x0002
-#define CMD_ACT_MAC_LOOPBACK_ON 0x0004
-#define CMD_ACT_MAC_WEP_ENABLE 0x0008
-#define CMD_ACT_MAC_INT_ENABLE 0x0010
-#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020
-#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040
-#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080
-#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100
-#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400
+#define CMD_ACT_MAC_RX_ON 0x0001
+#define CMD_ACT_MAC_TX_ON 0x0002
+#define CMD_ACT_MAC_LOOPBACK_ON 0x0004
+#define CMD_ACT_MAC_WEP_ENABLE 0x0008
+#define CMD_ACT_MAC_INT_ENABLE 0x0010
+#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020
+#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040
+#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080
+#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100
+#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400
/* Event flags for CMD_802_11_SUBSCRIBE_EVENT */
-#define CMD_SUBSCRIBE_RSSI_LOW 0x0001
-#define CMD_SUBSCRIBE_SNR_LOW 0x0002
-#define CMD_SUBSCRIBE_FAILCOUNT 0x0004
-#define CMD_SUBSCRIBE_BCNMISS 0x0008
-#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010
-#define CMD_SUBSCRIBE_SNR_HIGH 0x0020
+#define CMD_SUBSCRIBE_RSSI_LOW 0x0001
+#define CMD_SUBSCRIBE_SNR_LOW 0x0002
+#define CMD_SUBSCRIBE_FAILCOUNT 0x0004
+#define CMD_SUBSCRIBE_BCNMISS 0x0008
+#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010
+#define CMD_SUBSCRIBE_SNR_HIGH 0x0020
-#define RADIO_PREAMBLE_LONG 0x00
-#define RADIO_PREAMBLE_SHORT 0x02
-#define RADIO_PREAMBLE_AUTO 0x04
+#define RADIO_PREAMBLE_LONG 0x00
+#define RADIO_PREAMBLE_SHORT 0x02
+#define RADIO_PREAMBLE_AUTO 0x04
/* Define action or option for CMD_802_11_RF_CHANNEL */
-#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00
-#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01
+#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00
+#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01
/* Define action or option for CMD_802_11_DATA_RATE */
-#define CMD_ACT_SET_TX_AUTO 0x0000
-#define CMD_ACT_SET_TX_FIX_RATE 0x0001
-#define CMD_ACT_GET_TX_RATE 0x0002
-
-#define CMD_ACT_SET_RX 0x0001
-#define CMD_ACT_SET_TX 0x0002
-#define CMD_ACT_SET_BOTH 0x0003
-#define CMD_ACT_GET_RX 0x0004
-#define CMD_ACT_GET_TX 0x0008
-#define CMD_ACT_GET_BOTH 0x000c
+#define CMD_ACT_SET_TX_AUTO 0x0000
+#define CMD_ACT_SET_TX_FIX_RATE 0x0001
+#define CMD_ACT_GET_TX_RATE 0x0002
/* Define action or option for CMD_802_11_PS_MODE */
-#define CMD_TYPE_CAM 0x0000
-#define CMD_TYPE_MAX_PSP 0x0001
-#define CMD_TYPE_FAST_PSP 0x0002
+#define CMD_TYPE_CAM 0x0000
+#define CMD_TYPE_MAX_PSP 0x0001
+#define CMD_TYPE_FAST_PSP 0x0002
/* Options for CMD_802_11_FW_WAKE_METHOD */
-#define CMD_WAKE_METHOD_UNCHANGED 0x0000
-#define CMD_WAKE_METHOD_COMMAND_INT 0x0001
-#define CMD_WAKE_METHOD_GPIO 0x0002
+#define CMD_WAKE_METHOD_UNCHANGED 0x0000
+#define CMD_WAKE_METHOD_COMMAND_INT 0x0001
+#define CMD_WAKE_METHOD_GPIO 0x0002
/* Object IDs for CMD_802_11_SNMP_MIB */
-#define SNMP_MIB_OID_BSS_TYPE 0x0000
-#define SNMP_MIB_OID_OP_RATE_SET 0x0001
-#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */
-#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */
-#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */
-#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005
-#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006
-#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007
-#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008
-#define SNMP_MIB_OID_11D_ENABLE 0x0009
-#define SNMP_MIB_OID_11H_ENABLE 0x000A
+#define SNMP_MIB_OID_BSS_TYPE 0x0000
+#define SNMP_MIB_OID_OP_RATE_SET 0x0001
+#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */
+#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */
+#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */
+#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005
+#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006
+#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007
+#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008
+#define SNMP_MIB_OID_11D_ENABLE 0x0009
+#define SNMP_MIB_OID_11H_ENABLE 0x000A
/* Define action or option for CMD_BT_ACCESS */
enum cmd_bt_access_opts {
@@ -302,4 +291,672 @@ enum cmd_mesh_config_types {
#define MACREG_INT_CODE_MESH_AUTO_STARTED 35
#define MACREG_INT_CODE_FIRMWARE_READY 48
+
+/* 802.11-related definitions */
+
+/* TxPD descriptor */
+struct txpd {
+ /* union to cope up with later FW revisions */
+ union {
+ /* Current Tx packet status */
+ __le32 tx_status;
+ struct {
+ /* BSS type: client, AP, etc. */
+ u8 bss_type;
+ /* BSS number */
+ u8 bss_num;
+ /* Reserved */
+ __le16 reserved;
+ } bss;
+ } u;
+ /* Tx control */
+ __le32 tx_control;
+ __le32 tx_packet_location;
+ /* Tx packet length */
+ __le16 tx_packet_length;
+ /* First 2 byte of destination MAC address */
+ u8 tx_dest_addr_high[2];
+ /* Last 4 byte of destination MAC address */
+ u8 tx_dest_addr_low[4];
+ /* Pkt Priority */
+ u8 priority;
+ /* Pkt Trasnit Power control */
+ u8 powermgmt;
+ /* Amount of time the packet has been queued (units = 2ms) */
+ u8 pktdelay_2ms;
+ /* reserved */
+ u8 reserved1;
+} __attribute__ ((packed));
+
+/* RxPD Descriptor */
+struct rxpd {
+ /* union to cope up with later FW revisions */
+ union {
+ /* Current Rx packet status */
+ __le16 status;
+ struct {
+ /* BSS type: client, AP, etc. */
+ u8 bss_type;
+ /* BSS number */
+ u8 bss_num;
+ } __attribute__ ((packed)) bss;
+ } __attribute__ ((packed)) u;
+
+ /* SNR */
+ u8 snr;
+
+ /* Tx control */
+ u8 rx_control;
+
+ /* Pkt length */
+ __le16 pkt_len;
+
+ /* Noise Floor */
+ u8 nf;
+
+ /* Rx Packet Rate */
+ u8 rx_rate;
+
+ /* Pkt addr */
+ __le32 pkt_ptr;
+
+ /* Next Rx RxPD addr */
+ __le32 next_rxpd_ptr;
+
+ /* Pkt Priority */
+ u8 priority;
+ u8 reserved[3];
+} __attribute__ ((packed));
+
+struct cmd_header {
+ __le16 command;
+ __le16 size;
+ __le16 seqnum;
+ __le16 result;
+} __attribute__ ((packed));
+
+/* Generic structure to hold all key types. */
+struct enc_key {
+ u16 len;
+ u16 flags; /* KEY_INFO_* from defs.h */
+ u16 type; /* KEY_TYPE_* from defs.h */
+ u8 key[32];
+};
+
+/* lbs_offset_value */
+struct lbs_offset_value {
+ u32 offset;
+ u32 value;
+} __attribute__ ((packed));
+
+/*
+ * Define data structure for CMD_GET_HW_SPEC
+ * This structure defines the response for the GET_HW_SPEC command
+ */
+struct cmd_ds_get_hw_spec {
+ struct cmd_header hdr;
+
+ /* HW Interface version number */
+ __le16 hwifversion;
+ /* HW version number */
+ __le16 version;
+ /* Max number of TxPD FW can handle */
+ __le16 nr_txpd;
+ /* Max no of Multicast address */
+ __le16 nr_mcast_adr;
+ /* MAC address */
+ u8 permanentaddr[6];
+
+ /* region Code */
+ __le16 regioncode;
+
+ /* Number of antenna used */
+ __le16 nr_antenna;
+
+ /* FW release number, example 0x01030304 = 2.3.4p1 */
+ __le32 fwrelease;
+
+ /* Base Address of TxPD queue */
+ __le32 wcb_base;
+ /* Read Pointer of RxPd queue */
+ __le32 rxpd_rdptr;
+
+ /* Write Pointer of RxPd queue */
+ __le32 rxpd_wrptr;
+
+ /*FW/HW capability */
+ __le32 fwcapinfo;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_subscribe_event {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 events;
+
+ /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a
+ * number of TLVs. From the v5.1 manual, those TLVs would add up to
+ * 40 bytes. However, future firmware might add additional TLVs, so I
+ * bump this up a bit.
+ */
+ uint8_t tlv[128];
+} __attribute__ ((packed));
+
+/*
+ * This scan handle Country Information IE(802.11d compliant)
+ * Define data structure for CMD_802_11_SCAN
+ */
+struct cmd_ds_802_11_scan {
+ struct cmd_header hdr;
+
+ uint8_t bsstype;
+ uint8_t bssid[ETH_ALEN];
+ uint8_t tlvbuffer[0];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_scan_rsp {
+ struct cmd_header hdr;
+
+ __le16 bssdescriptsize;
+ uint8_t nr_sets;
+ uint8_t bssdesc_and_tlvbuffer[0];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_get_log {
+ struct cmd_header hdr;
+
+ __le32 mcasttxframe;
+ __le32 failed;
+ __le32 retry;
+ __le32 multiretry;
+ __le32 framedup;
+ __le32 rtssuccess;
+ __le32 rtsfailure;
+ __le32 ackfailure;
+ __le32 rxfrag;
+ __le32 mcastrxframe;
+ __le32 fcserror;
+ __le32 txframe;
+ __le32 wepundecryptable;
+} __attribute__ ((packed));
+
+struct cmd_ds_mac_control {
+ struct cmd_header hdr;
+ __le16 action;
+ u16 reserved;
+} __attribute__ ((packed));
+
+struct cmd_ds_mac_multicast_adr {
+ struct cmd_header hdr;
+ __le16 action;
+ __le16 nr_of_adrs;
+ u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_authenticate {
+ struct cmd_header hdr;
+
+ u8 bssid[ETH_ALEN];
+ u8 authtype;
+ u8 reserved[10];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_deauthenticate {
+ struct cmd_header hdr;
+
+ u8 macaddr[ETH_ALEN];
+ __le16 reasoncode;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_associate {
+ struct cmd_header hdr;
+
+ u8 bssid[6];
+ __le16 capability;
+ __le16 listeninterval;
+ __le16 bcnperiod;
+ u8 dtimperiod;
+ u8 iebuf[512]; /* Enough for required and most optional IEs */
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_associate_response {
+ struct cmd_header hdr;
+
+ __le16 capability;
+ __le16 statuscode;
+ __le16 aid;
+ u8 iebuf[512];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_set_wep {
+ struct cmd_header hdr;
+
+ /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */
+ __le16 action;
+
+ /* key Index selected for Tx */
+ __le16 keyindex;
+
+ /* 40, 128bit or TXWEP */
+ uint8_t keytype[4];
+ uint8_t keymaterial[4][16];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_snmp_mib {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 oid;
+ __le16 bufsize;
+ u8 value[128];
+} __attribute__ ((packed));
+
+struct cmd_ds_mac_reg_access {
+ __le16 action;
+ __le16 offset;
+ __le32 value;
+} __attribute__ ((packed));
+
+struct cmd_ds_bbp_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __attribute__ ((packed));
+
+struct cmd_ds_rf_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_radio_control {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 control;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_beacon_control {
+ __le16 action;
+ __le16 beacon_enable;
+ __le16 beacon_period;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_sleep_params {
+ struct cmd_header hdr;
+
+ /* ACT_GET/ACT_SET */
+ __le16 action;
+
+ /* Sleep clock error in ppm */
+ __le16 error;
+
+ /* Wakeup offset in usec */
+ __le16 offset;
+
+ /* Clock stabilization time in usec */
+ __le16 stabletime;
+
+ /* control periodic calibration */
+ uint8_t calcontrol;
+
+ /* control the use of external sleep clock */
+ uint8_t externalsleepclk;
+
+ /* reserved field, should be set to zero */
+ __le16 reserved;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_rf_channel {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 channel;
+ __le16 rftype; /* unused */
+ __le16 reserved; /* unused */
+ u8 channellist[32]; /* unused */
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_rssi {
+ /* weighting factor */
+ __le16 N;
+
+ __le16 reserved_0;
+ __le16 reserved_1;
+ __le16 reserved_2;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_rssi_rsp {
+ __le16 SNR;
+ __le16 noisefloor;
+ __le16 avgSNR;
+ __le16 avgnoisefloor;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_mac_address {
+ struct cmd_header hdr;
+
+ __le16 action;
+ u8 macadd[ETH_ALEN];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_rf_tx_power {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 curlevel;
+ s8 maxlevel;
+ s8 minlevel;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_monitor_mode {
+ __le16 action;
+ __le16 mode;
+} __attribute__ ((packed));
+
+struct cmd_ds_set_boot2_ver {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 version;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_fw_wake_method {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 method;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_ps_mode {
+ __le16 action;
+ __le16 nullpktinterval;
+ __le16 multipledtim;
+ __le16 reserved;
+ __le16 locallisteninterval;
+} __attribute__ ((packed));
+
+struct cmd_confirm_sleep {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 nullpktinterval;
+ __le16 multipledtim;
+ __le16 reserved;
+ __le16 locallisteninterval;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_data_rate {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 reserved;
+ u8 rates[MAX_RATES];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_rate_adapt_rateset {
+ struct cmd_header hdr;
+ __le16 action;
+ __le16 enablehwauto;
+ __le16 bitmap;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_ad_hoc_start {
+ struct cmd_header hdr;
+
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 bsstype;
+ __le16 beaconperiod;
+ u8 dtimperiod; /* Reserved on v9 and later */
+ struct ieee_ie_ibss_param_set ibss;
+ u8 reserved1[4];
+ struct ieee_ie_ds_param_set ds;
+ u8 reserved2[4];
+ __le16 probedelay; /* Reserved on v9 and later */
+ __le16 capability;
+ u8 rates[MAX_RATES];
+ u8 tlv_memory_size_pad[100];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_ad_hoc_result {
+ struct cmd_header hdr;
+
+ u8 pad[3];
+ u8 bssid[ETH_ALEN];
+} __attribute__ ((packed));
+
+struct adhoc_bssdesc {
+ u8 bssid[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 type;
+ __le16 beaconperiod;
+ u8 dtimperiod;
+ __le64 timestamp;
+ __le64 localtime;
+ struct ieee_ie_ds_param_set ds;
+ u8 reserved1[4];
+ struct ieee_ie_ibss_param_set ibss;
+ u8 reserved2[4];
+ __le16 capability;
+ u8 rates[MAX_RATES];
+
+ /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the
+ * Adhoc join command and will cause a binary layout mismatch with
+ * the firmware
+ */
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_ad_hoc_join {
+ struct cmd_header hdr;
+
+ struct adhoc_bssdesc bss;
+ __le16 failtimeout; /* Reserved on v9 and later */
+ __le16 probedelay; /* Reserved on v9 and later */
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_ad_hoc_stop {
+ struct cmd_header hdr;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_enable_rsn {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 enable;
+} __attribute__ ((packed));
+
+struct MrvlIEtype_keyParamSet {
+ /* type ID */
+ __le16 type;
+
+ /* length of Payload */
+ __le16 length;
+
+ /* type of key: WEP=0, TKIP=1, AES=2 */
+ __le16 keytypeid;
+
+ /* key control Info specific to a keytypeid */
+ __le16 keyinfo;
+
+ /* length of key */
+ __le16 keylen;
+
+ /* key material of size keylen */
+ u8 key[32];
+} __attribute__ ((packed));
+
+#define MAX_WOL_RULES 16
+
+struct host_wol_rule {
+ uint8_t rule_no;
+ uint8_t rule_ops;
+ __le16 sig_offset;
+ __le16 sig_length;
+ __le16 reserve;
+ __be32 sig_mask;
+ __be32 signature;
+} __attribute__ ((packed));
+
+struct wol_config {
+ uint8_t action;
+ uint8_t pattern;
+ uint8_t no_rules_in_cmd;
+ uint8_t result;
+ struct host_wol_rule rule[MAX_WOL_RULES];
+} __attribute__ ((packed));
+
+struct cmd_ds_host_sleep {
+ struct cmd_header hdr;
+ __le32 criteria;
+ uint8_t gpio;
+ uint16_t gap;
+ struct wol_config wol_conf;
+} __attribute__ ((packed));
+
+
+
+struct cmd_ds_802_11_key_material {
+ struct cmd_header hdr;
+
+ __le16 action;
+ struct MrvlIEtype_keyParamSet keyParamSet[2];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_eeprom_access {
+ struct cmd_header hdr;
+ __le16 action;
+ __le16 offset;
+ __le16 len;
+ /* firmware says it returns a maximum of 20 bytes */
+#define LBS_EEPROM_READ_LEN 20
+ u8 value[LBS_EEPROM_READ_LEN];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_tpc_cfg {
+ struct cmd_header hdr;
+
+ __le16 action;
+ uint8_t enable;
+ int8_t P0;
+ int8_t P1;
+ int8_t P2;
+ uint8_t usesnr;
+} __attribute__ ((packed));
+
+
+struct cmd_ds_802_11_pa_cfg {
+ struct cmd_header hdr;
+
+ __le16 action;
+ uint8_t enable;
+ int8_t P0;
+ int8_t P1;
+ int8_t P2;
+} __attribute__ ((packed));
+
+
+struct cmd_ds_802_11_led_ctrl {
+ __le16 action;
+ __le16 numled;
+ u8 data[256];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_afc {
+ __le16 afc_auto;
+ union {
+ struct {
+ __le16 threshold;
+ __le16 period;
+ };
+ struct {
+ __le16 timing_offset; /* signed */
+ __le16 carrier_offset; /* signed */
+ };
+ };
+} __attribute__ ((packed));
+
+struct cmd_tx_rate_query {
+ __le16 txrate;
+} __attribute__ ((packed));
+
+struct cmd_ds_get_tsf {
+ __le64 tsfvalue;
+} __attribute__ ((packed));
+
+struct cmd_ds_bt_access {
+ __le16 action;
+ __le32 id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+} __attribute__ ((packed));
+
+struct cmd_ds_fwt_access {
+ __le16 action;
+ __le32 id;
+ u8 valid;
+ u8 da[ETH_ALEN];
+ u8 dir;
+ u8 ra[ETH_ALEN];
+ __le32 ssn;
+ __le32 dsn;
+ __le32 metric;
+ u8 rate;
+ u8 hopcount;
+ u8 ttl;
+ __le32 expiration;
+ u8 sleepmode;
+ __le32 snr;
+ __le32 references;
+ u8 prec[ETH_ALEN];
+} __attribute__ ((packed));
+
+struct cmd_ds_mesh_config {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le16 channel;
+ __le16 type;
+ __le16 length;
+ u8 data[128]; /* last position reserved */
+} __attribute__ ((packed));
+
+struct cmd_ds_mesh_access {
+ struct cmd_header hdr;
+
+ __le16 action;
+ __le32 data[32]; /* last position reserved */
+} __attribute__ ((packed));
+
+/* Number of stats counters returned by the firmware */
+#define MESH_STATS_NUM 8
+
+struct cmd_ds_command {
+ /* command header */
+ __le16 command;
+ __le16 size;
+ __le16 seqnum;
+ __le16 result;
+
+ /* command Body */
+ union {
+ struct cmd_ds_802_11_ps_mode psmode;
+ struct cmd_ds_802_11_monitor_mode monitor;
+ struct cmd_ds_802_11_rssi rssi;
+ struct cmd_ds_802_11_rssi_rsp rssirsp;
+ struct cmd_ds_mac_reg_access macreg;
+ struct cmd_ds_bbp_reg_access bbpreg;
+ struct cmd_ds_rf_reg_access rfreg;
+
+ struct cmd_ds_802_11_tpc_cfg tpccfg;
+ struct cmd_ds_802_11_afc afc;
+ struct cmd_ds_802_11_led_ctrl ledgpio;
+
+ struct cmd_ds_bt_access bt;
+ struct cmd_ds_fwt_access fwt;
+ struct cmd_ds_802_11_beacon_control bcn_ctrl;
+ } params;
+} __attribute__ ((packed));
+
#endif
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
deleted file mode 100644
index c8a1998d474..00000000000
--- a/drivers/net/wireless/libertas/hostcmd.h
+++ /dev/null
@@ -1,800 +0,0 @@
-/*
- * This file contains the function prototypes, data structure
- * and defines for all the host/station commands
- */
-#ifndef _LBS_HOSTCMD_H
-#define _LBS_HOSTCMD_H
-
-#include <linux/wireless.h>
-#include "11d.h"
-#include "types.h"
-
-/* 802.11-related definitions */
-
-/* TxPD descriptor */
-struct txpd {
- /* union to cope up with later FW revisions */
- union {
- /* Current Tx packet status */
- __le32 tx_status;
- struct {
- /* BSS type: client, AP, etc. */
- u8 bss_type;
- /* BSS number */
- u8 bss_num;
- /* Reserved */
- __le16 reserved;
- } bss;
- } u;
- /* Tx control */
- __le32 tx_control;
- __le32 tx_packet_location;
- /* Tx packet length */
- __le16 tx_packet_length;
- /* First 2 byte of destination MAC address */
- u8 tx_dest_addr_high[2];
- /* Last 4 byte of destination MAC address */
- u8 tx_dest_addr_low[4];
- /* Pkt Priority */
- u8 priority;
- /* Pkt Trasnit Power control */
- u8 powermgmt;
- /* Amount of time the packet has been queued in the driver (units = 2ms) */
- u8 pktdelay_2ms;
- /* reserved */
- u8 reserved1;
-} __attribute__ ((packed));
-
-/* RxPD Descriptor */
-struct rxpd {
- /* union to cope up with later FW revisions */
- union {
- /* Current Rx packet status */
- __le16 status;
- struct {
- /* BSS type: client, AP, etc. */
- u8 bss_type;
- /* BSS number */
- u8 bss_num;
- } __attribute__ ((packed)) bss;
- } __attribute__ ((packed)) u;
-
- /* SNR */
- u8 snr;
-
- /* Tx control */
- u8 rx_control;
-
- /* Pkt length */
- __le16 pkt_len;
-
- /* Noise Floor */
- u8 nf;
-
- /* Rx Packet Rate */
- u8 rx_rate;
-
- /* Pkt addr */
- __le32 pkt_ptr;
-
- /* Next Rx RxPD addr */
- __le32 next_rxpd_ptr;
-
- /* Pkt Priority */
- u8 priority;
- u8 reserved[3];
-} __attribute__ ((packed));
-
-struct cmd_header {
- __le16 command;
- __le16 size;
- __le16 seqnum;
- __le16 result;
-} __attribute__ ((packed));
-
-struct cmd_ctrl_node {
- struct list_head list;
- int result;
- /* command response */
- int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *);
- unsigned long callback_arg;
- /* command data */
- struct cmd_header *cmdbuf;
- /* wait queue */
- u16 cmdwaitqwoken;
- wait_queue_head_t cmdwait_q;
-};
-
-/* Generic structure to hold all key types. */
-struct enc_key {
- u16 len;
- u16 flags; /* KEY_INFO_* from defs.h */
- u16 type; /* KEY_TYPE_* from defs.h */
- u8 key[32];
-};
-
-/* lbs_offset_value */
-struct lbs_offset_value {
- u32 offset;
- u32 value;
-} __attribute__ ((packed));
-
-/* Define general data structure */
-/* cmd_DS_GEN */
-struct cmd_ds_gen {
- __le16 command;
- __le16 size;
- __le16 seqnum;
- __le16 result;
- void *cmdresp[0];
-} __attribute__ ((packed));
-
-#define S_DS_GEN sizeof(struct cmd_ds_gen)
-
-
-/*
- * Define data structure for CMD_GET_HW_SPEC
- * This structure defines the response for the GET_HW_SPEC command
- */
-struct cmd_ds_get_hw_spec {
- struct cmd_header hdr;
-
- /* HW Interface version number */
- __le16 hwifversion;
- /* HW version number */
- __le16 version;
- /* Max number of TxPD FW can handle */
- __le16 nr_txpd;
- /* Max no of Multicast address */
- __le16 nr_mcast_adr;
- /* MAC address */
- u8 permanentaddr[6];
-
- /* region Code */
- __le16 regioncode;
-
- /* Number of antenna used */
- __le16 nr_antenna;
-
- /* FW release number, example 0x01030304 = 2.3.4p1 */
- __le32 fwrelease;
-
- /* Base Address of TxPD queue */
- __le32 wcb_base;
- /* Read Pointer of RxPd queue */
- __le32 rxpd_rdptr;
-
- /* Write Pointer of RxPd queue */
- __le32 rxpd_wrptr;
-
- /*FW/HW capability */
- __le32 fwcapinfo;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_subscribe_event {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 events;
-
- /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a
- * number of TLVs. From the v5.1 manual, those TLVs would add up to
- * 40 bytes. However, future firmware might add additional TLVs, so I
- * bump this up a bit.
- */
- uint8_t tlv[128];
-} __attribute__ ((packed));
-
-/*
- * This scan handle Country Information IE(802.11d compliant)
- * Define data structure for CMD_802_11_SCAN
- */
-struct cmd_ds_802_11_scan {
- struct cmd_header hdr;
-
- uint8_t bsstype;
- uint8_t bssid[ETH_ALEN];
- uint8_t tlvbuffer[0];
-#if 0
- mrvlietypes_ssidparamset_t ssidParamSet;
- mrvlietypes_chanlistparamset_t ChanListParamSet;
- mrvlietypes_ratesparamset_t OpRateSet;
-#endif
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_scan_rsp {
- struct cmd_header hdr;
-
- __le16 bssdescriptsize;
- uint8_t nr_sets;
- uint8_t bssdesc_and_tlvbuffer[0];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_get_log {
- struct cmd_header hdr;
-
- __le32 mcasttxframe;
- __le32 failed;
- __le32 retry;
- __le32 multiretry;
- __le32 framedup;
- __le32 rtssuccess;
- __le32 rtsfailure;
- __le32 ackfailure;
- __le32 rxfrag;
- __le32 mcastrxframe;
- __le32 fcserror;
- __le32 txframe;
- __le32 wepundecryptable;
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_control {
- struct cmd_header hdr;
- __le16 action;
- u16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_multicast_adr {
- struct cmd_header hdr;
- __le16 action;
- __le16 nr_of_adrs;
- u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
-} __attribute__ ((packed));
-
-struct cmd_ds_gspi_bus_config {
- struct cmd_header hdr;
- __le16 action;
- __le16 bus_delay_mode;
- __le16 host_time_delay_to_read_port;
- __le16 host_time_delay_to_read_register;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_authenticate {
- struct cmd_header hdr;
-
- u8 bssid[ETH_ALEN];
- u8 authtype;
- u8 reserved[10];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_deauthenticate {
- struct cmd_header hdr;
-
- u8 macaddr[ETH_ALEN];
- __le16 reasoncode;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_associate {
- struct cmd_header hdr;
-
- u8 bssid[6];
- __le16 capability;
- __le16 listeninterval;
- __le16 bcnperiod;
- u8 dtimperiod;
- u8 iebuf[512]; /* Enough for required and most optional IEs */
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_associate_response {
- struct cmd_header hdr;
-
- __le16 capability;
- __le16 statuscode;
- __le16 aid;
- u8 iebuf[512];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_set_wep {
- struct cmd_header hdr;
-
- /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */
- __le16 action;
-
- /* key Index selected for Tx */
- __le16 keyindex;
-
- /* 40, 128bit or TXWEP */
- uint8_t keytype[4];
- uint8_t keymaterial[4][16];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_3_get_stat {
- __le32 xmitok;
- __le32 rcvok;
- __le32 xmiterror;
- __le32 rcverror;
- __le32 rcvnobuffer;
- __le32 rcvcrcerror;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_get_stat {
- __le32 txfragmentcnt;
- __le32 mcasttxframecnt;
- __le32 failedcnt;
- __le32 retrycnt;
- __le32 Multipleretrycnt;
- __le32 rtssuccesscnt;
- __le32 rtsfailurecnt;
- __le32 ackfailurecnt;
- __le32 frameduplicatecnt;
- __le32 rxfragmentcnt;
- __le32 mcastrxframecnt;
- __le32 fcserrorcnt;
- __le32 bcasttxframecnt;
- __le32 bcastrxframecnt;
- __le32 txbeacon;
- __le32 rxbeacon;
- __le32 wepundecryptable;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_snmp_mib {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 oid;
- __le16 bufsize;
- u8 value[128];
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_reg_map {
- __le16 buffersize;
- u8 regmap[128];
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_bbp_reg_map {
- __le16 buffersize;
- u8 regmap[128];
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_rf_reg_map {
- __le16 buffersize;
- u8 regmap[64];
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_mac_reg_access {
- __le16 action;
- __le16 offset;
- __le32 value;
-} __attribute__ ((packed));
-
-struct cmd_ds_bbp_reg_access {
- __le16 action;
- __le16 offset;
- u8 value;
- u8 reserved[3];
-} __attribute__ ((packed));
-
-struct cmd_ds_rf_reg_access {
- __le16 action;
- __le16 offset;
- u8 value;
- u8 reserved[3];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_radio_control {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 control;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_beacon_control {
- __le16 action;
- __le16 beacon_enable;
- __le16 beacon_period;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_sleep_params {
- struct cmd_header hdr;
-
- /* ACT_GET/ACT_SET */
- __le16 action;
-
- /* Sleep clock error in ppm */
- __le16 error;
-
- /* Wakeup offset in usec */
- __le16 offset;
-
- /* Clock stabilization time in usec */
- __le16 stabletime;
-
- /* control periodic calibration */
- uint8_t calcontrol;
-
- /* control the use of external sleep clock */
- uint8_t externalsleepclk;
-
- /* reserved field, should be set to zero */
- __le16 reserved;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_inactivity_timeout {
- struct cmd_header hdr;
-
- /* ACT_GET/ACT_SET */
- __le16 action;
-
- /* Inactivity timeout in msec */
- __le16 timeout;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rf_channel {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 channel;
- __le16 rftype; /* unused */
- __le16 reserved; /* unused */
- u8 channellist[32]; /* unused */
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rssi {
- /* weighting factor */
- __le16 N;
-
- __le16 reserved_0;
- __le16 reserved_1;
- __le16 reserved_2;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rssi_rsp {
- __le16 SNR;
- __le16 noisefloor;
- __le16 avgSNR;
- __le16 avgnoisefloor;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_mac_address {
- struct cmd_header hdr;
-
- __le16 action;
- u8 macadd[ETH_ALEN];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rf_tx_power {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 curlevel;
- s8 maxlevel;
- s8 minlevel;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rf_antenna {
- __le16 action;
-
- /* Number of antennas or 0xffff(diversity) */
- __le16 antennamode;
-
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_monitor_mode {
- __le16 action;
- __le16 mode;
-} __attribute__ ((packed));
-
-struct cmd_ds_set_boot2_ver {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 version;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_fw_wake_method {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 method;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_sleep_period {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 period;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ps_mode {
- __le16 action;
- __le16 nullpktinterval;
- __le16 multipledtim;
- __le16 reserved;
- __le16 locallisteninterval;
-} __attribute__ ((packed));
-
-struct cmd_confirm_sleep {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 nullpktinterval;
- __le16 multipledtim;
- __le16 reserved;
- __le16 locallisteninterval;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_data_rate {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 reserved;
- u8 rates[MAX_RATES];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_rate_adapt_rateset {
- struct cmd_header hdr;
- __le16 action;
- __le16 enablehwauto;
- __le16 bitmap;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ad_hoc_start {
- struct cmd_header hdr;
-
- u8 ssid[IW_ESSID_MAX_SIZE];
- u8 bsstype;
- __le16 beaconperiod;
- u8 dtimperiod; /* Reserved on v9 and later */
- struct ieee_ie_ibss_param_set ibss;
- u8 reserved1[4];
- struct ieee_ie_ds_param_set ds;
- u8 reserved2[4];
- __le16 probedelay; /* Reserved on v9 and later */
- __le16 capability;
- u8 rates[MAX_RATES];
- u8 tlv_memory_size_pad[100];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ad_hoc_result {
- struct cmd_header hdr;
-
- u8 pad[3];
- u8 bssid[ETH_ALEN];
-} __attribute__ ((packed));
-
-struct adhoc_bssdesc {
- u8 bssid[ETH_ALEN];
- u8 ssid[IW_ESSID_MAX_SIZE];
- u8 type;
- __le16 beaconperiod;
- u8 dtimperiod;
- __le64 timestamp;
- __le64 localtime;
- struct ieee_ie_ds_param_set ds;
- u8 reserved1[4];
- struct ieee_ie_ibss_param_set ibss;
- u8 reserved2[4];
- __le16 capability;
- u8 rates[MAX_RATES];
-
- /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the
- * Adhoc join command and will cause a binary layout mismatch with
- * the firmware
- */
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ad_hoc_join {
- struct cmd_header hdr;
-
- struct adhoc_bssdesc bss;
- __le16 failtimeout; /* Reserved on v9 and later */
- __le16 probedelay; /* Reserved on v9 and later */
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_ad_hoc_stop {
- struct cmd_header hdr;
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_enable_rsn {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 enable;
-} __attribute__ ((packed));
-
-struct MrvlIEtype_keyParamSet {
- /* type ID */
- __le16 type;
-
- /* length of Payload */
- __le16 length;
-
- /* type of key: WEP=0, TKIP=1, AES=2 */
- __le16 keytypeid;
-
- /* key control Info specific to a keytypeid */
- __le16 keyinfo;
-
- /* length of key */
- __le16 keylen;
-
- /* key material of size keylen */
- u8 key[32];
-} __attribute__ ((packed));
-
-#define MAX_WOL_RULES 16
-
-struct host_wol_rule {
- uint8_t rule_no;
- uint8_t rule_ops;
- __le16 sig_offset;
- __le16 sig_length;
- __le16 reserve;
- __be32 sig_mask;
- __be32 signature;
-} __attribute__ ((packed));
-
-struct wol_config {
- uint8_t action;
- uint8_t pattern;
- uint8_t no_rules_in_cmd;
- uint8_t result;
- struct host_wol_rule rule[MAX_WOL_RULES];
-} __attribute__ ((packed));
-
-struct cmd_ds_host_sleep {
- struct cmd_header hdr;
- __le32 criteria;
- uint8_t gpio;
- uint16_t gap;
- struct wol_config wol_conf;
-} __attribute__ ((packed));
-
-
-
-struct cmd_ds_802_11_key_material {
- struct cmd_header hdr;
-
- __le16 action;
- struct MrvlIEtype_keyParamSet keyParamSet[2];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_eeprom_access {
- struct cmd_header hdr;
- __le16 action;
- __le16 offset;
- __le16 len;
- /* firmware says it returns a maximum of 20 bytes */
-#define LBS_EEPROM_READ_LEN 20
- u8 value[LBS_EEPROM_READ_LEN];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_tpc_cfg {
- struct cmd_header hdr;
-
- __le16 action;
- uint8_t enable;
- int8_t P0;
- int8_t P1;
- int8_t P2;
- uint8_t usesnr;
-} __attribute__ ((packed));
-
-
-struct cmd_ds_802_11_pa_cfg {
- struct cmd_header hdr;
-
- __le16 action;
- uint8_t enable;
- int8_t P0;
- int8_t P1;
- int8_t P2;
-} __attribute__ ((packed));
-
-
-struct cmd_ds_802_11_led_ctrl {
- __le16 action;
- __le16 numled;
- u8 data[256];
-} __attribute__ ((packed));
-
-struct cmd_ds_802_11_afc {
- __le16 afc_auto;
- union {
- struct {
- __le16 threshold;
- __le16 period;
- };
- struct {
- __le16 timing_offset; /* signed */
- __le16 carrier_offset; /* signed */
- };
- };
-} __attribute__ ((packed));
-
-struct cmd_tx_rate_query {
- __le16 txrate;
-} __attribute__ ((packed));
-
-struct cmd_ds_get_tsf {
- __le64 tsfvalue;
-} __attribute__ ((packed));
-
-struct cmd_ds_bt_access {
- __le16 action;
- __le32 id;
- u8 addr1[ETH_ALEN];
- u8 addr2[ETH_ALEN];
-} __attribute__ ((packed));
-
-struct cmd_ds_fwt_access {
- __le16 action;
- __le32 id;
- u8 valid;
- u8 da[ETH_ALEN];
- u8 dir;
- u8 ra[ETH_ALEN];
- __le32 ssn;
- __le32 dsn;
- __le32 metric;
- u8 rate;
- u8 hopcount;
- u8 ttl;
- __le32 expiration;
- u8 sleepmode;
- __le32 snr;
- __le32 references;
- u8 prec[ETH_ALEN];
-} __attribute__ ((packed));
-
-
-struct cmd_ds_mesh_config {
- struct cmd_header hdr;
-
- __le16 action;
- __le16 channel;
- __le16 type;
- __le16 length;
- u8 data[128]; /* last position reserved */
-} __attribute__ ((packed));
-
-
-struct cmd_ds_mesh_access {
- struct cmd_header hdr;
-
- __le16 action;
- __le32 data[32]; /* last position reserved */
-} __attribute__ ((packed));
-
-/* Number of stats counters returned by the firmware */
-#define MESH_STATS_NUM 8
-
-struct cmd_ds_command {
- /* command header */
- __le16 command;
- __le16 size;
- __le16 seqnum;
- __le16 result;
-
- /* command Body */
- union {
- struct cmd_ds_802_11_ps_mode psmode;
- struct cmd_ds_802_11_get_stat gstat;
- struct cmd_ds_802_3_get_stat gstat_8023;
- struct cmd_ds_802_11_rf_antenna rant;
- struct cmd_ds_802_11_monitor_mode monitor;
- struct cmd_ds_802_11_rssi rssi;
- struct cmd_ds_802_11_rssi_rsp rssirsp;
- struct cmd_ds_mac_reg_access macreg;
- struct cmd_ds_bbp_reg_access bbpreg;
- struct cmd_ds_rf_reg_access rfreg;
-
- struct cmd_ds_802_11d_domain_info domaininfo;
- struct cmd_ds_802_11d_domain_info domaininforesp;
-
- struct cmd_ds_802_11_tpc_cfg tpccfg;
- struct cmd_ds_802_11_afc afc;
- struct cmd_ds_802_11_led_ctrl ledgpio;
-
- struct cmd_tx_rate_query txrate;
- struct cmd_ds_bt_access bt;
- struct cmd_ds_fwt_access fwt;
- struct cmd_ds_get_tsf gettsf;
- struct cmd_ds_802_11_beacon_control bcn_ctrl;
- } params;
-} __attribute__ ((packed));
-
-#endif
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index 62381768f2d..1f6cb58dd66 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -48,6 +48,7 @@
MODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>");
MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("libertas_cs_helper.fw");
@@ -590,7 +591,7 @@ static int if_cs_prog_helper(struct if_cs_card *card)
/* TODO: make firmware file configurable */
ret = request_firmware(&fw, "libertas_cs_helper.fw",
- &handle_to_dev(card->p_dev));
+ &card->p_dev->dev);
if (ret) {
lbs_pr_err("can't load helper firmware\n");
ret = -ENODEV;
@@ -663,7 +664,7 @@ static int if_cs_prog_real(struct if_cs_card *card)
/* TODO: make firmware file configurable */
ret = request_firmware(&fw, "libertas_cs.fw",
- &handle_to_dev(card->p_dev));
+ &card->p_dev->dev);
if (ret) {
lbs_pr_err("can't load firmware\n");
ret = -ENODEV;
@@ -793,18 +794,37 @@ static void if_cs_release(struct pcmcia_device *p_dev)
* configure the card at this point -- we wait until we receive a card
* insertion event.
*/
+
+static int if_cs_ioprobe(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ if (cfg->io.nwin != 1) {
+ lbs_pr_err("wrong CIS (check number of IO windows)\n");
+ return -ENODEV;
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
static int if_cs_probe(struct pcmcia_device *p_dev)
{
int ret = -ENOMEM;
unsigned int prod_id;
struct lbs_private *priv;
struct if_cs_card *card;
- /* CIS parsing */
- tuple_t tuple;
- cisparse_t parse;
- cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
- cistpl_io_t *io = &cfg->io;
- u_char buf[64];
lbs_deb_enter(LBS_DEB_CS);
@@ -818,48 +838,15 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
p_dev->irq.Handler = NULL;
- p_dev->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
p_dev->conf.Attributes = 0;
p_dev->conf.IntType = INT_MEMORY_AND_IO;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- if ((ret = pcmcia_get_first_tuple(p_dev, &tuple)) != 0 ||
- (ret = pcmcia_get_tuple_data(p_dev, &tuple)) != 0 ||
- (ret = pcmcia_parse_tuple(&tuple, &parse)) != 0)
- {
- lbs_pr_err("error in pcmcia_get_first_tuple etc\n");
- goto out1;
- }
-
- p_dev->conf.ConfigIndex = cfg->index;
-
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1) {
- p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
- }
-
- /* IO window settings */
- if (cfg->io.nwin != 1) {
- lbs_pr_err("wrong CIS (check number of IO windows)\n");
- ret = -ENODEV;
+ if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) {
+ lbs_pr_err("error in pcmcia_loop_config\n");
goto out1;
}
- p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- p_dev->io.BasePort1 = io->win[0].base;
- p_dev->io.NumPorts1 = io->win[0].len;
- /* This reserves IO space but doesn't actually enable it */
- ret = pcmcia_request_io(p_dev, &p_dev->io);
- if (ret) {
- lbs_pr_err("error in pcmcia_request_io\n");
- goto out1;
- }
/*
* Allocate an interrupt line. Note that this does not assign
@@ -946,6 +933,9 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
card->priv = priv;
priv->card = card;
priv->hw_host_to_card = if_cs_host_to_card;
+ priv->enter_deep_sleep = NULL;
+ priv->exit_deep_sleep = NULL;
+ priv->reset_deep_sleep_wakeup = NULL;
priv->fw_ready = 1;
/* Now actually get the IRQ */
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 485a8d40652..7a73f625273 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -99,6 +99,12 @@ static struct if_sdio_model if_sdio_models[] = {
.firmware = "sd8688.bin",
},
};
+MODULE_FIRMWARE("sd8385_helper.bin");
+MODULE_FIRMWARE("sd8385.bin");
+MODULE_FIRMWARE("sd8686_helper.bin");
+MODULE_FIRMWARE("sd8686.bin");
+MODULE_FIRMWARE("sd8688_helper.bin");
+MODULE_FIRMWARE("sd8688.bin");
struct if_sdio_packet {
struct if_sdio_packet *next;
@@ -831,6 +837,58 @@ out:
return ret;
}
+static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
+{
+ int ret = -1;
+ struct cmd_header cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ lbs_deb_sdio("send DEEP_SLEEP command\n");
+ ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
+ lbs_cmd_copyback, (unsigned long) &cmd);
+ if (ret)
+ lbs_pr_err("DEEP_SLEEP cmd failed\n");
+
+ mdelay(200);
+ return ret;
+}
+
+static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret = -1;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+ sdio_claim_host(card->func);
+
+ sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+ if (ret)
+ lbs_pr_err("sdio_writeb failed!\n");
+
+ sdio_release_host(card->func);
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+}
+
+static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
+{
+ struct if_sdio_card *card = priv->card;
+ int ret = -1;
+
+ lbs_deb_enter(LBS_DEB_SDIO);
+ sdio_claim_host(card->func);
+
+ sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
+ if (ret)
+ lbs_pr_err("sdio_writeb failed!\n");
+
+ sdio_release_host(card->func);
+ lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+ return ret;
+
+}
+
/*******************************************************************/
/* SDIO callbacks */
/*******************************************************************/
@@ -859,6 +917,7 @@ static void if_sdio_interrupt(struct sdio_func *func)
* Ignore the define name, this really means the card has
* successfully received the command.
*/
+ card->priv->is_activity_detected = 1;
if (cause & IF_SDIO_H_INT_DNLD)
lbs_host_to_card_done(card->priv);
@@ -934,7 +993,7 @@ static int if_sdio_probe(struct sdio_func *func,
}
if (i == ARRAY_SIZE(if_sdio_models)) {
- lbs_pr_err("unkown card model 0x%x\n", card->model);
+ lbs_pr_err("unknown card model 0x%x\n", card->model);
ret = -ENODEV;
goto free;
}
@@ -998,6 +1057,9 @@ static int if_sdio_probe(struct sdio_func *func,
priv->card = card;
priv->hw_host_to_card = if_sdio_host_to_card;
+ priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
+ priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
+ priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
priv->fw_ready = 1;
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h
index 60c9b2fcef0..12179c1dc9c 100644
--- a/drivers/net/wireless/libertas/if_sdio.h
+++ b/drivers/net/wireless/libertas/if_sdio.h
@@ -51,5 +51,6 @@
#define IF_SDIO_EVENT 0x80fc
#define IF_SDIO_BLOCK_SIZE 256
-
+#define CONFIGURATION_REG 0x03
+#define HOST_POWER_UP (0x1U << 1)
#endif
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index 5b3672c4d0c..bf4bfbae622 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -32,12 +32,6 @@
#include "dev.h"
#include "if_spi.h"
-struct if_spi_packet {
- struct list_head list;
- u16 blen;
- u8 buffer[0] __attribute__((aligned(4)));
-};
-
struct if_spi_card {
struct spi_device *spi;
struct lbs_private *priv;
@@ -66,33 +60,10 @@ struct if_spi_card {
struct semaphore spi_thread_terminated;
u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE];
-
- /* A buffer of incoming packets from libertas core.
- * Since we can't sleep in hw_host_to_card, we have to buffer
- * them. */
- struct list_head cmd_packet_list;
- struct list_head data_packet_list;
-
- /* Protects cmd_packet_list and data_packet_list */
- spinlock_t buffer_lock;
};
static void free_if_spi_card(struct if_spi_card *card)
{
- struct list_head *cursor, *next;
- struct if_spi_packet *packet;
-
- BUG_ON(card->run_thread);
- list_for_each_safe(cursor, next, &card->cmd_packet_list) {
- packet = container_of(cursor, struct if_spi_packet, list);
- list_del(&packet->list);
- kfree(packet);
- }
- list_for_each_safe(cursor, next, &card->data_packet_list) {
- packet = container_of(cursor, struct if_spi_packet, list);
- list_del(&packet->list);
- kfree(packet);
- }
spi_set_drvdata(card->spi, NULL);
kfree(card);
}
@@ -774,40 +745,6 @@ out:
return err;
}
-/* Move data or a command from the host to the card. */
-static void if_spi_h2c(struct if_spi_card *card,
- struct if_spi_packet *packet, int type)
-{
- int err = 0;
- u16 int_type, port_reg;
-
- switch (type) {
- case MVMS_DAT:
- int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER;
- port_reg = IF_SPI_DATA_RDWRPORT_REG;
- break;
- case MVMS_CMD:
- int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER;
- port_reg = IF_SPI_CMD_RDWRPORT_REG;
- break;
- default:
- lbs_pr_err("can't transfer buffer of type %d\n", type);
- err = -EINVAL;
- goto out;
- }
-
- /* Write the data to the card */
- err = spu_write(card, port_reg, packet->buffer, packet->blen);
- if (err)
- goto out;
-
-out:
- kfree(packet);
-
- if (err)
- lbs_pr_err("%s: error %d\n", __func__, err);
-}
-
/* Inform the host about a card event */
static void if_spi_e2h(struct if_spi_card *card)
{
@@ -837,8 +774,6 @@ static int lbs_spi_thread(void *data)
int err;
struct if_spi_card *card = data;
u16 hiStatus;
- unsigned long flags;
- struct if_spi_packet *packet;
while (1) {
/* Wait to be woken up by one of two things. First, our ISR
@@ -877,43 +812,9 @@ static int lbs_spi_thread(void *data)
if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY ||
(card->priv->psstate != PS_STATE_FULL_POWER &&
(hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) {
- /* This means two things. First of all,
- * if there was a previous command sent, the card has
- * successfully received it.
- * Secondly, it is now ready to download another
- * command.
- */
lbs_host_to_card_done(card->priv);
-
- /* Do we have any command packets from the host to
- * send? */
- packet = NULL;
- spin_lock_irqsave(&card->buffer_lock, flags);
- if (!list_empty(&card->cmd_packet_list)) {
- packet = (struct if_spi_packet *)(card->
- cmd_packet_list.next);
- list_del(&packet->list);
- }
- spin_unlock_irqrestore(&card->buffer_lock, flags);
-
- if (packet)
- if_spi_h2c(card, packet, MVMS_CMD);
}
- if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) {
- /* Do we have any data packets from the host to
- * send? */
- packet = NULL;
- spin_lock_irqsave(&card->buffer_lock, flags);
- if (!list_empty(&card->data_packet_list)) {
- packet = (struct if_spi_packet *)(card->
- data_packet_list.next);
- list_del(&packet->list);
- }
- spin_unlock_irqrestore(&card->buffer_lock, flags);
- if (packet)
- if_spi_h2c(card, packet, MVMS_DAT);
- }
if (hiStatus & IF_SPI_HIST_CARD_EVENT)
if_spi_e2h(card);
@@ -942,40 +843,18 @@ static int if_spi_host_to_card(struct lbs_private *priv,
u8 type, u8 *buf, u16 nb)
{
int err = 0;
- unsigned long flags;
struct if_spi_card *card = priv->card;
- struct if_spi_packet *packet;
- u16 blen;
lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb);
- if (nb == 0) {
- lbs_pr_err("%s: invalid size requested: %d\n", __func__, nb);
- err = -EINVAL;
- goto out;
- }
- blen = ALIGN(nb, 4);
- packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC);
- if (!packet) {
- err = -ENOMEM;
- goto out;
- }
- packet->blen = blen;
- memcpy(packet->buffer, buf, nb);
- memset(packet->buffer + nb, 0, blen - nb);
+ nb = ALIGN(nb, 4);
switch (type) {
case MVMS_CMD:
- priv->dnld_sent = DNLD_CMD_SENT;
- spin_lock_irqsave(&card->buffer_lock, flags);
- list_add_tail(&packet->list, &card->cmd_packet_list);
- spin_unlock_irqrestore(&card->buffer_lock, flags);
+ err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, buf, nb);
break;
case MVMS_DAT:
- priv->dnld_sent = DNLD_DATA_SENT;
- spin_lock_irqsave(&card->buffer_lock, flags);
- list_add_tail(&packet->list, &card->data_packet_list);
- spin_unlock_irqrestore(&card->buffer_lock, flags);
+ err = spu_write(card, IF_SPI_DATA_RDWRPORT_REG, buf, nb);
break;
default:
lbs_pr_err("can't transfer buffer of type %d", type);
@@ -983,9 +862,6 @@ static int if_spi_host_to_card(struct lbs_private *priv,
break;
}
- /* Wake up the spi thread */
- up(&card->spi_ready);
-out:
lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err);
return err;
}
@@ -1026,6 +902,10 @@ static int if_spi_calculate_fw_names(u16 card_id,
chip_id_to_device_name[i].name);
return 0;
}
+MODULE_FIRMWARE("libertas/gspi8385_hlp.bin");
+MODULE_FIRMWARE("libertas/gspi8385.bin");
+MODULE_FIRMWARE("libertas/gspi8686_hlp.bin");
+MODULE_FIRMWARE("libertas/gspi8686.bin");
static int __devinit if_spi_probe(struct spi_device *spi)
{
@@ -1062,9 +942,6 @@ static int __devinit if_spi_probe(struct spi_device *spi)
sema_init(&card->spi_ready, 0);
sema_init(&card->spi_thread_terminated, 0);
- INIT_LIST_HEAD(&card->cmd_packet_list);
- INIT_LIST_HEAD(&card->data_packet_list);
- spin_lock_init(&card->buffer_lock);
/* Initialize the SPI Interface Unit */
err = spu_init(card, pdata->use_dummy_writes);
@@ -1117,6 +994,9 @@ static int __devinit if_spi_probe(struct spi_device *spi)
card->priv = priv;
priv->card = card;
priv->hw_host_to_card = if_spi_host_to_card;
+ priv->enter_deep_sleep = NULL;
+ priv->exit_deep_sleep = NULL;
+ priv->reset_deep_sleep_wakeup = NULL;
priv->fw_ready = 1;
/* Initialize interrupt handling stuff. */
@@ -1138,6 +1018,9 @@ static int __devinit if_spi_probe(struct spi_device *spi)
goto terminate_thread;
}
+ /* poke the IRQ handler so that we don't miss the first interrupt */
+ up(&card->spi_ready);
+
/* Start the card.
* This will call register_netdev, and we'll start
* getting interrupts... */
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index 3fac4efa5ac..65e174595d1 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -28,6 +28,8 @@
static char *lbs_fw_name = "usb8388.bin";
module_param_named(fw_name, lbs_fw_name, charp, 0644);
+MODULE_FIRMWARE("usb8388.bin");
+
static struct usb_device_id if_usb_table[] = {
/* Enter the device signature inside */
{ USB_DEVICE(0x1286, 0x2001) },
@@ -300,6 +302,9 @@ static int if_usb_probe(struct usb_interface *intf,
cardp->priv->fw_ready = 1;
priv->hw_host_to_card = if_usb_host_to_card;
+ priv->enter_deep_sleep = NULL;
+ priv->exit_deep_sleep = NULL;
+ priv->reset_deep_sleep_wakeup = NULL;
#ifdef CONFIG_OLPC
if (machine_is_olpc())
priv->reset_card = if_usb_reset_olpc_card;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 87b4e497faa..db38a5a719f 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -14,11 +14,13 @@
#include <linux/stddef.h>
#include <linux/ieee80211.h>
#include <net/iw_handler.h>
+#include <net/cfg80211.h>
#include "host.h"
#include "decl.h"
#include "dev.h"
#include "wext.h"
+#include "cfg.h"
#include "debugfs.h"
#include "scan.h"
#include "assoc.h"
@@ -43,119 +45,6 @@ module_param_named(libertas_debug, lbs_debug, int, 0644);
struct cmd_confirm_sleep confirm_sleep;
-#define LBS_TX_PWR_DEFAULT 20 /*100mW */
-#define LBS_TX_PWR_US_DEFAULT 20 /*100mW */
-#define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */
-#define LBS_TX_PWR_FR_DEFAULT 20 /*100mW */
-#define LBS_TX_PWR_EMEA_DEFAULT 20 /*100mW */
-
-/* Format { channel, frequency (MHz), maxtxpower } */
-/* band: 'B/G', region: USA FCC/Canada IC */
-static struct chan_freq_power channel_freq_power_US_BG[] = {
- {1, 2412, LBS_TX_PWR_US_DEFAULT},
- {2, 2417, LBS_TX_PWR_US_DEFAULT},
- {3, 2422, LBS_TX_PWR_US_DEFAULT},
- {4, 2427, LBS_TX_PWR_US_DEFAULT},
- {5, 2432, LBS_TX_PWR_US_DEFAULT},
- {6, 2437, LBS_TX_PWR_US_DEFAULT},
- {7, 2442, LBS_TX_PWR_US_DEFAULT},
- {8, 2447, LBS_TX_PWR_US_DEFAULT},
- {9, 2452, LBS_TX_PWR_US_DEFAULT},
- {10, 2457, LBS_TX_PWR_US_DEFAULT},
- {11, 2462, LBS_TX_PWR_US_DEFAULT}
-};
-
-/* band: 'B/G', region: Europe ETSI */
-static struct chan_freq_power channel_freq_power_EU_BG[] = {
- {1, 2412, LBS_TX_PWR_EMEA_DEFAULT},
- {2, 2417, LBS_TX_PWR_EMEA_DEFAULT},
- {3, 2422, LBS_TX_PWR_EMEA_DEFAULT},
- {4, 2427, LBS_TX_PWR_EMEA_DEFAULT},
- {5, 2432, LBS_TX_PWR_EMEA_DEFAULT},
- {6, 2437, LBS_TX_PWR_EMEA_DEFAULT},
- {7, 2442, LBS_TX_PWR_EMEA_DEFAULT},
- {8, 2447, LBS_TX_PWR_EMEA_DEFAULT},
- {9, 2452, LBS_TX_PWR_EMEA_DEFAULT},
- {10, 2457, LBS_TX_PWR_EMEA_DEFAULT},
- {11, 2462, LBS_TX_PWR_EMEA_DEFAULT},
- {12, 2467, LBS_TX_PWR_EMEA_DEFAULT},
- {13, 2472, LBS_TX_PWR_EMEA_DEFAULT}
-};
-
-/* band: 'B/G', region: Spain */
-static struct chan_freq_power channel_freq_power_SPN_BG[] = {
- {10, 2457, LBS_TX_PWR_DEFAULT},
- {11, 2462, LBS_TX_PWR_DEFAULT}
-};
-
-/* band: 'B/G', region: France */
-static struct chan_freq_power channel_freq_power_FR_BG[] = {
- {10, 2457, LBS_TX_PWR_FR_DEFAULT},
- {11, 2462, LBS_TX_PWR_FR_DEFAULT},
- {12, 2467, LBS_TX_PWR_FR_DEFAULT},
- {13, 2472, LBS_TX_PWR_FR_DEFAULT}
-};
-
-/* band: 'B/G', region: Japan */
-static struct chan_freq_power channel_freq_power_JPN_BG[] = {
- {1, 2412, LBS_TX_PWR_JP_DEFAULT},
- {2, 2417, LBS_TX_PWR_JP_DEFAULT},
- {3, 2422, LBS_TX_PWR_JP_DEFAULT},
- {4, 2427, LBS_TX_PWR_JP_DEFAULT},
- {5, 2432, LBS_TX_PWR_JP_DEFAULT},
- {6, 2437, LBS_TX_PWR_JP_DEFAULT},
- {7, 2442, LBS_TX_PWR_JP_DEFAULT},
- {8, 2447, LBS_TX_PWR_JP_DEFAULT},
- {9, 2452, LBS_TX_PWR_JP_DEFAULT},
- {10, 2457, LBS_TX_PWR_JP_DEFAULT},
- {11, 2462, LBS_TX_PWR_JP_DEFAULT},
- {12, 2467, LBS_TX_PWR_JP_DEFAULT},
- {13, 2472, LBS_TX_PWR_JP_DEFAULT},
- {14, 2484, LBS_TX_PWR_JP_DEFAULT}
-};
-
-/**
- * the structure for channel, frequency and power
- */
-struct region_cfp_table {
- u8 region;
- struct chan_freq_power *cfp_BG;
- int cfp_no_BG;
-};
-
-/**
- * the structure for the mapping between region and CFP
- */
-static struct region_cfp_table region_cfp_table[] = {
- {0x10, /*US FCC */
- channel_freq_power_US_BG,
- ARRAY_SIZE(channel_freq_power_US_BG),
- }
- ,
- {0x20, /*CANADA IC */
- channel_freq_power_US_BG,
- ARRAY_SIZE(channel_freq_power_US_BG),
- }
- ,
- {0x30, /*EU*/ channel_freq_power_EU_BG,
- ARRAY_SIZE(channel_freq_power_EU_BG),
- }
- ,
- {0x31, /*SPAIN*/ channel_freq_power_SPN_BG,
- ARRAY_SIZE(channel_freq_power_SPN_BG),
- }
- ,
- {0x32, /*FRANCE*/ channel_freq_power_FR_BG,
- ARRAY_SIZE(channel_freq_power_FR_BG),
- }
- ,
- {0x40, /*JAPAN*/ channel_freq_power_JPN_BG,
- ARRAY_SIZE(channel_freq_power_JPN_BG),
- }
- ,
-/*Add new region here */
-};
-
/**
* the table to keep region code
*/
@@ -163,13 +52,6 @@ u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] =
{ 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 };
/**
- * 802.11b/g supported bitrates (in 500Kb/s units)
- */
-u8 lbs_bg_rates[MAX_RATES] =
- { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
-0x00, 0x00 };
-
-/**
* FW rate table. FW refers to rates by their index in this table, not by the
* rate value itself. Values of 0x00 are
* reserved positions.
@@ -212,107 +94,9 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
return 0;
}
-/**
- * Attributes exported through sysfs
- */
-
-/**
- * @brief Get function for sysfs attribute anycast_mask
- */
-static ssize_t lbs_anycast_get(struct device *dev,
- struct device_attribute *attr, char * buf)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- int ret;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
-}
-
-/**
- * @brief Set function for sysfs attribute anycast_mask
- */
-static ssize_t lbs_anycast_set(struct device *dev,
- struct device_attribute *attr, const char * buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- uint32_t datum;
- int ret;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
- sscanf(buf, "%x", &datum);
- mesh_access.data[0] = cpu_to_le32(datum);
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute prb_rsp_limit
- */
-static ssize_t lbs_prb_rsp_limit_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- int ret;
- u32 retry_limit;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
- mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
- &mesh_access);
- if (ret)
- return ret;
-
- retry_limit = le32_to_cpu(mesh_access.data[1]);
- return snprintf(buf, 10, "%d\n", retry_limit);
-}
-
-/**
- * @brief Set function for sysfs attribute prb_rsp_limit
- */
-static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_access mesh_access;
- int ret;
- unsigned long retry_limit;
-
- memset(&mesh_access, 0, sizeof(mesh_access));
- mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
-
- if (!strict_strtoul(buf, 10, &retry_limit))
- return -ENOTSUPP;
- if (retry_limit > 15)
- return -ENOTSUPP;
-
- mesh_access.data[1] = cpu_to_le32(retry_limit);
-
- ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
- &mesh_access);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
static int lbs_add_rtap(struct lbs_private *priv);
static void lbs_remove_rtap(struct lbs_private *priv);
-static int lbs_add_mesh(struct lbs_private *priv);
-static void lbs_remove_mesh(struct lbs_private *priv);
/**
@@ -378,74 +162,7 @@ static ssize_t lbs_rtap_set(struct device *dev,
static DEVICE_ATTR(lbs_rtap, 0644, lbs_rtap_get, lbs_rtap_set );
/**
- * Get function for sysfs attribute mesh
- */
-static ssize_t lbs_mesh_get(struct device *dev,
- struct device_attribute *attr, char * buf)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
-}
-
-/**
- * Set function for sysfs attribute mesh
- */
-static ssize_t lbs_mesh_set(struct device *dev,
- struct device_attribute *attr, const char * buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- int enable;
- int ret, action = CMD_ACT_MESH_CONFIG_STOP;
-
- sscanf(buf, "%x", &enable);
- enable = !!enable;
- if (enable == !!priv->mesh_dev)
- return count;
- if (enable)
- action = CMD_ACT_MESH_CONFIG_START;
- ret = lbs_mesh_config(priv, action, priv->curbssparams.channel);
- if (ret)
- return ret;
-
- if (enable)
- lbs_add_mesh(priv);
- else
- lbs_remove_mesh(priv);
-
- return count;
-}
-
-/**
- * lbs_mesh attribute to be exported per ethX interface
- * through sysfs (/sys/class/net/ethX/lbs_mesh)
- */
-static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
-
-/**
- * anycast_mask attribute to be exported per mshX interface
- * through sysfs (/sys/class/net/mshX/anycast_mask)
- */
-static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
-
-/**
- * prb_rsp_limit attribute to be exported per mshX interface
- * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
- */
-static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
- lbs_prb_rsp_limit_set);
-
-static struct attribute *lbs_mesh_sysfs_entries[] = {
- &dev_attr_anycast_mask.attr,
- &dev_attr_prb_rsp_limit.attr,
- NULL,
-};
-
-static struct attribute_group lbs_mesh_attr_group = {
- .attrs = lbs_mesh_sysfs_entries,
-};
-
-/**
- * @brief This function opens the ethX or mshX interface
+ * @brief This function opens the ethX interface
*
* @param dev A pointer to net_device structure
* @return 0 or -EBUSY if monitor mode active
@@ -464,18 +181,12 @@ static int lbs_dev_open(struct net_device *dev)
goto out;
}
- if (dev == priv->mesh_dev) {
- priv->mesh_open = 1;
- priv->mesh_connect_status = LBS_CONNECTED;
- netif_carrier_on(dev);
- } else {
- priv->infra_open = 1;
+ priv->infra_open = 1;
- if (priv->connect_status == LBS_CONNECTED)
- netif_carrier_on(dev);
- else
- netif_carrier_off(dev);
- }
+ if (priv->connect_status == LBS_CONNECTED)
+ netif_carrier_on(dev);
+ else
+ netif_carrier_off(dev);
if (!priv->tx_pending_len)
netif_wake_queue(dev);
@@ -487,33 +198,6 @@ static int lbs_dev_open(struct net_device *dev)
}
/**
- * @brief This function closes the mshX interface
- *
- * @param dev A pointer to net_device structure
- * @return 0
- */
-static int lbs_mesh_stop(struct net_device *dev)
-{
- struct lbs_private *priv = dev->ml_priv;
-
- lbs_deb_enter(LBS_DEB_MESH);
- spin_lock_irq(&priv->driver_lock);
-
- priv->mesh_open = 0;
- priv->mesh_connect_status = LBS_DISCONNECTED;
-
- netif_stop_queue(dev);
- netif_carrier_off(dev);
-
- spin_unlock_irq(&priv->driver_lock);
-
- schedule_work(&priv->mcast_work);
-
- lbs_deb_leave(LBS_DEB_MESH);
- return 0;
-}
-
-/**
* @brief This function closes the ethX interface
*
* @param dev A pointer to net_device structure
@@ -574,15 +258,17 @@ void lbs_host_to_card_done(struct lbs_private *priv)
priv->dnld_sent = DNLD_RES_RECEIVED;
/* Wake main thread if commands are pending */
- if (!priv->cur_cmd || priv->tx_pending_len > 0)
- wake_up_interruptible(&priv->waitq);
+ if (!priv->cur_cmd || priv->tx_pending_len > 0) {
+ if (!priv->wakeup_dev_required)
+ wake_up_interruptible(&priv->waitq);
+ }
spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_leave(LBS_DEB_THREAD);
}
EXPORT_SYMBOL_GPL(lbs_host_to_card_done);
-static int lbs_set_mac_address(struct net_device *dev, void *addr)
+int lbs_set_mac_address(struct net_device *dev, void *addr)
{
int ret = 0;
struct lbs_private *priv = dev->ml_priv;
@@ -716,7 +402,7 @@ static void lbs_set_mcast_worker(struct work_struct *work)
lbs_deb_leave(LBS_DEB_NET);
}
-static void lbs_set_multicast_list(struct net_device *dev)
+void lbs_set_multicast_list(struct net_device *dev)
{
struct lbs_private *priv = dev->ml_priv;
@@ -770,7 +456,8 @@ static int lbs_thread(void *data)
shouldsleep = 0; /* We have a command response */
else if (priv->cur_cmd)
shouldsleep = 1; /* Can't send a command; one already running */
- else if (!list_empty(&priv->cmdpendingq))
+ else if (!list_empty(&priv->cmdpendingq) &&
+ !(priv->wakeup_dev_required))
shouldsleep = 0; /* We have a command to send */
else if (__kfifo_len(priv->event_fifo))
shouldsleep = 0; /* We have an event to process */
@@ -822,6 +509,26 @@ static int lbs_thread(void *data)
}
spin_unlock_irq(&priv->driver_lock);
+ /* Process hardware events, e.g. card removed, link lost */
+ spin_lock_irq(&priv->driver_lock);
+ while (__kfifo_len(priv->event_fifo)) {
+ u32 event;
+ __kfifo_get(priv->event_fifo, (unsigned char *) &event,
+ sizeof(event));
+ spin_unlock_irq(&priv->driver_lock);
+ lbs_process_event(priv, event);
+ spin_lock_irq(&priv->driver_lock);
+ }
+ spin_unlock_irq(&priv->driver_lock);
+
+ if (priv->wakeup_dev_required) {
+ lbs_deb_thread("Waking up device...\n");
+ /* Wake up device */
+ if (priv->exit_deep_sleep(priv))
+ lbs_deb_thread("Wakeup device failed\n");
+ continue;
+ }
+
/* command timeout stuff */
if (priv->cmd_timed_out && priv->cur_cmd) {
struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
@@ -849,18 +556,7 @@ static int lbs_thread(void *data)
}
priv->cmd_timed_out = 0;
- /* Process hardware events, e.g. card removed, link lost */
- spin_lock_irq(&priv->driver_lock);
- while (__kfifo_len(priv->event_fifo)) {
- u32 event;
- __kfifo_get(priv->event_fifo, (unsigned char *) &event,
- sizeof(event));
- spin_unlock_irq(&priv->driver_lock);
- lbs_process_event(priv, event);
- spin_lock_irq(&priv->driver_lock);
- }
- spin_unlock_irq(&priv->driver_lock);
if (!priv->fw_ready)
continue;
@@ -894,6 +590,9 @@ static int lbs_thread(void *data)
(priv->psstate == PS_STATE_PRE_SLEEP))
continue;
+ if (priv->is_deep_sleep)
+ continue;
+
/* Execute the next command */
if (!priv->dnld_sent && !priv->cur_cmd)
lbs_execute_next_command(priv);
@@ -928,6 +627,7 @@ static int lbs_thread(void *data)
}
del_timer(&priv->command_timer);
+ del_timer(&priv->auto_deepsleep_timer);
wake_up_all(&priv->cmd_pending);
lbs_deb_leave(LBS_DEB_THREAD);
@@ -1050,6 +750,62 @@ out:
lbs_deb_leave(LBS_DEB_CMD);
}
+/**
+ * This function put the device back to deep sleep mode when timer expires
+ * and no activity (command, event, data etc.) is detected.
+ */
+static void auto_deepsleep_timer_fn(unsigned long data)
+{
+ struct lbs_private *priv = (struct lbs_private *)data;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (priv->is_activity_detected) {
+ priv->is_activity_detected = 0;
+ } else {
+ if (priv->is_auto_deep_sleep_enabled &&
+ (!priv->wakeup_dev_required) &&
+ (priv->connect_status != LBS_CONNECTED)) {
+ lbs_deb_main("Entering auto deep sleep mode...\n");
+ ret = lbs_prepare_and_send_command(priv,
+ CMD_802_11_DEEP_SLEEP, 0,
+ 0, 0, NULL);
+ if (ret)
+ lbs_pr_err("Enter Deep Sleep command failed\n");
+ }
+ }
+ mod_timer(&priv->auto_deepsleep_timer , jiffies +
+ (priv->auto_deep_sleep_timeout * HZ)/1000);
+ lbs_deb_leave(LBS_DEB_CMD);
+}
+
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv)
+{
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ priv->is_auto_deep_sleep_enabled = 1;
+ if (priv->is_deep_sleep)
+ priv->wakeup_dev_required = 1;
+ mod_timer(&priv->auto_deepsleep_timer ,
+ jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000);
+
+ lbs_deb_leave(LBS_DEB_SDIO);
+ return 0;
+}
+
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
+{
+ lbs_deb_enter(LBS_DEB_SDIO);
+
+ priv->is_auto_deep_sleep_enabled = 0;
+ priv->auto_deep_sleep_timeout = 0;
+ del_timer(&priv->auto_deepsleep_timer);
+
+ lbs_deb_leave(LBS_DEB_SDIO);
+ return 0;
+}
+
static void lbs_sync_channel_worker(struct work_struct *work)
{
struct lbs_private *priv = container_of(work, struct lbs_private,
@@ -1092,18 +848,24 @@ static int lbs_init_adapter(struct lbs_private *priv)
priv->mesh_connect_status = LBS_DISCONNECTED;
priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
priv->mode = IW_MODE_INFRA;
- priv->curbssparams.channel = DEFAULT_AD_HOC_CHANNEL;
+ priv->channel = DEFAULT_AD_HOC_CHANNEL;
priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
priv->radio_on = 1;
priv->enablehwauto = 1;
priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
priv->psmode = LBS802_11POWERMODECAM;
priv->psstate = PS_STATE_FULL_POWER;
+ priv->is_deep_sleep = 0;
+ priv->is_auto_deep_sleep_enabled = 0;
+ priv->wakeup_dev_required = 0;
+ init_waitqueue_head(&priv->ds_awake_q);
mutex_init(&priv->lock);
setup_timer(&priv->command_timer, command_timer_fn,
(unsigned long)priv);
+ setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
+ (unsigned long)priv);
INIT_LIST_HEAD(&priv->cmdfreeq);
INIT_LIST_HEAD(&priv->cmdpendingq);
@@ -1142,6 +904,7 @@ static void lbs_free_adapter(struct lbs_private *priv)
if (priv->event_fifo)
kfifo_free(priv->event_fifo);
del_timer(&priv->command_timer);
+ del_timer(&priv->auto_deepsleep_timer);
kfree(priv->networks);
priv->networks = NULL;
@@ -1168,31 +931,41 @@ static const struct net_device_ops lbs_netdev_ops = {
*/
struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
{
- struct net_device *dev = NULL;
+ struct net_device *dev;
+ struct wireless_dev *wdev;
struct lbs_private *priv = NULL;
lbs_deb_enter(LBS_DEB_MAIN);
/* Allocate an Ethernet device and register it */
- dev = alloc_etherdev(sizeof(struct lbs_private));
- if (!dev) {
- lbs_pr_err("init wlanX device failed\n");
+ wdev = lbs_cfg_alloc(dmdev);
+ if (IS_ERR(wdev)) {
+ lbs_pr_err("cfg80211 init failed\n");
goto done;
}
- priv = netdev_priv(dev);
- dev->ml_priv = priv;
+ /* TODO? */
+ wdev->iftype = NL80211_IFTYPE_STATION;
+ priv = wdev_priv(wdev);
+ priv->wdev = wdev;
if (lbs_init_adapter(priv)) {
lbs_pr_err("failed to initialize adapter structure.\n");
- goto err_init_adapter;
+ goto err_wdev;
}
+ //TODO? dev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES);
+ dev = alloc_netdev(0, "wlan%d", ether_setup);
+ if (!dev) {
+ dev_err(dmdev, "no memory for network device instance\n");
+ goto err_adapter;
+ }
+
+ dev->ieee80211_ptr = wdev;
+ dev->ml_priv = priv;
+ SET_NETDEV_DEV(dev, dmdev);
+ wdev->netdev = dev;
priv->dev = dev;
- priv->card = card;
- priv->mesh_open = 0;
- priv->infra_open = 0;
- /* Setup the OS Interface to our functions */
dev->netdev_ops = &lbs_netdev_ops;
dev->watchdog_timeo = 5 * HZ;
dev->ethtool_ops = &lbs_ethtool_ops;
@@ -1201,7 +974,13 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
#endif
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- SET_NETDEV_DEV(dev, dmdev);
+
+ // TODO: kzalloc + iwm_init_default_profile(iwm, iwm->umac_profile); ??
+
+
+ priv->card = card;
+ priv->infra_open = 0;
+
priv->rtap_net_dev = NULL;
strcpy(dev->name, "wlan%d");
@@ -1211,7 +990,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");
if (IS_ERR(priv->main_thread)) {
lbs_deb_thread("Error creating main thread.\n");
- goto err_init_adapter;
+ goto err_ndev;
}
priv->work_thread = create_singlethread_workqueue("lbs_worker");
@@ -1220,6 +999,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);
INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker);
+ priv->mesh_open = 0;
sprintf(priv->mesh_ssid, "mesh");
priv->mesh_ssid_len = 4;
@@ -1228,9 +1008,15 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
goto done;
-err_init_adapter:
- lbs_free_adapter(priv);
+ err_ndev:
free_netdev(dev);
+
+ err_adapter:
+ lbs_free_adapter(priv);
+
+ err_wdev:
+ lbs_cfg_free(priv);
+
priv = NULL;
done:
@@ -1243,7 +1029,6 @@ EXPORT_SYMBOL_GPL(lbs_add_card);
void lbs_remove_card(struct lbs_private *priv)
{
struct net_device *dev = priv->dev;
- union iwreq_data wrqu;
lbs_deb_enter(LBS_DEB_MAIN);
@@ -1268,15 +1053,19 @@ void lbs_remove_card(struct lbs_private *priv)
lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
}
- memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+ lbs_send_disconnect_notification(priv);
+
+ if (priv->is_deep_sleep) {
+ priv->is_deep_sleep = 0;
+ wake_up_interruptible(&priv->ds_awake_q);
+ }
/* Stop the thread servicing the interrupts */
priv->surpriseremoved = 1;
kthread_stop(priv->main_thread);
lbs_free_adapter(priv);
+ lbs_cfg_free(priv);
priv->dev = NULL;
free_netdev(dev);
@@ -1298,60 +1087,19 @@ int lbs_start_card(struct lbs_private *priv)
if (ret)
goto done;
- /* init 802.11d */
- lbs_init_11d(priv);
-
- if (register_netdev(dev)) {
- lbs_pr_err("cannot register ethX device\n");
+ if (lbs_cfg_register(priv)) {
+ lbs_pr_err("cannot register device\n");
goto done;
}
lbs_update_channel(priv);
- /* Check mesh FW version and appropriately send the mesh start
- * command
+ /*
+ * While rtap isn't related to mesh, only mesh-enabled
+ * firmware implements the rtap functionality via
+ * CMD_802_11_MONITOR_MODE.
*/
- if (priv->mesh_fw_ver == MESH_FW_OLD) {
- /* Enable mesh, if supported, and work out which TLV it uses.
- 0x100 + 291 is an unofficial value used in 5.110.20.pXX
- 0x100 + 37 is the official value used in 5.110.21.pXX
- but we check them in that order because 20.pXX doesn't
- give an error -- it just silently fails. */
-
- /* 5.110.20.pXX firmware will fail the command if the channel
- doesn't match the existing channel. But only if the TLV
- is correct. If the channel is wrong, _BOTH_ versions will
- give an error to 0x100+291, and allow 0x100+37 to succeed.
- It's just that 5.110.20.pXX will not have done anything
- useful */
-
- priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel)) {
- priv->mesh_tlv = TLV_TYPE_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel))
- priv->mesh_tlv = 0;
- }
- } else if (priv->mesh_fw_ver == MESH_FW_NEW) {
- /* 10.0.0.pXX new firmwares should succeed with TLV
- * 0x100+37; Do not invoke command with old TLV.
- */
- priv->mesh_tlv = TLV_TYPE_MESH_ID;
- if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel))
- priv->mesh_tlv = 0;
- }
- if (priv->mesh_tlv) {
- lbs_add_mesh(priv);
-
- if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
- lbs_pr_err("cannot register lbs_mesh attribute\n");
-
- /* While rtap isn't related to mesh, only mesh-enabled
- * firmware implements the rtap functionality via
- * CMD_802_11_MONITOR_MODE.
- */
+ if (lbs_init_mesh(priv)) {
if (device_create_file(&dev->dev, &dev_attr_lbs_rtap))
lbs_pr_err("cannot register lbs_rtap attribute\n");
}
@@ -1385,13 +1133,12 @@ void lbs_stop_card(struct lbs_private *priv)
netif_carrier_off(dev);
lbs_debugfs_remove_one(priv);
- if (priv->mesh_tlv) {
- device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
+ if (lbs_deinit_mesh(priv))
device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
- }
/* Delete the timeout of the currently processing command */
del_timer_sync(&priv->command_timer);
+ del_timer_sync(&priv->auto_deepsleep_timer);
/* Flush pending command nodes */
spin_lock_irqsave(&priv->driver_lock, flags);
@@ -1420,157 +1167,6 @@ out:
EXPORT_SYMBOL_GPL(lbs_stop_card);
-static const struct net_device_ops mesh_netdev_ops = {
- .ndo_open = lbs_dev_open,
- .ndo_stop = lbs_mesh_stop,
- .ndo_start_xmit = lbs_hard_start_xmit,
- .ndo_set_mac_address = lbs_set_mac_address,
- .ndo_set_multicast_list = lbs_set_multicast_list,
-};
-
-/**
- * @brief This function adds mshX interface
- *
- * @param priv A pointer to the struct lbs_private structure
- * @return 0 if successful, -X otherwise
- */
-static int lbs_add_mesh(struct lbs_private *priv)
-{
- struct net_device *mesh_dev = NULL;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_MESH);
-
- /* Allocate a virtual mesh device */
- if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) {
- lbs_deb_mesh("init mshX device failed\n");
- ret = -ENOMEM;
- goto done;
- }
- mesh_dev->ml_priv = priv;
- priv->mesh_dev = mesh_dev;
-
- mesh_dev->netdev_ops = &mesh_netdev_ops;
- mesh_dev->ethtool_ops = &lbs_ethtool_ops;
- memcpy(mesh_dev->dev_addr, priv->dev->dev_addr,
- sizeof(priv->dev->dev_addr));
-
- SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
-
-#ifdef WIRELESS_EXT
- mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def;
-#endif
- mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- /* Register virtual mesh interface */
- ret = register_netdev(mesh_dev);
- if (ret) {
- lbs_pr_err("cannot register mshX virtual interface\n");
- goto err_free;
- }
-
- ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
- if (ret)
- goto err_unregister;
-
- lbs_persist_config_init(mesh_dev);
-
- /* Everything successful */
- ret = 0;
- goto done;
-
-err_unregister:
- unregister_netdev(mesh_dev);
-
-err_free:
- free_netdev(mesh_dev);
-
-done:
- lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
- return ret;
-}
-
-static void lbs_remove_mesh(struct lbs_private *priv)
-{
- struct net_device *mesh_dev;
-
-
- mesh_dev = priv->mesh_dev;
- if (!mesh_dev)
- return;
-
- lbs_deb_enter(LBS_DEB_MESH);
- netif_stop_queue(mesh_dev);
- netif_carrier_off(mesh_dev);
- sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
- lbs_persist_config_remove(mesh_dev);
- unregister_netdev(mesh_dev);
- priv->mesh_dev = NULL;
- free_netdev(mesh_dev);
- lbs_deb_leave(LBS_DEB_MESH);
-}
-
-/**
- * @brief This function finds the CFP in
- * region_cfp_table based on region and band parameter.
- *
- * @param region The region code
- * @param band The band
- * @param cfp_no A pointer to CFP number
- * @return A pointer to CFP
- */
-struct chan_freq_power *lbs_get_region_cfp_table(u8 region, int *cfp_no)
-{
- int i, end;
-
- lbs_deb_enter(LBS_DEB_MAIN);
-
- end = ARRAY_SIZE(region_cfp_table);
-
- for (i = 0; i < end ; i++) {
- lbs_deb_main("region_cfp_table[i].region=%d\n",
- region_cfp_table[i].region);
- if (region_cfp_table[i].region == region) {
- *cfp_no = region_cfp_table[i].cfp_no_BG;
- lbs_deb_leave(LBS_DEB_MAIN);
- return region_cfp_table[i].cfp_BG;
- }
- }
-
- lbs_deb_leave_args(LBS_DEB_MAIN, "ret NULL");
- return NULL;
-}
-
-int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band)
-{
- int ret = 0;
- int i = 0;
-
- struct chan_freq_power *cfp;
- int cfp_no;
-
- lbs_deb_enter(LBS_DEB_MAIN);
-
- memset(priv->region_channel, 0, sizeof(priv->region_channel));
-
- cfp = lbs_get_region_cfp_table(region, &cfp_no);
- if (cfp != NULL) {
- priv->region_channel[i].nrcfp = cfp_no;
- priv->region_channel[i].CFP = cfp;
- } else {
- lbs_deb_main("wrong region code %#x in band B/G\n",
- region);
- ret = -1;
- goto out;
- }
- priv->region_channel[i].valid = 1;
- priv->region_channel[i].region = region;
- priv->region_channel[i].band = band;
- i++;
-out:
- lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
- return ret;
-}
-
void lbs_queue_event(struct lbs_private *priv, u32 event)
{
unsigned long flags;
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c
new file mode 100644
index 00000000000..2f91c9b808a
--- /dev/null
+++ b/drivers/net/wireless/libertas/mesh.c
@@ -0,0 +1,1141 @@
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/kthread.h>
+#include <linux/kfifo.h>
+
+#include "mesh.h"
+#include "decl.h"
+#include "cmd.h"
+
+
+/***************************************************************************
+ * Mesh sysfs support
+ */
+
+/**
+ * Attributes exported through sysfs
+ */
+
+/**
+ * @brief Get function for sysfs attribute anycast_mask
+ */
+static ssize_t lbs_anycast_get(struct device *dev,
+ struct device_attribute *attr, char * buf)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
+}
+
+/**
+ * @brief Set function for sysfs attribute anycast_mask
+ */
+static ssize_t lbs_anycast_set(struct device *dev,
+ struct device_attribute *attr, const char * buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ uint32_t datum;
+ int ret;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ sscanf(buf, "%x", &datum);
+ mesh_access.data[0] = cpu_to_le32(datum);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * @brief Get function for sysfs attribute prb_rsp_limit
+ */
+static ssize_t lbs_prb_rsp_limit_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+ u32 retry_limit;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
+ &mesh_access);
+ if (ret)
+ return ret;
+
+ retry_limit = le32_to_cpu(mesh_access.data[1]);
+ return snprintf(buf, 10, "%d\n", retry_limit);
+}
+
+/**
+ * @brief Set function for sysfs attribute prb_rsp_limit
+ */
+static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+ unsigned long retry_limit;
+
+ memset(&mesh_access, 0, sizeof(mesh_access));
+ mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
+
+ if (!strict_strtoul(buf, 10, &retry_limit))
+ return -ENOTSUPP;
+ if (retry_limit > 15)
+ return -ENOTSUPP;
+
+ mesh_access.data[1] = cpu_to_le32(retry_limit);
+
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
+ &mesh_access);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * Get function for sysfs attribute mesh
+ */
+static ssize_t lbs_mesh_get(struct device *dev,
+ struct device_attribute *attr, char * buf)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
+}
+
+/**
+ * Set function for sysfs attribute mesh
+ */
+static ssize_t lbs_mesh_set(struct device *dev,
+ struct device_attribute *attr, const char * buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ int enable;
+ int ret, action = CMD_ACT_MESH_CONFIG_STOP;
+
+ sscanf(buf, "%x", &enable);
+ enable = !!enable;
+ if (enable == !!priv->mesh_dev)
+ return count;
+ if (enable)
+ action = CMD_ACT_MESH_CONFIG_START;
+ ret = lbs_mesh_config(priv, action, priv->channel);
+ if (ret)
+ return ret;
+
+ if (enable)
+ lbs_add_mesh(priv);
+ else
+ lbs_remove_mesh(priv);
+
+ return count;
+}
+
+/**
+ * lbs_mesh attribute to be exported per ethX interface
+ * through sysfs (/sys/class/net/ethX/lbs_mesh)
+ */
+static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
+
+/**
+ * anycast_mask attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/anycast_mask)
+ */
+static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
+
+/**
+ * prb_rsp_limit attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
+ */
+static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
+ lbs_prb_rsp_limit_set);
+
+static struct attribute *lbs_mesh_sysfs_entries[] = {
+ &dev_attr_anycast_mask.attr,
+ &dev_attr_prb_rsp_limit.attr,
+ NULL,
+};
+
+static struct attribute_group lbs_mesh_attr_group = {
+ .attrs = lbs_mesh_sysfs_entries,
+};
+
+
+
+/***************************************************************************
+ * Initializing and starting, stopping mesh
+ */
+
+/*
+ * Check mesh FW version and appropriately send the mesh start
+ * command
+ */
+int lbs_init_mesh(struct lbs_private *priv)
+{
+ struct net_device *dev = priv->dev;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ if (priv->mesh_fw_ver == MESH_FW_OLD) {
+ /* Enable mesh, if supported, and work out which TLV it uses.
+ 0x100 + 291 is an unofficial value used in 5.110.20.pXX
+ 0x100 + 37 is the official value used in 5.110.21.pXX
+ but we check them in that order because 20.pXX doesn't
+ give an error -- it just silently fails. */
+
+ /* 5.110.20.pXX firmware will fail the command if the channel
+ doesn't match the existing channel. But only if the TLV
+ is correct. If the channel is wrong, _BOTH_ versions will
+ give an error to 0x100+291, and allow 0x100+37 to succeed.
+ It's just that 5.110.20.pXX will not have done anything
+ useful */
+
+ priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
+ priv->channel)) {
+ priv->mesh_tlv = TLV_TYPE_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
+ priv->channel))
+ priv->mesh_tlv = 0;
+ }
+ } else if (priv->mesh_fw_ver == MESH_FW_NEW) {
+ /* 10.0.0.pXX new firmwares should succeed with TLV
+ * 0x100+37; Do not invoke command with old TLV.
+ */
+ priv->mesh_tlv = TLV_TYPE_MESH_ID;
+ if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
+ priv->channel))
+ priv->mesh_tlv = 0;
+ }
+ if (priv->mesh_tlv) {
+ lbs_add_mesh(priv);
+
+ if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
+ lbs_pr_err("cannot register lbs_mesh attribute\n");
+
+ ret = 1;
+ }
+
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+
+int lbs_deinit_mesh(struct lbs_private *priv)
+{
+ struct net_device *dev = priv->dev;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ if (priv->mesh_tlv) {
+ device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
+ ret = 1;
+ }
+
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+
+/**
+ * @brief This function closes the mshX interface
+ *
+ * @param dev A pointer to net_device structure
+ * @return 0
+ */
+static int lbs_mesh_stop(struct net_device *dev)
+{
+ struct lbs_private *priv = dev->ml_priv;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+ spin_lock_irq(&priv->driver_lock);
+
+ priv->mesh_open = 0;
+ priv->mesh_connect_status = LBS_DISCONNECTED;
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ spin_unlock_irq(&priv->driver_lock);
+
+ schedule_work(&priv->mcast_work);
+
+ lbs_deb_leave(LBS_DEB_MESH);
+ return 0;
+}
+
+/**
+ * @brief This function opens the mshX interface
+ *
+ * @param dev A pointer to net_device structure
+ * @return 0 or -EBUSY if monitor mode active
+ */
+static int lbs_mesh_dev_open(struct net_device *dev)
+{
+ struct lbs_private *priv = dev->ml_priv;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_NET);
+
+ spin_lock_irq(&priv->driver_lock);
+
+ if (priv->monitormode) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ priv->mesh_open = 1;
+ priv->mesh_connect_status = LBS_CONNECTED;
+ netif_carrier_on(dev);
+
+ if (!priv->tx_pending_len)
+ netif_wake_queue(dev);
+ out:
+
+ spin_unlock_irq(&priv->driver_lock);
+ lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
+ return ret;
+}
+
+static const struct net_device_ops mesh_netdev_ops = {
+ .ndo_open = lbs_mesh_dev_open,
+ .ndo_stop = lbs_mesh_stop,
+ .ndo_start_xmit = lbs_hard_start_xmit,
+ .ndo_set_mac_address = lbs_set_mac_address,
+ .ndo_set_multicast_list = lbs_set_multicast_list,
+};
+
+/**
+ * @brief This function adds mshX interface
+ *
+ * @param priv A pointer to the struct lbs_private structure
+ * @return 0 if successful, -X otherwise
+ */
+int lbs_add_mesh(struct lbs_private *priv)
+{
+ struct net_device *mesh_dev = NULL;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+
+ /* Allocate a virtual mesh device */
+ mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
+ if (!mesh_dev) {
+ lbs_deb_mesh("init mshX device failed\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+ mesh_dev->ml_priv = priv;
+ priv->mesh_dev = mesh_dev;
+
+ mesh_dev->netdev_ops = &mesh_netdev_ops;
+ mesh_dev->ethtool_ops = &lbs_ethtool_ops;
+ memcpy(mesh_dev->dev_addr, priv->dev->dev_addr,
+ sizeof(priv->dev->dev_addr));
+
+ SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
+
+#ifdef WIRELESS_EXT
+ mesh_dev->wireless_handlers = &mesh_handler_def;
+#endif
+ mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+ /* Register virtual mesh interface */
+ ret = register_netdev(mesh_dev);
+ if (ret) {
+ lbs_pr_err("cannot register mshX virtual interface\n");
+ goto err_free;
+ }
+
+ ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+ if (ret)
+ goto err_unregister;
+
+ lbs_persist_config_init(mesh_dev);
+
+ /* Everything successful */
+ ret = 0;
+ goto done;
+
+err_unregister:
+ unregister_netdev(mesh_dev);
+
+err_free:
+ free_netdev(mesh_dev);
+
+done:
+ lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+ return ret;
+}
+
+void lbs_remove_mesh(struct lbs_private *priv)
+{
+ struct net_device *mesh_dev;
+
+ mesh_dev = priv->mesh_dev;
+ if (!mesh_dev)
+ return;
+
+ lbs_deb_enter(LBS_DEB_MESH);
+ netif_stop_queue(mesh_dev);
+ netif_carrier_off(mesh_dev);
+ sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+ lbs_persist_config_remove(mesh_dev);
+ unregister_netdev(mesh_dev);
+ priv->mesh_dev = NULL;
+ free_netdev(mesh_dev);
+ lbs_deb_leave(LBS_DEB_MESH);
+}
+
+
+
+/***************************************************************************
+ * Sending and receiving
+ */
+struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
+ struct net_device *dev, struct rxpd *rxpd)
+{
+ if (priv->mesh_dev) {
+ if (priv->mesh_fw_ver == MESH_FW_OLD) {
+ if (rxpd->rx_control & RxPD_MESH_FRAME)
+ dev = priv->mesh_dev;
+ } else if (priv->mesh_fw_ver == MESH_FW_NEW) {
+ if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
+ dev = priv->mesh_dev;
+ }
+ }
+ return dev;
+}
+
+
+void lbs_mesh_set_txpd(struct lbs_private *priv,
+ struct net_device *dev, struct txpd *txpd)
+{
+ if (dev == priv->mesh_dev) {
+ if (priv->mesh_fw_ver == MESH_FW_OLD)
+ txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
+ else if (priv->mesh_fw_ver == MESH_FW_NEW)
+ txpd->u.bss.bss_num = MESH_IFACE_ID;
+ }
+}
+
+
+/***************************************************************************
+ * Mesh command handling
+ */
+
+int lbs_cmd_bt_access(struct cmd_ds_command *cmd,
+ u16 cmd_action, void *pdata_buf)
+{
+ struct cmd_ds_bt_access *bt_access = &cmd->params.bt;
+ lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+ cmd->command = cpu_to_le16(CMD_BT_ACCESS);
+ cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) +
+ sizeof(struct cmd_header));
+ cmd->result = 0;
+ bt_access->action = cpu_to_le16(cmd_action);
+
+ switch (cmd_action) {
+ case CMD_ACT_BT_ACCESS_ADD:
+ memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN);
+ lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr",
+ bt_access->addr1, 6);
+ break;
+ case CMD_ACT_BT_ACCESS_DEL:
+ memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN);
+ lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr",
+ bt_access->addr1, 6);
+ break;
+ case CMD_ACT_BT_ACCESS_LIST:
+ bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
+ break;
+ case CMD_ACT_BT_ACCESS_RESET:
+ break;
+ case CMD_ACT_BT_ACCESS_SET_INVERT:
+ bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
+ break;
+ case CMD_ACT_BT_ACCESS_GET_INVERT:
+ break;
+ default:
+ break;
+ }
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+int lbs_cmd_fwt_access(struct cmd_ds_command *cmd,
+ u16 cmd_action, void *pdata_buf)
+{
+ struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt;
+ lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+ cmd->command = cpu_to_le16(CMD_FWT_ACCESS);
+ cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) +
+ sizeof(struct cmd_header));
+ cmd->result = 0;
+
+ if (pdata_buf)
+ memcpy(fwt_access, pdata_buf, sizeof(*fwt_access));
+ else
+ memset(fwt_access, 0, sizeof(*fwt_access));
+
+ fwt_access->action = cpu_to_le16(cmd_action);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
+ struct cmd_ds_mesh_access *cmd)
+{
+ int ret;
+
+ lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+ cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
+ cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
+ cmd->hdr.result = 0;
+
+ cmd->action = cpu_to_le16(cmd_action);
+
+ ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return ret;
+}
+
+static int __lbs_mesh_config_send(struct lbs_private *priv,
+ struct cmd_ds_mesh_config *cmd,
+ uint16_t action, uint16_t type)
+{
+ int ret;
+ u16 command = CMD_MESH_CONFIG_OLD;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ /*
+ * Command id is 0xac for v10 FW along with mesh interface
+ * id in bits 14-13-12.
+ */
+ if (priv->mesh_fw_ver == MESH_FW_NEW)
+ command = CMD_MESH_CONFIG |
+ (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
+
+ cmd->hdr.command = cpu_to_le16(command);
+ cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
+ cmd->hdr.result = 0;
+
+ cmd->type = cpu_to_le16(type);
+ cmd->action = cpu_to_le16(action);
+
+ ret = lbs_cmd_with_response(priv, command, cmd);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return ret;
+}
+
+int lbs_mesh_config_send(struct lbs_private *priv,
+ struct cmd_ds_mesh_config *cmd,
+ uint16_t action, uint16_t type)
+{
+ int ret;
+
+ if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
+ return -EOPNOTSUPP;
+
+ ret = __lbs_mesh_config_send(priv, cmd, action, type);
+ return ret;
+}
+
+/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
+ * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
+ * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
+ * lbs_mesh_config_send.
+ */
+int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_meshie *ie;
+ DECLARE_SSID_BUF(ssid);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.channel = cpu_to_le16(chan);
+ ie = (struct mrvl_meshie *)cmd.data;
+
+ switch (action) {
+ case CMD_ACT_MESH_CONFIG_START:
+ ie->id = WLAN_EID_GENERIC;
+ ie->val.oui[0] = 0x00;
+ ie->val.oui[1] = 0x50;
+ ie->val.oui[2] = 0x43;
+ ie->val.type = MARVELL_MESH_IE_TYPE;
+ ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
+ ie->val.version = MARVELL_MESH_IE_VERSION;
+ ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
+ ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
+ ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
+ ie->val.mesh_id_len = priv->mesh_ssid_len;
+ memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
+ ie->len = sizeof(struct mrvl_meshie_val) -
+ IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len;
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
+ break;
+ case CMD_ACT_MESH_CONFIG_STOP:
+ break;
+ default:
+ return -1;
+ }
+ lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
+ action, priv->mesh_tlv, chan,
+ print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
+
+ return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
+}
+
+
+
+/***************************************************************************
+ * Persistent configuration support
+ */
+
+static int mesh_get_default_parameters(struct device *dev,
+ struct mrvl_mesh_defaults *defs)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ int ret;
+
+ memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
+ CMD_TYPE_MESH_GET_DEFAULTS);
+
+ if (ret)
+ return -EOPNOTSUPP;
+
+ memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
+
+ return 0;
+}
+
+/**
+ * @brief Get function for sysfs attribute bootflag
+ */
+static ssize_t bootflag_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
+}
+
+/**
+ * @brief Set function for sysfs attribute bootflag
+ */
+static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 1))
+ return -EINVAL;
+
+ *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
+ cmd.length = cpu_to_le16(sizeof(uint32_t));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_BOOTFLAG);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * @brief Get function for sysfs attribute boottime
+ */
+static ssize_t boottime_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "%d\n", defs.boottime);
+}
+
+/**
+ * @brief Set function for sysfs attribute boottime
+ */
+static ssize_t boottime_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* A too small boot time will result in the device booting into
+ * standalone (no-host) mode before the host can take control of it,
+ * so the change will be hard to revert. This may be a desired
+ * feature (e.g to configure a very fast boot time for devices that
+ * will not be attached to a host), but dangerous. So I'm enforcing a
+ * lower limit of 20 seconds: remove and recompile the driver if this
+ * does not work for you.
+ */
+ datum = (datum < 20) ? 20 : datum;
+ cmd.data[0] = datum;
+ cmd.length = cpu_to_le16(sizeof(uint8_t));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_BOOTTIME);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * @brief Get function for sysfs attribute channel
+ */
+static ssize_t channel_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
+}
+
+/**
+ * @brief Set function for sysfs attribute channel
+ */
+static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ struct cmd_ds_mesh_config cmd;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if (ret != 1 || datum < 1 || datum > 11)
+ return -EINVAL;
+
+ *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
+ cmd.length = cpu_to_le16(sizeof(uint16_t));
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_DEF_CHANNEL);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * @brief Get function for sysfs attribute mesh_id
+ */
+static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int maxlen;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {
+ lbs_pr_err("inconsistent mesh ID length");
+ defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
+ }
+
+ /* SSID not null terminated: reserve room for \0 + \n */
+ maxlen = defs.meshie.val.mesh_id_len + 2;
+ maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE;
+
+ defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0';
+
+ return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id);
+}
+
+/**
+ * @brief Set function for sysfs attribute mesh_id
+ */
+static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ int len;
+ int ret;
+
+ if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)
+ return -EINVAL;
+
+ memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+
+ len = count - 1;
+ memcpy(ie->val.mesh_id, buf, len);
+ /* SSID len */
+ ie->val.mesh_id_len = len;
+ /* IE len */
+ ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * @brief Get function for sysfs attribute protocol_id
+ */
+static ssize_t protocol_id_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
+}
+
+/**
+ * @brief Set function for sysfs attribute protocol_id
+ */
+static ssize_t protocol_id_set(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+ /* update protocol id */
+ ie->val.active_protocol_id = datum;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * @brief Get function for sysfs attribute metric_id
+ */
+static ssize_t metric_id_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
+}
+
+/**
+ * @brief Set function for sysfs attribute metric_id
+ */
+static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+ /* update metric id */
+ ie->val.active_metric_id = datum;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+/**
+ * @brief Get function for sysfs attribute capability
+ */
+static ssize_t capability_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mrvl_mesh_defaults defs;
+ int ret;
+
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ if (ret)
+ return ret;
+
+ return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
+}
+
+/**
+ * @brief Set function for sysfs attribute capability
+ */
+static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cmd_ds_mesh_config cmd;
+ struct mrvl_mesh_defaults defs;
+ struct mrvl_meshie *ie;
+ struct lbs_private *priv = to_net_dev(dev)->ml_priv;
+ uint32_t datum;
+ int ret;
+
+ memset(&cmd, 0, sizeof(cmd));
+ ret = sscanf(buf, "%d", &datum);
+ if ((ret != 1) || (datum > 255))
+ return -EINVAL;
+
+ /* fetch all other Information Element parameters */
+ ret = mesh_get_default_parameters(dev, &defs);
+
+ cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
+
+ /* transfer IE elements */
+ ie = (struct mrvl_meshie *) &cmd.data[0];
+ memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
+ /* update value */
+ ie->val.mesh_capability = datum;
+
+ ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
+ CMD_TYPE_MESH_SET_MESH_IE);
+ if (ret)
+ return ret;
+
+ return strlen(buf);
+}
+
+
+static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
+static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
+static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
+static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
+static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
+static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
+static DEVICE_ATTR(capability, 0644, capability_get, capability_set);
+
+static struct attribute *boot_opts_attrs[] = {
+ &dev_attr_bootflag.attr,
+ &dev_attr_boottime.attr,
+ &dev_attr_channel.attr,
+ NULL
+};
+
+static struct attribute_group boot_opts_group = {
+ .name = "boot_options",
+ .attrs = boot_opts_attrs,
+};
+
+static struct attribute *mesh_ie_attrs[] = {
+ &dev_attr_mesh_id.attr,
+ &dev_attr_protocol_id.attr,
+ &dev_attr_metric_id.attr,
+ &dev_attr_capability.attr,
+ NULL
+};
+
+static struct attribute_group mesh_ie_group = {
+ .name = "mesh_ie",
+ .attrs = mesh_ie_attrs,
+};
+
+void lbs_persist_config_init(struct net_device *dev)
+{
+ int ret;
+ ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
+ ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
+}
+
+void lbs_persist_config_remove(struct net_device *dev)
+{
+ sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
+ sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
+}
+
+
+
+/***************************************************************************
+ * Ethtool related
+ */
+
+static const char *mesh_stat_strings[] = {
+ "drop_duplicate_bcast",
+ "drop_ttl_zero",
+ "drop_no_fwd_route",
+ "drop_no_buffers",
+ "fwded_unicast_cnt",
+ "fwded_bcast_cnt",
+ "drop_blind_table",
+ "tx_failed_cnt"
+};
+
+void lbs_mesh_ethtool_get_stats(struct net_device *dev,
+ struct ethtool_stats *stats, uint64_t *data)
+{
+ struct lbs_private *priv = dev->ml_priv;
+ struct cmd_ds_mesh_access mesh_access;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+
+ /* Get Mesh Statistics */
+ ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
+
+ if (ret) {
+ memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
+ return;
+ }
+
+ priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
+ priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
+ priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
+ priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
+ priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
+ priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
+ priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
+ priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
+
+ data[0] = priv->mstats.fwd_drop_rbt;
+ data[1] = priv->mstats.fwd_drop_ttl;
+ data[2] = priv->mstats.fwd_drop_noroute;
+ data[3] = priv->mstats.fwd_drop_nobuf;
+ data[4] = priv->mstats.fwd_unicast_cnt;
+ data[5] = priv->mstats.fwd_bcast_cnt;
+ data[6] = priv->mstats.drop_blind;
+ data[7] = priv->mstats.tx_failed_cnt;
+
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+}
+
+int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset)
+{
+ struct lbs_private *priv = dev->ml_priv;
+
+ if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
+ return MESH_STATS_NUM;
+
+ return -EOPNOTSUPP;
+}
+
+void lbs_mesh_ethtool_get_strings(struct net_device *dev,
+ uint32_t stringset, uint8_t *s)
+{
+ int i;
+
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < MESH_STATS_NUM; i++) {
+ memcpy(s + i * ETH_GSTRING_LEN,
+ mesh_stat_strings[i],
+ ETH_GSTRING_LEN);
+ }
+ break;
+ }
+ lbs_deb_enter(LBS_DEB_ETHTOOL);
+}
diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h
new file mode 100644
index 00000000000..fea9b5d005f
--- /dev/null
+++ b/drivers/net/wireless/libertas/mesh.h
@@ -0,0 +1,78 @@
+/**
+ * Contains all definitions needed for the Libertas' MESH implementation.
+ */
+#ifndef _LBS_MESH_H_
+#define _LBS_MESH_H_
+
+
+#include <net/iw_handler.h>
+#include <net/lib80211.h>
+
+
+/* Mesh statistics */
+struct lbs_mesh_stats {
+ u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */
+ u32 fwd_unicast_cnt; /* Fwd: Unicast counter */
+ u32 fwd_drop_ttl; /* Fwd: TTL zero */
+ u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */
+ u32 fwd_drop_noroute; /* Fwd: No route to Destination */
+ u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */
+ u32 drop_blind; /* Rx: Dropped by blinding table */
+ u32 tx_failed_cnt; /* Tx: Failed transmissions */
+};
+
+
+struct net_device;
+struct lbs_private;
+
+int lbs_init_mesh(struct lbs_private *priv);
+int lbs_deinit_mesh(struct lbs_private *priv);
+
+int lbs_add_mesh(struct lbs_private *priv);
+void lbs_remove_mesh(struct lbs_private *priv);
+
+
+/* Sending / Receiving */
+
+struct rxpd;
+struct txpd;
+
+struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
+ struct net_device *dev, struct rxpd *rxpd);
+void lbs_mesh_set_txpd(struct lbs_private *priv,
+ struct net_device *dev, struct txpd *txpd);
+
+
+/* Command handling */
+
+struct cmd_ds_command;
+
+int lbs_cmd_bt_access(struct cmd_ds_command *cmd,
+ u16 cmd_action, void *pdata_buf);
+int lbs_cmd_fwt_access(struct cmd_ds_command *cmd,
+ u16 cmd_action, void *pdata_buf);
+
+
+/* Persistent configuration */
+
+void lbs_persist_config_init(struct net_device *net);
+void lbs_persist_config_remove(struct net_device *net);
+
+
+/* WEXT handler */
+
+extern struct iw_handler_def mesh_handler_def;
+
+
+/* Ethtool statistics */
+
+struct ethtool_stats;
+
+void lbs_mesh_ethtool_get_stats(struct net_device *dev,
+ struct ethtool_stats *stats, uint64_t *data);
+int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset);
+void lbs_mesh_ethtool_get_strings(struct net_device *dev,
+ uint32_t stringset, uint8_t *s);
+
+
+#endif
diff --git a/drivers/net/wireless/libertas/persistcfg.c b/drivers/net/wireless/libertas/persistcfg.c
deleted file mode 100644
index 18fe29faf99..00000000000
--- a/drivers/net/wireless/libertas/persistcfg.c
+++ /dev/null
@@ -1,453 +0,0 @@
-#include <linux/moduleparam.h>
-#include <linux/delay.h>
-#include <linux/etherdevice.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/kthread.h>
-#include <linux/kfifo.h>
-
-#include "host.h"
-#include "decl.h"
-#include "dev.h"
-#include "wext.h"
-#include "debugfs.h"
-#include "scan.h"
-#include "assoc.h"
-#include "cmd.h"
-
-static int mesh_get_default_parameters(struct device *dev,
- struct mrvl_mesh_defaults *defs)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- int ret;
-
- memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
- CMD_TYPE_MESH_GET_DEFAULTS);
-
- if (ret)
- return -EOPNOTSUPP;
-
- memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
-
- return 0;
-}
-
-/**
- * @brief Get function for sysfs attribute bootflag
- */
-static ssize_t bootflag_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
-}
-
-/**
- * @brief Set function for sysfs attribute bootflag
- */
-static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 1))
- return -EINVAL;
-
- *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
- cmd.length = cpu_to_le16(sizeof(uint32_t));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_BOOTFLAG);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute boottime
- */
-static ssize_t boottime_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "%d\n", defs.boottime);
-}
-
-/**
- * @brief Set function for sysfs attribute boottime
- */
-static ssize_t boottime_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* A too small boot time will result in the device booting into
- * standalone (no-host) mode before the host can take control of it,
- * so the change will be hard to revert. This may be a desired
- * feature (e.g to configure a very fast boot time for devices that
- * will not be attached to a host), but dangerous. So I'm enforcing a
- * lower limit of 20 seconds: remove and recompile the driver if this
- * does not work for you.
- */
- datum = (datum < 20) ? 20 : datum;
- cmd.data[0] = datum;
- cmd.length = cpu_to_le16(sizeof(uint8_t));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_BOOTTIME);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute channel
- */
-static ssize_t channel_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
-}
-
-/**
- * @brief Set function for sysfs attribute channel
- */
-static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- struct cmd_ds_mesh_config cmd;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if (ret != 1 || datum < 1 || datum > 11)
- return -EINVAL;
-
- *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
- cmd.length = cpu_to_le16(sizeof(uint16_t));
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_DEF_CHANNEL);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute mesh_id
- */
-static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int maxlen;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- if (defs.meshie.val.mesh_id_len > IW_ESSID_MAX_SIZE) {
- lbs_pr_err("inconsistent mesh ID length");
- defs.meshie.val.mesh_id_len = IW_ESSID_MAX_SIZE;
- }
-
- /* SSID not null terminated: reserve room for \0 + \n */
- maxlen = defs.meshie.val.mesh_id_len + 2;
- maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE;
-
- defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0';
-
- return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id);
-}
-
-/**
- * @brief Set function for sysfs attribute mesh_id
- */
-static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- int len;
- int ret;
-
- if (count < 2 || count > IW_ESSID_MAX_SIZE + 1)
- return -EINVAL;
-
- memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
- ie = (struct mrvl_meshie *) &cmd.data[0];
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
-
- len = count - 1;
- memcpy(ie->val.mesh_id, buf, len);
- /* SSID len */
- ie->val.mesh_id_len = len;
- /* IE len */
- ie->len = sizeof(struct mrvl_meshie_val) - IW_ESSID_MAX_SIZE + len;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute protocol_id
- */
-static ssize_t protocol_id_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
-}
-
-/**
- * @brief Set function for sysfs attribute protocol_id
- */
-static ssize_t protocol_id_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- ie = (struct mrvl_meshie *) &cmd.data[0];
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
- /* update protocol id */
- ie->val.active_protocol_id = datum;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute metric_id
- */
-static ssize_t metric_id_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
-}
-
-/**
- * @brief Set function for sysfs attribute metric_id
- */
-static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- ie = (struct mrvl_meshie *) &cmd.data[0];
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
- /* update metric id */
- ie->val.active_metric_id = datum;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-/**
- * @brief Get function for sysfs attribute capability
- */
-static ssize_t capability_get(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct mrvl_mesh_defaults defs;
- int ret;
-
- ret = mesh_get_default_parameters(dev, &defs);
-
- if (ret)
- return ret;
-
- return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
-}
-
-/**
- * @brief Set function for sysfs attribute capability
- */
-static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct cmd_ds_mesh_config cmd;
- struct mrvl_mesh_defaults defs;
- struct mrvl_meshie *ie;
- struct lbs_private *priv = to_net_dev(dev)->ml_priv;
- uint32_t datum;
- int ret;
-
- memset(&cmd, 0, sizeof(cmd));
- ret = sscanf(buf, "%d", &datum);
- if ((ret != 1) || (datum > 255))
- return -EINVAL;
-
- /* fetch all other Information Element parameters */
- ret = mesh_get_default_parameters(dev, &defs);
-
- cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
-
- /* transfer IE elements */
- ie = (struct mrvl_meshie *) &cmd.data[0];
- memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
- /* update value */
- ie->val.mesh_capability = datum;
-
- ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
- CMD_TYPE_MESH_SET_MESH_IE);
- if (ret)
- return ret;
-
- return strlen(buf);
-}
-
-
-static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
-static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
-static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
-static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
-static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
-static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
-static DEVICE_ATTR(capability, 0644, capability_get, capability_set);
-
-static struct attribute *boot_opts_attrs[] = {
- &dev_attr_bootflag.attr,
- &dev_attr_boottime.attr,
- &dev_attr_channel.attr,
- NULL
-};
-
-static struct attribute_group boot_opts_group = {
- .name = "boot_options",
- .attrs = boot_opts_attrs,
-};
-
-static struct attribute *mesh_ie_attrs[] = {
- &dev_attr_mesh_id.attr,
- &dev_attr_protocol_id.attr,
- &dev_attr_metric_id.attr,
- &dev_attr_capability.attr,
- NULL
-};
-
-static struct attribute_group mesh_ie_group = {
- .name = "mesh_ie",
- .attrs = mesh_ie_attrs,
-};
-
-void lbs_persist_config_init(struct net_device *dev)
-{
- int ret;
- ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
- ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
-}
-
-void lbs_persist_config_remove(struct net_device *dev)
-{
- sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
- sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
-}
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
index 65f02cc6752..2daf8ffdb7e 100644
--- a/drivers/net/wireless/libertas/rx.c
+++ b/drivers/net/wireless/libertas/rx.c
@@ -4,7 +4,7 @@
#include <linux/etherdevice.h>
#include <linux/types.h>
-#include "hostcmd.h"
+#include "host.h"
#include "radiotap.h"
#include "decl.h"
#include "dev.h"
@@ -160,15 +160,8 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
p_rx_pd = (struct rxpd *) skb->data;
p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd +
le32_to_cpu(p_rx_pd->pkt_ptr));
- if (priv->mesh_dev) {
- if (priv->mesh_fw_ver == MESH_FW_OLD) {
- if (p_rx_pd->rx_control & RxPD_MESH_FRAME)
- dev = priv->mesh_dev;
- } else if (priv->mesh_fw_ver == MESH_FW_NEW) {
- if (p_rx_pd->u.bss.bss_num == MESH_IFACE_ID)
- dev = priv->mesh_dev;
- }
- }
+
+ dev = lbs_mesh_set_dev(priv, dev, p_rx_pd);
lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data,
min_t(unsigned int, skb->len, 100));
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
index 6c95af3023c..c6a6c042b82 100644
--- a/drivers/net/wireless/libertas/scan.c
+++ b/drivers/net/wireless/libertas/scan.c
@@ -12,18 +12,19 @@
#include <net/lib80211.h>
#include "host.h"
-#include "decl.h"
#include "dev.h"
#include "scan.h"
+#include "assoc.h"
+#include "wext.h"
#include "cmd.h"
//! Approximate amount of data needed to pass a scan result back to iwlist
#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \
- + IW_ESSID_MAX_SIZE \
+ + IEEE80211_MAX_SSID_LEN \
+ IW_EV_UINT_LEN \
+ IW_EV_FREQ_LEN \
+ IW_EV_QUAL_LEN \
- + IW_ESSID_MAX_SIZE \
+ + IEEE80211_MAX_SSID_LEN \
+ IW_EV_PARAM_LEN \
+ 40) /* 40 for WPAIE */
@@ -121,6 +122,189 @@ static inline int is_same_network(struct bss_descriptor *src,
+/*********************************************************************/
+/* */
+/* Region channel support */
+/* */
+/*********************************************************************/
+
+#define LBS_TX_PWR_DEFAULT 20 /*100mW */
+#define LBS_TX_PWR_US_DEFAULT 20 /*100mW */
+#define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */
+#define LBS_TX_PWR_FR_DEFAULT 20 /*100mW */
+#define LBS_TX_PWR_EMEA_DEFAULT 20 /*100mW */
+
+/* Format { channel, frequency (MHz), maxtxpower } */
+/* band: 'B/G', region: USA FCC/Canada IC */
+static struct chan_freq_power channel_freq_power_US_BG[] = {
+ {1, 2412, LBS_TX_PWR_US_DEFAULT},
+ {2, 2417, LBS_TX_PWR_US_DEFAULT},
+ {3, 2422, LBS_TX_PWR_US_DEFAULT},
+ {4, 2427, LBS_TX_PWR_US_DEFAULT},
+ {5, 2432, LBS_TX_PWR_US_DEFAULT},
+ {6, 2437, LBS_TX_PWR_US_DEFAULT},
+ {7, 2442, LBS_TX_PWR_US_DEFAULT},
+ {8, 2447, LBS_TX_PWR_US_DEFAULT},
+ {9, 2452, LBS_TX_PWR_US_DEFAULT},
+ {10, 2457, LBS_TX_PWR_US_DEFAULT},
+ {11, 2462, LBS_TX_PWR_US_DEFAULT}
+};
+
+/* band: 'B/G', region: Europe ETSI */
+static struct chan_freq_power channel_freq_power_EU_BG[] = {
+ {1, 2412, LBS_TX_PWR_EMEA_DEFAULT},
+ {2, 2417, LBS_TX_PWR_EMEA_DEFAULT},
+ {3, 2422, LBS_TX_PWR_EMEA_DEFAULT},
+ {4, 2427, LBS_TX_PWR_EMEA_DEFAULT},
+ {5, 2432, LBS_TX_PWR_EMEA_DEFAULT},
+ {6, 2437, LBS_TX_PWR_EMEA_DEFAULT},
+ {7, 2442, LBS_TX_PWR_EMEA_DEFAULT},
+ {8, 2447, LBS_TX_PWR_EMEA_DEFAULT},
+ {9, 2452, LBS_TX_PWR_EMEA_DEFAULT},
+ {10, 2457, LBS_TX_PWR_EMEA_DEFAULT},
+ {11, 2462, LBS_TX_PWR_EMEA_DEFAULT},
+ {12, 2467, LBS_TX_PWR_EMEA_DEFAULT},
+ {13, 2472, LBS_TX_PWR_EMEA_DEFAULT}
+};
+
+/* band: 'B/G', region: Spain */
+static struct chan_freq_power channel_freq_power_SPN_BG[] = {
+ {10, 2457, LBS_TX_PWR_DEFAULT},
+ {11, 2462, LBS_TX_PWR_DEFAULT}
+};
+
+/* band: 'B/G', region: France */
+static struct chan_freq_power channel_freq_power_FR_BG[] = {
+ {10, 2457, LBS_TX_PWR_FR_DEFAULT},
+ {11, 2462, LBS_TX_PWR_FR_DEFAULT},
+ {12, 2467, LBS_TX_PWR_FR_DEFAULT},
+ {13, 2472, LBS_TX_PWR_FR_DEFAULT}
+};
+
+/* band: 'B/G', region: Japan */
+static struct chan_freq_power channel_freq_power_JPN_BG[] = {
+ {1, 2412, LBS_TX_PWR_JP_DEFAULT},
+ {2, 2417, LBS_TX_PWR_JP_DEFAULT},
+ {3, 2422, LBS_TX_PWR_JP_DEFAULT},
+ {4, 2427, LBS_TX_PWR_JP_DEFAULT},
+ {5, 2432, LBS_TX_PWR_JP_DEFAULT},
+ {6, 2437, LBS_TX_PWR_JP_DEFAULT},
+ {7, 2442, LBS_TX_PWR_JP_DEFAULT},
+ {8, 2447, LBS_TX_PWR_JP_DEFAULT},
+ {9, 2452, LBS_TX_PWR_JP_DEFAULT},
+ {10, 2457, LBS_TX_PWR_JP_DEFAULT},
+ {11, 2462, LBS_TX_PWR_JP_DEFAULT},
+ {12, 2467, LBS_TX_PWR_JP_DEFAULT},
+ {13, 2472, LBS_TX_PWR_JP_DEFAULT},
+ {14, 2484, LBS_TX_PWR_JP_DEFAULT}
+};
+
+/**
+ * the structure for channel, frequency and power
+ */
+struct region_cfp_table {
+ u8 region;
+ struct chan_freq_power *cfp_BG;
+ int cfp_no_BG;
+};
+
+/**
+ * the structure for the mapping between region and CFP
+ */
+static struct region_cfp_table region_cfp_table[] = {
+ {0x10, /*US FCC */
+ channel_freq_power_US_BG,
+ ARRAY_SIZE(channel_freq_power_US_BG),
+ }
+ ,
+ {0x20, /*CANADA IC */
+ channel_freq_power_US_BG,
+ ARRAY_SIZE(channel_freq_power_US_BG),
+ }
+ ,
+ {0x30, /*EU*/ channel_freq_power_EU_BG,
+ ARRAY_SIZE(channel_freq_power_EU_BG),
+ }
+ ,
+ {0x31, /*SPAIN*/ channel_freq_power_SPN_BG,
+ ARRAY_SIZE(channel_freq_power_SPN_BG),
+ }
+ ,
+ {0x32, /*FRANCE*/ channel_freq_power_FR_BG,
+ ARRAY_SIZE(channel_freq_power_FR_BG),
+ }
+ ,
+ {0x40, /*JAPAN*/ channel_freq_power_JPN_BG,
+ ARRAY_SIZE(channel_freq_power_JPN_BG),
+ }
+ ,
+/*Add new region here */
+};
+
+/**
+ * @brief This function finds the CFP in
+ * region_cfp_table based on region and band parameter.
+ *
+ * @param region The region code
+ * @param band The band
+ * @param cfp_no A pointer to CFP number
+ * @return A pointer to CFP
+ */
+static struct chan_freq_power *lbs_get_region_cfp_table(u8 region, int *cfp_no)
+{
+ int i, end;
+
+ lbs_deb_enter(LBS_DEB_MAIN);
+
+ end = ARRAY_SIZE(region_cfp_table);
+
+ for (i = 0; i < end ; i++) {
+ lbs_deb_main("region_cfp_table[i].region=%d\n",
+ region_cfp_table[i].region);
+ if (region_cfp_table[i].region == region) {
+ *cfp_no = region_cfp_table[i].cfp_no_BG;
+ lbs_deb_leave(LBS_DEB_MAIN);
+ return region_cfp_table[i].cfp_BG;
+ }
+ }
+
+ lbs_deb_leave_args(LBS_DEB_MAIN, "ret NULL");
+ return NULL;
+}
+
+int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band)
+{
+ int ret = 0;
+ int i = 0;
+
+ struct chan_freq_power *cfp;
+ int cfp_no;
+
+ lbs_deb_enter(LBS_DEB_MAIN);
+
+ memset(priv->region_channel, 0, sizeof(priv->region_channel));
+
+ cfp = lbs_get_region_cfp_table(region, &cfp_no);
+ if (cfp != NULL) {
+ priv->region_channel[i].nrcfp = cfp_no;
+ priv->region_channel[i].CFP = cfp;
+ } else {
+ lbs_deb_main("wrong region code %#x in band B/G\n",
+ region);
+ ret = -1;
+ goto out;
+ }
+ priv->region_channel[i].valid = 1;
+ priv->region_channel[i].region = region;
+ priv->region_channel[i].band = band;
+ i++;
+out:
+ lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
+ return ret;
+}
+
+
+
/*********************************************************************/
/* */
@@ -161,31 +345,15 @@ static int lbs_scan_create_channel_list(struct lbs_private *priv,
scantype = CMD_SCAN_TYPE_ACTIVE;
for (rgnidx = 0; rgnidx < ARRAY_SIZE(priv->region_channel); rgnidx++) {
- if (priv->enable11d && (priv->connect_status != LBS_CONNECTED)
- && (priv->mesh_connect_status != LBS_CONNECTED)) {
- /* Scan all the supported chan for the first scan */
- if (!priv->universal_channel[rgnidx].valid)
- continue;
- scanregion = &priv->universal_channel[rgnidx];
-
- /* clear the parsed_region_chan for the first scan */
- memset(&priv->parsed_region_chan, 0x00,
- sizeof(priv->parsed_region_chan));
- } else {
- if (!priv->region_channel[rgnidx].valid)
- continue;
- scanregion = &priv->region_channel[rgnidx];
- }
+ if (!priv->region_channel[rgnidx].valid)
+ continue;
+ scanregion = &priv->region_channel[rgnidx];
for (nextchan = 0; nextchan < scanregion->nrcfp; nextchan++, chanidx++) {
struct chanscanparamset *chan = &scanchanlist[chanidx];
cfp = scanregion->CFP + nextchan;
- if (priv->enable11d)
- scantype = lbs_get_scan_type_11d(cfp->channel,
- &priv->parsed_region_chan);
-
if (scanregion->band == BAND_B || scanregion->band == BAND_G)
chan->radiotype = CMD_SCAN_RADIO_TYPE_BG;
@@ -519,7 +687,6 @@ static int lbs_process_bss(struct bss_descriptor *bss,
struct ieee_ie_cf_param_set *cf;
struct ieee_ie_ibss_param_set *ibss;
DECLARE_SSID_BUF(ssid);
- struct ieee_ie_country_info_set *pcountryinfo;
uint8_t *pos, *end, *p;
uint8_t n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0;
uint16_t beaconsize = 0;
@@ -642,26 +809,6 @@ static int lbs_process_bss(struct bss_descriptor *bss,
lbs_deb_scan("got IBSS IE\n");
break;
- case WLAN_EID_COUNTRY:
- pcountryinfo = (struct ieee_ie_country_info_set *) pos;
- lbs_deb_scan("got COUNTRY IE\n");
- if (pcountryinfo->header.len < sizeof(pcountryinfo->countrycode)
- || pcountryinfo->header.len > 254) {
- lbs_deb_scan("%s: 11D- Err CountryInfo len %d, min %zd, max 254\n",
- __func__,
- pcountryinfo->header.len,
- sizeof(pcountryinfo->countrycode));
- ret = -1;
- goto done;
- }
-
- memcpy(&bss->countryinfo, pcountryinfo,
- pcountryinfo->header.len + 2);
- lbs_deb_hex(LBS_DEB_SCAN, "process_bss: 11d countryinfo",
- (uint8_t *) pcountryinfo,
- (int) (pcountryinfo->header.len + 2));
- break;
-
case WLAN_EID_EXT_SUPP_RATES:
/* only process extended supported rate if data rate is
* already found. Data rate IE should come before
@@ -812,7 +959,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv,
/* SSID */
iwe.cmd = SIOCGIWESSID;
iwe.u.data.flags = 1;
- iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IW_ESSID_MAX_SIZE);
+ iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IEEE80211_MAX_SSID_LEN);
start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid);
/* Mode */
@@ -1022,9 +1169,12 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
return -EAGAIN;
/* Update RSSI if current BSS is a locally created ad-hoc BSS */
- if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate)
- lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
- CMD_OPTION_WAITFORRSP, 0, NULL);
+ if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate) {
+ err = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
+ CMD_OPTION_WAITFORRSP, 0, NULL);
+ if (err)
+ goto out;
+ }
mutex_lock(&priv->lock);
list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) {
@@ -1058,7 +1208,7 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
dwrq->length = (ev - extra);
dwrq->flags = 0;
-
+out:
lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err);
return err;
}
@@ -1141,11 +1291,11 @@ static int lbs_ret_80211_scan(struct lbs_private *priv, unsigned long dummy,
/* The size of the TLV buffer is equal to the entire command response
* size (scanrespsize) minus the fixed fields (sizeof()'s), the
* BSS Descriptions (bssdescriptsize as bytesLef) and the command
- * response header (S_DS_GEN)
+ * response header (sizeof(struct cmd_header))
*/
tlvbufsize = scanrespsize - (bytesleft + sizeof(scanresp->bssdescriptsize)
+ sizeof(scanresp->nr_sets)
- + S_DS_GEN);
+ + sizeof(struct cmd_header));
/*
* Process each scan response returned (scanresp->nr_sets). Save
diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h
index fab7d5d097f..8fb1706d752 100644
--- a/drivers/net/wireless/libertas/scan.h
+++ b/drivers/net/wireless/libertas/scan.h
@@ -9,8 +9,36 @@
#include <net/iw_handler.h>
+struct lbs_private;
+
#define MAX_NETWORK_COUNT 128
+/** Chan-freq-TxPower mapping table*/
+struct chan_freq_power {
+ /** channel Number */
+ u16 channel;
+ /** frequency of this channel */
+ u32 freq;
+ /** Max allowed Tx power level */
+ u16 maxtxpower;
+ /** TRUE:channel unsupported; FLASE:supported*/
+ u8 unsupported;
+};
+
+/** region-band mapping table*/
+struct region_channel {
+ /** TRUE if this entry is valid */
+ u8 valid;
+ /** region code for US, Japan ... */
+ u8 region;
+ /** band B/G/A, used for BAND_CONFIG cmd */
+ u8 band;
+ /** Actual No. of elements in the array below */
+ u8 nrcfp;
+ /** chan-freq-txpower mapping table*/
+ struct chan_freq_power *CFP;
+};
+
/**
* @brief Maximum number of channels that can be sent in a setuserscan ioctl
*/
@@ -18,6 +46,8 @@
int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len);
+int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band);
+
int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid,
u8 ssid_len);
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
index 8c3766a6e8e..315d1ce286c 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/libertas/tx.c
@@ -5,7 +5,7 @@
#include <linux/etherdevice.h>
#include <linux/sched.h>
-#include "hostcmd.h"
+#include "host.h"
#include "radiotap.h"
#include "decl.h"
#include "defs.h"
@@ -131,12 +131,7 @@ netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
txpd->tx_packet_length = cpu_to_le16(pkt_len);
txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
- if (dev == priv->mesh_dev) {
- if (priv->mesh_fw_ver == MESH_FW_OLD)
- txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
- else if (priv->mesh_fw_ver == MESH_FW_NEW)
- txpd->u.bss.bss_num = MESH_IFACE_ID;
- }
+ lbs_mesh_set_txpd(priv, dev, txpd);
lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd));
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h
index 99905df65b2..3e72c86ceca 100644
--- a/drivers/net/wireless/libertas/types.h
+++ b/drivers/net/wireless/libertas/types.h
@@ -5,8 +5,8 @@
#define _LBS_TYPES_H_
#include <linux/if_ether.h>
+#include <linux/ieee80211.h>
#include <asm/byteorder.h>
-#include <linux/wireless.h>
struct ieee_ie_header {
u8 id;
@@ -247,7 +247,7 @@ struct mrvl_meshie_val {
uint8_t active_metric_id;
uint8_t mesh_capability;
uint8_t mesh_id_len;
- uint8_t mesh_id[IW_ESSID_MAX_SIZE];
+ uint8_t mesh_id[IEEE80211_MAX_SSID_LEN];
} __attribute__ ((packed));
struct mrvl_meshie {
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
index be837a0d251..a8eb9e1fcf3 100644
--- a/drivers/net/wireless/libertas/wext.c
+++ b/drivers/net/wireless/libertas/wext.c
@@ -45,6 +45,63 @@ static inline void lbs_cancel_association_work(struct lbs_private *priv)
priv->pending_assoc_req = NULL;
}
+void lbs_send_disconnect_notification(struct lbs_private *priv)
+{
+ union iwreq_data wrqu;
+
+ memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN);
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
+{
+ union iwreq_data iwrq;
+ u8 buf[50];
+
+ lbs_deb_enter(LBS_DEB_WEXT);
+
+ memset(&iwrq, 0, sizeof(union iwreq_data));
+ memset(buf, 0, sizeof(buf));
+
+ snprintf(buf, sizeof(buf) - 1, "%s", str);
+
+ iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;
+
+ /* Send Event to upper layer */
+ lbs_deb_wext("event indication string %s\n", (char *)buf);
+ lbs_deb_wext("event indication length %d\n", iwrq.data.length);
+ lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str);
+
+ wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf);
+
+ lbs_deb_leave(LBS_DEB_WEXT);
+}
+
+/**
+ * @brief This function handles MIC failure event.
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @para event the event id
+ * @return n/a
+ */
+void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event)
+{
+ char buf[50];
+
+ lbs_deb_enter(LBS_DEB_CMD);
+ memset(buf, 0, sizeof(buf));
+
+ sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication ");
+
+ if (event == MACREG_INT_CODE_MIC_ERR_UNICAST)
+ strcat(buf, "unicast ");
+ else
+ strcat(buf, "multicast ");
+
+ lbs_send_iwevcustom_event(priv, buf);
+ lbs_deb_leave(LBS_DEB_CMD);
+}
/**
* @brief Find the channel frequency power info with specific channel
@@ -66,8 +123,6 @@ struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
rc = &priv->region_channel[j];
- if (priv->enable11d)
- rc = &priv->universal_channel[j];
if (!rc->valid || !rc->CFP)
continue;
if (rc->band != band)
@@ -107,8 +162,6 @@ static struct chan_freq_power *find_cfp_by_band_and_freq(
for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
rc = &priv->region_channel[j];
- if (priv->enable11d)
- rc = &priv->universal_channel[j];
if (!rc->valid || !rc->CFP)
continue;
if (rc->band != band)
@@ -169,12 +222,12 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
lbs_deb_enter(LBS_DEB_WEXT);
cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
- priv->curbssparams.channel);
+ priv->channel);
if (!cfp) {
- if (priv->curbssparams.channel)
+ if (priv->channel)
lbs_deb_wext("invalid channel %d\n",
- priv->curbssparams.channel);
+ priv->channel);
return -EINVAL;
}
@@ -547,8 +600,6 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
struct chan_freq_power *cfp;
u8 rates[MAX_RATES + 1];
- u8 flag = 0;
-
lbs_deb_enter(LBS_DEB_WEXT);
dwrq->length = sizeof(struct iw_range);
@@ -570,52 +621,21 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
range->scan_capa = IW_SCAN_CAPA_ESSID;
- if (priv->enable11d &&
- (priv->connect_status == LBS_CONNECTED ||
- priv->mesh_connect_status == LBS_CONNECTED)) {
- u8 chan_no;
- u8 band;
-
- struct parsed_region_chan_11d *parsed_region_chan =
- &priv->parsed_region_chan;
-
- if (parsed_region_chan == NULL) {
- lbs_deb_wext("11d: parsed_region_chan is NULL\n");
- goto out;
- }
- band = parsed_region_chan->band;
- lbs_deb_wext("band %d, nr_char %d\n", band,
- parsed_region_chan->nr_chan);
-
+ for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+ && (j < ARRAY_SIZE(priv->region_channel)); j++) {
+ cfp = priv->region_channel[j].CFP;
for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
- && (i < parsed_region_chan->nr_chan); i++) {
- chan_no = parsed_region_chan->chanpwr[i].chan;
- lbs_deb_wext("chan_no %d\n", chan_no);
- range->freq[range->num_frequency].i = (long)chan_no;
+ && priv->region_channel[j].valid
+ && cfp
+ && (i < priv->region_channel[j].nrcfp); i++) {
+ range->freq[range->num_frequency].i =
+ (long)cfp->channel;
range->freq[range->num_frequency].m =
- (long)lbs_chan_2_freq(chan_no) * 100000;
+ (long)cfp->freq * 100000;
range->freq[range->num_frequency].e = 1;
+ cfp++;
range->num_frequency++;
}
- flag = 1;
- }
- if (!flag) {
- for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
- && (j < ARRAY_SIZE(priv->region_channel)); j++) {
- cfp = priv->region_channel[j].CFP;
- for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
- && priv->region_channel[j].valid
- && cfp
- && (i < priv->region_channel[j].nrcfp); i++) {
- range->freq[range->num_frequency].i =
- (long)cfp->channel;
- range->freq[range->num_frequency].m =
- (long)cfp->freq * 100000;
- range->freq[range->num_frequency].e = 1;
- cfp++;
- range->num_frequency++;
- }
- }
}
lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
@@ -700,7 +720,6 @@ static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
| IW_ENC_CAPA_CIPHER_CCMP;
}
-out:
lbs_deb_leave(LBS_DEB_WEXT);
return 0;
}
@@ -709,6 +728,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
struct lbs_private *priv = dev->ml_priv;
+ int ret = 0;
lbs_deb_enter(LBS_DEB_WEXT);
@@ -737,8 +757,54 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
"setting power timeout is not supported\n");
return -EINVAL;
} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
- lbs_deb_wext("setting power period not supported\n");
- return -EINVAL;
+ vwrq->value = vwrq->value / 1000;
+ if (!priv->enter_deep_sleep) {
+ lbs_pr_err("deep sleep feature is not implemented "
+ "for this interface driver\n");
+ return -EINVAL;
+ }
+
+ if (priv->connect_status == LBS_CONNECTED) {
+ if ((priv->is_auto_deep_sleep_enabled) &&
+ (vwrq->value == -1000)) {
+ lbs_exit_auto_deep_sleep(priv);
+ return 0;
+ } else {
+ lbs_pr_err("can't use deep sleep cmd in "
+ "connected state\n");
+ return -EINVAL;
+ }
+ }
+
+ if ((vwrq->value < 0) && (vwrq->value != -1000)) {
+ lbs_pr_err("unknown option\n");
+ return -EINVAL;
+ }
+
+ if (vwrq->value > 0) {
+ if (!priv->is_auto_deep_sleep_enabled) {
+ priv->is_activity_detected = 0;
+ priv->auto_deep_sleep_timeout = vwrq->value;
+ lbs_enter_auto_deep_sleep(priv);
+ } else {
+ priv->auto_deep_sleep_timeout = vwrq->value;
+ lbs_deb_debugfs("auto deep sleep: "
+ "already enabled\n");
+ }
+ return 0;
+ } else {
+ if (priv->is_auto_deep_sleep_enabled) {
+ lbs_exit_auto_deep_sleep(priv);
+ /* Try to exit deep sleep if auto */
+ /*deep sleep disabled */
+ ret = lbs_set_deep_sleep(priv, 0);
+ }
+ if (vwrq->value == 0)
+ ret = lbs_set_deep_sleep(priv, 1);
+ else if (vwrq->value == -1000)
+ ret = lbs_set_deep_sleep(priv, 0);
+ return ret;
+ }
}
if (priv->psmode != LBS802_11POWERMODECAM) {
@@ -752,6 +818,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
}
lbs_deb_leave(LBS_DEB_WEXT);
+
return 0;
}
@@ -785,7 +852,7 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
u32 rssi_qual;
u32 tx_qual;
u32 quality = 0;
- int stats_valid = 0;
+ int ret, stats_valid = 0;
u8 rssi;
u32 tx_retries;
struct cmd_ds_802_11_get_log log;
@@ -834,7 +901,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
memset(&log, 0, sizeof(log));
log.hdr.size = cpu_to_le16(sizeof(log));
- lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
+ ret = lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log);
+ if (ret)
+ goto out;
tx_retries = le32_to_cpu(log.retry);
@@ -862,8 +931,10 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
stats_valid = 1;
/* update stats asynchronously for future calls */
- lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
+ ret = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
0, 0, NULL);
+ if (ret)
+ lbs_pr_err("RSSI command failed\n");
out:
if (!stats_valid) {
priv->wstats.miss.beacon = 0;
@@ -973,7 +1044,7 @@ static int lbs_mesh_set_freq(struct net_device *dev,
goto out;
}
- if (fwrq->m != priv->curbssparams.channel) {
+ if (fwrq->m != priv->channel) {
lbs_deb_wext("mesh channel change forces eth disconnect\n");
if (priv->mode == IW_MODE_INFRA)
lbs_cmd_80211_deauthenticate(priv,
@@ -1000,6 +1071,7 @@ static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
u8 rates[MAX_RATES + 1];
lbs_deb_enter(LBS_DEB_WEXT);
+
lbs_deb_wext("vwrq->value %d\n", vwrq->value);
lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
@@ -1975,7 +2047,7 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
{
struct lbs_private *priv = dev->ml_priv;
int ret = 0;
- u8 ssid[IW_ESSID_MAX_SIZE];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len = 0;
struct assoc_request * assoc_req;
int in_ssid_len = dwrq->length;
@@ -1989,7 +2061,7 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
}
/* Check the size of the string */
- if (in_ssid_len > IW_ESSID_MAX_SIZE) {
+ if (in_ssid_len > IEEE80211_MAX_SSID_LEN) {
ret = -E2BIG;
goto out;
}
@@ -2020,7 +2092,7 @@ out:
ret = -ENOMEM;
} else {
/* Copy the SSID to the association request */
- memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE);
+ memcpy(&assoc_req->ssid, &ssid, IEEE80211_MAX_SSID_LEN);
assoc_req->ssid_len = ssid_len;
set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
lbs_postpone_association_work(priv);
@@ -2071,7 +2143,7 @@ static int lbs_mesh_set_essid(struct net_device *dev,
}
/* Check the size of the string */
- if (dwrq->length > IW_ESSID_MAX_SIZE) {
+ if (dwrq->length > IEEE80211_MAX_SSID_LEN) {
ret = -E2BIG;
goto out;
}
@@ -2086,7 +2158,7 @@ static int lbs_mesh_set_essid(struct net_device *dev,
}
lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
- priv->curbssparams.channel);
+ priv->channel);
out:
lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
return ret;
diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h
index 4c08db49760..f3f19fe8c6c 100644
--- a/drivers/net/wireless/libertas/wext.h
+++ b/drivers/net/wireless/libertas/wext.h
@@ -4,7 +4,14 @@
#ifndef _LBS_WEXT_H_
#define _LBS_WEXT_H_
+void lbs_send_disconnect_notification(struct lbs_private *priv);
+void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
+
+struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
+ struct lbs_private *priv,
+ u8 band,
+ u16 channel);
+
extern struct iw_handler_def lbs_handler_def;
-extern struct iw_handler_def mesh_handler_def;
#endif
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c
index 392337b37b1..3691c307e67 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/libertas_tf/if_usb.c
@@ -23,6 +23,8 @@
static char *lbtf_fw_name = "lbtf_usb.bin";
module_param_named(fw_name, lbtf_fw_name, charp, 0644);
+MODULE_FIRMWARE("lbtf_usb.bin");
+
static struct usb_device_id if_usb_table[] = {
/* Enter the device signature inside */
{ USB_DEVICE(0x1286, 0x2001) },
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 38cfd79e059..88e41176e7f 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -284,7 +284,7 @@ struct mac80211_hwsim_data {
struct ieee80211_channel *channel;
unsigned long beacon_int; /* in jiffies unit */
unsigned int rx_filter;
- int started;
+ bool started, idle;
struct timer_list beacon_timer;
enum ps_mode {
PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
@@ -365,6 +365,49 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
}
+static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct sk_buff *skb;
+ struct hwsim_radiotap_hdr *hdr;
+ u16 flags;
+ struct ieee80211_hdr *hdr11;
+
+ if (!netif_running(hwsim_mon))
+ return;
+
+ skb = dev_alloc_skb(100);
+ if (skb == NULL)
+ return;
+
+ hdr = (struct hwsim_radiotap_hdr *) skb_put(skb, sizeof(*hdr));
+ hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ hdr->hdr.it_pad = 0;
+ hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr));
+ hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL));
+ hdr->rt_flags = 0;
+ hdr->rt_rate = 0;
+ hdr->rt_channel = cpu_to_le16(data->channel->center_freq);
+ flags = IEEE80211_CHAN_2GHZ;
+ hdr->rt_chbitmask = cpu_to_le16(flags);
+
+ hdr11 = (struct ieee80211_hdr *) skb_put(skb, 10);
+ hdr11->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_ACK);
+ hdr11->duration_id = cpu_to_le16(0);
+ memcpy(hdr11->addr1, addr, ETH_ALEN);
+
+ skb->dev = hwsim_mon;
+ skb_set_mac_header(skb, 0);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+}
+
+
static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
struct sk_buff *skb)
{
@@ -402,6 +445,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rx_status rx_status;
+ if (data->idle) {
+ printk(KERN_DEBUG "%s: Trying to TX when idle - reject\n",
+ wiphy_name(hw->wiphy));
+ return false;
+ }
+
memset(&rx_status, 0, sizeof(rx_status));
/* TODO: set mactime */
rx_status.freq = data->channel->center_freq;
@@ -428,7 +477,8 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
if (data == data2)
continue;
- if (!data2->started || !hwsim_ps_rx_ok(data2, skb) ||
+ if (data2->idle || !data2->started ||
+ !hwsim_ps_rx_ok(data2, skb) ||
!data->channel || !data2->channel ||
data->channel->center_freq != data2->channel->center_freq ||
!(data->group & data2->group))
@@ -464,6 +514,10 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
}
ack = mac80211_hwsim_tx_frame(hw, skb);
+ if (ack && skb->len >= 16) {
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(hw, hdr->addr2);
+ }
txi = IEEE80211_SKB_CB(skb);
@@ -571,6 +625,8 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
!!(conf->flags & IEEE80211_CONF_IDLE),
!!(conf->flags & IEEE80211_CONF_PS));
+ data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
+
data->channel = conf->channel;
if (!data->started || !data->beacon_int)
del_timer(&data->beacon_timer);
@@ -1045,19 +1101,20 @@ static int __init init_mac80211_hwsim(void)
sband->channels = data->channels_2ghz;
sband->n_channels =
ARRAY_SIZE(hwsim_channels_2ghz);
+ sband->bitrates = data->rates;
+ sband->n_bitrates = ARRAY_SIZE(hwsim_rates);
break;
case IEEE80211_BAND_5GHZ:
sband->channels = data->channels_5ghz;
sband->n_channels =
ARRAY_SIZE(hwsim_channels_5ghz);
+ sband->bitrates = data->rates + 4;
+ sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
break;
default:
break;
}
- sband->bitrates = data->rates;
- sband->n_bitrates = ARRAY_SIZE(hwsim_rates);
-
sband->ht_cap.ht_supported = true;
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
@@ -1089,46 +1146,46 @@ static int __init init_mac80211_hwsim(void)
break;
case HWSIM_REGTEST_WORLD_ROAM:
if (i == 0) {
- hw->wiphy->custom_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
wiphy_apply_custom_regulatory(hw->wiphy,
&hwsim_world_regdom_custom_01);
}
break;
case HWSIM_REGTEST_CUSTOM_WORLD:
- hw->wiphy->custom_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
wiphy_apply_custom_regulatory(hw->wiphy,
&hwsim_world_regdom_custom_01);
break;
case HWSIM_REGTEST_CUSTOM_WORLD_2:
if (i == 0) {
- hw->wiphy->custom_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
wiphy_apply_custom_regulatory(hw->wiphy,
&hwsim_world_regdom_custom_01);
} else if (i == 1) {
- hw->wiphy->custom_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
wiphy_apply_custom_regulatory(hw->wiphy,
&hwsim_world_regdom_custom_02);
}
break;
case HWSIM_REGTEST_STRICT_ALL:
- hw->wiphy->strict_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
break;
case HWSIM_REGTEST_STRICT_FOLLOW:
case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
if (i == 0)
- hw->wiphy->strict_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
break;
case HWSIM_REGTEST_ALL:
if (i == 0) {
- hw->wiphy->custom_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
wiphy_apply_custom_regulatory(hw->wiphy,
&hwsim_world_regdom_custom_01);
} else if (i == 1) {
- hw->wiphy->custom_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
wiphy_apply_custom_regulatory(hw->wiphy,
&hwsim_world_regdom_custom_02);
} else if (i == 4)
- hw->wiphy->strict_regulatory = true;
+ hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
break;
default:
break;
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 746532ebe5a..0cb5ecc822a 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/pci.h>
@@ -27,18 +28,6 @@
#define MWL8K_NAME KBUILD_MODNAME
#define MWL8K_VERSION "0.10"
-MODULE_DESCRIPTION(MWL8K_DESC);
-MODULE_VERSION(MWL8K_VERSION);
-MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>");
-MODULE_LICENSE("GPL");
-
-static DEFINE_PCI_DEVICE_TABLE(mwl8k_table) = {
- { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = 8687, },
- { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = 8687, },
- { }
-};
-MODULE_DEVICE_TABLE(pci, mwl8k_table);
-
/* Register definitions */
#define MWL8K_HIU_GEN_PTR 0x00000c10
#define MWL8K_MODE_STA 0x0000005a
@@ -88,72 +77,89 @@ MODULE_DEVICE_TABLE(pci, mwl8k_table);
MWL8K_A2H_INT_RX_READY | \
MWL8K_A2H_INT_TX_DONE)
-/* WME stream classes */
-#define WME_AC_BE 0 /* best effort */
-#define WME_AC_BK 1 /* background */
-#define WME_AC_VI 2 /* video */
-#define WME_AC_VO 3 /* voice */
-
#define MWL8K_RX_QUEUES 1
#define MWL8K_TX_QUEUES 4
+struct rxd_ops {
+ int rxd_size;
+ void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr);
+ void (*rxd_refill)(void *rxd, dma_addr_t addr, int len);
+ int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status);
+};
+
+struct mwl8k_device_info {
+ char *part_name;
+ char *helper_image;
+ char *fw_image;
+ struct rxd_ops *rxd_ops;
+ u16 modes;
+};
+
struct mwl8k_rx_queue {
- int rx_desc_count;
+ int rxd_count;
/* hw receives here */
- int rx_head;
+ int head;
/* refill descs here */
- int rx_tail;
+ int tail;
- struct mwl8k_rx_desc *rx_desc_area;
- dma_addr_t rx_desc_dma;
- struct sk_buff **rx_skb;
+ void *rxd;
+ dma_addr_t rxd_dma;
+ struct {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(dma)
+ } *buf;
};
struct mwl8k_tx_queue {
/* hw transmits here */
- int tx_head;
+ int head;
/* sw appends here */
- int tx_tail;
+ int tail;
- struct ieee80211_tx_queue_stats tx_stats;
- struct mwl8k_tx_desc *tx_desc_area;
- dma_addr_t tx_desc_dma;
- struct sk_buff **tx_skb;
+ struct ieee80211_tx_queue_stats stats;
+ struct mwl8k_tx_desc *txd;
+ dma_addr_t txd_dma;
+ struct sk_buff **skb;
};
/* Pointers to the firmware data and meta information about it. */
struct mwl8k_firmware {
- /* Microcode */
- struct firmware *ucode;
-
/* Boot helper code */
struct firmware *helper;
+
+ /* Microcode */
+ struct firmware *ucode;
};
struct mwl8k_priv {
+ void __iomem *sram;
void __iomem *regs;
struct ieee80211_hw *hw;
struct pci_dev *pdev;
- u8 name[16];
+
+ struct mwl8k_device_info *device_info;
+ bool ap_fw;
+ struct rxd_ops *rxd_ops;
/* firmware files and meta data */
struct mwl8k_firmware fw;
- u32 part_num;
/* firmware access */
struct mutex fw_mutex;
struct task_struct *fw_mutex_owner;
int fw_mutex_depth;
- struct completion *tx_wait;
struct completion *hostcmd_wait;
/* lock held over TX and TX reap */
spinlock_t tx_lock;
+ /* TX quiesce completion, protected by fw_mutex and tx_lock */
+ struct completion *tx_wait;
+
struct ieee80211_vif *vif;
struct ieee80211_channel *current_channel;
@@ -178,10 +184,11 @@ struct mwl8k_priv {
/* PHY parameters */
struct ieee80211_supported_band band;
struct ieee80211_channel channels[14];
- struct ieee80211_rate rates[12];
+ struct ieee80211_rate rates[13];
bool radio_on;
bool radio_short_preamble;
+ bool sniffer_enabled;
bool wmm_enabled;
/* XXX need to convert this to handle multiple interfaces */
@@ -199,9 +206,6 @@ struct mwl8k_priv {
/* Tasklet to reclaim TX descriptors and buffers after tx */
struct tasklet_struct tx_reclaim_task;
-
- /* Work thread to serialize configuration requests */
- struct workqueue_struct *config_wq;
};
/* Per interface specific private data */
@@ -220,7 +224,7 @@ struct mwl8k_vif {
* Subset of supported legacy rates.
* Intersection of AP and STA supported rates.
*/
- struct ieee80211_rate legacy_rates[12];
+ struct ieee80211_rate legacy_rates[13];
/* number of supported legacy rates */
u8 legacy_nrates;
@@ -252,9 +256,10 @@ static const struct ieee80211_rate mwl8k_rates[] = {
{ .bitrate = 10, .hw_value = 2, },
{ .bitrate = 20, .hw_value = 4, },
{ .bitrate = 55, .hw_value = 11, },
+ { .bitrate = 110, .hw_value = 22, },
+ { .bitrate = 220, .hw_value = 44, },
{ .bitrate = 60, .hw_value = 12, },
{ .bitrate = 90, .hw_value = 18, },
- { .bitrate = 110, .hw_value = 22, },
{ .bitrate = 120, .hw_value = 24, },
{ .bitrate = 180, .hw_value = 36, },
{ .bitrate = 240, .hw_value = 48, },
@@ -270,10 +275,12 @@ static const struct ieee80211_rate mwl8k_rates[] = {
/* Firmware command codes */
#define MWL8K_CMD_CODE_DNLD 0x0001
#define MWL8K_CMD_GET_HW_SPEC 0x0003
+#define MWL8K_CMD_SET_HW_SPEC 0x0004
#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010
#define MWL8K_CMD_GET_STAT 0x0014
#define MWL8K_CMD_RADIO_CONTROL 0x001c
#define MWL8K_CMD_RF_TX_POWER 0x001e
+#define MWL8K_CMD_RF_ANTENNA 0x0020
#define MWL8K_CMD_SET_PRE_SCAN 0x0107
#define MWL8K_CMD_SET_POST_SCAN 0x0108
#define MWL8K_CMD_SET_RF_CHANNEL 0x010a
@@ -287,6 +294,7 @@ static const struct ieee80211_rate mwl8k_rates[] = {
#define MWL8K_CMD_MIMO_CONFIG 0x0125
#define MWL8K_CMD_USE_FIXED_RATE 0x0126
#define MWL8K_CMD_ENABLE_SNIFFER 0x0150
+#define MWL8K_CMD_SET_MAC_ADDR 0x0202
#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
#define MWL8K_CMD_UPDATE_STADB 0x1123
@@ -299,10 +307,12 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize)
switch (cmd & ~0x8000) {
MWL8K_CMDNAME(CODE_DNLD);
MWL8K_CMDNAME(GET_HW_SPEC);
+ MWL8K_CMDNAME(SET_HW_SPEC);
MWL8K_CMDNAME(MAC_MULTICAST_ADR);
MWL8K_CMDNAME(GET_STAT);
MWL8K_CMDNAME(RADIO_CONTROL);
MWL8K_CMDNAME(RF_TX_POWER);
+ MWL8K_CMDNAME(RF_ANTENNA);
MWL8K_CMDNAME(SET_PRE_SCAN);
MWL8K_CMDNAME(SET_POST_SCAN);
MWL8K_CMDNAME(SET_RF_CHANNEL);
@@ -316,6 +326,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize)
MWL8K_CMDNAME(MIMO_CONFIG);
MWL8K_CMDNAME(USE_FIXED_RATE);
MWL8K_CMDNAME(ENABLE_SNIFFER);
+ MWL8K_CMDNAME(SET_MAC_ADDR);
MWL8K_CMDNAME(SET_RATEADAPT_MODE);
MWL8K_CMDNAME(UPDATE_STADB);
default:
@@ -353,41 +364,35 @@ static void mwl8k_release_firmware(struct mwl8k_priv *priv)
/* Request fw image */
static int mwl8k_request_fw(struct mwl8k_priv *priv,
- const char *fname, struct firmware **fw)
+ const char *fname, struct firmware **fw)
{
/* release current image */
if (*fw != NULL)
mwl8k_release_fw(fw);
return request_firmware((const struct firmware **)fw,
- fname, &priv->pdev->dev);
+ fname, &priv->pdev->dev);
}
-static int mwl8k_request_firmware(struct mwl8k_priv *priv, u32 part_num)
+static int mwl8k_request_firmware(struct mwl8k_priv *priv)
{
- u8 filename[64];
+ struct mwl8k_device_info *di = priv->device_info;
int rc;
- priv->part_num = part_num;
-
- snprintf(filename, sizeof(filename),
- "mwl8k/helper_%u.fw", priv->part_num);
-
- rc = mwl8k_request_fw(priv, filename, &priv->fw.helper);
- if (rc) {
- printk(KERN_ERR
- "%s Error requesting helper firmware file %s\n",
- pci_name(priv->pdev), filename);
- return rc;
+ if (di->helper_image != NULL) {
+ rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw.helper);
+ if (rc) {
+ printk(KERN_ERR "%s: Error requesting helper "
+ "firmware file %s\n", pci_name(priv->pdev),
+ di->helper_image);
+ return rc;
+ }
}
- snprintf(filename, sizeof(filename),
- "mwl8k/fmimage_%u.fw", priv->part_num);
-
- rc = mwl8k_request_fw(priv, filename, &priv->fw.ucode);
+ rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw.ucode);
if (rc) {
- printk(KERN_ERR "%s Error requesting firmware file %s\n",
- pci_name(priv->pdev), filename);
+ printk(KERN_ERR "%s: Error requesting firmware file %s\n",
+ pci_name(priv->pdev), di->fw_image);
mwl8k_release_fw(&priv->fw.helper);
return rc;
}
@@ -395,6 +400,9 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv, u32 part_num)
return 0;
}
+MODULE_FIRMWARE("mwl8k/helper_8687.fw");
+MODULE_FIRMWARE("mwl8k/fmimage_8687.fw");
+
struct mwl8k_cmd_pkt {
__le16 code;
__le16 length;
@@ -434,6 +442,7 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)
break;
}
+ cond_resched();
udelay(1);
} while (--loops);
@@ -542,43 +551,62 @@ static int mwl8k_feed_fw_image(struct mwl8k_priv *priv,
return rc;
}
-static int mwl8k_load_firmware(struct mwl8k_priv *priv)
+static int mwl8k_load_firmware(struct ieee80211_hw *hw)
{
- int loops, rc;
+ struct mwl8k_priv *priv = hw->priv;
+ struct firmware *fw = priv->fw.ucode;
+ struct mwl8k_device_info *di = priv->device_info;
+ int rc;
+ int loops;
- const u8 *ucode = priv->fw.ucode->data;
- size_t ucode_len = priv->fw.ucode->size;
- const u8 *helper = priv->fw.helper->data;
- size_t helper_len = priv->fw.helper->size;
+ if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) {
+ struct firmware *helper = priv->fw.helper;
- if (!memcmp(ucode, "\x01\x00\x00\x00", 4)) {
- rc = mwl8k_load_fw_image(priv, helper, helper_len);
+ if (helper == NULL) {
+ printk(KERN_ERR "%s: helper image needed but none "
+ "given\n", pci_name(priv->pdev));
+ return -EINVAL;
+ }
+
+ rc = mwl8k_load_fw_image(priv, helper->data, helper->size);
if (rc) {
printk(KERN_ERR "%s: unable to load firmware "
- "helper image\n", pci_name(priv->pdev));
+ "helper image\n", pci_name(priv->pdev));
return rc;
}
msleep(1);
- rc = mwl8k_feed_fw_image(priv, ucode, ucode_len);
+ rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);
} else {
- rc = mwl8k_load_fw_image(priv, ucode, ucode_len);
+ rc = mwl8k_load_fw_image(priv, fw->data, fw->size);
}
if (rc) {
- printk(KERN_ERR "%s: unable to load firmware data\n",
- pci_name(priv->pdev));
+ printk(KERN_ERR "%s: unable to load firmware image\n",
+ pci_name(priv->pdev));
return rc;
}
- iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
+ if (di->modes & BIT(NL80211_IFTYPE_AP))
+ iowrite32(MWL8K_MODE_AP, priv->regs + MWL8K_HIU_GEN_PTR);
+ else
+ iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
msleep(1);
loops = 200000;
do {
- if (ioread32(priv->regs + MWL8K_HIU_INT_CODE)
- == MWL8K_FWSTA_READY)
+ u32 ready_code;
+
+ ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE);
+ if (ready_code == MWL8K_FWAP_READY) {
+ priv->ap_fw = 1;
+ break;
+ } else if (ready_code == MWL8K_FWSTA_READY) {
+ priv->ap_fw = 0;
break;
+ }
+
+ cond_resched();
udelay(1);
} while (--loops);
@@ -605,7 +633,7 @@ struct ewc_ht_info {
/* Peer Entry flags - used to define the type of the peer node */
#define MWL8K_PEER_TYPE_ACCESSPOINT 2
-#define MWL8K_IEEE_LEGACY_DATA_RATES 12
+#define MWL8K_IEEE_LEGACY_DATA_RATES 13
#define MWL8K_MCS_BITMAP_SIZE 16
struct peer_capability_info {
@@ -731,16 +759,96 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb)
/*
- * Packet reception.
+ * Packet reception for 88w8366.
*/
-#define MWL8K_RX_CTRL_OWNED_BY_HOST 0x02
+struct mwl8k_rxd_8366 {
+ __le16 pkt_len;
+ __u8 sq2;
+ __u8 rate;
+ __le32 pkt_phys_addr;
+ __le32 next_rxd_phys_addr;
+ __le16 qos_control;
+ __le16 htsig2;
+ __le32 hw_rssi_info;
+ __le32 hw_noise_floor_info;
+ __u8 noise_floor;
+ __u8 pad0[3];
+ __u8 rssi;
+ __u8 rx_status;
+ __u8 channel;
+ __u8 rx_ctrl;
+} __attribute__((packed));
+
+#define MWL8K_8366_RX_CTRL_OWNED_BY_HOST 0x80
+
+static void mwl8k_rxd_8366_init(void *_rxd, dma_addr_t next_dma_addr)
+{
+ struct mwl8k_rxd_8366 *rxd = _rxd;
+
+ rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
+ rxd->rx_ctrl = MWL8K_8366_RX_CTRL_OWNED_BY_HOST;
+}
+
+static void mwl8k_rxd_8366_refill(void *_rxd, dma_addr_t addr, int len)
+{
+ struct mwl8k_rxd_8366 *rxd = _rxd;
+
+ rxd->pkt_len = cpu_to_le16(len);
+ rxd->pkt_phys_addr = cpu_to_le32(addr);
+ wmb();
+ rxd->rx_ctrl = 0;
+}
+
+static int
+mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status)
+{
+ struct mwl8k_rxd_8366 *rxd = _rxd;
+
+ if (!(rxd->rx_ctrl & MWL8K_8366_RX_CTRL_OWNED_BY_HOST))
+ return -1;
+ rmb();
+
+ memset(status, 0, sizeof(*status));
+
+ status->signal = -rxd->rssi;
+ status->noise = -rxd->noise_floor;
-struct mwl8k_rx_desc {
+ if (rxd->rate & 0x80) {
+ status->flag |= RX_FLAG_HT;
+ status->rate_idx = rxd->rate & 0x7f;
+ } else {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mwl8k_rates); i++) {
+ if (mwl8k_rates[i].hw_value == rxd->rate) {
+ status->rate_idx = i;
+ break;
+ }
+ }
+ }
+
+ status->band = IEEE80211_BAND_2GHZ;
+ status->freq = ieee80211_channel_to_frequency(rxd->channel);
+
+ return le16_to_cpu(rxd->pkt_len);
+}
+
+static struct rxd_ops rxd_8366_ops = {
+ .rxd_size = sizeof(struct mwl8k_rxd_8366),
+ .rxd_init = mwl8k_rxd_8366_init,
+ .rxd_refill = mwl8k_rxd_8366_refill,
+ .rxd_process = mwl8k_rxd_8366_process,
+};
+
+/*
+ * Packet reception for 88w8687.
+ */
+struct mwl8k_rxd_8687 {
__le16 pkt_len;
__u8 link_quality;
__u8 noise_level;
__le32 pkt_phys_addr;
- __le32 next_rx_desc_phys_addr;
+ __le32 next_rxd_phys_addr;
__le16 qos_control;
__le16 rate_info;
__le32 pad0[4];
@@ -752,6 +860,76 @@ struct mwl8k_rx_desc {
__u8 pad2[2];
} __attribute__((packed));
+#define MWL8K_8687_RATE_INFO_SHORTPRE 0x8000
+#define MWL8K_8687_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3)
+#define MWL8K_8687_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f)
+#define MWL8K_8687_RATE_INFO_40MHZ 0x0004
+#define MWL8K_8687_RATE_INFO_SHORTGI 0x0002
+#define MWL8K_8687_RATE_INFO_MCS_FORMAT 0x0001
+
+#define MWL8K_8687_RX_CTRL_OWNED_BY_HOST 0x02
+
+static void mwl8k_rxd_8687_init(void *_rxd, dma_addr_t next_dma_addr)
+{
+ struct mwl8k_rxd_8687 *rxd = _rxd;
+
+ rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
+ rxd->rx_ctrl = MWL8K_8687_RX_CTRL_OWNED_BY_HOST;
+}
+
+static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len)
+{
+ struct mwl8k_rxd_8687 *rxd = _rxd;
+
+ rxd->pkt_len = cpu_to_le16(len);
+ rxd->pkt_phys_addr = cpu_to_le32(addr);
+ wmb();
+ rxd->rx_ctrl = 0;
+}
+
+static int
+mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status)
+{
+ struct mwl8k_rxd_8687 *rxd = _rxd;
+ u16 rate_info;
+
+ if (!(rxd->rx_ctrl & MWL8K_8687_RX_CTRL_OWNED_BY_HOST))
+ return -1;
+ rmb();
+
+ rate_info = le16_to_cpu(rxd->rate_info);
+
+ memset(status, 0, sizeof(*status));
+
+ status->signal = -rxd->rssi;
+ status->noise = -rxd->noise_level;
+ status->qual = rxd->link_quality;
+ status->antenna = MWL8K_8687_RATE_INFO_ANTSELECT(rate_info);
+ status->rate_idx = MWL8K_8687_RATE_INFO_RATEID(rate_info);
+
+ if (rate_info & MWL8K_8687_RATE_INFO_SHORTPRE)
+ status->flag |= RX_FLAG_SHORTPRE;
+ if (rate_info & MWL8K_8687_RATE_INFO_40MHZ)
+ status->flag |= RX_FLAG_40MHZ;
+ if (rate_info & MWL8K_8687_RATE_INFO_SHORTGI)
+ status->flag |= RX_FLAG_SHORT_GI;
+ if (rate_info & MWL8K_8687_RATE_INFO_MCS_FORMAT)
+ status->flag |= RX_FLAG_HT;
+
+ status->band = IEEE80211_BAND_2GHZ;
+ status->freq = ieee80211_channel_to_frequency(rxd->channel);
+
+ return le16_to_cpu(rxd->pkt_len);
+}
+
+static struct rxd_ops rxd_8687_ops = {
+ .rxd_size = sizeof(struct mwl8k_rxd_8687),
+ .rxd_init = mwl8k_rxd_8687_init,
+ .rxd_refill = mwl8k_rxd_8687_refill,
+ .rxd_process = mwl8k_rxd_8687_process,
+};
+
+
#define MWL8K_RX_DESCS 256
#define MWL8K_RX_MAXSZ 3800
@@ -762,43 +940,44 @@ static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index)
int size;
int i;
- rxq->rx_desc_count = 0;
- rxq->rx_head = 0;
- rxq->rx_tail = 0;
+ rxq->rxd_count = 0;
+ rxq->head = 0;
+ rxq->tail = 0;
- size = MWL8K_RX_DESCS * sizeof(struct mwl8k_rx_desc);
+ size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size;
- rxq->rx_desc_area =
- pci_alloc_consistent(priv->pdev, size, &rxq->rx_desc_dma);
- if (rxq->rx_desc_area == NULL) {
+ rxq->rxd = pci_alloc_consistent(priv->pdev, size, &rxq->rxd_dma);
+ if (rxq->rxd == NULL) {
printk(KERN_ERR "%s: failed to alloc RX descriptors\n",
- priv->name);
+ wiphy_name(hw->wiphy));
return -ENOMEM;
}
- memset(rxq->rx_desc_area, 0, size);
+ memset(rxq->rxd, 0, size);
- rxq->rx_skb = kmalloc(MWL8K_RX_DESCS *
- sizeof(*rxq->rx_skb), GFP_KERNEL);
- if (rxq->rx_skb == NULL) {
+ rxq->buf = kmalloc(MWL8K_RX_DESCS * sizeof(*rxq->buf), GFP_KERNEL);
+ if (rxq->buf == NULL) {
printk(KERN_ERR "%s: failed to alloc RX skbuff list\n",
- priv->name);
- pci_free_consistent(priv->pdev, size,
- rxq->rx_desc_area, rxq->rx_desc_dma);
+ wiphy_name(hw->wiphy));
+ pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma);
return -ENOMEM;
}
- memset(rxq->rx_skb, 0, MWL8K_RX_DESCS * sizeof(*rxq->rx_skb));
+ memset(rxq->buf, 0, MWL8K_RX_DESCS * sizeof(*rxq->buf));
for (i = 0; i < MWL8K_RX_DESCS; i++) {
- struct mwl8k_rx_desc *rx_desc;
+ int desc_size;
+ void *rxd;
int nexti;
+ dma_addr_t next_dma_addr;
- rx_desc = rxq->rx_desc_area + i;
- nexti = (i + 1) % MWL8K_RX_DESCS;
+ desc_size = priv->rxd_ops->rxd_size;
+ rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size);
- rx_desc->next_rx_desc_phys_addr =
- cpu_to_le32(rxq->rx_desc_dma
- + nexti * sizeof(*rx_desc));
- rx_desc->rx_ctrl = MWL8K_RX_CTRL_OWNED_BY_HOST;
+ nexti = i + 1;
+ if (nexti == MWL8K_RX_DESCS)
+ nexti = 0;
+ next_dma_addr = rxq->rxd_dma + (nexti * desc_size);
+
+ priv->rxd_ops->rxd_init(rxd, next_dma_addr);
}
return 0;
@@ -811,27 +990,28 @@ static int rxq_refill(struct ieee80211_hw *hw, int index, int limit)
int refilled;
refilled = 0;
- while (rxq->rx_desc_count < MWL8K_RX_DESCS && limit--) {
+ while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) {
struct sk_buff *skb;
+ dma_addr_t addr;
int rx;
+ void *rxd;
skb = dev_alloc_skb(MWL8K_RX_MAXSZ);
if (skb == NULL)
break;
- rxq->rx_desc_count++;
-
- rx = rxq->rx_tail;
- rxq->rx_tail = (rx + 1) % MWL8K_RX_DESCS;
+ addr = pci_map_single(priv->pdev, skb->data,
+ MWL8K_RX_MAXSZ, DMA_FROM_DEVICE);
- rxq->rx_desc_area[rx].pkt_phys_addr =
- cpu_to_le32(pci_map_single(priv->pdev, skb->data,
- MWL8K_RX_MAXSZ, DMA_FROM_DEVICE));
+ rxq->rxd_count++;
+ rx = rxq->tail++;
+ if (rxq->tail == MWL8K_RX_DESCS)
+ rxq->tail = 0;
+ rxq->buf[rx].skb = skb;
+ pci_unmap_addr_set(&rxq->buf[rx], dma, addr);
- rxq->rx_desc_area[rx].pkt_len = cpu_to_le16(MWL8K_RX_MAXSZ);
- rxq->rx_skb[rx] = skb;
- wmb();
- rxq->rx_desc_area[rx].rx_ctrl = 0;
+ rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size);
+ priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ);
refilled++;
}
@@ -847,24 +1027,24 @@ static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index)
int i;
for (i = 0; i < MWL8K_RX_DESCS; i++) {
- if (rxq->rx_skb[i] != NULL) {
- unsigned long addr;
-
- addr = le32_to_cpu(rxq->rx_desc_area[i].pkt_phys_addr);
- pci_unmap_single(priv->pdev, addr, MWL8K_RX_MAXSZ,
- PCI_DMA_FROMDEVICE);
- kfree_skb(rxq->rx_skb[i]);
- rxq->rx_skb[i] = NULL;
+ if (rxq->buf[i].skb != NULL) {
+ pci_unmap_single(priv->pdev,
+ pci_unmap_addr(&rxq->buf[i], dma),
+ MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
+ pci_unmap_addr_set(&rxq->buf[i], dma, 0);
+
+ kfree_skb(rxq->buf[i].skb);
+ rxq->buf[i].skb = NULL;
}
}
- kfree(rxq->rx_skb);
- rxq->rx_skb = NULL;
+ kfree(rxq->buf);
+ rxq->buf = NULL;
pci_free_consistent(priv->pdev,
- MWL8K_RX_DESCS * sizeof(struct mwl8k_rx_desc),
- rxq->rx_desc_area, rxq->rx_desc_dma);
- rxq->rx_desc_area = NULL;
+ MWL8K_RX_DESCS * priv->rxd_ops->rxd_size,
+ rxq->rxd, rxq->rxd_dma);
+ rxq->rxd = NULL;
}
@@ -880,9 +1060,11 @@ mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)
!compare_ether_addr(wh->addr3, priv->capture_bssid);
}
-static inline void mwl8k_save_beacon(struct mwl8k_priv *priv,
- struct sk_buff *skb)
+static inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
+ struct mwl8k_priv *priv = hw->priv;
+
priv->capture_beacon = false;
memset(priv->capture_bssid, 0, ETH_ALEN);
@@ -893,8 +1075,7 @@ static inline void mwl8k_save_beacon(struct mwl8k_priv *priv,
*/
priv->beacon_skb = skb_copy(skb, GFP_ATOMIC);
if (priv->beacon_skb != NULL)
- queue_work(priv->config_wq,
- &priv->finalize_join_worker);
+ ieee80211_queue_work(hw, &priv->finalize_join_worker);
}
static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
@@ -904,53 +1085,46 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
int processed;
processed = 0;
- while (rxq->rx_desc_count && limit--) {
- struct mwl8k_rx_desc *rx_desc;
+ while (rxq->rxd_count && limit--) {
struct sk_buff *skb;
+ void *rxd;
+ int pkt_len;
struct ieee80211_rx_status status;
- unsigned long addr;
- struct ieee80211_hdr *wh;
- rx_desc = rxq->rx_desc_area + rxq->rx_head;
- if (!(rx_desc->rx_ctrl & MWL8K_RX_CTRL_OWNED_BY_HOST))
+ skb = rxq->buf[rxq->head].skb;
+ if (skb == NULL)
break;
- rmb();
- skb = rxq->rx_skb[rxq->rx_head];
- if (skb == NULL)
+ rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size);
+
+ pkt_len = priv->rxd_ops->rxd_process(rxd, &status);
+ if (pkt_len < 0)
break;
- rxq->rx_skb[rxq->rx_head] = NULL;
- rxq->rx_head = (rxq->rx_head + 1) % MWL8K_RX_DESCS;
- rxq->rx_desc_count--;
+ rxq->buf[rxq->head].skb = NULL;
- addr = le32_to_cpu(rx_desc->pkt_phys_addr);
- pci_unmap_single(priv->pdev, addr,
- MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
+ pci_unmap_single(priv->pdev,
+ pci_unmap_addr(&rxq->buf[rxq->head], dma),
+ MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE);
+ pci_unmap_addr_set(&rxq->buf[rxq->head], dma, 0);
- skb_put(skb, le16_to_cpu(rx_desc->pkt_len));
- mwl8k_remove_dma_header(skb);
+ rxq->head++;
+ if (rxq->head == MWL8K_RX_DESCS)
+ rxq->head = 0;
+
+ rxq->rxd_count--;
- wh = (struct ieee80211_hdr *)skb->data;
+ skb_put(skb, pkt_len);
+ mwl8k_remove_dma_header(skb);
/*
- * Check for pending join operation. save a copy of
- * the beacon and schedule a tasklet to send finalize
- * join command to the firmware.
+ * Check for a pending join operation. Save a
+ * copy of the beacon and schedule a tasklet to
+ * send a FINALIZE_JOIN command to the firmware.
*/
- if (mwl8k_capture_bssid(priv, wh))
- mwl8k_save_beacon(priv, skb);
-
- memset(&status, 0, sizeof(status));
- status.mactime = 0;
- status.signal = -rx_desc->rssi;
- status.noise = -rx_desc->noise_level;
- status.qual = rx_desc->link_quality;
- status.antenna = 1;
- status.rate_idx = 1;
- status.flag = 0;
- status.band = IEEE80211_BAND_2GHZ;
- status.freq = ieee80211_channel_to_frequency(rx_desc->channel);
+ if (mwl8k_capture_bssid(priv, (void *)skb->data))
+ mwl8k_save_beacon(hw, skb);
+
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
ieee80211_rx_irqsafe(hw, skb);
@@ -965,24 +1139,10 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
* Packet transmission.
*/
-/* Transmit queue assignment. */
-enum {
- MWL8K_WME_AC_BK = 0, /* background access */
- MWL8K_WME_AC_BE = 1, /* best effort access */
- MWL8K_WME_AC_VI = 2, /* video access */
- MWL8K_WME_AC_VO = 3, /* voice access */
-};
-
/* Transmit packet ACK policy */
#define MWL8K_TXD_ACK_POLICY_NORMAL 0
#define MWL8K_TXD_ACK_POLICY_BLOCKACK 3
-#define GET_TXQ(_ac) (\
- ((_ac) == WME_AC_VO) ? MWL8K_WME_AC_VO : \
- ((_ac) == WME_AC_VI) ? MWL8K_WME_AC_VI : \
- ((_ac) == WME_AC_BK) ? MWL8K_WME_AC_BK : \
- MWL8K_WME_AC_BE)
-
#define MWL8K_TXD_STATUS_OK 0x00000001
#define MWL8K_TXD_STATUS_OK_RETRY 0x00000002
#define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004
@@ -997,7 +1157,7 @@ struct mwl8k_tx_desc {
__le32 pkt_phys_addr;
__le16 pkt_len;
__u8 dest_MAC_addr[ETH_ALEN];
- __le32 next_tx_desc_phys_addr;
+ __le32 next_txd_phys_addr;
__le32 reserved;
__le16 rate_info;
__u8 peer_id;
@@ -1013,44 +1173,40 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index)
int size;
int i;
- memset(&txq->tx_stats, 0, sizeof(struct ieee80211_tx_queue_stats));
- txq->tx_stats.limit = MWL8K_TX_DESCS;
- txq->tx_head = 0;
- txq->tx_tail = 0;
+ memset(&txq->stats, 0, sizeof(struct ieee80211_tx_queue_stats));
+ txq->stats.limit = MWL8K_TX_DESCS;
+ txq->head = 0;
+ txq->tail = 0;
size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc);
- txq->tx_desc_area =
- pci_alloc_consistent(priv->pdev, size, &txq->tx_desc_dma);
- if (txq->tx_desc_area == NULL) {
+ txq->txd = pci_alloc_consistent(priv->pdev, size, &txq->txd_dma);
+ if (txq->txd == NULL) {
printk(KERN_ERR "%s: failed to alloc TX descriptors\n",
- priv->name);
+ wiphy_name(hw->wiphy));
return -ENOMEM;
}
- memset(txq->tx_desc_area, 0, size);
+ memset(txq->txd, 0, size);
- txq->tx_skb = kmalloc(MWL8K_TX_DESCS * sizeof(*txq->tx_skb),
- GFP_KERNEL);
- if (txq->tx_skb == NULL) {
+ txq->skb = kmalloc(MWL8K_TX_DESCS * sizeof(*txq->skb), GFP_KERNEL);
+ if (txq->skb == NULL) {
printk(KERN_ERR "%s: failed to alloc TX skbuff list\n",
- priv->name);
- pci_free_consistent(priv->pdev, size,
- txq->tx_desc_area, txq->tx_desc_dma);
+ wiphy_name(hw->wiphy));
+ pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma);
return -ENOMEM;
}
- memset(txq->tx_skb, 0, MWL8K_TX_DESCS * sizeof(*txq->tx_skb));
+ memset(txq->skb, 0, MWL8K_TX_DESCS * sizeof(*txq->skb));
for (i = 0; i < MWL8K_TX_DESCS; i++) {
struct mwl8k_tx_desc *tx_desc;
int nexti;
- tx_desc = txq->tx_desc_area + i;
+ tx_desc = txq->txd + i;
nexti = (i + 1) % MWL8K_TX_DESCS;
tx_desc->status = 0;
- tx_desc->next_tx_desc_phys_addr =
- cpu_to_le32(txq->tx_desc_dma +
- nexti * sizeof(*tx_desc));
+ tx_desc->next_txd_phys_addr =
+ cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc));
}
return 0;
@@ -1065,11 +1221,6 @@ static inline void mwl8k_tx_start(struct mwl8k_priv *priv)
ioread32(priv->regs + MWL8K_HIU_INT_CODE);
}
-static inline int mwl8k_txq_busy(struct mwl8k_priv *priv)
-{
- return priv->pending_tx_pkts;
-}
-
struct mwl8k_txq_info {
u32 fw_owned;
u32 drv_owned;
@@ -1089,14 +1240,13 @@ static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv,
memset(txinfo, 0, MWL8K_TX_QUEUES * sizeof(struct mwl8k_txq_info));
- spin_lock_bh(&priv->tx_lock);
for (count = 0; count < MWL8K_TX_QUEUES; count++) {
txq = priv->txq + count;
- txinfo[count].len = txq->tx_stats.len;
- txinfo[count].head = txq->tx_head;
- txinfo[count].tail = txq->tx_tail;
+ txinfo[count].len = txq->stats.len;
+ txinfo[count].head = txq->head;
+ txinfo[count].tail = txq->tail;
for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
- tx_desc = txq->tx_desc_area + desc;
+ tx_desc = txq->txd + desc;
status = le32_to_cpu(tx_desc->status);
if (status & MWL8K_TXD_STATUS_FW_OWNED)
@@ -1108,30 +1258,26 @@ static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv,
txinfo[count].unused++;
}
}
- spin_unlock_bh(&priv->tx_lock);
return ndescs;
}
/*
- * Must be called with hw->fw_mutex held and tx queues stopped.
+ * Must be called with priv->fw_mutex held and tx queues stopped.
*/
static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
{
struct mwl8k_priv *priv = hw->priv;
- DECLARE_COMPLETION_ONSTACK(cmd_wait);
+ DECLARE_COMPLETION_ONSTACK(tx_wait);
u32 count;
unsigned long timeout;
might_sleep();
spin_lock_bh(&priv->tx_lock);
- count = mwl8k_txq_busy(priv);
- if (count) {
- priv->tx_wait = &cmd_wait;
- if (priv->radio_on)
- mwl8k_tx_start(priv);
- }
+ count = priv->pending_tx_pkts;
+ if (count)
+ priv->tx_wait = &tx_wait;
spin_unlock_bh(&priv->tx_lock);
if (count) {
@@ -1139,23 +1285,23 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
int index;
int newcount;
- timeout = wait_for_completion_timeout(&cmd_wait,
+ timeout = wait_for_completion_timeout(&tx_wait,
msecs_to_jiffies(5000));
if (timeout)
return 0;
spin_lock_bh(&priv->tx_lock);
priv->tx_wait = NULL;
- newcount = mwl8k_txq_busy(priv);
+ newcount = priv->pending_tx_pkts;
+ mwl8k_scan_tx_ring(priv, txinfo);
spin_unlock_bh(&priv->tx_lock);
printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n",
__func__, __LINE__, count, newcount);
- mwl8k_scan_tx_ring(priv, txinfo);
for (index = 0; index < MWL8K_TX_QUEUES; index++)
- printk(KERN_ERR
- "TXQ:%u L:%u H:%u T:%u FW:%u DRV:%u U:%u\n",
+ printk(KERN_ERR "TXQ:%u L:%u H:%u T:%u FW:%u "
+ "DRV:%u U:%u\n",
index,
txinfo[index].len,
txinfo[index].head,
@@ -1181,7 +1327,7 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force)
struct mwl8k_tx_queue *txq = priv->txq + index;
int wake = 0;
- while (txq->tx_stats.len > 0) {
+ while (txq->stats.len > 0) {
int tx;
struct mwl8k_tx_desc *tx_desc;
unsigned long addr;
@@ -1190,8 +1336,8 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force)
struct ieee80211_tx_info *info;
u32 status;
- tx = txq->tx_head;
- tx_desc = txq->tx_desc_area + tx;
+ tx = txq->head;
+ tx_desc = txq->txd + tx;
status = le32_to_cpu(tx_desc->status);
@@ -1202,15 +1348,15 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force)
~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED);
}
- txq->tx_head = (tx + 1) % MWL8K_TX_DESCS;
- BUG_ON(txq->tx_stats.len == 0);
- txq->tx_stats.len--;
+ txq->head = (tx + 1) % MWL8K_TX_DESCS;
+ BUG_ON(txq->stats.len == 0);
+ txq->stats.len--;
priv->pending_tx_pkts--;
addr = le32_to_cpu(tx_desc->pkt_phys_addr);
size = le16_to_cpu(tx_desc->pkt_len);
- skb = txq->tx_skb[tx];
- txq->tx_skb[tx] = NULL;
+ skb = txq->skb[tx];
+ txq->skb[tx] = NULL;
BUG_ON(skb == NULL);
pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE);
@@ -1243,13 +1389,13 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)
mwl8k_txq_reclaim(hw, index, 1);
- kfree(txq->tx_skb);
- txq->tx_skb = NULL;
+ kfree(txq->skb);
+ txq->skb = NULL;
pci_free_consistent(priv->pdev,
MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc),
- txq->tx_desc_area, txq->tx_desc_dma);
- txq->tx_desc_area = NULL;
+ txq->txd, txq->txd_dma);
+ txq->txd = NULL;
}
static int
@@ -1317,7 +1463,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
if (pci_dma_mapping_error(priv->pdev, dma)) {
printk(KERN_DEBUG "%s: failed to dma map skb, "
- "dropping TX frame.\n", priv->name);
+ "dropping TX frame.\n", wiphy_name(hw->wiphy));
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
@@ -1326,10 +1472,10 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
txq = priv->txq + index;
- BUG_ON(txq->tx_skb[txq->tx_tail] != NULL);
- txq->tx_skb[txq->tx_tail] = skb;
+ BUG_ON(txq->skb[txq->tail] != NULL);
+ txq->skb[txq->tail] = skb;
- tx = txq->tx_desc_area + txq->tx_tail;
+ tx = txq->txd + txq->tail;
tx->data_rate = txdatarate;
tx->tx_priority = index;
tx->qos_control = cpu_to_le16(qos);
@@ -1340,15 +1486,15 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
wmb();
tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
- txq->tx_stats.count++;
- txq->tx_stats.len++;
+ txq->stats.count++;
+ txq->stats.len++;
priv->pending_tx_pkts++;
- txq->tx_tail++;
- if (txq->tx_tail == MWL8K_TX_DESCS)
- txq->tx_tail = 0;
+ txq->tail++;
+ if (txq->tail == MWL8K_TX_DESCS)
+ txq->tail = 0;
- if (txq->tx_head == txq->tx_tail)
+ if (txq->head == txq->tail)
ieee80211_stop_queue(hw, index);
mwl8k_tx_start(priv);
@@ -1431,7 +1577,7 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
unsigned long timeout = 0;
u8 buf[32];
- cmd->result = 0xFFFF;
+ cmd->result = 0xffff;
dma_size = le16_to_cpu(cmd->length);
dma_addr = pci_map_single(priv->pdev, cmd, dma_size,
PCI_DMA_BIDIRECTIONAL);
@@ -1464,7 +1610,7 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
if (!timeout) {
printk(KERN_ERR "%s: Command %s timeout after %u ms\n",
- priv->name,
+ wiphy_name(hw->wiphy),
mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
MWL8K_CMD_TIMEOUT_MS);
rc = -ETIMEDOUT;
@@ -1472,7 +1618,7 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
rc = cmd->result ? -EINVAL : 0;
if (rc)
printk(KERN_ERR "%s: Command %s error 0x%x\n",
- priv->name,
+ wiphy_name(hw->wiphy),
mwl8k_cmd_name(cmd->code, buf, sizeof(buf)),
le16_to_cpu(cmd->result));
}
@@ -1481,9 +1627,9 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
}
/*
- * GET_HW_SPEC.
+ * CMD_GET_HW_SPEC (STA version).
*/
-struct mwl8k_cmd_get_hw_spec {
+struct mwl8k_cmd_get_hw_spec_sta {
struct mwl8k_cmd_pkt header;
__u8 hw_rev;
__u8 host_interface;
@@ -1499,13 +1645,13 @@ struct mwl8k_cmd_get_hw_spec {
__le32 tx_queue_ptrs[MWL8K_TX_QUEUES];
__le32 caps2;
__le32 num_tx_desc_per_queue;
- __le32 total_rx_desc;
+ __le32 total_rxd;
} __attribute__((packed));
-static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw)
+static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
{
struct mwl8k_priv *priv = hw->priv;
- struct mwl8k_cmd_get_hw_spec *cmd;
+ struct mwl8k_cmd_get_hw_spec_sta *cmd;
int rc;
int i;
@@ -1518,12 +1664,12 @@ static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw)
memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
- cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rx_desc_dma);
+ cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
for (i = 0; i < MWL8K_TX_QUEUES; i++)
- cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].tx_desc_dma);
+ cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
- cmd->total_rx_desc = cpu_to_le32(MWL8K_RX_DESCS);
+ cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
rc = mwl8k_post_cmd(hw, &cmd->header);
@@ -1539,6 +1685,129 @@ static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw)
}
/*
+ * CMD_GET_HW_SPEC (AP version).
+ */
+struct mwl8k_cmd_get_hw_spec_ap {
+ struct mwl8k_cmd_pkt header;
+ __u8 hw_rev;
+ __u8 host_interface;
+ __le16 num_wcb;
+ __le16 num_mcaddrs;
+ __u8 perm_addr[ETH_ALEN];
+ __le16 region_code;
+ __le16 num_antenna;
+ __le32 fw_rev;
+ __le32 wcbbase0;
+ __le32 rxwrptr;
+ __le32 rxrdptr;
+ __le32 ps_cookie;
+ __le32 wcbbase1;
+ __le32 wcbbase2;
+ __le32 wcbbase3;
+} __attribute__((packed));
+
+static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
+{
+ struct mwl8k_priv *priv = hw->priv;
+ struct mwl8k_cmd_get_hw_spec_ap *cmd;
+ int rc;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC);
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
+
+ memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));
+ cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
+
+ rc = mwl8k_post_cmd(hw, &cmd->header);
+
+ if (!rc) {
+ int off;
+
+ SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr);
+ priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
+ priv->fw_rev = le32_to_cpu(cmd->fw_rev);
+ priv->hw_rev = cmd->hw_rev;
+
+ off = le32_to_cpu(cmd->wcbbase0) & 0xffff;
+ iowrite32(cpu_to_le32(priv->txq[0].txd_dma), priv->sram + off);
+
+ off = le32_to_cpu(cmd->rxwrptr) & 0xffff;
+ iowrite32(cpu_to_le32(priv->rxq[0].rxd_dma), priv->sram + off);
+
+ off = le32_to_cpu(cmd->rxrdptr) & 0xffff;
+ iowrite32(cpu_to_le32(priv->rxq[0].rxd_dma), priv->sram + off);
+
+ off = le32_to_cpu(cmd->wcbbase1) & 0xffff;
+ iowrite32(cpu_to_le32(priv->txq[1].txd_dma), priv->sram + off);
+
+ off = le32_to_cpu(cmd->wcbbase2) & 0xffff;
+ iowrite32(cpu_to_le32(priv->txq[2].txd_dma), priv->sram + off);
+
+ off = le32_to_cpu(cmd->wcbbase3) & 0xffff;
+ iowrite32(cpu_to_le32(priv->txq[3].txd_dma), priv->sram + off);
+ }
+
+ kfree(cmd);
+ return rc;
+}
+
+/*
+ * CMD_SET_HW_SPEC.
+ */
+struct mwl8k_cmd_set_hw_spec {
+ struct mwl8k_cmd_pkt header;
+ __u8 hw_rev;
+ __u8 host_interface;
+ __le16 num_mcaddrs;
+ __u8 perm_addr[ETH_ALEN];
+ __le16 region_code;
+ __le32 fw_rev;
+ __le32 ps_cookie;
+ __le32 caps;
+ __le32 rx_queue_ptr;
+ __le32 num_tx_queues;
+ __le32 tx_queue_ptrs[MWL8K_TX_QUEUES];
+ __le32 flags;
+ __le32 num_tx_desc_per_queue;
+ __le32 total_rxd;
+} __attribute__((packed));
+
+#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080
+
+static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)
+{
+ struct mwl8k_priv *priv = hw->priv;
+ struct mwl8k_cmd_set_hw_spec *cmd;
+ int rc;
+ int i;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC);
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
+
+ cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);
+ cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma);
+ cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES);
+ for (i = 0; i < MWL8K_TX_QUEUES; i++)
+ cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);
+ cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT);
+ cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);
+ cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS);
+
+ rc = mwl8k_post_cmd(hw, &cmd->header);
+ kfree(cmd);
+
+ return rc;
+}
+
+/*
* CMD_MAC_MULTICAST_ADR.
*/
struct mwl8k_cmd_mac_multicast_adr {
@@ -1548,19 +1817,23 @@ struct mwl8k_cmd_mac_multicast_adr {
__u8 addr[0][ETH_ALEN];
};
-#define MWL8K_ENABLE_RX_MULTICAST 0x000F
+#define MWL8K_ENABLE_RX_DIRECTED 0x0001
+#define MWL8K_ENABLE_RX_MULTICAST 0x0002
+#define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004
+#define MWL8K_ENABLE_RX_BROADCAST 0x0008
static struct mwl8k_cmd_pkt *
-__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw,
+__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
int mc_count, struct dev_addr_list *mclist)
{
struct mwl8k_priv *priv = hw->priv;
struct mwl8k_cmd_mac_multicast_adr *cmd;
int size;
- int i;
- if (mc_count > priv->num_mcaddrs)
- mc_count = priv->num_mcaddrs;
+ if (allmulti || mc_count > priv->num_mcaddrs) {
+ allmulti = 1;
+ mc_count = 0;
+ }
size = sizeof(*cmd) + mc_count * ETH_ALEN;
@@ -1570,16 +1843,24 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw,
cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR);
cmd->header.length = cpu_to_le16(size);
- cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
- cmd->numaddr = cpu_to_le16(mc_count);
-
- for (i = 0; i < mc_count && mclist; i++) {
- if (mclist->da_addrlen != ETH_ALEN) {
- kfree(cmd);
- return NULL;
+ cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED |
+ MWL8K_ENABLE_RX_BROADCAST);
+
+ if (allmulti) {
+ cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST);
+ } else if (mc_count) {
+ int i;
+
+ cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST);
+ cmd->numaddr = cpu_to_le16(mc_count);
+ for (i = 0; i < mc_count && mclist; i++) {
+ if (mclist->da_addrlen != ETH_ALEN) {
+ kfree(cmd);
+ return NULL;
+ }
+ memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN);
+ mclist = mclist->next;
}
- memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN);
- mclist = mclist->next;
}
return &cmd->header;
@@ -1590,7 +1871,6 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw,
*/
struct mwl8k_cmd_802_11_get_stat {
struct mwl8k_cmd_pkt header;
- __le16 action;
__le32 stats[64];
} __attribute__((packed));
@@ -1611,7 +1891,6 @@ static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw,
cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT);
cmd->header.length = cpu_to_le16(sizeof(*cmd));
- cmd->action = cpu_to_le16(MWL8K_CMD_GET);
rc = mwl8k_post_cmd(hw, &cmd->header);
if (!rc) {
@@ -1727,6 +2006,39 @@ static int mwl8k_cmd_802_11_rf_tx_power(struct ieee80211_hw *hw, int dBm)
}
/*
+ * CMD_RF_ANTENNA.
+ */
+struct mwl8k_cmd_rf_antenna {
+ struct mwl8k_cmd_pkt header;
+ __le16 antenna;
+ __le16 mode;
+} __attribute__((packed));
+
+#define MWL8K_RF_ANTENNA_RX 1
+#define MWL8K_RF_ANTENNA_TX 2
+
+static int
+mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
+{
+ struct mwl8k_cmd_rf_antenna *cmd;
+ int rc;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA);
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
+ cmd->antenna = cpu_to_le16(antenna);
+ cmd->mode = cpu_to_le16(mask);
+
+ rc = mwl8k_post_cmd(hw, &cmd->header);
+ kfree(cmd);
+
+ return rc;
+}
+
+/*
* CMD_SET_PRE_SCAN.
*/
struct mwl8k_cmd_set_pre_scan {
@@ -1904,6 +2216,46 @@ static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable)
}
/*
+ * CMD_SET_MAC_ADDR.
+ */
+struct mwl8k_cmd_set_mac_addr {
+ struct mwl8k_cmd_pkt header;
+ union {
+ struct {
+ __le16 mac_type;
+ __u8 mac_addr[ETH_ALEN];
+ } mbss;
+ __u8 mac_addr[ETH_ALEN];
+ };
+} __attribute__((packed));
+
+static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac)
+{
+ struct mwl8k_priv *priv = hw->priv;
+ struct mwl8k_cmd_set_mac_addr *cmd;
+ int rc;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
+ if (priv->ap_fw) {
+ cmd->mbss.mac_type = 0;
+ memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
+ } else {
+ memcpy(cmd->mac_addr, mac, ETH_ALEN);
+ }
+
+ rc = mwl8k_post_cmd(hw, &cmd->header);
+ kfree(cmd);
+
+ return rc;
+}
+
+
+/*
* CMD_SET_RATEADAPT_MODE.
*/
struct mwl8k_cmd_set_rate_adapt_mode {
@@ -2005,17 +2357,34 @@ struct mwl8k_cmd_set_edca_params {
/* TX opportunity in units of 32 us */
__le16 txop;
- /* Log exponent of max contention period: 0...15*/
- __u8 log_cw_max;
+ union {
+ struct {
+ /* Log exponent of max contention period: 0...15 */
+ __le32 log_cw_max;
+
+ /* Log exponent of min contention period: 0...15 */
+ __le32 log_cw_min;
+
+ /* Adaptive interframe spacing in units of 32us */
+ __u8 aifs;
+
+ /* TX queue to configure */
+ __u8 txq;
+ } ap;
+ struct {
+ /* Log exponent of max contention period: 0...15 */
+ __u8 log_cw_max;
- /* Log exponent of min contention period: 0...15 */
- __u8 log_cw_min;
+ /* Log exponent of min contention period: 0...15 */
+ __u8 log_cw_min;
- /* Adaptive interframe spacing in units of 32us */
- __u8 aifs;
+ /* Adaptive interframe spacing in units of 32us */
+ __u8 aifs;
- /* TX queue to configure */
- __u8 txq;
+ /* TX queue to configure */
+ __u8 txq;
+ } sta;
+ };
} __attribute__((packed));
#define MWL8K_SET_EDCA_CW 0x01
@@ -2031,6 +2400,7 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
__u16 cw_min, __u16 cw_max,
__u8 aifs, __u16 txop)
{
+ struct mwl8k_priv *priv = hw->priv;
struct mwl8k_cmd_set_edca_params *cmd;
int rc;
@@ -2038,14 +2408,27 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
if (cmd == NULL)
return -ENOMEM;
+ /*
+ * Queues 0 (BE) and 1 (BK) are swapped in hardware for
+ * this call.
+ */
+ qnum ^= !(qnum >> 1);
+
cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS);
cmd->header.length = cpu_to_le16(sizeof(*cmd));
cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL);
cmd->txop = cpu_to_le16(txop);
- cmd->log_cw_max = (u8)ilog2(cw_max + 1);
- cmd->log_cw_min = (u8)ilog2(cw_min + 1);
- cmd->aifs = aifs;
- cmd->txq = qnum;
+ if (priv->ap_fw) {
+ cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1));
+ cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1));
+ cmd->ap.aifs = aifs;
+ cmd->ap.txq = qnum;
+ } else {
+ cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1);
+ cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1);
+ cmd->sta.aifs = aifs;
+ cmd->sta.txq = qnum;
+ }
rc = mwl8k_post_cmd(hw, &cmd->header);
kfree(cmd);
@@ -2093,8 +2476,8 @@ static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame,
/* XXX TBD Might just have to abort and return an error */
if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
printk(KERN_ERR "%s(): WARNING: Incomplete beacon "
- "sent to firmware. Sz=%u MAX=%u\n", __func__,
- payload_len, MWL8K_FJ_BEACON_MAXLEN);
+ "sent to firmware. Sz=%u MAX=%u\n", __func__,
+ payload_len, MWL8K_FJ_BEACON_MAXLEN);
if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
payload_len = MWL8K_FJ_BEACON_MAXLEN;
@@ -2341,9 +2724,10 @@ static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw,
cmd->rate_type = cpu_to_le32(rate_type);
if (rate_table != NULL) {
- /* Copy over each field manually so
- * that bitflipping can be done
- */
+ /*
+ * Copy over each field manually so that endian
+ * conversion can be done.
+ */
cmd->rate_table.allow_rate_drop =
cpu_to_le32(rate_table->allow_rate_drop);
cmd->rate_table.num_rates =
@@ -2399,7 +2783,7 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id)
if (status & MWL8K_A2H_INT_QUEUE_EMPTY) {
if (!mutex_is_locked(&priv->fw_mutex) &&
- priv->radio_on && mwl8k_txq_busy(priv))
+ priv->radio_on && priv->pending_tx_pkts)
mwl8k_tx_start(priv);
}
@@ -2418,7 +2802,7 @@ static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (priv->current_channel == NULL) {
printk(KERN_DEBUG "%s: dropped TX frame since radio "
- "disabled\n", priv->name);
+ "disabled\n", wiphy_name(hw->wiphy));
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
@@ -2433,11 +2817,11 @@ static int mwl8k_start(struct ieee80211_hw *hw)
struct mwl8k_priv *priv = hw->priv;
int rc;
- rc = request_irq(priv->pdev->irq, &mwl8k_interrupt,
+ rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
IRQF_SHARED, MWL8K_NAME, hw);
if (rc) {
printk(KERN_ERR "%s: failed to register IRQ handler\n",
- priv->name);
+ wiphy_name(hw->wiphy));
return -EIO;
}
@@ -2451,12 +2835,17 @@ static int mwl8k_start(struct ieee80211_hw *hw)
if (!rc) {
rc = mwl8k_cmd_802_11_radio_enable(hw);
- if (!rc)
- rc = mwl8k_cmd_set_pre_scan(hw);
+ if (!priv->ap_fw) {
+ if (!rc)
+ rc = mwl8k_enable_sniffer(hw, 0);
- if (!rc)
- rc = mwl8k_cmd_set_post_scan(hw,
- "\x00\x00\x00\x00\x00\x00");
+ if (!rc)
+ rc = mwl8k_cmd_set_pre_scan(hw);
+
+ if (!rc)
+ rc = mwl8k_cmd_set_post_scan(hw,
+ "\x00\x00\x00\x00\x00\x00");
+ }
if (!rc)
rc = mwl8k_cmd_setrateadaptmode(hw, 0);
@@ -2464,9 +2853,6 @@ static int mwl8k_start(struct ieee80211_hw *hw)
if (!rc)
rc = mwl8k_set_wmm(hw, 0);
- if (!rc)
- rc = mwl8k_enable_sniffer(hw, 0);
-
mwl8k_fw_unlock(hw);
}
@@ -2500,9 +2886,6 @@ static void mwl8k_stop(struct ieee80211_hw *hw)
/* Stop tx reclaim tasklet */
tasklet_disable(&priv->tx_reclaim_task);
- /* Stop config thread */
- flush_workqueue(priv->config_wq);
-
/* Return all skbs to mac80211 */
for (i = 0; i < MWL8K_TX_QUEUES; i++)
mwl8k_txq_reclaim(hw, i, 1);
@@ -2526,11 +2909,24 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
if (conf->type != NL80211_IFTYPE_STATION)
return -EINVAL;
+ /*
+ * Reject interface creation if sniffer mode is active, as
+ * STA operation is mutually exclusive with hardware sniffer
+ * mode.
+ */
+ if (priv->sniffer_enabled) {
+ printk(KERN_INFO "%s: unable to create STA "
+ "interface due to sniffer mode being enabled\n",
+ wiphy_name(hw->wiphy));
+ return -EINVAL;
+ }
+
/* Clean out driver private area */
mwl8k_vif = MWL8K_VIF(conf->vif);
memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
- /* Save the mac address */
+ /* Set and save the mac address */
+ mwl8k_set_mac_addr(hw, conf->mac_addr);
memcpy(mwl8k_vif->mac_addr, conf->mac_addr, ETH_ALEN);
/* Back pointer to parent config block */
@@ -2558,6 +2954,8 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw,
if (priv->vif == NULL)
return;
+ mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
+
priv->vif = NULL;
}
@@ -2593,8 +2991,13 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
if (rc)
goto out;
- if (mwl8k_cmd_mimo_config(hw, 0x7, 0x7))
- rc = -EINVAL;
+ if (priv->ap_fw) {
+ rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x7);
+ if (!rc)
+ rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
+ } else {
+ rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7);
+ }
out:
mwl8k_fw_unlock(hw);
@@ -2681,32 +3084,108 @@ static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw,
{
struct mwl8k_cmd_pkt *cmd;
- cmd = __mwl8k_cmd_mac_multicast_adr(hw, mc_count, mclist);
+ /*
+ * Synthesize and return a command packet that programs the
+ * hardware multicast address filter. At this point we don't
+ * know whether FIF_ALLMULTI is being requested, but if it is,
+ * we'll end up throwing this packet away and creating a new
+ * one in mwl8k_configure_filter().
+ */
+ cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_count, mclist);
return (unsigned long)cmd;
}
+static int
+mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags)
+{
+ struct mwl8k_priv *priv = hw->priv;
+
+ /*
+ * Hardware sniffer mode is mutually exclusive with STA
+ * operation, so refuse to enable sniffer mode if a STA
+ * interface is active.
+ */
+ if (priv->vif != NULL) {
+ if (net_ratelimit())
+ printk(KERN_INFO "%s: not enabling sniffer "
+ "mode because STA interface is active\n",
+ wiphy_name(hw->wiphy));
+ return 0;
+ }
+
+ if (!priv->sniffer_enabled) {
+ if (mwl8k_enable_sniffer(hw, 1))
+ return 0;
+ priv->sniffer_enabled = true;
+ }
+
+ *total_flags &= FIF_PROMISC_IN_BSS | FIF_ALLMULTI |
+ FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL |
+ FIF_OTHER_BSS;
+
+ return 1;
+}
+
static void mwl8k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
u64 multicast)
{
struct mwl8k_priv *priv = hw->priv;
- struct mwl8k_cmd_pkt *multicast_adr_cmd;
+ struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast;
+
+ /*
+ * AP firmware doesn't allow fine-grained control over
+ * the receive filter.
+ */
+ if (priv->ap_fw) {
+ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
+ kfree(cmd);
+ return;
+ }
+
+ /*
+ * Enable hardware sniffer mode if FIF_CONTROL or
+ * FIF_OTHER_BSS is requested.
+ */
+ if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) &&
+ mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) {
+ kfree(cmd);
+ return;
+ }
/* Clear unsupported feature flags */
- *total_flags &= FIF_BCN_PRBRESP_PROMISC;
+ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
if (mwl8k_fw_lock(hw))
return;
+ if (priv->sniffer_enabled) {
+ mwl8k_enable_sniffer(hw, 0);
+ priv->sniffer_enabled = false;
+ }
+
if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
- if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+ if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
+ /*
+ * Disable the BSS filter.
+ */
mwl8k_cmd_set_pre_scan(hw);
- else {
+ } else {
u8 *bssid;
- bssid = "\x00\x00\x00\x00\x00\x00";
+ /*
+ * Enable the BSS filter.
+ *
+ * If there is an active STA interface, use that
+ * interface's BSSID, otherwise use a dummy one
+ * (where the OUI part needs to be nonzero for
+ * the BSSID to be accepted by POST_SCAN).
+ */
+ bssid = "\x01\x00\x00\x00\x00\x00";
if (priv->vif != NULL)
bssid = MWL8K_VIF(priv->vif)->bssid;
@@ -2714,10 +3193,20 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw,
}
}
- multicast_adr_cmd = (void *)(unsigned long)multicast;
- if (multicast_adr_cmd != NULL) {
- mwl8k_post_cmd(hw, multicast_adr_cmd);
- kfree(multicast_adr_cmd);
+ /*
+ * If FIF_ALLMULTI is being requested, throw away the command
+ * packet that ->prepare_multicast() built and replace it with
+ * a command packet that enables reception of all multicast
+ * packets.
+ */
+ if (*total_flags & FIF_ALLMULTI) {
+ kfree(cmd);
+ cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, 0, NULL);
+ }
+
+ if (cmd != NULL) {
+ mwl8k_post_cmd(hw, cmd);
+ kfree(cmd);
}
mwl8k_fw_unlock(hw);
@@ -2762,7 +3251,7 @@ static int mwl8k_get_tx_stats(struct ieee80211_hw *hw,
spin_lock_bh(&priv->tx_lock);
for (index = 0; index < MWL8K_TX_QUEUES; index++) {
txq = priv->txq + index;
- memcpy(&stats[index], &txq->tx_stats,
+ memcpy(&stats[index], &txq->stats,
sizeof(struct ieee80211_tx_queue_stats));
}
spin_unlock_bh(&priv->tx_lock);
@@ -2802,7 +3291,7 @@ static void mwl8k_tx_reclaim_handler(unsigned long data)
for (i = 0; i < MWL8K_TX_QUEUES; i++)
mwl8k_txq_reclaim(hw, i, 0);
- if (priv->tx_wait != NULL && mwl8k_txq_busy(priv) == 0) {
+ if (priv->tx_wait != NULL && !priv->pending_tx_pkts) {
complete(priv->tx_wait);
priv->tx_wait = NULL;
}
@@ -2822,6 +3311,36 @@ static void mwl8k_finalize_join_worker(struct work_struct *work)
priv->beacon_skb = NULL;
}
+enum {
+ MWL8687 = 0,
+ MWL8366,
+};
+
+static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = {
+ {
+ .part_name = "88w8687",
+ .helper_image = "mwl8k/helper_8687.fw",
+ .fw_image = "mwl8k/fmimage_8687.fw",
+ .rxd_ops = &rxd_8687_ops,
+ .modes = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .part_name = "88w8366",
+ .helper_image = "mwl8k/helper_8366.fw",
+ .fw_image = "mwl8k/fmimage_8366.fw",
+ .rxd_ops = &rxd_8366_ops,
+ .modes = 0,
+ },
+};
+
+static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
+ { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
+ { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },
+ { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
+
static int __devinit mwl8k_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -2862,17 +3381,34 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
priv = hw->priv;
priv->hw = hw;
priv->pdev = pdev;
+ priv->device_info = &mwl8k_info_tbl[id->driver_data];
+ priv->rxd_ops = priv->device_info->rxd_ops;
+ priv->sniffer_enabled = false;
priv->wmm_enabled = false;
priv->pending_tx_pkts = 0;
- strncpy(priv->name, MWL8K_NAME, sizeof(priv->name));
SET_IEEE80211_DEV(hw, &pdev->dev);
pci_set_drvdata(pdev, hw);
+ priv->sram = pci_iomap(pdev, 0, 0x10000);
+ if (priv->sram == NULL) {
+ printk(KERN_ERR "%s: Cannot map device SRAM\n",
+ wiphy_name(hw->wiphy));
+ goto err_iounmap;
+ }
+
+ /*
+ * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
+ * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
+ */
priv->regs = pci_iomap(pdev, 1, 0x10000);
if (priv->regs == NULL) {
- printk(KERN_ERR "%s: Cannot map device memory\n", priv->name);
- goto err_iounmap;
+ priv->regs = pci_iomap(pdev, 2, 0x10000);
+ if (priv->regs == NULL) {
+ printk(KERN_ERR "%s: Cannot map device registers\n",
+ wiphy_name(hw->wiphy));
+ goto err_iounmap;
+ }
}
memcpy(priv->channels, mwl8k_channels, sizeof(mwl8k_channels));
@@ -2897,7 +3433,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
hw->queues = MWL8K_TX_QUEUES;
- hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ hw->wiphy->interface_modes = priv->device_info->modes;
/* Set rssi and noise values to dBm */
hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM;
@@ -2916,11 +3452,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
mwl8k_tx_reclaim_handler, (unsigned long)hw);
tasklet_disable(&priv->tx_reclaim_task);
- /* Config workthread */
- priv->config_wq = create_singlethread_workqueue("mwl8k_config");
- if (priv->config_wq == NULL)
- goto err_iounmap;
-
/* Power management cookie */
priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
if (priv->cookie == NULL)
@@ -2934,11 +3465,12 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
mutex_init(&priv->fw_mutex);
priv->fw_mutex_owner = NULL;
priv->fw_mutex_depth = 0;
- priv->tx_wait = NULL;
priv->hostcmd_wait = NULL;
spin_lock_init(&priv->tx_lock);
+ priv->tx_wait = NULL;
+
for (i = 0; i < MWL8K_TX_QUEUES; i++) {
rc = mwl8k_txq_init(hw, i);
if (rc)
@@ -2950,11 +3482,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL);
iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);
- rc = request_irq(priv->pdev->irq, &mwl8k_interrupt,
+ rc = request_irq(priv->pdev->irq, mwl8k_interrupt,
IRQF_SHARED, MWL8K_NAME, hw);
if (rc) {
printk(KERN_ERR "%s: failed to register IRQ handler\n",
- priv->name);
+ wiphy_name(hw->wiphy));
goto err_free_queues;
}
@@ -2962,16 +3494,18 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
mwl8k_hw_reset(priv);
/* Ask userland hotplug daemon for the device firmware */
- rc = mwl8k_request_firmware(priv, (u32)id->driver_data);
+ rc = mwl8k_request_firmware(priv);
if (rc) {
- printk(KERN_ERR "%s: Firmware files not found\n", priv->name);
+ printk(KERN_ERR "%s: Firmware files not found\n",
+ wiphy_name(hw->wiphy));
goto err_free_irq;
}
/* Load firmware into hardware */
- rc = mwl8k_load_firmware(priv);
+ rc = mwl8k_load_firmware(hw);
if (rc) {
- printk(KERN_ERR "%s: Cannot start firmware\n", priv->name);
+ printk(KERN_ERR "%s: Cannot start firmware\n",
+ wiphy_name(hw->wiphy));
goto err_stop_firmware;
}
@@ -2986,16 +3520,31 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
/* Get config data, mac addrs etc */
- rc = mwl8k_cmd_get_hw_spec(hw);
+ if (priv->ap_fw) {
+ rc = mwl8k_cmd_get_hw_spec_ap(hw);
+ if (!rc)
+ rc = mwl8k_cmd_set_hw_spec(hw);
+ } else {
+ rc = mwl8k_cmd_get_hw_spec_sta(hw);
+ }
if (rc) {
- printk(KERN_ERR "%s: Cannot initialise firmware\n", priv->name);
+ printk(KERN_ERR "%s: Cannot initialise firmware\n",
+ wiphy_name(hw->wiphy));
goto err_stop_firmware;
}
/* Turn radio off */
rc = mwl8k_cmd_802_11_radio_disable(hw);
if (rc) {
- printk(KERN_ERR "%s: Cannot disable\n", priv->name);
+ printk(KERN_ERR "%s: Cannot disable\n", wiphy_name(hw->wiphy));
+ goto err_stop_firmware;
+ }
+
+ /* Clear MAC address */
+ rc = mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
+ if (rc) {
+ printk(KERN_ERR "%s: Cannot clear MAC address\n",
+ wiphy_name(hw->wiphy));
goto err_stop_firmware;
}
@@ -3005,13 +3554,15 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
rc = ieee80211_register_hw(hw);
if (rc) {
- printk(KERN_ERR "%s: Cannot register device\n", priv->name);
+ printk(KERN_ERR "%s: Cannot register device\n",
+ wiphy_name(hw->wiphy));
goto err_stop_firmware;
}
- printk(KERN_INFO "%s: 88w%u v%d, %pM, firmware version %u.%u.%u.%u\n",
- wiphy_name(hw->wiphy), priv->part_num, priv->hw_rev,
- hw->wiphy->perm_addr,
+ printk(KERN_INFO "%s: %s v%d, %pM, %s firmware %u.%u.%u.%u\n",
+ wiphy_name(hw->wiphy), priv->device_info->part_name,
+ priv->hw_rev, hw->wiphy->perm_addr,
+ priv->ap_fw ? "AP" : "STA",
(priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff,
(priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff);
@@ -3038,8 +3589,8 @@ err_iounmap:
if (priv->regs != NULL)
pci_iounmap(pdev, priv->regs);
- if (priv->config_wq != NULL)
- destroy_workqueue(priv->config_wq);
+ if (priv->sram != NULL)
+ pci_iounmap(pdev, priv->sram);
pci_set_drvdata(pdev, NULL);
ieee80211_free_hw(hw);
@@ -3073,9 +3624,6 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
/* Remove tx reclaim tasklet */
tasklet_kill(&priv->tx_reclaim_task);
- /* Stop config thread */
- destroy_workqueue(priv->config_wq);
-
/* Stop hardware */
mwl8k_hw_reset(priv);
@@ -3088,10 +3636,10 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
mwl8k_rxq_deinit(hw, 0);
- pci_free_consistent(priv->pdev, 4,
- priv->cookie, priv->cookie_dma);
+ pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma);
pci_iounmap(pdev, priv->regs);
+ pci_iounmap(pdev, priv->sram);
pci_set_drvdata(pdev, NULL);
ieee80211_free_hw(hw);
pci_release_regions(pdev);
@@ -3100,7 +3648,7 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
static struct pci_driver mwl8k_driver = {
.name = MWL8K_NAME,
- .id_table = mwl8k_table,
+ .id_table = mwl8k_pci_id_table,
.probe = mwl8k_probe,
.remove = __devexit_p(mwl8k_remove),
.shutdown = __devexit_p(mwl8k_shutdown),
@@ -3118,3 +3666,8 @@ static void __exit mwl8k_exit(void)
module_init(mwl8k_init);
module_exit(mwl8k_exit);
+
+MODULE_DESCRIPTION(MWL8K_DESC);
+MODULE_VERSION(MWL8K_VERSION);
+MODULE_AUTHOR("Lennert Buytenhek <buytenh@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig
index 83b635fd778..e2a2c18920a 100644
--- a/drivers/net/wireless/orinoco/Kconfig
+++ b/drivers/net/wireless/orinoco/Kconfig
@@ -1,8 +1,10 @@
config HERMES
tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
- depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211
- depends on CFG80211
+ depends on (PPC_PMAC || PCI || PCMCIA)
+ depends on CFG80211 && CFG80211_WEXT
select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
select FW_LOADER
select CRYPTO
select CRYPTO_MICHAEL_MIC
diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c
index 1257250a1e2..cfa72962052 100644
--- a/drivers/net/wireless/orinoco/fw.c
+++ b/drivers/net/wireless/orinoco/fw.c
@@ -28,6 +28,12 @@ static const struct fw_info orinoco_fw[] = {
{ NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
{ "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 }
};
+MODULE_FIRMWARE("agere_sta_fw.bin");
+MODULE_FIRMWARE("agere_ap_fw.bin");
+MODULE_FIRMWARE("prism_sta_fw.bin");
+MODULE_FIRMWARE("prism_ap_fw.bin");
+MODULE_FIRMWARE("symbol_sp24t_prim_fw");
+MODULE_FIRMWARE("symbol_sp24t_sec_fw");
/* Structure used to access fields in FW
* Make sure LE decoding macros are used
diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c
index a3eefe109df..84200da900b 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.c
+++ b/drivers/net/wireless/orinoco/hermes_dld.c
@@ -550,7 +550,7 @@ static const struct { \
#define DEFAULT_PDR(pid) default_pdr_data_##pid
-/* HWIF Compatiblity */
+/* HWIF Compatibility */
DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
/* PPPPSign */
@@ -656,7 +656,7 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
record_id + 1, pdi);
}
break;
- case 0x5: /* HWIF Compatiblity */
+ case 0x5: /* HWIF Compatibility */
default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
break;
case 0x108: /* PPPPSign */
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
index 359652d35e6..404830f47ab 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/orinoco/hw.c
@@ -60,8 +60,15 @@ static inline fwtype_t determine_firmware_type(struct comp_id *nic_id)
/* Set priv->firmware type, determine firmware properties
* This function can be called before we have registerred with netdev,
* so all errors go out with dev_* rather than printk
+ *
+ * If non-NULL stores a firmware description in fw_name.
+ * If non-NULL stores a HW version in hw_ver
+ *
+ * These are output via generic cfg80211 ethtool support.
*/
-int determine_fw_capabilities(struct orinoco_private *priv)
+int determine_fw_capabilities(struct orinoco_private *priv,
+ char *fw_name, size_t fw_name_len,
+ u32 *hw_ver)
{
struct device *dev = priv->dev;
hermes_t *hw = &priv->hw;
@@ -85,6 +92,12 @@ int determine_fw_capabilities(struct orinoco_private *priv)
dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n",
nic_id.id, nic_id.variant, nic_id.major, nic_id.minor);
+ if (hw_ver)
+ *hw_ver = (((nic_id.id & 0xff) << 24) |
+ ((nic_id.variant & 0xff) << 16) |
+ ((nic_id.major & 0xff) << 8) |
+ (nic_id.minor & 0xff));
+
priv->firmware_type = determine_firmware_type(&nic_id);
/* Get the firmware version */
@@ -135,8 +148,9 @@ int determine_fw_capabilities(struct orinoco_private *priv)
case FIRMWARE_TYPE_AGERE:
/* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout,
ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */
- snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
- "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor);
+ if (fw_name)
+ snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d",
+ sta_id.major, sta_id.minor);
firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor;
@@ -185,8 +199,8 @@ int determine_fw_capabilities(struct orinoco_private *priv)
tmp[SYMBOL_MAX_VER_LEN] = '\0';
}
- snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
- "Symbol %s", tmp);
+ if (fw_name)
+ snprintf(fw_name, fw_name_len, "Symbol %s", tmp);
priv->has_ibss = (firmver >= 0x20000);
priv->has_wep = (firmver >= 0x15012);
@@ -224,9 +238,9 @@ int determine_fw_capabilities(struct orinoco_private *priv)
* different and less well tested */
/* D-Link MAC : 00:40:05:* */
/* Addtron MAC : 00:90:D1:* */
- snprintf(priv->fw_name, sizeof(priv->fw_name) - 1,
- "Intersil %d.%d.%d", sta_id.major, sta_id.minor,
- sta_id.variant);
+ if (fw_name)
+ snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d",
+ sta_id.major, sta_id.minor, sta_id.variant);
firmver = ((unsigned long)sta_id.major << 16) |
((unsigned long)sta_id.minor << 8) | sta_id.variant;
@@ -245,7 +259,8 @@ int determine_fw_capabilities(struct orinoco_private *priv)
}
break;
}
- dev_info(dev, "Firmware determined as %s\n", priv->fw_name);
+ if (fw_name)
+ dev_info(dev, "Firmware determined as %s\n", fw_name);
return 0;
}
diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h
index 8df6e8752be..e2f7fdc4d45 100644
--- a/drivers/net/wireless/orinoco/hw.h
+++ b/drivers/net/wireless/orinoco/hw.h
@@ -24,7 +24,8 @@
struct orinoco_private;
struct dev_addr_list;
-int determine_fw_capabilities(struct orinoco_private *priv);
+int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name,
+ size_t fw_name_len, u32 *hw_ver);
int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr);
int orinoco_hw_allocate_fid(struct orinoco_private *priv);
int orinoco_get_bitratemode(int bitrate, int automatic);
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 7a32bcb0c03..753a1804eee 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -83,7 +83,6 @@
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
#include <linux/suspend.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
@@ -162,8 +161,6 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
| HERMES_EV_WTERR | HERMES_EV_INFO \
| HERMES_EV_INFDROP)
-static const struct ethtool_ops orinoco_ethtool_ops;
-
/********************************************************************/
/* Data types */
/********************************************************************/
@@ -1994,7 +1991,9 @@ int orinoco_init(struct orinoco_private *priv)
goto out;
}
- err = determine_fw_capabilities(priv);
+ err = determine_fw_capabilities(priv, wiphy->fw_version,
+ sizeof(wiphy->fw_version),
+ &wiphy->hw_version);
if (err != 0) {
dev_err(dev, "Incompatible firmware, aborting\n");
goto out;
@@ -2010,7 +2009,9 @@ int orinoco_init(struct orinoco_private *priv)
priv->do_fw_download = 0;
/* Check firmware version again */
- err = determine_fw_capabilities(priv);
+ err = determine_fw_capabilities(priv, wiphy->fw_version,
+ sizeof(wiphy->fw_version),
+ &wiphy->hw_version);
if (err != 0) {
dev_err(dev, "Incompatible firmware, aborting\n");
goto out;
@@ -2212,7 +2213,6 @@ int orinoco_if_add(struct orinoco_private *priv,
dev->ieee80211_ptr = wdev;
dev->netdev_ops = &orinoco_netdev_ops;
dev->watchdog_timeo = HZ; /* 1 second timeout */
- dev->ethtool_ops = &orinoco_ethtool_ops;
dev->wireless_handlers = &orinoco_handler_def;
#ifdef WIRELESS_SPY
dev->wireless_data = &priv->wireless_data;
@@ -2225,6 +2225,7 @@ int orinoco_if_add(struct orinoco_private *priv,
netif_carrier_off(dev);
memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN);
+ memcpy(dev->perm_addr, wiphy->perm_addr, ETH_ALEN);
dev->base_addr = base_addr;
dev->irq = irq;
@@ -2348,27 +2349,6 @@ void orinoco_down(struct orinoco_private *priv)
}
EXPORT_SYMBOL(orinoco_down);
-static void orinoco_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- struct orinoco_private *priv = ndev_priv(dev);
-
- strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
- strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);
- strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1);
- if (dev->dev.parent)
- strncpy(info->bus_info, dev_name(dev->dev.parent),
- sizeof(info->bus_info) - 1);
- else
- snprintf(info->bus_info, sizeof(info->bus_info) - 1,
- "PCMCIA %p", priv->hw.iobase);
-}
-
-static const struct ethtool_ops orinoco_ethtool_ops = {
- .get_drvinfo = orinoco_get_drvinfo,
- .get_link = ethtool_op_get_link,
-};
-
/********************************************************************/
/* Module initialization */
/********************************************************************/
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index 9ac6f1dda4b..665ef56f838 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -93,7 +93,6 @@ struct orinoco_private {
/* Capabilities of the hardware/firmware */
fwtype_t firmware_type;
- char fw_name[32];
int ibss_port;
int nicbuf_size;
u16 channel_mask;
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c
index 38c1c9d2abb..f27bb8367c9 100644
--- a/drivers/net/wireless/orinoco/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco/orinoco_cs.c
@@ -109,7 +109,7 @@ orinoco_cs_probe(struct pcmcia_device *link)
struct orinoco_private *priv;
struct orinoco_pccard *card;
- priv = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ priv = alloc_orinocodev(sizeof(*card), &link->dev,
orinoco_cs_hard_reset, NULL);
if (!priv)
return -ENOMEM;
@@ -120,10 +120,8 @@ orinoco_cs_probe(struct pcmcia_device *link)
link->priv = priv;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = orinoco_interrupt;
- link->irq.Instance = priv;
/* General socket configuration defaults can go here. In this
* client, we assume very little, and rely on the CIS for
@@ -160,12 +158,6 @@ static void orinoco_cs_detach(struct pcmcia_device *link)
* device available to the system.
*/
-#define CS_CHECK(fn, ret) do { \
- last_fn = (fn); \
- if ((last_ret = (ret)) != 0) \
- goto cs_failed; \
-} while (0)
-
static int orinoco_cs_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -240,7 +232,7 @@ orinoco_cs_config(struct pcmcia_device *link)
struct orinoco_private *priv = link->priv;
struct orinoco_pccard *card = priv->card;
hermes_t *hw = &priv->hw;
- int last_fn, last_ret;
+ int ret;
void __iomem *mem;
/*
@@ -257,13 +249,12 @@ orinoco_cs_config(struct pcmcia_device *link)
* and most client drivers will only use the CIS to fill in
* implementation-defined details.
*/
- last_ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL);
- if (last_ret) {
+ ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL);
+ if (ret) {
if (!ignore_cis_vcc)
printk(KERN_ERR PFX "GetNextTuple(): No matching "
"CIS configuration. Maybe you need the "
"ignore_cis_vcc=1 parameter.\n");
- cs_error(link, RequestIO, last_ret);
goto failed;
}
@@ -272,14 +263,16 @@ orinoco_cs_config(struct pcmcia_device *link)
* a handler to the interrupt, unless the 'Handler' member of
* the irq structure is initialized.
*/
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
/* We initialize the hermes structure before completing PCMCIA
* configuration just in case the interrupt handler gets
* called. */
mem = ioport_map(link->io.BasePort1, link->io.NumPorts1);
if (!mem)
- goto cs_failed;
+ goto failed;
hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
@@ -288,8 +281,9 @@ orinoco_cs_config(struct pcmcia_device *link)
* the I/O windows and the interrupt mapping, and putting the
* card and host interface into "Memory and IO" mode.
*/
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/* Ok, we have the configuration, prepare to register the netdev */
card->node.major = card->node.minor = 0;
@@ -315,9 +309,6 @@ orinoco_cs_config(struct pcmcia_device *link)
* net_device has been registered */
return 0;
- cs_failed:
- cs_error(link, last_fn, last_ret);
-
failed:
orinoco_cs_release(link);
return -ENODEV;
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c
index c361310b885..59bda240fdc 100644
--- a/drivers/net/wireless/orinoco/spectrum_cs.c
+++ b/drivers/net/wireless/orinoco/spectrum_cs.c
@@ -73,9 +73,6 @@ static void spectrum_cs_release(struct pcmcia_device *link);
#define HCR_MEM16 0x10 /* memory width bit, should be preserved */
-#define CS_CHECK(fn, ret) \
- do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
/*
* Reset the card using configuration registers COR and CCSR.
* If IDLE is 1, stop the firmware, so that it can be safely rewritten.
@@ -83,7 +80,7 @@ static void spectrum_cs_release(struct pcmcia_device *link);
static int
spectrum_reset(struct pcmcia_device *link, int idle)
{
- int last_ret, last_fn;
+ int ret;
conf_reg_t reg;
u_int save_cor;
@@ -95,23 +92,26 @@ spectrum_reset(struct pcmcia_device *link, int idle)
reg.Function = 0;
reg.Action = CS_READ;
reg.Offset = CISREG_COR;
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
+ ret = pcmcia_access_configuration_register(link, &reg);
+ if (ret)
+ goto failed;
save_cor = reg.Value;
/* Soft-Reset card */
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = (save_cor | COR_SOFT_RESET);
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
+ ret = pcmcia_access_configuration_register(link, &reg);
+ if (ret)
+ goto failed;
udelay(1000);
/* Read CCSR */
reg.Action = CS_READ;
reg.Offset = CISREG_CCSR;
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
+ ret = pcmcia_access_configuration_register(link, &reg);
+ if (ret)
+ goto failed;
/*
* Start or stop the firmware. Memory width bit should be
@@ -120,21 +120,22 @@ spectrum_reset(struct pcmcia_device *link, int idle)
reg.Action = CS_WRITE;
reg.Offset = CISREG_CCSR;
reg.Value = (idle ? HCR_IDLE : HCR_RUN) | (reg.Value & HCR_MEM16);
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
+ ret = pcmcia_access_configuration_register(link, &reg);
+ if (ret)
+ goto failed;
udelay(1000);
/* Restore original COR configuration index */
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = (save_cor & ~COR_SOFT_RESET);
- CS_CHECK(AccessConfigurationRegister,
- pcmcia_access_configuration_register(link, &reg));
+ ret = pcmcia_access_configuration_register(link, &reg);
+ if (ret)
+ goto failed;
udelay(1000);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
+failed:
return -ENODEV;
}
@@ -181,7 +182,7 @@ spectrum_cs_probe(struct pcmcia_device *link)
struct orinoco_private *priv;
struct orinoco_pccard *card;
- priv = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ priv = alloc_orinocodev(sizeof(*card), &link->dev,
spectrum_cs_hard_reset,
spectrum_cs_stop_firmware);
if (!priv)
@@ -193,10 +194,8 @@ spectrum_cs_probe(struct pcmcia_device *link)
link->priv = priv;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = orinoco_interrupt;
- link->irq.Instance = priv;
/* General socket configuration defaults can go here. In this
* client, we assume very little, and rely on the CIS for
@@ -307,7 +306,7 @@ spectrum_cs_config(struct pcmcia_device *link)
struct orinoco_private *priv = link->priv;
struct orinoco_pccard *card = priv->card;
hermes_t *hw = &priv->hw;
- int last_fn, last_ret;
+ int ret;
void __iomem *mem;
/*
@@ -324,13 +323,12 @@ spectrum_cs_config(struct pcmcia_device *link)
* and most client drivers will only use the CIS to fill in
* implementation-defined details.
*/
- last_ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
- if (last_ret) {
+ ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
+ if (ret) {
if (!ignore_cis_vcc)
printk(KERN_ERR PFX "GetNextTuple(): No matching "
"CIS configuration. Maybe you need the "
"ignore_cis_vcc=1 parameter.\n");
- cs_error(link, RequestIO, last_ret);
goto failed;
}
@@ -339,14 +337,16 @@ spectrum_cs_config(struct pcmcia_device *link)
* a handler to the interrupt, unless the 'Handler' member of
* the irq structure is initialized.
*/
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
/* We initialize the hermes structure before completing PCMCIA
* configuration just in case the interrupt handler gets
* called. */
mem = ioport_map(link->io.BasePort1, link->io.NumPorts1);
if (!mem)
- goto cs_failed;
+ goto failed;
hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
@@ -355,8 +355,9 @@ spectrum_cs_config(struct pcmcia_device *link)
* the I/O windows and the interrupt mapping, and putting the
* card and host interface into "Memory and IO" mode.
*/
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/* Ok, we have the configuration, prepare to register the netdev */
card->node.major = card->node.minor = 0;
@@ -386,9 +387,6 @@ spectrum_cs_config(struct pcmcia_device *link)
* net_device has been registered */
return 0;
- cs_failed:
- cs_error(link, last_fn, last_ret);
-
failed:
spectrum_cs_release(link);
return -ENODEV;
diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/p54/Kconfig
index b45d6a4ed1e..b0342a520bf 100644
--- a/drivers/net/wireless/p54/Kconfig
+++ b/drivers/net/wireless/p54/Kconfig
@@ -1,6 +1,6 @@
config P54_COMMON
tristate "Softmac Prism54 support"
- depends on MAC80211 && WLAN_80211 && EXPERIMENTAL
+ depends on MAC80211 && EXPERIMENTAL
select FW_LOADER
---help---
This is common code for isl38xx/stlc45xx based modules.
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c
index 0efe67deede..8e3818f6832 100644
--- a/drivers/net/wireless/p54/eeprom.c
+++ b/drivers/net/wireless/p54/eeprom.c
@@ -126,7 +126,7 @@ static int p54_generate_band(struct ieee80211_hw *dev,
int ret = -ENOMEM;
if ((!list->entries) || (!list->band_channel_num[band]))
- return 0;
+ return -EINVAL;
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
@@ -158,6 +158,7 @@ static int p54_generate_band(struct ieee80211_hw *dev,
(list->channels[i].data & CHAN_HAS_CURVE ? "" :
" [curve data]"),
list->channels[i].index, list->channels[i].freq);
+ continue;
}
tmp->channels[j].band = list->channels[i].band;
@@ -165,7 +166,16 @@ static int p54_generate_band(struct ieee80211_hw *dev,
j++;
}
- tmp->n_channels = list->band_channel_num[band];
+ if (j == 0) {
+ printk(KERN_ERR "%s: Disabling totally damaged %s band.\n",
+ wiphy_name(dev->wiphy), (band == IEEE80211_BAND_2GHZ) ?
+ "2 GHz" : "5 GHz");
+
+ ret = -ENODATA;
+ goto err_out;
+ }
+
+ tmp->n_channels = j;
old = priv->band_table[band];
priv->band_table[band] = tmp;
if (old) {
@@ -228,13 +238,13 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev)
struct p54_common *priv = dev->priv;
struct p54_channel_list *list;
unsigned int i, j, max_channel_num;
- int ret = -ENOMEM;
+ int ret = 0;
u16 freq;
if ((priv->iq_autocal_len != priv->curve_data->entries) ||
(priv->iq_autocal_len != priv->output_limit->entries))
- printk(KERN_ERR "%s: EEPROM is damaged... you may not be able"
- "to use all channels with this device.\n",
+ printk(KERN_ERR "%s: Unsupported or damaged EEPROM detected. "
+ "You may not be able to use all channels.\n",
wiphy_name(dev->wiphy));
max_channel_num = max_t(unsigned int, priv->output_limit->entries,
@@ -243,8 +253,10 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev)
priv->curve_data->entries);
list = kzalloc(sizeof(*list), GFP_KERNEL);
- if (!list)
+ if (!list) {
+ ret = -ENOMEM;
goto free;
+ }
list->max_entries = max_channel_num;
list->channels = kzalloc(sizeof(struct p54_channel_entry) *
@@ -282,13 +294,8 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev)
p54_compare_channels, NULL);
for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) {
- if (list->band_channel_num[i]) {
- ret = p54_generate_band(dev, list, i);
- if (ret)
- goto free;
-
+ if (p54_generate_band(dev, list, i) == 0)
j++;
- }
}
if (j == 0) {
/* no useable band available. */
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index 4d486bf9f72..18012dbfb45 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -579,7 +579,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
* For now, disable PS by default because it affects
* link stability significantly.
*/
- dev->wiphy->ps_default = false;
+ dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
mutex_init(&priv->conf_mutex);
mutex_init(&priv->eeprom_mutex);
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c
index d348c265e86..a15962a19b2 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/p54/p54pci.c
@@ -411,7 +411,7 @@ static int p54p_open(struct ieee80211_hw *dev)
int err;
init_completion(&priv->boot_comp);
- err = request_irq(priv->pdev->irq, &p54p_interrupt,
+ err = request_irq(priv->pdev->irq, p54p_interrupt,
IRQF_SHARED, "p54pci", dev);
if (err) {
dev_err(&priv->pdev->dev, "failed to register IRQ handler\n");
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index bc08464d832..f7f5c793514 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -1897,7 +1897,7 @@ prism54_get_mac(struct net_device *ndev, struct iw_request_info *info,
return 0;
}
-/* Setting policy also clears the MAC acl, even if we don't change the defaut
+/* Setting policy also clears the MAC acl, even if we don't change the default
* policy
*/
@@ -2323,7 +2323,7 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
case DOT11_OID_BEACON:
send_formatted_event(priv,
- "Received a beacon from an unkown AP",
+ "Received a beacon from an unknown AP",
mlme, 0);
break;
diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c
index 2505be56ae3..a3ba3539db0 100644
--- a/drivers/net/wireless/prism54/islpci_dev.c
+++ b/drivers/net/wireless/prism54/islpci_dev.c
@@ -41,6 +41,9 @@
#define ISL3877_IMAGE_FILE "isl3877"
#define ISL3886_IMAGE_FILE "isl3886"
#define ISL3890_IMAGE_FILE "isl3890"
+MODULE_FIRMWARE(ISL3877_IMAGE_FILE);
+MODULE_FIRMWARE(ISL3886_IMAGE_FILE);
+MODULE_FIRMWARE(ISL3890_IMAGE_FILE);
static int prism54_bring_down(islpci_private *);
static int islpci_alloc_memory(islpci_private *);
diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c
index 83d366258c8..e4f2bb7368f 100644
--- a/drivers/net/wireless/prism54/islpci_hotplug.c
+++ b/drivers/net/wireless/prism54/islpci_hotplug.c
@@ -181,7 +181,7 @@ prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id)
isl38xx_disable_interrupts(priv->device_base);
/* request for the interrupt before uploading the firmware */
- rvalue = request_irq(pdev->irq, &islpci_interrupt,
+ rvalue = request_irq(pdev->irq, islpci_interrupt,
IRQF_SHARED, ndev->name, priv);
if (rvalue) {
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 1c88c2ea59a..88e1e4e32b2 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -71,25 +71,7 @@ typedef u_char mac_addr[ETH_ALEN]; /* Hardware address */
#include "rayctl.h"
#include "ray_cs.h"
-/* All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-#ifdef RAYLINK_DEBUG
-#define PCMCIA_DEBUG RAYLINK_DEBUG
-#endif
-#ifdef PCMCIA_DEBUG
-static int ray_debug;
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-/* #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); */
-#define DEBUG(n, args...) if (pc_debug > (n)) printk(args);
-#else
-#define DEBUG(n, args...)
-#endif
/** Prototypes based on PCMCIA skeleton driver *******************************/
static int ray_config(struct pcmcia_device *link);
static void ray_release(struct pcmcia_device *link);
@@ -325,7 +307,7 @@ static int ray_probe(struct pcmcia_device *p_dev)
ray_dev_t *local;
struct net_device *dev;
- DEBUG(1, "ray_attach()\n");
+ dev_dbg(&p_dev->dev, "ray_attach()\n");
/* Allocate space for private device-specific data */
dev = alloc_etherdev(sizeof(ray_dev_t));
@@ -341,8 +323,7 @@ static int ray_probe(struct pcmcia_device *p_dev)
p_dev->io.IOAddrLines = 5;
/* Interrupt setup. For PCMCIA, driver takes what's given */
- p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
p_dev->irq.Handler = &ray_interrupt;
/* General socket configuration */
@@ -351,13 +332,12 @@ static int ray_probe(struct pcmcia_device *p_dev)
p_dev->conf.ConfigIndex = 1;
p_dev->priv = dev;
- p_dev->irq.Instance = dev;
local->finder = p_dev;
local->card_status = CARD_INSERTED;
local->authentication_state = UNAUTHENTICATED;
local->num_multi = 0;
- DEBUG(2, "ray_attach p_dev = %p, dev = %p, local = %p, intr = %p\n",
+ dev_dbg(&p_dev->dev, "ray_attach p_dev = %p, dev = %p, local = %p, intr = %p\n",
p_dev, dev, local, &ray_interrupt);
/* Raylink entries in the device structure */
@@ -370,7 +350,7 @@ static int ray_probe(struct pcmcia_device *p_dev)
#endif /* WIRELESS_SPY */
- DEBUG(2, "ray_cs ray_attach calling ether_setup.)\n");
+ dev_dbg(&p_dev->dev, "ray_cs ray_attach calling ether_setup.)\n");
netif_stop_queue(dev);
init_timer(&local->timer);
@@ -393,7 +373,7 @@ static void ray_detach(struct pcmcia_device *link)
struct net_device *dev;
ray_dev_t *local;
- DEBUG(1, "ray_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "ray_detach\n");
this_device = NULL;
dev = link->priv;
@@ -408,7 +388,7 @@ static void ray_detach(struct pcmcia_device *link)
unregister_netdev(dev);
free_netdev(dev);
}
- DEBUG(2, "ray_cs ray_detach ending\n");
+ dev_dbg(&link->dev, "ray_cs ray_detach ending\n");
} /* ray_detach */
/*=============================================================================
@@ -416,19 +396,17 @@ static void ray_detach(struct pcmcia_device *link)
is received, to configure the PCMCIA socket, and to make the
ethernet device available to the system.
=============================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
#define MAX_TUPLE_SIZE 128
static int ray_config(struct pcmcia_device *link)
{
- int last_fn = 0, last_ret = 0;
+ int ret = 0;
int i;
win_req_t req;
memreq_t mem;
struct net_device *dev = (struct net_device *)link->priv;
ray_dev_t *local = netdev_priv(dev);
- DEBUG(1, "ray_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "ray_config\n");
/* Determine card type and firmware version */
printk(KERN_INFO "ray_cs Detected: %s%s%s%s\n",
@@ -440,14 +418,17 @@ static int ray_config(struct pcmcia_device *link)
/* Now allocate an interrupt line. Note that this does not
actually assign a handler to the interrupt.
*/
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
dev->irq = link->irq.AssignedIRQ;
/* This actually configures the PCMCIA socket -- setting up
the I/O windows and the interrupt mapping.
*/
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*** Set up 32k window for shared memory (transmit and control) ************/
req.Attributes =
@@ -455,10 +436,14 @@ static int ray_config(struct pcmcia_device *link)
req.Base = 0;
req.Size = 0x8000;
req.AccessSpeed = ray_mem_speed;
- CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
+ ret = pcmcia_request_window(link, &req, &link->win);
+ if (ret)
+ goto failed;
mem.CardOffset = 0x0000;
mem.Page = 0;
- CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
+ ret = pcmcia_map_mem_page(link, link->win, &mem);
+ if (ret)
+ goto failed;
local->sram = ioremap(req.Base, req.Size);
/*** Set up 16k window for shared memory (receive buffer) ***************/
@@ -467,11 +452,14 @@ static int ray_config(struct pcmcia_device *link)
req.Base = 0;
req.Size = 0x4000;
req.AccessSpeed = ray_mem_speed;
- CS_CHECK(RequestWindow,
- pcmcia_request_window(&link, &req, &local->rmem_handle));
+ ret = pcmcia_request_window(link, &req, &local->rmem_handle);
+ if (ret)
+ goto failed;
mem.CardOffset = 0x8000;
mem.Page = 0;
- CS_CHECK(MapMemPage, pcmcia_map_mem_page(local->rmem_handle, &mem));
+ ret = pcmcia_map_mem_page(link, local->rmem_handle, &mem);
+ if (ret)
+ goto failed;
local->rmem = ioremap(req.Base, req.Size);
/*** Set up window for attribute memory ***********************************/
@@ -480,22 +468,25 @@ static int ray_config(struct pcmcia_device *link)
req.Base = 0;
req.Size = 0x1000;
req.AccessSpeed = ray_mem_speed;
- CS_CHECK(RequestWindow,
- pcmcia_request_window(&link, &req, &local->amem_handle));
+ ret = pcmcia_request_window(link, &req, &local->amem_handle);
+ if (ret)
+ goto failed;
mem.CardOffset = 0x0000;
mem.Page = 0;
- CS_CHECK(MapMemPage, pcmcia_map_mem_page(local->amem_handle, &mem));
+ ret = pcmcia_map_mem_page(link, local->amem_handle, &mem);
+ if (ret)
+ goto failed;
local->amem = ioremap(req.Base, req.Size);
- DEBUG(3, "ray_config sram=%p\n", local->sram);
- DEBUG(3, "ray_config rmem=%p\n", local->rmem);
- DEBUG(3, "ray_config amem=%p\n", local->amem);
+ dev_dbg(&link->dev, "ray_config sram=%p\n", local->sram);
+ dev_dbg(&link->dev, "ray_config rmem=%p\n", local->rmem);
+ dev_dbg(&link->dev, "ray_config amem=%p\n", local->amem);
if (ray_init(dev) < 0) {
ray_release(link);
return -ENODEV;
}
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
i = register_netdev(dev);
if (i != 0) {
printk("ray_config register_netdev() failed\n");
@@ -511,9 +502,7 @@ static int ray_config(struct pcmcia_device *link)
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
-
+failed:
ray_release(link);
return -ENODEV;
} /* ray_config */
@@ -543,9 +532,9 @@ static int ray_init(struct net_device *dev)
struct ccs __iomem *pccs;
ray_dev_t *local = netdev_priv(dev);
struct pcmcia_device *link = local->finder;
- DEBUG(1, "ray_init(0x%p)\n", dev);
+ dev_dbg(&link->dev, "ray_init(0x%p)\n", dev);
if (!(pcmcia_dev_present(link))) {
- DEBUG(0, "ray_init - device not present\n");
+ dev_dbg(&link->dev, "ray_init - device not present\n");
return -1;
}
@@ -567,13 +556,13 @@ static int ray_init(struct net_device *dev)
local->fw_ver = local->startup_res.firmware_version[0];
local->fw_bld = local->startup_res.firmware_version[1];
local->fw_var = local->startup_res.firmware_version[2];
- DEBUG(1, "ray_init firmware version %d.%d \n", local->fw_ver,
+ dev_dbg(&link->dev, "ray_init firmware version %d.%d \n", local->fw_ver,
local->fw_bld);
local->tib_length = 0x20;
if ((local->fw_ver == 5) && (local->fw_bld >= 30))
local->tib_length = local->startup_res.tib_length;
- DEBUG(2, "ray_init tib_length = 0x%02x\n", local->tib_length);
+ dev_dbg(&link->dev, "ray_init tib_length = 0x%02x\n", local->tib_length);
/* Initialize CCS's to buffer free state */
pccs = ccs_base(local);
for (i = 0; i < NUMBER_OF_CCS; i++) {
@@ -592,7 +581,7 @@ static int ray_init(struct net_device *dev)
clear_interrupt(local); /* Clear any interrupt from the card */
local->card_status = CARD_AWAITING_PARAM;
- DEBUG(2, "ray_init ending\n");
+ dev_dbg(&link->dev, "ray_init ending\n");
return 0;
} /* ray_init */
@@ -605,9 +594,9 @@ static int dl_startup_params(struct net_device *dev)
struct ccs __iomem *pccs;
struct pcmcia_device *link = local->finder;
- DEBUG(1, "dl_startup_params entered\n");
+ dev_dbg(&link->dev, "dl_startup_params entered\n");
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs dl_startup_params - device not present\n");
+ dev_dbg(&link->dev, "ray_cs dl_startup_params - device not present\n");
return -1;
}
@@ -625,7 +614,7 @@ static int dl_startup_params(struct net_device *dev)
local->dl_param_ccs = ccsindex;
pccs = ccs_base(local) + ccsindex;
writeb(CCS_DOWNLOAD_STARTUP_PARAMS, &pccs->cmd);
- DEBUG(2, "dl_startup_params start ccsindex = %d\n",
+ dev_dbg(&link->dev, "dl_startup_params start ccsindex = %d\n",
local->dl_param_ccs);
/* Interrupt the firmware to process the command */
if (interrupt_ecf(local, ccsindex)) {
@@ -641,7 +630,7 @@ static int dl_startup_params(struct net_device *dev)
local->timer.data = (long)local;
local->timer.function = &verify_dl_startup;
add_timer(&local->timer);
- DEBUG(2,
+ dev_dbg(&link->dev,
"ray_cs dl_startup_params started timer for verify_dl_startup\n");
return 0;
} /* dl_startup_params */
@@ -717,11 +706,11 @@ static void verify_dl_startup(u_long data)
struct pcmcia_device *link = local->finder;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs verify_dl_startup - device not present\n");
+ dev_dbg(&link->dev, "ray_cs verify_dl_startup - device not present\n");
return;
}
-#ifdef PCMCIA_DEBUG
- if (pc_debug > 2) {
+#if 0
+ {
int i;
printk(KERN_DEBUG
"verify_dl_startup parameters sent via ccs %d:\n",
@@ -760,7 +749,7 @@ static void start_net(u_long data)
int ccsindex;
struct pcmcia_device *link = local->finder;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs start_net - device not present\n");
+ dev_dbg(&link->dev, "ray_cs start_net - device not present\n");
return;
}
/* Fill in the CCS fields for the ECF */
@@ -771,7 +760,7 @@ static void start_net(u_long data)
writeb(0, &pccs->var.start_network.update_param);
/* Interrupt the firmware to process the command */
if (interrupt_ecf(local, ccsindex)) {
- DEBUG(1, "ray start net failed - card not ready for intr\n");
+ dev_dbg(&link->dev, "ray start net failed - card not ready for intr\n");
writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
return;
}
@@ -790,7 +779,7 @@ static void join_net(u_long data)
struct pcmcia_device *link = local->finder;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs join_net - device not present\n");
+ dev_dbg(&link->dev, "ray_cs join_net - device not present\n");
return;
}
/* Fill in the CCS fields for the ECF */
@@ -802,7 +791,7 @@ static void join_net(u_long data)
writeb(0, &pccs->var.join_network.net_initiated);
/* Interrupt the firmware to process the command */
if (interrupt_ecf(local, ccsindex)) {
- DEBUG(1, "ray join net failed - card not ready for intr\n");
+ dev_dbg(&link->dev, "ray join net failed - card not ready for intr\n");
writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
return;
}
@@ -821,7 +810,7 @@ static void ray_release(struct pcmcia_device *link)
ray_dev_t *local = netdev_priv(dev);
int i;
- DEBUG(1, "ray_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "ray_release\n");
del_timer(&local->timer);
@@ -829,15 +818,15 @@ static void ray_release(struct pcmcia_device *link)
iounmap(local->rmem);
iounmap(local->amem);
/* Do bother checking to see if these succeed or not */
- i = pcmcia_release_window(local->amem_handle);
+ i = pcmcia_release_window(link, local->amem_handle);
if (i != 0)
- DEBUG(0, "ReleaseWindow(local->amem) ret = %x\n", i);
- i = pcmcia_release_window(local->rmem_handle);
+ dev_dbg(&link->dev, "ReleaseWindow(local->amem) ret = %x\n", i);
+ i = pcmcia_release_window(link, local->rmem_handle);
if (i != 0)
- DEBUG(0, "ReleaseWindow(local->rmem) ret = %x\n", i);
+ dev_dbg(&link->dev, "ReleaseWindow(local->rmem) ret = %x\n", i);
pcmcia_disable_device(link);
- DEBUG(2, "ray_release ending\n");
+ dev_dbg(&link->dev, "ray_release ending\n");
}
static int ray_suspend(struct pcmcia_device *link)
@@ -871,9 +860,9 @@ static int ray_dev_init(struct net_device *dev)
ray_dev_t *local = netdev_priv(dev);
struct pcmcia_device *link = local->finder;
- DEBUG(1, "ray_dev_init(dev=%p)\n", dev);
+ dev_dbg(&link->dev, "ray_dev_init(dev=%p)\n", dev);
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_dev_init - device not present\n");
+ dev_dbg(&link->dev, "ray_dev_init - device not present\n");
return -1;
}
#ifdef RAY_IMMEDIATE_INIT
@@ -887,7 +876,7 @@ static int ray_dev_init(struct net_device *dev)
/* Postpone the card init so that we can still configure the card,
* for example using the Wireless Extensions. The init will happen
* in ray_open() - Jean II */
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_dev_init: postponing card init to ray_open() ; Status = %d\n",
local->card_status);
#endif /* RAY_IMMEDIATE_INIT */
@@ -896,7 +885,7 @@ static int ray_dev_init(struct net_device *dev)
memcpy(dev->dev_addr, &local->sparm.b4.a_mac_addr, ADDRLEN);
memset(dev->broadcast, 0xff, ETH_ALEN);
- DEBUG(2, "ray_dev_init ending\n");
+ dev_dbg(&link->dev, "ray_dev_init ending\n");
return 0;
}
@@ -906,9 +895,9 @@ static int ray_dev_config(struct net_device *dev, struct ifmap *map)
ray_dev_t *local = netdev_priv(dev);
struct pcmcia_device *link = local->finder;
/* Dummy routine to satisfy device structure */
- DEBUG(1, "ray_dev_config(dev=%p,ifmap=%p)\n", dev, map);
+ dev_dbg(&link->dev, "ray_dev_config(dev=%p,ifmap=%p)\n", dev, map);
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_dev_config - device not present\n");
+ dev_dbg(&link->dev, "ray_dev_config - device not present\n");
return -1;
}
@@ -924,14 +913,14 @@ static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb,
short length = skb->len;
if (!pcmcia_dev_present(link)) {
- DEBUG(2, "ray_dev_start_xmit - device not present\n");
+ dev_dbg(&link->dev, "ray_dev_start_xmit - device not present\n");
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
- DEBUG(3, "ray_dev_start_xmit(skb=%p, dev=%p)\n", skb, dev);
+ dev_dbg(&link->dev, "ray_dev_start_xmit(skb=%p, dev=%p)\n", skb, dev);
if (local->authentication_state == NEED_TO_AUTH) {
- DEBUG(0, "ray_cs Sending authentication request.\n");
+ dev_dbg(&link->dev, "ray_cs Sending authentication request.\n");
if (!build_auth_frame(local, local->auth_id, OPEN_AUTH_REQUEST)) {
local->authentication_state = AUTHENTICATED;
netif_stop_queue(dev);
@@ -971,7 +960,7 @@ static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev,
struct tx_msg __iomem *ptx; /* Address of xmit buffer in PC space */
short int addr; /* Address of xmit buffer in card space */
- DEBUG(3, "ray_hw_xmit(data=%p, len=%d, dev=%p)\n", data, len, dev);
+ pr_debug("ray_hw_xmit(data=%p, len=%d, dev=%p)\n", data, len, dev);
if (len + TX_HEADER_LENGTH > TX_BUF_SIZE) {
printk(KERN_INFO "ray_hw_xmit packet too large: %d bytes\n",
len);
@@ -979,9 +968,9 @@ static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev,
}
switch (ccsindex = get_free_tx_ccs(local)) {
case ECCSBUSY:
- DEBUG(2, "ray_hw_xmit tx_ccs table busy\n");
+ pr_debug("ray_hw_xmit tx_ccs table busy\n");
case ECCSFULL:
- DEBUG(2, "ray_hw_xmit No free tx ccs\n");
+ pr_debug("ray_hw_xmit No free tx ccs\n");
case ECARDGONE:
netif_stop_queue(dev);
return XMIT_NO_CCS;
@@ -1018,12 +1007,12 @@ static int ray_hw_xmit(unsigned char *data, int len, struct net_device *dev,
writeb(PSM_CAM, &pccs->var.tx_request.pow_sav_mode);
writeb(local->net_default_tx_rate, &pccs->var.tx_request.tx_rate);
writeb(0, &pccs->var.tx_request.antenna);
- DEBUG(3, "ray_hw_xmit default_tx_rate = 0x%x\n",
+ pr_debug("ray_hw_xmit default_tx_rate = 0x%x\n",
local->net_default_tx_rate);
/* Interrupt the firmware to process the command */
if (interrupt_ecf(local, ccsindex)) {
- DEBUG(2, "ray_hw_xmit failed - ECF not ready for intr\n");
+ pr_debug("ray_hw_xmit failed - ECF not ready for intr\n");
/* TBD very inefficient to copy packet to buffer, and then not
send it, but the alternative is to queue the messages and that
won't be done for a while. Maybe set tbusy until a CCS is free?
@@ -1040,7 +1029,7 @@ static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx,
{
__be16 proto = ((struct ethhdr *)data)->h_proto;
if (ntohs(proto) >= 1536) { /* DIX II ethernet frame */
- DEBUG(3, "ray_cs translate_frame DIX II\n");
+ pr_debug("ray_cs translate_frame DIX II\n");
/* Copy LLC header to card buffer */
memcpy_toio(&ptx->var, eth2_llc, sizeof(eth2_llc));
memcpy_toio(((void __iomem *)&ptx->var) + sizeof(eth2_llc),
@@ -1056,9 +1045,9 @@ static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx,
len - ETH_HLEN);
return (int)sizeof(struct snaphdr_t) - ETH_HLEN;
} else { /* already 802 type, and proto is length */
- DEBUG(3, "ray_cs translate_frame 802\n");
+ pr_debug("ray_cs translate_frame 802\n");
if (proto == htons(0xffff)) { /* evil netware IPX 802.3 without LLC */
- DEBUG(3, "ray_cs translate_frame evil IPX\n");
+ pr_debug("ray_cs translate_frame evil IPX\n");
memcpy_toio(&ptx->var, data + ETH_HLEN, len - ETH_HLEN);
return 0 - ETH_HLEN;
}
@@ -1603,7 +1592,7 @@ static int ray_open(struct net_device *dev)
struct pcmcia_device *link;
link = local->finder;
- DEBUG(1, "ray_open('%s')\n", dev->name);
+ dev_dbg(&link->dev, "ray_open('%s')\n", dev->name);
if (link->open == 0)
local->num_multi = 0;
@@ -1613,7 +1602,7 @@ static int ray_open(struct net_device *dev)
if (local->card_status == CARD_AWAITING_PARAM) {
int i;
- DEBUG(1, "ray_open: doing init now !\n");
+ dev_dbg(&link->dev, "ray_open: doing init now !\n");
/* Download startup parameters */
if ((i = dl_startup_params(dev)) < 0) {
@@ -1629,7 +1618,7 @@ static int ray_open(struct net_device *dev)
else
netif_start_queue(dev);
- DEBUG(2, "ray_open ending\n");
+ dev_dbg(&link->dev, "ray_open ending\n");
return 0;
} /* end ray_open */
@@ -1640,7 +1629,7 @@ static int ray_dev_close(struct net_device *dev)
struct pcmcia_device *link;
link = local->finder;
- DEBUG(1, "ray_dev_close('%s')\n", dev->name);
+ dev_dbg(&link->dev, "ray_dev_close('%s')\n", dev->name);
link->open--;
netif_stop_queue(dev);
@@ -1656,7 +1645,7 @@ static int ray_dev_close(struct net_device *dev)
/*===========================================================================*/
static void ray_reset(struct net_device *dev)
{
- DEBUG(1, "ray_reset entered\n");
+ pr_debug("ray_reset entered\n");
return;
}
@@ -1669,17 +1658,17 @@ static int interrupt_ecf(ray_dev_t *local, int ccs)
struct pcmcia_device *link = local->finder;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs interrupt_ecf - device not present\n");
+ dev_dbg(&link->dev, "ray_cs interrupt_ecf - device not present\n");
return -1;
}
- DEBUG(2, "interrupt_ecf(local=%p, ccs = 0x%x\n", local, ccs);
+ dev_dbg(&link->dev, "interrupt_ecf(local=%p, ccs = 0x%x\n", local, ccs);
while (i &&
(readb(local->amem + CIS_OFFSET + ECF_INTR_OFFSET) &
ECF_INTR_SET))
i--;
if (i == 0) {
- DEBUG(2, "ray_cs interrupt_ecf card not ready for interrupt\n");
+ dev_dbg(&link->dev, "ray_cs interrupt_ecf card not ready for interrupt\n");
return -1;
}
/* Fill the mailbox, then kick the card */
@@ -1698,12 +1687,12 @@ static int get_free_tx_ccs(ray_dev_t *local)
struct pcmcia_device *link = local->finder;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs get_free_tx_ccs - device not present\n");
+ dev_dbg(&link->dev, "ray_cs get_free_tx_ccs - device not present\n");
return ECARDGONE;
}
if (test_and_set_bit(0, &local->tx_ccs_lock)) {
- DEBUG(1, "ray_cs tx_ccs_lock busy\n");
+ dev_dbg(&link->dev, "ray_cs tx_ccs_lock busy\n");
return ECCSBUSY;
}
@@ -1716,7 +1705,7 @@ static int get_free_tx_ccs(ray_dev_t *local)
}
}
local->tx_ccs_lock = 0;
- DEBUG(2, "ray_cs ERROR no free tx CCS for raylink card\n");
+ dev_dbg(&link->dev, "ray_cs ERROR no free tx CCS for raylink card\n");
return ECCSFULL;
} /* get_free_tx_ccs */
@@ -1730,11 +1719,11 @@ static int get_free_ccs(ray_dev_t *local)
struct pcmcia_device *link = local->finder;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs get_free_ccs - device not present\n");
+ dev_dbg(&link->dev, "ray_cs get_free_ccs - device not present\n");
return ECARDGONE;
}
if (test_and_set_bit(0, &local->ccs_lock)) {
- DEBUG(1, "ray_cs ccs_lock busy\n");
+ dev_dbg(&link->dev, "ray_cs ccs_lock busy\n");
return ECCSBUSY;
}
@@ -1747,7 +1736,7 @@ static int get_free_ccs(ray_dev_t *local)
}
}
local->ccs_lock = 0;
- DEBUG(1, "ray_cs ERROR no free CCS for raylink card\n");
+ dev_dbg(&link->dev, "ray_cs ERROR no free CCS for raylink card\n");
return ECCSFULL;
} /* get_free_ccs */
@@ -1823,7 +1812,7 @@ static struct net_device_stats *ray_get_stats(struct net_device *dev)
struct pcmcia_device *link = local->finder;
struct status __iomem *p = local->sram + STATUS_BASE;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs net_device_stats - device not present\n");
+ dev_dbg(&link->dev, "ray_cs net_device_stats - device not present\n");
return &local->stats;
}
if (readb(&p->mrx_overflow_for_host)) {
@@ -1856,12 +1845,12 @@ static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value,
struct ccs __iomem *pccs;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_update_parm - device not present\n");
+ dev_dbg(&link->dev, "ray_update_parm - device not present\n");
return;
}
if ((ccsindex = get_free_ccs(local)) < 0) {
- DEBUG(0, "ray_update_parm - No free ccs\n");
+ dev_dbg(&link->dev, "ray_update_parm - No free ccs\n");
return;
}
pccs = ccs_base(local) + ccsindex;
@@ -1874,7 +1863,7 @@ static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value,
}
/* Interrupt the firmware to process the command */
if (interrupt_ecf(local, ccsindex)) {
- DEBUG(0, "ray_cs associate failed - ECF not ready for intr\n");
+ dev_dbg(&link->dev, "ray_cs associate failed - ECF not ready for intr\n");
writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
}
}
@@ -1891,12 +1880,12 @@ static void ray_update_multi_list(struct net_device *dev, int all)
void __iomem *p = local->sram + HOST_TO_ECF_BASE;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_update_multi_list - device not present\n");
+ dev_dbg(&link->dev, "ray_update_multi_list - device not present\n");
return;
} else
- DEBUG(2, "ray_update_multi_list(%p)\n", dev);
+ dev_dbg(&link->dev, "ray_update_multi_list(%p)\n", dev);
if ((ccsindex = get_free_ccs(local)) < 0) {
- DEBUG(1, "ray_update_multi - No free ccs\n");
+ dev_dbg(&link->dev, "ray_update_multi - No free ccs\n");
return;
}
pccs = ccs_base(local) + ccsindex;
@@ -1910,7 +1899,7 @@ static void ray_update_multi_list(struct net_device *dev, int all)
for (dmip = &dev->mc_list; (dmi = *dmip) != NULL;
dmip = &dmi->next) {
memcpy_toio(p, dmi->dmi_addr, ETH_ALEN);
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_update_multi add addr %02x%02x%02x%02x%02x%02x\n",
dmi->dmi_addr[0], dmi->dmi_addr[1],
dmi->dmi_addr[2], dmi->dmi_addr[3],
@@ -1921,12 +1910,12 @@ static void ray_update_multi_list(struct net_device *dev, int all)
if (i > 256 / ADDRLEN)
i = 256 / ADDRLEN;
writeb((UCHAR) i, &pccs->var);
- DEBUG(1, "ray_cs update_multi %d addresses in list\n", i);
+ dev_dbg(&link->dev, "ray_cs update_multi %d addresses in list\n", i);
/* Interrupt the firmware to process the command */
local->num_multi = i;
}
if (interrupt_ecf(local, ccsindex)) {
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs update_multi failed - ECF not ready for intr\n");
writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
}
@@ -1938,11 +1927,11 @@ static void set_multicast_list(struct net_device *dev)
ray_dev_t *local = netdev_priv(dev);
UCHAR promisc;
- DEBUG(2, "ray_cs set_multicast_list(%p)\n", dev);
+ pr_debug("ray_cs set_multicast_list(%p)\n", dev);
if (dev->flags & IFF_PROMISC) {
if (local->sparm.b5.a_promiscuous_mode == 0) {
- DEBUG(1, "ray_cs set_multicast_list promisc on\n");
+ pr_debug("ray_cs set_multicast_list promisc on\n");
local->sparm.b5.a_promiscuous_mode = 1;
promisc = 1;
ray_update_parm(dev, OBJID_promiscuous_mode,
@@ -1950,7 +1939,7 @@ static void set_multicast_list(struct net_device *dev)
}
} else {
if (local->sparm.b5.a_promiscuous_mode == 1) {
- DEBUG(1, "ray_cs set_multicast_list promisc off\n");
+ pr_debug("ray_cs set_multicast_list promisc off\n");
local->sparm.b5.a_promiscuous_mode = 0;
promisc = 0;
ray_update_parm(dev, OBJID_promiscuous_mode,
@@ -1984,19 +1973,19 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
if (dev == NULL) /* Note that we want interrupts with dev->start == 0 */
return IRQ_NONE;
- DEBUG(4, "ray_cs: interrupt for *dev=%p\n", dev);
+ pr_debug("ray_cs: interrupt for *dev=%p\n", dev);
local = netdev_priv(dev);
link = (struct pcmcia_device *)local->finder;
if (!pcmcia_dev_present(link)) {
- DEBUG(2,
- "ray_cs interrupt from device not present or suspended.\n");
+ pr_debug(
+ "ray_cs interrupt from device not present or suspended.\n");
return IRQ_NONE;
}
rcsindex = readb(&((struct scb __iomem *)(local->sram))->rcs_index);
if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) {
- DEBUG(1, "ray_cs interrupt bad rcsindex = 0x%x\n", rcsindex);
+ dev_dbg(&link->dev, "ray_cs interrupt bad rcsindex = 0x%x\n", rcsindex);
clear_interrupt(local);
return IRQ_HANDLED;
}
@@ -2008,33 +1997,33 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
case CCS_DOWNLOAD_STARTUP_PARAMS: /* Happens in firmware someday */
del_timer(&local->timer);
if (status == CCS_COMMAND_COMPLETE) {
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs interrupt download_startup_parameters OK\n");
} else {
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs interrupt download_startup_parameters fail\n");
}
break;
case CCS_UPDATE_PARAMS:
- DEBUG(1, "ray_cs interrupt update params done\n");
+ dev_dbg(&link->dev, "ray_cs interrupt update params done\n");
if (status != CCS_COMMAND_COMPLETE) {
tmp =
readb(&pccs->var.update_param.
failure_cause);
- DEBUG(0,
+ dev_dbg(&link->dev,
"ray_cs interrupt update params failed - reason %d\n",
tmp);
}
break;
case CCS_REPORT_PARAMS:
- DEBUG(1, "ray_cs interrupt report params done\n");
+ dev_dbg(&link->dev, "ray_cs interrupt report params done\n");
break;
case CCS_UPDATE_MULTICAST_LIST: /* Note that this CCS isn't returned */
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs interrupt CCS Update Multicast List done\n");
break;
case CCS_UPDATE_POWER_SAVINGS_MODE:
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs interrupt update power save mode done\n");
break;
case CCS_START_NETWORK:
@@ -2043,11 +2032,11 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
if (readb
(&pccs->var.start_network.net_initiated) ==
1) {
- DEBUG(0,
+ dev_dbg(&link->dev,
"ray_cs interrupt network \"%s\" started\n",
local->sparm.b4.a_current_ess_id);
} else {
- DEBUG(0,
+ dev_dbg(&link->dev,
"ray_cs interrupt network \"%s\" joined\n",
local->sparm.b4.a_current_ess_id);
}
@@ -2075,12 +2064,12 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
local->timer.expires = jiffies + HZ * 5;
local->timer.data = (long)local;
if (status == CCS_START_NETWORK) {
- DEBUG(0,
+ dev_dbg(&link->dev,
"ray_cs interrupt network \"%s\" start failed\n",
local->sparm.b4.a_current_ess_id);
local->timer.function = &start_net;
} else {
- DEBUG(0,
+ dev_dbg(&link->dev,
"ray_cs interrupt network \"%s\" join failed\n",
local->sparm.b4.a_current_ess_id);
local->timer.function = &join_net;
@@ -2091,19 +2080,19 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
case CCS_START_ASSOCIATION:
if (status == CCS_COMMAND_COMPLETE) {
local->card_status = CARD_ASSOC_COMPLETE;
- DEBUG(0, "ray_cs association successful\n");
+ dev_dbg(&link->dev, "ray_cs association successful\n");
} else {
- DEBUG(0, "ray_cs association failed,\n");
+ dev_dbg(&link->dev, "ray_cs association failed,\n");
local->card_status = CARD_ASSOC_FAILED;
join_net((u_long) local);
}
break;
case CCS_TX_REQUEST:
if (status == CCS_COMMAND_COMPLETE) {
- DEBUG(3,
+ dev_dbg(&link->dev,
"ray_cs interrupt tx request complete\n");
} else {
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs interrupt tx request failed\n");
}
if (!sniffer)
@@ -2111,21 +2100,21 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
netif_wake_queue(dev);
break;
case CCS_TEST_MEMORY:
- DEBUG(1, "ray_cs interrupt mem test done\n");
+ dev_dbg(&link->dev, "ray_cs interrupt mem test done\n");
break;
case CCS_SHUTDOWN:
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs interrupt Unexpected CCS returned - Shutdown\n");
break;
case CCS_DUMP_MEMORY:
- DEBUG(1, "ray_cs interrupt dump memory done\n");
+ dev_dbg(&link->dev, "ray_cs interrupt dump memory done\n");
break;
case CCS_START_TIMER:
- DEBUG(2,
+ dev_dbg(&link->dev,
"ray_cs interrupt DING - raylink timer expired\n");
break;
default:
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs interrupt Unexpected CCS 0x%x returned 0x%x\n",
rcsindex, cmd);
}
@@ -2139,7 +2128,7 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
ray_rx(dev, local, prcs);
break;
case REJOIN_NET_COMPLETE:
- DEBUG(1, "ray_cs interrupt rejoin net complete\n");
+ dev_dbg(&link->dev, "ray_cs interrupt rejoin net complete\n");
local->card_status = CARD_ACQ_COMPLETE;
/* do we need to clear tx buffers CCS's? */
if (local->sparm.b4.a_network_type == ADHOC) {
@@ -2149,7 +2138,7 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
memcpy_fromio(&local->bss_id,
prcs->var.rejoin_net_complete.
bssid, ADDRLEN);
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs new BSSID = %02x%02x%02x%02x%02x%02x\n",
local->bss_id[0], local->bss_id[1],
local->bss_id[2], local->bss_id[3],
@@ -2159,15 +2148,15 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id)
}
break;
case ROAMING_INITIATED:
- DEBUG(1, "ray_cs interrupt roaming initiated\n");
+ dev_dbg(&link->dev, "ray_cs interrupt roaming initiated\n");
netif_stop_queue(dev);
local->card_status = CARD_DOING_ACQ;
break;
case JAPAN_CALL_SIGN_RXD:
- DEBUG(1, "ray_cs interrupt japan call sign rx\n");
+ dev_dbg(&link->dev, "ray_cs interrupt japan call sign rx\n");
break;
default:
- DEBUG(1,
+ dev_dbg(&link->dev,
"ray_cs Unexpected interrupt for RCS 0x%x cmd = 0x%x\n",
rcsindex,
(unsigned int)readb(&prcs->interrupt_id));
@@ -2186,7 +2175,7 @@ static void ray_rx(struct net_device *dev, ray_dev_t *local,
int rx_len;
unsigned int pkt_addr;
void __iomem *pmsg;
- DEBUG(4, "ray_rx process rx packet\n");
+ pr_debug("ray_rx process rx packet\n");
/* Calculate address of packet within Rx buffer */
pkt_addr = ((readb(&prcs->var.rx_packet.rx_data_ptr[0]) << 8)
@@ -2199,28 +2188,28 @@ static void ray_rx(struct net_device *dev, ray_dev_t *local,
pmsg = local->rmem + pkt_addr;
switch (readb(pmsg)) {
case DATA_TYPE:
- DEBUG(4, "ray_rx data type\n");
+ pr_debug("ray_rx data type\n");
rx_data(dev, prcs, pkt_addr, rx_len);
break;
case AUTHENTIC_TYPE:
- DEBUG(4, "ray_rx authentic type\n");
+ pr_debug("ray_rx authentic type\n");
if (sniffer)
rx_data(dev, prcs, pkt_addr, rx_len);
else
rx_authenticate(local, prcs, pkt_addr, rx_len);
break;
case DEAUTHENTIC_TYPE:
- DEBUG(4, "ray_rx deauth type\n");
+ pr_debug("ray_rx deauth type\n");
if (sniffer)
rx_data(dev, prcs, pkt_addr, rx_len);
else
rx_deauthenticate(local, prcs, pkt_addr, rx_len);
break;
case NULL_MSG_TYPE:
- DEBUG(3, "ray_cs rx NULL msg\n");
+ pr_debug("ray_cs rx NULL msg\n");
break;
case BEACON_TYPE:
- DEBUG(4, "ray_rx beacon type\n");
+ pr_debug("ray_rx beacon type\n");
if (sniffer)
rx_data(dev, prcs, pkt_addr, rx_len);
@@ -2233,7 +2222,7 @@ static void ray_rx(struct net_device *dev, ray_dev_t *local,
ray_get_stats(dev);
break;
default:
- DEBUG(0, "ray_cs unknown pkt type %2x\n",
+ pr_debug("ray_cs unknown pkt type %2x\n",
(unsigned int)readb(pmsg));
break;
}
@@ -2262,7 +2251,7 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
rx_len >
(dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN +
FCS_LEN)) {
- DEBUG(0,
+ pr_debug(
"ray_cs invalid packet length %d received \n",
rx_len);
return;
@@ -2273,17 +2262,17 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
rx_len >
(dev->mtu + RX_MAC_HEADER_LENGTH + ETH_HLEN +
FCS_LEN)) {
- DEBUG(0,
+ pr_debug(
"ray_cs invalid packet length %d received \n",
rx_len);
return;
}
}
}
- DEBUG(4, "ray_cs rx_data packet\n");
+ pr_debug("ray_cs rx_data packet\n");
/* If fragmented packet, verify sizes of fragments add up */
if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) {
- DEBUG(1, "ray_cs rx'ed fragment\n");
+ pr_debug("ray_cs rx'ed fragment\n");
tmp = (readb(&prcs->var.rx_packet.totalpacketlength[0]) << 8)
+ readb(&prcs->var.rx_packet.totalpacketlength[1]);
total_len = tmp;
@@ -2301,7 +2290,7 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
} while (1);
if (tmp < 0) {
- DEBUG(0,
+ pr_debug(
"ray_cs rx_data fragment lengths don't add up\n");
local->stats.rx_dropped++;
release_frag_chain(local, prcs);
@@ -2313,7 +2302,7 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
skb = dev_alloc_skb(total_len + 5);
if (skb == NULL) {
- DEBUG(0, "ray_cs rx_data could not allocate skb\n");
+ pr_debug("ray_cs rx_data could not allocate skb\n");
local->stats.rx_dropped++;
if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF)
release_frag_chain(local, prcs);
@@ -2321,7 +2310,7 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
}
skb_reserve(skb, 2); /* Align IP on 16 byte (TBD check this) */
- DEBUG(4, "ray_cs rx_data total_len = %x, rx_len = %x\n", total_len,
+ pr_debug("ray_cs rx_data total_len = %x, rx_len = %x\n", total_len,
rx_len);
/************************/
@@ -2354,7 +2343,7 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs,
tmp = 17;
if (readb(&prcs->var.rx_packet.next_frag_rcs_index) != 0xFF) {
prcslink = prcs;
- DEBUG(1, "ray_cs rx_data in fragment loop\n");
+ pr_debug("ray_cs rx_data in fragment loop\n");
do {
prcslink = rcs_base(local)
+
@@ -2426,8 +2415,8 @@ static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len)
memcpy(destaddr, ieee80211_get_DA(pmac), ADDRLEN);
memcpy(srcaddr, ieee80211_get_SA(pmac), ADDRLEN);
-#ifdef PCMCIA_DEBUG
- if (pc_debug > 3) {
+#if 0
+ if {
print_hex_dump(KERN_DEBUG, "skb->data before untranslate: ",
DUMP_PREFIX_NONE, 16, 1,
skb->data, 64, true);
@@ -2441,7 +2430,7 @@ static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len)
if (psnap->dsap != 0xaa || psnap->ssap != 0xaa || psnap->ctrl != 3) {
/* not a snap type so leave it alone */
- DEBUG(3, "ray_cs untranslate NOT SNAP %02x %02x %02x\n",
+ pr_debug("ray_cs untranslate NOT SNAP %02x %02x %02x\n",
psnap->dsap, psnap->ssap, psnap->ctrl);
delta = RX_MAC_HEADER_LENGTH - ETH_HLEN;
@@ -2450,7 +2439,7 @@ static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len)
} else { /* Its a SNAP */
if (memcmp(psnap->org, org_bridge, 3) == 0) {
/* EtherII and nuke the LLC */
- DEBUG(3, "ray_cs untranslate Bridge encap\n");
+ pr_debug("ray_cs untranslate Bridge encap\n");
delta = RX_MAC_HEADER_LENGTH
+ sizeof(struct snaphdr_t) - ETH_HLEN;
peth = (struct ethhdr *)(skb->data + delta);
@@ -2459,14 +2448,14 @@ static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len)
switch (ntohs(type)) {
case ETH_P_IPX:
case ETH_P_AARP:
- DEBUG(3, "ray_cs untranslate RFC IPX/AARP\n");
+ pr_debug("ray_cs untranslate RFC IPX/AARP\n");
delta = RX_MAC_HEADER_LENGTH - ETH_HLEN;
peth = (struct ethhdr *)(skb->data + delta);
peth->h_proto =
htons(len - RX_MAC_HEADER_LENGTH);
break;
default:
- DEBUG(3, "ray_cs untranslate RFC default\n");
+ pr_debug("ray_cs untranslate RFC default\n");
delta = RX_MAC_HEADER_LENGTH +
sizeof(struct snaphdr_t) - ETH_HLEN;
peth = (struct ethhdr *)(skb->data + delta);
@@ -2482,12 +2471,12 @@ static void untranslate(ray_dev_t *local, struct sk_buff *skb, int len)
}
/* TBD reserve skb_reserve(skb, delta); */
skb_pull(skb, delta);
- DEBUG(3, "untranslate after skb_pull(%d), skb->data = %p\n", delta,
+ pr_debug("untranslate after skb_pull(%d), skb->data = %p\n", delta,
skb->data);
memcpy(peth->h_dest, destaddr, ADDRLEN);
memcpy(peth->h_source, srcaddr, ADDRLEN);
-#ifdef PCMCIA_DEBUG
- if (pc_debug > 3) {
+#if 0
+ {
int i;
printk(KERN_DEBUG "skb->data after untranslate:");
for (i = 0; i < 64; i++)
@@ -2529,7 +2518,7 @@ static void release_frag_chain(ray_dev_t *local, struct rcs __iomem *prcs)
while (tmp--) {
writeb(CCS_BUFFER_FREE, &prcslink->buffer_status);
if (rcsindex >= (NUMBER_OF_CCS + NUMBER_OF_RCS)) {
- DEBUG(1, "ray_cs interrupt bad rcsindex = 0x%x\n",
+ pr_debug("ray_cs interrupt bad rcsindex = 0x%x\n",
rcsindex);
break;
}
@@ -2543,9 +2532,9 @@ static void release_frag_chain(ray_dev_t *local, struct rcs __iomem *prcs)
static void authenticate(ray_dev_t *local)
{
struct pcmcia_device *link = local->finder;
- DEBUG(0, "ray_cs Starting authentication.\n");
+ dev_dbg(&link->dev, "ray_cs Starting authentication.\n");
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs authenticate - device not present\n");
+ dev_dbg(&link->dev, "ray_cs authenticate - device not present\n");
return;
}
@@ -2573,11 +2562,11 @@ static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs,
copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff);
/* if we are trying to get authenticated */
if (local->sparm.b4.a_network_type == ADHOC) {
- DEBUG(1, "ray_cs rx_auth var= %02x %02x %02x %02x %02x %02x\n",
+ pr_debug("ray_cs rx_auth var= %02x %02x %02x %02x %02x %02x\n",
msg->var[0], msg->var[1], msg->var[2], msg->var[3],
msg->var[4], msg->var[5]);
if (msg->var[2] == 1) {
- DEBUG(0, "ray_cs Sending authentication response.\n");
+ pr_debug("ray_cs Sending authentication response.\n");
if (!build_auth_frame
(local, msg->mac.addr_2, OPEN_AUTH_RESPONSE)) {
local->authentication_state = NEED_TO_AUTH;
@@ -2591,13 +2580,13 @@ static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs,
/* Verify authentication sequence #2 and success */
if (msg->var[2] == 2) {
if ((msg->var[3] | msg->var[4]) == 0) {
- DEBUG(1, "Authentication successful\n");
+ pr_debug("Authentication successful\n");
local->card_status = CARD_AUTH_COMPLETE;
associate(local);
local->authentication_state =
AUTHENTICATED;
} else {
- DEBUG(0, "Authentication refused\n");
+ pr_debug("Authentication refused\n");
local->card_status = CARD_AUTH_REFUSED;
join_net((u_long) local);
local->authentication_state =
@@ -2617,22 +2606,22 @@ static void associate(ray_dev_t *local)
struct net_device *dev = link->priv;
int ccsindex;
if (!(pcmcia_dev_present(link))) {
- DEBUG(2, "ray_cs associate - device not present\n");
+ dev_dbg(&link->dev, "ray_cs associate - device not present\n");
return;
}
/* If no tx buffers available, return */
if ((ccsindex = get_free_ccs(local)) < 0) {
/* TBD should never be here but... what if we are? */
- DEBUG(1, "ray_cs associate - No free ccs\n");
+ dev_dbg(&link->dev, "ray_cs associate - No free ccs\n");
return;
}
- DEBUG(1, "ray_cs Starting association with access point\n");
+ dev_dbg(&link->dev, "ray_cs Starting association with access point\n");
pccs = ccs_base(local) + ccsindex;
/* fill in the CCS */
writeb(CCS_START_ASSOCIATION, &pccs->cmd);
/* Interrupt the firmware to process the command */
if (interrupt_ecf(local, ccsindex)) {
- DEBUG(1, "ray_cs associate failed - ECF not ready for intr\n");
+ dev_dbg(&link->dev, "ray_cs associate failed - ECF not ready for intr\n");
writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
del_timer(&local->timer);
@@ -2655,7 +2644,7 @@ static void rx_deauthenticate(ray_dev_t *local, struct rcs __iomem *prcs,
/* UCHAR buff[256];
struct rx_msg *msg = (struct rx_msg *)buff;
*/
- DEBUG(0, "Deauthentication frame received\n");
+ pr_debug("Deauthentication frame received\n");
local->authentication_state = UNAUTHENTICATED;
/* Need to reauthenticate or rejoin depending on reason code */
/* copy_from_rx_buff(local, buff, pkt_addr, rx_len & 0xff);
@@ -2823,7 +2812,7 @@ static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type)
/* If no tx buffers available, return */
if ((ccsindex = get_free_tx_ccs(local)) < 0) {
- DEBUG(1, "ray_cs send authenticate - No free tx ccs\n");
+ pr_debug("ray_cs send authenticate - No free tx ccs\n");
return -1;
}
@@ -2855,7 +2844,7 @@ static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type)
/* Interrupt the firmware to process the command */
if (interrupt_ecf(local, ccsindex)) {
- DEBUG(1,
+ pr_debug(
"ray_cs send authentication request failed - ECF not ready for intr\n");
writeb(CCS_BUFFER_FREE, &(pccs++)->buffer_status);
return -1;
@@ -2865,18 +2854,8 @@ static int build_auth_frame(ray_dev_t *local, UCHAR *dest, int auth_type)
/*===========================================================================*/
#ifdef CONFIG_PROC_FS
-static void raycs_write(const char *name, write_proc_t *w, void *data)
-{
- struct proc_dir_entry *entry =
- create_proc_entry(name, S_IFREG | S_IWUSR, NULL);
- if (entry) {
- entry->write_proc = w;
- entry->data = data;
- }
-}
-
-static int write_essid(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+static ssize_t ray_cs_essid_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *pos)
{
static char proc_essid[33];
unsigned int len = count;
@@ -2890,8 +2869,13 @@ static int write_essid(struct file *file, const char __user *buffer,
return count;
}
-static int write_int(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+static const struct file_operations ray_cs_essid_proc_fops = {
+ .owner = THIS_MODULE,
+ .write = ray_cs_essid_proc_write,
+};
+
+static ssize_t int_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *pos)
{
static char proc_number[10];
char *p;
@@ -2914,9 +2898,14 @@ static int write_int(struct file *file, const char __user *buffer,
nr = nr * 10 + c;
p++;
} while (--len);
- *(int *)data = nr;
+ *(int *)PDE(file->f_path.dentry->d_inode)->data = nr;
return count;
}
+
+static const struct file_operations int_proc_fops = {
+ .owner = THIS_MODULE,
+ .write = int_proc_write,
+};
#endif
static struct pcmcia_device_id ray_ids[] = {
@@ -2942,18 +2931,18 @@ static int __init init_ray_cs(void)
{
int rc;
- DEBUG(1, "%s\n", rcsid);
+ pr_debug("%s\n", rcsid);
rc = pcmcia_register_driver(&ray_driver);
- DEBUG(1, "raylink init_module register_pcmcia_driver returns 0x%x\n",
+ pr_debug("raylink init_module register_pcmcia_driver returns 0x%x\n",
rc);
#ifdef CONFIG_PROC_FS
proc_mkdir("driver/ray_cs", NULL);
proc_create("driver/ray_cs/ray_cs", 0, NULL, &ray_cs_proc_fops);
- raycs_write("driver/ray_cs/essid", write_essid, NULL);
- raycs_write("driver/ray_cs/net_type", write_int, &net_type);
- raycs_write("driver/ray_cs/translate", write_int, &translate);
+ proc_create("driver/ray_cs/essid", S_IWUSR, NULL, &ray_cs_essid_proc_fops);
+ proc_create_data("driver/ray_cs/net_type", S_IWUSR, NULL, &int_proc_fops, &net_type);
+ proc_create_data("driver/ray_cs/translate", S_IWUSR, NULL, &int_proc_fops, &translate);
#endif
if (translate != 0)
translate = 1;
@@ -2964,7 +2953,7 @@ static int __init init_ray_cs(void)
static void __exit exit_ray_cs(void)
{
- DEBUG(0, "ray_cs: cleanup_module\n");
+ pr_debug("ray_cs: cleanup_module\n");
#ifdef CONFIG_PROC_FS
remove_proc_entry("driver/ray_cs/ray_cs", NULL);
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 54175b6fa86..2ecbedb26e1 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -83,11 +83,11 @@ MODULE_PARM_DESC(roamdelta,
"set roaming tendency: 0=aggressive, 1=moderate, "
"2=conservative (default: moderate)");
-static int modparam_workaround_interval = 500;
+static int modparam_workaround_interval;
module_param_named(workaround_interval, modparam_workaround_interval,
int, 0444);
MODULE_PARM_DESC(workaround_interval,
- "set stall workaround interval in msecs (default: 500)");
+ "set stall workaround interval in msecs (0=disabled) (default: 0)");
/* various RNDIS OID defs */
@@ -733,12 +733,13 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
le32_to_cpu(u.get_c->status));
if (ret == 0) {
+ memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len);
+
ret = le32_to_cpu(u.get_c->len);
if (ret > *len)
*len = ret;
- memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len);
- ret = rndis_error_status(u.get_c->status);
+ ret = rndis_error_status(u.get_c->status);
if (ret < 0)
devdbg(dev, "rndis_query_oid(%s): device returned "
"error, 0x%08x (%d)", oid_to_string(oid),
@@ -1072,6 +1073,8 @@ static int set_auth_mode(struct usbnet *usbdev, u32 wpa_version,
auth_mode = NDIS_80211_AUTH_SHARED;
else if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
auth_mode = NDIS_80211_AUTH_OPEN;
+ else if (auth_type == NL80211_AUTHTYPE_AUTOMATIC)
+ auth_mode = NDIS_80211_AUTH_AUTO_SWITCH;
else
return -ENOTSUPP;
@@ -2547,7 +2550,7 @@ static void rndis_device_poller(struct work_struct *work)
/* Workaround transfer stalls on poor quality links.
* TODO: find right way to fix these stalls (as stalls do not happen
* with ndiswrapper/windows driver). */
- if (priv->last_qual <= 25) {
+ if (priv->param_workaround_interval > 0 && priv->last_qual <= 25) {
/* Decrease stats worker interval to catch stalls.
* faster. Faster than 400-500ms causes packet loss,
* Slower doesn't catch stalls fast enough.
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig
index ed1f997e352..bf60689aaab 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
@@ -1,6 +1,6 @@
menuconfig RT2X00
tristate "Ralink driver support"
- depends on MAC80211 && WLAN_80211
+ depends on MAC80211
---help---
This will enable the support for the Ralink drivers,
developed in the rt2x00 project <http://rt2x00.serialmonkey.com>.
@@ -53,6 +53,36 @@ config RT61PCI
When compiled as a module, this driver will be called rt61pci.
+config RT2800PCI_PCI
+ tristate
+ depends on PCI
+ default y
+
+config RT2800PCI_SOC
+ tristate
+ depends on RALINK_RT288X || RALINK_RT305X
+ default y
+
+config RT2800PCI
+ tristate "Ralink rt2800 (PCI/PCMCIA) support (VERY EXPERIMENTAL)"
+ depends on (RT2800PCI_PCI || RT2800PCI_SOC) && EXPERIMENTAL
+ select RT2800_LIB
+ select RT2X00_LIB_PCI if RT2800PCI_PCI
+ select RT2X00_LIB_SOC if RT2800PCI_SOC
+ select RT2X00_LIB_HT
+ select RT2X00_LIB_FIRMWARE
+ select RT2X00_LIB_CRYPTO
+ select CRC_CCITT
+ select EEPROM_93CX6
+ ---help---
+ This adds support for rt2800 wireless chipset family.
+ Supported chips: RT2760, RT2790, RT2860, RT2880, RT2890 & RT3052
+
+ This driver is non-functional at the moment and is intended for
+ developers.
+
+ When compiled as a module, this driver will be called "rt2800pci.ko".
+
config RT2500USB
tristate "Ralink rt2500 (USB) support"
depends on USB
@@ -78,8 +108,9 @@ config RT73USB
When compiled as a module, this driver will be called rt73usb.
config RT2800USB
- tristate "Ralink rt2800 (USB) support"
+ tristate "Ralink rt2800 (USB) support (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
+ select RT2800_LIB
select RT2X00_LIB_USB
select RT2X00_LIB_HT
select RT2X00_LIB_FIRMWARE
@@ -89,12 +120,23 @@ config RT2800USB
This adds experimental support for rt2800 wireless chipset family.
Supported chips: RT2770, RT2870 & RT3070.
+ Known issues:
+ - support for RT2870 chips doesn't work with 802.11n APs yet
+ - support for RT3070 chips is non-functional at the moment
+
When compiled as a module, this driver will be called "rt2800usb.ko".
+config RT2800_LIB
+ tristate
+
config RT2X00_LIB_PCI
tristate
select RT2X00_LIB
+config RT2X00_LIB_SOC
+ tristate
+ select RT2X00_LIB
+
config RT2X00_LIB_USB
tristate
select RT2X00_LIB
diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile
index 13043ea9766..97133985829 100644
--- a/drivers/net/wireless/rt2x00/Makefile
+++ b/drivers/net/wireless/rt2x00/Makefile
@@ -11,10 +11,13 @@ rt2x00lib-$(CONFIG_RT2X00_LIB_HT) += rt2x00ht.o
obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o
obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o
+obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o
obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o
+obj-$(CONFIG_RT2800_LIB) += rt2800lib.o
obj-$(CONFIG_RT2400PCI) += rt2400pci.o
obj-$(CONFIG_RT2500PCI) += rt2500pci.o
obj-$(CONFIG_RT61PCI) += rt61pci.o
+obj-$(CONFIG_RT2800PCI) += rt2800pci.o
obj-$(CONFIG_RT2500USB) += rt2500usb.o
obj-$(CONFIG_RT73USB) += rt73usb.o
obj-$(CONFIG_RT2800USB) += rt2800usb.o
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 798f625e38f..e7f46405a41 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -1341,6 +1341,7 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
rt2x00pci_register_read(rt2x00dev, CSR0, &reg);
rt2x00_set_chip_rf(rt2x00dev, value, reg);
+ rt2x00_print_chip(rt2x00dev);
if (!rt2x00_rf(&rt2x00dev->chip, RF2420) &&
!rt2x00_rf(&rt2x00dev->chip, RF2421)) {
@@ -1431,7 +1432,6 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
- rt2x00dev->hw->extra_tx_headroom = 0;
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
@@ -1622,20 +1622,21 @@ static const struct data_queue_desc rt2400pci_queue_atim = {
};
static const struct rt2x00_ops rt2400pci_ops = {
- .name = KBUILD_MODNAME,
- .max_sta_intf = 1,
- .max_ap_intf = 1,
- .eeprom_size = EEPROM_SIZE,
- .rf_size = RF_SIZE,
- .tx_queues = NUM_TX_QUEUES,
- .rx = &rt2400pci_queue_rx,
- .tx = &rt2400pci_queue_tx,
- .bcn = &rt2400pci_queue_bcn,
- .atim = &rt2400pci_queue_atim,
- .lib = &rt2400pci_rt2x00_ops,
- .hw = &rt2400pci_mac80211_ops,
+ .name = KBUILD_MODNAME,
+ .max_sta_intf = 1,
+ .max_ap_intf = 1,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .extra_tx_headroom = 0,
+ .rx = &rt2400pci_queue_rx,
+ .tx = &rt2400pci_queue_tx,
+ .bcn = &rt2400pci_queue_bcn,
+ .atim = &rt2400pci_queue_atim,
+ .lib = &rt2400pci_rt2x00_ops,
+ .hw = &rt2400pci_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
- .debugfs = &rt2400pci_rt2x00debug,
+ .debugfs = &rt2400pci_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
};
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h
index ccd644104ad..c3dea697b90 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.h
+++ b/drivers/net/wireless/rt2x00/rt2400pci.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -35,7 +35,7 @@
/*
* Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
*/
#define DEFAULT_RSSI_OFFSET 100
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index 2e872ac6982..408fcfc120f 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -1505,6 +1505,7 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
rt2x00pci_register_read(rt2x00dev, CSR0, &reg);
rt2x00_set_chip_rf(rt2x00dev, value, reg);
+ rt2x00_print_chip(rt2x00dev);
if (!rt2x00_rf(&rt2x00dev->chip, RF2522) &&
!rt2x00_rf(&rt2x00dev->chip, RF2523) &&
@@ -1732,8 +1733,6 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
- rt2x00dev->hw->extra_tx_headroom = 0;
-
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
rt2x00_eeprom_addr(rt2x00dev,
@@ -1921,20 +1920,21 @@ static const struct data_queue_desc rt2500pci_queue_atim = {
};
static const struct rt2x00_ops rt2500pci_ops = {
- .name = KBUILD_MODNAME,
- .max_sta_intf = 1,
- .max_ap_intf = 1,
- .eeprom_size = EEPROM_SIZE,
- .rf_size = RF_SIZE,
- .tx_queues = NUM_TX_QUEUES,
- .rx = &rt2500pci_queue_rx,
- .tx = &rt2500pci_queue_tx,
- .bcn = &rt2500pci_queue_bcn,
- .atim = &rt2500pci_queue_atim,
- .lib = &rt2500pci_rt2x00_ops,
- .hw = &rt2500pci_mac80211_ops,
+ .name = KBUILD_MODNAME,
+ .max_sta_intf = 1,
+ .max_ap_intf = 1,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .extra_tx_headroom = 0,
+ .rx = &rt2500pci_queue_rx,
+ .tx = &rt2500pci_queue_tx,
+ .bcn = &rt2500pci_queue_bcn,
+ .atim = &rt2500pci_queue_atim,
+ .lib = &rt2500pci_rt2x00_ops,
+ .hw = &rt2500pci_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
- .debugfs = &rt2500pci_rt2x00debug,
+ .debugfs = &rt2500pci_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
};
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h
index 54d37957883..c6bd1fcae7e 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.h
+++ b/drivers/net/wireless/rt2x00/rt2500pci.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -46,7 +46,7 @@
/*
* Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
*/
#define DEFAULT_RSSI_OFFSET 121
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 22dd6d9e298..83f2592c59d 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -716,139 +716,6 @@ static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev,
}
/*
- * NOTE: This function is directly ported from legacy driver, but
- * despite it being declared it was never called. Although link tuning
- * sounds like a good idea, and usually works well for the other drivers,
- * it does _not_ work with rt2500usb. Enabling this function will result
- * in TX capabilities only until association kicks in. Immediately
- * after the successful association all TX frames will be kept in the
- * hardware queue and never transmitted.
- */
-#if 0
-static void rt2500usb_link_tuner(struct rt2x00_dev *rt2x00dev)
-{
- int rssi = rt2x00_get_link_rssi(&rt2x00dev->link);
- u16 bbp_thresh;
- u16 vgc_bound;
- u16 sens;
- u16 r24;
- u16 r25;
- u16 r61;
- u16 r17_sens;
- u8 r17;
- u8 up_bound;
- u8 low_bound;
-
- /*
- * Read current r17 value, as well as the sensitivity values
- * for the r17 register.
- */
- rt2500usb_bbp_read(rt2x00dev, 17, &r17);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &r17_sens);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &vgc_bound);
- up_bound = rt2x00_get_field16(vgc_bound, EEPROM_BBPTUNE_VGCUPPER);
- low_bound = rt2x00_get_field16(vgc_bound, EEPROM_BBPTUNE_VGCLOWER);
-
- /*
- * If we are not associated, we should go straight to the
- * dynamic CCA tuning.
- */
- if (!rt2x00dev->intf_associated)
- goto dynamic_cca_tune;
-
- /*
- * Determine the BBP tuning threshold and correctly
- * set BBP 24, 25 and 61.
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &bbp_thresh);
- bbp_thresh = rt2x00_get_field16(bbp_thresh, EEPROM_BBPTUNE_THRESHOLD);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &r24);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &r25);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &r61);
-
- if ((rssi + bbp_thresh) > 0) {
- r24 = rt2x00_get_field16(r24, EEPROM_BBPTUNE_R24_HIGH);
- r25 = rt2x00_get_field16(r25, EEPROM_BBPTUNE_R25_HIGH);
- r61 = rt2x00_get_field16(r61, EEPROM_BBPTUNE_R61_HIGH);
- } else {
- r24 = rt2x00_get_field16(r24, EEPROM_BBPTUNE_R24_LOW);
- r25 = rt2x00_get_field16(r25, EEPROM_BBPTUNE_R25_LOW);
- r61 = rt2x00_get_field16(r61, EEPROM_BBPTUNE_R61_LOW);
- }
-
- rt2500usb_bbp_write(rt2x00dev, 24, r24);
- rt2500usb_bbp_write(rt2x00dev, 25, r25);
- rt2500usb_bbp_write(rt2x00dev, 61, r61);
-
- /*
- * A too low RSSI will cause too much false CCA which will
- * then corrupt the R17 tuning. To remidy this the tuning should
- * be stopped (While making sure the R17 value will not exceed limits)
- */
- if (rssi >= -40) {
- if (r17 != 0x60)
- rt2500usb_bbp_write(rt2x00dev, 17, 0x60);
- return;
- }
-
- /*
- * Special big-R17 for short distance
- */
- if (rssi >= -58) {
- sens = rt2x00_get_field16(r17_sens, EEPROM_BBPTUNE_R17_LOW);
- if (r17 != sens)
- rt2500usb_bbp_write(rt2x00dev, 17, sens);
- return;
- }
-
- /*
- * Special mid-R17 for middle distance
- */
- if (rssi >= -74) {
- sens = rt2x00_get_field16(r17_sens, EEPROM_BBPTUNE_R17_HIGH);
- if (r17 != sens)
- rt2500usb_bbp_write(rt2x00dev, 17, sens);
- return;
- }
-
- /*
- * Leave short or middle distance condition, restore r17
- * to the dynamic tuning range.
- */
- low_bound = 0x32;
- if (rssi < -77)
- up_bound -= (-77 - rssi);
-
- if (up_bound < low_bound)
- up_bound = low_bound;
-
- if (r17 > up_bound) {
- rt2500usb_bbp_write(rt2x00dev, 17, up_bound);
- rt2x00dev->link.vgc_level = up_bound;
- return;
- }
-
-dynamic_cca_tune:
-
- /*
- * R17 is inside the dynamic tuning range,
- * start tuning the link based on the false cca counter.
- */
- if (rt2x00dev->link.qual.false_cca > 512 && r17 < up_bound) {
- rt2500usb_bbp_write(rt2x00dev, 17, ++r17);
- rt2x00dev->link.vgc_level = r17;
- } else if (rt2x00dev->link.qual.false_cca < 100 && r17 > low_bound) {
- rt2500usb_bbp_write(rt2x00dev, 17, --r17);
- rt2x00dev->link.vgc_level = r17;
- }
-}
-#else
-#define rt2500usb_link_tuner NULL
-#endif
-
-/*
* Initialization functions.
*/
static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev)
@@ -1542,6 +1409,7 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
rt2500usb_register_read(rt2x00dev, MAC_CSR0, &reg);
rt2x00_set_chip(rt2x00dev, RT2570, value, reg);
+ rt2x00_print_chip(rt2x00dev);
if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0) ||
rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) {
@@ -1788,8 +1656,6 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
- rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE;
-
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
rt2x00_eeprom_addr(rt2x00dev,
@@ -1910,7 +1776,6 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
.rfkill_poll = rt2500usb_rfkill_poll,
.link_stats = rt2500usb_link_stats,
.reset_tuner = rt2500usb_reset_tuner,
- .link_tuner = rt2500usb_link_tuner,
.write_tx_desc = rt2500usb_write_tx_desc,
.write_tx_data = rt2x00usb_write_tx_data,
.write_beacon = rt2500usb_write_beacon,
@@ -1956,20 +1821,21 @@ static const struct data_queue_desc rt2500usb_queue_atim = {
};
static const struct rt2x00_ops rt2500usb_ops = {
- .name = KBUILD_MODNAME,
- .max_sta_intf = 1,
- .max_ap_intf = 1,
- .eeprom_size = EEPROM_SIZE,
- .rf_size = RF_SIZE,
- .tx_queues = NUM_TX_QUEUES,
- .rx = &rt2500usb_queue_rx,
- .tx = &rt2500usb_queue_tx,
- .bcn = &rt2500usb_queue_bcn,
- .atim = &rt2500usb_queue_atim,
- .lib = &rt2500usb_rt2x00_ops,
- .hw = &rt2500usb_mac80211_ops,
+ .name = KBUILD_MODNAME,
+ .max_sta_intf = 1,
+ .max_ap_intf = 1,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .extra_tx_headroom = TXD_DESC_SIZE,
+ .rx = &rt2500usb_queue_rx,
+ .tx = &rt2500usb_queue_tx,
+ .bcn = &rt2500usb_queue_bcn,
+ .atim = &rt2500usb_queue_atim,
+ .lib = &rt2500usb_rt2x00_ops,
+ .hw = &rt2500usb_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
- .debugfs = &rt2500usb_rt2x00debug,
+ .debugfs = &rt2500usb_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
};
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h
index b01edca4258..b493306a7ee 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.h
+++ b/drivers/net/wireless/rt2x00/rt2500usb.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -46,7 +46,7 @@
/*
* Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
*/
#define DEFAULT_RSSI_OFFSET 120
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
new file mode 100644
index 00000000000..c5fe867665e
--- /dev/null
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -0,0 +1,1852 @@
+/*
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ Module: rt2800
+ Abstract: Data structures and registers for the rt2800 modules.
+ Supported chipsets: RT2800E, RT2800ED & RT2800U.
+ */
+
+#ifndef RT2800_H
+#define RT2800_H
+
+/*
+ * RF chip defines.
+ *
+ * RF2820 2.4G 2T3R
+ * RF2850 2.4G/5G 2T3R
+ * RF2720 2.4G 1T2R
+ * RF2750 2.4G/5G 1T2R
+ * RF3020 2.4G 1T1R
+ * RF2020 2.4G B/G
+ * RF3021 2.4G 1T2R
+ * RF3022 2.4G 2T2R
+ * RF3052 2.4G 2T2R
+ */
+#define RF2820 0x0001
+#define RF2850 0x0002
+#define RF2720 0x0003
+#define RF2750 0x0004
+#define RF3020 0x0005
+#define RF2020 0x0006
+#define RF3021 0x0007
+#define RF3022 0x0008
+#define RF3052 0x0009
+
+/*
+ * Chipset version.
+ */
+#define RT2860C_VERSION 0x28600100
+#define RT2860D_VERSION 0x28600101
+#define RT2880E_VERSION 0x28720200
+#define RT2883_VERSION 0x28830300
+#define RT3070_VERSION 0x30700200
+
+/*
+ * Signal information.
+ * Default offset is required for RSSI <-> dBm conversion.
+ */
+#define DEFAULT_RSSI_OFFSET 120 /* FIXME */
+
+/*
+ * Register layout information.
+ */
+#define CSR_REG_BASE 0x1000
+#define CSR_REG_SIZE 0x0800
+#define EEPROM_BASE 0x0000
+#define EEPROM_SIZE 0x0110
+#define BBP_BASE 0x0000
+#define BBP_SIZE 0x0080
+#define RF_BASE 0x0004
+#define RF_SIZE 0x0010
+
+/*
+ * Number of TX queues.
+ */
+#define NUM_TX_QUEUES 4
+
+/*
+ * USB registers.
+ */
+
+/*
+ * INT_SOURCE_CSR: Interrupt source register.
+ * Write one to clear corresponding bit.
+ * TX_FIFO_STATUS: FIFO Statistics is full, sw should read 0x171c
+ */
+#define INT_SOURCE_CSR 0x0200
+#define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001)
+#define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002)
+#define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004)
+#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008)
+#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010)
+#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020)
+#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040)
+#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080)
+#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100)
+#define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200)
+#define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400)
+#define INT_SOURCE_CSR_TBTT FIELD32(0x00000800)
+#define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000)
+#define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000)
+#define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000)
+#define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000)
+#define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000)
+#define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000)
+
+/*
+ * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF.
+ */
+#define INT_MASK_CSR 0x0204
+#define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001)
+#define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002)
+#define INT_MASK_CSR_RX_DONE FIELD32(0x00000004)
+#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008)
+#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010)
+#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020)
+#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040)
+#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080)
+#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100)
+#define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200)
+#define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400)
+#define INT_MASK_CSR_TBTT FIELD32(0x00000800)
+#define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000)
+#define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000)
+#define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000)
+#define INT_MASK_CSR_GPTIMER FIELD32(0x00008000)
+#define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000)
+#define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000)
+
+/*
+ * WPDMA_GLO_CFG
+ */
+#define WPDMA_GLO_CFG 0x0208
+#define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001)
+#define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002)
+#define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004)
+#define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008)
+#define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030)
+#define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040)
+#define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080)
+#define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00)
+#define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000)
+
+/*
+ * WPDMA_RST_IDX
+ */
+#define WPDMA_RST_IDX 0x020c
+#define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001)
+#define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002)
+#define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004)
+#define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008)
+#define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010)
+#define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020)
+#define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000)
+
+/*
+ * DELAY_INT_CFG
+ */
+#define DELAY_INT_CFG 0x0210
+#define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff)
+#define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00)
+#define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000)
+#define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000)
+#define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000)
+#define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000)
+
+/*
+ * WMM_AIFSN_CFG: Aifsn for each EDCA AC
+ * AIFSN0: AC_BE
+ * AIFSN1: AC_BK
+ * AIFSN2: AC_VI
+ * AIFSN3: AC_VO
+ */
+#define WMM_AIFSN_CFG 0x0214
+#define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f)
+#define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0)
+#define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00)
+#define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000)
+
+/*
+ * WMM_CWMIN_CSR: CWmin for each EDCA AC
+ * CWMIN0: AC_BE
+ * CWMIN1: AC_BK
+ * CWMIN2: AC_VI
+ * CWMIN3: AC_VO
+ */
+#define WMM_CWMIN_CFG 0x0218
+#define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f)
+#define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0)
+#define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00)
+#define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000)
+
+/*
+ * WMM_CWMAX_CSR: CWmax for each EDCA AC
+ * CWMAX0: AC_BE
+ * CWMAX1: AC_BK
+ * CWMAX2: AC_VI
+ * CWMAX3: AC_VO
+ */
+#define WMM_CWMAX_CFG 0x021c
+#define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f)
+#define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0)
+#define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00)
+#define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000)
+
+/*
+ * AC_TXOP0: AC_BK/AC_BE TXOP register
+ * AC0TXOP: AC_BK in unit of 32us
+ * AC1TXOP: AC_BE in unit of 32us
+ */
+#define WMM_TXOP0_CFG 0x0220
+#define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff)
+#define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000)
+
+/*
+ * AC_TXOP1: AC_VO/AC_VI TXOP register
+ * AC2TXOP: AC_VI in unit of 32us
+ * AC3TXOP: AC_VO in unit of 32us
+ */
+#define WMM_TXOP1_CFG 0x0224
+#define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff)
+#define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000)
+
+/*
+ * GPIO_CTRL_CFG:
+ */
+#define GPIO_CTRL_CFG 0x0228
+#define GPIO_CTRL_CFG_BIT0 FIELD32(0x00000001)
+#define GPIO_CTRL_CFG_BIT1 FIELD32(0x00000002)
+#define GPIO_CTRL_CFG_BIT2 FIELD32(0x00000004)
+#define GPIO_CTRL_CFG_BIT3 FIELD32(0x00000008)
+#define GPIO_CTRL_CFG_BIT4 FIELD32(0x00000010)
+#define GPIO_CTRL_CFG_BIT5 FIELD32(0x00000020)
+#define GPIO_CTRL_CFG_BIT6 FIELD32(0x00000040)
+#define GPIO_CTRL_CFG_BIT7 FIELD32(0x00000080)
+#define GPIO_CTRL_CFG_BIT8 FIELD32(0x00000100)
+
+/*
+ * MCU_CMD_CFG
+ */
+#define MCU_CMD_CFG 0x022c
+
+/*
+ * AC_BK register offsets
+ */
+#define TX_BASE_PTR0 0x0230
+#define TX_MAX_CNT0 0x0234
+#define TX_CTX_IDX0 0x0238
+#define TX_DTX_IDX0 0x023c
+
+/*
+ * AC_BE register offsets
+ */
+#define TX_BASE_PTR1 0x0240
+#define TX_MAX_CNT1 0x0244
+#define TX_CTX_IDX1 0x0248
+#define TX_DTX_IDX1 0x024c
+
+/*
+ * AC_VI register offsets
+ */
+#define TX_BASE_PTR2 0x0250
+#define TX_MAX_CNT2 0x0254
+#define TX_CTX_IDX2 0x0258
+#define TX_DTX_IDX2 0x025c
+
+/*
+ * AC_VO register offsets
+ */
+#define TX_BASE_PTR3 0x0260
+#define TX_MAX_CNT3 0x0264
+#define TX_CTX_IDX3 0x0268
+#define TX_DTX_IDX3 0x026c
+
+/*
+ * HCCA register offsets
+ */
+#define TX_BASE_PTR4 0x0270
+#define TX_MAX_CNT4 0x0274
+#define TX_CTX_IDX4 0x0278
+#define TX_DTX_IDX4 0x027c
+
+/*
+ * MGMT register offsets
+ */
+#define TX_BASE_PTR5 0x0280
+#define TX_MAX_CNT5 0x0284
+#define TX_CTX_IDX5 0x0288
+#define TX_DTX_IDX5 0x028c
+
+/*
+ * RX register offsets
+ */
+#define RX_BASE_PTR 0x0290
+#define RX_MAX_CNT 0x0294
+#define RX_CRX_IDX 0x0298
+#define RX_DRX_IDX 0x029c
+
+/*
+ * PBF_SYS_CTRL
+ * HOST_RAM_WRITE: enable Host program ram write selection
+ */
+#define PBF_SYS_CTRL 0x0400
+#define PBF_SYS_CTRL_READY FIELD32(0x00000080)
+#define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000)
+
+/*
+ * HOST-MCU shared memory
+ */
+#define HOST_CMD_CSR 0x0404
+#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff)
+
+/*
+ * PBF registers
+ * Most are for debug. Driver doesn't touch PBF register.
+ */
+#define PBF_CFG 0x0408
+#define PBF_MAX_PCNT 0x040c
+#define PBF_CTRL 0x0410
+#define PBF_INT_STA 0x0414
+#define PBF_INT_ENA 0x0418
+
+/*
+ * BCN_OFFSET0:
+ */
+#define BCN_OFFSET0 0x042c
+#define BCN_OFFSET0_BCN0 FIELD32(0x000000ff)
+#define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00)
+#define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000)
+#define BCN_OFFSET0_BCN3 FIELD32(0xff000000)
+
+/*
+ * BCN_OFFSET1:
+ */
+#define BCN_OFFSET1 0x0430
+#define BCN_OFFSET1_BCN4 FIELD32(0x000000ff)
+#define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00)
+#define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000)
+#define BCN_OFFSET1_BCN7 FIELD32(0xff000000)
+
+/*
+ * PBF registers
+ * Most are for debug. Driver doesn't touch PBF register.
+ */
+#define TXRXQ_PCNT 0x0438
+#define PBF_DBG 0x043c
+
+/*
+ * RF registers
+ */
+#define RF_CSR_CFG 0x0500
+#define RF_CSR_CFG_DATA FIELD32(0x000000ff)
+#define RF_CSR_CFG_REGNUM FIELD32(0x00001f00)
+#define RF_CSR_CFG_WRITE FIELD32(0x00010000)
+#define RF_CSR_CFG_BUSY FIELD32(0x00020000)
+
+/*
+ * EFUSE_CSR: RT30x0 EEPROM
+ */
+#define EFUSE_CTRL 0x0580
+#define EFUSE_CTRL_ADDRESS_IN FIELD32(0x03fe0000)
+#define EFUSE_CTRL_MODE FIELD32(0x000000c0)
+#define EFUSE_CTRL_KICK FIELD32(0x40000000)
+#define EFUSE_CTRL_PRESENT FIELD32(0x80000000)
+
+/*
+ * EFUSE_DATA0
+ */
+#define EFUSE_DATA0 0x0590
+
+/*
+ * EFUSE_DATA1
+ */
+#define EFUSE_DATA1 0x0594
+
+/*
+ * EFUSE_DATA2
+ */
+#define EFUSE_DATA2 0x0598
+
+/*
+ * EFUSE_DATA3
+ */
+#define EFUSE_DATA3 0x059c
+
+/*
+ * MAC Control/Status Registers(CSR).
+ * Some values are set in TU, whereas 1 TU == 1024 us.
+ */
+
+/*
+ * MAC_CSR0: ASIC revision number.
+ * ASIC_REV: 0
+ * ASIC_VER: 2860 or 2870
+ */
+#define MAC_CSR0 0x1000
+#define MAC_CSR0_ASIC_REV FIELD32(0x0000ffff)
+#define MAC_CSR0_ASIC_VER FIELD32(0xffff0000)
+
+/*
+ * MAC_SYS_CTRL:
+ */
+#define MAC_SYS_CTRL 0x1004
+#define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001)
+#define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002)
+#define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004)
+#define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008)
+#define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010)
+#define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020)
+#define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040)
+#define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080)
+
+/*
+ * MAC_ADDR_DW0: STA MAC register 0
+ */
+#define MAC_ADDR_DW0 0x1008
+#define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff)
+#define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00)
+#define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000)
+#define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000)
+
+/*
+ * MAC_ADDR_DW1: STA MAC register 1
+ * UNICAST_TO_ME_MASK:
+ * Used to mask off bits from byte 5 of the MAC address
+ * to determine the UNICAST_TO_ME bit for RX frames.
+ * The full mask is complemented by BSS_ID_MASK:
+ * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK
+ */
+#define MAC_ADDR_DW1 0x100c
+#define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff)
+#define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00)
+#define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000)
+
+/*
+ * MAC_BSSID_DW0: BSSID register 0
+ */
+#define MAC_BSSID_DW0 0x1010
+#define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff)
+#define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00)
+#define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000)
+#define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000)
+
+/*
+ * MAC_BSSID_DW1: BSSID register 1
+ * BSS_ID_MASK:
+ * 0: 1-BSSID mode (BSS index = 0)
+ * 1: 2-BSSID mode (BSS index: Byte5, bit 0)
+ * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1)
+ * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2)
+ * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the
+ * BSSID. This will make sure that those bits will be ignored
+ * when determining the MY_BSS of RX frames.
+ */
+#define MAC_BSSID_DW1 0x1014
+#define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff)
+#define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00)
+#define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000)
+#define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000)
+
+/*
+ * MAX_LEN_CFG: Maximum frame length register.
+ * MAX_MPDU: rt2860b max 16k bytes
+ * MAX_PSDU: Maximum PSDU length
+ * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16
+ */
+#define MAX_LEN_CFG 0x1018
+#define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff)
+#define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000)
+#define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000)
+#define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000)
+
+/*
+ * BBP_CSR_CFG: BBP serial control register
+ * VALUE: Register value to program into BBP
+ * REG_NUM: Selected BBP register
+ * READ_CONTROL: 0 write BBP, 1 read BBP
+ * BUSY: ASIC is busy executing BBP commands
+ * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks
+ * BBP_RW_MODE: 0 serial, 1 paralell
+ */
+#define BBP_CSR_CFG 0x101c
+#define BBP_CSR_CFG_VALUE FIELD32(0x000000ff)
+#define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00)
+#define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000)
+#define BBP_CSR_CFG_BUSY FIELD32(0x00020000)
+#define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000)
+#define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000)
+
+/*
+ * RF_CSR_CFG0: RF control register
+ * REGID_AND_VALUE: Register value to program into RF
+ * BITWIDTH: Selected RF register
+ * STANDBYMODE: 0 high when standby, 1 low when standby
+ * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate
+ * BUSY: ASIC is busy executing RF commands
+ */
+#define RF_CSR_CFG0 0x1020
+#define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff)
+#define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000)
+#define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff)
+#define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000)
+#define RF_CSR_CFG0_SEL FIELD32(0x40000000)
+#define RF_CSR_CFG0_BUSY FIELD32(0x80000000)
+
+/*
+ * RF_CSR_CFG1: RF control register
+ * REGID_AND_VALUE: Register value to program into RF
+ * RFGAP: Gap between BB_CONTROL_RF and RF_LE
+ * 0: 3 system clock cycle (37.5usec)
+ * 1: 5 system clock cycle (62.5usec)
+ */
+#define RF_CSR_CFG1 0x1024
+#define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff)
+#define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000)
+
+/*
+ * RF_CSR_CFG2: RF control register
+ * VALUE: Register value to program into RF
+ */
+#define RF_CSR_CFG2 0x1028
+#define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff)
+
+/*
+ * LED_CFG: LED control
+ * color LED's:
+ * 0: off
+ * 1: blinking upon TX2
+ * 2: periodic slow blinking
+ * 3: always on
+ * LED polarity:
+ * 0: active low
+ * 1: active high
+ */
+#define LED_CFG 0x102c
+#define LED_CFG_ON_PERIOD FIELD32(0x000000ff)
+#define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00)
+#define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000)
+#define LED_CFG_R_LED_MODE FIELD32(0x03000000)
+#define LED_CFG_G_LED_MODE FIELD32(0x0c000000)
+#define LED_CFG_Y_LED_MODE FIELD32(0x30000000)
+#define LED_CFG_LED_POLAR FIELD32(0x40000000)
+
+/*
+ * XIFS_TIME_CFG: MAC timing
+ * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX
+ * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX
+ * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX
+ * when MAC doesn't reference BBP signal BBRXEND
+ * EIFS: unit 1us
+ * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer
+ *
+ */
+#define XIFS_TIME_CFG 0x1100
+#define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff)
+#define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00)
+#define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000)
+#define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000)
+#define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000)
+
+/*
+ * BKOFF_SLOT_CFG:
+ */
+#define BKOFF_SLOT_CFG 0x1104
+#define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff)
+#define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00)
+
+/*
+ * NAV_TIME_CFG:
+ */
+#define NAV_TIME_CFG 0x1108
+#define NAV_TIME_CFG_SIFS FIELD32(0x000000ff)
+#define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00)
+#define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000)
+#define NAV_TIME_ZERO_SIFS FIELD32(0x02000000)
+
+/*
+ * CH_TIME_CFG: count as channel busy
+ */
+#define CH_TIME_CFG 0x110c
+
+/*
+ * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us
+ */
+#define PBF_LIFE_TIMER 0x1110
+
+/*
+ * BCN_TIME_CFG:
+ * BEACON_INTERVAL: in unit of 1/16 TU
+ * TSF_TICKING: Enable TSF auto counting
+ * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode
+ * BEACON_GEN: Enable beacon generator
+ */
+#define BCN_TIME_CFG 0x1114
+#define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff)
+#define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000)
+#define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000)
+#define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000)
+#define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000)
+#define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000)
+
+/*
+ * TBTT_SYNC_CFG:
+ */
+#define TBTT_SYNC_CFG 0x1118
+
+/*
+ * TSF_TIMER_DW0: Local lsb TSF timer, read-only
+ */
+#define TSF_TIMER_DW0 0x111c
+#define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff)
+
+/*
+ * TSF_TIMER_DW1: Local msb TSF timer, read-only
+ */
+#define TSF_TIMER_DW1 0x1120
+#define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff)
+
+/*
+ * TBTT_TIMER: TImer remains till next TBTT, read-only
+ */
+#define TBTT_TIMER 0x1124
+
+/*
+ * INT_TIMER_CFG:
+ */
+#define INT_TIMER_CFG 0x1128
+
+/*
+ * INT_TIMER_EN: GP-timer and pre-tbtt Int enable
+ */
+#define INT_TIMER_EN 0x112c
+
+/*
+ * CH_IDLE_STA: channel idle time
+ */
+#define CH_IDLE_STA 0x1130
+
+/*
+ * CH_BUSY_STA: channel busy time
+ */
+#define CH_BUSY_STA 0x1134
+
+/*
+ * MAC_STATUS_CFG:
+ * BBP_RF_BUSY: When set to 0, BBP and RF are stable.
+ * if 1 or higher one of the 2 registers is busy.
+ */
+#define MAC_STATUS_CFG 0x1200
+#define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003)
+
+/*
+ * PWR_PIN_CFG:
+ */
+#define PWR_PIN_CFG 0x1204
+
+/*
+ * AUTOWAKEUP_CFG: Manual power control / status register
+ * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set
+ * AUTOWAKE: 0:sleep, 1:awake
+ */
+#define AUTOWAKEUP_CFG 0x1208
+#define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff)
+#define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00)
+#define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000)
+
+/*
+ * EDCA_AC0_CFG:
+ */
+#define EDCA_AC0_CFG 0x1300
+#define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff)
+#define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00)
+#define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000)
+#define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000)
+
+/*
+ * EDCA_AC1_CFG:
+ */
+#define EDCA_AC1_CFG 0x1304
+#define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff)
+#define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00)
+#define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000)
+#define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000)
+
+/*
+ * EDCA_AC2_CFG:
+ */
+#define EDCA_AC2_CFG 0x1308
+#define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff)
+#define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00)
+#define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000)
+#define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000)
+
+/*
+ * EDCA_AC3_CFG:
+ */
+#define EDCA_AC3_CFG 0x130c
+#define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff)
+#define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00)
+#define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000)
+#define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000)
+
+/*
+ * EDCA_TID_AC_MAP:
+ */
+#define EDCA_TID_AC_MAP 0x1310
+
+/*
+ * TX_PWR_CFG_0:
+ */
+#define TX_PWR_CFG_0 0x1314
+#define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f)
+#define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0)
+#define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00)
+#define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000)
+#define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000)
+#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000)
+#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000)
+#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000)
+
+/*
+ * TX_PWR_CFG_1:
+ */
+#define TX_PWR_CFG_1 0x1318
+#define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f)
+#define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0)
+#define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00)
+#define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000)
+#define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000)
+#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000)
+
+/*
+ * TX_PWR_CFG_2:
+ */
+#define TX_PWR_CFG_2 0x131c
+#define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f)
+#define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0)
+#define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00)
+#define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000)
+#define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000)
+#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000)
+#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000)
+#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000)
+
+/*
+ * TX_PWR_CFG_3:
+ */
+#define TX_PWR_CFG_3 0x1320
+#define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0)
+#define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000)
+#define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000)
+#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000)
+#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000)
+
+/*
+ * TX_PWR_CFG_4:
+ */
+#define TX_PWR_CFG_4 0x1324
+#define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f)
+#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0)
+#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00)
+#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000)
+
+/*
+ * TX_PIN_CFG:
+ */
+#define TX_PIN_CFG 0x1328
+#define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001)
+#define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002)
+#define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004)
+#define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008)
+#define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010)
+#define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020)
+#define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040)
+#define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080)
+#define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100)
+#define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200)
+#define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400)
+#define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800)
+#define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000)
+#define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000)
+#define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000)
+#define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000)
+#define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000)
+#define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000)
+#define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000)
+#define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000)
+
+/*
+ * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz
+ */
+#define TX_BAND_CFG 0x132c
+#define TX_BAND_CFG_HT40_PLUS FIELD32(0x00000001)
+#define TX_BAND_CFG_A FIELD32(0x00000002)
+#define TX_BAND_CFG_BG FIELD32(0x00000004)
+
+/*
+ * TX_SW_CFG0:
+ */
+#define TX_SW_CFG0 0x1330
+
+/*
+ * TX_SW_CFG1:
+ */
+#define TX_SW_CFG1 0x1334
+
+/*
+ * TX_SW_CFG2:
+ */
+#define TX_SW_CFG2 0x1338
+
+/*
+ * TXOP_THRES_CFG:
+ */
+#define TXOP_THRES_CFG 0x133c
+
+/*
+ * TXOP_CTRL_CFG:
+ */
+#define TXOP_CTRL_CFG 0x1340
+
+/*
+ * TX_RTS_CFG:
+ * RTS_THRES: unit:byte
+ * RTS_FBK_EN: enable rts rate fallback
+ */
+#define TX_RTS_CFG 0x1344
+#define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff)
+#define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00)
+#define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000)
+
+/*
+ * TX_TIMEOUT_CFG:
+ * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us
+ * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure
+ * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation.
+ * it is recommended that:
+ * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT)
+ */
+#define TX_TIMEOUT_CFG 0x1348
+#define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0)
+#define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00)
+#define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000)
+
+/*
+ * TX_RTY_CFG:
+ * SHORT_RTY_LIMIT: short retry limit
+ * LONG_RTY_LIMIT: long retry limit
+ * LONG_RTY_THRE: Long retry threshoold
+ * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode
+ * 0:expired by retry limit, 1: expired by mpdu life timer
+ * AGG_RTY_MODE: Aggregate MPDU retry mode
+ * 0:expired by retry limit, 1: expired by mpdu life timer
+ * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable
+ */
+#define TX_RTY_CFG 0x134c
+#define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff)
+#define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00)
+#define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000)
+#define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000)
+#define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000)
+#define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000)
+
+/*
+ * TX_LINK_CFG:
+ * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us
+ * MFB_ENABLE: TX apply remote MFB 1:enable
+ * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable
+ * 0: not apply remote remote unsolicit (MFS=7)
+ * TX_MRQ_EN: MCS request TX enable
+ * TX_RDG_EN: RDG TX enable
+ * TX_CF_ACK_EN: Piggyback CF-ACK enable
+ * REMOTE_MFB: remote MCS feedback
+ * REMOTE_MFS: remote MCS feedback sequence number
+ */
+#define TX_LINK_CFG 0x1350
+#define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff)
+#define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100)
+#define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200)
+#define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400)
+#define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800)
+#define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000)
+#define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000)
+#define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000)
+
+/*
+ * HT_FBK_CFG0:
+ */
+#define HT_FBK_CFG0 0x1354
+#define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f)
+#define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0)
+#define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00)
+#define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000)
+#define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000)
+#define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000)
+#define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000)
+#define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000)
+
+/*
+ * HT_FBK_CFG1:
+ */
+#define HT_FBK_CFG1 0x1358
+#define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f)
+#define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0)
+#define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00)
+#define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000)
+#define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000)
+#define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000)
+#define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000)
+#define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000)
+
+/*
+ * LG_FBK_CFG0:
+ */
+#define LG_FBK_CFG0 0x135c
+#define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f)
+#define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0)
+#define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00)
+#define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000)
+#define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000)
+#define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000)
+#define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000)
+#define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000)
+
+/*
+ * LG_FBK_CFG1:
+ */
+#define LG_FBK_CFG1 0x1360
+#define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f)
+#define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0)
+#define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00)
+#define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000)
+
+/*
+ * CCK_PROT_CFG: CCK Protection
+ * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd)
+ * PROTECT_CTRL: Protection control frame type for CCK TX
+ * 0:none, 1:RTS/CTS, 2:CTS-to-self
+ * PROTECT_NAV: TXOP protection type for CCK TX
+ * 0:none, 1:ShortNAVprotect, 2:LongNAVProtect
+ * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow
+ * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow
+ * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow
+ * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow
+ * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow
+ * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow
+ * RTS_TH_EN: RTS threshold enable on CCK TX
+ */
+#define CCK_PROT_CFG 0x1364
+#define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
+#define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
+#define CCK_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
+#define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
+#define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
+#define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
+#define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
+#define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
+#define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
+#define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
+
+/*
+ * OFDM_PROT_CFG: OFDM Protection
+ */
+#define OFDM_PROT_CFG 0x1368
+#define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
+#define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
+#define OFDM_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
+#define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
+#define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
+#define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
+#define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
+#define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
+#define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
+#define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
+
+/*
+ * MM20_PROT_CFG: MM20 Protection
+ */
+#define MM20_PROT_CFG 0x136c
+#define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
+#define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
+#define MM20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
+#define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
+#define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
+#define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
+#define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
+#define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
+#define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
+#define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
+
+/*
+ * MM40_PROT_CFG: MM40 Protection
+ */
+#define MM40_PROT_CFG 0x1370
+#define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
+#define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
+#define MM40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
+#define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
+#define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
+#define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
+#define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
+#define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
+#define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
+#define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
+
+/*
+ * GF20_PROT_CFG: GF20 Protection
+ */
+#define GF20_PROT_CFG 0x1374
+#define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
+#define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
+#define GF20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
+#define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
+#define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
+#define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
+#define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
+#define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
+#define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
+#define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
+
+/*
+ * GF40_PROT_CFG: GF40 Protection
+ */
+#define GF40_PROT_CFG 0x1378
+#define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
+#define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
+#define GF40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
+#define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
+#define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
+#define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
+#define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
+#define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
+#define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
+#define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
+
+/*
+ * EXP_CTS_TIME:
+ */
+#define EXP_CTS_TIME 0x137c
+
+/*
+ * EXP_ACK_TIME:
+ */
+#define EXP_ACK_TIME 0x1380
+
+/*
+ * RX_FILTER_CFG: RX configuration register.
+ */
+#define RX_FILTER_CFG 0x1400
+#define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001)
+#define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002)
+#define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004)
+#define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008)
+#define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010)
+#define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020)
+#define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040)
+#define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080)
+#define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100)
+#define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200)
+#define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400)
+#define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800)
+#define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000)
+#define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000)
+#define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000)
+#define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000)
+#define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000)
+
+/*
+ * AUTO_RSP_CFG:
+ * AUTORESPONDER: 0: disable, 1: enable
+ * BAC_ACK_POLICY: 0:long, 1:short preamble
+ * CTS_40_MMODE: Response CTS 40MHz duplicate mode
+ * CTS_40_MREF: Response CTS 40MHz duplicate mode
+ * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble
+ * DUAL_CTS_EN: Power bit value in control frame
+ * ACK_CTS_PSM_BIT:Power bit value in control frame
+ */
+#define AUTO_RSP_CFG 0x1404
+#define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001)
+#define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002)
+#define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004)
+#define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008)
+#define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010)
+#define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040)
+#define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080)
+
+/*
+ * LEGACY_BASIC_RATE:
+ */
+#define LEGACY_BASIC_RATE 0x1408
+
+/*
+ * HT_BASIC_RATE:
+ */
+#define HT_BASIC_RATE 0x140c
+
+/*
+ * HT_CTRL_CFG:
+ */
+#define HT_CTRL_CFG 0x1410
+
+/*
+ * SIFS_COST_CFG:
+ */
+#define SIFS_COST_CFG 0x1414
+
+/*
+ * RX_PARSER_CFG:
+ * Set NAV for all received frames
+ */
+#define RX_PARSER_CFG 0x1418
+
+/*
+ * TX_SEC_CNT0:
+ */
+#define TX_SEC_CNT0 0x1500
+
+/*
+ * RX_SEC_CNT0:
+ */
+#define RX_SEC_CNT0 0x1504
+
+/*
+ * CCMP_FC_MUTE:
+ */
+#define CCMP_FC_MUTE 0x1508
+
+/*
+ * TXOP_HLDR_ADDR0:
+ */
+#define TXOP_HLDR_ADDR0 0x1600
+
+/*
+ * TXOP_HLDR_ADDR1:
+ */
+#define TXOP_HLDR_ADDR1 0x1604
+
+/*
+ * TXOP_HLDR_ET:
+ */
+#define TXOP_HLDR_ET 0x1608
+
+/*
+ * QOS_CFPOLL_RA_DW0:
+ */
+#define QOS_CFPOLL_RA_DW0 0x160c
+
+/*
+ * QOS_CFPOLL_RA_DW1:
+ */
+#define QOS_CFPOLL_RA_DW1 0x1610
+
+/*
+ * QOS_CFPOLL_QC:
+ */
+#define QOS_CFPOLL_QC 0x1614
+
+/*
+ * RX_STA_CNT0: RX PLCP error count & RX CRC error count
+ */
+#define RX_STA_CNT0 0x1700
+#define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff)
+#define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000)
+
+/*
+ * RX_STA_CNT1: RX False CCA count & RX LONG frame count
+ */
+#define RX_STA_CNT1 0x1704
+#define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff)
+#define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000)
+
+/*
+ * RX_STA_CNT2:
+ */
+#define RX_STA_CNT2 0x1708
+#define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff)
+#define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000)
+
+/*
+ * TX_STA_CNT0: TX Beacon count
+ */
+#define TX_STA_CNT0 0x170c
+#define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff)
+#define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_STA_CNT1: TX tx count
+ */
+#define TX_STA_CNT1 0x1710
+#define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff)
+#define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000)
+
+/*
+ * TX_STA_CNT2: TX tx count
+ */
+#define TX_STA_CNT2 0x1714
+#define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff)
+#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_STA_FIFO: TX Result for specific PID status fifo register
+ */
+#define TX_STA_FIFO 0x1718
+#define TX_STA_FIFO_VALID FIELD32(0x00000001)
+#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e)
+#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020)
+#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040)
+#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080)
+#define TX_STA_FIFO_WCID FIELD32(0x0000ff00)
+#define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000)
+#define TX_STA_FIFO_MCS FIELD32(0x007f0000)
+#define TX_STA_FIFO_PHYMODE FIELD32(0xc0000000)
+
+/*
+ * TX_AGG_CNT: Debug counter
+ */
+#define TX_AGG_CNT 0x171c
+#define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT0:
+ */
+#define TX_AGG_CNT0 0x1720
+#define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT1:
+ */
+#define TX_AGG_CNT1 0x1724
+#define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT2:
+ */
+#define TX_AGG_CNT2 0x1728
+#define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT3:
+ */
+#define TX_AGG_CNT3 0x172c
+#define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT4:
+ */
+#define TX_AGG_CNT4 0x1730
+#define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT5:
+ */
+#define TX_AGG_CNT5 0x1734
+#define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT6:
+ */
+#define TX_AGG_CNT6 0x1738
+#define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000)
+
+/*
+ * TX_AGG_CNT7:
+ */
+#define TX_AGG_CNT7 0x173c
+#define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff)
+#define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000)
+
+/*
+ * MPDU_DENSITY_CNT:
+ * TX_ZERO_DEL: TX zero length delimiter count
+ * RX_ZERO_DEL: RX zero length delimiter count
+ */
+#define MPDU_DENSITY_CNT 0x1740
+#define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff)
+#define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000)
+
+/*
+ * Security key table memory.
+ * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry
+ * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry
+ * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry
+ * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry
+ * SHARED_KEY_TABLE_BASE: 32-byte * 16-entry
+ * SHARED_KEY_MODE_BASE: 4-byte * 16-entry
+ */
+#define MAC_WCID_BASE 0x1800
+#define PAIRWISE_KEY_TABLE_BASE 0x4000
+#define MAC_IVEIV_TABLE_BASE 0x6000
+#define MAC_WCID_ATTRIBUTE_BASE 0x6800
+#define SHARED_KEY_TABLE_BASE 0x6c00
+#define SHARED_KEY_MODE_BASE 0x7000
+
+#define MAC_WCID_ENTRY(__idx) \
+ ( MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry)) )
+#define PAIRWISE_KEY_ENTRY(__idx) \
+ ( PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) )
+#define MAC_IVEIV_ENTRY(__idx) \
+ ( MAC_IVEIV_TABLE_BASE + ((__idx) & sizeof(struct mac_iveiv_entry)) )
+#define MAC_WCID_ATTR_ENTRY(__idx) \
+ ( MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32)) )
+#define SHARED_KEY_ENTRY(__idx) \
+ ( SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) )
+#define SHARED_KEY_MODE_ENTRY(__idx) \
+ ( SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32)) )
+
+struct mac_wcid_entry {
+ u8 mac[6];
+ u8 reserved[2];
+} __attribute__ ((packed));
+
+struct hw_key_entry {
+ u8 key[16];
+ u8 tx_mic[8];
+ u8 rx_mic[8];
+} __attribute__ ((packed));
+
+struct mac_iveiv_entry {
+ u8 iv[8];
+} __attribute__ ((packed));
+
+/*
+ * MAC_WCID_ATTRIBUTE:
+ */
+#define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001)
+#define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e)
+#define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070)
+#define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380)
+
+/*
+ * SHARED_KEY_MODE:
+ */
+#define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007)
+#define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070)
+#define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700)
+#define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000)
+#define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000)
+#define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000)
+#define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000)
+#define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000)
+
+/*
+ * HOST-MCU communication
+ */
+
+/*
+ * H2M_MAILBOX_CSR: Host-to-MCU Mailbox.
+ */
+#define H2M_MAILBOX_CSR 0x7010
+#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff)
+#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00)
+#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000)
+#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000)
+
+/*
+ * H2M_MAILBOX_CID:
+ */
+#define H2M_MAILBOX_CID 0x7014
+#define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff)
+#define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00)
+#define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000)
+#define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000)
+
+/*
+ * H2M_MAILBOX_STATUS:
+ */
+#define H2M_MAILBOX_STATUS 0x701c
+
+/*
+ * H2M_INT_SRC:
+ */
+#define H2M_INT_SRC 0x7024
+
+/*
+ * H2M_BBP_AGENT:
+ */
+#define H2M_BBP_AGENT 0x7028
+
+/*
+ * MCU_LEDCS: LED control for MCU Mailbox.
+ */
+#define MCU_LEDCS_LED_MODE FIELD8(0x1f)
+#define MCU_LEDCS_POLARITY FIELD8(0x01)
+
+/*
+ * HW_CS_CTS_BASE:
+ * Carrier-sense CTS frame base address.
+ * It's where mac stores carrier-sense frame for carrier-sense function.
+ */
+#define HW_CS_CTS_BASE 0x7700
+
+/*
+ * HW_DFS_CTS_BASE:
+ * DFS CTS frame base address. It's where mac stores CTS frame for DFS.
+ */
+#define HW_DFS_CTS_BASE 0x7780
+
+/*
+ * TXRX control registers - base address 0x3000
+ */
+
+/*
+ * TXRX_CSR1:
+ * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first..
+ */
+#define TXRX_CSR1 0x77d0
+
+/*
+ * HW_DEBUG_SETTING_BASE:
+ * since NULL frame won't be that long (256 byte)
+ * We steal 16 tail bytes to save debugging settings
+ */
+#define HW_DEBUG_SETTING_BASE 0x77f0
+#define HW_DEBUG_SETTING_BASE2 0x7770
+
+/*
+ * HW_BEACON_BASE
+ * In order to support maximum 8 MBSS and its maximum length
+ * is 512 bytes for each beacon
+ * Three section discontinue memory segments will be used.
+ * 1. The original region for BCN 0~3
+ * 2. Extract memory from FCE table for BCN 4~5
+ * 3. Extract memory from Pair-wise key table for BCN 6~7
+ * It occupied those memory of wcid 238~253 for BCN 6
+ * and wcid 222~237 for BCN 7
+ *
+ * IMPORTANT NOTE: Not sure why legacy driver does this,
+ * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6.
+ */
+#define HW_BEACON_BASE0 0x7800
+#define HW_BEACON_BASE1 0x7a00
+#define HW_BEACON_BASE2 0x7c00
+#define HW_BEACON_BASE3 0x7e00
+#define HW_BEACON_BASE4 0x7200
+#define HW_BEACON_BASE5 0x7400
+#define HW_BEACON_BASE6 0x5dc0
+#define HW_BEACON_BASE7 0x5bc0
+
+#define HW_BEACON_OFFSET(__index) \
+ ( ((__index) < 4) ? ( HW_BEACON_BASE0 + (__index * 0x0200) ) : \
+ (((__index) < 6) ? ( HW_BEACON_BASE4 + ((__index - 4) * 0x0200) ) : \
+ (HW_BEACON_BASE6 - ((__index - 6) * 0x0200))) )
+
+/*
+ * BBP registers.
+ * The wordsize of the BBP is 8 bits.
+ */
+
+/*
+ * BBP 1: TX Antenna
+ */
+#define BBP1_TX_POWER FIELD8(0x07)
+#define BBP1_TX_ANTENNA FIELD8(0x18)
+
+/*
+ * BBP 3: RX Antenna
+ */
+#define BBP3_RX_ANTENNA FIELD8(0x18)
+#define BBP3_HT40_PLUS FIELD8(0x20)
+
+/*
+ * BBP 4: Bandwidth
+ */
+#define BBP4_TX_BF FIELD8(0x01)
+#define BBP4_BANDWIDTH FIELD8(0x18)
+
+/*
+ * RFCSR registers
+ * The wordsize of the RFCSR is 8 bits.
+ */
+
+/*
+ * RFCSR 6:
+ */
+#define RFCSR6_R FIELD8(0x03)
+
+/*
+ * RFCSR 7:
+ */
+#define RFCSR7_RF_TUNING FIELD8(0x01)
+
+/*
+ * RFCSR 12:
+ */
+#define RFCSR12_TX_POWER FIELD8(0x1f)
+
+/*
+ * RFCSR 22:
+ */
+#define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01)
+
+/*
+ * RFCSR 23:
+ */
+#define RFCSR23_FREQ_OFFSET FIELD8(0x7f)
+
+/*
+ * RFCSR 30:
+ */
+#define RFCSR30_RF_CALIBRATION FIELD8(0x80)
+
+/*
+ * RF registers
+ */
+
+/*
+ * RF 2
+ */
+#define RF2_ANTENNA_RX2 FIELD32(0x00000040)
+#define RF2_ANTENNA_TX1 FIELD32(0x00004000)
+#define RF2_ANTENNA_RX1 FIELD32(0x00020000)
+
+/*
+ * RF 3
+ */
+#define RF3_TXPOWER_G FIELD32(0x00003e00)
+#define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200)
+#define RF3_TXPOWER_A FIELD32(0x00003c00)
+
+/*
+ * RF 4
+ */
+#define RF4_TXPOWER_G FIELD32(0x000007c0)
+#define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040)
+#define RF4_TXPOWER_A FIELD32(0x00000780)
+#define RF4_FREQ_OFFSET FIELD32(0x001f8000)
+#define RF4_HT40 FIELD32(0x00200000)
+
+/*
+ * EEPROM content.
+ * The wordsize of the EEPROM is 16 bits.
+ */
+
+/*
+ * EEPROM Version
+ */
+#define EEPROM_VERSION 0x0001
+#define EEPROM_VERSION_FAE FIELD16(0x00ff)
+#define EEPROM_VERSION_VERSION FIELD16(0xff00)
+
+/*
+ * HW MAC address.
+ */
+#define EEPROM_MAC_ADDR_0 0x0002
+#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff)
+#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00)
+#define EEPROM_MAC_ADDR_1 0x0003
+#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff)
+#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00)
+#define EEPROM_MAC_ADDR_2 0x0004
+#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff)
+#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00)
+
+/*
+ * EEPROM ANTENNA config
+ * RXPATH: 1: 1R, 2: 2R, 3: 3R
+ * TXPATH: 1: 1T, 2: 2T
+ */
+#define EEPROM_ANTENNA 0x001a
+#define EEPROM_ANTENNA_RXPATH FIELD16(0x000f)
+#define EEPROM_ANTENNA_TXPATH FIELD16(0x00f0)
+#define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0f00)
+
+/*
+ * EEPROM NIC config
+ * CARDBUS_ACCEL: 0 - enable, 1 - disable
+ */
+#define EEPROM_NIC 0x001b
+#define EEPROM_NIC_HW_RADIO FIELD16(0x0001)
+#define EEPROM_NIC_DYNAMIC_TX_AGC FIELD16(0x0002)
+#define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0004)
+#define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0008)
+#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0010)
+#define EEPROM_NIC_BW40M_SB_BG FIELD16(0x0020)
+#define EEPROM_NIC_BW40M_SB_A FIELD16(0x0040)
+#define EEPROM_NIC_WPS_PBC FIELD16(0x0080)
+#define EEPROM_NIC_BW40M_BG FIELD16(0x0100)
+#define EEPROM_NIC_BW40M_A FIELD16(0x0200)
+
+/*
+ * EEPROM frequency
+ */
+#define EEPROM_FREQ 0x001d
+#define EEPROM_FREQ_OFFSET FIELD16(0x00ff)
+#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00)
+#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000)
+
+/*
+ * EEPROM LED
+ * POLARITY_RDY_G: Polarity RDY_G setting.
+ * POLARITY_RDY_A: Polarity RDY_A setting.
+ * POLARITY_ACT: Polarity ACT setting.
+ * POLARITY_GPIO_0: Polarity GPIO0 setting.
+ * POLARITY_GPIO_1: Polarity GPIO1 setting.
+ * POLARITY_GPIO_2: Polarity GPIO2 setting.
+ * POLARITY_GPIO_3: Polarity GPIO3 setting.
+ * POLARITY_GPIO_4: Polarity GPIO4 setting.
+ * LED_MODE: Led mode.
+ */
+#define EEPROM_LED1 0x001e
+#define EEPROM_LED2 0x001f
+#define EEPROM_LED3 0x0020
+#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001)
+#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002)
+#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004)
+#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008)
+#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010)
+#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020)
+#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040)
+#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080)
+#define EEPROM_LED_LED_MODE FIELD16(0x1f00)
+
+/*
+ * EEPROM LNA
+ */
+#define EEPROM_LNA 0x0022
+#define EEPROM_LNA_BG FIELD16(0x00ff)
+#define EEPROM_LNA_A0 FIELD16(0xff00)
+
+/*
+ * EEPROM RSSI BG offset
+ */
+#define EEPROM_RSSI_BG 0x0023
+#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff)
+#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00)
+
+/*
+ * EEPROM RSSI BG2 offset
+ */
+#define EEPROM_RSSI_BG2 0x0024
+#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff)
+#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00)
+
+/*
+ * EEPROM RSSI A offset
+ */
+#define EEPROM_RSSI_A 0x0025
+#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff)
+#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00)
+
+/*
+ * EEPROM RSSI A2 offset
+ */
+#define EEPROM_RSSI_A2 0x0026
+#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff)
+#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00)
+
+/*
+ * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power.
+ * This is delta in 40MHZ.
+ * VALUE: Tx Power dalta value (MAX=4)
+ * TYPE: 1: Plus the delta value, 0: minus the delta value
+ * TXPOWER: Enable:
+ */
+#define EEPROM_TXPOWER_DELTA 0x0028
+#define EEPROM_TXPOWER_DELTA_VALUE FIELD16(0x003f)
+#define EEPROM_TXPOWER_DELTA_TYPE FIELD16(0x0040)
+#define EEPROM_TXPOWER_DELTA_TXPOWER FIELD16(0x0080)
+
+/*
+ * EEPROM TXPOWER 802.11BG
+ */
+#define EEPROM_TXPOWER_BG1 0x0029
+#define EEPROM_TXPOWER_BG2 0x0030
+#define EEPROM_TXPOWER_BG_SIZE 7
+#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff)
+#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00)
+
+/*
+ * EEPROM TXPOWER 802.11A
+ */
+#define EEPROM_TXPOWER_A1 0x003c
+#define EEPROM_TXPOWER_A2 0x0053
+#define EEPROM_TXPOWER_A_SIZE 6
+#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff)
+#define EEPROM_TXPOWER_A_2 FIELD16(0xff00)
+
+/*
+ * EEPROM TXpower byrate: 20MHZ power
+ */
+#define EEPROM_TXPOWER_BYRATE 0x006f
+
+/*
+ * EEPROM BBP.
+ */
+#define EEPROM_BBP_START 0x0078
+#define EEPROM_BBP_SIZE 16
+#define EEPROM_BBP_VALUE FIELD16(0x00ff)
+#define EEPROM_BBP_REG_ID FIELD16(0xff00)
+
+/*
+ * MCU mailbox commands.
+ */
+#define MCU_SLEEP 0x30
+#define MCU_WAKEUP 0x31
+#define MCU_RADIO_OFF 0x35
+#define MCU_CURRENT 0x36
+#define MCU_LED 0x50
+#define MCU_LED_STRENGTH 0x51
+#define MCU_LED_1 0x52
+#define MCU_LED_2 0x53
+#define MCU_LED_3 0x54
+#define MCU_RADAR 0x60
+#define MCU_BOOT_SIGNAL 0x72
+#define MCU_BBP_SIGNAL 0x80
+#define MCU_POWER_SAVE 0x83
+
+/*
+ * MCU mailbox tokens
+ */
+#define TOKEN_WAKUP 3
+
+/*
+ * DMA descriptor defines.
+ */
+#define TXWI_DESC_SIZE ( 4 * sizeof(__le32) )
+#define RXWI_DESC_SIZE ( 4 * sizeof(__le32) )
+
+/*
+ * TX WI structure
+ */
+
+/*
+ * Word0
+ * FRAG: 1 To inform TKIP engine this is a fragment.
+ * MIMO_PS: The remote peer is in dynamic MIMO-PS mode
+ * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs
+ * BW: Channel bandwidth 20MHz or 40 MHz
+ * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED
+ */
+#define TXWI_W0_FRAG FIELD32(0x00000001)
+#define TXWI_W0_MIMO_PS FIELD32(0x00000002)
+#define TXWI_W0_CF_ACK FIELD32(0x00000004)
+#define TXWI_W0_TS FIELD32(0x00000008)
+#define TXWI_W0_AMPDU FIELD32(0x00000010)
+#define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0)
+#define TXWI_W0_TX_OP FIELD32(0x00000300)
+#define TXWI_W0_MCS FIELD32(0x007f0000)
+#define TXWI_W0_BW FIELD32(0x00800000)
+#define TXWI_W0_SHORT_GI FIELD32(0x01000000)
+#define TXWI_W0_STBC FIELD32(0x06000000)
+#define TXWI_W0_IFS FIELD32(0x08000000)
+#define TXWI_W0_PHYMODE FIELD32(0xc0000000)
+
+/*
+ * Word1
+ */
+#define TXWI_W1_ACK FIELD32(0x00000001)
+#define TXWI_W1_NSEQ FIELD32(0x00000002)
+#define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc)
+#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00)
+#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000)
+#define TXWI_W1_PACKETID FIELD32(0xf0000000)
+
+/*
+ * Word2
+ */
+#define TXWI_W2_IV FIELD32(0xffffffff)
+
+/*
+ * Word3
+ */
+#define TXWI_W3_EIV FIELD32(0xffffffff)
+
+/*
+ * RX WI structure
+ */
+
+/*
+ * Word0
+ */
+#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff)
+#define RXWI_W0_KEY_INDEX FIELD32(0x00000300)
+#define RXWI_W0_BSSID FIELD32(0x00001c00)
+#define RXWI_W0_UDF FIELD32(0x0000e000)
+#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000)
+#define RXWI_W0_TID FIELD32(0xf0000000)
+
+/*
+ * Word1
+ */
+#define RXWI_W1_FRAG FIELD32(0x0000000f)
+#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0)
+#define RXWI_W1_MCS FIELD32(0x007f0000)
+#define RXWI_W1_BW FIELD32(0x00800000)
+#define RXWI_W1_SHORT_GI FIELD32(0x01000000)
+#define RXWI_W1_STBC FIELD32(0x06000000)
+#define RXWI_W1_PHYMODE FIELD32(0xc0000000)
+
+/*
+ * Word2
+ */
+#define RXWI_W2_RSSI0 FIELD32(0x000000ff)
+#define RXWI_W2_RSSI1 FIELD32(0x0000ff00)
+#define RXWI_W2_RSSI2 FIELD32(0x00ff0000)
+
+/*
+ * Word3
+ */
+#define RXWI_W3_SNR0 FIELD32(0x000000ff)
+#define RXWI_W3_SNR1 FIELD32(0x0000ff00)
+
+/*
+ * Macros for converting txpower from EEPROM to mac80211 value
+ * and from mac80211 value to register value.
+ */
+#define MIN_G_TXPOWER 0
+#define MIN_A_TXPOWER -7
+#define MAX_G_TXPOWER 31
+#define MAX_A_TXPOWER 15
+#define DEFAULT_TXPOWER 5
+
+#define TXPOWER_G_FROM_DEV(__txpower) \
+ ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
+
+#define TXPOWER_G_TO_DEV(__txpower) \
+ clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER)
+
+#define TXPOWER_A_FROM_DEV(__txpower) \
+ ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
+
+#define TXPOWER_A_TO_DEV(__txpower) \
+ clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER)
+
+#endif /* RT2800_H */
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
new file mode 100644
index 00000000000..eb1e1d00bec
--- /dev/null
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -0,0 +1,2284 @@
+/*
+ Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
+ Copyright (C) 2009 Gertjan van Wingerde <gwingerde@gmail.com>
+
+ Based on the original rt2800pci.c and rt2800usb.c.
+ Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ Module: rt2800lib
+ Abstract: rt2800 generic device routines.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "rt2x00.h"
+#ifdef CONFIG_RT2800USB
+#include "rt2x00usb.h"
+#endif
+#include "rt2800lib.h"
+#include "rt2800.h"
+#include "rt2800usb.h"
+
+MODULE_AUTHOR("Bartlomiej Zolnierkiewicz");
+MODULE_DESCRIPTION("rt2800 library");
+MODULE_LICENSE("GPL");
+
+/*
+ * Register access.
+ * All access to the CSR registers will go through the methods
+ * rt2800_register_read and rt2800_register_write.
+ * BBP and RF register require indirect register access,
+ * and use the CSR registers BBPCSR and RFCSR to achieve this.
+ * These indirect registers work with busy bits,
+ * and we will try maximal REGISTER_BUSY_COUNT times to access
+ * the register while taking a REGISTER_BUSY_DELAY us delay
+ * between each attampt. When the busy bit is still set at that time,
+ * the access attempt is considered to have failed,
+ * and we will print an error.
+ * The _lock versions must be used if you already hold the csr_mutex
+ */
+#define WAIT_FOR_BBP(__dev, __reg) \
+ rt2800_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg))
+#define WAIT_FOR_RFCSR(__dev, __reg) \
+ rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg))
+#define WAIT_FOR_RF(__dev, __reg) \
+ rt2800_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg))
+#define WAIT_FOR_MCU(__dev, __reg) \
+ rt2800_regbusy_read((__dev), H2M_MAILBOX_CSR, \
+ H2M_MAILBOX_CSR_OWNER, (__reg))
+
+static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, const u8 value)
+{
+ u32 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_VALUE, value);
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_REGNUM, word);
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_BUSY, 1);
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_READ_CONTROL, 0);
+ if (rt2x00_intf_is_pci(rt2x00dev))
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_BBP_RW_MODE, 1);
+
+ rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg);
+ }
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+static void rt2800_bbp_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, u8 *value)
+{
+ u32 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the BBP becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
+ */
+ if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_REGNUM, word);
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_BUSY, 1);
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_READ_CONTROL, 1);
+ if (rt2x00_intf_is_pci(rt2x00dev))
+ rt2x00_set_field32(&reg, BBP_CSR_CFG_BBP_RW_MODE, 1);
+
+ rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg);
+
+ WAIT_FOR_BBP(rt2x00dev, &reg);
+ }
+
+ *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE);
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, const u8 value)
+{
+ u32 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the RFCSR becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_RFCSR(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, RF_CSR_CFG_DATA, value);
+ rt2x00_set_field32(&reg, RF_CSR_CFG_REGNUM, word);
+ rt2x00_set_field32(&reg, RF_CSR_CFG_WRITE, 1);
+ rt2x00_set_field32(&reg, RF_CSR_CFG_BUSY, 1);
+
+ rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg);
+ }
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, u8 *value)
+{
+ u32 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the RFCSR becomes available, afterwards we
+ * can safely write the read request into the register.
+ * After the data has been written, we wait until hardware
+ * returns the correct value, if at any time the register
+ * doesn't become available in time, reg will be 0xffffffff
+ * which means we return 0xff to the caller.
+ */
+ if (WAIT_FOR_RFCSR(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, RF_CSR_CFG_REGNUM, word);
+ rt2x00_set_field32(&reg, RF_CSR_CFG_WRITE, 0);
+ rt2x00_set_field32(&reg, RF_CSR_CFG_BUSY, 1);
+
+ rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg);
+
+ WAIT_FOR_RFCSR(rt2x00dev, &reg);
+ }
+
+ *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA);
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int word, const u32 value)
+{
+ u32 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the RF becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_RF(rt2x00dev, &reg)) {
+ reg = 0;
+ rt2x00_set_field32(&reg, RF_CSR_CFG0_REG_VALUE_BW, value);
+ rt2x00_set_field32(&reg, RF_CSR_CFG0_STANDBYMODE, 0);
+ rt2x00_set_field32(&reg, RF_CSR_CFG0_SEL, 0);
+ rt2x00_set_field32(&reg, RF_CSR_CFG0_BUSY, 1);
+
+ rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg);
+ rt2x00_rf_write(rt2x00dev, word, value);
+ }
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
+ const u8 command, const u8 token,
+ const u8 arg0, const u8 arg1)
+{
+ u32 reg;
+
+ /*
+ * RT2880 and RT3052 don't support MCU requests.
+ */
+ if (rt2x00_rt(&rt2x00dev->chip, RT2880) ||
+ rt2x00_rt(&rt2x00dev->chip, RT3052))
+ return;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ /*
+ * Wait until the MCU becomes available, afterwards we
+ * can safely write the new data into the register.
+ */
+ if (WAIT_FOR_MCU(rt2x00dev, &reg)) {
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_OWNER, 1);
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_CMD_TOKEN, token);
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG0, arg0);
+ rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG1, arg1);
+ rt2800_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg);
+
+ reg = 0;
+ rt2x00_set_field32(&reg, HOST_CMD_CSR_HOST_COMMAND, command);
+ rt2800_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg);
+ }
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+EXPORT_SYMBOL_GPL(rt2800_mcu_request);
+
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
+const struct rt2x00debug rt2800_rt2x00debug = {
+ .owner = THIS_MODULE,
+ .csr = {
+ .read = rt2800_register_read,
+ .write = rt2800_register_write,
+ .flags = RT2X00DEBUGFS_OFFSET,
+ .word_base = CSR_REG_BASE,
+ .word_size = sizeof(u32),
+ .word_count = CSR_REG_SIZE / sizeof(u32),
+ },
+ .eeprom = {
+ .read = rt2x00_eeprom_read,
+ .write = rt2x00_eeprom_write,
+ .word_base = EEPROM_BASE,
+ .word_size = sizeof(u16),
+ .word_count = EEPROM_SIZE / sizeof(u16),
+ },
+ .bbp = {
+ .read = rt2800_bbp_read,
+ .write = rt2800_bbp_write,
+ .word_base = BBP_BASE,
+ .word_size = sizeof(u8),
+ .word_count = BBP_SIZE / sizeof(u8),
+ },
+ .rf = {
+ .read = rt2x00_rf_read,
+ .write = rt2800_rf_write,
+ .word_base = RF_BASE,
+ .word_size = sizeof(u32),
+ .word_count = RF_SIZE / sizeof(u32),
+ },
+};
+EXPORT_SYMBOL_GPL(rt2800_rt2x00debug);
+#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
+
+int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+ return rt2x00_get_field32(reg, GPIO_CTRL_CFG_BIT2);
+}
+EXPORT_SYMBOL_GPL(rt2800_rfkill_poll);
+
+#ifdef CONFIG_RT2X00_LIB_LEDS
+static void rt2800_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct rt2x00_led *led =
+ container_of(led_cdev, struct rt2x00_led, led_dev);
+ unsigned int enabled = brightness != LED_OFF;
+ unsigned int bg_mode =
+ (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ);
+ unsigned int polarity =
+ rt2x00_get_field16(led->rt2x00dev->led_mcu_reg,
+ EEPROM_FREQ_LED_POLARITY);
+ unsigned int ledmode =
+ rt2x00_get_field16(led->rt2x00dev->led_mcu_reg,
+ EEPROM_FREQ_LED_MODE);
+
+ if (led->type == LED_TYPE_RADIO) {
+ rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode,
+ enabled ? 0x20 : 0);
+ } else if (led->type == LED_TYPE_ASSOC) {
+ rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode,
+ enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20);
+ } else if (led->type == LED_TYPE_QUALITY) {
+ /*
+ * The brightness is divided into 6 levels (0 - 5),
+ * The specs tell us the following levels:
+ * 0, 1 ,3, 7, 15, 31
+ * to determine the level in a simple way we can simply
+ * work with bitshifting:
+ * (1 << level) - 1
+ */
+ rt2800_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff,
+ (1 << brightness / (LED_FULL / 6)) - 1,
+ polarity);
+ }
+}
+
+static int rt2800_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct rt2x00_led *led =
+ container_of(led_cdev, struct rt2x00_led, led_dev);
+ u32 reg;
+
+ rt2800_register_read(led->rt2x00dev, LED_CFG, &reg);
+ rt2x00_set_field32(&reg, LED_CFG_ON_PERIOD, *delay_on);
+ rt2x00_set_field32(&reg, LED_CFG_OFF_PERIOD, *delay_off);
+ rt2x00_set_field32(&reg, LED_CFG_SLOW_BLINK_PERIOD, 3);
+ rt2x00_set_field32(&reg, LED_CFG_R_LED_MODE, 3);
+ rt2x00_set_field32(&reg, LED_CFG_G_LED_MODE, 12);
+ rt2x00_set_field32(&reg, LED_CFG_Y_LED_MODE, 3);
+ rt2x00_set_field32(&reg, LED_CFG_LED_POLAR, 1);
+ rt2800_register_write(led->rt2x00dev, LED_CFG, reg);
+
+ return 0;
+}
+
+void rt2800_init_led(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00_led *led, enum led_type type)
+{
+ led->rt2x00dev = rt2x00dev;
+ led->type = type;
+ led->led_dev.brightness_set = rt2800_brightness_set;
+ led->led_dev.blink_set = rt2800_blink_set;
+ led->flags = LED_INITIALIZED;
+}
+EXPORT_SYMBOL_GPL(rt2800_init_led);
+#endif /* CONFIG_RT2X00_LIB_LEDS */
+
+/*
+ * Configuration handlers.
+ */
+static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key)
+{
+ struct mac_wcid_entry wcid_entry;
+ struct mac_iveiv_entry iveiv_entry;
+ u32 offset;
+ u32 reg;
+
+ offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx);
+
+ rt2800_register_read(rt2x00dev, offset, &reg);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_KEYTAB,
+ !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE));
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER,
+ (crypto->cmd == SET_KEY) * crypto->cipher);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX,
+ (crypto->cmd == SET_KEY) * crypto->bssidx);
+ rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher);
+ rt2800_register_write(rt2x00dev, offset, reg);
+
+ offset = MAC_IVEIV_ENTRY(key->hw_key_idx);
+
+ memset(&iveiv_entry, 0, sizeof(iveiv_entry));
+ if ((crypto->cipher == CIPHER_TKIP) ||
+ (crypto->cipher == CIPHER_TKIP_NO_MIC) ||
+ (crypto->cipher == CIPHER_AES))
+ iveiv_entry.iv[3] |= 0x20;
+ iveiv_entry.iv[3] |= key->keyidx << 6;
+ rt2800_register_multiwrite(rt2x00dev, offset,
+ &iveiv_entry, sizeof(iveiv_entry));
+
+ offset = MAC_WCID_ENTRY(key->hw_key_idx);
+
+ memset(&wcid_entry, 0, sizeof(wcid_entry));
+ if (crypto->cmd == SET_KEY)
+ memcpy(&wcid_entry, crypto->address, ETH_ALEN);
+ rt2800_register_multiwrite(rt2x00dev, offset,
+ &wcid_entry, sizeof(wcid_entry));
+}
+
+int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key)
+{
+ struct hw_key_entry key_entry;
+ struct rt2x00_field32 field;
+ u32 offset;
+ u32 reg;
+
+ if (crypto->cmd == SET_KEY) {
+ key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx;
+
+ memcpy(key_entry.key, crypto->key,
+ sizeof(key_entry.key));
+ memcpy(key_entry.tx_mic, crypto->tx_mic,
+ sizeof(key_entry.tx_mic));
+ memcpy(key_entry.rx_mic, crypto->rx_mic,
+ sizeof(key_entry.rx_mic));
+
+ offset = SHARED_KEY_ENTRY(key->hw_key_idx);
+ rt2800_register_multiwrite(rt2x00dev, offset,
+ &key_entry, sizeof(key_entry));
+ }
+
+ /*
+ * The cipher types are stored over multiple registers
+ * starting with SHARED_KEY_MODE_BASE each word will have
+ * 32 bits and contains the cipher types for 2 bssidx each.
+ * Using the correct defines correctly will cause overhead,
+ * so just calculate the correct offset.
+ */
+ field.bit_offset = 4 * (key->hw_key_idx % 8);
+ field.bit_mask = 0x7 << field.bit_offset;
+
+ offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8);
+
+ rt2800_register_read(rt2x00dev, offset, &reg);
+ rt2x00_set_field32(&reg, field,
+ (crypto->cmd == SET_KEY) * crypto->cipher);
+ rt2800_register_write(rt2x00dev, offset, reg);
+
+ /*
+ * Update WCID information
+ */
+ rt2800_config_wcid_attr(rt2x00dev, crypto, key);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_config_shared_key);
+
+int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key)
+{
+ struct hw_key_entry key_entry;
+ u32 offset;
+
+ if (crypto->cmd == SET_KEY) {
+ /*
+ * 1 pairwise key is possible per AID, this means that the AID
+ * equals our hw_key_idx. Make sure the WCID starts _after_ the
+ * last possible shared key entry.
+ */
+ if (crypto->aid > (256 - 32))
+ return -ENOSPC;
+
+ key->hw_key_idx = 32 + crypto->aid;
+
+ memcpy(key_entry.key, crypto->key,
+ sizeof(key_entry.key));
+ memcpy(key_entry.tx_mic, crypto->tx_mic,
+ sizeof(key_entry.tx_mic));
+ memcpy(key_entry.rx_mic, crypto->rx_mic,
+ sizeof(key_entry.rx_mic));
+
+ offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx);
+ rt2800_register_multiwrite(rt2x00dev, offset,
+ &key_entry, sizeof(key_entry));
+ }
+
+ /*
+ * Update WCID information
+ */
+ rt2800_config_wcid_attr(rt2x00dev, crypto, key);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key);
+
+void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
+ const unsigned int filter_flags)
+{
+ u32 reg;
+
+ /*
+ * Start configuration steps.
+ * Note that the version error will always be dropped
+ * and broadcast frames will always be accepted since
+ * there is no filter for it at this time.
+ */
+ rt2800_register_read(rt2x00dev, RX_FILTER_CFG, &reg);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CRC_ERROR,
+ !(filter_flags & FIF_FCSFAIL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_PHY_ERROR,
+ !(filter_flags & FIF_PLCPFAIL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_NOT_TO_ME,
+ !(filter_flags & FIF_PROMISC_IN_BSS));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_VER_ERROR, 1);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_MULTICAST,
+ !(filter_flags & FIF_ALLMULTI));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BROADCAST, 0);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_DUPLICATE, 1);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CF_END_ACK,
+ !(filter_flags & FIF_CONTROL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CF_END,
+ !(filter_flags & FIF_CONTROL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_ACK,
+ !(filter_flags & FIF_CONTROL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CTS,
+ !(filter_flags & FIF_CONTROL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_RTS,
+ !(filter_flags & FIF_CONTROL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_PSPOLL,
+ !(filter_flags & FIF_PSPOLL));
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BA, 1);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BAR, 0);
+ rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CNTL,
+ !(filter_flags & FIF_CONTROL));
+ rt2800_register_write(rt2x00dev, RX_FILTER_CFG, reg);
+}
+EXPORT_SYMBOL_GPL(rt2800_config_filter);
+
+void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
+ struct rt2x00intf_conf *conf, const unsigned int flags)
+{
+ unsigned int beacon_base;
+ u32 reg;
+
+ if (flags & CONFIG_UPDATE_TYPE) {
+ /*
+ * Clear current synchronisation setup.
+ * For the Beacon base registers we only need to clear
+ * the first byte since that byte contains the VALID and OWNER
+ * bits which (when set to 0) will invalidate the entire beacon.
+ */
+ beacon_base = HW_BEACON_OFFSET(intf->beacon->entry_idx);
+ rt2800_register_write(rt2x00dev, beacon_base, 0);
+
+ /*
+ * Enable synchronisation.
+ */
+ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, conf->sync);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE,
+ (conf->sync == TSF_SYNC_BEACON));
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+ }
+
+ if (flags & CONFIG_UPDATE_MAC) {
+ reg = le32_to_cpu(conf->mac[1]);
+ rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
+ conf->mac[1] = cpu_to_le32(reg);
+
+ rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0,
+ conf->mac, sizeof(conf->mac));
+ }
+
+ if (flags & CONFIG_UPDATE_BSSID) {
+ reg = le32_to_cpu(conf->bssid[1]);
+ rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 0);
+ rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 0);
+ conf->bssid[1] = cpu_to_le32(reg);
+
+ rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0,
+ conf->bssid, sizeof(conf->bssid));
+ }
+}
+EXPORT_SYMBOL_GPL(rt2800_config_intf);
+
+void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp)
+{
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, &reg);
+ rt2x00_set_field32(&reg, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 0x20);
+ rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
+ rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
+ !!erp->short_preamble);
+ rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
+ !!erp->short_preamble);
+ rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
+ erp->cts_protection ? 2 : 0);
+ rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+
+ rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE,
+ erp->basic_rates);
+ rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+
+ rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
+ rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time);
+ rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2);
+ rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
+ rt2x00_set_field32(&reg, XIFS_TIME_CFG_CCKM_SIFS_TIME, erp->sifs);
+ rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_SIFS_TIME, erp->sifs);
+ rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4);
+ rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
+ rt2x00_set_field32(&reg, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1);
+ rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
+ erp->beacon_int * 16);
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+}
+EXPORT_SYMBOL_GPL(rt2800_config_erp);
+
+void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
+{
+ u8 r1;
+ u8 r3;
+
+ rt2800_bbp_read(rt2x00dev, 1, &r1);
+ rt2800_bbp_read(rt2x00dev, 3, &r3);
+
+ /*
+ * Configure the TX antenna.
+ */
+ switch ((int)ant->tx) {
+ case 1:
+ rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0);
+ if (rt2x00_intf_is_pci(rt2x00dev))
+ rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0);
+ break;
+ case 2:
+ rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
+ break;
+ case 3:
+ /* Do nothing */
+ break;
+ }
+
+ /*
+ * Configure the RX antenna.
+ */
+ switch ((int)ant->rx) {
+ case 1:
+ rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0);
+ break;
+ case 2:
+ rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1);
+ break;
+ case 3:
+ rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2);
+ break;
+ }
+
+ rt2800_bbp_write(rt2x00dev, 3, r3);
+ rt2800_bbp_write(rt2x00dev, 1, r1);
+}
+EXPORT_SYMBOL_GPL(rt2800_config_ant);
+
+static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
+ u16 eeprom;
+ short lna_gain;
+
+ if (libconf->rf.channel <= 14) {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG);
+ } else if (libconf->rf.channel <= 64) {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0);
+ } else if (libconf->rf.channel <= 128) {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1);
+ } else {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2);
+ }
+
+ rt2x00dev->lna_gain = lna_gain;
+}
+
+static void rt2800_config_channel_rt2x(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_conf *conf,
+ struct rf_channel *rf,
+ struct channel_info *info)
+{
+ rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
+
+ if (rt2x00dev->default_ant.tx == 1)
+ rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1);
+
+ if (rt2x00dev->default_ant.rx == 1) {
+ rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1);
+ rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1);
+ } else if (rt2x00dev->default_ant.rx == 2)
+ rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1);
+
+ if (rf->channel > 14) {
+ /*
+ * When TX power is below 0, we should increase it by 7 to
+ * make it a positive value (Minumum value is -7).
+ * However this means that values between 0 and 7 have
+ * double meaning, and we should set a 7DBm boost flag.
+ */
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST,
+ (info->tx_power1 >= 0));
+
+ if (info->tx_power1 < 0)
+ info->tx_power1 += 7;
+
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A,
+ TXPOWER_A_TO_DEV(info->tx_power1));
+
+ rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST,
+ (info->tx_power2 >= 0));
+
+ if (info->tx_power2 < 0)
+ info->tx_power2 += 7;
+
+ rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A,
+ TXPOWER_A_TO_DEV(info->tx_power2));
+ } else {
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G,
+ TXPOWER_G_TO_DEV(info->tx_power1));
+ rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G,
+ TXPOWER_G_TO_DEV(info->tx_power2));
+ }
+
+ rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf));
+
+ rt2800_rf_write(rt2x00dev, 1, rf->rf1);
+ rt2800_rf_write(rt2x00dev, 2, rf->rf2);
+ rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
+ rt2800_rf_write(rt2x00dev, 4, rf->rf4);
+
+ udelay(200);
+
+ rt2800_rf_write(rt2x00dev, 1, rf->rf1);
+ rt2800_rf_write(rt2x00dev, 2, rf->rf2);
+ rt2800_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004);
+ rt2800_rf_write(rt2x00dev, 4, rf->rf4);
+
+ udelay(200);
+
+ rt2800_rf_write(rt2x00dev, 1, rf->rf1);
+ rt2800_rf_write(rt2x00dev, 2, rf->rf2);
+ rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
+ rt2800_rf_write(rt2x00dev, 4, rf->rf4);
+}
+
+static void rt2800_config_channel_rt3x(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_conf *conf,
+ struct rf_channel *rf,
+ struct channel_info *info)
+{
+ u8 rfcsr;
+
+ rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1);
+ rt2800_rfcsr_write(rt2x00dev, 3, rf->rf3);
+
+ rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR6_R, rf->rf2);
+ rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER,
+ TXPOWER_G_TO_DEV(info->tx_power1));
+ rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
+ rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
+
+ rt2800_rfcsr_write(rt2x00dev, 24,
+ rt2x00dev->calibration[conf_is_ht40(conf)]);
+
+ rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1);
+ rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
+}
+
+static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_conf *conf,
+ struct rf_channel *rf,
+ struct channel_info *info)
+{
+ u32 reg;
+ unsigned int tx_pin;
+ u8 bbp;
+
+ if ((rt2x00_rt(&rt2x00dev->chip, RT3070) ||
+ rt2x00_rt(&rt2x00dev->chip, RT3090)) &&
+ (rt2x00_rf(&rt2x00dev->chip, RF2020) ||
+ rt2x00_rf(&rt2x00dev->chip, RF3020) ||
+ rt2x00_rf(&rt2x00dev->chip, RF3021) ||
+ rt2x00_rf(&rt2x00dev->chip, RF3022)))
+ rt2800_config_channel_rt3x(rt2x00dev, conf, rf, info);
+ else
+ rt2800_config_channel_rt2x(rt2x00dev, conf, rf, info);
+
+ /*
+ * Change BBP settings
+ */
+ rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
+ rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
+ rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain);
+ rt2800_bbp_write(rt2x00dev, 86, 0);
+
+ if (rf->channel <= 14) {
+ if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) {
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
+ } else {
+ rt2800_bbp_write(rt2x00dev, 82, 0x84);
+ rt2800_bbp_write(rt2x00dev, 75, 0x50);
+ }
+ } else {
+ rt2800_bbp_write(rt2x00dev, 82, 0xf2);
+
+ if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags))
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
+ else
+ rt2800_bbp_write(rt2x00dev, 75, 0x50);
+ }
+
+ rt2800_register_read(rt2x00dev, TX_BAND_CFG, &reg);
+ rt2x00_set_field32(&reg, TX_BAND_CFG_HT40_PLUS, conf_is_ht40_plus(conf));
+ rt2x00_set_field32(&reg, TX_BAND_CFG_A, rf->channel > 14);
+ rt2x00_set_field32(&reg, TX_BAND_CFG_BG, rf->channel <= 14);
+ rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg);
+
+ tx_pin = 0;
+
+ /* Turn on unused PA or LNA when not using 1T or 1R */
+ if (rt2x00dev->default_ant.tx != 1) {
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1);
+ }
+
+ /* Turn on unused PA or LNA when not using 1T or 1R */
+ if (rt2x00dev->default_ant.rx != 1) {
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1);
+ }
+
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, rf->channel <= 14);
+ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14);
+
+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin);
+
+ rt2800_bbp_read(rt2x00dev, 4, &bbp);
+ rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf));
+ rt2800_bbp_write(rt2x00dev, 4, bbp);
+
+ rt2800_bbp_read(rt2x00dev, 3, &bbp);
+ rt2x00_set_field8(&bbp, BBP3_HT40_PLUS, conf_is_ht40_plus(conf));
+ rt2800_bbp_write(rt2x00dev, 3, bbp);
+
+ if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) {
+ if (conf_is_ht40(conf)) {
+ rt2800_bbp_write(rt2x00dev, 69, 0x1a);
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+ rt2800_bbp_write(rt2x00dev, 73, 0x16);
+ } else {
+ rt2800_bbp_write(rt2x00dev, 69, 0x16);
+ rt2800_bbp_write(rt2x00dev, 70, 0x08);
+ rt2800_bbp_write(rt2x00dev, 73, 0x11);
+ }
+ }
+
+ msleep(1);
+}
+
+static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
+ const int txpower)
+{
+ u32 reg;
+ u32 value = TXPOWER_G_TO_DEV(txpower);
+ u8 r1;
+
+ rt2800_bbp_read(rt2x00dev, 1, &r1);
+ rt2x00_set_field8(&reg, BBP1_TX_POWER, 0);
+ rt2800_bbp_write(rt2x00dev, 1, r1);
+
+ rt2800_register_read(rt2x00dev, TX_PWR_CFG_0, &reg);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_1MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_2MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_55MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_11MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_6MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_9MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_12MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_0_18MBS, value);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, reg);
+
+ rt2800_register_read(rt2x00dev, TX_PWR_CFG_1, &reg);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_24MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_36MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_48MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_54MBS, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS0, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS1, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS2, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS3, value);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, reg);
+
+ rt2800_register_read(rt2x00dev, TX_PWR_CFG_2, &reg);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS4, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS5, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS6, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS7, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS8, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS9, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS10, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS11, value);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, reg);
+
+ rt2800_register_read(rt2x00dev, TX_PWR_CFG_3, &reg);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS12, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS13, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS14, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS15, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN1, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN2, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN3, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN4, value);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, reg);
+
+ rt2800_register_read(rt2x00dev, TX_PWR_CFG_4, &reg);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN5, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN6, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN7, value);
+ rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN8, value);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, reg);
+}
+
+static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, TX_RTY_CFG, &reg);
+ rt2x00_set_field32(&reg, TX_RTY_CFG_SHORT_RTY_LIMIT,
+ libconf->conf->short_frame_max_tx_count);
+ rt2x00_set_field32(&reg, TX_RTY_CFG_LONG_RTY_LIMIT,
+ libconf->conf->long_frame_max_tx_count);
+ rt2x00_set_field32(&reg, TX_RTY_CFG_LONG_RTY_THRE, 2000);
+ rt2x00_set_field32(&reg, TX_RTY_CFG_NON_AGG_RTY_MODE, 0);
+ rt2x00_set_field32(&reg, TX_RTY_CFG_AGG_RTY_MODE, 0);
+ rt2x00_set_field32(&reg, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1);
+ rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg);
+}
+
+static void rt2800_config_ps(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf)
+{
+ enum dev_state state =
+ (libconf->conf->flags & IEEE80211_CONF_PS) ?
+ STATE_SLEEP : STATE_AWAKE;
+ u32 reg;
+
+ if (state == STATE_SLEEP) {
+ rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0);
+
+ rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, &reg);
+ rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5);
+ rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE,
+ libconf->conf->listen_interval - 1);
+ rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTOWAKE, 1);
+ rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg);
+
+ rt2x00dev->ops->lib->set_device_state(rt2x00dev, state);
+ } else {
+ rt2x00dev->ops->lib->set_device_state(rt2x00dev, state);
+
+ rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, &reg);
+ rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0);
+ rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0);
+ rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTOWAKE, 0);
+ rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg);
+ }
+}
+
+void rt2800_config(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf,
+ const unsigned int flags)
+{
+ /* Always recalculate LNA gain before changing configuration */
+ rt2800_config_lna_gain(rt2x00dev, libconf);
+
+ if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
+ rt2800_config_channel(rt2x00dev, libconf->conf,
+ &libconf->rf, &libconf->channel);
+ if (flags & IEEE80211_CONF_CHANGE_POWER)
+ rt2800_config_txpower(rt2x00dev, libconf->conf->power_level);
+ if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
+ rt2800_config_retry_limit(rt2x00dev, libconf);
+ if (flags & IEEE80211_CONF_CHANGE_PS)
+ rt2800_config_ps(rt2x00dev, libconf);
+}
+EXPORT_SYMBOL_GPL(rt2800_config);
+
+/*
+ * Link tuning
+ */
+void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual)
+{
+ u32 reg;
+
+ /*
+ * Update FCS error count from register.
+ */
+ rt2800_register_read(rt2x00dev, RX_STA_CNT0, &reg);
+ qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR);
+}
+EXPORT_SYMBOL_GPL(rt2800_link_stats);
+
+static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
+{
+ if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
+ if (rt2x00_intf_is_usb(rt2x00dev) &&
+ rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION)
+ return 0x1c + (2 * rt2x00dev->lna_gain);
+ else
+ return 0x2e + rt2x00dev->lna_gain;
+ }
+
+ if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
+ return 0x32 + (rt2x00dev->lna_gain * 5) / 3;
+ else
+ return 0x3a + (rt2x00dev->lna_gain * 5) / 3;
+}
+
+static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev,
+ struct link_qual *qual, u8 vgc_level)
+{
+ if (qual->vgc_level != vgc_level) {
+ rt2800_bbp_write(rt2x00dev, 66, vgc_level);
+ qual->vgc_level = vgc_level;
+ qual->vgc_level_reg = vgc_level;
+ }
+}
+
+void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual)
+{
+ rt2800_set_vgc(rt2x00dev, qual, rt2800_get_default_vgc(rt2x00dev));
+}
+EXPORT_SYMBOL_GPL(rt2800_reset_tuner);
+
+void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
+ const u32 count)
+{
+ if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION)
+ return;
+
+ /*
+ * When RSSI is better then -80 increase VGC level with 0x10
+ */
+ rt2800_set_vgc(rt2x00dev, qual,
+ rt2800_get_default_vgc(rt2x00dev) +
+ ((qual->rssi > -80) * 0x10));
+}
+EXPORT_SYMBOL_GPL(rt2800_link_tuner);
+
+/*
+ * Initialization functions.
+ */
+int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ unsigned int i;
+
+ if (rt2x00_intf_is_usb(rt2x00dev)) {
+ /*
+ * Wait until BBP and RF are ready.
+ */
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
+ if (reg && reg != ~0)
+ break;
+ msleep(1);
+ }
+
+ if (i == REGISTER_BUSY_COUNT) {
+ ERROR(rt2x00dev, "Unstable hardware.\n");
+ return -EBUSY;
+ }
+
+ rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL,
+ reg & ~0x00002000);
+ } else if (rt2x00_intf_is_pci(rt2x00dev))
+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
+
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+ if (rt2x00_intf_is_usb(rt2x00dev)) {
+ rt2800_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000);
+#ifdef CONFIG_RT2800USB
+ rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0,
+ USB_MODE_RESET, REGISTER_TIMEOUT);
+#endif
+ }
+
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
+
+ rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
+ rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */
+ rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */
+ rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */
+ rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */
+ rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
+
+ rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
+ rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */
+ rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */
+ rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */
+ rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */
+ rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
+
+ rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
+ rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
+
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
+
+ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL, 0);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, 0);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0);
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+ if (rt2x00_intf_is_usb(rt2x00dev) &&
+ rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) {
+ rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
+ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000);
+ rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
+ } else {
+ rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000);
+ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
+ }
+
+ rt2800_register_read(rt2x00dev, TX_LINK_CFG, &reg);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_MFB_ENABLE, 0);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_TX_MRQ_EN, 0);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_TX_RDG_EN, 0);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_TX_CF_ACK_EN, 1);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_MFB, 0);
+ rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_MFS, 0);
+ rt2800_register_write(rt2x00dev, TX_LINK_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, &reg);
+ rt2x00_set_field32(&reg, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9);
+ rt2x00_set_field32(&reg, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10);
+ rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MAX_LEN_CFG, &reg);
+ rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE);
+ if (rt2x00_rev(&rt2x00dev->chip) >= RT2880E_VERSION &&
+ rt2x00_rev(&rt2x00dev->chip) < RT3070_VERSION)
+ rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_PSDU, 2);
+ else
+ rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_PSDU, 1);
+ rt2x00_set_field32(&reg, MAX_LEN_CFG_MIN_PSDU, 0);
+ rt2x00_set_field32(&reg, MAX_LEN_CFG_MIN_MPDU, 0);
+ rt2800_register_write(rt2x00dev, MAX_LEN_CFG, reg);
+
+ rt2800_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f);
+
+ rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
+ rt2x00_set_field32(&reg, AUTO_RSP_CFG_AUTORESPONDER, 1);
+ rt2x00_set_field32(&reg, AUTO_RSP_CFG_CTS_40_MMODE, 0);
+ rt2x00_set_field32(&reg, AUTO_RSP_CFG_CTS_40_MREF, 0);
+ rt2x00_set_field32(&reg, AUTO_RSP_CFG_DUAL_CTS_EN, 0);
+ rt2x00_set_field32(&reg, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0);
+ rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, CCK_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_PROTECT_RATE, 8);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_PROTECT_CTRL, 0);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_PROTECT_NAV, 1);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 1);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 1);
+ rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_RATE, 8);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL, 0);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_NAV, 1);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 1);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 1);
+ rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MM20_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_RATE, 0x4004);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_CTRL, 0);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_NAV, 1);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0);
+ rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MM40_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_RATE, 0x4084);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_CTRL, 0);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_NAV, 1);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1);
+ rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, GF20_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_RATE, 0x4004);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_CTRL, 0);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_NAV, 1);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0);
+ rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, GF40_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_RATE, 0x4084);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_CTRL, 0);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_NAV, 1);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1);
+ rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg);
+
+ if (rt2x00_intf_is_usb(rt2x00dev)) {
+ rt2800_register_write(rt2x00dev, PBF_CFG, 0xf40006);
+
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_BIG_ENDIAN, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_HDR_SEG_LEN, 0);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+ }
+
+ rt2800_register_write(rt2x00dev, TXOP_CTRL_CFG, 0x0000583f);
+ rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, 0x00000002);
+
+ rt2800_register_read(rt2x00dev, TX_RTS_CFG, &reg);
+ rt2x00_set_field32(&reg, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32);
+ rt2x00_set_field32(&reg, TX_RTS_CFG_RTS_THRES,
+ IEEE80211_MAX_RTS_THRESHOLD);
+ rt2x00_set_field32(&reg, TX_RTS_CFG_RTS_FBK_EN, 0);
+ rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg);
+
+ rt2800_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca);
+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
+
+ /*
+ * ASIC will keep garbage value after boot, clear encryption keys.
+ */
+ for (i = 0; i < 4; i++)
+ rt2800_register_write(rt2x00dev,
+ SHARED_KEY_MODE_ENTRY(i), 0);
+
+ for (i = 0; i < 256; i++) {
+ u32 wcid[2] = { 0xffffffff, 0x00ffffff };
+ rt2800_register_multiwrite(rt2x00dev, MAC_WCID_ENTRY(i),
+ wcid, sizeof(wcid));
+
+ rt2800_register_write(rt2x00dev, MAC_WCID_ATTR_ENTRY(i), 1);
+ rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0);
+ }
+
+ /*
+ * Clear all beacons
+ * For the Beacon base registers we only need to clear
+ * the first byte since that byte contains the VALID and OWNER
+ * bits which (when set to 0) will invalidate the entire beacon.
+ */
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE0, 0);
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE1, 0);
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE2, 0);
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE3, 0);
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE4, 0);
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE5, 0);
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE6, 0);
+ rt2800_register_write(rt2x00dev, HW_BEACON_BASE7, 0);
+
+ if (rt2x00_intf_is_usb(rt2x00dev)) {
+ rt2800_register_read(rt2x00dev, USB_CYC_CFG, &reg);
+ rt2x00_set_field32(&reg, USB_CYC_CFG_CLOCK_CYCLE, 30);
+ rt2800_register_write(rt2x00dev, USB_CYC_CFG, reg);
+ }
+
+ rt2800_register_read(rt2x00dev, HT_FBK_CFG0, &reg);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS0FBK, 0);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS1FBK, 0);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS2FBK, 1);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS3FBK, 2);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS4FBK, 3);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS5FBK, 4);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS6FBK, 5);
+ rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS7FBK, 6);
+ rt2800_register_write(rt2x00dev, HT_FBK_CFG0, reg);
+
+ rt2800_register_read(rt2x00dev, HT_FBK_CFG1, &reg);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS8FBK, 8);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS9FBK, 8);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS10FBK, 9);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS11FBK, 10);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS12FBK, 11);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS13FBK, 12);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS14FBK, 13);
+ rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS15FBK, 14);
+ rt2800_register_write(rt2x00dev, HT_FBK_CFG1, reg);
+
+ rt2800_register_read(rt2x00dev, LG_FBK_CFG0, &reg);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS0FBK, 8);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS1FBK, 8);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS2FBK, 9);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS3FBK, 10);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS4FBK, 11);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS5FBK, 12);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS6FBK, 13);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS7FBK, 14);
+ rt2800_register_write(rt2x00dev, LG_FBK_CFG0, reg);
+
+ rt2800_register_read(rt2x00dev, LG_FBK_CFG1, &reg);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS0FBK, 0);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS1FBK, 0);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS2FBK, 1);
+ rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS3FBK, 2);
+ rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg);
+
+ /*
+ * We must clear the error counters.
+ * These registers are cleared on read,
+ * so we may pass a useless variable to store the value.
+ */
+ rt2800_register_read(rt2x00dev, RX_STA_CNT0, &reg);
+ rt2800_register_read(rt2x00dev, RX_STA_CNT1, &reg);
+ rt2800_register_read(rt2x00dev, RX_STA_CNT2, &reg);
+ rt2800_register_read(rt2x00dev, TX_STA_CNT0, &reg);
+ rt2800_register_read(rt2x00dev, TX_STA_CNT1, &reg);
+ rt2800_register_read(rt2x00dev, TX_STA_CNT2, &reg);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_init_registers);
+
+static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u32 reg;
+
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, MAC_STATUS_CFG, &reg);
+ if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY))
+ return 0;
+
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ ERROR(rt2x00dev, "BBP/RF register access failed, aborting.\n");
+ return -EACCES;
+}
+
+static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u8 value;
+
+ /*
+ * BBP was enabled after firmware was loaded,
+ * but we need to reactivate it now.
+ */
+ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
+ msleep(1);
+
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_bbp_read(rt2x00dev, 0, &value);
+ if ((value != 0xff) && (value != 0x00))
+ return 0;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ ERROR(rt2x00dev, "BBP register access failed, aborting.\n");
+ return -EACCES;
+}
+
+int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u16 eeprom;
+ u8 reg_id;
+ u8 value;
+
+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
+ rt2800_wait_bbp_ready(rt2x00dev)))
+ return -EACCES;
+
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
+ rt2800_bbp_write(rt2x00dev, 81, 0x37);
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+ rt2800_bbp_write(rt2x00dev, 103, 0x00);
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) {
+ rt2800_bbp_write(rt2x00dev, 69, 0x16);
+ rt2800_bbp_write(rt2x00dev, 73, 0x12);
+ }
+
+ if (rt2x00_rev(&rt2x00dev->chip) > RT2860D_VERSION)
+ rt2800_bbp_write(rt2x00dev, 84, 0x19);
+
+ if (rt2x00_intf_is_usb(rt2x00dev) &&
+ rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) {
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+ }
+
+ if (rt2x00_rt(&rt2x00dev->chip, RT3052)) {
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+ rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+ rt2800_bbp_write(rt2x00dev, 80, 0x08);
+ }
+
+ for (i = 0; i < EEPROM_BBP_SIZE; i++) {
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
+
+ if (eeprom != 0xffff && eeprom != 0x0000) {
+ reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID);
+ value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE);
+ rt2800_bbp_write(rt2x00dev, reg_id, value);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_init_bbp);
+
+static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev,
+ bool bw40, u8 rfcsr24, u8 filter_target)
+{
+ unsigned int i;
+ u8 bbp;
+ u8 rfcsr;
+ u8 passband;
+ u8 stopband;
+ u8 overtuned = 0;
+
+ rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24);
+
+ rt2800_bbp_read(rt2x00dev, 4, &bbp);
+ rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40);
+ rt2800_bbp_write(rt2x00dev, 4, bbp);
+
+ rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1);
+ rt2800_rfcsr_write(rt2x00dev, 22, rfcsr);
+
+ /*
+ * Set power & frequency of passband test tone
+ */
+ rt2800_bbp_write(rt2x00dev, 24, 0);
+
+ for (i = 0; i < 100; i++) {
+ rt2800_bbp_write(rt2x00dev, 25, 0x90);
+ msleep(1);
+
+ rt2800_bbp_read(rt2x00dev, 55, &passband);
+ if (passband)
+ break;
+ }
+
+ /*
+ * Set power & frequency of stopband test tone
+ */
+ rt2800_bbp_write(rt2x00dev, 24, 0x06);
+
+ for (i = 0; i < 100; i++) {
+ rt2800_bbp_write(rt2x00dev, 25, 0x90);
+ msleep(1);
+
+ rt2800_bbp_read(rt2x00dev, 55, &stopband);
+
+ if ((passband - stopband) <= filter_target) {
+ rfcsr24++;
+ overtuned += ((passband - stopband) == filter_target);
+ } else
+ break;
+
+ rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24);
+ }
+
+ rfcsr24 -= !!overtuned;
+
+ rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24);
+ return rfcsr24;
+}
+
+int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
+{
+ u8 rfcsr;
+ u8 bbp;
+
+ if (rt2x00_intf_is_usb(rt2x00dev) &&
+ rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION)
+ return 0;
+
+ if (rt2x00_intf_is_pci(rt2x00dev)) {
+ if (!rt2x00_rf(&rt2x00dev->chip, RF3020) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF3021) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF3022))
+ return 0;
+ }
+
+ /*
+ * Init RF calibration.
+ */
+ rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1);
+ rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+ msleep(1);
+ rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0);
+ rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+ if (rt2x00_intf_is_usb(rt2x00dev)) {
+ rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
+ rt2800_rfcsr_write(rt2x00dev, 5, 0x03);
+ rt2800_rfcsr_write(rt2x00dev, 6, 0x02);
+ rt2800_rfcsr_write(rt2x00dev, 7, 0x70);
+ rt2800_rfcsr_write(rt2x00dev, 9, 0x0f);
+ rt2800_rfcsr_write(rt2x00dev, 10, 0x71);
+ rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+ rt2800_rfcsr_write(rt2x00dev, 12, 0x7b);
+ rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
+ rt2800_rfcsr_write(rt2x00dev, 15, 0x58);
+ rt2800_rfcsr_write(rt2x00dev, 16, 0xb3);
+ rt2800_rfcsr_write(rt2x00dev, 17, 0x92);
+ rt2800_rfcsr_write(rt2x00dev, 18, 0x2c);
+ rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
+ rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
+ rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
+ rt2800_rfcsr_write(rt2x00dev, 24, 0x16);
+ rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+ rt2800_rfcsr_write(rt2x00dev, 27, 0x03);
+ rt2800_rfcsr_write(rt2x00dev, 29, 0x1f);
+ } else if (rt2x00_intf_is_pci(rt2x00dev)) {
+ rt2800_rfcsr_write(rt2x00dev, 0, 0x50);
+ rt2800_rfcsr_write(rt2x00dev, 1, 0x01);
+ rt2800_rfcsr_write(rt2x00dev, 2, 0xf7);
+ rt2800_rfcsr_write(rt2x00dev, 3, 0x75);
+ rt2800_rfcsr_write(rt2x00dev, 4, 0x40);
+ rt2800_rfcsr_write(rt2x00dev, 5, 0x03);
+ rt2800_rfcsr_write(rt2x00dev, 6, 0x02);
+ rt2800_rfcsr_write(rt2x00dev, 7, 0x50);
+ rt2800_rfcsr_write(rt2x00dev, 8, 0x39);
+ rt2800_rfcsr_write(rt2x00dev, 9, 0x0f);
+ rt2800_rfcsr_write(rt2x00dev, 10, 0x60);
+ rt2800_rfcsr_write(rt2x00dev, 11, 0x21);
+ rt2800_rfcsr_write(rt2x00dev, 12, 0x75);
+ rt2800_rfcsr_write(rt2x00dev, 13, 0x75);
+ rt2800_rfcsr_write(rt2x00dev, 14, 0x90);
+ rt2800_rfcsr_write(rt2x00dev, 15, 0x58);
+ rt2800_rfcsr_write(rt2x00dev, 16, 0xb3);
+ rt2800_rfcsr_write(rt2x00dev, 17, 0x92);
+ rt2800_rfcsr_write(rt2x00dev, 18, 0x2c);
+ rt2800_rfcsr_write(rt2x00dev, 19, 0x02);
+ rt2800_rfcsr_write(rt2x00dev, 20, 0xba);
+ rt2800_rfcsr_write(rt2x00dev, 21, 0xdb);
+ rt2800_rfcsr_write(rt2x00dev, 22, 0x00);
+ rt2800_rfcsr_write(rt2x00dev, 23, 0x31);
+ rt2800_rfcsr_write(rt2x00dev, 24, 0x08);
+ rt2800_rfcsr_write(rt2x00dev, 25, 0x01);
+ rt2800_rfcsr_write(rt2x00dev, 26, 0x25);
+ rt2800_rfcsr_write(rt2x00dev, 27, 0x23);
+ rt2800_rfcsr_write(rt2x00dev, 28, 0x13);
+ rt2800_rfcsr_write(rt2x00dev, 29, 0x83);
+ }
+
+ /*
+ * Set RX Filter calibration for 20MHz and 40MHz
+ */
+ rt2x00dev->calibration[0] =
+ rt2800_init_rx_filter(rt2x00dev, false, 0x07, 0x16);
+ rt2x00dev->calibration[1] =
+ rt2800_init_rx_filter(rt2x00dev, true, 0x27, 0x19);
+
+ /*
+ * Set back to initial state
+ */
+ rt2800_bbp_write(rt2x00dev, 24, 0);
+
+ rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0);
+ rt2800_rfcsr_write(rt2x00dev, 22, rfcsr);
+
+ /*
+ * set BBP back to BW20
+ */
+ rt2800_bbp_read(rt2x00dev, 4, &bbp);
+ rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0);
+ rt2800_bbp_write(rt2x00dev, 4, bbp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_init_rfcsr);
+
+int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, EFUSE_CTRL, &reg);
+
+ return rt2x00_get_field32(reg, EFUSE_CTRL_PRESENT);
+}
+EXPORT_SYMBOL_GPL(rt2800_efuse_detect);
+
+static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i)
+{
+ u32 reg;
+
+ mutex_lock(&rt2x00dev->csr_mutex);
+
+ rt2800_register_read_lock(rt2x00dev, EFUSE_CTRL, &reg);
+ rt2x00_set_field32(&reg, EFUSE_CTRL_ADDRESS_IN, i);
+ rt2x00_set_field32(&reg, EFUSE_CTRL_MODE, 0);
+ rt2x00_set_field32(&reg, EFUSE_CTRL_KICK, 1);
+ rt2800_register_write_lock(rt2x00dev, EFUSE_CTRL, reg);
+
+ /* Wait until the EEPROM has been loaded */
+ rt2800_regbusy_read(rt2x00dev, EFUSE_CTRL, EFUSE_CTRL_KICK, &reg);
+
+ /* Apparently the data is read from end to start */
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA3,
+ (u32 *)&rt2x00dev->eeprom[i]);
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA2,
+ (u32 *)&rt2x00dev->eeprom[i + 2]);
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA1,
+ (u32 *)&rt2x00dev->eeprom[i + 4]);
+ rt2800_register_read_lock(rt2x00dev, EFUSE_DATA0,
+ (u32 *)&rt2x00dev->eeprom[i + 6]);
+
+ mutex_unlock(&rt2x00dev->csr_mutex);
+}
+
+void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+
+ for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8)
+ rt2800_efuse_read(rt2x00dev, i);
+}
+EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);
+
+int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
+{
+ u16 word;
+ u8 *mac;
+ u8 default_lna_gain;
+
+ /*
+ * Start validation of the data that has been read.
+ */
+ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
+ if (!is_valid_ether_addr(mac)) {
+ random_ether_addr(mac);
+ EEPROM(rt2x00dev, "MAC: %pM\n", mac);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_TXPATH, 1);
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2820);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word);
+ EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word);
+ } else if (rt2x00_rev(&rt2x00dev->chip) < RT2883_VERSION) {
+ /*
+ * There is a max of 2 RX streams for RT28x0 series
+ */
+ if (rt2x00_get_field16(word, EEPROM_ANTENNA_RXPATH) > 2)
+ rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word);
+ if (word == 0xffff) {
+ rt2x00_set_field16(&word, EEPROM_NIC_HW_RADIO, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_DYNAMIC_TX_AGC, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_BG, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_A, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_WPS_PBC, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_BW40M_BG, 0);
+ rt2x00_set_field16(&word, EEPROM_NIC_BW40M_A, 0);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word);
+ EEPROM(rt2x00dev, "NIC: 0x%04x\n", word);
+ }
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
+ if ((word & 0x00ff) == 0x00ff) {
+ rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0);
+ rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE,
+ LED_MODE_TXRX_ACTIVITY);
+ rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_LED1, 0x5555);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_LED2, 0x2221);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_LED3, 0xa9f8);
+ EEPROM(rt2x00dev, "Freq: 0x%04x\n", word);
+ }
+
+ /*
+ * During the LNA validation we are going to use
+ * lna0 as correct value. Note that EEPROM_LNA
+ * is never validated.
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
+ default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
+ if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10)
+ rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0);
+ if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10)
+ rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
+ if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10)
+ rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0);
+ if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
+ rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
+ default_lna_gain);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
+ if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10)
+ rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0);
+ if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10)
+ rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
+ if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10)
+ rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0);
+ if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
+ rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
+ default_lna_gain);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_validate_eeprom);
+
+int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ u16 value;
+ u16 eeprom;
+
+ /*
+ * Read EEPROM word for configuration.
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom);
+
+ /*
+ * Identify RF chipset.
+ */
+ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
+ rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
+
+ rt2x00_set_chip_rf(rt2x00dev, value, reg);
+
+ if (rt2x00_intf_is_usb(rt2x00dev)) {
+ struct rt2x00_chip *chip = &rt2x00dev->chip;
+
+ /*
+ * The check for rt2860 is not a typo, some rt2870 hardware
+ * identifies itself as rt2860 in the CSR register.
+ */
+ if (rt2x00_check_rev(chip, 0xfff00000, 0x28600000) ||
+ rt2x00_check_rev(chip, 0xfff00000, 0x28700000) ||
+ rt2x00_check_rev(chip, 0xfff00000, 0x28800000)) {
+ rt2x00_set_chip_rt(rt2x00dev, RT2870);
+ } else if (rt2x00_check_rev(chip, 0xffff0000, 0x30700000)) {
+ rt2x00_set_chip_rt(rt2x00dev, RT3070);
+ } else {
+ ERROR(rt2x00dev, "Invalid RT chipset detected.\n");
+ return -ENODEV;
+ }
+ }
+ rt2x00_print_chip(rt2x00dev);
+
+ if (!rt2x00_rf(&rt2x00dev->chip, RF2820) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF2850) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF2720) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF2750) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF3020) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF2020) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF3021) &&
+ !rt2x00_rf(&rt2x00dev->chip, RF3022)) {
+ ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Identify default antenna configuration.
+ */
+ rt2x00dev->default_ant.tx =
+ rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH);
+ rt2x00dev->default_ant.rx =
+ rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH);
+
+ /*
+ * Read frequency offset and RF programming sequence.
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+ rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET);
+
+ /*
+ * Read external LNA informations.
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom);
+
+ if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A))
+ __set_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags);
+ if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG))
+ __set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags);
+
+ /*
+ * Detect if this device has an hardware controlled radio.
+ */
+ if (rt2x00_get_field16(eeprom, EEPROM_NIC_HW_RADIO))
+ __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
+
+ /*
+ * Store led settings, for correct led behaviour.
+ */
+#ifdef CONFIG_RT2X00_LIB_LEDS
+ rt2800_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
+ rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC);
+ rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &rt2x00dev->led_mcu_reg);
+#endif /* CONFIG_RT2X00_LIB_LEDS */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_init_eeprom);
+
+/*
+ * RF value list for rt28x0
+ * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750)
+ */
+static const struct rf_channel rf_vals[] = {
+ { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b },
+ { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f },
+ { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b },
+ { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f },
+ { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b },
+ { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f },
+ { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b },
+ { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f },
+ { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b },
+ { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f },
+ { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b },
+ { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f },
+ { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b },
+ { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 },
+
+ /* 802.11 UNI / HyperLan 2 */
+ { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 },
+ { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 },
+ { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 },
+ { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 },
+ { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b },
+ { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b },
+ { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 },
+ { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 },
+ { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b },
+ { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 },
+ { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 },
+ { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 },
+
+ /* 802.11 HyperLan 2 */
+ { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 },
+ { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 },
+ { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 },
+ { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 },
+ { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 },
+ { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b },
+ { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 },
+ { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 },
+ { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 },
+ { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 },
+ { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b },
+ { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 },
+ { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b },
+ { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 },
+ { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b },
+ { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 },
+
+ /* 802.11 UNII */
+ { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 },
+ { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 },
+ { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f },
+ { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f },
+ { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 },
+ { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 },
+ { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 },
+ { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f },
+ { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 },
+ { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 },
+ { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f },
+
+ /* 802.11 Japan */
+ { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b },
+ { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 },
+ { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b },
+ { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 },
+ { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 },
+ { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b },
+ { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 },
+};
+
+/*
+ * RF value list for rt3070
+ * Supports: 2.4 GHz
+ */
+static const struct rf_channel rf_vals_302x[] = {
+ {1, 241, 2, 2 },
+ {2, 241, 2, 7 },
+ {3, 242, 2, 2 },
+ {4, 242, 2, 7 },
+ {5, 243, 2, 2 },
+ {6, 243, 2, 7 },
+ {7, 244, 2, 2 },
+ {8, 244, 2, 7 },
+ {9, 245, 2, 2 },
+ {10, 245, 2, 7 },
+ {11, 246, 2, 2 },
+ {12, 246, 2, 7 },
+ {13, 247, 2, 2 },
+ {14, 248, 2, 4 },
+};
+
+int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
+{
+ struct rt2x00_chip *chip = &rt2x00dev->chip;
+ struct hw_mode_spec *spec = &rt2x00dev->spec;
+ struct channel_info *info;
+ char *tx_power1;
+ char *tx_power2;
+ unsigned int i;
+ u16 eeprom;
+
+ /*
+ * Initialize all hw fields.
+ */
+ rt2x00dev->hw->flags =
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_PS_NULLFUNC_STACK;
+
+ SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
+ SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
+ rt2x00_eeprom_addr(rt2x00dev,
+ EEPROM_MAC_ADDR_0));
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom);
+
+ /*
+ * Initialize hw_mode information.
+ */
+ spec->supported_bands = SUPPORT_BAND_2GHZ;
+ spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
+
+ if (rt2x00_rf(chip, RF2820) ||
+ rt2x00_rf(chip, RF2720) ||
+ (rt2x00_intf_is_pci(rt2x00dev) && rt2x00_rf(chip, RF3052))) {
+ spec->num_channels = 14;
+ spec->channels = rf_vals;
+ } else if (rt2x00_rf(chip, RF2850) || rt2x00_rf(chip, RF2750)) {
+ spec->supported_bands |= SUPPORT_BAND_5GHZ;
+ spec->num_channels = ARRAY_SIZE(rf_vals);
+ spec->channels = rf_vals;
+ } else if (rt2x00_rf(chip, RF3020) ||
+ rt2x00_rf(chip, RF2020) ||
+ rt2x00_rf(chip, RF3021) ||
+ rt2x00_rf(chip, RF3022)) {
+ spec->num_channels = ARRAY_SIZE(rf_vals_302x);
+ spec->channels = rf_vals_302x;
+ }
+
+ /*
+ * Initialize HT information.
+ */
+ if (!rt2x00_rf(chip, RF2020))
+ spec->ht.ht_supported = true;
+ else
+ spec->ht.ht_supported = false;
+
+ spec->ht.cap =
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_TX_STBC |
+ IEEE80211_HT_CAP_RX_STBC |
+ IEEE80211_HT_CAP_PSMP_SUPPORT;
+ spec->ht.ampdu_factor = 3;
+ spec->ht.ampdu_density = 4;
+ spec->ht.mcs.tx_params =
+ IEEE80211_HT_MCS_TX_DEFINED |
+ IEEE80211_HT_MCS_TX_RX_DIFF |
+ ((rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH) - 1) <<
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
+
+ switch (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH)) {
+ case 3:
+ spec->ht.mcs.rx_mask[2] = 0xff;
+ case 2:
+ spec->ht.mcs.rx_mask[1] = 0xff;
+ case 1:
+ spec->ht.mcs.rx_mask[0] = 0xff;
+ spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */
+ break;
+ }
+
+ /*
+ * Create channel information array
+ */
+ info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ spec->channels_info = info;
+
+ tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
+ tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+
+ for (i = 0; i < 14; i++) {
+ info[i].tx_power1 = TXPOWER_G_FROM_DEV(tx_power1[i]);
+ info[i].tx_power2 = TXPOWER_G_FROM_DEV(tx_power2[i]);
+ }
+
+ if (spec->num_channels > 14) {
+ tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
+ tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
+
+ for (i = 14; i < spec->num_channels; i++) {
+ info[i].tx_power1 = TXPOWER_A_FROM_DEV(tx_power1[i]);
+ info[i].tx_power2 = TXPOWER_A_FROM_DEV(tx_power2[i]);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_probe_hw_mode);
+
+/*
+ * IEEE80211 stack callback functions.
+ */
+static void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx,
+ u32 *iv32, u16 *iv16)
+{
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ struct mac_iveiv_entry iveiv_entry;
+ u32 offset;
+
+ offset = MAC_IVEIV_ENTRY(hw_key_idx);
+ rt2800_register_multiread(rt2x00dev, offset,
+ &iveiv_entry, sizeof(iveiv_entry));
+
+ memcpy(&iveiv_entry.iv[0], iv16, sizeof(iv16));
+ memcpy(&iveiv_entry.iv[4], iv32, sizeof(iv32));
+}
+
+static int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ u32 reg;
+ bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD);
+
+ rt2800_register_read(rt2x00dev, TX_RTS_CFG, &reg);
+ rt2x00_set_field32(&reg, TX_RTS_CFG_RTS_THRES, value);
+ rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, CCK_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, CCK_PROT_CFG_RTS_TH_EN, enabled);
+ rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, OFDM_PROT_CFG_RTS_TH_EN, enabled);
+ rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MM20_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, MM20_PROT_CFG_RTS_TH_EN, enabled);
+ rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MM40_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, MM40_PROT_CFG_RTS_TH_EN, enabled);
+ rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, GF20_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, GF20_PROT_CFG_RTS_TH_EN, enabled);
+ rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, GF40_PROT_CFG, &reg);
+ rt2x00_set_field32(&reg, GF40_PROT_CFG_RTS_TH_EN, enabled);
+ rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg);
+
+ return 0;
+}
+
+static int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ struct data_queue *queue;
+ struct rt2x00_field32 field;
+ int retval;
+ u32 reg;
+ u32 offset;
+
+ /*
+ * First pass the configuration through rt2x00lib, that will
+ * update the queue settings and validate the input. After that
+ * we are free to update the registers based on the value
+ * in the queue parameter.
+ */
+ retval = rt2x00mac_conf_tx(hw, queue_idx, params);
+ if (retval)
+ return retval;
+
+ /*
+ * We only need to perform additional register initialization
+ * for WMM queues/
+ */
+ if (queue_idx >= 4)
+ return 0;
+
+ queue = rt2x00queue_get_queue(rt2x00dev, queue_idx);
+
+ /* Update WMM TXOP register */
+ offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2)));
+ field.bit_offset = (queue_idx & 1) * 16;
+ field.bit_mask = 0xffff << field.bit_offset;
+
+ rt2800_register_read(rt2x00dev, offset, &reg);
+ rt2x00_set_field32(&reg, field, queue->txop);
+ rt2800_register_write(rt2x00dev, offset, reg);
+
+ /* Update WMM registers */
+ field.bit_offset = queue_idx * 4;
+ field.bit_mask = 0xf << field.bit_offset;
+
+ rt2800_register_read(rt2x00dev, WMM_AIFSN_CFG, &reg);
+ rt2x00_set_field32(&reg, field, queue->aifs);
+ rt2800_register_write(rt2x00dev, WMM_AIFSN_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, WMM_CWMIN_CFG, &reg);
+ rt2x00_set_field32(&reg, field, queue->cw_min);
+ rt2800_register_write(rt2x00dev, WMM_CWMIN_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, WMM_CWMAX_CFG, &reg);
+ rt2x00_set_field32(&reg, field, queue->cw_max);
+ rt2800_register_write(rt2x00dev, WMM_CWMAX_CFG, reg);
+
+ /* Update EDCA registers */
+ offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx);
+
+ rt2800_register_read(rt2x00dev, offset, &reg);
+ rt2x00_set_field32(&reg, EDCA_AC0_CFG_TX_OP, queue->txop);
+ rt2x00_set_field32(&reg, EDCA_AC0_CFG_AIFSN, queue->aifs);
+ rt2x00_set_field32(&reg, EDCA_AC0_CFG_CWMIN, queue->cw_min);
+ rt2x00_set_field32(&reg, EDCA_AC0_CFG_CWMAX, queue->cw_max);
+ rt2800_register_write(rt2x00dev, offset, reg);
+
+ return 0;
+}
+
+static u64 rt2800_get_tsf(struct ieee80211_hw *hw)
+{
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+ u64 tsf;
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, TSF_TIMER_DW1, &reg);
+ tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32;
+ rt2800_register_read(rt2x00dev, TSF_TIMER_DW0, &reg);
+ tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD);
+
+ return tsf;
+}
+
+const struct ieee80211_ops rt2800_mac80211_ops = {
+ .tx = rt2x00mac_tx,
+ .start = rt2x00mac_start,
+ .stop = rt2x00mac_stop,
+ .add_interface = rt2x00mac_add_interface,
+ .remove_interface = rt2x00mac_remove_interface,
+ .config = rt2x00mac_config,
+ .configure_filter = rt2x00mac_configure_filter,
+ .set_tim = rt2x00mac_set_tim,
+ .set_key = rt2x00mac_set_key,
+ .get_stats = rt2x00mac_get_stats,
+ .get_tkip_seq = rt2800_get_tkip_seq,
+ .set_rts_threshold = rt2800_set_rts_threshold,
+ .bss_info_changed = rt2x00mac_bss_info_changed,
+ .conf_tx = rt2800_conf_tx,
+ .get_tx_stats = rt2x00mac_get_tx_stats,
+ .get_tsf = rt2800_get_tsf,
+ .rfkill_poll = rt2x00mac_rfkill_poll,
+};
+EXPORT_SYMBOL_GPL(rt2800_mac80211_ops);
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
new file mode 100644
index 00000000000..535ce22f2ac
--- /dev/null
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 2009 Bartlomiej Zolnierkiewicz
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef RT2800LIB_H
+#define RT2800LIB_H
+
+struct rt2800_ops {
+ void (*register_read)(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset, u32 *value);
+ void (*register_read_lock)(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset, u32 *value);
+ void (*register_write)(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset, u32 value);
+ void (*register_write_lock)(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset, u32 value);
+
+ void (*register_multiread)(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ void *value, const u32 length);
+ void (*register_multiwrite)(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ const void *value, const u32 length);
+
+ int (*regbusy_read)(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ const struct rt2x00_field32 field, u32 *reg);
+};
+
+static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 *value)
+{
+ const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
+
+ rt2800ops->register_read(rt2x00dev, offset, value);
+}
+
+static inline void rt2800_register_read_lock(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 *value)
+{
+ const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
+
+ rt2800ops->register_read_lock(rt2x00dev, offset, value);
+}
+
+static inline void rt2800_register_write(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 value)
+{
+ const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
+
+ rt2800ops->register_write(rt2x00dev, offset, value);
+}
+
+static inline void rt2800_register_write_lock(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ u32 value)
+{
+ const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
+
+ rt2800ops->register_write_lock(rt2x00dev, offset, value);
+}
+
+static inline void rt2800_register_multiread(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ void *value, const u32 length)
+{
+ const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
+
+ rt2800ops->register_multiread(rt2x00dev, offset, value, length);
+}
+
+static inline void rt2800_register_multiwrite(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ const void *value,
+ const u32 length)
+{
+ const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
+
+ rt2800ops->register_multiwrite(rt2x00dev, offset, value, length);
+}
+
+static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ const struct rt2x00_field32 field,
+ u32 *reg)
+{
+ const struct rt2800_ops *rt2800ops = rt2x00dev->priv;
+
+ return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg);
+}
+
+void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
+ const u8 command, const u8 token,
+ const u8 arg0, const u8 arg1);
+
+extern const struct rt2x00debug rt2800_rt2x00debug;
+
+int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev);
+void rt2800_init_led(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00_led *led, enum led_type type);
+int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key);
+int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_crypto *crypto,
+ struct ieee80211_key_conf *key);
+void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
+ const unsigned int filter_flags);
+void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
+ struct rt2x00intf_conf *conf, const unsigned int flags);
+void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp);
+void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant);
+void rt2800_config(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00lib_conf *libconf,
+ const unsigned int flags);
+void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual);
+void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual);
+void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
+ const u32 count);
+
+int rt2800_init_registers(struct rt2x00_dev *rt2x00dev);
+int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev);
+int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev);
+
+int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev);
+void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev);
+int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev);
+int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev);
+int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev);
+
+extern const struct ieee80211_ops rt2800_mac80211_ops;
+
+#endif /* RT2800LIB_H */
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
new file mode 100644
index 00000000000..dfc886fcb44
--- /dev/null
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -0,0 +1,1322 @@
+/*
+ Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ Module: rt2800pci
+ Abstract: rt2800pci device specific routines.
+ Supported chipsets: RT2800E & RT2800ED.
+ */
+
+#include <linux/crc-ccitt.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/eeprom_93cx6.h>
+
+#include "rt2x00.h"
+#include "rt2x00pci.h"
+#include "rt2x00soc.h"
+#include "rt2800lib.h"
+#include "rt2800.h"
+#include "rt2800pci.h"
+
+#ifdef CONFIG_RT2800PCI_PCI_MODULE
+#define CONFIG_RT2800PCI_PCI
+#endif
+
+#ifdef CONFIG_RT2800PCI_WISOC_MODULE
+#define CONFIG_RT2800PCI_WISOC
+#endif
+
+/*
+ * Allow hardware encryption to be disabled.
+ */
+static int modparam_nohwcrypt = 1;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token)
+{
+ unsigned int i;
+ u32 reg;
+
+ for (i = 0; i < 200; i++) {
+ rt2800_register_read(rt2x00dev, H2M_MAILBOX_CID, &reg);
+
+ if ((rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD0) == token) ||
+ (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD1) == token) ||
+ (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD2) == token) ||
+ (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD3) == token))
+ break;
+
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ if (i == 200)
+ ERROR(rt2x00dev, "MCU request failed, no response from hardware\n");
+
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0);
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0);
+}
+
+#ifdef CONFIG_RT2800PCI_WISOC
+static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
+{
+ u32 *base_addr = (u32 *) KSEG1ADDR(0x1F040000); /* XXX for RT3052 */
+
+ memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE);
+}
+#else
+static inline void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
+{
+}
+#endif /* CONFIG_RT2800PCI_WISOC */
+
+#ifdef CONFIG_RT2800PCI_PCI
+static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
+{
+ struct rt2x00_dev *rt2x00dev = eeprom->data;
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, E2PROM_CSR, &reg);
+
+ eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN);
+ eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT);
+ eeprom->reg_data_clock =
+ !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK);
+ eeprom->reg_chip_select =
+ !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT);
+}
+
+static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom)
+{
+ struct rt2x00_dev *rt2x00dev = eeprom->data;
+ u32 reg = 0;
+
+ rt2x00_set_field32(&reg, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in);
+ rt2x00_set_field32(&reg, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out);
+ rt2x00_set_field32(&reg, E2PROM_CSR_DATA_CLOCK,
+ !!eeprom->reg_data_clock);
+ rt2x00_set_field32(&reg, E2PROM_CSR_CHIP_SELECT,
+ !!eeprom->reg_chip_select);
+
+ rt2800_register_write(rt2x00dev, E2PROM_CSR, reg);
+}
+
+static void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
+{
+ struct eeprom_93cx6 eeprom;
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, E2PROM_CSR, &reg);
+
+ eeprom.data = rt2x00dev;
+ eeprom.register_read = rt2800pci_eepromregister_read;
+ eeprom.register_write = rt2800pci_eepromregister_write;
+ eeprom.width = !rt2x00_get_field32(reg, E2PROM_CSR_TYPE) ?
+ PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66;
+ eeprom.reg_data_in = 0;
+ eeprom.reg_data_out = 0;
+ eeprom.reg_data_clock = 0;
+ eeprom.reg_chip_select = 0;
+
+ eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom,
+ EEPROM_SIZE / sizeof(u16));
+}
+
+static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
+{
+ return rt2800_efuse_detect(rt2x00dev);
+}
+
+static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_read_eeprom_efuse(rt2x00dev);
+}
+#else
+static inline void rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev)
+{
+}
+
+static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev)
+{
+ return 0;
+}
+
+static inline void rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
+{
+}
+#endif /* CONFIG_RT2800PCI_PCI */
+
+/*
+ * Firmware functions
+ */
+static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev)
+{
+ return FIRMWARE_RT2860;
+}
+
+static int rt2800pci_check_firmware(struct rt2x00_dev *rt2x00dev,
+ const u8 *data, const size_t len)
+{
+ u16 fw_crc;
+ u16 crc;
+
+ /*
+ * Only support 8kb firmware files.
+ */
+ if (len != 8192)
+ return FW_BAD_LENGTH;
+
+ /*
+ * The last 2 bytes in the firmware array are the crc checksum itself,
+ * this means that we should never pass those 2 bytes to the crc
+ * algorithm.
+ */
+ fw_crc = (data[len - 2] << 8 | data[len - 1]);
+
+ /*
+ * Use the crc ccitt algorithm.
+ * This will return the same value as the legacy driver which
+ * used bit ordering reversion on the both the firmware bytes
+ * before input input as well as on the final output.
+ * Obviously using crc ccitt directly is much more efficient.
+ */
+ crc = crc_ccitt(~0, data, len - 2);
+
+ /*
+ * There is a small difference between the crc-itu-t + bitrev and
+ * the crc-ccitt crc calculation. In the latter method the 2 bytes
+ * will be swapped, use swab16 to convert the crc to the correct
+ * value.
+ */
+ crc = swab16(crc);
+
+ return (fw_crc == crc) ? FW_OK : FW_BAD_CRC;
+}
+
+static int rt2800pci_load_firmware(struct rt2x00_dev *rt2x00dev,
+ const u8 *data, const size_t len)
+{
+ unsigned int i;
+ u32 reg;
+
+ /*
+ * Wait for stable hardware.
+ */
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
+ if (reg && reg != ~0)
+ break;
+ msleep(1);
+ }
+
+ if (i == REGISTER_BUSY_COUNT) {
+ ERROR(rt2x00dev, "Unstable hardware.\n");
+ return -EBUSY;
+ }
+
+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);
+ rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);
+
+ /*
+ * Disable DMA, will be reenabled later when enabling
+ * the radio.
+ */
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+ /*
+ * enable Host program ram write selection
+ */
+ reg = 0;
+ rt2x00_set_field32(&reg, PBF_SYS_CTRL_HOST_RAM_WRITE, 1);
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, reg);
+
+ /*
+ * Write firmware to device.
+ */
+ rt2800_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE,
+ data, len);
+
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000);
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001);
+
+ /*
+ * Wait for device to stabilize.
+ */
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
+ if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY))
+ break;
+ msleep(1);
+ }
+
+ if (i == REGISTER_BUSY_COUNT) {
+ ERROR(rt2x00dev, "PBF system register not ready.\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Disable interrupts
+ */
+ rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF);
+
+ /*
+ * Initialize BBP R/W access agent
+ */
+ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
+
+ return 0;
+}
+
+/*
+ * Initialization functions.
+ */
+static bool rt2800pci_get_entry_state(struct queue_entry *entry)
+{
+ struct queue_entry_priv_pci *entry_priv = entry->priv_data;
+ u32 word;
+
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 1, &word);
+
+ return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE));
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 1, &word);
+
+ return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE));
+ }
+}
+
+static void rt2800pci_clear_entry(struct queue_entry *entry)
+{
+ struct queue_entry_priv_pci *entry_priv = entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ u32 word;
+
+ if (entry->queue->qid == QID_RX) {
+ rt2x00_desc_read(entry_priv->desc, 0, &word);
+ rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma);
+ rt2x00_desc_write(entry_priv->desc, 0, word);
+
+ rt2x00_desc_read(entry_priv->desc, 1, &word);
+ rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0);
+ rt2x00_desc_write(entry_priv->desc, 1, word);
+ } else {
+ rt2x00_desc_read(entry_priv->desc, 1, &word);
+ rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1);
+ rt2x00_desc_write(entry_priv->desc, 1, word);
+ }
+}
+
+static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev)
+{
+ struct queue_entry_priv_pci *entry_priv;
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DRX_IDX0, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
+
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
+
+ /*
+ * Initialize registers.
+ */
+ entry_priv = rt2x00dev->tx[0].entries[0].priv_data;
+ rt2800_register_write(rt2x00dev, TX_BASE_PTR0, entry_priv->desc_dma);
+ rt2800_register_write(rt2x00dev, TX_MAX_CNT0, rt2x00dev->tx[0].limit);
+ rt2800_register_write(rt2x00dev, TX_CTX_IDX0, 0);
+ rt2800_register_write(rt2x00dev, TX_DTX_IDX0, 0);
+
+ entry_priv = rt2x00dev->tx[1].entries[0].priv_data;
+ rt2800_register_write(rt2x00dev, TX_BASE_PTR1, entry_priv->desc_dma);
+ rt2800_register_write(rt2x00dev, TX_MAX_CNT1, rt2x00dev->tx[1].limit);
+ rt2800_register_write(rt2x00dev, TX_CTX_IDX1, 0);
+ rt2800_register_write(rt2x00dev, TX_DTX_IDX1, 0);
+
+ entry_priv = rt2x00dev->tx[2].entries[0].priv_data;
+ rt2800_register_write(rt2x00dev, TX_BASE_PTR2, entry_priv->desc_dma);
+ rt2800_register_write(rt2x00dev, TX_MAX_CNT2, rt2x00dev->tx[2].limit);
+ rt2800_register_write(rt2x00dev, TX_CTX_IDX2, 0);
+ rt2800_register_write(rt2x00dev, TX_DTX_IDX2, 0);
+
+ entry_priv = rt2x00dev->tx[3].entries[0].priv_data;
+ rt2800_register_write(rt2x00dev, TX_BASE_PTR3, entry_priv->desc_dma);
+ rt2800_register_write(rt2x00dev, TX_MAX_CNT3, rt2x00dev->tx[3].limit);
+ rt2800_register_write(rt2x00dev, TX_CTX_IDX3, 0);
+ rt2800_register_write(rt2x00dev, TX_DTX_IDX3, 0);
+
+ entry_priv = rt2x00dev->rx->entries[0].priv_data;
+ rt2800_register_write(rt2x00dev, RX_BASE_PTR, entry_priv->desc_dma);
+ rt2800_register_write(rt2x00dev, RX_MAX_CNT, rt2x00dev->rx[0].limit);
+ rt2800_register_write(rt2x00dev, RX_CRX_IDX, rt2x00dev->rx[0].limit - 1);
+ rt2800_register_write(rt2x00dev, RX_DRX_IDX, 0);
+
+ /*
+ * Enable global DMA configuration
+ */
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+ rt2800_register_write(rt2x00dev, DELAY_INT_CFG, 0);
+
+ return 0;
+}
+
+/*
+ * Device state switch handlers.
+ */
+static void rt2800pci_toggle_rx(struct rt2x00_dev *rt2x00dev,
+ enum dev_state state)
+{
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX,
+ (state == STATE_RADIO_RX_ON) ||
+ (state == STATE_RADIO_RX_ON_LINK));
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+}
+
+static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
+ enum dev_state state)
+{
+ int mask = (state == STATE_RADIO_IRQ_ON);
+ u32 reg;
+
+ /*
+ * When interrupts are being enabled, the interrupt registers
+ * should clear the register to assure a clean state.
+ */
+ if (state == STATE_RADIO_IRQ_ON) {
+ rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
+ rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+ }
+
+ rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_RXDELAYINT, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_TXDELAYINT, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_RX_DONE, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_AC0_DMA_DONE, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_AC1_DMA_DONE, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_AC2_DMA_DONE, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_AC3_DMA_DONE, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_HCCA_DMA_DONE, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_MGMT_DMA_DONE, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_MCU_COMMAND, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_RXTX_COHERENT, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_AUTO_WAKEUP, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_GPTIMER, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_RX_COHERENT, mask);
+ rt2x00_set_field32(&reg, INT_MASK_CSR_TX_COHERENT, mask);
+ rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg);
+}
+
+static int rt2800pci_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u32 reg;
+
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) &&
+ !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY))
+ return 0;
+
+ msleep(1);
+ }
+
+ ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n");
+ return -EACCES;
+}
+
+static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ u16 word;
+
+ /*
+ * Initialize all registers.
+ */
+ if (unlikely(rt2800pci_wait_wpdma_ready(rt2x00dev) ||
+ rt2800pci_init_queues(rt2x00dev) ||
+ rt2800_init_registers(rt2x00dev) ||
+ rt2800pci_wait_wpdma_ready(rt2x00dev) ||
+ rt2800_init_bbp(rt2x00dev) ||
+ rt2800_init_rfcsr(rt2x00dev)))
+ return -EIO;
+
+ /*
+ * Send signal to firmware during boot time.
+ */
+ rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0xff, 0, 0);
+
+ /*
+ * Enable RX.
+ */
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+ /*
+ * Initialize LED control
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word);
+ rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff,
+ word & 0xff, (word >> 8) & 0xff);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word);
+ rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff,
+ word & 0xff, (word >> 8) & 0xff);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word);
+ rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff,
+ word & 0xff, (word >> 8) & 0xff);
+
+ return 0;
+}
+
+static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0);
+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0);
+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0);
+
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001280);
+
+ rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DRX_IDX0, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
+
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
+ rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
+
+ /* Wait for DMA, ignore error */
+ rt2800pci_wait_wpdma_ready(rt2x00dev);
+}
+
+static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev,
+ enum dev_state state)
+{
+ /*
+ * Always put the device to sleep (even when we intend to wakeup!)
+ * if the device is booting and wasn't asleep it will return
+ * failure when attempting to wakeup.
+ */
+ rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 2);
+
+ if (state == STATE_AWAKE) {
+ rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0);
+ rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKUP);
+ }
+
+ return 0;
+}
+
+static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
+ enum dev_state state)
+{
+ int retval = 0;
+
+ switch (state) {
+ case STATE_RADIO_ON:
+ /*
+ * Before the radio can be enabled, the device first has
+ * to be woken up. After that it needs a bit of time
+ * to be fully awake and then the radio can be enabled.
+ */
+ rt2800pci_set_state(rt2x00dev, STATE_AWAKE);
+ msleep(1);
+ retval = rt2800pci_enable_radio(rt2x00dev);
+ break;
+ case STATE_RADIO_OFF:
+ /*
+ * After the radio has been disabled, the device should
+ * be put to sleep for powersaving.
+ */
+ rt2800pci_disable_radio(rt2x00dev);
+ rt2800pci_set_state(rt2x00dev, STATE_SLEEP);
+ break;
+ case STATE_RADIO_RX_ON:
+ case STATE_RADIO_RX_ON_LINK:
+ case STATE_RADIO_RX_OFF:
+ case STATE_RADIO_RX_OFF_LINK:
+ rt2800pci_toggle_rx(rt2x00dev, state);
+ break;
+ case STATE_RADIO_IRQ_ON:
+ case STATE_RADIO_IRQ_OFF:
+ rt2800pci_toggle_irq(rt2x00dev, state);
+ break;
+ case STATE_DEEP_SLEEP:
+ case STATE_SLEEP:
+ case STATE_STANDBY:
+ case STATE_AWAKE:
+ retval = rt2800pci_set_state(rt2x00dev, state);
+ break;
+ default:
+ retval = -ENOTSUPP;
+ break;
+ }
+
+ if (unlikely(retval))
+ ERROR(rt2x00dev, "Device failed to enter state %d (%d).\n",
+ state, retval);
+
+ return retval;
+}
+
+/*
+ * TX descriptor initialization
+ */
+static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
+ struct sk_buff *skb,
+ struct txentry_desc *txdesc)
+{
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
+ __le32 *txd = skbdesc->desc;
+ __le32 *txwi = (__le32 *)(skb->data - rt2x00dev->ops->extra_tx_headroom);
+ u32 word;
+
+ /*
+ * Initialize TX Info descriptor
+ */
+ rt2x00_desc_read(txwi, 0, &word);
+ rt2x00_set_field32(&word, TXWI_W0_FRAG,
+ test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
+ rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0);
+ rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0);
+ rt2x00_set_field32(&word, TXWI_W0_TS,
+ test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
+ rt2x00_set_field32(&word, TXWI_W0_AMPDU,
+ test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags));
+ rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, txdesc->mpdu_density);
+ rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->ifs);
+ rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->mcs);
+ rt2x00_set_field32(&word, TXWI_W0_BW,
+ test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags));
+ rt2x00_set_field32(&word, TXWI_W0_SHORT_GI,
+ test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags));
+ rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->stbc);
+ rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode);
+ rt2x00_desc_write(txwi, 0, word);
+
+ rt2x00_desc_read(txwi, 1, &word);
+ rt2x00_set_field32(&word, TXWI_W1_ACK,
+ test_bit(ENTRY_TXD_ACK, &txdesc->flags));
+ rt2x00_set_field32(&word, TXWI_W1_NSEQ,
+ test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags));
+ rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->ba_size);
+ rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID,
+ test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
+ txdesc->key_idx : 0xff);
+ rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
+ skb->len - txdesc->l2pad);
+ rt2x00_set_field32(&word, TXWI_W1_PACKETID,
+ skbdesc->entry->queue->qid + 1);
+ rt2x00_desc_write(txwi, 1, word);
+
+ /*
+ * Always write 0 to IV/EIV fields, hardware will insert the IV
+ * from the IVEIV register when TXD_W3_WIV is set to 0.
+ * When TXD_W3_WIV is set to 1 it will use the IV data
+ * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which
+ * crypto entry in the registers should be used to encrypt the frame.
+ */
+ _rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */);
+ _rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */);
+
+ /*
+ * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1
+ * must contains a TXWI structure + 802.11 header + padding + 802.11
+ * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and
+ * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11
+ * data. It means that LAST_SEC0 is always 0.
+ */
+
+ /*
+ * Initialize TX descriptor
+ */
+ rt2x00_desc_read(txd, 0, &word);
+ rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma);
+ rt2x00_desc_write(txd, 0, word);
+
+ rt2x00_desc_read(txd, 1, &word);
+ rt2x00_set_field32(&word, TXD_W1_SD_LEN1, skb->len);
+ rt2x00_set_field32(&word, TXD_W1_LAST_SEC1,
+ !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
+ rt2x00_set_field32(&word, TXD_W1_BURST,
+ test_bit(ENTRY_TXD_BURST, &txdesc->flags));
+ rt2x00_set_field32(&word, TXD_W1_SD_LEN0,
+ rt2x00dev->ops->extra_tx_headroom);
+ rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0);
+ rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0);
+ rt2x00_desc_write(txd, 1, word);
+
+ rt2x00_desc_read(txd, 2, &word);
+ rt2x00_set_field32(&word, TXD_W2_SD_PTR1,
+ skbdesc->skb_dma + rt2x00dev->ops->extra_tx_headroom);
+ rt2x00_desc_write(txd, 2, word);
+
+ rt2x00_desc_read(txd, 3, &word);
+ rt2x00_set_field32(&word, TXD_W3_WIV,
+ !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags));
+ rt2x00_set_field32(&word, TXD_W3_QSEL, 2);
+ rt2x00_desc_write(txd, 3, word);
+}
+
+/*
+ * TX data initialization
+ */
+static void rt2800pci_write_beacon(struct queue_entry *entry)
+{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ unsigned int beacon_base;
+ u32 reg;
+
+ /*
+ * Disable beaconing while we are reloading the beacon data,
+ * otherwise we might be sending out invalid data.
+ */
+ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+ /*
+ * Write entire beacon with descriptor to register.
+ */
+ beacon_base = HW_BEACON_OFFSET(entry->entry_idx);
+ rt2800_register_multiwrite(rt2x00dev,
+ beacon_base,
+ skbdesc->desc, skbdesc->desc_len);
+ rt2800_register_multiwrite(rt2x00dev,
+ beacon_base + skbdesc->desc_len,
+ entry->skb->data, entry->skb->len);
+
+ /*
+ * Clean up beacon skb.
+ */
+ dev_kfree_skb_any(entry->skb);
+ entry->skb = NULL;
+}
+
+static void rt2800pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
+ const enum data_queue_qid queue_idx)
+{
+ struct data_queue *queue;
+ unsigned int idx, qidx = 0;
+ u32 reg;
+
+ if (queue_idx == QID_BEACON) {
+ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+ if (!rt2x00_get_field32(reg, BCN_TIME_CFG_BEACON_GEN)) {
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
+ rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+ }
+ return;
+ }
+
+ if (queue_idx > QID_HCCA && queue_idx != QID_MGMT)
+ return;
+
+ queue = rt2x00queue_get_queue(rt2x00dev, queue_idx);
+ idx = queue->index[Q_INDEX];
+
+ if (queue_idx == QID_MGMT)
+ qidx = 5;
+ else
+ qidx = queue_idx;
+
+ rt2800_register_write(rt2x00dev, TX_CTX_IDX(qidx), idx);
+}
+
+static void rt2800pci_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
+ const enum data_queue_qid qid)
+{
+ u32 reg;
+
+ if (qid == QID_BEACON) {
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, 0);
+ return;
+ }
+
+ rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, (qid == QID_AC_BE));
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, (qid == QID_AC_BK));
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, (qid == QID_AC_VI));
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, (qid == QID_AC_VO));
+ rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
+}
+
+/*
+ * RX control handlers
+ */
+static void rt2800pci_fill_rxdone(struct queue_entry *entry,
+ struct rxdone_entry_desc *rxdesc)
+{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ struct queue_entry_priv_pci *entry_priv = entry->priv_data;
+ __le32 *rxd = entry_priv->desc;
+ __le32 *rxwi = (__le32 *)entry->skb->data;
+ u32 rxd3;
+ u32 rxwi0;
+ u32 rxwi1;
+ u32 rxwi2;
+ u32 rxwi3;
+
+ rt2x00_desc_read(rxd, 3, &rxd3);
+ rt2x00_desc_read(rxwi, 0, &rxwi0);
+ rt2x00_desc_read(rxwi, 1, &rxwi1);
+ rt2x00_desc_read(rxwi, 2, &rxwi2);
+ rt2x00_desc_read(rxwi, 3, &rxwi3);
+
+ if (rt2x00_get_field32(rxd3, RXD_W3_CRC_ERROR))
+ rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
+
+ if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
+ /*
+ * Unfortunately we don't know the cipher type used during
+ * decryption. This prevents us from correct providing
+ * correct statistics through debugfs.
+ */
+ rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF);
+ rxdesc->cipher_status =
+ rt2x00_get_field32(rxd3, RXD_W3_CIPHER_ERROR);
+ }
+
+ if (rt2x00_get_field32(rxd3, RXD_W3_DECRYPTED)) {
+ /*
+ * Hardware has stripped IV/EIV data from 802.11 frame during
+ * decryption. Unfortunately the descriptor doesn't contain
+ * any fields with the EIV/IV data either, so they can't
+ * be restored by rt2x00lib.
+ */
+ rxdesc->flags |= RX_FLAG_IV_STRIPPED;
+
+ if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
+ rxdesc->flags |= RX_FLAG_DECRYPTED;
+ else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
+ rxdesc->flags |= RX_FLAG_MMIC_ERROR;
+ }
+
+ if (rt2x00_get_field32(rxd3, RXD_W3_MY_BSS))
+ rxdesc->dev_flags |= RXDONE_MY_BSS;
+
+ if (rt2x00_get_field32(rxd3, RXD_W3_L2PAD)) {
+ rxdesc->dev_flags |= RXDONE_L2PAD;
+ skbdesc->flags |= SKBDESC_L2_PADDED;
+ }
+
+ if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI))
+ rxdesc->flags |= RX_FLAG_SHORT_GI;
+
+ if (rt2x00_get_field32(rxwi1, RXWI_W1_BW))
+ rxdesc->flags |= RX_FLAG_40MHZ;
+
+ /*
+ * Detect RX rate, always use MCS as signal type.
+ */
+ rxdesc->dev_flags |= RXDONE_SIGNAL_MCS;
+ rxdesc->rate_mode = rt2x00_get_field32(rxwi1, RXWI_W1_PHYMODE);
+ rxdesc->signal = rt2x00_get_field32(rxwi1, RXWI_W1_MCS);
+
+ /*
+ * Mask of 0x8 bit to remove the short preamble flag.
+ */
+ if (rxdesc->rate_mode == RATE_MODE_CCK)
+ rxdesc->signal &= ~0x8;
+
+ rxdesc->rssi =
+ (rt2x00_get_field32(rxwi2, RXWI_W2_RSSI0) +
+ rt2x00_get_field32(rxwi2, RXWI_W2_RSSI1)) / 2;
+
+ rxdesc->noise =
+ (rt2x00_get_field32(rxwi3, RXWI_W3_SNR0) +
+ rt2x00_get_field32(rxwi3, RXWI_W3_SNR1)) / 2;
+
+ rxdesc->size = rt2x00_get_field32(rxwi0, RXWI_W0_MPDU_TOTAL_BYTE_COUNT);
+
+ /*
+ * Set RX IDX in register to inform hardware that we have handled
+ * this entry and it is available for reuse again.
+ */
+ rt2800_register_write(rt2x00dev, RX_CRX_IDX, entry->entry_idx);
+
+ /*
+ * Remove TXWI descriptor from start of buffer.
+ */
+ skb_pull(entry->skb, RXWI_DESC_SIZE);
+ skb_trim(entry->skb, rxdesc->size);
+}
+
+/*
+ * Interrupt functions.
+ */
+static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
+{
+ struct data_queue *queue;
+ struct queue_entry *entry;
+ struct queue_entry *entry_done;
+ struct queue_entry_priv_pci *entry_priv;
+ struct txdone_entry_desc txdesc;
+ u32 word;
+ u32 reg;
+ u32 old_reg;
+ unsigned int type;
+ unsigned int index;
+ u16 mcs, real_mcs;
+
+ /*
+ * During each loop we will compare the freshly read
+ * TX_STA_FIFO register value with the value read from
+ * the previous loop. If the 2 values are equal then
+ * we should stop processing because the chance it
+ * quite big that the device has been unplugged and
+ * we risk going into an endless loop.
+ */
+ old_reg = 0;
+
+ while (1) {
+ rt2800_register_read(rt2x00dev, TX_STA_FIFO, &reg);
+ if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
+ break;
+
+ if (old_reg == reg)
+ break;
+ old_reg = reg;
+
+ /*
+ * Skip this entry when it contains an invalid
+ * queue identication number.
+ */
+ type = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE) - 1;
+ if (type >= QID_RX)
+ continue;
+
+ queue = rt2x00queue_get_queue(rt2x00dev, type);
+ if (unlikely(!queue))
+ continue;
+
+ /*
+ * Skip this entry when it contains an invalid
+ * index number.
+ */
+ index = rt2x00_get_field32(reg, TX_STA_FIFO_WCID) - 1;
+ if (unlikely(index >= queue->limit))
+ continue;
+
+ entry = &queue->entries[index];
+ entry_priv = entry->priv_data;
+ rt2x00_desc_read((__le32 *)entry->skb->data, 0, &word);
+
+ entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
+ while (entry != entry_done) {
+ /*
+ * Catch up.
+ * Just report any entries we missed as failed.
+ */
+ WARNING(rt2x00dev,
+ "TX status report missed for entry %d\n",
+ entry_done->entry_idx);
+
+ txdesc.flags = 0;
+ __set_bit(TXDONE_UNKNOWN, &txdesc.flags);
+ txdesc.retry = 0;
+
+ rt2x00lib_txdone(entry_done, &txdesc);
+ entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
+ }
+
+ /*
+ * Obtain the status about this packet.
+ */
+ txdesc.flags = 0;
+ if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS))
+ __set_bit(TXDONE_SUCCESS, &txdesc.flags);
+ else
+ __set_bit(TXDONE_FAILURE, &txdesc.flags);
+
+ /*
+ * Ralink has a retry mechanism using a global fallback
+ * table. We setup this fallback table to try immediate
+ * lower rate for all rates. In the TX_STA_FIFO,
+ * the MCS field contains the MCS used for the successfull
+ * transmission. If the first transmission succeed,
+ * we have mcs == tx_mcs. On the second transmission,
+ * we have mcs = tx_mcs - 1. So the number of
+ * retry is (tx_mcs - mcs).
+ */
+ mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
+ real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
+ __set_bit(TXDONE_FALLBACK, &txdesc.flags);
+ txdesc.retry = mcs - min(mcs, real_mcs);
+
+ rt2x00lib_txdone(entry, &txdesc);
+ }
+}
+
+static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
+{
+ struct rt2x00_dev *rt2x00dev = dev_instance;
+ u32 reg;
+
+ /* Read status and ACK all interrupts */
+ rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
+ rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+
+ if (!reg)
+ return IRQ_NONE;
+
+ if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ return IRQ_HANDLED;
+
+ /*
+ * 1 - Rx ring done interrupt.
+ */
+ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
+ rt2x00pci_rxdone(rt2x00dev);
+
+ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
+ rt2800pci_txdone(rt2x00dev);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Device probe functions.
+ */
+static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
+{
+ /*
+ * Read EEPROM into buffer
+ */
+ switch (rt2x00dev->chip.rt) {
+ case RT2880:
+ case RT3052:
+ rt2800pci_read_eeprom_soc(rt2x00dev);
+ break;
+ default:
+ if (rt2800pci_efuse_detect(rt2x00dev))
+ rt2800pci_read_eeprom_efuse(rt2x00dev);
+ else
+ rt2800pci_read_eeprom_pci(rt2x00dev);
+ break;
+ }
+
+ return rt2800_validate_eeprom(rt2x00dev);
+}
+
+static const struct rt2800_ops rt2800pci_rt2800_ops = {
+ .register_read = rt2x00pci_register_read,
+ .register_read_lock = rt2x00pci_register_read, /* same for PCI */
+ .register_write = rt2x00pci_register_write,
+ .register_write_lock = rt2x00pci_register_write, /* same for PCI */
+
+ .register_multiread = rt2x00pci_register_multiread,
+ .register_multiwrite = rt2x00pci_register_multiwrite,
+
+ .regbusy_read = rt2x00pci_regbusy_read,
+};
+
+static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
+{
+ int retval;
+
+ rt2x00dev->priv = (void *)&rt2800pci_rt2800_ops;
+
+ /*
+ * Allocate eeprom data.
+ */
+ retval = rt2800pci_validate_eeprom(rt2x00dev);
+ if (retval)
+ return retval;
+
+ retval = rt2800_init_eeprom(rt2x00dev);
+ if (retval)
+ return retval;
+
+ /*
+ * Initialize hw specifications.
+ */
+ retval = rt2800_probe_hw_mode(rt2x00dev);
+ if (retval)
+ return retval;
+
+ /*
+ * This device has multiple filters for control frames
+ * and has a separate filter for PS Poll frames.
+ */
+ __set_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags);
+ __set_bit(DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, &rt2x00dev->flags);
+
+ /*
+ * This device requires firmware.
+ */
+ if (!rt2x00_rt(&rt2x00dev->chip, RT2880) &&
+ !rt2x00_rt(&rt2x00dev->chip, RT3052))
+ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags);
+ __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags);
+ __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags);
+ if (!modparam_nohwcrypt)
+ __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
+
+ /*
+ * Set the rssi offset.
+ */
+ rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
+
+ return 0;
+}
+
+static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
+ .irq_handler = rt2800pci_interrupt,
+ .probe_hw = rt2800pci_probe_hw,
+ .get_firmware_name = rt2800pci_get_firmware_name,
+ .check_firmware = rt2800pci_check_firmware,
+ .load_firmware = rt2800pci_load_firmware,
+ .initialize = rt2x00pci_initialize,
+ .uninitialize = rt2x00pci_uninitialize,
+ .get_entry_state = rt2800pci_get_entry_state,
+ .clear_entry = rt2800pci_clear_entry,
+ .set_device_state = rt2800pci_set_device_state,
+ .rfkill_poll = rt2800_rfkill_poll,
+ .link_stats = rt2800_link_stats,
+ .reset_tuner = rt2800_reset_tuner,
+ .link_tuner = rt2800_link_tuner,
+ .write_tx_desc = rt2800pci_write_tx_desc,
+ .write_tx_data = rt2x00pci_write_tx_data,
+ .write_beacon = rt2800pci_write_beacon,
+ .kick_tx_queue = rt2800pci_kick_tx_queue,
+ .kill_tx_queue = rt2800pci_kill_tx_queue,
+ .fill_rxdone = rt2800pci_fill_rxdone,
+ .config_shared_key = rt2800_config_shared_key,
+ .config_pairwise_key = rt2800_config_pairwise_key,
+ .config_filter = rt2800_config_filter,
+ .config_intf = rt2800_config_intf,
+ .config_erp = rt2800_config_erp,
+ .config_ant = rt2800_config_ant,
+ .config = rt2800_config,
+};
+
+static const struct data_queue_desc rt2800pci_queue_rx = {
+ .entry_num = RX_ENTRIES,
+ .data_size = AGGREGATION_SIZE,
+ .desc_size = RXD_DESC_SIZE,
+ .priv_size = sizeof(struct queue_entry_priv_pci),
+};
+
+static const struct data_queue_desc rt2800pci_queue_tx = {
+ .entry_num = TX_ENTRIES,
+ .data_size = AGGREGATION_SIZE,
+ .desc_size = TXD_DESC_SIZE,
+ .priv_size = sizeof(struct queue_entry_priv_pci),
+};
+
+static const struct data_queue_desc rt2800pci_queue_bcn = {
+ .entry_num = 8 * BEACON_ENTRIES,
+ .data_size = 0, /* No DMA required for beacons */
+ .desc_size = TXWI_DESC_SIZE,
+ .priv_size = sizeof(struct queue_entry_priv_pci),
+};
+
+static const struct rt2x00_ops rt2800pci_ops = {
+ .name = KBUILD_MODNAME,
+ .max_sta_intf = 1,
+ .max_ap_intf = 8,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .extra_tx_headroom = TXWI_DESC_SIZE,
+ .rx = &rt2800pci_queue_rx,
+ .tx = &rt2800pci_queue_tx,
+ .bcn = &rt2800pci_queue_bcn,
+ .lib = &rt2800pci_rt2x00_ops,
+ .hw = &rt2800_mac80211_ops,
+#ifdef CONFIG_RT2X00_LIB_DEBUGFS
+ .debugfs = &rt2800_rt2x00debug,
+#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
+};
+
+/*
+ * RT2800pci module information.
+ */
+static struct pci_device_id rt2800pci_device_table[] = {
+ { PCI_DEVICE(0x1462, 0x891a), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1432, 0x7708), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1432, 0x7727), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1432, 0x7728), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1432, 0x7738), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1432, 0x7748), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1432, 0x7758), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1432, 0x7768), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0601), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0681), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0701), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x0781), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x3060), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x3062), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x3090), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x3091), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x3092), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x3562), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1814, 0x3592), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { PCI_DEVICE(0x1a3b, 0x1059), PCI_DEVICE_DATA(&rt2800pci_ops) },
+ { 0, }
+};
+
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver.");
+MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards");
+#ifdef CONFIG_RT2800PCI_PCI
+MODULE_FIRMWARE(FIRMWARE_RT2860);
+MODULE_DEVICE_TABLE(pci, rt2800pci_device_table);
+#endif /* CONFIG_RT2800PCI_PCI */
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_RT2800PCI_WISOC
+#if defined(CONFIG_RALINK_RT288X)
+__rt2x00soc_probe(RT2880, &rt2800pci_ops);
+#elif defined(CONFIG_RALINK_RT305X)
+__rt2x00soc_probe(RT3052, &rt2800pci_ops);
+#endif
+
+static struct platform_driver rt2800soc_driver = {
+ .driver = {
+ .name = "rt2800_wmac",
+ .owner = THIS_MODULE,
+ .mod_name = KBUILD_MODNAME,
+ },
+ .probe = __rt2x00soc_probe,
+ .remove = __devexit_p(rt2x00soc_remove),
+ .suspend = rt2x00soc_suspend,
+ .resume = rt2x00soc_resume,
+};
+#endif /* CONFIG_RT2800PCI_WISOC */
+
+#ifdef CONFIG_RT2800PCI_PCI
+static struct pci_driver rt2800pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rt2800pci_device_table,
+ .probe = rt2x00pci_probe,
+ .remove = __devexit_p(rt2x00pci_remove),
+ .suspend = rt2x00pci_suspend,
+ .resume = rt2x00pci_resume,
+};
+#endif /* CONFIG_RT2800PCI_PCI */
+
+static int __init rt2800pci_init(void)
+{
+ int ret = 0;
+
+#ifdef CONFIG_RT2800PCI_WISOC
+ ret = platform_driver_register(&rt2800soc_driver);
+ if (ret)
+ return ret;
+#endif
+#ifdef CONFIG_RT2800PCI_PCI
+ ret = pci_register_driver(&rt2800pci_driver);
+ if (ret) {
+#ifdef CONFIG_RT2800PCI_WISOC
+ platform_driver_unregister(&rt2800soc_driver);
+#endif
+ return ret;
+ }
+#endif
+
+ return ret;
+}
+
+static void __exit rt2800pci_exit(void)
+{
+#ifdef CONFIG_RT2800PCI_PCI
+ pci_unregister_driver(&rt2800pci_driver);
+#endif
+#ifdef CONFIG_RT2800PCI_WISOC
+ platform_driver_unregister(&rt2800soc_driver);
+#endif
+}
+
+module_init(rt2800pci_init);
+module_exit(rt2800pci_exit);
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/rt2x00/rt2800pci.h
new file mode 100644
index 00000000000..afc8e7da27c
--- /dev/null
+++ b/drivers/net/wireless/rt2x00/rt2800pci.h
@@ -0,0 +1,159 @@
+/*
+ Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
+ Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
+ Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com>
+ Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ Module: rt2800pci
+ Abstract: Data structures and registers for the rt2800pci module.
+ Supported chipsets: RT2800E & RT2800ED.
+ */
+
+#ifndef RT2800PCI_H
+#define RT2800PCI_H
+
+/*
+ * PCI registers.
+ */
+
+/*
+ * E2PROM_CSR: EEPROM control register.
+ * RELOAD: Write 1 to reload eeprom content.
+ * TYPE: 0: 93c46, 1:93c66.
+ * LOAD_STATUS: 1:loading, 0:done.
+ */
+#define E2PROM_CSR 0x0004
+#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000001)
+#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000002)
+#define E2PROM_CSR_DATA_IN FIELD32(0x00000004)
+#define E2PROM_CSR_DATA_OUT FIELD32(0x00000008)
+#define E2PROM_CSR_TYPE FIELD32(0x00000030)
+#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040)
+#define E2PROM_CSR_RELOAD FIELD32(0x00000080)
+
+/*
+ * Queue register offset macros
+ */
+#define TX_QUEUE_REG_OFFSET 0x10
+#define TX_BASE_PTR(__x) TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)
+#define TX_MAX_CNT(__x) TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)
+#define TX_CTX_IDX(__x) TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)
+#define TX_DTX_IDX(__x) TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)
+
+/*
+ * 8051 firmware image.
+ */
+#define FIRMWARE_RT2860 "rt2860.bin"
+#define FIRMWARE_IMAGE_BASE 0x2000
+
+/*
+ * DMA descriptor defines.
+ */
+#define TXD_DESC_SIZE ( 4 * sizeof(__le32) )
+#define RXD_DESC_SIZE ( 4 * sizeof(__le32) )
+
+/*
+ * TX descriptor format for TX, PRIO and Beacon Ring.
+ */
+
+/*
+ * Word0
+ */
+#define TXD_W0_SD_PTR0 FIELD32(0xffffffff)
+
+/*
+ * Word1
+ */
+#define TXD_W1_SD_LEN1 FIELD32(0x00003fff)
+#define TXD_W1_LAST_SEC1 FIELD32(0x00004000)
+#define TXD_W1_BURST FIELD32(0x00008000)
+#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000)
+#define TXD_W1_LAST_SEC0 FIELD32(0x40000000)
+#define TXD_W1_DMA_DONE FIELD32(0x80000000)
+
+/*
+ * Word2
+ */
+#define TXD_W2_SD_PTR1 FIELD32(0xffffffff)
+
+/*
+ * Word3
+ * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI
+ * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler.
+ * 0:MGMT, 1:HCCA 2:EDCA
+ */
+#define TXD_W3_WIV FIELD32(0x01000000)
+#define TXD_W3_QSEL FIELD32(0x06000000)
+#define TXD_W3_TCO FIELD32(0x20000000)
+#define TXD_W3_UCO FIELD32(0x40000000)
+#define TXD_W3_ICO FIELD32(0x80000000)
+
+/*
+ * RX descriptor format for RX Ring.
+ */
+
+/*
+ * Word0
+ */
+#define RXD_W0_SDP0 FIELD32(0xffffffff)
+
+/*
+ * Word1
+ */
+#define RXD_W1_SDL1 FIELD32(0x00003fff)
+#define RXD_W1_SDL0 FIELD32(0x3fff0000)
+#define RXD_W1_LS0 FIELD32(0x40000000)
+#define RXD_W1_DMA_DONE FIELD32(0x80000000)
+
+/*
+ * Word2
+ */
+#define RXD_W2_SDP1 FIELD32(0xffffffff)
+
+/*
+ * Word3
+ * AMSDU: RX with 802.3 header, not 802.11 header.
+ * DECRYPTED: This frame is being decrypted.
+ */
+#define RXD_W3_BA FIELD32(0x00000001)
+#define RXD_W3_DATA FIELD32(0x00000002)
+#define RXD_W3_NULLDATA FIELD32(0x00000004)
+#define RXD_W3_FRAG FIELD32(0x00000008)
+#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010)
+#define RXD_W3_MULTICAST FIELD32(0x00000020)
+#define RXD_W3_BROADCAST FIELD32(0x00000040)
+#define RXD_W3_MY_BSS FIELD32(0x00000080)
+#define RXD_W3_CRC_ERROR FIELD32(0x00000100)
+#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600)
+#define RXD_W3_AMSDU FIELD32(0x00000800)
+#define RXD_W3_HTC FIELD32(0x00001000)
+#define RXD_W3_RSSI FIELD32(0x00002000)
+#define RXD_W3_L2PAD FIELD32(0x00004000)
+#define RXD_W3_AMPDU FIELD32(0x00008000)
+#define RXD_W3_DECRYPTED FIELD32(0x00010000)
+#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000)
+#define RXD_W3_PLCP_RSSI FIELD32(0x00040000)
+
+#endif /* RT2800PCI_H */
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 9fe770f7d7b..af85d18cdbe 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -1,5 +1,9 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ Copyright (C) 2009 Axel Kollhofer <rain_maker@root-forum.org>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -34,6 +38,8 @@
#include "rt2x00.h"
#include "rt2x00usb.h"
+#include "rt2800lib.h"
+#include "rt2800.h"
#include "rt2800usb.h"
/*
@@ -44,1027 +50,6 @@ module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
/*
- * Register access.
- * All access to the CSR registers will go through the methods
- * rt2x00usb_register_read and rt2x00usb_register_write.
- * BBP and RF register require indirect register access,
- * and use the CSR registers BBPCSR and RFCSR to achieve this.
- * These indirect registers work with busy bits,
- * and we will try maximal REGISTER_BUSY_COUNT times to access
- * the register while taking a REGISTER_BUSY_DELAY us delay
- * between each attampt. When the busy bit is still set at that time,
- * the access attempt is considered to have failed,
- * and we will print an error.
- * The _lock versions must be used if you already hold the csr_mutex
- */
-#define WAIT_FOR_BBP(__dev, __reg) \
- rt2x00usb_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg))
-#define WAIT_FOR_RFCSR(__dev, __reg) \
- rt2x00usb_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg))
-#define WAIT_FOR_RF(__dev, __reg) \
- rt2x00usb_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg))
-#define WAIT_FOR_MCU(__dev, __reg) \
- rt2x00usb_regbusy_read((__dev), H2M_MAILBOX_CSR, \
- H2M_MAILBOX_CSR_OWNER, (__reg))
-
-static void rt2800usb_bbp_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, const u8 value)
-{
- u32 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the BBP becomes available, afterwards we
- * can safely write the new data into the register.
- */
- if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field32(&reg, BBP_CSR_CFG_VALUE, value);
- rt2x00_set_field32(&reg, BBP_CSR_CFG_REGNUM, word);
- rt2x00_set_field32(&reg, BBP_CSR_CFG_BUSY, 1);
- rt2x00_set_field32(&reg, BBP_CSR_CFG_READ_CONTROL, 0);
-
- rt2x00usb_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg);
- }
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-static void rt2800usb_bbp_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u8 *value)
-{
- u32 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the BBP becomes available, afterwards we
- * can safely write the read request into the register.
- * After the data has been written, we wait until hardware
- * returns the correct value, if at any time the register
- * doesn't become available in time, reg will be 0xffffffff
- * which means we return 0xff to the caller.
- */
- if (WAIT_FOR_BBP(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field32(&reg, BBP_CSR_CFG_REGNUM, word);
- rt2x00_set_field32(&reg, BBP_CSR_CFG_BUSY, 1);
- rt2x00_set_field32(&reg, BBP_CSR_CFG_READ_CONTROL, 1);
-
- rt2x00usb_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg);
-
- WAIT_FOR_BBP(rt2x00dev, &reg);
- }
-
- *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE);
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-static void rt2800usb_rfcsr_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, const u8 value)
-{
- u32 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the RFCSR becomes available, afterwards we
- * can safely write the new data into the register.
- */
- if (WAIT_FOR_RFCSR(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field32(&reg, RF_CSR_CFG_DATA, value);
- rt2x00_set_field32(&reg, RF_CSR_CFG_REGNUM, word);
- rt2x00_set_field32(&reg, RF_CSR_CFG_WRITE, 1);
- rt2x00_set_field32(&reg, RF_CSR_CFG_BUSY, 1);
-
- rt2x00usb_register_write_lock(rt2x00dev, RF_CSR_CFG, reg);
- }
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-static void rt2800usb_rfcsr_read(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, u8 *value)
-{
- u32 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the RFCSR becomes available, afterwards we
- * can safely write the read request into the register.
- * After the data has been written, we wait until hardware
- * returns the correct value, if at any time the register
- * doesn't become available in time, reg will be 0xffffffff
- * which means we return 0xff to the caller.
- */
- if (WAIT_FOR_RFCSR(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field32(&reg, RF_CSR_CFG_REGNUM, word);
- rt2x00_set_field32(&reg, RF_CSR_CFG_WRITE, 0);
- rt2x00_set_field32(&reg, RF_CSR_CFG_BUSY, 1);
-
- rt2x00usb_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg);
-
- WAIT_FOR_RFCSR(rt2x00dev, &reg);
- }
-
- *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA);
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-static void rt2800usb_rf_write(struct rt2x00_dev *rt2x00dev,
- const unsigned int word, const u32 value)
-{
- u32 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the RF becomes available, afterwards we
- * can safely write the new data into the register.
- */
- if (WAIT_FOR_RF(rt2x00dev, &reg)) {
- reg = 0;
- rt2x00_set_field32(&reg, RF_CSR_CFG0_REG_VALUE_BW, value);
- rt2x00_set_field32(&reg, RF_CSR_CFG0_STANDBYMODE, 0);
- rt2x00_set_field32(&reg, RF_CSR_CFG0_SEL, 0);
- rt2x00_set_field32(&reg, RF_CSR_CFG0_BUSY, 1);
-
- rt2x00usb_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg);
- rt2x00_rf_write(rt2x00dev, word, value);
- }
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-static void rt2800usb_mcu_request(struct rt2x00_dev *rt2x00dev,
- const u8 command, const u8 token,
- const u8 arg0, const u8 arg1)
-{
- u32 reg;
-
- mutex_lock(&rt2x00dev->csr_mutex);
-
- /*
- * Wait until the MCU becomes available, afterwards we
- * can safely write the new data into the register.
- */
- if (WAIT_FOR_MCU(rt2x00dev, &reg)) {
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_OWNER, 1);
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_CMD_TOKEN, token);
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG0, arg0);
- rt2x00_set_field32(&reg, H2M_MAILBOX_CSR_ARG1, arg1);
- rt2x00usb_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg);
-
- reg = 0;
- rt2x00_set_field32(&reg, HOST_CMD_CSR_HOST_COMMAND, command);
- rt2x00usb_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg);
- }
-
- mutex_unlock(&rt2x00dev->csr_mutex);
-}
-
-#ifdef CONFIG_RT2X00_LIB_DEBUGFS
-static const struct rt2x00debug rt2800usb_rt2x00debug = {
- .owner = THIS_MODULE,
- .csr = {
- .read = rt2x00usb_register_read,
- .write = rt2x00usb_register_write,
- .flags = RT2X00DEBUGFS_OFFSET,
- .word_base = CSR_REG_BASE,
- .word_size = sizeof(u32),
- .word_count = CSR_REG_SIZE / sizeof(u32),
- },
- .eeprom = {
- .read = rt2x00_eeprom_read,
- .write = rt2x00_eeprom_write,
- .word_base = EEPROM_BASE,
- .word_size = sizeof(u16),
- .word_count = EEPROM_SIZE / sizeof(u16),
- },
- .bbp = {
- .read = rt2800usb_bbp_read,
- .write = rt2800usb_bbp_write,
- .word_base = BBP_BASE,
- .word_size = sizeof(u8),
- .word_count = BBP_SIZE / sizeof(u8),
- },
- .rf = {
- .read = rt2x00_rf_read,
- .write = rt2800usb_rf_write,
- .word_base = RF_BASE,
- .word_size = sizeof(u32),
- .word_count = RF_SIZE / sizeof(u32),
- },
-};
-#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
-
-static int rt2800usb_rfkill_poll(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
-
- rt2x00usb_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
- return rt2x00_get_field32(reg, GPIO_CTRL_CFG_BIT2);
-}
-
-#ifdef CONFIG_RT2X00_LIB_LEDS
-static void rt2800usb_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct rt2x00_led *led =
- container_of(led_cdev, struct rt2x00_led, led_dev);
- unsigned int enabled = brightness != LED_OFF;
- unsigned int bg_mode =
- (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ);
- unsigned int polarity =
- rt2x00_get_field16(led->rt2x00dev->led_mcu_reg,
- EEPROM_FREQ_LED_POLARITY);
- unsigned int ledmode =
- rt2x00_get_field16(led->rt2x00dev->led_mcu_reg,
- EEPROM_FREQ_LED_MODE);
-
- if (led->type == LED_TYPE_RADIO) {
- rt2800usb_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode,
- enabled ? 0x20 : 0);
- } else if (led->type == LED_TYPE_ASSOC) {
- rt2800usb_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode,
- enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20);
- } else if (led->type == LED_TYPE_QUALITY) {
- /*
- * The brightness is divided into 6 levels (0 - 5),
- * The specs tell us the following levels:
- * 0, 1 ,3, 7, 15, 31
- * to determine the level in a simple way we can simply
- * work with bitshifting:
- * (1 << level) - 1
- */
- rt2800usb_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff,
- (1 << brightness / (LED_FULL / 6)) - 1,
- polarity);
- }
-}
-
-static int rt2800usb_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on,
- unsigned long *delay_off)
-{
- struct rt2x00_led *led =
- container_of(led_cdev, struct rt2x00_led, led_dev);
- u32 reg;
-
- rt2x00usb_register_read(led->rt2x00dev, LED_CFG, &reg);
- rt2x00_set_field32(&reg, LED_CFG_ON_PERIOD, *delay_on);
- rt2x00_set_field32(&reg, LED_CFG_OFF_PERIOD, *delay_off);
- rt2x00_set_field32(&reg, LED_CFG_SLOW_BLINK_PERIOD, 3);
- rt2x00_set_field32(&reg, LED_CFG_R_LED_MODE, 3);
- rt2x00_set_field32(&reg, LED_CFG_G_LED_MODE, 12);
- rt2x00_set_field32(&reg, LED_CFG_Y_LED_MODE, 3);
- rt2x00_set_field32(&reg, LED_CFG_LED_POLAR, 1);
- rt2x00usb_register_write(led->rt2x00dev, LED_CFG, reg);
-
- return 0;
-}
-
-static void rt2800usb_init_led(struct rt2x00_dev *rt2x00dev,
- struct rt2x00_led *led,
- enum led_type type)
-{
- led->rt2x00dev = rt2x00dev;
- led->type = type;
- led->led_dev.brightness_set = rt2800usb_brightness_set;
- led->led_dev.blink_set = rt2800usb_blink_set;
- led->flags = LED_INITIALIZED;
-}
-#endif /* CONFIG_RT2X00_LIB_LEDS */
-
-/*
- * Configuration handlers.
- */
-static void rt2800usb_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_crypto *crypto,
- struct ieee80211_key_conf *key)
-{
- struct mac_wcid_entry wcid_entry;
- struct mac_iveiv_entry iveiv_entry;
- u32 offset;
- u32 reg;
-
- offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx);
-
- rt2x00usb_register_read(rt2x00dev, offset, &reg);
- rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_KEYTAB,
- !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE));
- rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER,
- (crypto->cmd == SET_KEY) * crypto->cipher);
- rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX,
- (crypto->cmd == SET_KEY) * crypto->bssidx);
- rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher);
- rt2x00usb_register_write(rt2x00dev, offset, reg);
-
- offset = MAC_IVEIV_ENTRY(key->hw_key_idx);
-
- memset(&iveiv_entry, 0, sizeof(iveiv_entry));
- if ((crypto->cipher == CIPHER_TKIP) ||
- (crypto->cipher == CIPHER_TKIP_NO_MIC) ||
- (crypto->cipher == CIPHER_AES))
- iveiv_entry.iv[3] |= 0x20;
- iveiv_entry.iv[3] |= key->keyidx << 6;
- rt2x00usb_register_multiwrite(rt2x00dev, offset,
- &iveiv_entry, sizeof(iveiv_entry));
-
- offset = MAC_WCID_ENTRY(key->hw_key_idx);
-
- memset(&wcid_entry, 0, sizeof(wcid_entry));
- if (crypto->cmd == SET_KEY)
- memcpy(&wcid_entry, crypto->address, ETH_ALEN);
- rt2x00usb_register_multiwrite(rt2x00dev, offset,
- &wcid_entry, sizeof(wcid_entry));
-}
-
-static int rt2800usb_config_shared_key(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_crypto *crypto,
- struct ieee80211_key_conf *key)
-{
- struct hw_key_entry key_entry;
- struct rt2x00_field32 field;
- int timeout;
- u32 offset;
- u32 reg;
-
- if (crypto->cmd == SET_KEY) {
- key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx;
-
- memcpy(key_entry.key, crypto->key,
- sizeof(key_entry.key));
- memcpy(key_entry.tx_mic, crypto->tx_mic,
- sizeof(key_entry.tx_mic));
- memcpy(key_entry.rx_mic, crypto->rx_mic,
- sizeof(key_entry.rx_mic));
-
- offset = SHARED_KEY_ENTRY(key->hw_key_idx);
- timeout = REGISTER_TIMEOUT32(sizeof(key_entry));
- rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT,
- offset, &key_entry,
- sizeof(key_entry),
- timeout);
- }
-
- /*
- * The cipher types are stored over multiple registers
- * starting with SHARED_KEY_MODE_BASE each word will have
- * 32 bits and contains the cipher types for 2 bssidx each.
- * Using the correct defines correctly will cause overhead,
- * so just calculate the correct offset.
- */
- field.bit_offset = 4 * (key->hw_key_idx % 8);
- field.bit_mask = 0x7 << field.bit_offset;
-
- offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8);
-
- rt2x00usb_register_read(rt2x00dev, offset, &reg);
- rt2x00_set_field32(&reg, field,
- (crypto->cmd == SET_KEY) * crypto->cipher);
- rt2x00usb_register_write(rt2x00dev, offset, reg);
-
- /*
- * Update WCID information
- */
- rt2800usb_config_wcid_attr(rt2x00dev, crypto, key);
-
- return 0;
-}
-
-static int rt2800usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_crypto *crypto,
- struct ieee80211_key_conf *key)
-{
- struct hw_key_entry key_entry;
- int timeout;
- u32 offset;
-
- if (crypto->cmd == SET_KEY) {
- /*
- * 1 pairwise key is possible per AID, this means that the AID
- * equals our hw_key_idx. Make sure the WCID starts _after_ the
- * last possible shared key entry.
- */
- if (crypto->aid > (256 - 32))
- return -ENOSPC;
-
- key->hw_key_idx = 32 + crypto->aid;
-
- memcpy(key_entry.key, crypto->key,
- sizeof(key_entry.key));
- memcpy(key_entry.tx_mic, crypto->tx_mic,
- sizeof(key_entry.tx_mic));
- memcpy(key_entry.rx_mic, crypto->rx_mic,
- sizeof(key_entry.rx_mic));
-
- offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx);
- timeout = REGISTER_TIMEOUT32(sizeof(key_entry));
- rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE,
- USB_VENDOR_REQUEST_OUT,
- offset, &key_entry,
- sizeof(key_entry),
- timeout);
- }
-
- /*
- * Update WCID information
- */
- rt2800usb_config_wcid_attr(rt2x00dev, crypto, key);
-
- return 0;
-}
-
-static void rt2800usb_config_filter(struct rt2x00_dev *rt2x00dev,
- const unsigned int filter_flags)
-{
- u32 reg;
-
- /*
- * Start configuration steps.
- * Note that the version error will always be dropped
- * and broadcast frames will always be accepted since
- * there is no filter for it at this time.
- */
- rt2x00usb_register_read(rt2x00dev, RX_FILTER_CFG, &reg);
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CRC_ERROR,
- !(filter_flags & FIF_FCSFAIL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_PHY_ERROR,
- !(filter_flags & FIF_PLCPFAIL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_NOT_TO_ME,
- !(filter_flags & FIF_PROMISC_IN_BSS));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0);
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_VER_ERROR, 1);
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_MULTICAST,
- !(filter_flags & FIF_ALLMULTI));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BROADCAST, 0);
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_DUPLICATE, 1);
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CF_END_ACK,
- !(filter_flags & FIF_CONTROL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CF_END,
- !(filter_flags & FIF_CONTROL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_ACK,
- !(filter_flags & FIF_CONTROL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CTS,
- !(filter_flags & FIF_CONTROL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_RTS,
- !(filter_flags & FIF_CONTROL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_PSPOLL,
- !(filter_flags & FIF_PSPOLL));
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BA, 1);
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_BAR, 0);
- rt2x00_set_field32(&reg, RX_FILTER_CFG_DROP_CNTL,
- !(filter_flags & FIF_CONTROL));
- rt2x00usb_register_write(rt2x00dev, RX_FILTER_CFG, reg);
-}
-
-static void rt2800usb_config_intf(struct rt2x00_dev *rt2x00dev,
- struct rt2x00_intf *intf,
- struct rt2x00intf_conf *conf,
- const unsigned int flags)
-{
- unsigned int beacon_base;
- u32 reg;
-
- if (flags & CONFIG_UPDATE_TYPE) {
- /*
- * Clear current synchronisation setup.
- * For the Beacon base registers we only need to clear
- * the first byte since that byte contains the VALID and OWNER
- * bits which (when set to 0) will invalidate the entire beacon.
- */
- beacon_base = HW_BEACON_OFFSET(intf->beacon->entry_idx);
- rt2x00usb_register_write(rt2x00dev, beacon_base, 0);
-
- /*
- * Enable synchronisation.
- */
- rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, conf->sync);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
- rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
- }
-
- if (flags & CONFIG_UPDATE_MAC) {
- reg = le32_to_cpu(conf->mac[1]);
- rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
- conf->mac[1] = cpu_to_le32(reg);
-
- rt2x00usb_register_multiwrite(rt2x00dev, MAC_ADDR_DW0,
- conf->mac, sizeof(conf->mac));
- }
-
- if (flags & CONFIG_UPDATE_BSSID) {
- reg = le32_to_cpu(conf->bssid[1]);
- rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 0);
- rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 0);
- conf->bssid[1] = cpu_to_le32(reg);
-
- rt2x00usb_register_multiwrite(rt2x00dev, MAC_BSSID_DW0,
- conf->bssid, sizeof(conf->bssid));
- }
-}
-
-static void rt2800usb_config_erp(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_erp *erp)
-{
- u32 reg;
-
- rt2x00usb_register_read(rt2x00dev, TX_TIMEOUT_CFG, &reg);
- rt2x00_set_field32(&reg, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 0x20);
- rt2x00usb_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
- rt2x00_set_field32(&reg, AUTO_RSP_CFG_BAC_ACK_POLICY,
- !!erp->short_preamble);
- rt2x00_set_field32(&reg, AUTO_RSP_CFG_AR_PREAMBLE,
- !!erp->short_preamble);
- rt2x00usb_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL,
- erp->cts_protection ? 2 : 0);
- rt2x00usb_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
-
- rt2x00usb_register_write(rt2x00dev, LEGACY_BASIC_RATE,
- erp->basic_rates);
- rt2x00usb_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
-
- rt2x00usb_register_read(rt2x00dev, BKOFF_SLOT_CFG, &reg);
- rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_SLOT_TIME, erp->slot_time);
- rt2x00_set_field32(&reg, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2);
- rt2x00usb_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, XIFS_TIME_CFG, &reg);
- rt2x00_set_field32(&reg, XIFS_TIME_CFG_CCKM_SIFS_TIME, erp->sifs);
- rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_SIFS_TIME, erp->sifs);
- rt2x00_set_field32(&reg, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4);
- rt2x00_set_field32(&reg, XIFS_TIME_CFG_EIFS, erp->eifs);
- rt2x00_set_field32(&reg, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1);
- rt2x00usb_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL,
- erp->beacon_int * 16);
- rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-}
-
-static void rt2800usb_config_ant(struct rt2x00_dev *rt2x00dev,
- struct antenna_setup *ant)
-{
- u8 r1;
- u8 r3;
-
- rt2800usb_bbp_read(rt2x00dev, 1, &r1);
- rt2800usb_bbp_read(rt2x00dev, 3, &r3);
-
- /*
- * Configure the TX antenna.
- */
- switch ((int)ant->tx) {
- case 1:
- rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0);
- break;
- case 2:
- rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
- break;
- case 3:
- /* Do nothing */
- break;
- }
-
- /*
- * Configure the RX antenna.
- */
- switch ((int)ant->rx) {
- case 1:
- rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0);
- break;
- case 2:
- rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1);
- break;
- case 3:
- rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2);
- break;
- }
-
- rt2800usb_bbp_write(rt2x00dev, 3, r3);
- rt2800usb_bbp_write(rt2x00dev, 1, r1);
-}
-
-static void rt2800usb_config_lna_gain(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf)
-{
- u16 eeprom;
- short lna_gain;
-
- if (libconf->rf.channel <= 14) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
- lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG);
- } else if (libconf->rf.channel <= 64) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
- lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0);
- } else if (libconf->rf.channel <= 128) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
- lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1);
- } else {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
- lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2);
- }
-
- rt2x00dev->lna_gain = lna_gain;
-}
-
-static void rt2800usb_config_channel_rt2x(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_conf *conf,
- struct rf_channel *rf,
- struct channel_info *info)
-{
- rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
-
- if (rt2x00dev->default_ant.tx == 1)
- rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1);
-
- if (rt2x00dev->default_ant.rx == 1) {
- rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1);
- rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1);
- } else if (rt2x00dev->default_ant.rx == 2)
- rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1);
-
- if (rf->channel > 14) {
- /*
- * When TX power is below 0, we should increase it by 7 to
- * make it a positive value (Minumum value is -7).
- * However this means that values between 0 and 7 have
- * double meaning, and we should set a 7DBm boost flag.
- */
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST,
- (info->tx_power1 >= 0));
-
- if (info->tx_power1 < 0)
- info->tx_power1 += 7;
-
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A,
- TXPOWER_A_TO_DEV(info->tx_power1));
-
- rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST,
- (info->tx_power2 >= 0));
-
- if (info->tx_power2 < 0)
- info->tx_power2 += 7;
-
- rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A,
- TXPOWER_A_TO_DEV(info->tx_power2));
- } else {
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G,
- TXPOWER_G_TO_DEV(info->tx_power1));
- rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G,
- TXPOWER_G_TO_DEV(info->tx_power2));
- }
-
- rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf));
-
- rt2800usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt2800usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt2800usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
- rt2800usb_rf_write(rt2x00dev, 4, rf->rf4);
-
- udelay(200);
-
- rt2800usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt2800usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt2800usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004);
- rt2800usb_rf_write(rt2x00dev, 4, rf->rf4);
-
- udelay(200);
-
- rt2800usb_rf_write(rt2x00dev, 1, rf->rf1);
- rt2800usb_rf_write(rt2x00dev, 2, rf->rf2);
- rt2800usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004);
- rt2800usb_rf_write(rt2x00dev, 4, rf->rf4);
-}
-
-static void rt2800usb_config_channel_rt3x(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_conf *conf,
- struct rf_channel *rf,
- struct channel_info *info)
-{
- u8 rfcsr;
-
- rt2800usb_rfcsr_write(rt2x00dev, 2, rf->rf1);
- rt2800usb_rfcsr_write(rt2x00dev, 2, rf->rf3);
-
- rt2800usb_rfcsr_read(rt2x00dev, 6, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR6_R, rf->rf2);
- rt2800usb_rfcsr_write(rt2x00dev, 6, rfcsr);
-
- rt2800usb_rfcsr_read(rt2x00dev, 12, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER,
- TXPOWER_G_TO_DEV(info->tx_power1));
- rt2800usb_rfcsr_write(rt2x00dev, 12, rfcsr);
-
- rt2800usb_rfcsr_read(rt2x00dev, 23, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
- rt2800usb_rfcsr_write(rt2x00dev, 23, rfcsr);
-
- rt2800usb_rfcsr_write(rt2x00dev, 24,
- rt2x00dev->calibration[conf_is_ht40(conf)]);
-
- rt2800usb_rfcsr_read(rt2x00dev, 23, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1);
- rt2800usb_rfcsr_write(rt2x00dev, 23, rfcsr);
-}
-
-static void rt2800usb_config_channel(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_conf *conf,
- struct rf_channel *rf,
- struct channel_info *info)
-{
- u32 reg;
- unsigned int tx_pin;
- u8 bbp;
-
- if (rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION)
- rt2800usb_config_channel_rt2x(rt2x00dev, conf, rf, info);
- else
- rt2800usb_config_channel_rt3x(rt2x00dev, conf, rf, info);
-
- /*
- * Change BBP settings
- */
- rt2800usb_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
- rt2800usb_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
- rt2800usb_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain);
- rt2800usb_bbp_write(rt2x00dev, 86, 0);
-
- if (rf->channel <= 14) {
- if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) {
- rt2800usb_bbp_write(rt2x00dev, 82, 0x62);
- rt2800usb_bbp_write(rt2x00dev, 75, 0x46);
- } else {
- rt2800usb_bbp_write(rt2x00dev, 82, 0x84);
- rt2800usb_bbp_write(rt2x00dev, 75, 0x50);
- }
- } else {
- rt2800usb_bbp_write(rt2x00dev, 82, 0xf2);
-
- if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags))
- rt2800usb_bbp_write(rt2x00dev, 75, 0x46);
- else
- rt2800usb_bbp_write(rt2x00dev, 75, 0x50);
- }
-
- rt2x00usb_register_read(rt2x00dev, TX_BAND_CFG, &reg);
- rt2x00_set_field32(&reg, TX_BAND_CFG_HT40_PLUS, conf_is_ht40_plus(conf));
- rt2x00_set_field32(&reg, TX_BAND_CFG_A, rf->channel > 14);
- rt2x00_set_field32(&reg, TX_BAND_CFG_BG, rf->channel <= 14);
- rt2x00usb_register_write(rt2x00dev, TX_BAND_CFG, reg);
-
- tx_pin = 0;
-
- /* Turn on unused PA or LNA when not using 1T or 1R */
- if (rt2x00dev->default_ant.tx != 1) {
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1);
- }
-
- /* Turn on unused PA or LNA when not using 1T or 1R */
- if (rt2x00dev->default_ant.rx != 1) {
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1);
- }
-
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, rf->channel <= 14);
- rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14);
-
- rt2x00usb_register_write(rt2x00dev, TX_PIN_CFG, tx_pin);
-
- rt2800usb_bbp_read(rt2x00dev, 4, &bbp);
- rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf));
- rt2800usb_bbp_write(rt2x00dev, 4, bbp);
-
- rt2800usb_bbp_read(rt2x00dev, 3, &bbp);
- rt2x00_set_field8(&bbp, BBP3_HT40_PLUS, conf_is_ht40_plus(conf));
- rt2800usb_bbp_write(rt2x00dev, 3, bbp);
-
- if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) {
- if (conf_is_ht40(conf)) {
- rt2800usb_bbp_write(rt2x00dev, 69, 0x1a);
- rt2800usb_bbp_write(rt2x00dev, 70, 0x0a);
- rt2800usb_bbp_write(rt2x00dev, 73, 0x16);
- } else {
- rt2800usb_bbp_write(rt2x00dev, 69, 0x16);
- rt2800usb_bbp_write(rt2x00dev, 70, 0x08);
- rt2800usb_bbp_write(rt2x00dev, 73, 0x11);
- }
- }
-
- msleep(1);
-}
-
-static void rt2800usb_config_txpower(struct rt2x00_dev *rt2x00dev,
- const int txpower)
-{
- u32 reg;
- u32 value = TXPOWER_G_TO_DEV(txpower);
- u8 r1;
-
- rt2800usb_bbp_read(rt2x00dev, 1, &r1);
- rt2x00_set_field8(&reg, BBP1_TX_POWER, 0);
- rt2800usb_bbp_write(rt2x00dev, 1, r1);
-
- rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_0, &reg);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_1MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_2MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_55MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_11MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_6MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_9MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_12MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_0_18MBS, value);
- rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_0, reg);
-
- rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_1, &reg);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_24MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_36MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_48MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_54MBS, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS0, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS1, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS2, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS3, value);
- rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_1, reg);
-
- rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_2, &reg);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS4, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS5, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS6, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS7, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS8, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS9, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS10, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS11, value);
- rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_2, reg);
-
- rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_3, &reg);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS12, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS13, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS14, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS15, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN1, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN2, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN3, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN4, value);
- rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_3, reg);
-
- rt2x00usb_register_read(rt2x00dev, TX_PWR_CFG_4, &reg);
- rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN5, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN6, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN7, value);
- rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN8, value);
- rt2x00usb_register_write(rt2x00dev, TX_PWR_CFG_4, reg);
-}
-
-static void rt2800usb_config_retry_limit(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf)
-{
- u32 reg;
-
- rt2x00usb_register_read(rt2x00dev, TX_RTY_CFG, &reg);
- rt2x00_set_field32(&reg, TX_RTY_CFG_SHORT_RTY_LIMIT,
- libconf->conf->short_frame_max_tx_count);
- rt2x00_set_field32(&reg, TX_RTY_CFG_LONG_RTY_LIMIT,
- libconf->conf->long_frame_max_tx_count);
- rt2x00_set_field32(&reg, TX_RTY_CFG_LONG_RTY_THRE, 2000);
- rt2x00_set_field32(&reg, TX_RTY_CFG_NON_AGG_RTY_MODE, 0);
- rt2x00_set_field32(&reg, TX_RTY_CFG_AGG_RTY_MODE, 0);
- rt2x00_set_field32(&reg, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1);
- rt2x00usb_register_write(rt2x00dev, TX_RTY_CFG, reg);
-}
-
-static void rt2800usb_config_ps(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf)
-{
- enum dev_state state =
- (libconf->conf->flags & IEEE80211_CONF_PS) ?
- STATE_SLEEP : STATE_AWAKE;
- u32 reg;
-
- if (state == STATE_SLEEP) {
- rt2x00usb_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0);
-
- rt2x00usb_register_read(rt2x00dev, AUTOWAKEUP_CFG, &reg);
- rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5);
- rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE,
- libconf->conf->listen_interval - 1);
- rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTOWAKE, 1);
- rt2x00usb_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg);
-
- rt2x00dev->ops->lib->set_device_state(rt2x00dev, state);
- } else {
- rt2x00dev->ops->lib->set_device_state(rt2x00dev, state);
-
- rt2x00usb_register_read(rt2x00dev, AUTOWAKEUP_CFG, &reg);
- rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0);
- rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0);
- rt2x00_set_field32(&reg, AUTOWAKEUP_CFG_AUTOWAKE, 0);
- rt2x00usb_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg);
- }
-}
-
-static void rt2800usb_config(struct rt2x00_dev *rt2x00dev,
- struct rt2x00lib_conf *libconf,
- const unsigned int flags)
-{
- /* Always recalculate LNA gain before changing configuration */
- rt2800usb_config_lna_gain(rt2x00dev, libconf);
-
- if (flags & IEEE80211_CONF_CHANGE_CHANNEL)
- rt2800usb_config_channel(rt2x00dev, libconf->conf,
- &libconf->rf, &libconf->channel);
- if (flags & IEEE80211_CONF_CHANGE_POWER)
- rt2800usb_config_txpower(rt2x00dev, libconf->conf->power_level);
- if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
- rt2800usb_config_retry_limit(rt2x00dev, libconf);
- if (flags & IEEE80211_CONF_CHANGE_PS)
- rt2800usb_config_ps(rt2x00dev, libconf);
-}
-
-/*
- * Link tuning
- */
-static void rt2800usb_link_stats(struct rt2x00_dev *rt2x00dev,
- struct link_qual *qual)
-{
- u32 reg;
-
- /*
- * Update FCS error count from register.
- */
- rt2x00usb_register_read(rt2x00dev, RX_STA_CNT0, &reg);
- qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR);
-}
-
-static u8 rt2800usb_get_default_vgc(struct rt2x00_dev *rt2x00dev)
-{
- if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
- if (rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION)
- return 0x1c + (2 * rt2x00dev->lna_gain);
- else
- return 0x2e + rt2x00dev->lna_gain;
- }
-
- if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
- return 0x32 + (rt2x00dev->lna_gain * 5) / 3;
- else
- return 0x3a + (rt2x00dev->lna_gain * 5) / 3;
-}
-
-static inline void rt2800usb_set_vgc(struct rt2x00_dev *rt2x00dev,
- struct link_qual *qual, u8 vgc_level)
-{
- if (qual->vgc_level != vgc_level) {
- rt2800usb_bbp_write(rt2x00dev, 66, vgc_level);
- qual->vgc_level = vgc_level;
- qual->vgc_level_reg = vgc_level;
- }
-}
-
-static void rt2800usb_reset_tuner(struct rt2x00_dev *rt2x00dev,
- struct link_qual *qual)
-{
- rt2800usb_set_vgc(rt2x00dev, qual,
- rt2800usb_get_default_vgc(rt2x00dev));
-}
-
-static void rt2800usb_link_tuner(struct rt2x00_dev *rt2x00dev,
- struct link_qual *qual, const u32 count)
-{
- if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION)
- return;
-
- /*
- * When RSSI is better then -80 increase VGC level with 0x10
- */
- rt2800usb_set_vgc(rt2x00dev, qual,
- rt2800usb_get_default_vgc(rt2x00dev) +
- ((qual->rssi > -80) * 0x10));
-}
-
-/*
* Firmware functions
*/
static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev)
@@ -1172,7 +157,7 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
* Wait for stable hardware.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00usb_register_read(rt2x00dev, MAC_CSR0, &reg);
+ rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
if (reg && reg != ~0)
break;
msleep(1);
@@ -1192,8 +177,8 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
data + offset, length,
REGISTER_TIMEOUT32(length));
- rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0);
- rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0);
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0);
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0);
/*
* Send firmware request to device to load firmware,
@@ -1208,18 +193,18 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
}
msleep(10);
- rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
/*
* Send signal to firmware during boot time.
*/
- rt2800usb_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0xff, 0, 0);
+ rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0xff, 0, 0);
if ((chipset == 0x3070) ||
(chipset == 0x3071) ||
(chipset == 0x3572)) {
udelay(200);
- rt2800usb_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0);
+ rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0);
udelay(10);
}
@@ -1227,7 +212,7 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
* Wait for device to stabilize.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
+ rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY))
break;
msleep(1);
@@ -1241,536 +226,14 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
/*
* Initialize firmware.
*/
- rt2x00usb_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
- rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
+ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
+ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
msleep(1);
return 0;
}
/*
- * Initialization functions.
- */
-static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- unsigned int i;
-
- /*
- * Wait untill BBP and RF are ready.
- */
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00usb_register_read(rt2x00dev, MAC_CSR0, &reg);
- if (reg && reg != ~0)
- break;
- msleep(1);
- }
-
- if (i == REGISTER_BUSY_COUNT) {
- ERROR(rt2x00dev, "Unstable hardware.\n");
- return -EBUSY;
- }
-
- rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
- rt2x00usb_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000);
-
- rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
- rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-
- rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000);
-
- rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0,
- USB_MODE_RESET, REGISTER_TIMEOUT);
-
- rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
-
- rt2x00usb_register_read(rt2x00dev, BCN_OFFSET0, &reg);
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */
- rt2x00usb_register_write(rt2x00dev, BCN_OFFSET0, reg);
-
- rt2x00usb_register_read(rt2x00dev, BCN_OFFSET1, &reg);
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */
- rt2x00usb_register_write(rt2x00dev, BCN_OFFSET1, reg);
-
- rt2x00usb_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
- rt2x00usb_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
-
- rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
-
- rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL, 0);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, 0);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
- rt2x00_set_field32(&reg, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0);
- rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
-
- if (rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) {
- rt2x00usb_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
- rt2x00usb_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000);
- rt2x00usb_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
- } else {
- rt2x00usb_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000);
- rt2x00usb_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
- }
-
- rt2x00usb_register_read(rt2x00dev, TX_LINK_CFG, &reg);
- rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32);
- rt2x00_set_field32(&reg, TX_LINK_CFG_MFB_ENABLE, 0);
- rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0);
- rt2x00_set_field32(&reg, TX_LINK_CFG_TX_MRQ_EN, 0);
- rt2x00_set_field32(&reg, TX_LINK_CFG_TX_RDG_EN, 0);
- rt2x00_set_field32(&reg, TX_LINK_CFG_TX_CF_ACK_EN, 1);
- rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_MFB, 0);
- rt2x00_set_field32(&reg, TX_LINK_CFG_REMOTE_MFS, 0);
- rt2x00usb_register_write(rt2x00dev, TX_LINK_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, TX_TIMEOUT_CFG, &reg);
- rt2x00_set_field32(&reg, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9);
- rt2x00_set_field32(&reg, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10);
- rt2x00usb_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, MAX_LEN_CFG, &reg);
- rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE);
- if (rt2x00_rev(&rt2x00dev->chip) >= RT2880E_VERSION &&
- rt2x00_rev(&rt2x00dev->chip) < RT3070_VERSION)
- rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_PSDU, 2);
- else
- rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_PSDU, 1);
- rt2x00_set_field32(&reg, MAX_LEN_CFG_MIN_PSDU, 0);
- rt2x00_set_field32(&reg, MAX_LEN_CFG_MIN_MPDU, 0);
- rt2x00usb_register_write(rt2x00dev, MAX_LEN_CFG, reg);
-
- rt2x00usb_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f);
-
- rt2x00usb_register_read(rt2x00dev, AUTO_RSP_CFG, &reg);
- rt2x00_set_field32(&reg, AUTO_RSP_CFG_AUTORESPONDER, 1);
- rt2x00_set_field32(&reg, AUTO_RSP_CFG_CTS_40_MMODE, 0);
- rt2x00_set_field32(&reg, AUTO_RSP_CFG_CTS_40_MREF, 0);
- rt2x00_set_field32(&reg, AUTO_RSP_CFG_DUAL_CTS_EN, 0);
- rt2x00_set_field32(&reg, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0);
- rt2x00usb_register_write(rt2x00dev, AUTO_RSP_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, CCK_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_PROTECT_RATE, 8);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_PROTECT_CTRL, 0);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_PROTECT_NAV, 1);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 1);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 1);
- rt2x00usb_register_write(rt2x00dev, CCK_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_RATE, 8);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_CTRL, 0);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_PROTECT_NAV, 1);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 1);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 1);
- rt2x00usb_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, MM20_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_RATE, 0x4004);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_CTRL, 0);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_PROTECT_NAV, 1);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0);
- rt2x00usb_register_write(rt2x00dev, MM20_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, MM40_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_RATE, 0x4084);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_CTRL, 0);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_PROTECT_NAV, 1);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1);
- rt2x00usb_register_write(rt2x00dev, MM40_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, GF20_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_RATE, 0x4004);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_CTRL, 0);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_PROTECT_NAV, 1);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0);
- rt2x00usb_register_write(rt2x00dev, GF20_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, GF40_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_RATE, 0x4084);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_CTRL, 0);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_PROTECT_NAV, 1);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1);
- rt2x00usb_register_write(rt2x00dev, GF40_PROT_CFG, reg);
-
- rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006);
-
- rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_BIG_ENDIAN, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_HDR_SEG_LEN, 0);
- rt2x00usb_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
-
- rt2x00usb_register_write(rt2x00dev, TXOP_CTRL_CFG, 0x0000583f);
- rt2x00usb_register_write(rt2x00dev, TXOP_HLDR_ET, 0x00000002);
-
- rt2x00usb_register_read(rt2x00dev, TX_RTS_CFG, &reg);
- rt2x00_set_field32(&reg, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32);
- rt2x00_set_field32(&reg, TX_RTS_CFG_RTS_THRES,
- IEEE80211_MAX_RTS_THRESHOLD);
- rt2x00_set_field32(&reg, TX_RTS_CFG_RTS_FBK_EN, 0);
- rt2x00usb_register_write(rt2x00dev, TX_RTS_CFG, reg);
-
- rt2x00usb_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca);
- rt2x00usb_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
-
- /*
- * ASIC will keep garbage value after boot, clear encryption keys.
- */
- for (i = 0; i < 4; i++)
- rt2x00usb_register_write(rt2x00dev,
- SHARED_KEY_MODE_ENTRY(i), 0);
-
- for (i = 0; i < 256; i++) {
- u32 wcid[2] = { 0xffffffff, 0x00ffffff };
- rt2x00usb_register_multiwrite(rt2x00dev, MAC_WCID_ENTRY(i),
- wcid, sizeof(wcid));
-
- rt2x00usb_register_write(rt2x00dev, MAC_WCID_ATTR_ENTRY(i), 1);
- rt2x00usb_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0);
- }
-
- /*
- * Clear all beacons
- * For the Beacon base registers we only need to clear
- * the first byte since that byte contains the VALID and OWNER
- * bits which (when set to 0) will invalidate the entire beacon.
- */
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0);
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0);
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0);
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0);
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE4, 0);
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE5, 0);
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE6, 0);
- rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE7, 0);
-
- rt2x00usb_register_read(rt2x00dev, USB_CYC_CFG, &reg);
- rt2x00_set_field32(&reg, USB_CYC_CFG_CLOCK_CYCLE, 30);
- rt2x00usb_register_write(rt2x00dev, USB_CYC_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, HT_FBK_CFG0, &reg);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS0FBK, 0);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS1FBK, 0);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS2FBK, 1);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS3FBK, 2);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS4FBK, 3);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS5FBK, 4);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS6FBK, 5);
- rt2x00_set_field32(&reg, HT_FBK_CFG0_HTMCS7FBK, 6);
- rt2x00usb_register_write(rt2x00dev, HT_FBK_CFG0, reg);
-
- rt2x00usb_register_read(rt2x00dev, HT_FBK_CFG1, &reg);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS8FBK, 8);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS9FBK, 8);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS10FBK, 9);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS11FBK, 10);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS12FBK, 11);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS13FBK, 12);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS14FBK, 13);
- rt2x00_set_field32(&reg, HT_FBK_CFG1_HTMCS15FBK, 14);
- rt2x00usb_register_write(rt2x00dev, HT_FBK_CFG1, reg);
-
- rt2x00usb_register_read(rt2x00dev, LG_FBK_CFG0, &reg);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS0FBK, 8);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS1FBK, 8);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS2FBK, 9);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS3FBK, 10);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS4FBK, 11);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS5FBK, 12);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS6FBK, 13);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_OFDMMCS7FBK, 14);
- rt2x00usb_register_write(rt2x00dev, LG_FBK_CFG0, reg);
-
- rt2x00usb_register_read(rt2x00dev, LG_FBK_CFG1, &reg);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS0FBK, 0);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS1FBK, 0);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS2FBK, 1);
- rt2x00_set_field32(&reg, LG_FBK_CFG0_CCKMCS3FBK, 2);
- rt2x00usb_register_write(rt2x00dev, LG_FBK_CFG1, reg);
-
- /*
- * We must clear the error counters.
- * These registers are cleared on read,
- * so we may pass a useless variable to store the value.
- */
- rt2x00usb_register_read(rt2x00dev, RX_STA_CNT0, &reg);
- rt2x00usb_register_read(rt2x00dev, RX_STA_CNT1, &reg);
- rt2x00usb_register_read(rt2x00dev, RX_STA_CNT2, &reg);
- rt2x00usb_register_read(rt2x00dev, TX_STA_CNT0, &reg);
- rt2x00usb_register_read(rt2x00dev, TX_STA_CNT1, &reg);
- rt2x00usb_register_read(rt2x00dev, TX_STA_CNT2, &reg);
-
- return 0;
-}
-
-static int rt2800usb_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev)
-{
- unsigned int i;
- u32 reg;
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00usb_register_read(rt2x00dev, MAC_STATUS_CFG, &reg);
- if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY))
- return 0;
-
- udelay(REGISTER_BUSY_DELAY);
- }
-
- ERROR(rt2x00dev, "BBP/RF register access failed, aborting.\n");
- return -EACCES;
-}
-
-static int rt2800usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
-{
- unsigned int i;
- u8 value;
-
- /*
- * BBP was enabled after firmware was loaded,
- * but we need to reactivate it now.
- */
- rt2x00usb_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
- rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
- msleep(1);
-
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2800usb_bbp_read(rt2x00dev, 0, &value);
- if ((value != 0xff) && (value != 0x00))
- return 0;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- ERROR(rt2x00dev, "BBP register access failed, aborting.\n");
- return -EACCES;
-}
-
-static int rt2800usb_init_bbp(struct rt2x00_dev *rt2x00dev)
-{
- unsigned int i;
- u16 eeprom;
- u8 reg_id;
- u8 value;
-
- if (unlikely(rt2800usb_wait_bbp_rf_ready(rt2x00dev) ||
- rt2800usb_wait_bbp_ready(rt2x00dev)))
- return -EACCES;
-
- rt2800usb_bbp_write(rt2x00dev, 65, 0x2c);
- rt2800usb_bbp_write(rt2x00dev, 66, 0x38);
- rt2800usb_bbp_write(rt2x00dev, 69, 0x12);
- rt2800usb_bbp_write(rt2x00dev, 70, 0x0a);
- rt2800usb_bbp_write(rt2x00dev, 73, 0x10);
- rt2800usb_bbp_write(rt2x00dev, 81, 0x37);
- rt2800usb_bbp_write(rt2x00dev, 82, 0x62);
- rt2800usb_bbp_write(rt2x00dev, 83, 0x6a);
- rt2800usb_bbp_write(rt2x00dev, 84, 0x99);
- rt2800usb_bbp_write(rt2x00dev, 86, 0x00);
- rt2800usb_bbp_write(rt2x00dev, 91, 0x04);
- rt2800usb_bbp_write(rt2x00dev, 92, 0x00);
- rt2800usb_bbp_write(rt2x00dev, 103, 0x00);
- rt2800usb_bbp_write(rt2x00dev, 105, 0x05);
-
- if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) {
- rt2800usb_bbp_write(rt2x00dev, 69, 0x16);
- rt2800usb_bbp_write(rt2x00dev, 73, 0x12);
- }
-
- if (rt2x00_rev(&rt2x00dev->chip) > RT2860D_VERSION) {
- rt2800usb_bbp_write(rt2x00dev, 84, 0x19);
- }
-
- if (rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) {
- rt2800usb_bbp_write(rt2x00dev, 70, 0x0a);
- rt2800usb_bbp_write(rt2x00dev, 84, 0x99);
- rt2800usb_bbp_write(rt2x00dev, 105, 0x05);
- }
-
- for (i = 0; i < EEPROM_BBP_SIZE; i++) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
-
- if (eeprom != 0xffff && eeprom != 0x0000) {
- reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID);
- value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE);
- rt2800usb_bbp_write(rt2x00dev, reg_id, value);
- }
- }
-
- return 0;
-}
-
-static u8 rt2800usb_init_rx_filter(struct rt2x00_dev *rt2x00dev,
- bool bw40, u8 rfcsr24, u8 filter_target)
-{
- unsigned int i;
- u8 bbp;
- u8 rfcsr;
- u8 passband;
- u8 stopband;
- u8 overtuned = 0;
-
- rt2800usb_rfcsr_write(rt2x00dev, 24, rfcsr24);
-
- rt2800usb_bbp_read(rt2x00dev, 4, &bbp);
- rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40);
- rt2800usb_bbp_write(rt2x00dev, 4, bbp);
-
- rt2800usb_rfcsr_read(rt2x00dev, 22, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1);
- rt2800usb_rfcsr_write(rt2x00dev, 22, rfcsr);
-
- /*
- * Set power & frequency of passband test tone
- */
- rt2800usb_bbp_write(rt2x00dev, 24, 0);
-
- for (i = 0; i < 100; i++) {
- rt2800usb_bbp_write(rt2x00dev, 25, 0x90);
- msleep(1);
-
- rt2800usb_bbp_read(rt2x00dev, 55, &passband);
- if (passband)
- break;
- }
-
- /*
- * Set power & frequency of stopband test tone
- */
- rt2800usb_bbp_write(rt2x00dev, 24, 0x06);
-
- for (i = 0; i < 100; i++) {
- rt2800usb_bbp_write(rt2x00dev, 25, 0x90);
- msleep(1);
-
- rt2800usb_bbp_read(rt2x00dev, 55, &stopband);
-
- if ((passband - stopband) <= filter_target) {
- rfcsr24++;
- overtuned += ((passband - stopband) == filter_target);
- } else
- break;
-
- rt2800usb_rfcsr_write(rt2x00dev, 24, rfcsr24);
- }
-
- rfcsr24 -= !!overtuned;
-
- rt2800usb_rfcsr_write(rt2x00dev, 24, rfcsr24);
- return rfcsr24;
-}
-
-static int rt2800usb_init_rfcsr(struct rt2x00_dev *rt2x00dev)
-{
- u8 rfcsr;
- u8 bbp;
-
- if (rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION)
- return 0;
-
- /*
- * Init RF calibration.
- */
- rt2800usb_rfcsr_read(rt2x00dev, 30, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1);
- rt2800usb_rfcsr_write(rt2x00dev, 30, rfcsr);
- msleep(1);
- rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0);
- rt2800usb_rfcsr_write(rt2x00dev, 30, rfcsr);
-
- rt2800usb_rfcsr_write(rt2x00dev, 4, 0x40);
- rt2800usb_rfcsr_write(rt2x00dev, 5, 0x03);
- rt2800usb_rfcsr_write(rt2x00dev, 6, 0x02);
- rt2800usb_rfcsr_write(rt2x00dev, 7, 0x70);
- rt2800usb_rfcsr_write(rt2x00dev, 9, 0x0f);
- rt2800usb_rfcsr_write(rt2x00dev, 10, 0x71);
- rt2800usb_rfcsr_write(rt2x00dev, 11, 0x21);
- rt2800usb_rfcsr_write(rt2x00dev, 12, 0x7b);
- rt2800usb_rfcsr_write(rt2x00dev, 14, 0x90);
- rt2800usb_rfcsr_write(rt2x00dev, 15, 0x58);
- rt2800usb_rfcsr_write(rt2x00dev, 16, 0xb3);
- rt2800usb_rfcsr_write(rt2x00dev, 17, 0x92);
- rt2800usb_rfcsr_write(rt2x00dev, 18, 0x2c);
- rt2800usb_rfcsr_write(rt2x00dev, 19, 0x02);
- rt2800usb_rfcsr_write(rt2x00dev, 20, 0xba);
- rt2800usb_rfcsr_write(rt2x00dev, 21, 0xdb);
- rt2800usb_rfcsr_write(rt2x00dev, 24, 0x16);
- rt2800usb_rfcsr_write(rt2x00dev, 25, 0x01);
- rt2800usb_rfcsr_write(rt2x00dev, 27, 0x03);
- rt2800usb_rfcsr_write(rt2x00dev, 29, 0x1f);
-
- /*
- * Set RX Filter calibration for 20MHz and 40MHz
- */
- rt2x00dev->calibration[0] =
- rt2800usb_init_rx_filter(rt2x00dev, false, 0x07, 0x16);
- rt2x00dev->calibration[1] =
- rt2800usb_init_rx_filter(rt2x00dev, true, 0x27, 0x19);
-
- /*
- * Set back to initial state
- */
- rt2800usb_bbp_write(rt2x00dev, 24, 0);
-
- rt2800usb_rfcsr_read(rt2x00dev, 22, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0);
- rt2800usb_rfcsr_write(rt2x00dev, 22, rfcsr);
-
- /*
- * set BBP back to BW20
- */
- rt2800usb_bbp_read(rt2x00dev, 4, &bbp);
- rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0);
- rt2800usb_bbp_write(rt2x00dev, 4, bbp);
-
- return 0;
-}
-
-/*
* Device state switch handlers.
*/
static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev,
@@ -1778,11 +241,11 @@ static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX,
(state == STATE_RADIO_RX_ON) ||
(state == STATE_RADIO_RX_ON_LINK));
- rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
}
static int rt2800usb_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
@@ -1791,7 +254,7 @@ static int rt2800usb_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
u32 reg;
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) &&
!rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY))
return 0;
@@ -1812,25 +275,25 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
* Initialize all registers.
*/
if (unlikely(rt2800usb_wait_wpdma_ready(rt2x00dev) ||
- rt2800usb_init_registers(rt2x00dev) ||
- rt2800usb_init_bbp(rt2x00dev) ||
- rt2800usb_init_rfcsr(rt2x00dev)))
+ rt2800_init_registers(rt2x00dev) ||
+ rt2800_init_bbp(rt2x00dev) ||
+ rt2800_init_rfcsr(rt2x00dev)))
return -EIO;
- rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
- rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
udelay(50);
- rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1);
- rt2x00usb_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
- rt2x00usb_register_read(rt2x00dev, USB_DMA_CFG, &reg);
+ rt2800_register_read(rt2x00dev, USB_DMA_CFG, &reg);
rt2x00_set_field32(&reg, USB_DMA_CFG_PHY_CLEAR, 0);
/* Don't use bulk in aggregation when working with USB 1.1 */
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_EN,
@@ -1844,26 +307,26 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
((RX_ENTRIES * DATA_FRAME_SIZE) / 1024) - 3);
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_EN, 1);
rt2x00_set_field32(&reg, USB_DMA_CFG_TX_BULK_EN, 1);
- rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg);
+ rt2800_register_write(rt2x00dev, USB_DMA_CFG, reg);
- rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
- rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
/*
* Initialize LED control
*/
rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word);
- rt2800usb_mcu_request(rt2x00dev, MCU_LED_1, 0xff,
+ rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff,
word & 0xff, (word >> 8) & 0xff);
rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word);
- rt2800usb_mcu_request(rt2x00dev, MCU_LED_2, 0xff,
+ rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff,
word & 0xff, (word >> 8) & 0xff);
rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word);
- rt2800usb_mcu_request(rt2x00dev, MCU_LED_3, 0xff,
+ rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff,
word & 0xff, (word >> 8) & 0xff);
return 0;
@@ -1873,14 +336,14 @@ static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev)
{
u32 reg;
- rt2x00usb_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
- rt2x00usb_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
- rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0);
- rt2x00usb_register_write(rt2x00dev, PWR_PIN_CFG, 0);
- rt2x00usb_register_write(rt2x00dev, TX_PIN_CFG, 0);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0);
+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0);
+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0);
/* Wait for DMA, ignore error */
rt2800usb_wait_wpdma_ready(rt2x00dev);
@@ -1892,9 +355,9 @@ static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev,
enum dev_state state)
{
if (state == STATE_AWAKE)
- rt2800usb_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0);
+ rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0);
else
- rt2800usb_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 2);
+ rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 2);
return 0;
}
@@ -2048,9 +511,9 @@ static void rt2800usb_write_beacon(struct queue_entry *entry)
* Disable beaconing while we are reloading the beacon data,
* otherwise we might be sending out invalid data.
*/
- rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
- rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
/*
* Write entire beacon with descriptor to register.
@@ -2093,12 +556,12 @@ static void rt2800usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
return;
}
- rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
+ rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
if (!rt2x00_get_field32(reg, BCN_TIME_CFG_BEACON_GEN)) {
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
- rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+ rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
}
}
@@ -2124,7 +587,7 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
*/
memcpy(skbdesc->desc, rxd, skbdesc->desc_len);
rxd = (__le32 *)skbdesc->desc;
- rxwi = &rxd[RXD_DESC_SIZE / sizeof(__le32)];
+ rxwi = &rxd[RXINFO_DESC_SIZE / sizeof(__le32)];
/*
* It is now safe to read the descriptor on all architectures.
@@ -2135,16 +598,16 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
rt2x00_desc_read(rxwi, 2, &rxwi2);
rt2x00_desc_read(rxwi, 3, &rxwi3);
- if (rt2x00_get_field32(rxd0, RXD_W0_CRC_ERROR))
+ if (rt2x00_get_field32(rxd0, RXINFO_W0_CRC_ERROR))
rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF);
rxdesc->cipher_status =
- rt2x00_get_field32(rxd0, RXD_W0_CIPHER_ERROR);
+ rt2x00_get_field32(rxd0, RXINFO_W0_CIPHER_ERROR);
}
- if (rt2x00_get_field32(rxd0, RXD_W0_DECRYPTED)) {
+ if (rt2x00_get_field32(rxd0, RXINFO_W0_DECRYPTED)) {
/*
* Hardware has stripped IV/EIV data from 802.11 frame during
* decryption. Unfortunately the descriptor doesn't contain
@@ -2159,10 +622,10 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
rxdesc->flags |= RX_FLAG_MMIC_ERROR;
}
- if (rt2x00_get_field32(rxd0, RXD_W0_MY_BSS))
+ if (rt2x00_get_field32(rxd0, RXINFO_W0_MY_BSS))
rxdesc->dev_flags |= RXDONE_MY_BSS;
- if (rt2x00_get_field32(rxd0, RXD_W0_L2PAD)) {
+ if (rt2x00_get_field32(rxd0, RXINFO_W0_L2PAD)) {
rxdesc->dev_flags |= RXDONE_L2PAD;
skbdesc->flags |= SKBDESC_L2_PADDED;
}
@@ -2208,402 +671,33 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
*/
static int rt2800usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
{
- u16 word;
- u8 *mac;
- u8 default_lna_gain;
-
- rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE);
-
- /*
- * Start validation of the data that has been read.
- */
- mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
- if (!is_valid_ether_addr(mac)) {
- random_ether_addr(mac);
- EEPROM(rt2x00dev, "MAC: %pM\n", mac);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_TXPATH, 1);
- rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2820);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word);
- EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word);
- } else if (rt2x00_rev(&rt2x00dev->chip) < RT2883_VERSION) {
- /*
- * There is a max of 2 RX streams for RT2870 series
- */
- if (rt2x00_get_field16(word, EEPROM_ANTENNA_RXPATH) > 2)
- rt2x00_set_field16(&word, EEPROM_ANTENNA_RXPATH, 2);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word);
- if (word == 0xffff) {
- rt2x00_set_field16(&word, EEPROM_NIC_HW_RADIO, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_DYNAMIC_TX_AGC, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_BG, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_BW40M_SB_A, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_WPS_PBC, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_BW40M_BG, 0);
- rt2x00_set_field16(&word, EEPROM_NIC_BW40M_A, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word);
- EEPROM(rt2x00dev, "NIC: 0x%04x\n", word);
- }
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
- if ((word & 0x00ff) == 0x00ff) {
- rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0);
- rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE,
- LED_MODE_TXRX_ACTIVITY);
- rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_LED1, 0x5555);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_LED2, 0x2221);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_LED3, 0xa9f8);
- EEPROM(rt2x00dev, "Freq: 0x%04x\n", word);
- }
-
- /*
- * During the LNA validation we are going to use
- * lna0 as correct value. Note that EEPROM_LNA
- * is never validated.
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
- default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
- if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10)
- rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0);
- if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10)
- rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
- if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10)
- rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0);
- if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
- rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
- rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
- default_lna_gain);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
- if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10)
- rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0);
- if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10)
- rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
- if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10)
- rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0);
- if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
- rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
- rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
- default_lna_gain);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+ if (rt2800_efuse_detect(rt2x00dev))
+ rt2800_read_eeprom_efuse(rt2x00dev);
+ else
+ rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom,
+ EEPROM_SIZE);
- return 0;
+ return rt2800_validate_eeprom(rt2x00dev);
}
-static int rt2800usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- u16 value;
- u16 eeprom;
-
- /*
- * Read EEPROM word for configuration.
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom);
-
- /*
- * Identify RF chipset.
- */
- value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
- rt2x00usb_register_read(rt2x00dev, MAC_CSR0, &reg);
- rt2x00_set_chip(rt2x00dev, RT2870, value, reg);
-
- /*
- * The check for rt2860 is not a typo, some rt2870 hardware
- * identifies itself as rt2860 in the CSR register.
- */
- if (!rt2x00_check_rev(&rt2x00dev->chip, 0xfff00000, 0x28600000) &&
- !rt2x00_check_rev(&rt2x00dev->chip, 0xfff00000, 0x28700000) &&
- !rt2x00_check_rev(&rt2x00dev->chip, 0xfff00000, 0x28800000) &&
- !rt2x00_check_rev(&rt2x00dev->chip, 0xffff0000, 0x30700000)) {
- ERROR(rt2x00dev, "Invalid RT chipset detected.\n");
- return -ENODEV;
- }
-
- if (!rt2x00_rf(&rt2x00dev->chip, RF2820) &&
- !rt2x00_rf(&rt2x00dev->chip, RF2850) &&
- !rt2x00_rf(&rt2x00dev->chip, RF2720) &&
- !rt2x00_rf(&rt2x00dev->chip, RF2750) &&
- !rt2x00_rf(&rt2x00dev->chip, RF3020) &&
- !rt2x00_rf(&rt2x00dev->chip, RF2020)) {
- ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
- return -ENODEV;
- }
-
- /*
- * Identify default antenna configuration.
- */
- rt2x00dev->default_ant.tx =
- rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH);
- rt2x00dev->default_ant.rx =
- rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH);
-
- /*
- * Read frequency offset and RF programming sequence.
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
- rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET);
-
- /*
- * Read external LNA informations.
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom);
-
- if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A))
- __set_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags);
- if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG))
- __set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags);
-
- /*
- * Detect if this device has an hardware controlled radio.
- */
- if (rt2x00_get_field16(eeprom, EEPROM_NIC_HW_RADIO))
- __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
-
- /*
- * Store led settings, for correct led behaviour.
- */
-#ifdef CONFIG_RT2X00_LIB_LEDS
- rt2800usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO);
- rt2800usb_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC);
- rt2800usb_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY);
+static const struct rt2800_ops rt2800usb_rt2800_ops = {
+ .register_read = rt2x00usb_register_read,
+ .register_read_lock = rt2x00usb_register_read_lock,
+ .register_write = rt2x00usb_register_write,
+ .register_write_lock = rt2x00usb_register_write_lock,
- rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ,
- &rt2x00dev->led_mcu_reg);
-#endif /* CONFIG_RT2X00_LIB_LEDS */
+ .register_multiread = rt2x00usb_register_multiread,
+ .register_multiwrite = rt2x00usb_register_multiwrite,
- return 0;
-}
-
-/*
- * RF value list for rt2870
- * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750)
- */
-static const struct rf_channel rf_vals[] = {
- { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b },
- { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f },
- { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b },
- { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f },
- { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b },
- { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f },
- { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b },
- { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f },
- { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b },
- { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f },
- { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b },
- { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f },
- { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b },
- { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 },
-
- /* 802.11 UNI / HyperLan 2 */
- { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 },
- { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 },
- { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 },
- { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 },
- { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b },
- { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b },
- { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 },
- { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 },
- { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b },
- { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 },
- { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 },
- { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 },
-
- /* 802.11 HyperLan 2 */
- { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 },
- { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 },
- { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 },
- { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 },
- { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 },
- { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b },
- { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 },
- { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 },
- { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 },
- { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 },
- { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b },
- { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 },
- { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b },
- { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 },
- { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b },
- { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 },
-
- /* 802.11 UNII */
- { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 },
- { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 },
- { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f },
- { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f },
- { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 },
- { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 },
- { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 },
- { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f },
- { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 },
- { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 },
- { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f },
-
- /* 802.11 Japan */
- { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b },
- { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 },
- { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b },
- { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 },
- { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 },
- { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b },
- { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 },
-};
-
-/*
- * RF value list for rt3070
- * Supports: 2.4 GHz
- */
-static const struct rf_channel rf_vals_3070[] = {
- {1, 241, 2, 2 },
- {2, 241, 2, 7 },
- {3, 242, 2, 2 },
- {4, 242, 2, 7 },
- {5, 243, 2, 2 },
- {6, 243, 2, 7 },
- {7, 244, 2, 2 },
- {8, 244, 2, 7 },
- {9, 245, 2, 2 },
- {10, 245, 2, 7 },
- {11, 246, 2, 2 },
- {12, 246, 2, 7 },
- {13, 247, 2, 2 },
- {14, 248, 2, 4 },
+ .regbusy_read = rt2x00usb_regbusy_read,
};
-static int rt2800usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
-{
- struct hw_mode_spec *spec = &rt2x00dev->spec;
- struct channel_info *info;
- char *tx_power1;
- char *tx_power2;
- unsigned int i;
- u16 eeprom;
-
- /*
- * Initialize all hw fields.
- */
- rt2x00dev->hw->flags =
- IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
- IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_SUPPORTS_PS |
- IEEE80211_HW_PS_NULLFUNC_STACK;
- rt2x00dev->hw->extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE;
-
- SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
- SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
- rt2x00_eeprom_addr(rt2x00dev,
- EEPROM_MAC_ADDR_0));
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom);
-
- /*
- * Initialize HT information.
- */
- spec->ht.ht_supported = true;
- spec->ht.cap =
- IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_GRN_FLD |
- IEEE80211_HT_CAP_SGI_20 |
- IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_TX_STBC |
- IEEE80211_HT_CAP_RX_STBC |
- IEEE80211_HT_CAP_PSMP_SUPPORT;
- spec->ht.ampdu_factor = 3;
- spec->ht.ampdu_density = 4;
- spec->ht.mcs.tx_params =
- IEEE80211_HT_MCS_TX_DEFINED |
- IEEE80211_HT_MCS_TX_RX_DIFF |
- ((rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TXPATH) - 1) <<
- IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
-
- switch (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RXPATH)) {
- case 3:
- spec->ht.mcs.rx_mask[2] = 0xff;
- case 2:
- spec->ht.mcs.rx_mask[1] = 0xff;
- case 1:
- spec->ht.mcs.rx_mask[0] = 0xff;
- spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */
- break;
- }
-
- /*
- * Initialize hw_mode information.
- */
- spec->supported_bands = SUPPORT_BAND_2GHZ;
- spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
-
- if (rt2x00_rf(&rt2x00dev->chip, RF2820) ||
- rt2x00_rf(&rt2x00dev->chip, RF2720)) {
- spec->num_channels = 14;
- spec->channels = rf_vals;
- } else if (rt2x00_rf(&rt2x00dev->chip, RF2850) ||
- rt2x00_rf(&rt2x00dev->chip, RF2750)) {
- spec->supported_bands |= SUPPORT_BAND_5GHZ;
- spec->num_channels = ARRAY_SIZE(rf_vals);
- spec->channels = rf_vals;
- } else if (rt2x00_rf(&rt2x00dev->chip, RF3020) ||
- rt2x00_rf(&rt2x00dev->chip, RF2020)) {
- spec->num_channels = ARRAY_SIZE(rf_vals_3070);
- spec->channels = rf_vals_3070;
- }
-
- /*
- * Create channel information array
- */
- info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- spec->channels_info = info;
-
- tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
- tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
-
- for (i = 0; i < 14; i++) {
- info[i].tx_power1 = TXPOWER_G_FROM_DEV(tx_power1[i]);
- info[i].tx_power2 = TXPOWER_G_FROM_DEV(tx_power2[i]);
- }
-
- if (spec->num_channels > 14) {
- tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
- tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
-
- for (i = 14; i < spec->num_channels; i++) {
- info[i].tx_power1 = TXPOWER_A_FROM_DEV(tx_power1[i]);
- info[i].tx_power2 = TXPOWER_A_FROM_DEV(tx_power2[i]);
- }
- }
-
- return 0;
-}
-
static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ rt2x00dev->priv = (void *)&rt2800usb_rt2800_ops;
+
/*
* Allocate eeprom data.
*/
@@ -2611,14 +705,14 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
if (retval)
return retval;
- retval = rt2800usb_init_eeprom(rt2x00dev);
+ retval = rt2800_init_eeprom(rt2x00dev);
if (retval)
return retval;
/*
* Initialize hw specifications.
*/
- retval = rt2800usb_probe_hw_mode(rt2x00dev);
+ retval = rt2800_probe_hw_mode(rt2x00dev);
if (retval)
return retval;
@@ -2645,162 +739,6 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return 0;
}
-/*
- * IEEE80211 stack callback functions.
- */
-static void rt2800usb_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx,
- u32 *iv32, u16 *iv16)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- struct mac_iveiv_entry iveiv_entry;
- u32 offset;
-
- offset = MAC_IVEIV_ENTRY(hw_key_idx);
- rt2x00usb_register_multiread(rt2x00dev, offset,
- &iveiv_entry, sizeof(iveiv_entry));
-
- memcpy(&iveiv_entry.iv[0], iv16, sizeof(iv16));
- memcpy(&iveiv_entry.iv[4], iv32, sizeof(iv32));
-}
-
-static int rt2800usb_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- u32 reg;
- bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD);
-
- rt2x00usb_register_read(rt2x00dev, TX_RTS_CFG, &reg);
- rt2x00_set_field32(&reg, TX_RTS_CFG_RTS_THRES, value);
- rt2x00usb_register_write(rt2x00dev, TX_RTS_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, CCK_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, CCK_PROT_CFG_RTS_TH_EN, enabled);
- rt2x00usb_register_write(rt2x00dev, CCK_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, OFDM_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, OFDM_PROT_CFG_RTS_TH_EN, enabled);
- rt2x00usb_register_write(rt2x00dev, OFDM_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, MM20_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, MM20_PROT_CFG_RTS_TH_EN, enabled);
- rt2x00usb_register_write(rt2x00dev, MM20_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, MM40_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, MM40_PROT_CFG_RTS_TH_EN, enabled);
- rt2x00usb_register_write(rt2x00dev, MM40_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, GF20_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, GF20_PROT_CFG_RTS_TH_EN, enabled);
- rt2x00usb_register_write(rt2x00dev, GF20_PROT_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, GF40_PROT_CFG, &reg);
- rt2x00_set_field32(&reg, GF40_PROT_CFG_RTS_TH_EN, enabled);
- rt2x00usb_register_write(rt2x00dev, GF40_PROT_CFG, reg);
-
- return 0;
-}
-
-static int rt2800usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
- const struct ieee80211_tx_queue_params *params)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- struct data_queue *queue;
- struct rt2x00_field32 field;
- int retval;
- u32 reg;
- u32 offset;
-
- /*
- * First pass the configuration through rt2x00lib, that will
- * update the queue settings and validate the input. After that
- * we are free to update the registers based on the value
- * in the queue parameter.
- */
- retval = rt2x00mac_conf_tx(hw, queue_idx, params);
- if (retval)
- return retval;
-
- /*
- * We only need to perform additional register initialization
- * for WMM queues/
- */
- if (queue_idx >= 4)
- return 0;
-
- queue = rt2x00queue_get_queue(rt2x00dev, queue_idx);
-
- /* Update WMM TXOP register */
- offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2)));
- field.bit_offset = (queue_idx & 1) * 16;
- field.bit_mask = 0xffff << field.bit_offset;
-
- rt2x00usb_register_read(rt2x00dev, offset, &reg);
- rt2x00_set_field32(&reg, field, queue->txop);
- rt2x00usb_register_write(rt2x00dev, offset, reg);
-
- /* Update WMM registers */
- field.bit_offset = queue_idx * 4;
- field.bit_mask = 0xf << field.bit_offset;
-
- rt2x00usb_register_read(rt2x00dev, WMM_AIFSN_CFG, &reg);
- rt2x00_set_field32(&reg, field, queue->aifs);
- rt2x00usb_register_write(rt2x00dev, WMM_AIFSN_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, WMM_CWMIN_CFG, &reg);
- rt2x00_set_field32(&reg, field, queue->cw_min);
- rt2x00usb_register_write(rt2x00dev, WMM_CWMIN_CFG, reg);
-
- rt2x00usb_register_read(rt2x00dev, WMM_CWMAX_CFG, &reg);
- rt2x00_set_field32(&reg, field, queue->cw_max);
- rt2x00usb_register_write(rt2x00dev, WMM_CWMAX_CFG, reg);
-
- /* Update EDCA registers */
- offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx);
-
- rt2x00usb_register_read(rt2x00dev, offset, &reg);
- rt2x00_set_field32(&reg, EDCA_AC0_CFG_TX_OP, queue->txop);
- rt2x00_set_field32(&reg, EDCA_AC0_CFG_AIFSN, queue->aifs);
- rt2x00_set_field32(&reg, EDCA_AC0_CFG_CWMIN, queue->cw_min);
- rt2x00_set_field32(&reg, EDCA_AC0_CFG_CWMAX, queue->cw_max);
- rt2x00usb_register_write(rt2x00dev, offset, reg);
-
- return 0;
-}
-
-static u64 rt2800usb_get_tsf(struct ieee80211_hw *hw)
-{
- struct rt2x00_dev *rt2x00dev = hw->priv;
- u64 tsf;
- u32 reg;
-
- rt2x00usb_register_read(rt2x00dev, TSF_TIMER_DW1, &reg);
- tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32;
- rt2x00usb_register_read(rt2x00dev, TSF_TIMER_DW0, &reg);
- tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD);
-
- return tsf;
-}
-
-static const struct ieee80211_ops rt2800usb_mac80211_ops = {
- .tx = rt2x00mac_tx,
- .start = rt2x00mac_start,
- .stop = rt2x00mac_stop,
- .add_interface = rt2x00mac_add_interface,
- .remove_interface = rt2x00mac_remove_interface,
- .config = rt2x00mac_config,
- .configure_filter = rt2x00mac_configure_filter,
- .set_tim = rt2x00mac_set_tim,
- .set_key = rt2x00mac_set_key,
- .get_stats = rt2x00mac_get_stats,
- .get_tkip_seq = rt2800usb_get_tkip_seq,
- .set_rts_threshold = rt2800usb_set_rts_threshold,
- .bss_info_changed = rt2x00mac_bss_info_changed,
- .conf_tx = rt2800usb_conf_tx,
- .get_tx_stats = rt2x00mac_get_tx_stats,
- .get_tsf = rt2800usb_get_tsf,
- .rfkill_poll = rt2x00mac_rfkill_poll,
-};
-
static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.probe_hw = rt2800usb_probe_hw,
.get_firmware_name = rt2800usb_get_firmware_name,
@@ -2810,10 +748,10 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.uninitialize = rt2x00usb_uninitialize,
.clear_entry = rt2x00usb_clear_entry,
.set_device_state = rt2800usb_set_device_state,
- .rfkill_poll = rt2800usb_rfkill_poll,
- .link_stats = rt2800usb_link_stats,
- .reset_tuner = rt2800usb_reset_tuner,
- .link_tuner = rt2800usb_link_tuner,
+ .rfkill_poll = rt2800_rfkill_poll,
+ .link_stats = rt2800_link_stats,
+ .reset_tuner = rt2800_reset_tuner,
+ .link_tuner = rt2800_link_tuner,
.write_tx_desc = rt2800usb_write_tx_desc,
.write_tx_data = rt2x00usb_write_tx_data,
.write_beacon = rt2800usb_write_beacon,
@@ -2821,19 +759,19 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.kick_tx_queue = rt2800usb_kick_tx_queue,
.kill_tx_queue = rt2x00usb_kill_tx_queue,
.fill_rxdone = rt2800usb_fill_rxdone,
- .config_shared_key = rt2800usb_config_shared_key,
- .config_pairwise_key = rt2800usb_config_pairwise_key,
- .config_filter = rt2800usb_config_filter,
- .config_intf = rt2800usb_config_intf,
- .config_erp = rt2800usb_config_erp,
- .config_ant = rt2800usb_config_ant,
- .config = rt2800usb_config,
+ .config_shared_key = rt2800_config_shared_key,
+ .config_pairwise_key = rt2800_config_pairwise_key,
+ .config_filter = rt2800_config_filter,
+ .config_intf = rt2800_config_intf,
+ .config_erp = rt2800_config_erp,
+ .config_ant = rt2800_config_ant,
+ .config = rt2800_config,
};
static const struct data_queue_desc rt2800usb_queue_rx = {
.entry_num = RX_ENTRIES,
.data_size = AGGREGATION_SIZE,
- .desc_size = RXD_DESC_SIZE + RXWI_DESC_SIZE,
+ .desc_size = RXINFO_DESC_SIZE + RXWI_DESC_SIZE,
.priv_size = sizeof(struct queue_entry_priv_usb),
};
@@ -2852,19 +790,20 @@ static const struct data_queue_desc rt2800usb_queue_bcn = {
};
static const struct rt2x00_ops rt2800usb_ops = {
- .name = KBUILD_MODNAME,
- .max_sta_intf = 1,
- .max_ap_intf = 8,
- .eeprom_size = EEPROM_SIZE,
- .rf_size = RF_SIZE,
- .tx_queues = NUM_TX_QUEUES,
- .rx = &rt2800usb_queue_rx,
- .tx = &rt2800usb_queue_tx,
- .bcn = &rt2800usb_queue_bcn,
- .lib = &rt2800usb_rt2x00_ops,
- .hw = &rt2800usb_mac80211_ops,
+ .name = KBUILD_MODNAME,
+ .max_sta_intf = 1,
+ .max_ap_intf = 8,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE,
+ .rx = &rt2800usb_queue_rx,
+ .tx = &rt2800usb_queue_tx,
+ .bcn = &rt2800usb_queue_bcn,
+ .lib = &rt2800usb_rt2x00_ops,
+ .hw = &rt2800_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
- .debugfs = &rt2800usb_rt2x00debug,
+ .debugfs = &rt2800_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
};
@@ -2886,17 +825,23 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0e0b, 0x9041), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Amit */
{ USB_DEVICE(0x15c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) },
+ /* Askey */
+ { USB_DEVICE(0x1690, 0x0740), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x1690, 0x0744), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) },
/* ASUS */
{ USB_DEVICE(0x0b05, 0x1731), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0b05, 0x1732), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0b05, 0x1742), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0b05, 0x1760), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0b05, 0x1761), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0b05, 0x1784), USB_DEVICE_DATA(&rt2800usb_ops) },
/* AzureWave */
{ USB_DEVICE(0x13d3, 0x3247), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x13d3, 0x3262), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x13d3, 0x3273), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x13d3, 0x3284), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x13d3, 0x3305), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Belkin */
{ USB_DEVICE(0x050d, 0x8053), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x050d, 0x805c), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -2905,6 +850,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
/* Buffalo */
{ USB_DEVICE(0x0411, 0x00e8), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0411, 0x012e), USB_DEVICE_DATA(&rt2800usb_ops) },
+ /* Cisco */
+ { USB_DEVICE(0x167b, 0x4001), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Conceptronic */
{ USB_DEVICE(0x14b2, 0x3c06), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x14b2, 0x3c07), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -2920,6 +867,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x07aa, 0x002f), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x07aa, 0x003c), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x07aa, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x07aa, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x07aa, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x18c5, 0x0008), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x18c5, 0x0012), USB_DEVICE_DATA(&rt2800usb_ops) },
/* D-Link */
@@ -2931,18 +880,24 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x07d1, 0x3c0f), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x07d1, 0x3c11), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x07d1, 0x3c13), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x07d1, 0x3c15), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Edimax */
{ USB_DEVICE(0x7392, 0x7711), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x7392, 0x7717), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x7392, 0x7718), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Encore */
{ USB_DEVICE(0x203d, 0x1480), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x203d, 0x14a1), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x203d, 0x14a9), USB_DEVICE_DATA(&rt2800usb_ops) },
/* EnGenius */
{ USB_DEVICE(0X1740, 0x9701), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1740, 0x9702), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1740, 0x9703), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1740, 0x9705), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1740, 0x9706), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x1740, 0x9707), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x1740, 0x9708), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x1740, 0x9709), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1740, 0x9801), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Gemtek */
{ USB_DEVICE(0x15a9, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -2956,7 +911,10 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0e66, 0x0009), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0e66, 0x000b), USB_DEVICE_DATA(&rt2800usb_ops) },
/* I-O DATA */
+ { USB_DEVICE(0x04bb, 0x0944), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x04bb, 0x0945), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x04bb, 0x0947), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x04bb, 0x0948), USB_DEVICE_DATA(&rt2800usb_ops) },
/* LevelOne */
{ USB_DEVICE(0x1740, 0x0605), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1740, 0x0615), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -2971,8 +929,18 @@ static struct usb_device_id rt2800usb_device_table[] = {
/* Motorola */
{ USB_DEVICE(0x100d, 0x9031), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x100d, 0x9032), USB_DEVICE_DATA(&rt2800usb_ops) },
+ /* MSI */
+ { USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0db0, 0x3821), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0db0, 0x3870), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0db0, 0x821a), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0db0, 0x870a), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0db0, 0x899a), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Ovislink */
{ USB_DEVICE(0x1b75, 0x3072), USB_DEVICE_DATA(&rt2800usb_ops) },
+ /* Para */
+ { USB_DEVICE(0x20b8, 0x8888), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Pegatron */
{ USB_DEVICE(0x1d4d, 0x0002), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x1d4d, 0x000c), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -2988,8 +956,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
/* Quanta */
{ USB_DEVICE(0x1a32, 0x0304), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Ralink */
- { USB_DEVICE(0x0db0, 0x3820), USB_DEVICE_DATA(&rt2800usb_ops) },
- { USB_DEVICE(0x0db0, 0x6899), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x148f, 0x2070), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x148f, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x148f, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -3013,7 +979,12 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0df6, 0x003e), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0df6, 0x003f), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0df6, 0x0040), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0df6, 0x0041), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x0df6, 0x0042), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0df6, 0x0047), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0df6, 0x0048), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0df6, 0x004a), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x0df6, 0x004d), USB_DEVICE_DATA(&rt2800usb_ops) },
/* SMC */
{ USB_DEVICE(0x083a, 0x6618), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x083a, 0x7511), USB_DEVICE_DATA(&rt2800usb_ops) },
@@ -3022,6 +993,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x083a, 0x8522), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x083a, 0xa512), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x083a, 0xa618), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x083a, 0xa701), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x083a, 0xa702), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x083a, 0xb522), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x083a, 0xc522), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Sparklan */
@@ -3039,6 +1012,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x5a57, 0x0280), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x5a57, 0x0282), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x5a57, 0x0283), USB_DEVICE_DATA(&rt2800usb_ops) },
+ { USB_DEVICE(0x5a57, 0x0284), USB_DEVICE_DATA(&rt2800usb_ops) },
{ USB_DEVICE(0x5a57, 0x5257), USB_DEVICE_DATA(&rt2800usb_ops) },
/* Zyxel */
{ USB_DEVICE(0x0586, 0x3416), USB_DEVICE_DATA(&rt2800usb_ops) },
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h
index 4d9991c9a51..1e4340a182e 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.h
+++ b/drivers/net/wireless/rt2x00/rt2800usb.h
@@ -1,5 +1,9 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
+ Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
+ Copyright (C) 2009 Axel Kollhofer <rain_maker@root-forum.org>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -28,288 +32,10 @@
#define RT2800USB_H
/*
- * RF chip defines.
- *
- * RF2820 2.4G 2T3R
- * RF2850 2.4G/5G 2T3R
- * RF2720 2.4G 1T2R
- * RF2750 2.4G/5G 1T2R
- * RF3020 2.4G 1T1R
- * RF2020 2.4G B/G
- * RF3021 2.4G 1T2R
- * RF3022 2.4G 2T2R
- * RF3052 2.4G 2T2R
- */
-#define RF2820 0x0001
-#define RF2850 0x0002
-#define RF2720 0x0003
-#define RF2750 0x0004
-#define RF3020 0x0005
-#define RF2020 0x0006
-#define RF3021 0x0007
-#define RF3022 0x0008
-#define RF3052 0x0009
-
-/*
- * RT2870 version
- */
-#define RT2860C_VERSION 0x28600100
-#define RT2860D_VERSION 0x28600101
-#define RT2880E_VERSION 0x28720200
-#define RT2883_VERSION 0x28830300
-#define RT3070_VERSION 0x30700200
-
-/*
- * Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
- */
-#define DEFAULT_RSSI_OFFSET 120 /* FIXME */
-
-/*
- * Register layout information.
- */
-#define CSR_REG_BASE 0x1000
-#define CSR_REG_SIZE 0x0800
-#define EEPROM_BASE 0x0000
-#define EEPROM_SIZE 0x0110
-#define BBP_BASE 0x0000
-#define BBP_SIZE 0x0080
-#define RF_BASE 0x0004
-#define RF_SIZE 0x0010
-
-/*
- * Number of TX queues.
- */
-#define NUM_TX_QUEUES 4
-
-/*
* USB registers.
*/
/*
- * HOST-MCU shared memory
- */
-#define HOST_CMD_CSR 0x0404
-#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff)
-
-/*
- * INT_SOURCE_CSR: Interrupt source register.
- * Write one to clear corresponding bit.
- * TX_FIFO_STATUS: FIFO Statistics is full, sw should read 0x171c
- */
-#define INT_SOURCE_CSR 0x0200
-#define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001)
-#define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002)
-#define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004)
-#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008)
-#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010)
-#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020)
-#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040)
-#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080)
-#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100)
-#define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200)
-#define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400)
-#define INT_SOURCE_CSR_TBTT FIELD32(0x00000800)
-#define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000)
-#define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000)
-#define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000)
-#define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000)
-#define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000)
-#define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000)
-
-/*
- * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF.
- */
-#define INT_MASK_CSR 0x0204
-#define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001)
-#define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002)
-#define INT_MASK_CSR_RX_DONE FIELD32(0x00000004)
-#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008)
-#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010)
-#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020)
-#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040)
-#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080)
-#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100)
-#define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200)
-#define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400)
-#define INT_MASK_CSR_TBTT FIELD32(0x00000800)
-#define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000)
-#define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000)
-#define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000)
-#define INT_MASK_CSR_GPTIMER FIELD32(0x00008000)
-#define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000)
-#define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000)
-
-/*
- * WPDMA_GLO_CFG
- */
-#define WPDMA_GLO_CFG 0x0208
-#define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001)
-#define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002)
-#define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004)
-#define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008)
-#define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030)
-#define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040)
-#define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080)
-#define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00)
-#define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000)
-
-/*
- * WPDMA_RST_IDX
- */
-#define WPDMA_RST_IDX 0x020c
-#define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001)
-#define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002)
-#define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004)
-#define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008)
-#define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010)
-#define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020)
-#define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000)
-
-/*
- * DELAY_INT_CFG
- */
-#define DELAY_INT_CFG 0x0210
-#define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff)
-#define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00)
-#define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000)
-#define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000)
-#define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000)
-#define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000)
-
-/*
- * WMM_AIFSN_CFG: Aifsn for each EDCA AC
- * AIFSN0: AC_BE
- * AIFSN1: AC_BK
- * AIFSN1: AC_VI
- * AIFSN1: AC_VO
- */
-#define WMM_AIFSN_CFG 0x0214
-#define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f)
-#define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0)
-#define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00)
-#define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000)
-
-/*
- * WMM_CWMIN_CSR: CWmin for each EDCA AC
- * CWMIN0: AC_BE
- * CWMIN1: AC_BK
- * CWMIN1: AC_VI
- * CWMIN1: AC_VO
- */
-#define WMM_CWMIN_CFG 0x0218
-#define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f)
-#define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0)
-#define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00)
-#define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000)
-
-/*
- * WMM_CWMAX_CSR: CWmax for each EDCA AC
- * CWMAX0: AC_BE
- * CWMAX1: AC_BK
- * CWMAX1: AC_VI
- * CWMAX1: AC_VO
- */
-#define WMM_CWMAX_CFG 0x021c
-#define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f)
-#define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0)
-#define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00)
-#define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000)
-
-/*
- * AC_TXOP0: AC_BK/AC_BE TXOP register
- * AC0TXOP: AC_BK in unit of 32us
- * AC1TXOP: AC_BE in unit of 32us
- */
-#define WMM_TXOP0_CFG 0x0220
-#define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff)
-#define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000)
-
-/*
- * AC_TXOP1: AC_VO/AC_VI TXOP register
- * AC2TXOP: AC_VI in unit of 32us
- * AC3TXOP: AC_VO in unit of 32us
- */
-#define WMM_TXOP1_CFG 0x0224
-#define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff)
-#define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000)
-
-/*
- * GPIO_CTRL_CFG:
- */
-#define GPIO_CTRL_CFG 0x0228
-#define GPIO_CTRL_CFG_BIT0 FIELD32(0x00000001)
-#define GPIO_CTRL_CFG_BIT1 FIELD32(0x00000002)
-#define GPIO_CTRL_CFG_BIT2 FIELD32(0x00000004)
-#define GPIO_CTRL_CFG_BIT3 FIELD32(0x00000008)
-#define GPIO_CTRL_CFG_BIT4 FIELD32(0x00000010)
-#define GPIO_CTRL_CFG_BIT5 FIELD32(0x00000020)
-#define GPIO_CTRL_CFG_BIT6 FIELD32(0x00000040)
-#define GPIO_CTRL_CFG_BIT7 FIELD32(0x00000080)
-#define GPIO_CTRL_CFG_BIT8 FIELD32(0x00000100)
-
-/*
- * MCU_CMD_CFG
- */
-#define MCU_CMD_CFG 0x022c
-
-/*
- * AC_BK register offsets
- */
-#define TX_BASE_PTR0 0x0230
-#define TX_MAX_CNT0 0x0234
-#define TX_CTX_IDX0 0x0238
-#define TX_DTX_IDX0 0x023c
-
-/*
- * AC_BE register offsets
- */
-#define TX_BASE_PTR1 0x0240
-#define TX_MAX_CNT1 0x0244
-#define TX_CTX_IDX1 0x0248
-#define TX_DTX_IDX1 0x024c
-
-/*
- * AC_VI register offsets
- */
-#define TX_BASE_PTR2 0x0250
-#define TX_MAX_CNT2 0x0254
-#define TX_CTX_IDX2 0x0258
-#define TX_DTX_IDX2 0x025c
-
-/*
- * AC_VO register offsets
- */
-#define TX_BASE_PTR3 0x0260
-#define TX_MAX_CNT3 0x0264
-#define TX_CTX_IDX3 0x0268
-#define TX_DTX_IDX3 0x026c
-
-/*
- * HCCA register offsets
- */
-#define TX_BASE_PTR4 0x0270
-#define TX_MAX_CNT4 0x0274
-#define TX_CTX_IDX4 0x0278
-#define TX_DTX_IDX4 0x027c
-
-/*
- * MGMT register offsets
- */
-#define TX_BASE_PTR5 0x0280
-#define TX_MAX_CNT5 0x0284
-#define TX_CTX_IDX5 0x0288
-#define TX_DTX_IDX5 0x028c
-
-/*
- * RX register offsets
- */
-#define RX_BASE_PTR 0x0290
-#define RX_MAX_CNT 0x0294
-#define RX_CRX_IDX 0x0298
-#define RX_DRX_IDX 0x029c
-
-/*
* USB_DMA_CFG
* RX_BULK_AGG_TIMEOUT: Rx Bulk Aggregation TimeOut in unit of 33ns.
* RX_BULK_AGG_LIMIT: Rx Bulk Aggregation Limit in unit of 256 bytes.
@@ -343,1448 +69,16 @@
#define USB_CYC_CFG_CLOCK_CYCLE FIELD32(0x000000ff)
/*
- * PBF_SYS_CTRL
- * HOST_RAM_WRITE: enable Host program ram write selection
- */
-#define PBF_SYS_CTRL 0x0400
-#define PBF_SYS_CTRL_READY FIELD32(0x00000080)
-#define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000)
-
-/*
- * PBF registers
- * Most are for debug. Driver doesn't touch PBF register.
- */
-#define PBF_CFG 0x0408
-#define PBF_MAX_PCNT 0x040c
-#define PBF_CTRL 0x0410
-#define PBF_INT_STA 0x0414
-#define PBF_INT_ENA 0x0418
-
-/*
- * BCN_OFFSET0:
- */
-#define BCN_OFFSET0 0x042c
-#define BCN_OFFSET0_BCN0 FIELD32(0x000000ff)
-#define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00)
-#define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000)
-#define BCN_OFFSET0_BCN3 FIELD32(0xff000000)
-
-/*
- * BCN_OFFSET1:
- */
-#define BCN_OFFSET1 0x0430
-#define BCN_OFFSET1_BCN4 FIELD32(0x000000ff)
-#define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00)
-#define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000)
-#define BCN_OFFSET1_BCN7 FIELD32(0xff000000)
-
-/*
- * PBF registers
- * Most are for debug. Driver doesn't touch PBF register.
- */
-#define TXRXQ_PCNT 0x0438
-#define PBF_DBG 0x043c
-
-/*
- * RF registers
- */
-#define RF_CSR_CFG 0x0500
-#define RF_CSR_CFG_DATA FIELD32(0x000000ff)
-#define RF_CSR_CFG_REGNUM FIELD32(0x00001f00)
-#define RF_CSR_CFG_WRITE FIELD32(0x00010000)
-#define RF_CSR_CFG_BUSY FIELD32(0x00020000)
-
-/*
- * MAC Control/Status Registers(CSR).
- * Some values are set in TU, whereas 1 TU == 1024 us.
- */
-
-/*
- * MAC_CSR0: ASIC revision number.
- * ASIC_REV: 0
- * ASIC_VER: 2870
- */
-#define MAC_CSR0 0x1000
-#define MAC_CSR0_ASIC_REV FIELD32(0x0000ffff)
-#define MAC_CSR0_ASIC_VER FIELD32(0xffff0000)
-
-/*
- * MAC_SYS_CTRL:
- */
-#define MAC_SYS_CTRL 0x1004
-#define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001)
-#define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002)
-#define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004)
-#define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008)
-#define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010)
-#define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020)
-#define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040)
-#define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080)
-
-/*
- * MAC_ADDR_DW0: STA MAC register 0
- */
-#define MAC_ADDR_DW0 0x1008
-#define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff)
-#define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00)
-#define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000)
-#define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000)
-
-/*
- * MAC_ADDR_DW1: STA MAC register 1
- * UNICAST_TO_ME_MASK:
- * Used to mask off bits from byte 5 of the MAC address
- * to determine the UNICAST_TO_ME bit for RX frames.
- * The full mask is complemented by BSS_ID_MASK:
- * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK
- */
-#define MAC_ADDR_DW1 0x100c
-#define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff)
-#define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00)
-#define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000)
-
-/*
- * MAC_BSSID_DW0: BSSID register 0
- */
-#define MAC_BSSID_DW0 0x1010
-#define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff)
-#define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00)
-#define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000)
-#define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000)
-
-/*
- * MAC_BSSID_DW1: BSSID register 1
- * BSS_ID_MASK:
- * 0: 1-BSSID mode (BSS index = 0)
- * 1: 2-BSSID mode (BSS index: Byte5, bit 0)
- * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1)
- * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2)
- * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the
- * BSSID. This will make sure that those bits will be ignored
- * when determining the MY_BSS of RX frames.
- */
-#define MAC_BSSID_DW1 0x1014
-#define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff)
-#define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00)
-#define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000)
-#define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000)
-
-/*
- * MAX_LEN_CFG: Maximum frame length register.
- * MAX_MPDU: rt2860b max 16k bytes
- * MAX_PSDU: Maximum PSDU length
- * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16
- */
-#define MAX_LEN_CFG 0x1018
-#define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff)
-#define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000)
-#define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000)
-#define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000)
-
-/*
- * BBP_CSR_CFG: BBP serial control register
- * VALUE: Register value to program into BBP
- * REG_NUM: Selected BBP register
- * READ_CONTROL: 0 write BBP, 1 read BBP
- * BUSY: ASIC is busy executing BBP commands
- * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks
- * BBP_RW_MODE: 0 serial, 1 paralell
- */
-#define BBP_CSR_CFG 0x101c
-#define BBP_CSR_CFG_VALUE FIELD32(0x000000ff)
-#define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00)
-#define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000)
-#define BBP_CSR_CFG_BUSY FIELD32(0x00020000)
-#define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000)
-#define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000)
-
-/*
- * RF_CSR_CFG0: RF control register
- * REGID_AND_VALUE: Register value to program into RF
- * BITWIDTH: Selected RF register
- * STANDBYMODE: 0 high when standby, 1 low when standby
- * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate
- * BUSY: ASIC is busy executing RF commands
- */
-#define RF_CSR_CFG0 0x1020
-#define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff)
-#define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000)
-#define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff)
-#define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000)
-#define RF_CSR_CFG0_SEL FIELD32(0x40000000)
-#define RF_CSR_CFG0_BUSY FIELD32(0x80000000)
-
-/*
- * RF_CSR_CFG1: RF control register
- * REGID_AND_VALUE: Register value to program into RF
- * RFGAP: Gap between BB_CONTROL_RF and RF_LE
- * 0: 3 system clock cycle (37.5usec)
- * 1: 5 system clock cycle (62.5usec)
- */
-#define RF_CSR_CFG1 0x1024
-#define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff)
-#define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000)
-
-/*
- * RF_CSR_CFG2: RF control register
- * VALUE: Register value to program into RF
- * RFGAP: Gap between BB_CONTROL_RF and RF_LE
- * 0: 3 system clock cycle (37.5usec)
- * 1: 5 system clock cycle (62.5usec)
- */
-#define RF_CSR_CFG2 0x1028
-#define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff)
-
-/*
- * LED_CFG: LED control
- * color LED's:
- * 0: off
- * 1: blinking upon TX2
- * 2: periodic slow blinking
- * 3: always on
- * LED polarity:
- * 0: active low
- * 1: active high
- */
-#define LED_CFG 0x102c
-#define LED_CFG_ON_PERIOD FIELD32(0x000000ff)
-#define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00)
-#define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000)
-#define LED_CFG_R_LED_MODE FIELD32(0x03000000)
-#define LED_CFG_G_LED_MODE FIELD32(0x0c000000)
-#define LED_CFG_Y_LED_MODE FIELD32(0x30000000)
-#define LED_CFG_LED_POLAR FIELD32(0x40000000)
-
-/*
- * XIFS_TIME_CFG: MAC timing
- * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX
- * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX
- * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX
- * when MAC doesn't reference BBP signal BBRXEND
- * EIFS: unit 1us
- * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer
- *
- */
-#define XIFS_TIME_CFG 0x1100
-#define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff)
-#define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00)
-#define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000)
-#define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000)
-#define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000)
-
-/*
- * BKOFF_SLOT_CFG:
- */
-#define BKOFF_SLOT_CFG 0x1104
-#define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff)
-#define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00)
-
-/*
- * NAV_TIME_CFG:
- */
-#define NAV_TIME_CFG 0x1108
-#define NAV_TIME_CFG_SIFS FIELD32(0x000000ff)
-#define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00)
-#define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000)
-#define NAV_TIME_ZERO_SIFS FIELD32(0x02000000)
-
-/*
- * CH_TIME_CFG: count as channel busy
- */
-#define CH_TIME_CFG 0x110c
-
-/*
- * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us
- */
-#define PBF_LIFE_TIMER 0x1110
-
-/*
- * BCN_TIME_CFG:
- * BEACON_INTERVAL: in unit of 1/16 TU
- * TSF_TICKING: Enable TSF auto counting
- * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode
- * BEACON_GEN: Enable beacon generator
- */
-#define BCN_TIME_CFG 0x1114
-#define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff)
-#define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000)
-#define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000)
-#define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000)
-#define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000)
-#define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000)
-
-/*
- * TBTT_SYNC_CFG:
- */
-#define TBTT_SYNC_CFG 0x1118
-
-/*
- * TSF_TIMER_DW0: Local lsb TSF timer, read-only
- */
-#define TSF_TIMER_DW0 0x111c
-#define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff)
-
-/*
- * TSF_TIMER_DW1: Local msb TSF timer, read-only
- */
-#define TSF_TIMER_DW1 0x1120
-#define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff)
-
-/*
- * TBTT_TIMER: TImer remains till next TBTT, read-only
- */
-#define TBTT_TIMER 0x1124
-
-/*
- * INT_TIMER_CFG:
- */
-#define INT_TIMER_CFG 0x1128
-
-/*
- * INT_TIMER_EN: GP-timer and pre-tbtt Int enable
- */
-#define INT_TIMER_EN 0x112c
-
-/*
- * CH_IDLE_STA: channel idle time
- */
-#define CH_IDLE_STA 0x1130
-
-/*
- * CH_BUSY_STA: channel busy time
- */
-#define CH_BUSY_STA 0x1134
-
-/*
- * MAC_STATUS_CFG:
- * BBP_RF_BUSY: When set to 0, BBP and RF are stable.
- * if 1 or higher one of the 2 registers is busy.
- */
-#define MAC_STATUS_CFG 0x1200
-#define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003)
-
-/*
- * PWR_PIN_CFG:
- */
-#define PWR_PIN_CFG 0x1204
-
-/*
- * AUTOWAKEUP_CFG: Manual power control / status register
- * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set
- * AUTOWAKE: 0:sleep, 1:awake
- */
-#define AUTOWAKEUP_CFG 0x1208
-#define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff)
-#define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00)
-#define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000)
-
-/*
- * EDCA_AC0_CFG:
- */
-#define EDCA_AC0_CFG 0x1300
-#define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff)
-#define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00)
-#define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000)
-#define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000)
-
-/*
- * EDCA_AC1_CFG:
- */
-#define EDCA_AC1_CFG 0x1304
-#define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff)
-#define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00)
-#define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000)
-#define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000)
-
-/*
- * EDCA_AC2_CFG:
- */
-#define EDCA_AC2_CFG 0x1308
-#define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff)
-#define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00)
-#define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000)
-#define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000)
-
-/*
- * EDCA_AC3_CFG:
- */
-#define EDCA_AC3_CFG 0x130c
-#define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff)
-#define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00)
-#define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000)
-#define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000)
-
-/*
- * EDCA_TID_AC_MAP:
- */
-#define EDCA_TID_AC_MAP 0x1310
-
-/*
- * TX_PWR_CFG_0:
- */
-#define TX_PWR_CFG_0 0x1314
-#define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f)
-#define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0)
-#define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00)
-#define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000)
-#define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000)
-#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000)
-#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000)
-#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000)
-
-/*
- * TX_PWR_CFG_1:
- */
-#define TX_PWR_CFG_1 0x1318
-#define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f)
-#define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0)
-#define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00)
-#define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000)
-#define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000)
-#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000)
-#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000)
-#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000)
-
-/*
- * TX_PWR_CFG_2:
- */
-#define TX_PWR_CFG_2 0x131c
-#define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f)
-#define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0)
-#define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00)
-#define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000)
-#define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000)
-#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000)
-#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000)
-#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000)
-
-/*
- * TX_PWR_CFG_3:
- */
-#define TX_PWR_CFG_3 0x1320
-#define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f)
-#define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0)
-#define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00)
-#define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000)
-#define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000)
-#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000)
-#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000)
-#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000)
-
-/*
- * TX_PWR_CFG_4:
- */
-#define TX_PWR_CFG_4 0x1324
-#define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f)
-#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0)
-#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00)
-#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000)
-
-/*
- * TX_PIN_CFG:
- */
-#define TX_PIN_CFG 0x1328
-#define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001)
-#define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002)
-#define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004)
-#define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008)
-#define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010)
-#define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020)
-#define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040)
-#define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080)
-#define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100)
-#define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200)
-#define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400)
-#define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800)
-#define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000)
-#define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000)
-#define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000)
-#define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000)
-#define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000)
-#define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000)
-#define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000)
-#define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000)
-
-/*
- * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz
- */
-#define TX_BAND_CFG 0x132c
-#define TX_BAND_CFG_HT40_PLUS FIELD32(0x00000001)
-#define TX_BAND_CFG_A FIELD32(0x00000002)
-#define TX_BAND_CFG_BG FIELD32(0x00000004)
-
-/*
- * TX_SW_CFG0:
- */
-#define TX_SW_CFG0 0x1330
-
-/*
- * TX_SW_CFG1:
- */
-#define TX_SW_CFG1 0x1334
-
-/*
- * TX_SW_CFG2:
- */
-#define TX_SW_CFG2 0x1338
-
-/*
- * TXOP_THRES_CFG:
- */
-#define TXOP_THRES_CFG 0x133c
-
-/*
- * TXOP_CTRL_CFG:
- */
-#define TXOP_CTRL_CFG 0x1340
-
-/*
- * TX_RTS_CFG:
- * RTS_THRES: unit:byte
- * RTS_FBK_EN: enable rts rate fallback
- */
-#define TX_RTS_CFG 0x1344
-#define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff)
-#define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00)
-#define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000)
-
-/*
- * TX_TIMEOUT_CFG:
- * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us
- * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure
- * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation.
- * it is recommended that:
- * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT)
- */
-#define TX_TIMEOUT_CFG 0x1348
-#define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0)
-#define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00)
-#define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000)
-
-/*
- * TX_RTY_CFG:
- * SHORT_RTY_LIMIT: short retry limit
- * LONG_RTY_LIMIT: long retry limit
- * LONG_RTY_THRE: Long retry threshoold
- * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode
- * 0:expired by retry limit, 1: expired by mpdu life timer
- * AGG_RTY_MODE: Aggregate MPDU retry mode
- * 0:expired by retry limit, 1: expired by mpdu life timer
- * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable
- */
-#define TX_RTY_CFG 0x134c
-#define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff)
-#define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00)
-#define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000)
-#define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000)
-#define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000)
-#define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000)
-
-/*
- * TX_LINK_CFG:
- * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us
- * MFB_ENABLE: TX apply remote MFB 1:enable
- * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable
- * 0: not apply remote remote unsolicit (MFS=7)
- * TX_MRQ_EN: MCS request TX enable
- * TX_RDG_EN: RDG TX enable
- * TX_CF_ACK_EN: Piggyback CF-ACK enable
- * REMOTE_MFB: remote MCS feedback
- * REMOTE_MFS: remote MCS feedback sequence number
- */
-#define TX_LINK_CFG 0x1350
-#define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff)
-#define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100)
-#define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200)
-#define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400)
-#define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800)
-#define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000)
-#define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000)
-#define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000)
-
-/*
- * HT_FBK_CFG0:
- */
-#define HT_FBK_CFG0 0x1354
-#define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f)
-#define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0)
-#define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00)
-#define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000)
-#define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000)
-#define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000)
-#define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000)
-#define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000)
-
-/*
- * HT_FBK_CFG1:
- */
-#define HT_FBK_CFG1 0x1358
-#define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f)
-#define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0)
-#define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00)
-#define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000)
-#define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000)
-#define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000)
-#define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000)
-#define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000)
-
-/*
- * LG_FBK_CFG0:
- */
-#define LG_FBK_CFG0 0x135c
-#define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f)
-#define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0)
-#define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00)
-#define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000)
-#define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000)
-#define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000)
-#define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000)
-#define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000)
-
-/*
- * LG_FBK_CFG1:
- */
-#define LG_FBK_CFG1 0x1360
-#define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f)
-#define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0)
-#define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00)
-#define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000)
-
-/*
- * CCK_PROT_CFG: CCK Protection
- * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd)
- * PROTECT_CTRL: Protection control frame type for CCK TX
- * 0:none, 1:RTS/CTS, 2:CTS-to-self
- * PROTECT_NAV: TXOP protection type for CCK TX
- * 0:none, 1:ShortNAVprotect, 2:LongNAVProtect
- * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow
- * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow
- * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow
- * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow
- * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow
- * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow
- * RTS_TH_EN: RTS threshold enable on CCK TX
- */
-#define CCK_PROT_CFG 0x1364
-#define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
-#define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
-#define CCK_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
-#define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
-#define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
-#define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
-#define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
-#define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
-#define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
-#define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
-
-/*
- * OFDM_PROT_CFG: OFDM Protection
- */
-#define OFDM_PROT_CFG 0x1368
-#define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
-#define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
-#define OFDM_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
-#define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
-#define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
-#define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
-#define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
-#define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
-#define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
-#define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
-
-/*
- * MM20_PROT_CFG: MM20 Protection
- */
-#define MM20_PROT_CFG 0x136c
-#define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
-#define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
-#define MM20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
-#define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
-#define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
-#define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
-#define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
-#define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
-#define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
-#define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
-
-/*
- * MM40_PROT_CFG: MM40 Protection
- */
-#define MM40_PROT_CFG 0x1370
-#define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
-#define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
-#define MM40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
-#define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
-#define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
-#define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
-#define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
-#define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
-#define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
-#define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
-
-/*
- * GF20_PROT_CFG: GF20 Protection
- */
-#define GF20_PROT_CFG 0x1374
-#define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
-#define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
-#define GF20_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
-#define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
-#define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
-#define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
-#define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
-#define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
-#define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
-#define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
-
-/*
- * GF40_PROT_CFG: GF40 Protection
- */
-#define GF40_PROT_CFG 0x1378
-#define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff)
-#define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000)
-#define GF40_PROT_CFG_PROTECT_NAV FIELD32(0x000c0000)
-#define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000)
-#define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000)
-#define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000)
-#define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000)
-#define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000)
-#define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000)
-#define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000)
-
-/*
- * EXP_CTS_TIME:
- */
-#define EXP_CTS_TIME 0x137c
-
-/*
- * EXP_ACK_TIME:
- */
-#define EXP_ACK_TIME 0x1380
-
-/*
- * RX_FILTER_CFG: RX configuration register.
- */
-#define RX_FILTER_CFG 0x1400
-#define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001)
-#define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002)
-#define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004)
-#define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008)
-#define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010)
-#define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020)
-#define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040)
-#define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080)
-#define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100)
-#define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200)
-#define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400)
-#define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800)
-#define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000)
-#define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000)
-#define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000)
-#define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000)
-#define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000)
-
-/*
- * AUTO_RSP_CFG:
- * AUTORESPONDER: 0: disable, 1: enable
- * BAC_ACK_POLICY: 0:long, 1:short preamble
- * CTS_40_MMODE: Response CTS 40MHz duplicate mode
- * CTS_40_MREF: Response CTS 40MHz duplicate mode
- * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble
- * DUAL_CTS_EN: Power bit value in control frame
- * ACK_CTS_PSM_BIT:Power bit value in control frame
- */
-#define AUTO_RSP_CFG 0x1404
-#define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001)
-#define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002)
-#define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004)
-#define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008)
-#define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010)
-#define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040)
-#define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080)
-
-/*
- * LEGACY_BASIC_RATE:
- */
-#define LEGACY_BASIC_RATE 0x1408
-
-/*
- * HT_BASIC_RATE:
- */
-#define HT_BASIC_RATE 0x140c
-
-/*
- * HT_CTRL_CFG:
- */
-#define HT_CTRL_CFG 0x1410
-
-/*
- * SIFS_COST_CFG:
- */
-#define SIFS_COST_CFG 0x1414
-
-/*
- * RX_PARSER_CFG:
- * Set NAV for all received frames
- */
-#define RX_PARSER_CFG 0x1418
-
-/*
- * TX_SEC_CNT0:
- */
-#define TX_SEC_CNT0 0x1500
-
-/*
- * RX_SEC_CNT0:
- */
-#define RX_SEC_CNT0 0x1504
-
-/*
- * CCMP_FC_MUTE:
- */
-#define CCMP_FC_MUTE 0x1508
-
-/*
- * TXOP_HLDR_ADDR0:
- */
-#define TXOP_HLDR_ADDR0 0x1600
-
-/*
- * TXOP_HLDR_ADDR1:
- */
-#define TXOP_HLDR_ADDR1 0x1604
-
-/*
- * TXOP_HLDR_ET:
- */
-#define TXOP_HLDR_ET 0x1608
-
-/*
- * QOS_CFPOLL_RA_DW0:
- */
-#define QOS_CFPOLL_RA_DW0 0x160c
-
-/*
- * QOS_CFPOLL_RA_DW1:
- */
-#define QOS_CFPOLL_RA_DW1 0x1610
-
-/*
- * QOS_CFPOLL_QC:
- */
-#define QOS_CFPOLL_QC 0x1614
-
-/*
- * RX_STA_CNT0: RX PLCP error count & RX CRC error count
- */
-#define RX_STA_CNT0 0x1700
-#define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff)
-#define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000)
-
-/*
- * RX_STA_CNT1: RX False CCA count & RX LONG frame count
- */
-#define RX_STA_CNT1 0x1704
-#define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff)
-#define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000)
-
-/*
- * RX_STA_CNT2:
- */
-#define RX_STA_CNT2 0x1708
-#define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff)
-#define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000)
-
-/*
- * TX_STA_CNT0: TX Beacon count
- */
-#define TX_STA_CNT0 0x170c
-#define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff)
-#define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_STA_CNT1: TX tx count
- */
-#define TX_STA_CNT1 0x1710
-#define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff)
-#define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000)
-
-/*
- * TX_STA_CNT2: TX tx count
- */
-#define TX_STA_CNT2 0x1714
-#define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff)
-#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_STA_FIFO: TX Result for specific PID status fifo register
- */
-#define TX_STA_FIFO 0x1718
-#define TX_STA_FIFO_VALID FIELD32(0x00000001)
-#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e)
-#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020)
-#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040)
-#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080)
-#define TX_STA_FIFO_WCID FIELD32(0x0000ff00)
-#define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT: Debug counter
- */
-#define TX_AGG_CNT 0x171c
-#define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT0:
- */
-#define TX_AGG_CNT0 0x1720
-#define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT1:
- */
-#define TX_AGG_CNT1 0x1724
-#define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT2:
- */
-#define TX_AGG_CNT2 0x1728
-#define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT3:
- */
-#define TX_AGG_CNT3 0x172c
-#define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT4:
- */
-#define TX_AGG_CNT4 0x1730
-#define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT5:
- */
-#define TX_AGG_CNT5 0x1734
-#define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT6:
- */
-#define TX_AGG_CNT6 0x1738
-#define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000)
-
-/*
- * TX_AGG_CNT7:
- */
-#define TX_AGG_CNT7 0x173c
-#define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff)
-#define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000)
-
-/*
- * MPDU_DENSITY_CNT:
- * TX_ZERO_DEL: TX zero length delimiter count
- * RX_ZERO_DEL: RX zero length delimiter count
- */
-#define MPDU_DENSITY_CNT 0x1740
-#define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff)
-#define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000)
-
-/*
- * Security key table memory.
- * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry
- * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry
- * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry
- * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry
- * SHARED_KEY_TABLE_BASE: 32 bytes * 32-entry
- * SHARED_KEY_MODE_BASE: 4 bits * 32-entry
- */
-#define MAC_WCID_BASE 0x1800
-#define PAIRWISE_KEY_TABLE_BASE 0x4000
-#define MAC_IVEIV_TABLE_BASE 0x6000
-#define MAC_WCID_ATTRIBUTE_BASE 0x6800
-#define SHARED_KEY_TABLE_BASE 0x6c00
-#define SHARED_KEY_MODE_BASE 0x7000
-
-#define MAC_WCID_ENTRY(__idx) \
- ( MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry)) )
-#define PAIRWISE_KEY_ENTRY(__idx) \
- ( PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) )
-#define MAC_IVEIV_ENTRY(__idx) \
- ( MAC_IVEIV_TABLE_BASE + ((__idx) & sizeof(struct mac_iveiv_entry)) )
-#define MAC_WCID_ATTR_ENTRY(__idx) \
- ( MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32)) )
-#define SHARED_KEY_ENTRY(__idx) \
- ( SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry)) )
-#define SHARED_KEY_MODE_ENTRY(__idx) \
- ( SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32)) )
-
-struct mac_wcid_entry {
- u8 mac[6];
- u8 reserved[2];
-} __attribute__ ((packed));
-
-struct hw_key_entry {
- u8 key[16];
- u8 tx_mic[8];
- u8 rx_mic[8];
-} __attribute__ ((packed));
-
-struct mac_iveiv_entry {
- u8 iv[8];
-} __attribute__ ((packed));
-
-/*
- * MAC_WCID_ATTRIBUTE:
- */
-#define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001)
-#define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e)
-#define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070)
-#define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380)
-
-/*
- * SHARED_KEY_MODE:
- */
-#define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007)
-#define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070)
-#define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700)
-#define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000)
-#define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000)
-#define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000)
-#define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000)
-#define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000)
-
-/*
- * HOST-MCU communication
- */
-
-/*
- * H2M_MAILBOX_CSR: Host-to-MCU Mailbox.
- */
-#define H2M_MAILBOX_CSR 0x7010
-#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff)
-#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00)
-#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000)
-#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000)
-
-/*
- * H2M_MAILBOX_CID:
- */
-#define H2M_MAILBOX_CID 0x7014
-#define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff)
-#define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00)
-#define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000)
-#define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000)
-
-/*
- * H2M_MAILBOX_STATUS:
- */
-#define H2M_MAILBOX_STATUS 0x701c
-
-/*
- * H2M_INT_SRC:
- */
-#define H2M_INT_SRC 0x7024
-
-/*
- * H2M_BBP_AGENT:
- */
-#define H2M_BBP_AGENT 0x7028
-
-/*
- * MCU_LEDCS: LED control for MCU Mailbox.
- */
-#define MCU_LEDCS_LED_MODE FIELD8(0x1f)
-#define MCU_LEDCS_POLARITY FIELD8(0x01)
-
-/*
- * HW_CS_CTS_BASE:
- * Carrier-sense CTS frame base address.
- * It's where mac stores carrier-sense frame for carrier-sense function.
- */
-#define HW_CS_CTS_BASE 0x7700
-
-/*
- * HW_DFS_CTS_BASE:
- * FS CTS frame base address. It's where mac stores CTS frame for DFS.
- */
-#define HW_DFS_CTS_BASE 0x7780
-
-/*
- * TXRX control registers - base address 0x3000
- */
-
-/*
- * TXRX_CSR1:
- * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first..
- */
-#define TXRX_CSR1 0x77d0
-
-/*
- * HW_DEBUG_SETTING_BASE:
- * since NULL frame won't be that long (256 byte)
- * We steal 16 tail bytes to save debugging settings
- */
-#define HW_DEBUG_SETTING_BASE 0x77f0
-#define HW_DEBUG_SETTING_BASE2 0x7770
-
-/*
- * HW_BEACON_BASE
- * In order to support maximum 8 MBSS and its maximum length
- * is 512 bytes for each beacon
- * Three section discontinue memory segments will be used.
- * 1. The original region for BCN 0~3
- * 2. Extract memory from FCE table for BCN 4~5
- * 3. Extract memory from Pair-wise key table for BCN 6~7
- * It occupied those memory of wcid 238~253 for BCN 6
- * and wcid 222~237 for BCN 7
- *
- * IMPORTANT NOTE: Not sure why legacy driver does this,
- * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6.
- */
-#define HW_BEACON_BASE0 0x7800
-#define HW_BEACON_BASE1 0x7a00
-#define HW_BEACON_BASE2 0x7c00
-#define HW_BEACON_BASE3 0x7e00
-#define HW_BEACON_BASE4 0x7200
-#define HW_BEACON_BASE5 0x7400
-#define HW_BEACON_BASE6 0x5dc0
-#define HW_BEACON_BASE7 0x5bc0
-
-#define HW_BEACON_OFFSET(__index) \
- ( ((__index) < 4) ? ( HW_BEACON_BASE0 + (__index * 0x0200) ) : \
- (((__index) < 6) ? ( HW_BEACON_BASE4 + ((__index - 4) * 0x0200) ) : \
- (HW_BEACON_BASE6 - ((__index - 6) * 0x0200))) )
-
-/*
* 8051 firmware image.
*/
#define FIRMWARE_RT2870 "rt2870.bin"
#define FIRMWARE_IMAGE_BASE 0x3000
/*
- * BBP registers.
- * The wordsize of the BBP is 8 bits.
- */
-
-/*
- * BBP 1: TX Antenna
- */
-#define BBP1_TX_POWER FIELD8(0x07)
-#define BBP1_TX_ANTENNA FIELD8(0x18)
-
-/*
- * BBP 3: RX Antenna
- */
-#define BBP3_RX_ANTENNA FIELD8(0x18)
-#define BBP3_HT40_PLUS FIELD8(0x20)
-
-/*
- * BBP 4: Bandwidth
- */
-#define BBP4_TX_BF FIELD8(0x01)
-#define BBP4_BANDWIDTH FIELD8(0x18)
-
-/*
- * RFCSR registers
- * The wordsize of the RFCSR is 8 bits.
- */
-
-/*
- * RFCSR 6:
- */
-#define RFCSR6_R FIELD8(0x03)
-
-/*
- * RFCSR 7:
- */
-#define RFCSR7_RF_TUNING FIELD8(0x01)
-
-/*
- * RFCSR 12:
- */
-#define RFCSR12_TX_POWER FIELD8(0x1f)
-
-/*
- * RFCSR 22:
- */
-#define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01)
-
-/*
- * RFCSR 23:
- */
-#define RFCSR23_FREQ_OFFSET FIELD8(0x7f)
-
-/*
- * RFCSR 30:
- */
-#define RFCSR30_RF_CALIBRATION FIELD8(0x80)
-
-/*
- * RF registers
- */
-
-/*
- * RF 2
- */
-#define RF2_ANTENNA_RX2 FIELD32(0x00000040)
-#define RF2_ANTENNA_TX1 FIELD32(0x00004000)
-#define RF2_ANTENNA_RX1 FIELD32(0x00020000)
-
-/*
- * RF 3
- */
-#define RF3_TXPOWER_G FIELD32(0x00003e00)
-#define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200)
-#define RF3_TXPOWER_A FIELD32(0x00003c00)
-
-/*
- * RF 4
- */
-#define RF4_TXPOWER_G FIELD32(0x000007c0)
-#define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040)
-#define RF4_TXPOWER_A FIELD32(0x00000780)
-#define RF4_FREQ_OFFSET FIELD32(0x001f8000)
-#define RF4_HT40 FIELD32(0x00200000)
-
-/*
- * EEPROM content.
- * The wordsize of the EEPROM is 16 bits.
- */
-
-/*
- * EEPROM Version
- */
-#define EEPROM_VERSION 0x0001
-#define EEPROM_VERSION_FAE FIELD16(0x00ff)
-#define EEPROM_VERSION_VERSION FIELD16(0xff00)
-
-/*
- * HW MAC address.
- */
-#define EEPROM_MAC_ADDR_0 0x0002
-#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff)
-#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_1 0x0003
-#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff)
-#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_2 0x0004
-#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff)
-#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00)
-
-/*
- * EEPROM ANTENNA config
- * RXPATH: 1: 1R, 2: 2R, 3: 3R
- * TXPATH: 1: 1T, 2: 2T
- */
-#define EEPROM_ANTENNA 0x001a
-#define EEPROM_ANTENNA_RXPATH FIELD16(0x000f)
-#define EEPROM_ANTENNA_TXPATH FIELD16(0x00f0)
-#define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0f00)
-
-/*
- * EEPROM NIC config
- * CARDBUS_ACCEL: 0 - enable, 1 - disable
- */
-#define EEPROM_NIC 0x001b
-#define EEPROM_NIC_HW_RADIO FIELD16(0x0001)
-#define EEPROM_NIC_DYNAMIC_TX_AGC FIELD16(0x0002)
-#define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0004)
-#define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0008)
-#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0010)
-#define EEPROM_NIC_BW40M_SB_BG FIELD16(0x0020)
-#define EEPROM_NIC_BW40M_SB_A FIELD16(0x0040)
-#define EEPROM_NIC_WPS_PBC FIELD16(0x0080)
-#define EEPROM_NIC_BW40M_BG FIELD16(0x0100)
-#define EEPROM_NIC_BW40M_A FIELD16(0x0200)
-
-/*
- * EEPROM frequency
- */
-#define EEPROM_FREQ 0x001d
-#define EEPROM_FREQ_OFFSET FIELD16(0x00ff)
-#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00)
-#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000)
-
-/*
- * EEPROM LED
- * POLARITY_RDY_G: Polarity RDY_G setting.
- * POLARITY_RDY_A: Polarity RDY_A setting.
- * POLARITY_ACT: Polarity ACT setting.
- * POLARITY_GPIO_0: Polarity GPIO0 setting.
- * POLARITY_GPIO_1: Polarity GPIO1 setting.
- * POLARITY_GPIO_2: Polarity GPIO2 setting.
- * POLARITY_GPIO_3: Polarity GPIO3 setting.
- * POLARITY_GPIO_4: Polarity GPIO4 setting.
- * LED_MODE: Led mode.
- */
-#define EEPROM_LED1 0x001e
-#define EEPROM_LED2 0x001f
-#define EEPROM_LED3 0x0020
-#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001)
-#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002)
-#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004)
-#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008)
-#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010)
-#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020)
-#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040)
-#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080)
-#define EEPROM_LED_LED_MODE FIELD16(0x1f00)
-
-/*
- * EEPROM LNA
- */
-#define EEPROM_LNA 0x0022
-#define EEPROM_LNA_BG FIELD16(0x00ff)
-#define EEPROM_LNA_A0 FIELD16(0xff00)
-
-/*
- * EEPROM RSSI BG offset
- */
-#define EEPROM_RSSI_BG 0x0023
-#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff)
-#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00)
-
-/*
- * EEPROM RSSI BG2 offset
- */
-#define EEPROM_RSSI_BG2 0x0024
-#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff)
-#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00)
-
-/*
- * EEPROM RSSI A offset
- */
-#define EEPROM_RSSI_A 0x0025
-#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff)
-#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00)
-
-/*
- * EEPROM RSSI A2 offset
- */
-#define EEPROM_RSSI_A2 0x0026
-#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff)
-#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00)
-
-/*
- * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power.
- * This is delta in 40MHZ.
- * VALUE: Tx Power dalta value (MAX=4)
- * TYPE: 1: Plus the delta value, 0: minus the delta value
- * TXPOWER: Enable:
- */
-#define EEPROM_TXPOWER_DELTA 0x0028
-#define EEPROM_TXPOWER_DELTA_VALUE FIELD16(0x003f)
-#define EEPROM_TXPOWER_DELTA_TYPE FIELD16(0x0040)
-#define EEPROM_TXPOWER_DELTA_TXPOWER FIELD16(0x0080)
-
-/*
- * EEPROM TXPOWER 802.11BG
- */
-#define EEPROM_TXPOWER_BG1 0x0029
-#define EEPROM_TXPOWER_BG2 0x0030
-#define EEPROM_TXPOWER_BG_SIZE 7
-#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff)
-#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00)
-
-/*
- * EEPROM TXPOWER 802.11A
- */
-#define EEPROM_TXPOWER_A1 0x003c
-#define EEPROM_TXPOWER_A2 0x0053
-#define EEPROM_TXPOWER_A_SIZE 6
-#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff)
-#define EEPROM_TXPOWER_A_2 FIELD16(0xff00)
-
-/*
- * EEPROM TXpower byrate: 20MHZ power
- */
-#define EEPROM_TXPOWER_BYRATE 0x006f
-
-/*
- * EEPROM BBP.
- */
-#define EEPROM_BBP_START 0x0078
-#define EEPROM_BBP_SIZE 16
-#define EEPROM_BBP_VALUE FIELD16(0x00ff)
-#define EEPROM_BBP_REG_ID FIELD16(0xff00)
-
-/*
- * MCU mailbox commands.
- */
-#define MCU_SLEEP 0x30
-#define MCU_WAKEUP 0x31
-#define MCU_RADIO_OFF 0x35
-#define MCU_CURRENT 0x36
-#define MCU_LED 0x50
-#define MCU_LED_STRENGTH 0x51
-#define MCU_LED_1 0x52
-#define MCU_LED_2 0x53
-#define MCU_LED_3 0x54
-#define MCU_RADAR 0x60
-#define MCU_BOOT_SIGNAL 0x72
-#define MCU_BBP_SIGNAL 0x80
-#define MCU_POWER_SAVE 0x83
-
-/*
- * MCU mailbox tokens
- */
-#define TOKEN_WAKUP 3
-
-/*
* DMA descriptor defines.
*/
-#define TXD_DESC_SIZE ( 4 * sizeof(__le32) )
#define TXINFO_DESC_SIZE ( 1 * sizeof(__le32) )
-#define TXWI_DESC_SIZE ( 4 * sizeof(__le32) )
-#define RXD_DESC_SIZE ( 1 * sizeof(__le32) )
-#define RXWI_DESC_SIZE ( 4 * sizeof(__le32) )
-
-/*
- * TX descriptor format for TX, PRIO and Beacon Ring.
- */
-
-/*
- * Word0
- */
-#define TXD_W0_SD_PTR0 FIELD32(0xffffffff)
-
-/*
- * Word1
- */
-#define TXD_W1_SD_LEN1 FIELD32(0x00003fff)
-#define TXD_W1_LAST_SEC1 FIELD32(0x00004000)
-#define TXD_W1_BURST FIELD32(0x00008000)
-#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000)
-#define TXD_W1_LAST_SEC0 FIELD32(0x40000000)
-#define TXD_W1_DMA_DONE FIELD32(0x80000000)
-
-/*
- * Word2
- */
-#define TXD_W2_SD_PTR1 FIELD32(0xffffffff)
-
-/*
- * Word3
- * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI
- * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler.
- * 0:MGMT, 1:HCCA 2:EDCA
- */
-#define TXD_W3_WIV FIELD32(0x01000000)
-#define TXD_W3_QSEL FIELD32(0x06000000)
-#define TXD_W3_TCO FIELD32(0x20000000)
-#define TXD_W3_UCO FIELD32(0x40000000)
-#define TXD_W3_ICO FIELD32(0x80000000)
+#define RXINFO_DESC_SIZE ( 1 * sizeof(__le32) )
/*
* TX Info structure
@@ -1807,52 +101,6 @@ struct mac_iveiv_entry {
#define TXINFO_W0_USB_DMA_TX_BURST FIELD32(0x80000000)
/*
- * TX WI structure
- */
-
-/*
- * Word0
- * FRAG: 1 To inform TKIP engine this is a fragment.
- * MIMO_PS: The remote peer is in dynamic MIMO-PS mode
- * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs
- * BW: Channel bandwidth 20MHz or 40 MHz
- * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED
- */
-#define TXWI_W0_FRAG FIELD32(0x00000001)
-#define TXWI_W0_MIMO_PS FIELD32(0x00000002)
-#define TXWI_W0_CF_ACK FIELD32(0x00000004)
-#define TXWI_W0_TS FIELD32(0x00000008)
-#define TXWI_W0_AMPDU FIELD32(0x00000010)
-#define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0)
-#define TXWI_W0_TX_OP FIELD32(0x00000300)
-#define TXWI_W0_MCS FIELD32(0x007f0000)
-#define TXWI_W0_BW FIELD32(0x00800000)
-#define TXWI_W0_SHORT_GI FIELD32(0x01000000)
-#define TXWI_W0_STBC FIELD32(0x06000000)
-#define TXWI_W0_IFS FIELD32(0x08000000)
-#define TXWI_W0_PHYMODE FIELD32(0xc0000000)
-
-/*
- * Word1
- */
-#define TXWI_W1_ACK FIELD32(0x00000001)
-#define TXWI_W1_NSEQ FIELD32(0x00000002)
-#define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc)
-#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00)
-#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000)
-#define TXWI_W1_PACKETID FIELD32(0xf0000000)
-
-/*
- * Word2
- */
-#define TXWI_W2_IV FIELD32(0xffffffff)
-
-/*
- * Word3
- */
-#define TXWI_W3_EIV FIELD32(0xffffffff)
-
-/*
* RX descriptor format for RX Ring.
*/
@@ -1867,85 +115,25 @@ struct mac_iveiv_entry {
* AMSDU: rx with 802.3 header, not 802.11 header.
*/
-#define RXD_W0_BA FIELD32(0x00000001)
-#define RXD_W0_DATA FIELD32(0x00000002)
-#define RXD_W0_NULLDATA FIELD32(0x00000004)
-#define RXD_W0_FRAG FIELD32(0x00000008)
-#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000010)
-#define RXD_W0_MULTICAST FIELD32(0x00000020)
-#define RXD_W0_BROADCAST FIELD32(0x00000040)
-#define RXD_W0_MY_BSS FIELD32(0x00000080)
-#define RXD_W0_CRC_ERROR FIELD32(0x00000100)
-#define RXD_W0_CIPHER_ERROR FIELD32(0x00000600)
-#define RXD_W0_AMSDU FIELD32(0x00000800)
-#define RXD_W0_HTC FIELD32(0x00001000)
-#define RXD_W0_RSSI FIELD32(0x00002000)
-#define RXD_W0_L2PAD FIELD32(0x00004000)
-#define RXD_W0_AMPDU FIELD32(0x00008000)
-#define RXD_W0_DECRYPTED FIELD32(0x00010000)
-#define RXD_W0_PLCP_RSSI FIELD32(0x00020000)
-#define RXD_W0_CIPHER_ALG FIELD32(0x00040000)
-#define RXD_W0_LAST_AMSDU FIELD32(0x00080000)
-#define RXD_W0_PLCP_SIGNAL FIELD32(0xfff00000)
-
-/*
- * RX WI structure
- */
-
-/*
- * Word0
- */
-#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff)
-#define RXWI_W0_KEY_INDEX FIELD32(0x00000300)
-#define RXWI_W0_BSSID FIELD32(0x00001c00)
-#define RXWI_W0_UDF FIELD32(0x0000e000)
-#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000)
-#define RXWI_W0_TID FIELD32(0xf0000000)
-
-/*
- * Word1
- */
-#define RXWI_W1_FRAG FIELD32(0x0000000f)
-#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0)
-#define RXWI_W1_MCS FIELD32(0x007f0000)
-#define RXWI_W1_BW FIELD32(0x00800000)
-#define RXWI_W1_SHORT_GI FIELD32(0x01000000)
-#define RXWI_W1_STBC FIELD32(0x06000000)
-#define RXWI_W1_PHYMODE FIELD32(0xc0000000)
-
-/*
- * Word2
- */
-#define RXWI_W2_RSSI0 FIELD32(0x000000ff)
-#define RXWI_W2_RSSI1 FIELD32(0x0000ff00)
-#define RXWI_W2_RSSI2 FIELD32(0x00ff0000)
-
-/*
- * Word3
- */
-#define RXWI_W3_SNR0 FIELD32(0x000000ff)
-#define RXWI_W3_SNR1 FIELD32(0x0000ff00)
-
-/*
- * Macros for converting txpower from EEPROM to mac80211 value
- * and from mac80211 value to register value.
- */
-#define MIN_G_TXPOWER 0
-#define MIN_A_TXPOWER -7
-#define MAX_G_TXPOWER 31
-#define MAX_A_TXPOWER 15
-#define DEFAULT_TXPOWER 5
-
-#define TXPOWER_G_FROM_DEV(__txpower) \
- ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
-
-#define TXPOWER_G_TO_DEV(__txpower) \
- clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER)
-
-#define TXPOWER_A_FROM_DEV(__txpower) \
- ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
-
-#define TXPOWER_A_TO_DEV(__txpower) \
- clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER)
+#define RXINFO_W0_BA FIELD32(0x00000001)
+#define RXINFO_W0_DATA FIELD32(0x00000002)
+#define RXINFO_W0_NULLDATA FIELD32(0x00000004)
+#define RXINFO_W0_FRAG FIELD32(0x00000008)
+#define RXINFO_W0_UNICAST_TO_ME FIELD32(0x00000010)
+#define RXINFO_W0_MULTICAST FIELD32(0x00000020)
+#define RXINFO_W0_BROADCAST FIELD32(0x00000040)
+#define RXINFO_W0_MY_BSS FIELD32(0x00000080)
+#define RXINFO_W0_CRC_ERROR FIELD32(0x00000100)
+#define RXINFO_W0_CIPHER_ERROR FIELD32(0x00000600)
+#define RXINFO_W0_AMSDU FIELD32(0x00000800)
+#define RXINFO_W0_HTC FIELD32(0x00001000)
+#define RXINFO_W0_RSSI FIELD32(0x00002000)
+#define RXINFO_W0_L2PAD FIELD32(0x00004000)
+#define RXINFO_W0_AMPDU FIELD32(0x00008000)
+#define RXINFO_W0_DECRYPTED FIELD32(0x00010000)
+#define RXINFO_W0_PLCP_RSSI FIELD32(0x00020000)
+#define RXINFO_W0_CIPHER_ALG FIELD32(0x00040000)
+#define RXINFO_W0_LAST_AMSDU FIELD32(0x00080000)
+#define RXINFO_W0_PLCP_SIGNAL FIELD32(0xfff00000)
#endif /* RT2800USB_H */
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 27bc6b7fbfd..4d841c07c97 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -144,6 +145,11 @@ struct avg_val {
int avg_weight;
};
+enum rt2x00_chip_intf {
+ RT2X00_CHIP_INTF_PCI,
+ RT2X00_CHIP_INTF_USB,
+};
+
/*
* Chipset identification
* The chipset on the device is composed of a RT and RF chip.
@@ -158,10 +164,20 @@ struct rt2x00_chip {
#define RT2561 0x0302
#define RT2661 0x0401
#define RT2571 0x1300
+#define RT2860 0x0601 /* 2.4GHz PCI/CB */
+#define RT2860D 0x0681 /* 2.4GHz, 5GHz PCI/CB */
+#define RT2890 0x0701 /* 2.4GHz PCIe */
+#define RT2890D 0x0781 /* 2.4GHz, 5GHz PCIe */
+#define RT2880 0x2880 /* WSOC */
+#define RT3052 0x3052 /* WSOC */
+#define RT3090 0x3090 /* 2.4GHz PCIe */
#define RT2870 0x1600
+#define RT3070 0x1800
u16 rf;
u32 rev;
+
+ enum rt2x00_chip_intf intf;
};
/*
@@ -299,13 +315,6 @@ struct link {
struct avg_val avg_rssi;
/*
- * Currently precalculated percentages of successful
- * TX and RX frames.
- */
- int rx_percentage;
- int tx_percentage;
-
- /*
* Work structure for scheduling periodic link tuning.
*/
struct delayed_work work;
@@ -579,6 +588,7 @@ struct rt2x00_ops {
const unsigned int eeprom_size;
const unsigned int rf_size;
const unsigned int tx_queues;
+ const unsigned int extra_tx_headroom;
const struct data_queue_desc *rx;
const struct data_queue_desc *tx;
const struct data_queue_desc *bcn;
@@ -835,9 +845,23 @@ struct rt2x00_dev {
* Firmware image.
*/
const struct firmware *fw;
+
+ /*
+ * Driver specific data.
+ */
+ void *priv;
};
/*
+ * Register defines.
+ * Some registers require multiple attempts before success,
+ * in those cases REGISTER_BUSY_COUNT attempts should be
+ * taken with a REGISTER_BUSY_DELAY interval.
+ */
+#define REGISTER_BUSY_COUNT 5
+#define REGISTER_BUSY_DELAY 100
+
+/*
* Generic RF access.
* The RF is being accessed by word index.
*/
@@ -883,10 +907,6 @@ static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev,
static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev,
const u16 rt, const u16 rf, const u32 rev)
{
- INFO(rt2x00dev,
- "Chipset detected - rt: %04x, rf: %04x, rev: %08x.\n",
- rt, rf, rev);
-
rt2x00dev->chip.rt = rt;
rt2x00dev->chip.rf = rf;
rt2x00dev->chip.rev = rev;
@@ -904,6 +924,13 @@ static inline void rt2x00_set_chip_rf(struct rt2x00_dev *rt2x00dev,
rt2x00_set_chip(rt2x00dev, rt2x00dev->chip.rt, rf, rev);
}
+static inline void rt2x00_print_chip(struct rt2x00_dev *rt2x00dev)
+{
+ INFO(rt2x00dev,
+ "Chipset detected - rt: %04x, rf: %04x, rev: %08x.\n",
+ rt2x00dev->chip.rt, rt2x00dev->chip.rf, rt2x00dev->chip.rev);
+}
+
static inline char rt2x00_rt(const struct rt2x00_chip *chipset, const u16 chip)
{
return (chipset->rt == chip);
@@ -925,6 +952,28 @@ static inline bool rt2x00_check_rev(const struct rt2x00_chip *chipset,
return ((chipset->rev & mask) == rev);
}
+static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev,
+ enum rt2x00_chip_intf intf)
+{
+ rt2x00dev->chip.intf = intf;
+}
+
+static inline bool rt2x00_intf(const struct rt2x00_chip *chipset,
+ enum rt2x00_chip_intf intf)
+{
+ return (chipset->intf == intf);
+}
+
+static inline bool rt2x00_intf_is_pci(struct rt2x00_dev *rt2x00dev)
+{
+ return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_PCI);
+}
+
+static inline bool rt2x00_intf_is_usb(struct rt2x00_dev *rt2x00dev)
+{
+ return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_USB);
+}
+
/**
* rt2x00queue_map_txskb - Map a skb into DMA for TX purposes.
* @rt2x00dev: Pointer to &struct rt2x00_dev.
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 40a201e2e15..098315a271c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c
index de36837dcf8..d291c7862e1 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index 68bc9bb1dbf..7d323a763b5 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/rt2x00/rt2x00debug.h
index 035cbc98c59..fa11409cb5c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.h
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 73bbec58341..06c43ca39bf 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -205,6 +205,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
enum data_queue_qid qid = skb_get_queue_mapping(entry->skb);
unsigned int header_length = ieee80211_get_hdrlen_from_skb(entry->skb);
u8 rate_idx, rate_flags, retry_rates;
+ u8 skbdesc_flags = skbdesc->flags;
unsigned int i;
bool success;
@@ -287,12 +288,12 @@ void rt2x00lib_txdone(struct queue_entry *entry,
}
/*
- * Only send the status report to mac80211 when TX status was
- * requested by it. If this was a extra frame coming through
- * a mac80211 library call (RTS/CTS) then we should not send the
- * status report back.
+ * Only send the status report to mac80211 when it's a frame
+ * that originated in mac80211. If this was a extra frame coming
+ * through a mac80211 library call (RTS/CTS) then we should not
+ * send the status report back.
*/
- if (tx_info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ if (!(skbdesc_flags & SKBDESC_NOT_MAC80211))
ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb);
else
dev_kfree_skb_irq(entry->skb);
@@ -430,7 +431,6 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
rx_status->mactime = rxdesc.timestamp;
rx_status->rate_idx = rate_idx;
- rx_status->qual = rt2x00link_calculate_signal(rt2x00dev, rxdesc.rssi);
rx_status->signal = rxdesc.rssi;
rx_status->noise = rxdesc.noise;
rx_status->flag = rxdesc.flags;
@@ -684,6 +684,11 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
rt2x00dev->hw->queues = rt2x00dev->ops->tx_queues;
/*
+ * Initialize extra TX headroom required.
+ */
+ rt2x00dev->hw->extra_tx_headroom = rt2x00dev->ops->extra_tx_headroom;
+
+ /*
* Register HW.
*/
status = ieee80211_register_hw(rt2x00dev->hw);
diff --git a/drivers/net/wireless/rt2x00/rt2x00dump.h b/drivers/net/wireless/rt2x00/rt2x00dump.h
index fdedb512292..727019a748e 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dump.h
+++ b/drivers/net/wireless/rt2x00/rt2x00dump.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c
index d2deea2f267..34beb00c434 100644
--- a/drivers/net/wireless/rt2x00/rt2x00firmware.c
+++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00ht.c b/drivers/net/wireless/rt2x00/rt2x00ht.c
index e3cec839e54..1056c92143a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00ht.c
+++ b/drivers/net/wireless/rt2x00/rt2x00ht.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/rt2x00/rt2x00leds.c
index 49671fed91d..ca585e34d00 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.c
+++ b/drivers/net/wireless/rt2x00/rt2x00leds.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.h b/drivers/net/wireless/rt2x00/rt2x00leds.h
index 1046977e6a1..3b46f0c3332 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.h
+++ b/drivers/net/wireless/rt2x00/rt2x00leds.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -33,8 +33,6 @@ enum led_type {
LED_TYPE_QUALITY,
};
-#ifdef CONFIG_RT2X00_LIB_LEDS
-
struct rt2x00_led {
struct rt2x00_dev *rt2x00dev;
struct led_classdev led_dev;
@@ -45,6 +43,4 @@ struct rt2x00_led {
#define LED_REGISTERED ( 1 << 1 )
};
-#endif /* CONFIG_RT2X00_LIB_LEDS */
-
#endif /* RT2X00LEDS_H */
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 567f029a8cd..be2e37fb407 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -161,8 +162,10 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length);
* rt2x00queue_write_tx_frame - Write TX frame to hardware
* @queue: Queue over which the frame should be send
* @skb: The skb to send
+ * @local: frame is not from mac80211
*/
-int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb);
+int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
+ bool local);
/**
* rt2x00queue_update_beacon - Send new beacon from mac80211 to hardware
@@ -223,19 +226,6 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,
struct rxdone_entry_desc *rxdesc);
/**
- * rt2x00link_calculate_signal - Calculate signal quality
- * @rt2x00dev: Pointer to &struct rt2x00_dev.
- * @rssi: RX Frame RSSI
- *
- * Calculate the signal quality of a frame based on the rssi
- * measured during the receiving of the frame and the global
- * link quality statistics measured since the start of the
- * link tuning. The result is a value between 0 and 100 which
- * is an indication of the signal quality.
- */
-int rt2x00link_calculate_signal(struct rt2x00_dev *rt2x00dev, int rssi);
-
-/**
* rt2x00link_start_tuner - Start periodic link tuner work
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c
index c708d0be915..0efbf5a6c25 100644
--- a/drivers/net/wireless/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/rt2x00/rt2x00link.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -36,24 +36,6 @@
#define DEFAULT_RSSI -128
/*
- * When no TX/RX percentage could be calculated due to lack of
- * frames on the air, we fallback to a percentage of 50%.
- * This will assure we will get at least get some decent value
- * when the link tuner starts.
- * The value will be dropped and overwritten with the correct (measured)
- * value anyway during the first run of the link tuner.
- */
-#define DEFAULT_PERCENTAGE 50
-
-/*
- * Small helper macro for percentage calculation
- * This is a very simple macro with the only catch that it will
- * produce a default value in case no total value was provided.
- */
-#define PERCENTAGE(__value, __total) \
- ( (__total) ? (((__value) * 100) / (__total)) : (DEFAULT_PERCENTAGE) )
-
-/*
* Helper struct and macro to work with moving/walking averages.
* When adding a value to the average value the following calculation
* is needed:
@@ -91,27 +73,6 @@
__new; \
})
-/*
- * For calculating the Signal quality we have determined
- * the total number of success and failed RX and TX frames.
- * With the addition of the average RSSI value we can determine
- * the link quality using the following algorithm:
- *
- * rssi_percentage = (avg_rssi * 100) / rssi_offset
- * rx_percentage = (rx_success * 100) / rx_total
- * tx_percentage = (tx_success * 100) / tx_total
- * avg_signal = ((WEIGHT_RSSI * avg_rssi) +
- * (WEIGHT_TX * tx_percentage) +
- * (WEIGHT_RX * rx_percentage)) / 100
- *
- * This value should then be checked to not be greater then 100.
- * This means the values of WEIGHT_RSSI, WEIGHT_RX, WEIGHT_TX must
- * sum up to 100 as well.
- */
-#define WEIGHT_RSSI 20
-#define WEIGHT_RX 40
-#define WEIGHT_TX 40
-
static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)
{
struct link_ant *ant = &rt2x00dev->link.ant;
@@ -304,46 +265,6 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,
ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi);
}
-static void rt2x00link_precalculate_signal(struct rt2x00_dev *rt2x00dev)
-{
- struct link *link = &rt2x00dev->link;
- struct link_qual *qual = &rt2x00dev->link.qual;
-
- link->rx_percentage =
- PERCENTAGE(qual->rx_success, qual->rx_failed + qual->rx_success);
- link->tx_percentage =
- PERCENTAGE(qual->tx_success, qual->tx_failed + qual->tx_success);
-}
-
-int rt2x00link_calculate_signal(struct rt2x00_dev *rt2x00dev, int rssi)
-{
- struct link *link = &rt2x00dev->link;
- int rssi_percentage = 0;
- int signal;
-
- /*
- * We need a positive value for the RSSI.
- */
- if (rssi < 0)
- rssi += rt2x00dev->rssi_offset;
-
- /*
- * Calculate the different percentages,
- * which will be used for the signal.
- */
- rssi_percentage = PERCENTAGE(rssi, rt2x00dev->rssi_offset);
-
- /*
- * Add the individual percentages and use the WEIGHT
- * defines to calculate the current link signal.
- */
- signal = ((WEIGHT_RSSI * rssi_percentage) +
- (WEIGHT_TX * link->tx_percentage) +
- (WEIGHT_RX * link->rx_percentage)) / 100;
-
- return max_t(int, signal, 100);
-}
-
void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
{
struct link *link = &rt2x00dev->link;
@@ -357,9 +278,6 @@ void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
if (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)
return;
- link->rx_percentage = DEFAULT_PERCENTAGE;
- link->tx_percentage = DEFAULT_PERCENTAGE;
-
rt2x00link_reset_tuner(rt2x00dev, false);
if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
@@ -448,12 +366,6 @@ static void rt2x00link_tuner(struct work_struct *work)
rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
/*
- * Precalculate a portion of the link signal which is
- * in based on the tx/rx success/failure counters.
- */
- rt2x00link_precalculate_signal(rt2x00dev);
-
- /*
* Send a signal to the led to update the led signal strength.
*/
rt2x00leds_led_quality(rt2x00dev, qual->rssi);
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 929b85f34f3..de549c244ed 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -66,7 +66,6 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev,
rts_info = IEEE80211_SKB_CB(skb);
rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS;
rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT;
- rts_info->flags &= ~IEEE80211_TX_CTL_REQ_TX_STATUS;
if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
rts_info->flags |= IEEE80211_TX_CTL_NO_ACK;
@@ -91,7 +90,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev,
frag_skb->data, data_length, tx_info,
(struct ieee80211_rts *)(skb->data));
- retval = rt2x00queue_write_tx_frame(queue, skb);
+ retval = rt2x00queue_write_tx_frame(queue, skb, true);
if (retval) {
dev_kfree_skb_any(skb);
WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n");
@@ -104,10 +103,8 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
enum data_queue_qid qid = skb_get_queue_mapping(skb);
struct data_queue *queue;
- u16 frame_control;
/*
* Mac80211 might be calling this function while we are trying
@@ -142,7 +139,6 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* either RTS or CTS-to-self frame and handles everything
* inside the hardware.
*/
- frame_control = le16_to_cpu(ieee80211hdr->frame_control);
if ((tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS |
IEEE80211_TX_RC_USE_CTS_PROTECT)) &&
!rt2x00dev->ops->hw->set_rts_threshold) {
@@ -153,7 +149,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
goto exit_fail;
}
- if (rt2x00queue_write_tx_frame(queue, skb))
+ if (rt2x00queue_write_tx_frame(queue, skb, false))
goto exit_fail;
if (rt2x00queue_threshold(queue))
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c
index cdd5154bd4c..0feb4d0e466 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.c
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -310,6 +310,8 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
rt2x00dev->irq = pci_dev->irq;
rt2x00dev->name = pci_name(pci_dev);
+ rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI);
+
/*
* Determine RT chipset by reading PCI header.
*/
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h
index 15a12487e04..d4f9449ab0a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.h
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -35,15 +35,6 @@
#define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops)
/*
- * Register defines.
- * Some registers require multiple attempts before success,
- * in those cases REGISTER_BUSY_COUNT attempts should be
- * taken with a REGISTER_BUSY_DELAY interval.
- */
-#define REGISTER_BUSY_COUNT 5
-#define REGISTER_BUSY_DELAY 100
-
-/*
* Register access.
*/
static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev,
@@ -53,10 +44,9 @@ static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev,
*value = readl(rt2x00dev->csr.base + offset);
}
-static inline void
-rt2x00pci_register_multiread(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- void *value, const u16 length)
+static inline void rt2x00pci_register_multiread(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ void *value, const u32 length)
{
memcpy_fromio(value, rt2x00dev->csr.base + offset, length);
}
@@ -68,10 +58,10 @@ static inline void rt2x00pci_register_write(struct rt2x00_dev *rt2x00dev,
writel(value, rt2x00dev->csr.base + offset);
}
-static inline void
-rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- const void *value, const u16 length)
+static inline void rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev,
+ const unsigned int offset,
+ const void *value,
+ const u32 length)
{
memcpy_toio(rt2x00dev->csr.base + offset, value, length);
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 577029efe32..239afc7a9c0 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -161,10 +162,10 @@ void rt2x00queue_align_frame(struct sk_buff *skb)
skb_trim(skb, frame_length);
}
-void rt2x00queue_align_payload(struct sk_buff *skb, unsigned int header_lengt)
+void rt2x00queue_align_payload(struct sk_buff *skb, unsigned int header_length)
{
unsigned int frame_length = skb->len;
- unsigned int align = ALIGN_SIZE(skb, header_lengt);
+ unsigned int align = ALIGN_SIZE(skb, header_length);
if (!align)
return;
@@ -213,7 +214,7 @@ void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
skb_push(skb, header_align);
memmove(skb->data, skb->data + header_align, header_length);
memmove(skb->data + header_length + l2pad,
- skb->data + header_length + l2pad + header_align,
+ skb->data + header_length + l2pad + payload_align,
frame_length - header_length);
skbdesc->flags |= SKBDESC_L2_PADDED;
}
@@ -453,7 +454,8 @@ static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry,
rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, queue->qid);
}
-int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
+int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
+ bool local)
{
struct ieee80211_tx_info *tx_info;
struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX);
@@ -494,6 +496,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb)
skbdesc->tx_rate_idx = rate_idx;
skbdesc->tx_rate_flags = rate_flags;
+ if (local)
+ skbdesc->flags |= SKBDESC_NOT_MAC80211;
+
/*
* When hardware encryption is supported, and this frame
* is to be encrypted, we should strip the IV/EIV data from
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index a5591fb2b19..70775e5ba1a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -94,12 +94,15 @@ enum data_queue_qid {
* mac80211 but was stripped for processing by the driver.
* @SKBDESC_L2_PADDED: Payload has been padded for 4-byte alignment,
* the padded bytes are located between header and payload.
+ * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211,
+ * don't try to pass it back.
*/
enum skb_frame_desc_flags {
SKBDESC_DMA_MAPPED_RX = 1 << 0,
SKBDESC_DMA_MAPPED_TX = 1 << 1,
SKBDESC_IV_STRIPPED = 1 << 2,
- SKBDESC_L2_PADDED = 1 << 3
+ SKBDESC_L2_PADDED = 1 << 3,
+ SKBDESC_NOT_MAC80211 = 1 << 4,
};
/**
diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h
index 983e52e127a..603bfc0adaa 100644
--- a/drivers/net/wireless/rt2x00/rt2x00reg.h
+++ b/drivers/net/wireless/rt2x00/rt2x00reg.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/rt2x00/rt2x00soc.c
new file mode 100644
index 00000000000..19e684f8ffa
--- /dev/null
+++ b/drivers/net/wireless/rt2x00/rt2x00soc.c
@@ -0,0 +1,165 @@
+/*
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2004 - 2009 Felix Fietkau <nbd@openwrt.org>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ Module: rt2x00soc
+ Abstract: rt2x00 generic soc device routines.
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "rt2x00.h"
+#include "rt2x00soc.h"
+
+static void rt2x00soc_free_reg(struct rt2x00_dev *rt2x00dev)
+{
+ kfree(rt2x00dev->rf);
+ rt2x00dev->rf = NULL;
+
+ kfree(rt2x00dev->eeprom);
+ rt2x00dev->eeprom = NULL;
+}
+
+static int rt2x00soc_alloc_reg(struct rt2x00_dev *rt2x00dev)
+{
+ struct platform_device *pdev = to_platform_device(rt2x00dev->dev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ rt2x00dev->csr.base = (void __iomem *)KSEG1ADDR(res->start);
+ if (!rt2x00dev->csr.base)
+ goto exit;
+
+ rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL);
+ if (!rt2x00dev->eeprom)
+ goto exit;
+
+ rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL);
+ if (!rt2x00dev->rf)
+ goto exit;
+
+ return 0;
+
+exit:
+ ERROR_PROBE("Failed to allocate registers.\n");
+ rt2x00soc_free_reg(rt2x00dev);
+
+ return -ENOMEM;
+}
+
+int rt2x00soc_probe(struct platform_device *pdev,
+ const unsigned short chipset,
+ const struct rt2x00_ops *ops)
+{
+ struct ieee80211_hw *hw;
+ struct rt2x00_dev *rt2x00dev;
+ int retval;
+
+ hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
+ if (!hw) {
+ ERROR_PROBE("Failed to allocate hardware.\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, hw);
+
+ rt2x00dev = hw->priv;
+ rt2x00dev->dev = &pdev->dev;
+ rt2x00dev->ops = ops;
+ rt2x00dev->hw = hw;
+ rt2x00dev->irq = platform_get_irq(pdev, 0);
+ rt2x00dev->name = pdev->dev.driver->name;
+
+ /*
+ * SoC devices mimic PCI behavior.
+ */
+ rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI);
+
+ rt2x00_set_chip_rt(rt2x00dev, chipset);
+
+ retval = rt2x00soc_alloc_reg(rt2x00dev);
+ if (retval)
+ goto exit_free_device;
+
+ retval = rt2x00lib_probe_dev(rt2x00dev);
+ if (retval)
+ goto exit_free_reg;
+
+ return 0;
+
+exit_free_reg:
+ rt2x00soc_free_reg(rt2x00dev);
+
+exit_free_device:
+ ieee80211_free_hw(hw);
+
+ return retval;
+}
+
+int rt2x00soc_remove(struct platform_device *pdev)
+{
+ struct ieee80211_hw *hw = platform_get_drvdata(pdev);
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+
+ /*
+ * Free all allocated data.
+ */
+ rt2x00lib_remove_dev(rt2x00dev);
+ rt2x00soc_free_reg(rt2x00dev);
+ ieee80211_free_hw(hw);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2x00soc_remove);
+
+#ifdef CONFIG_PM
+int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ieee80211_hw *hw = platform_get_drvdata(pdev);
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+
+ return rt2x00lib_suspend(rt2x00dev, state);
+}
+EXPORT_SYMBOL_GPL(rt2x00soc_suspend);
+
+int rt2x00soc_resume(struct platform_device *pdev)
+{
+ struct ieee80211_hw *hw = platform_get_drvdata(pdev);
+ struct rt2x00_dev *rt2x00dev = hw->priv;
+
+ return rt2x00lib_resume(rt2x00dev);
+}
+EXPORT_SYMBOL_GPL(rt2x00soc_resume);
+#endif /* CONFIG_PM */
+
+/*
+ * rt2x00soc module information.
+ */
+MODULE_AUTHOR(DRV_PROJECT);
+MODULE_VERSION(DRV_VERSION);
+MODULE_DESCRIPTION("rt2x00 soc library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/rt2x00/rt2x00soc.h
new file mode 100644
index 00000000000..8a3416624af
--- /dev/null
+++ b/drivers/net/wireless/rt2x00/rt2x00soc.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ <http://rt2x00.serialmonkey.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the
+ Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ Module: rt2x00soc
+ Abstract: Data structures for the rt2x00soc module.
+ */
+
+#ifndef RT2X00SOC_H
+#define RT2X00SOC_H
+
+#define KSEG1ADDR(__ptr) __ptr
+
+#define __rt2x00soc_probe(__chipset, __ops) \
+static int __rt2x00soc_probe(struct platform_device *pdev) \
+{ \
+ return rt2x00soc_probe(pdev, (__chipset), (__ops)); \
+}
+
+/*
+ * SoC driver handlers.
+ */
+int rt2x00soc_probe(struct platform_device *pdev,
+ const unsigned short chipset,
+ const struct rt2x00_ops *ops);
+int rt2x00soc_remove(struct platform_device *pdev);
+#ifdef CONFIG_PM
+int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state);
+int rt2x00soc_resume(struct platform_device *pdev);
+#else
+#define rt2x00soc_suspend NULL
+#define rt2x00soc_resume NULL
+#endif /* CONFIG_PM */
+
+#endif /* RT2X00SOC_H */
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index f02b48a9059..0a751e73aa0 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -160,7 +160,7 @@ EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_large_buff);
int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
const unsigned int offset,
- struct rt2x00_field32 field,
+ const struct rt2x00_field32 field,
u32 *reg)
{
unsigned int i;
@@ -653,6 +653,8 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
rt2x00dev->ops = ops;
rt2x00dev->hw = hw;
+ rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB);
+
retval = rt2x00usb_alloc_reg(rt2x00dev);
if (retval)
goto exit_free_device;
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h
index bd2d59c85f1..3da6841b5d4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -26,6 +26,8 @@
#ifndef RT2X00USB_H
#define RT2X00USB_H
+#include <linux/usb.h>
+
#define to_usb_device_intf(d) \
({ \
struct usb_interface *intf = to_usb_interface(d); \
@@ -39,17 +41,11 @@
#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops)
/*
- * Register defines.
- * Some registers require multiple attempts before success,
- * in those cases REGISTER_BUSY_COUNT attempts should be
- * taken with a REGISTER_BUSY_DELAY interval.
* For USB vendor requests we need to pass a timeout
* time in ms, for this we use the REGISTER_TIMEOUT,
* however when loading firmware a higher value is
* required. In that case we use the REGISTER_TIMEOUT_FIRMWARE.
*/
-#define REGISTER_BUSY_COUNT 5
-#define REGISTER_BUSY_DELAY 100
#define REGISTER_TIMEOUT 500
#define REGISTER_TIMEOUT_FIRMWARE 1000
@@ -232,7 +228,7 @@ static inline int rt2x00usb_eeprom_read(struct rt2x00_dev *rt2x00dev,
}
/**
- * rt2x00usb_regbusy_read - Read 32bit register word
+ * rt2x00usb_register_read - Read 32bit register word
* @rt2x00dev: Device pointer, see &struct rt2x00_dev.
* @offset: Register offset
* @value: Pointer to where register contents should be stored
@@ -340,12 +336,13 @@ static inline void rt2x00usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
* through rt2x00usb_vendor_request_buff().
*/
static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
- const unsigned int offset,
- void *value, const u32 length)
+ const unsigned int offset,
+ const void *value,
+ const u32 length)
{
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
USB_VENDOR_REQUEST_OUT, offset,
- value, length,
+ (void *)value, length,
REGISTER_TIMEOUT32(length));
}
@@ -364,7 +361,7 @@ static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev,
*/
int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
const unsigned int offset,
- struct rt2x00_field32 field,
+ const struct rt2x00_field32 field,
u32 *reg);
/*
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index b20e3eac9d6..687e17dc2e9 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -51,7 +51,7 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
* These indirect registers work with busy bits,
* and we will try maximal REGISTER_BUSY_COUNT times to access
* the register while taking a REGISTER_BUSY_DELAY us delay
- * between each attampt. When the busy bit is still set at that time,
+ * between each attempt. When the busy bit is still set at that time,
* the access attempt is considered to have failed,
* and we will print an error.
*/
@@ -386,7 +386,7 @@ static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev,
* The driver does not support the IV/EIV generation
* in hardware. However it doesn't support the IV/EIV
* inside the ieee80211 frame either, but requires it
- * to be provided seperately for the descriptor.
+ * to be provided separately for the descriptor.
* rt2x00lib will cut the IV/EIV data out of all frames
* given to us by mac80211, but we must tell mac80211
* to generate the IV/EIV data.
@@ -397,7 +397,7 @@ static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev,
/*
* SEC_CSR0 contains only single-bit fields to indicate
* a particular key is valid. Because using the FIELD32()
- * defines directly will cause a lot of overhead we use
+ * defines directly will cause a lot of overhead, we use
* a calculation to determine the correct bit directly.
*/
mask = 1 << key->hw_key_idx;
@@ -425,11 +425,11 @@ static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
/*
* rt2x00lib can't determine the correct free
* key_idx for pairwise keys. We have 2 registers
- * with key valid bits. The goal is simple, read
- * the first register, if that is full move to
+ * with key valid bits. The goal is simple: read
+ * the first register. If that is full, move to
* the next register.
- * When both registers are full, we drop the key,
- * otherwise we use the first invalid entry.
+ * When both registers are full, we drop the key.
+ * Otherwise, we use the first invalid entry.
*/
rt2x00pci_register_read(rt2x00dev, SEC_CSR2, &reg);
if (reg && reg == ~0) {
@@ -464,8 +464,8 @@ static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
&addr_entry, sizeof(addr_entry));
/*
- * Enable pairwise lookup table for given BSS idx,
- * without this received frames will not be decrypted
+ * Enable pairwise lookup table for given BSS idx.
+ * Without this, received frames will not be decrypted
* by the hardware.
*/
rt2x00pci_register_read(rt2x00dev, SEC_CSR4, &reg);
@@ -487,7 +487,7 @@ static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
/*
* SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate
* a particular key is valid. Because using the FIELD32()
- * defines directly will cause a lot of overhead we use
+ * defines directly will cause a lot of overhead, we use
* a calculation to determine the correct bit directly.
*/
if (key->hw_key_idx < 32) {
@@ -556,7 +556,7 @@ static void rt61pci_config_intf(struct rt2x00_dev *rt2x00dev,
if (flags & CONFIG_UPDATE_TYPE) {
/*
* Clear current synchronisation setup.
- * For the Beacon base registers we only need to clear
+ * For the Beacon base registers, we only need to clear
* the first byte since that byte contains the VALID and OWNER
* bits which (when set to 0) will invalidate the entire beacon.
*/
@@ -1168,8 +1168,8 @@ static int rt61pci_check_firmware(struct rt2x00_dev *rt2x00dev,
return FW_BAD_LENGTH;
/*
- * The last 2 bytes in the firmware array are the crc checksum itself,
- * this means that we should never pass those 2 bytes to the crc
+ * The last 2 bytes in the firmware array are the crc checksum itself.
+ * This means that we should never pass those 2 bytes to the crc
* algorithm.
*/
fw_crc = (data[len - 2] << 8 | data[len - 1]);
@@ -1986,7 +1986,7 @@ static void rt61pci_fill_rxdone(struct queue_entry *entry,
/*
* Hardware has stripped IV/EIV data from 802.11 frame during
- * decryption. It has provided the data seperately but rt2x00lib
+ * decryption. It has provided the data separately but rt2x00lib
* should decide if it should be reinserted.
*/
rxdesc->flags |= RX_FLAG_IV_STRIPPED;
@@ -2042,7 +2042,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
* During each loop we will compare the freshly read
* STA_CSR4 register value with the value read from
* the previous loop. If the 2 values are equal then
- * we should stop processing because the chance it
+ * we should stop processing because the chance is
* quite big that the device has been unplugged and
* we risk going into an endless loop.
*/
@@ -2300,6 +2300,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
rt2x00pci_register_read(rt2x00dev, MAC_CSR0, &reg);
rt2x00_set_chip_rf(rt2x00dev, value, reg);
+ rt2x00_print_chip(rt2x00dev);
if (!rt2x00_rf(&rt2x00dev->chip, RF5225) &&
!rt2x00_rf(&rt2x00dev->chip, RF5325) &&
@@ -2330,7 +2331,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
__set_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags);
/*
- * Detect if this device has an hardware controlled radio.
+ * Detect if this device has a hardware controlled radio.
*/
if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
__set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
@@ -2355,7 +2356,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
__set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags);
/*
- * When working with a RF2529 chip without double antenna
+ * When working with a RF2529 chip without double antenna,
* the antenna settings should be gathered from the NIC
* eeprom word.
*/
@@ -2545,7 +2546,6 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
- rt2x00dev->hw->extra_tx_headroom = 0;
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
@@ -2668,7 +2668,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
/*
* We only need to perform additional register initialization
- * for WMM queues/
+ * for WMM queues.
*/
if (queue_idx >= 4)
return 0;
@@ -2787,19 +2787,20 @@ static const struct data_queue_desc rt61pci_queue_bcn = {
};
static const struct rt2x00_ops rt61pci_ops = {
- .name = KBUILD_MODNAME,
- .max_sta_intf = 1,
- .max_ap_intf = 4,
- .eeprom_size = EEPROM_SIZE,
- .rf_size = RF_SIZE,
- .tx_queues = NUM_TX_QUEUES,
- .rx = &rt61pci_queue_rx,
- .tx = &rt61pci_queue_tx,
- .bcn = &rt61pci_queue_bcn,
- .lib = &rt61pci_rt2x00_ops,
- .hw = &rt61pci_mac80211_ops,
+ .name = KBUILD_MODNAME,
+ .max_sta_intf = 1,
+ .max_ap_intf = 4,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .extra_tx_headroom = 0,
+ .rx = &rt61pci_queue_rx,
+ .tx = &rt61pci_queue_tx,
+ .bcn = &rt61pci_queue_bcn,
+ .lib = &rt61pci_rt2x00_ops,
+ .hw = &rt61pci_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
- .debugfs = &rt61pci_rt2x00debug,
+ .debugfs = &rt61pci_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
};
diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h
index 93eb699165c..8f13810622b 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.h
+++ b/drivers/net/wireless/rt2x00/rt61pci.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -37,7 +37,7 @@
/*
* Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
*/
#define DEFAULT_RSSI_OFFSET 120
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 14e7bb21007..ced3b6ab5e1 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -1825,6 +1825,7 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE);
rt2x00usb_register_read(rt2x00dev, MAC_CSR0, &reg);
rt2x00_set_chip(rt2x00dev, RT2571, value, reg);
+ rt2x00_print_chip(rt2x00dev);
if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0x25730) ||
rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) {
@@ -2068,7 +2069,6 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK;
- rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE;
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
@@ -2305,19 +2305,20 @@ static const struct data_queue_desc rt73usb_queue_bcn = {
};
static const struct rt2x00_ops rt73usb_ops = {
- .name = KBUILD_MODNAME,
- .max_sta_intf = 1,
- .max_ap_intf = 4,
- .eeprom_size = EEPROM_SIZE,
- .rf_size = RF_SIZE,
- .tx_queues = NUM_TX_QUEUES,
- .rx = &rt73usb_queue_rx,
- .tx = &rt73usb_queue_tx,
- .bcn = &rt73usb_queue_bcn,
- .lib = &rt73usb_rt2x00_ops,
- .hw = &rt73usb_mac80211_ops,
+ .name = KBUILD_MODNAME,
+ .max_sta_intf = 1,
+ .max_ap_intf = 4,
+ .eeprom_size = EEPROM_SIZE,
+ .rf_size = RF_SIZE,
+ .tx_queues = NUM_TX_QUEUES,
+ .extra_tx_headroom = TXD_DESC_SIZE,
+ .rx = &rt73usb_queue_rx,
+ .tx = &rt73usb_queue_tx,
+ .bcn = &rt73usb_queue_bcn,
+ .lib = &rt73usb_rt2x00_ops,
+ .hw = &rt73usb_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS
- .debugfs = &rt73usb_rt2x00debug,
+ .debugfs = &rt73usb_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */
};
diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h
index 81fe0be51c4..7942f810e92 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.h
+++ b/drivers/net/wireless/rt2x00/rt73usb.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 rt2x00 SourceForge Project
+ Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -37,7 +37,7 @@
/*
* Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
*/
#define DEFAULT_RSSI_OFFSET 120
diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 16429c49139..a1a3dd15c66 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -548,7 +548,7 @@ static int rtl8180_start(struct ieee80211_hw *dev)
rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma);
rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma);
- ret = request_irq(priv->pdev->irq, &rtl8180_interrupt,
+ ret = request_irq(priv->pdev->irq, rtl8180_interrupt,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (ret) {
printk(KERN_ERR "%s: failed to register IRQ handler\n",
diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h
index bf9175a8c1f..abb4907cf29 100644
--- a/drivers/net/wireless/rtl818x/rtl8187.h
+++ b/drivers/net/wireless/rtl818x/rtl8187.h
@@ -119,7 +119,6 @@ struct rtl8187_priv {
} hw_rev;
struct sk_buff_head rx_queue;
u8 signal;
- u8 quality;
u8 noise;
u8 slot_time;
u8 aifsn[4];
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index 2017ccc0014..76973b8c709 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -320,7 +320,6 @@ static void rtl8187_rx_cb(struct urb *urb)
struct ieee80211_rx_status rx_status = { 0 };
int rate, signal;
u32 flags;
- u32 quality;
unsigned long f;
spin_lock_irqsave(&priv->rx_queue.lock, f);
@@ -338,10 +337,9 @@ static void rtl8187_rx_cb(struct urb *urb)
(typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
flags = le32_to_cpu(hdr->flags);
/* As with the RTL8187B below, the AGC is used to calculate
- * signal strength and quality. In this case, the scaling
+ * signal strength. In this case, the scaling
* constants are derived from the output of p54usb.
*/
- quality = 130 - ((41 * hdr->agc) >> 6);
signal = -4 - ((27 * hdr->agc) >> 6);
rx_status.antenna = (hdr->signal >> 7) & 1;
rx_status.mactime = le64_to_cpu(hdr->mac_time);
@@ -354,23 +352,18 @@ static void rtl8187_rx_cb(struct urb *urb)
* In testing, none of these quantities show qualitative
* agreement with AP signal strength, except for the AGC,
* which is inversely proportional to the strength of the
- * signal. In the following, the quality and signal strength
- * are derived from the AGC. The arbitrary scaling constants
+ * signal. In the following, the signal strength
+ * is derived from the AGC. The arbitrary scaling constants
* are chosen to make the results close to the values obtained
* for a BCM4312 using b43 as the driver. The noise is ignored
* for now.
*/
flags = le32_to_cpu(hdr->flags);
- quality = 170 - hdr->agc;
signal = 14 - hdr->agc / 2;
rx_status.antenna = (hdr->rssi >> 7) & 1;
rx_status.mactime = le64_to_cpu(hdr->mac_time);
}
- if (quality > 100)
- quality = 100;
- rx_status.qual = quality;
- priv->quality = quality;
rx_status.signal = signal;
priv->signal = signal;
rate = (flags >> 20) & 0xF;
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig
index 88060e11754..785e0244e30 100644
--- a/drivers/net/wireless/wl12xx/Kconfig
+++ b/drivers/net/wireless/wl12xx/Kconfig
@@ -1,6 +1,6 @@
menuconfig WL12XX
tristate "TI wl12xx driver support"
- depends on MAC80211 && WLAN_80211 && EXPERIMENTAL
+ depends on MAC80211 && EXPERIMENTAL
---help---
This will enable TI wl12xx driver support. The drivers make
use of the mac80211 stack.
@@ -42,6 +42,7 @@ config WL1251_SDIO
config WL1271
tristate "TI wl1271 support"
depends on WL12XX && SPI_MASTER && GENERIC_HARDIRQS
+ depends on INET
select FW_LOADER
select CRC7
---help---
diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h
index 998e4b6252b..054533f7a12 100644
--- a/drivers/net/wireless/wl12xx/wl1251.h
+++ b/drivers/net/wireless/wl12xx/wl1251.h
@@ -269,6 +269,7 @@ struct wl1251 {
void (*set_power)(bool enable);
int irq;
+ bool use_eeprom;
enum wl1251_state state;
struct mutex mutex;
@@ -354,6 +355,8 @@ struct wl1251 {
/* is firmware in elp mode */
bool elp;
+ struct delayed_work elp_work;
+
/* we can be in psm, but not in elp, we have to differentiate */
bool psm;
@@ -374,6 +377,8 @@ struct wl1251 {
u8 buffer_busyword[WL1251_BUSY_WORD_LEN];
struct wl1251_rx_descriptor *rx_descriptor;
+ struct ieee80211_vif *vif;
+
u32 chip_id;
char fw_ver[21];
};
diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.c b/drivers/net/wireless/wl12xx/wl1251_acx.c
index 10b26c4532c..acfa086dbfc 100644
--- a/drivers/net/wireless/wl12xx/wl1251_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1251_acx.c
@@ -494,7 +494,7 @@ out:
return ret;
}
-int wl1251_acx_beacon_filter_opt(struct wl1251 *wl)
+int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter)
{
struct acx_beacon_filter_option *beacon_filter;
int ret;
@@ -507,7 +507,7 @@ int wl1251_acx_beacon_filter_opt(struct wl1251 *wl)
goto out;
}
- beacon_filter->enable = 0;
+ beacon_filter->enable = enable_filter;
beacon_filter->max_num_beacons = 0;
ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_OPT,
@@ -525,6 +525,7 @@ out:
int wl1251_acx_beacon_filter_table(struct wl1251 *wl)
{
struct acx_beacon_filter_ie_table *ie_table;
+ int idx = 0;
int ret;
wl1251_debug(DEBUG_ACX, "acx beacon filter table");
@@ -535,8 +536,10 @@ int wl1251_acx_beacon_filter_table(struct wl1251 *wl)
goto out;
}
- ie_table->num_ie = 0;
- memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE);
+ /* configure default beacon pass-through rules */
+ ie_table->num_ie = 1;
+ ie_table->table[idx++] = BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN;
+ ie_table->table[idx++] = BEACON_RULE_PASS_ON_APPEARANCE;
ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_TABLE,
ie_table, sizeof(*ie_table));
@@ -550,6 +553,35 @@ out:
return ret;
}
+int wl1251_acx_conn_monit_params(struct wl1251 *wl)
+{
+ struct acx_conn_monit_params *acx;
+ int ret;
+
+ wl1251_debug(DEBUG_ACX, "acx connection monitor parameters");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD;
+ acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT;
+
+ ret = wl1251_cmd_configure(wl, ACX_CONN_MONIT_PARAMS,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1251_warning("failed to set connection monitor "
+ "parameters: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
int wl1251_acx_sg_enable(struct wl1251 *wl)
{
struct acx_bt_wlan_coex *pta;
@@ -916,3 +948,31 @@ out:
kfree(mem_conf);
return ret;
}
+
+int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim)
+{
+ struct wl1251_acx_wr_tbtt_and_dtim *acx;
+ int ret;
+
+ wl1251_debug(DEBUG_ACX, "acx tbtt and dtim");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->tbtt = tbtt;
+ acx->dtim = dtim;
+
+ ret = wl1251_cmd_configure(wl, ACX_WR_TBTT_AND_DTIM,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1251_warning("failed to set tbtt and dtim: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.h b/drivers/net/wireless/wl12xx/wl1251_acx.h
index cafb9145950..652371432cd 100644
--- a/drivers/net/wireless/wl12xx/wl1251_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1251_acx.h
@@ -450,6 +450,11 @@ struct acx_beacon_filter_option {
(BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \
BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE))
+#define BEACON_RULE_PASS_ON_CHANGE BIT(0)
+#define BEACON_RULE_PASS_ON_APPEARANCE BIT(1)
+
+#define BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN (37)
+
struct acx_beacon_filter_ie_table {
struct acx_header header;
@@ -458,6 +463,16 @@ struct acx_beacon_filter_ie_table {
u8 pad[3];
} __attribute__ ((packed));
+#define SYNCH_FAIL_DEFAULT_THRESHOLD 10 /* number of beacons */
+#define NO_BEACON_DEFAULT_TIMEOUT (500) /* in microseconds */
+
+struct acx_conn_monit_params {
+ struct acx_header header;
+
+ u32 synch_fail_thold; /* number of beacons missed */
+ u32 bss_lose_timeout; /* number of TU's from synch fail */
+};
+
enum {
SG_ENABLE = 0,
SG_DISABLE,
@@ -1134,6 +1149,23 @@ struct wl1251_acx_mem_map {
u32 num_rx_mem_blocks;
} __attribute__ ((packed));
+
+struct wl1251_acx_wr_tbtt_and_dtim {
+
+ struct acx_header header;
+
+ /* Time in TUs between two consecutive beacons */
+ u16 tbtt;
+
+ /*
+ * DTIM period
+ * For BSS: Number of TBTTs in a DTIM period (range: 1-10)
+ * For IBSS: value shall be set to 1
+ */
+ u8 dtim;
+ u8 padding;
+} __attribute__ ((packed));
+
/*************************************************************************
Host Interrupt Register (WiLink -> Host)
@@ -1273,8 +1305,9 @@ int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time);
int wl1251_acx_group_address_tbl(struct wl1251 *wl);
int wl1251_acx_service_period_timeout(struct wl1251 *wl);
int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold);
-int wl1251_acx_beacon_filter_opt(struct wl1251 *wl);
+int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter);
int wl1251_acx_beacon_filter_table(struct wl1251 *wl);
+int wl1251_acx_conn_monit_params(struct wl1251 *wl);
int wl1251_acx_sg_enable(struct wl1251 *wl);
int wl1251_acx_sg_cfg(struct wl1251 *wl);
int wl1251_acx_cca_threshold(struct wl1251 *wl);
@@ -1288,5 +1321,6 @@ int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats);
int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime);
int wl1251_acx_rate_policies(struct wl1251 *wl);
int wl1251_acx_mem_cfg(struct wl1251 *wl);
+int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim);
#endif /* __WL1251_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c
index 452d748e42c..2e733e7bdfd 100644
--- a/drivers/net/wireless/wl12xx/wl1251_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1251_boot.c
@@ -296,8 +296,12 @@ int wl1251_boot_run_firmware(struct wl1251 *wl)
WL1251_ACX_INTR_INIT_COMPLETE;
wl1251_boot_target_enable_interrupts(wl);
- /* unmask all mbox events */
- wl->event_mask = 0xffffffff;
+ wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID |
+ SYNCHRONIZATION_TIMEOUT_EVENT_ID |
+ ROAMING_TRIGGER_LOW_RSSI_EVENT_ID |
+ ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID |
+ REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID |
+ BT_PTA_PREDICTION_EVENT_ID;
ret = wl1251_event_unmask(wl);
if (ret < 0) {
@@ -314,8 +318,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl)
static int wl1251_boot_upload_firmware(struct wl1251 *wl)
{
int addr, chunk_num, partition_limit;
- size_t fw_data_len;
- u8 *p;
+ size_t fw_data_len, len;
+ u8 *p, *buf;
/* whal_FwCtrl_LoadFwImageSm() */
@@ -334,6 +338,12 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl)
return -EIO;
}
+ buf = kmalloc(CHUNK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ wl1251_error("allocation for firmware upload chunk failed");
+ return -ENOMEM;
+ }
+
wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START,
WL1251_PART_DOWN_MEM_SIZE,
WL1251_PART_DOWN_REG_START,
@@ -364,7 +374,11 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl)
p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE;
wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
p, addr);
- wl1251_mem_write(wl, addr, p, CHUNK_SIZE);
+
+ /* need to copy the chunk for dma */
+ len = CHUNK_SIZE;
+ memcpy(buf, p, len);
+ wl1251_mem_write(wl, addr, buf, len);
chunk_num++;
}
@@ -372,9 +386,16 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl)
/* 10.4 upload the last chunk */
addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE;
p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE;
+
+ /* need to copy the chunk for dma */
+ len = fw_data_len % CHUNK_SIZE;
+ memcpy(buf, p, len);
+
wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x",
- fw_data_len % CHUNK_SIZE, p, addr);
- wl1251_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE);
+ len, p, addr);
+ wl1251_mem_write(wl, addr, buf, len);
+
+ kfree(buf);
return 0;
}
@@ -473,13 +494,19 @@ int wl1251_boot(struct wl1251 *wl)
goto out;
/* 2. start processing NVS file */
- ret = wl1251_boot_upload_nvs(wl);
- if (ret < 0)
- goto out;
-
- /* write firmware's last address (ie. it's length) to
- * ACX_EEPROMLESS_IND_REG */
- wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len);
+ if (wl->use_eeprom) {
+ wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR);
+ msleep(4000);
+ wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM);
+ } else {
+ ret = wl1251_boot_upload_nvs(wl);
+ if (ret < 0)
+ goto out;
+
+ /* write firmware's last address (ie. it's length) to
+ * ACX_EEPROMLESS_IND_REG */
+ wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len);
+ }
/* 6. read the EEPROM parameters */
tmp = wl1251_reg_read32(wl, SCR_PAD2);
diff --git a/drivers/net/wireless/wl12xx/wl1251_event.c b/drivers/net/wireless/wl12xx/wl1251_event.c
index 00076c4a8a2..020d764f9c1 100644
--- a/drivers/net/wireless/wl12xx/wl1251_event.c
+++ b/drivers/net/wireless/wl12xx/wl1251_event.c
@@ -79,6 +79,21 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
}
}
+ if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID && wl->psm) {
+ wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT");
+
+ /* indicate to the stack, that beacons have been lost */
+ ieee80211_beacon_loss(wl->vif);
+ }
+
+ if (vector & REGAINED_BSS_EVENT_ID) {
+ if (wl->psm_requested) {
+ ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
return 0;
}
diff --git a/drivers/net/wireless/wl12xx/wl1251_init.c b/drivers/net/wireless/wl12xx/wl1251_init.c
index b2ee4f468fc..5cb573383ee 100644
--- a/drivers/net/wireless/wl12xx/wl1251_init.c
+++ b/drivers/net/wireless/wl12xx/wl1251_init.c
@@ -147,7 +147,8 @@ int wl1251_hw_init_beacon_filter(struct wl1251 *wl)
{
int ret;
- ret = wl1251_acx_beacon_filter_opt(wl);
+ /* disable beacon filtering at this stage */
+ ret = wl1251_acx_beacon_filter_opt(wl, false);
if (ret < 0)
return ret;
@@ -364,6 +365,11 @@ int wl1251_hw_init(struct wl1251 *wl)
if (ret < 0)
goto out_free_data_path;
+ /* Initialize connection monitoring thresholds */
+ ret = wl1251_acx_conn_monit_params(wl);
+ if (ret < 0)
+ goto out_free_data_path;
+
/* Beacon filtering */
ret = wl1251_hw_init_beacon_filter(wl);
if (ret < 0)
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index 1103256ad98..ff4be7bf5d3 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -28,6 +28,7 @@
#include <linux/irq.h>
#include <linux/crc32.h>
#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
#include "wl1251.h"
#include "wl12xx_80211.h"
@@ -83,7 +84,7 @@ static int wl1251_fetch_firmware(struct wl1251 *wl)
}
wl->fw_len = fw->size;
- wl->fw = kmalloc(wl->fw_len, GFP_KERNEL);
+ wl->fw = vmalloc(wl->fw_len);
if (!wl->fw) {
wl1251_error("could not allocate memory for the firmware");
@@ -183,8 +184,11 @@ static int wl1251_chip_wakeup(struct wl1251 *wl)
wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)",
wl->chip_id);
break;
- case CHIP_ID_1251_PG10:
case CHIP_ID_1251_PG11:
+ wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG11)",
+ wl->chip_id);
+ break;
+ case CHIP_ID_1251_PG10:
default:
wl1251_error("unsupported chip id: 0x%x", wl->chip_id);
ret = -ENODEV;
@@ -208,9 +212,10 @@ out:
return ret;
}
+#define WL1251_IRQ_LOOP_COUNT 10
static void wl1251_irq_work(struct work_struct *work)
{
- u32 intr;
+ u32 intr, ctr = WL1251_IRQ_LOOP_COUNT;
struct wl1251 *wl =
container_of(work, struct wl1251, irq_work);
int ret;
@@ -231,78 +236,86 @@ static void wl1251_irq_work(struct work_struct *work)
intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr);
- if (wl->data_path) {
- wl->rx_counter =
- wl1251_mem_read32(wl, wl->data_path->rx_control_addr);
-
- /* We handle a frmware bug here */
- switch ((wl->rx_counter - wl->rx_handled) & 0xf) {
- case 0:
- wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync");
- intr &= ~WL1251_ACX_INTR_RX0_DATA;
- intr &= ~WL1251_ACX_INTR_RX1_DATA;
- break;
- case 1:
- wl1251_debug(DEBUG_IRQ, "RX: FW +1");
- intr |= WL1251_ACX_INTR_RX0_DATA;
- intr &= ~WL1251_ACX_INTR_RX1_DATA;
- break;
- case 2:
- wl1251_debug(DEBUG_IRQ, "RX: FW +2");
- intr |= WL1251_ACX_INTR_RX0_DATA;
- intr |= WL1251_ACX_INTR_RX1_DATA;
- break;
- default:
- wl1251_warning("RX: FW and host out of sync: %d",
- wl->rx_counter - wl->rx_handled);
- break;
- }
-
- wl->rx_handled = wl->rx_counter;
+ do {
+ if (wl->data_path) {
+ wl->rx_counter = wl1251_mem_read32(
+ wl, wl->data_path->rx_control_addr);
+
+ /* We handle a frmware bug here */
+ switch ((wl->rx_counter - wl->rx_handled) & 0xf) {
+ case 0:
+ wl1251_debug(DEBUG_IRQ,
+ "RX: FW and host in sync");
+ intr &= ~WL1251_ACX_INTR_RX0_DATA;
+ intr &= ~WL1251_ACX_INTR_RX1_DATA;
+ break;
+ case 1:
+ wl1251_debug(DEBUG_IRQ, "RX: FW +1");
+ intr |= WL1251_ACX_INTR_RX0_DATA;
+ intr &= ~WL1251_ACX_INTR_RX1_DATA;
+ break;
+ case 2:
+ wl1251_debug(DEBUG_IRQ, "RX: FW +2");
+ intr |= WL1251_ACX_INTR_RX0_DATA;
+ intr |= WL1251_ACX_INTR_RX1_DATA;
+ break;
+ default:
+ wl1251_warning(
+ "RX: FW and host out of sync: %d",
+ wl->rx_counter - wl->rx_handled);
+ break;
+ }
+ wl->rx_handled = wl->rx_counter;
- wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter);
- }
+ wl1251_debug(DEBUG_IRQ, "RX counter: %d",
+ wl->rx_counter);
+ }
- intr &= wl->intr_mask;
+ intr &= wl->intr_mask;
- if (intr == 0) {
- wl1251_debug(DEBUG_IRQ, "INTR is 0");
- wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK,
- ~(wl->intr_mask));
+ if (intr == 0) {
+ wl1251_debug(DEBUG_IRQ, "INTR is 0");
+ goto out_sleep;
+ }
- goto out_sleep;
- }
+ if (intr & WL1251_ACX_INTR_RX0_DATA) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA");
+ wl1251_rx(wl);
+ }
- if (intr & WL1251_ACX_INTR_RX0_DATA) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA");
- wl1251_rx(wl);
- }
+ if (intr & WL1251_ACX_INTR_RX1_DATA) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA");
+ wl1251_rx(wl);
+ }
- if (intr & WL1251_ACX_INTR_RX1_DATA) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA");
- wl1251_rx(wl);
- }
+ if (intr & WL1251_ACX_INTR_TX_RESULT) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
+ wl1251_tx_complete(wl);
+ }
- if (intr & WL1251_ACX_INTR_TX_RESULT) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
- wl1251_tx_complete(wl);
- }
+ if (intr & (WL1251_ACX_INTR_EVENT_A |
+ WL1251_ACX_INTR_EVENT_B)) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)",
+ intr);
+ if (intr & WL1251_ACX_INTR_EVENT_A)
+ wl1251_event_handle(wl, 0);
+ else
+ wl1251_event_handle(wl, 1);
+ }
- if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr);
- if (intr & WL1251_ACX_INTR_EVENT_A)
- wl1251_event_handle(wl, 0);
- else
- wl1251_event_handle(wl, 1);
- }
+ if (intr & WL1251_ACX_INTR_INIT_COMPLETE)
+ wl1251_debug(DEBUG_IRQ,
+ "WL1251_ACX_INTR_INIT_COMPLETE");
- if (intr & WL1251_ACX_INTR_INIT_COMPLETE)
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE");
+ if (--ctr == 0)
+ break;
- wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask));
+ intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
+ } while (intr);
out_sleep:
+ wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask));
wl1251_ps_elp_sleep(wl);
out:
@@ -506,6 +519,12 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw,
conf->type, conf->mac_addr);
mutex_lock(&wl->mutex);
+ if (wl->vif) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ wl->vif = conf->vif;
switch (conf->type) {
case NL80211_IFTYPE_STATION:
@@ -535,7 +554,12 @@ out:
static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
+ struct wl1251 *wl = hw->priv;
+
+ mutex_lock(&wl->mutex);
wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface");
+ wl->vif = NULL;
+ mutex_unlock(&wl->mutex);
}
static int wl1251_build_null_data(struct wl1251 *wl)
@@ -552,7 +576,8 @@ static int wl1251_build_null_data(struct wl1251 *wl)
memcpy(template.header.sa, wl->mac_addr, ETH_ALEN);
template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA |
- IEEE80211_STYPE_NULLFUNC);
+ IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_TODS);
return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template,
sizeof(template));
@@ -565,7 +590,10 @@ static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid)
memcpy(template.bssid, wl->bssid, ETH_ALEN);
memcpy(template.ta, wl->mac_addr, ETH_ALEN);
- template.aid = aid;
+
+ /* aid in PS-Poll has its two MSBs each set to 1 */
+ template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid);
+
template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template,
@@ -1087,8 +1115,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
wl->beacon_int = bss_conf->beacon_int;
wl->dtim_period = bss_conf->dtim_period;
- /* FIXME: call join */
-
+ ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int,
+ wl->dtim_period);
wl->aid = bss_conf->aid;
ret = wl1251_build_ps_poll(wl, wl->aid);
@@ -1308,7 +1336,9 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
wl->hw->channel_change_time = 10000;
wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_NOISE_DBM;
+ IEEE80211_HW_NOISE_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_BEACON_FILTER;
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
wl->hw->wiphy->max_scan_ssids = 1;
@@ -1351,6 +1381,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
skb_queue_head_init(&wl->tx_queue);
INIT_WORK(&wl->filter_work, wl1251_filter_work);
+ INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
wl->channel = WL1251_DEFAULT_CHANNEL;
wl->scanning = false;
wl->default_key = 0;
@@ -1368,6 +1399,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
+ wl->vif = NULL;
for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
wl->tx_frames[i] = NULL;
@@ -1409,7 +1441,7 @@ int wl1251_free_hw(struct wl1251 *wl)
kfree(wl->target_mem_map);
kfree(wl->data_path);
- kfree(wl->fw);
+ vfree(wl->fw);
wl->fw = NULL;
kfree(wl->nvs);
wl->nvs = NULL;
@@ -1426,4 +1458,5 @@ EXPORT_SYMBOL_GPL(wl1251_free_hw);
MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>");
-MODULE_ALIAS("spi:wl12xx");
+MODULE_ALIAS("spi:wl1251");
+MODULE_FIRMWARE(WL1251_FW_NAME);
diff --git a/drivers/net/wireless/wl12xx/wl1251_netlink.h b/drivers/net/wireless/wl12xx/wl1251_netlink.h
deleted file mode 100644
index ee36695e134..00000000000
--- a/drivers/net/wireless/wl12xx/wl1251_netlink.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * This file is part of wl1251
- *
- * Copyright (C) 2009 Nokia Corporation
- *
- * Contact: Kalle Valo <kalle.valo@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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __WL1251_NETLINK_H__
-#define __WL1251_NETLINK_H__
-
-int wl1251_nl_register(void);
-void wl1251_nl_unregister(void);
-
-#endif /* __WL1251_NETLINK_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c
index c53e28727ed..9931b197ff7 100644
--- a/drivers/net/wireless/wl12xx/wl1251_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1251_ps.c
@@ -28,17 +28,41 @@
#define WL1251_WAKEUP_TIMEOUT 2000
-/* Routines to toggle sleep mode while in ELP */
-void wl1251_ps_elp_sleep(struct wl1251 *wl)
+void wl1251_elp_work(struct work_struct *work)
{
+ struct delayed_work *dwork;
+ struct wl1251 *wl;
+
+ dwork = container_of(work, struct delayed_work, work);
+ wl = container_of(dwork, struct wl1251, elp_work);
+
+ wl1251_debug(DEBUG_PSM, "elp work");
+
+ mutex_lock(&wl->mutex);
+
if (wl->elp || !wl->psm)
- return;
+ goto out;
wl1251_debug(DEBUG_PSM, "chip to elp");
-
wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
-
wl->elp = true;
+
+out:
+ mutex_unlock(&wl->mutex);
+}
+
+#define ELP_ENTRY_DELAY 5
+
+/* Routines to toggle sleep mode while in ELP */
+void wl1251_ps_elp_sleep(struct wl1251 *wl)
+{
+ unsigned long delay;
+
+ if (wl->psm) {
+ cancel_delayed_work(&wl->elp_work);
+ delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
+ ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
+ }
}
int wl1251_ps_elp_wakeup(struct wl1251 *wl)
@@ -119,6 +143,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
case STATION_POWER_SAVE_MODE:
wl1251_debug(DEBUG_PSM, "entering psm");
+ /* enable beacon filtering */
+ ret = wl1251_acx_beacon_filter_opt(wl, true);
+ if (ret < 0)
+ return ret;
+
ret = wl1251_acx_wake_up_conditions(wl,
WAKE_UP_EVENT_DTIM_BITMAP,
wl->listen_int);
@@ -142,6 +171,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
if (ret < 0)
return ret;
+ /* disable beacon filtering */
+ ret = wl1251_acx_beacon_filter_opt(wl, false);
+ if (ret < 0)
+ return ret;
+
ret = wl1251_acx_wake_up_conditions(wl,
WAKE_UP_EVENT_DTIM_BITMAP,
wl->listen_int);
diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.h b/drivers/net/wireless/wl12xx/wl1251_ps.h
index db036fe12f2..c688ac57aee 100644
--- a/drivers/net/wireless/wl12xx/wl1251_ps.h
+++ b/drivers/net/wireless/wl12xx/wl1251_ps.h
@@ -31,6 +31,7 @@
int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode);
void wl1251_ps_elp_sleep(struct wl1251 *wl);
int wl1251_ps_elp_wakeup(struct wl1251 *wl);
+void wl1251_elp_work(struct work_struct *work);
#endif /* __WL1251_PS_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1251_reg.h b/drivers/net/wireless/wl12xx/wl1251_reg.h
index 06e1bd94a73..0ca3b432605 100644
--- a/drivers/net/wireless/wl12xx/wl1251_reg.h
+++ b/drivers/net/wireless/wl12xx/wl1251_reg.h
@@ -370,6 +370,7 @@ enum wl12xx_acx_int_reg {
EEPROM location specified in the EE_ADDR register.
The Wlan hardware hardware clears this bit automatically.
*===============================================*/
+#define EE_CTL (REGISTERS_BASE + 0x2000)
#define ACX_EE_CTL_REG EE_CTL
#define EE_WRITE 0x00000001ul
#define EE_READ 0x00000002ul
@@ -380,6 +381,7 @@ enum wl12xx_acx_int_reg {
This register specifies the address
within the EEPROM from/to which to read/write data.
===============================================*/
+#define EE_ADDR (REGISTERS_BASE + 0x2008)
#define ACX_EE_ADDR_REG EE_ADDR
/*===============================================
@@ -389,8 +391,12 @@ enum wl12xx_acx_int_reg {
data from the EEPROM or the write data
to be written to the EEPROM.
===============================================*/
+#define EE_DATA (REGISTERS_BASE + 0x2004)
#define ACX_EE_DATA_REG EE_DATA
+#define EEPROM_ACCESS_TO 10000 /* timeout counter */
+#define START_EEPROM_MGR 0x00000001
+
/*===============================================
EEPROM Base Address - 32bit RW
------------------------------------------
diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c
index 17c54b59ef8..f84cc89cbff 100644
--- a/drivers/net/wireless/wl12xx/wl1251_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1251_rx.c
@@ -72,10 +72,6 @@ static void wl1251_rx_status(struct wl1251 *wl,
}
status->signal = desc->rssi;
- status->qual = (desc->rssi - WL1251_RX_MIN_RSSI) * 100 /
- (WL1251_RX_MAX_RSSI - WL1251_RX_MIN_RSSI);
- status->qual = min(status->qual, 100);
- status->qual = max(status->qual, 0);
/*
* FIXME: guessing that snr needs to be divided by two, otherwise
@@ -153,7 +149,7 @@ static void wl1251_rx_body(struct wl1251 *wl,
beacon ? "beacon" : "");
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
- ieee80211_rx(wl->hw, skb);
+ ieee80211_rx_ni(wl->hw, skb);
}
static void wl1251_rx_ack(struct wl1251 *wl)
diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c
index 14eff2b3d4c..9cc8c323830 100644
--- a/drivers/net/wireless/wl12xx/wl1251_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1251_spi.c
@@ -270,6 +270,8 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi)
return -ENODEV;
}
+ wl->use_eeprom = pdata->use_eeprom;
+
ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl);
if (ret < 0) {
wl1251_error("request_irq() failed: %d", ret);
@@ -307,7 +309,7 @@ static int __devexit wl1251_spi_remove(struct spi_device *spi)
static struct spi_driver wl1251_spi_driver = {
.driver = {
- .name = "wl12xx",
+ .name = "wl1251",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 55818f94017..94359b1a861 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -32,6 +32,8 @@
#include <linux/bitops.h>
#include <net/mac80211.h>
+#include "wl1271_conf.h"
+
#define DRIVER_NAME "wl1271"
#define DRIVER_PREFIX DRIVER_NAME ": "
@@ -97,21 +99,42 @@ enum {
} while (0)
#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \
- CFG_BSSID_FILTER_EN)
+ CFG_BSSID_FILTER_EN | \
+ CFG_MC_FILTER_EN)
#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \
CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
CFG_RX_CTL_EN | CFG_RX_BCN_EN | \
CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN)
+#define WL1271_DEFAULT_BASIC_RATE_SET (CONF_TX_RATE_MASK_ALL)
+
#define WL1271_FW_NAME "wl1271-fw.bin"
#define WL1271_NVS_NAME "wl1271-nvs.bin"
-#define WL1271_BUSY_WORD_LEN 8
+/*
+ * Enable/disable 802.11a support for WL1273
+ */
+#undef WL1271_80211A_ENABLED
+
+/*
+ * FIXME: for the wl1271, a busy word count of 1 here will result in a more
+ * optimal SPI interface. There is some SPI bug however, causing RXS time outs
+ * with this mode occasionally on boot, so lets have three for now. A value of
+ * three should make sure, that the chipset will always be ready, though this
+ * will impact throughput and latencies slightly.
+ */
+#define WL1271_BUSY_WORD_CNT 3
+#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32))
#define WL1271_ELP_HW_STATE_ASLEEP 0
#define WL1271_ELP_HW_STATE_IRQ 1
+#define WL1271_DEFAULT_BEACON_INT 100
+#define WL1271_DEFAULT_DTIM_PERIOD 1
+
+#define ACX_TX_DESCRIPTORS 32
+
enum wl1271_state {
WL1271_STATE_OFF,
WL1271_STATE_ON,
@@ -134,6 +157,8 @@ struct wl1271_partition {
struct wl1271_partition_set {
struct wl1271_partition mem;
struct wl1271_partition reg;
+ struct wl1271_partition mem2;
+ struct wl1271_partition mem3;
};
struct wl1271;
@@ -258,15 +283,15 @@ struct wl1271_debugfs {
/* FW status registers */
struct wl1271_fw_status {
- u32 intr;
+ __le32 intr;
u8 fw_rx_counter;
u8 drv_rx_counter;
u8 reserved;
u8 tx_results_counter;
- u32 rx_pkt_descs[NUM_RX_PKT_DESC];
- u32 tx_released_blks[NUM_TX_QUEUES];
- u32 fw_localtime;
- u32 padding[2];
+ __le32 rx_pkt_descs[NUM_RX_PKT_DESC];
+ __le32 tx_released_blks[NUM_TX_QUEUES];
+ __le32 fw_localtime;
+ __le32 padding[2];
} __attribute__ ((packed));
struct wl1271_rx_mem_pool_addr {
@@ -274,6 +299,15 @@ struct wl1271_rx_mem_pool_addr {
u32 addr_extra;
};
+struct wl1271_scan {
+ u8 state;
+ u8 ssid[IW_ESSID_MAX_SIZE+1];
+ size_t ssid_len;
+ u8 active;
+ u8 high_prio;
+ u8 probe_requests;
+};
+
struct wl1271 {
struct ieee80211_hw *hw;
bool mac80211_registered;
@@ -288,10 +322,7 @@ struct wl1271 {
enum wl1271_state state;
struct mutex mutex;
- int physical_mem_addr;
- int physical_reg_addr;
- int virtual_mem_addr;
- int virtual_reg_addr;
+ struct wl1271_partition_set part;
struct wl1271_chip chip;
@@ -308,7 +339,6 @@ struct wl1271 {
u8 bss_type;
u8 ssid[IW_ESSID_MAX_SIZE + 1];
u8 ssid_len;
- u8 listen_int;
int channel;
struct wl1271_acx_mem_map *target_mem_map;
@@ -332,10 +362,14 @@ struct wl1271 {
bool tx_queue_stopped;
struct work_struct tx_work;
- struct work_struct filter_work;
/* Pending TX frames */
- struct sk_buff *tx_frames[16];
+ struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];
+
+ /* Security sequence number counters */
+ u8 tx_security_last_seq;
+ u16 tx_security_seq_16;
+ u32 tx_security_seq_32;
/* FW Rx counter */
u32 rx_counter;
@@ -354,10 +388,17 @@ struct wl1271 {
/* Are we currently scanning */
bool scanning;
+ struct wl1271_scan scan;
/* Our association ID */
u16 aid;
+ /* currently configured rate set */
+ u32 basic_rate_set;
+
+ /* The current band */
+ enum ieee80211_band band;
+
/* Default key (for WEP) */
u32 default_key;
@@ -368,6 +409,7 @@ struct wl1271 {
bool elp;
struct completion *elp_compl;
+ struct delayed_work elp_work;
/* we can be in psm, but not in elp, we have to differentiate */
bool psm;
@@ -375,6 +417,9 @@ struct wl1271 {
/* PSM mode requested */
bool psm_requested;
+ /* retry counter for PSM entries */
+ u8 psm_entry_retry;
+
/* in dBm */
int power_level;
@@ -383,11 +428,20 @@ struct wl1271 {
u32 buffer_32;
u32 buffer_cmd;
- u8 buffer_busyword[WL1271_BUSY_WORD_LEN];
- struct wl1271_rx_descriptor *rx_descriptor;
+ u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
struct wl1271_fw_status *fw_status;
struct wl1271_tx_hw_res_if *tx_res_if;
+
+ struct ieee80211_vif *vif;
+
+ /* Used for a workaround to send disconnect before rejoining */
+ bool joined;
+
+ /* Current chipset configuration */
+ struct conf_drv_settings conf;
+
+ struct list_head list;
};
int wl1271_plt_start(struct wl1271 *wl);
@@ -404,4 +458,13 @@ int wl1271_plt_stop(struct wl1271 *wl);
/* WL1271 needs a 200ms sleep after power on */
#define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */
+static inline bool wl1271_11a_enabled(void)
+{
+#ifdef WL1271_80211A_ENABLED
+ return true;
+#else
+ return false;
+#endif
+}
+
#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index f622a409261..5cc89bbdac7 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -34,8 +34,7 @@
#include "wl1271_spi.h"
#include "wl1271_ps.h"
-int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event,
- u8 listen_interval)
+int wl1271_acx_wake_up_conditions(struct wl1271 *wl)
{
struct acx_wake_up_condition *wake_up;
int ret;
@@ -48,8 +47,8 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event,
goto out;
}
- wake_up->wake_up_event = wake_up_event;
- wake_up->listen_interval = listen_interval;
+ wake_up->wake_up_event = wl->conf.conn.wake_up_event;
+ wake_up->listen_interval = wl->conf.conn.listen_interval;
ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS,
wake_up, sizeof(*wake_up));
@@ -137,7 +136,12 @@ int wl1271_acx_tx_power(struct wl1271 *wl, int power)
goto out;
}
- acx->current_tx_power = power * 10;
+ /*
+ * FIXME: This is a workaround needed while we don't the correct
+ * calibration, to avoid distortions
+ */
+ /* acx->current_tx_power = power * 10; */
+ acx->current_tx_power = 120;
ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx));
if (ret < 0) {
@@ -193,7 +197,7 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map,
return 0;
}
-int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time)
+int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl)
{
struct acx_rx_msdu_lifetime *acx;
int ret;
@@ -206,7 +210,7 @@ int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time)
goto out;
}
- acx->lifetime = life_time;
+ acx->lifetime = cpu_to_le32(wl->conf.rx.rx_msdu_life_time);
ret = wl1271_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME,
acx, sizeof(*acx));
if (ret < 0) {
@@ -232,8 +236,8 @@ int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter)
goto out;
}
- rx_config->config_options = config;
- rx_config->filter_options = filter;
+ rx_config->config_options = cpu_to_le32(config);
+ rx_config->filter_options = cpu_to_le32(filter);
ret = wl1271_cmd_configure(wl, ACX_RX_CFG,
rx_config, sizeof(*rx_config));
@@ -260,7 +264,7 @@ int wl1271_acx_pd_threshold(struct wl1271 *wl)
goto out;
}
- /* FIXME: threshold value not set */
+ pd->threshold = cpu_to_le32(wl->conf.rx.packet_detection_threshold);
ret = wl1271_cmd_configure(wl, ACX_PD_THRESHOLD, pd, sizeof(*pd));
if (ret < 0) {
@@ -300,7 +304,8 @@ out:
return ret;
}
-int wl1271_acx_group_address_tbl(struct wl1271 *wl)
+int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
+ void *mc_list, u32 mc_list_len)
{
struct acx_dot11_grp_addr_tbl *acx;
int ret;
@@ -314,9 +319,9 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl)
}
/* MAC filtering */
- acx->enabled = 0;
- acx->num_groups = 0;
- memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN);
+ acx->enabled = enable;
+ acx->num_groups = mc_list_len;
+ memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL,
acx, sizeof(*acx));
@@ -343,8 +348,8 @@ int wl1271_acx_service_period_timeout(struct wl1271 *wl)
wl1271_debug(DEBUG_ACX, "acx service period timeout");
- rx_timeout->ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF;
- rx_timeout->upsd_timeout = RX_TIMEOUT_UPSD_DEF;
+ rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout);
+ rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout);
ret = wl1271_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT,
rx_timeout, sizeof(*rx_timeout));
@@ -372,7 +377,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold)
goto out;
}
- rts->threshold = rts_threshold;
+ rts->threshold = cpu_to_le16(rts_threshold);
ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts));
if (ret < 0) {
@@ -385,20 +390,29 @@ out:
return ret;
}
-int wl1271_acx_beacon_filter_opt(struct wl1271 *wl)
+int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
{
- struct acx_beacon_filter_option *beacon_filter;
- int ret;
+ struct acx_beacon_filter_option *beacon_filter = NULL;
+ int ret = 0;
wl1271_debug(DEBUG_ACX, "acx beacon filter opt");
+ if (enable_filter &&
+ wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED)
+ goto out;
+
beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL);
if (!beacon_filter) {
ret = -ENOMEM;
goto out;
}
- beacon_filter->enable = 0;
+ beacon_filter->enable = enable_filter;
+
+ /*
+ * When set to zero, and the filter is enabled, beacons
+ * without the unicast TIM bit set are dropped.
+ */
beacon_filter->max_num_beacons = 0;
ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_OPT,
@@ -416,7 +430,9 @@ out:
int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
{
struct acx_beacon_filter_ie_table *ie_table;
+ int i, idx = 0;
int ret;
+ bool vendor_spec = false;
wl1271_debug(DEBUG_ACX, "acx beacon filter table");
@@ -426,8 +442,32 @@ int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
goto out;
}
+ /* configure default beacon pass-through rules */
ie_table->num_ie = 0;
- memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE);
+ for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) {
+ struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]);
+ ie_table->table[idx++] = r->ie;
+ ie_table->table[idx++] = r->rule;
+
+ if (r->ie == WLAN_EID_VENDOR_SPECIFIC) {
+ /* only one vendor specific ie allowed */
+ if (vendor_spec)
+ continue;
+
+ /* for vendor specific rules configure the
+ additional fields */
+ memcpy(&(ie_table->table[idx]), r->oui,
+ CONF_BCN_IE_OUI_LEN);
+ idx += CONF_BCN_IE_OUI_LEN;
+ ie_table->table[idx++] = r->type;
+ memcpy(&(ie_table->table[idx]), r->version,
+ CONF_BCN_IE_VER_LEN);
+ idx += CONF_BCN_IE_VER_LEN;
+ vendor_spec = true;
+ }
+
+ ie_table->num_ie++;
+ }
ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_TABLE,
ie_table, sizeof(*ie_table));
@@ -441,6 +481,36 @@ out:
return ret;
}
+int wl1271_acx_conn_monit_params(struct wl1271 *wl)
+{
+ struct acx_conn_monit_params *acx;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx connection monitor parameters");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->synch_fail_thold = cpu_to_le32(wl->conf.conn.synch_fail_thold);
+ acx->bss_lose_timeout = cpu_to_le32(wl->conf.conn.bss_lose_timeout);
+
+ ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("failed to set connection monitor "
+ "parameters: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+
int wl1271_acx_sg_enable(struct wl1271 *wl)
{
struct acx_bt_wlan_coex *pta;
@@ -470,6 +540,7 @@ out:
int wl1271_acx_sg_cfg(struct wl1271 *wl)
{
struct acx_bt_wlan_coex_param *param;
+ struct conf_sg_settings *c = &wl->conf.sg;
int ret;
wl1271_debug(DEBUG_ACX, "acx sg cfg");
@@ -481,34 +552,19 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)
}
/* BT-WLAN coext parameters */
- param->min_rate = RATE_INDEX_24MBPS;
- param->bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF;
- param->wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF;
- param->sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF;
- param->rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF;
- param->tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF;
- param->rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF;
- param->tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF;
- param->wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF;
- param->bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF;
- param->next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF;
- param->wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF;
- param->hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF;
- param->next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF;
- param->antenna_type = PTA_ANTENNA_TYPE_DEF;
- param->signal_type = PTA_SIGNALING_TYPE_DEF;
- param->afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF;
- param->quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF;
- param->max_cts = PTA_MAX_NUM_CTS_DEF;
- param->wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF;
- param->bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF;
- param->missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF;
- param->wlan_elp_hp = PTA_ELP_HP_DEF;
- param->bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF;
- param->ack_mode_dual_ant = PTA_ACK_MODE_DEF;
- param->pa_sd_enable = PTA_ALLOW_PA_SD_DEF;
- param->pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF;
- param->bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF;
+ param->per_threshold = cpu_to_le32(c->per_threshold);
+ param->max_scan_compensation_time =
+ cpu_to_le32(c->max_scan_compensation_time);
+ param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval);
+ param->load_ratio = c->load_ratio;
+ param->auto_ps_mode = c->auto_ps_mode;
+ param->probe_req_compensation = c->probe_req_compensation;
+ param->scan_window_compensation = c->scan_window_compensation;
+ param->antenna_config = c->antenna_config;
+ param->beacon_miss_threshold = c->beacon_miss_threshold;
+ param->rate_adaptation_threshold =
+ cpu_to_le32(c->rate_adaptation_threshold);
+ param->rate_adaptation_snr = c->rate_adaptation_snr;
ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
if (ret < 0) {
@@ -534,8 +590,8 @@ int wl1271_acx_cca_threshold(struct wl1271 *wl)
goto out;
}
- detection->rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D;
- detection->tx_energy_detection = 0;
+ detection->rx_cca_threshold = cpu_to_le16(wl->conf.rx.rx_cca_threshold);
+ detection->tx_energy_detection = wl->conf.tx.tx_energy_detection;
ret = wl1271_cmd_configure(wl, ACX_CCA_THRESHOLD,
detection, sizeof(*detection));
@@ -562,10 +618,10 @@ int wl1271_acx_bcn_dtim_options(struct wl1271 *wl)
goto out;
}
- bb->beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE;
- bb->broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE;
- bb->rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE;
- bb->ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF;
+ bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout);
+ bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout);
+ bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps;
+ bb->ps_poll_threshold = wl->conf.conn.ps_poll_threshold;
ret = wl1271_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb));
if (ret < 0) {
@@ -591,7 +647,7 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid)
goto out;
}
- acx_aid->aid = aid;
+ acx_aid->aid = cpu_to_le16(aid);
ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid));
if (ret < 0) {
@@ -618,9 +674,8 @@ int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask)
}
/* high event mask is unused */
- mask->high_event_mask = 0xffffffff;
-
- mask->event_mask = event_mask;
+ mask->high_event_mask = cpu_to_le32(0xffffffff);
+ mask->event_mask = cpu_to_le32(event_mask);
ret = wl1271_cmd_configure(wl, ACX_EVENT_MBOX_MASK,
mask, sizeof(*mask));
@@ -703,9 +758,10 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
return 0;
}
-int wl1271_acx_rate_policies(struct wl1271 *wl)
+int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates)
{
struct acx_rate_policy *acx;
+ struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf;
int ret = 0;
wl1271_debug(DEBUG_ACX, "acx rate policies");
@@ -718,11 +774,11 @@ int wl1271_acx_rate_policies(struct wl1271 *wl)
}
/* configure one default (one-size-fits-all) rate class */
- acx->rate_class_cnt = 1;
- acx->rate_class[0].enabled_rates = ACX_RATE_MASK_ALL;
- acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT;
- acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT;
- acx->rate_class[0].aflags = 0;
+ acx->rate_class_cnt = cpu_to_le32(1);
+ acx->rate_class[0].enabled_rates = cpu_to_le32(enabled_rates);
+ acx->rate_class[0].short_retry_limit = c->short_retry_limit;
+ acx->rate_class[0].long_retry_limit = c->long_retry_limit;
+ acx->rate_class[0].aflags = c->aflags;
ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
if (ret < 0) {
@@ -749,22 +805,14 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl)
goto out;
}
- /*
- * FIXME: Configure each AC with appropriate values (most suitable
- * values will probably be different for each AC.
- */
- for (i = 0; i < WL1271_ACX_AC_COUNT; i++) {
- acx->ac = i;
-
- /*
- * FIXME: The following default values originate from
- * the TI reference driver. What do they mean?
- */
- acx->cw_min = 15;
- acx->cw_max = 63;
- acx->aifsn = 3;
+ for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
+ struct conf_tx_ac_category *c = &(wl->conf.tx.ac_conf[i]);
+ acx->ac = c->ac;
+ acx->cw_min = c->cw_min;
+ acx->cw_max = cpu_to_le16(c->cw_max);
+ acx->aifsn = c->aifsn;
acx->reserved = 0;
- acx->tx_op_limit = 0;
+ acx->tx_op_limit = cpu_to_le16(c->tx_op_limit);
ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx));
if (ret < 0) {
@@ -793,12 +841,15 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl)
goto out;
}
- /* FIXME: configure each TID with a different AC reference */
- for (i = 0; i < WL1271_ACX_TID_COUNT; i++) {
- acx->queue_id = i;
- acx->tsid = WL1271_ACX_AC_BE;
- acx->ps_scheme = WL1271_ACX_PS_SCHEME_LEGACY;
- acx->ack_policy = WL1271_ACX_ACK_POLICY_LEGACY;
+ for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
+ struct conf_tx_tid *c = &(wl->conf.tx.tid_conf[i]);
+ acx->queue_id = c->queue_id;
+ acx->channel_type = c->channel_type;
+ acx->tsid = c->tsid;
+ acx->ps_scheme = c->ps_scheme;
+ acx->ack_policy = c->ack_policy;
+ acx->apsd_conf[0] = cpu_to_le32(c->apsd_conf[0]);
+ acx->apsd_conf[1] = cpu_to_le32(c->apsd_conf[1]);
ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx));
if (ret < 0) {
@@ -826,7 +877,7 @@ int wl1271_acx_frag_threshold(struct wl1271 *wl)
goto out;
}
- acx->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
+ acx->frag_threshold = cpu_to_le16(wl->conf.tx.frag_threshold);
ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("Setting of frag threshold failed: %d", ret);
@@ -852,8 +903,8 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl)
goto out;
}
- acx->tx_compl_timeout = WL1271_ACX_TX_COMPL_TIMEOUT;
- acx->tx_compl_threshold = WL1271_ACX_TX_COMPL_THRESHOLD;
+ acx->tx_compl_timeout = cpu_to_le16(wl->conf.tx.tx_compl_timeout);
+ acx->tx_compl_threshold = cpu_to_le16(wl->conf.tx.tx_compl_threshold);
ret = wl1271_cmd_configure(wl, ACX_TX_CONFIG_OPT, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("Setting of tx options failed: %d", ret);
@@ -879,11 +930,11 @@ int wl1271_acx_mem_cfg(struct wl1271 *wl)
}
/* memory config */
- mem_conf->num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS);
+ mem_conf->num_stations = DEFAULT_NUM_STATIONS;
mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS;
mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS;
mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES;
- mem_conf->total_tx_descriptors = ACX_TX_DESCRIPTORS;
+ mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS);
ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf,
sizeof(*mem_conf));
@@ -906,7 +957,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl)
return ret;
wl->target_mem_map = kzalloc(sizeof(struct wl1271_acx_mem_map),
- GFP_KERNEL);
+ GFP_KERNEL);
if (!wl->target_mem_map) {
wl1271_error("couldn't allocate target memory map");
return -ENOMEM;
@@ -923,7 +974,8 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl)
}
/* initialize TX block book keeping */
- wl->tx_blocks_available = wl->target_mem_map->num_tx_mem_blocks;
+ wl->tx_blocks_available =
+ le32_to_cpu(wl->target_mem_map->num_tx_mem_blocks);
wl1271_debug(DEBUG_TX, "available tx blocks: %d",
wl->tx_blocks_available);
@@ -943,10 +995,10 @@ int wl1271_acx_init_rx_interrupt(struct wl1271 *wl)
goto out;
}
- rx_conf->threshold = WL1271_RX_INTR_THRESHOLD_DEF;
- rx_conf->timeout = WL1271_RX_INTR_TIMEOUT_DEF;
- rx_conf->mblk_threshold = USHORT_MAX; /* Disabled */
- rx_conf->queue_type = RX_QUEUE_TYPE_RX_LOW_PRIORITY;
+ rx_conf->threshold = cpu_to_le16(wl->conf.rx.irq_pkt_threshold);
+ rx_conf->timeout = cpu_to_le16(wl->conf.rx.irq_timeout);
+ rx_conf->mblk_threshold = cpu_to_le16(wl->conf.rx.irq_blk_threshold);
+ rx_conf->queue_type = wl->conf.rx.queue_type;
ret = wl1271_cmd_configure(wl, ACX_RX_CONFIG_OPT, rx_conf,
sizeof(*rx_conf));
@@ -959,3 +1011,124 @@ out:
kfree(rx_conf);
return ret;
}
+
+int wl1271_acx_smart_reflex(struct wl1271 *wl)
+{
+ struct acx_smart_reflex_state *sr_state = NULL;
+ struct acx_smart_reflex_config_params *sr_param = NULL;
+ int i, ret;
+
+ wl1271_debug(DEBUG_ACX, "acx smart reflex");
+
+ sr_param = kzalloc(sizeof(*sr_param), GFP_KERNEL);
+ if (!sr_param) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < CONF_SR_ERR_TBL_COUNT; i++) {
+ struct conf_mart_reflex_err_table *e =
+ &(wl->conf.init.sr_err_tbl[i]);
+
+ sr_param->error_table[i].len = e->len;
+ sr_param->error_table[i].upper_limit = e->upper_limit;
+ memcpy(sr_param->error_table[i].values, e->values, e->len);
+ }
+
+ ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_PARAMS,
+ sr_param, sizeof(*sr_param));
+ if (ret < 0) {
+ wl1271_warning("failed to set smart reflex params: %d", ret);
+ goto out;
+ }
+
+ sr_state = kzalloc(sizeof(*sr_state), GFP_KERNEL);
+ if (!sr_state) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* enable smart reflex */
+ sr_state->enable = wl->conf.init.sr_enable;
+
+ ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_STATE,
+ sr_state, sizeof(*sr_state));
+ if (ret < 0) {
+ wl1271_warning("failed to set smart reflex params: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(sr_state);
+ kfree(sr_param);
+ return ret;
+
+}
+
+int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable)
+{
+ struct wl1271_acx_bet_enable *acx = NULL;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_ACX, "acx bet enable");
+
+ if (enable && wl->conf.conn.bet_enable == CONF_BET_MODE_DISABLE)
+ goto out;
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE;
+ acx->max_consecutive = wl->conf.conn.bet_max_consecutive;
+
+ ret = wl1271_cmd_configure(wl, ACX_BET_ENABLE, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx bet enable failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
+ u8 version)
+{
+ struct wl1271_acx_arp_filter *acx;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable);
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->version = version;
+ acx->enable = enable;
+
+ if (enable == true) {
+ if (version == ACX_IPV4_VERSION)
+ memcpy(acx->address, address, ACX_IPV4_ADDR_SIZE);
+ else if (version == ACX_IPV6_VERSION)
+ memcpy(acx->address, address, sizeof(acx->address));
+ else
+ wl1271_error("Invalid IP version");
+ }
+
+ ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("failed to set arp ip filter: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index 9068daaf0dd..2ce0a812854 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -61,8 +61,9 @@
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
-#define WL1271_INTR_MASK (WL1271_ACX_INTR_EVENT_A | \
- WL1271_ACX_INTR_EVENT_B | \
+#define WL1271_INTR_MASK (WL1271_ACX_INTR_EVENT_A | \
+ WL1271_ACX_INTR_EVENT_B | \
+ WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
/* Target's information element */
@@ -70,11 +71,11 @@ struct acx_header {
struct wl1271_cmd_header cmd;
/* acx (or information element) header */
- u16 id;
+ __le16 id;
/* payload length (not including headers */
- u16 len;
-};
+ __le16 len;
+} __attribute__ ((packed));
struct acx_error_counter {
struct acx_header header;
@@ -82,21 +83,21 @@ struct acx_error_counter {
/* The number of PLCP errors since the last time this */
/* information element was interrogated. This field is */
/* automatically cleared when it is interrogated.*/
- u32 PLCP_error;
+ __le32 PLCP_error;
/* The number of FCS errors since the last time this */
/* information element was interrogated. This field is */
/* automatically cleared when it is interrogated.*/
- u32 FCS_error;
+ __le32 FCS_error;
/* The number of MPDUs without PLCP header errors received*/
/* since the last time this information element was interrogated. */
/* This field is automatically cleared when it is interrogated.*/
- u32 valid_frame;
+ __le32 valid_frame;
/* the number of missed sequence numbers in the squentially */
/* values of frames seq numbers */
- u32 seq_num_miss;
+ __le32 seq_num_miss;
} __attribute__ ((packed));
struct acx_revision {
@@ -125,7 +126,7 @@ struct acx_revision {
* (1 = first spin, 2 = second spin, and so on).
* bits 24 - 31: Chip ID - The WiLink chip ID.
*/
- u32 hw_version;
+ __le32 hw_version;
} __attribute__ ((packed));
enum wl1271_psm_mode {
@@ -170,7 +171,6 @@ enum {
#define DP_RX_PACKET_RING_CHUNK_NUM 2
#define DP_TX_PACKET_RING_CHUNK_NUM 2
#define DP_TX_COMPLETE_TIME_OUT 20
-#define FW_TX_CMPLT_BLOCK_SIZE 16
#define TX_MSDU_LIFETIME_MIN 0
#define TX_MSDU_LIFETIME_MAX 3000
@@ -186,7 +186,7 @@ struct acx_rx_msdu_lifetime {
* The maximum amount of time, in TU, before the
* firmware discards the MSDU.
*/
- u32 lifetime;
+ __le32 lifetime;
} __attribute__ ((packed));
/*
@@ -273,14 +273,14 @@ struct acx_rx_msdu_lifetime {
struct acx_rx_config {
struct acx_header header;
- u32 config_options;
- u32 filter_options;
+ __le32 config_options;
+ __le32 filter_options;
} __attribute__ ((packed));
struct acx_packet_detection {
struct acx_header header;
- u32 threshold;
+ __le32 threshold;
} __attribute__ ((packed));
@@ -302,8 +302,8 @@ struct acx_slot {
} __attribute__ ((packed));
-#define ADDRESS_GROUP_MAX (8)
-#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX)
+#define ACX_MC_ADDRESS_GROUP_MAX (8)
+#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX)
struct acx_dot11_grp_addr_tbl {
struct acx_header header;
@@ -314,40 +314,17 @@ struct acx_dot11_grp_addr_tbl {
u8 mac_table[ADDRESS_GROUP_MAX_LEN];
} __attribute__ ((packed));
-
-#define RX_TIMEOUT_PS_POLL_MIN 0
-#define RX_TIMEOUT_PS_POLL_MAX (200000)
-#define RX_TIMEOUT_PS_POLL_DEF (15)
-#define RX_TIMEOUT_UPSD_MIN 0
-#define RX_TIMEOUT_UPSD_MAX (200000)
-#define RX_TIMEOUT_UPSD_DEF (15)
-
struct acx_rx_timeout {
struct acx_header header;
- /*
- * The longest time the STA will wait to receive
- * traffic from the AP after a PS-poll has been
- * transmitted.
- */
- u16 ps_poll_timeout;
-
- /*
- * The longest time the STA will wait to receive
- * traffic from the AP after a frame has been sent
- * from an UPSD enabled queue.
- */
- u16 upsd_timeout;
+ __le16 ps_poll_timeout;
+ __le16 upsd_timeout;
} __attribute__ ((packed));
-#define RTS_THRESHOLD_MIN 0
-#define RTS_THRESHOLD_MAX 4096
-#define RTS_THRESHOLD_DEF 2347
-
struct acx_rts_threshold {
struct acx_header header;
- u16 threshold;
+ __le16 threshold;
u8 pad[2];
} __attribute__ ((packed));
@@ -408,6 +385,13 @@ struct acx_beacon_filter_ie_table {
u8 pad[3];
} __attribute__ ((packed));
+struct acx_conn_monit_params {
+ struct acx_header header;
+
+ __le32 synch_fail_thold; /* number of beacons missed */
+ __le32 bss_lose_timeout; /* number of TU's from synch fail */
+} __attribute__ ((packed));
+
enum {
SG_ENABLE = 0,
SG_DISABLE,
@@ -431,6 +415,25 @@ struct acx_bt_wlan_coex {
u8 pad[3];
} __attribute__ ((packed));
+struct acx_smart_reflex_state {
+ struct acx_header header;
+
+ u8 enable;
+ u8 padding[3];
+} __attribute__ ((packed));
+
+struct smart_reflex_err_table {
+ u8 len;
+ s8 upper_limit;
+ s8 values[14];
+} __attribute__ ((packed));
+
+struct acx_smart_reflex_config_params {
+ struct acx_header header;
+
+ struct smart_reflex_err_table error_table[3];
+} __attribute__ ((packed));
+
#define PTA_ANTENNA_TYPE_DEF (0)
#define PTA_BT_HP_MAXTIME_DEF (2000)
#define PTA_WLAN_HP_MAX_TIME_DEF (5000)
@@ -463,150 +466,34 @@ struct acx_bt_wlan_coex {
struct acx_bt_wlan_coex_param {
struct acx_header header;
- /*
- * The minimum rate of a received WLAN packet in the STA,
- * during protective mode, of which a new BT-HP request
- * during this Rx will always be respected and gain the antenna.
- */
- u32 min_rate;
-
- /* Max time the BT HP will be respected. */
- u16 bt_hp_max_time;
-
- /* Max time the WLAN HP will be respected. */
- u16 wlan_hp_max_time;
-
- /*
- * The time between the last BT activity
- * and the moment when the sense mode returns
- * to SENSE_INACTIVE.
- */
- u16 sense_disable_timer;
-
- /* Time before the next BT HP instance */
- u16 rx_time_bt_hp;
- u16 tx_time_bt_hp;
-
- /* range: 10-20000 default: 1500 */
- u16 rx_time_bt_hp_fast;
- u16 tx_time_bt_hp_fast;
-
- /* range: 2000-65535 default: 8700 */
- u16 wlan_cycle_fast;
-
- /* range: 0 - 15000 (Msec) default: 1000 */
- u16 bt_anti_starvation_period;
-
- /* range 400-10000(Usec) default: 3000 */
- u16 next_bt_lp_packet;
-
- /* Deafult: worst case for BT DH5 traffic */
- u16 wake_up_beacon;
-
- /* range: 0-50000(Usec) default: 1050 */
- u16 hp_dm_max_guard_time;
-
- /*
- * This is to prevent both BT & WLAN antenna
- * starvation.
- * Range: 100-50000(Usec) default:2550
- */
- u16 next_wlan_packet;
-
- /* 0 -> shared antenna */
- u8 antenna_type;
-
- /*
- * 0 -> TI legacy
- * 1 -> Palau
- */
- u8 signal_type;
-
- /*
- * BT AFH status
- * 0 -> no AFH
- * 1 -> from dedicated GPIO
- * 2 -> AFH on (from host)
- */
- u8 afh_leverage_on;
-
- /*
- * The number of cycles during which no
- * TX will be sent after 1 cycle of RX
- * transaction in protective mode
- */
- u8 quiet_cycle_num;
-
- /*
- * The maximum number of CTSs that will
- * be sent for receiving RX packet in
- * protective mode
- */
- u8 max_cts;
-
- /*
- * The number of WLAN packets
- * transferred in common mode before
- * switching to BT.
- */
- u8 wlan_packets_num;
-
- /*
- * The number of BT packets
- * transferred in common mode before
- * switching to WLAN.
- */
- u8 bt_packets_num;
-
- /* range: 1-255 default: 5 */
- u8 missed_rx_avalanche;
-
- /* range: 0-1 default: 1 */
- u8 wlan_elp_hp;
-
- /* range: 0 - 15 default: 4 */
- u8 bt_anti_starvation_cycles;
-
- u8 ack_mode_dual_ant;
-
- /*
- * Allow PA_SD assertion/de-assertion
- * during enabled BT activity.
- */
- u8 pa_sd_enable;
-
- /*
- * Enable/Disable PTA in auto mode:
- * Support Both Active & P.S modes
- */
- u8 pta_auto_mode_enable;
-
- /* range: 0 - 20 default: 1 */
- u8 bt_hp_respected_num;
+ __le32 per_threshold;
+ __le32 max_scan_compensation_time;
+ __le16 nfs_sample_interval;
+ u8 load_ratio;
+ u8 auto_ps_mode;
+ u8 probe_req_compensation;
+ u8 scan_window_compensation;
+ u8 antenna_config;
+ u8 beacon_miss_threshold;
+ __le32 rate_adaptation_threshold;
+ s8 rate_adaptation_snr;
+ u8 padding[3];
} __attribute__ ((packed));
-#define CCA_THRSH_ENABLE_ENERGY_D 0x140A
-#define CCA_THRSH_DISABLE_ENERGY_D 0xFFEF
-
struct acx_energy_detection {
struct acx_header header;
/* The RX Clear Channel Assessment threshold in the PHY */
- u16 rx_cca_threshold;
+ __le16 rx_cca_threshold;
u8 tx_energy_detection;
u8 pad;
} __attribute__ ((packed));
-#define BCN_RX_TIMEOUT_DEF_VALUE 10000
-#define BROADCAST_RX_TIMEOUT_DEF_VALUE 20000
-#define RX_BROADCAST_IN_PS_DEF_VALUE 1
-#define CONSECUTIVE_PS_POLL_FAILURE_DEF 4
-
struct acx_beacon_broadcast {
struct acx_header header;
- u16 beacon_rx_timeout;
- u16 broadcast_timeout;
+ __le16 beacon_rx_timeout;
+ __le16 broadcast_timeout;
/* Enables receiving of broadcast packets in PS mode */
u8 rx_broadcast_in_ps;
@@ -619,8 +506,8 @@ struct acx_beacon_broadcast {
struct acx_event_mask {
struct acx_header header;
- u32 event_mask;
- u32 high_event_mask; /* Unused */
+ __le32 event_mask;
+ __le32 high_event_mask; /* Unused */
} __attribute__ ((packed));
#define CFG_RX_FCS BIT(2)
@@ -657,11 +544,15 @@ struct acx_event_mask {
#define SCAN_TRIGGERED BIT(2)
#define SCAN_PRIORITY_HIGH BIT(3)
+/* When set, disable HW encryption */
+#define DF_ENCRYPTION_DISABLE 0x01
+#define DF_SNIFF_MODE_ENABLE 0x80
+
struct acx_feature_config {
struct acx_header header;
- u32 options;
- u32 data_flow_options;
+ __le32 options;
+ __le32 data_flow_options;
} __attribute__ ((packed));
struct acx_current_tx_power {
@@ -671,14 +562,6 @@ struct acx_current_tx_power {
u8 padding[3];
} __attribute__ ((packed));
-enum acx_wake_up_event {
- WAKE_UP_EVENT_BEACON_BITMAP = 0x01, /* Wake on every Beacon*/
- WAKE_UP_EVENT_DTIM_BITMAP = 0x02, /* Wake on every DTIM*/
- WAKE_UP_EVENT_N_DTIM_BITMAP = 0x04, /* Wake on every Nth DTIM */
- WAKE_UP_EVENT_N_BEACONS_BITMAP = 0x08, /* Wake on every Nth Beacon */
- WAKE_UP_EVENT_BITS_MASK = 0x0F
-};
-
struct acx_wake_up_condition {
struct acx_header header;
@@ -693,7 +576,7 @@ struct acx_aid {
/*
* To be set when associated with an AP.
*/
- u16 aid;
+ __le16 aid;
u8 pad[2];
} __attribute__ ((packed));
@@ -725,152 +608,152 @@ struct acx_ctsprotect {
} __attribute__ ((packed));
struct acx_tx_statistics {
- u32 internal_desc_overflow;
+ __le32 internal_desc_overflow;
} __attribute__ ((packed));
struct acx_rx_statistics {
- u32 out_of_mem;
- u32 hdr_overflow;
- u32 hw_stuck;
- u32 dropped;
- u32 fcs_err;
- u32 xfr_hint_trig;
- u32 path_reset;
- u32 reset_counter;
+ __le32 out_of_mem;
+ __le32 hdr_overflow;
+ __le32 hw_stuck;
+ __le32 dropped;
+ __le32 fcs_err;
+ __le32 xfr_hint_trig;
+ __le32 path_reset;
+ __le32 reset_counter;
} __attribute__ ((packed));
struct acx_dma_statistics {
- u32 rx_requested;
- u32 rx_errors;
- u32 tx_requested;
- u32 tx_errors;
+ __le32 rx_requested;
+ __le32 rx_errors;
+ __le32 tx_requested;
+ __le32 tx_errors;
} __attribute__ ((packed));
struct acx_isr_statistics {
/* host command complete */
- u32 cmd_cmplt;
+ __le32 cmd_cmplt;
/* fiqisr() */
- u32 fiqs;
+ __le32 fiqs;
/* (INT_STS_ND & INT_TRIG_RX_HEADER) */
- u32 rx_headers;
+ __le32 rx_headers;
/* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
- u32 rx_completes;
+ __le32 rx_completes;
/* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
- u32 rx_mem_overflow;
+ __le32 rx_mem_overflow;
/* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
- u32 rx_rdys;
+ __le32 rx_rdys;
/* irqisr() */
- u32 irqs;
+ __le32 irqs;
/* (INT_STS_ND & INT_TRIG_TX_PROC) */
- u32 tx_procs;
+ __le32 tx_procs;
/* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
- u32 decrypt_done;
+ __le32 decrypt_done;
/* (INT_STS_ND & INT_TRIG_DMA0) */
- u32 dma0_done;
+ __le32 dma0_done;
/* (INT_STS_ND & INT_TRIG_DMA1) */
- u32 dma1_done;
+ __le32 dma1_done;
/* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
- u32 tx_exch_complete;
+ __le32 tx_exch_complete;
/* (INT_STS_ND & INT_TRIG_COMMAND) */
- u32 commands;
+ __le32 commands;
/* (INT_STS_ND & INT_TRIG_RX_PROC) */
- u32 rx_procs;
+ __le32 rx_procs;
/* (INT_STS_ND & INT_TRIG_PM_802) */
- u32 hw_pm_mode_changes;
+ __le32 hw_pm_mode_changes;
/* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
- u32 host_acknowledges;
+ __le32 host_acknowledges;
/* (INT_STS_ND & INT_TRIG_PM_PCI) */
- u32 pci_pm;
+ __le32 pci_pm;
/* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
- u32 wakeups;
+ __le32 wakeups;
/* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
- u32 low_rssi;
+ __le32 low_rssi;
} __attribute__ ((packed));
struct acx_wep_statistics {
/* WEP address keys configured */
- u32 addr_key_count;
+ __le32 addr_key_count;
/* default keys configured */
- u32 default_key_count;
+ __le32 default_key_count;
- u32 reserved;
+ __le32 reserved;
/* number of times that WEP key not found on lookup */
- u32 key_not_found;
+ __le32 key_not_found;
/* number of times that WEP key decryption failed */
- u32 decrypt_fail;
+ __le32 decrypt_fail;
/* WEP packets decrypted */
- u32 packets;
+ __le32 packets;
/* WEP decrypt interrupts */
- u32 interrupt;
+ __le32 interrupt;
} __attribute__ ((packed));
#define ACX_MISSED_BEACONS_SPREAD 10
struct acx_pwr_statistics {
/* the amount of enters into power save mode (both PD & ELP) */
- u32 ps_enter;
+ __le32 ps_enter;
/* the amount of enters into ELP mode */
- u32 elp_enter;
+ __le32 elp_enter;
/* the amount of missing beacon interrupts to the host */
- u32 missing_bcns;
+ __le32 missing_bcns;
/* the amount of wake on host-access times */
- u32 wake_on_host;
+ __le32 wake_on_host;
/* the amount of wake on timer-expire */
- u32 wake_on_timer_exp;
+ __le32 wake_on_timer_exp;
/* the number of packets that were transmitted with PS bit set */
- u32 tx_with_ps;
+ __le32 tx_with_ps;
/* the number of packets that were transmitted with PS bit clear */
- u32 tx_without_ps;
+ __le32 tx_without_ps;
/* the number of received beacons */
- u32 rcvd_beacons;
+ __le32 rcvd_beacons;
/* the number of entering into PowerOn (power save off) */
- u32 power_save_off;
+ __le32 power_save_off;
/* the number of entries into power save mode */
- u16 enable_ps;
+ __le16 enable_ps;
/*
* the number of exits from power save, not including failed PS
* transitions
*/
- u16 disable_ps;
+ __le16 disable_ps;
/*
* the number of times the TSF counter was adjusted because
* of drift
*/
- u32 fix_tsf_ps;
+ __le32 fix_tsf_ps;
/* Gives statistics about the spread continuous missed beacons.
* The 16 LSB are dedicated for the PS mode.
@@ -881,53 +764,53 @@ struct acx_pwr_statistics {
* ...
* cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
*/
- u32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
+ __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
/* the number of beacons in awake mode */
- u32 rcvd_awake_beacons;
+ __le32 rcvd_awake_beacons;
} __attribute__ ((packed));
struct acx_mic_statistics {
- u32 rx_pkts;
- u32 calc_failure;
+ __le32 rx_pkts;
+ __le32 calc_failure;
} __attribute__ ((packed));
struct acx_aes_statistics {
- u32 encrypt_fail;
- u32 decrypt_fail;
- u32 encrypt_packets;
- u32 decrypt_packets;
- u32 encrypt_interrupt;
- u32 decrypt_interrupt;
+ __le32 encrypt_fail;
+ __le32 decrypt_fail;
+ __le32 encrypt_packets;
+ __le32 decrypt_packets;
+ __le32 encrypt_interrupt;
+ __le32 decrypt_interrupt;
} __attribute__ ((packed));
struct acx_event_statistics {
- u32 heart_beat;
- u32 calibration;
- u32 rx_mismatch;
- u32 rx_mem_empty;
- u32 rx_pool;
- u32 oom_late;
- u32 phy_transmit_error;
- u32 tx_stuck;
+ __le32 heart_beat;
+ __le32 calibration;
+ __le32 rx_mismatch;
+ __le32 rx_mem_empty;
+ __le32 rx_pool;
+ __le32 oom_late;
+ __le32 phy_transmit_error;
+ __le32 tx_stuck;
} __attribute__ ((packed));
struct acx_ps_statistics {
- u32 pspoll_timeouts;
- u32 upsd_timeouts;
- u32 upsd_max_sptime;
- u32 upsd_max_apturn;
- u32 pspoll_max_apturn;
- u32 pspoll_utilization;
- u32 upsd_utilization;
+ __le32 pspoll_timeouts;
+ __le32 upsd_timeouts;
+ __le32 upsd_max_sptime;
+ __le32 upsd_max_apturn;
+ __le32 pspoll_max_apturn;
+ __le32 pspoll_utilization;
+ __le32 upsd_utilization;
} __attribute__ ((packed));
struct acx_rxpipe_statistics {
- u32 rx_prep_beacon_drop;
- u32 descr_host_int_trig_rx_data;
- u32 beacon_buffer_thres_host_int_trig_rx_data;
- u32 missed_beacon_host_int_trig_rx_data;
- u32 tx_xfr_host_int_trig_rx_data;
+ __le32 rx_prep_beacon_drop;
+ __le32 descr_host_int_trig_rx_data;
+ __le32 beacon_buffer_thres_host_int_trig_rx_data;
+ __le32 missed_beacon_host_int_trig_rx_data;
+ __le32 tx_xfr_host_int_trig_rx_data;
} __attribute__ ((packed));
struct acx_statistics {
@@ -946,13 +829,8 @@ struct acx_statistics {
struct acx_rxpipe_statistics rxpipe;
} __attribute__ ((packed));
-#define ACX_MAX_RATE_CLASSES 8
-#define ACX_RATE_MASK_UNSPECIFIED 0
-#define ACX_RATE_MASK_ALL 0x1eff
-#define ACX_RATE_RETRY_LIMIT 10
-
struct acx_rate_class {
- u32 enabled_rates;
+ __le32 enabled_rates;
u8 short_retry_limit;
u8 long_retry_limit;
u8 aflags;
@@ -962,47 +840,20 @@ struct acx_rate_class {
struct acx_rate_policy {
struct acx_header header;
- u32 rate_class_cnt;
- struct acx_rate_class rate_class[ACX_MAX_RATE_CLASSES];
+ __le32 rate_class_cnt;
+ struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES];
} __attribute__ ((packed));
-#define WL1271_ACX_AC_COUNT 4
-
struct acx_ac_cfg {
struct acx_header header;
u8 ac;
u8 cw_min;
- u16 cw_max;
+ __le16 cw_max;
u8 aifsn;
u8 reserved;
- u16 tx_op_limit;
+ __le16 tx_op_limit;
} __attribute__ ((packed));
-enum wl1271_acx_ac {
- WL1271_ACX_AC_BE = 0,
- WL1271_ACX_AC_BK = 1,
- WL1271_ACX_AC_VI = 2,
- WL1271_ACX_AC_VO = 3,
- WL1271_ACX_AC_CTS2SELF = 4,
- WL1271_ACX_AC_ANY_TID = 0x1F,
- WL1271_ACX_AC_INVALID = 0xFF,
-};
-
-enum wl1271_acx_ps_scheme {
- WL1271_ACX_PS_SCHEME_LEGACY = 0,
- WL1271_ACX_PS_SCHEME_UPSD_TRIGGER = 1,
- WL1271_ACX_PS_SCHEME_LEGACY_PSPOLL = 2,
- WL1271_ACX_PS_SCHEME_SAPSD = 3,
-};
-
-enum wl1271_acx_ack_policy {
- WL1271_ACX_ACK_POLICY_LEGACY = 0,
- WL1271_ACX_ACK_POLICY_NO_ACK = 1,
- WL1271_ACX_ACK_POLICY_BLOCK = 2,
-};
-
-#define WL1271_ACX_TID_COUNT 7
-
struct acx_tid_config {
struct acx_header header;
u8 queue_id;
@@ -1011,22 +862,19 @@ struct acx_tid_config {
u8 ps_scheme;
u8 ack_policy;
u8 padding[3];
- u32 apsd_conf[2];
+ __le32 apsd_conf[2];
} __attribute__ ((packed));
struct acx_frag_threshold {
struct acx_header header;
- u16 frag_threshold;
+ __le16 frag_threshold;
u8 padding[2];
} __attribute__ ((packed));
-#define WL1271_ACX_TX_COMPL_TIMEOUT 5
-#define WL1271_ACX_TX_COMPL_THRESHOLD 5
-
struct acx_tx_config_options {
struct acx_header header;
- u16 tx_compl_timeout; /* msec */
- u16 tx_compl_threshold; /* number of packets */
+ __le16 tx_compl_timeout; /* msec */
+ __le16 tx_compl_threshold; /* number of packets */
} __attribute__ ((packed));
#define ACX_RX_MEM_BLOCKS 64
@@ -1041,79 +889,87 @@ struct wl1271_acx_config_memory {
u8 tx_min_mem_block_num;
u8 num_stations;
u8 num_ssid_profiles;
- u32 total_tx_descriptors;
+ __le32 total_tx_descriptors;
} __attribute__ ((packed));
struct wl1271_acx_mem_map {
struct acx_header header;
- void *code_start;
- void *code_end;
+ __le32 code_start;
+ __le32 code_end;
- void *wep_defkey_start;
- void *wep_defkey_end;
+ __le32 wep_defkey_start;
+ __le32 wep_defkey_end;
- void *sta_table_start;
- void *sta_table_end;
+ __le32 sta_table_start;
+ __le32 sta_table_end;
- void *packet_template_start;
- void *packet_template_end;
+ __le32 packet_template_start;
+ __le32 packet_template_end;
/* Address of the TX result interface (control block) */
- u32 tx_result;
- u32 tx_result_queue_start;
+ __le32 tx_result;
+ __le32 tx_result_queue_start;
- void *queue_memory_start;
- void *queue_memory_end;
+ __le32 queue_memory_start;
+ __le32 queue_memory_end;
- u32 packet_memory_pool_start;
- u32 packet_memory_pool_end;
+ __le32 packet_memory_pool_start;
+ __le32 packet_memory_pool_end;
- void *debug_buffer1_start;
- void *debug_buffer1_end;
+ __le32 debug_buffer1_start;
+ __le32 debug_buffer1_end;
- void *debug_buffer2_start;
- void *debug_buffer2_end;
+ __le32 debug_buffer2_start;
+ __le32 debug_buffer2_end;
/* Number of blocks FW allocated for TX packets */
- u32 num_tx_mem_blocks;
+ __le32 num_tx_mem_blocks;
/* Number of blocks FW allocated for RX packets */
- u32 num_rx_mem_blocks;
+ __le32 num_rx_mem_blocks;
/* the following 4 fields are valid in SLAVE mode only */
u8 *tx_cbuf;
u8 *rx_cbuf;
- void *rx_ctrl;
- void *tx_ctrl;
+ __le32 rx_ctrl;
+ __le32 tx_ctrl;
} __attribute__ ((packed));
-enum wl1271_acx_rx_queue_type {
- RX_QUEUE_TYPE_RX_LOW_PRIORITY, /* All except the high priority */
- RX_QUEUE_TYPE_RX_HIGH_PRIORITY, /* Management and voice packets */
- RX_QUEUE_TYPE_NUM,
- RX_QUEUE_TYPE_MAX = USHORT_MAX
-};
-
-#define WL1271_RX_INTR_THRESHOLD_DEF 0 /* no pacing, send interrupt on
- * every event */
-#define WL1271_RX_INTR_THRESHOLD_MIN 0
-#define WL1271_RX_INTR_THRESHOLD_MAX 15
-
-#define WL1271_RX_INTR_TIMEOUT_DEF 5
-#define WL1271_RX_INTR_TIMEOUT_MIN 1
-#define WL1271_RX_INTR_TIMEOUT_MAX 100
-
struct wl1271_acx_rx_config_opt {
struct acx_header header;
- u16 mblk_threshold;
- u16 threshold;
- u16 timeout;
+ __le16 mblk_threshold;
+ __le16 threshold;
+ __le16 timeout;
u8 queue_type;
u8 reserved;
} __attribute__ ((packed));
+
+struct wl1271_acx_bet_enable {
+ struct acx_header header;
+
+ u8 enable;
+ u8 max_consecutive;
+ u8 padding[2];
+} __attribute__ ((packed));
+
+#define ACX_IPV4_VERSION 4
+#define ACX_IPV6_VERSION 6
+#define ACX_IPV4_ADDR_SIZE 4
+struct wl1271_acx_arp_filter {
+ struct acx_header header;
+ u8 version; /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */
+ u8 enable; /* 1 to enable ARP filtering, 0 to disable */
+ u8 padding[2];
+ u8 address[16]; /* The configured device IP address - all ARP
+ requests directed to this IP address will pass
+ through. For IPv4, the first four bytes are
+ used. */
+} __attribute__((packed));
+
+
enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
@@ -1170,6 +1026,9 @@ enum {
ACX_PEER_HT_CAP = 0x0057,
ACX_HT_BSS_OPERATION = 0x0058,
ACX_COEX_ACTIVITY = 0x0059,
+ ACX_SET_SMART_REFLEX_DEBUG = 0x005A,
+ ACX_SET_SMART_REFLEX_STATE = 0x005B,
+ ACX_SET_SMART_REFLEX_PARAMS = 0x005F,
DOT11_RX_MSDU_LIFE_TIME = 0x1004,
DOT11_CUR_TX_PWR = 0x100D,
DOT11_RX_DOT11_MODE = 0x1012,
@@ -1182,23 +1041,24 @@ enum {
};
-int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event,
- u8 listen_interval);
+int wl1271_acx_wake_up_conditions(struct wl1271 *wl);
int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth);
int wl1271_acx_fw_version(struct wl1271 *wl, char *buf, size_t len);
int wl1271_acx_tx_power(struct wl1271 *wl, int power);
int wl1271_acx_feature_cfg(struct wl1271 *wl);
int wl1271_acx_mem_map(struct wl1271 *wl,
struct acx_header *mem_map, size_t len);
-int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time);
+int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl);
int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter);
int wl1271_acx_pd_threshold(struct wl1271 *wl);
int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time);
-int wl1271_acx_group_address_tbl(struct wl1271 *wl);
+int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
+ void *mc_list, u32 mc_list_len);
int wl1271_acx_service_period_timeout(struct wl1271 *wl);
int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold);
-int wl1271_acx_beacon_filter_opt(struct wl1271 *wl);
+int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
+int wl1271_acx_conn_monit_params(struct wl1271 *wl);
int wl1271_acx_sg_enable(struct wl1271 *wl);
int wl1271_acx_sg_cfg(struct wl1271 *wl);
int wl1271_acx_cca_threshold(struct wl1271 *wl);
@@ -1207,9 +1067,9 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid);
int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask);
int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl,
- enum acx_ctsprotect_type ctsprotect);
+ enum acx_ctsprotect_type ctsprotect);
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
-int wl1271_acx_rate_policies(struct wl1271 *wl);
+int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates);
int wl1271_acx_ac_cfg(struct wl1271 *wl);
int wl1271_acx_tid_cfg(struct wl1271 *wl);
int wl1271_acx_frag_threshold(struct wl1271 *wl);
@@ -1217,5 +1077,9 @@ int wl1271_acx_tx_config_options(struct wl1271 *wl);
int wl1271_acx_mem_cfg(struct wl1271 *wl);
int wl1271_acx_init_mem_config(struct wl1271 *wl);
int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
+int wl1271_acx_smart_reflex(struct wl1271 *wl);
+int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable);
+int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
+ u8 version);
#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 8228ef474a7..b7c96454cca 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -39,6 +39,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
.start = REGISTERS_BASE,
.size = 0x00008800
},
+ .mem2 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
},
[PART_WORK] = {
@@ -48,7 +56,15 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
},
.reg = {
.start = REGISTERS_BASE,
- .size = 0x0000b000
+ .size = 0x0000a000
+ },
+ .mem2 = {
+ .start = 0x003004f8,
+ .size = 0x00000004
+ },
+ .mem3 = {
+ .start = 0x00040404,
+ .size = 0x00000000
},
},
@@ -60,6 +76,14 @@ static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
.reg = {
.start = DRPW_BASE,
.size = 0x00006000
+ },
+ .mem2 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00000000
}
}
};
@@ -69,19 +93,19 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
u32 cpu_ctrl;
/* 10.5.0 run the firmware (I) */
- cpu_ctrl = wl1271_reg_read32(wl, ACX_REG_ECPU_CONTROL);
+ cpu_ctrl = wl1271_spi_read32(wl, ACX_REG_ECPU_CONTROL);
/* 10.5.1 run the firmware (II) */
cpu_ctrl |= flag;
- wl1271_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
+ wl1271_spi_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl);
}
static void wl1271_boot_fw_version(struct wl1271 *wl)
{
struct wl1271_static_data static_data;
- wl1271_spi_mem_read(wl, wl->cmd_box_addr,
- &static_data, sizeof(static_data));
+ wl1271_spi_read(wl, wl->cmd_box_addr,
+ &static_data, sizeof(static_data), false);
strncpy(wl->chip.fw_ver, static_data.fw_version,
sizeof(wl->chip.fw_ver));
@@ -93,8 +117,9 @@ static void wl1271_boot_fw_version(struct wl1271 *wl)
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
size_t fw_data_len, u32 dest)
{
+ struct wl1271_partition_set partition;
int addr, chunk_num, partition_limit;
- u8 *p;
+ u8 *p, *chunk;
/* whal_FwCtrl_LoadFwImageSm() */
@@ -103,16 +128,20 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d",
fw_data_len, CHUNK_SIZE);
-
if ((fw_data_len % 4) != 0) {
wl1271_error("firmware length not multiple of four");
return -EIO;
}
- wl1271_set_partition(wl, dest,
- part_table[PART_DOWN].mem.size,
- part_table[PART_DOWN].reg.start,
- part_table[PART_DOWN].reg.size);
+ chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL);
+ if (!chunk) {
+ wl1271_error("allocation for firmware upload chunk failed");
+ return -ENOMEM;
+ }
+
+ memcpy(&partition, &part_table[PART_DOWN], sizeof(partition));
+ partition.mem.start = dest;
+ wl1271_set_partition(wl, &partition);
/* 10.1 set partition limit and chunk num */
chunk_num = 0;
@@ -125,21 +154,17 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
addr = dest + chunk_num * CHUNK_SIZE;
partition_limit = chunk_num * CHUNK_SIZE +
part_table[PART_DOWN].mem.size;
-
- /* FIXME: Over 80 chars! */
- wl1271_set_partition(wl,
- addr,
- part_table[PART_DOWN].mem.size,
- part_table[PART_DOWN].reg.start,
- part_table[PART_DOWN].reg.size);
+ partition.mem.start = addr;
+ wl1271_set_partition(wl, &partition);
}
/* 10.3 upload the chunk */
addr = dest + chunk_num * CHUNK_SIZE;
p = buf + chunk_num * CHUNK_SIZE;
+ memcpy(chunk, p, CHUNK_SIZE);
wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
p, addr);
- wl1271_spi_mem_write(wl, addr, p, CHUNK_SIZE);
+ wl1271_spi_write(wl, addr, chunk, CHUNK_SIZE, false);
chunk_num++;
}
@@ -147,28 +172,31 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
/* 10.4 upload the last chunk */
addr = dest + chunk_num * CHUNK_SIZE;
p = buf + chunk_num * CHUNK_SIZE;
+ memcpy(chunk, p, fw_data_len % CHUNK_SIZE);
wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x",
fw_data_len % CHUNK_SIZE, p, addr);
- wl1271_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE);
+ wl1271_spi_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false);
+ kfree(chunk);
return 0;
}
static int wl1271_boot_upload_firmware(struct wl1271 *wl)
{
u32 chunks, addr, len;
+ int ret = 0;
u8 *fw;
fw = wl->fw;
- chunks = be32_to_cpup((u32 *) fw);
+ chunks = be32_to_cpup((__be32 *) fw);
fw += sizeof(u32);
wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u", chunks);
while (chunks--) {
- addr = be32_to_cpup((u32 *) fw);
+ addr = be32_to_cpup((__be32 *) fw);
fw += sizeof(u32);
- len = be32_to_cpup((u32 *) fw);
+ len = be32_to_cpup((__be32 *) fw);
fw += sizeof(u32);
if (len > 300000) {
@@ -177,11 +205,13 @@ static int wl1271_boot_upload_firmware(struct wl1271 *wl)
}
wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u",
chunks, addr, len);
- wl1271_boot_upload_firmware_chunk(wl, fw, len, addr);
+ ret = wl1271_boot_upload_firmware_chunk(wl, fw, len, addr);
+ if (ret != 0)
+ break;
fw += len;
}
- return 0;
+ return ret;
}
static int wl1271_boot_upload_nvs(struct wl1271 *wl)
@@ -235,7 +265,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT,
"nvs burst write 0x%x: 0x%x",
dest_addr, val);
- wl1271_reg_write32(wl, dest_addr, val);
+ wl1271_spi_write32(wl, dest_addr, val);
nvs_ptr += 4;
dest_addr += 4;
@@ -253,20 +283,18 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
/* FIXME: The driver sets the partition here, but this is not needed,
since it sets to the same one as currently in use */
/* Now we must set the partition correctly */
- wl1271_set_partition(wl,
- part_table[PART_WORK].mem.start,
- part_table[PART_WORK].mem.size,
- part_table[PART_WORK].reg.start,
- part_table[PART_WORK].reg.size);
+ wl1271_set_partition(wl, &part_table[PART_WORK]);
/* Copy the NVS tables to a new block to ensure alignment */
nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL);
+ if (!nvs_aligned)
+ return -ENOMEM;
/* And finally we upload the NVS tables */
/* FIXME: In wl1271, we upload everything at once.
No endianness handling needed here?! The ref driver doesn't do
anything about it at this point */
- wl1271_spi_mem_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len);
+ wl1271_spi_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, false);
kfree(nvs_aligned);
return 0;
@@ -275,9 +303,9 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
static void wl1271_boot_enable_interrupts(struct wl1271 *wl)
{
enable_irq(wl->irq);
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK,
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
- wl1271_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL);
+ wl1271_spi_write32(wl, HI_CFG, HI_CFG_DEF_VAL);
}
static int wl1271_boot_soft_reset(struct wl1271 *wl)
@@ -286,12 +314,13 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl)
u32 boot_data;
/* perform soft reset */
- wl1271_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT);
+ wl1271_spi_write32(wl, ACX_REG_SLV_SOFT_RESET,
+ ACX_SLV_SOFT_RESET_BIT);
/* SOFT_RESET is self clearing */
timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME);
while (1) {
- boot_data = wl1271_reg_read32(wl, ACX_REG_SLV_SOFT_RESET);
+ boot_data = wl1271_spi_read32(wl, ACX_REG_SLV_SOFT_RESET);
wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data);
if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0)
break;
@@ -307,10 +336,10 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl)
}
/* disable Rx/Tx */
- wl1271_reg_write32(wl, ENABLE, 0x0);
+ wl1271_spi_write32(wl, ENABLE, 0x0);
/* disable auto calibration on start*/
- wl1271_reg_write32(wl, SPARE_A2, 0xffff);
+ wl1271_spi_write32(wl, SPARE_A2, 0xffff);
return 0;
}
@@ -322,7 +351,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT);
- chip_id = wl1271_reg_read32(wl, CHIP_ID_B);
+ chip_id = wl1271_spi_read32(wl, CHIP_ID_B);
wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id);
@@ -335,7 +364,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
loop = 0;
while (loop++ < INIT_LOOP) {
udelay(INIT_LOOP_DELAY);
- interrupt = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
+ interrupt = wl1271_spi_read32(wl,
+ ACX_REG_INTERRUPT_NO_CLEAR);
if (interrupt == 0xffffffff) {
wl1271_error("error reading hardware complete "
@@ -344,30 +374,26 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
}
/* check that ACX_INTR_INIT_COMPLETE is enabled */
else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) {
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK,
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK,
WL1271_ACX_INTR_INIT_COMPLETE);
break;
}
}
- if (loop >= INIT_LOOP) {
+ if (loop > INIT_LOOP) {
wl1271_error("timeout waiting for the hardware to "
"complete initialization");
return -EIO;
}
/* get hardware config command mail box */
- wl->cmd_box_addr = wl1271_reg_read32(wl, REG_COMMAND_MAILBOX_PTR);
+ wl->cmd_box_addr = wl1271_spi_read32(wl, REG_COMMAND_MAILBOX_PTR);
/* get hardware config event mail box */
- wl->event_box_addr = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
+ wl->event_box_addr = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR);
/* set the working partition to its "running" mode offset */
- wl1271_set_partition(wl,
- part_table[PART_WORK].mem.start,
- part_table[PART_WORK].mem.size,
- part_table[PART_WORK].reg.start,
- part_table[PART_WORK].reg.size);
+ wl1271_set_partition(wl, &part_table[PART_WORK]);
wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
wl->cmd_box_addr, wl->event_box_addr);
@@ -379,11 +405,10 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
* ready to receive event from the command mailbox
*/
- /* enable gpio interrupts */
- wl1271_boot_enable_interrupts(wl);
-
- /* unmask all mbox events */
- wl->event_mask = 0xffffffff;
+ /* unmask required mbox events */
+ wl->event_mask = BSS_LOSE_EVENT_ID |
+ SCAN_COMPLETE_EVENT_ID |
+ PS_REPORT_EVENT_ID;
ret = wl1271_event_unmask(wl);
if (ret < 0) {
@@ -399,34 +424,13 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
static int wl1271_boot_write_irq_polarity(struct wl1271 *wl)
{
- u32 polarity, status, i;
-
- wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY);
- wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_READ);
-
- /* Wait until the command is complete (ie. bit 18 is set) */
- for (i = 0; i < OCP_CMD_LOOP; i++) {
- polarity = wl1271_reg_read32(wl, OCP_DATA_READ);
- if (polarity & OCP_READY_MASK)
- break;
- }
- if (i == OCP_CMD_LOOP) {
- wl1271_error("OCP command timeout!");
- return -EIO;
- }
+ u32 polarity;
- status = polarity & OCP_STATUS_MASK;
- if (status != OCP_STATUS_OK) {
- wl1271_error("OCP command failed (%d)", status);
- return -EIO;
- }
+ polarity = wl1271_top_reg_read(wl, OCP_REG_POLARITY);
/* We use HIGH polarity, so unset the LOW bit */
polarity &= ~POLARITY_LOW;
-
- wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY);
- wl1271_reg_write32(wl, OCP_DATA_WRITE, polarity);
- wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_WRITE);
+ wl1271_top_reg_write(wl, OCP_REG_POLARITY, polarity);
return 0;
}
@@ -436,16 +440,32 @@ int wl1271_boot(struct wl1271 *wl)
int ret = 0;
u32 tmp, clk, pause;
- if (REF_CLOCK == 0 || REF_CLOCK == 2)
- /* ref clk: 19.2/38.4 */
+ if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4)
+ /* ref clk: 19.2/38.4/38.4-XTAL */
clk = 0x3;
else if (REF_CLOCK == 1 || REF_CLOCK == 3)
/* ref clk: 26/52 */
clk = 0x5;
- wl1271_reg_write32(wl, PLL_PARAMETERS, clk);
+ if (REF_CLOCK != 0) {
+ u16 val;
+ /* Set clock type */
+ val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
+ val &= FREF_CLK_TYPE_BITS;
+ val |= CLK_REQ_PRCM;
+ wl1271_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
+ } else {
+ u16 val;
+ /* Set clock polarity */
+ val = wl1271_top_reg_read(wl, OCP_REG_CLK_POLARITY);
+ val &= FREF_CLK_POLARITY_BITS;
+ val |= CLK_REQ_OUTN_SEL;
+ wl1271_top_reg_write(wl, OCP_REG_CLK_POLARITY, val);
+ }
+
+ wl1271_spi_write32(wl, PLL_PARAMETERS, clk);
- pause = wl1271_reg_read32(wl, PLL_PARAMETERS);
+ pause = wl1271_spi_read32(wl, PLL_PARAMETERS);
wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause);
@@ -454,39 +474,31 @@ int wl1271_boot(struct wl1271 *wl)
* 0x3ff (magic number ). How does
* this work?! */
pause |= WU_COUNTER_PAUSE_VAL;
- wl1271_reg_write32(wl, WU_COUNTER_PAUSE, pause);
+ wl1271_spi_write32(wl, WU_COUNTER_PAUSE, pause);
/* Continue the ELP wake up sequence */
- wl1271_reg_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
+ wl1271_spi_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
udelay(500);
- wl1271_set_partition(wl,
- part_table[PART_DRPW].mem.start,
- part_table[PART_DRPW].mem.size,
- part_table[PART_DRPW].reg.start,
- part_table[PART_DRPW].reg.size);
+ wl1271_set_partition(wl, &part_table[PART_DRPW]);
/* Read-modify-write DRPW_SCRATCH_START register (see next state)
to be used by DRPw FW. The RTRIM value will be added by the FW
before taking DRPw out of reset */
wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START);
- clk = wl1271_reg_read32(wl, DRPW_SCRATCH_START);
+ clk = wl1271_spi_read32(wl, DRPW_SCRATCH_START);
wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
/* 2 */
clk |= (REF_CLOCK << 1) << 4;
- wl1271_reg_write32(wl, DRPW_SCRATCH_START, clk);
+ wl1271_spi_write32(wl, DRPW_SCRATCH_START, clk);
- wl1271_set_partition(wl,
- part_table[PART_WORK].mem.start,
- part_table[PART_WORK].mem.size,
- part_table[PART_WORK].reg.start,
- part_table[PART_WORK].reg.size);
+ wl1271_set_partition(wl, &part_table[PART_WORK]);
/* Disable interrupts */
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
ret = wl1271_boot_soft_reset(wl);
if (ret < 0)
@@ -501,21 +513,22 @@ int wl1271_boot(struct wl1271 *wl)
* ACX_EEPROMLESS_IND_REG */
wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG");
- wl1271_reg_write32(wl, ACX_EEPROMLESS_IND_REG, ACX_EEPROMLESS_IND_REG);
+ wl1271_spi_write32(wl, ACX_EEPROMLESS_IND_REG,
+ ACX_EEPROMLESS_IND_REG);
- tmp = wl1271_reg_read32(wl, CHIP_ID_B);
+ tmp = wl1271_spi_read32(wl, CHIP_ID_B);
wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
/* 6. read the EEPROM parameters */
- tmp = wl1271_reg_read32(wl, SCR_PAD2);
+ tmp = wl1271_spi_read32(wl, SCR_PAD2);
ret = wl1271_boot_write_irq_polarity(wl);
if (ret < 0)
goto out;
/* FIXME: Need to check whether this is really what we want */
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK,
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
WL1271_ACX_ALL_EVENTS_VECTOR);
/* WL1271: The reference driver skips steps 7 to 10 (jumps directly
@@ -530,6 +543,9 @@ int wl1271_boot(struct wl1271 *wl)
if (ret < 0)
goto out;
+ /* Enable firmware interrupts now */
+ wl1271_boot_enable_interrupts(wl);
+
/* set the wl1271 default filters */
wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h
index b0d8fb46a43..412443ee655 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.h
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.h
@@ -50,23 +50,17 @@ struct wl1271_static_data {
#define WU_COUNTER_PAUSE_VAL 0x3FF
#define WELP_ARM_COMMAND_VAL 0x4
-#define OCP_CMD_LOOP 32
-
-#define OCP_CMD_WRITE 0x1
-#define OCP_CMD_READ 0x2
-
-#define OCP_READY_MASK BIT(18)
-#define OCP_STATUS_MASK (BIT(16) | BIT(17))
-
-#define OCP_STATUS_NO_RESP 0x00000
-#define OCP_STATUS_OK 0x10000
-#define OCP_STATUS_REQ_FAILED 0x20000
-#define OCP_STATUS_RESP_ERROR 0x30000
-
-#define OCP_REG_POLARITY 0x30032
+#define OCP_REG_POLARITY 0x0064
+#define OCP_REG_CLK_TYPE 0x0448
+#define OCP_REG_CLK_POLARITY 0x0cb2
#define CMD_MBOX_ADDRESS 0x407B4
#define POLARITY_LOW BIT(1)
+#define FREF_CLK_TYPE_BITS 0xfffffe7f
+#define CLK_REQ_PRCM 0x100
+#define FREF_CLK_POLARITY_BITS 0xfffff8ff
+#define CLK_REQ_OUTN_SEL 0x700
+
#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 2a4351ff54d..886a9bc39cc 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -42,26 +42,28 @@
* @buf: buffer containing the command, must work with dma
* @len: length of the buffer
*/
-int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
+ size_t res_len)
{
struct wl1271_cmd_header *cmd;
unsigned long timeout;
u32 intr;
int ret = 0;
+ u16 status;
cmd = buf;
- cmd->id = id;
+ cmd->id = cpu_to_le16(id);
cmd->status = 0;
WARN_ON(len % 4 != 0);
- wl1271_spi_mem_write(wl, wl->cmd_box_addr, buf, len);
+ wl1271_spi_write(wl, wl->cmd_box_addr, buf, len, false);
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD);
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD);
timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT);
- intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
+ intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) {
if (time_after(jiffies, timeout)) {
wl1271_error("command complete timeout");
@@ -71,17 +73,28 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len)
msleep(1);
- intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
+ intr = wl1271_spi_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
}
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK,
+ /* read back the status code of the command */
+ if (res_len == 0)
+ res_len = sizeof(struct wl1271_cmd_header);
+ wl1271_spi_read(wl, wl->cmd_box_addr, cmd, res_len, false);
+
+ status = le16_to_cpu(cmd->status);
+ if (status != CMD_STATUS_SUCCESS) {
+ wl1271_error("command execute failure %d", status);
+ ret = -EIO;
+ }
+
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_ACK,
WL1271_ACX_INTR_CMD_COMPLETE);
out:
return ret;
}
-int wl1271_cmd_cal_channel_tune(struct wl1271 *wl)
+static int wl1271_cmd_cal_channel_tune(struct wl1271 *wl)
{
struct wl1271_cmd_cal_channel_tune *cmd;
int ret = 0;
@@ -104,7 +117,7 @@ int wl1271_cmd_cal_channel_tune(struct wl1271 *wl)
return ret;
}
-int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl)
+static int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl)
{
struct wl1271_cmd_cal_update_ref_point *cmd;
int ret = 0;
@@ -129,7 +142,7 @@ int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl)
return ret;
}
-int wl1271_cmd_cal_p2g(struct wl1271 *wl)
+static int wl1271_cmd_cal_p2g(struct wl1271 *wl)
{
struct wl1271_cmd_cal_p2g *cmd;
int ret = 0;
@@ -150,7 +163,7 @@ int wl1271_cmd_cal_p2g(struct wl1271 *wl)
return ret;
}
-int wl1271_cmd_cal(struct wl1271 *wl)
+static int wl1271_cmd_cal(struct wl1271 *wl)
{
/*
* FIXME: we must make sure that we're not sleeping when calibration
@@ -175,11 +188,116 @@ int wl1271_cmd_cal(struct wl1271 *wl)
return ret;
}
-int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
- u16 beacon_interval, u8 wait)
+int wl1271_cmd_general_parms(struct wl1271 *wl)
+{
+ struct wl1271_general_parms_cmd *gen_parms;
+ struct conf_general_parms *g = &wl->conf.init.genparam;
+ int ret;
+
+ gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
+ if (!gen_parms)
+ return -ENOMEM;
+
+ gen_parms->test.id = TEST_CMD_INI_FILE_GENERAL_PARAM;
+
+ gen_parms->ref_clk = g->ref_clk;
+ gen_parms->settling_time = g->settling_time;
+ gen_parms->clk_valid_on_wakeup = g->clk_valid_on_wakeup;
+ gen_parms->dc2dcmode = g->dc2dcmode;
+ gen_parms->single_dual_band = g->single_dual_band;
+ gen_parms->tx_bip_fem_autodetect = g->tx_bip_fem_autodetect;
+ gen_parms->tx_bip_fem_manufacturer = g->tx_bip_fem_manufacturer;
+ gen_parms->settings = g->settings;
+
+ ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0);
+ if (ret < 0)
+ wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
+
+ kfree(gen_parms);
+ return ret;
+}
+
+int wl1271_cmd_radio_parms(struct wl1271 *wl)
+{
+ struct wl1271_radio_parms_cmd *radio_parms;
+ struct conf_radio_parms *r = &wl->conf.init.radioparam;
+ int i, ret;
+
+ radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL);
+ if (!radio_parms)
+ return -ENOMEM;
+
+ radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM;
+
+ /* Static radio parameters */
+ radio_parms->rx_trace_loss = r->rx_trace_loss;
+ radio_parms->tx_trace_loss = r->tx_trace_loss;
+ memcpy(radio_parms->rx_rssi_and_proc_compens,
+ r->rx_rssi_and_proc_compens,
+ CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE);
+
+ memcpy(radio_parms->rx_trace_loss_5, r->rx_trace_loss_5,
+ CONF_NUMBER_OF_SUB_BANDS_5);
+ memcpy(radio_parms->tx_trace_loss_5, r->tx_trace_loss_5,
+ CONF_NUMBER_OF_SUB_BANDS_5);
+ memcpy(radio_parms->rx_rssi_and_proc_compens_5,
+ r->rx_rssi_and_proc_compens_5,
+ CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE);
+
+ /* Dynamic radio parameters */
+ radio_parms->tx_ref_pd_voltage = cpu_to_le16(r->tx_ref_pd_voltage);
+ radio_parms->tx_ref_power = r->tx_ref_power;
+ radio_parms->tx_offset_db = r->tx_offset_db;
+
+ memcpy(radio_parms->tx_rate_limits_normal, r->tx_rate_limits_normal,
+ CONF_NUMBER_OF_RATE_GROUPS);
+ memcpy(radio_parms->tx_rate_limits_degraded, r->tx_rate_limits_degraded,
+ CONF_NUMBER_OF_RATE_GROUPS);
+
+ memcpy(radio_parms->tx_channel_limits_11b, r->tx_channel_limits_11b,
+ CONF_NUMBER_OF_CHANNELS_2_4);
+ memcpy(radio_parms->tx_channel_limits_ofdm, r->tx_channel_limits_ofdm,
+ CONF_NUMBER_OF_CHANNELS_2_4);
+ memcpy(radio_parms->tx_pdv_rate_offsets, r->tx_pdv_rate_offsets,
+ CONF_NUMBER_OF_RATE_GROUPS);
+ memcpy(radio_parms->tx_ibias, r->tx_ibias, CONF_NUMBER_OF_RATE_GROUPS);
+
+ radio_parms->rx_fem_insertion_loss = r->rx_fem_insertion_loss;
+
+ for (i = 0; i < CONF_NUMBER_OF_SUB_BANDS_5; i++)
+ radio_parms->tx_ref_pd_voltage_5[i] =
+ cpu_to_le16(r->tx_ref_pd_voltage_5[i]);
+ memcpy(radio_parms->tx_ref_power_5, r->tx_ref_power_5,
+ CONF_NUMBER_OF_SUB_BANDS_5);
+ memcpy(radio_parms->tx_offset_db_5, r->tx_offset_db_5,
+ CONF_NUMBER_OF_SUB_BANDS_5);
+ memcpy(radio_parms->tx_rate_limits_normal_5,
+ r->tx_rate_limits_normal_5, CONF_NUMBER_OF_RATE_GROUPS);
+ memcpy(radio_parms->tx_rate_limits_degraded_5,
+ r->tx_rate_limits_degraded_5, CONF_NUMBER_OF_RATE_GROUPS);
+ memcpy(radio_parms->tx_channel_limits_ofdm_5,
+ r->tx_channel_limits_ofdm_5, CONF_NUMBER_OF_CHANNELS_5);
+ memcpy(radio_parms->tx_pdv_rate_offsets_5, r->tx_pdv_rate_offsets_5,
+ CONF_NUMBER_OF_RATE_GROUPS);
+ memcpy(radio_parms->tx_ibias_5, r->tx_ibias_5,
+ CONF_NUMBER_OF_RATE_GROUPS);
+ memcpy(radio_parms->rx_fem_insertion_loss_5,
+ r->rx_fem_insertion_loss_5, CONF_NUMBER_OF_SUB_BANDS_5);
+
+ wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
+ radio_parms, sizeof(*radio_parms));
+
+ ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
+ if (ret < 0)
+ wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");
+
+ kfree(radio_parms);
+ return ret;
+}
+
+int wl1271_cmd_join(struct wl1271 *wl)
{
static bool do_cal = true;
- unsigned long timeout;
struct wl1271_cmd_join *join;
int ret, i;
u8 *bssid;
@@ -193,6 +311,18 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
do_cal = false;
}
+ /* FIXME: This is a workaround, because with the current stack, we
+ * cannot know when we have disassociated. So, if we have already
+ * joined, we disconnect before joining again. */
+ if (wl->joined) {
+ ret = wl1271_cmd_disconnect(wl);
+ if (ret < 0) {
+ wl1271_error("failed to disconnect before rejoining");
+ goto out;
+ }
+
+ wl->joined = false;
+ }
join = kzalloc(sizeof(*join), GFP_KERNEL);
if (!join) {
@@ -207,15 +337,34 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
for (i = 0; i < ETH_ALEN; i++)
bssid[i] = wl->bssid[ETH_ALEN - i - 1];
- join->rx_config_options = wl->rx_config;
- join->rx_filter_options = wl->rx_filter;
+ join->rx_config_options = cpu_to_le32(wl->rx_config);
+ join->rx_filter_options = cpu_to_le32(wl->rx_filter);
+ join->bss_type = wl->bss_type;
- join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS |
- RATE_MASK_5_5MBPS | RATE_MASK_11MBPS;
+ /*
+ * FIXME: disable temporarily all filters because after commit
+ * 9cef8737 "mac80211: fix managed mode BSSID handling" broke
+ * association. The filter logic needs to be implemented properly
+ * and once that is done, this hack can be removed.
+ */
+ join->rx_config_options = cpu_to_le32(0);
+ join->rx_filter_options = cpu_to_le32(WL1271_DEFAULT_RX_FILTER);
+
+ if (wl->band == IEEE80211_BAND_2GHZ)
+ join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS |
+ CONF_HW_BIT_RATE_2MBPS |
+ CONF_HW_BIT_RATE_5_5MBPS |
+ CONF_HW_BIT_RATE_11MBPS);
+ else {
+ join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ;
+ join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_6MBPS |
+ CONF_HW_BIT_RATE_12MBPS |
+ CONF_HW_BIT_RATE_24MBPS);
+ }
+
+ join->beacon_interval = cpu_to_le16(WL1271_DEFAULT_BEACON_INT);
+ join->dtim_interval = WL1271_DEFAULT_DTIM_PERIOD;
- join->beacon_interval = beacon_interval;
- join->dtim_interval = dtim_interval;
- join->bss_type = bss_type;
join->channel = wl->channel;
join->ssid_len = wl->ssid_len;
memcpy(join->ssid, wl->ssid, wl->ssid_len);
@@ -228,21 +377,24 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
join->ctrl |= wl->session_counter << WL1271_JOIN_CMD_TX_SESSION_OFFSET;
+ /* reset TX security counters */
+ wl->tx_security_last_seq = 0;
+ wl->tx_security_seq_16 = 0;
+ wl->tx_security_seq_32 = 0;
- ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join));
+ ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join), 0);
if (ret < 0) {
wl1271_error("failed to initiate cmd join");
goto out_free;
}
- timeout = msecs_to_jiffies(JOIN_TIMEOUT);
+ wl->joined = true;
/*
* ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to
* simplify locking we just sleep instead, for now
*/
- if (wait)
- msleep(10);
+ msleep(10);
out_free:
kfree(join);
@@ -262,34 +414,21 @@ out:
int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer)
{
int ret;
+ size_t res_len = 0;
wl1271_debug(DEBUG_CMD, "cmd test");
- ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len);
+ if (answer)
+ res_len = buf_len;
+
+ ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len, res_len);
if (ret < 0) {
wl1271_warning("TEST command failed");
return ret;
}
- if (answer) {
- struct wl1271_command *cmd_answer;
-
- /*
- * The test command got in, we can read the answer.
- * The answer would be a wl1271_command, where the
- * parameter array contains the actual answer.
- */
- wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, buf_len);
-
- cmd_answer = buf;
-
- if (cmd_answer->header.status != CMD_STATUS_SUCCESS)
- wl1271_error("TEST command answer error: %d",
- cmd_answer->header.status);
- }
-
- return 0;
+ return ret;
}
/**
@@ -307,26 +446,15 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
wl1271_debug(DEBUG_CMD, "cmd interrogate");
- acx->id = id;
+ acx->id = cpu_to_le16(id);
/* payload length, does not include any headers */
- acx->len = len - sizeof(*acx);
+ acx->len = cpu_to_le16(len - sizeof(*acx));
- ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx));
- if (ret < 0) {
+ ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len);
+ if (ret < 0)
wl1271_error("INTERROGATE command failed");
- goto out;
- }
- /* the interrogate command got in, we can read the answer */
- wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, len);
-
- acx = buf;
- if (acx->cmd.status != CMD_STATUS_SUCCESS)
- wl1271_error("INTERROGATE command error: %d",
- acx->cmd.status);
-
-out:
return ret;
}
@@ -345,12 +473,12 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
wl1271_debug(DEBUG_CMD, "cmd configure");
- acx->id = id;
+ acx->id = cpu_to_le16(id);
/* payload length, does not include any headers */
- acx->len = len - sizeof(*acx);
+ acx->len = cpu_to_le16(len - sizeof(*acx));
- ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len);
+ ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0);
if (ret < 0) {
wl1271_warning("CONFIGURE command NOK");
return ret;
@@ -383,7 +511,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable)
cmd_tx = CMD_DISABLE_TX;
}
- ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd));
+ ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("rx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
@@ -393,7 +521,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable)
wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d",
enable ? "start" : "stop", channel);
- ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd));
+ ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("tx %s cmd for channel %d failed",
enable ? "start" : "stop", channel);
@@ -414,8 +542,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode)
int ret = 0;
/* FIXME: this should be in ps.c */
- ret = wl1271_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP,
- wl->listen_int);
+ ret = wl1271_acx_wake_up_conditions(wl);
if (ret < 0) {
wl1271_error("couldn't set wake up conditions");
goto out;
@@ -433,10 +560,10 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode)
ps_params->send_null_data = 1;
ps_params->retries = 5;
ps_params->hang_over_period = 128;
- ps_params->null_data_rate = 1; /* 1 Mbps */
+ ps_params->null_data_rate = cpu_to_le32(1); /* 1 Mbps */
ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params,
- sizeof(*ps_params));
+ sizeof(*ps_params), 0);
if (ret < 0) {
wl1271_error("cmd set_ps_mode failed");
goto out;
@@ -464,22 +591,17 @@ int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
WARN_ON(len > MAX_READ_SIZE);
len = min_t(size_t, len, MAX_READ_SIZE);
- cmd->addr = addr;
- cmd->size = len;
+ cmd->addr = cpu_to_le32(addr);
+ cmd->size = cpu_to_le32(len);
- ret = wl1271_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd));
+ ret = wl1271_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd),
+ sizeof(*cmd));
if (ret < 0) {
wl1271_error("read memory command failed: %d", ret);
goto out;
}
- /* the read command got in, we can now read the answer */
- wl1271_spi_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd));
-
- if (cmd->header.status != CMD_STATUS_SUCCESS)
- wl1271_error("error in read command result: %d",
- cmd->header.status);
-
+ /* the read command got in */
memcpy(answer, cmd->value, len);
out:
@@ -488,14 +610,31 @@ out:
}
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
- u8 active_scan, u8 high_prio, u8 num_channels,
+ u8 active_scan, u8 high_prio, u8 band,
u8 probe_requests)
{
struct wl1271_cmd_trigger_scan_to *trigger = NULL;
struct wl1271_cmd_scan *params = NULL;
- int i, ret;
+ struct ieee80211_channel *channels;
+ int i, j, n_ch, ret;
u16 scan_options = 0;
+ u8 ieee_band;
+
+ if (band == WL1271_SCAN_BAND_2_4_GHZ)
+ ieee_band = IEEE80211_BAND_2GHZ;
+ else if (band == WL1271_SCAN_BAND_DUAL && wl1271_11a_enabled())
+ ieee_band = IEEE80211_BAND_2GHZ;
+ else if (band == WL1271_SCAN_BAND_5_GHZ && wl1271_11a_enabled())
+ ieee_band = IEEE80211_BAND_5GHZ;
+ else
+ return -EINVAL;
+
+ if (wl->hw->wiphy->bands[ieee_band]->channels == NULL)
+ return -EINVAL;
+
+ channels = wl->hw->wiphy->bands[ieee_band]->channels;
+ n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels;
if (wl->scanning)
return -EINVAL;
@@ -512,32 +651,43 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
scan_options |= WL1271_SCAN_OPT_PASSIVE;
if (high_prio)
scan_options |= WL1271_SCAN_OPT_PRIORITY_HIGH;
- params->params.scan_options = scan_options;
+ params->params.scan_options = cpu_to_le16(scan_options);
- params->params.num_channels = num_channels;
params->params.num_probe_requests = probe_requests;
- params->params.tx_rate = cpu_to_le32(RATE_MASK_2MBPS);
+ /* Let the fw autodetect suitable tx_rate for probes */
+ params->params.tx_rate = 0;
params->params.tid_trigger = 0;
params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
- for (i = 0; i < num_channels; i++) {
- params->channels[i].min_duration =
- cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION);
- params->channels[i].max_duration =
- cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION);
- memset(&params->channels[i].bssid_lsb, 0xff, 4);
- memset(&params->channels[i].bssid_msb, 0xff, 2);
- params->channels[i].early_termination = 0;
- params->channels[i].tx_power_att = WL1271_SCAN_CURRENT_TX_PWR;
- params->channels[i].channel = i + 1;
+ if (band == WL1271_SCAN_BAND_DUAL)
+ params->params.band = WL1271_SCAN_BAND_2_4_GHZ;
+ else
+ params->params.band = band;
+
+ for (i = 0, j = 0; i < n_ch && i < WL1271_SCAN_MAX_CHANNELS; i++) {
+ if (!(channels[i].flags & IEEE80211_CHAN_DISABLED)) {
+ params->channels[j].min_duration =
+ cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION);
+ params->channels[j].max_duration =
+ cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION);
+ memset(&params->channels[j].bssid_lsb, 0xff, 4);
+ memset(&params->channels[j].bssid_msb, 0xff, 2);
+ params->channels[j].early_termination = 0;
+ params->channels[j].tx_power_att =
+ WL1271_SCAN_CURRENT_TX_PWR;
+ params->channels[j].channel = channels[i].hw_value;
+ j++;
+ }
}
+ params->params.num_channels = j;
+
if (len && ssid) {
params->params.ssid_len = len;
memcpy(params->params.ssid, ssid, len);
}
- ret = wl1271_cmd_build_probe_req(wl, ssid, len);
+ ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
@@ -553,7 +703,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
trigger->timeout = 0;
ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
- sizeof(*trigger));
+ sizeof(*trigger), 0);
if (ret < 0) {
wl1271_error("trigger scan to failed for hw scan");
goto out;
@@ -562,20 +712,24 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params));
wl->scanning = true;
+ if (wl1271_11a_enabled()) {
+ wl->scan.state = band;
+ if (band == WL1271_SCAN_BAND_DUAL) {
+ wl->scan.active = active_scan;
+ wl->scan.high_prio = high_prio;
+ wl->scan.probe_requests = probe_requests;
+ if (len && ssid) {
+ wl->scan.ssid_len = len;
+ memcpy(wl->scan.ssid, ssid, len);
+ } else
+ wl->scan.ssid_len = 0;
+ }
+ }
- ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params));
+ ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
- goto out;
- }
-
- wl1271_spi_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params));
-
- if (params->header.status != CMD_STATUS_SUCCESS) {
- wl1271_error("Scan command error: %d",
- params->header.status);
wl->scanning = false;
- ret = -EIO;
goto out;
}
@@ -603,14 +757,14 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
cmd->len = cpu_to_le16(buf_len);
cmd->template_type = template_id;
- cmd->enabled_rates = ACX_RATE_MASK_UNSPECIFIED;
- cmd->short_retry_limit = ACX_RATE_RETRY_LIMIT;
- cmd->long_retry_limit = ACX_RATE_RETRY_LIMIT;
+ cmd->enabled_rates = cpu_to_le32(wl->conf.tx.rc_conf.enabled_rates);
+ cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit;
+ cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit;
if (buf)
memcpy(cmd->template_data, buf, buf_len);
- ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd));
+ ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_warning("cmd set_template failed: %d", ret);
goto out_free;
@@ -623,30 +777,62 @@ out:
return ret;
}
-static int wl1271_build_basic_rates(char *rates)
+static int wl1271_build_basic_rates(char *rates, u8 band)
{
u8 index = 0;
- rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
- rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
- rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB;
- rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB;
+ if (band == IEEE80211_BAND_2GHZ) {
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB;
+ } else if (band == IEEE80211_BAND_5GHZ) {
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
+ } else {
+ wl1271_error("build_basic_rates invalid band: %d", band);
+ }
return index;
}
-static int wl1271_build_extended_rates(char *rates)
+static int wl1271_build_extended_rates(char *rates, u8 band)
{
u8 index = 0;
- rates[index++] = IEEE80211_OFDM_RATE_6MB;
- rates[index++] = IEEE80211_OFDM_RATE_9MB;
- rates[index++] = IEEE80211_OFDM_RATE_12MB;
- rates[index++] = IEEE80211_OFDM_RATE_18MB;
- rates[index++] = IEEE80211_OFDM_RATE_24MB;
- rates[index++] = IEEE80211_OFDM_RATE_36MB;
- rates[index++] = IEEE80211_OFDM_RATE_48MB;
- rates[index++] = IEEE80211_OFDM_RATE_54MB;
+ if (band == IEEE80211_BAND_2GHZ) {
+ rates[index++] = IEEE80211_OFDM_RATE_6MB;
+ rates[index++] = IEEE80211_OFDM_RATE_9MB;
+ rates[index++] = IEEE80211_OFDM_RATE_12MB;
+ rates[index++] = IEEE80211_OFDM_RATE_18MB;
+ rates[index++] = IEEE80211_OFDM_RATE_24MB;
+ rates[index++] = IEEE80211_OFDM_RATE_36MB;
+ rates[index++] = IEEE80211_OFDM_RATE_48MB;
+ rates[index++] = IEEE80211_OFDM_RATE_54MB;
+ } else if (band == IEEE80211_BAND_5GHZ) {
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB;
+ rates[index++] =
+ IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB;
+ } else {
+ wl1271_error("build_basic_rates invalid band: %d", band);
+ }
return index;
}
@@ -665,7 +851,8 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl)
memcpy(template.header.sa, wl->mac_addr, ETH_ALEN);
template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA |
- IEEE80211_STYPE_NULLFUNC);
+ IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_TODS);
return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template,
sizeof(template));
@@ -678,7 +865,10 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
memcpy(template.bssid, wl->bssid, ETH_ALEN);
memcpy(template.ta, wl->mac_addr, ETH_ALEN);
- template.aid = aid;
+
+ /* aid in PS-Poll has its two MSBs each set to 1 */
+ template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid);
+
template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template,
@@ -686,12 +876,14 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
}
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len)
+int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
+ u8 band)
{
struct wl12xx_probe_req_template template;
struct wl12xx_ie_rates *rates;
char *ptr;
u16 size;
+ int ret;
ptr = (char *)&template;
size = sizeof(struct ieee80211_header);
@@ -713,20 +905,25 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len)
/* Basic Rates */
rates = (struct wl12xx_ie_rates *)ptr;
rates->header.id = WLAN_EID_SUPP_RATES;
- rates->header.len = wl1271_build_basic_rates(rates->rates);
+ rates->header.len = wl1271_build_basic_rates(rates->rates, band);
size += sizeof(struct wl12xx_ie_header) + rates->header.len;
ptr += sizeof(struct wl12xx_ie_header) + rates->header.len;
/* Extended rates */
rates = (struct wl12xx_ie_rates *)ptr;
rates->header.id = WLAN_EID_EXT_SUPP_RATES;
- rates->header.len = wl1271_build_extended_rates(rates->rates);
+ rates->header.len = wl1271_build_extended_rates(rates->rates, band);
size += sizeof(struct wl12xx_ie_header) + rates->header.len;
wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size);
- return wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
- &template, size);
+ if (band == IEEE80211_BAND_2GHZ)
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+ &template, size);
+ else
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+ &template, size);
+ return ret;
}
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
@@ -743,10 +940,10 @@ int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
}
cmd->id = id;
- cmd->key_action = KEY_SET_ID;
+ cmd->key_action = cpu_to_le16(KEY_SET_ID);
cmd->key_type = KEY_WEP;
- ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd));
+ ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_warning("cmd set_default_wep_key failed: %d", ret);
goto out;
@@ -759,7 +956,8 @@ out:
}
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
- u8 key_size, const u8 *key, const u8 *addr)
+ u8 key_size, const u8 *key, const u8 *addr,
+ u32 tx_seq_32, u16 tx_seq_16)
{
struct wl1271_cmd_set_keys *cmd;
int ret = 0;
@@ -773,16 +971,18 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
if (key_type != KEY_WEP)
memcpy(cmd->addr, addr, ETH_ALEN);
- cmd->key_action = action;
+ cmd->key_action = cpu_to_le16(action);
cmd->key_size = key_size;
cmd->key_type = key_type;
+ cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
+ cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);
+
/* we have only one SSID profile */
cmd->ssid_profile = 0;
cmd->id = id;
- /* FIXME: this is from wl1251, needs to be checked */
if (key_type == KEY_TKIP) {
/*
* We get the key in the following form:
@@ -800,7 +1000,7 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
wl1271_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd));
- ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd));
+ ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_warning("could not set keys");
goto out;
@@ -811,3 +1011,34 @@ out:
return ret;
}
+
+int wl1271_cmd_disconnect(struct wl1271 *wl)
+{
+ struct wl1271_cmd_disconnect *cmd;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_CMD, "cmd disconnect");
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->rx_config_options = cpu_to_le32(wl->rx_config);
+ cmd->rx_filter_options = cpu_to_le32(wl->rx_filter);
+ /* disconnect reason is not used in immediate disconnections */
+ cmd->type = DISCONNECT_IMMEDIATE;
+
+ ret = wl1271_cmd_send(wl, CMD_DISCONNECT, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("failed to send disconnect command");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h
index 951a8447a51..b4fa4acb922 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.h
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h
@@ -29,9 +29,11 @@
struct acx_header;
-int wl1271_cmd_send(struct wl1271 *wl, u16 type, void *buf, size_t buf_len);
-int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
- u16 beacon_interval, u8 wait);
+int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
+ size_t res_len);
+int wl1271_cmd_general_parms(struct wl1271 *wl);
+int wl1271_cmd_radio_parms(struct wl1271 *wl);
+int wl1271_cmd_join(struct wl1271 *wl);
int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
@@ -40,16 +42,19 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len);
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
- u8 active_scan, u8 high_prio, u8 num_channels,
+ u8 active_scan, u8 high_prio, u8 band,
u8 probe_requests);
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
void *buf, size_t buf_len);
int wl1271_cmd_build_null_data(struct wl1271 *wl);
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len);
+int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
+ u8 band);
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
- u8 key_size, const u8 *key, const u8 *addr);
+ u8 key_size, const u8 *key, const u8 *addr,
+ u32 tx_seq_32, u16 tx_seq_16);
+int wl1271_cmd_disconnect(struct wl1271 *wl);
enum wl1271_commands {
CMD_INTERROGATE = 1, /*use this to read information elements*/
@@ -118,8 +123,8 @@ enum cmd_templ {
#define WL1271_CMD_TEMPL_MAX_SIZE 252
struct wl1271_cmd_header {
- u16 id;
- u16 status;
+ __le16 id;
+ __le16 status;
/* payload */
u8 data[0];
} __attribute__ ((packed));
@@ -172,17 +177,17 @@ struct cmd_read_write_memory {
struct wl1271_cmd_header header;
/* The address of the memory to read from or write to.*/
- u32 addr;
+ __le32 addr;
/* The amount of data in bytes to read from or write to the WiLink
* device.*/
- u32 size;
+ __le32 size;
/* The actual value read from or written to the Wilink. The source
of this field is the Host in WRITE command or the Wilink in READ
command. */
u8 value[MAX_READ_SIZE];
-};
+} __attribute__ ((packed));
#define CMDMBOX_HEADER_LEN 4
#define CMDMBOX_INFO_ELEM_HEADER_LEN 4
@@ -196,22 +201,23 @@ enum {
#define WL1271_JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */
#define WL1271_JOIN_CMD_TX_SESSION_OFFSET 1
+#define WL1271_JOIN_CMD_BSS_TYPE_5GHZ 0x10
struct wl1271_cmd_join {
struct wl1271_cmd_header header;
- u32 bssid_lsb;
- u16 bssid_msb;
- u16 beacon_interval; /* in TBTTs */
- u32 rx_config_options;
- u32 rx_filter_options;
+ __le32 bssid_lsb;
+ __le16 bssid_msb;
+ __le16 beacon_interval; /* in TBTTs */
+ __le32 rx_config_options;
+ __le32 rx_filter_options;
/*
* The target uses this field to determine the rate at
* which to transmit control frame responses (such as
* ACK or CTS frames).
*/
- u32 basic_rate_set;
+ __le32 basic_rate_set;
u8 dtim_interval;
/*
* bits 0-2: This bitwise field specifies the type
@@ -240,10 +246,10 @@ struct cmd_enabledisable_path {
struct wl1271_cmd_template_set {
struct wl1271_cmd_header header;
- u16 len;
+ __le16 len;
u8 template_type;
u8 index; /* relevant only for KLV_TEMPLATE type */
- u32 enabled_rates;
+ __le32 enabled_rates;
u8 short_retry_limit;
u8 long_retry_limit;
u8 aflags;
@@ -280,18 +286,13 @@ struct wl1271_cmd_ps_params {
* to power save mode.
*/
u8 hang_over_period;
- u32 null_data_rate;
+ __le32 null_data_rate;
} __attribute__ ((packed));
/* HW encryption keys */
#define NUM_ACCESS_CATEGORIES_COPY 4
#define MAX_KEY_SIZE 32
-/* When set, disable HW encryption */
-#define DF_ENCRYPTION_DISABLE 0x01
-/* When set, disable HW decryption */
-#define DF_SNIFF_MODE_ENABLE 0x80
-
enum wl1271_cmd_key_action {
KEY_ADD_OR_REPLACE = 1,
KEY_REMOVE = 2,
@@ -316,9 +317,9 @@ struct wl1271_cmd_set_keys {
u8 addr[ETH_ALEN];
/* key_action_e */
- u16 key_action;
+ __le16 key_action;
- u16 reserved_1;
+ __le16 reserved_1;
/* key size in bytes */
u8 key_size;
@@ -334,8 +335,8 @@ struct wl1271_cmd_set_keys {
u8 id;
u8 reserved_2[6];
u8 key[MAX_KEY_SIZE];
- u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
- u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
+ __le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
+ __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __attribute__ ((packed));
@@ -347,19 +348,22 @@ struct wl1271_cmd_set_keys {
#define WL1271_SCAN_OPT_PRIORITY_HIGH 4
#define WL1271_SCAN_CHAN_MIN_DURATION 30000 /* TU */
#define WL1271_SCAN_CHAN_MAX_DURATION 60000 /* TU */
+#define WL1271_SCAN_BAND_2_4_GHZ 0
+#define WL1271_SCAN_BAND_5_GHZ 1
+#define WL1271_SCAN_BAND_DUAL 2
struct basic_scan_params {
- u32 rx_config_options;
- u32 rx_filter_options;
+ __le32 rx_config_options;
+ __le32 rx_filter_options;
/* Scan option flags (WL1271_SCAN_OPT_*) */
- u16 scan_options;
+ __le16 scan_options;
/* Number of scan channels in the list (maximum 30) */
u8 num_channels;
/* This field indicates the number of probe requests to send
per channel for an active scan */
u8 num_probe_requests;
/* Rate bit field for sending the probes */
- u32 tx_rate;
+ __le32 tx_rate;
u8 tid_trigger;
u8 ssid_len;
/* in order to align */
@@ -374,10 +378,10 @@ struct basic_scan_params {
struct basic_scan_channel_params {
/* Duration in TU to wait for frames on a channel for active scan */
- u32 min_duration;
- u32 max_duration;
- u32 bssid_lsb;
- u16 bssid_msb;
+ __le32 min_duration;
+ __le32 max_duration;
+ __le32 bssid_lsb;
+ __le16 bssid_msb;
u8 early_termination;
u8 tx_power_att;
u8 channel;
@@ -397,13 +401,13 @@ struct wl1271_cmd_scan {
struct wl1271_cmd_trigger_scan_to {
struct wl1271_cmd_header header;
- u32 timeout;
-};
+ __le32 timeout;
+} __attribute__ ((packed));
struct wl1271_cmd_test_header {
u8 id;
u8 padding[3];
-};
+} __attribute__ ((packed));
enum wl1271_channel_tune_bands {
WL1271_CHANNEL_TUNE_BAND_2_4,
@@ -416,6 +420,76 @@ enum wl1271_channel_tune_bands {
#define TEST_CMD_P2G_CAL 0x02
#define TEST_CMD_CHANNEL_TUNE 0x0d
#define TEST_CMD_UPDATE_PD_REFERENCE_POINT 0x1d
+#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19
+#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E
+
+struct wl1271_general_parms_cmd {
+ struct wl1271_cmd_header header;
+
+ struct wl1271_cmd_test_header test;
+
+ u8 ref_clk;
+ u8 settling_time;
+ u8 clk_valid_on_wakeup;
+ u8 dc2dcmode;
+ u8 single_dual_band;
+
+ u8 tx_bip_fem_autodetect;
+ u8 tx_bip_fem_manufacturer;
+ u8 settings;
+} __attribute__ ((packed));
+
+struct wl1271_radio_parms_cmd {
+ struct wl1271_cmd_header header;
+
+ struct wl1271_cmd_test_header test;
+
+ /* Static radio parameters */
+ /* 2.4GHz */
+ u8 rx_trace_loss;
+ u8 tx_trace_loss;
+ s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE];
+
+ /* 5GHz */
+ u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE];
+
+ /* Dynamic radio parameters */
+ /* 2.4GHz */
+ __le16 tx_ref_pd_voltage;
+ s8 tx_ref_power;
+ s8 tx_offset_db;
+
+ s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS];
+ s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS];
+
+ s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4];
+ s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4];
+ s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS];
+
+ u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS];
+ u8 rx_fem_insertion_loss;
+
+ u8 padding2;
+
+ /* 5GHz */
+ __le16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5];
+
+ s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS];
+ s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS];
+
+ s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5];
+ s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS];
+
+ /* FIXME: this is inconsistent with the types for 2.4GHz */
+ s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS];
+ s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
+
+ u8 padding3[2];
+} __attribute__ ((packed));
struct wl1271_cmd_cal_channel_tune {
struct wl1271_cmd_header header;
@@ -425,7 +499,7 @@ struct wl1271_cmd_cal_channel_tune {
u8 band;
u8 channel;
- u16 radio_status;
+ __le16 radio_status;
} __attribute__ ((packed));
struct wl1271_cmd_cal_update_ref_point {
@@ -433,8 +507,8 @@ struct wl1271_cmd_cal_update_ref_point {
struct wl1271_cmd_test_header test;
- s32 ref_power;
- s32 ref_detector;
+ __le32 ref_power;
+ __le32 ref_detector;
u8 sub_band;
u8 padding[3];
} __attribute__ ((packed));
@@ -449,16 +523,42 @@ struct wl1271_cmd_cal_p2g {
struct wl1271_cmd_test_header test;
- u16 len;
+ __le16 len;
u8 buf[MAX_TLV_LENGTH];
u8 type;
u8 padding;
- s16 radio_status;
+ __le16 radio_status;
u8 nvs_version[MAX_NVS_VERSION_LENGTH];
u8 sub_band_mask;
u8 padding2;
} __attribute__ ((packed));
+
+/*
+ * There are three types of disconnections:
+ *
+ * DISCONNECT_IMMEDIATE: the fw doesn't send any frames
+ * DISCONNECT_DEAUTH: the fw generates a DEAUTH request with the reason
+ * we have passed
+ * DISCONNECT_DISASSOC: the fw generates a DESASSOC request with the reason
+ * we have passed
+ */
+enum wl1271_disconnect_type {
+ DISCONNECT_IMMEDIATE,
+ DISCONNECT_DEAUTH,
+ DISCONNECT_DISASSOC
+};
+
+struct wl1271_cmd_disconnect {
+ __le32 rx_config_options;
+ __le32 rx_filter_options;
+
+ __le16 reason;
+ u8 type;
+
+ u8 padding;
+} __attribute__ ((packed));
+
#endif /* __WL1271_CMD_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h
new file mode 100644
index 00000000000..565373ede26
--- /dev/null
+++ b/drivers/net/wireless/wl12xx/wl1271_conf.h
@@ -0,0 +1,919 @@
+/*
+ * This file is part of wl1271
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL1271_CONF_H__
+#define __WL1271_CONF_H__
+
+enum {
+ CONF_HW_BIT_RATE_1MBPS = BIT(0),
+ CONF_HW_BIT_RATE_2MBPS = BIT(1),
+ CONF_HW_BIT_RATE_5_5MBPS = BIT(2),
+ CONF_HW_BIT_RATE_6MBPS = BIT(3),
+ CONF_HW_BIT_RATE_9MBPS = BIT(4),
+ CONF_HW_BIT_RATE_11MBPS = BIT(5),
+ CONF_HW_BIT_RATE_12MBPS = BIT(6),
+ CONF_HW_BIT_RATE_18MBPS = BIT(7),
+ CONF_HW_BIT_RATE_22MBPS = BIT(8),
+ CONF_HW_BIT_RATE_24MBPS = BIT(9),
+ CONF_HW_BIT_RATE_36MBPS = BIT(10),
+ CONF_HW_BIT_RATE_48MBPS = BIT(11),
+ CONF_HW_BIT_RATE_54MBPS = BIT(12),
+ CONF_HW_BIT_RATE_MCS_0 = BIT(13),
+ CONF_HW_BIT_RATE_MCS_1 = BIT(14),
+ CONF_HW_BIT_RATE_MCS_2 = BIT(15),
+ CONF_HW_BIT_RATE_MCS_3 = BIT(16),
+ CONF_HW_BIT_RATE_MCS_4 = BIT(17),
+ CONF_HW_BIT_RATE_MCS_5 = BIT(18),
+ CONF_HW_BIT_RATE_MCS_6 = BIT(19),
+ CONF_HW_BIT_RATE_MCS_7 = BIT(20)
+};
+
+enum {
+ CONF_HW_RATE_INDEX_1MBPS = 0,
+ CONF_HW_RATE_INDEX_2MBPS = 1,
+ CONF_HW_RATE_INDEX_5_5MBPS = 2,
+ CONF_HW_RATE_INDEX_6MBPS = 3,
+ CONF_HW_RATE_INDEX_9MBPS = 4,
+ CONF_HW_RATE_INDEX_11MBPS = 5,
+ CONF_HW_RATE_INDEX_12MBPS = 6,
+ CONF_HW_RATE_INDEX_18MBPS = 7,
+ CONF_HW_RATE_INDEX_22MBPS = 8,
+ CONF_HW_RATE_INDEX_24MBPS = 9,
+ CONF_HW_RATE_INDEX_36MBPS = 10,
+ CONF_HW_RATE_INDEX_48MBPS = 11,
+ CONF_HW_RATE_INDEX_54MBPS = 12,
+ CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS,
+};
+
+struct conf_sg_settings {
+ /*
+ * Defines the PER threshold in PPM of the BT voice of which reaching
+ * this value will trigger raising the priority of the BT voice by
+ * the BT IP until next NFS sample interval time as defined in
+ * nfs_sample_interval.
+ *
+ * Unit: PER value in PPM (parts per million)
+ * #Error_packets / #Total_packets
+
+ * Range: u32
+ */
+ u32 per_threshold;
+
+ /*
+ * This value is an absolute time in micro-seconds to limit the
+ * maximum scan duration compensation while in SG
+ */
+ u32 max_scan_compensation_time;
+
+ /* Defines the PER threshold of the BT voice of which reaching this
+ * value will trigger raising the priority of the BT voice until next
+ * NFS sample interval time as defined in sample_interval.
+ *
+ * Unit: msec
+ * Range: 1-65000
+ */
+ u16 nfs_sample_interval;
+
+ /*
+ * Defines the load ratio for the BT.
+ * The WLAN ratio is: 100 - load_ratio
+ *
+ * Unit: Percent
+ * Range: 0-100
+ */
+ u8 load_ratio;
+
+ /*
+ * true - Co-ex is allowed to enter/exit P.S automatically and
+ * transparently to the host
+ *
+ * false - Co-ex is disallowed to enter/exit P.S and will trigger an
+ * event to the host to notify for the need to enter/exit P.S
+ * due to BT change state
+ *
+ */
+ u8 auto_ps_mode;
+
+ /*
+ * This parameter defines the compensation percentage of num of probe
+ * requests in case scan is initiated during BT voice/BT ACL
+ * guaranteed link.
+ *
+ * Unit: Percent
+ * Range: 0-255 (0 - No compensation)
+ */
+ u8 probe_req_compensation;
+
+ /*
+ * This parameter defines the compensation percentage of scan window
+ * size in case scan is initiated during BT voice/BT ACL Guaranteed
+ * link.
+ *
+ * Unit: Percent
+ * Range: 0-255 (0 - No compensation)
+ */
+ u8 scan_window_compensation;
+
+ /*
+ * Defines the antenna configuration.
+ *
+ * Range: 0 - Single Antenna; 1 - Dual Antenna
+ */
+ u8 antenna_config;
+
+ /*
+ * The percent out of the Max consecutive beacon miss roaming trigger
+ * which is the threshold for raising the priority of beacon
+ * reception.
+ *
+ * Range: 1-100
+ * N = MaxConsecutiveBeaconMiss
+ * P = coexMaxConsecutiveBeaconMissPrecent
+ * Threshold = MIN( N-1, round(N * P / 100))
+ */
+ u8 beacon_miss_threshold;
+
+ /*
+ * The RX rate threshold below which rate adaptation is assumed to be
+ * occurring at the AP which will raise priority for ACTIVE_RX and RX
+ * SP.
+ *
+ * Range: HW_BIT_RATE_*
+ */
+ u32 rate_adaptation_threshold;
+
+ /*
+ * The SNR above which the RX rate threshold indicating AP rate
+ * adaptation is valid
+ *
+ * Range: -128 - 127
+ */
+ s8 rate_adaptation_snr;
+};
+
+enum conf_rx_queue_type {
+ CONF_RX_QUEUE_TYPE_LOW_PRIORITY, /* All except the high priority */
+ CONF_RX_QUEUE_TYPE_HIGH_PRIORITY, /* Management and voice packets */
+};
+
+struct conf_rx_settings {
+ /*
+ * The maximum amount of time, in TU, before the
+ * firmware discards the MSDU.
+ *
+ * Range: 0 - 0xFFFFFFFF
+ */
+ u32 rx_msdu_life_time;
+
+ /*
+ * Packet detection threshold in the PHY.
+ *
+ * FIXME: details unknown.
+ */
+ u32 packet_detection_threshold;
+
+ /*
+ * The longest time the STA will wait to receive traffic from the AP
+ * after a PS-poll has been transmitted.
+ *
+ * Range: 0 - 200000
+ */
+ u16 ps_poll_timeout;
+ /*
+ * The longest time the STA will wait to receive traffic from the AP
+ * after a frame has been sent from an UPSD enabled queue.
+ *
+ * Range: 0 - 200000
+ */
+ u16 upsd_timeout;
+
+ /*
+ * The number of octets in an MPDU, below which an RTS/CTS
+ * handshake is not performed.
+ *
+ * Range: 0 - 4096
+ */
+ u16 rts_threshold;
+
+ /*
+ * The RX Clear Channel Assessment threshold in the PHY
+ * (the energy threshold).
+ *
+ * Range: ENABLE_ENERGY_D == 0x140A
+ * DISABLE_ENERGY_D == 0xFFEF
+ */
+ u16 rx_cca_threshold;
+
+ /*
+ * Occupied Rx mem-blocks number which requires interrupting the host
+ * (0 = no buffering, 0xffff = disabled).
+ *
+ * Range: u16
+ */
+ u16 irq_blk_threshold;
+
+ /*
+ * Rx packets number which requires interrupting the host
+ * (0 = no buffering).
+ *
+ * Range: u16
+ */
+ u16 irq_pkt_threshold;
+
+ /*
+ * Max time in msec the FW may delay RX-Complete interrupt.
+ *
+ * Range: 1 - 100
+ */
+ u16 irq_timeout;
+
+ /*
+ * The RX queue type.
+ *
+ * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY,
+ */
+ u8 queue_type;
+};
+
+#define CONF_TX_MAX_RATE_CLASSES 8
+
+#define CONF_TX_RATE_MASK_UNSPECIFIED 0
+#define CONF_TX_RATE_MASK_ALL 0x1eff
+#define CONF_TX_RATE_RETRY_LIMIT 10
+
+struct conf_tx_rate_class {
+
+ /*
+ * The rates enabled for this rate class.
+ *
+ * Range: CONF_HW_BIT_RATE_* bit mask
+ */
+ u32 enabled_rates;
+
+ /*
+ * The dot11 short retry limit used for TX retries.
+ *
+ * Range: u8
+ */
+ u8 short_retry_limit;
+
+ /*
+ * The dot11 long retry limit used for TX retries.
+ *
+ * Range: u8
+ */
+ u8 long_retry_limit;
+
+ /*
+ * Flags controlling the attributes of TX transmission.
+ *
+ * Range: bit 0: Truncate - when set, FW attempts to send a frame stop
+ * when the total valid per-rate attempts have
+ * been exhausted; otherwise transmissions
+ * will continue at the lowest available rate
+ * until the appropriate one of the
+ * short_retry_limit, long_retry_limit,
+ * dot11_max_transmit_msdu_life_time, or
+ * max_tx_life_time, is exhausted.
+ * 1: Preamble Override - indicates if the preamble type
+ * should be used in TX.
+ * 2: Preamble Type - the type of the preamble to be used by
+ * the policy (0 - long preamble, 1 - short preamble.
+ */
+ u8 aflags;
+};
+
+#define CONF_TX_MAX_AC_COUNT 4
+
+/* Slot number setting to start transmission at PIFS interval */
+#define CONF_TX_AIFS_PIFS 1
+/* Slot number setting to start transmission at DIFS interval normal
+ * DCF access */
+#define CONF_TX_AIFS_DIFS 2
+
+
+enum conf_tx_ac {
+ CONF_TX_AC_BE = 0, /* best effort / legacy */
+ CONF_TX_AC_BK = 1, /* background */
+ CONF_TX_AC_VI = 2, /* video */
+ CONF_TX_AC_VO = 3, /* voice */
+ CONF_TX_AC_CTS2SELF = 4, /* fictious AC, follows AC_VO */
+ CONF_TX_AC_ANY_TID = 0x1f
+};
+
+struct conf_tx_ac_category {
+ /*
+ * The AC class identifier.
+ *
+ * Range: enum conf_tx_ac
+ */
+ u8 ac;
+
+ /*
+ * The contention window minimum size (in slots) for the access
+ * class.
+ *
+ * Range: u8
+ */
+ u8 cw_min;
+
+ /*
+ * The contention window maximum size (in slots) for the access
+ * class.
+ *
+ * Range: u8
+ */
+ u16 cw_max;
+
+ /*
+ * The AIF value (in slots) for the access class.
+ *
+ * Range: u8
+ */
+ u8 aifsn;
+
+ /*
+ * The TX Op Limit (in microseconds) for the access class.
+ *
+ * Range: u16
+ */
+ u16 tx_op_limit;
+};
+
+#define CONF_TX_MAX_TID_COUNT 7
+
+enum {
+ CONF_CHANNEL_TYPE_DCF = 0, /* DC/LEGACY*/
+ CONF_CHANNEL_TYPE_EDCF = 1, /* EDCA*/
+ CONF_CHANNEL_TYPE_HCCA = 2, /* HCCA*/
+};
+
+enum {
+ CONF_PS_SCHEME_LEGACY = 0,
+ CONF_PS_SCHEME_UPSD_TRIGGER = 1,
+ CONF_PS_SCHEME_LEGACY_PSPOLL = 2,
+ CONF_PS_SCHEME_SAPSD = 3,
+};
+
+enum {
+ CONF_ACK_POLICY_LEGACY = 0,
+ CONF_ACK_POLICY_NO_ACK = 1,
+ CONF_ACK_POLICY_BLOCK = 2,
+};
+
+
+struct conf_tx_tid {
+ u8 queue_id;
+ u8 channel_type;
+ u8 tsid;
+ u8 ps_scheme;
+ u8 ack_policy;
+ u32 apsd_conf[2];
+};
+
+struct conf_tx_settings {
+ /*
+ * The TX ED value for TELEC Enable/Disable.
+ *
+ * Range: 0, 1
+ */
+ u8 tx_energy_detection;
+
+ /*
+ * Configuration for rate classes for TX (currently only one
+ * rate class supported.)
+ */
+ struct conf_tx_rate_class rc_conf;
+
+ /*
+ * Configuration for access categories for TX rate control.
+ */
+ u8 ac_conf_count;
+ struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT];
+
+ /*
+ * Configuration for TID parameters.
+ */
+ u8 tid_conf_count;
+ struct conf_tx_tid tid_conf[CONF_TX_MAX_TID_COUNT];
+
+ /*
+ * The TX fragmentation threshold.
+ *
+ * Range: u16
+ */
+ u16 frag_threshold;
+
+ /*
+ * Max time in msec the FW may delay frame TX-Complete interrupt.
+ *
+ * Range: u16
+ */
+ u16 tx_compl_timeout;
+
+ /*
+ * Completed TX packet count which requires to issue the TX-Complete
+ * interrupt.
+ *
+ * Range: u16
+ */
+ u16 tx_compl_threshold;
+
+};
+
+enum {
+ CONF_WAKE_UP_EVENT_BEACON = 0x01, /* Wake on every Beacon*/
+ CONF_WAKE_UP_EVENT_DTIM = 0x02, /* Wake on every DTIM*/
+ CONF_WAKE_UP_EVENT_N_DTIM = 0x04, /* Wake every Nth DTIM */
+ CONF_WAKE_UP_EVENT_N_BEACONS = 0x08, /* Wake every Nth beacon */
+ CONF_WAKE_UP_EVENT_BITS_MASK = 0x0F
+};
+
+#define CONF_MAX_BCN_FILT_IE_COUNT 32
+
+#define CONF_BCN_RULE_PASS_ON_CHANGE BIT(0)
+#define CONF_BCN_RULE_PASS_ON_APPEARANCE BIT(1)
+
+#define CONF_BCN_IE_OUI_LEN 3
+#define CONF_BCN_IE_VER_LEN 2
+
+struct conf_bcn_filt_rule {
+ /*
+ * IE number to which to associate a rule.
+ *
+ * Range: u8
+ */
+ u8 ie;
+
+ /*
+ * Rule to associate with the specific ie.
+ *
+ * Range: CONF_BCN_RULE_PASS_ON_*
+ */
+ u8 rule;
+
+ /*
+ * OUI for the vendor specifie IE (221)
+ */
+ u8 oui[CONF_BCN_IE_OUI_LEN];
+
+ /*
+ * Type for the vendor specifie IE (221)
+ */
+ u8 type;
+
+ /*
+ * Version for the vendor specifie IE (221)
+ */
+ u8 version[CONF_BCN_IE_VER_LEN];
+};
+
+#define CONF_MAX_RSSI_SNR_TRIGGERS 8
+
+enum {
+ CONF_TRIG_METRIC_RSSI_BEACON = 0,
+ CONF_TRIG_METRIC_RSSI_DATA,
+ CONF_TRIG_METRIC_SNR_BEACON,
+ CONF_TRIG_METRIC_SNR_DATA
+};
+
+enum {
+ CONF_TRIG_EVENT_TYPE_LEVEL = 0,
+ CONF_TRIG_EVENT_TYPE_EDGE
+};
+
+enum {
+ CONF_TRIG_EVENT_DIR_LOW = 0,
+ CONF_TRIG_EVENT_DIR_HIGH,
+ CONF_TRIG_EVENT_DIR_BIDIR
+};
+
+
+struct conf_sig_trigger {
+ /*
+ * The RSSI / SNR threshold value.
+ *
+ * FIXME: what is the range?
+ */
+ s16 threshold;
+
+ /*
+ * Minimum delay between two trigger events for this trigger in ms.
+ *
+ * Range: 0 - 60000
+ */
+ u16 pacing;
+
+ /*
+ * The measurement data source for this trigger.
+ *
+ * Range: CONF_TRIG_METRIC_*
+ */
+ u8 metric;
+
+ /*
+ * The trigger type of this trigger.
+ *
+ * Range: CONF_TRIG_EVENT_TYPE_*
+ */
+ u8 type;
+
+ /*
+ * The direction of the trigger.
+ *
+ * Range: CONF_TRIG_EVENT_DIR_*
+ */
+ u8 direction;
+
+ /*
+ * Hysteresis range of the trigger around the threshold (in dB)
+ *
+ * Range: u8
+ */
+ u8 hysteresis;
+
+ /*
+ * Index of the trigger rule.
+ *
+ * Range: 0 - CONF_MAX_RSSI_SNR_TRIGGERS-1
+ */
+ u8 index;
+
+ /*
+ * Enable / disable this rule (to use for clearing rules.)
+ *
+ * Range: 1 - Enabled, 2 - Not enabled
+ */
+ u8 enable;
+};
+
+struct conf_sig_weights {
+
+ /*
+ * RSSI from beacons average weight.
+ *
+ * Range: u8
+ */
+ u8 rssi_bcn_avg_weight;
+
+ /*
+ * RSSI from data average weight.
+ *
+ * Range: u8
+ */
+ u8 rssi_pkt_avg_weight;
+
+ /*
+ * SNR from beacons average weight.
+ *
+ * Range: u8
+ */
+ u8 snr_bcn_avg_weight;
+
+ /*
+ * SNR from data average weight.
+ *
+ * Range: u8
+ */
+ u8 snr_pkt_avg_weight;
+};
+
+enum conf_bcn_filt_mode {
+ CONF_BCN_FILT_MODE_DISABLED = 0,
+ CONF_BCN_FILT_MODE_ENABLED = 1
+};
+
+enum conf_bet_mode {
+ CONF_BET_MODE_DISABLE = 0,
+ CONF_BET_MODE_ENABLE = 1,
+};
+
+struct conf_conn_settings {
+ /*
+ * Firmware wakeup conditions configuration. The host may set only
+ * one bit.
+ *
+ * Range: CONF_WAKE_UP_EVENT_*
+ */
+ u8 wake_up_event;
+
+ /*
+ * Listen interval for beacons or Dtims.
+ *
+ * Range: 0 for beacon and Dtim wakeup
+ * 1-10 for x Dtims
+ * 1-255 for x beacons
+ */
+ u8 listen_interval;
+
+ /*
+ * Enable or disable the beacon filtering.
+ *
+ * Range: CONF_BCN_FILT_MODE_*
+ */
+ enum conf_bcn_filt_mode bcn_filt_mode;
+
+ /*
+ * Configure Beacon filter pass-thru rules.
+ */
+ u8 bcn_filt_ie_count;
+ struct conf_bcn_filt_rule bcn_filt_ie[CONF_MAX_BCN_FILT_IE_COUNT];
+
+ /*
+ * The number of consequtive beacons to lose, before the firmware
+ * becomes out of synch.
+ *
+ * Range: u32
+ */
+ u32 synch_fail_thold;
+
+ /*
+ * After out-of-synch, the number of TU's to wait without a further
+ * received beacon (or probe response) before issuing the BSS_EVENT_LOSE
+ * event.
+ *
+ * Range: u32
+ */
+ u32 bss_lose_timeout;
+
+ /*
+ * Beacon receive timeout.
+ *
+ * Range: u32
+ */
+ u32 beacon_rx_timeout;
+
+ /*
+ * Broadcast receive timeout.
+ *
+ * Range: u32
+ */
+ u32 broadcast_timeout;
+
+ /*
+ * Enable/disable reception of broadcast packets in power save mode
+ *
+ * Range: 1 - enable, 0 - disable
+ */
+ u8 rx_broadcast_in_ps;
+
+ /*
+ * Consequtive PS Poll failures before sending event to driver
+ *
+ * Range: u8
+ */
+ u8 ps_poll_threshold;
+
+ /*
+ * Configuration of signal (rssi/snr) triggers.
+ */
+ u8 sig_trigger_count;
+ struct conf_sig_trigger sig_trigger[CONF_MAX_RSSI_SNR_TRIGGERS];
+
+ /*
+ * Configuration of signal average weights.
+ */
+ struct conf_sig_weights sig_weights;
+
+ /*
+ * Specifies if beacon early termination procedure is enabled or
+ * disabled.
+ *
+ * Range: CONF_BET_MODE_*
+ */
+ u8 bet_enable;
+
+ /*
+ * Specifies the maximum number of consecutive beacons that may be
+ * early terminated. After this number is reached at least one full
+ * beacon must be correctly received in FW before beacon ET
+ * resumes.
+ *
+ * Range 0 - 255
+ */
+ u8 bet_max_consecutive;
+
+ /*
+ * Specifies the maximum number of times to try PSM entry if it fails
+ * (if sending the appropriate null-func message fails.)
+ *
+ * Range 0 - 255
+ */
+ u8 psm_entry_retries;
+};
+
+#define CONF_SR_ERR_TBL_MAX_VALUES 14
+
+struct conf_mart_reflex_err_table {
+ /*
+ * Length of the error table values table.
+ *
+ * Range: 0 - CONF_SR_ERR_TBL_MAX_VALUES
+ */
+ u8 len;
+
+ /*
+ * Smart Reflex error table upper limit.
+ *
+ * Range: s8
+ */
+ s8 upper_limit;
+
+ /*
+ * Smart Reflex error table values.
+ *
+ * Range: s8
+ */
+ s8 values[CONF_SR_ERR_TBL_MAX_VALUES];
+};
+
+enum {
+ CONF_REF_CLK_19_2_E,
+ CONF_REF_CLK_26_E,
+ CONF_REF_CLK_38_4_E,
+ CONF_REF_CLK_52_E
+};
+
+enum single_dual_band_enum {
+ CONF_SINGLE_BAND,
+ CONF_DUAL_BAND
+};
+
+struct conf_general_parms {
+ /*
+ * RF Reference Clock type / speed
+ *
+ * Range: CONF_REF_CLK_*
+ */
+ u8 ref_clk;
+
+ /*
+ * Settling time of the reference clock after boot.
+ *
+ * Range: u8
+ */
+ u8 settling_time;
+
+ /*
+ * Flag defining whether clock is valid on wakeup.
+ *
+ * Range: 0 - not valid on wakeup, 1 - valid on wakeup
+ */
+ u8 clk_valid_on_wakeup;
+
+ /*
+ * DC-to-DC mode.
+ *
+ * Range: Unknown
+ */
+ u8 dc2dcmode;
+
+ /*
+ * Flag defining whether used as single or dual-band.
+ *
+ * Range: CONF_SINGLE_BAND, CONF_DUAL_BAND
+ */
+ u8 single_dual_band;
+
+ /*
+ * TX bip fem autodetect flag.
+ *
+ * Range: Unknown
+ */
+ u8 tx_bip_fem_autodetect;
+
+ /*
+ * TX bip gem manufacturer.
+ *
+ * Range: Unknown
+ */
+ u8 tx_bip_fem_manufacturer;
+
+ /*
+ * Settings flags.
+ *
+ * Range: Unknown
+ */
+ u8 settings;
+};
+
+#define CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE 15
+#define CONF_NUMBER_OF_SUB_BANDS_5 7
+#define CONF_NUMBER_OF_RATE_GROUPS 6
+#define CONF_NUMBER_OF_CHANNELS_2_4 14
+#define CONF_NUMBER_OF_CHANNELS_5 35
+
+struct conf_radio_parms {
+ /*
+ * Static radio parameters for 2.4GHz
+ *
+ * Range: unknown
+ */
+ u8 rx_trace_loss;
+ u8 tx_trace_loss;
+ s8 rx_rssi_and_proc_compens[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE];
+
+ /*
+ * Static radio parameters for 5GHz
+ *
+ * Range: unknown
+ */
+ u8 rx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ u8 tx_trace_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ s8 rx_rssi_and_proc_compens_5[CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE];
+
+ /*
+ * Dynamic radio parameters for 2.4GHz
+ *
+ * Range: unknown
+ */
+ s16 tx_ref_pd_voltage;
+ s8 tx_ref_power;
+ s8 tx_offset_db;
+
+ s8 tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS];
+ s8 tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS];
+
+ s8 tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4];
+ s8 tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4];
+ s8 tx_pdv_rate_offsets[CONF_NUMBER_OF_RATE_GROUPS];
+
+ u8 tx_ibias[CONF_NUMBER_OF_RATE_GROUPS];
+ u8 rx_fem_insertion_loss;
+
+ /*
+ * Dynamic radio parameters for 5GHz
+ *
+ * Range: unknown
+ */
+ s16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ s8 tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5];
+ s8 tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5];
+
+ s8 tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS];
+ s8 tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS];
+
+ s8 tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5];
+ s8 tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS];
+
+ /* FIXME: this is inconsistent with the types for 2.4GHz */
+ s8 tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS];
+ s8 rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
+};
+
+#define CONF_SR_ERR_TBL_COUNT 3
+
+struct conf_init_settings {
+ /*
+ * Configure Smart Reflex error table values.
+ */
+ struct conf_mart_reflex_err_table sr_err_tbl[CONF_SR_ERR_TBL_COUNT];
+
+ /*
+ * Smart Reflex enable flag.
+ *
+ * Range: 1 - Smart Reflex enabled, 0 - Smart Reflex disabled
+ */
+ u8 sr_enable;
+
+ /*
+ * Configure general parameters.
+ */
+ struct conf_general_parms genparam;
+
+ /*
+ * Configure radio parameters.
+ */
+ struct conf_radio_parms radioparam;
+
+};
+
+struct conf_drv_settings {
+ struct conf_sg_settings sg;
+ struct conf_rx_settings rx;
+ struct conf_tx_settings tx;
+ struct conf_conn_settings conn;
+ struct conf_init_settings init;
+};
+
+#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c
index f3afd4a6ff3..d13fdd99c85 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.c
+++ b/drivers/net/wireless/wl12xx/wl1271_event.c
@@ -26,23 +26,86 @@
#include "wl1271_spi.h"
#include "wl1271_event.h"
#include "wl1271_ps.h"
+#include "wl12xx_80211.h"
static int wl1271_event_scan_complete(struct wl1271 *wl,
struct event_mailbox *mbox)
{
+ int size = sizeof(struct wl12xx_probe_req_template);
wl1271_debug(DEBUG_EVENT, "status: 0x%x",
mbox->scheduled_scan_status);
if (wl->scanning) {
- mutex_unlock(&wl->mutex);
- ieee80211_scan_completed(wl->hw, false);
- mutex_lock(&wl->mutex);
- wl->scanning = false;
+ if (wl->scan.state == WL1271_SCAN_BAND_DUAL) {
+ wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+ NULL, size);
+ /* 2.4 GHz band scanned, scan 5 GHz band, pretend
+ * to the wl1271_cmd_scan function that we are not
+ * scanning as it checks that.
+ */
+ wl->scanning = false;
+ wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
+ wl->scan.active,
+ wl->scan.high_prio,
+ WL1271_SCAN_BAND_5_GHZ,
+ wl->scan.probe_requests);
+ } else {
+ if (wl->scan.state == WL1271_SCAN_BAND_2_4_GHZ)
+ wl1271_cmd_template_set(wl,
+ CMD_TEMPL_CFG_PROBE_REQ_2_4,
+ NULL, size);
+ else
+ wl1271_cmd_template_set(wl,
+ CMD_TEMPL_CFG_PROBE_REQ_5,
+ NULL, size);
+
+ mutex_unlock(&wl->mutex);
+ ieee80211_scan_completed(wl->hw, false);
+ mutex_lock(&wl->mutex);
+ wl->scanning = false;
+ }
}
-
return 0;
}
+static int wl1271_event_ps_report(struct wl1271 *wl,
+ struct event_mailbox *mbox,
+ bool *beacon_loss)
+{
+ int ret = 0;
+
+ wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status);
+
+ switch (mbox->ps_status) {
+ case EVENT_ENTER_POWER_SAVE_FAIL:
+ if (!wl->psm) {
+ wl->psm_entry_retry = 0;
+ break;
+ }
+
+ if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) {
+ wl->psm_entry_retry++;
+ ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
+ } else {
+ wl1271_error("PSM entry failed, giving up.\n");
+ wl->psm_entry_retry = 0;
+ *beacon_loss = true;
+ }
+ break;
+ case EVENT_ENTER_POWER_SAVE_SUCCESS:
+ wl->psm_entry_retry = 0;
+ break;
+ case EVENT_EXIT_POWER_SAVE_FAIL:
+ wl1271_info("PSM exit failed");
+ break;
+ case EVENT_EXIT_POWER_SAVE_SUCCESS:
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
{
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
@@ -54,10 +117,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
{
int ret;
u32 vector;
+ bool beacon_loss = false;
wl1271_event_mbox_dump(mbox);
- vector = mbox->events_vector & ~(mbox->events_mask);
+ vector = le32_to_cpu(mbox->events_vector);
+ vector &= ~(le32_to_cpu(mbox->events_mask));
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
@@ -66,14 +131,34 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
return ret;
}
- if (vector & BSS_LOSE_EVENT_ID) {
+ /*
+ * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
+ * filtering) is enabled. Without PSM, the stack will receive all
+ * beacons and can detect beacon loss by itself.
+ */
+ if (vector & BSS_LOSE_EVENT_ID && wl->psm) {
wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
- if (wl->psm_requested && wl->psm) {
- ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
- if (ret < 0)
- return ret;
- }
+ /* indicate to the stack, that beacons have been lost */
+ beacon_loss = true;
+ }
+
+ if (vector & PS_REPORT_EVENT_ID) {
+ wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
+ ret = wl1271_event_ps_report(wl, mbox, &beacon_loss);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (beacon_loss) {
+ /* Obviously, it's dangerous to release the mutex while
+ we are holding many of the variables in the wl struct.
+ That's why it's done last in the function, and care must
+ be taken that nothing more is done after this function
+ returns. */
+ mutex_unlock(&wl->mutex);
+ ieee80211_beacon_loss(wl->vif);
+ mutex_lock(&wl->mutex);
}
return 0;
@@ -92,14 +177,14 @@ int wl1271_event_unmask(struct wl1271 *wl)
void wl1271_event_mbox_config(struct wl1271 *wl)
{
- wl->mbox_ptr[0] = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR);
+ wl->mbox_ptr[0] = wl1271_spi_read32(wl, REG_EVENT_MAILBOX_PTR);
wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
}
-int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
+int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack)
{
struct event_mailbox mbox;
int ret;
@@ -110,8 +195,8 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
return -EINVAL;
/* first we read the mbox descriptor */
- wl1271_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox,
- sizeof(struct event_mailbox));
+ wl1271_spi_read(wl, wl->mbox_ptr[mbox_num], &mbox,
+ sizeof(struct event_mailbox), false);
/* process the descriptor */
ret = wl1271_event_process(wl, &mbox);
@@ -119,7 +204,9 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
return ret;
/* then we let the firmware know it can go on...*/
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
+ if (do_ack)
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG,
+ INTR_TRIG_EVENT_ACK);
return 0;
}
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h
index 2cdce7c34bf..4e3f55ebb1a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.h
+++ b/drivers/net/wireless/wl12xx/wl1271_event.h
@@ -63,36 +63,43 @@ enum {
EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff,
};
+enum {
+ EVENT_ENTER_POWER_SAVE_FAIL = 0,
+ EVENT_ENTER_POWER_SAVE_SUCCESS,
+ EVENT_EXIT_POWER_SAVE_FAIL,
+ EVENT_EXIT_POWER_SAVE_SUCCESS,
+};
+
struct event_debug_report {
u8 debug_event_id;
u8 num_params;
- u16 pad;
- u32 report_1;
- u32 report_2;
- u32 report_3;
+ __le16 pad;
+ __le32 report_1;
+ __le32 report_2;
+ __le32 report_3;
} __attribute__ ((packed));
#define NUM_OF_RSSI_SNR_TRIGGERS 8
struct event_mailbox {
- u32 events_vector;
- u32 events_mask;
- u32 reserved_1;
- u32 reserved_2;
+ __le32 events_vector;
+ __le32 events_mask;
+ __le32 reserved_1;
+ __le32 reserved_2;
u8 dbg_event_id;
u8 num_relevant_params;
- u16 reserved_3;
- u32 event_report_p1;
- u32 event_report_p2;
- u32 event_report_p3;
+ __le16 reserved_3;
+ __le32 event_report_p1;
+ __le32 event_report_p2;
+ __le32 event_report_p3;
u8 number_of_scan_results;
u8 scan_tag;
u8 reserved_4[2];
- u32 compl_scheduled_scan_status;
+ __le32 compl_scheduled_scan_status;
- u16 scheduled_scan_attended_channels;
+ __le16 scheduled_scan_attended_channels;
u8 soft_gemini_sense_info;
u8 soft_gemini_protective_info;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
@@ -105,6 +112,6 @@ struct event_mailbox {
int wl1271_event_unmask(struct wl1271 *wl);
void wl1271_event_mbox_config(struct wl1271 *wl);
-int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
+int wl1271_event_handle(struct wl1271 *wl, u8 mbox, bool do_ack);
#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index 490df217605..11249b436cf 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -59,6 +59,14 @@ static int wl1271_init_templates_config(struct wl1271 *wl)
if (ret < 0)
return ret;
+ if (wl1271_11a_enabled()) {
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+ NULL,
+ sizeof(struct wl12xx_probe_req_template));
+ if (ret < 0)
+ return ret;
+ }
+
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
sizeof(struct wl12xx_null_data_template));
if (ret < 0)
@@ -94,7 +102,7 @@ static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter)
{
int ret;
- ret = wl1271_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF);
+ ret = wl1271_acx_rx_msdu_life_time(wl);
if (ret < 0)
return ret;
@@ -117,7 +125,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl)
if (ret < 0)
return ret;
- ret = wl1271_acx_group_address_tbl(wl);
+ ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
if (ret < 0)
return ret;
@@ -125,7 +133,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl)
if (ret < 0)
return ret;
- ret = wl1271_acx_rts_threshold(wl, RTS_THRESHOLD_DEF);
+ ret = wl1271_acx_rts_threshold(wl, wl->conf.rx.rts_threshold);
if (ret < 0)
return ret;
@@ -136,7 +144,8 @@ static int wl1271_init_beacon_filter(struct wl1271 *wl)
{
int ret;
- ret = wl1271_acx_beacon_filter_opt(wl);
+ /* disable beacon filtering at this stage */
+ ret = wl1271_acx_beacon_filter_opt(wl, false);
if (ret < 0)
return ret;
@@ -184,118 +193,15 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
return 0;
}
-static int wl1271_init_general_parms(struct wl1271 *wl)
-{
- struct wl1271_general_parms *gen_parms;
- int ret;
-
- gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL);
- if (!gen_parms)
- return -ENOMEM;
-
- gen_parms->id = TEST_CMD_INI_FILE_GENERAL_PARAM;
-
- gen_parms->ref_clk = REF_CLK_38_4_E;
- /* FIXME: magic numbers */
- gen_parms->settling_time = 5;
- gen_parms->clk_valid_on_wakeup = 0;
- gen_parms->dc2dcmode = 0;
- gen_parms->single_dual_band = 0;
- gen_parms->tx_bip_fem_autodetect = 1;
- gen_parms->tx_bip_fem_manufacturer = 1;
- gen_parms->settings = 1;
-
- ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0);
- if (ret < 0) {
- wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
- return ret;
- }
-
- kfree(gen_parms);
- return 0;
-}
-
-static int wl1271_init_radio_parms(struct wl1271 *wl)
-{
- /*
- * FIXME: All these magic numbers should be moved to some place where
- * they can be configured (separate file?)
- */
-
- struct wl1271_radio_parms *radio_parms;
- int ret;
- u8 compensation[] = { 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, 0xfc, 0x00,
- 0x08, 0x10, 0xf0, 0xf8, 0x00, 0x0a, 0x14 };
-
- u8 tx_rate_limits_normal[] = { 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 };
- u8 tx_rate_limits_degraded[] = { 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 };
-
- u8 tx_channel_limits_11b[] = { 0x22, 0x50, 0x50, 0x50,
- 0x50, 0x50, 0x50, 0x50,
- 0x50, 0x50, 0x22, 0x50,
- 0x22, 0x50 };
-
- u8 tx_channel_limits_ofdm[] = { 0x20, 0x50, 0x50, 0x50,
- 0x50, 0x50, 0x50, 0x50,
- 0x50, 0x50, 0x20, 0x50,
- 0x20, 0x50 };
-
- u8 tx_pdv_rate_offsets[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-
- u8 tx_ibias[] = { 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 };
-
- radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL);
- if (!radio_parms)
- return -ENOMEM;
-
- radio_parms->id = TEST_CMD_INI_FILE_RADIO_PARAM;
-
- /* Static radio parameters */
- radio_parms->rx_trace_loss = 10;
- radio_parms->tx_trace_loss = 10;
- memcpy(radio_parms->rx_rssi_and_proc_compens, compensation,
- sizeof(compensation));
-
- /* We don't set the 5GHz -- N/A */
-
- /* Dynamic radio parameters */
- radio_parms->tx_ref_pd_voltage = cpu_to_le16(0x24e);
- radio_parms->tx_ref_power = 0x78;
- radio_parms->tx_offset_db = 0x0;
-
- memcpy(radio_parms->tx_rate_limits_normal, tx_rate_limits_normal,
- sizeof(tx_rate_limits_normal));
- memcpy(radio_parms->tx_rate_limits_degraded, tx_rate_limits_degraded,
- sizeof(tx_rate_limits_degraded));
-
- memcpy(radio_parms->tx_channel_limits_11b, tx_channel_limits_11b,
- sizeof(tx_channel_limits_11b));
- memcpy(radio_parms->tx_channel_limits_ofdm, tx_channel_limits_ofdm,
- sizeof(tx_channel_limits_ofdm));
- memcpy(radio_parms->tx_pdv_rate_offsets, tx_pdv_rate_offsets,
- sizeof(tx_pdv_rate_offsets));
- memcpy(radio_parms->tx_ibias, tx_ibias,
- sizeof(tx_ibias));
-
- radio_parms->rx_fem_insertion_loss = 0x14;
-
- ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0);
- if (ret < 0)
- wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed");
-
- kfree(radio_parms);
- return ret;
-}
-
int wl1271_hw_init(struct wl1271 *wl)
{
int ret;
- ret = wl1271_init_general_parms(wl);
+ ret = wl1271_cmd_general_parms(wl);
if (ret < 0)
return ret;
- ret = wl1271_init_radio_parms(wl);
+ ret = wl1271_cmd_radio_parms(wl);
if (ret < 0)
return ret;
@@ -311,8 +217,8 @@ int wl1271_hw_init(struct wl1271 *wl)
/* RX config */
ret = wl1271_init_rx_config(wl,
- RX_CFG_PROMISCUOUS | RX_CFG_TSF,
- RX_FILTER_OPTION_DEF);
+ RX_CFG_PROMISCUOUS | RX_CFG_TSF,
+ RX_FILTER_OPTION_DEF);
/* RX_CONFIG_OPTION_ANY_DST_ANY_BSS,
RX_FILTER_OPTION_FILTER_ALL); */
if (ret < 0)
@@ -323,6 +229,11 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;
+ /* Initialize connection monitoring thresholds */
+ ret = wl1271_acx_conn_monit_params(wl);
+ if (ret < 0)
+ goto out_free_memmap;
+
/* Beacon filtering */
ret = wl1271_init_beacon_filter(wl);
if (ret < 0)
@@ -369,7 +280,7 @@ int wl1271_hw_init(struct wl1271 *wl)
goto out_free_memmap;
/* Configure TX rate classes */
- ret = wl1271_acx_rate_policies(wl);
+ ret = wl1271_acx_rate_policies(wl, CONF_TX_RATE_MASK_ALL);
if (ret < 0)
goto out_free_memmap;
@@ -388,10 +299,16 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;
+ /* Configure smart reflex */
+ ret = wl1271_acx_smart_reflex(wl);
+ if (ret < 0)
+ goto out_free_memmap;
+
return 0;
out_free_memmap:
kfree(wl->target_mem_map);
+ wl->target_mem_map = NULL;
return ret;
}
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.h b/drivers/net/wireless/wl12xx/wl1271_init.h
index bd8ff0fa227..930677fbe85 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.h
+++ b/drivers/net/wireless/wl12xx/wl1271_init.h
@@ -29,87 +29,4 @@
int wl1271_hw_init_power_auth(struct wl1271 *wl);
int wl1271_hw_init(struct wl1271 *wl);
-/* These are not really a TEST_CMD, but the ref driver uses them as such */
-#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19
-#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E
-
-struct wl1271_general_parms {
- u8 id;
- u8 padding[3];
-
- u8 ref_clk;
- u8 settling_time;
- u8 clk_valid_on_wakeup;
- u8 dc2dcmode;
- u8 single_dual_band;
-
- u8 tx_bip_fem_autodetect;
- u8 tx_bip_fem_manufacturer;
- u8 settings;
-} __attribute__ ((packed));
-
-enum ref_clk_enum {
- REF_CLK_19_2_E,
- REF_CLK_26_E,
- REF_CLK_38_4_E,
- REF_CLK_52_E
-};
-
-#define RSSI_AND_PROCESS_COMPENSATION_SIZE 15
-#define NUMBER_OF_SUB_BANDS_5 7
-#define NUMBER_OF_RATE_GROUPS 6
-#define NUMBER_OF_CHANNELS_2_4 14
-#define NUMBER_OF_CHANNELS_5 35
-
-struct wl1271_radio_parms {
- u8 id;
- u8 padding[3];
-
- /* Static radio parameters */
- /* 2.4GHz */
- u8 rx_trace_loss;
- u8 tx_trace_loss;
- s8 rx_rssi_and_proc_compens[RSSI_AND_PROCESS_COMPENSATION_SIZE];
-
- /* 5GHz */
- u8 rx_trace_loss_5[NUMBER_OF_SUB_BANDS_5];
- u8 tx_trace_loss_5[NUMBER_OF_SUB_BANDS_5];
- s8 rx_rssi_and_proc_compens_5[RSSI_AND_PROCESS_COMPENSATION_SIZE];
-
- /* Dynamic radio parameters */
- /* 2.4GHz */
- s16 tx_ref_pd_voltage;
- s8 tx_ref_power;
- s8 tx_offset_db;
-
- s8 tx_rate_limits_normal[NUMBER_OF_RATE_GROUPS];
- s8 tx_rate_limits_degraded[NUMBER_OF_RATE_GROUPS];
-
- s8 tx_channel_limits_11b[NUMBER_OF_CHANNELS_2_4];
- s8 tx_channel_limits_ofdm[NUMBER_OF_CHANNELS_2_4];
- s8 tx_pdv_rate_offsets[NUMBER_OF_RATE_GROUPS];
-
- u8 tx_ibias[NUMBER_OF_RATE_GROUPS];
- u8 rx_fem_insertion_loss;
-
- u8 padding2;
-
- /* 5GHz */
- s16 tx_ref_pd_voltage_5[NUMBER_OF_SUB_BANDS_5];
- s8 tx_ref_power_5[NUMBER_OF_SUB_BANDS_5];
- s8 tx_offset_db_5[NUMBER_OF_SUB_BANDS_5];
-
- s8 tx_rate_limits_normal_5[NUMBER_OF_RATE_GROUPS];
- s8 tx_rate_limits_degraded_5[NUMBER_OF_RATE_GROUPS];
-
- s8 tx_channel_limits_ofdm_5[NUMBER_OF_CHANNELS_5];
- s8 tx_pdv_rate_offsets_5[NUMBER_OF_RATE_GROUPS];
-
- /* FIXME: this is inconsistent with the types for 2.4GHz */
- s8 tx_ibias_5[NUMBER_OF_RATE_GROUPS];
- s8 rx_fem_insertion_loss_5[NUMBER_OF_SUB_BANDS_5];
-
- u8 padding3[2];
-} __attribute__ ((packed));
-
#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 27298b19d5b..b62c00ff42f 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -30,7 +30,9 @@
#include <linux/spi/spi.h>
#include <linux/crc32.h>
#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
#include <linux/spi/wl12xx.h>
+#include <linux/inetdevice.h>
#include "wl1271.h"
#include "wl12xx_80211.h"
@@ -45,10 +47,314 @@
#include "wl1271_cmd.h"
#include "wl1271_boot.h"
+static struct conf_drv_settings default_conf = {
+ .sg = {
+ .per_threshold = 7500,
+ .max_scan_compensation_time = 120000,
+ .nfs_sample_interval = 400,
+ .load_ratio = 50,
+ .auto_ps_mode = 0,
+ .probe_req_compensation = 170,
+ .scan_window_compensation = 50,
+ .antenna_config = 0,
+ .beacon_miss_threshold = 60,
+ .rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS,
+ .rate_adaptation_snr = 0
+ },
+ .rx = {
+ .rx_msdu_life_time = 512000,
+ .packet_detection_threshold = 0,
+ .ps_poll_timeout = 15,
+ .upsd_timeout = 15,
+ .rts_threshold = 2347,
+ .rx_cca_threshold = 0xFFEF,
+ .irq_blk_threshold = 0,
+ .irq_pkt_threshold = USHORT_MAX,
+ .irq_timeout = 5,
+ .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
+ },
+ .tx = {
+ .tx_energy_detection = 0,
+ .rc_conf = {
+ .enabled_rates = CONF_TX_RATE_MASK_UNSPECIFIED,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0
+ },
+ .ac_conf_count = 4,
+ .ac_conf = {
+ [0] = {
+ .ac = CONF_TX_AC_BE,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = 3,
+ .tx_op_limit = 0,
+ },
+ [1] = {
+ .ac = CONF_TX_AC_BK,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = 7,
+ .tx_op_limit = 0,
+ },
+ [2] = {
+ .ac = CONF_TX_AC_VI,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = CONF_TX_AIFS_PIFS,
+ .tx_op_limit = 3008,
+ },
+ [3] = {
+ .ac = CONF_TX_AC_VO,
+ .cw_min = 15,
+ .cw_max = 63,
+ .aifsn = CONF_TX_AIFS_PIFS,
+ .tx_op_limit = 1504,
+ },
+ },
+ .tid_conf_count = 7,
+ .tid_conf = {
+ [0] = {
+ .queue_id = 0,
+ .channel_type = CONF_CHANNEL_TYPE_DCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [1] = {
+ .queue_id = 1,
+ .channel_type = CONF_CHANNEL_TYPE_DCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [2] = {
+ .queue_id = 2,
+ .channel_type = CONF_CHANNEL_TYPE_DCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [3] = {
+ .queue_id = 3,
+ .channel_type = CONF_CHANNEL_TYPE_DCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [4] = {
+ .queue_id = 4,
+ .channel_type = CONF_CHANNEL_TYPE_DCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [5] = {
+ .queue_id = 5,
+ .channel_type = CONF_CHANNEL_TYPE_DCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ },
+ [6] = {
+ .queue_id = 6,
+ .channel_type = CONF_CHANNEL_TYPE_DCF,
+ .tsid = CONF_TX_AC_BE,
+ .ps_scheme = CONF_PS_SCHEME_LEGACY,
+ .ack_policy = CONF_ACK_POLICY_LEGACY,
+ .apsd_conf = {0, 0},
+ }
+ },
+ .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
+ .tx_compl_timeout = 5,
+ .tx_compl_threshold = 5
+ },
+ .conn = {
+ .wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
+ .listen_interval = 0,
+ .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
+ .bcn_filt_ie_count = 1,
+ .bcn_filt_ie = {
+ [0] = {
+ .ie = WLAN_EID_CHANNEL_SWITCH,
+ .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE,
+ }
+ },
+ .synch_fail_thold = 5,
+ .bss_lose_timeout = 100,
+ .beacon_rx_timeout = 10000,
+ .broadcast_timeout = 20000,
+ .rx_broadcast_in_ps = 1,
+ .ps_poll_threshold = 4,
+ .sig_trigger_count = 2,
+ .sig_trigger = {
+ [0] = {
+ .threshold = -75,
+ .pacing = 500,
+ .metric = CONF_TRIG_METRIC_RSSI_BEACON,
+ .type = CONF_TRIG_EVENT_TYPE_EDGE,
+ .direction = CONF_TRIG_EVENT_DIR_LOW,
+ .hysteresis = 2,
+ .index = 0,
+ .enable = 1
+ },
+ [1] = {
+ .threshold = -75,
+ .pacing = 500,
+ .metric = CONF_TRIG_METRIC_RSSI_BEACON,
+ .type = CONF_TRIG_EVENT_TYPE_EDGE,
+ .direction = CONF_TRIG_EVENT_DIR_HIGH,
+ .hysteresis = 2,
+ .index = 1,
+ .enable = 1
+ }
+ },
+ .sig_weights = {
+ .rssi_bcn_avg_weight = 10,
+ .rssi_pkt_avg_weight = 10,
+ .snr_bcn_avg_weight = 10,
+ .snr_pkt_avg_weight = 10
+ },
+ .bet_enable = CONF_BET_MODE_ENABLE,
+ .bet_max_consecutive = 10,
+ .psm_entry_retries = 3
+ },
+ .init = {
+ .sr_err_tbl = {
+ [0] = {
+ .len = 7,
+ .upper_limit = 0x03,
+ .values = {
+ 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8,
+ 0x00 }
+ },
+ [1] = {
+ .len = 7,
+ .upper_limit = 0x03,
+ .values = {
+ 0x18, 0x10, 0x05, 0xf6, 0xf0, 0xe8,
+ 0x00 }
+ },
+ [2] = {
+ .len = 7,
+ .upper_limit = 0x03,
+ .values = {
+ 0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8,
+ 0x00 }
+ }
+ },
+ .sr_enable = 1,
+ .genparam = {
+ .ref_clk = CONF_REF_CLK_38_4_E,
+ .settling_time = 5,
+ .clk_valid_on_wakeup = 0,
+ .dc2dcmode = 0,
+ .single_dual_band = CONF_SINGLE_BAND,
+ .tx_bip_fem_autodetect = 0,
+ .tx_bip_fem_manufacturer = 1,
+ .settings = 1,
+ },
+ .radioparam = {
+ .rx_trace_loss = 10,
+ .tx_trace_loss = 10,
+ .rx_rssi_and_proc_compens = {
+ 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8,
+ 0xfc, 0x00, 0x08, 0x10, 0xf0, 0xf8,
+ 0x00, 0x0a, 0x14 },
+ .rx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 },
+ .tx_trace_loss_5 = { 0, 0, 0, 0, 0, 0, 0 },
+ .rx_rssi_and_proc_compens_5 = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00 },
+ .tx_ref_pd_voltage = 0x24e,
+ .tx_ref_power = 0x78,
+ .tx_offset_db = 0x0,
+ .tx_rate_limits_normal = {
+ 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 },
+ .tx_rate_limits_degraded = {
+ 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 },
+ .tx_channel_limits_11b = {
+ 0x22, 0x50, 0x50, 0x50, 0x50, 0x50,
+ 0x50, 0x50, 0x50, 0x50, 0x22, 0x50,
+ 0x22, 0x50 },
+ .tx_channel_limits_ofdm = {
+ 0x20, 0x50, 0x50, 0x50, 0x50, 0x50,
+ 0x50, 0x50, 0x50, 0x50, 0x20, 0x50,
+ 0x20, 0x50 },
+ .tx_pdv_rate_offsets = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ .tx_ibias = {
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 },
+ .rx_fem_insertion_loss = 0x14,
+ .tx_ref_pd_voltage_5 = {
+ 0x0190, 0x01a4, 0x01c3, 0x01d8,
+ 0x020a, 0x021c },
+ .tx_ref_power_5 = {
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 },
+ .tx_offset_db_5 = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ .tx_rate_limits_normal_5 = {
+ 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
+ .tx_rate_limits_degraded_5 = {
+ 0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
+ .tx_channel_limits_ofdm_5 = {
+ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
+ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
+ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
+ 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
+ 0x50, 0x50, 0x50 },
+ .tx_pdv_rate_offsets_5 = {
+ 0x01, 0x02, 0x02, 0x02, 0x02, 0x00 },
+ .tx_ibias_5 = {
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
+ .rx_fem_insertion_loss_5 = {
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }
+ }
+ }
+};
+
+static LIST_HEAD(wl_list);
+
+static void wl1271_conf_init(struct wl1271 *wl)
+{
+
+ /*
+ * This function applies the default configuration to the driver. This
+ * function is invoked upon driver load (spi probe.)
+ *
+ * The configuration is stored in a run-time structure in order to
+ * facilitate for run-time adjustment of any of the parameters. Making
+ * changes to the configuration structure will apply the new values on
+ * the next interface up (wl1271_op_start.)
+ */
+
+ /* apply driver default configuration */
+ memcpy(&wl->conf, &default_conf, sizeof(default_conf));
+
+ if (wl1271_11a_enabled())
+ wl->conf.init.genparam.single_dual_band = CONF_DUAL_BAND;
+}
+
+
static int wl1271_plt_init(struct wl1271 *wl)
{
int ret;
+ ret = wl1271_cmd_general_parms(wl);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_cmd_radio_parms(wl);
+ if (ret < 0)
+ return ret;
+
ret = wl1271_acx_init_mem_config(wl);
if (ret < 0)
return ret;
@@ -75,20 +381,14 @@ static void wl1271_power_on(struct wl1271 *wl)
wl->set_power(true);
}
-static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status)
+static void wl1271_fw_status(struct wl1271 *wl,
+ struct wl1271_fw_status *status)
{
u32 total = 0;
int i;
- /*
- * FIXME: Reading the FW status directly from the registers seems to
- * be the right thing to do, but it doesn't work. And in the
- * reference driver, there is a workaround called
- * USE_SDIO_24M_WORKAROUND, which reads the status from memory
- * instead, so we do the same here.
- */
-
- wl1271_spi_mem_read(wl, STATUS_MEM_ADDRESS, status, sizeof(*status));
+ wl1271_spi_read(wl, FW_STATUS_ADDR, status,
+ sizeof(*status), false);
wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
"drv_rx_counter = %d, tx_results_counter = %d)",
@@ -99,25 +399,28 @@ static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status)
/* update number of available TX blocks */
for (i = 0; i < NUM_TX_QUEUES; i++) {
- u32 cnt = status->tx_released_blks[i] - wl->tx_blocks_freed[i];
- wl->tx_blocks_freed[i] = status->tx_released_blks[i];
+ u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
+ wl->tx_blocks_freed[i];
+
+ wl->tx_blocks_freed[i] =
+ le32_to_cpu(status->tx_released_blks[i]);
wl->tx_blocks_available += cnt;
total += cnt;
}
/* if more blocks are available now, schedule some tx work */
if (total && !skb_queue_empty(&wl->tx_queue))
- schedule_work(&wl->tx_work);
+ ieee80211_queue_work(wl->hw, &wl->tx_work);
/* update the host-chipset time offset */
- wl->time_offset = jiffies_to_usecs(jiffies) - status->fw_localtime;
+ wl->time_offset = jiffies_to_usecs(jiffies) -
+ le32_to_cpu(status->fw_localtime);
}
-#define WL1271_IRQ_MAX_LOOPS 10
static void wl1271_irq_work(struct work_struct *work)
{
- u32 intr, ctr = WL1271_IRQ_MAX_LOOPS;
int ret;
+ u32 intr;
struct wl1271 *wl =
container_of(work, struct wl1271, irq_work);
@@ -132,9 +435,10 @@ static void wl1271_irq_work(struct work_struct *work)
if (ret < 0)
goto out;
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
- intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
+ wl1271_fw_status(wl, wl->fw_status);
+ intr = le32_to_cpu(wl->fw_status->intr);
if (!intr) {
wl1271_debug(DEBUG_IRQ, "Zero interrupt received.");
goto out_sleep;
@@ -142,46 +446,39 @@ static void wl1271_irq_work(struct work_struct *work)
intr &= WL1271_INTR_MASK;
- do {
- wl1271_fw_status(wl, wl->fw_status);
-
-
- if (intr & (WL1271_ACX_INTR_EVENT_A |
- WL1271_ACX_INTR_EVENT_B)) {
- wl1271_debug(DEBUG_IRQ,
- "WL1271_ACX_INTR_EVENT (0x%x)", intr);
- if (intr & WL1271_ACX_INTR_EVENT_A)
- wl1271_event_handle(wl, 0);
- else
- wl1271_event_handle(wl, 1);
- }
+ if (intr & WL1271_ACX_INTR_EVENT_A) {
+ bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true;
+ wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
+ wl1271_event_handle(wl, 0, do_ack);
+ }
- if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
- wl1271_debug(DEBUG_IRQ,
- "WL1271_ACX_INTR_INIT_COMPLETE");
+ if (intr & WL1271_ACX_INTR_EVENT_B) {
+ wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
+ wl1271_event_handle(wl, 1, true);
+ }
- if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
- wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
+ if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
+ wl1271_debug(DEBUG_IRQ,
+ "WL1271_ACX_INTR_INIT_COMPLETE");
- if (intr & WL1271_ACX_INTR_DATA) {
- u8 tx_res_cnt = wl->fw_status->tx_results_counter -
- wl->tx_results_count;
+ if (intr & WL1271_ACX_INTR_HW_AVAILABLE)
+ wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE");
- wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
+ if (intr & WL1271_ACX_INTR_DATA) {
+ u8 tx_res_cnt = wl->fw_status->tx_results_counter -
+ wl->tx_results_count;
- /* check for tx results */
- if (tx_res_cnt)
- wl1271_tx_complete(wl, tx_res_cnt);
+ wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
- wl1271_rx(wl, wl->fw_status);
- }
+ /* check for tx results */
+ if (tx_res_cnt)
+ wl1271_tx_complete(wl, tx_res_cnt);
- intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
- intr &= WL1271_INTR_MASK;
- } while (intr && --ctr);
+ wl1271_rx(wl, wl->fw_status);
+ }
out_sleep:
- wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK,
+ wl1271_spi_write32(wl, ACX_REG_INTERRUPT_MASK,
WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
wl1271_ps_elp_sleep(wl);
@@ -205,7 +502,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
wl->elp_compl = NULL;
}
- schedule_work(&wl->irq_work);
+ ieee80211_queue_work(wl->hw, &wl->irq_work);
spin_unlock_irqrestore(&wl->wl_lock, flags);
return IRQ_HANDLED;
@@ -231,7 +528,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
}
wl->fw_len = fw->size;
- wl->fw = kmalloc(wl->fw_len, GFP_KERNEL);
+ wl->fw = vmalloc(wl->fw_len);
if (!wl->fw) {
wl1271_error("could not allocate memory for the firmware");
@@ -292,7 +589,7 @@ static void wl1271_fw_wakeup(struct wl1271 *wl)
u32 elp_reg;
elp_reg = ELPCTRL_WAKE_UP;
- wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
+ wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
}
static int wl1271_setup(struct wl1271 *wl)
@@ -314,6 +611,7 @@ static int wl1271_setup(struct wl1271 *wl)
static int wl1271_chip_wakeup(struct wl1271 *wl)
{
+ struct wl1271_partition_set partition;
int ret = 0;
wl1271_power_on(wl);
@@ -323,11 +621,10 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
/* We don't need a real memory partition here, because we only want
* to use the registers at this point. */
- wl1271_set_partition(wl,
- 0x00000000,
- 0x00000000,
- REGISTERS_BASE,
- REGISTERS_DOWN_SIZE);
+ memset(&partition, 0, sizeof(partition));
+ partition.reg.start = REGISTERS_BASE;
+ partition.reg.size = REGISTERS_DOWN_SIZE;
+ wl1271_set_partition(wl, &partition);
/* ELP module wake up */
wl1271_fw_wakeup(wl);
@@ -335,7 +632,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
/* whal_FwCtrl_BootSm() */
/* 0. read chip id from CHIP_ID */
- wl->chip.id = wl1271_reg_read32(wl, CHIP_ID_B);
+ wl->chip.id = wl1271_spi_read32(wl, CHIP_ID_B);
/* 1. check if chip id is valid */
@@ -346,7 +643,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
ret = wl1271_setup(wl);
if (ret < 0)
- goto out;
+ goto out_power_off;
break;
case CHIP_ID_1271_PG20:
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
@@ -354,56 +651,34 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
ret = wl1271_setup(wl);
if (ret < 0)
- goto out;
+ goto out_power_off;
break;
default:
wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
ret = -ENODEV;
- goto out;
+ goto out_power_off;
}
if (wl->fw == NULL) {
ret = wl1271_fetch_firmware(wl);
if (ret < 0)
- goto out;
+ goto out_power_off;
}
/* No NVS from netlink, try to get it from the filesystem */
if (wl->nvs == NULL) {
ret = wl1271_fetch_nvs(wl);
if (ret < 0)
- goto out;
+ goto out_power_off;
}
-out:
- return ret;
-}
-
-static void wl1271_filter_work(struct work_struct *work)
-{
- struct wl1271 *wl =
- container_of(work, struct wl1271, filter_work);
- int ret;
-
- mutex_lock(&wl->mutex);
-
- if (wl->state == WL1271_STATE_OFF)
- goto out;
-
- ret = wl1271_ps_elp_wakeup(wl, false);
- if (ret < 0)
- goto out;
-
- /* FIXME: replace the magic numbers with proper definitions */
- ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0);
- if (ret < 0)
- goto out_sleep;
+ goto out;
-out_sleep:
- wl1271_ps_elp_sleep(wl);
+out_power_off:
+ wl1271_power_off(wl);
out:
- mutex_unlock(&wl->mutex);
+ return ret;
}
int wl1271_plt_start(struct wl1271 *wl)
@@ -429,13 +704,26 @@ int wl1271_plt_start(struct wl1271 *wl)
ret = wl1271_boot(wl);
if (ret < 0)
- goto out;
+ goto out_power_off;
wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver);
ret = wl1271_plt_init(wl);
if (ret < 0)
- goto out;
+ goto out_irq_disable;
+
+ /* Make sure power saving is disabled */
+ ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+ if (ret < 0)
+ goto out_irq_disable;
+
+ goto out;
+
+out_irq_disable:
+ wl1271_disable_interrupts(wl);
+
+out_power_off:
+ wl1271_power_off(wl);
out:
mutex_unlock(&wl->mutex);
@@ -462,6 +750,7 @@ int wl1271_plt_stop(struct wl1271 *wl)
wl1271_power_off(wl);
wl->state = WL1271_STATE_OFF;
+ wl->rx_counter = 0;
out:
mutex_unlock(&wl->mutex);
@@ -481,7 +770,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* before that, the tx_work will not be initialized!
*/
- schedule_work(&wl->tx_work);
+ ieee80211_queue_work(wl->hw, &wl->tx_work);
/*
* The workqueue is slow to process the tx_queue and we need stop
@@ -501,6 +790,93 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return NETDEV_TX_OK;
}
+static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
+ void *arg)
+{
+ struct net_device *dev;
+ struct wireless_dev *wdev;
+ struct wiphy *wiphy;
+ struct ieee80211_hw *hw;
+ struct wl1271 *wl;
+ struct wl1271 *wl_temp;
+ struct in_device *idev;
+ struct in_ifaddr *ifa = arg;
+ int ret = 0;
+
+ /* FIXME: this ugly function should probably be implemented in the
+ * mac80211, and here should only be a simple callback handling actual
+ * setting of the filters. Now we need to dig up references to
+ * various structures to gain access to what we need.
+ * Also, because of this, there is no "initial" setting of the filter
+ * in "op_start", because we don't want to dig up struct net_device
+ * there - the filter will be set upon first change of the interface
+ * IP address. */
+
+ dev = ifa->ifa_dev->dev;
+
+ wdev = dev->ieee80211_ptr;
+ if (wdev == NULL)
+ return NOTIFY_DONE;
+
+ wiphy = wdev->wiphy;
+ if (wiphy == NULL)
+ return NOTIFY_DONE;
+
+ hw = wiphy_priv(wiphy);
+ if (hw == NULL)
+ return NOTIFY_DONE;
+
+ /* Check that the interface is one supported by this driver. */
+ wl_temp = hw->priv;
+ list_for_each_entry(wl, &wl_list, list) {
+ if (wl == wl_temp)
+ break;
+ }
+ if (wl == NULL)
+ return NOTIFY_DONE;
+
+ /* Get the interface IP address for the device. "ifa" will become
+ NULL if:
+ - there is no IPV4 protocol address configured
+ - there are multiple (virtual) IPV4 addresses configured
+ When "ifa" is NULL, filtering will be disabled.
+ */
+ ifa = NULL;
+ idev = dev->ip_ptr;
+ if (idev)
+ ifa = idev->ifa_list;
+
+ if (ifa && ifa->ifa_next)
+ ifa = NULL;
+
+ mutex_lock(&wl->mutex);
+
+ if (wl->state == WL1271_STATE_OFF)
+ goto out;
+
+ ret = wl1271_ps_elp_wakeup(wl, false);
+ if (ret < 0)
+ goto out;
+ if (ifa)
+ ret = wl1271_acx_arp_ip_filter(wl, true,
+ (u8 *)&ifa->ifa_address,
+ ACX_IPV4_VERSION);
+ else
+ ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
+ ACX_IPV4_VERSION);
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block wl1271_dev_notifier = {
+ .notifier_call = wl1271_dev_notify,
+};
+
+
static int wl1271_op_start(struct ieee80211_hw *hw)
{
struct wl1271 *wl = hw->priv;
@@ -523,22 +899,32 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
ret = wl1271_boot(wl);
if (ret < 0)
- goto out;
+ goto out_power_off;
ret = wl1271_hw_init(wl);
if (ret < 0)
- goto out;
+ goto out_irq_disable;
wl->state = WL1271_STATE_ON;
wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
-out:
- if (ret < 0)
- wl1271_power_off(wl);
+ goto out;
+
+out_irq_disable:
+ wl1271_disable_interrupts(wl);
+
+out_power_off:
+ wl1271_power_off(wl);
+out:
mutex_unlock(&wl->mutex);
+ if (!ret) {
+ list_add(&wl->list, &wl_list);
+ register_inetaddr_notifier(&wl1271_dev_notifier);
+ }
+
return ret;
}
@@ -551,6 +937,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
+ unregister_inetaddr_notifier(&wl1271_dev_notifier);
+ list_del(&wl->list);
+
mutex_lock(&wl->mutex);
WARN_ON(wl->state != WL1271_STATE_ON);
@@ -570,7 +959,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
cancel_work_sync(&wl->irq_work);
cancel_work_sync(&wl->tx_work);
- cancel_work_sync(&wl->filter_work);
mutex_lock(&wl->mutex);
@@ -581,19 +969,25 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
memset(wl->bssid, 0, ETH_ALEN);
memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
wl->ssid_len = 0;
- wl->listen_int = 1;
wl->bss_type = MAX_BSS_TYPE;
+ wl->band = IEEE80211_BAND_2GHZ;
wl->rx_counter = 0;
wl->elp = false;
wl->psm = 0;
+ wl->psm_entry_retry = 0;
wl->tx_queue_stopped = false;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->tx_blocks_available = 0;
wl->tx_results_count = 0;
wl->tx_packets_count = 0;
+ wl->tx_security_last_seq = 0;
+ wl->tx_security_seq_16 = 0;
+ wl->tx_security_seq_32 = 0;
wl->time_offset = 0;
wl->session_counter = 0;
+ wl->joined = false;
+
for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_blocks_freed[i] = 0;
@@ -611,6 +1005,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
conf->type, conf->mac_addr);
mutex_lock(&wl->mutex);
+ if (wl->vif) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ wl->vif = conf->vif;
switch (conf->type) {
case NL80211_IFTYPE_STATION:
@@ -634,7 +1034,12 @@ out:
static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
+ struct wl1271 *wl = hw->priv;
+
+ mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
+ wl->vif = NULL;
+ mutex_unlock(&wl->mutex);
}
#if 0
@@ -657,23 +1062,24 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
- memcpy(wl->bssid, conf->bssid, ETH_ALEN);
+ if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) {
+ wl1271_debug(DEBUG_MAC80211, "bssid changed");
- ret = wl1271_cmd_build_null_data(wl);
- if (ret < 0)
- goto out_sleep;
+ memcpy(wl->bssid, conf->bssid, ETH_ALEN);
- wl->ssid_len = conf->ssid_len;
- if (wl->ssid_len)
- memcpy(wl->ssid, conf->ssid, wl->ssid_len);
+ ret = wl1271_cmd_join(wl);
+ if (ret < 0)
+ goto out_sleep;
- if (wl->bss_type != BSS_TYPE_IBSS) {
- /* FIXME: replace the magic numbers with proper definitions */
- ret = wl1271_cmd_join(wl, wl->bss_type, 5, 100, 1);
+ ret = wl1271_cmd_build_null_data(wl);
if (ret < 0)
goto out_sleep;
}
+ wl->ssid_len = conf->ssid_len;
+ if (wl->ssid_len)
+ memcpy(wl->ssid, conf->ssid, wl->ssid_len);
+
if (conf->changed & IEEE80211_IFCC_BEACON) {
beacon = ieee80211_beacon_get(hw, vif);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
@@ -691,12 +1097,6 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw,
if (ret < 0)
goto out_sleep;
-
- /* FIXME: replace the magic numbers with proper definitions */
- ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0);
-
- if (ret < 0)
- goto out_sleep;
}
out_sleep:
@@ -724,26 +1124,22 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock(&wl->mutex);
+ wl->band = conf->channel->band;
+
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
if (channel != wl->channel) {
- u8 old_channel = wl->channel;
+ /*
+ * We assume that the stack will configure the right channel
+ * before associating, so we don't need to send a join
+ * command here. We will join the right channel when the
+ * BSSID changes
+ */
wl->channel = channel;
-
- /* FIXME: use beacon interval provided by mac80211 */
- ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0);
- if (ret < 0) {
- wl->channel = old_channel;
- goto out_sleep;
- }
}
- ret = wl1271_cmd_build_null_data(wl);
- if (ret < 0)
- goto out_sleep;
-
if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
wl1271_info("psm enabled");
@@ -768,7 +1164,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
if (conf->power_level != wl->power_level) {
ret = wl1271_acx_tx_power(wl, conf->power_level);
if (ret < 0)
- goto out;
+ goto out_sleep;
wl->power_level = conf->power_level;
}
@@ -782,6 +1178,45 @@ out:
return ret;
}
+struct wl1271_filter_params {
+ bool enabled;
+ int mc_list_length;
+ u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
+};
+
+static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
+ struct dev_addr_list *mc_list)
+{
+ struct wl1271_filter_params *fp;
+ int i;
+
+ fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
+ if (!fp) {
+ wl1271_error("Out of memory setting filters.");
+ return 0;
+ }
+
+ /* update multicast filtering parameters */
+ fp->enabled = true;
+ if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
+ mc_count = 0;
+ fp->enabled = false;
+ }
+
+ fp->mc_list_length = 0;
+ for (i = 0; i < mc_count; i++) {
+ if (mc_list->da_addrlen == ETH_ALEN) {
+ memcpy(fp->mc_list[fp->mc_list_length],
+ mc_list->da_addr, ETH_ALEN);
+ fp->mc_list_length++;
+ } else
+ wl1271_warning("Unknown mc address length.");
+ mc_list = mc_list->next;
+ }
+
+ return (u64)(unsigned long)fp;
+}
+
#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_FCSFAIL | \
@@ -791,28 +1226,53 @@ out:
static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
unsigned int changed,
- unsigned int *total,u64 multicast)
+ unsigned int *total, u64 multicast)
{
+ struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
struct wl1271 *wl = hw->priv;
+ int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
+ mutex_lock(&wl->mutex);
+
+ if (wl->state == WL1271_STATE_OFF)
+ goto out;
+
+ ret = wl1271_ps_elp_wakeup(wl, false);
+ if (ret < 0)
+ goto out;
+
*total &= WL1271_SUPPORTED_FILTERS;
changed &= WL1271_SUPPORTED_FILTERS;
+ if (*total & FIF_ALLMULTI)
+ ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
+ else if (fp)
+ ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
+ fp->mc_list,
+ fp->mc_list_length);
+ if (ret < 0)
+ goto out_sleep;
+
+ kfree(fp);
+
+ /* FIXME: We still need to set our filters properly */
+
+ /* determine, whether supported filter values have changed */
if (changed == 0)
- return;
+ goto out_sleep;
- /* FIXME: wl->rx_config and wl->rx_filter are not protected */
- wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
- wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
+ /* apply configured filters */
+ ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
+ if (ret < 0)
+ goto out_sleep;
- /*
- * FIXME: workqueues need to be properly cancelled on stop(), for
- * now let's just disable changing the filter settings. They will
- * be updated any on config().
- */
- /* schedule_work(&wl->filter_work); */
+out_sleep:
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
}
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -823,6 +1283,8 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct wl1271 *wl = hw->priv;
const u8 *addr;
int ret;
+ u32 tx_seq_32 = 0;
+ u16 tx_seq_16 = 0;
u8 key_type;
static const u8 bcast_addr[ETH_ALEN] =
@@ -861,11 +1323,15 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
key_type = KEY_TKIP;
key_conf->hw_key_idx = key_conf->keyidx;
+ tx_seq_32 = wl->tx_security_seq_32;
+ tx_seq_16 = wl->tx_security_seq_16;
break;
case ALG_CCMP:
key_type = KEY_AES;
key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ tx_seq_32 = wl->tx_security_seq_32;
+ tx_seq_16 = wl->tx_security_seq_16;
break;
default:
wl1271_error("Unknown key algo 0x%x", key_conf->alg);
@@ -879,7 +1345,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
- addr);
+ addr, tx_seq_32, tx_seq_16);
if (ret < 0) {
wl1271_error("Could not add or replace key");
goto out_sleep;
@@ -890,7 +1356,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
- addr);
+ addr, 0, 0);
if (ret < 0) {
wl1271_error("Could not remove key");
goto out_sleep;
@@ -921,13 +1387,13 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
struct wl1271 *wl = hw->priv;
int ret;
u8 *ssid = NULL;
- size_t ssid_len = 0;
+ size_t len = 0;
wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
if (req->n_ssids) {
ssid = req->ssids[0].ssid;
- ssid_len = req->ssids[0].ssid_len;
+ len = req->ssids[0].ssid_len;
}
mutex_lock(&wl->mutex);
@@ -936,7 +1402,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
- ret = wl1271_cmd_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3);
+ if (wl1271_11a_enabled())
+ ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
+ WL1271_SCAN_BAND_DUAL, 3);
+ else
+ ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
+ WL1271_SCAN_BAND_2_4_GHZ, 3);
wl1271_ps_elp_sleep(wl);
@@ -969,6 +1440,22 @@ out:
return ret;
}
+static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set)
+{
+ struct ieee80211_supported_band *band;
+ u32 enabled_rates = 0;
+ int bit;
+
+ band = wl->hw->wiphy->bands[wl->band];
+ for (bit = 0; bit < band->n_bitrates; bit++) {
+ if (basic_rate_set & 0x1)
+ enabled_rates |= band->bitrates[bit].hw_value;
+ basic_rate_set >>= 1;
+ }
+
+ return enabled_rates;
+}
+
static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@@ -990,6 +1477,12 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (bss_conf->assoc) {
wl->aid = bss_conf->aid;
+ /*
+ * with wl1271, we don't need to update the
+ * beacon_int and dtim_period, because the firmware
+ * updates it by itself when the first beacon is
+ * received after a join.
+ */
ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
if (ret < 0)
goto out_sleep;
@@ -1005,8 +1498,14 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (ret < 0)
goto out_sleep;
}
+ } else {
+ /* use defaults when not associated */
+ wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
+ wl->aid = 0;
}
+
}
+
if (changed & BSS_CHANGED_ERP_SLOT) {
if (bss_conf->use_short_slot)
ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
@@ -1036,6 +1535,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
}
}
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ wl->basic_rate_set = wl1271_enabled_rates_get(
+ wl, bss_conf->basic_rates);
+
+ ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set);
+ if (ret < 0) {
+ wl1271_warning("Set rate policies failed %d", ret);
+ goto out_sleep;
+ }
+ }
+
out_sleep:
wl1271_ps_elp_sleep(wl);
@@ -1047,44 +1557,44 @@ out:
/* can't be const, mac80211 writes to this */
static struct ieee80211_rate wl1271_rates[] = {
{ .bitrate = 10,
- .hw_value = 0x1,
- .hw_value_short = 0x1, },
+ .hw_value = CONF_HW_BIT_RATE_1MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_1MBPS, },
{ .bitrate = 20,
- .hw_value = 0x2,
- .hw_value_short = 0x2,
+ .hw_value = CONF_HW_BIT_RATE_2MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_2MBPS,
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 55,
- .hw_value = 0x4,
- .hw_value_short = 0x4,
+ .hw_value = CONF_HW_BIT_RATE_5_5MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS,
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 110,
- .hw_value = 0x20,
- .hw_value_short = 0x20,
+ .hw_value = CONF_HW_BIT_RATE_11MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_11MBPS,
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60,
- .hw_value = 0x8,
- .hw_value_short = 0x8, },
+ .hw_value = CONF_HW_BIT_RATE_6MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
{ .bitrate = 90,
- .hw_value = 0x10,
- .hw_value_short = 0x10, },
+ .hw_value = CONF_HW_BIT_RATE_9MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
{ .bitrate = 120,
- .hw_value = 0x40,
- .hw_value_short = 0x40, },
+ .hw_value = CONF_HW_BIT_RATE_12MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
{ .bitrate = 180,
- .hw_value = 0x80,
- .hw_value_short = 0x80, },
+ .hw_value = CONF_HW_BIT_RATE_18MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
{ .bitrate = 240,
- .hw_value = 0x200,
- .hw_value_short = 0x200, },
+ .hw_value = CONF_HW_BIT_RATE_24MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
{ .bitrate = 360,
- .hw_value = 0x400,
- .hw_value_short = 0x400, },
+ .hw_value = CONF_HW_BIT_RATE_36MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
{ .bitrate = 480,
- .hw_value = 0x800,
- .hw_value_short = 0x800, },
+ .hw_value = CONF_HW_BIT_RATE_48MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
{ .bitrate = 540,
- .hw_value = 0x1000,
- .hw_value_short = 0x1000, },
+ .hw_value = CONF_HW_BIT_RATE_54MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
};
/* can't be const, mac80211 writes to this */
@@ -1112,6 +1622,88 @@ static struct ieee80211_supported_band wl1271_band_2ghz = {
.n_bitrates = ARRAY_SIZE(wl1271_rates),
};
+/* 5 GHz data rates for WL1273 */
+static struct ieee80211_rate wl1271_rates_5ghz[] = {
+ { .bitrate = 60,
+ .hw_value = CONF_HW_BIT_RATE_6MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_6MBPS, },
+ { .bitrate = 90,
+ .hw_value = CONF_HW_BIT_RATE_9MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_9MBPS, },
+ { .bitrate = 120,
+ .hw_value = CONF_HW_BIT_RATE_12MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_12MBPS, },
+ { .bitrate = 180,
+ .hw_value = CONF_HW_BIT_RATE_18MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_18MBPS, },
+ { .bitrate = 240,
+ .hw_value = CONF_HW_BIT_RATE_24MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_24MBPS, },
+ { .bitrate = 360,
+ .hw_value = CONF_HW_BIT_RATE_36MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_36MBPS, },
+ { .bitrate = 480,
+ .hw_value = CONF_HW_BIT_RATE_48MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_48MBPS, },
+ { .bitrate = 540,
+ .hw_value = CONF_HW_BIT_RATE_54MBPS,
+ .hw_value_short = CONF_HW_BIT_RATE_54MBPS, },
+};
+
+/* 5 GHz band channels for WL1273 */
+static struct ieee80211_channel wl1271_channels_5ghz[] = {
+ { .hw_value = 183, .center_freq = 4915},
+ { .hw_value = 184, .center_freq = 4920},
+ { .hw_value = 185, .center_freq = 4925},
+ { .hw_value = 187, .center_freq = 4935},
+ { .hw_value = 188, .center_freq = 4940},
+ { .hw_value = 189, .center_freq = 4945},
+ { .hw_value = 192, .center_freq = 4960},
+ { .hw_value = 196, .center_freq = 4980},
+ { .hw_value = 7, .center_freq = 5035},
+ { .hw_value = 8, .center_freq = 5040},
+ { .hw_value = 9, .center_freq = 5045},
+ { .hw_value = 11, .center_freq = 5055},
+ { .hw_value = 12, .center_freq = 5060},
+ { .hw_value = 16, .center_freq = 5080},
+ { .hw_value = 34, .center_freq = 5170},
+ { .hw_value = 36, .center_freq = 5180},
+ { .hw_value = 38, .center_freq = 5190},
+ { .hw_value = 40, .center_freq = 5200},
+ { .hw_value = 42, .center_freq = 5210},
+ { .hw_value = 44, .center_freq = 5220},
+ { .hw_value = 46, .center_freq = 5230},
+ { .hw_value = 48, .center_freq = 5240},
+ { .hw_value = 52, .center_freq = 5260},
+ { .hw_value = 56, .center_freq = 5280},
+ { .hw_value = 60, .center_freq = 5300},
+ { .hw_value = 64, .center_freq = 5320},
+ { .hw_value = 100, .center_freq = 5500},
+ { .hw_value = 104, .center_freq = 5520},
+ { .hw_value = 108, .center_freq = 5540},
+ { .hw_value = 112, .center_freq = 5560},
+ { .hw_value = 116, .center_freq = 5580},
+ { .hw_value = 120, .center_freq = 5600},
+ { .hw_value = 124, .center_freq = 5620},
+ { .hw_value = 128, .center_freq = 5640},
+ { .hw_value = 132, .center_freq = 5660},
+ { .hw_value = 136, .center_freq = 5680},
+ { .hw_value = 140, .center_freq = 5700},
+ { .hw_value = 149, .center_freq = 5745},
+ { .hw_value = 153, .center_freq = 5765},
+ { .hw_value = 157, .center_freq = 5785},
+ { .hw_value = 161, .center_freq = 5805},
+ { .hw_value = 165, .center_freq = 5825},
+};
+
+
+static struct ieee80211_supported_band wl1271_band_5ghz = {
+ .channels = wl1271_channels_5ghz,
+ .n_channels = ARRAY_SIZE(wl1271_channels_5ghz),
+ .bitrates = wl1271_rates_5ghz,
+ .n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
+};
+
static const struct ieee80211_ops wl1271_ops = {
.start = wl1271_op_start,
.stop = wl1271_op_stop,
@@ -1119,6 +1711,7 @@ static const struct ieee80211_ops wl1271_ops = {
.remove_interface = wl1271_op_remove_interface,
.config = wl1271_op_config,
/* .config_interface = wl1271_op_config_interface, */
+ .prepare_multicast = wl1271_op_prepare_multicast,
.configure_filter = wl1271_op_configure_filter,
.tx = wl1271_op_tx,
.set_key = wl1271_op_set_key,
@@ -1151,24 +1744,26 @@ static int wl1271_register_hw(struct wl1271 *wl)
static int wl1271_init_ieee80211(struct wl1271 *wl)
{
- /*
- * The tx descriptor buffer and the TKIP space.
- *
- * FIXME: add correct 1271 descriptor size
- */
- wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE;
+ /* The tx descriptor buffer and the TKIP space. */
+ wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
+ sizeof(struct wl1271_tx_hw_descr);
/* unit us */
/* FIXME: find a proper value */
wl->hw->channel_change_time = 10000;
wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_NOISE_DBM;
+ IEEE80211_HW_NOISE_DBM |
+ IEEE80211_HW_BEACON_FILTER |
+ IEEE80211_HW_SUPPORTS_PS;
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
wl->hw->wiphy->max_scan_ssids = 1;
wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
+ if (wl1271_11a_enabled())
+ wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
+
SET_IEEE80211_DEV(wl->hw, &wl->spi->dev);
return 0;
@@ -1213,29 +1808,33 @@ static int __devinit wl1271_probe(struct spi_device *spi)
wl = hw->priv;
memset(wl, 0, sizeof(*wl));
+ INIT_LIST_HEAD(&wl->list);
+
wl->hw = hw;
dev_set_drvdata(&spi->dev, wl);
wl->spi = spi;
skb_queue_head_init(&wl->tx_queue);
- INIT_WORK(&wl->filter_work, wl1271_filter_work);
+ INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
wl->channel = WL1271_DEFAULT_CHANNEL;
wl->scanning = false;
wl->default_key = 0;
- wl->listen_int = 1;
wl->rx_counter = 0;
wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
wl->elp = false;
wl->psm = 0;
wl->psm_requested = false;
+ wl->psm_entry_retry = 0;
wl->tx_queue_stopped = false;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
+ wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
+ wl->band = IEEE80211_BAND_2GHZ;
+ wl->vif = NULL;
+ wl->joined = false;
- /* We use the default power on sleep time until we know which chip
- * we're using */
- for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
+ for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
wl->tx_frames[i] = NULL;
spin_lock_init(&wl->wl_lock);
@@ -1250,13 +1849,6 @@ static int __devinit wl1271_probe(struct spi_device *spi)
wl->state = WL1271_STATE_OFF;
mutex_init(&wl->mutex);
- wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL);
- if (!wl->rx_descriptor) {
- wl1271_error("could not allocate memory for rx descriptor");
- ret = -ENOMEM;
- goto out_free;
- }
-
/* This is the only SPI value that we need to set here, the rest
* comes from the board-peripherals file */
spi->bits_per_word = 32;
@@ -1298,6 +1890,9 @@ static int __devinit wl1271_probe(struct spi_device *spi)
}
dev_set_drvdata(&wl1271_device.dev, wl);
+ /* Apply default driver configuration. */
+ wl1271_conf_init(wl);
+
ret = wl1271_init_ieee80211(wl);
if (ret)
goto out_platform;
@@ -1319,9 +1914,6 @@ static int __devinit wl1271_probe(struct spi_device *spi)
free_irq(wl->irq, wl);
out_free:
- kfree(wl->rx_descriptor);
- wl->rx_descriptor = NULL;
-
ieee80211_free_hw(hw);
return ret;
@@ -1337,14 +1929,11 @@ static int __devexit wl1271_remove(struct spi_device *spi)
platform_device_unregister(&wl1271_device);
free_irq(wl->irq, wl);
kfree(wl->target_mem_map);
- kfree(wl->fw);
+ vfree(wl->fw);
wl->fw = NULL;
kfree(wl->nvs);
wl->nvs = NULL;
- kfree(wl->rx_descriptor);
- wl->rx_descriptor = NULL;
-
kfree(wl->fw_status);
kfree(wl->tx_res_if);
@@ -1391,3 +1980,5 @@ module_exit(wl1271_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
+MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
+MODULE_FIRMWARE(WL1271_FW_NAME);
diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c
index 1dc74b0c773..507cd91d7ee 100644
--- a/drivers/net/wireless/wl12xx/wl1271_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1271_ps.c
@@ -27,25 +27,38 @@
#define WL1271_WAKEUP_TIMEOUT 500
+void wl1271_elp_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wl1271 *wl;
+
+ dwork = container_of(work, struct delayed_work, work);
+ wl = container_of(dwork, struct wl1271, elp_work);
+
+ wl1271_debug(DEBUG_PSM, "elp work");
+
+ mutex_lock(&wl->mutex);
+
+ if (wl->elp || !wl->psm)
+ goto out;
+
+ wl1271_debug(DEBUG_PSM, "chip to elp");
+ wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
+ wl->elp = true;
+
+out:
+ mutex_unlock(&wl->mutex);
+}
+
+#define ELP_ENTRY_DELAY 5
+
/* Routines to toggle sleep mode while in ELP */
void wl1271_ps_elp_sleep(struct wl1271 *wl)
{
- /*
- * FIXME: due to a problem in the firmware (causing a firmware
- * crash), ELP entry is prevented below. Remove the "true" to
- * re-enable ELP entry.
- */
- if (true || wl->elp || !wl->psm)
- return;
-
- /*
- * Go to ELP unless there is work already pending - pending work
- * will immediately wakeup the chipset anyway.
- */
- if (!work_pending(&wl->irq_work) && !work_pending(&wl->tx_work)) {
- wl1271_debug(DEBUG_PSM, "chip to elp");
- wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
- wl->elp = true;
+ if (wl->psm) {
+ cancel_delayed_work(&wl->elp_work);
+ ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
+ msecs_to_jiffies(ELP_ENTRY_DELAY));
}
}
@@ -73,7 +86,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake)
wl->elp_compl = &compl;
spin_unlock_irqrestore(&wl->wl_lock, flags);
- wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
+ wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
if (!pending) {
ret = wait_for_completion_timeout(
@@ -111,6 +124,17 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
switch (mode) {
case STATION_POWER_SAVE_MODE:
wl1271_debug(DEBUG_PSM, "entering psm");
+
+ /* enable beacon filtering */
+ ret = wl1271_acx_beacon_filter_opt(wl, true);
+ if (ret < 0)
+ return ret;
+
+ /* enable beacon early termination */
+ ret = wl1271_acx_bet_enable(wl, true);
+ if (ret < 0)
+ return ret;
+
ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
if (ret < 0)
return ret;
@@ -128,6 +152,16 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
if (ret < 0)
return ret;
+ /* disable beacon early termination */
+ ret = wl1271_acx_bet_enable(wl, false);
+ if (ret < 0)
+ return ret;
+
+ /* disable beacon filtering */
+ ret = wl1271_acx_beacon_filter_opt(wl, false);
+ if (ret < 0)
+ return ret;
+
ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.h b/drivers/net/wireless/wl12xx/wl1271_ps.h
index de2bd3c7dc9..779653d0ae8 100644
--- a/drivers/net/wireless/wl12xx/wl1271_ps.h
+++ b/drivers/net/wireless/wl12xx/wl1271_ps.h
@@ -30,6 +30,6 @@
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode);
void wl1271_ps_elp_sleep(struct wl1271 *wl);
int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake);
-
+void wl1271_elp_work(struct work_struct *work);
#endif /* __WL1271_PS_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_reg.h b/drivers/net/wireless/wl12xx/wl1271_reg.h
index f8ed4a4fc69..1f237389d1c 100644
--- a/drivers/net/wireless/wl12xx/wl1271_reg.h
+++ b/drivers/net/wireless/wl12xx/wl1271_reg.h
@@ -34,7 +34,7 @@
#define REGISTERS_WORK_SIZE 0x0000b000
#define HW_ACCESS_ELP_CTRL_REG_ADDR 0x1FFFC
-#define STATUS_MEM_ADDRESS 0x40400
+#define FW_STATUS_ADDR (0x14FC0 + 0xA000)
/* ELP register commands */
#define ELPCTRL_WAKE_UP 0x1
@@ -213,7 +213,6 @@
==============================================*/
#define ACX_REG_INTERRUPT_ACK (REGISTERS_BASE + 0x04F0)
-#define RX_DRIVER_DUMMY_WRITE_ADDRESS (REGISTERS_BASE + 0x0534)
#define RX_DRIVER_COUNTER_ADDRESS (REGISTERS_BASE + 0x0538)
/* Device Configuration registers*/
@@ -614,50 +613,6 @@ enum {
MAX_RADIO_BANDS = 0xFF
};
-enum {
- NO_RATE = 0,
- RATE_1MBPS = 0x0A,
- RATE_2MBPS = 0x14,
- RATE_5_5MBPS = 0x37,
- RATE_6MBPS = 0x0B,
- RATE_9MBPS = 0x0F,
- RATE_11MBPS = 0x6E,
- RATE_12MBPS = 0x0A,
- RATE_18MBPS = 0x0E,
- RATE_22MBPS = 0xDC,
- RATE_24MBPS = 0x09,
- RATE_36MBPS = 0x0D,
- RATE_48MBPS = 0x08,
- RATE_54MBPS = 0x0C
-};
-
-enum {
- RATE_INDEX_1MBPS = 0,
- RATE_INDEX_2MBPS = 1,
- RATE_INDEX_5_5MBPS = 2,
- RATE_INDEX_6MBPS = 3,
- RATE_INDEX_9MBPS = 4,
- RATE_INDEX_11MBPS = 5,
- RATE_INDEX_12MBPS = 6,
- RATE_INDEX_18MBPS = 7,
- RATE_INDEX_22MBPS = 8,
- RATE_INDEX_24MBPS = 9,
- RATE_INDEX_36MBPS = 10,
- RATE_INDEX_48MBPS = 11,
- RATE_INDEX_54MBPS = 12,
- RATE_INDEX_MAX = RATE_INDEX_54MBPS,
- MAX_RATE_INDEX,
- INVALID_RATE_INDEX = MAX_RATE_INDEX,
- RATE_INDEX_ENUM_MAX_SIZE = 0x7FFFFFFF
-};
-
-enum {
- RATE_MASK_1MBPS = 0x1,
- RATE_MASK_2MBPS = 0x2,
- RATE_MASK_5_5MBPS = 0x4,
- RATE_MASK_11MBPS = 0x20,
-};
-
#define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */
#define OFDM_RATE_BIT BIT(6)
#define PBCC_RATE_BIT BIT(7)
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c
index ad8b6904c5e..ca645f38109 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.c
@@ -30,14 +30,15 @@
static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status,
u32 drv_rx_counter)
{
- return status->rx_pkt_descs[drv_rx_counter] & RX_MEM_BLOCK_MASK;
+ return le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) &
+ RX_MEM_BLOCK_MASK;
}
static u32 wl1271_rx_get_buf_size(struct wl1271_fw_status *status,
u32 drv_rx_counter)
{
- return (status->rx_pkt_descs[drv_rx_counter] & RX_BUF_SIZE_MASK) >>
- RX_BUF_SIZE_SHIFT_DIV;
+ return (le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]) &
+ RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV;
}
/* The values of this table must match the wl1271_rates[] array */
@@ -70,6 +71,36 @@ static u8 wl1271_rx_rate_to_idx[] = {
0 /* WL1271_RATE_1 */
};
+/* The values of this table must match the wl1271_rates[] array */
+static u8 wl1271_5_ghz_rx_rate_to_idx[] = {
+ /* MCS rates are used only with 11n */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS7 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS6 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS5 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS4 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS3 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS2 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS1 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS0 */
+
+ 7, /* WL1271_RATE_54 */
+ 6, /* WL1271_RATE_48 */
+ 5, /* WL1271_RATE_36 */
+ 4, /* WL1271_RATE_24 */
+
+ /* TI-specific rate */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_22 */
+
+ 3, /* WL1271_RATE_18 */
+ 2, /* WL1271_RATE_12 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_11 */
+ 1, /* WL1271_RATE_9 */
+ 0, /* WL1271_RATE_6 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_5_5 */
+ WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_2 */
+ WL1271_RX_RATE_UNSUPPORTED /* WL1271_RATE_1 */
+};
+
static void wl1271_rx_status(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct ieee80211_rx_status *status,
@@ -77,12 +108,21 @@ static void wl1271_rx_status(struct wl1271 *wl,
{
memset(status, 0, sizeof(struct ieee80211_rx_status));
- if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG)
+ if ((desc->flags & WL1271_RX_DESC_BAND_MASK) ==
+ WL1271_RX_DESC_BAND_BG) {
status->band = IEEE80211_BAND_2GHZ;
- else
+ status->rate_idx = wl1271_rx_rate_to_idx[desc->rate];
+ } else if ((desc->flags & WL1271_RX_DESC_BAND_MASK) ==
+ WL1271_RX_DESC_BAND_A) {
+ status->band = IEEE80211_BAND_5GHZ;
+ status->rate_idx = wl1271_5_ghz_rx_rate_to_idx[desc->rate];
+ } else
wl1271_warning("unsupported band 0x%x",
desc->flags & WL1271_RX_DESC_BAND_MASK);
+ if (unlikely(status->rate_idx == WL1271_RX_RATE_UNSUPPORTED))
+ wl1271_warning("unsupported rate");
+
/*
* FIXME: Add mactime handling. For IBSS (ad-hoc) we need to get the
* timestamp from the beacon (acx_tsf_info). In BSS mode (infra) we
@@ -91,12 +131,6 @@ static void wl1271_rx_status(struct wl1271 *wl,
*/
status->signal = desc->rssi;
- /* FIXME: Should this be optimized? */
- status->qual = (desc->rssi - WL1271_RX_MIN_RSSI) * 100 /
- (WL1271_RX_MAX_RSSI - WL1271_RX_MIN_RSSI);
- status->qual = min(status->qual, 100);
- status->qual = max(status->qual, 0);
-
/*
* FIXME: In wl1251, the SNR should be divided by two. In wl1271 we
* need to divide by two for now, but TI has been discussing about
@@ -109,17 +143,11 @@ static void wl1271_rx_status(struct wl1271 *wl,
if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) {
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
- if (likely(!(desc->flags & WL1271_RX_DESC_DECRYPT_FAIL)))
+ if (likely(!(desc->status & WL1271_RX_DESC_DECRYPT_FAIL)))
status->flag |= RX_FLAG_DECRYPTED;
-
- if (unlikely(desc->flags & WL1271_RX_DESC_MIC_FAIL))
+ if (unlikely(desc->status & WL1271_RX_DESC_MIC_FAIL))
status->flag |= RX_FLAG_MMIC_ERROR;
}
-
- status->rate_idx = wl1271_rx_rate_to_idx[desc->rate];
-
- if (status->rate_idx == WL1271_RX_RATE_UNSUPPORTED)
- wl1271_warning("unsupported rate");
}
static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
@@ -131,14 +159,14 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
u8 *buf;
u8 beacon = 0;
- skb = dev_alloc_skb(length);
+ skb = __dev_alloc_skb(length, GFP_KERNEL);
if (!skb) {
wl1271_error("Couldn't allocate RX frame");
return;
}
buf = skb_put(skb, length);
- wl1271_spi_reg_read(wl, WL1271_SLV_MEM_DATA, buf, length, true);
+ wl1271_spi_read(wl, WL1271_SLV_MEM_DATA, buf, length, true);
/* the data read starts with the descriptor */
desc = (struct wl1271_rx_descriptor *) buf;
@@ -156,7 +184,7 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length)
beacon ? "beacon" : "");
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
- ieee80211_rx(wl->hw, skb);
+ ieee80211_rx_ni(wl->hw, skb);
}
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
@@ -176,15 +204,15 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
break;
}
- wl->rx_mem_pool_addr.addr =
- (mem_block << 8) + wl_mem_map->packet_memory_pool_start;
+ wl->rx_mem_pool_addr.addr = (mem_block << 8) +
+ le32_to_cpu(wl_mem_map->packet_memory_pool_start);
wl->rx_mem_pool_addr.addr_extra =
wl->rx_mem_pool_addr.addr + 4;
/* Choose the block we want to read */
- wl1271_spi_reg_write(wl, WL1271_SLV_REG_DATA,
- &wl->rx_mem_pool_addr,
- sizeof(wl->rx_mem_pool_addr), false);
+ wl1271_spi_write(wl, WL1271_SLV_REG_DATA,
+ &wl->rx_mem_pool_addr,
+ sizeof(wl->rx_mem_pool_addr), false);
wl1271_rx_handle_data(wl, buf_size);
@@ -192,9 +220,5 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status)
drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
}
- wl1271_reg_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
-
- /* This is a workaround for some problems in the chip */
- wl1271_reg_write32(wl, RX_DRIVER_DUMMY_WRITE_ADDRESS, 0x1);
-
+ wl1271_spi_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
}
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.h b/drivers/net/wireless/wl12xx/wl1271_rx.h
index d1ca60e43a2..1ae6d1783ed 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.h
@@ -102,14 +102,14 @@
#define RX_BUF_SIZE_SHIFT_DIV 6
struct wl1271_rx_descriptor {
- u16 length;
+ __le16 length;
u8 status;
u8 flags;
u8 rate;
u8 channel;
s8 rssi;
u8 snr;
- u32 timestamp;
+ __le32 timestamp;
u8 packet_class;
u8 process_id;
u8 pad_len;
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c
index 4a12880c16a..02978a16e73 100644
--- a/drivers/net/wireless/wl12xx/wl1271_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1271_spi.c
@@ -30,17 +30,29 @@
#include "wl12xx_80211.h"
#include "wl1271_spi.h"
-static int wl1271_translate_reg_addr(struct wl1271 *wl, int addr)
+static int wl1271_translate_addr(struct wl1271 *wl, int addr)
{
- return addr - wl->physical_reg_addr + wl->virtual_reg_addr;
-}
-
-static int wl1271_translate_mem_addr(struct wl1271 *wl, int addr)
-{
- return addr - wl->physical_mem_addr + wl->virtual_mem_addr;
+ /*
+ * To translate, first check to which window of addresses the
+ * particular address belongs. Then subtract the starting address
+ * of that window from the address. Then, add offset of the
+ * translated region.
+ *
+ * The translated regions occur next to each other in physical device
+ * memory, so just add the sizes of the preceeding address regions to
+ * get the offset to the new region.
+ *
+ * Currently, only the two first regions are addressed, and the
+ * assumption is that all addresses will fall into either of those
+ * two.
+ */
+ if ((addr >= wl->part.reg.start) &&
+ (addr < wl->part.reg.start + wl->part.reg.size))
+ return addr - wl->part.reg.start + wl->part.mem.size;
+ else
+ return addr - wl->part.mem.start;
}
-
void wl1271_spi_reset(struct wl1271 *wl)
{
u8 *cmd;
@@ -123,133 +135,137 @@ void wl1271_spi_init(struct wl1271 *wl)
/* Set the SPI partitions to access the chip addresses
*
- * There are two VIRTUAL (SPI) partitions (the memory partition and the
- * registers partition), which are mapped to two different areas of the
- * PHYSICAL (hardware) memory. This function also makes other checks to
- * ensure that the partitions are not overlapping. In the diagram below, the
- * memory partition comes before the register partition, but the opposite is
- * also supported.
+ * To simplify driver code, a fixed (virtual) memory map is defined for
+ * register and memory addresses. Because in the chipset, in different stages
+ * of operation, those addresses will move around, an address translation
+ * mechanism is required.
*
- * PHYSICAL address
+ * There are four partitions (three memory and one register partition),
+ * which are mapped to two different areas of the hardware memory.
+ *
+ * Virtual address
* space
*
* | |
- * ...+----+--> mem_start
- * VIRTUAL address ... | |
+ * ...+----+--> mem.start
+ * Physical address ... | |
* space ... | | [PART_0]
* ... | |
- * 0x00000000 <--+----+... ...+----+--> mem_start + mem_size
+ * 00000000 <--+----+... ...+----+--> mem.start + mem.size
* | | ... | |
* |MEM | ... | |
* | | ... | |
- * part_size <--+----+... | | {unused area)
+ * mem.size <--+----+... | | {unused area)
* | | ... | |
* |REG | ... | |
- * part_size | | ... | |
- * + <--+----+... ...+----+--> reg_start
- * reg_size ... | |
- * ... | | [PART_1]
- * ... | |
- * ...+----+--> reg_start + reg_size
+ * mem.size | | ... | |
+ * + <--+----+... ...+----+--> reg.start
+ * reg.size | | ... | |
+ * |MEM2| ... | | [PART_1]
+ * | | ... | |
+ * ...+----+--> reg.start + reg.size
* | |
*
*/
int wl1271_set_partition(struct wl1271 *wl,
- u32 mem_start, u32 mem_size,
- u32 reg_start, u32 reg_size)
+ struct wl1271_partition_set *p)
{
- struct wl1271_partition *partition;
- struct spi_transfer t;
- struct spi_message m;
- size_t len, cmd_len;
- u32 *cmd;
- int addr;
-
- cmd_len = sizeof(u32) + 2 * sizeof(struct wl1271_partition);
- cmd = kzalloc(cmd_len, GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
-
- spi_message_init(&m);
- memset(&t, 0, sizeof(t));
-
- partition = (struct wl1271_partition *) (cmd + 1);
- addr = HW_ACCESS_PART0_SIZE_ADDR;
- len = 2 * sizeof(struct wl1271_partition);
-
- *cmd |= WSPI_CMD_WRITE;
- *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH;
- *cmd |= addr & WSPI_CMD_BYTE_ADDR;
+ /* copy partition info */
+ memcpy(&wl->part, p, sizeof(*p));
wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
- mem_start, mem_size);
+ p->mem.start, p->mem.size);
wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
- reg_start, reg_size);
-
- /* Make sure that the two partitions together don't exceed the
- * address range */
- if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) {
- wl1271_debug(DEBUG_SPI, "Total size exceeds maximum virtual"
- " address range. Truncating partition[0].");
- mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size;
- wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
- mem_start, mem_size);
- wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
- reg_start, reg_size);
- }
+ p->reg.start, p->reg.size);
+ wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X",
+ p->mem2.start, p->mem2.size);
+ wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X",
+ p->mem3.start, p->mem3.size);
+
+ /* write partition info to the chipset */
+ wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start);
+ wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size);
+ wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start);
+ wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size);
+ wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start);
+ wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size);
+ wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start);
- if ((mem_start < reg_start) &&
- ((mem_start + mem_size) > reg_start)) {
- /* Guarantee that the memory partition doesn't overlap the
- * registers partition */
- wl1271_debug(DEBUG_SPI, "End of partition[0] is "
- "overlapping partition[1]. Adjusted.");
- mem_size = reg_start - mem_start;
- wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
- mem_start, mem_size);
- wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
- reg_start, reg_size);
- } else if ((reg_start < mem_start) &&
- ((reg_start + reg_size) > mem_start)) {
- /* Guarantee that the register partition doesn't overlap the
- * memory partition */
- wl1271_debug(DEBUG_SPI, "End of partition[1] is"
- " overlapping partition[0]. Adjusted.");
- reg_size = mem_start - reg_start;
- wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X",
- mem_start, mem_size);
- wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X",
- reg_start, reg_size);
- }
+ return 0;
+}
- partition[0].start = mem_start;
- partition[0].size = mem_size;
- partition[1].start = reg_start;
- partition[1].size = reg_size;
+#define WL1271_BUSY_WORD_TIMEOUT 1000
- wl->physical_mem_addr = mem_start;
- wl->physical_reg_addr = reg_start;
+/* FIXME: Check busy words, removed due to SPI bug */
+#if 0
+static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len)
+{
+ struct spi_transfer t[1];
+ struct spi_message m;
+ u32 *busy_buf;
+ int num_busy_bytes = 0;
- wl->virtual_mem_addr = 0;
- wl->virtual_reg_addr = mem_size;
+ wl1271_info("spi read BUSY!");
- t.tx_buf = cmd;
- t.len = cmd_len;
- spi_message_add_tail(&t, &m);
+ /*
+ * Look for the non-busy word in the read buffer, and if found,
+ * read in the remaining data into the buffer.
+ */
+ busy_buf = (u32 *)buf;
+ for (; (u32)busy_buf < (u32)buf + len; busy_buf++) {
+ num_busy_bytes += sizeof(u32);
+ if (*busy_buf & 0x1) {
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+ memmove(buf, busy_buf, len - num_busy_bytes);
+ t[0].rx_buf = buf + (len - num_busy_bytes);
+ t[0].len = num_busy_bytes;
+ spi_message_add_tail(&t[0], &m);
+ spi_sync(wl->spi, &m);
+ return;
+ }
+ }
- spi_sync(wl->spi, &m);
+ /*
+ * Read further busy words from SPI until a non-busy word is
+ * encountered, then read the data itself into the buffer.
+ */
+ wl1271_info("spi read BUSY-polling needed!");
- kfree(cmd);
+ num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT;
+ busy_buf = wl->buffer_busyword;
+ while (num_busy_bytes) {
+ num_busy_bytes--;
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+ t[0].rx_buf = busy_buf;
+ t[0].len = sizeof(u32);
+ spi_message_add_tail(&t[0], &m);
+ spi_sync(wl->spi, &m);
+
+ if (*busy_buf & 0x1) {
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+ t[0].rx_buf = buf;
+ t[0].len = len;
+ spi_message_add_tail(&t[0], &m);
+ spi_sync(wl->spi, &m);
+ return;
+ }
+ }
- return 0;
+ /* The SPI bus is unresponsive, the read failed. */
+ memset(buf, 0, len);
+ wl1271_error("SPI read busy-word timeout!\n");
}
+#endif
-void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf,
- size_t len, bool fixed)
+void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
+ size_t len, bool fixed)
{
struct spi_transfer t[3];
struct spi_message m;
- u8 *busy_buf;
+ u32 *busy_buf;
u32 *cmd;
cmd = &wl->buffer_cmd;
@@ -281,14 +297,16 @@ void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf,
spi_sync(wl->spi, &m);
- /* FIXME: check busy words */
+ /* FIXME: Check busy words, removed due to SPI bug */
+ /* if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1))
+ wl1271_spi_read_busy(wl, buf, len); */
wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd));
wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len);
}
-void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf,
- size_t len, bool fixed)
+void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
+ size_t len, bool fixed)
{
struct spi_transfer t[2];
struct spi_message m;
@@ -321,62 +339,77 @@ void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf,
wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len);
}
-void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf,
- size_t len)
+void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len,
+ bool fixed)
{
int physical;
- physical = wl1271_translate_mem_addr(wl, addr);
+ physical = wl1271_translate_addr(wl, addr);
- wl1271_spi_read(wl, physical, buf, len, false);
+ wl1271_spi_raw_read(wl, physical, buf, len, fixed);
}
-void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf,
- size_t len)
+void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len,
+ bool fixed)
{
int physical;
- physical = wl1271_translate_mem_addr(wl, addr);
+ physical = wl1271_translate_addr(wl, addr);
- wl1271_spi_write(wl, physical, buf, len, false);
+ wl1271_spi_raw_write(wl, physical, buf, len, fixed);
}
-void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len,
- bool fixed)
+u32 wl1271_spi_read32(struct wl1271 *wl, int addr)
{
- int physical;
-
- physical = wl1271_translate_reg_addr(wl, addr);
+ return wl1271_raw_read32(wl, wl1271_translate_addr(wl, addr));
+}
- wl1271_spi_read(wl, physical, buf, len, fixed);
+void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val)
+{
+ wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val);
}
-void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len,
- bool fixed)
+void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val)
{
- int physical;
+ /* write address >> 1 + 0x30000 to OCP_POR_CTR */
+ addr = (addr >> 1) + 0x30000;
+ wl1271_spi_write32(wl, OCP_POR_CTR, addr);
- physical = wl1271_translate_reg_addr(wl, addr);
+ /* write value to OCP_POR_WDATA */
+ wl1271_spi_write32(wl, OCP_DATA_WRITE, val);
- wl1271_spi_write(wl, physical, buf, len, fixed);
+ /* write 1 to OCP_CMD */
+ wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_WRITE);
}
-u32 wl1271_mem_read32(struct wl1271 *wl, int addr)
+u16 wl1271_top_reg_read(struct wl1271 *wl, int addr)
{
- return wl1271_read32(wl, wl1271_translate_mem_addr(wl, addr));
-}
+ u32 val;
+ int timeout = OCP_CMD_LOOP;
-void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val)
-{
- wl1271_write32(wl, wl1271_translate_mem_addr(wl, addr), val);
-}
+ /* write address >> 1 + 0x30000 to OCP_POR_CTR */
+ addr = (addr >> 1) + 0x30000;
+ wl1271_spi_write32(wl, OCP_POR_CTR, addr);
-u32 wl1271_reg_read32(struct wl1271 *wl, int addr)
-{
- return wl1271_read32(wl, wl1271_translate_reg_addr(wl, addr));
-}
+ /* write 2 to OCP_CMD */
+ wl1271_spi_write32(wl, OCP_CMD, OCP_CMD_READ);
-void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val)
-{
- wl1271_write32(wl, wl1271_translate_reg_addr(wl, addr), val);
+ /* poll for data ready */
+ do {
+ val = wl1271_spi_read32(wl, OCP_DATA_READ);
+ timeout--;
+ } while (!(val & OCP_READY_MASK) && timeout);
+
+ if (!timeout) {
+ wl1271_warning("Top register access timed out.");
+ return 0xffff;
+ }
+
+ /* check data status and return if OK */
+ if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK)
+ return val & 0xffff;
+ else {
+ wl1271_warning("Top register access returned error.");
+ return 0xffff;
+ }
}
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.h b/drivers/net/wireless/wl12xx/wl1271_spi.h
index 2c996845864..cb7df1c5631 100644
--- a/drivers/net/wireless/wl12xx/wl1271_spi.h
+++ b/drivers/net/wireless/wl12xx/wl1271_spi.h
@@ -29,10 +29,14 @@
#define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0
-#define HW_ACCESS_PART0_SIZE_ADDR 0x1FFC0
-#define HW_ACCESS_PART0_START_ADDR 0x1FFC4
-#define HW_ACCESS_PART1_SIZE_ADDR 0x1FFC8
-#define HW_ACCESS_PART1_START_ADDR 0x1FFCC
+#define HW_PARTITION_REGISTERS_ADDR 0x1ffc0
+#define HW_PART0_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR)
+#define HW_PART0_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 4)
+#define HW_PART1_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 8)
+#define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12)
+#define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16)
+#define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20)
+#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
#define HW_ACCESS_REGISTER_SIZE 4
@@ -67,47 +71,56 @@
((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32))
#define HW_ACCESS_WSPI_INIT_CMD_MASK 0
+#define OCP_CMD_LOOP 32
+
+#define OCP_CMD_WRITE 0x1
+#define OCP_CMD_READ 0x2
+
+#define OCP_READY_MASK BIT(18)
+#define OCP_STATUS_MASK (BIT(16) | BIT(17))
+
+#define OCP_STATUS_NO_RESP 0x00000
+#define OCP_STATUS_OK 0x10000
+#define OCP_STATUS_REQ_FAILED 0x20000
+#define OCP_STATUS_RESP_ERROR 0x30000
/* Raw target IO, address is not translated */
-void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf,
+void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed);
-void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf,
+void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed);
-/* Memory target IO, address is tranlated to partition 0 */
-void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, size_t len);
-void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, size_t len);
-u32 wl1271_mem_read32(struct wl1271 *wl, int addr);
-void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val);
+/* Translated target IO */
+void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, size_t len,
+ bool fixed);
+void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, size_t len,
+ bool fixed);
+u32 wl1271_spi_read32(struct wl1271 *wl, int addr);
+void wl1271_spi_write32(struct wl1271 *wl, int addr, u32 val);
-/* Registers IO */
-void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len,
- bool fixed);
-void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len,
- bool fixed);
-u32 wl1271_reg_read32(struct wl1271 *wl, int addr);
-void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val);
+/* Top Register IO */
+void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val);
+u16 wl1271_top_reg_read(struct wl1271 *wl, int addr);
/* INIT and RESET words */
void wl1271_spi_reset(struct wl1271 *wl);
void wl1271_spi_init(struct wl1271 *wl);
int wl1271_set_partition(struct wl1271 *wl,
- u32 part_start, u32 part_size,
- u32 reg_start, u32 reg_size);
+ struct wl1271_partition_set *p);
-static inline u32 wl1271_read32(struct wl1271 *wl, int addr)
+static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr)
{
- wl1271_spi_read(wl, addr, &wl->buffer_32,
- sizeof(wl->buffer_32), false);
+ wl1271_spi_raw_read(wl, addr, &wl->buffer_32,
+ sizeof(wl->buffer_32), false);
return wl->buffer_32;
}
-static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val)
+static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val)
{
wl->buffer_32 = val;
- wl1271_spi_write(wl, addr, &wl->buffer_32,
- sizeof(wl->buffer_32), false);
+ wl1271_spi_raw_write(wl, addr, &wl->buffer_32,
+ sizeof(wl->buffer_32), false);
}
#endif /* __WL1271_SPI_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index ff221258b94..00af065c77c 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -33,8 +33,7 @@
static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb)
{
int i;
-
- for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
+ for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
if (wl->tx_frames[i] == NULL) {
wl->tx_frames[i] = skb;
return i;
@@ -58,8 +57,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
/* approximate the number of blocks required for this packet
in the firmware */
/* FIXME: try to figure out what is done here and make it cleaner */
- total_blocks = (skb->len) >> TX_HW_BLOCK_SHIFT_DIV;
- excluded = (total_blocks << 2) + (skb->len & 0xff) + 34;
+ total_blocks = (total_len + 20) >> TX_HW_BLOCK_SHIFT_DIV;
+ excluded = (total_blocks << 2) + ((total_len + 20) & 0xff) + 34;
total_blocks += (excluded > 252) ? 2 : 1;
total_blocks += TX_HW_BLOCK_SPARE;
@@ -89,15 +88,25 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
{
struct wl1271_tx_hw_descr *desc;
int pad;
+ u16 tx_attr;
desc = (struct wl1271_tx_hw_descr *) skb->data;
+ /* relocate space for security header */
+ if (extra) {
+ void *framestart = skb->data + sizeof(*desc);
+ u16 fc = *(u16 *)(framestart + extra);
+ int hdrlen = ieee80211_hdrlen(cpu_to_le16(fc));
+ memmove(framestart, framestart + extra, hdrlen);
+ }
+
/* configure packet life time */
- desc->start_time = jiffies_to_usecs(jiffies) - wl->time_offset;
- desc->life_time = TX_HW_MGMT_PKT_LIFETIME_TU;
+ desc->start_time = cpu_to_le32(jiffies_to_usecs(jiffies) -
+ wl->time_offset);
+ desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
/* configure the tx attributes */
- desc->tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
+ tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
/* FIXME: do we know the packet priority? can we identify mgmt
packets, and use max prio for them at least? */
desc->tid = 0;
@@ -106,11 +115,13 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
/* align the length (and store in terms of words) */
pad = WL1271_TX_ALIGN(skb->len);
- desc->length = pad >> 2;
+ desc->length = cpu_to_le16(pad >> 2);
/* calculate number of padding bytes */
pad = pad - skb->len;
- desc->tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD;
+ tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD;
+
+ desc->tx_attr = cpu_to_le16(tx_attr);
wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad);
return 0;
@@ -147,11 +158,11 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb,
len = WL1271_TX_ALIGN(skb->len);
/* perform a fixed address block write with the packet */
- wl1271_spi_reg_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true);
+ wl1271_spi_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true);
/* write packet new counter into the write access register */
wl->tx_packets_count++;
- wl1271_reg_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
+ wl1271_spi_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
desc = (struct wl1271_tx_hw_descr *) skb->data;
wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)",
@@ -254,14 +265,13 @@ out:
static void wl1271_tx_complete_packet(struct wl1271 *wl,
struct wl1271_tx_hw_res_descr *result)
{
-
struct ieee80211_tx_info *info;
struct sk_buff *skb;
- u32 header_len;
+ u16 seq;
int id = result->id;
/* check for id legality */
- if (id >= TX_HW_RESULT_QUEUE_LEN || wl->tx_frames[id] == NULL) {
+ if (id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL) {
wl1271_warning("TX result illegal id: %d", id);
return;
}
@@ -284,22 +294,32 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
/* info->status.retry_count = result->ack_failures; */
wl->stats.retry_count += result->ack_failures;
- /* get header len */
+ /* update security sequence number */
+ seq = wl->tx_security_seq_16 +
+ (result->lsb_security_sequence_number -
+ wl->tx_security_last_seq);
+ wl->tx_security_last_seq = result->lsb_security_sequence_number;
+
+ if (seq < wl->tx_security_seq_16)
+ wl->tx_security_seq_32++;
+ wl->tx_security_seq_16 = seq;
+
+ /* remove private header from packet */
+ skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
+
+ /* remove TKIP header space if present */
if (info->control.hw_key &&
- info->control.hw_key->alg == ALG_TKIP)
- header_len = WL1271_TKIP_IV_SPACE +
- sizeof(struct wl1271_tx_hw_descr);
- else
- header_len = sizeof(struct wl1271_tx_hw_descr);
+ info->control.hw_key->alg == ALG_TKIP) {
+ int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+ memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen);
+ skb_pull(skb, WL1271_TKIP_IV_SPACE);
+ }
wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x"
" status 0x%x",
result->id, skb, result->ack_failures,
result->rate_class_index, result->status);
- /* remove private header from packet */
- skb_pull(skb, header_len);
-
/* return the packet to the stack */
ieee80211_tx_status(wl->hw, skb);
wl->tx_frames[result->id] = NULL;
@@ -315,8 +335,8 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count)
wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count);
/* read the tx results from the chipset */
- wl1271_spi_mem_read(wl, memmap->tx_result,
- wl->tx_res_if, sizeof(*wl->tx_res_if));
+ wl1271_spi_read(wl, le32_to_cpu(memmap->tx_result),
+ wl->tx_res_if, sizeof(*wl->tx_res_if), false);
/* verify that the result buffer is not getting overrun */
if (count > TX_HW_RESULT_QUEUE_LEN) {
@@ -337,10 +357,10 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count)
}
/* write host counter to chipset (to ack) */
- wl1271_mem_write32(wl, memmap->tx_result +
+ wl1271_spi_write32(wl, le32_to_cpu(memmap->tx_result) +
offsetof(struct wl1271_tx_hw_res_if,
tx_result_host_counter),
- wl->tx_res_if->tx_result_fw_counter);
+ le32_to_cpu(wl->tx_res_if->tx_result_fw_counter));
}
/* caller must hold wl->mutex */
@@ -364,7 +384,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
ieee80211_tx_status(wl->hw, skb);
}
- for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
+ for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
if (wl->tx_frames[i] != NULL) {
skb = wl->tx_frames[i];
info = IEEE80211_SKB_CB(skb);
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h
index 4a614067ddb..416396caf0a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.h
@@ -58,7 +58,7 @@
struct wl1271_tx_hw_descr {
/* Length of packet in words, including descriptor+header+data */
- u16 length;
+ __le16 length;
/* Number of extra memory blocks to allocate for this packet in
addition to the number of blocks derived from the packet length */
u8 extra_mem_blocks;
@@ -67,12 +67,12 @@ struct wl1271_tx_hw_descr {
HW!! */
u8 total_mem_blocks;
/* Device time (in us) when the packet arrived to the driver */
- u32 start_time;
+ __le32 start_time;
/* Max delay in TUs until transmission. The last device time the
packet can be transmitted is: startTime+(1024*LifeTime) */
- u16 life_time;
+ __le16 life_time;
/* Bitwise fields - see TX_ATTR... definitions above. */
- u16 tx_attr;
+ __le16 tx_attr;
/* Packet identifier used also in the Tx-Result. */
u8 id;
/* The packet TID value (as User-Priority) */
@@ -100,12 +100,12 @@ struct wl1271_tx_hw_res_descr {
several possible reasons for failure. */
u8 status;
/* Total air access duration including all retrys and overheads.*/
- u16 medium_usage;
+ __le16 medium_usage;
/* The time passed from host xfer to Tx-complete.*/
- u32 fw_handling_time;
+ __le32 fw_handling_time;
/* Total media delay
(from 1st EDCA AIFS counter until TX Complete). */
- u32 medium_delay;
+ __le32 medium_delay;
/* LS-byte of last TKIP seq-num (saved per AC for recovery). */
u8 lsb_security_sequence_number;
/* Retry count - number of transmissions without successful ACK.*/
@@ -118,8 +118,8 @@ struct wl1271_tx_hw_res_descr {
} __attribute__ ((packed));
struct wl1271_tx_hw_res_if {
- u32 tx_result_fw_counter;
- u32 tx_result_host_counter;
+ __le32 tx_result_fw_counter;
+ __le32 tx_result_host_counter;
struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN];
} __attribute__ ((packed));
diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h
index 657c2dbcb7d..055d7bc6f59 100644
--- a/drivers/net/wireless/wl12xx/wl12xx_80211.h
+++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h
@@ -122,8 +122,8 @@ struct wl12xx_null_data_template {
} __attribute__ ((packed));
struct wl12xx_ps_poll_template {
- u16 fc;
- u16 aid;
+ __le16 fc;
+ __le16 aid;
u8 bssid[ETH_ALEN];
u8 ta[ETH_ALEN];
} __attribute__ ((packed));
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index 4f1e0cfe609..7b9621de239 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -67,23 +67,7 @@
/* For rough constant delay */
#define WL3501_NOPLOOP(n) { int x = 0; while (x++ < n) slow_down_io(); }
-/*
- * All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If you do not
- * define PCMCIA_DEBUG at all, all the debug code will be left out. If you
- * compile with PCMCIA_DEBUG=0, the debug code will be present but disabled --
- * but it can then be enabled for specific modules at load time with a
- * 'pc_debug=#' option to insmod.
- */
-#define PCMCIA_DEBUG 0
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define dprintk(n, format, args...) \
- { if (pc_debug > (n)) \
- printk(KERN_INFO "%s: " format "\n", __func__ , ##args); }
-#else
-#define dprintk(n, format, args...)
-#endif
+
#define wl3501_outb(a, b) { outb(a, b); slow_down_io(); }
#define wl3501_outb_p(a, b) { outb_p(a, b); slow_down_io(); }
@@ -381,7 +365,7 @@ static void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr)
static int wl3501_esbq_req_test(struct wl3501_card *this)
{
- u8 tmp;
+ u8 tmp = 0;
wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp));
return tmp & 0x80;
@@ -684,10 +668,10 @@ static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr)
int matchflag = 0;
struct wl3501_scan_confirm sig;
- dprintk(3, "entry");
+ pr_debug("entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS) {
- dprintk(3, "success");
+ pr_debug("success");
if ((this->net_type == IW_MODE_INFRA &&
(sig.cap_info & WL3501_MGMT_CAPABILITY_ESS)) ||
(this->net_type == IW_MODE_ADHOC &&
@@ -722,7 +706,7 @@ static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr)
}
}
} else if (sig.status == WL3501_STATUS_TIMEOUT) {
- dprintk(3, "timeout");
+ pr_debug("timeout");
this->join_sta_bss = 0;
for (i = this->join_sta_bss; i < this->bss_cnt; i++)
if (!wl3501_mgmt_join(this, i))
@@ -879,7 +863,7 @@ static int wl3501_mgmt_auth(struct wl3501_card *this)
.timeout = 1000,
};
- dprintk(3, "entry");
+ pr_debug("entry");
memcpy(sig.mac_addr, this->bssid, ETH_ALEN);
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
@@ -893,7 +877,7 @@ static int wl3501_mgmt_association(struct wl3501_card *this)
.cap_info = this->cap_info,
};
- dprintk(3, "entry");
+ pr_debug("entry");
memcpy(sig.mac_addr, this->bssid, ETH_ALEN);
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
@@ -903,7 +887,7 @@ static void wl3501_mgmt_join_confirm(struct net_device *dev, u16 addr)
struct wl3501_card *this = netdev_priv(dev);
struct wl3501_join_confirm sig;
- dprintk(3, "entry");
+ pr_debug("entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS) {
if (this->net_type == IW_MODE_INFRA) {
@@ -962,7 +946,7 @@ static inline void wl3501_md_confirm_interrupt(struct net_device *dev,
{
struct wl3501_md_confirm sig;
- dprintk(3, "entry");
+ pr_debug("entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
wl3501_free_tx_buffer(this, sig.data);
if (netif_queue_stopped(dev))
@@ -1017,7 +1001,7 @@ static inline void wl3501_md_ind_interrupt(struct net_device *dev,
static inline void wl3501_get_confirm_interrupt(struct wl3501_card *this,
u16 addr, void *sig, int size)
{
- dprintk(3, "entry");
+ pr_debug("entry");
wl3501_get_from_wla(this, addr, &this->sig_get_confirm,
sizeof(this->sig_get_confirm));
wake_up(&this->wait);
@@ -1029,7 +1013,7 @@ static inline void wl3501_start_confirm_interrupt(struct net_device *dev,
{
struct wl3501_start_confirm sig;
- dprintk(3, "entry");
+ pr_debug("entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS)
netif_wake_queue(dev);
@@ -1041,7 +1025,7 @@ static inline void wl3501_assoc_confirm_interrupt(struct net_device *dev,
struct wl3501_card *this = netdev_priv(dev);
struct wl3501_assoc_confirm sig;
- dprintk(3, "entry");
+ pr_debug("entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS)
@@ -1053,7 +1037,7 @@ static inline void wl3501_auth_confirm_interrupt(struct wl3501_card *this,
{
struct wl3501_auth_confirm sig;
- dprintk(3, "entry");
+ pr_debug("entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS)
@@ -1069,7 +1053,7 @@ static inline void wl3501_rx_interrupt(struct net_device *dev)
u8 sig_id;
struct wl3501_card *this = netdev_priv(dev);
- dprintk(3, "entry");
+ pr_debug("entry");
loop:
morepkts = 0;
if (!wl3501_esbq_confirm(this))
@@ -1302,7 +1286,7 @@ static int wl3501_reset(struct net_device *dev)
wl3501_ack_interrupt(this);
wl3501_unblock_interrupt(this);
wl3501_mgmt_scan(this, 100);
- dprintk(1, "%s: device reset", dev->name);
+ pr_debug("%s: device reset", dev->name);
rc = 0;
out:
return rc;
@@ -1376,7 +1360,7 @@ static int wl3501_open(struct net_device *dev)
link->open++;
/* Initial WL3501 firmware */
- dprintk(1, "%s: Initialize WL3501 firmware...", dev->name);
+ pr_debug("%s: Initialize WL3501 firmware...", dev->name);
if (wl3501_init_firmware(this))
goto fail;
/* Initial device variables */
@@ -1388,7 +1372,7 @@ static int wl3501_open(struct net_device *dev)
wl3501_unblock_interrupt(this);
wl3501_mgmt_scan(this, 100);
rc = 0;
- dprintk(1, "%s: WL3501 opened", dev->name);
+ pr_debug("%s: WL3501 opened", dev->name);
printk(KERN_INFO "%s: Card Name: %s\n"
"%s: Firmware Date: %s\n",
dev->name, this->card_name,
@@ -1914,8 +1898,7 @@ static int wl3501_probe(struct pcmcia_device *p_dev)
p_dev->io.IOAddrLines = 5;
/* Interrupt setup */
- p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
p_dev->irq.Handler = wl3501_interrupt;
/* General socket configuration */
@@ -1938,16 +1921,13 @@ static int wl3501_probe(struct pcmcia_device *p_dev)
dev->wireless_handlers = &wl3501_handler_def;
SET_ETHTOOL_OPS(dev, &ops);
netif_stop_queue(dev);
- p_dev->priv = p_dev->irq.Instance = dev;
+ p_dev->priv = dev;
return wl3501_config(p_dev);
out_link:
return -ENOMEM;
}
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
/**
* wl3501_config - configure the PCMCIA socket and make eth device available
* @link - FILL_IN
@@ -1959,7 +1939,7 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
static int wl3501_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- int i = 0, j, last_fn, last_ret;
+ int i = 0, j, ret;
struct wl3501_card *this;
/* Try allocating IO ports. This tries a few fixed addresses. If you
@@ -1975,24 +1955,26 @@ static int wl3501_config(struct pcmcia_device *link)
if (i == 0)
break;
}
- if (i != 0) {
- cs_error(link, RequestIO, i);
+ if (i != 0)
goto failed;
- }
/* Now allocate an interrupt line. Note that this does not actually
* assign a handler to the interrupt. */
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
/* This actually configures the PCMCIA socket -- setting up the I/O
* windows and the interrupt mapping. */
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev)) {
printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n");
goto failed;
@@ -2041,8 +2023,6 @@ static int wl3501_config(struct pcmcia_device *link)
netif_start_queue(dev);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
wl3501_release(link);
return -ENODEV;
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index bc81974a2bc..33c8be7ec8e 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -112,6 +112,9 @@ exit:
return err;
}
+MODULE_FIRMWARE("zd1201-ap.fw");
+MODULE_FIRMWARE("zd1201.fw");
+
static void zd1201_usbfree(struct urb *urb)
{
struct zd1201 *zd = urb->context;
diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig
index 74b31eafe72..5f809695f71 100644
--- a/drivers/net/wireless/zd1211rw/Kconfig
+++ b/drivers/net/wireless/zd1211rw/Kconfig
@@ -1,6 +1,6 @@
config ZD1211RW
tristate "ZyDAS ZD1211/ZD1211B USB-wireless support"
- depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL
+ depends on USB && MAC80211 && EXPERIMENTAL
select FW_LOADER
---help---
This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 4e79a980013..dfa1b9bc22c 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -755,7 +755,7 @@ static int hw_reset_phy(struct zd_chip *chip)
static int zd1211_hw_init_hmac(struct zd_chip *chip)
{
static const struct zd_ioreq32 ioreqs[] = {
- { CR_ZD1211_RETRY_MAX, 0x2 },
+ { CR_ZD1211_RETRY_MAX, ZD1211_RETRY_COUNT },
{ CR_RX_THRESHOLD, 0x000c0640 },
};
@@ -767,7 +767,7 @@ static int zd1211_hw_init_hmac(struct zd_chip *chip)
static int zd1211b_hw_init_hmac(struct zd_chip *chip)
{
static const struct zd_ioreq32 ioreqs[] = {
- { CR_ZD1211B_RETRY_MAX, 0x02020202 },
+ { CR_ZD1211B_RETRY_MAX, ZD1211B_RETRY_COUNT },
{ CR_ZD1211B_CWIN_MAX_MIN_AC0, 0x007f003f },
{ CR_ZD1211B_CWIN_MAX_MIN_AC1, 0x007f003f },
{ CR_ZD1211B_CWIN_MAX_MIN_AC2, 0x003f001f },
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index 678c139a840..9fd8f3508d6 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -642,13 +642,29 @@ enum {
#define CR_ZD1211B_TXOP CTL_REG(0x0b20)
#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28)
+/* Value for CR_ZD1211_RETRY_MAX & CR_ZD1211B_RETRY_MAX. Vendor driver uses 2,
+ * we use 0. The first rate is tried (count+2), then all next rates are tried
+ * twice, until 1 Mbits is tried. */
+#define ZD1211_RETRY_COUNT 0
+#define ZD1211B_RETRY_COUNT \
+ (ZD1211_RETRY_COUNT << 0)| \
+ (ZD1211_RETRY_COUNT << 8)| \
+ (ZD1211_RETRY_COUNT << 16)| \
+ (ZD1211_RETRY_COUNT << 24)
+
/* Used to detect PLL lock */
#define UW2453_INTR_REG ((zd_addr_t)0x85c1)
#define CWIN_SIZE 0x007f043f
-#define HWINT_ENABLED 0x004f0000
+#define HWINT_ENABLED \
+ (INT_TX_COMPLETE_EN| \
+ INT_RX_COMPLETE_EN| \
+ INT_RETRY_FAIL_EN| \
+ INT_WAKEUP_EN| \
+ INT_CFG_NEXT_BCN_EN)
+
#define HWINT_DISABLED 0
#define E2P_PWR_INT_GUARD 8
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6d666359a42..cf51e8f8174 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -88,6 +88,34 @@ static const struct ieee80211_rate zd_rates[] = {
.flags = 0 },
};
+/*
+ * Zydas retry rates table. Each line is listed in the same order as
+ * in zd_rates[] and contains all the rate used when a packet is sent
+ * starting with a given rates. Let's consider an example :
+ *
+ * "11 Mbits : 4, 3, 2, 1, 0" means :
+ * - packet is sent using 4 different rates
+ * - 1st rate is index 3 (ie 11 Mbits)
+ * - 2nd rate is index 2 (ie 5.5 Mbits)
+ * - 3rd rate is index 1 (ie 2 Mbits)
+ * - 4th rate is index 0 (ie 1 Mbits)
+ */
+
+static const struct tx_retry_rate zd_retry_rates[] = {
+ { /* 1 Mbits */ 1, { 0 }},
+ { /* 2 Mbits */ 2, { 1, 0 }},
+ { /* 5.5 Mbits */ 3, { 2, 1, 0 }},
+ { /* 11 Mbits */ 4, { 3, 2, 1, 0 }},
+ { /* 6 Mbits */ 5, { 4, 3, 2, 1, 0 }},
+ { /* 9 Mbits */ 6, { 5, 4, 3, 2, 1, 0}},
+ { /* 12 Mbits */ 5, { 6, 3, 2, 1, 0 }},
+ { /* 18 Mbits */ 6, { 7, 6, 3, 2, 1, 0 }},
+ { /* 24 Mbits */ 6, { 8, 6, 3, 2, 1, 0 }},
+ { /* 36 Mbits */ 7, { 9, 8, 6, 3, 2, 1, 0 }},
+ { /* 48 Mbits */ 8, {10, 9, 8, 6, 3, 2, 1, 0 }},
+ { /* 54 Mbits */ 9, {11, 10, 9, 8, 6, 3, 2, 1, 0 }}
+};
+
static const struct ieee80211_channel zd_channels[] = {
{ .center_freq = 2412, .hw_value = 1 },
{ .center_freq = 2417, .hw_value = 2 },
@@ -282,7 +310,7 @@ static void zd_op_stop(struct ieee80211_hw *hw)
}
/**
- * tx_status - reports tx status of a packet if required
+ * zd_mac_tx_status - reports tx status of a packet if required
* @hw - a &struct ieee80211_hw pointer
* @skb - a sk-buffer
* @flags: extra flags to set in the TX status info
@@ -295,15 +323,49 @@ static void zd_op_stop(struct ieee80211_hw *hw)
*
* If no status information has been requested, the skb is freed.
*/
-static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
- int ackssi, bool success)
+static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
+ int ackssi, struct tx_status *tx_status)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int i;
+ int success = 1, retry = 1;
+ int first_idx;
+ const struct tx_retry_rate *retries;
ieee80211_tx_info_clear_status(info);
- if (success)
+ if (tx_status) {
+ success = !tx_status->failure;
+ retry = tx_status->retry + success;
+ }
+
+ if (success) {
+ /* success */
info->flags |= IEEE80211_TX_STAT_ACK;
+ } else {
+ /* failure */
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
+ }
+
+ first_idx = info->status.rates[0].idx;
+ ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates));
+ retries = &zd_retry_rates[first_idx];
+ ZD_ASSERT(0<=retry && retry<=retries->count);
+
+ info->status.rates[0].idx = retries->rate[0];
+ info->status.rates[0].count = 1; // (retry > 1 ? 2 : 1);
+
+ for (i=1; i<IEEE80211_TX_MAX_RATES-1 && i<retry; i++) {
+ info->status.rates[i].idx = retries->rate[i];
+ info->status.rates[i].count = 1; // ((i==retry-1) && success ? 1:2);
+ }
+ for (; i<IEEE80211_TX_MAX_RATES && i<retry; i++) {
+ info->status.rates[i].idx = retries->rate[retry-1];
+ info->status.rates[i].count = 1; // (success ? 1:2);
+ }
+ if (i<IEEE80211_TX_MAX_RATES)
+ info->status.rates[i].idx = -1; /* terminate */
+
info->status.ack_signal = ackssi;
ieee80211_tx_status_irqsafe(hw, skb);
}
@@ -312,20 +374,83 @@ static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
* zd_mac_tx_failed - callback for failed frames
* @dev: the mac80211 wireless device
*
- * This function is called if a frame couldn't be succesfully be
+ * This function is called if a frame couldn't be successfully be
* transferred. The first frame from the tx queue, will be selected and
* reported as error to the upper layers.
*/
-void zd_mac_tx_failed(struct ieee80211_hw *hw)
+void zd_mac_tx_failed(struct urb *urb)
{
- struct sk_buff_head *q = &zd_hw_mac(hw)->ack_wait_queue;
+ struct ieee80211_hw * hw = zd_usb_to_hw(urb->context);
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct sk_buff_head *q = &mac->ack_wait_queue;
struct sk_buff *skb;
+ struct tx_status *tx_status = (struct tx_status *)urb->transfer_buffer;
+ unsigned long flags;
+ int success = !tx_status->failure;
+ int retry = tx_status->retry + success;
+ int found = 0;
+ int i, position = 0;
- skb = skb_dequeue(q);
- if (skb == NULL)
- return;
+ q = &mac->ack_wait_queue;
+ spin_lock_irqsave(&q->lock, flags);
+
+ skb_queue_walk(q, skb) {
+ struct ieee80211_hdr *tx_hdr;
+ struct ieee80211_tx_info *info;
+ int first_idx, final_idx;
+ const struct tx_retry_rate *retries;
+ u8 final_rate;
+
+ position ++;
+
+ /* if the hardware reports a failure and we had a 802.11 ACK
+ * pending, then we skip the first skb when searching for a
+ * matching frame */
+ if (tx_status->failure && mac->ack_pending &&
+ skb_queue_is_first(q, skb)) {
+ continue;
+ }
+
+ tx_hdr = (struct ieee80211_hdr *)skb->data;
+
+ /* we skip all frames not matching the reported destination */
+ if (unlikely(memcmp(tx_hdr->addr1, tx_status->mac, ETH_ALEN))) {
+ continue;
+ }
+
+ /* we skip all frames not matching the reported final rate */
- tx_status(hw, skb, 0, 0);
+ info = IEEE80211_SKB_CB(skb);
+ first_idx = info->status.rates[0].idx;
+ ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates));
+ retries = &zd_retry_rates[first_idx];
+ if (retry < 0 || retry > retries->count) {
+ continue;
+ }
+
+ ZD_ASSERT(0<=retry && retry<=retries->count);
+ final_idx = retries->rate[retry-1];
+ final_rate = zd_rates[final_idx].hw_value;
+
+ if (final_rate != tx_status->rate) {
+ continue;
+ }
+
+ found = 1;
+ break;
+ }
+
+ if (found) {
+ for (i=1; i<=position; i++) {
+ skb = __skb_dequeue(q);
+ zd_mac_tx_status(hw, skb,
+ mac->ack_pending ? mac->ack_signal : 0,
+ i == position ? tx_status : NULL);
+ mac->ack_pending = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&q->lock, flags);
}
/**
@@ -342,18 +467,27 @@ void zd_mac_tx_to_dev(struct sk_buff *skb, int error)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hw *hw = info->rate_driver_data[0];
+ struct zd_mac *mac = zd_hw_mac(hw);
+
+ ieee80211_tx_info_clear_status(info);
skb_pull(skb, sizeof(struct zd_ctrlset));
if (unlikely(error ||
(info->flags & IEEE80211_TX_CTL_NO_ACK))) {
- tx_status(hw, skb, 0, !error);
+ /*
+ * FIXME : do we need to fill in anything ?
+ */
+ ieee80211_tx_status_irqsafe(hw, skb);
} else {
- struct sk_buff_head *q =
- &zd_hw_mac(hw)->ack_wait_queue;
+ struct sk_buff_head *q = &mac->ack_wait_queue;
skb_queue_tail(q, skb);
- while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS)
- zd_mac_tx_failed(hw);
+ while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) {
+ zd_mac_tx_status(hw, skb_dequeue(q),
+ mac->ack_pending ? mac->ack_signal : 0,
+ NULL);
+ mac->ack_pending = 0;
+ }
}
}
@@ -606,27 +740,47 @@ fail:
static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
struct ieee80211_rx_status *stats)
{
+ struct zd_mac *mac = zd_hw_mac(hw);
struct sk_buff *skb;
struct sk_buff_head *q;
unsigned long flags;
+ int found = 0;
+ int i, position = 0;
if (!ieee80211_is_ack(rx_hdr->frame_control))
return 0;
- q = &zd_hw_mac(hw)->ack_wait_queue;
+ q = &mac->ack_wait_queue;
spin_lock_irqsave(&q->lock, flags);
skb_queue_walk(q, skb) {
struct ieee80211_hdr *tx_hdr;
+ position ++;
+
+ if (mac->ack_pending && skb_queue_is_first(q, skb))
+ continue;
+
tx_hdr = (struct ieee80211_hdr *)skb->data;
if (likely(!memcmp(tx_hdr->addr2, rx_hdr->addr1, ETH_ALEN)))
{
- __skb_unlink(skb, q);
- tx_status(hw, skb, stats->signal, 1);
- goto out;
+ found = 1;
+ break;
}
}
-out:
+
+ if (found) {
+ for (i=1; i<position; i++) {
+ skb = __skb_dequeue(q);
+ zd_mac_tx_status(hw, skb,
+ mac->ack_pending ? mac->ack_signal : 0,
+ NULL);
+ mac->ack_pending = 0;
+ }
+
+ mac->ack_pending = 1;
+ mac->ack_signal = stats->signal;
+ }
+
spin_unlock_irqrestore(&q->lock, flags);
return 1;
}
@@ -709,6 +863,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
skb_reserve(skb, 2);
}
+ /* FIXME : could we avoid this big memcpy ? */
memcpy(skb_put(skb, length), buffer, length);
memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
@@ -999,7 +1154,14 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
hw->queues = 1;
hw->extra_tx_headroom = sizeof(struct zd_ctrlset);
+ /*
+ * Tell mac80211 that we support multi rate retries
+ */
+ hw->max_rates = IEEE80211_TX_MAX_RATES;
+ hw->max_rate_tries = 18; /* 9 rates * 2 retries/rate */
+
skb_queue_head_init(&mac->ack_wait_queue);
+ mac->ack_pending = 0;
zd_chip_init(&mac->chip, hw, intf);
housekeeping_init(mac);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index 7c2759118d1..630c298a730 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -140,6 +140,21 @@ struct rx_status {
#define ZD_RX_CRC16_ERROR 0x40
#define ZD_RX_ERROR 0x80
+struct tx_retry_rate {
+ int count; /* number of valid element in rate[] array */
+ int rate[10]; /* retry rates, described by an index in zd_rates[] */
+};
+
+struct tx_status {
+ u8 type; /* must always be 0x01 : USB_INT_TYPE */
+ u8 id; /* must always be 0xa0 : USB_INT_ID_RETRY_FAILED */
+ u8 rate;
+ u8 pad;
+ u8 mac[ETH_ALEN];
+ u8 retry;
+ u8 failure;
+} __attribute__((packed));
+
enum mac_flags {
MAC_FIXED_CHANNEL = 0x01,
};
@@ -150,7 +165,7 @@ struct housekeeping {
#define ZD_MAC_STATS_BUFFER_SIZE 16
-#define ZD_MAC_MAX_ACK_WAITERS 10
+#define ZD_MAC_MAX_ACK_WAITERS 50
struct zd_mac {
struct zd_chip chip;
@@ -184,6 +199,12 @@ struct zd_mac {
/* whether to pass control frames to stack */
unsigned int pass_ctrl:1;
+
+ /* whether we have received a 802.11 ACK that is pending */
+ unsigned int ack_pending:1;
+
+ /* signal strength of the last 802.11 ACK received */
+ int ack_signal;
};
#define ZD_REGDOMAIN_FCC 0x10
@@ -279,7 +300,7 @@ int zd_mac_preinit_hw(struct ieee80211_hw *hw);
int zd_mac_init_hw(struct ieee80211_hw *hw);
int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length);
-void zd_mac_tx_failed(struct ieee80211_hw *hw);
+void zd_mac_tx_failed(struct urb *urb);
void zd_mac_tx_to_dev(struct sk_buff *skb, int error);
#ifdef DEBUG
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 23a6a6d4863..ac19ecd19cf 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -318,6 +318,13 @@ error:
return r;
}
+MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ur");
+MODULE_FIRMWARE(FW_ZD1211_PREFIX "ur");
+MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ub");
+MODULE_FIRMWARE(FW_ZD1211_PREFIX "ub");
+MODULE_FIRMWARE(FW_ZD1211B_PREFIX "uphr");
+MODULE_FIRMWARE(FW_ZD1211_PREFIX "uphr");
+
/* Read data from device address space using "firmware interface" which does
* not require firmware to be loaded. */
int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len)
@@ -419,7 +426,7 @@ static void int_urb_complete(struct urb *urb)
handle_regs_int(urb);
break;
case USB_INT_ID_RETRY_FAILED:
- zd_mac_tx_failed(zd_usb_to_hw(urb->context));
+ zd_mac_tx_failed(urb);
break;
default:
dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb,
@@ -553,6 +560,8 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
if (length < sizeof(struct rx_length_info)) {
/* It's not a complete packet anyhow. */
+ printk("%s: invalid, small RX packet : %d\n",
+ __func__, length);
return;
}
length_info = (struct rx_length_info *)
diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c
index 83a044dbd1d..8c777ba4e2b 100644
--- a/drivers/net/xilinx_emaclite.c
+++ b/drivers/net/xilinx_emaclite.c
@@ -660,7 +660,7 @@ static int xemaclite_open(struct net_device *dev)
xemaclite_set_mac_address(lp, dev->dev_addr);
/* Grab the IRQ */
- retval = request_irq(dev->irq, &xemaclite_interrupt, 0, dev->name, dev);
+ retval = request_irq(dev->irq, xemaclite_interrupt, 0, dev->name, dev);
if (retval) {
dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n",
dev->irq);
diff --git a/drivers/net/xtsonic.c b/drivers/net/xtsonic.c
index 0c44135c0b1..389ba9df712 100644
--- a/drivers/net/xtsonic.c
+++ b/drivers/net/xtsonic.c
@@ -92,7 +92,7 @@ static unsigned short known_revisions[] =
static int xtsonic_open(struct net_device *dev)
{
- if (request_irq(dev->irq,&sonic_interrupt,IRQF_DISABLED,"sonic",dev)) {
+ if (request_irq(dev->irq,sonic_interrupt,IRQF_DISABLED,"sonic",dev)) {
printk(KERN_ERR "%s: unable to get IRQ %d.\n",
dev->name, dev->irq);
return -EAGAIN;
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index 40ad0dee040..0f773a9a3ff 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -579,7 +579,7 @@ static int yellowfin_open(struct net_device *dev)
/* Reset the chip. */
iowrite32(0x80000000, ioaddr + DMACtrl);
- ret = request_irq(dev->irq, &yellowfin_interrupt, IRQF_SHARED, dev->name, dev);
+ ret = request_irq(dev->irq, yellowfin_interrupt, IRQF_SHARED, dev->name, dev);
if (ret)
return ret;
@@ -944,8 +944,8 @@ static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance)
dev_kfree_skb_irq(skb);
yp->tx_skbuff[entry] = NULL;
}
- if (yp->tx_full
- && yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) {
+ if (yp->tx_full &&
+ yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) {
/* The ring is no longer full, clear tbusy. */
yp->tx_full = 0;
netif_wake_queue(dev);
@@ -1014,8 +1014,8 @@ static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance)
}
#endif
- if (yp->tx_full
- && yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) {
+ if (yp->tx_full &&
+ yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) {
/* The ring is no longer full, clear tbusy. */
yp->tx_full = 0;
netif_wake_queue(dev);
diff --git a/drivers/net/znet.c b/drivers/net/znet.c
index b4234733375..bc5ae0f6e93 100644
--- a/drivers/net/znet.c
+++ b/drivers/net/znet.c
@@ -103,8 +103,7 @@
#include <asm/io.h>
#include <asm/dma.h>
-/* This include could be elsewhere, since it is not wireless specific */
-#include "wireless/i82593.h"
+#include <linux/i82593.h>
static char version[] __initdata = "znet.c:v1.02 9/23/94 becker@scyld.com\n";
@@ -170,7 +169,7 @@ static int znet_request_resources (struct net_device *dev)
{
struct znet_private *znet = netdev_priv(dev);
- if (request_irq (dev->irq, &znet_interrupt, 0, "ZNet", dev))
+ if (request_irq (dev->irq, znet_interrupt, 0, "ZNet", dev))
goto failed;
if (request_dma (znet->rx_dma, "ZNet rx"))
goto free_irq;
@@ -698,8 +697,8 @@ static void znet_rx(struct net_device *dev)
the same area of the backwards links we now have. This allows us to
pass packets to the upper layers in the order they were received --
important for fast-path sequential operations. */
- while (znet->rx_start + cur_frame_end_offset != znet->rx_cur
- && ++boguscount < 5) {
+ while (znet->rx_start + cur_frame_end_offset != znet->rx_cur &&
+ ++boguscount < 5) {
unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
int count, status;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index ddf224d456b..e6627b2320f 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -9,7 +9,8 @@
*
* Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net
*
- * Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell.
+ * Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and
+ * Grant Likely.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -82,6 +83,29 @@ struct property *of_find_property(const struct device_node *np,
}
EXPORT_SYMBOL(of_find_property);
+/**
+ * of_find_all_nodes - Get next node in global list
+ * @prev: Previous node or NULL to start iteration
+ * of_node_put() will be called on it
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_all_nodes(struct device_node *prev)
+{
+ struct device_node *np;
+
+ read_lock(&devtree_lock);
+ np = prev ? prev->allnext : allnodes;
+ for (; np != NULL; np = np->allnext)
+ if (of_node_get(np))
+ break;
+ of_node_put(prev);
+ read_unlock(&devtree_lock);
+ return np;
+}
+EXPORT_SYMBOL(of_find_all_nodes);
+
/*
* Find a property with a given name for a given node
* and return the value.
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
index a6b4a5a53d4..f511e70d454 100644
--- a/drivers/parisc/ccio-dma.c
+++ b/drivers/parisc/ccio-dma.c
@@ -650,7 +650,7 @@ ccio_clear_io_tlb(struct ioc *ioc, dma_addr_t iovp, size_t byte_cnt)
* Mark the I/O Pdir entries invalid and blow away the corresponding I/O
* TLB entries.
*
- * FIXME: at some threshhold it might be "cheaper" to just blow
+ * FIXME: at some threshold it might be "cheaper" to just blow
* away the entire I/O TLB instead of individual entries.
*
* FIXME: Uturn has 256 TLB entries. We don't need to purge every
diff --git a/drivers/parisc/eisa_eeprom.c b/drivers/parisc/eisa_eeprom.c
index 8c0b26e9b98..cce00ed81f3 100644
--- a/drivers/parisc/eisa_eeprom.c
+++ b/drivers/parisc/eisa_eeprom.c
@@ -75,17 +75,8 @@ static ssize_t eisa_eeprom_read(struct file * file,
return ret;
}
-static int eisa_eeprom_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- return -ENOTTY;
-}
-
static int eisa_eeprom_open(struct inode *inode, struct file *file)
{
- cycle_kernel_lock();
-
if (file->f_mode & FMODE_WRITE)
return -EINVAL;
@@ -104,7 +95,6 @@ static const struct file_operations eisa_eeprom_fops = {
.owner = THIS_MODULE,
.llseek = eisa_eeprom_llseek,
.read = eisa_eeprom_read,
- .ioctl = eisa_eeprom_ioctl,
.open = eisa_eeprom_open,
.release = eisa_eeprom_release,
};
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index 9581d361945..79caf1ca4a2 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -352,11 +352,9 @@ static __inline__ int led_get_net_activity(void)
rx_total = tx_total = 0;
- /* we are running as a workqueue task, so locking dev_base
- * for reading should be OK */
- read_lock(&dev_base_lock);
+ /* we are running as a workqueue task, so we can use an RCU lookup */
rcu_read_lock();
- for_each_netdev(&init_net, dev) {
+ for_each_netdev_rcu(&init_net, dev) {
const struct net_device_stats *stats;
struct in_device *in_dev = __in_dev_get_rcu(dev);
if (!in_dev || !in_dev->ifa_list)
@@ -368,7 +366,6 @@ static __inline__ int led_get_net_activity(void)
tx_total += stats->tx_packets;
}
rcu_read_unlock();
- read_unlock(&dev_base_lock);
retval = 0;
diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c
index 8fdfa4f537a..7dd370fa343 100644
--- a/drivers/parport/parport_cs.c
+++ b/drivers/parport/parport_cs.c
@@ -67,14 +67,6 @@ MODULE_LICENSE("Dual MPL/GPL");
INT_MODULE_PARM(epp_mode, 1);
-#ifdef PCMCIA_DEBUG
-INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"parport_cs.c 1.29 2002/10/11 06:57:41 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -103,7 +95,7 @@ static int parport_probe(struct pcmcia_device *link)
{
parport_info_t *info;
- DEBUG(0, "parport_attach()\n");
+ dev_dbg(&link->dev, "parport_attach()\n");
/* Create new parport device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -114,7 +106,6 @@ static int parport_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -132,7 +123,7 @@ static int parport_probe(struct pcmcia_device *link)
static void parport_detach(struct pcmcia_device *link)
{
- DEBUG(0, "parport_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "parport_detach\n");
parport_cs_release(link);
@@ -147,9 +138,6 @@ static void parport_detach(struct pcmcia_device *link)
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int parport_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -178,18 +166,20 @@ static int parport_config(struct pcmcia_device *link)
{
parport_info_t *info = link->priv;
struct parport *p;
- int last_ret, last_fn;
+ int ret;
- DEBUG(0, "parport_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "parport_config\n");
- last_ret = pcmcia_loop_config(link, parport_config_check, NULL);
- if (last_ret) {
- cs_error(link, RequestIO, last_ret);
+ ret = pcmcia_loop_config(link, parport_config_check, NULL);
+ if (ret)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
p = parport_pc_probe_port(link->io.BasePort1, link->io.BasePort2,
link->irq.AssignedIRQ, PARPORT_DMA_NONE,
@@ -213,8 +203,6 @@ static int parport_config(struct pcmcia_device *link)
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
parport_cs_release(link);
return -ENODEV;
@@ -232,7 +220,7 @@ static void parport_cs_release(struct pcmcia_device *link)
{
parport_info_t *info = link->priv;
- DEBUG(0, "parport_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "parport_release\n");
if (info->ndev) {
struct parport *p = info->port;
diff --git a/drivers/parport/parport_mfc3.c b/drivers/parport/parport_mfc3.c
index 6dec9ba5ed2..362db31d8ca 100644
--- a/drivers/parport/parport_mfc3.c
+++ b/drivers/parport/parport_mfc3.c
@@ -386,7 +386,7 @@ static void __exit parport_mfc3_exit(void)
if (!this_port[i])
continue;
parport_remove_port(this_port[i]);
- if (!this_port[i]->irq != PARPORT_IRQ_NONE) {
+ if (this_port[i]->irq != PARPORT_IRQ_NONE) {
if (--use_cnt == 0)
free_irq(IRQ_AMIGA_PORTS, &pp_mfc3_ops);
}
diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c
index 8eefe56f1cb..3f56bc086cb 100644
--- a/drivers/parport/procfs.c
+++ b/drivers/parport/procfs.c
@@ -233,10 +233,10 @@ static int do_hardware_modes (ctl_table *table, int write,
return copy_to_user(result, buffer, len) ? -EFAULT : 0;
}
-#define PARPORT_PORT_DIR(CHILD) { .ctl_name = 0, .procname = NULL, .mode = 0555, .child = CHILD }
-#define PARPORT_PARPORT_DIR(CHILD) { .ctl_name = DEV_PARPORT, .procname = "parport", \
+#define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD }
+#define PARPORT_PARPORT_DIR(CHILD) { .procname = "parport", \
.mode = 0555, .child = CHILD }
-#define PARPORT_DEV_DIR(CHILD) { .ctl_name = CTL_DEV, .procname = "dev", .mode = 0555, .child = CHILD }
+#define PARPORT_DEV_DIR(CHILD) { .procname = "dev", .mode = 0555, .child = CHILD }
#define PARPORT_DEVICES_ROOT_DIR { .procname = "devices", \
.mode = 0555, .child = NULL }
@@ -270,7 +270,7 @@ static const struct parport_sysctl_table parport_sysctl_template = {
.data = NULL,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = (void*) &parport_min_spintime_value,
.extra2 = (void*) &parport_max_spintime_value
},
@@ -279,28 +279,28 @@ static const struct parport_sysctl_table parport_sysctl_template = {
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_hardware_base_addr
+ .proc_handler = do_hardware_base_addr
},
{
.procname = "irq",
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_hardware_irq
+ .proc_handler = do_hardware_irq
},
{
.procname = "dma",
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_hardware_dma
+ .proc_handler = do_hardware_dma
},
{
.procname = "modes",
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_hardware_modes
+ .proc_handler = do_hardware_modes
},
PARPORT_DEVICES_ROOT_DIR,
#ifdef CONFIG_PARPORT_1284
@@ -309,35 +309,35 @@ static const struct parport_sysctl_table parport_sysctl_template = {
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_autoprobe
+ .proc_handler = do_autoprobe
},
{
.procname = "autoprobe0",
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_autoprobe
+ .proc_handler = do_autoprobe
},
{
.procname = "autoprobe1",
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_autoprobe
+ .proc_handler = do_autoprobe
},
{
.procname = "autoprobe2",
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_autoprobe
+ .proc_handler = do_autoprobe
},
{
.procname = "autoprobe3",
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_autoprobe
+ .proc_handler = do_autoprobe
},
#endif /* IEEE 1284 support */
{}
@@ -348,7 +348,7 @@ static const struct parport_sysctl_table parport_sysctl_template = {
.data = NULL,
.maxlen = 0,
.mode = 0444,
- .proc_handler = &do_active_device
+ .proc_handler = do_active_device
},
{}
},
@@ -386,14 +386,13 @@ parport_device_sysctl_template = {
.data = NULL,
.maxlen = sizeof(unsigned long),
.mode = 0644,
- .proc_handler = &proc_doulongvec_ms_jiffies_minmax,
+ .proc_handler = proc_doulongvec_ms_jiffies_minmax,
.extra1 = (void*) &parport_min_timeslice_value,
.extra2 = (void*) &parport_max_timeslice_value
},
},
{
{
- .ctl_name = 0,
.procname = NULL,
.data = NULL,
.maxlen = 0,
@@ -438,7 +437,7 @@ parport_default_sysctl_table = {
.data = &parport_default_timeslice,
.maxlen = sizeof(parport_default_timeslice),
.mode = 0644,
- .proc_handler = &proc_doulongvec_ms_jiffies_minmax,
+ .proc_handler = proc_doulongvec_ms_jiffies_minmax,
.extra1 = (void*) &parport_min_timeslice_value,
.extra2 = (void*) &parport_max_timeslice_value
},
@@ -447,7 +446,7 @@ parport_default_sysctl_table = {
.data = &parport_default_spintime,
.maxlen = sizeof(parport_default_spintime),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = (void*) &parport_min_spintime_value,
.extra2 = (void*) &parport_max_spintime_value
},
@@ -455,7 +454,6 @@ parport_default_sysctl_table = {
},
{
{
- .ctl_name = DEV_PARPORT_DEFAULT,
.procname = "default",
.mode = 0555,
.child = parport_default_sysctl_table.vars
@@ -495,7 +493,6 @@ int parport_proc_register(struct parport *port)
t->vars[6 + i].extra2 = &port->probe_info[i];
t->port_dir[0].procname = port->name;
- t->port_dir[0].ctl_name = 0;
t->port_dir[0].child = t->vars;
t->parport_dir[0].child = t->port_dir;
@@ -534,11 +531,9 @@ int parport_device_proc_register(struct pardevice *device)
t->dev_dir[0].child = t->parport_dir;
t->parport_dir[0].child = t->port_dir;
t->port_dir[0].procname = port->name;
- t->port_dir[0].ctl_name = 0;
t->port_dir[0].child = t->devices_root_dir;
t->devices_root_dir[0].child = t->device_dir;
- t->device_dir[0].ctl_name = 0;
t->device_dir[0].procname = device->name;
t->device_dir[0].child = t->vars;
t->vars[0].data = &device->timeslice;
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index b952ebc7a78..416f6ac65b7 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -645,10 +645,13 @@ void __init detect_intel_iommu(void)
"x2apic and Intr-remapping.\n");
#endif
#ifdef CONFIG_DMAR
- if (ret && !no_iommu && !iommu_detected && !swiotlb &&
- !dmar_disabled)
+ if (ret && !no_iommu && !iommu_detected && !dmar_disabled)
iommu_detected = 1;
#endif
+#ifdef CONFIG_X86
+ if (ret)
+ x86_init.iommu.iommu_init = intel_iommu_init;
+#endif
}
early_acpi_os_unmap_memory(dmar_tbl, dmar_tbl_size);
dmar_tbl = NULL;
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index a73028ec52e..0f32571b94d 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -471,7 +471,7 @@ int acpi_pci_detect_ejectable(acpi_handle handle)
return found;
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
- check_hotplug, (void *)&found, NULL);
+ check_hotplug, NULL, (void *)&found, NULL);
return found;
}
EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable);
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 58d25a163a8..df1b0ea089d 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -266,7 +266,7 @@ static int detect_ejectable_slots(acpi_handle handle)
int found = acpi_pci_detect_ejectable(handle);
if (!found) {
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- is_pci_dock_device, (void *)&found, NULL);
+ is_pci_dock_device, NULL, (void *)&found, NULL);
}
return found;
}
@@ -281,7 +281,7 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)
/* register all slot objects under this bridge */
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1,
- register_slot, bridge, NULL);
+ register_slot, NULL, bridge, NULL);
if (ACPI_FAILURE(status)) {
list_del(&bridge->list);
return;
@@ -447,7 +447,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
/* search P2P bridges under this p2p bridge */
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- find_p2p_bridge, NULL, NULL);
+ find_p2p_bridge, NULL, NULL, NULL);
if (ACPI_FAILURE(status))
warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
@@ -485,7 +485,7 @@ static int add_bridge(acpi_handle handle)
/* search P2P bridges under this host bridge */
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- find_p2p_bridge, NULL, NULL);
+ find_p2p_bridge, NULL, NULL, NULL);
if (ACPI_FAILURE(status))
warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
@@ -573,7 +573,7 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
/* cleanup p2p bridges under this P2P bridge
in a depth-first manner */
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
- cleanup_p2p_bridge, NULL, NULL);
+ cleanup_p2p_bridge, NULL, NULL, NULL);
bridge = acpiphp_handle_to_bridge(handle);
if (bridge)
@@ -589,7 +589,7 @@ static void remove_bridge(acpi_handle handle)
/* cleanup p2p bridges under this host bridge
in a depth-first manner */
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- (u32)1, cleanup_p2p_bridge, NULL, NULL);
+ (u32)1, cleanup_p2p_bridge, NULL, NULL, NULL);
/*
* On root bridges with hotplug slots directly underneath (ie,
@@ -778,7 +778,7 @@ static int acpiphp_configure_ioapics(acpi_handle handle)
{
ioapic_add(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- ACPI_UINT32_MAX, ioapic_add, NULL, NULL);
+ ACPI_UINT32_MAX, ioapic_add, NULL, NULL, NULL);
return 0;
}
@@ -786,7 +786,7 @@ static int acpiphp_unconfigure_ioapics(acpi_handle handle)
{
ioapic_remove(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
+ ACPI_UINT32_MAX, ioapic_remove, NULL, NULL, NULL);
return 0;
}
@@ -1367,7 +1367,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont
bridge = acpiphp_handle_to_bridge(handle);
if (type == ACPI_NOTIFY_BUS_CHECK) {
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX,
- count_sub_bridges, &num_sub_bridges, NULL);
+ count_sub_bridges, NULL, &num_sub_bridges, NULL);
}
if (!bridge && !num_sub_bridges) {
@@ -1388,7 +1388,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont
}
if (num_sub_bridges)
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
- ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL);
+ ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
break;
case ACPI_NOTIFY_DEVICE_CHECK:
@@ -1512,7 +1512,7 @@ int __init acpiphp_glue_init(void)
int num = 0;
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, find_root_bridges, &num, NULL);
+ ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL);
if (num <= 0)
return -1;
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index e7be66dbac2..aa5df485f8c 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -434,7 +434,7 @@ static int __init ibm_acpiphp_init(void)
dbg("%s\n", __func__);
if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
- ACPI_UINT32_MAX, ibm_find_acpi_device,
+ ACPI_UINT32_MAX, ibm_find_acpi_device, NULL,
&ibm_acpi_handle, NULL) != FOUND_APCI) {
err("%s: acpi_walk_namespace failed\n", __func__);
retval = -ENODEV;
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 1840a0578a4..9261327b49f 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -3266,7 +3266,7 @@ int __init intel_iommu_init(void)
* Check the need for DMA-remapping initialization now.
* Above initialization will also be used by Interrupt-remapping.
*/
- if (no_iommu || swiotlb || dmar_disabled)
+ if (no_iommu || dmar_disabled)
return -ENODEV;
iommu_init_mempool();
@@ -3287,7 +3287,9 @@ int __init intel_iommu_init(void)
"PCI-DMA: Intel(R) Virtualization Technology for Directed I/O\n");
init_timer(&unmap_timer);
- force_iommu = 1;
+#ifdef CONFIG_SWIOTLB
+ swiotlb = 0;
+#endif
dma_ops = &intel_dma_ops;
init_iommu_sysfs();
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c
index 0ed78a764de..3b3658669be 100644
--- a/drivers/pci/intr_remapping.c
+++ b/drivers/pci/intr_remapping.c
@@ -2,6 +2,7 @@
#include <linux/dmar.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
+#include <linux/hpet.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <asm/io_apic.h>
@@ -14,7 +15,8 @@
#include "pci.h"
static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
-static int ir_ioapic_num;
+static struct hpet_scope ir_hpet[MAX_HPET_TBS];
+static int ir_ioapic_num, ir_hpet_num;
int intr_remapping_enabled;
static int disable_intremap;
@@ -343,6 +345,16 @@ int flush_irte(int irq)
return rc;
}
+struct intel_iommu *map_hpet_to_ir(u8 hpet_id)
+{
+ int i;
+
+ for (i = 0; i < MAX_HPET_TBS; i++)
+ if (ir_hpet[i].id == hpet_id)
+ return ir_hpet[i].iommu;
+ return NULL;
+}
+
struct intel_iommu *map_ioapic_to_ir(int apic)
{
int i;
@@ -470,6 +482,36 @@ int set_ioapic_sid(struct irte *irte, int apic)
return 0;
}
+int set_hpet_sid(struct irte *irte, u8 id)
+{
+ int i;
+ u16 sid = 0;
+
+ if (!irte)
+ return -1;
+
+ for (i = 0; i < MAX_HPET_TBS; i++) {
+ if (ir_hpet[i].id == id) {
+ sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn;
+ break;
+ }
+ }
+
+ if (sid == 0) {
+ pr_warning("Failed to set source-id of HPET block (%d)\n", id);
+ return -1;
+ }
+
+ /*
+ * Should really use SQ_ALL_16. Some platforms are broken.
+ * While we figure out the right quirks for these broken platforms, use
+ * SQ_13_IGNORE_3 for now.
+ */
+ set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid);
+
+ return 0;
+}
+
int set_msi_sid(struct irte *irte, struct pci_dev *dev)
{
struct pci_dev *bridge;
@@ -711,6 +753,34 @@ error:
return -1;
}
+static void ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope,
+ struct intel_iommu *iommu)
+{
+ struct acpi_dmar_pci_path *path;
+ u8 bus;
+ int count;
+
+ bus = scope->bus;
+ path = (struct acpi_dmar_pci_path *)(scope + 1);
+ count = (scope->length - sizeof(struct acpi_dmar_device_scope))
+ / sizeof(struct acpi_dmar_pci_path);
+
+ while (--count > 0) {
+ /*
+ * Access PCI directly due to the PCI
+ * subsystem isn't initialized yet.
+ */
+ bus = read_pci_config_byte(bus, path->dev, path->fn,
+ PCI_SECONDARY_BUS);
+ path++;
+ }
+ ir_hpet[ir_hpet_num].bus = bus;
+ ir_hpet[ir_hpet_num].devfn = PCI_DEVFN(path->dev, path->fn);
+ ir_hpet[ir_hpet_num].iommu = iommu;
+ ir_hpet[ir_hpet_num].id = scope->enumeration_id;
+ ir_hpet_num++;
+}
+
static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
struct intel_iommu *iommu)
{
@@ -740,8 +810,8 @@ static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope,
ir_ioapic_num++;
}
-static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
- struct intel_iommu *iommu)
+static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header,
+ struct intel_iommu *iommu)
{
struct acpi_dmar_hardware_unit *drhd;
struct acpi_dmar_device_scope *scope;
@@ -765,6 +835,17 @@ static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
drhd->address);
ir_parse_one_ioapic_scope(scope, iommu);
+ } else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) {
+ if (ir_hpet_num == MAX_HPET_TBS) {
+ printk(KERN_WARNING "Exceeded Max HPET blocks\n");
+ return -1;
+ }
+
+ printk(KERN_INFO "HPET id %d under DRHD base"
+ " 0x%Lx\n", scope->enumeration_id,
+ drhd->address);
+
+ ir_parse_one_hpet_scope(scope, iommu);
}
start += scope->length;
}
@@ -785,7 +866,7 @@ int __init parse_ioapics_under_ir(void)
struct intel_iommu *iommu = drhd->iommu;
if (ecap_ir_support(iommu->ecap)) {
- if (ir_parse_ioapic_scope(drhd->hdr, iommu))
+ if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu))
return -1;
ir_supported = 1;
diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h
index 63a263c1841..5662fecfee6 100644
--- a/drivers/pci/intr_remapping.h
+++ b/drivers/pci/intr_remapping.h
@@ -7,4 +7,11 @@ struct ioapic_scope {
unsigned int devfn; /* PCI devfn number */
};
+struct hpet_scope {
+ struct intel_iommu *iommu;
+ u8 id;
+ unsigned int bus;
+ unsigned int devfn;
+};
+
#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 17f38a781d4..cd5082d3ca1 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -17,24 +17,6 @@ menuconfig PCCARD
if PCCARD
-config PCMCIA_DEBUG
- bool "Enable PCCARD debugging"
- help
- Say Y here to enable PCMCIA subsystem debugging. You
- will need to choose the debugging level either via the
- kernel command line, or module options depending whether
- you build the PCMCIA as modules.
-
- The kernel command line options are:
- pcmcia_core.pc_debug=N
- pcmcia.pc_debug=N
- sa11xx_core.pc_debug=N
-
- The module option is called pc_debug=N
-
- In all the above examples, N is the debugging verbosity
- level.
-
config PCMCIA
tristate "16-bit PCMCIA support"
select CRC32
@@ -196,9 +178,13 @@ config PCMCIA_BCM63XX
tristate "bcm63xx pcmcia support"
depends on BCM63XX && PCMCIA
+config PCMCIA_SOC_COMMON
+ tristate
+
config PCMCIA_SA1100
tristate "SA1100 support"
depends on ARM && ARCH_SA1100 && PCMCIA
+ select PCMCIA_SOC_COMMON
help
Say Y here to include support for SA11x0-based PCMCIA or CF
sockets, found on HP iPAQs, Yopy, and other StrongARM(R)/
@@ -209,6 +195,7 @@ config PCMCIA_SA1100
config PCMCIA_SA1111
tristate "SA1111 support"
depends on ARM && ARCH_SA1100 && SA1111 && PCMCIA
+ select PCMCIA_SOC_COMMON
help
Say Y here to include support for SA1111-based PCMCIA or CF
sockets, found on the Jornada 720, Graphicsmaster and other
@@ -222,9 +209,28 @@ config PCMCIA_PXA2XX
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
|| ARCH_VIPER || ARCH_PXA_ESERIES || MACH_STARGATE2)
+ select PCMCIA_SOC_COMMON
help
Say Y here to include support for the PXA2xx PCMCIA controller
+config PCMCIA_DEBUG
+ bool "Enable debugging"
+ depends on (PCMCIA_SA1111 || PCMCIA_SA1100 || PCMCIA_PXA2XX)
+ help
+ Say Y here to enable debugging for the SoC PCMCIA layer.
+ You will need to choose the debugging level either via the
+ kernel command line, or module options depending whether
+ you build the drivers as modules.
+
+ The kernel command line options are:
+ sa11xx_core.pc_debug=N
+ pxa2xx_core.pc_debug=N
+
+ The module option is called pc_debug=N
+
+ In all the above examples, N is the debugging verbosity
+ level.
+
config PCMCIA_PROBE
bool
default y if ISA && !ARCH_SA1100 && !ARCH_CLPS711X && !PARISC
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index a03a38acd77..38293831399 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -22,8 +22,9 @@ obj-$(CONFIG_I82365) += i82365.o
obj-$(CONFIG_I82092) += i82092.o
obj-$(CONFIG_TCIC) += tcic.o
obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o
-obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_core.o sa1100_cs.o
-obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o
+obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o
+obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_base.o sa1100_cs.o
+obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_base.o sa1111_cs.o
obj-$(CONFIG_M32R_PCC) += m32r_pcc.o
obj-$(CONFIG_M32R_CFC) += m32r_cfc.o
obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o
@@ -35,9 +36,6 @@ obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o
obj-$(CONFIG_AT91_CF) += at91_cf.o
obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
-sa11xx_core-y += soc_common.o sa11xx_base.o
-pxa2xx_core-y += soc_common.o pxa2xx_base.o
-
au1x00_ss-y += au1000_generic.o
au1x00_ss-$(CONFIG_MIPS_PB1000) += au1000_pb1x00.o
au1x00_ss-$(CONFIG_MIPS_PB1100) += au1000_pb1x00.o
@@ -77,4 +75,4 @@ pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
-obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o $(pxa2xx-obj-y)
+obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index db77e1f3309..4cd70d05681 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -91,7 +91,7 @@ static u_int xlate_rom_addr(void __iomem *b, u_int addr)
static void cb_release_cis_mem(struct pcmcia_socket * s)
{
if (s->cb_cis_virt) {
- cs_dbg(s, 1, "cb_release_cis_mem()\n");
+ dev_dbg(&s->dev, "cb_release_cis_mem()\n");
iounmap(s->cb_cis_virt);
s->cb_cis_virt = NULL;
s->cb_cis_res = NULL;
@@ -132,7 +132,7 @@ int read_cb_mem(struct pcmcia_socket * s, int space, u_int addr, u_int len, void
struct pci_dev *dev;
struct resource *res;
- cs_dbg(s, 3, "read_cb_mem(%d, %#x, %u)\n", space, addr, len);
+ dev_dbg(&s->dev, "read_cb_mem(%d, %#x, %u)\n", space, addr, len);
dev = pci_get_slot(s->cb_dev->subordinate, 0);
if (!dev)
diff --git a/drivers/pcmcia/cirrus.h b/drivers/pcmcia/cirrus.h
index ecd4fc7f666..446a4576e73 100644
--- a/drivers/pcmcia/cirrus.h
+++ b/drivers/pcmcia/cirrus.h
@@ -30,16 +30,6 @@
#ifndef _LINUX_CIRRUS_H
#define _LINUX_CIRRUS_H
-#ifndef PCI_VENDOR_ID_CIRRUS
-#define PCI_VENDOR_ID_CIRRUS 0x1013
-#endif
-#ifndef PCI_DEVICE_ID_CIRRUS_6729
-#define PCI_DEVICE_ID_CIRRUS_6729 0x1100
-#endif
-#ifndef PCI_DEVICE_ID_CIRRUS_6832
-#define PCI_DEVICE_ID_CIRRUS_6832 0x1110
-#endif
-
#define PD67_MISC_CTL_1 0x16 /* Misc control 1 */
#define PD67_FIFO_CTL 0x17 /* FIFO control */
#define PD67_MISC_CTL_2 0x1E /* Misc control 2 */
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 6c4a4fc8363..8c1b73cf021 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -138,7 +138,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
void __iomem *sys, *end;
unsigned char *buf = ptr;
- cs_dbg(s, 3, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
if (attr & IS_INDIRECT) {
/* Indirect accesses use a bunch of special registers at fixed
@@ -190,7 +190,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
addr = 0;
}
}
- cs_dbg(s, 3, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
+ dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
*(u_char *)(ptr+0), *(u_char *)(ptr+1),
*(u_char *)(ptr+2), *(u_char *)(ptr+3));
return 0;
@@ -204,7 +204,7 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
void __iomem *sys, *end;
unsigned char *buf = ptr;
- cs_dbg(s, 3, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
if (attr & IS_INDIRECT) {
/* Indirect accesses use a bunch of special registers at fixed
@@ -584,7 +584,7 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
ofs += link[1] + 2;
}
if (i == MAX_TUPLES) {
- cs_dbg(s, 1, "cs: overrun in pcmcia_get_next_tuple\n");
+ dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n");
return -ENOSPC;
}
@@ -1440,7 +1440,7 @@ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
break;
}
if (ret)
- __cs_dbg(0, "parse_tuple failed %d\n", ret);
+ pr_debug("parse_tuple failed %d\n", ret);
return ret;
}
EXPORT_SYMBOL(pcmcia_parse_tuple);
@@ -1482,6 +1482,67 @@ done:
}
EXPORT_SYMBOL(pccard_read_tuple);
+
+/**
+ * pccard_loop_tuple() - loop over tuples in the CIS
+ * @s: the struct pcmcia_socket where the card is inserted
+ * @function: the device function we loop for
+ * @code: which CIS code shall we look for?
+ * @parse: buffer where the tuple shall be parsed (or NULL, if no parse)
+ * @priv_data: private data to be passed to the loop_tuple function.
+ * @loop_tuple: function to call for each CIS entry of type @function. IT
+ * gets passed the raw tuple, the paresed tuple (if @parse is
+ * set) and @priv_data.
+ *
+ * pccard_loop_tuple() loops over all CIS entries of type @function, and
+ * calls the @loop_tuple function for each entry. If the call to @loop_tuple
+ * returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
+ */
+int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
+ cisdata_t code, cisparse_t *parse, void *priv_data,
+ int (*loop_tuple) (tuple_t *tuple,
+ cisparse_t *parse,
+ void *priv_data))
+{
+ tuple_t tuple;
+ cisdata_t *buf;
+ int ret;
+
+ buf = kzalloc(256, GFP_KERNEL);
+ if (buf == NULL) {
+ dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+ return -ENOMEM;
+ }
+
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = 255;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = code;
+ tuple.Attributes = 0;
+
+ ret = pccard_get_first_tuple(s, function, &tuple);
+ while (!ret) {
+ if (pccard_get_tuple_data(s, &tuple))
+ goto next_entry;
+
+ if (parse)
+ if (pcmcia_parse_tuple(&tuple, parse))
+ goto next_entry;
+
+ ret = loop_tuple(&tuple, parse, priv_data);
+ if (!ret)
+ break;
+
+next_entry:
+ ret = pccard_get_next_tuple(s, function, &tuple);
+ }
+
+ kfree(buf);
+ return ret;
+}
+EXPORT_SYMBOL(pccard_loop_tuple);
+
+
/*======================================================================
This tries to determine if a card has a sensible CIS. It returns
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 698d75cda08..790af87a922 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -61,17 +61,6 @@ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */
/* Access speed for attribute memory windows */
INT_MODULE_PARM(cis_speed, 300); /* ns */
-#ifdef CONFIG_PCMCIA_DEBUG
-static int pc_debug;
-
-module_param(pc_debug, int, 0644);
-
-int cs_debug_level(int level)
-{
- return pc_debug > level;
-}
-#endif
-
socket_state_t dead_socket = {
.csc_mask = SS_DETECT,
@@ -190,7 +179,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
if (!socket || !socket->ops || !socket->dev.parent || !socket->resource_ops)
return -EINVAL;
- cs_dbg(socket, 0, "pcmcia_register_socket(0x%p)\n", socket->ops);
+ dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops);
spin_lock_init(&socket->lock);
@@ -262,6 +251,13 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
pcmcia_parse_events(socket, SS_DETECT);
+ /*
+ * Let's try to get the PCMCIA module for 16-bit PCMCIA support.
+ * If it fails, it doesn't matter -- we still have 32-bit CardBus
+ * support to offer, so this is not a failure mode.
+ */
+ request_module_nowait("pcmcia");
+
return 0;
err:
@@ -282,7 +278,7 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket)
if (!socket)
return;
- cs_dbg(socket, 0, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
+ dev_dbg(&socket->dev, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
if (socket->thread)
kthread_stop(socket->thread);
@@ -335,7 +331,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
if (s->state & SOCKET_CARDBUS)
return 0;
- cs_dbg(s, 1, "send_event(event %d, pri %d, callback 0x%p)\n",
+ dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
event, priority, s->callback);
if (!s->callback)
@@ -352,7 +348,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
static void socket_remove_drivers(struct pcmcia_socket *skt)
{
- cs_dbg(skt, 4, "remove_drivers\n");
+ dev_dbg(&skt->dev, "remove_drivers\n");
send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
}
@@ -361,7 +357,7 @@ static int socket_reset(struct pcmcia_socket *skt)
{
int status, i;
- cs_dbg(skt, 4, "reset\n");
+ dev_dbg(&skt->dev, "reset\n");
skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
skt->ops->set_socket(skt, &skt->socket);
@@ -383,7 +379,7 @@ static int socket_reset(struct pcmcia_socket *skt)
msleep(unreset_check * 10);
}
- cs_err(skt, "time out after reset.\n");
+ dev_printk(KERN_ERR, &skt->dev, "time out after reset.\n");
return -ETIMEDOUT;
}
@@ -397,7 +393,7 @@ static void socket_shutdown(struct pcmcia_socket *s)
{
int status;
- cs_dbg(s, 4, "shutdown\n");
+ dev_dbg(&s->dev, "shutdown\n");
socket_remove_drivers(s);
s->state &= SOCKET_INUSE | SOCKET_PRESENT;
@@ -432,7 +428,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
{
int status, i;
- cs_dbg(skt, 4, "setup\n");
+ dev_dbg(&skt->dev, "setup\n");
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT))
@@ -452,13 +448,15 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
}
if (status & SS_PENDING) {
- cs_err(skt, "voltage interrogation timed out.\n");
+ dev_printk(KERN_ERR, &skt->dev,
+ "voltage interrogation timed out.\n");
return -ETIMEDOUT;
}
if (status & SS_CARDBUS) {
if (!(skt->features & SS_CAP_CARDBUS)) {
- cs_err(skt, "cardbus cards are not supported.\n");
+ dev_printk(KERN_ERR, &skt->dev,
+ "cardbus cards are not supported.\n");
return -EINVAL;
}
skt->state |= SOCKET_CARDBUS;
@@ -472,7 +470,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
else if (!(status & SS_XVCARD))
skt->socket.Vcc = skt->socket.Vpp = 50;
else {
- cs_err(skt, "unsupported voltage key.\n");
+ dev_printk(KERN_ERR, &skt->dev, "unsupported voltage key.\n");
return -EIO;
}
@@ -489,7 +487,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
skt->ops->get_status(skt, &status);
if (!(status & SS_POWERON)) {
- cs_err(skt, "unable to apply power.\n");
+ dev_printk(KERN_ERR, &skt->dev, "unable to apply power.\n");
return -EIO;
}
@@ -509,7 +507,7 @@ static int socket_insert(struct pcmcia_socket *skt)
{
int ret;
- cs_dbg(skt, 4, "insert\n");
+ dev_dbg(&skt->dev, "insert\n");
if (!cs_socket_get(skt))
return -ENODEV;
@@ -529,7 +527,7 @@ static int socket_insert(struct pcmcia_socket *skt)
skt->state |= SOCKET_CARDBUS_CONFIG;
}
#endif
- cs_dbg(skt, 4, "insert done\n");
+ dev_dbg(&skt->dev, "insert done\n");
send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
} else {
@@ -576,7 +574,7 @@ static int socket_late_resume(struct pcmcia_socket *skt)
* FIXME: need a better check here for cardbus cards.
*/
if (verify_cis_cache(skt) != 0) {
- cs_dbg(skt, 4, "cis mismatch - different card\n");
+ dev_dbg(&skt->dev, "cis mismatch - different card\n");
socket_remove_drivers(skt);
destroy_cis_cache(skt);
/*
@@ -587,7 +585,7 @@ static int socket_late_resume(struct pcmcia_socket *skt)
msleep(200);
send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
} else {
- cs_dbg(skt, 4, "cis matches cache\n");
+ dev_dbg(&skt->dev, "cis matches cache\n");
send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
}
} else {
@@ -723,7 +721,7 @@ static int pccardd(void *__skt)
void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
{
unsigned long flags;
- cs_dbg(s, 4, "parse_events: events %08x\n", events);
+ dev_dbg(&s->dev, "parse_events: events %08x\n", events);
if (s->thread) {
spin_lock_irqsave(&s->thread_lock, flags);
s->thread_events |= events;
@@ -773,19 +771,22 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
{
int ret;
- cs_dbg(skt, 1, "resetting socket\n");
+ dev_dbg(&skt->dev, "resetting socket\n");
mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
+ dev_dbg(&skt->dev, "can't reset, not present\n");
ret = -ENODEV;
break;
}
if (skt->state & SOCKET_SUSPEND) {
+ dev_dbg(&skt->dev, "can't reset, suspended\n");
ret = -EBUSY;
break;
}
if (skt->state & SOCKET_CARDBUS) {
+ dev_dbg(&skt->dev, "can't reset, is cardbus\n");
ret = -EPERM;
break;
}
@@ -818,7 +819,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt)
{
int ret;
- cs_dbg(skt, 1, "suspending socket\n");
+ dev_dbg(&skt->dev, "suspending socket\n");
mutex_lock(&skt->skt_mutex);
do {
@@ -848,7 +849,7 @@ int pcmcia_resume_card(struct pcmcia_socket *skt)
{
int ret;
- cs_dbg(skt, 1, "waking up socket\n");
+ dev_dbg(&skt->dev, "waking up socket\n");
mutex_lock(&skt->skt_mutex);
do {
@@ -876,7 +877,7 @@ int pcmcia_eject_card(struct pcmcia_socket *skt)
{
int ret;
- cs_dbg(skt, 1, "user eject request\n");
+ dev_dbg(&skt->dev, "user eject request\n");
mutex_lock(&skt->skt_mutex);
do {
@@ -905,7 +906,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt)
{
int ret;
- cs_dbg(skt, 1, "user insert request\n");
+ dev_dbg(&skt->dev, "user insert request\n");
mutex_lock(&skt->skt_mutex);
do {
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index 1f4098f1354..3bc02d53a3a 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -107,28 +107,6 @@ static inline void cs_socket_put(struct pcmcia_socket *skt)
}
}
-#ifdef CONFIG_PCMCIA_DEBUG
-extern int cs_debug_level(int);
-
-#define cs_dbg(skt, lvl, fmt, arg...) do { \
- if (cs_debug_level(lvl)) \
- dev_printk(KERN_DEBUG, &skt->dev, \
- "cs: " fmt, ## arg); \
-} while (0)
-#define __cs_dbg(lvl, fmt, arg...) do { \
- if (cs_debug_level(lvl)) \
- printk(KERN_DEBUG \
- "cs: " fmt, ## arg); \
-} while (0)
-
-#else
-#define cs_dbg(skt, lvl, fmt, arg...) do { } while (0)
-#define __cs_dbg(lvl, fmt, arg...) do { } while (0)
-#endif
-
-#define cs_err(skt, fmt, arg...) \
- dev_printk(KERN_ERR, &skt->dev, "cs: " fmt, ## arg)
-
/*
* Stuff internal to module "pcmcia_core":
@@ -170,10 +148,6 @@ extern struct rw_semaphore pcmcia_socket_list_rwsem;
extern struct list_head pcmcia_socket_list;
extern struct class pcmcia_socket_class;
-int pcmcia_get_window(struct pcmcia_socket *s,
- window_handle_t *handle,
- int idx,
- win_req_t *req);
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr);
@@ -199,6 +173,22 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
const u8 *data, const size_t len);
int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *count);
+/* loop over CIS entries */
+int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
+ cisdata_t code, cisparse_t *parse, void *priv_data,
+ int (*loop_tuple) (tuple_t *tuple,
+ cisparse_t *parse,
+ void *priv_data));
+
+int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
+ tuple_t *tuple);
+
+int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
+ tuple_t *tuple);
+
+int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
+
+
/* rsrc_mgr.c */
int pcmcia_validate_mem(struct pcmcia_socket *s);
struct resource *pcmcia_find_io_region(unsigned long base,
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index f5b7079f13d..05893d41dd4 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -41,129 +41,11 @@ MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("PCMCIA Driver Services");
MODULE_LICENSE("GPL");
-#ifdef CONFIG_PCMCIA_DEBUG
-int ds_pc_debug;
-
-module_param_named(pc_debug, ds_pc_debug, int, 0644);
-
-#define ds_dbg(lvl, fmt, arg...) do { \
- if (ds_pc_debug > (lvl)) \
- printk(KERN_DEBUG "ds: " fmt , ## arg); \
-} while (0)
-#define ds_dev_dbg(lvl, dev, fmt, arg...) do { \
- if (ds_pc_debug > (lvl)) \
- dev_printk(KERN_DEBUG, dev, "ds: " fmt , ## arg); \
-} while (0)
-#else
-#define ds_dbg(lvl, fmt, arg...) do { } while (0)
-#define ds_dev_dbg(lvl, dev, fmt, arg...) do { } while (0)
-#endif
spinlock_t pcmcia_dev_list_lock;
/*====================================================================*/
-/* code which was in cs.c before */
-
-/* String tables for error messages */
-
-typedef struct lookup_t {
- const int key;
- const char *msg;
-} lookup_t;
-
-static const lookup_t error_table[] = {
- { 0, "Operation succeeded" },
- { -EIO, "Input/Output error" },
- { -ENODEV, "No card present" },
- { -EINVAL, "Bad parameter" },
- { -EACCES, "Configuration locked" },
- { -EBUSY, "Resource in use" },
- { -ENOSPC, "No more items" },
- { -ENOMEM, "Out of resource" },
-};
-
-
-static const lookup_t service_table[] = {
- { AccessConfigurationRegister, "AccessConfigurationRegister" },
- { AddSocketServices, "AddSocketServices" },
- { AdjustResourceInfo, "AdjustResourceInfo" },
- { CheckEraseQueue, "CheckEraseQueue" },
- { CloseMemory, "CloseMemory" },
- { DeregisterClient, "DeregisterClient" },
- { DeregisterEraseQueue, "DeregisterEraseQueue" },
- { GetCardServicesInfo, "GetCardServicesInfo" },
- { GetClientInfo, "GetClientInfo" },
- { GetConfigurationInfo, "GetConfigurationInfo" },
- { GetEventMask, "GetEventMask" },
- { GetFirstClient, "GetFirstClient" },
- { GetFirstRegion, "GetFirstRegion" },
- { GetFirstTuple, "GetFirstTuple" },
- { GetNextClient, "GetNextClient" },
- { GetNextRegion, "GetNextRegion" },
- { GetNextTuple, "GetNextTuple" },
- { GetStatus, "GetStatus" },
- { GetTupleData, "GetTupleData" },
- { MapMemPage, "MapMemPage" },
- { ModifyConfiguration, "ModifyConfiguration" },
- { ModifyWindow, "ModifyWindow" },
- { OpenMemory, "OpenMemory" },
- { ParseTuple, "ParseTuple" },
- { ReadMemory, "ReadMemory" },
- { RegisterClient, "RegisterClient" },
- { RegisterEraseQueue, "RegisterEraseQueue" },
- { RegisterMTD, "RegisterMTD" },
- { ReleaseConfiguration, "ReleaseConfiguration" },
- { ReleaseIO, "ReleaseIO" },
- { ReleaseIRQ, "ReleaseIRQ" },
- { ReleaseWindow, "ReleaseWindow" },
- { RequestConfiguration, "RequestConfiguration" },
- { RequestIO, "RequestIO" },
- { RequestIRQ, "RequestIRQ" },
- { RequestSocketMask, "RequestSocketMask" },
- { RequestWindow, "RequestWindow" },
- { ResetCard, "ResetCard" },
- { SetEventMask, "SetEventMask" },
- { ValidateCIS, "ValidateCIS" },
- { WriteMemory, "WriteMemory" },
- { BindDevice, "BindDevice" },
- { BindMTD, "BindMTD" },
- { ReportError, "ReportError" },
- { SuspendCard, "SuspendCard" },
- { ResumeCard, "ResumeCard" },
- { EjectCard, "EjectCard" },
- { InsertCard, "InsertCard" },
- { ReplaceCIS, "ReplaceCIS" }
-};
-
-const char *pcmcia_error_func(int func)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(service_table); i++)
- if (service_table[i].key == func)
- return service_table[i].msg;
-
- return "Unknown service number";
-}
-EXPORT_SYMBOL(pcmcia_error_func);
-
-const char *pcmcia_error_ret(int ret)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(error_table); i++)
- if (error_table[i].key == ret)
- return error_table[i].msg;
-
- return "unknown";
-}
-EXPORT_SYMBOL(pcmcia_error_ret);
-
-/*======================================================================*/
-
-
-
static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
{
struct pcmcia_device_id *did = p_drv->id_table;
@@ -303,7 +185,7 @@ int pcmcia_register_driver(struct pcmcia_driver *driver)
spin_lock_init(&driver->dynids.lock);
INIT_LIST_HEAD(&driver->dynids.list);
- ds_dbg(3, "registering driver %s\n", driver->drv.name);
+ pr_debug("registering driver %s\n", driver->drv.name);
error = driver_register(&driver->drv);
if (error < 0)
@@ -323,7 +205,7 @@ EXPORT_SYMBOL(pcmcia_register_driver);
*/
void pcmcia_unregister_driver(struct pcmcia_driver *driver)
{
- ds_dbg(3, "unregistering driver %s\n", driver->drv.name);
+ pr_debug("unregistering driver %s\n", driver->drv.name);
driver_unregister(&driver->drv);
pcmcia_free_dynids(driver);
}
@@ -350,14 +232,14 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev)
static void pcmcia_release_function(struct kref *ref)
{
struct config_t *c = container_of(ref, struct config_t, ref);
- ds_dbg(1, "releasing config_t\n");
+ pr_debug("releasing config_t\n");
kfree(c);
}
static void pcmcia_release_dev(struct device *dev)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- ds_dev_dbg(1, dev, "releasing device\n");
+ dev_dbg(dev, "releasing device\n");
pcmcia_put_socket(p_dev->socket);
kfree(p_dev->devname);
kref_put(&p_dev->function_config->ref, pcmcia_release_function);
@@ -367,7 +249,7 @@ static void pcmcia_release_dev(struct device *dev)
static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc)
{
if (!s->pcmcia_state.device_add_pending) {
- ds_dev_dbg(1, &s->dev, "scheduling to add %s secondary"
+ dev_dbg(&s->dev, "scheduling to add %s secondary"
" device to %d\n", mfc ? "mfc" : "pfc", s->sock);
s->pcmcia_state.device_add_pending = 1;
s->pcmcia_state.mfc_pfc = mfc;
@@ -405,7 +287,7 @@ static int pcmcia_device_probe(struct device * dev)
*/
did = dev_get_drvdata(&p_dev->dev);
- ds_dev_dbg(1, dev, "trying to bind to %s\n", p_drv->drv.name);
+ dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name);
if ((!p_drv->probe) || (!p_dev->function_config) ||
(!try_module_get(p_drv->owner))) {
@@ -428,7 +310,7 @@ static int pcmcia_device_probe(struct device * dev)
ret = p_drv->probe(p_dev);
if (ret) {
- ds_dev_dbg(1, dev, "binding to %s failed with %d\n",
+ dev_dbg(dev, "binding to %s failed with %d\n",
p_drv->drv.name, ret);
goto put_module;
}
@@ -456,7 +338,7 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
struct pcmcia_device *tmp;
unsigned long flags;
- ds_dev_dbg(2, leftover ? &leftover->dev : &s->dev,
+ dev_dbg(leftover ? &leftover->dev : &s->dev,
"pcmcia_card_remove(%d) %s\n", s->sock,
leftover ? leftover->devname : "");
@@ -475,7 +357,7 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
p_dev->_removed=1;
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
- ds_dev_dbg(2, &p_dev->dev, "unregistering device\n");
+ dev_dbg(&p_dev->dev, "unregistering device\n");
device_unregister(&p_dev->dev);
}
@@ -492,7 +374,7 @@ static int pcmcia_device_remove(struct device * dev)
p_dev = to_pcmcia_dev(dev);
p_drv = to_pcmcia_drv(dev->driver);
- ds_dev_dbg(1, dev, "removing device\n");
+ dev_dbg(dev, "removing device\n");
/* If we're removing the primary module driving a
* pseudo multi-function card, we need to unbind
@@ -572,7 +454,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
}
if (!pccard_read_tuple(p_dev->socket, p_dev->func,
CISTPL_DEVICE_GEO, devgeo)) {
- ds_dev_dbg(0, &p_dev->dev,
+ dev_dbg(&p_dev->dev,
"mem device geometry probably means "
"FUNCID_MEMORY\n");
p_dev->func_id = CISTPL_FUNCID_MEMORY;
@@ -628,7 +510,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
mutex_lock(&device_add_lock);
- ds_dbg(3, "adding device to %d, function %d\n", s->sock, function);
+ pr_debug("adding device to %d, function %d\n", s->sock, function);
/* max of 4 devices per card */
if (s->device_count == 4)
@@ -654,7 +536,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
p_dev->devname = kasprintf(GFP_KERNEL, "pcmcia%s", dev_name(&p_dev->dev));
if (!p_dev->devname)
goto err_free;
- ds_dev_dbg(3, &p_dev->dev, "devname is %s\n", p_dev->devname);
+ dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname);
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
@@ -677,7 +559,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
if (!p_dev->function_config) {
- ds_dev_dbg(3, &p_dev->dev, "creating config_t\n");
+ dev_dbg(&p_dev->dev, "creating config_t\n");
p_dev->function_config = kzalloc(sizeof(struct config_t),
GFP_KERNEL);
if (!p_dev->function_config)
@@ -722,20 +604,20 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
int ret = 0;
if (!(s->resource_setup_done)) {
- ds_dev_dbg(3, &s->dev,
+ dev_dbg(&s->dev,
"no resources available, delaying card_add\n");
return -EAGAIN; /* try again, but later... */
}
if (pcmcia_validate_mem(s)) {
- ds_dev_dbg(3, &s->dev, "validating mem resources failed, "
+ dev_dbg(&s->dev, "validating mem resources failed, "
"delaying card_add\n");
return -EAGAIN; /* try again, but later... */
}
ret = pccard_validate_cis(s, &no_chains);
if (ret || !no_chains) {
- ds_dev_dbg(0, &s->dev, "invalid CIS or invalid resources\n");
+ dev_dbg(&s->dev, "invalid CIS or invalid resources\n");
return -ENODEV;
}
@@ -756,7 +638,7 @@ static void pcmcia_delayed_add_device(struct work_struct *work)
{
struct pcmcia_socket *s =
container_of(work, struct pcmcia_socket, device_add);
- ds_dev_dbg(1, &s->dev, "adding additional device to %d\n", s->sock);
+ dev_dbg(&s->dev, "adding additional device to %d\n", s->sock);
pcmcia_device_add(s, s->pcmcia_state.mfc_pfc);
s->pcmcia_state.device_add_pending = 0;
s->pcmcia_state.mfc_pfc = 0;
@@ -766,7 +648,7 @@ static int pcmcia_requery(struct device *dev, void * _data)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
if (!p_dev->dev.driver) {
- ds_dev_dbg(1, dev, "update device information\n");
+ dev_dbg(dev, "update device information\n");
pcmcia_device_query(p_dev);
}
@@ -780,7 +662,7 @@ static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis)
unsigned long flags;
/* must be called with skt_mutex held */
- ds_dev_dbg(0, &skt->dev, "re-scanning socket %d\n", skt->sock);
+ dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock);
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
if (list_empty(&skt->devices_list))
@@ -835,7 +717,7 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
if (!filename)
return -EINVAL;
- ds_dev_dbg(1, &dev->dev, "trying to load CIS file %s\n", filename);
+ dev_dbg(&dev->dev, "trying to load CIS file %s\n", filename);
if (request_firmware(&fw, filename, &dev->dev) == 0) {
if (fw->size >= CISTPL_MAX_CIS_SIZE) {
@@ -953,14 +835,14 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
* after it has re-checked that there is no possible module
* with a prod_id/manf_id/card_id match.
*/
- ds_dev_dbg(0, &dev->dev,
+ dev_dbg(&dev->dev,
"skipping FUNC_ID match until userspace interaction\n");
if (!dev->allow_func_id_match)
return 0;
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
- ds_dev_dbg(0, &dev->dev, "device needs a fake CIS\n");
+ dev_dbg(&dev->dev, "device needs a fake CIS\n");
if (!dev->socket->fake_cis)
pcmcia_load_firmware(dev, did->cisfile);
@@ -992,9 +874,9 @@ static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) {
/* match dynamic devices first */
spin_lock(&p_drv->dynids.lock);
list_for_each_entry(dynid, &p_drv->dynids.list, node) {
- ds_dev_dbg(3, dev, "trying to match to %s\n", drv->name);
+ dev_dbg(dev, "trying to match to %s\n", drv->name);
if (pcmcia_devmatch(p_dev, &dynid->id)) {
- ds_dev_dbg(0, dev, "matched to %s\n", drv->name);
+ dev_dbg(dev, "matched to %s\n", drv->name);
spin_unlock(&p_drv->dynids.lock);
return 1;
}
@@ -1004,15 +886,15 @@ static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) {
#ifdef CONFIG_PCMCIA_IOCTL
/* matching by cardmgr */
if (p_dev->cardmgr == p_drv) {
- ds_dev_dbg(0, dev, "cardmgr matched to %s\n", drv->name);
+ dev_dbg(dev, "cardmgr matched to %s\n", drv->name);
return 1;
}
#endif
while (did && did->match_flags) {
- ds_dev_dbg(3, dev, "trying to match to %s\n", drv->name);
+ dev_dbg(dev, "trying to match to %s\n", drv->name);
if (pcmcia_devmatch(p_dev, did)) {
- ds_dev_dbg(0, dev, "matched to %s\n", drv->name);
+ dev_dbg(dev, "matched to %s\n", drv->name);
return 1;
}
did++;
@@ -1218,7 +1100,7 @@ static int pcmcia_dev_suspend(struct device * dev, pm_message_t state)
if (p_dev->suspended)
return 0;
- ds_dev_dbg(2, dev, "suspending\n");
+ dev_dbg(dev, "suspending\n");
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
@@ -1238,7 +1120,7 @@ static int pcmcia_dev_suspend(struct device * dev, pm_message_t state)
}
if (p_dev->device_no == p_dev->func) {
- ds_dev_dbg(2, dev, "releasing configuration\n");
+ dev_dbg(dev, "releasing configuration\n");
pcmcia_release_configuration(p_dev);
}
@@ -1258,7 +1140,7 @@ static int pcmcia_dev_resume(struct device * dev)
if (!p_dev->suspended)
return 0;
- ds_dev_dbg(2, dev, "resuming\n");
+ dev_dbg(dev, "resuming\n");
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
@@ -1267,7 +1149,7 @@ static int pcmcia_dev_resume(struct device * dev)
goto out;
if (p_dev->device_no == p_dev->func) {
- ds_dev_dbg(2, dev, "requesting configuration\n");
+ dev_dbg(dev, "requesting configuration\n");
ret = pcmcia_request_configuration(p_dev, &p_dev->conf);
if (ret)
goto out;
@@ -1309,14 +1191,14 @@ static int pcmcia_bus_resume_callback(struct device *dev, void * _data)
static int pcmcia_bus_resume(struct pcmcia_socket *skt)
{
- ds_dev_dbg(2, &skt->dev, "resuming socket %d\n", skt->sock);
+ dev_dbg(&skt->dev, "resuming socket %d\n", skt->sock);
bus_for_each_dev(&pcmcia_bus_type, NULL, skt, pcmcia_bus_resume_callback);
return 0;
}
static int pcmcia_bus_suspend(struct pcmcia_socket *skt)
{
- ds_dev_dbg(2, &skt->dev, "suspending socket %d\n", skt->sock);
+ dev_dbg(&skt->dev, "suspending socket %d\n", skt->sock);
if (bus_for_each_dev(&pcmcia_bus_type, NULL, skt,
pcmcia_bus_suspend_callback)) {
pcmcia_bus_resume(skt);
@@ -1348,7 +1230,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
return -ENODEV;
}
- ds_dev_dbg(1, &skt->dev, "ds_event(0x%06x, %d, 0x%p)\n",
+ dev_dbg(&skt->dev, "ds_event(0x%06x, %d, 0x%p)\n",
event, priority, skt);
switch (event) {
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index a4aacb830b8..c13fd936051 100644
--- a/drivers/pcmcia/i82365.c
+++ b/drivers/pcmcia/i82365.c
@@ -63,21 +63,6 @@
#include "vg468.h"
#include "ricoh.h"
-#ifdef CONFIG_PCMCIA_DEBUG
-static const char version[] =
-"i82365.c 1.265 1999/11/10 18:36:21 (David Hinds)";
-
-static int pc_debug;
-
-module_param(pc_debug, int, 0644);
-
-#define debug(lvl, fmt, arg...) do { \
- if (pc_debug > (lvl)) \
- printk(KERN_DEBUG "i82365: " fmt , ## arg); \
-} while (0)
-#else
-#define debug(lvl, fmt, arg...) do { } while (0)
-#endif
static irqreturn_t i365_count_irq(int, void *);
static inline int _check_irq(int irq, int flags)
@@ -501,13 +486,13 @@ static irqreturn_t i365_count_irq(int irq, void *dev)
{
i365_get(irq_sock, I365_CSC);
irq_hits++;
- debug(2, "-> hit on irq %d\n", irq);
+ pr_debug("i82365: -> hit on irq %d\n", irq);
return IRQ_HANDLED;
}
static u_int __init test_irq(u_short sock, int irq)
{
- debug(2, " testing ISA irq %d\n", irq);
+ pr_debug("i82365: testing ISA irq %d\n", irq);
if (request_irq(irq, i365_count_irq, IRQF_PROBE_SHARED, "scan",
i365_count_irq) != 0)
return 1;
@@ -515,7 +500,7 @@ static u_int __init test_irq(u_short sock, int irq)
msleep(10);
if (irq_hits) {
free_irq(irq, i365_count_irq);
- debug(2, " spurious hit!\n");
+ pr_debug("i82365: spurious hit!\n");
return 1;
}
@@ -528,7 +513,7 @@ static u_int __init test_irq(u_short sock, int irq)
/* mask all interrupts */
i365_set(sock, I365_CSCINT, 0);
- debug(2, " hits = %d\n", irq_hits);
+ pr_debug("i82365: hits = %d\n", irq_hits);
return (irq_hits != 1);
}
@@ -854,7 +839,7 @@ static irqreturn_t pcic_interrupt(int irq, void *dev)
u_long flags = 0;
int handled = 0;
- debug(4, "pcic_interrupt(%d)\n", irq);
+ pr_debug("pcic_interrupt(%d)\n", irq);
for (j = 0; j < 20; j++) {
active = 0;
@@ -878,7 +863,7 @@ static irqreturn_t pcic_interrupt(int irq, void *dev)
events |= (csc & I365_CSC_READY) ? SS_READY : 0;
}
ISA_UNLOCK(i, flags);
- debug(2, "socket %d event 0x%02x\n", i, events);
+ pr_debug("socket %d event 0x%02x\n", i, events);
if (events)
pcmcia_parse_events(&socket[i].socket, events);
@@ -890,7 +875,7 @@ static irqreturn_t pcic_interrupt(int irq, void *dev)
if (j == 20)
printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n");
- debug(4, "interrupt done\n");
+ pr_debug("pcic_interrupt done\n");
return IRQ_RETVAL(handled);
} /* pcic_interrupt */
@@ -932,7 +917,7 @@ static int i365_get_status(u_short sock, u_int *value)
}
}
- debug(1, "GetStatus(%d) = %#4.4x\n", sock, *value);
+ pr_debug("GetStatus(%d) = %#4.4x\n", sock, *value);
return 0;
} /* i365_get_status */
@@ -943,7 +928,7 @@ static int i365_set_socket(u_short sock, socket_state_t *state)
struct i82365_socket *t = &socket[sock];
u_char reg;
- debug(1, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ pr_debug("SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
@@ -1052,7 +1037,7 @@ static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
{
u_char map, ioctl;
- debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", sock, io->map, io->flags, io->speed,
(unsigned long long)io->start, (unsigned long long)io->stop);
map = io->map;
@@ -1082,7 +1067,7 @@ static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
u_short base, i;
u_char map;
- debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, "
+ pr_debug("SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, "
"%#x)\n", sock, mem->map, mem->flags, mem->speed,
(unsigned long long)mem->res->start,
(unsigned long long)mem->res->end, mem->card_start);
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
index 7dfbee1dcd7..26a621c9e2f 100644
--- a/drivers/pcmcia/m32r_cfc.c
+++ b/drivers/pcmcia/m32r_cfc.c
@@ -38,17 +38,6 @@
#include "m32r_cfc.h"
-#ifdef CONFIG_PCMCIA_DEBUG
-static int m32r_cfc_debug;
-module_param(m32r_cfc_debug, int, 0644);
-#define debug(lvl, fmt, arg...) do { \
- if (m32r_cfc_debug > (lvl)) \
- printk(KERN_DEBUG "m32r_cfc: " fmt , ## arg); \
-} while (0)
-#else
-#define debug(n, args...) do { } while (0)
-#endif
-
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval = 0;
@@ -123,7 +112,7 @@ void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size,
unsigned char *bp = (unsigned char *)buf;
unsigned long flags;
- debug(3, "m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
+ pr_debug("m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
"size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
@@ -132,7 +121,7 @@ void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size,
printk("m32r_cfc:ioread_byte null port :%#lx\n",port);
return;
}
- debug(3, "m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr);
+ pr_debug("m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
/* read Byte */
@@ -148,7 +137,7 @@ void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size,
unsigned short *bp = (unsigned short *)buf;
unsigned long flags;
- debug(3, "m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
+ pr_debug("m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
@@ -163,7 +152,7 @@ void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size,
printk("m32r_cfc:ioread_word null port :%#lx\n",port);
return;
}
- debug(3, "m32r_cfc: pcc_ioread_word: addr=%#lx\n", addr);
+ pr_debug("m32r_cfc: pcc_ioread_word: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
/* read Word */
@@ -179,7 +168,7 @@ void pcc_iowrite_byte(int sock, unsigned long port, void *buf, size_t size,
unsigned char *bp = (unsigned char *)buf;
unsigned long flags;
- debug(3, "m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, "
+ pr_debug("m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
@@ -189,7 +178,7 @@ void pcc_iowrite_byte(int sock, unsigned long port, void *buf, size_t size,
printk("m32r_cfc:iowrite_byte null port:%#lx\n",port);
return;
}
- debug(3, "m32r_cfc: pcc_iowrite_byte: addr=%#lx\n", addr);
+ pr_debug("m32r_cfc: pcc_iowrite_byte: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
while (nmemb--)
@@ -204,7 +193,7 @@ void pcc_iowrite_word(int sock, unsigned long port, void *buf, size_t size,
unsigned short *bp = (unsigned short *)buf;
unsigned long flags;
- debug(3, "m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, "
+ pr_debug("m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
@@ -226,7 +215,7 @@ void pcc_iowrite_word(int sock, unsigned long port, void *buf, size_t size,
return;
}
#endif
- debug(3, "m32r_cfc: pcc_iowrite_word: addr=%#lx\n", addr);
+ pr_debug("m32r_cfc: pcc_iowrite_word: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
while (nmemb--)
@@ -262,7 +251,7 @@ static struct timer_list poll_timer;
static unsigned int pcc_get(u_short sock, unsigned int reg)
{
unsigned int val = inw(reg);
- debug(3, "m32r_cfc: pcc_get: reg(0x%08x)=0x%04x\n", reg, val);
+ pr_debug("m32r_cfc: pcc_get: reg(0x%08x)=0x%04x\n", reg, val);
return val;
}
@@ -270,7 +259,7 @@ static unsigned int pcc_get(u_short sock, unsigned int reg)
static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
{
outw(data, reg);
- debug(3, "m32r_cfc: pcc_set: reg(0x%08x)=0x%04x\n", reg, data);
+ pr_debug("m32r_cfc: pcc_set: reg(0x%08x)=0x%04x\n", reg, data);
}
/*======================================================================
@@ -286,14 +275,14 @@ static int __init is_alive(u_short sock)
{
unsigned int stat;
- debug(3, "m32r_cfc: is_alive:\n");
+ pr_debug("m32r_cfc: is_alive:\n");
printk("CF: ");
stat = pcc_get(sock, (unsigned int)PLD_CFSTS);
if (!stat)
printk("No ");
printk("Card is detected at socket %d : stat = 0x%08x\n", sock, stat);
- debug(3, "m32r_cfc: is_alive: sock stat is 0x%04x\n", stat);
+ pr_debug("m32r_cfc: is_alive: sock stat is 0x%04x\n", stat);
return 0;
}
@@ -303,7 +292,7 @@ static void add_pcc_socket(ulong base, int irq, ulong mapaddr,
{
pcc_socket_t *t = &socket[pcc_sockets];
- debug(3, "m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, "
+ pr_debug("m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, "
"mapaddr=%#lx, ioaddr=%08x\n",
base, irq, mapaddr, ioaddr);
@@ -358,7 +347,7 @@ static void add_pcc_socket(ulong base, int irq, ulong mapaddr,
/* eject interrupt */
request_irq(irq+1, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
#endif
- debug(3, "m32r_cfc: enable CFMSK, RDYSEL\n");
+ pr_debug("m32r_cfc: enable CFMSK, RDYSEL\n");
pcc_set(pcc_sockets, (unsigned int)PLD_CFIMASK, 0x01);
#endif /* CONFIG_PLAT_USRV */
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
@@ -378,26 +367,26 @@ static irqreturn_t pcc_interrupt(int irq, void *dev)
u_int events = 0;
int handled = 0;
- debug(3, "m32r_cfc: pcc_interrupt: irq=%d, dev=%p\n", irq, dev);
+ pr_debug("m32r_cfc: pcc_interrupt: irq=%d, dev=%p\n", irq, dev);
for (i = 0; i < pcc_sockets; i++) {
if (socket[i].cs_irq1 != irq && socket[i].cs_irq2 != irq)
continue;
handled = 1;
- debug(3, "m32r_cfc: pcc_interrupt: socket %d irq 0x%02x ",
+ pr_debug("m32r_cfc: pcc_interrupt: socket %d irq 0x%02x ",
i, irq);
events |= SS_DETECT; /* insert or eject */
if (events)
pcmcia_parse_events(&socket[i].socket, events);
}
- debug(3, "m32r_cfc: pcc_interrupt: done\n");
+ pr_debug("m32r_cfc: pcc_interrupt: done\n");
return IRQ_RETVAL(handled);
} /* pcc_interrupt */
static void pcc_interrupt_wrapper(u_long data)
{
- debug(3, "m32r_cfc: pcc_interrupt_wrapper:\n");
+ pr_debug("m32r_cfc: pcc_interrupt_wrapper:\n");
pcc_interrupt(0, NULL);
init_timer(&poll_timer);
poll_timer.expires = jiffies + poll_interval;
@@ -410,17 +399,17 @@ static int _pcc_get_status(u_short sock, u_int *value)
{
u_int status;
- debug(3, "m32r_cfc: _pcc_get_status:\n");
+ pr_debug("m32r_cfc: _pcc_get_status:\n");
status = pcc_get(sock, (unsigned int)PLD_CFSTS);
*value = (status) ? SS_DETECT : 0;
- debug(3, "m32r_cfc: _pcc_get_status: status=0x%08x\n", status);
+ pr_debug("m32r_cfc: _pcc_get_status: status=0x%08x\n", status);
#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
if ( status ) {
/* enable CF power */
status = inw((unsigned int)PLD_CPCR);
if (!(status & PLD_CPCR_CF)) {
- debug(3, "m32r_cfc: _pcc_get_status: "
+ pr_debug("m32r_cfc: _pcc_get_status: "
"power on (CPCR=0x%08x)\n", status);
status |= PLD_CPCR_CF;
outw(status, (unsigned int)PLD_CPCR);
@@ -439,7 +428,7 @@ static int _pcc_get_status(u_short sock, u_int *value)
status &= ~PLD_CPCR_CF;
outw(status, (unsigned int)PLD_CPCR);
udelay(100);
- debug(3, "m32r_cfc: _pcc_get_status: "
+ pr_debug("m32r_cfc: _pcc_get_status: "
"power off (CPCR=0x%08x)\n", status);
}
#elif defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_MAPPI3)
@@ -465,13 +454,13 @@ static int _pcc_get_status(u_short sock, u_int *value)
/* disable CF power */
pcc_set(sock, (unsigned int)PLD_CPCR, 0);
udelay(100);
- debug(3, "m32r_cfc: _pcc_get_status: "
+ pr_debug("m32r_cfc: _pcc_get_status: "
"power off (CPCR=0x%08x)\n", status);
}
#else
#error no platform configuration
#endif
- debug(3, "m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x\n",
+ pr_debug("m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x\n",
sock, *value);
return 0;
} /* _get_status */
@@ -480,7 +469,7 @@ static int _pcc_get_status(u_short sock, u_int *value)
static int _pcc_set_socket(u_short sock, socket_state_t *state)
{
- debug(3, "m32r_cfc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ pr_debug("m32r_cfc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
@@ -492,41 +481,39 @@ static int _pcc_set_socket(u_short sock, socket_state_t *state)
}
#endif
if (state->flags & SS_RESET) {
- debug(3, ":RESET\n");
+ pr_debug(":RESET\n");
pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x101);
}else{
pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x100);
}
if (state->flags & SS_OUTPUT_ENA){
- debug(3, ":OUTPUT_ENA\n");
+ pr_debug(":OUTPUT_ENA\n");
/* bit clear */
pcc_set(sock,(unsigned int)PLD_CFBUFCR,0);
} else {
pcc_set(sock,(unsigned int)PLD_CFBUFCR,1);
}
-#ifdef CONFIG_PCMCIA_DEBUG
if(state->flags & SS_IOCARD){
- debug(3, ":IOCARD");
+ pr_debug(":IOCARD");
}
if (state->flags & SS_PWR_AUTO) {
- debug(3, ":PWR_AUTO");
+ pr_debug(":PWR_AUTO");
}
if (state->csc_mask & SS_DETECT)
- debug(3, ":csc-SS_DETECT");
+ pr_debug(":csc-SS_DETECT");
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
- debug(3, ":STSCHG");
+ pr_debug(":STSCHG");
} else {
if (state->csc_mask & SS_BATDEAD)
- debug(3, ":BATDEAD");
+ pr_debug(":BATDEAD");
if (state->csc_mask & SS_BATWARN)
- debug(3, ":BATWARN");
+ pr_debug(":BATWARN");
if (state->csc_mask & SS_READY)
- debug(3, ":READY");
+ pr_debug(":READY");
}
- debug(3, "\n");
-#endif
+ pr_debug("\n");
return 0;
} /* _set_socket */
@@ -536,7 +523,7 @@ static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
{
u_char map;
- debug(3, "m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", sock, io->map, io->flags,
io->speed, (unsigned long long)io->start,
(unsigned long long)io->stop);
@@ -554,7 +541,7 @@ static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
u_long addr;
pcc_socket_t *t = &socket[sock];
- debug(3, "m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#llx, %#x)\n", sock, map, mem->flags,
mem->speed, (unsigned long long)mem->static_start,
mem->card_start);
@@ -640,11 +627,11 @@ static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
- debug(3, "m32r_cfc: pcc_get_status: sock(%d) -EINVAL\n", sock);
+ dev_dbg(&s->dev, "pcc_get_status: sock(%d) -EINVAL\n", sock);
*value = 0;
return -EINVAL;
}
- debug(3, "m32r_cfc: pcc_get_status: sock(%d)\n", sock);
+ dev_dbg(&s->dev, "pcc_get_status: sock(%d)\n", sock);
LOCKED(_pcc_get_status(sock, value));
}
@@ -653,10 +640,10 @@ static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
- debug(3, "m32r_cfc: pcc_set_socket: sock(%d) -EINVAL\n", sock);
+ dev_dbg(&s->dev, "pcc_set_socket: sock(%d) -EINVAL\n", sock);
return -EINVAL;
}
- debug(3, "m32r_cfc: pcc_set_socket: sock(%d)\n", sock);
+ dev_dbg(&s->dev, "pcc_set_socket: sock(%d)\n", sock);
LOCKED(_pcc_set_socket(sock, state));
}
@@ -665,10 +652,10 @@ static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
- debug(3, "m32r_cfc: pcc_set_io_map: sock(%d) -EINVAL\n", sock);
+ dev_dbg(&s->dev, "pcc_set_io_map: sock(%d) -EINVAL\n", sock);
return -EINVAL;
}
- debug(3, "m32r_cfc: pcc_set_io_map: sock(%d)\n", sock);
+ dev_dbg(&s->dev, "pcc_set_io_map: sock(%d)\n", sock);
LOCKED(_pcc_set_io_map(sock, io));
}
@@ -677,16 +664,16 @@ static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
if (socket[sock].flags & IS_ALIVE) {
- debug(3, "m32r_cfc: pcc_set_mem_map: sock(%d) -EINVAL\n", sock);
+ dev_dbg(&s->dev, "pcc_set_mem_map: sock(%d) -EINVAL\n", sock);
return -EINVAL;
}
- debug(3, "m32r_cfc: pcc_set_mem_map: sock(%d)\n", sock);
+ dev_dbg(&s->dev, "pcc_set_mem_map: sock(%d)\n", sock);
LOCKED(_pcc_set_mem_map(sock, mem));
}
static int pcc_init(struct pcmcia_socket *s)
{
- debug(3, "m32r_cfc: pcc_init()\n");
+ dev_dbg(&s->dev, "pcc_init()\n");
return 0;
}
diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c
index c6524f99ccc..72844c5a6d0 100644
--- a/drivers/pcmcia/m32r_pcc.c
+++ b/drivers/pcmcia/m32r_pcc.c
@@ -45,16 +45,6 @@
#define PCC_DEBUG_DBEX
-#ifdef CONFIG_PCMCIA_DEBUG
-static int m32r_pcc_debug;
-module_param(m32r_pcc_debug, int, 0644);
-#define debug(lvl, fmt, arg...) do { \
- if (m32r_pcc_debug > (lvl)) \
- printk(KERN_DEBUG "m32r_pcc: " fmt , ## arg); \
-} while (0)
-#else
-#define debug(n, args...) do { } while (0)
-#endif
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval = 0;
@@ -358,7 +348,7 @@ static irqreturn_t pcc_interrupt(int irq, void *dev)
u_int events, active;
int handled = 0;
- debug(4, "m32r: pcc_interrupt(%d)\n", irq);
+ pr_debug("m32r_pcc: pcc_interrupt(%d)\n", irq);
for (j = 0; j < 20; j++) {
active = 0;
@@ -369,13 +359,14 @@ static irqreturn_t pcc_interrupt(int irq, void *dev)
handled = 1;
irc = pcc_get(i, PCIRC);
irc >>=16;
- debug(2, "m32r-pcc:interrupt: socket %d pcirc 0x%02x ", i, irc);
+ pr_debug("m32r_pcc: interrupt: socket %d pcirc 0x%02x ",
+ i, irc);
if (!irc)
continue;
events = (irc) ? SS_DETECT : 0;
events |= (pcc_get(i,PCCR) & PCCR_PCEN) ? SS_READY : 0;
- debug(2, " event 0x%02x\n", events);
+ pr_debug("m32r_pcc: event 0x%02x\n", events);
if (events)
pcmcia_parse_events(&socket[i].socket, events);
@@ -388,7 +379,7 @@ static irqreturn_t pcc_interrupt(int irq, void *dev)
if (j == 20)
printk(KERN_NOTICE "m32r-pcc: infinite loop in interrupt handler\n");
- debug(4, "m32r-pcc: interrupt done\n");
+ pr_debug("m32r_pcc: interrupt done\n");
return IRQ_RETVAL(handled);
} /* pcc_interrupt */
@@ -422,7 +413,7 @@ static int _pcc_get_status(u_short sock, u_int *value)
status = pcc_get(sock,PCCSIGCR);
*value |= (status & PCCSIGCR_VEN) ? SS_POWERON : 0;
- debug(3, "m32r-pcc: GetStatus(%d) = %#4.4x\n", sock, *value);
+ pr_debug("m32r_pcc: GetStatus(%d) = %#4.4x\n", sock, *value);
return 0;
} /* _get_status */
@@ -432,7 +423,7 @@ static int _pcc_set_socket(u_short sock, socket_state_t *state)
{
u_long reg = 0;
- debug(3, "m32r-pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ pr_debug("m32r_pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)", sock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
@@ -448,11 +439,11 @@ static int _pcc_set_socket(u_short sock, socket_state_t *state)
}
if (state->flags & SS_RESET) {
- debug(3, ":RESET\n");
+ pr_debug("m32r_pcc: :RESET\n");
reg |= PCCSIGCR_CRST;
}
if (state->flags & SS_OUTPUT_ENA){
- debug(3, ":OUTPUT_ENA\n");
+ pr_debug("m32r_pcc: :OUTPUT_ENA\n");
/* bit clear */
} else {
reg |= PCCSIGCR_SEN;
@@ -460,28 +451,26 @@ static int _pcc_set_socket(u_short sock, socket_state_t *state)
pcc_set(sock,PCCSIGCR,reg);
-#ifdef CONFIG_PCMCIA_DEBUG
if(state->flags & SS_IOCARD){
- debug(3, ":IOCARD");
+ pr_debug("m32r_pcc: :IOCARD");
}
if (state->flags & SS_PWR_AUTO) {
- debug(3, ":PWR_AUTO");
+ pr_debug("m32r_pcc: :PWR_AUTO");
}
if (state->csc_mask & SS_DETECT)
- debug(3, ":csc-SS_DETECT");
+ pr_debug("m32r_pcc: :csc-SS_DETECT");
if (state->flags & SS_IOCARD) {
if (state->csc_mask & SS_STSCHG)
- debug(3, ":STSCHG");
+ pr_debug("m32r_pcc: :STSCHG");
} else {
if (state->csc_mask & SS_BATDEAD)
- debug(3, ":BATDEAD");
+ pr_debug("m32r_pcc: :BATDEAD");
if (state->csc_mask & SS_BATWARN)
- debug(3, ":BATWARN");
+ pr_debug("m32r_pcc: :BATWARN");
if (state->csc_mask & SS_READY)
- debug(3, ":READY");
+ pr_debug("m32r_pcc: :READY");
}
- debug(3, "\n");
-#endif
+ pr_debug("m32r_pcc: \n");
return 0;
} /* _set_socket */
@@ -491,7 +480,7 @@ static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
{
u_char map;
- debug(3, "m32r-pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("m32r_pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", sock, io->map, io->flags,
io->speed, (unsigned long long)io->start,
(unsigned long long)io->stop);
@@ -515,7 +504,7 @@ static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
#endif
#endif
- debug(3, "m32r-pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("m32r_pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#llx, %#x)\n", sock, map, mem->flags,
mem->speed, (unsigned long long)mem->static_start,
mem->card_start);
@@ -662,7 +651,7 @@ static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
static int pcc_init(struct pcmcia_socket *s)
{
- debug(4, "m32r-pcc: init call\n");
+ pr_debug("m32r_pcc: init call\n");
return 0;
}
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index 403559ba49d..7f79c4e169a 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -64,14 +64,6 @@
#include <pcmcia/cs.h>
#include <pcmcia/ss.h>
-#ifdef CONFIG_PCMCIA_DEBUG
-static int pc_debug;
-module_param(pc_debug, int, 0);
-#define dprintk(args...) printk(KERN_DEBUG "m8xx_pcmcia: " args);
-#else
-#define dprintk(args...)
-#endif
-
#define pcmcia_info(args...) printk(KERN_INFO "m8xx_pcmcia: "args)
#define pcmcia_error(args...) printk(KERN_ERR "m8xx_pcmcia: "args)
@@ -565,7 +557,7 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev)
unsigned int i, events, pscr, pipr, per;
pcmconf8xx_t *pcmcia = socket[0].pcmcia;
- dprintk("Interrupt!\n");
+ pr_debug("m8xx_pcmcia: Interrupt!\n");
/* get interrupt sources */
pscr = in_be32(&pcmcia->pcmc_pscr);
@@ -614,7 +606,7 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev)
/* call the handler */
- dprintk("slot %u: events = 0x%02x, pscr = 0x%08x, "
+ pr_debug("m8xx_pcmcia: slot %u: events = 0x%02x, pscr = 0x%08x, "
"pipr = 0x%08x\n", i, events, pscr, pipr);
if (events) {
@@ -641,7 +633,7 @@ static irqreturn_t m8xx_interrupt(int irq, void *dev)
/* clear the interrupt sources */
out_be32(&pcmcia->pcmc_pscr, pscr);
- dprintk("Interrupt done.\n");
+ pr_debug("m8xx_pcmcia: Interrupt done.\n");
return IRQ_HANDLED;
}
@@ -815,7 +807,7 @@ static int m8xx_get_status(struct pcmcia_socket *sock, unsigned int *value)
};
}
- dprintk("GetStatus(%d) = %#2.2x\n", lsock, *value);
+ pr_debug("m8xx_pcmcia: GetStatus(%d) = %#2.2x\n", lsock, *value);
return 0;
}
@@ -828,7 +820,7 @@ static int m8xx_set_socket(struct pcmcia_socket *sock, socket_state_t * state)
unsigned long flags;
pcmconf8xx_t *pcmcia = socket[0].pcmcia;
- dprintk("SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ pr_debug("m8xx_pcmcia: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)\n", lsock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
@@ -974,7 +966,7 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
#define M8XX_SIZE (io->stop - io->start + 1)
#define M8XX_BASE (PCMCIA_IO_WIN_BASE + io->start)
- dprintk("SetIOMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("m8xx_pcmcia: SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#4.4llx-%#4.4llx)\n", lsock, io->map, io->flags,
io->speed, (unsigned long long)io->start,
(unsigned long long)io->stop);
@@ -988,7 +980,7 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
if (io->flags & MAP_ACTIVE) {
- dprintk("io->flags & MAP_ACTIVE\n");
+ pr_debug("m8xx_pcmcia: io->flags & MAP_ACTIVE\n");
winnr = (PCMCIA_MEM_WIN_NO * PCMCIA_SOCKETS_NO)
+ (lsock * PCMCIA_IO_WIN_NO) + io->map;
@@ -1018,8 +1010,8 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
out_be32(&w->or, reg);
- dprintk("Socket %u: Mapped io window %u at %#8.8x, "
- "OR = %#8.8x.\n", lsock, io->map, w->br, w->or);
+ pr_debug("m8xx_pcmcia: Socket %u: Mapped io window %u at "
+ "%#8.8x, OR = %#8.8x.\n", lsock, io->map, w->br, w->or);
} else {
/* shutdown IO window */
winnr = (PCMCIA_MEM_WIN_NO * PCMCIA_SOCKETS_NO)
@@ -1033,14 +1025,14 @@ static int m8xx_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
out_be32(&w->or, 0); /* turn off window */
out_be32(&w->br, 0); /* turn off base address */
- dprintk("Socket %u: Unmapped io window %u at %#8.8x, "
- "OR = %#8.8x.\n", lsock, io->map, w->br, w->or);
+ pr_debug("m8xx_pcmcia: Socket %u: Unmapped io window %u at "
+ "%#8.8x, OR = %#8.8x.\n", lsock, io->map, w->br, w->or);
}
/* copy the struct and modify the copy */
s->io_win[io->map] = *io;
s->io_win[io->map].flags &= (MAP_WRPROT | MAP_16BIT | MAP_ACTIVE);
- dprintk("SetIOMap exit\n");
+ pr_debug("m8xx_pcmcia: SetIOMap exit\n");
return 0;
}
@@ -1055,7 +1047,7 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock,
unsigned int reg, winnr;
pcmconf8xx_t *pcmcia = s->pcmcia;
- dprintk("SetMemMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("m8xx_pcmcia: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#5.5llx, %#5.5x)\n", lsock, mem->map, mem->flags,
mem->speed, (unsigned long long)mem->static_start,
mem->card_start);
@@ -1098,7 +1090,7 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock,
out_be32(&w->or, reg);
- dprintk("Socket %u: Mapped memory window %u at %#8.8x, "
+ pr_debug("m8xx_pcmcia: Socket %u: Mapped memory window %u at %#8.8x, "
"OR = %#8.8x.\n", lsock, mem->map, w->br, w->or);
if (mem->flags & MAP_ACTIVE) {
@@ -1108,7 +1100,7 @@ static int m8xx_set_mem_map(struct pcmcia_socket *sock,
+ mem->card_start;
}
- dprintk("SetMemMap(%d, %d, %#2.2x, %d ns, "
+ pr_debug("m8xx_pcmcia: SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#5.5llx, %#5.5x)\n", lsock, mem->map, mem->flags,
mem->speed, (unsigned long long)mem->static_start,
mem->card_start);
@@ -1129,7 +1121,7 @@ static int m8xx_sock_init(struct pcmcia_socket *sock)
pccard_io_map io = { 0, 0, 0, 0, 1 };
pccard_mem_map mem = { 0, 0, 0, 0, 0, 0 };
- dprintk("sock_init(%d)\n", s);
+ pr_debug("m8xx_pcmcia: sock_init(%d)\n", s);
m8xx_set_socket(sock, &dead_socket);
for (i = 0; i < PCMCIA_IO_WIN_NO; i++) {
diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h
index 72188c462c9..624442fc0d3 100644
--- a/drivers/pcmcia/o2micro.h
+++ b/drivers/pcmcia/o2micro.h
@@ -30,28 +30,6 @@
#ifndef _LINUX_O2MICRO_H
#define _LINUX_O2MICRO_H
-#ifndef PCI_VENDOR_ID_O2
-#define PCI_VENDOR_ID_O2 0x1217
-#endif
-#ifndef PCI_DEVICE_ID_O2_6729
-#define PCI_DEVICE_ID_O2_6729 0x6729
-#endif
-#ifndef PCI_DEVICE_ID_O2_6730
-#define PCI_DEVICE_ID_O2_6730 0x673a
-#endif
-#ifndef PCI_DEVICE_ID_O2_6832
-#define PCI_DEVICE_ID_O2_6832 0x6832
-#endif
-#ifndef PCI_DEVICE_ID_O2_6836
-#define PCI_DEVICE_ID_O2_6836 0x6836
-#endif
-#ifndef PCI_DEVICE_ID_O2_6812
-#define PCI_DEVICE_ID_O2_6812 0x6872
-#endif
-#ifndef PCI_DEVICE_ID_O2_6933
-#define PCI_DEVICE_ID_O2_6933 0x6933
-#endif
-
/* Additional PCI configuration registers */
#define O2_MUX_CONTROL 0x90 /* 32 bit */
diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c
index 68570bc3ac8..663781d2012 100644
--- a/drivers/pcmcia/omap_cf.c
+++ b/drivers/pcmcia/omap_cf.c
@@ -23,8 +23,8 @@
#include <asm/io.h>
#include <asm/sizes.h>
-#include <mach/mux.h>
-#include <mach/tc.h>
+#include <plat/mux.h>
+#include <plat/tc.h>
/* NOTE: don't expect this to support many I/O cards. The 16xx chips have
diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c
index 30cf71d2ee2..c4d7908fa37 100644
--- a/drivers/pcmcia/pcmcia_ioctl.c
+++ b/drivers/pcmcia/pcmcia_ioctl.c
@@ -58,17 +58,6 @@ typedef struct user_info_t {
} user_info_t;
-#ifdef CONFIG_PCMCIA_DEBUG
-extern int ds_pc_debug;
-
-#define ds_dbg(lvl, fmt, arg...) do { \
- if (ds_pc_debug >= lvl) \
- printk(KERN_DEBUG "ds: " fmt , ## arg); \
-} while (0)
-#else
-#define ds_dbg(lvl, fmt, arg...) do { } while (0)
-#endif
-
static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
unsigned int function)
{
@@ -229,6 +218,61 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
return (ret);
}
+
+/** pcmcia_get_window
+ */
+static int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *wh_out,
+ window_handle_t wh, win_req_t *req)
+{
+ pccard_mem_map *win;
+ window_handle_t w;
+
+ wh--;
+ if (!s || !(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+ if (wh >= MAX_WIN)
+ return -EINVAL;
+ for (w = wh; w < MAX_WIN; w++)
+ if (s->state & SOCKET_WIN_REQ(w))
+ break;
+ if (w == MAX_WIN)
+ return -EINVAL;
+ win = &s->win[w];
+ req->Base = win->res->start;
+ req->Size = win->res->end - win->res->start + 1;
+ req->AccessSpeed = win->speed;
+ req->Attributes = 0;
+ if (win->flags & MAP_ATTRIB)
+ req->Attributes |= WIN_MEMORY_TYPE_AM;
+ if (win->flags & MAP_ACTIVE)
+ req->Attributes |= WIN_ENABLE;
+ if (win->flags & MAP_16BIT)
+ req->Attributes |= WIN_DATA_WIDTH_16;
+ if (win->flags & MAP_USE_WAIT)
+ req->Attributes |= WIN_USE_WAIT;
+
+ *wh_out = w + 1;
+ return 0;
+} /* pcmcia_get_window */
+
+
+/** pcmcia_get_mem_page
+ *
+ * Change the card address of an already open memory window.
+ */
+static int pcmcia_get_mem_page(struct pcmcia_socket *skt, window_handle_t wh,
+ memreq_t *req)
+{
+ wh--;
+ if (wh >= MAX_WIN)
+ return -EINVAL;
+
+ req->Page = 0;
+ req->CardOffset = skt->win[wh].card_start;
+ return 0;
+} /* pcmcia_get_mem_page */
+
+
/** pccard_get_status
*
* Get the current socket state bits. We don't support the latched
@@ -431,7 +475,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
if (!s)
return -EINVAL;
- ds_dbg(2, "bind_request(%d, '%s')\n", s->sock,
+ pr_debug("bind_request(%d, '%s')\n", s->sock,
(char *)bind_info->dev_info);
p_drv = get_pcmcia_driver(&bind_info->dev_info);
@@ -623,7 +667,7 @@ static int ds_open(struct inode *inode, struct file *file)
static int warning_printed = 0;
int ret = 0;
- ds_dbg(0, "ds_open(socket %d)\n", i);
+ pr_debug("ds_open(socket %d)\n", i);
lock_kernel();
s = pcmcia_get_socket_by_nr(i);
@@ -685,7 +729,7 @@ static int ds_release(struct inode *inode, struct file *file)
struct pcmcia_socket *s;
user_info_t *user, **link;
- ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
+ pr_debug("ds_release(socket %d)\n", iminor(inode));
user = file->private_data;
if (CHECK_USER(user))
@@ -719,7 +763,7 @@ static ssize_t ds_read(struct file *file, char __user *buf,
user_info_t *user;
int ret;
- ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
+ pr_debug("ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
if (count < 4)
return -EINVAL;
@@ -744,7 +788,7 @@ static ssize_t ds_read(struct file *file, char __user *buf,
static ssize_t ds_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
+ pr_debug("ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
if (count != 4)
return -EINVAL;
@@ -762,7 +806,7 @@ static u_int ds_poll(struct file *file, poll_table *wait)
struct pcmcia_socket *s;
user_info_t *user;
- ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
+ pr_debug("ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
user = file->private_data;
if (CHECK_USER(user))
@@ -790,7 +834,7 @@ static int ds_ioctl(struct inode * inode, struct file * file,
ds_ioctl_arg_t *buf;
user_info_t *user;
- ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
+ pr_debug("ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
user = file->private_data;
if (CHECK_USER(user))
@@ -809,13 +853,13 @@ static int ds_ioctl(struct inode * inode, struct file * file,
if (cmd & IOC_IN) {
if (!access_ok(VERIFY_READ, uarg, size)) {
- ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
+ pr_debug("ds_ioctl(): verify_read = %d\n", -EFAULT);
return -EFAULT;
}
}
if (cmd & IOC_OUT) {
if (!access_ok(VERIFY_WRITE, uarg, size)) {
- ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
+ pr_debug("ds_ioctl(): verify_write = %d\n", -EFAULT);
return -EFAULT;
}
}
@@ -927,15 +971,15 @@ static int ds_ioctl(struct inode * inode, struct file * file,
goto free_out;
break;
case DS_GET_FIRST_WINDOW:
- ret = pcmcia_get_window(s, &buf->win_info.handle, 0,
+ ret = pcmcia_get_window(s, &buf->win_info.handle, 1,
&buf->win_info.window);
break;
case DS_GET_NEXT_WINDOW:
ret = pcmcia_get_window(s, &buf->win_info.handle,
- buf->win_info.handle->index + 1, &buf->win_info.window);
+ buf->win_info.handle + 1, &buf->win_info.window);
break;
case DS_GET_MEM_PAGE:
- ret = pcmcia_get_mem_page(buf->win_info.handle,
+ ret = pcmcia_get_mem_page(s, buf->win_info.handle,
&buf->win_info.map);
break;
case DS_REPLACE_CIS:
@@ -962,7 +1006,7 @@ static int ds_ioctl(struct inode * inode, struct file * file,
}
if ((err == 0) && (ret != 0)) {
- ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
+ pr_debug("ds_ioctl: ret = %d\n", ret);
switch (ret) {
case -ENODEV:
case -EINVAL:
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index d919e96c0af..a8bf8c1b45e 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/device.h>
+#include <linux/netdevice.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
@@ -43,21 +44,6 @@ static u8 pcmcia_used_irq[NR_IRQS];
#endif
-#ifdef CONFIG_PCMCIA_DEBUG
-extern int ds_pc_debug;
-
-#define ds_dbg(skt, lvl, fmt, arg...) do { \
- if (ds_pc_debug >= lvl) \
- dev_printk(KERN_DEBUG, &skt->dev, \
- "pcmcia_resource: " fmt, \
- ## arg); \
-} while (0)
-#else
-#define ds_dbg(skt, lvl, fmt, arg...) do { } while (0)
-#endif
-
-
-
/** alloc_io_space
*
* Special stuff for managing IO windows, because they are scarce
@@ -72,14 +58,14 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr,
align = (*base) ? (lines ? 1<<lines : 0) : 1;
if (align && (align < num)) {
if (*base) {
- ds_dbg(s, 0, "odd IO request: num %#x align %#x\n",
+ dev_dbg(&s->dev, "odd IO request: num %#x align %#x\n",
num, align);
align = 0;
} else
while (align && (align < num)) align <<= 1;
}
if (*base & ~(align-1)) {
- ds_dbg(s, 0, "odd IO request: base %#x align %#x\n",
+ dev_dbg(&s->dev, "odd IO request: base %#x align %#x\n",
*base, align);
align = 0;
}
@@ -173,8 +159,10 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
s = p_dev->socket;
c = p_dev->function_config;
- if (!(c->state & CONFIG_LOCKED))
+ if (!(c->state & CONFIG_LOCKED)) {
+ dev_dbg(&s->dev, "Configuration isnt't locked\n");
return -EACCES;
+ }
addr = (c->ConfigBase + reg->Offset) >> 1;
@@ -188,6 +176,7 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
pcmcia_write_cis_mem(s, 1, addr, 1, &val);
break;
default:
+ dev_dbg(&s->dev, "Invalid conf register request\n");
return -EINVAL;
break;
}
@@ -196,68 +185,21 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
EXPORT_SYMBOL(pcmcia_access_configuration_register);
-/** pcmcia_get_window
- */
-int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle,
- int idx, win_req_t *req)
-{
- window_t *win;
- int w;
-
- if (!s || !(s->state & SOCKET_PRESENT))
- return -ENODEV;
- for (w = idx; w < MAX_WIN; w++)
- if (s->state & SOCKET_WIN_REQ(w))
- break;
- if (w == MAX_WIN)
- return -EINVAL;
- win = &s->win[w];
- req->Base = win->ctl.res->start;
- req->Size = win->ctl.res->end - win->ctl.res->start + 1;
- req->AccessSpeed = win->ctl.speed;
- req->Attributes = 0;
- if (win->ctl.flags & MAP_ATTRIB)
- req->Attributes |= WIN_MEMORY_TYPE_AM;
- if (win->ctl.flags & MAP_ACTIVE)
- req->Attributes |= WIN_ENABLE;
- if (win->ctl.flags & MAP_16BIT)
- req->Attributes |= WIN_DATA_WIDTH_16;
- if (win->ctl.flags & MAP_USE_WAIT)
- req->Attributes |= WIN_USE_WAIT;
- *handle = win;
- return 0;
-} /* pcmcia_get_window */
-EXPORT_SYMBOL(pcmcia_get_window);
-
-
-/** pcmcia_get_mem_page
- *
- * Change the card address of an already open memory window.
- */
-int pcmcia_get_mem_page(window_handle_t win, memreq_t *req)
+int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
+ memreq_t *req)
{
- if ((win == NULL) || (win->magic != WINDOW_MAGIC))
- return -EINVAL;
- req->Page = 0;
- req->CardOffset = win->ctl.card_start;
- return 0;
-} /* pcmcia_get_mem_page */
-EXPORT_SYMBOL(pcmcia_get_mem_page);
-
+ struct pcmcia_socket *s = p_dev->socket;
-int pcmcia_map_mem_page(window_handle_t win, memreq_t *req)
-{
- struct pcmcia_socket *s;
- if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+ wh--;
+ if (wh >= MAX_WIN)
return -EINVAL;
- s = win->sock;
if (req->Page != 0) {
- ds_dbg(s, 0, "failure: requested page is zero\n");
+ dev_dbg(&s->dev, "failure: requested page is zero\n");
return -EINVAL;
}
- win->ctl.card_start = req->CardOffset;
- if (s->ops->set_mem_map(s, &win->ctl) != 0) {
- ds_dbg(s, 0, "failed to set_mem_map\n");
+ s->win[wh].card_start = req->CardOffset;
+ if (s->ops->set_mem_map(s, &s->win[wh]) != 0) {
+ dev_dbg(&s->dev, "failed to set_mem_map\n");
return -EIO;
}
return 0;
@@ -278,10 +220,14 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
s = p_dev->socket;
c = p_dev->function_config;
- if (!(s->state & SOCKET_PRESENT))
+ if (!(s->state & SOCKET_PRESENT)) {
+ dev_dbg(&s->dev, "No card present\n");
return -ENODEV;
- if (!(c->state & CONFIG_LOCKED))
+ }
+ if (!(c->state & CONFIG_LOCKED)) {
+ dev_dbg(&s->dev, "Configuration isnt't locked\n");
return -EACCES;
+ }
if (mod->Attributes & CONF_IRQ_CHANGE_VALID) {
if (mod->Attributes & CONF_ENABLE_IRQ) {
@@ -295,7 +241,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
}
if (mod->Attributes & CONF_VCC_CHANGE_VALID) {
- ds_dbg(s, 0, "changing Vcc is not allowed at this time\n");
+ dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n");
return -EINVAL;
}
@@ -303,7 +249,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) &&
(mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
if (mod->Vpp1 != mod->Vpp2) {
- ds_dbg(s, 0, "Vpp1 and Vpp2 must be the same\n");
+ dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n");
return -EINVAL;
}
s->socket.Vpp = mod->Vpp1;
@@ -314,7 +260,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
}
} else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
(mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
- ds_dbg(s, 0, "changing Vcc is not allowed at this time\n");
+ dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n");
return -EINVAL;
}
@@ -425,11 +371,11 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
if (c->state & CONFIG_LOCKED)
return -EACCES;
if (c->irq.Attributes != req->Attributes) {
- ds_dbg(s, 0, "IRQ attributes must match assigned ones\n");
+ dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n");
return -EINVAL;
}
if (s->irq.AssignedIRQ != req->AssignedIRQ) {
- ds_dbg(s, 0, "IRQ must match assigned one\n");
+ dev_dbg(&s->dev, "IRQ must match assigned one\n");
return -EINVAL;
}
if (--s->irq.Config == 0) {
@@ -437,8 +383,8 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
s->irq.AssignedIRQ = 0;
}
- if (req->Attributes & IRQ_HANDLE_PRESENT) {
- free_irq(req->AssignedIRQ, req->Instance);
+ if (req->Handler) {
+ free_irq(req->AssignedIRQ, p_dev->priv);
}
#ifdef CONFIG_PCMCIA_PROBE
@@ -449,30 +395,34 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
} /* pcmcia_release_irq */
-int pcmcia_release_window(window_handle_t win)
+int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
{
- struct pcmcia_socket *s;
+ struct pcmcia_socket *s = p_dev->socket;
+ pccard_mem_map *win;
- if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+ wh--;
+ if (wh >= MAX_WIN)
return -EINVAL;
- s = win->sock;
- if (!(win->handle->_win & CLIENT_WIN_REQ(win->index)))
+
+ win = &s->win[wh];
+
+ if (!(p_dev->_win & CLIENT_WIN_REQ(wh))) {
+ dev_dbg(&s->dev, "not releasing unknown window\n");
return -EINVAL;
+ }
/* Shut down memory window */
- win->ctl.flags &= ~MAP_ACTIVE;
- s->ops->set_mem_map(s, &win->ctl);
- s->state &= ~SOCKET_WIN_REQ(win->index);
+ win->flags &= ~MAP_ACTIVE;
+ s->ops->set_mem_map(s, win);
+ s->state &= ~SOCKET_WIN_REQ(wh);
/* Release system memory */
- if (win->ctl.res) {
- release_resource(win->ctl.res);
- kfree(win->ctl.res);
- win->ctl.res = NULL;
+ if (win->res) {
+ release_resource(win->res);
+ kfree(win->res);
+ win->res = NULL;
}
- win->handle->_win &= ~CLIENT_WIN_REQ(win->index);
-
- win->magic = 0;
+ p_dev->_win &= ~CLIENT_WIN_REQ(wh);
return 0;
} /* pcmcia_release_window */
@@ -492,12 +442,14 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
return -ENODEV;
if (req->IntType & INT_CARDBUS) {
- ds_dbg(p_dev->socket, 0, "IntType may not be INT_CARDBUS\n");
+ dev_dbg(&s->dev, "IntType may not be INT_CARDBUS\n");
return -EINVAL;
}
c = p_dev->function_config;
- if (c->state & CONFIG_LOCKED)
+ if (c->state & CONFIG_LOCKED) {
+ dev_dbg(&s->dev, "Configuration is locked\n");
return -EACCES;
+ }
/* Do power control. We don't allow changes in Vcc. */
s->socket.Vpp = req->Vpp;
@@ -609,40 +561,44 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req)
struct pcmcia_socket *s = p_dev->socket;
config_t *c;
- if (!(s->state & SOCKET_PRESENT))
+ if (!(s->state & SOCKET_PRESENT)) {
+ dev_dbg(&s->dev, "No card present\n");
return -ENODEV;
+ }
if (!req)
return -EINVAL;
c = p_dev->function_config;
- if (c->state & CONFIG_LOCKED)
+ if (c->state & CONFIG_LOCKED) {
+ dev_dbg(&s->dev, "Configuration is locked\n");
return -EACCES;
+ }
if (c->state & CONFIG_IO_REQ) {
- ds_dbg(s, 0, "IO already configured\n");
+ dev_dbg(&s->dev, "IO already configured\n");
return -EBUSY;
}
if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) {
- ds_dbg(s, 0, "bad attribute setting for IO region 1\n");
+ dev_dbg(&s->dev, "bad attribute setting for IO region 1\n");
return -EINVAL;
}
if ((req->NumPorts2 > 0) &&
(req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) {
- ds_dbg(s, 0, "bad attribute setting for IO region 2\n");
+ dev_dbg(&s->dev, "bad attribute setting for IO region 2\n");
return -EINVAL;
}
- ds_dbg(s, 1, "trying to allocate resource 1\n");
+ dev_dbg(&s->dev, "trying to allocate resource 1\n");
if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
req->NumPorts1, req->IOAddrLines)) {
- ds_dbg(s, 0, "allocation of resource 1 failed\n");
+ dev_dbg(&s->dev, "allocation of resource 1 failed\n");
return -EBUSY;
}
if (req->NumPorts2) {
- ds_dbg(s, 1, "trying to allocate resource 2\n");
+ dev_dbg(&s->dev, "trying to allocate resource 2\n");
if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
req->NumPorts2, req->IOAddrLines)) {
- ds_dbg(s, 0, "allocation of resource 2 failed\n");
+ dev_dbg(&s->dev, "allocation of resource 2 failed\n");
release_io_space(s, req->BasePort1, req->NumPorts1);
return -EBUSY;
}
@@ -680,13 +636,17 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
int ret = -EINVAL, irq = 0;
int type;
- if (!(s->state & SOCKET_PRESENT))
+ if (!(s->state & SOCKET_PRESENT)) {
+ dev_dbg(&s->dev, "No card present\n");
return -ENODEV;
+ }
c = p_dev->function_config;
- if (c->state & CONFIG_LOCKED)
+ if (c->state & CONFIG_LOCKED) {
+ dev_dbg(&s->dev, "Configuration is locked\n");
return -EACCES;
+ }
if (c->state & CONFIG_IRQ_REQ) {
- ds_dbg(s, 0, "IRQ already configured\n");
+ dev_dbg(&s->dev, "IRQ already configured\n");
return -EBUSY;
}
@@ -704,7 +664,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
/* if the underlying IRQ infrastructure allows for it, only allocate
* the IRQ, but do not enable it
*/
- if (!(req->Attributes & IRQ_HANDLE_PRESENT))
+ if (!(req->Handler))
type |= IRQ_NOAUTOEN;
#endif /* IRQ_NOAUTOEN */
@@ -714,7 +674,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
} else {
int try;
u32 mask = s->irq_mask;
- void *data = &p_dev->dev.driver; /* something unique to this device */
+ void *data = p_dev; /* something unique to this device */
for (try = 0; try < 64; try++) {
irq = try % 32;
@@ -731,12 +691,12 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
* registering a dummy handle works, i.e. if the IRQ isn't
* marked as used by the kernel resource management core */
ret = request_irq(irq,
- (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action,
+ (req->Handler) ? req->Handler : test_action,
type,
p_dev->devname,
- (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data);
+ (req->Handler) ? p_dev->priv : data);
if (!ret) {
- if (!(req->Attributes & IRQ_HANDLE_PRESENT))
+ if (!req->Handler)
free_irq(irq, data);
break;
}
@@ -745,17 +705,22 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
#endif
/* only assign PCI irq if no IRQ already assigned */
if (ret && !s->irq.AssignedIRQ) {
- if (!s->pci_irq)
+ if (!s->pci_irq) {
+ dev_printk(KERN_INFO, &s->dev, "no IRQ found\n");
return ret;
+ }
type = IRQF_SHARED;
irq = s->pci_irq;
}
- if (ret && (req->Attributes & IRQ_HANDLE_PRESENT)) {
+ if (ret && req->Handler) {
ret = request_irq(irq, req->Handler, type,
- p_dev->devname, req->Instance);
- if (ret)
+ p_dev->devname, p_dev->priv);
+ if (ret) {
+ dev_printk(KERN_INFO, &s->dev,
+ "request_irq() failed\n");
return ret;
+ }
}
/* Make sure the fact the request type was overridden is passed back */
@@ -787,17 +752,19 @@ EXPORT_SYMBOL(pcmcia_request_irq);
* Request_window() establishes a mapping between card memory space
* and system memory space.
*/
-int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_handle_t *wh)
+int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_handle_t *wh)
{
- struct pcmcia_socket *s = (*p_dev)->socket;
- window_t *win;
+ struct pcmcia_socket *s = p_dev->socket;
+ pccard_mem_map *win;
u_long align;
int w;
- if (!(s->state & SOCKET_PRESENT))
+ if (!(s->state & SOCKET_PRESENT)) {
+ dev_dbg(&s->dev, "No card present\n");
return -ENODEV;
+ }
if (req->Attributes & (WIN_PAGED | WIN_SHARED)) {
- ds_dbg(s, 0, "bad attribute setting for iomem region\n");
+ dev_dbg(&s->dev, "bad attribute setting for iomem region\n");
return -EINVAL;
}
@@ -808,12 +775,12 @@ int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_h
(req->Attributes & WIN_STRICT_ALIGN)) ?
req->Size : s->map_size);
if (req->Size & (s->map_size-1)) {
- ds_dbg(s, 0, "invalid map size\n");
+ dev_dbg(&s->dev, "invalid map size\n");
return -EINVAL;
}
if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) ||
(req->Base & (align-1))) {
- ds_dbg(s, 0, "invalid base address\n");
+ dev_dbg(&s->dev, "invalid base address\n");
return -EINVAL;
}
if (req->Base)
@@ -823,52 +790,48 @@ int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_h
for (w = 0; w < MAX_WIN; w++)
if (!(s->state & SOCKET_WIN_REQ(w))) break;
if (w == MAX_WIN) {
- ds_dbg(s, 0, "all windows are used already\n");
+ dev_dbg(&s->dev, "all windows are used already\n");
return -EINVAL;
}
win = &s->win[w];
- win->magic = WINDOW_MAGIC;
- win->index = w;
- win->handle = *p_dev;
- win->sock = s;
if (!(s->features & SS_CAP_STATIC_MAP)) {
- win->ctl.res = pcmcia_find_mem_region(req->Base, req->Size, align,
+ win->res = pcmcia_find_mem_region(req->Base, req->Size, align,
(req->Attributes & WIN_MAP_BELOW_1MB), s);
- if (!win->ctl.res) {
- ds_dbg(s, 0, "allocating mem region failed\n");
+ if (!win->res) {
+ dev_dbg(&s->dev, "allocating mem region failed\n");
return -EINVAL;
}
}
- (*p_dev)->_win |= CLIENT_WIN_REQ(w);
+ p_dev->_win |= CLIENT_WIN_REQ(w);
/* Configure the socket controller */
- win->ctl.map = w+1;
- win->ctl.flags = 0;
- win->ctl.speed = req->AccessSpeed;
+ win->map = w+1;
+ win->flags = 0;
+ win->speed = req->AccessSpeed;
if (req->Attributes & WIN_MEMORY_TYPE)
- win->ctl.flags |= MAP_ATTRIB;
+ win->flags |= MAP_ATTRIB;
if (req->Attributes & WIN_ENABLE)
- win->ctl.flags |= MAP_ACTIVE;
+ win->flags |= MAP_ACTIVE;
if (req->Attributes & WIN_DATA_WIDTH_16)
- win->ctl.flags |= MAP_16BIT;
+ win->flags |= MAP_16BIT;
if (req->Attributes & WIN_USE_WAIT)
- win->ctl.flags |= MAP_USE_WAIT;
- win->ctl.card_start = 0;
- if (s->ops->set_mem_map(s, &win->ctl) != 0) {
- ds_dbg(s, 0, "failed to set memory mapping\n");
+ win->flags |= MAP_USE_WAIT;
+ win->card_start = 0;
+ if (s->ops->set_mem_map(s, win) != 0) {
+ dev_dbg(&s->dev, "failed to set memory mapping\n");
return -EIO;
}
s->state |= SOCKET_WIN_REQ(w);
/* Return window handle */
if (s->features & SS_CAP_STATIC_MAP) {
- req->Base = win->ctl.static_start;
+ req->Base = win->static_start;
} else {
- req->Base = win->ctl.res->start;
+ req->Base = win->res->start;
}
- *wh = win;
+ *wh = w + 1;
return 0;
} /* pcmcia_request_window */
@@ -879,19 +842,46 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev) {
pcmcia_release_io(p_dev, &p_dev->io);
pcmcia_release_irq(p_dev, &p_dev->irq);
if (p_dev->win)
- pcmcia_release_window(p_dev->win);
+ pcmcia_release_window(p_dev, p_dev->win);
}
EXPORT_SYMBOL(pcmcia_disable_device);
struct pcmcia_cfg_mem {
- tuple_t tuple;
+ struct pcmcia_device *p_dev;
+ void *priv_data;
+ int (*conf_check) (struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data);
cisparse_t parse;
- u8 buf[256];
cistpl_cftable_entry_t dflt;
};
/**
+ * pcmcia_do_loop_config() - internal helper for pcmcia_loop_config()
+ *
+ * pcmcia_do_loop_config() is the internal callback for the call from
+ * pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred
+ * by a struct pcmcia_cfg_mem.
+ */
+static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv)
+{
+ cistpl_cftable_entry_t *cfg = &parse->cftable_entry;
+ struct pcmcia_cfg_mem *cfg_mem = priv;
+
+ /* default values */
+ cfg_mem->p_dev->conf.ConfigIndex = cfg->index;
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ cfg_mem->dflt = *cfg;
+
+ return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt,
+ cfg_mem->p_dev->socket->socket.Vcc,
+ cfg_mem->priv_data);
+}
+
+/**
* pcmcia_loop_config() - loop over configuration options
* @p_dev: the struct pcmcia_device which we need to loop for.
* @conf_check: function to call for each configuration option.
@@ -913,48 +903,174 @@ int pcmcia_loop_config(struct pcmcia_device *p_dev,
void *priv_data)
{
struct pcmcia_cfg_mem *cfg_mem;
-
- tuple_t *tuple;
int ret;
- unsigned int vcc;
cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
if (cfg_mem == NULL)
return -ENOMEM;
- /* get the current Vcc setting */
- vcc = p_dev->socket->socket.Vcc;
+ cfg_mem->p_dev = p_dev;
+ cfg_mem->conf_check = conf_check;
+ cfg_mem->priv_data = priv_data;
- tuple = &cfg_mem->tuple;
- tuple->TupleData = cfg_mem->buf;
- tuple->TupleDataMax = 255;
- tuple->TupleOffset = 0;
- tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple->Attributes = 0;
+ ret = pccard_loop_tuple(p_dev->socket, p_dev->func,
+ CISTPL_CFTABLE_ENTRY, &cfg_mem->parse,
+ cfg_mem, pcmcia_do_loop_config);
- ret = pcmcia_get_first_tuple(p_dev, tuple);
- while (!ret) {
- cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
+ kfree(cfg_mem);
+ return ret;
+}
+EXPORT_SYMBOL(pcmcia_loop_config);
+
+
+struct pcmcia_loop_mem {
+ struct pcmcia_device *p_dev;
+ void *priv_data;
+ int (*loop_tuple) (struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data);
+};
+
+/**
+ * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config()
+ *
+ * pcmcia_do_loop_tuple() is the internal callback for the call from
+ * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred
+ * by a struct pcmcia_cfg_mem.
+ */
+static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv)
+{
+ struct pcmcia_loop_mem *loop = priv;
+
+ return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data);
+};
+
+/**
+ * pcmcia_loop_tuple() - loop over tuples in the CIS
+ * @p_dev: the struct pcmcia_device which we need to loop for.
+ * @code: which CIS code shall we look for?
+ * @priv_data: private data to be passed to the loop_tuple function.
+ * @loop_tuple: function to call for each CIS entry of type @function. IT
+ * gets passed the raw tuple and @priv_data.
+ *
+ * pcmcia_loop_tuple() loops over all CIS entries of type @function, and
+ * calls the @loop_tuple function for each entry. If the call to @loop_tuple
+ * returns 0, the loop exits. Returns 0 on success or errorcode otherwise.
+ */
+int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code,
+ int (*loop_tuple) (struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv_data),
+ void *priv_data)
+{
+ struct pcmcia_loop_mem loop = {
+ .p_dev = p_dev,
+ .loop_tuple = loop_tuple,
+ .priv_data = priv_data};
- if (pcmcia_get_tuple_data(p_dev, tuple))
- goto next_entry;
+ return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL,
+ &loop, pcmcia_do_loop_tuple);
+};
+EXPORT_SYMBOL(pcmcia_loop_tuple);
- if (pcmcia_parse_tuple(tuple, &cfg_mem->parse))
- goto next_entry;
- /* default values */
- p_dev->conf.ConfigIndex = cfg->index;
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- cfg_mem->dflt = *cfg;
+struct pcmcia_loop_get {
+ size_t len;
+ cisdata_t **buf;
+};
- ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
- if (!ret)
- break;
+/**
+ * pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple()
+ *
+ * pcmcia_do_get_tuple() is the internal callback for the call from
+ * pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in
+ * the first tuple, return 0 unconditionally. Create a memory buffer large
+ * enough to hold the content of the tuple, and fill it with the tuple data.
+ * The caller is responsible to free the buffer.
+ */
+static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple,
+ void *priv)
+{
+ struct pcmcia_loop_get *get = priv;
+
+ *get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL);
+ if (*get->buf) {
+ get->len = tuple->TupleDataLen;
+ memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen);
+ } else
+ dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n");
+ return 0;
+};
-next_entry:
- ret = pcmcia_get_next_tuple(p_dev, tuple);
+/**
+ * pcmcia_get_tuple() - get first tuple from CIS
+ * @p_dev: the struct pcmcia_device which we need to loop for.
+ * @code: which CIS code shall we look for?
+ * @buf: pointer to store the buffer to.
+ *
+ * pcmcia_get_tuple() gets the content of the first CIS entry of type @code.
+ * It returns the buffer length (or zero). The caller is responsible to free
+ * the buffer passed in @buf.
+ */
+size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code,
+ unsigned char **buf)
+{
+ struct pcmcia_loop_get get = {
+ .len = 0,
+ .buf = buf,
+ };
+
+ *get.buf = NULL;
+ pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get);
+
+ return get.len;
+};
+EXPORT_SYMBOL(pcmcia_get_tuple);
+
+
+/**
+ * pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis()
+ *
+ * pcmcia_do_get_mac() is the internal callback for the call from
+ * pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the
+ * tuple contains a proper LAN_NODE_ID of length 6, and copy the data
+ * to struct net_device->dev_addr[i].
+ */
+static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple,
+ void *priv)
+{
+ struct net_device *dev = priv;
+ int i;
+
+ if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID)
+ return -EINVAL;
+ if (tuple->TupleDataLen < ETH_ALEN + 2) {
+ dev_warn(&p_dev->dev, "Invalid CIS tuple length for "
+ "LAN_NODE_ID\n");
+ return -EINVAL;
}
- return ret;
-}
-EXPORT_SYMBOL(pcmcia_loop_config);
+ if (tuple->TupleData[1] != ETH_ALEN) {
+ dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = tuple->TupleData[i+2];
+ return 0;
+};
+
+/**
+ * pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE
+ * @p_dev: the struct pcmcia_device for which we want the address.
+ * @dev: a properly prepared struct net_device to store the info to.
+ *
+ * pcmcia_get_mac_from_cis() reads out the hardware MAC address from
+ * CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which
+ * must be set up properly by the driver (see examples!).
+ */
+int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev)
+{
+ return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev);
+};
+EXPORT_SYMBOL(pcmcia_get_mac_from_cis);
+
diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c
index 70a33468bcd..e1741cd875a 100644
--- a/drivers/pcmcia/pd6729.c
+++ b/drivers/pcmcia/pd6729.c
@@ -213,7 +213,8 @@ static irqreturn_t pd6729_interrupt(int irq, void *dev)
if (csc & I365_CSC_DETECT) {
events |= SS_DETECT;
- dprintk("Card detected in socket %i!\n", i);
+ dev_vdbg(&socket[i].socket.dev,
+ "Card detected in socket %i!\n", i);
}
if (indirect_read(&socket[i], I365_INTCTL)
@@ -331,11 +332,11 @@ static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
if (state->flags & SS_PWR_AUTO) {
- dprintk("Auto power\n");
+ dev_dbg(&sock->dev, "Auto power\n");
reg |= I365_PWR_AUTO; /* automatic power mngmnt */
}
if (state->flags & SS_OUTPUT_ENA) {
- dprintk("Power Enabled\n");
+ dev_dbg(&sock->dev, "Power Enabled\n");
reg |= I365_PWR_OUT; /* enable power */
}
@@ -343,40 +344,44 @@ static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
case 0:
break;
case 33:
- dprintk("setting voltage to Vcc to 3.3V on socket %i\n",
+ dev_dbg(&sock->dev,
+ "setting voltage to Vcc to 3.3V on socket %i\n",
socket->number);
reg |= I365_VCC_5V;
indirect_setbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
break;
case 50:
- dprintk("setting voltage to Vcc to 5V on socket %i\n",
+ dev_dbg(&sock->dev,
+ "setting voltage to Vcc to 5V on socket %i\n",
socket->number);
reg |= I365_VCC_5V;
indirect_resetbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
break;
default:
- dprintk("pd6729: pd6729_set_socket called with "
- "invalid VCC power value: %i\n",
- state->Vcc);
+ dev_dbg(&sock->dev,
+ "pd6729_set_socket called with invalid VCC power "
+ "value: %i\n", state->Vcc);
return -EINVAL;
}
switch (state->Vpp) {
case 0:
- dprintk("not setting Vpp on socket %i\n", socket->number);
+ dev_dbg(&sock->dev, "not setting Vpp on socket %i\n",
+ socket->number);
break;
case 33:
case 50:
- dprintk("setting Vpp to Vcc for socket %i\n", socket->number);
+ dev_dbg(&sock->dev, "setting Vpp to Vcc for socket %i\n",
+ socket->number);
reg |= I365_VPP1_5V;
break;
case 120:
- dprintk("setting Vpp to 12.0\n");
+ dev_dbg(&sock->dev, "setting Vpp to 12.0\n");
reg |= I365_VPP1_12V;
break;
default:
- dprintk("pd6729: pd6729_set_socket called with invalid VPP power value: %i\n",
- state->Vpp);
+ dev_dbg(&sock->dev, "pd6729: pd6729_set_socket called with "
+ "invalid VPP power value: %i\n", state->Vpp);
return -EINVAL;
}
@@ -438,7 +443,7 @@ static int pd6729_set_io_map(struct pcmcia_socket *sock,
/* Check error conditions */
if (map > 1) {
- dprintk("pd6729_set_io_map with invalid map");
+ dev_dbg(&sock->dev, "pd6729_set_io_map with invalid map\n");
return -EINVAL;
}
@@ -446,7 +451,7 @@ static int pd6729_set_io_map(struct pcmcia_socket *sock,
if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_IO(map))
indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_IO(map));
- /* dprintk("set_io_map: Setting range to %x - %x\n",
+ /* dev_dbg(&sock->dev, "set_io_map: Setting range to %x - %x\n",
io->start, io->stop);*/
/* write the new values */
@@ -478,12 +483,12 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock,
map = mem->map;
if (map > 4) {
- printk("pd6729_set_mem_map: invalid map");
+ dev_warn(&sock->dev, "invalid map requested\n");
return -EINVAL;
}
if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) {
- printk("pd6729_set_mem_map: invalid address / speed");
+ dev_warn(&sock->dev, "invalid invalid address / speed\n");
return -EINVAL;
}
@@ -529,12 +534,12 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock,
if (mem->flags & MAP_WRPROT)
i |= I365_MEM_WRPROT;
if (mem->flags & MAP_ATTRIB) {
- /* dprintk("requesting attribute memory for socket %i\n",
- socket->number);*/
+ /* dev_dbg(&sock->dev, "requesting attribute memory for "
+ "socket %i\n", socket->number);*/
i |= I365_MEM_REG;
} else {
- /* dprintk("requesting normal memory for socket %i\n",
- socket->number);*/
+ /* dev_dbg(&sock->dev, "requesting normal memory for "
+ "socket %i\n", socket->number);*/
}
indirect_write16(socket, base + I365_W_OFF, i);
@@ -577,7 +582,7 @@ static struct pccard_operations pd6729_operations = {
static irqreturn_t pd6729_test(int irq, void *dev)
{
- dprintk("-> hit on irq %d\n", irq);
+ pr_devel("-> hit on irq %d\n", irq);
return IRQ_HANDLED;
}
@@ -642,13 +647,13 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev,
goto err_out_free_mem;
if (!pci_resource_start(dev, 0)) {
- printk(KERN_INFO "pd6729: refusing to load the driver "
- "as the io_base is 0.\n");
+ dev_warn(&dev->dev, "refusing to load the driver as the "
+ "io_base is NULL.\n");
goto err_out_free_mem;
}
- printk(KERN_INFO "pd6729: Cirrus PD6729 PCI to PCMCIA Bridge "
- "at 0x%llx on irq %d\n",
+ dev_info(&dev->dev, "Cirrus PD6729 PCI to PCMCIA Bridge at 0x%llx "
+ "on irq %d\n",
(unsigned long long)pci_resource_start(dev, 0), dev->irq);
/*
* Since we have no memory BARs some firmware may not
@@ -656,14 +661,14 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev,
*/
pci_read_config_byte(dev, PCI_COMMAND, &configbyte);
if (!(configbyte & PCI_COMMAND_MEMORY)) {
- printk(KERN_DEBUG "pd6729: Enabling PCI_COMMAND_MEMORY.\n");
+ dev_dbg(&dev->dev, "pd6729: Enabling PCI_COMMAND_MEMORY.\n");
configbyte |= PCI_COMMAND_MEMORY;
pci_write_config_byte(dev, PCI_COMMAND, configbyte);
}
ret = pci_request_regions(dev, "pd6729");
if (ret) {
- printk(KERN_INFO "pd6729: pci request region failed.\n");
+ dev_warn(&dev->dev, "pci request region failed.\n");
goto err_out_disable;
}
@@ -672,7 +677,7 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev,
mask = pd6729_isa_scan();
if (irq_mode == 0 && mask == 0) {
- printk(KERN_INFO "pd6729: no ISA interrupt is available.\n");
+ dev_warn(&dev->dev, "no ISA interrupt is available.\n");
goto err_out_free_res;
}
@@ -697,8 +702,8 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev,
/* Register the interrupt handler */
if ((ret = request_irq(dev->irq, pd6729_interrupt, IRQF_SHARED,
"pd6729", socket))) {
- printk(KERN_ERR "pd6729: Failed to register irq %d, "
- "aborting\n", dev->irq);
+ dev_err(&dev->dev, "Failed to register irq %d\n",
+ dev->irq);
goto err_out_free_res;
}
} else {
@@ -713,8 +718,7 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev,
for (i = 0; i < MAX_SOCKETS; i++) {
ret = pcmcia_register_socket(&socket[i].socket);
if (ret) {
- printk(KERN_INFO "pd6729: pcmcia_register_socket "
- "failed.\n");
+ dev_warn(&dev->dev, "pcmcia_register_socket failed.\n");
for (j = 0; j < i ; j++)
pcmcia_unregister_socket(&socket[j].socket);
goto err_out_free_res2;
diff --git a/drivers/pcmcia/pd6729.h b/drivers/pcmcia/pd6729.h
index f392e458cdf..41418d394c5 100644
--- a/drivers/pcmcia/pd6729.h
+++ b/drivers/pcmcia/pd6729.h
@@ -1,13 +1,6 @@
#ifndef _INCLUDE_GUARD_PD6729_H_
#define _INCLUDE_GUARD_PD6729_H_
-/* Debuging defines */
-#ifdef NOTRACE
-#define dprintk(fmt, args...) printk(fmt , ## args)
-#else
-#define dprintk(fmt, args...) do {} while (0)
-#endif
-
/* Flags for I365_GENCTL */
#define I365_DF_VS1 0x40 /* DF-step Voltage Sense */
#define I365_DF_VS2 0x80
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 0e35acb1366..84dde7768ad 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -228,9 +228,43 @@ static const char *skt_names[] = {
#define SKT_DEV_INFO_SIZE(n) \
(sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
+int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
+{
+ skt->res_skt.start = _PCMCIA(skt->nr);
+ skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
+ skt->res_skt.name = skt_names[skt->nr];
+ skt->res_skt.flags = IORESOURCE_MEM;
+
+ skt->res_io.start = _PCMCIAIO(skt->nr);
+ skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
+ skt->res_io.name = "io";
+ skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+
+ skt->res_mem.start = _PCMCIAMem(skt->nr);
+ skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
+ skt->res_mem.name = "memory";
+ skt->res_mem.flags = IORESOURCE_MEM;
+
+ skt->res_attr.start = _PCMCIAAttr(skt->nr);
+ skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
+ skt->res_attr.name = "attribute";
+ skt->res_attr.flags = IORESOURCE_MEM;
+
+ return soc_pcmcia_add_one(skt);
+}
+
+void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
+{
+ /* Provide our PXA2xx specific timing routines. */
+ ops->set_timing = pxa2xx_pcmcia_set_timing;
+#ifdef CONFIG_CPU_FREQ
+ ops->frequency_change = pxa2xx_pcmcia_frequency_change;
+#endif
+}
+
int __pxa2xx_drv_pcmcia_probe(struct device *dev)
{
- int i, ret;
+ int i, ret = 0;
struct pcmcia_low_level *ops;
struct skt_dev_info *sinfo;
struct soc_pcmcia_socket *skt;
@@ -240,6 +274,8 @@ int __pxa2xx_drv_pcmcia_probe(struct device *dev)
ops = (struct pcmcia_low_level *)dev->platform_data;
+ pxa2xx_drv_pcmcia_ops(ops);
+
sinfo = kzalloc(SKT_DEV_INFO_SIZE(ops->nr), GFP_KERNEL);
if (!sinfo)
return -ENOMEM;
@@ -250,40 +286,25 @@ int __pxa2xx_drv_pcmcia_probe(struct device *dev)
for (i = 0; i < ops->nr; i++) {
skt = &sinfo->skt[i];
- skt->nr = ops->first + i;
- skt->irq = NO_IRQ;
-
- skt->res_skt.start = _PCMCIA(skt->nr);
- skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
- skt->res_skt.name = skt_names[skt->nr];
- skt->res_skt.flags = IORESOURCE_MEM;
-
- skt->res_io.start = _PCMCIAIO(skt->nr);
- skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
- skt->res_io.name = "io";
- skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ skt->nr = ops->first + i;
+ skt->ops = ops;
+ skt->socket.owner = ops->owner;
+ skt->socket.dev.parent = dev;
+ skt->socket.pci_irq = NO_IRQ;
- skt->res_mem.start = _PCMCIAMem(skt->nr);
- skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
- skt->res_mem.name = "memory";
- skt->res_mem.flags = IORESOURCE_MEM;
-
- skt->res_attr.start = _PCMCIAAttr(skt->nr);
- skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
- skt->res_attr.name = "attribute";
- skt->res_attr.flags = IORESOURCE_MEM;
+ ret = pxa2xx_drv_pcmcia_add_one(skt);
+ if (ret)
+ break;
}
- /* Provide our PXA2xx specific timing routines. */
- ops->set_timing = pxa2xx_pcmcia_set_timing;
-#ifdef CONFIG_CPU_FREQ
- ops->frequency_change = pxa2xx_pcmcia_frequency_change;
-#endif
-
- ret = soc_common_drv_pcmcia_probe(dev, ops, sinfo);
-
- if (!ret)
+ if (ret) {
+ while (--i >= 0)
+ soc_pcmcia_remove_one(&sinfo->skt[i]);
+ kfree(sinfo);
+ } else {
pxa2xx_configure_sockets(dev);
+ dev_set_drvdata(dev, sinfo);
+ }
return ret;
}
@@ -297,7 +318,16 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev)
{
- return soc_common_drv_pcmcia_remove(&dev->dev);
+ struct skt_dev_info *sinfo = platform_get_drvdata(dev);
+ int i;
+
+ platform_set_drvdata(dev, NULL);
+
+ for (i = 0; i < sinfo->nskt; i++)
+ soc_pcmcia_remove_one(&sinfo->skt[i]);
+
+ kfree(sinfo);
+ return 0;
}
static int pxa2xx_drv_pcmcia_suspend(struct device *dev)
diff --git a/drivers/pcmcia/pxa2xx_base.h b/drivers/pcmcia/pxa2xx_base.h
index 235d681652c..cb5efaec886 100644
--- a/drivers/pcmcia/pxa2xx_base.h
+++ b/drivers/pcmcia/pxa2xx_base.h
@@ -1,3 +1,6 @@
/* temporary measure */
extern int __pxa2xx_drv_pcmcia_probe(struct device *);
+int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt);
+void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops);
+
diff --git a/drivers/pcmcia/pxa2xx_cm_x255.c b/drivers/pcmcia/pxa2xx_cm_x255.c
index 5143a760153..05913d0bbdb 100644
--- a/drivers/pcmcia/pxa2xx_cm_x255.c
+++ b/drivers/pcmcia/pxa2xx_cm_x255.c
@@ -44,7 +44,7 @@ static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
return ret;
gpio_direction_output(GPIO_PCMCIA_RESET, 0);
- skt->irq = skt->nr == 0 ? PCMCIA_S0_RDYINT : PCMCIA_S1_RDYINT;
+ skt->socket.pci_irq = skt->nr == 0 ? PCMCIA_S0_RDYINT : PCMCIA_S1_RDYINT;
ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
if (!ret)
gpio_free(GPIO_PCMCIA_RESET);
diff --git a/drivers/pcmcia/pxa2xx_cm_x270.c b/drivers/pcmcia/pxa2xx_cm_x270.c
index a7b943d01e3..5662646b84d 100644
--- a/drivers/pcmcia/pxa2xx_cm_x270.c
+++ b/drivers/pcmcia/pxa2xx_cm_x270.c
@@ -38,7 +38,7 @@ static int cmx270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
return ret;
gpio_direction_output(GPIO_PCMCIA_RESET, 0);
- skt->irq = PCMCIA_S0_RDYINT;
+ skt->socket.pci_irq = PCMCIA_S0_RDYINT;
ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
if (!ret)
gpio_free(GPIO_PCMCIA_RESET);
diff --git a/drivers/pcmcia/pxa2xx_e740.c b/drivers/pcmcia/pxa2xx_e740.c
index d09c0dc4a31..8bfbd4dca13 100644
--- a/drivers/pcmcia/pxa2xx_e740.c
+++ b/drivers/pcmcia/pxa2xx_e740.c
@@ -38,7 +38,7 @@ static struct pcmcia_irqs cd_irqs[] = {
static int e740_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->irq = skt->nr == 0 ? IRQ_GPIO(GPIO_E740_PCMCIA_RDY0) :
+ skt->socket.pci_irq = skt->nr == 0 ? IRQ_GPIO(GPIO_E740_PCMCIA_RDY0) :
IRQ_GPIO(GPIO_E740_PCMCIA_RDY1);
return soc_pcmcia_request_irqs(skt, &cd_irqs[skt->nr], 1);
diff --git a/drivers/pcmcia/pxa2xx_lubbock.c b/drivers/pcmcia/pxa2xx_lubbock.c
index 6cbb1b1f7cf..b9f8c8fb42b 100644
--- a/drivers/pcmcia/pxa2xx_lubbock.c
+++ b/drivers/pcmcia/pxa2xx_lubbock.c
@@ -32,6 +32,7 @@ static int
lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
const socket_state_t *state)
{
+ struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int pa_dwr_mask, pa_dwr_set, misc_mask, misc_set;
int ret = 0;
@@ -149,7 +150,7 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
if (ret == 0) {
lubbock_set_misc_wr(misc_mask, misc_set);
- sa1111_set_io(SA1111_DEV(skt->dev), pa_dwr_mask, pa_dwr_set);
+ sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
}
#if 1
@@ -175,7 +176,7 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
* Switch to 5V, Configure socket with 5V voltage
*/
lubbock_set_misc_wr(misc_mask, 0);
- sa1111_set_io(SA1111_DEV(skt->dev), pa_dwr_mask, 0);
+ sa1111_set_io(s->dev, pa_dwr_mask, 0);
/*
* It takes about 100ms to turn off Vcc.
@@ -200,12 +201,8 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
static struct pcmcia_low_level lubbock_pcmcia_ops = {
.owner = THIS_MODULE,
- .hw_init = sa1111_pcmcia_hw_init,
- .hw_shutdown = sa1111_pcmcia_hw_shutdown,
- .socket_state = sa1111_pcmcia_socket_state,
.configure_socket = lubbock_pcmcia_configure_socket,
.socket_init = sa1111_pcmcia_socket_init,
- .socket_suspend = sa1111_pcmcia_socket_suspend,
.first = 0,
.nr = 2,
};
@@ -228,8 +225,9 @@ int pcmcia_lubbock_init(struct sa1111_dev *sadev)
/* Set CF Socket 1 power to standby mode. */
lubbock_set_misc_wr((1 << 15) | (1 << 14), 0);
- sadev->dev.platform_data = &lubbock_pcmcia_ops;
- ret = __pxa2xx_drv_pcmcia_probe(&sadev->dev);
+ pxa2xx_drv_pcmcia_ops(&lubbock_pcmcia_ops);
+ ret = sa1111_pcmcia_add(sadev, &lubbock_pcmcia_ops,
+ pxa2xx_drv_pcmcia_add_one);
}
return ret;
diff --git a/drivers/pcmcia/pxa2xx_mainstone.c b/drivers/pcmcia/pxa2xx_mainstone.c
index 1138551ba8f..92016fe932b 100644
--- a/drivers/pcmcia/pxa2xx_mainstone.c
+++ b/drivers/pcmcia/pxa2xx_mainstone.c
@@ -44,7 +44,7 @@ static int mst_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
* before we enable them as outputs.
*/
- skt->irq = (skt->nr == 0) ? MAINSTONE_S0_IRQ : MAINSTONE_S1_IRQ;
+ skt->socket.pci_irq = (skt->nr == 0) ? MAINSTONE_S0_IRQ : MAINSTONE_S1_IRQ;
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
diff --git a/drivers/pcmcia/pxa2xx_palmld.c b/drivers/pcmcia/pxa2xx_palmld.c
index 5ba9b3664a0..6fb6f7f0672 100644
--- a/drivers/pcmcia/pxa2xx_palmld.c
+++ b/drivers/pcmcia/pxa2xx_palmld.c
@@ -45,7 +45,7 @@ static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
if (ret)
goto err4;
- skt->irq = IRQ_GPIO(GPIO_NR_PALMLD_PCMCIA_READY);
+ skt->socket.pci_irq = IRQ_GPIO(GPIO_NR_PALMLD_PCMCIA_READY);
return 0;
err4:
diff --git a/drivers/pcmcia/pxa2xx_palmtx.c b/drivers/pcmcia/pxa2xx_palmtx.c
index e07b5c51ec5..b07b247a399 100644
--- a/drivers/pcmcia/pxa2xx_palmtx.c
+++ b/drivers/pcmcia/pxa2xx_palmtx.c
@@ -53,7 +53,7 @@ static int palmtx_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
if (ret)
goto err5;
- skt->irq = gpio_to_irq(GPIO_NR_PALMTX_PCMCIA_READY);
+ skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMTX_PCMCIA_READY);
return 0;
err5:
diff --git a/drivers/pcmcia/pxa2xx_sharpsl.c b/drivers/pcmcia/pxa2xx_sharpsl.c
index bc43f78f6f0..0ea3b29440e 100644
--- a/drivers/pcmcia/pxa2xx_sharpsl.c
+++ b/drivers/pcmcia/pxa2xx_sharpsl.c
@@ -66,7 +66,7 @@ static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
}
}
- skt->irq = SCOOP_DEV[skt->nr].irq;
+ skt->socket.pci_irq = SCOOP_DEV[skt->nr].irq;
return 0;
}
diff --git a/drivers/pcmcia/pxa2xx_trizeps4.c b/drivers/pcmcia/pxa2xx_trizeps4.c
index e0e5cb339b4..b7e596620db 100644
--- a/drivers/pcmcia/pxa2xx_trizeps4.c
+++ b/drivers/pcmcia/pxa2xx_trizeps4.c
@@ -53,7 +53,7 @@ static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
gpio_free(GPIO_PRDY);
return -EINVAL;
}
- skt->irq = IRQ_GPIO(GPIO_PRDY);
+ skt->socket.pci_irq = IRQ_GPIO(GPIO_PRDY);
break;
#ifndef CONFIG_MACH_TRIZEPS_CONXS
@@ -63,7 +63,7 @@ static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
break;
}
/* release the reset of this card */
- pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->irq);
+ pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq);
/* supplementory irqs for the socket */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c
index 17871360fe9..27be2e154df 100644
--- a/drivers/pcmcia/pxa2xx_viper.c
+++ b/drivers/pcmcia/pxa2xx_viper.c
@@ -40,7 +40,7 @@ static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
unsigned long flags;
- skt->irq = gpio_to_irq(VIPER_CF_RDY_GPIO);
+ skt->socket.pci_irq = gpio_to_irq(VIPER_CF_RDY_GPIO);
if (gpio_request(VIPER_CF_CD_GPIO, "CF detect"))
goto err_request_cd;
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
index e592e0e0d7e..de0e770ce6a 100644
--- a/drivers/pcmcia/rsrc_mgr.c
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -18,6 +18,7 @@
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
#include "cs_internal.h"
diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c
index ac8aa09ba0d..fd013a1ef47 100644
--- a/drivers/pcmcia/sa1100_assabet.c
+++ b/drivers/pcmcia/sa1100_assabet.c
@@ -27,7 +27,7 @@ static struct pcmcia_irqs irqs[] = {
static int assabet_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->irq = ASSABET_IRQ_GPIO_CF_IRQ;
+ skt->socket.pci_irq = ASSABET_IRQ_GPIO_CF_IRQ;
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
diff --git a/drivers/pcmcia/sa1100_badge4.c b/drivers/pcmcia/sa1100_badge4.c
index 1ca9737ea79..1ce53f493be 100644
--- a/drivers/pcmcia/sa1100_badge4.c
+++ b/drivers/pcmcia/sa1100_badge4.c
@@ -127,13 +127,10 @@ badge4_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state
static struct pcmcia_low_level badge4_pcmcia_ops = {
.owner = THIS_MODULE,
- .hw_init = sa1111_pcmcia_hw_init,
- .hw_shutdown = sa1111_pcmcia_hw_shutdown,
- .socket_state = sa1111_pcmcia_socket_state,
.configure_socket = badge4_pcmcia_configure_socket,
-
.socket_init = sa1111_pcmcia_socket_init,
- .socket_suspend = sa1111_pcmcia_socket_suspend,
+ .first = 0,
+ .nr = 2,
};
int pcmcia_badge4_init(struct device *dev)
@@ -146,7 +143,9 @@ int pcmcia_badge4_init(struct device *dev)
__func__,
badge4_pcmvcc, badge4_pcmvpp, badge4_cfvcc);
- ret = sa11xx_drv_pcmcia_probe(dev, &badge4_pcmcia_ops, 0, 2);
+ sa11xx_drv_pcmcia_ops(&badge4_pcmcia_ops);
+ ret = sa1111_pcmcia_add(dev, &badge4_pcmcia_ops,
+ sa11xx_drv_pcmcia_add_one);
}
return ret;
diff --git a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c
index 63e6bc431a0..9bf088b1727 100644
--- a/drivers/pcmcia/sa1100_cerf.c
+++ b/drivers/pcmcia/sa1100_cerf.c
@@ -27,7 +27,7 @@ static struct pcmcia_irqs irqs[] = {
static int cerf_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->irq = CERF_IRQ_GPIO_CF_IRQ;
+ skt->socket.pci_irq = CERF_IRQ_GPIO_CF_IRQ;
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c
index 2d0e9975153..8db86b90c20 100644
--- a/drivers/pcmcia/sa1100_generic.c
+++ b/drivers/pcmcia/sa1100_generic.c
@@ -51,7 +51,7 @@ static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = {
#ifdef CONFIG_SA1100_CERF
pcmcia_cerf_init,
#endif
-#ifdef CONFIG_SA1100_H3600
+#if defined(CONFIG_SA1100_H3100) || defined(CONFIG_SA1100_H3600)
pcmcia_h3600_init,
#endif
#ifdef CONFIG_SA1100_SHANNON
@@ -83,7 +83,16 @@ static int sa11x0_drv_pcmcia_probe(struct platform_device *dev)
static int sa11x0_drv_pcmcia_remove(struct platform_device *dev)
{
- return soc_common_drv_pcmcia_remove(&dev->dev);
+ struct skt_dev_info *sinfo = platform_get_drvdata(dev);
+ int i;
+
+ platform_set_drvdata(dev, NULL);
+
+ for (i = 0; i < sinfo->nskt; i++)
+ soc_pcmcia_remove_one(&sinfo->skt[i]);
+
+ kfree(sinfo);
+ return 0;
}
static int sa11x0_drv_pcmcia_suspend(struct platform_device *dev,
diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c
index 0cc3748f375..56329ad575a 100644
--- a/drivers/pcmcia/sa1100_h3600.c
+++ b/drivers/pcmcia/sa1100_h3600.c
@@ -10,47 +10,139 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <mach/h3600.h>
+#include <mach/h3xxx.h>
#include "sa1100_generic.h"
static struct pcmcia_irqs irqs[] = {
- { 0, IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" },
- { 1, IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" }
+ { .sock = 0, .str = "PCMCIA CD0" }, /* .irq will be filled later */
+ { .sock = 1, .str = "PCMCIA CD1" }
};
static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->irq = skt->nr ? IRQ_GPIO_H3600_PCMCIA_IRQ1
- : IRQ_GPIO_H3600_PCMCIA_IRQ0;
+ int err;
+ switch (skt->nr) {
+ case 0:
+ err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ0, "PCMCIA IRQ0");
+ if (err)
+ goto err00;
+ err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ0);
+ if (err)
+ goto err01;
+ skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ0);
+
+ err = gpio_request(H3XXX_GPIO_PCMCIA_CD0, "PCMCIA CD0");
+ if (err)
+ goto err01;
+ err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD0);
+ if (err)
+ goto err02;
+ irqs[0].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD0);
+
+ err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON");
+ if (err)
+ goto err02;
+ err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0);
+ if (err)
+ goto err03;
+ err = gpio_request(H3XXX_EGPIO_OPT_ON, "OPT ON");
+ if (err)
+ goto err03;
+ err = gpio_direction_output(H3XXX_EGPIO_OPT_ON, 0);
+ if (err)
+ goto err04;
+ err = gpio_request(H3XXX_EGPIO_OPT_RESET, "OPT RESET");
+ if (err)
+ goto err04;
+ err = gpio_direction_output(H3XXX_EGPIO_OPT_RESET, 0);
+ if (err)
+ goto err05;
+ err = gpio_request(H3XXX_EGPIO_CARD_RESET, "PCMCIA CARD RESET");
+ if (err)
+ goto err05;
+ err = gpio_direction_output(H3XXX_EGPIO_CARD_RESET, 0);
+ if (err)
+ goto err06;
+ err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ if (err)
+ goto err06;
+ break;
+ case 1:
+ err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ1, "PCMCIA IRQ1");
+ if (err)
+ goto err10;
+ err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ1);
+ if (err)
+ goto err11;
+ skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ1);
+
+ err = gpio_request(H3XXX_GPIO_PCMCIA_CD1, "PCMCIA CD1");
+ if (err)
+ goto err11;
+ err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD1);
+ if (err)
+ goto err12;
+ irqs[1].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD1);
+
+ err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ if (err)
+ goto err12;
+ break;
+ }
+ return 0;
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+err06: gpio_free(H3XXX_EGPIO_CARD_RESET);
+err05: gpio_free(H3XXX_EGPIO_OPT_RESET);
+err04: gpio_free(H3XXX_EGPIO_OPT_ON);
+err03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON);
+err02: gpio_free(H3XXX_GPIO_PCMCIA_CD0);
+err01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0);
+err00: return err;
+
+err12: gpio_free(H3XXX_GPIO_PCMCIA_CD0);
+err11: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0);
+err10: return err;
}
static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
- /* Disable CF bus: */
- assign_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON, 0);
- assign_h3600_egpio(IPAQ_EGPIO_OPT_ON, 0);
- assign_h3600_egpio(IPAQ_EGPIO_OPT_RESET, 1);
+ switch (skt->nr) {
+ case 0:
+ /* Disable CF bus: */
+ gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0);
+ gpio_set_value(H3XXX_EGPIO_OPT_ON, 0);
+ gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1);
+
+ gpio_free(H3XXX_EGPIO_CARD_RESET);
+ gpio_free(H3XXX_EGPIO_OPT_RESET);
+ gpio_free(H3XXX_EGPIO_OPT_ON);
+ gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON);
+ gpio_free(H3XXX_GPIO_PCMCIA_CD0);
+ gpio_free(H3XXX_GPIO_PCMCIA_IRQ0);
+ break;
+ case 1:
+ gpio_free(H3XXX_GPIO_PCMCIA_CD1);
+ gpio_free(H3XXX_GPIO_PCMCIA_IRQ1);
+ break;
+ }
}
static void
h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
- unsigned long levels = GPLR;
-
switch (skt->nr) {
case 0:
- state->detect = levels & GPIO_H3600_PCMCIA_CD0 ? 0 : 1;
- state->ready = levels & GPIO_H3600_PCMCIA_IRQ0 ? 1 : 0;
+ state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD0);
+ state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ0);
state->bvd1 = 0;
state->bvd2 = 0;
state->wrprot = 0; /* Not available on H3600. */
@@ -59,8 +151,8 @@ h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *st
break;
case 1:
- state->detect = levels & GPIO_H3600_PCMCIA_CD1 ? 0 : 1;
- state->ready = levels & GPIO_H3600_PCMCIA_IRQ1 ? 1 : 0;
+ state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD1);
+ state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ1);
state->bvd1 = 0;
state->bvd2 = 0;
state->wrprot = 0; /* Not available on H3600. */
@@ -79,7 +171,7 @@ h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_
return -1;
}
- assign_h3600_egpio(IPAQ_EGPIO_CARD_RESET, !!(state->flags & SS_RESET));
+ gpio_set_value(H3XXX_EGPIO_CARD_RESET, !!(state->flags & SS_RESET));
/* Silently ignore Vpp, output enable, speaker enable. */
@@ -89,9 +181,9 @@ h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_
static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
{
/* Enable CF bus: */
- assign_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON, 1);
- assign_h3600_egpio(IPAQ_EGPIO_OPT_ON, 1);
- assign_h3600_egpio(IPAQ_EGPIO_OPT_RESET, 0);
+ gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 1);
+ gpio_set_value(H3XXX_EGPIO_OPT_ON, 1);
+ gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0);
msleep(10);
@@ -109,10 +201,10 @@ static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
* socket 0 then socket 1.
*/
if (skt->nr == 1) {
- assign_h3600_egpio(IPAQ_EGPIO_OPT_ON, 0);
- assign_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON, 0);
+ gpio_set_value(H3XXX_EGPIO_OPT_ON, 0);
+ gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0);
/* hmm, does this suck power? */
- assign_h3600_egpio(IPAQ_EGPIO_OPT_RESET, 1);
+ gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1);
}
}
@@ -131,7 +223,7 @@ int __init pcmcia_h3600_init(struct device *dev)
{
int ret = -ENODEV;
- if (machine_is_h3600())
+ if (machine_is_h3600() || machine_is_h3100())
ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2);
return ret;
diff --git a/drivers/pcmcia/sa1100_jornada720.c b/drivers/pcmcia/sa1100_jornada720.c
index 7eedb42f800..6bcabee6bde 100644
--- a/drivers/pcmcia/sa1100_jornada720.c
+++ b/drivers/pcmcia/sa1100_jornada720.c
@@ -22,25 +22,10 @@
#define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3)
#define SOCKET1_3V GPIO_GPIO3
-static int jornada720_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
-{
- unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3;
-
- /*
- * What is all this crap for?
- */
- GRER |= 0x00000002;
- /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */
- sa1111_set_io_dir(SA1111_DEV(skt->dev), pin, 0, 0);
- sa1111_set_io(SA1111_DEV(skt->dev), pin, 0);
- sa1111_set_sleep_io(SA1111_DEV(skt->dev), pin, 0);
-
- return sa1111_pcmcia_hw_init(skt);
-}
-
static int
jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
+ struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int pa_dwr_mask, pa_dwr_set;
int ret;
@@ -97,7 +82,7 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s
unsigned long flags;
local_irq_save(flags);
- sa1111_set_io(SA1111_DEV(skt->dev), pa_dwr_mask, pa_dwr_set);
+ sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
local_irq_restore(flags);
}
@@ -106,21 +91,30 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s
static struct pcmcia_low_level jornada720_pcmcia_ops = {
.owner = THIS_MODULE,
- .hw_init = jornada720_pcmcia_hw_init,
- .hw_shutdown = sa1111_pcmcia_hw_shutdown,
- .socket_state = sa1111_pcmcia_socket_state,
.configure_socket = jornada720_pcmcia_configure_socket,
-
.socket_init = sa1111_pcmcia_socket_init,
- .socket_suspend = sa1111_pcmcia_socket_suspend,
+ .first = 0,
+ .nr = 2,
};
int __devinit pcmcia_jornada720_init(struct device *dev)
{
int ret = -ENODEV;
- if (machine_is_jornada720())
- ret = sa11xx_drv_pcmcia_probe(dev, &jornada720_pcmcia_ops, 0, 2);
+ if (machine_is_jornada720()) {
+ unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3;
+
+ GRER |= 0x00000002;
+
+ /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */
+ sa1111_set_io_dir(dev, pin, 0, 0);
+ sa1111_set_io(dev, pin, 0);
+ sa1111_set_sleep_io(dev, pin, 0);
+
+ sa11xx_drv_pcmcia_ops(&jornada720_pcmcia_ops);
+ ret = sa1111_pcmcia_add(dev, &jornada720_pcmcia_ops,
+ sa11xx_drv_pcmcia_add_one);
+ }
return ret;
}
diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c
index 0c76d337815..c95639b5f2a 100644
--- a/drivers/pcmcia/sa1100_neponset.c
+++ b/drivers/pcmcia/sa1100_neponset.c
@@ -43,6 +43,7 @@
static int
neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
+ struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int ncr_mask, ncr_set, pa_dwr_mask, pa_dwr_set;
int ret;
@@ -99,7 +100,7 @@ neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_sta
NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set;
local_irq_restore(flags);
- sa1111_set_io(SA1111_DEV(skt->dev), pa_dwr_mask, pa_dwr_set);
+ sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
}
return 0;
@@ -115,12 +116,10 @@ static void neponset_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
static struct pcmcia_low_level neponset_pcmcia_ops = {
.owner = THIS_MODULE,
- .hw_init = sa1111_pcmcia_hw_init,
- .hw_shutdown = sa1111_pcmcia_hw_shutdown,
- .socket_state = sa1111_pcmcia_socket_state,
.configure_socket = neponset_pcmcia_configure_socket,
.socket_init = neponset_pcmcia_socket_init,
- .socket_suspend = sa1111_pcmcia_socket_suspend,
+ .first = 0,
+ .nr = 2,
};
int pcmcia_neponset_init(struct sa1111_dev *sadev)
@@ -135,7 +134,9 @@ int pcmcia_neponset_init(struct sa1111_dev *sadev)
sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0);
sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
- ret = sa11xx_drv_pcmcia_probe(&sadev->dev, &neponset_pcmcia_ops, 0, 2);
+ sa11xx_drv_pcmcia_ops(&neponset_pcmcia_ops);
+ ret = sa1111_pcmcia_add(sadev, &neponset_pcmcia_ops,
+ sa11xx_drv_pcmcia_add_one);
}
return ret;
diff --git a/drivers/pcmcia/sa1100_shannon.c b/drivers/pcmcia/sa1100_shannon.c
index 46d8c1977c2..c4d51867a05 100644
--- a/drivers/pcmcia/sa1100_shannon.c
+++ b/drivers/pcmcia/sa1100_shannon.c
@@ -28,7 +28,7 @@ static int shannon_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
GAFR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 |
SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1);
- skt->irq = skt->nr ? SHANNON_IRQ_GPIO_RDY_1 : SHANNON_IRQ_GPIO_RDY_0;
+ skt->socket.pci_irq = skt->nr ? SHANNON_IRQ_GPIO_RDY_1 : SHANNON_IRQ_GPIO_RDY_0;
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
diff --git a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c
index 33a08ae09fd..05bd504e6f1 100644
--- a/drivers/pcmcia/sa1100_simpad.c
+++ b/drivers/pcmcia/sa1100_simpad.c
@@ -28,7 +28,7 @@ static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
- skt->irq = IRQ_GPIO_CF_IRQ;
+ skt->socket.pci_irq = IRQ_GPIO_CF_IRQ;
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c
index 4be4e172ffa..de6bc333d29 100644
--- a/drivers/pcmcia/sa1111_generic.c
+++ b/drivers/pcmcia/sa1111_generic.c
@@ -28,23 +28,20 @@ static struct pcmcia_irqs irqs[] = {
{ 1, IRQ_S1_BVD1_STSCHG, "SA1111 CF BVD1" },
};
-int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+static int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- if (skt->irq == NO_IRQ)
- skt->irq = skt->nr ? IRQ_S1_READY_NINT : IRQ_S0_READY_NINT;
-
return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
-void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+static void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
- struct sa1111_dev *sadev = SA1111_DEV(skt->dev);
- unsigned long status = sa1111_readl(sadev->mapbase + SA1111_PCSR);
+ struct sa1111_pcmcia_socket *s = to_skt(skt);
+ unsigned long status = sa1111_readl(s->dev->mapbase + SA1111_PCSR);
switch (skt->nr) {
case 0:
@@ -71,7 +68,7 @@ void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_sta
int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
- struct sa1111_dev *sadev = SA1111_DEV(skt->dev);
+ struct sa1111_pcmcia_socket *s = to_skt(skt);
unsigned int pccr_skt_mask, pccr_set_mask, val;
unsigned long flags;
@@ -100,10 +97,10 @@ int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s
pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT;
local_irq_save(flags);
- val = sa1111_readl(sadev->mapbase + SA1111_PCCR);
+ val = sa1111_readl(s->dev->mapbase + SA1111_PCCR);
val &= ~pccr_skt_mask;
val |= pccr_set_mask & pccr_skt_mask;
- sa1111_writel(val, sadev->mapbase + SA1111_PCCR);
+ sa1111_writel(val, s->dev->mapbase + SA1111_PCCR);
local_irq_restore(flags);
return 0;
@@ -114,15 +111,51 @@ void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
-void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+static void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
+int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
+ int (*add)(struct soc_pcmcia_socket *))
+{
+ struct sa1111_pcmcia_socket *s;
+ int i, ret = 0;
+
+ ops->hw_init = sa1111_pcmcia_hw_init;
+ ops->hw_shutdown = sa1111_pcmcia_hw_shutdown;
+ ops->socket_state = sa1111_pcmcia_socket_state;
+ ops->socket_suspend = sa1111_pcmcia_socket_suspend;
+
+ for (i = 0; i < ops->nr; i++) {
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ s->soc.nr = ops->first + i;
+ s->soc.ops = ops;
+ s->soc.socket.owner = ops->owner;
+ s->soc.socket.dev.parent = &dev->dev;
+ s->soc.socket.pci_irq = s->soc.nr ? IRQ_S1_READY_NINT : IRQ_S0_READY_NINT;
+ s->dev = dev;
+
+ ret = add(&s->soc);
+ if (ret == 0) {
+ s->next = dev_get_drvdata(&dev->dev);
+ dev_set_drvdata(&dev->dev, s);
+ } else
+ kfree(s);
+ }
+
+ return ret;
+}
+
static int pcmcia_probe(struct sa1111_dev *dev)
{
void __iomem *base;
+ dev_set_drvdata(&dev->dev, NULL);
+
if (!request_mem_region(dev->res.start, 512,
SA1111_DRIVER_NAME(dev)))
return -EBUSY;
@@ -152,7 +185,15 @@ static int pcmcia_probe(struct sa1111_dev *dev)
static int __devexit pcmcia_remove(struct sa1111_dev *dev)
{
- soc_common_drv_pcmcia_remove(&dev->dev);
+ struct sa1111_pcmcia_socket *next, *s = dev_get_drvdata(&dev->dev);
+
+ dev_set_drvdata(&dev->dev, NULL);
+
+ for (; next = s->next, s; s = next) {
+ soc_pcmcia_remove_one(&s->soc);
+ kfree(s);
+ }
+
release_mem_region(dev->res.start, 512);
return 0;
}
diff --git a/drivers/pcmcia/sa1111_generic.h b/drivers/pcmcia/sa1111_generic.h
index 10ced4a210d..02dc8577cda 100644
--- a/drivers/pcmcia/sa1111_generic.h
+++ b/drivers/pcmcia/sa1111_generic.h
@@ -1,12 +1,23 @@
#include "soc_common.h"
#include "sa11xx_base.h"
-extern int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *);
-extern void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *);
+struct sa1111_pcmcia_socket {
+ struct soc_pcmcia_socket soc;
+ struct sa1111_dev *dev;
+ struct sa1111_pcmcia_socket *next;
+};
+
+static inline struct sa1111_pcmcia_socket *to_skt(struct soc_pcmcia_socket *s)
+{
+ return container_of(s, struct sa1111_pcmcia_socket, soc);
+}
+
+int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
+ int (*add)(struct soc_pcmcia_socket *));
+
extern void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *, struct pcmcia_state *);
extern int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *, const socket_state_t *);
extern void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *);
-extern void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *);
extern int pcmcia_badge4_init(struct device *);
extern int pcmcia_jornada720_init(struct device *);
diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c
index e15d59f2d8a..fc9a6527019 100644
--- a/drivers/pcmcia/sa11xx_base.c
+++ b/drivers/pcmcia/sa11xx_base.c
@@ -171,12 +171,58 @@ static const char *skt_names[] = {
#define SKT_DEV_INFO_SIZE(n) \
(sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
+int sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
+{
+ skt->res_skt.start = _PCMCIA(skt->nr);
+ skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
+ skt->res_skt.name = skt_names[skt->nr];
+ skt->res_skt.flags = IORESOURCE_MEM;
+
+ skt->res_io.start = _PCMCIAIO(skt->nr);
+ skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
+ skt->res_io.name = "io";
+ skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+
+ skt->res_mem.start = _PCMCIAMem(skt->nr);
+ skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
+ skt->res_mem.name = "memory";
+ skt->res_mem.flags = IORESOURCE_MEM;
+
+ skt->res_attr.start = _PCMCIAAttr(skt->nr);
+ skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
+ skt->res_attr.name = "attribute";
+ skt->res_attr.flags = IORESOURCE_MEM;
+
+ return soc_pcmcia_add_one(skt);
+}
+EXPORT_SYMBOL(sa11xx_drv_pcmcia_add_one);
+
+void sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
+{
+ /*
+ * set default MECR calculation if the board specific
+ * code did not specify one...
+ */
+ if (!ops->get_timing)
+ ops->get_timing = sa1100_pcmcia_default_mecr_timing;
+
+ /* Provide our SA11x0 specific timing routines. */
+ ops->set_timing = sa1100_pcmcia_set_timing;
+ ops->show_timing = sa1100_pcmcia_show_timing;
+#ifdef CONFIG_CPU_FREQ
+ ops->frequency_change = sa1100_pcmcia_frequency_change;
+#endif
+}
+EXPORT_SYMBOL(sa11xx_drv_pcmcia_ops);
+
int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
int first, int nr)
{
struct skt_dev_info *sinfo;
struct soc_pcmcia_socket *skt;
- int i;
+ int i, ret = 0;
+
+ sa11xx_drv_pcmcia_ops(ops);
sinfo = kzalloc(SKT_DEV_INFO_SIZE(nr), GFP_KERNEL);
if (!sinfo)
@@ -188,45 +234,26 @@ int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
for (i = 0; i < nr; i++) {
skt = &sinfo->skt[i];
- skt->nr = first + i;
- skt->irq = NO_IRQ;
-
- skt->res_skt.start = _PCMCIA(skt->nr);
- skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
- skt->res_skt.name = skt_names[skt->nr];
- skt->res_skt.flags = IORESOURCE_MEM;
-
- skt->res_io.start = _PCMCIAIO(skt->nr);
- skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
- skt->res_io.name = "io";
- skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ skt->nr = first + i;
+ skt->ops = ops;
+ skt->socket.owner = ops->owner;
+ skt->socket.dev.parent = dev;
+ skt->socket.pci_irq = NO_IRQ;
- skt->res_mem.start = _PCMCIAMem(skt->nr);
- skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
- skt->res_mem.name = "memory";
- skt->res_mem.flags = IORESOURCE_MEM;
-
- skt->res_attr.start = _PCMCIAAttr(skt->nr);
- skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
- skt->res_attr.name = "attribute";
- skt->res_attr.flags = IORESOURCE_MEM;
+ ret = sa11xx_drv_pcmcia_add_one(skt);
+ if (ret)
+ break;
}
- /*
- * set default MECR calculation if the board specific
- * code did not specify one...
- */
- if (!ops->get_timing)
- ops->get_timing = sa1100_pcmcia_default_mecr_timing;
-
- /* Provide our SA11x0 specific timing routines. */
- ops->set_timing = sa1100_pcmcia_set_timing;
- ops->show_timing = sa1100_pcmcia_show_timing;
-#ifdef CONFIG_CPU_FREQ
- ops->frequency_change = sa1100_pcmcia_frequency_change;
-#endif
+ if (ret) {
+ while (--i >= 0)
+ soc_pcmcia_remove_one(&sinfo->skt[i]);
+ kfree(sinfo);
+ } else {
+ dev_set_drvdata(dev, sinfo);
+ }
- return soc_common_drv_pcmcia_probe(dev, ops, sinfo);
+ return ret;
}
EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
diff --git a/drivers/pcmcia/sa11xx_base.h b/drivers/pcmcia/sa11xx_base.h
index 7bc20828052..3d76d720f46 100644
--- a/drivers/pcmcia/sa11xx_base.h
+++ b/drivers/pcmcia/sa11xx_base.h
@@ -118,6 +118,8 @@ static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz,
}
+int sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt);
+void sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops);
extern int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr);
#endif /* !defined(_PCMCIA_SA1100_H) */
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index ef7e9e58782..6f1a86b43c6 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -144,10 +144,10 @@ soc_common_pcmcia_config_skt(struct soc_pcmcia_socket *skt, socket_state_t *stat
*/
if (skt->irq_state != 1 && state->io_irq) {
skt->irq_state = 1;
- set_irq_type(skt->irq, IRQ_TYPE_EDGE_FALLING);
+ set_irq_type(skt->socket.pci_irq, IRQ_TYPE_EDGE_FALLING);
} else if (skt->irq_state == 1 && state->io_irq == 0) {
skt->irq_state = 0;
- set_irq_type(skt->irq, IRQ_TYPE_NONE);
+ set_irq_type(skt->socket.pci_irq, IRQ_TYPE_NONE);
}
skt->cs_state = *state;
@@ -492,7 +492,8 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr, ch
p+=sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc);
p+=sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp);
- p+=sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, skt->irq);
+ p+=sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq,
+ skt->socket.pci_irq);
if (skt->ops->show_timing)
p+=skt->ops->show_timing(skt, p);
@@ -574,7 +575,7 @@ void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt,
EXPORT_SYMBOL(soc_pcmcia_enable_irqs);
-LIST_HEAD(soc_pcmcia_sockets);
+static LIST_HEAD(soc_pcmcia_sockets);
static DEFINE_MUTEX(soc_pcmcia_sockets_lock);
#ifdef CONFIG_CPU_FREQ
@@ -609,177 +610,137 @@ static int soc_pcmcia_cpufreq_register(void)
"notifier for PCMCIA (%d)\n", ret);
return ret;
}
+fs_initcall(soc_pcmcia_cpufreq_register);
static void soc_pcmcia_cpufreq_unregister(void)
{
cpufreq_unregister_notifier(&soc_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
}
+module_exit(soc_pcmcia_cpufreq_unregister);
-#else
-static int soc_pcmcia_cpufreq_register(void) { return 0; }
-static void soc_pcmcia_cpufreq_unregister(void) {}
#endif
-int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
- struct skt_dev_info *sinfo)
+void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt)
{
- struct soc_pcmcia_socket *skt;
- int ret, i;
-
mutex_lock(&soc_pcmcia_sockets_lock);
+ del_timer_sync(&skt->poll_timer);
- /*
- * Initialise the per-socket structure.
- */
- for (i = 0; i < sinfo->nskt; i++) {
- skt = &sinfo->skt[i];
+ pcmcia_unregister_socket(&skt->socket);
- skt->socket.ops = &soc_common_pcmcia_operations;
- skt->socket.owner = ops->owner;
- skt->socket.dev.parent = dev;
+ flush_scheduled_work();
- init_timer(&skt->poll_timer);
- skt->poll_timer.function = soc_common_pcmcia_poll_event;
- skt->poll_timer.data = (unsigned long)skt;
- skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD;
+ skt->ops->hw_shutdown(skt);
- skt->dev = dev;
- skt->ops = ops;
+ soc_common_pcmcia_config_skt(skt, &dead_socket);
- ret = request_resource(&iomem_resource, &skt->res_skt);
- if (ret)
- goto out_err_1;
+ list_del(&skt->node);
+ mutex_unlock(&soc_pcmcia_sockets_lock);
- ret = request_resource(&skt->res_skt, &skt->res_io);
- if (ret)
- goto out_err_2;
+ iounmap(skt->virt_io);
+ skt->virt_io = NULL;
+ release_resource(&skt->res_attr);
+ release_resource(&skt->res_mem);
+ release_resource(&skt->res_io);
+ release_resource(&skt->res_skt);
+}
+EXPORT_SYMBOL(soc_pcmcia_remove_one);
- ret = request_resource(&skt->res_skt, &skt->res_mem);
- if (ret)
- goto out_err_3;
+int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
+{
+ int ret;
- ret = request_resource(&skt->res_skt, &skt->res_attr);
- if (ret)
- goto out_err_4;
+ init_timer(&skt->poll_timer);
+ skt->poll_timer.function = soc_common_pcmcia_poll_event;
+ skt->poll_timer.data = (unsigned long)skt;
+ skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD;
- skt->virt_io = ioremap(skt->res_io.start, 0x10000);
- if (skt->virt_io == NULL) {
- ret = -ENOMEM;
- goto out_err_5;
- }
+ ret = request_resource(&iomem_resource, &skt->res_skt);
+ if (ret)
+ goto out_err_1;
- if (list_empty(&soc_pcmcia_sockets))
- soc_pcmcia_cpufreq_register();
+ ret = request_resource(&skt->res_skt, &skt->res_io);
+ if (ret)
+ goto out_err_2;
- list_add(&skt->node, &soc_pcmcia_sockets);
+ ret = request_resource(&skt->res_skt, &skt->res_mem);
+ if (ret)
+ goto out_err_3;
- /*
- * We initialize default socket timing here, because
- * we are not guaranteed to see a SetIOMap operation at
- * runtime.
- */
- ops->set_timing(skt);
+ ret = request_resource(&skt->res_skt, &skt->res_attr);
+ if (ret)
+ goto out_err_4;
- ret = ops->hw_init(skt);
- if (ret)
- goto out_err_6;
+ skt->virt_io = ioremap(skt->res_io.start, 0x10000);
+ if (skt->virt_io == NULL) {
+ ret = -ENOMEM;
+ goto out_err_5;
+ }
- skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD;
- skt->socket.resource_ops = &pccard_static_ops;
- skt->socket.irq_mask = 0;
- skt->socket.map_size = PAGE_SIZE;
- skt->socket.pci_irq = skt->irq;
- skt->socket.io_offset = (unsigned long)skt->virt_io;
+ mutex_lock(&soc_pcmcia_sockets_lock);
- skt->status = soc_common_pcmcia_skt_state(skt);
+ list_add(&skt->node, &soc_pcmcia_sockets);
- ret = pcmcia_register_socket(&skt->socket);
- if (ret)
- goto out_err_7;
+ /*
+ * We initialize default socket timing here, because
+ * we are not guaranteed to see a SetIOMap operation at
+ * runtime.
+ */
+ skt->ops->set_timing(skt);
- WARN_ON(skt->socket.sock != i);
+ ret = skt->ops->hw_init(skt);
+ if (ret)
+ goto out_err_6;
- add_timer(&skt->poll_timer);
+ skt->socket.ops = &soc_common_pcmcia_operations;
+ skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD;
+ skt->socket.resource_ops = &pccard_static_ops;
+ skt->socket.irq_mask = 0;
+ skt->socket.map_size = PAGE_SIZE;
+ skt->socket.io_offset = (unsigned long)skt->virt_io;
- ret = device_create_file(&skt->socket.dev, &dev_attr_status);
- if (ret)
- goto out_err_8;
- }
+ skt->status = soc_common_pcmcia_skt_state(skt);
- dev_set_drvdata(dev, sinfo);
- ret = 0;
- goto out;
+ ret = pcmcia_register_socket(&skt->socket);
+ if (ret)
+ goto out_err_7;
- do {
- skt = &sinfo->skt[i];
+ add_timer(&skt->poll_timer);
+
+ mutex_unlock(&soc_pcmcia_sockets_lock);
+
+ ret = device_create_file(&skt->socket.dev, &dev_attr_status);
+ if (ret)
+ goto out_err_8;
+
+ return ret;
- device_remove_file(&skt->socket.dev, &dev_attr_status);
out_err_8:
- del_timer_sync(&skt->poll_timer);
- pcmcia_unregister_socket(&skt->socket);
+ mutex_lock(&soc_pcmcia_sockets_lock);
+ del_timer_sync(&skt->poll_timer);
+ pcmcia_unregister_socket(&skt->socket);
out_err_7:
- flush_scheduled_work();
+ flush_scheduled_work();
- ops->hw_shutdown(skt);
+ skt->ops->hw_shutdown(skt);
out_err_6:
- list_del(&skt->node);
- iounmap(skt->virt_io);
+ list_del(&skt->node);
+ mutex_unlock(&soc_pcmcia_sockets_lock);
+ iounmap(skt->virt_io);
out_err_5:
- release_resource(&skt->res_attr);
+ release_resource(&skt->res_attr);
out_err_4:
- release_resource(&skt->res_mem);
+ release_resource(&skt->res_mem);
out_err_3:
- release_resource(&skt->res_io);
+ release_resource(&skt->res_io);
out_err_2:
- release_resource(&skt->res_skt);
+ release_resource(&skt->res_skt);
out_err_1:
- i--;
- } while (i > 0);
- kfree(sinfo);
-
- out:
- mutex_unlock(&soc_pcmcia_sockets_lock);
return ret;
}
+EXPORT_SYMBOL(soc_pcmcia_add_one);
-int soc_common_drv_pcmcia_remove(struct device *dev)
-{
- struct skt_dev_info *sinfo = dev_get_drvdata(dev);
- int i;
-
- dev_set_drvdata(dev, NULL);
-
- mutex_lock(&soc_pcmcia_sockets_lock);
- for (i = 0; i < sinfo->nskt; i++) {
- struct soc_pcmcia_socket *skt = &sinfo->skt[i];
-
- del_timer_sync(&skt->poll_timer);
-
- pcmcia_unregister_socket(&skt->socket);
-
- flush_scheduled_work();
-
- skt->ops->hw_shutdown(skt);
-
- soc_common_pcmcia_config_skt(skt, &dead_socket);
-
- list_del(&skt->node);
- iounmap(skt->virt_io);
- skt->virt_io = NULL;
- release_resource(&skt->res_attr);
- release_resource(&skt->res_mem);
- release_resource(&skt->res_io);
- release_resource(&skt->res_skt);
- }
- if (list_empty(&soc_pcmcia_sockets))
- soc_pcmcia_cpufreq_unregister();
-
- mutex_unlock(&soc_pcmcia_sockets_lock);
-
- kfree(sinfo);
-
- return 0;
-}
-EXPORT_SYMBOL(soc_common_drv_pcmcia_remove);
+MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services: Common SoC support");
+MODULE_LICENSE("Dual MPL/GPL");
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h
index 290e143839e..e40824ce6b0 100644
--- a/drivers/pcmcia/soc_common.h
+++ b/drivers/pcmcia/soc_common.h
@@ -30,14 +30,12 @@ struct soc_pcmcia_socket {
/*
* Info from low level handler
*/
- struct device *dev;
unsigned int nr;
- unsigned int irq;
/*
* Core PCMCIA state
*/
- struct pcmcia_low_level *ops;
+ const struct pcmcia_low_level *ops;
unsigned int status;
socket_state_t cs_state;
@@ -135,10 +133,8 @@ extern void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_
extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *);
-extern struct list_head soc_pcmcia_sockets;
-
-extern int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, struct skt_dev_info *sinfo);
-extern int soc_common_drv_pcmcia_remove(struct device *dev);
+void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt);
+int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt);
#ifdef CONFIG_PCMCIA_DEBUG
diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c
index 6918849d511..12c49ee135e 100644
--- a/drivers/pcmcia/tcic.c
+++ b/drivers/pcmcia/tcic.c
@@ -55,21 +55,6 @@
#include <pcmcia/ss.h>
#include "tcic.h"
-#ifdef CONFIG_PCMCIA_DEBUG
-static int pc_debug;
-
-module_param(pc_debug, int, 0644);
-static const char version[] =
-"tcic.c 1.111 2000/02/15 04:13:12 (David Hinds)";
-
-#define debug(lvl, fmt, arg...) do { \
- if (pc_debug > (lvl)) \
- printk(KERN_DEBUG "tcic: " fmt , ## arg); \
-} while (0)
-#else
-#define debug(lvl, fmt, arg...) do { } while (0)
-#endif
-
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver");
MODULE_LICENSE("Dual MPL/GPL");
@@ -574,7 +559,7 @@ static irqreturn_t tcic_interrupt(int irq, void *dev)
} else
active = 1;
- debug(2, "tcic_interrupt()\n");
+ pr_debug("tcic_interrupt()\n");
for (i = 0; i < sockets; i++) {
psock = socket_table[i].psock;
@@ -611,13 +596,13 @@ static irqreturn_t tcic_interrupt(int irq, void *dev)
}
active = 0;
- debug(2, "interrupt done\n");
+ pr_debug("interrupt done\n");
return IRQ_HANDLED;
} /* tcic_interrupt */
static void tcic_timer(u_long data)
{
- debug(2, "tcic_timer()\n");
+ pr_debug("tcic_timer()\n");
tcic_timer_pending = 0;
tcic_interrupt(0, NULL);
} /* tcic_timer */
@@ -644,7 +629,7 @@ static int tcic_get_status(struct pcmcia_socket *sock, u_int *value)
reg = tcic_getb(TCIC_PWR);
if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
*value |= SS_POWERON;
- debug(1, "GetStatus(%d) = %#2.2x\n", psock, *value);
+ dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value);
return 0;
} /* tcic_get_status */
@@ -656,7 +641,7 @@ static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
u_char reg;
u_short scf1, scf2;
- debug(1, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
"io_irq %d, csc_mask %#2.2x)\n", psock, state->flags,
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
@@ -731,7 +716,7 @@ static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
u_int addr;
u_short base, len, ioctl;
- debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, "
+ dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx)\n", psock, io->map, io->flags, io->speed,
(unsigned long long)io->start, (unsigned long long)io->stop);
if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
@@ -768,7 +753,7 @@ static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *m
u_short addr, ctl;
u_long base, len, mmap;
- debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, "
+ dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, "
"%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags,
mem->speed, (unsigned long long)mem->res->start,
(unsigned long long)mem->res->end, mem->card_start);
diff --git a/drivers/pcmcia/topic.h b/drivers/pcmcia/topic.h
index edccfa5bb40..615a45a8fe8 100644
--- a/drivers/pcmcia/topic.h
+++ b/drivers/pcmcia/topic.h
@@ -114,22 +114,17 @@ static void topic97_zoom_video(struct pcmcia_socket *sock, int onoff)
reg_zv |= TOPIC97_ZV_CONTROL_ENABLE;
config_writeb(socket, TOPIC97_ZOOM_VIDEO_CONTROL, reg_zv);
- reg = config_readb(socket, TOPIC97_MISC2);
- reg |= TOPIC97_MISC2_ZV_ENABLE;
- config_writeb(socket, TOPIC97_MISC2, reg);
-
- /* not sure this is needed, doc is unclear */
-#if 0
reg = config_readb(socket, TOPIC97_AUDIO_VIDEO_SWITCH);
reg |= TOPIC97_AVS_AUDIO_CONTROL | TOPIC97_AVS_VIDEO_CONTROL;
config_writeb(socket, TOPIC97_AUDIO_VIDEO_SWITCH, reg);
-#endif
- }
- else {
+ } else {
reg_zv &= ~TOPIC97_ZV_CONTROL_ENABLE;
config_writeb(socket, TOPIC97_ZOOM_VIDEO_CONTROL, reg_zv);
- }
+ reg = config_readb(socket, TOPIC97_AUDIO_VIDEO_SWITCH);
+ reg &= ~(TOPIC97_AVS_AUDIO_CONTROL | TOPIC97_AVS_VIDEO_CONTROL);
+ config_writeb(socket, TOPIC97_AUDIO_VIDEO_SWITCH, reg);
+ }
}
static int topic97_override(struct yenta_socket *socket)
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 9652c3fe7f5..8390dca2b4e 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -1,5 +1,3 @@
-# drivers/platform/Kconfig
-
if X86
source "drivers/platform/x86/Kconfig"
endif
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
index 29432a50be4..f0a90a6bf39 100644
--- a/drivers/platform/x86/intel_menlow.c
+++ b/drivers/platform/x86/intel_menlow.c
@@ -510,7 +510,7 @@ static int __init intel_menlow_module_init(void)
/* Looking for sensors in each ACPI thermal zone */
status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
- intel_menlow_register_sensor, NULL, NULL);
+ intel_menlow_register_sensor, NULL, NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index a2a742c8ff7..7a2cc8a5c97 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -1203,7 +1203,7 @@ static int sony_nc_add(struct acpi_device *device)
if (debug) {
status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
- 1, sony_walk_callback, NULL, NULL);
+ 1, sony_walk_callback, NULL, NULL, NULL);
if (ACPI_FAILURE(status)) {
printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
result = -ENODEV;
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index a848c7e20ae..0ed84806f8a 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -1087,7 +1087,7 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
*/
status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
- tpacpi_acpi_walk_find_bcl, NULL,
+ tpacpi_acpi_walk_find_bcl, NULL, NULL,
&bcl_ptr);
if (ACPI_SUCCESS(status) && bcl_levels > 2) {
@@ -6545,7 +6545,7 @@ static struct ibm_struct volume_driver_data = {
* The speeds are stored on handles
* (FANA:FAN9), (FANC:FANB), (FANE:FAND).
*
- * There are three default speed sets, acessible as handles:
+ * There are three default speed sets, accessible as handles:
* FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
*
* ACPI DSDT switches which set is in use depending on various
diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c
index 87b4f49a525..a5135ebe5f0 100644
--- a/drivers/pnp/pnpbios/rsparser.c
+++ b/drivers/pnp/pnpbios/rsparser.c
@@ -191,7 +191,7 @@ static unsigned char *pnpbios_parse_allocated_resource_data(struct pnp_dev *dev,
return (unsigned char *)p;
break;
- default: /* an unkown tag */
+ default: /* an unknown tag */
len_err:
dev_err(&dev->dev, "unknown tag %#x length %d\n",
tag, len);
@@ -405,7 +405,7 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end,
case SMALL_TAG_END:
return p + 2;
- default: /* an unkown tag */
+ default: /* an unknown tag */
len_err:
dev_err(&dev->dev, "unknown tag %#x length %d\n",
tag, len);
@@ -475,7 +475,7 @@ static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p,
return (unsigned char *)p;
break;
- default: /* an unkown tag */
+ default: /* an unknown tag */
len_err:
dev_err(&dev->dev, "unknown tag %#x length %d\n",
tag, len);
@@ -744,7 +744,7 @@ static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev
return (unsigned char *)p;
break;
- default: /* an unkown tag */
+ default: /* an unknown tag */
len_err:
dev_err(&dev->dev, "unknown tag %#x length %d\n",
tag, len);
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index cea6cef27e8..11867492551 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -77,6 +77,13 @@ config BATTERY_TOSA
Say Y to enable support for the battery on the Sharp Zaurus
SL-6000 (tosa) models.
+config BATTERY_COLLIE
+ tristate "Sharp SL-5500 (collie) battery"
+ depends on SA1100_COLLIE && MCP_UCB1200
+ help
+ Say Y to enable support for the battery on the Sharp Zaurus
+ SL-5500 (collie) models.
+
config BATTERY_WM97XX
bool "WM97xx generic battery driver"
depends on TOUCHSCREEN_WM97XX=y
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b96f29d91c2..356cdfd3c8b 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
+obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c
new file mode 100644
index 00000000000..039f41ae217
--- /dev/null
+++ b/drivers/power/collie_battery.c
@@ -0,0 +1,418 @@
+/*
+ * Battery and Power Management code for the Sharp SL-5x00
+ *
+ * Copyright (C) 2009 Thomas Kunze
+ *
+ * based on tosa_battery.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mfd/ucb1x00.h>
+
+#include <asm/mach/sharpsl_param.h>
+#include <asm/mach-types.h>
+#include <mach/collie.h>
+
+static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
+static struct work_struct bat_work;
+static struct ucb1x00 *ucb;
+
+struct collie_bat {
+ int status;
+ struct power_supply psy;
+ int full_chrg;
+
+ struct mutex work_lock; /* protects data */
+
+ bool (*is_present)(struct collie_bat *bat);
+ int gpio_full;
+ int gpio_charge_on;
+
+ int technology;
+
+ int gpio_bat;
+ int adc_bat;
+ int adc_bat_divider;
+ int bat_max;
+ int bat_min;
+
+ int gpio_temp;
+ int adc_temp;
+ int adc_temp_divider;
+};
+
+static struct collie_bat collie_bat_main;
+
+static unsigned long collie_read_bat(struct collie_bat *bat)
+{
+ unsigned long value = 0;
+
+ if (bat->gpio_bat < 0 || bat->adc_bat < 0)
+ return 0;
+ mutex_lock(&bat_lock);
+ gpio_set_value(bat->gpio_bat, 1);
+ msleep(5);
+ ucb1x00_adc_enable(ucb);
+ value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
+ ucb1x00_adc_disable(ucb);
+ gpio_set_value(bat->gpio_bat, 0);
+ mutex_unlock(&bat_lock);
+ value = value * 1000000 / bat->adc_bat_divider;
+
+ return value;
+}
+
+static unsigned long collie_read_temp(struct collie_bat *bat)
+{
+ unsigned long value = 0;
+ if (bat->gpio_temp < 0 || bat->adc_temp < 0)
+ return 0;
+
+ mutex_lock(&bat_lock);
+ gpio_set_value(bat->gpio_temp, 1);
+ msleep(5);
+ ucb1x00_adc_enable(ucb);
+ value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
+ ucb1x00_adc_disable(ucb);
+ gpio_set_value(bat->gpio_temp, 0);
+ mutex_unlock(&bat_lock);
+
+ value = value * 10000 / bat->adc_temp_divider;
+
+ return value;
+}
+
+static int collie_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct collie_bat *bat = container_of(psy, struct collie_bat, psy);
+
+ if (bat->is_present && !bat->is_present(bat)
+ && psp != POWER_SUPPLY_PROP_PRESENT) {
+ return -ENODEV;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = bat->status;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = bat->technology;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = collie_read_bat(bat);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ if (bat->full_chrg == -1)
+ val->intval = bat->bat_max;
+ else
+ val->intval = bat->full_chrg;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = bat->bat_max;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = bat->bat_min;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = collie_read_temp(bat);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = bat->is_present ? bat->is_present(bat) : 1;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static void collie_bat_external_power_changed(struct power_supply *psy)
+{
+ schedule_work(&bat_work);
+}
+
+static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
+{
+ pr_info("collie_bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq)));
+ schedule_work(&bat_work);
+ return IRQ_HANDLED;
+}
+
+static void collie_bat_update(struct collie_bat *bat)
+{
+ int old;
+ struct power_supply *psy = &bat->psy;
+
+ mutex_lock(&bat->work_lock);
+
+ old = bat->status;
+
+ if (bat->is_present && !bat->is_present(bat)) {
+ printk(KERN_NOTICE "%s not present\n", psy->name);
+ bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+ bat->full_chrg = -1;
+ } else if (power_supply_am_i_supplied(psy)) {
+ if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ gpio_set_value(bat->gpio_charge_on, 1);
+ mdelay(15);
+ }
+
+ if (gpio_get_value(bat->gpio_full)) {
+ if (old == POWER_SUPPLY_STATUS_CHARGING ||
+ bat->full_chrg == -1)
+ bat->full_chrg = collie_read_bat(bat);
+
+ gpio_set_value(bat->gpio_charge_on, 0);
+ bat->status = POWER_SUPPLY_STATUS_FULL;
+ } else {
+ gpio_set_value(bat->gpio_charge_on, 1);
+ bat->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+ } else {
+ gpio_set_value(bat->gpio_charge_on, 0);
+ bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+
+ if (old != bat->status)
+ power_supply_changed(psy);
+
+ mutex_unlock(&bat->work_lock);
+}
+
+static void collie_bat_work(struct work_struct *work)
+{
+ collie_bat_update(&collie_bat_main);
+}
+
+
+static enum power_supply_property collie_bat_main_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static enum power_supply_property collie_bat_bu_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_PRESENT,
+};
+
+static struct collie_bat collie_bat_main = {
+ .status = POWER_SUPPLY_STATUS_DISCHARGING,
+ .full_chrg = -1,
+ .psy = {
+ .name = "main-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = collie_bat_main_props,
+ .num_properties = ARRAY_SIZE(collie_bat_main_props),
+ .get_property = collie_bat_get_property,
+ .external_power_changed = collie_bat_external_power_changed,
+ .use_for_apm = 1,
+ },
+
+ .gpio_full = COLLIE_GPIO_CO,
+ .gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
+
+ .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
+
+ .gpio_bat = COLLIE_GPIO_MBAT_ON,
+ .adc_bat = UCB_ADC_INP_AD1,
+ .adc_bat_divider = 155,
+ .bat_max = 4310000,
+ .bat_min = 1551 * 1000000 / 414,
+
+ .gpio_temp = COLLIE_GPIO_TMP_ON,
+ .adc_temp = UCB_ADC_INP_AD0,
+ .adc_temp_divider = 10000,
+};
+
+static struct collie_bat collie_bat_bu = {
+ .status = POWER_SUPPLY_STATUS_UNKNOWN,
+ .full_chrg = -1,
+
+ .psy = {
+ .name = "backup-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = collie_bat_bu_props,
+ .num_properties = ARRAY_SIZE(collie_bat_bu_props),
+ .get_property = collie_bat_get_property,
+ .external_power_changed = collie_bat_external_power_changed,
+ },
+
+ .gpio_full = -1,
+ .gpio_charge_on = -1,
+
+ .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
+
+ .gpio_bat = COLLIE_GPIO_BBAT_ON,
+ .adc_bat = UCB_ADC_INP_AD1,
+ .adc_bat_divider = 155,
+ .bat_max = 3000000,
+ .bat_min = 1900000,
+
+ .gpio_temp = -1,
+ .adc_temp = -1,
+ .adc_temp_divider = -1,
+};
+
+static struct {
+ int gpio;
+ char *name;
+ bool output;
+ int value;
+} gpios[] = {
+ { COLLIE_GPIO_CO, "main battery full", 0, 0 },
+ { COLLIE_GPIO_MAIN_BAT_LOW, "main battery low", 0, 0 },
+ { COLLIE_GPIO_CHARGE_ON, "main charge on", 1, 0 },
+ { COLLIE_GPIO_MBAT_ON, "main battery", 1, 0 },
+ { COLLIE_GPIO_TMP_ON, "main battery temp", 1, 0 },
+ { COLLIE_GPIO_BBAT_ON, "backup battery", 1, 0 },
+};
+
+#ifdef CONFIG_PM
+static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state)
+{
+ /* flush all pending status updates */
+ flush_scheduled_work();
+ return 0;
+}
+
+static int collie_bat_resume(struct ucb1x00_dev *dev)
+{
+ /* things may have changed while we were away */
+ schedule_work(&bat_work);
+ return 0;
+}
+#else
+#define collie_bat_suspend NULL
+#define collie_bat_resume NULL
+#endif
+
+static int __devinit collie_bat_probe(struct ucb1x00_dev *dev)
+{
+ int ret;
+ int i;
+
+ if (!machine_is_collie())
+ return -ENODEV;
+
+ ucb = dev->ucb;
+
+ for (i = 0; i < ARRAY_SIZE(gpios); i++) {
+ ret = gpio_request(gpios[i].gpio, gpios[i].name);
+ if (ret) {
+ i--;
+ goto err_gpio;
+ }
+
+ if (gpios[i].output)
+ ret = gpio_direction_output(gpios[i].gpio,
+ gpios[i].value);
+ else
+ ret = gpio_direction_input(gpios[i].gpio);
+
+ if (ret)
+ goto err_gpio;
+ }
+
+ mutex_init(&collie_bat_main.work_lock);
+
+ INIT_WORK(&bat_work, collie_bat_work);
+
+ ret = power_supply_register(&dev->ucb->dev, &collie_bat_main.psy);
+ if (ret)
+ goto err_psy_reg_main;
+ ret = power_supply_register(&dev->ucb->dev, &collie_bat_bu.psy);
+ if (ret)
+ goto err_psy_reg_bu;
+
+ ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
+ collie_bat_gpio_isr,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "main full", &collie_bat_main);
+ if (!ret) {
+ schedule_work(&bat_work);
+ return 0;
+ }
+ power_supply_unregister(&collie_bat_bu.psy);
+err_psy_reg_bu:
+ power_supply_unregister(&collie_bat_main.psy);
+err_psy_reg_main:
+
+ /* see comment in collie_bat_remove */
+ flush_scheduled_work();
+
+ i--;
+err_gpio:
+ for (; i >= 0; i--)
+ gpio_free(gpios[i].gpio);
+
+ return ret;
+}
+
+static void __devexit collie_bat_remove(struct ucb1x00_dev *dev)
+{
+ int i;
+
+ free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
+
+ power_supply_unregister(&collie_bat_bu.psy);
+ power_supply_unregister(&collie_bat_main.psy);
+
+ /*
+ * now flush all pending work.
+ * we won't get any more schedules, since all
+ * sources (isr and external_power_changed)
+ * are unregistered now.
+ */
+ flush_scheduled_work();
+
+ for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--)
+ gpio_free(gpios[i].gpio);
+}
+
+static struct ucb1x00_driver collie_bat_driver = {
+ .add = collie_bat_probe,
+ .remove = __devexit_p(collie_bat_remove),
+ .suspend = collie_bat_suspend,
+ .resume = collie_bat_resume,
+};
+
+static int __init collie_bat_init(void)
+{
+ return ucb1x00_register_driver(&collie_bat_driver);
+}
+
+static void __exit collie_bat_exit(void)
+{
+ ucb1x00_unregister_driver(&collie_bat_driver);
+}
+
+module_init(collie_bat_init);
+module_exit(collie_bat_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Kunze");
+MODULE_DESCRIPTION("Collie battery driver");
diff --git a/drivers/ps3/ps3-sys-manager.c b/drivers/ps3/ps3-sys-manager.c
index 88cb7408861..3cbaf1811bd 100644
--- a/drivers/ps3/ps3-sys-manager.c
+++ b/drivers/ps3/ps3-sys-manager.c
@@ -46,7 +46,7 @@
/**
* struct ps3_sys_manager_header - System manager message header.
* @version: Header version, currently 1.
- * @size: Header size in bytes, curently 16.
+ * @size: Header size in bytes, currently 16.
* @payload_size: Message payload size in bytes.
* @service_id: Message type, one of enum ps3_sys_manager_service_id.
* @request_tag: Unique number to identify reply.
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 3c20dae43ce..f2e1004d12c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -509,6 +509,15 @@ config RTC_DRV_M48T59
This driver can also be built as a module, if so, the module
will be called "rtc-m48t59".
+config RTC_DRV_MSM6242
+ tristate "Oki MSM6242"
+ help
+ If you say yes here you get support for the Oki MSM6242
+ timekeeping chip. It is used in some Amiga models (e.g. A2000).
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-msm6242.
+
config RTC_MXC
tristate "Freescale MXC Real Time Clock"
depends on ARCH_MXC
@@ -529,6 +538,16 @@ config RTC_DRV_BQ4802
This driver can also be built as a module. If so, the module
will be called rtc-bq4802.
+config RTC_DRV_RP5C01
+ tristate "Ricoh RP5C01"
+ help
+ If you say yes here you get support for the Ricoh RP5C01
+ timekeeping chip. It is used in some Amiga models (e.g. A3000
+ and A4000).
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rp5c01.
+
config RTC_DRV_V3020
tristate "EM Microelectronic V3020"
help
@@ -780,7 +799,7 @@ config RTC_DRV_TX4939
config RTC_DRV_MV
tristate "Marvell SoC RTC"
- depends on ARCH_KIRKWOOD
+ depends on ARCH_KIRKWOOD || ARCH_DOVE
help
If you say yes here you will get support for the in-chip RTC
that can be found in some of Marvell's SoC devices, such as
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index aa3fbd5517a..af1ba7ae285 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
@@ -64,6 +65,7 @@ obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
+obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
index d490628b64d..1e73c8f42e3 100644
--- a/drivers/rtc/rtc-ds1302.c
+++ b/drivers/rtc/rtc-ds1302.c
@@ -201,7 +201,7 @@ static struct platform_driver ds1302_platform_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
- .remove = __exit_p(ds1302_rtc_remove),
+ .remove = __devexit_p(ds1302_rtc_remove),
};
static int __init ds1302_rtc_init(void)
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index 0b6b7730c71..539676e25fd 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -2,7 +2,7 @@
* An rtc driver for the Dallas DS1511
*
* Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
- * Copyright (C) 2007 Andrew Sharp <andy.sharp@onstor.com>
+ * Copyright (C) 2007 Andrew Sharp <andy.sharp@lsi.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
@@ -636,7 +636,7 @@ ds1511_rtc_exit(void)
module_init(ds1511_rtc_init);
module_exit(ds1511_rtc_exit);
-MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>");
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>");
MODULE_DESCRIPTION("Dallas DS1511 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c
new file mode 100644
index 00000000000..5f5968a4892
--- /dev/null
+++ b/drivers/rtc/rtc-msm6242.c
@@ -0,0 +1,269 @@
+/*
+ * Oki MSM6242 RTC Driver
+ *
+ * Copyright 2009 Geert Uytterhoeven
+ *
+ * Based on the A2000 TOD code in arch/m68k/amiga/config.c
+ * Copyright (C) 1993 Hamish Macdonald
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+
+enum {
+ MSM6242_SECOND1 = 0x0, /* 1-second digit register */
+ MSM6242_SECOND10 = 0x1, /* 10-second digit register */
+ MSM6242_MINUTE1 = 0x2, /* 1-minute digit register */
+ MSM6242_MINUTE10 = 0x3, /* 10-minute digit register */
+ MSM6242_HOUR1 = 0x4, /* 1-hour digit register */
+ MSM6242_HOUR10 = 0x5, /* PM/AM, 10-hour digit register */
+ MSM6242_DAY1 = 0x6, /* 1-day digit register */
+ MSM6242_DAY10 = 0x7, /* 10-day digit register */
+ MSM6242_MONTH1 = 0x8, /* 1-month digit register */
+ MSM6242_MONTH10 = 0x9, /* 10-month digit register */
+ MSM6242_YEAR1 = 0xa, /* 1-year digit register */
+ MSM6242_YEAR10 = 0xb, /* 10-year digit register */
+ MSM6242_WEEK = 0xc, /* Week register */
+ MSM6242_CD = 0xd, /* Control Register D */
+ MSM6242_CE = 0xe, /* Control Register E */
+ MSM6242_CF = 0xf, /* Control Register F */
+};
+
+#define MSM6242_HOUR10_AM (0 << 2)
+#define MSM6242_HOUR10_PM (1 << 2)
+#define MSM6242_HOUR10_HR_MASK (3 << 0)
+
+#define MSM6242_WEEK_SUNDAY 0
+#define MSM6242_WEEK_MONDAY 1
+#define MSM6242_WEEK_TUESDAY 2
+#define MSM6242_WEEK_WEDNESDAY 3
+#define MSM6242_WEEK_THURSDAY 4
+#define MSM6242_WEEK_FRIDAY 5
+#define MSM6242_WEEK_SATURDAY 6
+
+#define MSM6242_CD_30_S_ADJ (1 << 3) /* 30-second adjustment */
+#define MSM6242_CD_IRQ_FLAG (1 << 2)
+#define MSM6242_CD_BUSY (1 << 1)
+#define MSM6242_CD_HOLD (1 << 0)
+
+#define MSM6242_CE_T_MASK (3 << 2)
+#define MSM6242_CE_T_64HZ (0 << 2) /* period 1/64 second */
+#define MSM6242_CE_T_1HZ (1 << 2) /* period 1 second */
+#define MSM6242_CE_T_1MINUTE (2 << 2) /* period 1 minute */
+#define MSM6242_CE_T_1HOUR (3 << 2) /* period 1 hour */
+
+#define MSM6242_CE_ITRPT_STND (1 << 1)
+#define MSM6242_CE_MASK (1 << 0) /* STD.P output control */
+
+#define MSM6242_CF_TEST (1 << 3)
+#define MSM6242_CF_12H (0 << 2)
+#define MSM6242_CF_24H (1 << 2)
+#define MSM6242_CF_STOP (1 << 1)
+#define MSM6242_CF_REST (1 << 0) /* reset */
+
+
+struct msm6242_priv {
+ u32 __iomem *regs;
+ struct rtc_device *rtc;
+};
+
+static inline unsigned int msm6242_read(struct msm6242_priv *priv,
+ unsigned int reg)
+{
+ return __raw_readl(&priv->regs[reg]) & 0xf;
+}
+
+static inline void msm6242_write(struct msm6242_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ return __raw_writel(val, &priv->regs[reg]);
+}
+
+static inline void msm6242_set(struct msm6242_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ msm6242_write(priv, msm6242_read(priv, reg) | val, reg);
+}
+
+static inline void msm6242_clear(struct msm6242_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ msm6242_write(priv, msm6242_read(priv, reg) & ~val, reg);
+}
+
+static void msm6242_lock(struct msm6242_priv *priv)
+{
+ int cnt = 5;
+
+ msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD);
+
+ while ((msm6242_read(priv, MSM6242_CD) & MSM6242_CD_BUSY) && cnt) {
+ msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD);
+ udelay(70);
+ msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD);
+ cnt--;
+ }
+
+ if (!cnt)
+ pr_warning("msm6242: timed out waiting for RTC (0x%x)\n",
+ msm6242_read(priv, MSM6242_CD));
+}
+
+static void msm6242_unlock(struct msm6242_priv *priv)
+{
+ msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD);
+}
+
+static int msm6242_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct msm6242_priv *priv = dev_get_drvdata(dev);
+
+ msm6242_lock(priv);
+
+ tm->tm_sec = msm6242_read(priv, MSM6242_SECOND10) * 10 +
+ msm6242_read(priv, MSM6242_SECOND1);
+ tm->tm_min = msm6242_read(priv, MSM6242_MINUTE10) * 10 +
+ msm6242_read(priv, MSM6242_MINUTE1);
+ tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10 & 3)) * 10 +
+ msm6242_read(priv, MSM6242_HOUR1);
+ tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 +
+ msm6242_read(priv, MSM6242_DAY1);
+ tm->tm_wday = msm6242_read(priv, MSM6242_WEEK);
+ tm->tm_mon = msm6242_read(priv, MSM6242_MONTH10) * 10 +
+ msm6242_read(priv, MSM6242_MONTH1) - 1;
+ tm->tm_year = msm6242_read(priv, MSM6242_YEAR10) * 10 +
+ msm6242_read(priv, MSM6242_YEAR1);
+ if (tm->tm_year <= 69)
+ tm->tm_year += 100;
+
+ if (!(msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)) {
+ unsigned int pm = msm6242_read(priv, MSM6242_HOUR10) &
+ MSM6242_HOUR10_PM;
+ if (!pm && tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ else if (pm && tm->tm_hour != 12)
+ tm->tm_hour += 12;
+ }
+
+ msm6242_unlock(priv);
+
+ return rtc_valid_tm(tm);
+}
+
+static int msm6242_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct msm6242_priv *priv = dev_get_drvdata(dev);
+
+ msm6242_lock(priv);
+
+ msm6242_write(priv, tm->tm_sec / 10, MSM6242_SECOND10);
+ msm6242_write(priv, tm->tm_sec % 10, MSM6242_SECOND1);
+ msm6242_write(priv, tm->tm_min / 10, MSM6242_MINUTE10);
+ msm6242_write(priv, tm->tm_min % 10, MSM6242_MINUTE1);
+ if (msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)
+ msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
+ else if (tm->tm_hour >= 12)
+ msm6242_write(priv, MSM6242_HOUR10_PM + (tm->tm_hour - 12) / 10,
+ MSM6242_HOUR10);
+ else
+ msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
+ msm6242_write(priv, tm->tm_hour % 10, MSM6242_HOUR1);
+ msm6242_write(priv, tm->tm_mday / 10, MSM6242_DAY10);
+ msm6242_write(priv, tm->tm_mday % 10, MSM6242_DAY1);
+ if (tm->tm_wday != -1)
+ msm6242_write(priv, tm->tm_wday, MSM6242_WEEK);
+ msm6242_write(priv, (tm->tm_mon + 1) / 10, MSM6242_MONTH10);
+ msm6242_write(priv, (tm->tm_mon + 1) % 10, MSM6242_MONTH1);
+ if (tm->tm_year >= 100)
+ tm->tm_year -= 100;
+ msm6242_write(priv, tm->tm_year / 10, MSM6242_YEAR10);
+ msm6242_write(priv, tm->tm_year % 10, MSM6242_YEAR1);
+
+ msm6242_unlock(priv);
+ return 0;
+}
+
+static const struct rtc_class_ops msm6242_rtc_ops = {
+ .read_time = msm6242_read_time,
+ .set_time = msm6242_set_time,
+};
+
+static int __init msm6242_rtc_probe(struct platform_device *dev)
+{
+ struct resource *res;
+ struct msm6242_priv *priv;
+ struct rtc_device *rtc;
+ int error;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regs = ioremap(res->start, resource_size(res));
+ if (!priv->regs) {
+ error = -ENOMEM;
+ goto out_free_priv;
+ }
+
+ rtc = rtc_device_register("rtc-msm6242", &dev->dev, &msm6242_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ error = PTR_ERR(rtc);
+ goto out_unmap;
+ }
+
+ priv->rtc = rtc;
+ platform_set_drvdata(dev, priv);
+ return 0;
+
+out_unmap:
+ iounmap(priv->regs);
+out_free_priv:
+ kfree(priv);
+ return error;
+}
+
+static int __exit msm6242_rtc_remove(struct platform_device *dev)
+{
+ struct msm6242_priv *priv = platform_get_drvdata(dev);
+
+ rtc_device_unregister(priv->rtc);
+ iounmap(priv->regs);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver msm6242_rtc_driver = {
+ .driver = {
+ .name = "rtc-msm6242",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(msm6242_rtc_remove),
+};
+
+static int __init msm6242_rtc_init(void)
+{
+ return platform_driver_probe(&msm6242_rtc_driver, msm6242_rtc_probe);
+}
+
+static void __exit msm6242_rtc_fini(void)
+{
+ platform_driver_unregister(&msm6242_rtc_driver);
+}
+
+module_init(msm6242_rtc_init);
+module_exit(msm6242_rtc_fini);
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Oki MSM6242 RTC driver");
+MODULE_ALIAS("platform:rtc-msm6242");
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
new file mode 100644
index 00000000000..e1313feb060
--- /dev/null
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -0,0 +1,222 @@
+/*
+ * Ricoh RP5C01 RTC Driver
+ *
+ * Copyright 2009 Geert Uytterhoeven
+ *
+ * Based on the A3000 TOD code in arch/m68k/amiga/config.c
+ * Copyright (C) 1993 Hamish Macdonald
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+
+enum {
+ RP5C01_1_SECOND = 0x0, /* MODE 00 */
+ RP5C01_10_SECOND = 0x1, /* MODE 00 */
+ RP5C01_1_MINUTE = 0x2, /* MODE 00 and MODE 01 */
+ RP5C01_10_MINUTE = 0x3, /* MODE 00 and MODE 01 */
+ RP5C01_1_HOUR = 0x4, /* MODE 00 and MODE 01 */
+ RP5C01_10_HOUR = 0x5, /* MODE 00 and MODE 01 */
+ RP5C01_DAY_OF_WEEK = 0x6, /* MODE 00 and MODE 01 */
+ RP5C01_1_DAY = 0x7, /* MODE 00 and MODE 01 */
+ RP5C01_10_DAY = 0x8, /* MODE 00 and MODE 01 */
+ RP5C01_1_MONTH = 0x9, /* MODE 00 */
+ RP5C01_10_MONTH = 0xa, /* MODE 00 */
+ RP5C01_1_YEAR = 0xb, /* MODE 00 */
+ RP5C01_10_YEAR = 0xc, /* MODE 00 */
+
+ RP5C01_12_24_SELECT = 0xa, /* MODE 01 */
+ RP5C01_LEAP_YEAR = 0xb, /* MODE 01 */
+
+ RP5C01_MODE = 0xd, /* all modes */
+ RP5C01_TEST = 0xe, /* all modes */
+ RP5C01_RESET = 0xf, /* all modes */
+};
+
+#define RP5C01_12_24_SELECT_12 (0 << 0)
+#define RP5C01_12_24_SELECT_24 (1 << 0)
+
+#define RP5C01_10_HOUR_AM (0 << 1)
+#define RP5C01_10_HOUR_PM (1 << 1)
+
+#define RP5C01_MODE_TIMER_EN (1 << 3) /* timer enable */
+#define RP5C01_MODE_ALARM_EN (1 << 2) /* alarm enable */
+
+#define RP5C01_MODE_MODE_MASK (3 << 0)
+#define RP5C01_MODE_MODE00 (0 << 0) /* time */
+#define RP5C01_MODE_MODE01 (1 << 0) /* alarm, 12h/24h, leap year */
+#define RP5C01_MODE_RAM_BLOCK10 (2 << 0) /* RAM 4 bits x 13 */
+#define RP5C01_MODE_RAM_BLOCK11 (3 << 0) /* RAM 4 bits x 13 */
+
+#define RP5C01_RESET_1HZ_PULSE (1 << 3)
+#define RP5C01_RESET_16HZ_PULSE (1 << 2)
+#define RP5C01_RESET_SECOND (1 << 1) /* reset divider stages for */
+ /* seconds or smaller units */
+#define RP5C01_RESET_ALARM (1 << 0) /* reset all alarm registers */
+
+
+struct rp5c01_priv {
+ u32 __iomem *regs;
+ struct rtc_device *rtc;
+};
+
+static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
+ unsigned int reg)
+{
+ return __raw_readl(&priv->regs[reg]) & 0xf;
+}
+
+static inline void rp5c01_write(struct rp5c01_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ return __raw_writel(val, &priv->regs[reg]);
+}
+
+static void rp5c01_lock(struct rp5c01_priv *priv)
+{
+ rp5c01_write(priv, RP5C01_MODE_MODE00, RP5C01_MODE);
+}
+
+static void rp5c01_unlock(struct rp5c01_priv *priv)
+{
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+}
+
+static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+
+ rp5c01_lock(priv);
+
+ tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 +
+ rp5c01_read(priv, RP5C01_1_SECOND);
+ tm->tm_min = rp5c01_read(priv, RP5C01_10_MINUTE) * 10 +
+ rp5c01_read(priv, RP5C01_1_MINUTE);
+ tm->tm_hour = rp5c01_read(priv, RP5C01_10_HOUR) * 10 +
+ rp5c01_read(priv, RP5C01_1_HOUR);
+ tm->tm_mday = rp5c01_read(priv, RP5C01_10_DAY) * 10 +
+ rp5c01_read(priv, RP5C01_1_DAY);
+ tm->tm_wday = rp5c01_read(priv, RP5C01_DAY_OF_WEEK);
+ tm->tm_mon = rp5c01_read(priv, RP5C01_10_MONTH) * 10 +
+ rp5c01_read(priv, RP5C01_1_MONTH) - 1;
+ tm->tm_year = rp5c01_read(priv, RP5C01_10_YEAR) * 10 +
+ rp5c01_read(priv, RP5C01_1_YEAR);
+ if (tm->tm_year <= 69)
+ tm->tm_year += 100;
+
+ rp5c01_unlock(priv);
+
+ return rtc_valid_tm(tm);
+}
+
+static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+
+ rp5c01_lock(priv);
+
+ rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND);
+ rp5c01_write(priv, tm->tm_sec % 10, RP5C01_1_SECOND);
+ rp5c01_write(priv, tm->tm_min / 10, RP5C01_10_MINUTE);
+ rp5c01_write(priv, tm->tm_min % 10, RP5C01_1_MINUTE);
+ rp5c01_write(priv, tm->tm_hour / 10, RP5C01_10_HOUR);
+ rp5c01_write(priv, tm->tm_hour % 10, RP5C01_1_HOUR);
+ rp5c01_write(priv, tm->tm_mday / 10, RP5C01_10_DAY);
+ rp5c01_write(priv, tm->tm_mday % 10, RP5C01_1_DAY);
+ if (tm->tm_wday != -1)
+ rp5c01_write(priv, tm->tm_wday, RP5C01_DAY_OF_WEEK);
+ rp5c01_write(priv, (tm->tm_mon + 1) / 10, RP5C01_10_MONTH);
+ rp5c01_write(priv, (tm->tm_mon + 1) % 10, RP5C01_1_MONTH);
+ if (tm->tm_year >= 100)
+ tm->tm_year -= 100;
+ rp5c01_write(priv, tm->tm_year / 10, RP5C01_10_YEAR);
+ rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);
+
+ rp5c01_unlock(priv);
+ return 0;
+}
+
+static const struct rtc_class_ops rp5c01_rtc_ops = {
+ .read_time = rp5c01_read_time,
+ .set_time = rp5c01_set_time,
+};
+
+static int __init rp5c01_rtc_probe(struct platform_device *dev)
+{
+ struct resource *res;
+ struct rp5c01_priv *priv;
+ struct rtc_device *rtc;
+ int error;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regs = ioremap(res->start, resource_size(res));
+ if (!priv->regs) {
+ error = -ENOMEM;
+ goto out_free_priv;
+ }
+
+ rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ error = PTR_ERR(rtc);
+ goto out_unmap;
+ }
+
+ priv->rtc = rtc;
+ platform_set_drvdata(dev, priv);
+ return 0;
+
+out_unmap:
+ iounmap(priv->regs);
+out_free_priv:
+ kfree(priv);
+ return error;
+}
+
+static int __exit rp5c01_rtc_remove(struct platform_device *dev)
+{
+ struct rp5c01_priv *priv = platform_get_drvdata(dev);
+
+ rtc_device_unregister(priv->rtc);
+ iounmap(priv->regs);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver rp5c01_rtc_driver = {
+ .driver = {
+ .name = "rtc-rp5c01",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(rp5c01_rtc_remove),
+};
+
+static int __init rp5c01_rtc_init(void)
+{
+ return platform_driver_probe(&rp5c01_rtc_driver, rp5c01_rtc_probe);
+}
+
+static void __exit rp5c01_rtc_fini(void)
+{
+ platform_driver_unregister(&rp5c01_rtc_driver);
+}
+
+module_init(rp5c01_rtc_init);
+module_exit(rp5c01_rtc_fini);
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Ricoh RP5C01 RTC driver");
+MODULE_ALIAS("platform:rtc-rp5c01");
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index 7d1547b0070..d491eb265c3 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -286,7 +286,7 @@ static struct bin_attribute stk17ta8_nvram_attr = {
.write = stk17ta8_nvram_write,
};
-static int __init stk17ta8_rtc_probe(struct platform_device *pdev)
+static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
index 423cd5a30b1..ad741afd47d 100644
--- a/drivers/rtc/rtc-v3020.c
+++ b/drivers/rtc/rtc-v3020.c
@@ -335,7 +335,7 @@ static int rtc_probe(struct platform_device *pdev)
goto err_io;
}
- /* Make sure frequency measurment mode, test modes, and lock
+ /* Make sure frequency measurement mode, test modes, and lock
* are all disabled */
v3020_set_reg(chip, V3020_STATUS_0, 0x0);
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index aaccc8ecfa8..fdb2e7c1450 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -24,7 +24,6 @@
#include <asm/ccwdev.h>
#include <asm/ebcdic.h>
#include <asm/idals.h>
-#include <asm/todclk.h>
#include <asm/itcw.h>
/* This is ugly... */
@@ -64,6 +63,7 @@ static void do_restore_device(struct work_struct *);
static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
static void dasd_device_timeout(unsigned long);
static void dasd_block_timeout(unsigned long);
+static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *);
/*
* SECTION: Operations on the device structure.
@@ -960,7 +960,7 @@ static void dasd_device_timeout(unsigned long ptr)
device = (struct dasd_device *) ptr;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* re-activate request queue */
- device->stopped &= ~DASD_STOPPED_PENDING;
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
dasd_schedule_device_bh(device);
}
@@ -994,10 +994,9 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
return;
cqr = (struct dasd_ccw_req *) intparm;
if (cqr->status != DASD_CQR_IN_IO) {
- DBF_EVENT(DBF_DEBUG,
- "invalid status in handle_killed_request: "
- "bus_id %s, status %02x",
- dev_name(&cdev->dev), cqr->status);
+ DBF_EVENT_DEVID(DBF_DEBUG, cdev,
+ "invalid status in handle_killed_request: "
+ "%02x", cqr->status);
return;
}
@@ -1023,7 +1022,7 @@ void dasd_generic_handle_state_change(struct dasd_device *device)
/* First of all start sense subsystem status request. */
dasd_eer_snss(device);
- device->stopped &= ~DASD_STOPPED_PENDING;
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
dasd_schedule_device_bh(device);
if (device->block)
dasd_schedule_block_bh(device->block);
@@ -1045,12 +1044,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
case -EIO:
break;
case -ETIMEDOUT:
- DBF_EVENT(DBF_WARNING, "%s(%s): request timed out\n",
- __func__, dev_name(&cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
+ "request timed out\n", __func__);
break;
default:
- DBF_EVENT(DBF_WARNING, "%s(%s): unknown error %ld\n",
- __func__, dev_name(&cdev->dev), PTR_ERR(irb));
+ DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
+ "unknown error %ld\n", __func__,
+ PTR_ERR(irb));
}
dasd_handle_killed_request(cdev, intparm);
return;
@@ -1405,6 +1405,20 @@ void dasd_schedule_device_bh(struct dasd_device *device)
tasklet_hi_schedule(&device->tasklet);
}
+void dasd_device_set_stop_bits(struct dasd_device *device, int bits)
+{
+ device->stopped |= bits;
+}
+EXPORT_SYMBOL_GPL(dasd_device_set_stop_bits);
+
+void dasd_device_remove_stop_bits(struct dasd_device *device, int bits)
+{
+ device->stopped &= ~bits;
+ if (!device->stopped)
+ wake_up(&generic_waitq);
+}
+EXPORT_SYMBOL_GPL(dasd_device_remove_stop_bits);
+
/*
* Queue a request to the head of the device ccw_queue.
* Start the I/O if possible.
@@ -1465,58 +1479,135 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
}
/*
- * Queue a request to the tail of the device ccw_queue and wait for
- * it's completion.
+ * checks if error recovery is necessary, returns 1 if yes, 0 otherwise.
*/
-int dasd_sleep_on(struct dasd_ccw_req *cqr)
+static int __dasd_sleep_on_erp(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
- int rc;
+ dasd_erp_fn_t erp_fn;
+ if (cqr->status == DASD_CQR_FILLED)
+ return 0;
device = cqr->startdev;
+ if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
+ if (cqr->status == DASD_CQR_TERMINATED) {
+ device->discipline->handle_terminated_request(cqr);
+ return 1;
+ }
+ if (cqr->status == DASD_CQR_NEED_ERP) {
+ erp_fn = device->discipline->erp_action(cqr);
+ erp_fn(cqr);
+ return 1;
+ }
+ if (cqr->status == DASD_CQR_FAILED)
+ dasd_log_sense(cqr, &cqr->irb);
+ if (cqr->refers) {
+ __dasd_process_erp(device, cqr);
+ return 1;
+ }
+ }
+ return 0;
+}
- cqr->callback = dasd_wakeup_cb;
- cqr->callback_data = (void *) &generic_waitq;
- dasd_add_request_tail(cqr);
- wait_event(generic_waitq, _wait_for_wakeup(cqr));
+static int __dasd_sleep_on_loop_condition(struct dasd_ccw_req *cqr)
+{
+ if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
+ if (cqr->refers) /* erp is not done yet */
+ return 1;
+ return ((cqr->status != DASD_CQR_DONE) &&
+ (cqr->status != DASD_CQR_FAILED));
+ } else
+ return (cqr->status == DASD_CQR_FILLED);
+}
- if (cqr->status == DASD_CQR_DONE)
+static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
+{
+ struct dasd_device *device;
+ int rc;
+ struct list_head ccw_queue;
+ struct dasd_ccw_req *cqr;
+
+ INIT_LIST_HEAD(&ccw_queue);
+ maincqr->status = DASD_CQR_FILLED;
+ device = maincqr->startdev;
+ list_add(&maincqr->blocklist, &ccw_queue);
+ for (cqr = maincqr; __dasd_sleep_on_loop_condition(cqr);
+ cqr = list_first_entry(&ccw_queue,
+ struct dasd_ccw_req, blocklist)) {
+
+ if (__dasd_sleep_on_erp(cqr))
+ continue;
+ if (cqr->status != DASD_CQR_FILLED) /* could be failed */
+ continue;
+
+ /* Non-temporary stop condition will trigger fail fast */
+ if (device->stopped & ~DASD_STOPPED_PENDING &&
+ test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
+ (!dasd_eer_enabled(device))) {
+ cqr->status = DASD_CQR_FAILED;
+ continue;
+ }
+
+ /* Don't try to start requests if device is stopped */
+ if (interruptible) {
+ rc = wait_event_interruptible(
+ generic_waitq, !(device->stopped));
+ if (rc == -ERESTARTSYS) {
+ cqr->status = DASD_CQR_FAILED;
+ maincqr->intrc = rc;
+ continue;
+ }
+ } else
+ wait_event(generic_waitq, !(device->stopped));
+
+ cqr->callback = dasd_wakeup_cb;
+ cqr->callback_data = (void *) &generic_waitq;
+ dasd_add_request_tail(cqr);
+ if (interruptible) {
+ rc = wait_event_interruptible(
+ generic_waitq, _wait_for_wakeup(cqr));
+ if (rc == -ERESTARTSYS) {
+ dasd_cancel_req(cqr);
+ /* wait (non-interruptible) for final status */
+ wait_event(generic_waitq,
+ _wait_for_wakeup(cqr));
+ cqr->status = DASD_CQR_FAILED;
+ maincqr->intrc = rc;
+ continue;
+ }
+ } else
+ wait_event(generic_waitq, _wait_for_wakeup(cqr));
+ }
+
+ maincqr->endclk = get_clock();
+ if ((maincqr->status != DASD_CQR_DONE) &&
+ (maincqr->intrc != -ERESTARTSYS))
+ dasd_log_sense(maincqr, &maincqr->irb);
+ if (maincqr->status == DASD_CQR_DONE)
rc = 0;
- else if (cqr->intrc)
- rc = cqr->intrc;
+ else if (maincqr->intrc)
+ rc = maincqr->intrc;
else
rc = -EIO;
return rc;
}
/*
+ * Queue a request to the tail of the device ccw_queue and wait for
+ * it's completion.
+ */
+int dasd_sleep_on(struct dasd_ccw_req *cqr)
+{
+ return _dasd_sleep_on(cqr, 0);
+}
+
+/*
* Queue a request to the tail of the device ccw_queue and wait
* interruptible for it's completion.
*/
int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
{
- struct dasd_device *device;
- int rc;
-
- device = cqr->startdev;
- cqr->callback = dasd_wakeup_cb;
- cqr->callback_data = (void *) &generic_waitq;
- dasd_add_request_tail(cqr);
- rc = wait_event_interruptible(generic_waitq, _wait_for_wakeup(cqr));
- if (rc == -ERESTARTSYS) {
- dasd_cancel_req(cqr);
- /* wait (non-interruptible) for final status */
- wait_event(generic_waitq, _wait_for_wakeup(cqr));
- cqr->intrc = rc;
- }
-
- if (cqr->status == DASD_CQR_DONE)
- rc = 0;
- else if (cqr->intrc)
- rc = cqr->intrc;
- else
- rc = -EIO;
- return rc;
+ return _dasd_sleep_on(cqr, 1);
}
/*
@@ -1630,7 +1721,7 @@ static void dasd_block_timeout(unsigned long ptr)
block = (struct dasd_block *) ptr;
spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags);
/* re-activate request queue */
- block->base->stopped &= ~DASD_STOPPED_PENDING;
+ dasd_device_remove_stop_bits(block->base, DASD_STOPPED_PENDING);
spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags);
dasd_schedule_block_bh(block);
}
@@ -1657,11 +1748,10 @@ void dasd_block_clear_timer(struct dasd_block *block)
/*
* Process finished error recovery ccw.
*/
-static inline void __dasd_block_process_erp(struct dasd_block *block,
- struct dasd_ccw_req *cqr)
+static void __dasd_process_erp(struct dasd_device *device,
+ struct dasd_ccw_req *cqr)
{
dasd_erp_fn_t erp_fn;
- struct dasd_device *device = block->base;
if (cqr->status == DASD_CQR_DONE)
DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
@@ -1725,9 +1815,12 @@ static void __dasd_process_request_queue(struct dasd_block *block)
*/
if (!list_empty(&block->ccw_queue))
break;
- spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags);
- basedev->stopped |= DASD_STOPPED_PENDING;
- spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags);
+ spin_lock_irqsave(
+ get_ccwdev_lock(basedev->cdev), flags);
+ dasd_device_set_stop_bits(basedev,
+ DASD_STOPPED_PENDING);
+ spin_unlock_irqrestore(
+ get_ccwdev_lock(basedev->cdev), flags);
dasd_block_set_timer(block, HZ/2);
break;
}
@@ -1813,7 +1906,7 @@ restart:
cqr->status = DASD_CQR_FILLED;
cqr->retries = 255;
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
- base->stopped |= DASD_STOPPED_QUIESCE;
+ dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
flags);
goto restart;
@@ -1821,7 +1914,7 @@ restart:
/* Process finished ERP request. */
if (cqr->refers) {
- __dasd_block_process_erp(block, cqr);
+ __dasd_process_erp(base, cqr);
goto restart;
}
@@ -1952,7 +2045,7 @@ restart_cb:
/* Process finished ERP request. */
if (cqr->refers) {
spin_lock_bh(&block->queue_lock);
- __dasd_block_process_erp(block, cqr);
+ __dasd_process_erp(block->base, cqr);
spin_unlock_bh(&block->queue_lock);
/* restart list_for_xx loop since dasd_process_erp
* might remove multiple elements */
@@ -2208,18 +2301,11 @@ int dasd_generic_probe(struct ccw_device *cdev,
{
int ret;
- ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
- if (ret) {
- DBF_EVENT(DBF_WARNING,
- "dasd_generic_probe: could not set ccw-device options "
- "for %s\n", dev_name(&cdev->dev));
- return ret;
- }
ret = dasd_add_sysfs_files(cdev);
if (ret) {
- DBF_EVENT(DBF_WARNING,
- "dasd_generic_probe: could not add sysfs entries "
- "for %s\n", dev_name(&cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
+ "dasd_generic_probe: could not add "
+ "sysfs entries");
return ret;
}
cdev->handler = &dasd_int_handler;
@@ -2418,16 +2504,16 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
cqr->status = DASD_CQR_QUEUED;
cqr->retries++;
}
- device->stopped |= DASD_STOPPED_DC_WAIT;
+ dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT);
dasd_device_clear_timer(device);
dasd_schedule_device_bh(device);
ret = 1;
break;
case CIO_OPER:
/* FIXME: add a sanity check. */
- device->stopped &= ~DASD_STOPPED_DC_WAIT;
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT);
if (device->stopped & DASD_UNRESUMED_PM) {
- device->stopped &= ~DASD_UNRESUMED_PM;
+ dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM);
dasd_restore_device(device);
ret = 1;
break;
@@ -2452,7 +2538,7 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev)
if (IS_ERR(device))
return PTR_ERR(device);
/* disallow new I/O */
- device->stopped |= DASD_STOPPED_PM;
+ dasd_device_set_stop_bits(device, DASD_STOPPED_PM);
/* clear active requests */
INIT_LIST_HEAD(&freeze_queue);
spin_lock_irq(get_ccwdev_lock(cdev));
@@ -2504,14 +2590,18 @@ int dasd_generic_restore_device(struct ccw_device *cdev)
return PTR_ERR(device);
/* allow new IO again */
- device->stopped &= ~DASD_STOPPED_PM;
- device->stopped &= ~DASD_UNRESUMED_PM;
+ dasd_device_remove_stop_bits(device,
+ (DASD_STOPPED_PM | DASD_UNRESUMED_PM));
dasd_schedule_device_bh(device);
- if (device->discipline->restore)
+ /*
+ * call discipline restore function
+ * if device is stopped do nothing e.g. for disconnected devices
+ */
+ if (device->discipline->restore && !(device->stopped))
rc = device->discipline->restore(device);
- if (rc)
+ if (rc || device->stopped)
/*
* if the resume failed for the DASD we put it in
* an UNRESUMED stop state
@@ -2561,8 +2651,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
cqr->startdev = device;
cqr->memdev = device;
cqr->expires = 10*HZ;
- clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
- cqr->retries = 2;
+ cqr->retries = 256;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
return cqr;
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index e8ff7b0c961..44796ba4eb9 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -12,7 +12,6 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <asm/idals.h>
-#include <asm/todclk.h>
#define PRINTK_HEADER "dasd_erp(3990): "
@@ -70,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
* processing until the started timer has expired or an related
* interrupt was received.
*/
-static void
-dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
+static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires)
{
struct dasd_device *device = erp->startdev;
@@ -81,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
"blocking request queue for %is", expires/HZ);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped |= DASD_STOPPED_PENDING;
+ dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
erp->status = DASD_CQR_FILLED;
- dasd_block_set_timer(device->block, expires);
+ if (erp->block)
+ dasd_block_set_timer(erp->block, expires);
+ else
+ dasd_device_set_timer(device, expires);
}
/*
@@ -243,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
* DESCRIPTION
* Setup ERP to do the ERP action 1 (see Reference manual).
* Repeat the operation on a different channel path.
- * If all alternate paths have been tried, the request is posted with a
- * permanent error.
- * Note: duplex handling is not implemented (yet).
+ * As deviation from the recommended recovery action, we reset the path mask
+ * after we have tried each path and go through all paths a second time.
+ * This will cover situations where only one path at a time is actually down,
+ * but all paths fail and recover just with the same sequence and timing as
+ * we try to use them (flapping links).
+ * If all alternate paths have been tried twice, the request is posted with
+ * a permanent error.
*
* PARAMETER
* erp pointer to the current ERP
@@ -254,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
* erp pointer to the ERP
*
*/
-static struct dasd_ccw_req *
-dasd_3990_erp_action_1(struct dasd_ccw_req * erp)
+static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp)
{
+ erp->function = dasd_3990_erp_action_1_sec;
+ dasd_3990_erp_alternate_path(erp);
+ return erp;
+}
+static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
+{
erp->function = dasd_3990_erp_action_1;
-
dasd_3990_erp_alternate_path(erp);
-
+ if (erp->status == DASD_CQR_FAILED) {
+ erp->status = DASD_CQR_FILLED;
+ erp->retries = 10;
+ erp->lpm = LPM_ANYPATH;
+ erp->function = dasd_3990_erp_action_1_sec;
+ }
return erp;
-
-} /* end dasd_3990_erp_action_1 */
+} /* end dasd_3990_erp_action_1(b) */
/*
* DASD_3990_ERP_ACTION_4
@@ -2295,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
return cqr;
}
+ ccw = cqr->cpaddr;
if (cqr->cpmode == 1) {
/* make a shallow copy of the original tcw but set new tsb */
erp->cpmode = 1;
@@ -2303,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
tsb = (struct tsb *) &tcw[1];
*tcw = *((struct tcw *)cqr->cpaddr);
tcw->tsb = (long)tsb;
+ } else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) {
+ /* PSF cannot be chained from NOOP/TIC */
+ erp->cpaddr = cqr->cpaddr;
} else {
/* initialize request with default TIC to current ERP/CQR */
ccw = erp->cpaddr;
@@ -2487,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
erp = dasd_3990_erp_action_1(erp);
+ } else if (erp->function == dasd_3990_erp_action_1_sec) {
+ erp = dasd_3990_erp_action_1_sec(erp);
} else if (erp->function == dasd_3990_erp_action_5) {
/* retries have not been successful */
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 70a008c0052..fd1231738ef 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -152,6 +152,7 @@ static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work);
INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
spin_lock_init(&lcu->lock);
+ init_completion(&lcu->lcu_setup);
return lcu;
out_err4:
@@ -240,6 +241,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
}
/*
+ * The first device to be registered on an LCU will have to do
+ * some additional setup steps to configure that LCU on the
+ * storage server. All further devices should wait with their
+ * initialization until the first device is done.
+ * To synchronize this work, the first device will call
+ * dasd_alias_lcu_setup_complete when it is done, and all
+ * other devices will wait for it with dasd_alias_wait_for_lcu_setup.
+ */
+void dasd_alias_lcu_setup_complete(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ unsigned long flags;
+ struct alias_server *server;
+ struct alias_lcu *lcu;
+ struct dasd_uid *uid;
+
+ private = (struct dasd_eckd_private *) device->private;
+ uid = &private->uid;
+ lcu = NULL;
+ spin_lock_irqsave(&aliastree.lock, flags);
+ server = _find_server(uid);
+ if (server)
+ lcu = _find_lcu(server, uid);
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+ if (!lcu) {
+ DBF_EVENT_DEVID(DBF_ERR, device->cdev,
+ "could not find lcu for %04x %02x",
+ uid->ssid, uid->real_unit_addr);
+ WARN_ON(1);
+ return;
+ }
+ complete_all(&lcu->lcu_setup);
+}
+
+void dasd_alias_wait_for_lcu_setup(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ unsigned long flags;
+ struct alias_server *server;
+ struct alias_lcu *lcu;
+ struct dasd_uid *uid;
+
+ private = (struct dasd_eckd_private *) device->private;
+ uid = &private->uid;
+ lcu = NULL;
+ spin_lock_irqsave(&aliastree.lock, flags);
+ server = _find_server(uid);
+ if (server)
+ lcu = _find_lcu(server, uid);
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+ if (!lcu) {
+ DBF_EVENT_DEVID(DBF_ERR, device->cdev,
+ "could not find lcu for %04x %02x",
+ uid->ssid, uid->real_unit_addr);
+ WARN_ON(1);
+ return;
+ }
+ wait_for_completion(&lcu->lcu_setup);
+}
+
+/*
* This function removes a device from the scope of alias management.
* The complicated part is to make sure that it is not in use by
* any of the workers. If necessary cancel the work.
@@ -755,11 +817,11 @@ static void __stop_device_on_lcu(struct dasd_device *device,
{
/* If pos == device then device is already locked! */
if (pos == device) {
- pos->stopped |= DASD_STOPPED_SU;
+ dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
return;
}
spin_lock(get_ccwdev_lock(pos->cdev));
- pos->stopped |= DASD_STOPPED_SU;
+ dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
spin_unlock(get_ccwdev_lock(pos->cdev));
}
@@ -793,26 +855,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
list_for_each_entry(device, &lcu->active_devices, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped &= ~DASD_STOPPED_SU;
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped &= ~DASD_STOPPED_SU;
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
list_for_each_entry(pavgroup, &lcu->grouplist, group) {
list_for_each_entry(device, &pavgroup->baselist, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped &= ~DASD_STOPPED_SU;
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
flags);
}
list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped &= ~DASD_STOPPED_SU;
+ dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
flags);
}
@@ -836,7 +898,8 @@ static void summary_unit_check_handling_work(struct work_struct *work)
/* 2. reset summary unit check */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING);
+ dasd_device_remove_stop_bits(device,
+ (DASD_STOPPED_SU | DASD_STOPPED_PENDING));
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
reset_summary_unit_check(lcu, device, suc_data->reason);
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 4e49b4a6c88..f64d0db881b 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -24,7 +24,6 @@
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/s390_ext.h>
-#include <asm/todclk.h>
#include <asm/vtoc.h>
#include <asm/diag.h>
@@ -145,6 +144,15 @@ dasd_diag_erp(struct dasd_device *device)
mdsk_term_io(device);
rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
+ if (rc == 4) {
+ if (!(device->features & DASD_FEATURE_READONLY)) {
+ dev_warn(&device->cdev->dev,
+ "The access mode of a DIAG device changed"
+ " to read-only");
+ device->features |= DASD_FEATURE_READONLY;
+ }
+ rc = 0;
+ }
if (rc)
dev_warn(&device->cdev->dev, "DIAG ERP failed with "
"rc=%d\n", rc);
@@ -433,16 +441,20 @@ dasd_diag_check_device(struct dasd_device *device)
for (sb = 512; sb < bsize; sb = sb << 1)
block->s2b_shift++;
rc = mdsk_init_io(device, block->bp_block, 0, NULL);
- if (rc) {
+ if (rc && (rc != 4)) {
dev_warn(&device->cdev->dev, "DIAG initialization "
"failed with rc=%d\n", rc);
rc = -EIO;
} else {
+ if (rc == 4)
+ device->features |= DASD_FEATURE_READONLY;
dev_info(&device->cdev->dev,
- "New DASD with %ld byte/block, total size %ld KB\n",
+ "New DASD with %ld byte/block, total size %ld KB%s\n",
(unsigned long) block->bp_block,
(unsigned long) (block->blocks <<
- block->s2b_shift) >> 1);
+ block->s2b_shift) >> 1,
+ (rc == 4) ? ", read-only device" : "");
+ rc = 0;
}
out_label:
free_page((long) label);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 417b97cd3f9..5819dc02a14 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -24,7 +24,6 @@
#include <asm/idals.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
-#include <asm/todclk.h>
#include <asm/uaccess.h>
#include <asm/cio.h>
#include <asm/ccwdev.h>
@@ -78,6 +77,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
static struct ccw_driver dasd_eckd_driver; /* see below */
+#define INIT_CQR_OK 0
+#define INIT_CQR_UNFORMATTED 1
+#define INIT_CQR_ERROR 2
+
+
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
static int
@@ -86,11 +90,12 @@ dasd_eckd_probe (struct ccw_device *cdev)
int ret;
/* set ECKD specific ccw-device options */
- ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
+ ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE |
+ CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH);
if (ret) {
- DBF_EVENT(DBF_WARNING,
- "dasd_eckd_probe: could not set ccw-device options "
- "for %s\n", dev_name(&cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
+ "dasd_eckd_probe: could not set "
+ "ccw-device options");
return ret;
}
ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
@@ -749,8 +754,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
cqr->block = NULL;
cqr->expires = 10*HZ;
cqr->lpm = lpm;
- clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
- cqr->retries = 2;
+ cqr->retries = 256;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
return cqr;
@@ -885,16 +889,15 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
rc = dasd_eckd_read_conf_lpm(device, &conf_data,
&conf_len, lpm);
if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */
- DBF_EVENT(DBF_WARNING,
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
"Read configuration data returned "
- "error %d for device: %s", rc,
- dev_name(&device->cdev->dev));
+ "error %d", rc);
return rc;
}
if (conf_data == NULL) {
- DBF_EVENT(DBF_WARNING, "No configuration "
- "data retrieved for device: %s",
- dev_name(&device->cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+ "No configuration data "
+ "retrieved");
continue; /* no error */
}
/* save first valid configuration data */
@@ -941,16 +944,14 @@ static int dasd_eckd_read_features(struct dasd_device *device)
sizeof(struct dasd_rssd_features)),
device);
if (IS_ERR(cqr)) {
- DBF_EVENT(DBF_WARNING, "Could not allocate initialization "
- "request for device: %s",
- dev_name(&device->cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not "
+ "allocate initialization request");
return PTR_ERR(cqr);
}
cqr->startdev = device;
cqr->memdev = device;
cqr->block = NULL;
- clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
- cqr->retries = 5;
+ cqr->retries = 256;
cqr->expires = 10 * HZ;
/* Prepare for Read Subsystem Data */
@@ -1012,9 +1013,9 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
}
psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
psf_ssc_data->order = PSF_ORDER_SSC;
- psf_ssc_data->suborder = 0x40;
+ psf_ssc_data->suborder = 0xc0;
if (enable_pav) {
- psf_ssc_data->suborder |= 0x88;
+ psf_ssc_data->suborder |= 0x08;
psf_ssc_data->reserved[0] = 0x88;
}
ccw = cqr->cpaddr;
@@ -1025,6 +1026,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
cqr->startdev = device;
cqr->memdev = device;
cqr->block = NULL;
+ cqr->retries = 256;
cqr->expires = 10*HZ;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
@@ -1057,7 +1059,7 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
/*
* Valide storage server of current device.
*/
-static int dasd_eckd_validate_server(struct dasd_device *device)
+static void dasd_eckd_validate_server(struct dasd_device *device)
{
int rc;
struct dasd_eckd_private *private;
@@ -1068,15 +1070,12 @@ static int dasd_eckd_validate_server(struct dasd_device *device)
else
enable_pav = 1;
rc = dasd_eckd_psf_ssc(device, enable_pav);
+
/* may be requested feature is not available on server,
* therefore just report error and go ahead */
private = (struct dasd_eckd_private *) device->private;
- DBF_EVENT(DBF_WARNING, "PSF-SSC on storage subsystem %s.%s.%04x "
- "returned rc=%d for device: %s",
- private->uid.vendor, private->uid.serial,
- private->uid.ssid, rc, dev_name(&device->cdev->dev));
- /* RE-Read Configuration Data */
- return dasd_eckd_read_conf(device);
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
+ "returned rc=%d", private->uid.ssid, rc);
}
/*
@@ -1090,6 +1089,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
struct dasd_block *block;
int is_known, rc;
+ if (!ccw_device_is_pathgroup(device->cdev)) {
+ dev_warn(&device->cdev->dev,
+ "A channel path group could not be established\n");
+ return -EIO;
+ }
+ if (!ccw_device_is_multipath(device->cdev)) {
+ dev_info(&device->cdev->dev,
+ "The DASD is not operating in multipath mode\n");
+ }
private = (struct dasd_eckd_private *) device->private;
if (!private) {
private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
@@ -1123,9 +1131,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (private->uid.type == UA_BASE_DEVICE) {
block = dasd_alloc_block();
if (IS_ERR(block)) {
- DBF_EVENT(DBF_WARNING, "could not allocate dasd "
- "block structure for device: %s",
- dev_name(&device->cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+ "could not allocate dasd "
+ "block structure");
rc = PTR_ERR(block);
goto out_err1;
}
@@ -1139,12 +1147,21 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
rc = is_known;
goto out_err2;
}
+ /*
+ * dasd_eckd_vaildate_server is done on the first device that
+ * is found for an LCU. All later other devices have to wait
+ * for it, so they will read the correct feature codes.
+ */
if (!is_known) {
- /* new lcu found */
- rc = dasd_eckd_validate_server(device); /* will switch pav on */
- if (rc)
- goto out_err3;
- }
+ dasd_eckd_validate_server(device);
+ dasd_alias_lcu_setup_complete(device);
+ } else
+ dasd_alias_wait_for_lcu_setup(device);
+
+ /* device may report different configuration data after LCU setup */
+ rc = dasd_eckd_read_conf(device);
+ if (rc)
+ goto out_err3;
/* Read Feature Codes */
dasd_eckd_read_features(device);
@@ -1153,9 +1170,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
&private->rdc_data, 64);
if (rc) {
- DBF_EVENT(DBF_WARNING,
- "Read device characteristics failed, rc=%d for "
- "device: %s", rc, dev_name(&device->cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+ "Read device characteristic failed, rc=%d", rc);
goto out_err3;
}
/* find the vaild cylinder size */
@@ -1256,12 +1272,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
cqr->block = NULL;
cqr->startdev = device;
cqr->memdev = device;
- cqr->retries = 0;
+ cqr->retries = 255;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
return cqr;
}
+/* differentiate between 'no record found' and any other error */
+static int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
+{
+ char *sense;
+ if (init_cqr->status == DASD_CQR_DONE)
+ return INIT_CQR_OK;
+ else if (init_cqr->status == DASD_CQR_NEED_ERP ||
+ init_cqr->status == DASD_CQR_FAILED) {
+ sense = dasd_get_sense(&init_cqr->irb);
+ if (sense && (sense[1] & SNS1_NO_REC_FOUND))
+ return INIT_CQR_UNFORMATTED;
+ else
+ return INIT_CQR_ERROR;
+ } else
+ return INIT_CQR_ERROR;
+}
+
/*
* This is the callback function for the init_analysis cqr. It saves
* the status of the initial analysis ccw before it frees it and kicks
@@ -1269,21 +1302,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
* dasd_eckd_do_analysis again (if the devices has not been marked
* for deletion in the meantime).
*/
-static void
-dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
+static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
+ void *data)
{
struct dasd_eckd_private *private;
struct dasd_device *device;
device = init_cqr->startdev;
private = (struct dasd_eckd_private *) device->private;
- private->init_cqr_status = init_cqr->status;
+ private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr);
dasd_sfree_request(init_cqr, device);
dasd_kick_device(device);
}
-static int
-dasd_eckd_start_analysis(struct dasd_block *block)
+static int dasd_eckd_start_analysis(struct dasd_block *block)
{
struct dasd_eckd_private *private;
struct dasd_ccw_req *init_cqr;
@@ -1295,27 +1327,44 @@ dasd_eckd_start_analysis(struct dasd_block *block)
init_cqr->callback = dasd_eckd_analysis_callback;
init_cqr->callback_data = NULL;
init_cqr->expires = 5*HZ;
+ /* first try without ERP, so we can later handle unformatted
+ * devices as special case
+ */
+ clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
+ init_cqr->retries = 0;
dasd_add_request_head(init_cqr);
return -EAGAIN;
}
-static int
-dasd_eckd_end_analysis(struct dasd_block *block)
+static int dasd_eckd_end_analysis(struct dasd_block *block)
{
struct dasd_device *device;
struct dasd_eckd_private *private;
struct eckd_count *count_area;
unsigned int sb, blk_per_trk;
int status, i;
+ struct dasd_ccw_req *init_cqr;
device = block->base;
private = (struct dasd_eckd_private *) device->private;
status = private->init_cqr_status;
private->init_cqr_status = -1;
- if (status != DASD_CQR_DONE) {
- dev_warn(&device->cdev->dev,
- "The DASD is not formatted\n");
+ if (status == INIT_CQR_ERROR) {
+ /* try again, this time with full ERP */
+ init_cqr = dasd_eckd_analysis_ccw(device);
+ dasd_sleep_on(init_cqr);
+ status = dasd_eckd_analysis_evaluation(init_cqr);
+ dasd_sfree_request(init_cqr, device);
+ }
+
+ if (status == INIT_CQR_UNFORMATTED) {
+ dev_warn(&device->cdev->dev, "The DASD is not formatted\n");
return -EMEDIUMTYPE;
+ } else if (status == INIT_CQR_ERROR) {
+ dev_err(&device->cdev->dev,
+ "Detecting the DASD disk layout failed because "
+ "of an I/O error\n");
+ return -EIO;
}
private->uses_cdl = 1;
@@ -1607,8 +1656,7 @@ dasd_eckd_format_device(struct dasd_device * device,
}
fcp->startdev = device;
fcp->memdev = device;
- clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags);
- fcp->retries = 5; /* set retry counter to enable default ERP */
+ fcp->retries = 256;
fcp->buildclk = get_clock();
fcp->status = DASD_CQR_FILLED;
return fcp;
@@ -2690,6 +2738,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
cqr->startdev = device;
cqr->memdev = device;
cqr->retries = 0;
+ clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
cqr->expires = 10 * HZ;
/* Prepare for Read Subsystem Data */
@@ -3240,11 +3289,15 @@ int dasd_eckd_restore_device(struct dasd_device *device)
if (is_known < 0)
return is_known;
if (!is_known) {
- /* new lcu found */
- rc = dasd_eckd_validate_server(device); /* will switch pav on */
- if (rc)
- goto out_err;
- }
+ dasd_eckd_validate_server(device);
+ dasd_alias_lcu_setup_complete(device);
+ } else
+ dasd_alias_wait_for_lcu_setup(device);
+
+ /* RE-Read Configuration Data */
+ rc = dasd_eckd_read_conf(device);
+ if (rc)
+ goto out_err;
/* Read Feature Codes */
dasd_eckd_read_features(device);
@@ -3253,9 +3306,8 @@ int dasd_eckd_restore_device(struct dasd_device *device)
rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
&temp_rdc_data, 64);
if (rc) {
- DBF_EVENT(DBF_WARNING,
- "Read device characteristics failed, rc=%d for "
- "device: %s", rc, dev_name(&device->cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+ "Read device characteristic failed, rc=%d", rc);
goto out_err;
}
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index ad45bcac3ce..864d53c0420 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -414,6 +414,7 @@ struct alias_lcu {
struct summary_unit_check_work_data suc_data;
struct read_uac_work_data ruac_data;
struct dasd_ccw_req *rsu_cqr;
+ struct completion lcu_setup;
};
struct alias_pav_group {
@@ -460,5 +461,6 @@ int dasd_alias_remove_device(struct dasd_device *);
struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *);
void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
-
+void dasd_alias_lcu_setup_complete(struct dasd_device *);
+void dasd_alias_wait_for_lcu_setup(struct dasd_device *);
#endif /* DASD_ECKD_H */
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index d96039eae59..1f3e967aaba 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -536,7 +536,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);
if (!eerb)
return -ENOMEM;
- lock_kernel();
eerb->buffer_page_count = eer_pages;
if (eerb->buffer_page_count < 1 ||
eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {
@@ -544,7 +543,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
DBF_EVENT(DBF_WARNING, "can't open device since module "
"parameter eer_pages is smaller than 1 or"
" bigger than %d", (int)(INT_MAX / PAGE_SIZE));
- unlock_kernel();
return -EINVAL;
}
eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE;
@@ -552,14 +550,12 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
GFP_KERNEL);
if (!eerb->buffer) {
kfree(eerb);
- unlock_kernel();
return -ENOMEM;
}
if (dasd_eer_allocate_buffer_pages(eerb->buffer,
eerb->buffer_page_count)) {
kfree(eerb->buffer);
kfree(eerb);
- unlock_kernel();
return -ENOMEM;
}
filp->private_data = eerb;
@@ -567,7 +563,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
list_add(&eerb->list, &bufferlist);
spin_unlock_irqrestore(&bufferlock, flags);
- unlock_kernel();
return nonseekable_open(inp,filp);
}
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index f245377e8e2..0f152444ac7 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -20,7 +20,6 @@
#include <asm/idals.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
-#include <asm/todclk.h>
#include <asm/ccwdev.h>
#include "dasd_int.h"
@@ -141,9 +140,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
}
block = dasd_alloc_block();
if (IS_ERR(block)) {
- DBF_EVENT(DBF_WARNING, "could not allocate dasd block "
- "structure for device: %s",
- dev_name(&device->cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate "
+ "dasd block structure");
device->private = NULL;
kfree(private);
return PTR_ERR(block);
@@ -155,9 +153,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC,
&private->rdc_data, 32);
if (rc) {
- DBF_EVENT(DBF_WARNING, "Read device characteristics returned "
- "error %d for device: %s",
- rc, dev_name(&device->cdev->dev));
+ DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device "
+ "characteristics returned error %d", rc);
device->block = NULL;
dasd_free_block(block);
device->private = NULL;
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 8afd9fa0087..e4c2143dabf 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -108,6 +108,16 @@ do { \
d_data); \
} while(0)
+#define DBF_EVENT_DEVID(d_level, d_cdev, d_str, d_data...) \
+do { \
+ struct ccw_dev_id __dev_id; \
+ ccw_device_get_id(d_cdev, &__dev_id); \
+ debug_sprintf_event(dasd_debug_area, \
+ d_level, \
+ "0.%x.%04x " d_str "\n", \
+ __dev_id.ssid, __dev_id.devno, d_data); \
+} while (0)
+
#define DBF_EXC(d_level, d_str, d_data...)\
do { \
debug_sprintf_exception(dasd_debug_area, \
@@ -595,6 +605,9 @@ int dasd_generic_restore_device(struct ccw_device *);
int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int);
char *dasd_get_sense(struct irb *);
+void dasd_device_set_stop_bits(struct dasd_device *, int);
+void dasd_device_remove_stop_bits(struct dasd_device *, int);
+
/* externals in dasd_devmap.c */
extern int dasd_max_devindex;
extern int dasd_probeonly;
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index f756a1b0c57..478bcdb90b6 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block)
pr_info("%s: The DASD has been put in the quiesce "
"state\n", dev_name(&base->cdev->dev));
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
- base->stopped |= DASD_STOPPED_QUIESCE;
+ dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
return 0;
}
@@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block)
pr_info("%s: I/O operations have been resumed "
"on the DASD\n", dev_name(&base->cdev->dev));
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
- base->stopped &= ~DASD_STOPPED_QUIESCE;
+ dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
dasd_schedule_block_bh(block);
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 654daa3cdfd..5f23eca8280 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -215,7 +215,7 @@ dasd_statistics_read(char *page, char **start, off_t off,
}
prof = &dasd_global_profile;
- /* prevent couter 'overflow' on output */
+ /* prevent counter 'overflow' on output */
for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
factor *= 10);
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 21639d6c996..9d61683b563 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -857,7 +857,6 @@ static struct console con3215 = {
/*
* 3215 console initialization code called from console_init().
- * NOTE: This is called before kmalloc is available.
*/
static int __init con3215_init(void)
{
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index bb838bdf829..6bca81aea39 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -572,7 +572,6 @@ static struct console con3270 = {
/*
* 3270 console initialization code called from console_init().
- * NOTE: This is called before kmalloc is available.
*/
static int __init
con3270_init(void)
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index 097d3846a82..28e4649fa9e 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -38,6 +38,8 @@ struct fs3270 {
size_t rdbuf_size; /* size of data returned by RDBUF */
};
+static DEFINE_MUTEX(fs3270_mutex);
+
static void
fs3270_wake_up(struct raw3270_request *rq, void *data)
{
@@ -74,7 +76,7 @@ fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq)
}
rc = raw3270_start(view, rq);
if (rc == 0) {
- /* Started sucessfully. Now wait for completion. */
+ /* Started successfully. Now wait for completion. */
wait_event(fp->wait, raw3270_request_final(rq));
}
} while (rc == -EACCES);
@@ -328,7 +330,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (!fp)
return -ENODEV;
rc = 0;
- lock_kernel();
+ mutex_lock(&fs3270_mutex);
switch (cmd) {
case TUBICMD:
fp->read_command = arg;
@@ -354,7 +356,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
rc = -EFAULT;
break;
}
- unlock_kernel();
+ mutex_unlock(&fs3270_mutex);
return rc;
}
@@ -437,7 +439,7 @@ fs3270_open(struct inode *inode, struct file *filp)
minor = tty->index + RAW3270_FIRSTMINOR;
tty_kref_put(tty);
}
- lock_kernel();
+ mutex_lock(&fs3270_mutex);
/* Check if some other program is already using fullscreen mode. */
fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
if (!IS_ERR(fp)) {
@@ -478,7 +480,7 @@ fs3270_open(struct inode *inode, struct file *filp)
}
filp->private_data = fp;
out:
- unlock_kernel();
+ mutex_unlock(&fs3270_mutex);
return rc;
}
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 66e21dd2315..60473f86e1f 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -283,7 +282,6 @@ static int mon_open(struct inode *inode, struct file *filp)
/*
* only one user allowed
*/
- lock_kernel();
rc = -EBUSY;
if (test_and_set_bit(MON_IN_USE, &mon_in_use))
goto out;
@@ -321,7 +319,6 @@ static int mon_open(struct inode *inode, struct file *filp)
}
filp->private_data = monpriv;
dev_set_drvdata(monreader_device, monpriv);
- unlock_kernel();
return nonseekable_open(inode, filp);
out_path:
@@ -331,7 +328,6 @@ out_priv:
out_use:
clear_bit(MON_IN_USE, &mon_in_use);
out:
- unlock_kernel();
return rc;
}
@@ -607,6 +603,10 @@ static int __init mon_init(void)
}
dcss_mkname(mon_dcss_name, &user_data_connect[8]);
+ /*
+ * misc_register() has to be the last action in module_init(), because
+ * file operations will be available right after this.
+ */
rc = misc_register(&mon_dev);
if (rc < 0 )
goto out;
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index 66fb8eba93f..6532ed8b4af 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -13,7 +13,6 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
-#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
@@ -185,13 +184,11 @@ static int monwrite_open(struct inode *inode, struct file *filp)
monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
if (!monpriv)
return -ENOMEM;
- lock_kernel();
INIT_LIST_HEAD(&monpriv->list);
monpriv->hdr_to_read = sizeof(monpriv->hdr);
mutex_init(&monpriv->thread_mutex);
filp->private_data = monpriv;
list_add_tail(&monpriv->priv_list, &mon_priv_list);
- unlock_kernel();
return nonseekable_open(inode, filp);
}
@@ -364,6 +361,10 @@ static int __init mon_init(void)
goto out_driver;
}
+ /*
+ * misc_register() has to be the last action in module_init(), because
+ * file operations will be available right after this.
+ */
rc = misc_register(&mon_dev);
if (rc)
goto out_device;
diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c
index b44462a6c6d..740fe405c39 100644
--- a/drivers/s390/char/sclp_async.c
+++ b/drivers/s390/char/sclp_async.c
@@ -101,18 +101,17 @@ static struct ctl_table callhome_table[] = {
.mode = 0644,
.proc_handler = proc_handler_callhome,
},
- { .ctl_name = 0 }
+ {}
};
static struct ctl_table kern_dir_table[] = {
{
- .ctl_name = CTL_KERN,
.procname = "kernel",
.maxlen = 0,
.mode = 0555,
.child = callhome_table,
},
- { .ctl_name = 0 }
+ {}
};
/*
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 5cc11c636d3..28b5afc129c 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -84,6 +84,7 @@ static void __init sclp_read_info_early(void)
do {
memset(sccb, 0, sizeof(*sccb));
sccb->header.length = sizeof(*sccb);
+ sccb->header.function_code = 0x80;
sccb->header.control_mask[2] = 0x80;
rc = sclp_cmd_sync_early(commands[i], sccb);
} while (rc == -EBUSY);
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h
index a2633377470..7a242f07363 100644
--- a/drivers/s390/char/tape.h
+++ b/drivers/s390/char/tape.h
@@ -212,6 +212,9 @@ struct tape_device {
struct tape_class_device * nt;
struct tape_class_device * rt;
+ /* Device mutex to serialize tape commands. */
+ struct mutex mutex;
+
/* Device discipline information. */
struct tape_discipline * discipline;
void * discdata;
@@ -292,9 +295,9 @@ extern int tape_generic_pm_suspend(struct ccw_device *);
extern int tape_generic_probe(struct ccw_device *);
extern void tape_generic_remove(struct ccw_device *);
-extern struct tape_device *tape_get_device(int devindex);
-extern struct tape_device *tape_get_device_reference(struct tape_device *);
-extern struct tape_device *tape_put_device(struct tape_device *);
+extern struct tape_device *tape_find_device(int devindex);
+extern struct tape_device *tape_get_device(struct tape_device *);
+extern void tape_put_device(struct tape_device *);
/* Externals from tape_char.c */
extern int tapechar_init(void);
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 2fe45ff77b7..3657fe103c2 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -113,16 +113,16 @@ tape_34xx_work_handler(struct work_struct *work)
{
struct tape_34xx_work *p =
container_of(work, struct tape_34xx_work, work);
+ struct tape_device *device = p->device;
switch(p->op) {
case TO_MSEN:
- tape_34xx_medium_sense(p->device);
+ tape_34xx_medium_sense(device);
break;
default:
DBF_EVENT(3, "T34XX: internal error: unknown work\n");
}
-
- p->device = tape_put_device(p->device);
+ tape_put_device(device);
kfree(p);
}
@@ -136,7 +136,7 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
INIT_WORK(&p->work, tape_34xx_work_handler);
- p->device = tape_get_device_reference(device);
+ p->device = tape_get_device(device);
p->op = op;
schedule_work(&p->work);
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index e4cc3aae916..0c72aadb839 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -608,7 +608,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op)
INIT_WORK(&p->work, tape_3590_work_handler);
- p->device = tape_get_device_reference(device);
+ p->device = tape_get_device(device);
p->op = op;
schedule_work(&p->work);
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 0c0705b91c2..4799cc2f73c 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -54,7 +54,7 @@ static const struct block_device_operations tapeblock_fops = {
.owner = THIS_MODULE,
.open = tapeblock_open,
.release = tapeblock_release,
- .locked_ioctl = tapeblock_ioctl,
+ .ioctl = tapeblock_ioctl,
.media_changed = tapeblock_medium_changed,
.revalidate_disk = tapeblock_revalidate_disk,
};
@@ -239,7 +239,7 @@ tapeblock_setup_device(struct tape_device * device)
disk->major = tapeblock_major;
disk->first_minor = device->first_minor;
disk->fops = &tapeblock_fops;
- disk->private_data = tape_get_device_reference(device);
+ disk->private_data = tape_get_device(device);
disk->queue = blkdat->request_queue;
set_capacity(disk, 0);
sprintf(disk->disk_name, "btibm%d",
@@ -247,11 +247,11 @@ tapeblock_setup_device(struct tape_device * device)
blkdat->disk = disk;
blkdat->medium_changed = 1;
- blkdat->request_queue->queuedata = tape_get_device_reference(device);
+ blkdat->request_queue->queuedata = tape_get_device(device);
add_disk(disk);
- tape_get_device_reference(device);
+ tape_get_device(device);
INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
return 0;
@@ -274,13 +274,14 @@ tapeblock_cleanup_device(struct tape_device *device)
}
del_gendisk(device->blk_data.disk);
- device->blk_data.disk->private_data =
- tape_put_device(device->blk_data.disk->private_data);
+ device->blk_data.disk->private_data = NULL;
+ tape_put_device(device);
put_disk(device->blk_data.disk);
device->blk_data.disk = NULL;
cleanup_queue:
- device->blk_data.request_queue->queuedata = tape_put_device(device);
+ device->blk_data.request_queue->queuedata = NULL;
+ tape_put_device(device);
blk_cleanup_queue(device->blk_data.request_queue);
device->blk_data.request_queue = NULL;
@@ -363,7 +364,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode)
struct tape_device * device;
int rc;
- device = tape_get_device_reference(disk->private_data);
+ device = tape_get_device(disk->private_data);
if (device->required_tapemarks) {
DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c
index 31566c55adf..23d773a0d11 100644
--- a/drivers/s390/char/tape_char.c
+++ b/drivers/s390/char/tape_char.c
@@ -33,8 +33,7 @@ static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *);
static int tapechar_open(struct inode *,struct file *);
static int tapechar_release(struct inode *,struct file *);
-static int tapechar_ioctl(struct inode *, struct file *, unsigned int,
- unsigned long);
+static long tapechar_ioctl(struct file *, unsigned int, unsigned long);
static long tapechar_compat_ioctl(struct file *, unsigned int,
unsigned long);
@@ -43,7 +42,7 @@ static const struct file_operations tape_fops =
.owner = THIS_MODULE,
.read = tapechar_read,
.write = tapechar_write,
- .ioctl = tapechar_ioctl,
+ .unlocked_ioctl = tapechar_ioctl,
.compat_ioctl = tapechar_compat_ioctl,
.open = tapechar_open,
.release = tapechar_release,
@@ -170,7 +169,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
if (rc == 0) {
rc = block_size - request->rescnt;
DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc);
- filp->f_pos += rc;
/* Copy data from idal buffer to user space. */
if (idal_buffer_to_user(device->char_data.idal_buf,
data, rc) != 0)
@@ -238,7 +236,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t
break;
DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
block_size - request->rescnt);
- filp->f_pos += block_size - request->rescnt;
written += block_size - request->rescnt;
if (request->rescnt != 0)
break;
@@ -286,26 +283,20 @@ tapechar_open (struct inode *inode, struct file *filp)
if (imajor(filp->f_path.dentry->d_inode) != tapechar_major)
return -ENODEV;
- lock_kernel();
minor = iminor(filp->f_path.dentry->d_inode);
- device = tape_get_device(minor / TAPE_MINORS_PER_DEV);
+ device = tape_find_device(minor / TAPE_MINORS_PER_DEV);
if (IS_ERR(device)) {
- DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n");
- rc = PTR_ERR(device);
- goto out;
+ DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n");
+ return PTR_ERR(device);
}
-
rc = tape_open(device);
if (rc == 0) {
filp->private_data = device;
- rc = nonseekable_open(inode, filp);
- }
- else
+ nonseekable_open(inode, filp);
+ } else
tape_put_device(device);
-out:
- unlock_kernel();
return rc;
}
@@ -342,7 +333,8 @@ tapechar_release(struct inode *inode, struct file *filp)
device->char_data.idal_buf = NULL;
}
tape_release(device);
- filp->private_data = tape_put_device(device);
+ filp->private_data = NULL;
+ tape_put_device(device);
return 0;
}
@@ -351,16 +343,11 @@ tapechar_release(struct inode *inode, struct file *filp)
* Tape device io controls.
*/
static int
-tapechar_ioctl(struct inode *inp, struct file *filp,
- unsigned int no, unsigned long data)
+__tapechar_ioctl(struct tape_device *device,
+ unsigned int no, unsigned long data)
{
- struct tape_device *device;
int rc;
- DBF_EVENT(6, "TCHAR:ioct\n");
-
- device = (struct tape_device *) filp->private_data;
-
if (no == MTIOCTOP) {
struct mtop op;
@@ -453,15 +440,30 @@ tapechar_ioctl(struct inode *inp, struct file *filp,
}
static long
+tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data)
+{
+ struct tape_device *device;
+ long rc;
+
+ DBF_EVENT(6, "TCHAR:ioct\n");
+
+ device = (struct tape_device *) filp->private_data;
+ mutex_lock(&device->mutex);
+ rc = __tapechar_ioctl(device, no, data);
+ mutex_unlock(&device->mutex);
+ return rc;
+}
+
+static long
tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data)
{
struct tape_device *device = filp->private_data;
int rval = -ENOIOCTLCMD;
if (device->discipline->ioctl_fn) {
- lock_kernel();
+ mutex_lock(&device->mutex);
rval = device->discipline->ioctl_fn(device, no, data);
- unlock_kernel();
+ mutex_unlock(&device->mutex);
if (rval == -EINVAL)
rval = -ENOIOCTLCMD;
}
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index 5cd31e07164..f5d6802dc5d 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -492,6 +492,7 @@ tape_alloc_device(void)
kfree(device);
return ERR_PTR(-ENOMEM);
}
+ mutex_init(&device->mutex);
INIT_LIST_HEAD(&device->req_queue);
INIT_LIST_HEAD(&device->node);
init_waitqueue_head(&device->state_change_wq);
@@ -511,11 +512,12 @@ tape_alloc_device(void)
* increment the reference count.
*/
struct tape_device *
-tape_get_device_reference(struct tape_device *device)
+tape_get_device(struct tape_device *device)
{
- DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device,
- atomic_inc_return(&device->ref_count));
+ int count;
+ count = atomic_inc_return(&device->ref_count);
+ DBF_EVENT(4, "tape_get_device(%p) = %i\n", device, count);
return device;
}
@@ -525,32 +527,25 @@ tape_get_device_reference(struct tape_device *device)
* The function returns a NULL pointer to be used by the caller
* for clearing reference pointers.
*/
-struct tape_device *
+void
tape_put_device(struct tape_device *device)
{
- int remain;
+ int count;
- remain = atomic_dec_return(&device->ref_count);
- if (remain > 0) {
- DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain);
- } else {
- if (remain < 0) {
- DBF_EVENT(4, "put device without reference\n");
- } else {
- DBF_EVENT(4, "tape_free_device(%p)\n", device);
- kfree(device->modeset_byte);
- kfree(device);
- }
+ count = atomic_dec_return(&device->ref_count);
+ DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, count);
+ BUG_ON(count < 0);
+ if (count == 0) {
+ kfree(device->modeset_byte);
+ kfree(device);
}
-
- return NULL;
}
/*
* Find tape device by a device index.
*/
struct tape_device *
-tape_get_device(int devindex)
+tape_find_device(int devindex)
{
struct tape_device *device, *tmp;
@@ -558,7 +553,7 @@ tape_get_device(int devindex)
read_lock(&tape_device_lock);
list_for_each_entry(tmp, &tape_device_list, node) {
if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) {
- device = tape_get_device_reference(tmp);
+ device = tape_get_device(tmp);
break;
}
}
@@ -579,7 +574,8 @@ tape_generic_probe(struct ccw_device *cdev)
device = tape_alloc_device();
if (IS_ERR(device))
return -ENODEV;
- ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
+ ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP |
+ CCWDEV_DO_MULTIPATH);
ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);
if (ret) {
tape_put_device(device);
@@ -606,7 +602,8 @@ __tape_discard_requests(struct tape_device *device)
list_del(&request->list);
/* Decrease ref_count for removed request. */
- request->device = tape_put_device(device);
+ request->device = NULL;
+ tape_put_device(device);
request->rc = -EIO;
if (request->callback != NULL)
request->callback(request, request->callback_data);
@@ -664,9 +661,11 @@ tape_generic_remove(struct ccw_device *cdev)
tape_cleanup_device(device);
}
- if (!dev_get_drvdata(&cdev->dev)) {
+ device = dev_get_drvdata(&cdev->dev);
+ if (device) {
sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group);
- dev_set_drvdata(&cdev->dev, tape_put_device(dev_get_drvdata(&cdev->dev)));
+ dev_set_drvdata(&cdev->dev, NULL);
+ tape_put_device(device);
}
}
@@ -721,9 +720,8 @@ tape_free_request (struct tape_request * request)
{
DBF_LH(6, "Free request %p\n", request);
- if (request->device != NULL) {
- request->device = tape_put_device(request->device);
- }
+ if (request->device)
+ tape_put_device(request->device);
kfree(request->cpdata);
kfree(request->cpaddr);
kfree(request);
@@ -838,7 +836,8 @@ static void tape_long_busy_timeout(unsigned long data)
BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY);
DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id);
__tape_start_next_request(device);
- device->lb_timeout.data = (unsigned long) tape_put_device(device);
+ device->lb_timeout.data = 0UL;
+ tape_put_device(device);
spin_unlock_irq(get_ccwdev_lock(device->cdev));
}
@@ -918,7 +917,7 @@ __tape_start_request(struct tape_device *device, struct tape_request *request)
}
/* Increase use count of device for the added request. */
- request->device = tape_get_device_reference(device);
+ request->device = tape_get_device(device);
if (list_empty(&device->req_queue)) {
/* No other requests are on the queue. Start this one. */
@@ -1117,8 +1116,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
if (req->status == TAPE_REQUEST_LONG_BUSY) {
DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id);
if (del_timer(&device->lb_timeout)) {
- device->lb_timeout.data = (unsigned long)
- tape_put_device(device);
+ device->lb_timeout.data = 0UL;
+ tape_put_device(device);
__tape_start_next_request(device);
}
return;
@@ -1173,7 +1172,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
break;
case TAPE_IO_LONG_BUSY:
device->lb_timeout.data =
- (unsigned long)tape_get_device_reference(device);
+ (unsigned long) tape_get_device(device);
device->lb_timeout.expires = jiffies +
LONG_BUSY_TIMEOUT * HZ;
DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id);
@@ -1326,7 +1325,7 @@ EXPORT_SYMBOL(tape_generic_online);
EXPORT_SYMBOL(tape_generic_offline);
EXPORT_SYMBOL(tape_generic_pm_suspend);
EXPORT_SYMBOL(tape_put_device);
-EXPORT_SYMBOL(tape_get_device_reference);
+EXPORT_SYMBOL(tape_get_device);
EXPORT_SYMBOL(tape_state_verbose);
EXPORT_SYMBOL(tape_op_verbose);
EXPORT_SYMBOL(tape_state_set);
diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c
index 202f4213293..ebd820ccfb2 100644
--- a/drivers/s390/char/tape_proc.c
+++ b/drivers/s390/char/tape_proc.c
@@ -45,7 +45,7 @@ static int tape_proc_show(struct seq_file *m, void *v)
seq_printf(m, "TapeNo\tBusID CuType/Model\t"
"DevType/Model\tBlkSize\tState\tOp\tMedState\n");
}
- device = tape_get_device(n);
+ device = tape_find_device(n);
if (IS_ERR(device))
return 0;
spin_lock_irq(get_ccwdev_lock(device->cdev));
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 38385677c65..911822db614 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/bootmem.h>
+#include <linux/compat.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -1731,6 +1732,22 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file,
return kbd_ioctl(tp->kbd, file, cmd, arg);
}
+#ifdef CONFIG_COMPAT
+static long
+tty3270_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct tty3270 *tp;
+
+ tp = tty->driver_data;
+ if (!tp)
+ return -ENODEV;
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ return kbd_ioctl(tp->kbd, file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
static const struct tty_operations tty3270_ops = {
.open = tty3270_open,
.close = tty3270_close,
@@ -1745,6 +1762,9 @@ static const struct tty_operations tty3270_ops = {
.hangup = tty3270_hangup,
.wait_until_sent = tty3270_wait_until_sent,
.ioctl = tty3270_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = tty3270_compat_ioctl,
+#endif
.set_termios = tty3270_set_termios
};
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index d1a142fa3eb..899aa795bf3 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -312,11 +312,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
return -ENOSYS;
/* Besure this device hasn't already been opened */
- lock_kernel();
spin_lock_bh(&logptr->priv_lock);
if (logptr->dev_in_use) {
spin_unlock_bh(&logptr->priv_lock);
- unlock_kernel();
return -EBUSY;
}
logptr->dev_in_use = 1;
@@ -360,9 +358,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
|| (logptr->iucv_path_severed));
if (logptr->iucv_path_severed)
goto out_record;
- ret = nonseekable_open(inode, filp);
- unlock_kernel();
- return ret;
+ nonseekable_open(inode, filp);
+ return 0;
out_record:
if (logptr->autorecording)
@@ -372,7 +369,6 @@ out_path:
logptr->path = NULL;
out_dev:
logptr->dev_in_use = 0;
- unlock_kernel();
return -EIO;
}
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 77571b68539..cc56fc708ba 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -695,7 +695,6 @@ static int ur_open(struct inode *inode, struct file *file)
if (accmode == O_RDWR)
return -EACCES;
- lock_kernel();
/*
* We treat the minor number as the devno of the ur device
* to find in the driver tree.
@@ -749,7 +748,6 @@ static int ur_open(struct inode *inode, struct file *file)
goto fail_urfile_free;
urf->file_reclen = rc;
file->private_data = urf;
- unlock_kernel();
return 0;
fail_urfile_free:
@@ -761,7 +759,6 @@ fail_unlock:
fail_put:
urdev_put(urd);
out:
- unlock_kernel();
return rc;
}
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index f2bc287b69e..c974058e48d 100644
--- a/drivers/s390/char/vmwatchdog.c
+++ b/drivers/s390/char/vmwatchdog.c
@@ -19,7 +19,6 @@
#include <linux/moduleparam.h>
#include <linux/suspend.h>
#include <linux/watchdog.h>
-#include <linux/smp_lock.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
@@ -49,6 +48,8 @@ static unsigned int vmwdt_interval = 60;
static unsigned long vmwdt_is_open;
static int vmwdt_expect_close;
+static DEFINE_MUTEX(vmwdt_mutex);
+
#define VMWDT_OPEN 0 /* devnode is open or suspend in progress */
#define VMWDT_RUNNING 1 /* The watchdog is armed */
@@ -133,15 +134,11 @@ static int __init vmwdt_probe(void)
static int vmwdt_open(struct inode *i, struct file *f)
{
int ret;
- lock_kernel();
- if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) {
- unlock_kernel();
+ if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open))
return -EBUSY;
- }
ret = vmwdt_keepalive();
if (ret)
clear_bit(VMWDT_OPEN, &vmwdt_is_open);
- unlock_kernel();
return ret ? ret : nonseekable_open(i, f);
}
@@ -160,8 +157,7 @@ static struct watchdog_info vmwdt_info = {
.identity = "z/VM Watchdog Timer",
};
-static int vmwdt_ioctl(struct inode *i, struct file *f,
- unsigned int cmd, unsigned long arg)
+static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case WDIOC_GETSUPPORT:
@@ -205,10 +201,19 @@ static int vmwdt_ioctl(struct inode *i, struct file *f,
case WDIOC_KEEPALIVE:
return vmwdt_keepalive();
}
-
return -EINVAL;
}
+static long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ int rc;
+
+ mutex_lock(&vmwdt_mutex);
+ rc = __vmwdt_ioctl(cmd, arg);
+ mutex_unlock(&vmwdt_mutex);
+ return (long) rc;
+}
+
static ssize_t vmwdt_write(struct file *f, const char __user *buf,
size_t count, loff_t *ppos)
{
@@ -288,7 +293,7 @@ static struct notifier_block vmwdt_power_notifier = {
static const struct file_operations vmwdt_fops = {
.open = &vmwdt_open,
.release = &vmwdt_close,
- .ioctl = &vmwdt_ioctl,
+ .unlocked_ioctl = &vmwdt_ioctl,
.write = &vmwdt_write,
.owner = THIS_MODULE,
};
@@ -309,6 +314,10 @@ static int __init vmwdt_init(void)
ret = register_pm_notifier(&vmwdt_power_notifier);
if (ret)
return ret;
+ /*
+ * misc_register() has to be the last action in module_init(), because
+ * file operations will be available right after this.
+ */
ret = misc_register(&vmwdt_dev);
if (ret) {
unregister_pm_notifier(&vmwdt_power_notifier);
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index fa4c9662f65..d033414f759 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -3,7 +3,7 @@
#
obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \
- fcx.o itcw.o crw.o
+ fcx.o itcw.o crw.o ccwreq.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c
new file mode 100644
index 00000000000..9509e386093
--- /dev/null
+++ b/drivers/s390/cio/ccwreq.c
@@ -0,0 +1,328 @@
+/*
+ * Handling of internal CCW device requests.
+ *
+ * Copyright IBM Corp. 2009
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/err.h>
+#include <asm/ccwdev.h>
+#include <asm/cio.h>
+
+#include "io_sch.h"
+#include "cio.h"
+#include "device.h"
+#include "cio_debug.h"
+
+/**
+ * lpm_adjust - adjust path mask
+ * @lpm: path mask to adjust
+ * @mask: mask of available paths
+ *
+ * Shift @lpm right until @lpm and @mask have at least one bit in common or
+ * until @lpm is zero. Return the resulting lpm.
+ */
+int lpm_adjust(int lpm, int mask)
+{
+ while (lpm && ((lpm & mask) == 0))
+ lpm >>= 1;
+ return lpm;
+}
+
+/*
+ * Adjust path mask to use next path and reset retry count. Return resulting
+ * path mask.
+ */
+static u16 ccwreq_next_path(struct ccw_device *cdev)
+{
+ struct ccw_request *req = &cdev->private->req;
+
+ req->retries = req->maxretries;
+ req->mask = lpm_adjust(req->mask >>= 1, req->lpm);
+
+ return req->mask;
+}
+
+/*
+ * Clean up device state and report to callback.
+ */
+static void ccwreq_stop(struct ccw_device *cdev, int rc)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+
+ if (req->done)
+ return;
+ req->done = 1;
+ ccw_device_set_timeout(cdev, 0);
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
+ sch->lpm = sch->schib.pmcw.pam;
+ if (rc && rc != -ENODEV && req->drc)
+ rc = req->drc;
+ req->callback(cdev, req->data, rc);
+}
+
+/*
+ * (Re-)Start the operation until retries and paths are exhausted.
+ */
+static void ccwreq_do(struct ccw_device *cdev)
+{
+ struct ccw_request *req = &cdev->private->req;
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw1 *cp = req->cp;
+ int rc = -EACCES;
+
+ while (req->mask) {
+ if (req->retries-- == 0) {
+ /* Retries exhausted, try next path. */
+ ccwreq_next_path(cdev);
+ continue;
+ }
+ /* Perform start function. */
+ sch->lpm = 0xff;
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
+ rc = cio_start(sch, cp, (u8) req->mask);
+ if (rc == 0) {
+ /* I/O started successfully. */
+ ccw_device_set_timeout(cdev, req->timeout);
+ return;
+ }
+ if (rc == -ENODEV) {
+ /* Permanent device error. */
+ break;
+ }
+ if (rc == -EACCES) {
+ /* Permant path error. */
+ ccwreq_next_path(cdev);
+ continue;
+ }
+ /* Temporary improper status. */
+ rc = cio_clear(sch);
+ if (rc)
+ break;
+ return;
+ }
+ ccwreq_stop(cdev, rc);
+}
+
+/**
+ * ccw_request_start - perform I/O request
+ * @cdev: ccw device
+ *
+ * Perform the I/O request specified by cdev->req.
+ */
+void ccw_request_start(struct ccw_device *cdev)
+{
+ struct ccw_request *req = &cdev->private->req;
+
+ /* Try all paths twice to counter link flapping. */
+ req->mask = 0x8080;
+ req->retries = req->maxretries;
+ req->mask = lpm_adjust(req->mask, req->lpm);
+ req->drc = 0;
+ req->done = 0;
+ req->cancel = 0;
+ if (!req->mask)
+ goto out_nopath;
+ ccwreq_do(cdev);
+ return;
+
+out_nopath:
+ ccwreq_stop(cdev, -EACCES);
+}
+
+/**
+ * ccw_request_cancel - cancel running I/O request
+ * @cdev: ccw device
+ *
+ * Cancel the I/O request specified by cdev->req. Return non-zero if request
+ * has already finished, zero otherwise.
+ */
+int ccw_request_cancel(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+ int rc;
+
+ if (req->done)
+ return 1;
+ req->cancel = 1;
+ rc = cio_clear(sch);
+ if (rc)
+ ccwreq_stop(cdev, rc);
+ return 0;
+}
+
+/*
+ * Return the status of the internal I/O started on the specified ccw device.
+ * Perform BASIC SENSE if required.
+ */
+static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
+{
+ struct irb *irb = &cdev->private->irb;
+ struct cmd_scsw *scsw = &irb->scsw.cmd;
+
+ /* Perform BASIC SENSE if needed. */
+ if (ccw_device_accumulate_and_sense(cdev, lcirb))
+ return IO_RUNNING;
+ /* Check for halt/clear interrupt. */
+ if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+ return IO_KILLED;
+ /* Check for path error. */
+ if (scsw->cc == 3 || scsw->pno)
+ return IO_PATH_ERROR;
+ /* Handle BASIC SENSE data. */
+ if (irb->esw.esw0.erw.cons) {
+ CIO_TRACE_EVENT(2, "sensedata");
+ CIO_HEX_EVENT(2, &cdev->private->dev_id,
+ sizeof(struct ccw_dev_id));
+ CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT);
+ /* Check for command reject. */
+ if (irb->ecw[0] & SNS0_CMD_REJECT)
+ return IO_REJECTED;
+ /* Assume that unexpected SENSE data implies an error. */
+ return IO_STATUS_ERROR;
+ }
+ /* Check for channel errors. */
+ if (scsw->cstat != 0)
+ return IO_STATUS_ERROR;
+ /* Check for device errors. */
+ if (scsw->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+ return IO_STATUS_ERROR;
+ /* Check for final state. */
+ if (!(scsw->dstat & DEV_STAT_DEV_END))
+ return IO_RUNNING;
+ /* Check for other improper status. */
+ if (scsw->cc == 1 && (scsw->stctl & SCSW_STCTL_ALERT_STATUS))
+ return IO_STATUS_ERROR;
+ return IO_DONE;
+}
+
+/*
+ * Log ccw request status.
+ */
+static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status)
+{
+ struct ccw_request *req = &cdev->private->req;
+ struct {
+ struct ccw_dev_id dev_id;
+ u16 retries;
+ u8 lpm;
+ u8 status;
+ } __attribute__ ((packed)) data;
+ data.dev_id = cdev->private->dev_id;
+ data.retries = req->retries;
+ data.lpm = (u8) req->mask;
+ data.status = (u8) status;
+ CIO_TRACE_EVENT(2, "reqstat");
+ CIO_HEX_EVENT(2, &data, sizeof(data));
+}
+
+/**
+ * ccw_request_handler - interrupt handler for I/O request procedure.
+ * @cdev: ccw device
+ *
+ * Handle interrupt during I/O request procedure.
+ */
+void ccw_request_handler(struct ccw_device *cdev)
+{
+ struct ccw_request *req = &cdev->private->req;
+ struct irb *irb = (struct irb *) __LC_IRB;
+ enum io_status status;
+ int rc = -EOPNOTSUPP;
+
+ /* Check status of I/O request. */
+ status = ccwreq_status(cdev, irb);
+ if (req->filter)
+ status = req->filter(cdev, req->data, irb, status);
+ if (status != IO_RUNNING)
+ ccw_device_set_timeout(cdev, 0);
+ if (status != IO_DONE && status != IO_RUNNING)
+ ccwreq_log_status(cdev, status);
+ switch (status) {
+ case IO_DONE:
+ break;
+ case IO_RUNNING:
+ return;
+ case IO_REJECTED:
+ goto err;
+ case IO_PATH_ERROR:
+ goto out_next_path;
+ case IO_STATUS_ERROR:
+ goto out_restart;
+ case IO_KILLED:
+ /* Check if request was cancelled on purpose. */
+ if (req->cancel) {
+ rc = -EIO;
+ goto err;
+ }
+ goto out_restart;
+ }
+ /* Check back with request initiator. */
+ if (!req->check)
+ goto out;
+ switch (req->check(cdev, req->data)) {
+ case 0:
+ break;
+ case -EAGAIN:
+ goto out_restart;
+ case -EACCES:
+ goto out_next_path;
+ default:
+ goto err;
+ }
+out:
+ ccwreq_stop(cdev, 0);
+ return;
+
+out_next_path:
+ /* Try next path and restart I/O. */
+ if (!ccwreq_next_path(cdev)) {
+ rc = -EACCES;
+ goto err;
+ }
+out_restart:
+ /* Restart. */
+ ccwreq_do(cdev);
+ return;
+err:
+ ccwreq_stop(cdev, rc);
+}
+
+
+/**
+ * ccw_request_timeout - timeout handler for I/O request procedure
+ * @cdev: ccw device
+ *
+ * Handle timeout during I/O request procedure.
+ */
+void ccw_request_timeout(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+ int rc;
+
+ if (!ccwreq_next_path(cdev)) {
+ /* set the final return code for this request */
+ req->drc = -ETIME;
+ }
+ rc = cio_clear(sch);
+ if (rc)
+ goto err;
+ return;
+
+err:
+ ccwreq_stop(cdev, rc);
+}
+
+/**
+ * ccw_request_notoper - notoper handler for I/O request procedure
+ * @cdev: ccw device
+ *
+ * Handle timeout during I/O request procedure.
+ */
+void ccw_request_notoper(struct ccw_device *cdev)
+{
+ ccwreq_stop(cdev, -ENODEV);
+}
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 8ab51608da5..c268a2e5b7c 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -65,7 +65,7 @@ static void set_chp_logically_online(struct chp_id chpid, int onoff)
chpid_to_chp(chpid)->state = onoff;
}
-/* On succes return 0 if channel-path is varied offline, 1 if it is varied
+/* On success return 0 if channel-path is varied offline, 1 if it is varied
* online. Return -ENODEV if channel-path is not registered. */
int chp_get_status(struct chp_id chpid)
{
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 138124fcfca..126f240715a 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -618,6 +618,7 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
old_regs = set_irq_regs(regs);
s390_idle_check();
irq_enter();
+ __get_cpu_var(s390_idle).nohz_delay = 1;
if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
/* Serve timer interrupts first. */
clock_comparator_work();
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 2e43558c704..bf7f80f5a33 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -68,6 +68,11 @@ struct schib {
__u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4)));
+enum sch_todo {
+ SCH_TODO_NOTHING,
+ SCH_TODO_UNREG,
+};
+
/* subchannel data structure used by I/O subroutines */
struct subchannel {
struct subchannel_id schid;
@@ -95,7 +100,8 @@ struct subchannel {
struct device dev; /* entry in device tree */
struct css_driver *driver;
void *private; /* private per subchannel type data */
- struct work_struct work;
+ enum sch_todo todo;
+ struct work_struct todo_work;
struct schib_config config;
} __attribute__ ((aligned(8)));
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 30f51611130..2985eb43948 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -462,7 +462,7 @@ static struct cmb_area cmb_area = {
* block of memory, which can not be moved as long as any channel
* is active. Therefore, a maximum number of subchannels needs to
* be defined somewhere. This is a module parameter, defaulting to
- * a resonable value of 1024, or 32 kb of memory.
+ * a reasonable value of 1024, or 32 kb of memory.
* Current kernels don't allow kmalloc with more than 128kb, so the
* maximum is 4096.
*/
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 91c25706fa8..92ff88ac110 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -133,6 +133,8 @@ out:
return rc;
}
+static void css_sch_todo(struct work_struct *work);
+
static struct subchannel *
css_alloc_subchannel(struct subchannel_id schid)
{
@@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid)
kfree(sch);
return ERR_PTR(ret);
}
+ INIT_WORK(&sch->todo_work, css_sch_todo);
return sch;
}
@@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch)
}
EXPORT_SYMBOL_GPL(css_sch_device_unregister);
+static void css_sch_todo(struct work_struct *work)
+{
+ struct subchannel *sch;
+ enum sch_todo todo;
+
+ sch = container_of(work, struct subchannel, todo_work);
+ /* Find out todo. */
+ spin_lock_irq(sch->lock);
+ todo = sch->todo;
+ CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid,
+ sch->schid.sch_no, todo);
+ sch->todo = SCH_TODO_NOTHING;
+ spin_unlock_irq(sch->lock);
+ /* Perform todo. */
+ if (todo == SCH_TODO_UNREG)
+ css_sch_device_unregister(sch);
+ /* Release workqueue ref. */
+ put_device(&sch->dev);
+}
+
+/**
+ * css_sched_sch_todo - schedule a subchannel operation
+ * @sch: subchannel
+ * @todo: todo
+ *
+ * Schedule the operation identified by @todo to be performed on the slow path
+ * workqueue. Do nothing if another operation with higher priority is already
+ * scheduled. Needs to be called with subchannel lock held.
+ */
+void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
+{
+ CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n",
+ sch->schid.ssid, sch->schid.sch_no, todo);
+ if (sch->todo >= todo)
+ return;
+ /* Get workqueue ref. */
+ if (!get_device(&sch->dev))
+ return;
+ sch->todo = todo;
+ if (!queue_work(slow_path_wq, &sch->todo_work)) {
+ /* Already queued, release workqueue ref. */
+ put_device(&sch->dev);
+ }
+}
+
static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
{
int i;
@@ -376,8 +424,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
/* Unusable - ignore. */
return 0;
}
- CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, "
- "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER);
+ CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid,
+ schid.sch_no);
return css_probe_device(schid);
}
@@ -394,6 +442,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
"Got subchannel machine check but "
"no sch_event handler provided.\n");
}
+ if (ret != 0 && ret != -EAGAIN) {
+ CIO_MSG_EVENT(2, "eval: sch 0.%x.%04x, rc=%d\n",
+ sch->schid.ssid, sch->schid.sch_no, ret);
+ }
return ret;
}
@@ -684,6 +736,7 @@ static int __init setup_css(int nr)
css->pseudo_subchannel->dev.parent = &css->device;
css->pseudo_subchannel->dev.release = css_subchannel_release;
dev_set_name(&css->pseudo_subchannel->dev, "defunct");
+ mutex_init(&css->pseudo_subchannel->reg_mutex);
ret = cio_create_sch_lock(css->pseudo_subchannel);
if (ret) {
kfree(css->pseudo_subchannel);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 68d6b0bf151..fe84b92cde6 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -11,6 +11,8 @@
#include <asm/chpid.h>
#include <asm/schid.h>
+#include "cio.h"
+
/*
* path grouping stuff
*/
@@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq;
void css_wait_for_slow_path(void);
+void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo);
#endif
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 2490b741e16..9fecfb4223a 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -7,6 +7,10 @@
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
+
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
@@ -299,53 +303,18 @@ int ccw_device_is_orphan(struct ccw_device *cdev)
static void ccw_device_unregister(struct ccw_device *cdev)
{
- if (test_and_clear_bit(1, &cdev->private->registered)) {
+ if (device_is_registered(&cdev->dev)) {
+ /* Undo device_add(). */
device_del(&cdev->dev);
+ }
+ if (cdev->private->flags.initialized) {
+ cdev->private->flags.initialized = 0;
/* Release reference from device_initialize(). */
put_device(&cdev->dev);
}
}
-static void ccw_device_remove_orphan_cb(struct work_struct *work)
-{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
-
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
- ccw_device_unregister(cdev);
- /* Release cdev reference for workqueue processing. */
- put_device(&cdev->dev);
-}
-
-static void
-ccw_device_remove_disconnected(struct ccw_device *cdev)
-{
- unsigned long flags;
-
- /*
- * Forced offline in disconnected state means
- * 'throw away device'.
- */
- if (ccw_device_is_orphan(cdev)) {
- /*
- * Deregister ccw device.
- * Unfortunately, we cannot do this directly from the
- * attribute method.
- */
- /* Get cdev reference for workqueue processing. */
- if (!get_device(&cdev->dev))
- return;
- spin_lock_irqsave(cdev->ccwlock, flags);
- cdev->private->state = DEV_STATE_NOT_OPER;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_remove_orphan_cb);
- queue_work(slow_path_wq, &cdev->private->kick_work);
- } else
- /* Deregister subchannel, which will kill the ccw device. */
- ccw_device_schedule_sch_unregister(cdev);
-}
+static void io_subchannel_quiesce(struct subchannel *);
/**
* ccw_device_set_offline() - disable a ccw device for I/O
@@ -360,7 +329,8 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
*/
int ccw_device_set_offline(struct ccw_device *cdev)
{
- int ret;
+ struct subchannel *sch;
+ int ret, state;
if (!cdev)
return -ENODEV;
@@ -374,6 +344,7 @@ int ccw_device_set_offline(struct ccw_device *cdev)
}
cdev->online = 0;
spin_lock_irq(cdev->ccwlock);
+ sch = to_subchannel(cdev->dev.parent);
/* Wait until a final state or DISCONNECTED is reached */
while (!dev_fsm_final_state(cdev) &&
cdev->private->state != DEV_STATE_DISCONNECTED) {
@@ -382,20 +353,37 @@ int ccw_device_set_offline(struct ccw_device *cdev)
cdev->private->state == DEV_STATE_DISCONNECTED));
spin_lock_irq(cdev->ccwlock);
}
- ret = ccw_device_offline(cdev);
- if (ret)
- goto error;
+ do {
+ ret = ccw_device_offline(cdev);
+ if (!ret)
+ break;
+ CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device "
+ "0.%x.%04x\n", ret, cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno);
+ if (ret != -EBUSY)
+ goto error;
+ state = cdev->private->state;
+ spin_unlock_irq(cdev->ccwlock);
+ io_subchannel_quiesce(sch);
+ spin_lock_irq(cdev->ccwlock);
+ cdev->private->state = state;
+ } while (ret == -EBUSY);
spin_unlock_irq(cdev->ccwlock);
wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
cdev->private->state == DEV_STATE_DISCONNECTED));
+ /* Inform the user if set offline failed. */
+ if (cdev->private->state == DEV_STATE_BOXED) {
+ pr_warning("%s: The device entered boxed state while "
+ "being set offline\n", dev_name(&cdev->dev));
+ } else if (cdev->private->state == DEV_STATE_NOT_OPER) {
+ pr_warning("%s: The device stopped operating while "
+ "being set offline\n", dev_name(&cdev->dev));
+ }
/* Give up reference from ccw_device_set_online(). */
put_device(&cdev->dev);
return 0;
error:
- CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n",
- ret, cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
cdev->private->state = DEV_STATE_OFFLINE;
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
spin_unlock_irq(cdev->ccwlock);
@@ -448,6 +436,16 @@ int ccw_device_set_online(struct ccw_device *cdev)
if ((cdev->private->state != DEV_STATE_ONLINE) &&
(cdev->private->state != DEV_STATE_W4SENSE)) {
spin_unlock_irq(cdev->ccwlock);
+ /* Inform the user that set online failed. */
+ if (cdev->private->state == DEV_STATE_BOXED) {
+ pr_warning("%s: Setting the device online failed "
+ "because it is boxed\n",
+ dev_name(&cdev->dev));
+ } else if (cdev->private->state == DEV_STATE_NOT_OPER) {
+ pr_warning("%s: Setting the device online failed "
+ "because it is not operational\n",
+ dev_name(&cdev->dev));
+ }
/* Give up online reference since onlining failed. */
put_device(&cdev->dev);
return -ENODEV;
@@ -494,27 +492,22 @@ error:
static int online_store_handle_offline(struct ccw_device *cdev)
{
- if (cdev->private->state == DEV_STATE_DISCONNECTED)
- ccw_device_remove_disconnected(cdev);
- else if (cdev->online && cdev->drv && cdev->drv->set_offline)
+ if (cdev->private->state == DEV_STATE_DISCONNECTED) {
+ spin_lock_irq(cdev->ccwlock);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
+ spin_unlock_irq(cdev->ccwlock);
+ } else if (cdev->online && cdev->drv && cdev->drv->set_offline)
return ccw_device_set_offline(cdev);
return 0;
}
static int online_store_recog_and_online(struct ccw_device *cdev)
{
- int ret;
-
/* Do device recognition, if needed. */
if (cdev->private->state == DEV_STATE_BOXED) {
- ret = ccw_device_recognition(cdev);
- if (ret) {
- CIO_MSG_EVENT(0, "Couldn't start recognition "
- "for device 0.%x.%04x (ret=%d)\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno, ret);
- return ret;
- }
+ spin_lock_irq(cdev->ccwlock);
+ ccw_device_recognition(cdev);
+ spin_unlock_irq(cdev->ccwlock);
wait_event(cdev->private->wait_q,
cdev->private->flags.recog_done);
if (cdev->private->state != DEV_STATE_OFFLINE)
@@ -553,11 +546,10 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr,
int force, ret;
unsigned long i;
- if ((cdev->private->state != DEV_STATE_OFFLINE &&
- cdev->private->state != DEV_STATE_ONLINE &&
- cdev->private->state != DEV_STATE_BOXED &&
- cdev->private->state != DEV_STATE_DISCONNECTED) ||
- atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
+ if (!dev_fsm_final_state(cdev) &&
+ cdev->private->state != DEV_STATE_DISCONNECTED)
+ return -EAGAIN;
+ if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
return -EAGAIN;
if (cdev->drv && !try_module_get(cdev->drv->owner)) {
@@ -665,81 +657,31 @@ static int ccw_device_register(struct ccw_device *cdev)
cdev->private->dev_id.devno);
if (ret)
return ret;
- ret = device_add(dev);
- if (ret)
- return ret;
-
- set_bit(1, &cdev->private->registered);
- return ret;
+ return device_add(dev);
}
-struct match_data {
- struct ccw_dev_id dev_id;
- struct ccw_device * sibling;
-};
-
-static int
-match_devno(struct device * dev, void * data)
-{
- struct match_data * d = data;
- struct ccw_device * cdev;
-
- cdev = to_ccwdev(dev);
- if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
- !ccw_device_is_orphan(cdev) &&
- ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) &&
- (cdev != d->sibling))
- return 1;
- return 0;
-}
-
-static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id,
- struct ccw_device *sibling)
-{
- struct device *dev;
- struct match_data data;
-
- data.dev_id = *dev_id;
- data.sibling = sibling;
- dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);
-
- return dev ? to_ccwdev(dev) : NULL;
-}
-
-static int match_orphan(struct device *dev, void *data)
+static int match_dev_id(struct device *dev, void *data)
{
- struct ccw_dev_id *dev_id;
- struct ccw_device *cdev;
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct ccw_dev_id *dev_id = data;
- dev_id = data;
- cdev = to_ccwdev(dev);
return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);
}
-static struct ccw_device *
-get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css,
- struct ccw_dev_id *dev_id)
+static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id)
{
struct device *dev;
- dev = device_find_child(&css->pseudo_subchannel->dev, dev_id,
- match_orphan);
+ dev = bus_find_device(&ccw_bus_type, NULL, dev_id, match_dev_id);
return dev ? to_ccwdev(dev) : NULL;
}
-void ccw_device_do_unbind_bind(struct work_struct *work)
+static void ccw_device_do_unbind_bind(struct ccw_device *cdev)
{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
- struct subchannel *sch;
int ret;
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
- sch = to_subchannel(cdev->dev.parent);
-
- if (test_bit(1, &cdev->private->registered)) {
+ if (device_is_registered(&cdev->dev)) {
device_release_driver(&cdev->dev);
ret = device_attach(&cdev->dev);
WARN_ON(ret == -ENODEV);
@@ -773,6 +715,8 @@ static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
return ERR_PTR(-ENOMEM);
}
+static void ccw_device_todo(struct work_struct *work);
+
static int io_subchannel_initialize_dev(struct subchannel *sch,
struct ccw_device *cdev)
{
@@ -780,7 +724,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
atomic_set(&cdev->private->onoff, 0);
cdev->dev.parent = &sch->dev;
cdev->dev.release = ccw_device_release;
- INIT_WORK(&cdev->private->kick_work, NULL);
+ INIT_WORK(&cdev->private->todo_work, ccw_device_todo);
cdev->dev.groups = ccwdev_attr_groups;
/* Do first half of device_register. */
device_initialize(&cdev->dev);
@@ -789,6 +733,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
put_device(&cdev->dev);
return -ENODEV;
}
+ cdev->private->flags.initialized = 1;
return 0;
}
@@ -806,76 +751,7 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
return cdev;
}
-static int io_subchannel_recog(struct ccw_device *, struct subchannel *);
-
-static void sch_attach_device(struct subchannel *sch,
- struct ccw_device *cdev)
-{
- css_update_ssd_info(sch);
- spin_lock_irq(sch->lock);
- sch_set_cdev(sch, cdev);
- cdev->private->schid = sch->schid;
- cdev->ccwlock = sch->lock;
- ccw_device_trigger_reprobe(cdev);
- spin_unlock_irq(sch->lock);
-}
-
-static void sch_attach_disconnected_device(struct subchannel *sch,
- struct ccw_device *cdev)
-{
- struct subchannel *other_sch;
- int ret;
-
- /* Get reference for new parent. */
- if (!get_device(&sch->dev))
- return;
- other_sch = to_subchannel(cdev->dev.parent);
- /* Note: device_move() changes cdev->dev.parent */
- ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
- if (ret) {
- CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed "
- "(ret=%d)!\n", cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno, ret);
- /* Put reference for new parent. */
- put_device(&sch->dev);
- return;
- }
- sch_set_cdev(other_sch, NULL);
- /* No need to keep a subchannel without ccw device around. */
- css_sch_device_unregister(other_sch);
- sch_attach_device(sch, cdev);
- /* Put reference for old parent. */
- put_device(&other_sch->dev);
-}
-
-static void sch_attach_orphaned_device(struct subchannel *sch,
- struct ccw_device *cdev)
-{
- int ret;
- struct subchannel *pseudo_sch;
-
- /* Get reference for new parent. */
- if (!get_device(&sch->dev))
- return;
- pseudo_sch = to_subchannel(cdev->dev.parent);
- /*
- * Try to move the ccw device to its new subchannel.
- * Note: device_move() changes cdev->dev.parent
- */
- ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
- if (ret) {
- CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
- "failed (ret=%d)!\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno, ret);
- /* Put reference for new parent. */
- put_device(&sch->dev);
- return;
- }
- sch_attach_device(sch, cdev);
- /* Put reference on pseudo subchannel. */
- put_device(&pseudo_sch->dev);
-}
+static void io_subchannel_recog(struct ccw_device *, struct subchannel *);
static void sch_create_and_recog_new_device(struct subchannel *sch)
{
@@ -888,100 +764,19 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
css_sch_device_unregister(sch);
return;
}
- spin_lock_irq(sch->lock);
- sch_set_cdev(sch, cdev);
- spin_unlock_irq(sch->lock);
/* Start recognition for the new ccw device. */
- if (io_subchannel_recog(cdev, sch)) {
- spin_lock_irq(sch->lock);
- sch_set_cdev(sch, NULL);
- spin_unlock_irq(sch->lock);
- css_sch_device_unregister(sch);
- /* Put reference from io_subchannel_create_ccwdev(). */
- put_device(&sch->dev);
- /* Give up initial reference. */
- put_device(&cdev->dev);
- }
-}
-
-
-void ccw_device_move_to_orphanage(struct work_struct *work)
-{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
- struct ccw_device *replacing_cdev;
- struct subchannel *sch;
- int ret;
- struct channel_subsystem *css;
- struct ccw_dev_id dev_id;
-
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
- sch = to_subchannel(cdev->dev.parent);
- css = to_css(sch->dev.parent);
- dev_id.devno = sch->schib.pmcw.dev;
- dev_id.ssid = sch->schid.ssid;
-
- /* Increase refcount for pseudo subchannel. */
- get_device(&css->pseudo_subchannel->dev);
- /*
- * Move the orphaned ccw device to the orphanage so the replacing
- * ccw device can take its place on the subchannel.
- * Note: device_move() changes cdev->dev.parent
- */
- ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev,
- DPM_ORDER_NONE);
- if (ret) {
- CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
- "(ret=%d)!\n", cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno, ret);
- /* Decrease refcount for pseudo subchannel again. */
- put_device(&css->pseudo_subchannel->dev);
- return;
- }
- cdev->ccwlock = css->pseudo_subchannel->lock;
- /*
- * Search for the replacing ccw device
- * - among the disconnected devices
- * - in the orphanage
- */
- replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
- if (replacing_cdev) {
- sch_attach_disconnected_device(sch, replacing_cdev);
- /* Release reference from get_disc_ccwdev_by_dev_id() */
- put_device(&replacing_cdev->dev);
- /* Release reference of subchannel from old cdev. */
- put_device(&sch->dev);
- return;
- }
- replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
- if (replacing_cdev) {
- sch_attach_orphaned_device(sch, replacing_cdev);
- /* Release reference from get_orphaned_ccwdev_by_dev_id() */
- put_device(&replacing_cdev->dev);
- /* Release reference of subchannel from old cdev. */
- put_device(&sch->dev);
- return;
- }
- sch_create_and_recog_new_device(sch);
- /* Release reference of subchannel from old cdev. */
- put_device(&sch->dev);
+ io_subchannel_recog(cdev, sch);
}
/*
* Register recognized device.
*/
-static void
-io_subchannel_register(struct work_struct *work)
+static void io_subchannel_register(struct ccw_device *cdev)
{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
struct subchannel *sch;
int ret;
unsigned long flags;
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
/*
* Check if subchannel is still registered. It may have become
@@ -1033,41 +828,23 @@ out:
cdev->private->flags.recog_done = 1;
wake_up(&cdev->private->wait_q);
out_err:
- /* Release reference for workqueue processing. */
- put_device(&cdev->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
-static void ccw_device_call_sch_unregister(struct work_struct *work)
+static void ccw_device_call_sch_unregister(struct ccw_device *cdev)
{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
struct subchannel *sch;
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
/* Get subchannel reference for local processing. */
if (!get_device(cdev->dev.parent))
return;
sch = to_subchannel(cdev->dev.parent);
css_sch_device_unregister(sch);
- /* Release cdev reference for workqueue processing.*/
- put_device(&cdev->dev);
/* Release subchannel reference for local processing. */
put_device(&sch->dev);
}
-void ccw_device_schedule_sch_unregister(struct ccw_device *cdev)
-{
- /* Get cdev reference for workqueue processing. */
- if (!get_device(&cdev->dev))
- return;
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_call_sch_unregister);
- queue_work(slow_path_wq, &cdev->private->kick_work);
-}
-
/*
* subchannel recognition done. Called from the state machine.
*/
@@ -1083,7 +860,8 @@ io_subchannel_recog_done(struct ccw_device *cdev)
/* Device did not respond in time. */
case DEV_STATE_NOT_OPER:
cdev->private->flags.recog_done = 1;
- ccw_device_schedule_sch_unregister(cdev);
+ /* Remove device found not operational. */
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
break;
@@ -1092,22 +870,15 @@ io_subchannel_recog_done(struct ccw_device *cdev)
* We can't register the device in interrupt context so
* we schedule a work item.
*/
- if (!get_device(&cdev->dev))
- break;
- PREPARE_WORK(&cdev->private->kick_work,
- io_subchannel_register);
- queue_work(slow_path_wq, &cdev->private->kick_work);
+ ccw_device_sched_todo(cdev, CDEV_TODO_REGISTER);
break;
}
}
-static int
-io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
+static void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
{
- int rc;
struct ccw_device_private *priv;
- sch_set_cdev(sch, cdev);
cdev->ccwlock = sch->lock;
/* Init private data. */
@@ -1125,62 +896,81 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
/* Start async. device sensing. */
spin_lock_irq(sch->lock);
- rc = ccw_device_recognition(cdev);
+ sch_set_cdev(sch, cdev);
+ ccw_device_recognition(cdev);
spin_unlock_irq(sch->lock);
- if (rc) {
- if (atomic_dec_and_test(&ccw_device_init_count))
- wake_up(&ccw_device_init_wq);
- }
- return rc;
}
-static void ccw_device_move_to_sch(struct work_struct *work)
+static int ccw_device_move_to_sch(struct ccw_device *cdev,
+ struct subchannel *sch)
{
- struct ccw_device_private *priv;
- int rc;
- struct subchannel *sch;
- struct ccw_device *cdev;
- struct subchannel *former_parent;
+ struct subchannel *old_sch;
+ int rc, old_enabled = 0;
- priv = container_of(work, struct ccw_device_private, kick_work);
- sch = priv->sch;
- cdev = priv->cdev;
- former_parent = to_subchannel(cdev->dev.parent);
- /* Get reference for new parent. */
+ old_sch = to_subchannel(cdev->dev.parent);
+ /* Obtain child reference for new parent. */
if (!get_device(&sch->dev))
- return;
+ return -ENODEV;
+
+ if (!sch_is_pseudo_sch(old_sch)) {
+ spin_lock_irq(old_sch->lock);
+ old_enabled = old_sch->schib.pmcw.ena;
+ rc = 0;
+ if (old_enabled)
+ rc = cio_disable_subchannel(old_sch);
+ spin_unlock_irq(old_sch->lock);
+ if (rc == -EBUSY) {
+ /* Release child reference for new parent. */
+ put_device(&sch->dev);
+ return rc;
+ }
+ }
+
mutex_lock(&sch->reg_mutex);
- /*
- * Try to move the ccw device to its new subchannel.
- * Note: device_move() changes cdev->dev.parent
- */
rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
mutex_unlock(&sch->reg_mutex);
if (rc) {
- CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel "
- "0.%x.%04x failed (ret=%d)!\n",
+ CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no, rc);
- css_sch_device_unregister(sch);
- /* Put reference for new parent again. */
+ sch->schib.pmcw.dev, rc);
+ if (old_enabled) {
+ /* Try to reenable the old subchannel. */
+ spin_lock_irq(old_sch->lock);
+ cio_enable_subchannel(old_sch, (u32)(addr_t)old_sch);
+ spin_unlock_irq(old_sch->lock);
+ }
+ /* Release child reference for new parent. */
put_device(&sch->dev);
- goto out;
+ return rc;
}
- if (!sch_is_pseudo_sch(former_parent)) {
- spin_lock_irq(former_parent->lock);
- sch_set_cdev(former_parent, NULL);
- spin_unlock_irq(former_parent->lock);
- css_sch_device_unregister(former_parent);
- /* Reset intparm to zeroes. */
- former_parent->config.intparm = 0;
- cio_commit_config(former_parent);
+ /* Clean up old subchannel. */
+ if (!sch_is_pseudo_sch(old_sch)) {
+ spin_lock_irq(old_sch->lock);
+ sch_set_cdev(old_sch, NULL);
+ spin_unlock_irq(old_sch->lock);
+ css_schedule_eval(old_sch->schid);
}
- sch_attach_device(sch, cdev);
-out:
- /* Put reference for old parent. */
- put_device(&former_parent->dev);
- put_device(&cdev->dev);
+ /* Release child reference for old parent. */
+ put_device(&old_sch->dev);
+ /* Initialize new subchannel. */
+ spin_lock_irq(sch->lock);
+ cdev->private->schid = sch->schid;
+ cdev->ccwlock = sch->lock;
+ if (!sch_is_pseudo_sch(sch))
+ sch_set_cdev(sch, cdev);
+ spin_unlock_irq(sch->lock);
+ if (!sch_is_pseudo_sch(sch))
+ css_update_ssd_info(sch);
+ return 0;
+}
+
+static int ccw_device_move_to_orph(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct channel_subsystem *css = to_css(sch->dev.parent);
+
+ return ccw_device_move_to_sch(cdev, css->pseudo_subchannel);
}
static void io_subchannel_irq(struct subchannel *sch)
@@ -1199,9 +989,6 @@ void io_subchannel_init_config(struct subchannel *sch)
{
memset(&sch->config, 0, sizeof(sch->config));
sch->config.csense = 1;
- /* Use subchannel mp mode when there is more than 1 installed CHPID. */
- if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0)
- sch->config.mp = 1;
}
static void io_subchannel_init_fields(struct subchannel *sch)
@@ -1222,23 +1009,6 @@ static void io_subchannel_init_fields(struct subchannel *sch)
io_subchannel_init_config(sch);
}
-static void io_subchannel_do_unreg(struct work_struct *work)
-{
- struct subchannel *sch;
-
- sch = container_of(work, struct subchannel, work);
- css_sch_device_unregister(sch);
- put_device(&sch->dev);
-}
-
-/* Schedule unregister if we have no cdev. */
-static void io_subchannel_schedule_removal(struct subchannel *sch)
-{
- get_device(&sch->dev);
- INIT_WORK(&sch->work, io_subchannel_do_unreg);
- queue_work(slow_path_wq, &sch->work);
-}
-
/*
* Note: We always return 0 so that we bind to the device even on error.
* This is needed so that our remove function is called on unregister.
@@ -1247,8 +1017,6 @@ static int io_subchannel_probe(struct subchannel *sch)
{
struct ccw_device *cdev;
int rc;
- unsigned long flags;
- struct ccw_dev_id dev_id;
if (cio_is_console(sch->schid)) {
rc = sysfs_create_group(&sch->dev.kobj,
@@ -1268,6 +1036,7 @@ static int io_subchannel_probe(struct subchannel *sch)
cdev = sch_get_cdev(sch);
cdev->dev.groups = ccwdev_attr_groups;
device_initialize(&cdev->dev);
+ cdev->private->flags.initialized = 1;
ccw_device_register(cdev);
/*
* Check if the device is already online. If it is
@@ -1292,44 +1061,14 @@ static int io_subchannel_probe(struct subchannel *sch)
sch->private = kzalloc(sizeof(struct io_subchannel_private),
GFP_KERNEL | GFP_DMA);
if (!sch->private)
- goto out_err;
- /*
- * First check if a fitting device may be found amongst the
- * disconnected devices or in the orphanage.
- */
- dev_id.devno = sch->schib.pmcw.dev;
- dev_id.ssid = sch->schid.ssid;
- cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
- if (!cdev)
- cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
- &dev_id);
- if (cdev) {
- /*
- * Schedule moving the device until when we have a registered
- * subchannel to move to and succeed the probe. We can
- * unregister later again, when the probe is through.
- */
- cdev->private->sch = sch;
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_move_to_sch);
- queue_work(slow_path_wq, &cdev->private->kick_work);
- return 0;
- }
- cdev = io_subchannel_create_ccwdev(sch);
- if (IS_ERR(cdev))
- goto out_err;
- rc = io_subchannel_recog(cdev, sch);
- if (rc) {
- spin_lock_irqsave(sch->lock, flags);
- io_subchannel_recog_done(cdev);
- spin_unlock_irqrestore(sch->lock, flags);
- }
+ goto out_schedule;
+ css_schedule_eval(sch->schid);
return 0;
-out_err:
- kfree(sch->private);
- sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
+
out_schedule:
- io_subchannel_schedule_removal(sch);
+ spin_lock_irq(sch->lock);
+ css_sched_sch_todo(sch, SCH_TODO_UNREG);
+ spin_unlock_irq(sch->lock);
return 0;
}
@@ -1337,32 +1076,23 @@ static int
io_subchannel_remove (struct subchannel *sch)
{
struct ccw_device *cdev;
- unsigned long flags;
cdev = sch_get_cdev(sch);
if (!cdev)
- return 0;
+ goto out_free;
+ io_subchannel_quiesce(sch);
/* Set ccw device to not operational and drop reference. */
- spin_lock_irqsave(cdev->ccwlock, flags);
+ spin_lock_irq(cdev->ccwlock);
sch_set_cdev(sch, NULL);
cdev->private->state = DEV_STATE_NOT_OPER;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ spin_unlock_irq(cdev->ccwlock);
ccw_device_unregister(cdev);
+out_free:
kfree(sch->private);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return 0;
}
-static int io_subchannel_notify(struct subchannel *sch, int event)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return 0;
- return ccw_device_notify(cdev, event);
-}
-
static void io_subchannel_verify(struct subchannel *sch)
{
struct ccw_device *cdev;
@@ -1372,36 +1102,6 @@ static void io_subchannel_verify(struct subchannel *sch)
dev_fsm_event(cdev, DEV_EVENT_VERIFY);
}
-static int check_for_io_on_path(struct subchannel *sch, int mask)
-{
- if (cio_update_schib(sch))
- return 0;
- if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
- return 1;
- return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch,
- struct ccw_device *cdev)
-{
- if (cio_clear(sch)) {
- /* Recheck device in case clear failed. */
- sch->lpm = 0;
- if (cdev->online)
- dev_fsm_event(cdev, DEV_EVENT_VERIFY);
- else
- css_schedule_eval(sch->schid);
- return;
- }
- cdev->private->state = DEV_STATE_CLEAR_VERIFY;
- /* Request retry of internal operation. */
- cdev->private->flags.intretry = 1;
- /* Call handler. */
- if (cdev->handler)
- cdev->handler(cdev, cdev->private->intparm,
- ERR_PTR(-EIO));
-}
-
static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
{
struct ccw_device *cdev;
@@ -1409,18 +1109,24 @@ static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
cdev = sch_get_cdev(sch);
if (!cdev)
return;
- if (check_for_io_on_path(sch, mask)) {
- if (cdev->private->state == DEV_STATE_ONLINE)
- ccw_device_kill_io(cdev);
- else {
- terminate_internal_io(sch, cdev);
- /* Re-start path verification. */
- dev_fsm_event(cdev, DEV_EVENT_VERIFY);
- }
- } else
- /* trigger path verification. */
- dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ if (cio_update_schib(sch))
+ goto err;
+ /* Check for I/O on path. */
+ if (scsw_actl(&sch->schib.scsw) == 0 || sch->schib.pmcw.lpum != mask)
+ goto out;
+ if (cdev->private->state == DEV_STATE_ONLINE) {
+ ccw_device_kill_io(cdev);
+ goto out;
+ }
+ if (cio_clear(sch))
+ goto err;
+out:
+ /* Trigger path verification. */
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ return;
+err:
+ dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
}
static int io_subchannel_chp_event(struct subchannel *sch,
@@ -1457,46 +1163,41 @@ static int io_subchannel_chp_event(struct subchannel *sch,
return 0;
}
-static void
-io_subchannel_shutdown(struct subchannel *sch)
+static void io_subchannel_quiesce(struct subchannel *sch)
{
struct ccw_device *cdev;
int ret;
+ spin_lock_irq(sch->lock);
cdev = sch_get_cdev(sch);
-
if (cio_is_console(sch->schid))
- return;
+ goto out_unlock;
if (!sch->schib.pmcw.ena)
- /* Nothing to do. */
- return;
+ goto out_unlock;
ret = cio_disable_subchannel(sch);
if (ret != -EBUSY)
- /* Subchannel is disabled, we're done. */
- return;
- cdev->private->state = DEV_STATE_QUIESCE;
+ goto out_unlock;
if (cdev->handler)
- cdev->handler(cdev, cdev->private->intparm,
- ERR_PTR(-EIO));
- ret = ccw_device_cancel_halt_clear(cdev);
- if (ret == -EBUSY) {
- ccw_device_set_timeout(cdev, HZ/10);
- wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
+ cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
+ while (ret == -EBUSY) {
+ cdev->private->state = DEV_STATE_QUIESCE;
+ ret = ccw_device_cancel_halt_clear(cdev);
+ if (ret == -EBUSY) {
+ ccw_device_set_timeout(cdev, HZ/10);
+ spin_unlock_irq(sch->lock);
+ wait_event(cdev->private->wait_q,
+ cdev->private->state != DEV_STATE_QUIESCE);
+ spin_lock_irq(sch->lock);
+ }
+ ret = cio_disable_subchannel(sch);
}
- cio_disable_subchannel(sch);
+out_unlock:
+ spin_unlock_irq(sch->lock);
}
-static int io_subchannel_get_status(struct subchannel *sch)
+static void io_subchannel_shutdown(struct subchannel *sch)
{
- struct schib schib;
-
- if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
- return CIO_GONE;
- if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
- return CIO_REVALIDATE;
- if (!sch->lpm)
- return CIO_NO_PATH;
- return CIO_OPER;
+ io_subchannel_quiesce(sch);
}
static int device_is_disconnected(struct ccw_device *cdev)
@@ -1575,20 +1276,16 @@ static void ccw_device_schedule_recovery(void)
static int purge_fn(struct device *dev, void *data)
{
struct ccw_device *cdev = to_ccwdev(dev);
- struct ccw_device_private *priv = cdev->private;
- int unreg;
+ struct ccw_dev_id *id = &cdev->private->dev_id;
spin_lock_irq(cdev->ccwlock);
- unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) &&
- (priv->state == DEV_STATE_OFFLINE);
+ if (is_blacklisted(id->ssid, id->devno) &&
+ (cdev->private->state == DEV_STATE_OFFLINE)) {
+ CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid,
+ id->devno);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
+ }
spin_unlock_irq(cdev->ccwlock);
- if (!unreg)
- goto out;
- CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid,
- priv->dev_id.devno);
- ccw_device_schedule_sch_unregister(cdev);
-
-out:
/* Abort loop in case of pending signal. */
if (signal_pending(current))
return -EINTR;
@@ -1630,91 +1327,169 @@ void ccw_device_set_notoper(struct ccw_device *cdev)
cdev->private->state = DEV_STATE_NOT_OPER;
}
-static int io_subchannel_sch_event(struct subchannel *sch, int slow)
+enum io_sch_action {
+ IO_SCH_UNREG,
+ IO_SCH_ORPH_UNREG,
+ IO_SCH_ATTACH,
+ IO_SCH_UNREG_ATTACH,
+ IO_SCH_ORPH_ATTACH,
+ IO_SCH_REPROBE,
+ IO_SCH_VERIFY,
+ IO_SCH_DISC,
+ IO_SCH_NOP,
+};
+
+static enum io_sch_action sch_get_action(struct subchannel *sch)
+{
+ struct ccw_device *cdev;
+
+ cdev = sch_get_cdev(sch);
+ if (cio_update_schib(sch)) {
+ /* Not operational. */
+ if (!cdev)
+ return IO_SCH_UNREG;
+ if (!ccw_device_notify(cdev, CIO_GONE))
+ return IO_SCH_UNREG;
+ return IO_SCH_ORPH_UNREG;
+ }
+ /* Operational. */
+ if (!cdev)
+ return IO_SCH_ATTACH;
+ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
+ if (!ccw_device_notify(cdev, CIO_GONE))
+ return IO_SCH_UNREG_ATTACH;
+ return IO_SCH_ORPH_ATTACH;
+ }
+ if ((sch->schib.pmcw.pam & sch->opm) == 0) {
+ if (!ccw_device_notify(cdev, CIO_NO_PATH))
+ return IO_SCH_UNREG;
+ return IO_SCH_DISC;
+ }
+ if (device_is_disconnected(cdev))
+ return IO_SCH_REPROBE;
+ if (cdev->online)
+ return IO_SCH_VERIFY;
+ return IO_SCH_NOP;
+}
+
+/**
+ * io_subchannel_sch_event - process subchannel event
+ * @sch: subchannel
+ * @process: non-zero if function is called in process context
+ *
+ * An unspecified event occurred for this subchannel. Adjust data according
+ * to the current operational state of the subchannel and device. Return
+ * zero when the event has been handled sufficiently or -EAGAIN when this
+ * function should be called again in process context.
+ */
+static int io_subchannel_sch_event(struct subchannel *sch, int process)
{
- int event, ret, disc;
unsigned long flags;
- enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action;
struct ccw_device *cdev;
+ struct ccw_dev_id dev_id;
+ enum io_sch_action action;
+ int rc = -EAGAIN;
spin_lock_irqsave(sch->lock, flags);
+ if (!device_is_registered(&sch->dev))
+ goto out_unlock;
+ if (work_pending(&sch->todo_work))
+ goto out_unlock;
cdev = sch_get_cdev(sch);
- disc = device_is_disconnected(cdev);
- if (disc && slow) {
- /* Disconnected devices are evaluated directly only.*/
- spin_unlock_irqrestore(sch->lock, flags);
- return 0;
- }
- /* No interrupt after machine check - kill pending timers. */
- if (cdev)
- ccw_device_set_timeout(cdev, 0);
- if (!disc && !slow) {
- /* Non-disconnected devices are evaluated on the slow path. */
- spin_unlock_irqrestore(sch->lock, flags);
- return -EAGAIN;
+ if (cdev && work_pending(&cdev->private->todo_work))
+ goto out_unlock;
+ action = sch_get_action(sch);
+ CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n",
+ sch->schid.ssid, sch->schid.sch_no, process,
+ action);
+ /* Perform immediate actions while holding the lock. */
+ switch (action) {
+ case IO_SCH_REPROBE:
+ /* Trigger device recognition. */
+ ccw_device_trigger_reprobe(cdev);
+ rc = 0;
+ goto out_unlock;
+ case IO_SCH_VERIFY:
+ /* Trigger path verification. */
+ io_subchannel_verify(sch);
+ rc = 0;
+ goto out_unlock;
+ case IO_SCH_DISC:
+ ccw_device_set_disconnected(cdev);
+ rc = 0;
+ goto out_unlock;
+ case IO_SCH_ORPH_UNREG:
+ case IO_SCH_ORPH_ATTACH:
+ ccw_device_set_disconnected(cdev);
+ break;
+ case IO_SCH_UNREG_ATTACH:
+ case IO_SCH_UNREG:
+ if (cdev)
+ ccw_device_set_notoper(cdev);
+ break;
+ case IO_SCH_NOP:
+ rc = 0;
+ goto out_unlock;
+ default:
+ break;
}
- event = io_subchannel_get_status(sch);
- CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
- sch->schid.ssid, sch->schid.sch_no, event,
- disc ? "disconnected" : "normal",
- slow ? "slow" : "fast");
- /* Analyze subchannel status. */
- action = NONE;
- switch (event) {
- case CIO_NO_PATH:
- if (disc) {
- /* Check if paths have become available. */
- action = REPROBE;
- break;
- }
- /* fall through */
- case CIO_GONE:
- /* Ask driver what to do with device. */
- if (io_subchannel_notify(sch, event))
- action = DISC;
- else
- action = UNREGISTER;
+ spin_unlock_irqrestore(sch->lock, flags);
+ /* All other actions require process context. */
+ if (!process)
+ goto out;
+ /* Handle attached ccw device. */
+ switch (action) {
+ case IO_SCH_ORPH_UNREG:
+ case IO_SCH_ORPH_ATTACH:
+ /* Move ccw device to orphanage. */
+ rc = ccw_device_move_to_orph(cdev);
+ if (rc)
+ goto out;
break;
- case CIO_REVALIDATE:
- /* Device will be removed, so no notify necessary. */
- if (disc)
- /* Reprobe because immediate unregister might block. */
- action = REPROBE;
- else
- action = UNREGISTER_PROBE;
+ case IO_SCH_UNREG_ATTACH:
+ /* Unregister ccw device. */
+ ccw_device_unregister(cdev);
break;
- case CIO_OPER:
- if (disc)
- /* Get device operational again. */
- action = REPROBE;
+ default:
break;
}
- /* Perform action. */
- ret = 0;
+ /* Handle subchannel. */
switch (action) {
- case UNREGISTER:
- case UNREGISTER_PROBE:
- ccw_device_set_notoper(cdev);
- /* Unregister device (will use subchannel lock). */
- spin_unlock_irqrestore(sch->lock, flags);
+ case IO_SCH_ORPH_UNREG:
+ case IO_SCH_UNREG:
css_sch_device_unregister(sch);
- spin_lock_irqsave(sch->lock, flags);
break;
- case REPROBE:
+ case IO_SCH_ORPH_ATTACH:
+ case IO_SCH_UNREG_ATTACH:
+ case IO_SCH_ATTACH:
+ dev_id.ssid = sch->schid.ssid;
+ dev_id.devno = sch->schib.pmcw.dev;
+ cdev = get_ccwdev_by_dev_id(&dev_id);
+ if (!cdev) {
+ sch_create_and_recog_new_device(sch);
+ break;
+ }
+ rc = ccw_device_move_to_sch(cdev, sch);
+ if (rc) {
+ /* Release reference from get_ccwdev_by_dev_id() */
+ put_device(&cdev->dev);
+ goto out;
+ }
+ spin_lock_irqsave(sch->lock, flags);
ccw_device_trigger_reprobe(cdev);
- break;
- case DISC:
- ccw_device_set_disconnected(cdev);
+ spin_unlock_irqrestore(sch->lock, flags);
+ /* Release reference from get_ccwdev_by_dev_id() */
+ put_device(&cdev->dev);
break;
default:
break;
}
- spin_unlock_irqrestore(sch->lock, flags);
- /* Probe if necessary. */
- if (action == UNREGISTER_PROBE)
- ret = css_probe_device(sch->schid);
+ return 0;
- return ret;
+out_unlock:
+ spin_unlock_irqrestore(sch->lock, flags);
+out:
+ return rc;
}
#ifdef CONFIG_CCW_CONSOLE
@@ -1744,10 +1519,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
sch->driver = &io_subchannel_driver;
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
- rc = io_subchannel_recog(cdev, sch);
- if (rc)
- return rc;
-
+ io_subchannel_recog(cdev, sch);
/* Now wait for the async. recognition to come to an end. */
spin_lock_irq(cdev->ccwlock);
while (!dev_fsm_final_state(cdev))
@@ -1763,7 +1535,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
rc = 0;
out_unlock:
spin_unlock_irq(cdev->ccwlock);
- return 0;
+ return rc;
}
struct ccw_device *
@@ -1919,7 +1691,7 @@ static int ccw_device_pm_prepare(struct device *dev)
{
struct ccw_device *cdev = to_ccwdev(dev);
- if (work_pending(&cdev->private->kick_work))
+ if (work_pending(&cdev->private->todo_work))
return -EAGAIN;
/* Fail while device is being set online/offline. */
if (atomic_read(&cdev->private->onoff))
@@ -2005,7 +1777,6 @@ static int ccw_device_pm_thaw(struct device *dev)
static void __ccw_device_pm_restore(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
- int ret;
if (cio_is_console(sch->schid))
goto out;
@@ -2015,22 +1786,10 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev)
*/
spin_lock_irq(sch->lock);
cdev->private->flags.resuming = 1;
- ret = ccw_device_recognition(cdev);
+ ccw_device_recognition(cdev);
spin_unlock_irq(sch->lock);
- if (ret) {
- CIO_MSG_EVENT(0, "Couldn't start recognition for device "
- "0.%x.%04x (ret=%d)\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno, ret);
- spin_lock_irq(sch->lock);
- cdev->private->state = DEV_STATE_DISCONNECTED;
- spin_unlock_irq(sch->lock);
- /* notify driver after the resume cb */
- goto out;
- }
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) ||
cdev->private->state == DEV_STATE_DISCONNECTED);
-
out:
cdev->private->flags.resuming = 0;
}
@@ -2040,7 +1799,7 @@ static int resume_handle_boxed(struct ccw_device *cdev)
cdev->private->state = DEV_STATE_BOXED;
if (ccw_device_notify(cdev, CIO_BOXED))
return 0;
- ccw_device_schedule_sch_unregister(cdev);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
return -ENODEV;
}
@@ -2049,7 +1808,7 @@ static int resume_handle_disc(struct ccw_device *cdev)
cdev->private->state = DEV_STATE_DISCONNECTED;
if (ccw_device_notify(cdev, CIO_GONE))
return 0;
- ccw_device_schedule_sch_unregister(cdev);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
return -ENODEV;
}
@@ -2094,9 +1853,7 @@ static int ccw_device_pm_restore(struct device *dev)
/* check if the device type has changed */
if (!ccw_device_test_sense_data(cdev)) {
ccw_device_update_sense_data(cdev);
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_do_unbind_bind);
- queue_work(ccw_device_work, &cdev->private->kick_work);
+ ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
ret = -ENODEV;
goto out_unlock;
}
@@ -2140,7 +1897,7 @@ out_disc_unlock:
goto out_restore;
out_unreg_unlock:
- ccw_device_schedule_sch_unregister(cdev);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
ret = -ENODEV;
out_unlock:
spin_unlock_irq(sch->lock);
@@ -2205,6 +1962,77 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev)
return sch->schid;
}
+static void ccw_device_todo(struct work_struct *work)
+{
+ struct ccw_device_private *priv;
+ struct ccw_device *cdev;
+ struct subchannel *sch;
+ enum cdev_todo todo;
+
+ priv = container_of(work, struct ccw_device_private, todo_work);
+ cdev = priv->cdev;
+ sch = to_subchannel(cdev->dev.parent);
+ /* Find out todo. */
+ spin_lock_irq(cdev->ccwlock);
+ todo = priv->todo;
+ priv->todo = CDEV_TODO_NOTHING;
+ CIO_MSG_EVENT(4, "cdev_todo: cdev=0.%x.%04x todo=%d\n",
+ priv->dev_id.ssid, priv->dev_id.devno, todo);
+ spin_unlock_irq(cdev->ccwlock);
+ /* Perform todo. */
+ switch (todo) {
+ case CDEV_TODO_ENABLE_CMF:
+ cmf_reenable(cdev);
+ break;
+ case CDEV_TODO_REBIND:
+ ccw_device_do_unbind_bind(cdev);
+ break;
+ case CDEV_TODO_REGISTER:
+ io_subchannel_register(cdev);
+ break;
+ case CDEV_TODO_UNREG_EVAL:
+ if (!sch_is_pseudo_sch(sch))
+ css_schedule_eval(sch->schid);
+ /* fall-through */
+ case CDEV_TODO_UNREG:
+ if (sch_is_pseudo_sch(sch))
+ ccw_device_unregister(cdev);
+ else
+ ccw_device_call_sch_unregister(cdev);
+ break;
+ default:
+ break;
+ }
+ /* Release workqueue ref. */
+ put_device(&cdev->dev);
+}
+
+/**
+ * ccw_device_sched_todo - schedule ccw device operation
+ * @cdev: ccw device
+ * @todo: todo
+ *
+ * Schedule the operation identified by @todo to be performed on the slow path
+ * workqueue. Do nothing if another operation with higher priority is already
+ * scheduled. Needs to be called with ccwdev lock held.
+ */
+void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
+{
+ CIO_MSG_EVENT(4, "cdev_todo: sched cdev=0.%x.%04x todo=%d\n",
+ cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
+ todo);
+ if (cdev->private->todo >= todo)
+ return;
+ cdev->private->todo = todo;
+ /* Get workqueue ref. */
+ if (!get_device(&cdev->dev))
+ return;
+ if (!queue_work(slow_path_wq, &cdev->private->todo_work)) {
+ /* Already queued, release workqueue ref. */
+ put_device(&cdev->dev);
+ }
+}
+
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 246c6482842..bcfe13e4263 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -21,7 +21,6 @@ enum dev_state {
DEV_STATE_DISBAND_PGID,
DEV_STATE_BOXED,
/* states to wait for i/o completion before doing something */
- DEV_STATE_CLEAR_VERIFY,
DEV_STATE_TIMEOUT_KILL,
DEV_STATE_QUIESCE,
/* special states for devices gone not operational */
@@ -29,6 +28,7 @@ enum dev_state {
DEV_STATE_DISCONNECTED_SENSE_ID,
DEV_STATE_CMFCHANGE,
DEV_STATE_CMFUPDATE,
+ DEV_STATE_STEAL_LOCK,
/* last element! */
NR_DEV_STATES
};
@@ -81,17 +81,16 @@ void io_subchannel_init_config(struct subchannel *sch);
int ccw_device_cancel_halt_clear(struct ccw_device *);
-void ccw_device_do_unbind_bind(struct work_struct *);
-void ccw_device_move_to_orphanage(struct work_struct *);
int ccw_device_is_orphan(struct ccw_device *);
-int ccw_device_recognition(struct ccw_device *);
+void ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
void ccw_device_update_sense_data(struct ccw_device *);
int ccw_device_test_sense_data(struct ccw_device *);
void ccw_device_schedule_sch_unregister(struct ccw_device *);
int ccw_purge_blacklisted(void);
+void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo);
/* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
@@ -99,24 +98,28 @@ void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
int ccw_device_accumulate_and_sense(struct ccw_device *, struct irb *);
int ccw_device_do_sense(struct ccw_device *, struct irb *);
+/* Function prototype for internal request handling. */
+int lpm_adjust(int lpm, int mask);
+void ccw_request_start(struct ccw_device *);
+int ccw_request_cancel(struct ccw_device *cdev);
+void ccw_request_handler(struct ccw_device *cdev);
+void ccw_request_timeout(struct ccw_device *cdev);
+void ccw_request_notoper(struct ccw_device *cdev);
+
/* Function prototypes for sense id stuff. */
void ccw_device_sense_id_start(struct ccw_device *);
-void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event);
void ccw_device_sense_id_done(struct ccw_device *, int);
/* Function prototypes for path grouping stuff. */
-void ccw_device_sense_pgid_start(struct ccw_device *);
-void ccw_device_sense_pgid_irq(struct ccw_device *, enum dev_event);
-void ccw_device_sense_pgid_done(struct ccw_device *, int);
-
void ccw_device_verify_start(struct ccw_device *);
-void ccw_device_verify_irq(struct ccw_device *, enum dev_event);
void ccw_device_verify_done(struct ccw_device *, int);
void ccw_device_disband_start(struct ccw_device *);
-void ccw_device_disband_irq(struct ccw_device *, enum dev_event);
void ccw_device_disband_done(struct ccw_device *, int);
+void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *);
+void ccw_device_stlck_done(struct ccw_device *, void *, int);
+
int ccw_device_call_handler(struct ccw_device *);
int ccw_device_stlck(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index b9613d7df9e..ae760658a13 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -229,8 +229,8 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
sch = to_subchannel(cdev->dev.parent);
- ccw_device_set_timeout(cdev, 0);
- cio_disable_subchannel(sch);
+ if (cio_disable_subchannel(sch))
+ state = DEV_STATE_NOT_OPER;
/*
* Now that we tried recognition, we have performed device selection
* through ssch() and the path information is up to date.
@@ -263,22 +263,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
}
switch (state) {
case DEV_STATE_NOT_OPER:
- CIO_MSG_EVENT(2, "SenseID : unknown device %04x on "
- "subchannel 0.%x.%04x\n",
- cdev->private->dev_id.devno,
- sch->schid.ssid, sch->schid.sch_no);
break;
case DEV_STATE_OFFLINE:
if (!cdev->online) {
ccw_device_update_sense_data(cdev);
- /* Issue device info message. */
- CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: "
- "CU Type/Mod = %04X/%02X, Dev Type/Mod "
- "= %04X/%02X\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno,
- cdev->id.cu_type, cdev->id.cu_model,
- cdev->id.dev_type, cdev->id.dev_model);
break;
}
cdev->private->state = DEV_STATE_OFFLINE;
@@ -289,16 +277,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
wake_up(&cdev->private->wait_q);
} else {
ccw_device_update_sense_data(cdev);
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_do_unbind_bind);
- queue_work(ccw_device_work, &cdev->private->kick_work);
+ ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
}
return;
case DEV_STATE_BOXED:
- CIO_MSG_EVENT(0, "SenseID : boxed device %04x on "
- " subchannel 0.%x.%04x\n",
- cdev->private->dev_id.devno,
- sch->schid.ssid, sch->schid.sch_no);
if (cdev->id.cu_type != 0) { /* device was recognized before */
cdev->private->flags.recog_done = 1;
cdev->private->state = DEV_STATE_BOXED;
@@ -343,28 +325,16 @@ int ccw_device_notify(struct ccw_device *cdev, int event)
return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
}
-static void cmf_reenable_delayed(struct work_struct *work)
-{
- struct ccw_device_private *priv;
- struct ccw_device *cdev;
-
- priv = container_of(work, struct ccw_device_private, kick_work);
- cdev = priv->cdev;
- cmf_reenable(cdev);
-}
-
static void ccw_device_oper_notify(struct ccw_device *cdev)
{
if (ccw_device_notify(cdev, CIO_OPER)) {
/* Reenable channel measurements, if needed. */
- PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed);
- queue_work(ccw_device_work, &cdev->private->kick_work);
+ ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
return;
}
/* Driver doesn't want device back. */
ccw_device_set_notoper(cdev);
- PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unbind_bind);
- queue_work(ccw_device_work, &cdev->private->kick_work);
+ ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
}
/*
@@ -392,14 +362,14 @@ ccw_device_done(struct ccw_device *cdev, int state)
CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n",
cdev->private->dev_id.devno, sch->schid.sch_no);
if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED))
- ccw_device_schedule_sch_unregister(cdev);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
cdev->private->flags.donotify = 0;
break;
case DEV_STATE_NOT_OPER:
CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n",
cdev->private->dev_id.devno, sch->schid.sch_no);
if (!ccw_device_notify(cdev, CIO_GONE))
- ccw_device_schedule_sch_unregister(cdev);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
else
ccw_device_set_disconnected(cdev);
cdev->private->flags.donotify = 0;
@@ -409,7 +379,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
"%04x\n", cdev->private->dev_id.devno,
sch->schid.sch_no);
if (!ccw_device_notify(cdev, CIO_NO_PATH))
- ccw_device_schedule_sch_unregister(cdev);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
else
ccw_device_set_disconnected(cdev);
cdev->private->flags.donotify = 0;
@@ -425,107 +395,12 @@ ccw_device_done(struct ccw_device *cdev, int state)
wake_up(&cdev->private->wait_q);
}
-static int cmp_pgid(struct pgid *p1, struct pgid *p2)
-{
- char *c1;
- char *c2;
-
- c1 = (char *)p1;
- c2 = (char *)p2;
-
- return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1);
-}
-
-static void __ccw_device_get_common_pgid(struct ccw_device *cdev)
-{
- int i;
- int last;
-
- last = 0;
- for (i = 0; i < 8; i++) {
- if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET)
- /* No PGID yet */
- continue;
- if (cdev->private->pgid[last].inf.ps.state1 ==
- SNID_STATE1_RESET) {
- /* First non-zero PGID */
- last = i;
- continue;
- }
- if (cmp_pgid(&cdev->private->pgid[i],
- &cdev->private->pgid[last]) == 0)
- /* Non-conflicting PGIDs */
- continue;
-
- /* PGID mismatch, can't pathgroup. */
- CIO_MSG_EVENT(0, "SNID - pgid mismatch for device "
- "0.%x.%04x, can't pathgroup\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
- cdev->private->options.pgroup = 0;
- return;
- }
- if (cdev->private->pgid[last].inf.ps.state1 ==
- SNID_STATE1_RESET)
- /* No previous pgid found */
- memcpy(&cdev->private->pgid[0],
- &channel_subsystems[0]->global_pgid,
- sizeof(struct pgid));
- else
- /* Use existing pgid */
- memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last],
- sizeof(struct pgid));
-}
-
-/*
- * Function called from device_pgid.c after sense path ground has completed.
- */
-void
-ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
-{
- struct subchannel *sch;
-
- sch = to_subchannel(cdev->dev.parent);
- switch (err) {
- case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
- cdev->private->options.pgroup = 0;
- break;
- case 0: /* success */
- case -EACCES: /* partial success, some paths not operational */
- /* Check if all pgids are equal or 0. */
- __ccw_device_get_common_pgid(cdev);
- break;
- case -ETIME: /* Sense path group id stopped by timeout. */
- case -EUSERS: /* device is reserved for someone else. */
- ccw_device_done(cdev, DEV_STATE_BOXED);
- return;
- default:
- ccw_device_done(cdev, DEV_STATE_NOT_OPER);
- return;
- }
- /* Start Path Group verification. */
- cdev->private->state = DEV_STATE_VERIFY;
- cdev->private->flags.doverify = 0;
- ccw_device_verify_start(cdev);
-}
-
/*
* Start device recognition.
*/
-int
-ccw_device_recognition(struct ccw_device *cdev)
+void ccw_device_recognition(struct ccw_device *cdev)
{
- struct subchannel *sch;
- int ret;
-
- sch = to_subchannel(cdev->dev.parent);
- ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
- if (ret != 0)
- /* Couldn't enable the subchannel for i/o. Sick device. */
- return ret;
-
- /* After 60s the device recognition is considered to have failed. */
- ccw_device_set_timeout(cdev, 60*HZ);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
/*
* We used to start here with a sense pgid to find out whether a device
@@ -537,32 +412,33 @@ ccw_device_recognition(struct ccw_device *cdev)
*/
cdev->private->flags.recog_done = 0;
cdev->private->state = DEV_STATE_SENSE_ID;
+ if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) {
+ ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
+ return;
+ }
ccw_device_sense_id_start(cdev);
- return 0;
}
/*
- * Handle timeout in device recognition.
+ * Handle events for states that use the ccw request infrastructure.
*/
-static void
-ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
+static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
{
- int ret;
-
- ret = ccw_device_cancel_halt_clear(cdev);
- switch (ret) {
- case 0:
- ccw_device_recog_done(cdev, DEV_STATE_BOXED);
+ switch (e) {
+ case DEV_EVENT_NOTOPER:
+ ccw_request_notoper(cdev);
break;
- case -ENODEV:
- ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
+ case DEV_EVENT_INTERRUPT:
+ ccw_request_handler(cdev);
+ break;
+ case DEV_EVENT_TIMEOUT:
+ ccw_request_timeout(cdev);
break;
default:
- ccw_device_set_timeout(cdev, 3*HZ);
+ break;
}
}
-
void
ccw_device_verify_done(struct ccw_device *cdev, int err)
{
@@ -571,21 +447,18 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
sch = to_subchannel(cdev->dev.parent);
/* Update schib - pom may have changed. */
if (cio_update_schib(sch)) {
- cdev->private->flags.donotify = 0;
- ccw_device_done(cdev, DEV_STATE_NOT_OPER);
- return;
+ err = -ENODEV;
+ goto callback;
}
/* Update lpm with verified path mask. */
sch->lpm = sch->vpm;
/* Repeat path verification? */
if (cdev->private->flags.doverify) {
- cdev->private->flags.doverify = 0;
ccw_device_verify_start(cdev);
return;
}
+callback:
switch (err) {
- case -EOPNOTSUPP: /* path grouping not supported, just set online. */
- cdev->private->options.pgroup = 0;
case 0:
ccw_device_done(cdev, DEV_STATE_ONLINE);
/* Deliver fake irb to device driver, if needed. */
@@ -604,18 +477,20 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
}
break;
case -ETIME:
+ case -EUSERS:
/* Reset oper notify indication after verify error. */
cdev->private->flags.donotify = 0;
ccw_device_done(cdev, DEV_STATE_BOXED);
break;
+ case -EACCES:
+ /* Reset oper notify indication after verify error. */
+ cdev->private->flags.donotify = 0;
+ ccw_device_done(cdev, DEV_STATE_DISCONNECTED);
+ break;
default:
/* Reset oper notify indication after verify error. */
cdev->private->flags.donotify = 0;
- if (cdev->online) {
- ccw_device_set_timeout(cdev, 0);
- dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
- } else
- ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break;
}
}
@@ -640,17 +515,9 @@ ccw_device_online(struct ccw_device *cdev)
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return ret;
}
- /* Do we want to do path grouping? */
- if (!cdev->private->options.pgroup) {
- /* Start initial path verification. */
- cdev->private->state = DEV_STATE_VERIFY;
- cdev->private->flags.doverify = 0;
- ccw_device_verify_start(cdev);
- return 0;
- }
- /* Do a SensePGID first. */
- cdev->private->state = DEV_STATE_SENSE_PGID;
- ccw_device_sense_pgid_start(cdev);
+ /* Start initial path verification. */
+ cdev->private->state = DEV_STATE_VERIFY;
+ ccw_device_verify_start(cdev);
return 0;
}
@@ -666,7 +533,6 @@ ccw_device_disband_done(struct ccw_device *cdev, int err)
break;
default:
cdev->private->flags.donotify = 0;
- dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break;
}
@@ -703,7 +569,7 @@ ccw_device_offline(struct ccw_device *cdev)
if (cdev->private->state != DEV_STATE_ONLINE)
return -EINVAL;
/* Are we doing path grouping? */
- if (!cdev->private->options.pgroup) {
+ if (!cdev->private->flags.pgroup) {
/* No, set state offline immediately. */
ccw_device_done(cdev, DEV_STATE_OFFLINE);
return 0;
@@ -715,43 +581,13 @@ ccw_device_offline(struct ccw_device *cdev)
}
/*
- * Handle timeout in device online/offline process.
- */
-static void
-ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event)
-{
- int ret;
-
- ret = ccw_device_cancel_halt_clear(cdev);
- switch (ret) {
- case 0:
- ccw_device_done(cdev, DEV_STATE_BOXED);
- break;
- case -ENODEV:
- ccw_device_done(cdev, DEV_STATE_NOT_OPER);
- break;
- default:
- ccw_device_set_timeout(cdev, 3*HZ);
- }
-}
-
-/*
- * Handle not oper event in device recognition.
- */
-static void
-ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event)
-{
- ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
-}
-
-/*
* Handle not operational event in non-special state.
*/
static void ccw_device_generic_notoper(struct ccw_device *cdev,
enum dev_event dev_event)
{
if (!ccw_device_notify(cdev, CIO_GONE))
- ccw_device_schedule_sch_unregister(cdev);
+ ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
else
ccw_device_set_disconnected(cdev);
}
@@ -802,11 +638,27 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
}
/* Device is idle, we can do the path verification. */
cdev->private->state = DEV_STATE_VERIFY;
- cdev->private->flags.doverify = 0;
ccw_device_verify_start(cdev);
}
/*
+ * Handle path verification event in boxed state.
+ */
+static void ccw_device_boxed_verify(struct ccw_device *cdev,
+ enum dev_event dev_event)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ if (cdev->online) {
+ if (cio_enable_subchannel(sch, (u32) (addr_t) sch))
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ else
+ ccw_device_online_verify(cdev, dev_event);
+ } else
+ css_schedule_eval(sch->schid);
+}
+
+/*
* Got an interrupt for a normal io (state online).
*/
static void
@@ -904,12 +756,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
*/
if (scsw_fctl(&irb->scsw) &
(SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
- /* Retry Basic Sense if requested. */
- if (cdev->private->flags.intretry) {
- cdev->private->flags.intretry = 0;
- ccw_device_do_sense(cdev, irb);
- return;
- }
cdev->private->flags.dosense = 0;
memset(&cdev->private->irb, 0, sizeof(struct irb));
ccw_device_accumulate_irb(cdev, irb);
@@ -933,21 +779,6 @@ call_handler:
}
static void
-ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event)
-{
- struct irb *irb;
-
- irb = (struct irb *) __LC_IRB;
- /* Accumulate status. We don't do basic sense. */
- ccw_device_accumulate_irb(cdev, irb);
- /* Remember to clear irb to avoid residuals. */
- memset(&cdev->private->irb, 0, sizeof(struct irb));
- /* Try to start delayed device verification. */
- ccw_device_online_verify(cdev, 0);
- /* Note: Don't call handler for cio initiated clear! */
-}
-
-static void
ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
struct subchannel *sch;
@@ -1004,32 +835,6 @@ ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
}
static void
-ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
-{
- struct irb *irb;
-
- switch (dev_event) {
- case DEV_EVENT_INTERRUPT:
- irb = (struct irb *) __LC_IRB;
- /* Check for unsolicited interrupt. */
- if ((scsw_stctl(&irb->scsw) ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
- (!scsw_cc(&irb->scsw)))
- /* FIXME: we should restart stlck here, but this
- * is extremely unlikely ... */
- goto out_wakeup;
-
- ccw_device_accumulate_irb(cdev, irb);
- /* We don't care about basic sense etc. */
- break;
- default: /* timeout */
- break;
- }
-out_wakeup:
- wake_up(&cdev->private->wait_q);
-}
-
-static void
ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
{
struct subchannel *sch;
@@ -1038,10 +843,6 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0)
/* Couldn't enable the subchannel for i/o. Sick device. */
return;
-
- /* After 60s the device recognition is considered to have failed. */
- ccw_device_set_timeout(cdev, 60*HZ);
-
cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID;
ccw_device_sense_id_start(cdev);
}
@@ -1072,22 +873,20 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
/* We should also udate ssd info, but this has to wait. */
/* Check if this is another device which appeared on the same sch. */
- if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
- PREPARE_WORK(&cdev->private->kick_work,
- ccw_device_move_to_orphanage);
- queue_work(slow_path_wq, &cdev->private->kick_work);
- } else
+ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno)
+ css_schedule_eval(sch->schid);
+ else
ccw_device_start_id(cdev, 0);
}
-static void
-ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void ccw_device_disabled_irq(struct ccw_device *cdev,
+ enum dev_event dev_event)
{
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
/*
- * An interrupt in state offline means a previous disable was not
+ * An interrupt in a disabled state means a previous disable was not
* successful - should not happen, but we try to disable again.
*/
cio_disable_subchannel(sch);
@@ -1113,10 +912,7 @@ static void
ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
{
ccw_device_set_timeout(cdev, 0);
- if (dev_event == DEV_EVENT_NOTOPER)
- cdev->private->state = DEV_STATE_NOT_OPER;
- else
- cdev->private->state = DEV_STATE_OFFLINE;
+ cdev->private->state = DEV_STATE_NOT_OPER;
wake_up(&cdev->private->wait_q);
}
@@ -1126,17 +922,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
ret = ccw_device_cancel_halt_clear(cdev);
- switch (ret) {
- case 0:
- cdev->private->state = DEV_STATE_OFFLINE;
- wake_up(&cdev->private->wait_q);
- break;
- case -ENODEV:
+ if (ret == -EBUSY) {
+ ccw_device_set_timeout(cdev, HZ/10);
+ } else {
cdev->private->state = DEV_STATE_NOT_OPER;
wake_up(&cdev->private->wait_q);
- break;
- default:
- ccw_device_set_timeout(cdev, HZ/10);
}
}
@@ -1150,50 +940,37 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
}
/*
- * Bug operation action.
- */
-static void
-ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event)
-{
- CIO_MSG_EVENT(0, "Internal state [%i][%i] not handled for device "
- "0.%x.%04x\n", cdev->private->state, dev_event,
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
- BUG();
-}
-
-/*
* device statemachine
*/
fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_STATE_NOT_OPER] = {
[DEV_EVENT_NOTOPER] = ccw_device_nop,
- [DEV_EVENT_INTERRUPT] = ccw_device_bug,
+ [DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq,
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
[DEV_EVENT_VERIFY] = ccw_device_nop,
},
[DEV_STATE_SENSE_PGID] = {
- [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq,
- [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout,
+ [DEV_EVENT_NOTOPER] = ccw_device_request_event,
+ [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
+ [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
[DEV_EVENT_VERIFY] = ccw_device_nop,
},
[DEV_STATE_SENSE_ID] = {
- [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq,
- [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout,
+ [DEV_EVENT_NOTOPER] = ccw_device_request_event,
+ [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
+ [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
[DEV_EVENT_VERIFY] = ccw_device_nop,
},
[DEV_STATE_OFFLINE] = {
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_offline_irq,
+ [DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq,
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
[DEV_EVENT_VERIFY] = ccw_device_offline_verify,
},
[DEV_STATE_VERIFY] = {
- [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq,
- [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout,
+ [DEV_EVENT_NOTOPER] = ccw_device_request_event,
+ [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
+ [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
[DEV_EVENT_VERIFY] = ccw_device_delay_verify,
},
[DEV_STATE_ONLINE] = {
@@ -1209,24 +986,18 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_VERIFY] = ccw_device_online_verify,
},
[DEV_STATE_DISBAND_PGID] = {
- [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq,
- [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout,
+ [DEV_EVENT_NOTOPER] = ccw_device_request_event,
+ [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
+ [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
[DEV_EVENT_VERIFY] = ccw_device_nop,
},
[DEV_STATE_BOXED] = {
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_stlck_done,
- [DEV_EVENT_TIMEOUT] = ccw_device_stlck_done,
- [DEV_EVENT_VERIFY] = ccw_device_nop,
- },
- /* states to wait for i/o completion before doing something */
- [DEV_STATE_CLEAR_VERIFY] = {
- [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_clear_verify,
+ [DEV_EVENT_INTERRUPT] = ccw_device_nop,
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
- [DEV_EVENT_VERIFY] = ccw_device_nop,
+ [DEV_EVENT_VERIFY] = ccw_device_boxed_verify,
},
+ /* states to wait for i/o completion before doing something */
[DEV_STATE_TIMEOUT_KILL] = {
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
[DEV_EVENT_INTERRUPT] = ccw_device_killing_irq,
@@ -1243,13 +1014,13 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_STATE_DISCONNECTED] = {
[DEV_EVENT_NOTOPER] = ccw_device_nop,
[DEV_EVENT_INTERRUPT] = ccw_device_start_id,
- [DEV_EVENT_TIMEOUT] = ccw_device_bug,
+ [DEV_EVENT_TIMEOUT] = ccw_device_nop,
[DEV_EVENT_VERIFY] = ccw_device_start_id,
},
[DEV_STATE_DISCONNECTED_SENSE_ID] = {
- [DEV_EVENT_NOTOPER] = ccw_device_recog_notoper,
- [DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq,
- [DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout,
+ [DEV_EVENT_NOTOPER] = ccw_device_request_event,
+ [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
+ [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
[DEV_EVENT_VERIFY] = ccw_device_nop,
},
[DEV_STATE_CMFCHANGE] = {
@@ -1264,6 +1035,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock,
[DEV_EVENT_VERIFY] = ccw_device_update_cmfblock,
},
+ [DEV_STATE_STEAL_LOCK] = {
+ [DEV_EVENT_NOTOPER] = ccw_device_request_event,
+ [DEV_EVENT_INTERRUPT] = ccw_device_request_event,
+ [DEV_EVENT_TIMEOUT] = ccw_device_request_event,
+ [DEV_EVENT_VERIFY] = ccw_device_nop,
+ },
};
EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index 1bdaa614e34..78a0b43862c 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -1,40 +1,39 @@
/*
- * drivers/s390/cio/device_id.c
+ * CCW device SENSE ID I/O handling.
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
- * Martin Schwidefsky (schwidefsky@de.ibm.com)
- *
- * Sense ID functions.
+ * Copyright IBM Corp. 2002,2009
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
-#include <linux/module.h>
-#include <linux/init.h>
#include <linux/kernel.h>
-
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
#include <asm/ccwdev.h>
-#include <asm/delay.h>
+#include <asm/setup.h>
#include <asm/cio.h>
-#include <asm/lowcore.h>
#include <asm/diag.h>
#include "cio.h"
#include "cio_debug.h"
-#include "css.h"
#include "device.h"
-#include "ioasm.h"
#include "io_sch.h"
+#define SENSE_ID_RETRIES 256
+#define SENSE_ID_TIMEOUT (10 * HZ)
+#define SENSE_ID_MIN_LEN 4
+#define SENSE_ID_BASIC_LEN 7
+
/**
- * vm_vdev_to_cu_type - Convert vm virtual device into control unit type
- * for certain devices.
- * @class: virtual device class
- * @type: virtual device type
+ * diag210_to_senseid - convert diag 0x210 data to sense id information
+ * @senseid: sense id
+ * @diag: diag 0x210 data
*
- * Returns control unit type if a match was made or %0xffff otherwise.
+ * Return 0 on success, non-zero otherwise.
*/
-static int vm_vdev_to_cu_type(int class, int type)
+static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)
{
static struct {
int class, type, cu_type;
@@ -71,253 +70,153 @@ static int vm_vdev_to_cu_type(int class, int type)
};
int i;
- for (i = 0; i < ARRAY_SIZE(vm_devices); i++)
- if (class == vm_devices[i].class && type == vm_devices[i].type)
- return vm_devices[i].cu_type;
+ /* Special case for osa devices. */
+ if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) {
+ senseid->cu_type = 0x3088;
+ senseid->cu_model = 0x60;
+ senseid->reserved = 0xff;
+ return 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(vm_devices); i++) {
+ if (diag->vrdcvcla == vm_devices[i].class &&
+ diag->vrdcvtyp == vm_devices[i].type) {
+ senseid->cu_type = vm_devices[i].cu_type;
+ senseid->reserved = 0xff;
+ return 0;
+ }
+ }
- return 0xffff;
+ return -ENODEV;
}
/**
- * diag_get_dev_info - retrieve device information via DIAG X'210'
- * @devno: device number
- * @ps: pointer to sense ID data area
+ * diag_get_dev_info - retrieve device information via diag 0x210
+ * @cdev: ccw device
*
* Returns zero on success, non-zero otherwise.
*/
-static int diag_get_dev_info(u16 devno, struct senseid *ps)
+static int diag210_get_dev_info(struct ccw_device *cdev)
{
+ struct ccw_dev_id *dev_id = &cdev->private->dev_id;
+ struct senseid *senseid = &cdev->private->senseid;
struct diag210 diag_data;
- int ccode;
-
- CIO_TRACE_EVENT (4, "VMvdinf");
-
- diag_data = (struct diag210) {
- .vrdcdvno = devno,
- .vrdclen = sizeof (diag_data),
- };
-
- ccode = diag210 (&diag_data);
- if ((ccode == 0) || (ccode == 2)) {
- ps->reserved = 0xff;
-
- /* Special case for osa devices. */
- if (diag_data.vrdcvcla == 0x02 && diag_data.vrdcvtyp == 0x20) {
- ps->cu_type = 0x3088;
- ps->cu_model = 0x60;
- return 0;
- }
- ps->cu_type = vm_vdev_to_cu_type(diag_data.vrdcvcla,
- diag_data.vrdcvtyp);
- if (ps->cu_type != 0xffff)
- return 0;
- }
-
- CIO_MSG_EVENT(0, "DIAG X'210' for device %04X returned (cc = %d):"
- "vdev class : %02X, vdev type : %04X \n ... "
- "rdev class : %02X, rdev type : %04X, "
- "rdev model: %02X\n",
- devno, ccode,
- diag_data.vrdcvcla, diag_data.vrdcvtyp,
- diag_data.vrdcrccl, diag_data.vrdccrty,
- diag_data.vrdccrmd);
-
+ int rc;
+
+ if (dev_id->ssid != 0)
+ return -ENODEV;
+ memset(&diag_data, 0, sizeof(diag_data));
+ diag_data.vrdcdvno = dev_id->devno;
+ diag_data.vrdclen = sizeof(diag_data);
+ rc = diag210(&diag_data);
+ CIO_TRACE_EVENT(4, "diag210");
+ CIO_HEX_EVENT(4, &rc, sizeof(rc));
+ CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data));
+ if (rc != 0 && rc != 2)
+ goto err_failed;
+ if (diag210_to_senseid(senseid, &diag_data))
+ goto err_unknown;
+ return 0;
+
+err_unknown:
+ CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",
+ dev_id->ssid, dev_id->devno);
+ return -ENODEV;
+err_failed:
+ CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",
+ dev_id->ssid, dev_id->devno, rc);
return -ENODEV;
}
/*
- * Start Sense ID helper function.
- * Try to obtain the 'control unit'/'device type' information
- * associated with the subchannel.
+ * Initialize SENSE ID data.
*/
-static int
-__ccw_device_sense_id_start(struct ccw_device *cdev)
-{
- struct subchannel *sch;
- struct ccw1 *ccw;
- int ret;
-
- sch = to_subchannel(cdev->dev.parent);
- /* Setup sense channel program. */
- ccw = cdev->private->iccws;
- ccw->cmd_code = CCW_CMD_SENSE_ID;
- ccw->cda = (__u32) __pa (&cdev->private->senseid);
- ccw->count = sizeof (struct senseid);
- ccw->flags = CCW_FLAG_SLI;
-
- /* Reset device status. */
- memset(&cdev->private->irb, 0, sizeof(struct irb));
-
- /* Try on every path. */
- ret = -ENODEV;
- while (cdev->private->imask != 0) {
- cdev->private->senseid.cu_type = 0xFFFF;
- if ((sch->opm & cdev->private->imask) != 0 &&
- cdev->private->iretry > 0) {
- cdev->private->iretry--;
- /* Reset internal retry indication. */
- cdev->private->flags.intretry = 0;
- ret = cio_start (sch, cdev->private->iccws,
- cdev->private->imask);
- /* ret is 0, -EBUSY, -EACCES or -ENODEV */
- if (ret != -EACCES)
- return ret;
- }
- cdev->private->imask >>= 1;
- cdev->private->iretry = 5;
- }
- return ret;
-}
-
-void
-ccw_device_sense_id_start(struct ccw_device *cdev)
+static void snsid_init(struct ccw_device *cdev)
{
- int ret;
-
- memset (&cdev->private->senseid, 0, sizeof (struct senseid));
- cdev->private->imask = 0x80;
- cdev->private->iretry = 5;
- ret = __ccw_device_sense_id_start(cdev);
- if (ret && ret != -EBUSY)
- ccw_device_sense_id_done(cdev, ret);
+ cdev->private->flags.esid = 0;
+ memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid));
+ cdev->private->senseid.cu_type = 0xffff;
}
/*
- * Called from interrupt context to check if a valid answer
- * to Sense ID was received.
+ * Check for complete SENSE ID data.
*/
-static int
-ccw_device_check_sense_id(struct ccw_device *cdev)
+static int snsid_check(struct ccw_device *cdev, void *data)
{
- struct subchannel *sch;
- struct irb *irb;
-
- sch = to_subchannel(cdev->dev.parent);
- irb = &cdev->private->irb;
-
- /* Check the error cases. */
- if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
- /* Retry Sense ID if requested. */
- if (cdev->private->flags.intretry) {
- cdev->private->flags.intretry = 0;
- return -EAGAIN;
- }
- return -ETIME;
- }
- if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) {
- /*
- * if the device doesn't support the SenseID
- * command further retries wouldn't help ...
- * NB: We don't check here for intervention required like we
- * did before, because tape devices with no tape inserted
- * may present this status *in conjunction with* the
- * sense id information. So, for intervention required,
- * we use the "whack it until it talks" strategy...
- */
- CIO_MSG_EVENT(0, "SenseID : device %04x on Subchannel "
- "0.%x.%04x reports cmd reject\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no);
+ struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd;
+ int len = sizeof(struct senseid) - scsw->count;
+
+ /* Check for incomplete SENSE ID data. */
+ if (len < SENSE_ID_MIN_LEN)
+ goto out_restart;
+ if (cdev->private->senseid.cu_type == 0xffff)
+ goto out_restart;
+ /* Check for incompatible SENSE ID data. */
+ if (cdev->private->senseid.reserved != 0xff)
return -EOPNOTSUPP;
- }
- if (irb->esw.esw0.erw.cons) {
- CIO_MSG_EVENT(2, "SenseID : UC on dev 0.%x.%04x, "
- "lpum %02X, cnt %02d, sns :"
- " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno,
- irb->esw.esw0.sublog.lpum,
- irb->esw.esw0.erw.scnt,
- irb->ecw[0], irb->ecw[1],
- irb->ecw[2], irb->ecw[3],
- irb->ecw[4], irb->ecw[5],
- irb->ecw[6], irb->ecw[7]);
- return -EAGAIN;
- }
- if (irb->scsw.cmd.cc == 3) {
- u8 lpm;
+ /* Check for extended-identification information. */
+ if (len > SENSE_ID_BASIC_LEN)
+ cdev->private->flags.esid = 1;
+ return 0;
- lpm = to_io_private(sch)->orb.cmd.lpm;
- if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
- CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
- "on subchannel 0.%x.%04x is "
- "'not operational'\n", lpm,
- cdev->private->dev_id.devno,
- sch->schid.ssid, sch->schid.sch_no);
- return -EACCES;
- }
-
- /* Did we get a proper answer ? */
- if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
- cdev->private->senseid.reserved == 0xFF) {
- if (irb->scsw.cmd.count < sizeof(struct senseid) - 8)
- cdev->private->flags.esid = 1;
- return 0; /* Success */
- }
-
- /* Hmm, whatever happened, try again. */
- CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
- "subchannel 0.%x.%04x returns status %02X%02X\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no,
- irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
+out_restart:
+ snsid_init(cdev);
return -EAGAIN;
}
/*
- * Got interrupt for Sense ID.
+ * Process SENSE ID request result.
*/
-void
-ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
{
- struct subchannel *sch;
- struct irb *irb;
- int ret;
-
- sch = to_subchannel(cdev->dev.parent);
- irb = (struct irb *) __LC_IRB;
- /* Retry sense id, if needed. */
- if (irb->scsw.cmd.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) {
- ret = __ccw_device_sense_id_start(cdev);
- if (ret && ret != -EBUSY)
- ccw_device_sense_id_done(cdev, ret);
+ struct ccw_dev_id *id = &cdev->private->dev_id;
+ struct senseid *senseid = &cdev->private->senseid;
+ int vm = 0;
+
+ if (rc && MACHINE_IS_VM) {
+ /* Try diag 0x210 fallback on z/VM. */
+ snsid_init(cdev);
+ if (diag210_get_dev_info(cdev) == 0) {
+ rc = 0;
+ vm = 1;
}
- return;
}
- if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
- return;
- ret = ccw_device_check_sense_id(cdev);
- memset(&cdev->private->irb, 0, sizeof(struct irb));
- switch (ret) {
- /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN or -EACCES */
- case 0: /* Sense id succeeded. */
- case -ETIME: /* Sense id stopped by timeout. */
- ccw_device_sense_id_done(cdev, ret);
- break;
- case -EACCES: /* channel is not operational. */
- sch->lpm &= ~cdev->private->imask;
- cdev->private->imask >>= 1;
- cdev->private->iretry = 5;
- /* fall through. */
- case -EAGAIN: /* try again. */
- ret = __ccw_device_sense_id_start(cdev);
- if (ret == 0 || ret == -EBUSY)
- break;
- /* fall through. */
- default: /* Sense ID failed. Try asking VM. */
- if (MACHINE_IS_VM)
- ret = diag_get_dev_info(cdev->private->dev_id.devno,
- &cdev->private->senseid);
- else
- /*
- * If we can't couldn't identify the device type we
- * consider the device "not operational".
- */
- ret = -ENODEV;
+ CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "
+ "%04x/%02x%s\n", id->ssid, id->devno, rc,
+ senseid->cu_type, senseid->cu_model, senseid->dev_type,
+ senseid->dev_model, vm ? " (diag210)" : "");
+ ccw_device_sense_id_done(cdev, rc);
+}
- ccw_device_sense_id_done(cdev, ret);
- break;
- }
+/**
+ * ccw_device_sense_id_start - perform SENSE ID
+ * @cdev: ccw device
+ *
+ * Execute a SENSE ID channel program on @cdev to update its sense id
+ * information. When finished, call ccw_device_sense_id_done with a
+ * return code specifying the result.
+ */
+void ccw_device_sense_id_start(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+ struct ccw1 *cp = cdev->private->iccws;
+
+ CIO_TRACE_EVENT(4, "snsid");
+ CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+ /* Data setup. */
+ snsid_init(cdev);
+ /* Channel program setup. */
+ cp->cmd_code = CCW_CMD_SENSE_ID;
+ cp->cda = (u32) (addr_t) &cdev->private->senseid;
+ cp->count = sizeof(struct senseid);
+ cp->flags = CCW_FLAG_SLI;
+ /* Request setup. */
+ memset(req, 0, sizeof(*req));
+ req->cp = cp;
+ req->timeout = SENSE_ID_TIMEOUT;
+ req->maxretries = SENSE_ID_RETRIES;
+ req->lpm = sch->schib.pmcw.pam & sch->opm;
+ req->check = snsid_check;
+ req->callback = snsid_callback;
+ ccw_request_start(cdev);
}
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 2d0efee8a29..6da84543dfe 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -11,6 +11,7 @@
#include <linux/list.h>
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/completion.h>
#include <asm/ccwdev.h>
#include <asm/idals.h>
@@ -46,6 +47,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags)
cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0;
cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0;
cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0;
+ cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0;
return 0;
}
@@ -74,6 +76,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags)
cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0;
cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0;
cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0;
+ cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0;
return 0;
}
@@ -90,9 +93,34 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags)
cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0;
cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0;
cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0;
+ cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0;
}
/**
+ * ccw_device_is_pathgroup - determine if paths to this device are grouped
+ * @cdev: ccw device
+ *
+ * Return non-zero if there is a path group, zero otherwise.
+ */
+int ccw_device_is_pathgroup(struct ccw_device *cdev)
+{
+ return cdev->private->flags.pgroup;
+}
+EXPORT_SYMBOL(ccw_device_is_pathgroup);
+
+/**
+ * ccw_device_is_multipath - determine if device is operating in multipath mode
+ * @cdev: ccw device
+ *
+ * Return non-zero if device is operating in multipath mode, zero otherwise.
+ */
+int ccw_device_is_multipath(struct ccw_device *cdev)
+{
+ return cdev->private->flags.mpath;
+}
+EXPORT_SYMBOL(ccw_device_is_multipath);
+
+/**
* ccw_device_clear() - terminate I/O request processing
* @cdev: target ccw device
* @intparm: interruption parameter; value is only used if no I/O is
@@ -167,8 +195,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
- if (cdev->private->state == DEV_STATE_VERIFY ||
- cdev->private->state == DEV_STATE_CLEAR_VERIFY) {
+ if (cdev->private->state == DEV_STATE_VERIFY) {
/* Remember to fake irb when finished. */
if (!cdev->private->flags.fake_irb) {
cdev->private->flags.fake_irb = 1;
@@ -478,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
return sch->lpm;
}
-/*
- * Try to break the lock on a boxed device.
- */
-int
-ccw_device_stlck(struct ccw_device *cdev)
-{
- void *buf, *buf2;
- unsigned long flags;
- struct subchannel *sch;
- int ret;
+struct stlck_data {
+ struct completion done;
+ int rc;
+};
- if (!cdev)
- return -ENODEV;
+void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
+{
+ struct stlck_data *sdata = data;
- if (cdev->drv && !cdev->private->options.force)
- return -EINVAL;
+ sdata->rc = rc;
+ complete(&sdata->done);
+}
- sch = to_subchannel(cdev->dev.parent);
-
- CIO_TRACE_EVENT(2, "stl lock");
- CIO_TRACE_EVENT(2, dev_name(&cdev->dev));
+/*
+ * Perform unconditional reserve + release.
+ */
+int ccw_device_stlck(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct stlck_data data;
+ u8 *buffer;
+ int rc;
- buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
- if (!buf2) {
- kfree(buf);
- return -ENOMEM;
+ /* Check if steal lock operation is valid for this device. */
+ if (cdev->drv) {
+ if (!cdev->private->options.force)
+ return -EINVAL;
}
- spin_lock_irqsave(sch->lock, flags);
- ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
- if (ret)
- goto out_unlock;
- /*
- * Setup ccw. We chain an unconditional reserve and a release so we
- * only break the lock.
- */
- cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK;
- cdev->private->iccws[0].cda = (__u32) __pa(buf);
- cdev->private->iccws[0].count = 32;
- cdev->private->iccws[0].flags = CCW_FLAG_CC;
- cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE;
- cdev->private->iccws[1].cda = (__u32) __pa(buf2);
- cdev->private->iccws[1].count = 32;
- cdev->private->iccws[1].flags = 0;
- ret = cio_start(sch, cdev->private->iccws, 0);
- if (ret) {
- cio_disable_subchannel(sch); //FIXME: return code?
+ buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ init_completion(&data.done);
+ data.rc = -EIO;
+ spin_lock_irq(sch->lock);
+ rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
+ if (rc)
goto out_unlock;
+ /* Perform operation. */
+ cdev->private->state = DEV_STATE_STEAL_LOCK,
+ ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
+ spin_unlock_irq(sch->lock);
+ /* Wait for operation to finish. */
+ if (wait_for_completion_interruptible(&data.done)) {
+ /* Got a signal. */
+ spin_lock_irq(sch->lock);
+ ccw_request_cancel(cdev);
+ spin_unlock_irq(sch->lock);
+ wait_for_completion(&data.done);
}
- cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
- spin_unlock_irqrestore(sch->lock, flags);
- wait_event(cdev->private->wait_q,
- cdev->private->irb.scsw.cmd.actl == 0);
- spin_lock_irqsave(sch->lock, flags);
- cio_disable_subchannel(sch); //FIXME: return code?
- if ((cdev->private->irb.scsw.cmd.dstat !=
- (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
- (cdev->private->irb.scsw.cmd.cstat != 0))
- ret = -EIO;
- /* Clear irb. */
- memset(&cdev->private->irb, 0, sizeof(struct irb));
+ rc = data.rc;
+ /* Check results. */
+ spin_lock_irq(sch->lock);
+ cio_disable_subchannel(sch);
+ cdev->private->state = DEV_STATE_BOXED;
out_unlock:
- kfree(buf);
- kfree(buf2);
- spin_unlock_irqrestore(sch->lock, flags);
- return ret;
+ spin_unlock_irq(sch->lock);
+ kfree(buffer);
+
+ return rc;
}
void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index fc5ca1dd52b..aad188e43b4 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -1,594 +1,561 @@
/*
- * drivers/s390/cio/device_pgid.c
+ * CCW device PGID and path verification I/O handling.
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
- * Martin Schwidefsky (schwidefsky@de.ibm.com)
- *
- * Path Group ID functions.
+ * Copyright IBM Corp. 2002,2009
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
-#include <linux/module.h>
-#include <linux/init.h>
-
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
-#include <asm/delay.h>
-#include <asm/lowcore.h>
#include "cio.h"
#include "cio_debug.h"
-#include "css.h"
#include "device.h"
-#include "ioasm.h"
#include "io_sch.h"
+#define PGID_RETRIES 256
+#define PGID_TIMEOUT (10 * HZ)
+
/*
- * Helper function called from interrupt context to decide whether an
- * operation should be tried again.
+ * Process path verification data and report result.
*/
-static int __ccw_device_should_retry(union scsw *scsw)
+static void verify_done(struct ccw_device *cdev, int rc)
{
- /* CC is only valid if start function bit is set. */
- if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1)
- return 1;
- /* No more activity. For sense and set PGID we stubbornly try again. */
- if (!scsw->cmd.actl)
- return 1;
- return 0;
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_dev_id *id = &cdev->private->dev_id;
+ int mpath = cdev->private->flags.mpath;
+ int pgroup = cdev->private->flags.pgroup;
+
+ if (rc)
+ goto out;
+ /* Ensure consistent multipathing state at device and channel. */
+ if (sch->config.mp != mpath) {
+ sch->config.mp = mpath;
+ rc = cio_commit_config(sch);
+ }
+out:
+ CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d "
+ "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath,
+ sch->vpm);
+ ccw_device_verify_done(cdev, rc);
}
/*
- * Start Sense Path Group ID helper function. Used in ccw_device_recog
- * and ccw_device_sense_pgid.
+ * Create channel program to perform a NOOP.
*/
-static int
-__ccw_device_sense_pgid_start(struct ccw_device *cdev)
+static void nop_build_cp(struct ccw_device *cdev)
{
- struct subchannel *sch;
- struct ccw1 *ccw;
- int ret;
- int i;
-
- sch = to_subchannel(cdev->dev.parent);
- /* Return if we already checked on all paths. */
- if (cdev->private->imask == 0)
- return (sch->lpm == 0) ? -ENODEV : -EACCES;
- i = 8 - ffs(cdev->private->imask);
-
- /* Setup sense path group id channel program. */
- ccw = cdev->private->iccws;
- ccw->cmd_code = CCW_CMD_SENSE_PGID;
- ccw->count = sizeof (struct pgid);
- ccw->flags = CCW_FLAG_SLI;
-
- /* Reset device status. */
- memset(&cdev->private->irb, 0, sizeof(struct irb));
- /* Try on every path. */
- ret = -ENODEV;
- while (cdev->private->imask != 0) {
- /* Try every path multiple times. */
- ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
- if (cdev->private->iretry > 0) {
- cdev->private->iretry--;
- /* Reset internal retry indication. */
- cdev->private->flags.intretry = 0;
- ret = cio_start (sch, cdev->private->iccws,
- cdev->private->imask);
- /* ret is 0, -EBUSY, -EACCES or -ENODEV */
- if (ret != -EACCES)
- return ret;
- CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel "
- "0.%x.%04x, lpm %02X, became 'not "
- "operational'\n",
- cdev->private->dev_id.devno,
- sch->schid.ssid,
- sch->schid.sch_no, cdev->private->imask);
-
- }
- cdev->private->imask >>= 1;
- cdev->private->iretry = 5;
- i++;
- }
-
- return ret;
+ struct ccw_request *req = &cdev->private->req;
+ struct ccw1 *cp = cdev->private->iccws;
+
+ cp->cmd_code = CCW_CMD_NOOP;
+ cp->cda = 0;
+ cp->count = 0;
+ cp->flags = CCW_FLAG_SLI;
+ req->cp = cp;
}
-void
-ccw_device_sense_pgid_start(struct ccw_device *cdev)
+/*
+ * Perform NOOP on a single path.
+ */
+static void nop_do(struct ccw_device *cdev)
{
- int ret;
-
- /* Set a timeout of 60s */
- ccw_device_set_timeout(cdev, 60*HZ);
-
- cdev->private->state = DEV_STATE_SENSE_PGID;
- cdev->private->imask = 0x80;
- cdev->private->iretry = 5;
- memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
- ret = __ccw_device_sense_pgid_start(cdev);
- if (ret && ret != -EBUSY)
- ccw_device_sense_pgid_done(cdev, ret);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+
+ /* Adjust lpm. */
+ req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm);
+ if (!req->lpm)
+ goto out_nopath;
+ nop_build_cp(cdev);
+ ccw_request_start(cdev);
+ return;
+
+out_nopath:
+ verify_done(cdev, sch->vpm ? 0 : -EACCES);
}
/*
- * Called from interrupt context to check if a valid answer
- * to Sense Path Group ID was received.
+ * Adjust NOOP I/O status.
*/
-static int
-__ccw_device_check_sense_pgid(struct ccw_device *cdev)
+static enum io_status nop_filter(struct ccw_device *cdev, void *data,
+ struct irb *irb, enum io_status status)
{
- struct subchannel *sch;
- struct irb *irb;
- int i;
-
- sch = to_subchannel(cdev->dev.parent);
- irb = &cdev->private->irb;
- if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
- /* Retry Sense PGID if requested. */
- if (cdev->private->flags.intretry) {
- cdev->private->flags.intretry = 0;
- return -EAGAIN;
- }
- return -ETIME;
- }
- if (irb->esw.esw0.erw.cons &&
- (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
- /*
- * If the device doesn't support the Sense Path Group ID
- * command further retries wouldn't help ...
- */
- return -EOPNOTSUPP;
- }
- if (irb->esw.esw0.erw.cons) {
- CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
- "lpum %02X, cnt %02d, sns : "
- "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno,
- irb->esw.esw0.sublog.lpum,
- irb->esw.esw0.erw.scnt,
- irb->ecw[0], irb->ecw[1],
- irb->ecw[2], irb->ecw[3],
- irb->ecw[4], irb->ecw[5],
- irb->ecw[6], irb->ecw[7]);
- return -EAGAIN;
- }
- if (irb->scsw.cmd.cc == 3) {
- u8 lpm;
-
- lpm = to_io_private(sch)->orb.cmd.lpm;
- CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x,"
- " lpm %02X, became 'not operational'\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no, lpm);
- return -EACCES;
- }
- i = 8 - ffs(cdev->private->imask);
- if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
- CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
- "is reserved by someone else\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no);
- return -EUSERS;
- }
- return 0;
+ /* Only subchannel status might indicate a path error. */
+ if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0)
+ return IO_DONE;
+ return status;
}
/*
- * Got interrupt for Sense Path Group ID.
+ * Process NOOP request result for a single path.
*/
-void
-ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void nop_callback(struct ccw_device *cdev, void *data, int rc)
{
- struct subchannel *sch;
- struct irb *irb;
- int ret;
-
- irb = (struct irb *) __LC_IRB;
-
- if (irb->scsw.cmd.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (__ccw_device_should_retry(&irb->scsw)) {
- ret = __ccw_device_sense_pgid_start(cdev);
- if (ret && ret != -EBUSY)
- ccw_device_sense_pgid_done(cdev, ret);
- }
- return;
- }
- if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
- return;
- sch = to_subchannel(cdev->dev.parent);
- ret = __ccw_device_check_sense_pgid(cdev);
- memset(&cdev->private->irb, 0, sizeof(struct irb));
- switch (ret) {
- /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
- case -EOPNOTSUPP: /* Sense Path Group ID not supported */
- ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
- break;
- case -ETIME: /* Sense path group id stopped by timeout. */
- ccw_device_sense_pgid_done(cdev, -ETIME);
- break;
- case -EACCES: /* channel is not operational. */
- sch->lpm &= ~cdev->private->imask;
- /* Fall through. */
- case 0: /* Sense Path Group ID successful. */
- cdev->private->imask >>= 1;
- cdev->private->iretry = 5;
- /* Fall through. */
- case -EAGAIN: /* Try again. */
- ret = __ccw_device_sense_pgid_start(cdev);
- if (ret != 0 && ret != -EBUSY)
- ccw_device_sense_pgid_done(cdev, ret);
- break;
- case -EUSERS: /* device is reserved for someone else. */
- ccw_device_sense_pgid_done(cdev, -EUSERS);
- break;
- }
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+
+ if (rc == 0)
+ sch->vpm |= req->lpm;
+ else if (rc != -EACCES)
+ goto err;
+ req->lpm >>= 1;
+ nop_do(cdev);
+ return;
+
+err:
+ verify_done(cdev, rc);
}
/*
- * Path Group ID helper function.
+ * Create channel program to perform SET PGID on a single path.
*/
-static int
-__ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
+static void spid_build_cp(struct ccw_device *cdev, u8 fn)
{
- struct subchannel *sch;
- struct ccw1 *ccw;
- int ret;
-
- sch = to_subchannel(cdev->dev.parent);
-
- /* Setup sense path group id channel program. */
- cdev->private->pgid[0].inf.fc = func;
- ccw = cdev->private->iccws;
- if (cdev->private->flags.pgid_single)
- cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
- else
- cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
- ccw->cmd_code = CCW_CMD_SET_PGID;
- ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
- ccw->count = sizeof (struct pgid);
- ccw->flags = CCW_FLAG_SLI;
-
- /* Reset device status. */
- memset(&cdev->private->irb, 0, sizeof(struct irb));
-
- /* Try multiple times. */
- ret = -EACCES;
- if (cdev->private->iretry > 0) {
- cdev->private->iretry--;
- /* Reset internal retry indication. */
- cdev->private->flags.intretry = 0;
- ret = cio_start (sch, cdev->private->iccws,
- cdev->private->imask);
- /* We expect an interrupt in case of success or busy
- * indication. */
- if ((ret == 0) || (ret == -EBUSY))
- return ret;
- }
- /* PGID command failed on this path. */
- CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel "
- "0.%x.%04x, lpm %02X, became 'not operational'\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no, cdev->private->imask);
- return ret;
+ struct ccw_request *req = &cdev->private->req;
+ struct ccw1 *cp = cdev->private->iccws;
+ int i = 8 - ffs(req->lpm);
+ struct pgid *pgid = &cdev->private->pgid[i];
+
+ pgid->inf.fc = fn;
+ cp->cmd_code = CCW_CMD_SET_PGID;
+ cp->cda = (u32) (addr_t) pgid;
+ cp->count = sizeof(*pgid);
+ cp->flags = CCW_FLAG_SLI;
+ req->cp = cp;
}
/*
- * Helper function to send a nop ccw down a path.
+ * Perform establish/resign SET PGID on a single path.
*/
-static int __ccw_device_do_nop(struct ccw_device *cdev)
+static void spid_do(struct ccw_device *cdev)
{
- struct subchannel *sch;
- struct ccw1 *ccw;
- int ret;
-
- sch = to_subchannel(cdev->dev.parent);
-
- /* Setup nop channel program. */
- ccw = cdev->private->iccws;
- ccw->cmd_code = CCW_CMD_NOOP;
- ccw->cda = 0;
- ccw->count = 0;
- ccw->flags = CCW_FLAG_SLI;
-
- /* Reset device status. */
- memset(&cdev->private->irb, 0, sizeof(struct irb));
-
- /* Try multiple times. */
- ret = -EACCES;
- if (cdev->private->iretry > 0) {
- cdev->private->iretry--;
- /* Reset internal retry indication. */
- cdev->private->flags.intretry = 0;
- ret = cio_start (sch, cdev->private->iccws,
- cdev->private->imask);
- /* We expect an interrupt in case of success or busy
- * indication. */
- if ((ret == 0) || (ret == -EBUSY))
- return ret;
- }
- /* nop command failed on this path. */
- CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel "
- "0.%x.%04x, lpm %02X, became 'not operational'\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no, cdev->private->imask);
- return ret;
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+ u8 fn;
+
+ /* Use next available path that is not already in correct state. */
+ req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm);
+ if (!req->lpm)
+ goto out_nopath;
+ /* Channel program setup. */
+ if (req->lpm & sch->opm)
+ fn = SPID_FUNC_ESTABLISH;
+ else
+ fn = SPID_FUNC_RESIGN;
+ if (cdev->private->flags.mpath)
+ fn |= SPID_FUNC_MULTI_PATH;
+ spid_build_cp(cdev, fn);
+ ccw_request_start(cdev);
+ return;
+
+out_nopath:
+ verify_done(cdev, sch->vpm ? 0 : -EACCES);
}
+static void verify_start(struct ccw_device *cdev);
/*
- * Called from interrupt context to check if a valid answer
- * to Set Path Group ID was received.
+ * Process SET PGID request result for a single path.
*/
-static int
-__ccw_device_check_pgid(struct ccw_device *cdev)
+static void spid_callback(struct ccw_device *cdev, void *data, int rc)
{
- struct subchannel *sch;
- struct irb *irb;
-
- sch = to_subchannel(cdev->dev.parent);
- irb = &cdev->private->irb;
- if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
- /* Retry Set PGID if requested. */
- if (cdev->private->flags.intretry) {
- cdev->private->flags.intretry = 0;
- return -EAGAIN;
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+
+ switch (rc) {
+ case 0:
+ sch->vpm |= req->lpm & sch->opm;
+ break;
+ case -EACCES:
+ break;
+ case -EOPNOTSUPP:
+ if (cdev->private->flags.mpath) {
+ /* Try without multipathing. */
+ cdev->private->flags.mpath = 0;
+ goto out_restart;
}
- return -ETIME;
+ /* Try without pathgrouping. */
+ cdev->private->flags.pgroup = 0;
+ goto out_restart;
+ default:
+ goto err;
}
- if (irb->esw.esw0.erw.cons) {
- if (irb->ecw[0] & SNS0_CMD_REJECT)
- return -EOPNOTSUPP;
- /* Hmm, whatever happened, try again. */
- CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
- "cnt %02d, "
- "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno,
- irb->esw.esw0.erw.scnt,
- irb->ecw[0], irb->ecw[1],
- irb->ecw[2], irb->ecw[3],
- irb->ecw[4], irb->ecw[5],
- irb->ecw[6], irb->ecw[7]);
- return -EAGAIN;
- }
- if (irb->scsw.cmd.cc == 3) {
- CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x,"
- " lpm %02X, became 'not operational'\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no, cdev->private->imask);
- return -EACCES;
- }
- return 0;
+ req->lpm >>= 1;
+ spid_do(cdev);
+ return;
+
+out_restart:
+ verify_start(cdev);
+ return;
+err:
+ verify_done(cdev, rc);
+}
+
+static void spid_start(struct ccw_device *cdev)
+{
+ struct ccw_request *req = &cdev->private->req;
+
+ /* Initialize request data. */
+ memset(req, 0, sizeof(*req));
+ req->timeout = PGID_TIMEOUT;
+ req->maxretries = PGID_RETRIES;
+ req->lpm = 0x80;
+ req->callback = spid_callback;
+ spid_do(cdev);
+}
+
+static int pgid_cmp(struct pgid *p1, struct pgid *p2)
+{
+ return memcmp((char *) p1 + 1, (char *) p2 + 1,
+ sizeof(struct pgid) - 1);
}
/*
- * Called from interrupt context to check the path status after a nop has
- * been send.
+ * Determine pathgroup state from PGID data.
*/
-static int __ccw_device_check_nop(struct ccw_device *cdev)
+static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
+ int *mismatch, int *reserved, int *reset)
{
- struct subchannel *sch;
- struct irb *irb;
-
- sch = to_subchannel(cdev->dev.parent);
- irb = &cdev->private->irb;
- if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
- /* Retry NOP if requested. */
- if (cdev->private->flags.intretry) {
- cdev->private->flags.intretry = 0;
- return -EAGAIN;
+ struct pgid *pgid = &cdev->private->pgid[0];
+ struct pgid *first = NULL;
+ int lpm;
+ int i;
+
+ *mismatch = 0;
+ *reserved = 0;
+ *reset = 0;
+ for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) {
+ if ((cdev->private->pgid_valid_mask & lpm) == 0)
+ continue;
+ if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
+ *reserved = 1;
+ if (pgid->inf.ps.state1 == SNID_STATE1_RESET) {
+ /* A PGID was reset. */
+ *reset = 1;
+ continue;
}
- return -ETIME;
- }
- if (irb->scsw.cmd.cc == 3) {
- CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x,"
- " lpm %02X, became 'not operational'\n",
- cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no, cdev->private->imask);
- return -EACCES;
+ if (!first) {
+ first = pgid;
+ continue;
+ }
+ if (pgid_cmp(pgid, first) != 0)
+ *mismatch = 1;
}
- return 0;
+ if (!first)
+ first = &channel_subsystems[0]->global_pgid;
+ *p = first;
}
-static void
-__ccw_device_verify_start(struct ccw_device *cdev)
+static u8 pgid_to_vpm(struct ccw_device *cdev)
{
- struct subchannel *sch;
- __u8 func;
- int ret;
-
- sch = to_subchannel(cdev->dev.parent);
- /* Repeat for all paths. */
- for (; cdev->private->imask; cdev->private->imask >>= 1,
- cdev->private->iretry = 5) {
- if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
- /* Path not available, try next. */
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct pgid *pgid;
+ int i;
+ int lpm;
+ u8 vpm = 0;
+
+ /* Set VPM bits for paths which are already in the target state. */
+ for (i = 0; i < 8; i++) {
+ lpm = 0x80 >> i;
+ if ((cdev->private->pgid_valid_mask & lpm) == 0)
continue;
- if (cdev->private->options.pgroup) {
- if (sch->opm & cdev->private->imask)
- func = SPID_FUNC_ESTABLISH;
- else
- func = SPID_FUNC_RESIGN;
- ret = __ccw_device_do_pgid(cdev, func);
- } else
- ret = __ccw_device_do_nop(cdev);
- /* We expect an interrupt in case of success or busy
- * indication. */
- if (ret == 0 || ret == -EBUSY)
- return;
- /* Permanent path failure, try next. */
+ pgid = &cdev->private->pgid[i];
+ if (sch->opm & lpm) {
+ if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED)
+ continue;
+ } else {
+ if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED)
+ continue;
+ }
+ if (cdev->private->flags.mpath) {
+ if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH)
+ continue;
+ } else {
+ if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
+ continue;
+ }
+ vpm |= lpm;
}
- /* Done with all paths. */
- ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
+
+ return vpm;
}
-
-/*
- * Got interrupt for Set Path Group ID.
- */
-void
-ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
+
+static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
{
- struct subchannel *sch;
- struct irb *irb;
- int ret;
+ int i;
- irb = (struct irb *) __LC_IRB;
+ for (i = 0; i < 8; i++)
+ memcpy(&cdev->private->pgid[i], pgid, sizeof(struct pgid));
+}
- if (irb->scsw.cmd.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (__ccw_device_should_retry(&irb->scsw))
- __ccw_device_verify_start(cdev);
- return;
+/*
+ * Process SENSE PGID data and report result.
+ */
+static void snid_done(struct ccw_device *cdev, int rc)
+{
+ struct ccw_dev_id *id = &cdev->private->dev_id;
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct pgid *pgid;
+ int mismatch = 0;
+ int reserved = 0;
+ int reset = 0;
+
+ if (rc)
+ goto out;
+ pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset);
+ if (reserved)
+ rc = -EUSERS;
+ else if (mismatch)
+ rc = -EOPNOTSUPP;
+ else {
+ sch->vpm = pgid_to_vpm(cdev);
+ pgid_fill(cdev, pgid);
}
- if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
- return;
- sch = to_subchannel(cdev->dev.parent);
- if (cdev->private->options.pgroup)
- ret = __ccw_device_check_pgid(cdev);
- else
- ret = __ccw_device_check_nop(cdev);
- memset(&cdev->private->irb, 0, sizeof(struct irb));
-
- switch (ret) {
- /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
+out:
+ CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
+ "mism=%d rsvd=%d reset=%d\n", id->ssid, id->devno, rc,
+ cdev->private->pgid_valid_mask, sch->vpm, mismatch,
+ reserved, reset);
+ switch (rc) {
case 0:
- /* Path verification ccw finished successfully, update lpm. */
- sch->vpm |= sch->opm & cdev->private->imask;
- /* Go on with next path. */
- cdev->private->imask >>= 1;
- cdev->private->iretry = 5;
- __ccw_device_verify_start(cdev);
+ /* Anything left to do? */
+ if (sch->vpm == sch->schib.pmcw.pam) {
+ verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
+ return;
+ }
+ /* Perform path-grouping. */
+ spid_start(cdev);
break;
case -EOPNOTSUPP:
- /*
- * One of those strange devices which claim to be able
- * to do multipathing but not for Set Path Group ID.
- */
- if (cdev->private->flags.pgid_single)
- cdev->private->options.pgroup = 0;
- else
- cdev->private->flags.pgid_single = 1;
- /* Retry */
- sch->vpm = 0;
- cdev->private->imask = 0x80;
- cdev->private->iretry = 5;
- /* fall through. */
- case -EAGAIN: /* Try again. */
- __ccw_device_verify_start(cdev);
- break;
- case -ETIME: /* Set path group id stopped by timeout. */
- ccw_device_verify_done(cdev, -ETIME);
- break;
- case -EACCES: /* channel is not operational. */
- cdev->private->imask >>= 1;
- cdev->private->iretry = 5;
- __ccw_device_verify_start(cdev);
+ /* Path-grouping not supported. */
+ cdev->private->flags.pgroup = 0;
+ cdev->private->flags.mpath = 0;
+ verify_start(cdev);
break;
+ default:
+ verify_done(cdev, rc);
}
}
-void
-ccw_device_verify_start(struct ccw_device *cdev)
+/*
+ * Create channel program to perform a SENSE PGID on a single path.
+ */
+static void snid_build_cp(struct ccw_device *cdev)
+{
+ struct ccw_request *req = &cdev->private->req;
+ struct ccw1 *cp = cdev->private->iccws;
+ int i = 8 - ffs(req->lpm);
+
+ /* Channel program setup. */
+ cp->cmd_code = CCW_CMD_SENSE_PGID;
+ cp->cda = (u32) (addr_t) &cdev->private->pgid[i];
+ cp->count = sizeof(struct pgid);
+ cp->flags = CCW_FLAG_SLI;
+ req->cp = cp;
+}
+
+/*
+ * Perform SENSE PGID on a single path.
+ */
+static void snid_do(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+
+ /* Adjust lpm if paths are not set in pam. */
+ req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam);
+ if (!req->lpm)
+ goto out_nopath;
+ snid_build_cp(cdev);
+ ccw_request_start(cdev);
+ return;
+
+out_nopath:
+ snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES);
+}
- cdev->private->flags.pgid_single = 0;
- cdev->private->imask = 0x80;
- cdev->private->iretry = 5;
+/*
+ * Process SENSE PGID request result for single path.
+ */
+static void snid_callback(struct ccw_device *cdev, void *data, int rc)
+{
+ struct ccw_request *req = &cdev->private->req;
+
+ if (rc == 0)
+ cdev->private->pgid_valid_mask |= req->lpm;
+ else if (rc != -EACCES)
+ goto err;
+ req->lpm >>= 1;
+ snid_do(cdev);
+ return;
+
+err:
+ snid_done(cdev, rc);
+}
- /* Start with empty vpm. */
- sch->vpm = 0;
+/*
+ * Perform path verification.
+ */
+static void verify_start(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+ struct ccw_dev_id *devid = &cdev->private->dev_id;
- /* Get current pam. */
- if (cio_update_schib(sch)) {
- ccw_device_verify_done(cdev, -ENODEV);
- return;
+ sch->vpm = 0;
+ /* Initialize request data. */
+ memset(req, 0, sizeof(*req));
+ req->timeout = PGID_TIMEOUT;
+ req->maxretries = PGID_RETRIES;
+ req->lpm = 0x80;
+ if (cdev->private->flags.pgroup) {
+ CIO_TRACE_EVENT(4, "snid");
+ CIO_HEX_EVENT(4, devid, sizeof(*devid));
+ req->callback = snid_callback;
+ snid_do(cdev);
+ } else {
+ CIO_TRACE_EVENT(4, "nop");
+ CIO_HEX_EVENT(4, devid, sizeof(*devid));
+ req->filter = nop_filter;
+ req->callback = nop_callback;
+ nop_do(cdev);
}
- /* After 60s path verification is considered to have failed. */
- ccw_device_set_timeout(cdev, 60*HZ);
- __ccw_device_verify_start(cdev);
}
-static void
-__ccw_device_disband_start(struct ccw_device *cdev)
+/**
+ * ccw_device_verify_start - perform path verification
+ * @cdev: ccw device
+ *
+ * Perform an I/O on each available channel path to @cdev to determine which
+ * paths are operational. The resulting path mask is stored in sch->vpm.
+ * If device options specify pathgrouping, establish a pathgroup for the
+ * operational paths. When finished, call ccw_device_verify_done with a
+ * return code specifying the result.
+ */
+void ccw_device_verify_start(struct ccw_device *cdev)
{
- struct subchannel *sch;
- int ret;
-
- sch = to_subchannel(cdev->dev.parent);
- while (cdev->private->imask != 0) {
- if (sch->lpm & cdev->private->imask) {
- ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
- if (ret == 0)
- return;
- }
- cdev->private->iretry = 5;
- cdev->private->imask >>= 1;
- }
- ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
+ CIO_TRACE_EVENT(4, "vrfy");
+ CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+ /* Initialize PGID data. */
+ memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid));
+ cdev->private->pgid_valid_mask = 0;
+ /*
+ * Initialize pathgroup and multipath state with target values.
+ * They may change in the course of path verification.
+ */
+ cdev->private->flags.pgroup = cdev->private->options.pgroup;
+ cdev->private->flags.mpath = cdev->private->options.mpath;
+ cdev->private->flags.doverify = 0;
+ verify_start(cdev);
}
/*
- * Got interrupt for Unset Path Group ID.
+ * Process disband SET PGID request result.
*/
-void
-ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void disband_callback(struct ccw_device *cdev, void *data, int rc)
{
- struct subchannel *sch;
- struct irb *irb;
- int ret;
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_dev_id *id = &cdev->private->dev_id;
+
+ if (rc)
+ goto out;
+ /* Ensure consistent multipathing state at device and channel. */
+ cdev->private->flags.mpath = 0;
+ if (sch->config.mp) {
+ sch->config.mp = 0;
+ rc = cio_commit_config(sch);
+ }
+out:
+ CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno,
+ rc);
+ ccw_device_disband_done(cdev, rc);
+}
- irb = (struct irb *) __LC_IRB;
+/**
+ * ccw_device_disband_start - disband pathgroup
+ * @cdev: ccw device
+ *
+ * Execute a SET PGID channel program on @cdev to disband a previously
+ * established pathgroup. When finished, call ccw_device_disband_done with
+ * a return code specifying the result.
+ */
+void ccw_device_disband_start(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+ u8 fn;
+
+ CIO_TRACE_EVENT(4, "disb");
+ CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+ /* Request setup. */
+ memset(req, 0, sizeof(*req));
+ req->timeout = PGID_TIMEOUT;
+ req->maxretries = PGID_RETRIES;
+ req->lpm = sch->schib.pmcw.pam & sch->opm;
+ req->callback = disband_callback;
+ fn = SPID_FUNC_DISBAND;
+ if (cdev->private->flags.mpath)
+ fn |= SPID_FUNC_MULTI_PATH;
+ spid_build_cp(cdev, fn);
+ ccw_request_start(cdev);
+}
- if (irb->scsw.cmd.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (__ccw_device_should_retry(&irb->scsw))
- __ccw_device_disband_start(cdev);
- return;
- }
- if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
- return;
- sch = to_subchannel(cdev->dev.parent);
- ret = __ccw_device_check_pgid(cdev);
- memset(&cdev->private->irb, 0, sizeof(struct irb));
- switch (ret) {
- /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
- case 0: /* disband successful. */
- ccw_device_disband_done(cdev, ret);
- break;
- case -EOPNOTSUPP:
- /*
- * One of those strange devices which claim to be able
- * to do multipathing but not for Unset Path Group ID.
- */
- cdev->private->flags.pgid_single = 1;
- /* fall through. */
- case -EAGAIN: /* Try again. */
- __ccw_device_disband_start(cdev);
- break;
- case -ETIME: /* Set path group id stopped by timeout. */
- ccw_device_disband_done(cdev, -ETIME);
- break;
- case -EACCES: /* channel is not operational. */
- cdev->private->imask >>= 1;
- cdev->private->iretry = 5;
- __ccw_device_disband_start(cdev);
- break;
- }
+static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
+{
+ struct ccw_request *req = &cdev->private->req;
+ struct ccw1 *cp = cdev->private->iccws;
+
+ cp[0].cmd_code = CCW_CMD_STLCK;
+ cp[0].cda = (u32) (addr_t) buf1;
+ cp[0].count = 32;
+ cp[0].flags = CCW_FLAG_CC;
+ cp[1].cmd_code = CCW_CMD_RELEASE;
+ cp[1].cda = (u32) (addr_t) buf2;
+ cp[1].count = 32;
+ cp[1].flags = 0;
+ req->cp = cp;
}
-void
-ccw_device_disband_start(struct ccw_device *cdev)
+static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
{
- /* After 60s disbanding is considered to have failed. */
- ccw_device_set_timeout(cdev, 60*HZ);
+ ccw_device_stlck_done(cdev, data, rc);
+}
- cdev->private->flags.pgid_single = 0;
- cdev->private->iretry = 5;
- cdev->private->imask = 0x80;
- __ccw_device_disband_start(cdev);
+/**
+ * ccw_device_stlck_start - perform unconditional release
+ * @cdev: ccw device
+ * @data: data pointer to be passed to ccw_device_stlck_done
+ * @buf1: data pointer used in channel program
+ * @buf2: data pointer used in channel program
+ *
+ * Execute a channel program on @cdev to release an existing PGID reservation.
+ * When finished, call ccw_device_stlck_done with a return code specifying the
+ * result.
+ */
+void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
+ void *buf2)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct ccw_request *req = &cdev->private->req;
+
+ CIO_TRACE_EVENT(4, "stlck");
+ CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+ /* Request setup. */
+ memset(req, 0, sizeof(*req));
+ req->timeout = PGID_TIMEOUT;
+ req->maxretries = PGID_RETRIES;
+ req->lpm = sch->schib.pmcw.pam & sch->opm;
+ req->data = data;
+ req->callback = stlck_callback;
+ stlck_build_cp(cdev, buf1, buf2);
+ ccw_request_start(cdev);
}
+
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 5814dbee241..66d8066ef22 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -336,9 +336,6 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
sense_ccw->count = SENSE_MAX_COUNT;
sense_ccw->flags = CCW_FLAG_SLI;
- /* Reset internal retry indication. */
- cdev->private->flags.intretry = 0;
-
rc = cio_start(sch, sense_ccw, 0xff);
if (rc == -ENODEV || rc == -EACCES)
dev_fsm_event(cdev, DEV_EVENT_VERIFY);
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 0b8f381bd20..d72ae4c93af 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -1,7 +1,10 @@
#ifndef S390_IO_SCH_H
#define S390_IO_SCH_H
+#include <linux/types.h>
#include <asm/schid.h>
+#include <asm/ccwdev.h>
+#include "css.h"
/*
* command-mode operation request block
@@ -68,6 +71,52 @@ struct io_subchannel_private {
#define MAX_CIWS 8
/*
+ * Possible status values for a CCW request's I/O.
+ */
+enum io_status {
+ IO_DONE,
+ IO_RUNNING,
+ IO_STATUS_ERROR,
+ IO_PATH_ERROR,
+ IO_REJECTED,
+ IO_KILLED
+};
+
+/**
+ * ccw_request - Internal CCW request.
+ * @cp: channel program to start
+ * @timeout: maximum allowable time in jiffies between start I/O and interrupt
+ * @maxretries: number of retries per I/O operation and path
+ * @lpm: mask of paths to use
+ * @check: optional callback that determines if results are final
+ * @filter: optional callback to adjust request status based on IRB data
+ * @callback: final callback
+ * @data: user-defined pointer passed to all callbacks
+ * @mask: current path mask
+ * @retries: current number of retries
+ * @drc: delayed return code
+ * @cancel: non-zero if request was cancelled
+ * @done: non-zero if request was finished
+ */
+struct ccw_request {
+ struct ccw1 *cp;
+ unsigned long timeout;
+ u16 maxretries;
+ u8 lpm;
+ int (*check)(struct ccw_device *, void *);
+ enum io_status (*filter)(struct ccw_device *, void *, struct irb *,
+ enum io_status);
+ void (*callback)(struct ccw_device *, void *, int);
+ void *data;
+ /* These fields are used internally. */
+ u16 mask;
+ u16 retries;
+ int drc;
+ int cancel:1;
+ int done:1;
+} __attribute__((packed));
+
+/*
* sense-id response buffer layout
*/
struct senseid {
@@ -82,32 +131,43 @@ struct senseid {
struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */
} __attribute__ ((packed, aligned(4)));
+enum cdev_todo {
+ CDEV_TODO_NOTHING,
+ CDEV_TODO_ENABLE_CMF,
+ CDEV_TODO_REBIND,
+ CDEV_TODO_REGISTER,
+ CDEV_TODO_UNREG,
+ CDEV_TODO_UNREG_EVAL,
+};
+
struct ccw_device_private {
struct ccw_device *cdev;
struct subchannel *sch;
int state; /* device state */
atomic_t onoff;
- unsigned long registered;
struct ccw_dev_id dev_id; /* device id */
struct subchannel_id schid; /* subchannel number */
- u8 imask; /* lpm mask for SNID/SID/SPGID */
- int iretry; /* retry counter SNID/SID/SPGID */
+ struct ccw_request req; /* internal I/O request */
+ int iretry;
+ u8 pgid_valid_mask; /* mask of valid PGIDs */
struct {
unsigned int fast:1; /* post with "channel end" */
unsigned int repall:1; /* report every interrupt status */
unsigned int pgroup:1; /* do path grouping */
unsigned int force:1; /* allow forced online */
+ unsigned int mpath:1; /* do multipathing */
} __attribute__ ((packed)) options;
struct {
- unsigned int pgid_single:1; /* use single path for Set PGID */
unsigned int esid:1; /* Ext. SenseID supported by HW */
unsigned int dosense:1; /* delayed SENSE required */
unsigned int doverify:1; /* delayed path verification */
unsigned int donotify:1; /* call notify function */
unsigned int recog_done:1; /* dev. recog. complete */
unsigned int fake_irb:1; /* deliver faked irb */
- unsigned int intretry:1; /* retry internal operation */
unsigned int resuming:1; /* recognition while resume */
+ unsigned int pgroup:1; /* pathgroup is set up */
+ unsigned int mpath:1; /* multipathing is set up */
+ unsigned int initialized:1; /* set if initial reference held */
} __attribute__((packed)) flags;
unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data;
@@ -115,7 +175,8 @@ struct ccw_device_private {
struct senseid senseid; /* SenseID info */
struct pgid pgid[8]; /* path group IDs per chpid*/
struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */
- struct work_struct kick_work;
+ struct work_struct todo_work;
+ enum cdev_todo todo;
wait_queue_head_t wait_q;
struct timer_list timer;
void *cmb; /* measurement information */
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 1294876bf7b..20836eff88c 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -102,6 +102,7 @@ static atomic_t ap_poll_requests = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
static struct task_struct *ap_poll_kthread = NULL;
static DEFINE_MUTEX(ap_poll_thread_mutex);
+static DEFINE_SPINLOCK(ap_poll_timer_lock);
static void *ap_interrupt_indicator;
static struct hrtimer ap_poll_timer;
/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
@@ -282,6 +283,7 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
* @psmid: The program supplied message identifier
* @msg: The message text
* @length: The message length
+ * @special: Special Bit
*
* Returns AP queue status structure.
* Condition code 1 on NQAP can't happen because the L bit is 1.
@@ -289,7 +291,8 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
* because a segment boundary was reached. The NQAP is repeated.
*/
static inline struct ap_queue_status
-__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
+__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length,
+ unsigned int special)
{
typedef struct { char _[length]; } msgblock;
register unsigned long reg0 asm ("0") = qid | 0x40000000UL;
@@ -299,6 +302,9 @@ __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);
register unsigned long reg5 asm ("5") = (unsigned int) psmid;
+ if (special == 1)
+ reg0 |= 0x400000UL;
+
asm volatile (
"0: .long 0xb2ad0042\n" /* DQAP */
" brc 2,0b"
@@ -312,13 +318,15 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
{
struct ap_queue_status status;
- status = __ap_send(qid, psmid, msg, length);
+ status = __ap_send(qid, psmid, msg, length, 0);
switch (status.response_code) {
case AP_RESPONSE_NORMAL:
return 0;
case AP_RESPONSE_Q_FULL:
case AP_RESPONSE_RESET_IN_PROGRESS:
return -EBUSY;
+ case AP_RESPONSE_REQ_FAC_NOT_INST:
+ return -EINVAL;
default: /* Device is gone. */
return -ENODEV;
}
@@ -1008,7 +1016,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev)
}
status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
- msg, sizeof(msg));
+ msg, sizeof(msg), 0);
if (status.response_code != AP_RESPONSE_NORMAL) {
rc = -ENODEV;
goto out_free;
@@ -1163,16 +1171,19 @@ ap_config_timeout(unsigned long ptr)
static inline void ap_schedule_poll_timer(void)
{
ktime_t hr_time;
+
+ spin_lock_bh(&ap_poll_timer_lock);
if (ap_using_interrupts() || ap_suspend_flag)
- return;
+ goto out;
if (hrtimer_is_queued(&ap_poll_timer))
- return;
+ goto out;
if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) {
hr_time = ktime_set(0, poll_timeout);
hrtimer_forward_now(&ap_poll_timer, hr_time);
hrtimer_restart(&ap_poll_timer);
}
- return;
+out:
+ spin_unlock_bh(&ap_poll_timer_lock);
}
/**
@@ -1243,7 +1254,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
/* Start the next request on the queue. */
ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
status = __ap_send(ap_dev->qid, ap_msg->psmid,
- ap_msg->message, ap_msg->length);
+ ap_msg->message, ap_msg->length, ap_msg->special);
switch (status.response_code) {
case AP_RESPONSE_NORMAL:
atomic_inc(&ap_poll_requests);
@@ -1261,6 +1272,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
*flags |= 2;
break;
case AP_RESPONSE_MESSAGE_TOO_BIG:
+ case AP_RESPONSE_REQ_FAC_NOT_INST:
return -EINVAL;
default:
return -ENODEV;
@@ -1302,7 +1314,8 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
if (list_empty(&ap_dev->requestq) &&
ap_dev->queue_count < ap_dev->queue_depth) {
status = __ap_send(ap_dev->qid, ap_msg->psmid,
- ap_msg->message, ap_msg->length);
+ ap_msg->message, ap_msg->length,
+ ap_msg->special);
switch (status.response_code) {
case AP_RESPONSE_NORMAL:
list_add_tail(&ap_msg->list, &ap_dev->pendingq);
@@ -1317,6 +1330,7 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
ap_dev->requestq_count++;
ap_dev->total_request_count++;
return -EBUSY;
+ case AP_RESPONSE_REQ_FAC_NOT_INST:
case AP_RESPONSE_MESSAGE_TOO_BIG:
ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
return -EINVAL;
@@ -1658,6 +1672,7 @@ int __init ap_module_init(void)
*/
if (MACHINE_IS_VM)
poll_timeout = 1500000;
+ spin_lock_init(&ap_poll_timer_lock);
hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
ap_poll_timer.function = ap_poll_timeout;
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index a3536224180..4785d07cd44 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -87,6 +87,7 @@ struct ap_queue_status {
#define AP_RESPONSE_INDEX_TOO_BIG 0x11
#define AP_RESPONSE_NO_FIRST_PART 0x13
#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15
+#define AP_RESPONSE_REQ_FAC_NOT_INST 0x16
/*
* Known device types
@@ -96,8 +97,8 @@ struct ap_queue_status {
#define AP_DEVICE_TYPE_PCIXCC 5
#define AP_DEVICE_TYPE_CEX2A 6
#define AP_DEVICE_TYPE_CEX2C 7
-#define AP_DEVICE_TYPE_CEX2A2 8
-#define AP_DEVICE_TYPE_CEX2C2 9
+#define AP_DEVICE_TYPE_CEX3A 8
+#define AP_DEVICE_TYPE_CEX3C 9
/*
* AP reset flag states
@@ -161,12 +162,25 @@ struct ap_message {
size_t length; /* Message length. */
void *private; /* ap driver private pointer. */
+ unsigned int special:1; /* Used for special commands. */
};
#define AP_DEVICE(dt) \
.dev_type=(dt), \
.match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
+/**
+ * ap_init_message() - Initialize ap_message.
+ * Initialize a message before using. Otherwise this might result in
+ * unexpected behaviour.
+ */
+static inline void ap_init_message(struct ap_message *ap_msg)
+{
+ ap_msg->psmid = 0;
+ ap_msg->length = 0;
+ ap_msg->special = 0;
+}
+
/*
* Note: don't use ap_send/ap_recv after using ap_queue_message
* for the first time. Otherwise the ap message queue will get
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 65b6a96afe6..0d4d18bdd45 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -299,9 +299,7 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf,
*/
static int zcrypt_open(struct inode *inode, struct file *filp)
{
- lock_kernel();
atomic_inc(&zcrypt_open_count);
- unlock_kernel();
return 0;
}
@@ -1009,6 +1007,10 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
zcrypt_count_type(ZCRYPT_CEX2C));
len += sprintf(resp_buff + len, "CEX2A count: %d\n",
zcrypt_count_type(ZCRYPT_CEX2A));
+ len += sprintf(resp_buff + len, "CEX3C count: %d\n",
+ zcrypt_count_type(ZCRYPT_CEX3C));
+ len += sprintf(resp_buff + len, "CEX3A count: %d\n",
+ zcrypt_count_type(ZCRYPT_CEX3A));
len += sprintf(resp_buff + len, "requestq count: %d\n",
zcrypt_requestq_count());
len += sprintf(resp_buff + len, "pendingq count: %d\n",
@@ -1017,7 +1019,7 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
atomic_read(&zcrypt_open_count));
zcrypt_status_mask(workarea);
len += sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
- "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A",
+ "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A 7=CEX3C 8=CEX3A",
resp_buff+len, workarea, AP_DEVICES);
zcrypt_qdepth_mask(workarea);
len += sprinthx("Waiting work element counts",
@@ -1095,8 +1097,9 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer,
* '0' for no device, '1' for PCICA, '2' for PCICC,
* '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3,
* '5' for CEX2C and '6' for CEX2A'
+ * '7' for CEX3C and '8' for CEX3A
*/
- if (*ptr >= '0' && *ptr <= '6')
+ if (*ptr >= '0' && *ptr <= '8')
j++;
else if (*ptr == 'd' || *ptr == 'D')
zcrypt_disable_card(j++);
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 1d1ec74dadb..8e7ffbf2466 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -71,6 +71,8 @@ struct ica_z90_status {
#define ZCRYPT_PCIXCC_MCL3 4
#define ZCRYPT_CEX2C 5
#define ZCRYPT_CEX2A 6
+#define ZCRYPT_CEX3C 7
+#define ZCRYPT_CEX3A 8
/**
* Large random numbers are pulled in 4096 byte chunks from the crypto cards
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 326ea08f67c..c6fb0aa8950 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -39,17 +39,24 @@
#define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */
#define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */
+#define CEX3A_MIN_MOD_SIZE CEX2A_MIN_MOD_SIZE
+#define CEX3A_MAX_MOD_SIZE CEX2A_MAX_MOD_SIZE
#define CEX2A_SPEED_RATING 970
+#define CEX3A_SPEED_RATING 900 /* Fixme: Needs finetuning */
#define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */
#define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
+#define CEX3A_MAX_MESSAGE_SIZE CEX2A_MAX_MESSAGE_SIZE
+#define CEX3A_MAX_RESPONSE_SIZE CEX2A_MAX_RESPONSE_SIZE
+
#define CEX2A_CLEANUP_TIME (15*HZ)
+#define CEX3A_CLEANUP_TIME CEX2A_CLEANUP_TIME
static struct ap_device_id zcrypt_cex2a_ids[] = {
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2A) },
- { AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) },
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX3A) },
{ /* end of list */ },
};
@@ -298,6 +305,7 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
struct completion work;
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -335,6 +343,7 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
struct completion work;
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -373,31 +382,45 @@ static struct zcrypt_ops zcrypt_cex2a_ops = {
*/
static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
{
- struct zcrypt_device *zdev;
- int rc;
-
- zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
- if (!zdev)
- return -ENOMEM;
- zdev->ap_dev = ap_dev;
- zdev->ops = &zcrypt_cex2a_ops;
- zdev->online = 1;
- zdev->user_space_type = ZCRYPT_CEX2A;
- zdev->type_string = "CEX2A";
- zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
- zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
- zdev->short_crt = 1;
- zdev->speed_rating = CEX2A_SPEED_RATING;
- ap_dev->reply = &zdev->reply;
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
- if (rc)
- goto out_free;
- return 0;
-
-out_free:
- ap_dev->private = NULL;
- zcrypt_device_free(zdev);
+ struct zcrypt_device *zdev = NULL;
+ int rc = 0;
+
+ switch (ap_dev->device_type) {
+ case AP_DEVICE_TYPE_CEX2A:
+ zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->user_space_type = ZCRYPT_CEX2A;
+ zdev->type_string = "CEX2A";
+ zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
+ zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
+ zdev->short_crt = 1;
+ zdev->speed_rating = CEX2A_SPEED_RATING;
+ break;
+ case AP_DEVICE_TYPE_CEX3A:
+ zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->user_space_type = ZCRYPT_CEX3A;
+ zdev->type_string = "CEX3A";
+ zdev->min_mod_size = CEX3A_MIN_MOD_SIZE;
+ zdev->max_mod_size = CEX3A_MAX_MOD_SIZE;
+ zdev->short_crt = 1;
+ zdev->speed_rating = CEX3A_SPEED_RATING;
+ break;
+ }
+ if (zdev != NULL) {
+ zdev->ap_dev = ap_dev;
+ zdev->ops = &zcrypt_cex2a_ops;
+ zdev->online = 1;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
+ }
+ if (rc) {
+ ap_dev->private = NULL;
+ zcrypt_device_free(zdev);
+ }
return rc;
}
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index 17ba81b58c7..e78df3671ca 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -281,6 +281,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
struct completion work;
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -318,6 +319,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
struct completion work;
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
index f4b0c479543..a23726a0735 100644
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -483,6 +483,7 @@ static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev,
struct completion work;
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -521,6 +522,7 @@ static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev,
struct completion work;
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index 5677b40e4ac..79c120578e6 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -43,10 +43,13 @@
#define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */
#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */
#define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */
+#define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE
+#define CEX3C_MAX_MOD_SIZE PCIXCC_MAX_MOD_SIZE
-#define PCIXCC_MCL2_SPEED_RATING 7870 /* FIXME: needs finetuning */
+#define PCIXCC_MCL2_SPEED_RATING 7870
#define PCIXCC_MCL3_SPEED_RATING 7870
-#define CEX2C_SPEED_RATING 8540
+#define CEX2C_SPEED_RATING 7000
+#define CEX3C_SPEED_RATING 6500 /* FIXME: needs finetuning */
#define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */
#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */
@@ -72,7 +75,7 @@ struct response_type {
static struct ap_device_id zcrypt_pcixcc_ids[] = {
{ AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) },
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2C) },
- { AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) },
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX3C) },
{ /* end of list */ },
};
@@ -326,6 +329,11 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
+ if (memcmp(function_code, "US", 2) == 0)
+ ap_msg->special = 1;
+ else
+ ap_msg->special = 0;
+
/* copy data block */
if (xcRB->request_data_length &&
copy_from_user(req_data, xcRB->request_data_address,
@@ -688,6 +696,7 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev,
};
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -727,6 +736,7 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev,
};
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -766,6 +776,7 @@ static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev,
};
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -805,6 +816,7 @@ static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev,
};
int rc;
+ ap_init_message(&ap_msg);
ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -972,6 +984,7 @@ static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev)
} __attribute__((packed)) *reply;
int rc, i;
+ ap_init_message(&ap_msg);
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
if (!ap_msg.message)
return -ENOMEM;
@@ -1016,14 +1029,15 @@ out_free:
static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
{
struct zcrypt_device *zdev;
- int rc;
+ int rc = 0;
zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE);
if (!zdev)
return -ENOMEM;
zdev->ap_dev = ap_dev;
zdev->online = 1;
- if (ap_dev->device_type == AP_DEVICE_TYPE_PCIXCC) {
+ switch (ap_dev->device_type) {
+ case AP_DEVICE_TYPE_PCIXCC:
rc = zcrypt_pcixcc_mcl(ap_dev);
if (rc < 0) {
zcrypt_device_free(zdev);
@@ -1041,13 +1055,25 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
}
- } else {
+ break;
+ case AP_DEVICE_TYPE_CEX2C:
zdev->user_space_type = ZCRYPT_CEX2C;
zdev->type_string = "CEX2C";
zdev->speed_rating = CEX2C_SPEED_RATING;
zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
+ break;
+ case AP_DEVICE_TYPE_CEX3C:
+ zdev->user_space_type = ZCRYPT_CEX3C;
+ zdev->type_string = "CEX3C";
+ zdev->speed_rating = CEX3C_SPEED_RATING;
+ zdev->min_mod_size = CEX3C_MIN_MOD_SIZE;
+ zdev->max_mod_size = CEX3C_MAX_MOD_SIZE;
+ break;
+ default:
+ goto out_free;
}
+
rc = zcrypt_pcixcc_rng_supported(ap_dev);
if (rc < 0) {
zcrypt_device_free(zdev);
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile
index 96eddb3b1d0..6cab5a62f99 100644
--- a/drivers/s390/net/Makefile
+++ b/drivers/s390/net/Makefile
@@ -3,11 +3,11 @@
#
ctcm-y += ctcm_main.o ctcm_fsms.o ctcm_mpc.o ctcm_sysfs.o ctcm_dbug.o
-obj-$(CONFIG_CTCM) += ctcm.o fsm.o cu3088.o
+obj-$(CONFIG_CTCM) += ctcm.o fsm.o
obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
-obj-$(CONFIG_LCS) += lcs.o cu3088.o
-obj-$(CONFIG_CLAW) += claw.o cu3088.o
+obj-$(CONFIG_LCS) += lcs.o
+obj-$(CONFIG_CLAW) += claw.o
qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o
obj-$(CONFIG_QETH) += qeth.o
qeth_l2-y += qeth_l2_main.o
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index c63babefb69..3c77bfe0764 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -90,7 +90,6 @@
#include <linux/timer.h>
#include <linux/types.h>
-#include "cu3088.h"
#include "claw.h"
/*
@@ -258,6 +257,9 @@ static int claw_pm_prepare(struct ccwgroup_device *gdev)
return -EPERM;
}
+/* the root device for claw group devices */
+static struct device *claw_root_dev;
+
/* ccwgroup table */
static struct ccwgroup_driver claw_group_driver = {
@@ -272,6 +274,47 @@ static struct ccwgroup_driver claw_group_driver = {
.prepare = claw_pm_prepare,
};
+static struct ccw_device_id claw_ids[] = {
+ {CCW_DEVICE(0x3088, 0x61), .driver_info = claw_channel_type_claw},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, claw_ids);
+
+static struct ccw_driver claw_ccw_driver = {
+ .owner = THIS_MODULE,
+ .name = "claw",
+ .ids = claw_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+};
+
+static ssize_t
+claw_driver_group_store(struct device_driver *ddrv, const char *buf,
+ size_t count)
+{
+ int err;
+ err = ccwgroup_create_from_string(claw_root_dev,
+ claw_group_driver.driver_id,
+ &claw_ccw_driver, 3, buf);
+ return err ? err : count;
+}
+
+static DRIVER_ATTR(group, 0200, NULL, claw_driver_group_store);
+
+static struct attribute *claw_group_attrs[] = {
+ &driver_attr_group.attr,
+ NULL,
+};
+
+static struct attribute_group claw_group_attr_group = {
+ .attrs = claw_group_attrs,
+};
+
+static const struct attribute_group *claw_group_attr_groups[] = {
+ &claw_group_attr_group,
+ NULL,
+};
+
/*
* Key functions
*/
@@ -3326,7 +3369,11 @@ claw_remove_files(struct device *dev)
static void __exit
claw_cleanup(void)
{
- unregister_cu3088_discipline(&claw_group_driver);
+ driver_remove_file(&claw_group_driver.driver,
+ &driver_attr_group);
+ ccwgroup_driver_unregister(&claw_group_driver);
+ ccw_driver_unregister(&claw_ccw_driver);
+ root_device_unregister(claw_root_dev);
claw_unregister_debug_facility();
pr_info("Driver unloaded\n");
@@ -3348,16 +3395,31 @@ claw_init(void)
if (ret) {
pr_err("Registering with the S/390 debug feature"
" failed with error code %d\n", ret);
- return ret;
+ goto out_err;
}
CLAW_DBF_TEXT(2, setup, "init_mod");
- ret = register_cu3088_discipline(&claw_group_driver);
- if (ret) {
- CLAW_DBF_TEXT(2, setup, "init_bad");
- claw_unregister_debug_facility();
- pr_err("Registering with the cu3088 device driver failed "
- "with error code %d\n", ret);
- }
+ claw_root_dev = root_device_register("qeth");
+ ret = IS_ERR(claw_root_dev) ? PTR_ERR(claw_root_dev) : 0;
+ if (ret)
+ goto register_err;
+ ret = ccw_driver_register(&claw_ccw_driver);
+ if (ret)
+ goto ccw_err;
+ claw_group_driver.driver.groups = claw_group_attr_groups;
+ ret = ccwgroup_driver_register(&claw_group_driver);
+ if (ret)
+ goto ccwgroup_err;
+ return 0;
+
+ccwgroup_err:
+ ccw_driver_unregister(&claw_ccw_driver);
+ccw_err:
+ root_device_unregister(claw_root_dev);
+register_err:
+ CLAW_DBF_TEXT(2, setup, "init_bad");
+ claw_unregister_debug_facility();
+out_err:
+ pr_err("Initializing the claw device driver failed\n");
return ret;
}
diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h
index 005072c420d..46d59a13db1 100644
--- a/drivers/s390/net/claw.h
+++ b/drivers/s390/net/claw.h
@@ -129,6 +129,18 @@ static inline int claw_dbf_passes(debug_info_t *dbf_grp, int level)
} \
} while (0)
+/**
+ * Enum for classifying detected devices.
+ */
+enum claw_channel_types {
+ /* Device is not a channel */
+ claw_channel_type_none,
+
+ /* Device is a CLAW channel device */
+ claw_channel_type_claw
+};
+
+
/*******************************************************
* Define Control Blocks *
* *
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 4ded9ac2c5e..70eb7f13841 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -44,7 +44,6 @@
#include <asm/idals.h>
#include "fsm.h"
-#include "cu3088.h"
#include "ctcm_dbug.h"
#include "ctcm_main.h"
diff --git a/drivers/s390/net/ctcm_fsms.h b/drivers/s390/net/ctcm_fsms.h
index 2326aba9807..046d077fabb 100644
--- a/drivers/s390/net/ctcm_fsms.h
+++ b/drivers/s390/net/ctcm_fsms.h
@@ -39,7 +39,6 @@
#include <asm/idals.h>
#include "fsm.h"
-#include "cu3088.h"
#include "ctcm_main.h"
/*
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index c5b83874500..e35713dd050 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -51,12 +51,16 @@
#include <asm/idals.h>
-#include "cu3088.h"
#include "ctcm_fsms.h"
#include "ctcm_main.h"
/* Some common global variables */
+/**
+ * The root device for ctcm group devices
+ */
+static struct device *ctcm_root_dev;
+
/*
* Linked list of all detected channels.
*/
@@ -246,7 +250,7 @@ static void channel_remove(struct channel *ch)
*
* returns Pointer to a channel or NULL if no matching channel available.
*/
-static struct channel *channel_get(enum channel_types type,
+static struct channel *channel_get(enum ctcm_channel_types type,
char *id, int direction)
{
struct channel *ch = channels;
@@ -1342,7 +1346,7 @@ static int ctcm_probe_device(struct ccwgroup_device *cgdev)
*
* returns 0 on success, !0 on error.
*/
-static int add_channel(struct ccw_device *cdev, enum channel_types type,
+static int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type,
struct ctcm_priv *priv)
{
struct channel **c = &channels;
@@ -1501,13 +1505,13 @@ free_return: /* note that all channel pointers are 0 or valid */
/*
* Return type of a detected device.
*/
-static enum channel_types get_channel_type(struct ccw_device_id *id)
+static enum ctcm_channel_types get_channel_type(struct ccw_device_id *id)
{
- enum channel_types type;
- type = (enum channel_types)id->driver_info;
+ enum ctcm_channel_types type;
+ type = (enum ctcm_channel_types)id->driver_info;
- if (type == channel_type_ficon)
- type = channel_type_escon;
+ if (type == ctcm_channel_type_ficon)
+ type = ctcm_channel_type_escon;
return type;
}
@@ -1525,16 +1529,21 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
char read_id[CTCM_ID_SIZE];
char write_id[CTCM_ID_SIZE];
int direction;
- enum channel_types type;
+ enum ctcm_channel_types type;
struct ctcm_priv *priv;
struct net_device *dev;
struct ccw_device *cdev0;
struct ccw_device *cdev1;
+ struct channel *readc;
+ struct channel *writec;
int ret;
+ int result;
priv = dev_get_drvdata(&cgdev->dev);
- if (!priv)
- return -ENODEV;
+ if (!priv) {
+ result = -ENODEV;
+ goto out_err_result;
+ }
cdev0 = cgdev->cdev[0];
cdev1 = cgdev->cdev[1];
@@ -1545,31 +1554,40 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev));
ret = add_channel(cdev0, type, priv);
- if (ret)
- return ret;
+ if (ret) {
+ result = ret;
+ goto out_err_result;
+ }
ret = add_channel(cdev1, type, priv);
- if (ret)
- return ret;
+ if (ret) {
+ result = ret;
+ goto out_remove_channel1;
+ }
ret = ccw_device_set_online(cdev0);
if (ret != 0) {
- /* may be ok to fail now - can be done later */
CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
"%s(%s) set_online rc=%d",
CTCM_FUNTAIL, read_id, ret);
+ result = -EIO;
+ goto out_remove_channel2;
}
ret = ccw_device_set_online(cdev1);
if (ret != 0) {
- /* may be ok to fail now - can be done later */
CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
"%s(%s) set_online rc=%d",
CTCM_FUNTAIL, write_id, ret);
+
+ result = -EIO;
+ goto out_ccw1;
}
dev = ctcm_init_netdevice(priv);
- if (dev == NULL)
- goto out;
+ if (dev == NULL) {
+ result = -ENODEV;
+ goto out_ccw2;
+ }
for (direction = READ; direction <= WRITE; direction++) {
priv->channel[direction] =
@@ -1587,12 +1605,14 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
/* sysfs magic */
SET_NETDEV_DEV(dev, &cgdev->dev);
- if (register_netdev(dev))
- goto out_dev;
+ if (register_netdev(dev)) {
+ result = -ENODEV;
+ goto out_dev;
+ }
if (ctcm_add_attributes(&cgdev->dev)) {
- unregister_netdev(dev);
- goto out_dev;
+ result = -ENODEV;
+ goto out_unregister;
}
strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));
@@ -1608,13 +1628,22 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
priv->channel[WRITE]->id, priv->protocol);
return 0;
+out_unregister:
+ unregister_netdev(dev);
out_dev:
ctcm_free_netdevice(dev);
-out:
+out_ccw2:
ccw_device_set_offline(cgdev->cdev[1]);
+out_ccw1:
ccw_device_set_offline(cgdev->cdev[0]);
-
- return -ENODEV;
+out_remove_channel2:
+ readc = channel_get(type, read_id, READ);
+ channel_remove(readc);
+out_remove_channel1:
+ writec = channel_get(type, write_id, WRITE);
+ channel_remove(writec);
+out_err_result:
+ return result;
}
/**
@@ -1695,6 +1724,11 @@ static int ctcm_pm_suspend(struct ccwgroup_device *gdev)
return 0;
netif_device_detach(priv->channel[READ]->netdev);
ctcm_close(priv->channel[READ]->netdev);
+ if (!wait_event_timeout(priv->fsm->wait_q,
+ fsm_getstate(priv->fsm) == DEV_STATE_STOPPED, CTCM_TIME_5_SEC)) {
+ netif_device_attach(priv->channel[READ]->netdev);
+ return -EBUSY;
+ }
ccw_device_set_offline(gdev->cdev[1]);
ccw_device_set_offline(gdev->cdev[0]);
return 0;
@@ -1719,6 +1753,22 @@ err_out:
return rc;
}
+static struct ccw_device_id ctcm_ids[] = {
+ {CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel},
+ {CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon},
+ {CCW_DEVICE(0x3088, 0x1f), .driver_info = ctcm_channel_type_escon},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, ctcm_ids);
+
+static struct ccw_driver ctcm_ccw_driver = {
+ .owner = THIS_MODULE,
+ .name = "ctcm",
+ .ids = ctcm_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+};
+
static struct ccwgroup_driver ctcm_group_driver = {
.owner = THIS_MODULE,
.name = CTC_DRIVER_NAME,
@@ -1733,6 +1783,33 @@ static struct ccwgroup_driver ctcm_group_driver = {
.restore = ctcm_pm_resume,
};
+static ssize_t
+ctcm_driver_group_store(struct device_driver *ddrv, const char *buf,
+ size_t count)
+{
+ int err;
+
+ err = ccwgroup_create_from_string(ctcm_root_dev,
+ ctcm_group_driver.driver_id,
+ &ctcm_ccw_driver, 2, buf);
+ return err ? err : count;
+}
+
+static DRIVER_ATTR(group, 0200, NULL, ctcm_driver_group_store);
+
+static struct attribute *ctcm_group_attrs[] = {
+ &driver_attr_group.attr,
+ NULL,
+};
+
+static struct attribute_group ctcm_group_attr_group = {
+ .attrs = ctcm_group_attrs,
+};
+
+static const struct attribute_group *ctcm_group_attr_groups[] = {
+ &ctcm_group_attr_group,
+ NULL,
+};
/*
* Module related routines
@@ -1746,7 +1823,10 @@ static struct ccwgroup_driver ctcm_group_driver = {
*/
static void __exit ctcm_exit(void)
{
- unregister_cu3088_discipline(&ctcm_group_driver);
+ driver_remove_file(&ctcm_group_driver.driver, &driver_attr_group);
+ ccwgroup_driver_unregister(&ctcm_group_driver);
+ ccw_driver_unregister(&ctcm_ccw_driver);
+ root_device_unregister(ctcm_root_dev);
ctcm_unregister_dbf_views();
pr_info("CTCM driver unloaded\n");
}
@@ -1772,17 +1852,31 @@ static int __init ctcm_init(void)
channels = NULL;
ret = ctcm_register_dbf_views();
- if (ret) {
- return ret;
- }
- ret = register_cu3088_discipline(&ctcm_group_driver);
- if (ret) {
- ctcm_unregister_dbf_views();
- pr_err("%s / register_cu3088_discipline failed, ret = %d\n",
- __func__, ret);
- return ret;
- }
+ if (ret)
+ goto out_err;
+ ctcm_root_dev = root_device_register("ctcm");
+ ret = IS_ERR(ctcm_root_dev) ? PTR_ERR(ctcm_root_dev) : 0;
+ if (ret)
+ goto register_err;
+ ret = ccw_driver_register(&ctcm_ccw_driver);
+ if (ret)
+ goto ccw_err;
+ ctcm_group_driver.driver.groups = ctcm_group_attr_groups;
+ ret = ccwgroup_driver_register(&ctcm_group_driver);
+ if (ret)
+ goto ccwgroup_err;
print_banner();
+ return 0;
+
+ccwgroup_err:
+ ccw_driver_unregister(&ctcm_ccw_driver);
+ccw_err:
+ root_device_unregister(ctcm_root_dev);
+register_err:
+ ctcm_unregister_dbf_views();
+out_err:
+ pr_err("%s / Initializing the ctcm device driver failed, ret = %d\n",
+ __func__, ret);
return ret;
}
diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h
index d925e732b7d..d34fa14f44e 100644
--- a/drivers/s390/net/ctcm_main.h
+++ b/drivers/s390/net/ctcm_main.h
@@ -16,7 +16,6 @@
#include <linux/netdevice.h>
#include "fsm.h"
-#include "cu3088.h"
#include "ctcm_dbug.h"
#include "ctcm_mpc.h"
@@ -66,6 +65,23 @@
ctcmpc_dumpit(buf, len); \
} while (0)
+/**
+ * Enum for classifying detected devices
+ */
+enum ctcm_channel_types {
+ /* Device is not a channel */
+ ctcm_channel_type_none,
+
+ /* Device is a CTC/A */
+ ctcm_channel_type_parallel,
+
+ /* Device is a FICON channel */
+ ctcm_channel_type_ficon,
+
+ /* Device is a ESCON channel */
+ ctcm_channel_type_escon
+};
+
/*
* CCW commands, used in this driver.
*/
@@ -121,7 +137,7 @@ struct channel {
* Type of this channel.
* CTC/A or Escon for valid channels.
*/
- enum channel_types type;
+ enum ctcm_channel_types type;
/*
* Misc. flags. See CHANNEL_FLAGS_... below
*/
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
index 781e18be7e8..5978b390153 100644
--- a/drivers/s390/net/ctcm_mpc.c
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -53,7 +53,6 @@
#include <linux/moduleparam.h>
#include <asm/idals.h>
-#include "cu3088.h"
#include "ctcm_mpc.h"
#include "ctcm_main.h"
#include "ctcm_fsms.h"
diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c
index 8452bb052d6..738ad26c74a 100644
--- a/drivers/s390/net/ctcm_sysfs.c
+++ b/drivers/s390/net/ctcm_sysfs.c
@@ -158,6 +158,15 @@ static ssize_t ctcm_proto_store(struct device *dev,
return count;
}
+const char *ctcm_type[] = {
+ "not a channel",
+ "CTC/A",
+ "FICON channel",
+ "ESCON channel",
+ "unknown channel type",
+ "unsupported channel type",
+};
+
static ssize_t ctcm_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -168,7 +177,7 @@ static ssize_t ctcm_type_show(struct device *dev,
return -ENODEV;
return sprintf(buf, "%s\n",
- cu3088_type[cgdev->cdev[0]->id.driver_info]);
+ ctcm_type[cgdev->cdev[0]->id.driver_info]);
}
static DEVICE_ATTR(buffer, 0644, ctcm_buffer_show, ctcm_buffer_write);
diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c
deleted file mode 100644
index 48383459e99..00000000000
--- a/drivers/s390/net/cu3088.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * CTC / LCS ccw_device driver
- *
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Arnd Bergmann <arndb@de.ibm.com>
- * Cornelia Huck <cornelia.huck@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/err.h>
-
-#include <asm/ccwdev.h>
-#include <asm/ccwgroup.h>
-
-#include "cu3088.h"
-
-const char *cu3088_type[] = {
- "not a channel",
- "CTC/A",
- "ESCON channel",
- "FICON channel",
- "OSA LCS card",
- "CLAW channel device",
- "unknown channel type",
- "unsupported channel type",
-};
-
-/* static definitions */
-
-static struct ccw_device_id cu3088_ids[] = {
- { CCW_DEVICE(0x3088, 0x08), .driver_info = channel_type_parallel },
- { CCW_DEVICE(0x3088, 0x1f), .driver_info = channel_type_escon },
- { CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon },
- { CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 },
- { CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw },
- { /* end of list */ }
-};
-
-static struct ccw_driver cu3088_driver;
-
-static struct device *cu3088_root_dev;
-
-static ssize_t
-group_write(struct device_driver *drv, const char *buf, size_t count)
-{
- int ret;
- struct ccwgroup_driver *cdrv;
-
- cdrv = to_ccwgroupdrv(drv);
- if (!cdrv)
- return -EINVAL;
- ret = ccwgroup_create_from_string(cu3088_root_dev, cdrv->driver_id,
- &cu3088_driver, 2, buf);
-
- return (ret == 0) ? count : ret;
-}
-
-static DRIVER_ATTR(group, 0200, NULL, group_write);
-
-/* Register-unregister for ctc&lcs */
-int
-register_cu3088_discipline(struct ccwgroup_driver *dcp)
-{
- int rc;
-
- if (!dcp)
- return -EINVAL;
-
- /* Register discipline.*/
- rc = ccwgroup_driver_register(dcp);
- if (rc)
- return rc;
-
- rc = driver_create_file(&dcp->driver, &driver_attr_group);
- if (rc)
- ccwgroup_driver_unregister(dcp);
-
- return rc;
-
-}
-
-void
-unregister_cu3088_discipline(struct ccwgroup_driver *dcp)
-{
- if (!dcp)
- return;
-
- driver_remove_file(&dcp->driver, &driver_attr_group);
- ccwgroup_driver_unregister(dcp);
-}
-
-static struct ccw_driver cu3088_driver = {
- .owner = THIS_MODULE,
- .ids = cu3088_ids,
- .name = "cu3088",
- .probe = ccwgroup_probe_ccwdev,
- .remove = ccwgroup_remove_ccwdev,
-};
-
-/* module setup */
-static int __init
-cu3088_init (void)
-{
- int rc;
-
- cu3088_root_dev = root_device_register("cu3088");
- if (IS_ERR(cu3088_root_dev))
- return PTR_ERR(cu3088_root_dev);
- rc = ccw_driver_register(&cu3088_driver);
- if (rc)
- root_device_unregister(cu3088_root_dev);
-
- return rc;
-}
-
-static void __exit
-cu3088_exit (void)
-{
- ccw_driver_unregister(&cu3088_driver);
- root_device_unregister(cu3088_root_dev);
-}
-
-MODULE_DEVICE_TABLE(ccw,cu3088_ids);
-MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
-MODULE_LICENSE("GPL");
-
-module_init(cu3088_init);
-module_exit(cu3088_exit);
-
-EXPORT_SYMBOL_GPL(cu3088_type);
-EXPORT_SYMBOL_GPL(register_cu3088_discipline);
-EXPORT_SYMBOL_GPL(unregister_cu3088_discipline);
diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h
deleted file mode 100644
index d8558a7105a..00000000000
--- a/drivers/s390/net/cu3088.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef _CU3088_H
-#define _CU3088_H
-
-/**
- * Enum for classifying detected devices.
- */
-enum channel_types {
- /* Device is not a channel */
- channel_type_none,
-
- /* Device is a CTC/A */
- channel_type_parallel,
-
- /* Device is a ESCON channel */
- channel_type_escon,
-
- /* Device is a FICON channel */
- channel_type_ficon,
-
- /* Device is a OSA2 card */
- channel_type_osa2,
-
- /* Device is a CLAW channel device */
- channel_type_claw,
-
- /* Device is a channel, but we don't know
- * anything about it */
- channel_type_unknown,
-
- /* Device is an unsupported model */
- channel_type_unsupported,
-
- /* number of type entries */
- num_channel_types
-};
-
-extern const char *cu3088_type[num_channel_types];
-extern int register_cu3088_discipline(struct ccwgroup_driver *);
-extern void unregister_cu3088_discipline(struct ccwgroup_driver *);
-
-#endif
diff --git a/drivers/s390/net/fsm.c b/drivers/s390/net/fsm.c
index 2c1db8036b7..cae48cbc5e9 100644
--- a/drivers/s390/net/fsm.c
+++ b/drivers/s390/net/fsm.c
@@ -27,6 +27,7 @@ init_fsm(char *name, const char **state_names, const char **event_names, int nr_
return NULL;
}
strlcpy(this->name, name, sizeof(this->name));
+ init_waitqueue_head(&this->wait_q);
f = kzalloc(sizeof(fsm), order);
if (f == NULL) {
diff --git a/drivers/s390/net/fsm.h b/drivers/s390/net/fsm.h
index af679c10f1b..1e8b235d95b 100644
--- a/drivers/s390/net/fsm.h
+++ b/drivers/s390/net/fsm.h
@@ -66,6 +66,7 @@ typedef struct fsm_instance_t {
char name[16];
void *userdata;
int userint;
+ wait_queue_head_t wait_q;
#if FSM_DEBUG_HISTORY
int history_index;
int history_size;
@@ -197,6 +198,7 @@ fsm_newstate(fsm_instance *fi, int newstate)
printk(KERN_DEBUG "fsm(%s): New state %s\n", fi->name,
fi->f->state_names[newstate]);
#endif
+ wake_up(&fi->wait_q);
}
/**
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index a70de9b4bf2..f6cc46dc050 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -47,7 +47,6 @@
#include <asm/ccwgroup.h>
#include "lcs.h"
-#include "cu3088.h"
#if !defined(CONFIG_NET_ETHERNET) && \
@@ -60,7 +59,11 @@
*/
static char version[] __initdata = "LCS driver";
-static char debug_buffer[255];
+
+/**
+ * the root device for lcs group devices
+ */
+static struct device *lcs_root_dev;
/**
* Some prototypes.
@@ -76,6 +79,7 @@ static int lcs_recovery(void *ptr);
/**
* Debug Facility Stuff
*/
+static char debug_buffer[255];
static debug_info_t *lcs_dbf_setup;
static debug_info_t *lcs_dbf_trace;
@@ -889,7 +893,7 @@ lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,
rc = lcs_ready_buffer(&card->write, buffer);
if (rc)
return rc;
- init_timer(&timer);
+ init_timer_on_stack(&timer);
timer.function = lcs_lancmd_timeout;
timer.data = (unsigned long) reply;
timer.expires = jiffies + HZ*card->lancmd_timeout;
@@ -1968,6 +1972,15 @@ lcs_portno_store (struct device *dev, struct device_attribute *attr, const char
static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store);
+const char *lcs_type[] = {
+ "not a channel",
+ "2216 parallel",
+ "2216 channel",
+ "OSA LCS card",
+ "unknown channel type",
+ "unsupported channel type",
+};
+
static ssize_t
lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -1977,7 +1990,7 @@ lcs_type_show(struct device *dev, struct device_attribute *attr, char *buf)
if (!cgdev)
return -ENODEV;
- return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]);
+ return sprintf(buf, "%s\n", lcs_type[cgdev->cdev[0]->id.driver_info]);
}
static DEVICE_ATTR(type, 0444, lcs_type_show, NULL);
@@ -2130,8 +2143,12 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
card->write.ccwdev = ccwgdev->cdev[1];
recover_state = card->state;
- ccw_device_set_online(card->read.ccwdev);
- ccw_device_set_online(card->write.ccwdev);
+ rc = ccw_device_set_online(card->read.ccwdev);
+ if (rc)
+ goto out_err;
+ rc = ccw_device_set_online(card->write.ccwdev);
+ if (rc)
+ goto out_werr;
LCS_DBF_TEXT(3, setup, "lcsnewdv");
@@ -2210,8 +2227,10 @@ netdev_out:
return 0;
out:
- ccw_device_set_offline(card->read.ccwdev);
ccw_device_set_offline(card->write.ccwdev);
+out_werr:
+ ccw_device_set_offline(card->read.ccwdev);
+out_err:
return -ENODEV;
}
@@ -2364,6 +2383,22 @@ static int lcs_restore(struct ccwgroup_device *gdev)
return lcs_pm_resume(card);
}
+static struct ccw_device_id lcs_ids[] = {
+ {CCW_DEVICE(0x3088, 0x08), .driver_info = lcs_channel_type_parallel},
+ {CCW_DEVICE(0x3088, 0x1f), .driver_info = lcs_channel_type_2216},
+ {CCW_DEVICE(0x3088, 0x60), .driver_info = lcs_channel_type_osa2},
+ {},
+};
+MODULE_DEVICE_TABLE(ccw, lcs_ids);
+
+static struct ccw_driver lcs_ccw_driver = {
+ .owner = THIS_MODULE,
+ .name = "lcs",
+ .ids = lcs_ids,
+ .probe = ccwgroup_probe_ccwdev,
+ .remove = ccwgroup_remove_ccwdev,
+};
+
/**
* LCS ccwgroup driver registration
*/
@@ -2383,6 +2418,33 @@ static struct ccwgroup_driver lcs_group_driver = {
.restore = lcs_restore,
};
+static ssize_t
+lcs_driver_group_store(struct device_driver *ddrv, const char *buf,
+ size_t count)
+{
+ int err;
+ err = ccwgroup_create_from_string(lcs_root_dev,
+ lcs_group_driver.driver_id,
+ &lcs_ccw_driver, 2, buf);
+ return err ? err : count;
+}
+
+static DRIVER_ATTR(group, 0200, NULL, lcs_driver_group_store);
+
+static struct attribute *lcs_group_attrs[] = {
+ &driver_attr_group.attr,
+ NULL,
+};
+
+static struct attribute_group lcs_group_attr_group = {
+ .attrs = lcs_group_attrs,
+};
+
+static const struct attribute_group *lcs_group_attr_groups[] = {
+ &lcs_group_attr_group,
+ NULL,
+};
+
/**
* LCS Module/Kernel initialization function
*/
@@ -2394,17 +2456,30 @@ __init lcs_init_module(void)
pr_info("Loading %s\n", version);
rc = lcs_register_debug_facility();
LCS_DBF_TEXT(0, setup, "lcsinit");
- if (rc) {
- pr_err("Initialization failed\n");
- return rc;
- }
-
- rc = register_cu3088_discipline(&lcs_group_driver);
- if (rc) {
- pr_err("Initialization failed\n");
- return rc;
- }
+ if (rc)
+ goto out_err;
+ lcs_root_dev = root_device_register("lcs");
+ rc = IS_ERR(lcs_root_dev) ? PTR_ERR(lcs_root_dev) : 0;
+ if (rc)
+ goto register_err;
+ rc = ccw_driver_register(&lcs_ccw_driver);
+ if (rc)
+ goto ccw_err;
+ lcs_group_driver.driver.groups = lcs_group_attr_groups;
+ rc = ccwgroup_driver_register(&lcs_group_driver);
+ if (rc)
+ goto ccwgroup_err;
return 0;
+
+ccwgroup_err:
+ ccw_driver_unregister(&lcs_ccw_driver);
+ccw_err:
+ root_device_unregister(lcs_root_dev);
+register_err:
+ lcs_unregister_debug_facility();
+out_err:
+ pr_err("Initializing the lcs device driver failed\n");
+ return rc;
}
@@ -2416,7 +2491,11 @@ __exit lcs_cleanup_module(void)
{
pr_info("Terminating lcs module.\n");
LCS_DBF_TEXT(0, trace, "cleanup");
- unregister_cu3088_discipline(&lcs_group_driver);
+ driver_remove_file(&lcs_group_driver.driver,
+ &driver_attr_group);
+ ccwgroup_driver_unregister(&lcs_group_driver);
+ ccw_driver_unregister(&lcs_ccw_driver);
+ root_device_unregister(lcs_root_dev);
lcs_unregister_debug_facility();
}
diff --git a/drivers/s390/net/lcs.h b/drivers/s390/net/lcs.h
index 6d668642af2..8c03392ac83 100644
--- a/drivers/s390/net/lcs.h
+++ b/drivers/s390/net/lcs.h
@@ -36,6 +36,24 @@ static inline int lcs_dbf_passes(debug_info_t *dbf_grp, int level)
#define CARD_FROM_DEV(cdev) \
(struct lcs_card *) dev_get_drvdata( \
&((struct ccwgroup_device *)dev_get_drvdata(&cdev->dev))->dev);
+
+/**
+ * Enum for classifying detected devices.
+ */
+enum lcs_channel_types {
+ /* Device is not a channel */
+ lcs_channel_type_none,
+
+ /* Device is a 2216 channel */
+ lcs_channel_type_parallel,
+
+ /* Device is a 2216 channel */
+ lcs_channel_type_2216,
+
+ /* Device is a OSA2 card */
+ lcs_channel_type_osa2
+};
+
/**
* CCW commands used in this driver
*/
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index c84eadd3602..395c04c2b00 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -741,13 +741,13 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg)
if (single_flag) {
if ((skb = skb_dequeue(&conn->commit_queue))) {
atomic_dec(&skb->users);
- dev_kfree_skb_any(skb);
if (privptr) {
privptr->stats.tx_packets++;
privptr->stats.tx_bytes +=
(skb->len - NETIUCV_HDRLEN
- - NETIUCV_HDRLEN);
+ - NETIUCV_HDRLEN);
}
+ dev_kfree_skb_any(skb);
}
}
conn->tx_buff->data = conn->tx_buff->head;
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 31a2b4e502c..b232693378c 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -122,7 +122,6 @@ struct qeth_perf_stats {
__u64 outbound_do_qdio_start_time;
unsigned int outbound_do_qdio_cnt;
unsigned int outbound_do_qdio_time;
- /* eddp data */
unsigned int large_send_bytes;
unsigned int large_send_cnt;
unsigned int sg_skbs_sent;
@@ -135,6 +134,7 @@ struct qeth_perf_stats {
unsigned int sg_frags_rx;
unsigned int sg_alloc_page_rx;
unsigned int tx_csum;
+ unsigned int tx_lin;
};
/* Routing stuff */
@@ -648,6 +648,7 @@ struct qeth_card_options {
enum qeth_large_send_types large_send;
int performance_stats;
int rx_sg_cb;
+ enum qeth_ipa_isolation_modes isolation;
};
/*
@@ -776,7 +777,6 @@ static inline void qeth_put_buffer_pool_entry(struct qeth_card *card,
list_add_tail(&entry->list, &card->qdio.in_buf_pool.entry_list);
}
-struct qeth_eddp_context;
extern struct ccwgroup_driver qeth_l2_ccwgroup_driver;
extern struct ccwgroup_driver qeth_l3_ccwgroup_driver;
const char *qeth_get_cardname_short(struct qeth_card *);
@@ -836,7 +836,6 @@ void qeth_prepare_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, char);
struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *);
int qeth_mdio_read(struct net_device *, int, int);
int qeth_snmp_command(struct qeth_card *, char __user *);
-int qeth_set_large_send(struct qeth_card *, enum qeth_large_send_types);
struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *, __u32, __u32);
int qeth_default_setadapterparms_cb(struct qeth_card *, struct qeth_reply *,
unsigned long);
@@ -849,13 +848,14 @@ int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
struct sk_buff *, struct qeth_hdr *, int, int, int);
int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *,
struct sk_buff *, struct qeth_hdr *, int);
-int qeth_core_get_stats_count(struct net_device *);
+int qeth_core_get_sset_count(struct net_device *, int);
void qeth_core_get_ethtool_stats(struct net_device *,
struct ethtool_stats *, u64 *);
void qeth_core_get_strings(struct net_device *, u32, u8 *);
void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...);
int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *);
+int qeth_set_access_ctrl_online(struct qeth_card *card);
/* exports for OSN */
int qeth_osn_assist(struct net_device *, void *, int);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index c4a42d97015..d34804d5ece 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -270,41 +270,6 @@ int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)
return qeth_alloc_buffer_pool(card);
}
-int qeth_set_large_send(struct qeth_card *card,
- enum qeth_large_send_types type)
-{
- int rc = 0;
-
- if (card->dev == NULL) {
- card->options.large_send = type;
- return 0;
- }
- if (card->state == CARD_STATE_UP)
- netif_tx_disable(card->dev);
- card->options.large_send = type;
- switch (card->options.large_send) {
- case QETH_LARGE_SEND_TSO:
- if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
- card->dev->features |= NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM;
- } else {
- card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM);
- card->options.large_send = QETH_LARGE_SEND_NO;
- rc = -EOPNOTSUPP;
- }
- break;
- default: /* includes QETH_LARGE_SEND_NO */
- card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
- NETIF_F_HW_CSUM);
- break;
- }
- if (card->state == CARD_STATE_UP)
- netif_wake_queue(card->dev);
- return rc;
-}
-EXPORT_SYMBOL_GPL(qeth_set_large_send);
-
static int qeth_issue_next_read(struct qeth_card *card)
{
int rc;
@@ -1079,6 +1044,7 @@ static void qeth_set_intial_options(struct qeth_card *card)
card->options.add_hhlen = DEFAULT_ADD_HHLEN;
card->options.performance_stats = 0;
card->options.rx_sg_cb = QETH_RX_SG_CB;
+ card->options.isolation = ISOLATION_MODE_NONE;
}
static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread)
@@ -3389,6 +3355,156 @@ int qeth_setadpparms_change_macaddr(struct qeth_card *card)
}
EXPORT_SYMBOL_GPL(qeth_setadpparms_change_macaddr);
+static int qeth_setadpparms_set_access_ctrl_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_set_access_ctrl *access_ctrl_req;
+ int rc;
+
+ QETH_DBF_TEXT(TRACE, 4, "setaccb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl;
+ QETH_DBF_TEXT_(SETUP, 2, "setaccb");
+ QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);
+ QETH_DBF_TEXT_(SETUP, 2, "rc=%d",
+ cmd->data.setadapterparms.hdr.return_code);
+ switch (cmd->data.setadapterparms.hdr.return_code) {
+ case SET_ACCESS_CTRL_RC_SUCCESS:
+ case SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED:
+ case SET_ACCESS_CTRL_RC_ALREADY_ISOLATED:
+ {
+ card->options.isolation = access_ctrl_req->subcmd_code;
+ if (card->options.isolation == ISOLATION_MODE_NONE) {
+ dev_info(&card->gdev->dev,
+ "QDIO data connection isolation is deactivated\n");
+ } else {
+ dev_info(&card->gdev->dev,
+ "QDIO data connection isolation is activated\n");
+ }
+ QETH_DBF_MESSAGE(3, "OK:SET_ACCESS_CTRL(%s, %d)==%d\n",
+ card->gdev->dev.kobj.name,
+ access_ctrl_req->subcmd_code,
+ cmd->data.setadapterparms.hdr.return_code);
+ rc = 0;
+ break;
+ }
+ case SET_ACCESS_CTRL_RC_NOT_SUPPORTED:
+ {
+ QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_CTRL(%s,%d)==%d\n",
+ card->gdev->dev.kobj.name,
+ access_ctrl_req->subcmd_code,
+ cmd->data.setadapterparms.hdr.return_code);
+ dev_err(&card->gdev->dev, "Adapter does not "
+ "support QDIO data connection isolation\n");
+
+ /* ensure isolation mode is "none" */
+ card->options.isolation = ISOLATION_MODE_NONE;
+ rc = -EOPNOTSUPP;
+ break;
+ }
+ case SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER:
+ {
+ QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n",
+ card->gdev->dev.kobj.name,
+ access_ctrl_req->subcmd_code,
+ cmd->data.setadapterparms.hdr.return_code);
+ dev_err(&card->gdev->dev,
+ "Adapter is dedicated. "
+ "QDIO data connection isolation not supported\n");
+
+ /* ensure isolation mode is "none" */
+ card->options.isolation = ISOLATION_MODE_NONE;
+ rc = -EOPNOTSUPP;
+ break;
+ }
+ case SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF:
+ {
+ QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d\n",
+ card->gdev->dev.kobj.name,
+ access_ctrl_req->subcmd_code,
+ cmd->data.setadapterparms.hdr.return_code);
+ dev_err(&card->gdev->dev,
+ "TSO does not permit QDIO data connection isolation\n");
+
+ /* ensure isolation mode is "none" */
+ card->options.isolation = ISOLATION_MODE_NONE;
+ rc = -EPERM;
+ break;
+ }
+ default:
+ {
+ /* this should never happen */
+ QETH_DBF_MESSAGE(3, "ERR:SET_ACCESS_MODE(%s,%d)==%d"
+ "==UNKNOWN\n",
+ card->gdev->dev.kobj.name,
+ access_ctrl_req->subcmd_code,
+ cmd->data.setadapterparms.hdr.return_code);
+
+ /* ensure isolation mode is "none" */
+ card->options.isolation = ISOLATION_MODE_NONE;
+ rc = 0;
+ break;
+ }
+ }
+ qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
+ return rc;
+}
+
+static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card,
+ enum qeth_ipa_isolation_modes isolation)
+{
+ int rc;
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+ struct qeth_set_access_ctrl *access_ctrl_req;
+
+ QETH_DBF_TEXT(TRACE, 4, "setacctl");
+
+ QETH_DBF_TEXT_(SETUP, 2, "setacctl");
+ QETH_DBF_TEXT_(SETUP, 2, "%s", card->gdev->dev.kobj.name);
+
+ iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL,
+ sizeof(struct qeth_ipacmd_setadpparms_hdr) +
+ sizeof(struct qeth_set_access_ctrl));
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl;
+ access_ctrl_req->subcmd_code = isolation;
+
+ rc = qeth_send_ipa_cmd(card, iob, qeth_setadpparms_set_access_ctrl_cb,
+ NULL);
+ QETH_DBF_TEXT_(SETUP, 2, "rc=%d", rc);
+ return rc;
+}
+
+int qeth_set_access_ctrl_online(struct qeth_card *card)
+{
+ int rc = 0;
+
+ QETH_DBF_TEXT(TRACE, 4, "setactlo");
+
+ if (card->info.type == QETH_CARD_TYPE_OSAE &&
+ qeth_adp_supported(card, IPA_SETADP_SET_ACCESS_CONTROL)) {
+ rc = qeth_setadpparms_set_access_ctrl(card,
+ card->options.isolation);
+ if (rc) {
+ QETH_DBF_MESSAGE(3,
+ "IPA(SET_ACCESS_CTRL,%s,%d) sent failed",
+ card->gdev->dev.kobj.name,
+ rc);
+ }
+ } else if (card->options.isolation != ISOLATION_MODE_NONE) {
+ card->options.isolation = ISOLATION_MODE_NONE;
+
+ dev_err(&card->gdev->dev, "Adapter does not "
+ "support QDIO data connection isolation\n");
+ rc = -EOPNOTSUPP;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_set_access_ctrl_online);
+
void qeth_tx_timeout(struct net_device *dev)
{
struct qeth_card *card;
@@ -3732,30 +3848,36 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev,
int qeth_core_hardsetup_card(struct qeth_card *card)
{
struct qdio_ssqd_desc *ssqd;
- int retries = 3;
+ int retries = 0;
int mpno = 0;
int rc;
QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
atomic_set(&card->force_alloc_skb, 0);
retry:
- if (retries < 3) {
+ if (retries)
QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",
dev_name(&card->gdev->dev));
- ccw_device_set_offline(CARD_DDEV(card));
- ccw_device_set_offline(CARD_WDEV(card));
- ccw_device_set_offline(CARD_RDEV(card));
- ccw_device_set_online(CARD_RDEV(card));
- ccw_device_set_online(CARD_WDEV(card));
- ccw_device_set_online(CARD_DDEV(card));
- }
+ ccw_device_set_offline(CARD_DDEV(card));
+ ccw_device_set_offline(CARD_WDEV(card));
+ ccw_device_set_offline(CARD_RDEV(card));
+ rc = ccw_device_set_online(CARD_RDEV(card));
+ if (rc)
+ goto retriable;
+ rc = ccw_device_set_online(CARD_WDEV(card));
+ if (rc)
+ goto retriable;
+ rc = ccw_device_set_online(CARD_DDEV(card));
+ if (rc)
+ goto retriable;
rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD);
+retriable:
if (rc == -ERESTARTSYS) {
QETH_DBF_TEXT(SETUP, 2, "break1");
return rc;
} else if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
- if (--retries < 0)
+ if (++retries > 3)
goto out;
else
goto retry;
@@ -4303,13 +4425,19 @@ static struct {
{"tx do_QDIO time"},
{"tx do_QDIO count"},
{"tx csum"},
+ {"tx lin"},
};
-int qeth_core_get_stats_count(struct net_device *dev)
+int qeth_core_get_sset_count(struct net_device *dev, int stringset)
{
- return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN);
+ switch (stringset) {
+ case ETH_SS_STATS:
+ return (sizeof(qeth_ethtool_stats_keys) / ETH_GSTRING_LEN);
+ default:
+ return -EINVAL;
+ }
}
-EXPORT_SYMBOL_GPL(qeth_core_get_stats_count);
+EXPORT_SYMBOL_GPL(qeth_core_get_sset_count);
void qeth_core_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
@@ -4355,6 +4483,7 @@ void qeth_core_get_ethtool_stats(struct net_device *dev,
data[31] = card->perf_stats.outbound_do_qdio_time;
data[32] = card->perf_stats.outbound_do_qdio_cnt;
data[33] = card->perf_stats.tx_csum;
+ data[34] = card->perf_stats.tx_lin;
}
EXPORT_SYMBOL_GPL(qeth_core_get_ethtool_stats);
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h
index eecb2ee62e8..1ba51152f66 100644
--- a/drivers/s390/net/qeth_core_mpc.h
+++ b/drivers/s390/net/qeth_core_mpc.h
@@ -234,18 +234,19 @@ enum qeth_ipa_setdelip_flags {
/* SETADAPTER IPA Command: ****************************************************/
enum qeth_ipa_setadp_cmd {
- IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x0001,
- IPA_SETADP_ALTER_MAC_ADDRESS = 0x0002,
- IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x0004,
- IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x0008,
- IPA_SETADP_SET_ADDRESSING_MODE = 0x0010,
- IPA_SETADP_SET_CONFIG_PARMS = 0x0020,
- IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x0040,
- IPA_SETADP_SET_BROADCAST_MODE = 0x0080,
- IPA_SETADP_SEND_OSA_MESSAGE = 0x0100,
- IPA_SETADP_SET_SNMP_CONTROL = 0x0200,
- IPA_SETADP_QUERY_CARD_INFO = 0x0400,
- IPA_SETADP_SET_PROMISC_MODE = 0x0800,
+ IPA_SETADP_QUERY_COMMANDS_SUPPORTED = 0x00000001L,
+ IPA_SETADP_ALTER_MAC_ADDRESS = 0x00000002L,
+ IPA_SETADP_ADD_DELETE_GROUP_ADDRESS = 0x00000004L,
+ IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR = 0x00000008L,
+ IPA_SETADP_SET_ADDRESSING_MODE = 0x00000010L,
+ IPA_SETADP_SET_CONFIG_PARMS = 0x00000020L,
+ IPA_SETADP_SET_CONFIG_PARMS_EXTENDED = 0x00000040L,
+ IPA_SETADP_SET_BROADCAST_MODE = 0x00000080L,
+ IPA_SETADP_SEND_OSA_MESSAGE = 0x00000100L,
+ IPA_SETADP_SET_SNMP_CONTROL = 0x00000200L,
+ IPA_SETADP_QUERY_CARD_INFO = 0x00000400L,
+ IPA_SETADP_SET_PROMISC_MODE = 0x00000800L,
+ IPA_SETADP_SET_ACCESS_CONTROL = 0x00010000L,
};
enum qeth_ipa_mac_ops {
CHANGE_ADDR_READ_MAC = 0,
@@ -264,6 +265,20 @@ enum qeth_ipa_promisc_modes {
SET_PROMISC_MODE_OFF = 0,
SET_PROMISC_MODE_ON = 1,
};
+enum qeth_ipa_isolation_modes {
+ ISOLATION_MODE_NONE = 0x00000000L,
+ ISOLATION_MODE_FWD = 0x00000001L,
+ ISOLATION_MODE_DROP = 0x00000002L,
+};
+enum qeth_ipa_set_access_mode_rc {
+ SET_ACCESS_CTRL_RC_SUCCESS = 0x0000,
+ SET_ACCESS_CTRL_RC_NOT_SUPPORTED = 0x0004,
+ SET_ACCESS_CTRL_RC_ALREADY_NOT_ISOLATED = 0x0008,
+ SET_ACCESS_CTRL_RC_ALREADY_ISOLATED = 0x0010,
+ SET_ACCESS_CTRL_RC_NONE_SHARED_ADAPTER = 0x0014,
+ SET_ACCESS_CTRL_RC_ACTIVE_CHECKSUM_OFF = 0x0018,
+};
+
/* (SET)DELIP(M) IPA stuff ***************************************************/
struct qeth_ipacmd_setdelip4 {
@@ -376,6 +391,11 @@ struct qeth_snmp_ureq {
struct qeth_snmp_cmd cmd;
} __attribute__((packed));
+/* SET_ACCESS_CONTROL: same format for request and reply */
+struct qeth_set_access_ctrl {
+ __u32 subcmd_code;
+} __attribute__((packed));
+
struct qeth_ipacmd_setadpparms_hdr {
__u32 supp_hw_cmds;
__u32 reserved1;
@@ -394,6 +414,7 @@ struct qeth_ipacmd_setadpparms {
struct qeth_query_cmds_supp query_cmds_supp;
struct qeth_change_addr change_addr;
struct qeth_snmp_cmd snmp;
+ struct qeth_set_access_ctrl set_access_ctrl;
__u32 mode;
} data;
} __attribute__ ((packed));
@@ -507,7 +528,7 @@ extern unsigned char ULP_ENABLE[];
(PDU_ENCAPSULATION(buffer) + 0x17)
#define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) \
(PDU_ENCAPSULATION(buffer) + 0x2b)
-/* Layer 2 defintions */
+/* Layer 2 definitions */
#define QETH_PROT_LAYER2 0x08
#define QETH_PROT_TCPIP 0x03
#define QETH_PROT_OSN2 0x0a
diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c
index 33505c2a0e3..9ff2b36fdc4 100644
--- a/drivers/s390/net/qeth_core_sys.c
+++ b/drivers/s390/net/qeth_core_sys.c
@@ -416,7 +416,11 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,
static DEVICE_ATTR(layer2, 0644, qeth_dev_layer2_show,
qeth_dev_layer2_store);
-static ssize_t qeth_dev_large_send_show(struct device *dev,
+#define ATTR_QETH_ISOLATION_NONE ("none")
+#define ATTR_QETH_ISOLATION_FWD ("forward")
+#define ATTR_QETH_ISOLATION_DROP ("drop")
+
+static ssize_t qeth_dev_isolation_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
@@ -424,44 +428,69 @@ static ssize_t qeth_dev_large_send_show(struct device *dev,
if (!card)
return -EINVAL;
- switch (card->options.large_send) {
- case QETH_LARGE_SEND_NO:
- return sprintf(buf, "%s\n", "no");
- case QETH_LARGE_SEND_TSO:
- return sprintf(buf, "%s\n", "TSO");
+ switch (card->options.isolation) {
+ case ISOLATION_MODE_NONE:
+ return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_NONE);
+ case ISOLATION_MODE_FWD:
+ return snprintf(buf, 9, "%s\n", ATTR_QETH_ISOLATION_FWD);
+ case ISOLATION_MODE_DROP:
+ return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_DROP);
default:
- return sprintf(buf, "%s\n", "N/A");
+ return snprintf(buf, 5, "%s\n", "N/A");
}
}
-static ssize_t qeth_dev_large_send_store(struct device *dev,
+static ssize_t qeth_dev_isolation_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
- enum qeth_large_send_types type;
+ enum qeth_ipa_isolation_modes isolation;
int rc = 0;
- char *tmp;
+ char *tmp, *curtoken;
+ curtoken = (char *) buf;
- if (!card)
- return -EINVAL;
- tmp = strsep((char **) &buf, "\n");
- if (!strcmp(tmp, "no")) {
- type = QETH_LARGE_SEND_NO;
- } else if (!strcmp(tmp, "TSO")) {
- type = QETH_LARGE_SEND_TSO;
+ if (!card) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* check for unknown, too, in case we do not yet know who we are */
+ if (card->info.type != QETH_CARD_TYPE_OSAE &&
+ card->info.type != QETH_CARD_TYPE_UNKNOWN) {
+ rc = -EOPNOTSUPP;
+ dev_err(&card->gdev->dev, "Adapter does not "
+ "support QDIO data connection isolation\n");
+ goto out;
+ }
+
+ /* parse input into isolation mode */
+ tmp = strsep(&curtoken, "\n");
+ if (!strcmp(tmp, ATTR_QETH_ISOLATION_NONE)) {
+ isolation = ISOLATION_MODE_NONE;
+ } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_FWD)) {
+ isolation = ISOLATION_MODE_FWD;
+ } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_DROP)) {
+ isolation = ISOLATION_MODE_DROP;
} else {
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
- if (card->options.large_send == type)
- return count;
- rc = qeth_set_large_send(card, type);
- if (rc)
- return rc;
- return count;
+ rc = count;
+
+ /* defer IP assist if device is offline (until discipline->set_online)*/
+ card->options.isolation = isolation;
+ if (card->state == CARD_STATE_SOFTSETUP ||
+ card->state == CARD_STATE_UP) {
+ int ipa_rc = qeth_set_access_ctrl_online(card);
+ if (ipa_rc != 0)
+ rc = ipa_rc;
+ }
+out:
+ return rc;
}
-static DEVICE_ATTR(large_send, 0644, qeth_dev_large_send_show,
- qeth_dev_large_send_store);
+static DEVICE_ATTR(isolation, 0644, qeth_dev_isolation_show,
+ qeth_dev_isolation_store);
static ssize_t qeth_dev_blkt_show(char *buf, struct qeth_card *card, int value)
{
@@ -582,7 +611,7 @@ static struct attribute *qeth_device_attrs[] = {
&dev_attr_recover.attr,
&dev_attr_performance_stats.attr,
&dev_attr_layer2.attr,
- &dev_attr_large_send.attr,
+ &dev_attr_isolation.attr,
NULL,
};
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index f4f3ca1393b..0b763396d5d 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -866,7 +866,7 @@ static const struct ethtool_ops qeth_l2_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_strings = qeth_core_get_strings,
.get_ethtool_stats = qeth_core_get_ethtool_stats,
- .get_stats_count = qeth_core_get_stats_count,
+ .get_sset_count = qeth_core_get_sset_count,
.get_drvinfo = qeth_core_get_drvinfo,
.get_settings = qeth_core_ethtool_get_settings,
};
@@ -874,7 +874,7 @@ static const struct ethtool_ops qeth_l2_ethtool_ops = {
static const struct ethtool_ops qeth_l2_osn_ops = {
.get_strings = qeth_core_get_strings,
.get_ethtool_stats = qeth_core_get_ethtool_stats,
- .get_stats_count = qeth_core_get_stats_count,
+ .get_sset_count = qeth_core_get_sset_count,
.get_drvinfo = qeth_core_get_drvinfo,
};
@@ -940,30 +940,17 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1);
recover_flag = card->state;
- rc = ccw_device_set_online(CARD_RDEV(card));
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
- return -EIO;
- }
- rc = ccw_device_set_online(CARD_WDEV(card));
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
- return -EIO;
- }
- rc = ccw_device_set_online(CARD_DDEV(card));
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
- return -EIO;
- }
-
rc = qeth_core_hardsetup_card(card);
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
+ rc = -ENODEV;
goto out_remove;
}
- if (!card->dev && qeth_l2_setup_netdev(card))
+ if (!card->dev && qeth_l2_setup_netdev(card)) {
+ rc = -ENODEV;
goto out_remove;
+ }
if (card->info.type != QETH_CARD_TYPE_OSN)
qeth_l2_send_setmac(card, &card->dev->dev_addr[0]);
@@ -983,12 +970,14 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
card->lan_online = 0;
return 0;
}
+ rc = -ENODEV;
goto out_remove;
} else
card->lan_online = 1;
if (card->info.type != QETH_CARD_TYPE_OSN) {
- qeth_set_large_send(card, card->options.large_send);
+ /* configure isolation level */
+ qeth_set_access_ctrl_online(card);
qeth_l2_process_vlans(card, 0);
}
@@ -997,6 +986,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
rc = qeth_init_qdio_queues(card);
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+ rc = -ENODEV;
goto out_remove;
}
card->state = CARD_STATE_SOFTSETUP;
@@ -1018,6 +1008,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
/* let user_space know that device is online */
kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
return 0;
+
out_remove:
card->use_hard_stop = 1;
qeth_l2_stop_card(card, 0);
@@ -1028,7 +1019,7 @@ out_remove:
card->state = CARD_STATE_RECOVER;
else
card->state = CARD_STATE_DOWN;
- return -ENODEV;
+ return rc;
}
static int qeth_l2_set_online(struct ccwgroup_device *gdev)
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index 9f143c83bba..321988fa9f7 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -60,5 +60,7 @@ void qeth_l3_del_vipa(struct qeth_card *, enum qeth_prot_versions, const u8 *);
int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions,
const u8 *);
+int qeth_l3_set_large_send(struct qeth_card *, enum qeth_large_send_types);
+int qeth_l3_set_rx_csum(struct qeth_card *, enum qeth_checksum_types);
#endif /* __QETH_L3_H__ */
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 073b6d35491..fd1b6ed3721 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -41,6 +41,32 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *,
static int __qeth_l3_set_online(struct ccwgroup_device *, int);
static int __qeth_l3_set_offline(struct ccwgroup_device *, int);
+int qeth_l3_set_large_send(struct qeth_card *card,
+ enum qeth_large_send_types type)
+{
+ int rc = 0;
+
+ card->options.large_send = type;
+ if (card->dev == NULL)
+ return 0;
+
+ if (card->options.large_send == QETH_LARGE_SEND_TSO) {
+ if (qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
+ card->dev->features |= NETIF_F_TSO | NETIF_F_SG |
+ NETIF_F_HW_CSUM;
+ } else {
+ card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
+ NETIF_F_HW_CSUM);
+ card->options.large_send = QETH_LARGE_SEND_NO;
+ rc = -EOPNOTSUPP;
+ }
+ } else {
+ card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
+ NETIF_F_HW_CSUM);
+ card->options.large_send = QETH_LARGE_SEND_NO;
+ }
+ return rc;
+}
static int qeth_l3_isxdigit(char *buf)
{
@@ -1439,6 +1465,35 @@ static int qeth_l3_send_checksum_command(struct qeth_card *card)
return 0;
}
+int qeth_l3_set_rx_csum(struct qeth_card *card,
+ enum qeth_checksum_types csum_type)
+{
+ int rc = 0;
+
+ if (card->options.checksum_type == HW_CHECKSUMMING) {
+ if ((csum_type != HW_CHECKSUMMING) &&
+ (card->state != CARD_STATE_DOWN)) {
+ rc = qeth_l3_send_simple_setassparms(card,
+ IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
+ if (rc)
+ return -EIO;
+ }
+ } else {
+ if (csum_type == HW_CHECKSUMMING) {
+ if (card->state != CARD_STATE_DOWN) {
+ if (!qeth_is_supported(card,
+ IPA_INBOUND_CHECKSUM))
+ return -EPERM;
+ rc = qeth_l3_send_checksum_command(card);
+ if (rc)
+ return -EIO;
+ }
+ }
+ }
+ card->options.checksum_type = csum_type;
+ return rc;
+}
+
static int qeth_l3_start_ipa_checksum(struct qeth_card *card)
{
int rc = 0;
@@ -1506,6 +1561,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card)
static int qeth_l3_start_ipassists(struct qeth_card *card)
{
QETH_DBF_TEXT(TRACE, 3, "strtipas");
+
+ qeth_set_access_ctrl_online(card); /* go on*/
qeth_l3_start_ipa_arp_processing(card); /* go on*/
qeth_l3_start_ipa_ip_fragmentation(card); /* go on*/
qeth_l3_start_ipa_source_mac(card); /* go on*/
@@ -2684,6 +2741,24 @@ static void qeth_tx_csum(struct sk_buff *skb)
*(__sum16 *)(skb->data + offset) = csum_fold(csum);
}
+static inline int qeth_l3_tso_elements(struct sk_buff *skb)
+{
+ unsigned long tcpd = (unsigned long)tcp_hdr(skb) +
+ tcp_hdr(skb)->doff * 4;
+ int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data);
+ int elements = PFN_UP(tcpd + tcpd_len) - PFN_DOWN(tcpd);
+ elements += skb_shinfo(skb)->nr_frags;
+ return elements;
+}
+
+static inline int qeth_l3_tso_check(struct sk_buff *skb)
+{
+ int len = ((unsigned long)tcp_hdr(skb) + tcp_hdr(skb)->doff * 4) -
+ (unsigned long)skb->data;
+ return (((unsigned long)skb->data & PAGE_MASK) !=
+ (((unsigned long)skb->data + len) & PAGE_MASK));
+}
+
static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
int rc;
@@ -2777,16 +2852,21 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* fix hardware limitation: as long as we do not have sbal
* chaining we can not send long frag lists
*/
- if ((large_send == QETH_LARGE_SEND_TSO) &&
- ((skb_shinfo(new_skb)->nr_frags + 2) > 16)) {
- if (skb_linearize(new_skb))
- goto tx_drop;
+ if (large_send == QETH_LARGE_SEND_TSO) {
+ if (qeth_l3_tso_elements(new_skb) + 1 > 16) {
+ if (skb_linearize(new_skb))
+ goto tx_drop;
+ if (card->options.performance_stats)
+ card->perf_stats.tx_lin++;
+ }
}
if ((large_send == QETH_LARGE_SEND_TSO) &&
(cast_type == RTN_UNSPEC)) {
hdr = (struct qeth_hdr *)skb_push(new_skb,
sizeof(struct qeth_hdr_tso));
+ if (qeth_l3_tso_check(new_skb))
+ QETH_DBF_MESSAGE(2, "tso skb misaligned\n");
memset(hdr, 0, sizeof(struct qeth_hdr_tso));
qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type);
qeth_tso_fill_header(card, hdr, new_skb);
@@ -2903,46 +2983,28 @@ static u32 qeth_l3_ethtool_get_rx_csum(struct net_device *dev)
static int qeth_l3_ethtool_set_rx_csum(struct net_device *dev, u32 data)
{
struct qeth_card *card = dev->ml_priv;
- enum qeth_card_states old_state;
enum qeth_checksum_types csum_type;
- if ((card->state != CARD_STATE_UP) &&
- (card->state != CARD_STATE_DOWN))
- return -EPERM;
-
if (data)
csum_type = HW_CHECKSUMMING;
else
csum_type = SW_CHECKSUMMING;
- if (card->options.checksum_type != csum_type) {
- old_state = card->state;
- if (card->state == CARD_STATE_UP)
- __qeth_l3_set_offline(card->gdev, 1);
- card->options.checksum_type = csum_type;
- if (old_state == CARD_STATE_UP)
- __qeth_l3_set_online(card->gdev, 1);
- }
- return 0;
+ return qeth_l3_set_rx_csum(card, csum_type);
}
static int qeth_l3_ethtool_set_tso(struct net_device *dev, u32 data)
{
struct qeth_card *card = dev->ml_priv;
+ int rc = 0;
if (data) {
- if (card->options.large_send == QETH_LARGE_SEND_NO) {
- if (card->info.type == QETH_CARD_TYPE_IQD)
- return -EPERM;
- else
- card->options.large_send = QETH_LARGE_SEND_TSO;
- dev->features |= NETIF_F_TSO;
- }
+ rc = qeth_l3_set_large_send(card, QETH_LARGE_SEND_TSO);
} else {
dev->features &= ~NETIF_F_TSO;
card->options.large_send = QETH_LARGE_SEND_NO;
}
- return 0;
+ return rc;
}
static const struct ethtool_ops qeth_l3_ethtool_ops = {
@@ -2957,7 +3019,7 @@ static const struct ethtool_ops qeth_l3_ethtool_ops = {
.set_tso = qeth_l3_ethtool_set_tso,
.get_strings = qeth_core_get_strings,
.get_ethtool_stats = qeth_core_get_ethtool_stats,
- .get_stats_count = qeth_core_get_stats_count,
+ .get_sset_count = qeth_core_get_sset_count,
.get_drvinfo = qeth_core_get_drvinfo,
.get_settings = qeth_core_ethtool_get_settings,
};
@@ -3058,6 +3120,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
NETIF_F_HW_VLAN_RX |
NETIF_F_HW_VLAN_FILTER;
card->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+ card->dev->gso_max_size = 15 * PAGE_SIZE;
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
return register_netdev(card->dev);
@@ -3154,32 +3217,19 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 1);
recover_flag = card->state;
- rc = ccw_device_set_online(CARD_RDEV(card));
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
- return -EIO;
- }
- rc = ccw_device_set_online(CARD_WDEV(card));
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
- return -EIO;
- }
- rc = ccw_device_set_online(CARD_DDEV(card));
- if (rc) {
- QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
- return -EIO;
- }
-
rc = qeth_core_hardsetup_card(card);
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
+ rc = -ENODEV;
goto out_remove;
}
qeth_l3_query_ipassists(card, QETH_PROT_IPV4);
- if (!card->dev && qeth_l3_setup_netdev(card))
+ if (!card->dev && qeth_l3_setup_netdev(card)) {
+ rc = -ENODEV;
goto out_remove;
+ }
card->state = CARD_STATE_HARDSETUP;
qeth_print_status_message(card);
@@ -3196,10 +3246,11 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
card->lan_online = 0;
return 0;
}
+ rc = -ENODEV;
goto out_remove;
} else
card->lan_online = 1;
- qeth_set_large_send(card, card->options.large_send);
+ qeth_l3_set_large_send(card, card->options.large_send);
rc = qeth_l3_setadapter_parms(card);
if (rc)
@@ -3218,6 +3269,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
rc = qeth_init_qdio_queues(card);
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+ rc = -ENODEV;
goto out_remove;
}
card->state = CARD_STATE_SOFTSETUP;
@@ -3248,7 +3300,7 @@ out_remove:
card->state = CARD_STATE_RECOVER;
else
card->state = CARD_STATE_DOWN;
- return -ENODEV;
+ return rc;
}
static int qeth_l3_set_online(struct ccwgroup_device *gdev)
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index c144b9924d5..3360b0941aa 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -293,31 +293,79 @@ static ssize_t qeth_l3_dev_checksum_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
+ enum qeth_checksum_types csum_type;
char *tmp;
+ int rc;
if (!card)
return -EINVAL;
- if ((card->state != CARD_STATE_DOWN) &&
- (card->state != CARD_STATE_RECOVER))
- return -EPERM;
-
tmp = strsep((char **) &buf, "\n");
if (!strcmp(tmp, "sw_checksumming"))
- card->options.checksum_type = SW_CHECKSUMMING;
+ csum_type = SW_CHECKSUMMING;
else if (!strcmp(tmp, "hw_checksumming"))
- card->options.checksum_type = HW_CHECKSUMMING;
+ csum_type = HW_CHECKSUMMING;
else if (!strcmp(tmp, "no_checksumming"))
- card->options.checksum_type = NO_CHECKSUMMING;
- else {
+ csum_type = NO_CHECKSUMMING;
+ else
return -EINVAL;
- }
+
+ rc = qeth_l3_set_rx_csum(card, csum_type);
+ if (rc)
+ return rc;
return count;
}
static DEVICE_ATTR(checksumming, 0644, qeth_l3_dev_checksum_show,
qeth_l3_dev_checksum_store);
+static ssize_t qeth_l3_dev_large_send_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+
+ if (!card)
+ return -EINVAL;
+
+ switch (card->options.large_send) {
+ case QETH_LARGE_SEND_NO:
+ return sprintf(buf, "%s\n", "no");
+ case QETH_LARGE_SEND_TSO:
+ return sprintf(buf, "%s\n", "TSO");
+ default:
+ return sprintf(buf, "%s\n", "N/A");
+ }
+}
+
+static ssize_t qeth_l3_dev_large_send_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct qeth_card *card = dev_get_drvdata(dev);
+ enum qeth_large_send_types type;
+ int rc = 0;
+ char *tmp;
+
+ if (!card)
+ return -EINVAL;
+ tmp = strsep((char **) &buf, "\n");
+ if (!strcmp(tmp, "no"))
+ type = QETH_LARGE_SEND_NO;
+ else if (!strcmp(tmp, "TSO"))
+ type = QETH_LARGE_SEND_TSO;
+ else
+ return -EINVAL;
+
+ if (card->options.large_send == type)
+ return count;
+ rc = qeth_l3_set_large_send(card, type);
+ if (rc)
+ return rc;
+ return count;
+}
+
+static DEVICE_ATTR(large_send, 0644, qeth_l3_dev_large_send_show,
+ qeth_l3_dev_large_send_store);
+
static struct attribute *qeth_l3_device_attrs[] = {
&dev_attr_route4.attr,
&dev_attr_route6.attr,
@@ -325,6 +373,7 @@ static struct attribute *qeth_l3_device_attrs[] = {
&dev_attr_broadcast_mode.attr,
&dev_attr_canonical_macaddr.attr,
&dev_attr_checksumming.attr,
+ &dev_attr_large_send.attr,
NULL,
};
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 2889e5f2dfd..9d0c941b7d3 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -31,6 +31,7 @@
#include <linux/miscdevice.h>
#include <linux/seq_file.h>
#include "zfcp_ext.h"
+#include "zfcp_fc.h"
#define ZFCP_BUS_ID_SIZE 20
@@ -80,48 +81,40 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
{
- struct ccw_device *ccwdev;
+ struct ccw_device *cdev;
struct zfcp_adapter *adapter;
struct zfcp_port *port;
struct zfcp_unit *unit;
- ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
- if (!ccwdev)
+ cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
+ if (!cdev)
return;
- if (ccw_device_set_online(ccwdev))
- goto out_ccwdev;
+ if (ccw_device_set_online(cdev))
+ goto out_ccw_device;
- mutex_lock(&zfcp_data.config_mutex);
- adapter = dev_get_drvdata(&ccwdev->dev);
+ adapter = zfcp_ccw_adapter_by_cdev(cdev);
if (!adapter)
- goto out_unlock;
- zfcp_adapter_get(adapter);
+ goto out_ccw_device;
port = zfcp_get_port_by_wwpn(adapter, wwpn);
if (!port)
goto out_port;
- zfcp_port_get(port);
unit = zfcp_unit_enqueue(port, lun);
if (IS_ERR(unit))
goto out_unit;
- mutex_unlock(&zfcp_data.config_mutex);
zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL);
zfcp_erp_wait(adapter);
flush_work(&unit->scsi_work);
- mutex_lock(&zfcp_data.config_mutex);
- zfcp_unit_put(unit);
out_unit:
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
out_port:
- zfcp_adapter_put(adapter);
-out_unlock:
- mutex_unlock(&zfcp_data.config_mutex);
-out_ccwdev:
- put_device(&ccwdev->dev);
+ zfcp_ccw_adapter_put(adapter);
+out_ccw_device:
+ put_device(&cdev->dev);
return;
}
@@ -167,7 +160,7 @@ static int __init zfcp_module_init(void)
int retval = -ENOMEM;
zfcp_data.gpn_ft_cache = zfcp_cache_hw_align("zfcp_gpn",
- sizeof(struct ct_iu_gpn_ft_req));
+ sizeof(struct zfcp_fc_gpn_ft_req));
if (!zfcp_data.gpn_ft_cache)
goto out;
@@ -182,12 +175,14 @@ static int __init zfcp_module_init(void)
goto out_sr_cache;
zfcp_data.gid_pn_cache = zfcp_cache_hw_align("zfcp_gid",
- sizeof(struct zfcp_gid_pn_data));
+ sizeof(struct zfcp_fc_gid_pn));
if (!zfcp_data.gid_pn_cache)
goto out_gid_cache;
- mutex_init(&zfcp_data.config_mutex);
- rwlock_init(&zfcp_data.config_lock);
+ zfcp_data.adisc_cache = zfcp_cache_hw_align("zfcp_adisc",
+ sizeof(struct zfcp_fc_els_adisc));
+ if (!zfcp_data.adisc_cache)
+ goto out_adisc_cache;
zfcp_data.scsi_transport_template =
fc_attach_transport(&zfcp_transport_functions);
@@ -200,7 +195,7 @@ static int __init zfcp_module_init(void)
goto out_misc;
}
- retval = zfcp_ccw_register();
+ retval = ccw_driver_register(&zfcp_ccw_driver);
if (retval) {
pr_err("The zfcp device driver could not register with "
"the common I/O layer\n");
@@ -216,6 +211,8 @@ out_ccw_register:
out_misc:
fc_release_transport(zfcp_data.scsi_transport_template);
out_transport:
+ kmem_cache_destroy(zfcp_data.adisc_cache);
+out_adisc_cache:
kmem_cache_destroy(zfcp_data.gid_pn_cache);
out_gid_cache:
kmem_cache_destroy(zfcp_data.sr_buffer_cache);
@@ -229,6 +226,20 @@ out:
module_init(zfcp_module_init);
+static void __exit zfcp_module_exit(void)
+{
+ ccw_driver_unregister(&zfcp_ccw_driver);
+ misc_deregister(&zfcp_cfdc_misc);
+ fc_release_transport(zfcp_data.scsi_transport_template);
+ kmem_cache_destroy(zfcp_data.adisc_cache);
+ kmem_cache_destroy(zfcp_data.gid_pn_cache);
+ kmem_cache_destroy(zfcp_data.sr_buffer_cache);
+ kmem_cache_destroy(zfcp_data.qtcb_cache);
+ kmem_cache_destroy(zfcp_data.gpn_ft_cache);
+}
+
+module_exit(zfcp_module_exit);
+
/**
* zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN
* @port: pointer to port to search for unit
@@ -238,12 +249,18 @@ module_init(zfcp_module_init);
*/
struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
{
+ unsigned long flags;
struct zfcp_unit *unit;
- list_for_each_entry(unit, &port->unit_list_head, list)
- if ((unit->fcp_lun == fcp_lun) &&
- !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE))
- return unit;
+ read_lock_irqsave(&port->unit_list_lock, flags);
+ list_for_each_entry(unit, &port->unit_list, list)
+ if (unit->fcp_lun == fcp_lun) {
+ if (!get_device(&unit->sysfs_device))
+ unit = NULL;
+ read_unlock_irqrestore(&port->unit_list_lock, flags);
+ return unit;
+ }
+ read_unlock_irqrestore(&port->unit_list_lock, flags);
return NULL;
}
@@ -257,18 +274,35 @@ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
u64 wwpn)
{
+ unsigned long flags;
struct zfcp_port *port;
- list_for_each_entry(port, &adapter->port_list_head, list)
- if ((port->wwpn == wwpn) &&
- !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE))
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list)
+ if (port->wwpn == wwpn) {
+ if (!get_device(&port->sysfs_device))
+ port = NULL;
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
return port;
+ }
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
return NULL;
}
-static void zfcp_sysfs_unit_release(struct device *dev)
+/**
+ * zfcp_unit_release - dequeue unit
+ * @dev: pointer to device
+ *
+ * waits until all work is done on unit and removes it then from the unit->list
+ * of the associated port.
+ */
+static void zfcp_unit_release(struct device *dev)
{
- kfree(container_of(dev, struct zfcp_unit, sysfs_device));
+ struct zfcp_unit *unit = container_of(dev, struct zfcp_unit,
+ sysfs_device);
+
+ put_device(&unit->port->sysfs_device);
+ kfree(unit);
}
/**
@@ -276,43 +310,40 @@ static void zfcp_sysfs_unit_release(struct device *dev)
* @port: pointer to port where unit is added
* @fcp_lun: FCP LUN of unit to be enqueued
* Returns: pointer to enqueued unit on success, ERR_PTR on error
- * Locks: config_mutex must be held to serialize changes to the unit list
*
* Sets up some unit internal structures and creates sysfs entry.
*/
struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
{
struct zfcp_unit *unit;
+ int retval = -ENOMEM;
- read_lock_irq(&zfcp_data.config_lock);
- if (zfcp_get_unit_by_lun(port, fcp_lun)) {
- read_unlock_irq(&zfcp_data.config_lock);
- return ERR_PTR(-EINVAL);
+ get_device(&port->sysfs_device);
+
+ unit = zfcp_get_unit_by_lun(port, fcp_lun);
+ if (unit) {
+ put_device(&unit->sysfs_device);
+ retval = -EEXIST;
+ goto err_out;
}
- read_unlock_irq(&zfcp_data.config_lock);
unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
if (!unit)
- return ERR_PTR(-ENOMEM);
-
- atomic_set(&unit->refcount, 0);
- init_waitqueue_head(&unit->remove_wq);
- INIT_WORK(&unit->scsi_work, zfcp_scsi_scan);
+ goto err_out;
unit->port = port;
unit->fcp_lun = fcp_lun;
+ unit->sysfs_device.parent = &port->sysfs_device;
+ unit->sysfs_device.release = zfcp_unit_release;
if (dev_set_name(&unit->sysfs_device, "0x%016llx",
(unsigned long long) fcp_lun)) {
kfree(unit);
- return ERR_PTR(-ENOMEM);
+ goto err_out;
}
- unit->sysfs_device.parent = &port->sysfs_device;
- unit->sysfs_device.release = zfcp_sysfs_unit_release;
- dev_set_drvdata(&unit->sysfs_device, unit);
+ retval = -EINVAL;
- /* mark unit unusable as long as sysfs registration is not complete */
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+ INIT_WORK(&unit->scsi_work, zfcp_scsi_scan);
spin_lock_init(&unit->latencies.lock);
unit->latencies.write.channel.min = 0xFFFFFFFF;
@@ -324,50 +355,30 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
if (device_register(&unit->sysfs_device)) {
put_device(&unit->sysfs_device);
- return ERR_PTR(-EINVAL);
+ goto err_out;
}
if (sysfs_create_group(&unit->sysfs_device.kobj,
- &zfcp_sysfs_unit_attrs)) {
- device_unregister(&unit->sysfs_device);
- return ERR_PTR(-EINVAL);
- }
+ &zfcp_sysfs_unit_attrs))
+ goto err_out_put;
- zfcp_unit_get(unit);
+ write_lock_irq(&port->unit_list_lock);
+ list_add_tail(&unit->list, &port->unit_list);
+ write_unlock_irq(&port->unit_list_lock);
- write_lock_irq(&zfcp_data.config_lock);
- list_add_tail(&unit->list, &port->unit_list_head);
- atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
- write_unlock_irq(&zfcp_data.config_lock);
-
- zfcp_port_get(port);
-
return unit;
-}
-/**
- * zfcp_unit_dequeue - dequeue unit
- * @unit: pointer to zfcp_unit
- *
- * waits until all work is done on unit and removes it then from the unit->list
- * of the associated port.
- */
-void zfcp_unit_dequeue(struct zfcp_unit *unit)
-{
- wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0);
- write_lock_irq(&zfcp_data.config_lock);
- list_del(&unit->list);
- write_unlock_irq(&zfcp_data.config_lock);
- zfcp_port_put(unit->port);
- sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs);
+err_out_put:
device_unregister(&unit->sysfs_device);
+err_out:
+ put_device(&port->sysfs_device);
+ return ERR_PTR(retval);
}
static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
{
- /* must only be called with zfcp_data.config_mutex taken */
adapter->pool.erp_req =
mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req));
if (!adapter->pool.erp_req)
@@ -405,9 +416,9 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
if (!adapter->pool.status_read_data)
return -ENOMEM;
- adapter->pool.gid_pn_data =
+ adapter->pool.gid_pn =
mempool_create_slab_pool(1, zfcp_data.gid_pn_cache);
- if (!adapter->pool.gid_pn_data)
+ if (!adapter->pool.gid_pn)
return -ENOMEM;
return 0;
@@ -415,7 +426,6 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
{
- /* zfcp_data.config_mutex must be held */
if (adapter->pool.erp_req)
mempool_destroy(adapter->pool.erp_req);
if (adapter->pool.scsi_req)
@@ -428,8 +438,8 @@ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
mempool_destroy(adapter->pool.status_read_req);
if (adapter->pool.status_read_data)
mempool_destroy(adapter->pool.status_read_data);
- if (adapter->pool.gid_pn_data)
- mempool_destroy(adapter->pool.gid_pn_data);
+ if (adapter->pool.gid_pn)
+ mempool_destroy(adapter->pool.gid_pn);
}
/**
@@ -497,53 +507,56 @@ static void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter)
* zfcp_adapter_enqueue - enqueue a new adapter to the list
* @ccw_device: pointer to the struct cc_device
*
- * Returns: 0 if a new adapter was successfully enqueued
- * -ENOMEM if alloc failed
+ * Returns: struct zfcp_adapter*
* Enqueues an adapter at the end of the adapter list in the driver data.
* All adapter internal structures are set up.
* Proc-fs entries are also created.
- * locks: config_mutex must be held to serialize changes to the adapter list
*/
-int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
+struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
- /*
- * Note: It is safe to release the list_lock, as any list changes
- * are protected by the config_mutex, which must be held to get here
- */
+ if (!get_device(&ccw_device->dev))
+ return ERR_PTR(-ENODEV);
adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL);
- if (!adapter)
- return -ENOMEM;
+ if (!adapter) {
+ put_device(&ccw_device->dev);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ kref_init(&adapter->ref);
ccw_device->handler = NULL;
adapter->ccw_device = ccw_device;
- atomic_set(&adapter->refcount, 0);
+
+ INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
+ INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
if (zfcp_qdio_setup(adapter))
- goto qdio_failed;
+ goto failed;
if (zfcp_allocate_low_mem_buffers(adapter))
- goto low_mem_buffers_failed;
+ goto failed;
if (zfcp_reqlist_alloc(adapter))
- goto low_mem_buffers_failed;
+ goto failed;
if (zfcp_dbf_adapter_register(adapter))
- goto debug_register_failed;
+ goto failed;
if (zfcp_setup_adapter_work_queue(adapter))
- goto work_queue_failed;
+ goto failed;
if (zfcp_fc_gs_setup(adapter))
- goto generic_services_failed;
+ goto failed;
+
+ rwlock_init(&adapter->port_list_lock);
+ INIT_LIST_HEAD(&adapter->port_list);
- init_waitqueue_head(&adapter->remove_wq);
init_waitqueue_head(&adapter->erp_ready_wq);
init_waitqueue_head(&adapter->erp_done_wqh);
- INIT_LIST_HEAD(&adapter->port_list_head);
INIT_LIST_HEAD(&adapter->erp_ready_head);
INIT_LIST_HEAD(&adapter->erp_running_head);
@@ -553,83 +566,85 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
rwlock_init(&adapter->abort_lock);
if (zfcp_erp_thread_setup(adapter))
- goto erp_thread_failed;
-
- INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
- INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later);
+ goto failed;
adapter->service_level.seq_print = zfcp_print_sl;
- /* mark adapter unusable as long as sysfs registration is not complete */
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
-
dev_set_drvdata(&ccw_device->dev, adapter);
if (sysfs_create_group(&ccw_device->dev.kobj,
&zfcp_sysfs_adapter_attrs))
- goto sysfs_failed;
-
- atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
+ goto failed;
if (!zfcp_adapter_scsi_register(adapter))
- return 0;
+ return adapter;
-sysfs_failed:
- zfcp_erp_thread_kill(adapter);
-erp_thread_failed:
- zfcp_fc_gs_destroy(adapter);
-generic_services_failed:
+failed:
+ zfcp_adapter_unregister(adapter);
+ return ERR_PTR(-ENOMEM);
+}
+
+void zfcp_adapter_unregister(struct zfcp_adapter *adapter)
+{
+ struct ccw_device *cdev = adapter->ccw_device;
+
+ cancel_work_sync(&adapter->scan_work);
+ cancel_work_sync(&adapter->stat_work);
zfcp_destroy_adapter_work_queue(adapter);
-work_queue_failed:
+
+ zfcp_fc_wka_ports_force_offline(adapter->gs);
+ zfcp_adapter_scsi_unregister(adapter);
+ sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs);
+
+ zfcp_erp_thread_kill(adapter);
zfcp_dbf_adapter_unregister(adapter->dbf);
-debug_register_failed:
- dev_set_drvdata(&ccw_device->dev, NULL);
- kfree(adapter->req_list);
-low_mem_buffers_failed:
- zfcp_free_low_mem_buffers(adapter);
-qdio_failed:
zfcp_qdio_destroy(adapter->qdio);
- kfree(adapter);
- return -ENOMEM;
+
+ zfcp_ccw_adapter_put(adapter); /* final put to release */
}
/**
- * zfcp_adapter_dequeue - remove the adapter from the resource list
- * @adapter: pointer to struct zfcp_adapter which should be removed
+ * zfcp_adapter_release - remove the adapter from the resource list
+ * @ref: pointer to struct kref
* locks: adapter list write lock is assumed to be held by caller
*/
-void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
+void zfcp_adapter_release(struct kref *ref)
{
- int retval = 0;
- unsigned long flags;
+ struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter,
+ ref);
+ struct ccw_device *cdev = adapter->ccw_device;
- cancel_work_sync(&adapter->stat_work);
- zfcp_fc_wka_ports_force_offline(adapter->gs);
- sysfs_remove_group(&adapter->ccw_device->dev.kobj,
- &zfcp_sysfs_adapter_attrs);
dev_set_drvdata(&adapter->ccw_device->dev, NULL);
- /* sanity check: no pending FSF requests */
- spin_lock_irqsave(&adapter->req_list_lock, flags);
- retval = zfcp_reqlist_isempty(adapter);
- spin_unlock_irqrestore(&adapter->req_list_lock, flags);
- if (!retval)
- return;
-
zfcp_fc_gs_destroy(adapter);
- zfcp_erp_thread_kill(adapter);
- zfcp_destroy_adapter_work_queue(adapter);
- zfcp_dbf_adapter_unregister(adapter->dbf);
zfcp_free_low_mem_buffers(adapter);
- zfcp_qdio_destroy(adapter->qdio);
kfree(adapter->req_list);
kfree(adapter->fc_stats);
kfree(adapter->stats_reset_data);
kfree(adapter);
+ put_device(&cdev->dev);
}
-static void zfcp_sysfs_port_release(struct device *dev)
+/**
+ * zfcp_device_unregister - remove port, unit from system
+ * @dev: reference to device which is to be removed
+ * @grp: related reference to attribute group
+ *
+ * Helper function to unregister port, unit from system
+ */
+void zfcp_device_unregister(struct device *dev,
+ const struct attribute_group *grp)
{
- kfree(container_of(dev, struct zfcp_port, sysfs_device));
+ sysfs_remove_group(&dev->kobj, grp);
+ device_unregister(dev);
+}
+
+static void zfcp_port_release(struct device *dev)
+{
+ struct zfcp_port *port = container_of(dev, struct zfcp_port,
+ sysfs_device);
+
+ zfcp_ccw_adapter_put(port->adapter);
+ kfree(port);
}
/**
@@ -639,7 +654,6 @@ static void zfcp_sysfs_port_release(struct device *dev)
* @status: initial status for the port
* @d_id: destination id of the remote port to be enqueued
* Returns: pointer to enqueued port on success, ERR_PTR on error
- * Locks: config_mutex must be held to serialize changes to the port list
*
* All port internal structures are set up and the sysfs entry is generated.
* d_id is used to enqueue ports with a well known address like the Directory
@@ -649,20 +663,24 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
u32 status, u32 d_id)
{
struct zfcp_port *port;
+ int retval = -ENOMEM;
- read_lock_irq(&zfcp_data.config_lock);
- if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
- read_unlock_irq(&zfcp_data.config_lock);
- return ERR_PTR(-EINVAL);
+ kref_get(&adapter->ref);
+
+ port = zfcp_get_port_by_wwpn(adapter, wwpn);
+ if (port) {
+ put_device(&port->sysfs_device);
+ retval = -EEXIST;
+ goto err_out;
}
- read_unlock_irq(&zfcp_data.config_lock);
port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL);
if (!port)
- return ERR_PTR(-ENOMEM);
+ goto err_out;
+
+ rwlock_init(&port->unit_list_lock);
+ INIT_LIST_HEAD(&port->unit_list);
- init_waitqueue_head(&port->remove_wq);
- INIT_LIST_HEAD(&port->unit_list_head);
INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup);
INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
INIT_WORK(&port->rport_work, zfcp_scsi_rport_work);
@@ -671,58 +689,38 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
port->d_id = d_id;
port->wwpn = wwpn;
port->rport_task = RPORT_NONE;
-
- /* mark port unusable as long as sysfs registration is not complete */
- atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
- atomic_set(&port->refcount, 0);
+ port->sysfs_device.parent = &adapter->ccw_device->dev;
+ port->sysfs_device.release = zfcp_port_release;
if (dev_set_name(&port->sysfs_device, "0x%016llx",
(unsigned long long)wwpn)) {
kfree(port);
- return ERR_PTR(-ENOMEM);
+ goto err_out;
}
- port->sysfs_device.parent = &adapter->ccw_device->dev;
- port->sysfs_device.release = zfcp_sysfs_port_release;
- dev_set_drvdata(&port->sysfs_device, port);
+ retval = -EINVAL;
if (device_register(&port->sysfs_device)) {
put_device(&port->sysfs_device);
- return ERR_PTR(-EINVAL);
+ goto err_out;
}
if (sysfs_create_group(&port->sysfs_device.kobj,
- &zfcp_sysfs_port_attrs)) {
- device_unregister(&port->sysfs_device);
- return ERR_PTR(-EINVAL);
- }
+ &zfcp_sysfs_port_attrs))
+ goto err_out_put;
- zfcp_port_get(port);
+ write_lock_irq(&adapter->port_list_lock);
+ list_add_tail(&port->list, &adapter->port_list);
+ write_unlock_irq(&adapter->port_list_lock);
- write_lock_irq(&zfcp_data.config_lock);
- list_add_tail(&port->list, &adapter->port_list_head);
- atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
- atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
+ atomic_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status);
- write_unlock_irq(&zfcp_data.config_lock);
-
- zfcp_adapter_get(adapter);
return port;
-}
-/**
- * zfcp_port_dequeue - dequeues a port from the port list of the adapter
- * @port: pointer to struct zfcp_port which should be removed
- */
-void zfcp_port_dequeue(struct zfcp_port *port)
-{
- write_lock_irq(&zfcp_data.config_lock);
- list_del(&port->list);
- write_unlock_irq(&zfcp_data.config_lock);
- wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
- cancel_work_sync(&port->rport_work); /* usually not necessary */
- zfcp_adapter_put(port->adapter);
- sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs);
+err_out_put:
device_unregister(&port->sysfs_device);
+err_out:
+ zfcp_ccw_adapter_put(adapter);
+ return ERR_PTR(retval);
}
/**
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index e08339428ec..c22cb72a5ae 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -13,28 +13,34 @@
#define ZFCP_MODEL_PRIV 0x4
-static int zfcp_ccw_suspend(struct ccw_device *cdev)
+static DEFINE_SPINLOCK(zfcp_ccw_adapter_ref_lock);
+struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *cdev)
{
- struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
-
- if (!adapter)
- return 0;
-
- mutex_lock(&zfcp_data.config_mutex);
+ struct zfcp_adapter *adapter;
+ unsigned long flags;
- zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL);
- zfcp_erp_wait(adapter);
+ spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags);
+ adapter = dev_get_drvdata(&cdev->dev);
+ if (adapter)
+ kref_get(&adapter->ref);
+ spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags);
+ return adapter;
+}
- mutex_unlock(&zfcp_data.config_mutex);
+void zfcp_ccw_adapter_put(struct zfcp_adapter *adapter)
+{
+ unsigned long flags;
- return 0;
+ spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags);
+ kref_put(&adapter->ref, zfcp_adapter_release);
+ spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags);
}
static int zfcp_ccw_activate(struct ccw_device *cdev)
{
- struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
if (!adapter)
return 0;
@@ -46,6 +52,8 @@ static int zfcp_ccw_activate(struct ccw_device *cdev)
zfcp_erp_wait(adapter);
flush_work(&adapter->scan_work);
+ zfcp_ccw_adapter_put(adapter);
+
return 0;
}
@@ -67,28 +75,28 @@ int zfcp_ccw_priv_sch(struct zfcp_adapter *adapter)
/**
* zfcp_ccw_probe - probe function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
*
* This function gets called by the common i/o layer for each FCP
* device found on the current system. This is only a stub to make cio
* work: To only allocate adapter resources for devices actually used,
* the allocation is deferred to the first call to ccw_set_online.
*/
-static int zfcp_ccw_probe(struct ccw_device *ccw_device)
+static int zfcp_ccw_probe(struct ccw_device *cdev)
{
return 0;
}
/**
* zfcp_ccw_remove - remove function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
*
* This function gets called by the common i/o layer and removes an adapter
* from the system. Task of this function is to get rid of all units and
* ports that belong to this adapter. And in addition all resources of this
* adapter will be freed too.
*/
-static void zfcp_ccw_remove(struct ccw_device *ccw_device)
+static void zfcp_ccw_remove(struct ccw_device *cdev)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port, *p;
@@ -96,49 +104,37 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device)
LIST_HEAD(unit_remove_lh);
LIST_HEAD(port_remove_lh);
- ccw_device_set_offline(ccw_device);
+ ccw_device_set_offline(cdev);
- mutex_lock(&zfcp_data.config_mutex);
- adapter = dev_get_drvdata(&ccw_device->dev);
+ adapter = zfcp_ccw_adapter_by_cdev(cdev);
if (!adapter)
- goto out;
- mutex_unlock(&zfcp_data.config_mutex);
+ return;
- cancel_work_sync(&adapter->scan_work);
-
- mutex_lock(&zfcp_data.config_mutex);
-
- /* this also removes the scsi devices, so call it first */
- zfcp_adapter_scsi_unregister(adapter);
-
- write_lock_irq(&zfcp_data.config_lock);
- list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
- list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
+ write_lock_irq(&adapter->port_list_lock);
+ list_for_each_entry_safe(port, p, &adapter->port_list, list) {
+ write_lock(&port->unit_list_lock);
+ list_for_each_entry_safe(unit, u, &port->unit_list, list)
list_move(&unit->list, &unit_remove_lh);
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
- &unit->status);
- }
+ write_unlock(&port->unit_list_lock);
list_move(&port->list, &port_remove_lh);
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
}
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
- write_unlock_irq(&zfcp_data.config_lock);
+ write_unlock_irq(&adapter->port_list_lock);
+ zfcp_ccw_adapter_put(adapter); /* put from zfcp_ccw_adapter_by_cdev */
- list_for_each_entry_safe(port, p, &port_remove_lh, list) {
- list_for_each_entry_safe(unit, u, &unit_remove_lh, list)
- zfcp_unit_dequeue(unit);
- zfcp_port_dequeue(port);
- }
- wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
- zfcp_adapter_dequeue(adapter);
+ list_for_each_entry_safe(unit, u, &unit_remove_lh, list)
+ zfcp_device_unregister(&unit->sysfs_device,
+ &zfcp_sysfs_unit_attrs);
-out:
- mutex_unlock(&zfcp_data.config_mutex);
+ list_for_each_entry_safe(port, p, &port_remove_lh, list)
+ zfcp_device_unregister(&port->sysfs_device,
+ &zfcp_sysfs_port_attrs);
+
+ zfcp_adapter_unregister(adapter);
}
/**
* zfcp_ccw_set_online - set_online function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
*
* This function gets called by the common i/o layer and sets an
* adapter into state online. The first call will allocate all
@@ -149,23 +145,20 @@ out:
* the SCSI stack, that the QDIO queues will be set up and that the
* adapter will be opened.
*/
-static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_online(struct ccw_device *cdev)
{
- struct zfcp_adapter *adapter;
- int ret = 0;
-
- mutex_lock(&zfcp_data.config_mutex);
- adapter = dev_get_drvdata(&ccw_device->dev);
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
if (!adapter) {
- ret = zfcp_adapter_enqueue(ccw_device);
- if (ret) {
- dev_err(&ccw_device->dev,
+ adapter = zfcp_adapter_enqueue(cdev);
+
+ if (IS_ERR(adapter)) {
+ dev_err(&cdev->dev,
"Setting up data structures for the "
"FCP adapter failed\n");
- goto out;
+ return PTR_ERR(adapter);
}
- adapter = dev_get_drvdata(&ccw_device->dev);
+ kref_get(&adapter->ref);
}
/* initialize request counter */
@@ -177,58 +170,61 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
"ccsonl2", NULL);
zfcp_erp_wait(adapter);
-out:
- mutex_unlock(&zfcp_data.config_mutex);
- if (!ret)
- flush_work(&adapter->scan_work);
- return ret;
+
+ flush_work(&adapter->scan_work);
+
+ zfcp_ccw_adapter_put(adapter);
+ return 0;
}
/**
* zfcp_ccw_set_offline - set_offline function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
*
* This function gets called by the common i/o layer and sets an adapter
* into state offline.
*/
-static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_offline(struct ccw_device *cdev)
{
- struct zfcp_adapter *adapter;
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+
+ if (!adapter)
+ return 0;
- mutex_lock(&zfcp_data.config_mutex);
- adapter = dev_get_drvdata(&ccw_device->dev);
zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL);
zfcp_erp_wait(adapter);
- mutex_unlock(&zfcp_data.config_mutex);
+
+ zfcp_ccw_adapter_put(adapter);
return 0;
}
/**
* zfcp_ccw_notify - ccw notify function
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
* @event: indicates if adapter was detached or attached
*
* This function gets called by the common i/o layer if an adapter has gone
* or reappeared.
*/
-static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
+static int zfcp_ccw_notify(struct ccw_device *cdev, int event)
{
- struct zfcp_adapter *adapter = dev_get_drvdata(&ccw_device->dev);
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+
+ if (!adapter)
+ return 1;
switch (event) {
case CIO_GONE:
- dev_warn(&adapter->ccw_device->dev,
- "The FCP device has been detached\n");
+ dev_warn(&cdev->dev, "The FCP device has been detached\n");
zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1", NULL);
break;
case CIO_NO_PATH:
- dev_warn(&adapter->ccw_device->dev,
+ dev_warn(&cdev->dev,
"The CHPID for the FCP device is offline\n");
zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2", NULL);
break;
case CIO_OPER:
- dev_info(&adapter->ccw_device->dev,
- "The FCP device is operational again\n");
+ dev_info(&cdev->dev, "The FCP device is operational again\n");
zfcp_erp_modify_adapter_status(adapter, "ccnoti3", NULL,
ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
@@ -236,11 +232,13 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
"ccnoti4", NULL);
break;
case CIO_BOXED:
- dev_warn(&adapter->ccw_device->dev, "The FCP device "
- "did not respond within the specified time\n");
+ dev_warn(&cdev->dev, "The FCP device did not respond within "
+ "the specified time\n");
zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5", NULL);
break;
}
+
+ zfcp_ccw_adapter_put(adapter);
return 1;
}
@@ -250,18 +248,16 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
*/
static void zfcp_ccw_shutdown(struct ccw_device *cdev)
{
- struct zfcp_adapter *adapter;
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
- mutex_lock(&zfcp_data.config_mutex);
- adapter = dev_get_drvdata(&cdev->dev);
if (!adapter)
- goto out;
+ return;
zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL);
zfcp_erp_wait(adapter);
zfcp_erp_thread_kill(adapter);
-out:
- mutex_unlock(&zfcp_data.config_mutex);
+
+ zfcp_ccw_adapter_put(adapter);
}
struct ccw_driver zfcp_ccw_driver = {
@@ -274,18 +270,7 @@ struct ccw_driver zfcp_ccw_driver = {
.set_offline = zfcp_ccw_set_offline,
.notify = zfcp_ccw_notify,
.shutdown = zfcp_ccw_shutdown,
- .freeze = zfcp_ccw_suspend,
+ .freeze = zfcp_ccw_set_offline,
.thaw = zfcp_ccw_activate,
.restore = zfcp_ccw_activate,
};
-
-/**
- * zfcp_ccw_register - ccw register function
- *
- * Registers the driver at the common i/o layer. This function will be called
- * at module load time/system start.
- */
-int __init zfcp_ccw_register(void)
-{
- return ccw_driver_register(&zfcp_ccw_driver);
-}
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
index ef681dfed0c..f932400e980 100644
--- a/drivers/s390/scsi/zfcp_cfdc.c
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -86,22 +86,17 @@ static int zfcp_cfdc_copy_to_user(void __user *user_buffer,
static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
{
char busid[9];
- struct ccw_device *ccwdev;
- struct zfcp_adapter *adapter = NULL;
+ struct ccw_device *cdev;
+ struct zfcp_adapter *adapter;
snprintf(busid, sizeof(busid), "0.0.%04x", devno);
- ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
- if (!ccwdev)
- goto out;
-
- adapter = dev_get_drvdata(&ccwdev->dev);
- if (!adapter)
- goto out_put;
-
- zfcp_adapter_get(adapter);
-out_put:
- put_device(&ccwdev->dev);
-out:
+ cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
+ if (!cdev)
+ return NULL;
+
+ adapter = zfcp_ccw_adapter_by_cdev(cdev);
+
+ put_device(&cdev->dev);
return adapter;
}
@@ -212,7 +207,6 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
retval = -ENXIO;
goto free_buffer;
}
- zfcp_adapter_get(adapter);
retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
data_user->control_file);
@@ -245,7 +239,7 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
free_sg:
zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
adapter_put:
- zfcp_adapter_put(adapter);
+ zfcp_ccw_adapter_put(adapter);
free_buffer:
kfree(data);
no_mem_sense:
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 215b70749e9..84450955ae1 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -13,6 +13,7 @@
#include <asm/debug.h>
#include "zfcp_dbf.h"
#include "zfcp_ext.h"
+#include "zfcp_fc.h"
static u32 dbfsize = 4;
@@ -177,8 +178,7 @@ void _zfcp_dbf_hba_fsf_response(const char *tag2, int level,
case FSF_QTCB_SEND_ELS:
send_els = (struct zfcp_send_els *)fsf_req->data;
- response->u.els.d_id = qtcb->bottom.support.d_id;
- response->u.els.ls_code = send_els->ls_code >> 24;
+ response->u.els.d_id = ntoh24(qtcb->bottom.support.d_id);
break;
case FSF_QTCB_ABORT_FCP_CMND:
@@ -348,7 +348,6 @@ static void zfcp_dbf_hba_view_response(char **p,
case FSF_QTCB_SEND_ELS:
zfcp_dbf_out(p, "d_id", "0x%06x", r->u.els.d_id);
- zfcp_dbf_out(p, "ls_code", "0x%02x", r->u.els.ls_code);
break;
case FSF_QTCB_ABORT_FCP_CMND:
@@ -677,14 +676,14 @@ void zfcp_dbf_rec_action(char *id2, struct zfcp_erp_action *erp_action)
/**
* zfcp_dbf_san_ct_request - trace event for issued CT request
* @fsf_req: request containing issued CT data
+ * @d_id: destination id where ct request is sent to
*/
-void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req, u32 d_id)
{
- struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
- struct zfcp_wka_port *wka_port = ct->wka_port;
- struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
struct zfcp_dbf *dbf = adapter->dbf;
- struct ct_hdr *hdr = sg_virt(ct->req);
+ struct fc_ct_hdr *hdr = sg_virt(ct->req);
struct zfcp_dbf_san_record *r = &dbf->san_buf;
struct zfcp_dbf_san_record_ct_request *oct = &r->u.ct_req;
int level = 3;
@@ -695,19 +694,18 @@ void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req)
strncpy(r->tag, "octc", ZFCP_DBF_TAG_SIZE);
r->fsf_reqid = fsf_req->req_id;
r->fsf_seqno = fsf_req->seq_no;
- r->s_id = fc_host_port_id(adapter->scsi_host);
- r->d_id = wka_port->d_id;
- oct->cmd_req_code = hdr->cmd_rsp_code;
- oct->revision = hdr->revision;
- oct->gs_type = hdr->gs_type;
- oct->gs_subtype = hdr->gs_subtype;
- oct->options = hdr->options;
- oct->max_res_size = hdr->max_res_size;
- oct->len = min((int)ct->req->length - (int)sizeof(struct ct_hdr),
+ oct->d_id = d_id;
+ oct->cmd_req_code = hdr->ct_cmd;
+ oct->revision = hdr->ct_rev;
+ oct->gs_type = hdr->ct_fs_type;
+ oct->gs_subtype = hdr->ct_fs_subtype;
+ oct->options = hdr->ct_options;
+ oct->max_res_size = hdr->ct_mr_size;
+ oct->len = min((int)ct->req->length - (int)sizeof(struct fc_ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
debug_event(dbf->san, level, r, sizeof(*r));
zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level,
- (void *)hdr + sizeof(struct ct_hdr), oct->len);
+ (void *)hdr + sizeof(struct fc_ct_hdr), oct->len);
spin_unlock_irqrestore(&dbf->san_lock, flags);
}
@@ -717,10 +715,9 @@ void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req)
*/
void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req)
{
- struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
- struct zfcp_wka_port *wka_port = ct->wka_port;
- struct zfcp_adapter *adapter = wka_port->adapter;
- struct ct_hdr *hdr = sg_virt(ct->resp);
+ struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
+ struct fc_ct_hdr *hdr = sg_virt(ct->resp);
struct zfcp_dbf *dbf = adapter->dbf;
struct zfcp_dbf_san_record *r = &dbf->san_buf;
struct zfcp_dbf_san_record_ct_response *rct = &r->u.ct_resp;
@@ -732,25 +729,23 @@ void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req)
strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE);
r->fsf_reqid = fsf_req->req_id;
r->fsf_seqno = fsf_req->seq_no;
- r->s_id = wka_port->d_id;
- r->d_id = fc_host_port_id(adapter->scsi_host);
- rct->cmd_rsp_code = hdr->cmd_rsp_code;
- rct->revision = hdr->revision;
- rct->reason_code = hdr->reason_code;
- rct->expl = hdr->reason_code_expl;
- rct->vendor_unique = hdr->vendor_unique;
- rct->max_res_size = hdr->max_res_size;
- rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr),
+ rct->cmd_rsp_code = hdr->ct_cmd;
+ rct->revision = hdr->ct_rev;
+ rct->reason_code = hdr->ct_reason;
+ rct->expl = hdr->ct_explan;
+ rct->vendor_unique = hdr->ct_vendor;
+ rct->max_res_size = hdr->ct_mr_size;
+ rct->len = min((int)ct->resp->length - (int)sizeof(struct fc_ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
debug_event(dbf->san, level, r, sizeof(*r));
zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level,
- (void *)hdr + sizeof(struct ct_hdr), rct->len);
+ (void *)hdr + sizeof(struct fc_ct_hdr), rct->len);
spin_unlock_irqrestore(&dbf->san_lock, flags);
}
static void zfcp_dbf_san_els(const char *tag, int level,
- struct zfcp_fsf_req *fsf_req, u32 s_id, u32 d_id,
- u8 ls_code, void *buffer, int buflen)
+ struct zfcp_fsf_req *fsf_req, u32 d_id,
+ void *buffer, int buflen)
{
struct zfcp_adapter *adapter = fsf_req->adapter;
struct zfcp_dbf *dbf = adapter->dbf;
@@ -762,9 +757,7 @@ static void zfcp_dbf_san_els(const char *tag, int level,
strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE);
rec->fsf_reqid = fsf_req->req_id;
rec->fsf_seqno = fsf_req->seq_no;
- rec->s_id = s_id;
- rec->d_id = d_id;
- rec->u.els.ls_code = ls_code;
+ rec->u.els.d_id = d_id;
debug_event(dbf->san, level, rec, sizeof(*rec));
zfcp_dbf_hexdump(dbf->san, rec, sizeof(*rec), level,
buffer, min(buflen, ZFCP_DBF_SAN_MAX_PAYLOAD));
@@ -777,12 +770,11 @@ static void zfcp_dbf_san_els(const char *tag, int level,
*/
void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req)
{
- struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data;
+ struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data;
+ u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id);
- zfcp_dbf_san_els("oels", 2, fsf_req,
- fc_host_port_id(els->adapter->scsi_host),
- els->d_id, *(u8 *) sg_virt(els->req),
- sg_virt(els->req), els->req->length);
+ zfcp_dbf_san_els("oels", 2, fsf_req, d_id,
+ sg_virt(els->req), els->req->length);
}
/**
@@ -791,12 +783,11 @@ void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req)
*/
void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req)
{
- struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data;
+ struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data;
+ u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id);
- zfcp_dbf_san_els("rels", 2, fsf_req, els->d_id,
- fc_host_port_id(els->adapter->scsi_host),
- *(u8 *)sg_virt(els->req), sg_virt(els->resp),
- els->resp->length);
+ zfcp_dbf_san_els("rels", 2, fsf_req, d_id,
+ sg_virt(els->resp), els->resp->length);
}
/**
@@ -805,16 +796,13 @@ void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req)
*/
void zfcp_dbf_san_incoming_els(struct zfcp_fsf_req *fsf_req)
{
- struct zfcp_adapter *adapter = fsf_req->adapter;
struct fsf_status_read_buffer *buf =
(struct fsf_status_read_buffer *)fsf_req->data;
int length = (int)buf->length -
(int)((void *)&buf->payload - (void *)buf);
- zfcp_dbf_san_els("iels", 1, fsf_req, buf->d_id,
- fc_host_port_id(adapter->scsi_host),
- buf->payload.data[0], (void *)buf->payload.data,
- length);
+ zfcp_dbf_san_els("iels", 1, fsf_req, ntoh24(buf->d_id),
+ (void *)buf->payload.data, length);
}
static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view,
@@ -829,11 +817,10 @@ static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_tag(&p, "tag", r->tag);
zfcp_dbf_out(&p, "fsf_reqid", "0x%0Lx", r->fsf_reqid);
zfcp_dbf_out(&p, "fsf_seqno", "0x%08x", r->fsf_seqno);
- zfcp_dbf_out(&p, "s_id", "0x%06x", r->s_id);
- zfcp_dbf_out(&p, "d_id", "0x%06x", r->d_id);
if (strncmp(r->tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) {
struct zfcp_dbf_san_record_ct_request *ct = &r->u.ct_req;
+ zfcp_dbf_out(&p, "d_id", "0x%06x", ct->d_id);
zfcp_dbf_out(&p, "cmd_req_code", "0x%04x", ct->cmd_req_code);
zfcp_dbf_out(&p, "revision", "0x%02x", ct->revision);
zfcp_dbf_out(&p, "gs_type", "0x%02x", ct->gs_type);
@@ -852,7 +839,7 @@ static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view,
strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) {
struct zfcp_dbf_san_record_els *els = &r->u.els;
- zfcp_dbf_out(&p, "ls_code", "0x%02x", els->ls_code);
+ zfcp_dbf_out(&p, "d_id", "0x%06x", els->d_id);
}
return p - out_buf;
}
@@ -870,8 +857,9 @@ void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level,
struct zfcp_dbf_scsi_record *rec = &dbf->scsi_buf;
struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec;
unsigned long flags;
- struct fcp_rsp_iu *fcp_rsp;
- char *fcp_rsp_info = NULL, *fcp_sns_info = NULL;
+ struct fcp_resp_with_ext *fcp_rsp;
+ struct fcp_resp_rsp_info *fcp_rsp_info = NULL;
+ char *fcp_sns_info = NULL;
int offset = 0, buflen = 0;
spin_lock_irqsave(&dbf->scsi_lock, flags);
@@ -895,20 +883,22 @@ void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level,
rec->scsi_allowed = scsi_cmnd->allowed;
}
if (fsf_req != NULL) {
- fcp_rsp = (struct fcp_rsp_iu *)
- &(fsf_req->qtcb->bottom.io.fcp_rsp);
- fcp_rsp_info = (unsigned char *) &fcp_rsp[1];
- fcp_sns_info =
- zfcp_get_fcp_sns_info_ptr(fcp_rsp);
-
- rec->rsp_validity = fcp_rsp->validity.value;
- rec->rsp_scsi_status = fcp_rsp->scsi_status;
- rec->rsp_resid = fcp_rsp->fcp_resid;
- if (fcp_rsp->validity.bits.fcp_rsp_len_valid)
- rec->rsp_code = *(fcp_rsp_info + 3);
- if (fcp_rsp->validity.bits.fcp_sns_len_valid) {
- buflen = min((int)fcp_rsp->fcp_sns_len,
- ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO);
+ fcp_rsp = (struct fcp_resp_with_ext *)
+ &(fsf_req->qtcb->bottom.io.fcp_rsp);
+ fcp_rsp_info = (struct fcp_resp_rsp_info *)
+ &fcp_rsp[1];
+ fcp_sns_info = (char *) &fcp_rsp[1];
+ if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL)
+ fcp_sns_info += fcp_rsp->ext.fr_sns_len;
+
+ rec->rsp_validity = fcp_rsp->resp.fr_flags;
+ rec->rsp_scsi_status = fcp_rsp->resp.fr_status;
+ rec->rsp_resid = fcp_rsp->ext.fr_resid;
+ if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL)
+ rec->rsp_code = fcp_rsp_info->rsp_code;
+ if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) {
+ buflen = min(fcp_rsp->ext.fr_sns_len,
+ (u32)ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO);
rec->sns_info_len = buflen;
memcpy(rec->sns_info, fcp_sns_info,
min(buflen,
@@ -1067,6 +1057,8 @@ err_out:
*/
void zfcp_dbf_adapter_unregister(struct zfcp_dbf *dbf)
{
+ if (!dbf)
+ return;
debug_unregister(dbf->scsi);
debug_unregister(dbf->san);
debug_unregister(dbf->hba);
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 6b1461e8f84..8b7fd9a1033 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -22,6 +22,7 @@
#ifndef ZFCP_DBF_H
#define ZFCP_DBF_H
+#include <scsi/fc/fc_fcp.h>
#include "zfcp_ext.h"
#include "zfcp_fsf.h"
#include "zfcp_def.h"
@@ -122,7 +123,6 @@ struct zfcp_dbf_hba_record_response {
} unit;
struct {
u32 d_id;
- u8 ls_code;
} els;
} u;
} __attribute__ ((packed));
@@ -166,6 +166,7 @@ struct zfcp_dbf_san_record_ct_request {
u8 options;
u16 max_res_size;
u32 len;
+ u32 d_id;
} __attribute__ ((packed));
struct zfcp_dbf_san_record_ct_response {
@@ -179,16 +180,13 @@ struct zfcp_dbf_san_record_ct_response {
} __attribute__ ((packed));
struct zfcp_dbf_san_record_els {
- u8 ls_code;
- u32 len;
+ u32 d_id;
} __attribute__ ((packed));
struct zfcp_dbf_san_record {
u8 tag[ZFCP_DBF_TAG_SIZE];
u64 fsf_reqid;
u32 fsf_seqno;
- u32 s_id;
- u32 d_id;
union {
struct zfcp_dbf_san_record_ct_request ct_req;
struct zfcp_dbf_san_record_ct_response ct_resp;
@@ -343,7 +341,7 @@ static inline
void zfcp_dbf_scsi_devreset(const char *tag, u8 flag, struct zfcp_unit *unit,
struct scsi_cmnd *scsi_cmnd)
{
- zfcp_dbf_scsi(flag == FCP_TARGET_RESET ? "trst" : "lrst", tag, 1,
+ zfcp_dbf_scsi(flag == FCP_TMF_TGT_RESET ? "trst" : "lrst", tag, 1,
unit->port->adapter->dbf, scsi_cmnd, NULL, 0);
}
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 7da2fad8f51..e1b5b88e2dd 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -71,131 +71,6 @@
/* timeout value for "default timer" for fsf requests */
#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ)
-/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/
-
-/* task attribute values in FCP-2 FCP_CMND IU */
-#define SIMPLE_Q 0
-#define HEAD_OF_Q 1
-#define ORDERED_Q 2
-#define ACA_Q 4
-#define UNTAGGED 5
-
-/* task management flags in FCP-2 FCP_CMND IU */
-#define FCP_CLEAR_ACA 0x40
-#define FCP_TARGET_RESET 0x20
-#define FCP_LOGICAL_UNIT_RESET 0x10
-#define FCP_CLEAR_TASK_SET 0x04
-#define FCP_ABORT_TASK_SET 0x02
-
-#define FCP_CDB_LENGTH 16
-
-#define ZFCP_DID_MASK 0x00FFFFFF
-
-/* FCP(-2) FCP_CMND IU */
-struct fcp_cmnd_iu {
- u64 fcp_lun; /* FCP logical unit number */
- u8 crn; /* command reference number */
- u8 reserved0:5; /* reserved */
- u8 task_attribute:3; /* task attribute */
- u8 task_management_flags; /* task management flags */
- u8 add_fcp_cdb_length:6; /* additional FCP_CDB length */
- u8 rddata:1; /* read data */
- u8 wddata:1; /* write data */
- u8 fcp_cdb[FCP_CDB_LENGTH];
-} __attribute__((packed));
-
-/* FCP(-2) FCP_RSP IU */
-struct fcp_rsp_iu {
- u8 reserved0[10];
- union {
- struct {
- u8 reserved1:3;
- u8 fcp_conf_req:1;
- u8 fcp_resid_under:1;
- u8 fcp_resid_over:1;
- u8 fcp_sns_len_valid:1;
- u8 fcp_rsp_len_valid:1;
- } bits;
- u8 value;
- } validity;
- u8 scsi_status;
- u32 fcp_resid;
- u32 fcp_sns_len;
- u32 fcp_rsp_len;
-} __attribute__((packed));
-
-
-#define RSP_CODE_GOOD 0
-#define RSP_CODE_LENGTH_MISMATCH 1
-#define RSP_CODE_FIELD_INVALID 2
-#define RSP_CODE_RO_MISMATCH 3
-#define RSP_CODE_TASKMAN_UNSUPP 4
-#define RSP_CODE_TASKMAN_FAILED 5
-
-/* see fc-fs */
-#define LS_RSCN 0x61
-#define LS_LOGO 0x05
-#define LS_PLOGI 0x03
-
-struct fcp_rscn_head {
- u8 command;
- u8 page_length; /* always 0x04 */
- u16 payload_len;
-} __attribute__((packed));
-
-struct fcp_rscn_element {
- u8 reserved:2;
- u8 event_qual:4;
- u8 addr_format:2;
- u32 nport_did:24;
-} __attribute__((packed));
-
-/* see fc-ph */
-struct fcp_logo {
- u32 command;
- u32 nport_did;
- u64 nport_wwpn;
-} __attribute__((packed));
-
-/*
- * FC-FS stuff
- */
-#define R_A_TOV 10 /* seconds */
-
-#define ZFCP_LS_RLS 0x0f
-#define ZFCP_LS_ADISC 0x52
-#define ZFCP_LS_RPS 0x56
-#define ZFCP_LS_RSCN 0x61
-#define ZFCP_LS_RNID 0x78
-
-struct zfcp_ls_adisc {
- u8 code;
- u8 field[3];
- u32 hard_nport_id;
- u64 wwpn;
- u64 wwnn;
- u32 nport_id;
-} __attribute__ ((packed));
-
-/*
- * FC-GS-2 stuff
- */
-#define ZFCP_CT_REVISION 0x01
-#define ZFCP_CT_DIRECTORY_SERVICE 0xFC
-#define ZFCP_CT_NAME_SERVER 0x02
-#define ZFCP_CT_SYNCHRONOUS 0x00
-#define ZFCP_CT_SCSI_FCP 0x08
-#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
-#define ZFCP_CT_GID_PN 0x0121
-#define ZFCP_CT_GPN_FT 0x0172
-#define ZFCP_CT_ACCEPT 0x8002
-#define ZFCP_CT_REJECT 0x8001
-
-/*
- * FC-GS-4 stuff
- */
-#define ZFCP_CT_TIMEOUT (3 * R_A_TOV)
-
/*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
/*
@@ -205,7 +80,6 @@ struct zfcp_ls_adisc {
#define ZFCP_COMMON_FLAGS 0xfff00000
/* common status bits */
-#define ZFCP_STATUS_COMMON_REMOVE 0x80000000
#define ZFCP_STATUS_COMMON_RUNNING 0x40000000
#define ZFCP_STATUS_COMMON_ERP_FAILED 0x20000000
#define ZFCP_STATUS_COMMON_UNBLOCKED 0x10000000
@@ -222,21 +96,10 @@ struct zfcp_ls_adisc {
#define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100
#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200
-/* FC-PH/FC-GS well-known address identifiers for generic services */
-#define ZFCP_DID_WKA 0xFFFFF0
-
/* remote port status */
#define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001
#define ZFCP_STATUS_PORT_LINK_TEST 0x00000002
-/* well known address (WKA) port status*/
-enum zfcp_wka_status {
- ZFCP_WKA_PORT_OFFLINE,
- ZFCP_WKA_PORT_CLOSING,
- ZFCP_WKA_PORT_OPENING,
- ZFCP_WKA_PORT_ONLINE,
-};
-
/* logical unit status */
#define ZFCP_STATUS_UNIT_SHARED 0x00000004
#define ZFCP_STATUS_UNIT_READONLY 0x00000008
@@ -247,10 +110,7 @@ enum zfcp_wka_status {
#define ZFCP_STATUS_FSFREQ_CLEANUP 0x00000010
#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED 0x00000040
#define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED 0x00000080
-#define ZFCP_STATUS_FSFREQ_ABORTED 0x00000100
#define ZFCP_STATUS_FSFREQ_TMFUNCFAILED 0x00000200
-#define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP 0x00000400
-#define ZFCP_STATUS_FSFREQ_RETRY 0x00000800
#define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000
/************************* STRUCTURE DEFINITIONS *****************************/
@@ -265,125 +125,10 @@ struct zfcp_adapter_mempool {
mempool_t *scsi_abort;
mempool_t *status_read_req;
mempool_t *status_read_data;
- mempool_t *gid_pn_data;
+ mempool_t *gid_pn;
mempool_t *qtcb_pool;
};
-/*
- * header for CT_IU
- */
-struct ct_hdr {
- u8 revision; // 0x01
- u8 in_id[3]; // 0x00
- u8 gs_type; // 0xFC Directory Service
- u8 gs_subtype; // 0x02 Name Server
- u8 options; // 0x00 single bidirectional exchange
- u8 reserved0;
- u16 cmd_rsp_code; // 0x0121 GID_PN, or 0x0100 GA_NXT
- u16 max_res_size; // <= (4096 - 16) / 4
- u8 reserved1;
- u8 reason_code;
- u8 reason_code_expl;
- u8 vendor_unique;
-} __attribute__ ((packed));
-
-/* nameserver request CT_IU -- for requests where
- * a port name is required */
-struct ct_iu_gid_pn_req {
- struct ct_hdr header;
- u64 wwpn;
-} __attribute__ ((packed));
-
-/* FS_ACC IU and data unit for GID_PN nameserver request */
-struct ct_iu_gid_pn_resp {
- struct ct_hdr header;
- u32 d_id;
-} __attribute__ ((packed));
-
-struct ct_iu_gpn_ft_req {
- struct ct_hdr header;
- u8 flags;
- u8 domain_id_scope;
- u8 area_id_scope;
- u8 fc4_type;
-} __attribute__ ((packed));
-
-
-/**
- * struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct
- * @wka_port: port where the request is sent to
- * @req: scatter-gather list for request
- * @resp: scatter-gather list for response
- * @handler: handler function (called for response to the request)
- * @handler_data: data passed to handler function
- * @completion: completion for synchronization purposes
- * @status: used to pass error status to calling function
- */
-struct zfcp_send_ct {
- struct zfcp_wka_port *wka_port;
- struct scatterlist *req;
- struct scatterlist *resp;
- void (*handler)(unsigned long);
- unsigned long handler_data;
- struct completion *completion;
- int status;
-};
-
-/* used for name server requests in error recovery */
-struct zfcp_gid_pn_data {
- struct zfcp_send_ct ct;
- struct scatterlist req;
- struct scatterlist resp;
- struct ct_iu_gid_pn_req ct_iu_req;
- struct ct_iu_gid_pn_resp ct_iu_resp;
- struct zfcp_port *port;
-};
-
-/**
- * struct zfcp_send_els - used to pass parameters to function zfcp_fsf_send_els
- * @adapter: adapter where request is sent from
- * @port: port where ELS is destinated (port reference count has to be increased)
- * @d_id: destiniation id of port where request is sent to
- * @req: scatter-gather list for request
- * @resp: scatter-gather list for response
- * @handler: handler function (called for response to the request)
- * @handler_data: data passed to handler function
- * @completion: completion for synchronization purposes
- * @ls_code: hex code of ELS command
- * @status: used to pass error status to calling function
- */
-struct zfcp_send_els {
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
- u32 d_id;
- struct scatterlist *req;
- struct scatterlist *resp;
- void (*handler)(unsigned long);
- unsigned long handler_data;
- struct completion *completion;
- int ls_code;
- int status;
-};
-
-struct zfcp_wka_port {
- struct zfcp_adapter *adapter;
- wait_queue_head_t completion_wq;
- enum zfcp_wka_status status;
- atomic_t refcount;
- u32 d_id;
- u32 handle;
- struct mutex mutex;
- struct delayed_work work;
-};
-
-struct zfcp_wka_ports {
- struct zfcp_wka_port ms; /* management service */
- struct zfcp_wka_port ts; /* time service */
- struct zfcp_wka_port ds; /* directory service */
- struct zfcp_wka_port as; /* alias service */
- struct zfcp_wka_port ks; /* key distribution service */
-};
-
struct zfcp_qdio_queue {
struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q];
u8 first; /* index of next free bfr in queue */
@@ -446,9 +191,7 @@ struct zfcp_qdio {
};
struct zfcp_adapter {
- atomic_t refcount; /* reference count */
- wait_queue_head_t remove_wq; /* can be used to wait for
- refcount drop to zero */
+ struct kref ref;
u64 peer_wwnn; /* P2P peer WWNN */
u64 peer_wwpn; /* P2P peer WWPN */
u32 peer_d_id; /* P2P peer D_ID */
@@ -461,7 +204,8 @@ struct zfcp_adapter {
u32 hardware_version; /* of FCP channel */
u16 timer_ticks; /* time int for a tick */
struct Scsi_Host *scsi_host; /* Pointer to mid-layer */
- struct list_head port_list_head; /* remote port list */
+ struct list_head port_list; /* remote port list */
+ rwlock_t port_list_lock; /* port list lock */
unsigned long req_no; /* unique FSF req number */
struct list_head *req_list; /* list of pending reqs */
spinlock_t req_list_lock; /* request list lock */
@@ -485,7 +229,7 @@ struct zfcp_adapter {
u32 erp_low_mem_count; /* nr of erp actions waiting
for memory */
struct task_struct *erp_thread;
- struct zfcp_wka_ports *gs; /* generic services */
+ struct zfcp_fc_wka_ports *gs; /* generic services */
struct zfcp_dbf *dbf; /* debug traces */
struct zfcp_adapter_mempool pool; /* Adapter memory pools */
struct fc_host_statistics *fc_stats;
@@ -500,11 +244,9 @@ struct zfcp_port {
struct device sysfs_device; /* sysfs device */
struct fc_rport *rport; /* rport of fc transport class */
struct list_head list; /* list of remote ports */
- atomic_t refcount; /* reference count */
- wait_queue_head_t remove_wq; /* can be used to wait for
- refcount drop to zero */
struct zfcp_adapter *adapter; /* adapter used to access port */
- struct list_head unit_list_head; /* head of logical unit list */
+ struct list_head unit_list; /* head of logical unit list */
+ rwlock_t unit_list_lock; /* unit list lock */
atomic_t status; /* status of this remote port */
u64 wwnn; /* WWNN if known */
u64 wwpn; /* WWPN */
@@ -523,9 +265,6 @@ struct zfcp_port {
struct zfcp_unit {
struct device sysfs_device; /* sysfs device */
struct list_head list; /* list of logical units */
- atomic_t refcount; /* reference count */
- wait_queue_head_t remove_wq; /* can be used to wait for
- refcount drop to zero */
struct zfcp_port *port; /* remote port of unit */
atomic_t status; /* status of this logical unit */
u64 fcp_lun; /* own FCP_LUN */
@@ -601,14 +340,11 @@ struct zfcp_fsf_req {
struct zfcp_data {
struct scsi_host_template scsi_host_template;
struct scsi_transport_template *scsi_transport_template;
- rwlock_t config_lock; /* serialises changes
- to adapter/port/unit
- lists */
- struct mutex config_mutex;
struct kmem_cache *gpn_ft_cache;
struct kmem_cache *qtcb_cache;
struct kmem_cache *sr_buffer_cache;
struct kmem_cache *gid_pn_cache;
+ struct kmem_cache *adisc_cache;
};
/********************** ZFCP SPECIFIC DEFINES ********************************/
@@ -657,47 +393,4 @@ zfcp_reqlist_find_safe(struct zfcp_adapter *adapter, struct zfcp_fsf_req *req)
return NULL;
}
-/*
- * functions needed for reference/usage counting
- */
-
-static inline void
-zfcp_unit_get(struct zfcp_unit *unit)
-{
- atomic_inc(&unit->refcount);
-}
-
-static inline void
-zfcp_unit_put(struct zfcp_unit *unit)
-{
- if (atomic_dec_return(&unit->refcount) == 0)
- wake_up(&unit->remove_wq);
-}
-
-static inline void
-zfcp_port_get(struct zfcp_port *port)
-{
- atomic_inc(&port->refcount);
-}
-
-static inline void
-zfcp_port_put(struct zfcp_port *port)
-{
- if (atomic_dec_return(&port->refcount) == 0)
- wake_up(&port->remove_wq);
-}
-
-static inline void
-zfcp_adapter_get(struct zfcp_adapter *adapter)
-{
- atomic_inc(&adapter->refcount);
-}
-
-static inline void
-zfcp_adapter_put(struct zfcp_adapter *adapter)
-{
- if (atomic_dec_return(&adapter->refcount) == 0)
- wake_up(&adapter->remove_wq);
-}
-
#endif /* ZFCP_DEF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index f73e2180f33..b51a11a82e6 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -99,9 +99,12 @@ static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
zfcp_erp_action_dismiss(&port->erp_action);
- else
- list_for_each_entry(unit, &port->unit_list_head, list)
- zfcp_erp_action_dismiss_unit(unit);
+ else {
+ read_lock(&port->unit_list_lock);
+ list_for_each_entry(unit, &port->unit_list, list)
+ zfcp_erp_action_dismiss_unit(unit);
+ read_unlock(&port->unit_list_lock);
+ }
}
static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
@@ -110,9 +113,12 @@ static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
zfcp_erp_action_dismiss(&adapter->erp_action);
- else
- list_for_each_entry(port, &adapter->port_list_head, list)
+ else {
+ read_lock(&adapter->port_list_lock);
+ list_for_each_entry(port, &adapter->port_list, list)
zfcp_erp_action_dismiss_port(port);
+ read_unlock(&adapter->port_list_lock);
+ }
}
static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter,
@@ -168,7 +174,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
switch (need) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
- zfcp_unit_get(unit);
+ if (!get_device(&unit->sysfs_device))
+ return NULL;
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
erp_action = &unit->erp_action;
if (!(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_RUNNING))
@@ -177,7 +184,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
case ZFCP_ERP_ACTION_REOPEN_PORT:
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
- zfcp_port_get(port);
+ if (!get_device(&port->sysfs_device))
+ return NULL;
zfcp_erp_action_dismiss_port(port);
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
erp_action = &port->erp_action;
@@ -186,7 +194,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- zfcp_adapter_get(adapter);
+ kref_get(&adapter->ref);
zfcp_erp_action_dismiss_adapter(adapter);
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
erp_action = &adapter->erp_action;
@@ -264,11 +272,16 @@ void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear,
{
unsigned long flags;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
- _zfcp_erp_adapter_reopen(adapter, clear, id, ref);
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ zfcp_erp_adapter_block(adapter, clear);
+ zfcp_scsi_schedule_rports_block(adapter);
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+ zfcp_erp_adapter_failed(adapter, "erareo1", NULL);
+ else
+ zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter,
+ NULL, NULL, id, ref);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
}
/**
@@ -345,11 +358,9 @@ void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id,
unsigned long flags;
struct zfcp_adapter *adapter = port->adapter;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
+ write_lock_irqsave(&adapter->erp_lock, flags);
_zfcp_erp_port_forced_reopen(port, clear, id, ref);
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
}
static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id,
@@ -377,15 +388,13 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id,
*/
int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, void *ref)
{
- unsigned long flags;
int retval;
+ unsigned long flags;
struct zfcp_adapter *adapter = port->adapter;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
+ write_lock_irqsave(&adapter->erp_lock, flags);
retval = _zfcp_erp_port_reopen(port, clear, id, ref);
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
return retval;
}
@@ -424,11 +433,9 @@ void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, char *id,
struct zfcp_port *port = unit->port;
struct zfcp_adapter *adapter = port->adapter;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
+ write_lock_irqsave(&adapter->erp_lock, flags);
_zfcp_erp_unit_reopen(unit, clear, id, ref);
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
}
static int status_change_set(unsigned long mask, atomic_t *status)
@@ -540,8 +547,10 @@ static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
{
struct zfcp_port *port;
- list_for_each_entry(port, &adapter->port_list_head, list)
+ read_lock(&adapter->port_list_lock);
+ list_for_each_entry(port, &adapter->port_list, list)
_zfcp_erp_port_reopen(port, clear, id, ref);
+ read_unlock(&adapter->port_list_lock);
}
static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear,
@@ -549,8 +558,10 @@ static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear,
{
struct zfcp_unit *unit;
- list_for_each_entry(unit, &port->unit_list_head, list)
+ read_lock(&port->unit_list_lock);
+ list_for_each_entry(unit, &port->unit_list, list)
_zfcp_erp_unit_reopen(unit, clear, id, ref);
+ read_unlock(&port->unit_list_lock);
}
static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act)
@@ -590,16 +601,14 @@ static void zfcp_erp_wakeup(struct zfcp_adapter *adapter)
{
unsigned long flags;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- read_lock(&adapter->erp_lock);
+ read_lock_irqsave(&adapter->erp_lock, flags);
if (list_empty(&adapter->erp_ready_head) &&
list_empty(&adapter->erp_running_head)) {
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
&adapter->status);
wake_up(&adapter->erp_done_wqh);
}
- read_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ read_unlock_irqrestore(&adapter->erp_lock, flags);
}
static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *act)
@@ -1170,28 +1179,28 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
switch (act->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) {
- zfcp_unit_get(unit);
+ get_device(&unit->sysfs_device);
if (scsi_queue_work(unit->port->adapter->scsi_host,
&unit->scsi_work) <= 0)
- zfcp_unit_put(unit);
+ put_device(&unit->sysfs_device);
}
- zfcp_unit_put(unit);
+ put_device(&unit->sysfs_device);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
if (result == ZFCP_ERP_SUCCEEDED)
zfcp_scsi_schedule_rport_register(port);
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
if (result == ZFCP_ERP_SUCCEEDED) {
register_service_level(&adapter->service_level);
- schedule_work(&adapter->scan_work);
+ queue_work(adapter->work_queue, &adapter->scan_work);
} else
unregister_service_level(&adapter->service_level);
- zfcp_adapter_put(adapter);
+ kref_put(&adapter->ref, zfcp_adapter_release);
break;
}
}
@@ -1214,12 +1223,12 @@ static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
{
int retval;
- struct zfcp_adapter *adapter = erp_action->adapter;
unsigned long flags;
+ struct zfcp_adapter *adapter = erp_action->adapter;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
+ kref_get(&adapter->ref);
+ write_lock_irqsave(&adapter->erp_lock, flags);
zfcp_erp_strategy_check_fsfreq(erp_action);
if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
@@ -1231,11 +1240,9 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
zfcp_erp_action_to_running(erp_action);
/* no lock to allow for blocking operations */
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
retval = zfcp_erp_strategy_do_action(erp_action);
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
+ write_lock_irqsave(&adapter->erp_lock, flags);
if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED)
retval = ZFCP_ERP_CONTINUES;
@@ -1273,12 +1280,12 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
zfcp_erp_strategy_followup_failed(erp_action);
unlock:
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
if (retval != ZFCP_ERP_CONTINUES)
zfcp_erp_action_cleanup(erp_action, retval);
+ kref_put(&adapter->ref, zfcp_adapter_release);
return retval;
}
@@ -1415,6 +1422,7 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id,
void *ref, u32 mask, int set_or_clear)
{
struct zfcp_port *port;
+ unsigned long flags;
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
if (set_or_clear == ZFCP_SET) {
@@ -1429,10 +1437,13 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id,
atomic_set(&adapter->erp_counter, 0);
}
- if (common_mask)
- list_for_each_entry(port, &adapter->port_list_head, list)
+ if (common_mask) {
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list)
zfcp_erp_modify_port_status(port, id, ref, common_mask,
set_or_clear);
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
+ }
}
/**
@@ -1449,6 +1460,7 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref,
u32 mask, int set_or_clear)
{
struct zfcp_unit *unit;
+ unsigned long flags;
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
if (set_or_clear == ZFCP_SET) {
@@ -1463,10 +1475,13 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref,
atomic_set(&port->erp_counter, 0);
}
- if (common_mask)
- list_for_each_entry(unit, &port->unit_list_head, list)
+ if (common_mask) {
+ read_lock_irqsave(&port->unit_list_lock, flags);
+ list_for_each_entry(unit, &port->unit_list, list)
zfcp_erp_modify_unit_status(unit, id, ref, common_mask,
set_or_clear);
+ read_unlock_irqrestore(&port->unit_list_lock, flags);
+ }
}
/**
@@ -1502,12 +1517,8 @@ void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, char *id, void *ref,
*/
void zfcp_erp_port_boxed(struct zfcp_port *port, char *id, void *ref)
{
- unsigned long flags;
-
- read_lock_irqsave(&zfcp_data.config_lock, flags);
zfcp_erp_modify_port_status(port, id, ref,
ZFCP_STATUS_COMMON_ACCESS_BOXED, ZFCP_SET);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
}
@@ -1535,13 +1546,9 @@ void zfcp_erp_unit_boxed(struct zfcp_unit *unit, char *id, void *ref)
*/
void zfcp_erp_port_access_denied(struct zfcp_port *port, char *id, void *ref)
{
- unsigned long flags;
-
- read_lock_irqsave(&zfcp_data.config_lock, flags);
zfcp_erp_modify_port_status(port, id, ref,
ZFCP_STATUS_COMMON_ERP_FAILED |
ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
/**
@@ -1574,12 +1581,15 @@ static void zfcp_erp_port_access_changed(struct zfcp_port *port, char *id,
void *ref)
{
struct zfcp_unit *unit;
+ unsigned long flags;
int status = atomic_read(&port->status);
if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED |
ZFCP_STATUS_COMMON_ACCESS_BOXED))) {
- list_for_each_entry(unit, &port->unit_list_head, list)
+ read_lock_irqsave(&port->unit_list_lock, flags);
+ list_for_each_entry(unit, &port->unit_list, list)
zfcp_erp_unit_access_changed(unit, id, ref);
+ read_unlock_irqrestore(&port->unit_list_lock, flags);
return;
}
@@ -1595,14 +1605,14 @@ static void zfcp_erp_port_access_changed(struct zfcp_port *port, char *id,
void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, char *id,
void *ref)
{
- struct zfcp_port *port;
unsigned long flags;
+ struct zfcp_port *port;
if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
return;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &adapter->port_list_head, list)
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list)
zfcp_erp_port_access_changed(port, id, ref);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
}
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index b3f28deb450..03dec832b46 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -9,26 +9,31 @@
#ifndef ZFCP_EXT_H
#define ZFCP_EXT_H
+#include <linux/types.h>
+#include <scsi/fc/fc_els.h>
#include "zfcp_def.h"
+#include "zfcp_fc.h"
/* zfcp_aux.c */
extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, u64);
extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, u64);
-extern int zfcp_adapter_enqueue(struct ccw_device *);
-extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
+extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32,
u32);
-extern void zfcp_port_dequeue(struct zfcp_port *);
extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, u64);
-extern void zfcp_unit_dequeue(struct zfcp_unit *);
extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
extern void zfcp_sg_free_table(struct scatterlist *, int);
extern int zfcp_sg_setup_table(struct scatterlist *, int);
+extern void zfcp_device_unregister(struct device *,
+ const struct attribute_group *);
+extern void zfcp_adapter_release(struct kref *);
+extern void zfcp_adapter_unregister(struct zfcp_adapter *);
/* zfcp_ccw.c */
-extern int zfcp_ccw_register(void);
extern int zfcp_ccw_priv_sch(struct zfcp_adapter *);
extern struct ccw_driver zfcp_ccw_driver;
+extern struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *);
+extern void zfcp_ccw_adapter_put(struct zfcp_adapter *);
/* zfcp_cfdc.c */
extern struct miscdevice zfcp_cfdc_misc;
@@ -51,7 +56,7 @@ extern void _zfcp_dbf_hba_fsf_unsol(const char *, int level, struct zfcp_dbf *,
struct fsf_status_read_buffer *);
extern void zfcp_dbf_hba_qdio(struct zfcp_dbf *, unsigned int, int, int);
extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *);
+extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *, u32);
extern void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *);
extern void zfcp_dbf_san_els_request(struct zfcp_fsf_req *);
extern void zfcp_dbf_san_els_response(struct zfcp_fsf_req *);
@@ -92,24 +97,22 @@ extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, char *,
extern void zfcp_erp_timeout_handler(unsigned long);
/* zfcp_fc.c */
-extern int zfcp_fc_scan_ports(struct zfcp_adapter *);
-extern void _zfcp_fc_scan_ports_later(struct work_struct *);
+extern void zfcp_fc_scan_ports(struct work_struct *);
extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
extern void zfcp_fc_port_did_lookup(struct work_struct *);
extern void zfcp_fc_trigger_did_lookup(struct zfcp_port *);
-extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
+extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fc_els_flogi *);
extern void zfcp_fc_test_link(struct zfcp_port *);
extern void zfcp_fc_link_test_work(struct work_struct *);
-extern void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *);
+extern void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *);
extern int zfcp_fc_gs_setup(struct zfcp_adapter *);
extern void zfcp_fc_gs_destroy(struct zfcp_adapter *);
-extern int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *);
-extern int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *);
+extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *);
/* zfcp_fsf.c */
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
-extern int zfcp_fsf_open_wka_port(struct zfcp_wka_port *);
-extern int zfcp_fsf_close_wka_port(struct zfcp_wka_port *);
+extern int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *);
+extern int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *);
extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
@@ -125,8 +128,10 @@ extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *,
extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
extern int zfcp_fsf_status_read(struct zfcp_qdio *);
extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
-extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *);
-extern int zfcp_fsf_send_els(struct zfcp_send_els *);
+extern int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *, struct zfcp_fsf_ct_els *,
+ mempool_t *);
+extern int zfcp_fsf_send_els(struct zfcp_adapter *, u32,
+ struct zfcp_fsf_ct_els *);
extern int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *,
struct scsi_cmnd *);
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
@@ -153,7 +158,6 @@ extern void zfcp_qdio_close(struct zfcp_qdio *);
extern struct zfcp_data zfcp_data;
extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
-extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
extern struct fc_function_template zfcp_transport_functions;
extern void zfcp_scsi_rport_work(struct work_struct *);
extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index df23bcead23..ac5e3b7a357 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -9,73 +9,38 @@
#define KMSG_COMPONENT "zfcp"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#include <linux/types.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/libfc.h>
#include "zfcp_ext.h"
+#include "zfcp_fc.h"
-enum rscn_address_format {
- RSCN_PORT_ADDRESS = 0x0,
- RSCN_AREA_ADDRESS = 0x1,
- RSCN_DOMAIN_ADDRESS = 0x2,
- RSCN_FABRIC_ADDRESS = 0x3,
+static u32 zfcp_fc_rscn_range_mask[] = {
+ [ELS_ADDR_FMT_PORT] = 0xFFFFFF,
+ [ELS_ADDR_FMT_AREA] = 0xFFFF00,
+ [ELS_ADDR_FMT_DOM] = 0xFF0000,
+ [ELS_ADDR_FMT_FAB] = 0x000000,
};
-static u32 rscn_range_mask[] = {
- [RSCN_PORT_ADDRESS] = 0xFFFFFF,
- [RSCN_AREA_ADDRESS] = 0xFFFF00,
- [RSCN_DOMAIN_ADDRESS] = 0xFF0000,
- [RSCN_FABRIC_ADDRESS] = 0x000000,
-};
-
-struct gpn_ft_resp_acc {
- u8 control;
- u8 port_id[3];
- u8 reserved[4];
- u64 wwpn;
-} __attribute__ ((packed));
-
-#define ZFCP_CT_SIZE_ONE_PAGE (PAGE_SIZE - sizeof(struct ct_hdr))
-#define ZFCP_GPN_FT_ENTRIES (ZFCP_CT_SIZE_ONE_PAGE \
- / sizeof(struct gpn_ft_resp_acc))
-#define ZFCP_GPN_FT_BUFFERS 4
-#define ZFCP_GPN_FT_MAX_SIZE (ZFCP_GPN_FT_BUFFERS * PAGE_SIZE \
- - sizeof(struct ct_hdr))
-#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
-
-struct ct_iu_gpn_ft_resp {
- struct ct_hdr header;
- struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES];
-} __attribute__ ((packed));
-
-struct zfcp_gpn_ft {
- struct zfcp_send_ct ct;
- struct scatterlist sg_req;
- struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS];
-};
-
-struct zfcp_fc_ns_handler_data {
- struct completion done;
- void (*handler)(unsigned long);
- unsigned long handler_data;
-};
-
-static int zfcp_fc_wka_port_get(struct zfcp_wka_port *wka_port)
+static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port *wka_port)
{
if (mutex_lock_interruptible(&wka_port->mutex))
return -ERESTARTSYS;
- if (wka_port->status == ZFCP_WKA_PORT_OFFLINE ||
- wka_port->status == ZFCP_WKA_PORT_CLOSING) {
- wka_port->status = ZFCP_WKA_PORT_OPENING;
+ if (wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE ||
+ wka_port->status == ZFCP_FC_WKA_PORT_CLOSING) {
+ wka_port->status = ZFCP_FC_WKA_PORT_OPENING;
if (zfcp_fsf_open_wka_port(wka_port))
- wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
}
mutex_unlock(&wka_port->mutex);
wait_event(wka_port->completion_wq,
- wka_port->status == ZFCP_WKA_PORT_ONLINE ||
- wka_port->status == ZFCP_WKA_PORT_OFFLINE);
+ wka_port->status == ZFCP_FC_WKA_PORT_ONLINE ||
+ wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE);
- if (wka_port->status == ZFCP_WKA_PORT_ONLINE) {
+ if (wka_port->status == ZFCP_FC_WKA_PORT_ONLINE) {
atomic_inc(&wka_port->refcount);
return 0;
}
@@ -85,24 +50,24 @@ static int zfcp_fc_wka_port_get(struct zfcp_wka_port *wka_port)
static void zfcp_fc_wka_port_offline(struct work_struct *work)
{
struct delayed_work *dw = to_delayed_work(work);
- struct zfcp_wka_port *wka_port =
- container_of(dw, struct zfcp_wka_port, work);
+ struct zfcp_fc_wka_port *wka_port =
+ container_of(dw, struct zfcp_fc_wka_port, work);
mutex_lock(&wka_port->mutex);
if ((atomic_read(&wka_port->refcount) != 0) ||
- (wka_port->status != ZFCP_WKA_PORT_ONLINE))
+ (wka_port->status != ZFCP_FC_WKA_PORT_ONLINE))
goto out;
- wka_port->status = ZFCP_WKA_PORT_CLOSING;
+ wka_port->status = ZFCP_FC_WKA_PORT_CLOSING;
if (zfcp_fsf_close_wka_port(wka_port)) {
- wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
wake_up(&wka_port->completion_wq);
}
out:
mutex_unlock(&wka_port->mutex);
}
-static void zfcp_fc_wka_port_put(struct zfcp_wka_port *wka_port)
+static void zfcp_fc_wka_port_put(struct zfcp_fc_wka_port *wka_port)
{
if (atomic_dec_return(&wka_port->refcount) != 0)
return;
@@ -110,7 +75,7 @@ static void zfcp_fc_wka_port_put(struct zfcp_wka_port *wka_port)
schedule_delayed_work(&wka_port->work, HZ / 100);
}
-static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id,
+static void zfcp_fc_wka_port_init(struct zfcp_fc_wka_port *wka_port, u32 d_id,
struct zfcp_adapter *adapter)
{
init_waitqueue_head(&wka_port->completion_wq);
@@ -118,107 +83,107 @@ static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id,
wka_port->adapter = adapter;
wka_port->d_id = d_id;
- wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
atomic_set(&wka_port->refcount, 0);
mutex_init(&wka_port->mutex);
INIT_DELAYED_WORK(&wka_port->work, zfcp_fc_wka_port_offline);
}
-static void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka)
+static void zfcp_fc_wka_port_force_offline(struct zfcp_fc_wka_port *wka)
{
cancel_delayed_work_sync(&wka->work);
mutex_lock(&wka->mutex);
- wka->status = ZFCP_WKA_PORT_OFFLINE;
+ wka->status = ZFCP_FC_WKA_PORT_OFFLINE;
mutex_unlock(&wka->mutex);
}
-void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *gs)
+void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *gs)
{
+ if (!gs)
+ return;
zfcp_fc_wka_port_force_offline(&gs->ms);
zfcp_fc_wka_port_force_offline(&gs->ts);
zfcp_fc_wka_port_force_offline(&gs->ds);
zfcp_fc_wka_port_force_offline(&gs->as);
- zfcp_fc_wka_port_force_offline(&gs->ks);
}
static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
- struct fcp_rscn_element *elem)
+ struct fc_els_rscn_page *page)
{
unsigned long flags;
+ struct zfcp_adapter *adapter = fsf_req->adapter;
struct zfcp_port *port;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
- if ((port->d_id & range) == (elem->nport_did & range))
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list) {
+ if ((port->d_id & range) == (ntoh24(page->rscn_fid) & range))
zfcp_fc_test_link(port);
if (!port->d_id)
zfcp_erp_port_reopen(port,
ZFCP_STATUS_COMMON_ERP_FAILED,
"fcrscn1", NULL);
}
-
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
}
static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
{
struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data;
- struct fcp_rscn_head *fcp_rscn_head;
- struct fcp_rscn_element *fcp_rscn_element;
+ struct fc_els_rscn *head;
+ struct fc_els_rscn_page *page;
u16 i;
u16 no_entries;
- u32 range_mask;
+ unsigned int afmt;
- fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data;
- fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head;
+ head = (struct fc_els_rscn *) status_buffer->payload.data;
+ page = (struct fc_els_rscn_page *) head;
/* see FC-FS */
- no_entries = fcp_rscn_head->payload_len /
- sizeof(struct fcp_rscn_element);
+ no_entries = head->rscn_plen / sizeof(struct fc_els_rscn_page);
for (i = 1; i < no_entries; i++) {
/* skip head and start with 1st element */
- fcp_rscn_element++;
- range_mask = rscn_range_mask[fcp_rscn_element->addr_format];
- _zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
+ page++;
+ afmt = page->rscn_page_flags & ELS_RSCN_ADDR_FMT_MASK;
+ _zfcp_fc_incoming_rscn(fsf_req, zfcp_fc_rscn_range_mask[afmt],
+ page);
}
- schedule_work(&fsf_req->adapter->scan_work);
+ queue_work(fsf_req->adapter->work_queue, &fsf_req->adapter->scan_work);
}
static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn)
{
+ unsigned long flags;
struct zfcp_adapter *adapter = req->adapter;
struct zfcp_port *port;
- unsigned long flags;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &adapter->port_list_head, list)
- if (port->wwpn == wwpn)
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list)
+ if (port->wwpn == wwpn) {
+ zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req);
break;
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- if (port && (port->wwpn == wwpn))
- zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req);
+ }
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
}
static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
{
- struct fsf_status_read_buffer *status_buffer =
- (struct fsf_status_read_buffer *)req->data;
- struct fsf_plogi *els_plogi =
- (struct fsf_plogi *) status_buffer->payload.data;
+ struct fsf_status_read_buffer *status_buffer;
+ struct fc_els_flogi *plogi;
- zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn);
+ status_buffer = (struct fsf_status_read_buffer *) req->data;
+ plogi = (struct fc_els_flogi *) status_buffer->payload.data;
+ zfcp_fc_incoming_wwpn(req, plogi->fl_wwpn);
}
static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req)
{
struct fsf_status_read_buffer *status_buffer =
(struct fsf_status_read_buffer *)req->data;
- struct fcp_logo *els_logo =
- (struct fcp_logo *) status_buffer->payload.data;
+ struct fc_els_logo *logo =
+ (struct fc_els_logo *) status_buffer->payload.data;
- zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn);
+ zfcp_fc_incoming_wwpn(req, logo->fl_n_port_wwn);
}
/**
@@ -232,79 +197,72 @@ void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
unsigned int els_type = status_buffer->payload.data[0];
zfcp_dbf_san_incoming_els(fsf_req);
- if (els_type == LS_PLOGI)
+ if (els_type == ELS_PLOGI)
zfcp_fc_incoming_plogi(fsf_req);
- else if (els_type == LS_LOGO)
+ else if (els_type == ELS_LOGO)
zfcp_fc_incoming_logo(fsf_req);
- else if (els_type == LS_RSCN)
+ else if (els_type == ELS_RSCN)
zfcp_fc_incoming_rscn(fsf_req);
}
-static void zfcp_fc_ns_handler(unsigned long data)
+static void zfcp_fc_ns_gid_pn_eval(void *data)
{
- struct zfcp_fc_ns_handler_data *compl_rec =
- (struct zfcp_fc_ns_handler_data *) data;
-
- if (compl_rec->handler)
- compl_rec->handler(compl_rec->handler_data);
-
- complete(&compl_rec->done);
-}
-
-static void zfcp_fc_ns_gid_pn_eval(unsigned long data)
-{
- struct zfcp_gid_pn_data *gid_pn = (struct zfcp_gid_pn_data *) data;
- struct zfcp_send_ct *ct = &gid_pn->ct;
- struct ct_iu_gid_pn_req *ct_iu_req = sg_virt(ct->req);
- struct ct_iu_gid_pn_resp *ct_iu_resp = sg_virt(ct->resp);
+ struct zfcp_fc_gid_pn *gid_pn = data;
+ struct zfcp_fsf_ct_els *ct = &gid_pn->ct;
+ struct zfcp_fc_gid_pn_req *gid_pn_req = sg_virt(ct->req);
+ struct zfcp_fc_gid_pn_resp *gid_pn_resp = sg_virt(ct->resp);
struct zfcp_port *port = gid_pn->port;
if (ct->status)
return;
- if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT)
+ if (gid_pn_resp->ct_hdr.ct_cmd != FC_FS_ACC)
return;
/* paranoia */
- if (ct_iu_req->wwpn != port->wwpn)
+ if (gid_pn_req->gid_pn.fn_wwpn != port->wwpn)
return;
/* looks like a valid d_id */
- port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
+ port->d_id = ntoh24(gid_pn_resp->gid_pn.fp_fid);
+}
+
+static void zfcp_fc_complete(void *data)
+{
+ complete(data);
}
static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port,
- struct zfcp_gid_pn_data *gid_pn)
+ struct zfcp_fc_gid_pn *gid_pn)
{
struct zfcp_adapter *adapter = port->adapter;
- struct zfcp_fc_ns_handler_data compl_rec;
+ DECLARE_COMPLETION_ONSTACK(completion);
int ret;
/* setup parameters for send generic command */
gid_pn->port = port;
- gid_pn->ct.wka_port = &adapter->gs->ds;
- gid_pn->ct.handler = zfcp_fc_ns_handler;
- gid_pn->ct.handler_data = (unsigned long) &compl_rec;
- gid_pn->ct.req = &gid_pn->req;
- gid_pn->ct.resp = &gid_pn->resp;
- sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
- sizeof(struct ct_iu_gid_pn_req));
- sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
- sizeof(struct ct_iu_gid_pn_resp));
+ gid_pn->ct.handler = zfcp_fc_complete;
+ gid_pn->ct.handler_data = &completion;
+ gid_pn->ct.req = &gid_pn->sg_req;
+ gid_pn->ct.resp = &gid_pn->sg_resp;
+ sg_init_one(&gid_pn->sg_req, &gid_pn->gid_pn_req,
+ sizeof(struct zfcp_fc_gid_pn_req));
+ sg_init_one(&gid_pn->sg_resp, &gid_pn->gid_pn_resp,
+ sizeof(struct zfcp_fc_gid_pn_resp));
/* setup nameserver request */
- gid_pn->ct_iu_req.header.revision = ZFCP_CT_REVISION;
- gid_pn->ct_iu_req.header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
- gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
- gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
- gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
- gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4;
- gid_pn->ct_iu_req.wwpn = port->wwpn;
-
- init_completion(&compl_rec.done);
- compl_rec.handler = zfcp_fc_ns_gid_pn_eval;
- compl_rec.handler_data = (unsigned long) gid_pn;
- ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.gid_pn_req);
- if (!ret)
- wait_for_completion(&compl_rec.done);
+ gid_pn->gid_pn_req.ct_hdr.ct_rev = FC_CT_REV;
+ gid_pn->gid_pn_req.ct_hdr.ct_fs_type = FC_FST_DIR;
+ gid_pn->gid_pn_req.ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE;
+ gid_pn->gid_pn_req.ct_hdr.ct_options = 0;
+ gid_pn->gid_pn_req.ct_hdr.ct_cmd = FC_NS_GID_PN;
+ gid_pn->gid_pn_req.ct_hdr.ct_mr_size = ZFCP_FC_CT_SIZE_PAGE / 4;
+ gid_pn->gid_pn_req.gid_pn.fn_wwpn = port->wwpn;
+
+ ret = zfcp_fsf_send_ct(&adapter->gs->ds, &gid_pn->ct,
+ adapter->pool.gid_pn_req);
+ if (!ret) {
+ wait_for_completion(&completion);
+ zfcp_fc_ns_gid_pn_eval(gid_pn);
+ }
return ret;
}
@@ -316,10 +274,10 @@ static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port,
static int zfcp_fc_ns_gid_pn(struct zfcp_port *port)
{
int ret;
- struct zfcp_gid_pn_data *gid_pn;
+ struct zfcp_fc_gid_pn *gid_pn;
struct zfcp_adapter *adapter = port->adapter;
- gid_pn = mempool_alloc(adapter->pool.gid_pn_data, GFP_ATOMIC);
+ gid_pn = mempool_alloc(adapter->pool.gid_pn, GFP_ATOMIC);
if (!gid_pn)
return -ENOMEM;
@@ -333,7 +291,7 @@ static int zfcp_fc_ns_gid_pn(struct zfcp_port *port)
zfcp_fc_wka_port_put(&adapter->gs->ds);
out:
- mempool_free(gid_pn, adapter->pool.gid_pn_data);
+ mempool_free(gid_pn, adapter->pool.gid_pn);
return ret;
}
@@ -357,7 +315,7 @@ void zfcp_fc_port_did_lookup(struct work_struct *work)
zfcp_erp_port_reopen(port, 0, "fcgpn_3", NULL);
out:
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
/**
@@ -366,9 +324,9 @@ out:
*/
void zfcp_fc_trigger_did_lookup(struct zfcp_port *port)
{
- zfcp_port_get(port);
+ get_device(&port->sysfs_device);
if (!queue_work(port->adapter->work_queue, &port->gid_pn_work))
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
/**
@@ -378,33 +336,36 @@ void zfcp_fc_trigger_did_lookup(struct zfcp_port *port)
*
* Evaluate PLOGI playload and copy important fields into zfcp_port structure
*/
-void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
-{
- port->maxframe_size = plogi->serv_param.common_serv_param[7] |
- ((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
- if (plogi->serv_param.class1_serv_param[0] & 0x80)
+void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fc_els_flogi *plogi)
+{
+ if (plogi->fl_wwpn != port->wwpn) {
+ port->d_id = 0;
+ dev_warn(&port->adapter->ccw_device->dev,
+ "A port opened with WWPN 0x%016Lx returned data that "
+ "identifies it as WWPN 0x%016Lx\n",
+ (unsigned long long) port->wwpn,
+ (unsigned long long) plogi->fl_wwpn);
+ return;
+ }
+
+ port->wwnn = plogi->fl_wwnn;
+ port->maxframe_size = plogi->fl_csp.sp_bb_data;
+
+ if (plogi->fl_cssp[0].cp_class & FC_CPC_VALID)
port->supported_classes |= FC_COS_CLASS1;
- if (plogi->serv_param.class2_serv_param[0] & 0x80)
+ if (plogi->fl_cssp[1].cp_class & FC_CPC_VALID)
port->supported_classes |= FC_COS_CLASS2;
- if (plogi->serv_param.class3_serv_param[0] & 0x80)
+ if (plogi->fl_cssp[2].cp_class & FC_CPC_VALID)
port->supported_classes |= FC_COS_CLASS3;
- if (plogi->serv_param.class4_serv_param[0] & 0x80)
+ if (plogi->fl_cssp[3].cp_class & FC_CPC_VALID)
port->supported_classes |= FC_COS_CLASS4;
}
-struct zfcp_els_adisc {
- struct zfcp_send_els els;
- struct scatterlist req;
- struct scatterlist resp;
- struct zfcp_ls_adisc ls_adisc;
- struct zfcp_ls_adisc ls_adisc_acc;
-};
-
-static void zfcp_fc_adisc_handler(unsigned long data)
+static void zfcp_fc_adisc_handler(void *data)
{
- struct zfcp_els_adisc *adisc = (struct zfcp_els_adisc *) data;
+ struct zfcp_fc_els_adisc *adisc = data;
struct zfcp_port *port = adisc->els.port;
- struct zfcp_ls_adisc *ls_adisc = &adisc->ls_adisc_acc;
+ struct fc_els_adisc *adisc_resp = &adisc->adisc_resp;
if (adisc->els.status) {
/* request rejected or timed out */
@@ -414,9 +375,9 @@ static void zfcp_fc_adisc_handler(unsigned long data)
}
if (!port->wwnn)
- port->wwnn = ls_adisc->wwnn;
+ port->wwnn = adisc_resp->adisc_wwnn;
- if ((port->wwpn != ls_adisc->wwpn) ||
+ if ((port->wwpn != adisc_resp->adisc_wwpn) ||
!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) {
zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
"fcadh_2", NULL);
@@ -427,40 +388,44 @@ static void zfcp_fc_adisc_handler(unsigned long data)
zfcp_scsi_schedule_rport_register(port);
out:
atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
- zfcp_port_put(port);
- kfree(adisc);
+ put_device(&port->sysfs_device);
+ kmem_cache_free(zfcp_data.adisc_cache, adisc);
}
static int zfcp_fc_adisc(struct zfcp_port *port)
{
- struct zfcp_els_adisc *adisc;
+ struct zfcp_fc_els_adisc *adisc;
struct zfcp_adapter *adapter = port->adapter;
+ int ret;
- adisc = kzalloc(sizeof(struct zfcp_els_adisc), GFP_ATOMIC);
+ adisc = kmem_cache_alloc(zfcp_data.adisc_cache, GFP_ATOMIC);
if (!adisc)
return -ENOMEM;
+ adisc->els.port = port;
adisc->els.req = &adisc->req;
adisc->els.resp = &adisc->resp;
- sg_init_one(adisc->els.req, &adisc->ls_adisc,
- sizeof(struct zfcp_ls_adisc));
- sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
- sizeof(struct zfcp_ls_adisc));
+ sg_init_one(adisc->els.req, &adisc->adisc_req,
+ sizeof(struct fc_els_adisc));
+ sg_init_one(adisc->els.resp, &adisc->adisc_resp,
+ sizeof(struct fc_els_adisc));
- adisc->els.adapter = adapter;
- adisc->els.port = port;
- adisc->els.d_id = port->d_id;
adisc->els.handler = zfcp_fc_adisc_handler;
- adisc->els.handler_data = (unsigned long) adisc;
- adisc->els.ls_code = adisc->ls_adisc.code = ZFCP_LS_ADISC;
+ adisc->els.handler_data = adisc;
/* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
without FC-AL-2 capability, so we don't set it */
- adisc->ls_adisc.wwpn = fc_host_port_name(adapter->scsi_host);
- adisc->ls_adisc.wwnn = fc_host_node_name(adapter->scsi_host);
- adisc->ls_adisc.nport_id = fc_host_port_id(adapter->scsi_host);
+ adisc->adisc_req.adisc_wwpn = fc_host_port_name(adapter->scsi_host);
+ adisc->adisc_req.adisc_wwnn = fc_host_node_name(adapter->scsi_host);
+ adisc->adisc_req.adisc_cmd = ELS_ADISC;
+ hton24(adisc->adisc_req.adisc_port_id,
+ fc_host_port_id(adapter->scsi_host));
+
+ ret = zfcp_fsf_send_els(adapter, port->d_id, &adisc->els);
+ if (ret)
+ kmem_cache_free(zfcp_data.adisc_cache, adisc);
- return zfcp_fsf_send_els(&adisc->els);
+ return ret;
}
void zfcp_fc_link_test_work(struct work_struct *work)
@@ -469,7 +434,7 @@ void zfcp_fc_link_test_work(struct work_struct *work)
container_of(work, struct zfcp_port, test_link_work);
int retval;
- zfcp_port_get(port);
+ get_device(&port->sysfs_device);
port->rport_task = RPORT_DEL;
zfcp_scsi_rport_work(&port->rport_work);
@@ -488,7 +453,7 @@ void zfcp_fc_link_test_work(struct work_struct *work)
zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);
out:
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
/**
@@ -501,12 +466,12 @@ out:
*/
void zfcp_fc_test_link(struct zfcp_port *port)
{
- zfcp_port_get(port);
+ get_device(&port->sysfs_device);
if (!queue_work(port->adapter->work_queue, &port->test_link_work))
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
-static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
+static void zfcp_free_sg_env(struct zfcp_fc_gpn_ft *gpn_ft, int buf_num)
{
struct scatterlist *sg = &gpn_ft->sg_req;
@@ -516,10 +481,10 @@ static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
kfree(gpn_ft);
}
-static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num)
+static struct zfcp_fc_gpn_ft *zfcp_alloc_sg_env(int buf_num)
{
- struct zfcp_gpn_ft *gpn_ft;
- struct ct_iu_gpn_ft_req *req;
+ struct zfcp_fc_gpn_ft *gpn_ft;
+ struct zfcp_fc_gpn_ft_req *req;
gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
if (!gpn_ft)
@@ -542,159 +507,152 @@ out:
}
-static int zfcp_fc_send_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
+static int zfcp_fc_send_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
struct zfcp_adapter *adapter, int max_bytes)
{
- struct zfcp_send_ct *ct = &gpn_ft->ct;
- struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
- struct zfcp_fc_ns_handler_data compl_rec;
+ struct zfcp_fsf_ct_els *ct = &gpn_ft->ct;
+ struct zfcp_fc_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
+ DECLARE_COMPLETION_ONSTACK(completion);
int ret;
/* prepare CT IU for GPN_FT */
- req->header.revision = ZFCP_CT_REVISION;
- req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
- req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
- req->header.options = ZFCP_CT_SYNCHRONOUS;
- req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
- req->header.max_res_size = max_bytes / 4;
- req->flags = 0;
- req->domain_id_scope = 0;
- req->area_id_scope = 0;
- req->fc4_type = ZFCP_CT_SCSI_FCP;
+ req->ct_hdr.ct_rev = FC_CT_REV;
+ req->ct_hdr.ct_fs_type = FC_FST_DIR;
+ req->ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE;
+ req->ct_hdr.ct_options = 0;
+ req->ct_hdr.ct_cmd = FC_NS_GPN_FT;
+ req->ct_hdr.ct_mr_size = max_bytes / 4;
+ req->gpn_ft.fn_domain_id_scope = 0;
+ req->gpn_ft.fn_area_id_scope = 0;
+ req->gpn_ft.fn_fc4_type = FC_TYPE_FCP;
/* prepare zfcp_send_ct */
- ct->wka_port = &adapter->gs->ds;
- ct->handler = zfcp_fc_ns_handler;
- ct->handler_data = (unsigned long)&compl_rec;
+ ct->handler = zfcp_fc_complete;
+ ct->handler_data = &completion;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
- init_completion(&compl_rec.done);
- compl_rec.handler = NULL;
- ret = zfcp_fsf_send_ct(ct, NULL);
+ ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct, NULL);
if (!ret)
- wait_for_completion(&compl_rec.done);
+ wait_for_completion(&completion);
return ret;
}
-static void zfcp_fc_validate_port(struct zfcp_port *port)
+static void zfcp_fc_validate_port(struct zfcp_port *port, struct list_head *lh)
{
- struct zfcp_adapter *adapter = port->adapter;
-
if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC))
return;
atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
if ((port->supported_classes != 0) ||
- !list_empty(&port->unit_list_head)) {
- zfcp_port_put(port);
+ !list_empty(&port->unit_list))
return;
- }
- zfcp_erp_port_shutdown(port, 0, "fcpval1", NULL);
- zfcp_erp_wait(adapter);
- zfcp_port_put(port);
- zfcp_port_dequeue(port);
+
+ list_move_tail(&port->list, lh);
}
-static int zfcp_fc_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
+static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
+ struct zfcp_adapter *adapter, int max_entries)
{
- struct zfcp_send_ct *ct = &gpn_ft->ct;
+ struct zfcp_fsf_ct_els *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
- struct ct_hdr *hdr = sg_virt(sg);
- struct gpn_ft_resp_acc *acc = sg_virt(sg);
- struct zfcp_adapter *adapter = ct->wka_port->adapter;
+ struct fc_ct_hdr *hdr = sg_virt(sg);
+ struct fc_gpn_ft_resp *acc = sg_virt(sg);
struct zfcp_port *port, *tmp;
+ unsigned long flags;
+ LIST_HEAD(remove_lh);
u32 d_id;
int ret = 0, x, last = 0;
if (ct->status)
return -EIO;
- if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) {
- if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD)
+ if (hdr->ct_cmd != FC_FS_ACC) {
+ if (hdr->ct_reason == FC_BA_RJT_UNABLE)
return -EAGAIN; /* might be a temporary condition */
return -EIO;
}
- if (hdr->max_res_size) {
+ if (hdr->ct_mr_size) {
dev_warn(&adapter->ccw_device->dev,
"The name server reported %d words residual data\n",
- hdr->max_res_size);
+ hdr->ct_mr_size);
return -E2BIG;
}
- mutex_lock(&zfcp_data.config_mutex);
-
/* first entry is the header */
for (x = 1; x < max_entries && !last; x++) {
- if (x % (ZFCP_GPN_FT_ENTRIES + 1))
+ if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1))
acc++;
else
acc = sg_virt(++sg);
- last = acc->control & 0x80;
- d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
- acc->port_id[2];
+ last = acc->fp_flags & FC_NS_FID_LAST;
+ d_id = ntoh24(acc->fp_fid);
/* don't attach ports with a well known address */
- if ((d_id & ZFCP_DID_WKA) == ZFCP_DID_WKA)
+ if (d_id >= FC_FID_WELL_KNOWN_BASE)
continue;
/* skip the adapter's port and known remote ports */
- if (acc->wwpn == fc_host_port_name(adapter->scsi_host))
- continue;
- port = zfcp_get_port_by_wwpn(adapter, acc->wwpn);
- if (port)
+ if (acc->fp_wwpn == fc_host_port_name(adapter->scsi_host))
continue;
- port = zfcp_port_enqueue(adapter, acc->wwpn,
+ port = zfcp_port_enqueue(adapter, acc->fp_wwpn,
ZFCP_STATUS_COMMON_NOESC, d_id);
- if (IS_ERR(port))
- ret = PTR_ERR(port);
- else
+ if (!IS_ERR(port))
zfcp_erp_port_reopen(port, 0, "fcegpf1", NULL);
+ else if (PTR_ERR(port) != -EEXIST)
+ ret = PTR_ERR(port);
}
zfcp_erp_wait(adapter);
- list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
- zfcp_fc_validate_port(port);
- mutex_unlock(&zfcp_data.config_mutex);
+ write_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry_safe(port, tmp, &adapter->port_list, list)
+ zfcp_fc_validate_port(port, &remove_lh);
+ write_unlock_irqrestore(&adapter->port_list_lock, flags);
+
+ list_for_each_entry_safe(port, tmp, &remove_lh, list) {
+ zfcp_erp_port_shutdown(port, 0, "fcegpf2", NULL);
+ zfcp_device_unregister(&port->sysfs_device,
+ &zfcp_sysfs_port_attrs);
+ }
+
return ret;
}
/**
* zfcp_fc_scan_ports - scan remote ports and attach new ports
- * @adapter: pointer to struct zfcp_adapter
+ * @work: reference to scheduled work
*/
-int zfcp_fc_scan_ports(struct zfcp_adapter *adapter)
+void zfcp_fc_scan_ports(struct work_struct *work)
{
+ struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter,
+ scan_work);
int ret, i;
- struct zfcp_gpn_ft *gpn_ft;
+ struct zfcp_fc_gpn_ft *gpn_ft;
int chain, max_entries, buf_num, max_bytes;
chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
- buf_num = chain ? ZFCP_GPN_FT_BUFFERS : 1;
- max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES;
- max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE;
+ buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1;
+ max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE;
+ max_bytes = chain ? ZFCP_FC_GPN_FT_MAX_SIZE : ZFCP_FC_CT_SIZE_PAGE;
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT &&
fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV)
- return 0;
+ return;
- ret = zfcp_fc_wka_port_get(&adapter->gs->ds);
- if (ret)
- return ret;
+ if (zfcp_fc_wka_port_get(&adapter->gs->ds))
+ return;
gpn_ft = zfcp_alloc_sg_env(buf_num);
- if (!gpn_ft) {
- ret = -ENOMEM;
+ if (!gpn_ft)
goto out;
- }
for (i = 0; i < 3; i++) {
ret = zfcp_fc_send_gpn_ft(gpn_ft, adapter, max_bytes);
if (!ret) {
- ret = zfcp_fc_eval_gpn_ft(gpn_ft, max_entries);
+ ret = zfcp_fc_eval_gpn_ft(gpn_ft, adapter, max_entries);
if (ret == -EAGAIN)
ssleep(1);
else
@@ -704,174 +662,116 @@ int zfcp_fc_scan_ports(struct zfcp_adapter *adapter)
zfcp_free_sg_env(gpn_ft, buf_num);
out:
zfcp_fc_wka_port_put(&adapter->gs->ds);
- return ret;
-}
-
-
-void _zfcp_fc_scan_ports_later(struct work_struct *work)
-{
- zfcp_fc_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
}
-struct zfcp_els_fc_job {
- struct zfcp_send_els els;
- struct fc_bsg_job *job;
-};
-
-static void zfcp_fc_generic_els_handler(unsigned long data)
+static void zfcp_fc_ct_els_job_handler(void *data)
{
- struct zfcp_els_fc_job *els_fc_job = (struct zfcp_els_fc_job *) data;
- struct fc_bsg_job *job = els_fc_job->job;
- struct fc_bsg_reply *reply = job->reply;
-
- if (els_fc_job->els.status) {
- /* request rejected or timed out */
- reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_REJECT;
- goto out;
- }
-
- reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
- reply->reply_payload_rcv_len = job->reply_payload.payload_len;
+ struct fc_bsg_job *job = data;
+ struct zfcp_fsf_ct_els *zfcp_ct_els = job->dd_data;
+ int status = zfcp_ct_els->status;
+ int reply_status;
-out:
- job->state_flags = FC_RQST_STATE_DONE;
+ reply_status = status ? FC_CTELS_STATUS_REJECT : FC_CTELS_STATUS_OK;
+ job->reply->reply_data.ctels_reply.status = reply_status;
+ job->reply->reply_payload_rcv_len = job->reply_payload.payload_len;
job->job_done(job);
- kfree(els_fc_job);
}
-int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *job)
+static int zfcp_fc_exec_els_job(struct fc_bsg_job *job,
+ struct zfcp_adapter *adapter)
{
- struct zfcp_els_fc_job *els_fc_job;
+ struct zfcp_fsf_ct_els *els = job->dd_data;
struct fc_rport *rport = job->rport;
- struct Scsi_Host *shost;
- struct zfcp_adapter *adapter;
struct zfcp_port *port;
- u8 *port_did;
-
- shost = rport ? rport_to_shost(rport) : job->shost;
- adapter = (struct zfcp_adapter *)shost->hostdata[0];
-
- if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN))
- return -EINVAL;
-
- els_fc_job = kzalloc(sizeof(struct zfcp_els_fc_job), GFP_KERNEL);
- if (!els_fc_job)
- return -ENOMEM;
+ u32 d_id;
- els_fc_job->els.adapter = adapter;
if (rport) {
- read_lock_irq(&zfcp_data.config_lock);
port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
- if (port)
- els_fc_job->els.d_id = port->d_id;
- read_unlock_irq(&zfcp_data.config_lock);
- if (!port) {
- kfree(els_fc_job);
+ if (!port)
return -EINVAL;
- }
- } else {
- port_did = job->request->rqst_data.h_els.port_id;
- els_fc_job->els.d_id = (port_did[0] << 16) +
- (port_did[1] << 8) + port_did[2];
- }
-
- els_fc_job->els.req = job->request_payload.sg_list;
- els_fc_job->els.resp = job->reply_payload.sg_list;
- els_fc_job->els.handler = zfcp_fc_generic_els_handler;
- els_fc_job->els.handler_data = (unsigned long) els_fc_job;
- els_fc_job->job = job;
-
- return zfcp_fsf_send_els(&els_fc_job->els);
-}
-
-struct zfcp_ct_fc_job {
- struct zfcp_send_ct ct;
- struct fc_bsg_job *job;
-};
-
-static void zfcp_fc_generic_ct_handler(unsigned long data)
-{
- struct zfcp_ct_fc_job *ct_fc_job = (struct zfcp_ct_fc_job *) data;
- struct fc_bsg_job *job = ct_fc_job->job;
-
- job->reply->reply_data.ctels_reply.status = ct_fc_job->ct.status ?
- FC_CTELS_STATUS_REJECT : FC_CTELS_STATUS_OK;
- job->reply->reply_payload_rcv_len = job->reply_payload.payload_len;
- job->state_flags = FC_RQST_STATE_DONE;
- job->job_done(job);
- zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port);
+ d_id = port->d_id;
+ put_device(&port->sysfs_device);
+ } else
+ d_id = ntoh24(job->request->rqst_data.h_els.port_id);
- kfree(ct_fc_job);
+ return zfcp_fsf_send_els(adapter, d_id, els);
}
-int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *job)
+static int zfcp_fc_exec_ct_job(struct fc_bsg_job *job,
+ struct zfcp_adapter *adapter)
{
int ret;
u8 gs_type;
- struct fc_rport *rport = job->rport;
- struct Scsi_Host *shost;
- struct zfcp_adapter *adapter;
- struct zfcp_ct_fc_job *ct_fc_job;
+ struct zfcp_fsf_ct_els *ct = job->dd_data;
+ struct zfcp_fc_wka_port *wka_port;
u32 preamble_word1;
- shost = rport ? rport_to_shost(rport) : job->shost;
-
- adapter = (struct zfcp_adapter *)shost->hostdata[0];
- if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN))
- return -EINVAL;
-
- ct_fc_job = kzalloc(sizeof(struct zfcp_ct_fc_job), GFP_KERNEL);
- if (!ct_fc_job)
- return -ENOMEM;
-
preamble_word1 = job->request->rqst_data.r_ct.preamble_word1;
gs_type = (preamble_word1 & 0xff000000) >> 24;
switch (gs_type) {
case FC_FST_ALIAS:
- ct_fc_job->ct.wka_port = &adapter->gs->as;
+ wka_port = &adapter->gs->as;
break;
case FC_FST_MGMT:
- ct_fc_job->ct.wka_port = &adapter->gs->ms;
+ wka_port = &adapter->gs->ms;
break;
case FC_FST_TIME:
- ct_fc_job->ct.wka_port = &adapter->gs->ts;
+ wka_port = &adapter->gs->ts;
break;
case FC_FST_DIR:
- ct_fc_job->ct.wka_port = &adapter->gs->ds;
+ wka_port = &adapter->gs->ds;
break;
default:
- kfree(ct_fc_job);
return -EINVAL; /* no such service */
}
- ret = zfcp_fc_wka_port_get(ct_fc_job->ct.wka_port);
- if (ret) {
- kfree(ct_fc_job);
+ ret = zfcp_fc_wka_port_get(wka_port);
+ if (ret)
return ret;
- }
- ct_fc_job->ct.req = job->request_payload.sg_list;
- ct_fc_job->ct.resp = job->reply_payload.sg_list;
- ct_fc_job->ct.handler = zfcp_fc_generic_ct_handler;
- ct_fc_job->ct.handler_data = (unsigned long) ct_fc_job;
- ct_fc_job->ct.completion = NULL;
- ct_fc_job->job = job;
+ ret = zfcp_fsf_send_ct(wka_port, ct, NULL);
+ if (ret)
+ zfcp_fc_wka_port_put(wka_port);
- ret = zfcp_fsf_send_ct(&ct_fc_job->ct, NULL);
- if (ret) {
- kfree(ct_fc_job);
- zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port);
- }
return ret;
}
+int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job)
+{
+ struct Scsi_Host *shost;
+ struct zfcp_adapter *adapter;
+ struct zfcp_fsf_ct_els *ct_els = job->dd_data;
+
+ shost = job->rport ? rport_to_shost(job->rport) : job->shost;
+ adapter = (struct zfcp_adapter *)shost->hostdata[0];
+
+ if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN))
+ return -EINVAL;
+
+ ct_els->req = job->request_payload.sg_list;
+ ct_els->resp = job->reply_payload.sg_list;
+ ct_els->handler = zfcp_fc_ct_els_job_handler;
+ ct_els->handler_data = job;
+
+ switch (job->request->msgcode) {
+ case FC_BSG_RPT_ELS:
+ case FC_BSG_HST_ELS_NOLOGIN:
+ return zfcp_fc_exec_els_job(job, adapter);
+ case FC_BSG_RPT_CT:
+ case FC_BSG_HST_CT:
+ return zfcp_fc_exec_ct_job(job, adapter);
+ default:
+ return -EINVAL;
+ }
+}
+
int zfcp_fc_gs_setup(struct zfcp_adapter *adapter)
{
- struct zfcp_wka_ports *wka_ports;
+ struct zfcp_fc_wka_ports *wka_ports;
- wka_ports = kzalloc(sizeof(struct zfcp_wka_ports), GFP_KERNEL);
+ wka_ports = kzalloc(sizeof(struct zfcp_fc_wka_ports), GFP_KERNEL);
if (!wka_ports)
return -ENOMEM;
@@ -880,7 +780,6 @@ int zfcp_fc_gs_setup(struct zfcp_adapter *adapter)
zfcp_fc_wka_port_init(&wka_ports->ts, FC_FID_TIME_SERV, adapter);
zfcp_fc_wka_port_init(&wka_ports->ds, FC_FID_DIR_SERV, adapter);
zfcp_fc_wka_port_init(&wka_ports->as, FC_FID_ALIASES, adapter);
- zfcp_fc_wka_port_init(&wka_ports->ks, FC_FID_SEC_KEY, adapter);
return 0;
}
diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h
new file mode 100644
index 00000000000..cb2a3669a38
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_fc.h
@@ -0,0 +1,260 @@
+/*
+ * zfcp device driver
+ *
+ * Fibre Channel related definitions and inline functions for the zfcp
+ * device driver
+ *
+ * Copyright IBM Corporation 2009
+ */
+
+#ifndef ZFCP_FC_H
+#define ZFCP_FC_H
+
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fcp.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include "zfcp_fsf.h"
+
+#define ZFCP_FC_CT_SIZE_PAGE (PAGE_SIZE - sizeof(struct fc_ct_hdr))
+#define ZFCP_FC_GPN_FT_ENT_PAGE (ZFCP_FC_CT_SIZE_PAGE \
+ / sizeof(struct fc_gpn_ft_resp))
+#define ZFCP_FC_GPN_FT_NUM_BUFS 4 /* memory pages */
+
+#define ZFCP_FC_GPN_FT_MAX_SIZE (ZFCP_FC_GPN_FT_NUM_BUFS * PAGE_SIZE \
+ - sizeof(struct fc_ct_hdr))
+#define ZFCP_FC_GPN_FT_MAX_ENT (ZFCP_FC_GPN_FT_NUM_BUFS * \
+ (ZFCP_FC_GPN_FT_ENT_PAGE + 1))
+
+/**
+ * struct zfcp_fc_gid_pn_req - container for ct header plus gid_pn request
+ * @ct_hdr: FC GS common transport header
+ * @gid_pn: GID_PN request
+ */
+struct zfcp_fc_gid_pn_req {
+ struct fc_ct_hdr ct_hdr;
+ struct fc_ns_gid_pn gid_pn;
+} __packed;
+
+/**
+ * struct zfcp_fc_gid_pn_resp - container for ct header plus gid_pn response
+ * @ct_hdr: FC GS common transport header
+ * @gid_pn: GID_PN response
+ */
+struct zfcp_fc_gid_pn_resp {
+ struct fc_ct_hdr ct_hdr;
+ struct fc_gid_pn_resp gid_pn;
+} __packed;
+
+/**
+ * struct zfcp_fc_gid_pn - everything required in zfcp for gid_pn request
+ * @ct: data passed to zfcp_fsf for issuing fsf request
+ * @sg_req: scatterlist entry for request data
+ * @sg_resp: scatterlist entry for response data
+ * @gid_pn_req: GID_PN request data
+ * @gid_pn_resp: GID_PN response data
+ */
+struct zfcp_fc_gid_pn {
+ struct zfcp_fsf_ct_els ct;
+ struct scatterlist sg_req;
+ struct scatterlist sg_resp;
+ struct zfcp_fc_gid_pn_req gid_pn_req;
+ struct zfcp_fc_gid_pn_resp gid_pn_resp;
+ struct zfcp_port *port;
+};
+
+/**
+ * struct zfcp_fc_gpn_ft - container for ct header plus gpn_ft request
+ * @ct_hdr: FC GS common transport header
+ * @gpn_ft: GPN_FT request
+ */
+struct zfcp_fc_gpn_ft_req {
+ struct fc_ct_hdr ct_hdr;
+ struct fc_ns_gid_ft gpn_ft;
+} __packed;
+
+/**
+ * struct zfcp_fc_gpn_ft_resp - container for ct header plus gpn_ft response
+ * @ct_hdr: FC GS common transport header
+ * @gpn_ft: Array of gpn_ft response data to fill one memory page
+ */
+struct zfcp_fc_gpn_ft_resp {
+ struct fc_ct_hdr ct_hdr;
+ struct fc_gpn_ft_resp gpn_ft[ZFCP_FC_GPN_FT_ENT_PAGE];
+} __packed;
+
+/**
+ * struct zfcp_fc_gpn_ft - zfcp data for gpn_ft request
+ * @ct: data passed to zfcp_fsf for issuing fsf request
+ * @sg_req: scatter list entry for gpn_ft request
+ * @sg_resp: scatter list entries for gpn_ft responses (per memory page)
+ */
+struct zfcp_fc_gpn_ft {
+ struct zfcp_fsf_ct_els ct;
+ struct scatterlist sg_req;
+ struct scatterlist sg_resp[ZFCP_FC_GPN_FT_NUM_BUFS];
+};
+
+/**
+ * struct zfcp_fc_els_adisc - everything required in zfcp for issuing ELS ADISC
+ * @els: data required for issuing els fsf command
+ * @req: scatterlist entry for ELS ADISC request
+ * @resp: scatterlist entry for ELS ADISC response
+ * @adisc_req: ELS ADISC request data
+ * @adisc_resp: ELS ADISC response data
+ */
+struct zfcp_fc_els_adisc {
+ struct zfcp_fsf_ct_els els;
+ struct scatterlist req;
+ struct scatterlist resp;
+ struct fc_els_adisc adisc_req;
+ struct fc_els_adisc adisc_resp;
+};
+
+/**
+ * enum zfcp_fc_wka_status - FC WKA port status in zfcp
+ * @ZFCP_FC_WKA_PORT_OFFLINE: Port is closed and not in use
+ * @ZFCP_FC_WKA_PORT_CLOSING: The FSF "close port" request is pending
+ * @ZFCP_FC_WKA_PORT_OPENING: The FSF "open port" request is pending
+ * @ZFCP_FC_WKA_PORT_ONLINE: The port is open and the port handle is valid
+ */
+enum zfcp_fc_wka_status {
+ ZFCP_FC_WKA_PORT_OFFLINE,
+ ZFCP_FC_WKA_PORT_CLOSING,
+ ZFCP_FC_WKA_PORT_OPENING,
+ ZFCP_FC_WKA_PORT_ONLINE,
+};
+
+/**
+ * struct zfcp_fc_wka_port - representation of well-known-address (WKA) FC port
+ * @adapter: Pointer to adapter structure this WKA port belongs to
+ * @completion_wq: Wait for completion of open/close command
+ * @status: Current status of WKA port
+ * @refcount: Reference count to keep port open as long as it is in use
+ * @d_id: FC destination id or well-known-address
+ * @handle: FSF handle for the open WKA port
+ * @mutex: Mutex used during opening/closing state changes
+ * @work: For delaying the closing of the WKA port
+ */
+struct zfcp_fc_wka_port {
+ struct zfcp_adapter *adapter;
+ wait_queue_head_t completion_wq;
+ enum zfcp_fc_wka_status status;
+ atomic_t refcount;
+ u32 d_id;
+ u32 handle;
+ struct mutex mutex;
+ struct delayed_work work;
+};
+
+/**
+ * struct zfcp_fc_wka_ports - Data structures for FC generic services
+ * @ms: FC Management service
+ * @ts: FC time service
+ * @ds: FC directory service
+ * @as: FC alias service
+ */
+struct zfcp_fc_wka_ports {
+ struct zfcp_fc_wka_port ms;
+ struct zfcp_fc_wka_port ts;
+ struct zfcp_fc_wka_port ds;
+ struct zfcp_fc_wka_port as;
+};
+
+/**
+ * zfcp_fc_scsi_to_fcp - setup FCP command with data from scsi_cmnd
+ * @fcp: fcp_cmnd to setup
+ * @scsi: scsi_cmnd where to get LUN, task attributes/flags and CDB
+ */
+static inline
+void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi)
+{
+ char tag[2];
+
+ int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun);
+
+ if (scsi_populate_tag_msg(scsi, tag)) {
+ switch (tag[0]) {
+ case MSG_ORDERED_TAG:
+ fcp->fc_pri_ta |= FCP_PTA_ORDERED;
+ break;
+ case MSG_SIMPLE_TAG:
+ fcp->fc_pri_ta |= FCP_PTA_SIMPLE;
+ break;
+ };
+ } else
+ fcp->fc_pri_ta = FCP_PTA_SIMPLE;
+
+ if (scsi->sc_data_direction == DMA_FROM_DEVICE)
+ fcp->fc_flags |= FCP_CFL_RDDATA;
+ if (scsi->sc_data_direction == DMA_TO_DEVICE)
+ fcp->fc_flags |= FCP_CFL_WRDATA;
+
+ memcpy(fcp->fc_cdb, scsi->cmnd, scsi->cmd_len);
+
+ fcp->fc_dl = scsi_bufflen(scsi);
+}
+
+/**
+ * zfcp_fc_fcp_tm - setup FCP command as task management command
+ * @fcp: fcp_cmnd to setup
+ * @dev: scsi_device where to send the task management command
+ * @tm: task management flags to setup tm command
+ */
+static inline
+void zfcp_fc_fcp_tm(struct fcp_cmnd *fcp, struct scsi_device *dev, u8 tm_flags)
+{
+ int_to_scsilun(dev->lun, (struct scsi_lun *) &fcp->fc_lun);
+ fcp->fc_tm_flags |= tm_flags;
+}
+
+/**
+ * zfcp_fc_evap_fcp_rsp - evaluate FCP RSP IU and update scsi_cmnd accordingly
+ * @fcp_rsp: FCP RSP IU to evaluate
+ * @scsi: SCSI command where to update status and sense buffer
+ */
+static inline
+void zfcp_fc_eval_fcp_rsp(struct fcp_resp_with_ext *fcp_rsp,
+ struct scsi_cmnd *scsi)
+{
+ struct fcp_resp_rsp_info *rsp_info;
+ char *sense;
+ u32 sense_len, resid;
+ u8 rsp_flags;
+
+ set_msg_byte(scsi, COMMAND_COMPLETE);
+ scsi->result |= fcp_rsp->resp.fr_status;
+
+ rsp_flags = fcp_rsp->resp.fr_flags;
+
+ if (unlikely(rsp_flags & FCP_RSP_LEN_VAL)) {
+ rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1];
+ if (rsp_info->rsp_code == FCP_TMF_CMPL)
+ set_host_byte(scsi, DID_OK);
+ else {
+ set_host_byte(scsi, DID_ERROR);
+ return;
+ }
+ }
+
+ if (unlikely(rsp_flags & FCP_SNS_LEN_VAL)) {
+ sense = (char *) &fcp_rsp[1];
+ if (rsp_flags & FCP_RSP_LEN_VAL)
+ sense += fcp_rsp->ext.fr_sns_len;
+ sense_len = min(fcp_rsp->ext.fr_sns_len,
+ (u32) SCSI_SENSE_BUFFERSIZE);
+ memcpy(scsi->sense_buffer, sense, sense_len);
+ }
+
+ if (unlikely(rsp_flags & FCP_RESID_UNDER)) {
+ resid = fcp_rsp->ext.fr_resid;
+ scsi_set_resid(scsi, resid);
+ if (scsi_bufflen(scsi) - resid < scsi->underflow &&
+ !(rsp_flags & FCP_SNS_LEN_VAL) &&
+ fcp_rsp->resp.fr_status == SAM_STAT_GOOD)
+ set_host_byte(scsi, DID_ERROR);
+ }
+}
+
+#endif
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 4e41baa0c14..482dcd97aa5 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -10,7 +10,9 @@
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/blktrace_api.h>
+#include <scsi/fc/fc_els.h>
#include "zfcp_ext.h"
+#include "zfcp_fc.h"
#include "zfcp_dbf.h"
static void zfcp_fsf_request_timeout_handler(unsigned long data)
@@ -122,36 +124,32 @@ void zfcp_fsf_req_free(struct zfcp_fsf_req *req)
static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req)
{
+ unsigned long flags;
struct fsf_status_read_buffer *sr_buf = req->data;
struct zfcp_adapter *adapter = req->adapter;
struct zfcp_port *port;
- int d_id = sr_buf->d_id & ZFCP_DID_MASK;
- unsigned long flags;
+ int d_id = ntoh24(sr_buf->d_id);
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- list_for_each_entry(port, &adapter->port_list_head, list)
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list)
if (port->d_id == d_id) {
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
zfcp_erp_port_reopen(port, 0, "fssrpc1", req);
- return;
+ break;
}
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
}
static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, char *id,
struct fsf_link_down_info *link_down)
{
struct zfcp_adapter *adapter = req->adapter;
- unsigned long flags;
if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
return;
atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
- read_lock_irqsave(&zfcp_data.config_lock, flags);
zfcp_scsi_schedule_rports_block(adapter);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
if (!link_down)
goto out;
@@ -291,7 +289,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
zfcp_erp_adapter_access_changed(adapter, "fssrh_3",
req);
if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS)
- schedule_work(&adapter->scan_work);
+ queue_work(adapter->work_queue, &adapter->scan_work);
break;
case FSF_STATUS_READ_CFDC_UPDATED:
zfcp_erp_adapter_access_changed(adapter, "fssrh_4", req);
@@ -317,7 +315,6 @@ static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req)
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
return;
case FSF_SQ_COMMAND_ABORTED:
- req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
break;
case FSF_SQ_NO_RECOM:
dev_err(&req->adapter->ccw_device->dev,
@@ -358,8 +355,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
zfcp_dbf_hba_fsf_response(req);
if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
return;
}
@@ -377,7 +373,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
case FSF_PROT_ERROR_STATE:
case FSF_PROT_SEQ_NUMB_ERROR:
zfcp_erp_adapter_reopen(adapter, 0, "fspse_2", req);
- req->status |= ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_PROT_UNSUPP_QTCB_TYPE:
dev_err(&adapter->ccw_device->dev,
@@ -480,20 +476,27 @@ void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
{
- struct fsf_qtcb_bottom_config *bottom;
+ struct fsf_qtcb_bottom_config *bottom = &req->qtcb->bottom.config;
struct zfcp_adapter *adapter = req->adapter;
struct Scsi_Host *shost = adapter->scsi_host;
+ struct fc_els_flogi *nsp, *plogi;
- bottom = &req->qtcb->bottom.config;
+ /* adjust pointers for missing command code */
+ nsp = (struct fc_els_flogi *) ((u8 *)&bottom->nport_serv_param
+ - sizeof(u32));
+ plogi = (struct fc_els_flogi *) ((u8 *)&bottom->plogi_payload
+ - sizeof(u32));
if (req->data)
memcpy(req->data, bottom, sizeof(*bottom));
- fc_host_node_name(shost) = bottom->nport_serv_param.wwnn;
- fc_host_port_name(shost) = bottom->nport_serv_param.wwpn;
- fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK;
+ fc_host_port_name(shost) = nsp->fl_wwpn;
+ fc_host_node_name(shost) = nsp->fl_wwnn;
+ fc_host_port_id(shost) = ntoh24(bottom->s_id);
fc_host_speed(shost) = bottom->fc_link_speed;
fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3;
+ fc_host_supported_fc4s(shost)[2] = 1; /* FCP */
+ fc_host_active_fc4s(shost)[2] = 1; /* FCP */
adapter->hydra_version = bottom->adapter_type;
adapter->timer_ticks = bottom->timer_interval;
@@ -503,9 +506,9 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
switch (bottom->fc_topology) {
case FSF_TOPO_P2P:
- adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK;
- adapter->peer_wwpn = bottom->plogi_payload.wwpn;
- adapter->peer_wwnn = bottom->plogi_payload.wwnn;
+ adapter->peer_d_id = ntoh24(bottom->peer_d_id);
+ adapter->peer_wwpn = plogi->fl_wwpn;
+ adapter->peer_wwnn = plogi->fl_wwnn;
fc_host_port_type(shost) = FC_PORTTYPE_PTP;
break;
case FSF_TOPO_FABRIC:
@@ -881,13 +884,11 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
break;
case FSF_PORT_BOXED:
zfcp_erp_port_boxed(unit->port, "fsafch3", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_LUN_BOXED:
zfcp_erp_unit_boxed(unit, "fsafch4", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (fsq->word[0]) {
@@ -958,10 +959,10 @@ out:
static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
{
struct zfcp_adapter *adapter = req->adapter;
- struct zfcp_send_ct *send_ct = req->data;
+ struct zfcp_fsf_ct_els *ct = req->data;
struct fsf_qtcb_header *header = &req->qtcb->header;
- send_ct->status = -EINVAL;
+ ct->status = -EINVAL;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
goto skip_fsfstatus;
@@ -969,7 +970,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status) {
case FSF_GOOD:
zfcp_dbf_san_ct_response(req);
- send_ct->status = 0;
+ ct->status = 0;
break;
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
zfcp_fsf_class_not_supp(req);
@@ -985,8 +986,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
case FSF_ACCESS_DENIED:
break;
case FSF_PORT_BOXED:
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_PORT_HANDLE_NOT_VALID:
zfcp_erp_adapter_reopen(adapter, 0, "fsscth1", req);
@@ -1001,8 +1001,8 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
}
skip_fsfstatus:
- if (send_ct->handler)
- send_ct->handler(send_ct->handler_data);
+ if (ct->handler)
+ ct->handler(ct->handler_data);
}
static void zfcp_fsf_setup_ct_els_unchained(struct qdio_buffer_element *sbale,
@@ -1071,15 +1071,17 @@ static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req,
int max_sbals)
{
int ret;
+ unsigned int fcp_chan_timeout;
ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp, max_sbals);
if (ret)
return ret;
/* common settings for ct/gs and els requests */
+ fcp_chan_timeout = 2 * FC_DEF_R_A_TOV / 1000;
req->qtcb->bottom.support.service_class = FSF_CLASS_3;
- req->qtcb->bottom.support.timeout = 2 * R_A_TOV;
- zfcp_fsf_start_timer(req, (2 * R_A_TOV + 10) * HZ);
+ req->qtcb->bottom.support.timeout = fcp_chan_timeout;
+ zfcp_fsf_start_timer(req, (fcp_chan_timeout + 10) * HZ);
return 0;
}
@@ -1089,9 +1091,9 @@ static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req,
* @ct: pointer to struct zfcp_send_ct with data for request
* @pool: if non-null this mempool is used to allocate struct zfcp_fsf_req
*/
-int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool)
+int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port,
+ struct zfcp_fsf_ct_els *ct, mempool_t *pool)
{
- struct zfcp_wka_port *wka_port = ct->wka_port;
struct zfcp_qdio *qdio = wka_port->adapter->qdio;
struct zfcp_fsf_req *req;
int ret = -EIO;
@@ -1117,7 +1119,7 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool)
req->qtcb->header.port_handle = wka_port->handle;
req->data = ct;
- zfcp_dbf_san_ct_request(req);
+ zfcp_dbf_san_ct_request(req, wka_port->d_id);
ret = zfcp_fsf_req_send(req);
if (ret)
@@ -1134,7 +1136,7 @@ out:
static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
{
- struct zfcp_send_els *send_els = req->data;
+ struct zfcp_fsf_ct_els *send_els = req->data;
struct zfcp_port *port = send_els->port;
struct fsf_qtcb_header *header = &req->qtcb->header;
@@ -1154,9 +1156,6 @@ static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]){
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- if (port && (send_els->ls_code != ZFCP_LS_ADISC))
- zfcp_fc_test_link(port);
- /*fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
case FSF_SQ_RETRY_IF_POSSIBLE:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -1188,10 +1187,11 @@ skip_fsfstatus:
* zfcp_fsf_send_els - initiate an ELS command (FC-FS)
* @els: pointer to struct zfcp_send_els with data for the command
*/
-int zfcp_fsf_send_els(struct zfcp_send_els *els)
+int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id,
+ struct zfcp_fsf_ct_els *els)
{
struct zfcp_fsf_req *req;
- struct zfcp_qdio *qdio = els->adapter->qdio;
+ struct zfcp_qdio *qdio = adapter->qdio;
int ret = -EIO;
spin_lock_bh(&qdio->req_q_lock);
@@ -1211,7 +1211,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
if (ret)
goto failed_send;
- req->qtcb->bottom.support.d_id = els->d_id;
+ hton24(req->qtcb->bottom.support.d_id, d_id);
req->handler = zfcp_fsf_send_els_handler;
req->data = els;
@@ -1422,7 +1422,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
{
struct zfcp_port *port = req->data;
struct fsf_qtcb_header *header = &req->qtcb->header;
- struct fsf_plogi *plogi;
+ struct fc_els_flogi *plogi;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
goto out;
@@ -1472,23 +1472,10 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
* another GID_PN straight after a port has been opened.
* Alternately, an ADISC/PDISC ELS should suffice, as well.
*/
- plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
+ plogi = (struct fc_els_flogi *) req->qtcb->bottom.support.els;
if (req->qtcb->bottom.support.els1_length >=
- FSF_PLOGI_MIN_LEN) {
- if (plogi->serv_param.wwpn != port->wwpn) {
- port->d_id = 0;
- dev_warn(&port->adapter->ccw_device->dev,
- "A port opened with WWPN 0x%016Lx "
- "returned data that identifies it as "
- "WWPN 0x%016Lx\n",
- (unsigned long long) port->wwpn,
- (unsigned long long)
- plogi->serv_param.wwpn);
- } else {
- port->wwnn = plogi->serv_param.wwnn;
+ FSF_PLOGI_MIN_LEN)
zfcp_fc_plogi_evaluate(port, plogi);
- }
- }
break;
case FSF_UNKNOWN_OP_SUBTYPE:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -1496,7 +1483,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
}
out:
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
/**
@@ -1530,18 +1517,18 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
req->handler = zfcp_fsf_open_port_handler;
- req->qtcb->bottom.support.d_id = port->d_id;
+ hton24(req->qtcb->bottom.support.d_id, port->d_id);
req->data = port;
req->erp_action = erp_action;
erp_action->fsf_req = req;
- zfcp_port_get(port);
+ get_device(&port->sysfs_device);
zfcp_fsf_start_erp_timer(req);
retval = zfcp_fsf_req_send(req);
if (retval) {
zfcp_fsf_req_free(req);
erp_action->fsf_req = NULL;
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
out:
spin_unlock_bh(&qdio->req_q_lock);
@@ -1618,11 +1605,11 @@ out:
static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
{
- struct zfcp_wka_port *wka_port = req->data;
+ struct zfcp_fc_wka_port *wka_port = req->data;
struct fsf_qtcb_header *header = &req->qtcb->header;
if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
- wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
goto out;
}
@@ -1635,13 +1622,13 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
/* fall through */
case FSF_ACCESS_DENIED:
- wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
break;
case FSF_GOOD:
wka_port->handle = header->port_handle;
/* fall through */
case FSF_PORT_ALREADY_OPEN:
- wka_port->status = ZFCP_WKA_PORT_ONLINE;
+ wka_port->status = ZFCP_FC_WKA_PORT_ONLINE;
}
out:
wake_up(&wka_port->completion_wq);
@@ -1649,10 +1636,10 @@ out:
/**
* zfcp_fsf_open_wka_port - create and send open wka-port request
- * @wka_port: pointer to struct zfcp_wka_port
+ * @wka_port: pointer to struct zfcp_fc_wka_port
* Returns: 0 on success, error otherwise
*/
-int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port)
+int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
{
struct qdio_buffer_element *sbale;
struct zfcp_qdio *qdio = wka_port->adapter->qdio;
@@ -1677,7 +1664,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port)
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
req->handler = zfcp_fsf_open_wka_port_handler;
- req->qtcb->bottom.support.d_id = wka_port->d_id;
+ hton24(req->qtcb->bottom.support.d_id, wka_port->d_id);
req->data = wka_port;
zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
@@ -1691,23 +1678,23 @@ out:
static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req)
{
- struct zfcp_wka_port *wka_port = req->data;
+ struct zfcp_fc_wka_port *wka_port = req->data;
if (req->qtcb->header.fsf_status == FSF_PORT_HANDLE_NOT_VALID) {
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
zfcp_erp_adapter_reopen(wka_port->adapter, 0, "fscwph1", req);
}
- wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+ wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
wake_up(&wka_port->completion_wq);
}
/**
* zfcp_fsf_close_wka_port - create and send close wka port request
- * @erp_action: pointer to struct zfcp_erp_action
+ * @wka_port: WKA port to open
* Returns: 0 on success, error otherwise
*/
-int zfcp_fsf_close_wka_port(struct zfcp_wka_port *wka_port)
+int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
{
struct qdio_buffer_element *sbale;
struct zfcp_qdio *qdio = wka_port->adapter->qdio;
@@ -1765,13 +1752,13 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
/* can't use generic zfcp_erp_modify_port_status because
* ZFCP_STATUS_COMMON_OPEN must not be reset for the port */
atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
- list_for_each_entry(unit, &port->unit_list_head, list)
+ read_lock(&port->unit_list_lock);
+ list_for_each_entry(unit, &port->unit_list, list)
atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
&unit->status);
+ read_unlock(&port->unit_list_lock);
zfcp_erp_port_boxed(port, "fscpph2", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
-
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]) {
@@ -1787,9 +1774,11 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
* ZFCP_STATUS_COMMON_OPEN must not be reset for the port
*/
atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
- list_for_each_entry(unit, &port->unit_list_head, list)
+ read_lock(&port->unit_list_lock);
+ list_for_each_entry(unit, &port->unit_list, list)
atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
&unit->status);
+ read_unlock(&port->unit_list_lock);
break;
}
}
@@ -1873,8 +1862,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
break;
case FSF_PORT_BOXED:
zfcp_erp_port_boxed(unit->port, "fsouh_2", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_LUN_SHARING_VIOLATION:
if (header->fsf_status_qual.word[0])
@@ -2036,8 +2024,7 @@ static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req)
break;
case FSF_PORT_BOXED:
zfcp_erp_port_boxed(unit->port, "fscuh_3", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (req->qtcb->header.fsf_status_qual.word[0]) {
@@ -2109,72 +2096,57 @@ static void zfcp_fsf_update_lat(struct fsf_latency_record *lat_rec, u32 lat)
lat_rec->max = max(lat_rec->max, lat);
}
-static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req)
+static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi)
{
- struct fsf_qual_latency_info *lat_inf;
- struct latency_cont *lat;
+ struct fsf_qual_latency_info *lat_in;
+ struct latency_cont *lat = NULL;
struct zfcp_unit *unit = req->unit;
+ struct zfcp_blk_drv_data blktrc;
+ int ticks = req->adapter->timer_ticks;
- lat_inf = &req->qtcb->prefix.prot_status_qual.latency_info;
-
- switch (req->qtcb->bottom.io.data_direction) {
- case FSF_DATADIR_READ:
- lat = &unit->latencies.read;
- break;
- case FSF_DATADIR_WRITE:
- lat = &unit->latencies.write;
- break;
- case FSF_DATADIR_CMND:
- lat = &unit->latencies.cmd;
- break;
- default:
- return;
- }
+ lat_in = &req->qtcb->prefix.prot_status_qual.latency_info;
- spin_lock(&unit->latencies.lock);
- zfcp_fsf_update_lat(&lat->channel, lat_inf->channel_lat);
- zfcp_fsf_update_lat(&lat->fabric, lat_inf->fabric_lat);
- lat->counter++;
- spin_unlock(&unit->latencies.lock);
-}
-
-#ifdef CONFIG_BLK_DEV_IO_TRACE
-static void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req)
-{
- struct fsf_qual_latency_info *lat_inf;
- struct scsi_cmnd *scsi_cmnd = (struct scsi_cmnd *)fsf_req->data;
- struct request *req = scsi_cmnd->request;
- struct zfcp_blk_drv_data trace;
- int ticks = fsf_req->adapter->timer_ticks;
+ blktrc.flags = 0;
+ blktrc.magic = ZFCP_BLK_DRV_DATA_MAGIC;
+ if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+ blktrc.flags |= ZFCP_BLK_REQ_ERROR;
+ blktrc.inb_usage = req->queue_req.qdio_inb_usage;
+ blktrc.outb_usage = req->queue_req.qdio_outb_usage;
+
+ if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) {
+ blktrc.flags |= ZFCP_BLK_LAT_VALID;
+ blktrc.channel_lat = lat_in->channel_lat * ticks;
+ blktrc.fabric_lat = lat_in->fabric_lat * ticks;
+
+ switch (req->qtcb->bottom.io.data_direction) {
+ case FSF_DATADIR_READ:
+ lat = &unit->latencies.read;
+ break;
+ case FSF_DATADIR_WRITE:
+ lat = &unit->latencies.write;
+ break;
+ case FSF_DATADIR_CMND:
+ lat = &unit->latencies.cmd;
+ break;
+ }
- trace.flags = 0;
- trace.magic = ZFCP_BLK_DRV_DATA_MAGIC;
- if (fsf_req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) {
- trace.flags |= ZFCP_BLK_LAT_VALID;
- lat_inf = &fsf_req->qtcb->prefix.prot_status_qual.latency_info;
- trace.channel_lat = lat_inf->channel_lat * ticks;
- trace.fabric_lat = lat_inf->fabric_lat * ticks;
+ if (lat) {
+ spin_lock(&unit->latencies.lock);
+ zfcp_fsf_update_lat(&lat->channel, lat_in->channel_lat);
+ zfcp_fsf_update_lat(&lat->fabric, lat_in->fabric_lat);
+ lat->counter++;
+ spin_unlock(&unit->latencies.lock);
+ }
}
- if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
- trace.flags |= ZFCP_BLK_REQ_ERROR;
- trace.inb_usage = fsf_req->queue_req.qdio_inb_usage;
- trace.outb_usage = fsf_req->queue_req.qdio_outb_usage;
- blk_add_driver_data(req->q, req, &trace, sizeof(trace));
-}
-#else
-static inline void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req)
-{
+ blk_add_driver_data(scsi->request->q, scsi->request, &blktrc,
+ sizeof(blktrc));
}
-#endif
static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
{
struct scsi_cmnd *scpnt;
- struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
- &(req->qtcb->bottom.io.fcp_rsp);
- u32 sns_len;
- char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
+ struct fcp_resp_with_ext *fcp_rsp;
unsigned long flags;
read_lock_irqsave(&req->adapter->abort_lock, flags);
@@ -2185,50 +2157,16 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
return;
}
- if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ABORTED)) {
- set_host_byte(scpnt, DID_SOFT_ERROR);
- goto skip_fsfstatus;
- }
-
if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
- set_host_byte(scpnt, DID_ERROR);
+ set_host_byte(scpnt, DID_TRANSPORT_DISRUPTED);
goto skip_fsfstatus;
}
- set_msg_byte(scpnt, COMMAND_COMPLETE);
-
- scpnt->result |= fcp_rsp_iu->scsi_status;
+ fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp;
+ zfcp_fc_eval_fcp_rsp(fcp_rsp, scpnt);
- if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)
- zfcp_fsf_req_latency(req);
-
- zfcp_fsf_trace_latency(req);
-
- if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) {
- if (fcp_rsp_info[3] == RSP_CODE_GOOD)
- set_host_byte(scpnt, DID_OK);
- else {
- set_host_byte(scpnt, DID_ERROR);
- goto skip_fsfstatus;
- }
- }
-
- if (unlikely(fcp_rsp_iu->validity.bits.fcp_sns_len_valid)) {
- sns_len = FSF_FCP_RSP_SIZE - sizeof(struct fcp_rsp_iu) +
- fcp_rsp_iu->fcp_rsp_len;
- sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE);
- sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
-
- memcpy(scpnt->sense_buffer,
- zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len);
- }
+ zfcp_fsf_req_trace(req, scpnt);
- if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) {
- scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid);
- if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) <
- scpnt->underflow)
- set_host_byte(scpnt, DID_ERROR);
- }
skip_fsfstatus:
if (scpnt->result != 0)
zfcp_dbf_scsi_result("erro", 3, req->adapter->dbf, scpnt, req);
@@ -2250,11 +2188,13 @@ skip_fsfstatus:
static void zfcp_fsf_send_fcp_ctm_handler(struct zfcp_fsf_req *req)
{
- struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
- &(req->qtcb->bottom.io.fcp_rsp);
- char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
+ struct fcp_resp_with_ext *fcp_rsp;
+ struct fcp_resp_rsp_info *rsp_info;
+
+ fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp;
+ rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1];
- if ((fcp_rsp_info[3] != RSP_CODE_GOOD) ||
+ if ((rsp_info->rsp_code != FCP_TMF_CMPL) ||
(req->status & ZFCP_STATUS_FSFREQ_ERROR))
req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
}
@@ -2314,13 +2254,11 @@ static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
break;
case FSF_PORT_BOXED:
zfcp_erp_port_boxed(unit->port, "fssfch5", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_LUN_BOXED:
zfcp_erp_unit_boxed(unit, "fssfch6", req);
- req->status |= ZFCP_STATUS_FSFREQ_ERROR |
- ZFCP_STATUS_FSFREQ_RETRY;
+ req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_ADAPTER_STATUS_AVAILABLE:
if (header->fsf_status_qual.word[0] ==
@@ -2335,24 +2273,10 @@ skip_fsfstatus:
else {
zfcp_fsf_send_fcp_command_task_handler(req);
req->unit = NULL;
- zfcp_unit_put(unit);
+ put_device(&unit->sysfs_device);
}
}
-static void zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, u32 fcp_dl)
-{
- u32 *fcp_dl_ptr;
-
- /*
- * fcp_dl_addr = start address of fcp_cmnd structure +
- * size of fixed part + size of dynamically sized add_dcp_cdb field
- * SEE FCP-2 documentation
- */
- fcp_dl_ptr = (u32 *) ((unsigned char *) &fcp_cmd[1] +
- (fcp_cmd->add_fcp_cdb_length << 2));
- *fcp_dl_ptr = fcp_dl;
-}
-
/**
* zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
* @unit: unit where command is sent to
@@ -2362,7 +2286,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
struct scsi_cmnd *scsi_cmnd)
{
struct zfcp_fsf_req *req;
- struct fcp_cmnd_iu *fcp_cmnd_iu;
+ struct fcp_cmnd *fcp_cmnd;
unsigned int sbtype = SBAL_FLAGS0_TYPE_READ;
int real_bytes, retval = -EIO;
struct zfcp_adapter *adapter = unit->port->adapter;
@@ -2387,23 +2311,21 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
}
req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
- zfcp_unit_get(unit);
+ get_device(&unit->sysfs_device);
req->unit = unit;
req->data = scsi_cmnd;
req->handler = zfcp_fsf_send_fcp_command_handler;
req->qtcb->header.lun_handle = unit->handle;
req->qtcb->header.port_handle = unit->port->handle;
req->qtcb->bottom.io.service_class = FSF_CLASS_3;
+ req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN;
scsi_cmnd->host_scribble = (unsigned char *) req->req_id;
- fcp_cmnd_iu = (struct fcp_cmnd_iu *) &(req->qtcb->bottom.io.fcp_cmnd);
- fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
/*
* set depending on data direction:
* data direction bits in SBALE (SB Type)
* data direction bits in QTCB
- * data direction bits in FCP_CMND IU
*/
switch (scsi_cmnd->sc_data_direction) {
case DMA_NONE:
@@ -2411,32 +2333,17 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
break;
case DMA_FROM_DEVICE:
req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
- fcp_cmnd_iu->rddata = 1;
break;
case DMA_TO_DEVICE:
req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
sbtype = SBAL_FLAGS0_TYPE_WRITE;
- fcp_cmnd_iu->wddata = 1;
break;
case DMA_BIDIRECTIONAL:
goto failed_scsi_cmnd;
}
- if (likely((scsi_cmnd->device->simple_tags) ||
- ((atomic_read(&unit->status) & ZFCP_STATUS_UNIT_READONLY) &&
- (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_SHARED))))
- fcp_cmnd_iu->task_attribute = SIMPLE_Q;
- else
- fcp_cmnd_iu->task_attribute = UNTAGGED;
-
- if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH))
- fcp_cmnd_iu->add_fcp_cdb_length =
- (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
-
- memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
-
- req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
- fcp_cmnd_iu->add_fcp_cdb_length + sizeof(u32);
+ fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
+ zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd);
real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->queue_req, sbtype,
scsi_sglist(scsi_cmnd),
@@ -2454,8 +2361,6 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
goto failed_scsi_cmnd;
}
- zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
-
retval = zfcp_fsf_req_send(req);
if (unlikely(retval))
goto failed_scsi_cmnd;
@@ -2463,7 +2368,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
goto out;
failed_scsi_cmnd:
- zfcp_unit_put(unit);
+ put_device(&unit->sysfs_device);
zfcp_fsf_req_free(req);
scsi_cmnd->host_scribble = NULL;
out:
@@ -2481,7 +2386,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
{
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req = NULL;
- struct fcp_cmnd_iu *fcp_cmnd_iu;
+ struct fcp_cmnd *fcp_cmnd;
struct zfcp_qdio *qdio = unit->port->adapter->qdio;
if (unlikely(!(atomic_read(&unit->status) &
@@ -2507,16 +2412,14 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
req->qtcb->header.port_handle = unit->port->handle;
req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
req->qtcb->bottom.io.service_class = FSF_CLASS_3;
- req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
- sizeof(u32);
+ req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN;
sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
- fcp_cmnd_iu = (struct fcp_cmnd_iu *) &req->qtcb->bottom.io.fcp_cmnd;
- fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
- fcp_cmnd_iu->task_management_flags = tm_flags;
+ fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
+ zfcp_fc_fcp_tm(fcp_cmnd, unit->device, tm_flags);
zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
if (!zfcp_fsf_req_send(req))
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index dcc7c1dbcf5..b3de682b64c 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -11,6 +11,7 @@
#include <linux/pfn.h>
#include <linux/scatterlist.h>
+#include <scsi/libfc.h>
#define FSF_QTCB_CURRENT_VERSION 0x00000001
@@ -228,7 +229,8 @@ struct fsf_status_read_buffer {
u32 length;
u32 res1;
struct fsf_queue_designator queue_designator;
- u32 d_id;
+ u8 res2;
+ u8 d_id[3];
u32 class;
u64 fcp_lun;
u8 res3[24];
@@ -309,22 +311,7 @@ struct fsf_qtcb_header {
u8 res4[16];
} __attribute__ ((packed));
-struct fsf_nport_serv_param {
- u8 common_serv_param[16];
- u64 wwpn;
- u64 wwnn;
- u8 class1_serv_param[16];
- u8 class2_serv_param[16];
- u8 class3_serv_param[16];
- u8 class4_serv_param[16];
- u8 vendor_version_level[16];
-} __attribute__ ((packed));
-
#define FSF_PLOGI_MIN_LEN 112
-struct fsf_plogi {
- u32 code;
- struct fsf_nport_serv_param serv_param;
-} __attribute__ ((packed));
#define FSF_FCP_CMND_SIZE 288
#define FSF_FCP_RSP_SIZE 128
@@ -342,8 +329,8 @@ struct fsf_qtcb_bottom_io {
struct fsf_qtcb_bottom_support {
u32 operation_subtype;
- u8 res1[12];
- u32 d_id;
+ u8 res1[13];
+ u8 d_id[3];
u32 option;
u64 fcp_lun;
u64 res2;
@@ -372,18 +359,18 @@ struct fsf_qtcb_bottom_config {
u32 fc_topology;
u32 fc_link_speed;
u32 adapter_type;
- u32 peer_d_id;
+ u8 res0;
+ u8 peer_d_id[3];
u8 res1[2];
u16 timer_interval;
- u8 res2[8];
- u32 s_id;
- struct fsf_nport_serv_param nport_serv_param;
- u8 reserved_nport_serv_param[16];
+ u8 res2[9];
+ u8 s_id[3];
+ u8 nport_serv_param[128];
u8 res3[8];
u32 adapter_ports;
u32 hardware_version;
u8 serial_number[32];
- struct fsf_nport_serv_param plogi_payload;
+ u8 plogi_payload[112];
struct fsf_statistics_info stat_info;
u8 res4[112];
} __attribute__ ((packed));
@@ -450,4 +437,22 @@ struct zfcp_blk_drv_data {
u64 fabric_lat;
} __attribute__ ((packed));
+/**
+ * struct zfcp_fsf_ct_els - zfcp data for ct or els request
+ * @req: scatter-gather list for request
+ * @resp: scatter-gather list for response
+ * @handler: handler function (called for response to the request)
+ * @handler_data: data passed to handler function
+ * @port: Optional pointer to port for zfcp internal ELS (only test link ADISC)
+ * @status: used to pass error status to calling function
+ */
+struct zfcp_fsf_ct_els {
+ struct scatterlist *req;
+ struct scatterlist *resp;
+ void (*handler)(void *);
+ void *handler_data;
+ struct zfcp_port *port;
+ int status;
+};
+
#endif /* FSF_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 0e1a34627a2..771cc536a98 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -9,29 +9,33 @@
#define KMSG_COMPONENT "zfcp"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#include <linux/types.h>
+#include <scsi/fc/fc_fcp.h>
#include <asm/atomic.h>
#include "zfcp_ext.h"
#include "zfcp_dbf.h"
+#include "zfcp_fc.h"
static unsigned int default_depth = 32;
module_param_named(queue_depth, default_depth, uint, 0600);
MODULE_PARM_DESC(queue_depth, "Default queue depth for new SCSI devices");
-/* Find start of Sense Information in FCP response unit*/
-char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
+static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth,
+ int reason)
{
- char *fcp_sns_info_ptr;
-
- fcp_sns_info_ptr = (unsigned char *) &fcp_rsp_iu[1];
- if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)
- fcp_sns_info_ptr += fcp_rsp_iu->fcp_rsp_len;
-
- return fcp_sns_info_ptr;
-}
-
-static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth)
-{
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ scsi_track_queue_full(sdev, depth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
return sdev->queue_depth;
}
@@ -39,7 +43,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
{
struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
unit->device = NULL;
- zfcp_unit_put(unit);
+ put_device(&unit->sysfs_device);
}
static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
@@ -99,12 +103,26 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
}
status = atomic_read(&unit->status);
- if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) ||
- !(status & ZFCP_STATUS_COMMON_RUNNING))) {
+ if (unlikely(status & ZFCP_STATUS_COMMON_ERP_FAILED) &&
+ !(atomic_read(&unit->port->status) &
+ ZFCP_STATUS_COMMON_ERP_FAILED)) {
+ /* only unit access denied, but port is good
+ * not covered by FC transport, have to fail here */
zfcp_scsi_command_fail(scpnt, DID_ERROR);
return 0;
}
+ if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) {
+ /* This could be either
+ * open unit pending: this is temporary, will result in
+ * open unit or ERP_FAILED, so retry command
+ * call to rport_delete pending: mimic retry from
+ * fc_remote_port_chkready until rport is BLOCKED
+ */
+ zfcp_scsi_command_fail(scpnt, DID_IMM_RETRY);
+ return 0;
+ }
+
ret = zfcp_fsf_send_fcp_command_task(unit, scpnt);
if (unlikely(ret == -EBUSY))
return SCSI_MLQUEUE_DEVICE_BUSY;
@@ -115,49 +133,44 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
}
static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *adapter,
- int channel, unsigned int id,
- unsigned int lun)
+ unsigned int id, u64 lun)
{
+ unsigned long flags;
struct zfcp_port *port;
- struct zfcp_unit *unit;
- int scsi_lun;
+ struct zfcp_unit *unit = NULL;
- list_for_each_entry(port, &adapter->port_list_head, list) {
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list) {
if (!port->rport || (id != port->rport->scsi_target_id))
continue;
- list_for_each_entry(unit, &port->unit_list_head, list) {
- scsi_lun = scsilun_to_int(
- (struct scsi_lun *)&unit->fcp_lun);
- if (lun == scsi_lun)
- return unit;
- }
+ unit = zfcp_get_unit_by_lun(port, lun);
+ if (unit)
+ break;
}
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
- return NULL;
+ return unit;
}
static int zfcp_scsi_slave_alloc(struct scsi_device *sdp)
{
struct zfcp_adapter *adapter;
struct zfcp_unit *unit;
- unsigned long flags;
- int retval = -ENXIO;
+ u64 lun;
adapter = (struct zfcp_adapter *) sdp->host->hostdata[0];
if (!adapter)
goto out;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
+ int_to_scsilun(sdp->lun, (struct scsi_lun *)&lun);
+ unit = zfcp_unit_lookup(adapter, sdp->id, lun);
if (unit) {
sdp->hostdata = unit;
unit->device = sdp;
- zfcp_unit_get(unit);
- retval = 0;
+ return 0;
}
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
out:
- return retval;
+ return -ENXIO;
}
static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
@@ -196,6 +209,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
break;
zfcp_erp_wait(adapter);
+ fc_block_scsi_eh(scpnt);
if (!(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_RUNNING)) {
zfcp_dbf_scsi_abort("nres", adapter->dbf, scpnt, NULL,
@@ -235,6 +249,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
break;
zfcp_erp_wait(adapter);
+ fc_block_scsi_eh(scpnt);
if (!(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_RUNNING)) {
zfcp_dbf_scsi_devreset("nres", tm_flags, unit, scpnt);
@@ -249,9 +264,6 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
zfcp_dbf_scsi_devreset("fail", tm_flags, unit, scpnt);
retval = FAILED;
- } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) {
- zfcp_dbf_scsi_devreset("nsup", tm_flags, unit, scpnt);
- retval = FAILED;
} else
zfcp_dbf_scsi_devreset("okay", tm_flags, unit, scpnt);
@@ -261,12 +273,12 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt)
{
- return zfcp_task_mgmt_function(scpnt, FCP_LOGICAL_UNIT_RESET);
+ return zfcp_task_mgmt_function(scpnt, FCP_TMF_LUN_RESET);
}
static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt)
{
- return zfcp_task_mgmt_function(scpnt, FCP_TARGET_RESET);
+ return zfcp_task_mgmt_function(scpnt, FCP_TMF_TGT_RESET);
}
static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
@@ -276,6 +288,7 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
zfcp_erp_adapter_reopen(adapter, 0, "schrh_1", scpnt);
zfcp_erp_wait(adapter);
+ fc_block_scsi_eh(scpnt);
return SUCCESS;
}
@@ -303,7 +316,7 @@ int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
adapter->scsi_host->max_lun = 1;
adapter->scsi_host->max_channel = 0;
adapter->scsi_host->unique_id = dev_id.devno;
- adapter->scsi_host->max_cmd_len = 255;
+ adapter->scsi_host->max_cmd_len = 16; /* in struct fcp_cmnd */
adapter->scsi_host->transportt = zfcp_data.scsi_transport_template;
adapter->scsi_host->hostdata[0] = (unsigned long) adapter;
@@ -325,12 +338,11 @@ void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
if (!shost)
return;
- read_lock_irq(&zfcp_data.config_lock);
- list_for_each_entry(port, &adapter->port_list_head, list)
- if (port->rport)
- port->rport = NULL;
+ read_lock_irq(&adapter->port_list_lock);
+ list_for_each_entry(port, &adapter->port_list, list)
+ port->rport = NULL;
+ read_unlock_irq(&adapter->port_list_lock);
- read_unlock_irq(&zfcp_data.config_lock);
fc_remove_host(shost);
scsi_remove_host(shost);
scsi_host_put(shost);
@@ -348,7 +360,7 @@ zfcp_init_fc_host_stats(struct zfcp_adapter *adapter)
fc_stats = kmalloc(sizeof(*fc_stats), GFP_KERNEL);
if (!fc_stats)
return NULL;
- adapter->fc_stats = fc_stats; /* freed in adater_dequeue */
+ adapter->fc_stats = fc_stats; /* freed in adapter_release */
}
memset(adapter->fc_stats, 0, sizeof(*adapter->fc_stats));
return adapter->fc_stats;
@@ -464,7 +476,7 @@ static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
adapter->stats_reset = jiffies/HZ;
kfree(adapter->stats_reset_data);
adapter->stats_reset_data = data; /* finally freed in
- adapter_dequeue */
+ adapter_release */
}
}
@@ -495,7 +507,7 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
* @rport: The FC rport where to teminate I/O
*
* Abort all pending SCSI commands for a port by closing the
- * port. Using a reopen for avoids a conflict with a shutdown
+ * port. Using a reopen avoiding a conflict with a shutdown
* overwriting a reopen.
*/
static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
@@ -505,15 +517,11 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
struct zfcp_adapter *adapter =
(struct zfcp_adapter *)shost->hostdata[0];
- write_lock_irq(&zfcp_data.config_lock);
port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
- if (port)
- zfcp_port_get(port);
- write_unlock_irq(&zfcp_data.config_lock);
if (port) {
zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL);
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
}
@@ -555,31 +563,34 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
{
- zfcp_port_get(port);
+ get_device(&port->sysfs_device);
port->rport_task = RPORT_ADD;
if (!queue_work(port->adapter->work_queue, &port->rport_work))
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
void zfcp_scsi_schedule_rport_block(struct zfcp_port *port)
{
- zfcp_port_get(port);
+ get_device(&port->sysfs_device);
port->rport_task = RPORT_DEL;
if (port->rport && queue_work(port->adapter->work_queue,
&port->rport_work))
return;
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter)
{
+ unsigned long flags;
struct zfcp_port *port;
- list_for_each_entry(port, &adapter->port_list_head, list)
+ read_lock_irqsave(&adapter->port_list_lock, flags);
+ list_for_each_entry(port, &adapter->port_list, list)
zfcp_scsi_schedule_rport_block(port);
+ read_unlock_irqrestore(&adapter->port_list_lock, flags);
}
void zfcp_scsi_rport_work(struct work_struct *work)
@@ -597,7 +608,7 @@ void zfcp_scsi_rport_work(struct work_struct *work)
}
}
- zfcp_port_put(port);
+ put_device(&port->sysfs_device);
}
@@ -615,21 +626,7 @@ void zfcp_scsi_scan(struct work_struct *work)
scsilun_to_int((struct scsi_lun *)
&unit->fcp_lun), 0);
- zfcp_unit_put(unit);
-}
-
-static int zfcp_execute_fc_job(struct fc_bsg_job *job)
-{
- switch (job->request->msgcode) {
- case FC_BSG_RPT_ELS:
- case FC_BSG_HST_ELS_NOLOGIN:
- return zfcp_fc_execute_els_fc_job(job);
- case FC_BSG_RPT_CT:
- case FC_BSG_HST_CT:
- return zfcp_fc_execute_ct_fc_job(job);
- default:
- return -EINVAL;
- }
+ put_device(&unit->sysfs_device);
}
struct fc_function_template zfcp_transport_functions = {
@@ -643,6 +640,7 @@ struct fc_function_template zfcp_transport_functions = {
.show_host_port_name = 1,
.show_host_permanent_port_name = 1,
.show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
.show_host_supported_speeds = 1,
.show_host_maxframe_size = 1,
.show_host_serial_number = 1,
@@ -652,13 +650,15 @@ struct fc_function_template zfcp_transport_functions = {
.get_host_port_state = zfcp_get_host_port_state,
.terminate_rport_io = zfcp_scsi_terminate_rport_io,
.show_host_port_state = 1,
- .bsg_request = zfcp_execute_fc_job,
+ .show_host_active_fc4s = 1,
+ .bsg_request = zfcp_fc_exec_bsg_job,
/* no functions registered for following dynamic attributes but
directly set by LLDD */
.show_host_port_type = 1,
.show_host_speed = 1,
.show_host_port_id = 1,
.disable_target_scan = 1,
+ .dd_bsg_size = sizeof(struct zfcp_fsf_ct_els),
};
struct zfcp_data zfcp_data = {
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index d31000886ca..f539e006683 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -3,7 +3,7 @@
*
* sysfs attributes.
*
- * Copyright IBM Corporation 2008
+ * Copyright IBM Corporation 2008, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -19,30 +19,44 @@ static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \
struct device_attribute *at,\
char *buf) \
{ \
- struct _feat_def *_feat = dev_get_drvdata(dev); \
+ struct _feat_def *_feat = container_of(dev, struct _feat_def, \
+ sysfs_device); \
\
return sprintf(buf, _format, _value); \
} \
static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \
zfcp_sysfs_##_feat##_##_name##_show, NULL);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, status, "0x%08x\n",
- atomic_read(&adapter->status));
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwnn, "0x%016llx\n",
- (unsigned long long) adapter->peer_wwnn);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n",
- (unsigned long long) adapter->peer_wwpn);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_d_id, "0x%06x\n",
- adapter->peer_d_id);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, card_version, "0x%04x\n",
- adapter->hydra_version);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, lic_version, "0x%08x\n",
- adapter->fsf_lic_version);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, hardware_version, "0x%08x\n",
- adapter->hardware_version);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, in_recovery, "%d\n",
- (atomic_read(&adapter->status) &
- ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+#define ZFCP_DEFINE_A_ATTR(_name, _format, _value) \
+static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \
+ struct device_attribute *at,\
+ char *buf) \
+{ \
+ struct ccw_device *cdev = to_ccwdev(dev); \
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); \
+ int i; \
+ \
+ if (!adapter) \
+ return -ENODEV; \
+ \
+ i = sprintf(buf, _format, _value); \
+ zfcp_ccw_adapter_put(adapter); \
+ return i; \
+} \
+static ZFCP_DEV_ATTR(adapter, _name, S_IRUGO, \
+ zfcp_sysfs_adapter_##_name##_show, NULL);
+
+ZFCP_DEFINE_A_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
+ZFCP_DEFINE_A_ATTR(peer_wwnn, "0x%016llx\n",
+ (unsigned long long) adapter->peer_wwnn);
+ZFCP_DEFINE_A_ATTR(peer_wwpn, "0x%016llx\n",
+ (unsigned long long) adapter->peer_wwpn);
+ZFCP_DEFINE_A_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id);
+ZFCP_DEFINE_A_ATTR(card_version, "0x%04x\n", adapter->hydra_version);
+ZFCP_DEFINE_A_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
+ZFCP_DEFINE_A_ATTR(hardware_version, "0x%08x\n", adapter->hardware_version);
+ZFCP_DEFINE_A_ATTR(in_recovery, "%d\n", (atomic_read(&adapter->status) &
+ ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n",
atomic_read(&port->status));
@@ -73,7 +87,8 @@ static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
- struct _feat_def *_feat = dev_get_drvdata(dev); \
+ struct _feat_def *_feat = container_of(dev, struct _feat_def, \
+ sysfs_device); \
\
if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED) \
return sprintf(buf, "1\n"); \
@@ -84,15 +99,13 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
struct device_attribute *attr,\
const char *buf, size_t count)\
{ \
- struct _feat_def *_feat = dev_get_drvdata(dev); \
+ struct _feat_def *_feat = container_of(dev, struct _feat_def, \
+ sysfs_device); \
unsigned long val; \
int retval = 0; \
\
- mutex_lock(&zfcp_data.config_mutex); \
- if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) { \
- retval = -EBUSY; \
- goto out; \
- } \
+ if (!(_feat && get_device(&_feat->sysfs_device))) \
+ return -EBUSY; \
\
if (strict_strtoul(buf, 0, &val) || val != 0) { \
retval = -EINVAL; \
@@ -105,29 +118,82 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
_reopen_id, NULL); \
zfcp_erp_wait(_adapter); \
out: \
- mutex_unlock(&zfcp_data.config_mutex); \
+ put_device(&_feat->sysfs_device); \
return retval ? retval : (ssize_t) count; \
} \
static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \
zfcp_sysfs_##_feat##_failed_show, \
zfcp_sysfs_##_feat##_failed_store);
-ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, "syafai1", "syafai2");
ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, "sypfai1", "sypfai2");
ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, "syufai1", "syufai2");
+static ssize_t zfcp_sysfs_adapter_failed_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+ int i;
+
+ if (!adapter)
+ return -ENODEV;
+
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+ i = sprintf(buf, "1\n");
+ else
+ i = sprintf(buf, "0\n");
+
+ zfcp_ccw_adapter_put(adapter);
+ return i;
+}
+
+static ssize_t zfcp_sysfs_adapter_failed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+ unsigned long val;
+ int retval = 0;
+
+ if (!adapter)
+ return -ENODEV;
+
+ if (strict_strtoul(buf, 0, &val) || val != 0) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ zfcp_erp_modify_adapter_status(adapter, "syafai1", NULL,
+ ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
+ zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
+ "syafai2", NULL);
+ zfcp_erp_wait(adapter);
+out:
+ zfcp_ccw_adapter_put(adapter);
+ return retval ? retval : (ssize_t) count;
+}
+static ZFCP_DEV_ATTR(adapter, failed, S_IWUSR | S_IRUGO,
+ zfcp_sysfs_adapter_failed_show,
+ zfcp_sysfs_adapter_failed_store);
+
static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct zfcp_adapter *adapter = dev_get_drvdata(dev);
- int ret;
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
- if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE)
- return -EBUSY;
+ if (!adapter)
+ return -ENODEV;
- ret = zfcp_fc_scan_ports(adapter);
- return ret ? ret : (ssize_t) count;
+ /* sync the user-space- with the kernel-invocation of scan_work */
+ queue_work(adapter->work_queue, &adapter->scan_work);
+ flush_work(&adapter->scan_work);
+ zfcp_ccw_adapter_put(adapter);
+
+ return (ssize_t) count;
}
static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
zfcp_sysfs_port_rescan_store);
@@ -136,44 +202,34 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct zfcp_adapter *adapter = dev_get_drvdata(dev);
+ struct ccw_device *cdev = to_ccwdev(dev);
+ struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
struct zfcp_port *port;
u64 wwpn;
- int retval = 0;
- LIST_HEAD(port_remove_lh);
+ int retval = -EINVAL;
- mutex_lock(&zfcp_data.config_mutex);
- if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
- retval = -EBUSY;
- goto out;
- }
+ if (!adapter)
+ return -ENODEV;
- if (strict_strtoull(buf, 0, (unsigned long long *) &wwpn)) {
- retval = -EINVAL;
+ if (strict_strtoull(buf, 0, (unsigned long long *) &wwpn))
goto out;
- }
- write_lock_irq(&zfcp_data.config_lock);
port = zfcp_get_port_by_wwpn(adapter, wwpn);
- if (port && (atomic_read(&port->refcount) == 0)) {
- zfcp_port_get(port);
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
- list_move(&port->list, &port_remove_lh);
- } else
- port = NULL;
- write_unlock_irq(&zfcp_data.config_lock);
-
- if (!port) {
- retval = -ENXIO;
+ if (!port)
goto out;
- }
+ else
+ retval = 0;
+
+ write_lock_irq(&adapter->port_list_lock);
+ list_del(&port->list);
+ write_unlock_irq(&adapter->port_list_lock);
+
+ put_device(&port->sysfs_device);
zfcp_erp_port_shutdown(port, 0, "syprs_1", NULL);
- zfcp_erp_wait(adapter);
- zfcp_port_put(port);
- zfcp_port_dequeue(port);
+ zfcp_device_unregister(&port->sysfs_device, &zfcp_sysfs_port_attrs);
out:
- mutex_unlock(&zfcp_data.config_mutex);
+ zfcp_ccw_adapter_put(adapter);
return retval ? retval : (ssize_t) count;
}
static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL,
@@ -202,16 +258,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct zfcp_port *port = dev_get_drvdata(dev);
+ struct zfcp_port *port = container_of(dev, struct zfcp_port,
+ sysfs_device);
struct zfcp_unit *unit;
u64 fcp_lun;
int retval = -EINVAL;
- mutex_lock(&zfcp_data.config_mutex);
- if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
- retval = -EBUSY;
- goto out;
- }
+ if (!(port && get_device(&port->sysfs_device)))
+ return -EBUSY;
if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
goto out;
@@ -219,15 +273,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
unit = zfcp_unit_enqueue(port, fcp_lun);
if (IS_ERR(unit))
goto out;
-
- retval = 0;
+ else
+ retval = 0;
zfcp_erp_unit_reopen(unit, 0, "syuas_1", NULL);
zfcp_erp_wait(unit->port->adapter);
flush_work(&unit->scsi_work);
- zfcp_unit_put(unit);
out:
- mutex_unlock(&zfcp_data.config_mutex);
+ put_device(&port->sysfs_device);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
@@ -236,54 +289,37 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct zfcp_port *port = dev_get_drvdata(dev);
+ struct zfcp_port *port = container_of(dev, struct zfcp_port,
+ sysfs_device);
struct zfcp_unit *unit;
u64 fcp_lun;
- int retval = 0;
- LIST_HEAD(unit_remove_lh);
+ int retval = -EINVAL;
- mutex_lock(&zfcp_data.config_mutex);
- if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
- retval = -EBUSY;
- goto out;
- }
+ if (!(port && get_device(&port->sysfs_device)))
+ return -EBUSY;
- if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) {
- retval = -EINVAL;
+ if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
goto out;
- }
- write_lock_irq(&zfcp_data.config_lock);
unit = zfcp_get_unit_by_lun(port, fcp_lun);
- if (unit) {
- write_unlock_irq(&zfcp_data.config_lock);
- /* wait for possible timeout during SCSI probe */
- flush_work(&unit->scsi_work);
- write_lock_irq(&zfcp_data.config_lock);
-
- if (atomic_read(&unit->refcount) == 0) {
- zfcp_unit_get(unit);
- atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
- &unit->status);
- list_move(&unit->list, &unit_remove_lh);
- } else {
- unit = NULL;
- }
- }
+ if (!unit)
+ goto out;
+ else
+ retval = 0;
- write_unlock_irq(&zfcp_data.config_lock);
+ /* wait for possible timeout during SCSI probe */
+ flush_work(&unit->scsi_work);
- if (!unit) {
- retval = -ENXIO;
- goto out;
- }
+ write_lock_irq(&port->unit_list_lock);
+ list_del(&unit->list);
+ write_unlock_irq(&port->unit_list_lock);
+
+ put_device(&unit->sysfs_device);
zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL);
- zfcp_erp_wait(unit->port->adapter);
- zfcp_unit_put(unit);
- zfcp_unit_dequeue(unit);
+ zfcp_device_unregister(&unit->sysfs_device, &zfcp_sysfs_unit_attrs);
out:
- mutex_unlock(&zfcp_data.config_mutex);
+ put_device(&port->sysfs_device);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c
index 58e583b61e6..aa2b60a868b 100644
--- a/drivers/sbus/char/envctrl.c
+++ b/drivers/sbus/char/envctrl.c
@@ -92,11 +92,11 @@
#define ENVCTRL_CPUTEMP_MON 1 /* cpu temperature monitor */
#define ENVCTRL_CPUVOLTAGE_MON 2 /* voltage monitor */
#define ENVCTRL_FANSTAT_MON 3 /* fan status monitor */
-#define ENVCTRL_ETHERTEMP_MON 4 /* ethernet temperarture */
+#define ENVCTRL_ETHERTEMP_MON 4 /* ethernet temperature */
/* monitor */
#define ENVCTRL_VOLTAGESTAT_MON 5 /* voltage status monitor */
#define ENVCTRL_MTHRBDTEMP_MON 6 /* motherboard temperature */
-#define ENVCTRL_SCSITEMP_MON 7 /* scsi temperarture */
+#define ENVCTRL_SCSITEMP_MON 7 /* scsi temperature */
#define ENVCTRL_GLOBALADDR_MON 8 /* global address */
/* Child device type.
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c
index 36c21b19e5d..3bf75924741 100644
--- a/drivers/scsi/3w-9xxx.c
+++ b/drivers/scsi/3w-9xxx.c
@@ -186,8 +186,12 @@ static ssize_t twa_show_stats(struct device *dev,
} /* End twa_show_stats() */
/* This function will set a devices queue depth */
-static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > TW_Q_LENGTH-2)
queue_depth = TW_Q_LENGTH-2;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
@@ -732,7 +736,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
break;
case TW_IOCTL_GET_COMPATIBILITY_INFO:
tw_ioctl->driver_command.status = 0;
- /* Copy compatiblity struct into ioctl data buffer */
+ /* Copy compatibility struct into ioctl data buffer */
tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer;
memcpy(tw_compat_info, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info));
break;
diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c
new file mode 100644
index 00000000000..4d314d740de
--- /dev/null
+++ b/drivers/scsi/3w-sas.c
@@ -0,0 +1,1924 @@
+/*
+ 3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@lsi.com>
+
+ Copyright (C) 2009 LSI Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Controllers supported by this driver:
+
+ LSI 3ware 9750 6Gb/s SAS/SATA-RAID
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@lsi.com
+
+ For more information, goto:
+ http://www.lsi.com
+
+ History
+ -------
+ 3.26.02.000 - Initial driver release.
+*/
+
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_cmnd.h>
+#include "3w-sas.h"
+
+/* Globals */
+#define TW_DRIVER_VERSION "3.26.02.000"
+static TW_Device_Extension *twl_device_extension_list[TW_MAX_SLOT];
+static unsigned int twl_device_extension_count;
+static int twl_major = -1;
+extern struct timezone sys_tz;
+
+/* Module parameters */
+MODULE_AUTHOR ("LSI");
+MODULE_DESCRIPTION ("LSI 3ware SAS/SATA-RAID Linux Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(TW_DRIVER_VERSION);
+
+static int use_msi;
+module_param(use_msi, int, S_IRUGO);
+MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0");
+
+/* Function prototypes */
+static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset);
+
+/* Functions */
+
+/* This function returns AENs through sysfs */
+static ssize_t twl_sysfs_aen_read(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *outbuf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct Scsi_Host *shost = class_to_shost(dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
+ unsigned long flags = 0;
+ ssize_t ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ ret = memory_read_from_buffer(outbuf, count, &offset, tw_dev->event_queue[0], sizeof(TW_Event) * TW_Q_LENGTH);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ return ret;
+} /* End twl_sysfs_aen_read() */
+
+/* aen_read sysfs attribute initializer */
+static struct bin_attribute twl_sysfs_aen_read_attr = {
+ .attr = {
+ .name = "3ware_aen_read",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = twl_sysfs_aen_read
+};
+
+/* This function returns driver compatibility info through sysfs */
+static ssize_t twl_sysfs_compat_info(struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *outbuf, loff_t offset, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct Scsi_Host *shost = class_to_shost(dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
+ unsigned long flags = 0;
+ ssize_t ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ ret = memory_read_from_buffer(outbuf, count, &offset, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info));
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ return ret;
+} /* End twl_sysfs_compat_info() */
+
+/* compat_info sysfs attribute initializer */
+static struct bin_attribute twl_sysfs_compat_info_attr = {
+ .attr = {
+ .name = "3ware_compat_info",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = twl_sysfs_compat_info
+};
+
+/* Show some statistics about the card */
+static ssize_t twl_show_stats(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *host = class_to_shost(dev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+ unsigned long flags = 0;
+ ssize_t len;
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ len = snprintf(buf, PAGE_SIZE, "3w-sas Driver version: %s\n"
+ "Current commands posted: %4d\n"
+ "Max commands posted: %4d\n"
+ "Last sgl length: %4d\n"
+ "Max sgl length: %4d\n"
+ "Last sector count: %4d\n"
+ "Max sector count: %4d\n"
+ "SCSI Host Resets: %4d\n"
+ "AEN's: %4d\n",
+ TW_DRIVER_VERSION,
+ tw_dev->posted_request_count,
+ tw_dev->max_posted_request_count,
+ tw_dev->sgl_entries,
+ tw_dev->max_sgl_entries,
+ tw_dev->sector_count,
+ tw_dev->max_sector_count,
+ tw_dev->num_resets,
+ tw_dev->aen_count);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ return len;
+} /* End twl_show_stats() */
+
+/* This function will set a devices queue depth */
+static int twl_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
+{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
+ if (queue_depth > TW_Q_LENGTH-2)
+ queue_depth = TW_Q_LENGTH-2;
+ scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
+ return queue_depth;
+} /* End twl_change_queue_depth() */
+
+/* stats sysfs attribute initializer */
+static struct device_attribute twl_host_stats_attr = {
+ .attr = {
+ .name = "3ware_stats",
+ .mode = S_IRUGO,
+ },
+ .show = twl_show_stats
+};
+
+/* Host attributes initializer */
+static struct device_attribute *twl_host_attrs[] = {
+ &twl_host_stats_attr,
+ NULL,
+};
+
+/* This function will look up an AEN severity string */
+static char *twl_aen_severity_lookup(unsigned char severity_code)
+{
+ char *retval = NULL;
+
+ if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) ||
+ (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG))
+ goto out;
+
+ retval = twl_aen_severity_table[severity_code];
+out:
+ return retval;
+} /* End twl_aen_severity_lookup() */
+
+/* This function will queue an event */
+static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
+{
+ u32 local_time;
+ struct timeval time;
+ TW_Event *event;
+ unsigned short aen;
+ char host[16];
+ char *error_str;
+
+ tw_dev->aen_count++;
+
+ /* Fill out event info */
+ event = tw_dev->event_queue[tw_dev->error_index];
+
+ host[0] = '\0';
+ if (tw_dev->host)
+ sprintf(host, " scsi%d:", tw_dev->host->host_no);
+
+ aen = le16_to_cpu(header->status_block.error);
+ memset(event, 0, sizeof(TW_Event));
+
+ event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
+ do_gettimeofday(&time);
+ local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
+ event->time_stamp_sec = local_time;
+ event->aen_code = aen;
+ event->retrieved = TW_AEN_NOT_RETRIEVED;
+ event->sequence_id = tw_dev->error_sequence_id;
+ tw_dev->error_sequence_id++;
+
+ /* Check for embedded error string */
+ error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]);
+
+ header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
+ event->parameter_len = strlen(header->err_specific_desc);
+ memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str));
+ if (event->severity != TW_AEN_SEVERITY_DEBUG)
+ printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
+ host,
+ twl_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)),
+ TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, error_str,
+ header->err_specific_desc);
+ else
+ tw_dev->aen_count--;
+
+ tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
+} /* End twl_aen_queue_event() */
+
+/* This function will attempt to post a command packet to the board */
+static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
+{
+ dma_addr_t command_que_value;
+
+ command_que_value = tw_dev->command_packet_phys[request_id];
+ command_que_value += TW_COMMAND_OFFSET;
+
+ /* First write upper 4 bytes */
+ writel((u32)((u64)command_que_value >> 32), TWL_HIBQPH_REG_ADDR(tw_dev));
+ /* Then the lower 4 bytes */
+ writel((u32)(command_que_value | TWL_PULL_MODE), TWL_HIBQPL_REG_ADDR(tw_dev));
+
+ tw_dev->state[request_id] = TW_S_POSTED;
+ tw_dev->posted_request_count++;
+ if (tw_dev->posted_request_count > tw_dev->max_posted_request_count)
+ tw_dev->max_posted_request_count = tw_dev->posted_request_count;
+
+ return 0;
+} /* End twl_post_command_packet() */
+
+/* This function will perform a pci-dma mapping for a scatter gather list */
+static int twl_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
+{
+ int use_sg;
+ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+
+ use_sg = scsi_dma_map(cmd);
+ if (!use_sg)
+ return 0;
+ else if (use_sg < 0) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Failed to map scatter gather list");
+ return 0;
+ }
+
+ cmd->SCp.phase = TW_PHASE_SGLIST;
+ cmd->SCp.have_data_in = use_sg;
+
+ return use_sg;
+} /* End twl_map_scsi_sg_data() */
+
+/* This function hands scsi cdb's to the firmware */
+static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command_Apache *command_packet;
+ int i, sg_count;
+ struct scsi_cmnd *srb = NULL;
+ struct scatterlist *sglist = NULL, *sg;
+ int retval = 1;
+
+ if (tw_dev->srb[request_id]) {
+ srb = tw_dev->srb[request_id];
+ if (scsi_sglist(srb))
+ sglist = scsi_sglist(srb);
+ }
+
+ /* Initialize command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ full_command_packet->header.header_desc.size_header = 128;
+ full_command_packet->header.status_block.error = 0;
+ full_command_packet->header.status_block.severity__reserved = 0;
+
+ command_packet = &full_command_packet->command.newcommand;
+ command_packet->status = 0;
+ command_packet->opcode__reserved = TW_OPRES_IN(0, TW_OP_EXECUTE_SCSI);
+
+ /* We forced 16 byte cdb use earlier */
+ if (!cdb)
+ memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN);
+ else
+ memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN);
+
+ if (srb) {
+ command_packet->unit = srb->device->id;
+ command_packet->request_id__lunl =
+ cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id));
+ } else {
+ command_packet->request_id__lunl =
+ cpu_to_le16(TW_REQ_LUN_IN(0, request_id));
+ command_packet->unit = 0;
+ }
+
+ command_packet->sgl_offset = 16;
+
+ if (!sglistarg) {
+ /* Map sglist from scsi layer to cmd packet */
+ if (scsi_sg_count(srb)) {
+ sg_count = twl_map_scsi_sg_data(tw_dev, request_id);
+ if (sg_count == 0)
+ goto out;
+
+ scsi_for_each_sg(srb, sg, sg_count, i) {
+ command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg));
+ command_packet->sg_list[i].length = TW_CPU_TO_SGL(sg_dma_len(sg));
+ }
+ command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id])));
+ }
+ } else {
+ /* Internal cdb post */
+ for (i = 0; i < use_sg; i++) {
+ command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address);
+ command_packet->sg_list[i].length = TW_CPU_TO_SGL(sglistarg[i].length);
+ }
+ command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg));
+ }
+
+ /* Update some stats */
+ if (srb) {
+ tw_dev->sector_count = scsi_bufflen(srb) / 512;
+ if (tw_dev->sector_count > tw_dev->max_sector_count)
+ tw_dev->max_sector_count = tw_dev->sector_count;
+ tw_dev->sgl_entries = scsi_sg_count(srb);
+ if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
+ tw_dev->max_sgl_entries = tw_dev->sgl_entries;
+ }
+
+ /* Now post the command to the board */
+ retval = twl_post_command_packet(tw_dev, request_id);
+
+out:
+ return retval;
+} /* End twl_scsiop_execute_scsi() */
+
+/* This function will read the aen queue from the isr */
+static int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
+{
+ char cdb[TW_MAX_CDB_LEN];
+ TW_SG_Entry_ISO sglist[1];
+ TW_Command_Full *full_command_packet;
+ int retval = 1;
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+ /* Initialize cdb */
+ memset(&cdb, 0, TW_MAX_CDB_LEN);
+ cdb[0] = REQUEST_SENSE; /* opcode */
+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+ /* Initialize sglist */
+ memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
+ sglist[0].length = TW_SECTOR_SIZE;
+ sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Now post the command packet */
+ if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Post failed while reading AEN queue");
+ goto out;
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_aen_read_queue() */
+
+/* This function will sync firmware time with the host time */
+static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
+{
+ u32 schedulertime;
+ struct timeval utc;
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Param_Apache *param;
+ u32 local_time;
+
+ /* Fill out the command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ command_packet = &full_command_packet->command.oldcommand;
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
+ command_packet->request_id = request_id;
+ command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
+ command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE);
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->byte6_offset.parameter_count = cpu_to_le16(1);
+
+ /* Setup the param */
+ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+ memset(param, 0, TW_SECTOR_SIZE);
+ param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */
+ param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */
+ param->parameter_size_bytes = cpu_to_le16(4);
+
+ /* Convert system time in UTC to local time seconds since last
+ Sunday 12:00AM */
+ do_gettimeofday(&utc);
+ local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
+ schedulertime = local_time - (3 * 86400);
+ schedulertime = cpu_to_le32(schedulertime % 604800);
+
+ memcpy(param->data, &schedulertime, sizeof(u32));
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Now post the command */
+ twl_post_command_packet(tw_dev, request_id);
+} /* End twl_aen_sync_time() */
+
+/* This function will assign an available request id */
+static void twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
+{
+ *request_id = tw_dev->free_queue[tw_dev->free_head];
+ tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
+ tw_dev->state[*request_id] = TW_S_STARTED;
+} /* End twl_get_request_id() */
+
+/* This function will free a request id */
+static void twl_free_request_id(TW_Device_Extension *tw_dev, int request_id)
+{
+ tw_dev->free_queue[tw_dev->free_tail] = request_id;
+ tw_dev->state[request_id] = TW_S_FINISHED;
+ tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
+} /* End twl_free_request_id() */
+
+/* This function will complete an aen request from the isr */
+static int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Command_Apache_Header *header;
+ unsigned short aen;
+ int retval = 1;
+
+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+ tw_dev->posted_request_count--;
+ aen = le16_to_cpu(header->status_block.error);
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ command_packet = &full_command_packet->command.oldcommand;
+
+ /* First check for internal completion of set param for time sync */
+ if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) {
+ /* Keep reading the queue in case there are more aen's */
+ if (twl_aen_read_queue(tw_dev, request_id))
+ goto out2;
+ else {
+ retval = 0;
+ goto out;
+ }
+ }
+
+ switch (aen) {
+ case TW_AEN_QUEUE_EMPTY:
+ /* Quit reading the queue if this is the last one */
+ break;
+ case TW_AEN_SYNC_TIME_WITH_HOST:
+ twl_aen_sync_time(tw_dev, request_id);
+ retval = 0;
+ goto out;
+ default:
+ twl_aen_queue_event(tw_dev, header);
+
+ /* If there are more aen's, keep reading the queue */
+ if (twl_aen_read_queue(tw_dev, request_id))
+ goto out2;
+ else {
+ retval = 0;
+ goto out;
+ }
+ }
+ retval = 0;
+out2:
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+out:
+ return retval;
+} /* End twl_aen_complete() */
+
+/* This function will poll for a response */
+static int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
+{
+ unsigned long before;
+ dma_addr_t mfa;
+ u32 regh, regl;
+ u32 response;
+ int retval = 1;
+ int found = 0;
+
+ before = jiffies;
+
+ while (!found) {
+ if (sizeof(dma_addr_t) > 4) {
+ regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev));
+ regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+ mfa = ((u64)regh << 32) | regl;
+ } else
+ mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+
+ response = (u32)mfa;
+
+ if (TW_RESID_OUT(response) == request_id)
+ found = 1;
+
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_poll_response() */
+
+/* This function will drain the aen queue */
+static int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
+{
+ int request_id = 0;
+ char cdb[TW_MAX_CDB_LEN];
+ TW_SG_Entry_ISO sglist[1];
+ int finished = 0, count = 0;
+ TW_Command_Full *full_command_packet;
+ TW_Command_Apache_Header *header;
+ unsigned short aen;
+ int first_reset = 0, queue = 0, retval = 1;
+
+ if (no_check_reset)
+ first_reset = 0;
+ else
+ first_reset = 1;
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+ /* Initialize cdb */
+ memset(&cdb, 0, TW_MAX_CDB_LEN);
+ cdb[0] = REQUEST_SENSE; /* opcode */
+ cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+ /* Initialize sglist */
+ memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
+ sglist[0].length = TW_SECTOR_SIZE;
+ sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+ /* Mark internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ do {
+ /* Send command to the board */
+ if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "Error posting request sense");
+ goto out;
+ }
+
+ /* Now poll for completion */
+ if (twl_poll_response(tw_dev, request_id, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "No valid response while draining AEN queue");
+ tw_dev->posted_request_count--;
+ goto out;
+ }
+
+ tw_dev->posted_request_count--;
+ header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+ aen = le16_to_cpu(header->status_block.error);
+ queue = 0;
+ count++;
+
+ switch (aen) {
+ case TW_AEN_QUEUE_EMPTY:
+ if (first_reset != 1)
+ goto out;
+ else
+ finished = 1;
+ break;
+ case TW_AEN_SOFT_RESET:
+ if (first_reset == 0)
+ first_reset = 1;
+ else
+ queue = 1;
+ break;
+ case TW_AEN_SYNC_TIME_WITH_HOST:
+ break;
+ default:
+ queue = 1;
+ }
+
+ /* Now queue an event info */
+ if (queue)
+ twl_aen_queue_event(tw_dev, header);
+ } while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));
+
+ if (count == TW_MAX_AEN_DRAIN)
+ goto out;
+
+ retval = 0;
+out:
+ tw_dev->state[request_id] = TW_S_INITIAL;
+ return retval;
+} /* End twl_aen_drain_queue() */
+
+/* This function will allocate memory and check if it is correctly aligned */
+static int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
+{
+ int i;
+ dma_addr_t dma_handle;
+ unsigned long *cpu_addr;
+ int retval = 1;
+
+ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+ if (!cpu_addr) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
+ goto out;
+ }
+
+ memset(cpu_addr, 0, size*TW_Q_LENGTH);
+
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ switch(which) {
+ case 0:
+ tw_dev->command_packet_phys[i] = dma_handle+(i*size);
+ tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ case 1:
+ tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
+ tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ case 2:
+ tw_dev->sense_buffer_phys[i] = dma_handle+(i*size);
+ tw_dev->sense_buffer_virt[i] = (TW_Command_Apache_Header *)((unsigned char *)cpu_addr + (i*size));
+ break;
+ }
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_allocate_memory() */
+
+/* This function will load the request id and various sgls for ioctls */
+static void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
+{
+ TW_Command *oldcommand;
+ TW_Command_Apache *newcommand;
+ TW_SG_Entry_ISO *sgl;
+ unsigned int pae = 0;
+
+ if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4))
+ pae = 1;
+
+ if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {
+ newcommand = &full_command_packet->command.newcommand;
+ newcommand->request_id__lunl =
+ cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id));
+ if (length) {
+ newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ newcommand->sg_list[0].length = TW_CPU_TO_SGL(length);
+ }
+ newcommand->sgl_entries__lunh =
+ cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0));
+ } else {
+ oldcommand = &full_command_packet->command.oldcommand;
+ oldcommand->request_id = request_id;
+
+ if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) {
+ /* Load the sg list */
+ sgl = (TW_SG_Entry_ISO *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry_ISO)/4) + pae + (sizeof(dma_addr_t) > 4 ? 1 : 0));
+ sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+ sgl->length = TW_CPU_TO_SGL(length);
+ oldcommand->size += pae;
+ oldcommand->size += sizeof(dma_addr_t) > 4 ? 1 : 0;
+ }
+ }
+} /* End twl_load_sgl() */
+
+/* This function handles ioctl for the character device
+ This interface is used by smartmontools open source software */
+static int twl_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long timeout;
+ unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
+ dma_addr_t dma_handle;
+ int request_id = 0;
+ TW_Ioctl_Driver_Command driver_command;
+ TW_Ioctl_Buf_Apache *tw_ioctl;
+ TW_Command_Full *full_command_packet;
+ TW_Device_Extension *tw_dev = twl_device_extension_list[iminor(inode)];
+ int retval = -EFAULT;
+ void __user *argp = (void __user *)arg;
+
+ /* Only let one of these through at a time */
+ if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) {
+ retval = -EINTR;
+ goto out;
+ }
+
+ /* First copy down the driver command */
+ if (copy_from_user(&driver_command, argp, sizeof(TW_Ioctl_Driver_Command)))
+ goto out2;
+
+ /* Check data buffer size */
+ if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) {
+ retval = -EINVAL;
+ goto out2;
+ }
+
+ /* Hardware can only do multiple of 512 byte transfers */
+ data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;
+
+ /* Now allocate ioctl buf memory */
+ cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle, GFP_KERNEL);
+ if (!cpu_addr) {
+ retval = -ENOMEM;
+ goto out2;
+ }
+
+ tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
+
+ /* Now copy down the entire ioctl */
+ if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+ goto out3;
+
+ /* See which ioctl we are doing */
+ switch (cmd) {
+ case TW_IOCTL_FIRMWARE_PASS_THROUGH:
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ twl_get_request_id(tw_dev, &request_id);
+
+ /* Flag internal command */
+ tw_dev->srb[request_id] = NULL;
+
+ /* Flag chrdev ioctl */
+ tw_dev->chrdev_request_id = request_id;
+
+ full_command_packet = (TW_Command_Full *)&tw_ioctl->firmware_command;
+
+ /* Load request id and sglist for both command types */
+ twl_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
+
+ memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full));
+
+ /* Now post the command packet to the controller */
+ twl_post_command_packet(tw_dev, request_id);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
+
+ /* Now wait for command to complete */
+ timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+
+ /* We timed out, and didn't get an interrupt */
+ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+ /* Now we need to reset the board */
+ printk(KERN_WARNING "3w-sas: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n",
+ tw_dev->host->host_no, TW_DRIVER, 0x6,
+ cmd);
+ retval = -EIO;
+ twl_reset_device_extension(tw_dev, 1);
+ goto out3;
+ }
+
+ /* Now copy in the command packet response */
+ memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
+
+ /* Now complete the io */
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+ break;
+ default:
+ retval = -ENOTTY;
+ goto out3;
+ }
+
+ /* Now copy the entire response to userspace */
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+ retval = 0;
+out3:
+ /* Now free ioctl buf memory */
+ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
+out2:
+ mutex_unlock(&tw_dev->ioctl_lock);
+out:
+ return retval;
+} /* End twl_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int twl_chrdev_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor_number;
+ int retval = -ENODEV;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ retval = -EACCES;
+ goto out;
+ }
+
+ cycle_kernel_lock();
+ minor_number = iminor(inode);
+ if (minor_number >= twl_device_extension_count)
+ goto out;
+ retval = 0;
+out:
+ return retval;
+} /* End twl_chrdev_open() */
+
+/* File operations struct for character device */
+static const struct file_operations twl_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = twl_chrdev_ioctl,
+ .open = twl_chrdev_open,
+ .release = NULL
+};
+
+/* This function passes sense data from firmware to scsi layer */
+static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, int copy_sense, int print_host)
+{
+ TW_Command_Apache_Header *header;
+ TW_Command_Full *full_command_packet;
+ unsigned short error;
+ char *error_str;
+ int retval = 1;
+
+ header = tw_dev->sense_buffer_virt[i];
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+
+ /* Get embedded firmware error string */
+ error_str = &(header->err_specific_desc[strlen(header->err_specific_desc) + 1]);
+
+ /* Don't print error for Logical unit not supported during rollcall */
+ error = le16_to_cpu(header->status_block.error);
+ if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE) && (error != TW_ERROR_INVALID_FIELD_IN_CDB)) {
+ if (print_host)
+ printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+ tw_dev->host->host_no,
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ header->status_block.error,
+ error_str,
+ header->err_specific_desc);
+ else
+ printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+ TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+ header->status_block.error,
+ error_str,
+ header->err_specific_desc);
+ }
+
+ if (copy_sense) {
+ memcpy(tw_dev->srb[request_id]->sense_buffer, header->sense_data, TW_SENSE_DATA_LENGTH);
+ tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1);
+ goto out;
+ }
+out:
+ return retval;
+} /* End twl_fill_sense() */
+
+/* This function will free up device extension resources */
+static void twl_free_device_extension(TW_Device_Extension *tw_dev)
+{
+ if (tw_dev->command_packet_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ sizeof(TW_Command_Full)*TW_Q_LENGTH,
+ tw_dev->command_packet_virt[0],
+ tw_dev->command_packet_phys[0]);
+
+ if (tw_dev->generic_buffer_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ TW_SECTOR_SIZE*TW_Q_LENGTH,
+ tw_dev->generic_buffer_virt[0],
+ tw_dev->generic_buffer_phys[0]);
+
+ if (tw_dev->sense_buffer_virt[0])
+ pci_free_consistent(tw_dev->tw_pci_dev,
+ sizeof(TW_Command_Apache_Header)*
+ TW_Q_LENGTH,
+ tw_dev->sense_buffer_virt[0],
+ tw_dev->sense_buffer_phys[0]);
+
+ kfree(tw_dev->event_queue[0]);
+} /* End twl_free_device_extension() */
+
+/* This function will get parameter table entries from the firmware */
+static void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Command *command_packet;
+ TW_Param_Apache *param;
+ void *retval = NULL;
+
+ /* Setup the command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ command_packet = &full_command_packet->command.oldcommand;
+
+ command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+ command_packet->size = TW_COMMAND_SIZE;
+ command_packet->request_id = request_id;
+ command_packet->byte6_offset.block_count = cpu_to_le16(1);
+
+ /* Now setup the param */
+ param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+ memset(param, 0, TW_SECTOR_SIZE);
+ param->table_id = cpu_to_le16(table_id | 0x8000);
+ param->parameter_id = cpu_to_le16(parameter_id);
+ param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes);
+
+ command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
+ command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE);
+
+ /* Post the command packet to the board */
+ twl_post_command_packet(tw_dev, request_id);
+
+ /* Poll for completion */
+ if (twl_poll_response(tw_dev, request_id, 30))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "No valid response during get param")
+ else
+ retval = (void *)&(param->data[0]);
+
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_INITIAL;
+
+ return retval;
+} /* End twl_get_param() */
+
+/* This function will send an initconnection command to controller */
+static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits,
+ u32 set_features, unsigned short current_fw_srl,
+ unsigned short current_fw_arch_id,
+ unsigned short current_fw_branch,
+ unsigned short current_fw_build,
+ unsigned short *fw_on_ctlr_srl,
+ unsigned short *fw_on_ctlr_arch_id,
+ unsigned short *fw_on_ctlr_branch,
+ unsigned short *fw_on_ctlr_build,
+ u32 *init_connect_result)
+{
+ TW_Command_Full *full_command_packet;
+ TW_Initconnect *tw_initconnect;
+ int request_id = 0, retval = 1;
+
+ /* Initialize InitConnection command packet */
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+ memset(full_command_packet, 0, sizeof(TW_Command_Full));
+ full_command_packet->header.header_desc.size_header = 128;
+
+ tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
+ tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION);
+ tw_initconnect->request_id = request_id;
+ tw_initconnect->message_credits = cpu_to_le16(message_credits);
+ tw_initconnect->features = set_features;
+
+ /* Turn on 64-bit sgl support if we need to */
+ tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0;
+
+ tw_initconnect->features = cpu_to_le32(tw_initconnect->features);
+
+ if (set_features & TW_EXTENDED_INIT_CONNECT) {
+ tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
+ tw_initconnect->fw_srl = cpu_to_le16(current_fw_srl);
+ tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id);
+ tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch);
+ tw_initconnect->fw_build = cpu_to_le16(current_fw_build);
+ } else
+ tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
+
+ /* Send command packet to the board */
+ twl_post_command_packet(tw_dev, request_id);
+
+ /* Poll for completion */
+ if (twl_poll_response(tw_dev, request_id, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x8, "No valid response during init connection");
+ } else {
+ if (set_features & TW_EXTENDED_INIT_CONNECT) {
+ *fw_on_ctlr_srl = le16_to_cpu(tw_initconnect->fw_srl);
+ *fw_on_ctlr_arch_id = le16_to_cpu(tw_initconnect->fw_arch_id);
+ *fw_on_ctlr_branch = le16_to_cpu(tw_initconnect->fw_branch);
+ *fw_on_ctlr_build = le16_to_cpu(tw_initconnect->fw_build);
+ *init_connect_result = le32_to_cpu(tw_initconnect->result);
+ }
+ retval = 0;
+ }
+
+ tw_dev->posted_request_count--;
+ tw_dev->state[request_id] = TW_S_INITIAL;
+
+ return retval;
+} /* End twl_initconnection() */
+
+/* This function will initialize the fields of a device extension */
+static int twl_initialize_device_extension(TW_Device_Extension *tw_dev)
+{
+ int i, retval = 1;
+
+ /* Initialize command packet buffers */
+ if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x9, "Command packet memory allocation failed");
+ goto out;
+ }
+
+ /* Initialize generic buffer */
+ if (twl_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Generic memory allocation failed");
+ goto out;
+ }
+
+ /* Allocate sense buffers */
+ if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Apache_Header), 2)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xb, "Sense buffer allocation failed");
+ goto out;
+ }
+
+ /* Allocate event info space */
+ tw_dev->event_queue[0] = kcalloc(TW_Q_LENGTH, sizeof(TW_Event), GFP_KERNEL);
+ if (!tw_dev->event_queue[0]) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "Event info memory allocation failed");
+ goto out;
+ }
+
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event)));
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->error_sequence_id = 1;
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+ mutex_init(&tw_dev->ioctl_lock);
+ init_waitqueue_head(&tw_dev->ioctl_wqueue);
+
+ retval = 0;
+out:
+ return retval;
+} /* End twl_initialize_device_extension() */
+
+/* This function will perform a pci-dma unmap */
+static void twl_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
+{
+ struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+
+ if (cmd->SCp.phase == TW_PHASE_SGLIST)
+ scsi_dma_unmap(cmd);
+} /* End twl_unmap_scsi_data() */
+
+/* This function will handle attention interrupts */
+static int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev)
+{
+ int retval = 1;
+ u32 request_id, doorbell;
+
+ /* Read doorbell status */
+ doorbell = readl(TWL_HOBDB_REG_ADDR(tw_dev));
+
+ /* Check for controller errors */
+ if (doorbell & TWL_DOORBELL_CONTROLLER_ERROR) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "Microcontroller Error: clearing");
+ goto out;
+ }
+
+ /* Check if we need to perform an AEN drain */
+ if (doorbell & TWL_DOORBELL_ATTENTION_INTERRUPT) {
+ if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {
+ twl_get_request_id(tw_dev, &request_id);
+ if (twl_aen_read_queue(tw_dev, request_id)) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+ }
+ }
+ }
+
+ retval = 0;
+out:
+ /* Clear doorbell interrupt */
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+ /* Make sure the clear was flushed by reading it back */
+ readl(TWL_HOBDBC_REG_ADDR(tw_dev));
+
+ return retval;
+} /* End twl_handle_attention_interrupt() */
+
+/* Interrupt service routine */
+static irqreturn_t twl_interrupt(int irq, void *dev_instance)
+{
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
+ int i, handled = 0, error = 0;
+ dma_addr_t mfa = 0;
+ u32 reg, regl, regh, response, request_id = 0;
+ struct scsi_cmnd *cmd;
+ TW_Command_Full *full_command_packet;
+
+ spin_lock(tw_dev->host->host_lock);
+
+ /* Read host interrupt status */
+ reg = readl(TWL_HISTAT_REG_ADDR(tw_dev));
+
+ /* Check if this is our interrupt, otherwise bail */
+ if (!(reg & TWL_HISTATUS_VALID_INTERRUPT))
+ goto twl_interrupt_bail;
+
+ handled = 1;
+
+ /* If we are resetting, bail */
+ if (test_bit(TW_IN_RESET, &tw_dev->flags))
+ goto twl_interrupt_bail;
+
+ /* Attention interrupt */
+ if (reg & TWL_HISTATUS_ATTENTION_INTERRUPT) {
+ if (twl_handle_attention_interrupt(tw_dev)) {
+ TWL_MASK_INTERRUPTS(tw_dev);
+ goto twl_interrupt_bail;
+ }
+ }
+
+ /* Response interrupt */
+ while (reg & TWL_HISTATUS_RESPONSE_INTERRUPT) {
+ if (sizeof(dma_addr_t) > 4) {
+ regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev));
+ regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+ mfa = ((u64)regh << 32) | regl;
+ } else
+ mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+
+ error = 0;
+ response = (u32)mfa;
+
+ /* Check for command packet error */
+ if (!TW_NOTMFA_OUT(response)) {
+ for (i=0;i<TW_Q_LENGTH;i++) {
+ if (tw_dev->sense_buffer_phys[i] == mfa) {
+ request_id = le16_to_cpu(tw_dev->sense_buffer_virt[i]->header_desc.request_id);
+ if (tw_dev->srb[request_id] != NULL)
+ error = twl_fill_sense(tw_dev, i, request_id, 1, 1);
+ else {
+ /* Skip ioctl error prints */
+ if (request_id != tw_dev->chrdev_request_id)
+ error = twl_fill_sense(tw_dev, i, request_id, 0, 1);
+ else
+ memcpy(tw_dev->command_packet_virt[request_id], tw_dev->sense_buffer_virt[i], sizeof(TW_Command_Apache_Header));
+ }
+
+ /* Now re-post the sense buffer */
+ writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev));
+ writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev));
+ break;
+ }
+ }
+ } else
+ request_id = TW_RESID_OUT(response);
+
+ full_command_packet = tw_dev->command_packet_virt[request_id];
+
+ /* Check for correct state */
+ if (tw_dev->state[request_id] != TW_S_POSTED) {
+ if (tw_dev->srb[request_id] != NULL) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Received a request id that wasn't posted");
+ TWL_MASK_INTERRUPTS(tw_dev);
+ goto twl_interrupt_bail;
+ }
+ }
+
+ /* Check for internal command completion */
+ if (tw_dev->srb[request_id] == NULL) {
+ if (request_id != tw_dev->chrdev_request_id) {
+ if (twl_aen_complete(tw_dev, request_id))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "Error completing AEN during attention interrupt");
+ } else {
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+ wake_up(&tw_dev->ioctl_wqueue);
+ }
+ } else {
+ cmd = tw_dev->srb[request_id];
+
+ if (!error)
+ cmd->result = (DID_OK << 16);
+
+ /* Report residual bytes for single sgl */
+ if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) {
+ if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id]))
+ scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length);
+ }
+
+ /* Now complete the io */
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ tw_dev->posted_request_count--;
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ twl_unmap_scsi_data(tw_dev, request_id);
+ }
+
+ /* Check for another response interrupt */
+ reg = readl(TWL_HISTAT_REG_ADDR(tw_dev));
+ }
+
+twl_interrupt_bail:
+ spin_unlock(tw_dev->host->host_lock);
+ return IRQ_RETVAL(handled);
+} /* End twl_interrupt() */
+
+/* This function will poll for a register change */
+static int twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value, u32 result, int seconds)
+{
+ unsigned long before;
+ int retval = 1;
+ u32 reg_value;
+
+ reg_value = readl(reg);
+ before = jiffies;
+
+ while ((reg_value & value) != result) {
+ reg_value = readl(reg);
+ if (time_after(jiffies, before + HZ * seconds))
+ goto out;
+ msleep(50);
+ }
+ retval = 0;
+out:
+ return retval;
+} /* End twl_poll_register() */
+
+/* This function will reset a controller */
+static int twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
+{
+ int retval = 1;
+ int i = 0;
+ u32 status = 0;
+ unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0;
+ unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
+ u32 init_connect_result = 0;
+ int tries = 0;
+ int do_soft_reset = soft_reset;
+
+ while (tries < TW_MAX_RESET_TRIES) {
+ /* Do a soft reset if one is needed */
+ if (do_soft_reset) {
+ TWL_SOFT_RESET(tw_dev);
+
+ /* Make sure controller is in a good state */
+ if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, 0x0, 30)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Controller never went non-ready during reset sequence");
+ tries++;
+ continue;
+ }
+ if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, TWL_CONTROLLER_READY, 60)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x11, "Controller not ready during reset sequence");
+ tries++;
+ continue;
+ }
+ }
+
+ /* Initconnect */
+ if (twl_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS,
+ TW_EXTENDED_INIT_CONNECT, TW_CURRENT_DRIVER_SRL,
+ TW_9750_ARCH_ID, TW_CURRENT_DRIVER_BRANCH,
+ TW_CURRENT_DRIVER_BUILD, &fw_on_ctlr_srl,
+ &fw_on_ctlr_arch_id, &fw_on_ctlr_branch,
+ &fw_on_ctlr_build, &init_connect_result)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x12, "Initconnection failed while checking SRL");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* Load sense buffers */
+ while (i < TW_Q_LENGTH) {
+ writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev));
+ writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev));
+
+ /* Check status for over-run after each write */
+ status = readl(TWL_STATUS_REG_ADDR(tw_dev));
+ if (!(status & TWL_STATUS_OVERRUN_SUBMIT))
+ i++;
+ }
+
+ /* Now check status */
+ status = readl(TWL_STATUS_REG_ADDR(tw_dev));
+ if (status) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "Bad controller status after loading sense buffers");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* Drain the AEN queue */
+ if (twl_aen_drain_queue(tw_dev, soft_reset)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x14, "AEN drain failed during reset sequence");
+ do_soft_reset = 1;
+ tries++;
+ continue;
+ }
+
+ /* Load rest of compatibility struct */
+ strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
+ tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL;
+ tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
+ tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD;
+ tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL;
+ tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH;
+ tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD;
+ tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl;
+ tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch;
+ tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build;
+
+ /* If we got here, controller is in a good state */
+ retval = 0;
+ goto out;
+ }
+out:
+ return retval;
+} /* End twl_reset_sequence() */
+
+/* This function will reset a device extension */
+static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset)
+{
+ int i = 0, retval = 1;
+ unsigned long flags = 0;
+
+ /* Block SCSI requests while we are resetting */
+ if (ioctl_reset)
+ scsi_block_requests(tw_dev->host);
+
+ set_bit(TW_IN_RESET, &tw_dev->flags);
+ TWL_MASK_INTERRUPTS(tw_dev);
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+ spin_lock_irqsave(tw_dev->host->host_lock, flags);
+
+ /* Abort all requests that are in progress */
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ if ((tw_dev->state[i] != TW_S_FINISHED) &&
+ (tw_dev->state[i] != TW_S_INITIAL) &&
+ (tw_dev->state[i] != TW_S_COMPLETED)) {
+ if (tw_dev->srb[i]) {
+ tw_dev->srb[i]->result = (DID_RESET << 16);
+ tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+ twl_unmap_scsi_data(tw_dev, i);
+ }
+ }
+ }
+
+ /* Reset queues and counts */
+ for (i = 0; i < TW_Q_LENGTH; i++) {
+ tw_dev->free_queue[i] = i;
+ tw_dev->state[i] = TW_S_INITIAL;
+ }
+ tw_dev->free_head = TW_Q_START;
+ tw_dev->free_tail = TW_Q_START;
+ tw_dev->posted_request_count = 0;
+
+ spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+ if (twl_reset_sequence(tw_dev, 1))
+ goto out;
+
+ TWL_UNMASK_INTERRUPTS(tw_dev);
+
+ clear_bit(TW_IN_RESET, &tw_dev->flags);
+ tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+ retval = 0;
+out:
+ if (ioctl_reset)
+ scsi_unblock_requests(tw_dev->host);
+ return retval;
+} /* End twl_reset_device_extension() */
+
+/* This funciton returns unit geometry in cylinders/heads/sectors */
+static int twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
+{
+ int heads, sectors;
+ TW_Device_Extension *tw_dev;
+
+ tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
+
+ if (capacity >= 0x200000) {
+ heads = 255;
+ sectors = 63;
+ } else {
+ heads = 64;
+ sectors = 32;
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = sector_div(capacity, heads * sectors); /* cylinders */
+
+ return 0;
+} /* End twl_scsi_biosparam() */
+
+/* This is the new scsi eh reset function */
+static int twl_scsi_eh_reset(struct scsi_cmnd *SCpnt)
+{
+ TW_Device_Extension *tw_dev = NULL;
+ int retval = FAILED;
+
+ tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ tw_dev->num_resets++;
+
+ sdev_printk(KERN_WARNING, SCpnt->device,
+ "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n",
+ TW_DRIVER, 0x2c, SCpnt->cmnd[0]);
+
+ /* Make sure we are not issuing an ioctl or resetting from ioctl */
+ mutex_lock(&tw_dev->ioctl_lock);
+
+ /* Now reset the card and some of the device extension data */
+ if (twl_reset_device_extension(tw_dev, 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "Controller reset failed during scsi host reset");
+ goto out;
+ }
+
+ retval = SUCCESS;
+out:
+ mutex_unlock(&tw_dev->ioctl_lock);
+ return retval;
+} /* End twl_scsi_eh_reset() */
+
+/* This is the main scsi queue function to handle scsi opcodes */
+static int twl_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ int request_id, retval;
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+ /* If we are resetting due to timed out ioctl, report as busy */
+ if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
+ retval = SCSI_MLQUEUE_HOST_BUSY;
+ goto out;
+ }
+
+ /* Save done function into scsi_cmnd struct */
+ SCpnt->scsi_done = done;
+
+ /* Get a free request id */
+ twl_get_request_id(tw_dev, &request_id);
+
+ /* Save the scsi command for use by the ISR */
+ tw_dev->srb[request_id] = SCpnt;
+
+ /* Initialize phase to zero */
+ SCpnt->SCp.phase = TW_PHASE_INITIAL;
+
+ retval = twl_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
+ if (retval) {
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ twl_free_request_id(tw_dev, request_id);
+ SCpnt->result = (DID_ERROR << 16);
+ done(SCpnt);
+ retval = 0;
+ }
+out:
+ return retval;
+} /* End twl_scsi_queue() */
+
+/* This function tells the controller to shut down */
+static void __twl_shutdown(TW_Device_Extension *tw_dev)
+{
+ /* Disable interrupts */
+ TWL_MASK_INTERRUPTS(tw_dev);
+
+ /* Free up the IRQ */
+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+ printk(KERN_WARNING "3w-sas: Shutting down host %d.\n", tw_dev->host->host_no);
+
+ /* Tell the card we are shutting down */
+ if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Connection shutdown failed");
+ } else {
+ printk(KERN_WARNING "3w-sas: Shutdown complete.\n");
+ }
+
+ /* Clear doorbell interrupt just before exit */
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+} /* End __twl_shutdown() */
+
+/* Wrapper for __twl_shutdown */
+static void twl_shutdown(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev;
+
+ if (!host)
+ return;
+
+ tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ if (tw_dev->online)
+ __twl_shutdown(tw_dev);
+} /* End twl_shutdown() */
+
+/* This function configures unit settings when a unit is coming on-line */
+static int twl_slave_configure(struct scsi_device *sdev)
+{
+ /* Force 60 second timeout */
+ blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
+
+ return 0;
+} /* End twl_slave_configure() */
+
+/* scsi_host_template initializer */
+static struct scsi_host_template driver_template = {
+ .module = THIS_MODULE,
+ .name = "3w-sas",
+ .queuecommand = twl_scsi_queue,
+ .eh_host_reset_handler = twl_scsi_eh_reset,
+ .bios_param = twl_scsi_biosparam,
+ .change_queue_depth = twl_change_queue_depth,
+ .can_queue = TW_Q_LENGTH-2,
+ .slave_configure = twl_slave_configure,
+ .this_id = -1,
+ .sg_tablesize = TW_LIBERATOR_MAX_SGL_LENGTH,
+ .max_sectors = TW_MAX_SECTORS,
+ .cmd_per_lun = TW_MAX_CMDS_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = twl_host_attrs,
+ .emulated = 1
+};
+
+/* This function will probe and initialize a card */
+static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+{
+ struct Scsi_Host *host = NULL;
+ TW_Device_Extension *tw_dev;
+ int retval = -ENODEV;
+ int *ptr_phycount, phycount=0;
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ TW_PRINTK(host, TW_DRIVER, 0x17, "Failed to enable pci device");
+ goto out_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)))
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+ || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
+ if (!host) {
+ TW_PRINTK(host, TW_DRIVER, 0x19, "Failed to allocate memory for device extension");
+ retval = -ENOMEM;
+ goto out_disable_device;
+ }
+ tw_dev = shost_priv(host);
+
+ /* Save values to device extension */
+ tw_dev->host = host;
+ tw_dev->tw_pci_dev = pdev;
+
+ if (twl_initialize_device_extension(tw_dev)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Failed to initialize device extension");
+ goto out_free_device_extension;
+ }
+
+ /* Request IO regions */
+ retval = pci_request_regions(pdev, "3w-sas");
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Failed to get mem region");
+ goto out_free_device_extension;
+ }
+
+ /* Save base address, use region 1 */
+ tw_dev->base_addr = pci_iomap(pdev, 1, 0);
+ if (!tw_dev->base_addr) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to ioremap");
+ goto out_release_mem_region;
+ }
+
+ /* Disable interrupts on the card */
+ TWL_MASK_INTERRUPTS(tw_dev);
+
+ /* Initialize the card */
+ if (twl_reset_sequence(tw_dev, 0)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Controller reset failed during probe");
+ goto out_iounmap;
+ }
+
+ /* Set host specific parameters */
+ host->max_id = TW_MAX_UNITS;
+ host->max_cmd_len = TW_MAX_CDB_LEN;
+ host->max_lun = TW_MAX_LUNS;
+ host->max_channel = 0;
+
+ /* Register the card with the kernel SCSI layer */
+ retval = scsi_add_host(host, &pdev->dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "scsi add host failed");
+ goto out_iounmap;
+ }
+
+ pci_set_drvdata(pdev, host);
+
+ printk(KERN_WARNING "3w-sas: scsi%d: Found an LSI 3ware %s Controller at 0x%llx, IRQ: %d.\n",
+ host->host_no,
+ (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE,
+ TW_PARAM_MODEL, TW_PARAM_MODEL_LENGTH),
+ (u64)pci_resource_start(pdev, 1), pdev->irq);
+
+ ptr_phycount = twl_get_param(tw_dev, 2, TW_PARAM_PHY_SUMMARY_TABLE,
+ TW_PARAM_PHYCOUNT, TW_PARAM_PHYCOUNT_LENGTH);
+ if (ptr_phycount)
+ phycount = le32_to_cpu(*(int *)ptr_phycount);
+
+ printk(KERN_WARNING "3w-sas: scsi%d: Firmware %s, BIOS %s, Phys: %d.\n",
+ host->host_no,
+ (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE,
+ TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH),
+ (char *)twl_get_param(tw_dev, 2, TW_VERSION_TABLE,
+ TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH),
+ phycount);
+
+ /* Try to enable MSI */
+ if (use_msi && !pci_enable_msi(pdev))
+ set_bit(TW_USING_MSI, &tw_dev->flags);
+
+ /* Now setup the interrupt handler */
+ retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Error requesting IRQ");
+ goto out_remove_host;
+ }
+
+ twl_device_extension_list[twl_device_extension_count] = tw_dev;
+ twl_device_extension_count++;
+
+ /* Re-enable interrupts on the card */
+ TWL_UNMASK_INTERRUPTS(tw_dev);
+
+ /* Finally, scan the host */
+ scsi_scan_host(host);
+
+ /* Add sysfs binary files */
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Failed to create sysfs binary file: 3ware_aen_read");
+ if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr))
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Failed to create sysfs binary file: 3ware_compat_info");
+
+ if (twl_major == -1) {
+ if ((twl_major = register_chrdev (0, "twl", &twl_fops)) < 0)
+ TW_PRINTK(host, TW_DRIVER, 0x22, "Failed to register character device");
+ }
+ tw_dev->online = 1;
+ return 0;
+
+out_remove_host:
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_disable_msi(pdev);
+ scsi_remove_host(host);
+out_iounmap:
+ iounmap(tw_dev->base_addr);
+out_release_mem_region:
+ pci_release_regions(pdev);
+out_free_device_extension:
+ twl_free_device_extension(tw_dev);
+ scsi_host_put(host);
+out_disable_device:
+ pci_disable_device(pdev);
+
+ return retval;
+} /* End twl_probe() */
+
+/* This function is called to remove a device */
+static void twl_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev;
+
+ if (!host)
+ return;
+
+ tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ if (!tw_dev->online)
+ return;
+
+ /* Remove sysfs binary files */
+ sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr);
+ sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr);
+
+ scsi_remove_host(tw_dev->host);
+
+ /* Unregister character device */
+ if (twl_major >= 0) {
+ unregister_chrdev(twl_major, "twl");
+ twl_major = -1;
+ }
+
+ /* Shutdown the card */
+ __twl_shutdown(tw_dev);
+
+ /* Disable MSI if enabled */
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_disable_msi(pdev);
+
+ /* Free IO remapping */
+ iounmap(tw_dev->base_addr);
+
+ /* Free up the mem region */
+ pci_release_regions(pdev);
+
+ /* Free up device extension resources */
+ twl_free_device_extension(tw_dev);
+
+ scsi_host_put(tw_dev->host);
+ pci_disable_device(pdev);
+ twl_device_extension_count--;
+} /* End twl_remove() */
+
+#ifdef CONFIG_PM
+/* This function is called on PCI suspend */
+static int twl_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ printk(KERN_WARNING "3w-sas: Suspending host %d.\n", tw_dev->host->host_no);
+ /* Disable interrupts */
+ TWL_MASK_INTERRUPTS(tw_dev);
+
+ free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+ /* Tell the card we are shutting down */
+ if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x23, "Connection shutdown failed during suspend");
+ } else {
+ printk(KERN_WARNING "3w-sas: Suspend complete.\n");
+ }
+
+ /* Clear doorbell interrupt */
+ TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+} /* End twl_suspend() */
+
+/* This function is called on PCI resume */
+static int twl_resume(struct pci_dev *pdev)
+{
+ int retval = 0;
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+ printk(KERN_WARNING "3w-sas: Resuming host %d.\n", tw_dev->host->host_no);
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x24, "Enable device failed during resume");
+ return retval;
+ }
+
+ 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)))
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+ || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ TW_PRINTK(host, TW_DRIVER, 0x25, "Failed to set dma mask during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Initialize the card */
+ if (twl_reset_sequence(tw_dev, 0)) {
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Now setup the interrupt handler */
+ retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev);
+ if (retval) {
+ TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Error requesting IRQ during resume");
+ retval = -ENODEV;
+ goto out_disable_device;
+ }
+
+ /* Now enable MSI if enabled */
+ if (test_bit(TW_USING_MSI, &tw_dev->flags))
+ pci_enable_msi(pdev);
+
+ /* Re-enable interrupts on the card */
+ TWL_UNMASK_INTERRUPTS(tw_dev);
+
+ printk(KERN_WARNING "3w-sas: Resume complete.\n");
+ return 0;
+
+out_disable_device:
+ scsi_remove_host(host);
+ pci_disable_device(pdev);
+
+ return retval;
+} /* End twl_resume() */
+#endif
+
+/* PCI Devices supported by this driver */
+static struct pci_device_id twl_pci_tbl[] __devinitdata = {
+ { PCI_VDEVICE(3WARE, PCI_DEVICE_ID_3WARE_9750) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, twl_pci_tbl);
+
+/* pci_driver initializer */
+static struct pci_driver twl_driver = {
+ .name = "3w-sas",
+ .id_table = twl_pci_tbl,
+ .probe = twl_probe,
+ .remove = twl_remove,
+#ifdef CONFIG_PM
+ .suspend = twl_suspend,
+ .resume = twl_resume,
+#endif
+ .shutdown = twl_shutdown
+};
+
+/* This function is called on driver initialization */
+static int __init twl_init(void)
+{
+ printk(KERN_INFO "LSI 3ware SAS/SATA-RAID Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
+
+ return pci_register_driver(&twl_driver);
+} /* End twl_init() */
+
+/* This function is called on driver exit */
+static void __exit twl_exit(void)
+{
+ pci_unregister_driver(&twl_driver);
+} /* End twl_exit() */
+
+module_init(twl_init);
+module_exit(twl_exit);
+
diff --git a/drivers/scsi/3w-sas.h b/drivers/scsi/3w-sas.h
new file mode 100644
index 00000000000..d474892701d
--- /dev/null
+++ b/drivers/scsi/3w-sas.h
@@ -0,0 +1,396 @@
+/*
+ 3w-sas.h -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@lsi.com>
+
+ Copyright (C) 2009 LSI Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@lsi.com
+
+ For more information, goto:
+ http://www.lsi.com
+*/
+
+#ifndef _3W_SAS_H
+#define _3W_SAS_H
+
+/* AEN severity table */
+static char *twl_aen_severity_table[] =
+{
+ "None", "ERROR", "WARNING", "INFO", "DEBUG", NULL
+};
+
+/* Liberator register offsets */
+#define TWL_STATUS 0x0 /* Status */
+#define TWL_HIBDB 0x20 /* Inbound doorbell */
+#define TWL_HISTAT 0x30 /* Host interrupt status */
+#define TWL_HIMASK 0x34 /* Host interrupt mask */
+#define TWL_HOBDB 0x9C /* Outbound doorbell */
+#define TWL_HOBDBC 0xA0 /* Outbound doorbell clear */
+#define TWL_SCRPD3 0xBC /* Scratchpad */
+#define TWL_HIBQPL 0xC0 /* Host inbound Q low */
+#define TWL_HIBQPH 0xC4 /* Host inbound Q high */
+#define TWL_HOBQPL 0xC8 /* Host outbound Q low */
+#define TWL_HOBQPH 0xCC /* Host outbound Q high */
+#define TWL_HISTATUS_VALID_INTERRUPT 0xC
+#define TWL_HISTATUS_ATTENTION_INTERRUPT 0x4
+#define TWL_HISTATUS_RESPONSE_INTERRUPT 0x8
+#define TWL_STATUS_OVERRUN_SUBMIT 0x2000
+#define TWL_ISSUE_SOFT_RESET 0x100
+#define TWL_CONTROLLER_READY 0x2000
+#define TWL_DOORBELL_CONTROLLER_ERROR 0x200000
+#define TWL_DOORBELL_ATTENTION_INTERRUPT 0x40000
+#define TWL_PULL_MODE 0x1
+
+/* Command packet opcodes used by the driver */
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_GET_PARAM 0x12
+#define TW_OP_SET_PARAM 0x13
+#define TW_OP_EXECUTE_SCSI 0x10
+
+/* Asynchronous Event Notification (AEN) codes used by the driver */
+#define TW_AEN_QUEUE_EMPTY 0x0000
+#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_SYNC_TIME_WITH_HOST 0x031
+#define TW_AEN_SEVERITY_ERROR 0x1
+#define TW_AEN_SEVERITY_DEBUG 0x4
+#define TW_AEN_NOT_RETRIEVED 0x1
+
+/* Command state defines */
+#define TW_S_INITIAL 0x1 /* Initial state */
+#define TW_S_STARTED 0x2 /* Id in use */
+#define TW_S_POSTED 0x4 /* Posted to the controller */
+#define TW_S_COMPLETED 0x8 /* Completed by isr */
+#define TW_S_FINISHED 0x10 /* I/O completely done */
+
+/* Compatibility defines */
+#define TW_9750_ARCH_ID 10
+#define TW_CURRENT_DRIVER_SRL 40
+#define TW_CURRENT_DRIVER_BUILD 0
+#define TW_CURRENT_DRIVER_BRANCH 0
+
+/* Phase defines */
+#define TW_PHASE_INITIAL 0
+#define TW_PHASE_SGLIST 2
+
+/* Misc defines */
+#define TW_SECTOR_SIZE 512
+#define TW_MAX_UNITS 32
+#define TW_INIT_MESSAGE_CREDITS 0x100
+#define TW_INIT_COMMAND_PACKET_SIZE 0x3
+#define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED 0x6
+#define TW_EXTENDED_INIT_CONNECT 0x2
+#define TW_BASE_FW_SRL 24
+#define TW_BASE_FW_BRANCH 0
+#define TW_BASE_FW_BUILD 1
+#define TW_Q_LENGTH 256
+#define TW_Q_START 0
+#define TW_MAX_SLOT 32
+#define TW_MAX_RESET_TRIES 2
+#define TW_MAX_CMDS_PER_LUN 254
+#define TW_MAX_AEN_DRAIN 255
+#define TW_IN_RESET 2
+#define TW_USING_MSI 3
+#define TW_IN_ATTENTION_LOOP 4
+#define TW_MAX_SECTORS 256
+#define TW_MAX_CDB_LEN 16
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
+#define TW_COMMAND_OFFSET 128 /* 128 bytes */
+#define TW_VERSION_TABLE 0x0402
+#define TW_TIMEKEEP_TABLE 0x040A
+#define TW_INFORMATION_TABLE 0x0403
+#define TW_PARAM_FWVER 3
+#define TW_PARAM_FWVER_LENGTH 16
+#define TW_PARAM_BIOSVER 4
+#define TW_PARAM_BIOSVER_LENGTH 16
+#define TW_PARAM_MODEL 8
+#define TW_PARAM_MODEL_LENGTH 16
+#define TW_PARAM_PHY_SUMMARY_TABLE 1
+#define TW_PARAM_PHYCOUNT 2
+#define TW_PARAM_PHYCOUNT_LENGTH 1
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108 // Used by smartmontools
+#define TW_ALLOCATION_LENGTH 128
+#define TW_SENSE_DATA_LENGTH 18
+#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a
+#define TW_ERROR_INVALID_FIELD_IN_CDB 0x10d
+#define TW_ERROR_UNIT_OFFLINE 0x128
+#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3
+#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4
+#define TW_DRIVER 6
+#ifndef PCI_DEVICE_ID_3WARE_9750
+#define PCI_DEVICE_ID_3WARE_9750 0x1010
+#endif
+
+/* Bitmask macros to eliminate bitfields */
+
+/* opcode: 5, reserved: 3 */
+#define TW_OPRES_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_OP_OUT(x) (x & 0x1f)
+
+/* opcode: 5, sgloffset: 3 */
+#define TW_OPSGL_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_SGL_OUT(x) ((x >> 5) & 0x7)
+
+/* severity: 3, reserved: 5 */
+#define TW_SEV_OUT(x) (x & 0x7)
+
+/* not_mfa: 1, reserved: 7, status: 8, request_id: 16 */
+#define TW_RESID_OUT(x) ((x >> 16) & 0xffff)
+#define TW_NOTMFA_OUT(x) (x & 0x1)
+
+/* request_id: 12, lun: 4 */
+#define TW_REQ_LUN_IN(lun, request_id) (((lun << 12) & 0xf000) | (request_id & 0xfff))
+#define TW_LUN_OUT(lun) ((lun >> 12) & 0xf)
+
+/* Register access macros */
+#define TWL_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_STATUS)
+#define TWL_HOBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPL)
+#define TWL_HOBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPH)
+#define TWL_HOBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDB)
+#define TWL_HOBDBC_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDBC)
+#define TWL_HIMASK_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIMASK)
+#define TWL_HISTAT_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HISTAT)
+#define TWL_HIBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPH)
+#define TWL_HIBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPL)
+#define TWL_HIBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBDB)
+#define TWL_SCRPD3_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_SCRPD3)
+#define TWL_MASK_INTERRUPTS(x) (writel(~0, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_UNMASK_INTERRUPTS(x) (writel(~TWL_HISTATUS_VALID_INTERRUPT, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_CLEAR_DB_INTERRUPT(x) (writel(~0, TWL_HOBDBC_REG_ADDR(tw_dev)))
+#define TWL_SOFT_RESET(x) (writel(TWL_ISSUE_SOFT_RESET, TWL_HIBDB_REG_ADDR(tw_dev)))
+
+/* Macros */
+#define TW_PRINTK(h,a,b,c) { \
+if (h) \
+printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s.\n",h->host_no,a,b,c); \
+else \
+printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \
+}
+#define TW_MAX_LUNS 16
+#define TW_COMMAND_SIZE (sizeof(dma_addr_t) > 4 ? 6 : 4)
+#define TW_LIBERATOR_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 46 : 92)
+#define TW_LIBERATOR_MAX_SGL_LENGTH_OLD (sizeof(dma_addr_t) > 4 ? 47 : 94)
+#define TW_PADDING_LENGTH_LIBERATOR 136
+#define TW_PADDING_LENGTH_LIBERATOR_OLD 132
+#define TW_CPU_TO_SGL(x) (sizeof(dma_addr_t) > 4 ? cpu_to_le64(x) : cpu_to_le32(x))
+
+#pragma pack(1)
+
+/* SGL entry */
+typedef struct TAG_TW_SG_Entry_ISO {
+ dma_addr_t address;
+ dma_addr_t length;
+} TW_SG_Entry_ISO;
+
+/* Old Command Packet with ISO SGL */
+typedef struct TW_Command {
+ unsigned char opcode__sgloffset;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char unit__hostid;
+ /* Second DWORD */
+ unsigned char status;
+ unsigned char flags;
+ union {
+ unsigned short block_count;
+ unsigned short parameter_count;
+ } byte6_offset;
+ union {
+ struct {
+ u32 lba;
+ TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD];
+ unsigned char padding[TW_PADDING_LENGTH_LIBERATOR_OLD];
+ } io;
+ struct {
+ TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD];
+ u32 padding;
+ unsigned char padding2[TW_PADDING_LENGTH_LIBERATOR_OLD];
+ } param;
+ } byte8_offset;
+} TW_Command;
+
+/* New Command Packet with ISO SGL */
+typedef struct TAG_TW_Command_Apache {
+ unsigned char opcode__reserved;
+ unsigned char unit;
+ unsigned short request_id__lunl;
+ unsigned char status;
+ unsigned char sgl_offset;
+ unsigned short sgl_entries__lunh;
+ unsigned char cdb[16];
+ TW_SG_Entry_ISO sg_list[TW_LIBERATOR_MAX_SGL_LENGTH];
+ unsigned char padding[TW_PADDING_LENGTH_LIBERATOR];
+} TW_Command_Apache;
+
+/* New command packet header */
+typedef struct TAG_TW_Command_Apache_Header {
+ unsigned char sense_data[TW_SENSE_DATA_LENGTH];
+ struct {
+ char reserved[4];
+ unsigned short error;
+ unsigned char padding;
+ unsigned char severity__reserved;
+ } status_block;
+ unsigned char err_specific_desc[98];
+ struct {
+ unsigned char size_header;
+ unsigned short request_id;
+ unsigned char size_sense;
+ } header_desc;
+} TW_Command_Apache_Header;
+
+/* This struct is a union of the 2 command packets */
+typedef struct TAG_TW_Command_Full {
+ TW_Command_Apache_Header header;
+ union {
+ TW_Command oldcommand;
+ TW_Command_Apache newcommand;
+ } command;
+} TW_Command_Full;
+
+/* Initconnection structure */
+typedef struct TAG_TW_Initconnect {
+ unsigned char opcode__reserved;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char res2;
+ unsigned char status;
+ unsigned char flags;
+ unsigned short message_credits;
+ u32 features;
+ unsigned short fw_srl;
+ unsigned short fw_arch_id;
+ unsigned short fw_branch;
+ unsigned short fw_build;
+ u32 result;
+} TW_Initconnect;
+
+/* Event info structure */
+typedef struct TAG_TW_Event
+{
+ unsigned int sequence_id;
+ unsigned int time_stamp_sec;
+ unsigned short aen_code;
+ unsigned char severity;
+ unsigned char retrieved;
+ unsigned char repeat_count;
+ unsigned char parameter_len;
+ unsigned char parameter_data[98];
+} TW_Event;
+
+typedef struct TAG_TW_Ioctl_Driver_Command {
+ unsigned int control_code;
+ unsigned int status;
+ unsigned int unique_id;
+ unsigned int sequence_id;
+ unsigned int os_specific;
+ unsigned int buffer_length;
+} TW_Ioctl_Driver_Command;
+
+typedef struct TAG_TW_Ioctl_Apache {
+ TW_Ioctl_Driver_Command driver_command;
+ char padding[488];
+ TW_Command_Full firmware_command;
+ char data_buffer[1];
+} TW_Ioctl_Buf_Apache;
+
+/* GetParam descriptor */
+typedef struct {
+ unsigned short table_id;
+ unsigned short parameter_id;
+ unsigned short parameter_size_bytes;
+ unsigned short actual_parameter_size_bytes;
+ unsigned char data[1];
+} TW_Param_Apache;
+
+/* Compatibility information structure */
+typedef struct TAG_TW_Compatibility_Info
+{
+ char driver_version[32];
+ unsigned short working_srl;
+ unsigned short working_branch;
+ unsigned short working_build;
+ unsigned short driver_srl_high;
+ unsigned short driver_branch_high;
+ unsigned short driver_build_high;
+ unsigned short driver_srl_low;
+ unsigned short driver_branch_low;
+ unsigned short driver_build_low;
+ unsigned short fw_on_ctlr_srl;
+ unsigned short fw_on_ctlr_branch;
+ unsigned short fw_on_ctlr_build;
+} TW_Compatibility_Info;
+
+#pragma pack()
+
+typedef struct TAG_TW_Device_Extension {
+ void __iomem *base_addr;
+ unsigned long *generic_buffer_virt[TW_Q_LENGTH];
+ dma_addr_t generic_buffer_phys[TW_Q_LENGTH];
+ TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
+ dma_addr_t command_packet_phys[TW_Q_LENGTH];
+ TW_Command_Apache_Header *sense_buffer_virt[TW_Q_LENGTH];
+ dma_addr_t sense_buffer_phys[TW_Q_LENGTH];
+ struct pci_dev *tw_pci_dev;
+ struct scsi_cmnd *srb[TW_Q_LENGTH];
+ unsigned char free_queue[TW_Q_LENGTH];
+ unsigned char free_head;
+ unsigned char free_tail;
+ int state[TW_Q_LENGTH];
+ unsigned int posted_request_count;
+ unsigned int max_posted_request_count;
+ unsigned int max_sgl_entries;
+ unsigned int sgl_entries;
+ unsigned int num_resets;
+ unsigned int sector_count;
+ unsigned int max_sector_count;
+ unsigned int aen_count;
+ struct Scsi_Host *host;
+ long flags;
+ TW_Event *event_queue[TW_Q_LENGTH];
+ unsigned char error_index;
+ unsigned int error_sequence_id;
+ int chrdev_request_id;
+ wait_queue_head_t ioctl_wqueue;
+ struct mutex ioctl_lock;
+ TW_Compatibility_Info tw_compat_info;
+ char online;
+} TW_Device_Extension;
+
+#endif /* _3W_SAS_H */
+
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c
index faa0fcfed71..f65a1e92340 100644
--- a/drivers/scsi/3w-xxxx.c
+++ b/drivers/scsi/3w-xxxx.c
@@ -8,7 +8,7 @@
Copyright (C) 1999-2009 3ware Inc.
- Kernel compatiblity By: Andre Hedrick <andre@suse.com>
+ Kernel compatibility By: Andre Hedrick <andre@suse.com>
Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com>
Further tiny build fixes and trivial hoovering Alan Cox
@@ -521,8 +521,12 @@ static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr,
} /* End tw_show_stats() */
/* This function will set a devices queue depth */
-static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > TW_Q_LENGTH-2)
queue_depth = TW_Q_LENGTH-2;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index f5a9addb705..9f4a911a6d8 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -175,7 +175,7 @@ STATIC void NCR_700_chip_reset(struct Scsi_Host *host);
STATIC int NCR_700_slave_alloc(struct scsi_device *SDpnt);
STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt);
STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt);
-static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth);
+static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth, int reason);
static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth);
STATIC struct device_attribute *NCR_700_dev_attrs[];
@@ -1491,7 +1491,7 @@ NCR_700_intr(int irq, void *dev_id)
unsigned long flags;
int handled = 0;
- /* Use the host lock to serialise acess to the 53c700
+ /* Use the host lock to serialise access to the 53c700
* hardware. Note: In future, we may need to take the queue
* lock to enter the done routines. When that happens, we
* need to ensure that for this driver, the host lock and the
@@ -2082,8 +2082,11 @@ NCR_700_slave_destroy(struct scsi_device *SDp)
}
static int
-NCR_700_change_queue_depth(struct scsi_device *SDp, int depth)
+NCR_700_change_queue_depth(struct scsi_device *SDp, int depth, int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (depth > NCR_700_MAX_TAGS)
depth = NCR_700_MAX_TAGS;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index e11cca4c784..36900c71a59 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -399,6 +399,17 @@ config SCSI_3W_9XXX
Please read the comments at the top of
<file:drivers/scsi/3w-9xxx.c>.
+config SCSI_3W_SAS
+ tristate "3ware 97xx SAS/SATA-RAID support"
+ depends on PCI && SCSI
+ help
+ This driver supports the LSI 3ware 9750 6Gb/s SAS/SATA-RAID cards.
+
+ <http://www.lsi.com>
+
+ Please read the comments at the top of
+ <file:drivers/scsi/3w-sas.c>.
+
config SCSI_7000FASST
tristate "7000FASST SCSI support"
depends on ISA && SCSI && ISA_DMA_API
@@ -621,6 +632,14 @@ config SCSI_FLASHPOINT
substantial, so users of MultiMaster Host Adapters may not
wish to include it.
+config VMWARE_PVSCSI
+ tristate "VMware PVSCSI driver support"
+ depends on PCI && SCSI && X86
+ help
+ This driver supports VMware's para virtualized SCSI HBA.
+ To compile this driver as a module, choose M here: the
+ module will be called vmw_pvscsi.
+
config LIBFC
tristate "LibFC module"
select SCSI_FC_ATTRS
@@ -644,7 +663,7 @@ config FCOE
config FCOE_FNIC
tristate "Cisco FNIC Driver"
depends on PCI && X86
- select LIBFC
+ select LIBFCOE
help
This is support for the Cisco PCI-Express FCoE HBA.
@@ -1818,6 +1837,14 @@ config SCSI_PMCRAID
---help---
This driver supports the PMC SIERRA MaxRAID adapters.
+config SCSI_PM8001
+ tristate "PMC-Sierra SPC 8001 SAS/SATA Based Host Adapter driver"
+ depends on PCI && SCSI
+ select SCSI_SAS_LIBSAS
+ help
+ This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip
+ based host adapters.
+
config SCSI_SRP
tristate "SCSI RDMA Protocol helper library"
depends on SCSI && PCI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 3ad61db5e3f..280d3c657d6 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_SCSI_AIC79XX) += aic7xxx/
obj-$(CONFIG_SCSI_AACRAID) += aacraid/
obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/
+obj-$(CONFIG_SCSI_PM8001) += pm8001/
obj-$(CONFIG_SCSI_IPS) += ips.o
obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o
obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
@@ -113,6 +114,7 @@ obj-$(CONFIG_SCSI_MESH) += mesh.o
obj-$(CONFIG_SCSI_MAC53C94) += mac53c94.o
obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o
obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o
+obj-$(CONFIG_SCSI_3W_SAS) += 3w-sas.o
obj-$(CONFIG_SCSI_PPA) += ppa.o
obj-$(CONFIG_SCSI_IMM) += imm.o
obj-$(CONFIG_JAZZ_ESP) += esp_scsi.o jazz_esp.o
@@ -133,6 +135,7 @@ obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/
obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
+obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
obj-$(CONFIG_ARM) += arm/
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index cdbdec9f4fb..83986ed8655 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -526,10 +526,10 @@ struct aac_driver_ident
/*
* The adapter interface specs all queues to be located in the same
- * physically contigous block. The host structure that defines the
+ * physically contiguous block. The host structure that defines the
* commuication queues will assume they are each a separate physically
- * contigous memory region that will support them all being one big
- * contigous block.
+ * contiguous memory region that will support them all being one big
+ * contiguous block.
* There is a command and response queue for each level and direction of
* commuication. These regions are accessed by both the host and adapter.
*/
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index d598eba630d..666d5151d62 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -226,7 +226,7 @@ static int aac_comm_init(struct aac_dev * dev)
spin_lock_init(&dev->fib_lock);
/*
- * Allocate the physically contigous space for the commuication
+ * Allocate the physically contiguous space for the commuication
* queue headers.
*/
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 9b97c3e016f..e9373a2d14f 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -472,8 +472,12 @@ static int aac_slave_configure(struct scsi_device *sdev)
* total capacity and the queue depth supported by the target device.
*/
-static int aac_change_queue_depth(struct scsi_device *sdev, int depth)
+static int aac_change_queue_depth(struct scsi_device *sdev, int depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (sdev->tagged_supported && (sdev->type == TYPE_DISK) &&
(sdev_channel(sdev) == CONTAINER_CHANNEL)) {
struct scsi_device * dev;
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index b756041f0b2..22626abdb63 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -7969,7 +7969,7 @@ static int advansys_reset(struct scsi_cmnd *scp)
ASC_DBG(1, "before AscInitAsc1000Driver()\n");
status = AscInitAsc1000Driver(asc_dvc);
- /* Refer to ASC_IERR_* defintions for meaning of 'err_code'. */
+ /* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */
if (asc_dvc->err_code) {
scmd_printk(KERN_INFO, scp, "SCSI bus reset error: "
"0x%x\n", asc_dvc->err_code);
diff --git a/drivers/scsi/aic7xxx/aic79xx.seq b/drivers/scsi/aic7xxx/aic79xx.seq
index 58bc17591b5..2fb78e35a9e 100644
--- a/drivers/scsi/aic7xxx/aic79xx.seq
+++ b/drivers/scsi/aic7xxx/aic79xx.seq
@@ -217,7 +217,7 @@ BEGIN_CRITICAL;
scbdma_tohost_done:
test CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
/*
- * An SCB has been succesfully uploaded to the host.
+ * An SCB has been successfully uploaded to the host.
* If the SCB was uploaded for some reason other than
* bad SCSI status (currently only for underruns), we
* queue the SCB for normal completion. Otherwise, we
@@ -1281,7 +1281,7 @@ END_CRITICAL;
* Is it a disconnect message? Set a flag in the SCB to remind us
* and await the bus going free. If this is an untagged transaction
* store the SCB id for it in our untagged target table for lookup on
- * a reselction.
+ * a reselection.
*/
mesgin_disconnect:
/*
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index 63b521d615f..4d419c155ce 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -2487,7 +2487,7 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
/*
* Although the driver does not care about the
* 'Selection in Progress' status bit, the busy
- * LED does. SELINGO is only cleared by a sucessfull
+ * LED does. SELINGO is only cleared by a successfull
* selection, so we must manually clear it to insure
* the LED turns off just incase no future successful
* selections occur (e.g. no devices on the bus).
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
index 75b23317bd2..1222a7ac698 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm.c
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -2335,7 +2335,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
/*
* The sequencer will never re-reference the
* in-core SCB. To make sure we are notified
- * during reslection, set the MK_MESSAGE flag in
+ * during reselection, set the MK_MESSAGE flag in
* the card's copy of the SCB.
*/
ahd_outb(ahd, SCB_CONTROL,
diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq
index 15196390e28..5a4cfc954a9 100644
--- a/drivers/scsi/aic7xxx/aic7xxx.seq
+++ b/drivers/scsi/aic7xxx/aic7xxx.seq
@@ -1693,7 +1693,7 @@ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
* Is it a disconnect message? Set a flag in the SCB to remind us
* and await the bus going free. If this is an untagged transaction
* store the SCB id for it in our untagged target table for lookup on
- * a reselction.
+ * a reselection.
*/
mesgin_disconnect:
/*
diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c
index 8dfb59d5899..45aa728a76b 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_core.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_core.c
@@ -1733,7 +1733,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
/*
* Although the driver does not care about the
* 'Selection in Progress' status bit, the busy
- * LED does. SELINGO is only cleared by a sucessfull
+ * LED does. SELINGO is only cleared by a successfull
* selection, so we must manually clear it to insure
* the LED turns off just incase no future successful
* selections occur (e.g. no devices on the bus).
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index fd2b9785ff4..8cb05dc8e6a 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -2290,7 +2290,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
* In the non-paging case, the sequencer will
* never re-reference the in-core SCB.
* To make sure we are notified during
- * reslection, set the MK_MESSAGE flag in
+ * reselection, set the MK_MESSAGE flag in
* the card's copy of the SCB.
*/
if ((ahc->flags & AHC_PAGESCBS) == 0) {
diff --git a/drivers/scsi/aic94xx/aic94xx_reg_def.h b/drivers/scsi/aic94xx/aic94xx_reg_def.h
index a43e8cdf4ee..28aaf349c11 100644
--- a/drivers/scsi/aic94xx/aic94xx_reg_def.h
+++ b/drivers/scsi/aic94xx/aic94xx_reg_def.h
@@ -1,5 +1,5 @@
/*
- * Aic94xx SAS/SATA driver hardware registers defintions.
+ * Aic94xx SAS/SATA driver hardware registers definitions.
*
* Copyright (C) 2004 Adaptec, Inc. All rights reserved.
* Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 80aac01b5a6..47d5d19f8c9 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -98,8 +98,11 @@ static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb);
static const char *arcmsr_info(struct Scsi_Host *);
static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth)
+ int queue_depth, int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
queue_depth = ARCMSR_MAX_CMD_PERLUN;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
diff --git a/drivers/scsi/be2iscsi/be.h b/drivers/scsi/be2iscsi/be.h
index b36020dcf01..a93a5040f08 100644
--- a/drivers/scsi/be2iscsi/be.h
+++ b/drivers/scsi/be2iscsi/be.h
@@ -20,8 +20,10 @@
#include <linux/pci.h>
#include <linux/if_vlan.h>
-
-#define FW_VER_LEN 32
+#include <linux/blk-iopoll.h>
+#define FW_VER_LEN 32
+#define MCC_Q_LEN 128
+#define MCC_CQ_LEN 256
struct be_dma_mem {
void *va;
@@ -74,18 +76,14 @@ static inline void queue_tail_inc(struct be_queue_info *q)
struct be_eq_obj {
struct be_queue_info q;
- char desc[32];
-
- /* Adaptive interrupt coalescing (AIC) info */
- bool enable_aic;
- u16 min_eqd; /* in usecs */
- u16 max_eqd; /* in usecs */
- u16 cur_eqd; /* in usecs */
+ struct beiscsi_hba *phba;
+ struct be_queue_info *cq;
+ struct blk_iopoll iopoll;
};
struct be_mcc_obj {
- struct be_queue_info *q;
- struct be_queue_info *cq;
+ struct be_queue_info q;
+ struct be_queue_info cq;
};
struct be_ctrl_info {
@@ -176,8 +174,4 @@ static inline void swap_dws(void *wrb, int len)
} while (len);
#endif /* __BIG_ENDIAN */
}
-
-extern void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
- u16 num_popped);
-
#endif /* BEISCSI_H */
diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c
index 08007b6e42d..698a527d6cc 100644
--- a/drivers/scsi/be2iscsi/be_cmds.c
+++ b/drivers/scsi/be2iscsi/be_cmds.c
@@ -19,6 +19,16 @@
#include "be_mgmt.h"
#include "be_main.h"
+static void be_mcc_notify(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ u32 val = 0;
+
+ val |= mccq->id & DB_MCCQ_RING_ID_MASK;
+ val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT;
+ iowrite32(val, phba->db_va + DB_MCCQ_OFFSET);
+}
+
static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl)
{
if (compl->flags != 0) {
@@ -54,13 +64,56 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
return 0;
}
+
static inline bool is_link_state_evt(u32 trailer)
{
return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
- ASYNC_TRAILER_EVENT_CODE_MASK) == ASYNC_EVENT_CODE_LINK_STATE);
+ ASYNC_TRAILER_EVENT_CODE_MASK) ==
+ ASYNC_EVENT_CODE_LINK_STATE);
+}
+
+static struct be_mcc_compl *be_mcc_compl_get(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *mcc_cq = &phba->ctrl.mcc_obj.cq;
+ struct be_mcc_compl *compl = queue_tail_node(mcc_cq);
+
+ if (be_mcc_compl_is_new(compl)) {
+ queue_tail_inc(mcc_cq);
+ return compl;
+ }
+ return NULL;
+}
+
+static void be2iscsi_fail_session(struct iscsi_cls_session *cls_session)
+{
+ iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED);
+}
+
+static void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
+ struct be_async_event_link_state *evt)
+{
+ switch (evt->port_link_status) {
+ case ASYNC_EVENT_LINK_DOWN:
+ SE_DEBUG(DBG_LVL_1, "Link Down on Physical Port %d \n",
+ evt->physical_port);
+ phba->state |= BE_ADAPTER_LINK_DOWN;
+ break;
+ case ASYNC_EVENT_LINK_UP:
+ phba->state = BE_ADAPTER_UP;
+ SE_DEBUG(DBG_LVL_1, "Link UP on Physical Port %d \n",
+ evt->physical_port);
+ iscsi_host_for_each_session(phba->shost,
+ be2iscsi_fail_session);
+ break;
+ default:
+ SE_DEBUG(DBG_LVL_1, "Unexpected Async Notification %d on"
+ "Physical Port %d \n",
+ evt->port_link_status,
+ evt->physical_port);
+ }
}
-void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
+static void beiscsi_cq_notify(struct beiscsi_hba *phba, u16 qid, bool arm,
u16 num_popped)
{
u32 val = 0;
@@ -68,7 +121,66 @@ void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
if (arm)
val |= 1 << DB_CQ_REARM_SHIFT;
val |= num_popped << DB_CQ_NUM_POPPED_SHIFT;
- iowrite32(val, ctrl->db + DB_CQ_OFFSET);
+ iowrite32(val, phba->db_va + DB_CQ_OFFSET);
+}
+
+
+int beiscsi_process_mcc(struct beiscsi_hba *phba)
+{
+ struct be_mcc_compl *compl;
+ int num = 0, status = 0;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ spin_lock_bh(&phba->ctrl.mcc_cq_lock);
+ while ((compl = be_mcc_compl_get(phba))) {
+ if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
+ /* Interpret flags as an async trailer */
+ BUG_ON(!is_link_state_evt(compl->flags));
+
+ /* Interpret compl as a async link evt */
+ beiscsi_async_link_state_process(phba,
+ (struct be_async_event_link_state *) compl);
+ } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) {
+ status = be_mcc_compl_process(ctrl, compl);
+ atomic_dec(&phba->ctrl.mcc_obj.q.used);
+ }
+ be_mcc_compl_use(compl);
+ num++;
+ }
+
+ if (num)
+ beiscsi_cq_notify(phba, phba->ctrl.mcc_obj.cq.id, true, num);
+
+ spin_unlock_bh(&phba->ctrl.mcc_cq_lock);
+ return status;
+}
+
+/* Wait till no more pending mcc requests are present */
+static int be_mcc_wait_compl(struct beiscsi_hba *phba)
+{
+#define mcc_timeout 120000 /* 5s timeout */
+ int i, status;
+ for (i = 0; i < mcc_timeout; i++) {
+ status = beiscsi_process_mcc(phba);
+ if (status)
+ return status;
+
+ if (atomic_read(&phba->ctrl.mcc_obj.q.used) == 0)
+ break;
+ udelay(100);
+ }
+ if (i == mcc_timeout) {
+ dev_err(&phba->pcidev->dev, "mccq poll timed out\n");
+ return -1;
+ }
+ return 0;
+}
+
+/* Notify MCC requests and wait for completion */
+int be_mcc_notify_wait(struct beiscsi_hba *phba)
+{
+ be_mcc_notify(phba);
+ return be_mcc_wait_compl(phba);
}
static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl)
@@ -142,6 +254,52 @@ int be_mbox_notify(struct be_ctrl_info *ctrl)
return 0;
}
+/*
+ * Insert the mailbox address into the doorbell in two steps
+ * Polls on the mbox doorbell till a command completion (or a timeout) occurs
+ */
+static int be_mbox_notify_wait(struct beiscsi_hba *phba)
+{
+ int status;
+ u32 val = 0;
+ void __iomem *db = phba->ctrl.db + MPU_MAILBOX_DB_OFFSET;
+ struct be_dma_mem *mbox_mem = &phba->ctrl.mbox_mem;
+ struct be_mcc_mailbox *mbox = mbox_mem->va;
+ struct be_mcc_compl *compl = &mbox->compl;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ val |= MPU_MAILBOX_DB_HI_MASK;
+ /* at bits 2 - 31 place mbox dma addr msb bits 34 - 63 */
+ val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2;
+ iowrite32(val, db);
+
+ /* wait for ready to be set */
+ status = be_mbox_db_ready_wait(ctrl);
+ if (status != 0)
+ return status;
+
+ val = 0;
+ /* at bits 2 - 31 place mbox dma addr lsb bits 4 - 33 */
+ val |= (u32)(mbox_mem->dma >> 4) << 2;
+ iowrite32(val, db);
+
+ status = be_mbox_db_ready_wait(ctrl);
+ if (status != 0)
+ return status;
+
+ /* A cq entry has been made now */
+ if (be_mcc_compl_is_new(compl)) {
+ status = be_mcc_compl_process(ctrl, &mbox->compl);
+ be_mcc_compl_use(compl);
+ if (status)
+ return status;
+ } else {
+ dev_err(&phba->pcidev->dev, "invalid mailbox completion\n");
+ return -1;
+ }
+ return 0;
+}
+
void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
bool embedded, u8 sge_cnt)
{
@@ -203,6 +361,20 @@ struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem)
return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb;
}
+struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+ struct be_mcc_wrb *wrb;
+
+ BUG_ON(atomic_read(&mccq->used) >= mccq->len);
+ wrb = queue_head_node(mccq);
+ queue_head_inc(mccq);
+ atomic_inc(&mccq->used);
+ memset(wrb, 0, sizeof(*wrb));
+ return wrb;
+}
+
+
int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_queue_info *eq, int eq_delay)
{
@@ -212,6 +384,7 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
struct be_dma_mem *q_mem = &eq->dma_mem;
int status;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_eq_create\n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -249,6 +422,7 @@ int be_cmd_fw_initialize(struct be_ctrl_info *ctrl)
int status;
u8 *endian_check;
+ SE_DEBUG(DBG_LVL_8, "In be_cmd_fw_initialize\n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -282,6 +456,7 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
void *ctxt = &req->context;
int status;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_cq_create \n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -289,7 +464,6 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_CQ_CREATE, sizeof(*req));
-
if (!q_mem->va)
SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n");
@@ -329,6 +503,53 @@ static u32 be_encoded_q_len(int q_len)
len_encoded = 0;
return len_encoded;
}
+
+int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
+ struct be_queue_info *mccq,
+ struct be_queue_info *cq)
+{
+ struct be_mcc_wrb *wrb;
+ struct be_cmd_req_mcc_create *req;
+ struct be_dma_mem *q_mem = &mccq->dma_mem;
+ struct be_ctrl_info *ctrl;
+ void *ctxt;
+ int status;
+
+ spin_lock(&phba->ctrl.mbox_lock);
+ ctrl = &phba->ctrl;
+ wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ req = embedded_payload(wrb);
+ ctxt = &req->context;
+
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+ OPCODE_COMMON_MCC_CREATE, sizeof(*req));
+
+ req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
+
+ AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt,
+ PCI_FUNC(phba->pcidev->devfn));
+ AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1);
+ AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt,
+ be_encoded_q_len(mccq->len));
+ AMAP_SET_BITS(struct amap_mcc_context, cq_id, ctxt, cq->id);
+
+ be_dws_cpu_to_le(ctxt, sizeof(req->context));
+
+ be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
+
+ status = be_mbox_notify_wait(phba);
+ if (!status) {
+ struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb);
+ mccq->id = le16_to_cpu(resp->id);
+ mccq->created = true;
+ }
+ spin_unlock(&phba->ctrl.mbox_lock);
+
+ return status;
+}
+
int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
int queue_type)
{
@@ -337,6 +558,7 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
u8 subsys = 0, opcode = 0;
int status;
+ SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_q_destroy \n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -350,6 +572,10 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
subsys = CMD_SUBSYSTEM_COMMON;
opcode = OPCODE_COMMON_CQ_DESTROY;
break;
+ case QTYPE_MCCQ:
+ subsys = CMD_SUBSYSTEM_COMMON;
+ opcode = OPCODE_COMMON_MCC_DESTROY;
+ break;
case QTYPE_WRBQ:
subsys = CMD_SUBSYSTEM_ISCSI;
opcode = OPCODE_COMMON_ISCSI_WRBQ_DESTROY;
@@ -377,30 +603,6 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
return status;
}
-int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr)
-{
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
- struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb);
- int status;
-
- spin_lock(&ctrl->mbox_lock);
- memset(wrb, 0, sizeof(*wrb));
- be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
- be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
- OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
- sizeof(*req));
-
- status = be_mbox_notify(ctrl);
- if (!status) {
- struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb);
-
- memcpy(mac_addr, resp->mac_address, ETH_ALEN);
- }
-
- spin_unlock(&ctrl->mbox_lock);
- return status;
-}
-
int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
struct be_queue_info *cq,
struct be_queue_info *dq, int length,
@@ -412,6 +614,7 @@ int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
void *ctxt = &req->context;
int status;
+ SE_DEBUG(DBG_LVL_8, "In be_cmd_create_default_pdu_queue\n");
spin_lock(&ctrl->mbox_lock);
memset(wrb, 0, sizeof(*wrb));
@@ -468,8 +671,10 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
status = be_mbox_notify(ctrl);
- if (!status)
+ if (!status) {
wrbq->id = le16_to_cpu(resp->cid);
+ wrbq->created = true;
+ }
spin_unlock(&ctrl->mbox_lock);
return status;
}
diff --git a/drivers/scsi/be2iscsi/be_cmds.h b/drivers/scsi/be2iscsi/be_cmds.h
index c20d686cbb4..5de8acb924c 100644
--- a/drivers/scsi/be2iscsi/be_cmds.h
+++ b/drivers/scsi/be2iscsi/be_cmds.h
@@ -47,6 +47,8 @@ struct be_mcc_wrb {
#define CQE_FLAGS_VALID_MASK (1 << 31)
#define CQE_FLAGS_ASYNC_MASK (1 << 30)
+#define CQE_FLAGS_COMPLETED_MASK (1 << 28)
+#define CQE_FLAGS_CONSUMED_MASK (1 << 27)
/* Completion Status */
#define MCC_STATUS_SUCCESS 0x0
@@ -173,7 +175,7 @@ struct be_cmd_req_hdr {
u8 domain; /* dword 0 */
u32 timeout; /* dword 1 */
u32 request_length; /* dword 2 */
- u32 rsvd; /* dword 3 */
+ u32 rsvd0; /* dword 3 */
};
struct be_cmd_resp_hdr {
@@ -382,7 +384,6 @@ struct be_cmd_req_modify_eq_delay {
#define ETH_ALEN 6
-
struct be_cmd_req_get_mac_addr {
struct be_cmd_req_hdr hdr;
u32 nic_port_count;
@@ -417,14 +418,21 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
int type);
+int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
+ struct be_queue_info *mccq,
+ struct be_queue_info *cq);
+
int be_poll_mcc(struct be_ctrl_info *ctrl);
-unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl);
-int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr);
+unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba);
+int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr);
/*ISCSI Functuions */
int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem);
+struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba);
+int be_mcc_notify_wait(struct beiscsi_hba *phba);
int be_mbox_notify(struct be_ctrl_info *ctrl);
@@ -531,6 +539,23 @@ struct amap_sol_cqe {
u8 valid; /* dword 3 */
} __packed;
+#define SOL_ICD_INDEX_MASK 0x0003FFC0
+struct amap_sol_cqe_ring {
+ u8 hw_sts[8]; /* dword 0 */
+ u8 i_sts[8]; /* dword 0 */
+ u8 i_resp[8]; /* dword 0 */
+ u8 i_flags[7]; /* dword 0 */
+ u8 s; /* dword 0 */
+ u8 i_exp_cmd_sn[32]; /* dword 1 */
+ u8 code[6]; /* dword 2 */
+ u8 icd_index[12]; /* dword 2 */
+ u8 rsvd[6]; /* dword 2 */
+ u8 i_cmd_wnd[8]; /* dword 2 */
+ u8 i_res_cnt[31]; /* dword 3 */
+ u8 valid; /* dword 3 */
+} __packed;
+
+
/**
* Post WRB Queue Doorbell Register used by the host Storage
@@ -664,8 +689,8 @@ struct be_fw_cfg {
#define OPCODE_COMMON_TCP_UPLOAD 56
#define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1
/* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */
-#define CMD_ISCSI_CONNECTION_INVALIDATE 1
-#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 2
+#define CMD_ISCSI_CONNECTION_INVALIDATE 0x8001
+#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 0x8002
#define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
#define INI_WR_CMD 1 /* Initiator write command */
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index 2fd25442cfa..d587b0362f1 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -67,11 +67,11 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn;
}
- cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
- shost, cmds_max,
- sizeof(*beiscsi_sess),
- sizeof(*io_task),
- initial_cmdsn, ISCSI_MAX_TARGET);
+ cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
+ shost, cmds_max,
+ sizeof(*beiscsi_sess),
+ sizeof(*io_task),
+ initial_cmdsn, ISCSI_MAX_TARGET);
if (!cls_session)
return NULL;
sess = cls_session->dd_data;
@@ -297,7 +297,7 @@ int beiscsi_get_host_param(struct Scsi_Host *shost,
switch (param) {
case ISCSI_HOST_PARAM_HWADDRESS:
- be_cmd_get_mac_addr(&phba->ctrl, phba->mac_address);
+ be_cmd_get_mac_addr(phba, phba->mac_address);
len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
break;
default:
@@ -377,16 +377,12 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_endpoint *beiscsi_ep;
struct beiscsi_offload_params params;
- struct iscsi_session *session = conn->session;
- struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
- struct beiscsi_hba *phba = iscsi_host_priv(shost);
memset(&params, 0, sizeof(struct beiscsi_offload_params));
beiscsi_ep = beiscsi_conn->ep;
if (!beiscsi_ep)
SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n");
- free_mgmt_sgl_handle(phba, beiscsi_conn->plogin_sgl_handle);
beiscsi_conn->login_in_progress = 0;
beiscsi_set_params_for_offld(beiscsi_conn, &params);
beiscsi_offload_connection(beiscsi_conn, &params);
@@ -498,6 +494,13 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
SE_DEBUG(DBG_LVL_1, "shost is NULL \n");
return ERR_PTR(ret);
}
+
+ if (phba->state) {
+ ret = -EBUSY;
+ SE_DEBUG(DBG_LVL_1, "The Adapet state is Not UP \n");
+ return ERR_PTR(ret);
+ }
+
ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint));
if (!ep) {
ret = -ENOMEM;
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 4f1aca346e3..1a557fa7788 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -39,7 +39,8 @@
static unsigned int be_iopoll_budget = 10;
static unsigned int be_max_phys_size = 64;
-static unsigned int enable_msix;
+static unsigned int enable_msix = 1;
+static unsigned int ring_mode;
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
@@ -58,6 +59,17 @@ static int beiscsi_slave_configure(struct scsi_device *sdev)
return 0;
}
+/*------------------- PCI Driver operations and data ----------------- */
+static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
+ { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
+ { 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(BE_VENDOR_ID, OC_DEVICE_ID4) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
+
static struct scsi_host_template beiscsi_sht = {
.module = THIS_MODULE,
.name = "ServerEngines 10Gbe open-iscsi Initiator Driver",
@@ -76,16 +88,8 @@ static struct scsi_host_template beiscsi_sht = {
.cmd_per_lun = BEISCSI_CMD_PER_LUN,
.use_clustering = ENABLE_CLUSTERING,
};
-static struct scsi_transport_template *beiscsi_scsi_transport;
-/*------------------- PCI Driver operations and data ----------------- */
-static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
- { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
- { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
- { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
+static struct scsi_transport_template *beiscsi_scsi_transport;
static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
{
@@ -104,7 +108,6 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
shost->max_cmd_len = BEISCSI_MAX_CMD_LEN;
shost->max_lun = BEISCSI_NUM_MAX_LUN;
shost->transportt = beiscsi_scsi_transport;
-
phba = iscsi_host_priv(shost);
memset(phba, 0, sizeof(*phba));
phba->shost = shost;
@@ -181,6 +184,7 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
return ret;
}
+ pci_set_master(pcidev);
if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) {
ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
if (ret) {
@@ -203,7 +207,6 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
status = beiscsi_map_pci_bars(phba, pdev);
if (status)
return status;
-
mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
mbox_mem_alloc->va = pci_alloc_consistent(pdev,
mbox_mem_alloc->size,
@@ -219,6 +222,9 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
spin_lock_init(&ctrl->mbox_lock);
+ spin_lock_init(&phba->ctrl.mcc_lock);
+ spin_lock_init(&phba->ctrl.mcc_cq_lock);
+
return status;
}
@@ -268,6 +274,113 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba,
}
/**
+ * be_isr_mcc - The isr routine of the driver.
+ * @irq: Not used
+ * @dev_id: Pointer to host adapter structure
+ */
+static irqreturn_t be_isr_mcc(int irq, void *dev_id)
+{
+ struct beiscsi_hba *phba;
+ struct be_eq_entry *eqe = NULL;
+ struct be_queue_info *eq;
+ struct be_queue_info *mcc;
+ unsigned int num_eq_processed;
+ struct be_eq_obj *pbe_eq;
+ unsigned long flags;
+
+ pbe_eq = dev_id;
+ eq = &pbe_eq->q;
+ phba = pbe_eq->phba;
+ mcc = &phba->ctrl.mcc_obj.cq;
+ eqe = queue_tail_node(eq);
+ if (!eqe)
+ SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
+
+ num_eq_processed = 0;
+
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ if (((eqe->dw[offsetof(struct amap_eq_entry,
+ resource_id) / 32] &
+ EQE_RESID_MASK) >> 16) == mcc->id) {
+ spin_lock_irqsave(&phba->isr_lock, flags);
+ phba->todo_mcc_cq = 1;
+ 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_mcc_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;
+}
+
+/**
+ * be_isr_msix - The isr routine of the driver.
+ * @irq: Not used
+ * @dev_id: Pointer to host adapter structure
+ */
+static irqreturn_t be_isr_msix(int irq, void *dev_id)
+{
+ struct beiscsi_hba *phba;
+ struct be_eq_entry *eqe = NULL;
+ struct be_queue_info *eq;
+ struct be_queue_info *cq;
+ unsigned int num_eq_processed;
+ struct be_eq_obj *pbe_eq;
+ unsigned long flags;
+
+ pbe_eq = dev_id;
+ eq = &pbe_eq->q;
+ cq = pbe_eq->cq;
+ eqe = queue_tail_node(eq);
+ if (!eqe)
+ SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
+
+ phba = pbe_eq->phba;
+ num_eq_processed = 0;
+ if (blk_iopoll_enabled) {
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
+ blk_iopoll_sched(&pbe_eq->iopoll);
+
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ 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;
+ 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;
+ }
+}
+
+/**
* be_isr - The isr routine of the driver.
* @irq: Not used
* @dev_id: Pointer to host adapter structure
@@ -280,48 +393,70 @@ static irqreturn_t be_isr(int irq, void *dev_id)
struct be_eq_entry *eqe = NULL;
struct be_queue_info *eq;
struct be_queue_info *cq;
+ struct be_queue_info *mcc;
unsigned long flags, index;
- unsigned int num_eq_processed;
+ unsigned int num_mcceq_processed, num_ioeq_processed;
struct be_ctrl_info *ctrl;
+ struct be_eq_obj *pbe_eq;
int isr;
phba = dev_id;
- if (!enable_msix) {
- ctrl = &phba->ctrl;;
- isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
- (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE));
- if (!isr)
- return IRQ_NONE;
- }
+ ctrl = &phba->ctrl;;
+ isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
+ (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE));
+ if (!isr)
+ return IRQ_NONE;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- eq = &phwi_context->be_eq.q;
- cq = &phwi_context->be_cq;
+ pbe_eq = &phwi_context->be_eq[0];
+
+ eq = &phwi_context->be_eq[0].q;
+ mcc = &phba->ctrl.mcc_obj.cq;
index = 0;
eqe = queue_tail_node(eq);
if (!eqe)
SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
- num_eq_processed = 0;
+ num_ioeq_processed = 0;
+ num_mcceq_processed = 0;
if (blk_iopoll_enabled) {
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
& EQE_VALID_MASK) {
- if (!blk_iopoll_sched_prep(&phba->iopoll))
- blk_iopoll_sched(&phba->iopoll);
-
+ if (((eqe->dw[offsetof(struct amap_eq_entry,
+ resource_id) / 32] &
+ EQE_RESID_MASK) >> 16) == mcc->id) {
+ spin_lock_irqsave(&phba->isr_lock, flags);
+ phba->todo_mcc_cq = 1;
+ spin_unlock_irqrestore(&phba->isr_lock, flags);
+ num_mcceq_processed++;
+ } else {
+ if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
+ blk_iopoll_sched(&pbe_eq->iopoll);
+ num_ioeq_processed++;
+ }
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
queue_tail_inc(eq);
eqe = queue_tail_node(eq);
- num_eq_processed++;
- SE_DEBUG(DBG_LVL_8, "Valid EQE\n");
}
- if (num_eq_processed) {
- hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 0, 1);
+ if (num_ioeq_processed || num_mcceq_processed) {
+ if (phba->todo_mcc_cq)
+ queue_work(phba->wq, &phba->work_cqs);
+
+ if ((num_mcceq_processed) && (!num_ioeq_processed))
+ hwi_ring_eq_db(phba, eq->id, 0,
+ (num_ioeq_processed +
+ num_mcceq_processed) , 1, 1);
+ else
+ hwi_ring_eq_db(phba, eq->id, 0,
+ (num_ioeq_processed +
+ num_mcceq_processed), 0, 1);
+
return IRQ_HANDLED;
} else
return IRQ_NONE;
} else {
+ cq = &phwi_context->be_cq[0];
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
& EQE_VALID_MASK) {
@@ -339,13 +474,14 @@ static irqreturn_t be_isr(int irq, void *dev_id)
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
queue_tail_inc(eq);
eqe = queue_tail_node(eq);
- num_eq_processed++;
+ num_ioeq_processed++;
}
if (phba->todo_cq || phba->todo_mcc_cq)
queue_work(phba->wq, &phba->work_cqs);
- if (num_eq_processed) {
- hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 1, 1);
+ if (num_ioeq_processed) {
+ hwi_ring_eq_db(phba, eq->id, 0,
+ num_ioeq_processed, 1, 1);
return IRQ_HANDLED;
} else
return IRQ_NONE;
@@ -355,13 +491,32 @@ static irqreturn_t be_isr(int irq, void *dev_id)
static int beiscsi_init_irqs(struct beiscsi_hba *phba)
{
struct pci_dev *pcidev = phba->pcidev;
- int ret;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ int ret, msix_vec, i = 0;
+ char desc[32];
- ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED, "beiscsi", phba);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
- "Failed to register irq\\n");
- return ret;
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
+
+ if (phba->msix_enabled) {
+ for (i = 0; i < phba->num_cpus; i++) {
+ sprintf(desc, "beiscsi_msix_%04x", i);
+ msix_vec = phba->msix_entries[i].vector;
+ ret = request_irq(msix_vec, be_isr_msix, 0, desc,
+ &phwi_context->be_eq[i]);
+ }
+ msix_vec = phba->msix_entries[i].vector;
+ ret = request_irq(msix_vec, be_isr_mcc, 0, "beiscsi_msix_mcc",
+ &phwi_context->be_eq[i]);
+ } else {
+ ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED,
+ "beiscsi", phba);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
+ "Failed to register irq\\n");
+ return ret;
+ }
}
return 0;
}
@@ -378,15 +533,6 @@ static void hwi_ring_cq_db(struct beiscsi_hba *phba,
iowrite32(val, phba->db_va + DB_CQ_OFFSET);
}
-/*
- * async pdus include
- * a. unsolicited NOP-In (target initiated NOP-In)
- * b. Async Messages
- * c. Reject PDU
- * d. Login response
- * These headers arrive unprocessed by the EP firmware and iSCSI layer
- * process them
- */
static unsigned int
beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba,
@@ -397,6 +543,9 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
{
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
+ struct iscsi_task *task;
+ struct beiscsi_io_task *io_task;
+ struct iscsi_hdr *login_hdr;
switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] &
PDUBASE_OPCODE_MASK) {
@@ -412,6 +561,10 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
SE_DEBUG(DBG_LVL_1, "In ISCSI_OP_REJECT\n");
break;
case ISCSI_OP_LOGIN_RSP:
+ task = conn->login_task;
+ io_task = task->dd_data;
+ login_hdr = (struct iscsi_hdr *)ppdu;
+ login_hdr->itt = io_task->libiscsi_itt;
break;
default:
shost_printk(KERN_WARNING, phba->shost,
@@ -440,7 +593,8 @@ static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
io_sgl_alloc_index];
phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL;
phba->io_sgl_hndl_avbl--;
- if (phba->io_sgl_alloc_index == (phba->params.ios_per_ctrl - 1))
+ if (phba->io_sgl_alloc_index == (phba->params.
+ ios_per_ctrl - 1))
phba->io_sgl_alloc_index = 0;
else
phba->io_sgl_alloc_index++;
@@ -490,9 +644,18 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
phwi_ctrlr = phba->phwi_ctrlr;
pwrb_context = &phwi_ctrlr->wrb_context[cid];
- pwrb_handle = pwrb_context->pwrb_handle_base[index];
- pwrb_handle->wrb_index = index;
- pwrb_handle->nxt_wrb_index = index;
+ if (pwrb_context->wrb_handles_available) {
+ pwrb_handle = pwrb_context->pwrb_handle_base[
+ pwrb_context->alloc_index];
+ pwrb_context->wrb_handles_available--;
+ pwrb_handle->nxt_wrb_index = pwrb_handle->wrb_index;
+ if (pwrb_context->alloc_index ==
+ (phba->params.wrbs_per_cxn - 1))
+ pwrb_context->alloc_index = 0;
+ else
+ pwrb_context->alloc_index++;
+ } else
+ pwrb_handle = NULL;
return pwrb_handle;
}
@@ -508,11 +671,20 @@ static void
free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context,
struct wrb_handle *pwrb_handle)
{
+ if (!ring_mode)
+ pwrb_context->pwrb_handle_base[pwrb_context->free_index] =
+ pwrb_handle;
+ pwrb_context->wrb_handles_available++;
+ if (pwrb_context->free_index == (phba->params.wrbs_per_cxn - 1))
+ pwrb_context->free_index = 0;
+ else
+ pwrb_context->free_index++;
+
SE_DEBUG(DBG_LVL_8,
- "FREE WRB: pwrb_handle=%p free_index=%d=0x%x"
+ "FREE WRB: pwrb_handle=%p free_index=0x%x"
"wrb_handles_available=%d \n",
pwrb_handle, pwrb_context->free_index,
- pwrb_context->free_index, pwrb_context->wrb_handles_available);
+ pwrb_context->wrb_handles_available);
}
static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba)
@@ -540,6 +712,8 @@ void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
{
+ SE_DEBUG(DBG_LVL_8, "In free_mgmt_sgl_handle,eh_sgl_free_index=%d \n",
+ phba->eh_sgl_free_index);
if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) {
/*
* this can happen if clean_task is called on a task that
@@ -572,10 +746,10 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
u32 resid = 0, exp_cmdsn, max_cmdsn;
u8 rsp, status, flags;
- exp_cmdsn = be32_to_cpu(psol->
+ exp_cmdsn = (psol->
dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
& SOL_EXP_CMD_SN_MASK);
- max_cmdsn = be32_to_cpu((psol->
+ 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)
@@ -610,9 +784,9 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
}
if (status == SAM_STAT_CHECK_CONDITION) {
+ unsigned short *slen = (unsigned short *)sts_bhs->sense_info;
sense = sts_bhs->sense_info + sizeof(unsigned short);
- sense_len =
- cpu_to_be16((unsigned short)(sts_bhs->sense_info[0]));
+ sense_len = cpu_to_be16(*slen);
memcpy(task->sc->sense_buffer, sense,
min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE));
}
@@ -620,8 +794,8 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
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);
+ dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
+ & SOL_RES_CNT_MASK);
}
unmap:
scsi_dma_unmap(io_task->scsi_cmnd);
@@ -633,6 +807,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
struct iscsi_task *task, struct sol_cqe *psol)
{
struct iscsi_logout_rsp *hdr;
+ struct beiscsi_io_task *io_task = task->dd_data;
struct iscsi_conn *conn = beiscsi_conn->conn;
hdr = (struct iscsi_logout_rsp *)task->hdr;
@@ -651,7 +826,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
/ 32] & SOL_CMD_WND_MASK) >> 24) - 1);
hdr->hlength = 0;
-
+ hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -661,6 +836,7 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
{
struct iscsi_tm_rsp *hdr;
struct iscsi_conn *conn = beiscsi_conn->conn;
+ struct beiscsi_io_task *io_task = task->dd_data;
hdr = (struct iscsi_tm_rsp *)task->hdr;
hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
@@ -668,11 +844,12 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
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);
+ 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->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -681,18 +858,36 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
struct beiscsi_hba *phba, struct sol_cqe *psol)
{
struct hwi_wrb_context *pwrb_context;
- struct wrb_handle *pwrb_handle;
+ struct wrb_handle *pwrb_handle = NULL;
+ struct sgl_handle *psgl_handle = NULL;
struct hwi_controller *phwi_ctrlr;
+ struct iscsi_task *task;
+ struct beiscsi_io_task *io_task;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
phwi_ctrlr = phba->phwi_ctrlr;
- pwrb_context = &phwi_ctrlr->wrb_context[((psol->
+ if (ring_mode) {
+ psgl_handle = phba->sgl_hndl_array[((psol->
+ dw[offsetof(struct amap_sol_cqe_ring, icd_index) /
+ 32] & SOL_ICD_INDEX_MASK) >> 6)];
+ pwrb_context = &phwi_ctrlr->wrb_context[psgl_handle->cid];
+ task = psgl_handle->task;
+ pwrb_handle = NULL;
+ } else {
+ pwrb_context = &phwi_ctrlr->wrb_context[((psol->
dw[offsetof(struct amap_sol_cqe, cid) / 32] &
SOL_CID_MASK) >> 6)];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
dw[offsetof(struct amap_sol_cqe, wrb_index) /
32] & SOL_WRB_INDEX_MASK) >> 16)];
+ task = pwrb_handle->pio_handle;
+ }
+
+ io_task = task->dd_data;
+ spin_lock(&phba->mgmt_sgl_lock);
+ free_mgmt_sgl_handle(phba, io_task->psgl_handle);
+ spin_unlock(&phba->mgmt_sgl_lock);
spin_lock_bh(&session->lock);
free_wrb_handle(phba, pwrb_context, pwrb_handle);
spin_unlock_bh(&session->lock);
@@ -704,6 +899,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
{
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]
@@ -715,6 +911,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
/ 32] & SOL_CMD_WND_MASK) >> 24) - 1);
hdr->opcode = ISCSI_OP_NOOP_IN;
+ hdr->itt = io_task->libiscsi_itt;
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}
@@ -726,25 +923,33 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
struct iscsi_wrb *pwrb = NULL;
struct hwi_controller *phwi_ctrlr;
struct iscsi_task *task;
- struct beiscsi_io_task *io_task;
+ struct sgl_handle *psgl_handle = NULL;
+ unsigned int type;
struct iscsi_conn *conn = beiscsi_conn->conn;
struct iscsi_session *session = conn->session;
phwi_ctrlr = phba->phwi_ctrlr;
-
- pwrb_context = &phwi_ctrlr->
- wrb_context[((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32]
- & SOL_CID_MASK) >> 6)];
- pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
+ if (ring_mode) {
+ psgl_handle = phba->sgl_hndl_array[((psol->
+ dw[offsetof(struct amap_sol_cqe_ring, icd_index) /
+ 32] & SOL_ICD_INDEX_MASK) >> 6)];
+ task = psgl_handle->task;
+ type = psgl_handle->type;
+ } else {
+ pwrb_context = &phwi_ctrlr->
+ wrb_context[((psol->dw[offsetof
+ (struct amap_sol_cqe, cid) / 32]
+ & SOL_CID_MASK) >> 6)];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
dw[offsetof(struct amap_sol_cqe, wrb_index) /
32] & SOL_WRB_INDEX_MASK) >> 16)];
-
- task = pwrb_handle->pio_handle;
- io_task = task->dd_data;
+ task = pwrb_handle->pio_handle;
+ pwrb = pwrb_handle->pwrb;
+ type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
+ WRB_TYPE_MASK) >> 28;
+ }
spin_lock_bh(&session->lock);
- pwrb = pwrb_handle->pwrb;
- switch ((pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
- WRB_TYPE_MASK) >> 28) {
+ switch (type) {
case HWH_TYPE_IO:
case HWH_TYPE_IO_RD:
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) ==
@@ -773,12 +978,21 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
break;
default:
- shost_printk(KERN_WARNING, phba->shost,
- "wrb_index 0x%x CID 0x%x\n",
- ((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));
+ if (ring_mode)
+ shost_printk(KERN_WARNING, phba->shost,
+ "In hwi_complete_cmd, unknown type = %d"
+ "icd_index 0x%x CID 0x%x\n", type,
+ ((psol->dw[offsetof(struct amap_sol_cqe_ring,
+ icd_index) / 32] & SOL_ICD_INDEX_MASK) >> 6),
+ psgl_handle->cid);
+ else
+ shost_printk(KERN_WARNING, phba->shost,
+ "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));
break;
}
@@ -1208,40 +1422,55 @@ static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
hwi_post_async_buffers(phba, pasync_handle->is_header);
}
-static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
+
+static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
{
- struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
struct be_queue_info *cq;
struct sol_cqe *sol;
struct dmsg_cqe *dmsg;
unsigned int num_processed = 0;
unsigned int tot_nump = 0;
struct beiscsi_conn *beiscsi_conn;
+ struct sgl_handle *psgl_handle = NULL;
+ struct beiscsi_hba *phba;
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
- cq = &phwi_context->be_cq;
+ cq = pbe_eq->cq;
sol = queue_tail_node(cq);
+ phba = pbe_eq->phba;
while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] &
CQE_VALID_MASK) {
be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
- beiscsi_conn = phba->conn_table[(u32) (sol->
+ if (ring_mode) {
+ psgl_handle = phba->sgl_hndl_array[((sol->
+ dw[offsetof(struct amap_sol_cqe_ring,
+ icd_index) / 32] & SOL_ICD_INDEX_MASK)
+ >> 6)];
+ beiscsi_conn = phba->conn_table[psgl_handle->cid];
+ if (!beiscsi_conn || !beiscsi_conn->ep) {
+ shost_printk(KERN_WARNING, phba->shost,
+ "Connection table empty for cid = %d\n",
+ psgl_handle->cid);
+ return 0;
+ }
+
+ } else {
+ beiscsi_conn = phba->conn_table[(u32) (sol->
dw[offsetof(struct amap_sol_cqe, cid) / 32] &
SOL_CID_MASK) >> 6];
- if (!beiscsi_conn || !beiscsi_conn->ep) {
- shost_printk(KERN_WARNING, phba->shost,
+ if (!beiscsi_conn || !beiscsi_conn->ep) {
+ shost_printk(KERN_WARNING, phba->shost,
"Connection table empty for cid = %d\n",
(u32)(sol->dw[offsetof(struct amap_sol_cqe,
cid) / 32] & SOL_CID_MASK) >> 6);
- return 0;
+ return 0;
+ }
}
if (num_processed >= 32) {
- hwi_ring_cq_db(phba, phwi_context->be_cq.id,
+ hwi_ring_cq_db(phba, cq->id,
num_processed, 0, 0);
tot_nump += num_processed;
num_processed = 0;
@@ -1258,8 +1487,12 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
hwi_complete_drvr_msgs(beiscsi_conn, phba, sol);
break;
case UNSOL_HDR_NOTIFY:
+ SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR_ NOTIFY\n");
+ hwi_process_default_pdu_ring(beiscsi_conn, phba,
+ (struct i_t_dpdu_cqe *)sol);
+ break;
case UNSOL_DATA_NOTIFY:
- SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR/DATA_NOTIFY\n");
+ SE_DEBUG(DBG_LVL_8, "Received UNSOL_DATA_NOTIFY\n");
hwi_process_default_pdu_ring(beiscsi_conn, phba,
(struct i_t_dpdu_cqe *)sol);
break;
@@ -1278,13 +1511,21 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
case CMD_CXN_KILLED_ITT_INVALID:
case CMD_CXN_KILLED_SEQ_OUTOFORDER:
case CMD_CXN_KILLED_INVALID_DATASN_RCVD:
- SE_DEBUG(DBG_LVL_1,
+ if (ring_mode) {
+ SE_DEBUG(DBG_LVL_1,
+ "CQ Error notification for cmd.. "
+ "code %d cid 0x%x\n",
+ sol->dw[offsetof(struct amap_sol_cqe, code) /
+ 32] & CQE_CODE_MASK, psgl_handle->cid);
+ } else {
+ SE_DEBUG(DBG_LVL_1,
"CQ Error notification for cmd.. "
"code %d cid 0x%x\n",
sol->dw[offsetof(struct amap_sol_cqe, code) /
32] & CQE_CODE_MASK,
(sol->dw[offsetof(struct amap_sol_cqe, cid) /
32] & SOL_CID_MASK));
+ }
break;
case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
SE_DEBUG(DBG_LVL_1,
@@ -1306,23 +1547,37 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
case CXN_KILLED_OVER_RUN_RESIDUAL:
case CXN_KILLED_UNDER_RUN_RESIDUAL:
case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN:
- SE_DEBUG(DBG_LVL_1, "CQ Error %d, resetting CID "
+ if (ring_mode) {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
+ "0x%x...\n",
+ sol->dw[offsetof(struct amap_sol_cqe, code) /
+ 32] & CQE_CODE_MASK, psgl_handle->cid);
+ } else {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
"0x%x...\n",
sol->dw[offsetof(struct amap_sol_cqe, code) /
32] & CQE_CODE_MASK,
sol->dw[offsetof(struct amap_sol_cqe, cid) /
32] & CQE_CID_MASK);
+ }
iscsi_conn_failure(beiscsi_conn->conn,
ISCSI_ERR_CONN_FAILED);
break;
case CXN_KILLED_RST_SENT:
case CXN_KILLED_RST_RCVD:
- SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset received/sent "
- "on CID 0x%x...\n",
+ if (ring_mode) {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
+ "received/sent on CID 0x%x...\n",
+ sol->dw[offsetof(struct amap_sol_cqe, code) /
+ 32] & CQE_CODE_MASK, psgl_handle->cid);
+ } else {
+ SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
+ "received/sent on CID 0x%x...\n",
sol->dw[offsetof(struct amap_sol_cqe, code) /
32] & CQE_CODE_MASK,
sol->dw[offsetof(struct amap_sol_cqe, cid) /
32] & CQE_CID_MASK);
+ }
iscsi_conn_failure(beiscsi_conn->conn,
ISCSI_ERR_CONN_FAILED);
break;
@@ -1344,8 +1599,7 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
if (num_processed > 0) {
tot_nump += num_processed;
- hwi_ring_cq_db(phba, phwi_context->be_cq.id, num_processed,
- 1, 0);
+ hwi_ring_cq_db(phba, cq->id, num_processed, 1, 0);
}
return tot_nump;
}
@@ -1353,21 +1607,30 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
static 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);
+ 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) {
spin_lock_irqsave(&phba->isr_lock, flags);
phba->todo_mcc_cq = 0;
spin_unlock_irqrestore(&phba->isr_lock, flags);
- SE_DEBUG(DBG_LVL_1, "MCC Interrupt Not expected \n");
}
if (phba->todo_cq) {
spin_lock_irqsave(&phba->isr_lock, flags);
phba->todo_cq = 0;
spin_unlock_irqrestore(&phba->isr_lock, flags);
- beiscsi_process_cq(phba);
+ beiscsi_process_cq(pbe_eq);
}
}
@@ -1375,19 +1638,15 @@ static int be_iopoll(struct blk_iopoll *iop, int budget)
{
static unsigned int ret;
struct beiscsi_hba *phba;
+ struct be_eq_obj *pbe_eq;
- phba = container_of(iop, struct beiscsi_hba, iopoll);
-
- ret = beiscsi_process_cq(phba);
+ pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
+ ret = beiscsi_process_cq(pbe_eq);
if (ret < budget) {
- struct hwi_controller *phwi_ctrlr;
- struct hwi_context_memory *phwi_context;
-
- phwi_ctrlr = phba->phwi_ctrlr;
- phwi_context = phwi_ctrlr->phwi_ctxt;
+ phba = pbe_eq->phba;
blk_iopoll_complete(iop);
- hwi_ring_eq_db(phba, phwi_context->be_eq.q.id, 0,
- 0, 1, 1);
+ SE_DEBUG(DBG_LVL_8, "rearm pbe_eq->q.id =%d\n", pbe_eq->q.id);
+ hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
}
return ret;
}
@@ -1537,14 +1796,12 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
{
- unsigned int num_cq_pages, num_eq_pages, num_async_pdu_buf_pages;
+ unsigned int num_cq_pages, num_async_pdu_buf_pages;
unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn;
unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages;
num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
sizeof(struct sol_cqe));
- num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
- sizeof(struct be_eq_entry));
num_async_pdu_buf_pages =
PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
phba->params.defpdu_hdr_sz);
@@ -1565,8 +1822,6 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
phba->mem_req[HWI_MEM_ADDN_CONTEXT] =
sizeof(struct hwi_context_memory);
- phba->mem_req[HWI_MEM_CQ] = num_cq_pages * PAGE_SIZE;
- phba->mem_req[HWI_MEM_EQ] = num_eq_pages * PAGE_SIZE;
phba->mem_req[HWI_MEM_WRB] = sizeof(struct iscsi_wrb)
* (phba->params.wrbs_per_cxn)
@@ -1751,8 +2006,6 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
pwrb_context = &phwi_ctrlr->wrb_context[index];
- SE_DEBUG(DBG_LVL_8, "cid=%d pwrb_context=%p \n", index,
- pwrb_context);
pwrb_context->pwrb_handle_base =
kzalloc(sizeof(struct wrb_handle *) *
phba->params.wrbs_per_cxn, GFP_KERNEL);
@@ -1767,6 +2020,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
pwrb_context->pwrb_handle_basestd[j] =
pwrb_handle;
pwrb_context->wrb_handles_available++;
+ pwrb_handle->wrb_index = j;
pwrb_handle++;
}
pwrb_context->free_index = 0;
@@ -1785,6 +2039,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
pwrb_context->pwrb_handle_basestd[j] =
pwrb_handle;
pwrb_context->wrb_handles_available++;
+ pwrb_handle->wrb_index = j;
pwrb_handle++;
}
pwrb_context->free_index = 0;
@@ -2042,79 +2297,126 @@ static int be_fill_queue(struct be_queue_info *q,
return 0;
}
-static int beiscsi_create_eq(struct beiscsi_hba *phba,
+static int beiscsi_create_eqs(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context)
{
- unsigned int idx;
- int ret;
+ unsigned int i, num_eq_pages;
+ int ret, eq_for_mcc;
struct be_queue_info *eq;
struct be_dma_mem *mem;
- struct be_mem_descriptor *mem_descr;
void *eq_vaddress;
+ dma_addr_t paddr;
- idx = 0;
- eq = &phwi_context->be_eq.q;
- mem = &eq->dma_mem;
- mem_descr = phba->init_mem;
- mem_descr += HWI_MEM_EQ;
- eq_vaddress = mem_descr->mem_array[idx].virtual_address;
-
- ret = be_fill_queue(eq, phba->params.num_eq_entries,
- sizeof(struct be_eq_entry), eq_vaddress);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for EQ \n");
- return ret;
- }
+ num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
+ sizeof(struct be_eq_entry));
- mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address;
+ if (phba->msix_enabled)
+ eq_for_mcc = 1;
+ else
+ eq_for_mcc = 0;
+ for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
+ eq = &phwi_context->be_eq[i].q;
+ mem = &eq->dma_mem;
+ phwi_context->be_eq[i].phba = phba;
+ eq_vaddress = pci_alloc_consistent(phba->pcidev,
+ num_eq_pages * PAGE_SIZE,
+ &paddr);
+ if (!eq_vaddress)
+ goto create_eq_error;
+
+ mem->va = eq_vaddress;
+ ret = be_fill_queue(eq, phba->params.num_eq_entries,
+ sizeof(struct be_eq_entry), eq_vaddress);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "be_fill_queue Failed for EQ \n");
+ goto create_eq_error;
+ }
- ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
- phwi_context->be_eq.cur_eqd);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost, "beiscsi_cmd_eq_create"
- "Failedfor EQ \n");
- return ret;
+ mem->dma = paddr;
+ ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
+ phwi_context->cur_eqd);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "beiscsi_cmd_eq_create"
+ "Failedfor EQ \n");
+ goto create_eq_error;
+ }
+ SE_DEBUG(DBG_LVL_8, "eqid = %d\n", phwi_context->be_eq[i].q.id);
}
- SE_DEBUG(DBG_LVL_8, "eq id is %d\n", phwi_context->be_eq.q.id);
return 0;
+create_eq_error:
+ for (i = 0; i < (phba->num_cpus + 1); i++) {
+ eq = &phwi_context->be_eq[i].q;
+ mem = &eq->dma_mem;
+ if (mem->va)
+ pci_free_consistent(phba->pcidev, num_eq_pages
+ * PAGE_SIZE,
+ mem->va, mem->dma);
+ }
+ return ret;
}
-static int beiscsi_create_cq(struct beiscsi_hba *phba,
+static int beiscsi_create_cqs(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context)
{
- unsigned int idx;
+ unsigned int i, num_cq_pages;
int ret;
struct be_queue_info *cq, *eq;
struct be_dma_mem *mem;
- struct be_mem_descriptor *mem_descr;
+ struct be_eq_obj *pbe_eq;
void *cq_vaddress;
+ dma_addr_t paddr;
- idx = 0;
- cq = &phwi_context->be_cq;
- eq = &phwi_context->be_eq.q;
- mem = &cq->dma_mem;
- mem_descr = phba->init_mem;
- mem_descr += HWI_MEM_CQ;
- cq_vaddress = mem_descr->mem_array[idx].virtual_address;
- ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2,
- sizeof(struct sol_cqe), cq_vaddress);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "be_fill_queue Failed for ISCSI CQ \n");
- return ret;
- }
+ num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
+ sizeof(struct sol_cqe));
- mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address;
- ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false, false, 0);
- if (ret) {
- shost_printk(KERN_ERR, phba->shost,
- "beiscsi_cmd_eq_create Failed for ISCSI CQ \n");
- return ret;
+ for (i = 0; i < phba->num_cpus; i++) {
+ cq = &phwi_context->be_cq[i];
+ eq = &phwi_context->be_eq[i].q;
+ pbe_eq = &phwi_context->be_eq[i];
+ pbe_eq->cq = cq;
+ pbe_eq->phba = phba;
+ mem = &cq->dma_mem;
+ cq_vaddress = pci_alloc_consistent(phba->pcidev,
+ num_cq_pages * PAGE_SIZE,
+ &paddr);
+ if (!cq_vaddress)
+ goto create_cq_error;
+ ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2,
+ sizeof(struct sol_cqe), cq_vaddress);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "be_fill_queue Failed for ISCSI CQ \n");
+ goto create_cq_error;
+ }
+
+ mem->dma = paddr;
+ ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false,
+ false, 0);
+ if (ret) {
+ shost_printk(KERN_ERR, phba->shost,
+ "beiscsi_cmd_eq_create"
+ "Failed for ISCSI CQ \n");
+ goto create_cq_error;
+ }
+ SE_DEBUG(DBG_LVL_8, "iscsi cq_id is %d for eq_id %d\n",
+ cq->id, eq->id);
+ SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
}
- SE_DEBUG(DBG_LVL_8, "iscsi cq id is %d\n", phwi_context->be_cq.id);
- SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
return 0;
+
+create_cq_error:
+ for (i = 0; i < phba->num_cpus; i++) {
+ cq = &phwi_context->be_cq[i];
+ mem = &cq->dma_mem;
+ if (mem->va)
+ pci_free_consistent(phba->pcidev, num_cq_pages
+ * PAGE_SIZE,
+ mem->va, mem->dma);
+ }
+ return ret;
+
}
static int
@@ -2132,7 +2434,7 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba,
idx = 0;
dq = &phwi_context->be_def_hdrq;
- cq = &phwi_context->be_cq;
+ cq = &phwi_context->be_cq[0];
mem = &dq->dma_mem;
mem_descr = phba->init_mem;
mem_descr += HWI_MEM_ASYNC_HEADER_RING;
@@ -2176,7 +2478,7 @@ beiscsi_create_def_data(struct beiscsi_hba *phba,
idx = 0;
dataq = &phwi_context->be_def_dataq;
- cq = &phwi_context->be_cq;
+ cq = &phwi_context->be_cq[0];
mem = &dataq->dma_mem;
mem_descr = phba->init_mem;
mem_descr += HWI_MEM_ASYNC_DATA_RING;
@@ -2239,6 +2541,30 @@ beiscsi_post_pages(struct beiscsi_hba *phba)
return 0;
}
+static void be_queue_free(struct beiscsi_hba *phba, struct be_queue_info *q)
+{
+ struct be_dma_mem *mem = &q->dma_mem;
+ if (mem->va)
+ pci_free_consistent(phba->pcidev, mem->size,
+ mem->va, mem->dma);
+}
+
+static int be_queue_alloc(struct beiscsi_hba *phba, struct be_queue_info *q,
+ u16 len, u16 entry_size)
+{
+ struct be_dma_mem *mem = &q->dma_mem;
+
+ memset(q, 0, sizeof(*q));
+ q->len = len;
+ q->entry_size = entry_size;
+ mem->size = len * entry_size;
+ mem->va = pci_alloc_consistent(phba->pcidev, mem->size, &mem->dma);
+ if (!mem->va)
+ return -1;
+ memset(mem->va, 0, mem->size);
+ return 0;
+}
+
static int
beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
struct hwi_context_memory *phwi_context,
@@ -2328,13 +2654,29 @@ static void free_wrb_handles(struct beiscsi_hba *phba)
}
}
+static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
+{
+ struct be_queue_info *q;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ q = &phba->ctrl.mcc_obj.q;
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
+ be_queue_free(phba, q);
+
+ q = &phba->ctrl.mcc_obj.cq;
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ be_queue_free(phba, q);
+}
+
static void hwi_cleanup(struct beiscsi_hba *phba)
{
struct be_queue_info *q;
struct be_ctrl_info *ctrl = &phba->ctrl;
struct hwi_controller *phwi_ctrlr;
struct hwi_context_memory *phwi_context;
- int i;
+ int i, eq_num;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
@@ -2343,7 +2685,6 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
if (q->created)
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
}
-
free_wrb_handles(phba);
q = &phwi_context->be_def_hdrq;
@@ -2356,13 +2697,76 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
- q = &phwi_context->be_cq;
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ for (i = 0; i < (phba->num_cpus); i++) {
+ q = &phwi_context->be_cq[i];
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+ }
+ if (phba->msix_enabled)
+ eq_num = 1;
+ else
+ eq_num = 0;
+ for (i = 0; i < (phba->num_cpus + eq_num); i++) {
+ q = &phwi_context->be_eq[i].q;
+ if (q->created)
+ beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+ }
+ be_mcc_queues_destroy(phba);
+}
- q = &phwi_context->be_eq.q;
- if (q->created)
- beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+static int be_mcc_queues_create(struct beiscsi_hba *phba,
+ struct hwi_context_memory *phwi_context)
+{
+ struct be_queue_info *q, *cq;
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+
+ /* Alloc MCC compl queue */
+ cq = &phba->ctrl.mcc_obj.cq;
+ if (be_queue_alloc(phba, cq, MCC_CQ_LEN,
+ sizeof(struct be_mcc_compl)))
+ goto err;
+ /* Ask BE to create MCC compl queue; */
+ if (phba->msix_enabled) {
+ if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq
+ [phba->num_cpus].q, false, true, 0))
+ goto mcc_cq_free;
+ } else {
+ if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq[0].q,
+ false, true, 0))
+ goto mcc_cq_free;
+ }
+
+ /* Alloc MCC queue */
+ q = &phba->ctrl.mcc_obj.q;
+ if (be_queue_alloc(phba, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb)))
+ goto mcc_cq_destroy;
+
+ /* Ask BE to create MCC queue */
+ if (beiscsi_cmd_mccq_create(phba, q, cq))
+ goto mcc_q_free;
+
+ return 0;
+
+mcc_q_free:
+ be_queue_free(phba, q);
+mcc_cq_destroy:
+ beiscsi_cmd_q_destroy(ctrl, cq, QTYPE_CQ);
+mcc_cq_free:
+ be_queue_free(phba, cq);
+err:
+ return -1;
+}
+
+static int find_num_cpus(void)
+{
+ int num_cpus = 0;
+
+ num_cpus = num_online_cpus();
+ if (num_cpus >= MAX_CPUS)
+ num_cpus = MAX_CPUS - 1;
+
+ SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", num_cpus);
+ return num_cpus;
}
static int hwi_init_port(struct beiscsi_hba *phba)
@@ -2376,26 +2780,33 @@ static int hwi_init_port(struct beiscsi_hba *phba)
def_pdu_ring_sz =
phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr);
phwi_ctrlr = phba->phwi_ctrlr;
-
phwi_context = phwi_ctrlr->phwi_ctxt;
- phwi_context->be_eq.max_eqd = 0;
- phwi_context->be_eq.min_eqd = 0;
- phwi_context->be_eq.cur_eqd = 64;
- phwi_context->be_eq.enable_aic = false;
+ phwi_context->max_eqd = 0;
+ phwi_context->min_eqd = 0;
+ phwi_context->cur_eqd = 64;
be_cmd_fw_initialize(&phba->ctrl);
- status = beiscsi_create_eq(phba, phwi_context);
+
+ status = beiscsi_create_eqs(phba, phwi_context);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost, "EQ not created \n");
goto error;
}
- status = mgmt_check_supported_fw(ctrl);
+ status = be_mcc_queues_create(phba, phwi_context);
+ if (status != 0)
+ goto error;
+
+ status = mgmt_check_supported_fw(ctrl, phba);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost,
"Unsupported fw version \n");
goto error;
}
+ if (phba->fw_config.iscsi_features == 0x1)
+ ring_mode = 1;
+ else
+ ring_mode = 0;
status = mgmt_get_fw_config(ctrl, phba);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost,
@@ -2403,7 +2814,7 @@ static int hwi_init_port(struct beiscsi_hba *phba)
goto error;
}
- status = beiscsi_create_cq(phba, phwi_context);
+ status = beiscsi_create_cqs(phba, phwi_context);
if (status != 0) {
shost_printk(KERN_ERR, phba->shost, "CQ not created\n");
goto error;
@@ -2447,7 +2858,6 @@ error:
return -ENOMEM;
}
-
static int hwi_init_controller(struct beiscsi_hba *phba)
{
struct hwi_controller *phwi_ctrlr;
@@ -2530,6 +2940,18 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
phba->io_sgl_hndl_avbl = 0;
phba->eh_sgl_hndl_avbl = 0;
+
+ if (ring_mode) {
+ phba->sgl_hndl_array = kzalloc(sizeof(struct sgl_handle *) *
+ phba->params.icds_per_ctrl,
+ GFP_KERNEL);
+ if (!phba->sgl_hndl_array) {
+ shost_printk(KERN_ERR, phba->shost,
+ "Mem Alloc Failed. Failing to load\n");
+ return -ENOMEM;
+ }
+ }
+
mem_descr_sglh = phba->init_mem;
mem_descr_sglh += HWI_MEM_SGLH;
if (1 == mem_descr_sglh->num_elements) {
@@ -2537,6 +2959,8 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
phba->params.ios_per_ctrl,
GFP_KERNEL);
if (!phba->io_sgl_hndl_base) {
+ if (ring_mode)
+ kfree(phba->sgl_hndl_array);
shost_printk(KERN_ERR, phba->shost,
"Mem Alloc Failed. Failing to load\n");
return -ENOMEM;
@@ -2656,13 +3080,12 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba)
struct hwi_context_memory *phwi_context;
struct be_queue_info *eq;
u8 __iomem *addr;
- u32 reg;
+ u32 reg, i;
u32 enabled;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- eq = &phwi_context->be_eq.q;
addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg +
PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET);
reg = ioread32(addr);
@@ -2673,9 +3096,11 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba)
reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p \n", reg, addr);
iowrite32(reg, addr);
- SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id);
-
- hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
+ for (i = 0; i <= phba->num_cpus; i++) {
+ eq = &phwi_context->be_eq[i].q;
+ SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id);
+ hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
+ }
} else
shost_printk(KERN_WARNING, phba->shost,
"In hwi_enable_intr, Not Enabled \n");
@@ -2720,6 +3145,8 @@ static int beiscsi_init_port(struct beiscsi_hba *phba)
if (hba_setup_cid_tbls(phba)) {
shost_printk(KERN_ERR, phba->shost,
"Failed in hba_setup_cid_tbls\n");
+ if (ring_mode)
+ kfree(phba->sgl_hndl_array);
kfree(phba->io_sgl_hndl_base);
kfree(phba->eh_sgl_hndl_base);
goto do_cleanup_ctrlr;
@@ -2738,17 +3165,25 @@ static void hwi_purge_eq(struct beiscsi_hba *phba)
struct hwi_context_memory *phwi_context;
struct be_queue_info *eq;
struct be_eq_entry *eqe = NULL;
+ int i, eq_msix;
phwi_ctrlr = phba->phwi_ctrlr;
phwi_context = phwi_ctrlr->phwi_ctxt;
- eq = &phwi_context->be_eq.q;
- eqe = queue_tail_node(eq);
+ if (phba->msix_enabled)
+ eq_msix = 1;
+ else
+ eq_msix = 0;
- while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
- & EQE_VALID_MASK) {
- AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
- queue_tail_inc(eq);
+ for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
+ eq = &phwi_context->be_eq[i].q;
eqe = queue_tail_node(eq);
+
+ while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+ & EQE_VALID_MASK) {
+ AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+ queue_tail_inc(eq);
+ eqe = queue_tail_node(eq);
+ }
}
}
@@ -2762,6 +3197,8 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba)
"mgmt_epfw_cleanup FAILED \n");
hwi_cleanup(phba);
hwi_purge_eq(phba);
+ if (ring_mode)
+ kfree(phba->sgl_hndl_array);
kfree(phba->io_sgl_hndl_base);
kfree(phba->eh_sgl_hndl_base);
kfree(phba->cid_array);
@@ -2846,8 +3283,9 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
be_dws_le_to_cpu(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) <<
- DB_DEF_PDU_WRB_INDEX_SHIFT;
+ if (!ring_mode)
+ doorbell |= (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);
@@ -2856,7 +3294,7 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt,
int *index, int *age)
{
- *index = be32_to_cpu(itt) >> 16;
+ *index = (int)itt;
if (age)
*age = conn->session->age;
}
@@ -2885,15 +3323,13 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool,
GFP_KERNEL, &paddr);
-
if (!io_task->cmd_bhs)
return -ENOMEM;
-
io_task->bhs_pa.u.a64.address = paddr;
+ io_task->libiscsi_itt = (itt_t)task->itt;
io_task->pwrb_handle = alloc_wrb_handle(phba,
beiscsi_conn->beiscsi_conn_cid,
task->itt);
- io_task->pwrb_handle->pio_handle = task;
io_task->conn = beiscsi_conn;
task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr;
@@ -2905,7 +3341,6 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
spin_unlock(&phba->io_sgl_lock);
if (!io_task->psgl_handle)
goto free_hndls;
-
} else {
io_task->scsi_cmnd = NULL;
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
@@ -2932,8 +3367,18 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
goto free_hndls;
}
}
- itt = (itt_t) cpu_to_be32(((unsigned int)task->itt << 16) |
- (unsigned int)(io_task->psgl_handle->sgl_index));
+ itt = (itt_t) cpu_to_be32(((unsigned int)io_task->pwrb_handle->
+ wrb_index << 16) | (unsigned int)
+ (io_task->psgl_handle->sgl_index));
+ if (ring_mode) {
+ phba->sgl_hndl_array[io_task->psgl_handle->sgl_index -
+ phba->fw_config.iscsi_cid_start] =
+ io_task->psgl_handle;
+ io_task->psgl_handle->task = task;
+ io_task->psgl_handle->cid = beiscsi_conn->beiscsi_conn_cid;
+ } else
+ io_task->pwrb_handle->pio_handle = task;
+
io_task->cmd_bhs->iscsi_hdr.itt = itt;
return 0;
@@ -3006,7 +3451,6 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
io_task->bhs_len = sizeof(struct be_cmd_bhs);
if (writedir) {
- SE_DEBUG(DBG_LVL_4, " WRITE Command \t");
memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48);
AMAP_SET_BITS(struct amap_pdu_data_out, itt,
&io_task->cmd_bhs->iscsi_data_pdu,
@@ -3016,11 +3460,18 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
ISCSI_OPCODE_SCSI_DATA_OUT);
AMAP_SET_BITS(struct amap_pdu_data_out, final_bit,
&io_task->cmd_bhs->iscsi_data_pdu, 1);
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_WR_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_WR_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
} else {
- SE_DEBUG(DBG_LVL_4, "READ Command \t");
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_RD_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_RD_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
}
memcpy(&io_task->cmd_bhs->iscsi_data_pdu.
@@ -3045,7 +3496,8 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
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 &
+ if (!ring_mode)
+ 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;
@@ -3059,10 +3511,16 @@ static int beiscsi_mtask(struct iscsi_task *task)
struct iscsi_conn *conn = task->conn;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct beiscsi_hba *phba = beiscsi_conn->phba;
+ struct iscsi_session *session;
struct iscsi_wrb *pwrb = NULL;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_wrb_context *pwrb_context;
+ struct wrb_handle *pwrb_handle;
unsigned int doorbell = 0;
+ unsigned int i, cid;
struct iscsi_task *aborted_task;
+ cid = beiscsi_conn->beiscsi_conn_cid;
pwrb = io_task->pwrb_handle->pwrb;
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
be32_to_cpu(task->cmdsn));
@@ -3073,38 +3531,63 @@ static int beiscsi_mtask(struct iscsi_task *task)
switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
case ISCSI_OP_LOGIN:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, TGT_DM_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = TGT_DM_CMD;
+ else
+ 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);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_NOOP_OUT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_RD_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_RD_CMD);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_TEXT:
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_WR_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_WR_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_SCSI_TMFUNC:
- aborted_task = iscsi_itt_to_task(conn,
- ((struct iscsi_tm *)task->hdr)->rtt);
+ session = conn->session;
+ i = ((struct iscsi_tm *)task->hdr)->rtt;
+ phwi_ctrlr = phba->phwi_ctrlr;
+ pwrb_context = &phwi_ctrlr->wrb_context[cid];
+ pwrb_handle = pwrb_context->pwrb_handle_basestd[be32_to_cpu(i)
+ >> 16];
+ aborted_task = pwrb_handle->pio_handle;
if (!aborted_task)
return 0;
+
aborted_io_task = aborted_task->dd_data;
if (!aborted_io_task->scsi_cmnd)
return 0;
mgmt_invalidate_icds(phba,
aborted_io_task->psgl_handle->sgl_index,
- beiscsi_conn->beiscsi_conn_cid);
- AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_TMF_CMD);
+ cid);
+ if (ring_mode)
+ io_task->psgl_handle->type = INI_TMF_CMD;
+ else
+ AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+ INI_TMF_CMD);
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
hwi_write_buffer(pwrb, task);
break;
case ISCSI_OP_LOGOUT:
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
+ if (ring_mode)
+ io_task->psgl_handle->type = HWH_TYPE_LOGOUT;
+ else
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
HWH_TYPE_LOGOUT);
hwi_write_buffer(pwrb, task);
@@ -3122,8 +3605,9 @@ static int beiscsi_mtask(struct iscsi_task *task)
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 &
+ doorbell |= cid & DB_WRB_POST_CID_MASK;
+ if (!ring_mode)
+ 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);
@@ -3165,9 +3649,14 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
return beiscsi_iotask(task, sg, num_sg, xferlen, writedir);
}
+
static void beiscsi_remove(struct pci_dev *pcidev)
{
struct beiscsi_hba *phba = NULL;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ struct be_eq_obj *pbe_eq;
+ unsigned int i, msix_vec;
phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev);
if (!phba) {
@@ -3175,12 +3664,24 @@ static void beiscsi_remove(struct pci_dev *pcidev)
return;
}
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
hwi_disable_intr(phba);
- if (phba->pcidev->irq)
- free_irq(phba->pcidev->irq, phba);
+ if (phba->msix_enabled) {
+ for (i = 0; i <= phba->num_cpus; i++) {
+ msix_vec = phba->msix_entries[i].vector;
+ free_irq(msix_vec, &phwi_context->be_eq[i]);
+ }
+ } else
+ if (phba->pcidev->irq)
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msix(phba->pcidev);
destroy_workqueue(phba->wq);
if (blk_iopoll_enabled)
- blk_iopoll_disable(&phba->iopoll);
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ blk_iopoll_disable(&pbe_eq->iopoll);
+ }
beiscsi_clean_port(phba);
beiscsi_free_mem(phba);
@@ -3194,11 +3695,29 @@ static void beiscsi_remove(struct pci_dev *pcidev)
iscsi_host_free(phba->shost);
}
+static void beiscsi_msix_enable(struct beiscsi_hba *phba)
+{
+ int i, status;
+
+ for (i = 0; i <= phba->num_cpus; i++)
+ phba->msix_entries[i].entry = i;
+
+ status = pci_enable_msix(phba->pcidev, phba->msix_entries,
+ (phba->num_cpus + 1));
+ if (!status)
+ phba->msix_enabled = true;
+
+ return;
+}
+
static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
const struct pci_device_id *id)
{
struct beiscsi_hba *phba = NULL;
- int ret;
+ struct hwi_controller *phwi_ctrlr;
+ struct hwi_context_memory *phwi_context;
+ struct be_eq_obj *pbe_eq;
+ int ret, msix_vec, num_cpus, i;
ret = beiscsi_enable_pci(pcidev);
if (ret < 0) {
@@ -3213,8 +3732,18 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
" Failed in beiscsi_hba_alloc \n");
goto disable_pci;
}
+ SE_DEBUG(DBG_LVL_8, " phba = %p \n", phba);
pci_set_drvdata(pcidev, phba);
+ if (enable_msix)
+ num_cpus = find_num_cpus();
+ else
+ num_cpus = 1;
+ phba->num_cpus = num_cpus;
+ SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", phba->num_cpus);
+
+ if (enable_msix)
+ beiscsi_msix_enable(phba);
ret = be_ctrl_init(phba, pcidev);
if (ret) {
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
@@ -3235,7 +3764,7 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u",
phba->shost->host_no);
- phba->wq = create_singlethread_workqueue(phba->wq_name);
+ phba->wq = create_workqueue(phba->wq_name);
if (!phba->wq) {
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
"Failed to allocate work queue\n");
@@ -3244,11 +3773,16 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs);
+ phwi_ctrlr = phba->phwi_ctrlr;
+ phwi_context = phwi_ctrlr->phwi_ctxt;
if (blk_iopoll_enabled) {
- blk_iopoll_init(&phba->iopoll, be_iopoll_budget, be_iopoll);
- blk_iopoll_enable(&phba->iopoll);
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ blk_iopoll_init(&pbe_eq->iopoll, be_iopoll_budget,
+ be_iopoll);
+ blk_iopoll_enable(&pbe_eq->iopoll);
+ }
}
-
ret = beiscsi_init_irqs(phba);
if (ret < 0) {
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
@@ -3261,17 +3795,26 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
"Failed to hwi_enable_intr\n");
goto free_ctrlr;
}
-
SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED \n\n\n");
return 0;
free_ctrlr:
- if (phba->pcidev->irq)
- free_irq(phba->pcidev->irq, phba);
+ if (phba->msix_enabled) {
+ for (i = 0; i <= phba->num_cpus; i++) {
+ msix_vec = phba->msix_entries[i].vector;
+ free_irq(msix_vec, &phwi_context->be_eq[i]);
+ }
+ } else
+ if (phba->pcidev->irq)
+ free_irq(phba->pcidev->irq, phba);
+ pci_disable_msix(phba->pcidev);
free_blkenbld:
destroy_workqueue(phba->wq);
if (blk_iopoll_enabled)
- blk_iopoll_disable(&phba->iopoll);
+ for (i = 0; i < phba->num_cpus; i++) {
+ pbe_eq = &phwi_context->be_eq[i];
+ blk_iopoll_disable(&pbe_eq->iopoll);
+ }
free_twq:
beiscsi_clean_port(phba);
beiscsi_free_mem(phba);
@@ -3316,7 +3859,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
@@ -3351,6 +3894,7 @@ static struct pci_driver beiscsi_pci_driver = {
.id_table = beiscsi_pci_id_table
};
+
static int __init beiscsi_module_init(void)
{
int ret;
@@ -3373,6 +3917,7 @@ static int __init beiscsi_module_init(void)
"beiscsi pci driver.\n");
goto unregister_iscsi_transport;
}
+ ring_mode = 0;
return 0;
unregister_iscsi_transport:
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 53c9b70ac7a..25e6b208b77 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -21,11 +21,9 @@
#ifndef _BEISCSI_MAIN_
#define _BEISCSI_MAIN_
-
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/in.h>
-#include <linux/blk-iopoll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -35,12 +33,8 @@
#include <scsi/scsi_transport_iscsi.h>
#include "be.h"
-
-
-
#define DRV_NAME "be2iscsi"
#define BUILD_STR "2.0.527.0"
-
#define BE_NAME "ServerEngines BladeEngine2" \
"Linux iSCSI Driver version" BUILD_STR
#define DRV_DESC BE_NAME " " "Driver"
@@ -49,6 +43,8 @@
#define BE_DEVICE_ID1 0x212
#define OC_DEVICE_ID1 0x702
#define OC_DEVICE_ID2 0x703
+#define OC_DEVICE_ID3 0x712
+#define OC_DEVICE_ID4 0x222
#define BE2_MAX_SESSIONS 64
#define BE2_CMDS_PER_CXN 128
@@ -63,6 +59,7 @@
#define BE2_IO_DEPTH \
(BE2_MAX_ICDS / 2 - (BE2_LOGOUTS + BE2_TMFS + BE2_NOPOUT_REQ))
+#define MAX_CPUS 31
#define BEISCSI_SGLIST_ELEMENTS BE2_SGE
#define BEISCSI_MAX_CMNDS 1024 /* Max IO's per Ctrlr sht->can_queue */
@@ -79,7 +76,7 @@
#define BE_SENSE_INFO_SIZE 258
#define BE_ISCSI_PDU_HEADER_SIZE 64
#define BE_MIN_MEM_SIZE 16384
-
+#define MAX_CMD_SZ 65536
#define IIOC_SCSI_DATA 0x05 /* Write Operation */
#define DBG_LVL 0x00000001
@@ -100,6 +97,8 @@ do { \
} \
} while (0);
+#define BE_ADAPTER_UP 0x00000000
+#define BE_ADAPTER_LINK_DOWN 0x00000001
/**
* hardware needs the async PDU buffers to be posted in multiples of 8
* So have atleast 8 of them by default
@@ -160,21 +159,19 @@ do { \
enum be_mem_enum {
HWI_MEM_ADDN_CONTEXT,
- HWI_MEM_CQ,
- HWI_MEM_EQ,
HWI_MEM_WRB,
HWI_MEM_WRBH,
- HWI_MEM_SGLH, /* 5 */
+ HWI_MEM_SGLH,
HWI_MEM_SGE,
- HWI_MEM_ASYNC_HEADER_BUF,
+ HWI_MEM_ASYNC_HEADER_BUF, /* 5 */
HWI_MEM_ASYNC_DATA_BUF,
HWI_MEM_ASYNC_HEADER_RING,
- HWI_MEM_ASYNC_DATA_RING, /* 10 */
+ HWI_MEM_ASYNC_DATA_RING,
HWI_MEM_ASYNC_HEADER_HANDLE,
- HWI_MEM_ASYNC_DATA_HANDLE,
+ HWI_MEM_ASYNC_DATA_HANDLE, /* 10 */
HWI_MEM_ASYNC_PDU_CONTEXT,
ISCSI_MEM_GLOBAL_HEADER,
- SE_MEM_MAX /* 15 */
+ SE_MEM_MAX
};
struct be_bus_address32 {
@@ -212,6 +209,9 @@ struct be_mem_descriptor {
struct sgl_handle {
unsigned int sgl_index;
+ unsigned int type;
+ unsigned int cid;
+ struct iscsi_task *task;
struct iscsi_sge *pfrag;
};
@@ -274,13 +274,17 @@ struct beiscsi_hba {
struct pci_dev *pcidev;
unsigned int state;
unsigned short asic_revision;
- struct blk_iopoll iopoll;
+ unsigned int num_cpus;
+ unsigned int nxt_cqid;
+ struct msix_entry msix_entries[MAX_CPUS + 1];
+ bool msix_enabled;
struct be_mem_descriptor *init_mem;
unsigned short io_sgl_alloc_index;
unsigned short io_sgl_free_index;
unsigned short io_sgl_hndl_avbl;
struct sgl_handle **io_sgl_hndl_base;
+ struct sgl_handle **sgl_hndl_array;
unsigned short eh_sgl_alloc_index;
unsigned short eh_sgl_free_index;
@@ -315,6 +319,7 @@ struct beiscsi_hba {
unsigned short cid_alloc;
unsigned short cid_free;
unsigned short avlbl_cids;
+ unsigned short iscsi_features;
spinlock_t cid_lock;
} fw_config;
@@ -343,6 +348,7 @@ struct beiscsi_conn {
unsigned short login_in_progress;
struct sgl_handle *plogin_sgl_handle;
struct beiscsi_session *beiscsi_sess;
+ struct iscsi_task *task;
};
/* This structure is used by the chip */
@@ -390,7 +396,7 @@ struct beiscsi_io_task {
unsigned int flags;
unsigned short cid;
unsigned short header_len;
-
+ itt_t libiscsi_itt;
struct be_cmd_bhs *cmd_bhs;
struct be_bus_address bhs_pa;
unsigned short bhs_len;
@@ -599,7 +605,6 @@ struct amap_cq_db {
void beiscsi_process_eq(struct beiscsi_hba *phba);
-
struct iscsi_wrb {
u32 dw[16];
} __packed;
@@ -820,10 +825,12 @@ struct wrb_handle {
};
struct hwi_context_memory {
- struct be_eq_obj be_eq;
- struct be_queue_info be_cq;
- struct be_queue_info be_mcc_cq;
- struct be_queue_info be_mcc;
+ /* Adaptive interrupt coalescing (AIC) info */
+ u16 min_eqd; /* in usecs */
+ 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_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 12e644fc746..79c2bd525a8 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.c
+++ b/drivers/scsi/be2iscsi/be_mgmt.c
@@ -35,7 +35,6 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
-
status = be_mbox_notify(ctrl);
if (!status) {
struct be_fw_cfg *pfw_cfg;
@@ -58,7 +57,8 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
return status;
}
-unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
+unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba)
{
struct be_dma_mem nonemb_cmd;
struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
@@ -85,7 +85,6 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd.size);
-
status = be_mbox_notify(ctrl);
if (!status) {
struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va;
@@ -95,21 +94,25 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
resp->params.hba_attribs.firmware_version_string);
SE_DEBUG(DBG_LVL_8,
"Developer Build, not performing version check...\n");
-
+ phba->fw_config.iscsi_features =
+ resp->params.hba_attribs.iscsi_features;
+ SE_DEBUG(DBG_LVL_8, " phba->fw_config.iscsi_features = %d\n",
+ phba->fw_config.iscsi_features);
} else
SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n");
+ spin_unlock(&ctrl->mbox_lock);
if (nonemb_cmd.va)
pci_free_consistent(ctrl->pdev, nonemb_cmd.size,
nonemb_cmd.va, nonemb_cmd.dma);
- spin_unlock(&ctrl->mbox_lock);
return status;
}
+
unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct iscsi_cleanup_req *req = embedded_payload(wrb);
int status = 0;
@@ -124,7 +127,7 @@ unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
req->hdr_ring_id = 0;
req->data_ring_id = 0;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
shost_printk(KERN_WARNING, phba->shost,
" mgmt_epfw_cleanup , FAILED\n");
@@ -137,7 +140,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
{
struct be_dma_mem nonemb_cmd;
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct be_sge *sge = nonembedded_sgl(wrb);
struct invalidate_commands_params_in *req;
int status = 0;
@@ -169,7 +172,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
sge->len = cpu_to_le32(nonemb_cmd.size);
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n");
spin_unlock(&ctrl->mbox_lock);
@@ -186,7 +189,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
unsigned short savecfg_flag)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct iscsi_invalidate_connection_params_in *req =
embedded_payload(wrb);
int status = 0;
@@ -205,7 +208,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
else
req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE;
req->save_cfg = savecfg_flag;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n");
@@ -217,7 +220,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
unsigned short cid, unsigned int upload_flag)
{
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct tcp_upload_params_in *req = embedded_payload(wrb);
int status = 0;
@@ -229,7 +232,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
OPCODE_COMMON_TCP_UPLOAD, sizeof(*req));
req->id = (unsigned short)cid;
req->upload_type = (unsigned char)upload_flag;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (status)
SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n");
spin_unlock(&ctrl->mbox_lock);
@@ -245,13 +248,14 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr;
struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr;
struct be_ctrl_info *ctrl = &phba->ctrl;
- struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
struct tcp_connect_and_offload_in *req = embedded_payload(wrb);
unsigned short def_hdr_id;
unsigned short def_data_id;
struct phys_addr template_address = { 0, 0 };
struct phys_addr *ptemplate_address;
int status = 0;
+ unsigned int i;
unsigned short cid = beiscsi_ep->ep_cid;
phwi_ctrlr = phba->phwi_ctrlr;
@@ -296,14 +300,18 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
}
req->cid = cid;
- req->cq_id = phwi_context->be_cq.id;
+ i = phba->nxt_cqid++;
+ if (phba->nxt_cqid == phba->num_cpus)
+ phba->nxt_cqid = 0;
+ req->cq_id = phwi_context->be_cq[i].id;
+ SE_DEBUG(DBG_LVL_8, "i=%d cq_id=%d \n", i, req->cq_id);
req->defq_id = def_hdr_id;
req->hdr_ring_id = def_hdr_id;
req->data_ring_id = def_data_id;
req->do_offload = 1;
req->dataout_template_pa.lo = ptemplate_address->lo;
req->dataout_template_pa.hi = ptemplate_address->hi;
- status = be_mbox_notify(ctrl);
+ status = be_mcc_notify_wait(phba);
if (!status) {
struct iscsi_endpoint *ep;
struct tcp_connect_and_offload_out *ptcpcnct_out =
@@ -311,7 +319,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
ep = phba->ep_array[ptcpcnct_out->cid];
beiscsi_ep = ep->dd_data;
- beiscsi_ep->fw_handle = 0;
+ beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
beiscsi_ep->cid_vld = 1;
SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
} else
@@ -319,3 +327,30 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
spin_unlock(&ctrl->mbox_lock);
return status;
}
+
+int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr)
+{
+ struct be_ctrl_info *ctrl = &phba->ctrl;
+ struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
+ struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb);
+ int status;
+
+ SE_DEBUG(DBG_LVL_8, "In be_cmd_get_mac_addr\n");
+ spin_lock(&ctrl->mbox_lock);
+ memset(wrb, 0, sizeof(*wrb));
+ be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+ be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+ OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
+ sizeof(*req));
+
+ status = be_mcc_notify_wait(phba);
+ if (!status) {
+ struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb);
+
+ memcpy(mac_addr, resp->mac_address, ETH_ALEN);
+ }
+
+ spin_unlock(&ctrl->mbox_lock);
+ return status;
+}
+
diff --git a/drivers/scsi/be2iscsi/be_mgmt.h b/drivers/scsi/be2iscsi/be_mgmt.h
index 00e816ee807..24eaff923f8 100644
--- a/drivers/scsi/be2iscsi/be_mgmt.h
+++ b/drivers/scsi/be2iscsi/be_mgmt.h
@@ -175,7 +175,9 @@ struct mgmt_hba_attributes {
u8 phy_port;
u32 firmware_post_status;
u32 hba_mtu[8];
- u32 future_u32[4];
+ u8 iscsi_features;
+ u8 future_u8[3];
+ u32 future_u32[3];
} __packed;
struct mgmt_controller_attributes {
@@ -246,4 +248,8 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
unsigned short cid,
unsigned short issue_reset,
unsigned short savecfg_flag);
+
+unsigned char mgmt_fw_cmd(struct be_ctrl_info *ctrl,
+ struct beiscsi_hba *phba,
+ char *buf, unsigned int len);
#endif
diff --git a/drivers/scsi/bfa/bfa_cb_ioim_macros.h b/drivers/scsi/bfa/bfa_cb_ioim_macros.h
index 0050c838c35..961fe439daa 100644
--- a/drivers/scsi/bfa/bfa_cb_ioim_macros.h
+++ b/drivers/scsi/bfa/bfa_cb_ioim_macros.h
@@ -51,7 +51,7 @@ bfad_int_to_lun(u32 luno)
lun.bfa_lun = 0;
lun.scsi_lun[0] = bfa_os_htons(luno);
- return (lun.bfa_lun);
+ return lun.bfa_lun;
}
/**
@@ -68,7 +68,7 @@ bfa_cb_ioim_get_cdb(struct bfad_ioim_s *dio)
{
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
- return ((u8 *) cmnd->cmnd);
+ return (u8 *) cmnd->cmnd;
}
/**
@@ -97,7 +97,7 @@ bfa_cb_ioim_get_size(struct bfad_ioim_s *dio)
{
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
- return (scsi_bufflen(cmnd));
+ return scsi_bufflen(cmnd);
}
/**
@@ -129,7 +129,7 @@ bfa_cb_ioim_get_sgaddr(struct bfad_ioim_s *dio, int sgeid)
sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid;
addr = (u64) sg_dma_address(sge);
- return (*(union bfi_addr_u *) &addr);
+ return *((union bfi_addr_u *) &addr);
}
static inline u32
@@ -197,7 +197,7 @@ bfa_cb_ioim_get_cdblen(struct bfad_ioim_s *dio)
{
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
- return (cmnd->cmd_len);
+ return cmnd->cmd_len;
}
diff --git a/drivers/scsi/bfa/bfa_cee.c b/drivers/scsi/bfa/bfa_cee.c
index 7a959c34e78..2b917792c6b 100644
--- a/drivers/scsi/bfa/bfa_cee.c
+++ b/drivers/scsi/bfa/bfa_cee.c
@@ -228,7 +228,7 @@ bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status)
u32
bfa_cee_meminfo(void)
{
- return (bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo());
+ return bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo();
}
/**
diff --git a/drivers/scsi/bfa/bfa_csdebug.c b/drivers/scsi/bfa/bfa_csdebug.c
index 1b71d349451..caeb1143a4e 100644
--- a/drivers/scsi/bfa/bfa_csdebug.c
+++ b/drivers/scsi/bfa/bfa_csdebug.c
@@ -47,12 +47,12 @@ bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe)
tqe = bfa_q_next(q);
while (tqe != q) {
if (tqe == qe)
- return (1);
+ return 1;
tqe = bfa_q_next(tqe);
if (tqe == NULL)
break;
}
- return (0);
+ return 0;
}
diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c
index 401babe3494..790c945aeae 100644
--- a/drivers/scsi/bfa/bfa_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcpim.c
@@ -131,7 +131,7 @@ bfa_fcpim_path_tov_get(struct bfa_s *bfa)
{
struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
- return (fcpim->path_tov / 1000);
+ return fcpim->path_tov / 1000;
}
bfa_status_t
@@ -169,7 +169,7 @@ bfa_fcpim_qdepth_get(struct bfa_s *bfa)
{
struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
- return (fcpim->q_depth);
+ return fcpim->q_depth;
}
diff --git a/drivers/scsi/bfa/bfa_fcpim_priv.h b/drivers/scsi/bfa/bfa_fcpim_priv.h
index 153206cfb37..5cf418460f7 100644
--- a/drivers/scsi/bfa/bfa_fcpim_priv.h
+++ b/drivers/scsi/bfa/bfa_fcpim_priv.h
@@ -35,7 +35,7 @@
#define BFA_FCPIM_PATHTOV_MAX (90 * 1000) /* in millisecs */
#define bfa_fcpim_stats(__fcpim, __stats) \
- (__fcpim)->stats.__stats ++
+ ((__fcpim)->stats.__stats++)
struct bfa_fcpim_mod_s {
struct bfa_s *bfa;
@@ -143,7 +143,7 @@ struct bfa_itnim_s {
struct bfa_itnim_hal_stats_s stats;
};
-#define bfa_itnim_is_online(_itnim) (_itnim)->is_online
+#define bfa_itnim_is_online(_itnim) ((_itnim)->is_online)
#define BFA_FCPIM_MOD(_hal) (&(_hal)->modules.fcpim_mod)
#define BFA_IOIM_FROM_TAG(_fcpim, _iotag) \
(&fcpim->ioim_arr[_iotag])
diff --git a/drivers/scsi/bfa/bfa_fcport.c b/drivers/scsi/bfa/bfa_fcport.c
index 992435987de..aef648b55df 100644
--- a/drivers/scsi/bfa/bfa_fcport.c
+++ b/drivers/scsi/bfa/bfa_fcport.c
@@ -388,32 +388,29 @@ bfa_pport_sm_linkup(struct bfa_pport_s *pport, enum bfa_pport_sm_event event)
bfa_pport_callback(pport, BFA_PPORT_LINKDOWN);
bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL,
BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown");
- if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+ if (BFA_PORT_IS_DISABLED(pport->bfa))
bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
- } else {
+ else
bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
- }
break;
case BFA_PPORT_SM_STOP:
bfa_sm_set_state(pport, bfa_pport_sm_stopped);
bfa_pport_reset_linkinfo(pport);
- if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+ if (BFA_PORT_IS_DISABLED(pport->bfa))
bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
- } else {
+ else
bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
- }
break;
case BFA_PPORT_SM_HWFAIL:
bfa_sm_set_state(pport, bfa_pport_sm_iocdown);
bfa_pport_reset_linkinfo(pport);
bfa_pport_callback(pport, BFA_PPORT_LINKDOWN);
- if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+ if (BFA_PORT_IS_DISABLED(pport->bfa))
bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
- } else {
+ else
bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
- }
break;
default:
@@ -999,10 +996,10 @@ bfa_pport_enable(struct bfa_s *bfa)
struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
if (pport->diag_busy)
- return (BFA_STATUS_DIAG_BUSY);
+ return BFA_STATUS_DIAG_BUSY;
else if (bfa_sm_cmp_state
(BFA_PORT_MOD(bfa), bfa_pport_sm_disabling_qwait))
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_ENABLE);
return BFA_STATUS_OK;
@@ -1032,7 +1029,7 @@ bfa_pport_cfg_speed(struct bfa_s *bfa, enum bfa_pport_speed speed)
pport->cfg.speed = speed;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1068,7 +1065,7 @@ bfa_pport_cfg_topology(struct bfa_s *bfa, enum bfa_pport_topology topology)
}
pport->cfg.topology = topology;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1094,7 +1091,7 @@ bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa)
pport->cfg.cfg_hardalpa = BFA_TRUE;
pport->cfg.hardalpa = alpa;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -1106,7 +1103,7 @@ bfa_pport_clr_hardalpa(struct bfa_s *bfa)
bfa_trc(bfa, pport->cfg.hardalpa);
pport->cfg.cfg_hardalpa = BFA_FALSE;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_boolean_t
@@ -1138,16 +1135,16 @@ bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxfrsize)
* with in range
*/
if ((maxfrsize > FC_MAX_PDUSZ) || (maxfrsize < FC_MIN_PDUSZ))
- return (BFA_STATUS_INVLD_DFSZ);
+ return BFA_STATUS_INVLD_DFSZ;
/*
* power of 2, if not the max frame size of 2112
*/
if ((maxfrsize != FC_MAX_PDUSZ) && (maxfrsize & (maxfrsize - 1)))
- return (BFA_STATUS_INVLD_DFSZ);
+ return BFA_STATUS_INVLD_DFSZ;
pport->cfg.maxfrsize = maxfrsize;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
u16
@@ -1415,7 +1412,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
if (port->stats_busy) {
bfa_trc(bfa, port->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
port->stats_busy = BFA_TRUE;
@@ -1427,7 +1424,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
bfa_timer_start(bfa, &port->timer, bfa_port_stats_timeout, port,
BFA_PORT_STATS_TOV);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -1437,7 +1434,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
if (port->stats_busy) {
bfa_trc(bfa, port->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
port->stats_busy = BFA_TRUE;
@@ -1448,7 +1445,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port,
BFA_PORT_STATS_TOV);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -1515,7 +1512,7 @@ bfa_pport_get_qos_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
/*
* QoS stats is embedded in port stats
*/
- return (bfa_pport_get_stats(bfa, stats, cbfn, cbarg));
+ return bfa_pport_get_stats(bfa, stats, cbfn, cbarg);
}
bfa_status_t
@@ -1525,7 +1522,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
if (port->stats_busy) {
bfa_trc(bfa, port->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
port->stats_busy = BFA_TRUE;
@@ -1536,7 +1533,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port,
BFA_PORT_STATS_TOV);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1545,7 +1542,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
bfa_status_t
bfa_pport_trunk_disable(struct bfa_s *bfa)
{
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_boolean_t
@@ -1562,8 +1559,8 @@ bfa_pport_is_disabled(struct bfa_s *bfa)
{
struct bfa_pport_s *port = BFA_PORT_MOD(bfa);
- return (bfa_sm_to_state(hal_pport_sm_table, port->sm) ==
- BFA_PPORT_ST_DISABLED);
+ return bfa_sm_to_state(hal_pport_sm_table, port->sm) ==
+ BFA_PPORT_ST_DISABLED;
}
@@ -1572,7 +1569,7 @@ bfa_pport_is_ratelim(struct bfa_s *bfa)
{
struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
-return (pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE);
+ return pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE;
}
@@ -1620,7 +1617,7 @@ bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, enum bfa_pport_speed speed)
pport->cfg.trl_def_speed = speed;
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
@@ -1632,7 +1629,7 @@ bfa_pport_get_ratelim_speed(struct bfa_s *bfa)
struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
bfa_trc(bfa, pport->cfg.trl_def_speed);
- return (pport->cfg.trl_def_speed);
+ return pport->cfg.trl_def_speed;
}
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index 8975ed041dc..c7ab257f10a 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -568,11 +568,10 @@ bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port)
__port_action[port->fabric->fab_type].offline(port);
- if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) {
+ if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE)
bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DISCONNECT);
- } else {
+ else
bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_OFFLINE);
- }
bfa_fcb_port_offline(port->fcs->bfad, port->port_cfg.roles,
port->fabric->vf_drv,
(port->vport == NULL) ? NULL : port->vport->vport_drv);
@@ -777,7 +776,7 @@ bfa_fcs_port_get_rport_by_pwwn(struct bfa_fcs_port_s *port, wwn_t pwwn)
}
bfa_trc(port->fcs, pwwn);
- return (NULL);
+ return NULL;
}
/**
@@ -796,7 +795,7 @@ bfa_fcs_port_get_rport_by_nwwn(struct bfa_fcs_port_s *port, wwn_t nwwn)
}
bfa_trc(port->fcs, nwwn);
- return (NULL);
+ return NULL;
}
/**
@@ -870,7 +869,7 @@ bfa_fcs_port_lip(struct bfa_fcs_port_s *port)
bfa_boolean_t
bfa_fcs_port_is_online(struct bfa_fcs_port_s *port)
{
- return (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online));
+ return bfa_sm_cmp_state(port, bfa_fcs_port_sm_online);
}
/**
diff --git a/drivers/scsi/bfa/bfa_fcxp.c b/drivers/scsi/bfa/bfa_fcxp.c
index 4754a0e9006..cf0ad678268 100644
--- a/drivers/scsi/bfa/bfa_fcxp.c
+++ b/drivers/scsi/bfa/bfa_fcxp.c
@@ -199,7 +199,7 @@ bfa_fcxp_get(struct bfa_fcxp_mod_s *fm)
if (fcxp)
list_add_tail(&fcxp->qe, &fm->fcxp_active_q);
- return (fcxp);
+ return fcxp;
}
static void
@@ -503,7 +503,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa));
if (fcxp == NULL)
- return (NULL);
+ return NULL;
bfa_trc(bfa, fcxp->fcxp_tag);
@@ -568,7 +568,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
}
}
- return (fcxp);
+ return fcxp;
}
/**
@@ -709,7 +709,7 @@ bfa_status_t
bfa_fcxp_abort(struct bfa_fcxp_s *fcxp)
{
bfa_assert(0);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
void
diff --git a/drivers/scsi/bfa/bfa_intr.c b/drivers/scsi/bfa/bfa_intr.c
index 0ca125712a0..b36540e4ed7 100644
--- a/drivers/scsi/bfa/bfa_intr.c
+++ b/drivers/scsi/bfa/bfa_intr.c
@@ -59,7 +59,7 @@ bfa_intx(struct bfa_s *bfa)
qintr = intr & __HFN_INT_RME_MASK;
bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr);
- for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue ++) {
+ for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) {
if (intr & (__HFN_INT_RME_Q0 << queue))
bfa_msix_rspq(bfa, queue & (BFI_IOC_MAX_CQS - 1));
}
diff --git a/drivers/scsi/bfa/bfa_intr_priv.h b/drivers/scsi/bfa/bfa_intr_priv.h
index 8ce6e6b105c..5fc301cf4d1 100644
--- a/drivers/scsi/bfa/bfa_intr_priv.h
+++ b/drivers/scsi/bfa/bfa_intr_priv.h
@@ -26,9 +26,9 @@ void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m);
void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func);
-#define bfa_reqq_pi(__bfa, __reqq) (__bfa)->iocfc.req_cq_pi[__reqq]
+#define bfa_reqq_pi(__bfa, __reqq) ((__bfa)->iocfc.req_cq_pi[__reqq])
#define bfa_reqq_ci(__bfa, __reqq) \
- *(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva)
+ (*(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva))
#define bfa_reqq_full(__bfa, __reqq) \
(((bfa_reqq_pi(__bfa, __reqq) + 1) & \
@@ -50,14 +50,16 @@ void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func);
} while (0)
#define bfa_rspq_pi(__bfa, __rspq) \
- *(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva)
+ (*(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva))
-#define bfa_rspq_ci(__bfa, __rspq) (__bfa)->iocfc.rsp_cq_ci[__rspq]
+#define bfa_rspq_ci(__bfa, __rspq) ((__bfa)->iocfc.rsp_cq_ci[__rspq])
#define bfa_rspq_elem(__bfa, __rspq, __ci) \
- &((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci]
+ (&((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci])
-#define CQ_INCR(__index, __size) \
- (__index)++; (__index) &= ((__size) - 1)
+#define CQ_INCR(__index, __size) do { \
+ (__index)++; \
+ (__index) &= ((__size) - 1); \
+} while (0)
/**
* Queue element to wait for room in request queue. FIFO order is
@@ -94,7 +96,7 @@ bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg),
wqe->cbarg = cbarg;
}
-#define bfa_reqq(__bfa, __reqq) &(__bfa)->reqq_waitq[__reqq]
+#define bfa_reqq(__bfa, __reqq) (&(__bfa)->reqq_waitq[__reqq])
/**
* static inline void
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 149348934ce..397d7e9eade 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -51,7 +51,7 @@ BFA_TRC_FILE(HAL, IOC);
(sizeof(struct bfa_trc_mod_s) - \
BFA_TRC_MAX * sizeof(struct bfa_trc_s)))
#define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn))
-#define bfa_ioc_stats(_ioc, _stats) (_ioc)->stats._stats ++
+#define bfa_ioc_stats(_ioc, _stats) ((_ioc)->stats._stats++)
#define BFA_FLASH_CHUNK_NO(off) (off / BFI_FLASH_CHUNK_SZ_WORDS)
#define BFA_FLASH_OFFSET_IN_CHUNK(off) (off % BFI_FLASH_CHUNK_SZ_WORDS)
@@ -1953,8 +1953,8 @@ bfa_ioc_error_isr(struct bfa_ioc_s *ioc)
bfa_boolean_t
bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
{
- return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling)
- || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled));
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling)
+ || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled);
}
/**
@@ -1963,9 +1963,9 @@ bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
bfa_boolean_t
bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc)
{
- return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset)
+ return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset)
|| bfa_fsm_cmp_state(ioc, bfa_ioc_sm_fwcheck)
- || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch));
+ || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch);
}
#define bfa_ioc_state_disabled(__sm) \
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index 58efd4b1314..7c30f05ab13 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -179,16 +179,16 @@ struct bfa_ioc_s {
struct bfa_ioc_mbox_mod_s mbox_mod;
};
-#define bfa_ioc_pcifn(__ioc) (__ioc)->pcidev.pci_func
-#define bfa_ioc_devid(__ioc) (__ioc)->pcidev.device_id
-#define bfa_ioc_bar0(__ioc) (__ioc)->pcidev.pci_bar_kva
+#define bfa_ioc_pcifn(__ioc) ((__ioc)->pcidev.pci_func)
+#define bfa_ioc_devid(__ioc) ((__ioc)->pcidev.device_id)
+#define bfa_ioc_bar0(__ioc) ((__ioc)->pcidev.pci_bar_kva)
#define bfa_ioc_portid(__ioc) ((__ioc)->port_id)
#define bfa_ioc_fetch_stats(__ioc, __stats) \
- ((__stats)->drv_stats) = (__ioc)->stats
+ (((__stats)->drv_stats) = (__ioc)->stats)
#define bfa_ioc_clr_stats(__ioc) \
bfa_os_memset(&(__ioc)->stats, 0, sizeof((__ioc)->stats))
-#define bfa_ioc_maxfrsize(__ioc) (__ioc)->attr->maxfrsize
-#define bfa_ioc_rx_bbcredit(__ioc) (__ioc)->attr->rx_bbcredit
+#define bfa_ioc_maxfrsize(__ioc) ((__ioc)->attr->maxfrsize)
+#define bfa_ioc_rx_bbcredit(__ioc) ((__ioc)->attr->rx_bbcredit)
#define bfa_ioc_speed_sup(__ioc) \
BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop)
diff --git a/drivers/scsi/bfa/bfa_iocfc.c b/drivers/scsi/bfa/bfa_iocfc.c
index 12350b022d6..d7ab792a9e5 100644
--- a/drivers/scsi/bfa/bfa_iocfc.c
+++ b/drivers/scsi/bfa/bfa_iocfc.c
@@ -794,7 +794,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats,
if (iocfc->stats_busy) {
bfa_trc(bfa, iocfc->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
iocfc->stats_busy = BFA_TRUE;
@@ -804,7 +804,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats,
bfa_iocfc_stats_query(bfa);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
bfa_status_t
@@ -814,7 +814,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg)
if (iocfc->stats_busy) {
bfa_trc(bfa, iocfc->stats_busy);
- return (BFA_STATUS_DEVBUSY);
+ return BFA_STATUS_DEVBUSY;
}
iocfc->stats_busy = BFA_TRUE;
@@ -822,7 +822,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg)
iocfc->stats_cbarg = cbarg;
bfa_iocfc_stats_clear(bfa);
- return (BFA_STATUS_OK);
+ return BFA_STATUS_OK;
}
/**
diff --git a/drivers/scsi/bfa/bfa_iocfc.h b/drivers/scsi/bfa/bfa_iocfc.h
index 7ad177ed4cf..ce9a830a420 100644
--- a/drivers/scsi/bfa/bfa_iocfc.h
+++ b/drivers/scsi/bfa/bfa_iocfc.h
@@ -107,13 +107,13 @@ struct bfa_iocfc_s {
#define bfa_lpuid(__bfa) bfa_ioc_portid(&(__bfa)->ioc)
#define bfa_msix_init(__bfa, __nvecs) \
- (__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs)
+ ((__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs))
#define bfa_msix_install(__bfa) \
- (__bfa)->iocfc.hwif.hw_msix_install(__bfa)
+ ((__bfa)->iocfc.hwif.hw_msix_install(__bfa))
#define bfa_msix_uninstall(__bfa) \
- (__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa)
+ ((__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa))
#define bfa_isr_mode_set(__bfa, __msix) \
- (__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix)
+ ((__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix))
#define bfa_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec) \
(__bfa)->iocfc.hwif.hw_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec)
diff --git a/drivers/scsi/bfa/bfa_ioim.c b/drivers/scsi/bfa/bfa_ioim.c
index 7ae2552e1e1..f81d359b708 100644
--- a/drivers/scsi/bfa/bfa_ioim.c
+++ b/drivers/scsi/bfa/bfa_ioim.c
@@ -105,13 +105,13 @@ bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event)
bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
list_del(&ioim->qe);
list_add_tail(&ioim->qe,
- &ioim->fcpim->ioim_comp_q);
+ &ioim->fcpim->ioim_comp_q);
bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
__bfa_cb_ioim_pathtov, ioim);
} else {
list_del(&ioim->qe);
list_add_tail(&ioim->qe,
- &ioim->itnim->pending_q);
+ &ioim->itnim->pending_q);
}
break;
}
diff --git a/drivers/scsi/bfa/bfa_itnim.c b/drivers/scsi/bfa/bfa_itnim.c
index 4d5c61a4f85..eabf7d38bd0 100644
--- a/drivers/scsi/bfa/bfa_itnim.c
+++ b/drivers/scsi/bfa/bfa_itnim.c
@@ -1029,7 +1029,7 @@ bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn)
bfa_stats(itnim, creates);
bfa_sm_send_event(itnim, BFA_ITNIM_SM_CREATE);
- return (itnim);
+ return itnim;
}
void
@@ -1061,7 +1061,7 @@ bfa_itnim_offline(struct bfa_itnim_s *itnim)
bfa_boolean_t
bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
{
- return (
+ return
itnim->fcpim->path_tov && itnim->iotov_active &&
(bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwcreate) ||
bfa_sm_cmp_state(itnim, bfa_itnim_sm_sler) ||
@@ -1069,7 +1069,7 @@ bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwdelete) ||
bfa_sm_cmp_state(itnim, bfa_itnim_sm_offline) ||
bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable))
-);
+ ;
}
void
diff --git a/drivers/scsi/bfa/bfa_log.c b/drivers/scsi/bfa/bfa_log.c
index c2735e55cf0..e7514016c9c 100644
--- a/drivers/scsi/bfa/bfa_log.c
+++ b/drivers/scsi/bfa/bfa_log.c
@@ -231,9 +231,9 @@ bfa_log_get_level(struct bfa_log_mod_s *log_mod, int mod_id)
return BFA_LOG_INVALID;
if (log_mod)
- return (log_mod->log_level[mod_id]);
+ return log_mod->log_level[mod_id];
else
- return (bfa_log_info[mod_id].level);
+ return bfa_log_info[mod_id].level;
}
enum bfa_log_severity
diff --git a/drivers/scsi/bfa/bfa_port_priv.h b/drivers/scsi/bfa/bfa_port_priv.h
index 4b97e275990..51f698a06b6 100644
--- a/drivers/scsi/bfa/bfa_port_priv.h
+++ b/drivers/scsi/bfa/bfa_port_priv.h
@@ -59,8 +59,8 @@ struct bfa_pport_s {
u8 *stats_kva;
u64 stats_pa;
union bfa_pport_stats_u *stats; /* pport stats */
- u32 mypid : 24;
- u32 rsvd_b : 8;
+ u32 mypid:24;
+ u32 rsvd_b:8;
struct bfa_timer_s timer; /* timer */
union bfa_pport_stats_u *stats_ret;
/* driver stats location */
diff --git a/drivers/scsi/bfa/bfa_rport.c b/drivers/scsi/bfa/bfa_rport.c
index 16da77a8db2..3e1990a7425 100644
--- a/drivers/scsi/bfa/bfa_rport.c
+++ b/drivers/scsi/bfa/bfa_rport.c
@@ -677,7 +677,7 @@ bfa_rport_alloc(struct bfa_rport_mod_s *mod)
if (rport)
list_add_tail(&rport->qe, &mod->rp_active_q);
- return (rport);
+ return rport;
}
static void
@@ -834,7 +834,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv)
rp = bfa_rport_alloc(BFA_RPORT_MOD(bfa));
if (rp == NULL)
- return (NULL);
+ return NULL;
rp->bfa = bfa;
rp->rport_drv = rport_drv;
@@ -843,7 +843,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv)
bfa_assert(bfa_sm_cmp_state(rp, bfa_rport_sm_uninit));
bfa_sm_send_event(rp, BFA_RPORT_SM_CREATE);
- return (rp);
+ return rp;
}
void
diff --git a/drivers/scsi/bfa/bfa_tskim.c b/drivers/scsi/bfa/bfa_tskim.c
index 010d40d1e5d..ff7a4dc0bf3 100644
--- a/drivers/scsi/bfa/bfa_tskim.c
+++ b/drivers/scsi/bfa/bfa_tskim.c
@@ -23,13 +23,14 @@ BFA_TRC_FILE(HAL, TSKIM);
/**
* task management completion handling
*/
-#define bfa_tskim_qcomp(__tskim, __cbfn) do { \
- bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, __cbfn, (__tskim)); \
+#define bfa_tskim_qcomp(__tskim, __cbfn) do { \
+ bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, \
+ __cbfn, (__tskim)); \
bfa_tskim_notify_comp(__tskim); \
} while (0)
-#define bfa_tskim_notify_comp(__tskim) do { \
- if ((__tskim)->notify) \
+#define bfa_tskim_notify_comp(__tskim) do { \
+ if ((__tskim)->notify) \
bfa_itnim_tskdone((__tskim)->itnim); \
} while (0)
diff --git a/drivers/scsi/bfa/bfa_uf.c b/drivers/scsi/bfa/bfa_uf.c
index ff5f9deb1b2..4b3c2417d18 100644
--- a/drivers/scsi/bfa/bfa_uf.c
+++ b/drivers/scsi/bfa/bfa_uf.c
@@ -185,7 +185,7 @@ bfa_uf_get(struct bfa_uf_mod_s *uf_mod)
struct bfa_uf_s *uf;
bfa_q_deq(&uf_mod->uf_free_q, &uf);
- return (uf);
+ return uf;
}
static void
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index 6f2be5abf56..b52b773d49d 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -188,8 +188,8 @@ static struct bfad_port_s *
bfad_get_drv_port(struct bfad_s *bfad, struct bfad_vf_s *vf_drv,
struct bfad_vport_s *vp_drv)
{
- return ((vp_drv) ? (&(vp_drv)->drv_port)
- : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport)));
+ return (vp_drv) ? (&(vp_drv)->drv_port)
+ : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport));
}
struct bfad_port_s *
@@ -716,7 +716,7 @@ bfad_drv_init(struct bfad_s *bfad)
if ((bfad->bfad_flags & BFAD_MSIX_ON)
&& bfad_install_msix_handler(bfad)) {
printk(KERN_WARNING "%s: install_msix failed, bfad%d\n",
- __FUNCTION__, bfad->inst_no);
+ __func__, bfad->inst_no);
}
bfad_init_timer(bfad);
diff --git a/drivers/scsi/bfa/bfad_fwimg.c b/drivers/scsi/bfa/bfad_fwimg.c
index bd34b0db2d6..2ad65f275a9 100644
--- a/drivers/scsi/bfa/bfad_fwimg.c
+++ b/drivers/scsi/bfa/bfad_fwimg.c
@@ -65,10 +65,10 @@ bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
memcpy(*bfi_image, fw->data, fw->size);
*bfi_image_size = fw->size/sizeof(u32);
- return(*bfi_image);
+ return *bfi_image;
error:
- return(NULL);
+ return NULL;
}
u32 *
@@ -78,12 +78,12 @@ bfad_get_firmware_buf(struct pci_dev *pdev)
if (bfi_image_ct_size == 0)
bfad_read_firmware(pdev, &bfi_image_ct,
&bfi_image_ct_size, BFAD_FW_FILE_CT);
- return(bfi_image_ct);
+ return bfi_image_ct;
} else {
if (bfi_image_cb_size == 0)
bfad_read_firmware(pdev, &bfi_image_cb,
&bfi_image_cb_size, BFAD_FW_FILE_CB);
- return(bfi_image_cb);
+ return bfi_image_cb;
}
}
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index 55d012a9a66..f788c2a0ab0 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -1050,7 +1050,7 @@ bfad_im_itnim_work_handler(struct work_struct *work)
} else {
printk(KERN_WARNING
"%s: itnim %llx is already in online state\n",
- __FUNCTION__,
+ __func__,
bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim));
}
diff --git a/drivers/scsi/bfa/bfad_im_compat.h b/drivers/scsi/bfa/bfad_im_compat.h
index 1d3e74ec338..b36be15044a 100644
--- a/drivers/scsi/bfa/bfad_im_compat.h
+++ b/drivers/scsi/bfa/bfad_im_compat.h
@@ -31,7 +31,7 @@ u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
static inline u32 *
bfad_load_fwimg(struct pci_dev *pdev)
{
- return(bfad_get_firmware_buf(pdev));
+ return bfad_get_firmware_buf(pdev);
}
static inline void
diff --git a/drivers/scsi/bfa/bfad_intr.c b/drivers/scsi/bfa/bfad_intr.c
index f104e029cac..7de8832f6fe 100644
--- a/drivers/scsi/bfa/bfad_intr.c
+++ b/drivers/scsi/bfa/bfad_intr.c
@@ -23,13 +23,12 @@ BFA_TRC_FILE(LDRV, INTR);
/**
* bfa_isr BFA driver interrupt functions
*/
-irqreturn_t bfad_intx(int irq, void *dev_id);
static int msix_disable;
module_param(msix_disable, int, S_IRUGO | S_IWUSR);
/**
* Line based interrupt handler.
*/
-irqreturn_t
+static irqreturn_t
bfad_intx(int irq, void *dev_id)
{
struct bfad_s *bfad = dev_id;
diff --git a/drivers/scsi/bfa/fabric.c b/drivers/scsi/bfa/fabric.c
index a8b14c47b00..a4b5dd44957 100644
--- a/drivers/scsi/bfa/fabric.c
+++ b/drivers/scsi/bfa/fabric.c
@@ -36,12 +36,12 @@ BFA_TRC_FILE(FCS, FABRIC);
#define BFA_FCS_FABRIC_RETRY_DELAY (2000) /* Milliseconds */
#define BFA_FCS_FABRIC_CLEANUP_DELAY (10000) /* Milliseconds */
-#define bfa_fcs_fabric_set_opertype(__fabric) do { \
- if (bfa_pport_get_topology((__fabric)->fcs->bfa) \
- == BFA_PPORT_TOPOLOGY_P2P) \
- (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \
- else \
- (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \
+#define bfa_fcs_fabric_set_opertype(__fabric) do { \
+ if (bfa_pport_get_topology((__fabric)->fcs->bfa) \
+ == BFA_PPORT_TOPOLOGY_P2P) \
+ (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT; \
+ else \
+ (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \
} while (0)
/*
@@ -887,7 +887,7 @@ bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs)
bfa_boolean_t
bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric)
{
- return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback));
+ return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback);
}
enum bfa_pport_type
@@ -974,7 +974,7 @@ bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric)
int
bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric)
{
- return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online));
+ return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online);
}
@@ -1015,7 +1015,7 @@ bfa_fcs_fabric_vport_lookup(struct bfa_fcs_fabric_s *fabric, wwn_t pwwn)
u16
bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric)
{
- return (fabric->num_vports);
+ return fabric->num_vports;
}
/**
diff --git a/drivers/scsi/bfa/fcbuild.c b/drivers/scsi/bfa/fcbuild.c
index d174706b9ca..fee5456451c 100644
--- a/drivers/scsi/bfa/fcbuild.c
+++ b/drivers/scsi/bfa/fcbuild.c
@@ -188,14 +188,14 @@ fc_els_rsp_parse(struct fchs_s *fchs, int len)
switch (els_cmd->els_code) {
case FC_ELS_LS_RJT:
if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
- return (FC_PARSE_BUSY);
+ return FC_PARSE_BUSY;
else
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
case FC_ELS_ACC:
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
static void
@@ -228,7 +228,7 @@ fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
bfa_os_memcpy(&plogi->port_name, &port_name, sizeof(wwn_t));
bfa_os_memcpy(&plogi->node_name, &node_name, sizeof(wwn_t));
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -267,7 +267,7 @@ fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
flogi->csp.npiv_supp = 1; /* @todo. field name is not correct */
vvl_info[0] = bfa_os_htonl(FLOGI_VVL_BRCD);
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -287,7 +287,7 @@ fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
flogi->csp.bbcred = bfa_os_htons(local_bb_credits);
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -306,7 +306,7 @@ fc_fdisc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
flogi->port_name = port_name;
flogi->node_name = node_name;
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -338,26 +338,26 @@ fc_plogi_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
case FC_ELS_LS_RJT:
ls_rjt = (struct fc_ls_rjt_s *) (fchs + 1);
if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
- return (FC_PARSE_BUSY);
+ return FC_PARSE_BUSY;
else
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
case FC_ELS_ACC:
plogi = (struct fc_logi_s *) (fchs + 1);
if (len < sizeof(struct fc_logi_s))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(plogi->port_name, port_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!plogi->class3.class_valid)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (bfa_os_ntohs(plogi->class3.rxsz) < (FC_MIN_PDUSZ))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
default:
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
}
}
@@ -372,7 +372,7 @@ fc_plogi_parse(struct fchs_s *fchs)
if ((bfa_os_ntohs(plogi->class3.rxsz) < FC_MIN_PDUSZ)
|| (bfa_os_ntohs(plogi->class3.rxsz) > FC_MAX_PDUSZ)
|| (plogi->class3.rxsz == 0))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
return FC_PARSE_OK;
}
@@ -393,7 +393,7 @@ fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
prli->parampage.servparams.task_retry_id = 0;
prli->parampage.servparams.confirm = 1;
- return (sizeof(struct fc_prli_s));
+ return sizeof(struct fc_prli_s);
}
u16
@@ -414,41 +414,41 @@ fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
prli->parampage.rspcode = FC_PRLI_ACC_XQTD;
- return (sizeof(struct fc_prli_s));
+ return sizeof(struct fc_prli_s);
}
enum fc_parse_status
fc_prli_rsp_parse(struct fc_prli_s *prli, int len)
{
if (len < sizeof(struct fc_prli_s))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (prli->command != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if ((prli->parampage.rspcode != FC_PRLI_ACC_XQTD)
&& (prli->parampage.rspcode != FC_PRLI_ACC_PREDEF_IMG))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (prli->parampage.servparams.target != 1)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
enum fc_parse_status
fc_prli_parse(struct fc_prli_s *prli)
{
if (prli->parampage.type != FC_TYPE_FCP)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!prli->parampage.imagepair)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!prli->parampage.servparams.initiator)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
u16
@@ -462,7 +462,7 @@ fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id,
logo->nport_id = (s_id);
logo->orig_port_name = port_name;
- return (sizeof(struct fc_logo_s));
+ return sizeof(struct fc_logo_s);
}
static u16
@@ -484,7 +484,7 @@ fc_adisc_x_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id,
adisc->orig_node_name = node_name;
adisc->nport_id = (s_id);
- return (sizeof(struct fc_adisc_s));
+ return sizeof(struct fc_adisc_s);
}
u16
@@ -511,15 +511,15 @@ fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, wwn_t port_name,
{
if (len < sizeof(struct fc_adisc_s))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (adisc->els_cmd.els_code != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(adisc->orig_port_name, port_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
enum fc_parse_status
@@ -529,14 +529,14 @@ fc_adisc_parse(struct fchs_s *fchs, void *pld, u32 host_dap,
struct fc_adisc_s *adisc = (struct fc_adisc_s *) pld;
if (adisc->els_cmd.els_code != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if ((adisc->nport_id == (host_dap))
&& wwn_is_equal(adisc->orig_port_name, port_name)
&& wwn_is_equal(adisc->orig_node_name, node_name))
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
}
enum fc_parse_status
@@ -550,13 +550,13 @@ fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, wwn_t port_name)
if ((bfa_os_ntohs(pdisc->class3.rxsz) <
(FC_MIN_PDUSZ - sizeof(struct fchs_s)))
|| (pdisc->class3.rxsz == 0))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(pdisc->port_name, port_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
if (!wwn_is_equal(pdisc->node_name, node_name))
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
return FC_PARSE_OK;
}
@@ -570,7 +570,7 @@ fc_abts_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id)
fchs->s_id = (s_id);
fchs->ox_id = bfa_os_htons(ox_id);
- return (sizeof(struct fchs_s));
+ return sizeof(struct fchs_s);
}
enum fc_parse_status
@@ -578,9 +578,9 @@ fc_abts_rsp_parse(struct fchs_s *fchs, int len)
{
if ((fchs->cat_info == FC_CAT_BA_ACC)
|| (fchs->cat_info == FC_CAT_BA_RJT))
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
}
u16
@@ -597,7 +597,7 @@ fc_rrq_build(struct fchs_s *fchs, struct fc_rrq_s *rrq, u32 d_id,
rrq->ox_id = bfa_os_htons(rrq_oxid);
rrq->rx_id = FC_RXID_ANY;
- return (sizeof(struct fc_rrq_s));
+ return sizeof(struct fc_rrq_s);
}
u16
@@ -611,7 +611,7 @@ fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
memset(acc, 0, sizeof(struct fc_els_cmd_s));
acc->els_code = FC_ELS_ACC;
- return (sizeof(struct fc_els_cmd_s));
+ return sizeof(struct fc_els_cmd_s);
}
u16
@@ -627,7 +627,7 @@ fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, u32 d_id,
ls_rjt->reason_code_expl = reason_code_expl;
ls_rjt->vendor_unique = 0x00;
- return (sizeof(struct fc_ls_rjt_s));
+ return sizeof(struct fc_ls_rjt_s);
}
u16
@@ -643,7 +643,7 @@ fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, u32 d_id,
ba_acc->ox_id = fchs->ox_id;
ba_acc->rx_id = fchs->rx_id;
- return (sizeof(struct fc_ba_acc_s));
+ return sizeof(struct fc_ba_acc_s);
}
u16
@@ -654,7 +654,7 @@ fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd,
memset(els_cmd, 0, sizeof(struct fc_els_cmd_s));
els_cmd->els_code = FC_ELS_ACC;
- return (sizeof(struct fc_els_cmd_s));
+ return sizeof(struct fc_els_cmd_s);
}
int
@@ -696,7 +696,7 @@ fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc,
tprlo_acc->tprlo_acc_params[page].orig_process_assc = 0;
tprlo_acc->tprlo_acc_params[page].resp_process_assc = 0;
}
- return (bfa_os_ntohs(tprlo_acc->payload_len));
+ return bfa_os_ntohs(tprlo_acc->payload_len);
}
u16
@@ -721,7 +721,7 @@ fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc,
prlo_acc->prlo_acc_params[page].resp_process_assc = 0;
}
- return (bfa_os_ntohs(prlo_acc->payload_len));
+ return bfa_os_ntohs(prlo_acc->payload_len);
}
u16
@@ -735,7 +735,7 @@ fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, u32 d_id,
rnid->els_cmd.els_code = FC_ELS_RNID;
rnid->node_id_data_format = data_format;
- return (sizeof(struct fc_rnid_cmd_s));
+ return sizeof(struct fc_rnid_cmd_s);
}
u16
@@ -759,10 +759,10 @@ fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc,
rnid_acc->specific_id_data_length =
sizeof(struct fc_rnid_general_topology_data_s);
bfa_os_assign(rnid_acc->gen_topology_data, *gen_topo_data);
- return (sizeof(struct fc_rnid_acc_s));
+ return sizeof(struct fc_rnid_acc_s);
} else {
- return (sizeof(struct fc_rnid_acc_s) -
- sizeof(struct fc_rnid_general_topology_data_s));
+ return sizeof(struct fc_rnid_acc_s) -
+ sizeof(struct fc_rnid_general_topology_data_s);
}
}
@@ -776,7 +776,7 @@ fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, u32 d_id,
memset(rpsc, 0, sizeof(struct fc_rpsc_cmd_s));
rpsc->els_cmd.els_code = FC_ELS_RPSC;
- return (sizeof(struct fc_rpsc_cmd_s));
+ return sizeof(struct fc_rpsc_cmd_s);
}
u16
@@ -797,8 +797,8 @@ fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rpsc2,
for (i = 0; i < npids; i++)
rpsc2->pid_list[i].pid = pid_list[i];
- return (sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) *
- (sizeof(u32))));
+ return sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) *
+ (sizeof(u32)));
}
u16
@@ -819,7 +819,7 @@ fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc,
rpsc_acc->speed_info[0].port_op_speed =
bfa_os_htons(oper_speed->port_op_speed);
- return (sizeof(struct fc_rpsc_acc_s));
+ return sizeof(struct fc_rpsc_acc_s);
}
@@ -856,7 +856,7 @@ fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
pdisc->port_name = port_name;
pdisc->node_name = node_name;
- return (sizeof(struct fc_logi_s));
+ return sizeof(struct fc_logi_s);
}
u16
@@ -865,21 +865,21 @@ fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
struct fc_logi_s *pdisc = (struct fc_logi_s *) (fchs + 1);
if (len < sizeof(struct fc_logi_s))
- return (FC_PARSE_LEN_INVAL);
+ return FC_PARSE_LEN_INVAL;
if (pdisc->els_cmd.els_code != FC_ELS_ACC)
- return (FC_PARSE_ACC_INVAL);
+ return FC_PARSE_ACC_INVAL;
if (!wwn_is_equal(pdisc->port_name, port_name))
- return (FC_PARSE_PWWN_NOT_EQUAL);
+ return FC_PARSE_PWWN_NOT_EQUAL;
if (!pdisc->class3.class_valid)
- return (FC_PARSE_NWWN_NOT_EQUAL);
+ return FC_PARSE_NWWN_NOT_EQUAL;
if (bfa_os_ntohs(pdisc->class3.rxsz) < (FC_MIN_PDUSZ))
- return (FC_PARSE_RXSZ_INVAL);
+ return FC_PARSE_RXSZ_INVAL;
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
u16
@@ -903,7 +903,7 @@ fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id,
prlo->prlo_params[page].resp_process_assc = 0;
}
- return (bfa_os_ntohs(prlo->payload_len));
+ return bfa_os_ntohs(prlo->payload_len);
}
u16
@@ -916,7 +916,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len)
len = len;
if (prlo->command != FC_ELS_ACC)
- return (FC_PARSE_FAILURE);
+ return FC_PARSE_FAILURE;
num_pages = ((bfa_os_ntohs(prlo->payload_len)) - 4) / 16;
@@ -936,7 +936,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len)
if (prlo->prlo_acc_params[page].resp_process_assc != 0)
return FC_PARSE_FAILURE;
}
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
@@ -968,7 +968,7 @@ fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
}
}
- return (bfa_os_ntohs(tprlo->payload_len));
+ return bfa_os_ntohs(tprlo->payload_len);
}
u16
@@ -981,23 +981,23 @@ fc_tprlo_rsp_parse(struct fchs_s *fchs, int len)
len = len;
if (tprlo->command != FC_ELS_ACC)
- return (FC_PARSE_ACC_INVAL);
+ return FC_PARSE_ACC_INVAL;
num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16;
for (page = 0; page < num_pages; page++) {
if (tprlo->tprlo_acc_params[page].type != FC_TYPE_FCP)
- return (FC_PARSE_NOT_FCP);
+ return FC_PARSE_NOT_FCP;
if (tprlo->tprlo_acc_params[page].opa_valid != 0)
- return (FC_PARSE_OPAFLAG_INVAL);
+ return FC_PARSE_OPAFLAG_INVAL;
if (tprlo->tprlo_acc_params[page].rpa_valid != 0)
- return (FC_PARSE_RPAFLAG_INVAL);
+ return FC_PARSE_RPAFLAG_INVAL;
if (tprlo->tprlo_acc_params[page].orig_process_assc != 0)
- return (FC_PARSE_OPA_INVAL);
+ return FC_PARSE_OPA_INVAL;
if (tprlo->tprlo_acc_params[page].resp_process_assc != 0)
- return (FC_PARSE_RPA_INVAL);
+ return FC_PARSE_RPA_INVAL;
}
- return (FC_PARSE_OK);
+ return FC_PARSE_OK;
}
enum fc_parse_status
@@ -1024,7 +1024,7 @@ fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
fchs->cat_info = FC_CAT_BA_RJT;
ba_rjt->reason_code = reason_code;
ba_rjt->reason_expl = reason_expl;
- return (sizeof(struct fc_ba_rjt_s));
+ return sizeof(struct fc_ba_rjt_s);
}
static void
@@ -1073,7 +1073,7 @@ fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
bfa_os_memset(gidpn, 0, sizeof(struct fcgs_gidpn_req_s));
gidpn->port_name = port_name;
- return (sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1090,7 +1090,7 @@ fc_gpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
bfa_os_memset(gpnid, 0, sizeof(fcgs_gpnid_req_t));
gpnid->dap = port_id;
- return (sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s));
+ return sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s);
}
u16
@@ -1107,7 +1107,7 @@ fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
bfa_os_memset(gnnid, 0, sizeof(fcgs_gnnid_req_t));
gnnid->dap = port_id;
- return (sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s));
+ return sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s);
}
u16
@@ -1137,7 +1137,7 @@ fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, u8 set_br_reg,
if (set_br_reg)
scr->vu_reg_func = FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE;
- return (sizeof(struct fc_scr_s));
+ return sizeof(struct fc_scr_s);
}
u16
@@ -1157,7 +1157,7 @@ fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, u32 s_id,
rscn->event[0].format = FC_RSCN_FORMAT_PORTID;
rscn->event[0].portid = s_id;
- return (sizeof(struct fc_rscn_pl_s));
+ return sizeof(struct fc_rscn_pl_s);
}
u16
@@ -1188,7 +1188,7 @@ fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
rftid->fc4_type[index] |= bfa_os_htonl(type_value);
}
- return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1210,7 +1210,7 @@ fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id,
bfa_os_memcpy((void *)rftid->fc4_type, (void *)fc4_bitmap,
(bitmap_size < 32 ? bitmap_size : 32));
- return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1231,7 +1231,7 @@ fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
rffid->fc4ftr_bits = fc4_ftrs;
rffid->fc4_type = fc4_type;
- return (sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1253,7 +1253,7 @@ fc_rspnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
rspnid->spn_len = (u8) strlen((char *)name);
strncpy((char *)rspnid->spn, (char *)name, rspnid->spn_len);
- return (sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1275,7 +1275,7 @@ fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id,
gidft->domain_id = 0;
gidft->area_id = 0;
- return (sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1294,7 +1294,7 @@ fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rpnid->port_id = port_id;
rpnid->port_name = port_name;
- return (sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1313,7 +1313,7 @@ fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rnnid->port_id = port_id;
rnnid->node_name = node_name;
- return (sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1332,7 +1332,7 @@ fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rcsid->port_id = port_id;
rcsid->cos = cos;
- return (sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1351,7 +1351,7 @@ fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
rptid->port_id = port_id;
rptid->port_type = port_type;
- return (sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s));
+ return sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s);
}
u16
@@ -1368,7 +1368,7 @@ fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id)
bfa_os_memset(ganxt, 0, sizeof(struct fcgs_ganxt_req_s));
ganxt->port_id = port_id;
- return (sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s));
+ return sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s);
}
/*
@@ -1385,7 +1385,7 @@ fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id,
fc_gs_fchdr_build(fchs, d_id, s_id, 0);
fc_gs_fdmi_cthdr_build(cthdr, s_id, cmd_code);
- return (sizeof(struct ct_hdr_s));
+ return sizeof(struct ct_hdr_s);
}
/*
@@ -1425,7 +1425,7 @@ fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn)
bfa_os_memset(gmal, 0, sizeof(fcgs_gmal_req_t));
gmal->wwn = wwn;
- return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t));
+ return sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t);
}
/*
@@ -1445,5 +1445,5 @@ fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn)
bfa_os_memset(gfn, 0, sizeof(fcgs_gfn_req_t));
gfn->wwn = wwn;
- return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t));
+ return sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t);
}
diff --git a/drivers/scsi/bfa/fcbuild.h b/drivers/scsi/bfa/fcbuild.h
index 4d248424f7b..8fa7f270ef7 100644
--- a/drivers/scsi/bfa/fcbuild.h
+++ b/drivers/scsi/bfa/fcbuild.h
@@ -32,8 +32,8 @@
* Utility Macros/functions
*/
-#define fcif_sof_set(_ifhdr, _sof) (_ifhdr)->sof = FC_ ## _sof
-#define fcif_eof_set(_ifhdr, _eof) (_ifhdr)->eof = FC_ ## _eof
+#define fcif_sof_set(_ifhdr, _sof) ((_ifhdr)->sof = FC_ ## _sof)
+#define fcif_eof_set(_ifhdr, _eof) ((_ifhdr)->eof = FC_ ## _eof)
#define wwn_is_equal(_wwn1, _wwn2) \
(memcmp(&(_wwn1), &(_wwn2), sizeof(wwn_t)) == 0)
@@ -49,7 +49,7 @@
static inline u32
fc_get_ctresp_pyld_len(u32 resp_len)
{
- return (resp_len - sizeof(struct ct_hdr_s));
+ return resp_len - sizeof(struct ct_hdr_s);
}
/*
diff --git a/drivers/scsi/bfa/fcpim.c b/drivers/scsi/bfa/fcpim.c
index 8ce5d893467..1f3c06efaa9 100644
--- a/drivers/scsi/bfa/fcpim.c
+++ b/drivers/scsi/bfa/fcpim.c
@@ -286,11 +286,10 @@ bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
bfa_fcb_itnim_offline(itnim->itnim_drv);
bfa_itnim_offline(itnim->bfa_itnim);
- if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE) {
+ if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE)
bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT);
- } else {
+ else
bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE);
- }
break;
case BFA_FCS_ITNIM_SM_DELETE:
@@ -732,7 +731,7 @@ bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn)
return NULL;
bfa_assert(rport->itnim != NULL);
- return (rport->itnim);
+ return rport->itnim;
}
bfa_status_t
diff --git a/drivers/scsi/bfa/fcs.h b/drivers/scsi/bfa/fcs.h
index deee685e847..8d08230e629 100644
--- a/drivers/scsi/bfa/fcs.h
+++ b/drivers/scsi/bfa/fcs.h
@@ -23,7 +23,7 @@
#ifndef __FCS_H__
#define __FCS_H__
-#define __fcs_min_cfg(__fcs) (__fcs)->min_cfg
+#define __fcs_min_cfg(__fcs) ((__fcs)->min_cfg)
void bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs);
diff --git a/drivers/scsi/bfa/fdmi.c b/drivers/scsi/bfa/fdmi.c
index b845eb272c7..df2a1e54e16 100644
--- a/drivers/scsi/bfa/fdmi.c
+++ b/drivers/scsi/bfa/fdmi.c
@@ -72,9 +72,9 @@ static u16 bfa_fcs_port_fdmi_build_rpa_pyld(
struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld);
static u16 bfa_fcs_port_fdmi_build_portattr_block(
struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld);
-void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
+static void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_hba_attr_s *hba_attr);
-void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
+static void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_port_attr_s *port_attr);
/**
* fcs_fdmi_sm FCS FDMI state machine
@@ -1091,7 +1091,7 @@ bfa_fcs_port_fdmi_timeout(void *arg)
bfa_sm_send_event(fdmi, FDMISM_EVENT_TIMEOUT);
}
-void
+static void
bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_hba_attr_s *hba_attr)
{
@@ -1145,7 +1145,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
}
-void
+static void
bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
struct bfa_fcs_fdmi_port_attr_s *port_attr)
{
diff --git a/drivers/scsi/bfa/include/aen/bfa_aen.h b/drivers/scsi/bfa/include/aen/bfa_aen.h
index da8cac093d3..d9cbc2a783d 100644
--- a/drivers/scsi/bfa/include/aen/bfa_aen.h
+++ b/drivers/scsi/bfa/include/aen/bfa_aen.h
@@ -54,7 +54,7 @@ bfa_aen_get_max_cfg_entry(void)
static inline s32
bfa_aen_get_meminfo(void)
{
- return (sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry());
+ return sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry();
}
static inline s32
diff --git a/drivers/scsi/bfa/include/bfa.h b/drivers/scsi/bfa/include/bfa.h
index 64c1412c570..d4bc0d9fa42 100644
--- a/drivers/scsi/bfa/include/bfa.h
+++ b/drivers/scsi/bfa/include/bfa.h
@@ -76,11 +76,11 @@ struct bfa_meminfo_s {
struct bfa_mem_elem_s meminfo[BFA_MEM_TYPE_MAX];
};
#define bfa_meminfo_kva(_m) \
- (_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp
+ ((_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp)
#define bfa_meminfo_dma_virt(_m) \
- (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp
+ ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp)
#define bfa_meminfo_dma_phys(_m) \
- (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp
+ ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp)
/**
* Generic Scatter Gather Element used by driver
@@ -100,7 +100,7 @@ struct bfa_sge_s {
/*
* bfa stats interfaces
*/
-#define bfa_stats(_mod, _stats) (_mod)->stats._stats ++
+#define bfa_stats(_mod, _stats) ((_mod)->stats._stats++)
#define bfa_ioc_get_stats(__bfa, __ioc_stats) \
bfa_ioc_fetch_stats(&(__bfa)->ioc, __ioc_stats)
@@ -136,7 +136,7 @@ void bfa_isr_enable(struct bfa_s *bfa);
void bfa_isr_disable(struct bfa_s *bfa);
void bfa_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap,
u32 *num_vecs, u32 *max_vec_bit);
-#define bfa_msix(__bfa, __vec) (__bfa)->msix.handler[__vec](__bfa, __vec)
+#define bfa_msix(__bfa, __vec) ((__bfa)->msix.handler[__vec](__bfa, __vec))
void bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q);
void bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q);
diff --git a/drivers/scsi/bfa/include/bfa_svc.h b/drivers/scsi/bfa/include/bfa_svc.h
index 0c80b74f72e..268d956bad8 100644
--- a/drivers/scsi/bfa/include/bfa_svc.h
+++ b/drivers/scsi/bfa/include/bfa_svc.h
@@ -34,10 +34,10 @@ struct bfa_fcxp_s;
*/
struct bfa_rport_info_s {
u16 max_frmsz; /* max rcv pdu size */
- u32 pid : 24, /* remote port ID */
- lp_tag : 8;
- u32 local_pid : 24, /* local port ID */
- cisc : 8; /* CIRO supported */
+ u32 pid:24, /* remote port ID */
+ lp_tag:8;
+ u32 local_pid:24, /* local port ID */
+ cisc:8; /* CIRO supported */
u8 fc_class; /* supported FC classes. enum fc_cos */
u8 vf_en; /* virtual fabric enable */
u16 vf_id; /* virtual fabric ID */
diff --git a/drivers/scsi/bfa/include/bfi/bfi.h b/drivers/scsi/bfa/include/bfi/bfi.h
index 6cadfe0d4ba..7042c18e542 100644
--- a/drivers/scsi/bfa/include/bfi/bfi.h
+++ b/drivers/scsi/bfa/include/bfi/bfi.h
@@ -93,13 +93,13 @@ union bfi_addr_u {
*/
struct bfi_sge_s {
#ifdef __BIGENDIAN
- u32 flags : 2,
- rsvd : 2,
- sg_len : 28;
+ u32 flags:2,
+ rsvd:2,
+ sg_len:28;
#else
- u32 sg_len : 28,
- rsvd : 2,
- flags : 2;
+ u32 sg_len:28,
+ rsvd:2,
+ flags:2;
#endif
union bfi_addr_u sga;
};
diff --git a/drivers/scsi/bfa/include/bfi/bfi_ioc.h b/drivers/scsi/bfa/include/bfi/bfi_ioc.h
index 026e9c06ae9..96ef0567065 100644
--- a/drivers/scsi/bfa/include/bfi/bfi_ioc.h
+++ b/drivers/scsi/bfa/include/bfi/bfi_ioc.h
@@ -142,7 +142,7 @@ enum {
BFI_ADAPTER_UNSUPP = 0x400000, /* unknown adapter type */
};
-#define BFI_ADAPTER_GETP(__prop,__adap_prop) \
+#define BFI_ADAPTER_GETP(__prop, __adap_prop) \
(((__adap_prop) & BFI_ADAPTER_ ## __prop ## _MK) >> \
BFI_ADAPTER_ ## __prop ## _SH)
#define BFI_ADAPTER_SETP(__prop, __val) \
diff --git a/drivers/scsi/bfa/include/bfi/bfi_lps.h b/drivers/scsi/bfa/include/bfi/bfi_lps.h
index 414b0e30f6e..c59d47badb4 100644
--- a/drivers/scsi/bfa/include/bfi/bfi_lps.h
+++ b/drivers/scsi/bfa/include/bfi/bfi_lps.h
@@ -55,8 +55,8 @@ struct bfi_lps_login_rsp_s {
u16 bb_credit;
u8 f_port;
u8 npiv_en;
- u32 lp_pid : 24;
- u32 auth_req : 8;
+ u32 lp_pid:24;
+ u32 auth_req:8;
mac_t lp_mac;
mac_t fcf_mac;
u8 ext_status;
diff --git a/drivers/scsi/bfa/include/bfi/bfi_rport.h b/drivers/scsi/bfa/include/bfi/bfi_rport.h
index 3520f55f09d..e1cd83b56ec 100644
--- a/drivers/scsi/bfa/include/bfi/bfi_rport.h
+++ b/drivers/scsi/bfa/include/bfi/bfi_rport.h
@@ -38,10 +38,10 @@ struct bfi_rport_create_req_s {
struct bfi_mhdr_s mh; /* common msg header */
u16 bfa_handle; /* host rport handle */
u16 max_frmsz; /* max rcv pdu size */
- u32 pid : 24, /* remote port ID */
- lp_tag : 8; /* local port tag */
- u32 local_pid : 24, /* local port ID */
- cisc : 8;
+ u32 pid:24, /* remote port ID */
+ lp_tag:8; /* local port tag */
+ u32 local_pid:24, /* local port ID */
+ cisc:8;
u8 fc_class; /* supported FC classes */
u8 vf_en; /* virtual fabric enable */
u16 vf_id; /* virtual fabric ID */
diff --git a/drivers/scsi/bfa/include/cs/bfa_checksum.h b/drivers/scsi/bfa/include/cs/bfa_checksum.h
index af8c1d533ba..650f8d0aaff 100644
--- a/drivers/scsi/bfa/include/cs/bfa_checksum.h
+++ b/drivers/scsi/bfa/include/cs/bfa_checksum.h
@@ -31,7 +31,7 @@ bfa_checksum_u32(u32 *buf, int sz)
for (i = 0; i < m; i++)
sum ^= buf[i];
- return (sum);
+ return sum;
}
static inline u16
@@ -43,7 +43,7 @@ bfa_checksum_u16(u16 *buf, int sz)
for (i = 0; i < m; i++)
sum ^= buf[i];
- return (sum);
+ return sum;
}
static inline u8
@@ -55,6 +55,6 @@ bfa_checksum_u8(u8 *buf, int sz)
for (i = 0; i < sz; i++)
sum ^= buf[i];
- return (sum);
+ return sum;
}
#endif
diff --git a/drivers/scsi/bfa/include/cs/bfa_sm.h b/drivers/scsi/bfa/include/cs/bfa_sm.h
index 9877066680a..b0a92baf665 100644
--- a/drivers/scsi/bfa/include/cs/bfa_sm.h
+++ b/drivers/scsi/bfa/include/cs/bfa_sm.h
@@ -24,8 +24,8 @@
typedef void (*bfa_sm_t)(void *sm, int event);
-#define bfa_sm_set_state(_sm, _state) (_sm)->sm = (bfa_sm_t)(_state)
-#define bfa_sm_send_event(_sm, _event) (_sm)->sm((_sm), (_event))
+#define bfa_sm_set_state(_sm, _state) ((_sm)->sm = (bfa_sm_t)(_state))
+#define bfa_sm_send_event(_sm, _event) ((_sm)->sm((_sm), (_event)))
#define bfa_sm_get_state(_sm) ((_sm)->sm)
#define bfa_sm_cmp_state(_sm, _state) ((_sm)->sm == (bfa_sm_t)(_state))
@@ -62,7 +62,7 @@ typedef void (*bfa_fsm_t)(void *fsm, int event);
} while (0)
#define bfa_fsm_send_event(_fsm, _event) \
- (_fsm)->fsm((_fsm), (_event))
+ ((_fsm)->fsm((_fsm), (_event)))
#define bfa_fsm_cmp_state(_fsm, _state) \
((_fsm)->fsm == (bfa_fsm_t)(_state))
diff --git a/drivers/scsi/bfa/include/cs/bfa_trc.h b/drivers/scsi/bfa/include/cs/bfa_trc.h
index 3e743928c74..310771c888e 100644
--- a/drivers/scsi/bfa/include/cs/bfa_trc.h
+++ b/drivers/scsi/bfa/include/cs/bfa_trc.h
@@ -24,7 +24,7 @@
#endif
#ifndef BFA_TRC_TS
-#define BFA_TRC_TS(_trcm) ((_trcm)->ticks ++)
+#define BFA_TRC_TS(_trcm) ((_trcm)->ticks++)
#endif
struct bfa_trc_s {
diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_pport.h b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h
index a000bc4e2d4..bf320412ee2 100644
--- a/drivers/scsi/bfa/include/defs/bfa_defs_pport.h
+++ b/drivers/scsi/bfa/include/defs/bfa_defs_pport.h
@@ -61,7 +61,7 @@ enum bfa_pport_speed {
* Port operational type (in sync with SNIA port type).
*/
enum bfa_pport_type {
- BFA_PPORT_TYPE_UNKNOWN = 1, /* port type is unkown */
+ BFA_PPORT_TYPE_UNKNOWN = 1, /* port type is unknown */
BFA_PPORT_TYPE_TRUNKED = 2, /* Trunked mode */
BFA_PPORT_TYPE_NPORT = 5, /* P2P with switched fabric */
BFA_PPORT_TYPE_NLPORT = 6, /* public loop */
diff --git a/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h
index 31881d21851..ade763dbc8c 100644
--- a/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h
+++ b/drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h
@@ -25,7 +25,7 @@
* Temperature sensor status values
*/
enum bfa_tsensor_status {
- BFA_TSENSOR_STATUS_UNKNOWN = 1, /* unkown status */
+ BFA_TSENSOR_STATUS_UNKNOWN = 1, /* unknown status */
BFA_TSENSOR_STATUS_FAULTY = 2, /* sensor is faulty */
BFA_TSENSOR_STATUS_BELOW_MIN = 3, /* temperature below mininum */
BFA_TSENSOR_STATUS_NOMINAL = 4, /* normal temperature */
diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h
index 4ffd2242d3d..08b79d5e46f 100644
--- a/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h
+++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h
@@ -75,7 +75,7 @@ struct bfa_fcs_fabric_s {
*/
};
-#define bfa_fcs_fabric_npiv_capable(__f) (__f)->is_npiv
+#define bfa_fcs_fabric_npiv_capable(__f) ((__f)->is_npiv)
#define bfa_fcs_fabric_is_switched(__f) \
((__f)->fab_type == BFA_FCS_FABRIC_SWITCHED)
diff --git a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h
index b85cba884b9..967ceb0eb07 100644
--- a/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h
+++ b/drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h
@@ -125,12 +125,12 @@ union bfa_fcs_port_topo_u {
struct bfa_fcs_port_s {
struct list_head qe; /* used by port/vport */
bfa_sm_t sm; /* state machine */
- struct bfa_fcs_fabric_s *fabric; /* parent fabric */
- struct bfa_port_cfg_s port_cfg; /* port configuration */
+ struct bfa_fcs_fabric_s *fabric;/* parent fabric */
+ struct bfa_port_cfg_s port_cfg;/* port configuration */
struct bfa_timer_s link_timer; /* timer for link offline */
- u32 pid : 24; /* FC address */
- u8 lp_tag; /* lport tag */
- u16 num_rports; /* Num of r-ports */
+ u32 pid:24; /* FC address */
+ u8 lp_tag; /* lport tag */
+ u16 num_rports; /* Num of r-ports */
struct list_head rport_q; /* queue of discovered r-ports */
struct bfa_fcs_s *fcs; /* FCS instance */
union bfa_fcs_port_topo_u port_topo; /* fabric/loop/n2n details */
@@ -188,13 +188,14 @@ bfa_fcs_port_get_drvport(struct bfa_fcs_port_s *port)
}
-#define bfa_fcs_port_get_opertype(_lport) (_lport)->fabric->oper_type
+#define bfa_fcs_port_get_opertype(_lport) ((_lport)->fabric->oper_type)
-#define bfa_fcs_port_get_fabric_name(_lport) (_lport)->fabric->fabric_name
+#define bfa_fcs_port_get_fabric_name(_lport) ((_lport)->fabric->fabric_name)
-#define bfa_fcs_port_get_fabric_ipaddr(_lport) (_lport)->fabric->fabric_ip_addr
+#define bfa_fcs_port_get_fabric_ipaddr(_lport) \
+ ((_lport)->fabric->fabric_ip_addr)
/**
* bfa fcs port public functions
diff --git a/drivers/scsi/bfa/include/protocol/ct.h b/drivers/scsi/bfa/include/protocol/ct.h
index c59d6630b07..b82540a230c 100644
--- a/drivers/scsi/bfa/include/protocol/ct.h
+++ b/drivers/scsi/bfa/include/protocol/ct.h
@@ -82,7 +82,7 @@ enum {
};
/*
- * defintions for CT reason code
+ * definitions for CT reason code
*/
enum {
CT_RSN_INV_CMD = 0x01,
@@ -129,7 +129,7 @@ enum {
};
/*
- * defintions for the explanation code for all servers
+ * definitions for the explanation code for all servers
*/
enum {
CT_EXP_AUTH_EXCEPTION = 0xF1,
@@ -193,11 +193,11 @@ struct fcgs_rftid_req_s {
#define FC_GS_FCP_FC4_FEATURE_TARGET 0x01
struct fcgs_rffid_req_s{
- u32 rsvd :8;
- u32 dap :24; /* port identifier */
- u32 rsvd1 :16;
- u32 fc4ftr_bits :8; /* fc4 feature bits */
- u32 fc4_type :8; /* corresponding FC4 Type */
+ u32 rsvd:8;
+ u32 dap:24; /* port identifier */
+ u32 rsvd1:16;
+ u32 fc4ftr_bits:8; /* fc4 feature bits */
+ u32 fc4_type:8; /* corresponding FC4 Type */
};
/**
diff --git a/drivers/scsi/bfa/include/protocol/fc.h b/drivers/scsi/bfa/include/protocol/fc.h
index 3e39ba58cfb..14969eecf6a 100644
--- a/drivers/scsi/bfa/include/protocol/fc.h
+++ b/drivers/scsi/bfa/include/protocol/fc.h
@@ -486,14 +486,14 @@ struct fc_rsi_s {
* see FC-PH-X table 113 & 115 for explanation also FCP table 8
*/
struct fc_prli_params_s{
- u32 reserved: 16;
+ u32 reserved:16;
#ifdef __BIGENDIAN
- u32 reserved1: 5;
- u32 rec_support : 1;
- u32 task_retry_id : 1;
- u32 retry : 1;
+ u32 reserved1:5;
+ u32 rec_support:1;
+ u32 task_retry_id:1;
+ u32 retry:1;
- u32 confirm : 1;
+ u32 confirm:1;
u32 doverlay:1;
u32 initiator:1;
u32 target:1;
@@ -502,10 +502,10 @@ struct fc_prli_params_s{
u32 rxrdisab:1;
u32 wxrdisab:1;
#else
- u32 retry : 1;
- u32 task_retry_id : 1;
- u32 rec_support : 1;
- u32 reserved1: 5;
+ u32 retry:1;
+ u32 task_retry_id:1;
+ u32 rec_support:1;
+ u32 reserved1:5;
u32 wxrdisab:1;
u32 rxrdisab:1;
@@ -514,7 +514,7 @@ struct fc_prli_params_s{
u32 target:1;
u32 initiator:1;
u32 doverlay:1;
- u32 confirm : 1;
+ u32 confirm:1;
#endif
};
diff --git a/drivers/scsi/bfa/loop.c b/drivers/scsi/bfa/loop.c
index a418dedebe9..f7c7f4f3c64 100644
--- a/drivers/scsi/bfa/loop.c
+++ b/drivers/scsi/bfa/loop.c
@@ -58,49 +58,16 @@ static const u8 port_loop_alpa_map[] = {
/*
* Local Functions
*/
-bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_plogi_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
-
-bfa_status_t bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_adisc_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
-
-bfa_status_t bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_plogi_acc_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
-
-bfa_status_t bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port,
- u8 alpa);
-
-void bfa_fcs_port_loop_adisc_acc_response(void *fcsarg,
- struct bfa_fcxp_s *fcxp,
- void *cbarg,
- bfa_status_t req_status,
- u32 rsp_len,
- u32 resid_len,
- struct fchs_s *rsp_fchs);
+static bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port,
+ u8 alpa);
+
+static void bfa_fcs_port_loop_plogi_response(void *fcsarg,
+ struct bfa_fcxp_s *fcxp,
+ void *cbarg,
+ bfa_status_t req_status,
+ u32 rsp_len,
+ u32 resid_len,
+ struct fchs_s *rsp_fchs);
/**
* Called by port to initializar in provate LOOP topology.
*/
@@ -179,7 +146,7 @@ bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port)
/**
* Local Functions.
*/
-bfa_status_t
+static bfa_status_t
bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa)
{
struct fchs_s fchs;
@@ -208,7 +175,7 @@ bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa)
/**
* Called by fcxp to notify the Plogi response
*/
-void
+static void
bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
void *cbarg, bfa_status_t req_status,
u32 rsp_len, u32 resid_len,
@@ -244,179 +211,3 @@ bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
bfa_assert(0);
}
}
-
-bfa_status_t
-bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, u8 alpa)
-{
- struct fchs_s fchs;
- struct bfa_fcxp_s *fcxp;
- int len;
-
- bfa_trc(port->fcs, alpa);
-
- fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
- NULL);
- bfa_assert(fcxp);
-
- len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
- bfa_fcs_port_get_fcid(port), 0,
- port->port_cfg.pwwn, port->port_cfg.nwwn,
- bfa_pport_get_maxfrsize(port->fcs->bfa));
-
- bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
- FC_CLASS_3, len, &fchs,
- bfa_fcs_port_loop_plogi_acc_response,
- (void *)port, FC_MAX_PDUSZ, 0); /* No response
- * expected
- */
-
- return BFA_STATUS_OK;
-}
-
-/*
- * Plogi Acc Response
- * We donot do any processing here.
- */
-void
-bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
- void *cbarg, bfa_status_t req_status,
- u32 rsp_len, u32 resid_len,
- struct fchs_s *rsp_fchs)
-{
-
- struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
-
- bfa_trc(port->fcs, port->pid);
-
- /*
- * Sanity Checks
- */
- if (req_status != BFA_STATUS_OK) {
- bfa_trc(port->fcs, req_status);
- return;
- }
-}
-
-bfa_status_t
-bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, u8 alpa)
-{
- struct fchs_s fchs;
- struct bfa_fcxp_s *fcxp;
- int len;
-
- bfa_trc(port->fcs, alpa);
-
- fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
- NULL);
- bfa_assert(fcxp);
-
- len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
- bfa_fcs_port_get_fcid(port), 0,
- port->port_cfg.pwwn, port->port_cfg.nwwn);
-
- bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
- FC_CLASS_3, len, &fchs,
- bfa_fcs_port_loop_adisc_response, (void *)port,
- FC_MAX_PDUSZ, FC_RA_TOV);
-
- return BFA_STATUS_OK;
-}
-
-/**
- * Called by fcxp to notify the ADISC response
- */
-void
-bfa_fcs_port_loop_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
- void *cbarg, bfa_status_t req_status,
- u32 rsp_len, u32 resid_len,
- struct fchs_s *rsp_fchs)
-{
- struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
- struct bfa_fcs_rport_s *rport;
- struct fc_adisc_s *adisc_resp;
- struct fc_els_cmd_s *els_cmd;
- u32 pid = rsp_fchs->s_id;
-
- bfa_trc(port->fcs, req_status);
-
- /*
- * Sanity Checks
- */
- if (req_status != BFA_STATUS_OK) {
- /*
- * TBD : we may need to retry certain requests
- */
- bfa_fcxp_free(fcxp);
- return;
- }
-
- els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp);
- adisc_resp = (struct fc_adisc_s *) els_cmd;
-
- if (els_cmd->els_code == FC_ELS_ACC) {
- } else {
- bfa_trc(port->fcs, adisc_resp->els_cmd.els_code);
-
- /*
- * TBD: we may need to check for reject codes and retry
- */
- rport = bfa_fcs_port_get_rport_by_pid(port, pid);
- if (rport) {
- list_del(&rport->qe);
- bfa_fcs_rport_delete(rport);
- }
-
- }
- return;
-}
-
-bfa_status_t
-bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, u8 alpa)
-{
- struct fchs_s fchs;
- struct bfa_fcxp_s *fcxp;
- int len;
-
- bfa_trc(port->fcs, alpa);
-
- fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
- NULL);
- bfa_assert(fcxp);
-
- len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
- bfa_fcs_port_get_fcid(port), 0,
- port->port_cfg.pwwn, port->port_cfg.nwwn);
-
- bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
- FC_CLASS_3, len, &fchs,
- bfa_fcs_port_loop_adisc_acc_response,
- (void *)port, FC_MAX_PDUSZ, 0); /* no reponse
- * expected
- */
-
- return BFA_STATUS_OK;
-}
-
-/*
- * Adisc Acc Response
- * We donot do any processing here.
- */
-void
-bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
- void *cbarg, bfa_status_t req_status,
- u32 rsp_len, u32 resid_len,
- struct fchs_s *rsp_fchs)
-{
-
- struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
-
- bfa_trc(port->fcs, port->pid);
-
- /*
- * Sanity Checks
- */
- if (req_status != BFA_STATUS_OK) {
- bfa_trc(port->fcs, req_status);
- return;
- }
-}
diff --git a/drivers/scsi/bfa/lport_api.c b/drivers/scsi/bfa/lport_api.c
index 8f51a83f183..1e06792cd4c 100644
--- a/drivers/scsi/bfa/lport_api.c
+++ b/drivers/scsi/bfa/lport_api.c
@@ -43,7 +43,7 @@ bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg)
struct bfa_fcs_port_s *
bfa_fcs_get_base_port(struct bfa_fcs_s *fcs)
{
- return (&fcs->fabric.bport);
+ return &fcs->fabric.bport;
}
wwn_t
@@ -88,11 +88,10 @@ bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, int index,
}
bfa_trc(fcs, i);
- if (rport) {
+ if (rport)
return rport->pwwn;
- } else {
+ else
return (wwn_t) 0;
- }
}
void
@@ -198,17 +197,17 @@ bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn)
vf = bfa_fcs_vf_lookup(fcs, vf_id);
if (vf == NULL) {
bfa_trc(fcs, vf_id);
- return (NULL);
+ return NULL;
}
if (!lpwwn || (vf->bport.port_cfg.pwwn == lpwwn))
- return (&vf->bport);
+ return &vf->bport;
vport = bfa_fcs_fabric_vport_lookup(vf, lpwwn);
if (vport)
- return (&vport->lport);
+ return &vport->lport;
- return (NULL);
+ return NULL;
}
/*
diff --git a/drivers/scsi/bfa/ns.c b/drivers/scsi/bfa/ns.c
index 59fea99d67a..2f8b880060b 100644
--- a/drivers/scsi/bfa/ns.c
+++ b/drivers/scsi/bfa/ns.c
@@ -932,11 +932,10 @@ bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
}
ns->fcxp = fcxp;
- if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) {
+ if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port))
fc4_ftrs = FC_GS_FCP_FC4_FEATURE_INITIATOR;
- } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) {
+ else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port))
fc4_ftrs = FC_GS_FCP_FC4_FEATURE_TARGET;
- }
len = fc_rffid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
bfa_fcs_port_get_fcid(port), 0, FC_TYPE_FCP,
diff --git a/drivers/scsi/bfa/plog.c b/drivers/scsi/bfa/plog.c
index 86af818d17b..fcb8864d327 100644
--- a/drivers/scsi/bfa/plog.c
+++ b/drivers/scsi/bfa/plog.c
@@ -180,5 +180,5 @@ bfa_plog_disable(struct bfa_plog_s *plog)
bfa_boolean_t
bfa_plog_get_setting(struct bfa_plog_s *plog)
{
- return((bfa_boolean_t)plog->plog_enabled);
+ return (bfa_boolean_t)plog->plog_enabled;
}
diff --git a/drivers/scsi/bfa/rport_ftrs.c b/drivers/scsi/bfa/rport_ftrs.c
index 8a1f59d596c..e1932c885ac 100644
--- a/drivers/scsi/bfa/rport_ftrs.c
+++ b/drivers/scsi/bfa/rport_ftrs.c
@@ -79,7 +79,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_RPORT_ONLINE :
+ case RPFSM_EVENT_RPORT_ONLINE:
if (!BFA_FCS_PID_IS_WKA(rport->pid)) {
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
rpf->rpsc_retries = 0;
@@ -87,7 +87,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
break;
};
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
break;
default:
@@ -107,7 +107,7 @@ bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc);
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rpf->fcxp_wqe);
rpf->rpsc_retries = 0;
@@ -130,11 +130,10 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
case RPFSM_EVENT_RPSC_COMP:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online);
/* Update speed info in f/w via BFA */
- if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) {
+ if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN)
bfa_rport_speed(rport->bfa_rport, rpf->rpsc_speed);
- } else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) {
+ else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN)
bfa_rport_speed(rport->bfa_rport, rpf->assigned_speed);
- }
break;
case RPFSM_EVENT_RPSC_FAIL:
@@ -154,7 +153,7 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
}
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
bfa_fcxp_discard(rpf->fcxp);
rpf->rpsc_retries = 0;
@@ -174,13 +173,13 @@ bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_TIMEOUT :
+ case RPFSM_EVENT_TIMEOUT:
/* re-send the RPSC */
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
bfa_fcs_rpf_send_rpsc2(rpf, NULL);
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_timer_stop(&rpf->timer);
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
rpf->rpsc_retries = 0;
@@ -201,7 +200,7 @@ bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
rpf->rpsc_retries = 0;
break;
@@ -221,12 +220,12 @@ bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
bfa_trc(rport->fcs, event);
switch (event) {
- case RPFSM_EVENT_RPORT_ONLINE :
+ case RPFSM_EVENT_RPORT_ONLINE:
bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
bfa_fcs_rpf_send_rpsc2(rpf, NULL);
break;
- case RPFSM_EVENT_RPORT_OFFLINE :
+ case RPFSM_EVENT_RPORT_OFFLINE:
break;
default:
@@ -366,10 +365,9 @@ bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
bfa_trc(rport->fcs, ls_rjt->reason_code);
bfa_trc(rport->fcs, ls_rjt->reason_code_expl);
rport->stats.rpsc_rejects++;
- if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) {
+ if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP)
bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_FAIL);
- } else {
+ else
bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR);
- }
}
}
diff --git a/drivers/scsi/bfa/vfapi.c b/drivers/scsi/bfa/vfapi.c
index 31d81fe2fc4..391a4790beb 100644
--- a/drivers/scsi/bfa/vfapi.c
+++ b/drivers/scsi/bfa/vfapi.c
@@ -189,7 +189,7 @@ bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id)
{
bfa_trc(fcs, vf_id);
if (vf_id == FC_VF_ID_NULL)
- return (&fcs->fabric);
+ return &fcs->fabric;
/**
* @todo vf support
diff --git a/drivers/scsi/bfa/vport.c b/drivers/scsi/bfa/vport.c
index c10af06c571..e90f1e38c32 100644
--- a/drivers/scsi/bfa/vport.c
+++ b/drivers/scsi/bfa/vport.c
@@ -31,13 +31,13 @@
BFA_TRC_FILE(FCS, VPORT);
-#define __vport_fcs(__vp) (__vp)->lport.fcs
-#define __vport_pwwn(__vp) (__vp)->lport.port_cfg.pwwn
-#define __vport_nwwn(__vp) (__vp)->lport.port_cfg.nwwn
-#define __vport_bfa(__vp) (__vp)->lport.fcs->bfa
-#define __vport_fcid(__vp) (__vp)->lport.pid
-#define __vport_fabric(__vp) (__vp)->lport.fabric
-#define __vport_vfid(__vp) (__vp)->lport.fabric->vf_id
+#define __vport_fcs(__vp) ((__vp)->lport.fcs)
+#define __vport_pwwn(__vp) ((__vp)->lport.port_cfg.pwwn)
+#define __vport_nwwn(__vp) ((__vp)->lport.port_cfg.nwwn)
+#define __vport_bfa(__vp) ((__vp)->lport.fcs->bfa)
+#define __vport_fcid(__vp) ((__vp)->lport.pid)
+#define __vport_fabric(__vp) ((__vp)->lport.fabric)
+#define __vport_vfid(__vp) ((__vp)->lport.fabric->vf_id)
#define BFA_FCS_VPORT_MAX_RETRIES 5
/*
@@ -641,9 +641,9 @@ bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs)
bfa_get_attr(fcs->bfa, &ioc_attr);
if (ioc_attr.pci_attr.device_id == BFA_PCI_DEVICE_ID_CT)
- return (BFA_FCS_MAX_VPORTS_SUPP_CT);
+ return BFA_FCS_MAX_VPORTS_SUPP_CT;
else
- return (BFA_FCS_MAX_VPORTS_SUPP_CB);
+ return BFA_FCS_MAX_VPORTS_SUPP_CB;
}
@@ -675,7 +675,7 @@ bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs,
struct bfad_vport_s *vport_drv)
{
if (vport_cfg->pwwn == 0)
- return (BFA_STATUS_INVALID_WWN);
+ return BFA_STATUS_INVALID_WWN;
if (bfa_fcs_port_get_pwwn(&fcs->fabric.bport) == vport_cfg->pwwn)
return BFA_STATUS_VPORT_WWN_BP;
diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h
index 5edde1a8c04..2b973f3c2eb 100644
--- a/drivers/scsi/bnx2i/bnx2i.h
+++ b/drivers/scsi/bnx2i/bnx2i.h
@@ -232,7 +232,6 @@ struct bnx2i_conn {
struct iscsi_cls_conn *cls_conn;
struct bnx2i_hba *hba;
struct completion cmd_cleanup_cmpl;
- int is_bound;
u32 iscsi_conn_cid;
#define BNX2I_CID_RESERVED 0x5AFF
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index cafb888c237..132898c88d5 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -1161,9 +1161,6 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
struct bnx2i_cmd *cmd = task->dd_data;
struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
- if (!bnx2i_conn->is_bound)
- return -ENOTCONN;
-
/*
* If there is no scsi_cmnd this must be a mgmt task
*/
@@ -1371,7 +1368,6 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
bnx2i_conn->ep = bnx2i_ep;
bnx2i_conn->iscsi_conn_cid = bnx2i_ep->ep_iscsi_cid;
bnx2i_conn->fw_cid = bnx2i_ep->ep_cid;
- bnx2i_conn->is_bound = 1;
ret_code = bnx2i_bind_conn_to_iscsi_cid(hba, bnx2i_conn,
bnx2i_ep->ep_iscsi_cid);
@@ -1883,7 +1879,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
bnx2i_ep = ep->dd_data;
- /* driver should not attempt connection cleanup untill TCP_CONNECT
+ /* driver should not attempt connection cleanup until TCP_CONNECT
* completes either successfully or fails. Timeout is 9-secs, so
* wait for it to complete
*/
@@ -1896,9 +1892,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
conn = bnx2i_conn->cls_conn->dd_data;
session = conn->session;
- spin_lock_bh(&session->lock);
- bnx2i_conn->is_bound = 0;
- spin_unlock_bh(&session->lock);
+ iscsi_suspend_queue(conn);
}
hba = bnx2i_ep->hba;
@@ -2034,7 +2028,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_NETDEV_NAME,
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index 63abb06c4ed..9129bcf117c 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -141,6 +141,7 @@ static const struct value_name_pair serv_out12_arr[] = {
static const struct value_name_pair serv_in16_arr[] = {
{0x10, "Read capacity(16)"},
{0x11, "Read long(16)"},
+ {0x12, "Get LBA status"},
};
#define SERV_IN16_SZ ARRAY_SIZE(serv_in16_arr)
diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
index 2631bddd255..969c83162cc 100644
--- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
+++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c
@@ -937,7 +937,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index 075e2397273..6c59c02c1ed 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -1509,7 +1509,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
* Try anyway?
*
* We could, BUT: Sometimes the TRM_S1040 misses to produce a Selection
- * Timeout, a Disconnect or a Reselction IRQ, so we would be screwed!
+ * Timeout, a Disconnect or a Reselection IRQ, so we would be screwed!
* (This is likely to be a bug in the hardware. Obviously, most people
* only have one initiator per SCSI bus.)
* Instead let this fail and have the timer make sure the command is
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index 3ee1cbc8947..e19a1a55270 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -226,7 +226,7 @@ store_dh_state(struct device *dev, struct device_attribute *attr,
* Activate a device handler
*/
if (scsi_dh->activate)
- err = scsi_dh->activate(sdev);
+ err = scsi_dh->activate(sdev, NULL, NULL);
else
err = 0;
}
@@ -304,18 +304,15 @@ static int scsi_dh_notifier(struct notifier_block *nb,
sdev = to_scsi_device(dev);
if (action == BUS_NOTIFY_ADD_DEVICE) {
+ err = device_create_file(dev, &scsi_dh_state_attr);
+ /* don't care about err */
devinfo = device_handler_match(NULL, sdev);
- if (!devinfo)
- goto out;
-
- err = scsi_dh_handler_attach(sdev, devinfo);
- if (!err)
- err = device_create_file(dev, &scsi_dh_state_attr);
+ if (devinfo)
+ err = scsi_dh_handler_attach(sdev, devinfo);
} else if (action == BUS_NOTIFY_DEL_DEVICE) {
device_remove_file(dev, &scsi_dh_state_attr);
scsi_dh_handler_detach(sdev, NULL);
}
-out:
return err;
}
@@ -423,10 +420,17 @@ EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
/*
* scsi_dh_activate - activate the path associated with the scsi_device
* corresponding to the given request queue.
- * @q - Request queue that is associated with the scsi_device to be
- * activated.
+ * Returns immediately without waiting for activation to be completed.
+ * @q - Request queue that is associated with the scsi_device to be
+ * activated.
+ * @fn - Function to be called upon completion of the activation.
+ * Function fn is called with data (below) and the error code.
+ * Function fn may be called from the same calling context. So,
+ * do not hold the lock in the caller which may be needed in fn.
+ * @data - data passed to the function fn upon completion.
+ *
*/
-int scsi_dh_activate(struct request_queue *q)
+int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
{
int err = 0;
unsigned long flags;
@@ -445,7 +449,7 @@ int scsi_dh_activate(struct request_queue *q)
return err;
if (scsi_dh->activate)
- err = scsi_dh->activate(sdev);
+ err = scsi_dh->activate(sdev, fn, data);
put_device(&sdev->sdev_gendev);
return err;
}
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index b5cdefaf260..4f0d0138f48 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -60,11 +60,17 @@ struct alua_dh_data {
int bufflen;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
int senselen;
+ struct scsi_device *sdev;
+ activate_complete callback_fn;
+ void *callback_data;
};
#define ALUA_POLICY_SWITCH_CURRENT 0
#define ALUA_POLICY_SWITCH_ALL 1
+static char print_alua_state(int);
+static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *);
+
static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
@@ -231,18 +237,71 @@ done:
}
/*
+ * alua_stpg - Evaluate SET TARGET GROUP STATES
+ * @sdev: the device to be evaluated
+ * @state: the new target group state
+ *
+ * Send a SET TARGET GROUP STATES command to the device.
+ * We only have to test here if we should resubmit the command;
+ * any other error is assumed as a failure.
+ */
+static void stpg_endio(struct request *req, int error)
+{
+ struct alua_dh_data *h = req->end_io_data;
+ struct scsi_sense_hdr sense_hdr;
+ unsigned err = SCSI_DH_IO;
+
+ if (error || host_byte(req->errors) != DID_OK ||
+ msg_byte(req->errors) != COMMAND_COMPLETE)
+ goto done;
+
+ if (err == SCSI_DH_IO && h->senselen > 0) {
+ err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
+ &sense_hdr);
+ if (!err) {
+ err = SCSI_DH_IO;
+ goto done;
+ }
+ err = alua_check_sense(h->sdev, &sense_hdr);
+ if (err == ADD_TO_MLQUEUE) {
+ err = SCSI_DH_RETRY;
+ goto done;
+ }
+ sdev_printk(KERN_INFO, h->sdev,
+ "%s: stpg sense code: %02x/%02x/%02x\n",
+ ALUA_DH_NAME, sense_hdr.sense_key,
+ sense_hdr.asc, sense_hdr.ascq);
+ err = SCSI_DH_IO;
+ }
+ if (err == SCSI_DH_OK) {
+ h->state = TPGS_STATE_OPTIMIZED;
+ sdev_printk(KERN_INFO, h->sdev,
+ "%s: port group %02x switched to state %c\n",
+ ALUA_DH_NAME, h->group_id,
+ print_alua_state(h->state));
+ }
+done:
+ blk_put_request(req);
+ if (h->callback_fn) {
+ h->callback_fn(h->callback_data, err);
+ h->callback_fn = h->callback_data = NULL;
+ }
+ return;
+}
+
+/*
* submit_stpg - Issue a SET TARGET GROUP STATES command
- * @sdev: sdev the command should be sent to
*
* Currently we're only setting the current target port group state
* to 'active/optimized' and let the array firmware figure out
* the states of the remaining groups.
*/
-static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
+static unsigned submit_stpg(struct alua_dh_data *h)
{
struct request *rq;
int err = SCSI_DH_RES_TEMP_UNAVAIL;
int stpg_len = 8;
+ struct scsi_device *sdev = h->sdev;
/* Prepare the data buffer */
memset(h->buff, 0, stpg_len);
@@ -252,7 +311,7 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
if (!rq)
- goto done;
+ return SCSI_DH_RES_TEMP_UNAVAIL;
/* Prepare the command. */
rq->cmd[0] = MAINTENANCE_OUT;
@@ -266,17 +325,9 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
rq->sense = h->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
rq->sense_len = h->senselen = 0;
+ rq->end_io_data = h;
- err = blk_execute_rq(rq->q, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: stpg failed with %x\n",
- ALUA_DH_NAME, rq->errors);
- h->senselen = rq->sense_len;
- err = SCSI_DH_IO;
- }
- blk_put_request(rq);
-done:
+ blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio);
return err;
}
@@ -477,50 +528,6 @@ static int alua_check_sense(struct scsi_device *sdev,
}
/*
- * alua_stpg - Evaluate SET TARGET GROUP STATES
- * @sdev: the device to be evaluated
- * @state: the new target group state
- *
- * Send a SET TARGET GROUP STATES command to the device.
- * We only have to test here if we should resubmit the command;
- * any other error is assumed as a failure.
- */
-static int alua_stpg(struct scsi_device *sdev, int state,
- struct alua_dh_data *h)
-{
- struct scsi_sense_hdr sense_hdr;
- unsigned err;
- int retry = ALUA_FAILOVER_RETRIES;
-
- retry:
- err = submit_stpg(sdev, h);
- if (err == SCSI_DH_IO && h->senselen > 0) {
- err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
- &sense_hdr);
- if (!err)
- return SCSI_DH_IO;
- err = alua_check_sense(sdev, &sense_hdr);
- if (retry > 0 && err == ADD_TO_MLQUEUE) {
- retry--;
- goto retry;
- }
- sdev_printk(KERN_INFO, sdev,
- "%s: stpg sense code: %02x/%02x/%02x\n",
- ALUA_DH_NAME, sense_hdr.sense_key,
- sense_hdr.asc, sense_hdr.ascq);
- err = SCSI_DH_IO;
- }
- if (err == SCSI_DH_OK) {
- h->state = state;
- sdev_printk(KERN_INFO, sdev,
- "%s: port group %02x switched to state %c\n",
- ALUA_DH_NAME, h->group_id,
- print_alua_state(h->state) );
- }
- return err;
-}
-
-/*
* alua_rtpg - Evaluate REPORT TARGET GROUP STATES
* @sdev: the device to be evaluated.
*
@@ -652,7 +659,8 @@ out:
* based on a certain policy. But until we actually encounter them it
* should be okay.
*/
-static int alua_activate(struct scsi_device *sdev)
+static int alua_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
struct alua_dh_data *h = get_alua_data(sdev);
int err = SCSI_DH_OK;
@@ -663,11 +671,19 @@ static int alua_activate(struct scsi_device *sdev)
goto out;
}
- if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED)
- err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h);
+ if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) {
+ h->callback_fn = fn;
+ h->callback_data = data;
+ err = submit_stpg(h);
+ if (err == SCSI_DH_OK)
+ return 0;
+ h->callback_fn = h->callback_data = NULL;
+ }
out:
- return err;
+ if (fn)
+ fn(data, err);
+ return 0;
}
/*
@@ -745,6 +761,7 @@ static int alua_bus_attach(struct scsi_device *sdev)
h->rel_port = -1;
h->buff = h->inq;
h->bufflen = ALUA_INQUIRY_SIZE;
+ h->sdev = sdev;
err = alua_initialize(sdev, h);
if (err != SCSI_DH_OK)
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 0cffe84976f..61966750bd6 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -528,7 +528,8 @@ retry:
return err;
}
-static int clariion_activate(struct scsi_device *sdev)
+static int clariion_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
struct clariion_dh_data *csdev = get_clariion_data(sdev);
int result;
@@ -559,7 +560,9 @@ done:
csdev->port, lun_state[csdev->lun_state],
csdev->default_sp + 'A');
- return result;
+ if (fn)
+ fn(data, result);
+ return 0;
}
/*
* params - parameters in the following format
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
index f7da7530875..857fdd6032b 100644
--- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c
+++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -39,8 +39,14 @@ struct hp_sw_dh_data {
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
int path_state;
int retries;
+ int retry_cnt;
+ struct scsi_device *sdev;
+ activate_complete callback_fn;
+ void *callback_data;
};
+static int hp_sw_start_stop(struct hp_sw_dh_data *);
+
static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
@@ -191,19 +197,53 @@ static int start_done(struct scsi_device *sdev, unsigned char *sense)
return rc;
}
+static void start_stop_endio(struct request *req, int error)
+{
+ struct hp_sw_dh_data *h = req->end_io_data;
+ unsigned err = SCSI_DH_OK;
+
+ if (error || host_byte(req->errors) != DID_OK ||
+ msg_byte(req->errors) != COMMAND_COMPLETE) {
+ sdev_printk(KERN_WARNING, h->sdev,
+ "%s: sending start_stop_unit failed with %x\n",
+ HP_SW_NAME, req->errors);
+ err = SCSI_DH_IO;
+ goto done;
+ }
+
+ if (req->sense_len > 0) {
+ err = start_done(h->sdev, h->sense);
+ if (err == SCSI_DH_RETRY) {
+ err = SCSI_DH_IO;
+ if (--h->retry_cnt) {
+ blk_put_request(req);
+ err = hp_sw_start_stop(h);
+ if (err == SCSI_DH_OK)
+ return;
+ }
+ }
+ }
+done:
+ blk_put_request(req);
+ if (h->callback_fn) {
+ h->callback_fn(h->callback_data, err);
+ h->callback_fn = h->callback_data = NULL;
+ }
+ return;
+
+}
+
/*
* hp_sw_start_stop - Send START STOP UNIT command
* @sdev: sdev command should be sent to
*
* Sending START STOP UNIT activates the SP.
*/
-static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h)
+static int hp_sw_start_stop(struct hp_sw_dh_data *h)
{
struct request *req;
- int ret, retry;
-retry:
- req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
+ req = blk_get_request(h->sdev->request_queue, WRITE, GFP_ATOMIC);
if (!req)
return SCSI_DH_RES_TEMP_UNAVAIL;
@@ -217,32 +257,10 @@ retry:
req->sense = h->sense;
memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
req->sense_len = 0;
- retry = h->retries;
-
- ret = blk_execute_rq(req->q, NULL, req, 1);
- if (ret == -EIO) {
- if (req->sense_len > 0) {
- ret = start_done(sdev, h->sense);
- } else {
- sdev_printk(KERN_WARNING, sdev,
- "%s: sending start_stop_unit failed with %x\n",
- HP_SW_NAME, req->errors);
- ret = SCSI_DH_IO;
- }
- } else
- ret = SCSI_DH_OK;
+ req->end_io_data = h;
- if (ret == SCSI_DH_RETRY) {
- if (--retry) {
- blk_put_request(req);
- goto retry;
- }
- ret = SCSI_DH_IO;
- }
-
- blk_put_request(req);
-
- return ret;
+ blk_execute_rq_nowait(req->q, NULL, req, 1, start_stop_endio);
+ return SCSI_DH_OK;
}
static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
@@ -268,7 +286,8 @@ static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
* activate the passive path (and deactivate the
* previously active one).
*/
-static int hp_sw_activate(struct scsi_device *sdev)
+static int hp_sw_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
int ret = SCSI_DH_OK;
struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
@@ -276,14 +295,18 @@ static int hp_sw_activate(struct scsi_device *sdev)
ret = hp_sw_tur(sdev, h);
if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
- ret = hp_sw_start_stop(sdev, h);
+ h->retry_cnt = h->retries;
+ h->callback_fn = fn;
+ h->callback_data = data;
+ ret = hp_sw_start_stop(h);
if (ret == SCSI_DH_OK)
- sdev_printk(KERN_INFO, sdev,
- "%s: activated path\n",
- HP_SW_NAME);
+ return 0;
+ h->callback_fn = h->callback_data = NULL;
}
- return ret;
+ if (fn)
+ fn(data, ret);
+ return 0;
}
static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
@@ -326,6 +349,7 @@ static int hp_sw_bus_attach(struct scsi_device *sdev)
h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
h->path_state = HP_SW_PATH_UNINITIALIZED;
h->retries = HP_SW_RETRIES;
+ h->sdev = sdev;
ret = hp_sw_tur(sdev, h);
if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 268189d31d9..47cfe1c49c3 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -22,6 +22,7 @@
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
+#include <linux/workqueue.h>
#define RDAC_NAME "rdac"
#define RDAC_RETRY_COUNT 5
@@ -138,7 +139,13 @@ struct rdac_controller {
} mode_select;
u8 index;
u8 array_name[ARRAY_LABEL_LEN];
+ spinlock_t ms_lock;
+ int ms_queued;
+ struct work_struct ms_work;
+ struct scsi_device *ms_sdev;
+ struct list_head ms_head;
};
+
struct c8_inquiry {
u8 peripheral_info;
u8 page_code; /* 0xC8 */
@@ -198,8 +205,17 @@ static const char *lun_state[] =
"owned (AVT mode)",
};
+struct rdac_queue_data {
+ struct list_head entry;
+ struct rdac_dh_data *h;
+ activate_complete callback_fn;
+ void *callback_data;
+};
+
static LIST_HEAD(ctlr_list);
static DEFINE_SPINLOCK(list_lock);
+static struct workqueue_struct *kmpath_rdacd;
+static void send_mode_select(struct work_struct *work);
/*
* module parameter to enable rdac debug logging.
@@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
rdac_pg->subpage_code = 0x1;
rdac_pg->page_len[0] = 0x01;
rdac_pg->page_len[1] = 0x28;
- rdac_pg->lun_table[h->lun] = 0x81;
} else {
struct rdac_pg_legacy *rdac_pg;
@@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
common = &rdac_pg->common;
rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
rdac_pg->page_len = 0x68;
- rdac_pg->lun_table[h->lun] = 0x81;
}
common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
@@ -325,6 +339,7 @@ static void release_controller(struct kref *kref)
struct rdac_controller *ctlr;
ctlr = container_of(kref, struct rdac_controller, kref);
+ flush_workqueue(kmpath_rdacd);
spin_lock(&list_lock);
list_del(&ctlr->node);
spin_unlock(&list_lock);
@@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
kref_init(&ctlr->kref);
ctlr->use_ms10 = -1;
+ ctlr->ms_queued = 0;
+ ctlr->ms_sdev = NULL;
+ spin_lock_init(&ctlr->ms_lock);
+ INIT_WORK(&ctlr->ms_work, send_mode_select);
+ INIT_LIST_HEAD(&ctlr->ms_head);
list_add(&ctlr->node, &ctlr_list);
done:
spin_unlock(&list_lock);
@@ -490,7 +510,7 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
}
static int mode_select_handle_sense(struct scsi_device *sdev,
- unsigned char *sensebuf)
+ unsigned char *sensebuf)
{
struct scsi_sense_hdr sense_hdr;
int err = SCSI_DH_IO, ret;
@@ -533,11 +553,29 @@ done:
return err;
}
-static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
+static void send_mode_select(struct work_struct *work)
{
+ struct rdac_controller *ctlr =
+ container_of(work, struct rdac_controller, ms_work);
struct request *rq;
+ struct scsi_device *sdev = ctlr->ms_sdev;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
struct request_queue *q = sdev->request_queue;
int err, retry_cnt = RDAC_RETRY_COUNT;
+ struct rdac_queue_data *tmp, *qdata;
+ LIST_HEAD(list);
+ u8 *lun_table;
+
+ spin_lock(&ctlr->ms_lock);
+ list_splice_init(&ctlr->ms_head, &list);
+ ctlr->ms_queued = 0;
+ ctlr->ms_sdev = NULL;
+ spin_unlock(&ctlr->ms_lock);
+
+ if (ctlr->use_ms10)
+ lun_table = ctlr->mode_select.expanded.lun_table;
+ else
+ lun_table = ctlr->mode_select.legacy.lun_table;
retry:
err = SCSI_DH_RES_TEMP_UNAVAIL;
@@ -545,6 +583,10 @@ retry:
if (!rq)
goto done;
+ list_for_each_entry(qdata, &list, entry) {
+ lun_table[qdata->h->lun] = 0x81;
+ }
+
RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
"%s MODE_SELECT command",
(char *) h->ctlr->array_name, h->ctlr->index,
@@ -565,10 +607,45 @@ retry:
}
done:
- return err;
+ list_for_each_entry_safe(qdata, tmp, &list, entry) {
+ list_del(&qdata->entry);
+ if (err == SCSI_DH_OK)
+ qdata->h->state = RDAC_STATE_ACTIVE;
+ if (qdata->callback_fn)
+ qdata->callback_fn(qdata->callback_data, err);
+ kfree(qdata);
+ }
+ return;
+}
+
+static int queue_mode_select(struct scsi_device *sdev,
+ activate_complete fn, void *data)
+{
+ struct rdac_queue_data *qdata;
+ struct rdac_controller *ctlr;
+
+ qdata = kzalloc(sizeof(*qdata), GFP_KERNEL);
+ if (!qdata)
+ return SCSI_DH_RETRY;
+
+ qdata->h = get_rdac_data(sdev);
+ qdata->callback_fn = fn;
+ qdata->callback_data = data;
+
+ ctlr = qdata->h->ctlr;
+ spin_lock(&ctlr->ms_lock);
+ list_add_tail(&qdata->entry, &ctlr->ms_head);
+ if (!ctlr->ms_queued) {
+ ctlr->ms_queued = 1;
+ ctlr->ms_sdev = sdev;
+ queue_work(kmpath_rdacd, &ctlr->ms_work);
+ }
+ spin_unlock(&ctlr->ms_lock);
+ return SCSI_DH_OK;
}
-static int rdac_activate(struct scsi_device *sdev)
+static int rdac_activate(struct scsi_device *sdev,
+ activate_complete fn, void *data)
{
struct rdac_dh_data *h = get_rdac_data(sdev);
int err = SCSI_DH_OK;
@@ -577,10 +654,15 @@ static int rdac_activate(struct scsi_device *sdev)
if (err != SCSI_DH_OK)
goto done;
- if (h->lun_state == RDAC_LUN_UNOWNED)
- err = send_mode_select(sdev, h);
+ if (h->lun_state == RDAC_LUN_UNOWNED) {
+ err = queue_mode_select(sdev, fn, data);
+ if (err == SCSI_DH_OK)
+ return 0;
+ }
done:
- return err;
+ if (fn)
+ fn(data, err);
+ return 0;
}
static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
@@ -790,13 +872,26 @@ static int __init rdac_init(void)
int r;
r = scsi_register_device_handler(&rdac_dh);
- if (r != 0)
+ if (r != 0) {
printk(KERN_ERR "Failed to register scsi device handler.");
+ goto done;
+ }
+
+ /*
+ * Create workqueue to handle mode selects for rdac
+ */
+ kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd");
+ if (!kmpath_rdacd) {
+ scsi_unregister_device_handler(&rdac_dh);
+ printk(KERN_ERR "kmpath_rdacd creation failed.\n");
+ }
+done:
return r;
}
static void __exit rdac_exit(void)
{
+ destroy_workqueue(kmpath_rdacd);
scsi_unregister_device_handler(&rdac_dh);
}
diff --git a/drivers/scsi/dmx3191d.c b/drivers/scsi/dmx3191d.c
index fa738ec8692..207352cc70c 100644
--- a/drivers/scsi/dmx3191d.c
+++ b/drivers/scsi/dmx3191d.c
@@ -31,7 +31,7 @@
#include <scsi/scsi_host.h>
/*
- * Defintions for the generic 5380 driver.
+ * Definitions for the generic 5380 driver.
*/
#define AUTOSENSE
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 704b8e03494..a30ffaa1222 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -66,14 +66,14 @@ LIST_HEAD(fcoe_hostlist);
DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
/* Function Prototypes */
-static int fcoe_reset(struct Scsi_Host *shost);
+static int fcoe_reset(struct Scsi_Host *);
static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
static int fcoe_rcv(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
-static int fcoe_percpu_receive_thread(void *arg);
-static void fcoe_clean_pending_queue(struct fc_lport *lp);
-static void fcoe_percpu_clean(struct fc_lport *lp);
-static int fcoe_link_ok(struct fc_lport *lp);
+static int fcoe_percpu_receive_thread(void *);
+static void fcoe_clean_pending_queue(struct fc_lport *);
+static void fcoe_percpu_clean(struct fc_lport *);
+static int fcoe_link_ok(struct fc_lport *);
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
static int fcoe_hostlist_add(const struct fc_lport *);
@@ -82,15 +82,69 @@ static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
static int fcoe_device_notification(struct notifier_block *, ulong, void *);
static void fcoe_dev_setup(void);
static void fcoe_dev_cleanup(void);
-static struct fcoe_interface *
- fcoe_hostlist_lookup_port(const struct net_device *dev);
+static struct fcoe_interface
+*fcoe_hostlist_lookup_port(const struct net_device *);
+
+static int fcoe_fip_recv(struct sk_buff *, struct net_device *,
+ struct packet_type *, struct net_device *);
+
+static void fcoe_fip_send(struct fcoe_ctlr *, struct sk_buff *);
+static void fcoe_update_src_mac(struct fc_lport *, u8 *);
+static u8 *fcoe_get_src_mac(struct fc_lport *);
+static void fcoe_destroy_work(struct work_struct *);
+
+static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *,
+ unsigned int);
+static int fcoe_ddp_done(struct fc_lport *, u16);
+
+static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *);
+
+static int fcoe_create(const char *, struct kernel_param *);
+static int fcoe_destroy(const char *, struct kernel_param *);
+
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
+ u32 did, struct fc_frame *,
+ unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *,
+ void *),
+ void *, u32 timeout);
+static void fcoe_recv_frame(struct sk_buff *skb);
+
+static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
-/* notification function from net device */
+/* notification function for packets from net device */
static struct notifier_block fcoe_notifier = {
.notifier_call = fcoe_device_notification,
};
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
+/* notification function for CPU hotplug events */
+static struct notifier_block fcoe_cpu_notifier = {
+ .notifier_call = fcoe_cpu_callback,
+};
+
+static struct scsi_transport_template *fcoe_transport_template;
+static struct scsi_transport_template *fcoe_vport_transport_template;
+
+static int fcoe_vport_destroy(struct fc_vport *);
+static int fcoe_vport_create(struct fc_vport *, bool disabled);
+static int fcoe_vport_disable(struct fc_vport *, bool disable);
+static void fcoe_set_vport_symbolic_name(struct fc_vport *);
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+ .frame_send = fcoe_xmit,
+ .ddp_setup = fcoe_ddp_setup,
+ .ddp_done = fcoe_ddp_done,
+ .elsct_send = fcoe_elsct_send,
+ .get_lesb = fcoe_get_lesb,
+};
struct fc_function_template fcoe_transport_function = {
.show_host_node_name = 1,
@@ -123,6 +177,48 @@ struct fc_function_template fcoe_transport_function = {
.issue_fc_host_lip = fcoe_reset,
.terminate_rport_io = fc_rport_terminate_io,
+
+ .vport_create = fcoe_vport_create,
+ .vport_delete = fcoe_vport_destroy,
+ .vport_disable = fcoe_vport_disable,
+ .set_vport_symbolic_name = fcoe_set_vport_symbolic_name,
+
+ .bsg_request = fc_lport_bsg_request,
+};
+
+struct fc_function_template fcoe_vport_transport_function = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_fc4s = 1,
+ .show_host_active_fc4s = 1,
+ .show_host_maxframe_size = 1,
+
+ .show_host_port_id = 1,
+ .show_host_supported_speeds = 1,
+ .get_host_speed = fc_get_host_speed,
+ .show_host_speed = 1,
+ .show_host_port_type = 1,
+ .get_host_port_state = fc_get_host_port_state,
+ .show_host_port_state = 1,
+ .show_host_symbolic_name = 1,
+
+ .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+ .show_rport_maxframe_size = 1,
+ .show_rport_supported_classes = 1,
+
+ .show_host_fabric_name = 1,
+ .show_starget_node_name = 1,
+ .show_starget_port_name = 1,
+ .show_starget_port_id = 1,
+ .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+ .get_fc_host_stats = fc_get_host_stats,
+ .issue_fc_host_lip = fcoe_reset,
+
+ .terminate_rport_io = fc_rport_terminate_io,
+
+ .bsg_request = fc_lport_bsg_request,
};
static struct scsi_host_template fcoe_shost_template = {
@@ -137,20 +233,17 @@ static struct scsi_host_template fcoe_shost_template = {
.change_queue_depth = fc_change_queue_depth,
.change_queue_type = fc_change_queue_type,
.this_id = -1,
- .cmd_per_lun = 32,
+ .cmd_per_lun = 3,
.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
.use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = SG_ALL,
.max_sectors = 0xffff,
};
-static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *ptype,
- struct net_device *orig_dev);
/**
- * fcoe_interface_setup()
- * @fcoe: new fcoe_interface
- * @netdev : ptr to the associated netdevice struct
+ * fcoe_interface_setup() - Setup a FCoE interface
+ * @fcoe: The new FCoE interface
+ * @netdev: The net device that the fcoe interface is on
*
* Returns : 0 for success
* Locking: must be called with the RTNL mutex held
@@ -160,23 +253,36 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
{
struct fcoe_ctlr *fip = &fcoe->ctlr;
struct netdev_hw_addr *ha;
+ struct net_device *real_dev;
u8 flogi_maddr[ETH_ALEN];
+ const struct net_device_ops *ops;
fcoe->netdev = netdev;
+ /* Let LLD initialize for FCoE */
+ ops = netdev->netdev_ops;
+ if (ops->ndo_fcoe_enable) {
+ if (ops->ndo_fcoe_enable(netdev))
+ FCOE_NETDEV_DBG(netdev, "Failed to enable FCoE"
+ " specific feature for LLD.\n");
+ }
+
/* Do not support for bonding device */
if ((netdev->priv_flags & IFF_MASTER_ALB) ||
(netdev->priv_flags & IFF_SLAVE_INACTIVE) ||
(netdev->priv_flags & IFF_MASTER_8023AD)) {
+ FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");
return -EOPNOTSUPP;
}
/* look for SAN MAC address, if multiple SAN MACs exist, only
* use the first one for SPMA */
+ real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
+ vlan_dev_real_dev(netdev) : netdev;
rcu_read_lock();
- for_each_dev_addr(netdev, ha) {
+ for_each_dev_addr(real_dev, ha) {
if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
- (is_valid_ether_addr(fip->ctl_src_addr))) {
+ (is_valid_ether_addr(ha->addr))) {
memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN);
fip->spma = 1;
break;
@@ -216,19 +322,16 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
return 0;
}
-static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb);
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new);
-static void fcoe_destroy_work(struct work_struct *work);
-
/**
- * fcoe_interface_create()
- * @netdev: network interface
+ * fcoe_interface_create() - Create a FCoE interface on a net device
+ * @netdev: The net device to create the FCoE interface on
*
* Returns: pointer to a struct fcoe_interface or NULL on error
*/
static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
{
struct fcoe_interface *fcoe;
+ int err;
fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
if (!fcoe) {
@@ -245,15 +348,22 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
fcoe_ctlr_init(&fcoe->ctlr);
fcoe->ctlr.send = fcoe_fip_send;
fcoe->ctlr.update_mac = fcoe_update_src_mac;
+ fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
- fcoe_interface_setup(fcoe, netdev);
+ err = fcoe_interface_setup(fcoe, netdev);
+ if (err) {
+ fcoe_ctlr_destroy(&fcoe->ctlr);
+ kfree(fcoe);
+ dev_put(netdev);
+ return NULL;
+ }
return fcoe;
}
/**
- * fcoe_interface_cleanup() - clean up netdev configurations
- * @fcoe:
+ * fcoe_interface_cleanup() - Clean up a FCoE interface
+ * @fcoe: The FCoE interface to be cleaned up
*
* Caller must be holding the RTNL mutex
*/
@@ -262,6 +372,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
struct net_device *netdev = fcoe->netdev;
struct fcoe_ctlr *fip = &fcoe->ctlr;
u8 flogi_maddr[ETH_ALEN];
+ const struct net_device_ops *ops;
/*
* Don't listen for Ethernet packets anymore.
@@ -276,16 +387,22 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
/* Delete secondary MAC addresses */
memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
dev_unicast_delete(netdev, flogi_maddr);
- if (!is_zero_ether_addr(fip->data_src_addr))
- dev_unicast_delete(netdev, fip->data_src_addr);
if (fip->spma)
dev_unicast_delete(netdev, fip->ctl_src_addr);
dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+
+ /* Tell the LLD we are done w/ FCoE */
+ ops = netdev->netdev_ops;
+ if (ops->ndo_fcoe_disable) {
+ if (ops->ndo_fcoe_disable(netdev))
+ FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"
+ " specific feature for LLD.\n");
+ }
}
/**
* fcoe_interface_release() - fcoe_port kref release function
- * @kref: embedded reference count in an fcoe_interface struct
+ * @kref: Embedded reference count in an fcoe_interface struct
*/
static void fcoe_interface_release(struct kref *kref)
{
@@ -301,8 +418,8 @@ static void fcoe_interface_release(struct kref *kref)
}
/**
- * fcoe_interface_get()
- * @fcoe:
+ * fcoe_interface_get() - Get a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be held
*/
static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
{
@@ -310,8 +427,8 @@ static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
}
/**
- * fcoe_interface_put()
- * @fcoe:
+ * fcoe_interface_put() - Put a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be released
*/
static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
{
@@ -319,15 +436,16 @@ static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
}
/**
- * fcoe_fip_recv - handle a received FIP frame.
- * @skb: the receive skb
- * @dev: associated &net_device
- * @ptype: the &packet_type structure which was used to register this handler.
- * @orig_dev: original receive &net_device, in case @dev is a bond.
+ * fcoe_fip_recv() - Handler for received FIP frames
+ * @skb: The receive skb
+ * @netdev: The associated net device
+ * @ptype: The packet_type structure which was used to register this handler
+ * @orig_dev: The original net_device the the skb was received on.
+ * (in case dev is a bond)
*
* Returns: 0 for success
*/
-static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
+static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *ptype,
struct net_device *orig_dev)
{
@@ -339,9 +457,9 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
}
/**
- * fcoe_fip_send() - send an Ethernet-encapsulated FIP frame.
- * @fip: FCoE controller.
- * @skb: FIP Packet.
+ * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame
+ * @fip: The FCoE controller
+ * @skb: The FIP packet to be sent
*/
static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
@@ -350,88 +468,101 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
}
/**
- * fcoe_update_src_mac() - Update Ethernet MAC filters.
- * @fip: FCoE controller.
- * @old: Unicast MAC address to delete if the MAC is non-zero.
- * @new: Unicast MAC address to add.
+ * fcoe_update_src_mac() - Update the Ethernet MAC filters
+ * @lport: The local port to update the source MAC on
+ * @addr: Unicast MAC address to add
*
* Remove any previously-set unicast MAC filter.
* Add secondary FCoE MAC address filter for our OUI.
*/
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new)
+static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
{
- struct fcoe_interface *fcoe;
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->fcoe;
- fcoe = fcoe_from_ctlr(fip);
rtnl_lock();
- if (!is_zero_ether_addr(old))
- dev_unicast_delete(fcoe->netdev, old);
- dev_unicast_add(fcoe->netdev, new);
+ if (!is_zero_ether_addr(port->data_src_addr))
+ dev_unicast_delete(fcoe->netdev, port->data_src_addr);
+ if (!is_zero_ether_addr(addr))
+ dev_unicast_add(fcoe->netdev, addr);
+ memcpy(port->data_src_addr, addr, ETH_ALEN);
rtnl_unlock();
}
/**
- * fcoe_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
+ * fcoe_get_src_mac() - return the Ethernet source address for an lport
+ * @lport: libfc lport
+ */
+static u8 *fcoe_get_src_mac(struct fc_lport *lport)
+{
+ struct fcoe_port *port = lport_priv(lport);
+
+ return port->data_src_addr;
+}
+
+/**
+ * fcoe_lport_config() - Set up a local port
+ * @lport: The local port to be setup
*
* Returns: 0 for success
*/
-static int fcoe_lport_config(struct fc_lport *lp)
+static int fcoe_lport_config(struct fc_lport *lport)
{
- lp->link_up = 0;
- lp->qfull = 0;
- lp->max_retry_count = 3;
- lp->max_rport_retry_count = 3;
- lp->e_d_tov = 2 * 1000; /* FC-FS default */
- lp->r_a_tov = 2 * 2 * 1000;
- lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
- FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
- fc_lport_init_stats(lp);
+ lport->link_up = 0;
+ lport->qfull = 0;
+ lport->max_retry_count = 3;
+ lport->max_rport_retry_count = 3;
+ lport->e_d_tov = 2 * 1000; /* FC-FS default */
+ lport->r_a_tov = 2 * 2 * 1000;
+ lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+ FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+ lport->does_npiv = 1;
+
+ fc_lport_init_stats(lport);
/* lport fc_lport related configuration */
- fc_lport_config(lp);
+ fc_lport_config(lport);
/* offload related configuration */
- lp->crc_offload = 0;
- lp->seq_offload = 0;
- lp->lro_enabled = 0;
- lp->lro_xid = 0;
- lp->lso_max = 0;
+ lport->crc_offload = 0;
+ lport->seq_offload = 0;
+ lport->lro_enabled = 0;
+ lport->lro_xid = 0;
+ lport->lso_max = 0;
return 0;
}
/**
- * fcoe_queue_timer() - fcoe queue timer
- * @lp: the fc_lport pointer
+ * fcoe_queue_timer() - The fcoe queue timer
+ * @lport: The local port
*
* Calls fcoe_check_wait_queue on timeout
- *
*/
-static void fcoe_queue_timer(ulong lp)
+static void fcoe_queue_timer(ulong lport)
{
- fcoe_check_wait_queue((struct fc_lport *)lp, NULL);
+ fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
}
/**
- * fcoe_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
+ * fcoe_netdev_config() - Set up net devive for SW FCoE
+ * @lport: The local port that is associated with the net device
+ * @netdev: The associated net device
*
- * Must be called after fcoe_lport_config() as it will use lport mutex
+ * Must be called after fcoe_lport_config() as it will use local port mutex
*
- * Returns : 0 for success
+ * Returns: 0 for success
*/
-static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
{
u32 mfs;
u64 wwnn, wwpn;
struct fcoe_interface *fcoe;
struct fcoe_port *port;
+ int vid = 0;
/* Setup lport private data to point to fcoe softc */
- port = lport_priv(lp);
+ port = lport_priv(lport);
fcoe = port->fcoe;
/*
@@ -439,86 +570,112 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
* user-configured limit. If the MFS is too low, fcoe_link_ok()
* will return 0, so do this first.
*/
- mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
- sizeof(struct fcoe_crc_eof));
- if (fc_set_mfs(lp, mfs))
+ mfs = netdev->mtu;
+ if (netdev->features & NETIF_F_FCOE_MTU) {
+ mfs = FCOE_MTU;
+ FCOE_NETDEV_DBG(netdev, "Supports FCOE_MTU of %d bytes\n", mfs);
+ }
+ mfs -= (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof));
+ if (fc_set_mfs(lport, mfs))
return -EINVAL;
/* offload features support */
if (netdev->features & NETIF_F_SG)
- lp->sg_supp = 1;
+ lport->sg_supp = 1;
if (netdev->features & NETIF_F_FCOE_CRC) {
- lp->crc_offload = 1;
+ lport->crc_offload = 1;
FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
}
if (netdev->features & NETIF_F_FSO) {
- lp->seq_offload = 1;
- lp->lso_max = netdev->gso_max_size;
+ lport->seq_offload = 1;
+ lport->lso_max = netdev->gso_max_size;
FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
- lp->lso_max);
+ lport->lso_max);
}
if (netdev->fcoe_ddp_xid) {
- lp->lro_enabled = 1;
- lp->lro_xid = netdev->fcoe_ddp_xid;
+ lport->lro_enabled = 1;
+ lport->lro_xid = netdev->fcoe_ddp_xid;
FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
- lp->lro_xid);
+ lport->lro_xid);
}
skb_queue_head_init(&port->fcoe_pending_queue);
port->fcoe_pending_queue_active = 0;
- setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp);
+ setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
- wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0);
- fc_set_wwnn(lp, wwnn);
- /* XXX - 3rd arg needs to be vlan id */
- wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0);
- fc_set_wwpn(lp, wwpn);
+ if (!lport->vport) {
+ /*
+ * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
+ * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
+ * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
+ */
+ if (netdev->priv_flags & IFF_802_1Q_VLAN)
+ vid = vlan_dev_vlan_id(netdev);
+ wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+ fc_set_wwnn(lport, wwnn);
+ wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, vid);
+ fc_set_wwpn(lport, wwpn);
+ }
return 0;
}
/**
- * fcoe_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
+ * fcoe_shost_config() - Set up the SCSI host associated with a local port
+ * @lport: The local port
+ * @shost: The SCSI host to associate with the local port
+ * @dev: The device associated with the SCSI host
*
* Must be called after fcoe_lport_config() and fcoe_netdev_config()
*
- * Returns : 0 for success
+ * Returns: 0 for success
*/
-static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
- struct device *dev)
+static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost,
+ struct device *dev)
{
int rc = 0;
/* lport scsi host config */
- lp->host = shost;
-
- lp->host->max_lun = FCOE_MAX_LUN;
- lp->host->max_id = FCOE_MAX_FCP_TARGET;
- lp->host->max_channel = 0;
- lp->host->transportt = scsi_transport_fcoe_sw;
+ lport->host->max_lun = FCOE_MAX_LUN;
+ lport->host->max_id = FCOE_MAX_FCP_TARGET;
+ lport->host->max_channel = 0;
+ if (lport->vport)
+ lport->host->transportt = fcoe_vport_transport_template;
+ else
+ lport->host->transportt = fcoe_transport_template;
/* add the new host to the SCSI-ml */
- rc = scsi_add_host(lp->host, dev);
+ rc = scsi_add_host(lport->host, dev);
if (rc) {
- FCOE_NETDEV_DBG(fcoe_netdev(lp), "fcoe_shost_config: "
+ FCOE_NETDEV_DBG(fcoe_netdev(lport), "fcoe_shost_config: "
"error on scsi_add_host\n");
return rc;
}
- sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
- FCOE_NAME, FCOE_VERSION,
- fcoe_netdev(lp)->name);
+
+ if (!lport->vport)
+ fc_host_max_npiv_vports(lport->host) = USHORT_MAX;
+
+ snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
+ "%s v%s over %s", FCOE_NAME, FCOE_VERSION,
+ fcoe_netdev(lport)->name);
return 0;
}
-/*
- * fcoe_oem_match() - match for read types IO
- * @fp: the fc_frame for new IO.
+/**
+ * fcoe_oem_match() - The match routine for the offloaded exchange manager
+ * @fp: The I/O frame
+ *
+ * This routine will be associated with an exchange manager (EM). When
+ * the libfc exchange handling code is looking for an EM to use it will
+ * call this routine and pass it the frame that it wishes to send. This
+ * routine will return True if the associated EM is to be used and False
+ * if the echange code should continue looking for an EM.
*
- * Returns : true for read types IO, otherwise returns false.
+ * The offload EM that this routine is associated with will handle any
+ * packets that are for SCSI read requests.
+ *
+ * Returns: True for read types I/O, otherwise returns false.
*/
bool fcoe_oem_match(struct fc_frame *fp)
{
@@ -527,14 +684,14 @@ bool fcoe_oem_match(struct fc_frame *fp)
}
/**
- * fcoe_em_config() - allocates em for this lport
- * @lp: the fcoe that em is to allocated for
+ * fcoe_em_config() - Allocate and configure an exchange manager
+ * @lport: The local port that the new EM will be associated with
*
- * Returns : 0 on success
+ * Returns: 0 on success
*/
-static inline int fcoe_em_config(struct fc_lport *lp)
+static inline int fcoe_em_config(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->fcoe;
struct fcoe_interface *oldfcoe = NULL;
struct net_device *old_real_dev, *cur_real_dev;
@@ -545,8 +702,9 @@ static inline int fcoe_em_config(struct fc_lport *lp)
* Check if need to allocate an em instance for
* offload exchange ids to be shared across all VN_PORTs/lport.
*/
- if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) {
- lp->lro_xid = 0;
+ if (!lport->lro_enabled || !lport->lro_xid ||
+ (lport->lro_xid >= max_xid)) {
+ lport->lro_xid = 0;
goto skip_oem;
}
@@ -572,16 +730,16 @@ static inline int fcoe_em_config(struct fc_lport *lp)
}
if (fcoe->oem) {
- if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) {
+ if (!fc_exch_mgr_add(lport, fcoe->oem, fcoe_oem_match)) {
printk(KERN_ERR "fcoe_em_config: failed to add "
"offload em:%p on interface:%s\n",
fcoe->oem, fcoe->netdev->name);
return -ENOMEM;
}
} else {
- fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3,
- FCOE_MIN_XID, lp->lro_xid,
- fcoe_oem_match);
+ fcoe->oem = fc_exch_mgr_alloc(lport, FC_CLASS_3,
+ FCOE_MIN_XID, lport->lro_xid,
+ fcoe_oem_match);
if (!fcoe->oem) {
printk(KERN_ERR "fcoe_em_config: failed to allocate "
"em for offload exches on interface:%s\n",
@@ -593,10 +751,10 @@ static inline int fcoe_em_config(struct fc_lport *lp)
/*
* Exclude offload EM xid range from next EM xid range.
*/
- min_xid += lp->lro_xid + 1;
+ min_xid += lport->lro_xid + 1;
skip_oem:
- if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) {
+ if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, min_xid, max_xid, NULL)) {
printk(KERN_ERR "fcoe_em_config: failed to "
"allocate em on interface %s\n", fcoe->netdev->name);
return -ENOMEM;
@@ -606,8 +764,8 @@ skip_oem:
}
/**
- * fcoe_if_destroy() - FCoE software HBA tear-down function
- * @lport: fc_lport to destroy
+ * fcoe_if_destroy() - Tear down a SW FCoE instance
+ * @lport: The local port to be destroyed
*/
static void fcoe_if_destroy(struct fc_lport *lport)
{
@@ -630,6 +788,11 @@ static void fcoe_if_destroy(struct fc_lport *lport)
/* Free existing transmit skbs */
fcoe_clean_pending_queue(lport);
+ rtnl_lock();
+ if (!is_zero_ether_addr(port->data_src_addr))
+ dev_unicast_delete(netdev, port->data_src_addr);
+ rtnl_unlock();
+
/* receives may not be stopped until after this */
fcoe_interface_put(fcoe);
@@ -650,82 +813,89 @@ static void fcoe_if_destroy(struct fc_lport *lport)
scsi_host_put(lport->host);
}
-/*
- * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp: the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
+/**
+ * fcoe_ddp_setup() - Call a LLD's ddp_setup through the net device
+ * @lport: The local port to setup DDP for
+ * @xid: The exchange ID for this DDP transfer
+ * @sgl: The scatterlist describing this transfer
+ * @sgc: The number of sg items
*
- * Returns : 0 no ddp
+ * Returns: 0 if the DDP context was not configured
*/
-static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
- struct scatterlist *sgl, unsigned int sgc)
+static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid,
+ struct scatterlist *sgl, unsigned int sgc)
{
- struct net_device *n = fcoe_netdev(lp);
+ struct net_device *netdev = fcoe_netdev(lport);
- if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
- return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+ if (netdev->netdev_ops->ndo_fcoe_ddp_setup)
+ return netdev->netdev_ops->ndo_fcoe_ddp_setup(netdev,
+ xid, sgl,
+ sgc);
return 0;
}
-/*
- * fcoe_ddp_done - calls LLD's ddp_done through net_device
- * @lp: the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
+/**
+ * fcoe_ddp_done() - Call a LLD's ddp_done through the net device
+ * @lport: The local port to complete DDP on
+ * @xid: The exchange ID for this DDP transfer
*
- * Returns : the length of data that have been completed by ddp
+ * Returns: the length of data that have been completed by DDP
*/
-static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
{
- struct net_device *n = fcoe_netdev(lp);
+ struct net_device *netdev = fcoe_netdev(lport);
- if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
- return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+ if (netdev->netdev_ops->ndo_fcoe_ddp_done)
+ return netdev->netdev_ops->ndo_fcoe_ddp_done(netdev, xid);
return 0;
}
-static struct libfc_function_template fcoe_libfc_fcn_templ = {
- .frame_send = fcoe_xmit,
- .ddp_setup = fcoe_ddp_setup,
- .ddp_done = fcoe_ddp_done,
-};
-
/**
- * fcoe_if_create() - this function creates the fcoe port
- * @fcoe: fcoe_interface structure to create an fc_lport instance on
- * @parent: device pointer to be the parent in sysfs for the SCSI host
+ * fcoe_if_create() - Create a FCoE instance on an interface
+ * @fcoe: The FCoE interface to create a local port on
+ * @parent: The device pointer to be the parent in sysfs for the SCSI host
+ * @npiv: Indicates if the port is a vport or not
*
- * Creates fc_lport struct and scsi_host for lport, configures lport.
+ * Creates a fc_lport instance and a Scsi_Host instance and configure them.
*
- * Returns : The allocated fc_lport or an error pointer
+ * Returns: The allocated fc_lport or an error pointer
*/
static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
- struct device *parent)
+ struct device *parent, int npiv)
{
- int rc;
+ struct net_device *netdev = fcoe->netdev;
struct fc_lport *lport = NULL;
struct fcoe_port *port;
struct Scsi_Host *shost;
- struct net_device *netdev = fcoe->netdev;
+ int rc;
+ /*
+ * parent is only a vport if npiv is 1,
+ * but we'll only use vport in that case so go ahead and set it
+ */
+ struct fc_vport *vport = dev_to_vport(parent);
FCOE_NETDEV_DBG(netdev, "Create Interface\n");
- shost = libfc_host_alloc(&fcoe_shost_template,
- sizeof(struct fcoe_port));
- if (!shost) {
+ if (!npiv) {
+ lport = libfc_host_alloc(&fcoe_shost_template,
+ sizeof(struct fcoe_port));
+ } else {
+ lport = libfc_vport_create(vport,
+ sizeof(struct fcoe_port));
+ }
+ if (!lport) {
FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
rc = -ENOMEM;
goto out;
}
- lport = shost_priv(shost);
+ shost = lport->host;
port = lport_priv(lport);
port->lport = lport;
port->fcoe = fcoe;
INIT_WORK(&port->destroy_work, fcoe_destroy_work);
- /* configure fc_lport, e.g., em */
+ /* configure a fc_lport including the exchange manager */
rc = fcoe_lport_config(lport);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure lport for the "
@@ -733,6 +903,13 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
goto out_host_put;
}
+ if (npiv) {
+ FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n",
+ vport->node_name, vport->port_name);
+ fc_set_wwnn(lport, vport->node_name);
+ fc_set_wwpn(lport, vport->port_name);
+ }
+
/* configure lport network properties */
rc = fcoe_netdev_config(lport, netdev);
if (rc) {
@@ -757,21 +934,24 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
goto out_lp_destroy;
}
- /*
- * fcoe_em_alloc() and fcoe_hostlist_add() both
- * need to be atomic with respect to other changes to the hostlist
- * since fcoe_em_alloc() looks for an existing EM
- * instance on host list updated by fcoe_hostlist_add().
- *
- * This is currently handled through the fcoe_config_mutex begin held.
- */
+ if (!npiv) {
+ /*
+ * fcoe_em_alloc() and fcoe_hostlist_add() both
+ * need to be atomic with respect to other changes to the
+ * hostlist since fcoe_em_alloc() looks for an existing EM
+ * instance on host list updated by fcoe_hostlist_add().
+ *
+ * This is currently handled through the fcoe_config_mutex
+ * begin held.
+ */
- /* lport exch manager allocation */
- rc = fcoe_em_config(lport);
- if (rc) {
- FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the "
- "interface\n");
- goto out_lp_destroy;
+ /* lport exch manager allocation */
+ rc = fcoe_em_config(lport);
+ if (rc) {
+ FCOE_NETDEV_DBG(netdev, "Could not configure the EM "
+ "for the interface\n");
+ goto out_lp_destroy;
+ }
}
fcoe_interface_get(fcoe);
@@ -786,17 +966,20 @@ out:
}
/**
- * fcoe_if_init() - attach to scsi transport
+ * fcoe_if_init() - Initialization routine for fcoe.ko
+ *
+ * Attaches the SW FCoE transport to the FC transport
*
- * Returns : 0 on success
+ * Returns: 0 on success
*/
static int __init fcoe_if_init(void)
{
/* attach to scsi transport */
- scsi_transport_fcoe_sw =
- fc_attach_transport(&fcoe_transport_function);
+ fcoe_transport_template = fc_attach_transport(&fcoe_transport_function);
+ fcoe_vport_transport_template =
+ fc_attach_transport(&fcoe_vport_transport_function);
- if (!scsi_transport_fcoe_sw) {
+ if (!fcoe_transport_template) {
printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
return -ENODEV;
}
@@ -805,20 +988,24 @@ static int __init fcoe_if_init(void)
}
/**
- * fcoe_if_exit() - detach from scsi transport
+ * fcoe_if_exit() - Tear down fcoe.ko
+ *
+ * Detaches the SW FCoE transport from the FC transport
*
- * Returns : 0 on success
+ * Returns: 0 on success
*/
int __exit fcoe_if_exit(void)
{
- fc_release_transport(scsi_transport_fcoe_sw);
- scsi_transport_fcoe_sw = NULL;
+ fc_release_transport(fcoe_transport_template);
+ fc_release_transport(fcoe_vport_transport_template);
+ fcoe_transport_template = NULL;
+ fcoe_vport_transport_template = NULL;
return 0;
}
/**
- * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
- * @cpu: cpu index for the online cpu
+ * fcoe_percpu_thread_create() - Create a receive thread for an online CPU
+ * @cpu: The CPU index of the CPU to create a receive thread for
*/
static void fcoe_percpu_thread_create(unsigned int cpu)
{
@@ -841,8 +1028,8 @@ static void fcoe_percpu_thread_create(unsigned int cpu)
}
/**
- * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
- * @cpu: cpu index the rx thread is to be removed
+ * fcoe_percpu_thread_destroy() - Remove the receive thread of a CPU
+ * @cpu: The CPU index of the CPU whose receive thread is to be destroyed
*
* Destroys a per-CPU Rx thread. Any pending skbs are moved to the
* current CPU's Rx thread. If the thread being destroyed is bound to
@@ -890,7 +1077,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
} else {
/*
* The targeted CPU is not initialized and cannot accept
- * new skbs. Unlock the targeted CPU and drop the skbs
+ * new skbs. Unlock the targeted CPU and drop the skbs
* on the CPU that is going offline.
*/
while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
@@ -931,12 +1118,12 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
}
/**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
+ * fcoe_cpu_callback() - Handler for CPU hotplug events
+ * @nfb: The callback data block
+ * @action: The event triggering the callback
+ * @hcpu: The index of the CPU that the event is for
*
- * This creates or destroys per cpu data for fcoe
+ * This creates or destroys per-CPU data for fcoe
*
* Returns NOTIFY_OK always.
*/
@@ -962,25 +1149,22 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static struct notifier_block fcoe_cpu_notifier = {
- .notifier_call = fcoe_cpu_callback,
-};
-
/**
- * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
- * @skb: the receive skb
- * @dev: associated net device
- * @ptype: context
- * @olddev: last device
+ * fcoe_rcv() - Receive packets from a net device
+ * @skb: The received packet
+ * @netdev: The net device that the packet was received on
+ * @ptype: The packet type context
+ * @olddev: The last device net device
*
- * this function will receive the packet and build fc frame and pass it up
+ * This routine is called by NET_RX_SOFTIRQ. It receives a packet, builds a
+ * FC frame and passes the frame to libfc.
*
* Returns: 0 for success
*/
-int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *ptype, struct net_device *olddev)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fcoe_rcv_info *fr;
struct fcoe_interface *fcoe;
struct fc_frame_header *fh;
@@ -988,15 +1172,15 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
unsigned int cpu;
fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
- lp = fcoe->ctlr.lp;
- if (unlikely(lp == NULL)) {
- FCOE_NETDEV_DBG(dev, "Cannot find hba structure");
+ lport = fcoe->ctlr.lp;
+ if (unlikely(!lport)) {
+ FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
goto err2;
}
- if (!lp->link_up)
+ if (!lport->link_up)
goto err2;
- FCOE_NETDEV_DBG(dev, "skb_info: len:%d data_len:%d head:%p "
+ FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p "
"data:%p tail:%p end:%p sum:%d dev:%s",
skb->len, skb->data_len, skb->head, skb->data,
skb_tail_pointer(skb), skb_end_pointer(skb),
@@ -1004,7 +1188,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
/* check for FCOE packet type */
if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
- FCOE_NETDEV_DBG(dev, "Wrong FC type frame");
+ FCOE_NETDEV_DBG(netdev, "Wrong FC type frame");
goto err;
}
@@ -1013,14 +1197,14 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
* and FC headers are pulled into the linear data area.
*/
if (unlikely((skb->len < FCOE_MIN_FRAME) ||
- !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+ !pskb_may_pull(skb, FCOE_HEADER_LEN)))
goto err;
skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
fh = (struct fc_frame_header *) skb_transport_header(skb);
fr = fcoe_dev_from_skb(skb);
- fr->fr_dev = lp;
+ fr->fr_dev = lport;
fr->ptype = ptype;
/*
@@ -1042,7 +1226,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
* the first CPU now. For non-SMP systems this
* will check the same CPU twice.
*/
- FCOE_NETDEV_DBG(dev, "CPU is online, but no receive thread "
+ FCOE_NETDEV_DBG(netdev, "CPU is online, but no receive thread "
"ready for incoming skb- using first online "
"CPU.\n");
@@ -1061,15 +1245,29 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
* this skb. We also have this receive thread locked,
* so we're free to queue skbs into it's queue.
*/
- __skb_queue_tail(&fps->fcoe_rx_list, skb);
- if (fps->fcoe_rx_list.qlen == 1)
- wake_up_process(fps->thread);
- spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ /* If this is a SCSI-FCP frame, and this is already executing on the
+ * correct CPU, and the queue for this CPU is empty, then go ahead
+ * and process the frame directly in the softirq context.
+ * This lets us process completions without context switching from the
+ * NET_RX softirq, to our receive processing thread, and then back to
+ * BLOCK softirq context.
+ */
+ if (fh->fh_type == FC_TYPE_FCP &&
+ cpu == smp_processor_id() &&
+ skb_queue_empty(&fps->fcoe_rx_list)) {
+ spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ fcoe_recv_frame(skb);
+ } else {
+ __skb_queue_tail(&fps->fcoe_rx_list, skb);
+ if (fps->fcoe_rx_list.qlen == 1)
+ wake_up_process(fps->thread);
+ spin_unlock_bh(&fps->fcoe_rx_list.lock);
+ }
return 0;
err:
- fc_lport_get_stats(lp)->ErrorFrames++;
+ fc_lport_get_stats(lport)->ErrorFrames++;
err2:
kfree_skb(skb);
@@ -1077,17 +1275,21 @@ err2:
}
/**
- * fcoe_start_io() - pass to netdev to start xmit for fcoe
- * @skb: the skb to be xmitted
+ * fcoe_start_io() - Start FCoE I/O
+ * @skb: The packet to be transmitted
+ *
+ * This routine is called from the net device to start transmitting
+ * FCoE packets.
*
* Returns: 0 for success
*/
static inline int fcoe_start_io(struct sk_buff *skb)
{
+ struct sk_buff *nskb;
int rc;
- skb_get(skb);
- rc = dev_queue_xmit(skb);
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ rc = dev_queue_xmit(nskb);
if (rc != 0)
return rc;
kfree_skb(skb);
@@ -1095,9 +1297,15 @@ static inline int fcoe_start_io(struct sk_buff *skb)
}
/**
- * fcoe_get_paged_crc_eof() - in case we need to alloc a page for crc_eof
- * @skb: the skb to be xmitted
- * @tlen: total len
+ * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
+ * @skb: The packet to be transmitted
+ * @tlen: The total length of the trailer
+ *
+ * This routine allocates a page for frame trailers. The page is re-used if
+ * there is enough room left on it for the current trailer. If there isn't
+ * enough buffer left a new page is allocated for the trailer. Reference to
+ * the page from this function as well as the skbs using the page fragments
+ * ensure that the page is freed at the appropriate time.
*
* Returns: 0 for success
*/
@@ -1136,11 +1344,12 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
}
/**
- * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
- * @fp: the fc_frame containing data to be checksummed
+ * fcoe_fc_crc() - Calculates the CRC for a given frame
+ * @fp: The frame to be checksumed
+ *
+ * This uses crc32() routine to calculate the CRC for a frame
*
- * This uses crc32() to calculate the crc for port frame
- * Return : 32 bit crc
+ * Return: The 32 bit CRC value
*/
u32 fcoe_fc_crc(struct fc_frame *fp)
{
@@ -1171,13 +1380,13 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
}
/**
- * fcoe_xmit() - FCoE frame transmit function
- * @lp: the associated local fcoe
- * @fp: the fc_frame to be transmitted
+ * fcoe_xmit() - Transmit a FCoE frame
+ * @lport: The local port that the frame is to be transmitted for
+ * @fp: The frame to be transmitted
*
- * Return : 0 for success
+ * Return: 0 for success
*/
-int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
{
int wlen;
u32 crc;
@@ -1189,7 +1398,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
unsigned int hlen; /* header length implies the version */
unsigned int tlen; /* trailer length */
unsigned int elen; /* eth header, may include vlan */
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
struct fcoe_interface *fcoe = port->fcoe;
u8 sof, eof;
struct fcoe_hdr *hp;
@@ -1200,13 +1409,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
skb = fp_skb(fp);
wlen = skb->len / FCOE_WORD_TO_BYTE;
- if (!lp->link_up) {
+ if (!lport->link_up) {
kfree_skb(skb);
return 0;
}
if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
- fcoe_ctlr_els_send(&fcoe->ctlr, skb))
+ fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
return 0;
sof = fr_sof(fp);
@@ -1218,7 +1427,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
/* crc offload */
- if (likely(lp->crc_offload)) {
+ if (likely(lport->crc_offload)) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb);
skb->csum_offset = skb->len;
@@ -1271,7 +1480,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
else
- memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN);
+ memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
hp = (struct fcoe_hdr *)(eh + 1);
memset(hp, 0, sizeof(*hp));
@@ -1280,7 +1489,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
hp->fcoe_sof = sof;
/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
- if (lp->seq_offload && fr_max_payload(fp)) {
+ if (lport->seq_offload && fr_max_payload(fp)) {
skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
skb_shinfo(skb)->gso_size = fr_max_payload(fp);
} else {
@@ -1288,23 +1497,23 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
skb_shinfo(skb)->gso_size = 0;
}
/* update tx stats: regardless if LLD fails */
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->TxFrames++;
stats->TxWords += wlen;
/* send down to lld */
- fr_dev(fp) = lp;
+ fr_dev(fp) = lport;
if (port->fcoe_pending_queue.qlen)
- fcoe_check_wait_queue(lp, skb);
+ fcoe_check_wait_queue(lport, skb);
else if (fcoe_start_io(skb))
- fcoe_check_wait_queue(lp, skb);
+ fcoe_check_wait_queue(lport, skb);
return 0;
}
/**
- * fcoe_percpu_flush_done() - Indicate percpu queue flush completion.
- * @skb: the skb being completed.
+ * fcoe_percpu_flush_done() - Indicate per-CPU queue flush completion
+ * @skb: The completed skb (argument required by destructor)
*/
static void fcoe_percpu_flush_done(struct sk_buff *skb)
{
@@ -1312,26 +1521,134 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
}
/**
- * fcoe_percpu_receive_thread() - recv thread per cpu
- * @arg: ptr to the fcoe per cpu struct
- *
- * Return: 0 for success
+ * fcoe_recv_frame() - process a single received frame
+ * @skb: frame to process
*/
-int fcoe_percpu_receive_thread(void *arg)
+static void fcoe_recv_frame(struct sk_buff *skb)
{
- struct fcoe_percpu_s *p = arg;
u32 fr_len;
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fcoe_rcv_info *fr;
struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
- struct sk_buff *skb;
struct fcoe_crc_eof crc_eof;
struct fc_frame *fp;
u8 *mac = NULL;
struct fcoe_port *port;
struct fcoe_hdr *hp;
+ fr = fcoe_dev_from_skb(skb);
+ lport = fr->fr_dev;
+ if (unlikely(!lport)) {
+ if (skb->destructor != fcoe_percpu_flush_done)
+ FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
+ kfree_skb(skb);
+ return;
+ }
+
+ FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
+ "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
+ skb->len, skb->data_len,
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb), skb->csum,
+ skb->dev ? skb->dev->name : "<NULL>");
+
+ /*
+ * Save source MAC address before discarding header.
+ */
+ port = lport_priv(lport);
+ if (skb_is_nonlinear(skb))
+ skb_linearize(skb); /* not ideal */
+ mac = eth_hdr(skb)->h_source;
+
+ /*
+ * Frame length checks and setting up the header pointers
+ * was done in fcoe_rcv already.
+ */
+ hp = (struct fcoe_hdr *) skb_network_header(skb);
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+ stats = fc_lport_get_stats(lport);
+ if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+ if (stats->ErrorFrames < 5)
+ printk(KERN_WARNING "fcoe: FCoE version "
+ "mismatch: The frame has "
+ "version %x, but the "
+ "initiator supports version "
+ "%x\n", FC_FCOE_DECAPS_VER(hp),
+ FC_FCOE_VER);
+ stats->ErrorFrames++;
+ kfree_skb(skb);
+ return;
+ }
+
+ skb_pull(skb, sizeof(struct fcoe_hdr));
+ fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+ stats->RxFrames++;
+ stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+
+ fp = (struct fc_frame *)skb;
+ fc_frame_init(fp);
+ fr_dev(fp) = lport;
+ fr_sof(fp) = hp->fcoe_sof;
+
+ /* Copy out the CRC and EOF trailer for access */
+ if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+ kfree_skb(skb);
+ return;
+ }
+ fr_eof(fp) = crc_eof.fcoe_eof;
+ fr_crc(fp) = crc_eof.fcoe_crc32;
+ if (pskb_trim(skb, fr_len)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * We only check CRC if no offload is available and if it is
+ * it's solicited data, in which case, the FCP layer would
+ * check it during the copy.
+ */
+ if (lport->crc_offload &&
+ skb->ip_summed == CHECKSUM_UNNECESSARY)
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ else
+ fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+ fh = fc_frame_header_get(fp);
+ if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
+ fh->fh_type == FC_TYPE_FCP) {
+ fc_exch_recv(lport, fp);
+ return;
+ }
+ if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+ if (le32_to_cpu(fr_crc(fp)) !=
+ ~crc32(~0, skb->data, fr_len)) {
+ if (stats->InvalidCRCCount < 5)
+ printk(KERN_WARNING "fcoe: dropping "
+ "frame with CRC error\n");
+ stats->InvalidCRCCount++;
+ stats->ErrorFrames++;
+ fc_frame_free(fp);
+ return;
+ }
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+ }
+ fc_exch_recv(lport, fp);
+}
+
+/**
+ * fcoe_percpu_receive_thread() - The per-CPU packet receive thread
+ * @arg: The per-CPU context
+ *
+ * Return: 0 for success
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+ struct fcoe_percpu_s *p = arg;
+ struct sk_buff *skb;
+
set_user_nice(current, -20);
while (!kthread_should_stop()) {
@@ -1347,129 +1664,27 @@ int fcoe_percpu_receive_thread(void *arg)
spin_lock_bh(&p->fcoe_rx_list.lock);
}
spin_unlock_bh(&p->fcoe_rx_list.lock);
- fr = fcoe_dev_from_skb(skb);
- lp = fr->fr_dev;
- if (unlikely(lp == NULL)) {
- if (skb->destructor != fcoe_percpu_flush_done)
- FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
- kfree_skb(skb);
- continue;
- }
-
- FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
- "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
- skb->len, skb->data_len,
- skb->head, skb->data, skb_tail_pointer(skb),
- skb_end_pointer(skb), skb->csum,
- skb->dev ? skb->dev->name : "<NULL>");
-
- /*
- * Save source MAC address before discarding header.
- */
- port = lport_priv(lp);
- if (skb_is_nonlinear(skb))
- skb_linearize(skb); /* not ideal */
- mac = eth_hdr(skb)->h_source;
-
- /*
- * Frame length checks and setting up the header pointers
- * was done in fcoe_rcv already.
- */
- hp = (struct fcoe_hdr *) skb_network_header(skb);
- fh = (struct fc_frame_header *) skb_transport_header(skb);
-
- stats = fc_lport_get_stats(lp);
- if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
- if (stats->ErrorFrames < 5)
- printk(KERN_WARNING "fcoe: FCoE version "
- "mismatch: The frame has "
- "version %x, but the "
- "initiator supports version "
- "%x\n", FC_FCOE_DECAPS_VER(hp),
- FC_FCOE_VER);
- stats->ErrorFrames++;
- kfree_skb(skb);
- continue;
- }
-
- skb_pull(skb, sizeof(struct fcoe_hdr));
- fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-
- stats->RxFrames++;
- stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-
- fp = (struct fc_frame *)skb;
- fc_frame_init(fp);
- fr_dev(fp) = lp;
- fr_sof(fp) = hp->fcoe_sof;
-
- /* Copy out the CRC and EOF trailer for access */
- if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
- kfree_skb(skb);
- continue;
- }
- fr_eof(fp) = crc_eof.fcoe_eof;
- fr_crc(fp) = crc_eof.fcoe_crc32;
- if (pskb_trim(skb, fr_len)) {
- kfree_skb(skb);
- continue;
- }
-
- /*
- * We only check CRC if no offload is available and if it is
- * it's solicited data, in which case, the FCP layer would
- * check it during the copy.
- */
- if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
- else
- fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
- fh = fc_frame_header_get(fp);
- if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
- fh->fh_type == FC_TYPE_FCP) {
- fc_exch_recv(lp, fp);
- continue;
- }
- if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
- if (le32_to_cpu(fr_crc(fp)) !=
- ~crc32(~0, skb->data, fr_len)) {
- if (stats->InvalidCRCCount < 5)
- printk(KERN_WARNING "fcoe: dropping "
- "frame with CRC error\n");
- stats->InvalidCRCCount++;
- stats->ErrorFrames++;
- fc_frame_free(fp);
- continue;
- }
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
- }
- if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
- fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) {
- fc_frame_free(fp);
- continue;
- }
- fc_exch_recv(lp, fp);
+ fcoe_recv_frame(skb);
}
return 0;
}
/**
- * fcoe_check_wait_queue() - attempt to clear the transmit backlog
- * @lp: the fc_lport
+ * fcoe_check_wait_queue() - Attempt to clear the transmit backlog
+ * @lport: The local port whose backlog is to be cleared
*
- * This empties the wait_queue, dequeue the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet, if all skb have been
- * transmitted, return qlen or -1 if a error occurs, then restore
- * wait_queue and try again later.
+ * This empties the wait_queue, dequeues the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet. If all skb have been
+ * transmitted it returns the qlen. If an error occurs it restores
+ * wait_queue (to try again later) and returns -1.
*
- * The wait_queue is used when the skb transmit fails. skb will go
- * in the wait_queue which will be emptied by the timer function or
+ * The wait_queue is used when the skb transmit fails. The failed skb
+ * will go in the wait_queue which will be emptied by the timer function or
* by the next skb transmit.
*/
-static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
+static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
{
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
int rc;
spin_lock_bh(&port->fcoe_pending_queue.lock);
@@ -1501,19 +1716,19 @@ static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
}
if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
- lp->qfull = 0;
+ lport->qfull = 0;
if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
mod_timer(&port->timer, jiffies + 2);
port->fcoe_pending_queue_active = 0;
out:
if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
- lp->qfull = 1;
+ lport->qfull = 1;
spin_unlock_bh(&port->fcoe_pending_queue.lock);
return;
}
/**
- * fcoe_dev_setup() - setup link change notification interface
+ * fcoe_dev_setup() - Setup the link change notification interface
*/
static void fcoe_dev_setup(void)
{
@@ -1521,7 +1736,7 @@ static void fcoe_dev_setup(void)
}
/**
- * fcoe_dev_cleanup() - cleanup link change notification interface
+ * fcoe_dev_cleanup() - Cleanup the link change notification interface
*/
static void fcoe_dev_cleanup(void)
{
@@ -1529,19 +1744,19 @@ static void fcoe_dev_cleanup(void)
}
/**
- * fcoe_device_notification() - netdev event notification callback
- * @notifier: context of the notification
- * @event: type of event
- * @ptr: fixed array for output parsed ifname
+ * fcoe_device_notification() - Handler for net device events
+ * @notifier: The context of the notification
+ * @event: The type of event
+ * @ptr: The net device that the event was on
*
- * This function is called by the ethernet driver in case of link change event
+ * This function is called by the Ethernet driver in case of link change event.
*
* Returns: 0 for success
*/
static int fcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr)
{
- struct fc_lport *lp = NULL;
+ struct fc_lport *lport = NULL;
struct net_device *netdev = ptr;
struct fcoe_interface *fcoe;
struct fcoe_port *port;
@@ -1552,11 +1767,11 @@ static int fcoe_device_notification(struct notifier_block *notifier,
list_for_each_entry(fcoe, &fcoe_hostlist, list) {
if (fcoe->netdev == netdev) {
- lp = fcoe->ctlr.lp;
+ lport = fcoe->ctlr.lp;
break;
}
}
- if (lp == NULL) {
+ if (!lport) {
rc = NOTIFY_DONE;
goto out;
}
@@ -1570,10 +1785,12 @@ static int fcoe_device_notification(struct notifier_block *notifier,
case NETDEV_CHANGE:
break;
case NETDEV_CHANGEMTU:
+ if (netdev->features & NETIF_F_FCOE_MTU)
+ break;
mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
sizeof(struct fcoe_crc_eof));
if (mfs >= FC_MIN_MAX_FRAME)
- fc_set_mfs(lp, mfs);
+ fc_set_mfs(lport, mfs);
break;
case NETDEV_REGISTER:
break;
@@ -1588,22 +1805,22 @@ static int fcoe_device_notification(struct notifier_block *notifier,
FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
"from netdev netlink\n", event);
}
- if (link_possible && !fcoe_link_ok(lp))
+ if (link_possible && !fcoe_link_ok(lport))
fcoe_ctlr_link_up(&fcoe->ctlr);
else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->LinkFailureCount++;
- fcoe_clean_pending_queue(lp);
+ fcoe_clean_pending_queue(lport);
}
out:
return rc;
}
/**
- * fcoe_if_to_netdev() - parse a name buffer to get netdev
- * @buffer: incoming buffer to be copied
+ * fcoe_if_to_netdev() - Parse a name buffer to get a net device
+ * @buffer: The name of the net device
*
- * Returns: NULL or ptr to net_device
+ * Returns: NULL or a ptr to net_device
*/
static struct net_device *fcoe_if_to_netdev(const char *buffer)
{
@@ -1621,9 +1838,11 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
}
/**
- * fcoe_destroy() - handles the destroy from sysfs
- * @buffer: expected to be an eth if name
- * @kp: associated kernel param
+ * fcoe_destroy() - Destroy a FCoE interface
+ * @buffer: The name of the Ethernet interface to be destroyed
+ * @kp: The associated kernel parameter
+ *
+ * Called from sysfs.
*
* Returns: 0 for success
*/
@@ -1631,7 +1850,7 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
{
struct fcoe_interface *fcoe;
struct net_device *netdev;
- int rc;
+ int rc = 0;
mutex_lock(&fcoe_config_mutex);
#ifdef CONFIG_FCOE_MODULE
@@ -1670,6 +1889,10 @@ out_nodev:
return rc;
}
+/**
+ * fcoe_destroy_work() - Destroy a FCoE port in a deferred work context
+ * @work: Handle to the FCoE port to be destroyed
+ */
static void fcoe_destroy_work(struct work_struct *work)
{
struct fcoe_port *port;
@@ -1681,9 +1904,11 @@ static void fcoe_destroy_work(struct work_struct *work)
}
/**
- * fcoe_create() - Handles the create call from sysfs
- * @buffer: expected to be an eth if name
- * @kp: associated kernel param
+ * fcoe_create() - Create a fcoe interface
+ * @buffer: The name of the Ethernet interface to create on
+ * @kp: The associated kernel param
+ *
+ * Called from sysfs.
*
* Returns: 0 for success
*/
@@ -1726,7 +1951,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
goto out_putdev;
}
- lport = fcoe_if_create(fcoe, &netdev->dev);
+ lport = fcoe_if_create(fcoe, &netdev->dev, 0);
if (IS_ERR(lport)) {
printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
netdev->name);
@@ -1762,16 +1987,9 @@ out_nodev:
return rc;
}
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
-
/**
- * fcoe_link_ok() - Check if link is ok for the fc_lport
- * @lp: ptr to the fc_lport
+ * fcoe_link_ok() - Check if the link is OK for a local port
+ * @lport: The local port to check link on
*
* Any permanently-disqualifying conditions have been previously checked.
* This also updates the speed setting, which may change with link for 100/1000.
@@ -1783,26 +2001,26 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
* Returns: 0 if link is OK for use by FCoE.
*
*/
-int fcoe_link_ok(struct fc_lport *lp)
+int fcoe_link_ok(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lp);
- struct net_device *dev = port->fcoe->netdev;
+ struct fcoe_port *port = lport_priv(lport);
+ struct net_device *netdev = port->fcoe->netdev;
struct ethtool_cmd ecmd = { ETHTOOL_GSET };
- if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
- (!dev_ethtool_get_settings(dev, &ecmd))) {
- lp->link_supported_speeds &=
+ if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) &&
+ (!dev_ethtool_get_settings(netdev, &ecmd))) {
+ lport->link_supported_speeds &=
~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
if (ecmd.supported & (SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full))
- lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+ lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
if (ecmd.supported & SUPPORTED_10000baseT_Full)
- lp->link_supported_speeds |=
+ lport->link_supported_speeds |=
FC_PORTSPEED_10GBIT;
if (ecmd.speed == SPEED_1000)
- lp->link_speed = FC_PORTSPEED_1GBIT;
+ lport->link_speed = FC_PORTSPEED_1GBIT;
if (ecmd.speed == SPEED_10000)
- lp->link_speed = FC_PORTSPEED_10GBIT;
+ lport->link_speed = FC_PORTSPEED_10GBIT;
return 0;
}
@@ -1810,8 +2028,8 @@ int fcoe_link_ok(struct fc_lport *lp)
}
/**
- * fcoe_percpu_clean() - Clear the pending skbs for an lport
- * @lp: the fc_lport
+ * fcoe_percpu_clean() - Clear all pending skbs for an local port
+ * @lport: The local port whose skbs are to be cleared
*
* Must be called with fcoe_create_mutex held to single-thread completion.
*
@@ -1820,7 +2038,7 @@ int fcoe_link_ok(struct fc_lport *lp)
* there no packets that will be handled by the lport, but also that any
* threads already handling packet have returned.
*/
-void fcoe_percpu_clean(struct fc_lport *lp)
+void fcoe_percpu_clean(struct fc_lport *lport)
{
struct fcoe_percpu_s *pp;
struct fcoe_rcv_info *fr;
@@ -1838,7 +2056,7 @@ void fcoe_percpu_clean(struct fc_lport *lp)
skb = next) {
next = skb->next;
fr = fcoe_dev_from_skb(skb);
- if (fr->fr_dev == lp) {
+ if (fr->fr_dev == lport) {
__skb_unlink(skb, list);
kfree_skb(skb);
}
@@ -1867,13 +2085,11 @@ void fcoe_percpu_clean(struct fc_lport *lp)
/**
* fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lp: the corresponding fc_lport
- *
- * Returns: none
+ * @lport: The local port to dequeue a skb on
*/
-void fcoe_clean_pending_queue(struct fc_lport *lp)
+void fcoe_clean_pending_queue(struct fc_lport *lport)
{
- struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lport);
struct sk_buff *skb;
spin_lock_bh(&port->fcoe_pending_queue.lock);
@@ -1886,10 +2102,10 @@ void fcoe_clean_pending_queue(struct fc_lport *lp)
}
/**
- * fcoe_reset() - Resets the fcoe
- * @shost: shost the reset is from
+ * fcoe_reset() - Reset a local port
+ * @shost: The SCSI host associated with the local port to be reset
*
- * Returns: always 0
+ * Returns: Always 0 (return value required by FC transport template)
*/
int fcoe_reset(struct Scsi_Host *shost)
{
@@ -1899,30 +2115,33 @@ int fcoe_reset(struct Scsi_Host *shost)
}
/**
- * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device
- * @dev: this is currently ptr to net_device
+ * fcoe_hostlist_lookup_port() - Find the FCoE interface associated with a net device
+ * @netdev: The net device used as a key
+ *
+ * Locking: Must be called with the RNL mutex held.
*
- * Returns: NULL or the located fcoe_port
- * Locking: must be called with the RNL mutex held
+ * Returns: NULL or the FCoE interface
*/
static struct fcoe_interface *
-fcoe_hostlist_lookup_port(const struct net_device *dev)
+fcoe_hostlist_lookup_port(const struct net_device *netdev)
{
struct fcoe_interface *fcoe;
list_for_each_entry(fcoe, &fcoe_hostlist, list) {
- if (fcoe->netdev == dev)
+ if (fcoe->netdev == netdev)
return fcoe;
}
return NULL;
}
/**
- * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
- * @netdev: ptr to net_device
+ * fcoe_hostlist_lookup() - Find the local port associated with a
+ * given net device
+ * @netdev: The netdevice used as a key
*
- * Returns: 0 for success
- * Locking: must be called with the RTNL mutex held
+ * Locking: Must be called with the RTNL mutex held
+ *
+ * Returns: NULL or the local port
*/
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
{
@@ -1933,11 +2152,13 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
}
/**
- * fcoe_hostlist_add() - Add a lport to lports list
- * @lp: ptr to the fc_lport to be added
+ * fcoe_hostlist_add() - Add the FCoE interface identified by a local
+ * port to the hostlist
+ * @lport: The local port that identifies the FCoE interface to be added
*
- * Returns: 0 for success
* Locking: must be called with the RTNL mutex held
+ *
+ * Returns: 0 for success
*/
static int fcoe_hostlist_add(const struct fc_lport *lport)
{
@@ -1954,15 +2175,15 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
}
/**
- * fcoe_init() - fcoe module loading initialization
+ * fcoe_init() - Initialize fcoe.ko
*
- * Returns 0 on success, negative on failure
+ * Returns: 0 on success, or a negative value on failure
*/
static int __init fcoe_init(void)
{
+ struct fcoe_percpu_s *p;
unsigned int cpu;
int rc = 0;
- struct fcoe_percpu_s *p;
mutex_lock(&fcoe_config_mutex);
@@ -1999,15 +2220,15 @@ out_free:
module_init(fcoe_init);
/**
- * fcoe_exit() - fcoe module unloading cleanup
+ * fcoe_exit() - Clean up fcoe.ko
*
- * Returns 0 on success, negative on failure
+ * Returns: 0 on success or a negative value on failure
*/
static void __exit fcoe_exit(void)
{
- unsigned int cpu;
struct fcoe_interface *fcoe, *tmp;
struct fcoe_port *port;
+ unsigned int cpu;
mutex_lock(&fcoe_config_mutex);
@@ -2033,9 +2254,238 @@ static void __exit fcoe_exit(void)
/* flush any asyncronous interface destroys,
* this should happen after the netdev notifier is unregistered */
flush_scheduled_work();
+ /* That will flush out all the N_Ports on the hostlist, but now we
+ * may have NPIV VN_Ports scheduled for destruction */
+ flush_scheduled_work();
/* detach from scsi transport
* must happen after all destroys are done, therefor after the flush */
fcoe_if_exit();
}
module_exit(fcoe_exit);
+
+/**
+ * fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler
+ * @seq: active sequence in the FLOGI or FDISC exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc FLOGI response handler.
+ */
+static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fcoe_ctlr *fip = arg;
+ struct fc_exch *exch = fc_seq_exch(seq);
+ struct fc_lport *lport = exch->lp;
+ u8 *mac;
+
+ if (IS_ERR(fp))
+ goto done;
+
+ mac = fr_cb(fp)->granted_mac;
+ if (is_zero_ether_addr(mac)) {
+ /* pre-FIP */
+ if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
+ fc_frame_free(fp);
+ return;
+ }
+ }
+ fcoe_update_src_mac(lport, mac);
+done:
+ fc_lport_flogi_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_logo_resp() - FCoE specific LOGO response handler
+ * @seq: active sequence in the LOGO exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc LOGO response handler.
+ */
+static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+ struct fc_lport *lport = arg;
+ static u8 zero_mac[ETH_ALEN] = { 0 };
+
+ if (!IS_ERR(fp))
+ fcoe_update_src_mac(lport, zero_mac);
+ fc_lport_logo_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_elsct_send - FCoE specific ELS handler
+ *
+ * This does special case handling of FIP encapsualted ELS exchanges for FCoE,
+ * using FCoE specific response handlers and passing the FIP controller as
+ * the argument (the lport is still available from the exchange).
+ *
+ * Most of the work here is just handed off to the libfc routine.
+ */
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
+ struct fc_frame *fp, unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *,
+ void *),
+ void *arg, u32 timeout)
+{
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+
+ switch (op) {
+ case ELS_FLOGI:
+ case ELS_FDISC:
+ return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp,
+ fip, timeout);
+ case ELS_LOGO:
+ /* only hook onto fabric logouts, not port logouts */
+ if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
+ break;
+ return fc_elsct_send(lport, did, fp, op, fcoe_logo_resp,
+ lport, timeout);
+ }
+ return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
+}
+
+/**
+ * fcoe_vport_create() - create an fc_host/scsi_host for a vport
+ * @vport: fc_vport object to create a new fc_host for
+ * @disabled: start the new fc_host in a disabled state by default?
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fcoe_port *port = lport_priv(n_port);
+ struct fcoe_interface *fcoe = port->fcoe;
+ struct net_device *netdev = fcoe->netdev;
+ struct fc_lport *vn_port;
+
+ mutex_lock(&fcoe_config_mutex);
+ vn_port = fcoe_if_create(fcoe, &vport->dev, 1);
+ mutex_unlock(&fcoe_config_mutex);
+
+ if (IS_ERR(vn_port)) {
+ printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n",
+ netdev->name);
+ return -EIO;
+ }
+
+ if (disabled) {
+ fc_vport_set_state(vport, FC_VPORT_DISABLED);
+ } else {
+ vn_port->boot_time = jiffies;
+ fc_fabric_login(vn_port);
+ fc_vport_setlink(vn_port);
+ }
+ return 0;
+}
+
+/**
+ * fcoe_vport_destroy() - destroy the fc_host/scsi_host for a vport
+ * @vport: fc_vport object that is being destroyed
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_vport_destroy(struct fc_vport *vport)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fc_lport *vn_port = vport->dd_data;
+ struct fcoe_port *port = lport_priv(vn_port);
+
+ mutex_lock(&n_port->lp_mutex);
+ list_del(&vn_port->list);
+ mutex_unlock(&n_port->lp_mutex);
+ schedule_work(&port->destroy_work);
+ return 0;
+}
+
+/**
+ * fcoe_vport_disable() - change vport state
+ * @vport: vport to bring online/offline
+ * @disable: should the vport be disabled?
+ */
+static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
+{
+ struct fc_lport *lport = vport->dd_data;
+
+ if (disable) {
+ fc_vport_set_state(vport, FC_VPORT_DISABLED);
+ fc_fabric_logoff(lport);
+ } else {
+ lport->boot_time = jiffies;
+ fc_fabric_login(lport);
+ fc_vport_setlink(lport);
+ }
+
+ return 0;
+}
+
+/**
+ * fcoe_vport_set_symbolic_name() - append vport string to symbolic name
+ * @vport: fc_vport with a new symbolic name string
+ *
+ * After generating a new symbolic name string, a new RSPN_ID request is
+ * sent to the name server. There is no response handler, so if it fails
+ * for some reason it will not be retried.
+ */
+static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
+{
+ struct fc_lport *lport = vport->dd_data;
+ struct fc_frame *fp;
+ size_t len;
+
+ snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
+ "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION,
+ fcoe_netdev(lport)->name, vport->symbolic_name);
+
+ if (lport->state != LPORT_ST_READY)
+ return;
+
+ len = strnlen(fc_host_symbolic_name(lport->host), 255);
+ fp = fc_frame_alloc(lport,
+ sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_rspn) + len);
+ if (!fp)
+ return;
+ lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RSPN_ID,
+ NULL, NULL, 3 * lport->r_a_tov);
+}
+
+/**
+ * fcoe_get_lesb() - Fill the FCoE Link Error Status Block
+ * @lport: the local port
+ * @fc_lesb: the link error status block
+ */
+static void fcoe_get_lesb(struct fc_lport *lport,
+ struct fc_els_lesb *fc_lesb)
+{
+ unsigned int cpu;
+ u32 lfc, vlfc, mdac;
+ struct fcoe_dev_stats *devst;
+ struct fcoe_fc_els_lesb *lesb;
+ struct net_device *netdev = fcoe_netdev(lport);
+
+ lfc = 0;
+ vlfc = 0;
+ mdac = 0;
+ lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
+ memset(lesb, 0, sizeof(*lesb));
+ for_each_possible_cpu(cpu) {
+ devst = per_cpu_ptr(lport->dev_stats, cpu);
+ lfc += devst->LinkFailureCount;
+ vlfc += devst->VLinkFailureCount;
+ mdac += devst->MissDiscAdvCount;
+ }
+ lesb->lesb_link_fail = htonl(lfc);
+ lesb->lesb_vlink_fail = htonl(vlfc);
+ lesb->lesb_miss_fka = htonl(mdac);
+ lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
+}
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index ce7f60fb1bc..c69b2c56c2d 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -32,7 +32,7 @@
#define FCOE_NAME "fcoe"
#define FCOE_VENDOR "Open-FCoE.org"
-#define FCOE_MAX_LUN 255
+#define FCOE_MAX_LUN 0xFFFF
#define FCOE_MAX_FCP_TARGET 256
#define FCOE_MAX_OUTSTANDING_COMMANDS 1024
@@ -40,11 +40,17 @@
#define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */
#define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */
+/*
+ * Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload)
+ * + 4 (FC CRC) + 4 (FCoE trailer) = 2158 bytes
+ */
+#define FCOE_MTU 2158
+
unsigned int fcoe_debug_logging;
module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-#define FCOE_LOGGING 0x01 /* General logging, not categorized */
+#define FCOE_LOGGING 0x01 /* General logging, not categorized */
#define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */
#define FCOE_CHECK_LOGGING(LEVEL, CMD) \
@@ -64,8 +70,13 @@ do { \
printk(KERN_INFO "fcoe: %s: " fmt, \
netdev->name, ##args);)
-/*
- * this percpu struct for fcoe
+/**
+ * struct fcoe_percpu_s - The per-CPU context for FCoE receive threads
+ * @thread: The thread context
+ * @fcoe_rx_list: The queue of pending packets to process
+ * @page: The memory page for calculating frame trailer CRCs
+ * @crc_eof_offset: The offset into the CRC page pointing to available
+ * memory for a new trailer
*/
struct fcoe_percpu_s {
struct task_struct *thread;
@@ -74,37 +85,62 @@ struct fcoe_percpu_s {
int crc_eof_offset;
};
-/*
- * an FCoE interface, 1:1 with netdev
+/**
+ * struct fcoe_interface - A FCoE interface
+ * @list: Handle for a list of FCoE interfaces
+ * @netdev: The associated net device
+ * @fcoe_packet_type: FCoE packet type
+ * @fip_packet_type: FIP packet type
+ * @ctlr: The FCoE controller (for FIP)
+ * @oem: The offload exchange manager for all local port
+ * instances associated with this port
+ * @kref: The kernel reference
+ *
+ * This structure is 1:1 with a net devive.
*/
struct fcoe_interface {
- struct list_head list;
- struct net_device *netdev;
- struct packet_type fcoe_packet_type;
- struct packet_type fip_packet_type;
- struct fcoe_ctlr ctlr;
- struct fc_exch_mgr *oem; /* offload exchange manager */
- struct kref kref;
+ struct list_head list;
+ struct net_device *netdev;
+ struct packet_type fcoe_packet_type;
+ struct packet_type fip_packet_type;
+ struct fcoe_ctlr ctlr;
+ struct fc_exch_mgr *oem;
+ struct kref kref;
};
-/*
- * the FCoE private structure that's allocated along with the
- * Scsi_Host and libfc fc_lport structures
+/**
+ * struct fcoe_port - The FCoE private structure
+ * @fcoe: The associated fcoe interface
+ * @lport: The associated local port
+ * @fcoe_pending_queue: The pending Rx queue of skbs
+ * @fcoe_pending_queue_active: Indicates if the pending queue is active
+ * @timer: The queue timer
+ * @destroy_work: Handle for work context
+ * (to prevent RTNL deadlocks)
+ * @data_srt_addr: Source address for data
+ *
+ * An instance of this structure is to be allocated along with the
+ * Scsi_Host and libfc fc_lport structures.
*/
struct fcoe_port {
struct fcoe_interface *fcoe;
- struct fc_lport *lport;
- struct sk_buff_head fcoe_pending_queue;
- u8 fcoe_pending_queue_active;
- struct timer_list timer; /* queue timer */
- struct work_struct destroy_work; /* to prevent rtnl deadlocks */
+ struct fc_lport *lport;
+ struct sk_buff_head fcoe_pending_queue;
+ u8 fcoe_pending_queue_active;
+ struct timer_list timer;
+ struct work_struct destroy_work;
+ u8 data_src_addr[ETH_ALEN];
};
#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
-static inline struct net_device *fcoe_netdev(const struct fc_lport *lp)
+/**
+ * fcoe_netdev() - Return the net device associated with a local port
+ * @lport: The local port to get the net device from
+ */
+static inline struct net_device *fcoe_netdev(const struct fc_lport *lport)
{
- return ((struct fcoe_port *)lport_priv(lp))->fcoe->netdev;
+ return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev;
}
#endif /* _FCOE_H_ */
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 11ae5c94608..9823291395a 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -59,26 +59,30 @@ unsigned int libfcoe_debug_logging;
module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
+#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */
#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
-#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \
-do { \
- if (unlikely(libfcoe_debug_logging & LEVEL)) \
- do { \
- CMD; \
- } while (0); \
+#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \
+do { \
+ if (unlikely(libfcoe_debug_logging & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
} while (0)
#define LIBFCOE_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \
printk(KERN_INFO "libfcoe: " fmt, ##args);)
-#define LIBFCOE_FIP_DBG(fmt, args...) \
+#define LIBFCOE_FIP_DBG(fip, fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \
- printk(KERN_INFO "fip: " fmt, ##args);)
+ printk(KERN_INFO "host%d: fip: " fmt, \
+ (fip)->lp->host->host_no, ##args);)
-/*
+/**
+ * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
+ * @fcf: The FCF to check
+ *
* Return non-zero if FCF fcoe_size has been validated.
*/
static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
@@ -86,7 +90,10 @@ static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
return (fcf->flags & FIP_FL_SOL) != 0;
}
-/*
+/**
+ * fcoe_ctlr_fcf_usable() - Check if a FCF is usable
+ * @fcf: The FCF to check
+ *
* Return non-zero if the FCF is usable.
*/
static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
@@ -97,12 +104,13 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
}
/**
- * fcoe_ctlr_init() - Initialize the FCoE Controller instance.
- * @fip: FCoE controller.
+ * fcoe_ctlr_init() - Initialize the FCoE Controller instance
+ * @fip: The FCoE controller to initialize
*/
void fcoe_ctlr_init(struct fcoe_ctlr *fip)
{
fip->state = FIP_ST_LINK_WAIT;
+ fip->mode = FIP_ST_AUTO;
INIT_LIST_HEAD(&fip->fcfs);
spin_lock_init(&fip->lock);
fip->flogi_oxid = FC_XID_UNKNOWN;
@@ -114,8 +122,8 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip)
EXPORT_SYMBOL(fcoe_ctlr_init);
/**
- * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller
+ * @fip: The FCoE controller whose FCFs are to be reset
*
* Called with &fcoe_ctlr lock held.
*/
@@ -134,8 +142,8 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_destroy() - Disable and tear-down the FCoE controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_destroy() - Disable and tear down a FCoE controller
+ * @fip: The FCoE controller to tear down
*
* This is called by FCoE drivers before freeing the &fcoe_ctlr.
*
@@ -148,9 +156,7 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
{
cancel_work_sync(&fip->recv_work);
- spin_lock_bh(&fip->fip_recv_list.lock);
- __skb_queue_purge(&fip->fip_recv_list);
- spin_unlock_bh(&fip->fip_recv_list.lock);
+ skb_queue_purge(&fip->fip_recv_list);
spin_lock_bh(&fip->lock);
fip->state = FIP_ST_DISABLED;
@@ -162,8 +168,8 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
EXPORT_SYMBOL(fcoe_ctlr_destroy);
/**
- * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port.
- * @fip: FCoE controller.
+ * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port
+ * @fip: The FCoE controller to get the maximum FCoE size from
*
* Returns the maximum packet size including the FCoE header and trailer,
* but not including any Ethernet or VLAN headers.
@@ -180,9 +186,9 @@ static inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_solicit() - Send a solicitation.
- * @fip: FCoE controller.
- * @fcf: Destination FCF. If NULL, a multicast solicitation is sent.
+ * fcoe_ctlr_solicit() - Send a FIP solicitation
+ * @fip: The FCoE controller to send the solicitation on
+ * @fcf: The destination FCF (if NULL, a multicast solicitation is sent)
*/
static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
{
@@ -241,8 +247,8 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
}
/**
- * fcoe_ctlr_link_up() - Start FCoE controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_link_up() - Start FCoE controller
+ * @fip: The FCoE controller to start
*
* Called from the LLD when the network link is ready.
*/
@@ -255,11 +261,12 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
spin_unlock_bh(&fip->lock);
fc_linkup(fip->lp);
} else if (fip->state == FIP_ST_LINK_WAIT) {
- fip->state = FIP_ST_AUTO;
+ fip->state = fip->mode;
fip->last_link = 1;
fip->link = 1;
spin_unlock_bh(&fip->lock);
- LIBFCOE_FIP_DBG("%s", "setting AUTO mode.\n");
+ if (fip->state == FIP_ST_AUTO)
+ LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
fc_linkup(fip->lp);
fcoe_ctlr_solicit(fip, NULL);
} else
@@ -268,45 +275,23 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
EXPORT_SYMBOL(fcoe_ctlr_link_up);
/**
- * fcoe_ctlr_reset() - Reset FIP.
- * @fip: FCoE controller.
- * @new_state: FIP state to be entered.
- *
- * Returns non-zero if the link was up and now isn't.
+ * fcoe_ctlr_reset() - Reset a FCoE controller
+ * @fip: The FCoE controller to reset
*/
-static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state)
+static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
{
- struct fc_lport *lp = fip->lp;
- int link_dropped;
-
- spin_lock_bh(&fip->lock);
fcoe_ctlr_reset_fcfs(fip);
del_timer(&fip->timer);
- fip->state = new_state;
fip->ctlr_ka_time = 0;
fip->port_ka_time = 0;
fip->sol_time = 0;
fip->flogi_oxid = FC_XID_UNKNOWN;
fip->map_dest = 0;
- fip->last_link = 0;
- link_dropped = fip->link;
- fip->link = 0;
- spin_unlock_bh(&fip->lock);
-
- if (link_dropped)
- fc_linkdown(lp);
-
- if (new_state == FIP_ST_ENABLED) {
- fcoe_ctlr_solicit(fip, NULL);
- fc_linkup(lp);
- link_dropped = 0;
- }
- return link_dropped;
}
/**
- * fcoe_ctlr_link_down() - Stop FCoE controller.
- * @fip: FCoE controller.
+ * fcoe_ctlr_link_down() - Stop a FCoE controller
+ * @fip: The FCoE controller to be stopped
*
* Returns non-zero if the link was up and now isn't.
*
@@ -315,15 +300,29 @@ static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state)
*/
int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
{
- return fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+ int link_dropped;
+
+ LIBFCOE_FIP_DBG(fip, "link down.\n");
+ spin_lock_bh(&fip->lock);
+ fcoe_ctlr_reset(fip);
+ link_dropped = fip->link;
+ fip->link = 0;
+ fip->last_link = 0;
+ fip->state = FIP_ST_LINK_WAIT;
+ spin_unlock_bh(&fip->lock);
+
+ if (link_dropped)
+ fc_linkdown(fip->lp);
+ return link_dropped;
}
EXPORT_SYMBOL(fcoe_ctlr_link_down);
/**
- * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF.
- * @fip: FCoE controller.
- * @ports: 0 for controller keep-alive, 1 for port keep-alive.
- * @sa: source MAC address.
+ * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF
+ * @fip: The FCoE controller to send the FKA on
+ * @lport: libfc fc_lport to send from
+ * @ports: 0 for controller keep-alive, 1 for port keep-alive
+ * @sa: The source MAC address
*
* A controller keep-alive is sent every fka_period (typically 8 seconds).
* The source MAC is the native MAC address.
@@ -332,7 +331,9 @@ EXPORT_SYMBOL(fcoe_ctlr_link_down);
* The source MAC is the assigned mapped source address.
* The destination is the FCF's F-port.
*/
-static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
+static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
+ struct fc_lport *lport,
+ int ports, u8 *sa)
{
struct sk_buff *skb;
struct fip_kal {
@@ -350,8 +351,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
if (!fcf || !fc_host_port_id(lp->host))
return;
- len = fcoe_ctlr_fcoe_size(fip) + sizeof(struct ethhdr);
- BUG_ON(len < sizeof(*kal) + sizeof(*vn));
+ len = sizeof(*kal) + ports * sizeof(*vn);
skb = dev_alloc_skb(len);
if (!skb)
return;
@@ -366,7 +366,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
kal->fip.fip_op = htons(FIP_OP_CTRL);
kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE;
kal->fip.fip_dl_len = htons((sizeof(kal->mac) +
- ports * sizeof(*vn)) / FIP_BPW);
+ ports * sizeof(*vn)) / FIP_BPW);
kal->fip.fip_flags = htons(FIP_FL_FPMA);
if (fip->spma)
kal->fip.fip_flags |= htons(FIP_FL_SPMA);
@@ -374,16 +374,14 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
kal->mac.fd_desc.fip_dtype = FIP_DT_MAC;
kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW;
memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
-
if (ports) {
vn = (struct fip_vn_desc *)(kal + 1);
vn->fd_desc.fip_dtype = FIP_DT_VN_ID;
vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW;
- memcpy(vn->fd_mac, fip->data_src_addr, ETH_ALEN);
+ memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
hton24(vn->fd_fc_id, fc_host_port_id(lp->host));
put_unaligned_be64(lp->wwpn, &vn->fd_wwpn);
}
-
skb_put(skb, len);
skb->protocol = htons(ETH_P_FIP);
skb_reset_mac_header(skb);
@@ -392,10 +390,10 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
}
/**
- * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it.
- * @fip: FCoE controller.
- * @dtype: FIP descriptor type for the frame.
- * @skb: FCoE ELS frame including FC header but no FCoE headers.
+ * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it
+ * @fip: The FCoE controller for the ELS frame
+ * @dtype: The FIP descriptor type for the frame
+ * @skb: The FCoE ELS frame including FC header but no FCoE headers
*
* Returns non-zero error code on failure.
*
@@ -405,7 +403,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
* Headroom includes the FIP encapsulation description, FIP header, and
* Ethernet header. The tailroom is for the FIP MAC descriptor.
*/
-static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
+static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
u8 dtype, struct sk_buff *skb)
{
struct fip_encaps_head {
@@ -449,8 +447,8 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
memset(mac, 0, sizeof(mac));
mac->fd_desc.fip_dtype = FIP_DT_MAC;
mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
- if (dtype != FIP_DT_FLOGI)
- memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN);
+ if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC)
+ memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
else if (fip->spma)
memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
@@ -463,6 +461,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
/**
* fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate.
* @fip: FCoE controller.
+ * @lport: libfc fc_lport to send from
* @skb: FCoE ELS frame including FC header but no FCoE headers.
*
* Returns a non-zero error code if the frame should not be sent.
@@ -471,11 +470,13 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
* The caller must check that the length is a multiple of 4.
* The SKB must have enough headroom (28 bytes) and tailroom (8 bytes).
*/
-int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
+int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
+ struct sk_buff *skb)
{
struct fc_frame_header *fh;
u16 old_xid;
u8 op;
+ u8 mac[ETH_ALEN];
fh = (struct fc_frame_header *)skb->data;
op = *(u8 *)(fh + 1);
@@ -498,6 +499,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (fip->state == FIP_ST_NON_FIP)
return 0;
+ if (!fip->sel_fcf)
+ goto drop;
switch (op) {
case ELS_FLOGI:
@@ -530,14 +533,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
* FLOGI.
*/
fip->flogi_oxid = FC_XID_UNKNOWN;
- fc_fcoe_set_mac(fip->data_src_addr, fh->fh_s_id);
+ fc_fcoe_set_mac(mac, fh->fh_d_id);
+ fip->update_mac(lport, mac);
return 0;
default:
if (fip->state != FIP_ST_ENABLED)
goto drop;
return 0;
}
- if (fcoe_ctlr_encaps(fip, op, skb))
+ if (fcoe_ctlr_encaps(fip, lport, op, skb))
goto drop;
fip->send(fip, skb);
return -EINPROGRESS;
@@ -547,9 +551,9 @@ drop:
}
EXPORT_SYMBOL(fcoe_ctlr_els_send);
-/*
- * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller.
- * @fip: FCoE controller.
+/**
+ * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller
+ * @fip: The FCoE controller to free FCFs on
*
* Called with lock held.
*
@@ -558,14 +562,28 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send);
* times its keep-alive period including fuzz.
*
* In addition, determine the time when an FCF selection can occur.
+ *
+ * Also, increment the MissDiscAdvCount when no advertisement is received
+ * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB).
*/
static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
{
struct fcoe_fcf *fcf;
struct fcoe_fcf *next;
unsigned long sel_time = 0;
+ unsigned long mda_time = 0;
list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+ mda_time = fcf->fka_period + (fcf->fka_period >> 1);
+ if ((fip->sel_fcf == fcf) &&
+ (time_after(jiffies, fcf->time + mda_time))) {
+ mod_timer(&fip->timer, jiffies + mda_time);
+ fc_lport_get_stats(fip->lp)->MissDiscAdvCount++;
+ printk(KERN_INFO "libfcoe: host%d: Missing Discovery "
+ "Advertisement for fab %llx count %lld\n",
+ fip->lp->host->host_no, fcf->fabric_name,
+ fc_lport_get_stats(fip->lp)->MissDiscAdvCount);
+ }
if (time_after(jiffies, fcf->time + fcf->fka_period * 3 +
msecs_to_jiffies(FIP_FCF_FUZZ * 3))) {
if (fip->sel_fcf == fcf)
@@ -574,6 +592,7 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
WARN_ON(!fip->fcf_count);
fip->fcf_count--;
kfree(fcf);
+ fc_lport_get_stats(fip->lp)->VLinkFailureCount++;
} else if (fcoe_ctlr_mtu_valid(fcf) &&
(!sel_time || time_before(sel_time, fcf->time))) {
sel_time = fcf->time;
@@ -590,14 +609,16 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry.
- * @skb: received FIP advertisement frame
- * @fcf: resulting FCF entry.
+ * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry
+ * @fip: The FCoE controller receiving the advertisement
+ * @skb: The received FIP advertisement frame
+ * @fcf: The resulting FCF entry
*
* Returns zero on a valid parsed advertisement,
* otherwise returns non zero value.
*/
-static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
+static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
+ struct sk_buff *skb, struct fcoe_fcf *fcf)
{
struct fip_header *fiph;
struct fip_desc *desc = NULL;
@@ -636,7 +657,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
((struct fip_mac_desc *)desc)->fd_mac,
ETH_ALEN);
if (!is_valid_ether_addr(fcf->fcf_mac)) {
- LIBFCOE_FIP_DBG("Invalid MAC address "
+ LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
"in FIP adv\n");
return -EINVAL;
}
@@ -659,6 +680,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
if (dlen != sizeof(struct fip_fka_desc))
goto len_err;
fka = (struct fip_fka_desc *)desc;
+ if (fka->fd_flags & FIP_FKA_ADV_D)
+ fcf->fd_flags = 1;
t = ntohl(fka->fd_fka_period);
if (t >= FCOE_CTLR_MIN_FKA)
fcf->fka_period = msecs_to_jiffies(t);
@@ -670,7 +693,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
case FIP_DT_LOGO:
case FIP_DT_ELP:
default:
- LIBFCOE_FIP_DBG("unexpected descriptor type %x "
+ LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
"in FIP adv\n", desc->fip_dtype);
/* standard says ignore unknown descriptors >= 128 */
if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
@@ -687,15 +710,15 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
return 0;
len_err:
- LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+ LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
desc->fip_dtype, dlen);
return -EINVAL;
}
/**
- * fcoe_ctlr_recv_adv() - Handle an incoming advertisement.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv_adv() - Handle an incoming advertisement
+ * @fip: The FCoE controller receiving the advertisement
+ * @skb: The received FIP packet
*/
static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
@@ -706,7 +729,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
int first = 0;
int mtu_valid;
- if (fcoe_ctlr_parse_adv(skb, &new))
+ if (fcoe_ctlr_parse_adv(fip, skb, &new))
return;
spin_lock_bh(&fip->lock);
@@ -752,7 +775,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
mtu_valid = fcoe_ctlr_mtu_valid(fcf);
fcf->time = jiffies;
if (!found) {
- LIBFCOE_FIP_DBG("New FCF for fab %llx map %x val %d\n",
+ LIBFCOE_FIP_DBG(fip, "New FCF for fab %llx map %x val %d\n",
fcf->fabric_name, fcf->fc_map, mtu_valid);
}
@@ -778,7 +801,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
*/
if (mtu_valid && !fip->sel_time && fcoe_ctlr_fcf_usable(fcf)) {
fip->sel_time = jiffies +
- msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+ msecs_to_jiffies(FCOE_CTLR_START_DELAY);
if (!timer_pending(&fip->timer) ||
time_before(fip->sel_time, fip->timer.expires))
mod_timer(&fip->timer, fip->sel_time);
@@ -788,15 +811,15 @@ out:
}
/**
- * fcoe_ctlr_recv_els() - Handle an incoming FIP-encapsulated ELS frame.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame
+ * @fip: The FCoE controller which received the packet
+ * @skb: The received FIP packet
*/
static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- struct fc_lport *lp = fip->lp;
+ struct fc_lport *lport = fip->lp;
struct fip_header *fiph;
- struct fc_frame *fp;
+ struct fc_frame *fp = (struct fc_frame *)skb;
struct fc_frame_header *fh = NULL;
struct fip_desc *desc;
struct fip_encaps *els;
@@ -831,10 +854,11 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
((struct fip_mac_desc *)desc)->fd_mac,
ETH_ALEN);
if (!is_valid_ether_addr(granted_mac)) {
- LIBFCOE_FIP_DBG("Invalid MAC address "
+ LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
"in FIP ELS\n");
goto drop;
}
+ memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
break;
case FIP_DT_FLOGI:
case FIP_DT_FDISC:
@@ -850,7 +874,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
els_dtype = desc->fip_dtype;
break;
default:
- LIBFCOE_FIP_DBG("unexpected descriptor type %x "
+ LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
"in FIP adv\n", desc->fip_dtype);
/* standard says ignore unknown descriptors >= 128 */
if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
@@ -867,11 +891,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP &&
fip->flogi_oxid == ntohs(fh->fh_ox_id) &&
- els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) {
+ els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac))
fip->flogi_oxid = FC_XID_UNKNOWN;
- fip->update_mac(fip, fip->data_src_addr, granted_mac);
- memcpy(fip->data_src_addr, granted_mac, ETH_ALEN);
- }
/*
* Convert skb into an fc_frame containing only the ELS.
@@ -882,32 +903,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
fc_frame_init(fp);
fr_sof(fp) = FC_SOF_I3;
fr_eof(fp) = FC_EOF_T;
- fr_dev(fp) = lp;
+ fr_dev(fp) = lport;
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->RxFrames++;
stats->RxWords += skb->len / FIP_BPW;
- fc_exch_recv(lp, fp);
+ fc_exch_recv(lport, fp);
return;
len_err:
- LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+ LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
desc->fip_dtype, dlen);
drop:
kfree_skb(skb);
}
/**
- * fcoe_ctlr_recv_els() - Handle an incoming link reset frame.
- * @fip: FCoE controller.
- * @fh: Received FIP header.
+ * fcoe_ctlr_recv_els() - Handle an incoming link reset frame
+ * @fip: The FCoE controller that received the frame
+ * @fh: The received FIP header
*
* There may be multiple VN_Port descriptors.
* The overall length has already been checked.
*/
static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
- struct fip_header *fh)
+ struct fip_header *fh)
{
struct fip_desc *desc;
struct fip_mac_desc *mp;
@@ -916,13 +937,13 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
size_t rlen;
size_t dlen;
struct fcoe_fcf *fcf = fip->sel_fcf;
- struct fc_lport *lp = fip->lp;
+ struct fc_lport *lport = fip->lp;
u32 desc_mask;
- LIBFCOE_FIP_DBG("Clear Virtual Link received\n");
+ LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
if (!fcf)
return;
- if (!fcf || !fc_host_port_id(lp->host))
+ if (!fcf || !fc_host_port_id(lport->host))
return;
/*
@@ -958,9 +979,10 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
if (dlen < sizeof(*vp))
return;
if (compare_ether_addr(vp->fd_mac,
- fip->data_src_addr) == 0 &&
- get_unaligned_be64(&vp->fd_wwpn) == lp->wwpn &&
- ntoh24(vp->fd_fc_id) == fc_host_port_id(lp->host))
+ fip->get_src_addr(lport)) == 0 &&
+ get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
+ ntoh24(vp->fd_fc_id) ==
+ fc_host_port_id(lport->host))
desc_mask &= ~BIT(FIP_DT_VN_ID);
break;
default:
@@ -977,33 +999,39 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
* reset only if all required descriptors were present and valid.
*/
if (desc_mask) {
- LIBFCOE_FIP_DBG("missing descriptors mask %x\n", desc_mask);
+ LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
+ desc_mask);
} else {
- LIBFCOE_FIP_DBG("performing Clear Virtual Link\n");
- fcoe_ctlr_reset(fip, FIP_ST_ENABLED);
+ LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
+
+ spin_lock_bh(&fip->lock);
+ fc_lport_get_stats(lport)->VLinkFailureCount++;
+ fcoe_ctlr_reset(fip);
+ spin_unlock_bh(&fip->lock);
+
+ fc_lport_reset(fip->lp);
+ fcoe_ctlr_solicit(fip, NULL);
}
}
/**
- * fcoe_ctlr_recv() - Receive a FIP frame.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv() - Receive a FIP packet
+ * @fip: The FCoE controller that received the packet
+ * @skb: The received FIP packet
*
- * This is called from NET_RX_SOFTIRQ.
+ * This may be called from either NET_RX_SOFTIRQ or IRQ.
*/
void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- spin_lock_bh(&fip->fip_recv_list.lock);
- __skb_queue_tail(&fip->fip_recv_list, skb);
- spin_unlock_bh(&fip->fip_recv_list.lock);
+ skb_queue_tail(&fip->fip_recv_list, skb);
schedule_work(&fip->recv_work);
}
EXPORT_SYMBOL(fcoe_ctlr_recv);
/**
- * fcoe_ctlr_recv_handler() - Receive a FIP frame.
- * @fip: FCoE controller.
- * @skb: Received FIP packet.
+ * fcoe_ctlr_recv_handler() - Receive a FIP frame
+ * @fip: The FCoE controller that received the frame
+ * @skb: The received FIP frame
*
* Returns non-zero if the frame is dropped.
*/
@@ -1038,7 +1066,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
fip->map_dest = 0;
fip->state = FIP_ST_ENABLED;
state = FIP_ST_ENABLED;
- LIBFCOE_FIP_DBG("Using FIP mode\n");
+ LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
}
spin_unlock_bh(&fip->lock);
if (state != FIP_ST_ENABLED)
@@ -1060,8 +1088,8 @@ drop:
}
/**
- * fcoe_ctlr_select() - Select the best FCF, if possible.
- * @fip: FCoE controller.
+ * fcoe_ctlr_select() - Select the best FCF (if possible)
+ * @fip: The FCoE controller
*
* If there are conflicting advertisements, no FCF can be chosen.
*
@@ -1073,11 +1101,11 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
struct fcoe_fcf *best = NULL;
list_for_each_entry(fcf, &fip->fcfs, list) {
- LIBFCOE_FIP_DBG("consider FCF for fab %llx VFID %d map %x "
+ LIBFCOE_FIP_DBG(fip, "consider FCF for fab %llx VFID %d map %x "
"val %d\n", fcf->fabric_name, fcf->vfid,
fcf->fc_map, fcoe_ctlr_mtu_valid(fcf));
if (!fcoe_ctlr_fcf_usable(fcf)) {
- LIBFCOE_FIP_DBG("FCF for fab %llx map %x %svalid "
+ LIBFCOE_FIP_DBG(fip, "FCF for fab %llx map %x %svalid "
"%savailable\n", fcf->fabric_name,
fcf->fc_map, (fcf->flags & FIP_FL_SOL)
? "" : "in", (fcf->flags & FIP_FL_AVAIL)
@@ -1091,7 +1119,7 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
if (fcf->fabric_name != best->fabric_name ||
fcf->vfid != best->vfid ||
fcf->fc_map != best->fc_map) {
- LIBFCOE_FIP_DBG("Conflicting fabric, VFID, "
+ LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
"or FC-MAP\n");
return;
}
@@ -1102,8 +1130,8 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
}
/**
- * fcoe_ctlr_timeout() - FIP timer function.
- * @arg: &fcoe_ctlr pointer.
+ * fcoe_ctlr_timeout() - FIP timeout handler
+ * @arg: The FCoE controller that timed out
*
* Ages FCFs. Triggers FCF selection if possible. Sends keep-alives.
*/
@@ -1113,8 +1141,6 @@ static void fcoe_ctlr_timeout(unsigned long arg)
struct fcoe_fcf *sel;
struct fcoe_fcf *fcf;
unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
- u8 send_ctlr_ka;
- u8 send_port_ka;
spin_lock_bh(&fip->lock);
if (fip->state == FIP_ST_DISABLED) {
@@ -1140,53 +1166,47 @@ static void fcoe_ctlr_timeout(unsigned long arg)
fip->lp->host->host_no, sel->fcf_mac);
memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
fip->port_ka_time = jiffies +
- msecs_to_jiffies(FIP_VN_KA_PERIOD);
+ msecs_to_jiffies(FIP_VN_KA_PERIOD);
fip->ctlr_ka_time = jiffies + sel->fka_period;
- fip->link = 1;
} else {
printk(KERN_NOTICE "libfcoe: host%d: "
- "FIP Fibre-Channel Forwarder timed out. "
+ "FIP Fibre-Channel Forwarder timed out. "
"Starting FCF discovery.\n",
fip->lp->host->host_no);
- fip->link = 0;
+ fip->reset_req = 1;
+ schedule_work(&fip->link_work);
}
- schedule_work(&fip->link_work);
}
- send_ctlr_ka = 0;
- send_port_ka = 0;
- if (sel) {
+ if (sel && !sel->fd_flags) {
if (time_after_eq(jiffies, fip->ctlr_ka_time)) {
fip->ctlr_ka_time = jiffies + sel->fka_period;
- send_ctlr_ka = 1;
+ fip->send_ctlr_ka = 1;
}
if (time_after(next_timer, fip->ctlr_ka_time))
next_timer = fip->ctlr_ka_time;
if (time_after_eq(jiffies, fip->port_ka_time)) {
fip->port_ka_time += jiffies +
- msecs_to_jiffies(FIP_VN_KA_PERIOD);
- send_port_ka = 1;
+ msecs_to_jiffies(FIP_VN_KA_PERIOD);
+ fip->send_port_ka = 1;
}
if (time_after(next_timer, fip->port_ka_time))
next_timer = fip->port_ka_time;
mod_timer(&fip->timer, next_timer);
} else if (fip->sel_time) {
next_timer = fip->sel_time +
- msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+ msecs_to_jiffies(FCOE_CTLR_START_DELAY);
mod_timer(&fip->timer, next_timer);
}
+ if (fip->send_ctlr_ka || fip->send_port_ka)
+ schedule_work(&fip->link_work);
spin_unlock_bh(&fip->lock);
-
- if (send_ctlr_ka)
- fcoe_ctlr_send_keep_alive(fip, 0, fip->ctl_src_addr);
- if (send_port_ka)
- fcoe_ctlr_send_keep_alive(fip, 1, fip->data_src_addr);
}
/**
- * fcoe_ctlr_link_work() - worker thread function for link changes.
- * @work: pointer to link_work member inside &fcoe_ctlr.
+ * fcoe_ctlr_link_work() - Worker thread function for link changes
+ * @work: Handle to a FCoE controller
*
* See if the link status has changed and if so, report it.
*
@@ -1196,27 +1216,49 @@ static void fcoe_ctlr_timeout(unsigned long arg)
static void fcoe_ctlr_link_work(struct work_struct *work)
{
struct fcoe_ctlr *fip;
+ struct fc_lport *vport;
+ u8 *mac;
int link;
int last_link;
+ int reset;
fip = container_of(work, struct fcoe_ctlr, link_work);
spin_lock_bh(&fip->lock);
last_link = fip->last_link;
link = fip->link;
fip->last_link = link;
+ reset = fip->reset_req;
+ fip->reset_req = 0;
spin_unlock_bh(&fip->lock);
if (last_link != link) {
if (link)
fc_linkup(fip->lp);
else
- fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+ fc_linkdown(fip->lp);
+ } else if (reset && link)
+ fc_lport_reset(fip->lp);
+
+ if (fip->send_ctlr_ka) {
+ fip->send_ctlr_ka = 0;
+ fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
+ }
+ if (fip->send_port_ka) {
+ fip->send_port_ka = 0;
+ mutex_lock(&fip->lp->lp_mutex);
+ mac = fip->get_src_addr(fip->lp);
+ fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
+ list_for_each_entry(vport, &fip->lp->vports, list) {
+ mac = fip->get_src_addr(vport);
+ fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
+ }
+ mutex_unlock(&fip->lp->lp_mutex);
}
}
/**
- * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames.
- * @recv_work: pointer to recv_work member inside &fcoe_ctlr.
+ * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames
+ * @recv_work: Handle to a FCoE controller
*/
static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
{
@@ -1224,20 +1266,14 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
struct sk_buff *skb;
fip = container_of(recv_work, struct fcoe_ctlr, recv_work);
- spin_lock_bh(&fip->fip_recv_list.lock);
- while ((skb = __skb_dequeue(&fip->fip_recv_list))) {
- spin_unlock_bh(&fip->fip_recv_list.lock);
+ while ((skb = skb_dequeue(&fip->fip_recv_list)))
fcoe_ctlr_recv_handler(fip, skb);
- spin_lock_bh(&fip->fip_recv_list.lock);
- }
- spin_unlock_bh(&fip->fip_recv_list.lock);
}
/**
- * fcoe_ctlr_recv_flogi() - snoop Pre-FIP receipt of FLOGI response or request.
- * @fip: FCoE controller.
- * @fp: FC frame.
- * @sa: Ethernet source MAC address from received FCoE frame.
+ * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response
+ * @fip: The FCoE controller
+ * @fp: The FC frame to snoop
*
* Snoop potential response to FLOGI or even incoming FLOGI.
*
@@ -1245,15 +1281,18 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
* by fip->flogi_oxid != FC_XID_UNKNOWN.
*
* The caller is responsible for freeing the frame.
+ * Fill in the granted_mac address.
*
* Return non-zero if the frame should not be delivered to libfc.
*/
-int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
+ struct fc_frame *fp)
{
struct fc_frame_header *fh;
u8 op;
- u8 mac[ETH_ALEN];
+ u8 *sa;
+ sa = eth_hdr(&fp->skb)->h_source;
fh = fc_frame_header_get(fp);
if (fh->fh_type != FC_TYPE_ELS)
return 0;
@@ -1268,7 +1307,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
return -EINVAL;
}
fip->state = FIP_ST_NON_FIP;
- LIBFCOE_FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n");
+ LIBFCOE_FIP_DBG(fip,
+ "received FLOGI LS_ACC using non-FIP mode\n");
/*
* FLOGI accepted.
@@ -1283,11 +1323,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
fip->map_dest = 0;
}
fip->flogi_oxid = FC_XID_UNKNOWN;
- memcpy(mac, fip->data_src_addr, ETH_ALEN);
- fc_fcoe_set_mac(fip->data_src_addr, fh->fh_d_id);
spin_unlock_bh(&fip->lock);
-
- fip->update_mac(fip, mac, fip->data_src_addr);
+ fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
/*
* Save source MAC for point-to-point responses.
@@ -1297,7 +1334,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
memcpy(fip->dest_addr, sa, ETH_ALEN);
fip->map_dest = 0;
if (fip->state == FIP_ST_NON_FIP)
- LIBFCOE_FIP_DBG("received FLOGI REQ, "
+ LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, "
"using non-FIP mode\n");
fip->state = FIP_ST_NON_FIP;
}
@@ -1308,10 +1345,10 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
EXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
/**
- * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
- * @mac: mac address
- * @scheme: check port
- * @port: port indicator for converting
+ * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN
+ * @mac: The MAC address to convert
+ * @scheme: The scheme to use when converting
+ * @port: The port indicator for converting
*
* Returns: u64 fc world wide name
*/
@@ -1349,24 +1386,26 @@ u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
/**
- * fcoe_libfc_config() - sets up libfc related properties for lport
- * @lp: ptr to the fc_lport
- * @tt: libfc function template
+ * fcoe_libfc_config() - Sets up libfc related properties for local port
+ * @lp: The local port to configure libfc for
+ * @tt: The libfc function template
*
* Returns : 0 for success
*/
-int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+int fcoe_libfc_config(struct fc_lport *lport,
+ struct libfc_function_template *tt)
{
/* Set the function pointers set by the LLDD */
- memcpy(&lp->tt, tt, sizeof(*tt));
- if (fc_fcp_init(lp))
+ memcpy(&lport->tt, tt, sizeof(*tt));
+ if (fc_fcp_init(lport))
return -ENOMEM;
- fc_exch_init(lp);
- fc_elsct_init(lp);
- fc_lport_init(lp);
- fc_rport_init(lp);
- fc_disc_init(lp);
+ fc_exch_init(lport);
+ fc_elsct_init(lport);
+ fc_lport_init(lport);
+ fc_rport_init(lport);
+ fc_disc_init(lport);
return 0;
}
EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index e4c0a3d7d87..bb208a6091e 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -22,6 +22,7 @@
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
#include "fnic_io.h"
#include "fnic_res.h"
#include "vnic_dev.h"
@@ -44,7 +45,7 @@
#define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */
#define FNIC_DFLT_QUEUE_DEPTH 32
#define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */
-
+#define FNIC_MAX_CMD_LEN 16 /* Supported CDB length */
/*
* Tag bits used for special requests.
*/
@@ -145,6 +146,7 @@ struct mempool;
/* Per-instance private data structure */
struct fnic {
struct fc_lport *lport;
+ struct fcoe_ctlr ctlr; /* FIP FCoE controller structure */
struct vnic_dev_bar bar0;
struct msix_entry msix_entry[FNIC_MSIX_INTR_MAX];
@@ -162,23 +164,16 @@ struct fnic {
unsigned int wq_count;
unsigned int cq_count;
- u32 fcoui_mode:1; /* use fcoui address*/
u32 vlan_hw_insert:1; /* let hw insert the tag */
u32 in_remove:1; /* fnic device in removal */
u32 stop_rx_link_events:1; /* stop proc. rx frames, link events */
struct completion *remove_wait; /* device remove thread blocks */
- struct fc_frame *flogi;
- struct fc_frame *flogi_resp;
- u16 flogi_oxid;
- unsigned long s_id;
enum fnic_state state;
spinlock_t fnic_lock;
u16 vlan_id; /* VLAN tag including priority */
- u8 mac_addr[ETH_ALEN];
- u8 dest_addr[ETH_ALEN];
u8 data_src_addr[ETH_ALEN];
u64 fcp_input_bytes; /* internal statistic */
u64 fcp_output_bytes; /* internal statistic */
@@ -205,6 +200,7 @@ struct fnic {
struct work_struct link_work;
struct work_struct frame_work;
struct sk_buff_head frame_queue;
+ struct sk_buff_head tx_queue;
/* copy work queue cache line section */
____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX];
@@ -224,6 +220,11 @@ struct fnic {
____cacheline_aligned struct vnic_intr intr[FNIC_MSIX_INTR_MAX];
};
+static inline struct fnic *fnic_from_ctlr(struct fcoe_ctlr *fip)
+{
+ return container_of(fip, struct fnic, ctlr);
+}
+
extern struct workqueue_struct *fnic_event_queue;
extern struct device_attribute *fnic_attrs[];
@@ -239,7 +240,11 @@ void fnic_handle_link(struct work_struct *work);
int fnic_rq_cmpl_handler(struct fnic *fnic, int);
int fnic_alloc_rq_frame(struct vnic_rq *rq);
void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf);
-int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp);
+void fnic_flush_tx(struct fnic *);
+void fnic_eth_send(struct fcoe_ctlr *, struct sk_buff *skb);
+void fnic_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+void fnic_update_mac(struct fc_lport *, u8 *new);
+void fnic_update_mac_locked(struct fnic *, u8 *new);
int fnic_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
int fnic_abort_cmd(struct scsi_cmnd *);
@@ -252,7 +257,7 @@ void fnic_empty_scsi_cleanup(struct fc_lport *);
void fnic_exch_mgr_reset(struct fc_lport *, u32, u32);
int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int);
int fnic_wq_cmpl_handler(struct fnic *fnic, int);
-int fnic_flogi_reg_handler(struct fnic *fnic);
+int fnic_flogi_reg_handler(struct fnic *fnic, u32);
void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
struct fcpio_host_req *desc);
int fnic_fw_reset_handler(struct fnic *fnic);
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 50db3e36a61..54f8d0e5407 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -23,6 +23,7 @@
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/workqueue.h>
+#include <scsi/fc/fc_fip.h>
#include <scsi/fc/fc_els.h>
#include <scsi/fc/fc_fcoe.h>
#include <scsi/fc_frame.h>
@@ -34,6 +35,8 @@
struct workqueue_struct *fnic_event_queue;
+static void fnic_set_eth_mode(struct fnic *);
+
void fnic_handle_link(struct work_struct *work)
{
struct fnic *fnic = container_of(work, struct fnic, link_work);
@@ -64,10 +67,10 @@ void fnic_handle_link(struct work_struct *work)
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"link down\n");
- fc_linkdown(fnic->lport);
+ fcoe_ctlr_link_down(&fnic->ctlr);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"link up\n");
- fc_linkup(fnic->lport);
+ fcoe_ctlr_link_up(&fnic->ctlr);
} else
/* UP -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -76,13 +79,13 @@ void fnic_handle_link(struct work_struct *work)
/* DOWN -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
- fc_linkup(fnic->lport);
+ fcoe_ctlr_link_up(&fnic->ctlr);
} else {
/* UP -> DOWN */
fnic->lport->host_stats.link_failure_count++;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
- fc_linkdown(fnic->lport);
+ fcoe_ctlr_link_down(&fnic->ctlr);
}
}
@@ -107,197 +110,179 @@ void fnic_handle_frame(struct work_struct *work)
return;
}
fp = (struct fc_frame *)skb;
- /* if Flogi resp frame, register the address */
- if (fr_flags(fp)) {
- vnic_dev_add_addr(fnic->vdev,
- fnic->data_src_addr);
- fr_flags(fp) = 0;
+
+ /*
+ * If we're in a transitional state, just re-queue and return.
+ * The queue will be serviced when we get to a stable state.
+ */
+ if (fnic->state != FNIC_IN_FC_MODE &&
+ fnic->state != FNIC_IN_ETH_MODE) {
+ skb_queue_head(&fnic->frame_queue, skb);
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return;
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
fc_exch_recv(lp, fp);
}
-
-}
-
-static inline void fnic_import_rq_fc_frame(struct sk_buff *skb,
- u32 len, u8 sof, u8 eof)
-{
- struct fc_frame *fp = (struct fc_frame *)skb;
-
- skb_trim(skb, len);
- fr_eof(fp) = eof;
- fr_sof(fp) = sof;
}
-
-static inline int fnic_import_rq_eth_pkt(struct sk_buff *skb, u32 len)
+/**
+ * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame.
+ * @fnic: fnic instance.
+ * @skb: Ethernet Frame.
+ */
+static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb)
{
struct fc_frame *fp;
struct ethhdr *eh;
- struct vlan_ethhdr *vh;
struct fcoe_hdr *fcoe_hdr;
struct fcoe_crc_eof *ft;
- u32 transport_len = 0;
+ /*
+ * Undo VLAN encapsulation if present.
+ */
eh = (struct ethhdr *)skb->data;
- vh = (struct vlan_ethhdr *)skb->data;
- if (vh->h_vlan_proto == htons(ETH_P_8021Q) &&
- vh->h_vlan_encapsulated_proto == htons(ETH_P_FCOE)) {
- skb_pull(skb, sizeof(struct vlan_ethhdr));
- transport_len += sizeof(struct vlan_ethhdr);
- } else if (eh->h_proto == htons(ETH_P_FCOE)) {
- transport_len += sizeof(struct ethhdr);
- skb_pull(skb, sizeof(struct ethhdr));
- } else
- return -1;
+ if (eh->h_proto == htons(ETH_P_8021Q)) {
+ memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
+ eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
+ skb_reset_mac_header(skb);
+ }
+ if (eh->h_proto == htons(ETH_P_FIP)) {
+ skb_pull(skb, sizeof(*eh));
+ fcoe_ctlr_recv(&fnic->ctlr, skb);
+ return 1; /* let caller know packet was used */
+ }
+ if (eh->h_proto != htons(ETH_P_FCOE))
+ goto drop;
+ skb_set_network_header(skb, sizeof(*eh));
+ skb_pull(skb, sizeof(*eh));
fcoe_hdr = (struct fcoe_hdr *)skb->data;
if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER)
- return -1;
+ goto drop;
fp = (struct fc_frame *)skb;
fc_frame_init(fp);
fr_sof(fp) = fcoe_hdr->fcoe_sof;
skb_pull(skb, sizeof(struct fcoe_hdr));
- transport_len += sizeof(struct fcoe_hdr);
+ skb_reset_transport_header(skb);
- ft = (struct fcoe_crc_eof *)(skb->data + len -
- transport_len - sizeof(*ft));
+ ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft));
fr_eof(fp) = ft->fcoe_eof;
- skb_trim(skb, len - transport_len - sizeof(*ft));
+ skb_trim(skb, skb->len - sizeof(*ft));
return 0;
+drop:
+ dev_kfree_skb_irq(skb);
+ return -1;
}
-static inline int fnic_handle_flogi_resp(struct fnic *fnic,
- struct fc_frame *fp)
+/**
+ * fnic_update_mac_locked() - set data MAC address and filters.
+ * @fnic: fnic instance.
+ * @new: newly-assigned FCoE MAC address.
+ *
+ * Called with the fnic lock held.
+ */
+void fnic_update_mac_locked(struct fnic *fnic, u8 *new)
{
- u8 mac[ETH_ALEN] = FC_FCOE_FLOGI_MAC;
- struct ethhdr *eth_hdr;
- struct fc_frame_header *fh;
- int ret = 0;
- unsigned long flags;
- struct fc_frame *old_flogi_resp = NULL;
+ u8 *ctl = fnic->ctlr.ctl_src_addr;
+ u8 *data = fnic->data_src_addr;
- fh = (struct fc_frame_header *)fr_hdr(fp);
+ if (is_zero_ether_addr(new))
+ new = ctl;
+ if (!compare_ether_addr(data, new))
+ return;
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
+ if (!is_zero_ether_addr(data) && compare_ether_addr(data, ctl))
+ vnic_dev_del_addr(fnic->vdev, data);
+ memcpy(data, new, ETH_ALEN);
+ if (compare_ether_addr(new, ctl))
+ vnic_dev_add_addr(fnic->vdev, new);
+}
- spin_lock_irqsave(&fnic->fnic_lock, flags);
+/**
+ * fnic_update_mac() - set data MAC address and filters.
+ * @lport: local port.
+ * @new: newly-assigned FCoE MAC address.
+ */
+void fnic_update_mac(struct fc_lport *lport, u8 *new)
+{
+ struct fnic *fnic = lport_priv(lport);
- if (fnic->state == FNIC_IN_ETH_MODE) {
+ spin_lock_irq(&fnic->fnic_lock);
+ fnic_update_mac_locked(fnic, new);
+ spin_unlock_irq(&fnic->fnic_lock);
+}
- /*
- * Check if oxid matches on taking the lock. A new Flogi
- * issued by libFC might have changed the fnic cached oxid
- */
- if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) {
- FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
- "Flogi response oxid not"
- " matching cached oxid, dropping frame"
- "\n");
- ret = -1;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- dev_kfree_skb_irq(fp_skb(fp));
- goto handle_flogi_resp_end;
- }
+/**
+ * fnic_set_port_id() - set the port_ID after successful FLOGI.
+ * @lport: local port.
+ * @port_id: assigned FC_ID.
+ * @fp: received frame containing the FLOGI accept or NULL.
+ *
+ * This is called from libfc when a new FC_ID has been assigned.
+ * This causes us to reset the firmware to FC_MODE and setup the new MAC
+ * address and FC_ID.
+ *
+ * It is also called with FC_ID 0 when we're logged off.
+ *
+ * If the FC_ID is due to point-to-point, fp may be NULL.
+ */
+void fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
+{
+ struct fnic *fnic = lport_priv(lport);
+ u8 *mac;
+ int ret;
- /* Drop older cached flogi response frame, cache this frame */
- old_flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = fp;
- fnic->flogi_oxid = FC_XID_UNKNOWN;
+ FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n",
+ port_id, fp);
- /*
- * this frame is part of flogi get the src mac addr from this
- * frame if the src mac is fcoui based then we mark the
- * address mode flag to use fcoui base for dst mac addr
- * otherwise we have to store the fcoe gateway addr
- */
- eth_hdr = (struct ethhdr *)skb_mac_header(fp_skb(fp));
- memcpy(mac, eth_hdr->h_source, ETH_ALEN);
+ /*
+ * If we're clearing the FC_ID, change to use the ctl_src_addr.
+ * Set ethernet mode to send FLOGI.
+ */
+ if (!port_id) {
+ fnic_update_mac(lport, fnic->ctlr.ctl_src_addr);
+ fnic_set_eth_mode(fnic);
+ return;
+ }
- if (ntoh24(mac) == FC_FCOE_OUI)
- fnic->fcoui_mode = 1;
- else {
- fnic->fcoui_mode = 0;
- memcpy(fnic->dest_addr, mac, ETH_ALEN);
+ if (fp) {
+ mac = fr_cb(fp)->granted_mac;
+ if (is_zero_ether_addr(mac)) {
+ /* non-FIP - FLOGI already accepted - ignore return */
+ fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp);
}
+ fnic_update_mac(lport, mac);
+ }
- /*
- * Except for Flogi frame, all outbound frames from us have the
- * Eth Src address as FC_FCOE_OUI"our_sid". Flogi frame uses
- * the vnic MAC address as the Eth Src address
- */
- fc_fcoe_set_mac(fnic->data_src_addr, fh->fh_d_id);
-
- /* We get our s_id from the d_id of the flogi resp frame */
- fnic->s_id = ntoh24(fh->fh_d_id);
-
- /* Change state to reflect transition from Eth to FC mode */
+ /* Change state to reflect transition to FC mode */
+ spin_lock_irq(&fnic->fnic_lock);
+ if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
-
- } else {
+ else {
FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
"Unexpected fnic state %s while"
" processing flogi resp\n",
fnic_state_to_str(fnic->state));
- ret = -1;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- dev_kfree_skb_irq(fp_skb(fp));
- goto handle_flogi_resp_end;
+ spin_unlock_irq(&fnic->fnic_lock);
+ return;
}
-
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
- /* Drop older cached frame */
- if (old_flogi_resp)
- dev_kfree_skb_irq(fp_skb(old_flogi_resp));
+ spin_unlock_irq(&fnic->fnic_lock);
/*
- * send flogi reg request to firmware, this will put the fnic in
- * in FC mode
+ * Send FLOGI registration to firmware to set up FC mode.
+ * The new address will be set up when registration completes.
*/
- ret = fnic_flogi_reg_handler(fnic);
+ ret = fnic_flogi_reg_handler(fnic, port_id);
if (ret < 0) {
- int free_fp = 1;
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- /*
- * free the frame is some other thread is not
- * pointing to it
- */
- if (fnic->flogi_resp != fp)
- free_fp = 0;
- else
- fnic->flogi_resp = NULL;
-
+ spin_lock_irq(&fnic->fnic_lock);
if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
fnic->state = FNIC_IN_ETH_MODE;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (free_fp)
- dev_kfree_skb_irq(fp_skb(fp));
+ spin_unlock_irq(&fnic->fnic_lock);
}
-
- handle_flogi_resp_end:
- return ret;
-}
-
-/* Returns 1 for a response that matches cached flogi oxid */
-static inline int is_matching_flogi_resp_frame(struct fnic *fnic,
- struct fc_frame *fp)
-{
- struct fc_frame_header *fh;
- int ret = 0;
- u32 f_ctl;
-
- fh = fc_frame_header_get(fp);
- f_ctl = ntoh24(fh->fh_f_ctl);
-
- if (fnic->flogi_oxid == ntohs(fh->fh_ox_id) &&
- fh->fh_r_ctl == FC_RCTL_ELS_REP &&
- (f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == FC_FC_EX_CTX &&
- fh->fh_type == FC_TYPE_ELS)
- ret = 1;
-
- return ret;
}
static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
@@ -326,6 +311,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
PCI_DMA_FROMDEVICE);
skb = buf->os_buf;
+ fp = (struct fc_frame *)skb;
buf->os_buf = NULL;
cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
@@ -338,6 +324,9 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
&fcoe_enc_error, &fcs_ok, &vlan_stripped,
&vlan);
eth_hdrs_stripped = 1;
+ skb_trim(skb, fcp_bytes_written);
+ fr_sof(fp) = sof;
+ fr_eof(fp) = eof;
} else if (type == CQ_DESC_TYPE_RQ_ENET) {
cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
@@ -352,6 +341,14 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
&ipv4_csum_ok, &ipv6, &ipv4,
&ipv4_fragment, &fcs_ok);
eth_hdrs_stripped = 0;
+ skb_trim(skb, bytes_written);
+ if (!fcs_ok) {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "fcs error. dropping packet.\n");
+ goto drop;
+ }
+ if (fnic_import_rq_eth_pkt(fnic, skb))
+ return;
} else {
/* wrong CQ type*/
@@ -370,43 +367,11 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
goto drop;
}
- if (eth_hdrs_stripped)
- fnic_import_rq_fc_frame(skb, fcp_bytes_written, sof, eof);
- else if (fnic_import_rq_eth_pkt(skb, bytes_written))
- goto drop;
-
- fp = (struct fc_frame *)skb;
-
- /*
- * If frame is an ELS response that matches the cached FLOGI OX_ID,
- * and is accept, issue flogi_reg_request copy wq request to firmware
- * to register the S_ID and determine whether FC_OUI mode or GW mode.
- */
- if (is_matching_flogi_resp_frame(fnic, fp)) {
- if (!eth_hdrs_stripped) {
- if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
- fnic_handle_flogi_resp(fnic, fp);
- return;
- }
- /*
- * Recd. Flogi reject. No point registering
- * with fw, but forward to libFC
- */
- goto forward;
- }
- goto drop;
- }
- if (!eth_hdrs_stripped)
- goto drop;
-
-forward:
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (fnic->stop_rx_link_events) {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
goto drop;
}
- /* Use fr_flags to indicate whether succ. flogi resp or not */
- fr_flags(fp) = 0;
fr_dev(fp) = fnic->lport;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -494,12 +459,49 @@ void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
buf->os_buf = NULL;
}
-static inline int is_flogi_frame(struct fc_frame_header *fh)
+/**
+ * fnic_eth_send() - Send Ethernet frame.
+ * @fip: fcoe_ctlr instance.
+ * @skb: Ethernet Frame, FIP, without VLAN encapsulation.
+ */
+void fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- return fh->fh_r_ctl == FC_RCTL_ELS_REQ && *(u8 *)(fh + 1) == ELS_FLOGI;
+ struct fnic *fnic = fnic_from_ctlr(fip);
+ struct vnic_wq *wq = &fnic->wq[0];
+ dma_addr_t pa;
+ struct ethhdr *eth_hdr;
+ struct vlan_ethhdr *vlan_hdr;
+ unsigned long flags;
+
+ if (!fnic->vlan_hw_insert) {
+ eth_hdr = (struct ethhdr *)skb_mac_header(skb);
+ vlan_hdr = (struct vlan_ethhdr *)skb_push(skb,
+ sizeof(*vlan_hdr) - sizeof(*eth_hdr));
+ memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN);
+ vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
+ vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto;
+ vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
+ }
+
+ pa = pci_map_single(fnic->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+
+ spin_lock_irqsave(&fnic->wq_lock[0], flags);
+ if (!vnic_wq_desc_avail(wq)) {
+ pci_unmap_single(fnic->pdev, pa, skb->len, PCI_DMA_TODEVICE);
+ spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
+ kfree_skb(skb);
+ return;
+ }
+
+ fnic_queue_wq_eth_desc(wq, skb, pa, skb->len,
+ fnic->vlan_hw_insert, fnic->vlan_id, 1);
+ spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
}
-int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
+/*
+ * Send FC frame.
+ */
+static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
{
struct vnic_wq *wq = &fnic->wq[0];
struct sk_buff *skb;
@@ -515,6 +517,10 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
fh = fc_frame_header_get(fp);
skb = fp_skb(fp);
+ if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
+ fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb))
+ return 0;
+
if (!fnic->vlan_hw_insert) {
eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr);
vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, eth_hdr_len);
@@ -530,16 +536,11 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1);
}
- if (is_flogi_frame(fh)) {
+ if (fnic->ctlr.map_dest)
fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
- memcpy(eth_hdr->h_source, fnic->mac_addr, ETH_ALEN);
- } else {
- if (fnic->fcoui_mode)
- fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
- else
- memcpy(eth_hdr->h_dest, fnic->dest_addr, ETH_ALEN);
- memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
- }
+ else
+ memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN);
+ memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
tot_len = skb->len;
BUG_ON(tot_len % 4);
@@ -578,109 +579,85 @@ fnic_send_frame_end:
int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
{
struct fnic *fnic = lport_priv(lp);
- struct fc_frame_header *fh;
- int ret = 0;
- enum fnic_state old_state;
unsigned long flags;
- struct fc_frame *old_flogi = NULL;
- struct fc_frame *old_flogi_resp = NULL;
if (fnic->in_remove) {
dev_kfree_skb(fp_skb(fp));
- ret = -1;
- goto fnic_send_end;
+ return -1;
}
- fh = fc_frame_header_get(fp);
- /* if not an Flogi frame, send it out, this is the common case */
- if (!is_flogi_frame(fh))
- return fnic_send_frame(fnic, fp);
+ /*
+ * Queue frame if in a transitional state.
+ * This occurs while registering the Port_ID / MAC address after FLOGI.
+ */
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+ if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) {
+ skb_queue_tail(&fnic->tx_queue, fp_skb(fp));
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- /* Flogi frame, now enter the state machine */
+ return fnic_send_frame(fnic, fp);
+}
- spin_lock_irqsave(&fnic->fnic_lock, flags);
-again:
- /* Get any old cached frames, free them after dropping lock */
- old_flogi = fnic->flogi;
- fnic->flogi = NULL;
- old_flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
+/**
+ * fnic_flush_tx() - send queued frames.
+ * @fnic: fnic device
+ *
+ * Send frames that were waiting to go out in FC or Ethernet mode.
+ * Whenever changing modes we purge queued frames, so these frames should
+ * be queued for the stable mode that we're in, either FC or Ethernet.
+ *
+ * Called without fnic_lock held.
+ */
+void fnic_flush_tx(struct fnic *fnic)
+{
+ struct sk_buff *skb;
+ struct fc_frame *fp;
- fnic->flogi_oxid = FC_XID_UNKNOWN;
+ while ((skb = skb_dequeue(&fnic->frame_queue))) {
+ fp = (struct fc_frame *)skb;
+ fnic_send_frame(fnic, fp);
+ }
+}
+/**
+ * fnic_set_eth_mode() - put fnic into ethernet mode.
+ * @fnic: fnic device
+ *
+ * Called without fnic lock held.
+ */
+static void fnic_set_eth_mode(struct fnic *fnic)
+{
+ unsigned long flags;
+ enum fnic_state old_state;
+ int ret;
+
+ spin_lock_irqsave(&fnic->fnic_lock, flags);
+again:
old_state = fnic->state;
switch (old_state) {
case FNIC_IN_FC_MODE:
case FNIC_IN_ETH_TRANS_FC_MODE:
default:
fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (old_flogi) {
- dev_kfree_skb(fp_skb(old_flogi));
- old_flogi = NULL;
- }
- if (old_flogi_resp) {
- dev_kfree_skb(fp_skb(old_flogi_resp));
- old_flogi_resp = NULL;
- }
-
ret = fnic_fw_reset_handler(fnic);
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE)
goto again;
- if (ret) {
+ if (ret)
fnic->state = old_state;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- dev_kfree_skb(fp_skb(fp));
- goto fnic_send_end;
- }
- old_flogi = fnic->flogi;
- fnic->flogi = fp;
- fnic->flogi_oxid = ntohs(fh->fh_ox_id);
- old_flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
break;
case FNIC_IN_FC_TRANS_ETH_MODE:
- /*
- * A reset is pending with the firmware. Store the flogi
- * and its oxid. The transition out of this state happens
- * only when Firmware completes the reset, either with
- * success or failed. If success, transition to
- * FNIC_IN_ETH_MODE, if fail, then transition to
- * FNIC_IN_FC_MODE
- */
- fnic->flogi = fp;
- fnic->flogi_oxid = ntohs(fh->fh_ox_id);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- break;
-
case FNIC_IN_ETH_MODE:
- /*
- * The fw/hw is already in eth mode. Store the oxid,
- * and send the flogi frame out. The transition out of this
- * state happens only we receive flogi response from the
- * network, and the oxid matches the cached oxid when the
- * flogi frame was sent out. If they match, then we issue
- * a flogi_reg request and transition to state
- * FNIC_IN_ETH_TRANS_FC_MODE
- */
- fnic->flogi_oxid = ntohs(fh->fh_ox_id);
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- ret = fnic_send_frame(fnic, fp);
break;
}
-
-fnic_send_end:
- if (old_flogi)
- dev_kfree_skb(fp_skb(old_flogi));
- if (old_flogi_resp)
- dev_kfree_skb(fp_skb(old_flogi_resp));
- return ret;
+ spin_unlock_irqrestore(&fnic->fnic_lock, flags);
}
static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index 2b3064828ae..5c1f223cabc 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -48,9 +48,9 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data)
}
if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
- work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
- work_done += fnic_wq_cmpl_handler(fnic, 4);
- work_done += fnic_rq_cmpl_handler(fnic, 4);
+ work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ work_done += fnic_wq_cmpl_handler(fnic, -1);
+ work_done += fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
work_done,
@@ -66,9 +66,9 @@ static irqreturn_t fnic_isr_msi(int irq, void *data)
struct fnic *fnic = data;
unsigned long work_done = 0;
- work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
- work_done += fnic_wq_cmpl_handler(fnic, 4);
- work_done += fnic_rq_cmpl_handler(fnic, 4);
+ work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+ work_done += fnic_wq_cmpl_handler(fnic, -1);
+ work_done += fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[0],
work_done,
@@ -83,7 +83,7 @@ static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
struct fnic *fnic = data;
unsigned long rq_work_done = 0;
- rq_work_done = fnic_rq_cmpl_handler(fnic, 4);
+ rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
rq_work_done,
1 /* unmask intr */,
@@ -97,7 +97,7 @@ static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
struct fnic *fnic = data;
unsigned long wq_work_done = 0;
- wq_work_done = fnic_wq_cmpl_handler(fnic, 4);
+ wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
wq_work_done,
1 /* unmask intr */,
@@ -110,7 +110,7 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
struct fnic *fnic = data;
unsigned long wq_copy_work_done = 0;
- wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, 8);
+ wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
wq_copy_work_done,
1 /* unmask intr */,
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 71c7bbe26d0..fe1b1031f7a 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -25,6 +25,8 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
+#include <linux/if_ether.h>
+#include <scsi/fc/fc_fip.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
@@ -68,6 +70,7 @@ MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
static struct libfc_function_template fnic_transport_template = {
.frame_send = fnic_send,
+ .lport_set_port_id = fnic_set_port_id,
.fcp_abort_io = fnic_empty_scsi_cleanup,
.fcp_cleanup = fnic_empty_scsi_cleanup,
.exch_mgr_reset = fnic_exch_mgr_reset
@@ -140,6 +143,7 @@ static struct fc_function_template fnic_fc_functions = {
.get_fc_host_stats = fnic_get_stats,
.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
.terminate_rport_io = fnic_terminate_rport_io,
+ .bsg_request = fc_lport_bsg_request,
};
static void fnic_get_host_speed(struct Scsi_Host *shost)
@@ -324,9 +328,6 @@ static int fnic_cleanup(struct fnic *fnic)
{
unsigned int i;
int err;
- unsigned long flags;
- struct fc_frame *flogi = NULL;
- struct fc_frame *flogi_resp = NULL;
vnic_dev_disable(fnic->vdev);
for (i = 0; i < fnic->intr_count; i++)
@@ -367,24 +368,6 @@ static int fnic_cleanup(struct fnic *fnic)
for (i = 0; i < fnic->intr_count; i++)
vnic_intr_clean(&fnic->intr[i]);
- /*
- * Remove cached flogi and flogi resp frames if any
- * These frames are not in any queue, and therefore queue
- * cleanup does not clean them. So clean them explicitly
- */
- spin_lock_irqsave(&fnic->fnic_lock, flags);
- flogi = fnic->flogi;
- fnic->flogi = NULL;
- flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
- spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
- if (flogi)
- dev_kfree_skb(fp_skb(flogi));
-
- if (flogi_resp)
- dev_kfree_skb(fp_skb(flogi_resp));
-
mempool_destroy(fnic->io_req_pool);
for (i = 0; i < FNIC_SGL_NUM_CACHES; i++)
mempool_destroy(fnic->io_sgl_pool[i]);
@@ -409,6 +392,17 @@ static void *fnic_alloc_slab_dma(gfp_t gfp_mask, void *pool_data)
return kmem_cache_alloc(mem, gfp_mask | GFP_ATOMIC | GFP_DMA);
}
+/**
+ * fnic_get_mac() - get assigned data MAC address for FIP code.
+ * @lport: local port.
+ */
+static u8 *fnic_get_mac(struct fc_lport *lport)
+{
+ struct fnic *fnic = lport_priv(lport);
+
+ return fnic->data_src_addr;
+}
+
static int __devinit fnic_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -424,17 +418,16 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
* Allocate SCSI Host and set up association between host,
* local port, and fnic
*/
- host = scsi_host_alloc(&fnic_host_template,
- sizeof(struct fc_lport) + sizeof(struct fnic));
- if (!host) {
- printk(KERN_ERR PFX "Unable to alloc SCSI host\n");
+ lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic));
+ if (!lp) {
+ printk(KERN_ERR PFX "Unable to alloc libfc local port\n");
err = -ENOMEM;
goto err_out;
}
- lp = shost_priv(host);
- lp->host = host;
+ host = lp->host;
fnic = lport_priv(lp);
fnic->lport = lp;
+ fnic->ctlr.lp = lp;
snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
host->host_no);
@@ -543,12 +536,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
goto err_out_dev_close;
}
- err = vnic_dev_mac_addr(fnic->vdev, fnic->mac_addr);
+ err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
"vNIC get MAC addr failed \n");
goto err_out_dev_close;
}
+ /* set data_src for point-to-point mode and to keep it non-zero */
+ memcpy(fnic->data_src_addr, fnic->ctlr.ctl_src_addr, ETH_ALEN);
/* Get vNIC configuration */
err = fnic_get_vnic_config(fnic);
@@ -560,6 +555,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
}
host->max_lun = fnic->config.luns_per_tgt;
host->max_id = FNIC_MAX_FCP_TARGET;
+ host->max_cmd_len = FNIC_MAX_CMD_LEN;
fnic_get_res_counts(fnic);
@@ -571,19 +567,12 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
goto err_out_dev_close;
}
- err = fnic_request_intr(fnic);
- if (err) {
- shost_printk(KERN_ERR, fnic->lport->host,
- "Unable to request irq.\n");
- goto err_out_clear_intr;
- }
-
err = fnic_alloc_vnic_resources(fnic);
if (err) {
shost_printk(KERN_ERR, fnic->lport->host,
"Failed to alloc vNIC resources, "
"aborting.\n");
- goto err_out_free_intr;
+ goto err_out_clear_intr;
}
@@ -623,9 +612,21 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fnic->vlan_hw_insert = 1;
fnic->vlan_id = 0;
- fnic->flogi_oxid = FC_XID_UNKNOWN;
- fnic->flogi = NULL;
- fnic->flogi_resp = NULL;
+ /* Initialize the FIP fcoe_ctrl struct */
+ fnic->ctlr.send = fnic_eth_send;
+ fnic->ctlr.update_mac = fnic_update_mac;
+ fnic->ctlr.get_src_addr = fnic_get_mac;
+ fcoe_ctlr_init(&fnic->ctlr);
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "firmware supports FIP\n");
+ vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
+ vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
+ } else {
+ shost_printk(KERN_INFO, fnic->lport->host,
+ "firmware uses non-FIP mode\n");
+ fnic->ctlr.mode = FIP_ST_NON_FIP;
+ }
fnic->state = FNIC_IN_FC_MODE;
/* Enable hardware stripping of vlan header on ingress */
@@ -716,6 +717,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
INIT_WORK(&fnic->link_work, fnic_handle_link);
INIT_WORK(&fnic->frame_work, fnic_handle_frame);
skb_queue_head_init(&fnic->frame_queue);
+ skb_queue_head_init(&fnic->tx_queue);
/* Enable all queues */
for (i = 0; i < fnic->raw_wq_count; i++)
@@ -728,6 +730,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fc_fabric_login(lp);
vnic_dev_enable(fnic->vdev);
+
+ err = fnic_request_intr(fnic);
+ if (err) {
+ shost_printk(KERN_ERR, fnic->lport->host,
+ "Unable to request irq.\n");
+ goto err_out_free_exch_mgr;
+ }
+
for (i = 0; i < fnic->intr_count; i++)
vnic_intr_unmask(&fnic->intr[i]);
@@ -738,8 +748,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
err_out_free_exch_mgr:
fc_exch_mgr_free(lp);
err_out_remove_scsi_host:
- fc_remove_host(fnic->lport->host);
- scsi_remove_host(fnic->lport->host);
+ fc_remove_host(lp->host);
+ scsi_remove_host(lp->host);
err_out_free_rq_buf:
for (i = 0; i < fnic->rq_count; i++)
vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
@@ -752,8 +762,6 @@ err_out_free_ioreq_pool:
mempool_destroy(fnic->io_req_pool);
err_out_free_resources:
fnic_free_vnic_resources(fnic);
-err_out_free_intr:
- fnic_free_intr(fnic);
err_out_clear_intr:
fnic_clear_intr_mode(fnic);
err_out_dev_close:
@@ -775,6 +783,7 @@ err_out:
static void __devexit fnic_remove(struct pci_dev *pdev)
{
struct fnic *fnic = pci_get_drvdata(pdev);
+ struct fc_lport *lp = fnic->lport;
unsigned long flags;
/*
@@ -796,6 +805,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
*/
flush_workqueue(fnic_event_queue);
skb_queue_purge(&fnic->frame_queue);
+ skb_queue_purge(&fnic->tx_queue);
/*
* Log off the fabric. This stops all remote ports, dns port,
@@ -808,7 +818,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
fnic->in_remove = 1;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- fc_lport_destroy(fnic->lport);
+ fcoe_ctlr_destroy(&fnic->ctlr);
+ fc_lport_destroy(lp);
/*
* This stops the fnic device, masks all interrupts. Completed
@@ -818,6 +829,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
fnic_cleanup(fnic);
BUG_ON(!skb_queue_empty(&fnic->frame_queue));
+ BUG_ON(!skb_queue_empty(&fnic->tx_queue));
spin_lock_irqsave(&fnic_list_lock, flags);
list_del(&fnic->list);
@@ -827,8 +839,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
scsi_remove_host(fnic->lport->host);
fc_exch_mgr_free(fnic->lport);
vnic_dev_notify_unset(fnic->vdev);
- fnic_free_vnic_resources(fnic);
fnic_free_intr(fnic);
+ fnic_free_vnic_resources(fnic);
fnic_clear_intr_mode(fnic);
vnic_dev_close(fnic->vdev);
vnic_dev_unregister(fnic->vdev);
@@ -836,7 +848,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- scsi_host_put(fnic->lport->host);
+ scsi_host_put(lp->host);
}
static struct pci_driver fnic_driver = {
diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c
index 7ba61ec715d..50488f8e169 100644
--- a/drivers/scsi/fnic/fnic_res.c
+++ b/drivers/scsi/fnic/fnic_res.c
@@ -144,10 +144,9 @@ int fnic_get_vnic_config(struct fnic *fnic)
c->intr_timer_type = c->intr_timer_type;
shost_printk(KERN_INFO, fnic->lport->host,
- "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
+ "vNIC MAC addr %pM "
"wq/wq_copy/rq %d/%d/%d\n",
- fnic->mac_addr[0], fnic->mac_addr[1], fnic->mac_addr[2],
- fnic->mac_addr[3], fnic->mac_addr[4], fnic->mac_addr[5],
+ fnic->ctlr.ctl_src_addr,
c->wq_enet_desc_count, c->wq_copy_desc_count,
c->rq_desc_count);
shost_printk(KERN_INFO, fnic->lport->host,
diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h
index b6f31026253..ef8aaf2156d 100644
--- a/drivers/scsi/fnic/fnic_res.h
+++ b/drivers/scsi/fnic/fnic_res.h
@@ -51,6 +51,31 @@ static inline void fnic_queue_wq_desc(struct vnic_wq *wq,
vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop);
}
+static inline void fnic_queue_wq_eth_desc(struct vnic_wq *wq,
+ void *os_buf, dma_addr_t dma_addr,
+ unsigned int len,
+ int vlan_tag_insert,
+ unsigned int vlan_tag,
+ int cq_entry)
+{
+ struct wq_enet_desc *desc = vnic_wq_next_desc(wq);
+
+ wq_enet_desc_enc(desc,
+ (u64)dma_addr | VNIC_PADDR_TARGET,
+ (u16)len,
+ 0, /* mss_or_csum_offset */
+ 0, /* fc_eof */
+ 0, /* offload_mode */
+ 1, /* eop */
+ (u8)cq_entry,
+ 0, /* fcoe_encap */
+ (u8)vlan_tag_insert,
+ (u16)vlan_tag,
+ 0 /* loopback */);
+
+ vnic_wq_post(wq, os_buf, dma_addr, len, 1, 1);
+}
+
static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
u32 req_id,
u32 lunmap_id, u8 spl_flags,
@@ -58,6 +83,7 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
u64 sgl_addr, u64 sns_addr,
u8 crn, u8 pri_ta,
u8 flags, u8 *scsi_cdb,
+ u8 cdb_len,
u32 data_len, u8 *lun,
u32 d_id, u16 mss,
u32 ratov, u32 edtov)
@@ -82,7 +108,8 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
desc->u.icmnd_16.pri_ta = pri_ta; /* SCSI Pri & Task attribute */
desc->u.icmnd_16._resvd1 = 0; /* reserved: should be 0 */
desc->u.icmnd_16.flags = flags; /* command flags */
- memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, CDB_16); /* SCSI CDB */
+ memset(desc->u.icmnd_16.scsi_cdb, 0, CDB_16);
+ memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, cdb_len); /* SCSI CDB */
desc->u.icmnd_16.data_len = data_len; /* length of data expected */
memcpy(desc->u.icmnd_16.lun, lun, LUN_ADDRESS); /* LUN address */
desc->u.icmnd_16._resvd2 = 0; /* reserved */
@@ -132,12 +159,37 @@ static inline void fnic_queue_wq_copy_desc_flogi_reg(struct vnic_wq_copy *wq,
desc->hdr.tag.u.req_id = req_id; /* id for this request */
desc->u.flogi_reg.format = format;
+ desc->u.flogi_reg._resvd = 0;
hton24(desc->u.flogi_reg.s_id, s_id);
memcpy(desc->u.flogi_reg.gateway_mac, gw_mac, ETH_ALEN);
vnic_wq_copy_post(wq);
}
+static inline void fnic_queue_wq_copy_desc_fip_reg(struct vnic_wq_copy *wq,
+ u32 req_id, u32 s_id,
+ u8 *fcf_mac, u8 *ha_mac,
+ u32 r_a_tov, u32 e_d_tov)
+{
+ struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+ desc->hdr.type = FCPIO_FLOGI_FIP_REG; /* enum fcpio_type */
+ desc->hdr.status = 0; /* header status entry */
+ desc->hdr._resvd = 0; /* reserved */
+ desc->hdr.tag.u.req_id = req_id; /* id for this request */
+
+ desc->u.flogi_fip_reg._resvd0 = 0;
+ hton24(desc->u.flogi_fip_reg.s_id, s_id);
+ memcpy(desc->u.flogi_fip_reg.fcf_mac, fcf_mac, ETH_ALEN);
+ desc->u.flogi_fip_reg._resvd1 = 0;
+ desc->u.flogi_fip_reg.r_a_tov = r_a_tov;
+ desc->u.flogi_fip_reg.e_d_tov = e_d_tov;
+ memcpy(desc->u.flogi_fip_reg.ha_mac, ha_mac, ETH_ALEN);
+ desc->u.flogi_fip_reg._resvd2 = 0;
+
+ vnic_wq_copy_post(wq);
+}
+
static inline void fnic_queue_wq_copy_desc_fw_reset(struct vnic_wq_copy *wq,
u32 req_id)
{
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index bfc996971b8..65a39b0f6dc 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -174,6 +174,9 @@ int fnic_fw_reset_handler(struct fnic *fnic)
int ret = 0;
unsigned long flags;
+ skb_queue_purge(&fnic->frame_queue);
+ skb_queue_purge(&fnic->tx_queue);
+
spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
@@ -200,9 +203,11 @@ int fnic_fw_reset_handler(struct fnic *fnic)
* fnic_flogi_reg_handler
* Routine to send flogi register msg to fw
*/
-int fnic_flogi_reg_handler(struct fnic *fnic)
+int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id)
{
struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+ enum fcpio_flogi_reg_format_type format;
+ struct fc_lport *lp = fnic->lport;
u8 gw_mac[ETH_ALEN];
int ret = 0;
unsigned long flags;
@@ -217,23 +222,32 @@ int fnic_flogi_reg_handler(struct fnic *fnic)
goto flogi_reg_ioreq_end;
}
- if (fnic->fcoui_mode)
+ if (fnic->ctlr.map_dest) {
memset(gw_mac, 0xff, ETH_ALEN);
- else
- memcpy(gw_mac, fnic->dest_addr, ETH_ALEN);
+ format = FCPIO_FLOGI_REG_DEF_DEST;
+ } else {
+ memcpy(gw_mac, fnic->ctlr.dest_addr, ETH_ALEN);
+ format = FCPIO_FLOGI_REG_GW_DEST;
+ }
- fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
- FCPIO_FLOGI_REG_GW_DEST,
- fnic->s_id,
- gw_mac);
+ if ((fnic->config.flags & VFCF_FIP_CAPABLE) && !fnic->ctlr.map_dest) {
+ fnic_queue_wq_copy_desc_fip_reg(wq, SCSI_NO_TAG,
+ fc_id, gw_mac,
+ fnic->data_src_addr,
+ lp->r_a_tov, lp->e_d_tov);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "FLOGI FIP reg issued fcid %x src %pM dest %pM\n",
+ fc_id, fnic->data_src_addr, gw_mac);
+ } else {
+ fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
+ format, fc_id, gw_mac);
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "FLOGI reg issued fcid %x map %d dest %pM\n",
+ fc_id, fnic->ctlr.map_dest, gw_mac);
+ }
flogi_reg_ioreq_end:
spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
-
- if (!ret)
- FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
- "flog reg issued\n");
-
return ret;
}
@@ -319,7 +333,8 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
0, /* scsi cmd ref, always 0 */
pri_tag, /* scsi pri and tag */
flags, /* command flags */
- sc->cmnd, scsi_bufflen(sc),
+ sc->cmnd, sc->cmd_len,
+ scsi_bufflen(sc),
fc_lun.scsi_lun, io_req->port_id,
rport->maxframe_size, rp->r_a_tov,
rp->e_d_tov);
@@ -452,7 +467,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
u8 hdr_status;
struct fcpio_tag tag;
int ret = 0;
- struct fc_frame *flogi;
unsigned long flags;
fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
@@ -462,9 +476,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
spin_lock_irqsave(&fnic->fnic_lock, flags);
- flogi = fnic->flogi;
- fnic->flogi = NULL;
-
/* fnic should be in FC_TRANS_ETH_MODE */
if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
/* Check status of reset completion */
@@ -505,17 +516,14 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
* free the flogi frame. Else, send it out
*/
if (fnic->remove_wait || ret) {
- fnic->flogi_oxid = FC_XID_UNKNOWN;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (flogi)
- dev_kfree_skb_irq(fp_skb(flogi));
+ skb_queue_purge(&fnic->tx_queue);
goto reset_cmpl_handler_end;
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (flogi)
- ret = fnic_send_frame(fnic, flogi);
+ fnic_flush_tx(fnic);
reset_cmpl_handler_end:
return ret;
@@ -532,18 +540,13 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
u8 hdr_status;
struct fcpio_tag tag;
int ret = 0;
- struct fc_frame *flogi_resp = NULL;
unsigned long flags;
- struct sk_buff *skb;
fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
/* Update fnic state based on status of flogi reg completion */
spin_lock_irqsave(&fnic->fnic_lock, flags);
- flogi_resp = fnic->flogi_resp;
- fnic->flogi_resp = NULL;
-
if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) {
/* Check flogi registration completion status */
@@ -567,25 +570,17 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
ret = -1;
}
- /* Successful flogi reg cmpl, pass frame to LibFC */
- if (!ret && flogi_resp) {
+ if (!ret) {
if (fnic->stop_rx_link_events) {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
goto reg_cmpl_handler_end;
}
- skb = (struct sk_buff *)flogi_resp;
- /* Use fr_flags to indicate whether flogi resp or not */
- fr_flags(flogi_resp) = 1;
- fr_dev(flogi_resp) = fnic->lport;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- skb_queue_tail(&fnic->frame_queue, skb);
+ fnic_flush_tx(fnic);
queue_work(fnic_event_queue, &fnic->frame_work);
-
} else {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- if (flogi_resp)
- dev_kfree_skb_irq(fp_skb(flogi_resp));
}
reg_cmpl_handler_end:
@@ -907,6 +902,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
break;
case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
+ case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */
ret = fnic_fcpio_flogi_reg_cmpl_handler(fnic, desc);
break;
@@ -1224,22 +1220,6 @@ void fnic_terminate_rport_io(struct fc_rport *rport)
}
-static void fnic_block_error_handler(struct scsi_cmnd *sc)
-{
- struct Scsi_Host *shost = sc->device->host;
- struct fc_rport *rport = starget_to_rport(scsi_target(sc->device));
- unsigned long flags;
-
- spin_lock_irqsave(shost->host_lock, flags);
- while (rport->port_state == FC_PORTSTATE_BLOCKED) {
- spin_unlock_irqrestore(shost->host_lock, flags);
- msleep(1000);
- spin_lock_irqsave(shost->host_lock, flags);
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
-
-}
-
/*
* This function is exported to SCSI for sending abort cmnds.
* A SCSI IO is represented by a io_req in the driver.
@@ -1259,7 +1239,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
DECLARE_COMPLETION_ONSTACK(tm_done);
/* Wait for rport to unblock */
- fnic_block_error_handler(sc);
+ fc_block_scsi_eh(sc);
/* Get local-port, check ready and link up */
lp = shost_priv(sc->device->host);
@@ -1541,7 +1521,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
DECLARE_COMPLETION_ONSTACK(tm_done);
/* Wait for rport to unblock */
- fnic_block_error_handler(sc);
+ fc_block_scsi_eh(sc);
/* Get local-port, check ready and link up */
lp = shost_priv(sc->device->host);
@@ -1762,7 +1742,7 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
fnic->remove_wait = &remove_wait;
old_state = fnic->state;
fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+ fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
err = fnic_fw_reset_handler(fnic);
@@ -1802,7 +1782,7 @@ void fnic_scsi_cleanup(struct fc_lport *lp)
spin_lock_irqsave(&fnic->fnic_lock, flags);
old_state = fnic->state;
fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
- vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+ fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
if (fnic_fw_reset_handler(fnic)) {
diff --git a/drivers/scsi/fnic/vnic_scsi.h b/drivers/scsi/fnic/vnic_scsi.h
index 46baa525400..fbb55364e27 100644
--- a/drivers/scsi/fnic/vnic_scsi.h
+++ b/drivers/scsi/fnic/vnic_scsi.h
@@ -95,5 +95,6 @@ struct vnic_fc_config {
#define VFCF_FCP_SEQ_LVL_ERR 0x1 /* Enable FCP-2 Error Recovery */
#define VFCF_PERBI 0x2 /* persistent binding info available */
+#define VFCF_FIP_CAPABLE 0x4 /* firmware can handle FIP */
#endif /* _VNIC_SCSI_H_ */
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index c968cc31cd8..554626e1806 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -180,14 +180,20 @@ void scsi_remove_host(struct Scsi_Host *shost)
EXPORT_SYMBOL(scsi_remove_host);
/**
- * scsi_add_host - add a scsi host
+ * scsi_add_host_with_dma - add a scsi host with dma device
* @shost: scsi host pointer to add
* @dev: a struct device of type scsi class
+ * @dma_dev: dma device for the host
+ *
+ * Note: You rarely need to worry about this unless you're in a
+ * virtualised host environments, so use the simpler scsi_add_host()
+ * function instead.
*
* Return value:
* 0 on success / != 0 for error
**/
-int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
+int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
+ struct device *dma_dev)
{
struct scsi_host_template *sht = shost->hostt;
int error = -EINVAL;
@@ -207,6 +213,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
if (!shost->shost_gendev.parent)
shost->shost_gendev.parent = dev ? dev : &platform_bus;
+ shost->dma_dev = dma_dev;
error = device_add(&shost->shost_gendev);
if (error)
@@ -262,7 +269,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
fail:
return error;
}
-EXPORT_SYMBOL(scsi_add_host);
+EXPORT_SYMBOL(scsi_add_host_with_dma);
static void scsi_host_dev_release(struct device *dev)
{
diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c
index a0e7e711ff9..4f0556571f8 100644
--- a/drivers/scsi/hptiop.c
+++ b/drivers/scsi/hptiop.c
@@ -834,7 +834,7 @@ static int hptiop_reset_hba(struct hptiop_hba *hba)
atomic_read(&hba->resetting) == 0, 60 * HZ);
if (atomic_read(&hba->resetting)) {
- /* IOP is in unkown state, abort reset */
+ /* IOP is in unknown state, abort reset */
printk(KERN_ERR "scsi%d: reset failed\n", hba->host->host_no);
return -1;
}
@@ -861,10 +861,13 @@ static int hptiop_reset(struct scsi_cmnd *scp)
}
static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth)
+ int queue_depth, int reason)
{
struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata;
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (queue_depth > hba->max_requests)
queue_depth = hba->max_requests;
scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index bb2c696c006..87b536a97cb 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -39,6 +39,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
#include "ibmvfc.h"
static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
@@ -558,12 +559,11 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
/**
* ibmvfc_init_host - Start host initialization
* @vhost: ibmvfc host struct
- * @relogin: is this a re-login?
*
* Return value:
* nothing
**/
-static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
+static void ibmvfc_init_host(struct ibmvfc_host *vhost)
{
struct ibmvfc_target *tgt;
@@ -577,10 +577,8 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
}
if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
- if (!relogin) {
- memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
- vhost->async_crq.cur = 0;
- }
+ memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
+ vhost->async_crq.cur = 0;
list_for_each_entry(tgt, &vhost->targets, queue)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
@@ -1678,6 +1676,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
}
/**
+ * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands
+ * @evt: struct ibmvfc_event
+ *
+ **/
+static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+
+ ibmvfc_free_event(evt);
+ vhost->aborting_passthru = 0;
+ dev_info(vhost->dev, "Passthru command cancelled\n");
+}
+
+/**
+ * ibmvfc_bsg_timeout - Handle a BSG timeout
+ * @job: struct fc_bsg_job that timed out
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_timeout(struct fc_bsg_job *job)
+{
+ struct ibmvfc_host *vhost = shost_priv(job->shost);
+ unsigned long port_id = (unsigned long)job->dd_data;
+ struct ibmvfc_event *evt;
+ struct ibmvfc_tmf *tmf;
+ unsigned long flags;
+ int rc;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) {
+ __ibmvfc_reset_host(vhost);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return 0;
+ }
+
+ vhost->aborting_passthru = 1;
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT);
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->common.version = 1;
+ tmf->common.opcode = IBMVFC_TMF_MAD;
+ tmf->common.length = sizeof(*tmf);
+ tmf->scsi_id = port_id;
+ tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+ tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY;
+ rc = ibmvfc_send_event(evt, vhost, default_timeout);
+
+ if (rc != 0) {
+ vhost->aborting_passthru = 0;
+ dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc);
+ rc = -EIO;
+ } else
+ dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n",
+ port_id);
+
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command
+ * @vhost: struct ibmvfc_host to send command
+ * @port_id: port ID to send command
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
+{
+ struct ibmvfc_port_login *plogi;
+ struct ibmvfc_target *tgt;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp_iu;
+ unsigned long flags;
+ int rc = 0, issue_login = 1;
+
+ ENTER;
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (tgt->scsi_id == port_id) {
+ issue_login = 0;
+ break;
+ }
+ }
+
+ if (!issue_login)
+ goto unlock_out;
+ if (unlikely((rc = ibmvfc_host_chkready(vhost))))
+ goto unlock_out;
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+ plogi = &evt->iu.plogi;
+ memset(plogi, 0, sizeof(*plogi));
+ plogi->common.version = 1;
+ plogi->common.opcode = IBMVFC_PORT_LOGIN;
+ plogi->common.length = sizeof(*plogi);
+ plogi->scsi_id = port_id;
+ evt->sync_iu = &rsp_iu;
+ init_completion(&evt->comp);
+
+ rc = ibmvfc_send_event(evt, vhost, default_timeout);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (rc)
+ return -EIO;
+
+ wait_for_completion(&evt->comp);
+
+ if (rsp_iu.plogi.common.status)
+ rc = -EIO;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+unlock_out:
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ LEAVE;
+ return rc;
+}
+
+/**
+ * ibmvfc_bsg_request - Handle a BSG request
+ * @job: struct fc_bsg_job to be executed
+ *
+ * Returns:
+ * 0 on success / other on failure
+ **/
+static int ibmvfc_bsg_request(struct fc_bsg_job *job)
+{
+ struct ibmvfc_host *vhost = shost_priv(job->shost);
+ struct fc_rport *rport = job->rport;
+ struct ibmvfc_passthru_mad *mad;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp_iu;
+ unsigned long flags, port_id = -1;
+ unsigned int code = job->request->msgcode;
+ int rc = 0, req_seg, rsp_seg, issue_login = 0;
+ u32 fc_flags, rsp_len;
+
+ ENTER;
+ job->reply->reply_payload_rcv_len = 0;
+ if (rport)
+ port_id = rport->port_id;
+
+ switch (code) {
+ case FC_BSG_HST_ELS_NOLOGIN:
+ port_id = (job->request->rqst_data.h_els.port_id[0] << 16) |
+ (job->request->rqst_data.h_els.port_id[1] << 8) |
+ job->request->rqst_data.h_els.port_id[2];
+ case FC_BSG_RPT_ELS:
+ fc_flags = IBMVFC_FC_ELS;
+ break;
+ case FC_BSG_HST_CT:
+ issue_login = 1;
+ port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) |
+ (job->request->rqst_data.h_ct.port_id[1] << 8) |
+ job->request->rqst_data.h_ct.port_id[2];
+ case FC_BSG_RPT_CT:
+ fc_flags = IBMVFC_FC_CT_IU;
+ break;
+ default:
+ return -ENOTSUPP;
+ };
+
+ if (port_id == -1)
+ return -EINVAL;
+ if (!mutex_trylock(&vhost->passthru_mutex))
+ return -EBUSY;
+
+ job->dd_data = (void *)port_id;
+ req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ if (!req_seg) {
+ mutex_unlock(&vhost->passthru_mutex);
+ return -ENOMEM;
+ }
+
+ rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+ if (!rsp_seg) {
+ dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ mutex_unlock(&vhost->passthru_mutex);
+ return -ENOMEM;
+ }
+
+ if (req_seg > 1 || rsp_seg > 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (issue_login)
+ rc = ibmvfc_bsg_plogi(vhost, port_id);
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+
+ if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) ||
+ unlikely((rc = ibmvfc_host_chkready(vhost)))) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ goto out;
+ }
+
+ evt = ibmvfc_get_event(vhost);
+ ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.passthru;
+
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = 1;
+ mad->common.opcode = IBMVFC_PASSTHRU;
+ mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
+
+ mad->cmd_ioba.va = (u64)evt->crq.ioba +
+ offsetof(struct ibmvfc_passthru_mad, iu);
+ mad->cmd_ioba.len = sizeof(mad->iu);
+
+ mad->iu.cmd_len = job->request_payload.payload_len;
+ mad->iu.rsp_len = job->reply_payload.payload_len;
+ mad->iu.flags = fc_flags;
+ mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+
+ mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list);
+ mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list);
+ mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list);
+ mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list);
+ mad->iu.scsi_id = port_id;
+ mad->iu.tag = (u64)evt;
+ rsp_len = mad->iu.rsp.len;
+
+ evt->sync_iu = &rsp_iu;
+ init_completion(&evt->comp);
+ rc = ibmvfc_send_event(evt, vhost, 0);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (rc) {
+ rc = -EIO;
+ goto out;
+ }
+
+ wait_for_completion(&evt->comp);
+
+ if (rsp_iu.passthru.common.status)
+ rc = -EIO;
+ else
+ job->reply->reply_payload_rcv_len = rsp_len;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ job->reply->result = rc;
+ job->job_done(job);
+ rc = 0;
+out:
+ dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(vhost->dev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ mutex_unlock(&vhost->passthru_mutex);
+ LEAVE;
+ return rc;
+}
+
+/**
* ibmvfc_reset_device - Reset the device with the specified reset type
* @sdev: scsi device to reset
* @type: reset type
@@ -1731,7 +1999,10 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
wait_for_completion(&evt->comp);
- if (rsp_iu.cmd.status) {
+ if (rsp_iu.cmd.status)
+ rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+
+ if (rsp_code) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = fc_rsp->data.info.rsp_code;
@@ -1820,7 +2091,10 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
wait_for_completion(&evt->comp);
- if (rsp_iu.cmd.status) {
+ if (rsp_iu.cmd.status)
+ rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+
+ if (rsp_code) {
if (fc_rsp->flags & FCP_RSP_LEN_VALID)
rsp_code = fc_rsp->data.info.rsp_code;
@@ -2061,12 +2335,24 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
}
/**
- * ibmvfc_dev_cancel_all - Device iterated cancel all function
+ * ibmvfc_dev_cancel_all_abts - Device iterated cancel all function
* @sdev: scsi device struct
* @data: return code
*
**/
-static void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data)
+static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data)
+{
+ unsigned long *rc = data;
+ *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+}
+
+/**
+ * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
+ * @sdev: scsi device struct
+ * @data: return code
+ *
+ **/
+static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
{
unsigned long *rc = data;
*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
@@ -2102,7 +2388,7 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
ENTER;
ibmvfc_wait_while_resetting(vhost);
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+ starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
if (!cancel_rc && !reset_rc)
@@ -2144,7 +2430,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
int rc = FAILED;
ENTER;
- starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+ starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts);
starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
if (!cancel_rc && !abort_rc)
@@ -2297,13 +2583,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
/* Send back a response */
rc = ibmvfc_send_crq_init_complete(vhost);
if (rc == 0)
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
else
dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
break;
case IBMVFC_CRQ_INIT_COMPLETE:
dev_info(vhost->dev, "Partner initialization complete\n");
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
break;
default:
dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
@@ -2478,12 +2764,17 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev)
* ibmvfc_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
qdepth = IBMVFC_MAX_CMDS_PER_LUN;
@@ -3725,7 +4016,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
if (list_empty(&vhost->sent) &&
vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
- ibmvfc_init_host(vhost, 0);
+ ibmvfc_init_host(vhost);
return;
}
break;
@@ -3903,6 +4194,8 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
rport->supported_classes |= FC_COS_CLASS2;
if (tgt->service_parms.class3_parms[0] & 0x80000000)
rport->supported_classes |= FC_COS_CLASS3;
+ if (rport->rqst_q)
+ blk_queue_max_hw_segments(rport->rqst_q, 1);
} else
tgt_dbg(tgt, "rport add failed\n");
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4342,6 +4635,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
init_waitqueue_head(&vhost->work_wait_q);
init_waitqueue_head(&vhost->init_wait_q);
INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread);
+ mutex_init(&vhost->passthru_mutex);
if ((rc = ibmvfc_alloc_mem(vhost)))
goto free_scsi_host;
@@ -4374,6 +4668,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
goto remove_shost;
}
+ if (shost_to_fc_host(shost)->rqst_q)
+ blk_queue_max_hw_segments(shost_to_fc_host(shost)->rqst_q, 1);
dev_set_drvdata(dev, vhost);
spin_lock(&ibmvfc_driver_lock);
list_add_tail(&vhost->queue, &ibmvfc_head);
@@ -4414,7 +4710,11 @@ static int ibmvfc_remove(struct vio_dev *vdev)
ENTER;
ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
ibmvfc_wait_while_resetting(vhost);
ibmvfc_release_crq_queue(vhost);
kthread_stop(vhost->work_thread);
@@ -4498,6 +4798,9 @@ static struct fc_function_template ibmvfc_transport_functions = {
.get_starget_port_id = ibmvfc_get_starget_port_id,
.show_starget_port_id = 1,
+
+ .bsg_request = ibmvfc_bsg_request,
+ .bsg_timeout = ibmvfc_bsg_timeout,
};
/**
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 007fa1c9ef1..d25106a958d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -29,8 +29,8 @@
#include "viosrp.h"
#define IBMVFC_NAME "ibmvfc"
-#define IBMVFC_DRIVER_VERSION "1.0.6"
-#define IBMVFC_DRIVER_DATE "(May 28, 2009)"
+#define IBMVFC_DRIVER_VERSION "1.0.7"
+#define IBMVFC_DRIVER_DATE "(October 16, 2009)"
#define IBMVFC_DEFAULT_TIMEOUT 60
#define IBMVFC_ADISC_CANCEL_TIMEOUT 45
@@ -58,9 +58,10 @@
* 1 for ERP
* 1 for initialization
* 1 for NPIV Logout
+ * 2 for BSG passthru
* 2 for each discovery thread
*/
-#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + (disc_threads * 2))
+#define IBMVFC_NUM_INTERNAL_REQ (1 + 1 + 1 + 2 + (disc_threads * 2))
#define IBMVFC_MAD_SUCCESS 0x00
#define IBMVFC_MAD_NOT_SUPPORTED 0xF1
@@ -466,7 +467,10 @@ struct ibmvfc_passthru_iu {
u16 error;
u32 flags;
#define IBMVFC_FC_ELS 0x01
+#define IBMVFC_FC_CT_IU 0x02
u32 cancel_key;
+#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000
+#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001
u32 reserved;
struct srp_direct_buf cmd;
struct srp_direct_buf rsp;
@@ -693,6 +697,7 @@ struct ibmvfc_host {
int disc_buf_sz;
int log_level;
struct ibmvfc_discover_targets_buf *disc_buf;
+ struct mutex passthru_mutex;
int task_set;
int init_retries;
int discovery_threads;
@@ -702,6 +707,7 @@ struct ibmvfc_host {
int delay_init;
int scan_complete;
int logged_in;
+ int aborting_passthru;
int events_to_log;
#define IBMVFC_AE_LINKUP 0x0001
#define IBMVFC_AE_LINKDOWN 0x0002
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index d9b0e9d3198..e475b7957c2 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -1637,12 +1637,17 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev)
* ibmvscsi_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN)
qdepth = IBMVSCSI_MAX_CMDS_PER_LUN;
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 76d294fc784..206c2fa8c1b 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -3367,16 +3367,21 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; };
* ipr_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
**/
-static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
struct ipr_resource_entry *res;
unsigned long lock_flags = 0;
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
res = (struct ipr_resource_entry *)sdev->hostdata;
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index edc49ca49ce..517da3fd89d 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -903,7 +903,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_LU_RESET_TMO |
+ ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
ISCSI_PING_TMO | ISCSI_RECV_TMO |
ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile
index 55f982de3a9..4bb23ac86a5 100644
--- a/drivers/scsi/libfc/Makefile
+++ b/drivers/scsi/libfc/Makefile
@@ -3,10 +3,12 @@
obj-$(CONFIG_LIBFC) += libfc.o
libfc-objs := \
+ fc_libfc.o \
fc_disc.o \
fc_exch.o \
fc_elsct.o \
fc_frame.o \
fc_lport.o \
fc_rport.o \
- fc_fcp.o
+ fc_fcp.o \
+ fc_npiv.o
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index c48799e9dd8..9b0a5192a96 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -40,6 +40,8 @@
#include <scsi/libfc.h>
+#include "fc_libfc.h"
+
#define FC_DISC_RETRY_LIMIT 3 /* max retries */
#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
@@ -51,8 +53,8 @@ static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
static void fc_disc_restart(struct fc_disc *);
/**
- * fc_disc_stop_rports() - delete all the remote ports associated with the lport
- * @disc: The discovery job to stop rports on
+ * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
+ * @disc: The discovery job to stop remote ports on
*
* Locking Note: This function expects that the lport mutex is locked before
* calling it.
@@ -72,9 +74,9 @@ void fc_disc_stop_rports(struct fc_disc *disc)
/**
* fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
- * @sp: Current sequence of the RSCN exchange
- * @fp: RSCN Frame
- * @lport: Fibre Channel host port instance
+ * @sp: The sequence of the RSCN exchange
+ * @fp: The RSCN frame
+ * @lport: The local port that the request will be sent on
*
* Locking Note: This function expects that the disc_mutex is locked
* before it is called.
@@ -183,9 +185,9 @@ reject:
/**
* fc_disc_recv_req() - Handle incoming requests
- * @sp: Current sequence of the request exchange
- * @fp: The frame
- * @lport: The FC local port
+ * @sp: The sequence of the request exchange
+ * @fp: The request frame
+ * @lport: The local port receiving the request
*
* Locking Note: This function is called from the EM and will lock
* the disc_mutex before calling the handler for the
@@ -213,7 +215,7 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
/**
* fc_disc_restart() - Restart discovery
- * @lport: FC discovery context
+ * @disc: The discovery object to be restarted
*
* Locking Note: This function expects that the disc mutex
* is already locked.
@@ -240,9 +242,9 @@ static void fc_disc_restart(struct fc_disc *disc)
}
/**
- * fc_disc_start() - Fibre Channel Target discovery
- * @lport: FC local port
- * @disc_callback: function to be called when discovery is complete
+ * fc_disc_start() - Start discovery on a local port
+ * @lport: The local port to have discovery started on
+ * @disc_callback: Callback function to be called when discovery is complete
*/
static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
enum fc_disc_event),
@@ -263,8 +265,8 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
/**
* fc_disc_done() - Discovery has been completed
- * @disc: FC discovery context
- * @event: discovery completion status
+ * @disc: The discovery context
+ * @event: The discovery completion status
*
* Locking Note: This function expects that the disc mutex is locked before
* it is called. The discovery callback is then made with the lock released,
@@ -284,8 +286,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
}
/*
- * Go through all remote ports. If they were found in the latest
- * discovery, reverify or log them in. Otherwise, log them out.
+ * Go through all remote ports. If they were found in the latest
+ * discovery, reverify or log them in. Otherwise, log them out.
* Skip ports which were never discovered. These are the dNS port
* and ports which were created by PLOGI.
*/
@@ -305,8 +307,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
/**
* fc_disc_error() - Handle error on dNS request
- * @disc: FC discovery context
- * @fp: The frame pointer
+ * @disc: The discovery context
+ * @fp: The error code encoded as a frame pointer
*/
static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
{
@@ -342,7 +344,7 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
/**
* fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
- * @lport: FC discovery context
+ * @lport: The discovery context
*
* Locking Note: This function expects that the disc_mutex is locked
* before it is called.
@@ -368,17 +370,17 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc)
if (lport->tt.elsct_send(lport, 0, fp,
FC_NS_GPN_FT,
fc_disc_gpn_ft_resp,
- disc, lport->e_d_tov))
+ disc, 3 * lport->r_a_tov))
return;
err:
- fc_disc_error(disc, fp);
+ fc_disc_error(disc, NULL);
}
/**
* fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
- * @lport: Fibre Channel host port instance
- * @buf: GPN_FT response buffer
- * @len: size of response buffer
+ * @lport: The local port the GPN_FT was received on
+ * @buf: The GPN_FT response buffer
+ * @len: The size of response buffer
*
* Goes through the list of IDs and names resulting from a request.
*/
@@ -477,10 +479,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
}
/**
- * fc_disc_timeout() - Retry handler for the disc component
- * @work: Structure holding disc obj that needs retry discovery
- *
- * Handle retry of memory allocation for remote ports.
+ * fc_disc_timeout() - Handler for discovery timeouts
+ * @work: Structure holding discovery context that needs to retry discovery
*/
static void fc_disc_timeout(struct work_struct *work)
{
@@ -494,9 +494,9 @@ static void fc_disc_timeout(struct work_struct *work)
/**
* fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
- * @sp: Current sequence of GPN_FT exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel host port instance
+ * @sp: The sequence that the GPN_FT response was received on
+ * @fp: The GPN_FT response frame
+ * @lp_arg: The discovery context
*
* Locking Note: This function is called without disc mutex held, and
* should do all its processing with the mutex held
@@ -567,9 +567,9 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
/**
* fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
- * @sp: exchange sequence
- * @fp: response frame
- * @rdata_arg: remote port private data
+ * @sp: The sequence the GPN_ID is on
+ * @fp: The response frame
+ * @rdata_arg: The remote port that sent the GPN_ID response
*
* Locking Note: This function is called without disc mutex held.
*/
@@ -637,7 +637,7 @@ out:
/**
* fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
- * @lport: local port
+ * @lport: The local port to initiate discovery on
* @rdata: remote port private data
*
* Locking Note: This function expects that the disc_mutex is locked
@@ -654,7 +654,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
if (!fp)
return -ENOMEM;
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
- fc_disc_gpn_id_resp, rdata, lport->e_d_tov))
+ fc_disc_gpn_id_resp, rdata,
+ 3 * lport->r_a_tov))
return -ENOMEM;
kref_get(&rdata->kref);
return 0;
@@ -662,8 +663,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
/**
* fc_disc_single() - Discover the directory information for a single target
- * @lport: local port
- * @dp: The port to rediscover
+ * @lport: The local port the remote port is associated with
+ * @dp: The port to rediscover
*
* Locking Note: This function expects that the disc_mutex is locked
* before it is called.
@@ -681,7 +682,7 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
/**
* fc_disc_stop() - Stop discovery for a given lport
- * @lport: The lport that discovery should stop for
+ * @lport: The local port that discovery should stop on
*/
void fc_disc_stop(struct fc_lport *lport)
{
@@ -695,7 +696,7 @@ void fc_disc_stop(struct fc_lport *lport)
/**
* fc_disc_stop_final() - Stop discovery for a given lport
- * @lport: The lport that discovery should stop for
+ * @lport: The lport that discovery should stop on
*
* This function will block until discovery has been
* completely stopped and all rports have been deleted.
@@ -707,8 +708,8 @@ void fc_disc_stop_final(struct fc_lport *lport)
}
/**
- * fc_disc_init() - Initialize the discovery block
- * @lport: FC local port
+ * fc_disc_init() - Initialize the discovery layer for a local port
+ * @lport: The local port that needs the discovery layer to be initialized
*/
int fc_disc_init(struct fc_lport *lport)
{
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index 5cfa68732e9..53748724f2c 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -28,17 +28,22 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
-/*
- * fc_elsct_send - sends ELS/CT frame
+/**
+ * fc_elsct_send() - Send an ELS or CT frame
+ * @lport: The local port to send the frame on
+ * @did: The destination ID for the frame
+ * @fp: The frame to be sent
+ * @op: The operational code
+ * @resp: The callback routine when the response is received
+ * @arg: The argument to pass to the response callback routine
+ * @timer_msec: The timeout period for the frame (in msecs)
*/
-static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
- u32 did,
- struct fc_frame *fp,
- unsigned int op,
- void (*resp)(struct fc_seq *,
- struct fc_frame *fp,
- void *arg),
- void *arg, u32 timer_msec)
+struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
+ struct fc_frame *fp, unsigned int op,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *,
+ void *),
+ void *arg, u32 timer_msec)
{
enum fc_rctl r_ctl;
enum fc_fh_type fh_type;
@@ -53,15 +58,22 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
did = FC_FID_DIR_SERV;
}
- if (rc)
+ if (rc) {
+ fc_frame_free(fp);
return NULL;
+ }
fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
}
+EXPORT_SYMBOL(fc_elsct_send);
+/**
+ * fc_elsct_init() - Initialize the ELS/CT layer
+ * @lport: The local port to initialize the ELS/CT layer for
+ */
int fc_elsct_init(struct fc_lport *lport)
{
if (!lport->tt.elsct_send)
@@ -72,12 +84,15 @@ int fc_elsct_init(struct fc_lport *lport)
EXPORT_SYMBOL(fc_elsct_init);
/**
- * fc_els_resp_type() - return string describing ELS response for debug.
- * @fp: frame pointer with possible error code.
+ * fc_els_resp_type() - Return a string describing the ELS response
+ * @fp: The frame pointer or possible error code
*/
const char *fc_els_resp_type(struct fc_frame *fp)
{
const char *msg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+
if (IS_ERR(fp)) {
switch (-PTR_ERR(fp)) {
case FC_NO_ERR:
@@ -94,15 +109,41 @@ const char *fc_els_resp_type(struct fc_frame *fp)
break;
}
} else {
- switch (fc_frame_payload_op(fp)) {
- case ELS_LS_ACC:
- msg = "accept";
+ fh = fc_frame_header_get(fp);
+ switch (fh->fh_type) {
+ case FC_TYPE_ELS:
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_LS_ACC:
+ msg = "accept";
+ break;
+ case ELS_LS_RJT:
+ msg = "reject";
+ break;
+ default:
+ msg = "response unknown ELS";
+ break;
+ }
break;
- case ELS_LS_RJT:
- msg = "reject";
+ case FC_TYPE_CT:
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+ if (ct) {
+ switch (ntohs(ct->ct_cmd)) {
+ case FC_FS_ACC:
+ msg = "CT accept";
+ break;
+ case FC_FS_RJT:
+ msg = "CT reject";
+ break;
+ default:
+ msg = "response unknown CT";
+ break;
+ }
+ } else {
+ msg = "short CT response";
+ }
break;
default:
- msg = "response unknown ELS";
+ msg = "response not ELS or CT";
break;
}
}
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index c1c15748220..19d711cb938 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -32,10 +32,13 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+#include "fc_libfc.h"
+
u16 fc_cpu_mask; /* cpu mask for possible cpus */
EXPORT_SYMBOL(fc_cpu_mask);
static u16 fc_cpu_order; /* 2's power to represent total possible cpus */
-static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
+static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
+struct workqueue_struct *fc_exch_workqueue;
/*
* Structure and function definitions for managing Fibre Channel Exchanges
@@ -50,35 +53,46 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
* fc_seq holds the state for an individual sequence.
*/
-/*
- * Per cpu exchange pool
+/**
+ * struct fc_exch_pool - Per cpu exchange pool
+ * @next_index: Next possible free exchange index
+ * @total_exches: Total allocated exchanges
+ * @lock: Exch pool lock
+ * @ex_list: List of exchanges
*
* This structure manages per cpu exchanges in array of exchange pointers.
* This array is allocated followed by struct fc_exch_pool memory for
* assigned range of exchanges to per cpu pool.
*/
struct fc_exch_pool {
- u16 next_index; /* next possible free exchange index */
- u16 total_exches; /* total allocated exchanges */
- spinlock_t lock; /* exch pool lock */
- struct list_head ex_list; /* allocated exchanges list */
+ u16 next_index;
+ u16 total_exches;
+ spinlock_t lock;
+ struct list_head ex_list;
};
-/*
- * Exchange manager.
+/**
+ * struct fc_exch_mgr - The Exchange Manager (EM).
+ * @class: Default class for new sequences
+ * @kref: Reference counter
+ * @min_xid: Minimum exchange ID
+ * @max_xid: Maximum exchange ID
+ * @ep_pool: Reserved exchange pointers
+ * @pool_max_index: Max exch array index in exch pool
+ * @pool: Per cpu exch pool
+ * @stats: Statistics structure
*
* This structure is the center for creating exchanges and sequences.
* It manages the allocation of exchange IDs.
*/
struct fc_exch_mgr {
- enum fc_class class; /* default class for sequences */
- struct kref kref; /* exchange mgr reference count */
- u16 min_xid; /* min exchange ID */
- u16 max_xid; /* max exchange ID */
- struct list_head ex_list; /* allocated exchanges list */
- mempool_t *ep_pool; /* reserve ep's */
- u16 pool_max_index; /* max exch array index in exch pool */
- struct fc_exch_pool *pool; /* per cpu exch pool */
+ enum fc_class class;
+ struct kref kref;
+ u16 min_xid;
+ u16 max_xid;
+ mempool_t *ep_pool;
+ u16 pool_max_index;
+ struct fc_exch_pool *pool;
/*
* currently exchange mgr stats are updated but not used.
@@ -96,6 +110,18 @@ struct fc_exch_mgr {
};
#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+/**
+ * struct fc_exch_mgr_anchor - primary structure for list of EMs
+ * @ema_list: Exchange Manager Anchor list
+ * @mp: Exchange Manager associated with this anchor
+ * @match: Routine to determine if this anchor's EM should be used
+ *
+ * When walking the list of anchors the match routine will be called
+ * for each anchor to determine if that EM should be used. The last
+ * anchor in the list will always match to handle any exchanges not
+ * handled by other EMs. The non-default EMs would be added to the
+ * anchor list by HW that provides FCoE offloads.
+ */
struct fc_exch_mgr_anchor {
struct list_head ema_list;
struct fc_exch_mgr *mp;
@@ -108,7 +134,6 @@ static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
enum fc_els_rjt_explan);
static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *);
static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *);
-static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
/*
* Internal implementation notes.
@@ -196,6 +221,15 @@ static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT;
#define FC_TABLE_SIZE(x) (sizeof(x) / sizeof(x[0]))
+/**
+ * fc_exch_name_lookup() - Lookup name by opcode
+ * @op: Opcode to be looked up
+ * @table: Opcode/name table
+ * @max_index: Index not to be exceeded
+ *
+ * This routine is used to determine a human-readable string identifying
+ * a R_CTL opcode.
+ */
static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
unsigned int max_index)
{
@@ -208,25 +242,34 @@ static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
return name;
}
+/**
+ * fc_exch_rctl_name() - Wrapper routine for fc_exch_name_lookup()
+ * @op: The opcode to be looked up
+ */
static const char *fc_exch_rctl_name(unsigned int op)
{
return fc_exch_name_lookup(op, fc_exch_rctl_names,
FC_TABLE_SIZE(fc_exch_rctl_names));
}
-/*
- * Hold an exchange - keep it from being freed.
+/**
+ * fc_exch_hold() - Increment an exchange's reference count
+ * @ep: Echange to be held
*/
-static void fc_exch_hold(struct fc_exch *ep)
+static inline void fc_exch_hold(struct fc_exch *ep)
{
atomic_inc(&ep->ex_refcnt);
}
-/*
- * setup fc hdr by initializing few more FC header fields and sof/eof.
- * Initialized fields by this func:
- * - fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt
- * - sof and eof
+/**
+ * fc_exch_setup_hdr() - Initialize a FC header by initializing some fields
+ * and determine SOF and EOF.
+ * @ep: The exchange to that will use the header
+ * @fp: The frame whose header is to be modified
+ * @f_ctl: F_CTL bits that will be used for the frame header
+ *
+ * The fields initialized by this routine are: fh_ox_id, fh_rx_id,
+ * fh_seq_id, fh_seq_cnt and the SOF and EOF.
*/
static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
u32 f_ctl)
@@ -243,7 +286,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
if (fc_sof_needs_ack(ep->class))
fr_eof(fp) = FC_EOF_N;
/*
- * Form f_ctl.
+ * From F_CTL.
* The number of fill bytes to make the length a 4-byte
* multiple is the low order 2-bits of the f_ctl.
* The fill itself will have been cleared by the frame
@@ -273,10 +316,12 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
fh->fh_seq_cnt = htons(ep->seq.cnt);
}
-
-/*
- * Release a reference to an exchange.
- * If the refcnt goes to zero and the exchange is complete, it is freed.
+/**
+ * fc_exch_release() - Decrement an exchange's reference count
+ * @ep: Exchange to be released
+ *
+ * If the reference count reaches zero and the exchange is complete,
+ * it is freed.
*/
static void fc_exch_release(struct fc_exch *ep)
{
@@ -291,6 +336,10 @@ static void fc_exch_release(struct fc_exch *ep)
}
}
+/**
+ * fc_exch_done_locked() - Complete an exchange with the exchange lock held
+ * @ep: The exchange that is complete
+ */
static int fc_exch_done_locked(struct fc_exch *ep)
{
int rc = 1;
@@ -315,6 +364,15 @@ static int fc_exch_done_locked(struct fc_exch *ep)
return rc;
}
+/**
+ * fc_exch_ptr_get() - Return an exchange from an exchange pool
+ * @pool: Exchange Pool to get an exchange from
+ * @index: Index of the exchange within the pool
+ *
+ * Use the index to get an exchange from within an exchange pool. exches
+ * will point to an array of exchange pointers. The index will select
+ * the exchange within the array.
+ */
static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
u16 index)
{
@@ -322,12 +380,22 @@ static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
return exches[index];
}
+/**
+ * fc_exch_ptr_set() - Assign an exchange to a slot in an exchange pool
+ * @pool: The pool to assign the exchange to
+ * @index: The index in the pool where the exchange will be assigned
+ * @ep: The exchange to assign to the pool
+ */
static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
struct fc_exch *ep)
{
((struct fc_exch **)(pool + 1))[index] = ep;
}
+/**
+ * fc_exch_delete() - Delete an exchange
+ * @ep: The exchange to be deleted
+ */
static void fc_exch_delete(struct fc_exch *ep)
{
struct fc_exch_pool *pool;
@@ -343,8 +411,14 @@ static void fc_exch_delete(struct fc_exch *ep)
fc_exch_release(ep); /* drop hold for exch in mp */
}
-/*
- * Internal version of fc_exch_timer_set - used with lock held.
+/**
+ * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the
+ * the exchange lock held
+ * @ep: The exchange whose timer will start
+ * @timer_msec: The timeout period
+ *
+ * Used for upper level protocols to time out the exchange.
+ * The timer is cancelled when it fires or when the exchange completes.
*/
static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
unsigned int timer_msec)
@@ -354,17 +428,15 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
FC_EXCH_DBG(ep, "Exchange timer armed\n");
- if (schedule_delayed_work(&ep->timeout_work,
- msecs_to_jiffies(timer_msec)))
+ if (queue_delayed_work(fc_exch_workqueue, &ep->timeout_work,
+ msecs_to_jiffies(timer_msec)))
fc_exch_hold(ep); /* hold for timer */
}
-/*
- * Set timer for an exchange.
- * The time is a minimum delay in milliseconds until the timer fires.
- * Used for upper level protocols to time out the exchange.
- * The timer is cancelled when it fires or when the exchange completes.
- * Returns non-zero if a timer couldn't be allocated.
+/**
+ * fc_exch_timer_set() - Lock the exchange and set the timer
+ * @ep: The exchange whose timer will start
+ * @timer_msec: The timeout period
*/
static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
{
@@ -373,7 +445,115 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
spin_unlock_bh(&ep->ex_lock);
}
-int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
+/**
+ * fc_seq_send() - Send a frame using existing sequence/exchange pair
+ * @lport: The local port that the exchange will be sent on
+ * @sp: The sequence to be sent
+ * @fp: The frame to be sent on the exchange
+ */
+static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_exch *ep;
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ int error;
+ u32 f_ctl;
+
+ ep = fc_seq_exch(sp);
+ WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
+
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_exch_setup_hdr(ep, fp, f_ctl);
+
+ /*
+ * update sequence count if this frame is carrying
+ * multiple FC frames when sequence offload is enabled
+ * by LLD.
+ */
+ if (fr_max_payload(fp))
+ sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
+ fr_max_payload(fp));
+ else
+ sp->cnt++;
+
+ /*
+ * Send the frame.
+ */
+ error = lport->tt.frame_send(lport, fp);
+
+ /*
+ * Update the exchange and sequence flags,
+ * assuming all frames for the sequence have been sent.
+ * We can only be called to send once for each sequence.
+ */
+ spin_lock_bh(&ep->ex_lock);
+ ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */
+ if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ return error;
+}
+
+/**
+ * fc_seq_alloc() - Allocate a sequence for a given exchange
+ * @ep: The exchange to allocate a new sequence for
+ * @seq_id: The sequence ID to be used
+ *
+ * We don't support multiple originated sequences on the same exchange.
+ * By implication, any previously originated sequence on this exchange
+ * is complete, and we reallocate the same sequence.
+ */
+static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
+{
+ struct fc_seq *sp;
+
+ sp = &ep->seq;
+ sp->ssb_stat = 0;
+ sp->cnt = 0;
+ sp->id = seq_id;
+ return sp;
+}
+
+/**
+ * fc_seq_start_next_locked() - Allocate a new sequence on the same
+ * exchange as the supplied sequence
+ * @sp: The sequence/exchange to get a new sequence for
+ */
+static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ sp = fc_seq_alloc(ep, ep->seq_id++);
+ FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
+ ep->f_ctl, sp->id);
+ return sp;
+}
+
+/**
+ * fc_seq_start_next() - Lock the exchange and get a new sequence
+ * for a given sequence/exchange pair
+ * @sp: The sequence/exchange to get a new exchange for
+ */
+static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ sp = fc_seq_start_next_locked(sp);
+ spin_unlock_bh(&ep->ex_lock);
+
+ return sp;
+}
+
+/**
+ * fc_seq_exch_abort() - Abort an exchange and sequence
+ * @req_sp: The sequence to be aborted
+ * @timer_msec: The period of time to wait before aborting
+ *
+ * Generally called because of a timeout or an abort from the upper layer.
+ */
+static int fc_seq_exch_abort(const struct fc_seq *req_sp,
+ unsigned int timer_msec)
{
struct fc_seq *sp;
struct fc_exch *ep;
@@ -422,11 +602,10 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
error = -ENOBUFS;
return error;
}
-EXPORT_SYMBOL(fc_seq_exch_abort);
-/*
- * Exchange timeout - handle exchange timer expiration.
- * The timer will have been cancelled before this is called.
+/**
+ * fc_exch_timeout() - Handle exchange timer expiration
+ * @work: The work_struct identifying the exchange that timed out
*/
static void fc_exch_timeout(struct work_struct *work)
{
@@ -474,28 +653,10 @@ done:
fc_exch_release(ep);
}
-/*
- * Allocate a sequence.
- *
- * We don't support multiple originated sequences on the same exchange.
- * By implication, any previously originated sequence on this exchange
- * is complete, and we reallocate the same sequence.
- */
-static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
-{
- struct fc_seq *sp;
-
- sp = &ep->seq;
- sp->ssb_stat = 0;
- sp->cnt = 0;
- sp->id = seq_id;
- return sp;
-}
-
/**
- * fc_exch_em_alloc() - allocate an exchange from a specified EM.
- * @lport: ptr to the local port
- * @mp: ptr to the exchange manager
+ * fc_exch_em_alloc() - Allocate an exchange from a specified EM.
+ * @lport: The local port that the exchange is for
+ * @mp: The exchange manager that will allocate the exchange
*
* Returns pointer to allocated fc_exch with exch lock held.
*/
@@ -563,16 +724,18 @@ err:
}
/**
- * fc_exch_alloc() - allocate an exchange.
- * @lport: ptr to the local port
- * @fp: ptr to the FC frame
+ * fc_exch_alloc() - Allocate an exchange from an EM on a
+ * local port's list of EMs.
+ * @lport: The local port that will own the exchange
+ * @fp: The FC frame that the exchange will be for
*
- * This function walks the list of the exchange manager(EM)
- * anchors to select a EM for new exchange allocation. The
- * EM is selected having either a NULL match function pointer
- * or call to match function returning true.
+ * This function walks the list of exchange manager(EM)
+ * anchors to select an EM for a new exchange allocation. The
+ * EM is selected when a NULL match function pointer is encountered
+ * or when a call to a match function returns true.
*/
-struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
+static struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
+ struct fc_frame *fp)
{
struct fc_exch_mgr_anchor *ema;
struct fc_exch *ep;
@@ -586,10 +749,11 @@ struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
}
return NULL;
}
-EXPORT_SYMBOL(fc_exch_alloc);
-/*
- * Lookup and hold an exchange.
+/**
+ * fc_exch_find() - Lookup and hold an exchange
+ * @mp: The exchange manager to lookup the exchange from
+ * @xid: The XID of the exchange to look up
*/
static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
{
@@ -609,7 +773,13 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
return ep;
}
-void fc_exch_done(struct fc_seq *sp)
+
+/**
+ * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and
+ * the memory allocated for the related objects may be freed.
+ * @sp: The sequence that has completed
+ */
+static void fc_exch_done(struct fc_seq *sp)
{
struct fc_exch *ep = fc_seq_exch(sp);
int rc;
@@ -620,10 +790,13 @@ void fc_exch_done(struct fc_seq *sp)
if (!rc)
fc_exch_delete(ep);
}
-EXPORT_SYMBOL(fc_exch_done);
-/*
- * Allocate a new exchange as responder.
+/**
+ * fc_exch_resp() - Allocate a new exchange for a response frame
+ * @lport: The local port that the exchange was for
+ * @mp: The exchange manager to allocate the exchange from
+ * @fp: The response frame
+ *
* Sets the responder ID in the frame header.
*/
static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
@@ -664,8 +837,13 @@ static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
return ep;
}
-/*
- * Find a sequence for receive where the other end is originating the sequence.
+/**
+ * fc_seq_lookup_recip() - Find a sequence where the other end
+ * originated the sequence
+ * @lport: The local port that the frame was sent to
+ * @mp: The Exchange Manager to lookup the exchange from
+ * @fp: The frame associated with the sequence we're looking for
+ *
* If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold
* on the ep that should be released by the caller.
*/
@@ -771,10 +949,12 @@ rel:
return reject;
}
-/*
- * Find the sequence for a frame being received.
- * We originated the sequence, so it should be found.
- * We may or may not have originated the exchange.
+/**
+ * fc_seq_lookup_orig() - Find a sequence where this end
+ * originated the sequence
+ * @mp: The Exchange Manager to lookup the exchange from
+ * @fp: The frame associated with the sequence we're looking for
+ *
* Does not hold the sequence for the caller.
*/
static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
@@ -806,8 +986,12 @@ static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
return sp;
}
-/*
- * Set addresses for an exchange.
+/**
+ * fc_exch_set_addr() - Set the source and destination IDs for an exchange
+ * @ep: The exchange to set the addresses for
+ * @orig_id: The originator's ID
+ * @resp_id: The responder's ID
+ *
* Note this must be done before the first sequence of the exchange is sent.
*/
static void fc_exch_set_addr(struct fc_exch *ep,
@@ -823,76 +1007,15 @@ static void fc_exch_set_addr(struct fc_exch *ep,
}
}
-static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
-{
- struct fc_exch *ep = fc_seq_exch(sp);
-
- sp = fc_seq_alloc(ep, ep->seq_id++);
- FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
- ep->f_ctl, sp->id);
- return sp;
-}
-/*
- * Allocate a new sequence on the same exchange as the supplied sequence.
- * This will never return NULL.
+/**
+ * fc_seq_els_rsp_send() - Send an ELS response using infomation from
+ * the existing sequence/exchange.
+ * @sp: The sequence/exchange to get information from
+ * @els_cmd: The ELS command to be sent
+ * @els_data: The ELS data to be sent
*/
-struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
-{
- struct fc_exch *ep = fc_seq_exch(sp);
-
- spin_lock_bh(&ep->ex_lock);
- sp = fc_seq_start_next_locked(sp);
- spin_unlock_bh(&ep->ex_lock);
-
- return sp;
-}
-EXPORT_SYMBOL(fc_seq_start_next);
-
-int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp)
-{
- struct fc_exch *ep;
- struct fc_frame_header *fh = fc_frame_header_get(fp);
- int error;
- u32 f_ctl;
-
- ep = fc_seq_exch(sp);
- WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
-
- f_ctl = ntoh24(fh->fh_f_ctl);
- fc_exch_setup_hdr(ep, fp, f_ctl);
-
- /*
- * update sequence count if this frame is carrying
- * multiple FC frames when sequence offload is enabled
- * by LLD.
- */
- if (fr_max_payload(fp))
- sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
- fr_max_payload(fp));
- else
- sp->cnt++;
-
- /*
- * Send the frame.
- */
- error = lp->tt.frame_send(lp, fp);
-
- /*
- * Update the exchange and sequence flags,
- * assuming all frames for the sequence have been sent.
- * We can only be called to send once for each sequence.
- */
- spin_lock_bh(&ep->ex_lock);
- ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */
- if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
- ep->esb_stat &= ~ESB_ST_SEQ_INIT;
- spin_unlock_bh(&ep->ex_lock);
- return error;
-}
-EXPORT_SYMBOL(fc_seq_send);
-
-void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
- struct fc_seq_els_data *els_data)
+static void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+ struct fc_seq_els_data *els_data)
{
switch (els_cmd) {
case ELS_LS_RJT:
@@ -911,10 +1034,13 @@ void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd);
}
}
-EXPORT_SYMBOL(fc_seq_els_rsp_send);
-/*
- * Send a sequence, which is also the last sequence in the exchange.
+/**
+ * fc_seq_send_last() - Send a sequence that is the last in the exchange
+ * @sp: The sequence that is to be sent
+ * @fp: The frame that will be sent on the sequence
+ * @rctl: The R_CTL information to be sent
+ * @fh_type: The frame header type
*/
static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
enum fc_rctl rctl, enum fc_fh_type fh_type)
@@ -928,9 +1054,12 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
fc_seq_send(ep->lp, sp, fp);
}
-/*
+/**
+ * fc_seq_send_ack() - Send an acknowledgement that we've received a frame
+ * @sp: The sequence to send the ACK on
+ * @rx_fp: The received frame that is being acknoledged
+ *
* Send ACK_1 (or equiv.) indicating we received something.
- * The frame we're acking is supplied.
*/
static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
{
@@ -938,14 +1067,14 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
struct fc_frame_header *rx_fh;
struct fc_frame_header *fh;
struct fc_exch *ep = fc_seq_exch(sp);
- struct fc_lport *lp = ep->lp;
+ struct fc_lport *lport = ep->lp;
unsigned int f_ctl;
/*
* Don't send ACKs for class 3.
*/
if (fc_sof_needs_ack(fr_sof(rx_fp))) {
- fp = fc_frame_alloc(lp, 0);
+ fp = fc_frame_alloc(lport, 0);
if (!fp)
return;
@@ -980,12 +1109,16 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
else
fr_eof(fp) = FC_EOF_N;
- (void) lp->tt.frame_send(lp, fp);
+ lport->tt.frame_send(lport, fp);
}
}
-/*
- * Send BLS Reject.
+/**
+ * fc_exch_send_ba_rjt() - Send BLS Reject
+ * @rx_fp: The frame being rejected
+ * @reason: The reason the frame is being rejected
+ * @explan: The explaination for the rejection
+ *
* This is for rejecting BA_ABTS only.
*/
static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
@@ -996,11 +1129,11 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
struct fc_frame_header *rx_fh;
struct fc_frame_header *fh;
struct fc_ba_rjt *rp;
- struct fc_lport *lp;
+ struct fc_lport *lport;
unsigned int f_ctl;
- lp = fr_dev(rx_fp);
- fp = fc_frame_alloc(lp, sizeof(*rp));
+ lport = fr_dev(rx_fp);
+ fp = fc_frame_alloc(lport, sizeof(*rp));
if (!fp)
return;
fh = fc_frame_header_get(fp);
@@ -1045,13 +1178,17 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
if (fc_sof_needs_ack(fr_sof(fp)))
fr_eof(fp) = FC_EOF_N;
- (void) lp->tt.frame_send(lp, fp);
+ lport->tt.frame_send(lport, fp);
}
-/*
- * Handle an incoming ABTS. This would be for target mode usually,
- * but could be due to lost FCP transfer ready, confirm or RRQ.
- * We always handle this as an exchange abort, ignoring the parameter.
+/**
+ * fc_exch_recv_abts() - Handle an incoming ABTS
+ * @ep: The exchange the abort was on
+ * @rx_fp: The ABTS frame
+ *
+ * This would be for target mode usually, but could be due to lost
+ * FCP transfer ready, confirm or RRQ. We always handle this as an
+ * exchange abort, ignoring the parameter.
*/
static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
{
@@ -1100,10 +1237,14 @@ free:
fc_frame_free(rx_fp);
}
-/*
- * Handle receive where the other end is originating the sequence.
+/**
+ * fc_exch_recv_req() - Handler for an incoming request where is other
+ * end is originating the sequence
+ * @lport: The local port that received the request
+ * @mp: The EM that the exchange is on
+ * @fp: The request frame
*/
-static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
+static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
@@ -1114,8 +1255,17 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
u32 f_ctl;
enum fc_pf_rjt_reason reject;
+ /* We can have the wrong fc_lport at this point with NPIV, which is a
+ * problem now that we know a new exchange needs to be allocated
+ */
+ lport = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
+ if (!lport) {
+ fc_frame_free(fp);
+ return;
+ }
+
fr_seq(fp) = NULL;
- reject = fc_seq_lookup_recip(lp, mp, fp);
+ reject = fc_seq_lookup_recip(lport, mp, fp);
if (reject == FC_RJT_NONE) {
sp = fr_seq(fp); /* sequence will be held */
ep = fc_seq_exch(sp);
@@ -1138,17 +1288,21 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
if (ep->resp)
ep->resp(sp, fp, ep->arg);
else
- lp->tt.lport_recv(lp, sp, fp);
+ lport->tt.lport_recv(lport, sp, fp);
fc_exch_release(ep); /* release from lookup */
} else {
- FC_LPORT_DBG(lp, "exch/seq lookup failed: reject %x\n", reject);
+ FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n",
+ reject);
fc_frame_free(fp);
}
}
-/*
- * Handle receive where the other end is originating the sequence in
- * response to our exchange.
+/**
+ * fc_exch_recv_seq_resp() - Handler for an incoming response where the other
+ * end is the originator of the sequence that is a
+ * response to our initial exchange
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
*/
static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
{
@@ -1239,8 +1393,11 @@ out:
fc_frame_free(fp);
}
-/*
- * Handle receive for a sequence where other end is responding to our sequence.
+/**
+ * fc_exch_recv_resp() - Handler for a sequence where other end is
+ * responding to our sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
*/
static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
{
@@ -1256,9 +1413,13 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
fc_frame_free(fp);
}
-/*
- * Handle the response to an ABTS for exchange or sequence.
- * This can be BA_ACC or BA_RJT.
+/**
+ * fc_exch_abts_resp() - Handler for a response to an ABT
+ * @ep: The exchange that the frame is on
+ * @fp: The response frame
+ *
+ * This response would be to an ABTS cancelling an exchange or sequence.
+ * The response can be either BA_ACC or BA_RJT
*/
static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
{
@@ -1333,9 +1494,12 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
}
-/*
- * Receive BLS sequence.
- * This is always a sequence initiated by the remote side.
+/**
+ * fc_exch_recv_bls() - Handler for a BLS sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The request frame
+ *
+ * The BLS frame is always a sequence initiated by the remote side.
* We may be either the originator or recipient of the exchange.
*/
static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
@@ -1392,8 +1556,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
fc_exch_release(ep); /* release hold taken by fc_exch_find */
}
-/*
- * Accept sequence with LS_ACC.
+/**
+ * fc_seq_ls_acc() - Accept sequence with LS_ACC
+ * @req_sp: The request sequence
+ *
* If this fails due to allocation or transmit congestion, assume the
* originator will repeat the sequence.
*/
@@ -1413,8 +1579,12 @@ static void fc_seq_ls_acc(struct fc_seq *req_sp)
}
}
-/*
- * Reject sequence with ELS LS_RJT.
+/**
+ * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT
+ * @req_sp: The request sequence
+ * @reason: The reason the sequence is being rejected
+ * @explan: The explaination for the rejection
+ *
* If this fails due to allocation or transmit congestion, assume the
* originator will repeat the sequence.
*/
@@ -1437,6 +1607,10 @@ static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason,
}
}
+/**
+ * fc_exch_reset() - Reset an exchange
+ * @ep: The exchange to be reset
+ */
static void fc_exch_reset(struct fc_exch *ep)
{
struct fc_seq *sp;
@@ -1446,12 +1620,6 @@ static void fc_exch_reset(struct fc_exch *ep)
spin_lock_bh(&ep->ex_lock);
ep->state |= FC_EX_RST_CLEANUP;
- /*
- * we really want to call del_timer_sync, but cannot due
- * to the lport calling with the lport lock held (some resp
- * functions can also grab the lport lock which could cause
- * a deadlock).
- */
if (cancel_delayed_work(&ep->timeout_work))
atomic_dec(&ep->ex_refcnt); /* drop hold for timer */
resp = ep->resp;
@@ -1471,16 +1639,16 @@ static void fc_exch_reset(struct fc_exch *ep)
}
/**
- * fc_exch_pool_reset() - Resets an per cpu exches pool.
- * @lport: ptr to the local port
- * @pool: ptr to the per cpu exches pool
- * @sid: source FC ID
- * @did: destination FC ID
+ * fc_exch_pool_reset() - Reset a per cpu exchange pool
+ * @lport: The local port that the exchange pool is on
+ * @pool: The exchange pool to be reset
+ * @sid: The source ID
+ * @did: The destination ID
*
- * Resets an per cpu exches pool, releasing its all sequences
- * and exchanges. If sid is non-zero, then reset only exchanges
- * we sourced from that FID. If did is non-zero, reset only
- * exchanges destined to that FID.
+ * Resets a per cpu exches pool, releasing all of its sequences
+ * and exchanges. If sid is non-zero then reset only exchanges
+ * we sourced from the local port's FID. If did is non-zero then
+ * only reset exchanges destined for the local port's FID.
*/
static void fc_exch_pool_reset(struct fc_lport *lport,
struct fc_exch_pool *pool,
@@ -1514,15 +1682,15 @@ restart:
}
/**
- * fc_exch_mgr_reset() - Resets all EMs of a lport
- * @lport: ptr to the local port
- * @sid: source FC ID
- * @did: destination FC ID
+ * fc_exch_mgr_reset() - Reset all EMs of a local port
+ * @lport: The local port whose EMs are to be reset
+ * @sid: The source ID
+ * @did: The destination ID
*
- * Reset all EMs of a lport, releasing its all sequences and
- * exchanges. If sid is non-zero, then reset only exchanges
- * we sourced from that FID. If did is non-zero, reset only
- * exchanges destined to that FID.
+ * Reset all EMs associated with a given local port. Release all
+ * sequences and exchanges. If sid is non-zero then reset only the
+ * exchanges sent from the local port's FID. If did is non-zero then
+ * reset only exchanges destined for the local port's FID.
*/
void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
{
@@ -1538,8 +1706,11 @@ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
}
EXPORT_SYMBOL(fc_exch_mgr_reset);
-/*
- * Handle incoming ELS REC - Read Exchange Concise.
+/**
+ * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests
+ * @sp: The sequence the REC is on
+ * @rfp: The REC frame
+ *
* Note that the requesting port may be different than the S_ID in the request.
*/
static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
@@ -1621,10 +1792,11 @@ reject:
fc_frame_free(rfp);
}
-/*
- * Handle response from RRQ.
- * Not much to do here, really.
- * Should report errors.
+/**
+ * fc_exch_rrq_resp() - Handler for RRQ responses
+ * @sp: The sequence that the RRQ is on
+ * @fp: The RRQ frame
+ * @arg: The exchange that the RRQ is on
*
* TODO: fix error handler.
*/
@@ -1664,21 +1836,99 @@ cleanup:
fc_exch_release(aborted_ep);
}
-/*
- * Send ELS RRQ - Reinstate Recovery Qualifier.
+
+/**
+ * fc_exch_seq_send() - Send a frame using a new exchange and sequence
+ * @lport: The local port to send the frame on
+ * @fp: The frame to be sent
+ * @resp: The response handler for this request
+ * @destructor: The destructor for the exchange
+ * @arg: The argument to be passed to the response handler
+ * @timer_msec: The timeout period for the exchange
+ *
+ * The frame pointer with some of the header's fields must be
+ * filled before calling this routine, those fields are:
+ *
+ * - routing control
+ * - FC port did
+ * - FC port sid
+ * - FC header type
+ * - frame control
+ * - parameter or relative offset
+ */
+static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
+ struct fc_frame *fp,
+ void (*resp)(struct fc_seq *,
+ struct fc_frame *fp,
+ void *arg),
+ void (*destructor)(struct fc_seq *,
+ void *),
+ void *arg, u32 timer_msec)
+{
+ struct fc_exch *ep;
+ struct fc_seq *sp = NULL;
+ struct fc_frame_header *fh;
+ int rc = 1;
+
+ ep = fc_exch_alloc(lport, fp);
+ if (!ep) {
+ fc_frame_free(fp);
+ return NULL;
+ }
+ ep->esb_stat |= ESB_ST_SEQ_INIT;
+ fh = fc_frame_header_get(fp);
+ fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
+ ep->resp = resp;
+ ep->destructor = destructor;
+ ep->arg = arg;
+ ep->r_a_tov = FC_DEF_R_A_TOV;
+ ep->lp = lport;
+ sp = &ep->seq;
+
+ ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
+ ep->f_ctl = ntoh24(fh->fh_f_ctl);
+ fc_exch_setup_hdr(ep, fp, ep->f_ctl);
+ sp->cnt++;
+
+ if (ep->xid <= lport->lro_xid)
+ fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+
+ if (unlikely(lport->tt.frame_send(lport, fp)))
+ goto err;
+
+ if (timer_msec)
+ fc_exch_timer_set_locked(ep, timer_msec);
+ ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not first seq */
+
+ if (ep->f_ctl & FC_FC_SEQ_INIT)
+ ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+ spin_unlock_bh(&ep->ex_lock);
+ return sp;
+err:
+ rc = fc_exch_done_locked(ep);
+ spin_unlock_bh(&ep->ex_lock);
+ if (!rc)
+ fc_exch_delete(ep);
+ return NULL;
+}
+
+/**
+ * fc_exch_rrq() - Send an ELS RRQ (Reinstate Recovery Qualifier) command
+ * @ep: The exchange to send the RRQ on
+ *
* This tells the remote port to stop blocking the use of
* the exchange and the seq_cnt range.
*/
static void fc_exch_rrq(struct fc_exch *ep)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fc_els_rrq *rrq;
struct fc_frame *fp;
u32 did;
- lp = ep->lp;
+ lport = ep->lp;
- fp = fc_frame_alloc(lp, sizeof(*rrq));
+ fp = fc_frame_alloc(lport, sizeof(*rrq));
if (!fp)
goto retry;
@@ -1694,10 +1944,11 @@ static void fc_exch_rrq(struct fc_exch *ep)
did = ep->sid;
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did,
- fc_host_port_id(lp->host), FC_TYPE_ELS,
+ fc_host_port_id(lport->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- if (fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep, lp->e_d_tov))
+ if (fc_exch_seq_send(lport, fp, fc_exch_rrq_resp, NULL, ep,
+ lport->e_d_tov))
return;
retry:
@@ -1714,12 +1965,14 @@ retry:
}
-/*
- * Handle incoming ELS RRQ - Reset Recovery Qualifier.
+/**
+ * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests
+ * @sp: The sequence that the RRQ is on
+ * @fp: The RRQ frame
*/
static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
{
- struct fc_exch *ep; /* request or subject exchange */
+ struct fc_exch *ep = NULL; /* request or subject exchange */
struct fc_els_rrq *rp;
u32 sid;
u16 xid;
@@ -1769,17 +2022,24 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
* Send LS_ACC.
*/
fc_seq_ls_acc(sp);
- fc_frame_free(fp);
- return;
+ goto out;
unlock_reject:
spin_unlock_bh(&ep->ex_lock);
- fc_exch_release(ep); /* drop hold from fc_exch_find */
reject:
fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan);
+out:
fc_frame_free(fp);
+ if (ep)
+ fc_exch_release(ep); /* drop hold from fc_exch_find */
}
+/**
+ * fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs
+ * @lport: The local port to add the exchange manager to
+ * @mp: The exchange manager to be added to the local port
+ * @match: The match routine that indicates when this EM should be used
+ */
struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
struct fc_exch_mgr *mp,
bool (*match)(struct fc_frame *))
@@ -1799,6 +2059,10 @@ struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
}
EXPORT_SYMBOL(fc_exch_mgr_add);
+/**
+ * fc_exch_mgr_destroy() - Destroy an exchange manager
+ * @kref: The reference to the EM to be destroyed
+ */
static void fc_exch_mgr_destroy(struct kref *kref)
{
struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref);
@@ -1808,6 +2072,10 @@ static void fc_exch_mgr_destroy(struct kref *kref)
kfree(mp);
}
+/**
+ * fc_exch_mgr_del() - Delete an EM from a local port's list
+ * @ema: The exchange manager anchor identifying the EM to be deleted
+ */
void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
{
/* remove EM anchor from EM anchors list */
@@ -1817,7 +2085,35 @@ void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
}
EXPORT_SYMBOL(fc_exch_mgr_del);
-struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+/**
+ * fc_exch_mgr_list_clone() - Share all exchange manager objects
+ * @src: Source lport to clone exchange managers from
+ * @dst: New lport that takes references to all the exchange managers
+ */
+int fc_exch_mgr_list_clone(struct fc_lport *src, struct fc_lport *dst)
+{
+ struct fc_exch_mgr_anchor *ema, *tmp;
+
+ list_for_each_entry(ema, &src->ema_list, ema_list) {
+ if (!fc_exch_mgr_add(dst, ema->mp, ema->match))
+ goto err;
+ }
+ return 0;
+err:
+ list_for_each_entry_safe(ema, tmp, &dst->ema_list, ema_list)
+ fc_exch_mgr_del(ema);
+ return -ENOMEM;
+}
+
+/**
+ * fc_exch_mgr_alloc() - Allocate an exchange manager
+ * @lport: The local port that the new EM will be associated with
+ * @class: The default FC class for new exchanges
+ * @min_xid: The minimum XID for exchanges from the new EM
+ * @max_xid: The maximum XID for exchanges from the new EM
+ * @match: The match routine for the new EM
+ */
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
enum fc_class class,
u16 min_xid, u16 max_xid,
bool (*match)(struct fc_frame *))
@@ -1830,7 +2126,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN ||
(min_xid & fc_cpu_mask) != 0) {
- FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
+ FC_LPORT_DBG(lport, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
min_xid, max_xid);
return NULL;
}
@@ -1873,7 +2169,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
}
kref_init(&mp->kref);
- if (!fc_exch_mgr_add(lp, mp, match)) {
+ if (!fc_exch_mgr_add(lport, mp, match)) {
free_percpu(mp->pool);
goto free_mempool;
}
@@ -1894,76 +2190,26 @@ free_mp:
}
EXPORT_SYMBOL(fc_exch_mgr_alloc);
+/**
+ * fc_exch_mgr_free() - Free all exchange managers on a local port
+ * @lport: The local port whose EMs are to be freed
+ */
void fc_exch_mgr_free(struct fc_lport *lport)
{
struct fc_exch_mgr_anchor *ema, *next;
+ flush_workqueue(fc_exch_workqueue);
list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list)
fc_exch_mgr_del(ema);
}
EXPORT_SYMBOL(fc_exch_mgr_free);
-
-struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
- struct fc_frame *fp,
- void (*resp)(struct fc_seq *,
- struct fc_frame *fp,
- void *arg),
- void (*destructor)(struct fc_seq *, void *),
- void *arg, u32 timer_msec)
-{
- struct fc_exch *ep;
- struct fc_seq *sp = NULL;
- struct fc_frame_header *fh;
- int rc = 1;
-
- ep = fc_exch_alloc(lp, fp);
- if (!ep) {
- fc_frame_free(fp);
- return NULL;
- }
- ep->esb_stat |= ESB_ST_SEQ_INIT;
- fh = fc_frame_header_get(fp);
- fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
- ep->resp = resp;
- ep->destructor = destructor;
- ep->arg = arg;
- ep->r_a_tov = FC_DEF_R_A_TOV;
- ep->lp = lp;
- sp = &ep->seq;
-
- ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
- ep->f_ctl = ntoh24(fh->fh_f_ctl);
- fc_exch_setup_hdr(ep, fp, ep->f_ctl);
- sp->cnt++;
-
- if (ep->xid <= lp->lro_xid)
- fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
-
- if (unlikely(lp->tt.frame_send(lp, fp)))
- goto err;
-
- if (timer_msec)
- fc_exch_timer_set_locked(ep, timer_msec);
- ep->f_ctl &= ~FC_FC_FIRST_SEQ; /* not first seq */
-
- if (ep->f_ctl & FC_FC_SEQ_INIT)
- ep->esb_stat &= ~ESB_ST_SEQ_INIT;
- spin_unlock_bh(&ep->ex_lock);
- return sp;
-err:
- rc = fc_exch_done_locked(ep);
- spin_unlock_bh(&ep->ex_lock);
- if (!rc)
- fc_exch_delete(ep);
- return NULL;
-}
-EXPORT_SYMBOL(fc_exch_seq_send);
-
-/*
- * Receive a frame
+/**
+ * fc_exch_recv() - Handler for received frames
+ * @lport: The local port the frame was received on
+ * @fp: The received frame
*/
-void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
+void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
struct fc_exch_mgr_anchor *ema;
@@ -1971,8 +2217,8 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
u16 oxid;
/* lport lock ? */
- if (!lp || lp->state == LPORT_ST_DISABLED) {
- FC_LPORT_DBG(lp, "Receiving frames for an lport that "
+ if (!lport || lport->state == LPORT_ST_DISABLED) {
+ FC_LPORT_DBG(lport, "Receiving frames for an lport that "
"has not been initialized correctly\n");
fc_frame_free(fp);
return;
@@ -1981,7 +2227,7 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
f_ctl = ntoh24(fh->fh_f_ctl);
oxid = ntohs(fh->fh_ox_id);
if (f_ctl & FC_FC_EX_CTX) {
- list_for_each_entry(ema, &lp->ema_list, ema_list) {
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
if ((oxid >= ema->mp->min_xid) &&
(oxid <= ema->mp->max_xid)) {
found = 1;
@@ -1990,13 +2236,13 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
}
if (!found) {
- FC_LPORT_DBG(lp, "Received response for out "
+ FC_LPORT_DBG(lport, "Received response for out "
"of range oxid:%hx\n", oxid);
fc_frame_free(fp);
return;
}
} else
- ema = list_entry(lp->ema_list.prev, typeof(*ema), ema_list);
+ ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list);
/*
* If frame is marked invalid, just drop it.
@@ -2015,37 +2261,56 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
else if (f_ctl & FC_FC_SEQ_CTX)
fc_exch_recv_resp(ema->mp, fp);
else
- fc_exch_recv_req(lp, ema->mp, fp);
+ fc_exch_recv_req(lport, ema->mp, fp);
break;
default:
- FC_LPORT_DBG(lp, "dropping invalid frame (eof %x)", fr_eof(fp));
+ FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)",
+ fr_eof(fp));
fc_frame_free(fp);
}
}
EXPORT_SYMBOL(fc_exch_recv);
-int fc_exch_init(struct fc_lport *lp)
+/**
+ * fc_exch_init() - Initialize the exchange layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
+int fc_exch_init(struct fc_lport *lport)
{
- if (!lp->tt.seq_start_next)
- lp->tt.seq_start_next = fc_seq_start_next;
+ if (!lport->tt.seq_start_next)
+ lport->tt.seq_start_next = fc_seq_start_next;
- if (!lp->tt.exch_seq_send)
- lp->tt.exch_seq_send = fc_exch_seq_send;
+ if (!lport->tt.exch_seq_send)
+ lport->tt.exch_seq_send = fc_exch_seq_send;
- if (!lp->tt.seq_send)
- lp->tt.seq_send = fc_seq_send;
+ if (!lport->tt.seq_send)
+ lport->tt.seq_send = fc_seq_send;
- if (!lp->tt.seq_els_rsp_send)
- lp->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
+ if (!lport->tt.seq_els_rsp_send)
+ lport->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
- if (!lp->tt.exch_done)
- lp->tt.exch_done = fc_exch_done;
+ if (!lport->tt.exch_done)
+ lport->tt.exch_done = fc_exch_done;
- if (!lp->tt.exch_mgr_reset)
- lp->tt.exch_mgr_reset = fc_exch_mgr_reset;
+ if (!lport->tt.exch_mgr_reset)
+ lport->tt.exch_mgr_reset = fc_exch_mgr_reset;
- if (!lp->tt.seq_exch_abort)
- lp->tt.seq_exch_abort = fc_seq_exch_abort;
+ if (!lport->tt.seq_exch_abort)
+ lport->tt.seq_exch_abort = fc_seq_exch_abort;
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_exch_init);
+
+/**
+ * fc_setup_exch_mgr() - Setup an exchange manager
+ */
+int fc_setup_exch_mgr()
+{
+ fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!fc_em_cachep)
+ return -ENOMEM;
/*
* Initialize fc_cpu_mask and fc_cpu_order. The
@@ -2069,20 +2334,17 @@ int fc_exch_init(struct fc_lport *lp)
}
fc_cpu_mask--;
- return 0;
-}
-EXPORT_SYMBOL(fc_exch_init);
-
-int fc_setup_exch_mgr(void)
-{
- fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
- 0, SLAB_HWCACHE_ALIGN, NULL);
- if (!fc_em_cachep)
+ fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");
+ if (!fc_exch_workqueue)
return -ENOMEM;
return 0;
}
-void fc_destroy_exch_mgr(void)
+/**
+ * fc_destroy_exch_mgr() - Destroy an exchange manager
+ */
+void fc_destroy_exch_mgr()
{
+ destroy_workqueue(fc_exch_workqueue);
kmem_cache_destroy(fc_em_cachep);
}
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 59a4408b27b..c4b58d042f6 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -39,15 +39,9 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
-MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("libfc");
-MODULE_LICENSE("GPL v2");
+#include "fc_libfc.h"
-unsigned int fc_debug_logging;
-module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-
-static struct kmem_cache *scsi_pkt_cachep;
+struct kmem_cache *scsi_pkt_cachep;
/* SRB state definitions */
#define FC_SRB_FREE 0 /* cmd is free */
@@ -58,7 +52,6 @@ static struct kmem_cache *scsi_pkt_cachep;
#define FC_SRB_DISCONTIG (1 << 4) /* non-sequential data recvd */
#define FC_SRB_COMPL (1 << 5) /* fc_io_compl has been run */
#define FC_SRB_FCP_PROCESSING_TMO (1 << 6) /* timer function processing */
-#define FC_SRB_NOMEM (1 << 7) /* dropped to out of mem */
#define FC_SRB_READ (1 << 1)
#define FC_SRB_WRITE (1 << 0)
@@ -73,10 +66,20 @@ static struct kmem_cache *scsi_pkt_cachep;
#define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status)
#define CMD_RESID_LEN(Cmnd) ((Cmnd)->SCp.buffers_residual)
+/**
+ * struct fc_fcp_internal - FCP layer internal data
+ * @scsi_pkt_pool: Memory pool to draw FCP packets from
+ * @scsi_pkt_queue: Current FCP packets
+ * @last_can_queue_ramp_down_time: ramp down time
+ * @last_can_queue_ramp_up_time: ramp up time
+ * @max_can_queue: max can_queue size
+ */
struct fc_fcp_internal {
- mempool_t *scsi_pkt_pool;
+ mempool_t *scsi_pkt_pool;
struct list_head scsi_pkt_queue;
- u8 throttled;
+ unsigned long last_can_queue_ramp_down_time;
+ unsigned long last_can_queue_ramp_up_time;
+ int max_can_queue;
};
#define fc_get_scsi_internal(x) ((struct fc_fcp_internal *)(x)->scsi_priv)
@@ -90,9 +93,9 @@ static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
-static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp);
+static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_timeout_error(struct fc_fcp_pkt *);
-static void fc_fcp_timeout(unsigned long data);
+static void fc_fcp_timeout(unsigned long);
static void fc_fcp_rec(struct fc_fcp_pkt *);
static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *);
@@ -124,6 +127,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
#define FC_SCSI_TM_TOV (10 * HZ)
#define FC_SCSI_REC_TOV (2 * HZ)
#define FC_HOST_RESET_TIMEOUT (30 * HZ)
+#define FC_CAN_QUEUE_PERIOD (60 * HZ)
#define FC_MAX_ERROR_CNT 5
#define FC_MAX_RECOV_RETRY 3
@@ -131,23 +135,22 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
#define FC_FCP_DFLT_QUEUE_DEPTH 32
/**
- * fc_fcp_pkt_alloc - allocation routine for scsi_pkt packet
- * @lp: fc lport struct
- * @gfp: gfp flags for allocation
+ * fc_fcp_pkt_alloc() - Allocate a fcp_pkt
+ * @lport: The local port that the FCP packet is for
+ * @gfp: GFP flags for allocation
*
- * This is used by upper layer scsi driver.
- * Return Value : scsi_pkt structure or null on allocation failure.
- * Context : call from process context. no locking required.
+ * Return value: fcp_pkt structure or null on allocation failure.
+ * Context: Can be called from process context, no lock is required.
*/
-static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
+static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
struct fc_fcp_pkt *fsp;
fsp = mempool_alloc(si->scsi_pkt_pool, gfp);
if (fsp) {
memset(fsp, 0, sizeof(*fsp));
- fsp->lp = lp;
+ fsp->lp = lport;
atomic_set(&fsp->ref_cnt, 1);
init_timer(&fsp->timer);
INIT_LIST_HEAD(&fsp->list);
@@ -157,12 +160,11 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
}
/**
- * fc_fcp_pkt_release() - release hold on scsi_pkt packet
- * @fsp: fcp packet struct
+ * fc_fcp_pkt_release() - Release hold on a fcp_pkt
+ * @fsp: The FCP packet to be released
*
- * This is used by upper layer scsi driver.
- * Context : call from process and interrupt context.
- * no locking required
+ * Context: Can be called from process or interrupt context,
+ * no lock is required.
*/
static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
{
@@ -173,20 +175,25 @@ static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
}
}
+/**
+ * fc_fcp_pkt_hold() - Hold a fcp_pkt
+ * @fsp: The FCP packet to be held
+ */
static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp)
{
atomic_inc(&fsp->ref_cnt);
}
/**
- * fc_fcp_pkt_destory() - release hold on scsi_pkt packet
- * @seq: exchange sequence
- * @fsp: fcp packet struct
+ * fc_fcp_pkt_destory() - Release hold on a fcp_pkt
+ * @seq: The sequence that the FCP packet is on (required by destructor API)
+ * @fsp: The FCP packet to be released
+ *
+ * This routine is called by a destructor callback in the exch_seq_send()
+ * routine of the libfc Transport Template. The 'struct fc_seq' is a required
+ * argument even though it is not used by this routine.
*
- * Release hold on scsi_pkt packet set to keep scsi_pkt
- * till EM layer exch resource is not freed.
- * Context : called from from EM layer.
- * no locking required
+ * Context: No locking required.
*/
static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
{
@@ -194,10 +201,10 @@ static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
}
/**
- * fc_fcp_lock_pkt() - lock a packet and get a ref to it.
- * @fsp: fcp packet
+ * fc_fcp_lock_pkt() - Lock a fcp_pkt and increase its reference count
+ * @fsp: The FCP packet to be locked and incremented
*
- * We should only return error if we return a command to scsi-ml before
+ * We should only return error if we return a command to SCSI-ml before
* getting a response. This can happen in cases where we send a abort, but
* do not wait for the response and the abort and command can be passing
* each other on the wire/network-layer.
@@ -222,18 +229,33 @@ static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
return 0;
}
+/**
+ * fc_fcp_unlock_pkt() - Release a fcp_pkt's lock and decrement its
+ * reference count
+ * @fsp: The FCP packet to be unlocked and decremented
+ */
static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
{
spin_unlock_bh(&fsp->scsi_pkt_lock);
fc_fcp_pkt_release(fsp);
}
+/**
+ * fc_fcp_timer_set() - Start a timer for a fcp_pkt
+ * @fsp: The FCP packet to start a timer for
+ * @delay: The timeout period for the timer
+ */
static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
{
if (!(fsp->state & FC_SRB_COMPL))
mod_timer(&fsp->timer, jiffies + delay);
}
+/**
+ * fc_fcp_send_abort() - Send an abort for exchanges associated with a
+ * fcp_pkt
+ * @fsp: The FCP packet to abort exchanges on
+ */
static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
{
if (!fsp->seq_ptr)
@@ -243,9 +265,14 @@ static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);
}
-/*
- * Retry command.
- * An abort isn't needed.
+/**
+ * fc_fcp_retry_cmd() - Retry a fcp_pkt
+ * @fsp: The FCP packet to be retried
+ *
+ * Sets the status code to be FC_ERROR and then calls
+ * fc_fcp_complete_locked() which in turn calls fc_io_compl().
+ * fc_io_compl() will notify the SCSI-ml that the I/O is done.
+ * The SCSI-ml will retry the command.
*/
static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
{
@@ -260,64 +287,146 @@ static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
fc_fcp_complete_locked(fsp);
}
-/*
- * fc_fcp_ddp_setup - calls to LLD's ddp_setup to set up DDP
- * transfer for a read I/O indicated by the fc_fcp_pkt.
- * @fsp: ptr to the fc_fcp_pkt
- *
- * This is called in exch_seq_send() when we have a newly allocated
- * exchange with a valid exchange id to setup ddp.
- *
- * returns: none
+/**
+ * fc_fcp_ddp_setup() - Calls a LLD's ddp_setup routine to set up DDP context
+ * @fsp: The FCP packet that will manage the DDP frames
+ * @xid: The XID that will be used for the DDP exchange
*/
void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
if (!fsp)
return;
- lp = fsp->lp;
+ lport = fsp->lp;
if ((fsp->req_flags & FC_SRB_READ) &&
- (lp->lro_enabled) && (lp->tt.ddp_setup)) {
- if (lp->tt.ddp_setup(lp, xid, scsi_sglist(fsp->cmd),
- scsi_sg_count(fsp->cmd)))
+ (lport->lro_enabled) && (lport->tt.ddp_setup)) {
+ if (lport->tt.ddp_setup(lport, xid, scsi_sglist(fsp->cmd),
+ scsi_sg_count(fsp->cmd)))
fsp->xfer_ddp = xid;
}
}
-EXPORT_SYMBOL(fc_fcp_ddp_setup);
-/*
- * fc_fcp_ddp_done - calls to LLD's ddp_done to release any
- * DDP related resources for this I/O if it is initialized
- * as a ddp transfer
- * @fsp: ptr to the fc_fcp_pkt
- *
- * returns: none
+/**
+ * fc_fcp_ddp_done() - Calls a LLD's ddp_done routine to release any
+ * DDP related resources for a fcp_pkt
+ * @fsp: The FCP packet that DDP had been used on
*/
static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
if (!fsp)
return;
- lp = fsp->lp;
- if (fsp->xfer_ddp && lp->tt.ddp_done) {
- fsp->xfer_len = lp->tt.ddp_done(lp, fsp->xfer_ddp);
- fsp->xfer_ddp = 0;
+ if (fsp->xfer_ddp == FC_XID_UNKNOWN)
+ return;
+
+ lport = fsp->lp;
+ if (lport->tt.ddp_done) {
+ fsp->xfer_len = lport->tt.ddp_done(lport, fsp->xfer_ddp);
+ fsp->xfer_ddp = FC_XID_UNKNOWN;
}
}
+/**
+ * fc_fcp_can_queue_ramp_up() - increases can_queue
+ * @lport: lport to ramp up can_queue
+ *
+ * Locking notes: Called with Scsi_Host lock held
+ */
+static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+ int can_queue;
+
+ if (si->last_can_queue_ramp_up_time &&
+ (time_before(jiffies, si->last_can_queue_ramp_up_time +
+ FC_CAN_QUEUE_PERIOD)))
+ return;
+
+ if (time_before(jiffies, si->last_can_queue_ramp_down_time +
+ FC_CAN_QUEUE_PERIOD))
+ return;
+
+ si->last_can_queue_ramp_up_time = jiffies;
+
+ can_queue = lport->host->can_queue << 1;
+ if (can_queue >= si->max_can_queue) {
+ can_queue = si->max_can_queue;
+ si->last_can_queue_ramp_down_time = 0;
+ }
+ lport->host->can_queue = can_queue;
+ shost_printk(KERN_ERR, lport->host, "libfc: increased "
+ "can_queue to %d.\n", can_queue);
+}
+
+/**
+ * fc_fcp_can_queue_ramp_down() - reduces can_queue
+ * @lport: lport to reduce can_queue
+ *
+ * If we are getting memory allocation failures, then we may
+ * be trying to execute too many commands. We let the running
+ * commands complete or timeout, then try again with a reduced
+ * can_queue. Eventually we will hit the point where we run
+ * on all reserved structs.
+ *
+ * Locking notes: Called with Scsi_Host lock held
+ */
+static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
+{
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+ int can_queue;
+
+ if (si->last_can_queue_ramp_down_time &&
+ (time_before(jiffies, si->last_can_queue_ramp_down_time +
+ FC_CAN_QUEUE_PERIOD)))
+ return;
+
+ si->last_can_queue_ramp_down_time = jiffies;
+
+ can_queue = lport->host->can_queue;
+ can_queue >>= 1;
+ if (!can_queue)
+ can_queue = 1;
+ lport->host->can_queue = can_queue;
+ shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n"
+ "Reducing can_queue to %d.\n", can_queue);
+}
/*
- * Receive SCSI data from target.
- * Called after receiving solicited data.
+ * fc_fcp_frame_alloc() - Allocates fc_frame structure and buffer.
+ * @lport: fc lport struct
+ * @len: payload length
+ *
+ * Allocates fc_frame structure and buffer but if fails to allocate
+ * then reduce can_queue.
+ */
+static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,
+ size_t len)
+{
+ struct fc_frame *fp;
+ unsigned long flags;
+
+ fp = fc_frame_alloc(lport, len);
+ if (!fp) {
+ spin_lock_irqsave(lport->host->host_lock, flags);
+ fc_fcp_can_queue_ramp_down(lport);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
+ }
+ return fp;
+}
+
+/**
+ * fc_fcp_recv_data() - Handler for receiving SCSI-FCP data from a target
+ * @fsp: The FCP packet the data is on
+ * @fp: The data frame
*/
static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
struct scsi_cmnd *sc = fsp->cmd;
- struct fc_lport *lp = fsp->lp;
+ struct fc_lport *lport = fsp->lp;
struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
size_t start_offset;
@@ -327,7 +436,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
size_t len;
void *buf;
struct scatterlist *sg;
- size_t remaining;
+ u32 nents;
fh = fc_frame_header_get(fp);
offset = ntohl(fh->fh_parm_offset);
@@ -351,65 +460,29 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
if (offset != fsp->xfer_len)
fsp->state |= FC_SRB_DISCONTIG;
- crc = 0;
- if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
- crc = crc32(~0, (u8 *) fh, sizeof(*fh));
-
sg = scsi_sglist(sc);
- remaining = len;
-
- while (remaining > 0 && sg) {
- size_t off;
- void *page_addr;
- size_t sg_bytes;
+ nents = scsi_sg_count(sc);
- if (offset >= sg->length) {
- offset -= sg->length;
- sg = sg_next(sg);
- continue;
- }
- sg_bytes = min(remaining, sg->length - offset);
-
- /*
- * The scatterlist item may be bigger than PAGE_SIZE,
- * but we are limited to mapping PAGE_SIZE at a time.
- */
- off = offset + sg->offset;
- sg_bytes = min(sg_bytes, (size_t)
- (PAGE_SIZE - (off & ~PAGE_MASK)));
- page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
- KM_SOFTIRQ0);
- if (!page_addr)
- break; /* XXX panic? */
-
- if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
- crc = crc32(crc, buf, sg_bytes);
- memcpy((char *)page_addr + (off & ~PAGE_MASK), buf,
- sg_bytes);
-
- kunmap_atomic(page_addr, KM_SOFTIRQ0);
- buf += sg_bytes;
- offset += sg_bytes;
- remaining -= sg_bytes;
- copy_len += sg_bytes;
- }
-
- if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+ if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
+ copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
+ &offset, KM_SOFTIRQ0, NULL);
+ } else {
+ crc = crc32(~0, (u8 *) fh, sizeof(*fh));
+ copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
+ &offset, KM_SOFTIRQ0, &crc);
buf = fc_frame_payload_get(fp, 0);
- if (len % 4) {
+ if (len % 4)
crc = crc32(crc, buf + len, 4 - (len % 4));
- len += 4 - (len % 4);
- }
if (~crc != le32_to_cpu(fr_crc(fp))) {
crc_err:
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
stats->ErrorFrames++;
/* FIXME - per cpu count, not total count! */
if (stats->InvalidCRCCount++ < 5)
printk(KERN_WARNING "libfc: CRC error on data "
"frame for port (%6x)\n",
- fc_host_port_id(lp->host));
+ fc_host_port_id(lport->host));
/*
* Assume the frame is total garbage.
* We may have copied it over the good part
@@ -437,18 +510,17 @@ crc_err:
}
/**
- * fc_fcp_send_data() - Send SCSI data to target.
- * @fsp: ptr to fc_fcp_pkt
- * @sp: ptr to this sequence
- * @offset: starting offset for this data request
- * @seq_blen: the burst length for this data request
+ * fc_fcp_send_data() - Send SCSI data to a target
+ * @fsp: The FCP packet the data is on
+ * @sp: The sequence the data is to be sent on
+ * @offset: The starting offset for this data request
+ * @seq_blen: The burst length for this data request
*
* Called after receiving a Transfer Ready data descriptor.
- * if LLD is capable of seq offload then send down seq_blen
- * size of data in single frame, otherwise send multiple FC
- * frames of max FC frame payload supported by target port.
- *
- * Returns : 0 for success.
+ * If the LLD is capable of sequence offload then send down the
+ * seq_blen ammount of data in single frame, otherwise send
+ * multiple frames of the maximum frame payload supported by
+ * the target port.
*/
static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
size_t offset, size_t seq_blen)
@@ -457,16 +529,18 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
struct scsi_cmnd *sc;
struct scatterlist *sg;
struct fc_frame *fp = NULL;
- struct fc_lport *lp = fsp->lp;
+ struct fc_lport *lport = fsp->lp;
+ struct page *page;
size_t remaining;
size_t t_blen;
size_t tlen;
size_t sg_bytes;
size_t frame_offset, fh_parm_offset;
+ size_t off;
int error;
void *data = NULL;
void *page_addr;
- int using_sg = lp->sg_supp;
+ int using_sg = lport->sg_supp;
u32 f_ctl;
WARN_ON(seq_blen <= 0);
@@ -488,10 +562,10 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
* to max FC frame payload previously set in fsp->max_payload.
*/
t_blen = fsp->max_payload;
- if (lp->seq_offload) {
- t_blen = min(seq_blen, (size_t)lp->lso_max);
+ if (lport->seq_offload) {
+ t_blen = min(seq_blen, (size_t)lport->lso_max);
FC_FCP_DBG(fsp, "fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n",
- fsp, seq_blen, lp->lso_max, t_blen);
+ fsp, seq_blen, lport->lso_max, t_blen);
}
WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
@@ -503,7 +577,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
remaining = seq_blen;
fh_parm_offset = frame_offset = offset;
tlen = 0;
- seq = lp->tt.seq_start_next(seq);
+ seq = lport->tt.seq_start_next(seq);
f_ctl = FC_FC_REL_OFF;
WARN_ON(!seq);
@@ -525,43 +599,34 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
*/
if (tlen % 4)
using_sg = 0;
- if (using_sg) {
- fp = _fc_frame_alloc(lp, 0);
- if (!fp)
- return -ENOMEM;
- } else {
- fp = fc_frame_alloc(lp, tlen);
- if (!fp)
- return -ENOMEM;
+ fp = fc_frame_alloc(lport, using_sg ? 0 : tlen);
+ if (!fp)
+ return -ENOMEM;
- data = (void *)(fr_hdr(fp)) +
- sizeof(struct fc_frame_header);
- }
+ data = fc_frame_header_get(fp) + 1;
fh_parm_offset = frame_offset;
fr_max_payload(fp) = fsp->max_payload;
}
+
+ off = offset + sg->offset;
sg_bytes = min(tlen, sg->length - offset);
+ sg_bytes = min(sg_bytes,
+ (size_t) (PAGE_SIZE - (off & ~PAGE_MASK)));
+ page = sg_page(sg) + (off >> PAGE_SHIFT);
if (using_sg) {
- get_page(sg_page(sg));
+ get_page(page);
skb_fill_page_desc(fp_skb(fp),
skb_shinfo(fp_skb(fp))->nr_frags,
- sg_page(sg), sg->offset + offset,
- sg_bytes);
+ page, off & ~PAGE_MASK, sg_bytes);
fp_skb(fp)->data_len += sg_bytes;
fr_len(fp) += sg_bytes;
fp_skb(fp)->truesize += PAGE_SIZE;
} else {
- size_t off = offset + sg->offset;
-
/*
* The scatterlist item may be bigger than PAGE_SIZE,
* but we must not cross pages inside the kmap.
*/
- sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE -
- (off & ~PAGE_MASK)));
- page_addr = kmap_atomic(sg_page(sg) +
- (off >> PAGE_SHIFT),
- KM_SOFTIRQ0);
+ page_addr = kmap_atomic(page, KM_SOFTIRQ0);
memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),
sg_bytes);
kunmap_atomic(page_addr, KM_SOFTIRQ0);
@@ -572,7 +637,8 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
tlen -= sg_bytes;
remaining -= sg_bytes;
- if (tlen)
+ if ((skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN) &&
+ (tlen))
continue;
/*
@@ -589,7 +655,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
/*
* send fragment using for a sequence.
*/
- error = lp->tt.seq_send(lp, seq, fp);
+ error = lport->tt.seq_send(lport, seq, fp);
if (error) {
WARN_ON(1); /* send error should be rare */
fc_fcp_retry_cmd(fsp);
@@ -601,6 +667,11 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
return 0;
}
+/**
+ * fc_fcp_abts_resp() - Send an ABTS response
+ * @fsp: The FCP packet that is being aborted
+ * @fp: The response frame
+ */
static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
int ba_done = 1;
@@ -637,46 +708,13 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
}
/**
- * fc_fcp_reduce_can_queue() - drop can_queue
- * @lp: lport to drop queueing for
- *
- * If we are getting memory allocation failures, then we may
- * be trying to execute too many commands. We let the running
- * commands complete or timeout, then try again with a reduced
- * can_queue. Eventually we will hit the point where we run
- * on all reserved structs.
- */
-static void fc_fcp_reduce_can_queue(struct fc_lport *lp)
-{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
- unsigned long flags;
- int can_queue;
-
- spin_lock_irqsave(lp->host->host_lock, flags);
- if (si->throttled)
- goto done;
- si->throttled = 1;
-
- can_queue = lp->host->can_queue;
- can_queue >>= 1;
- if (!can_queue)
- can_queue = 1;
- lp->host->can_queue = can_queue;
- shost_printk(KERN_ERR, lp->host, "libfc: Could not allocate frame.\n"
- "Reducing can_queue to %d.\n", can_queue);
-done:
- spin_unlock_irqrestore(lp->host->host_lock, flags);
-}
-
-/**
- * fc_fcp_recv() - Reveive FCP frames
+ * fc_fcp_recv() - Reveive an FCP frame
* @seq: The sequence the frame is on
- * @fp: The FC frame
+ * @fp: The received frame
* @arg: The related FCP packet
*
- * Return : None
- * Context : called from Soft IRQ context
- * can not called holding list lock
+ * Context: Called from Soft IRQ context. Can not be called
+ * holding the FCP packet list lock.
*/
static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -687,8 +725,10 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
u8 r_ctl;
int rc = 0;
- if (IS_ERR(fp))
- goto errout;
+ if (IS_ERR(fp)) {
+ fc_fcp_error(fsp, fp);
+ return;
+ }
fh = fc_frame_header_get(fp);
r_ctl = fh->fh_r_ctl;
@@ -721,8 +761,6 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
(size_t) ntohl(dd->ft_burst_len));
if (!rc)
seq->rec_data = fsp->xfer_len;
- else if (rc == -ENOMEM)
- fsp->state |= FC_SRB_NOMEM;
} else if (r_ctl == FC_RCTL_DD_SOL_DATA) {
/*
* received a DATA frame
@@ -742,13 +780,13 @@ unlock:
fc_fcp_unlock_pkt(fsp);
out:
fc_frame_free(fp);
-errout:
- if (IS_ERR(fp))
- fc_fcp_error(fsp, fp);
- else if (rc == -ENOMEM)
- fc_fcp_reduce_can_queue(lport);
}
+/**
+ * fc_fcp_resp() - Handler for FCP responses
+ * @fsp: The FCP packet the response is for
+ * @fp: The response frame
+ */
static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
struct fc_frame_header *fh;
@@ -862,15 +900,16 @@ err:
}
/**
- * fc_fcp_complete_locked() - complete processing of a fcp packet
- * @fsp: fcp packet
+ * fc_fcp_complete_locked() - Complete processing of a fcp_pkt with the
+ * fcp_pkt lock held
+ * @fsp: The FCP packet to be completed
*
* This function may sleep if a timer is pending. The packet lock must be
* held, and the host lock must not be held.
*/
static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
{
- struct fc_lport *lp = fsp->lp;
+ struct fc_lport *lport = fsp->lp;
struct fc_seq *seq;
struct fc_exch *ep;
u32 f_ctl;
@@ -901,8 +940,8 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
struct fc_frame *conf_frame;
struct fc_seq *csp;
- csp = lp->tt.seq_start_next(seq);
- conf_frame = fc_frame_alloc(fsp->lp, 0);
+ csp = lport->tt.seq_start_next(seq);
+ conf_frame = fc_fcp_frame_alloc(fsp->lp, 0);
if (conf_frame) {
f_ctl = FC_FC_SEQ_INIT;
f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
@@ -910,43 +949,48 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL,
ep->did, ep->sid,
FC_TYPE_FCP, f_ctl, 0);
- lp->tt.seq_send(lp, csp, conf_frame);
+ lport->tt.seq_send(lport, csp, conf_frame);
}
}
- lp->tt.exch_done(seq);
+ lport->tt.exch_done(seq);
}
fc_io_compl(fsp);
}
+/**
+ * fc_fcp_cleanup_cmd() - Cancel the active exchange on a fcp_pkt
+ * @fsp: The FCP packet whose exchanges should be canceled
+ * @error: The reason for the cancellation
+ */
static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error)
{
- struct fc_lport *lp = fsp->lp;
+ struct fc_lport *lport = fsp->lp;
if (fsp->seq_ptr) {
- lp->tt.exch_done(fsp->seq_ptr);
+ lport->tt.exch_done(fsp->seq_ptr);
fsp->seq_ptr = NULL;
}
fsp->status_code = error;
}
/**
- * fc_fcp_cleanup_each_cmd() - Cleanup active commads
- * @lp: logical port
- * @id: target id
- * @lun: lun
- * @error: fsp status code
+ * fc_fcp_cleanup_each_cmd() - Cancel all exchanges on a local port
+ * @lport: The local port whose exchanges should be canceled
+ * @id: The target's ID
+ * @lun: The LUN
+ * @error: The reason for cancellation
*
* If lun or id is -1, they are ignored.
*/
-static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id,
+static void fc_fcp_cleanup_each_cmd(struct fc_lport *lport, unsigned int id,
unsigned int lun, int error)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
struct fc_fcp_pkt *fsp;
struct scsi_cmnd *sc_cmd;
unsigned long flags;
- spin_lock_irqsave(lp->host->host_lock, flags);
+ spin_lock_irqsave(lport->host->host_lock, flags);
restart:
list_for_each_entry(fsp, &si->scsi_pkt_queue, list) {
sc_cmd = fsp->cmd;
@@ -957,7 +1001,7 @@ restart:
continue;
fc_fcp_pkt_hold(fsp);
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
if (!fc_fcp_lock_pkt(fsp)) {
fc_fcp_cleanup_cmd(fsp, error);
@@ -966,35 +1010,36 @@ restart:
}
fc_fcp_pkt_release(fsp);
- spin_lock_irqsave(lp->host->host_lock, flags);
+ spin_lock_irqsave(lport->host->host_lock, flags);
/*
* while we dropped the lock multiple pkts could
* have been released, so we have to start over.
*/
goto restart;
}
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
}
-static void fc_fcp_abort_io(struct fc_lport *lp)
+/**
+ * fc_fcp_abort_io() - Abort all FCP-SCSI exchanges on a local port
+ * @lport: The local port whose exchanges are to be aborted
+ */
+static void fc_fcp_abort_io(struct fc_lport *lport)
{
- fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR);
+ fc_fcp_cleanup_each_cmd(lport, -1, -1, FC_HRD_ERROR);
}
/**
- * fc_fcp_pkt_send() - send a fcp packet to the lower level.
- * @lp: fc lport
- * @fsp: fc packet.
+ * fc_fcp_pkt_send() - Send a fcp_pkt
+ * @lport: The local port to send the FCP packet on
+ * @fsp: The FCP packet to send
*
- * This is called by upper layer protocol.
- * Return : zero for success and -1 for failure
- * Context : called from queuecommand which can be called from process
- * or scsi soft irq.
- * Locks : called with the host lock and irqs disabled.
+ * Return: Zero for success and -1 for failure
+ * Locks: Called with the host lock and irqs disabled.
*/
-static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
int rc;
fsp->cmd->SCp.ptr = (char *)fsp;
@@ -1006,16 +1051,22 @@ static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
list_add_tail(&fsp->list, &si->scsi_pkt_queue);
- spin_unlock_irq(lp->host->host_lock);
- rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv);
- spin_lock_irq(lp->host->host_lock);
+ spin_unlock_irq(lport->host->host_lock);
+ rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv);
+ spin_lock_irq(lport->host->host_lock);
if (rc)
list_del(&fsp->list);
return rc;
}
-static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+/**
+ * fc_fcp_cmd_send() - Send a FCP command
+ * @lport: The local port to send the command on
+ * @fsp: The FCP packet the command is on
+ * @resp: The handler for the response
+ */
+static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
void (*resp)(struct fc_seq *,
struct fc_frame *fp,
void *arg))
@@ -1023,14 +1074,14 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
struct fc_frame *fp;
struct fc_seq *seq;
struct fc_rport *rport;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
const size_t len = sizeof(fsp->cdb_cmd);
int rc = 0;
if (fc_fcp_lock_pkt(fsp))
return 0;
- fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd));
+ fp = fc_fcp_frame_alloc(lport, sizeof(fsp->cdb_cmd));
if (!fp) {
rc = -1;
goto unlock;
@@ -1040,15 +1091,15 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
fr_fsp(fp) = fsp;
rport = fsp->rport;
fsp->max_payload = rport->maxframe_size;
- rp = rport->dd_data;
+ rpriv = rport->dd_data;
fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id,
- fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+ fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
+ seq = lport->tt.exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy,
+ fsp, 0);
if (!seq) {
- fc_frame_free(fp);
rc = -1;
goto unlock;
}
@@ -1065,8 +1116,10 @@ unlock:
return rc;
}
-/*
- * transport error handler
+/**
+ * fc_fcp_error() - Handler for FCP layer errors
+ * @fsp: The FCP packet the error is on
+ * @fp: The frame that has errored
*/
static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
@@ -1091,11 +1144,13 @@ unlock:
fc_fcp_unlock_pkt(fsp);
}
-/*
- * Scsi abort handler- calls to send an abort
- * and then wait for abort completion
+/**
+ * fc_fcp_pkt_abort() - Abort a fcp_pkt
+ * @fsp: The FCP packet to abort on
+ *
+ * Called to send an abort and then wait for abort completion
*/
-static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
{
int rc = FAILED;
@@ -1122,14 +1177,15 @@ static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
return rc;
}
-/*
- * Retry LUN reset after resource allocation failed.
+/**
+ * fc_lun_reset_send() - Send LUN reset command
+ * @data: The FCP packet that identifies the LUN to be reset
*/
static void fc_lun_reset_send(unsigned long data)
{
struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
- struct fc_lport *lp = fsp->lp;
- if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) {
+ struct fc_lport *lport = fsp->lp;
+ if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) {
if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
return;
if (fc_fcp_lock_pkt(fsp))
@@ -1140,11 +1196,15 @@ static void fc_lun_reset_send(unsigned long data)
}
}
-/*
- * Scsi device reset handler- send a LUN RESET to the device
- * and wait for reset reply
+/**
+ * fc_lun_reset() - Send a LUN RESET command to a device
+ * and wait for the reply
+ * @lport: The local port to sent the comand on
+ * @fsp: The FCP packet that identifies the LUN to be reset
+ * @id: The SCSI command ID
+ * @lun: The LUN ID to be reset
*/
-static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
unsigned int id, unsigned int lun)
{
int rc;
@@ -1172,14 +1232,14 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
spin_lock_bh(&fsp->scsi_pkt_lock);
if (fsp->seq_ptr) {
- lp->tt.exch_done(fsp->seq_ptr);
+ lport->tt.exch_done(fsp->seq_ptr);
fsp->seq_ptr = NULL;
}
fsp->wait_for_comp = 0;
spin_unlock_bh(&fsp->scsi_pkt_lock);
if (!rc) {
- FC_SCSI_DBG(lp, "lun reset failed\n");
+ FC_SCSI_DBG(lport, "lun reset failed\n");
return FAILED;
}
@@ -1187,13 +1247,16 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
if (fsp->cdb_status != FCP_TMF_CMPL)
return FAILED;
- FC_SCSI_DBG(lp, "lun reset to lun %u completed\n", lun);
- fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED);
+ FC_SCSI_DBG(lport, "lun reset to lun %u completed\n", lun);
+ fc_fcp_cleanup_each_cmd(lport, id, lun, FC_CMD_ABORTED);
return SUCCESS;
}
-/*
- * Task Managment response handler
+/**
+ * fc_tm_done() - Task Managment response handler
+ * @seq: The sequence that the response is on
+ * @fp: The response frame
+ * @arg: The FCP packet the response is for
*/
static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -1230,34 +1293,31 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
fc_fcp_unlock_pkt(fsp);
}
-static void fc_fcp_cleanup(struct fc_lport *lp)
+/**
+ * fc_fcp_cleanup() - Cleanup all FCP exchanges on a local port
+ * @lport: The local port to be cleaned up
+ */
+static void fc_fcp_cleanup(struct fc_lport *lport)
{
- fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR);
+ fc_fcp_cleanup_each_cmd(lport, -1, -1, FC_ERROR);
}
-/*
- * fc_fcp_timeout: called by OS timer function.
- *
- * The timer has been inactivated and must be reactivated if desired
- * using fc_fcp_timer_set().
- *
- * Algorithm:
- *
- * If REC is supported, just issue it, and return. The REC exchange will
- * complete or time out, and recovery can continue at that point.
- *
- * Otherwise, if the response has been received without all the data,
- * it has been ER_TIMEOUT since the response was received.
+/**
+ * fc_fcp_timeout() - Handler for fcp_pkt timeouts
+ * @data: The FCP packet that has timed out
*
- * If the response has not been received,
- * we see if data was received recently. If it has been, we continue waiting,
- * otherwise, we abort the command.
+ * If REC is supported then just issue it and return. The REC exchange will
+ * complete or time out and recovery can continue at that point. Otherwise,
+ * if the response has been received without all the data it has been
+ * ER_TIMEOUT since the response was received. If the response has not been
+ * received we see if data was received recently. If it has been then we
+ * continue waiting, otherwise, we abort the command.
*/
static void fc_fcp_timeout(unsigned long data)
{
struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
struct fc_rport *rport = fsp->rport;
- struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct fc_rport_libfc_priv *rpriv = rport->dd_data;
if (fc_fcp_lock_pkt(fsp))
return;
@@ -1267,7 +1327,7 @@ static void fc_fcp_timeout(unsigned long data)
fsp->state |= FC_SRB_FCP_PROCESSING_TMO;
- if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED)
+ if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)
fc_fcp_rec(fsp);
else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2),
jiffies))
@@ -1281,39 +1341,40 @@ unlock:
fc_fcp_unlock_pkt(fsp);
}
-/*
- * Send a REC ELS request
+/**
+ * fc_fcp_rec() - Send a REC ELS request
+ * @fsp: The FCP packet to send the REC request on
*/
static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fc_frame *fp;
struct fc_rport *rport;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
- lp = fsp->lp;
+ lport = fsp->lp;
rport = fsp->rport;
- rp = rport->dd_data;
- if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
+ rpriv = rport->dd_data;
+ if (!fsp->seq_ptr || rpriv->rp_state != RPORT_ST_READY) {
fsp->status_code = FC_HRD_ERROR;
fsp->io_status = 0;
fc_fcp_complete_locked(fsp);
return;
}
- fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec));
+ fp = fc_fcp_frame_alloc(lport, sizeof(struct fc_els_rec));
if (!fp)
goto retry;
fr_seq(fp) = fsp->seq_ptr;
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
- fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
+ fc_host_port_id(rpriv->local_port->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- if (lp->tt.elsct_send(lp, rport->port_id, fp, ELS_REC, fc_fcp_rec_resp,
- fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
+ if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC,
+ fc_fcp_rec_resp, fsp,
+ jiffies_to_msecs(FC_SCSI_REC_TOV))) {
fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */
return;
}
- fc_frame_free(fp);
retry:
if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
@@ -1321,12 +1382,16 @@ retry:
fc_timeout_error(fsp);
}
-/*
- * Receive handler for REC ELS frame
- * if it is a reject then let the scsi layer to handle
- * the timeout. if it is a LS_ACC then if the io was not completed
- * then set the timeout and return otherwise complete the exchange
- * and tell the scsi layer to restart the I/O.
+/**
+ * fc_fcp_rec_resp() - Handler for REC ELS responses
+ * @seq: The sequence the response is on
+ * @fp: The response frame
+ * @arg: The FCP packet the response is on
+ *
+ * If the response is a reject then the scsi layer will handle
+ * the timeout. If the response is a LS_ACC then if the I/O was not completed
+ * set the timeout and return. If the I/O was completed then complete the
+ * exchange and tell the SCSI layer.
*/
static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -1338,7 +1403,7 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
u32 offset;
enum dma_data_direction data_dir;
enum fc_rctl r_ctl;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
if (IS_ERR(fp)) {
fc_fcp_rec_error(fsp, fp);
@@ -1361,13 +1426,13 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
/* fall through */
case ELS_RJT_UNSUP:
FC_FCP_DBG(fsp, "device does not support REC\n");
- rp = fsp->rport->dd_data;
+ rpriv = fsp->rport->dd_data;
/*
* if we do not spport RECs or got some bogus
* reason then resetup timer so we check for
* making progress.
*/
- rp->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
+ rpriv->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
break;
case ELS_RJT_LOGIC:
@@ -1464,8 +1529,10 @@ out:
fc_frame_free(fp);
}
-/*
- * Handle error response or timeout for REC exchange.
+/**
+ * fc_fcp_rec_error() - Handler for REC errors
+ * @fsp: The FCP packet the error is on
+ * @fp: The REC frame
*/
static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
@@ -1504,10 +1571,9 @@ out:
fc_fcp_pkt_release(fsp); /* drop hold for outstanding REC */
}
-/*
- * Time out error routine:
- * abort's the I/O close the exchange and
- * send completion notification to scsi layer
+/**
+ * fc_timeout_error() - Handler for fcp_pkt timeouts
+ * @fsp: The FCP packt that has timed out
*/
static void fc_timeout_error(struct fc_fcp_pkt *fsp)
{
@@ -1521,16 +1587,18 @@ static void fc_timeout_error(struct fc_fcp_pkt *fsp)
fc_fcp_send_abort(fsp);
}
-/*
- * Sequence retransmission request.
+/**
+ * fc_fcp_srr() - Send a SRR request (Sequence Retransmission Request)
+ * @fsp: The FCP packet the SRR is to be sent on
+ * @r_ctl: The R_CTL field for the SRR request
* This is called after receiving status but insufficient data, or
* when expecting status but the request has timed out.
*/
static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
{
- struct fc_lport *lp = fsp->lp;
+ struct fc_lport *lport = fsp->lp;
struct fc_rport *rport;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
struct fc_seq *seq;
struct fcp_srr *srr;
@@ -1538,12 +1606,13 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
u8 cdb_op;
rport = fsp->rport;
- rp = rport->dd_data;
+ rpriv = rport->dd_data;
cdb_op = fsp->cdb_cmd.fc_cdb[0];
- if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY)
+ if (!(rpriv->flags & FC_RP_FLAGS_RETRY) ||
+ rpriv->rp_state != RPORT_ST_READY)
goto retry; /* shouldn't happen */
- fp = fc_frame_alloc(lp, sizeof(*srr));
+ fp = fc_fcp_frame_alloc(lport, sizeof(*srr));
if (!fp)
goto retry;
@@ -1556,15 +1625,14 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
srr->srr_rel_off = htonl(offset);
fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id,
- fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+ fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL,
- fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
- if (!seq) {
- fc_frame_free(fp);
+ seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL,
+ fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
+ if (!seq)
goto retry;
- }
+
fsp->recov_seq = seq;
fsp->xfer_len = offset;
fsp->xfer_contig_end = offset;
@@ -1575,8 +1643,11 @@ retry:
fc_fcp_retry_cmd(fsp);
}
-/*
- * Handle response from SRR.
+/**
+ * fc_fcp_srr_resp() - Handler for SRR response
+ * @seq: The sequence the SRR is on
+ * @fp: The SRR frame
+ * @arg: The FCP packet the SRR is on
*/
static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
@@ -1622,6 +1693,11 @@ out:
fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
}
+/**
+ * fc_fcp_srr_error() - Handler for SRR errors
+ * @fsp: The FCP packet that the SRR error is on
+ * @fp: The SRR frame
+ */
static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
{
if (fc_fcp_lock_pkt(fsp))
@@ -1646,31 +1722,36 @@ out:
fc_fcp_pkt_release(fsp); /* drop hold for outstanding SRR */
}
-static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp)
+/**
+ * fc_fcp_lport_queue_ready() - Determine if the lport and it's queue is ready
+ * @lport: The local port to be checked
+ */
+static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
{
/* lock ? */
- return (lp->state == LPORT_ST_READY) && lp->link_up && !lp->qfull;
+ return (lport->state == LPORT_ST_READY) &&
+ lport->link_up && !lport->qfull;
}
/**
- * fc_queuecommand - The queuecommand function of the scsi template
- * @cmd: struct scsi_cmnd to be executed
- * @done: Callback function to be called when cmd is completed
+ * fc_queuecommand() - The queuecommand function of the SCSI template
+ * @cmd: The scsi_cmnd to be executed
+ * @done: The callback function to be called when the scsi_cmnd is complete
*
- * this is the i/o strategy routine, called by the scsi layer
- * this routine is called with holding the host_lock.
+ * This is the i/o strategy routine, called by the SCSI layer. This routine
+ * is called with the host_lock held.
*/
int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
struct fc_fcp_pkt *fsp;
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
int rval;
int rc = 0;
struct fcoe_dev_stats *stats;
- lp = shost_priv(sc_cmd->device->host);
+ lport = shost_priv(sc_cmd->device->host);
rval = fc_remote_port_chkready(rport);
if (rval) {
@@ -1689,14 +1770,16 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
goto out;
}
- rp = rport->dd_data;
+ rpriv = rport->dd_data;
- if (!fc_fcp_lport_queue_ready(lp)) {
+ if (!fc_fcp_lport_queue_ready(lport)) {
+ if (lport->qfull)
+ fc_fcp_can_queue_ramp_down(lport);
rc = SCSI_MLQUEUE_HOST_BUSY;
goto out;
}
- fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
+ fsp = fc_fcp_pkt_alloc(lport, GFP_ATOMIC);
if (fsp == NULL) {
rc = SCSI_MLQUEUE_HOST_BUSY;
goto out;
@@ -1706,8 +1789,9 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
* build the libfc request pkt
*/
fsp->cmd = sc_cmd; /* save the cmd */
- fsp->lp = lp; /* save the softc ptr */
+ fsp->lp = lport; /* save the softc ptr */
fsp->rport = rport; /* set the remote port ptr */
+ fsp->xfer_ddp = FC_XID_UNKNOWN;
sc_cmd->scsi_done = done;
/*
@@ -1719,7 +1803,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
/*
* setup the data direction
*/
- stats = fc_lport_get_stats(lp);
+ stats = fc_lport_get_stats(lport);
if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
fsp->req_flags = FC_SRB_READ;
stats->InputRequests++;
@@ -1733,7 +1817,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
stats->ControlRequests++;
}
- fsp->tgt_flags = rp->flags;
+ fsp->tgt_flags = rpriv->flags;
init_timer(&fsp->timer);
fsp->timer.data = (unsigned long)fsp;
@@ -1743,7 +1827,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
* if we get -1 return then put the request in the pending
* queue.
*/
- rval = fc_fcp_pkt_send(lp, fsp);
+ rval = fc_fcp_pkt_send(lport, fsp);
if (rval != 0) {
fsp->state = FC_SRB_FREE;
fc_fcp_pkt_release(fsp);
@@ -1755,18 +1839,17 @@ out:
EXPORT_SYMBOL(fc_queuecommand);
/**
- * fc_io_compl() - Handle responses for completed commands
- * @fsp: scsi packet
- *
- * Translates a error to a Linux SCSI error.
+ * fc_io_compl() - Handle responses for completed commands
+ * @fsp: The FCP packet that is complete
*
+ * Translates fcp_pkt errors to a Linux SCSI errors.
* The fcp packet lock must be held when calling.
*/
static void fc_io_compl(struct fc_fcp_pkt *fsp)
{
struct fc_fcp_internal *si;
struct scsi_cmnd *sc_cmd;
- struct fc_lport *lp;
+ struct fc_lport *lport;
unsigned long flags;
/* release outstanding ddp context */
@@ -1779,28 +1862,26 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
spin_lock_bh(&fsp->scsi_pkt_lock);
}
- lp = fsp->lp;
- si = fc_get_scsi_internal(lp);
- spin_lock_irqsave(lp->host->host_lock, flags);
+ lport = fsp->lp;
+ si = fc_get_scsi_internal(lport);
+ spin_lock_irqsave(lport->host->host_lock, flags);
if (!fsp->cmd) {
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
return;
}
/*
- * if a command timed out while we had to try and throttle IO
- * and it is now getting cleaned up, then we are about to
- * try again so clear the throttled flag incase we get more
- * time outs.
+ * if can_queue ramp down is done then try can_queue ramp up
+ * since commands are completing now.
*/
- if (si->throttled && fsp->state & FC_SRB_NOMEM)
- si->throttled = 0;
+ if (si->last_can_queue_ramp_down_time)
+ fc_fcp_can_queue_ramp_up(lport);
sc_cmd = fsp->cmd;
fsp->cmd = NULL;
if (!sc_cmd->SCp.ptr) {
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
return;
}
@@ -1814,21 +1895,6 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
sc_cmd->result = DID_OK << 16;
if (fsp->scsi_resid)
CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
- } else if (fsp->cdb_status == QUEUE_FULL) {
- struct scsi_device *tmp_sdev;
- struct scsi_device *sdev = sc_cmd->device;
-
- shost_for_each_device(tmp_sdev, sdev->host) {
- if (tmp_sdev->id != sdev->id)
- continue;
-
- if (tmp_sdev->queue_depth > 1) {
- scsi_track_queue_full(tmp_sdev,
- tmp_sdev->
- queue_depth - 1);
- }
- }
- sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
} else {
/*
* transport level I/O was ok but scsi
@@ -1846,7 +1912,8 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
* scsi status is good but transport level
* underrun.
*/
- sc_cmd->result = DID_OK << 16;
+ sc_cmd->result = (fsp->state & FC_SRB_RCV_STATUS ?
+ DID_OK : DID_ERROR) << 16;
} else {
/*
* scsi got underrun, this is an error
@@ -1881,60 +1948,42 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
list_del(&fsp->list);
sc_cmd->SCp.ptr = NULL;
sc_cmd->scsi_done(sc_cmd);
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
/* release ref from initial allocation in queue command */
fc_fcp_pkt_release(fsp);
}
/**
- * fc_fcp_complete() - complete processing of a fcp packet
- * @fsp: fcp packet
- *
- * This function may sleep if a fsp timer is pending.
- * The host lock must not be held by caller.
- */
-void fc_fcp_complete(struct fc_fcp_pkt *fsp)
-{
- if (fc_fcp_lock_pkt(fsp))
- return;
-
- fc_fcp_complete_locked(fsp);
- fc_fcp_unlock_pkt(fsp);
-}
-EXPORT_SYMBOL(fc_fcp_complete);
-
-/**
* fc_eh_abort() - Abort a command
- * @sc_cmd: scsi command to abort
+ * @sc_cmd: The SCSI command to abort
*
- * From scsi host template.
- * send ABTS to the target device and wait for the response
- * sc_cmd is the pointer to the command to be aborted.
+ * From SCSI host template.
+ * Send an ABTS to the target device and wait for the response.
*/
int fc_eh_abort(struct scsi_cmnd *sc_cmd)
{
struct fc_fcp_pkt *fsp;
- struct fc_lport *lp;
+ struct fc_lport *lport;
int rc = FAILED;
unsigned long flags;
- lp = shost_priv(sc_cmd->device->host);
- if (lp->state != LPORT_ST_READY)
+ lport = shost_priv(sc_cmd->device->host);
+ if (lport->state != LPORT_ST_READY)
return rc;
- else if (!lp->link_up)
+ else if (!lport->link_up)
return rc;
- spin_lock_irqsave(lp->host->host_lock, flags);
+ spin_lock_irqsave(lport->host->host_lock, flags);
fsp = CMD_SP(sc_cmd);
if (!fsp) {
/* command completed while scsi eh was setting up */
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
return SUCCESS;
}
/* grab a ref so the fsp and sc_cmd cannot be relased from under us */
fc_fcp_pkt_hold(fsp);
- spin_unlock_irqrestore(lp->host->host_lock, flags);
+ spin_unlock_irqrestore(lport->host->host_lock, flags);
if (fc_fcp_lock_pkt(fsp)) {
/* completed while we were waiting for timer to be deleted */
@@ -1942,7 +1991,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
goto release_pkt;
}
- rc = fc_fcp_pkt_abort(lp, fsp);
+ rc = fc_fcp_pkt_abort(fsp);
fc_fcp_unlock_pkt(fsp);
release_pkt:
@@ -1952,37 +2001,34 @@ release_pkt:
EXPORT_SYMBOL(fc_eh_abort);
/**
- * fc_eh_device_reset() Reset a single LUN
- * @sc_cmd: scsi command
+ * fc_eh_device_reset() - Reset a single LUN
+ * @sc_cmd: The SCSI command which identifies the device whose
+ * LUN is to be reset
*
- * Set from scsi host template to send tm cmd to the target and wait for the
- * response.
+ * Set from SCSI host template.
*/
int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
{
- struct fc_lport *lp;
+ struct fc_lport *lport;
struct fc_fcp_pkt *fsp;
struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
int rc = FAILED;
- struct fc_rport_libfc_priv *rp;
int rval;
rval = fc_remote_port_chkready(rport);
if (rval)
goto out;
- rp = rport->dd_data;
- lp = shost_priv(sc_cmd->device->host);
+ lport = shost_priv(sc_cmd->device->host);
- if (lp->state != LPORT_ST_READY)
+ if (lport->state != LPORT_ST_READY)
return rc;
- FC_SCSI_DBG(lp, "Resetting rport (%6x)\n", rport->port_id);
+ FC_SCSI_DBG(lport, "Resetting rport (%6x)\n", rport->port_id);
- fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
+ fsp = fc_fcp_pkt_alloc(lport, GFP_NOIO);
if (fsp == NULL) {
printk(KERN_WARNING "libfc: could not allocate scsi_pkt\n");
- sc_cmd->result = DID_NO_CONNECT << 16;
goto out;
}
@@ -1991,13 +2037,13 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
* the sc passed in is not setup for execution like when sent
* through the queuecommand callout.
*/
- fsp->lp = lp; /* save the softc ptr */
+ fsp->lp = lport; /* save the softc ptr */
fsp->rport = rport; /* set the remote port ptr */
/*
* flush outstanding commands
*/
- rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
+ rc = fc_lun_reset(lport, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
fsp->state = FC_SRB_FREE;
fc_fcp_pkt_release(fsp);
@@ -2007,38 +2053,39 @@ out:
EXPORT_SYMBOL(fc_eh_device_reset);
/**
- * fc_eh_host_reset() - The reset function will reset the ports on the host.
- * @sc_cmd: scsi command
+ * fc_eh_host_reset() - Reset a Scsi_Host.
+ * @sc_cmd: The SCSI command that identifies the SCSI host to be reset
*/
int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
{
struct Scsi_Host *shost = sc_cmd->device->host;
- struct fc_lport *lp = shost_priv(shost);
+ struct fc_lport *lport = shost_priv(shost);
unsigned long wait_tmo;
- FC_SCSI_DBG(lp, "Resetting host\n");
+ FC_SCSI_DBG(lport, "Resetting host\n");
- lp->tt.lport_reset(lp);
+ lport->tt.lport_reset(lport);
wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;
- while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo))
+ while (!fc_fcp_lport_queue_ready(lport) && time_before(jiffies,
+ wait_tmo))
msleep(1000);
- if (fc_fcp_lport_queue_ready(lp)) {
+ if (fc_fcp_lport_queue_ready(lport)) {
shost_printk(KERN_INFO, shost, "libfc: Host reset succeeded "
- "on port (%6x)\n", fc_host_port_id(lp->host));
+ "on port (%6x)\n", fc_host_port_id(lport->host));
return SUCCESS;
} else {
shost_printk(KERN_INFO, shost, "libfc: Host reset failed, "
"port (%6x) is not ready.\n",
- fc_host_port_id(lp->host));
+ fc_host_port_id(lport->host));
return FAILED;
}
}
EXPORT_SYMBOL(fc_eh_host_reset);
/**
- * fc_slave_alloc() - configure queue depth
- * @sdev: scsi device
+ * fc_slave_alloc() - Configure the queue depth of a Scsi_Host
+ * @sdev: The SCSI device that identifies the SCSI host
*
* Configures queue depth based on host's cmd_per_len. If not set
* then we use the libfc default.
@@ -2046,29 +2093,50 @@ EXPORT_SYMBOL(fc_eh_host_reset);
int fc_slave_alloc(struct scsi_device *sdev)
{
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
- int queue_depth;
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
- if (sdev->tagged_supported) {
- if (sdev->host->hostt->cmd_per_lun)
- queue_depth = sdev->host->hostt->cmd_per_lun;
- else
- queue_depth = FC_FCP_DFLT_QUEUE_DEPTH;
- scsi_activate_tcq(sdev, queue_depth);
- }
+ if (sdev->tagged_supported)
+ scsi_activate_tcq(sdev, FC_FCP_DFLT_QUEUE_DEPTH);
+ else
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev),
+ FC_FCP_DFLT_QUEUE_DEPTH);
+
return 0;
}
EXPORT_SYMBOL(fc_slave_alloc);
-int fc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+/**
+ * fc_change_queue_depth() - Change a device's queue depth
+ * @sdev: The SCSI device whose queue depth is to change
+ * @qdepth: The new queue depth
+ * @reason: The resason for the change
+ */
+int fc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
{
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ scsi_track_queue_full(sdev, qdepth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
return sdev->queue_depth;
}
EXPORT_SYMBOL(fc_change_queue_depth);
+/**
+ * fc_change_queue_type() - Change a device's queue type
+ * @sdev: The SCSI device whose queue depth is to change
+ * @tag_type: Identifier for queue type
+ */
int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
{
if (sdev->tagged_supported) {
@@ -2084,38 +2152,69 @@ int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
}
EXPORT_SYMBOL(fc_change_queue_type);
-void fc_fcp_destroy(struct fc_lport *lp)
+/**
+ * fc_fcp_destory() - Tear down the FCP layer for a given local port
+ * @lport: The local port that no longer needs the FCP layer
+ */
+void fc_fcp_destroy(struct fc_lport *lport)
{
- struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+ struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
if (!list_empty(&si->scsi_pkt_queue))
printk(KERN_ERR "libfc: Leaked SCSI packets when destroying "
- "port (%6x)\n", fc_host_port_id(lp->host));
+ "port (%6x)\n", fc_host_port_id(lport->host));
mempool_destroy(si->scsi_pkt_pool);
kfree(si);
- lp->scsi_priv = NULL;
+ lport->scsi_priv = NULL;
}
EXPORT_SYMBOL(fc_fcp_destroy);
-int fc_fcp_init(struct fc_lport *lp)
+int fc_setup_fcp()
+{
+ int rc = 0;
+
+ scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
+ sizeof(struct fc_fcp_pkt),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!scsi_pkt_cachep) {
+ printk(KERN_ERR "libfc: Unable to allocate SRB cache, "
+ "module load failed!");
+ rc = -ENOMEM;
+ }
+
+ return rc;
+}
+
+void fc_destroy_fcp()
+{
+ if (scsi_pkt_cachep)
+ kmem_cache_destroy(scsi_pkt_cachep);
+}
+
+/**
+ * fc_fcp_init() - Initialize the FCP layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
+int fc_fcp_init(struct fc_lport *lport)
{
int rc;
struct fc_fcp_internal *si;
- if (!lp->tt.fcp_cmd_send)
- lp->tt.fcp_cmd_send = fc_fcp_cmd_send;
+ if (!lport->tt.fcp_cmd_send)
+ lport->tt.fcp_cmd_send = fc_fcp_cmd_send;
- if (!lp->tt.fcp_cleanup)
- lp->tt.fcp_cleanup = fc_fcp_cleanup;
+ if (!lport->tt.fcp_cleanup)
+ lport->tt.fcp_cleanup = fc_fcp_cleanup;
- if (!lp->tt.fcp_abort_io)
- lp->tt.fcp_abort_io = fc_fcp_abort_io;
+ if (!lport->tt.fcp_abort_io)
+ lport->tt.fcp_abort_io = fc_fcp_abort_io;
si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL);
if (!si)
return -ENOMEM;
- lp->scsi_priv = si;
+ lport->scsi_priv = si;
+ si->max_can_queue = lport->host->can_queue;
INIT_LIST_HEAD(&si->scsi_pkt_queue);
si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep);
@@ -2130,42 +2229,3 @@ free_internal:
return rc;
}
EXPORT_SYMBOL(fc_fcp_init);
-
-static int __init libfc_init(void)
-{
- int rc;
-
- scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
- sizeof(struct fc_fcp_pkt),
- 0, SLAB_HWCACHE_ALIGN, NULL);
- if (scsi_pkt_cachep == NULL) {
- printk(KERN_ERR "libfc: Unable to allocate SRB cache, "
- "module load failed!");
- return -ENOMEM;
- }
-
- rc = fc_setup_exch_mgr();
- if (rc)
- goto destroy_pkt_cache;
-
- rc = fc_setup_rport();
- if (rc)
- goto destroy_em;
-
- return rc;
-destroy_em:
- fc_destroy_exch_mgr();
-destroy_pkt_cache:
- kmem_cache_destroy(scsi_pkt_cachep);
- return rc;
-}
-
-static void __exit libfc_exit(void)
-{
- kmem_cache_destroy(scsi_pkt_cachep);
- fc_destroy_exch_mgr();
- fc_destroy_rport();
-}
-
-module_init(libfc_init);
-module_exit(libfc_exit);
diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c
index 63fe00cfe66..6da01c61696 100644
--- a/drivers/scsi/libfc/fc_frame.c
+++ b/drivers/scsi/libfc/fc_frame.c
@@ -51,24 +51,24 @@ EXPORT_SYMBOL(fc_frame_crc_check);
* Allocate a frame intended to be sent via fcoe_xmit.
* Get an sk_buff for the frame and set the length.
*/
-struct fc_frame *__fc_frame_alloc(size_t len)
+struct fc_frame *_fc_frame_alloc(size_t len)
{
struct fc_frame *fp;
struct sk_buff *skb;
WARN_ON((len % sizeof(u32)) != 0);
len += sizeof(struct fc_frame_header);
- skb = dev_alloc_skb(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM);
+ skb = alloc_skb_fclone(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM +
+ NET_SKB_PAD, GFP_ATOMIC);
if (!skb)
return NULL;
+ skb_reserve(skb, NET_SKB_PAD + FC_FRAME_HEADROOM);
fp = (struct fc_frame *) skb;
fc_frame_init(fp);
- skb_reserve(skb, FC_FRAME_HEADROOM);
skb_put(skb, len);
return fp;
}
-EXPORT_SYMBOL(__fc_frame_alloc);
-
+EXPORT_SYMBOL(_fc_frame_alloc);
struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
{
@@ -78,7 +78,7 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
fill = payload_len % 4;
if (fill != 0)
fill = 4 - fill;
- fp = __fc_frame_alloc(payload_len + fill);
+ fp = _fc_frame_alloc(payload_len + fill);
if (fp) {
memset((char *) fr_hdr(fp) + payload_len, 0, fill);
/* trim is OK, we just allocated it so there are no fragments */
@@ -87,3 +87,4 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
}
return fp;
}
+EXPORT_SYMBOL(fc_frame_alloc_fill);
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
new file mode 100644
index 00000000000..39f4b6ab04b
--- /dev/null
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright(c) 2009 Intel 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/scatterlist.h>
+#include <linux/crc32.h>
+
+#include <scsi/libfc.h>
+
+#include "fc_libfc.h"
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("libfc");
+MODULE_LICENSE("GPL v2");
+
+unsigned int fc_debug_logging;
+module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
+/**
+ * libfc_init() - Initialize libfc.ko
+ */
+static int __init libfc_init(void)
+{
+ int rc = 0;
+
+ rc = fc_setup_fcp();
+ if (rc)
+ return rc;
+
+ rc = fc_setup_exch_mgr();
+ if (rc)
+ goto destroy_pkt_cache;
+
+ rc = fc_setup_rport();
+ if (rc)
+ goto destroy_em;
+
+ return rc;
+destroy_em:
+ fc_destroy_exch_mgr();
+destroy_pkt_cache:
+ fc_destroy_fcp();
+ return rc;
+}
+module_init(libfc_init);
+
+/**
+ * libfc_exit() - Tear down libfc.ko
+ */
+static void __exit libfc_exit(void)
+{
+ fc_destroy_fcp();
+ fc_destroy_exch_mgr();
+ fc_destroy_rport();
+}
+module_exit(libfc_exit);
+
+/**
+ * fc_copy_buffer_to_sglist() - This routine copies the data of a buffer
+ * into a scatter-gather list (SG list).
+ *
+ * @buf: pointer to the data buffer.
+ * @len: the byte-length of the data buffer.
+ * @sg: pointer to the pointer of the SG list.
+ * @nents: pointer to the remaining number of entries in the SG list.
+ * @offset: pointer to the current offset in the SG list.
+ * @km_type: dedicated page table slot type for kmap_atomic.
+ * @crc: pointer to the 32-bit crc value.
+ * If crc is NULL, CRC is not calculated.
+ */
+u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+ struct scatterlist *sg,
+ u32 *nents, size_t *offset,
+ enum km_type km_type, u32 *crc)
+{
+ size_t remaining = len;
+ u32 copy_len = 0;
+
+ while (remaining > 0 && sg) {
+ size_t off, sg_bytes;
+ void *page_addr;
+
+ if (*offset >= sg->length) {
+ /*
+ * Check for end and drop resources
+ * from the last iteration.
+ */
+ if (!(*nents))
+ break;
+ --(*nents);
+ *offset -= sg->length;
+ sg = sg_next(sg);
+ continue;
+ }
+ sg_bytes = min(remaining, sg->length - *offset);
+
+ /*
+ * The scatterlist item may be bigger than PAGE_SIZE,
+ * but we are limited to mapping PAGE_SIZE at a time.
+ */
+ off = *offset + sg->offset;
+ sg_bytes = min(sg_bytes,
+ (size_t)(PAGE_SIZE - (off & ~PAGE_MASK)));
+ page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
+ km_type);
+ if (crc)
+ *crc = crc32(*crc, buf, sg_bytes);
+ memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes);
+ kunmap_atomic(page_addr, km_type);
+ buf += sg_bytes;
+ *offset += sg_bytes;
+ remaining -= sg_bytes;
+ copy_len += sg_bytes;
+ }
+ return copy_len;
+}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
new file mode 100644
index 00000000000..741fd5c72e1
--- /dev/null
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright(c) 2009 Intel 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_LIBFC_H_
+#define _FC_LIBFC_H_
+
+#define FC_LIBFC_LOGGING 0x01 /* General logging, not categorized */
+#define FC_LPORT_LOGGING 0x02 /* lport layer logging */
+#define FC_DISC_LOGGING 0x04 /* discovery layer logging */
+#define FC_RPORT_LOGGING 0x08 /* rport layer logging */
+#define FC_FCP_LOGGING 0x10 /* I/O path logging */
+#define FC_EM_LOGGING 0x20 /* Exchange Manager logging */
+#define FC_EXCH_LOGGING 0x40 /* Exchange/Sequence logging */
+#define FC_SCSI_LOGGING 0x80 /* SCSI logging (mostly error handling) */
+
+extern unsigned int fc_debug_logging;
+
+#define FC_CHECK_LOGGING(LEVEL, CMD) \
+ do { \
+ if (unlikely(fc_debug_logging & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
+ } while (0)
+
+#define FC_LIBFC_DBG(fmt, args...) \
+ FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \
+ printk(KERN_INFO "libfc: " fmt, ##args))
+
+#define FC_LPORT_DBG(lport, fmt, args...) \
+ FC_CHECK_LOGGING(FC_LPORT_LOGGING, \
+ printk(KERN_INFO "host%u: lport %6x: " fmt, \
+ (lport)->host->host_no, \
+ fc_host_port_id((lport)->host), ##args))
+
+#define FC_DISC_DBG(disc, fmt, args...) \
+ FC_CHECK_LOGGING(FC_DISC_LOGGING, \
+ printk(KERN_INFO "host%u: disc: " fmt, \
+ (disc)->lport->host->host_no, \
+ ##args))
+
+#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \
+ FC_CHECK_LOGGING(FC_RPORT_LOGGING, \
+ printk(KERN_INFO "host%u: rport %6x: " fmt, \
+ (lport)->host->host_no, \
+ (port_id), ##args))
+
+#define FC_RPORT_DBG(rdata, fmt, args...) \
+ FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args)
+
+#define FC_FCP_DBG(pkt, fmt, args...) \
+ FC_CHECK_LOGGING(FC_FCP_LOGGING, \
+ printk(KERN_INFO "host%u: fcp: %6x: " fmt, \
+ (pkt)->lp->host->host_no, \
+ pkt->rport->port_id, ##args))
+
+#define FC_EXCH_DBG(exch, fmt, args...) \
+ FC_CHECK_LOGGING(FC_EXCH_LOGGING, \
+ printk(KERN_INFO "host%u: xid %4x: " fmt, \
+ (exch)->lp->host->host_no, \
+ exch->xid, ##args))
+
+#define FC_SCSI_DBG(lport, fmt, args...) \
+ FC_CHECK_LOGGING(FC_SCSI_LOGGING, \
+ printk(KERN_INFO "host%u: scsi: " fmt, \
+ (lport)->host->host_no, ##args))
+
+/*
+ * Set up direct-data placement for this I/O request
+ */
+void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
+
+/*
+ * Module setup functions
+ */
+int fc_setup_exch_mgr(void);
+void fc_destroy_exch_mgr(void);
+int fc_setup_rport(void);
+void fc_destroy_rport(void);
+int fc_setup_fcp(void);
+void fc_destroy_fcp(void);
+
+/*
+ * Internal libfc functions
+ */
+const char *fc_els_resp_type(struct fc_frame *);
+
+/*
+ * Copies a buffer into an sg list
+ */
+u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+ struct scatterlist *sg,
+ u32 *nents, size_t *offset,
+ enum km_type km_type, u32 *crc);
+
+#endif /* _FC_LIBFC_H_ */
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index bd2f7719744..74338c83ad0 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -56,7 +56,7 @@
* at the same time.
*
* When discovery succeeds or fails a callback is made to the lport as
- * notification. Currently, succesful discovery causes the lport to take no
+ * notification. Currently, successful discovery causes the lport to take no
* action. A failure will cause the lport to reset. There is likely a circular
* locking problem with this implementation.
*/
@@ -94,6 +94,9 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+#include <linux/scatterlist.h>
+
+#include "fc_libfc.h"
/* Fabric IDs to use for point-to-point mode, chosen on whims. */
#define FC_LOCAL_PTP_FID_LO 0x010101
@@ -106,8 +109,7 @@ static void fc_lport_error(struct fc_lport *, struct fc_frame *);
static void fc_lport_enter_reset(struct fc_lport *);
static void fc_lport_enter_flogi(struct fc_lport *);
static void fc_lport_enter_dns(struct fc_lport *);
-static void fc_lport_enter_rpn_id(struct fc_lport *);
-static void fc_lport_enter_rft_id(struct fc_lport *);
+static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state);
static void fc_lport_enter_scr(struct fc_lport *);
static void fc_lport_enter_ready(struct fc_lport *);
static void fc_lport_enter_logo(struct fc_lport *);
@@ -116,14 +118,40 @@ static const char *fc_lport_state_names[] = {
[LPORT_ST_DISABLED] = "disabled",
[LPORT_ST_FLOGI] = "FLOGI",
[LPORT_ST_DNS] = "dNS",
- [LPORT_ST_RPN_ID] = "RPN_ID",
+ [LPORT_ST_RNN_ID] = "RNN_ID",
+ [LPORT_ST_RSNN_NN] = "RSNN_NN",
+ [LPORT_ST_RSPN_ID] = "RSPN_ID",
[LPORT_ST_RFT_ID] = "RFT_ID",
+ [LPORT_ST_RFF_ID] = "RFF_ID",
[LPORT_ST_SCR] = "SCR",
[LPORT_ST_READY] = "Ready",
[LPORT_ST_LOGO] = "LOGO",
[LPORT_ST_RESET] = "reset",
};
+/**
+ * struct fc_bsg_info - FC Passthrough managemet structure
+ * @job: The passthrough job
+ * @lport: The local port to pass through a command
+ * @rsp_code: The expected response code
+ * @sg: job->reply_payload.sg_list
+ * @nents: job->reply_payload.sg_cnt
+ * @offset: The offset into the response data
+ */
+struct fc_bsg_info {
+ struct fc_bsg_job *job;
+ struct fc_lport *lport;
+ u16 rsp_code;
+ struct scatterlist *sg;
+ u32 nents;
+ size_t offset;
+};
+
+/**
+ * fc_frame_drop() - Dummy frame handler
+ * @lport: The local port the frame was received on
+ * @fp: The received frame
+ */
static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
{
fc_frame_free(fp);
@@ -150,8 +178,8 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
switch (event) {
case RPORT_EV_READY:
if (lport->state == LPORT_ST_DNS) {
- lport->dns_rp = rdata;
- fc_lport_enter_rpn_id(lport);
+ lport->dns_rdata = rdata;
+ fc_lport_enter_ns(lport, LPORT_ST_RNN_ID);
} else {
FC_LPORT_DBG(lport, "Received an READY event "
"on port (%6x) for the directory "
@@ -165,7 +193,7 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
- lport->dns_rp = NULL;
+ lport->dns_rdata = NULL;
break;
case RPORT_EV_NONE:
break;
@@ -189,8 +217,8 @@ static const char *fc_lport_state(struct fc_lport *lport)
/**
* fc_lport_ptp_setup() - Create an rport for point-to-point mode
- * @lport: The lport to attach the ptp rport to
- * @fid: The FID of the ptp rport
+ * @lport: The lport to attach the ptp rport to
+ * @remote_fid: The FID of the ptp rport
* @remote_wwpn: The WWPN of the ptp rport
* @remote_wwnn: The WWNN of the ptp rport
*/
@@ -199,18 +227,22 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
u64 remote_wwnn)
{
mutex_lock(&lport->disc.disc_mutex);
- if (lport->ptp_rp)
- lport->tt.rport_logoff(lport->ptp_rp);
- lport->ptp_rp = lport->tt.rport_create(lport, remote_fid);
- lport->ptp_rp->ids.port_name = remote_wwpn;
- lport->ptp_rp->ids.node_name = remote_wwnn;
+ if (lport->ptp_rdata)
+ lport->tt.rport_logoff(lport->ptp_rdata);
+ lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
+ lport->ptp_rdata->ids.port_name = remote_wwpn;
+ lport->ptp_rdata->ids.node_name = remote_wwnn;
mutex_unlock(&lport->disc.disc_mutex);
- lport->tt.rport_login(lport->ptp_rp);
+ lport->tt.rport_login(lport->ptp_rdata);
fc_lport_enter_ready(lport);
}
+/**
+ * fc_get_host_port_type() - Return the port type of the given Scsi_Host
+ * @shost: The SCSI host whose port type is to be determined
+ */
void fc_get_host_port_type(struct Scsi_Host *shost)
{
/* TODO - currently just NPORT */
@@ -218,17 +250,33 @@ void fc_get_host_port_type(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(fc_get_host_port_type);
+/**
+ * fc_get_host_port_state() - Return the port state of the given Scsi_Host
+ * @shost: The SCSI host whose port state is to be determined
+ */
void fc_get_host_port_state(struct Scsi_Host *shost)
{
- struct fc_lport *lp = shost_priv(shost);
+ struct fc_lport *lport = shost_priv(shost);
- if (lp->link_up)
- fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ mutex_lock(&lport->lp_mutex);
+ if (!lport->link_up)
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
else
- fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ switch (lport->state) {
+ case LPORT_ST_READY:
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ break;
+ default:
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ }
+ mutex_unlock(&lport->lp_mutex);
}
EXPORT_SYMBOL(fc_get_host_port_state);
+/**
+ * fc_get_host_speed() - Return the speed of the given Scsi_Host
+ * @shost: The SCSI host whose port speed is to be determined
+ */
void fc_get_host_speed(struct Scsi_Host *shost)
{
struct fc_lport *lport = shost_priv(shost);
@@ -237,24 +285,28 @@ void fc_get_host_speed(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(fc_get_host_speed);
+/**
+ * fc_get_host_stats() - Return the Scsi_Host's statistics
+ * @shost: The SCSI host whose statistics are to be returned
+ */
struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
{
struct fc_host_statistics *fcoe_stats;
- struct fc_lport *lp = shost_priv(shost);
+ struct fc_lport *lport = shost_priv(shost);
struct timespec v0, v1;
unsigned int cpu;
- fcoe_stats = &lp->host_stats;
+ fcoe_stats = &lport->host_stats;
memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
jiffies_to_timespec(jiffies, &v0);
- jiffies_to_timespec(lp->boot_time, &v1);
+ jiffies_to_timespec(lport->boot_time, &v1);
fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
for_each_possible_cpu(cpu) {
struct fcoe_dev_stats *stats;
- stats = per_cpu_ptr(lp->dev_stats, cpu);
+ stats = per_cpu_ptr(lport->dev_stats, cpu);
fcoe_stats->tx_frames += stats->TxFrames;
fcoe_stats->tx_words += stats->TxWords;
@@ -279,12 +331,15 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
}
EXPORT_SYMBOL(fc_get_host_stats);
-/*
- * Fill in FLOGI command for request.
+/**
+ * fc_lport_flogi_fill() - Fill in FLOGI command for request
+ * @lport: The local port the FLOGI is for
+ * @flogi: The FLOGI command
+ * @op: The opcode
*/
-static void
-fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
- unsigned int op)
+static void fc_lport_flogi_fill(struct fc_lport *lport,
+ struct fc_els_flogi *flogi,
+ unsigned int op)
{
struct fc_els_csp *sp;
struct fc_els_cssp *cp;
@@ -312,8 +367,10 @@ fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
}
}
-/*
- * Add a supported FC-4 type.
+/**
+ * fc_lport_add_fc4_type() - Add a supported FC-4 type to a local port
+ * @lport: The local port to add a new FC-4 type to
+ * @type: The new FC-4 type
*/
static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
{
@@ -325,11 +382,11 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
/**
* fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report.
+ * @sp: The sequence in the RLIR exchange
+ * @fp: The RLIR request frame
* @lport: Fibre Channel local port recieving the RLIR
- * @sp: current sequence in the RLIR exchange
- * @fp: RLIR request frame
*
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
@@ -344,11 +401,11 @@ static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
/**
* fc_lport_recv_echo_req() - Handle received ECHO request
- * @lport: Fibre Channel local port recieving the ECHO
- * @sp: current sequence in the ECHO exchange
- * @fp: ECHO request frame
+ * @sp: The sequence in the ECHO exchange
+ * @fp: ECHO request frame
+ * @lport: The local port recieving the ECHO
*
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
@@ -361,7 +418,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
void *dp;
u32 f_ctl;
- FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n",
+ FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n",
fc_lport_state(lport));
len = fr_len(in_fp) - sizeof(struct fc_frame_header);
@@ -374,7 +431,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
if (fp) {
dp = fc_frame_payload_get(fp, len);
memcpy(dp, pp, len);
- *((u32 *)dp) = htonl(ELS_LS_ACC << 24);
+ *((__be32 *)dp) = htonl(ELS_LS_ACC << 24);
sp = lport->tt.seq_start_next(sp);
f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
@@ -385,12 +442,12 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
}
/**
- * fc_lport_recv_echo_req() - Handle received Request Node ID data request
- * @lport: Fibre Channel local port recieving the RNID
- * @sp: current sequence in the RNID exchange
- * @fp: RNID request frame
+ * fc_lport_recv_rnid_req() - Handle received Request Node ID data request
+ * @sp: The sequence in the RNID exchange
+ * @fp: The RNID request frame
+ * @lport: The local port recieving the RNID
*
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
@@ -453,9 +510,9 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
/**
* fc_lport_recv_logo_req() - Handle received fabric LOGO request
- * @lport: Fibre Channel local port recieving the LOGO
- * @sp: current sequence in the LOGO exchange
- * @fp: LOGO request frame
+ * @sp: The sequence in the LOGO exchange
+ * @fp: The LOGO request frame
+ * @lport: The local port recieving the LOGO
*
* Locking Note: The lport lock is exected to be held before calling
* this function.
@@ -470,7 +527,7 @@ static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
/**
* fc_fabric_login() - Start the lport state machine
- * @lport: The lport that should log into the fabric
+ * @lport: The local port that should log into the fabric
*
* Locking Note: This function should not be called
* with the lport lock held.
@@ -491,47 +548,69 @@ int fc_fabric_login(struct fc_lport *lport)
EXPORT_SYMBOL(fc_fabric_login);
/**
- * fc_linkup() - Handler for transport linkup events
+ * __fc_linkup() - Handler for transport linkup events
* @lport: The lport whose link is up
+ *
+ * Locking: must be called with the lp_mutex held
*/
-void fc_linkup(struct fc_lport *lport)
+void __fc_linkup(struct fc_lport *lport)
{
- printk(KERN_INFO "libfc: Link up on port (%6x)\n",
- fc_host_port_id(lport->host));
-
- mutex_lock(&lport->lp_mutex);
if (!lport->link_up) {
lport->link_up = 1;
if (lport->state == LPORT_ST_RESET)
fc_lport_enter_flogi(lport);
}
+}
+
+/**
+ * fc_linkup() - Handler for transport linkup events
+ * @lport: The local port whose link is up
+ */
+void fc_linkup(struct fc_lport *lport)
+{
+ printk(KERN_INFO "host%d: libfc: Link up on port (%6x)\n",
+ lport->host->host_no, fc_host_port_id(lport->host));
+
+ mutex_lock(&lport->lp_mutex);
+ __fc_linkup(lport);
mutex_unlock(&lport->lp_mutex);
}
EXPORT_SYMBOL(fc_linkup);
/**
- * fc_linkdown() - Handler for transport linkdown events
+ * __fc_linkdown() - Handler for transport linkdown events
* @lport: The lport whose link is down
+ *
+ * Locking: must be called with the lp_mutex held
*/
-void fc_linkdown(struct fc_lport *lport)
+void __fc_linkdown(struct fc_lport *lport)
{
- mutex_lock(&lport->lp_mutex);
- printk(KERN_INFO "libfc: Link down on port (%6x)\n",
- fc_host_port_id(lport->host));
-
if (lport->link_up) {
lport->link_up = 0;
fc_lport_enter_reset(lport);
lport->tt.fcp_cleanup(lport);
}
+}
+
+/**
+ * fc_linkdown() - Handler for transport linkdown events
+ * @lport: The local port whose link is down
+ */
+void fc_linkdown(struct fc_lport *lport)
+{
+ printk(KERN_INFO "host%d: libfc: Link down on port (%6x)\n",
+ lport->host->host_no, fc_host_port_id(lport->host));
+
+ mutex_lock(&lport->lp_mutex);
+ __fc_linkdown(lport);
mutex_unlock(&lport->lp_mutex);
}
EXPORT_SYMBOL(fc_linkdown);
/**
* fc_fabric_logoff() - Logout of the fabric
- * @lport: fc_lport pointer to logoff the fabric
+ * @lport: The local port to logoff the fabric
*
* Return value:
* 0 for success, -1 for failure
@@ -540,8 +619,8 @@ int fc_fabric_logoff(struct fc_lport *lport)
{
lport->tt.disc_stop_final(lport);
mutex_lock(&lport->lp_mutex);
- if (lport->dns_rp)
- lport->tt.rport_logoff(lport->dns_rp);
+ if (lport->dns_rdata)
+ lport->tt.rport_logoff(lport->dns_rdata);
mutex_unlock(&lport->lp_mutex);
lport->tt.rport_flush_queue();
mutex_lock(&lport->lp_mutex);
@@ -553,11 +632,9 @@ int fc_fabric_logoff(struct fc_lport *lport)
EXPORT_SYMBOL(fc_fabric_logoff);
/**
- * fc_lport_destroy() - unregister a fc_lport
- * @lport: fc_lport pointer to unregister
+ * fc_lport_destroy() - Unregister a fc_lport
+ * @lport: The local port to unregister
*
- * Return value:
- * None
* Note:
* exit routine for fc_lport instance
* clean-up all the allocated memory
@@ -580,13 +657,9 @@ int fc_lport_destroy(struct fc_lport *lport)
EXPORT_SYMBOL(fc_lport_destroy);
/**
- * fc_set_mfs() - sets up the mfs for the corresponding fc_lport
- * @lport: fc_lport pointer to unregister
- * @mfs: the new mfs for fc_lport
- *
- * Set mfs for the given fc_lport to the new mfs.
- *
- * Return: 0 for success
+ * fc_set_mfs() - Set the maximum frame size for a local port
+ * @lport: The local port to set the MFS for
+ * @mfs: The new MFS
*/
int fc_set_mfs(struct fc_lport *lport, u32 mfs)
{
@@ -617,7 +690,7 @@ EXPORT_SYMBOL(fc_set_mfs);
/**
* fc_lport_disc_callback() - Callback for discovery events
- * @lport: FC local port
+ * @lport: The local port receiving the event
* @event: The discovery event
*/
void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
@@ -627,8 +700,9 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
FC_LPORT_DBG(lport, "Discovery succeeded\n");
break;
case DISC_EV_FAILED:
- printk(KERN_ERR "libfc: Discovery failed for port (%6x)\n",
- fc_host_port_id(lport->host));
+ printk(KERN_ERR "host%d: libfc: "
+ "Discovery failed for port (%6x)\n",
+ lport->host->host_no, fc_host_port_id(lport->host));
mutex_lock(&lport->lp_mutex);
fc_lport_enter_reset(lport);
mutex_unlock(&lport->lp_mutex);
@@ -641,7 +715,7 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
/**
* fc_rport_enter_ready() - Enter the ready state and start discovery
- * @lport: Fibre Channel local port that is ready
+ * @lport: The local port that is ready
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -652,22 +726,46 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_READY);
+ if (lport->vport)
+ fc_vport_set_state(lport->vport, FC_VPORT_ACTIVE);
+ fc_vports_linkchange(lport);
- if (!lport->ptp_rp)
+ if (!lport->ptp_rdata)
lport->tt.disc_start(fc_lport_disc_callback, lport);
}
/**
+ * fc_lport_set_port_id() - set the local port Port ID
+ * @lport: The local port which will have its Port ID set.
+ * @port_id: The new port ID.
+ * @fp: The frame containing the incoming request, or NULL.
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
+ struct fc_frame *fp)
+{
+ if (port_id)
+ printk(KERN_INFO "host%d: Assigned Port ID %6x\n",
+ lport->host->host_no, port_id);
+
+ fc_host_port_id(lport->host) = port_id;
+ if (lport->tt.lport_set_port_id)
+ lport->tt.lport_set_port_id(lport, port_id, fp);
+}
+
+/**
* fc_lport_recv_flogi_req() - Receive a FLOGI request
* @sp_in: The sequence the FLOGI is on
- * @rx_fp: The frame the FLOGI is in
- * @lport: The lport that recieved the request
+ * @rx_fp: The FLOGI frame
+ * @lport: The local port that recieved the request
*
* A received FLOGI request indicates a point-to-point connection.
* Accept it with the common service parameters indicating our N port.
* Set up to do a PLOGI if we have the higher-number WWPN.
*
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
@@ -695,8 +793,9 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
goto out;
remote_wwpn = get_unaligned_be64(&flp->fl_wwpn);
if (remote_wwpn == lport->wwpn) {
- printk(KERN_WARNING "libfc: Received FLOGI from port "
- "with same WWPN %llx\n", remote_wwpn);
+ printk(KERN_WARNING "host%d: libfc: Received FLOGI from port "
+ "with same WWPN %llx\n",
+ lport->host->host_no, remote_wwpn);
goto out;
}
FC_LPORT_DBG(lport, "FLOGI from port WWPN %llx\n", remote_wwpn);
@@ -715,7 +814,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
remote_fid = FC_LOCAL_PTP_FID_HI;
}
- fc_host_port_id(lport->host) = local_fid;
+ fc_lport_set_port_id(lport, local_fid, rx_fp);
fp = fc_frame_alloc(lport, sizeof(*flp));
if (fp) {
@@ -747,9 +846,9 @@ out:
/**
* fc_lport_recv_req() - The generic lport request handler
- * @lport: The lport that received the request
- * @sp: The sequence the request is on
- * @fp: The frame the request is in
+ * @lport: The local port that received the request
+ * @sp: The sequence the request is on
+ * @fp: The request frame
*
* This function will see if the lport handles the request or
* if an rport should handle the request.
@@ -817,8 +916,8 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
}
/**
- * fc_lport_reset() - Reset an lport
- * @lport: The lport which should be reset
+ * fc_lport_reset() - Reset a local port
+ * @lport: The local port which should be reset
*
* Locking Note: This functions should not be called with the
* lport lock held.
@@ -834,29 +933,31 @@ int fc_lport_reset(struct fc_lport *lport)
EXPORT_SYMBOL(fc_lport_reset);
/**
- * fc_lport_reset_locked() - Reset the local port
- * @lport: Fibre Channel local port to be reset
+ * fc_lport_reset_locked() - Reset the local port w/ the lport lock held
+ * @lport: The local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/
static void fc_lport_reset_locked(struct fc_lport *lport)
{
- if (lport->dns_rp)
- lport->tt.rport_logoff(lport->dns_rp);
+ if (lport->dns_rdata)
+ lport->tt.rport_logoff(lport->dns_rdata);
- lport->ptp_rp = NULL;
+ lport->ptp_rdata = NULL;
lport->tt.disc_stop(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
fc_host_fabric_name(lport->host) = 0;
- fc_host_port_id(lport->host) = 0;
+
+ if (fc_host_port_id(lport->host))
+ fc_lport_set_port_id(lport, 0, NULL);
}
/**
* fc_lport_enter_reset() - Reset the local port
- * @lport: Fibre Channel local port to be reset
+ * @lport: The local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -866,15 +967,22 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
fc_lport_state(lport));
+ if (lport->vport) {
+ if (lport->link_up)
+ fc_vport_set_state(lport->vport, FC_VPORT_INITIALIZING);
+ else
+ fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
+ }
fc_lport_state_enter(lport, LPORT_ST_RESET);
+ fc_vports_linkchange(lport);
fc_lport_reset_locked(lport);
if (lport->link_up)
fc_lport_enter_flogi(lport);
}
/**
- * fc_lport_enter_disabled() - disable the local port
- * @lport: Fibre Channel local port to be reset
+ * fc_lport_enter_disabled() - Disable the local port
+ * @lport: The local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -885,13 +993,14 @@ static void fc_lport_enter_disabled(struct fc_lport *lport)
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_DISABLED);
+ fc_vports_linkchange(lport);
fc_lport_reset_locked(lport);
}
/**
* fc_lport_error() - Handler for any errors
- * @lport: The fc_lport object
- * @fp: The frame pointer
+ * @lport: The local port that the error was on
+ * @fp: The error code encoded in a frame pointer
*
* If the error was caused by a resource allocation failure
* then wait for half a second and retry, otherwise retry
@@ -922,8 +1031,11 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
case LPORT_ST_DISABLED:
case LPORT_ST_READY:
case LPORT_ST_RESET:
- case LPORT_ST_RPN_ID:
+ case LPORT_ST_RNN_ID:
+ case LPORT_ST_RSNN_NN:
+ case LPORT_ST_RSPN_ID:
case LPORT_ST_RFT_ID:
+ case LPORT_ST_RFF_ID:
case LPORT_ST_SCR:
case LPORT_ST_DNS:
case LPORT_ST_FLOGI:
@@ -936,33 +1048,33 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
}
/**
- * fc_lport_rft_id_resp() - Handle response to Register Fibre
- * Channel Types by ID (RPN_ID) request
- * @sp: current sequence in RPN_ID exchange
- * @fp: response frame
+ * fc_lport_ns_resp() - Handle response to a name server
+ * registration exchange
+ * @sp: current sequence in exchange
+ * @fp: response frame
* @lp_arg: Fibre Channel host port instance
*
* Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
* and then unlock the lport.
*/
-static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *lp_arg)
+static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
{
struct fc_lport *lport = lp_arg;
struct fc_frame_header *fh;
struct fc_ct_hdr *ct;
- FC_LPORT_DBG(lport, "Received a RFT_ID %s\n", fc_els_resp_type(fp));
+ FC_LPORT_DBG(lport, "Received a ns %s\n", fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- if (lport->state != LPORT_ST_RFT_ID) {
- FC_LPORT_DBG(lport, "Received a RFT_ID response, but in state "
- "%s\n", fc_lport_state(lport));
+ if (lport->state < LPORT_ST_RNN_ID || lport->state > LPORT_ST_RFF_ID) {
+ FC_LPORT_DBG(lport, "Received a name server response, "
+ "but in state %s\n", fc_lport_state(lport));
if (IS_ERR(fp))
goto err;
goto out;
@@ -980,63 +1092,28 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
ct->ct_fs_type == FC_FST_DIR &&
ct->ct_fs_subtype == FC_NS_SUBTYPE &&
ntohs(ct->ct_cmd) == FC_FS_ACC)
- fc_lport_enter_scr(lport);
- else
- fc_lport_error(lport, fp);
-out:
- fc_frame_free(fp);
-err:
- mutex_unlock(&lport->lp_mutex);
-}
-
-/**
- * fc_lport_rpn_id_resp() - Handle response to Register Port
- * Name by ID (RPN_ID) request
- * @sp: current sequence in RPN_ID exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel host port instance
- *
- * Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
- * and then unlock the lport.
- */
-static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *lp_arg)
-{
- struct fc_lport *lport = lp_arg;
- struct fc_frame_header *fh;
- struct fc_ct_hdr *ct;
-
- FC_LPORT_DBG(lport, "Received a RPN_ID %s\n", fc_els_resp_type(fp));
-
- if (fp == ERR_PTR(-FC_EX_CLOSED))
- return;
-
- mutex_lock(&lport->lp_mutex);
-
- if (lport->state != LPORT_ST_RPN_ID) {
- FC_LPORT_DBG(lport, "Received a RPN_ID response, but in state "
- "%s\n", fc_lport_state(lport));
- if (IS_ERR(fp))
- goto err;
- goto out;
- }
-
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
- fh = fc_frame_header_get(fp);
- ct = fc_frame_payload_get(fp, sizeof(*ct));
- if (fh && ct && fh->fh_type == FC_TYPE_CT &&
- ct->ct_fs_type == FC_FST_DIR &&
- ct->ct_fs_subtype == FC_NS_SUBTYPE &&
- ntohs(ct->ct_cmd) == FC_FS_ACC)
- fc_lport_enter_rft_id(lport);
+ switch (lport->state) {
+ case LPORT_ST_RNN_ID:
+ fc_lport_enter_ns(lport, LPORT_ST_RSNN_NN);
+ break;
+ case LPORT_ST_RSNN_NN:
+ fc_lport_enter_ns(lport, LPORT_ST_RSPN_ID);
+ break;
+ case LPORT_ST_RSPN_ID:
+ fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+ break;
+ case LPORT_ST_RFT_ID:
+ fc_lport_enter_ns(lport, LPORT_ST_RFF_ID);
+ break;
+ case LPORT_ST_RFF_ID:
+ fc_lport_enter_scr(lport);
+ break;
+ default:
+ /* should have already been caught by state checks */
+ break;
+ }
else
fc_lport_error(lport, fp);
-
out:
fc_frame_free(fp);
err:
@@ -1045,8 +1122,8 @@ err:
/**
* fc_lport_scr_resp() - Handle response to State Change Register (SCR) request
- * @sp: current sequence in SCR exchange
- * @fp: response frame
+ * @sp: current sequence in SCR exchange
+ * @fp: response frame
* @lp_arg: Fibre Channel lport port instance that sent the registration request
*
* Locking Note: This function will be called without the lport lock
@@ -1092,8 +1169,8 @@ err:
}
/**
- * fc_lport_enter_scr() - Send a State Change Register (SCR) request
- * @lport: Fibre Channel local port to register for state changes
+ * fc_lport_enter_scr() - Send a SCR (State Change Register) request
+ * @lport: The local port to register for state changes
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -1114,78 +1191,74 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
}
if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR,
- fc_lport_scr_resp, lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
+ fc_lport_scr_resp, lport,
+ 2 * lport->r_a_tov))
+ fc_lport_error(lport, NULL);
}
/**
- * fc_lport_enter_rft_id() - Register FC4-types with the name server
+ * fc_lport_enter_ns() - register some object with the name server
* @lport: Fibre Channel local port to register
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/
-static void fc_lport_enter_rft_id(struct fc_lport *lport)
+static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
{
struct fc_frame *fp;
- struct fc_ns_fts *lps;
- int i;
+ enum fc_ns_req cmd;
+ int size = sizeof(struct fc_ct_hdr);
+ size_t len;
- FC_LPORT_DBG(lport, "Entered RFT_ID state from %s state\n",
+ FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
+ fc_lport_state_names[state],
fc_lport_state(lport));
- fc_lport_state_enter(lport, LPORT_ST_RFT_ID);
-
- lps = &lport->fcts;
- i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]);
- while (--i >= 0)
- if (ntohl(lps->ff_type_map[i]) != 0)
- break;
- if (i < 0) {
- /* nothing to register, move on to SCR */
- fc_lport_enter_scr(lport);
- return;
- }
+ fc_lport_state_enter(lport, state);
- fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
- sizeof(struct fc_ns_rft));
- if (!fp) {
- fc_lport_error(lport, fp);
+ switch (state) {
+ case LPORT_ST_RNN_ID:
+ cmd = FC_NS_RNN_ID;
+ size += sizeof(struct fc_ns_rn_id);
+ break;
+ case LPORT_ST_RSNN_NN:
+ len = strnlen(fc_host_symbolic_name(lport->host), 255);
+ /* if there is no symbolic name, skip to RFT_ID */
+ if (!len)
+ return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+ cmd = FC_NS_RSNN_NN;
+ size += sizeof(struct fc_ns_rsnn) + len;
+ break;
+ case LPORT_ST_RSPN_ID:
+ len = strnlen(fc_host_symbolic_name(lport->host), 255);
+ /* if there is no symbolic name, skip to RFT_ID */
+ if (!len)
+ return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+ cmd = FC_NS_RSPN_ID;
+ size += sizeof(struct fc_ns_rspn) + len;
+ break;
+ case LPORT_ST_RFT_ID:
+ cmd = FC_NS_RFT_ID;
+ size += sizeof(struct fc_ns_rft);
+ break;
+ case LPORT_ST_RFF_ID:
+ cmd = FC_NS_RFF_ID;
+ size += sizeof(struct fc_ns_rff_id);
+ break;
+ default:
+ fc_lport_error(lport, NULL);
return;
}
- if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RFT_ID,
- fc_lport_rft_id_resp,
- lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
-}
-
-/**
- * fc_rport_enter_rft_id() - Register port name with the name server
- * @lport: Fibre Channel local port to register
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
- */
-static void fc_lport_enter_rpn_id(struct fc_lport *lport)
-{
- struct fc_frame *fp;
-
- FC_LPORT_DBG(lport, "Entered RPN_ID state from %s state\n",
- fc_lport_state(lport));
-
- fc_lport_state_enter(lport, LPORT_ST_RPN_ID);
-
- fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
- sizeof(struct fc_ns_rn_id));
+ fp = fc_frame_alloc(lport, size);
if (!fp) {
fc_lport_error(lport, fp);
return;
}
- if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID,
- fc_lport_rpn_id_resp,
- lport, lport->e_d_tov))
+ if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, cmd,
+ fc_lport_ns_resp,
+ lport, 3 * lport->r_a_tov))
fc_lport_error(lport, fp);
}
@@ -1194,8 +1267,8 @@ static struct fc_rport_operations fc_lport_rport_ops = {
};
/**
- * fc_rport_enter_dns() - Create a rport to the name server
- * @lport: Fibre Channel local port requesting a rport for the name server
+ * fc_rport_enter_dns() - Create a fc_rport for the name server
+ * @lport: The local port requesting a remote port for the name server
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -1224,8 +1297,8 @@ err:
}
/**
- * fc_lport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_lport
+ * fc_lport_timeout() - Handler for the retry_work timer
+ * @work: The work struct of the local port
*/
static void fc_lport_timeout(struct work_struct *work)
{
@@ -1237,21 +1310,25 @@ static void fc_lport_timeout(struct work_struct *work)
switch (lport->state) {
case LPORT_ST_DISABLED:
+ WARN_ON(1);
+ break;
case LPORT_ST_READY:
- case LPORT_ST_RESET:
WARN_ON(1);
break;
+ case LPORT_ST_RESET:
+ break;
case LPORT_ST_FLOGI:
fc_lport_enter_flogi(lport);
break;
case LPORT_ST_DNS:
fc_lport_enter_dns(lport);
break;
- case LPORT_ST_RPN_ID:
- fc_lport_enter_rpn_id(lport);
- break;
+ case LPORT_ST_RNN_ID:
+ case LPORT_ST_RSNN_NN:
+ case LPORT_ST_RSPN_ID:
case LPORT_ST_RFT_ID:
- fc_lport_enter_rft_id(lport);
+ case LPORT_ST_RFF_ID:
+ fc_lport_enter_ns(lport, lport->state);
break;
case LPORT_ST_SCR:
fc_lport_enter_scr(lport);
@@ -1266,16 +1343,16 @@ static void fc_lport_timeout(struct work_struct *work)
/**
* fc_lport_logo_resp() - Handle response to LOGO request
- * @sp: current sequence in LOGO exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel lport port instance that sent the LOGO request
+ * @sp: The sequence that the LOGO was on
+ * @fp: The LOGO frame
+ * @lp_arg: The lport port that received the LOGO request
*
* Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
* and then unlock the lport.
*/
-static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *lp_arg)
+void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
{
struct fc_lport *lport = lp_arg;
u8 op;
@@ -1311,10 +1388,11 @@ out:
err:
mutex_unlock(&lport->lp_mutex);
}
+EXPORT_SYMBOL(fc_lport_logo_resp);
/**
* fc_rport_enter_logo() - Logout of the fabric
- * @lport: Fibre Channel local port to be logged out
+ * @lport: The local port to be logged out
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
@@ -1328,6 +1406,7 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_LOGO);
+ fc_vports_linkchange(lport);
fp = fc_frame_alloc(lport, sizeof(*logo));
if (!fp) {
@@ -1336,22 +1415,23 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
}
if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO,
- fc_lport_logo_resp, lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
+ fc_lport_logo_resp, lport,
+ 2 * lport->r_a_tov))
+ fc_lport_error(lport, NULL);
}
/**
* fc_lport_flogi_resp() - Handle response to FLOGI request
- * @sp: current sequence in FLOGI exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel lport port instance that sent the FLOGI request
+ * @sp: The sequence that the FLOGI was on
+ * @fp: The FLOGI response frame
+ * @lp_arg: The lport port that received the FLOGI response
*
* Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
* and then unlock the lport.
*/
-static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *lp_arg)
+void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
{
struct fc_lport *lport = lp_arg;
struct fc_frame_header *fh;
@@ -1385,11 +1465,6 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
fh = fc_frame_header_get(fp);
did = ntoh24(fh->fh_d_id);
if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
-
- printk(KERN_INFO "libfc: Assigned FID (%6x) in FLOGI response\n",
- did);
- fc_host_port_id(lport->host) = did;
-
flp = fc_frame_payload_get(fp, sizeof(*flp));
if (flp) {
mfs = ntohs(flp->fl_csp.sp_bb_data) &
@@ -1402,12 +1477,18 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
if (csp_flags & FC_SP_FT_EDTR)
e_d_tov /= 1000000;
+
+ lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC);
+
if ((csp_flags & FC_SP_FT_FPORT) == 0) {
if (e_d_tov > lport->e_d_tov)
lport->e_d_tov = e_d_tov;
lport->r_a_tov = 2 * e_d_tov;
- printk(KERN_INFO "libfc: Port (%6x) entered "
- "point to point mode\n", did);
+ fc_lport_set_port_id(lport, did, fp);
+ printk(KERN_INFO "host%d: libfc: "
+ "Port (%6x) entered "
+ "point-to-point mode\n",
+ lport->host->host_no, did);
fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
get_unaligned_be64(
&flp->fl_wwpn),
@@ -1418,6 +1499,7 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
lport->r_a_tov = r_a_tov;
fc_host_fabric_name(lport->host) =
get_unaligned_be64(&flp->fl_wwnn);
+ fc_lport_set_port_id(lport, did, fp);
fc_lport_enter_dns(lport);
}
}
@@ -1430,6 +1512,7 @@ out:
err:
mutex_unlock(&lport->lp_mutex);
}
+EXPORT_SYMBOL(fc_lport_flogi_resp);
/**
* fc_rport_enter_flogi() - Send a FLOGI request to the fabric manager
@@ -1451,12 +1534,18 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
if (!fp)
return fc_lport_error(lport, fp);
- if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI,
- fc_lport_flogi_resp, lport, lport->e_d_tov))
- fc_lport_error(lport, fp);
+ if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp,
+ lport->vport ? ELS_FDISC : ELS_FLOGI,
+ fc_lport_flogi_resp, lport,
+ lport->vport ? 2 * lport->r_a_tov :
+ lport->e_d_tov))
+ fc_lport_error(lport, NULL);
}
-/* Configure a fc_lport */
+/**
+ * fc_lport_config() - Configure a fc_lport
+ * @lport: The local port to be configured
+ */
int fc_lport_config(struct fc_lport *lport)
{
INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
@@ -1471,6 +1560,10 @@ int fc_lport_config(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_lport_config);
+/**
+ * fc_lport_init() - Initialize the lport layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
int fc_lport_init(struct fc_lport *lport)
{
if (!lport->tt.lport_recv)
@@ -1500,7 +1593,253 @@ int fc_lport_init(struct fc_lport *lport)
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
- INIT_LIST_HEAD(&lport->ema_list);
return 0;
}
EXPORT_SYMBOL(fc_lport_init);
+
+/**
+ * fc_lport_bsg_resp() - The common response handler for FC Passthrough requests
+ * @sp: The sequence for the FC Passthrough response
+ * @fp: The response frame
+ * @info_arg: The BSG info that the response is for
+ */
+static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *info_arg)
+{
+ struct fc_bsg_info *info = info_arg;
+ struct fc_bsg_job *job = info->job;
+ struct fc_lport *lport = info->lport;
+ struct fc_frame_header *fh;
+ size_t len;
+ void *buf;
+
+ if (IS_ERR(fp)) {
+ job->reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ?
+ -ECONNABORTED : -ETIMEDOUT;
+ job->reply_len = sizeof(uint32_t);
+ job->state_flags |= FC_RQST_STATE_DONE;
+ job->job_done(job);
+ kfree(info);
+ return;
+ }
+
+ mutex_lock(&lport->lp_mutex);
+ fh = fc_frame_header_get(fp);
+ len = fr_len(fp) - sizeof(*fh);
+ buf = fc_frame_payload_get(fp, 0);
+
+ if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) {
+ /* Get the response code from the first frame payload */
+ unsigned short cmd = (info->rsp_code == FC_FS_ACC) ?
+ ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) :
+ (unsigned short)fc_frame_payload_op(fp);
+
+ /* Save the reply status of the job */
+ job->reply->reply_data.ctels_reply.status =
+ (cmd == info->rsp_code) ?
+ FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT;
+ }
+
+ job->reply->reply_payload_rcv_len +=
+ fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents,
+ &info->offset, KM_BIO_SRC_IRQ, NULL);
+
+ if (fr_eof(fp) == FC_EOF_T &&
+ (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
+ (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
+ if (job->reply->reply_payload_rcv_len >
+ job->reply_payload.payload_len)
+ job->reply->reply_payload_rcv_len =
+ job->reply_payload.payload_len;
+ job->reply->result = 0;
+ job->state_flags |= FC_RQST_STATE_DONE;
+ job->job_done(job);
+ kfree(info);
+ }
+ fc_frame_free(fp);
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_els_request() - Send ELS passthrough request
+ * @job: The BSG Passthrough job
+ * @lport: The local port sending the request
+ * @did: The destination port id
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static int fc_lport_els_request(struct fc_bsg_job *job,
+ struct fc_lport *lport,
+ u32 did, u32 tov)
+{
+ struct fc_bsg_info *info;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ char *pp;
+ int len;
+
+ fp = fc_frame_alloc(lport, job->request_payload.payload_len);
+ if (!fp)
+ return -ENOMEM;
+
+ len = job->request_payload.payload_len;
+ pp = fc_frame_payload_get(fp, len);
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ pp, len);
+
+ fh = fc_frame_header_get(fp);
+ fh->fh_r_ctl = FC_RCTL_ELS_REQ;
+ hton24(fh->fh_d_id, did);
+ hton24(fh->fh_s_id, fc_host_port_id(lport->host));
+ fh->fh_type = FC_TYPE_ELS;
+ hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
+ FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+ fh->fh_cs_ctl = 0;
+ fh->fh_df_ctl = 0;
+ fh->fh_parm_offset = 0;
+
+ info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL);
+ if (!info) {
+ fc_frame_free(fp);
+ return -ENOMEM;
+ }
+
+ info->job = job;
+ info->lport = lport;
+ info->rsp_code = ELS_LS_ACC;
+ info->nents = job->reply_payload.sg_cnt;
+ info->sg = job->reply_payload.sg_list;
+
+ if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
+ NULL, info, tov))
+ return -ECOMM;
+ return 0;
+}
+
+/**
+ * fc_lport_ct_request() - Send CT Passthrough request
+ * @job: The BSG Passthrough job
+ * @lport: The local port sending the request
+ * @did: The destination FC-ID
+ * @tov: The timeout period to wait for the response
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static int fc_lport_ct_request(struct fc_bsg_job *job,
+ struct fc_lport *lport, u32 did, u32 tov)
+{
+ struct fc_bsg_info *info;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ struct fc_ct_req *ct;
+ size_t len;
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ job->request_payload.payload_len);
+ if (!fp)
+ return -ENOMEM;
+
+ len = job->request_payload.payload_len;
+ ct = fc_frame_payload_get(fp, len);
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ ct, len);
+
+ fh = fc_frame_header_get(fp);
+ fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL;
+ hton24(fh->fh_d_id, did);
+ hton24(fh->fh_s_id, fc_host_port_id(lport->host));
+ fh->fh_type = FC_TYPE_CT;
+ hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
+ FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+ fh->fh_cs_ctl = 0;
+ fh->fh_df_ctl = 0;
+ fh->fh_parm_offset = 0;
+
+ info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL);
+ if (!info) {
+ fc_frame_free(fp);
+ return -ENOMEM;
+ }
+
+ info->job = job;
+ info->lport = lport;
+ info->rsp_code = FC_FS_ACC;
+ info->nents = job->reply_payload.sg_cnt;
+ info->sg = job->reply_payload.sg_list;
+
+ if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
+ NULL, info, tov))
+ return -ECOMM;
+ return 0;
+}
+
+/**
+ * fc_lport_bsg_request() - The common entry point for sending
+ * FC Passthrough requests
+ * @job: The BSG passthrough job
+ */
+int fc_lport_bsg_request(struct fc_bsg_job *job)
+{
+ struct request *rsp = job->req->next_rq;
+ struct Scsi_Host *shost = job->shost;
+ struct fc_lport *lport = shost_priv(shost);
+ struct fc_rport *rport;
+ struct fc_rport_priv *rdata;
+ int rc = -EINVAL;
+ u32 did;
+
+ job->reply->reply_payload_rcv_len = 0;
+ rsp->resid_len = job->reply_payload.payload_len;
+
+ mutex_lock(&lport->lp_mutex);
+
+ switch (job->request->msgcode) {
+ case FC_BSG_RPT_ELS:
+ rport = job->rport;
+ if (!rport)
+ break;
+
+ rdata = rport->dd_data;
+ rc = fc_lport_els_request(job, lport, rport->port_id,
+ rdata->e_d_tov);
+ break;
+
+ case FC_BSG_RPT_CT:
+ rport = job->rport;
+ if (!rport)
+ break;
+
+ rdata = rport->dd_data;
+ rc = fc_lport_ct_request(job, lport, rport->port_id,
+ rdata->e_d_tov);
+ break;
+
+ case FC_BSG_HST_CT:
+ did = ntoh24(job->request->rqst_data.h_ct.port_id);
+ if (did == FC_FID_DIR_SERV)
+ rdata = lport->dns_rdata;
+ else
+ rdata = lport->tt.rport_lookup(lport, did);
+
+ if (!rdata)
+ break;
+
+ rc = fc_lport_ct_request(job, lport, did, rdata->e_d_tov);
+ break;
+
+ case FC_BSG_HST_ELS_NOLOGIN:
+ did = ntoh24(job->request->rqst_data.h_els.port_id);
+ rc = fc_lport_els_request(job, lport, did, lport->e_d_tov);
+ break;
+ }
+
+ mutex_unlock(&lport->lp_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(fc_lport_bsg_request);
diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
new file mode 100644
index 00000000000..c68f6c7341c
--- /dev/null
+++ b/drivers/scsi/libfc/fc_npiv.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright(c) 2009 Intel 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * NPIV VN_Port helper functions for libfc
+ */
+
+#include <scsi/libfc.h>
+
+/**
+ * fc_vport_create() - Create a new NPIV vport instance
+ * @vport: fc_vport structure from scsi_transport_fc
+ * @privsize: driver private data size to allocate along with the Scsi_Host
+ */
+
+struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)
+{
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+ struct fc_lport *vn_port;
+
+ vn_port = libfc_host_alloc(shost->hostt, privsize);
+ if (!vn_port)
+ goto err_out;
+ if (fc_exch_mgr_list_clone(n_port, vn_port))
+ goto err_put;
+
+ vn_port->vport = vport;
+ vport->dd_data = vn_port;
+
+ mutex_lock(&n_port->lp_mutex);
+ list_add_tail(&vn_port->list, &n_port->vports);
+ mutex_unlock(&n_port->lp_mutex);
+
+ return vn_port;
+
+err_put:
+ scsi_host_put(vn_port->host);
+err_out:
+ return NULL;
+}
+EXPORT_SYMBOL(libfc_vport_create);
+
+/**
+ * fc_vport_id_lookup() - find NPIV lport that matches a given fabric ID
+ * @n_port: Top level N_Port which may have multiple NPIV VN_Ports
+ * @port_id: Fabric ID to find a match for
+ *
+ * Returns: matching lport pointer or NULL if there is no match
+ */
+struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
+{
+ struct fc_lport *lport = NULL;
+ struct fc_lport *vn_port;
+
+ if (fc_host_port_id(n_port->host) == port_id)
+ return n_port;
+
+ mutex_lock(&n_port->lp_mutex);
+ list_for_each_entry(vn_port, &n_port->vports, list) {
+ if (fc_host_port_id(vn_port->host) == port_id) {
+ lport = vn_port;
+ break;
+ }
+ }
+ mutex_unlock(&n_port->lp_mutex);
+
+ return lport;
+}
+
+/*
+ * When setting the link state of vports during an lport state change, it's
+ * necessary to hold the lp_mutex of both the N_Port and the VN_Port.
+ * This tells the lockdep engine to treat the nested locking of the VN_Port
+ * as a different lock class.
+ */
+enum libfc_lport_mutex_class {
+ LPORT_MUTEX_NORMAL = 0,
+ LPORT_MUTEX_VN_PORT = 1,
+};
+
+/**
+ * __fc_vport_setlink() - update link and status on a VN_Port
+ * @n_port: parent N_Port
+ * @vn_port: VN_Port to update
+ *
+ * Locking: must be called with both the N_Port and VN_Port lp_mutex held
+ */
+static void __fc_vport_setlink(struct fc_lport *n_port,
+ struct fc_lport *vn_port)
+{
+ struct fc_vport *vport = vn_port->vport;
+
+ if (vn_port->state == LPORT_ST_DISABLED)
+ return;
+
+ if (n_port->state == LPORT_ST_READY) {
+ if (n_port->npiv_enabled) {
+ fc_vport_set_state(vport, FC_VPORT_INITIALIZING);
+ __fc_linkup(vn_port);
+ } else {
+ fc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
+ __fc_linkdown(vn_port);
+ }
+ } else {
+ fc_vport_set_state(vport, FC_VPORT_LINKDOWN);
+ __fc_linkdown(vn_port);
+ }
+}
+
+/**
+ * fc_vport_setlink() - update link and status on a VN_Port
+ * @vn_port: virtual port to update
+ */
+void fc_vport_setlink(struct fc_lport *vn_port)
+{
+ struct fc_vport *vport = vn_port->vport;
+ struct Scsi_Host *shost = vport_to_shost(vport);
+ struct fc_lport *n_port = shost_priv(shost);
+
+ mutex_lock(&n_port->lp_mutex);
+ mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
+ __fc_vport_setlink(n_port, vn_port);
+ mutex_unlock(&vn_port->lp_mutex);
+ mutex_unlock(&n_port->lp_mutex);
+}
+EXPORT_SYMBOL(fc_vport_setlink);
+
+/**
+ * fc_vports_linkchange() - change the link state of all vports
+ * @n_port: Parent N_Port that has changed state
+ *
+ * Locking: called with the n_port lp_mutex held
+ */
+void fc_vports_linkchange(struct fc_lport *n_port)
+{
+ struct fc_lport *vn_port;
+
+ list_for_each_entry(vn_port, &n_port->vports, list) {
+ mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
+ __fc_vport_setlink(n_port, vn_port);
+ mutex_unlock(&vn_port->lp_mutex);
+ }
+}
+
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 03ea6748e7e..35ca0e72df4 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -55,6 +55,8 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+#include "fc_libfc.h"
+
struct workqueue_struct *rport_event_queue;
static void fc_rport_enter_plogi(struct fc_rport_priv *);
@@ -86,12 +88,13 @@ static const char *fc_rport_state_names[] = {
[RPORT_ST_LOGO] = "LOGO",
[RPORT_ST_ADISC] = "ADISC",
[RPORT_ST_DELETE] = "Delete",
+ [RPORT_ST_RESTART] = "Restart",
};
/**
- * fc_rport_lookup() - lookup a remote port by port_id
- * @lport: Fibre Channel host port instance
- * @port_id: remote port port_id to match
+ * fc_rport_lookup() - Lookup a remote port by port_id
+ * @lport: The local port to lookup the remote port on
+ * @port_id: The remote port ID to look up
*/
static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
u32 port_id)
@@ -99,16 +102,17 @@ static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
struct fc_rport_priv *rdata;
list_for_each_entry(rdata, &lport->disc.rports, peers)
- if (rdata->ids.port_id == port_id &&
- rdata->rp_state != RPORT_ST_DELETE)
+ if (rdata->ids.port_id == port_id)
return rdata;
return NULL;
}
/**
* fc_rport_create() - Create a new remote port
- * @lport: The local port that the new remote port is for
- * @port_id: The port ID for the new remote port
+ * @lport: The local port this remote port will be associated with
+ * @ids: The identifiers for the new remote port
+ *
+ * The remote port will start in the INIT state.
*
* Locking note: must be called with the disc_mutex held.
*/
@@ -147,8 +151,8 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
}
/**
- * fc_rport_destroy() - free a remote port after last reference is released.
- * @kref: pointer to kref inside struct fc_rport_priv
+ * fc_rport_destroy() - Free a remote port after last reference is released
+ * @kref: The remote port's kref
*/
static void fc_rport_destroy(struct kref *kref)
{
@@ -159,8 +163,8 @@ static void fc_rport_destroy(struct kref *kref)
}
/**
- * fc_rport_state() - return a string for the state the rport is in
- * @rdata: remote port private data
+ * fc_rport_state() - Return a string identifying the remote port's state
+ * @rdata: The remote port
*/
static const char *fc_rport_state(struct fc_rport_priv *rdata)
{
@@ -173,9 +177,9 @@ static const char *fc_rport_state(struct fc_rport_priv *rdata)
}
/**
- * fc_set_rport_loss_tmo() - Set the remote port loss timeout in seconds.
- * @rport: Pointer to Fibre Channel remote port structure
- * @timeout: timeout in seconds
+ * fc_set_rport_loss_tmo() - Set the remote port loss timeout
+ * @rport: The remote port that gets a new timeout value
+ * @timeout: The new timeout value (in seconds)
*/
void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
{
@@ -187,9 +191,11 @@ void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
EXPORT_SYMBOL(fc_set_rport_loss_tmo);
/**
- * fc_plogi_get_maxframe() - Get max payload from the common service parameters
- * @flp: FLOGI payload structure
- * @maxval: upper limit, may be less than what is in the service parameters
+ * fc_plogi_get_maxframe() - Get the maximum payload from the common service
+ * parameters in a FLOGI frame
+ * @flp: The FLOGI payload
+ * @maxval: The maximum frame size upper limit; this may be less than what
+ * is in the service parameters
*/
static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
unsigned int maxval)
@@ -210,9 +216,9 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
}
/**
- * fc_rport_state_enter() - Change the rport's state
- * @rdata: The rport whose state should change
- * @new: The new state of the rport
+ * fc_rport_state_enter() - Change the state of a remote port
+ * @rdata: The remote port whose state should change
+ * @new: The new state
*
* Locking Note: Called with the rport lock held
*/
@@ -224,17 +230,22 @@ static void fc_rport_state_enter(struct fc_rport_priv *rdata,
rdata->rp_state = new;
}
+/**
+ * fc_rport_work() - Handler for remote port events in the rport_event_queue
+ * @work: Handle to the remote port being dequeued
+ */
static void fc_rport_work(struct work_struct *work)
{
u32 port_id;
struct fc_rport_priv *rdata =
container_of(work, struct fc_rport_priv, event_work);
- struct fc_rport_libfc_priv *rp;
+ struct fc_rport_libfc_priv *rpriv;
enum fc_rport_event event;
struct fc_lport *lport = rdata->local_port;
struct fc_rport_operations *rport_ops;
struct fc_rport_identifiers ids;
struct fc_rport *rport;
+ int restart = 0;
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
@@ -265,12 +276,12 @@ static void fc_rport_work(struct work_struct *work)
rport->maxframe_size = rdata->maxframe_size;
rport->supported_classes = rdata->supported_classes;
- rp = rport->dd_data;
- rp->local_port = lport;
- rp->rp_state = rdata->rp_state;
- rp->flags = rdata->flags;
- rp->e_d_tov = rdata->e_d_tov;
- rp->r_a_tov = rdata->r_a_tov;
+ rpriv = rport->dd_data;
+ rpriv->local_port = lport;
+ rpriv->rp_state = rdata->rp_state;
+ rpriv->flags = rdata->flags;
+ rpriv->e_d_tov = rdata->e_d_tov;
+ rpriv->r_a_tov = rdata->r_a_tov;
mutex_unlock(&rdata->rp_mutex);
if (rport_ops && rport_ops->event_callback) {
@@ -287,8 +298,19 @@ static void fc_rport_work(struct work_struct *work)
mutex_unlock(&rdata->rp_mutex);
if (port_id != FC_FID_DIR_SERV) {
+ /*
+ * We must drop rp_mutex before taking disc_mutex.
+ * Re-evaluate state to allow for restart.
+ * A transition to RESTART state must only happen
+ * while disc_mutex is held and rdata is on the list.
+ */
mutex_lock(&lport->disc.disc_mutex);
- list_del(&rdata->peers);
+ mutex_lock(&rdata->rp_mutex);
+ if (rdata->rp_state == RPORT_ST_RESTART)
+ restart = 1;
+ else
+ list_del(&rdata->peers);
+ mutex_unlock(&rdata->rp_mutex);
mutex_unlock(&lport->disc.disc_mutex);
}
@@ -305,14 +327,20 @@ static void fc_rport_work(struct work_struct *work)
lport->tt.exch_mgr_reset(lport, port_id, 0);
if (rport) {
- rp = rport->dd_data;
- rp->rp_state = RPORT_ST_DELETE;
+ rpriv = rport->dd_data;
+ rpriv->rp_state = RPORT_ST_DELETE;
mutex_lock(&rdata->rp_mutex);
rdata->rport = NULL;
mutex_unlock(&rdata->rp_mutex);
fc_remote_port_delete(rport);
}
- kref_put(&rdata->kref, lport->tt.rport_destroy);
+ if (restart) {
+ mutex_lock(&rdata->rp_mutex);
+ FC_RPORT_DBG(rdata, "work restart\n");
+ fc_rport_enter_plogi(rdata);
+ mutex_unlock(&rdata->rp_mutex);
+ } else
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
break;
default:
@@ -323,7 +351,7 @@ static void fc_rport_work(struct work_struct *work)
/**
* fc_rport_login() - Start the remote port login state machine
- * @rdata: private remote port
+ * @rdata: The remote port to be logged in to
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -342,6 +370,12 @@ int fc_rport_login(struct fc_rport_priv *rdata)
FC_RPORT_DBG(rdata, "ADISC port\n");
fc_rport_enter_adisc(rdata);
break;
+ case RPORT_ST_RESTART:
+ break;
+ case RPORT_ST_DELETE:
+ FC_RPORT_DBG(rdata, "Restart deleted port\n");
+ fc_rport_state_enter(rdata, RPORT_ST_RESTART);
+ break;
default:
FC_RPORT_DBG(rdata, "Login to port\n");
fc_rport_enter_plogi(rdata);
@@ -353,9 +387,9 @@ int fc_rport_login(struct fc_rport_priv *rdata)
}
/**
- * fc_rport_enter_delete() - schedule a remote port to be deleted.
- * @rdata: private remote port
- * @event: event to report as the reason for deletion
+ * fc_rport_enter_delete() - Schedule a remote port to be deleted
+ * @rdata: The remote port to be deleted
+ * @event: The event to report as the reason for deletion
*
* Locking Note: Called with the rport lock held.
*
@@ -382,8 +416,8 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_logoff() - Logoff and remove an rport
- * @rdata: private remote port
+ * fc_rport_logoff() - Logoff and remove a remote port
+ * @rdata: The remote port to be logged off of
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -397,26 +431,27 @@ int fc_rport_logoff(struct fc_rport_priv *rdata)
if (rdata->rp_state == RPORT_ST_DELETE) {
FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
- mutex_unlock(&rdata->rp_mutex);
goto out;
}
- fc_rport_enter_logo(rdata);
+ if (rdata->rp_state == RPORT_ST_RESTART)
+ FC_RPORT_DBG(rdata, "Port in Restart state, deleting\n");
+ else
+ fc_rport_enter_logo(rdata);
/*
* Change the state to Delete so that we discard
* the response.
*/
fc_rport_enter_delete(rdata, RPORT_EV_STOP);
- mutex_unlock(&rdata->rp_mutex);
-
out:
+ mutex_unlock(&rdata->rp_mutex);
return 0;
}
/**
- * fc_rport_enter_ready() - The rport is ready
- * @rdata: private remote port
+ * fc_rport_enter_ready() - Transition to the RPORT_ST_READY state
+ * @rdata: The remote port that is ready
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -433,8 +468,8 @@ static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
}
/**
- * fc_rport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_rport_priv
+ * fc_rport_timeout() - Handler for the retry_work timer
+ * @work: Handle to the remote port that has timed out
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -466,6 +501,7 @@ static void fc_rport_timeout(struct work_struct *work)
case RPORT_ST_READY:
case RPORT_ST_INIT:
case RPORT_ST_DELETE:
+ case RPORT_ST_RESTART:
break;
}
@@ -474,8 +510,8 @@ static void fc_rport_timeout(struct work_struct *work)
/**
* fc_rport_error() - Error handler, called once retries have been exhausted
- * @rdata: private remote port
- * @fp: The frame pointer
+ * @rdata: The remote port the error is happened on
+ * @fp: The error code encapsulated in a frame pointer
*
* Locking Note: The rport lock is expected to be held before
* calling this routine
@@ -499,6 +535,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
fc_rport_enter_logo(rdata);
break;
case RPORT_ST_DELETE:
+ case RPORT_ST_RESTART:
case RPORT_ST_READY:
case RPORT_ST_INIT:
break;
@@ -506,9 +543,9 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
}
/**
- * fc_rport_error_retry() - Error handler when retries are desired
- * @rdata: private remote port data
- * @fp: The frame pointer
+ * fc_rport_error_retry() - Handler for remote port state retries
+ * @rdata: The remote port whose state is to be retried
+ * @fp: The error code encapsulated in a frame pointer
*
* If the error was an exchange timeout retry immediately,
* otherwise wait for E_D_TOV.
@@ -540,10 +577,10 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_plogi_recv_resp() - Handle incoming ELS PLOGI response
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_plogi_recv_resp() - Handler for ELS PLOGI responses
+ * @sp: The sequence the PLOGI is on
+ * @fp: The PLOGI response frame
+ * @rdata_arg: The remote port that sent the PLOGI response
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
@@ -606,8 +643,8 @@ err:
}
/**
- * fc_rport_enter_plogi() - Send Port Login (PLOGI) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_plogi() - Send Port Login (PLOGI) request
+ * @rdata: The remote port to send a PLOGI to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -631,17 +668,18 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
rdata->e_d_tov = lport->e_d_tov;
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
- fc_rport_plogi_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_plogi_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
* fc_rport_prli_resp() - Process Login (PRLI) response handler
- * @sp: current sequence in the PRLI exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * @sp: The sequence the PRLI response was on
+ * @fp: The PRLI response frame
+ * @rdata_arg: The remote port that sent the PRLI response
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
@@ -710,10 +748,10 @@ err:
}
/**
- * fc_rport_logo_resp() - Logout (LOGO) response handler
- * @sp: current sequence in the LOGO exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_logo_resp() - Handler for logout (LOGO) responses
+ * @sp: The sequence the LOGO was on
+ * @fp: The LOGO response frame
+ * @rdata_arg: The remote port that sent the LOGO response
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
@@ -756,8 +794,8 @@ err:
}
/**
- * fc_rport_enter_prli() - Send Process Login (PRLI) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_prli() - Send Process Login (PRLI) request
+ * @rdata: The remote port to send the PRLI request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -792,17 +830,18 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
- fc_rport_prli_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_prli_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_els_rtv_resp() - Request Timeout Value response handler
- * @sp: current sequence in the RTV exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_els_rtv_resp() - Handler for Request Timeout Value (RTV) responses
+ * @sp: The sequence the RTV was on
+ * @fp: The RTV response frame
+ * @rdata_arg: The remote port that sent the RTV response
*
* Many targets don't seem to support this.
*
@@ -865,8 +904,8 @@ err:
}
/**
- * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request
+ * @rdata: The remote port to send the RTV request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -888,15 +927,16 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
- fc_rport_rtv_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_rtv_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_enter_logo() - Send Logout (LOGO) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_logo() - Send a logout (LOGO) request
+ * @rdata: The remote port to send the LOGO request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -918,24 +958,25 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
- fc_rport_logo_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_logo_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_els_adisc_resp() - Address Discovery response handler
- * @sp: current sequence in the ADISC exchange
- * @fp: response frame
- * @rdata_arg: remote port private.
+ * fc_rport_els_adisc_resp() - Handler for Address Discovery (ADISC) responses
+ * @sp: The sequence the ADISC response was on
+ * @fp: The ADISC response frame
+ * @rdata_arg: The remote port that sent the ADISC response
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rdata_arg)
+ void *rdata_arg)
{
struct fc_rport_priv *rdata = rdata_arg;
struct fc_els_adisc *adisc;
@@ -983,8 +1024,8 @@ err:
}
/**
- * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer
- * @rdata: remote port private data
+ * fc_rport_enter_adisc() - Send Address Discover (ADISC) request
+ * @rdata: The remote port to send the ADISC request to
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
@@ -1005,17 +1046,18 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
return;
}
if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
- fc_rport_adisc_resp, rdata, lport->e_d_tov))
- fc_rport_error_retry(rdata, fp);
+ fc_rport_adisc_resp, rdata,
+ 2 * lport->r_a_tov))
+ fc_rport_error_retry(rdata, NULL);
else
kref_get(&rdata->kref);
}
/**
- * fc_rport_recv_adisc_req() - Handle incoming Address Discovery (ADISC) Request
- * @rdata: remote port private
- * @sp: current sequence in the ADISC exchange
- * @in_fp: ADISC request frame
+ * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests
+ * @rdata: The remote port that sent the ADISC request
+ * @sp: The sequence the ADISC request was on
+ * @in_fp: The ADISC request frame
*
* Locking Note: Called with the lport and rport locks held.
*/
@@ -1056,10 +1098,82 @@ drop:
}
/**
- * fc_rport_recv_els_req() - handle a validated ELS request.
- * @lport: Fibre Channel local port
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
+ * fc_rport_recv_rls_req() - Handle received Read Link Status request
+ * @rdata: The remote port that sent the RLS request
+ * @sp: The sequence that the RLS was on
+ * @rx_fp: The PRLI request frame
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp, struct fc_frame *rx_fp)
+
+{
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_rls *rls;
+ struct fc_els_rls_resp *rsp;
+ struct fc_els_lesb *lesb;
+ struct fc_seq_els_data rjt_data;
+ struct fc_host_statistics *hst;
+ u32 f_ctl;
+
+ FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
+ fc_rport_state(rdata));
+
+ rls = fc_frame_payload_get(rx_fp, sizeof(*rls));
+ if (!rls) {
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ goto out_rjt;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(*rsp));
+ if (!fp) {
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
+ goto out_rjt;
+ }
+
+ rsp = fc_frame_payload_get(fp, sizeof(*rsp));
+ memset(rsp, 0, sizeof(*rsp));
+ rsp->rls_cmd = ELS_LS_ACC;
+ lesb = &rsp->rls_lesb;
+ if (lport->tt.get_lesb) {
+ /* get LESB from LLD if it supports it */
+ lport->tt.get_lesb(lport, lesb);
+ } else {
+ fc_get_host_stats(lport->host);
+ hst = &lport->host_stats;
+ lesb->lesb_link_fail = htonl(hst->link_failure_count);
+ lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count);
+ lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count);
+ lesb->lesb_prim_err = htonl(hst->prim_seq_protocol_err_count);
+ lesb->lesb_inv_word = htonl(hst->invalid_tx_word_count);
+ lesb->lesb_inv_crc = htonl(hst->invalid_crc_count);
+ }
+
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ goto out;
+
+out_rjt:
+ rjt_data.fp = NULL;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+out:
+ fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_rport_recv_els_req() - Handler for validated ELS requests
+ * @lport: The local port that received the ELS request
+ * @sp: The sequence that the ELS request was on
+ * @fp: The ELS request frame
*
* Handle incoming ELS requests that require port login.
* The ELS opcode has already been validated by the caller.
@@ -1117,6 +1231,9 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
els_data.fp = fp;
lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
break;
+ case ELS_RLS:
+ fc_rport_recv_rls_req(rdata, sp, fp);
+ break;
default:
fc_frame_free(fp); /* can't happen */
break;
@@ -1131,10 +1248,10 @@ reject:
}
/**
- * fc_rport_recv_req() - Handle a received ELS request from a rport
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
- * @lport: Fibre Channel local port
+ * fc_rport_recv_req() - Handler for requests
+ * @sp: The sequence the request was on
+ * @fp: The request frame
+ * @lport: The local port that received the request
*
* Locking Note: Called with the lport lock held.
*/
@@ -1161,6 +1278,7 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
case ELS_ADISC:
case ELS_RRQ:
case ELS_REC:
+ case ELS_RLS:
fc_rport_recv_els_req(lport, sp, fp);
break;
default:
@@ -1174,10 +1292,10 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
}
/**
- * fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request
- * @lport: local port
- * @sp: current sequence in the PLOGI exchange
- * @fp: PLOGI request frame
+ * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests
+ * @lport: The local port that received the PLOGI request
+ * @sp: The sequence that the PLOGI request was on
+ * @rx_fp: The PLOGI request frame
*
* Locking Note: The rport lock is held before calling this function.
*/
@@ -1248,6 +1366,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
}
break;
case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
case RPORT_ST_READY:
case RPORT_ST_ADISC:
FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
@@ -1255,11 +1374,14 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
/* XXX TBD - should reset */
break;
case RPORT_ST_DELETE:
- default:
- FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n",
- rdata->rp_state);
- fc_frame_free(rx_fp);
- goto out;
+ case RPORT_ST_LOGO:
+ case RPORT_ST_RESTART:
+ FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
+ fc_rport_state(rdata));
+ mutex_unlock(&rdata->rp_mutex);
+ rjt_data.reason = ELS_RJT_BUSY;
+ rjt_data.explan = ELS_EXPL_NONE;
+ goto reject;
}
/*
@@ -1295,10 +1417,10 @@ reject:
}
/**
- * fc_rport_recv_prli_req() - Handle incoming Process Login (PRLI) request
- * @rdata: private remote port data
- * @sp: current sequence in the PRLI exchange
- * @fp: PRLI request frame
+ * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests
+ * @rdata: The remote port that sent the PRLI request
+ * @sp: The sequence that the PRLI was on
+ * @rx_fp: The PRLI request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
@@ -1402,7 +1524,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
break;
case FC_TYPE_FCP:
fcp_parm = ntohl(rspp->spp_params);
- if (fcp_parm * FCP_SPPF_RETRY)
+ if (fcp_parm & FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
@@ -1452,10 +1574,10 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_recv_prlo_req() - Handle incoming Process Logout (PRLO) request
- * @rdata: private remote port data
- * @sp: current sequence in the PRLO exchange
- * @fp: PRLO request frame
+ * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests
+ * @rdata: The remote port that sent the PRLO request
+ * @sp: The sequence that the PRLO was on
+ * @fp: The PRLO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
@@ -1482,10 +1604,10 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
}
/**
- * fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request
- * @lport: local port.
- * @sp: current sequence in the LOGO exchange
- * @fp: LOGO request frame
+ * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests
+ * @lport: The local port that received the LOGO request
+ * @sp: The sequence that the LOGO request was on
+ * @fp: The LOGO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
@@ -1510,14 +1632,14 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
fc_rport_state(rdata));
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+
/*
- * If the remote port was created due to discovery,
- * log back in. It may have seen a stale RSCN about us.
+ * If the remote port was created due to discovery, set state
+ * to log back in. It may have seen a stale RSCN about us.
*/
- if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id)
- fc_rport_enter_plogi(rdata);
- else
- fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+ if (rdata->disc_id)
+ fc_rport_state_enter(rdata, RPORT_ST_RESTART);
mutex_unlock(&rdata->rp_mutex);
} else
FC_RPORT_ID_DBG(lport, sid,
@@ -1526,11 +1648,18 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
fc_frame_free(fp);
}
+/**
+ * fc_rport_flush_queue() - Flush the rport_event_queue
+ */
static void fc_rport_flush_queue(void)
{
flush_workqueue(rport_event_queue);
}
+/**
+ * fc_rport_init() - Initialize the remote port layer for a local port
+ * @lport: The local port to initialize the remote port layer for
+ */
int fc_rport_init(struct fc_lport *lport)
{
if (!lport->tt.rport_lookup)
@@ -1558,25 +1687,33 @@ int fc_rport_init(struct fc_lport *lport)
}
EXPORT_SYMBOL(fc_rport_init);
-int fc_setup_rport(void)
+/**
+ * fc_setup_rport() - Initialize the rport_event_queue
+ */
+int fc_setup_rport()
{
rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
if (!rport_event_queue)
return -ENOMEM;
return 0;
}
-EXPORT_SYMBOL(fc_setup_rport);
-void fc_destroy_rport(void)
+/**
+ * fc_destroy_rport() - Destroy the rport_event_queue
+ */
+void fc_destroy_rport()
{
destroy_workqueue(rport_event_queue);
}
-EXPORT_SYMBOL(fc_destroy_rport);
+/**
+ * fc_rport_terminate_io() - Stop all outstanding I/O on a remote port
+ * @rport: The remote port whose I/O should be terminated
+ */
void fc_rport_terminate_io(struct fc_rport *rport)
{
- struct fc_rport_libfc_priv *rp = rport->dd_data;
- struct fc_lport *lport = rp->local_port;
+ struct fc_rport_libfc_priv *rpriv = rport->dd_data;
+ struct fc_lport *lport = rpriv->local_port;
lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
lport->tt.exch_mgr_reset(lport, rport->port_id, 0);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index f1a4246f890..b7689f3d05f 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -266,6 +266,88 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
}
/**
+ * iscsi_check_tmf_restrictions - check if a task is affected by TMF
+ * @task: iscsi task
+ * @opcode: opcode to check for
+ *
+ * During TMF a task has to be checked if it's affected.
+ * All unrelated I/O can be passed through, but I/O to the
+ * affected LUN should be restricted.
+ * If 'fast_abort' is set we won't be sending any I/O to the
+ * affected LUN.
+ * Otherwise the target is waiting for all TTTs to be completed,
+ * so we have to send all outstanding Data-Out PDUs to the target.
+ */
+static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
+{
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_tm *tmf = &conn->tmhdr;
+ unsigned int hdr_lun;
+
+ if (conn->tmf_state == TMF_INITIAL)
+ return 0;
+
+ if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC)
+ return 0;
+
+ switch (ISCSI_TM_FUNC_VALUE(tmf)) {
+ case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+ /*
+ * Allow PDUs for unrelated LUNs
+ */
+ hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun);
+ if (hdr_lun != task->sc->device->lun)
+ return 0;
+ /* fall through */
+ case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+ /*
+ * Fail all SCSI cmd PDUs
+ */
+ if (opcode != ISCSI_OP_SCSI_DATA_OUT) {
+ iscsi_conn_printk(KERN_INFO, conn,
+ "task [op %x/%x itt "
+ "0x%x/0x%x] "
+ "rejected.\n",
+ task->hdr->opcode, opcode,
+ task->itt, task->hdr_itt);
+ return -EACCES;
+ }
+ /*
+ * And also all data-out PDUs in response to R2T
+ * if fast_abort is set.
+ */
+ if (conn->session->fast_abort) {
+ iscsi_conn_printk(KERN_INFO, conn,
+ "task [op %x/%x itt "
+ "0x%x/0x%x] fast abort.\n",
+ task->hdr->opcode, opcode,
+ task->itt, task->hdr_itt);
+ return -EACCES;
+ }
+ break;
+ case ISCSI_TM_FUNC_ABORT_TASK:
+ /*
+ * the caller has already checked if the task
+ * they want to abort was in the pending queue so if
+ * we are here the cmd pdu has gone out already, and
+ * we will only hit this for data-outs
+ */
+ if (opcode == ISCSI_OP_SCSI_DATA_OUT &&
+ task->hdr_itt == tmf->rtt) {
+ ISCSI_DBG_SESSION(conn->session,
+ "Preventing task %x/%x from sending "
+ "data-out due to abort task in "
+ "progress\n", task->itt,
+ task->hdr_itt);
+ return -EACCES;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
* iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
* @task: iscsi task
*
@@ -282,6 +364,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
itt_t itt;
int rc;
+ rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD);
+ if (rc)
+ return rc;
+
if (conn->session->tt->alloc_pdu) {
rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD);
if (rc)
@@ -577,12 +663,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
struct iscsi_session *session = conn->session;
struct iscsi_hdr *hdr = task->hdr;
struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
+ uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK;
if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
return -ENOTCONN;
- if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
- hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+ if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT)
nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
/*
* pre-format CmdSN for outgoing PDU.
@@ -590,9 +676,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
nop->cmdsn = cpu_to_be32(session->cmdsn);
if (hdr->itt != RESERVED_ITT) {
/*
- * TODO: We always use immediate, so we never hit this.
+ * TODO: We always use immediate for normal session pdus.
* If we start to send tmfs or nops as non-immediate then
* we should start checking the cmdsn numbers for mgmt tasks.
+ *
+ * During discovery sessions iscsid sends TEXT as non immediate,
+ * but we always only send one PDU at a time.
*/
if (conn->c_stage == ISCSI_CONN_STARTED &&
!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
@@ -620,22 +709,28 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
{
struct iscsi_session *session = conn->session;
struct iscsi_host *ihost = shost_priv(session->host);
+ uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK;
struct iscsi_task *task;
itt_t itt;
if (session->state == ISCSI_STATE_TERMINATE)
return NULL;
- if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
- hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+ if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) {
/*
* Login and Text are sent serially, in
* request-followed-by-response sequence.
* Same task can be used. Same ITT must be used.
* Note that login_task is preallocated at conn_create().
*/
+ if (conn->login_task->state != ISCSI_TASK_FREE) {
+ iscsi_conn_printk(KERN_ERR, conn, "Login/Text in "
+ "progress. Cannot start new task.\n");
+ return NULL;
+ }
+
task = conn->login_task;
- else {
+ } else {
if (session->state != ISCSI_STATE_LOGGED_IN)
return NULL;
@@ -1357,6 +1452,7 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_task);
**/
static int iscsi_data_xmit(struct iscsi_conn *conn)
{
+ struct iscsi_task *task;
int rc = 0;
spin_lock_bh(&conn->session->lock);
@@ -1394,11 +1490,8 @@ check_mgmt:
/* process pending command queue */
while (!list_empty(&conn->cmdqueue)) {
- if (conn->tmf_state == TMF_QUEUED)
- break;
-
- conn->task = list_entry(conn->cmdqueue.next,
- struct iscsi_task, running);
+ conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task,
+ running);
list_del_init(&conn->task->running);
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
fail_scsi_task(conn->task, DID_IMM_RETRY);
@@ -1406,7 +1499,7 @@ check_mgmt:
}
rc = iscsi_prep_scsi_cmd_pdu(conn->task);
if (rc) {
- if (rc == -ENOMEM) {
+ if (rc == -ENOMEM || rc == -EACCES) {
list_add_tail(&conn->task->running,
&conn->cmdqueue);
conn->task = NULL;
@@ -1428,17 +1521,18 @@ check_mgmt:
}
while (!list_empty(&conn->requeue)) {
- if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL)
- break;
-
/*
* we always do fastlogout - conn stop code will clean up.
*/
if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
break;
- conn->task = list_entry(conn->requeue.next,
- struct iscsi_task, running);
+ task = list_entry(conn->requeue.next, struct iscsi_task,
+ running);
+ if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT))
+ break;
+
+ conn->task = task;
list_del_init(&conn->task->running);
conn->task->state = ISCSI_TASK_RUNNING;
rc = iscsi_xmit_task(conn);
@@ -1591,7 +1685,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
if (!ihost->workq) {
reason = iscsi_prep_scsi_cmd_pdu(task);
if (reason) {
- if (reason == -ENOMEM) {
+ if (reason == -ENOMEM || reason == -EACCES) {
reason = FAILURE_OOM;
goto prepd_reject;
} else {
@@ -1643,9 +1737,21 @@ fault:
}
EXPORT_SYMBOL_GPL(iscsi_queuecommand);
-int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
+int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
{
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ scsi_track_queue_full(sdev, depth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
return sdev->queue_depth;
}
EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
@@ -1660,72 +1766,6 @@ int iscsi_target_alloc(struct scsi_target *starget)
}
EXPORT_SYMBOL_GPL(iscsi_target_alloc);
-void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
-{
- struct iscsi_session *session = cls_session->dd_data;
-
- spin_lock_bh(&session->lock);
- if (session->state != ISCSI_STATE_LOGGED_IN) {
- session->state = ISCSI_STATE_RECOVERY_FAILED;
- if (session->leadconn)
- wake_up(&session->leadconn->ehwait);
- }
- spin_unlock_bh(&session->lock);
-}
-EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
-
-int iscsi_eh_target_reset(struct scsi_cmnd *sc)
-{
- struct iscsi_cls_session *cls_session;
- struct iscsi_session *session;
- struct iscsi_conn *conn;
-
- cls_session = starget_to_session(scsi_target(sc->device));
- session = cls_session->dd_data;
- conn = session->leadconn;
-
- mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
- if (session->state == ISCSI_STATE_TERMINATE) {
-failed:
- ISCSI_DBG_EH(session,
- "failing target reset: Could not log back into "
- "target [age %d]\n",
- session->age);
- spin_unlock_bh(&session->lock);
- mutex_unlock(&session->eh_mutex);
- return FAILED;
- }
-
- spin_unlock_bh(&session->lock);
- mutex_unlock(&session->eh_mutex);
- /*
- * we drop the lock here but the leadconn cannot be destoyed while
- * we are in the scsi eh
- */
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-
- ISCSI_DBG_EH(session, "wait for relogin\n");
- wait_event_interruptible(conn->ehwait,
- session->state == ISCSI_STATE_TERMINATE ||
- session->state == ISCSI_STATE_LOGGED_IN ||
- session->state == ISCSI_STATE_RECOVERY_FAILED);
- if (signal_pending(current))
- flush_signals(current);
-
- mutex_lock(&session->eh_mutex);
- spin_lock_bh(&session->lock);
- if (session->state == ISCSI_STATE_LOGGED_IN) {
- ISCSI_DBG_EH(session,
- "target reset succeeded\n");
- } else
- goto failed;
- spin_unlock_bh(&session->lock);
- mutex_unlock(&session->eh_mutex);
- return SUCCESS;
-}
-EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
-
static void iscsi_tmf_timedout(unsigned long data)
{
struct iscsi_conn *conn = (struct iscsi_conn *)data;
@@ -2108,6 +2148,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
spin_lock_bh(&session->lock);
fail_scsi_task(task, DID_ABORT);
conn->tmf_state = TMF_INITIAL;
+ memset(hdr, 0, sizeof(*hdr));
spin_unlock_bh(&session->lock);
iscsi_start_tx(conn);
goto success_unlocked;
@@ -2118,6 +2159,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
case TMF_NOT_FOUND:
if (!sc->SCp.ptr) {
conn->tmf_state = TMF_INITIAL;
+ memset(hdr, 0, sizeof(*hdr));
/* task completed before tmf abort response */
ISCSI_DBG_EH(session, "sc completed while abort in "
"progress\n");
@@ -2212,6 +2254,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
iscsi_suspend_tx(conn);
spin_lock_bh(&session->lock);
+ memset(hdr, 0, sizeof(*hdr));
fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
conn->tmf_state = TMF_INITIAL;
spin_unlock_bh(&session->lock);
@@ -2229,6 +2272,172 @@ done:
}
EXPORT_SYMBOL_GPL(iscsi_eh_device_reset);
+void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
+{
+ struct iscsi_session *session = cls_session->dd_data;
+
+ spin_lock_bh(&session->lock);
+ if (session->state != ISCSI_STATE_LOGGED_IN) {
+ session->state = ISCSI_STATE_RECOVERY_FAILED;
+ if (session->leadconn)
+ wake_up(&session->leadconn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+}
+EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
+
+/**
+ * iscsi_eh_session_reset - drop session and attempt relogin
+ * @sc: scsi command
+ *
+ * This function will wait for a relogin, session termination from
+ * userspace, or a recovery/replacement timeout.
+ */
+static int iscsi_eh_session_reset(struct scsi_cmnd *sc)
+{
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
+ conn = session->leadconn;
+
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_TERMINATE) {
+failed:
+ ISCSI_DBG_EH(session,
+ "failing session reset: Could not log back into "
+ "%s, %s [age %d]\n", session->targetname,
+ conn->persistent_address, session->age);
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&session->eh_mutex);
+ return FAILED;
+ }
+
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&session->eh_mutex);
+ /*
+ * we drop the lock here but the leadconn cannot be destoyed while
+ * we are in the scsi eh
+ */
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+
+ ISCSI_DBG_EH(session, "wait for relogin\n");
+ wait_event_interruptible(conn->ehwait,
+ session->state == ISCSI_STATE_TERMINATE ||
+ session->state == ISCSI_STATE_LOGGED_IN ||
+ session->state == ISCSI_STATE_RECOVERY_FAILED);
+ if (signal_pending(current))
+ flush_signals(current);
+
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGED_IN) {
+ ISCSI_DBG_EH(session,
+ "session reset succeeded for %s,%s\n",
+ session->targetname, conn->persistent_address);
+ } else
+ goto failed;
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&session->eh_mutex);
+ return SUCCESS;
+}
+
+static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
+{
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+ hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ hdr->rtt = RESERVED_ITT;
+}
+
+/**
+ * iscsi_eh_target_reset - reset target
+ * @sc: scsi command
+ *
+ * This will attempt to send a warm target reset. If that fails
+ * then we will drop the session and attempt ERL0 recovery.
+ */
+int iscsi_eh_target_reset(struct scsi_cmnd *sc)
+{
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+ struct iscsi_tm *hdr;
+ int rc = FAILED;
+
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
+
+ ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc,
+ session->targetname);
+
+ mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ /*
+ * Just check if we are not logged in. We cannot check for
+ * the phase because the reset could come from a ioctl.
+ */
+ if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
+ goto unlock;
+ conn = session->leadconn;
+
+ /* only have one tmf outstanding at a time */
+ if (conn->tmf_state != TMF_INITIAL)
+ goto unlock;
+ conn->tmf_state = TMF_QUEUED;
+
+ hdr = &conn->tmhdr;
+ iscsi_prep_tgt_reset_pdu(sc, hdr);
+
+ if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+ session->tgt_reset_timeout)) {
+ rc = FAILED;
+ goto unlock;
+ }
+
+ switch (conn->tmf_state) {
+ case TMF_SUCCESS:
+ break;
+ case TMF_TIMEDOUT:
+ spin_unlock_bh(&session->lock);
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ goto done;
+ default:
+ conn->tmf_state = TMF_INITIAL;
+ goto unlock;
+ }
+
+ rc = SUCCESS;
+ spin_unlock_bh(&session->lock);
+
+ iscsi_suspend_tx(conn);
+
+ spin_lock_bh(&session->lock);
+ memset(hdr, 0, sizeof(*hdr));
+ fail_scsi_tasks(conn, -1, DID_ERROR);
+ conn->tmf_state = TMF_INITIAL;
+ spin_unlock_bh(&session->lock);
+
+ iscsi_start_tx(conn);
+ goto done;
+
+unlock:
+ spin_unlock_bh(&session->lock);
+done:
+ ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname,
+ rc == SUCCESS ? "SUCCESS" : "FAILED");
+ mutex_unlock(&session->eh_mutex);
+
+ if (rc == FAILED)
+ rc = iscsi_eh_session_reset(sc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
+
/*
* Pre-allocate a pool of @max items of @item_size. By default, the pool
* should be accessed via kfifo_{get,put} on q->queue.
@@ -2495,6 +2704,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
session->host = shost;
session->state = ISCSI_STATE_FREE;
session->fast_abort = 1;
+ session->tgt_reset_timeout = 30;
session->lu_reset_timeout = 15;
session->abort_timeout = 10;
session->scsi_cmds_max = scsi_cmds;
@@ -2856,6 +3066,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
spin_lock_bh(&session->lock);
fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED);
fail_mgmt_tasks(session, conn);
+ memset(&conn->tmhdr, 0, sizeof(conn->tmhdr));
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
}
@@ -2932,6 +3143,9 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
case ISCSI_PARAM_LU_RESET_TMO:
sscanf(buf, "%d", &session->lu_reset_timeout);
break;
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ sscanf(buf, "%d", &session->tgt_reset_timeout);
+ break;
case ISCSI_PARAM_PING_TMO:
sscanf(buf, "%d", &conn->ping_timeout);
break;
@@ -3031,6 +3245,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
case ISCSI_PARAM_LU_RESET_TMO:
len = sprintf(buf, "%d\n", session->lu_reset_timeout);
break;
+ case ISCSI_PARAM_TGT_RESET_TMO:
+ len = sprintf(buf, "%d\n", session->tgt_reset_timeout);
+ break;
case ISCSI_PARAM_INITIAL_R2T_EN:
len = sprintf(buf, "%d\n", session->initial_r2t_en);
break;
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index 2e0746d7030..ca25ee5190b 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -1004,7 +1004,7 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
* iscsi_tcp_task_xmit - xmit normal PDU task
* @task: iscsi command task
*
- * We're expected to return 0 when everything was transmitted succesfully,
+ * We're expected to return 0 when everything was transmitted successfully,
* -EAGAIN if there's still data in the queue, or != 0 for any other kind
* of error.
*/
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index 1c558d3bce1..14b13196b22 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -820,10 +820,14 @@ void sas_slave_destroy(struct scsi_device *scsi_dev)
ata_port_disable(dev->sata_dev.ap);
}
-int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth)
+int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth,
+ int reason)
{
int res = min(new_depth, SAS_MAX_QD);
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (scsi_dev->tagged_supported)
scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev),
res);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index aa10f795163..1cc23a69db5 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -109,7 +109,8 @@ struct hbq_dmabuf {
struct lpfc_dmabuf dbuf;
uint32_t size;
uint32_t tag;
- struct lpfc_rcqe rcqe;
+ struct lpfc_cq_event cq_event;
+ unsigned long time_stamp;
};
/* Priority bit. Set value to exceed low water mark in lpfc_mem. */
@@ -201,6 +202,7 @@ struct lpfc_stats {
uint32_t elsRcvLIRR;
uint32_t elsRcvRPS;
uint32_t elsRcvRPL;
+ uint32_t elsRcvRRQ;
uint32_t elsXmitFLOGI;
uint32_t elsXmitFDISC;
uint32_t elsXmitPLOGI;
@@ -289,8 +291,8 @@ struct lpfc_vport {
uint16_t vpi;
uint16_t vfi;
- uint8_t vfi_state;
-#define LPFC_VFI_REGISTERED 0x1
+ uint8_t vpi_state;
+#define LPFC_VPI_REGISTERED 0x1
uint32_t fc_flag; /* FC flags */
/* Several of these flags are HBA centric and should be moved to
@@ -405,6 +407,7 @@ struct lpfc_vport {
uint8_t stat_data_enabled;
uint8_t stat_data_blocked;
struct list_head rcv_buffer_list;
+ unsigned long rcv_buffer_time_stamp;
uint32_t vport_flag;
#define STATIC_VPORT 1
};
@@ -527,13 +530,16 @@ struct lpfc_hba {
#define HBA_ERATT_HANDLED 0x1 /* This flag is set when eratt handled */
#define DEFER_ERATT 0x2 /* Deferred error attention in progress */
#define HBA_FCOE_SUPPORT 0x4 /* HBA function supports FCOE */
-#define HBA_RECEIVE_BUFFER 0x8 /* Rcv buffer posted to worker thread */
+#define HBA_SP_QUEUE_EVT 0x8 /* Slow-path qevt posted to worker thread*/
#define HBA_POST_RECEIVE_BUFFER 0x10 /* Rcv buffers need to be posted */
#define FCP_XRI_ABORT_EVENT 0x20
#define ELS_XRI_ABORT_EVENT 0x40
#define ASYNC_EVENT 0x80
#define LINK_DISABLED 0x100 /* Link disabled by user */
#define FCF_DISC_INPROGRESS 0x200 /* FCF discovery in progress */
+#define HBA_FIP_SUPPORT 0x400 /* FIP support in HBA */
+#define HBA_AER_ENABLED 0x800 /* AER enabled with HBA */
+ uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/
struct lpfc_dmabuf slim2p;
MAILBOX_t *mbox;
@@ -551,6 +557,7 @@ struct lpfc_hba {
uint8_t fc_linkspeed; /* Link speed after last READ_LA */
uint32_t fc_eventTag; /* event tag for link attention */
+ uint32_t link_events;
/* These fields used to be binfo */
uint32_t fc_pref_DID; /* preferred D_ID */
@@ -604,8 +611,8 @@ struct lpfc_hba {
uint32_t cfg_enable_hba_reset;
uint32_t cfg_enable_hba_heartbeat;
uint32_t cfg_enable_bg;
- uint32_t cfg_enable_fip;
uint32_t cfg_log_verbose;
+ uint32_t cfg_aer_support;
lpfc_vpd_t vpd; /* vital product data */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index e1a30a16a9f..91542f786ed 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -23,12 +23,14 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
+#include <linux/aer.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -98,6 +100,28 @@ lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr,
return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
}
+/**
+ * lpfc_enable_fip_show - Return the fip mode of the HBA
+ * @dev: class unused variable.
+ * @attr: device attribute, not used.
+ * @buf: on return contains the module description text.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_enable_fip_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+
+ if (phba->hba_flag & HBA_FIP_SUPPORT)
+ return snprintf(buf, PAGE_SIZE, "1\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "0\n");
+}
+
static ssize_t
lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -654,7 +678,7 @@ lpfc_selective_reset(struct lpfc_hba *phba)
* Notes:
* Assumes any error from lpfc_selective_reset() will be negative.
* If lpfc_selective_reset() returns zero then the length of the buffer
- * is returned which indicates succcess
+ * is returned which indicates success
*
* Returns:
* -EINVAL if the buffer does not contain the string "selective"
@@ -762,9 +786,15 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
} else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0)
- status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return -EINVAL;
+ else
+ status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
else if (strncmp(buf, "error", sizeof("error") - 1) == 0)
- status = lpfc_do_offline(phba, LPFC_EVT_KILL);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return -EINVAL;
+ else
+ status = lpfc_do_offline(phba, LPFC_EVT_KILL);
else
return -EINVAL;
@@ -1126,6 +1156,9 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
if ((val & 0x3) != val)
return -EINVAL;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ val = 0;
+
spin_lock_irq(&phba->hbalock);
old_val = phba->cfg_poll;
@@ -1589,6 +1622,7 @@ static DEVICE_ATTR(num_discovered_ports, S_IRUGO,
static DEVICE_ATTR(menlo_mgmt_mode, S_IRUGO, lpfc_mlomgmt_show, NULL);
static DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL);
static DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, NULL);
+static DEVICE_ATTR(lpfc_enable_fip, S_IRUGO, lpfc_enable_fip_show, NULL);
static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR,
lpfc_board_mode_show, lpfc_board_mode_store);
static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset);
@@ -2759,6 +2793,196 @@ static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
lpfc_link_speed_show, lpfc_link_speed_store);
/*
+# lpfc_aer_support: Support PCIe device Advanced Error Reporting (AER)
+# 0 = aer disabled or not supported
+# 1 = aer supported and enabled (default)
+# Value range is [0,1]. Default value is 1.
+*/
+
+/**
+ * lpfc_aer_support_store - Set the adapter for aer support
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string "selective".
+ * @count: unused variable.
+ *
+ * Description:
+ * If the val is 1 and currently the device's AER capability was not
+ * enabled, invoke the kernel's enable AER helper routine, trying to
+ * enable the device's AER capability. If the helper routine enabling
+ * AER returns success, update the device's cfg_aer_support flag to
+ * indicate AER is supported by the device; otherwise, if the device
+ * AER capability is already enabled to support AER, then do nothing.
+ *
+ * If the val is 0 and currently the device's AER support was enabled,
+ * invoke the kernel's disable AER helper routine. After that, update
+ * the device's cfg_aer_support flag to indicate AER is not supported
+ * by the device; otherwise, if the device AER capability is already
+ * disabled from supporting AER, then do nothing.
+ *
+ * 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_aer_support_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;
+
+ /* AER not supported on OC devices yet */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+ return -EPERM;
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
+
+ switch (val) {
+ case 0:
+ if (phba->hba_flag & HBA_AER_ENABLED) {
+ rc = pci_disable_pcie_error_reporting(phba->pcidev);
+ if (!rc) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~HBA_AER_ENABLED;
+ spin_unlock_irq(&phba->hbalock);
+ phba->cfg_aer_support = 0;
+ rc = strlen(buf);
+ } else
+ rc = -EPERM;
+ } else {
+ phba->cfg_aer_support = 0;
+ rc = strlen(buf);
+ }
+ break;
+ case 1:
+ if (!(phba->hba_flag & HBA_AER_ENABLED)) {
+ rc = pci_enable_pcie_error_reporting(phba->pcidev);
+ if (!rc) {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag |= HBA_AER_ENABLED;
+ spin_unlock_irq(&phba->hbalock);
+ phba->cfg_aer_support = 1;
+ rc = strlen(buf);
+ } else
+ rc = -EPERM;
+ } else {
+ phba->cfg_aer_support = 1;
+ rc = strlen(buf);
+ }
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return rc;
+}
+
+static int lpfc_aer_support = 1;
+module_param(lpfc_aer_support, int, 1);
+MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support");
+lpfc_param_show(aer_support)
+
+/**
+ * lpfc_aer_support_init - Set the initial adapters aer support flag
+ * @phba: lpfc_hba pointer.
+ * @val: link speed value.
+ *
+ * Description:
+ * If val is in a valid range [0,1], then set the adapter's initial
+ * cfg_aer_support field. It will be up to the driver's probe_one
+ * routine to determine whether the device's AER support can be set
+ * or not.
+ *
+ * Notes:
+ * If the value is not in range log a kernel error message, and
+ * choose the default value of setting AER support and return.
+ *
+ * Returns:
+ * zero if val saved.
+ * -EINVAL val out of range
+ **/
+static int
+lpfc_aer_support_init(struct lpfc_hba *phba, int val)
+{
+ /* AER not supported on OC devices yet */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
+ phba->cfg_aer_support = 0;
+ return -EPERM;
+ }
+
+ if (val == 0 || val == 1) {
+ phba->cfg_aer_support = val;
+ return 0;
+ }
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2712 lpfc_aer_support attribute value %d out "
+ "of range, allowed values are 0|1, setting it "
+ "to default value of 1\n", val);
+ /* By default, try to enable AER on a device */
+ phba->cfg_aer_support = 1;
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR,
+ lpfc_aer_support_show, lpfc_aer_support_store);
+
+/**
+ * lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string "selective".
+ * @count: unused variable.
+ *
+ * Description:
+ * If the @buf contains 1 and the device currently has the AER support
+ * enabled, then invokes the kernel AER helper routine
+ * pci_cleanup_aer_uncorrect_error_status to clean up the uncorrectable
+ * error status register.
+ *
+ * Notes:
+ *
+ * Returns:
+ * -EINVAL if the buf does not contain the 1 or the device is not currently
+ * enabled with the AER support.
+ **/
+static ssize_t
+lpfc_aer_cleanup_state(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, rc = -1;
+
+ /* AER not supported on OC devices yet */
+ if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+ return -EPERM;
+ if (!isdigit(buf[0]))
+ return -EINVAL;
+ if (sscanf(buf, "%i", &val) != 1)
+ return -EINVAL;
+ if (val != 1)
+ return -EINVAL;
+
+ if (phba->hba_flag & HBA_AER_ENABLED)
+ rc = pci_cleanup_aer_uncorrect_error_status(phba->pcidev);
+
+ if (rc == 0)
+ return strlen(buf);
+ else
+ return -EPERM;
+}
+
+static DEVICE_ATTR(lpfc_aer_state_cleanup, S_IWUSR, NULL,
+ lpfc_aer_cleanup_state);
+
+/*
# lpfc_fcp_class: Determines FC class to use for the FCP protocol.
# Value range is [2,3]. Default value is 3.
*/
@@ -2846,7 +3070,7 @@ LPFC_ATTR_R(multi_ring_support, 1, 1, 2, "Determines number of primary "
# identifies what rctl value to configure the additional ring for.
# Value range is [1,0xff]. Default value is 4 (Unsolicated Data).
*/
-LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1,
+LPFC_ATTR_R(multi_ring_rctl, FC_RCTL_DD_UNSOL_DATA, 1,
255, "Identifies RCTL for additional ring configuration");
/*
@@ -2854,7 +3078,7 @@ LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1,
# identifies what type value to configure the additional ring for.
# Value range is [1,0xff]. Default value is 5 (LLC/SNAP).
*/
-LPFC_ATTR_R(multi_ring_type, FC_LLC_SNAP, 1,
+LPFC_ATTR_R(multi_ring_type, FC_TYPE_IP, 1,
255, "Identifies TYPE for additional ring configuration");
/*
@@ -2947,15 +3171,6 @@ LPFC_ATTR_R(enable_hba_heartbeat, 1, 0, 1, "Enable HBA Heartbeat.");
LPFC_ATTR_R(enable_bg, 0, 0, 1, "Enable BlockGuard Support");
/*
-# lpfc_enable_fip: When set, FIP is required to start discovery. If not
-# set, the driver will add an FCF record manually if the port has no
-# FCF records available and start discovery.
-# Value range is [0,1]. Default value is 1 (enabled)
-*/
-LPFC_ATTR_RW(enable_fip, 0, 0, 1, "Enable FIP Discovery");
-
-
-/*
# lpfc_prot_mask: i
# - Bit mask of host protection capabilities used to register with the
# SCSI mid-layer
@@ -3013,6 +3228,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_num_discovered_ports,
&dev_attr_menlo_mgmt_mode,
&dev_attr_lpfc_drvr_version,
+ &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_temp_sensor,
&dev_attr_lpfc_log_verbose,
&dev_attr_lpfc_lun_queue_depth,
@@ -3020,7 +3236,6 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_peer_port_login,
&dev_attr_lpfc_nodev_tmo,
&dev_attr_lpfc_devloss_tmo,
- &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_fcp_class,
&dev_attr_lpfc_use_adisc,
&dev_attr_lpfc_ack0,
@@ -3061,6 +3276,8 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_max_scsicmpl_time,
&dev_attr_lpfc_stat_data_ctrl,
&dev_attr_lpfc_prot_sg_seg_cnt,
+ &dev_attr_lpfc_aer_support,
+ &dev_attr_lpfc_aer_state_cleanup,
NULL,
};
@@ -3073,7 +3290,6 @@ struct device_attribute *lpfc_vport_attrs[] = {
&dev_attr_lpfc_lun_queue_depth,
&dev_attr_lpfc_nodev_tmo,
&dev_attr_lpfc_devloss_tmo,
- &dev_attr_lpfc_enable_fip,
&dev_attr_lpfc_hba_queue_depth,
&dev_attr_lpfc_peer_port_login,
&dev_attr_lpfc_restrict_login,
@@ -3147,7 +3363,7 @@ sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr,
* sysfs_ctlreg_read - Read method for reading from ctlreg
* @kobj: kernel kobject that contains the kernel class device.
* @bin_attr: kernel attributes passed to us.
- * @buf: if succesful contains the data from the adapter IOREG space.
+ * @buf: if successful contains the data from the adapter IOREG space.
* @off: offset into buffer to beginning of data.
* @count: bytes to transfer.
*
@@ -3815,7 +4031,11 @@ lpfc_get_stats(struct Scsi_Host *shost)
hs->invalid_crc_count -= lso->invalid_crc_count;
hs->error_frames -= lso->error_frames;
- if (phba->fc_topology == TOPOLOGY_LOOP) {
+ if (phba->hba_flag & HBA_FCOE_SUPPORT) {
+ hs->lip_count = -1;
+ hs->nos_count = (phba->link_events >> 1);
+ hs->nos_count -= lso->link_events;
+ } else if (phba->fc_topology == TOPOLOGY_LOOP) {
hs->lip_count = (phba->fc_eventTag >> 1);
hs->lip_count -= lso->link_events;
hs->nos_count = -1;
@@ -3906,7 +4126,10 @@ lpfc_reset_stats(struct Scsi_Host *shost)
lso->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord;
lso->invalid_crc_count = pmb->un.varRdLnk.crcCnt;
lso->error_frames = pmb->un.varRdLnk.crcCnt;
- lso->link_events = (phba->fc_eventTag >> 1);
+ if (phba->hba_flag & HBA_FCOE_SUPPORT)
+ lso->link_events = (phba->link_events >> 1);
+ else
+ lso->link_events = (phba->fc_eventTag >> 1);
psli->stats_start = get_seconds();
@@ -4222,14 +4445,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_enable_hba_reset_init(phba, lpfc_enable_hba_reset);
lpfc_enable_hba_heartbeat_init(phba, lpfc_enable_hba_heartbeat);
lpfc_enable_bg_init(phba, lpfc_enable_bg);
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ phba->cfg_poll = 0;
+ else
phba->cfg_poll = lpfc_poll;
phba->cfg_soft_wwnn = 0L;
phba->cfg_soft_wwpn = 0L;
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
lpfc_prot_sg_seg_cnt_init(phba, lpfc_prot_sg_seg_cnt);
lpfc_hba_queue_depth_init(phba, lpfc_hba_queue_depth);
- lpfc_enable_fip_init(phba, lpfc_enable_fip);
lpfc_hba_log_verbose_init(phba, lpfc_log_verbose);
+ lpfc_aer_support_init(phba, lpfc_aer_support);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index da6bf5aac9d..a5d9048235d 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -26,6 +26,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_bsg_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -148,8 +149,8 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job)
cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
cmd->un.genreq64.w5.hcsw.Dfctl = 0;
- cmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
- cmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+ cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+ cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
cmd->ulpBdeCount = 1;
cmd->ulpLe = 1;
cmd->ulpClass = CLASS3;
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 0830f37409a..650494d622c 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -49,6 +49,8 @@ void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
void lpfc_request_features(struct lpfc_hba *, struct lpfcMboxq *);
struct lpfc_vport *lpfc_find_vport_by_did(struct lpfc_hba *, uint32_t);
+void lpfc_cleanup_rcv_buffers(struct lpfc_vport *);
+void lpfc_rcv_seq_check_edtov(struct lpfc_vport *);
void lpfc_cleanup_rpis(struct lpfc_vport *, int);
int lpfc_linkdown(struct lpfc_hba *);
void lpfc_linkdown_port(struct lpfc_vport *);
@@ -144,6 +146,8 @@ void lpfc_hb_timeout_handler(struct lpfc_hba *);
void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
+void lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *);
int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t);
int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int);
void lpfc_fdmi_tmo(unsigned long);
@@ -188,7 +192,7 @@ int lpfc_mbox_tmo_val(struct lpfc_hba *, int);
void lpfc_init_vfi(struct lpfcMboxq *, struct lpfc_vport *);
void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t);
void lpfc_init_vpi(struct lpfc_hba *, struct lpfcMboxq *, uint16_t);
-void lpfc_unreg_vfi(struct lpfcMboxq *, uint16_t);
+void lpfc_unreg_vfi(struct lpfcMboxq *, struct lpfc_vport *);
void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t);
void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *);
@@ -212,7 +216,10 @@ void lpfc_stop_vport_timers(struct lpfc_vport *);
void lpfc_poll_timeout(unsigned long ptr);
void lpfc_poll_start_timer(struct lpfc_hba *);
void lpfc_poll_eratt(unsigned long);
-void lpfc_sli_poll_fcp_ring(struct lpfc_hba *);
+int
+lpfc_sli_handle_fast_ring_event(struct lpfc_hba *,
+ struct lpfc_sli_ring *, uint32_t);
+
struct lpfc_iocbq * lpfc_sli_get_iocbq(struct lpfc_hba *);
void lpfc_sli_release_iocbq(struct lpfc_hba *, struct lpfc_iocbq *);
uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *);
@@ -235,7 +242,7 @@ void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *);
int lpfc_sli_check_eratt(struct lpfc_hba *);
void lpfc_sli_handle_slow_ring_event(struct lpfc_hba *,
struct lpfc_sli_ring *, uint32_t);
-int lpfc_sli4_handle_received_buffer(struct lpfc_hba *);
+void lpfc_sli4_handle_received_buffer(struct lpfc_hba *, struct hbq_dmabuf *);
void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, uint32_t);
@@ -361,6 +368,7 @@ void lpfc_stop_port(struct lpfc_hba *);
void lpfc_parse_fcoe_conf(struct lpfc_hba *, uint8_t *, uint32_t);
int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *, int);
void lpfc_start_fdiscs(struct lpfc_hba *phba);
+struct lpfc_vport *lpfc_find_vport_by_vpid(struct lpfc_hba *, uint16_t);
#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
#define HBA_EVENT_RSCN 5
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 9a1bd9534d7..0ebcd9baca7 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -31,6 +31,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -87,7 +88,6 @@ void
lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct lpfc_iocbq *piocbq)
{
-
struct lpfc_dmabuf *mp = NULL;
IOCB_t *icmd = &piocbq->iocb;
int i;
@@ -160,6 +160,39 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
}
+/**
+ * lpfc_sli4_ct_abort_unsol_event - Default handle for sli4 unsol abort
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to the driver internal I/O ring.
+ * @piocbq: Pointer to the IOCBQ.
+ *
+ * This function serves as the default handler for the sli4 unsolicited
+ * abort event. It shall be invoked when there is no application interface
+ * registered unsolicited abort handler. This handler does nothing but
+ * just simply releases the dma buffer used by the unsol abort event.
+ **/
+void
+lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba,
+ struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *piocbq)
+{
+ IOCB_t *icmd = &piocbq->iocb;
+ struct lpfc_dmabuf *bdeBuf;
+ uint32_t size;
+
+ /* Forward abort event to any process registered to receive ct event */
+ lpfc_bsg_ct_unsol_event(phba, pring, piocbq);
+
+ /* If there is no BDE associated with IOCB, there is nothing to do */
+ if (icmd->ulpBdeCount == 0)
+ return;
+ bdeBuf = piocbq->context2;
+ piocbq->context2 = NULL;
+ size = icmd->un.cont64[0].tus.f.bdeSize;
+ lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf, size);
+ lpfc_in_buf_free(phba, bdeBuf);
+}
+
static void
lpfc_free_ct_rsp(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
{
@@ -304,8 +337,8 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
/* Fill in rest of iocb */
icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
icmd->un.genreq64.w5.hcsw.Dfctl = 0;
- icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
- icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+ icmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+ icmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
if (!tmo) {
/* FC spec states we need 3 * ratov for CT requests */
@@ -363,9 +396,14 @@ lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,
outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt);
if (!outmp)
return -ENOMEM;
-
+ /*
+ * Form the CT IOCB. The total number of BDEs in this IOCB
+ * is the single command plus response count from
+ * lpfc_alloc_ct_rsp.
+ */
+ cnt += 1;
status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp, 0,
- cnt+1, 0, retry);
+ cnt, 0, retry);
if (status) {
lpfc_free_ct_rsp(phba, outmp);
return -ENOMEM;
@@ -501,6 +539,9 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
SLI_CTNS_GFF_ID,
0, Did) == 0)
vport->num_disc_nodes++;
+ else
+ lpfc_setup_disc_node
+ (vport, Did);
}
else {
lpfc_debugfs_disc_trc(vport,
@@ -1209,7 +1250,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
be16_to_cpu(SLI_CTNS_RFF_ID);
CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID);
CtReq->un.rff.fbits = FC4_FEATURE_INIT;
- CtReq->un.rff.type_code = FC_FCP_DATA;
+ CtReq->un.rff.type_code = FC_TYPE_FCP;
cmpl = lpfc_cmpl_ct_cmd_rff_id;
break;
}
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 8d0f0de76b6..391584183d8 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -926,7 +926,7 @@ lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
goto out;
/* Round to page boundry */
- printk(KERN_ERR "BLKGRD %s: _dump_buf_data=0x%p\n",
+ printk(KERN_ERR "9059 BLKGRD: %s: _dump_buf_data=0x%p\n",
__func__, _dump_buf_data);
debug->buffer = _dump_buf_data;
if (!debug->buffer) {
@@ -956,8 +956,8 @@ lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
goto out;
/* Round to page boundry */
- printk(KERN_ERR "BLKGRD %s: _dump_buf_dif=0x%p file=%s\n", __func__,
- _dump_buf_dif, file->f_dentry->d_name.name);
+ printk(KERN_ERR "9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%s\n",
+ __func__, _dump_buf_dif, file->f_dentry->d_name.name);
debug->buffer = _dump_buf_dif;
if (!debug->buffer) {
kfree(debug);
@@ -1377,7 +1377,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
debugfs_create_dir(name, phba->hba_debugfs_root);
if (!vport->vport_debugfs_root) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0417 Cant create debugfs");
+ "0417 Cant create debugfs\n");
goto debug_failed;
}
atomic_inc(&phba->debugfs_vport_count);
@@ -1430,7 +1430,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
vport, &lpfc_debugfs_op_nodelist);
if (!vport->debug_nodelist) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
- "0409 Cant create debugfs nodelist");
+ "0409 Cant create debugfs nodelist\n");
goto debug_failed;
}
debug_failed:
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
index 1142070e948..2851d75ffc6 100644
--- a/drivers/scsi/lpfc/lpfc_disc.h
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -19,7 +19,7 @@
*******************************************************************/
#define FC_MAX_HOLD_RSCN 32 /* max number of deferred RSCNs */
-#define FC_MAX_NS_RSP 65536 /* max size NameServer rsp */
+#define FC_MAX_NS_RSP 64512 /* max size NameServer rsp */
#define FC_MAXLOOP 126 /* max devices supported on a fc loop */
#define LPFC_DISC_FLOGI_TMO 10 /* Discovery FLOGI ratov */
@@ -105,8 +105,6 @@ struct lpfc_nodelist {
struct lpfc_vport *vport;
struct lpfc_work_evt els_retry_evt;
struct lpfc_work_evt dev_loss_evt;
- unsigned long last_ramp_up_time; /* jiffy of last ramp up */
- unsigned long last_q_full_time; /* jiffy of last queue full */
struct kref kref;
atomic_t cmd_pending;
uint32_t cmd_qdepth;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 45337cd23fe..ce522702a6c 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -173,13 +173,26 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
* in FIP mode send FLOGI, FDISC and LOGO as FIP frames.
*/
if ((did == Fabric_DID) &&
- bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags) &&
+ (phba->hba_flag & HBA_FIP_SUPPORT) &&
((elscmd == ELS_CMD_FLOGI) ||
(elscmd == ELS_CMD_FDISC) ||
(elscmd == ELS_CMD_LOGO)))
- elsiocb->iocb_flag |= LPFC_FIP_ELS;
+ switch (elscmd) {
+ case ELS_CMD_FLOGI:
+ elsiocb->iocb_flag |= ((ELS_ID_FLOGI << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
+ case ELS_CMD_FDISC:
+ elsiocb->iocb_flag |= ((ELS_ID_FDISC << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
+ case ELS_CMD_LOGO:
+ elsiocb->iocb_flag |= ((ELS_ID_LOGO << LPFC_FIP_ELS_ID_SHIFT)
+ & LPFC_FIP_ELS_ID_MASK);
+ break;
+ }
else
- elsiocb->iocb_flag &= ~LPFC_FIP_ELS;
+ elsiocb->iocb_flag &= ~LPFC_FIP_ELS_ID_MASK;
icmd = &elsiocb->iocb;
@@ -591,7 +604,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
} else {
ndlp->nlp_type |= NLP_FABRIC;
lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
- if (vport->vfi_state & LPFC_VFI_REGISTERED) {
+ if (vport->vpi_state & LPFC_VPI_REGISTERED) {
lpfc_start_fdiscs(phba);
lpfc_do_scr_ns_plogi(phba, vport);
} else
@@ -802,7 +815,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
/* FLOGI completes successfully */
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "0101 FLOGI completes sucessfully "
+ "0101 FLOGI completes successfully "
"Data: x%x x%x x%x x%x\n",
irsp->un.ulpWord[4], sp->cmn.e_d_tov,
sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution);
@@ -2452,6 +2465,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
*/
del_timer_sync(&ndlp->nlp_delayfunc);
retry = ndlp->nlp_retry;
+ ndlp->nlp_retry = 0;
switch (cmd) {
case ELS_CMD_FLOGI:
@@ -2711,12 +2725,16 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
!lpfc_error_lost_link(irsp)) {
/* FLOGI retry policy */
retry = 1;
- maxretry = 48;
- if (cmdiocb->retry >= 32)
+ /* retry forever */
+ maxretry = 0;
+ if (cmdiocb->retry >= 100)
+ delay = 5000;
+ else if (cmdiocb->retry >= 32)
delay = 1000;
}
- if ((++cmdiocb->retry) >= maxretry) {
+ cmdiocb->retry++;
+ if (maxretry && (cmdiocb->retry >= maxretry)) {
phba->fc_stat.elsRetryExceeded++;
retry = 0;
}
@@ -4133,7 +4151,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
/* Indicate we are walking fc_rscn_id_list on this vport */
vport->fc_rscn_flush = 1;
spin_unlock_irq(shost->host_lock);
- /* Get the array count after sucessfully have the token */
+ /* Get the array count after successfully have the token */
rscn_cnt = vport->fc_rscn_id_cnt;
/* If we are already processing an RSCN, save the received
* RSCN payload buffer, cmdiocb->context2 to process later.
@@ -4503,6 +4521,29 @@ lpfc_els_rcv_lirr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
}
/**
+ * lpfc_els_rcv_rrq - Process an unsolicited rrq iocb
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes a Reinstate Recovery Qualifier (RRQ) IOCB
+ * received as an ELS unsolicited event. A request to RRQ shall only
+ * be accepted if the Originator Nx_Port N_Port_ID or the Responder
+ * Nx_Port N_Port_ID of the target Exchange is the same as the
+ * N_Port_ID of the Nx_Port that makes the request. If the RRQ is
+ * not accepted, an LS_RJT with reason code "Unable to perform
+ * command request" and reason code explanation "Invalid Originator
+ * S_ID" shall be returned. For now, we just unconditionally accept
+ * RRQ from the target.
+ **/
+static void
+lpfc_els_rcv_rrq(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_nodelist *ndlp)
+{
+ lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+}
+
+/**
* lpfc_els_rsp_rps_acc - Completion callbk func for MBX_READ_LNK_STAT mbox cmd
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
@@ -5396,7 +5437,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (lpfc_els_chk_latt(vport))
goto dropit;
- /* Ignore traffic recevied during vport shutdown. */
+ /* Ignore traffic received during vport shutdown. */
if (vport->load_flag & FC_UNLOADING)
goto dropit;
@@ -5618,6 +5659,16 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if (newnode)
lpfc_nlp_put(ndlp);
break;
+ case ELS_CMD_RRQ:
+ lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
+ "RCV RRQ: did:x%x/ste:x%x flg:x%x",
+ did, vport->port_state, ndlp->nlp_flag);
+
+ phba->fc_stat.elsRcvRRQ++;
+ lpfc_els_rcv_rrq(vport, elsiocb, ndlp);
+ if (newnode)
+ lpfc_nlp_put(ndlp);
+ break;
default:
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
"RCV ELS cmd: cmd:x%x did:x%x/ste:x%x",
@@ -5670,7 +5721,7 @@ dropit:
* NULL - No vport with the matching @vpi found
* Otherwise - Address to the vport with the matching @vpi.
**/
-static struct lpfc_vport *
+struct lpfc_vport *
lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi)
{
struct lpfc_vport *vport;
@@ -6024,11 +6075,6 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpStatus, irsp->un.ulpWord[4]);
goto fdisc_failed;
}
- if (vport->fc_vport->vport_state == FC_VPORT_INITIALIZING)
- lpfc_vport_set_state(vport, FC_VPORT_FAILED);
- lpfc_nlp_put(ndlp);
- /* giving up on FDISC. Cancel discovery timer */
- lpfc_can_disctmo(vport);
spin_lock_irq(shost->host_lock);
vport->fc_flag |= FC_FABRIC;
if (vport->phba->fc_topology == TOPOLOGY_LOOP)
@@ -6107,6 +6153,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int did = ndlp->nlp_DID;
int rc;
+ vport->port_state = LPFC_FDISC;
cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did,
ELS_CMD_FDISC);
@@ -6172,7 +6219,6 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
return 1;
}
lpfc_vport_set_state(vport, FC_VPORT_INITIALIZING);
- vport->port_state = LPFC_FDISC;
return 0;
}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index e6a47e25b21..3b942442765 100644..100755
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -525,8 +525,6 @@ lpfc_work_done(struct lpfc_hba *phba)
spin_unlock_irq(&phba->hbalock);
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
}
- if (phba->hba_flag & HBA_RECEIVE_BUFFER)
- lpfc_sli4_handle_received_buffer(phba);
}
vports = lpfc_create_vport_work_array(phba);
@@ -568,8 +566,9 @@ lpfc_work_done(struct lpfc_hba *phba)
pring = &phba->sli.ring[LPFC_ELS_RING];
status = (ha_copy & (HA_RXMASK << (4*LPFC_ELS_RING)));
status >>= (4*LPFC_ELS_RING);
- if ((status & HA_RXMASK)
- || (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
+ if ((status & HA_RXMASK) ||
+ (pring->flag & LPFC_DEFERRED_RING_EVENT) ||
+ (phba->hba_flag & HBA_SP_QUEUE_EVT)) {
if (pring->flag & LPFC_STOP_IOCB_EVENT) {
pring->flag |= LPFC_DEFERRED_RING_EVENT;
/* Set the lpfc data pending flag */
@@ -688,7 +687,8 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
lpfc_unreg_rpi(vport, ndlp);
/* Leave Fabric nodes alone on link down */
- if (!remove && ndlp->nlp_type & NLP_FABRIC)
+ if ((phba->sli_rev < LPFC_SLI_REV4) &&
+ (!remove && ndlp->nlp_type & NLP_FABRIC))
continue;
rc = lpfc_disc_state_machine(vport, ndlp, NULL,
remove
@@ -706,6 +706,9 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
void
lpfc_port_link_failure(struct lpfc_vport *vport)
{
+ /* Cleanup any outstanding received buffers */
+ lpfc_cleanup_rcv_buffers(vport);
+
/* Cleanup any outstanding RSCN activity */
lpfc_els_flush_rscn(vport);
@@ -1015,13 +1018,12 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
mempool_free(mboxq, phba->mbox_mem_pool);
return;
}
- if (vport->port_state != LPFC_FLOGI) {
- spin_lock_irqsave(&phba->hbalock, flags);
- phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
- phba->hba_flag &= ~FCF_DISC_INPROGRESS;
- spin_unlock_irqrestore(&phba->hbalock, flags);
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
+ if (vport->port_state != LPFC_FLOGI)
lpfc_initial_flogi(vport);
- }
mempool_free(mboxq, phba->mbox_mem_pool);
return;
@@ -1199,6 +1201,7 @@ lpfc_register_fcf(struct lpfc_hba *phba)
/* If the FCF is not availabe do nothing. */
if (!(phba->fcf.fcf_flag & FCF_AVAILABLE)) {
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}
@@ -1216,15 +1219,23 @@ lpfc_register_fcf(struct lpfc_hba *phba)
fcf_mbxq = mempool_alloc(phba->mbox_mem_pool,
GFP_KERNEL);
- if (!fcf_mbxq)
+ if (!fcf_mbxq) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
return;
+ }
lpfc_reg_fcfi(phba, fcf_mbxq);
fcf_mbxq->vport = phba->pport;
fcf_mbxq->mbox_cmpl = lpfc_mbx_cmpl_reg_fcfi;
rc = lpfc_sli_issue_mbox(phba, fcf_mbxq, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED)
+ if (rc == MBX_NOT_FINISHED) {
+ spin_lock_irqsave(&phba->hbalock, flags);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irqrestore(&phba->hbalock, flags);
mempool_free(fcf_mbxq, phba->mbox_mem_pool);
+ }
return;
}
@@ -1253,13 +1264,27 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
uint16_t *vlan_id)
{
struct lpfc_fcf_conn_entry *conn_entry;
+ int i, j, fcf_vlan_id = 0;
+
+ /* Find the lowest VLAN id in the FCF record */
+ for (i = 0; i < 512; i++) {
+ if (new_fcf_record->vlan_bitmap[i]) {
+ fcf_vlan_id = i * 8;
+ j = 0;
+ while (!((new_fcf_record->vlan_bitmap[i] >> j) & 1)) {
+ j++;
+ fcf_vlan_id++;
+ }
+ break;
+ }
+ }
/* If FCF not available return 0 */
if (!bf_get(lpfc_fcf_record_fcf_avail, new_fcf_record) ||
!bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record))
return 0;
- if (!phba->cfg_enable_fip) {
+ if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
*boot_flag = 0;
*addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
new_fcf_record);
@@ -1286,7 +1311,11 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
if (*addr_mode & LPFC_FCF_FPMA)
*addr_mode = LPFC_FCF_FPMA;
- *vlan_id = 0xFFFF;
+ /* If FCF record report a vlan id use that vlan id */
+ if (fcf_vlan_id)
+ *vlan_id = fcf_vlan_id;
+ else
+ *vlan_id = 0xFFFF;
return 1;
}
@@ -1384,8 +1413,15 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
(*addr_mode & LPFC_FCF_FPMA))
*addr_mode = LPFC_FCF_FPMA;
+ /* If matching connect list has a vlan id, use it */
if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID)
*vlan_id = conn_entry->conn_rec.vlan_tag;
+ /*
+ * If no vlan id is specified in connect list, use the vlan id
+ * in the FCF record
+ */
+ else if (fcf_vlan_id)
+ *vlan_id = fcf_vlan_id;
else
*vlan_id = 0xFFFF;
@@ -1423,6 +1459,15 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
if (phba->link_state >= LPFC_LINK_UP)
lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
+ else {
+ /*
+ * Do not continue FCF discovery and clear FCF_DISC_INPROGRESS
+ * flag
+ */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irq(&phba->hbalock);
+ }
if (unreg_fcf) {
spin_lock_irq(&phba->hbalock);
@@ -1659,9 +1704,8 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_initial_fdisc(vport);
else {
lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
- lpfc_printf_vlog(vport, KERN_ERR,
- LOG_ELS,
- "2606 No NPIV Fabric support\n");
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "2606 No NPIV Fabric support\n");
}
return;
}
@@ -1756,8 +1800,8 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
goto fail_free_mem;
}
- /* Mark the vport has registered with its VFI */
- vport->vfi_state |= LPFC_VFI_REGISTERED;
+ /* The VPI is implicitly registered when the VFI is registered */
+ vport->vpi_state |= LPFC_VPI_REGISTERED;
if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
lpfc_start_fdiscs(phba);
@@ -1861,7 +1905,10 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
if (phba->fc_topology == TOPOLOGY_LOOP) {
phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
- if (phba->cfg_enable_npiv)
+ /* if npiv is enabled and this adapter supports npiv log
+ * a message that npiv is not supported in this topology
+ */
+ if (phba->cfg_enable_npiv && phba->max_vpi)
lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
"1309 Link Up Event npiv not supported in loop "
"topology\n");
@@ -1955,7 +2002,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
* is phase 1 implementation that support FCF index 0 and driver
* defaults.
*/
- if (phba->cfg_enable_fip == 0) {
+ if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
fcf_record = kzalloc(sizeof(struct fcf_record),
GFP_KERNEL);
if (unlikely(!fcf_record)) {
@@ -2085,6 +2132,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
else
phba->sli.sli_flag &= ~LPFC_MENLO_MAINT;
+ phba->link_events++;
if (la->attType == AT_LINK_UP && (!la->mm)) {
phba->fc_stat.LinkUp++;
if (phba->link_flag & LS_LOOPBACK_MODE) {
@@ -2211,13 +2259,14 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
mb->mbxStatus);
break;
}
+ vport->vpi_state &= ~LPFC_VPI_REGISTERED;
vport->unreg_vpi_cmpl = VPORT_OK;
mempool_free(pmb, phba->mbox_mem_pool);
/*
* This shost reference might have been taken at the beginning of
* lpfc_vport_delete()
*/
- if (vport->load_flag & FC_UNLOADING)
+ if ((vport->load_flag & FC_UNLOADING) && (vport != phba->pport))
scsi_host_put(shost);
}
@@ -2268,6 +2317,7 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
goto out;
}
+ vport->vpi_state |= LPFC_VPI_REGISTERED;
vport->num_disc_nodes = 0;
/* go thru NPR list and issue ELS PLOGIs */
if (vport->fc_npr_cnt)
@@ -3077,7 +3127,7 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
struct lpfc_sli *psli;
struct lpfc_sli_ring *pring;
struct lpfc_iocbq *iocb, *next_iocb;
- uint32_t rpi, i;
+ uint32_t i;
lpfc_fabric_abort_nport(ndlp);
@@ -3086,7 +3136,6 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
* by firmware with a no rpi error.
*/
psli = &phba->sli;
- rpi = ndlp->nlp_rpi;
if (ndlp->nlp_flag & NLP_RPI_VALID) {
/* Now process each ring */
for (i = 0; i < psli->num_rings; i++) {
@@ -4322,6 +4371,14 @@ lpfc_fcf_inuse(struct lpfc_hba *phba)
ret = 1;
spin_unlock_irq(shost->host_lock);
goto out;
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "2624 RPI %x DID %x flg %x still "
+ "logged in\n",
+ ndlp->nlp_rpi, ndlp->nlp_DID,
+ ndlp->nlp_flag);
+ if (ndlp->nlp_flag & NLP_RPI_VALID)
+ ret = 1;
}
}
spin_unlock_irq(shost->host_lock);
@@ -4400,7 +4457,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
*/
if (!(phba->hba_flag & HBA_FCOE_SUPPORT) ||
!(phba->fcf.fcf_flag & FCF_REGISTERED) ||
- (phba->cfg_enable_fip == 0)) {
+ (!(phba->hba_flag & HBA_FIP_SUPPORT))) {
spin_unlock_irq(&phba->hbalock);
return;
}
@@ -4409,6 +4466,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
if (lpfc_fcf_inuse(phba))
return;
+ /* At this point, all discovery is aborted */
+ phba->pport->port_state = LPFC_VPORT_UNKNOWN;
/* Unregister VPIs */
vports = lpfc_create_vport_work_array(phba);
@@ -4416,8 +4475,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED))
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
lpfc_mbx_unreg_vpi(vports[i]);
- vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
- vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
+ vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -4431,7 +4490,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
return;
}
- lpfc_unreg_vfi(mbox, phba->pport->vfi);
+ lpfc_unreg_vfi(mbox, phba->pport);
mbox->vport = phba->pport;
mbox->mbox_cmpl = lpfc_unregister_vfi_cmpl;
@@ -4512,8 +4571,10 @@ lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba,
/* Free the current connect table */
list_for_each_entry_safe(conn_entry, next_conn_entry,
- &phba->fcf_conn_rec_list, list)
+ &phba->fcf_conn_rec_list, list) {
+ list_del_init(&conn_entry->list);
kfree(conn_entry);
+ }
conn_hdr = (struct lpfc_fcf_conn_hdr *) buff;
record_count = conn_hdr->length * sizeof(uint32_t)/
@@ -4569,14 +4630,6 @@ lpfc_read_fcoe_param(struct lpfc_hba *phba,
(fcoe_param_hdr->length != FCOE_PARAM_LENGTH))
return;
- if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
- FIPP_MODE_ON)
- phba->cfg_enable_fip = 1;
-
- if (bf_get(lpfc_fip_param_hdr_fipp_mode, fcoe_param_hdr) ==
- FIPP_MODE_OFF)
- phba->cfg_enable_fip = 0;
-
if (fcoe_param_hdr->parm_flags & FIPP_VLAN_VALID) {
phba->valid_vlan = 1;
phba->vlan_id = le16_to_cpu(fcoe_param->vlan_tag) &
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index ccb26724dc5..c9faa1d8c3c 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1124,21 +1124,6 @@ typedef struct {
/* Number of 4-byte words in an IOCB. */
#define IOCB_WORD_SZ 8
-/* defines for type field in fc header */
-#define FC_ELS_DATA 0x1
-#define FC_LLC_SNAP 0x5
-#define FC_FCP_DATA 0x8
-#define FC_COMMON_TRANSPORT_ULP 0x20
-
-/* defines for rctl field in fc header */
-#define FC_DEV_DATA 0x0
-#define FC_UNSOL_CTL 0x2
-#define FC_SOL_CTL 0x3
-#define FC_UNSOL_DATA 0x4
-#define FC_FCP_CMND 0x6
-#define FC_ELS_REQ 0x22
-#define FC_ELS_RSP 0x23
-
/* network headers for Dfctl field */
#define FC_NET_HDR 0x20
@@ -1183,6 +1168,8 @@ typedef struct {
#define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12
#define PCI_VENDOR_ID_SERVERENGINE 0x19a2
#define PCI_DEVICE_ID_TIGERSHARK 0x0704
+#define PCI_DEVICE_ID_TOMCAT 0x0714
+#define PCI_DEVICE_ID_FALCON 0xf180
#define JEDEC_ID_ADDRESS 0x0080001c
#define FIREFLY_JEDEC_ID 0x1ACC
@@ -1444,6 +1431,7 @@ typedef struct { /* FireFly BIU registers */
#define CMD_ABORT_MXRI64_CN 0x8C
#define CMD_RCV_ELS_REQ64_CX 0x8D
#define CMD_XMIT_ELS_RSP64_CX 0x95
+#define CMD_XMIT_BLS_RSP64_CX 0x97
#define CMD_FCP_IWRITE64_CR 0x98
#define CMD_FCP_IWRITE64_CX 0x99
#define CMD_FCP_IREAD64_CR 0x9A
@@ -2306,8 +2294,7 @@ typedef struct {
uint32_t rsvd1;
uint32_t rsvd2:8;
uint32_t sid:24;
- uint32_t rsvd3;
- uint32_t rsvd4;
+ uint32_t wwn[2];
uint32_t rsvd5;
uint16_t vfi;
uint16_t vpi;
@@ -2315,8 +2302,7 @@ typedef struct {
uint32_t rsvd1;
uint32_t sid:24;
uint32_t rsvd2:8;
- uint32_t rsvd3;
- uint32_t rsvd4;
+ uint32_t wwn[2];
uint32_t rsvd5;
uint16_t vpi;
uint16_t vfi;
@@ -2326,7 +2312,13 @@ typedef struct {
/* Structure for MB Command UNREG_VPI (0x97) */
typedef struct {
uint32_t rsvd1;
- uint32_t rsvd2;
+#ifdef __BIG_ENDIAN_BITFIELD
+ uint16_t rsvd2;
+ uint16_t sli4_vpi;
+#else /* __LITTLE_ENDIAN */
+ uint16_t sli4_vpi;
+ uint16_t rsvd2;
+#endif
uint32_t rsvd3;
uint32_t rsvd4;
uint32_t rsvd5;
@@ -3547,7 +3539,7 @@ typedef struct _IOCB { /* IOCB structure */
ASYNCSTAT_FIELDS asyncstat; /* async_status iocb */
QUE_XRI64_CX_FIELDS quexri64cx; /* que_xri64_cx fields */
struct rcv_seq64 rcvseq64; /* RCV_SEQ64 and RCV_CONT64 */
-
+ struct sli4_bls_acc bls_acc; /* UNSOL ABTS BLS_ACC params */
uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */
} un;
union {
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 3689eee0453..1585148a17e 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -194,6 +194,26 @@ struct lpfc_sli4_flags {
#define lpfc_fip_flag_WORD word0
};
+struct sli4_bls_acc {
+ uint32_t word0_rsvd; /* Word0 must be reserved */
+ uint32_t word1;
+#define lpfc_abts_orig_SHIFT 0
+#define lpfc_abts_orig_MASK 0x00000001
+#define lpfc_abts_orig_WORD word1
+#define LPFC_ABTS_UNSOL_RSP 1
+#define LPFC_ABTS_UNSOL_INT 0
+ uint32_t word2;
+#define lpfc_abts_rxid_SHIFT 0
+#define lpfc_abts_rxid_MASK 0x0000FFFF
+#define lpfc_abts_rxid_WORD word2
+#define lpfc_abts_oxid_SHIFT 16
+#define lpfc_abts_oxid_MASK 0x0000FFFF
+#define lpfc_abts_oxid_WORD word2
+ uint32_t word3;
+ uint32_t word4;
+ uint32_t word5_rsvd; /* Word5 must be reserved */
+};
+
/* event queue entry structure */
struct lpfc_eqe {
uint32_t word0;
@@ -425,7 +445,7 @@ struct lpfc_wqe_generic{
#define lpfc_wqe_gen_status_MASK 0x0000000F
#define lpfc_wqe_gen_status_WORD word7
#define lpfc_wqe_gen_ct_SHIFT 2
-#define lpfc_wqe_gen_ct_MASK 0x00000007
+#define lpfc_wqe_gen_ct_MASK 0x00000003
#define lpfc_wqe_gen_ct_WORD word7
uint32_t abort_tag;
uint32_t word9;
@@ -453,6 +473,13 @@ struct lpfc_wqe_generic{
#define lpfc_wqe_gen_wqec_SHIFT 7
#define lpfc_wqe_gen_wqec_MASK 0x00000001
#define lpfc_wqe_gen_wqec_WORD word11
+#define ELS_ID_FLOGI 3
+#define ELS_ID_FDISC 2
+#define ELS_ID_LOGO 1
+#define ELS_ID_DEFAULT 0
+#define lpfc_wqe_gen_els_id_SHIFT 4
+#define lpfc_wqe_gen_els_id_MASK 0x00000003
+#define lpfc_wqe_gen_els_id_WORD word11
#define lpfc_wqe_gen_cmd_type_SHIFT 0
#define lpfc_wqe_gen_cmd_type_MASK 0x0000000F
#define lpfc_wqe_gen_cmd_type_WORD word11
@@ -487,8 +514,8 @@ struct lpfc_register {
#define LPFC_UERR_STATUS_HI 0x00A4
#define LPFC_UERR_STATUS_LO 0x00A0
-#define LPFC_ONLINE0 0x00B0
-#define LPFC_ONLINE1 0x00B4
+#define LPFC_UE_MASK_HI 0x00AC
+#define LPFC_UE_MASK_LO 0x00A8
#define LPFC_SCRATCHPAD 0x0058
/* BAR0 Registers */
@@ -760,6 +787,7 @@ struct mbox_header {
#define LPFC_MBOX_OPCODE_MQ_DESTROY 0x35
#define LPFC_MBOX_OPCODE_CQ_DESTROY 0x36
#define LPFC_MBOX_OPCODE_EQ_DESTROY 0x37
+#define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A
#define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D
/* FCoE Opcodes */
@@ -1273,6 +1301,51 @@ struct lpfc_mbx_del_fcf_tbl_entry {
#define lpfc_mbx_del_fcf_tbl_index_WORD word10
};
+struct lpfc_mbx_query_fw_cfg {
+ struct mbox_header header;
+ uint32_t config_number;
+ uint32_t asic_rev;
+ uint32_t phys_port;
+ uint32_t function_mode;
+/* firmware Function Mode */
+#define lpfc_function_mode_toe_SHIFT 0
+#define lpfc_function_mode_toe_MASK 0x00000001
+#define lpfc_function_mode_toe_WORD function_mode
+#define lpfc_function_mode_nic_SHIFT 1
+#define lpfc_function_mode_nic_MASK 0x00000001
+#define lpfc_function_mode_nic_WORD function_mode
+#define lpfc_function_mode_rdma_SHIFT 2
+#define lpfc_function_mode_rdma_MASK 0x00000001
+#define lpfc_function_mode_rdma_WORD function_mode
+#define lpfc_function_mode_vm_SHIFT 3
+#define lpfc_function_mode_vm_MASK 0x00000001
+#define lpfc_function_mode_vm_WORD function_mode
+#define lpfc_function_mode_iscsi_i_SHIFT 4
+#define lpfc_function_mode_iscsi_i_MASK 0x00000001
+#define lpfc_function_mode_iscsi_i_WORD function_mode
+#define lpfc_function_mode_iscsi_t_SHIFT 5
+#define lpfc_function_mode_iscsi_t_MASK 0x00000001
+#define lpfc_function_mode_iscsi_t_WORD function_mode
+#define lpfc_function_mode_fcoe_i_SHIFT 6
+#define lpfc_function_mode_fcoe_i_MASK 0x00000001
+#define lpfc_function_mode_fcoe_i_WORD function_mode
+#define lpfc_function_mode_fcoe_t_SHIFT 7
+#define lpfc_function_mode_fcoe_t_MASK 0x00000001
+#define lpfc_function_mode_fcoe_t_WORD function_mode
+#define lpfc_function_mode_dal_SHIFT 8
+#define lpfc_function_mode_dal_MASK 0x00000001
+#define lpfc_function_mode_dal_WORD function_mode
+#define lpfc_function_mode_lro_SHIFT 9
+#define lpfc_function_mode_lro_MASK 0x00000001
+#define lpfc_function_mode_lro_WORD function_mode9
+#define lpfc_function_mode_flex10_SHIFT 10
+#define lpfc_function_mode_flex10_MASK 0x00000001
+#define lpfc_function_mode_flex10_WORD function_mode
+#define lpfc_function_mode_ncsi_SHIFT 11
+#define lpfc_function_mode_ncsi_MASK 0x00000001
+#define lpfc_function_mode_ncsi_WORD function_mode
+};
+
/* Status field for embedded SLI_CONFIG mailbox command */
#define STATUS_SUCCESS 0x0
#define STATUS_FAILED 0x1
@@ -1349,8 +1422,7 @@ struct lpfc_mbx_reg_vfi {
#define lpfc_reg_vfi_fcfi_SHIFT 0
#define lpfc_reg_vfi_fcfi_MASK 0x0000FFFF
#define lpfc_reg_vfi_fcfi_WORD word2
- uint32_t word3_rsvd;
- uint32_t word4_rsvd;
+ uint32_t wwn[2];
struct ulp_bde64 bde;
uint32_t word8_rsvd;
uint32_t word9_rsvd;
@@ -1555,6 +1627,11 @@ struct lpfc_mbx_read_rev {
#define lpfc_mbx_rd_rev_fcoe_SHIFT 20
#define lpfc_mbx_rd_rev_fcoe_MASK 0x00000001
#define lpfc_mbx_rd_rev_fcoe_WORD word1
+#define lpfc_mbx_rd_rev_cee_ver_SHIFT 21
+#define lpfc_mbx_rd_rev_cee_ver_MASK 0x00000003
+#define lpfc_mbx_rd_rev_cee_ver_WORD word1
+#define LPFC_PREDCBX_CEE_MODE 0
+#define LPFC_DCBX_CEE_MODE 1
#define lpfc_mbx_rd_rev_vpd_SHIFT 29
#define lpfc_mbx_rd_rev_vpd_MASK 0x00000001
#define lpfc_mbx_rd_rev_vpd_WORD word1
@@ -1804,6 +1881,7 @@ struct lpfc_mqe {
struct lpfc_mbx_read_config rd_config;
struct lpfc_mbx_request_features req_ftrs;
struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
+ struct lpfc_mbx_query_fw_cfg query_fw_cfg;
struct lpfc_mbx_nop nop;
} un;
};
@@ -1885,7 +1963,7 @@ struct lpfc_acqe_link {
};
struct lpfc_acqe_fcoe {
- uint32_t fcf_index;
+ uint32_t index;
uint32_t word1;
#define lpfc_acqe_fcoe_fcf_count_SHIFT 0
#define lpfc_acqe_fcoe_fcf_count_MASK 0x0000FFFF
@@ -1896,6 +1974,7 @@ struct lpfc_acqe_fcoe {
#define LPFC_FCOE_EVENT_TYPE_NEW_FCF 0x1
#define LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL 0x2
#define LPFC_FCOE_EVENT_TYPE_FCF_DEAD 0x3
+#define LPFC_FCOE_EVENT_TYPE_CVL 0x4
uint32_t event_tag;
uint32_t trailer;
};
@@ -1921,12 +2000,13 @@ struct lpfc_bmbx_create {
#define SGL_ALIGN_SZ 64
#define SGL_PAGE_SIZE 4096
/* align SGL addr on a size boundary - adjust address up */
-#define NO_XRI ((uint16_t)-1)
+#define NO_XRI ((uint16_t)-1)
+
struct wqe_common {
uint32_t word6;
-#define wqe_xri_SHIFT 0
-#define wqe_xri_MASK 0x0000FFFF
-#define wqe_xri_WORD word6
+#define wqe_xri_tag_SHIFT 0
+#define wqe_xri_tag_MASK 0x0000FFFF
+#define wqe_xri_tag_WORD word6
#define wqe_ctxt_tag_SHIFT 16
#define wqe_ctxt_tag_MASK 0x0000FFFF
#define wqe_ctxt_tag_WORD word6
@@ -1987,7 +2067,7 @@ struct wqe_common {
#define wqe_wqec_MASK 0x00000001
#define wqe_wqec_WORD word11
#define wqe_cqid_SHIFT 16
-#define wqe_cqid_MASK 0x000003ff
+#define wqe_cqid_MASK 0x0000ffff
#define wqe_cqid_WORD word11
};
@@ -1996,6 +2076,9 @@ struct wqe_did {
#define wqe_els_did_SHIFT 0
#define wqe_els_did_MASK 0x00FFFFFF
#define wqe_els_did_WORD word5
+#define wqe_xmit_bls_pt_SHIFT 28
+#define wqe_xmit_bls_pt_MASK 0x00000003
+#define wqe_xmit_bls_pt_WORD word5
#define wqe_xmit_bls_ar_SHIFT 30
#define wqe_xmit_bls_ar_MASK 0x00000001
#define wqe_xmit_bls_ar_WORD word5
@@ -2044,6 +2127,23 @@ struct xmit_els_rsp64_wqe {
struct xmit_bls_rsp64_wqe {
uint32_t payload0;
+/* Payload0 for BA_ACC */
+#define xmit_bls_rsp64_acc_seq_id_SHIFT 16
+#define xmit_bls_rsp64_acc_seq_id_MASK 0x000000ff
+#define xmit_bls_rsp64_acc_seq_id_WORD payload0
+#define xmit_bls_rsp64_acc_seq_id_vald_SHIFT 24
+#define xmit_bls_rsp64_acc_seq_id_vald_MASK 0x000000ff
+#define xmit_bls_rsp64_acc_seq_id_vald_WORD payload0
+/* Payload0 for BA_RJT */
+#define xmit_bls_rsp64_rjt_vspec_SHIFT 0
+#define xmit_bls_rsp64_rjt_vspec_MASK 0x000000ff
+#define xmit_bls_rsp64_rjt_vspec_WORD payload0
+#define xmit_bls_rsp64_rjt_expc_SHIFT 8
+#define xmit_bls_rsp64_rjt_expc_MASK 0x000000ff
+#define xmit_bls_rsp64_rjt_expc_WORD payload0
+#define xmit_bls_rsp64_rjt_rsnc_SHIFT 16
+#define xmit_bls_rsp64_rjt_rsnc_MASK 0x000000ff
+#define xmit_bls_rsp64_rjt_rsnc_WORD payload0
uint32_t word1;
#define xmit_bls_rsp64_rxid_SHIFT 0
#define xmit_bls_rsp64_rxid_MASK 0x0000ffff
@@ -2052,18 +2152,19 @@ struct xmit_bls_rsp64_wqe {
#define xmit_bls_rsp64_oxid_MASK 0x0000ffff
#define xmit_bls_rsp64_oxid_WORD word1
uint32_t word2;
-#define xmit_bls_rsp64_seqcntlo_SHIFT 0
-#define xmit_bls_rsp64_seqcntlo_MASK 0x0000ffff
-#define xmit_bls_rsp64_seqcntlo_WORD word2
-#define xmit_bls_rsp64_seqcnthi_SHIFT 16
+#define xmit_bls_rsp64_seqcnthi_SHIFT 0
#define xmit_bls_rsp64_seqcnthi_MASK 0x0000ffff
#define xmit_bls_rsp64_seqcnthi_WORD word2
+#define xmit_bls_rsp64_seqcntlo_SHIFT 16
+#define xmit_bls_rsp64_seqcntlo_MASK 0x0000ffff
+#define xmit_bls_rsp64_seqcntlo_WORD word2
uint32_t rsrvd3;
uint32_t rsrvd4;
struct wqe_did wqe_dest;
struct wqe_common wqe_com; /* words 6-11 */
uint32_t rsvd_12_15[4];
};
+
struct wqe_rctl_dfctl {
uint32_t word5;
#define wqe_si_SHIFT 2
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 562d8cee874..226920d15ea 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -28,6 +28,7 @@
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>
+#include <linux/aer.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -645,7 +646,7 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
* down the SLI Layer.
*
* Return codes
- * 0 - sucess.
+ * 0 - success.
* Any other value - error.
**/
static int
@@ -700,7 +701,7 @@ lpfc_hba_down_post_s3(struct lpfc_hba *phba)
* down the SLI Layer.
*
* Return codes
- * 0 - sucess.
+ * 0 - success.
* Any other value - error.
**/
static int
@@ -755,7 +756,7 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba)
* uninitialization after the HBA is reset when bring down the SLI Layer.
*
* Return codes
- * 0 - sucess.
+ * 0 - success.
* Any other value - error.
**/
int
@@ -852,12 +853,19 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
void
lpfc_hb_timeout_handler(struct lpfc_hba *phba)
{
+ struct lpfc_vport **vports;
LPFC_MBOXQ_t *pmboxq;
struct lpfc_dmabuf *buf_ptr;
- int retval;
+ int retval, i;
struct lpfc_sli *psli = &phba->sli;
LIST_HEAD(completions);
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
+ lpfc_rcv_seq_check_edtov(vports[i]);
+ lpfc_destroy_vport_work_array(phba, vports);
+
if ((phba->link_state == LPFC_HBA_ERROR) ||
(phba->pport->load_flag & FC_UNLOADING) ||
(phba->pport->fc_flag & FC_OFFLINE_MODE))
@@ -1254,7 +1262,7 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba)
* routine from the API jump table function pointer from the lpfc_hba struct.
*
* Return codes
- * 0 - sucess.
+ * 0 - success.
* Any other value - error.
**/
void
@@ -1521,10 +1529,10 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
int GE = 0;
int oneConnect = 0; /* default is not a oneConnect */
struct {
- char * name;
- int max_speed;
- char * bus;
- } m = {"<Unknown>", 0, ""};
+ char *name;
+ char *bus;
+ char *function;
+ } m = {"<Unknown>", "", ""};
if (mdp && mdp[0] != '\0'
&& descp && descp[0] != '\0')
@@ -1545,132 +1553,155 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
switch (dev_id) {
case PCI_DEVICE_ID_FIREFLY:
- m = (typeof(m)){"LP6000", max_speed, "PCI"};
+ m = (typeof(m)){"LP6000", "PCI", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SUPERFLY:
if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
- m = (typeof(m)){"LP7000", max_speed, "PCI"};
+ m = (typeof(m)){"LP7000", "PCI",
+ "Fibre Channel Adapter"};
else
- m = (typeof(m)){"LP7000E", max_speed, "PCI"};
+ m = (typeof(m)){"LP7000E", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_DRAGONFLY:
- m = (typeof(m)){"LP8000", max_speed, "PCI"};
+ m = (typeof(m)){"LP8000", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_CENTAUR:
if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
- m = (typeof(m)){"LP9002", max_speed, "PCI"};
+ m = (typeof(m)){"LP9002", "PCI",
+ "Fibre Channel Adapter"};
else
- m = (typeof(m)){"LP9000", max_speed, "PCI"};
+ m = (typeof(m)){"LP9000", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_RFLY:
- m = (typeof(m)){"LP952", max_speed, "PCI"};
+ m = (typeof(m)){"LP952", "PCI",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PEGASUS:
- m = (typeof(m)){"LP9802", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP9802", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_THOR:
- m = (typeof(m)){"LP10000", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP10000", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_VIPER:
- m = (typeof(m)){"LPX1000", max_speed, "PCI-X"};
+ m = (typeof(m)){"LPX1000", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PFLY:
- m = (typeof(m)){"LP982", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP982", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_TFLY:
- m = (typeof(m)){"LP1050", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP1050", "PCI-X",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS:
- m = (typeof(m)){"LP11000", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP11000", "PCI-X2",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS_SCSP:
- m = (typeof(m)){"LP11000-SP", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP11000-SP", "PCI-X2",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HELIOS_DCSP:
- m = (typeof(m)){"LP11002-SP", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP11002-SP", "PCI-X2",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE:
- m = (typeof(m)){"LPe1000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE_SCSP:
- m = (typeof(m)){"LPe1000-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1000-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_NEPTUNE_DCSP:
- m = (typeof(m)){"LPe1002-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1002-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_BMID:
- m = (typeof(m)){"LP1150", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP1150", "PCI-X2", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_BSMB:
- m = (typeof(m)){"LP111", max_speed, "PCI-X2"};
+ m = (typeof(m)){"LP111", "PCI-X2", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR:
- m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR_SCSP:
- m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZEPHYR_DCSP:
- m = (typeof(m)){"LP2105", max_speed, "PCIe"};
+ m = (typeof(m)){"LP2105", "PCIe", "FCoE Adapter"};
GE = 1;
break;
case PCI_DEVICE_ID_ZMID:
- m = (typeof(m)){"LPe1150", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1150", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_ZSMB:
- m = (typeof(m)){"LPe111", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe111", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP101:
- m = (typeof(m)){"LP101", max_speed, "PCI-X"};
+ m = (typeof(m)){"LP101", "PCI-X", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP10000S:
- m = (typeof(m)){"LP10000-S", max_speed, "PCI"};
+ m = (typeof(m)){"LP10000-S", "PCI", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LP11000S:
- m = (typeof(m)){"LP11000-S", max_speed,
- "PCI-X2"};
+ m = (typeof(m)){"LP11000-S", "PCI-X2", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_LPE11000S:
- m = (typeof(m)){"LPe11000-S", max_speed,
- "PCIe"};
+ m = (typeof(m)){"LPe11000-S", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT:
- m = (typeof(m)){"LPe12000", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12000", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_MID:
- m = (typeof(m)){"LPe1250", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe1250", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_SMB:
- m = (typeof(m)){"LPe121", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe121", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_DCSP:
- m = (typeof(m)){"LPe12002-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12002-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_SCSP:
- m = (typeof(m)){"LPe12000-SP", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12000-SP", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_SAT_S:
- m = (typeof(m)){"LPe12000-S", max_speed, "PCIe"};
+ m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_HORNET:
- m = (typeof(m)){"LP21000", max_speed, "PCIe"};
+ m = (typeof(m)){"LP21000", "PCIe", "FCoE Adapter"};
GE = 1;
break;
case PCI_DEVICE_ID_PROTEUS_VF:
- m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+ m = (typeof(m)){"LPev12000", "PCIe IOV",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PROTEUS_PF:
- m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+ m = (typeof(m)){"LPev12000", "PCIe IOV",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_PROTEUS_S:
- m = (typeof(m)) {"LPemv12002-S", max_speed, "PCIe IOV"};
+ m = (typeof(m)){"LPemv12002-S", "PCIe IOV",
+ "Fibre Channel Adapter"};
break;
case PCI_DEVICE_ID_TIGERSHARK:
oneConnect = 1;
- m = (typeof(m)) {"OCe10100-F", max_speed, "PCIe"};
+ m = (typeof(m)){"OCe10100", "PCIe", "FCoE"};
+ break;
+ case PCI_DEVICE_ID_TOMCAT:
+ oneConnect = 1;
+ m = (typeof(m)){"OCe11100", "PCIe", "FCoE"};
+ break;
+ case PCI_DEVICE_ID_FALCON:
+ m = (typeof(m)){"LPSe12002-ML1-E", "PCIe",
+ "EmulexSecure Fibre"};
break;
default:
- m = (typeof(m)){ NULL };
+ m = (typeof(m)){"Unknown", "", ""};
break;
}
@@ -1682,17 +1713,14 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
if (descp && descp[0] == '\0') {
if (oneConnect)
snprintf(descp, 255,
- "Emulex OneConnect %s, FCoE Initiator, Port %s",
- m.name,
+ "Emulex OneConnect %s, %s Initiator, Port %s",
+ m.name, m.function,
phba->Port);
else
snprintf(descp, 255,
"Emulex %s %d%s %s %s",
- m.name, m.max_speed,
- (GE) ? "GE" : "Gb",
- m.bus,
- (GE) ? "FCoE Adapter" :
- "Fibre Channel Adapter");
+ m.name, max_speed, (GE) ? "GE" : "Gb",
+ m.bus, m.function);
}
}
@@ -2217,7 +2245,7 @@ lpfc_offline_prep(struct lpfc_hba * phba)
if (vports[i]->load_flag & FC_UNLOADING)
continue;
- vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
+ vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
shost = lpfc_shost_from_vport(vports[i]);
list_for_each_entry_safe(ndlp, next_ndlp,
&vports[i]->fc_nodes,
@@ -2308,6 +2336,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
spin_lock_irq(&phba->hbalock);
/* Release all the lpfc_scsi_bufs maintained by this host. */
+ spin_lock(&phba->scsi_buf_list_lock);
list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
list_del(&sb->list);
pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data,
@@ -2315,6 +2344,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
kfree(sb);
phba->total_scsi_bufs--;
}
+ spin_unlock(&phba->scsi_buf_list_lock);
/* Release all the lpfc_iocbq entries maintained by this host. */
list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) {
@@ -2322,9 +2352,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
kfree(io);
phba->total_iocbq_bufs--;
}
-
spin_unlock_irq(&phba->hbalock);
-
return 0;
}
@@ -2408,7 +2436,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
vport->els_tmofunc.function = lpfc_els_timeout;
vport->els_tmofunc.data = (unsigned long)vport;
- error = scsi_add_host(shost, dev);
+ error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev);
if (error)
goto out_put_shost;
@@ -2699,6 +2727,63 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba)
}
/**
+ * lpfc_sli4_fw_cfg_check - Read the firmware config and verify FCoE support
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function uses the QUERY_FW_CFG mailbox command to determine if the
+ * firmware loaded supports FCoE. A return of zero indicates that the mailbox
+ * was successful and the firmware supports FCoE. Any other return indicates
+ * a error. It is assumed that this function will be called before interrupts
+ * are enabled.
+ **/
+static int
+lpfc_sli4_fw_cfg_check(struct lpfc_hba *phba)
+{
+ int rc = 0;
+ LPFC_MBOXQ_t *mboxq;
+ struct lpfc_mbx_query_fw_cfg *query_fw_cfg;
+ uint32_t length;
+ uint32_t shdr_status, shdr_add_status;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2621 Failed to allocate mbox for "
+ "query firmware config cmd\n");
+ return -ENOMEM;
+ }
+ query_fw_cfg = &mboxq->u.mqe.un.query_fw_cfg;
+ length = (sizeof(struct lpfc_mbx_query_fw_cfg) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+ LPFC_MBOX_OPCODE_QUERY_FW_CFG,
+ length, LPFC_SLI4_MBX_EMBED);
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+ /* The IOCTL status is embedded in the mailbox subheader. */
+ shdr_status = bf_get(lpfc_mbox_hdr_status,
+ &query_fw_cfg->header.cfg_shdr.response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &query_fw_cfg->header.cfg_shdr.response);
+ if (shdr_status || shdr_add_status || rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2622 Query Firmware Config failed "
+ "mbx status x%x, status x%x add_status x%x\n",
+ rc, shdr_status, shdr_add_status);
+ return -EINVAL;
+ }
+ if (!bf_get(lpfc_function_mode_fcoe_i, query_fw_cfg)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2623 FCoE Function not supported by firmware. "
+ "Function mode = %08x\n",
+ query_fw_cfg->function_mode);
+ return -EINVAL;
+ }
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return 0;
+}
+
+/**
* lpfc_sli4_parse_latt_fault - Parse sli4 link-attention link fault code
* @phba: pointer to lpfc hba data structure.
* @acqe_link: pointer to the async link completion queue entry.
@@ -2918,13 +3003,17 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
{
uint8_t event_type = bf_get(lpfc_acqe_fcoe_event_type, acqe_fcoe);
int rc;
+ struct lpfc_vport *vport;
+ struct lpfc_nodelist *ndlp;
+ struct Scsi_Host *shost;
+ phba->fc_eventTag = acqe_fcoe->event_tag;
phba->fcoe_eventtag = acqe_fcoe->event_tag;
switch (event_type) {
case LPFC_FCOE_EVENT_TYPE_NEW_FCF:
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
"2546 New FCF found index 0x%x tag 0x%x\n",
- acqe_fcoe->fcf_index,
+ acqe_fcoe->index,
acqe_fcoe->event_tag);
/*
* If the current FCF is in discovered state, or
@@ -2939,12 +3028,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
spin_unlock_irq(&phba->hbalock);
/* Read the FCF table and re-discover SAN. */
- rc = lpfc_sli4_read_fcf_record(phba,
- LPFC_FCOE_FCF_GET_FIRST);
+ rc = lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
if (rc)
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
- "2547 Read FCF record failed 0x%x\n",
- rc);
+ "2547 Read FCF record failed 0x%x\n",
+ rc);
break;
case LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL:
@@ -2956,11 +3044,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
case LPFC_FCOE_EVENT_TYPE_FCF_DEAD:
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
- "2549 FCF disconnected fron network index 0x%x"
- " tag 0x%x\n", acqe_fcoe->fcf_index,
+ "2549 FCF disconnected from network index 0x%x"
+ " tag 0x%x\n", acqe_fcoe->index,
acqe_fcoe->event_tag);
/* If the event is not for currently used fcf do nothing */
- if (phba->fcf.fcf_indx != acqe_fcoe->fcf_index)
+ if (phba->fcf.fcf_indx != acqe_fcoe->index)
break;
/*
* Currently, driver support only one FCF - so treat this as
@@ -2970,7 +3058,28 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
/* Unregister FCF if no devices connected to it */
lpfc_unregister_unused_fcf(phba);
break;
-
+ case LPFC_FCOE_EVENT_TYPE_CVL:
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+ "2718 Clear Virtual Link Received for VPI 0x%x"
+ " tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag);
+ vport = lpfc_find_vport_by_vpid(phba,
+ acqe_fcoe->index - phba->vpi_base);
+ if (!vport)
+ break;
+ ndlp = lpfc_findnode_did(vport, Fabric_DID);
+ if (!ndlp)
+ break;
+ shost = lpfc_shost_from_vport(vport);
+ lpfc_linkdown_port(vport);
+ if (vport->port_type != LPFC_NPIV_PORT) {
+ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+ spin_lock_irq(shost->host_lock);
+ ndlp->nlp_flag |= NLP_DELAY_TMO;
+ spin_unlock_irq(shost->host_lock);
+ ndlp->nlp_last_elscmd = ELS_CMD_FLOGI;
+ vport->port_state = LPFC_FLOGI;
+ }
+ break;
default:
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0288 Unknown FCoE event type 0x%x event tag "
@@ -2990,6 +3099,7 @@ static void
lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba,
struct lpfc_acqe_dcbx *acqe_dcbx)
{
+ phba->fc_eventTag = acqe_dcbx->event_tag;
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0290 The SLI4 DCBX asynchronous event is not "
"handled yet\n");
@@ -3124,7 +3234,7 @@ static void lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
* PCI devices.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -3220,7 +3330,7 @@ lpfc_reset_hba(struct lpfc_hba *phba)
* support the SLI-3 HBA device it attached to.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -3321,7 +3431,7 @@ lpfc_sli_driver_resource_unset(struct lpfc_hba *phba)
* support the SLI-4 HBA device it attached to.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -3432,7 +3542,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
/* Driver internel slow-path CQ Event pool */
INIT_LIST_HEAD(&phba->sli4_hba.sp_cqe_event_pool);
/* Response IOCB work queue list */
- INIT_LIST_HEAD(&phba->sli4_hba.sp_rspiocb_work_queue);
+ INIT_LIST_HEAD(&phba->sli4_hba.sp_queue_event);
/* Asynchronous event CQ Event work queue list */
INIT_LIST_HEAD(&phba->sli4_hba.sp_asynce_work_queue);
/* Fast-path XRI aborted CQ Event work queue list */
@@ -3461,6 +3571,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
if (unlikely(rc))
goto out_free_bsmbx;
+ rc = lpfc_sli4_fw_cfg_check(phba);
+ if (unlikely(rc))
+ goto out_free_bsmbx;
+
/* Set up the hba's configuration parameters. */
rc = lpfc_sli4_read_config(phba);
if (unlikely(rc))
@@ -3594,8 +3708,10 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
/* Free the current connect table */
list_for_each_entry_safe(conn_entry, next_conn_entry,
- &phba->fcf_conn_rec_list, list)
+ &phba->fcf_conn_rec_list, list) {
+ list_del_init(&conn_entry->list);
kfree(conn_entry);
+ }
return;
}
@@ -3642,7 +3758,7 @@ lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
* device specific resource setup to support the HBA device it attached to.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -3688,7 +3804,7 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
* device specific resource setup to support the HBA device it attached to.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -3753,7 +3869,7 @@ lpfc_free_iocb_list(struct lpfc_hba *phba)
* list and set up the IOCB tag array accordingly.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -3824,7 +3940,7 @@ lpfc_free_sgl_list(struct lpfc_hba *phba)
rc = lpfc_sli4_remove_all_sgl_pages(phba);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2005 Unable to deregister pages from HBA: %x", rc);
+ "2005 Unable to deregister pages from HBA: %x\n", rc);
}
kfree(phba->sli4_hba.lpfc_els_sgl_array);
}
@@ -3872,7 +3988,7 @@ lpfc_free_active_sgl(struct lpfc_hba *phba)
* list and set up the sgl xritag tag array accordingly.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -3986,7 +4102,7 @@ out_free_mem:
* enabled and the driver is reinitializing the device.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -4146,7 +4262,7 @@ lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *phba)
* PCI device data structure is set.
*
* Return codes
- * pointer to @phba - sucessful
+ * pointer to @phba - successful
* NULL - error
**/
static struct lpfc_hba *
@@ -4202,7 +4318,7 @@ lpfc_hba_free(struct lpfc_hba *phba)
* host with it.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -4273,7 +4389,8 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
_dump_buf_data =
(char *) __get_free_pages(GFP_KERNEL, pagecnt);
if (_dump_buf_data) {
- printk(KERN_ERR "BLKGRD allocated %d pages for "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9043 BLKGRD: allocated %d pages for "
"_dump_buf_data at 0x%p\n",
(1 << pagecnt), _dump_buf_data);
_dump_buf_data_order = pagecnt;
@@ -4284,17 +4401,20 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
--pagecnt;
}
if (!_dump_buf_data_order)
- printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9044 BLKGRD: ERROR unable to allocate "
"memory for hexdump\n");
} else
- printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9045 BLKGRD: already allocated _dump_buf_data=0x%p"
"\n", _dump_buf_data);
if (!_dump_buf_dif) {
while (pagecnt) {
_dump_buf_dif =
(char *) __get_free_pages(GFP_KERNEL, pagecnt);
if (_dump_buf_dif) {
- printk(KERN_ERR "BLKGRD allocated %d pages for "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9046 BLKGRD: allocated %d pages for "
"_dump_buf_dif at 0x%p\n",
(1 << pagecnt), _dump_buf_dif);
_dump_buf_dif_order = pagecnt;
@@ -4305,10 +4425,12 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
--pagecnt;
}
if (!_dump_buf_dif_order)
- printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9047 BLKGRD: ERROR unable to allocate "
"memory for hexdump\n");
} else
- printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9048 BLKGRD: already allocated _dump_buf_dif=0x%p\n",
_dump_buf_dif);
}
@@ -4365,7 +4487,7 @@ lpfc_post_init_setup(struct lpfc_hba *phba)
* with SLI-3 interface spec.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -4512,7 +4634,6 @@ int
lpfc_sli4_post_status_check(struct lpfc_hba *phba)
{
struct lpfc_register sta_reg, uerrlo_reg, uerrhi_reg, scratchpad;
- uint32_t onlnreg0, onlnreg1;
int i, port_error = -ENODEV;
if (!phba->sli4_hba.STAregaddr)
@@ -4556,21 +4677,20 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
bf_get(lpfc_scratchpad_slirev, &scratchpad),
bf_get(lpfc_scratchpad_featurelevel1, &scratchpad),
bf_get(lpfc_scratchpad_featurelevel2, &scratchpad));
-
+ phba->sli4_hba.ue_mask_lo = readl(phba->sli4_hba.UEMASKLOregaddr);
+ phba->sli4_hba.ue_mask_hi = readl(phba->sli4_hba.UEMASKHIregaddr);
/* With uncoverable error, log the error message and return error */
- onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
- onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
- if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
- uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
- uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
- if (uerrlo_reg.word0 || uerrhi_reg.word0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1422 HBA Unrecoverable error: "
- "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
- "online0_reg=0x%x, online1_reg=0x%x\n",
- uerrlo_reg.word0, uerrhi_reg.word0,
- onlnreg0, onlnreg1);
- }
+ uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
+ uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
+ if ((~phba->sli4_hba.ue_mask_lo & uerrlo_reg.word0) ||
+ (~phba->sli4_hba.ue_mask_hi & uerrhi_reg.word0)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1422 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n",
+ uerrlo_reg.word0, uerrhi_reg.word0,
+ phba->sli4_hba.ue_mask_lo,
+ phba->sli4_hba.ue_mask_hi);
return -ENODEV;
}
@@ -4591,10 +4711,10 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba)
LPFC_UERR_STATUS_LO;
phba->sli4_hba.UERRHIregaddr = phba->sli4_hba.conf_regs_memmap_p +
LPFC_UERR_STATUS_HI;
- phba->sli4_hba.ONLINE0regaddr = phba->sli4_hba.conf_regs_memmap_p +
- LPFC_ONLINE0;
- phba->sli4_hba.ONLINE1regaddr = phba->sli4_hba.conf_regs_memmap_p +
- LPFC_ONLINE1;
+ phba->sli4_hba.UEMASKLOregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_UE_MASK_LO;
+ phba->sli4_hba.UEMASKHIregaddr = phba->sli4_hba.conf_regs_memmap_p +
+ LPFC_UE_MASK_HI;
phba->sli4_hba.SCRATCHPADregaddr = phba->sli4_hba.conf_regs_memmap_p +
LPFC_SCRATCHPAD;
}
@@ -4662,7 +4782,7 @@ lpfc_sli4_bar2_register_memmap(struct lpfc_hba *phba, uint32_t vf)
* this routine.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - could not allocated memory.
**/
static int
@@ -4761,7 +4881,7 @@ lpfc_destroy_bootstrap_mbox(struct lpfc_hba *phba)
* allocation for the port.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -4825,7 +4945,8 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
phba->vpi_base = phba->sli4_hba.max_cfg_param.vpi_base;
phba->vfi_base = phba->sli4_hba.max_cfg_param.vfi_base;
phba->sli4_hba.next_rpi = phba->sli4_hba.max_cfg_param.rpi_base;
- phba->max_vpi = phba->sli4_hba.max_cfg_param.max_vpi;
+ phba->max_vpi = (phba->sli4_hba.max_cfg_param.max_vpi > 0) ?
+ (phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0;
phba->max_vports = phba->max_vpi;
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"2003 cfg params XRI(B:%d M:%d), "
@@ -4861,7 +4982,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
* HBA consistent with the SLI-4 interface spec.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -4910,7 +5031,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
* we just use some constant number as place holder.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -4979,10 +5100,9 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
/* It does not make sense to have more EQs than WQs */
if (cfg_fcp_eq_count > phba->cfg_fcp_wq_count) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
- "2593 The number of FCP EQs (%d) is more "
- "than the number of FCP WQs (%d), take "
- "the number of FCP EQs same as than of "
- "WQs (%d)\n", cfg_fcp_eq_count,
+ "2593 The FCP EQ count(%d) cannot be greater "
+ "than the FCP WQ count(%d), limiting the "
+ "FCP EQ count to %d\n", cfg_fcp_eq_count,
phba->cfg_fcp_wq_count,
phba->cfg_fcp_wq_count);
cfg_fcp_eq_count = phba->cfg_fcp_wq_count;
@@ -5058,15 +5178,6 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
}
phba->sli4_hba.els_cq = qdesc;
- /* Create slow-path Unsolicited Receive Complete Queue */
- qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
- phba->sli4_hba.cq_ecount);
- if (!qdesc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0502 Failed allocate slow-path USOL RX CQ\n");
- goto out_free_els_cq;
- }
- phba->sli4_hba.rxq_cq = qdesc;
/* Create fast-path FCP Completion Queue(s), one-to-one with EQs */
phba->sli4_hba.fcp_cq = kzalloc((sizeof(struct lpfc_queue *) *
@@ -5075,7 +5186,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"2577 Failed allocate memory for fast-path "
"CQ record array\n");
- goto out_free_rxq_cq;
+ goto out_free_els_cq;
}
for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
qdesc = lpfc_sli4_queue_alloc(phba, phba->sli4_hba.cq_esize,
@@ -5188,9 +5299,6 @@ out_free_fcp_cq:
phba->sli4_hba.fcp_cq[fcp_cqidx] = NULL;
}
kfree(phba->sli4_hba.fcp_cq);
-out_free_rxq_cq:
- lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
- phba->sli4_hba.rxq_cq = NULL;
out_free_els_cq:
lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
phba->sli4_hba.els_cq = NULL;
@@ -5218,7 +5326,7 @@ out_error:
* operation.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -5247,10 +5355,6 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
lpfc_sli4_queue_free(phba->sli4_hba.dat_rq);
phba->sli4_hba.dat_rq = NULL;
- /* Release unsolicited receive complete queue */
- lpfc_sli4_queue_free(phba->sli4_hba.rxq_cq);
- phba->sli4_hba.rxq_cq = NULL;
-
/* Release ELS complete queue */
lpfc_sli4_queue_free(phba->sli4_hba.els_cq);
phba->sli4_hba.els_cq = NULL;
@@ -5286,7 +5390,7 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
* operation.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -5383,25 +5487,6 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
phba->sli4_hba.els_cq->queue_id,
phba->sli4_hba.sp_eq->queue_id);
- /* Set up slow-path Unsolicited Receive Complete Queue */
- if (!phba->sli4_hba.rxq_cq) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0532 USOL RX CQ not allocated\n");
- goto out_destroy_els_cq;
- }
- rc = lpfc_cq_create(phba, phba->sli4_hba.rxq_cq, phba->sli4_hba.sp_eq,
- LPFC_RCQ, LPFC_USOL);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0533 Failed setup of slow-path USOL RX CQ: "
- "rc = 0x%x\n", rc);
- goto out_destroy_els_cq;
- }
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
- "2587 USL CQ setup: cq-id=%d, parent eq-id=%d\n",
- phba->sli4_hba.rxq_cq->queue_id,
- phba->sli4_hba.sp_eq->queue_id);
-
/* Set up fast-path FCP Response Complete Queue */
for (fcp_cqidx = 0; fcp_cqidx < phba->cfg_fcp_eq_count; fcp_cqidx++) {
if (!phba->sli4_hba.fcp_cq[fcp_cqidx]) {
@@ -5507,7 +5592,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
goto out_destroy_fcp_wq;
}
rc = lpfc_rq_create(phba, phba->sli4_hba.hdr_rq, phba->sli4_hba.dat_rq,
- phba->sli4_hba.rxq_cq, LPFC_USOL);
+ phba->sli4_hba.els_cq, LPFC_USOL);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0541 Failed setup of Receive Queue: "
@@ -5519,7 +5604,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
"parent cq-id=%d\n",
phba->sli4_hba.hdr_rq->queue_id,
phba->sli4_hba.dat_rq->queue_id,
- phba->sli4_hba.rxq_cq->queue_id);
+ phba->sli4_hba.els_cq->queue_id);
return 0;
out_destroy_fcp_wq:
@@ -5531,8 +5616,6 @@ out_destroy_mbx_wq:
out_destroy_fcp_cq:
for (--fcp_cqidx; fcp_cqidx >= 0; fcp_cqidx--)
lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_cqidx]);
- lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
-out_destroy_els_cq:
lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
out_destroy_mbx_cq:
lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
@@ -5552,7 +5635,7 @@ out_error:
* operation.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -5574,8 +5657,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
lpfc_cq_destroy(phba, phba->sli4_hba.mbx_cq);
/* Unset ELS complete queue */
lpfc_cq_destroy(phba, phba->sli4_hba.els_cq);
- /* Unset unsolicited receive complete queue */
- lpfc_cq_destroy(phba, phba->sli4_hba.rxq_cq);
/* Unset FCP response complete queue */
for (fcp_qidx = 0; fcp_qidx < phba->cfg_fcp_eq_count; fcp_qidx++)
lpfc_cq_destroy(phba, phba->sli4_hba.fcp_cq[fcp_qidx]);
@@ -5599,7 +5680,7 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
* Later, this can be used for all the slow-path events.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* -ENOMEM - No availble memory
**/
static int
@@ -5760,7 +5841,7 @@ lpfc_sli4_cq_event_release_all(struct lpfc_hba *phba)
* all resources assigned to the PCI function which originates this request.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No availble memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -5923,7 +6004,7 @@ lpfc_sli4_fcfi_unreg(struct lpfc_hba *phba, uint16_t fcfi)
* with SLI-4 interface spec.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -6052,7 +6133,7 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
* will be left with MSI-X enabled and leaks its vectors.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -6184,7 +6265,7 @@ lpfc_sli_disable_msix(struct lpfc_hba *phba)
* is done in this function.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
*/
static int
@@ -6243,7 +6324,7 @@ lpfc_sli_disable_msi(struct lpfc_hba *phba)
* MSI-X -> MSI -> IRQ.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static uint32_t
@@ -6333,7 +6414,7 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba)
* enabled and leaks its vectors.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -6443,7 +6524,7 @@ lpfc_sli4_disable_msix(struct lpfc_hba *phba)
* which is done in this function.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static int
@@ -6508,7 +6589,7 @@ lpfc_sli4_disable_msi(struct lpfc_hba *phba)
* MSI-X -> MSI -> IRQ.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* other values - error
**/
static uint32_t
@@ -6722,6 +6803,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct lpfc_hba *phba;
struct lpfc_vport *vport = NULL;
+ struct Scsi_Host *shost = NULL;
int error;
uint32_t cfg_mode, intr_mode;
@@ -6800,6 +6882,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_destroy_shost;
}
+ shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
/* Now, trying to enable interrupt and bring up the device */
cfg_mode = phba->cfg_use_msi;
while (true) {
@@ -6866,6 +6949,8 @@ out_unset_pci_mem_s3:
lpfc_sli_pci_mem_unset(phba);
out_disable_pci_dev:
lpfc_disable_pci_dev(phba);
+ if (shost)
+ scsi_host_put(shost);
out_free_phba:
lpfc_hba_free(phba);
return error;
@@ -7036,6 +7121,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
/* Restore device state from PCI config space */
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
+
if (pdev->is_busmaster)
pci_set_master(pdev);
@@ -7070,6 +7156,75 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
}
/**
+ * lpfc_sli_prep_dev_for_recover - Prepare SLI3 device for pci slot recover
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot recover. It
+ * aborts and stops all the on-going I/Os on the pci device.
+ **/
+static void
+lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba)
+{
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2723 PCI channel I/O abort preparing for recovery\n");
+ /* Prepare for bringing HBA offline */
+ lpfc_offline_prep(phba);
+ /* Clear sli active flag to prevent sysfs access to HBA */
+ spin_lock_irq(&phba->hbalock);
+ phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE;
+ spin_unlock_irq(&phba->hbalock);
+ /* Stop and flush all I/Os and bring HBA offline */
+ lpfc_offline(phba);
+}
+
+/**
+ * lpfc_sli_prep_dev_for_reset - Prepare SLI3 device for pci slot reset
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot reset. It
+ * disables the device interrupt and pci device, and aborts the internal FCP
+ * pending I/Os.
+ **/
+static void
+lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
+{
+ struct lpfc_sli *psli = &phba->sli;
+ struct lpfc_sli_ring *pring;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2710 PCI channel disable preparing for reset\n");
+ /* Disable interrupt and pci device */
+ lpfc_sli_disable_intr(phba);
+ pci_disable_device(phba->pcidev);
+ /*
+ * There may be I/Os dropped by the firmware.
+ * Error iocb (I/O) on txcmplq and let the SCSI layer
+ * retry it after re-establishing link.
+ */
+ pring = &psli->ring[psli->fcp_ring];
+ lpfc_sli_abort_iocb_ring(phba, pring);
+}
+
+/**
+ * lpfc_sli_prep_dev_for_perm_failure - Prepare SLI3 dev for pci slot disable
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot permanently
+ * disabling. It blocks the SCSI transport layer traffic and flushes the FCP
+ * pending I/Os.
+ **/
+static void
+lpfc_prep_dev_for_perm_failure(struct lpfc_hba *phba)
+{
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2711 PCI channel permanent disable for failure\n");
+ /* Block all SCSI devices' I/Os on the host */
+ lpfc_scsi_dev_block(phba);
+ /* Clean up all driver's outstanding SCSI I/Os */
+ lpfc_sli_flush_fcp_rings(phba);
+}
+
+/**
* lpfc_io_error_detected_s3 - Method for handling SLI-3 device PCI I/O error
* @pdev: pointer to PCI device.
* @state: the current PCI connection state.
@@ -7083,6 +7238,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
* as desired.
*
* Return codes
+ * PCI_ERS_RESULT_CAN_RECOVER - can be recovered with reset_link
* PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
* PCI_ERS_RESULT_DISCONNECT - device could not be recovered
**/
@@ -7091,33 +7247,27 @@ lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state)
{
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring;
- if (state == pci_channel_io_perm_failure) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0472 PCI channel I/O permanent failure\n");
- /* Block all SCSI devices' I/Os on the host */
- lpfc_scsi_dev_block(phba);
- /* Clean up all driver's outstanding SCSI I/Os */
- lpfc_sli_flush_fcp_rings(phba);
+ switch (state) {
+ case pci_channel_io_normal:
+ /* Non-fatal error, prepare for recovery */
+ lpfc_sli_prep_dev_for_recover(phba);
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ /* Fatal error, prepare for slot reset */
+ lpfc_sli_prep_dev_for_reset(phba);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ /* Permanent failure, prepare for device down */
+ lpfc_prep_dev_for_perm_failure(phba);
return PCI_ERS_RESULT_DISCONNECT;
+ default:
+ /* Unknown state, prepare and request slot reset */
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "0472 Unknown PCI error state: x%x\n", state);
+ lpfc_sli_prep_dev_for_reset(phba);
+ return PCI_ERS_RESULT_NEED_RESET;
}
-
- pci_disable_device(pdev);
- /*
- * There may be I/Os dropped by the firmware.
- * Error iocb (I/O) on txcmplq and let the SCSI layer
- * retry it after re-establishing link.
- */
- pring = &psli->ring[psli->fcp_ring];
- lpfc_sli_abort_iocb_ring(phba, pring);
-
- /* Disable interrupt */
- lpfc_sli_disable_intr(phba);
-
- /* Request a slot reset. */
- return PCI_ERS_RESULT_NEED_RESET;
}
/**
@@ -7197,7 +7347,12 @@ lpfc_io_resume_s3(struct pci_dev *pdev)
struct Scsi_Host *shost = pci_get_drvdata(pdev);
struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba;
+ /* Bring the device online */
lpfc_online(phba);
+
+ /* Clean up Advanced Error Reporting (AER) if needed */
+ if (phba->hba_flag & HBA_AER_ENABLED)
+ pci_cleanup_aer_uncorrect_error_status(pdev);
}
/**
@@ -7213,15 +7368,15 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba)
if (phba->sli_rev == LPFC_SLI_REV4) {
if (max_xri <= 100)
- return 4;
+ return 10;
else if (max_xri <= 256)
- return 8;
+ return 25;
else if (max_xri <= 512)
- return 16;
+ return 50;
else if (max_xri <= 1024)
- return 32;
+ return 100;
else
- return 48;
+ return 150;
} else
return 0;
}
@@ -7249,6 +7404,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
{
struct lpfc_hba *phba;
struct lpfc_vport *vport = NULL;
+ struct Scsi_Host *shost = NULL;
int error;
uint32_t cfg_mode, intr_mode;
int mcnt;
@@ -7329,6 +7485,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
goto out_destroy_shost;
}
+ shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
/* Now, trying to enable interrupt and bring up the device */
cfg_mode = phba->cfg_use_msi;
while (true) {
@@ -7397,6 +7554,8 @@ out_unset_pci_mem_s4:
lpfc_sli4_pci_mem_unset(phba);
out_disable_pci_dev:
lpfc_disable_pci_dev(phba);
+ if (shost)
+ scsi_host_put(shost);
out_free_phba:
lpfc_hba_free(phba);
return error;
@@ -7971,6 +8130,10 @@ static struct pci_device_id lpfc_id_table[] = {
PCI_ANY_ID, PCI_ANY_ID, },
{PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TIGERSHARK,
PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_SERVERENGINE, PCI_DEVICE_ID_TOMCAT,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FALCON,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ 0 }
};
@@ -8053,15 +8216,15 @@ lpfc_exit(void)
if (lpfc_enable_npiv)
fc_release_transport(lpfc_vport_transport_template);
if (_dump_buf_data) {
- printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_data "
- "at 0x%p\n",
+ printk(KERN_ERR "9062 BLKGRD: freeing %lu pages for "
+ "_dump_buf_data at 0x%p\n",
(1L << _dump_buf_data_order), _dump_buf_data);
free_pages((unsigned long)_dump_buf_data, _dump_buf_data_order);
}
if (_dump_buf_dif) {
- printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_dif "
- "at 0x%p\n",
+ printk(KERN_ERR "9049 BLKGRD: freeing %lu pages for "
+ "_dump_buf_dif at 0x%p\n",
(1L << _dump_buf_dif_order), _dump_buf_dif);
free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
}
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 1ab405902a1..a9afd8b94b6 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -25,8 +25,8 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport_fc.h>
-
#include <scsi/scsi.h>
+#include <scsi/fc/fc_fs.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -820,6 +820,10 @@ lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb)
mb->un.varRegVpi.vpi = vport->vpi + vport->phba->vpi_base;
mb->un.varRegVpi.sid = vport->fc_myDID;
mb->un.varRegVpi.vfi = vport->vfi + vport->phba->vfi_base;
+ memcpy(mb->un.varRegVpi.wwn, &vport->fc_portname,
+ sizeof(struct lpfc_name));
+ mb->un.varRegVpi.wwn[0] = cpu_to_le32(mb->un.varRegVpi.wwn[0]);
+ mb->un.varRegVpi.wwn[1] = cpu_to_le32(mb->un.varRegVpi.wwn[1]);
mb->mbxCommand = MBX_REG_VPI;
mb->mbxOwner = OWN_HOST;
@@ -849,7 +853,10 @@ lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb)
MAILBOX_t *mb = &pmb->u.mb;
memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
- mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
+ if (phba->sli_rev < LPFC_SLI_REV4)
+ mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
+ else
+ mb->un.varUnregVpi.sli4_vpi = vpi + phba->vpi_base;
mb->mbxCommand = MBX_UNREG_VPI;
mb->mbxOwner = OWN_HOST;
@@ -1132,7 +1139,7 @@ lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
/* Otherwise we setup specific rctl / type masks for this ring */
for (i = 0; i < pring->num_mask; i++) {
mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl;
- if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ)
+ if (mb->un.varCfgRing.rrRegs[i].rval != FC_RCTL_ELS_REQ)
mb->un.varCfgRing.rrRegs[i].rmask = 0xff;
else
mb->un.varCfgRing.rrRegs[i].rmask = 0xfe;
@@ -1654,9 +1661,12 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
/* Allocate record for keeping SGE virtual addresses */
mbox->sge_array = kmalloc(sizeof(struct lpfc_mbx_nembed_sge_virt),
GFP_KERNEL);
- if (!mbox->sge_array)
+ if (!mbox->sge_array) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2527 Failed to allocate non-embedded SGE "
+ "array.\n");
return 0;
-
+ }
for (pagen = 0, alloc_len = 0; pagen < pcount; pagen++) {
/* The DMA memory is always allocated in the length of a
* page even though the last SGE might not fill up to a
@@ -1753,11 +1763,6 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
/* Set up host requested features. */
bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1);
- if (phba->cfg_enable_fip)
- bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 0);
- else
- bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 1);
-
/* Enable DIF (block guard) only if configured to do so. */
if (phba->cfg_enable_bg)
bf_set(lpfc_mbx_rq_ftr_rq_dif, &mboxq->u.mqe.un.req_ftrs, 1);
@@ -1817,6 +1822,9 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
bf_set(lpfc_reg_vfi_vfi, reg_vfi, vport->vfi + vport->phba->vfi_base);
bf_set(lpfc_reg_vfi_fcfi, reg_vfi, vport->phba->fcf.fcfi);
bf_set(lpfc_reg_vfi_vpi, reg_vfi, vport->vpi + vport->phba->vpi_base);
+ memcpy(reg_vfi->wwn, &vport->fc_portname, sizeof(struct lpfc_name));
+ reg_vfi->wwn[0] = cpu_to_le32(reg_vfi->wwn[0]);
+ reg_vfi->wwn[1] = cpu_to_le32(reg_vfi->wwn[1]);
reg_vfi->bde.addrHigh = putPaddrHigh(phys);
reg_vfi->bde.addrLow = putPaddrLow(phys);
reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam);
@@ -1850,7 +1858,7 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi)
/**
* lpfc_unreg_vfi - Initialize the UNREG_VFI mailbox command
* @mbox: pointer to lpfc mbox command to initialize.
- * @vfi: VFI to be unregistered.
+ * @vport: vport associated with the VF.
*
* The UNREG_VFI mailbox command causes the SLI Host to put a virtual fabric
* (logical NPort) into the inactive state. The SLI Host must have logged out
@@ -1859,11 +1867,12 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi)
* fabric inactive.
**/
void
-lpfc_unreg_vfi(struct lpfcMboxq *mbox, uint16_t vfi)
+lpfc_unreg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport)
{
memset(mbox, 0, sizeof(*mbox));
bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_UNREG_VFI);
- bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi, vfi);
+ bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi,
+ vport->vfi + vport->phba->vfi_base);
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index 3e74136f1ed..2ed6af19493 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -1223,6 +1223,12 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
(ndlp == (struct lpfc_nodelist *) mb->context2)) {
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_sli4_free_rpi(phba,
+ mb->u.mb.un.varRegLogin.rpi);
+ spin_lock_irq(&phba->hbalock);
+ }
mp = (struct lpfc_dmabuf *) (mb->context1);
if (mp) {
__lpfc_mbuf_free(phba, mp->virt, mp->phys);
@@ -1230,6 +1236,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
}
lpfc_nlp_put(ndlp);
list_del(&mb->list);
+ phba->sli.mboxq_cnt--;
mempool_free(mb, phba->mbox_mem_pool);
}
}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index c88f59f0ce3..a246410ce9d 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -59,22 +59,26 @@ static char *dif_op_str[] = {
};
static void
lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
+static void
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
static void
-lpfc_debug_save_data(struct scsi_cmnd *cmnd)
+lpfc_debug_save_data(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
{
void *src, *dst;
struct scatterlist *sgde = scsi_sglist(cmnd);
if (!_dump_buf_data) {
- printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9050 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
__func__);
return;
}
if (!sgde) {
- printk(KERN_ERR "BLKGRD ERROR: data scatterlist is null\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9051 BLKGRD: ERROR: data scatterlist is null\n");
return;
}
@@ -88,19 +92,21 @@ lpfc_debug_save_data(struct scsi_cmnd *cmnd)
}
static void
-lpfc_debug_save_dif(struct scsi_cmnd *cmnd)
+lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
{
void *src, *dst;
struct scatterlist *sgde = scsi_prot_sglist(cmnd);
if (!_dump_buf_dif) {
- printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9052 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
__func__);
return;
}
if (!sgde) {
- printk(KERN_ERR "BLKGRD ERROR: prot scatterlist is null\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9053 BLKGRD: ERROR: prot scatterlist is null\n");
return;
}
@@ -242,6 +248,36 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
}
/**
+ * lpfc_change_queue_depth - Alter scsi device queue depth
+ * @sdev: Pointer the scsi device on which to change the queue depth.
+ * @qdepth: New queue depth to set the sdev to.
+ * @reason: The reason for the queue depth change.
+ *
+ * This function is called by the midlayer and the LLD to alter the queue
+ * depth for a scsi device. This function sets the queue depth to the new
+ * value and sends an event out to log the queue depth change.
+ **/
+int
+lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_rport_data *rdata;
+ unsigned long new_queue_depth, old_queue_depth;
+
+ old_queue_depth = sdev->queue_depth;
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ new_queue_depth = sdev->queue_depth;
+ rdata = sdev->hostdata;
+ if (rdata)
+ lpfc_send_sdev_queuedepth_change_event(phba, vport,
+ rdata->pnode, sdev->lun,
+ old_queue_depth,
+ new_queue_depth);
+ return sdev->queue_depth;
+}
+
+/**
* lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread
* @phba: The Hba for which this call is being executed.
*
@@ -305,8 +341,10 @@ lpfc_rampup_queue_depth(struct lpfc_vport *vport,
if (vport->cfg_lun_queue_depth <= queue_depth)
return;
spin_lock_irqsave(&phba->hbalock, flags);
- if (((phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) > jiffies) ||
- ((phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL ) > jiffies)) {
+ if (time_before(jiffies,
+ phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) ||
+ time_before(jiffies,
+ phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL)) {
spin_unlock_irqrestore(&phba->hbalock, flags);
return;
}
@@ -338,10 +376,9 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
struct lpfc_vport **vports;
struct Scsi_Host *shost;
struct scsi_device *sdev;
- unsigned long new_queue_depth, old_queue_depth;
+ unsigned long new_queue_depth;
unsigned long num_rsrc_err, num_cmd_success;
int i;
- struct lpfc_rport_data *rdata;
num_rsrc_err = atomic_read(&phba->num_rsrc_err);
num_cmd_success = atomic_read(&phba->num_cmd_success);
@@ -359,22 +396,8 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
else
new_queue_depth = sdev->queue_depth -
new_queue_depth;
- old_queue_depth = sdev->queue_depth;
- if (sdev->ordered_tags)
- scsi_adjust_queue_depth(sdev,
- MSG_ORDERED_TAG,
- new_queue_depth);
- else
- scsi_adjust_queue_depth(sdev,
- MSG_SIMPLE_TAG,
- new_queue_depth);
- rdata = sdev->hostdata;
- if (rdata)
- lpfc_send_sdev_queuedepth_change_event(
- phba, vports[i],
- rdata->pnode,
- sdev->lun, old_queue_depth,
- new_queue_depth);
+ lpfc_change_queue_depth(sdev, new_queue_depth,
+ SCSI_QDEPTH_DEFAULT);
}
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -398,7 +421,6 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
struct Scsi_Host *shost;
struct scsi_device *sdev;
int i;
- struct lpfc_rport_data *rdata;
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
@@ -408,22 +430,9 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
if (vports[i]->cfg_lun_queue_depth <=
sdev->queue_depth)
continue;
- if (sdev->ordered_tags)
- scsi_adjust_queue_depth(sdev,
- MSG_ORDERED_TAG,
- sdev->queue_depth+1);
- else
- scsi_adjust_queue_depth(sdev,
- MSG_SIMPLE_TAG,
- sdev->queue_depth+1);
- rdata = sdev->hostdata;
- if (rdata)
- lpfc_send_sdev_queuedepth_change_event(
- phba, vports[i],
- rdata->pnode,
- sdev->lun,
- sdev->queue_depth - 1,
- sdev->queue_depth);
+ lpfc_change_queue_depth(sdev,
+ sdev->queue_depth+1,
+ SCSI_QDEPTH_RAMP_UP);
}
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -589,7 +598,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
iocb->ulpClass = CLASS3;
psb->status = IOSTAT_SUCCESS;
/* Put it back into the SCSI buffer list */
- lpfc_release_scsi_buf_s4(phba, psb);
+ lpfc_release_scsi_buf_s3(phba, psb);
}
@@ -1024,7 +1033,8 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many sg segments from "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9064 BLKGRD: %s: Too many sg segments from "
"dma_map_sg. Config %d, seg_cnt %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
@@ -1112,7 +1122,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
* with the cmd
*/
static int
-lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
+lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc)
{
uint8_t guard_type = scsi_host_get_guard(sc->device->host);
uint8_t ret_prof = LPFC_PROF_INVALID;
@@ -1136,7 +1146,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
case SCSI_PROT_NORMAL:
default:
- printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9063 BLKGRD:Bad op/guard:%d/%d combination\n",
scsi_get_prot_op(sc), guard_type);
break;
@@ -1157,7 +1168,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
case SCSI_PROT_WRITE_STRIP:
case SCSI_PROT_NORMAL:
default:
- printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9075 BLKGRD: Bad op/guard:%d/%d combination\n",
scsi_get_prot_op(sc), guard_type);
break;
}
@@ -1259,7 +1271,7 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint16_t apptagmask, apptagval;
pde1 = (struct lpfc_pde *) bpl;
- prof = lpfc_sc_to_sli_prof(sc);
+ prof = lpfc_sc_to_sli_prof(phba, sc);
if (prof == LPFC_PROF_INVALID)
goto out;
@@ -1359,7 +1371,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
return 0;
}
- prof = lpfc_sc_to_sli_prof(sc);
+ prof = lpfc_sc_to_sli_prof(phba, sc);
if (prof == LPFC_PROF_INVALID)
goto out;
@@ -1408,7 +1420,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
subtotal = 0; /* total bytes processed for current prot grp */
while (!pgdone) {
if (!sgde) {
- printk(KERN_ERR "%s Invalid data segment\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9065 BLKGRD:%s Invalid data segment\n",
__func__);
return 0;
}
@@ -1462,7 +1475,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
reftag += protgrp_blks;
} else {
/* if we're here, we have a bug */
- printk(KERN_ERR "BLKGRD: bug in %s\n", __func__);
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9054 BLKGRD: bug in %s\n", __func__);
}
} while (!alldone);
@@ -1544,8 +1558,10 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
lpfc_cmd->seg_cnt = datasegcnt;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9067 BLKGRD: %s: Too many sg segments"
+ " from dma_map_sg. Config %d, seg_cnt"
+ " %d\n",
__func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
scsi_dma_unmap(scsi_cmnd);
@@ -1579,8 +1595,9 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
lpfc_cmd->prot_seg_cnt = protsegcnt;
if (lpfc_cmd->prot_seg_cnt
> phba->cfg_prot_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many prot sg segments "
- "from dma_map_sg. Config %d,"
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9068 BLKGRD: %s: Too many prot sg "
+ "segments from dma_map_sg. Config %d,"
"prot_seg_cnt %d\n", __func__,
phba->cfg_prot_sg_seg_cnt,
lpfc_cmd->prot_seg_cnt);
@@ -1671,23 +1688,26 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
uint32_t bgstat = bgf->bgstat;
uint64_t failing_sector = 0;
- printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%x "
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9069 BLKGRD: BG ERROR in cmd"
+ " 0x%x lba 0x%llx blk cnt 0x%x "
"bgstat=0x%x bghm=0x%x\n",
cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
blk_rq_sectors(cmd->request), bgstat, bghm);
spin_lock(&_dump_buf_lock);
if (!_dump_buf_done) {
- printk(KERN_ERR "Saving Data for %u blocks to debugfs\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9070 BLKGRD: Saving"
+ " Data for %u blocks to debugfs\n",
(cmd->cmnd[7] << 8 | cmd->cmnd[8]));
- lpfc_debug_save_data(cmd);
+ lpfc_debug_save_data(phba, cmd);
/* If we have a prot sgl, save the DIF buffer */
if (lpfc_prot_group_type(phba, cmd) ==
LPFC_PG_TYPE_DIF_BUF) {
- printk(KERN_ERR "Saving DIF for %u blocks to debugfs\n",
- (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
- lpfc_debug_save_dif(cmd);
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9071 BLKGRD: "
+ "Saving DIF for %u blocks to debugfs\n",
+ (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+ lpfc_debug_save_dif(phba, cmd);
}
_dump_buf_done = 1;
@@ -1696,15 +1716,17 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
if (lpfc_bgs_get_invalid_prof(bgstat)) {
cmd->result = ScsiResult(DID_ERROR, 0);
- printk(KERN_ERR "Invalid BlockGuard profile. bgstat:0x%x\n",
- bgstat);
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9072 BLKGRD: Invalid"
+ " BlockGuard profile. bgstat:0x%x\n",
+ bgstat);
ret = (-1);
goto out;
}
if (lpfc_bgs_get_uninit_dif_block(bgstat)) {
cmd->result = ScsiResult(DID_ERROR, 0);
- printk(KERN_ERR "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9073 BLKGRD: "
+ "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
bgstat);
ret = (-1);
goto out;
@@ -1718,7 +1740,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
cmd->result = DRIVER_SENSE << 24
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_guard_err_cnt++;
- printk(KERN_ERR "BLKGRD: guard_tag error\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9055 BLKGRD: guard_tag error\n");
}
if (lpfc_bgs_get_reftag_err(bgstat)) {
@@ -1730,7 +1753,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_reftag_err_cnt++;
- printk(KERN_ERR "BLKGRD: ref_tag error\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9056 BLKGRD: ref_tag error\n");
}
if (lpfc_bgs_get_apptag_err(bgstat)) {
@@ -1742,7 +1766,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
| ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
phba->bg_apptag_err_cnt++;
- printk(KERN_ERR "BLKGRD: app_tag error\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9061 BLKGRD: app_tag error\n");
}
if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
@@ -1763,7 +1788,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
if (!ret) {
/* No error was reported - problem in FW? */
cmd->result = ScsiResult(DID_ERROR, 0);
- printk(KERN_ERR "BLKGRD: no errors reported!\n");
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9057 BLKGRD: no errors reported!\n");
}
out:
@@ -1822,9 +1848,10 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
lpfc_cmd->seg_cnt = nseg;
if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
- printk(KERN_ERR "%s: Too many sg segments from "
- "dma_map_sg. Config %d, seg_cnt %d\n",
- __func__, phba->cfg_sg_seg_cnt,
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9074 BLKGRD:"
+ " %s: Too many sg segments from "
+ "dma_map_sg. Config %d, seg_cnt %d\n",
+ __func__, phba->cfg_sg_seg_cnt,
lpfc_cmd->seg_cnt);
scsi_dma_unmap(scsi_cmnd);
return 1;
@@ -2050,6 +2077,21 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
goto out;
}
+ if (resp_info & RSP_LEN_VALID) {
+ rsplen = be32_to_cpu(fcprsp->rspRspLen);
+ if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
+ (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "2719 Invalid response length: "
+ "tgt x%x lun x%x cmnd x%x rsplen x%x\n",
+ cmnd->device->id,
+ cmnd->device->lun, cmnd->cmnd[0],
+ rsplen);
+ host_status = DID_ERROR;
+ goto out;
+ }
+ }
+
if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) {
uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen);
if (snslen > SCSI_SENSE_BUFFERSIZE)
@@ -2074,15 +2116,6 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
be32_to_cpu(fcprsp->rspRspLen),
fcprsp->rspInfo3);
- if (resp_info & RSP_LEN_VALID) {
- rsplen = be32_to_cpu(fcprsp->rspRspLen);
- if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
- (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
- host_status = DID_ERROR;
- goto out;
- }
- }
-
scsi_set_resid(cmnd, 0);
if (resp_info & RESID_UNDER) {
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
@@ -2180,7 +2213,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
int result;
struct scsi_device *tmp_sdev;
- int depth = 0;
+ int depth;
unsigned long flags;
struct lpfc_fast_path_event *fast_path_evt;
struct Scsi_Host *shost = cmd->device->host;
@@ -2264,7 +2297,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
lpfc_printf_vlog(vport, KERN_WARNING,
LOG_BG,
"9031 non-zero BGSTAT "
- "on unprotected cmd");
+ "on unprotected cmd\n");
}
}
@@ -2347,67 +2380,29 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
return;
}
-
if (!result)
lpfc_rampup_queue_depth(vport, queue_depth);
- if (!result && pnode && NLP_CHK_NODE_ACT(pnode) &&
- ((jiffies - pnode->last_ramp_up_time) >
- LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
- ((jiffies - pnode->last_q_full_time) >
- LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
- (vport->cfg_lun_queue_depth > queue_depth)) {
- shost_for_each_device(tmp_sdev, shost) {
- if (vport->cfg_lun_queue_depth > tmp_sdev->queue_depth){
- if (tmp_sdev->id != scsi_id)
- continue;
- if (tmp_sdev->ordered_tags)
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_ORDERED_TAG,
- tmp_sdev->queue_depth+1);
- else
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_SIMPLE_TAG,
- tmp_sdev->queue_depth+1);
-
- pnode->last_ramp_up_time = jiffies;
- }
- }
- lpfc_send_sdev_queuedepth_change_event(phba, vport, pnode,
- 0xFFFFFFFF,
- queue_depth , queue_depth + 1);
- }
-
/*
* Check for queue full. If the lun is reporting queue full, then
* back off the lun queue depth to prevent target overloads.
*/
if (result == SAM_STAT_TASK_SET_FULL && pnode &&
NLP_CHK_NODE_ACT(pnode)) {
- pnode->last_q_full_time = jiffies;
-
shost_for_each_device(tmp_sdev, shost) {
if (tmp_sdev->id != scsi_id)
continue;
depth = scsi_track_queue_full(tmp_sdev,
- tmp_sdev->queue_depth - 1);
- }
- /*
- * The queue depth cannot be lowered any more.
- * Modify the returned error code to store
- * the final depth value set by
- * scsi_track_queue_full.
- */
- if (depth == -1)
- depth = shost->cmd_per_lun;
-
- if (depth) {
+ tmp_sdev->queue_depth-1);
+ if (depth <= 0)
+ continue;
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"0711 detected queue full - lun queue "
"depth adjusted to %d.\n", depth);
lpfc_send_sdev_queuedepth_change_event(phba, vport,
- pnode, 0xFFFFFFFF,
- depth+1, depth);
+ pnode,
+ tmp_sdev->lun,
+ depth+1, depth);
}
}
@@ -2745,7 +2740,9 @@ void lpfc_poll_timeout(unsigned long ptr)
struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- lpfc_sli_poll_fcp_ring (phba);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
@@ -2771,7 +2768,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_hba *phba = vport->phba;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *ndlp = rdata->pnode;
+ struct lpfc_nodelist *ndlp;
struct lpfc_scsi_buf *lpfc_cmd;
struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
int err;
@@ -2781,13 +2778,15 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->result = err;
goto out_fail_command;
}
+ ndlp = rdata->pnode;
if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
- printk(KERN_ERR "BLKGRD ERROR: rcvd protected cmd:%02x op:%02x "
- "str=%s without registering for BlockGuard - "
- "Rejecting command\n",
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9058 BLKGRD: ERROR: rcvd protected cmd:%02x"
+ " op:%02x str=%s without registering for"
+ " BlockGuard - Rejecting command\n",
cmnd->cmnd[0], scsi_get_prot_op(cmnd),
dif_op_str[scsi_get_prot_op(cmnd)]);
goto out_fail_command;
@@ -2827,61 +2826,66 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
cmnd->scsi_done = done;
if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ if (vport->phba->cfg_enable_bg) {
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
"str=%s\n",
cmnd->cmnd[0], scsi_get_prot_op(cmnd),
dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x\n",
cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
cmnd->cmnd[9]);
- if (cmnd->cmnd[0] == READ_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9035 BLKGRD: READ @ sector %llu, "
"count %u\n",
(unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request));
- else if (cmnd->cmnd[0] == WRITE_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9036 BLKGRD: WRITE @ sector %llu, "
"count %u cmd=%p\n",
(unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request),
cmnd);
+ }
err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
} else {
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9038 BLKGRD: rcvd unprotected cmd:%02x op:%02x"
- " str=%s\n",
- cmnd->cmnd[0], scsi_get_prot_op(cmnd),
- dif_op_str[scsi_get_prot_op(cmnd)]);
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9039 BLKGRD: CDB: %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x\n",
- cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
- cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
- cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
- cmnd->cmnd[9]);
- if (cmnd->cmnd[0] == READ_10)
+ if (vport->phba->cfg_enable_bg) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
- "9040 dbg: READ @ sector %llu, "
- "count %u\n",
- (unsigned long long)scsi_get_lba(cmnd),
+ "9038 BLKGRD: rcvd unprotected cmd:"
+ "%02x op:%02x str=%s\n",
+ cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+ dif_op_str[scsi_get_prot_op(cmnd)]);
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9039 BLKGRD: CDB: %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x\n",
+ cmnd->cmnd[0], cmnd->cmnd[1],
+ cmnd->cmnd[2], cmnd->cmnd[3],
+ cmnd->cmnd[4], cmnd->cmnd[5],
+ cmnd->cmnd[6], cmnd->cmnd[7],
+ cmnd->cmnd[8], cmnd->cmnd[9]);
+ if (cmnd->cmnd[0] == READ_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ "9040 dbg: READ @ sector %llu, "
+ "count %u\n",
+ (unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request));
- else if (cmnd->cmnd[0] == WRITE_10)
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ else if (cmnd->cmnd[0] == WRITE_10)
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9041 dbg: WRITE @ sector %llu, "
"count %u cmd=%p\n",
(unsigned long long)scsi_get_lba(cmnd),
blk_rq_sectors(cmnd->request), cmnd);
- else
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+ else
+ lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9042 dbg: parser not implemented\n");
+ }
err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
}
@@ -2898,7 +2902,11 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
goto out_host_busy_free_buf;
}
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- lpfc_sli_poll_fcp_ring(phba);
+ spin_unlock(shost->host_lock);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+
+ spin_lock(shost->host_lock);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
@@ -2917,28 +2925,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
}
/**
- * lpfc_block_error_handler - Routine to block error handler
- * @cmnd: Pointer to scsi_cmnd data structure.
- *
- * This routine blocks execution till fc_rport state is not FC_PORSTAT_BLCOEKD.
- **/
-static void
-lpfc_block_error_handler(struct scsi_cmnd *cmnd)
-{
- struct Scsi_Host *shost = cmnd->device->host;
- struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
-
- spin_lock_irq(shost->host_lock);
- while (rport->port_state == FC_PORTSTATE_BLOCKED) {
- spin_unlock_irq(shost->host_lock);
- msleep(1000);
- spin_lock_irq(shost->host_lock);
- }
- spin_unlock_irq(shost->host_lock);
- return;
-}
-
-/**
* lpfc_abort_handler - scsi_host_template eh_abort_handler entry point
* @cmnd: Pointer to scsi_cmnd data structure.
*
@@ -2961,7 +2947,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
int ret = SUCCESS;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
- lpfc_block_error_handler(cmnd);
+ fc_block_scsi_eh(cmnd);
lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble;
BUG_ON(!lpfc_cmd);
@@ -3001,6 +2987,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
icmd->ulpLe = 1;
icmd->ulpClass = cmd->ulpClass;
+
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocb->fcp_wqidx = iocb->fcp_wqidx;
+
if (lpfc_is_link_up(phba))
icmd->ulpCommand = CMD_ABORT_XRI_CN;
else
@@ -3016,7 +3006,8 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
}
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
- lpfc_sli_poll_fcp_ring (phba);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
lpfc_cmd->waitq = &waitq;
/* Wait for abort to complete */
@@ -3166,9 +3157,15 @@ static int
lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd)
{
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *pnode = rdata->pnode;
+ struct lpfc_nodelist *pnode;
unsigned long later;
+ if (!rdata) {
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+ "0797 Tgt Map rport failure: rdata x%p\n", rdata);
+ return FAILED;
+ }
+ pnode = rdata->pnode;
/*
* If target is not in a MAPPED state, delay until
* target is rediscovered or devloss timeout expires.
@@ -3253,13 +3250,19 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *pnode = rdata->pnode;
+ struct lpfc_nodelist *pnode;
unsigned tgt_id = cmnd->device->id;
unsigned int lun_id = cmnd->device->lun;
struct lpfc_scsi_event_header scsi_event;
int status;
- lpfc_block_error_handler(cmnd);
+ if (!rdata) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "0798 Device Reset rport failure: rdata x%p\n", rdata);
+ return FAILED;
+ }
+ pnode = rdata->pnode;
+ fc_block_scsi_eh(cmnd);
status = lpfc_chk_tgt_mapped(vport, cmnd);
if (status == FAILED) {
@@ -3312,13 +3315,19 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd)
struct Scsi_Host *shost = cmnd->device->host;
struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
struct lpfc_rport_data *rdata = cmnd->device->hostdata;
- struct lpfc_nodelist *pnode = rdata->pnode;
+ struct lpfc_nodelist *pnode;
unsigned tgt_id = cmnd->device->id;
unsigned int lun_id = cmnd->device->lun;
struct lpfc_scsi_event_header scsi_event;
int status;
- lpfc_block_error_handler(cmnd);
+ if (!rdata) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+ "0799 Target Reset rport failure: rdata x%p\n", rdata);
+ return FAILED;
+ }
+ pnode = rdata->pnode;
+ fc_block_scsi_eh(cmnd);
status = lpfc_chk_tgt_mapped(vport, cmnd);
if (status == FAILED) {
@@ -3384,7 +3393,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
fc_host_post_vendor_event(shost, fc_get_event_number(),
sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID);
- lpfc_block_error_handler(cmnd);
+ fc_block_scsi_eh(cmnd);
/*
* Since the driver manages a single bus device, reset all
@@ -3498,6 +3507,8 @@ lpfc_slave_alloc(struct scsi_device *sdev)
"Allocated %d buffers.\n",
num_to_alloc, num_allocated);
}
+ if (num_allocated > 0)
+ phba->total_scsi_bufs += num_allocated;
return 0;
}
@@ -3534,7 +3545,8 @@ lpfc_slave_configure(struct scsi_device *sdev)
rport->dev_loss_tmo = vport->cfg_devloss_tmo;
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- lpfc_sli_poll_fcp_ring(phba);
+ lpfc_sli_handle_fast_ring_event(phba,
+ &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
if (phba->cfg_poll & DISABLE_FCP_RING_INT)
lpfc_poll_rearm_timer(phba);
}
@@ -3576,6 +3588,7 @@ struct scsi_host_template lpfc_template = {
.shost_attrs = lpfc_hba_attrs,
.max_sectors = 0xFFFF,
.vendor_id = LPFC_NL_VENDOR_ID,
+ .change_queue_depth = lpfc_change_queue_depth,
};
struct scsi_host_template lpfc_vport_template = {
@@ -3597,4 +3610,5 @@ struct scsi_host_template lpfc_vport_template = {
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = lpfc_vport_attrs,
.max_sectors = 0xFFFF,
+ .change_queue_depth = lpfc_change_queue_depth,
};
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 43cbe336f1f..7935667b81a 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -30,6 +30,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc/fc_fs.h>
+#include <linux/aer.h>
#include "lpfc_hw4.h"
#include "lpfc_hw.h"
@@ -58,8 +59,11 @@ typedef enum _lpfc_iocb_type {
static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
uint32_t);
static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
- uint8_t *, uint32_t *);
-
+ uint8_t *, uint32_t *);
+static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *,
+ struct lpfc_iocbq *);
+static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
+ struct hbq_dmabuf *);
static IOCB_t *
lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
{
@@ -259,6 +263,9 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ /* PCI read to flush PCI pipeline on re-arming for INTx mode */
+ if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
+ readl(q->phba->sli4_hba.EQCQDBregaddr);
return released;
}
@@ -515,6 +522,8 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba)
struct lpfc_sglq *sglq = NULL;
uint16_t adj_xri;
list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list);
+ if (!sglq)
+ return NULL;
adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base;
phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq;
return sglq;
@@ -572,9 +581,9 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag);
if (sglq) {
if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED
- || ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
+ && ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
&& (iocbq->iocb.un.ulpWord[4]
- == IOERR_SLI_ABORTED))) {
+ == IOERR_ABORT_REQUESTED))) {
spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
iflag);
list_add(&sglq->list,
@@ -767,6 +776,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
case CMD_CLOSE_XRI_CX:
case CMD_XRI_ABORTED_CX:
case CMD_ABORT_MXRI64_CN:
+ case CMD_XMIT_BLS_RSP64_CX:
type = LPFC_ABORT_IOCB;
break;
case CMD_RCV_SEQUENCE_CX:
@@ -1794,7 +1804,7 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
*/
if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) ==
MBX_SHUTDOWN) {
- /* Unknow mailbox command compl */
+ /* Unknown mailbox command compl */
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"(%d):0323 Unknown Mailbox command "
"x%x (x%x) Cmpl\n",
@@ -2068,8 +2078,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) ||
(irsp->ulpCommand == CMD_RCV_ELS_REQ_CX) ||
(irsp->ulpCommand == CMD_IOCB_RCV_ELS64_CX)) {
- Rctl = FC_ELS_REQ;
- Type = FC_ELS_DATA;
+ Rctl = FC_RCTL_ELS_REQ;
+ Type = FC_TYPE_ELS;
} else {
w5p = (WORD5 *)&(saveq->iocb.un.ulpWord[5]);
Rctl = w5p->hcsw.Rctl;
@@ -2079,8 +2089,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
if ((Rctl == 0) && (pring->ringno == LPFC_ELS_RING) &&
(irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX ||
irsp->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
- Rctl = FC_ELS_REQ;
- Type = FC_ELS_DATA;
+ Rctl = FC_RCTL_ELS_REQ;
+ Type = FC_TYPE_ELS;
w5p->hcsw.Rctl = Rctl;
w5p->hcsw.Type = Type;
}
@@ -2324,168 +2334,6 @@ void lpfc_poll_eratt(unsigned long ptr)
return;
}
-/**
- * lpfc_sli_poll_fcp_ring - Handle FCP ring completion in polling mode
- * @phba: Pointer to HBA context object.
- *
- * This function is called from lpfc_queuecommand, lpfc_poll_timeout,
- * lpfc_abort_handler and lpfc_slave_configure when FCP_RING_POLLING
- * is enabled.
- *
- * The caller does not hold any lock.
- * The function processes each response iocb in the response ring until it
- * finds an iocb with LE bit set and chains all the iocbs upto the iocb with
- * LE bit set. The function will call the completion handler of the command iocb
- * if the response iocb indicates a completion for a command iocb or it is
- * an abort completion.
- **/
-void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
-{
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING];
- IOCB_t *irsp = NULL;
- IOCB_t *entry = NULL;
- struct lpfc_iocbq *cmdiocbq = NULL;
- struct lpfc_iocbq rspiocbq;
- struct lpfc_pgp *pgp = &phba->port_gp[pring->ringno];
- uint32_t status;
- uint32_t portRspPut, portRspMax;
- int type;
- uint32_t rsp_cmpl = 0;
- uint32_t ha_copy;
- unsigned long iflags;
-
- pring->stats.iocb_event++;
-
- /*
- * The next available response entry should never exceed the maximum
- * entries. If it does, treat it as an adapter hardware error.
- */
- portRspMax = pring->numRiocb;
- portRspPut = le32_to_cpu(pgp->rspPutInx);
- if (unlikely(portRspPut >= portRspMax)) {
- lpfc_sli_rsp_pointers_error(phba, pring);
- return;
- }
-
- rmb();
- while (pring->rspidx != portRspPut) {
- entry = lpfc_resp_iocb(phba, pring);
- if (++pring->rspidx >= portRspMax)
- pring->rspidx = 0;
-
- lpfc_sli_pcimem_bcopy((uint32_t *) entry,
- (uint32_t *) &rspiocbq.iocb,
- phba->iocb_rsp_size);
- irsp = &rspiocbq.iocb;
- type = lpfc_sli_iocb_cmd_type(irsp->ulpCommand & CMD_IOCB_MASK);
- pring->stats.iocb_rsp++;
- rsp_cmpl++;
-
- if (unlikely(irsp->ulpStatus)) {
- /* Rsp ring <ringno> error: IOCB */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0326 Rsp Ring %d error: IOCB Data: "
- "x%x x%x x%x x%x x%x x%x x%x x%x\n",
- pring->ringno,
- irsp->un.ulpWord[0],
- irsp->un.ulpWord[1],
- irsp->un.ulpWord[2],
- irsp->un.ulpWord[3],
- irsp->un.ulpWord[4],
- irsp->un.ulpWord[5],
- *(uint32_t *)&irsp->un1,
- *((uint32_t *)&irsp->un1 + 1));
- }
-
- switch (type) {
- case LPFC_ABORT_IOCB:
- case LPFC_SOL_IOCB:
- /*
- * Idle exchange closed via ABTS from port. No iocb
- * resources need to be recovered.
- */
- if (unlikely(irsp->ulpCommand == CMD_XRI_ABORTED_CX)) {
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "0314 IOCB cmd 0x%x "
- "processed. Skipping "
- "completion",
- irsp->ulpCommand);
- break;
- }
-
- spin_lock_irqsave(&phba->hbalock, iflags);
- cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
- &rspiocbq);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
- }
- break;
- default:
- if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
- char adaptermsg[LPFC_MAX_ADPTMSG];
- memset(adaptermsg, 0, LPFC_MAX_ADPTMSG);
- memcpy(&adaptermsg[0], (uint8_t *) irsp,
- MAX_MSG_DATA);
- dev_warn(&((phba->pcidev)->dev),
- "lpfc%d: %s\n",
- phba->brd_no, adaptermsg);
- } else {
- /* Unknown IOCB command */
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0321 Unknown IOCB command "
- "Data: x%x, x%x x%x x%x x%x\n",
- type, irsp->ulpCommand,
- irsp->ulpStatus,
- irsp->ulpIoTag,
- irsp->ulpContext);
- }
- break;
- }
-
- /*
- * The response IOCB has been processed. Update the ring
- * pointer in SLIM. If the port response put pointer has not
- * been updated, sync the pgp->rspPutInx and fetch the new port
- * response put pointer.
- */
- writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
-
- if (pring->rspidx == portRspPut)
- portRspPut = le32_to_cpu(pgp->rspPutInx);
- }
-
- ha_copy = readl(phba->HAregaddr);
- ha_copy >>= (LPFC_FCP_RING * 4);
-
- if ((rsp_cmpl > 0) && (ha_copy & HA_R0RE_REQ)) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->stats.iocb_rsp_full++;
- status = ((CA_R0ATT | CA_R0RE_RSP) << (LPFC_FCP_RING * 4));
- writel(status, phba->CAregaddr);
- readl(phba->CAregaddr);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- }
- if ((ha_copy & HA_R0CE_RSP) &&
- (pring->flag & LPFC_CALL_RING_AVAILABLE)) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->flag &= ~LPFC_CALL_RING_AVAILABLE;
- pring->stats.iocb_cmd_empty++;
-
- /* Force update of the local copy of cmdGetInx */
- pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
- lpfc_sli_resume_iocb(phba, pring);
-
- if ((pring->lpfc_sli_cmd_available))
- (pring->lpfc_sli_cmd_available) (phba, pring);
-
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- }
-
- return;
-}
/**
* lpfc_sli_handle_fast_ring_event - Handle ring events on FCP ring
@@ -2502,9 +2350,9 @@ void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
* an abort completion. The function will call lpfc_sli_process_unsol_iocb
* function if this is an unsolicited iocb.
* This routine presumes LPFC_FCP_RING handling and doesn't bother
- * to check it explicitly. This function always returns 1.
- **/
-static int
+ * to check it explicitly.
+ */
+int
lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t mask)
{
@@ -2534,6 +2382,11 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
spin_unlock_irqrestore(&phba->hbalock, iflag);
return 1;
}
+ if (phba->fcp_ring_in_use) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return 1;
+ } else
+ phba->fcp_ring_in_use = 1;
rmb();
while (pring->rspidx != portRspPut) {
@@ -2604,10 +2457,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
&rspiocbq);
if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
- if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
- } else {
spin_unlock_irqrestore(&phba->hbalock,
iflag);
(cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
@@ -2615,7 +2464,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
spin_lock_irqsave(&phba->hbalock,
iflag);
}
- }
break;
case LPFC_UNSOL_IOCB:
spin_unlock_irqrestore(&phba->hbalock, iflag);
@@ -2675,6 +2523,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
}
+ phba->fcp_ring_in_use = 0;
spin_unlock_irqrestore(&phba->hbalock, iflag);
return rc;
}
@@ -3018,16 +2867,39 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t mask)
{
struct lpfc_iocbq *irspiocbq;
+ struct hbq_dmabuf *dmabuf;
+ struct lpfc_cq_event *cq_event;
unsigned long iflag;
- while (!list_empty(&phba->sli4_hba.sp_rspiocb_work_queue)) {
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ phba->hba_flag &= ~HBA_SP_QUEUE_EVT;
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
/* Get the response iocb from the head of work queue */
spin_lock_irqsave(&phba->hbalock, iflag);
- list_remove_head(&phba->sli4_hba.sp_rspiocb_work_queue,
- irspiocbq, struct lpfc_iocbq, list);
+ list_remove_head(&phba->sli4_hba.sp_queue_event,
+ cq_event, struct lpfc_cq_event, list);
spin_unlock_irqrestore(&phba->hbalock, iflag);
- /* Process the response iocb */
- lpfc_sli_sp_handle_rspiocb(phba, pring, irspiocbq);
+
+ switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) {
+ case CQE_CODE_COMPL_WQE:
+ irspiocbq = container_of(cq_event, struct lpfc_iocbq,
+ cq_event);
+ /* Translate ELS WCQE to response IOCBQ */
+ irspiocbq = lpfc_sli4_els_wcqe_to_rspiocbq(phba,
+ irspiocbq);
+ if (irspiocbq)
+ lpfc_sli_sp_handle_rspiocb(phba, pring,
+ irspiocbq);
+ break;
+ case CQE_CODE_RECEIVE:
+ dmabuf = container_of(cq_event, struct hbq_dmabuf,
+ cq_event);
+ lpfc_sli4_handle_received_buffer(phba, dmabuf);
+ break;
+ default:
+ break;
+ }
}
}
@@ -3416,6 +3288,7 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
/* perform board reset */
phba->fc_eventTag = 0;
+ phba->link_events = 0;
phba->pport->fc_myDID = 0;
phba->pport->fc_prevDID = 0;
@@ -3476,6 +3349,7 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
/* perform board reset */
phba->fc_eventTag = 0;
+ phba->link_events = 0;
phba->pport->fc_myDID = 0;
phba->pport->fc_prevDID = 0;
@@ -3495,7 +3369,6 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
list_del_init(&phba->sli4_hba.dat_rq->list);
list_del_init(&phba->sli4_hba.mbx_cq->list);
list_del_init(&phba->sli4_hba.els_cq->list);
- list_del_init(&phba->sli4_hba.rxq_cq->list);
for (qindx = 0; qindx < phba->cfg_fcp_wq_count; qindx++)
list_del_init(&phba->sli4_hba.fcp_wq[qindx]->list);
for (qindx = 0; qindx < phba->cfg_fcp_eq_count; qindx++)
@@ -3531,9 +3404,13 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
struct lpfc_sli *psli;
volatile uint32_t word0;
void __iomem *to_slim;
+ uint32_t hba_aer_enabled;
spin_lock_irq(&phba->hbalock);
+ /* Take PCIe device Advanced Error Reporting (AER) state */
+ hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED;
+
psli = &phba->sli;
/* Restart HBA */
@@ -3573,6 +3450,10 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
/* Give the INITFF and Post time to settle. */
mdelay(100);
+ /* Reset HBA AER if it was enabled, note hba_flag was reset above */
+ if (hba_aer_enabled)
+ pci_disable_pcie_error_reporting(phba->pcidev);
+
lpfc_hba_down_post(phba);
return 0;
@@ -4042,6 +3923,24 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
if (rc)
goto lpfc_sli_hba_setup_error;
+ /* Enable PCIe device Advanced Error Reporting (AER) if configured */
+ if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) {
+ rc = pci_enable_pcie_error_reporting(phba->pcidev);
+ if (!rc) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2709 This device supports "
+ "Advanced Error Reporting (AER)\n");
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag |= HBA_AER_ENABLED;
+ spin_unlock_irq(&phba->hbalock);
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2708 This device does not support "
+ "Advanced Error Reporting (AER)\n");
+ phba->cfg_aer_support = 0;
+ }
+ }
+
if (phba->sli_rev == 3) {
phba->iocb_cmd_size = SLI3_IOCB_CMD_SIZE;
phba->iocb_rsp_size = SLI3_IOCB_RSP_SIZE;
@@ -4163,7 +4062,7 @@ lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
* addition, this routine gets the port vpd data.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - could not allocated memory.
**/
static int
@@ -4243,7 +4142,6 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
lpfc_sli4_cq_release(phba->sli4_hba.mbx_cq, LPFC_QUEUE_REARM);
lpfc_sli4_cq_release(phba->sli4_hba.els_cq, LPFC_QUEUE_REARM);
- lpfc_sli4_cq_release(phba->sli4_hba.rxq_cq, LPFC_QUEUE_REARM);
for (fcp_eqidx = 0; fcp_eqidx < phba->cfg_fcp_eq_count; fcp_eqidx++)
lpfc_sli4_cq_release(phba->sli4_hba.fcp_cq[fcp_eqidx],
LPFC_QUEUE_REARM);
@@ -4322,6 +4220,13 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
phba->sli_rev = bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev);
if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev))
phba->hba_flag |= HBA_FCOE_SUPPORT;
+
+ if (bf_get(lpfc_mbx_rd_rev_cee_ver, &mqe->un.read_rev) ==
+ LPFC_DCBX_CEE_MODE)
+ phba->hba_flag |= HBA_FIP_SUPPORT;
+ else
+ phba->hba_flag &= ~HBA_FIP_SUPPORT;
+
if (phba->sli_rev != LPFC_SLI_REV4 ||
!(phba->hba_flag & HBA_FCOE_SUPPORT)) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
@@ -4468,7 +4373,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
rc = lpfc_sli4_post_sgl_list(phba);
if (unlikely(rc)) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "0582 Error %d during sgl post operation", rc);
+ "0582 Error %d during sgl post operation\n",
+ rc);
rc = -ENODEV;
goto out_free_vpd;
}
@@ -4477,8 +4383,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
rc = lpfc_sli4_repost_scsi_sgl_list(phba);
if (unlikely(rc)) {
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_SLI,
- "0383 Error %d during scsi sgl post opeation",
- rc);
+ "0383 Error %d during scsi sgl post "
+ "operation\n", rc);
/* Some Scsi buffers were moved to the abort scsi list */
/* A pci function reset will repost them */
rc = -ENODEV;
@@ -4494,10 +4400,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
rc = -ENODEV;
goto out_free_vpd;
}
- if (phba->cfg_enable_fip)
- bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 1);
- else
- bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 0);
/* Set up all the queues to the device */
rc = lpfc_sli4_queue_setup(phba);
@@ -5669,7 +5571,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
case CMD_GEN_REQUEST64_CX:
if (!(phba->sli.sli_flag & LPFC_MENLO_MAINT) ||
(piocb->iocb.un.genreq64.w5.hcsw.Rctl !=
- FC_FCP_CMND) ||
+ FC_RCTL_DD_UNSOL_CMD) ||
(piocb->iocb.un.genreq64.w5.hcsw.Type !=
MENLO_TRANSPORT_TYPE))
@@ -5849,7 +5751,7 @@ static int
lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
union lpfc_wqe *wqe)
{
- uint32_t payload_len = 0;
+ uint32_t xmit_len = 0, total_len = 0;
uint8_t ct = 0;
uint32_t fip;
uint32_t abort_tag;
@@ -5857,12 +5759,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
uint8_t cmnd;
uint16_t xritag;
struct ulp_bde64 *bpl = NULL;
+ uint32_t els_id = ELS_ID_DEFAULT;
+ int numBdes, i;
+ struct ulp_bde64 bde;
- fip = bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags);
+ fip = phba->hba_flag & HBA_FIP_SUPPORT;
/* The fcp commands will set command type */
if (iocbq->iocb_flag & LPFC_IO_FCP)
command_type = FCP_COMMAND;
- else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS))
+ else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK))
command_type = ELS_COMMAND_FIP;
else
command_type = ELS_COMMAND_NON_FIP;
@@ -5874,6 +5779,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
wqe->words[7] = 0; /* The ct field has moved so reset */
/* words0-2 bpl convert bde */
if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
+ numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize /
+ sizeof(struct ulp_bde64);
bpl = (struct ulp_bde64 *)
((struct lpfc_dmabuf *)iocbq->context3)->virt;
if (!bpl)
@@ -5886,9 +5793,14 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
* can assign it to the sgl.
*/
wqe->generic.bde.tus.w = le32_to_cpu(bpl->tus.w);
- payload_len = wqe->generic.bde.tus.f.bdeSize;
+ xmit_len = wqe->generic.bde.tus.f.bdeSize;
+ total_len = 0;
+ for (i = 0; i < numBdes; i++) {
+ bde.tus.w = le32_to_cpu(bpl[i].tus.w);
+ total_len += bde.tus.f.bdeSize;
+ }
} else
- payload_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
+ xmit_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
iocbq->iocb.ulpIoTag = iocbq->iotag;
cmnd = iocbq->iocb.ulpCommand;
@@ -5902,7 +5814,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
iocbq->iocb.ulpCommand);
return IOCB_ERROR;
}
- wqe->els_req.payload_len = payload_len;
+ wqe->els_req.payload_len = xmit_len;
/* Els_reguest64 has a TMO */
bf_set(wqe_tmo, &wqe->els_req.wqe_com,
iocbq->iocb.ulpTimeout);
@@ -5923,7 +5835,23 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(lpfc_wqe_gen_ct, &wqe->generic, ct);
bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
/* CCP CCPE PV PRI in word10 were set in the memcpy */
+
+ if (command_type == ELS_COMMAND_FIP) {
+ els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK)
+ >> LPFC_FIP_ELS_ID_SHIFT);
+ }
+ bf_set(lpfc_wqe_gen_els_id, &wqe->generic, els_id);
+
break;
+ case CMD_XMIT_SEQUENCE64_CX:
+ bf_set(lpfc_wqe_gen_context, &wqe->generic,
+ iocbq->iocb.un.ulpWord[3]);
+ wqe->generic.word3 = 0;
+ bf_set(wqe_rcvoxid, &wqe->generic, iocbq->iocb.ulpContext);
+ bf_set(wqe_xc, &wqe->generic, 1);
+ /* The entire sequence is transmitted for this IOCB */
+ xmit_len = total_len;
+ cmnd = CMD_XMIT_SEQUENCE64_CR;
case CMD_XMIT_SEQUENCE64_CR:
/* word3 iocb=io_tag32 wqe=payload_offset */
/* payload offset used for multilpe outstanding
@@ -5933,7 +5861,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
/* word4 relative_offset memcpy */
/* word5 r_ctl/df_ctl memcpy */
bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
- wqe->xmit_sequence.xmit_len = payload_len;
+ wqe->xmit_sequence.xmit_len = xmit_len;
+ command_type = OTHER_COMMAND;
break;
case CMD_XMIT_BCAST64_CN:
/* word3 iocb=iotag32 wqe=payload_len */
@@ -5962,7 +5891,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
case CMD_FCP_IREAD64_CR:
/* FCP_CMD is always the 1st sgl entry */
wqe->fcp_iread.payload_len =
- payload_len + sizeof(struct fcp_rsp);
+ xmit_len + sizeof(struct fcp_rsp);
/* word 4 (xfer length) should have been set on the memcpy */
@@ -5999,7 +5928,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
* sgl[1] = rsp.
*
*/
- wqe->gen_req.command_len = payload_len;
+ wqe->gen_req.command_len = xmit_len;
/* Word4 parameter copied in the memcpy */
/* Word5 [rctl, type, df_ctl, la] copied in memcpy */
/* word6 context tag copied in memcpy */
@@ -6066,6 +5995,38 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
command_type = OTHER_COMMAND;
xritag = 0;
break;
+ case CMD_XMIT_BLS_RSP64_CX:
+ /* As BLS ABTS-ACC WQE is very different from other WQEs,
+ * we re-construct this WQE here based on information in
+ * iocbq from scratch.
+ */
+ memset(wqe, 0, sizeof(union lpfc_wqe));
+ /* OX_ID is invariable to who sent ABTS to CT exchange */
+ bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp,
+ bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_acc));
+ if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_acc) ==
+ LPFC_ABTS_UNSOL_INT) {
+ /* ABTS sent by initiator to CT exchange, the
+ * RX_ID field will be filled with the newly
+ * allocated responder XRI.
+ */
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+ iocbq->sli4_xritag);
+ } else {
+ /* ABTS sent by responder to CT exchange, the
+ * RX_ID field will be filled with the responder
+ * RX_ID from ABTS.
+ */
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+ bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_acc));
+ }
+ bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
+ bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
+ bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
+ iocbq->iocb.ulpContext);
+ /* Overwrite the pre-set comnd type with OTHER_COMMAND */
+ command_type = OTHER_COMMAND;
+ break;
case CMD_XRI_ABORTED_CX:
case CMD_CREATE_XRI_CR: /* Do we expect to use this? */
/* words0-2 are all 0's no bde */
@@ -6120,11 +6081,10 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
uint16_t xritag;
union lpfc_wqe wqe;
struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
- uint32_t fcp_wqidx;
if (piocb->sli4_xritag == NO_XRI) {
if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
- piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
+ piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
sglq = NULL;
else {
sglq = __lpfc_sli_get_sglq(phba);
@@ -6155,8 +6115,17 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
return IOCB_ERROR;
if (piocb->iocb_flag & LPFC_IO_FCP) {
- fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
- if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe))
+ /*
+ * For FCP command IOCB, get a new WQ index to distribute
+ * WQE across the WQsr. On the other hand, for abort IOCB,
+ * it carries the same WQ index to the original command
+ * IOCB.
+ */
+ if ((piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
+ (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN))
+ piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
+ if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx],
+ &wqe))
return IOCB_ERROR;
} else {
if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
@@ -6449,31 +6418,37 @@ lpfc_sli_setup(struct lpfc_hba *phba)
pring->iotag_max = 4096;
pring->lpfc_sli_rcv_async_status =
lpfc_sli_async_event_handler;
- pring->num_mask = 4;
+ pring->num_mask = LPFC_MAX_RING_MASK;
pring->prt[0].profile = 0; /* Mask 0 */
- pring->prt[0].rctl = FC_ELS_REQ;
- pring->prt[0].type = FC_ELS_DATA;
+ pring->prt[0].rctl = FC_RCTL_ELS_REQ;
+ pring->prt[0].type = FC_TYPE_ELS;
pring->prt[0].lpfc_sli_rcv_unsol_event =
lpfc_els_unsol_event;
pring->prt[1].profile = 0; /* Mask 1 */
- pring->prt[1].rctl = FC_ELS_RSP;
- pring->prt[1].type = FC_ELS_DATA;
+ pring->prt[1].rctl = FC_RCTL_ELS_REP;
+ pring->prt[1].type = FC_TYPE_ELS;
pring->prt[1].lpfc_sli_rcv_unsol_event =
lpfc_els_unsol_event;
pring->prt[2].profile = 0; /* Mask 2 */
/* NameServer Inquiry */
- pring->prt[2].rctl = FC_UNSOL_CTL;
+ pring->prt[2].rctl = FC_RCTL_DD_UNSOL_CTL;
/* NameServer */
- pring->prt[2].type = FC_COMMON_TRANSPORT_ULP;
+ pring->prt[2].type = FC_TYPE_CT;
pring->prt[2].lpfc_sli_rcv_unsol_event =
lpfc_ct_unsol_event;
pring->prt[3].profile = 0; /* Mask 3 */
/* NameServer response */
- pring->prt[3].rctl = FC_SOL_CTL;
+ pring->prt[3].rctl = FC_RCTL_DD_SOL_CTL;
/* NameServer */
- pring->prt[3].type = FC_COMMON_TRANSPORT_ULP;
+ pring->prt[3].type = FC_TYPE_CT;
pring->prt[3].lpfc_sli_rcv_unsol_event =
lpfc_ct_unsol_event;
+ /* abort unsolicited sequence */
+ pring->prt[4].profile = 0; /* Mask 4 */
+ pring->prt[4].rctl = FC_RCTL_BA_ABTS;
+ pring->prt[4].type = FC_TYPE_BLS;
+ pring->prt[4].lpfc_sli_rcv_unsol_event =
+ lpfc_sli4_ct_abort_unsol_event;
break;
}
totiocbsize += (pring->numCiocb * pring->sizeCiocb) +
@@ -6976,8 +6951,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
abort_iotag = cmdiocb->iocb.un.acxri.abortIoTag;
spin_lock_irq(&phba->hbalock);
- if (abort_iotag != 0 && abort_iotag <= phba->sli.last_iotag)
- abort_iocb = phba->sli.iocbq_lookup[abort_iotag];
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ if (abort_iotag != 0 &&
+ abort_iotag <= phba->sli.last_iotag)
+ abort_iocb =
+ phba->sli.iocbq_lookup[abort_iotag];
+ } else
+ /* For sli4 the abort_tag is the XRI,
+ * so the abort routine puts the iotag of the iocb
+ * being aborted in the context field of the abort
+ * IOCB.
+ */
+ abort_iocb = phba->sli.iocbq_lookup[abort_context];
lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_SLI,
"0327 Cannot abort els iocb %p "
@@ -6991,9 +6976,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
* might have completed already. Do not free it again.
*/
if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- spin_unlock_irq(&phba->hbalock);
- lpfc_sli_release_iocbq(phba, cmdiocb);
- return;
+ if (irsp->un.ulpWord[4] != IOERR_NO_XRI) {
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_sli_release_iocbq(phba, cmdiocb);
+ return;
+ }
+ /* For SLI4 the ulpContext field for abort IOCB
+ * holds the iotag of the IOCB being aborted so
+ * the local abort_context needs to be reset to
+ * match the aborted IOCBs ulpContext.
+ */
+ if (abort_iocb && phba->sli_rev == LPFC_SLI_REV4)
+ abort_context = abort_iocb->iocb.ulpContext;
}
/*
* make sure we have the right iocbq before taking it
@@ -7112,13 +7106,18 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
iabt = &abtsiocbp->iocb;
iabt->un.acxri.abortType = ABORT_TYPE_ABTS;
iabt->un.acxri.abortContextTag = icmd->ulpContext;
- if (phba->sli_rev == LPFC_SLI_REV4)
+ if (phba->sli_rev == LPFC_SLI_REV4) {
iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag;
+ iabt->un.acxri.abortContextTag = cmdiocb->iotag;
+ }
else
iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
iabt->ulpLe = 1;
iabt->ulpClass = icmd->ulpClass;
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx;
+
if (phba->link_state >= LPFC_LINK_UP)
iabt->ulpCommand = CMD_ABORT_XRI_CN;
else
@@ -7322,6 +7321,9 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
abtsiocb->iocb.ulpClass = cmd->ulpClass;
abtsiocb->vport = phba->pport;
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocb->fcp_wqidx = iocbq->fcp_wqidx;
+
if (lpfc_is_link_up(phba))
abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN;
else
@@ -7687,31 +7689,28 @@ static int
lpfc_sli4_eratt_read(struct lpfc_hba *phba)
{
uint32_t uerr_sta_hi, uerr_sta_lo;
- uint32_t onlnreg0, onlnreg1;
/* For now, use the SLI4 device internal unrecoverable error
* registers for error attention. This can be changed later.
*/
- onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
- onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
- if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
- uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
- uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
- if (uerr_sta_lo || uerr_sta_hi) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1423 HBA Unrecoverable error: "
- "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
- "online0_reg=0x%x, online1_reg=0x%x\n",
- uerr_sta_lo, uerr_sta_hi,
- onlnreg0, onlnreg1);
- phba->work_status[0] = uerr_sta_lo;
- phba->work_status[1] = uerr_sta_hi;
- /* Set the driver HA work bitmap */
- phba->work_ha |= HA_ERATT;
- /* Indicate polling handles this ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
- return 1;
- }
+ uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
+ uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
+ if ((~phba->sli4_hba.ue_mask_lo & uerr_sta_lo) ||
+ (~phba->sli4_hba.ue_mask_hi & uerr_sta_hi)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1423 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n",
+ uerr_sta_lo, uerr_sta_hi,
+ phba->sli4_hba.ue_mask_lo,
+ phba->sli4_hba.ue_mask_hi);
+ phba->work_status[0] = uerr_sta_lo;
+ phba->work_status[1] = uerr_sta_hi;
+ /* Set the driver HA work bitmap */
+ phba->work_ha |= HA_ERATT;
+ /* Indicate polling handles this ERATT */
+ phba->hba_flag |= HBA_ERATT_HANDLED;
+ return 1;
}
return 0;
}
@@ -7834,7 +7833,7 @@ irqreturn_t
lpfc_sli_sp_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
- uint32_t ha_copy;
+ uint32_t ha_copy, hc_copy;
uint32_t work_ha_copy;
unsigned long status;
unsigned long iflag;
@@ -7892,8 +7891,13 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
}
/* Clear up only attention source related to slow-path */
+ hc_copy = readl(phba->HCregaddr);
+ writel(hc_copy & ~(HC_MBINT_ENA | HC_R2INT_ENA |
+ HC_LAINT_ENA | HC_ERINT_ENA),
+ phba->HCregaddr);
writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
phba->HAregaddr);
+ writel(hc_copy, phba->HCregaddr);
readl(phba->HAregaddr); /* flush */
spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
@@ -8049,7 +8053,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
KERN_ERR,
LOG_MBOX | LOG_SLI,
"0350 rc should have"
- "been MBX_BUSY");
+ "been MBX_BUSY\n");
if (rc != MBX_NOT_FINISHED)
goto send_current_mbox;
}
@@ -8078,7 +8082,7 @@ send_current_mbox:
if (rc != MBX_SUCCESS)
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
LOG_SLI, "0349 rc should be "
- "MBX_SUCCESS");
+ "MBX_SUCCESS\n");
}
spin_lock_irqsave(&phba->hbalock, iflag);
@@ -8203,6 +8207,7 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
struct lpfc_hba *phba;
irqreturn_t sp_irq_rc, fp_irq_rc;
unsigned long status1, status2;
+ uint32_t hc_copy;
/*
* Get the driver's phba structure from the dev_id and
@@ -8240,7 +8245,12 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
}
/* Clear attention sources except link and error attentions */
+ hc_copy = readl(phba->HCregaddr);
+ writel(hc_copy & ~(HC_MBINT_ENA | HC_R0INT_ENA | HC_R1INT_ENA
+ | HC_R2INT_ENA | HC_LAINT_ENA | HC_ERINT_ENA),
+ phba->HCregaddr);
writel((phba->ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
+ writel(hc_copy, phba->HCregaddr);
readl(phba->HAregaddr); /* flush */
spin_unlock(&phba->hbalock);
@@ -8351,8 +8361,6 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
sizeof(struct lpfc_iocbq) - offset);
- memset(&pIocbIn->sli4_info, 0,
- sizeof(struct lpfc_sli4_rspiocb_info));
/* Map WCQE parameters into irspiocb parameters */
pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
if (pIocbOut->iocb_flag & LPFC_IO_FCP)
@@ -8364,16 +8372,49 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
else
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
- /* Load in additional WCQE parameters */
- pIocbIn->sli4_info.hw_status = bf_get(lpfc_wcqe_c_hw_status, wcqe);
- pIocbIn->sli4_info.bfield = 0;
- if (bf_get(lpfc_wcqe_c_xb, wcqe))
- pIocbIn->sli4_info.bfield |= LPFC_XB;
- if (bf_get(lpfc_wcqe_c_pv, wcqe)) {
- pIocbIn->sli4_info.bfield |= LPFC_PV;
- pIocbIn->sli4_info.priority =
- bf_get(lpfc_wcqe_c_priority, wcqe);
+}
+
+/**
+ * lpfc_sli4_els_wcqe_to_rspiocbq - Get response iocbq from els wcqe
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an ELS work-queue completion event and construct
+ * a pseudo response ELS IODBQ from the SLI4 ELS WCQE for the common
+ * discovery engine to handle.
+ *
+ * Return: Pointer to the receive IOCBQ, NULL otherwise.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba,
+ struct lpfc_iocbq *irspiocbq)
+{
+ struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_iocbq *cmdiocbq;
+ struct lpfc_wcqe_complete *wcqe;
+ unsigned long iflags;
+
+ wcqe = &irspiocbq->cq_event.cqe.wcqe_cmpl;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pring->stats.iocb_event++;
+ /* Look up the ELS command IOCB and create pseudo response IOCB */
+ cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ if (unlikely(!cmdiocbq)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0386 ELS complete with no corresponding "
+ "cmdiocb: iotag (%d)\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ lpfc_sli_release_iocbq(phba, irspiocbq);
+ return NULL;
}
+
+ /* Fake the irspiocbq and copy necessary response information */
+ lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
+
+ return irspiocbq;
}
/**
@@ -8566,45 +8607,26 @@ static bool
lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
struct lpfc_wcqe_complete *wcqe)
{
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
- struct lpfc_iocbq *cmdiocbq;
struct lpfc_iocbq *irspiocbq;
unsigned long iflags;
- bool workposted = false;
-
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->stats.iocb_event++;
- /* Look up the ELS command IOCB and create pseudo response IOCB */
- cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
- bf_get(lpfc_wcqe_c_request_tag, wcqe));
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- if (unlikely(!cmdiocbq)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0386 ELS complete with no corresponding "
- "cmdiocb: iotag (%d)\n",
- bf_get(lpfc_wcqe_c_request_tag, wcqe));
- return workposted;
- }
-
- /* Fake the irspiocbq and copy necessary response information */
+ /* Get an irspiocbq for later ELS response processing use */
irspiocbq = lpfc_sli_get_iocbq(phba);
if (!irspiocbq) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0387 Failed to allocate an iocbq\n");
- return workposted;
+ return false;
}
- lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
- /* Add the irspiocb to the response IOCB work list */
+ /* Save off the slow-path queue event for work thread to process */
+ memcpy(&irspiocbq->cq_event.cqe.wcqe_cmpl, wcqe, sizeof(*wcqe));
spin_lock_irqsave(&phba->hbalock, iflags);
- list_add_tail(&irspiocbq->list, &phba->sli4_hba.sp_rspiocb_work_queue);
- /* Indicate ELS ring attention */
- phba->work_ha |= (HA_R0ATT << (4*LPFC_ELS_RING));
+ list_add_tail(&irspiocbq->cq_event.list,
+ &phba->sli4_hba.sp_queue_event);
+ phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
- workposted = true;
- return workposted;
+ return true;
}
/**
@@ -8690,52 +8712,6 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
}
/**
- * lpfc_sli4_sp_handle_wcqe - Process a work-queue completion queue entry
- * @phba: Pointer to HBA context object.
- * @cq: Pointer to the completion queue.
- * @wcqe: Pointer to a completion queue entry.
- *
- * This routine process a slow-path work-queue completion queue entry.
- *
- * Return: true if work posted to worker thread, otherwise false.
- **/
-static bool
-lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
- struct lpfc_cqe *cqe)
-{
- struct lpfc_wcqe_complete wcqe;
- bool workposted = false;
-
- /* Copy the work queue CQE and convert endian order if needed */
- lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
-
- /* Check and process for different type of WCQE and dispatch */
- switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
- case CQE_CODE_COMPL_WQE:
- /* Process the WQ complete event */
- workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
- (struct lpfc_wcqe_complete *)&wcqe);
- break;
- case CQE_CODE_RELEASE_WQE:
- /* Process the WQ release event */
- lpfc_sli4_sp_handle_rel_wcqe(phba,
- (struct lpfc_wcqe_release *)&wcqe);
- break;
- case CQE_CODE_XRI_ABORTED:
- /* Process the WQ XRI abort event */
- workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
- (struct sli4_wcqe_xri_aborted *)&wcqe);
- break;
- default:
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0388 Not a valid WCQE code: x%x\n",
- bf_get(lpfc_wcqe_c_code, &wcqe));
- break;
- }
- return workposted;
-}
-
-/**
* lpfc_sli4_sp_handle_rcqe - Process a receive-queue completion queue entry
* @phba: Pointer to HBA context object.
* @rcqe: Pointer to receive-queue completion queue entry.
@@ -8745,9 +8721,8 @@ lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
* Return: true if work posted to worker thread, otherwise false.
**/
static bool
-lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
+lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe)
{
- struct lpfc_rcqe rcqe;
bool workposted = false;
struct lpfc_queue *hrq = phba->sli4_hba.hdr_rq;
struct lpfc_queue *drq = phba->sli4_hba.dat_rq;
@@ -8755,31 +8730,28 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
uint32_t status;
unsigned long iflags;
- /* Copy the receive queue CQE and convert endian order if needed */
- lpfc_sli_pcimem_bcopy(cqe, &rcqe, sizeof(struct lpfc_rcqe));
- lpfc_sli4_rq_release(hrq, drq);
- if (bf_get(lpfc_rcqe_code, &rcqe) != CQE_CODE_RECEIVE)
- goto out;
- if (bf_get(lpfc_rcqe_rq_id, &rcqe) != hrq->queue_id)
+ if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id)
goto out;
- status = bf_get(lpfc_rcqe_status, &rcqe);
+ status = bf_get(lpfc_rcqe_status, rcqe);
switch (status) {
case FC_STATUS_RQ_BUF_LEN_EXCEEDED:
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"2537 Receive Frame Truncated!!\n");
case FC_STATUS_RQ_SUCCESS:
+ lpfc_sli4_rq_release(hrq, drq);
spin_lock_irqsave(&phba->hbalock, iflags);
dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list);
if (!dma_buf) {
spin_unlock_irqrestore(&phba->hbalock, iflags);
goto out;
}
- memcpy(&dma_buf->rcqe, &rcqe, sizeof(rcqe));
+ memcpy(&dma_buf->cq_event.cqe.rcqe_cmpl, rcqe, sizeof(*rcqe));
/* save off the frame for the word thread to process */
- list_add_tail(&dma_buf->dbuf.list, &phba->rb_pend_list);
+ list_add_tail(&dma_buf->cq_event.list,
+ &phba->sli4_hba.sp_queue_event);
/* Frame received */
- phba->hba_flag |= HBA_RECEIVE_BUFFER;
+ phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
workposted = true;
break;
@@ -8794,7 +8766,58 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
}
out:
return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_cqe - Process a slow path completion queue entry
+ * @phba: Pointer to HBA context object.
+ * @cq: Pointer to the completion queue.
+ * @wcqe: Pointer to a completion queue entry.
+ *
+ * This routine process a slow-path work-queue or recieve queue completion queue
+ * entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+ struct lpfc_cqe *cqe)
+{
+ struct lpfc_cqe cqevt;
+ bool workposted = false;
+
+ /* Copy the work queue CQE and convert endian order if needed */
+ lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe));
+ /* Check and process for different type of WCQE and dispatch */
+ switch (bf_get(lpfc_cqe_code, &cqevt)) {
+ case CQE_CODE_COMPL_WQE:
+ /* Process the WQ/RQ complete event */
+ workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
+ (struct lpfc_wcqe_complete *)&cqevt);
+ break;
+ case CQE_CODE_RELEASE_WQE:
+ /* Process the WQ release event */
+ lpfc_sli4_sp_handle_rel_wcqe(phba,
+ (struct lpfc_wcqe_release *)&cqevt);
+ break;
+ case CQE_CODE_XRI_ABORTED:
+ /* Process the WQ XRI abort event */
+ workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
+ (struct sli4_wcqe_xri_aborted *)&cqevt);
+ break;
+ case CQE_CODE_RECEIVE:
+ /* Process the RQ event */
+ workposted = lpfc_sli4_sp_handle_rcqe(phba,
+ (struct lpfc_rcqe *)&cqevt);
+ break;
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "0388 Not a valid WCQE code: x%x\n",
+ bf_get(lpfc_cqe_code, &cqevt));
+ break;
+ }
+ return workposted;
}
/**
@@ -8858,14 +8881,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
break;
case LPFC_WCQ:
while ((cqe = lpfc_sli4_cq_get(cq))) {
- workposted |= lpfc_sli4_sp_handle_wcqe(phba, cq, cqe);
- if (!(++ecount % LPFC_GET_QE_REL_INT))
- lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
- }
- break;
- case LPFC_RCQ:
- while ((cqe = lpfc_sli4_cq_get(cq))) {
- workposted |= lpfc_sli4_sp_handle_rcqe(phba, cqe);
+ workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, cqe);
if (!(++ecount % LPFC_GET_QE_REL_INT))
lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
}
@@ -10427,8 +10443,7 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba)
return xritag;
}
spin_unlock_irq(&phba->hbalock);
-
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"2004 Failed to allocate XRI.last XRITAG is %d"
" Max XRI is %d, Used XRI is %d\n",
phba->sli4_hba.next_xri,
@@ -10492,15 +10507,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
lpfc_sli4_mbox_cmd_free(phba, mbox);
return -ENOMEM;
}
-
/* Get the first SGE entry from the non-embedded DMA memory */
- if (unlikely(!mbox->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2525 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
viraddr = mbox->sge_array->addr[0];
/* Set up the SGL pages in the non-embedded DMA pages */
@@ -10524,8 +10531,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
sgl_pg_pairs++;
}
bf_set(lpfc_post_sgl_pages_xri, sgl, xritag_start);
- pg_pairs = (pg_pairs > 0) ? (pg_pairs - 1) : pg_pairs;
- bf_set(lpfc_post_sgl_pages_xricnt, sgl, pg_pairs);
+ bf_set(lpfc_post_sgl_pages_xricnt, sgl, els_xri_cnt);
/* Perform endian conversion if necessary */
sgl->word0 = cpu_to_le32(sgl->word0);
@@ -10607,15 +10613,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
lpfc_sli4_mbox_cmd_free(phba, mbox);
return -ENOMEM;
}
-
/* Get the first SGE entry from the non-embedded DMA memory */
- if (unlikely(!mbox->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2565 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mbox);
- return -ENOMEM;
- }
viraddr = mbox->sge_array->addr[0];
/* Set up the SGL pages in the non-embedded DMA pages */
@@ -10802,6 +10800,105 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
}
/**
+ * lpfc_update_rcv_time_stamp - Update vport's rcv seq time stamp
+ * @vport: The vport to work on.
+ *
+ * This function updates the receive sequence time stamp for this vport. The
+ * receive sequence time stamp indicates the time that the last frame of the
+ * the sequence that has been idle for the longest amount of time was received.
+ * the driver uses this time stamp to indicate if any received sequences have
+ * timed out.
+ **/
+void
+lpfc_update_rcv_time_stamp(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf;
+ struct hbq_dmabuf *dmabuf = NULL;
+
+ /* get the oldest sequence on the rcv list */
+ h_buf = list_get_first(&vport->rcv_buffer_list,
+ struct lpfc_dmabuf, list);
+ if (!h_buf)
+ return;
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ vport->rcv_buffer_time_stamp = dmabuf->time_stamp;
+}
+
+/**
+ * lpfc_cleanup_rcv_buffers - Cleans up all outstanding receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function cleans up all outstanding received sequences. This is called
+ * by the driver when a link event or user action invalidates all the received
+ * sequences.
+ **/
+void
+lpfc_cleanup_rcv_buffers(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf, *hnext;
+ struct lpfc_dmabuf *d_buf, *dnext;
+ struct hbq_dmabuf *dmabuf = NULL;
+
+ /* start with the oldest sequence on the rcv list */
+ list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ list_del_init(&dmabuf->hbuf.list);
+ list_for_each_entry_safe(d_buf, dnext,
+ &dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+ }
+}
+
+/**
+ * lpfc_rcv_seq_check_edtov - Cleans up timed out receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function determines whether any received sequences have timed out by
+ * first checking the vport's rcv_buffer_time_stamp. If this time_stamp
+ * indicates that there is at least one timed out sequence this routine will
+ * go through the received sequences one at a time from most inactive to most
+ * active to determine which ones need to be cleaned up. Once it has determined
+ * that a sequence needs to be cleaned up it will simply free up the resources
+ * without sending an abort.
+ **/
+void
+lpfc_rcv_seq_check_edtov(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf, *hnext;
+ struct lpfc_dmabuf *d_buf, *dnext;
+ struct hbq_dmabuf *dmabuf = NULL;
+ unsigned long timeout;
+ int abort_count = 0;
+
+ timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+ vport->rcv_buffer_time_stamp);
+ if (list_empty(&vport->rcv_buffer_list) ||
+ time_before(jiffies, timeout))
+ return;
+ /* start with the oldest sequence on the rcv list */
+ list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+ dmabuf->time_stamp);
+ if (time_before(jiffies, timeout))
+ break;
+ abort_count++;
+ list_del_init(&dmabuf->hbuf.list);
+ list_for_each_entry_safe(d_buf, dnext,
+ &dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+ }
+ if (abort_count)
+ lpfc_update_rcv_time_stamp(vport);
+}
+
+/**
* lpfc_fc_frame_add - Adds a frame to the vport's list of received sequences
* @dmabuf: pointer to a dmabuf that describes the hdr and data of the FC frame
*
@@ -10823,6 +10920,8 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
struct hbq_dmabuf *seq_dmabuf = NULL;
struct hbq_dmabuf *temp_dmabuf = NULL;
+ INIT_LIST_HEAD(&dmabuf->dbuf.list);
+ dmabuf->time_stamp = jiffies;
new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
/* Use the hdr_buf to find the sequence that this frame belongs to */
list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
@@ -10841,13 +10940,21 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
* Queue the buffer on the vport's rcv_buffer_list.
*/
list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ lpfc_update_rcv_time_stamp(vport);
return dmabuf;
}
temp_hdr = seq_dmabuf->hbuf.virt;
if (new_hdr->fh_seq_cnt < temp_hdr->fh_seq_cnt) {
- list_add(&seq_dmabuf->dbuf.list, &dmabuf->dbuf.list);
+ list_del_init(&seq_dmabuf->hbuf.list);
+ list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ list_add_tail(&dmabuf->dbuf.list, &seq_dmabuf->dbuf.list);
+ lpfc_update_rcv_time_stamp(vport);
return dmabuf;
}
+ /* move this sequence to the tail to indicate a young sequence */
+ list_move_tail(&seq_dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ seq_dmabuf->time_stamp = jiffies;
+ lpfc_update_rcv_time_stamp(vport);
/* find the correct place in the sequence to insert this frame */
list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) {
temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
@@ -10865,6 +10972,210 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
}
/**
+ * lpfc_sli4_abort_partial_seq - Abort partially assembled unsol sequence
+ * @vport: pointer to a vitural port
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function tries to abort from the partially assembed sequence, described
+ * by the information from basic abbort @dmabuf. It checks to see whether such
+ * partially assembled sequence held by the driver. If so, it shall free up all
+ * the frames from the partially assembled sequence.
+ *
+ * Return
+ * true -- if there is matching partially assembled sequence present and all
+ * the frames freed with the sequence;
+ * false -- if there is no matching partially assembled sequence present so
+ * nothing got aborted in the lower layer driver
+ **/
+static bool
+lpfc_sli4_abort_partial_seq(struct lpfc_vport *vport,
+ struct hbq_dmabuf *dmabuf)
+{
+ struct fc_frame_header *new_hdr;
+ struct fc_frame_header *temp_hdr;
+ struct lpfc_dmabuf *d_buf, *n_buf, *h_buf;
+ struct hbq_dmabuf *seq_dmabuf = NULL;
+
+ /* Use the hdr_buf to find the sequence that matches this frame */
+ INIT_LIST_HEAD(&dmabuf->dbuf.list);
+ INIT_LIST_HEAD(&dmabuf->hbuf.list);
+ new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
+ temp_hdr = (struct fc_frame_header *)h_buf->virt;
+ if ((temp_hdr->fh_seq_id != new_hdr->fh_seq_id) ||
+ (temp_hdr->fh_ox_id != new_hdr->fh_ox_id) ||
+ (memcmp(&temp_hdr->fh_s_id, &new_hdr->fh_s_id, 3)))
+ continue;
+ /* found a pending sequence that matches this frame */
+ seq_dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ break;
+ }
+
+ /* Free up all the frames from the partially assembled sequence */
+ if (seq_dmabuf) {
+ list_for_each_entry_safe(d_buf, n_buf,
+ &seq_dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * lpfc_sli4_seq_abort_acc_cmpl - Accept seq abort iocb complete handler
+ * @phba: Pointer to HBA context object.
+ * @cmd_iocbq: pointer to the command iocbq structure.
+ * @rsp_iocbq: pointer to the response iocbq structure.
+ *
+ * This function handles the sequence abort accept iocb command complete
+ * event. It properly releases the memory allocated to the sequence abort
+ * accept iocb.
+ **/
+static void
+lpfc_sli4_seq_abort_acc_cmpl(struct lpfc_hba *phba,
+ struct lpfc_iocbq *cmd_iocbq,
+ struct lpfc_iocbq *rsp_iocbq)
+{
+ if (cmd_iocbq)
+ lpfc_sli_release_iocbq(phba, cmd_iocbq);
+}
+
+/**
+ * lpfc_sli4_seq_abort_acc - Accept sequence abort
+ * @phba: Pointer to HBA context object.
+ * @fc_hdr: pointer to a FC frame header.
+ *
+ * This function sends a basic accept to a previous unsol sequence abort
+ * event after aborting the sequence handling.
+ **/
+static void
+lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba,
+ struct fc_frame_header *fc_hdr)
+{
+ struct lpfc_iocbq *ctiocb = NULL;
+ struct lpfc_nodelist *ndlp;
+ uint16_t oxid, rxid;
+ uint32_t sid, fctl;
+ IOCB_t *icmd;
+
+ if (!lpfc_is_link_up(phba))
+ return;
+
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
+ oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+ rxid = be16_to_cpu(fc_hdr->fh_rx_id);
+
+ ndlp = lpfc_findnode_did(phba->pport, sid);
+ if (!ndlp) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+ "1268 Find ndlp returned NULL for oxid:x%x "
+ "SID:x%x\n", oxid, sid);
+ return;
+ }
+
+ /* Allocate buffer for acc iocb */
+ ctiocb = lpfc_sli_get_iocbq(phba);
+ if (!ctiocb)
+ return;
+
+ /* Extract the F_CTL field from FC_HDR */
+ fctl = sli4_fctl_from_fc_hdr(fc_hdr);
+
+ icmd = &ctiocb->iocb;
+ icmd->un.xseq64.bdl.bdeSize = 0;
+ icmd->un.xseq64.bdl.ulpIoTag32 = 0;
+ icmd->un.xseq64.w5.hcsw.Dfctl = 0;
+ icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_ACC;
+ icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_BLS;
+
+ /* Fill in the rest of iocb fields */
+ icmd->ulpCommand = CMD_XMIT_BLS_RSP64_CX;
+ icmd->ulpBdeCount = 0;
+ icmd->ulpLe = 1;
+ icmd->ulpClass = CLASS3;
+ icmd->ulpContext = ndlp->nlp_rpi;
+
+ ctiocb->iocb_cmpl = NULL;
+ ctiocb->vport = phba->pport;
+ ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_acc_cmpl;
+
+ if (fctl & FC_FC_EX_CTX) {
+ /* ABTS sent by responder to CT exchange, construction
+ * of BA_ACC will use OX_ID from ABTS for the XRI_TAG
+ * field and RX_ID from ABTS for RX_ID field.
+ */
+ bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_RSP);
+ bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, rxid);
+ ctiocb->sli4_xritag = oxid;
+ } else {
+ /* ABTS sent by initiator to CT exchange, construction
+ * of BA_ACC will need to allocate a new XRI as for the
+ * XRI_TAG and RX_ID fields.
+ */
+ bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_INT);
+ bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, NO_XRI);
+ ctiocb->sli4_xritag = NO_XRI;
+ }
+ bf_set(lpfc_abts_oxid, &icmd->un.bls_acc, oxid);
+
+ /* Xmit CT abts accept on exchange <xid> */
+ lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+ "1200 Xmit CT ABTS ACC on exchange x%x Data: x%x\n",
+ CMD_XMIT_BLS_RSP64_CX, phba->link_state);
+ lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
+}
+
+/**
+ * lpfc_sli4_handle_unsol_abort - Handle sli-4 unsolicited abort event
+ * @vport: Pointer to the vport on which this sequence was received
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function handles an SLI-4 unsolicited abort event. If the unsolicited
+ * receive sequence is only partially assembed by the driver, it shall abort
+ * the partially assembled frames for the sequence. Otherwise, if the
+ * unsolicited receive sequence has been completely assembled and passed to
+ * the Upper Layer Protocol (UPL), it then mark the per oxid status for the
+ * unsolicited sequence has been aborted. After that, it will issue a basic
+ * accept to accept the abort.
+ **/
+void
+lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport,
+ struct hbq_dmabuf *dmabuf)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct fc_frame_header fc_hdr;
+ uint32_t fctl;
+ bool abts_par;
+
+ /* Make a copy of fc_hdr before the dmabuf being released */
+ memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
+ fctl = sli4_fctl_from_fc_hdr(&fc_hdr);
+
+ if (fctl & FC_FC_EX_CTX) {
+ /*
+ * ABTS sent by responder to exchange, just free the buffer
+ */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ } else {
+ /*
+ * ABTS sent by initiator to exchange, need to do cleanup
+ */
+ /* Try to abort partially assembled seq */
+ abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);
+
+ /* Send abort to ULP if partially seq abort failed */
+ if (abts_par == false)
+ lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
+ else
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ }
+ /* Send basic accept (BA_ACC) to the abort requester */
+ lpfc_sli4_seq_abort_acc(phba, &fc_hdr);
+}
+
+/**
* lpfc_seq_complete - Indicates if a sequence is complete
* @dmabuf: pointer to a dmabuf that describes the FC sequence
*
@@ -10935,10 +11246,9 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
/* remove from receive buffer list */
list_del_init(&seq_dmabuf->hbuf.list);
+ lpfc_update_rcv_time_stamp(vport);
/* get the Remote Port's SID */
- sid = (fc_hdr->fh_s_id[0] << 16 |
- fc_hdr->fh_s_id[1] << 8 |
- fc_hdr->fh_s_id[2]);
+ sid = sli4_sid_from_fc_hdr(fc_hdr);
/* Get an iocbq struct to fill in. */
first_iocbq = lpfc_sli_get_iocbq(vport->phba);
if (first_iocbq) {
@@ -10957,7 +11267,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.un.rcvels.remoteID = sid;
first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+ bf_get(lpfc_rcqe_length,
+ &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
}
iocbq = first_iocbq;
/*
@@ -10975,7 +11286,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize =
LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+ bf_get(lpfc_rcqe_length,
+ &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
} else {
iocbq = lpfc_sli_get_iocbq(vport->phba);
if (!iocbq) {
@@ -10994,7 +11306,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
iocbq->iocb.un.cont64[0].tus.f.bdeSize =
LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
- bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+ bf_get(lpfc_rcqe_length,
+ &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
iocbq->iocb.un.rcvels.remoteID = sid;
list_add_tail(&iocbq->list, &first_iocbq->list);
}
@@ -11002,6 +11315,43 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
return first_iocbq;
}
+static void
+lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport,
+ struct hbq_dmabuf *seq_dmabuf)
+{
+ struct fc_frame_header *fc_hdr;
+ struct lpfc_iocbq *iocbq, *curr_iocb, *next_iocb;
+ struct lpfc_hba *phba = vport->phba;
+
+ fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+ iocbq = lpfc_prep_seq(vport, seq_dmabuf);
+ if (!iocbq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2707 Ring %d handler: Failed to allocate "
+ "iocb Rctl x%x Type x%x received\n",
+ LPFC_ELS_RING,
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+ return;
+ }
+ if (!lpfc_complete_unsol_iocb(phba,
+ &phba->sli.ring[LPFC_ELS_RING],
+ iocbq, fc_hdr->fh_r_ctl,
+ fc_hdr->fh_type))
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "2540 Ring %d handler: unexpected Rctl "
+ "x%x Type x%x received\n",
+ LPFC_ELS_RING,
+ fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+
+ /* Free iocb created in lpfc_prep_seq */
+ list_for_each_entry_safe(curr_iocb, next_iocb,
+ &iocbq->list, list) {
+ list_del_init(&curr_iocb->list);
+ lpfc_sli_release_iocbq(phba, curr_iocb);
+ }
+ lpfc_sli_release_iocbq(phba, iocbq);
+}
+
/**
* lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
* @phba: Pointer to HBA context object.
@@ -11014,67 +11364,54 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
* Worker thread calls lpfc_sli4_handle_received_buffer, which will call the
* appropriate receive function when the final frame in a sequence is received.
**/
-int
-lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba)
+void
+lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
+ struct hbq_dmabuf *dmabuf)
{
- LIST_HEAD(cmplq);
- struct hbq_dmabuf *dmabuf, *seq_dmabuf;
+ struct hbq_dmabuf *seq_dmabuf;
struct fc_frame_header *fc_hdr;
struct lpfc_vport *vport;
uint32_t fcfi;
- struct lpfc_iocbq *iocbq;
-
- /* Clear hba flag and get all received buffers into the cmplq */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~HBA_RECEIVE_BUFFER;
- list_splice_init(&phba->rb_pend_list, &cmplq);
- spin_unlock_irq(&phba->hbalock);
/* Process each received buffer */
- while ((dmabuf = lpfc_sli_hbqbuf_get(&cmplq)) != NULL) {
- fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
- /* check to see if this a valid type of frame */
- if (lpfc_fc_frame_check(phba, fc_hdr)) {
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
- continue;
- }
- fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->rcqe);
- vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
- if (!vport) {
- /* throw out the frame */
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
- continue;
- }
- /* Link this frame */
- seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf);
- if (!seq_dmabuf) {
- /* unable to add frame to vport - throw it out */
- lpfc_in_buf_free(phba, &dmabuf->dbuf);
- continue;
- }
- /* If not last frame in sequence continue processing frames. */
- if (!lpfc_seq_complete(seq_dmabuf)) {
- /*
- * When saving off frames post a new one and mark this
- * frame to be freed when it is finished.
- **/
- lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1);
- dmabuf->tag = -1;
- continue;
- }
- fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
- iocbq = lpfc_prep_seq(vport, seq_dmabuf);
- if (!lpfc_complete_unsol_iocb(phba,
- &phba->sli.ring[LPFC_ELS_RING],
- iocbq, fc_hdr->fh_r_ctl,
- fc_hdr->fh_type))
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "2540 Ring %d handler: unexpected Rctl "
- "x%x Type x%x received\n",
- LPFC_ELS_RING,
- fc_hdr->fh_r_ctl, fc_hdr->fh_type);
- };
- return 0;
+ fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+ /* check to see if this a valid type of frame */
+ if (lpfc_fc_frame_check(phba, fc_hdr)) {
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+ fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl);
+ vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
+ if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) {
+ /* throw out the frame */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+ /* Handle the basic abort sequence (BA_ABTS) event */
+ if (fc_hdr->fh_r_ctl == FC_RCTL_BA_ABTS) {
+ lpfc_sli4_handle_unsol_abort(vport, dmabuf);
+ return;
+ }
+
+ /* Link this frame */
+ seq_dmabuf = lpfc_fc_frame_add(vport, dmabuf);
+ if (!seq_dmabuf) {
+ /* unable to add frame to vport - throw it out */
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ return;
+ }
+ /* If not last frame in sequence continue processing frames. */
+ if (!lpfc_seq_complete(seq_dmabuf)) {
+ /*
+ * When saving off frames post a new one and mark this
+ * frame to be freed when it is finished.
+ **/
+ lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1);
+ dmabuf->tag = -1;
+ return;
+ }
+ /* Send the complete sequence to the upper layer protocol */
+ lpfc_sli4_send_seq_to_ulp(vport, seq_dmabuf);
}
/**
@@ -11091,7 +11428,7 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba)
* sequential.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* EIO - The mailbox failed to complete successfully.
* When this error occurs, the driver is not guaranteed
* to have any rpi regions posted to the device and
@@ -11129,7 +11466,7 @@ lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba)
* maps up to 64 rpi context regions.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No available memory
* EIO - The mailbox failed to complete successfully.
**/
@@ -11191,7 +11528,7 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page)
* PAGE_SIZE modulo 64 rpi context headers.
*
* Returns
- * A nonzero rpi defined as rpi_base <= rpi < max_rpi if sucessful
+ * A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful
* LPFC_RPI_ALLOC_ERROR if no rpis are available.
**/
int
@@ -11334,6 +11671,7 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
{
LPFC_MBOXQ_t *mboxq;
int rc = 0;
+ int retval = MBX_SUCCESS;
uint32_t mbox_tmo;
if (vpi == 0)
@@ -11344,16 +11682,17 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
lpfc_init_vpi(phba, mboxq, vpi);
mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_INIT_VPI);
rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
- if (rc != MBX_TIMEOUT)
- mempool_free(mboxq, phba->mbox_mem_pool);
if (rc != MBX_SUCCESS) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"2022 INIT VPI Mailbox failed "
"status %d, mbxStatus x%x\n", rc,
bf_get(lpfc_mqe_status, &mboxq->u.mqe));
- rc = -EIO;
+ retval = -EIO;
}
- return rc;
+ if (rc != MBX_TIMEOUT)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+
+ return retval;
}
/**
@@ -11438,13 +11777,6 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
*/
lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
- if (unlikely(!mboxq->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2526 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
- return -ENOMEM;
- }
virt_addr = mboxq->sge_array->addr[0];
/*
* Configure the FCF record for FCFI 0. This is the driver's
@@ -11542,7 +11874,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"2000 Failed to allocate mbox for "
"READ_FCF cmd\n");
- return -ENOMEM;
+ error = -ENOMEM;
+ goto fail_fcfscan;
}
req_len = sizeof(struct fcf_record) +
@@ -11558,8 +11891,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
"0291 Allocated DMA memory size (x%x) is "
"less than the requested DMA memory "
"size (x%x)\n", alloc_len, req_len);
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
- return -ENOMEM;
+ error = -ENOMEM;
+ goto fail_fcfscan;
}
/* Get the first SGE entry from the non-embedded DMA memory. This
@@ -11567,13 +11900,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
*/
lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
- if (unlikely(!mboxq->sge_array)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
- "2527 Failed to get the non-embedded SGE "
- "virtual address\n");
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
- return -ENOMEM;
- }
virt_addr = mboxq->sge_array->addr[0];
read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
@@ -11586,7 +11912,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
- lpfc_sli4_mbox_cmd_free(phba, mboxq);
error = -EIO;
} else {
spin_lock_irq(&phba->hbalock);
@@ -11594,6 +11919,15 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
spin_unlock_irq(&phba->hbalock);
error = 0;
}
+fail_fcfscan:
+ if (error) {
+ if (mboxq)
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ /* FCF scan failed, clear FCF_DISC_INPROGRESS flag */
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+ spin_unlock_irq(&phba->hbalock);
+ }
return error;
}
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index 3c53316cf6d..ba38de3c28f 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -29,14 +29,17 @@ typedef enum _lpfc_ctx_cmd {
LPFC_CTX_HOST
} lpfc_ctx_cmd;
-/* This structure is used to carry the needed response IOCB states */
-struct lpfc_sli4_rspiocb_info {
- uint8_t hw_status;
- uint8_t bfield;
-#define LPFC_XB 0x1
-#define LPFC_PV 0x2
- uint8_t priority;
- uint8_t reserved;
+struct lpfc_cq_event {
+ struct list_head list;
+ union {
+ struct lpfc_mcqe mcqe_cmpl;
+ struct lpfc_acqe_link acqe_link;
+ struct lpfc_acqe_fcoe acqe_fcoe;
+ struct lpfc_acqe_dcbx acqe_dcbx;
+ struct lpfc_rcqe rcqe_cmpl;
+ struct sli4_wcqe_xri_aborted wcqe_axri;
+ struct lpfc_wcqe_complete wcqe_cmpl;
+ } cqe;
};
/* This structure is used to handle IOCB requests / responses */
@@ -46,6 +49,7 @@ struct lpfc_iocbq {
struct list_head clist;
uint16_t iotag; /* pre-assigned IO tag */
uint16_t sli4_xritag; /* pre-assigned XRI, (OXID) tag. */
+ struct lpfc_cq_event cq_event;
IOCB_t iocb; /* IOCB cmd */
uint8_t retry; /* retry counter for IOCB cmd - if needed */
@@ -56,11 +60,13 @@ struct lpfc_iocbq {
#define LPFC_DRIVER_ABORTED 8 /* driver aborted this request */
#define LPFC_IO_FABRIC 0x10 /* Iocb send using fabric scheduler */
#define LPFC_DELAY_MEM_FREE 0x20 /* Defer free'ing of FC data */
-#define LPFC_FIP_ELS 0x40
+#define LPFC_FIP_ELS_ID_MASK 0xc0 /* ELS_ID range 0-3 */
+#define LPFC_FIP_ELS_ID_SHIFT 6
uint8_t abort_count;
uint8_t rsvd2;
uint32_t drvrTimeout; /* driver timeout in seconds */
+ uint32_t fcp_wqidx; /* index to FCP work queue */
struct lpfc_vport *vport;/* virtual port pointer */
void *context1; /* caller context information */
void *context2; /* caller context information */
@@ -76,7 +82,6 @@ struct lpfc_iocbq {
struct lpfc_iocbq *);
void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
struct lpfc_iocbq *);
- struct lpfc_sli4_rspiocb_info sli4_info;
};
#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */
@@ -110,7 +115,7 @@ typedef struct lpfcMboxq {
return */
#define MBX_NOWAIT 2 /* issue command then return immediately */
-#define LPFC_MAX_RING_MASK 4 /* max num of rctl/type masks allowed per
+#define LPFC_MAX_RING_MASK 5 /* max num of rctl/type masks allowed per
ring */
#define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index b5f4ba1a5c2..25d66d070cf 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -58,6 +58,16 @@
#define LPFC_FCOE_FKA_ADV_PER 0
#define LPFC_FCOE_FIP_PRIORITY 0x80
+#define sli4_sid_from_fc_hdr(fc_hdr) \
+ ((fc_hdr)->fh_s_id[0] << 16 | \
+ (fc_hdr)->fh_s_id[1] << 8 | \
+ (fc_hdr)->fh_s_id[2])
+
+#define sli4_fctl_from_fc_hdr(fc_hdr) \
+ ((fc_hdr)->fh_f_ctl[0] << 16 | \
+ (fc_hdr)->fh_f_ctl[1] << 8 | \
+ (fc_hdr)->fh_f_ctl[2])
+
enum lpfc_sli4_queue_type {
LPFC_EQ,
LPFC_GCQ,
@@ -110,18 +120,6 @@ struct lpfc_queue {
union sli4_qe qe[1]; /* array to index entries (must be last) */
};
-struct lpfc_cq_event {
- struct list_head list;
- union {
- struct lpfc_mcqe mcqe_cmpl;
- struct lpfc_acqe_link acqe_link;
- struct lpfc_acqe_fcoe acqe_fcoe;
- struct lpfc_acqe_dcbx acqe_dcbx;
- struct lpfc_rcqe rcqe_cmpl;
- struct sli4_wcqe_xri_aborted wcqe_axri;
- } cqe;
-};
-
struct lpfc_sli4_link {
uint8_t speed;
uint8_t duplex;
@@ -166,7 +164,7 @@ struct lpfc_fip_param_hdr {
#define lpfc_fip_param_hdr_fipp_mode_SHIFT 6
#define lpfc_fip_param_hdr_fipp_mode_MASK 0x3
#define lpfc_fip_param_hdr_fipp_mode_WORD parm_flags
-#define FIPP_MODE_ON 0x2
+#define FIPP_MODE_ON 0x1
#define FIPP_MODE_OFF 0x0
#define FIPP_VLAN_VALID 0x1
};
@@ -295,9 +293,8 @@ struct lpfc_sli4_hba {
/* BAR0 PCI config space register memory map */
void __iomem *UERRLOregaddr; /* Address to UERR_STATUS_LO register */
void __iomem *UERRHIregaddr; /* Address to UERR_STATUS_HI register */
- void __iomem *ONLINE0regaddr; /* Address to components of internal UE */
- void __iomem *ONLINE1regaddr; /* Address to components of internal UE */
-#define LPFC_ONLINE_NERR 0xFFFFFFFF
+ void __iomem *UEMASKLOregaddr; /* Address to UE_MASK_LO register */
+ void __iomem *UEMASKHIregaddr; /* Address to UE_MASK_HI register */
void __iomem *SCRATCHPADregaddr; /* Address to scratchpad register */
/* BAR1 FCoE function CSR register memory map */
void __iomem *STAregaddr; /* Address to HST_STATE register */
@@ -311,6 +308,8 @@ struct lpfc_sli4_hba {
void __iomem *MQDBregaddr; /* Address to MQ_DOORBELL register */
void __iomem *BMBXregaddr; /* Address to BootStrap MBX register */
+ uint32_t ue_mask_lo;
+ uint32_t ue_mask_hi;
struct msix_entry *msix_entries;
uint32_t cfg_eqn;
struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */
@@ -325,7 +324,6 @@ struct lpfc_sli4_hba {
struct lpfc_queue **fcp_cq;/* Fast-path FCP compl queue */
struct lpfc_queue *mbx_cq; /* Slow-path mailbox complete queue */
struct lpfc_queue *els_cq; /* Slow-path ELS response complete queue */
- struct lpfc_queue *rxq_cq; /* Slow-path unsolicited complete queue */
/* Setup information for various queue parameters */
int eq_esize;
@@ -360,7 +358,7 @@ struct lpfc_sli4_hba {
unsigned long *rpi_bmask;
uint16_t rpi_count;
struct lpfc_sli4_flags sli4_flags;
- struct list_head sp_rspiocb_work_queue;
+ struct list_head sp_queue_event;
struct list_head sp_cqe_event_pool;
struct list_head sp_asynce_work_queue;
struct list_head sp_fcp_xri_aborted_work_queue;
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 9ae20af4bdb..c7f3aed2aab 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,8 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.4"
-
+#define LPFC_DRIVER_VERSION "8.3.6"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
#define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp"
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 606efa76754..7d6dd83d359 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -389,7 +389,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
* by the port.
*/
if ((phba->sli_rev == LPFC_SLI_REV4) &&
- (pport->vfi_state & LPFC_VFI_REGISTERED)) {
+ (pport->vpi_state & LPFC_VPI_REGISTERED)) {
rc = lpfc_sli4_init_vpi(phba, vpi);
if (rc) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
@@ -700,6 +700,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
}
spin_unlock_irq(&phba->ndlp_lock);
}
+ if (vport->vpi_state != LPFC_VPI_REGISTERED)
+ goto skip_logo;
vport->unreg_vpi_cmpl = VPORT_INVAL;
timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
if (!lpfc_issue_els_npiv_logo(vport, ndlp))
diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h
index 512c2cc1a33..d310f49d077 100644
--- a/drivers/scsi/megaraid.h
+++ b/drivers/scsi/megaraid.h
@@ -381,7 +381,7 @@ typedef struct {
u8 battery_status; /*
* BIT 0: battery module missing
* BIT 1: VBAD
- * BIT 2: temprature high
+ * BIT 2: temperature high
* BIT 3: battery pack missing
* BIT 4,5:
* 00 - charge complete
diff --git a/drivers/scsi/megaraid/mbox_defs.h b/drivers/scsi/megaraid/mbox_defs.h
index b25b74764ec..ce2487a888e 100644
--- a/drivers/scsi/megaraid/mbox_defs.h
+++ b/drivers/scsi/megaraid/mbox_defs.h
@@ -497,7 +497,7 @@ typedef struct {
* @inserted_drive : channel:Id of inserted drive
* @battery_status : bit 0: battery module missing
* bit 1: VBAD
- * bit 2: temprature high
+ * bit 2: temperature high
* bit 3: battery pack missing
* bit 4,5:
* 00 - charge complete
diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c
index 234f0b7eb21..7f977967b88 100644
--- a/drivers/scsi/megaraid/megaraid_mbox.c
+++ b/drivers/scsi/megaraid/megaraid_mbox.c
@@ -335,12 +335,17 @@ static struct device_attribute *megaraid_sdev_attrs[] = {
* megaraid_change_queue_depth - Change the device's queue depth
* @sdev: scsi device struct
* @qdepth: depth to set
+ * @reason: calling context
*
* Return value:
* actual depth set
*/
-static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (qdepth > MBOX_MAX_SCSI_CMDS)
qdepth = MBOX_MAX_SCSI_CMDS;
scsi_adjust_queue_depth(sdev, 0, qdepth);
@@ -2704,7 +2709,7 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
}
else {
con_log(CL_ANN, (KERN_NOTICE
- "megaraid mbox: reset sequence completed sucessfully\n"));
+ "megaraid mbox: reset sequence completed successfully\n"));
}
diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c
index a39addc3a59..134c63ef6d3 100644
--- a/drivers/scsi/megaraid/megaraid_sas.c
+++ b/drivers/scsi/megaraid/megaraid_sas.c
@@ -10,7 +10,7 @@
* 2 of the License, or (at your option) any later version.
*
* FILE : megaraid_sas.c
- * Version : v00.00.04.01-rc1
+ * Version : v00.00.04.12-rc1
*
* Authors:
* (email-id : megaraidlinux@lsi.com)
@@ -40,6 +40,7 @@
#include <linux/compat.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
+#include <linux/poll.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -75,6 +76,10 @@ static struct pci_device_id megasas_pci_table[] = {
/* gen2*/
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0079GEN2)},
/* gen2*/
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0073SKINNY)},
+ /* skinny*/
+ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0071SKINNY)},
+ /* skinny*/
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)},
/* xscale IOP, vega */
{PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)},
@@ -89,8 +94,14 @@ static struct megasas_mgmt_info megasas_mgmt_info;
static struct fasync_struct *megasas_async_queue;
static DEFINE_MUTEX(megasas_async_queue_mutex);
+static int megasas_poll_wait_aen;
+static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait);
+static u32 support_poll_for_event;
static u32 megasas_dbg_lvl;
+/* define lock for aen poll */
+spinlock_t poll_aen_lock;
+
static void
megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
u8 alt_status);
@@ -215,7 +226,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs)
* @regs : MFI register set
*/
static inline void
-megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs)
+megasas_fire_cmd_xscale(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
+ struct megasas_register_set __iomem *regs)
{
writel((frame_phys_addr >> 3)|(frame_count),
&(regs)->inbound_queue_port);
@@ -312,7 +326,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs)
* @regs : MFI register set
*/
static inline void
-megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs)
+megasas_fire_cmd_ppc(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
+ struct megasas_register_set __iomem *regs)
{
writel((frame_phys_addr | (frame_count<<1))|1,
&(regs)->inbound_queue_port);
@@ -328,6 +345,104 @@ static struct megasas_instance_template megasas_instance_template_ppc = {
};
/**
+ * megasas_enable_intr_skinny - Enables interrupts
+ * @regs: MFI register set
+ */
+static inline void
+megasas_enable_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+ writel(0xFFFFFFFF, &(regs)->outbound_intr_mask);
+
+ writel(~MFI_SKINNY_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask);
+
+ /* Dummy readl to force pci flush */
+ readl(&regs->outbound_intr_mask);
+}
+
+/**
+ * megasas_disable_intr_skinny - Disables interrupt
+ * @regs: MFI register set
+ */
+static inline void
+megasas_disable_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+ u32 mask = 0xFFFFFFFF;
+ writel(mask, &regs->outbound_intr_mask);
+ /* Dummy readl to force pci flush */
+ readl(&regs->outbound_intr_mask);
+}
+
+/**
+ * megasas_read_fw_status_reg_skinny - returns the current FW status value
+ * @regs: MFI register set
+ */
+static u32
+megasas_read_fw_status_reg_skinny(struct megasas_register_set __iomem *regs)
+{
+ return readl(&(regs)->outbound_scratch_pad);
+}
+
+/**
+ * megasas_clear_interrupt_skinny - Check & clear interrupt
+ * @regs: MFI register set
+ */
+static int
+megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+ u32 status;
+ /*
+ * Check if it is our interrupt
+ */
+ status = readl(&regs->outbound_intr_status);
+
+ if (!(status & MFI_SKINNY_ENABLE_INTERRUPT_MASK)) {
+ return 1;
+ }
+
+ /*
+ * Clear the interrupt by writing back the same value
+ */
+ writel(status, &regs->outbound_intr_status);
+
+ /*
+ * dummy read to flush PCI
+ */
+ readl(&regs->outbound_intr_status);
+
+ return 0;
+}
+
+/**
+ * megasas_fire_cmd_skinny - Sends command to the FW
+ * @frame_phys_addr : Physical address of cmd
+ * @frame_count : Number of frames for the command
+ * @regs : MFI register set
+ */
+static inline void
+megasas_fire_cmd_skinny(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
+ struct megasas_register_set __iomem *regs)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&instance->fire_lock, flags);
+ writel(0, &(regs)->inbound_high_queue_port);
+ writel((frame_phys_addr | (frame_count<<1))|1,
+ &(regs)->inbound_low_queue_port);
+ spin_unlock_irqrestore(&instance->fire_lock, flags);
+}
+
+static struct megasas_instance_template megasas_instance_template_skinny = {
+
+ .fire_cmd = megasas_fire_cmd_skinny,
+ .enable_intr = megasas_enable_intr_skinny,
+ .disable_intr = megasas_disable_intr_skinny,
+ .clear_intr = megasas_clear_intr_skinny,
+ .read_fw_status_reg = megasas_read_fw_status_reg_skinny,
+};
+
+
+/**
* The following functions are defined for gen2 (deviceid : 0x78 0x79)
* controllers
*/
@@ -404,7 +519,9 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs)
* @regs : MFI register set
*/
static inline void
-megasas_fire_cmd_gen2(dma_addr_t frame_phys_addr, u32 frame_count,
+megasas_fire_cmd_gen2(struct megasas_instance *instance,
+ dma_addr_t frame_phys_addr,
+ u32 frame_count,
struct megasas_register_set __iomem *regs)
{
writel((frame_phys_addr | (frame_count<<1))|1,
@@ -446,7 +563,8 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
/*
* Issue the frame using inbound queue port
*/
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
/*
* Wait for cmd_status to change
@@ -477,7 +595,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
{
cmd->cmd_status = ENODATA;
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA),
MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
@@ -522,7 +641,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
cmd->sync_cmd = 1;
cmd->cmd_status = 0xFF;
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
/*
* Wait for this cmd to complete
@@ -592,6 +712,35 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
return sge_count;
}
+/**
+ * megasas_make_sgl_skinny - Prepares IEEE SGL
+ * @instance: Adapter soft state
+ * @scp: SCSI command from the mid-layer
+ * @mfi_sgl: SGL to be filled in
+ *
+ * If successful, this function returns the number of SG elements. Otherwise,
+ * it returnes -1.
+ */
+static int
+megasas_make_sgl_skinny(struct megasas_instance *instance,
+ struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl)
+{
+ int i;
+ int sge_count;
+ struct scatterlist *os_sgl;
+
+ sge_count = scsi_dma_map(scp);
+
+ if (sge_count) {
+ scsi_for_each_sg(scp, os_sgl, sge_count, i) {
+ mfi_sgl->sge_skinny[i].length = sg_dma_len(os_sgl);
+ mfi_sgl->sge_skinny[i].phys_addr =
+ sg_dma_address(os_sgl);
+ }
+ }
+ return sge_count;
+}
+
/**
* megasas_get_frame_count - Computes the number of frames
* @frame_type : type of frame- io or pthru frame
@@ -600,7 +749,8 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Returns the number of frames required for numnber of sge's (sge_count)
*/
-static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
+static u32 megasas_get_frame_count(struct megasas_instance *instance,
+ u8 sge_count, u8 frame_type)
{
int num_cnt;
int sge_bytes;
@@ -610,6 +760,10 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
sizeof(struct megasas_sge32);
+ if (instance->flag_ieee) {
+ sge_sz = sizeof(struct megasas_sge_skinny);
+ }
+
/*
* Main frame can contain 2 SGEs for 64-bit SGLs and
* 3 SGEs for 32-bit SGLs for ldio &
@@ -617,12 +771,16 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
* 2 SGEs for 32-bit SGLs for pthru frame
*/
if (unlikely(frame_type == PTHRU_FRAME)) {
- if (IS_DMA64)
+ if (instance->flag_ieee == 1) {
+ num_cnt = sge_count - 1;
+ } else if (IS_DMA64)
num_cnt = sge_count - 1;
else
num_cnt = sge_count - 2;
} else {
- if (IS_DMA64)
+ if (instance->flag_ieee == 1) {
+ num_cnt = sge_count - 1;
+ } else if (IS_DMA64)
num_cnt = sge_count - 2;
else
num_cnt = sge_count - 3;
@@ -671,6 +829,10 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
else if (scp->sc_data_direction == PCI_DMA_NONE)
flags = MFI_FRAME_DIR_NONE;
+ if (instance->flag_ieee == 1) {
+ flags |= MFI_FRAME_IEEE;
+ }
+
/*
* Prepare the DCDB frame
*/
@@ -687,9 +849,24 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
/*
+ * If the command is for the tape device, set the
+ * pthru timeout to the os layer timeout value.
+ */
+ if (scp->device->type == TYPE_TAPE) {
+ if ((scp->request->timeout / HZ) > 0xFFFF)
+ pthru->timeout = 0xFFFF;
+ else
+ pthru->timeout = scp->request->timeout / HZ;
+ }
+
+ /*
* Construct SGL
*/
- if (IS_DMA64) {
+ if (instance->flag_ieee == 1) {
+ pthru->flags |= MFI_FRAME_SGL64;
+ pthru->sge_count = megasas_make_sgl_skinny(instance, scp,
+ &pthru->sgl);
+ } else if (IS_DMA64) {
pthru->flags |= MFI_FRAME_SGL64;
pthru->sge_count = megasas_make_sgl64(instance, scp,
&pthru->sgl);
@@ -708,7 +885,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Compute the total number of frames this command consumes. FW uses
* this number to pull sufficient number of frames from host memory.
*/
- cmd->frame_count = megasas_get_frame_count(pthru->sge_count,
+ cmd->frame_count = megasas_get_frame_count(instance, pthru->sge_count,
PTHRU_FRAME);
return cmd->frame_count;
@@ -739,6 +916,10 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
flags = MFI_FRAME_DIR_READ;
+ if (instance->flag_ieee == 1) {
+ flags |= MFI_FRAME_IEEE;
+ }
+
/*
* Prepare the Logical IO frame: 2nd bit is zero for all read cmds
*/
@@ -809,7 +990,11 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
/*
* Construct SGL
*/
- if (IS_DMA64) {
+ if (instance->flag_ieee) {
+ ldio->flags |= MFI_FRAME_SGL64;
+ ldio->sge_count = megasas_make_sgl_skinny(instance, scp,
+ &ldio->sgl);
+ } else if (IS_DMA64) {
ldio->flags |= MFI_FRAME_SGL64;
ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl);
} else
@@ -826,7 +1011,8 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
* Compute the total number of frames this command consumes. FW uses
* this number to pull sufficient number of frames from host memory.
*/
- cmd->frame_count = megasas_get_frame_count(ldio->sge_count, IO_FRAME);
+ cmd->frame_count = megasas_get_frame_count(instance,
+ ldio->sge_count, IO_FRAME);
return cmd->frame_count;
}
@@ -983,7 +1169,8 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
*/
atomic_inc(&instance->fw_outstanding);
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set);
+ instance->instancet->fire_cmd(instance, cmd->frame_phys_addr,
+ cmd->frame_count-1, instance->reg_set);
/*
* Check if we have pend cmds to be completed
*/
@@ -1000,24 +1187,76 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
return 0;
}
+static struct megasas_instance *megasas_lookup_instance(u16 host_no)
+{
+ int i;
+
+ for (i = 0; i < megasas_mgmt_info.max_index; i++) {
+
+ if ((megasas_mgmt_info.instance[i]) &&
+ (megasas_mgmt_info.instance[i]->host->host_no == host_no))
+ return megasas_mgmt_info.instance[i];
+ }
+
+ return NULL;
+}
+
static int megasas_slave_configure(struct scsi_device *sdev)
{
+ u16 pd_index = 0;
+ struct megasas_instance *instance ;
+
+ instance = megasas_lookup_instance(sdev->host->host_no);
+
/*
- * Don't export physical disk devices to the disk driver.
- *
- * FIXME: Currently we don't export them to the midlayer at all.
- * That will be fixed once LSI engineers have audited the
- * firmware for possible issues.
- */
- if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK)
+ * Don't export physical disk devices to the disk driver.
+ *
+ * FIXME: Currently we don't export them to the midlayer at all.
+ * That will be fixed once LSI engineers have audited the
+ * firmware for possible issues.
+ */
+ if (sdev->channel < MEGASAS_MAX_PD_CHANNELS &&
+ sdev->type == TYPE_DISK) {
+ pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+ sdev->id;
+ if (instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) {
+ blk_queue_rq_timeout(sdev->request_queue,
+ MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+ return 0;
+ }
return -ENXIO;
+ }
/*
- * The RAID firmware may require extended timeouts.
- */
- if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS)
- blk_queue_rq_timeout(sdev->request_queue,
- MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+ * The RAID firmware may require extended timeouts.
+ */
+ blk_queue_rq_timeout(sdev->request_queue,
+ MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+ return 0;
+}
+
+static int megasas_slave_alloc(struct scsi_device *sdev)
+{
+ u16 pd_index = 0;
+ struct megasas_instance *instance ;
+ instance = megasas_lookup_instance(sdev->host->host_no);
+ if ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) &&
+ (sdev->type == TYPE_DISK)) {
+ /*
+ * Open the OS scan to the SYSTEM PD
+ */
+ pd_index =
+ (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+ sdev->id;
+ if ((instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) &&
+ (instance->pd_list[pd_index].driveType ==
+ TYPE_DISK)) {
+ return 0;
+ }
+ return -ENXIO;
+ }
return 0;
}
@@ -1072,7 +1311,14 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr)
spin_lock_irqsave(instance->host->host_lock, flags);
instance->flag &= ~MEGASAS_FW_BUSY;
- instance->host->can_queue =
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ instance->host->can_queue =
+ instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
+ } else
+ instance->host->can_queue =
instance->max_fw_cmds - MEGASAS_INT_CMDS;
spin_unlock_irqrestore(instance->host->host_lock, flags);
@@ -1117,8 +1363,16 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
* Send signal to FW to stop processing any pending cmds.
* The controller will be taken offline by the OS now.
*/
- writel(MFI_STOP_ADP,
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ writel(MFI_STOP_ADP,
+ &instance->reg_set->reserved_0[0]);
+ } else {
+ writel(MFI_STOP_ADP,
&instance->reg_set->inbound_doorbell);
+ }
megasas_dump_pending_frames(instance);
instance->hw_crit_error = 1;
return FAILED;
@@ -1266,6 +1520,8 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
return 0;
}
+static void megasas_aen_polling(struct work_struct *work);
+
/**
* megasas_service_aen - Processes an event notification
* @instance: Adapter soft state
@@ -1281,16 +1537,36 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
static void
megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
{
+ unsigned long flags;
/*
* Don't signal app if it is just an aborted previously registered aen
*/
- if (!cmd->abort_aen)
+ if ((!cmd->abort_aen) && (instance->unload == 0)) {
+ spin_lock_irqsave(&poll_aen_lock, flags);
+ megasas_poll_wait_aen = 1;
+ spin_unlock_irqrestore(&poll_aen_lock, flags);
+ wake_up(&megasas_poll_wait);
kill_fasync(&megasas_async_queue, SIGIO, POLL_IN);
+ }
else
cmd->abort_aen = 0;
instance->aen_cmd = NULL;
megasas_return_cmd(instance, cmd);
+
+ if (instance->unload == 0) {
+ struct megasas_aen_event *ev;
+ ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+ if (!ev) {
+ printk(KERN_ERR "megasas_service_aen: out of memory\n");
+ } else {
+ ev->instance = instance;
+ instance->ev = ev;
+ INIT_WORK(&ev->hotplug_work, megasas_aen_polling);
+ schedule_delayed_work(
+ (struct delayed_work *)&ev->hotplug_work, 0);
+ }
+ }
}
/*
@@ -1302,6 +1578,7 @@ static struct scsi_host_template megasas_template = {
.name = "LSI SAS based MegaRAID driver",
.proc_name = "megaraid_sas",
.slave_configure = megasas_slave_configure,
+ .slave_alloc = megasas_slave_alloc,
.queuecommand = megasas_queue_command,
.eh_device_reset_handler = megasas_reset_device,
.eh_bus_reset_handler = megasas_reset_bus_host,
@@ -1370,6 +1647,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
{
int exception = 0;
struct megasas_header *hdr = &cmd->frame->hdr;
+ unsigned long flags;
if (cmd->scmd)
cmd->scmd->SCp.ptr = NULL;
@@ -1459,6 +1737,12 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
case MFI_CMD_SMP:
case MFI_CMD_STP:
case MFI_CMD_DCMD:
+ if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET_INFO ||
+ cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET) {
+ spin_lock_irqsave(&poll_aen_lock, flags);
+ megasas_poll_wait_aen = 0;
+ spin_unlock_irqrestore(&poll_aen_lock, flags);
+ }
/*
* See if got an event notification
@@ -1536,6 +1820,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
u8 max_wait;
u32 fw_state;
u32 cur_state;
+ u32 abs_state, curr_abs_state;
fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK;
@@ -1545,6 +1830,9 @@ megasas_transition_to_ready(struct megasas_instance* instance)
while (fw_state != MFI_STATE_READY) {
+ abs_state =
+ instance->instancet->read_fw_status_reg(instance->reg_set);
+
switch (fw_state) {
case MFI_STATE_FAULT:
@@ -1556,18 +1844,36 @@ megasas_transition_to_ready(struct megasas_instance* instance)
/*
* Set the CLR bit in inbound doorbell
*/
- writel(MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
- &instance->reg_set->inbound_doorbell);
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+
+ writel(
+ MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
+ &instance->reg_set->reserved_0[0]);
+ } else {
+ writel(
+ MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
+ &instance->reg_set->inbound_doorbell);
+ }
- max_wait = 2;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_WAIT_HANDSHAKE;
break;
case MFI_STATE_BOOT_MESSAGE_PENDING:
- writel(MFI_INIT_HOTPLUG,
- &instance->reg_set->inbound_doorbell);
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ writel(MFI_INIT_HOTPLUG,
+ &instance->reg_set->reserved_0[0]);
+ } else
+ writel(MFI_INIT_HOTPLUG,
+ &instance->reg_set->inbound_doorbell);
- max_wait = 10;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_BOOT_MESSAGE_PENDING;
break;
@@ -1576,9 +1882,17 @@ megasas_transition_to_ready(struct megasas_instance* instance)
* Bring it to READY state; assuming max wait 10 secs
*/
instance->instancet->disable_intr(instance->reg_set);
- writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell);
+ if ((instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device ==
+ PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ writel(MFI_RESET_FLAGS,
+ &instance->reg_set->reserved_0[0]);
+ } else
+ writel(MFI_RESET_FLAGS,
+ &instance->reg_set->inbound_doorbell);
- max_wait = 60;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_OPERATIONAL;
break;
@@ -1586,32 +1900,32 @@ megasas_transition_to_ready(struct megasas_instance* instance)
/*
* This state should not last for more than 2 seconds
*/
- max_wait = 2;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_UNDEFINED;
break;
case MFI_STATE_BB_INIT:
- max_wait = 2;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_BB_INIT;
break;
case MFI_STATE_FW_INIT:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_FW_INIT;
break;
case MFI_STATE_FW_INIT_2:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_FW_INIT_2;
break;
case MFI_STATE_DEVICE_SCAN:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_DEVICE_SCAN;
break;
case MFI_STATE_FLUSH_CACHE:
- max_wait = 20;
+ max_wait = MEGASAS_RESET_WAIT_TIME;
cur_state = MFI_STATE_FLUSH_CACHE;
break;
@@ -1627,8 +1941,10 @@ megasas_transition_to_ready(struct megasas_instance* instance)
for (i = 0; i < (max_wait * 1000); i++) {
fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) &
MFI_STATE_MASK ;
+ curr_abs_state =
+ instance->instancet->read_fw_status_reg(instance->reg_set);
- if (fw_state == cur_state) {
+ if (abs_state == curr_abs_state) {
msleep(1);
} else
break;
@@ -1637,7 +1953,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
/*
* Return error if fw_state hasn't changed after max_wait
*/
- if (fw_state == cur_state) {
+ if (curr_abs_state == abs_state) {
printk(KERN_DEBUG "FW state [%d] hasn't changed "
"in %d secs\n", fw_state, max_wait);
return -ENODEV;
@@ -1715,6 +2031,10 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
sizeof(struct megasas_sge32);
+ if (instance->flag_ieee) {
+ sge_sz = sizeof(struct megasas_sge_skinny);
+ }
+
/*
* Calculated the number of 64byte frames required for SGL
*/
@@ -1777,6 +2097,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
}
cmd->frame->io.context = cmd->index;
+ cmd->frame->io.pad_0 = 0;
}
return 0;
@@ -1882,6 +2203,97 @@ static int megasas_alloc_cmds(struct megasas_instance *instance)
return 0;
}
+/*
+ * megasas_get_pd_list_info - Returns FW's pd_list structure
+ * @instance: Adapter soft state
+ * @pd_list: pd_list structure
+ *
+ * Issues an internal command (DCMD) to get the FW's controller PD
+ * list structure. This information is mainly used to find out SYSTEM
+ * supported by the FW.
+ */
+static int
+megasas_get_pd_list(struct megasas_instance *instance)
+{
+ int ret = 0, pd_index = 0;
+ struct megasas_cmd *cmd;
+ struct megasas_dcmd_frame *dcmd;
+ struct MR_PD_LIST *ci;
+ struct MR_PD_ADDRESS *pd_addr;
+ dma_addr_t ci_h = 0;
+
+ cmd = megasas_get_cmd(instance);
+
+ if (!cmd) {
+ printk(KERN_DEBUG "megasas (get_pd_list): Failed to get cmd\n");
+ return -ENOMEM;
+ }
+
+ dcmd = &cmd->frame->dcmd;
+
+ ci = pci_alloc_consistent(instance->pdev,
+ MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), &ci_h);
+
+ if (!ci) {
+ printk(KERN_DEBUG "Failed to alloc mem for pd_list\n");
+ megasas_return_cmd(instance, cmd);
+ return -ENOMEM;
+ }
+
+ memset(ci, 0, sizeof(*ci));
+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+ dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
+ dcmd->mbox.b[1] = 0;
+ dcmd->cmd = MFI_CMD_DCMD;
+ dcmd->cmd_status = 0xFF;
+ dcmd->sge_count = 1;
+ dcmd->flags = MFI_FRAME_DIR_READ;
+ dcmd->timeout = 0;
+ dcmd->data_xfer_len = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
+ dcmd->opcode = MR_DCMD_PD_LIST_QUERY;
+ dcmd->sgl.sge32[0].phys_addr = ci_h;
+ dcmd->sgl.sge32[0].length = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
+
+ if (!megasas_issue_polled(instance, cmd)) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+
+ /*
+ * the following function will get the instance PD LIST.
+ */
+
+ pd_addr = ci->addr;
+
+ if ( ret == 0 &&
+ (ci->count <
+ (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) {
+
+ memset(instance->pd_list, 0,
+ MEGASAS_MAX_PD * sizeof(struct megasas_pd_list));
+
+ for (pd_index = 0; pd_index < ci->count; pd_index++) {
+
+ instance->pd_list[pd_addr->deviceId].tid =
+ pd_addr->deviceId;
+ instance->pd_list[pd_addr->deviceId].driveType =
+ pd_addr->scsiDevType;
+ instance->pd_list[pd_addr->deviceId].driveState =
+ MR_PD_STATE_SYSTEM;
+ pd_addr++;
+ }
+ }
+
+ pci_free_consistent(instance->pdev,
+ MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
+ ci, ci_h);
+ megasas_return_cmd(instance, cmd);
+
+ return ret;
+}
+
/**
* megasas_get_controller_info - Returns FW's controller structure
* @instance: Adapter soft state
@@ -2081,6 +2493,8 @@ static int megasas_init_mfi(struct megasas_instance *instance)
* Map the message registers
*/
if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078GEN2) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
(instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0079GEN2)) {
instance->base_addr = pci_resource_start(instance->pdev, 1);
} else {
@@ -2111,6 +2525,10 @@ static int megasas_init_mfi(struct megasas_instance *instance)
case PCI_DEVICE_ID_LSI_SAS0079GEN2:
instance->instancet = &megasas_instance_template_gen2;
break;
+ case PCI_DEVICE_ID_LSI_SAS0073SKINNY:
+ case PCI_DEVICE_ID_LSI_SAS0071SKINNY:
+ instance->instancet = &megasas_instance_template_skinny;
+ break;
case PCI_DEVICE_ID_LSI_SAS1064R:
case PCI_DEVICE_ID_DELL_PERC5:
default:
@@ -2166,6 +2584,10 @@ static int megasas_init_mfi(struct megasas_instance *instance)
if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
+ memset(instance->pd_list, 0 ,
+ (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
+ megasas_get_pd_list(instance);
+
ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);
/*
@@ -2409,6 +2831,11 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h;
dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail);
+ if (instance->aen_cmd != NULL) {
+ megasas_return_cmd(instance, cmd);
+ return 0;
+ }
+
/*
* Store reference to the cmd used to register for AEN. When an
* application wants us to register for AEN, we have to abort this
@@ -2419,7 +2846,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
/*
* Issue the aen registration frame
*/
- instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+ instance->instancet->fire_cmd(instance,
+ cmd->frame_phys_addr, 0, instance->reg_set);
return 0;
}
@@ -2465,7 +2893,13 @@ static int megasas_io_attach(struct megasas_instance *instance)
*/
host->irq = instance->pdev->irq;
host->unique_id = instance->unique_id;
- host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS;
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ host->can_queue =
+ instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
+ } else
+ host->can_queue =
+ instance->max_fw_cmds - MEGASAS_INT_CMDS;
host->this_id = instance->init_id;
host->sg_tablesize = instance->max_num_sge;
host->max_sectors = instance->max_sectors_per_req;
@@ -2572,6 +3006,9 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
*instance->producer = 0;
*instance->consumer = 0;
+ megasas_poll_wait_aen = 0;
+ instance->flag_ieee = 0;
+ instance->ev = NULL;
instance->evt_detail = pci_alloc_consistent(pdev,
sizeof(struct
@@ -2595,10 +3032,11 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
init_waitqueue_head(&instance->abort_cmd_wait_q);
spin_lock_init(&instance->cmd_pool_lock);
+ spin_lock_init(&instance->fire_lock);
spin_lock_init(&instance->completion_lock);
+ spin_lock_init(&poll_aen_lock);
mutex_init(&instance->aen_mutex);
- sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
/*
* Initialize PCI related and misc parameters
@@ -2608,8 +3046,16 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
instance->unique_id = pdev->bus->number << 8 | pdev->devfn;
instance->init_id = MEGASAS_DEFAULT_INIT_ID;
+ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+ (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+ instance->flag_ieee = 1;
+ sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
+ } else
+ sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
+
megasas_dbg_lvl = 0;
instance->flag = 0;
+ instance->unload = 1;
instance->last_time = 0;
/*
@@ -2655,6 +3101,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (megasas_io_attach(instance))
goto fail_io_attach;
+ instance->unload = 0;
return 0;
fail_start_aen:
@@ -2778,12 +3225,23 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
instance = pci_get_drvdata(pdev);
host = instance->host;
+ instance->unload = 1;
if (poll_mode_io)
del_timer_sync(&instance->io_completion_timer);
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
+
+ /* cancel the delayed work if this work still in queue */
+ if (instance->ev != NULL) {
+ struct megasas_aen_event *ev = instance->ev;
+ cancel_delayed_work(
+ (struct delayed_work *)&ev->hotplug_work);
+ flush_scheduled_work();
+ instance->ev = NULL;
+ }
+
tasklet_kill(&instance->isr_tasklet);
pci_set_drvdata(instance->pdev, instance);
@@ -2873,6 +3331,8 @@ megasas_resume(struct pci_dev *pdev)
megasas_start_timer(instance, &instance->io_completion_timer,
megasas_io_completion_timer,
MEGASAS_COMPLETION_TIMER_INTERVAL);
+ instance->unload = 0;
+
return 0;
fail_irq:
@@ -2913,6 +3373,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
struct megasas_instance *instance;
instance = pci_get_drvdata(pdev);
+ instance->unload = 1;
host = instance->host;
if (poll_mode_io)
@@ -2921,6 +3382,16 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
scsi_remove_host(instance->host);
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
+
+ /* cancel the delayed work if this work still in queue*/
+ if (instance->ev != NULL) {
+ struct megasas_aen_event *ev = instance->ev;
+ cancel_delayed_work(
+ (struct delayed_work *)&ev->hotplug_work);
+ flush_scheduled_work();
+ instance->ev = NULL;
+ }
+
tasklet_kill(&instance->isr_tasklet);
/*
@@ -2969,6 +3440,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
static void megasas_shutdown(struct pci_dev *pdev)
{
struct megasas_instance *instance = pci_get_drvdata(pdev);
+ instance->unload = 1;
megasas_flush_cache(instance);
megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
}
@@ -3016,6 +3488,23 @@ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode)
}
/**
+ * megasas_mgmt_poll - char node "poll" entry point
+ * */
+static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask;
+ unsigned long flags;
+ poll_wait(file, &megasas_poll_wait, wait);
+ spin_lock_irqsave(&poll_aen_lock, flags);
+ if (megasas_poll_wait_aen)
+ mask = (POLLIN | POLLRDNORM);
+ else
+ mask = 0;
+ spin_unlock_irqrestore(&poll_aen_lock, flags);
+ return mask;
+}
+
+/**
* megasas_mgmt_fw_ioctl - Issues management ioctls to FW
* @instance: Adapter soft state
* @argp: User's ioctl packet
@@ -3032,7 +3521,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
int error = 0, i;
void *sense = NULL;
dma_addr_t sense_handle;
- u32 *sense_ptr;
+ unsigned long *sense_ptr;
memset(kbuff_arr, 0, sizeof(kbuff_arr));
@@ -3056,6 +3545,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
*/
memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
cmd->frame->hdr.context = cmd->index;
+ cmd->frame->hdr.pad_0 = 0;
/*
* The management interface between applications and the fw uses
@@ -3109,7 +3599,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
}
sense_ptr =
- (u32 *) ((unsigned long)cmd->frame + ioc->sense_off);
+ (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off);
*sense_ptr = sense_handle;
}
@@ -3140,8 +3630,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
* sense_ptr points to the location that has the user
* sense buffer address
*/
- sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw +
- ioc->sense_off);
+ sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw +
+ ioc->sense_off);
if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),
sense, ioc->sense_len)) {
@@ -3177,20 +3667,6 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
return error;
}
-static struct megasas_instance *megasas_lookup_instance(u16 host_no)
-{
- int i;
-
- for (i = 0; i < megasas_mgmt_info.max_index; i++) {
-
- if ((megasas_mgmt_info.instance[i]) &&
- (megasas_mgmt_info.instance[i]->host->host_no == host_no))
- return megasas_mgmt_info.instance[i];
- }
-
- return NULL;
-}
-
static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
{
struct megasas_iocpacket __user *user_ioc =
@@ -3214,6 +3690,17 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
goto out_kfree_ioc;
}
+ if (instance->hw_crit_error == 1) {
+ printk(KERN_DEBUG "Controller in Crit ERROR\n");
+ error = -ENODEV;
+ goto out_kfree_ioc;
+ }
+
+ if (instance->unload == 1) {
+ error = -ENODEV;
+ goto out_kfree_ioc;
+ }
+
/*
* We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds
*/
@@ -3249,6 +3736,14 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
if (!instance)
return -ENODEV;
+ if (instance->hw_crit_error == 1) {
+ error = -ENODEV;
+ }
+
+ if (instance->unload == 1) {
+ return -ENODEV;
+ }
+
mutex_lock(&instance->aen_mutex);
error = megasas_register_aen(instance, aen.seq_num,
aen.class_locale_word);
@@ -3337,6 +3832,7 @@ static const struct file_operations megasas_mgmt_fops = {
.open = megasas_mgmt_open,
.fasync = megasas_mgmt_fasync,
.unlocked_ioctl = megasas_mgmt_ioctl,
+ .poll = megasas_mgmt_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = megasas_mgmt_compat_ioctl,
#endif
@@ -3378,6 +3874,15 @@ static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
NULL);
static ssize_t
+megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf)
+{
+ return sprintf(buf, "%u\n", support_poll_for_event);
+}
+
+static DRIVER_ATTR(support_poll_for_event, S_IRUGO,
+ megasas_sysfs_show_support_poll_for_event, NULL);
+
+static ssize_t
megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
{
return sprintf(buf, "%u\n", megasas_dbg_lvl);
@@ -3451,6 +3956,92 @@ out:
return retval;
}
+static void
+megasas_aen_polling(struct work_struct *work)
+{
+ struct megasas_aen_event *ev =
+ container_of(work, struct megasas_aen_event, hotplug_work);
+ struct megasas_instance *instance = ev->instance;
+ union megasas_evt_class_locale class_locale;
+ struct Scsi_Host *host;
+ struct scsi_device *sdev1;
+ u16 pd_index = 0;
+ int i, j, doscan = 0;
+ u32 seq_num;
+ int error;
+
+ if (!instance) {
+ printk(KERN_ERR "invalid instance!\n");
+ kfree(ev);
+ return;
+ }
+ instance->ev = NULL;
+ host = instance->host;
+ if (instance->evt_detail) {
+
+ switch (instance->evt_detail->code) {
+ case MR_EVT_PD_INSERTED:
+ case MR_EVT_PD_REMOVED:
+ case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
+ doscan = 1;
+ break;
+ default:
+ doscan = 0;
+ break;
+ }
+ } else {
+ printk(KERN_ERR "invalid evt_detail!\n");
+ kfree(ev);
+ return;
+ }
+
+ if (doscan) {
+ printk(KERN_INFO "scanning ...\n");
+ megasas_get_pd_list(instance);
+ for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
+ for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+ pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j;
+ sdev1 = scsi_device_lookup(host, i, j, 0);
+ if (instance->pd_list[pd_index].driveState ==
+ MR_PD_STATE_SYSTEM) {
+ if (!sdev1) {
+ scsi_add_device(host, i, j, 0);
+ }
+ if (sdev1)
+ scsi_device_put(sdev1);
+ } else {
+ if (sdev1) {
+ scsi_remove_device(sdev1);
+ scsi_device_put(sdev1);
+ }
+ }
+ }
+ }
+ }
+
+ if ( instance->aen_cmd != NULL ) {
+ kfree(ev);
+ return ;
+ }
+
+ seq_num = instance->evt_detail->seq_num + 1;
+
+ /* Register AEN with FW for latest sequence number plus 1 */
+ class_locale.members.reserved = 0;
+ class_locale.members.locale = MR_EVT_LOCALE_ALL;
+ class_locale.members.class = MR_EVT_CLASS_DEBUG;
+ mutex_lock(&instance->aen_mutex);
+ error = megasas_register_aen(instance, seq_num,
+ class_locale.word);
+ mutex_unlock(&instance->aen_mutex);
+
+ if (error)
+ printk(KERN_ERR "register aen failed error %x\n", error);
+
+ kfree(ev);
+}
+
+
static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO,
megasas_sysfs_show_poll_mode_io,
megasas_sysfs_set_poll_mode_io);
@@ -3468,6 +4059,8 @@ static int __init megasas_init(void)
printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
MEGASAS_EXT_VERSION);
+ support_poll_for_event = 2;
+
memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info));
/*
@@ -3500,6 +4093,12 @@ static int __init megasas_init(void)
&driver_attr_release_date);
if (rval)
goto err_dcf_rel_date;
+
+ rval = driver_create_file(&megasas_pci_driver.driver,
+ &driver_attr_support_poll_for_event);
+ if (rval)
+ goto err_dcf_support_poll_for_event;
+
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_dbg_lvl);
if (rval)
@@ -3516,7 +4115,12 @@ err_dcf_poll_mode_io:
&driver_attr_dbg_lvl);
err_dcf_dbg_lvl:
driver_remove_file(&megasas_pci_driver.driver,
+ &driver_attr_support_poll_for_event);
+
+err_dcf_support_poll_for_event:
+ driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_release_date);
+
err_dcf_rel_date:
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
err_dcf_attr_ver:
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index 0d033248fdf..72b28e436e3 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -18,9 +18,9 @@
/*
* MegaRAID SAS Driver meta data
*/
-#define MEGASAS_VERSION "00.00.04.01"
-#define MEGASAS_RELDATE "July 24, 2008"
-#define MEGASAS_EXT_VERSION "Thu July 24 11:41:51 PST 2008"
+#define MEGASAS_VERSION "00.00.04.12-rc1"
+#define MEGASAS_RELDATE "Sep. 17, 2009"
+#define MEGASAS_EXT_VERSION "Thu Sep. 17 11:41:51 PST 2009"
/*
* Device IDs
@@ -30,6 +30,8 @@
#define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413
#define PCI_DEVICE_ID_LSI_SAS1078GEN2 0x0078
#define PCI_DEVICE_ID_LSI_SAS0079GEN2 0x0079
+#define PCI_DEVICE_ID_LSI_SAS0073SKINNY 0x0073
+#define PCI_DEVICE_ID_LSI_SAS0071SKINNY 0x0071
/*
* =====================================
@@ -94,6 +96,7 @@
#define MFI_FRAME_DIR_WRITE 0x0008
#define MFI_FRAME_DIR_READ 0x0010
#define MFI_FRAME_DIR_BOTH 0x0018
+#define MFI_FRAME_IEEE 0x0020
/*
* Definition for cmd_status
@@ -131,6 +134,7 @@
#define MR_DCMD_CLUSTER 0x08000000
#define MR_DCMD_CLUSTER_RESET_ALL 0x08010100
#define MR_DCMD_CLUSTER_RESET_LD 0x08010200
+#define MR_DCMD_PD_LIST_QUERY 0x02010100
/*
* MFI command completion codes
@@ -251,9 +255,100 @@ enum MR_EVT_ARGS {
MR_EVT_ARGS_STR,
MR_EVT_ARGS_TIME,
MR_EVT_ARGS_ECC,
+ MR_EVT_ARGS_LD_PROP,
+ MR_EVT_ARGS_PD_SPARE,
+ MR_EVT_ARGS_PD_INDEX,
+ MR_EVT_ARGS_DIAG_PASS,
+ MR_EVT_ARGS_DIAG_FAIL,
+ MR_EVT_ARGS_PD_LBA_LBA,
+ MR_EVT_ARGS_PORT_PHY,
+ MR_EVT_ARGS_PD_MISSING,
+ MR_EVT_ARGS_PD_ADDRESS,
+ MR_EVT_ARGS_BITMAP,
+ MR_EVT_ARGS_CONNECTOR,
+ MR_EVT_ARGS_PD_PD,
+ MR_EVT_ARGS_PD_FRU,
+ MR_EVT_ARGS_PD_PATHINFO,
+ MR_EVT_ARGS_PD_POWER_STATE,
+ MR_EVT_ARGS_GENERIC,
+};
+/*
+ * define constants for device list query options
+ */
+enum MR_PD_QUERY_TYPE {
+ MR_PD_QUERY_TYPE_ALL = 0,
+ MR_PD_QUERY_TYPE_STATE = 1,
+ MR_PD_QUERY_TYPE_POWER_STATE = 2,
+ MR_PD_QUERY_TYPE_MEDIA_TYPE = 3,
+ MR_PD_QUERY_TYPE_SPEED = 4,
+ MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5,
};
+#define MR_EVT_CFG_CLEARED 0x0004
+#define MR_EVT_LD_STATE_CHANGE 0x0051
+#define MR_EVT_PD_INSERTED 0x005b
+#define MR_EVT_PD_REMOVED 0x0070
+#define MR_EVT_LD_CREATED 0x008a
+#define MR_EVT_LD_DELETED 0x008b
+#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db
+#define MR_EVT_LD_OFFLINE 0x00fc
+#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152
+#define MAX_LOGICAL_DRIVES 64
+
+enum MR_PD_STATE {
+ MR_PD_STATE_UNCONFIGURED_GOOD = 0x00,
+ MR_PD_STATE_UNCONFIGURED_BAD = 0x01,
+ MR_PD_STATE_HOT_SPARE = 0x02,
+ MR_PD_STATE_OFFLINE = 0x10,
+ MR_PD_STATE_FAILED = 0x11,
+ MR_PD_STATE_REBUILD = 0x14,
+ MR_PD_STATE_ONLINE = 0x18,
+ MR_PD_STATE_COPYBACK = 0x20,
+ MR_PD_STATE_SYSTEM = 0x40,
+ };
+
+
+ /*
+ * defines the physical drive address structure
+ */
+struct MR_PD_ADDRESS {
+ u16 deviceId;
+ u16 enclDeviceId;
+
+ union {
+ struct {
+ u8 enclIndex;
+ u8 slotNumber;
+ } mrPdAddress;
+ struct {
+ u8 enclPosition;
+ u8 enclConnectorIndex;
+ } mrEnclAddress;
+ };
+ u8 scsiDevType;
+ union {
+ u8 connectedPortBitmap;
+ u8 connectedPortNumbers;
+ };
+ u64 sasAddr[2];
+} __packed;
+
+/*
+ * defines the physical drive list structure
+ */
+struct MR_PD_LIST {
+ u32 size;
+ u32 count;
+ struct MR_PD_ADDRESS addr[1];
+} __packed;
+
+struct megasas_pd_list {
+ u16 tid;
+ u8 driveType;
+ u8 driveState;
+} __packed;
+
/*
* SAS controller properties
*/
@@ -282,7 +377,7 @@ struct megasas_ctrl_prop {
u8 expose_encl_devices;
u8 reserved[38];
-} __attribute__ ((packed));
+} __packed;
/*
* SAS controller information
@@ -525,7 +620,7 @@ struct megasas_ctrl_info {
u8 pad[0x800 - 0x6a0];
-} __attribute__ ((packed));
+} __packed;
/*
* ===============================
@@ -540,6 +635,8 @@ struct megasas_ctrl_info {
#define MEGASAS_DEFAULT_INIT_ID -1
#define MEGASAS_MAX_LUN 8
#define MEGASAS_MAX_LD 64
+#define MEGASAS_MAX_PD (MEGASAS_MAX_PD_CHANNELS * \
+ MEGASAS_MAX_DEV_PER_CHANNEL)
#define MEGASAS_DBG_LVL 1
@@ -570,6 +667,7 @@ struct megasas_ctrl_info {
* is shown below
*/
#define MEGASAS_INT_CMDS 32
+#define MEGASAS_SKINNY_INT_CMDS 5
/*
* FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit
@@ -584,6 +682,8 @@ struct megasas_ctrl_info {
#define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000
#define MFI_REPLY_GEN2_MESSAGE_INTERRUPT 0x00000001
#define MFI_GEN2_ENABLE_INTERRUPT_MASK (0x00000001 | 0x00000004)
+#define MFI_REPLY_SKINNY_MESSAGE_INTERRUPT 0x40000000
+#define MFI_SKINNY_ENABLE_INTERRUPT_MASK (0x00000001)
/*
* register set for both 1068 and 1078 controllers
@@ -644,10 +744,17 @@ struct megasas_sge64 {
} __attribute__ ((packed));
+struct megasas_sge_skinny {
+ u64 phys_addr;
+ u32 length;
+ u32 flag;
+} __packed;
+
union megasas_sgl {
struct megasas_sge32 sge32[1];
struct megasas_sge64 sge64[1];
+ struct megasas_sge_skinny sge_skinny[1];
} __attribute__ ((packed));
@@ -1061,16 +1168,10 @@ struct megasas_evt_detail {
} __attribute__ ((packed));
- struct megasas_instance_template {
- void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *);
-
- void (*enable_intr)(struct megasas_register_set __iomem *) ;
- void (*disable_intr)(struct megasas_register_set __iomem *);
-
- int (*clear_intr)(struct megasas_register_set __iomem *);
-
- u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
- };
+struct megasas_aen_event {
+ struct work_struct hotplug_work;
+ struct megasas_instance *instance;
+};
struct megasas_instance {
@@ -1085,17 +1186,21 @@ struct megasas_instance {
unsigned long base_addr;
struct megasas_register_set __iomem *reg_set;
+ struct megasas_pd_list pd_list[MEGASAS_MAX_PD];
s8 init_id;
u16 max_num_sge;
u16 max_fw_cmds;
u32 max_sectors_per_req;
+ struct megasas_aen_event *ev;
struct megasas_cmd **cmd_list;
struct list_head cmd_pool;
spinlock_t cmd_pool_lock;
/* used to synch producer, consumer ptrs in dpc */
spinlock_t completion_lock;
+ /* used to sync fire the cmd to fw */
+ spinlock_t fire_lock;
struct dma_pool *frame_dma_pool;
struct dma_pool *sense_dma_pool;
@@ -1120,11 +1225,25 @@ struct megasas_instance {
struct tasklet_struct isr_tasklet;
u8 flag;
+ u8 unload;
+ u8 flag_ieee;
unsigned long last_time;
struct timer_list io_completion_timer;
};
+struct megasas_instance_template {
+ void (*fire_cmd)(struct megasas_instance *, dma_addr_t, \
+ u32, struct megasas_register_set __iomem *);
+
+ void (*enable_intr)(struct megasas_register_set __iomem *) ;
+ void (*disable_intr)(struct megasas_register_set __iomem *);
+
+ int (*clear_intr)(struct megasas_register_set __iomem *);
+
+ u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
+};
+
#define MEGASAS_IS_LOGICAL(scp) \
(scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h
index f9f6c083927..91416810529 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2.h
@@ -8,7 +8,7 @@
* scatter/gather formats.
* Creation Date: June 21, 2006
*
- * mpi2.h Version: 02.00.12
+ * mpi2.h Version: 02.00.13
*
* Version History
* ---------------
@@ -52,6 +52,7 @@
* 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.
* --------------------------------------------------------------------------
*/
@@ -77,7 +78,7 @@
#define MPI2_VERSION_02_00 (0x0200)
/* versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT (0x0C)
+#define MPI2_HEADER_VERSION_UNIT (0x0D)
#define MPI2_HEADER_VERSION_DEV (0x00)
#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00)
#define MPI2_HEADER_VERSION_UNIT_SHIFT (8)
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
index ab47c467964..1611c57a6fd 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
@@ -6,7 +6,7 @@
* Title: MPI Configuration messages and pages
* Creation Date: November 10, 2006
*
- * mpi2_cnfg.h Version: 02.00.11
+ * mpi2_cnfg.h Version: 02.00.12
*
* Version History
* ---------------
@@ -100,6 +100,13 @@
* 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.
* --------------------------------------------------------------------------
*/
@@ -182,6 +189,7 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
#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)
/*****************************************************************************
@@ -268,6 +276,14 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
#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
****************************************************************************/
@@ -349,6 +365,15 @@ typedef struct _MPI2_CONFIG_REPLY
#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064)
#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065)
+#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_SAS2208_7 (0x0086)
+#define MPI2_MFGPAGE_DEVID_SAS2208_8 (0x0087)
+
/* Manufacturing Page 0 */
@@ -787,6 +812,56 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 {
#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 */
+ U16 Reserved1; /* 0x04 */
+ U8 PCIeWidth; /* 0x06 */
+ U8 PCIeSpeed; /* 0x07 */
+ U32 ProcessorState; /* 0x08 */
+ U32 Reserved2; /* 0x0C */
+ U16 IOCTemperature; /* 0x10 */
+ U8 IOCTemperatureUnits; /* 0x12 */
+ U8 IOCSpeed; /* 0x13 */
+ U32 Reserved3; /* 0x14 */
+} MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
+ Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t;
+
+#define MPI2_IOUNITPAGE7_PAGEVERSION (0x00)
+
+/* 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 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)
+
+
+
/****************************************************************************
* IOC Config Pages
****************************************************************************/
@@ -1470,6 +1545,12 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1
/* values for PhyInfo fields */
#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000)
+
+#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000)
+#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)
@@ -1682,11 +1763,11 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1
/* values for SAS IO Unit Page 1 PortFlags */
#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01)
-/* values for SAS IO Unit Page 2 PhyFlags */
+/* 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 0 MaxMinLinkRate */
+/* 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)
@@ -1745,6 +1826,74 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4
#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 Reserved1; /* 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,
+ MPI2_POINTER PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+ Mpi2SasIOUnit5PhyPmSettings_t, MPI2_POINTER 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 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 Header.ExtPageLength or 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,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
+ Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t;
+
+#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x00)
+
+
+
+
/****************************************************************************
* SAS Expander Config Pages
****************************************************************************/
@@ -1927,6 +2076,8 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0
/* 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_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)
@@ -2343,5 +2494,122 @@ typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0
#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, MPI2_POINTER PTR_MPI2_ETHERNET_IP_ADDR,
+ Mpi2EthernetIpAddr_t, MPI2_POINTER 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, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_0,
+ Mpi2EthernetPage0_t, MPI2_POINTER 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, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_1,
+ Mpi2EthernetPage1_t, MPI2_POINTER 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)
+
+
#endif
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
index c294128bdeb..ea51ce86869 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
@@ -6,7 +6,7 @@
* Title: MPI IOC, Port, Event, FW Download, and FW Upload messages
* Creation Date: October 11, 2006
*
- * mpi2_ioc.h Version: 02.00.11
+ * mpi2_ioc.h Version: 02.00.12
*
* Version History
* ---------------
@@ -84,6 +84,9 @@
* 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.
* --------------------------------------------------------------------------
*/
@@ -274,6 +277,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY
#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)
@@ -448,6 +452,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
#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)
/* Log Entry Added Event data */
@@ -469,6 +474,16 @@ typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED
MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
Mpi2EventDataLogEntryAdded_t, MPI2_POINTER 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,
+ MPI2_POINTER PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT,
+ Mpi2EventDataGpioInterrupt_t, MPI2_POINTER pMpi2EventDataGpioInterrupt_t;
+
/* Hard Reset Received Event data */
typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED
@@ -1117,6 +1132,7 @@ typedef struct _MPI2_FW_IMAGE_HEADER
#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF)
/* SAS */
#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0010)
+#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0011)
/* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
index 7134816d904..5160c33d2a0 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h
@@ -6,7 +6,7 @@
* Title: MPI Integrated RAID messages and structures
* Creation Date: April 26, 2007
*
- * mpi2_raid.h Version: 02.00.03
+ * mpi2_raid.h Version: 02.00.04
*
* Version History
* ---------------
@@ -20,6 +20,8 @@
* 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.
* --------------------------------------------------------------------------
*/
@@ -217,10 +219,14 @@ typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT
/* 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)
-#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x04)
-#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x02)
-#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x01)
/* RAID Online Capacity Expansion Structure */
diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
index 007e950f7bf..73fcdbf9263 100644
--- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
+++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h
@@ -6,7 +6,7 @@
* Title: MPI diagnostic tool structures and definitions
* Creation Date: March 26, 2007
*
- * mpi2_tool.h Version: 02.00.03
+ * mpi2_tool.h Version: 02.00.04
*
* Version History
* ---------------
@@ -18,6 +18,10 @@
* 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.
* --------------------------------------------------------------------------
*/
@@ -282,7 +286,7 @@ typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY {
typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
{
- U8 Reserved1; /* 0x00 */
+ U8 ExtendedType; /* 0x00 */
U8 BufferType; /* 0x01 */
U8 ChainOffset; /* 0x02 */
U8 Function; /* 0x03 */
@@ -301,11 +305,15 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
} MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST,
Mpi2DiagBufferPostRequest_t, MPI2_POINTER 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 (0x02)
+#define MPI2_DIAG_BUF_TYPE_COUNT (0x03)
/****************************************************************************
@@ -314,7 +322,7 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
typedef struct _MPI2_DIAG_BUFFER_POST_REPLY
{
- U8 Reserved1; /* 0x00 */
+ U8 ExtendedType; /* 0x00 */
U8 BufferType; /* 0x01 */
U8 MsgLength; /* 0x02 */
U8 Function; /* 0x03 */
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 670241efa4b..6422e258fd5 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -57,6 +57,7 @@
#include <linux/dma-mapping.h>
#include <linux/sort.h>
#include <linux/io.h>
+#include <linux/time.h>
#include "mpt2sas_base.h"
@@ -77,6 +78,44 @@ static int msix_disable = -1;
module_param(msix_disable, int, 0);
MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
+/* 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;
+module_param(diag_buffer_enable, int, 0);
+MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers "
+ "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
+
+int mpt2sas_fwfault_debug;
+MODULE_PARM_DESC(mpt2sas_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 MPT2SAS_ADAPTER *ioc;
+
+ if (ret)
+ return ret;
+
+ printk(KERN_INFO "setting logging_level(0x%08x)\n",
+ mpt2sas_fwfault_debug);
+ list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
+ ioc->fwfault_debug = mpt2sas_fwfault_debug;
+ return 0;
+}
+module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug,
+ param_get_int, &mpt2sas_fwfault_debug, 0644);
+
/**
* _base_fault_reset_work - workq handling ioc fault conditions
* @work: input argument, used to derive ioc
@@ -121,7 +160,7 @@ _base_fault_reset_work(struct work_struct *work)
/**
* mpt2sas_base_start_watchdog - start the fault_reset_work_q
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* Context: sleep.
*
* Return nothing.
@@ -155,7 +194,7 @@ mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc)
/**
* mpt2sas_base_stop_watchdog - stop the fault_reset_work_q
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* Context: sleep.
*
* Return nothing.
@@ -177,10 +216,55 @@ mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc)
}
}
+/**
+ * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
+ * @ioc: per adapter object
+ * @fault_code: fault code
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
+{
+ printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
+ ioc->name, fault_code);
+}
+
+/**
+ * mpt2sas_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
+mpt2sas_halt_firmware(struct MPT2SAS_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)
+ mpt2sas_base_fault_info(ioc , doorbell);
+ else {
+ writel(0xC0FFEE00, &ioc->chip->Doorbell);
+ printk(MPT2SAS_ERR_FMT "Firmware is halted due to command "
+ "timeout\n", ioc->name);
+ }
+
+ panic("panic in %s\n", __func__);
+}
+
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
/**
* _base_sas_ioc_info - verbose translation of the ioc status
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @mpi_reply: reply mf payload returned from firmware
* @request_hdr: request mf
*
@@ -394,7 +478,7 @@ _base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
/**
* _base_display_event_data - verbose translation of firmware asyn events
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @mpi_reply: reply mf payload returned from firmware
*
* Return nothing.
@@ -474,7 +558,7 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc,
/**
* _base_sas_log_info - verbose translation of firmware log info
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @log_info: log info
*
* Return nothing.
@@ -526,22 +610,8 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
}
/**
- * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
- * @ioc: pointer to scsi command object
- * @fault_code: fault code
- *
- * Return nothing.
- */
-void
-mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
-{
- printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
- ioc->name, fault_code);
-}
-
-/**
* _base_display_reply_info -
- * @ioc: pointer to scsi command object
+ * @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)
@@ -570,7 +640,7 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
/**
* mpt2sas_base_done - base internal command completion routine
- * @ioc: pointer to scsi command object
+ * @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)
@@ -603,7 +673,7 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
/**
* _base_async_event - main callback handler for firmware asyn events
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
* @msix_index: MSIX table index supplied by the OS
* @reply: reply message frame(lower 32bit addr)
*
@@ -684,7 +754,7 @@ _base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid)
/**
* _base_mask_interrupts - disable interrupts
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
*
* Disabling ResetIRQ, Reply and Doorbell Interrupts
*
@@ -704,7 +774,7 @@ _base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc)
/**
* _base_unmask_interrupts - enable interrupts
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
*
* Enabling only Reply Interrupts
*
@@ -1258,12 +1328,13 @@ mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid)
* @ioc: per adapter object
* @smid: system request message index
*
- * Returns phys pointer to sense buffer.
+ * Returns phys pointer to the low 32bit address of the sense buffer.
*/
-dma_addr_t
+__le32
mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid)
{
- return ioc->sense_dma + ((smid - 1) * SCSI_SENSE_BUFFERSIZE);
+ return cpu_to_le32(ioc->sense_dma +
+ ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
}
/**
@@ -1697,6 +1768,12 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
}
if (ioc->facts.IOCCapabilities &
+ MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
+ printk(KERN_INFO "%sDiag Extended Buffer", i ? "," : "");
+ i++;
+ }
+
+ if (ioc->facts.IOCCapabilities &
MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
printk("%sTask Set Full", i ? "," : "");
i++;
@@ -2871,6 +2948,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
Mpi2IOCInitRequest_t mpi_request;
Mpi2IOCInitReply_t mpi_reply;
int r;
+ struct timeval current_time;
+ u16 ioc_status;
dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
__func__));
@@ -2921,6 +3000,13 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
cpu_to_le32(ioc->reply_post_free_dma);
#endif
+ /* This time stamp specifies number of milliseconds
+ * since epoch ~ midnight January 1, 1970.
+ */
+ do_gettimeofday(&current_time);
+ mpi_request.TimeStamp = (current_time.tv_sec * 1000) +
+ (current_time.tv_usec >> 3);
+
if (ioc->logging_level & MPT_DEBUG_INIT) {
u32 *mfp;
int i;
@@ -2943,7 +3029,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
return r;
}
- if (mpi_reply.IOCStatus != MPI2_IOCSTATUS_SUCCESS ||
+ ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+ if (ioc_status != MPI2_IOCSTATUS_SUCCESS ||
mpi_reply.IOCLogInfo) {
printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__);
r = -EIO;
@@ -3461,11 +3548,11 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
return r;
pci_set_drvdata(ioc->pdev, ioc->shost);
- r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
+ r = _base_get_ioc_facts(ioc, CAN_SLEEP);
if (r)
goto out_free_resources;
- r = _base_get_ioc_facts(ioc, CAN_SLEEP);
+ r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
if (r)
goto out_free_resources;
@@ -3531,6 +3618,8 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
goto out_free_resources;
mpt2sas_base_start_watchdog(ioc);
+ if (diag_buffer_enable != 0)
+ mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
return 0;
out_free_resources:
@@ -3684,6 +3773,9 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
__func__));
+ if (mpt2sas_fwfault_debug)
+ mpt2sas_halt_firmware(ioc);
+
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
if (ioc->shost_recovery) {
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index 0cf6bc236e4..bb4f14656af 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -69,8 +69,8 @@
#define MPT2SAS_DRIVER_NAME "mpt2sas"
#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION "02.100.03.00"
-#define MPT2SAS_MAJOR_VERSION 02
+#define MPT2SAS_DRIVER_VERSION "03.100.03.00"
+#define MPT2SAS_MAJOR_VERSION 03
#define MPT2SAS_MINOR_VERSION 100
#define MPT2SAS_BUILD_VERSION 03
#define MPT2SAS_RELEASE_VERSION 00
@@ -278,7 +278,7 @@ struct _internal_cmd {
* @sas_address: device sas address
* @device_name: retrieved from the SAS IDENTIFY frame.
* @handle: device handle
- * @parent_handle: handle to parent device
+ * @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)
@@ -296,7 +296,7 @@ struct _sas_device {
u64 sas_address;
u64 device_name;
u16 handle;
- u16 parent_handle;
+ u64 sas_address_parent;
u16 enclosure_handle;
u64 enclosure_logical_id;
u16 volume_handle;
@@ -352,8 +352,6 @@ struct _boot_device {
/**
* struct _sas_port - wide/narrow sas port information
* @port_list: list of ports belonging to expander
- * @handle: device handle for this port
- * @sas_address: sas address of this port
* @num_phys: number of phys belonging to this port
* @remote_identify: attached device identification
* @rphy: sas transport rphy object
@@ -362,8 +360,6 @@ struct _boot_device {
*/
struct _sas_port {
struct list_head port_list;
- u16 handle;
- u64 sas_address;
u8 num_phys;
struct sas_identify remote_identify;
struct sas_rphy *rphy;
@@ -398,7 +394,7 @@ struct _sas_phy {
* @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
- * @parent_handle: parent handle
+ * @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
@@ -411,7 +407,7 @@ struct _sas_node {
u8 num_phys;
u64 sas_address;
u16 handle;
- u16 parent_handle;
+ u64 sas_address_parent;
u16 enclosure_handle;
u64 enclosure_logical_id;
u8 responding;
@@ -470,6 +466,7 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
* @chip_phys: physical addrss prior to mapping
* @pio_chip: I/O mapped register space
* @logging_level: see mpt2sas_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
@@ -495,12 +492,14 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
* @msix_table_backup: backup msix table
* @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
* @base_cmds:
* @transport_cmds:
+ * @scsih_cmds:
* @tm_cmds:
* @ctl_cmds:
* @config_cmds:
@@ -591,6 +590,7 @@ struct MPT2SAS_ADAPTER {
unsigned long chip_phys;
unsigned long pio_chip;
int logging_level;
+ int fwfault_debug;
u8 ir_firmware;
int bars;
u8 mask_interrupts;
@@ -626,6 +626,7 @@ struct MPT2SAS_ADAPTER {
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 config_cb_idx;
@@ -633,6 +634,7 @@ struct MPT2SAS_ADAPTER {
u8 tm_sas_control_cb_idx;
struct _internal_cmd base_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;
@@ -773,7 +775,7 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid);
void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid);
void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr);
-dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc,
+__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc,
u16 smid);
/* hi-priority queue */
@@ -807,6 +809,8 @@ int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request);
void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type);
+void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc);
+
/* scsih shared API */
u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
u32 reply);
@@ -886,19 +890,22 @@ u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc,
Mpi2EventNotificationReply_t *mpi_reply);
+void mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc,
+ u8 bits_to_regsiter);
+
/* transport shared API */
u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
u32 reply);
struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc,
- u16 handle, u16 parent_handle);
+ u16 handle, u64 sas_address);
void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u16 parent_handle);
+ u64 sas_address_parent);
int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev);
-void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- u16 attached_handle, u8 phy_number, u8 link_rate);
+void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate);
extern struct sas_function_template mpt2sas_transport_functions;
extern struct scsi_transport_template *mpt2sas_transport_template;
extern int scsi_internal_device_block(struct scsi_device *sdev);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index 57d72463390..84a124f8e21 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -740,7 +740,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
Mpi2SCSIIORequest_t *scsiio_request =
(Mpi2SCSIIORequest_t *)mpi_request;
scsiio_request->SenseBufferLowAddress =
- (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+ mpt2sas_base_get_sense_buffer_dma(ioc, smid);
priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid);
memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE);
mpt2sas_base_put_smid_scsi_io(ioc, smid,
@@ -848,8 +848,9 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: "
"IOCStatus(0x%04x), IOCLogInfo(0x%08x), "
"TerminationCount(0x%08x)\n", ioc->name,
- tm_reply->IOCStatus, tm_reply->IOCLogInfo,
- tm_reply->TerminationCount);
+ le16_to_cpu(tm_reply->IOCStatus),
+ le32_to_cpu(tm_reply->IOCLogInfo),
+ le32_to_cpu(tm_reply->TerminationCount));
}
#endif
/* copy out xdata to user */
@@ -896,6 +897,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
printk(MPT2SAS_INFO_FMT "issue target reset: handle "
"= (0x%04x)\n", ioc->name,
mpi_request->FunctionDependent1);
+ mpt2sas_halt_firmware(ioc);
mutex_lock(&ioc->tm_cmds.mutex);
mpt2sas_scsih_issue_tm(ioc,
mpi_request->FunctionDependent1, 0,
@@ -1229,7 +1231,7 @@ _ctl_btdh_mapping(void __user *arg)
/**
* _ctl_diag_capability - return diag buffer capability
* @ioc: per adapter object
- * @buffer_type: specifies either TRACE or SNAPSHOT
+ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
*
* returns 1 when diag buffer support is enabled in firmware
*/
@@ -1249,24 +1251,25 @@ _ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type)
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 - application register with driver
- * @arg - user space buffer containing ioctl content
- * @state - NON_BLOCKING or BLOCKING
+ * _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
*
- * 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(void __user *arg, enum block_state state)
+_ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc,
+ struct mpt2_diag_register *diag_register)
{
- struct mpt2_diag_register karg;
- struct MPT2SAS_ADAPTER *ioc;
int rc, i;
void *request_data = NULL;
dma_addr_t request_data_dma;
@@ -1279,18 +1282,17 @@ _ctl_diag_register(void __user *arg, enum block_state state)
u16 ioc_status;
u8 issue_reset = 0;
- if (copy_from_user(&karg, arg, sizeof(karg))) {
- printk(KERN_ERR "failure at %s:%d/%s()!\n",
- __FILE__, __LINE__, __func__);
- return -EFAULT;
- }
- if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
- return -ENODEV;
-
dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
__func__));
- buffer_type = karg.buffer_type;
+ if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_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)) {
printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
"buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
@@ -1305,24 +1307,12 @@ _ctl_diag_register(void __user *arg, enum block_state state)
return -EINVAL;
}
- if (karg.requested_buffer_size % 4) {
+ if (diag_register->requested_buffer_size % 4) {
printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size "
"is not 4 byte aligned\n", ioc->name, __func__);
return -EINVAL;
}
- if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
- return -EAGAIN;
- else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
- return -ERESTARTSYS;
-
- if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
- printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
- ioc->name, __func__);
- rc = -EAGAIN;
- goto out;
- }
-
smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
if (!smid) {
printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
@@ -1338,12 +1328,12 @@ _ctl_diag_register(void __user *arg, enum block_state state)
ioc->ctl_cmds.smid = smid;
request_data = ioc->diag_buffer[buffer_type];
- request_data_sz = karg.requested_buffer_size;
- ioc->unique_id[buffer_type] = karg.unique_id;
+ 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], karg.product_specific,
- MPT2_PRODUCT_SPECIFIC_DWORDS);
- ioc->diagnostic_flags[buffer_type] = karg.diagnostic_flags;
+ memcpy(ioc->product_specific[buffer_type],
+ diag_register->product_specific, MPT2_PRODUCT_SPECIFIC_DWORDS);
+ ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags;
if (request_data) {
request_data_dma = ioc->diag_buffer_dma[buffer_type];
@@ -1373,8 +1363,8 @@ _ctl_diag_register(void __user *arg, enum block_state state)
}
mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
- mpi_request->BufferType = karg.buffer_type;
- mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags);
+ 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 */
@@ -1422,7 +1412,7 @@ _ctl_diag_register(void __user *arg, enum block_state state)
} else {
printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
"log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, mpi_reply->IOCLogInfo);
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
rc = -EFAULT;
}
@@ -1438,6 +1428,83 @@ _ctl_diag_register(void __user *arg, enum block_state state)
request_data, request_data_dma);
ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+ return rc;
+}
+
+/**
+ * mpt2sas_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
+mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register)
+{
+ struct mpt2_diag_register diag_register;
+
+ memset(&diag_register, 0, sizeof(struct mpt2_diag_register));
+
+ if (bits_to_register & 1) {
+ printk(MPT2SAS_INFO_FMT "registering trace buffer support\n",
+ ioc->name);
+ diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
+ /* register for 1MB buffers */
+ diag_register.requested_buffer_size = (1024 * 1024);
+ diag_register.unique_id = 0x7075900;
+ _ctl_diag_register_2(ioc, &diag_register);
+ }
+
+ if (bits_to_register & 2) {
+ printk(MPT2SAS_INFO_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) {
+ printk(MPT2SAS_INFO_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
+ * @arg - user space buffer containing ioctl content
+ * @state - NON_BLOCKING or BLOCKING
+ *
+ * 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(void __user *arg, enum block_state state)
+{
+ struct mpt2_diag_register karg;
+ struct MPT2SAS_ADAPTER *ioc;
+ long rc;
+
+ if (copy_from_user(&karg, arg, sizeof(karg))) {
+ printk(KERN_ERR "failure at %s:%d/%s()!\n",
+ __FILE__, __LINE__, __func__);
+ return -EFAULT;
+ }
+ if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+ return -ENODEV;
+
+ if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+ return -EAGAIN;
+ else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+ return -ERESTARTSYS;
+ rc = _ctl_diag_register_2(ioc, &karg);
mutex_unlock(&ioc->ctl_cmds.mutex);
return rc;
}
@@ -1600,7 +1667,7 @@ _ctl_diag_query(void __user *arg)
/**
* _ctl_send_release - Diag Release Message
* @ioc: per adapter object
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
* @issue_reset - specifies whether host reset is required.
*
*/
@@ -1690,7 +1757,7 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset)
} else {
printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
"log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, mpi_reply->IOCLogInfo);
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
rc = -EFAULT;
}
@@ -1951,7 +2018,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state)
} else {
printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
"log_info(0x%08x)\n", ioc->name, __func__,
- ioc_status, mpi_reply->IOCLogInfo);
+ ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
rc = -EFAULT;
}
@@ -2474,6 +2541,43 @@ _ctl_logging_level_store(struct device *cdev, struct device_attribute *attr,
static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
_ctl_logging_level_show, _ctl_logging_level_store);
+/* device attributes */
+/*
+ * _ctl_fwfault_debug_show - show/store fwfault_debug
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * mpt2sas_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 MPT2SAS_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 MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ int val = 0;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ ioc->fwfault_debug = val;
+ printk(MPT2SAS_INFO_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);
+
struct device_attribute *mpt2sas_host_attrs[] = {
&dev_attr_version_fw,
&dev_attr_version_bios,
@@ -2487,13 +2591,12 @@ struct device_attribute *mpt2sas_host_attrs[] = {
&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,
NULL,
};
-/* device attributes */
-
/**
* _ctl_device_sas_address_show - sas address
* @cdev - pointer to embedded class device
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
index 211f296dd19..8a5eeb1a5c8 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h
@@ -313,7 +313,7 @@ struct mpt2_ioctl_btdh_mapping {
* struct mpt2_diag_register - application register with driver
* @hdr - generic header
* @reserved -
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
* @application_flags - misc flags
* @diagnostic_flags - specifies flags affecting command processing
* @product_specific - product specific information
@@ -352,7 +352,7 @@ struct mpt2_diag_unregister {
* struct mpt2_diag_query - query relevant info associated with diag buffers
* @hdr - generic header
* @reserved -
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
* @application_flags - misc flags
* @diagnostic_flags - specifies flags affecting command processing
* @product_specific - product specific information
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 86ab32d7ab1..efabea1a3ce 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -76,6 +76,7 @@ static u8 tm_cb_idx = -1;
static u8 ctl_cb_idx = -1;
static u8 base_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;
@@ -196,10 +197,28 @@ static struct pci_device_id scsih_pci_table[] = {
PCI_ANY_ID, PCI_ANY_ID },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
PCI_ANY_ID, PCI_ANY_ID },
+ /* Meteor ~ 2116 */
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
PCI_ANY_ID, PCI_ANY_ID },
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
PCI_ANY_ID, PCI_ANY_ID },
+ /* Thunderbolt ~ 2208 */
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7,
+ PCI_ANY_ID, PCI_ANY_ID },
+ { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8,
+ PCI_ANY_ID, PCI_ANY_ID },
{0} /* Terminating entry */
};
MODULE_DEVICE_TABLE(pci, scsih_pci_table);
@@ -317,6 +336,47 @@ _scsih_is_boot_device(u64 sas_address, u64 device_name,
}
/**
+ * _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 MPT2SAS_ADAPTER *ioc, u16 handle,
+ u64 *sas_address)
+{
+ Mpi2SasDevicePage0_t sas_device_pg0;
+ Mpi2ConfigReply_t mpi_reply;
+ u32 ioc_status;
+
+ if (handle <= ioc->sas_hba.num_phys) {
+ *sas_address = ioc->sas_hba.sas_address;
+ return 0;
+ } else
+ *sas_address = 0;
+
+ if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+ printk(MPT2SAS_ERR_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) {
+ printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
+ "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
+ __FILE__, __LINE__, __func__);
+ return -EIO;
+ }
+
+ *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+ return 0;
+}
+
+/**
* _scsih_determine_boot_device - determine boot device.
* @ioc: per adapter object
* @device: either sas_device or raid_device object
@@ -510,8 +570,6 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
struct _sas_device *sas_device)
{
unsigned long flags;
- u16 handle, parent_handle;
- u64 sas_address;
dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle"
"(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
@@ -521,10 +579,8 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
list_add_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- handle = sas_device->handle;
- parent_handle = sas_device->parent_handle;
- sas_address = sas_device->sas_address;
- if (!mpt2sas_transport_port_add(ioc, handle, parent_handle))
+ if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent))
_scsih_sas_device_remove(ioc, sas_device);
}
@@ -553,31 +609,6 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc,
}
/**
- * mpt2sas_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 *
-mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_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;
-}
-
-/**
* _scsih_raid_device_find_by_id - raid device search
* @ioc: per adapter object
* @id: sas device target id
@@ -699,6 +730,31 @@ _scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc,
}
/**
+ * mpt2sas_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 *
+mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_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;
+}
+
+/**
* mpt2sas_scsih_expander_find_by_sas_address - expander device search
* @ioc: per adapter object
* @sas_address: sas address
@@ -1043,17 +1099,46 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
* _scsih_change_queue_depth - setting device queue depth
* @sdev: scsi device struct
* @qdepth: requested queue depth
+ * @reason: calling context
*
* Returns queue depth.
*/
static int
-_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
{
struct Scsi_Host *shost = sdev->host;
int max_depth;
int tag_type;
+ struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+ struct MPT2SAS_DEVICE *sas_device_priv_data;
+ struct MPT2SAS_TARGET *sas_target_priv_data;
+ struct _sas_device *sas_device;
+ unsigned long flags;
+
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
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 = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_device_priv_data->sas_target->sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ if (sas_device && sas_device->device_info &
+ MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+ max_depth = MPT2SAS_SATA_QUEUE_DEPTH;
+
+ not_sata:
+
if (!sdev->tagged_supported)
max_depth = 1;
if (qdepth > max_depth)
@@ -1488,7 +1573,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
r_level, raid_device->handle,
(unsigned long long)raid_device->wwid,
raid_device->num_pds, ds);
- _scsih_change_queue_depth(sdev, qdepth);
+ _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
return 0;
}
@@ -1534,7 +1619,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
_scsih_display_sata_capabilities(ioc, sas_device, sdev);
}
- _scsih_change_queue_depth(sdev, qdepth);
+ _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
if (ssp_target)
sas_read_port_mode_page(sdev);
@@ -1874,6 +1959,8 @@ _scsih_abort(struct scsi_cmnd *scmd)
goto out;
}
+ mpt2sas_halt_firmware(ioc);
+
mutex_lock(&ioc->tm_cmds.mutex);
handle = sas_device_priv_data->sas_target->handle;
mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun,
@@ -2297,7 +2384,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
u16 handle;
u16 reason_code;
u8 phy_number;
- u8 link_rate;
for (i = 0; i < event_data->NumEntries; i++) {
handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
@@ -2308,11 +2394,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
MPI2_EVENT_SAS_TOPO_RC_MASK;
if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
_scsih_block_io_device(ioc, handle);
- if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) {
- link_rate = event_data->PHY[i].LinkRate >> 4;
- if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
- _scsih_ublock_io_device(ioc, handle);
- }
}
}
@@ -2349,16 +2430,10 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
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);
- printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
- ioc->name, __func__);
- return;
- }
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
/* skip is hidden raid component */
- if (sas_device->hidden_raid_component)
+ if (sas_device && sas_device->hidden_raid_component)
return;
smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
@@ -2371,18 +2446,31 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL;
list_add_tail(&delayed_tr->list,
&ioc->delayed_tr_list);
- if (sas_device->starget)
+ if (sas_device && sas_device->starget) {
dewtprintk(ioc, starget_printk(KERN_INFO,
sas_device->starget, "DELAYED:tr:handle(0x%04x), "
- "(open)\n", sas_device->handle));
+ "(open)\n", handle));
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "DELAYED:tr:handle(0x%04x), (open)\n",
+ ioc->name, handle));
+ }
return;
}
- if (sas_device->starget && sas_device->starget->hostdata) {
- sas_target_priv_data = sas_device->starget->hostdata;
- sas_target_priv_data->tm_busy = 1;
- dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
- "tr:handle(0x%04x), (open)\n", sas_device->handle));
+ if (sas_device) {
+ sas_device->state |= MPTSAS_STATE_TR_SEND;
+ sas_device->state |= MPT2SAS_REQ_SAS_CNTRL;
+ if (sas_device->starget && sas_device->starget->hostdata) {
+ sas_target_priv_data = sas_device->starget->hostdata;
+ sas_target_priv_data->tm_busy = 1;
+ dewtprintk(ioc, starget_printk(KERN_INFO,
+ sas_device->starget, "tr:handle(0x%04x), (open)\n",
+ handle));
+ }
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "tr:handle(0x%04x), (open)\n", ioc->name, handle));
}
mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
@@ -2390,8 +2478,6 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
mpi_request->DevHandle = cpu_to_le16(handle);
mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
- sas_device->state |= MPTSAS_STATE_TR_SEND;
- sas_device->state |= MPT2SAS_REQ_SAS_CNTRL;
mpt2sas_base_put_smid_hi_priority(ioc, smid);
}
@@ -2426,21 +2512,25 @@ _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid,
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);
- printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
- ioc->name, __func__);
- return 1;
- }
- sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE;
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device->starget)
- dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
+ if (sas_device) {
+ sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE;
+ if (sas_device->starget)
+ dewtprintk(ioc, starget_printk(KERN_INFO,
+ sas_device->starget,
+ "sc_complete:handle(0x%04x), "
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ handle, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo)));
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
"sc_complete:handle(0x%04x), "
"ioc_status(0x%04x), loginfo(0x%08x)\n",
- handle, le16_to_cpu(mpi_reply->IOCStatus),
+ ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus),
le32_to_cpu(mpi_reply->IOCLogInfo)));
+ }
+
return 1;
}
@@ -2478,28 +2568,33 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
handle = le16_to_cpu(mpi_reply->DevHandle);
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);
- printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
- ioc->name, __func__);
- return 1;
- }
- sas_device->state |= MPTSAS_STATE_TR_COMPLETE;
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (sas_device->starget)
- dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
- "tr_complete:handle(0x%04x), (%s) ioc_status(0x%04x), "
- "loginfo(0x%08x), completed(%d)\n",
- sas_device->handle, (sas_device->state &
- MPT2SAS_REQ_SAS_CNTRL) ? "open" : "active",
- le16_to_cpu(mpi_reply->IOCStatus),
+ if (sas_device) {
+ sas_device->state |= MPTSAS_STATE_TR_COMPLETE;
+ if (sas_device->starget) {
+ dewtprintk(ioc, starget_printk(KERN_INFO,
+ sas_device->starget, "tr_complete:handle(0x%04x), "
+ "(%s) ioc_status(0x%04x), loginfo(0x%08x), "
+ "completed(%d)\n", sas_device->handle,
+ (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ?
+ "open" : "active",
+ le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo),
+ le32_to_cpu(mpi_reply->TerminationCount)));
+ if (sas_device->starget->hostdata) {
+ sas_target_priv_data =
+ sas_device->starget->hostdata;
+ sas_target_priv_data->tm_busy = 0;
+ }
+ }
+ } else {
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+ "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), "
+ "loginfo(0x%08x), completed(%d)\n", ioc->name,
+ handle, le16_to_cpu(mpi_reply->IOCStatus),
le32_to_cpu(mpi_reply->IOCLogInfo),
le32_to_cpu(mpi_reply->TerminationCount)));
-
- if (sas_device->starget && sas_device->starget->hostdata) {
- sas_target_priv_data = sas_device->starget->hostdata;
- sas_target_priv_data->tm_busy = 0;
}
if (!list_empty(&ioc->delayed_tr_list)) {
@@ -2514,8 +2609,7 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
} else
rc = 1;
-
- if (!(sas_device->state & MPT2SAS_REQ_SAS_CNTRL))
+ if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL))
return rc;
if (ioc->shost_recovery) {
@@ -2531,12 +2625,14 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
return rc;
}
+ if (sas_device)
+ sas_device->state |= MPTSAS_STATE_CNTRL_SEND;
+
mpi_request = mpt2sas_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_reply->DevHandle;
- sas_device->state |= MPTSAS_STATE_CNTRL_SEND;
mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl);
return rc;
}
@@ -2678,8 +2774,6 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
else
return;
- mpi_request->EEDPBlockSize = scmd->device->sector_size;
-
switch (prot_type) {
case SCSI_PROT_DIF_TYPE1:
@@ -2687,8 +2781,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
* enable ref/guard checking
* auto increment ref tag
*/
- mpi_request->EEDPFlags = eedp_flags |
- MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
mpi_request->CDB.EEDP32.PrimaryReferenceTag =
@@ -2701,11 +2794,11 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
/*
* enable guard checking
*/
- mpi_request->EEDPFlags = eedp_flags |
- MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
-
+ eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
break;
}
+ mpi_request->EEDPBlockSize = cpu_to_le32(scmd->device->sector_size);
+ mpi_request->EEDPFlags = cpu_to_le16(eedp_flags);
}
/**
@@ -2788,7 +2881,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
}
/* see if we are busy with task managment stuff */
- if (sas_target_priv_data->tm_busy)
+ if (sas_device_priv_data->block || sas_target_priv_data->tm_busy)
return SCSI_MLQUEUE_DEVICE_BUSY;
else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)
return SCSI_MLQUEUE_HOST_BUSY;
@@ -2842,7 +2935,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
mpi_request->SenseBufferLowAddress =
- (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+ mpt2sas_base_get_sense_buffer_dma(ioc, smid);
mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4;
mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI +
MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR);
@@ -2894,7 +2987,7 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
/**
- * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request
+ * _scsih_scsi_ioc_info - translated non-successfull SCSI_IO request
* @ioc: per adapter object
* @scmd: pointer to scsi command object
* @mpi_reply: reply mf payload returned from firmware
@@ -3059,7 +3152,7 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
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[3]);
+ _scsih_response_code(ioc, response_bytes[0]);
}
}
#endif
@@ -3177,7 +3270,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
u8 scsi_status;
u32 log_info;
struct MPT2SAS_DEVICE *sas_device_priv_data;
- u32 response_code;
+ u32 response_code = 0;
mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
scmd = _scsih_scsi_lookup_get(ioc, smid);
@@ -3199,16 +3292,16 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
}
/* 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 = (le32_to_cpu(mpi_reply->ResponseInfo)
- >> 24);
- if (response_code ==
- MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
- sas_device_priv_data->flags &=
- ~MPT_DEVICE_TLR_ON;
- }
+ 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);
@@ -3219,7 +3312,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
else
log_info = 0;
ioc_status &= MPI2_IOCSTATUS_MASK;
- scsi_state = mpi_reply->SCSIState;
scsi_status = mpi_reply->SCSIStatus;
if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
@@ -3255,10 +3347,9 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
if (sas_device_priv_data->block) {
- scmd->result = (DID_BUS_BUSY << 16);
- break;
+ scmd->result = DID_TRANSPORT_DISRUPTED << 16;
+ goto out;
}
-
case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
scmd->result = DID_RESET << 16;
@@ -3304,8 +3395,10 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
case MPI2_IOCSTATUS_SUCCESS:
scmd->result = (DID_OK << 16) | scsi_status;
- if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
- MPI2_SCSI_STATE_NO_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;
@@ -3344,7 +3437,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
/**
* _scsih_sas_host_refresh - refreshing sas host object contents
* @ioc: per adapter object
- * @update: update link information
* Context: user
*
* During port enable, fw will send topology events for every device. Its
@@ -3354,13 +3446,14 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
* Return nothing.
*/
static void
-_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
+_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc)
{
u16 sz;
u16 ioc_status;
int i;
Mpi2ConfigReply_t mpi_reply;
Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+ u16 attached_handle;
dtmprintk(ioc, printk(MPT2SAS_INFO_FMT
"updating handles for sas_host(0x%016llx)\n",
@@ -3374,27 +3467,24 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
ioc->name, __FILE__, __LINE__, __func__);
return;
}
- if (!(mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
- sas_iounit_pg0, sz))) {
- 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++) {
- ioc->sas_hba.phy[i].handle =
- le16_to_cpu(sas_iounit_pg0->PhyData[i].
- ControllerDevHandle);
- if (update)
- mpt2sas_transport_update_links(
- ioc,
- ioc->sas_hba.phy[i].handle,
- le16_to_cpu(sas_iounit_pg0->PhyData[i].
- AttachedDevHandle), i,
- sas_iounit_pg0->PhyData[i].
- NegotiatedLinkRate >> 4);
- }
- }
+ if ((mpt2sas_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++) {
+ 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);
+ mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address,
+ attached_handle, i, sas_iounit_pg0->PhyData[i].
+ NegotiatedLinkRate >> 4);
+ }
out:
kfree(sas_iounit_pg0);
}
@@ -3507,19 +3597,21 @@ _scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc)
ioc->name, __FILE__, __LINE__, __func__);
goto out;
}
- ioc->sas_hba.phy[i].handle =
- le16_to_cpu(sas_iounit_pg0->PhyData[i].ControllerDevHandle);
+
+ 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;
mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
phy_pg0, ioc->sas_hba.parent_dev);
}
if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
- MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.phy[0].handle))) {
+ MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
goto out;
}
- ioc->sas_hba.handle = le16_to_cpu(sas_device_pg0.DevHandle);
ioc->sas_hba.enclosure_handle =
le16_to_cpu(sas_device_pg0.EnclosureHandle);
ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
@@ -3562,7 +3654,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
Mpi2SasEnclosurePage0_t enclosure_pg0;
u32 ioc_status;
u16 parent_handle;
- __le64 sas_address;
+ __le64 sas_address, sas_address_parent = 0;
int i;
unsigned long flags;
struct _sas_port *mpt2sas_port = NULL;
@@ -3591,10 +3683,16 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
/* handle out of order topology events */
parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
- if (parent_handle >= ioc->sas_hba.num_phys) {
+ if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent)
+ != 0) {
+ printk(MPT2SAS_ERR_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 = mpt2sas_scsih_expander_find_by_handle(ioc,
- parent_handle);
+ sas_expander = mpt2sas_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);
@@ -3622,14 +3720,12 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
sas_expander->handle = handle;
sas_expander->num_phys = expander_pg0.NumPhys;
- sas_expander->parent_handle = parent_handle;
- sas_expander->enclosure_handle =
- le16_to_cpu(expander_pg0.EnclosureHandle);
+ sas_expander->sas_address_parent = sas_address_parent;
sas_expander->sas_address = sas_address;
printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x),"
" parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name,
- handle, sas_expander->parent_handle, (unsigned long long)
+ handle, parent_handle, (unsigned long long)
sas_expander->sas_address, sas_expander->num_phys);
if (!sas_expander->num_phys)
@@ -3645,7 +3741,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
INIT_LIST_HEAD(&sas_expander->sas_port_list);
mpt2sas_port = mpt2sas_transport_port_add(ioc, handle,
- sas_expander->parent_handle);
+ sas_address_parent);
if (!mpt2sas_port) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
@@ -3691,20 +3787,54 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
if (mpt2sas_port)
mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
- sas_expander->parent_handle);
+ sas_address_parent);
kfree(sas_expander);
return rc;
}
/**
+ * _scsih_done - scsih 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 message frames.
+ * 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 MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+ MPI2DefaultReply_t *mpi_reply;
+
+ mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
+ if (ioc->scsih_cmds.status == MPT2_CMD_NOT_USED)
+ return 1;
+ if (ioc->scsih_cmds.smid != smid)
+ return 1;
+ ioc->scsih_cmds.status |= MPT2_CMD_COMPLETE;
+ if (mpi_reply) {
+ memcpy(ioc->scsih_cmds.reply, mpi_reply,
+ mpi_reply->MsgLength*4);
+ ioc->scsih_cmds.status |= MPT2_CMD_REPLY_VALID;
+ }
+ ioc->scsih_cmds.status &= ~MPT2_CMD_PENDING;
+ complete(&ioc->scsih_cmds.done);
+ return 1;
+}
+
+/**
* _scsih_expander_remove - removing expander object
* @ioc: per adapter object
- * @handle: expander handle
+ * @sas_address: expander sas_address
*
* Return nothing.
*/
static void
-_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address)
{
struct _sas_node *sas_expander;
unsigned long flags;
@@ -3713,7 +3843,8 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
return;
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+ sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
_scsih_expander_node_remove(ioc, sas_expander);
}
@@ -3805,8 +3936,11 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
}
sas_device->handle = handle;
- sas_device->parent_handle =
- le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (_scsih_get_sas_address(ioc, le16_to_cpu
+ (sas_device_pg0.ParentDevHandle),
+ &sas_device->sas_address_parent) != 0)
+ printk(MPT2SAS_ERR_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 =
@@ -3836,43 +3970,39 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
/**
* _scsih_remove_device - removing sas device object
* @ioc: per adapter object
- * @handle: sas device handle
+ * @sas_device: the sas_device object
*
* Return nothing.
*/
static void
-_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device
+ *sas_device)
{
struct MPT2SAS_TARGET *sas_target_priv_data;
- struct _sas_device *sas_device;
- unsigned long flags;
Mpi2SasIoUnitControlReply_t mpi_reply;
Mpi2SasIoUnitControlRequest_t mpi_request;
- u16 device_handle;
+ u16 device_handle, handle;
- /* lookup sas_device */
- 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);
+ if (!sas_device)
return;
- }
- dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle"
- "(0x%04x)\n", ioc->name, __func__, handle));
+ handle = sas_device->handle;
+ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x),"
+ " sas_addr(0x%016llx)\n", ioc->name, __func__, 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;
}
- spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- if (ioc->remove_host)
+ if (ioc->remove_host || ioc->shost_recovery || !handle)
goto out;
if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) {
dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip "
- "target_reset handle(0x%04x)\n", ioc->name, handle));
+ "target_reset handle(0x%04x)\n", ioc->name,
+ handle));
goto skip_tr;
}
@@ -3925,10 +4055,10 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
_scsih_ublock_io_device(ioc, handle);
mpt2sas_transport_port_remove(ioc, sas_device->sas_address,
- sas_device->parent_handle);
+ sas_device->sas_address_parent);
printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr"
- "(0x%016llx)\n", ioc->name, sas_device->handle,
+ "(0x%016llx)\n", ioc->name, handle,
(unsigned long long) sas_device->sas_address);
_scsih_sas_device_remove(ioc, sas_device);
@@ -3952,7 +4082,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
u16 reason_code;
u8 phy_number;
char *status_str = NULL;
- char link_rate[25];
+ u8 link_rate, prev_link_rate;
switch (event_data->ExpStatus) {
case MPI2_EVENT_SAS_TOPO_ES_ADDED:
@@ -3962,6 +4092,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
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:
@@ -3987,30 +4118,30 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
MPI2_EVENT_SAS_TOPO_RC_MASK;
switch (reason_code) {
case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
- snprintf(link_rate, 25, ": add, link(0x%02x)",
- (event_data->PHY[i].LinkRate >> 4));
- status_str = link_rate;
+ status_str = "target add";
break;
case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
- status_str = ": remove";
+ status_str = "target remove";
break;
case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
- status_str = ": remove_delay";
+ status_str = "delay target remove";
break;
case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
- snprintf(link_rate, 25, ": link(0x%02x)",
- (event_data->PHY[i].LinkRate >> 4));
- status_str = link_rate;
+ status_str = "link rate change";
break;
case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
- status_str = ": responding";
+ status_str = "target responding";
break;
default:
- status_str = ": unknown";
+ status_str = "unknown";
break;
}
- printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x)%s\n",
- phy_number, handle, status_str);
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+ printk(KERN_DEBUG "\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
@@ -4031,8 +4162,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
u16 reason_code;
u8 phy_number;
struct _sas_node *sas_expander;
+ struct _sas_device *sas_device;
+ u64 sas_address;
unsigned long flags;
- u8 link_rate_;
+ u8 link_rate, prev_link_rate;
Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data;
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4040,10 +4173,13 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
_scsih_sas_topology_change_event_debug(ioc, event_data);
#endif
+ if (ioc->shost_recovery)
+ return;
+
if (!ioc->sas_hba.num_phys)
_scsih_sas_host_add(ioc);
else
- _scsih_sas_host_refresh(ioc, 0);
+ _scsih_sas_host_refresh(ioc);
if (fw_event->ignore) {
dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander "
@@ -4058,6 +4194,17 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
if (_scsih_expander_add(ioc, parent_handle) != 0)
return;
+ spin_lock_irqsave(&ioc->sas_node_lock, flags);
+ sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
+ parent_handle);
+ spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+ if (sas_expander)
+ sas_address = sas_expander->sas_address;
+ else if (parent_handle < ioc->sas_hba.num_phys)
+ sas_address = ioc->sas_hba.sas_address;
+ else
+ return;
+
/* handle siblings events */
for (i = 0; i < event_data->NumEntries; i++) {
if (fw_event->ignore) {
@@ -4077,48 +4224,47 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
if (!handle)
continue;
- link_rate_ = event_data->PHY[i].LinkRate >> 4;
+ 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 (link_rate == prev_link_rate)
+ break;
+
+ mpt2sas_transport_update_links(ioc, sas_address,
+ handle, phy_number, link_rate);
+
+ if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
+ _scsih_ublock_io_device(ioc, handle);
+ break;
case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
- if (!parent_handle) {
- if (phy_number < ioc->sas_hba.num_phys)
- mpt2sas_transport_update_links(
- ioc,
- ioc->sas_hba.phy[phy_number].handle,
- handle, phy_number, link_rate_);
- } else {
- spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_expander =
- mpt2sas_scsih_expander_find_by_handle(ioc,
- parent_handle);
- spin_unlock_irqrestore(&ioc->sas_node_lock,
- flags);
- if (sas_expander) {
- if (phy_number < sas_expander->num_phys)
- mpt2sas_transport_update_links(
- ioc,
- sas_expander->
- phy[phy_number].handle,
- handle, phy_number,
- link_rate_);
- }
- }
- if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) {
- if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5)
- break;
- _scsih_add_device(ioc, handle, phy_number, 0);
- }
+
+ mpt2sas_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_remove_device(ioc, handle);
+
+ 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);
+ break;
+ }
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+ _scsih_remove_device(ioc, sas_device);
break;
}
}
/* handle expander removal */
- if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
- _scsih_expander_remove(ioc, parent_handle);
+ if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING &&
+ sas_expander)
+ _scsih_expander_remove(ioc, sas_address);
}
@@ -4170,6 +4316,12 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
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;
@@ -4197,11 +4349,43 @@ static void
_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
+ struct MPT2SAS_TARGET *target_priv_data;
+ struct _sas_device *sas_device;
+ __le64 sas_address;
+ unsigned long flags;
+ Mpi2EventDataSasDeviceStatusChange_t *event_data =
+ fw_event->event_data;
+
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
_scsih_sas_device_status_change_event_debug(ioc,
- fw_event->event_data);
+ event_data);
#endif
+
+ 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 = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+ sas_address);
+ spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+ if (!sas_device || !sas_device->starget)
+ return;
+
+ target_priv_data = sas_device->starget->hostdata;
+ if (!target_priv_data)
+ 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;
}
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4281,6 +4465,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data;
#endif
+ u16 ioc_status;
dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: "
"phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum,
event_data->PortWidth));
@@ -4314,8 +4499,9 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
mpt2sas_scsih_issue_tm(ioc, handle, lun,
MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30);
ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
-
- if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) &&
+ ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+ & MPI2_IOCSTATUS_MASK;
+ if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) &&
(mpi_reply->ResponseCode ==
MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
mpi_reply->ResponseCode ==
@@ -4570,7 +4756,7 @@ _scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc,
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
if (!sas_device)
return;
- _scsih_remove_device(ioc, handle);
+ _scsih_remove_device(ioc, sas_device);
}
/**
@@ -4591,6 +4777,8 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
Mpi2ConfigReply_t mpi_reply;
Mpi2SasDevicePage0_t sas_device_pg0;
u32 ioc_status;
+ u64 sas_address;
+ u16 parent_handle;
spin_lock_irqsave(&ioc->sas_device_lock, flags);
sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
@@ -4615,9 +4803,10 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
return;
}
- mpt2sas_transport_update_links(ioc,
- le16_to_cpu(sas_device_pg0.ParentDevHandle),
- handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt2sas_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);
}
@@ -4857,7 +5046,7 @@ static void
_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
- u16 handle;
+ u16 handle, parent_handle;
u32 state;
struct _sas_device *sas_device;
unsigned long flags;
@@ -4865,6 +5054,7 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
Mpi2SasDevicePage0_t sas_device_pg0;
u32 ioc_status;
Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;
+ u64 sas_address;
if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
return;
@@ -4906,9 +5096,10 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
return;
}
- mpt2sas_transport_update_links(ioc,
- le16_to_cpu(sas_device_pg0.ParentDevHandle),
- handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+ parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+ if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+ mpt2sas_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);
@@ -4948,11 +5139,17 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc,
case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
reason_str = "consistency check";
break;
- default:
- reason_str = "unknown reason";
+ 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;
+
printk(MPT2SAS_INFO_FMT "raid operational status: (%s)"
"\thandle(0x%04x), percent complete(%d)\n",
ioc->name, reason_str,
@@ -5252,18 +5449,23 @@ _scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
{
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) {
- sas_expander->responding = 1;
- if (sas_expander->handle != handle) {
- printk(KERN_INFO "old handle(0x%04x)\n",
- sas_expander->handle);
- sas_expander->handle = handle;
- }
+ if (sas_expander->sas_address != sas_address)
+ continue;
+ sas_expander->responding = 1;
+ if (sas_expander->handle == handle)
goto out;
- }
+ printk(KERN_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);
@@ -5340,7 +5542,9 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
(unsigned long long)
sas_device->enclosure_logical_id,
sas_device->slot);
- _scsih_remove_device(ioc, sas_device->handle);
+ /* invalidate the device handle */
+ sas_device->handle = 0;
+ _scsih_remove_device(ioc, sas_device);
}
list_for_each_entry_safe(raid_device, raid_device_next,
@@ -5366,7 +5570,7 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
sas_expander->responding = 0;
continue;
}
- _scsih_expander_remove(ioc, sas_expander->handle);
+ _scsih_expander_remove(ioc, sas_expander->sas_address);
goto retry_expander_search;
}
}
@@ -5406,7 +5610,7 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
case MPT2_IOC_DONE_RESET:
dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
"MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
- _scsih_sas_host_refresh(ioc, 0);
+ _scsih_sas_host_refresh(ioc);
_scsih_search_responding_sas_devices(ioc);
_scsih_search_responding_raid_devices(ioc);
_scsih_search_responding_expanders(ioc);
@@ -5646,7 +5850,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
if (!sas_device)
continue;
- _scsih_remove_device(ioc, sas_device->handle);
+ _scsih_remove_device(ioc, sas_device);
if (ioc->shost_recovery)
return;
goto retry_device_search;
@@ -5669,7 +5873,8 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!expander_sibling)
continue;
- _scsih_expander_remove(ioc, expander_sibling->handle);
+ _scsih_expander_remove(ioc,
+ expander_sibling->sas_address);
if (ioc->shost_recovery)
return;
goto retry_expander_search;
@@ -5677,7 +5882,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
}
mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
- sas_expander->parent_handle);
+ sas_expander->sas_address_parent);
printk(MPT2SAS_INFO_FMT "expander_remove: handle"
"(0x%04x), sas_addr(0x%016llx)\n", ioc->name,
@@ -5690,9 +5895,99 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
}
/**
+ * _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 MPT2SAS_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 != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_ERR_FMT "%s: scsih_cmd in use\n",
+ ioc->name, __func__);
+ goto out;
+ }
+ ioc->scsih_cmds.status = MPT2_CMD_PENDING;
+
+ smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+ if (!smid) {
+ printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+ ioc->name, __func__);
+ ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+ goto out;
+ }
+
+ mpi_request = mpt2sas_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;
+
+ printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name);
+ init_completion(&ioc->scsih_cmds.done);
+ mpt2sas_base_put_smid_default(ioc, smid);
+ wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+ if (!(ioc->scsih_cmds.status & MPT2_CMD_COMPLETE)) {
+ printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+ ioc->name, __func__);
+ goto out;
+ }
+
+ if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) {
+ mpi_reply = ioc->scsih_cmds.reply;
+
+ printk(MPT2SAS_INFO_FMT "IR shutdown (complete): "
+ "ioc_status(0x%04x), loginfo(0x%08x)\n",
+ ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+ le32_to_cpu(mpi_reply->IOCLogInfo));
+ }
+
+ out:
+ ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+ mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+/**
+ * _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 MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+ _scsih_ir_shutdown(ioc);
+ mpt2sas_base_detach(ioc);
+}
+
+/**
* _scsih_remove - detach and remove add host
* @pdev: PCI device struct
*
+ * Routine called when unloading the driver.
* Return nothing.
*/
static void __devexit
@@ -5726,7 +6021,7 @@ _scsih_remove(struct pci_dev *pdev)
mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
mpt2sas_port->remote_identify.sas_address);
if (sas_device) {
- _scsih_remove_device(ioc, sas_device->handle);
+ _scsih_remove_device(ioc, sas_device);
goto retry_again;
}
} else {
@@ -5735,7 +6030,7 @@ _scsih_remove(struct pci_dev *pdev)
mpt2sas_port->remote_identify.sas_address);
if (expander_sibling) {
_scsih_expander_remove(ioc,
- expander_sibling->handle);
+ expander_sibling->sas_address);
goto retry_again;
}
}
@@ -5749,7 +6044,7 @@ _scsih_remove(struct pci_dev *pdev)
}
sas_remove_host(shost);
- mpt2sas_base_detach(ioc);
+ _scsih_shutdown(pdev);
list_del(&ioc->list);
scsi_remove_host(shost);
scsi_host_put(shost);
@@ -5770,7 +6065,8 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
void *device;
struct _sas_device *sas_device;
struct _raid_device *raid_device;
- u16 handle, parent_handle;
+ u16 handle;
+ u64 sas_address_parent;
u64 sas_address;
unsigned long flags;
int rc;
@@ -5799,17 +6095,17 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
} else {
sas_device = device;
handle = sas_device->handle;
- parent_handle = sas_device->parent_handle;
+ sas_address_parent = sas_device->sas_address_parent;
sas_address = sas_device->sas_address;
spin_lock_irqsave(&ioc->sas_device_lock, flags);
list_move_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
- sas_device->parent_handle)) {
+ sas_device->sas_address_parent)) {
_scsih_sas_device_remove(ioc, sas_device);
} else if (!sas_device->starget) {
mpt2sas_transport_port_remove(ioc, sas_address,
- parent_handle);
+ sas_address_parent);
_scsih_sas_device_remove(ioc, sas_device);
}
}
@@ -5849,8 +6145,6 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
{
struct _sas_device *sas_device, *next;
unsigned long flags;
- u16 handle, parent_handle;
- u64 sas_address;
/* SAS Device List */
list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
@@ -5859,14 +6153,13 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
list_move_tail(&sas_device->list, &ioc->sas_device_list);
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
- handle = sas_device->handle;
- parent_handle = sas_device->parent_handle;
- sas_address = sas_device->sas_address;
- if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) {
+ if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+ sas_device->sas_address_parent)) {
_scsih_sas_device_remove(ioc, sas_device);
} else if (!sas_device->starget) {
- mpt2sas_transport_port_remove(ioc, sas_address,
- parent_handle);
+ mpt2sas_transport_port_remove(ioc,
+ sas_device->sas_address,
+ sas_device->sas_address_parent);
_scsih_sas_device_remove(ioc, sas_device);
}
}
@@ -5935,6 +6228,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ioc->ctl_cb_idx = ctl_cb_idx;
ioc->base_cb_idx = base_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_sas_control_cb_idx = tm_sas_control_cb_idx;
@@ -6072,6 +6366,7 @@ static struct pci_driver scsih_driver = {
.id_table = scsih_pci_table,
.probe = _scsih_probe,
.remove = __devexit_p(_scsih_remove),
+ .shutdown = _scsih_shutdown,
#ifdef CONFIG_PM
.suspend = _scsih_suspend,
.resume = _scsih_resume,
@@ -6113,6 +6408,9 @@ _scsih_init(void)
transport_cb_idx = mpt2sas_base_register_callback_handler(
mpt2sas_transport_done);
+ /* scsih internal commands callback handler */
+ scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done);
+
/* configuration page API internal commands callback handler */
config_cb_idx = mpt2sas_base_register_callback_handler(
mpt2sas_config_done);
@@ -6152,6 +6450,7 @@ _scsih_exit(void)
mpt2sas_base_release_callback_handler(tm_cb_idx);
mpt2sas_base_release_callback_handler(base_cb_idx);
mpt2sas_base_release_callback_handler(transport_cb_idx);
+ mpt2sas_base_release_callback_handler(scsih_cb_idx);
mpt2sas_base_release_callback_handler(config_cb_idx);
mpt2sas_base_release_callback_handler(ctl_cb_idx);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c
index eb98188c7f3..3a82872bad4 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_transport.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c
@@ -59,24 +59,23 @@
#include "mpt2sas_base.h"
/**
- * _transport_sas_node_find_by_handle - sas node search
+ * _transport_sas_node_find_by_sas_address - sas node search
* @ioc: per adapter object
- * @handle: expander or hba handle (assigned by firmware)
+ * @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_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_transport_sas_node_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
+ u64 sas_address)
{
- int i;
-
- for (i = 0; i < ioc->sas_hba.num_phys; i++)
- if (ioc->sas_hba.phy[i].handle == handle)
- return &ioc->sas_hba;
-
- return mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+ if (ioc->sas_hba.sas_address == sas_address)
+ return &ioc->sas_hba;
+ else
+ return mpt2sas_scsih_expander_find_by_sas_address(ioc,
+ sas_address);
}
/**
@@ -259,8 +258,7 @@ struct rep_manu_reply{
u8 response_length;
u16 expander_change_count;
u8 reserved0[2];
- u8 sas_format:1;
- u8 reserved1:7;
+ u8 sas_format;
u8 reserved2[3];
u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
@@ -375,7 +373,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
mpi_request->VP_ID = 0;
sas_address_le = (u64 *)&mpi_request->SASAddress;
*sas_address_le = cpu_to_le64(sas_address);
- mpi_request->RequestDataLength = sizeof(struct rep_manu_request);
+ mpi_request->RequestDataLength =
+ cpu_to_le16(sizeof(struct rep_manu_request));
psge = &mpi_request->SGL;
/* WRITE sgel first */
@@ -438,8 +437,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
SAS_EXPANDER_PRODUCT_ID_LEN);
strncpy(edev->product_rev, manufacture_reply->product_rev,
SAS_EXPANDER_PRODUCT_REV_LEN);
- edev->level = manufacture_reply->sas_format;
- if (manufacture_reply->sas_format) {
+ 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);
@@ -469,7 +468,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
* mpt2sas_transport_port_add - insert port to the list
* @ioc: per adapter object
* @handle: handle of attached device
- * @parent_handle: parent handle(either hba or expander)
+ * @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.
@@ -478,7 +477,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
*/
struct _sas_port *
mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
- u16 parent_handle)
+ u64 sas_address)
{
struct _sas_phy *mpt2sas_phy, *next;
struct _sas_port *mpt2sas_port;
@@ -488,9 +487,6 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
int i;
struct sas_port *port;
- if (!parent_handle)
- return NULL;
-
mpt2sas_port = kzalloc(sizeof(struct _sas_port),
GFP_KERNEL);
if (!mpt2sas_port) {
@@ -502,17 +498,16 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
INIT_LIST_HEAD(&mpt2sas_port->port_list);
INIT_LIST_HEAD(&mpt2sas_port->phy_list);
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!sas_node) {
- printk(MPT2SAS_ERR_FMT "%s: Could not find parent(0x%04x)!\n",
- ioc->name, __func__, parent_handle);
+ printk(MPT2SAS_ERR_FMT "%s: Could not find "
+ "parent sas_address(0x%016llx)!\n", ioc->name,
+ __func__, (unsigned long long)sas_address);
goto out_fail;
}
- mpt2sas_port->handle = parent_handle;
- mpt2sas_port->sas_address = sas_node->sas_address;
if ((_transport_set_identify(ioc, handle,
&mpt2sas_port->remote_identify))) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
@@ -604,7 +599,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
* mpt2sas_transport_port_remove - remove port from the list
* @ioc: per adapter object
* @sas_address: sas address of attached device
- * @parent_handle: handle to the upstream parent(either hba or expander)
+ * @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
@@ -614,7 +609,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
*/
void
mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
- u16 parent_handle)
+ u64 sas_address_parent)
{
int i;
unsigned long flags;
@@ -624,7 +619,8 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
struct _sas_phy *mpt2sas_phy, *next_phy;
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc,
+ sas_address_parent);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!sas_node)
return;
@@ -650,8 +646,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
&mpt2sas_port->phy_list, port_siblings) {
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
dev_printk(KERN_INFO, &mpt2sas_port->port->dev,
- "remove: parent_handle(0x%04x), "
- "sas_addr(0x%016llx), phy(%d)\n", parent_handle,
+ "remove: sas_addr(0x%016llx), phy(%d)\n",
(unsigned long long)
mpt2sas_port->remote_identify.sas_address,
mpt2sas_phy->phy_id);
@@ -799,8 +794,8 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
/**
* mpt2sas_transport_update_links - refreshing phy link changes
* @ioc: per adapter object
- * @handle: handle to sas_host or expander
- * @attached_handle: attached device handle
+ * @sas_address: sas address of parent expander or sas host
+ * @handle: attached device handle
* @phy_numberv: phy number
* @link_rate: new link rate
*
@@ -808,28 +803,25 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*/
void
mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
- u16 handle, u16 attached_handle, u8 phy_number, u8 link_rate)
+ u64 sas_address, u16 handle, u8 phy_number, u8 link_rate)
{
unsigned long flags;
struct _sas_node *sas_node;
struct _sas_phy *mpt2sas_phy;
- if (ioc->shost_recovery) {
- printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
- __func__, ioc->name);
+ if (ioc->shost_recovery)
return;
- }
spin_lock_irqsave(&ioc->sas_node_lock, flags);
- sas_node = _transport_sas_node_find_by_handle(ioc, handle);
+ sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
if (!sas_node)
return;
mpt2sas_phy = &sas_node->phy[phy_number];
- mpt2sas_phy->attached_handle = attached_handle;
- if (attached_handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
- _transport_set_identify(ioc, mpt2sas_phy->attached_handle,
+ mpt2sas_phy->attached_handle = handle;
+ if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
+ _transport_set_identify(ioc, handle,
&mpt2sas_phy->remote_identify);
else
memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct
@@ -841,13 +833,11 @@ mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
- "refresh: handle(0x%04x), sas_addr(0x%016llx),\n"
+ "refresh: parent sas_addr(0x%016llx),\n"
"\tlink_rate(0x%02x), phy(%d)\n"
"\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
- handle, (unsigned long long)
- mpt2sas_phy->identify.sas_address, link_rate,
- phy_number, attached_handle,
- (unsigned long long)
+ (unsigned long long)sas_address,
+ link_rate, phy_number, handle, (unsigned long long)
mpt2sas_phy->remote_identify.sas_address);
}
@@ -1126,7 +1116,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_out) {
- mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
+ mpt2sas_base_free_smid(ioc, smid);
goto unmap;
}
@@ -1144,7 +1134,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
if (!dma_addr_in) {
- mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
+ mpt2sas_base_free_smid(ioc, smid);
goto unmap;
}
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index e3c482aa87b..a2d56982830 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -6495,7 +6495,7 @@ static void ncr_int_ma (struct ncb *np)
** we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids
** bloat for such a should_not_happen situation).
** In all other situation, we reset the BUS.
- ** Are these assumptions reasonnable ? (Wait and see ...)
+ ** Are these assumptions reasonable ? (Wait and see ...)
*/
unexpected_phase:
dsp -= 8;
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index 2be7d5b018d..2c98a6ee973 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -1419,7 +1419,7 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
nsp32_msg(KERN_ERR, "Received unexpected BMCNTERR IRQ! ");
/*
* TODO: To be implemented improving bus master
- * transfer reliablity when BMCNTERR is occurred in
+ * transfer reliability when BMCNTERR is occurred in
* AutoSCSI phase described in specification.
*/
}
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
index 7a117c18114..950202a70bc 100644
--- a/drivers/scsi/osd/osd_initiator.c
+++ b/drivers/scsi/osd/osd_initiator.c
@@ -73,7 +73,8 @@ static const char *_osd_ver_desc(struct osd_request *or)
#define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
-static int _osd_print_system_info(struct osd_dev *od, void *caps)
+static int _osd_get_print_system_info(struct osd_dev *od,
+ void *caps, struct osd_dev_info *odi)
{
struct osd_request *or;
struct osd_attr get_attrs[] = {
@@ -137,8 +138,12 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n",
(char *)pFirst);
- pFirst = get_attrs[a].val_ptr;
- OSD_INFO("OSD_NAME [%s]\n", (char *)pFirst);
+ odi->osdname_len = get_attrs[a].len;
+ /* Avoid NULL for memcmp optimization 0-length is good enough */
+ odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
+ if (odi->osdname_len)
+ memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
+ OSD_INFO("OSD_NAME [%s]\n", odi->osdname);
a++;
pFirst = get_attrs[a++].val_ptr;
@@ -171,6 +176,14 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
sid_dump, sizeof(sid_dump), true);
OSD_INFO("OSD_SYSTEM_ID(%d)\n"
" [%s]\n", len, sid_dump);
+
+ if (unlikely(len > sizeof(odi->systemid))) {
+ OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
+ "device idetification might not work\n", len);
+ len = sizeof(odi->systemid);
+ }
+ odi->systemid_len = len;
+ memcpy(odi->systemid, get_attrs[a].val_ptr, len);
a++;
}
out:
@@ -178,16 +191,17 @@ out:
return ret;
}
-int osd_auto_detect_ver(struct osd_dev *od, void *caps)
+int osd_auto_detect_ver(struct osd_dev *od,
+ void *caps, struct osd_dev_info *odi)
{
int ret;
/* Auto-detect the osd version */
- ret = _osd_print_system_info(od, caps);
+ ret = _osd_get_print_system_info(od, caps, odi);
if (ret) {
osd_dev_set_ver(od, OSD_VER1);
OSD_DEBUG("converting to OSD1\n");
- ret = _osd_print_system_info(od, caps);
+ ret = _osd_get_print_system_info(od, caps, odi);
}
return ret;
@@ -461,7 +475,8 @@ EXPORT_SYMBOL(osd_end_request);
int osd_execute_request(struct osd_request *or)
{
- return blk_execute_rq(or->request->q, NULL, or->request, 0);
+ return or->async_error =
+ blk_execute_rq(or->request->q, NULL, or->request, 0);
}
EXPORT_SYMBOL(osd_execute_request);
@@ -471,8 +486,12 @@ static void osd_request_async_done(struct request *req, int error)
or->async_error = error;
- if (error)
- OSD_DEBUG("osd_request_async_done error recieved %d\n", error);
+ if (unlikely(error)) {
+ OSD_DEBUG("osd_request_async_done error recieved %d "
+ "errors 0x%x\n", error, req->errors);
+ if (!req->errors) /* don't miss out on this one */
+ req->errors = error;
+ }
if (or->async_done)
or->async_done(or, or->async_private);
@@ -1153,6 +1172,7 @@ int osd_req_decode_get_attr_list(struct osd_request *or,
"c=%d r=%d n=%d\n",
cur_bytes, returned_bytes, n);
oa->val_ptr = NULL;
+ cur_bytes = returned_bytes; /* break the caller loop */
break;
}
@@ -1436,6 +1456,15 @@ int osd_finalize_request(struct osd_request *or,
}
EXPORT_SYMBOL(osd_finalize_request);
+static bool _is_osd_security_code(int code)
+{
+ return (code == osd_security_audit_value_frozen) ||
+ (code == osd_security_working_key_frozen) ||
+ (code == osd_nonce_not_unique) ||
+ (code == osd_nonce_timestamp_out_of_range) ||
+ (code == osd_invalid_dataout_buffer_integrity_check_value);
+}
+
#define OSD_SENSE_PRINT1(fmt, a...) \
do { \
if (__cur_sense_need_output) \
@@ -1458,9 +1487,16 @@ int osd_req_decode_sense_full(struct osd_request *or,
#else
bool __cur_sense_need_output = !silent;
#endif
+ int ret;
- if (!or->request->errors)
+ if (likely(!or->request->errors)) {
+ osi->out_resid = 0;
+ osi->in_resid = 0;
return 0;
+ }
+
+ osi = osi ? : &local_osi;
+ memset(osi, 0, sizeof(*osi));
ssdb = or->request->sense;
sense_len = or->request->sense_len;
@@ -1468,17 +1504,15 @@ int osd_req_decode_sense_full(struct osd_request *or,
OSD_ERR("Block-layer returned error(0x%x) but "
"sense_len(%u) || key(%d) is empty\n",
or->request->errors, sense_len, ssdb->sense_key);
- return -EIO;
+ goto analyze;
}
if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) {
OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n",
ssdb->response_code, sense_len);
- return -EIO;
+ goto analyze;
}
- osi = osi ? : &local_osi;
- memset(osi, 0, sizeof(*osi));
osi->key = ssdb->sense_key;
osi->additional_code = be16_to_cpu(ssdb->additional_sense_code);
original_sense_len = ssdb->additional_sense_length + 8;
@@ -1488,9 +1522,10 @@ int osd_req_decode_sense_full(struct osd_request *or,
__cur_sense_need_output = (osi->key > scsi_sk_recovered_error);
#endif
OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) "
- "additional_code=0x%x\n",
+ "additional_code=0x%x async_error=%d errors=0x%x\n",
osi->key, original_sense_len, sense_len,
- osi->additional_code);
+ osi->additional_code, or->async_error,
+ or->request->errors);
if (original_sense_len < sense_len)
sense_len = original_sense_len;
@@ -1569,15 +1604,14 @@ int osd_req_decode_sense_full(struct osd_request *or,
{
struct osd_sense_attributes_data_descriptor
*osadd = cur_descriptor;
- int len = min(cur_len, sense_len);
- int i = 0;
+ unsigned len = min(cur_len, sense_len);
struct osd_sense_attr *pattr = osadd->sense_attrs;
- while (len < 0) {
+ while (len >= sizeof(*pattr)) {
u32 attr_page = be32_to_cpu(pattr->attr_page);
u32 attr_id = be32_to_cpu(pattr->attr_id);
- if (i++ == 0) {
+ if (!osi->attr.attr_page) {
osi->attr.attr_page = attr_page;
osi->attr.attr_id = attr_id;
}
@@ -1588,6 +1622,8 @@ int osd_req_decode_sense_full(struct osd_request *or,
bad_attr_list++;
max_attr--;
}
+
+ len -= sizeof(*pattr);
OSD_SENSE_PRINT2(
"osd_sense_attribute_identification"
"attr_page=0x%x attr_id=0x%x\n",
@@ -1621,7 +1657,50 @@ int osd_req_decode_sense_full(struct osd_request *or,
cur_descriptor += cur_len;
}
- return (osi->key > scsi_sk_recovered_error) ? -EIO : 0;
+analyze:
+ if (!osi->key) {
+ /* scsi sense is Empty, the request was never issued to target
+ * linux return code might tell us what happened.
+ */
+ if (or->async_error == -ENOMEM)
+ osi->osd_err_pri = OSD_ERR_PRI_RESOURCE;
+ else
+ osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE;
+ ret = or->async_error;
+ } else if (osi->key <= scsi_sk_recovered_error) {
+ osi->osd_err_pri = 0;
+ ret = 0;
+ } else if (osi->additional_code == scsi_invalid_field_in_cdb) {
+ if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) {
+ osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES;
+ ret = -EFAULT; /* caller should recover from this */
+ } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) {
+ osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND;
+ ret = -ENOENT;
+ } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) {
+ osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS;
+ ret = -EACCES;
+ } else {
+ osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+ ret = -EINVAL;
+ }
+ } else if (osi->additional_code == osd_quota_error) {
+ osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE;
+ ret = -ENOSPC;
+ } else if (_is_osd_security_code(osi->additional_code)) {
+ osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+ ret = -EINVAL;
+ } else {
+ osi->osd_err_pri = OSD_ERR_PRI_EIO;
+ ret = -EIO;
+ }
+
+ if (or->out.req)
+ osi->out_resid = or->out.req->resid_len ?: or->out.total_bytes;
+ if (or->in.req)
+ osi->in_resid = or->in.req->resid_len ?: or->in.total_bytes;
+
+ return ret;
}
EXPORT_SYMBOL(osd_req_decode_sense_full);
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c
index 0bdef339090..0a90702b3d7 100644
--- a/drivers/scsi/osd/osd_uld.c
+++ b/drivers/scsi/osd/osd_uld.c
@@ -71,8 +71,7 @@
#define SCSI_OSD_MAX_MINOR 64
static const char osd_name[] = "osd";
-static const char *osd_version_string = "open-osd 0.1.0";
-const char osd_symlink[] = "scsi_osd";
+static const char *osd_version_string = "open-osd 0.2.0";
MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>");
MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko");
@@ -82,15 +81,25 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD);
struct osd_uld_device {
int minor;
- struct kref kref;
+ struct device class_dev;
struct cdev cdev;
struct osd_dev od;
+ struct osd_dev_info odi;
struct gendisk *disk;
- struct device *class_member;
};
-static void __uld_get(struct osd_uld_device *oud);
-static void __uld_put(struct osd_uld_device *oud);
+struct osd_dev_handle {
+ struct osd_dev od;
+ struct file *file;
+ struct osd_uld_device *oud;
+} ;
+
+static DEFINE_IDA(osd_minor_ida);
+
+static struct class osd_uld_class = {
+ .owner = THIS_MODULE,
+ .name = "scsi_osd",
+};
/*
* Char Device operations
@@ -101,7 +110,7 @@ static int osd_uld_open(struct inode *inode, struct file *file)
struct osd_uld_device *oud = container_of(inode->i_cdev,
struct osd_uld_device, cdev);
- __uld_get(oud);
+ get_device(&oud->class_dev);
/* cache osd_uld_device on file handle */
file->private_data = oud;
OSD_DEBUG("osd_uld_open %p\n", oud);
@@ -114,7 +123,7 @@ static int osd_uld_release(struct inode *inode, struct file *file)
OSD_DEBUG("osd_uld_release %p\n", file->private_data);
file->private_data = NULL;
- __uld_put(oud);
+ put_device(&oud->class_dev);
return 0;
}
@@ -177,7 +186,7 @@ static const struct file_operations osd_fops = {
struct osd_dev *osduld_path_lookup(const char *name)
{
struct osd_uld_device *oud;
- struct osd_dev *od;
+ struct osd_dev_handle *odh;
struct file *file;
int error;
@@ -186,8 +195,8 @@ struct osd_dev *osduld_path_lookup(const char *name)
return ERR_PTR(-EINVAL);
}
- od = kzalloc(sizeof(*od), GFP_KERNEL);
- if (!od)
+ odh = kzalloc(sizeof(*odh), GFP_KERNEL);
+ if (unlikely(!odh))
return ERR_PTR(-ENOMEM);
file = filp_open(name, O_RDWR, 0);
@@ -203,33 +212,134 @@ struct osd_dev *osduld_path_lookup(const char *name)
oud = file->private_data;
- *od = oud->od;
- od->file = file;
+ odh->od = oud->od;
+ odh->file = file;
+ odh->oud = oud;
- return od;
+ return &odh->od;
close_file:
fput(file);
free_od:
- kfree(od);
+ kfree(odh);
return ERR_PTR(error);
}
EXPORT_SYMBOL(osduld_path_lookup);
-void osduld_put_device(struct osd_dev *od)
+static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len,
+ const u8 *a2, unsigned a2_len)
{
+ if (!a2_len) /* User string is Empty means don't care */
+ return true;
+
+ if (a1_len != a2_len)
+ return false;
+
+ return 0 == memcmp(a1, a2, a1_len);
+}
+
+struct find_oud_t {
+ const struct osd_dev_info *odi;
+ struct device *dev;
+ struct osd_uld_device *oud;
+} ;
+
+int _mach_odi(struct device *dev, void *find_data)
+{
+ struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
+ class_dev);
+ struct find_oud_t *fot = find_data;
+ const struct osd_dev_info *odi = fot->odi;
+
+ if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+ odi->systemid, odi->systemid_len) &&
+ _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+ odi->osdname, odi->osdname_len)) {
+ OSD_DEBUG("found device sysid_len=%d osdname=%d\n",
+ odi->systemid_len, odi->osdname_len);
+ fot->oud = oud;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* osduld_info_lookup - Loop through all devices, return the requested osd_dev.
+ *
+ * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't
+ * care. .e.g if they're both zero /dev/osd0 is returned.
+ */
+struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi)
+{
+ struct find_oud_t find = {.odi = odi};
+
+ find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi);
+ if (likely(find.dev)) {
+ struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL);
+
+ if (unlikely(!odh)) {
+ put_device(find.dev);
+ return ERR_PTR(-ENOMEM);
+ }
+ odh->od = find.oud->od;
+ odh->oud = find.oud;
+
+ return &odh->od;
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL(osduld_info_lookup);
+
+void osduld_put_device(struct osd_dev *od)
+{
if (od && !IS_ERR(od)) {
- struct osd_uld_device *oud = od->file->private_data;
+ struct osd_dev_handle *odh =
+ container_of(od, struct osd_dev_handle, od);
+ struct osd_uld_device *oud = odh->oud;
BUG_ON(od->scsi_device != oud->od.scsi_device);
- fput(od->file);
- kfree(od);
+ /* If scsi has released the device (logout), and exofs has last
+ * reference on oud it will be freed by above osd_uld_release
+ * within fput below. But this will oops in cdev_release which
+ * is called after the fops->release. A get_/put_ pair makes
+ * sure we have a cdev for the duration of fput
+ */
+ if (odh->file) {
+ get_device(&oud->class_dev);
+ fput(odh->file);
+ }
+ put_device(&oud->class_dev);
+ kfree(odh);
}
}
EXPORT_SYMBOL(osduld_put_device);
+const struct osd_dev_info *osduld_device_info(struct osd_dev *od)
+{
+ struct osd_dev_handle *odh =
+ container_of(od, struct osd_dev_handle, od);
+ return &odh->oud->odi;
+}
+EXPORT_SYMBOL(osduld_device_info);
+
+bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi)
+{
+ struct osd_dev_handle *odh =
+ container_of(od, struct osd_dev_handle, od);
+ struct osd_uld_device *oud = odh->oud;
+
+ return (oud->odi.systemid_len == odi->systemid_len) &&
+ _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+ odi->systemid, odi->systemid_len) &&
+ (oud->odi.osdname_len == odi->osdname_len) &&
+ _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+ odi->osdname, odi->osdname_len);
+}
+EXPORT_SYMBOL(osduld_device_same);
+
/*
* Scsi Device operations
*/
@@ -250,14 +360,35 @@ static int __detect_osd(struct osd_uld_device *oud)
OSD_ERR("warning: scsi_test_unit_ready failed\n");
osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
- if (osd_auto_detect_ver(&oud->od, caps))
+ if (osd_auto_detect_ver(&oud->od, caps, &oud->odi))
return -ENODEV;
return 0;
}
-static struct class *osd_sysfs_class;
-static DEFINE_IDA(osd_minor_ida);
+static void __remove(struct device *dev)
+{
+ struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
+ class_dev);
+ struct scsi_device *scsi_device = oud->od.scsi_device;
+
+ kfree(oud->odi.osdname);
+
+ if (oud->cdev.owner)
+ cdev_del(&oud->cdev);
+
+ osd_dev_fini(&oud->od);
+ scsi_device_put(scsi_device);
+
+ OSD_INFO("osd_remove %s\n",
+ oud->disk ? oud->disk->disk_name : NULL);
+
+ if (oud->disk)
+ put_disk(oud->disk);
+ ida_remove(&osd_minor_ida, oud->minor);
+
+ kfree(oud);
+}
static int osd_probe(struct device *dev)
{
@@ -289,7 +420,6 @@ static int osd_probe(struct device *dev)
if (NULL == oud)
goto err_retract_minor;
- kref_init(&oud->kref);
dev_set_drvdata(dev, oud);
oud->minor = minor;
@@ -327,18 +457,25 @@ static int osd_probe(struct device *dev)
OSD_ERR("cdev_add failed\n");
goto err_put_disk;
}
- kobject_get(&oud->cdev.kobj); /* 2nd ref see osd_remove() */
-
- /* class_member */
- oud->class_member = device_create(osd_sysfs_class, dev,
- MKDEV(SCSI_OSD_MAJOR, oud->minor), "%s", disk->disk_name);
- if (IS_ERR(oud->class_member)) {
- OSD_ERR("class_device_create failed\n");
- error = PTR_ERR(oud->class_member);
+
+ /* class device member */
+ oud->class_dev.devt = oud->cdev.dev;
+ oud->class_dev.class = &osd_uld_class;
+ oud->class_dev.parent = dev;
+ oud->class_dev.release = __remove;
+ error = dev_set_name(&oud->class_dev, disk->disk_name);
+ if (error) {
+ OSD_ERR("dev_set_name failed => %d\n", error);
+ goto err_put_cdev;
+ }
+
+ error = device_register(&oud->class_dev);
+ if (error) {
+ OSD_ERR("device_register failed => %d\n", error);
goto err_put_cdev;
}
- dev_set_drvdata(oud->class_member, oud);
+ get_device(&oud->class_dev);
OSD_INFO("osd_probe %s\n", disk->disk_name);
return 0;
@@ -367,54 +504,12 @@ static int osd_remove(struct device *dev)
scsi_device);
}
- if (oud->class_member)
- device_destroy(osd_sysfs_class,
- MKDEV(SCSI_OSD_MAJOR, oud->minor));
+ device_unregister(&oud->class_dev);
- /* We have 2 references to the cdev. One is released here
- * and also takes down the /dev/osdX mapping. The second
- * Will be released in __remove() after all users have released
- * the osd_uld_device.
- */
- if (oud->cdev.owner)
- cdev_del(&oud->cdev);
-
- __uld_put(oud);
+ put_device(&oud->class_dev);
return 0;
}
-static void __remove(struct kref *kref)
-{
- struct osd_uld_device *oud = container_of(kref,
- struct osd_uld_device, kref);
- struct scsi_device *scsi_device = oud->od.scsi_device;
-
- /* now let delete the char_dev */
- kobject_put(&oud->cdev.kobj);
-
- osd_dev_fini(&oud->od);
- scsi_device_put(scsi_device);
-
- OSD_INFO("osd_remove %s\n",
- oud->disk ? oud->disk->disk_name : NULL);
-
- if (oud->disk)
- put_disk(oud->disk);
-
- ida_remove(&osd_minor_ida, oud->minor);
- kfree(oud);
-}
-
-static void __uld_get(struct osd_uld_device *oud)
-{
- kref_get(&oud->kref);
-}
-
-static void __uld_put(struct osd_uld_device *oud)
-{
- kref_put(&oud->kref, __remove);
-}
-
/*
* Global driver and scsi registration
*/
@@ -432,11 +527,10 @@ static int __init osd_uld_init(void)
{
int err;
- osd_sysfs_class = class_create(THIS_MODULE, osd_symlink);
- if (IS_ERR(osd_sysfs_class)) {
- OSD_ERR("Unable to register sysfs class => %ld\n",
- PTR_ERR(osd_sysfs_class));
- return PTR_ERR(osd_sysfs_class);
+ err = class_register(&osd_uld_class);
+ if (err) {
+ OSD_ERR("Unable to register sysfs class => %d\n", err);
+ return err;
}
err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0),
@@ -459,7 +553,7 @@ static int __init osd_uld_init(void)
err_out_chrdev:
unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
err_out:
- class_destroy(osd_sysfs_class);
+ class_unregister(&osd_uld_class);
return err;
}
@@ -467,7 +561,7 @@ static void __exit osd_uld_exit(void)
{
scsi_unregister_driver(&osd_driver.gendrv);
unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
- class_destroy(osd_sysfs_class);
+ class_unregister(&osd_uld_class);
OSD_INFO("UNLOADED %s\n", osd_version_string);
}
diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
index 67cde013806..528733b4a39 100644
--- a/drivers/scsi/pcmcia/aha152x_stub.c
+++ b/drivers/scsi/pcmcia/aha152x_stub.c
@@ -54,15 +54,6 @@
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"aha152x_cs.c 1.54 2000/06/12 21:27:25 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -103,7 +94,7 @@ static int aha152x_probe(struct pcmcia_device *link)
{
scsi_info_t *info;
- DEBUG(0, "aha152x_attach()\n");
+ dev_dbg(&link->dev, "aha152x_attach()\n");
/* Create new SCSI device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -115,7 +106,6 @@ static int aha152x_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
link->io.IOAddrLines = 10;
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.Present = PRESENT_OPTION;
@@ -127,7 +117,7 @@ static int aha152x_probe(struct pcmcia_device *link)
static void aha152x_detach(struct pcmcia_device *link)
{
- DEBUG(0, "aha152x_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "aha152x_detach\n");
aha152x_release_cs(link);
@@ -137,9 +127,6 @@ static void aha152x_detach(struct pcmcia_device *link)
/*====================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int aha152x_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -164,19 +151,22 @@ static int aha152x_config_cs(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
struct aha152x_setup s;
- int last_ret, last_fn;
+ int ret;
struct Scsi_Host *host;
- DEBUG(0, "aha152x_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "aha152x_config\n");
- last_ret = pcmcia_loop_config(link, aha152x_config_check, NULL);
- if (last_ret) {
- cs_error(link, RequestIO, last_ret);
- goto failed;
- }
+ ret = pcmcia_loop_config(link, aha152x_config_check, NULL);
+ if (ret)
+ goto failed;
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/* Set configuration options for the aha152x driver */
memset(&s, 0, sizeof(s));
@@ -194,7 +184,7 @@ static int aha152x_config_cs(struct pcmcia_device *link)
host = aha152x_probe_one(&s);
if (host == NULL) {
printk(KERN_INFO "aha152x_cs: no SCSI devices found\n");
- goto cs_failed;
+ goto failed;
}
sprintf(info->node.dev_name, "scsi%d", host->host_no);
@@ -203,8 +193,6 @@ static int aha152x_config_cs(struct pcmcia_device *link)
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
aha152x_release_cs(link);
return -ENODEV;
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
index 06254f46a0d..91404068407 100644
--- a/drivers/scsi/pcmcia/fdomain_stub.c
+++ b/drivers/scsi/pcmcia/fdomain_stub.c
@@ -59,16 +59,6 @@ MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
MODULE_LICENSE("Dual MPL/GPL");
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"fdomain_cs.c 1.47 2001/10/13 00:08:52 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
-
/*====================================================================*/
typedef struct scsi_info_t {
@@ -86,7 +76,7 @@ static int fdomain_probe(struct pcmcia_device *link)
{
scsi_info_t *info;
- DEBUG(0, "fdomain_attach()\n");
+ dev_dbg(&link->dev, "fdomain_attach()\n");
/* Create new SCSI device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -99,7 +89,6 @@ static int fdomain_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
link->io.IOAddrLines = 10;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.Present = PRESENT_OPTION;
@@ -111,7 +100,7 @@ static int fdomain_probe(struct pcmcia_device *link)
static void fdomain_detach(struct pcmcia_device *link)
{
- DEBUG(0, "fdomain_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "fdomain_detach\n");
fdomain_release(link);
@@ -120,9 +109,6 @@ static void fdomain_detach(struct pcmcia_device *link)
/*====================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int fdomain_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -137,20 +123,22 @@ static int fdomain_config_check(struct pcmcia_device *p_dev,
static int fdomain_config(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
- int last_ret, last_fn;
+ int ret;
char str[22];
struct Scsi_Host *host;
- DEBUG(0, "fdomain_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "fdomain_config\n");
- last_ret = pcmcia_loop_config(link, fdomain_config_check, NULL);
- if (last_ret) {
- cs_error(link, RequestIO, last_ret);
+ ret = pcmcia_loop_config(link, fdomain_config_check, NULL);
+ if (ret)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/* A bad hack... */
release_region(link->io.BasePort1, link->io.NumPorts1);
@@ -162,11 +150,11 @@ static int fdomain_config(struct pcmcia_device *link)
host = __fdomain_16x0_detect(&fdomain_driver_template);
if (!host) {
printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
- goto cs_failed;
+ goto failed;
}
if (scsi_add_host(host, NULL))
- goto cs_failed;
+ goto failed;
scsi_scan_host(host);
sprintf(info->node.dev_name, "scsi%d", host->host_no);
@@ -175,8 +163,6 @@ static int fdomain_config(struct pcmcia_device *link)
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
fdomain_release(link);
return -ENODEV;
@@ -188,7 +174,7 @@ static void fdomain_release(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
- DEBUG(0, "fdomain_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "fdomain_release\n");
scsi_remove_host(info->host);
pcmcia_disable_device(link);
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
index e32c344d7ad..c2341af587a 100644
--- a/drivers/scsi/pcmcia/nsp_cs.c
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -1564,12 +1564,10 @@ static int nsp_cs_probe(struct pcmcia_device *link)
link->io.IOAddrLines = 10; /* not used */
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
/* Interrupt handler */
link->irq.Handler = &nspintr;
- link->irq.Instance = info;
link->irq.Attributes |= IRQF_SHARED;
/* General socket configuration */
@@ -1684,10 +1682,10 @@ static int nsp_cs_config_check(struct pcmcia_device *p_dev,
if (cfg_mem->req.Size < 0x1000)
cfg_mem->req.Size = 0x1000;
cfg_mem->req.AccessSpeed = 0;
- if (pcmcia_request_window(&p_dev, &cfg_mem->req, &p_dev->win) != 0)
+ if (pcmcia_request_window(p_dev, &cfg_mem->req, &p_dev->win) != 0)
goto next_entry;
map.Page = 0; map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(p_dev->win, &map) != 0)
+ if (pcmcia_map_mem_page(p_dev, p_dev->win, &map) != 0)
goto next_entry;
cfg_mem->data->MmioAddress = (unsigned long) ioremap_nocache(cfg_mem->req.Base, cfg_mem->req.Size);
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
index 20c3e5e6d88..f85f094870b 100644
--- a/drivers/scsi/pcmcia/qlogic_stub.c
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -62,15 +62,6 @@
static char qlogic_name[] = "qlogic_cs";
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = "qlogic_cs.c 1.79-ac 2002/10/26 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
-
static struct scsi_host_template qlogicfas_driver_template = {
.module = THIS_MODULE,
.name = qlogic_name,
@@ -159,7 +150,7 @@ static int qlogic_probe(struct pcmcia_device *link)
{
scsi_info_t *info;
- DEBUG(0, "qlogic_attach()\n");
+ dev_dbg(&link->dev, "qlogic_attach()\n");
/* Create new SCSI device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -171,7 +162,6 @@ static int qlogic_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
link->io.IOAddrLines = 10;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.Present = PRESENT_OPTION;
@@ -183,7 +173,7 @@ static int qlogic_probe(struct pcmcia_device *link)
static void qlogic_detach(struct pcmcia_device *link)
{
- DEBUG(0, "qlogic_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "qlogic_detach\n");
qlogic_release(link);
kfree(link->priv);
@@ -192,9 +182,6 @@ static void qlogic_detach(struct pcmcia_device *link)
/*====================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int qlogic_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -213,19 +200,22 @@ static int qlogic_config_check(struct pcmcia_device *p_dev,
static int qlogic_config(struct pcmcia_device * link)
{
scsi_info_t *info = link->priv;
- int last_ret, last_fn;
+ int ret;
struct Scsi_Host *host;
- DEBUG(0, "qlogic_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "qlogic_config\n");
- last_ret = pcmcia_loop_config(link, qlogic_config_check, NULL);
- if (last_ret) {
- cs_error(link, RequestIO, last_ret);
+ ret = pcmcia_loop_config(link, qlogic_config_check, NULL);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
/* set ATAcmd */
@@ -244,7 +234,7 @@ static int qlogic_config(struct pcmcia_device * link)
if (!host) {
printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
- goto cs_failed;
+ goto failed;
}
sprintf(info->node.dev_name, "scsi%d", host->host_no);
@@ -253,12 +243,9 @@ static int qlogic_config(struct pcmcia_device * link)
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
- pcmcia_disable_device(link);
failed:
+ pcmcia_disable_device(link);
return -ENODEV;
-
} /* qlogic_config */
/*====================================================================*/
@@ -267,7 +254,7 @@ static void qlogic_release(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
- DEBUG(0, "qlogic_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "qlogic_release\n");
scsi_remove_host(info->host);
diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c
index b330c11a175..e7564d8f0cb 100644
--- a/drivers/scsi/pcmcia/sym53c500_cs.c
+++ b/drivers/scsi/pcmcia/sym53c500_cs.c
@@ -77,17 +77,6 @@
#include <pcmcia/ds.h>
#include <pcmcia/ciscode.h>
-/* ================================================================== */
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"sym53c500_cs.c 0.9c 2004/10/27 (Bob Tracy)";
-#else
-#define DEBUG(n, args...)
-#endif
/* ================================================================== */
@@ -525,7 +514,7 @@ SYM53C500_release(struct pcmcia_device *link)
struct scsi_info_t *info = link->priv;
struct Scsi_Host *shost = info->host;
- DEBUG(0, "SYM53C500_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "SYM53C500_release\n");
/*
* Do this before releasing/freeing resources.
@@ -697,9 +686,6 @@ static struct scsi_host_template sym53c500_driver_template = {
.shost_attrs = SYM53C500_shost_attrs
};
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int SYM53C500_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cfg,
cistpl_cftable_entry_t *dflt,
@@ -719,24 +705,27 @@ static int
SYM53C500_config(struct pcmcia_device *link)
{
struct scsi_info_t *info = link->priv;
- int last_ret, last_fn;
+ int ret;
int irq_level, port_base;
struct Scsi_Host *host;
struct scsi_host_template *tpnt = &sym53c500_driver_template;
struct sym53c500_data *data;
- DEBUG(0, "SYM53C500_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "SYM53C500_config\n");
info->manf_id = link->manf_id;
- last_ret = pcmcia_loop_config(link, SYM53C500_config_check, NULL);
- if (last_ret) {
- cs_error(link, RequestIO, last_ret);
+ ret = pcmcia_loop_config(link, SYM53C500_config_check, NULL);
+ if (ret)
+ goto failed;
+
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
goto failed;
- }
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
* That's the trouble with copying liberally from another driver.
@@ -824,8 +813,6 @@ err_release:
printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
return -ENODEV;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
SYM53C500_release(link);
return -ENODEV;
@@ -855,7 +842,7 @@ static int sym53c500_resume(struct pcmcia_device *link)
static void
SYM53C500_detach(struct pcmcia_device *link)
{
- DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "SYM53C500_detach\n");
SYM53C500_release(link);
@@ -868,7 +855,7 @@ SYM53C500_probe(struct pcmcia_device *link)
{
struct scsi_info_t *info;
- DEBUG(0, "SYM53C500_attach()\n");
+ dev_dbg(&link->dev, "SYM53C500_attach()\n");
/* Create new SCSI device */
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -880,7 +867,6 @@ SYM53C500_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
link->io.IOAddrLines = 10;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
diff --git a/drivers/scsi/pm8001/Makefile b/drivers/scsi/pm8001/Makefile
new file mode 100644
index 00000000000..52f04296171
--- /dev/null
+++ b/drivers/scsi/pm8001/Makefile
@@ -0,0 +1,12 @@
+#
+# Kernel configuration file for the PM8001 SAS/SATA 8x6G based HBA driver
+#
+# Copyright (C) 2008-2009 USI Co., Ltd.
+
+
+obj-$(CONFIG_SCSI_PM8001) += pm8001.o
+pm8001-y += pm8001_init.o \
+ pm8001_sas.o \
+ pm8001_ctl.o \
+ pm8001_hwi.o
+
diff --git a/drivers/scsi/pm8001/pm8001_chips.h b/drivers/scsi/pm8001/pm8001_chips.h
new file mode 100644
index 00000000000..4efa4d0950e
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_chips.h
@@ -0,0 +1,89 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_CHIPS_H_
+#define _PM8001_CHIPS_H_
+
+static inline u32 pm8001_read_32(void *virt_addr)
+{
+ return *((u32 *)virt_addr);
+}
+
+static inline void pm8001_write_32(void *addr, u32 offset, u32 val)
+{
+ *((u32 *)(addr + offset)) = val;
+}
+
+static inline u32 pm8001_cr32(struct pm8001_hba_info *pm8001_ha, u32 bar,
+ u32 offset)
+{
+ return readl(pm8001_ha->io_mem[bar].memvirtaddr + offset);
+}
+
+static inline void pm8001_cw32(struct pm8001_hba_info *pm8001_ha, u32 bar,
+ u32 addr, u32 val)
+{
+ writel(val, pm8001_ha->io_mem[bar].memvirtaddr + addr);
+}
+static inline u32 pm8001_mr32(void __iomem *addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+static inline void pm8001_mw32(void __iomem *addr, u32 offset, u32 val)
+{
+ writel(val, addr + offset);
+}
+static inline u32 get_pci_bar_index(u32 pcibar)
+{
+ switch (pcibar) {
+ case 0x18:
+ case 0x1C:
+ return 1;
+ case 0x20:
+ return 2;
+ case 0x24:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+#endif /* _PM8001_CHIPS_H_ */
+
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
new file mode 100644
index 00000000000..14b13acae6d
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -0,0 +1,573 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#include <linux/firmware.h>
+#include "pm8001_sas.h"
+#include "pm8001_ctl.h"
+
+/* scsi host attributes */
+
+/**
+ * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ pm8001_ha->main_cfg_tbl.interface_rev);
+}
+static
+DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL);
+
+/**
+ * pm8001_ctl_fw_version_show - firmware version
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_fw_version_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n",
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 24),
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 16),
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 8),
+ (u8)(pm8001_ha->main_cfg_tbl.firmware_rev));
+}
+static DEVICE_ATTR(fw_version, S_IRUGO, pm8001_ctl_fw_version_show, NULL);
+/**
+ * pm8001_ctl_max_out_io_show - max outstanding io supported
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ pm8001_ha->main_cfg_tbl.max_out_io);
+}
+static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL);
+/**
+ * pm8001_ctl_max_devices_show - max devices support
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_devices_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%04d\n",
+ (u16)(pm8001_ha->main_cfg_tbl.max_sgl >> 16));
+}
+static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL);
+/**
+ * pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no
+ * hardware limitation
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%04d\n",
+ pm8001_ha->main_cfg_tbl.max_sgl & 0x0000FFFF);
+}
+static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL);
+
+#define SAS_1_0 0x1
+#define SAS_1_1 0x2
+#define SAS_2_0 0x4
+
+static ssize_t
+show_sas_spec_support_status(unsigned int mode, char *buf)
+{
+ ssize_t len = 0;
+
+ if (mode & SAS_1_1)
+ len = sprintf(buf, "%s", "SAS1.1");
+ if (mode & SAS_2_0)
+ len += sprintf(buf + len, "%s%s", len ? ", " : "", "SAS2.0");
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+/**
+ * pm8001_ctl_sas_spec_support_show - sas spec supported
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_sas_spec_support_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int mode;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ mode = (pm8001_ha->main_cfg_tbl.ctrl_cap_flag & 0xfe000000)>>25;
+ return show_sas_spec_support_status(mode, buf);
+}
+static DEVICE_ATTR(sas_spec_support, S_IRUGO,
+ pm8001_ctl_sas_spec_support_show, NULL);
+
+/**
+ * pm8001_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 pm8001_ctl_host_sas_address_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+ be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr));
+}
+static DEVICE_ATTR(host_sas_address, S_IRUGO,
+ pm8001_ctl_host_sas_address_show, NULL);
+
+/**
+ * pm8001_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 pm8001_ctl_logging_level_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ return snprintf(buf, PAGE_SIZE, "%08xh\n", pm8001_ha->logging_level);
+}
+static ssize_t pm8001_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 sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ int val = 0;
+
+ if (sscanf(buf, "%x", &val) != 1)
+ return -EINVAL;
+
+ pm8001_ha->logging_level = val;
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
+ pm8001_ctl_logging_level_show, pm8001_ctl_logging_level_store);
+/**
+ * pm8001_ctl_aap_log_show - aap1 event log
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_aap_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ int i;
+#define AAP1_MEMMAP(r, c) \
+ (*(u32 *)((u8*)pm8001_ha->memoryMap.region[AAP1].virt_ptr + (r) * 32 \
+ + (c)))
+
+ char *str = buf;
+ int max = 2;
+ for (i = 0; i < max; i++) {
+ str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
+ "0x%08x 0x%08x\n",
+ AAP1_MEMMAP(i, 0),
+ AAP1_MEMMAP(i, 4),
+ AAP1_MEMMAP(i, 8),
+ AAP1_MEMMAP(i, 12),
+ AAP1_MEMMAP(i, 16),
+ AAP1_MEMMAP(i, 20),
+ AAP1_MEMMAP(i, 24),
+ AAP1_MEMMAP(i, 28));
+ }
+
+ return str - buf;
+}
+static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL);
+/**
+ * pm8001_ctl_aap_log_show - IOP event log
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_iop_log_show(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+#define IOP_MEMMAP(r, c) \
+ (*(u32 *)((u8*)pm8001_ha->memoryMap.region[IOP].virt_ptr + (r) * 32 \
+ + (c)))
+ int i;
+ char *str = buf;
+ int max = 2;
+ for (i = 0; i < max; i++) {
+ str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
+ "0x%08x 0x%08x\n",
+ IOP_MEMMAP(i, 0),
+ IOP_MEMMAP(i, 4),
+ IOP_MEMMAP(i, 8),
+ IOP_MEMMAP(i, 12),
+ IOP_MEMMAP(i, 16),
+ IOP_MEMMAP(i, 20),
+ IOP_MEMMAP(i, 24),
+ IOP_MEMMAP(i, 28));
+ }
+
+ return str - buf;
+}
+static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL);
+
+#define FLASH_CMD_NONE 0x00
+#define FLASH_CMD_UPDATE 0x01
+#define FLASH_CMD_SET_NVMD 0x02
+
+struct flash_command {
+ u8 command[8];
+ int code;
+};
+
+static struct flash_command flash_command_table[] =
+{
+ {"set_nvmd", FLASH_CMD_SET_NVMD},
+ {"update", FLASH_CMD_UPDATE},
+ {"", FLASH_CMD_NONE} /* Last entry should be NULL. */
+};
+
+struct error_fw {
+ char *reason;
+ int err_code;
+};
+
+static struct error_fw flash_error_table[] =
+{
+ {"Failed to open fw image file", FAIL_OPEN_BIOS_FILE},
+ {"image header mismatch", FLASH_UPDATE_HDR_ERR},
+ {"image offset mismatch", FLASH_UPDATE_OFFSET_ERR},
+ {"image CRC Error", FLASH_UPDATE_CRC_ERR},
+ {"image length Error.", FLASH_UPDATE_LENGTH_ERR},
+ {"Failed to program flash chip", FLASH_UPDATE_HW_ERR},
+ {"Flash chip not supported.", FLASH_UPDATE_DNLD_NOT_SUPPORTED},
+ {"Flash update disabled.", FLASH_UPDATE_DISABLED},
+ {"Flash in progress", FLASH_IN_PROGRESS},
+ {"Image file size Error", FAIL_FILE_SIZE},
+ {"Input parameter error", FAIL_PARAMETERS},
+ {"Out of memory", FAIL_OUT_MEMORY},
+ {"OK", 0} /* Last entry err_code = 0. */
+};
+
+static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pm8001_ioctl_payload *payload;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u8 *ioctlbuffer = NULL;
+ u32 length = 0;
+ u32 ret = 0;
+
+ length = 1024 * 5 + sizeof(*payload) - 1;
+ ioctlbuffer = kzalloc(length, GFP_KERNEL);
+ if (!ioctlbuffer)
+ return -ENOMEM;
+ if ((pm8001_ha->fw_image->size <= 0) ||
+ (pm8001_ha->fw_image->size > 4096)) {
+ ret = FAIL_FILE_SIZE;
+ goto out;
+ }
+ payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
+ memcpy((u8 *)payload->func_specific, (u8 *)pm8001_ha->fw_image->data,
+ pm8001_ha->fw_image->size);
+ payload->length = pm8001_ha->fw_image->size;
+ payload->id = 0;
+ pm8001_ha->nvmd_completion = &completion;
+ ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload);
+ wait_for_completion(&completion);
+out:
+ kfree(ioctlbuffer);
+ return ret;
+}
+
+static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pm8001_ioctl_payload *payload;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u8 *ioctlbuffer = NULL;
+ u32 length = 0;
+ struct fw_control_info *fwControl;
+ u32 loopNumber, loopcount = 0;
+ u32 sizeRead = 0;
+ u32 partitionSize, partitionSizeTmp;
+ u32 ret = 0;
+ u32 partitionNumber = 0;
+ struct pm8001_fw_image_header *image_hdr;
+
+ length = 1024 * 16 + sizeof(*payload) - 1;
+ ioctlbuffer = kzalloc(length, GFP_KERNEL);
+ image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data;
+ if (!ioctlbuffer)
+ return -ENOMEM;
+ if (pm8001_ha->fw_image->size < 28) {
+ ret = FAIL_FILE_SIZE;
+ goto out;
+ }
+
+ while (sizeRead < pm8001_ha->fw_image->size) {
+ partitionSizeTmp =
+ *(u32 *)((u8 *)&image_hdr->image_length + sizeRead);
+ partitionSize = be32_to_cpu(partitionSizeTmp);
+ loopcount = (partitionSize + HEADER_LEN)/IOCTL_BUF_SIZE;
+ if (loopcount % IOCTL_BUF_SIZE)
+ loopcount++;
+ if (loopcount == 0)
+ loopcount++;
+ for (loopNumber = 0; loopNumber < loopcount; loopNumber++) {
+ payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
+ payload->length = 1024*16;
+ payload->id = 0;
+ fwControl =
+ (struct fw_control_info *)payload->func_specific;
+ fwControl->len = IOCTL_BUF_SIZE; /* IN */
+ fwControl->size = partitionSize + HEADER_LEN;/* IN */
+ fwControl->retcode = 0;/* OUT */
+ fwControl->offset = loopNumber * IOCTL_BUF_SIZE;/*OUT */
+
+ /* for the last chunk of data in case file size is not even with
+ 4k, load only the rest*/
+ if (((loopcount-loopNumber) == 1) &&
+ ((partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE)) {
+ fwControl->len =
+ (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
+ memcpy((u8 *)fwControl->buffer,
+ (u8 *)pm8001_ha->fw_image->data + sizeRead,
+ (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE);
+ sizeRead +=
+ (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
+ } else {
+ memcpy((u8 *)fwControl->buffer,
+ (u8 *)pm8001_ha->fw_image->data + sizeRead,
+ IOCTL_BUF_SIZE);
+ sizeRead += IOCTL_BUF_SIZE;
+ }
+
+ pm8001_ha->nvmd_completion = &completion;
+ ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload);
+ wait_for_completion(&completion);
+ if (ret || (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS)) {
+ ret = fwControl->retcode;
+ kfree(ioctlbuffer);
+ ioctlbuffer = NULL;
+ break;
+ }
+ }
+ if (ret)
+ break;
+ partitionNumber++;
+}
+out:
+ kfree(ioctlbuffer);
+ return ret;
+}
+static ssize_t pm8001_store_update_fw(struct device *cdev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ char *cmd_ptr, *filename_ptr;
+ int res, i;
+ int flash_command = FLASH_CMD_NONE;
+ int err = 0;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+
+ if (!cmd_ptr) {
+ err = FAIL_OUT_MEMORY;
+ goto out;
+ }
+
+ filename_ptr = cmd_ptr + count;
+ res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr);
+ if (res != 2) {
+ err = FAIL_PARAMETERS;
+ goto out1;
+ }
+
+ for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
+ if (!memcmp(flash_command_table[i].command,
+ cmd_ptr, strlen(cmd_ptr))) {
+ flash_command = flash_command_table[i].code;
+ break;
+ }
+ }
+ if (flash_command == FLASH_CMD_NONE) {
+ err = FAIL_PARAMETERS;
+ goto out1;
+ }
+
+ if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) {
+ err = FLASH_IN_PROGRESS;
+ goto out1;
+ }
+ err = request_firmware(&pm8001_ha->fw_image,
+ filename_ptr,
+ pm8001_ha->dev);
+
+ if (err) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Failed to load firmware image file %s,"
+ " error %d\n", filename_ptr, err));
+ err = FAIL_OPEN_BIOS_FILE;
+ goto out1;
+ }
+
+ switch (flash_command) {
+ case FLASH_CMD_UPDATE:
+ pm8001_ha->fw_status = FLASH_IN_PROGRESS;
+ err = pm8001_update_flash(pm8001_ha);
+ break;
+ case FLASH_CMD_SET_NVMD:
+ pm8001_ha->fw_status = FLASH_IN_PROGRESS;
+ err = pm8001_set_nvmd(pm8001_ha);
+ break;
+ default:
+ pm8001_ha->fw_status = FAIL_PARAMETERS;
+ err = FAIL_PARAMETERS;
+ break;
+ }
+ release_firmware(pm8001_ha->fw_image);
+out1:
+ kfree(cmd_ptr);
+out:
+ pm8001_ha->fw_status = err;
+
+ if (!err)
+ return count;
+ else
+ return -err;
+}
+
+static ssize_t pm8001_show_update_fw(struct device *cdev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+ for (i = 0; flash_error_table[i].err_code != 0; i++) {
+ if (flash_error_table[i].err_code == pm8001_ha->fw_status)
+ break;
+ }
+ if (pm8001_ha->fw_status != FLASH_IN_PROGRESS)
+ pm8001_ha->fw_status = FLASH_OK;
+
+ return snprintf(buf, PAGE_SIZE, "status=%x %s\n",
+ flash_error_table[i].err_code,
+ flash_error_table[i].reason);
+}
+
+static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUGO,
+ pm8001_show_update_fw, pm8001_store_update_fw);
+struct device_attribute *pm8001_host_attrs[] = {
+ &dev_attr_interface_rev,
+ &dev_attr_fw_version,
+ &dev_attr_update_fw,
+ &dev_attr_aap_log,
+ &dev_attr_iop_log,
+ &dev_attr_max_out_io,
+ &dev_attr_max_devices,
+ &dev_attr_max_sg_list,
+ &dev_attr_sas_spec_support,
+ &dev_attr_logging_level,
+ &dev_attr_host_sas_address,
+ NULL,
+};
+
diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h
new file mode 100644
index 00000000000..22644de2639
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_ctl.h
@@ -0,0 +1,67 @@
+ /*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef PM8001_CTL_H_INCLUDED
+#define PM8001_CTL_H_INCLUDED
+
+#define IOCTL_BUF_SIZE 4096
+#define HEADER_LEN 28
+#define SIZE_OFFSET 16
+
+struct pm8001_ioctl_payload {
+ u32 signature;
+ u16 major_function;
+ u16 minor_function;
+ u16 length;
+ u16 status;
+ u16 offset;
+ u16 id;
+ u8 func_specific[1];
+};
+
+#define FLASH_OK 0x000000
+#define FAIL_OPEN_BIOS_FILE 0x000100
+#define FAIL_FILE_SIZE 0x000a00
+#define FAIL_PARAMETERS 0x000b00
+#define FAIL_OUT_MEMORY 0x000c00
+#define FLASH_IN_PROGRESS 0x001000
+
+#endif /* PM8001_CTL_H_INCLUDED */
+
diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h
new file mode 100644
index 00000000000..944afada61e
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_defs.h
@@ -0,0 +1,112 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_DEFS_H_
+#define _PM8001_DEFS_H_
+
+enum chip_flavors {
+ chip_8001,
+};
+#define USI_MAX_MEMCNT 9
+#define PM8001_MAX_DMA_SG SG_ALL
+enum phy_speed {
+ PHY_SPEED_15 = 0x01,
+ PHY_SPEED_30 = 0x02,
+ PHY_SPEED_60 = 0x04,
+};
+
+enum data_direction {
+ DATA_DIR_NONE = 0x0, /* NO TRANSFER */
+ DATA_DIR_IN = 0x01, /* INBOUND */
+ DATA_DIR_OUT = 0x02, /* OUTBOUND */
+ DATA_DIR_BYRECIPIENT = 0x04, /* UNSPECIFIED */
+};
+
+enum port_type {
+ PORT_TYPE_SAS = (1L << 1),
+ PORT_TYPE_SATA = (1L << 0),
+};
+
+/* driver compile-time configuration */
+#define PM8001_MAX_CCB 512 /* max ccbs supported */
+#define PM8001_MAX_INB_NUM 1
+#define PM8001_MAX_OUTB_NUM 1
+#define PM8001_CAN_QUEUE 128 /* SCSI Queue depth */
+
+/* unchangeable hardware details */
+#define PM8001_MAX_PHYS 8 /* max. possible phys */
+#define PM8001_MAX_PORTS 8 /* max. possible ports */
+#define PM8001_MAX_DEVICES 1024 /* max supported device */
+
+enum memory_region_num {
+ AAP1 = 0x0, /* application acceleration processor */
+ IOP, /* IO processor */
+ CI, /* consumer index */
+ PI, /* producer index */
+ IB, /* inbound queue */
+ OB, /* outbound queue */
+ NVMD, /* NVM device */
+ DEV_MEM, /* memory for devices */
+ CCB_MEM, /* memory for command control block */
+};
+#define PM8001_EVENT_LOG_SIZE (128 * 1024)
+
+/*error code*/
+enum mpi_err {
+ MPI_IO_STATUS_SUCCESS = 0x0,
+ MPI_IO_STATUS_BUSY = 0x01,
+ MPI_IO_STATUS_FAIL = 0x02,
+};
+
+/**
+ * Phy Control constants
+ */
+enum phy_control_type {
+ PHY_LINK_RESET = 0x01,
+ PHY_HARD_RESET = 0x02,
+ PHY_NOTIFY_ENABLE_SPINUP = 0x10,
+};
+
+enum pm8001_hba_info_flags {
+ PM8001F_INIT_TIME = (1U << 0),
+ PM8001F_RUN_TIME = (1U << 1),
+};
+
+#endif
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
new file mode 100644
index 00000000000..a3de306b904
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -0,0 +1,4458 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+ #include "pm8001_sas.h"
+ #include "pm8001_hwi.h"
+ #include "pm8001_chips.h"
+ #include "pm8001_ctl.h"
+
+/**
+ * read_main_config_table - read the configure table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit read_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+ pm8001_ha->main_cfg_tbl.signature = pm8001_mr32(address, 0x00);
+ pm8001_ha->main_cfg_tbl.interface_rev = pm8001_mr32(address, 0x04);
+ pm8001_ha->main_cfg_tbl.firmware_rev = pm8001_mr32(address, 0x08);
+ pm8001_ha->main_cfg_tbl.max_out_io = pm8001_mr32(address, 0x0C);
+ pm8001_ha->main_cfg_tbl.max_sgl = pm8001_mr32(address, 0x10);
+ pm8001_ha->main_cfg_tbl.ctrl_cap_flag = pm8001_mr32(address, 0x14);
+ pm8001_ha->main_cfg_tbl.gst_offset = pm8001_mr32(address, 0x18);
+ pm8001_ha->main_cfg_tbl.inbound_queue_offset =
+ pm8001_mr32(address, MAIN_IBQ_OFFSET);
+ pm8001_ha->main_cfg_tbl.outbound_queue_offset =
+ pm8001_mr32(address, MAIN_OBQ_OFFSET);
+ pm8001_ha->main_cfg_tbl.hda_mode_flag =
+ pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET);
+
+ /* read analog Setting offset from the configuration table */
+ pm8001_ha->main_cfg_tbl.anolog_setup_table_offset =
+ pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET);
+
+ /* read Error Dump Offset and Length */
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_offset0 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET);
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_length0 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH);
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_offset1 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET);
+ pm8001_ha->main_cfg_tbl.fatal_err_dump_length1 =
+ pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH);
+}
+
+/**
+ * read_general_status_table - read the general status table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_general_status_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->general_stat_tbl_addr;
+ pm8001_ha->gs_tbl.gst_len_mpistate = pm8001_mr32(address, 0x00);
+ pm8001_ha->gs_tbl.iq_freeze_state0 = pm8001_mr32(address, 0x04);
+ pm8001_ha->gs_tbl.iq_freeze_state1 = pm8001_mr32(address, 0x08);
+ pm8001_ha->gs_tbl.msgu_tcnt = pm8001_mr32(address, 0x0C);
+ pm8001_ha->gs_tbl.iop_tcnt = pm8001_mr32(address, 0x10);
+ pm8001_ha->gs_tbl.reserved = pm8001_mr32(address, 0x14);
+ pm8001_ha->gs_tbl.phy_state[0] = pm8001_mr32(address, 0x18);
+ pm8001_ha->gs_tbl.phy_state[1] = pm8001_mr32(address, 0x1C);
+ pm8001_ha->gs_tbl.phy_state[2] = pm8001_mr32(address, 0x20);
+ pm8001_ha->gs_tbl.phy_state[3] = pm8001_mr32(address, 0x24);
+ pm8001_ha->gs_tbl.phy_state[4] = pm8001_mr32(address, 0x28);
+ pm8001_ha->gs_tbl.phy_state[5] = pm8001_mr32(address, 0x2C);
+ pm8001_ha->gs_tbl.phy_state[6] = pm8001_mr32(address, 0x30);
+ pm8001_ha->gs_tbl.phy_state[7] = pm8001_mr32(address, 0x34);
+ pm8001_ha->gs_tbl.reserved1 = pm8001_mr32(address, 0x38);
+ pm8001_ha->gs_tbl.reserved2 = pm8001_mr32(address, 0x3C);
+ pm8001_ha->gs_tbl.reserved3 = pm8001_mr32(address, 0x40);
+ pm8001_ha->gs_tbl.recover_err_info[0] = pm8001_mr32(address, 0x44);
+ pm8001_ha->gs_tbl.recover_err_info[1] = pm8001_mr32(address, 0x48);
+ pm8001_ha->gs_tbl.recover_err_info[2] = pm8001_mr32(address, 0x4C);
+ pm8001_ha->gs_tbl.recover_err_info[3] = pm8001_mr32(address, 0x50);
+ pm8001_ha->gs_tbl.recover_err_info[4] = pm8001_mr32(address, 0x54);
+ pm8001_ha->gs_tbl.recover_err_info[5] = pm8001_mr32(address, 0x58);
+ pm8001_ha->gs_tbl.recover_err_info[6] = pm8001_mr32(address, 0x5C);
+ pm8001_ha->gs_tbl.recover_err_info[7] = pm8001_mr32(address, 0x60);
+}
+
+/**
+ * read_inbnd_queue_table - read the inbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+ int inbQ_num = 1;
+ int i;
+ void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+ for (i = 0; i < inbQ_num; i++) {
+ u32 offset = i * 0x20;
+ pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
+ get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
+ pm8001_ha->inbnd_q_tbl[i].pi_offset =
+ pm8001_mr32(address, (offset + 0x18));
+ }
+}
+
+/**
+ * read_outbnd_queue_table - read the outbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+ int outbQ_num = 1;
+ int i;
+ void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+ for (i = 0; i < outbQ_num; i++) {
+ u32 offset = i * 0x24;
+ pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
+ get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
+ pm8001_ha->outbnd_q_tbl[i].ci_offset =
+ pm8001_mr32(address, (offset + 0x18));
+ }
+}
+
+/**
+ * init_default_table_values - init the default table.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+init_default_table_values(struct pm8001_hba_info *pm8001_ha)
+{
+ int qn = 1;
+ int i;
+ u32 offsetib, offsetob;
+ void __iomem *addressib = pm8001_ha->inbnd_q_tbl_addr;
+ void __iomem *addressob = pm8001_ha->outbnd_q_tbl_addr;
+
+ pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd = 0;
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3 = 0;
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7 = 0;
+
+ pm8001_ha->main_cfg_tbl.upper_event_log_addr =
+ pm8001_ha->memoryMap.region[AAP1].phys_addr_hi;
+ pm8001_ha->main_cfg_tbl.lower_event_log_addr =
+ pm8001_ha->memoryMap.region[AAP1].phys_addr_lo;
+ pm8001_ha->main_cfg_tbl.event_log_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.event_log_option = 0x01;
+ pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr =
+ pm8001_ha->memoryMap.region[IOP].phys_addr_hi;
+ pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr =
+ pm8001_ha->memoryMap.region[IOP].phys_addr_lo;
+ pm8001_ha->main_cfg_tbl.iop_event_log_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->main_cfg_tbl.iop_event_log_option = 0x01;
+ pm8001_ha->main_cfg_tbl.fatal_err_interrupt = 0x01;
+ for (i = 0; i < qn; i++) {
+ pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
+ 0x00000100 | (0x00000040 << 16) | (0x00<<30);
+ pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[IB].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[IB].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[IB].virt_ptr;
+ pm8001_ha->inbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[IB].total_len;
+ pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr =
+ pm8001_ha->memoryMap.region[CI].phys_addr_hi;
+ pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr =
+ pm8001_ha->memoryMap.region[CI].phys_addr_lo;
+ pm8001_ha->inbnd_q_tbl[i].ci_virt =
+ pm8001_ha->memoryMap.region[CI].virt_ptr;
+ offsetib = i * 0x20;
+ pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
+ get_pci_bar_index(pm8001_mr32(addressib,
+ (offsetib + 0x14)));
+ pm8001_ha->inbnd_q_tbl[i].pi_offset =
+ pm8001_mr32(addressib, (offsetib + 0x18));
+ pm8001_ha->inbnd_q_tbl[i].producer_idx = 0;
+ pm8001_ha->inbnd_q_tbl[i].consumer_index = 0;
+ }
+ for (i = 0; i < qn; i++) {
+ pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
+ 256 | (64 << 16) | (1<<30);
+ pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
+ pm8001_ha->memoryMap.region[OB].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
+ pm8001_ha->memoryMap.region[OB].phys_addr_lo;
+ pm8001_ha->outbnd_q_tbl[i].base_virt =
+ (u8 *)pm8001_ha->memoryMap.region[OB].virt_ptr;
+ pm8001_ha->outbnd_q_tbl[i].total_length =
+ pm8001_ha->memoryMap.region[OB].total_len;
+ pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr =
+ pm8001_ha->memoryMap.region[PI].phys_addr_hi;
+ pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr =
+ pm8001_ha->memoryMap.region[PI].phys_addr_lo;
+ pm8001_ha->outbnd_q_tbl[i].interrup_vec_cnt_delay =
+ 0 | (10 << 16) | (0 << 24);
+ pm8001_ha->outbnd_q_tbl[i].pi_virt =
+ pm8001_ha->memoryMap.region[PI].virt_ptr;
+ offsetob = i * 0x24;
+ pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
+ get_pci_bar_index(pm8001_mr32(addressob,
+ offsetob + 0x14));
+ pm8001_ha->outbnd_q_tbl[i].ci_offset =
+ pm8001_mr32(addressob, (offsetob + 0x18));
+ pm8001_ha->outbnd_q_tbl[i].consumer_idx = 0;
+ pm8001_ha->outbnd_q_tbl[i].producer_index = 0;
+ }
+}
+
+/**
+ * update_main_config_table - update the main default table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+ pm8001_mw32(address, 0x24,
+ pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd);
+ pm8001_mw32(address, 0x28,
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3);
+ pm8001_mw32(address, 0x2C,
+ pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7);
+ pm8001_mw32(address, 0x30,
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3);
+ pm8001_mw32(address, 0x34,
+ pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7);
+ pm8001_mw32(address, 0x38,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3);
+ pm8001_mw32(address, 0x3C,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7);
+ pm8001_mw32(address, 0x40,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3);
+ pm8001_mw32(address, 0x44,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7);
+ pm8001_mw32(address, 0x48,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3);
+ pm8001_mw32(address, 0x4C,
+ pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7);
+ pm8001_mw32(address, 0x50,
+ pm8001_ha->main_cfg_tbl.upper_event_log_addr);
+ pm8001_mw32(address, 0x54,
+ pm8001_ha->main_cfg_tbl.lower_event_log_addr);
+ pm8001_mw32(address, 0x58, pm8001_ha->main_cfg_tbl.event_log_size);
+ pm8001_mw32(address, 0x5C, pm8001_ha->main_cfg_tbl.event_log_option);
+ pm8001_mw32(address, 0x60,
+ pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr);
+ pm8001_mw32(address, 0x64,
+ pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr);
+ pm8001_mw32(address, 0x68, pm8001_ha->main_cfg_tbl.iop_event_log_size);
+ pm8001_mw32(address, 0x6C,
+ pm8001_ha->main_cfg_tbl.iop_event_log_option);
+ pm8001_mw32(address, 0x70,
+ pm8001_ha->main_cfg_tbl.fatal_err_interrupt);
+}
+
+/**
+ * update_inbnd_queue_table - update the inbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+{
+ void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+ u16 offset = number * 0x20;
+ pm8001_mw32(address, offset + 0x00,
+ pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
+ pm8001_mw32(address, offset + 0x04,
+ pm8001_ha->inbnd_q_tbl[number].upper_base_addr);
+ pm8001_mw32(address, offset + 0x08,
+ pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
+ pm8001_mw32(address, offset + 0x0C,
+ pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr);
+ pm8001_mw32(address, offset + 0x10,
+ pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
+}
+
+/**
+ * update_outbnd_queue_table - update the outbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+{
+ void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+ u16 offset = number * 0x24;
+ pm8001_mw32(address, offset + 0x00,
+ pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
+ pm8001_mw32(address, offset + 0x04,
+ pm8001_ha->outbnd_q_tbl[number].upper_base_addr);
+ pm8001_mw32(address, offset + 0x08,
+ pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
+ pm8001_mw32(address, offset + 0x0C,
+ pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr);
+ pm8001_mw32(address, offset + 0x10,
+ pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
+ pm8001_mw32(address, offset + 0x1C,
+ pm8001_ha->outbnd_q_tbl[number].interrup_vec_cnt_delay);
+}
+
+/**
+ * bar4_shift - function is called to shift BAR base address
+ * @pm8001_ha : our hba card infomation
+ * @shiftValue : shifting value in memory bar.
+ */
+static int bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
+{
+ u32 regVal;
+ u32 max_wait_count;
+
+ /* program the inbound AXI translation Lower Address */
+ pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue);
+
+ /* confirm the setting is written */
+ max_wait_count = 1 * 1000 * 1000; /* 1 sec */
+ do {
+ udelay(1);
+ regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW);
+ } while ((regVal != shiftValue) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW"
+ " = 0x%x\n", regVal));
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * mpi_set_phys_g3_with_ssc
+ * @pm8001_ha: our hba card information
+ * @SSCbit: set SSCbit to 0 to disable all phys ssc; 1 to enable all phys ssc.
+ */
+static void __devinit
+mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
+{
+ u32 offset;
+ u32 value;
+ u32 i, j;
+ u32 bit_cnt;
+
+#define SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR 0x00030000
+#define SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR 0x00040000
+#define SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET 0x1074
+#define SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET 0x1074
+#define PHY_G3_WITHOUT_SSC_BIT_SHIFT 12
+#define PHY_G3_WITH_SSC_BIT_SHIFT 13
+#define SNW3_PHY_CAPABILITIES_PARITY 31
+
+ /*
+ * Using shifted destination address 0x3_0000:0x1074 + 0x4000*N (N=0:3)
+ * Using shifted destination address 0x4_0000:0x1074 + 0x4000*(N-4) (N=4:7)
+ */
+ if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR))
+ return;
+ /* set SSC bit of PHY 0 - 3 */
+ for (i = 0; i < 4; i++) {
+ offset = SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET + 0x4000 * i;
+ value = pm8001_cr32(pm8001_ha, 2, offset);
+ if (SSCbit) {
+ value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT);
+ } else {
+ value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT);
+ }
+ bit_cnt = 0;
+ for (j = 0; j < 31; j++)
+ if ((value >> j) & 0x00000001)
+ bit_cnt++;
+ if (bit_cnt % 2)
+ value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY);
+ else
+ value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY;
+
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+
+ /* shift membase 3 for SAS2_SETTINGS_LOCAL_PHY 4 - 7 */
+ if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR))
+ return;
+
+ /* set SSC bit of PHY 4 - 7 */
+ for (i = 4; i < 8; i++) {
+ offset = SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
+ value = pm8001_cr32(pm8001_ha, 2, offset);
+ if (SSCbit) {
+ value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT);
+ } else {
+ value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT;
+ value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT);
+ }
+ bit_cnt = 0;
+ for (j = 0; j < 31; j++)
+ if ((value >> j) & 0x00000001)
+ bit_cnt++;
+ if (bit_cnt % 2)
+ value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY);
+ else
+ value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY;
+
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+
+ /*set the shifted destination address to 0x0 to avoid error operation */
+ bar4_shift(pm8001_ha, 0x0);
+ return;
+}
+
+/**
+ * mpi_set_open_retry_interval_reg
+ * @pm8001_ha: our hba card information
+ * @interval - interval time for each OPEN_REJECT (RETRY). The units are in 1us.
+ */
+static void __devinit
+mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
+ u32 interval)
+{
+ u32 offset;
+ u32 value;
+ u32 i;
+
+#define OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR 0x00030000
+#define OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR 0x00040000
+#define OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET 0x30B4
+#define OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET 0x30B4
+#define OPEN_RETRY_INTERVAL_REG_MASK 0x0000FFFF
+
+ value = interval & OPEN_RETRY_INTERVAL_REG_MASK;
+ /* shift bar and set the OPEN_REJECT(RETRY) interval time of PHY 0 -3.*/
+ if (-1 == bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR))
+ return;
+ for (i = 0; i < 4; i++) {
+ offset = OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET + 0x4000 * i;
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+
+ if (-1 == bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR))
+ return;
+ for (i = 4; i < 8; i++) {
+ offset = OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
+ pm8001_cw32(pm8001_ha, 2, offset, value);
+ }
+ /*set the shifted destination address to 0x0 to avoid error operation */
+ bar4_shift(pm8001_ha, 0x0);
+ return;
+}
+
+/**
+ * mpi_init_check - check firmware initialization status.
+ * @pm8001_ha: our hba card information
+ */
+static int mpi_init_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 max_wait_count;
+ u32 value;
+ u32 gst_len_mpistate;
+ /* Write bit0=1 to Inbound DoorBell Register to tell the SPC FW the
+ table is updated */
+ pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_UPDATE);
+ /* wait until Inbound DoorBell Clear Register toggled */
+ max_wait_count = 1 * 1000 * 1000;/* 1 sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+ value &= SPC_MSGU_CFG_TABLE_UPDATE;
+ } while ((value != 0) && (--max_wait_count));
+
+ if (!max_wait_count)
+ return -1;
+ /* check the MPI-State for initialization */
+ gst_len_mpistate =
+ pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+ GST_GSTLEN_MPIS_OFFSET);
+ if (GST_MPI_STATE_INIT != (gst_len_mpistate & GST_MPI_STATE_MASK))
+ return -1;
+ /* check MPI Initialization error */
+ gst_len_mpistate = gst_len_mpistate >> 16;
+ if (0x0000 != gst_len_mpistate)
+ return -1;
+ return 0;
+}
+
+/**
+ * check_fw_ready - The LLDD check if the FW is ready, if not, return error.
+ * @pm8001_ha: our hba card information
+ */
+static int check_fw_ready(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 value, value1;
+ u32 max_wait_count;
+ /* check error state */
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ /* check AAP error */
+ if (SCRATCH_PAD1_ERR == (value & SCRATCH_PAD_STATE_MASK)) {
+ /* error state */
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+ return -1;
+ }
+
+ /* check IOP error */
+ if (SCRATCH_PAD2_ERR == (value1 & SCRATCH_PAD_STATE_MASK)) {
+ /* error state */
+ value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
+ return -1;
+ }
+
+ /* bit 4-31 of scratch pad1 should be zeros if it is not
+ in error state*/
+ if (value & SCRATCH_PAD1_STATE_MASK) {
+ /* error case */
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+ return -1;
+ }
+
+ /* bit 2, 4-31 of scratch pad2 should be zeros if it is not
+ in error state */
+ if (value1 & SCRATCH_PAD2_STATE_MASK) {
+ /* error case */
+ return -1;
+ }
+
+ max_wait_count = 1 * 1000 * 1000;/* 1 sec timeout */
+
+ /* wait until scratch pad 1 and 2 registers in ready state */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
+ & SCRATCH_PAD1_RDY;
+ value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
+ & SCRATCH_PAD2_RDY;
+ if ((--max_wait_count) == 0)
+ return -1;
+ } while ((value != SCRATCH_PAD1_RDY) || (value1 != SCRATCH_PAD2_RDY));
+ return 0;
+}
+
+static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
+{
+ void __iomem *base_addr;
+ u32 value;
+ u32 offset;
+ u32 pcibar;
+ u32 pcilogic;
+
+ value = pm8001_cr32(pm8001_ha, 0, 0x44);
+ offset = value & 0x03FFFFFF;
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Scratchpad 0 Offset: %x \n", offset));
+ pcilogic = (value & 0xFC000000) >> 26;
+ pcibar = get_pci_bar_index(pcilogic);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Scratchpad 0 PCI BAR: %d \n", pcibar));
+ pm8001_ha->main_cfg_tbl_addr = base_addr =
+ pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
+ pm8001_ha->general_stat_tbl_addr =
+ base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x18);
+ pm8001_ha->inbnd_q_tbl_addr =
+ base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C);
+ pm8001_ha->outbnd_q_tbl_addr =
+ base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x20);
+}
+
+/**
+ * pm8001_chip_init - the main init function that initialize whole PM8001 chip.
+ * @pm8001_ha: our hba card information
+ */
+static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
+{
+ /* check the firmware status */
+ if (-1 == check_fw_ready(pm8001_ha)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Firmware is not ready!\n"));
+ return -EBUSY;
+ }
+
+ /* Initialize pci space address eg: mpi offset */
+ init_pci_device_addresses(pm8001_ha);
+ init_default_table_values(pm8001_ha);
+ read_main_config_table(pm8001_ha);
+ read_general_status_table(pm8001_ha);
+ read_inbnd_queue_table(pm8001_ha);
+ read_outbnd_queue_table(pm8001_ha);
+ /* update main config table ,inbound table and outbound table */
+ update_main_config_table(pm8001_ha);
+ update_inbnd_queue_table(pm8001_ha, 0);
+ update_outbnd_queue_table(pm8001_ha, 0);
+ mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
+ mpi_set_open_retry_interval_reg(pm8001_ha, 7);
+ /* notify firmware update finished and check initialization status */
+ if (0 == mpi_init_check(pm8001_ha)) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("MPI initialize successful!\n"));
+ } else
+ return -EBUSY;
+ /*This register is a 16-bit timer with a resolution of 1us. This is the
+ timer used for interrupt delay/coalescing in the PCIe Application Layer.
+ Zero is not a valid value. A value of 1 in the register will cause the
+ interrupts to be normal. A value greater than 1 will cause coalescing
+ delays.*/
+ pm8001_cw32(pm8001_ha, 1, 0x0033c0, 0x1);
+ pm8001_cw32(pm8001_ha, 1, 0x0033c4, 0x0);
+ return 0;
+}
+
+static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 max_wait_count;
+ u32 value;
+ u32 gst_len_mpistate;
+ init_pci_device_addresses(pm8001_ha);
+ /* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the
+ table is stop */
+ pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_RESET);
+
+ /* wait until Inbound DoorBell Clear Register toggled */
+ max_wait_count = 1 * 1000 * 1000;/* 1 sec */
+ do {
+ udelay(1);
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+ value &= SPC_MSGU_CFG_TABLE_RESET;
+ } while ((value != 0) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT:IBDB value/=0x%x\n", value));
+ return -1;
+ }
+
+ /* check the MPI-State for termination in progress */
+ /* wait until Inbound DoorBell Clear Register toggled */
+ max_wait_count = 1 * 1000 * 1000; /* 1 sec */
+ do {
+ udelay(1);
+ gst_len_mpistate =
+ pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+ GST_GSTLEN_MPIS_OFFSET);
+ if (GST_MPI_STATE_UNINIT ==
+ (gst_len_mpistate & GST_MPI_STATE_MASK))
+ break;
+ } while (--max_wait_count);
+ if (!max_wait_count) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk(" TIME OUT MPI State = 0x%x\n",
+ gst_len_mpistate & GST_MPI_STATE_MASK));
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * soft_reset_ready_check - Function to check FW is ready for soft reset.
+ * @pm8001_ha: our hba card information
+ */
+static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 regVal, regVal1, regVal2;
+ if (mpi_uninit_check(pm8001_ha) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("MPI state is not ready\n"));
+ return -1;
+ }
+ /* read the scratch pad 2 register bit 2 */
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
+ & SCRATCH_PAD2_FWRDY_RST;
+ if (regVal == SCRATCH_PAD2_FWRDY_RST) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Firmware is ready for reset .\n"));
+ } else {
+ /* Trigger NMI twice via RB6 */
+ if (-1 == bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ RB6_ACCESS_REG));
+ return -1;
+ }
+ pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET,
+ RB6_MAGIC_NUMBER_RST);
+ pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, RB6_MAGIC_NUMBER_RST);
+ /* wait for 100 ms */
+ mdelay(100);
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) &
+ SCRATCH_PAD2_FWRDY_RST;
+ if (regVal != SCRATCH_PAD2_FWRDY_RST) {
+ regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT:MSGU_SCRATCH_PAD1"
+ "=0x%x, MSGU_SCRATCH_PAD2=0x%x\n",
+ regVal1, regVal2));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3)));
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * pm8001_chip_soft_rst - soft reset the PM8001 chip, so that the clear all
+ * the FW register status to the originated status.
+ * @pm8001_ha: our hba card information
+ * @signature: signature in host scratch pad0 register.
+ */
+static int
+pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
+{
+ u32 regVal, toggleVal;
+ u32 max_wait_count;
+ u32 regVal1, regVal2, regVal3;
+
+ /* step1: Check FW is ready for soft reset */
+ if (soft_reset_ready_check(pm8001_ha) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("FW is not ready\n"));
+ return -1;
+ }
+
+ /* step 2: clear NMI status register on AAP1 and IOP, write the same
+ value to clear */
+ /* map 0x60000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ MBIC_AAP1_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("MBIC - NMI Enable VPE0 (IOP)= 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0);
+ /* map 0x70000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ MBIC_IOP_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("MBIC - NMI Enable VPE0 (AAP1)= 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1, 0x0);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE -Event Interrupt Enable = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE, 0x0);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE - Event Interrupt = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT, regVal);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE -Error Interrupt Enable = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE, 0x0);
+
+ regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCIE - Error Interrupt = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT, regVal);
+
+ /* read the scratch pad 1 register bit 2 */
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
+ & SCRATCH_PAD1_RST;
+ toggleVal = regVal ^ SCRATCH_PAD1_RST;
+
+ /* set signature in host scratch pad0 register to tell SPC that the
+ host performs the soft reset */
+ pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, signature);
+
+ /* read required registers for confirmming */
+ /* map 0x0700000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ GSM_ADDR_BASE));
+ return -1;
+ }
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x0(0x00007b88)-GSM Configuration and"
+ " Reset = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+ /* step 3: host read GSM Configuration and Reset register */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
+ /* Put those bits to low */
+ /* GSM XCBI offset = 0x70 0000
+ 0x00 Bit 13 COM_SLV_SW_RSTB 1
+ 0x00 Bit 12 QSSP_SW_RSTB 1
+ 0x00 Bit 11 RAAE_SW_RSTB 1
+ 0x00 Bit 9 RB_1_SW_RSTB 1
+ 0x00 Bit 8 SM_SW_RSTB 1
+ */
+ regVal &= ~(0x00003b00);
+ /* host write GSM Configuration and Reset register */
+ pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM "
+ "Configuration and Reset is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+ /* step 4: */
+ /* disable GSM - Read Address Parity Check */
+ regVal1 = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity Check "
+ "Enable = 0x%x\n", regVal1));
+ pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, 0x0);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
+ "is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+
+ /* disable GSM - Write Address Parity Check */
+ regVal2 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700040 - Write Address Parity Check"
+ " Enable = 0x%x\n", regVal2));
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, 0x0);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700040 - Write Address Parity Check "
+ "Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+
+ /* disable GSM - Write Data Parity Check */
+ regVal3 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x300048 - Write Data Parity Check"
+ " Enable = 0x%x\n", regVal3));
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, 0x0);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x300048 - Write Data Parity Check Enable"
+ "is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+
+ /* step 5: delay 10 usec */
+ udelay(10);
+ /* step 5-b: set GPIO-0 output control to tristate anyway */
+ if (-1 == bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ GPIO_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GPIO Output Control Register:"
+ " = 0x%x\n", regVal));
+ /* set GPIO-0 output control to tri-state */
+ regVal &= 0xFFFFFFFC;
+ pm8001_cw32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET, regVal);
+
+ /* Step 6: Reset the IOP and AAP1 */
+ /* map 0x00000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
+ SPC_TOP_LEVEL_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Top Register before resetting IOP/AAP1"
+ ":= 0x%x\n", regVal));
+ regVal &= ~(SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 7: Reset the BDMA/OSSP */
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Top Register before resetting BDMA/OSSP"
+ ": = 0x%x\n", regVal));
+ regVal &= ~(SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 8: delay 10 usec */
+ udelay(10);
+
+ /* step 9: bring the BDMA and OSSP out of reset */
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("Top Register before bringing up BDMA/OSSP"
+ ":= 0x%x\n", regVal));
+ regVal |= (SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 10: delay 10 usec */
+ udelay(10);
+
+ /* step 11: reads and sets the GSM Configuration and Reset Register */
+ /* map 0x0700000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
+ GSM_ADDR_BASE));
+ return -1;
+ }
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x0 (0x00007b88)-GSM Configuration and "
+ "Reset = 0x%x\n", pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
+ /* Put those bits to high */
+ /* GSM XCBI offset = 0x70 0000
+ 0x00 Bit 13 COM_SLV_SW_RSTB 1
+ 0x00 Bit 12 QSSP_SW_RSTB 1
+ 0x00 Bit 11 RAAE_SW_RSTB 1
+ 0x00 Bit 9 RB_1_SW_RSTB 1
+ 0x00 Bit 8 SM_SW_RSTB 1
+ */
+ regVal |= (GSM_CONFIG_RESET_VALUE);
+ pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM (0x00004088 ==> 0x00007b88) - GSM"
+ " Configuration and Reset is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+ /* step 12: Restore GSM - Read Address Parity Check */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
+ /* just for debugging */
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
+ " = 0x%x\n", regVal));
+ pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, regVal1);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700038 - Read Address Parity"
+ " Check Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+ /* Restore GSM - Write Address Parity Check */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, regVal2);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700040 - Write Address Parity Check"
+ " Enable is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+ /* Restore GSM - Write Data Parity Check */
+ regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
+ pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, regVal3);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("GSM 0x700048 - Write Data Parity Check Enable"
+ "is set to = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+
+ /* step 13: bring the IOP and AAP1 out of reset */
+ /* map 0x00000 to BAR4(0x20), BAR2(win) */
+ if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Shift Bar4 to 0x%x failed\n",
+ SPC_TOP_LEVEL_ADDR_BASE));
+ return -1;
+ }
+ regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+ regVal |= (SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
+ pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+ /* step 14: delay 10 usec - Normal Mode */
+ udelay(10);
+ /* check Soft Reset Normal mode or Soft Reset HDA mode */
+ if (signature == SPC_SOFT_RESET_SIGNATURE) {
+ /* step 15 (Normal Mode): wait until scratch pad1 register
+ bit 2 toggled */
+ max_wait_count = 2 * 1000 * 1000;/* 2 sec */
+ do {
+ udelay(1);
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) &
+ SCRATCH_PAD1_RST;
+ } while ((regVal != toggleVal) && (--max_wait_count));
+
+ if (!max_wait_count) {
+ regVal = pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_1);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TIMEOUT : ToggleVal 0x%x,"
+ "MSGU_SCRATCH_PAD1 = 0x%x\n",
+ toggleVal, regVal));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_0)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD2 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_2)));
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_3)));
+ return -1;
+ }
+
+ /* step 16 (Normal) - Clear ODMR and ODCR */
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+
+ /* step 17 (Normal Mode): wait for the FW and IOP to get
+ ready - 1 sec timeout */
+ /* Wait for the SPC Configuration Table to be ready */
+ if (check_fw_ready(pm8001_ha) == -1) {
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+ /* return error if MPI Configuration Table not ready */
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("FW not ready SCRATCH_PAD1"
+ " = 0x%x\n", regVal));
+ regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+ /* return error if MPI Configuration Table not ready */
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("FW not ready SCRATCH_PAD2"
+ " = 0x%x\n", regVal));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_0)));
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+ pm8001_cr32(pm8001_ha, 0,
+ MSGU_SCRATCH_PAD_3)));
+ return -1;
+ }
+ }
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("SPC soft reset Complete\n"));
+ return 0;
+}
+
+static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 i;
+ u32 regVal;
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("chip reset start\n"));
+
+ /* do SPC chip reset. */
+ regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
+ regVal &= ~(SPC_REG_RESET_DEVICE);
+ pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);
+
+ /* delay 10 usec */
+ udelay(10);
+
+ /* bring chip reset out of reset */
+ regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
+ regVal |= SPC_REG_RESET_DEVICE;
+ pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);
+
+ /* delay 10 usec */
+ udelay(10);
+
+ /* wait for 20 msec until the firmware gets reloaded */
+ i = 20;
+ do {
+ mdelay(1);
+ } while ((--i) != 0);
+
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("chip reset finished\n"));
+}
+
+/**
+ * pm8001_chip_iounmap - which maped when initilized.
+ * @pm8001_ha: our hba card information
+ */
+static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha)
+{
+ s8 bar, logical = 0;
+ for (bar = 0; bar < 6; bar++) {
+ /*
+ ** logical BARs for SPC:
+ ** bar 0 and 1 - logical BAR0
+ ** bar 2 and 3 - logical BAR1
+ ** bar4 - logical BAR2
+ ** bar5 - logical BAR3
+ ** Skip the appropriate assignments:
+ */
+ if ((bar == 1) || (bar == 3))
+ continue;
+ if (pm8001_ha->io_mem[logical].memvirtaddr) {
+ iounmap(pm8001_ha->io_mem[logical].memvirtaddr);
+ logical++;
+ }
+ }
+}
+
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_intx_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
+{
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+}
+
+ /**
+ * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_intx_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+{
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL);
+}
+
+/**
+ * pm8001_chip_msix_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_msix_interrupt_enable(struct pm8001_hba_info *pm8001_ha,
+ u32 int_vec_idx)
+{
+ u32 msi_index;
+ u32 value;
+ msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
+ msi_index += MSIX_TABLE_BASE;
+ pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_ENABLE);
+ value = (1 << int_vec_idx);
+ pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, value);
+
+}
+
+/**
+ * pm8001_chip_msix_interrupt_disable - disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_msix_interrupt_disable(struct pm8001_hba_info *pm8001_ha,
+ u32 int_vec_idx)
+{
+ u32 msi_index;
+ msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
+ msi_index += MSIX_TABLE_BASE;
+ pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_DISABLE);
+
+}
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
+{
+#ifdef PM8001_USE_MSIX
+ pm8001_chip_msix_interrupt_enable(pm8001_ha, 0);
+ return;
+#endif
+ pm8001_chip_intx_interrupt_enable(pm8001_ha);
+
+}
+
+/**
+ * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+{
+#ifdef PM8001_USE_MSIX
+ pm8001_chip_msix_interrupt_disable(pm8001_ha, 0);
+ return;
+#endif
+ pm8001_chip_intx_interrupt_disable(pm8001_ha);
+
+}
+
+/**
+ * mpi_msg_free_get- get the free message buffer for transfer inbound queue.
+ * @circularQ: the inbound queue we want to transfer to HBA.
+ * @messageSize: the message size of this transfer, normally it is 64 bytes
+ * @messagePtr: the pointer to message.
+ */
+static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
+ u16 messageSize, void **messagePtr)
+{
+ u32 offset, consumer_index;
+ struct mpi_msg_hdr *msgHeader;
+ u8 bcCount = 1; /* only support single buffer */
+
+ /* Checks is the requested message size can be allocated in this queue*/
+ if (messageSize > 64) {
+ *messagePtr = NULL;
+ return -1;
+ }
+
+ /* Stores the new consumer index */
+ consumer_index = pm8001_read_32(circularQ->ci_virt);
+ circularQ->consumer_index = cpu_to_le32(consumer_index);
+ if (((circularQ->producer_idx + bcCount) % 256) ==
+ circularQ->consumer_index) {
+ *messagePtr = NULL;
+ return -1;
+ }
+ /* get memory IOMB buffer address */
+ offset = circularQ->producer_idx * 64;
+ /* increment to next bcCount element */
+ circularQ->producer_idx = (circularQ->producer_idx + bcCount) % 256;
+ /* Adds that distance to the base of the region virtual address plus
+ the message header size*/
+ msgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt + offset);
+ *messagePtr = ((void *)msgHeader) + sizeof(struct mpi_msg_hdr);
+ return 0;
+}
+
+/**
+ * mpi_build_cmd- build the message queue for transfer, update the PI to FW
+ * to tell the fw to get this message from IOMB.
+ * @pm8001_ha: our hba card information
+ * @circularQ: the inbound queue we want to transfer to HBA.
+ * @opCode: the operation code represents commands which LLDD and fw recognized.
+ * @payload: the command payload of each operation command.
+ */
+static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
+ struct inbound_queue_table *circularQ,
+ u32 opCode, void *payload)
+{
+ u32 Header = 0, hpriority = 0, bc = 1, category = 0x02;
+ u32 responseQueue = 0;
+ void *pMessage;
+
+ if (mpi_msg_free_get(circularQ, 64, &pMessage) < 0) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("No free mpi buffer \n"));
+ return -1;
+ }
+ BUG_ON(!payload);
+ /*Copy to the payload*/
+ memcpy(pMessage, payload, (64 - sizeof(struct mpi_msg_hdr)));
+
+ /*Build the header*/
+ Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24)
+ | ((responseQueue & 0x3F) << 16)
+ | ((category & 0xF) << 12) | (opCode & 0xFFF));
+
+ pm8001_write_32((pMessage - 4), 0, cpu_to_le32(Header));
+ /*Update the PI to the firmware*/
+ pm8001_cw32(pm8001_ha, circularQ->pi_pci_bar,
+ circularQ->pi_offset, circularQ->producer_idx);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("after PI= %d CI= %d \n", circularQ->producer_idx,
+ circularQ->consumer_index));
+ return 0;
+}
+
+static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
+ struct outbound_queue_table *circularQ, u8 bc)
+{
+ u32 producer_index;
+ struct mpi_msg_hdr *msgHeader;
+ struct mpi_msg_hdr *pOutBoundMsgHeader;
+
+ msgHeader = (struct mpi_msg_hdr *)(pMsg - sizeof(struct mpi_msg_hdr));
+ pOutBoundMsgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt +
+ circularQ->consumer_idx * 64);
+ if (pOutBoundMsgHeader != msgHeader) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("consumer_idx = %d msgHeader = %p\n",
+ circularQ->consumer_idx, msgHeader));
+
+ /* Update the producer index from SPC */
+ producer_index = pm8001_read_32(circularQ->pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_index);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("consumer_idx = %d producer_index = %d"
+ "msgHeader = %p\n", circularQ->consumer_idx,
+ circularQ->producer_index, msgHeader));
+ return 0;
+ }
+ /* free the circular queue buffer elements associated with the message*/
+ circularQ->consumer_idx = (circularQ->consumer_idx + bc) % 256;
+ /* update the CI of outbound queue */
+ pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, circularQ->ci_offset,
+ circularQ->consumer_idx);
+ /* Update the producer index from SPC*/
+ producer_index = pm8001_read_32(circularQ->pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_index);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" CI=%d PI=%d\n", circularQ->consumer_idx,
+ circularQ->producer_index));
+ return 0;
+}
+
+/**
+ * mpi_msg_consume- get the MPI message from outbound queue message table.
+ * @pm8001_ha: our hba card information
+ * @circularQ: the outbound queue table.
+ * @messagePtr1: the message contents of this outbound message.
+ * @pBC: the message size.
+ */
+static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
+ struct outbound_queue_table *circularQ,
+ void **messagePtr1, u8 *pBC)
+{
+ struct mpi_msg_hdr *msgHeader;
+ __le32 msgHeader_tmp;
+ u32 header_tmp;
+ do {
+ /* If there are not-yet-delivered messages ... */
+ if (circularQ->producer_index != circularQ->consumer_idx) {
+ /*Get the pointer to the circular queue buffer element*/
+ msgHeader = (struct mpi_msg_hdr *)
+ (circularQ->base_virt +
+ circularQ->consumer_idx * 64);
+ /* read header */
+ header_tmp = pm8001_read_32(msgHeader);
+ msgHeader_tmp = cpu_to_le32(header_tmp);
+ if (0 != (msgHeader_tmp & 0x80000000)) {
+ if (OPC_OUB_SKIP_ENTRY !=
+ (msgHeader_tmp & 0xfff)) {
+ *messagePtr1 =
+ ((u8 *)msgHeader) +
+ sizeof(struct mpi_msg_hdr);
+ *pBC = (u8)((msgHeader_tmp >> 24) &
+ 0x1f);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(": CI=%d PI=%d "
+ "msgHeader=%x\n",
+ circularQ->consumer_idx,
+ circularQ->producer_index,
+ msgHeader_tmp));
+ return MPI_IO_STATUS_SUCCESS;
+ } else {
+ circularQ->consumer_idx =
+ (circularQ->consumer_idx +
+ ((msgHeader_tmp >> 24) & 0x1f))
+ % 256;
+ msgHeader_tmp = 0;
+ pm8001_write_32(msgHeader, 0, 0);
+ /* update the CI of outbound queue */
+ pm8001_cw32(pm8001_ha,
+ circularQ->ci_pci_bar,
+ circularQ->ci_offset,
+ circularQ->consumer_idx);
+ }
+ } else {
+ circularQ->consumer_idx =
+ (circularQ->consumer_idx +
+ ((msgHeader_tmp >> 24) & 0x1f)) % 256;
+ msgHeader_tmp = 0;
+ pm8001_write_32(msgHeader, 0, 0);
+ /* update the CI of outbound queue */
+ pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar,
+ circularQ->ci_offset,
+ circularQ->consumer_idx);
+ return MPI_IO_STATUS_FAIL;
+ }
+ } else {
+ u32 producer_index;
+ void *pi_virt = circularQ->pi_virt;
+ /* Update the producer index from SPC */
+ producer_index = pm8001_read_32(pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_index);
+ }
+ } while (circularQ->producer_index != circularQ->consumer_idx);
+ /* while we don't have any more not-yet-delivered message */
+ /* report empty */
+ return MPI_IO_STATUS_BUSY;
+}
+
+static void pm8001_work_queue(struct work_struct *work)
+{
+ struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct pm8001_wq *wq = container_of(dw, struct pm8001_wq, work_q);
+ struct pm8001_device *pm8001_dev;
+ struct domain_device *dev;
+
+ switch (wq->handler) {
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ case IO_DS_IN_ERROR:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ pm8001_dev = wq->data;
+ dev = pm8001_dev->sas_device;
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ }
+ list_del(&wq->entry);
+ kfree(wq);
+}
+
+static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
+ int handler)
+{
+ struct pm8001_wq *wq;
+ int ret = 0;
+
+ wq = kmalloc(sizeof(struct pm8001_wq), GFP_ATOMIC);
+ if (wq) {
+ wq->pm8001_ha = pm8001_ha;
+ wq->data = data;
+ wq->handler = handler;
+ INIT_DELAYED_WORK(&wq->work_q, pm8001_work_queue);
+ list_add_tail(&wq->entry, &pm8001_ha->wq_list);
+ schedule_delayed_work(&wq->work_q, 0);
+ } else
+ ret = -ENOMEM;
+
+ return ret;
+}
+
+/**
+ * mpi_ssp_completion- process the event that FW response to the SSP request.
+ * @pm8001_ha: our hba card information
+ * @piomb: the message contents of this outbound message.
+ *
+ * When FW has completed a ssp request for example a IO request, after it has
+ * filled the SG data with the data, it will trigger this event represent
+ * that he has finished the job,please check the coresponding buffer.
+ * So we will tell the caller who maybe waiting the result to tell upper layer
+ * that the task has been finished.
+ */
+static void
+mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status;
+ u32 param;
+ u32 tag;
+ struct ssp_completion_resp *psspPayload;
+ struct task_status_struct *ts;
+ struct ssp_response_iu *iu;
+ struct pm8001_device *pm8001_dev;
+ psspPayload = (struct ssp_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psspPayload->status);
+ tag = le32_to_cpu(psspPayload->tag);
+ ccb = &pm8001_ha->ccb_info[tag];
+ pm8001_dev = ccb->device;
+ param = le32_to_cpu(psspPayload->param);
+
+ t = ccb->task;
+
+ if (status && status != IO_UNDERFLOW)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sas IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS"
+ ",param = %d \n", param));
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ } else {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PROTO_RESPONSE;
+ ts->residual = param;
+ iu = &psspPayload->ssp_resp_iu;
+ sas_ssp_task_response(pm8001_ha->dev, t, iu);
+ }
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB Tag \n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ break;
+ case IO_UNDERFLOW:
+ /* SSP Completion with error */
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW"
+ ",param = %d \n", param));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_NO_DEVICE\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_DMA:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_DMA\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_PORT_IN_RESET:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_PORT_IN_RESET\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_DS_NON_OPERATIONAL);
+ break;
+ case IO_DS_IN_RECOVERY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_IN_RECOVERY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_TM_TAG_NOT_FOUND:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_TM_TAG_NOT_FOUND\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_SSP_EXT_IU_ZERO_LEN_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_SSP_EXT_IU_ZERO_LEN_ERROR\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", status));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ break;
+ }
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("scsi_status = %x \n ",
+ psspPayload->ssp_resp_iu.status));
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+ " io_status 0x%x resp 0x%x "
+ "stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ struct sas_task *t;
+ unsigned long flags;
+ struct task_status_struct *ts;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct ssp_event_resp *psspPayload =
+ (struct ssp_event_resp *)(piomb + 4);
+ u32 event = le32_to_cpu(psspPayload->event);
+ u32 tag = le32_to_cpu(psspPayload->tag);
+ u32 port_id = le32_to_cpu(psspPayload->port_id);
+ u32 dev_id = le32_to_cpu(psspPayload->device_id);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ if (event)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sas IO status 0x%x\n", event));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("port_id = %x,device_id = %x\n",
+ port_id, dev_id));
+ switch (event) {
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n");)
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+ "_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ if (!t->uldd_task)
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_XFER_ERROR_UNEXPECTED_PHASE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ case IO_XFER_CMD_FRAME_ISSUED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" IO_XFER_CMD_FRAME_ISSUED\n"));
+ return;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", event));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+ " event 0x%x resp 0x%x "
+ "stat 0x%x but aborted by upper layer!\n",
+ t, event, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 param;
+ u32 status;
+ u32 tag;
+ struct sata_completion_resp *psataPayload;
+ struct task_status_struct *ts;
+ struct ata_task_resp *resp ;
+ u32 *sata_resp;
+ struct pm8001_device *pm8001_dev;
+
+ psataPayload = (struct sata_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psataPayload->status);
+ tag = le32_to_cpu(psataPayload->tag);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ param = le32_to_cpu(psataPayload->param);
+ t = ccb->task;
+ ts = &t->task_status;
+ pm8001_dev = ccb->device;
+ if (status)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sata IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ if (param == 0) {
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ } else {
+ u8 len;
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PROTO_RESPONSE;
+ ts->residual = param;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("SAS_PROTO_RESPONSE len = %d\n",
+ param));
+ sata_resp = &psataPayload->sata_resp[0];
+ resp = (struct ata_task_resp *)ts->buf;
+ if (t->ata_task.dma_xfer == 0 &&
+ t->data_dir == PCI_DMA_FROMDEVICE) {
+ len = sizeof(struct pio_setup_fis);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("PIO read len = %d\n", len));
+ } else if (t->ata_task.use_ncq) {
+ len = sizeof(struct set_dev_bits_fis);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("FPDMA len = %d\n", len));
+ } else {
+ len = sizeof(struct dev_to_host_fis);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("other len = %d\n", len));
+ }
+ if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
+ resp->frame_len = len;
+ memcpy(&resp->ending_fis[0], sata_resp, len);
+ ts->buf_valid_size = sizeof(*resp);
+ } else
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("response to large \n"));
+ }
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB Tag \n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ /* following cases are to do cases */
+ case IO_UNDERFLOW:
+ /* SATA Completion with error */
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_UNDERFLOW param = %d\n", param));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ ts->residual = param;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_NO_DEVICE\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+ "_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*in order to force CPU ordering*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES"
+ "_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_DMA:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_DMA\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ break;
+ case IO_XFER_ERROR_SATA_LINK_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_SATA_LINK_TIMEOUT\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_PORT_IN_RESET:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_PORT_IN_RESET\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha, pm8001_dev,
+ IO_DS_NON_OPERATIONAL);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_DS_IN_RECOVERY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" IO_DS_IN_RECOVERY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_IN_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_IN_ERROR\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha, pm8001_dev,
+ IO_DS_IN_ERROR);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", status));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task 0x%p done with io_status 0x%x"
+ " resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* ditto */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ struct sas_task *t;
+ unsigned long flags;
+ struct task_status_struct *ts;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct sata_event_resp *psataPayload =
+ (struct sata_event_resp *)(piomb + 4);
+ u32 event = le32_to_cpu(psataPayload->event);
+ u32 tag = le32_to_cpu(psataPayload->tag);
+ u32 port_id = le32_to_cpu(psataPayload->port_id);
+ u32 dev_id = le32_to_cpu(psataPayload->device_id);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
+ pm8001_dev = ccb->device;
+ if (event)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("sata IO status 0x%x\n", event));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+ ts = &t->task_status;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("port_id = %x,device_id = %x\n",
+ port_id, dev_id));
+ switch (event) {
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_INTERRUPTED;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+ "_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_EPROTO;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ if (!t->uldd_task) {
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/*ditto*/
+ t->task_done(t);
+ return;
+ }
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_NAK_RECEIVED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_PEER_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PEER_ABORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_UNEXPECTED_PHASE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_OFFSET_MISMATCH:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ case IO_XFER_CMD_FRAME_ISSUED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_CMD_FRAME_ISSUED\n"));
+ break;
+ case IO_XFER_PIO_SETUP_ERROR:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_PIO_SETUP_ERROR\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", event));
+ /* not allowed case. Therefore, return failed status */
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_TO;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task 0x%p done with io_status 0x%x"
+ " resp 0x%x stat 0x%x but aborted by upper layer!\n",
+ t, event, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 param;
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status;
+ u32 tag;
+ struct smp_completion_resp *psmpPayload;
+ struct task_status_struct *ts;
+ struct pm8001_device *pm8001_dev;
+
+ psmpPayload = (struct smp_completion_resp *)(piomb + 4);
+ status = le32_to_cpu(psmpPayload->status);
+ tag = le32_to_cpu(psmpPayload->tag);
+
+ ccb = &pm8001_ha->ccb_info[tag];
+ param = le32_to_cpu(psmpPayload->param);
+ t = ccb->task;
+ ts = &t->task_status;
+ pm8001_dev = ccb->device;
+ if (status)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("smp IO status 0x%x\n", status));
+ if (unlikely(!t || !t->lldd_task || !t->dev))
+ return;
+
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_ABORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ABORTED IOMB\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_ABORTED_TASK;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_OVERFLOW:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ break;
+ case IO_NO_DEVICE:
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_NO_DEVICE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ case IO_ERROR_HW_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ERROR_HW_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_BUSY;
+ break;
+ case IO_XFER_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_BUSY;
+ break;
+ case IO_XFER_ERROR_PHY_NOT_READY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_BUSY;
+ break;
+ case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ case IO_OPEN_CNX_ERROR_BREAK:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+ break;
+ case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ pm8001_handle_event(pm8001_ha,
+ pm8001_dev,
+ IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+ break;
+ case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+ break;
+ case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+ "NOT_SUPPORTED\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+ break;
+ case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+ break;
+ case IO_XFER_ERROR_RX_FRAME:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_ERROR_RX_FRAME\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_ERROR_INTERNAL_SMP_RESOURCE:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_ERROR_INTERNAL_SMP_RESOURCE\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_QUEUE_FULL;
+ break;
+ case IO_PORT_IN_RESET:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_PORT_IN_RESET\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_DS_NON_OPERATIONAL:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ break;
+ case IO_DS_IN_RECOVERY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_DS_IN_RECOVERY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ break;
+ default:
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("Unknown status 0x%x\n", status));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_DEV_NO_RESPONSE;
+ /* not allowed case. Therefore, return failed status */
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+ " io_status 0x%x resp 0x%x "
+ "stat 0x%x but aborted by upper layer!\n",
+ t, status, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ t->task_done(t);
+ }
+}
+
+static void
+mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct set_dev_state_resp *pPayload =
+ (struct set_dev_state_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(pPayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ struct pm8001_device *pm8001_dev = ccb->device;
+ u32 status = le32_to_cpu(pPayload->status);
+ u32 device_id = le32_to_cpu(pPayload->device_id);
+ u8 pds = le32_to_cpu(pPayload->pds_nds) | PDS_BITS;
+ u8 nds = le32_to_cpu(pPayload->pds_nds) | NDS_BITS;
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set device id = 0x%x state "
+ "from 0x%x to 0x%x status = 0x%x!\n",
+ device_id, pds, nds, status));
+ complete(pm8001_dev->setds_completion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static void
+mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct get_nvm_data_resp *pPayload =
+ (struct get_nvm_data_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(pPayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ u32 dlen_status = le32_to_cpu(pPayload->dlen_status);
+ complete(pm8001_ha->nvmd_completion);
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n"));
+ if ((dlen_status & NVMD_STAT) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Set nvm data error!\n"));
+ return;
+ }
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static void
+mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct fw_control_ex *fw_control_context;
+ struct get_nvm_data_resp *pPayload =
+ (struct get_nvm_data_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(pPayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ u32 dlen_status = le32_to_cpu(pPayload->dlen_status);
+ u32 ir_tds_bn_dps_das_nvm =
+ le32_to_cpu(pPayload->ir_tda_bn_dps_das_nvm);
+ void *virt_addr = pm8001_ha->memoryMap.region[NVMD].virt_ptr;
+ fw_control_context = ccb->fw_control_context;
+
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Get nvm data complete!\n"));
+ if ((dlen_status & NVMD_STAT) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Get nvm data error!\n"));
+ complete(pm8001_ha->nvmd_completion);
+ return;
+ }
+
+ if (ir_tds_bn_dps_das_nvm & IPMode) {
+ /* indirect mode - IR bit set */
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Get NVMD success, IR=1\n"));
+ if ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == TWI_DEVICE) {
+ if (ir_tds_bn_dps_das_nvm == 0x80a80200) {
+ memcpy(pm8001_ha->sas_addr,
+ ((u8 *)virt_addr + 4),
+ SAS_ADDR_SIZE);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Get SAS address"
+ " from VPD successfully!\n"));
+ }
+ } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == C_SEEPROM)
+ || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == VPD_FLASH) ||
+ ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == EXPAN_ROM)) {
+ ;
+ } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == AAP1_RDUMP)
+ || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == IOP_RDUMP)) {
+ ;
+ } else {
+ /* Should not be happened*/
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("(IR=1)Wrong Device type 0x%x\n",
+ ir_tds_bn_dps_das_nvm));
+ }
+ } else /* direct mode */{
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Get NVMD success, IR=0, dataLen=%d\n",
+ (dlen_status & NVMD_LEN) >> 24));
+ }
+ memcpy(fw_control_context->usrAddr,
+ pm8001_ha->memoryMap.region[NVMD].virt_ptr,
+ fw_control_context->len);
+ complete(pm8001_ha->nvmd_completion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct local_phy_ctl_resp *pPayload =
+ (struct local_phy_ctl_resp *)(piomb + 4);
+ u32 status = le32_to_cpu(pPayload->status);
+ u32 phy_id = le32_to_cpu(pPayload->phyop_phyid) & ID_BITS;
+ u32 phy_op = le32_to_cpu(pPayload->phyop_phyid) & OP_BITS;
+ if (status != 0) {
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("%x phy execute %x phy op failed! \n",
+ phy_id, phy_op));
+ } else
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("%x phy execute %x phy op success! \n",
+ phy_id, phy_op));
+ return 0;
+}
+
+/**
+ * pm8001_bytes_dmaed - one of the interface function communication with libsas
+ * @pm8001_ha: our hba card information
+ * @i: which phy that received the event.
+ *
+ * when HBA driver received the identify done event or initiate FIS received
+ * event(for SATA), it will invoke this function to notify the sas layer that
+ * the sas toplogy has formed, please discover the the whole sas domain,
+ * while receive a broadcast(change) primitive just tell the sas
+ * layer to discover the changed domain rather than the whole domain.
+ */
+static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i)
+{
+ struct pm8001_phy *phy = &pm8001_ha->phy[i];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_ha_struct *sas_ha;
+ if (!phy->phy_attached)
+ return;
+
+ sas_ha = pm8001_ha->sas;
+ if (sas_phy->phy) {
+ struct sas_phy *sphy = sas_phy->phy;
+ sphy->negotiated_linkrate = sas_phy->linkrate;
+ sphy->minimum_linkrate = phy->minimum_linkrate;
+ sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sphy->maximum_linkrate = phy->maximum_linkrate;
+ sphy->maximum_linkrate_hw = phy->maximum_linkrate;
+ }
+
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ struct sas_identify_frame *id;
+ id = (struct sas_identify_frame *)phy->frame_rcvd;
+ id->dev_type = phy->identify.device_type;
+ id->initiator_bits = SAS_PROTOCOL_ALL;
+ id->target_bits = phy->identify.target_port_protocols;
+ } else if (phy->phy_type & PORT_TYPE_SATA) {
+ /*Nothing*/
+ }
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("phy %d byte dmaded.\n", i));
+
+ sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
+ pm8001_ha->sas->notify_port_event(sas_phy, PORTE_BYTES_DMAED);
+}
+
+/* Get the link rate speed */
+static void get_lrate_mode(struct pm8001_phy *phy, u8 link_rate)
+{
+ struct sas_phy *sas_phy = phy->sas_phy.phy;
+
+ switch (link_rate) {
+ case PHY_SPEED_60:
+ phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS;
+ phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
+ break;
+ case PHY_SPEED_30:
+ phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS;
+ phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
+ break;
+ case PHY_SPEED_15:
+ phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS;
+ phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
+ break;
+ }
+ sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
+ sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_6_0_GBPS;
+ sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sas_phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
+ sas_phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+}
+
+/**
+ * asd_get_attached_sas_addr -- extract/generate attached SAS address
+ * @phy: pointer to asd_phy
+ * @sas_addr: pointer to buffer where the SAS address is to be written
+ *
+ * This function extracts the SAS address from an IDENTIFY frame
+ * received. If OOB is SATA, then a SAS address is generated from the
+ * HA tables.
+ *
+ * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
+ * buffer.
+ */
+static void pm8001_get_attached_sas_addr(struct pm8001_phy *phy,
+ u8 *sas_addr)
+{
+ if (phy->sas_phy.frame_rcvd[0] == 0x34
+ && phy->sas_phy.oob_mode == SATA_OOB_MODE) {
+ struct pm8001_hba_info *pm8001_ha = phy->sas_phy.ha->lldd_ha;
+ /* FIS device-to-host */
+ u64 addr = be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr);
+ addr += phy->sas_phy.id;
+ *(__be64 *)sas_addr = cpu_to_be64(addr);
+ } else {
+ struct sas_identify_frame *idframe =
+ (void *) phy->sas_phy.frame_rcvd;
+ memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);
+ }
+}
+
+/**
+ * pm8001_hw_event_ack_req- For PM8001,some events need to acknowage to FW.
+ * @pm8001_ha: our hba card information
+ * @Qnum: the outbound queue message number.
+ * @SEA: source of event to ack
+ * @port_id: port id.
+ * @phyId: phy id.
+ * @param0: parameter 0.
+ * @param1: parameter 1.
+ */
+static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
+ u32 Qnum, u32 SEA, u32 port_id, u32 phyId, u32 param0, u32 param1)
+{
+ struct hw_event_ack_req payload;
+ u32 opc = OPC_INB_SAS_HW_EVENT_ACK;
+
+ struct inbound_queue_table *circularQ;
+
+ memset((u8 *)&payload, 0, sizeof(payload));
+ circularQ = &pm8001_ha->inbnd_q_tbl[Qnum];
+ payload.tag = 1;
+ payload.sea_phyid_portid = cpu_to_le32(((SEA & 0xFFFF) << 8) |
+ ((phyId & 0x0F) << 4) | (port_id & 0x0F));
+ payload.param0 = cpu_to_le32(param0);
+ payload.param1 = cpu_to_le32(param1);
+ mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+}
+
+static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+ u32 phyId, u32 phy_op);
+
+/**
+ * hw_event_sas_phy_up -FW tells me a SAS phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 link_rate =
+ (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ unsigned long flags;
+ u8 deviceType = pPayload->sas_identify.dev_type;
+
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_SAS_PHY_UP \n"));
+
+ switch (deviceType) {
+ case SAS_PHY_UNUSED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("device type no device.\n"));
+ break;
+ case SAS_END_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("end device.\n"));
+ pm8001_chip_phy_ctl_req(pm8001_ha, phy_id,
+ PHY_NOTIFY_ENABLE_SPINUP);
+ get_lrate_mode(phy, link_rate);
+ break;
+ case SAS_EDGE_EXPANDER_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("expander device.\n"));
+ get_lrate_mode(phy, link_rate);
+ break;
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("fanout expander device.\n"));
+ get_lrate_mode(phy, link_rate);
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("unkown device type(%x)\n", deviceType));
+ break;
+ }
+ phy->phy_type |= PORT_TYPE_SAS;
+ phy->identify.device_type = deviceType;
+ phy->phy_attached = 1;
+ if (phy->identify.device_type == SAS_END_DEV)
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != NO_DEVICE)
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SMP;
+ phy->sas_phy.oob_mode = SAS_OOB_MODE;
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+ memcpy(phy->frame_rcvd, &pPayload->sas_identify,
+ sizeof(struct sas_identify_frame)-4);
+ phy->frame_rcvd_size = sizeof(struct sas_identify_frame) - 4;
+ pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+ spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+ if (pm8001_ha->flags == PM8001F_RUN_TIME)
+ mdelay(200);/*delay a moment to wait disk to spinup*/
+ pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_sata_phy_up -FW tells me a SATA phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 link_rate =
+ (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ unsigned long flags;
+ get_lrate_mode(phy, link_rate);
+ phy->phy_type |= PORT_TYPE_SATA;
+ phy->phy_attached = 1;
+ phy->sas_phy.oob_mode = SATA_OOB_MODE;
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+ spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+ memcpy(phy->frame_rcvd, ((u8 *)&pPayload->sata_fis - 4),
+ sizeof(struct dev_to_host_fis));
+ phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+ phy->identify.device_type = SATA_DEV;
+ pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+ spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+ pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_phy_down -we should notify the libsas the phy is down.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ u32 npip_portstate = le32_to_cpu(pPayload->npip_portstate);
+ u8 portstate = (u8)(npip_portstate & 0x0000000F);
+
+ switch (portstate) {
+ case PORT_VALID:
+ break;
+ case PORT_INVALID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" PortInvalid portID %d \n", port_id));
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" Last phy Down and port invalid\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+ port_id, phy_id, 0, 0);
+ break;
+ case PORT_IN_RESET:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" PortInReset portID %d \n", port_id));
+ break;
+ case PORT_NOT_ESTABLISHED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n"));
+ break;
+ case PORT_LOSTCOMM:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" phy Down and PORT_LOSTCOMM\n"));
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" Last phy Down and port invalid\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+ port_id, phy_id, 0, 0);
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" phy Down and(default) = %x\n",
+ portstate));
+ break;
+
+ }
+}
+
+/**
+ * mpi_reg_resp -process register device ID response.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ *
+ * when sas layer find a device it will notify LLDD, then the driver register
+ * the domain device to FW, this event is the return device ID which the FW
+ * has assigned, from now,inter-communication with FW is no longer using the
+ * SAS address, use device ID which FW assigned.
+ */
+static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 status;
+ u32 device_id;
+ u32 htag;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_device *pm8001_dev;
+ struct dev_reg_resp *registerRespPayload =
+ (struct dev_reg_resp *)(piomb + 4);
+
+ htag = le32_to_cpu(registerRespPayload->tag);
+ ccb = &pm8001_ha->ccb_info[registerRespPayload->tag];
+ pm8001_dev = ccb->device;
+ status = le32_to_cpu(registerRespPayload->status);
+ device_id = le32_to_cpu(registerRespPayload->device_id);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" register device is status = %d\n", status));
+ switch (status) {
+ case DEVREG_SUCCESS:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("DEVREG_SUCCESS\n"));
+ pm8001_dev->device_id = device_id;
+ break;
+ case DEVREG_FAILURE_OUT_OF_RESOURCE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_OUT_OF_RESOURCE\n"));
+ break;
+ case DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED\n"));
+ break;
+ case DEVREG_FAILURE_INVALID_PHY_ID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_INVALID_PHY_ID\n"));
+ break;
+ case DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED\n"));
+ break;
+ case DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE\n"));
+ break;
+ case DEVREG_FAILURE_PORT_NOT_VALID_STATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_PORT_NOT_VALID_STATE\n"));
+ break;
+ case DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_UNSORPORTED\n"));
+ break;
+ }
+ complete(pm8001_dev->dcompletion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, htag);
+ return 0;
+}
+
+static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 status;
+ u32 device_id;
+ struct dev_reg_resp *registerRespPayload =
+ (struct dev_reg_resp *)(piomb + 4);
+
+ status = le32_to_cpu(registerRespPayload->status);
+ device_id = le32_to_cpu(registerRespPayload->device_id);
+ if (status != 0)
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" deregister device failed ,status = %x"
+ ", device_id = %x\n", status, device_id));
+ return 0;
+}
+
+static int
+mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 status;
+ struct fw_control_ex fw_control_context;
+ struct fw_flash_Update_resp *ppayload =
+ (struct fw_flash_Update_resp *)(piomb + 4);
+ u32 tag = le32_to_cpu(ppayload->tag);
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+ status = le32_to_cpu(ppayload->status);
+ memcpy(&fw_control_context,
+ ccb->fw_control_context,
+ sizeof(fw_control_context));
+ switch (status) {
+ case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_COMPLETE_PENDING_REBOOT\n"));
+ break;
+ case FLASH_UPDATE_IN_PROGRESS:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_IN_PROGRESS\n"));
+ break;
+ case FLASH_UPDATE_HDR_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_HDR_ERR\n"));
+ break;
+ case FLASH_UPDATE_OFFSET_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_OFFSET_ERR\n"));
+ break;
+ case FLASH_UPDATE_CRC_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_CRC_ERR\n"));
+ break;
+ case FLASH_UPDATE_LENGTH_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_LENGTH_ERR\n"));
+ break;
+ case FLASH_UPDATE_HW_ERR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_HW_ERR\n"));
+ break;
+ case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_DNLD_NOT_SUPPORTED\n"));
+ break;
+ case FLASH_UPDATE_DISABLED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(": FLASH_UPDATE_DISABLED\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("No matched status = %d\n", status));
+ break;
+ }
+ ccb->fw_control_context->fw_control->retcode = status;
+ pci_free_consistent(pm8001_ha->pdev,
+ fw_control_context.len,
+ fw_control_context.virtAddr,
+ fw_control_context.phys_addr);
+ complete(pm8001_ha->nvmd_completion);
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, tag);
+ return 0;
+}
+
+static int
+mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+ u32 status;
+ int i;
+ struct general_event_resp *pPayload =
+ (struct general_event_resp *)(piomb + 4);
+ status = le32_to_cpu(pPayload->status);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk(" status = 0x%x\n", status));
+ for (i = 0; i < GENERAL_EVENT_PAYLOAD; i++)
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("inb_IOMB_payload[0x%x] 0x%x, \n", i,
+ pPayload->inb_IOMB_payload[i]));
+ return 0;
+}
+
+static int
+mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ struct sas_task *t;
+ struct pm8001_ccb_info *ccb;
+ unsigned long flags;
+ u32 status ;
+ u32 tag, scp;
+ struct task_status_struct *ts;
+
+ struct task_abort_resp *pPayload =
+ (struct task_abort_resp *)(piomb + 4);
+ ccb = &pm8001_ha->ccb_info[pPayload->tag];
+ t = ccb->task;
+
+
+ status = le32_to_cpu(pPayload->status);
+ tag = le32_to_cpu(pPayload->tag);
+ scp = le32_to_cpu(pPayload->scp);
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk(" status = 0x%x\n", status));
+ if (t == NULL)
+ return -1;
+ ts = &t->task_status;
+ if (status != 0)
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("task abort failed status 0x%x ,"
+ "tag = 0x%x, scp= 0x%x\n", status, tag, scp));
+ switch (status) {
+ case IO_SUCCESS:
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAM_GOOD;
+ break;
+ case IO_NOT_VALID:
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_NOT_VALID\n"));
+ ts->resp = TMF_RESP_FUNC_FAILED;
+ break;
+ }
+ spin_lock_irqsave(&t->task_state_lock, flags);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ spin_unlock_irqrestore(&t->task_state_lock, flags);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, pPayload->tag);
+ mb();
+ t->task_done(t);
+ return 0;
+}
+
+/**
+ * mpi_hw_event -The hw event has come.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
+{
+ unsigned long flags;
+ struct hw_event_resp *pPayload =
+ (struct hw_event_resp *)(piomb + 4);
+ u32 lr_evt_status_phyid_portid =
+ le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+ u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F);
+ u8 phy_id =
+ (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+ u16 eventType =
+ (u16)((lr_evt_status_phyid_portid & 0x00FFFF00) >> 8);
+ u8 status =
+ (u8)((lr_evt_status_phyid_portid & 0x0F000000) >> 24);
+ struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("outbound queue HW event & event type : "));
+ switch (eventType) {
+ case HW_EVENT_PHY_START_STATUS:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_START_STATUS"
+ " status = %x\n", status));
+ if (status == 0) {
+ phy->phy_state = 1;
+ if (pm8001_ha->flags == PM8001F_RUN_TIME)
+ complete(phy->enable_completion);
+ }
+ break;
+ case HW_EVENT_SAS_PHY_UP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_START_STATUS \n"));
+ hw_event_sas_phy_up(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_SATA_PHY_UP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_SATA_PHY_UP \n"));
+ hw_event_sata_phy_up(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_PHY_STOP_STATUS:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_STOP_STATUS "
+ "status = %x\n", status));
+ if (status == 0)
+ phy->phy_state = 0;
+ break;
+ case HW_EVENT_SATA_SPINUP_HOLD:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_SATA_SPINUP_HOLD \n"));
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
+ break;
+ case HW_EVENT_PHY_DOWN:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_DOWN \n"));
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
+ phy->phy_attached = 0;
+ phy->phy_state = 0;
+ hw_event_phy_down(pm8001_ha, piomb);
+ break;
+ case HW_EVENT_PORT_INVALID:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_INVALID\n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ /* the broadcast change primitive received, tell the LIBSAS this event
+ to revalidate the sas domain*/
+ case HW_EVENT_BROADCAST_CHANGE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_BROADCAST_CHANGE\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_BROADCAST_CHANGE,
+ port_id, phy_id, 1, 0);
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_CHANGE;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ break;
+ case HW_EVENT_PHY_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PHY_ERROR\n"));
+ sas_phy_disconnected(&phy->sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
+ break;
+ case HW_EVENT_BROADCAST_EXP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_BROADCAST_EXP\n"));
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_EXP;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ break;
+ case HW_EVENT_LINK_ERR_INVALID_DWORD:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_DISPARITY_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_DISPARITY_ERROR\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_DISPARITY_ERROR,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_CODE_VIOLATION:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_CODE_VIOLATION\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_CODE_VIOLATION,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_MALFUNCTION:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_MALFUNCTION\n"));
+ break;
+ case HW_EVENT_BROADCAST_SES:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_BROADCAST_SES\n"));
+ spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+ sas_phy->sas_prim = HW_EVENT_BROADCAST_SES;
+ spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+ sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+ break;
+ case HW_EVENT_INBOUND_CRC_ERROR:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_INBOUND_CRC_ERROR\n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_INBOUND_CRC_ERROR,
+ port_id, phy_id, 0, 0);
+ break;
+ case HW_EVENT_HARD_RESET_RECEIVED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_HARD_RESET_RECEIVED\n"));
+ sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
+ break;
+ case HW_EVENT_ID_FRAME_TIMEOUT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_ID_FRAME_TIMEOUT\n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_LINK_ERR_PHY_RESET_FAILED:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_LINK_ERR_PHY_RESET_FAILED \n"));
+ pm8001_hw_event_ack_req(pm8001_ha, 0,
+ HW_EVENT_LINK_ERR_PHY_RESET_FAILED,
+ port_id, phy_id, 0, 0);
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_PORT_RESET_TIMER_TMO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RESET_TIMER_TMO \n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_PORT_RECOVERY_TIMER_TMO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RECOVERY_TIMER_TMO \n"));
+ sas_phy_disconnected(sas_phy);
+ phy->phy_attached = 0;
+ sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+ break;
+ case HW_EVENT_PORT_RECOVER:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RECOVER \n"));
+ break;
+ case HW_EVENT_PORT_RESET_COMPLETE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("HW_EVENT_PORT_RESET_COMPLETE \n"));
+ break;
+ case EVENT_BROADCAST_ASYNCH_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("EVENT_BROADCAST_ASYNCH_EVENT\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Unknown event type = %x\n", eventType));
+ break;
+ }
+ return 0;
+}
+
+/**
+ * process_one_iomb - process one outbound Queue memory block
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+ u32 pHeader = (u32)*(u32 *)piomb;
+ u8 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF);
+
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:"));
+
+ switch (opc) {
+ case OPC_OUB_ECHO:
+ PM8001_MSG_DBG(pm8001_ha, pm8001_printk("OPC_OUB_ECHO \n"));
+ break;
+ case OPC_OUB_HW_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_HW_EVENT \n"));
+ mpi_hw_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_COMP \n"));
+ mpi_ssp_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SMP_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SMP_COMP \n"));
+ mpi_smp_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_LOCAL_PHY_CNTRL:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_LOCAL_PHY_CNTRL\n"));
+ mpi_local_phy_ctl(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEV_REGIST:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEV_REGIST \n"));
+ mpi_reg_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEREG_DEV:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("unresgister the deviece \n"));
+ mpi_dereg_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_DEV_HANDLE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_DEV_HANDLE \n"));
+ break;
+ case OPC_OUB_SATA_COMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SATA_COMP \n"));
+ mpi_sata_completion(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SATA_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SATA_EVENT \n"));
+ mpi_sata_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_EVENT\n"));
+ mpi_ssp_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEV_HANDLE_ARRIV:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEV_HANDLE_ARRIV\n"));
+ /*This is for target*/
+ break;
+ case OPC_OUB_SSP_RECV_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_RECV_EVENT\n"));
+ /*This is for target*/
+ break;
+ case OPC_OUB_DEV_INFO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEV_INFO\n"));
+ break;
+ case OPC_OUB_FW_FLASH_UPDATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_FW_FLASH_UPDATE\n"));
+ mpi_fw_flash_update_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GPIO_RESPONSE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
+ break;
+ case OPC_OUB_GPIO_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
+ break;
+ case OPC_OUB_GENERAL_EVENT:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GENERAL_EVENT\n"));
+ mpi_general_event(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SSP_ABORT_RSP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SSP_ABORT_RSP\n"));
+ mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SATA_ABORT_RSP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SATA_ABORT_RSP\n"));
+ mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SAS_DIAG_MODE_START_END:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_DIAG_MODE_START_END\n"));
+ break;
+ case OPC_OUB_SAS_DIAG_EXECUTE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_DIAG_EXECUTE\n"));
+ break;
+ case OPC_OUB_GET_TIME_STAMP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_TIME_STAMP\n"));
+ break;
+ case OPC_OUB_SAS_HW_EVENT_ACK:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_HW_EVENT_ACK\n"));
+ break;
+ case OPC_OUB_PORT_CONTROL:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_PORT_CONTROL\n"));
+ break;
+ case OPC_OUB_SMP_ABORT_RSP:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SMP_ABORT_RSP\n"));
+ mpi_task_abort_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_NVMD_DATA:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_NVMD_DATA\n"));
+ mpi_get_nvmd_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_SET_NVMD_DATA:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SET_NVMD_DATA\n"));
+ mpi_set_nvmd_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_DEVICE_HANDLE_REMOVAL:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_DEVICE_HANDLE_REMOVAL\n"));
+ break;
+ case OPC_OUB_SET_DEVICE_STATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SET_DEVICE_STATE\n"));
+ mpi_set_dev_state_resp(pm8001_ha, piomb);
+ break;
+ case OPC_OUB_GET_DEVICE_STATE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_GET_DEVICE_STATE\n"));
+ break;
+ case OPC_OUB_SET_DEV_INFO:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SET_DEV_INFO\n"));
+ break;
+ case OPC_OUB_SAS_RE_INITIALIZE:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("OPC_OUB_SAS_RE_INITIALIZE\n"));
+ break;
+ default:
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("Unknown outbound Queue IOMB OPC = %x\n",
+ opc));
+ break;
+ }
+}
+
+static int process_oq(struct pm8001_hba_info *pm8001_ha)
+{
+ struct outbound_queue_table *circularQ;
+ void *pMsg1 = NULL;
+ u8 bc = 0;
+ u32 ret = MPI_IO_STATUS_FAIL;
+
+ circularQ = &pm8001_ha->outbnd_q_tbl[0];
+ do {
+ ret = mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
+ if (MPI_IO_STATUS_SUCCESS == ret) {
+ /* process the outbound message */
+ process_one_iomb(pm8001_ha, (void *)(pMsg1 - 4));
+ /* free the message from the outbound circular buffer */
+ mpi_msg_free_set(pm8001_ha, pMsg1, circularQ, bc);
+ }
+ if (MPI_IO_STATUS_BUSY == ret) {
+ u32 producer_idx;
+ /* Update the producer index from SPC */
+ producer_idx = pm8001_read_32(circularQ->pi_virt);
+ circularQ->producer_index = cpu_to_le32(producer_idx);
+ if (circularQ->producer_index ==
+ circularQ->consumer_idx)
+ /* OQ is empty */
+ break;
+ }
+ } while (1);
+ return ret;
+}
+
+/* PCI_DMA_... to our direction translation. */
+static const u8 data_dir_flags[] = {
+ [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT,/* UNSPECIFIED */
+ [PCI_DMA_TODEVICE] = DATA_DIR_OUT,/* OUTBOUND */
+ [PCI_DMA_FROMDEVICE] = DATA_DIR_IN,/* INBOUND */
+ [PCI_DMA_NONE] = DATA_DIR_NONE,/* NO TRANSFER */
+};
+static void
+pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd)
+{
+ int i;
+ struct scatterlist *sg;
+ struct pm8001_prd *buf_prd = prd;
+
+ for_each_sg(scatter, sg, nr, i) {
+ buf_prd->addr = cpu_to_le64(sg_dma_address(sg));
+ buf_prd->im_len.len = cpu_to_le32(sg_dma_len(sg));
+ buf_prd->im_len.e = 0;
+ buf_prd++;
+ }
+}
+
+static void build_smp_cmd(u32 deviceID, u32 hTag, struct smp_req *psmp_cmd)
+{
+ psmp_cmd->tag = cpu_to_le32(hTag);
+ psmp_cmd->device_id = cpu_to_le32(deviceID);
+ psmp_cmd->len_ip_ir = cpu_to_le32(1|(1 << 1));
+}
+
+/**
+ * pm8001_chip_smp_req - send a SMP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ int elem, rc;
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct scatterlist *sg_req, *sg_resp;
+ u32 req_len, resp_len;
+ struct smp_req smp_cmd;
+ u32 opc;
+ struct inbound_queue_table *circularQ;
+
+ memset(&smp_cmd, 0, sizeof(smp_cmd));
+ /*
+ * DMA-map SMP request, response buffers
+ */
+ sg_req = &task->smp_task.smp_req;
+ elem = dma_map_sg(pm8001_ha->dev, sg_req, 1, PCI_DMA_TODEVICE);
+ if (!elem)
+ return -ENOMEM;
+ req_len = sg_dma_len(sg_req);
+
+ sg_resp = &task->smp_task.smp_resp;
+ elem = dma_map_sg(pm8001_ha->dev, sg_resp, 1, PCI_DMA_FROMDEVICE);
+ if (!elem) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ resp_len = sg_dma_len(sg_resp);
+ /* must be in dwords */
+ if ((req_len & 0x3) || (resp_len & 0x3)) {
+ rc = -EINVAL;
+ goto err_out_2;
+ }
+
+ opc = OPC_INB_SMP_REQUEST;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ smp_cmd.tag = cpu_to_le32(ccb->ccb_tag);
+ smp_cmd.long_smp_req.long_req_addr =
+ cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
+ smp_cmd.long_smp_req.long_req_size =
+ cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4);
+ smp_cmd.long_smp_req.long_resp_addr =
+ cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp));
+ smp_cmd.long_smp_req.long_resp_size =
+ cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
+ build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag, &smp_cmd);
+ mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd);
+ return 0;
+
+err_out_2:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1,
+ PCI_DMA_FROMDEVICE);
+err_out:
+ dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1,
+ PCI_DMA_TODEVICE);
+ return rc;
+}
+
+/**
+ * pm8001_chip_ssp_io_req - send a SSP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct ssp_ini_io_start_req ssp_cmd;
+ u32 tag = ccb->ccb_tag;
+ int ret;
+ __le64 phys_addr;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SSPINIIOSTART;
+ memset(&ssp_cmd, 0, sizeof(ssp_cmd));
+ memcpy(ssp_cmd.ssp_iu.lun, task->ssp_task.LUN, 8);
+ ssp_cmd.dir_m_tlr = data_dir_flags[task->data_dir] << 8 | 0x0;/*0 for
+ SAS 1.1 compatible TLR*/
+ ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id);
+ ssp_cmd.tag = cpu_to_le32(tag);
+ if (task->ssp_task.enable_first_burst)
+ ssp_cmd.ssp_iu.efb_prio_attr |= 0x80;
+ ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3);
+ ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7);
+ memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cdb, 16);
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
+ phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]));
+ ssp_cmd.addr_low = lower_32_bits(phys_addr);
+ ssp_cmd.addr_high = upper_32_bits(phys_addr);
+ ssp_cmd.esgl = cpu_to_le32(1<<31);
+ } else if (task->num_scatter == 1) {
+ __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+ ssp_cmd.addr_low = lower_32_bits(dma_addr);
+ ssp_cmd.addr_high = upper_32_bits(dma_addr);
+ ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.esgl = 0;
+ } else if (task->num_scatter == 0) {
+ ssp_cmd.addr_low = 0;
+ ssp_cmd.addr_high = 0;
+ ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+ ssp_cmd.esgl = 0;
+ }
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &ssp_cmd);
+ return ret;
+}
+
+static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_ha_dev = dev->lldd_dev;
+ u32 tag = ccb->ccb_tag;
+ int ret;
+ struct sata_start_req sata_cmd;
+ u32 hdr_tag, ncg_tag = 0;
+ __le64 phys_addr;
+ u32 ATAP = 0x0;
+ u32 dir;
+ struct inbound_queue_table *circularQ;
+ u32 opc = OPC_INB_SATA_HOST_OPSTART;
+ memset(&sata_cmd, 0, sizeof(sata_cmd));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ if (task->data_dir == PCI_DMA_NONE) {
+ ATAP = 0x04; /* no data*/
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("no data \n"));
+ } else if (likely(!task->ata_task.device_control_reg_update)) {
+ if (task->ata_task.dma_xfer) {
+ ATAP = 0x06; /* DMA */
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("DMA \n"));
+ } else {
+ ATAP = 0x05; /* PIO*/
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("PIO \n"));
+ }
+ if (task->ata_task.use_ncq &&
+ dev->sata_dev.command_set != ATAPI_COMMAND_SET) {
+ ATAP = 0x07; /* FPDMA */
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA \n"));
+ }
+ }
+ if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag))
+ ncg_tag = cpu_to_le32(hdr_tag);
+ dir = data_dir_flags[task->data_dir] << 8;
+ sata_cmd.tag = cpu_to_le32(tag);
+ sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+ sata_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.ncqtag_atap_dir_m =
+ cpu_to_le32(((ncg_tag & 0xff)<<16)|((ATAP & 0x3f) << 10) | dir);
+ sata_cmd.sata_fis = task->ata_task.fis;
+ if (likely(!task->ata_task.device_control_reg_update))
+ sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */
+ sata_cmd.sata_fis.flags &= 0xF0;/* PM_PORT field shall be 0 */
+ /* fill in PRD (scatter/gather) table, if any */
+ if (task->num_scatter > 1) {
+ pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
+ phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]));
+ sata_cmd.addr_low = lower_32_bits(phys_addr);
+ sata_cmd.addr_high = upper_32_bits(phys_addr);
+ sata_cmd.esgl = cpu_to_le32(1 << 31);
+ } else if (task->num_scatter == 1) {
+ __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+ sata_cmd.addr_low = lower_32_bits(dma_addr);
+ sata_cmd.addr_high = upper_32_bits(dma_addr);
+ sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.esgl = 0;
+ } else if (task->num_scatter == 0) {
+ sata_cmd.addr_low = 0;
+ sata_cmd.addr_high = 0;
+ sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+ sata_cmd.esgl = 0;
+ }
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd);
+ return ret;
+}
+
+/**
+ * pm8001_chip_phy_start_req - start phy via PHY_START COMMAND
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int
+pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id)
+{
+ struct phy_start_req payload;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 tag = 0x01;
+ u32 opcode = OPC_INB_PHYSTART;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = cpu_to_le32(tag);
+ /*
+ ** [0:7] PHY Identifier
+ ** [8:11] link rate 1.5G, 3G, 6G
+ ** [12:13] link mode 01b SAS mode; 10b SATA mode; 11b both
+ ** [14] 0b disable spin up hold; 1b enable spin up hold
+ */
+ payload.ase_sh_lm_slr_phyid = cpu_to_le32(SPINHOLD_DISABLE |
+ LINKMODE_AUTO | LINKRATE_15 |
+ LINKRATE_30 | LINKRATE_60 | phy_id);
+ payload.sas_identify.dev_type = SAS_END_DEV;
+ payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL;
+ memcpy(payload.sas_identify.sas_addr,
+ pm8001_ha->sas_addr, SAS_ADDR_SIZE);
+ payload.sas_identify.phy_id = phy_id;
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
+ return ret;
+}
+
+/**
+ * pm8001_chip_phy_stop_req - start phy via PHY_STOP COMMAND
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
+ u8 phy_id)
+{
+ struct phy_stop_req payload;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 tag = 0x01;
+ u32 opcode = OPC_INB_PHYSTOP;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = cpu_to_le32(tag);
+ payload.phy_id = cpu_to_le32(phy_id);
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
+ return ret;
+}
+
+/**
+ * see comments on mpi_reg_resp.
+ */
+static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 flag)
+{
+ struct reg_dev_req payload;
+ u32 opc;
+ u32 stp_sspsmp_sata = 0x4;
+ struct inbound_queue_table *circularQ;
+ u32 linkrate, phy_id;
+ int rc, tag = 0xdeadbeef;
+ struct pm8001_ccb_info *ccb;
+ u8 retryFlag = 0x1;
+ u16 firstBurstSize = 0;
+ u16 ITNT = 2000;
+ struct domain_device *dev = pm8001_dev->sas_device;
+ struct domain_device *parent_dev = dev->parent;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->device = pm8001_dev;
+ ccb->ccb_tag = tag;
+ payload.tag = cpu_to_le32(tag);
+ if (flag == 1)
+ stp_sspsmp_sata = 0x02; /*direct attached sata */
+ else {
+ if (pm8001_dev->dev_type == SATA_DEV)
+ stp_sspsmp_sata = 0x00; /* stp*/
+ else if (pm8001_dev->dev_type == SAS_END_DEV ||
+ pm8001_dev->dev_type == EDGE_DEV ||
+ pm8001_dev->dev_type == FANOUT_DEV)
+ stp_sspsmp_sata = 0x01; /*ssp or smp*/
+ }
+ if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
+ phy_id = parent_dev->ex_dev.ex_phy->phy_id;
+ else
+ phy_id = pm8001_dev->attached_phy;
+ opc = OPC_INB_REG_DEV;
+ linkrate = (pm8001_dev->sas_device->linkrate < dev->port->linkrate) ?
+ pm8001_dev->sas_device->linkrate : dev->port->linkrate;
+ payload.phyid_portid =
+ cpu_to_le32(((pm8001_dev->sas_device->port->id) & 0x0F) |
+ ((phy_id & 0x0F) << 4));
+ payload.dtype_dlr_retry = cpu_to_le32((retryFlag & 0x01) |
+ ((linkrate & 0x0F) * 0x1000000) |
+ ((stp_sspsmp_sata & 0x03) * 0x10000000));
+ payload.firstburstsize_ITNexustimeout =
+ cpu_to_le32(ITNT | (firstBurstSize * 0x10000));
+ memcpy(&payload.sas_addr_hi, pm8001_dev->sas_device->sas_addr,
+ SAS_ADDR_SIZE);
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return rc;
+}
+
+/**
+ * see comments on mpi_reg_resp.
+ */
+static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha,
+ u32 device_id)
+{
+ struct dereg_dev_req payload;
+ u32 opc = OPC_INB_DEREG_DEV_HANDLE;
+ int ret;
+ struct inbound_queue_table *circularQ;
+
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&payload, 0, sizeof(payload));
+ payload.tag = 1;
+ payload.device_id = cpu_to_le32(device_id);
+ PM8001_MSG_DBG(pm8001_ha,
+ pm8001_printk("unregister device device_id = %d\n", device_id));
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return ret;
+}
+
+/**
+ * pm8001_chip_phy_ctl_req - support the local phy operation
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to operate
+ * @phy_op:
+ */
+static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+ u32 phyId, u32 phy_op)
+{
+ struct local_phy_ctl_req payload;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 opc = OPC_INB_LOCAL_PHY_CONTROL;
+ memset((u8 *)&payload, 0, sizeof(payload));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = 1;
+ payload.phyop_phyid =
+ cpu_to_le32(((phy_op & 0xff) << 8) | (phyId & 0x0F));
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return ret;
+}
+
+static u32 pm8001_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 value;
+#ifdef PM8001_USE_MSIX
+ return 1;
+#endif
+ value = pm8001_cr32(pm8001_ha, 0, MSGU_ODR);
+ if (value)
+ return 1;
+ return 0;
+
+}
+
+/**
+ * pm8001_chip_isr - PM8001 isr handler.
+ * @pm8001_ha: our hba card information.
+ * @irq: irq number.
+ * @stat: stat.
+ */
+static irqreturn_t
+pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_chip_interrupt_disable(pm8001_ha);
+ process_oq(pm8001_ha);
+ pm8001_chip_interrupt_enable(pm8001_ha);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc,
+ u32 dev_id, u8 flag, u32 task_tag, u32 cmd_tag)
+{
+ struct task_abort_req task_abort;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&task_abort, 0, sizeof(task_abort));
+ if (ABORT_SINGLE == (flag & ABORT_MASK)) {
+ task_abort.abort_all = 0;
+ task_abort.device_id = cpu_to_le32(dev_id);
+ task_abort.tag_to_abort = cpu_to_le32(task_tag);
+ task_abort.tag = cpu_to_le32(cmd_tag);
+ } else if (ABORT_ALL == (flag & ABORT_MASK)) {
+ task_abort.abort_all = cpu_to_le32(1);
+ task_abort.device_id = cpu_to_le32(dev_id);
+ task_abort.tag = cpu_to_le32(cmd_tag);
+ }
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort);
+ return ret;
+}
+
+/**
+ * pm8001_chip_abort_task - SAS abort task when error or exception happened.
+ * @task: the task we wanted to aborted.
+ * @flag: the abort flag.
+ */
+static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, u32 cmd_tag)
+{
+ u32 opc, device_id;
+ int rc = TMF_RESP_FUNC_FAILED;
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("cmd_tag = %x, abort task tag"
+ " = %x", cmd_tag, task_tag));
+ if (pm8001_dev->dev_type == SAS_END_DEV)
+ opc = OPC_INB_SSP_ABORT;
+ else if (pm8001_dev->dev_type == SATA_DEV)
+ opc = OPC_INB_SATA_ABORT;
+ else
+ opc = OPC_INB_SMP_ABORT;/* SMP */
+ device_id = pm8001_dev->device_id;
+ rc = send_task_abort(pm8001_ha, opc, device_id, flag,
+ task_tag, cmd_tag);
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("rc= %d\n", rc));
+ return rc;
+}
+
+/**
+ * pm8001_chip_ssp_tm_req - built the task managment command.
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information.
+ * @tmf: task management function.
+ */
+static int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+{
+ struct sas_task *task = ccb->task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ u32 opc = OPC_INB_SSPINITMSTART;
+ struct inbound_queue_table *circularQ;
+ struct ssp_ini_tm_start_req sspTMCmd;
+ int ret;
+
+ memset(&sspTMCmd, 0, sizeof(sspTMCmd));
+ sspTMCmd.device_id = cpu_to_le32(pm8001_dev->device_id);
+ sspTMCmd.relate_tag = cpu_to_le32(tmf->tag_of_task_to_be_managed);
+ sspTMCmd.tmf = cpu_to_le32(tmf->tmf);
+ memcpy(sspTMCmd.lun, task->ssp_task.LUN, 8);
+ sspTMCmd.tag = cpu_to_le32(ccb->ccb_tag);
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sspTMCmd);
+ return ret;
+}
+
+static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+ void *payload)
+{
+ u32 opc = OPC_INB_GET_NVMD_DATA;
+ u32 nvmd_type;
+ int rc;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ struct inbound_queue_table *circularQ;
+ struct get_nvm_data_req nvmd_req;
+ struct fw_control_ex *fw_control_context;
+ struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+ nvmd_type = ioctl_payload->minor_function;
+ fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+ fw_control_context->usrAddr = (u8 *)&ioctl_payload->func_specific[0];
+ fw_control_context->len = ioctl_payload->length;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memset(&nvmd_req, 0, sizeof(nvmd_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->ccb_tag = tag;
+ ccb->fw_control_context = fw_control_context;
+ nvmd_req.tag = cpu_to_le32(tag);
+
+ switch (nvmd_type) {
+ case TWI_DEVICE: {
+ u32 twi_addr, twi_page_size;
+ twi_addr = 0xa8;
+ twi_page_size = 2;
+
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
+ twi_page_size << 8 | TWI_DEVICE);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case C_SEEPROM: {
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case VPD_FLASH: {
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case EXPAN_ROM: {
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ default:
+ break;
+ }
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+ return rc;
+}
+
+static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+ void *payload)
+{
+ u32 opc = OPC_INB_SET_NVMD_DATA;
+ u32 nvmd_type;
+ int rc;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ struct inbound_queue_table *circularQ;
+ struct set_nvm_data_req nvmd_req;
+ struct fw_control_ex *fw_control_context;
+ struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+ nvmd_type = ioctl_payload->minor_function;
+ fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr,
+ ioctl_payload->func_specific,
+ ioctl_payload->length);
+ memset(&nvmd_req, 0, sizeof(nvmd_req));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->fw_control_context = fw_control_context;
+ ccb->ccb_tag = tag;
+ nvmd_req.tag = cpu_to_le32(tag);
+ switch (nvmd_type) {
+ case TWI_DEVICE: {
+ u32 twi_addr, twi_page_size;
+ twi_addr = 0xa8;
+ twi_page_size = 2;
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
+ twi_page_size << 8 | TWI_DEVICE);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ }
+ case C_SEEPROM:
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ case VPD_FLASH:
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ case EXPAN_ROM:
+ nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
+ nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+ nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+ nvmd_req.resp_addr_hi =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+ nvmd_req.resp_addr_lo =
+ cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+ break;
+ default:
+ break;
+ }
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+ return rc;
+}
+
+/**
+ * pm8001_chip_fw_flash_update_build - support the firmware update operation
+ * @pm8001_ha: our hba card information.
+ * @fw_flash_updata_info: firmware flash update param
+ */
+static int
+pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
+ void *fw_flash_updata_info, u32 tag)
+{
+ struct fw_flash_Update_req payload;
+ struct fw_flash_updata_info *info;
+ struct inbound_queue_table *circularQ;
+ int ret;
+ u32 opc = OPC_INB_FW_FLASH_UPDATE;
+
+ memset(&payload, 0, sizeof(struct fw_flash_Update_req));
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ info = fw_flash_updata_info;
+ payload.tag = cpu_to_le32(tag);
+ payload.cur_image_len = cpu_to_le32(info->cur_image_len);
+ payload.cur_image_offset = cpu_to_le32(info->cur_image_offset);
+ payload.total_image_len = cpu_to_le32(info->total_image_len);
+ payload.len = info->sgl.im_len.len ;
+ payload.sgl_addr_lo = lower_32_bits(info->sgl.addr);
+ payload.sgl_addr_hi = upper_32_bits(info->sgl.addr);
+ ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return ret;
+}
+
+static int
+pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
+ void *payload)
+{
+ struct fw_flash_updata_info flash_update_info;
+ struct fw_control_info *fw_control;
+ struct fw_control_ex *fw_control_context;
+ int rc;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ void *buffer = NULL;
+ dma_addr_t phys_addr;
+ u32 phys_addr_hi;
+ u32 phys_addr_lo;
+ struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+ fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+ fw_control = (struct fw_control_info *)&ioctl_payload->func_specific[0];
+ if (fw_control->len != 0) {
+ if (pm8001_mem_alloc(pm8001_ha->pdev,
+ (void **)&buffer,
+ &phys_addr,
+ &phys_addr_hi,
+ &phys_addr_lo,
+ fw_control->len, 0) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Mem alloc failure\n"));
+ return -ENOMEM;
+ }
+ }
+ memset(buffer, 0, fw_control->len);
+ memcpy(buffer, fw_control->buffer, fw_control->len);
+ flash_update_info.sgl.addr = cpu_to_le64(phys_addr);
+ flash_update_info.sgl.im_len.len = cpu_to_le32(fw_control->len);
+ flash_update_info.sgl.im_len.e = 0;
+ flash_update_info.cur_image_offset = fw_control->offset;
+ flash_update_info.cur_image_len = fw_control->len;
+ flash_update_info.total_image_len = fw_control->size;
+ fw_control_context->fw_control = fw_control;
+ fw_control_context->virtAddr = buffer;
+ fw_control_context->len = fw_control->len;
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return rc;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->fw_control_context = fw_control_context;
+ ccb->ccb_tag = tag;
+ rc = pm8001_chip_fw_flash_update_build(pm8001_ha, &flash_update_info,
+ tag);
+ return rc;
+}
+
+static int
+pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 state)
+{
+ struct set_dev_state_req payload;
+ struct inbound_queue_table *circularQ;
+ struct pm8001_ccb_info *ccb;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_SET_DEVICE_STATE;
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return -1;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->ccb_tag = tag;
+ ccb->device = pm8001_dev;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = cpu_to_le32(tag);
+ payload.device_id = cpu_to_le32(pm8001_dev->device_id);
+ payload.nds = cpu_to_le32(state);
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return rc;
+
+}
+
+static int
+pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha)
+{
+ struct sas_re_initialization_req payload;
+ struct inbound_queue_table *circularQ;
+ struct pm8001_ccb_info *ccb;
+ int rc;
+ u32 tag;
+ u32 opc = OPC_INB_SAS_RE_INITIALIZE;
+ memset(&payload, 0, sizeof(payload));
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ return -1;
+ ccb = &pm8001_ha->ccb_info[tag];
+ ccb->ccb_tag = tag;
+ circularQ = &pm8001_ha->inbnd_q_tbl[0];
+ payload.tag = cpu_to_le32(tag);
+ payload.SSAHOLT = cpu_to_le32(0xd << 25);
+ payload.sata_hol_tmo = cpu_to_le32(80);
+ payload.open_reject_cmdretries_data_retries = cpu_to_le32(0xff00ff);
+ rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+ return rc;
+
+}
+
+const struct pm8001_dispatch pm8001_8001_dispatch = {
+ .name = "pmc8001",
+ .chip_init = pm8001_chip_init,
+ .chip_soft_rst = pm8001_chip_soft_rst,
+ .chip_rst = pm8001_hw_chip_rst,
+ .chip_iounmap = pm8001_chip_iounmap,
+ .isr = pm8001_chip_isr,
+ .is_our_interupt = pm8001_chip_is_our_interupt,
+ .isr_process_oq = process_oq,
+ .interrupt_enable = pm8001_chip_interrupt_enable,
+ .interrupt_disable = pm8001_chip_interrupt_disable,
+ .make_prd = pm8001_chip_make_sg,
+ .smp_req = pm8001_chip_smp_req,
+ .ssp_io_req = pm8001_chip_ssp_io_req,
+ .sata_req = pm8001_chip_sata_req,
+ .phy_start_req = pm8001_chip_phy_start_req,
+ .phy_stop_req = pm8001_chip_phy_stop_req,
+ .reg_dev_req = pm8001_chip_reg_dev_req,
+ .dereg_dev_req = pm8001_chip_dereg_dev_req,
+ .phy_ctl_req = pm8001_chip_phy_ctl_req,
+ .task_abort = pm8001_chip_abort_task,
+ .ssp_tm_req = pm8001_chip_ssp_tm_req,
+ .get_nvmd_req = pm8001_chip_get_nvmd_req,
+ .set_nvmd_req = pm8001_chip_set_nvmd_req,
+ .fw_flash_update_req = pm8001_chip_fw_flash_update_req,
+ .set_dev_state_req = pm8001_chip_set_dev_state_req,
+ .sas_re_init_req = pm8001_chip_sas_re_initialization,
+};
+
diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h
new file mode 100644
index 00000000000..96e4daa68b8
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_hwi.h
@@ -0,0 +1,1030 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#ifndef _PMC8001_REG_H_
+#define _PMC8001_REG_H_
+
+#include <linux/types.h>
+#include <scsi/libsas.h>
+
+
+/* for Request Opcode of IOMB */
+#define OPC_INB_ECHO 1 /* 0x000 */
+#define OPC_INB_PHYSTART 4 /* 0x004 */
+#define OPC_INB_PHYSTOP 5 /* 0x005 */
+#define OPC_INB_SSPINIIOSTART 6 /* 0x006 */
+#define OPC_INB_SSPINITMSTART 7 /* 0x007 */
+#define OPC_INB_SSPINIEXTIOSTART 8 /* 0x008 */
+#define OPC_INB_DEV_HANDLE_ACCEPT 9 /* 0x009 */
+#define OPC_INB_SSPTGTIOSTART 10 /* 0x00A */
+#define OPC_INB_SSPTGTRSPSTART 11 /* 0x00B */
+#define OPC_INB_SSPINIEDCIOSTART 12 /* 0x00C */
+#define OPC_INB_SSPINIEXTEDCIOSTART 13 /* 0x00D */
+#define OPC_INB_SSPTGTEDCIOSTART 14 /* 0x00E */
+#define OPC_INB_SSP_ABORT 15 /* 0x00F */
+#define OPC_INB_DEREG_DEV_HANDLE 16 /* 0x010 */
+#define OPC_INB_GET_DEV_HANDLE 17 /* 0x011 */
+#define OPC_INB_SMP_REQUEST 18 /* 0x012 */
+/* SMP_RESPONSE is removed */
+#define OPC_INB_SMP_RESPONSE 19 /* 0x013 */
+#define OPC_INB_SMP_ABORT 20 /* 0x014 */
+#define OPC_INB_REG_DEV 22 /* 0x016 */
+#define OPC_INB_SATA_HOST_OPSTART 23 /* 0x017 */
+#define OPC_INB_SATA_ABORT 24 /* 0x018 */
+#define OPC_INB_LOCAL_PHY_CONTROL 25 /* 0x019 */
+#define OPC_INB_GET_DEV_INFO 26 /* 0x01A */
+#define OPC_INB_FW_FLASH_UPDATE 32 /* 0x020 */
+#define OPC_INB_GPIO 34 /* 0x022 */
+#define OPC_INB_SAS_DIAG_MODE_START_END 35 /* 0x023 */
+#define OPC_INB_SAS_DIAG_EXECUTE 36 /* 0x024 */
+#define OPC_INB_SAS_HW_EVENT_ACK 37 /* 0x025 */
+#define OPC_INB_GET_TIME_STAMP 38 /* 0x026 */
+#define OPC_INB_PORT_CONTROL 39 /* 0x027 */
+#define OPC_INB_GET_NVMD_DATA 40 /* 0x028 */
+#define OPC_INB_SET_NVMD_DATA 41 /* 0x029 */
+#define OPC_INB_SET_DEVICE_STATE 42 /* 0x02A */
+#define OPC_INB_GET_DEVICE_STATE 43 /* 0x02B */
+#define OPC_INB_SET_DEV_INFO 44 /* 0x02C */
+#define OPC_INB_SAS_RE_INITIALIZE 45 /* 0x02D */
+
+/* for Response Opcode of IOMB */
+#define OPC_OUB_ECHO 1 /* 0x001 */
+#define OPC_OUB_HW_EVENT 4 /* 0x004 */
+#define OPC_OUB_SSP_COMP 5 /* 0x005 */
+#define OPC_OUB_SMP_COMP 6 /* 0x006 */
+#define OPC_OUB_LOCAL_PHY_CNTRL 7 /* 0x007 */
+#define OPC_OUB_DEV_REGIST 10 /* 0x00A */
+#define OPC_OUB_DEREG_DEV 11 /* 0x00B */
+#define OPC_OUB_GET_DEV_HANDLE 12 /* 0x00C */
+#define OPC_OUB_SATA_COMP 13 /* 0x00D */
+#define OPC_OUB_SATA_EVENT 14 /* 0x00E */
+#define OPC_OUB_SSP_EVENT 15 /* 0x00F */
+#define OPC_OUB_DEV_HANDLE_ARRIV 16 /* 0x010 */
+/* SMP_RECEIVED Notification is removed */
+#define OPC_OUB_SMP_RECV_EVENT 17 /* 0x011 */
+#define OPC_OUB_SSP_RECV_EVENT 18 /* 0x012 */
+#define OPC_OUB_DEV_INFO 19 /* 0x013 */
+#define OPC_OUB_FW_FLASH_UPDATE 20 /* 0x014 */
+#define OPC_OUB_GPIO_RESPONSE 22 /* 0x016 */
+#define OPC_OUB_GPIO_EVENT 23 /* 0x017 */
+#define OPC_OUB_GENERAL_EVENT 24 /* 0x018 */
+#define OPC_OUB_SSP_ABORT_RSP 26 /* 0x01A */
+#define OPC_OUB_SATA_ABORT_RSP 27 /* 0x01B */
+#define OPC_OUB_SAS_DIAG_MODE_START_END 28 /* 0x01C */
+#define OPC_OUB_SAS_DIAG_EXECUTE 29 /* 0x01D */
+#define OPC_OUB_GET_TIME_STAMP 30 /* 0x01E */
+#define OPC_OUB_SAS_HW_EVENT_ACK 31 /* 0x01F */
+#define OPC_OUB_PORT_CONTROL 32 /* 0x020 */
+#define OPC_OUB_SKIP_ENTRY 33 /* 0x021 */
+#define OPC_OUB_SMP_ABORT_RSP 34 /* 0x022 */
+#define OPC_OUB_GET_NVMD_DATA 35 /* 0x023 */
+#define OPC_OUB_SET_NVMD_DATA 36 /* 0x024 */
+#define OPC_OUB_DEVICE_HANDLE_REMOVAL 37 /* 0x025 */
+#define OPC_OUB_SET_DEVICE_STATE 38 /* 0x026 */
+#define OPC_OUB_GET_DEVICE_STATE 39 /* 0x027 */
+#define OPC_OUB_SET_DEV_INFO 40 /* 0x028 */
+#define OPC_OUB_SAS_RE_INITIALIZE 41 /* 0x029 */
+
+/* for phy start*/
+#define SPINHOLD_DISABLE (0x00 << 14)
+#define SPINHOLD_ENABLE (0x01 << 14)
+#define LINKMODE_SAS (0x01 << 12)
+#define LINKMODE_DSATA (0x02 << 12)
+#define LINKMODE_AUTO (0x03 << 12)
+#define LINKRATE_15 (0x01 << 8)
+#define LINKRATE_30 (0x02 << 8)
+#define LINKRATE_60 (0x04 << 8)
+
+struct mpi_msg_hdr{
+ __le32 header; /* Bits [11:0] - Message operation code */
+ /* Bits [15:12] - Message Category */
+ /* Bits [21:16] - Outboundqueue ID for the
+ operation completion message */
+ /* Bits [23:22] - Reserved */
+ /* Bits [28:24] - Buffer Count, indicates how
+ many buffer are allocated for the massage */
+ /* Bits [30:29] - Reserved */
+ /* Bits [31] - Message Valid bit */
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to describe enable the phy (64 bytes)
+ */
+struct phy_start_req {
+ __le32 tag;
+ __le32 ase_sh_lm_slr_phyid;
+ struct sas_identify_frame sas_identify;
+ u32 reserved[5];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to disable the phy (64 bytes)
+ */
+struct phy_stop_req {
+ __le32 tag;
+ __le32 phy_id;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/* set device bits fis - device to host */
+struct set_dev_bits_fis {
+ u8 fis_type; /* 0xA1*/
+ u8 n_i_pmport;
+ /* b7 : n Bit. Notification bit. If set device needs attention. */
+ /* b6 : i Bit. Interrupt Bit */
+ /* b5-b4: reserved2 */
+ /* b3-b0: PM Port */
+ u8 status;
+ u8 error;
+ u32 _r_a;
+} __attribute__ ((packed));
+/* PIO setup FIS - device to host */
+struct pio_setup_fis {
+ u8 fis_type; /* 0x5f */
+ u8 i_d_pmPort;
+ /* b7 : reserved */
+ /* b6 : i bit. Interrupt bit */
+ /* b5 : d bit. data transfer direction. set to 1 for device to host
+ xfer */
+ /* b4 : reserved */
+ /* b3-b0: PM Port */
+ u8 status;
+ u8 error;
+ u8 lbal;
+ u8 lbam;
+ u8 lbah;
+ u8 device;
+ u8 lbal_exp;
+ u8 lbam_exp;
+ u8 lbah_exp;
+ u8 _r_a;
+ u8 sector_count;
+ u8 sector_count_exp;
+ u8 _r_b;
+ u8 e_status;
+ u8 _r_c[2];
+ u8 transfer_count;
+} __attribute__ ((packed));
+
+/*
+ * brief the data structure of SATA Completion Response
+ * use to discribe the sata task response (64 bytes)
+ */
+struct sata_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ u32 sata_resp[12];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of SAS HW Event Notification
+ * use to alert the host about the hardware event(64 bytes)
+ */
+struct hw_event_resp {
+ __le32 lr_evt_status_phyid_portid;
+ __le32 evt_param;
+ __le32 npip_portstate;
+ struct sas_identify_frame sas_identify;
+ struct dev_to_host_fis sata_fis;
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of REGISTER DEVICE Command
+ * use to describe MPI REGISTER DEVICE Command (64 bytes)
+ */
+
+struct reg_dev_req {
+ __le32 tag;
+ __le32 phyid_portid;
+ __le32 dtype_dlr_retry;
+ __le32 firstburstsize_ITNexustimeout;
+ u32 sas_addr_hi;
+ u32 sas_addr_low;
+ __le32 upper_device_id;
+ u32 reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of DEREGISTER DEVICE Command
+ * use to request spc to remove all internal resources associated
+ * with the device id (64 bytes)
+ */
+
+struct dereg_dev_req {
+ __le32 tag;
+ __le32 device_id;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of DEVICE_REGISTRATION Response
+ * use to notify the completion of the device registration (64 bytes)
+ */
+
+struct dev_reg_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 device_id;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of Local PHY Control Command
+ * use to issue PHY CONTROL to local phy (64 bytes)
+ */
+struct local_phy_ctl_req {
+ __le32 tag;
+ __le32 phyop_phyid;
+ u32 reserved1[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Local Phy Control Response
+ * use to describe MPI Local Phy Control Response (64 bytes)
+ */
+struct local_phy_ctl_resp {
+ __le32 tag;
+ __le32 phyop_phyid;
+ __le32 status;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+#define OP_BITS 0x0000FF00
+#define ID_BITS 0x0000000F
+
+/*
+ * brief the data structure of PORT Control Command
+ * use to control port properties (64 bytes)
+ */
+
+struct port_ctl_req {
+ __le32 tag;
+ __le32 portop_portid;
+ __le32 param0;
+ __le32 param1;
+ u32 reserved1[11];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of HW Event Ack Command
+ * use to acknowledge receive HW event (64 bytes)
+ */
+
+struct hw_event_ack_req {
+ __le32 tag;
+ __le32 sea_phyid_portid;
+ __le32 param0;
+ __le32 param1;
+ u32 reserved1[11];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of SSP Completion Response
+ * use to indicate a SSP Completion (n bytes)
+ */
+struct ssp_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ __le32 ssptag_rescv_rescpad;
+ struct ssp_response_iu ssp_resp_iu;
+ __le32 residual_count;
+} __attribute__((packed, aligned(4)));
+
+
+#define SSP_RESCV_BIT 0x00010000
+
+/*
+ * brief the data structure of SATA EVNET esponse
+ * use to indicate a SATA Completion (64 bytes)
+ */
+
+struct sata_event_resp {
+ __le32 tag;
+ __le32 event;
+ __le32 port_id;
+ __le32 device_id;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SSP EVNET esponse
+ * use to indicate a SSP Completion (64 bytes)
+ */
+
+struct ssp_event_resp {
+ __le32 tag;
+ __le32 event;
+ __le32 port_id;
+ __le32 device_id;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of General Event Notification Response
+ * use to describe MPI General Event Notification Response (64 bytes)
+ */
+struct general_event_resp {
+ __le32 status;
+ __le32 inb_IOMB_payload[14];
+} __attribute__((packed, aligned(4)));
+
+
+#define GENERAL_EVENT_PAYLOAD 14
+#define OPCODE_BITS 0x00000fff
+
+/*
+ * brief the data structure of SMP Request Command
+ * use to describe MPI SMP REQUEST Command (64 bytes)
+ */
+struct smp_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 len_ip_ir;
+ /* Bits [0] - Indirect response */
+ /* Bits [1] - Indirect Payload */
+ /* Bits [15:2] - Reserved */
+ /* Bits [23:16] - direct payload Len */
+ /* Bits [31:24] - Reserved */
+ u8 smp_req16[16];
+ union {
+ u8 smp_req[32];
+ struct {
+ __le64 long_req_addr;/* sg dma address, LE */
+ __le32 long_req_size;/* LE */
+ u32 _r_a;
+ __le64 long_resp_addr;/* sg dma address, LE */
+ __le32 long_resp_size;/* LE */
+ u32 _r_b;
+ } long_smp_req;/* sequencer extension */
+ };
+} __attribute__((packed, aligned(4)));
+/*
+ * brief the data structure of SMP Completion Response
+ * use to describe MPI SMP Completion Response (64 bytes)
+ */
+struct smp_completion_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 param;
+ __le32 _r_a[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ *brief the data structure of SSP SMP SATA Abort Command
+ * use to describe MPI SSP SMP & SATA Abort Command (64 bytes)
+ */
+struct task_abort_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 tag_to_abort;
+ __le32 abort_all;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/* These flags used for SSP SMP & SATA Abort */
+#define ABORT_MASK 0x3
+#define ABORT_SINGLE 0x0
+#define ABORT_ALL 0x1
+
+/**
+ * brief the data structure of SSP SATA SMP Abort Response
+ * use to describe SSP SMP & SATA Abort Response ( 64 bytes)
+ */
+struct task_abort_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 scp;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Command
+ * use to describe MPI SAS Diagnostic Start/End Command (64 bytes)
+ */
+struct sas_diag_start_end_req {
+ __le32 tag;
+ __le32 operation_phyid;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Command
+ * use to describe MPI SAS Diagnostic Execute Command (64 bytes)
+ */
+struct sas_diag_execute_req{
+ __le32 tag;
+ __le32 cmdtype_cmddesc_phyid;
+ __le32 pat1_pat2;
+ __le32 threshold;
+ __le32 codepat_errmsk;
+ __le32 pmon;
+ __le32 pERF1CTL;
+ u32 reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+#define SAS_DIAG_PARAM_BYTES 24
+
+/*
+ * brief the data structure of Set Device State Command
+ * use to describe MPI Set Device State Command (64 bytes)
+ */
+struct set_dev_state_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 nds;
+ u32 reserved[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of sas_re_initialization
+ */
+struct sas_re_initialization_req {
+
+ __le32 tag;
+ __le32 SSAHOLT;/* bit29-set max port;
+ ** bit28-set open reject cmd retries.
+ ** bit27-set open reject data retries.
+ ** bit26-set open reject option, remap:1 or not:0.
+ ** bit25-set sata head of line time out.
+ */
+ __le32 reserved_maxPorts;
+ __le32 open_reject_cmdretries_data_retries;/* cmd retries: 31-bit16;
+ * data retries: bit15-bit0.
+ */
+ __le32 sata_hol_tmo;
+ u32 reserved1[10];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SATA Start Command
+ * use to describe MPI SATA IO Start Command (64 bytes)
+ */
+
+struct sata_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 data_len;
+ __le32 ncqtag_atap_dir_m;
+ struct host_to_dev_fis sata_fis;
+ u32 reserved1;
+ u32 reserved2;
+ u32 addr_low;
+ u32 addr_high;
+ __le32 len;
+ __le32 esgl;
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SSP INI TM Start Command
+ * use to describe MPI SSP INI TM Start Command (64 bytes)
+ */
+struct ssp_ini_tm_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 relate_tag;
+ __le32 tmf;
+ u8 lun[8];
+ __le32 ds_ads_m;
+ u32 reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+struct ssp_info_unit {
+ u8 lun[8];/* SCSI Logical Unit Number */
+ u8 reserved1;/* reserved */
+ u8 efb_prio_attr;
+ /* B7 : enabledFirstBurst */
+ /* B6-3 : taskPriority */
+ /* B2-0 : taskAttribute */
+ u8 reserved2; /* reserved */
+ u8 additional_cdb_len;
+ /* B7-2 : additional_cdb_len */
+ /* B1-0 : reserved */
+ u8 cdb[16];/* The SCSI CDB up to 16 bytes length */
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SSP INI IO Start Command
+ * use to describe MPI SSP INI IO Start Command (64 bytes)
+ */
+struct ssp_ini_io_start_req {
+ __le32 tag;
+ __le32 device_id;
+ __le32 data_len;
+ __le32 dir_m_tlr;
+ struct ssp_info_unit ssp_iu;
+ __le32 addr_low;
+ __le32 addr_high;
+ __le32 len;
+ __le32 esgl;
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Firmware download
+ * use to describe MPI FW DOWNLOAD Command (64 bytes)
+ */
+struct fw_flash_Update_req {
+ __le32 tag;
+ __le32 cur_image_offset;
+ __le32 cur_image_len;
+ __le32 total_image_len;
+ u32 reserved0[7];
+ __le32 sgl_addr_lo;
+ __le32 sgl_addr_hi;
+ __le32 len;
+ __le32 ext_reserved;
+} __attribute__((packed, aligned(4)));
+
+
+#define FWFLASH_IOMB_RESERVED_LEN 0x07
+/**
+ * brief the data structure of FW_FLASH_UPDATE Response
+ * use to describe MPI FW_FLASH_UPDATE Response (64 bytes)
+ *
+ */
+struct fw_flash_Update_resp {
+ dma_addr_t tag;
+ __le32 status;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Get NVM Data Command
+ * use to get data from NVM in HBA(64 bytes)
+ */
+struct get_nvm_data_req {
+ __le32 tag;
+ __le32 len_ir_vpdd;
+ __le32 vpd_offset;
+ u32 reserved[8];
+ __le32 resp_addr_lo;
+ __le32 resp_addr_hi;
+ __le32 resp_len;
+ u32 reserved1;
+} __attribute__((packed, aligned(4)));
+
+
+struct set_nvm_data_req {
+ __le32 tag;
+ __le32 len_ir_vpdd;
+ __le32 vpd_offset;
+ u32 reserved[8];
+ __le32 resp_addr_lo;
+ __le32 resp_addr_hi;
+ __le32 resp_len;
+ u32 reserved1;
+} __attribute__((packed, aligned(4)));
+
+
+#define TWI_DEVICE 0x0
+#define C_SEEPROM 0x1
+#define VPD_FLASH 0x4
+#define AAP1_RDUMP 0x5
+#define IOP_RDUMP 0x6
+#define EXPAN_ROM 0x7
+
+#define IPMode 0x80000000
+#define NVMD_TYPE 0x0000000F
+#define NVMD_STAT 0x0000FFFF
+#define NVMD_LEN 0xFF000000
+/**
+ * brief the data structure of Get NVMD Data Response
+ * use to describe MPI Get NVMD Data Response (64 bytes)
+ */
+struct get_nvm_data_resp {
+ __le32 tag;
+ __le32 ir_tda_bn_dps_das_nvm;
+ __le32 dlen_status;
+ __le32 nvm_data[12];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Response
+ * use to describe MPI SAS Diagnostic Start/End Response (64 bytes)
+ *
+ */
+struct sas_diag_start_end_resp {
+ __le32 tag;
+ __le32 status;
+ u32 reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Response
+ * use to describe MPI SAS Diagnostic Execute Response (64 bytes)
+ *
+ */
+struct sas_diag_execute_resp {
+ __le32 tag;
+ __le32 cmdtype_cmddesc_phyid;
+ __le32 Status;
+ __le32 ReportData;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Set Device State Response
+ * use to describe MPI Set Device State Response (64 bytes)
+ *
+ */
+struct set_dev_state_resp {
+ __le32 tag;
+ __le32 status;
+ __le32 device_id;
+ __le32 pds_nds;
+ u32 reserved[11];
+} __attribute__((packed, aligned(4)));
+
+
+#define NDS_BITS 0x0F
+#define PDS_BITS 0xF0
+
+/*
+ * HW Events type
+ */
+
+#define HW_EVENT_RESET_START 0x01
+#define HW_EVENT_CHIP_RESET_COMPLETE 0x02
+#define HW_EVENT_PHY_STOP_STATUS 0x03
+#define HW_EVENT_SAS_PHY_UP 0x04
+#define HW_EVENT_SATA_PHY_UP 0x05
+#define HW_EVENT_SATA_SPINUP_HOLD 0x06
+#define HW_EVENT_PHY_DOWN 0x07
+#define HW_EVENT_PORT_INVALID 0x08
+#define HW_EVENT_BROADCAST_CHANGE 0x09
+#define HW_EVENT_PHY_ERROR 0x0A
+#define HW_EVENT_BROADCAST_SES 0x0B
+#define HW_EVENT_INBOUND_CRC_ERROR 0x0C
+#define HW_EVENT_HARD_RESET_RECEIVED 0x0D
+#define HW_EVENT_MALFUNCTION 0x0E
+#define HW_EVENT_ID_FRAME_TIMEOUT 0x0F
+#define HW_EVENT_BROADCAST_EXP 0x10
+#define HW_EVENT_PHY_START_STATUS 0x11
+#define HW_EVENT_LINK_ERR_INVALID_DWORD 0x12
+#define HW_EVENT_LINK_ERR_DISPARITY_ERROR 0x13
+#define HW_EVENT_LINK_ERR_CODE_VIOLATION 0x14
+#define HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH 0x15
+#define HW_EVENT_LINK_ERR_PHY_RESET_FAILED 0x16
+#define HW_EVENT_PORT_RECOVERY_TIMER_TMO 0x17
+#define HW_EVENT_PORT_RECOVER 0x18
+#define HW_EVENT_PORT_RESET_TIMER_TMO 0x19
+#define HW_EVENT_PORT_RESET_COMPLETE 0x20
+#define EVENT_BROADCAST_ASYNCH_EVENT 0x21
+
+/* port state */
+#define PORT_NOT_ESTABLISHED 0x00
+#define PORT_VALID 0x01
+#define PORT_LOSTCOMM 0x02
+#define PORT_IN_RESET 0x04
+#define PORT_INVALID 0x08
+
+/*
+ * SSP/SMP/SATA IO Completion Status values
+ */
+
+#define IO_SUCCESS 0x00
+#define IO_ABORTED 0x01
+#define IO_OVERFLOW 0x02
+#define IO_UNDERFLOW 0x03
+#define IO_FAILED 0x04
+#define IO_ABORT_RESET 0x05
+#define IO_NOT_VALID 0x06
+#define IO_NO_DEVICE 0x07
+#define IO_ILLEGAL_PARAMETER 0x08
+#define IO_LINK_FAILURE 0x09
+#define IO_PROG_ERROR 0x0A
+#define IO_EDC_IN_ERROR 0x0B
+#define IO_EDC_OUT_ERROR 0x0C
+#define IO_ERROR_HW_TIMEOUT 0x0D
+#define IO_XFER_ERROR_BREAK 0x0E
+#define IO_XFER_ERROR_PHY_NOT_READY 0x0F
+#define IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED 0x10
+#define IO_OPEN_CNX_ERROR_ZONE_VIOLATION 0x11
+#define IO_OPEN_CNX_ERROR_BREAK 0x12
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS 0x13
+#define IO_OPEN_CNX_ERROR_BAD_DESTINATION 0x14
+#define IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED 0x15
+#define IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY 0x16
+#define IO_OPEN_CNX_ERROR_WRONG_DESTINATION 0x17
+#define IO_OPEN_CNX_ERROR_UNKNOWN_ERROR 0x18
+#define IO_XFER_ERROR_NAK_RECEIVED 0x19
+#define IO_XFER_ERROR_ACK_NAK_TIMEOUT 0x1A
+#define IO_XFER_ERROR_PEER_ABORTED 0x1B
+#define IO_XFER_ERROR_RX_FRAME 0x1C
+#define IO_XFER_ERROR_DMA 0x1D
+#define IO_XFER_ERROR_CREDIT_TIMEOUT 0x1E
+#define IO_XFER_ERROR_SATA_LINK_TIMEOUT 0x1F
+#define IO_XFER_ERROR_SATA 0x20
+#define IO_XFER_ERROR_ABORTED_DUE_TO_SRST 0x22
+#define IO_XFER_ERROR_REJECTED_NCQ_MODE 0x21
+#define IO_XFER_ERROR_ABORTED_NCQ_MODE 0x23
+#define IO_XFER_OPEN_RETRY_TIMEOUT 0x24
+#define IO_XFER_SMP_RESP_CONNECTION_ERROR 0x25
+#define IO_XFER_ERROR_UNEXPECTED_PHASE 0x26
+#define IO_XFER_ERROR_XFER_RDY_OVERRUN 0x27
+#define IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED 0x28
+
+#define IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT 0x30
+#define IO_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NAK 0x31
+#define IO_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK 0x32
+
+#define IO_XFER_ERROR_OFFSET_MISMATCH 0x34
+#define IO_XFER_ERROR_XFER_ZERO_DATA_LEN 0x35
+#define IO_XFER_CMD_FRAME_ISSUED 0x36
+#define IO_ERROR_INTERNAL_SMP_RESOURCE 0x37
+#define IO_PORT_IN_RESET 0x38
+#define IO_DS_NON_OPERATIONAL 0x39
+#define IO_DS_IN_RECOVERY 0x3A
+#define IO_TM_TAG_NOT_FOUND 0x3B
+#define IO_XFER_PIO_SETUP_ERROR 0x3C
+#define IO_SSP_EXT_IU_ZERO_LEN_ERROR 0x3D
+#define IO_DS_IN_ERROR 0x3E
+#define IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY 0x3F
+#define IO_ABORT_IN_PROGRESS 0x40
+#define IO_ABORT_DELAYED 0x41
+#define IO_INVALID_LENGTH 0x42
+
+/* WARNING: This error code must always be the last number.
+ * If you add error code, modify this code also
+ * It is used as an index
+ */
+#define IO_ERROR_UNKNOWN_GENERIC 0x43
+
+/* MSGU CONFIGURATION TABLE*/
+
+#define SPC_MSGU_CFG_TABLE_UPDATE 0x01/* Inbound doorbell bit0 */
+#define SPC_MSGU_CFG_TABLE_RESET 0x02/* Inbound doorbell bit1 */
+#define SPC_MSGU_CFG_TABLE_FREEZE 0x04/* Inbound doorbell bit2 */
+#define SPC_MSGU_CFG_TABLE_UNFREEZE 0x08/* Inbound doorbell bit4 */
+#define MSGU_IBDB_SET 0x04
+#define MSGU_HOST_INT_STATUS 0x08
+#define MSGU_HOST_INT_MASK 0x0C
+#define MSGU_IOPIB_INT_STATUS 0x18
+#define MSGU_IOPIB_INT_MASK 0x1C
+#define MSGU_IBDB_CLEAR 0x20/* RevB - Host not use */
+#define MSGU_MSGU_CONTROL 0x24
+#define MSGU_ODR 0x3C/* RevB */
+#define MSGU_ODCR 0x40/* RevB */
+#define MSGU_SCRATCH_PAD_0 0x44
+#define MSGU_SCRATCH_PAD_1 0x48
+#define MSGU_SCRATCH_PAD_2 0x4C
+#define MSGU_SCRATCH_PAD_3 0x50
+#define MSGU_HOST_SCRATCH_PAD_0 0x54
+#define MSGU_HOST_SCRATCH_PAD_1 0x58
+#define MSGU_HOST_SCRATCH_PAD_2 0x5C
+#define MSGU_HOST_SCRATCH_PAD_3 0x60
+#define MSGU_HOST_SCRATCH_PAD_4 0x64
+#define MSGU_HOST_SCRATCH_PAD_5 0x68
+#define MSGU_HOST_SCRATCH_PAD_6 0x6C
+#define MSGU_HOST_SCRATCH_PAD_7 0x70
+#define MSGU_ODMR 0x74/* RevB */
+
+/* bit definition for ODMR register */
+#define ODMR_MASK_ALL 0xFFFFFFFF/* mask all
+ interrupt vector */
+#define ODMR_CLEAR_ALL 0/* clear all
+ interrupt vector */
+/* bit definition for ODCR register */
+#define ODCR_CLEAR_ALL 0xFFFFFFFF /* mask all
+ interrupt vector*/
+/* MSIX Interupts */
+#define MSIX_TABLE_OFFSET 0x2000
+#define MSIX_TABLE_ELEMENT_SIZE 0x10
+#define MSIX_INTERRUPT_CONTROL_OFFSET 0xC
+#define MSIX_TABLE_BASE (MSIX_TABLE_OFFSET + MSIX_INTERRUPT_CONTROL_OFFSET)
+#define MSIX_INTERRUPT_DISABLE 0x1
+#define MSIX_INTERRUPT_ENABLE 0x0
+
+
+/* state definition for Scratch Pad1 register */
+#define SCRATCH_PAD1_POR 0x00 /* power on reset state */
+#define SCRATCH_PAD1_SFR 0x01 /* soft reset state */
+#define SCRATCH_PAD1_ERR 0x02 /* error state */
+#define SCRATCH_PAD1_RDY 0x03 /* ready state */
+#define SCRATCH_PAD1_RST 0x04 /* soft reset toggle flag */
+#define SCRATCH_PAD1_AAP1RDY_RST 0x08 /* AAP1 ready for soft reset */
+#define SCRATCH_PAD1_STATE_MASK 0xFFFFFFF0 /* ScratchPad1
+ Mask, bit1-0 State, bit2 Soft Reset, bit3 FW RDY for Soft Reset */
+#define SCRATCH_PAD1_RESERVED 0x000003F8 /* Scratch Pad1
+ Reserved bit 3 to 9 */
+
+ /* state definition for Scratch Pad2 register */
+#define SCRATCH_PAD2_POR 0x00 /* power on state */
+#define SCRATCH_PAD2_SFR 0x01 /* soft reset state */
+#define SCRATCH_PAD2_ERR 0x02 /* error state */
+#define SCRATCH_PAD2_RDY 0x03 /* ready state */
+#define SCRATCH_PAD2_FWRDY_RST 0x04 /* FW ready for soft reset flag*/
+#define SCRATCH_PAD2_IOPRDY_RST 0x08 /* IOP ready for soft reset */
+#define SCRATCH_PAD2_STATE_MASK 0xFFFFFFF4 /* ScratchPad 2
+ Mask, bit1-0 State */
+#define SCRATCH_PAD2_RESERVED 0x000003FC /* Scratch Pad1
+ Reserved bit 2 to 9 */
+
+#define SCRATCH_PAD_ERROR_MASK 0xFFFFFC00 /* Error mask bits */
+#define SCRATCH_PAD_STATE_MASK 0x00000003 /* State Mask bits */
+
+/* main configuration offset - byte offset */
+#define MAIN_SIGNATURE_OFFSET 0x00/* DWORD 0x00 */
+#define MAIN_INTERFACE_REVISION 0x04/* DWORD 0x01 */
+#define MAIN_FW_REVISION 0x08/* DWORD 0x02 */
+#define MAIN_MAX_OUTSTANDING_IO_OFFSET 0x0C/* DWORD 0x03 */
+#define MAIN_MAX_SGL_OFFSET 0x10/* DWORD 0x04 */
+#define MAIN_CNTRL_CAP_OFFSET 0x14/* DWORD 0x05 */
+#define MAIN_GST_OFFSET 0x18/* DWORD 0x06 */
+#define MAIN_IBQ_OFFSET 0x1C/* DWORD 0x07 */
+#define MAIN_OBQ_OFFSET 0x20/* DWORD 0x08 */
+#define MAIN_IQNPPD_HPPD_OFFSET 0x24/* DWORD 0x09 */
+#define MAIN_OB_HW_EVENT_PID03_OFFSET 0x28/* DWORD 0x0A */
+#define MAIN_OB_HW_EVENT_PID47_OFFSET 0x2C/* DWORD 0x0B */
+#define MAIN_OB_NCQ_EVENT_PID03_OFFSET 0x30/* DWORD 0x0C */
+#define MAIN_OB_NCQ_EVENT_PID47_OFFSET 0x34/* DWORD 0x0D */
+#define MAIN_TITNX_EVENT_PID03_OFFSET 0x38/* DWORD 0x0E */
+#define MAIN_TITNX_EVENT_PID47_OFFSET 0x3C/* DWORD 0x0F */
+#define MAIN_OB_SSP_EVENT_PID03_OFFSET 0x40/* DWORD 0x10 */
+#define MAIN_OB_SSP_EVENT_PID47_OFFSET 0x44/* DWORD 0x11 */
+#define MAIN_OB_SMP_EVENT_PID03_OFFSET 0x48/* DWORD 0x12 */
+#define MAIN_OB_SMP_EVENT_PID47_OFFSET 0x4C/* DWORD 0x13 */
+#define MAIN_EVENT_LOG_ADDR_HI 0x50/* DWORD 0x14 */
+#define MAIN_EVENT_LOG_ADDR_LO 0x54/* DWORD 0x15 */
+#define MAIN_EVENT_LOG_BUFF_SIZE 0x58/* DWORD 0x16 */
+#define MAIN_EVENT_LOG_OPTION 0x5C/* DWORD 0x17 */
+#define MAIN_IOP_EVENT_LOG_ADDR_HI 0x60/* DWORD 0x18 */
+#define MAIN_IOP_EVENT_LOG_ADDR_LO 0x64/* DWORD 0x19 */
+#define MAIN_IOP_EVENT_LOG_BUFF_SIZE 0x68/* DWORD 0x1A */
+#define MAIN_IOP_EVENT_LOG_OPTION 0x6C/* DWORD 0x1B */
+#define MAIN_FATAL_ERROR_INTERRUPT 0x70/* DWORD 0x1C */
+#define MAIN_FATAL_ERROR_RDUMP0_OFFSET 0x74/* DWORD 0x1D */
+#define MAIN_FATAL_ERROR_RDUMP0_LENGTH 0x78/* DWORD 0x1E */
+#define MAIN_FATAL_ERROR_RDUMP1_OFFSET 0x7C/* DWORD 0x1F */
+#define MAIN_FATAL_ERROR_RDUMP1_LENGTH 0x80/* DWORD 0x20 */
+#define MAIN_HDA_FLAGS_OFFSET 0x84/* DWORD 0x21 */
+#define MAIN_ANALOG_SETUP_OFFSET 0x88/* DWORD 0x22 */
+
+/* Gereral Status Table offset - byte offset */
+#define GST_GSTLEN_MPIS_OFFSET 0x00
+#define GST_IQ_FREEZE_STATE0_OFFSET 0x04
+#define GST_IQ_FREEZE_STATE1_OFFSET 0x08
+#define GST_MSGUTCNT_OFFSET 0x0C
+#define GST_IOPTCNT_OFFSET 0x10
+#define GST_PHYSTATE_OFFSET 0x18
+#define GST_PHYSTATE0_OFFSET 0x18
+#define GST_PHYSTATE1_OFFSET 0x1C
+#define GST_PHYSTATE2_OFFSET 0x20
+#define GST_PHYSTATE3_OFFSET 0x24
+#define GST_PHYSTATE4_OFFSET 0x28
+#define GST_PHYSTATE5_OFFSET 0x2C
+#define GST_PHYSTATE6_OFFSET 0x30
+#define GST_PHYSTATE7_OFFSET 0x34
+#define GST_RERRINFO_OFFSET 0x44
+
+/* General Status Table - MPI state */
+#define GST_MPI_STATE_UNINIT 0x00
+#define GST_MPI_STATE_INIT 0x01
+#define GST_MPI_STATE_TERMINATION 0x02
+#define GST_MPI_STATE_ERROR 0x03
+#define GST_MPI_STATE_MASK 0x07
+
+#define MBIC_NMI_ENABLE_VPE0_IOP 0x000418
+#define MBIC_NMI_ENABLE_VPE0_AAP1 0x000418
+/* PCIE registers - BAR2(0x18), BAR1(win) 0x010000 */
+#define PCIE_EVENT_INTERRUPT_ENABLE 0x003040
+#define PCIE_EVENT_INTERRUPT 0x003044
+#define PCIE_ERROR_INTERRUPT_ENABLE 0x003048
+#define PCIE_ERROR_INTERRUPT 0x00304C
+/* signature defintion for host scratch pad0 register */
+#define SPC_SOFT_RESET_SIGNATURE 0x252acbcd
+/* Signature for Soft Reset */
+
+/* SPC Reset register - BAR4(0x20), BAR2(win) (need dynamic mapping) */
+#define SPC_REG_RESET 0x000000/* reset register */
+
+/* bit difination for SPC_RESET register */
+#define SPC_REG_RESET_OSSP 0x00000001
+#define SPC_REG_RESET_RAAE 0x00000002
+#define SPC_REG_RESET_PCS_SPBC 0x00000004
+#define SPC_REG_RESET_PCS_IOP_SS 0x00000008
+#define SPC_REG_RESET_PCS_AAP1_SS 0x00000010
+#define SPC_REG_RESET_PCS_AAP2_SS 0x00000020
+#define SPC_REG_RESET_PCS_LM 0x00000040
+#define SPC_REG_RESET_PCS 0x00000080
+#define SPC_REG_RESET_GSM 0x00000100
+#define SPC_REG_RESET_DDR2 0x00010000
+#define SPC_REG_RESET_BDMA_CORE 0x00020000
+#define SPC_REG_RESET_BDMA_SXCBI 0x00040000
+#define SPC_REG_RESET_PCIE_AL_SXCBI 0x00080000
+#define SPC_REG_RESET_PCIE_PWR 0x00100000
+#define SPC_REG_RESET_PCIE_SFT 0x00200000
+#define SPC_REG_RESET_PCS_SXCBI 0x00400000
+#define SPC_REG_RESET_LMS_SXCBI 0x00800000
+#define SPC_REG_RESET_PMIC_SXCBI 0x01000000
+#define SPC_REG_RESET_PMIC_CORE 0x02000000
+#define SPC_REG_RESET_PCIE_PC_SXCBI 0x04000000
+#define SPC_REG_RESET_DEVICE 0x80000000
+
+/* registers for BAR Shifting - BAR2(0x18), BAR1(win) */
+#define SPC_IBW_AXI_TRANSLATION_LOW 0x003258
+
+#define MBIC_AAP1_ADDR_BASE 0x060000
+#define MBIC_IOP_ADDR_BASE 0x070000
+#define GSM_ADDR_BASE 0x0700000
+/* Dynamic map through Bar4 - 0x00700000 */
+#define GSM_CONFIG_RESET 0x00000000
+#define RAM_ECC_DB_ERR 0x00000018
+#define GSM_READ_ADDR_PARITY_INDIC 0x00000058
+#define GSM_WRITE_ADDR_PARITY_INDIC 0x00000060
+#define GSM_WRITE_DATA_PARITY_INDIC 0x00000068
+#define GSM_READ_ADDR_PARITY_CHECK 0x00000038
+#define GSM_WRITE_ADDR_PARITY_CHECK 0x00000040
+#define GSM_WRITE_DATA_PARITY_CHECK 0x00000048
+
+#define RB6_ACCESS_REG 0x6A0000
+#define HDAC_EXEC_CMD 0x0002
+#define HDA_C_PA 0xcb
+#define HDA_SEQ_ID_BITS 0x00ff0000
+#define HDA_GSM_OFFSET_BITS 0x00FFFFFF
+#define MBIC_AAP1_ADDR_BASE 0x060000
+#define MBIC_IOP_ADDR_BASE 0x070000
+#define GSM_ADDR_BASE 0x0700000
+#define SPC_TOP_LEVEL_ADDR_BASE 0x000000
+#define GSM_CONFIG_RESET_VALUE 0x00003b00
+#define GPIO_ADDR_BASE 0x00090000
+#define GPIO_GPIO_0_0UTPUT_CTL_OFFSET 0x0000010c
+
+/* RB6 offset */
+#define SPC_RB6_OFFSET 0x80C0
+/* Magic number of soft reset for RB6 */
+#define RB6_MAGIC_NUMBER_RST 0x1234
+
+/* Device Register status */
+#define DEVREG_SUCCESS 0x00
+#define DEVREG_FAILURE_OUT_OF_RESOURCE 0x01
+#define DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED 0x02
+#define DEVREG_FAILURE_INVALID_PHY_ID 0x03
+#define DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED 0x04
+#define DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE 0x05
+#define DEVREG_FAILURE_PORT_NOT_VALID_STATE 0x06
+#define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID 0x07
+
+#endif
+
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
new file mode 100644
index 00000000000..42ebe725d5a
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -0,0 +1,891 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include "pm8001_sas.h"
+#include "pm8001_chips.h"
+
+static struct scsi_transport_template *pm8001_stt;
+
+static const struct pm8001_chip_info pm8001_chips[] = {
+ [chip_8001] = { 8, &pm8001_8001_dispatch,},
+};
+static int pm8001_id;
+
+LIST_HEAD(hba_list);
+
+/**
+ * The main structure which LLDD must register for scsi core.
+ */
+static struct scsi_host_template pm8001_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .queuecommand = sas_queuecommand,
+ .target_alloc = sas_target_alloc,
+ .slave_configure = pm8001_slave_configure,
+ .slave_destroy = sas_slave_destroy,
+ .scan_finished = pm8001_scan_finished,
+ .scan_start = pm8001_scan_start,
+ .change_queue_depth = sas_change_queue_depth,
+ .change_queue_type = sas_change_queue_type,
+ .bios_param = sas_bios_param,
+ .can_queue = 1,
+ .cmd_per_lun = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_device_reset_handler = sas_eh_device_reset_handler,
+ .eh_bus_reset_handler = sas_eh_bus_reset_handler,
+ .slave_alloc = pm8001_slave_alloc,
+ .target_destroy = sas_target_destroy,
+ .ioctl = sas_ioctl,
+ .shost_attrs = pm8001_host_attrs,
+};
+
+/**
+ * Sas layer call this function to execute specific task.
+ */
+static struct sas_domain_function_template pm8001_transport_ops = {
+ .lldd_dev_found = pm8001_dev_found,
+ .lldd_dev_gone = pm8001_dev_gone,
+
+ .lldd_execute_task = pm8001_queue_command,
+ .lldd_control_phy = pm8001_phy_control,
+
+ .lldd_abort_task = pm8001_abort_task,
+ .lldd_abort_task_set = pm8001_abort_task_set,
+ .lldd_clear_aca = pm8001_clear_aca,
+ .lldd_clear_task_set = pm8001_clear_task_set,
+ .lldd_I_T_nexus_reset = pm8001_I_T_nexus_reset,
+ .lldd_lu_reset = pm8001_lu_reset,
+ .lldd_query_task = pm8001_query_task,
+};
+
+/**
+ *pm8001_phy_init - initiate our adapter phys
+ *@pm8001_ha: our hba structure.
+ *@phy_id: phy id.
+ */
+static void __devinit pm8001_phy_init(struct pm8001_hba_info *pm8001_ha,
+ int phy_id)
+{
+ struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ phy->phy_state = 0;
+ phy->pm8001_ha = pm8001_ha;
+ sas_phy->enabled = (phy_id < pm8001_ha->chip->n_phy) ? 1 : 0;
+ sas_phy->class = SAS;
+ sas_phy->iproto = SAS_PROTOCOL_ALL;
+ sas_phy->tproto = 0;
+ sas_phy->type = PHY_TYPE_PHYSICAL;
+ sas_phy->role = PHY_ROLE_INITIATOR;
+ sas_phy->oob_mode = OOB_NOT_CONNECTED;
+ sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
+ sas_phy->id = phy_id;
+ sas_phy->sas_addr = &pm8001_ha->sas_addr[0];
+ sas_phy->frame_rcvd = &phy->frame_rcvd[0];
+ sas_phy->ha = (struct sas_ha_struct *)pm8001_ha->shost->hostdata;
+ sas_phy->lldd_phy = phy;
+}
+
+/**
+ *pm8001_free - free hba
+ *@pm8001_ha: our hba structure.
+ *
+ */
+static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ struct pm8001_wq *wq;
+
+ if (!pm8001_ha)
+ return;
+
+ for (i = 0; i < USI_MAX_MEMCNT; i++) {
+ if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
+ pci_free_consistent(pm8001_ha->pdev,
+ pm8001_ha->memoryMap.region[i].element_size,
+ pm8001_ha->memoryMap.region[i].virt_ptr,
+ pm8001_ha->memoryMap.region[i].phys_addr);
+ }
+ }
+ PM8001_CHIP_DISP->chip_iounmap(pm8001_ha);
+ if (pm8001_ha->shost)
+ scsi_host_put(pm8001_ha->shost);
+ list_for_each_entry(wq, &pm8001_ha->wq_list, entry)
+ cancel_delayed_work(&wq->work_q);
+ kfree(pm8001_ha->tags);
+ kfree(pm8001_ha);
+}
+
+#ifdef PM8001_USE_TASKLET
+static void pm8001_tasklet(unsigned long opaque)
+{
+ struct pm8001_hba_info *pm8001_ha;
+ pm8001_ha = (struct pm8001_hba_info *)opaque;;
+ if (unlikely(!pm8001_ha))
+ BUG_ON(1);
+ PM8001_CHIP_DISP->isr(pm8001_ha);
+}
+#endif
+
+
+ /**
+ * pm8001_interrupt - when HBA originate a interrupt,we should invoke this
+ * dispatcher to handle each case.
+ * @irq: irq number.
+ * @opaque: the passed general host adapter struct
+ */
+static irqreturn_t pm8001_interrupt(int irq, void *opaque)
+{
+ struct pm8001_hba_info *pm8001_ha;
+ irqreturn_t ret = IRQ_HANDLED;
+ struct sas_ha_struct *sha = opaque;
+ pm8001_ha = sha->lldd_ha;
+ if (unlikely(!pm8001_ha))
+ return IRQ_NONE;
+ if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
+ return IRQ_NONE;
+#ifdef PM8001_USE_TASKLET
+ tasklet_schedule(&pm8001_ha->tasklet);
+#else
+ ret = PM8001_CHIP_DISP->isr(pm8001_ha);
+#endif
+ return ret;
+}
+
+/**
+ * pm8001_alloc - initiate our hba structure and 6 DMAs area.
+ * @pm8001_ha:our hba structure.
+ *
+ */
+static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ spin_lock_init(&pm8001_ha->lock);
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++)
+ pm8001_phy_init(pm8001_ha, i);
+
+ pm8001_ha->tags = kzalloc(PM8001_MAX_CCB, GFP_KERNEL);
+ if (!pm8001_ha->tags)
+ goto err_out;
+ /* MPI Memory region 1 for AAP Event Log for fw */
+ pm8001_ha->memoryMap.region[AAP1].num_elements = 1;
+ pm8001_ha->memoryMap.region[AAP1].element_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[AAP1].total_len = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[AAP1].alignment = 32;
+
+ /* MPI Memory region 2 for IOP Event Log for fw */
+ pm8001_ha->memoryMap.region[IOP].num_elements = 1;
+ pm8001_ha->memoryMap.region[IOP].element_size = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[IOP].total_len = PM8001_EVENT_LOG_SIZE;
+ pm8001_ha->memoryMap.region[IOP].alignment = 32;
+
+ /* MPI Memory region 3 for consumer Index of inbound queues */
+ pm8001_ha->memoryMap.region[CI].num_elements = 1;
+ pm8001_ha->memoryMap.region[CI].element_size = 4;
+ pm8001_ha->memoryMap.region[CI].total_len = 4;
+ pm8001_ha->memoryMap.region[CI].alignment = 4;
+
+ /* MPI Memory region 4 for producer Index of outbound queues */
+ pm8001_ha->memoryMap.region[PI].num_elements = 1;
+ pm8001_ha->memoryMap.region[PI].element_size = 4;
+ pm8001_ha->memoryMap.region[PI].total_len = 4;
+ pm8001_ha->memoryMap.region[PI].alignment = 4;
+
+ /* MPI Memory region 5 inbound queues */
+ pm8001_ha->memoryMap.region[IB].num_elements = 256;
+ pm8001_ha->memoryMap.region[IB].element_size = 64;
+ pm8001_ha->memoryMap.region[IB].total_len = 256 * 64;
+ pm8001_ha->memoryMap.region[IB].alignment = 64;
+
+ /* MPI Memory region 6 inbound queues */
+ pm8001_ha->memoryMap.region[OB].num_elements = 256;
+ pm8001_ha->memoryMap.region[OB].element_size = 64;
+ pm8001_ha->memoryMap.region[OB].total_len = 256 * 64;
+ pm8001_ha->memoryMap.region[OB].alignment = 64;
+
+ /* Memory region write DMA*/
+ pm8001_ha->memoryMap.region[NVMD].num_elements = 1;
+ pm8001_ha->memoryMap.region[NVMD].element_size = 4096;
+ pm8001_ha->memoryMap.region[NVMD].total_len = 4096;
+ /* Memory region for devices*/
+ pm8001_ha->memoryMap.region[DEV_MEM].num_elements = 1;
+ pm8001_ha->memoryMap.region[DEV_MEM].element_size = PM8001_MAX_DEVICES *
+ sizeof(struct pm8001_device);
+ pm8001_ha->memoryMap.region[DEV_MEM].total_len = PM8001_MAX_DEVICES *
+ sizeof(struct pm8001_device);
+
+ /* Memory region for ccb_info*/
+ pm8001_ha->memoryMap.region[CCB_MEM].num_elements = 1;
+ pm8001_ha->memoryMap.region[CCB_MEM].element_size = PM8001_MAX_CCB *
+ sizeof(struct pm8001_ccb_info);
+ pm8001_ha->memoryMap.region[CCB_MEM].total_len = PM8001_MAX_CCB *
+ sizeof(struct pm8001_ccb_info);
+
+ for (i = 0; i < USI_MAX_MEMCNT; i++) {
+ if (pm8001_mem_alloc(pm8001_ha->pdev,
+ &pm8001_ha->memoryMap.region[i].virt_ptr,
+ &pm8001_ha->memoryMap.region[i].phys_addr,
+ &pm8001_ha->memoryMap.region[i].phys_addr_hi,
+ &pm8001_ha->memoryMap.region[i].phys_addr_lo,
+ pm8001_ha->memoryMap.region[i].total_len,
+ pm8001_ha->memoryMap.region[i].alignment) != 0) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Mem%d alloc failed\n",
+ i));
+ goto err_out;
+ }
+ }
+
+ pm8001_ha->devices = pm8001_ha->memoryMap.region[DEV_MEM].virt_ptr;
+ for (i = 0; i < PM8001_MAX_DEVICES; i++) {
+ pm8001_ha->devices[i].dev_type = NO_DEVICE;
+ pm8001_ha->devices[i].id = i;
+ pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES;
+ pm8001_ha->devices[i].running_req = 0;
+ }
+ pm8001_ha->ccb_info = pm8001_ha->memoryMap.region[CCB_MEM].virt_ptr;
+ for (i = 0; i < PM8001_MAX_CCB; i++) {
+ pm8001_ha->ccb_info[i].ccb_dma_handle =
+ pm8001_ha->memoryMap.region[CCB_MEM].phys_addr +
+ i * sizeof(struct pm8001_ccb_info);
+ pm8001_ha->ccb_info[i].task = NULL;
+ pm8001_ha->ccb_info[i].ccb_tag = 0xffffffff;
+ pm8001_ha->ccb_info[i].device = NULL;
+ ++pm8001_ha->tags_num;
+ }
+ pm8001_ha->flags = PM8001F_INIT_TIME;
+ /* Initialize tags */
+ pm8001_tag_init(pm8001_ha);
+ return 0;
+err_out:
+ return 1;
+}
+
+/**
+ * pm8001_ioremap - remap the pci high physical address to kernal virtual
+ * address so that we can access them.
+ * @pm8001_ha:our hba structure.
+ */
+static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 bar;
+ u32 logicalBar = 0;
+ struct pci_dev *pdev;
+
+ pdev = pm8001_ha->pdev;
+ /* map pci mem (PMC pci base 0-3)*/
+ for (bar = 0; bar < 6; bar++) {
+ /*
+ ** logical BARs for SPC:
+ ** bar 0 and 1 - logical BAR0
+ ** bar 2 and 3 - logical BAR1
+ ** bar4 - logical BAR2
+ ** bar5 - logical BAR3
+ ** Skip the appropriate assignments:
+ */
+ if ((bar == 1) || (bar == 3))
+ continue;
+ if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+ pm8001_ha->io_mem[logicalBar].membase =
+ pci_resource_start(pdev, bar);
+ pm8001_ha->io_mem[logicalBar].membase &=
+ (u32)PCI_BASE_ADDRESS_MEM_MASK;
+ pm8001_ha->io_mem[logicalBar].memsize =
+ pci_resource_len(pdev, bar);
+ pm8001_ha->io_mem[logicalBar].memvirtaddr =
+ ioremap(pm8001_ha->io_mem[logicalBar].membase,
+ pm8001_ha->io_mem[logicalBar].memsize);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("PCI: bar %d, logicalBar %d "
+ "virt_addr=%lx,len=%d\n", bar, logicalBar,
+ (unsigned long)
+ pm8001_ha->io_mem[logicalBar].memvirtaddr,
+ pm8001_ha->io_mem[logicalBar].memsize));
+ } else {
+ pm8001_ha->io_mem[logicalBar].membase = 0;
+ pm8001_ha->io_mem[logicalBar].memsize = 0;
+ pm8001_ha->io_mem[logicalBar].memvirtaddr = 0;
+ }
+ logicalBar++;
+ }
+ return 0;
+}
+
+/**
+ * pm8001_pci_alloc - initialize our ha card structure
+ * @pdev: pci device.
+ * @ent: ent
+ * @shost: scsi host struct which has been initialized before.
+ */
+static struct pm8001_hba_info *__devinit
+pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost)
+{
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+
+ pm8001_ha = sha->lldd_ha;
+ if (!pm8001_ha)
+ return NULL;
+
+ pm8001_ha->pdev = pdev;
+ pm8001_ha->dev = &pdev->dev;
+ pm8001_ha->chip_id = chip_id;
+ pm8001_ha->chip = &pm8001_chips[pm8001_ha->chip_id];
+ pm8001_ha->irq = pdev->irq;
+ pm8001_ha->sas = sha;
+ pm8001_ha->shost = shost;
+ pm8001_ha->id = pm8001_id++;
+ INIT_LIST_HEAD(&pm8001_ha->wq_list);
+ pm8001_ha->logging_level = 0x01;
+ sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id);
+#ifdef PM8001_USE_TASKLET
+ tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
+ (unsigned long)pm8001_ha);
+#endif
+ pm8001_ioremap(pm8001_ha);
+ if (!pm8001_alloc(pm8001_ha))
+ return pm8001_ha;
+ pm8001_free(pm8001_ha);
+ return NULL;
+}
+
+/**
+ * pci_go_44 - pm8001 specified, its DMA is 44 bit rather than 64 bit
+ * @pdev: pci device.
+ */
+static int pci_go_44(struct pci_dev *pdev)
+{
+ int rc;
+
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(44))) {
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(44));
+ if (rc) {
+ rc = pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "44-bit DMA enable failed\n");
+ return rc;
+ }
+ }
+ } else {
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit DMA enable failed\n");
+ return rc;
+ }
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "32-bit consistent DMA enable failed\n");
+ return rc;
+ }
+ }
+ return rc;
+}
+
+/**
+ * pm8001_prep_sas_ha_init - allocate memory in general hba struct && init them.
+ * @shost: scsi host which has been allocated outside.
+ * @chip_info: our ha struct.
+ */
+static int __devinit pm8001_prep_sas_ha_init(struct Scsi_Host * shost,
+ const struct pm8001_chip_info *chip_info)
+{
+ int phy_nr, port_nr;
+ struct asd_sas_phy **arr_phy;
+ struct asd_sas_port **arr_port;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ phy_nr = chip_info->n_phy;
+ port_nr = phy_nr;
+ memset(sha, 0x00, sizeof(*sha));
+ arr_phy = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_phy)
+ goto exit;
+ arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_port)
+ goto exit_free2;
+
+ sha->sas_phy = arr_phy;
+ sha->sas_port = arr_port;
+ sha->lldd_ha = kzalloc(sizeof(struct pm8001_hba_info), GFP_KERNEL);
+ if (!sha->lldd_ha)
+ goto exit_free1;
+
+ shost->transportt = pm8001_stt;
+ shost->max_id = PM8001_MAX_DEVICES;
+ shost->max_lun = 8;
+ shost->max_channel = 0;
+ shost->unique_id = pm8001_id;
+ shost->max_cmd_len = 16;
+ shost->can_queue = PM8001_CAN_QUEUE;
+ shost->cmd_per_lun = 32;
+ return 0;
+exit_free1:
+ kfree(arr_port);
+exit_free2:
+ kfree(arr_phy);
+exit:
+ return -1;
+}
+
+/**
+ * pm8001_post_sas_ha_init - initialize general hba struct defined in libsas
+ * @shost: scsi host which has been allocated outside
+ * @chip_info: our ha struct.
+ */
+static void __devinit pm8001_post_sas_ha_init(struct Scsi_Host *shost,
+ const struct pm8001_chip_info *chip_info)
+{
+ int i = 0;
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+ pm8001_ha = sha->lldd_ha;
+ for (i = 0; i < chip_info->n_phy; i++) {
+ sha->sas_phy[i] = &pm8001_ha->phy[i].sas_phy;
+ sha->sas_port[i] = &pm8001_ha->port[i].sas_port;
+ }
+ sha->sas_ha_name = DRV_NAME;
+ sha->dev = pm8001_ha->dev;
+
+ sha->lldd_module = THIS_MODULE;
+ sha->sas_addr = &pm8001_ha->sas_addr[0];
+ sha->num_phys = chip_info->n_phy;
+ sha->lldd_max_execute_num = 1;
+ sha->lldd_queue_size = PM8001_CAN_QUEUE;
+ sha->core.shost = shost;
+}
+
+/**
+ * pm8001_init_sas_add - initialize sas address
+ * @chip_info: our ha struct.
+ *
+ * Currently we just set the fixed SAS address to our HBA,for manufacture,
+ * it should read from the EEPROM
+ */
+static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
+{
+ u8 i;
+#ifdef PM8001_READ_VPD
+ DECLARE_COMPLETION_ONSTACK(completion);
+ pm8001_ha->nvmd_completion = &completion;
+ PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, 0, 0);
+ wait_for_completion(&completion);
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ memcpy(&pm8001_ha->phy[i].dev_sas_addr, pm8001_ha->sas_addr,
+ SAS_ADDR_SIZE);
+ PM8001_INIT_DBG(pm8001_ha,
+ pm8001_printk("phy %d sas_addr = %x \n", i,
+ (u64)pm8001_ha->phy[i].dev_sas_addr));
+ }
+#else
+ for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+ pm8001_ha->phy[i].dev_sas_addr = 0x500e004010000004ULL;
+ pm8001_ha->phy[i].dev_sas_addr =
+ cpu_to_be64((u64)
+ (*(u64 *)&pm8001_ha->phy[i].dev_sas_addr));
+ }
+ memcpy(pm8001_ha->sas_addr, &pm8001_ha->phy[0].dev_sas_addr,
+ SAS_ADDR_SIZE);
+#endif
+}
+
+#ifdef PM8001_USE_MSIX
+/**
+ * pm8001_setup_msix - enable MSI-X interrupt
+ * @chip_info: our ha struct.
+ * @irq_handler: irq_handler
+ */
+static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha,
+ irq_handler_t irq_handler)
+{
+ u32 i = 0, j = 0;
+ u32 number_of_intr = 1;
+ int flag = 0;
+ u32 max_entry;
+ int rc;
+ max_entry = sizeof(pm8001_ha->msix_entries) /
+ sizeof(pm8001_ha->msix_entries[0]);
+ flag |= IRQF_DISABLED;
+ for (i = 0; i < max_entry ; i++)
+ pm8001_ha->msix_entries[i].entry = i;
+ rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries,
+ number_of_intr);
+ pm8001_ha->number_of_intr = number_of_intr;
+ if (!rc) {
+ for (i = 0; i < number_of_intr; i++) {
+ if (request_irq(pm8001_ha->msix_entries[i].vector,
+ irq_handler, flag, DRV_NAME,
+ SHOST_TO_SAS_HA(pm8001_ha->shost))) {
+ for (j = 0; j < i; j++)
+ free_irq(
+ pm8001_ha->msix_entries[j].vector,
+ SHOST_TO_SAS_HA(pm8001_ha->shost));
+ pci_disable_msix(pm8001_ha->pdev);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+#endif
+
+/**
+ * pm8001_request_irq - register interrupt
+ * @chip_info: our ha struct.
+ */
+static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha)
+{
+ struct pci_dev *pdev;
+ irq_handler_t irq_handler = pm8001_interrupt;
+ int rc;
+
+ pdev = pm8001_ha->pdev;
+
+#ifdef PM8001_USE_MSIX
+ if (pci_find_capability(pdev, PCI_CAP_ID_MSIX))
+ return pm8001_setup_msix(pm8001_ha, irq_handler);
+ else
+ goto intx;
+#endif
+
+intx:
+ /* intialize the INT-X interrupt */
+ rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME,
+ SHOST_TO_SAS_HA(pm8001_ha->shost));
+ return rc;
+}
+
+/**
+ * pm8001_pci_probe - probe supported device
+ * @pdev: pci device which kernel has been prepared for.
+ * @ent: pci device id
+ *
+ * This function is the main initialization function, when register a new
+ * pci driver it is invoked, all struct an hardware initilization should be done
+ * here, also, register interrupt
+ */
+static int __devinit pm8001_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ unsigned int rc;
+ u32 pci_reg;
+ struct pm8001_hba_info *pm8001_ha;
+ struct Scsi_Host *shost = NULL;
+ const struct pm8001_chip_info *chip;
+
+ dev_printk(KERN_INFO, &pdev->dev,
+ "pm8001: driver version %s\n", DRV_VERSION);
+ rc = pci_enable_device(pdev);
+ if (rc)
+ goto err_out_enable;
+ pci_set_master(pdev);
+ /*
+ * Enable pci slot busmaster by setting pci command register.
+ * This is required by FW for Cyclone card.
+ */
+
+ pci_read_config_dword(pdev, PCI_COMMAND, &pci_reg);
+ pci_reg |= 0x157;
+ pci_write_config_dword(pdev, PCI_COMMAND, pci_reg);
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc)
+ goto err_out_disable;
+ rc = pci_go_44(pdev);
+ if (rc)
+ goto err_out_regions;
+
+ shost = scsi_host_alloc(&pm8001_sht, sizeof(void *));
+ if (!shost) {
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+ chip = &pm8001_chips[ent->driver_data];
+ SHOST_TO_SAS_HA(shost) =
+ kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
+ if (!SHOST_TO_SAS_HA(shost)) {
+ rc = -ENOMEM;
+ goto err_out_free_host;
+ }
+
+ rc = pm8001_prep_sas_ha_init(shost, chip);
+ if (rc) {
+ rc = -ENOMEM;
+ goto err_out_free;
+ }
+ pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost));
+ pm8001_ha = pm8001_pci_alloc(pdev, chip_8001, shost);
+ if (!pm8001_ha) {
+ rc = -ENOMEM;
+ goto err_out_free;
+ }
+ list_add_tail(&pm8001_ha->list, &hba_list);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+ rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
+ if (rc)
+ goto err_out_ha_free;
+
+ rc = scsi_add_host(shost, &pdev->dev);
+ if (rc)
+ goto err_out_ha_free;
+ rc = pm8001_request_irq(pm8001_ha);
+ if (rc)
+ goto err_out_shost;
+
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
+ pm8001_init_sas_add(pm8001_ha);
+ pm8001_post_sas_ha_init(shost, chip);
+ rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
+ if (rc)
+ goto err_out_shost;
+ scsi_scan_host(pm8001_ha->shost);
+ return 0;
+
+err_out_shost:
+ scsi_remove_host(pm8001_ha->shost);
+err_out_ha_free:
+ pm8001_free(pm8001_ha);
+err_out_free:
+ kfree(SHOST_TO_SAS_HA(shost));
+err_out_free_host:
+ kfree(shost);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out_disable:
+ pci_disable_device(pdev);
+err_out_enable:
+ return rc;
+}
+
+static void __devexit pm8001_pci_remove(struct pci_dev *pdev)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct pm8001_hba_info *pm8001_ha;
+ int i;
+ pm8001_ha = sha->lldd_ha;
+ pci_set_drvdata(pdev, NULL);
+ sas_unregister_ha(sha);
+ sas_remove_host(pm8001_ha->shost);
+ list_del(&pm8001_ha->list);
+ scsi_remove_host(pm8001_ha->shost);
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+
+#ifdef PM8001_USE_MSIX
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ synchronize_irq(pm8001_ha->msix_entries[i].vector);
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ free_irq(pm8001_ha->msix_entries[i].vector, sha);
+ pci_disable_msix(pdev);
+#else
+ free_irq(pm8001_ha->irq, sha);
+#endif
+#ifdef PM8001_USE_TASKLET
+ tasklet_kill(&pm8001_ha->tasklet);
+#endif
+ pm8001_free(pm8001_ha);
+ kfree(sha->sas_phy);
+ kfree(sha->sas_port);
+ kfree(sha);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+/**
+ * pm8001_pci_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 pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct pm8001_hba_info *pm8001_ha;
+ int i , pos;
+ u32 device_state;
+ pm8001_ha = sha->lldd_ha;
+ flush_scheduled_work();
+ scsi_block_requests(pm8001_ha->shost);
+ pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pos == 0) {
+ printk(KERN_ERR " PCI PM not supported\n");
+ return -ENODEV;
+ }
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+#ifdef PM8001_USE_MSIX
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ synchronize_irq(pm8001_ha->msix_entries[i].vector);
+ for (i = 0; i < pm8001_ha->number_of_intr; i++)
+ free_irq(pm8001_ha->msix_entries[i].vector, sha);
+ pci_disable_msix(pdev);
+#else
+ free_irq(pm8001_ha->irq, sha);
+#endif
+#ifdef PM8001_USE_TASKLET
+ tasklet_kill(&pm8001_ha->tasklet);
+#endif
+ device_state = pci_choose_state(pdev, state);
+ pm8001_printk("pdev=0x%p, slot=%s, entering "
+ "operating state [D%d]\n", pdev,
+ pm8001_ha->name, device_state);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, device_state);
+ return 0;
+}
+
+/**
+ * pm8001_pci_resume - power management resume main entry point
+ * @pdev: PCI device struct
+ *
+ * Returns 0 success, anything else error.
+ */
+static int pm8001_pci_resume(struct pci_dev *pdev)
+{
+ struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+ struct pm8001_hba_info *pm8001_ha;
+ int rc;
+ u32 device_state;
+ pm8001_ha = sha->lldd_ha;
+ device_state = pdev->current_state;
+
+ pm8001_printk("pdev=0x%p, slot=%s, resuming from previous "
+ "operating state [D%d]\n", pdev, pm8001_ha->name, device_state);
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ pm8001_printk("slot=%s Enable device failed during resume\n",
+ pm8001_ha->name);
+ goto err_out_enable;
+ }
+
+ pci_set_master(pdev);
+ rc = pci_go_44(pdev);
+ if (rc)
+ goto err_out_disable;
+
+ PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+ rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
+ if (rc)
+ goto err_out_disable;
+ PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+ rc = pm8001_request_irq(pm8001_ha);
+ if (rc)
+ goto err_out_disable;
+ #ifdef PM8001_USE_TASKLET
+ tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
+ (unsigned long)pm8001_ha);
+ #endif
+ PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
+ scsi_unblock_requests(pm8001_ha->shost);
+ return 0;
+
+err_out_disable:
+ scsi_remove_host(pm8001_ha->shost);
+ pci_disable_device(pdev);
+err_out_enable:
+ return rc;
+}
+
+static struct pci_device_id __devinitdata pm8001_pci_table[] = {
+ {
+ PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001
+ },
+ {
+ PCI_DEVICE(0x117c, 0x0042),
+ .driver_data = chip_8001
+ },
+ {} /* terminate list */
+};
+
+static struct pci_driver pm8001_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = pm8001_pci_table,
+ .probe = pm8001_pci_probe,
+ .remove = __devexit_p(pm8001_pci_remove),
+ .suspend = pm8001_pci_suspend,
+ .resume = pm8001_pci_resume,
+};
+
+/**
+ * pm8001_init - initialize scsi transport template
+ */
+static int __init pm8001_init(void)
+{
+ int rc;
+ pm8001_id = 0;
+ pm8001_stt = sas_domain_attach_transport(&pm8001_transport_ops);
+ if (!pm8001_stt)
+ return -ENOMEM;
+ rc = pci_register_driver(&pm8001_pci_driver);
+ if (rc)
+ goto err_out;
+ return 0;
+err_out:
+ sas_release_transport(pm8001_stt);
+ return rc;
+}
+
+static void __exit pm8001_exit(void)
+{
+ pci_unregister_driver(&pm8001_pci_driver);
+ sas_release_transport(pm8001_stt);
+}
+
+module_init(pm8001_init);
+module_exit(pm8001_exit);
+
+MODULE_AUTHOR("Jack Wang <jack_wang@usish.com>");
+MODULE_DESCRIPTION("PMC-Sierra PM8001 SAS/SATA controller driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, pm8001_pci_table);
+
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c
new file mode 100644
index 00000000000..1f767a0e727
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_sas.c
@@ -0,0 +1,1103 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include "pm8001_sas.h"
+
+/**
+ * pm8001_find_tag - from sas task to find out tag that belongs to this task
+ * @task: the task sent to the LLDD
+ * @tag: the found tag associated with the task
+ */
+static int pm8001_find_tag(struct sas_task *task, u32 *tag)
+{
+ if (task->lldd_task) {
+ struct pm8001_ccb_info *ccb;
+ ccb = task->lldd_task;
+ *tag = ccb->ccb_tag;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * pm8001_tag_clear - clear the tags bitmap
+ * @pm8001_ha: our hba struct
+ * @tag: the found tag associated with the task
+ */
+static void pm8001_tag_clear(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+ void *bitmap = pm8001_ha->tags;
+ clear_bit(tag, bitmap);
+}
+
+static void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+ pm8001_tag_clear(pm8001_ha, tag);
+}
+
+static void pm8001_tag_set(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+ void *bitmap = pm8001_ha->tags;
+ set_bit(tag, bitmap);
+}
+
+/**
+ * pm8001_tag_alloc - allocate a empty tag for task used.
+ * @pm8001_ha: our hba struct
+ * @tag_out: the found empty tag .
+ */
+inline int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out)
+{
+ unsigned int index, tag;
+ void *bitmap = pm8001_ha->tags;
+
+ index = find_first_zero_bit(bitmap, pm8001_ha->tags_num);
+ tag = index;
+ if (tag >= pm8001_ha->tags_num)
+ return -SAS_QUEUE_FULL;
+ pm8001_tag_set(pm8001_ha, tag);
+ *tag_out = tag;
+ return 0;
+}
+
+void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha)
+{
+ int i;
+ for (i = 0; i < pm8001_ha->tags_num; ++i)
+ pm8001_tag_clear(pm8001_ha, i);
+}
+
+ /**
+ * pm8001_mem_alloc - allocate memory for pm8001.
+ * @pdev: pci device.
+ * @virt_addr: the allocated virtual address
+ * @pphys_addr_hi: the physical address high byte address.
+ * @pphys_addr_lo: the physical address low byte address.
+ * @mem_size: memory size.
+ */
+int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
+ dma_addr_t *pphys_addr, u32 *pphys_addr_hi,
+ u32 *pphys_addr_lo, u32 mem_size, u32 align)
+{
+ caddr_t mem_virt_alloc;
+ dma_addr_t mem_dma_handle;
+ u64 phys_align;
+ u64 align_offset = 0;
+ if (align)
+ align_offset = (dma_addr_t)align - 1;
+ mem_virt_alloc =
+ pci_alloc_consistent(pdev, mem_size + align, &mem_dma_handle);
+ if (!mem_virt_alloc) {
+ pm8001_printk("memory allocation error\n");
+ return -1;
+ }
+ memset((void *)mem_virt_alloc, 0, mem_size+align);
+ *pphys_addr = mem_dma_handle;
+ phys_align = (*pphys_addr + align_offset) & ~align_offset;
+ *virt_addr = (void *)mem_virt_alloc + phys_align - *pphys_addr;
+ *pphys_addr_hi = upper_32_bits(phys_align);
+ *pphys_addr_lo = lower_32_bits(phys_align);
+ return 0;
+}
+/**
+ * pm8001_find_ha_by_dev - from domain device which come from sas layer to
+ * find out our hba struct.
+ * @dev: the domain device which from sas layer.
+ */
+static
+struct pm8001_hba_info *pm8001_find_ha_by_dev(struct domain_device *dev)
+{
+ struct sas_ha_struct *sha = dev->port->ha;
+ struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+ return pm8001_ha;
+}
+
+/**
+ * pm8001_phy_control - this function should be registered to
+ * sas_domain_function_template to provide libsas used, note: this is just
+ * control the HBA phy rather than other expander phy if you want control
+ * other phy, you should use SMP command.
+ * @sas_phy: which phy in HBA phys.
+ * @func: the operation.
+ * @funcdata: always NULL.
+ */
+int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata)
+{
+ int rc = 0, phy_id = sas_phy->id;
+ struct pm8001_hba_info *pm8001_ha = NULL;
+ struct sas_phy_linkrates *rates;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ pm8001_ha = sas_phy->ha->lldd_ha;
+ pm8001_ha->phy[phy_id].enable_completion = &completion;
+ switch (func) {
+ case PHY_FUNC_SET_LINK_RATE:
+ rates = funcdata;
+ if (rates->minimum_linkrate) {
+ pm8001_ha->phy[phy_id].minimum_linkrate =
+ rates->minimum_linkrate;
+ }
+ if (rates->maximum_linkrate) {
+ pm8001_ha->phy[phy_id].maximum_linkrate =
+ rates->maximum_linkrate;
+ }
+ if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+ wait_for_completion(&completion);
+ }
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_LINK_RESET);
+ break;
+ case PHY_FUNC_HARD_RESET:
+ if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+ wait_for_completion(&completion);
+ }
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_HARD_RESET);
+ break;
+ case PHY_FUNC_LINK_RESET:
+ if (pm8001_ha->phy[phy_id].phy_state == 0) {
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+ wait_for_completion(&completion);
+ }
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_LINK_RESET);
+ break;
+ case PHY_FUNC_RELEASE_SPINUP_HOLD:
+ PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+ PHY_LINK_RESET);
+ break;
+ case PHY_FUNC_DISABLE:
+ PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, phy_id);
+ break;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ msleep(300);
+ return rc;
+}
+
+int pm8001_slave_alloc(struct scsi_device *scsi_dev)
+{
+ struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
+ if (dev_is_sata(dev)) {
+ /* We don't need to rescan targets
+ * if REPORT_LUNS request is failed
+ */
+ if (scsi_dev->lun > 0)
+ return -ENXIO;
+ scsi_dev->tagged_supported = 1;
+ }
+ return sas_slave_alloc(scsi_dev);
+}
+
+/**
+ * pm8001_scan_start - we should enable all HBA phys by sending the phy_start
+ * command to HBA.
+ * @shost: the scsi host data.
+ */
+void pm8001_scan_start(struct Scsi_Host *shost)
+{
+ int i;
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+ pm8001_ha = sha->lldd_ha;
+ PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha);
+ for (i = 0; i < pm8001_ha->chip->n_phy; ++i)
+ PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i);
+}
+
+int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ /* give the phy enabling interrupt event time to come in (1s
+ * is empirically about all it takes) */
+ if (time < HZ)
+ return 0;
+ /* Wait for discovery to finish */
+ scsi_flush_work(shost);
+ return 1;
+}
+
+/**
+ * pm8001_task_prep_smp - the dispatcher function, prepare data for smp task
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to smp task
+ */
+static int pm8001_task_prep_smp(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ return PM8001_CHIP_DISP->smp_req(pm8001_ha, ccb);
+}
+
+u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag)
+{
+ struct ata_queued_cmd *qc = task->uldd_task;
+ if (qc) {
+ if (qc->tf.command == ATA_CMD_FPDMA_WRITE ||
+ qc->tf.command == ATA_CMD_FPDMA_READ) {
+ *tag = qc->tag;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * pm8001_task_prep_ata - the dispatcher function, prepare data for sata task
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to sata task
+ */
+static int pm8001_task_prep_ata(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ return PM8001_CHIP_DISP->sata_req(pm8001_ha, ccb);
+}
+
+/**
+ * pm8001_task_prep_ssp_tm - the dispatcher function, prepare task management data
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to TM
+ * @tmf: the task management IU
+ */
+static int pm8001_task_prep_ssp_tm(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+{
+ return PM8001_CHIP_DISP->ssp_tm_req(pm8001_ha, ccb, tmf);
+}
+
+/**
+ * pm8001_task_prep_ssp - the dispatcher function,prepare ssp data for ssp task
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to ssp task
+ */
+static int pm8001_task_prep_ssp(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb)
+{
+ return PM8001_CHIP_DISP->ssp_io_req(pm8001_ha, ccb);
+}
+int pm8001_slave_configure(struct scsi_device *sdev)
+{
+ struct domain_device *dev = sdev_to_domain_dev(sdev);
+ int ret = sas_slave_configure(sdev);
+ if (ret)
+ return ret;
+ if (dev_is_sata(dev)) {
+ #ifdef PM8001_DISABLE_NCQ
+ struct ata_port *ap = dev->sata_dev.ap;
+ struct ata_device *adev = ap->link.device;
+ adev->flags |= ATA_DFLAG_NCQ_OFF;
+ scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, 1);
+ #endif
+ }
+ return 0;
+}
+/**
+ * pm8001_task_exec - queue the task(ssp, smp && ata) to the hardware.
+ * @task: the task to be execute.
+ * @num: if can_queue great than 1, the task can be queued up. for SMP task,
+ * we always execute one one time.
+ * @gfp_flags: gfp_flags.
+ * @is_tmf: if it is task management task.
+ * @tmf: the task management IU
+ */
+#define DEV_IS_GONE(pm8001_dev) \
+ ((!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE)))
+static int pm8001_task_exec(struct sas_task *task, const int num,
+ gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf)
+{
+ struct domain_device *dev = task->dev;
+ struct pm8001_hba_info *pm8001_ha;
+ struct pm8001_device *pm8001_dev;
+ struct sas_task *t = task;
+ struct pm8001_ccb_info *ccb;
+ u32 tag = 0xdeadbeef, rc, n_elem = 0;
+ u32 n = num;
+ unsigned long flags = 0;
+
+ if (!dev->port) {
+ struct task_status_struct *tsm = &t->task_status;
+ tsm->resp = SAS_TASK_UNDELIVERED;
+ tsm->stat = SAS_PHY_DOWN;
+ if (dev->dev_type != SATA_DEV)
+ t->task_done(t);
+ return 0;
+ }
+ pm8001_ha = pm8001_find_ha_by_dev(task->dev);
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("pm8001_task_exec device \n "));
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ do {
+ dev = t->dev;
+ pm8001_dev = dev->lldd_dev;
+ if (DEV_IS_GONE(pm8001_dev)) {
+ if (pm8001_dev) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("device %d not ready.\n",
+ pm8001_dev->device_id));
+ } else {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("device %016llx not "
+ "ready.\n", SAS_ADDR(dev->sas_addr)));
+ }
+ rc = SAS_PHY_DOWN;
+ goto out_done;
+ }
+ rc = pm8001_tag_alloc(pm8001_ha, &tag);
+ if (rc)
+ goto err_out;
+ ccb = &pm8001_ha->ccb_info[tag];
+
+ if (!sas_protocol_ata(t->task_proto)) {
+ if (t->num_scatter) {
+ n_elem = dma_map_sg(pm8001_ha->dev,
+ t->scatter,
+ t->num_scatter,
+ t->data_dir);
+ if (!n_elem) {
+ rc = -ENOMEM;
+ goto err_out_tag;
+ }
+ }
+ } else {
+ n_elem = t->num_scatter;
+ }
+
+ t->lldd_task = ccb;
+ ccb->n_elem = n_elem;
+ ccb->ccb_tag = tag;
+ ccb->task = t;
+ switch (t->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ rc = pm8001_task_prep_smp(pm8001_ha, ccb);
+ break;
+ case SAS_PROTOCOL_SSP:
+ if (is_tmf)
+ rc = pm8001_task_prep_ssp_tm(pm8001_ha,
+ ccb, tmf);
+ else
+ rc = pm8001_task_prep_ssp(pm8001_ha, ccb);
+ break;
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ rc = pm8001_task_prep_ata(pm8001_ha, ccb);
+ break;
+ default:
+ dev_printk(KERN_ERR, pm8001_ha->dev,
+ "unknown sas_task proto: 0x%x\n",
+ t->task_proto);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc) {
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("rc is %x\n", rc));
+ goto err_out_tag;
+ }
+ /* TODO: select normal or high priority */
+ spin_lock(&t->task_state_lock);
+ t->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock(&t->task_state_lock);
+ pm8001_dev->running_req++;
+ if (n > 1)
+ t = list_entry(t->list.next, struct sas_task, list);
+ } while (--n);
+ rc = 0;
+ goto out_done;
+
+err_out_tag:
+ pm8001_tag_free(pm8001_ha, tag);
+err_out:
+ dev_printk(KERN_ERR, pm8001_ha->dev, "pm8001 exec failed[%d]!\n", rc);
+ if (!sas_protocol_ata(t->task_proto))
+ if (n_elem)
+ dma_unmap_sg(pm8001_ha->dev, t->scatter, n_elem,
+ t->data_dir);
+out_done:
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return rc;
+}
+
+/**
+ * pm8001_queue_command - register for upper layer used, all IO commands sent
+ * to HBA are from this interface.
+ * @task: the task to be execute.
+ * @num: if can_queue great than 1, the task can be queued up. for SMP task,
+ * we always execute one one time
+ * @gfp_flags: gfp_flags
+ */
+int pm8001_queue_command(struct sas_task *task, const int num,
+ gfp_t gfp_flags)
+{
+ return pm8001_task_exec(task, num, gfp_flags, 0, NULL);
+}
+
+void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx)
+{
+ pm8001_tag_clear(pm8001_ha, ccb_idx);
+}
+
+/**
+ * pm8001_ccb_task_free - free the sg for ssp and smp command, free the ccb.
+ * @pm8001_ha: our hba card information
+ * @ccb: the ccb which attached to ssp task
+ * @task: the task to be free.
+ * @ccb_idx: ccb index.
+ */
+void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx)
+{
+ if (!ccb->task)
+ return;
+ if (!sas_protocol_ata(task->task_proto))
+ if (ccb->n_elem)
+ dma_unmap_sg(pm8001_ha->dev, task->scatter,
+ task->num_scatter, task->data_dir);
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_resp, 1,
+ PCI_DMA_FROMDEVICE);
+ dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_req, 1,
+ PCI_DMA_TODEVICE);
+ break;
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SSP:
+ default:
+ /* do nothing */
+ break;
+ }
+ task->lldd_task = NULL;
+ ccb->task = NULL;
+ ccb->ccb_tag = 0xFFFFFFFF;
+ pm8001_ccb_free(pm8001_ha, ccb_idx);
+}
+
+ /**
+ * pm8001_alloc_dev - find a empty pm8001_device
+ * @pm8001_ha: our hba card information
+ */
+struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha)
+{
+ u32 dev;
+ for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) {
+ if (pm8001_ha->devices[dev].dev_type == NO_DEVICE) {
+ pm8001_ha->devices[dev].id = dev;
+ return &pm8001_ha->devices[dev];
+ }
+ }
+ if (dev == PM8001_MAX_DEVICES) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("max support %d devices, ignore ..\n",
+ PM8001_MAX_DEVICES));
+ }
+ return NULL;
+}
+
+static void pm8001_free_dev(struct pm8001_device *pm8001_dev)
+{
+ u32 id = pm8001_dev->id;
+ memset(pm8001_dev, 0, sizeof(*pm8001_dev));
+ pm8001_dev->id = id;
+ pm8001_dev->dev_type = NO_DEVICE;
+ pm8001_dev->device_id = PM8001_MAX_DEVICES;
+ pm8001_dev->sas_device = NULL;
+}
+
+/**
+ * pm8001_dev_found_notify - libsas notify a device is found.
+ * @dev: the device structure which sas layer used.
+ *
+ * when libsas find a sas domain device, it should tell the LLDD that
+ * device is found, and then LLDD register this device to HBA firmware
+ * by the command "OPC_INB_REG_DEV", after that the HBA will assign a
+ * device ID(according to device's sas address) and returned it to LLDD. From
+ * now on, we communicate with HBA FW with the device ID which HBA assigned
+ * rather than sas address. it is the neccessary step for our HBA but it is
+ * the optional for other HBA driver.
+ */
+static int pm8001_dev_found_notify(struct domain_device *dev)
+{
+ unsigned long flags = 0;
+ int res = 0;
+ struct pm8001_hba_info *pm8001_ha = NULL;
+ struct domain_device *parent_dev = dev->parent;
+ struct pm8001_device *pm8001_device;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ u32 flag = 0;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ pm8001_device = pm8001_alloc_dev(pm8001_ha);
+ pm8001_device->sas_device = dev;
+ if (!pm8001_device) {
+ res = -1;
+ goto found_out;
+ }
+ dev->lldd_dev = pm8001_device;
+ pm8001_device->dev_type = dev->dev_type;
+ pm8001_device->dcompletion = &completion;
+ if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+ int phy_id;
+ struct ex_phy *phy;
+ for (phy_id = 0; phy_id < parent_dev->ex_dev.num_phys;
+ phy_id++) {
+ phy = &parent_dev->ex_dev.ex_phy[phy_id];
+ if (SAS_ADDR(phy->attached_sas_addr)
+ == SAS_ADDR(dev->sas_addr)) {
+ pm8001_device->attached_phy = phy_id;
+ break;
+ }
+ }
+ if (phy_id == parent_dev->ex_dev.num_phys) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Error: no attached dev:%016llx"
+ " at ex:%016llx.\n", SAS_ADDR(dev->sas_addr),
+ SAS_ADDR(parent_dev->sas_addr)));
+ res = -1;
+ }
+ } else {
+ if (dev->dev_type == SATA_DEV) {
+ pm8001_device->attached_phy =
+ dev->rphy->identify.phy_identifier;
+ flag = 1; /* directly sata*/
+ }
+ } /*register this device to HBA*/
+ PM8001_DISC_DBG(pm8001_ha, pm8001_printk("Found device \n"));
+ PM8001_CHIP_DISP->reg_dev_req(pm8001_ha, pm8001_device, flag);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ wait_for_completion(&completion);
+ if (dev->dev_type == SAS_END_DEV)
+ msleep(50);
+ pm8001_ha->flags = PM8001F_RUN_TIME ;
+ return 0;
+found_out:
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return res;
+}
+
+int pm8001_dev_found(struct domain_device *dev)
+{
+ return pm8001_dev_found_notify(dev);
+}
+
+/**
+ * pm8001_alloc_task - allocate a task structure for TMF
+ */
+static struct sas_task *pm8001_alloc_task(void)
+{
+ struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL);
+ if (task) {
+ INIT_LIST_HEAD(&task->list);
+ spin_lock_init(&task->task_state_lock);
+ task->task_state_flags = SAS_TASK_STATE_PENDING;
+ init_timer(&task->timer);
+ init_completion(&task->completion);
+ }
+ return task;
+}
+
+static void pm8001_free_task(struct sas_task *task)
+{
+ if (task) {
+ BUG_ON(!list_empty(&task->list));
+ kfree(task);
+ }
+}
+
+static void pm8001_task_done(struct sas_task *task)
+{
+ if (!del_timer(&task->timer))
+ return;
+ complete(&task->completion);
+}
+
+static void pm8001_tmf_timedout(unsigned long data)
+{
+ struct sas_task *task = (struct sas_task *)data;
+
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ complete(&task->completion);
+}
+
+#define PM8001_TASK_TIMEOUT 20
+/**
+ * pm8001_exec_internal_tmf_task - execute some task management commands.
+ * @dev: the wanted device.
+ * @tmf: which task management wanted to be take.
+ * @para_len: para_len.
+ * @parameter: ssp task parameter.
+ *
+ * when errors or exception happened, we may want to do something, for example
+ * abort the issued task which result in this execption, it is done by calling
+ * this function, note it is also with the task execute interface.
+ */
+static int pm8001_exec_internal_tmf_task(struct domain_device *dev,
+ void *parameter, u32 para_len, struct pm8001_tmf_task *tmf)
+{
+ int res, retry;
+ struct sas_task *task = NULL;
+ struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+ for (retry = 0; retry < 3; retry++) {
+ task = pm8001_alloc_task();
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = dev;
+ task->task_proto = dev->tproto;
+ memcpy(&task->ssp_task, parameter, para_len);
+ task->task_done = pm8001_task_done;
+ task->timer.data = (unsigned long)task;
+ task->timer.function = pm8001_tmf_timedout;
+ task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
+ add_timer(&task->timer);
+
+ res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf);
+
+ if (res) {
+ del_timer(&task->timer);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Executing internal task "
+ "failed\n"));
+ goto ex_err;
+ }
+ wait_for_completion(&task->completion);
+ res = -TMF_RESP_FUNC_FAILED;
+ /* Even TMF timed out, return direct. */
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TMF task[%x]timeout.\n",
+ tmf->tmf));
+ goto ex_err;
+ }
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAM_GOOD) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_UNDERRUN) {
+ /* no error, but return the number of bytes of
+ * underrun */
+ res = task->task_status.residual;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_OVERRUN) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Blocked task error.\n"));
+ res = -EMSGSIZE;
+ break;
+ } else {
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk(" Task to dev %016llx response:"
+ "0x%x status 0x%x\n",
+ SAS_ADDR(dev->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat));
+ pm8001_free_task(task);
+ task = NULL;
+ }
+ }
+ex_err:
+ BUG_ON(retry == 3 && task != NULL);
+ if (task != NULL)
+ pm8001_free_task(task);
+ return res;
+}
+
+static int
+pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, struct domain_device *dev, u32 flag,
+ u32 task_tag)
+{
+ int res, retry;
+ u32 ccb_tag;
+ struct pm8001_ccb_info *ccb;
+ struct sas_task *task = NULL;
+
+ for (retry = 0; retry < 3; retry++) {
+ task = pm8001_alloc_task();
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = dev;
+ task->task_proto = dev->tproto;
+ task->task_done = pm8001_task_done;
+ task->timer.data = (unsigned long)task;
+ task->timer.function = pm8001_tmf_timedout;
+ task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
+ add_timer(&task->timer);
+
+ res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
+ if (res)
+ return res;
+ ccb = &pm8001_ha->ccb_info[ccb_tag];
+ ccb->device = pm8001_dev;
+ ccb->ccb_tag = ccb_tag;
+ ccb->task = task;
+
+ res = PM8001_CHIP_DISP->task_abort(pm8001_ha,
+ pm8001_dev, flag, task_tag, ccb_tag);
+
+ if (res) {
+ del_timer(&task->timer);
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("Executing internal task "
+ "failed\n"));
+ goto ex_err;
+ }
+ wait_for_completion(&task->completion);
+ res = TMF_RESP_FUNC_FAILED;
+ /* Even TMF timed out, return direct. */
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ PM8001_FAIL_DBG(pm8001_ha,
+ pm8001_printk("TMF task timeout.\n"));
+ goto ex_err;
+ }
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAM_GOOD) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ break;
+
+ } else {
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk(" Task to dev %016llx response: "
+ "0x%x status 0x%x\n",
+ SAS_ADDR(dev->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat));
+ pm8001_free_task(task);
+ task = NULL;
+ }
+ }
+ex_err:
+ BUG_ON(retry == 3 && task != NULL);
+ if (task != NULL)
+ pm8001_free_task(task);
+ return res;
+}
+
+/**
+ * pm8001_dev_gone_notify - see the comments for "pm8001_dev_found_notify"
+ * @dev: the device structure which sas layer used.
+ */
+static void pm8001_dev_gone_notify(struct domain_device *dev)
+{
+ unsigned long flags = 0;
+ u32 tag;
+ struct pm8001_hba_info *pm8001_ha;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ u32 device_id = pm8001_dev->device_id;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ pm8001_tag_alloc(pm8001_ha, &tag);
+ if (pm8001_dev) {
+ PM8001_DISC_DBG(pm8001_ha,
+ pm8001_printk("found dev[%d:%x] is gone.\n",
+ pm8001_dev->device_id, pm8001_dev->dev_type));
+ if (pm8001_dev->running_req) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ }
+ PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id);
+ pm8001_free_dev(pm8001_dev);
+ } else {
+ PM8001_DISC_DBG(pm8001_ha,
+ pm8001_printk("Found dev has gone.\n"));
+ }
+ dev->lldd_dev = NULL;
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+}
+
+void pm8001_dev_gone(struct domain_device *dev)
+{
+ pm8001_dev_gone_notify(dev);
+}
+
+static int pm8001_issue_ssp_tmf(struct domain_device *dev,
+ u8 *lun, struct pm8001_tmf_task *tmf)
+{
+ struct sas_ssp_task ssp_task;
+ if (!(dev->tproto & SAS_PROTOCOL_SSP))
+ return TMF_RESP_FUNC_ESUPP;
+
+ strncpy((u8 *)&ssp_task.LUN, lun, 8);
+ return pm8001_exec_internal_tmf_task(dev, &ssp_task, sizeof(ssp_task),
+ tmf);
+}
+
+/**
+ * Standard mandates link reset for ATA (type 0) and hard reset for
+ * SSP (type 1) , only for RECOVERY
+ */
+int pm8001_I_T_nexus_reset(struct domain_device *dev)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_device *pm8001_dev;
+ struct pm8001_hba_info *pm8001_ha;
+ struct sas_phy *phy;
+ if (!dev || !dev->lldd_dev)
+ return -1;
+
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ phy = sas_find_local_phy(dev);
+
+ if (dev_is_sata(dev)) {
+ DECLARE_COMPLETION_ONSTACK(completion_setstate);
+ rc = sas_phy_reset(phy, 1);
+ msleep(2000);
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ pm8001_dev->setds_completion = &completion_setstate;
+ rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+ pm8001_dev, 0x01);
+ wait_for_completion(&completion_setstate);
+ } else{
+ rc = sas_phy_reset(phy, 1);
+ msleep(2000);
+ }
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n",
+ pm8001_dev->device_id, rc));
+ return rc;
+}
+
+/* mandatory SAM-3, the task reset the specified LUN*/
+int pm8001_lu_reset(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+ if (dev_is_sata(dev)) {
+ struct sas_phy *phy = sas_find_local_phy(dev);
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+ dev, 1, 0);
+ rc = sas_phy_reset(phy, 1);
+ rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+ pm8001_dev, 0x01);
+ msleep(2000);
+ } else {
+ tmf_task.tmf = TMF_LU_RESET;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+ }
+ /* If failed, fall-through I_T_Nexus reset */
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("for device[%x]:rc=%d\n",
+ pm8001_dev->device_id, rc));
+ return rc;
+}
+
+/* optional SAM-3 */
+int pm8001_query_task(struct sas_task *task)
+{
+ u32 tag = 0xdeadbeef;
+ int i = 0;
+ struct scsi_lun lun;
+ struct pm8001_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return rc;
+
+ if (task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ struct domain_device *dev = task->dev;
+ struct pm8001_hba_info *pm8001_ha =
+ pm8001_find_ha_by_dev(dev);
+
+ int_to_scsilun(cmnd->device->lun, &lun);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ PM8001_EH_DBG(pm8001_ha, pm8001_printk("Query:["));
+ for (i = 0; i < 16; i++)
+ printk(KERN_INFO "%02x ", cmnd->cmnd[i]);
+ printk(KERN_INFO "]\n");
+ tmf_task.tmf = TMF_QUERY_TASK;
+ tmf_task.tag_of_task_to_be_managed = tag;
+
+ rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ switch (rc) {
+ /* The task is still in Lun, release it then */
+ case TMF_RESP_FUNC_SUCC:
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("The task is still in Lun \n"));
+ /* The task is not in Lun or failed, reset the phy */
+ case TMF_RESP_FUNC_FAILED:
+ case TMF_RESP_FUNC_COMPLETE:
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("The task is not in Lun or failed,"
+ " reset the phy \n"));
+ break;
+ }
+ }
+ pm8001_printk(":rc= %d\n", rc);
+ return rc;
+}
+
+/* mandatory SAM-3, still need free task/ccb info, abord the specified task */
+int pm8001_abort_task(struct sas_task *task)
+{
+ unsigned long flags;
+ u32 tag = 0xdeadbeef;
+ u32 device_id;
+ struct domain_device *dev ;
+ struct pm8001_hba_info *pm8001_ha = NULL;
+ struct pm8001_ccb_info *ccb;
+ struct scsi_lun lun;
+ struct pm8001_device *pm8001_dev;
+ struct pm8001_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return rc;
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ rc = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ }
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ if (task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ dev = task->dev;
+ ccb = task->lldd_task;
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ int_to_scsilun(cmnd->device->lun, &lun);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ printk(KERN_INFO "No such tag in %s\n", __func__);
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ device_id = pm8001_dev->device_id;
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("abort io to deviceid= %d\n", device_id));
+ tmf_task.tmf = TMF_ABORT_TASK;
+ tmf_task.tag_of_task_to_be_managed = tag;
+ rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+ pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+ pm8001_dev->sas_device, 0, tag);
+ } else if (task->task_proto & SAS_PROTOCOL_SATA ||
+ task->task_proto & SAS_PROTOCOL_STP) {
+ dev = task->dev;
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ printk(KERN_INFO "No such tag in %s\n", __func__);
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+ pm8001_dev->sas_device, 0, tag);
+ } else if (task->task_proto & SAS_PROTOCOL_SMP) {
+ /* SMP */
+ dev = task->dev;
+ pm8001_dev = dev->lldd_dev;
+ pm8001_ha = pm8001_find_ha_by_dev(dev);
+ rc = pm8001_find_tag(task, &tag);
+ if (rc == 0) {
+ printk(KERN_INFO "No such tag in %s\n", __func__);
+ rc = TMF_RESP_FUNC_FAILED;
+ return rc;
+ }
+ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+ pm8001_dev->sas_device, 0, tag);
+
+ }
+out:
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ pm8001_printk("rc= %d\n", rc);
+ return rc;
+}
+
+int pm8001_abort_task_set(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_ABORT_TASK_SET;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+ return rc;
+}
+
+int pm8001_clear_aca(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_CLEAR_ACA;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+
+ return rc;
+}
+
+int pm8001_clear_task_set(struct domain_device *dev, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct pm8001_tmf_task tmf_task;
+ struct pm8001_device *pm8001_dev = dev->lldd_dev;
+ struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+ PM8001_EH_DBG(pm8001_ha,
+ pm8001_printk("I_T_L_Q clear task set[%x]\n",
+ pm8001_dev->device_id));
+ tmf_task.tmf = TMF_CLEAR_TASK_SET;
+ rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+ return rc;
+}
+
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
new file mode 100644
index 00000000000..30f2ede55a7
--- /dev/null
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -0,0 +1,481 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_SAS_H_
+#define _PM8001_SAS_H_
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <scsi/libsas.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/sas_ata.h>
+#include <asm/atomic.h>
+#include "pm8001_defs.h"
+
+#define DRV_NAME "pm8001"
+#define DRV_VERSION "0.1.36"
+#define PM8001_FAIL_LOGGING 0x01 /* libsas EH function logging */
+#define PM8001_INIT_LOGGING 0x02 /* driver init logging */
+#define PM8001_DISC_LOGGING 0x04 /* discovery layer logging */
+#define PM8001_IO_LOGGING 0x08 /* I/O path logging */
+#define PM8001_EH_LOGGING 0x10 /* Error message logging */
+#define PM8001_IOCTL_LOGGING 0x20 /* IOCTL message logging */
+#define PM8001_MSG_LOGGING 0x40 /* misc message logging */
+#define pm8001_printk(format, arg...) printk(KERN_INFO "%s %d:" format,\
+ __func__, __LINE__, ## arg)
+#define PM8001_CHECK_LOGGING(HBA, LEVEL, CMD) \
+do { \
+ if (unlikely(HBA->logging_level & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
+} while (0);
+
+#define PM8001_EH_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_EH_LOGGING, CMD)
+
+#define PM8001_INIT_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_INIT_LOGGING, CMD)
+
+#define PM8001_DISC_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_DISC_LOGGING, CMD)
+
+#define PM8001_IO_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_IO_LOGGING, CMD)
+
+#define PM8001_FAIL_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_FAIL_LOGGING, CMD)
+
+#define PM8001_IOCTL_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_IOCTL_LOGGING, CMD)
+
+#define PM8001_MSG_DBG(HBA, CMD) \
+ PM8001_CHECK_LOGGING(HBA, PM8001_MSG_LOGGING, CMD)
+
+
+#define PM8001_USE_TASKLET
+#define PM8001_USE_MSIX
+
+
+#define DEV_IS_EXPANDER(type) ((type == EDGE_DEV) || (type == FANOUT_DEV))
+
+#define PM8001_NAME_LENGTH 32/* generic length of strings */
+extern struct list_head hba_list;
+extern const struct pm8001_dispatch pm8001_8001_dispatch;
+
+struct pm8001_hba_info;
+struct pm8001_ccb_info;
+struct pm8001_device;
+struct pm8001_tmf_task;
+struct pm8001_dispatch {
+ char *name;
+ int (*chip_init)(struct pm8001_hba_info *pm8001_ha);
+ int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha, u32 signature);
+ void (*chip_rst)(struct pm8001_hba_info *pm8001_ha);
+ int (*chip_ioremap)(struct pm8001_hba_info *pm8001_ha);
+ void (*chip_iounmap)(struct pm8001_hba_info *pm8001_ha);
+ irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha);
+ u32 (*is_our_interupt)(struct pm8001_hba_info *pm8001_ha);
+ int (*isr_process_oq)(struct pm8001_hba_info *pm8001_ha);
+ void (*interrupt_enable)(struct pm8001_hba_info *pm8001_ha);
+ void (*interrupt_disable)(struct pm8001_hba_info *pm8001_ha);
+ void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
+ int (*smp_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb);
+ int (*ssp_io_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb);
+ int (*sata_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb);
+ int (*phy_start_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id);
+ int (*phy_stop_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id);
+ int (*reg_dev_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 flag);
+ int (*dereg_dev_req)(struct pm8001_hba_info *pm8001_ha, u32 device_id);
+ int (*phy_ctl_req)(struct pm8001_hba_info *pm8001_ha,
+ u32 phy_id, u32 phy_op);
+ int (*task_abort)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag,
+ u32 cmd_tag);
+ int (*ssp_tm_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf);
+ int (*get_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
+ int (*set_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
+ int (*fw_flash_update_req)(struct pm8001_hba_info *pm8001_ha,
+ void *payload);
+ int (*set_dev_state_req)(struct pm8001_hba_info *pm8001_ha,
+ struct pm8001_device *pm8001_dev, u32 state);
+ int (*sas_diag_start_end_req)(struct pm8001_hba_info *pm8001_ha,
+ u32 state);
+ int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
+ u32 state);
+ int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
+};
+
+struct pm8001_chip_info {
+ u32 n_phy;
+ const struct pm8001_dispatch *dispatch;
+};
+#define PM8001_CHIP_DISP (pm8001_ha->chip->dispatch)
+
+struct pm8001_port {
+ struct asd_sas_port sas_port;
+};
+
+struct pm8001_phy {
+ struct pm8001_hba_info *pm8001_ha;
+ struct pm8001_port *port;
+ struct asd_sas_phy sas_phy;
+ struct sas_identify identify;
+ struct scsi_device *sdev;
+ u64 dev_sas_addr;
+ u32 phy_type;
+ struct completion *enable_completion;
+ u32 frame_rcvd_size;
+ u8 frame_rcvd[32];
+ u8 phy_attached;
+ u8 phy_state;
+ enum sas_linkrate minimum_linkrate;
+ enum sas_linkrate maximum_linkrate;
+};
+
+struct pm8001_device {
+ enum sas_dev_type dev_type;
+ struct domain_device *sas_device;
+ u32 attached_phy;
+ u32 id;
+ struct completion *dcompletion;
+ struct completion *setds_completion;
+ u32 device_id;
+ u32 running_req;
+};
+
+struct pm8001_prd_imt {
+ __le32 len;
+ __le32 e;
+};
+
+struct pm8001_prd {
+ __le64 addr; /* 64-bit buffer address */
+ struct pm8001_prd_imt im_len; /* 64-bit length */
+} __attribute__ ((packed));
+/*
+ * CCB(Command Control Block)
+ */
+struct pm8001_ccb_info {
+ struct list_head entry;
+ struct sas_task *task;
+ u32 n_elem;
+ u32 ccb_tag;
+ dma_addr_t ccb_dma_handle;
+ struct pm8001_device *device;
+ struct pm8001_prd buf_prd[PM8001_MAX_DMA_SG];
+ struct fw_control_ex *fw_control_context;
+};
+
+struct mpi_mem {
+ void *virt_ptr;
+ dma_addr_t phys_addr;
+ u32 phys_addr_hi;
+ u32 phys_addr_lo;
+ u32 total_len;
+ u32 num_elements;
+ u32 element_size;
+ u32 alignment;
+};
+
+struct mpi_mem_req {
+ /* The number of element in the mpiMemory array */
+ u32 count;
+ /* The array of structures that define memroy regions*/
+ struct mpi_mem region[USI_MAX_MEMCNT];
+};
+
+struct main_cfg_table {
+ u32 signature;
+ u32 interface_rev;
+ u32 firmware_rev;
+ u32 max_out_io;
+ u32 max_sgl;
+ u32 ctrl_cap_flag;
+ u32 gst_offset;
+ u32 inbound_queue_offset;
+ u32 outbound_queue_offset;
+ u32 inbound_q_nppd_hppd;
+ u32 outbound_hw_event_pid0_3;
+ u32 outbound_hw_event_pid4_7;
+ u32 outbound_ncq_event_pid0_3;
+ u32 outbound_ncq_event_pid4_7;
+ u32 outbound_tgt_ITNexus_event_pid0_3;
+ u32 outbound_tgt_ITNexus_event_pid4_7;
+ u32 outbound_tgt_ssp_event_pid0_3;
+ u32 outbound_tgt_ssp_event_pid4_7;
+ u32 outbound_tgt_smp_event_pid0_3;
+ u32 outbound_tgt_smp_event_pid4_7;
+ u32 upper_event_log_addr;
+ u32 lower_event_log_addr;
+ u32 event_log_size;
+ u32 event_log_option;
+ u32 upper_iop_event_log_addr;
+ u32 lower_iop_event_log_addr;
+ u32 iop_event_log_size;
+ u32 iop_event_log_option;
+ u32 fatal_err_interrupt;
+ u32 fatal_err_dump_offset0;
+ u32 fatal_err_dump_length0;
+ u32 fatal_err_dump_offset1;
+ u32 fatal_err_dump_length1;
+ u32 hda_mode_flag;
+ u32 anolog_setup_table_offset;
+};
+struct general_status_table {
+ u32 gst_len_mpistate;
+ u32 iq_freeze_state0;
+ u32 iq_freeze_state1;
+ u32 msgu_tcnt;
+ u32 iop_tcnt;
+ u32 reserved;
+ u32 phy_state[8];
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 recover_err_info[8];
+};
+struct inbound_queue_table {
+ u32 element_pri_size_cnt;
+ u32 upper_base_addr;
+ u32 lower_base_addr;
+ u32 ci_upper_base_addr;
+ u32 ci_lower_base_addr;
+ u32 pi_pci_bar;
+ u32 pi_offset;
+ u32 total_length;
+ void *base_virt;
+ void *ci_virt;
+ u32 reserved;
+ __le32 consumer_index;
+ u32 producer_idx;
+};
+struct outbound_queue_table {
+ u32 element_size_cnt;
+ u32 upper_base_addr;
+ u32 lower_base_addr;
+ void *base_virt;
+ u32 pi_upper_base_addr;
+ u32 pi_lower_base_addr;
+ u32 ci_pci_bar;
+ u32 ci_offset;
+ u32 total_length;
+ void *pi_virt;
+ u32 interrup_vec_cnt_delay;
+ u32 dinterrup_to_pci_offset;
+ __le32 producer_index;
+ u32 consumer_idx;
+};
+struct pm8001_hba_memspace {
+ void __iomem *memvirtaddr;
+ u64 membase;
+ u32 memsize;
+};
+struct pm8001_hba_info {
+ char name[PM8001_NAME_LENGTH];
+ struct list_head list;
+ unsigned long flags;
+ spinlock_t lock;/* host-wide lock */
+ struct pci_dev *pdev;/* our device */
+ struct device *dev;
+ struct pm8001_hba_memspace io_mem[6];
+ struct mpi_mem_req memoryMap;
+ void __iomem *msg_unit_tbl_addr;/*Message Unit Table Addr*/
+ void __iomem *main_cfg_tbl_addr;/*Main Config Table Addr*/
+ void __iomem *general_stat_tbl_addr;/*General Status Table Addr*/
+ void __iomem *inbnd_q_tbl_addr;/*Inbound Queue Config Table Addr*/
+ void __iomem *outbnd_q_tbl_addr;/*Outbound Queue Config Table Addr*/
+ struct main_cfg_table main_cfg_tbl;
+ struct general_status_table gs_tbl;
+ struct inbound_queue_table inbnd_q_tbl[PM8001_MAX_INB_NUM];
+ struct outbound_queue_table outbnd_q_tbl[PM8001_MAX_OUTB_NUM];
+ u8 sas_addr[SAS_ADDR_SIZE];
+ struct sas_ha_struct *sas;/* SCSI/SAS glue */
+ struct Scsi_Host *shost;
+ u32 chip_id;
+ const struct pm8001_chip_info *chip;
+ struct completion *nvmd_completion;
+ int tags_num;
+ unsigned long *tags;
+ struct pm8001_phy phy[PM8001_MAX_PHYS];
+ struct pm8001_port port[PM8001_MAX_PHYS];
+ u32 id;
+ u32 irq;
+ struct pm8001_device *devices;
+ struct pm8001_ccb_info *ccb_info;
+#ifdef PM8001_USE_MSIX
+ struct msix_entry msix_entries[16];/*for msi-x interrupt*/
+ int number_of_intr;/*will be used in remove()*/
+#endif
+#ifdef PM8001_USE_TASKLET
+ struct tasklet_struct tasklet;
+#endif
+ struct list_head wq_list;
+ u32 logging_level;
+ u32 fw_status;
+ const struct firmware *fw_image;
+};
+
+struct pm8001_wq {
+ struct delayed_work work_q;
+ struct pm8001_hba_info *pm8001_ha;
+ void *data;
+ int handler;
+ struct list_head entry;
+};
+
+struct pm8001_fw_image_header {
+ u8 vender_id[8];
+ u8 product_id;
+ u8 hardware_rev;
+ u8 dest_partition;
+ u8 reserved;
+ u8 fw_rev[4];
+ __be32 image_length;
+ __be32 image_crc;
+ __be32 startup_entry;
+} __attribute__((packed, aligned(4)));
+
+/* define task management IU */
+struct pm8001_tmf_task {
+ u8 tmf;
+ u32 tag_of_task_to_be_managed;
+};
+/**
+ * FW Flash Update status values
+ */
+#define FLASH_UPDATE_COMPLETE_PENDING_REBOOT 0x00
+#define FLASH_UPDATE_IN_PROGRESS 0x01
+#define FLASH_UPDATE_HDR_ERR 0x02
+#define FLASH_UPDATE_OFFSET_ERR 0x03
+#define FLASH_UPDATE_CRC_ERR 0x04
+#define FLASH_UPDATE_LENGTH_ERR 0x05
+#define FLASH_UPDATE_HW_ERR 0x06
+#define FLASH_UPDATE_DNLD_NOT_SUPPORTED 0x10
+#define FLASH_UPDATE_DISABLED 0x11
+
+/**
+ * brief param structure for firmware flash update.
+ */
+struct fw_flash_updata_info {
+ u32 cur_image_offset;
+ u32 cur_image_len;
+ u32 total_image_len;
+ struct pm8001_prd sgl;
+};
+
+struct fw_control_info {
+ u32 retcode;/*ret code (status)*/
+ u32 phase;/*ret code phase*/
+ u32 phaseCmplt;/*percent complete for the current
+ update phase */
+ u32 version;/*Hex encoded firmware version number*/
+ u32 offset;/*Used for downloading firmware */
+ u32 len; /*len of buffer*/
+ u32 size;/* Used in OS VPD and Trace get size
+ operations.*/
+ u32 reserved;/* padding required for 64 bit
+ alignment */
+ u8 buffer[1];/* Start of buffer */
+};
+struct fw_control_ex {
+ struct fw_control_info *fw_control;
+ void *buffer;/* keep buffer pointer to be
+ freed when the responce comes*/
+ void *virtAddr;/* keep virtual address of the data */
+ void *usrAddr;/* keep virtual address of the
+ user data */
+ dma_addr_t phys_addr;
+ u32 len; /* len of buffer */
+ void *payload; /* pointer to IOCTL Payload */
+ u8 inProgress;/*if 1 - the IOCTL request is in
+ progress */
+ void *param1;
+ void *param2;
+ void *param3;
+};
+
+/******************** function prototype *********************/
+int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out);
+void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha);
+u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag);
+void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx);
+void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx);
+int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata);
+int pm8001_slave_alloc(struct scsi_device *scsi_dev);
+int pm8001_slave_configure(struct scsi_device *sdev);
+void pm8001_scan_start(struct Scsi_Host *shost);
+int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time);
+int pm8001_queue_command(struct sas_task *task, const int num,
+ gfp_t gfp_flags);
+int pm8001_abort_task(struct sas_task *task);
+int pm8001_abort_task_set(struct domain_device *dev, u8 *lun);
+int pm8001_clear_aca(struct domain_device *dev, u8 *lun);
+int pm8001_clear_task_set(struct domain_device *dev, u8 *lun);
+int pm8001_dev_found(struct domain_device *dev);
+void pm8001_dev_gone(struct domain_device *dev);
+int pm8001_lu_reset(struct domain_device *dev, u8 *lun);
+int pm8001_I_T_nexus_reset(struct domain_device *dev);
+int pm8001_query_task(struct sas_task *task);
+int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
+ dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo,
+ u32 mem_size, u32 align);
+
+
+/* ctl shared API */
+extern struct device_attribute *pm8001_host_attrs[];
+
+#endif
+
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
index 0a97bc9074b..34c6b896a91 100644
--- a/drivers/scsi/pmcraid.c
+++ b/drivers/scsi/pmcraid.c
@@ -278,12 +278,17 @@ static void pmcraid_slave_destroy(struct scsi_device *scsi_dev)
* pmcraid_change_queue_depth - Change the device's queue depth
* @scsi_dev: scsi device struct
* @depth: depth to set
+ * @reason: calling context
*
* Return value
* actual depth set
*/
-static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
+static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth,
+ int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
if (depth > PMCRAID_MAX_CMD_PER_LUN)
depth = PMCRAID_MAX_CMD_PER_LUN;
@@ -3342,7 +3347,7 @@ static int pmcraid_chr_fasync(int fd, struct file *filep, int mode)
* @direction : data transfer direction
*
* Return value
- * 0 on sucess, non-zero error code on failure
+ * 0 on success, non-zero error code on failure
*/
static int pmcraid_build_passthrough_ioadls(
struct pmcraid_cmd *cmd,
@@ -3401,7 +3406,7 @@ static int pmcraid_build_passthrough_ioadls(
* @direction: data transfer direction
*
* Return value
- * 0 on sucess, non-zero error code on failure
+ * 0 on success, non-zero error code on failure
*/
static void pmcraid_release_passthrough_ioadls(
struct pmcraid_cmd *cmd,
@@ -3429,7 +3434,7 @@ static void pmcraid_release_passthrough_ioadls(
* @arg: pointer to pmcraid_passthrough_buffer user buffer
*
* Return value
- * 0 on sucess, non-zero error code on failure
+ * 0 on success, non-zero error code on failure
*/
static long pmcraid_ioctl_passthrough(
struct pmcraid_instance *pinstance,
diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h
index 3441b3f9082..2752b56cad5 100644
--- a/drivers/scsi/pmcraid.h
+++ b/drivers/scsi/pmcraid.h
@@ -771,11 +771,11 @@ static struct pmcraid_ioasc_error pmcraid_ioasc_error_table[] = {
{0x01180600, IOASC_LOG_LEVEL_MUST,
"Recovered Error, soft media error, sector reassignment suggested"},
{0x015D0000, IOASC_LOG_LEVEL_MUST,
- "Recovered Error, failure prediction thresold exceeded"},
+ "Recovered Error, failure prediction threshold exceeded"},
{0x015D9200, IOASC_LOG_LEVEL_MUST,
- "Recovered Error, soft Cache Card Battery error thresold"},
+ "Recovered Error, soft Cache Card Battery error threshold"},
{0x015D9200, IOASC_LOG_LEVEL_MUST,
- "Recovered Error, soft Cache Card Battery error thresold"},
+ "Recovered Error, soft Cache Card Battery error threshold"},
{0x02048000, IOASC_LOG_LEVEL_MUST,
"Not Ready, IOA Reset Required"},
{0x02408500, IOASC_LOG_LEVEL_MUST,
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index fbcb82a2f7f..21e2bc4d740 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1654,7 +1654,8 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
}
- if (scsi_add_host(vha->host, &fc_vport->dev)) {
+ if (scsi_add_host_with_dma(vha->host, &fc_vport->dev,
+ &ha->pdev->dev)) {
DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n",
vha->host_no, vha->vp_idx));
goto vport_create_failed_2;
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index cca8e4ab037..cb2eca4c26d 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -377,6 +377,24 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
return ptr + sizeof(struct qla2xxx_mq_chain);
}
+static void
+qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (rval != QLA_SUCCESS) {
+ qla_printk(KERN_WARNING, ha,
+ "Failed to dump firmware (%x)!!!\n", rval);
+ ha->fw_dumped = 0;
+ } else {
+ qla_printk(KERN_INFO, ha,
+ "Firmware dump saved to temp buffer (%ld/%p).\n",
+ vha->host_no, ha->fw_dump);
+ ha->fw_dumped = 1;
+ qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
+ }
+}
+
/**
* qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
* @ha: HA context
@@ -530,17 +548,7 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
if (rval == QLA_SUCCESS)
qla2xxx_copy_queues(ha, nxt);
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla2300_fw_dump_failed:
if (!hardware_locked)
@@ -737,17 +745,7 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
if (rval == QLA_SUCCESS)
qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]);
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla2100_fw_dump_failed:
if (!hardware_locked)
@@ -984,17 +982,7 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
qla24xx_copy_eft(ha, nxt);
qla24xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla24xx_fw_dump_failed:
if (!hardware_locked)
@@ -1305,17 +1293,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
}
qla25xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla25xx_fw_dump_failed:
if (!hardware_locked)
@@ -1628,17 +1606,7 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
}
qla81xx_fw_dump_failed_0:
- if (rval != QLA_SUCCESS) {
- qla_printk(KERN_WARNING, ha,
- "Failed to dump firmware (%x)!!!\n", rval);
- ha->fw_dumped = 0;
-
- } else {
- qla_printk(KERN_INFO, ha,
- "Firmware dump saved to temp buffer (%ld/%p).\n",
- base_vha->host_no, ha->fw_dump);
- ha->fw_dumped = 1;
- }
+ qla2xxx_dump_post_process(base_vha, rval);
qla81xx_fw_dump_failed:
if (!hardware_locked)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 21506186179..6b9bf23c773 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2123,6 +2123,7 @@ enum qla_work_type {
QLA_EVT_ASYNC_LOGIN_DONE,
QLA_EVT_ASYNC_LOGOUT,
QLA_EVT_ASYNC_LOGOUT_DONE,
+ QLA_EVT_UEVENT,
};
@@ -2146,6 +2147,10 @@ struct qla_work_evt {
#define QLA_LOGIO_LOGIN_RETRIED BIT_0
u16 data[2];
} logio;
+ struct {
+ u32 code;
+#define QLA_UEVENT_CODE_FW_DUMP 0
+ } uevent;
} u;
};
@@ -2435,11 +2440,11 @@ struct qla_hw_data {
dma_addr_t edc_data_dma;
uint16_t edc_data_len;
-#define XGMAC_DATA_SIZE PAGE_SIZE
+#define XGMAC_DATA_SIZE 4096
void *xgmac_data;
dma_addr_t xgmac_data_dma;
-#define DCBX_TLV_DATA_SIZE PAGE_SIZE
+#define DCBX_TLV_DATA_SIZE 4096
void *dcbx_tlv;
dma_addr_t dcbx_tlv_dma;
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index f3d1d1afa95..e2185135850 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -92,6 +92,7 @@ extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *,
uint16_t *);
extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *,
fc_port_t *, uint16_t *);
+extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *);
@@ -246,7 +247,7 @@ qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
extern int
qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *,
- uint16_t *, uint16_t *, uint16_t *);
+ uint16_t *, uint16_t *, uint16_t *, uint16_t *);
extern int
qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 9e3eaac2559..b74924b279e 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -277,7 +277,6 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
vha->marker_needed = 0;
ha->isp_abort_cnt = 0;
ha->beacon_blink_led = 0;
- set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
set_bit(0, ha->req_qid_map);
set_bit(0, ha->rsp_qid_map);
@@ -1203,7 +1202,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
}
qla2x00_get_resource_cnts(vha, NULL,
&ha->fw_xcb_count, NULL, NULL,
- &ha->max_npiv_vports);
+ &ha->max_npiv_vports, NULL);
if (!fw_major_version && ql2xallocfwdump)
qla2x00_alloc_fw_dump(vha);
@@ -3573,6 +3572,15 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
ha->isp_abort_cnt = 0;
clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ if (IS_QLA81XX(ha))
+ qla2x00_get_fw_version(vha,
+ &ha->fw_major_version,
+ &ha->fw_minor_version,
+ &ha->fw_subminor_version,
+ &ha->fw_attributes, &ha->fw_memory_size,
+ ha->mpi_version, &ha->mpi_capabilities,
+ ha->phy_version);
+
if (ha->fce) {
ha->flags.fce_enabled = 1;
memset(ha->fce, 0,
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index b20a7169aac..804987397b7 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -313,10 +313,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
char *link_speed;
uint16_t handle_cnt;
- uint16_t cnt;
+ uint16_t cnt, mbx;
uint32_t handles[5];
struct qla_hw_data *ha = vha->hw;
struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
uint32_t rscn_entry, host_pid;
uint8_t rscn_queue_index;
unsigned long flags;
@@ -395,9 +396,10 @@ skip_rio:
break;
case MBA_SYSTEM_ERR: /* System Error */
+ mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox7) : 0;
qla_printk(KERN_INFO, ha,
- "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
- mb[1], mb[2], mb[3]);
+ "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh "
+ "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx);
ha->isp_ops->fw_dump(vha, 1);
@@ -419,9 +421,10 @@ skip_rio:
break;
case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
- DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
- vha->host_no));
- qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
+ DEBUG2(printk("scsi(%ld): ISP Request Transfer Error (%x).\n",
+ vha->host_no, mb[1]));
+ qla_printk(KERN_WARNING, ha,
+ "ISP Request Transfer Error (%x).\n", mb[1]);
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
@@ -485,10 +488,13 @@ skip_rio:
break;
case MBA_LOOP_DOWN: /* Loop Down Event */
+ mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox4) : 0;
DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN "
- "(%x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3]));
- qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n",
- mb[1], mb[2], mb[3]);
+ "(%x %x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3],
+ mbx));
+ qla_printk(KERN_INFO, ha,
+ "LOOP DOWN detected (%x %x %x %x).\n", mb[1], mb[2], mb[3],
+ mbx);
if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -1347,16 +1353,22 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
sense_len = rsp_info_len = resid_len = fw_resid_len = 0;
if (IS_FWI2_CAPABLE(ha)) {
- sense_len = le32_to_cpu(sts24->sense_len);
- rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
- resid_len = le32_to_cpu(sts24->rsp_residual_count);
- fw_resid_len = le32_to_cpu(sts24->residual_len);
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le32_to_cpu(sts24->sense_len);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+ if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
+ resid_len = le32_to_cpu(sts24->rsp_residual_count);
+ if (comp_status == CS_DATA_UNDERRUN)
+ fw_resid_len = le32_to_cpu(sts24->residual_len);
rsp_info = sts24->data;
sense_data = sts24->data;
host_to_fcp_swap(sts24->data, sizeof(sts24->data));
} else {
- sense_len = le16_to_cpu(sts->req_sense_length);
- rsp_info_len = le16_to_cpu(sts->rsp_info_len);
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le16_to_cpu(sts->req_sense_length);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le16_to_cpu(sts->rsp_info_len);
resid_len = le32_to_cpu(sts->residual_length);
rsp_info = sts->rsp_info;
sense_data = sts->req_sense_data;
@@ -1443,38 +1455,62 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
case CS_DATA_UNDERRUN:
- resid = resid_len;
+ DEBUG2(printk(KERN_INFO
+ "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x. "
+ "resid=0x%x fw_resid=0x%x cdb=0x%x os_underflow=0x%x\n",
+ vha->host_no, cp->device->id, cp->device->lun, comp_status,
+ scsi_status, resid_len, fw_resid_len, cp->cmnd[0],
+ cp->underflow));
+
/* Use F/W calculated residual length. */
- if (IS_FWI2_CAPABLE(ha)) {
- if (!(scsi_status & SS_RESIDUAL_UNDER)) {
- lscsi_status = 0;
- } else if (resid != fw_resid_len) {
- scsi_status &= ~SS_RESIDUAL_UNDER;
- lscsi_status = 0;
+ resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len;
+ scsi_set_resid(cp, resid);
+ if (scsi_status & SS_RESIDUAL_UNDER) {
+ if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
+ DEBUG2(printk(
+ "scsi(%ld:%d:%d:%d) Dropped frame(s) "
+ "detected (%x of %x bytes)...residual "
+ "length mismatch...retrying command.\n",
+ vha->host_no, cp->device->channel,
+ cp->device->id, cp->device->lun, resid,
+ scsi_bufflen(cp)));
+
+ cp->result = DID_ERROR << 16 | lscsi_status;
+ break;
}
- resid = fw_resid_len;
- }
- if (scsi_status & SS_RESIDUAL_UNDER) {
- scsi_set_resid(cp, resid);
- } else {
- DEBUG2(printk(KERN_INFO
- "scsi(%ld:%d:%d) UNDERRUN status detected "
- "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
- "os_underflow=0x%x\n", vha->host_no,
- cp->device->id, cp->device->lun, comp_status,
- scsi_status, resid_len, resid, cp->cmnd[0],
- cp->underflow));
+ if (!lscsi_status &&
+ ((unsigned)(scsi_bufflen(cp) - resid) <
+ cp->underflow)) {
+ qla_printk(KERN_INFO, ha,
+ "scsi(%ld:%d:%d:%d): Mid-layer underflow "
+ "detected (%x of %x bytes)...returning "
+ "error status.\n", vha->host_no,
+ cp->device->channel, cp->device->id,
+ cp->device->lun, resid, scsi_bufflen(cp));
+
+ cp->result = DID_ERROR << 16;
+ break;
+ }
+ } else if (!lscsi_status) {
+ DEBUG2(printk(
+ "scsi(%ld:%d:%d:%d) Dropped frame(s) detected "
+ "(%x of %x bytes)...firmware reported underrun..."
+ "retrying command.\n", vha->host_no,
+ cp->device->channel, cp->device->id,
+ cp->device->lun, resid, scsi_bufflen(cp)));
+ cp->result = DID_ERROR << 16;
+ break;
}
+ cp->result = DID_OK << 16 | lscsi_status;
+
/*
* Check to see if SCSI Status is non zero. If so report SCSI
* Status.
*/
if (lscsi_status != 0) {
- cp->result = DID_OK << 16 | lscsi_status;
-
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
DEBUG2(printk(KERN_INFO
"scsi(%ld): QUEUE FULL status detected "
@@ -1501,42 +1537,6 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
qla2x00_handle_sense(sp, sense_data, sense_len, rsp);
- } else {
- /*
- * If RISC reports underrun and target does not report
- * it then we must have a lost frame, so tell upper
- * layer to retry it by reporting an error.
- */
- if (!(scsi_status & SS_RESIDUAL_UNDER)) {
- DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
- "frame(s) detected (%x of %x bytes)..."
- "retrying command.\n",
- vha->host_no, cp->device->channel,
- cp->device->id, cp->device->lun, resid,
- scsi_bufflen(cp)));
-
- scsi_set_resid(cp, resid);
- cp->result = DID_ERROR << 16;
- break;
- }
-
- /* Handle mid-layer underflow */
- if ((unsigned)(scsi_bufflen(cp) - resid) <
- cp->underflow) {
- qla_printk(KERN_INFO, ha,
- "scsi(%ld:%d:%d:%d): Mid-layer underflow "
- "detected (%x of %x bytes)...returning "
- "error status.\n", vha->host_no,
- cp->device->channel, cp->device->id,
- cp->device->lun, resid,
- scsi_bufflen(cp));
-
- cp->result = DID_ERROR << 16;
- break;
- }
-
- /* Everybody online, looking good... */
- cp->result = DID_OK << 16;
}
break;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index b6202fe118a..05d595d9a7e 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -2006,7 +2006,7 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
int
qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt,
- uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports)
+ uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports, uint16_t *max_fcfs)
{
int rval;
mbx_cmd_t mc;
@@ -2017,6 +2017,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (IS_QLA81XX(vha->hw))
+ mcp->in_mb |= MBX_12;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -2027,9 +2029,10 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
vha->host_no, mcp->mb[0]));
} else {
DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
- "mb7=%x mb10=%x mb11=%x.\n", __func__, vha->host_no,
- mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
- mcp->mb[10], mcp->mb[11]));
+ "mb7=%x mb10=%x mb11=%x mb12=%x.\n", __func__,
+ vha->host_no, mcp->mb[1], mcp->mb[2], mcp->mb[3],
+ mcp->mb[6], mcp->mb[7], mcp->mb[10], mcp->mb[11],
+ mcp->mb[12]));
if (cur_xchg_cnt)
*cur_xchg_cnt = mcp->mb[3];
@@ -2041,6 +2044,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
*orig_iocb_cnt = mcp->mb[10];
if (vha->hw->flags.npiv_supported && max_npiv_vports)
*max_npiv_vports = mcp->mb[11];
+ if (IS_QLA81XX(vha->hw) && max_fcfs)
+ *max_fcfs = mcp->mb[12];
}
return (rval);
@@ -2313,6 +2318,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
{
int rval, rval2;
struct tsk_mgmt_cmd *tsk;
+ struct sts_entry_24xx *sts;
dma_addr_t tsk_dma;
scsi_qla_host_t *vha;
struct qla_hw_data *ha;
@@ -2352,20 +2358,37 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
sizeof(tsk->p.tsk.lun));
}
+ sts = &tsk->p.sts;
rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
if (rval != QLA_SUCCESS) {
DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB "
"(%x).\n", __func__, vha->host_no, name, rval));
- } else if (tsk->p.sts.entry_status != 0) {
+ } else if (sts->entry_status != 0) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- error status (%x).\n", __func__, vha->host_no,
- tsk->p.sts.entry_status));
+ sts->entry_status));
rval = QLA_FUNCTION_FAILED;
- } else if (tsk->p.sts.comp_status !=
+ } else if (sts->comp_status !=
__constant_cpu_to_le16(CS_COMPLETE)) {
DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
"-- completion status (%x).\n", __func__,
- vha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
+ vha->host_no, le16_to_cpu(sts->comp_status)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (!(le16_to_cpu(sts->scsi_status) &
+ SS_RESPONSE_INFO_LEN_VALID)) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- no response info (%x).\n", __func__, vha->host_no,
+ le16_to_cpu(sts->scsi_status)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (le32_to_cpu(sts->rsp_data_len) < 4) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- not enough response info (%d).\n", __func__,
+ vha->host_no, le32_to_cpu(sts->rsp_data_len)));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (sts->data[3]) {
+ DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+ "-- response (%x).\n", __func__,
+ vha->host_no, sts->data[3]));
rval = QLA_FUNCTION_FAILED;
}
@@ -2759,8 +2782,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
vp_idx, MSB(stat),
rptid_entry->port_id[2], rptid_entry->port_id[1],
rptid_entry->port_id[0]));
- if (vp_idx == 0)
- return;
+
+ vp = vha;
+ if (vp_idx == 0 && (MSB(stat) != 1))
+ goto reg_needed;
if (MSB(stat) == 1) {
DEBUG2(printk("scsi(%ld): Could not acquire ID for "
@@ -2783,8 +2808,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
* response queue. Handle it in dpc context.
*/
set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
- set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
+reg_needed:
+ set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+ set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
}
}
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index e07b3617f01..a47d34308a3 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -382,8 +382,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
vha->dpc_flags = 0L;
- set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
- set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
/*
* To fix the issue of processing a parent's RSCN for the vport before
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index b79fca7d461..41669357b18 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
+#include <linux/kobject.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsicam.h>
@@ -137,7 +138,7 @@ static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
-static int qla2x00_change_queue_depth(struct scsi_device *, int);
+static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
static int qla2x00_change_queue_type(struct scsi_device *, int);
struct scsi_host_template qla2xxx_driver_template = {
@@ -727,23 +728,6 @@ qla2x00_abort_fcport_cmds(fc_port_t *fcport)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
-static void
-qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
-{
- struct Scsi_Host *shost = cmnd->device->host;
- struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
- unsigned long flags;
-
- spin_lock_irqsave(shost->host_lock, flags);
- while (rport->port_state == FC_PORTSTATE_BLOCKED) {
- spin_unlock_irqrestore(shost->host_lock, flags);
- msleep(1000);
- spin_lock_irqsave(shost->host_lock, flags);
- }
- spin_unlock_irqrestore(shost->host_lock, flags);
- return;
-}
-
/**************************************************************************
* qla2xxx_eh_abort
*
@@ -773,7 +757,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
struct req_que *req = vha->req;
srb_t *spt;
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
if (!CMD_SP(cmd))
return SUCCESS;
@@ -904,7 +888,7 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
int err;
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
if (!fcport)
return FAILED;
@@ -984,7 +968,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
unsigned long serial;
srb_t *sp = (srb_t *) CMD_SP(cmd);
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
id = cmd->device->id;
lun = cmd->device->lun;
@@ -1047,7 +1031,7 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
srb_t *sp = (srb_t *) CMD_SP(cmd);
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
- qla2x00_block_error_handler(cmd);
+ fc_block_scsi_eh(cmd);
id = cmd->device->id;
lun = cmd->device->lun;
@@ -1234,8 +1218,11 @@ qla2xxx_slave_destroy(struct scsi_device *sdev)
}
static int
-qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth)
+qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
{
+ if (reason != SCSI_QDEPTH_DEFAULT)
+ return -EOPNOTSUPP;
+
scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
return sdev->queue_depth;
}
@@ -2653,6 +2640,37 @@ qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
+int
+qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code)
+{
+ struct qla_work_evt *e;
+
+ e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT);
+ if (!e)
+ return QLA_FUNCTION_FAILED;
+
+ e->u.uevent.code = code;
+ return qla2x00_post_work(vha, e);
+}
+
+static void
+qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code)
+{
+ char event_string[40];
+ char *envp[] = { event_string, NULL };
+
+ switch (code) {
+ case QLA_UEVENT_CODE_FW_DUMP:
+ snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+ vha->host_no);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
void
qla2x00_do_work(struct scsi_qla_host *vha)
{
@@ -2690,6 +2708,9 @@ qla2x00_do_work(struct scsi_qla_host *vha)
qla2x00_async_logout_done(vha, e->u.logio.fcport,
e->u.logio.data);
break;
+ case QLA_EVT_UEVENT:
+ qla2x00_uevent_emit(vha, e->u.uevent.code);
+ break;
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index ac107a2c34a..807e0dbc67f 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.03.01-k6"
+#define QLA2XXX_VERSION "8.03.01-k7"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 3
diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h
index 9c053bbaa87..e3c74d1ee2d 100644
--- a/drivers/scsi/qlogicpti.h
+++ b/drivers/scsi/qlogicpti.h
@@ -43,7 +43,7 @@
* determined for each queue request anew.
*/
#define QLOGICPTI_REQ_QUEUE_LEN 255 /* must be power of two - 1 */
-#define QLOGICPTI_MAX_SG(ql) (4 + ((ql) > 0) ? 7*((ql) - 1) : 0)
+#define QLOGICPTI_MAX_SG(ql) (4 + (((ql) > 0) ? 7*((ql) - 1) : 0))
/* mailbox command complete status codes */
#define MBOX_COMMAND_COMPLETE 0x4000
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index dd098cad337..a60da555557 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -940,10 +940,16 @@ EXPORT_SYMBOL(scsi_adjust_queue_depth);
*/
int scsi_track_queue_full(struct scsi_device *sdev, int depth)
{
- if ((jiffies >> 4) == sdev->last_queue_full_time)
+
+ /*
+ * Don't let QUEUE_FULLs on the same
+ * jiffies count, they could all be from
+ * same event.
+ */
+ if ((jiffies >> 4) == (sdev->last_queue_full_time >> 4))
return 0;
- sdev->last_queue_full_time = (jiffies >> 4);
+ sdev->last_queue_full_time = jiffies;
if (sdev->last_queue_full_depth != depth) {
sdev->last_queue_full_count = 1;
sdev->last_queue_full_depth = depth;
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index c4103bef41b..0b575c87100 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -44,6 +44,8 @@
#include <net/checksum.h>
+#include <asm/unaligned.h>
+
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -105,6 +107,10 @@ static const char * scsi_debug_version_date = "20070104";
#define DEF_ATO 1
#define DEF_PHYSBLK_EXP 0
#define DEF_LOWEST_ALIGNED 0
+#define DEF_UNMAP_MAX_BLOCKS 0
+#define DEF_UNMAP_MAX_DESC 0
+#define DEF_UNMAP_GRANULARITY 0
+#define DEF_UNMAP_ALIGNMENT 0
/* bit mask values for scsi_debug_opts */
#define SCSI_DEBUG_OPT_NOISE 1
@@ -162,6 +168,10 @@ static int scsi_debug_guard = DEF_GUARD;
static int scsi_debug_ato = DEF_ATO;
static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP;
static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED;
+static int scsi_debug_unmap_max_desc = DEF_UNMAP_MAX_DESC;
+static int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
+static int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY;
+static int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
static int scsi_debug_cmnd_count = 0;
@@ -223,7 +233,9 @@ static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
static unsigned char * fake_storep; /* ramdisk storage */
static unsigned char *dif_storep; /* protection info */
+static void *map_storep; /* provisioning map */
+static unsigned long map_size;
static int num_aborts = 0;
static int num_dev_resets = 0;
static int num_bus_resets = 0;
@@ -317,6 +329,7 @@ static void get_data_transfer_info(unsigned char *cmd,
(u32)cmd[28] << 24;
break;
+ case WRITE_SAME_16:
case WRITE_16:
case READ_16:
*lba = (u64)cmd[9] | (u64)cmd[8] << 8 |
@@ -335,6 +348,7 @@ static void get_data_transfer_info(unsigned char *cmd,
*num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 |
(u32)cmd[6] << 24;
break;
+ case WRITE_SAME:
case WRITE_10:
case READ_10:
case XDWRITEREAD_10:
@@ -671,10 +685,12 @@ static int inquiry_evpd_89(unsigned char * arr)
}
+/* Block limits VPD page (SBC-3) */
static unsigned char vpdb0_data[] = {
- /* from 4th byte */ 0,0,0,4,
- 0,0,0x4,0,
- 0,0,0,64,
+ /* from 4th byte */ 0,0,0,4, 0,0,0x4,0, 0,0,0,64,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
static int inquiry_evpd_b0(unsigned char * arr)
@@ -691,14 +707,40 @@ static int inquiry_evpd_b0(unsigned char * arr)
arr[6] = (sdebug_store_sectors >> 8) & 0xff;
arr[7] = sdebug_store_sectors & 0xff;
}
+
+ if (scsi_debug_unmap_max_desc) {
+ unsigned int blocks;
+
+ if (scsi_debug_unmap_max_blocks)
+ blocks = scsi_debug_unmap_max_blocks;
+ else
+ blocks = 0xffffffff;
+
+ put_unaligned_be32(blocks, &arr[16]);
+ put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]);
+ }
+
+ if (scsi_debug_unmap_alignment) {
+ put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]);
+ arr[28] |= 0x80; /* UGAVALID */
+ }
+
+ if (scsi_debug_unmap_granularity) {
+ put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]);
+ return 0x3c; /* Mandatory page length for thin provisioning */
+ }
+
return sizeof(vpdb0_data);
}
+/* Block device characteristics VPD page (SBC-3) */
static int inquiry_evpd_b1(unsigned char *arr)
{
memset(arr, 0, 0x3c);
arr[0] = 0;
- arr[1] = 1;
+ arr[1] = 1; /* non rotating medium (e.g. solid state) */
+ arr[2] = 0;
+ arr[3] = 5; /* less than 1.8" */
return 0x3c;
}
@@ -974,6 +1016,10 @@ static int resp_readcap16(struct scsi_cmnd * scp,
arr[11] = scsi_debug_sector_size & 0xff;
arr[13] = scsi_debug_physblk_exp & 0xf;
arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f;
+
+ if (scsi_debug_unmap_granularity)
+ arr[14] |= 0x80; /* TPE */
+
arr[15] = scsi_debug_lowest_aligned & 0xff;
if (scsi_debug_dif) {
@@ -1887,6 +1933,70 @@ out:
return ret;
}
+static unsigned int map_state(sector_t lba, unsigned int *num)
+{
+ unsigned int granularity, alignment, mapped;
+ sector_t block, next, end;
+
+ granularity = scsi_debug_unmap_granularity;
+ alignment = granularity - scsi_debug_unmap_alignment;
+ block = lba + alignment;
+ do_div(block, granularity);
+
+ mapped = test_bit(block, map_storep);
+
+ if (mapped)
+ next = find_next_zero_bit(map_storep, map_size, block);
+ else
+ next = find_next_bit(map_storep, map_size, block);
+
+ end = next * granularity - scsi_debug_unmap_alignment;
+ *num = end - lba;
+
+ return mapped;
+}
+
+static void map_region(sector_t lba, unsigned int len)
+{
+ unsigned int granularity, alignment;
+ sector_t end = lba + len;
+
+ granularity = scsi_debug_unmap_granularity;
+ alignment = granularity - scsi_debug_unmap_alignment;
+
+ while (lba < end) {
+ sector_t block, rem;
+
+ block = lba + alignment;
+ rem = do_div(block, granularity);
+
+ set_bit(block, map_storep);
+
+ lba += granularity - rem;
+ }
+}
+
+static void unmap_region(sector_t lba, unsigned int len)
+{
+ unsigned int granularity, alignment;
+ sector_t end = lba + len;
+
+ granularity = scsi_debug_unmap_granularity;
+ alignment = granularity - scsi_debug_unmap_alignment;
+
+ while (lba < end) {
+ sector_t block, rem;
+
+ block = lba + alignment;
+ rem = do_div(block, granularity);
+
+ if (rem == 0 && lba + granularity <= end)
+ clear_bit(block, map_storep);
+
+ lba += granularity - rem;
+ }
+}
+
static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
unsigned int num, struct sdebug_dev_info *devip,
u32 ei_lba)
@@ -1910,6 +2020,8 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
write_lock_irqsave(&atomic_rw, iflags);
ret = do_device_access(SCpnt, devip, lba, num, 1);
+ if (scsi_debug_unmap_granularity)
+ map_region(lba, num);
write_unlock_irqrestore(&atomic_rw, iflags);
if (-1 == ret)
return (DID_ERROR << 16);
@@ -1917,9 +2029,143 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
" IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
+
+ return 0;
+}
+
+static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
+ unsigned int num, struct sdebug_dev_info *devip,
+ u32 ei_lba, unsigned int unmap)
+{
+ unsigned long iflags;
+ unsigned long long i;
+ int ret;
+
+ ret = check_device_access_params(devip, lba, num);
+ if (ret)
+ return ret;
+
+ write_lock_irqsave(&atomic_rw, iflags);
+
+ if (unmap && scsi_debug_unmap_granularity) {
+ unmap_region(lba, num);
+ goto out;
+ }
+
+ /* Else fetch one logical block */
+ ret = fetch_to_dev_buffer(scmd,
+ fake_storep + (lba * scsi_debug_sector_size),
+ scsi_debug_sector_size);
+
+ if (-1 == ret) {
+ write_unlock_irqrestore(&atomic_rw, iflags);
+ return (DID_ERROR << 16);
+ } else if ((ret < (num * scsi_debug_sector_size)) &&
+ (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
+ printk(KERN_INFO "scsi_debug: write same: cdb indicated=%u, "
+ " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
+
+ /* Copy first sector to remaining blocks */
+ for (i = 1 ; i < num ; i++)
+ memcpy(fake_storep + ((lba + i) * scsi_debug_sector_size),
+ fake_storep + (lba * scsi_debug_sector_size),
+ scsi_debug_sector_size);
+
+ if (scsi_debug_unmap_granularity)
+ map_region(lba, num);
+out:
+ write_unlock_irqrestore(&atomic_rw, iflags);
+
return 0;
}
+struct unmap_block_desc {
+ __be64 lba;
+ __be32 blocks;
+ __be32 __reserved;
+};
+
+static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip)
+{
+ unsigned char *buf;
+ struct unmap_block_desc *desc;
+ unsigned int i, payload_len, descriptors;
+ int ret;
+
+ ret = check_readiness(scmd, 1, devip);
+ if (ret)
+ return ret;
+
+ payload_len = get_unaligned_be16(&scmd->cmnd[7]);
+ BUG_ON(scsi_bufflen(scmd) != payload_len);
+
+ descriptors = (payload_len - 8) / 16;
+
+ buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC);
+ if (!buf)
+ return check_condition_result;
+
+ scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
+
+ BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2);
+ BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16);
+
+ desc = (void *)&buf[8];
+
+ for (i = 0 ; i < descriptors ; i++) {
+ unsigned long long lba = get_unaligned_be64(&desc[i].lba);
+ unsigned int num = get_unaligned_be32(&desc[i].blocks);
+
+ ret = check_device_access_params(devip, lba, num);
+ if (ret)
+ goto out;
+
+ unmap_region(lba, num);
+ }
+
+ ret = 0;
+
+out:
+ kfree(buf);
+
+ return ret;
+}
+
+#define SDEBUG_GET_LBA_STATUS_LEN 32
+
+static int resp_get_lba_status(struct scsi_cmnd * scmd,
+ struct sdebug_dev_info * devip)
+{
+ unsigned long long lba;
+ unsigned int alloc_len, mapped, num;
+ unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN];
+ int ret;
+
+ ret = check_readiness(scmd, 1, devip);
+ if (ret)
+ return ret;
+
+ lba = get_unaligned_be64(&scmd->cmnd[2]);
+ alloc_len = get_unaligned_be32(&scmd->cmnd[10]);
+
+ if (alloc_len < 24)
+ return 0;
+
+ ret = check_device_access_params(devip, lba, 1);
+ if (ret)
+ return ret;
+
+ mapped = map_state(lba, &num);
+
+ memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN);
+ put_unaligned_be32(16, &arr[0]); /* Parameter Data Length */
+ put_unaligned_be64(lba, &arr[8]); /* LBA */
+ put_unaligned_be32(num, &arr[16]); /* Number of blocks */
+ arr[20] = !mapped; /* mapped = 0, unmapped = 1 */
+
+ return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN);
+}
+
#define SDEBUG_RLUN_ARR_SZ 256
static int resp_report_luns(struct scsi_cmnd * scp,
@@ -2430,6 +2676,10 @@ module_param_named(guard, scsi_debug_guard, int, S_IRUGO);
module_param_named(ato, scsi_debug_ato, int, S_IRUGO);
module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO);
module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO);
+module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO);
+module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO);
+module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO);
+module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO);
MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -2458,6 +2708,10 @@ MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)");
MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)");
MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)");
MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)");
+MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0)");
+MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=0)");
+MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=0)");
+MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)");
static char sdebug_info[256];
@@ -2816,6 +3070,23 @@ static ssize_t sdebug_ato_show(struct device_driver *ddp, char *buf)
}
DRIVER_ATTR(ato, S_IRUGO, sdebug_ato_show, NULL);
+static ssize_t sdebug_map_show(struct device_driver *ddp, char *buf)
+{
+ ssize_t count;
+
+ if (scsi_debug_unmap_granularity == 0)
+ return scnprintf(buf, PAGE_SIZE, "0-%u\n",
+ sdebug_store_sectors);
+
+ count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size);
+
+ buf[count++] = '\n';
+ buf[count++] = 0;
+
+ return count;
+}
+DRIVER_ATTR(map, S_IRUGO, sdebug_map_show, NULL);
+
/* Note: The following function creates attribute files in the
/sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
@@ -2847,11 +3118,13 @@ static int do_create_driverfs_files(void)
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dif);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_guard);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ato);
+ ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_map);
return ret;
}
static void do_remove_driverfs_files(void)
{
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_map);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ato);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_guard);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dif);
@@ -2989,6 +3262,36 @@ static int __init scsi_debug_init(void)
memset(dif_storep, 0xff, dif_size);
}
+ if (scsi_debug_unmap_granularity) {
+ unsigned int map_bytes;
+
+ if (scsi_debug_unmap_granularity < scsi_debug_unmap_alignment) {
+ printk(KERN_ERR
+ "%s: ERR: unmap_granularity < unmap_alignment\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ map_size = (sdebug_store_sectors / scsi_debug_unmap_granularity);
+ map_bytes = map_size >> 3;
+ map_storep = vmalloc(map_bytes);
+
+ printk(KERN_INFO "scsi_debug_init: %lu provisioning blocks\n",
+ map_size);
+
+ if (map_storep == NULL) {
+ printk(KERN_ERR "scsi_debug_init: out of mem. (MAP)\n");
+ ret = -ENOMEM;
+ goto free_vm;
+ }
+
+ memset(map_storep, 0x0, map_bytes);
+
+ /* Map first 1KB for partition table */
+ if (scsi_debug_num_parts)
+ map_region(0, 2);
+ }
+
ret = device_register(&pseudo_primary);
if (ret < 0) {
printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
@@ -3041,6 +3344,8 @@ bus_unreg:
dev_unreg:
device_unregister(&pseudo_primary);
free_vm:
+ if (map_storep)
+ vfree(map_storep);
if (dif_storep)
vfree(dif_storep);
vfree(fake_storep);
@@ -3167,6 +3472,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
int inj_dif = 0;
int inj_dix = 0;
int delay_override = 0;
+ int unmap = 0;
scsi_set_resid(SCpnt, 0);
if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
@@ -3272,13 +3578,21 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
errsts = resp_readcap(SCpnt, devip);
break;
case SERVICE_ACTION_IN:
- if (SAI_READ_CAPACITY_16 != cmd[1]) {
+ if (cmd[1] == SAI_READ_CAPACITY_16)
+ errsts = resp_readcap16(SCpnt, devip);
+ else if (cmd[1] == SAI_GET_LBA_STATUS) {
+
+ if (scsi_debug_unmap_max_desc == 0) {
+ mk_sense_buffer(devip, ILLEGAL_REQUEST,
+ INVALID_COMMAND_OPCODE, 0);
+ errsts = check_condition_result;
+ } else
+ errsts = resp_get_lba_status(SCpnt, devip);
+ } else {
mk_sense_buffer(devip, ILLEGAL_REQUEST,
INVALID_OPCODE, 0);
errsts = check_condition_result;
- break;
}
- errsts = resp_readcap16(SCpnt, devip);
break;
case MAINTENANCE_IN:
if (MI_REPORT_TARGET_PGS != cmd[1]) {
@@ -3378,6 +3692,29 @@ write:
errsts = illegal_condition_result;
}
break;
+ case WRITE_SAME_16:
+ if (cmd[1] & 0x8)
+ unmap = 1;
+ /* fall through */
+ case WRITE_SAME:
+ errsts = check_readiness(SCpnt, 0, devip);
+ if (errsts)
+ break;
+ get_data_transfer_info(cmd, &lba, &num, &ei_lba);
+ errsts = resp_write_same(SCpnt, lba, num, devip, ei_lba, unmap);
+ break;
+ case UNMAP:
+ errsts = check_readiness(SCpnt, 0, devip);
+ if (errsts)
+ break;
+
+ if (scsi_debug_unmap_max_desc == 0) {
+ mk_sense_buffer(devip, ILLEGAL_REQUEST,
+ INVALID_COMMAND_OPCODE, 0);
+ errsts = check_condition_result;
+ } else
+ errsts = resp_unmap(SCpnt, devip);
+ break;
case MODE_SENSE:
case MODE_SENSE_10:
errsts = resp_mode_sense(SCpnt, target, devip);
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 93c2622cb96..37af178b2d1 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -168,11 +168,10 @@ static struct {
{"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36},
{"Generic", "USB Storage-SMC", "0180", BLIST_FORCELUN | BLIST_INQUIRY_36},
{"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN | BLIST_INQUIRY_36},
- {"HITACHI", "DF400", "*", BLIST_SPARSELUN},
- {"HITACHI", "DF500", "*", BLIST_SPARSELUN},
- {"HITACHI", "DF600", "*", BLIST_SPARSELUN},
- {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN},
- {"HITACHI", "OPEN-E", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN},
+ {"HITACHI", "DF400", "*", BLIST_REPORTLUN2},
+ {"HITACHI", "DF500", "*", BLIST_REPORTLUN2},
+ {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_REPORTLUN2},
+ {"HITACHI", "OPEN-", "*", BLIST_REPORTLUN2},
{"HITACHI", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HITACHI", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
{"HITACHI", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
@@ -454,7 +453,7 @@ int scsi_get_device_flags(struct scsi_device *sdev,
/**
- * get_device_flags_keyed - get device specific flags from the dynamic device list.
+ * scsi_get_device_flags_keyed - get device specific flags from the dynamic device list
* @sdev: &scsi_device to get flags for
* @vendor: vendor name
* @model: model name
@@ -685,7 +684,7 @@ MODULE_PARM_DESC(default_dev_flags,
"scsi default device flag integer value");
/**
- * scsi_dev_info_list_delete - called from scsi.c:exit_scsi to remove the scsi_dev_info_list.
+ * scsi_exit_devinfo - remove /proc/scsi/device_info & the scsi_dev_info_list
**/
void scsi_exit_devinfo(void)
{
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 1b0060b791e..08ed506e605 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -331,6 +331,64 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
}
}
+static void scsi_handle_queue_ramp_up(struct scsi_device *sdev)
+{
+ struct scsi_host_template *sht = sdev->host->hostt;
+ struct scsi_device *tmp_sdev;
+
+ if (!sht->change_queue_depth ||
+ sdev->queue_depth >= sdev->max_queue_depth)
+ return;
+
+ if (time_before(jiffies,
+ sdev->last_queue_ramp_up + sdev->queue_ramp_up_period))
+ return;
+
+ if (time_before(jiffies,
+ sdev->last_queue_full_time + sdev->queue_ramp_up_period))
+ return;
+
+ /*
+ * Walk all devices of a target and do
+ * ramp up on them.
+ */
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (tmp_sdev->channel != sdev->channel ||
+ tmp_sdev->id != sdev->id ||
+ tmp_sdev->queue_depth == sdev->max_queue_depth)
+ continue;
+ /*
+ * call back into LLD to increase queue_depth by one
+ * with ramp up reason code.
+ */
+ sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1,
+ SCSI_QDEPTH_RAMP_UP);
+ sdev->last_queue_ramp_up = jiffies;
+ }
+}
+
+static void scsi_handle_queue_full(struct scsi_device *sdev)
+{
+ struct scsi_host_template *sht = sdev->host->hostt;
+ struct scsi_device *tmp_sdev;
+
+ if (!sht->change_queue_depth)
+ return;
+
+ shost_for_each_device(tmp_sdev, sdev->host) {
+ if (tmp_sdev->channel != sdev->channel ||
+ tmp_sdev->id != sdev->id)
+ continue;
+ /*
+ * We do not know the number of commands that were at
+ * the device when we got the queue full so we start
+ * from the highest possible value and work our way down.
+ */
+ sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth - 1,
+ SCSI_QDEPTH_QFULL);
+ }
+}
+
/**
* scsi_eh_completed_normally - Disposition a eh cmd on return from LLD.
* @scmd: SCSI cmd to examine.
@@ -371,6 +429,7 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
*/
switch (status_byte(scmd->result)) {
case GOOD:
+ scsi_handle_queue_ramp_up(scmd->device);
case COMMAND_TERMINATED:
return SUCCESS;
case CHECK_CONDITION:
@@ -387,8 +446,10 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
* let issuer deal with this, it could be just fine
*/
return SUCCESS;
- case BUSY:
case QUEUE_FULL:
+ scsi_handle_queue_full(scmd->device);
+ /* fall through */
+ case BUSY:
default:
return FAILED;
}
@@ -1387,6 +1448,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
*/
switch (status_byte(scmd->result)) {
case QUEUE_FULL:
+ scsi_handle_queue_full(scmd->device);
/*
* the case of trying to send too many commands to a
* tagged queueing device.
@@ -1400,6 +1462,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
*/
return ADD_TO_MLQUEUE;
case GOOD:
+ scsi_handle_queue_ramp_up(scmd->device);
case COMMAND_TERMINATED:
return SUCCESS;
case TASK_ABORTED:
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index b98f763931c..d9564fb04f6 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -308,6 +308,9 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
case SG_SCSI_RESET_DEVICE:
val = SCSI_TRY_RESET_DEVICE;
break;
+ case SG_SCSI_RESET_TARGET:
+ val = SCSI_TRY_RESET_TARGET;
+ break;
case SG_SCSI_RESET_BUS:
val = SCSI_TRY_RESET_BUS;
break;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 5987da85710..e495d381394 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -898,7 +898,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
scsi_print_sense("", cmd);
scsi_print_command(cmd);
}
- if (blk_end_request_err(req, -EIO))
+ if (blk_end_request_err(req, error))
scsi_requeue_command(q, cmd);
else
scsi_next_command(cmd);
@@ -1359,9 +1359,9 @@ static int scsi_lld_busy(struct request_queue *q)
static void scsi_kill_request(struct request *req, struct request_queue *q)
{
struct scsi_cmnd *cmd = req->special;
- struct scsi_device *sdev = cmd->device;
- struct scsi_target *starget = scsi_target(sdev);
- struct Scsi_Host *shost = sdev->host;
+ struct scsi_device *sdev;
+ struct scsi_target *starget;
+ struct Scsi_Host *shost;
blk_start_request(req);
@@ -1371,6 +1371,9 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
BUG();
}
+ sdev = cmd->device;
+ starget = scsi_target(sdev);
+ shost = sdev->host;
scsi_init_cmd_errh(cmd);
cmd->result = DID_NO_CONNECT << 16;
atomic_inc(&cmd->device->iorequest_cnt);
diff --git a/drivers/scsi/scsi_lib_dma.c b/drivers/scsi/scsi_lib_dma.c
index ac6855cd265..dcd128583b8 100644
--- a/drivers/scsi/scsi_lib_dma.c
+++ b/drivers/scsi/scsi_lib_dma.c
@@ -23,7 +23,7 @@ int scsi_dma_map(struct scsi_cmnd *cmd)
int nseg = 0;
if (scsi_sg_count(cmd)) {
- struct device *dev = cmd->device->host->shost_gendev.parent;
+ struct device *dev = cmd->device->host->dma_dev;
nseg = dma_map_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd),
cmd->sc_data_direction);
@@ -41,7 +41,7 @@ EXPORT_SYMBOL(scsi_dma_map);
void scsi_dma_unmap(struct scsi_cmnd *cmd)
{
if (scsi_sg_count(cmd)) {
- struct device *dev = cmd->device->host->shost_gendev.parent;
+ struct device *dev = cmd->device->host->dma_dev;
dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd),
cmd->sc_data_direction);
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c
index 723fdecd91b..0fd6ae6911a 100644
--- a/drivers/scsi/scsi_netlink.c
+++ b/drivers/scsi/scsi_netlink.c
@@ -613,7 +613,7 @@ EXPORT_SYMBOL_GPL(scsi_nl_send_transport_msg);
* @data_buf: pointer to vendor unique data buffer
*
* Returns:
- * 0 on succesful return
+ * 0 on successful return
* otherwise, failing error code
*
* Notes:
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 47291bcff0d..012f73a9688 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -251,6 +251,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
sdev->model = scsi_null_device_strs;
sdev->rev = scsi_null_device_strs;
sdev->host = shost;
+ sdev->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD;
sdev->id = starget->id;
sdev->lun = lun;
sdev->channel = starget->channel;
@@ -941,6 +942,8 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
}
}
+ sdev->max_queue_depth = sdev->queue_depth;
+
/*
* Ok, the device is now all set up, we can
* register it and tell the rest of the kernel
diff --git a/drivers/scsi/scsi_sysctl.c b/drivers/scsi/scsi_sysctl.c
index 63a30f566f3..2b6b93f7d8e 100644
--- a/drivers/scsi/scsi_sysctl.c
+++ b/drivers/scsi/scsi_sysctl.c
@@ -13,26 +13,23 @@
static ctl_table scsi_table[] = {
- { .ctl_name = DEV_SCSI_LOGGING_LEVEL,
- .procname = "logging_level",
+ { .procname = "logging_level",
.data = &scsi_logging_level,
.maxlen = sizeof(scsi_logging_level),
.mode = 0644,
- .proc_handler = &proc_dointvec },
+ .proc_handler = proc_dointvec },
{ }
};
static ctl_table scsi_dir_table[] = {
- { .ctl_name = DEV_SCSI,
- .procname = "scsi",
+ { .procname = "scsi",
.mode = 0555,
.child = scsi_table },
{ }
};
static ctl_table scsi_root_table[] = {
- { .ctl_name = CTL_DEV,
- .procname = "dev",
+ { .procname = "dev",
.mode = 0555,
.child = scsi_dir_table },
{ }
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 392d8db3390..5a065055e68 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -766,10 +766,13 @@ sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr,
if (depth < 1)
return -EINVAL;
- retval = sht->change_queue_depth(sdev, depth);
+ retval = sht->change_queue_depth(sdev, depth,
+ SCSI_QDEPTH_DEFAULT);
if (retval < 0)
return retval;
+ sdev->max_queue_depth = sdev->queue_depth;
+
return count;
}
@@ -778,6 +781,37 @@ static struct device_attribute sdev_attr_queue_depth_rw =
sdev_store_queue_depth_rw);
static ssize_t
+sdev_show_queue_ramp_up_period(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev;
+ sdev = to_scsi_device(dev);
+ return snprintf(buf, 20, "%u\n",
+ jiffies_to_msecs(sdev->queue_ramp_up_period));
+}
+
+static ssize_t
+sdev_store_queue_ramp_up_period(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ unsigned long period;
+
+ if (strict_strtoul(buf, 10, &period))
+ return -EINVAL;
+
+ sdev->queue_ramp_up_period = msecs_to_jiffies(period);
+ return period;
+}
+
+static struct device_attribute sdev_attr_queue_ramp_up_period =
+ __ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR,
+ sdev_show_queue_ramp_up_period,
+ sdev_store_queue_ramp_up_period);
+
+static ssize_t
sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -867,8 +901,12 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
sdev->is_visible = 1;
/* create queue files, which may be writable, depending on the host */
- if (sdev->host->hostt->change_queue_depth)
- error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw);
+ if (sdev->host->hostt->change_queue_depth) {
+ error = device_create_file(&sdev->sdev_gendev,
+ &sdev_attr_queue_depth_rw);
+ error = device_create_file(&sdev->sdev_gendev,
+ &sdev_attr_queue_ramp_up_period);
+ }
else
error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth);
if (error)
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index c6f70dae9b2..6531c91501b 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -27,6 +27,7 @@
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
@@ -2384,6 +2385,7 @@ fc_rport_final_delete(struct work_struct *work)
struct Scsi_Host *shost = rport_to_shost(rport);
struct fc_internal *i = to_fc_internal(shost->transportt);
unsigned long flags;
+ int do_callback = 0;
/*
* if a scan is pending, flush the SCSI Host work_q so that
@@ -2422,8 +2424,15 @@ fc_rport_final_delete(struct work_struct *work)
* Avoid this call if we already called it when we preserved the
* rport for the binding.
*/
+ spin_lock_irqsave(shost->host_lock, flags);
if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
- (i->f->dev_loss_tmo_callbk))
+ (i->f->dev_loss_tmo_callbk)) {
+ rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+ do_callback = 1;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+
+ if (do_callback)
i->f->dev_loss_tmo_callbk(rport);
fc_bsg_remove(rport->rqst_q);
@@ -2970,6 +2979,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
struct fc_internal *i = to_fc_internal(shost->transportt);
struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
unsigned long flags;
+ int do_callback = 0;
spin_lock_irqsave(shost->host_lock, flags);
@@ -3035,7 +3045,6 @@ fc_timeout_deleted_rport(struct work_struct *work)
rport->roles = FC_PORT_ROLE_UNKNOWN;
rport->port_state = FC_PORTSTATE_NOTPRESENT;
rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
- rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
/*
* Pre-emptively kill I/O rather than waiting for the work queue
@@ -3045,32 +3054,40 @@ fc_timeout_deleted_rport(struct work_struct *work)
spin_unlock_irqrestore(shost->host_lock, flags);
fc_terminate_rport_io(rport);
- BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT);
+ spin_lock_irqsave(shost->host_lock, flags);
+
+ if (rport->port_state == FC_PORTSTATE_NOTPRESENT) { /* still missing */
- /* remove the identifiers that aren't used in the consisting binding */
- switch (fc_host->tgtid_bind_type) {
- case FC_TGTID_BIND_BY_WWPN:
- rport->node_name = -1;
- rport->port_id = -1;
- break;
- case FC_TGTID_BIND_BY_WWNN:
- rport->port_name = -1;
- rport->port_id = -1;
- break;
- case FC_TGTID_BIND_BY_ID:
- rport->node_name = -1;
- rport->port_name = -1;
- break;
- case FC_TGTID_BIND_NONE: /* to keep compiler happy */
- break;
+ /* remove the identifiers that aren't used in the consisting binding */
+ switch (fc_host->tgtid_bind_type) {
+ case FC_TGTID_BIND_BY_WWPN:
+ rport->node_name = -1;
+ rport->port_id = -1;
+ break;
+ case FC_TGTID_BIND_BY_WWNN:
+ rport->port_name = -1;
+ rport->port_id = -1;
+ break;
+ case FC_TGTID_BIND_BY_ID:
+ rport->node_name = -1;
+ rport->port_name = -1;
+ break;
+ case FC_TGTID_BIND_NONE: /* to keep compiler happy */
+ break;
+ }
+
+ /*
+ * As this only occurs if the remote port (scsi target)
+ * went away and didn't come back - we'll remove
+ * all attached scsi devices.
+ */
+ rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+ fc_queue_work(shost, &rport->stgt_delete_work);
+
+ do_callback = 1;
}
- /*
- * As this only occurs if the remote port (scsi target)
- * went away and didn't come back - we'll remove
- * all attached scsi devices.
- */
- fc_queue_work(shost, &rport->stgt_delete_work);
+ spin_unlock_irqrestore(shost->host_lock, flags);
/*
* Notify the driver that the rport is now dead. The LLDD will
@@ -3078,7 +3095,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
*
* Note: we set the CALLBK_DONE flag above to correspond
*/
- if (i->f->dev_loss_tmo_callbk)
+ if (do_callback && i->f->dev_loss_tmo_callbk)
i->f->dev_loss_tmo_callbk(rport);
}
@@ -3128,6 +3145,31 @@ fc_scsi_scan_rport(struct work_struct *work)
spin_unlock_irqrestore(shost->host_lock, flags);
}
+/**
+ * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport
+ * @cmnd: SCSI command that scsi_eh is trying to recover
+ *
+ * This routine can be called from a FC LLD scsi_eh callback. It
+ * blocks the scsi_eh thread until the fc_rport leaves the
+ * FC_PORTSTATE_BLOCKED. This is necessary to avoid the scsi_eh
+ * failing recovery actions for blocked rports which would lead to
+ * offlined SCSI devices.
+ */
+void fc_block_scsi_eh(struct scsi_cmnd *cmnd)
+{
+ struct Scsi_Host *shost = cmnd->device->host;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+ unsigned long flags;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ while (rport->port_state == FC_PORTSTATE_BLOCKED) {
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ msleep(1000);
+ spin_lock_irqsave(shost->host_lock, flags);
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+}
+EXPORT_SYMBOL(fc_block_scsi_eh);
/**
* fc_vport_setup - allocates and creates a FC virtual port.
@@ -3769,8 +3811,9 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
return;
while (!blk_queue_plugged(q)) {
- if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED))
- break;
+ if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) &&
+ !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT))
+ break;
req = blk_fetch_request(q);
if (!req)
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index ad897df3661..ea3892e7e0f 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -30,7 +30,7 @@
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/iscsi_if.h>
-#define ISCSI_SESSION_ATTRS 21
+#define ISCSI_SESSION_ATTRS 22
#define ISCSI_CONN_ATTRS 13
#define ISCSI_HOST_ATTRS 4
@@ -627,8 +627,10 @@ static void __iscsi_block_session(struct work_struct *work)
spin_unlock_irqrestore(&session->lock, flags);
scsi_target_block(&session->dev);
ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
- queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
- session->recovery_tmo * HZ);
+ if (session->recovery_tmo >= 0)
+ queue_delayed_work(iscsi_eh_timer_workq,
+ &session->recovery_work,
+ session->recovery_tmo * HZ);
}
void iscsi_block_session(struct iscsi_cls_session *session)
@@ -1348,8 +1350,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
switch (ev->u.set_param.param) {
case ISCSI_PARAM_SESS_RECOVERY_TMO:
sscanf(data, "%d", &value);
- if (value != 0)
- session->recovery_tmo = value;
+ session->recovery_tmo = value;
break;
default:
err = transport->set_param(conn, ev->u.set_param.param,
@@ -1759,6 +1760,7 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0);
iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0)
@@ -2000,6 +2002,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+ SETUP_SESSION_RD_ATTR(tgt_reset_tmo,ISCSI_TGT_RESET_TMO);
SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index fd47cb1bee1..f27e52d963d 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -666,7 +666,7 @@ EXPORT_SYMBOL(sas_phy_add);
*
* Note:
* This function must only be called on a PHY that has not
- * sucessfully been added using sas_phy_add().
+ * successfully been added using sas_phy_add().
*/
void sas_phy_free(struct sas_phy *phy)
{
@@ -896,7 +896,7 @@ EXPORT_SYMBOL(sas_port_add);
*
* Note:
* This function must only be called on a PORT that has not
- * sucessfully been added using sas_port_add().
+ * successfully been added using sas_port_add().
*/
void sas_port_free(struct sas_port *port)
{
@@ -1476,7 +1476,7 @@ EXPORT_SYMBOL(sas_rphy_add);
*
* Note:
* This function must only be called on a remote
- * PHY that has not sucessfully been added using
+ * PHY that has not successfully been added using
* sas_rphy_add() (or has been sas_rphy_remove()'d)
*/
void sas_rphy_free(struct sas_rphy *rphy)
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 12d58a7ed6b..ad59abb4772 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -2280,7 +2280,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
} else if (code == MT_ST_SET_CLN) {
value = (options & ~MT_ST_OPTIONS) & 0xff;
if (value != 0 &&
- value < EXTENDED_SENSE_START && value >= SCSI_SENSE_BUFFERSIZE)
+ (value < EXTENDED_SENSE_START ||
+ value >= SCSI_SENSE_BUFFERSIZE))
return (-EINVAL);
STp->cln_mode = value;
STp->cln_sense_mask = (options >> 8) & 0xff;
diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c
index 09fa8861fc5..3058bb1aff9 100644
--- a/drivers/scsi/stex.c
+++ b/drivers/scsi/stex.c
@@ -36,11 +36,11 @@
#include <scsi/scsi_eh.h>
#define DRV_NAME "stex"
-#define ST_DRIVER_VERSION "4.6.0000.3"
+#define ST_DRIVER_VERSION "4.6.0000.4"
#define ST_VER_MAJOR 4
#define ST_VER_MINOR 6
#define ST_OEM 0
-#define ST_BUILD_VER 3
+#define ST_BUILD_VER 4
enum {
/* MU register offset */
@@ -64,24 +64,24 @@ enum {
YH2I_REQ_HI = 0xc4,
/* MU register value */
- MU_INBOUND_DOORBELL_HANDSHAKE = 1,
- MU_INBOUND_DOORBELL_REQHEADCHANGED = 2,
- MU_INBOUND_DOORBELL_STATUSTAILCHANGED = 4,
- MU_INBOUND_DOORBELL_HMUSTOPPED = 8,
- MU_INBOUND_DOORBELL_RESET = 16,
-
- MU_OUTBOUND_DOORBELL_HANDSHAKE = 1,
- MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = 2,
- MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = 4,
- MU_OUTBOUND_DOORBELL_BUSCHANGE = 8,
- MU_OUTBOUND_DOORBELL_HASEVENT = 16,
+ MU_INBOUND_DOORBELL_HANDSHAKE = (1 << 0),
+ MU_INBOUND_DOORBELL_REQHEADCHANGED = (1 << 1),
+ MU_INBOUND_DOORBELL_STATUSTAILCHANGED = (1 << 2),
+ MU_INBOUND_DOORBELL_HMUSTOPPED = (1 << 3),
+ MU_INBOUND_DOORBELL_RESET = (1 << 4),
+
+ MU_OUTBOUND_DOORBELL_HANDSHAKE = (1 << 0),
+ MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = (1 << 1),
+ MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED = (1 << 2),
+ MU_OUTBOUND_DOORBELL_BUSCHANGE = (1 << 3),
+ MU_OUTBOUND_DOORBELL_HASEVENT = (1 << 4),
+ MU_OUTBOUND_DOORBELL_REQUEST_RESET = (1 << 27),
/* MU status code */
MU_STATE_STARTING = 1,
- MU_STATE_FMU_READY_FOR_HANDSHAKE = 2,
- MU_STATE_SEND_HANDSHAKE_FRAME = 3,
- MU_STATE_STARTED = 4,
- MU_STATE_RESETTING = 5,
+ MU_STATE_STARTED = 2,
+ MU_STATE_RESETTING = 3,
+ MU_STATE_FAILED = 4,
MU_MAX_DELAY = 120,
MU_HANDSHAKE_SIGNATURE = 0x55aaaa55,
@@ -111,6 +111,8 @@ enum {
SS_H2I_INT_RESET = 0x100,
+ SS_I2H_REQUEST_RESET = 0x2000,
+
SS_MU_OPERATIONAL = 0x80000000,
STEX_CDB_LENGTH = 16,
@@ -160,6 +162,7 @@ enum {
INQUIRY_EVPD = 0x01,
ST_ADDITIONAL_MEM = 0x200000,
+ ST_ADDITIONAL_MEM_MIN = 0x80000,
};
struct st_sgitem {
@@ -311,6 +314,10 @@ struct st_hba {
struct st_ccb *wait_ccb;
__le32 *scratch;
+ char work_q_name[20];
+ struct workqueue_struct *work_q;
+ struct work_struct reset_work;
+ wait_queue_head_t reset_waitq;
unsigned int mu_status;
unsigned int cardtype;
int msi_enabled;
@@ -577,6 +584,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
lun = cmd->device->lun;
hba = (struct st_hba *) &host->hostdata[0];
+ if (unlikely(hba->mu_status == MU_STATE_RESETTING))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
switch (cmd->cmnd[0]) {
case MODE_SENSE_10:
{
@@ -841,7 +851,6 @@ static irqreturn_t stex_intr(int irq, void *__hba)
void __iomem *base = hba->mmio_base;
u32 data;
unsigned long flags;
- int handled = 0;
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -852,12 +861,16 @@ static irqreturn_t stex_intr(int irq, void *__hba)
writel(data, base + ODBL);
readl(base + ODBL); /* flush */
stex_mu_intr(hba, data);
- handled = 1;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ if (unlikely(data & MU_OUTBOUND_DOORBELL_REQUEST_RESET &&
+ hba->cardtype == st_shasta))
+ queue_work(hba->work_q, &hba->reset_work);
+ return IRQ_HANDLED;
}
spin_unlock_irqrestore(hba->host->host_lock, flags);
- return IRQ_RETVAL(handled);
+ return IRQ_NONE;
}
static void stex_ss_mu_intr(struct st_hba *hba)
@@ -939,7 +952,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
void __iomem *base = hba->mmio_base;
u32 data;
unsigned long flags;
- int handled = 0;
spin_lock_irqsave(hba->host->host_lock, flags);
@@ -948,12 +960,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
/* clear the interrupt */
writel(data, base + YI2H_INT_C);
stex_ss_mu_intr(hba);
- handled = 1;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ if (unlikely(data & SS_I2H_REQUEST_RESET))
+ queue_work(hba->work_q, &hba->reset_work);
+ return IRQ_HANDLED;
}
spin_unlock_irqrestore(hba->host->host_lock, flags);
- return IRQ_RETVAL(handled);
+ return IRQ_NONE;
}
static int stex_common_handshake(struct st_hba *hba)
@@ -1001,7 +1016,7 @@ static int stex_common_handshake(struct st_hba *hba)
h->partner_type = HMU_PARTNER_TYPE;
if (hba->extra_offset) {
h->extra_offset = cpu_to_le32(hba->extra_offset);
- h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM);
+ h->extra_size = cpu_to_le32(hba->dma_size - hba->extra_offset);
} else
h->extra_offset = h->extra_size = 0;
@@ -1046,7 +1061,7 @@ static int stex_ss_handshake(struct st_hba *hba)
struct st_msg_header *msg_h;
struct handshake_frame *h;
__le32 *scratch;
- u32 data;
+ u32 data, scratch_size;
unsigned long before;
int ret = 0;
@@ -1074,13 +1089,16 @@ static int stex_ss_handshake(struct st_hba *hba)
stex_gettime(&h->hosttime);
h->partner_type = HMU_PARTNER_TYPE;
h->extra_offset = h->extra_size = 0;
- h->scratch_size = cpu_to_le32((hba->sts_count+1)*sizeof(u32));
+ scratch_size = (hba->sts_count+1)*sizeof(u32);
+ h->scratch_size = cpu_to_le32(scratch_size);
data = readl(base + YINT_EN);
data &= ~4;
writel(data, base + YINT_EN);
writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI);
+ readl(base + YH2I_REQ_HI);
writel(hba->dma_handle, base + YH2I_REQ);
+ readl(base + YH2I_REQ); /* flush */
scratch = hba->scratch;
before = jiffies;
@@ -1096,7 +1114,7 @@ static int stex_ss_handshake(struct st_hba *hba)
msleep(1);
}
- *scratch = 0;
+ memset(scratch, 0, scratch_size);
msg_h->flag = 0;
return ret;
}
@@ -1105,19 +1123,24 @@ static int stex_handshake(struct st_hba *hba)
{
int err;
unsigned long flags;
+ unsigned int mu_status;
err = (hba->cardtype == st_yel) ?
stex_ss_handshake(hba) : stex_common_handshake(hba);
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ mu_status = hba->mu_status;
if (err == 0) {
- spin_lock_irqsave(hba->host->host_lock, flags);
hba->req_head = 0;
hba->req_tail = 0;
hba->status_head = 0;
hba->status_tail = 0;
hba->out_req_cnt = 0;
hba->mu_status = MU_STATE_STARTED;
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- }
+ } else
+ hba->mu_status = MU_STATE_FAILED;
+ if (mu_status == MU_STATE_RESETTING)
+ wake_up_all(&hba->reset_waitq);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
return err;
}
@@ -1137,17 +1160,11 @@ static int stex_abort(struct scsi_cmnd *cmd)
base = hba->mmio_base;
spin_lock_irqsave(host->host_lock, flags);
- if (tag < host->can_queue && hba->ccb[tag].cmd == cmd)
+ if (tag < host->can_queue &&
+ hba->ccb[tag].req && hba->ccb[tag].cmd == cmd)
hba->wait_ccb = &hba->ccb[tag];
- else {
- for (tag = 0; tag < host->can_queue; tag++)
- if (hba->ccb[tag].cmd == cmd) {
- hba->wait_ccb = &hba->ccb[tag];
- break;
- }
- if (tag >= host->can_queue)
- goto out;
- }
+ else
+ goto out;
if (hba->cardtype == st_yel) {
data = readl(base + YI2H_INT);
@@ -1221,6 +1238,37 @@ static void stex_hard_reset(struct st_hba *hba)
hba->pdev->saved_config_space[i]);
}
+static int stex_yos_reset(struct st_hba *hba)
+{
+ void __iomem *base;
+ unsigned long flags, before;
+ int ret = 0;
+
+ base = hba->mmio_base;
+ writel(MU_INBOUND_DOORBELL_RESET, base + IDBL);
+ readl(base + IDBL); /* flush */
+ before = jiffies;
+ while (hba->out_req_cnt > 0) {
+ if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
+ printk(KERN_WARNING DRV_NAME
+ "(%s): reset timeout\n", pci_name(hba->pdev));
+ ret = -1;
+ break;
+ }
+ msleep(1);
+ }
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (ret == -1)
+ hba->mu_status = MU_STATE_FAILED;
+ else
+ hba->mu_status = MU_STATE_STARTED;
+ wake_up_all(&hba->reset_waitq);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ return ret;
+}
+
static void stex_ss_reset(struct st_hba *hba)
{
writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT);
@@ -1228,66 +1276,86 @@ static void stex_ss_reset(struct st_hba *hba)
ssleep(5);
}
-static int stex_reset(struct scsi_cmnd *cmd)
+static int stex_do_reset(struct st_hba *hba)
{
- struct st_hba *hba;
- void __iomem *base;
- unsigned long flags, before;
+ struct st_ccb *ccb;
+ unsigned long flags;
+ unsigned int mu_status = MU_STATE_RESETTING;
+ u16 tag;
- hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->mu_status == MU_STATE_STARTING) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ printk(KERN_INFO DRV_NAME "(%s): request reset during init\n",
+ pci_name(hba->pdev));
+ return 0;
+ }
+ while (hba->mu_status == MU_STATE_RESETTING) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ wait_event_timeout(hba->reset_waitq,
+ hba->mu_status != MU_STATE_RESETTING,
+ MU_MAX_DELAY * HZ);
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ mu_status = hba->mu_status;
+ }
- printk(KERN_INFO DRV_NAME
- "(%s): resetting host\n", pci_name(hba->pdev));
- scsi_print_command(cmd);
+ if (mu_status != MU_STATE_RESETTING) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return (mu_status == MU_STATE_STARTED) ? 0 : -1;
+ }
hba->mu_status = MU_STATE_RESETTING;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ if (hba->cardtype == st_yosemite)
+ return stex_yos_reset(hba);
if (hba->cardtype == st_shasta)
stex_hard_reset(hba);
else if (hba->cardtype == st_yel)
stex_ss_reset(hba);
- if (hba->cardtype != st_yosemite) {
- if (stex_handshake(hba)) {
- printk(KERN_WARNING DRV_NAME
- "(%s): resetting: handshake failed\n",
- pci_name(hba->pdev));
- return FAILED;
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ for (tag = 0; tag < hba->host->can_queue; tag++) {
+ ccb = &hba->ccb[tag];
+ if (ccb->req == NULL)
+ continue;
+ ccb->req = NULL;
+ if (ccb->cmd) {
+ scsi_dma_unmap(ccb->cmd);
+ ccb->cmd->result = DID_RESET << 16;
+ ccb->cmd->scsi_done(ccb->cmd);
+ ccb->cmd = NULL;
}
- return SUCCESS;
}
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
- /* st_yosemite */
- writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL);
- readl(hba->mmio_base + IDBL); /* flush */
- before = jiffies;
- while (hba->out_req_cnt > 0) {
- if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
- printk(KERN_WARNING DRV_NAME
- "(%s): reset timeout\n", pci_name(hba->pdev));
- return FAILED;
- }
- msleep(1);
- }
+ if (stex_handshake(hba) == 0)
+ return 0;
- base = hba->mmio_base;
- writel(0, base + IMR0);
- readl(base + IMR0);
- writel(0, base + OMR0);
- readl(base + OMR0);
- writel(0, base + IMR1);
- readl(base + IMR1);
- writel(0, base + OMR1);
- readl(base + OMR1); /* flush */
- spin_lock_irqsave(hba->host->host_lock, flags);
- hba->req_head = 0;
- hba->req_tail = 0;
- hba->status_head = 0;
- hba->status_tail = 0;
- hba->out_req_cnt = 0;
- hba->mu_status = MU_STATE_STARTED;
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- return SUCCESS;
+ printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n",
+ pci_name(hba->pdev));
+ return -1;
+}
+
+static int stex_reset(struct scsi_cmnd *cmd)
+{
+ struct st_hba *hba;
+
+ hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+
+ printk(KERN_INFO DRV_NAME
+ "(%s): resetting host\n", pci_name(hba->pdev));
+ scsi_print_command(cmd);
+
+ return stex_do_reset(hba) ? FAILED : SUCCESS;
+}
+
+static void stex_reset_work(struct work_struct *work)
+{
+ struct st_hba *hba = container_of(work, struct st_hba, reset_work);
+
+ stex_do_reset(hba);
}
static int stex_biosparam(struct scsi_device *sdev,
@@ -1420,8 +1488,8 @@ static int stex_set_dma_mask(struct pci_dev * pdev)
{
int ret;
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
- && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+ && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
return 0;
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (!ret)
@@ -1528,10 +1596,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hba->dma_mem = dma_alloc_coherent(&pdev->dev,
hba->dma_size, &hba->dma_handle, GFP_KERNEL);
if (!hba->dma_mem) {
- err = -ENOMEM;
- printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
- pci_name(pdev));
- goto out_iounmap;
+ /* Retry minimum coherent mapping for st_seq and st_vsc */
+ if (hba->cardtype == st_seq ||
+ (hba->cardtype == st_vsc && (pdev->subsystem_device & 1))) {
+ printk(KERN_WARNING DRV_NAME
+ "(%s): allocating min buffer for controller\n",
+ pci_name(pdev));
+ hba->dma_size = hba->extra_offset
+ + ST_ADDITIONAL_MEM_MIN;
+ hba->dma_mem = dma_alloc_coherent(&pdev->dev,
+ hba->dma_size, &hba->dma_handle, GFP_KERNEL);
+ }
+
+ if (!hba->dma_mem) {
+ err = -ENOMEM;
+ printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
+ pci_name(pdev));
+ goto out_iounmap;
+ }
}
hba->ccb = kcalloc(ci->rq_count, sizeof(struct st_ccb), GFP_KERNEL);
@@ -1568,12 +1650,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
hba->host = host;
hba->pdev = pdev;
+ init_waitqueue_head(&hba->reset_waitq);
+
+ snprintf(hba->work_q_name, sizeof(hba->work_q_name),
+ "stex_wq_%d", host->host_no);
+ hba->work_q = create_singlethread_workqueue(hba->work_q_name);
+ if (!hba->work_q) {
+ printk(KERN_ERR DRV_NAME "(%s): create workqueue failed\n",
+ pci_name(pdev));
+ err = -ENOMEM;
+ goto out_ccb_free;
+ }
+ INIT_WORK(&hba->reset_work, stex_reset_work);
err = stex_request_irq(hba);
if (err) {
printk(KERN_ERR DRV_NAME "(%s): request irq failed\n",
pci_name(pdev));
- goto out_ccb_free;
+ goto out_free_wq;
}
err = stex_handshake(hba);
@@ -1602,6 +1696,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
out_free_irq:
stex_free_irq(hba);
+out_free_wq:
+ destroy_workqueue(hba->work_q);
out_ccb_free:
kfree(hba->ccb);
out_pci_free:
@@ -1669,6 +1765,8 @@ static void stex_hba_free(struct st_hba *hba)
{
stex_free_irq(hba);
+ destroy_workqueue(hba->work_q);
+
iounmap(hba->mmio_base);
pci_release_regions(hba->pdev);
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index 45374d66d26..2b38f6ad6e1 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -1864,7 +1864,7 @@ static pci_ers_result_t sym2_io_slot_dump(struct pci_dev *pdev)
*
* This routine is similar to sym_set_workarounds(), except
* that, at this point, we already know that the device was
- * succesfully intialized at least once before, and so most
+ * successfully intialized at least once before, and so most
* of the steps taken there are un-needed here.
*/
static void sym2_reset_workarounds(struct pci_dev *pdev)
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c
index 297deb817a5..a7bc8b7b09a 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.c
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c
@@ -2692,7 +2692,7 @@ static void sym_int_ma (struct sym_hcb *np)
* we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids
* bloat for such a should_not_happen situation).
* In all other situation, we reset the BUS.
- * Are these assumptions reasonnable ? (Wait and see ...)
+ * Are these assumptions reasonable ? (Wait and see ...)
*/
unexpected_phase:
dsp -= 8;
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h
index 053e63c8682..5a80cbac3f9 100644
--- a/drivers/scsi/sym53c8xx_2/sym_hipd.h
+++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h
@@ -54,7 +54,7 @@
*
* SYM_OPT_LIMIT_COMMAND_REORDERING
* When this option is set, the driver tries to limit tagged
- * command reordering to some reasonnable value.
+ * command reordering to some reasonable value.
* (set for Linux)
*/
#if 0
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
new file mode 100644
index 00000000000..d2604c813a2
--- /dev/null
+++ b/drivers/scsi/vmw_pvscsi.c
@@ -0,0 +1,1407 @@
+/*
+ * Linux driver for VMware's para-virtualized SCSI HBA.
+ *
+ * Copyright (C) 2008-2009, VMware, 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 the
+ * Free Software Foundation; version 2 of the License and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained by: Alok N Kataria <akataria@vmware.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "vmw_pvscsi.h"
+
+#define PVSCSI_LINUX_DRIVER_DESC "VMware PVSCSI driver"
+
+MODULE_DESCRIPTION(PVSCSI_LINUX_DRIVER_DESC);
+MODULE_AUTHOR("VMware, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PVSCSI_DRIVER_VERSION_STRING);
+
+#define PVSCSI_DEFAULT_NUM_PAGES_PER_RING 8
+#define PVSCSI_DEFAULT_NUM_PAGES_MSG_RING 1
+#define PVSCSI_DEFAULT_QUEUE_DEPTH 64
+#define SGL_SIZE PAGE_SIZE
+
+struct pvscsi_sg_list {
+ struct PVSCSISGElement sge[PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT];
+};
+
+struct pvscsi_ctx {
+ /*
+ * The index of the context in cmd_map serves as the context ID for a
+ * 1-to-1 mapping completions back to requests.
+ */
+ struct scsi_cmnd *cmd;
+ struct pvscsi_sg_list *sgl;
+ struct list_head list;
+ dma_addr_t dataPA;
+ dma_addr_t sensePA;
+ dma_addr_t sglPA;
+};
+
+struct pvscsi_adapter {
+ char *mmioBase;
+ unsigned int irq;
+ u8 rev;
+ bool use_msi;
+ bool use_msix;
+ bool use_msg;
+
+ spinlock_t hw_lock;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct PVSCSIRingReqDesc *req_ring;
+ unsigned req_pages;
+ unsigned req_depth;
+ dma_addr_t reqRingPA;
+
+ struct PVSCSIRingCmpDesc *cmp_ring;
+ unsigned cmp_pages;
+ dma_addr_t cmpRingPA;
+
+ struct PVSCSIRingMsgDesc *msg_ring;
+ unsigned msg_pages;
+ dma_addr_t msgRingPA;
+
+ struct PVSCSIRingsState *rings_state;
+ dma_addr_t ringStatePA;
+
+ struct pci_dev *dev;
+ struct Scsi_Host *host;
+
+ struct list_head cmd_pool;
+ struct pvscsi_ctx *cmd_map;
+};
+
+
+/* Command line parameters */
+static int pvscsi_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_PER_RING;
+static int pvscsi_msg_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_MSG_RING;
+static int pvscsi_cmd_per_lun = PVSCSI_DEFAULT_QUEUE_DEPTH;
+static bool pvscsi_disable_msi;
+static bool pvscsi_disable_msix;
+static bool pvscsi_use_msg = true;
+
+#define PVSCSI_RW (S_IRUSR | S_IWUSR)
+
+module_param_named(ring_pages, pvscsi_ring_pages, int, PVSCSI_RW);
+MODULE_PARM_DESC(ring_pages, "Number of pages per req/cmp ring - (default="
+ __stringify(PVSCSI_DEFAULT_NUM_PAGES_PER_RING) ")");
+
+module_param_named(msg_ring_pages, pvscsi_msg_ring_pages, int, PVSCSI_RW);
+MODULE_PARM_DESC(msg_ring_pages, "Number of pages for the msg ring - (default="
+ __stringify(PVSCSI_DEFAULT_NUM_PAGES_MSG_RING) ")");
+
+module_param_named(cmd_per_lun, pvscsi_cmd_per_lun, int, PVSCSI_RW);
+MODULE_PARM_DESC(cmd_per_lun, "Maximum commands per lun - (default="
+ __stringify(PVSCSI_MAX_REQ_QUEUE_DEPTH) ")");
+
+module_param_named(disable_msi, pvscsi_disable_msi, bool, PVSCSI_RW);
+MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)");
+
+module_param_named(disable_msix, pvscsi_disable_msix, bool, PVSCSI_RW);
+MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default=0)");
+
+module_param_named(use_msg, pvscsi_use_msg, bool, PVSCSI_RW);
+MODULE_PARM_DESC(use_msg, "Use msg ring when available - (default=1)");
+
+static const struct pci_device_id pvscsi_pci_tbl[] = {
+ { PCI_VDEVICE(VMWARE, PCI_DEVICE_ID_VMWARE_PVSCSI) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, pvscsi_pci_tbl);
+
+static struct device *
+pvscsi_dev(const struct pvscsi_adapter *adapter)
+{
+ return &(adapter->dev->dev);
+}
+
+static struct pvscsi_ctx *
+pvscsi_find_context(const struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd)
+{
+ struct pvscsi_ctx *ctx, *end;
+
+ end = &adapter->cmd_map[adapter->req_depth];
+ for (ctx = adapter->cmd_map; ctx < end; ctx++)
+ if (ctx->cmd == cmd)
+ return ctx;
+
+ return NULL;
+}
+
+static struct pvscsi_ctx *
+pvscsi_acquire_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd)
+{
+ struct pvscsi_ctx *ctx;
+
+ if (list_empty(&adapter->cmd_pool))
+ return NULL;
+
+ ctx = list_first_entry(&adapter->cmd_pool, struct pvscsi_ctx, list);
+ ctx->cmd = cmd;
+ list_del(&ctx->list);
+
+ return ctx;
+}
+
+static void pvscsi_release_context(struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx)
+{
+ ctx->cmd = NULL;
+ list_add(&ctx->list, &adapter->cmd_pool);
+}
+
+/*
+ * Map a pvscsi_ctx struct to a context ID field value; we map to a simple
+ * non-zero integer. ctx always points to an entry in cmd_map array, hence
+ * the return value is always >=1.
+ */
+static u64 pvscsi_map_context(const struct pvscsi_adapter *adapter,
+ const struct pvscsi_ctx *ctx)
+{
+ return ctx - adapter->cmd_map + 1;
+}
+
+static struct pvscsi_ctx *
+pvscsi_get_context(const struct pvscsi_adapter *adapter, u64 context)
+{
+ return &adapter->cmd_map[context - 1];
+}
+
+static void pvscsi_reg_write(const struct pvscsi_adapter *adapter,
+ u32 offset, u32 val)
+{
+ writel(val, adapter->mmioBase + offset);
+}
+
+static u32 pvscsi_reg_read(const struct pvscsi_adapter *adapter, u32 offset)
+{
+ return readl(adapter->mmioBase + offset);
+}
+
+static u32 pvscsi_read_intr_status(const struct pvscsi_adapter *adapter)
+{
+ return pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_INTR_STATUS);
+}
+
+static void pvscsi_write_intr_status(const struct pvscsi_adapter *adapter,
+ u32 val)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_STATUS, val);
+}
+
+static void pvscsi_unmask_intr(const struct pvscsi_adapter *adapter)
+{
+ u32 intr_bits;
+
+ intr_bits = PVSCSI_INTR_CMPL_MASK;
+ if (adapter->use_msg)
+ intr_bits |= PVSCSI_INTR_MSG_MASK;
+
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, intr_bits);
+}
+
+static void pvscsi_mask_intr(const struct pvscsi_adapter *adapter)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, 0);
+}
+
+static void pvscsi_write_cmd_desc(const struct pvscsi_adapter *adapter,
+ u32 cmd, const void *desc, size_t len)
+{
+ const u32 *ptr = desc;
+ size_t i;
+
+ len /= sizeof(*ptr);
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND, cmd);
+ for (i = 0; i < len; i++)
+ pvscsi_reg_write(adapter,
+ PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]);
+}
+
+static void pvscsi_abort_cmd(const struct pvscsi_adapter *adapter,
+ const struct pvscsi_ctx *ctx)
+{
+ struct PVSCSICmdDescAbortCmd cmd = { 0 };
+
+ cmd.target = ctx->cmd->device->id;
+ cmd.context = pvscsi_map_context(adapter, ctx);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ABORT_CMD, &cmd, sizeof(cmd));
+}
+
+static void pvscsi_kick_rw_io(const struct pvscsi_adapter *adapter)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
+}
+
+static void pvscsi_process_request_ring(const struct pvscsi_adapter *adapter)
+{
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_NON_RW_IO, 0);
+}
+
+static int scsi_is_rw(unsigned char op)
+{
+ return op == READ_6 || op == WRITE_6 ||
+ op == READ_10 || op == WRITE_10 ||
+ op == READ_12 || op == WRITE_12 ||
+ op == READ_16 || op == WRITE_16;
+}
+
+static void pvscsi_kick_io(const struct pvscsi_adapter *adapter,
+ unsigned char op)
+{
+ if (scsi_is_rw(op))
+ pvscsi_kick_rw_io(adapter);
+ else
+ pvscsi_process_request_ring(adapter);
+}
+
+static void ll_adapter_reset(const struct pvscsi_adapter *adapter)
+{
+ dev_dbg(pvscsi_dev(adapter), "Adapter Reset on %p\n", adapter);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
+}
+
+static void ll_bus_reset(const struct pvscsi_adapter *adapter)
+{
+ dev_dbg(pvscsi_dev(adapter), "Reseting bus on %p\n", adapter);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0);
+}
+
+static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target)
+{
+ struct PVSCSICmdDescResetDevice cmd = { 0 };
+
+ dev_dbg(pvscsi_dev(adapter), "Reseting device: target=%u\n", target);
+
+ cmd.target = target;
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_DEVICE,
+ &cmd, sizeof(cmd));
+}
+
+static void pvscsi_create_sg(struct pvscsi_ctx *ctx,
+ struct scatterlist *sg, unsigned count)
+{
+ unsigned i;
+ struct PVSCSISGElement *sge;
+
+ BUG_ON(count > PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT);
+
+ sge = &ctx->sgl->sge[0];
+ for (i = 0; i < count; i++, sg++) {
+ sge[i].addr = sg_dma_address(sg);
+ sge[i].length = sg_dma_len(sg);
+ sge[i].flags = 0;
+ }
+}
+
+/*
+ * Map all data buffers for a command into PCI space and
+ * setup the scatter/gather list if needed.
+ */
+static void pvscsi_map_buffers(struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd,
+ struct PVSCSIRingReqDesc *e)
+{
+ unsigned count;
+ unsigned bufflen = scsi_bufflen(cmd);
+ struct scatterlist *sg;
+
+ e->dataLen = bufflen;
+ e->dataAddr = 0;
+ if (bufflen == 0)
+ return;
+
+ sg = scsi_sglist(cmd);
+ count = scsi_sg_count(cmd);
+ if (count != 0) {
+ int segs = scsi_dma_map(cmd);
+ if (segs > 1) {
+ pvscsi_create_sg(ctx, sg, segs);
+
+ e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST;
+ ctx->sglPA = pci_map_single(adapter->dev, ctx->sgl,
+ SGL_SIZE, PCI_DMA_TODEVICE);
+ e->dataAddr = ctx->sglPA;
+ } else
+ e->dataAddr = sg_dma_address(sg);
+ } else {
+ /*
+ * In case there is no S/G list, scsi_sglist points
+ * directly to the buffer.
+ */
+ ctx->dataPA = pci_map_single(adapter->dev, sg, bufflen,
+ cmd->sc_data_direction);
+ e->dataAddr = ctx->dataPA;
+ }
+}
+
+static void pvscsi_unmap_buffers(const struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx)
+{
+ struct scsi_cmnd *cmd;
+ unsigned bufflen;
+
+ cmd = ctx->cmd;
+ bufflen = scsi_bufflen(cmd);
+
+ if (bufflen != 0) {
+ unsigned count = scsi_sg_count(cmd);
+
+ if (count != 0) {
+ scsi_dma_unmap(cmd);
+ if (ctx->sglPA) {
+ pci_unmap_single(adapter->dev, ctx->sglPA,
+ SGL_SIZE, PCI_DMA_TODEVICE);
+ ctx->sglPA = 0;
+ }
+ } else
+ pci_unmap_single(adapter->dev, ctx->dataPA, bufflen,
+ cmd->sc_data_direction);
+ }
+ if (cmd->sense_buffer)
+ pci_unmap_single(adapter->dev, ctx->sensePA,
+ SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
+}
+
+static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter)
+{
+ adapter->rings_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE,
+ &adapter->ringStatePA);
+ if (!adapter->rings_state)
+ return -ENOMEM;
+
+ adapter->req_pages = min(PVSCSI_MAX_NUM_PAGES_REQ_RING,
+ pvscsi_ring_pages);
+ adapter->req_depth = adapter->req_pages
+ * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+ adapter->req_ring = pci_alloc_consistent(adapter->dev,
+ adapter->req_pages * PAGE_SIZE,
+ &adapter->reqRingPA);
+ if (!adapter->req_ring)
+ return -ENOMEM;
+
+ adapter->cmp_pages = min(PVSCSI_MAX_NUM_PAGES_CMP_RING,
+ pvscsi_ring_pages);
+ adapter->cmp_ring = pci_alloc_consistent(adapter->dev,
+ adapter->cmp_pages * PAGE_SIZE,
+ &adapter->cmpRingPA);
+ if (!adapter->cmp_ring)
+ return -ENOMEM;
+
+ BUG_ON(!IS_ALIGNED(adapter->ringStatePA, PAGE_SIZE));
+ BUG_ON(!IS_ALIGNED(adapter->reqRingPA, PAGE_SIZE));
+ BUG_ON(!IS_ALIGNED(adapter->cmpRingPA, PAGE_SIZE));
+
+ if (!adapter->use_msg)
+ return 0;
+
+ adapter->msg_pages = min(PVSCSI_MAX_NUM_PAGES_MSG_RING,
+ pvscsi_msg_ring_pages);
+ adapter->msg_ring = pci_alloc_consistent(adapter->dev,
+ adapter->msg_pages * PAGE_SIZE,
+ &adapter->msgRingPA);
+ if (!adapter->msg_ring)
+ return -ENOMEM;
+ BUG_ON(!IS_ALIGNED(adapter->msgRingPA, PAGE_SIZE));
+
+ return 0;
+}
+
+static void pvscsi_setup_all_rings(const struct pvscsi_adapter *adapter)
+{
+ struct PVSCSICmdDescSetupRings cmd = { 0 };
+ dma_addr_t base;
+ unsigned i;
+
+ cmd.ringsStatePPN = adapter->ringStatePA >> PAGE_SHIFT;
+ cmd.reqRingNumPages = adapter->req_pages;
+ cmd.cmpRingNumPages = adapter->cmp_pages;
+
+ base = adapter->reqRingPA;
+ for (i = 0; i < adapter->req_pages; i++) {
+ cmd.reqRingPPNs[i] = base >> PAGE_SHIFT;
+ base += PAGE_SIZE;
+ }
+
+ base = adapter->cmpRingPA;
+ for (i = 0; i < adapter->cmp_pages; i++) {
+ cmd.cmpRingPPNs[i] = base >> PAGE_SHIFT;
+ base += PAGE_SIZE;
+ }
+
+ memset(adapter->rings_state, 0, PAGE_SIZE);
+ memset(adapter->req_ring, 0, adapter->req_pages * PAGE_SIZE);
+ memset(adapter->cmp_ring, 0, adapter->cmp_pages * PAGE_SIZE);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_RINGS,
+ &cmd, sizeof(cmd));
+
+ if (adapter->use_msg) {
+ struct PVSCSICmdDescSetupMsgRing cmd_msg = { 0 };
+
+ cmd_msg.numPages = adapter->msg_pages;
+
+ base = adapter->msgRingPA;
+ for (i = 0; i < adapter->msg_pages; i++) {
+ cmd_msg.ringPPNs[i] = base >> PAGE_SHIFT;
+ base += PAGE_SIZE;
+ }
+ memset(adapter->msg_ring, 0, adapter->msg_pages * PAGE_SIZE);
+
+ pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_MSG_RING,
+ &cmd_msg, sizeof(cmd_msg));
+ }
+}
+
+/*
+ * Pull a completion descriptor off and pass the completion back
+ * to the SCSI mid layer.
+ */
+static void pvscsi_complete_request(struct pvscsi_adapter *adapter,
+ const struct PVSCSIRingCmpDesc *e)
+{
+ struct pvscsi_ctx *ctx;
+ struct scsi_cmnd *cmd;
+ u32 btstat = e->hostStatus;
+ u32 sdstat = e->scsiStatus;
+
+ ctx = pvscsi_get_context(adapter, e->context);
+ cmd = ctx->cmd;
+ pvscsi_unmap_buffers(adapter, ctx);
+ pvscsi_release_context(adapter, ctx);
+ cmd->result = 0;
+
+ if (sdstat != SAM_STAT_GOOD &&
+ (btstat == BTSTAT_SUCCESS ||
+ btstat == BTSTAT_LINKED_COMMAND_COMPLETED ||
+ btstat == BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG)) {
+ cmd->result = (DID_OK << 16) | sdstat;
+ if (sdstat == SAM_STAT_CHECK_CONDITION && cmd->sense_buffer)
+ cmd->result |= (DRIVER_SENSE << 24);
+ } else
+ switch (btstat) {
+ case BTSTAT_SUCCESS:
+ case BTSTAT_LINKED_COMMAND_COMPLETED:
+ case BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG:
+ /* If everything went fine, let's move on.. */
+ cmd->result = (DID_OK << 16);
+ break;
+
+ case BTSTAT_DATARUN:
+ case BTSTAT_DATA_UNDERRUN:
+ /* Report residual data in underruns */
+ scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen);
+ cmd->result = (DID_ERROR << 16);
+ break;
+
+ case BTSTAT_SELTIMEO:
+ /* Our emulation returns this for non-connected devs */
+ cmd->result = (DID_BAD_TARGET << 16);
+ break;
+
+ case BTSTAT_LUNMISMATCH:
+ case BTSTAT_TAGREJECT:
+ case BTSTAT_BADMSG:
+ cmd->result = (DRIVER_INVALID << 24);
+ /* fall through */
+
+ case BTSTAT_HAHARDWARE:
+ case BTSTAT_INVPHASE:
+ case BTSTAT_HATIMEOUT:
+ case BTSTAT_NORESPONSE:
+ case BTSTAT_DISCONNECT:
+ case BTSTAT_HASOFTWARE:
+ case BTSTAT_BUSFREE:
+ case BTSTAT_SENSFAILED:
+ cmd->result |= (DID_ERROR << 16);
+ break;
+
+ case BTSTAT_SENTRST:
+ case BTSTAT_RECVRST:
+ case BTSTAT_BUSRESET:
+ cmd->result = (DID_RESET << 16);
+ break;
+
+ case BTSTAT_ABORTQUEUE:
+ cmd->result = (DID_ABORT << 16);
+ break;
+
+ case BTSTAT_SCSIPARITY:
+ cmd->result = (DID_PARITY << 16);
+ break;
+
+ default:
+ cmd->result = (DID_ERROR << 16);
+ scmd_printk(KERN_DEBUG, cmd,
+ "Unknown completion status: 0x%x\n",
+ btstat);
+ }
+
+ dev_dbg(&cmd->device->sdev_gendev,
+ "cmd=%p %x ctx=%p result=0x%x status=0x%x,%x\n",
+ cmd, cmd->cmnd[0], ctx, cmd->result, btstat, sdstat);
+
+ cmd->scsi_done(cmd);
+}
+
+/*
+ * barrier usage : Since the PVSCSI device is emulated, there could be cases
+ * where we may want to serialize some accesses between the driver and the
+ * emulation layer. We use compiler barriers instead of the more expensive
+ * memory barriers because PVSCSI is only supported on X86 which has strong
+ * memory access ordering.
+ */
+static void pvscsi_process_completion_ring(struct pvscsi_adapter *adapter)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+ struct PVSCSIRingCmpDesc *ring = adapter->cmp_ring;
+ u32 cmp_entries = s->cmpNumEntriesLog2;
+
+ while (s->cmpConsIdx != s->cmpProdIdx) {
+ struct PVSCSIRingCmpDesc *e = ring + (s->cmpConsIdx &
+ MASK(cmp_entries));
+ /*
+ * This barrier() ensures that *e is not dereferenced while
+ * the device emulation still writes data into the slot.
+ * Since the device emulation advances s->cmpProdIdx only after
+ * updating the slot we want to check it first.
+ */
+ barrier();
+ pvscsi_complete_request(adapter, e);
+ /*
+ * This barrier() ensures that compiler doesn't reorder write
+ * to s->cmpConsIdx before the read of (*e) inside
+ * pvscsi_complete_request. Otherwise, device emulation may
+ * overwrite *e before we had a chance to read it.
+ */
+ barrier();
+ s->cmpConsIdx++;
+ }
+}
+
+/*
+ * Translate a Linux SCSI request into a request ring entry.
+ */
+static int pvscsi_queue_ring(struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd)
+{
+ struct PVSCSIRingsState *s;
+ struct PVSCSIRingReqDesc *e;
+ struct scsi_device *sdev;
+ u32 req_entries;
+
+ s = adapter->rings_state;
+ sdev = cmd->device;
+ req_entries = s->reqNumEntriesLog2;
+
+ /*
+ * If this condition holds, we might have room on the request ring, but
+ * we might not have room on the completion ring for the response.
+ * However, we have already ruled out this possibility - we would not
+ * have successfully allocated a context if it were true, since we only
+ * have one context per request entry. Check for it anyway, since it
+ * would be a serious bug.
+ */
+ if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) {
+ scmd_printk(KERN_ERR, cmd, "vmw_pvscsi: "
+ "ring full: reqProdIdx=%d cmpConsIdx=%d\n",
+ s->reqProdIdx, s->cmpConsIdx);
+ return -1;
+ }
+
+ e = adapter->req_ring + (s->reqProdIdx & MASK(req_entries));
+
+ e->bus = sdev->channel;
+ e->target = sdev->id;
+ memset(e->lun, 0, sizeof(e->lun));
+ e->lun[1] = sdev->lun;
+
+ if (cmd->sense_buffer) {
+ ctx->sensePA = pci_map_single(adapter->dev, cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE,
+ PCI_DMA_FROMDEVICE);
+ e->senseAddr = ctx->sensePA;
+ e->senseLen = SCSI_SENSE_BUFFERSIZE;
+ } else {
+ e->senseLen = 0;
+ e->senseAddr = 0;
+ }
+ e->cdbLen = cmd->cmd_len;
+ e->vcpuHint = smp_processor_id();
+ memcpy(e->cdb, cmd->cmnd, e->cdbLen);
+
+ e->tag = SIMPLE_QUEUE_TAG;
+ if (sdev->tagged_supported &&
+ (cmd->tag == HEAD_OF_QUEUE_TAG ||
+ cmd->tag == ORDERED_QUEUE_TAG))
+ e->tag = cmd->tag;
+
+ if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ e->flags = PVSCSI_FLAG_CMD_DIR_TOHOST;
+ else if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ e->flags = PVSCSI_FLAG_CMD_DIR_TODEVICE;
+ else if (cmd->sc_data_direction == DMA_NONE)
+ e->flags = PVSCSI_FLAG_CMD_DIR_NONE;
+ else
+ e->flags = 0;
+
+ pvscsi_map_buffers(adapter, ctx, cmd, e);
+
+ e->context = pvscsi_map_context(adapter, ctx);
+
+ barrier();
+
+ s->reqProdIdx++;
+
+ return 0;
+}
+
+static int pvscsi_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ struct pvscsi_ctx *ctx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ ctx = pvscsi_acquire_context(adapter, cmd);
+ if (!ctx || pvscsi_queue_ring(adapter, ctx, cmd) != 0) {
+ if (ctx)
+ pvscsi_release_context(adapter, ctx);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ cmd->scsi_done = done;
+
+ dev_dbg(&cmd->device->sdev_gendev,
+ "queued cmd %p, ctx %p, op=%x\n", cmd, ctx, cmd->cmnd[0]);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ pvscsi_kick_io(adapter, cmd->cmnd[0]);
+
+ return 0;
+}
+
+static int pvscsi_abort(struct scsi_cmnd *cmd)
+{
+ struct pvscsi_adapter *adapter = shost_priv(cmd->device->host);
+ struct pvscsi_ctx *ctx;
+ unsigned long flags;
+
+ scmd_printk(KERN_DEBUG, cmd, "task abort on host %u, %p\n",
+ adapter->host->host_no, cmd);
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ /*
+ * Poll the completion ring first - we might be trying to abort
+ * a command that is waiting to be dispatched in the completion ring.
+ */
+ pvscsi_process_completion_ring(adapter);
+
+ /*
+ * If there is no context for the command, it either already succeeded
+ * or else was never properly issued. Not our problem.
+ */
+ ctx = pvscsi_find_context(adapter, cmd);
+ if (!ctx) {
+ scmd_printk(KERN_DEBUG, cmd, "Failed to abort cmd %p\n", cmd);
+ goto out;
+ }
+
+ pvscsi_abort_cmd(adapter, ctx);
+
+ pvscsi_process_completion_ring(adapter);
+
+out:
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ return SUCCESS;
+}
+
+/*
+ * Abort all outstanding requests. This is only safe to use if the completion
+ * ring will never be walked again or the device has been reset, because it
+ * destroys the 1-1 mapping between context field passed to emulation and our
+ * request structure.
+ */
+static void pvscsi_reset_all(struct pvscsi_adapter *adapter)
+{
+ unsigned i;
+
+ for (i = 0; i < adapter->req_depth; i++) {
+ struct pvscsi_ctx *ctx = &adapter->cmd_map[i];
+ struct scsi_cmnd *cmd = ctx->cmd;
+ if (cmd) {
+ scmd_printk(KERN_ERR, cmd,
+ "Forced reset on cmd %p\n", cmd);
+ pvscsi_unmap_buffers(adapter, ctx);
+ pvscsi_release_context(adapter, ctx);
+ cmd->result = (DID_RESET << 16);
+ cmd->scsi_done(cmd);
+ }
+ }
+}
+
+static int pvscsi_host_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ unsigned long flags;
+ bool use_msg;
+
+ scmd_printk(KERN_INFO, cmd, "SCSI Host reset\n");
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ use_msg = adapter->use_msg;
+
+ if (use_msg) {
+ adapter->use_msg = 0;
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ /*
+ * Now that we know that the ISR won't add more work on the
+ * workqueue we can safely flush any outstanding work.
+ */
+ flush_workqueue(adapter->workqueue);
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ }
+
+ /*
+ * We're going to tear down the entire ring structure and set it back
+ * up, so stalling new requests until all completions are flushed and
+ * the rings are back in place.
+ */
+
+ pvscsi_process_request_ring(adapter);
+
+ ll_adapter_reset(adapter);
+
+ /*
+ * Now process any completions. Note we do this AFTER adapter reset,
+ * which is strange, but stops races where completions get posted
+ * between processing the ring and issuing the reset. The backend will
+ * not touch the ring memory after reset, so the immediately pre-reset
+ * completion ring state is still valid.
+ */
+ pvscsi_process_completion_ring(adapter);
+
+ pvscsi_reset_all(adapter);
+ adapter->use_msg = use_msg;
+ pvscsi_setup_all_rings(adapter);
+ pvscsi_unmask_intr(adapter);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ return SUCCESS;
+}
+
+static int pvscsi_bus_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ unsigned long flags;
+
+ scmd_printk(KERN_INFO, cmd, "SCSI Bus reset\n");
+
+ /*
+ * We don't want to queue new requests for this bus after
+ * flushing all pending requests to emulation, since new
+ * requests could then sneak in during this bus reset phase,
+ * so take the lock now.
+ */
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ pvscsi_process_request_ring(adapter);
+ ll_bus_reset(adapter);
+ pvscsi_process_completion_ring(adapter);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ return SUCCESS;
+}
+
+static int pvscsi_device_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ unsigned long flags;
+
+ scmd_printk(KERN_INFO, cmd, "SCSI device reset on scsi%u:%u\n",
+ host->host_no, cmd->device->id);
+
+ /*
+ * We don't want to queue new requests for this device after flushing
+ * all pending requests to emulation, since new requests could then
+ * sneak in during this device reset phase, so take the lock now.
+ */
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ pvscsi_process_request_ring(adapter);
+ ll_device_reset(adapter, cmd->device->id);
+ pvscsi_process_completion_ring(adapter);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+ return SUCCESS;
+}
+
+static struct scsi_host_template pvscsi_template;
+
+static const char *pvscsi_info(struct Scsi_Host *host)
+{
+ struct pvscsi_adapter *adapter = shost_priv(host);
+ static char buf[256];
+
+ sprintf(buf, "VMware PVSCSI storage adapter rev %d, req/cmp/msg rings: "
+ "%u/%u/%u pages, cmd_per_lun=%u", adapter->rev,
+ adapter->req_pages, adapter->cmp_pages, adapter->msg_pages,
+ pvscsi_template.cmd_per_lun);
+
+ return buf;
+}
+
+static struct scsi_host_template pvscsi_template = {
+ .module = THIS_MODULE,
+ .name = "VMware PVSCSI Host Adapter",
+ .proc_name = "vmw_pvscsi",
+ .info = pvscsi_info,
+ .queuecommand = pvscsi_queue,
+ .this_id = -1,
+ .sg_tablesize = PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT,
+ .dma_boundary = UINT_MAX,
+ .max_sectors = 0xffff,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_abort_handler = pvscsi_abort,
+ .eh_device_reset_handler = pvscsi_device_reset,
+ .eh_bus_reset_handler = pvscsi_bus_reset,
+ .eh_host_reset_handler = pvscsi_host_reset,
+};
+
+static void pvscsi_process_msg(const struct pvscsi_adapter *adapter,
+ const struct PVSCSIRingMsgDesc *e)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+ struct Scsi_Host *host = adapter->host;
+ struct scsi_device *sdev;
+
+ printk(KERN_INFO "vmw_pvscsi: msg type: 0x%x - MSG RING: %u/%u (%u) \n",
+ e->type, s->msgProdIdx, s->msgConsIdx, s->msgNumEntriesLog2);
+
+ BUILD_BUG_ON(PVSCSI_MSG_LAST != 2);
+
+ if (e->type == PVSCSI_MSG_DEV_ADDED) {
+ struct PVSCSIMsgDescDevStatusChanged *desc;
+ desc = (struct PVSCSIMsgDescDevStatusChanged *)e;
+
+ printk(KERN_INFO
+ "vmw_pvscsi: msg: device added at scsi%u:%u:%u\n",
+ desc->bus, desc->target, desc->lun[1]);
+
+ if (!scsi_host_get(host))
+ return;
+
+ sdev = scsi_device_lookup(host, desc->bus, desc->target,
+ desc->lun[1]);
+ if (sdev) {
+ printk(KERN_INFO "vmw_pvscsi: device already exists\n");
+ scsi_device_put(sdev);
+ } else
+ scsi_add_device(adapter->host, desc->bus,
+ desc->target, desc->lun[1]);
+
+ scsi_host_put(host);
+ } else if (e->type == PVSCSI_MSG_DEV_REMOVED) {
+ struct PVSCSIMsgDescDevStatusChanged *desc;
+ desc = (struct PVSCSIMsgDescDevStatusChanged *)e;
+
+ printk(KERN_INFO
+ "vmw_pvscsi: msg: device removed at scsi%u:%u:%u\n",
+ desc->bus, desc->target, desc->lun[1]);
+
+ if (!scsi_host_get(host))
+ return;
+
+ sdev = scsi_device_lookup(host, desc->bus, desc->target,
+ desc->lun[1]);
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ } else
+ printk(KERN_INFO
+ "vmw_pvscsi: failed to lookup scsi%u:%u:%u\n",
+ desc->bus, desc->target, desc->lun[1]);
+
+ scsi_host_put(host);
+ }
+}
+
+static int pvscsi_msg_pending(const struct pvscsi_adapter *adapter)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+
+ return s->msgProdIdx != s->msgConsIdx;
+}
+
+static void pvscsi_process_msg_ring(const struct pvscsi_adapter *adapter)
+{
+ struct PVSCSIRingsState *s = adapter->rings_state;
+ struct PVSCSIRingMsgDesc *ring = adapter->msg_ring;
+ u32 msg_entries = s->msgNumEntriesLog2;
+
+ while (pvscsi_msg_pending(adapter)) {
+ struct PVSCSIRingMsgDesc *e = ring + (s->msgConsIdx &
+ MASK(msg_entries));
+
+ barrier();
+ pvscsi_process_msg(adapter, e);
+ barrier();
+ s->msgConsIdx++;
+ }
+}
+
+static void pvscsi_msg_workqueue_handler(struct work_struct *data)
+{
+ struct pvscsi_adapter *adapter;
+
+ adapter = container_of(data, struct pvscsi_adapter, work);
+
+ pvscsi_process_msg_ring(adapter);
+}
+
+static int pvscsi_setup_msg_workqueue(struct pvscsi_adapter *adapter)
+{
+ char name[32];
+
+ if (!pvscsi_use_msg)
+ return 0;
+
+ pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND,
+ PVSCSI_CMD_SETUP_MSG_RING);
+
+ if (pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_COMMAND_STATUS) == -1)
+ return 0;
+
+ snprintf(name, sizeof(name),
+ "vmw_pvscsi_wq_%u", adapter->host->host_no);
+
+ adapter->workqueue = create_singlethread_workqueue(name);
+ if (!adapter->workqueue) {
+ printk(KERN_ERR "vmw_pvscsi: failed to create work queue\n");
+ return 0;
+ }
+ INIT_WORK(&adapter->work, pvscsi_msg_workqueue_handler);
+
+ return 1;
+}
+
+static irqreturn_t pvscsi_isr(int irq, void *devp)
+{
+ struct pvscsi_adapter *adapter = devp;
+ int handled;
+
+ if (adapter->use_msi || adapter->use_msix)
+ handled = true;
+ else {
+ u32 val = pvscsi_read_intr_status(adapter);
+ handled = (val & PVSCSI_INTR_ALL_SUPPORTED) != 0;
+ if (handled)
+ pvscsi_write_intr_status(devp, val);
+ }
+
+ if (handled) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+
+ pvscsi_process_completion_ring(adapter);
+ if (adapter->use_msg && pvscsi_msg_pending(adapter))
+ queue_work(adapter->workqueue, &adapter->work);
+
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+static void pvscsi_free_sgls(const struct pvscsi_adapter *adapter)
+{
+ struct pvscsi_ctx *ctx = adapter->cmd_map;
+ unsigned i;
+
+ for (i = 0; i < adapter->req_depth; ++i, ++ctx)
+ free_pages((unsigned long)ctx->sgl, get_order(SGL_SIZE));
+}
+
+static int pvscsi_setup_msix(const struct pvscsi_adapter *adapter, int *irq)
+{
+ struct msix_entry entry = { 0, PVSCSI_VECTOR_COMPLETION };
+ int ret;
+
+ ret = pci_enable_msix(adapter->dev, &entry, 1);
+ if (ret)
+ return ret;
+
+ *irq = entry.vector;
+
+ return 0;
+}
+
+static void pvscsi_shutdown_intr(struct pvscsi_adapter *adapter)
+{
+ if (adapter->irq) {
+ free_irq(adapter->irq, adapter);
+ adapter->irq = 0;
+ }
+ if (adapter->use_msi) {
+ pci_disable_msi(adapter->dev);
+ adapter->use_msi = 0;
+ } else if (adapter->use_msix) {
+ pci_disable_msix(adapter->dev);
+ adapter->use_msix = 0;
+ }
+}
+
+static void pvscsi_release_resources(struct pvscsi_adapter *adapter)
+{
+ pvscsi_shutdown_intr(adapter);
+
+ if (adapter->workqueue)
+ destroy_workqueue(adapter->workqueue);
+
+ if (adapter->mmioBase)
+ pci_iounmap(adapter->dev, adapter->mmioBase);
+
+ pci_release_regions(adapter->dev);
+
+ if (adapter->cmd_map) {
+ pvscsi_free_sgls(adapter);
+ kfree(adapter->cmd_map);
+ }
+
+ if (adapter->rings_state)
+ pci_free_consistent(adapter->dev, PAGE_SIZE,
+ adapter->rings_state, adapter->ringStatePA);
+
+ if (adapter->req_ring)
+ pci_free_consistent(adapter->dev,
+ adapter->req_pages * PAGE_SIZE,
+ adapter->req_ring, adapter->reqRingPA);
+
+ if (adapter->cmp_ring)
+ pci_free_consistent(adapter->dev,
+ adapter->cmp_pages * PAGE_SIZE,
+ adapter->cmp_ring, adapter->cmpRingPA);
+
+ if (adapter->msg_ring)
+ pci_free_consistent(adapter->dev,
+ adapter->msg_pages * PAGE_SIZE,
+ adapter->msg_ring, adapter->msgRingPA);
+}
+
+/*
+ * Allocate scatter gather lists.
+ *
+ * These are statically allocated. Trying to be clever was not worth it.
+ *
+ * Dynamic allocation can fail, and we can't go deeep into the memory
+ * allocator, since we're a SCSI driver, and trying too hard to allocate
+ * memory might generate disk I/O. We also don't want to fail disk I/O
+ * in that case because we can't get an allocation - the I/O could be
+ * trying to swap out data to free memory. Since that is pathological,
+ * just use a statically allocated scatter list.
+ *
+ */
+static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
+{
+ struct pvscsi_ctx *ctx;
+ int i;
+
+ ctx = adapter->cmd_map;
+ BUILD_BUG_ON(sizeof(struct pvscsi_sg_list) > SGL_SIZE);
+
+ for (i = 0; i < adapter->req_depth; ++i, ++ctx) {
+ ctx->sgl = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(SGL_SIZE));
+ ctx->sglPA = 0;
+ BUG_ON(!IS_ALIGNED(((unsigned long)ctx->sgl), PAGE_SIZE));
+ if (!ctx->sgl) {
+ for (; i >= 0; --i, --ctx) {
+ free_pages((unsigned long)ctx->sgl,
+ get_order(SGL_SIZE));
+ ctx->sgl = NULL;
+ }
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int __devinit pvscsi_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct pvscsi_adapter *adapter;
+ struct Scsi_Host *host;
+ unsigned int i;
+ unsigned long flags = 0;
+ int error;
+
+ error = -ENODEV;
+
+ if (pci_enable_device(pdev))
+ return error;
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0 &&
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using 64bit dma\n");
+ } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) == 0 &&
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using 32bit dma\n");
+ } else {
+ printk(KERN_ERR "vmw_pvscsi: failed to set DMA mask\n");
+ goto out_disable_device;
+ }
+
+ pvscsi_template.can_queue =
+ min(PVSCSI_MAX_NUM_PAGES_REQ_RING, pvscsi_ring_pages) *
+ PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+ pvscsi_template.cmd_per_lun =
+ min(pvscsi_template.can_queue, pvscsi_cmd_per_lun);
+ host = scsi_host_alloc(&pvscsi_template, sizeof(struct pvscsi_adapter));
+ if (!host) {
+ printk(KERN_ERR "vmw_pvscsi: failed to allocate host\n");
+ goto out_disable_device;
+ }
+
+ adapter = shost_priv(host);
+ memset(adapter, 0, sizeof(*adapter));
+ adapter->dev = pdev;
+ adapter->host = host;
+
+ spin_lock_init(&adapter->hw_lock);
+
+ host->max_channel = 0;
+ host->max_id = 16;
+ host->max_lun = 1;
+ host->max_cmd_len = 16;
+
+ adapter->rev = pdev->revision;
+
+ if (pci_request_regions(pdev, "vmw_pvscsi")) {
+ printk(KERN_ERR "vmw_pvscsi: pci memory selection failed\n");
+ goto out_free_host;
+ }
+
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ if ((pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO))
+ continue;
+
+ if (pci_resource_len(pdev, i) < PVSCSI_MEM_SPACE_SIZE)
+ continue;
+
+ break;
+ }
+
+ if (i == DEVICE_COUNT_RESOURCE) {
+ printk(KERN_ERR
+ "vmw_pvscsi: adapter has no suitable MMIO region\n");
+ goto out_release_resources;
+ }
+
+ adapter->mmioBase = pci_iomap(pdev, i, PVSCSI_MEM_SPACE_SIZE);
+
+ if (!adapter->mmioBase) {
+ printk(KERN_ERR
+ "vmw_pvscsi: can't iomap for BAR %d memsize %lu\n",
+ i, PVSCSI_MEM_SPACE_SIZE);
+ goto out_release_resources;
+ }
+
+ pci_set_master(pdev);
+ pci_set_drvdata(pdev, host);
+
+ ll_adapter_reset(adapter);
+
+ adapter->use_msg = pvscsi_setup_msg_workqueue(adapter);
+
+ error = pvscsi_allocate_rings(adapter);
+ if (error) {
+ printk(KERN_ERR "vmw_pvscsi: unable to allocate ring memory\n");
+ goto out_release_resources;
+ }
+
+ /*
+ * From this point on we should reset the adapter if anything goes
+ * wrong.
+ */
+ pvscsi_setup_all_rings(adapter);
+
+ adapter->cmd_map = kcalloc(adapter->req_depth,
+ sizeof(struct pvscsi_ctx), GFP_KERNEL);
+ if (!adapter->cmd_map) {
+ printk(KERN_ERR "vmw_pvscsi: failed to allocate memory.\n");
+ error = -ENOMEM;
+ goto out_reset_adapter;
+ }
+
+ INIT_LIST_HEAD(&adapter->cmd_pool);
+ for (i = 0; i < adapter->req_depth; i++) {
+ struct pvscsi_ctx *ctx = adapter->cmd_map + i;
+ list_add(&ctx->list, &adapter->cmd_pool);
+ }
+
+ error = pvscsi_allocate_sg(adapter);
+ if (error) {
+ printk(KERN_ERR "vmw_pvscsi: unable to allocate s/g table\n");
+ goto out_reset_adapter;
+ }
+
+ if (!pvscsi_disable_msix &&
+ pvscsi_setup_msix(adapter, &adapter->irq) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using MSI-X\n");
+ adapter->use_msix = 1;
+ } else if (!pvscsi_disable_msi && pci_enable_msi(pdev) == 0) {
+ printk(KERN_INFO "vmw_pvscsi: using MSI\n");
+ adapter->use_msi = 1;
+ adapter->irq = pdev->irq;
+ } else {
+ printk(KERN_INFO "vmw_pvscsi: using INTx\n");
+ adapter->irq = pdev->irq;
+ flags = IRQF_SHARED;
+ }
+
+ error = request_irq(adapter->irq, pvscsi_isr, flags,
+ "vmw_pvscsi", adapter);
+ if (error) {
+ printk(KERN_ERR
+ "vmw_pvscsi: unable to request IRQ: %d\n", error);
+ adapter->irq = 0;
+ goto out_reset_adapter;
+ }
+
+ error = scsi_add_host(host, &pdev->dev);
+ if (error) {
+ printk(KERN_ERR
+ "vmw_pvscsi: scsi_add_host failed: %d\n", error);
+ goto out_reset_adapter;
+ }
+
+ dev_info(&pdev->dev, "VMware PVSCSI rev %d host #%u\n",
+ adapter->rev, host->host_no);
+
+ pvscsi_unmask_intr(adapter);
+
+ scsi_scan_host(host);
+
+ return 0;
+
+out_reset_adapter:
+ ll_adapter_reset(adapter);
+out_release_resources:
+ pvscsi_release_resources(adapter);
+out_free_host:
+ scsi_host_put(host);
+out_disable_device:
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+
+ return error;
+}
+
+static void __pvscsi_shutdown(struct pvscsi_adapter *adapter)
+{
+ pvscsi_mask_intr(adapter);
+
+ if (adapter->workqueue)
+ flush_workqueue(adapter->workqueue);
+
+ pvscsi_shutdown_intr(adapter);
+
+ pvscsi_process_request_ring(adapter);
+ pvscsi_process_completion_ring(adapter);
+ ll_adapter_reset(adapter);
+}
+
+static void pvscsi_shutdown(struct pci_dev *dev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(dev);
+ struct pvscsi_adapter *adapter = shost_priv(host);
+
+ __pvscsi_shutdown(adapter);
+}
+
+static void pvscsi_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *host = pci_get_drvdata(pdev);
+ struct pvscsi_adapter *adapter = shost_priv(host);
+
+ scsi_remove_host(host);
+
+ __pvscsi_shutdown(adapter);
+ pvscsi_release_resources(adapter);
+
+ scsi_host_put(host);
+
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver pvscsi_pci_driver = {
+ .name = "vmw_pvscsi",
+ .id_table = pvscsi_pci_tbl,
+ .probe = pvscsi_probe,
+ .remove = __devexit_p(pvscsi_remove),
+ .shutdown = pvscsi_shutdown,
+};
+
+static int __init pvscsi_init(void)
+{
+ pr_info("%s - version %s\n",
+ PVSCSI_LINUX_DRIVER_DESC, PVSCSI_DRIVER_VERSION_STRING);
+ return pci_register_driver(&pvscsi_pci_driver);
+}
+
+static void __exit pvscsi_exit(void)
+{
+ pci_unregister_driver(&pvscsi_pci_driver);
+}
+
+module_init(pvscsi_init);
+module_exit(pvscsi_exit);
diff --git a/drivers/scsi/vmw_pvscsi.h b/drivers/scsi/vmw_pvscsi.h
new file mode 100644
index 00000000000..62e36e75715
--- /dev/null
+++ b/drivers/scsi/vmw_pvscsi.h
@@ -0,0 +1,397 @@
+/*
+ * VMware PVSCSI header file
+ *
+ * Copyright (C) 2008-2009, VMware, 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 the
+ * Free Software Foundation; version 2 of the License and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained by: Alok N Kataria <akataria@vmware.com>
+ *
+ */
+
+#ifndef _VMW_PVSCSI_H_
+#define _VMW_PVSCSI_H_
+
+#include <linux/types.h>
+
+#define PVSCSI_DRIVER_VERSION_STRING "1.0.1.0-k"
+
+#define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128
+
+#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */
+
+#define PCI_VENDOR_ID_VMWARE 0x15AD
+#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0
+
+/*
+ * host adapter status/error codes
+ */
+enum HostBusAdapterStatus {
+ BTSTAT_SUCCESS = 0x00, /* CCB complete normally with no errors */
+ BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a,
+ BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
+ BTSTAT_DATA_UNDERRUN = 0x0c,
+ BTSTAT_SELTIMEO = 0x11, /* SCSI selection timeout */
+ BTSTAT_DATARUN = 0x12, /* data overrun/underrun */
+ BTSTAT_BUSFREE = 0x13, /* unexpected bus free */
+ BTSTAT_INVPHASE = 0x14, /* invalid bus phase or sequence requested by target */
+ BTSTAT_LUNMISMATCH = 0x17, /* linked CCB has different LUN from first CCB */
+ BTSTAT_SENSFAILED = 0x1b, /* auto request sense failed */
+ BTSTAT_TAGREJECT = 0x1c, /* SCSI II tagged queueing message rejected by target */
+ BTSTAT_BADMSG = 0x1d, /* unsupported message received by the host adapter */
+ BTSTAT_HAHARDWARE = 0x20, /* host adapter hardware failed */
+ BTSTAT_NORESPONSE = 0x21, /* target did not respond to SCSI ATN, sent a SCSI RST */
+ BTSTAT_SENTRST = 0x22, /* host adapter asserted a SCSI RST */
+ BTSTAT_RECVRST = 0x23, /* other SCSI devices asserted a SCSI RST */
+ BTSTAT_DISCONNECT = 0x24, /* target device reconnected improperly (w/o tag) */
+ BTSTAT_BUSRESET = 0x25, /* host adapter issued BUS device reset */
+ BTSTAT_ABORTQUEUE = 0x26, /* abort queue generated */
+ BTSTAT_HASOFTWARE = 0x27, /* host adapter software error */
+ BTSTAT_HATIMEOUT = 0x30, /* host adapter hardware timeout error */
+ BTSTAT_SCSIPARITY = 0x34, /* SCSI parity error detected */
+};
+
+/*
+ * Register offsets.
+ *
+ * These registers are accessible both via i/o space and mm i/o.
+ */
+
+enum PVSCSIRegOffset {
+ PVSCSI_REG_OFFSET_COMMAND = 0x0,
+ PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4,
+ PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8,
+ PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100,
+ PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104,
+ PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108,
+ PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c,
+ PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c,
+ PVSCSI_REG_OFFSET_INTR_MASK = 0x2010,
+ PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
+ PVSCSI_REG_OFFSET_DEBUG = 0x3018,
+ PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018,
+};
+
+/*
+ * Virtual h/w commands.
+ */
+
+enum PVSCSICommands {
+ PVSCSI_CMD_FIRST = 0, /* has to be first */
+
+ PVSCSI_CMD_ADAPTER_RESET = 1,
+ PVSCSI_CMD_ISSUE_SCSI = 2,
+ PVSCSI_CMD_SETUP_RINGS = 3,
+ PVSCSI_CMD_RESET_BUS = 4,
+ PVSCSI_CMD_RESET_DEVICE = 5,
+ PVSCSI_CMD_ABORT_CMD = 6,
+ PVSCSI_CMD_CONFIG = 7,
+ PVSCSI_CMD_SETUP_MSG_RING = 8,
+ PVSCSI_CMD_DEVICE_UNPLUG = 9,
+
+ PVSCSI_CMD_LAST = 10 /* has to be last */
+};
+
+/*
+ * Command descriptor for PVSCSI_CMD_RESET_DEVICE --
+ */
+
+struct PVSCSICmdDescResetDevice {
+ u32 target;
+ u8 lun[8];
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_ABORT_CMD --
+ *
+ * - currently does not support specifying the LUN.
+ * - _pad should be 0.
+ */
+
+struct PVSCSICmdDescAbortCmd {
+ u64 context;
+ u32 target;
+ u32 _pad;
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_RINGS --
+ *
+ * Notes:
+ * - reqRingNumPages and cmpRingNumPages need to be power of two.
+ * - reqRingNumPages and cmpRingNumPages need to be different from 0,
+ * - reqRingNumPages and cmpRingNumPages need to be inferior to
+ * PVSCSI_SETUP_RINGS_MAX_NUM_PAGES.
+ */
+
+#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32
+struct PVSCSICmdDescSetupRings {
+ u32 reqRingNumPages;
+ u32 cmpRingNumPages;
+ u64 ringsStatePPN;
+ u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+ u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_MSG_RING --
+ *
+ * Notes:
+ * - this command was not supported in the initial revision of the h/w
+ * interface. Before using it, you need to check that it is supported by
+ * writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then
+ * immediately after read the 'command status' register:
+ * * a value of -1 means that the cmd is NOT supported,
+ * * a value != -1 means that the cmd IS supported.
+ * If it's supported the 'command status' register should return:
+ * sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(u32).
+ * - this command should be issued _after_ the usual SETUP_RINGS so that the
+ * RingsState page is already setup. If not, the command is a nop.
+ * - numPages needs to be a power of two,
+ * - numPages needs to be different from 0,
+ * - _pad should be zero.
+ */
+
+#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES 16
+
+struct PVSCSICmdDescSetupMsgRing {
+ u32 numPages;
+ u32 _pad;
+ u64 ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES];
+} __packed;
+
+enum PVSCSIMsgType {
+ PVSCSI_MSG_DEV_ADDED = 0,
+ PVSCSI_MSG_DEV_REMOVED = 1,
+ PVSCSI_MSG_LAST = 2,
+};
+
+/*
+ * Msg descriptor.
+ *
+ * sizeof(struct PVSCSIRingMsgDesc) == 128.
+ *
+ * - type is of type enum PVSCSIMsgType.
+ * - the content of args depend on the type of event being delivered.
+ */
+
+struct PVSCSIRingMsgDesc {
+ u32 type;
+ u32 args[31];
+} __packed;
+
+struct PVSCSIMsgDescDevStatusChanged {
+ u32 type; /* PVSCSI_MSG_DEV _ADDED / _REMOVED */
+ u32 bus;
+ u32 target;
+ u8 lun[8];
+ u32 pad[27];
+} __packed;
+
+/*
+ * Rings state.
+ *
+ * - the fields:
+ * . msgProdIdx,
+ * . msgConsIdx,
+ * . msgNumEntriesLog2,
+ * .. are only used once the SETUP_MSG_RING cmd has been issued.
+ * - '_pad' helps to ensure that the msg related fields are on their own
+ * cache-line.
+ */
+
+struct PVSCSIRingsState {
+ u32 reqProdIdx;
+ u32 reqConsIdx;
+ u32 reqNumEntriesLog2;
+
+ u32 cmpProdIdx;
+ u32 cmpConsIdx;
+ u32 cmpNumEntriesLog2;
+
+ u8 _pad[104];
+
+ u32 msgProdIdx;
+ u32 msgConsIdx;
+ u32 msgNumEntriesLog2;
+} __packed;
+
+/*
+ * Request descriptor.
+ *
+ * sizeof(RingReqDesc) = 128
+ *
+ * - context: is a unique identifier of a command. It could normally be any
+ * 64bit value, however we currently store it in the serialNumber variable
+ * of struct SCSI_Command, so we have the following restrictions due to the
+ * way this field is handled in the vmkernel storage stack:
+ * * this value can't be 0,
+ * * the upper 32bit need to be 0 since serialNumber is as a u32.
+ * Currently tracked as PR 292060.
+ * - dataLen: contains the total number of bytes that need to be transferred.
+ * - dataAddr:
+ * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first
+ * s/g table segment, each s/g segment is entirely contained on a single
+ * page of physical memory,
+ * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of
+ * the buffer used for the DMA transfer,
+ * - flags:
+ * * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above,
+ * * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved,
+ * * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory,
+ * * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device,
+ * * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than
+ * 16bytes. To be specified.
+ * - vcpuHint: vcpuId of the processor that will be most likely waiting for the
+ * completion of the i/o. For guest OSes that use lowest priority message
+ * delivery mode (such as windows), we use this "hint" to deliver the
+ * completion action to the proper vcpu. For now, we can use the vcpuId of
+ * the processor that initiated the i/o as a likely candidate for the vcpu
+ * that will be waiting for the completion..
+ * - bus should be 0: we currently only support bus 0 for now.
+ * - unused should be zero'd.
+ */
+
+#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0)
+#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1)
+#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2)
+#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3)
+#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4)
+
+struct PVSCSIRingReqDesc {
+ u64 context;
+ u64 dataAddr;
+ u64 dataLen;
+ u64 senseAddr;
+ u32 senseLen;
+ u32 flags;
+ u8 cdb[16];
+ u8 cdbLen;
+ u8 lun[8];
+ u8 tag;
+ u8 bus;
+ u8 target;
+ u8 vcpuHint;
+ u8 unused[59];
+} __packed;
+
+/*
+ * Scatter-gather list management.
+ *
+ * As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the
+ * RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g
+ * table segment.
+ *
+ * - each segment of the s/g table contain a succession of struct
+ * PVSCSISGElement.
+ * - each segment is entirely contained on a single physical page of memory.
+ * - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in
+ * PVSCSISGElement.flags and in this case:
+ * * addr is the PA of the next s/g segment,
+ * * length is undefined, assumed to be 0.
+ */
+
+struct PVSCSISGElement {
+ u64 addr;
+ u32 length;
+ u32 flags;
+} __packed;
+
+/*
+ * Completion descriptor.
+ *
+ * sizeof(RingCmpDesc) = 32
+ *
+ * - context: identifier of the command. The same thing that was specified
+ * under "context" as part of struct RingReqDesc at initiation time,
+ * - dataLen: number of bytes transferred for the actual i/o operation,
+ * - senseLen: number of bytes written into the sense buffer,
+ * - hostStatus: adapter status,
+ * - scsiStatus: device status,
+ * - _pad should be zero.
+ */
+
+struct PVSCSIRingCmpDesc {
+ u64 context;
+ u64 dataLen;
+ u32 senseLen;
+ u16 hostStatus;
+ u16 scsiStatus;
+ u32 _pad[2];
+} __packed;
+
+/*
+ * Interrupt status / IRQ bits.
+ */
+
+#define PVSCSI_INTR_CMPL_0 (1 << 0)
+#define PVSCSI_INTR_CMPL_1 (1 << 1)
+#define PVSCSI_INTR_CMPL_MASK MASK(2)
+
+#define PVSCSI_INTR_MSG_0 (1 << 2)
+#define PVSCSI_INTR_MSG_1 (1 << 3)
+#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2)
+
+#define PVSCSI_INTR_ALL_SUPPORTED MASK(4)
+
+/*
+ * Number of MSI-X vectors supported.
+ */
+#define PVSCSI_MAX_INTRS 24
+
+/*
+ * Enumeration of supported MSI-X vectors
+ */
+#define PVSCSI_VECTOR_COMPLETION 0
+
+/*
+ * Misc constants for the rings.
+ */
+
+#define PVSCSI_MAX_NUM_PAGES_REQ_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_CMP_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_MSG_RING PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES
+
+#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \
+ (PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc))
+
+#define PVSCSI_MAX_REQ_QUEUE_DEPTH \
+ (PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE)
+
+#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES 1
+#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1
+#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES 2
+#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES 2
+#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES 2
+
+enum PVSCSIMemSpace {
+ PVSCSI_MEM_SPACE_COMMAND_PAGE = 0,
+ PVSCSI_MEM_SPACE_INTR_STATUS_PAGE = 1,
+ PVSCSI_MEM_SPACE_MISC_PAGE = 2,
+ PVSCSI_MEM_SPACE_KICK_IO_PAGE = 4,
+ PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE = 6,
+ PVSCSI_MEM_SPACE_MSIX_PBA_PAGE = 7,
+};
+
+#define PVSCSI_MEM_SPACE_NUM_PAGES \
+ (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_MISC_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_MSIX_NUM_PAGES)
+
+#define PVSCSI_MEM_SPACE_SIZE (PVSCSI_MEM_SPACE_NUM_PAGES * PAGE_SIZE)
+
+#endif /* _VMW_PVSCSI_H_ */
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index 093610bcfcc..2f6e9d8eaf7 100644
--- a/drivers/scsi/wd7000.c
+++ b/drivers/scsi/wd7000.c
@@ -161,7 +161,7 @@
*
* 2003/02/12 - Christoph Hellwig <hch@infradead.org>
*
- * Cleaned up host template defintion
+ * Cleaned up host template definition
* Removed now obsolete wd7000.h
*/
diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c
index d71dfe39894..36ede02ceac 100644
--- a/drivers/serial/8250_pnp.c
+++ b/drivers/serial/8250_pnp.c
@@ -361,9 +361,9 @@ static const struct pnp_device_id pnp_dev_table[] = {
{ "LTS0001", 0 },
/* Rockwell's (PORALiNK) 33600 INT PNP */
{ "WCI0003", 0 },
- /* Unkown PnP modems */
+ /* Unknown PnP modems */
{ "PNPCXXX", UNKNOWN_DEV },
- /* More unkown PnP modems */
+ /* More unknown PnP modems */
{ "PNPDXXX", UNKNOWN_DEV },
{ "", 0 }
};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index e5225725727..9ff47db0b2c 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -996,7 +996,7 @@ config SERIAL_IP22_ZILOG_CONSOLE
config SERIAL_SH_SCI
tristate "SuperH SCI(F) serial port support"
- depends on SUPERH || H8300
+ depends on HAVE_CLK && (SUPERH || H8300)
select SERIAL_CORE
config SERIAL_SH_SCI_NR_UARTS
@@ -1477,4 +1477,17 @@ config SERIAL_BCM63XX_CONSOLE
If you have enabled the serial port on the bcm63xx CPU
you can make it the console by answering Y to this option.
+config SERIAL_GRLIB_GAISLER_APBUART
+ tristate "GRLIB APBUART serial support"
+ depends on OF
+ ---help---
+ Add support for the GRLIB APBUART serial port.
+
+config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE
+ bool "Console on GRLIB APBUART serial port"
+ depends on SERIAL_GRLIB_GAISLER_APBUART=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Support for running a console on the GRLIB APBUART
+
endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index d21d5dd5d04..5548fe7df61 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -81,3 +81,4 @@ obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
+obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c
new file mode 100644
index 00000000000..fe91319b5f6
--- /dev/null
+++ b/drivers/serial/apbuart.c
@@ -0,0 +1,710 @@
+/*
+ * Driver for GRLIB serial ports (APBUART)
+ *
+ * Based on linux/drivers/serial/amba.c
+ *
+ * Copyright (C) 2000 Deep Blue Solutions Ltd.
+ * Copyright (C) 2003 Konrad Eisele <eiselekd@web.de>
+ * Copyright (C) 2006 Daniel Hellstrom <daniel@gaisler.com>, Aeroflex Gaisler AB
+ * Copyright (C) 2008 Gilead Kutnick <kutnickg@zin-tech.com>
+ * Copyright (C) 2009 Kristoffer Glembo <kristoffer@gaisler.com>, Aeroflex Gaisler AB
+ */
+
+#if defined(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/serial_core.h>
+#include <asm/irq.h>
+
+#include "apbuart.h"
+
+#define SERIAL_APBUART_MAJOR TTY_MAJOR
+#define SERIAL_APBUART_MINOR 64
+#define UART_DUMMY_RSR_RX 0x8000 /* for ignore all read */
+
+static void apbuart_tx_chars(struct uart_port *port);
+
+static void apbuart_stop_tx(struct uart_port *port)
+{
+ unsigned int cr;
+
+ cr = UART_GET_CTRL(port);
+ cr &= ~UART_CTRL_TI;
+ UART_PUT_CTRL(port, cr);
+}
+
+static void apbuart_start_tx(struct uart_port *port)
+{
+ unsigned int cr;
+
+ cr = UART_GET_CTRL(port);
+ cr |= UART_CTRL_TI;
+ UART_PUT_CTRL(port, cr);
+
+ if (UART_GET_STATUS(port) & UART_STATUS_THE)
+ apbuart_tx_chars(port);
+}
+
+static void apbuart_stop_rx(struct uart_port *port)
+{
+ unsigned int cr;
+
+ cr = UART_GET_CTRL(port);
+ cr &= ~(UART_CTRL_RI);
+ UART_PUT_CTRL(port, cr);
+}
+
+static void apbuart_enable_ms(struct uart_port *port)
+{
+ /* No modem status change interrupts for APBUART */
+}
+
+static void apbuart_rx_chars(struct uart_port *port)
+{
+ struct tty_struct *tty = port->state->port.tty;
+ unsigned int status, ch, rsr, flag;
+ unsigned int max_chars = port->fifosize;
+
+ status = UART_GET_STATUS(port);
+
+ while (UART_RX_DATA(status) && (max_chars--)) {
+
+ ch = UART_GET_CHAR(port);
+ flag = TTY_NORMAL;
+
+ port->icount.rx++;
+
+ rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX;
+ UART_PUT_STATUS(port, 0);
+ if (rsr & UART_STATUS_ERR) {
+
+ if (rsr & UART_STATUS_BR) {
+ rsr &= ~(UART_STATUS_FE | UART_STATUS_PE);
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ goto ignore_char;
+ } else if (rsr & UART_STATUS_PE) {
+ port->icount.parity++;
+ } else if (rsr & UART_STATUS_FE) {
+ port->icount.frame++;
+ }
+ if (rsr & UART_STATUS_OE)
+ port->icount.overrun++;
+
+ rsr &= port->read_status_mask;
+
+ if (rsr & UART_STATUS_PE)
+ flag = TTY_PARITY;
+ else if (rsr & UART_STATUS_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(port, ch))
+ goto ignore_char;
+
+ uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag);
+
+
+ ignore_char:
+ status = UART_GET_STATUS(port);
+ }
+
+ tty_flip_buffer_push(tty);
+}
+
+static void apbuart_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ int count;
+
+ if (port->x_char) {
+ UART_PUT_CHAR(port, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ apbuart_stop_tx(port);
+ return;
+ }
+
+ /* amba: fill FIFO */
+ count = port->fifosize >> 1;
+ do {
+ UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ apbuart_stop_tx(port);
+}
+
+static irqreturn_t apbuart_int(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ unsigned int status;
+
+ spin_lock(&port->lock);
+
+ status = UART_GET_STATUS(port);
+ if (status & UART_STATUS_DR)
+ apbuart_rx_chars(port);
+ if (status & UART_STATUS_THE)
+ apbuart_tx_chars(port);
+
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int apbuart_tx_empty(struct uart_port *port)
+{
+ unsigned int status = UART_GET_STATUS(port);
+ return status & UART_STATUS_THE ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int apbuart_get_mctrl(struct uart_port *port)
+{
+ /* The GRLIB APBUART handles flow control in hardware */
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* The GRLIB APBUART handles flow control in hardware */
+}
+
+static void apbuart_break_ctl(struct uart_port *port, int break_state)
+{
+ /* We don't support sending break */
+}
+
+static int apbuart_startup(struct uart_port *port)
+{
+ int retval;
+ unsigned int cr;
+
+ /* Allocate the IRQ */
+ retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port);
+ if (retval)
+ return retval;
+
+ /* Finally, enable interrupts */
+ cr = UART_GET_CTRL(port);
+ UART_PUT_CTRL(port,
+ cr | UART_CTRL_RE | UART_CTRL_TE |
+ UART_CTRL_RI | UART_CTRL_TI);
+
+ return 0;
+}
+
+static void apbuart_shutdown(struct uart_port *port)
+{
+ unsigned int cr;
+
+ /* disable all interrupts, disable the port */
+ cr = UART_GET_CTRL(port);
+ UART_PUT_CTRL(port,
+ cr & ~(UART_CTRL_RE | UART_CTRL_TE |
+ UART_CTRL_RI | UART_CTRL_TI));
+
+ /* Free the interrupt */
+ free_irq(port->irq, port);
+}
+
+static void apbuart_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned int cr;
+ unsigned long flags;
+ unsigned int baud, quot;
+
+ /* Ask the core to calculate the divisor for us. */
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+ if (baud == 0)
+ panic("invalid baudrate %i\n", port->uartclk / 16);
+
+ /* uart_get_divisor calc a *16 uart freq, apbuart is *8 */
+ quot = (uart_get_divisor(port, baud)) * 2;
+ cr = UART_GET_CTRL(port);
+ cr &= ~(UART_CTRL_PE | UART_CTRL_PS);
+
+ if (termios->c_cflag & PARENB) {
+ cr |= UART_CTRL_PE;
+ if ((termios->c_cflag & PARODD))
+ cr |= UART_CTRL_PS;
+ }
+
+ /* Enable flow control. */
+ if (termios->c_cflag & CRTSCTS)
+ cr |= UART_CTRL_FL;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* Update the per-port timeout. */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ port->read_status_mask = UART_STATUS_OE;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
+
+ /* Characters to ignore */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
+
+ /* Ignore all characters if CREAD is not set. */
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= UART_DUMMY_RSR_RX;
+
+ /* Set baud rate */
+ quot -= 1;
+ UART_PUT_SCAL(port, quot);
+ UART_PUT_CTRL(port, cr);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *apbuart_type(struct uart_port *port)
+{
+ return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL;
+}
+
+static void apbuart_release_port(struct uart_port *port)
+{
+ release_mem_region(port->mapbase, 0x100);
+}
+
+static int apbuart_request_port(struct uart_port *port)
+{
+ return request_mem_region(port->mapbase, 0x100, "grlib-apbuart")
+ != NULL ? 0 : -EBUSY;
+ return 0;
+}
+
+/* Configure/autoconfigure the port */
+static void apbuart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_APBUART;
+ apbuart_request_port(port);
+ }
+}
+
+/* Verify the new serial_struct (for TIOCSSERIAL) */
+static int apbuart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ int ret = 0;
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART)
+ ret = -EINVAL;
+ if (ser->irq < 0 || ser->irq >= NR_IRQS)
+ ret = -EINVAL;
+ if (ser->baud_base < 9600)
+ ret = -EINVAL;
+ return ret;
+}
+
+static struct uart_ops grlib_apbuart_ops = {
+ .tx_empty = apbuart_tx_empty,
+ .set_mctrl = apbuart_set_mctrl,
+ .get_mctrl = apbuart_get_mctrl,
+ .stop_tx = apbuart_stop_tx,
+ .start_tx = apbuart_start_tx,
+ .stop_rx = apbuart_stop_rx,
+ .enable_ms = apbuart_enable_ms,
+ .break_ctl = apbuart_break_ctl,
+ .startup = apbuart_startup,
+ .shutdown = apbuart_shutdown,
+ .set_termios = apbuart_set_termios,
+ .type = apbuart_type,
+ .release_port = apbuart_release_port,
+ .request_port = apbuart_request_port,
+ .config_port = apbuart_config_port,
+ .verify_port = apbuart_verify_port,
+};
+
+static struct uart_port grlib_apbuart_ports[UART_NR];
+static struct device_node *grlib_apbuart_nodes[UART_NR];
+
+static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber)
+{
+ int ctrl, loop = 0;
+ int status;
+ int fifosize;
+ unsigned long flags;
+
+ ctrl = UART_GET_CTRL(port);
+
+ /*
+ * Enable the transceiver and wait for it to be ready to send data.
+ * Clear interrupts so that this process will not be externally
+ * interrupted in the middle (which can cause the transceiver to
+ * drain prematurely).
+ */
+
+ local_irq_save(flags);
+
+ UART_PUT_CTRL(port, ctrl | UART_CTRL_TE);
+
+ while (!UART_TX_READY(UART_GET_STATUS(port)))
+ loop++;
+
+ /*
+ * Disable the transceiver so data isn't actually sent during the
+ * actual test.
+ */
+
+ UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE));
+
+ fifosize = 1;
+ UART_PUT_CHAR(port, 0);
+
+ /*
+ * So long as transmitting a character increments the tranceivier FIFO
+ * length the FIFO must be at least that big. These bytes will
+ * automatically drain off of the FIFO.
+ */
+
+ status = UART_GET_STATUS(port);
+ while (((status >> 20) & 0x3F) == fifosize) {
+ fifosize++;
+ UART_PUT_CHAR(port, 0);
+ status = UART_GET_STATUS(port);
+ }
+
+ fifosize--;
+
+ UART_PUT_CTRL(port, ctrl);
+ local_irq_restore(flags);
+
+ if (fifosize == 0)
+ fifosize = 1;
+
+ return fifosize;
+}
+
+static void apbuart_flush_fifo(struct uart_port *port)
+{
+ int i;
+
+ for (i = 0; i < port->fifosize; i++)
+ UART_GET_CHAR(port);
+}
+
+
+/* ======================================================================== */
+/* Console driver, if enabled */
+/* ======================================================================== */
+
+#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE
+
+static void apbuart_console_putchar(struct uart_port *port, int ch)
+{
+ unsigned int status;
+ do {
+ status = UART_GET_STATUS(port);
+ } while (!UART_TX_READY(status));
+ UART_PUT_CHAR(port, ch);
+}
+
+static void
+apbuart_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct uart_port *port = &grlib_apbuart_ports[co->index];
+ unsigned int status, old_cr, new_cr;
+
+ /* First save the CR then disable the interrupts */
+ old_cr = UART_GET_CTRL(port);
+ new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI);
+ UART_PUT_CTRL(port, new_cr);
+
+ uart_console_write(port, s, count, apbuart_console_putchar);
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the TCR
+ */
+ do {
+ status = UART_GET_STATUS(port);
+ } while (!UART_TX_READY(status));
+ UART_PUT_CTRL(port, old_cr);
+}
+
+static void __init
+apbuart_console_get_options(struct uart_port *port, int *baud,
+ int *parity, int *bits)
+{
+ if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) {
+
+ unsigned int quot, status;
+ status = UART_GET_STATUS(port);
+
+ *parity = 'n';
+ if (status & UART_CTRL_PE) {
+ if ((status & UART_CTRL_PS) == 0)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+
+ *bits = 8;
+ quot = UART_GET_SCAL(port) / 8;
+ *baud = port->uartclk / (16 * (quot + 1));
+ }
+}
+
+static int __init apbuart_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ int baud = 38400;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n",
+ co, co->index, options);
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index >= grlib_apbuart_port_nr)
+ co->index = 0;
+
+ port = &grlib_apbuart_ports[co->index];
+
+ spin_lock_init(&port->lock);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ apbuart_console_get_options(port, &baud, &parity, &bits);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver grlib_apbuart_driver;
+
+static struct console grlib_apbuart_console = {
+ .name = "ttyS",
+ .write = apbuart_console_write,
+ .device = uart_console_device,
+ .setup = apbuart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &grlib_apbuart_driver,
+};
+
+
+static void grlib_apbuart_configure(void);
+
+static int __init apbuart_console_init(void)
+{
+ grlib_apbuart_configure();
+ register_console(&grlib_apbuart_console);
+ return 0;
+}
+
+console_initcall(apbuart_console_init);
+
+#define APBUART_CONSOLE (&grlib_apbuart_console)
+#else
+#define APBUART_CONSOLE NULL
+#endif
+
+static struct uart_driver grlib_apbuart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "serial",
+ .dev_name = "ttyS",
+ .major = SERIAL_APBUART_MAJOR,
+ .minor = SERIAL_APBUART_MINOR,
+ .nr = UART_NR,
+ .cons = APBUART_CONSOLE,
+};
+
+
+/* ======================================================================== */
+/* OF Platform Driver */
+/* ======================================================================== */
+
+static int __devinit apbuart_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ int i = -1;
+ struct uart_port *port = NULL;
+
+ i = 0;
+ for (i = 0; i < grlib_apbuart_port_nr; i++) {
+ if (op->node == grlib_apbuart_nodes[i])
+ break;
+ }
+
+ port = &grlib_apbuart_ports[i];
+ port->dev = &op->dev;
+
+ uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port);
+
+ apbuart_flush_fifo((struct uart_port *) port);
+
+ printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n",
+ (unsigned long long) port->mapbase, port->irq);
+ return 0;
+
+}
+
+static struct of_device_id __initdata apbuart_match[] = {
+ {
+ .name = "GAISLER_APBUART",
+ },
+ {},
+};
+
+static struct of_platform_driver grlib_apbuart_of_driver = {
+ .match_table = apbuart_match,
+ .probe = apbuart_probe,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "grlib-apbuart",
+ },
+};
+
+
+static void grlib_apbuart_configure(void)
+{
+ static int enum_done;
+ struct device_node *np, *rp;
+ struct uart_port *port = NULL;
+ const u32 *prop;
+ int freq_khz;
+ int v = 0, d = 0;
+ unsigned int addr;
+ int irq, line;
+ struct amba_prom_registers *regs;
+
+ if (enum_done)
+ return;
+
+ /* Get bus frequency */
+ rp = of_find_node_by_path("/");
+ rp = of_get_next_child(rp, NULL);
+ prop = of_get_property(rp, "clock-frequency", NULL);
+ freq_khz = *prop;
+
+ line = 0;
+ for_each_matching_node(np, apbuart_match) {
+
+ int *vendor = (int *) of_get_property(np, "vendor", NULL);
+ int *device = (int *) of_get_property(np, "device", NULL);
+ int *irqs = (int *) of_get_property(np, "interrupts", NULL);
+ regs = (struct amba_prom_registers *)
+ of_get_property(np, "reg", NULL);
+
+ if (vendor)
+ v = *vendor;
+ if (device)
+ d = *device;
+
+ if (!irqs || !regs)
+ return;
+
+ grlib_apbuart_nodes[line] = np;
+
+ addr = regs->phys_addr;
+ irq = *irqs;
+
+ port = &grlib_apbuart_ports[line];
+
+ port->mapbase = addr;
+ port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map));
+ port->irq = irq;
+ port->iotype = UPIO_MEM;
+ port->ops = &grlib_apbuart_ops;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->line = line;
+ port->uartclk = freq_khz * 1000;
+ port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line);
+ line++;
+
+ /* We support maximum UART_NR uarts ... */
+ if (line == UART_NR)
+ break;
+
+ }
+
+ enum_done = 1;
+
+ grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line;
+}
+
+static int __init grlib_apbuart_init(void)
+{
+ int ret;
+
+ /* Find all APBUARTS in device the tree and initialize their ports */
+ grlib_apbuart_configure();
+
+ printk(KERN_INFO "Serial: GRLIB APBUART driver\n");
+
+ ret = uart_register_driver(&grlib_apbuart_driver);
+
+ if (ret) {
+ printk(KERN_ERR "%s: uart_register_driver failed (%i)\n",
+ __FILE__, ret);
+ return ret;
+ }
+
+ ret = of_register_platform_driver(&grlib_apbuart_of_driver);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: of_register_platform_driver failed (%i)\n",
+ __FILE__, ret);
+ uart_unregister_driver(&grlib_apbuart_driver);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit grlib_apbuart_exit(void)
+{
+ int i;
+
+ for (i = 0; i < grlib_apbuart_port_nr; i++)
+ uart_remove_one_port(&grlib_apbuart_driver,
+ &grlib_apbuart_ports[i]);
+
+ uart_unregister_driver(&grlib_apbuart_driver);
+ of_unregister_platform_driver(&grlib_apbuart_of_driver);
+}
+
+module_init(grlib_apbuart_init);
+module_exit(grlib_apbuart_exit);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB");
+MODULE_DESCRIPTION("GRLIB APBUART serial driver");
+MODULE_VERSION("2.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/apbuart.h b/drivers/serial/apbuart.h
new file mode 100644
index 00000000000..5faf87c8d2b
--- /dev/null
+++ b/drivers/serial/apbuart.h
@@ -0,0 +1,64 @@
+#ifndef __GRLIB_APBUART_H__
+#define __GRLIB_APBUART_H__
+
+#include <asm/io.h>
+
+#define UART_NR 8
+static int grlib_apbuart_port_nr;
+
+struct grlib_apbuart_regs_map {
+ u32 data;
+ u32 status;
+ u32 ctrl;
+ u32 scaler;
+};
+
+struct amba_prom_registers {
+ unsigned int phys_addr;
+ unsigned int reg_size;
+};
+
+/*
+ * The following defines the bits in the APBUART Status Registers.
+ */
+#define UART_STATUS_DR 0x00000001 /* Data Ready */
+#define UART_STATUS_TSE 0x00000002 /* TX Send Register Empty */
+#define UART_STATUS_THE 0x00000004 /* TX Hold Register Empty */
+#define UART_STATUS_BR 0x00000008 /* Break Error */
+#define UART_STATUS_OE 0x00000010 /* RX Overrun Error */
+#define UART_STATUS_PE 0x00000020 /* RX Parity Error */
+#define UART_STATUS_FE 0x00000040 /* RX Framing Error */
+#define UART_STATUS_ERR 0x00000078 /* Error Mask */
+
+/*
+ * The following defines the bits in the APBUART Ctrl Registers.
+ */
+#define UART_CTRL_RE 0x00000001 /* Receiver enable */
+#define UART_CTRL_TE 0x00000002 /* Transmitter enable */
+#define UART_CTRL_RI 0x00000004 /* Receiver interrupt enable */
+#define UART_CTRL_TI 0x00000008 /* Transmitter irq */
+#define UART_CTRL_PS 0x00000010 /* Parity select */
+#define UART_CTRL_PE 0x00000020 /* Parity enable */
+#define UART_CTRL_FL 0x00000040 /* Flow control enable */
+#define UART_CTRL_LB 0x00000080 /* Loopback enable */
+
+#define APBBASE(port) ((struct grlib_apbuart_regs_map *)((port)->membase))
+
+#define APBBASE_DATA_P(port) (&(APBBASE(port)->data))
+#define APBBASE_STATUS_P(port) (&(APBBASE(port)->status))
+#define APBBASE_CTRL_P(port) (&(APBBASE(port)->ctrl))
+#define APBBASE_SCALAR_P(port) (&(APBBASE(port)->scaler))
+
+#define UART_GET_CHAR(port) (__raw_readl(APBBASE_DATA_P(port)))
+#define UART_PUT_CHAR(port, v) (__raw_writel(v, APBBASE_DATA_P(port)))
+#define UART_GET_STATUS(port) (__raw_readl(APBBASE_STATUS_P(port)))
+#define UART_PUT_STATUS(port, v)(__raw_writel(v, APBBASE_STATUS_P(port)))
+#define UART_GET_CTRL(port) (__raw_readl(APBBASE_CTRL_P(port)))
+#define UART_PUT_CTRL(port, v) (__raw_writel(v, APBBASE_CTRL_P(port)))
+#define UART_GET_SCAL(port) (__raw_readl(APBBASE_SCALAR_P(port)))
+#define UART_PUT_SCAL(port, v) (__raw_writel(v, APBBASE_SCALAR_P(port)))
+
+#define UART_RX_DATA(s) (((s) & UART_STATUS_DR) != 0)
+#define UART_TX_READY(s) (((s) & UART_STATUS_THE) != 0)
+
+#endif /* __GRLIB_APBUART_H__ */
diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c
index b44382442bf..7bb5fee639e 100644
--- a/drivers/serial/mcf.c
+++ b/drivers/serial/mcf.c
@@ -602,7 +602,7 @@ static int __devinit mcf_probe(struct platform_device *pdev)
/****************************************************************************/
-static int mcf_remove(struct platform_device *pdev)
+static int __devexit mcf_remove(struct platform_device *pdev)
{
struct uart_port *port;
int i;
diff --git a/drivers/serial/pmac_zilog.h b/drivers/serial/pmac_zilog.h
index 570b0d925e8..f6e77f12acd 100644
--- a/drivers/serial/pmac_zilog.h
+++ b/drivers/serial/pmac_zilog.h
@@ -73,7 +73,7 @@ static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap)
}
/*
- * Register acessors. Note that we don't need to enforce a recovery
+ * Register accessors. Note that we don't need to enforce a recovery
* delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip,
* though if we try to use this driver on older machines, we might have
* to add it back
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
index c99f0821cae..73f089d3efd 100644
--- a/drivers/serial/s3c2410.c
+++ b/drivers/serial/s3c2410.c
@@ -2,7 +2,7 @@
*
* Driver for Samsung S3C2410 SoC onboard UARTs.
*
- * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c
index 6e057d8809d..ce75e28e36e 100644
--- a/drivers/serial/s3c2412.c
+++ b/drivers/serial/s3c2412.c
@@ -2,7 +2,7 @@
*
* Driver for Samsung S3C2412 and S3C2413 SoC onboard UARTs.
*
- * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/serial/s3c2440.c b/drivers/serial/s3c2440.c
index 69ff5d340f0..094cc3904b1 100644
--- a/drivers/serial/s3c2440.c
+++ b/drivers/serial/s3c2440.c
@@ -2,7 +2,7 @@
*
* Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs.
*
- * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c
index 26c49e18bdd..fad6083ca42 100644
--- a/drivers/serial/s3c24a0.c
+++ b/drivers/serial/s3c24a0.c
@@ -6,7 +6,7 @@
*
* Author: Sandeep Patil <sandeep.patil@azingo.com>
*
- * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
index 1523e8d9ae7..52e3df113ec 100644
--- a/drivers/serial/samsung.c
+++ b/drivers/serial/samsung.c
@@ -2,7 +2,7 @@
*
* Driver core for Samsung SoC onboard UARTs.
*
- * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h
index d3fe315969f..1fb22343df4 100644
--- a/drivers/serial/samsung.h
+++ b/drivers/serial/samsung.h
@@ -2,7 +2,7 @@
*
* Driver for Samsung SoC onboard UARTs.
*
- * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c
index 7c7914f5fa0..fc413f0f8dd 100644
--- a/drivers/serial/serial_cs.c
+++ b/drivers/serial/serial_cs.c
@@ -54,14 +54,6 @@
#include "8250.h"
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = "serial_cs.c 1.134 2002/05/04 05:48:53 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -121,24 +113,20 @@ static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_
static int quirk_post_ibm(struct pcmcia_device *link)
{
conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
- int last_ret, last_fn;
+ int ret;
+
+ ret = pcmcia_access_configuration_register(link, &reg);
+ if (ret)
+ goto failed;
- last_ret = pcmcia_access_configuration_register(link, &reg);
- if (last_ret) {
- last_fn = AccessConfigurationRegister;
- goto cs_failed;
- }
reg.Action = CS_WRITE;
reg.Value = reg.Value | 1;
- last_ret = pcmcia_access_configuration_register(link, &reg);
- if (last_ret) {
- last_fn = AccessConfigurationRegister;
- goto cs_failed;
- }
+ ret = pcmcia_access_configuration_register(link, &reg);
+ if (ret)
+ goto failed;
return 0;
- cs_failed:
- cs_error(link, last_fn, last_ret);
+ failed:
return -ENODEV;
}
@@ -283,7 +271,7 @@ static void serial_remove(struct pcmcia_device *link)
struct serial_info *info = link->priv;
int i;
- DEBUG(0, "serial_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "serial_release\n");
/*
* Recheck to see if the device is still configured.
@@ -334,7 +322,7 @@ static int serial_probe(struct pcmcia_device *link)
{
struct serial_info *info;
- DEBUG(0, "serial_attach()\n");
+ dev_dbg(&link->dev, "serial_attach()\n");
/* Create new serial device */
info = kzalloc(sizeof (*info), GFP_KERNEL);
@@ -346,7 +334,6 @@ static int serial_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts1 = 8;
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
if (do_sound) {
link->conf.Attributes |= CONF_ENABLE_SPKR;
@@ -370,7 +357,7 @@ static void serial_detach(struct pcmcia_device *link)
{
struct serial_info *info = link->priv;
- DEBUG(0, "serial_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "serial_detach\n");
/*
* Ensure any outstanding scheduled tasks are completed.
@@ -399,7 +386,7 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
port.irq = irq;
port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
port.uartclk = 1843200;
- port.dev = &handle_to_dev(handle);
+ port.dev = &handle->dev;
if (buggy_uart)
port.flags |= UPF_BUGGY_UART;
@@ -426,21 +413,6 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
/*====================================================================*/
-static int
-first_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse)
-{
- int i;
- i = pcmcia_get_first_tuple(handle, tuple);
- if (i != 0)
- return i;
- i = pcmcia_get_tuple_data(handle, tuple);
- if (i != 0)
- return i;
- return pcmcia_parse_tuple(tuple, parse);
-}
-
-/*====================================================================*/
-
static int simple_config_check(struct pcmcia_device *p_dev,
cistpl_cftable_entry_t *cf,
cistpl_cftable_entry_t *dflt,
@@ -522,15 +494,13 @@ static int simple_config(struct pcmcia_device *link)
printk(KERN_NOTICE
"serial_cs: no usable port range found, giving up\n");
- cs_error(link, RequestIO, i);
return -1;
found_port:
i = pcmcia_request_irq(link, &link->irq);
- if (i != 0) {
- cs_error(link, RequestIRQ, i);
+ if (i != 0)
link->irq.AssignedIRQ = 0;
- }
+
if (info->multi && (info->manfid == MANFID_3COM))
link->conf.ConfigIndex &= ~(0x08);
@@ -541,10 +511,8 @@ found_port:
info->quirk->config(link);
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- cs_error(link, RequestConfiguration, i);
+ if (i != 0)
return -1;
- }
return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ);
}
@@ -613,7 +581,6 @@ static int multi_config(struct pcmcia_device *link)
/* FIXME: comment does not fit, error handling does not fit */
printk(KERN_NOTICE
"serial_cs: no usable port range found, giving up\n");
- cs_error(link, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
@@ -624,10 +591,8 @@ static int multi_config(struct pcmcia_device *link)
info->quirk->config(link);
i = pcmcia_request_configuration(link, &link->conf);
- if (i != 0) {
- cs_error(link, RequestConfiguration, i);
+ if (i != 0)
return -ENODEV;
- }
/* The Oxford Semiconductor OXCF950 cards are in fact single-port:
* 8 registers are for the UART, the others are extra registers.
@@ -665,6 +630,25 @@ static int multi_config(struct pcmcia_device *link)
return 0;
}
+static int serial_check_for_multi(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ struct serial_info *info = p_dev->priv;
+
+ if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0))
+ info->multi = cf->io.win[0].len >> 3;
+
+ if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) &&
+ (cf->io.win[1].len == 8))
+ info->multi = 2;
+
+ return 0; /* break */
+}
+
+
/*======================================================================
serial_config() is scheduled to run after a CARD_INSERTION event
@@ -676,46 +660,14 @@ static int multi_config(struct pcmcia_device *link)
static int serial_config(struct pcmcia_device * link)
{
struct serial_info *info = link->priv;
- struct serial_cfg_mem *cfg_mem;
- tuple_t *tuple;
- u_char *buf;
- cisparse_t *parse;
- cistpl_cftable_entry_t *cf;
- int i, last_ret, last_fn;
-
- DEBUG(0, "serial_config(0x%p)\n", link);
-
- cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- goto failed;
+ int i;
- tuple = &cfg_mem->tuple;
- parse = &cfg_mem->parse;
- cf = &parse->cftable_entry;
- buf = cfg_mem->buf;
-
- tuple->TupleData = (cisdata_t *) buf;
- tuple->TupleOffset = 0;
- tuple->TupleDataMax = 255;
- tuple->Attributes = 0;
-
- /* Get configuration register information */
- tuple->DesiredTuple = CISTPL_CONFIG;
- last_ret = first_tuple(link, tuple, parse);
- if (last_ret != 0) {
- last_fn = ParseTuple;
- goto cs_failed;
- }
- link->conf.ConfigBase = parse->config.base;
- link->conf.Present = parse->config.rmask[0];
+ dev_dbg(&link->dev, "serial_config\n");
/* Is this a compliant multifunction card? */
- tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
- tuple->Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
- info->multi = (first_tuple(link, tuple, parse) == 0);
+ info->multi = (link->socket->functions > 1);
/* Is this a multiport card? */
- tuple->DesiredTuple = CISTPL_MANFID;
info->manfid = link->manf_id;
info->prodid = link->card_id;
@@ -730,20 +682,11 @@ static int serial_config(struct pcmcia_device * link)
/* Another check for dual-serial cards: look for either serial or
multifunction cards that ask for appropriate IO port ranges */
- tuple->DesiredTuple = CISTPL_FUNCID;
if ((info->multi == 0) &&
(link->has_func_id) &&
((link->func_id == CISTPL_FUNCID_MULTI) ||
- (link->func_id == CISTPL_FUNCID_SERIAL))) {
- tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
- if (first_tuple(link, tuple, parse) == 0) {
- if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0))
- info->multi = cf->io.win[0].len >> 3;
- if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) &&
- (cf->io.win[1].len == 8))
- info->multi = 2;
- }
- }
+ (link->func_id == CISTPL_FUNCID_SERIAL)))
+ pcmcia_loop_config(link, serial_check_for_multi, info);
/*
* Apply any multi-port quirk.
@@ -768,14 +711,10 @@ static int serial_config(struct pcmcia_device * link)
goto failed;
link->dev_node = &info->node[0];
- kfree(cfg_mem);
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
serial_remove(link);
- kfree(cfg_mem);
return -ENODEV;
}
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 6498bd1fb6d..ff38dbdb5c6 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -50,7 +50,6 @@
#include <linux/list.h>
#ifdef CONFIG_SUPERH
-#include <asm/clock.h>
#include <asm/sh_bios.h>
#endif
@@ -79,22 +78,18 @@ struct sci_port {
struct timer_list break_timer;
int break_flag;
-#ifdef CONFIG_HAVE_CLK
/* Interface clock */
struct clk *iclk;
/* Data clock */
struct clk *dclk;
-#endif
+
struct list_head node;
};
struct sh_sci_priv {
spinlock_t lock;
struct list_head ports;
-
-#ifdef CONFIG_HAVE_CLK
struct notifier_block clk_nb;
-#endif
};
/* Function prototypes */
@@ -156,32 +151,6 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
}
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
-#if defined(__H8300S__)
-enum { sci_disable, sci_enable };
-
-static void h8300_sci_config(struct uart_port *port, unsigned int ctrl)
-{
- volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL;
- int ch = (port->mapbase - SMR0) >> 3;
- unsigned char mask = 1 << (ch+1);
-
- if (ctrl == sci_disable)
- *mstpcrl |= mask;
- else
- *mstpcrl &= ~mask;
-}
-
-static void h8300_sci_enable(struct uart_port *port)
-{
- h8300_sci_config(port, sci_enable);
-}
-
-static void h8300_sci_disable(struct uart_port *port)
-{
- h8300_sci_config(port, sci_disable);
-}
-#endif
-
#if defined(__H8300H__) || defined(__H8300S__)
static void sci_init_pins(struct uart_port *port, unsigned int cflag)
{
@@ -733,7 +702,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
return ret;
}
-#ifdef CONFIG_HAVE_CLK
/*
* Here we define a transistion notifier so that we can update all of our
* ports' baud rate when the peripheral clock changes.
@@ -751,7 +719,6 @@ static int sci_notifier(struct notifier_block *self,
spin_lock_irqsave(&priv->lock, flags);
list_for_each_entry(sci_port, &priv->ports, node)
sci_port->port.uartclk = clk_get_rate(sci_port->dclk);
-
spin_unlock_irqrestore(&priv->lock, flags);
}
@@ -778,7 +745,6 @@ static void sci_clk_disable(struct uart_port *port)
clk_disable(sci_port->dclk);
}
-#endif
static int sci_request_irq(struct sci_port *port)
{
@@ -833,8 +799,8 @@ static void sci_free_irq(struct sci_port *port)
static unsigned int sci_tx_empty(struct uart_port *port)
{
- /* Can't detect */
- return TIOCSER_TEMT;
+ unsigned short status = sci_in(port, SCxSR);
+ return status & SCxSR_TEND(port) ? TIOCSER_TEMT : 0;
}
static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1077,21 +1043,10 @@ static void __devinit sci_init_single(struct platform_device *dev,
sci_port->port.iotype = UPIO_MEM;
sci_port->port.line = index;
sci_port->port.fifosize = 1;
-
-#if defined(__H8300H__) || defined(__H8300S__)
-#ifdef __H8300S__
- sci_port->enable = h8300_sci_enable;
- sci_port->disable = h8300_sci_disable;
-#endif
- sci_port->port.uartclk = CONFIG_CPU_CLOCK;
-#elif defined(CONFIG_HAVE_CLK)
sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL;
sci_port->dclk = clk_get(&dev->dev, "peripheral_clk");
sci_port->enable = sci_clk_enable;
sci_port->disable = sci_clk_disable;
-#else
-#error "Need a valid uartclk"
-#endif
sci_port->break_timer.data = (unsigned long)sci_port;
sci_port->break_timer.function = sci_break_timer;
@@ -1106,7 +1061,6 @@ static void __devinit sci_init_single(struct platform_device *dev,
sci_port->type = sci_port->port.type = p->type;
memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs));
-
}
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
@@ -1239,14 +1193,11 @@ static int sci_remove(struct platform_device *dev)
struct sci_port *p;
unsigned long flags;
-#ifdef CONFIG_HAVE_CLK
cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
spin_lock_irqsave(&priv->lock, flags);
list_for_each_entry(p, &priv->ports, node)
uart_remove_one_port(&sci_uart_driver, &p->port);
-
spin_unlock_irqrestore(&priv->lock, flags);
kfree(priv);
@@ -1307,10 +1258,8 @@ static int __devinit sci_probe(struct platform_device *dev)
spin_lock_init(&priv->lock);
platform_set_drvdata(dev, priv);
-#ifdef CONFIG_HAVE_CLK
priv->clk_nb.notifier_call = sci_notifier;
cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
if (dev->id != -1) {
ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]);
@@ -1370,7 +1319,7 @@ static struct dev_pm_ops sci_dev_pm_ops = {
static struct platform_driver sci_driver = {
.probe = sci_probe,
- .remove = __devexit_p(sci_remove),
+ .remove = sci_remove,
.driver = {
.name = "sh-sci",
.owner = THIS_MODULE,
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index 3e2fcf93b42..a32094eeb42 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -1,5 +1,5 @@
#include <linux/serial_core.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/gpio.h>
#if defined(CONFIG_H83007) || defined(CONFIG_H83068)
diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c
index 0c08f286a2e..46de564aaea 100644
--- a/drivers/serial/ucc_uart.c
+++ b/drivers/serial/ucc_uart.c
@@ -313,7 +313,7 @@ static void qe_uart_stop_tx(struct uart_port *port)
* This function will attempt to stuff of all the characters from the
* kernel's transmit buffer into TX BDs.
*
- * A return value of non-zero indicates that it sucessfully stuffed all
+ * A return value of non-zero indicates that it successfully stuffed all
* characters from the kernel buffer.
*
* A return value of zero indicates that there are still characters in the
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 6a025cefe6d..4956bf1f213 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -3,4 +3,5 @@
#
obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_MAPLE) += maple/
+obj-$(CONFIG_GENERIC_GPIO) += pfc.o
obj-y += intc.o
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index 559b5fe9dc0..a7e5c2e9986 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -2,6 +2,7 @@
* Shared interrupt handling code for IPR and INTC2 types of IRQs.
*
* Copyright (C) 2007, 2008 Magnus Damm
+ * Copyright (C) 2009 Paul Mundt
*
* Based on intc2.c and ipr.c
*
@@ -24,6 +25,7 @@
#include <linux/sysdev.h>
#include <linux/list.h>
#include <linux/topology.h>
+#include <linux/bitmap.h>
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
@@ -59,6 +61,20 @@ struct intc_desc_int {
static LIST_HEAD(intc_list);
+/*
+ * The intc_irq_map provides a global map of bound IRQ vectors for a
+ * given platform. Allocation of IRQs are either static through the CPU
+ * vector map, or dynamic in the case of board mux vectors or MSI.
+ *
+ * As this is a central point for all IRQ controllers on the system,
+ * each of the available sources are mapped out here. This combined with
+ * sparseirq makes it quite trivial to keep the vector map tightly packed
+ * when dynamically creating IRQs, as well as tying in to otherwise
+ * unused irq_desc positions in the sparse array.
+ */
+static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
+static DEFINE_SPINLOCK(vector_lock);
+
#ifdef CONFIG_SMP
#define IS_SMP(x) x.smp
#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c))
@@ -70,9 +86,7 @@ static LIST_HEAD(intc_list);
#endif
static unsigned int intc_prio_level[NR_IRQS]; /* for now */
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
static unsigned long ack_handle[NR_IRQS];
-#endif
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
{
@@ -250,7 +264,6 @@ static int intc_set_wake(unsigned int irq, unsigned int on)
return 0; /* allow wakeup, but setup hardware in intc_suspend() */
}
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
static void intc_mask_ack(unsigned int irq)
{
struct intc_desc_int *d = get_intc_desc(irq);
@@ -282,7 +295,6 @@ static void intc_mask_ack(unsigned int irq)
}
}
}
-#endif
static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
unsigned int nr_hp,
@@ -501,7 +513,6 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc,
return 0;
}
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
static unsigned int __init intc_ack_data(struct intc_desc *desc,
struct intc_desc_int *d,
intc_enum enum_id)
@@ -533,7 +544,6 @@ static unsigned int __init intc_ack_data(struct intc_desc *desc,
return 0;
}
-#endif
static unsigned int __init intc_sense_data(struct intc_desc *desc,
struct intc_desc_int *d,
@@ -572,6 +582,11 @@ static void __init intc_register_irq(struct intc_desc *desc,
struct intc_handle_int *hp;
unsigned int data[2], primary;
+ /*
+ * Register the IRQ position with the global IRQ map
+ */
+ set_bit(irq, intc_irq_map);
+
/* Prefer single interrupt source bitmap over other combinations:
* 1. bitmap, single interrupt source
* 2. priority, single interrupt source
@@ -641,10 +656,8 @@ static void __init intc_register_irq(struct intc_desc *desc,
/* irq should be disabled by default */
d->chip.mask(irq);
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
if (desc->ack_regs)
ack_handle[irq] = intc_ack_data(desc, d, enum_id);
-#endif
}
static unsigned int __init save_reg(struct intc_desc_int *d,
@@ -681,10 +694,8 @@ void __init register_intc_controller(struct intc_desc *desc)
d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0;
d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0;
d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0;
-
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0;
-#endif
+
d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
#ifdef CONFIG_SMP
d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
@@ -727,14 +738,12 @@ void __init register_intc_controller(struct intc_desc *desc)
d->chip.set_type = intc_set_sense;
d->chip.set_wake = intc_set_wake;
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
if (desc->ack_regs) {
for (i = 0; i < desc->nr_ack_regs; i++)
k += save_reg(d, k, desc->ack_regs[i].set_reg, 0);
d->chip.mask_ack = intc_mask_ack;
}
-#endif
BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
@@ -856,5 +865,91 @@ static int __init register_intc_sysdevs(void)
return error;
}
-
device_initcall(register_intc_sysdevs);
+
+/*
+ * Dynamic IRQ allocation and deallocation
+ */
+static unsigned int create_irq_on_node(unsigned int irq_want, int node)
+{
+ unsigned int irq = 0, new;
+ unsigned long flags;
+ struct irq_desc *desc;
+
+ spin_lock_irqsave(&vector_lock, flags);
+
+ /*
+ * First try the wanted IRQ, then scan.
+ */
+ if (test_and_set_bit(irq_want, intc_irq_map)) {
+ new = find_first_zero_bit(intc_irq_map, nr_irqs);
+ if (unlikely(new == nr_irqs))
+ goto out_unlock;
+
+ desc = irq_to_desc_alloc_node(new, node);
+ if (unlikely(!desc)) {
+ pr_info("can't get irq_desc for %d\n", new);
+ goto out_unlock;
+ }
+
+ desc = move_irq_desc(desc, node);
+ __set_bit(new, intc_irq_map);
+ irq = new;
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&vector_lock, flags);
+
+ if (irq > 0)
+ dynamic_irq_init(irq);
+
+ return irq;
+}
+
+int create_irq(void)
+{
+ int nid = cpu_to_node(smp_processor_id());
+ int irq;
+
+ irq = create_irq_on_node(NR_IRQS_LEGACY, nid);
+ if (irq == 0)
+ irq = -1;
+
+ return irq;
+}
+
+void destroy_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ dynamic_irq_cleanup(irq);
+
+ spin_lock_irqsave(&vector_lock, flags);
+ __clear_bit(irq, intc_irq_map);
+ spin_unlock_irqrestore(&vector_lock, flags);
+}
+
+int reserve_irq_vector(unsigned int irq)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vector_lock, flags);
+ if (test_and_set_bit(irq, intc_irq_map))
+ ret = -EBUSY;
+ spin_unlock_irqrestore(&vector_lock, flags);
+
+ return ret;
+}
+
+void reserve_irq_legacy(void)
+{
+ unsigned long flags;
+ int i, j;
+
+ spin_lock_irqsave(&vector_lock, flags);
+ j = find_first_bit(intc_irq_map, nr_irqs);
+ for (i = 0; i < j; i++)
+ __set_bit(i, intc_irq_map);
+ spin_unlock_irqrestore(&vector_lock, flags);
+}
diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
index 93c20e135ee..4e8f57d4131 100644
--- a/drivers/sh/maple/maple.c
+++ b/drivers/sh/maple/maple.c
@@ -106,7 +106,7 @@ static void maple_dma_reset(void)
* max delay is 11
*/
ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED);
- ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR);
+ ctrl_outl(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR);
ctrl_outl(1, MAPLE_ENABLE);
}
@@ -258,7 +258,7 @@ static void maple_build_block(struct mapleq *mq)
maple_lastptr = maple_sendptr;
*maple_sendptr++ = (port << 16) | len | 0x80000000;
- *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf);
+ *maple_sendptr++ = virt_to_phys(mq->recvbuf->buf);
*maple_sendptr++ =
mq->command | (to << 8) | (from << 16) | (len << 24);
while (len-- > 0)
diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c
new file mode 100644
index 00000000000..841ed5030c8
--- /dev/null
+++ b/drivers/sh/pfc.c
@@ -0,0 +1,577 @@
+/*
+ * Pinmuxed GPIO support for SuperH.
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+
+static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r)
+{
+ if (enum_id < r->begin)
+ return 0;
+
+ if (enum_id > r->end)
+ return 0;
+
+ return 1;
+}
+
+static unsigned long gpio_read_raw_reg(unsigned long reg,
+ unsigned long reg_width)
+{
+ switch (reg_width) {
+ case 8:
+ return __raw_readb(reg);
+ case 16:
+ return __raw_readw(reg);
+ case 32:
+ return __raw_readl(reg);
+ }
+
+ BUG();
+ return 0;
+}
+
+static void gpio_write_raw_reg(unsigned long reg,
+ unsigned long reg_width,
+ unsigned long data)
+{
+ switch (reg_width) {
+ case 8:
+ __raw_writeb(data, reg);
+ return;
+ case 16:
+ __raw_writew(data, reg);
+ return;
+ case 32:
+ __raw_writel(data, reg);
+ return;
+ }
+
+ BUG();
+}
+
+static void gpio_write_bit(struct pinmux_data_reg *dr,
+ unsigned long in_pos, unsigned long value)
+{
+ unsigned long pos;
+
+ pos = dr->reg_width - (in_pos + 1);
+
+ pr_debug("write_bit addr = %lx, value = %ld, pos = %ld, "
+ "r_width = %ld\n",
+ dr->reg, !!value, pos, dr->reg_width);
+
+ if (value)
+ set_bit(pos, &dr->reg_shadow);
+ else
+ clear_bit(pos, &dr->reg_shadow);
+
+ gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow);
+}
+
+static int gpio_read_reg(unsigned long reg, unsigned long reg_width,
+ unsigned long field_width, unsigned long in_pos)
+{
+ unsigned long data, mask, pos;
+
+ data = 0;
+ mask = (1 << field_width) - 1;
+ pos = reg_width - ((in_pos + 1) * field_width);
+
+ pr_debug("read_reg: addr = %lx, pos = %ld, "
+ "r_width = %ld, f_width = %ld\n",
+ reg, pos, reg_width, field_width);
+
+ data = gpio_read_raw_reg(reg, reg_width);
+ return (data >> pos) & mask;
+}
+
+static void gpio_write_reg(unsigned long reg, unsigned long reg_width,
+ unsigned long field_width, unsigned long in_pos,
+ unsigned long value)
+{
+ unsigned long mask, pos;
+
+ mask = (1 << field_width) - 1;
+ pos = reg_width - ((in_pos + 1) * field_width);
+
+ pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, "
+ "r_width = %ld, f_width = %ld\n",
+ reg, value, pos, reg_width, field_width);
+
+ mask = ~(mask << pos);
+ value = value << pos;
+
+ switch (reg_width) {
+ case 8:
+ __raw_writeb((__raw_readb(reg) & mask) | value, reg);
+ break;
+ case 16:
+ __raw_writew((__raw_readw(reg) & mask) | value, reg);
+ break;
+ case 32:
+ __raw_writel((__raw_readl(reg) & mask) | value, reg);
+ break;
+ }
+}
+
+static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
+ struct pinmux_data_reg *data_reg;
+ int k, n;
+
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
+ return -1;
+
+ k = 0;
+ while (1) {
+ data_reg = gpioc->data_regs + k;
+
+ if (!data_reg->reg_width)
+ break;
+
+ for (n = 0; n < data_reg->reg_width; n++) {
+ if (data_reg->enum_ids[n] == gpiop->enum_id) {
+ gpiop->flags &= ~PINMUX_FLAG_DREG;
+ gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT);
+ gpiop->flags &= ~PINMUX_FLAG_DBIT;
+ gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT);
+ return 0;
+ }
+ }
+ k++;
+ }
+
+ BUG();
+
+ return -1;
+}
+
+static void setup_data_regs(struct pinmux_info *gpioc)
+{
+ struct pinmux_data_reg *drp;
+ int k;
+
+ for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++)
+ setup_data_reg(gpioc, k);
+
+ k = 0;
+ while (1) {
+ drp = gpioc->data_regs + k;
+
+ if (!drp->reg_width)
+ break;
+
+ drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width);
+ k++;
+ }
+}
+
+static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio,
+ struct pinmux_data_reg **drp, int *bitp)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
+ int k, n;
+
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
+ return -1;
+
+ k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT;
+ n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT;
+ *drp = gpioc->data_regs + k;
+ *bitp = n;
+ return 0;
+}
+
+static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
+ struct pinmux_cfg_reg **crp, int *indexp,
+ unsigned long **cntp)
+{
+ struct pinmux_cfg_reg *config_reg;
+ unsigned long r_width, f_width;
+ int k, n;
+
+ k = 0;
+ while (1) {
+ config_reg = gpioc->cfg_regs + k;
+
+ r_width = config_reg->reg_width;
+ f_width = config_reg->field_width;
+
+ if (!r_width)
+ break;
+ for (n = 0; n < (r_width / f_width) * 1 << f_width; n++) {
+ if (config_reg->enum_ids[n] == enum_id) {
+ *crp = config_reg;
+ *indexp = n;
+ *cntp = &config_reg->cnt[n / (1 << f_width)];
+ return 0;
+ }
+ }
+ k++;
+ }
+
+ return -1;
+}
+
+static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio,
+ int pos, pinmux_enum_t *enum_idp)
+{
+ pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id;
+ pinmux_enum_t *data = gpioc->gpio_data;
+ int k;
+
+ if (!enum_in_range(enum_id, &gpioc->data)) {
+ if (!enum_in_range(enum_id, &gpioc->mark)) {
+ pr_err("non data/mark enum_id for gpio %d\n", gpio);
+ return -1;
+ }
+ }
+
+ if (pos) {
+ *enum_idp = data[pos + 1];
+ return pos + 1;
+ }
+
+ for (k = 0; k < gpioc->gpio_data_size; k++) {
+ if (data[k] == enum_id) {
+ *enum_idp = data[k + 1];
+ return k + 1;
+ }
+ }
+
+ pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio);
+ return -1;
+}
+
+static void write_config_reg(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ int index)
+{
+ unsigned long ncomb, pos, value;
+
+ ncomb = 1 << crp->field_width;
+ pos = index / ncomb;
+ value = index % ncomb;
+
+ gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value);
+}
+
+static int check_config_reg(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ int index)
+{
+ unsigned long ncomb, pos, value;
+
+ ncomb = 1 << crp->field_width;
+ pos = index / ncomb;
+ value = index % ncomb;
+
+ if (gpio_read_reg(crp->reg, crp->reg_width,
+ crp->field_width, pos) == value)
+ return 0;
+
+ return -1;
+}
+
+enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE };
+
+static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
+ int pinmux_type, int cfg_mode)
+{
+ struct pinmux_cfg_reg *cr = NULL;
+ pinmux_enum_t enum_id;
+ struct pinmux_range *range;
+ int in_range, pos, index;
+ unsigned long *cntp;
+
+ switch (pinmux_type) {
+
+ case PINMUX_TYPE_FUNCTION:
+ range = NULL;
+ break;
+
+ case PINMUX_TYPE_OUTPUT:
+ range = &gpioc->output;
+ break;
+
+ case PINMUX_TYPE_INPUT:
+ range = &gpioc->input;
+ break;
+
+ case PINMUX_TYPE_INPUT_PULLUP:
+ range = &gpioc->input_pu;
+ break;
+
+ case PINMUX_TYPE_INPUT_PULLDOWN:
+ range = &gpioc->input_pd;
+ break;
+
+ default:
+ goto out_err;
+ }
+
+ pos = 0;
+ enum_id = 0;
+ index = 0;
+ while (1) {
+ pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id);
+ if (pos <= 0)
+ goto out_err;
+
+ if (!enum_id)
+ break;
+
+ in_range = enum_in_range(enum_id, &gpioc->function);
+ if (!in_range && range) {
+ in_range = enum_in_range(enum_id, range);
+
+ if (in_range && enum_id == range->force)
+ continue;
+ }
+
+ if (!in_range)
+ continue;
+
+ if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0)
+ goto out_err;
+
+ switch (cfg_mode) {
+ case GPIO_CFG_DRYRUN:
+ if (!*cntp || !check_config_reg(gpioc, cr, index))
+ continue;
+ break;
+
+ case GPIO_CFG_REQ:
+ write_config_reg(gpioc, cr, index);
+ *cntp = *cntp + 1;
+ break;
+
+ case GPIO_CFG_FREE:
+ *cntp = *cntp - 1;
+ break;
+ }
+ }
+
+ return 0;
+ out_err:
+ return -1;
+}
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip)
+{
+ return container_of(chip, struct pinmux_info, chip);
+}
+
+static int sh_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ struct pinmux_data_reg *dummy;
+ unsigned long flags;
+ int i, ret, pinmux_type;
+
+ ret = -EINVAL;
+
+ if (!gpioc)
+ goto err_out;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE)
+ goto err_unlock;
+
+ /* setup pin function here if no data is associated with pin */
+
+ if (get_data_reg(gpioc, offset, &dummy, &i) != 0)
+ pinmux_type = PINMUX_TYPE_FUNCTION;
+ else
+ pinmux_type = PINMUX_TYPE_GPIO;
+
+ if (pinmux_type == PINMUX_TYPE_FUNCTION) {
+ if (pinmux_config_gpio(gpioc, offset,
+ pinmux_type,
+ GPIO_CFG_DRYRUN) != 0)
+ goto err_unlock;
+
+ if (pinmux_config_gpio(gpioc, offset,
+ pinmux_type,
+ GPIO_CFG_REQ) != 0)
+ BUG();
+ }
+
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= pinmux_type;
+
+ ret = 0;
+ err_unlock:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ err_out:
+ return ret;
+}
+
+static void sh_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ unsigned long flags;
+ int pinmux_type;
+
+ if (!gpioc)
+ return;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE;
+ pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE);
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static int pinmux_direction(struct pinmux_info *gpioc,
+ unsigned gpio, int new_pinmux_type)
+{
+ int pinmux_type;
+ int ret = -EINVAL;
+
+ if (!gpioc)
+ goto err_out;
+
+ pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE;
+
+ switch (pinmux_type) {
+ case PINMUX_TYPE_GPIO:
+ break;
+ case PINMUX_TYPE_OUTPUT:
+ case PINMUX_TYPE_INPUT:
+ case PINMUX_TYPE_INPUT_PULLUP:
+ case PINMUX_TYPE_INPUT_PULLDOWN:
+ pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE);
+ break;
+ default:
+ goto err_out;
+ }
+
+ if (pinmux_config_gpio(gpioc, gpio,
+ new_pinmux_type,
+ GPIO_CFG_DRYRUN) != 0)
+ goto err_out;
+
+ if (pinmux_config_gpio(gpioc, gpio,
+ new_pinmux_type,
+ GPIO_CFG_REQ) != 0)
+ BUG();
+
+ gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[gpio].flags |= new_pinmux_type;
+
+ ret = 0;
+ err_out:
+ return ret;
+}
+
+static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return ret;
+}
+
+static void sh_gpio_set_value(struct pinmux_info *gpioc,
+ unsigned gpio, int value)
+{
+ struct pinmux_data_reg *dr = NULL;
+ int bit = 0;
+
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
+ BUG();
+ else
+ gpio_write_bit(dr, bit, value);
+}
+
+static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct pinmux_info *gpioc = chip_to_pinmux(chip);
+ unsigned long flags;
+ int ret;
+
+ sh_gpio_set_value(gpioc, offset, value);
+ spin_lock_irqsave(&gpio_lock, flags);
+ ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return ret;
+}
+
+static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio)
+{
+ struct pinmux_data_reg *dr = NULL;
+ int bit = 0;
+
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) {
+ BUG();
+ return 0;
+ }
+
+ return gpio_read_reg(dr->reg, dr->reg_width, 1, bit);
+}
+
+static int sh_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ return sh_gpio_get_value(chip_to_pinmux(chip), offset);
+}
+
+static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ sh_gpio_set_value(chip_to_pinmux(chip), offset, value);
+}
+
+int register_pinmux(struct pinmux_info *pip)
+{
+ struct gpio_chip *chip = &pip->chip;
+
+ pr_info("sh pinmux: %s handling gpio %d -> %d\n",
+ pip->name, pip->first_gpio, pip->last_gpio);
+
+ setup_data_regs(pip);
+
+ chip->request = sh_gpio_request;
+ chip->free = sh_gpio_free;
+ chip->direction_input = sh_gpio_direction_input;
+ chip->get = sh_gpio_get;
+ chip->direction_output = sh_gpio_direction_output;
+ chip->set = sh_gpio_set;
+
+ WARN_ON(pip->first_gpio != 0); /* needs testing */
+
+ chip->label = pip->name;
+ chip->owner = THIS_MODULE;
+ chip->base = pip->first_gpio;
+ chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1;
+
+ return gpiochip_add(chip);
+}
diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c
index ba1a872b221..bf5f95a1941 100644
--- a/drivers/spi/omap2_mcspi.c
+++ b/drivers/spi/omap2_mcspi.c
@@ -35,8 +35,8 @@
#include <linux/spi/spi.h>
-#include <mach/dma.h>
-#include <mach/clock.h>
+#include <plat/dma.h>
+#include <plat/clock.h>
#define OMAP2_MCSPI_MAX_FREQ 48000000
diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c
index e75ba9b2889..6c3a8557db2 100644
--- a/drivers/spi/omap_uwire.c
+++ b/drivers/spi/omap_uwire.c
@@ -51,8 +51,8 @@
#include <asm/io.h>
#include <asm/mach-types.h>
-#include <mach/mux.h>
-#include <mach/omap730.h> /* OMAP730_IO_CONF registers */
+#include <plat/mux.h>
+#include <plat/omap7xx.h> /* OMAP7XX_IO_CONF registers */
/* FIXME address is now a platform device resource,
@@ -504,7 +504,7 @@ static int __init uwire_probe(struct platform_device *pdev)
}
clk_enable(uwire->ck);
- if (cpu_is_omap730())
+ if (cpu_is_omap7xx())
uwire_idx_shift = 1;
else
uwire_idx_shift = 2;
@@ -573,8 +573,8 @@ static int __init omap_uwire_init(void)
}
if (machine_is_omap_perseus2()) {
/* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */
- int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000;
- omap_writel(val | 0x00AAA000, OMAP730_IO_CONF_9);
+ int val = omap_readl(OMAP7XX_IO_CONF_9) & ~0x00EEE000;
+ omap_writel(val | 0x00AAA000, OMAP7XX_IO_CONF_9);
}
return platform_driver_probe(&uwire_driver, uwire_probe);
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 5d23983f02f..20d7322e2f7 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -30,7 +30,6 @@
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
@@ -42,7 +41,7 @@
* This supports acccess to SPI devices using normal userspace I/O calls.
* Note that while traditional UNIX/POSIX I/O semantics are half duplex,
* and often mask message boundaries, full SPI support requires full duplex
- * transfers. There are several kinds of of internal message boundaries to
+ * transfers. There are several kinds of internal message boundaries to
* handle chipselect management and other protocol options.
*
* SPI has a character major number assigned. We allocate minor numbers
@@ -477,7 +476,6 @@ static int spidev_open(struct inode *inode, struct file *filp)
struct spidev_data *spidev;
int status = -ENXIO;
- lock_kernel();
mutex_lock(&device_list_lock);
list_for_each_entry(spidev, &device_list, device_entry) {
@@ -503,7 +501,6 @@ static int spidev_open(struct inode *inode, struct file *filp)
pr_debug("spidev: nothing for minor %d\n", iminor(inode));
mutex_unlock(&device_list_lock);
- unlock_kernel();
return status;
}
diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c
index 538c570df33..f1dcd7969a5 100644
--- a/drivers/ssb/driver_pcicore.c
+++ b/drivers/ssb/driver_pcicore.c
@@ -551,13 +551,13 @@ int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
might_sleep_if(pdev->id.coreid != SSB_DEV_PCI);
/* Enable interrupts for this device. */
- if (bus->host_pci &&
- ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE))) {
+ if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) {
u32 coremask;
/* Calculate the "coremask" for the device. */
coremask = (1 << dev->core_index);
+ SSB_WARN_ON(bus->bustype != SSB_BUSTYPE_PCI);
err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp);
if (err)
goto out;
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 579b114be41..5681ebed9c6 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -140,6 +140,19 @@ static void ssb_device_put(struct ssb_device *dev)
put_device(dev->dev);
}
+static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv)
+{
+ if (drv)
+ get_driver(&drv->drv);
+ return drv;
+}
+
+static inline void ssb_driver_put(struct ssb_driver *drv)
+{
+ if (drv)
+ put_driver(&drv->drv);
+}
+
static int ssb_device_resume(struct device *dev)
{
struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
@@ -210,90 +223,81 @@ int ssb_bus_suspend(struct ssb_bus *bus)
EXPORT_SYMBOL(ssb_bus_suspend);
#ifdef CONFIG_SSB_SPROM
-int ssb_devices_freeze(struct ssb_bus *bus)
+/** ssb_devices_freeze - Freeze all devices on the bus.
+ *
+ * After freezing no device driver will be handling a device
+ * on this bus anymore. ssb_devices_thaw() must be called after
+ * a successful freeze to reactivate the devices.
+ *
+ * @bus: The bus.
+ * @ctx: Context structure. Pass this to ssb_devices_thaw().
+ */
+int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx)
{
- struct ssb_device *dev;
- struct ssb_driver *drv;
- int err = 0;
- int i;
- pm_message_t state = PMSG_FREEZE;
+ struct ssb_device *sdev;
+ struct ssb_driver *sdrv;
+ unsigned int i;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->bus = bus;
+ SSB_WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen));
- /* First check that we are capable to freeze all devices. */
for (i = 0; i < bus->nr_devices; i++) {
- dev = &(bus->devices[i]);
- if (!dev->dev ||
- !dev->dev->driver ||
- !device_is_registered(dev->dev))
- continue;
- drv = drv_to_ssb_drv(dev->dev->driver);
- if (!drv)
+ sdev = ssb_device_get(&bus->devices[i]);
+
+ if (!sdev->dev || !sdev->dev->driver ||
+ !device_is_registered(sdev->dev)) {
+ ssb_device_put(sdev);
continue;
- if (!drv->suspend) {
- /* Nope, can't suspend this one. */
- return -EOPNOTSUPP;
}
- }
- /* Now suspend all devices */
- for (i = 0; i < bus->nr_devices; i++) {
- dev = &(bus->devices[i]);
- if (!dev->dev ||
- !dev->dev->driver ||
- !device_is_registered(dev->dev))
- continue;
- drv = drv_to_ssb_drv(dev->dev->driver);
- if (!drv)
+ sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver));
+ if (!sdrv || SSB_WARN_ON(!sdrv->remove)) {
+ ssb_device_put(sdev);
continue;
- err = drv->suspend(dev, state);
- if (err) {
- ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n",
- dev_name(dev->dev));
- goto err_unwind;
}
+ sdrv->remove(sdev);
+ ctx->device_frozen[i] = 1;
}
return 0;
-err_unwind:
- for (i--; i >= 0; i--) {
- dev = &(bus->devices[i]);
- if (!dev->dev ||
- !dev->dev->driver ||
- !device_is_registered(dev->dev))
- continue;
- drv = drv_to_ssb_drv(dev->dev->driver);
- if (!drv)
- continue;
- if (drv->resume)
- drv->resume(dev);
- }
- return err;
}
-int ssb_devices_thaw(struct ssb_bus *bus)
+/** ssb_devices_thaw - Unfreeze all devices on the bus.
+ *
+ * This will re-attach the device drivers and re-init the devices.
+ *
+ * @ctx: The context structure from ssb_devices_freeze()
+ */
+int ssb_devices_thaw(struct ssb_freeze_context *ctx)
{
- struct ssb_device *dev;
- struct ssb_driver *drv;
- int err;
- int i;
+ struct ssb_bus *bus = ctx->bus;
+ struct ssb_device *sdev;
+ struct ssb_driver *sdrv;
+ unsigned int i;
+ int err, result = 0;
for (i = 0; i < bus->nr_devices; i++) {
- dev = &(bus->devices[i]);
- if (!dev->dev ||
- !dev->dev->driver ||
- !device_is_registered(dev->dev))
+ if (!ctx->device_frozen[i])
continue;
- drv = drv_to_ssb_drv(dev->dev->driver);
- if (!drv)
+ sdev = &bus->devices[i];
+
+ if (SSB_WARN_ON(!sdev->dev || !sdev->dev->driver))
continue;
- if (SSB_WARN_ON(!drv->resume))
+ sdrv = drv_to_ssb_drv(sdev->dev->driver);
+ if (SSB_WARN_ON(!sdrv || !sdrv->probe))
continue;
- err = drv->resume(dev);
+
+ err = sdrv->probe(sdev, &sdev->id);
if (err) {
ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",
- dev_name(dev->dev));
+ dev_name(sdev->dev));
+ result = err;
}
+ ssb_driver_put(sdrv);
+ ssb_device_put(sdev);
}
- return 0;
+ return result;
}
#endif /* CONFIG_SSB_SPROM */
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c
index 100e7a5c5ea..e72f4046a5e 100644
--- a/drivers/ssb/pcmcia.c
+++ b/drivers/ssb/pcmcia.c
@@ -617,136 +617,140 @@ static int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size)
} \
} while (0)
-int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
- struct ssb_init_invariants *iv)
+static int ssb_pcmcia_get_mac(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv)
{
- tuple_t tuple;
- int res;
- unsigned char buf[32];
+ struct ssb_sprom *sprom = priv;
+
+ if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID)
+ return -EINVAL;
+ if (tuple->TupleDataLen != ETH_ALEN + 2)
+ return -EINVAL;
+ if (tuple->TupleData[1] != ETH_ALEN)
+ return -EINVAL;
+ memcpy(sprom->il0mac, &tuple->TupleData[2], ETH_ALEN);
+ return 0;
+};
+
+static int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv)
+{
+ struct ssb_init_invariants *iv = priv;
struct ssb_sprom *sprom = &iv->sprom;
struct ssb_boardinfo *bi = &iv->boardinfo;
const char *error_description;
+ GOTO_ERROR_ON(tuple->TupleDataLen < 1, "VEN tpl < 1");
+ switch (tuple->TupleData[0]) {
+ case SSB_PCMCIA_CIS_ID:
+ GOTO_ERROR_ON((tuple->TupleDataLen != 5) &&
+ (tuple->TupleDataLen != 7),
+ "id tpl size");
+ bi->vendor = tuple->TupleData[1] |
+ ((u16)tuple->TupleData[2] << 8);
+ break;
+ case SSB_PCMCIA_CIS_BOARDREV:
+ GOTO_ERROR_ON(tuple->TupleDataLen != 2,
+ "boardrev tpl size");
+ sprom->board_rev = tuple->TupleData[1];
+ break;
+ case SSB_PCMCIA_CIS_PA:
+ GOTO_ERROR_ON((tuple->TupleDataLen != 9) &&
+ (tuple->TupleDataLen != 10),
+ "pa tpl size");
+ sprom->pa0b0 = tuple->TupleData[1] |
+ ((u16)tuple->TupleData[2] << 8);
+ sprom->pa0b1 = tuple->TupleData[3] |
+ ((u16)tuple->TupleData[4] << 8);
+ sprom->pa0b2 = tuple->TupleData[5] |
+ ((u16)tuple->TupleData[6] << 8);
+ sprom->itssi_a = tuple->TupleData[7];
+ sprom->itssi_bg = tuple->TupleData[7];
+ sprom->maxpwr_a = tuple->TupleData[8];
+ sprom->maxpwr_bg = tuple->TupleData[8];
+ break;
+ case SSB_PCMCIA_CIS_OEMNAME:
+ /* We ignore this. */
+ break;
+ case SSB_PCMCIA_CIS_CCODE:
+ GOTO_ERROR_ON(tuple->TupleDataLen != 2,
+ "ccode tpl size");
+ sprom->country_code = tuple->TupleData[1];
+ break;
+ case SSB_PCMCIA_CIS_ANTENNA:
+ GOTO_ERROR_ON(tuple->TupleDataLen != 2,
+ "ant tpl size");
+ sprom->ant_available_a = tuple->TupleData[1];
+ sprom->ant_available_bg = tuple->TupleData[1];
+ break;
+ case SSB_PCMCIA_CIS_ANTGAIN:
+ GOTO_ERROR_ON(tuple->TupleDataLen != 2,
+ "antg tpl size");
+ sprom->antenna_gain.ghz24.a0 = tuple->TupleData[1];
+ sprom->antenna_gain.ghz24.a1 = tuple->TupleData[1];
+ sprom->antenna_gain.ghz24.a2 = tuple->TupleData[1];
+ sprom->antenna_gain.ghz24.a3 = tuple->TupleData[1];
+ sprom->antenna_gain.ghz5.a0 = tuple->TupleData[1];
+ sprom->antenna_gain.ghz5.a1 = tuple->TupleData[1];
+ sprom->antenna_gain.ghz5.a2 = tuple->TupleData[1];
+ sprom->antenna_gain.ghz5.a3 = tuple->TupleData[1];
+ break;
+ case SSB_PCMCIA_CIS_BFLAGS:
+ GOTO_ERROR_ON((tuple->TupleDataLen != 3) &&
+ (tuple->TupleDataLen != 5),
+ "bfl tpl size");
+ sprom->boardflags_lo = tuple->TupleData[1] |
+ ((u16)tuple->TupleData[2] << 8);
+ break;
+ case SSB_PCMCIA_CIS_LEDS:
+ GOTO_ERROR_ON(tuple->TupleDataLen != 5,
+ "leds tpl size");
+ sprom->gpio0 = tuple->TupleData[1];
+ sprom->gpio1 = tuple->TupleData[2];
+ sprom->gpio2 = tuple->TupleData[3];
+ sprom->gpio3 = tuple->TupleData[4];
+ break;
+ }
+ return -ENOSPC; /* continue with next entry */
+
+error:
+ ssb_printk(KERN_ERR PFX
+ "PCMCIA: Failed to fetch device invariants: %s\n",
+ error_description);
+ return -ENODEV;
+}
+
+
+int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
+ struct ssb_init_invariants *iv)
+{
+ struct ssb_sprom *sprom = &iv->sprom;
+ int res;
+
memset(sprom, 0xFF, sizeof(*sprom));
sprom->revision = 1;
sprom->boardflags_lo = 0;
sprom->boardflags_hi = 0;
/* First fetch the MAC address. */
- memset(&tuple, 0, sizeof(tuple));
- tuple.DesiredTuple = CISTPL_FUNCE;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != 0, "MAC first tpl");
- res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != 0, "MAC first tpl data");
- while (1) {
- GOTO_ERROR_ON(tuple.TupleDataLen < 1, "MAC tpl < 1");
- if (tuple.TupleData[0] == CISTPL_FUNCE_LAN_NODE_ID)
- break;
- res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != 0, "MAC next tpl");
- res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != 0, "MAC next tpl data");
+ res = pcmcia_loop_tuple(bus->host_pcmcia, CISTPL_FUNCE,
+ ssb_pcmcia_get_mac, sprom);
+ if (res != 0) {
+ ssb_printk(KERN_ERR PFX
+ "PCMCIA: Failed to fetch MAC address\n");
+ return -ENODEV;
}
- GOTO_ERROR_ON(tuple.TupleDataLen != ETH_ALEN + 2, "MAC tpl size");
- memcpy(sprom->il0mac, &tuple.TupleData[2], ETH_ALEN);
/* Fetch the vendor specific tuples. */
- memset(&tuple, 0, sizeof(tuple));
- tuple.DesiredTuple = SSB_PCMCIA_CIS;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != 0, "VEN first tpl");
- res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != 0, "VEN first tpl data");
- while (1) {
- GOTO_ERROR_ON(tuple.TupleDataLen < 1, "VEN tpl < 1");
- switch (tuple.TupleData[0]) {
- case SSB_PCMCIA_CIS_ID:
- GOTO_ERROR_ON((tuple.TupleDataLen != 5) &&
- (tuple.TupleDataLen != 7),
- "id tpl size");
- bi->vendor = tuple.TupleData[1] |
- ((u16)tuple.TupleData[2] << 8);
- break;
- case SSB_PCMCIA_CIS_BOARDREV:
- GOTO_ERROR_ON(tuple.TupleDataLen != 2,
- "boardrev tpl size");
- sprom->board_rev = tuple.TupleData[1];
- break;
- case SSB_PCMCIA_CIS_PA:
- GOTO_ERROR_ON((tuple.TupleDataLen != 9) &&
- (tuple.TupleDataLen != 10),
- "pa tpl size");
- sprom->pa0b0 = tuple.TupleData[1] |
- ((u16)tuple.TupleData[2] << 8);
- sprom->pa0b1 = tuple.TupleData[3] |
- ((u16)tuple.TupleData[4] << 8);
- sprom->pa0b2 = tuple.TupleData[5] |
- ((u16)tuple.TupleData[6] << 8);
- sprom->itssi_a = tuple.TupleData[7];
- sprom->itssi_bg = tuple.TupleData[7];
- sprom->maxpwr_a = tuple.TupleData[8];
- sprom->maxpwr_bg = tuple.TupleData[8];
- break;
- case SSB_PCMCIA_CIS_OEMNAME:
- /* We ignore this. */
- break;
- case SSB_PCMCIA_CIS_CCODE:
- GOTO_ERROR_ON(tuple.TupleDataLen != 2,
- "ccode tpl size");
- sprom->country_code = tuple.TupleData[1];
- break;
- case SSB_PCMCIA_CIS_ANTENNA:
- GOTO_ERROR_ON(tuple.TupleDataLen != 2,
- "ant tpl size");
- sprom->ant_available_a = tuple.TupleData[1];
- sprom->ant_available_bg = tuple.TupleData[1];
- break;
- case SSB_PCMCIA_CIS_ANTGAIN:
- GOTO_ERROR_ON(tuple.TupleDataLen != 2,
- "antg tpl size");
- sprom->antenna_gain.ghz24.a0 = tuple.TupleData[1];
- sprom->antenna_gain.ghz24.a1 = tuple.TupleData[1];
- sprom->antenna_gain.ghz24.a2 = tuple.TupleData[1];
- sprom->antenna_gain.ghz24.a3 = tuple.TupleData[1];
- sprom->antenna_gain.ghz5.a0 = tuple.TupleData[1];
- sprom->antenna_gain.ghz5.a1 = tuple.TupleData[1];
- sprom->antenna_gain.ghz5.a2 = tuple.TupleData[1];
- sprom->antenna_gain.ghz5.a3 = tuple.TupleData[1];
- break;
- case SSB_PCMCIA_CIS_BFLAGS:
- GOTO_ERROR_ON((tuple.TupleDataLen != 3) &&
- (tuple.TupleDataLen != 5),
- "bfl tpl size");
- sprom->boardflags_lo = tuple.TupleData[1] |
- ((u16)tuple.TupleData[2] << 8);
- break;
- case SSB_PCMCIA_CIS_LEDS:
- GOTO_ERROR_ON(tuple.TupleDataLen != 5,
- "leds tpl size");
- sprom->gpio0 = tuple.TupleData[1];
- sprom->gpio1 = tuple.TupleData[2];
- sprom->gpio2 = tuple.TupleData[3];
- sprom->gpio3 = tuple.TupleData[4];
- break;
- }
- res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple);
- if (res == -ENOSPC)
- break;
- GOTO_ERROR_ON(res != 0, "VEN next tpl");
- res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != 0, "VEN next tpl data");
- }
+ res = pcmcia_loop_tuple(bus->host_pcmcia, SSB_PCMCIA_CIS,
+ ssb_pcmcia_do_get_invariants, sprom);
+ if ((res == 0) || (res == -ENOSPC))
+ return 0;
- return 0;
-error:
ssb_printk(KERN_ERR PFX
- "PCMCIA: Failed to fetch device invariants: %s\n",
- error_description);
+ "PCMCIA: Failed to fetch device invariants\n");
return -ENODEV;
}
diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c
index e8b89e8ac9b..0d6c0280eb3 100644
--- a/drivers/ssb/scan.c
+++ b/drivers/ssb/scan.c
@@ -354,7 +354,7 @@ int ssb_bus_scan(struct ssb_bus *bus,
dev->bus = bus;
dev->ops = bus->ops;
- ssb_dprintk(KERN_INFO PFX
+ printk(KERN_DEBUG PFX
"Core %d found: %s "
"(cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n",
i, ssb_core_name(dev->id.coreid),
diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
index 8943015a3ee..d0e6762fec5 100644
--- a/drivers/ssb/sprom.c
+++ b/drivers/ssb/sprom.c
@@ -13,6 +13,8 @@
#include "ssb_private.h"
+#include <linux/ctype.h>
+
static const struct ssb_sprom *fallback_sprom;
@@ -33,17 +35,27 @@ static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
static int hex2sprom(u16 *sprom, const char *dump, size_t len,
size_t sprom_size_words)
{
- char tmp[5] = { 0 };
- int cnt = 0;
+ char c, tmp[5] = { 0 };
+ int err, cnt = 0;
unsigned long parsed;
- if (len < sprom_size_words * 2)
+ /* Strip whitespace at the end. */
+ while (len) {
+ c = dump[len - 1];
+ if (!isspace(c) && c != '\0')
+ break;
+ len--;
+ }
+ /* Length must match exactly. */
+ if (len != sprom_size_words * 4)
return -EINVAL;
while (cnt < sprom_size_words) {
memcpy(tmp, dump, 4);
dump += 4;
- parsed = simple_strtoul(tmp, NULL, 16);
+ err = strict_strtoul(tmp, 16, &parsed);
+ if (err)
+ return err;
sprom[cnt++] = swab16((u16)parsed);
}
@@ -90,6 +102,7 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
u16 *sprom;
int res = 0, err = -ENOMEM;
size_t sprom_size_words = bus->sprom_size;
+ struct ssb_freeze_context freeze;
sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
if (!sprom)
@@ -111,18 +124,13 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
err = -ERESTARTSYS;
if (mutex_lock_interruptible(&bus->sprom_mutex))
goto out_kfree;
- err = ssb_devices_freeze(bus);
- if (err == -EOPNOTSUPP) {
- ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. "
- "No suspend support. Is CONFIG_PM enabled?\n");
- goto out_unlock;
- }
+ err = ssb_devices_freeze(bus, &freeze);
if (err) {
ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
goto out_unlock;
}
res = sprom_write(bus, sprom);
- err = ssb_devices_thaw(bus);
+ err = ssb_devices_thaw(&freeze);
if (err)
ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
out_unlock:
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 25433565dfd..56054be4d11 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -176,13 +176,21 @@ extern const struct ssb_sprom *ssb_get_fallback_sprom(void);
/* core.c */
extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
-extern int ssb_devices_freeze(struct ssb_bus *bus);
-extern int ssb_devices_thaw(struct ssb_bus *bus);
extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
int ssb_for_each_bus_call(unsigned long data,
int (*func)(struct ssb_bus *bus, unsigned long data));
extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev);
+struct ssb_freeze_context {
+ /* Pointer to the bus */
+ struct ssb_bus *bus;
+ /* Boolean list to indicate whether a device is frozen on this bus. */
+ bool device_frozen[SSB_MAX_NR_CORES];
+};
+extern int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx);
+extern int ssb_devices_thaw(struct ssb_freeze_context *ctx);
+
+
/* b43_pci_bridge.c */
#ifdef CONFIG_SSB_B43_PCI_BRIDGE
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d21b3469f6d..dfcd75cf490 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -125,5 +125,13 @@ source "drivers/staging/sep/Kconfig"
source "drivers/staging/iio/Kconfig"
+source "drivers/staging/strip/Kconfig"
+
+source "drivers/staging/arlan/Kconfig"
+
+source "drivers/staging/wavelan/Kconfig"
+
+source "drivers/staging/netwave/Kconfig"
+
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 8cbf1aebea2..7719d04a4a8 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -44,3 +44,8 @@ obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_RAR_REGISTER) += rar/
obj-$(CONFIG_DX_SEP) += sep/
obj-$(CONFIG_IIO) += iio/
+obj-$(CONFIG_STRIP) += strip/
+obj-$(CONFIG_ARLAN) += arlan/
+obj-$(CONFIG_WAVELAN) += wavelan/
+obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan/
+obj-$(CONFIG_PCMCIA_NETWAVE) += netwave/
diff --git a/drivers/staging/arlan/Kconfig b/drivers/staging/arlan/Kconfig
new file mode 100644
index 00000000000..5e42b81f97b
--- /dev/null
+++ b/drivers/staging/arlan/Kconfig
@@ -0,0 +1,15 @@
+config ARLAN
+ tristate "Aironet Arlan 655 & IC2200 DS support"
+ depends on ISA && !64BIT && WLAN
+ select WIRELESS_EXT
+ ---help---
+ Aironet makes Arlan, a class of wireless LAN adapters. These use the
+ www.Telxon.com chip, which is also used on several similar cards.
+ This driver is tested on the 655 and IC2200 series cards. Look at
+ <http://www.ylenurme.ee/~elmer/655/> for the latest information.
+
+ The driver is built as two modules, arlan and arlan-proc. The latter
+ is the /proc interface and is not needed most of time.
+
+ On some computers the card ends up in non-valid state after some
+ time. Use a ping-reset script to clear it.
diff --git a/drivers/staging/arlan/Makefile b/drivers/staging/arlan/Makefile
new file mode 100644
index 00000000000..9e58e5fae7b
--- /dev/null
+++ b/drivers/staging/arlan/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_ARLAN) += arlan.o
+
+arlan-objs := arlan-main.o arlan-proc.o
diff --git a/drivers/staging/arlan/TODO b/drivers/staging/arlan/TODO
new file mode 100644
index 00000000000..9bd15a2f6d9
--- /dev/null
+++ b/drivers/staging/arlan/TODO
@@ -0,0 +1,7 @@
+TODO:
+ - step up and maintain this driver to ensure that it continues
+ to work. Having the hardware for this is pretty much a
+ requirement. If this does not happen, the will be removed in
+ the 2.6.35 kernel release.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com>.
diff --git a/drivers/net/wireless/arlan-main.c b/drivers/staging/arlan/arlan-main.c
index 921a082487a..921a082487a 100644
--- a/drivers/net/wireless/arlan-main.c
+++ b/drivers/staging/arlan/arlan-main.c
diff --git a/drivers/net/wireless/arlan-proc.c b/drivers/staging/arlan/arlan-proc.c
index a8b689635a3..b22983e6c0c 100644
--- a/drivers/net/wireless/arlan-proc.c
+++ b/drivers/staging/arlan/arlan-proc.c
@@ -816,84 +816,83 @@ static int arlan_sysctl_reset(ctl_table * ctl, int write,
/* Place files in /proc/sys/dev/arlan */
-#define CTBLN(num,card,nam) \
- { .ctl_name = num,\
- .procname = #nam,\
+#define CTBLN(card,nam) \
+ { .procname = #nam,\
.data = &(arlan_conf[card].nam),\
- .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec}
+ .maxlen = sizeof(int), .mode = 0600, .proc_handler = proc_dointvec}
#ifdef ARLAN_DEBUGGING
#define ARLAN_PROC_DEBUG_ENTRIES \
- { .ctl_name = 48, .procname = "entry_exit_debug",\
+ { .procname = "entry_exit_debug",\
.data = &arlan_entry_and_exit_debug,\
- .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec},\
- { .ctl_name = 49, .procname = "debug", .data = &arlan_debug,\
- .maxlen = sizeof(int), .mode = 0600, .proc_handler = &proc_dointvec},
+ .maxlen = sizeof(int), .mode = 0600, .proc_handler = proc_dointvec},\
+ { .procname = "debug", .data = &arlan_debug,\
+ .maxlen = sizeof(int), .mode = 0600, .proc_handler = proc_dointvec},
#else
#define ARLAN_PROC_DEBUG_ENTRIES
#endif
#define ARLAN_SYSCTL_TABLE_TOTAL(cardNo)\
- CTBLN(1,cardNo,spreadingCode),\
- CTBLN(2,cardNo, channelNumber),\
- CTBLN(3,cardNo, scramblingDisable),\
- CTBLN(4,cardNo, txAttenuation),\
- CTBLN(5,cardNo, systemId), \
- CTBLN(6,cardNo, maxDatagramSize),\
- CTBLN(7,cardNo, maxFrameSize),\
- CTBLN(8,cardNo, maxRetries),\
- CTBLN(9,cardNo, receiveMode),\
- CTBLN(10,cardNo, priority),\
- CTBLN(11,cardNo, rootOrRepeater),\
- CTBLN(12,cardNo, SID),\
- CTBLN(13,cardNo, registrationMode),\
- CTBLN(14,cardNo, registrationFill),\
- CTBLN(15,cardNo, localTalkAddress),\
- CTBLN(16,cardNo, codeFormat),\
- CTBLN(17,cardNo, numChannels),\
- CTBLN(18,cardNo, channel1),\
- CTBLN(19,cardNo, channel2),\
- CTBLN(20,cardNo, channel3),\
- CTBLN(21,cardNo, channel4),\
- CTBLN(22,cardNo, txClear),\
- CTBLN(23,cardNo, txRetries),\
- CTBLN(24,cardNo, txRouting),\
- CTBLN(25,cardNo, txScrambled),\
- CTBLN(26,cardNo, rxParameter),\
- CTBLN(27,cardNo, txTimeoutMs),\
- CTBLN(28,cardNo, waitCardTimeout),\
- CTBLN(29,cardNo, channelSet), \
- {.ctl_name = 30, .procname = "name",\
+ CTBLN(cardNo,spreadingCode),\
+ CTBLN(cardNo, channelNumber),\
+ CTBLN(cardNo, scramblingDisable),\
+ CTBLN(cardNo, txAttenuation),\
+ CTBLN(cardNo, systemId), \
+ CTBLN(cardNo, maxDatagramSize),\
+ CTBLN(cardNo, maxFrameSize),\
+ CTBLN(cardNo, maxRetries),\
+ CTBLN(cardNo, receiveMode),\
+ CTBLN(cardNo, priority),\
+ CTBLN(cardNo, rootOrRepeater),\
+ CTBLN(cardNo, SID),\
+ CTBLN(cardNo, registrationMode),\
+ CTBLN(cardNo, registrationFill),\
+ CTBLN(cardNo, localTalkAddress),\
+ CTBLN(cardNo, codeFormat),\
+ CTBLN(cardNo, numChannels),\
+ CTBLN(cardNo, channel1),\
+ CTBLN(cardNo, channel2),\
+ CTBLN(cardNo, channel3),\
+ CTBLN(cardNo, channel4),\
+ CTBLN(cardNo, txClear),\
+ CTBLN(cardNo, txRetries),\
+ CTBLN(cardNo, txRouting),\
+ CTBLN(cardNo, txScrambled),\
+ CTBLN(cardNo, rxParameter),\
+ CTBLN(cardNo, txTimeoutMs),\
+ CTBLN(cardNo, waitCardTimeout),\
+ CTBLN(cardNo, channelSet), \
+ { .procname = "name",\
.data = arlan_conf[cardNo].siteName,\
- .maxlen = 16, .mode = 0600, .proc_handler = &proc_dostring},\
- CTBLN(31,cardNo,waitTime),\
- CTBLN(32,cardNo,lParameter),\
- CTBLN(33,cardNo,_15),\
- CTBLN(34,cardNo,headerSize),\
- CTBLN(36,cardNo,tx_delay_ms),\
- CTBLN(37,cardNo,retries),\
- CTBLN(38,cardNo,ReTransmitPacketMaxSize),\
- CTBLN(39,cardNo,waitReTransmitPacketMaxSize),\
- CTBLN(40,cardNo,fastReTransCount),\
- CTBLN(41,cardNo,driverRetransmissions),\
- CTBLN(42,cardNo,txAckTimeoutMs),\
- CTBLN(43,cardNo,registrationInterrupts),\
- CTBLN(44,cardNo,hardwareType),\
- CTBLN(45,cardNo,radioType),\
- CTBLN(46,cardNo,writeEEPROM),\
- CTBLN(47,cardNo,writeRadioType),\
+ .maxlen = 16, .mode = 0600, .proc_handler = proc_dostring},\
+ CTBLN(cardNo,waitTime),\
+ CTBLN(cardNo,lParameter),\
+ CTBLN(cardNo,_15),\
+ CTBLN(cardNo,headerSize),\
+ CTBLN(cardNo,tx_delay_ms),\
+ CTBLN(cardNo,retries),\
+ CTBLN(cardNo,ReTransmitPacketMaxSize),\
+ CTBLN(cardNo,waitReTransmitPacketMaxSize),\
+ CTBLN(cardNo,fastReTransCount),\
+ CTBLN(cardNo,driverRetransmissions),\
+ CTBLN(cardNo,txAckTimeoutMs),\
+ CTBLN(cardNo,registrationInterrupts),\
+ CTBLN(cardNo,hardwareType),\
+ CTBLN(cardNo,radioType),\
+ CTBLN(cardNo,writeEEPROM),\
+ CTBLN(cardNo,writeRadioType),\
ARLAN_PROC_DEBUG_ENTRIES\
- CTBLN(50,cardNo,in_speed),\
- CTBLN(51,cardNo,out_speed),\
- CTBLN(52,cardNo,in_speed10),\
- CTBLN(53,cardNo,out_speed10),\
- CTBLN(54,cardNo,in_speed_max),\
- CTBLN(55,cardNo,out_speed_max),\
- CTBLN(56,cardNo,measure_rate),\
- CTBLN(57,cardNo,pre_Command_Wait),\
- CTBLN(58,cardNo,rx_tweak1),\
- CTBLN(59,cardNo,rx_tweak2),\
- CTBLN(60,cardNo,tx_queue_len),\
+ CTBLN(cardNo,in_speed),\
+ CTBLN(cardNo,out_speed),\
+ CTBLN(cardNo,in_speed10),\
+ CTBLN(cardNo,out_speed10),\
+ CTBLN(cardNo,in_speed_max),\
+ CTBLN(cardNo,out_speed_max),\
+ CTBLN(cardNo,measure_rate),\
+ CTBLN(cardNo,pre_Command_Wait),\
+ CTBLN(cardNo,rx_tweak1),\
+ CTBLN(cardNo,rx_tweak2),\
+ CTBLN(cardNo,tx_queue_len),\
@@ -903,63 +902,56 @@ static ctl_table arlan_conf_table0[] =
#ifdef ARLAN_PROC_SHM_DUMP
{
- .ctl_name = 150,
.procname = "arlan0-txRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_infotxRing,
+ .proc_handler = arlan_sysctl_infotxRing,
},
{
- .ctl_name = 151,
.procname = "arlan0-rxRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_inforxRing,
+ .proc_handler = arlan_sysctl_inforxRing,
},
{
- .ctl_name = 152,
.procname = "arlan0-18",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info18,
+ .proc_handler = arlan_sysctl_info18,
},
{
- .ctl_name = 153,
.procname = "arlan0-ring",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info161719,
+ .proc_handler = arlan_sysctl_info161719,
},
{
- .ctl_name = 154,
.procname = "arlan0-shm-cpy",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info,
+ .proc_handler = arlan_sysctl_info,
},
#endif
{
- .ctl_name = 155,
.procname = "config0",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_configure
+ .proc_handler = arlan_configure
},
{
- .ctl_name = 156,
.procname = "reset0",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_sysctl_reset,
+ .proc_handler = arlan_sysctl_reset,
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table arlan_conf_table1[] =
@@ -969,63 +961,56 @@ static ctl_table arlan_conf_table1[] =
#ifdef ARLAN_PROC_SHM_DUMP
{
- .ctl_name = 150,
.procname = "arlan1-txRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_infotxRing,
+ .proc_handler = arlan_sysctl_infotxRing,
},
{
- .ctl_name = 151,
.procname = "arlan1-rxRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_inforxRing,
+ .proc_handler = arlan_sysctl_inforxRing,
},
{
- .ctl_name = 152,
.procname = "arlan1-18",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info18,
+ .proc_handler = arlan_sysctl_info18,
},
{
- .ctl_name = 153,
.procname = "arlan1-ring",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info161719,
+ .proc_handler = arlan_sysctl_info161719,
},
{
- .ctl_name = 154,
.procname = "arlan1-shm-cpy",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info,
+ .proc_handler = arlan_sysctl_info,
},
#endif
{
- .ctl_name = 155,
.procname = "config1",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_configure,
+ .proc_handler = arlan_configure,
},
{
- .ctl_name = 156,
.procname = "reset1",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_sysctl_reset,
+ .proc_handler = arlan_sysctl_reset,
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table arlan_conf_table2[] =
@@ -1035,63 +1020,56 @@ static ctl_table arlan_conf_table2[] =
#ifdef ARLAN_PROC_SHM_DUMP
{
- .ctl_name = 150,
.procname = "arlan2-txRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_infotxRing,
+ .proc_handler = arlan_sysctl_infotxRing,
},
{
- .ctl_name = 151,
.procname = "arlan2-rxRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_inforxRing,
+ .proc_handler = arlan_sysctl_inforxRing,
},
{
- .ctl_name = 152,
.procname = "arlan2-18",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info18,
+ .proc_handler = arlan_sysctl_info18,
},
{
- .ctl_name = 153,
.procname = "arlan2-ring",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info161719,
+ .proc_handler = arlan_sysctl_info161719,
},
{
- .ctl_name = 154,
.procname = "arlan2-shm-cpy",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info,
+ .proc_handler = arlan_sysctl_info,
},
#endif
{
- .ctl_name = 155,
.procname = "config2",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_configure,
+ .proc_handler = arlan_configure,
},
{
- .ctl_name = 156,
.procname = "reset2",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_sysctl_reset,
+ .proc_handler = arlan_sysctl_reset,
},
- { .ctl_name = 0 }
+ { }
};
static ctl_table arlan_conf_table3[] =
@@ -1101,63 +1079,56 @@ static ctl_table arlan_conf_table3[] =
#ifdef ARLAN_PROC_SHM_DUMP
{
- .ctl_name = 150,
.procname = "arlan3-txRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_infotxRing,
+ .proc_handler = arlan_sysctl_infotxRing,
},
{
- .ctl_name = 151,
.procname = "arlan3-rxRing",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_inforxRing,
+ .proc_handler = arlan_sysctl_inforxRing,
},
{
- .ctl_name = 152,
.procname = "arlan3-18",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info18,
+ .proc_handler = arlan_sysctl_info18,
},
{
- .ctl_name = 153,
.procname = "arlan3-ring",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info161719,
+ .proc_handler = arlan_sysctl_info161719,
},
{
- .ctl_name = 154,
.procname = "arlan3-shm-cpy",
.data = &arlan_drive_info,
.maxlen = ARLAN_STR_SIZE,
.mode = 0400,
- .proc_handler = &arlan_sysctl_info,
+ .proc_handler = arlan_sysctl_info,
},
#endif
{
- .ctl_name = 155,
.procname = "config3",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_configure,
+ .proc_handler = arlan_configure,
},
{
- .ctl_name = 156,
.procname = "reset3",
.data = &conf_reset_result,
.maxlen = 100,
.mode = 0400,
- .proc_handler = &arlan_sysctl_reset,
+ .proc_handler = arlan_sysctl_reset,
},
- { .ctl_name = 0 }
+ { }
};
@@ -1165,41 +1136,37 @@ static ctl_table arlan_conf_table3[] =
static ctl_table arlan_table[] =
{
{
- .ctl_name = 0,
.procname = "arlan0",
.maxlen = 0,
.mode = 0600,
.child = arlan_conf_table0,
},
{
- .ctl_name = 0,
.procname = "arlan1",
.maxlen = 0,
.mode = 0600,
.child = arlan_conf_table1,
},
{
- .ctl_name = 0,
.procname = "arlan2",
.maxlen = 0,
.mode = 0600,
.child = arlan_conf_table2,
},
{
- .ctl_name = 0,
.procname = "arlan3",
.maxlen = 0,
.mode = 0600,
.child = arlan_conf_table3,
},
- { .ctl_name = 0 }
+ { }
};
#else
-static ctl_table arlan_table[MAX_ARLANS + 1] =
+static ctl_table arlan_table[] =
{
- { .ctl_name = 0 }
+ { }
};
#endif
@@ -1209,22 +1176,14 @@ static ctl_table arlan_table[MAX_ARLANS + 1] =
static ctl_table arlan_root_table[] =
{
{
- .ctl_name = CTL_ARLAN,
.procname = "arlan",
.maxlen = 0,
.mode = 0555,
.child = arlan_table,
},
- { .ctl_name = 0 }
+ { }
};
-/* Make sure that /proc/sys/dev is there */
-//static ctl_table arlan_device_root_table[] =
-//{
-// {CTL_DEV, "dev", NULL, 0, 0555, arlan_root_table},
-// {0}
-//};
-
static struct ctl_table_header *arlan_device_sysctl_header;
@@ -1234,8 +1193,6 @@ int __init init_arlan_proc(void)
int i = 0;
if (arlan_device_sysctl_header)
return 0;
- for (i = 0; i < MAX_ARLANS && arlan_device[i]; i++)
- arlan_table[i].ctl_name = i + 1;
arlan_device_sysctl_header = register_sysctl_table(arlan_root_table);
if (!arlan_device_sysctl_header)
return -1;
diff --git a/drivers/net/wireless/arlan.h b/drivers/staging/arlan/arlan.h
index fb3ad51a1ca..fb3ad51a1ca 100644
--- a/drivers/net/wireless/arlan.h
+++ b/drivers/staging/arlan/arlan.h
diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c
index 80c0df8656f..39923cb388b 100644
--- a/drivers/staging/comedi/drivers/cb_das16_cs.c
+++ b/drivers/staging/comedi/drivers/cb_das16_cs.c
@@ -141,37 +141,14 @@ static int das16cs_timer_insn_config(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data);
-static int get_prodid(struct comedi_device *dev, struct pcmcia_device *link)
-{
- tuple_t tuple;
- u_short buf[128];
- int prodid = 0;
-
- tuple.TupleData = (cisdata_t *) buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.DesiredTuple = CISTPL_MANFID;
- tuple.Attributes = TUPLE_RETURN_COMMON;
- if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
- (pcmcia_get_tuple_data(link, &tuple) == 0)) {
- prodid = le16_to_cpu(buf[1]);
- }
-
- return prodid;
-}
-
static const struct das16cs_board *das16cs_probe(struct comedi_device *dev,
struct pcmcia_device *link)
{
- int id;
int i;
- id = get_prodid(dev, link);
-
for (i = 0; i < n_boards; i++) {
- if (das16cs_boards[i].device_id == id) {
+ if (das16cs_boards[i].device_id == link->card_id)
return das16cs_boards + i;
- }
}
printk("unknown board!\n");
@@ -660,27 +637,8 @@ static int das16cs_timer_insn_config(struct comedi_device *dev,
======================================================================*/
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
- "cb_das16_cs.c pcmcia code (David Schleef), modified from dummy_cs.c 1.31 2001/08/24 12:13:13 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
-
-/*====================================================================*/
-
static void das16cs_pcmcia_config(struct pcmcia_device *link);
static void das16cs_pcmcia_release(struct pcmcia_device *link);
static int das16cs_pcmcia_suspend(struct pcmcia_device *p_dev);
@@ -733,7 +691,7 @@ static int das16cs_pcmcia_attach(struct pcmcia_device *link)
{
struct local_info_t *local;
- DEBUG(0, "das16cs_pcmcia_attach()\n");
+ dev_dbg(&link->dev, "das16cs_pcmcia_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
@@ -745,7 +703,6 @@ static int das16cs_pcmcia_attach(struct pcmcia_device *link)
/* Initialize the pcmcia_device structure */
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->irq.Handler = NULL;
link->conf.Attributes = 0;
@@ -760,7 +717,7 @@ static int das16cs_pcmcia_attach(struct pcmcia_device *link)
static void das16cs_pcmcia_detach(struct pcmcia_device *link)
{
- DEBUG(0, "das16cs_pcmcia_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "das16cs_pcmcia_detach\n");
if (link->dev_node) {
((struct local_info_t *)link->priv)->stop = 1;
@@ -771,118 +728,55 @@ static void das16cs_pcmcia_detach(struct pcmcia_device *link)
kfree(link->priv);
} /* das16cs_pcmcia_detach */
-static void das16cs_pcmcia_config(struct pcmcia_device *link)
-{
- struct local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_fn, last_ret;
- u_char buf[64];
- cistpl_cftable_entry_t dflt = { 0 };
- DEBUG(0, "das16cs_pcmcia_config(0x%p)\n", link);
-
- /*
- This reads the card's CONFIG tuple to find its configuration
- registers.
- */
- tuple.DesiredTuple = CISTPL_CONFIG;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- last_fn = GetFirstTuple;
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret != 0)
- goto cs_failed;
-
- last_fn = GetTupleData;
- last_ret = pcmcia_get_tuple_data(link, &tuple);
- if (last_ret != 0)
- goto cs_failed;
-
- last_fn = ParseTuple;
- last_ret = pcmcia_parse_tuple(&tuple, &parse);
- if (last_ret != 0)
- goto cs_failed;
-
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
+static int das16cs_pcmcia_config_loop(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ return -EINVAL;
- /*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
- */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- last_fn = GetFirstTuple;
-
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret)
- goto cs_failed;
-
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple))
- goto next_entry;
- if (pcmcia_parse_tuple(&tuple, &parse))
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
-/* if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
-*/
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io))
- goto next_entry;
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
}
+ /* This reserves IO space but doesn't actually enable it */
+ return pcmcia_request_io(p_dev, &p_dev->io);
+ }
- /* If we got this far, we're cool! */
- break;
+ return 0;
+}
+
+static void das16cs_pcmcia_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ int ret;
-next_entry:
- last_fn = GetNextTuple;
+ dev_dbg(&link->dev, "das16cs_pcmcia_config\n");
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret)
- goto cs_failed;
+ ret = pcmcia_loop_config(link, das16cs_pcmcia_config_loop, NULL);
+ if (ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ goto failed;
}
/*
@@ -891,21 +785,18 @@ next_entry:
irq structure is initialized.
*/
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- last_fn = RequestIRQ;
-
- last_ret = pcmcia_request_irq(link, &link->irq);
- if (last_ret)
- goto cs_failed;
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
}
/*
This actually configures the PCMCIA socket -- setting up
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- last_fn = RequestConfiguration;
- last_ret = pcmcia_request_configuration(link, &link->conf);
- if (last_ret)
- goto cs_failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -930,14 +821,13 @@ next_entry:
return;
-cs_failed:
- cs_error(link, last_fn, last_ret);
+failed:
das16cs_pcmcia_release(link);
} /* das16cs_pcmcia_config */
static void das16cs_pcmcia_release(struct pcmcia_device *link)
{
- DEBUG(0, "das16cs_pcmcia_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "das16cs_pcmcia_release\n");
pcmcia_disable_device(link);
} /* das16cs_pcmcia_release */
@@ -983,14 +873,13 @@ struct pcmcia_driver das16cs_driver = {
static int __init init_das16cs_pcmcia_cs(void)
{
- DEBUG(0, "%s\n", version);
pcmcia_register_driver(&das16cs_driver);
return 0;
}
static void __exit exit_das16cs_pcmcia_cs(void)
{
- DEBUG(0, "das16cs_pcmcia_cs: unloading\n");
+ pr_debug("das16cs_pcmcia_cs: unloading\n");
pcmcia_unregister_driver(&das16cs_driver);
}
diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c
index 9cab21eaaa1..9b945e5fdd3 100644
--- a/drivers/staging/comedi/drivers/das08_cs.c
+++ b/drivers/staging/comedi/drivers/das08_cs.c
@@ -110,25 +110,6 @@ static int das08_cs_attach(struct comedi_device *dev,
======================================================================*/
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static const char *version =
- "das08.c pcmcia code (Frank Hess), modified from dummy_cs.c 1.31 2001/08/24 12:13:13 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
-
-/*====================================================================*/
static void das08_pcmcia_config(struct pcmcia_device *link);
static void das08_pcmcia_release(struct pcmcia_device *link);
static int das08_pcmcia_suspend(struct pcmcia_device *p_dev);
@@ -181,7 +162,7 @@ static int das08_pcmcia_attach(struct pcmcia_device *link)
{
struct local_info_t *local;
- DEBUG(0, "das08_pcmcia_attach()\n");
+ dev_dbg(&link->dev, "das08_pcmcia_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
@@ -192,7 +173,6 @@ static int das08_pcmcia_attach(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->irq.Handler = NULL;
/*
@@ -224,7 +204,7 @@ static int das08_pcmcia_attach(struct pcmcia_device *link)
static void das08_pcmcia_detach(struct pcmcia_device *link)
{
- DEBUG(0, "das08_pcmcia_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "das08_pcmcia_detach\n");
if (link->dev_node) {
((struct local_info_t *)link->priv)->stop = 1;
@@ -237,6 +217,44 @@ static void das08_pcmcia_detach(struct pcmcia_device *link)
} /* das08_pcmcia_detach */
+
+static int das08_pcmcia_config_loop(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ return -ENODEV;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ return pcmcia_request_io(p_dev, &p_dev->io);
+ }
+ return 0;
+}
+
+
/*======================================================================
das08_pcmcia_config() is scheduled to run after a CARD_INSERTION event
@@ -248,128 +266,20 @@ static void das08_pcmcia_detach(struct pcmcia_device *link)
static void das08_pcmcia_config(struct pcmcia_device *link)
{
struct local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_fn, last_ret;
- u_char buf[64];
- cistpl_cftable_entry_t dflt = { 0 };
-
- DEBUG(0, "das08_pcmcia_config(0x%p)\n", link);
-
- /*
- This reads the card's CONFIG tuple to find its configuration
- registers.
- */
- tuple.DesiredTuple = CISTPL_CONFIG;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- last_fn = GetFirstTuple;
-
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret)
- goto cs_failed;
-
- last_fn = GetTupleData;
-
- last_ret = pcmcia_get_tuple_data(link, &tuple);
- if (last_ret)
- goto cs_failed;
-
- last_fn = ParseTuple;
-
- last_ret = pcmcia_parse_tuple(&tuple, &parse);
- if (last_ret)
- goto cs_failed;
-
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
-
- /*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
- */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- last_fn = GetFirstTuple;
-
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret)
- goto cs_failed;
-
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
-
- last_ret = pcmcia_get_tuple_data(link, &tuple);
- if (last_ret)
- goto next_entry;
-
- last_ret = pcmcia_parse_tuple(&tuple, &parse);
- if (last_ret)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
-/* if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
-*/
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
-
- /* If we got this far, we're cool! */
- break;
+ int ret;
-next_entry:
- last_fn = GetNextTuple;
+ dev_dbg(&link->dev, "das08_pcmcia_config\n");
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret)
- goto cs_failed;
+ ret = pcmcia_loop_config(link, das08_pcmcia_config_loop, NULL);
+ if (ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ goto failed;
}
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- last_fn = RequestIRQ;
- last_ret = pcmcia_request_irq(link, &link->irq);
- if (last_ret)
- goto cs_failed;
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
}
/*
@@ -377,10 +287,9 @@ next_entry:
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- last_fn = RequestConfiguration;
- last_ret = pcmcia_request_configuration(link, &link->conf);
- if (last_ret)
- goto cs_failed;
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -405,8 +314,7 @@ next_entry:
return;
-cs_failed:
- cs_error(link, last_fn, last_ret);
+failed:
das08_pcmcia_release(link);
} /* das08_pcmcia_config */
@@ -421,7 +329,7 @@ cs_failed:
static void das08_pcmcia_release(struct pcmcia_device *link)
{
- DEBUG(0, "das08_pcmcia_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "das08_pcmcia_release\n");
pcmcia_disable_device(link);
} /* das08_pcmcia_release */
@@ -477,14 +385,13 @@ struct pcmcia_driver das08_cs_driver = {
static int __init init_das08_pcmcia_cs(void)
{
- DEBUG(0, "%s\n", version);
pcmcia_register_driver(&das08_cs_driver);
return 0;
}
static void __exit exit_das08_pcmcia_cs(void)
{
- DEBUG(0, "das08_pcmcia_cs: unloading\n");
+ pr_debug("das08_pcmcia_cs: unloading\n");
pcmcia_unregister_driver(&das08_cs_driver);
}
diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c
index ec31a397066..ef5e1183d47 100644
--- a/drivers/staging/comedi/drivers/ni_daq_700.c
+++ b/drivers/staging/comedi/drivers/ni_daq_700.c
@@ -436,25 +436,7 @@ static int dio700_detach(struct comedi_device *dev)
return 0;
};
-/* PCMCIA crap */
-
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = "ni_daq_700.c, based on dummy_cs.c";
-#else
-#define DEBUG(n, args...)
-#endif
-
-/*====================================================================*/
+/* PCMCIA crap -- watch your words, please! */
static void dio700_config(struct pcmcia_device *link);
static void dio700_release(struct pcmcia_device *link);
@@ -510,7 +492,7 @@ static int dio700_cs_attach(struct pcmcia_device *link)
printk(KERN_INFO "ni_daq_700: cs-attach\n");
- DEBUG(0, "dio700_cs_attach()\n");
+ dev_dbg(&link->dev, "dio700_cs_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
@@ -521,7 +503,6 @@ static int dio700_cs_attach(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->irq.Handler = NULL;
/*
@@ -555,7 +536,7 @@ static void dio700_cs_detach(struct pcmcia_device *link)
printk(KERN_INFO "ni_daq_700: cs-detach!\n");
- DEBUG(0, "dio700_cs_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "dio700_cs_detach\n");
if (link->dev_node) {
((struct local_info_t *)link->priv)->stop = 1;
@@ -576,141 +557,85 @@ static void dio700_cs_detach(struct pcmcia_device *link)
======================================================================*/
-static void dio700_config(struct pcmcia_device *link)
+static int dio700_pcmcia_config_loop(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- struct local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_ret;
- u_char buf[64];
- win_req_t req;
+ win_req_t *req = priv_data;
memreq_t map;
- cistpl_cftable_entry_t dflt = { 0 };
- printk(KERN_INFO "ni_daq_700: cs-config\n");
-
- DEBUG(0, "dio700_config(0x%p)\n", link);
+ if (cfg->index == 0)
+ return -ENODEV;
- /*
- This reads the card's CONFIG tuple to find its configuration
- registers.
- */
- tuple.DesiredTuple = CISTPL_CONFIG;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
}
- last_ret = pcmcia_get_tuple_data(link, &tuple);
- if (last_ret) {
- cs_error(link, GetTupleData, last_ret);
- goto cs_failed;
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ return -ENODEV;
}
- last_ret = pcmcia_parse_tuple(&tuple, &parse);
- if (last_ret) {
- cs_error(link, ParseTuple, last_ret);
- goto cs_failed;
+ if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) {
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+ req->Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
+ req->Attributes |= WIN_ENABLE;
+ req->Base = mem->win[0].host_addr;
+ req->Size = mem->win[0].len;
+ if (req->Size < 0x1000)
+ req->Size = 0x1000;
+ req->AccessSpeed = 0;
+ if (pcmcia_request_window(p_dev, req, &p_dev->win))
+ return -ENODEV;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(p_dev, p_dev->win, &map))
+ return -ENODEV;
}
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
-
- /*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
- */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret != 0) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
- }
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple) != 0)
- goto next_entry;
- if (pcmcia_parse_tuple(&tuple, &parse) != 0)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
- if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
+ /* If we got this far, we're cool! */
+ return 0;
+}
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
+static void dio700_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ win_req_t req;
+ int ret;
- if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
- cistpl_mem_t *mem =
- (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
- req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
- req.Attributes |= WIN_ENABLE;
- req.Base = mem->win[0].host_addr;
- req.Size = mem->win[0].len;
- if (req.Size < 0x1000)
- req.Size = 0x1000;
- req.AccessSpeed = 0;
- if (pcmcia_request_window(&link, &req, &link->win))
- goto next_entry;
- map.Page = 0;
- map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(link->win, &map))
- goto next_entry;
- }
- /* If we got this far, we're cool! */
- break;
+ printk(KERN_INFO "ni_daq_700: cs-config\n");
-next_entry:
+ dev_dbg(&link->dev, "dio700_config\n");
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetNextTuple, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_loop_config(link, dio700_pcmcia_config_loop, &req);
+ if (ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ goto failed;
}
/*
@@ -719,11 +644,9 @@ next_entry:
irq structure is initialized.
*/
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- last_ret = pcmcia_request_irq(link, &link->irq);
- if (last_ret) {
- cs_error(link, RequestIRQ, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
}
/*
@@ -731,11 +654,9 @@ next_entry:
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- last_ret = pcmcia_request_configuration(link, &link->conf);
- if (last_ret != 0) {
- cs_error(link, RequestConfiguration, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret != 0)
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -763,7 +684,7 @@ next_entry:
return;
-cs_failed:
+failed:
printk(KERN_INFO "ni_daq_700 cs failed");
dio700_release(link);
@@ -771,7 +692,7 @@ cs_failed:
static void dio700_release(struct pcmcia_device *link)
{
- DEBUG(0, "dio700_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "dio700_release\n");
pcmcia_disable_device(link);
} /* dio700_release */
@@ -830,15 +751,13 @@ struct pcmcia_driver dio700_cs_driver = {
static int __init init_dio700_cs(void)
{
- printk("ni_daq_700: cs-init \n");
- DEBUG(0, "%s\n", version);
pcmcia_register_driver(&dio700_cs_driver);
return 0;
}
static void __exit exit_dio700_cs(void)
{
- DEBUG(0, "ni_daq_700: unloading\n");
+ pr_debug("ni_daq_700: unloading\n");
pcmcia_unregister_driver(&dio700_cs_driver);
}
diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c
index 0700a8bddd1..9017be3a92f 100644
--- a/drivers/staging/comedi/drivers/ni_daq_dio24.c
+++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c
@@ -187,25 +187,7 @@ static int dio24_detach(struct comedi_device *dev)
return 0;
};
-/* PCMCIA crap */
-
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = "ni_daq_dio24.c, based on dummy_cs.c";
-#else
-#define DEBUG(n, args...)
-#endif
-
-/*====================================================================*/
+/* PCMCIA crap -- watch your words! */
static void dio24_config(struct pcmcia_device *link);
static void dio24_release(struct pcmcia_device *link);
@@ -261,7 +243,7 @@ static int dio24_cs_attach(struct pcmcia_device *link)
printk(KERN_INFO "ni_daq_dio24: HOLA SOY YO - CS-attach!\n");
- DEBUG(0, "dio24_cs_attach()\n");
+ dev_dbg(&link->dev, "dio24_cs_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
@@ -272,7 +254,6 @@ static int dio24_cs_attach(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->irq.Handler = NULL;
/*
@@ -306,7 +287,7 @@ static void dio24_cs_detach(struct pcmcia_device *link)
printk(KERN_INFO "ni_daq_dio24: HOLA SOY YO - cs-detach!\n");
- DEBUG(0, "dio24_cs_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "dio24_cs_detach\n");
if (link->dev_node) {
((struct local_info_t *)link->priv)->stop = 1;
@@ -327,142 +308,85 @@ static void dio24_cs_detach(struct pcmcia_device *link)
======================================================================*/
-static void dio24_config(struct pcmcia_device *link)
+static int dio24_pcmcia_config_loop(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- struct local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_ret;
- u_char buf[64];
- win_req_t req;
+ win_req_t *req = priv_data;
memreq_t map;
- cistpl_cftable_entry_t dflt = { 0 };
- printk(KERN_INFO "ni_daq_dio24: HOLA SOY YO! - config\n");
-
- DEBUG(0, "dio24_config(0x%p)\n", link);
-
- /*
- This reads the card's CONFIG tuple to find its configuration
- registers.
- */
- tuple.DesiredTuple = CISTPL_CONFIG;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
- }
+ if (cfg->index == 0)
+ return -ENODEV;
- last_ret = pcmcia_get_tuple_data(link, &tuple);
- if (last_ret) {
- cs_error(link, GetTupleData, last_ret);
- goto cs_failed;
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
}
- last_ret = pcmcia_parse_tuple(&tuple, &parse);
- if (last_ret) {
- cs_error(link, ParseTuple, last_ret);
- goto cs_failed;
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ return -ENODEV;
}
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
-
- /*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
- */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
+ if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) {
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+ req->Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
+ req->Attributes |= WIN_ENABLE;
+ req->Base = mem->win[0].host_addr;
+ req->Size = mem->win[0].len;
+ if (req->Size < 0x1000)
+ req->Size = 0x1000;
+ req->AccessSpeed = 0;
+ if (pcmcia_request_window(p_dev, req, &p_dev->win))
+ return -ENODEV;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(p_dev, p_dev->win, &map))
+ return -ENODEV;
}
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple) != 0)
- goto next_entry;
- if (pcmcia_parse_tuple(&tuple, &parse) != 0)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
- if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
+ /* If we got this far, we're cool! */
+ return 0;
+}
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
+static void dio24_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ int ret;
+ win_req_t req;
- if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
- cistpl_mem_t *mem =
- (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
- req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
- req.Attributes |= WIN_ENABLE;
- req.Base = mem->win[0].host_addr;
- req.Size = mem->win[0].len;
- if (req.Size < 0x1000)
- req.Size = 0x1000;
- req.AccessSpeed = 0;
- if (pcmcia_request_window(&link, &req, &link->win))
- goto next_entry;
- map.Page = 0;
- map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(link->win, &map))
- goto next_entry;
- }
- /* If we got this far, we're cool! */
- break;
+ printk(KERN_INFO "ni_daq_dio24: HOLA SOY YO! - config\n");
-next_entry:
+ dev_dbg(&link->dev, "dio24_config\n");
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetNextTuple, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_loop_config(link, dio24_pcmcia_config_loop, &req);
+ if (ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ goto failed;
}
/*
@@ -471,11 +395,9 @@ next_entry:
irq structure is initialized.
*/
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- last_ret = pcmcia_request_irq(link, &link->irq);
- if (last_ret) {
- cs_error(link, RequestIRQ, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
}
/*
@@ -483,11 +405,9 @@ next_entry:
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- last_ret = pcmcia_request_configuration(link, &link->conf);
- if (last_ret) {
- cs_error(link, RequestConfiguration, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -515,7 +435,7 @@ next_entry:
return;
-cs_failed:
+failed:
printk(KERN_INFO "Fallo");
dio24_release(link);
@@ -523,7 +443,7 @@ cs_failed:
static void dio24_release(struct pcmcia_device *link)
{
- DEBUG(0, "dio24_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "dio24_release\n");
pcmcia_disable_device(link);
} /* dio24_release */
@@ -582,14 +502,12 @@ struct pcmcia_driver dio24_cs_driver = {
static int __init init_dio24_cs(void)
{
printk("ni_daq_dio24: HOLA SOY YO!\n");
- DEBUG(0, "%s\n", version);
pcmcia_register_driver(&dio24_cs_driver);
return 0;
}
static void __exit exit_dio24_cs(void)
{
- DEBUG(0, "ni_dio24: unloading\n");
pcmcia_unregister_driver(&dio24_cs_driver);
}
diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c
index a3053b8da1c..7d514b3ee75 100644
--- a/drivers/staging/comedi/drivers/ni_labpc_cs.c
+++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c
@@ -153,23 +153,6 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return labpc_common_attach(dev, iobase, irq, 0);
}
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static const char *version =
- "ni_labpc.c, based on dummy_cs.c 1.31 2001/08/24 12:13:13";
-#else
-#define DEBUG(n, args...)
-#endif
-
/*====================================================================*/
/*
@@ -236,7 +219,7 @@ static int labpc_cs_attach(struct pcmcia_device *link)
{
struct local_info_t *local;
- DEBUG(0, "labpc_cs_attach()\n");
+ dev_dbg(&link->dev, "labpc_cs_attach()\n");
/* Allocate space for private device-specific data */
local = kzalloc(sizeof(struct local_info_t), GFP_KERNEL);
@@ -247,7 +230,6 @@ static int labpc_cs_attach(struct pcmcia_device *link)
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FORCED_PULSE;
- link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_PULSE_ID;
link->irq.Handler = NULL;
/*
@@ -278,7 +260,7 @@ static int labpc_cs_attach(struct pcmcia_device *link)
static void labpc_cs_detach(struct pcmcia_device *link)
{
- DEBUG(0, "labpc_cs_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "labpc_cs_detach\n");
/*
If the device is currently configured and active, we won't
@@ -305,135 +287,84 @@ static void labpc_cs_detach(struct pcmcia_device *link)
======================================================================*/
-static void labpc_config(struct pcmcia_device *link)
+static int labpc_pcmcia_config_loop(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- struct local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_ret;
- u_char buf[64];
- win_req_t req;
+ win_req_t *req = priv_data;
memreq_t map;
- cistpl_cftable_entry_t dflt = { 0 };
- DEBUG(0, "labpc_config(0x%p)\n", link);
+ if (cfg->index == 0)
+ return -ENODEV;
- /*
- This reads the card's CONFIG tuple to find its configuration
- registers.
- */
- tuple.DesiredTuple = CISTPL_CONFIG;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
}
- last_ret = pcmcia_get_tuple_data(link, &tuple);
- if (last_ret) {
- cs_error(link, GetTupleData, last_ret);
- goto cs_failed;
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ return -ENODEV;
}
- last_ret = pcmcia_parse_tuple(&tuple, &parse);
- if (last_ret) {
- cs_error(link, ParseTuple, last_ret);
- goto cs_failed;
+ if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) {
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+ req->Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
+ req->Attributes |= WIN_ENABLE;
+ req->Base = mem->win[0].host_addr;
+ req->Size = mem->win[0].len;
+ if (req->Size < 0x1000)
+ req->Size = 0x1000;
+ req->AccessSpeed = 0;
+ if (pcmcia_request_window(p_dev, req, &p_dev->win))
+ return -ENODEV;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(p_dev, p_dev->win, &map))
+ return -ENODEV;
}
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
+ /* If we got this far, we're cool! */
+ return 0;
+}
- /*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
- */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
- }
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple))
- goto next_entry;
- if (pcmcia_parse_tuple(&tuple, &parse))
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
- if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io))
- goto next_entry;
- }
+static void labpc_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ int ret;
+ win_req_t req;
- if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
- cistpl_mem_t *mem =
- (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
- req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM;
- req.Attributes |= WIN_ENABLE;
- req.Base = mem->win[0].host_addr;
- req.Size = mem->win[0].len;
- if (req.Size < 0x1000)
- req.Size = 0x1000;
- req.AccessSpeed = 0;
- link->win = (window_handle_t) link;
- if (pcmcia_request_window(&link, &req, &link->win))
- goto next_entry;
- map.Page = 0;
- map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(link->win, &map))
- goto next_entry;
- }
- /* If we got this far, we're cool! */
- break;
+ dev_dbg(&link->dev, "labpc_config\n");
-next_entry:
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetNextTuple, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_loop_config(link, labpc_pcmcia_config_loop, &req);
+ if (ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ goto failed;
}
/*
@@ -442,11 +373,9 @@ next_entry:
irq structure is initialized.
*/
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- last_ret = pcmcia_request_irq(link, &link->irq);
- if (last_ret) {
- cs_error(link, RequestIRQ, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
}
/*
@@ -454,11 +383,9 @@ next_entry:
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- last_ret = pcmcia_request_configuration(link, &link->conf);
- if (last_ret) {
- cs_error(link, RequestConfiguration, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -486,14 +413,14 @@ next_entry:
return;
-cs_failed:
+failed:
labpc_release(link);
} /* labpc_config */
static void labpc_release(struct pcmcia_device *link)
{
- DEBUG(0, "labpc_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "labpc_release\n");
pcmcia_disable_device(link);
} /* labpc_release */
@@ -551,14 +478,12 @@ struct pcmcia_driver labpc_cs_driver = {
static int __init init_labpc_cs(void)
{
- DEBUG(0, "%s\n", version);
pcmcia_register_driver(&labpc_cs_driver);
return 0;
}
static void __exit exit_labpc_cs(void)
{
- DEBUG(0, "ni_labpc: unloading\n");
pcmcia_unregister_driver(&labpc_cs_driver);
}
diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c
index 9aef87fc81d..d692f4bb47e 100644
--- a/drivers/staging/comedi/drivers/ni_mio_cs.c
+++ b/drivers/staging/comedi/drivers/ni_mio_cs.c
@@ -274,7 +274,6 @@ static int cs_attach(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
link->io.NumPorts1 = 16;
link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -312,96 +311,47 @@ static int mio_cs_resume(struct pcmcia_device *link)
return 0;
}
-static void mio_cs_config(struct pcmcia_device *link)
-{
- tuple_t tuple;
- u_short buf[128];
- cisparse_t parse;
- int manfid = 0, prodid = 0;
- int ret;
-
- DPRINTK("mio_cs_config(link=%p)\n", link);
- tuple.TupleData = (cisdata_t *) buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
+static int mio_pcmcia_config_loop(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int base, ret;
- tuple.DesiredTuple = CISTPL_CONFIG;
- ret = pcmcia_get_first_tuple(link, &tuple);
- ret = pcmcia_get_tuple_data(link, &tuple);
- ret = pcmcia_parse_tuple(&tuple, &parse);
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+ p_dev->io.IOAddrLines = cfg->io.flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.NumPorts2 = 0;
-#if 0
- tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
- tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
- info->multi(first_tuple(link, &tuple, &parse) == 0);
-#endif
-
- tuple.DesiredTuple = CISTPL_MANFID;
- tuple.Attributes = TUPLE_RETURN_COMMON;
- if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
- (pcmcia_get_tuple_data(link, &tuple) == 0)) {
- manfid = le16_to_cpu(buf[0]);
- prodid = le16_to_cpu(buf[1]);
+ for (base = 0x000; base < 0x400; base += 0x20) {
+ p_dev->io.BasePort1 = base;
+ ret = pcmcia_request_io(p_dev, &p_dev->io);
+ if (!ret)
+ return 0;
}
- /* printk("manfid = 0x%04x, 0x%04x\n",manfid,prodid); */
+ return -ENODEV;
+}
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- ret = pcmcia_get_first_tuple(link, &tuple);
- ret = pcmcia_get_tuple_data(link, &tuple);
- ret = pcmcia_parse_tuple(&tuple, &parse);
-#if 0
- printk(" index: 0x%x\n", parse.cftable_entry.index);
- printk(" flags: 0x%x\n", parse.cftable_entry.flags);
- printk(" io flags: 0x%x\n", parse.cftable_entry.io.flags);
- printk(" io nwin: 0x%x\n", parse.cftable_entry.io.nwin);
- printk(" io base: 0x%x\n", parse.cftable_entry.io.win[0].base);
- printk(" io len: 0x%x\n", parse.cftable_entry.io.win[0].len);
- printk(" irq1: 0x%x\n", parse.cftable_entry.irq.IRQInfo1);
- printk(" irq2: 0x%x\n", parse.cftable_entry.irq.IRQInfo2);
- printk(" mem flags: 0x%x\n", parse.cftable_entry.mem.flags);
- printk(" mem nwin: 0x%x\n", parse.cftable_entry.mem.nwin);
- printk(" subtuples: 0x%x\n", parse.cftable_entry.subtuples);
-#endif
+static void mio_cs_config(struct pcmcia_device *link)
+{
+ int ret;
-#if 0
- link->io.NumPorts1 = 0x20;
- link->io.IOAddrLines = 5;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
-#endif
- link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
- link->io.IOAddrLines =
- parse.cftable_entry.io.flags & CISTPL_IO_LINES_MASK;
- link->io.NumPorts2 = 0;
+ DPRINTK("mio_cs_config(link=%p)\n", link);
- {
- int base;
- for (base = 0x000; base < 0x400; base += 0x20) {
- link->io.BasePort1 = base;
- ret = pcmcia_request_io(link, &link->io);
- /* printk("RequestIO 0x%02x\n",ret); */
- if (!ret)
- break;
- }
+ ret = pcmcia_loop_config(link, mio_pcmcia_config_loop, NULL);
+ if (ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ return;
}
- link->irq.IRQInfo1 = parse.cftable_entry.irq.IRQInfo1;
- link->irq.IRQInfo2 = parse.cftable_entry.irq.IRQInfo2;
ret = pcmcia_request_irq(link, &link->irq);
if (ret) {
printk("pcmcia_request_irq() returned error: %i\n", ret);
}
- /* printk("RequestIRQ 0x%02x\n",ret); */
-
- link->conf.ConfigIndex = 1;
ret = pcmcia_request_configuration(link, &link->conf);
- /* printk("RequestConfiguration %d\n",ret); */
link->dev_node = &dev_node;
}
@@ -475,40 +425,17 @@ static int mio_cs_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return 0;
}
-static int get_prodid(struct comedi_device *dev, struct pcmcia_device *link)
-{
- tuple_t tuple;
- u_short buf[128];
- int prodid = 0;
-
- tuple.TupleData = (cisdata_t *) buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.DesiredTuple = CISTPL_MANFID;
- tuple.Attributes = TUPLE_RETURN_COMMON;
- if ((pcmcia_get_first_tuple(link, &tuple) == 0) &&
- (pcmcia_get_tuple_data(link, &tuple) == 0)) {
- prodid = le16_to_cpu(buf[1]);
- }
-
- return prodid;
-}
-
static int ni_getboardtype(struct comedi_device *dev,
struct pcmcia_device *link)
{
- int id;
int i;
- id = get_prodid(dev, link);
-
for (i = 0; i < n_ni_boards; i++) {
- if (ni_boards[i].device_id == id) {
+ if (ni_boards[i].device_id == link->card_id)
return i;
- }
}
- printk("unknown board 0x%04x -- pretend it is a ", id);
+ printk("unknown board 0x%04x -- pretend it is a ", link->card_id);
return 0;
}
diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
index 344b82353e0..5256fd93316 100644
--- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c
+++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
@@ -55,23 +55,6 @@ Devices: [Quatech] DAQP-208 (daqp), DAQP-308
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = "quatech_daqp_cs.c 1.10 2003/04/21 (Brent Baccala)";
-#else
-#define DEBUG(n, args...)
-#endif
-
/* Maximum number of separate DAQP devices we'll allow */
#define MAX_DEV 4
@@ -863,8 +846,6 @@ static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
int ret;
struct local_info_t *local = dev_table[it->options[0]];
- tuple_t tuple;
- int i;
struct comedi_subdevice *s;
if (it->options[0] < 0 || it->options[0] >= MAX_DEV || !local) {
@@ -883,29 +864,10 @@ static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
strcpy(local->board_name, "DAQP");
dev->board_name = local->board_name;
-
- tuple.DesiredTuple = CISTPL_VERS_1;
- if (pcmcia_get_first_tuple(local->link, &tuple) == 0) {
- u_char buf[128];
-
- buf[0] = buf[sizeof(buf) - 1] = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 2;
- if (pcmcia_get_tuple_data(local->link, &tuple) == 0) {
-
- for (i = 0; i < tuple.TupleDataLen - 4; i++)
- if (buf[i] == 0)
- break;
- for (i++; i < tuple.TupleDataLen - 4; i++)
- if (buf[i] == 0)
- break;
- i++;
- if ((i < tuple.TupleDataLen - 4)
- && (strncmp(buf + i, "DAQP", 4) == 0)) {
- strncpy(local->board_name, buf + i,
- sizeof(local->board_name));
- }
+ if (local->link->prod_id[2]) {
+ if (strncmp(local->link->prod_id[2], "DAQP", 4) == 0) {
+ strncpy(local->board_name, local->link->prod_id[2],
+ sizeof(local->board_name));
}
}
@@ -1058,7 +1020,7 @@ static int daqp_cs_attach(struct pcmcia_device *link)
struct local_info_t *local;
int i;
- DEBUG(0, "daqp_cs_attach()\n");
+ dev_dbg(&link->dev, "daqp_cs_attach()\n");
for (i = 0; i < MAX_DEV; i++)
if (dev_table[i] == NULL)
@@ -1079,10 +1041,8 @@ static int daqp_cs_attach(struct pcmcia_device *link)
link->priv = local;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = daqp_interrupt;
- link->irq.Instance = local;
/*
General socket configuration defaults can go here. In this
@@ -1112,7 +1072,7 @@ static void daqp_cs_detach(struct pcmcia_device *link)
{
struct local_info_t *dev = link->priv;
- DEBUG(0, "daqp_cs_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "daqp_cs_detach\n");
if (link->dev_node) {
dev->stop = 1;
@@ -1134,115 +1094,54 @@ static void daqp_cs_detach(struct pcmcia_device *link)
======================================================================*/
-static void daqp_cs_config(struct pcmcia_device *link)
-{
- struct local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_ret;
- u_char buf[64];
-
- DEBUG(0, "daqp_cs_config(0x%p)\n", link);
-
- /*
- This reads the card's CONFIG tuple to find its configuration
- registers.
- */
- tuple.DesiredTuple = CISTPL_CONFIG;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
- }
- last_ret = pcmcia_get_tuple_data(link, &tuple);
- if (last_ret) {
- cs_error(link, GetTupleData, last_ret);
- goto cs_failed;
- }
-
- last_ret = pcmcia_parse_tuple(&tuple, &parse);
- if (last_ret) {
- cs_error(link, ParseTuple, last_ret);
- goto cs_failed;
- }
- link->conf.ConfigBase = parse.config.base;
- link->conf.Present = parse.config.rmask[0];
+static int daqp_pcmcia_config_loop(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ return -ENODEV;
- /*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
- */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- last_ret = pcmcia_get_first_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetFirstTuple, last_ret);
- goto cs_failed;
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
}
- while (1) {
- cistpl_cftable_entry_t dflt = { 0 };
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple))
- goto next_entry;
- if (pcmcia_parse_tuple(&tuple, &parse))
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- }
+ /* This reserves IO space but doesn't actually enable it */
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io))
- goto next_entry;
+static void daqp_cs_config(struct pcmcia_device *link)
+{
+ struct local_info_t *dev = link->priv;
+ int ret;
- /* If we got this far, we're cool! */
- break;
+ dev_dbg(&link->dev, "daqp_cs_config\n");
-next_entry:
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret) {
- cs_error(link, GetNextTuple, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_loop_config(link, daqp_pcmcia_config_loop, NULL);
+ if (ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ goto failed;
}
/*
@@ -1251,11 +1150,9 @@ next_entry:
irq structure is initialized.
*/
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- last_ret = pcmcia_request_irq(link, &link->irq);
- if (last_ret) {
- cs_error(link, RequestIRQ, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
}
/*
@@ -1263,11 +1160,9 @@ next_entry:
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
- last_ret = pcmcia_request_configuration(link, &link->conf);
- if (last_ret) {
- cs_error(link, RequestConfiguration, last_ret);
- goto cs_failed;
- }
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
At this point, the dev_node_t structure(s) need to be
@@ -1296,14 +1191,14 @@ next_entry:
return;
-cs_failed:
+failed:
daqp_cs_release(link);
} /* daqp_cs_config */
static void daqp_cs_release(struct pcmcia_device *link)
{
- DEBUG(0, "daqp_cs_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "daqp_cs_release\n");
pcmcia_disable_device(link);
} /* daqp_cs_release */
@@ -1363,7 +1258,6 @@ struct pcmcia_driver daqp_cs_driver = {
int __init init_module(void)
{
- DEBUG(0, "%s\n", version);
pcmcia_register_driver(&daqp_cs_driver);
comedi_driver_register(&driver_daqp);
return 0;
@@ -1371,7 +1265,6 @@ int __init init_module(void)
void __exit cleanup_module(void)
{
- DEBUG(0, "daqp_cs: unloading\n");
comedi_driver_unregister(&driver_daqp);
pcmcia_unregister_driver(&daqp_cs_driver);
}
diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/go7007/Makefile
index 1301caa7495..d37b7edc279 100644
--- a/drivers/staging/go7007/Makefile
+++ b/drivers/staging/go7007/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_VIDEO_GO7007) += go7007.o
obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o
-obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o
+obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o
obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o
obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o
obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o
@@ -17,7 +17,7 @@ obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o
go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \
snd-go7007.o
-s2250-objs += s2250-board.o s2250-loader.o
+s2250-objs += s2250-board.o
# Uncomment when the saa7134 patches get into upstream
#ifneq ($(CONFIG_VIDEO_SAA7134),)
diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/go7007/go7007-driver.c
index 472f4bb08fd..fb1345ffb85 100644
--- a/drivers/staging/go7007/go7007-driver.c
+++ b/drivers/staging/go7007/go7007-driver.c
@@ -49,7 +49,7 @@ int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data)
go->hpi_ops->read_interrupt(go);
if (wait_event_timeout(go->interrupt_waitq,
go->interrupt_available, 5*HZ) < 0) {
- v4l2_err(go->video_dev, "timeout waiting for read interrupt\n");
+ v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n");
return -1;
}
if (!go->interrupt_available)
@@ -193,7 +193,8 @@ int go7007_reset_encoder(struct go7007 *go)
static int init_i2c_module(struct i2c_adapter *adapter, const char *type,
int id, int addr)
{
- struct i2c_board_info info;
+ struct go7007 *go = i2c_get_adapdata(adapter);
+ struct v4l2_device *v4l2_dev = &go->v4l2_dev;
char *modname;
switch (id) {
@@ -219,20 +220,16 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type,
modname = "wis-ov7640";
break;
case I2C_DRIVERID_S2250:
- modname = "s2250-board";
+ modname = "s2250";
break;
default:
modname = NULL;
break;
}
- if (modname != NULL)
- request_module(modname);
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = addr;
- strlcpy(info.type, type, I2C_NAME_SIZE);
- if (!i2c_new_device(adapter, &info))
+ if (v4l2_i2c_new_subdev(v4l2_dev, adapter, modname, type, addr, NULL))
return 0;
+
if (modname != NULL)
printk(KERN_INFO
"go7007: probing for module %s failed\n", modname);
@@ -262,6 +259,11 @@ int go7007_register_encoder(struct go7007 *go)
if (ret < 0)
return -1;
+ /* v4l2 init must happen before i2c subdevs */
+ ret = go7007_v4l2_init(go);
+ if (ret < 0)
+ return ret;
+
if (!go->i2c_adapter_online &&
go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) {
if (go7007_i2c_init(go) < 0)
@@ -282,7 +284,7 @@ int go7007_register_encoder(struct go7007 *go)
go->audio_enabled = 1;
go7007_snd_init(go);
}
- return go7007_v4l2_init(go);
+ return 0;
}
EXPORT_SYMBOL(go7007_register_encoder);
@@ -315,7 +317,7 @@ int go7007_start_encoder(struct go7007 *go)
if (go7007_send_firmware(go, fw, fw_len) < 0 ||
go7007_read_interrupt(go, &intr_val, &intr_data) < 0) {
- v4l2_err(go->video_dev, "error transferring firmware\n");
+ v4l2_err(&go->v4l2_dev, "error transferring firmware\n");
rv = -1;
goto start_error;
}
@@ -324,7 +326,7 @@ int go7007_start_encoder(struct go7007 *go)
go->parse_length = 0;
go->seen_frame = 0;
if (go7007_stream_start(go) < 0) {
- v4l2_err(go->video_dev, "error starting stream transfer\n");
+ v4l2_err(&go->v4l2_dev, "error starting stream transfer\n");
rv = -1;
goto start_error;
}
@@ -420,7 +422,7 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length)
for (i = 0; i < length; ++i) {
if (go->active_buf != NULL &&
go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) {
- v4l2_info(go->video_dev, "dropping oversized frame\n");
+ v4l2_info(&go->v4l2_dev, "dropping oversized frame\n");
go->active_buf->offset -= go->active_buf->bytesused;
go->active_buf->bytesused = 0;
go->active_buf->modet_active = 0;
@@ -668,7 +670,7 @@ void go7007_remove(struct go7007 *go)
if (i2c_del_adapter(&go->i2c_adapter) == 0)
go->i2c_adapter_online = 0;
else
- v4l2_err(go->video_dev,
+ v4l2_err(&go->v4l2_dev,
"error removing I2C adapter!\n");
}
diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/go7007/go7007-priv.h
index ce9307e3e18..b58c394c655 100644
--- a/drivers/staging/go7007/go7007-priv.h
+++ b/drivers/staging/go7007/go7007-priv.h
@@ -21,6 +21,8 @@
* user-space applications.
*/
+#include <media/v4l2-device.h>
+
struct go7007;
/* IDs to activate board-specific support code */
@@ -167,6 +169,7 @@ struct go7007 {
int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */
char name[64];
struct video_device *video_dev;
+ struct v4l2_device v4l2_dev;
int ref_count;
enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status;
spinlock_t spinlock;
@@ -240,6 +243,11 @@ struct go7007 {
unsigned short interrupt_data;
};
+static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct go7007, v4l2_dev);
+}
+
/* All of these must be called with the hpi_lock mutex held! */
#define go7007_interface_reset(go) \
((go)->hpi_ops->interface_reset(go))
diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/go7007/go7007-usb.c
index ecaa3c989cf..1e89dc04ec2 100644
--- a/drivers/staging/go7007/go7007-usb.c
+++ b/drivers/staging/go7007/go7007-usb.c
@@ -425,7 +425,7 @@ static struct go7007_usb_board board_sensoray_2250 = {
.num_i2c_devs = 1,
.i2c_devs = {
{
- .type = "s2250_board",
+ .type = "s2250",
.id = I2C_DRIVERID_S2250,
.addr = 0x43,
},
@@ -1057,7 +1057,7 @@ static int go7007_usb_probe(struct usb_interface *intf,
usb_rcvintpipe(usb->usbdev, 4),
usb->intr_urb->transfer_buffer, 2*sizeof(u16),
go7007_usb_readinterrupt_complete, go, 8);
- usb_set_intfdata(intf, go);
+ usb_set_intfdata(intf, &go->v4l2_dev);
/* Boot the GO7007 */
if (go7007_boot_encoder(go, go->board_info->flags &
@@ -1233,7 +1233,7 @@ allocfail:
static void go7007_usb_disconnect(struct usb_interface *intf)
{
- struct go7007 *go = usb_get_intfdata(intf);
+ struct go7007 *go = to_go7007(usb_get_intfdata(intf));
struct go7007_usb *usb = go->hpi_context;
struct urb *vurb, *aurb;
int i;
diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c
index 4bd353afa59..b18d8e2d4c5 100644
--- a/drivers/staging/go7007/go7007-v4l2.c
+++ b/drivers/staging/go7007/go7007-v4l2.c
@@ -29,6 +29,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
@@ -46,6 +47,9 @@
#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3
#endif
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_until_err(dev, 0, o, f, ##args)
+
static void deactivate_buffer(struct go7007_buffer *gobuf)
{
int i;
@@ -247,19 +251,23 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
go->modet_map[i] = 0;
if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
- struct video_decoder_resolution res;
+ struct v4l2_format res;
+
+ if (fmt != NULL) {
+ res = *fmt;
+ } else {
+ res.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ res.fmt.pix.width = width;
+ }
- res.width = width;
if (height > sensor_height / 2) {
- res.height = height / 2;
+ res.fmt.pix.height = height / 2;
go->encoder_v_halve = 0;
} else {
- res.height = height;
+ res.fmt.pix.height = height;
go->encoder_v_halve = 1;
}
- if (go->i2c_adapter_online)
- i2c_clients_command(&go->i2c_adapter,
- DECODER_SET_RESOLUTION, &res);
+ call_all(&go->v4l2_dev, video, s_fmt, &res);
} else {
if (width <= sensor_width / 4) {
go->encoder_h_halve = 1;
@@ -385,7 +393,7 @@ static int clip_to_modet_map(struct go7007 *go, int region,
}
#endif
-static int mpeg_queryctrl(struct v4l2_queryctrl *ctrl)
+static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl)
{
static const u32 mpeg_ctrls[] = {
V4L2_CID_MPEG_CLASS,
@@ -973,51 +981,35 @@ static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *query)
{
struct go7007 *go = ((struct go7007_file *) priv)->go;
+ int id = query->id;
- if (!go->i2c_adapter_online)
- return -EIO;
-
- i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, query);
+ if (0 == call_all(&go->v4l2_dev, core, queryctrl, query))
+ return 0;
- return (!query->name[0]) ? mpeg_queryctrl(query) : 0;
+ query->id = id;
+ return mpeg_query_ctrl(query);
}
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct go7007 *go = ((struct go7007_file *) priv)->go;
- struct v4l2_queryctrl query;
- if (!go->i2c_adapter_online)
- return -EIO;
-
- memset(&query, 0, sizeof(query));
- query.id = ctrl->id;
- i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
- if (query.name[0] == 0)
- return mpeg_g_ctrl(ctrl, go);
- i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, ctrl);
+ if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl))
+ return 0;
- return 0;
+ return mpeg_g_ctrl(ctrl, go);
}
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct go7007 *go = ((struct go7007_file *) priv)->go;
- struct v4l2_queryctrl query;
- if (!go->i2c_adapter_online)
- return -EIO;
-
- memset(&query, 0, sizeof(query));
- query.id = ctrl->id;
- i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
- if (query.name[0] == 0)
- return mpeg_s_ctrl(ctrl, go);
- i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, ctrl);
+ if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl))
+ return 0;
- return 0;
+ return mpeg_s_ctrl(ctrl, go);
}
static int vidioc_g_parm(struct file *filp, void *priv,
@@ -1135,8 +1127,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
if (go->streaming)
return -EBUSY;
- if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
- *std != 0)
+ if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0)
return -EINVAL;
if (*std == 0)
@@ -1146,9 +1137,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
go->input == go->board_info->num_inputs - 1) {
if (!go->i2c_adapter_online)
return -EIO;
- i2c_clients_command(&go->i2c_adapter,
- VIDIOC_S_STD, std);
- if (!*std) /* hack to indicate EINVAL from tuner */
+ if (call_all(&go->v4l2_dev, core, s_std, *std) < 0)
return -EINVAL;
}
@@ -1164,9 +1153,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
} else
return -EINVAL;
- if (go->i2c_adapter_online)
- i2c_clients_command(&go->i2c_adapter,
- VIDIOC_S_STD, std);
+ call_all(&go->v4l2_dev, core, s_std, *std);
set_capture_size(go, NULL, 0);
return 0;
@@ -1180,7 +1167,7 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
go->input == go->board_info->num_inputs - 1) {
if (!go->i2c_adapter_online)
return -EIO;
- i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYSTD, std);
+ return call_all(&go->v4l2_dev, video, querystd, std);
} else if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
*std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
else
@@ -1238,14 +1225,8 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int input)
return -EBUSY;
go->input = input;
- if (go->i2c_adapter_online) {
- i2c_clients_command(&go->i2c_adapter, VIDIOC_S_INPUT,
- &go->board_info->inputs[input].video_input);
- i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO,
- &go->board_info->inputs[input].audio_input);
- }
- return 0;
+ return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0);
}
static int vidioc_g_tuner(struct file *file, void *priv,
@@ -1260,10 +1241,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
if (!go->i2c_adapter_online)
return -EIO;
- i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, t);
-
- t->index = 0;
- return 0;
+ return call_all(&go->v4l2_dev, tuner, g_tuner, t);
}
static int vidioc_s_tuner(struct file *file, void *priv,
@@ -1287,9 +1265,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
break;
}
- i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, t);
-
- return 0;
+ return call_all(&go->v4l2_dev, tuner, s_tuner, t);
}
static int vidioc_g_frequency(struct file *file, void *priv,
@@ -1303,8 +1279,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
return -EIO;
f->type = V4L2_TUNER_ANALOG_TV;
- i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, f);
- return 0;
+
+ return call_all(&go->v4l2_dev, tuner, g_frequency, f);
}
static int vidioc_s_frequency(struct file *file, void *priv,
@@ -1317,9 +1293,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
if (!go->i2c_adapter_online)
return -EIO;
- i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, f);
-
- return 0;
+ return call_all(&go->v4l2_dev, tuner, s_frequency, f);
}
static int vidioc_cropcap(struct file *file, void *priv,
@@ -1827,7 +1801,7 @@ int go7007_v4l2_init(struct go7007 *go)
go->video_dev = video_device_alloc();
if (go->video_dev == NULL)
return -ENOMEM;
- memcpy(go->video_dev, &go7007_template, sizeof(go7007_template));
+ *go->video_dev = go7007_template;
go->video_dev->parent = go->dev;
rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1);
if (rv < 0) {
@@ -1835,6 +1809,12 @@ int go7007_v4l2_init(struct go7007 *go)
go->video_dev = NULL;
return rv;
}
+ rv = v4l2_device_register(go->dev, &go->v4l2_dev);
+ if (rv < 0) {
+ video_device_release(go->video_dev);
+ go->video_dev = NULL;
+ return rv;
+ }
video_set_drvdata(go->video_dev, go);
++go->ref_count;
printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
@@ -1858,4 +1838,5 @@ void go7007_v4l2_remove(struct go7007 *go)
mutex_unlock(&go->hw_lock);
if (go->video_dev)
video_unregister_device(go->video_dev);
+ v4l2_device_unregister(&go->v4l2_dev);
}
diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c
index f4a6541c3e6..8cf7f2750b3 100644
--- a/drivers/staging/go7007/s2250-board.c
+++ b/drivers/staging/go7007/s2250-board.c
@@ -20,10 +20,14 @@
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
-#include "s2250-loader.h"
+#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-subdev.h>
#include "go7007-priv.h"
-#include "wis-i2c.h"
+
+MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver");
+MODULE_LICENSE("GPL v2");
#define TLV320_ADDRESS 0x34
#define VPX322_ADDR_ANALOGCONTROL1 0x02
@@ -112,6 +116,7 @@ static u16 vid_regs_fp_pal[] =
};
struct s2250 {
+ struct v4l2_subdev sd;
v4l2_std_id std;
int input;
int brightness;
@@ -123,6 +128,11 @@ struct s2250 {
struct i2c_client *audio;
};
+static inline struct s2250 *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s2250, sd);
+}
+
/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
static int go7007_usb_vendor_request(struct go7007 *go, u16 request,
u16 value, u16 index, void *transfer_buffer, int length, int in)
@@ -306,253 +316,262 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs)
}
-static int s2250_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+/* ------------------------------------------------------------------------- */
+
+static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+ u32 config)
{
- struct s2250 *dec = i2c_get_clientdata(client);
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int vidsys;
+
+ vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
+ if (input == 0) {
+ /* composite */
+ write_reg_fp(client, 0x20, 0x020 | vidsys);
+ write_reg_fp(client, 0x21, 0x662);
+ write_reg_fp(client, 0x140, 0x060);
+ } else if (input == 1) {
+ /* S-Video */
+ write_reg_fp(client, 0x20, 0x040 | vidsys);
+ write_reg_fp(client, 0x21, 0x666);
+ write_reg_fp(client, 0x140, 0x060);
+ } else {
+ return -EINVAL;
+ }
+ state->input = input;
+ return 0;
+}
- switch (cmd) {
- case VIDIOC_S_INPUT:
- {
- int vidsys;
- int *input = arg;
-
- vidsys = (dec->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
- if (*input == 0) {
- /* composite */
- write_reg_fp(client, 0x20, 0x020 | vidsys);
- write_reg_fp(client, 0x21, 0x662);
- write_reg_fp(client, 0x140, 0x060);
- } else {
- /* S-Video */
- write_reg_fp(client, 0x20, 0x040 | vidsys);
- write_reg_fp(client, 0x21, 0x666);
- write_reg_fp(client, 0x140, 0x060);
- }
- dec->input = *input;
+static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 vidsource;
+
+ vidsource = (state->input == 1) ? 0x040 : 0x020;
+ switch (norm) {
+ case V4L2_STD_NTSC:
+ write_regs_fp(client, vid_regs_fp);
+ write_reg_fp(client, 0x20, vidsource | 1);
break;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *std = arg;
- u16 vidsource;
-
- vidsource = (dec->input == 1) ? 0x040 : 0x020;
- dec->std = *std;
- switch (dec->std) {
- case V4L2_STD_NTSC:
- write_regs_fp(client, vid_regs_fp);
- write_reg_fp(client, 0x20, vidsource | 1);
- break;
- case V4L2_STD_PAL:
- write_regs_fp(client, vid_regs_fp);
- write_regs_fp(client, vid_regs_fp_pal);
- write_reg_fp(client, 0x20, vidsource);
- break;
- default:
- return -EINVAL;
- }
+ case V4L2_STD_PAL:
+ write_regs_fp(client, vid_regs_fp);
+ write_regs_fp(client, vid_regs_fp_pal);
+ write_reg_fp(client, 0x20, vidsource);
break;
+ default:
+ return -EINVAL;
}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *ctrl = arg;
- static const u32 user_ctrls[] = {
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- 0
- };
- static const u32 *ctrl_classes[] = {
- user_ctrls,
- NULL
- };
-
- ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id);
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
- break;
- case V4L2_CID_CONTRAST:
- v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
- break;
- case V4L2_CID_SATURATION:
- v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
- break;
- case V4L2_CID_HUE:
- v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0);
- break;
- default:
- ctrl->name[0] = '\0';
- return -EINVAL;
- }
- break;
+ state->std = norm;
+ return 0;
+}
+
+static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query)
+{
+ switch (query->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(query, -50, 50, 1, 0);
+ default:
+ return -EINVAL;
}
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- int value1;
- u16 oldvalue;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value > 100)
- dec->brightness = 100;
- else if (ctrl->value < 0)
- dec->brightness = 0;
- else
- dec->brightness = ctrl->value;
- value1 = (dec->brightness - 50) * 255 / 100;
- read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
- value1 | (oldvalue & ~0xff));
- read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
- value1 | (oldvalue & ~0xff));
- write_reg_fp(client, 0x140, 0x60);
- break;
- case V4L2_CID_CONTRAST:
- if (ctrl->value > 100)
- dec->contrast = 100;
- else if (ctrl->value < 0)
- dec->contrast = 0;
- else
- dec->contrast = ctrl->value;
- value1 = dec->contrast * 0x40 / 100;
- if (value1 > 0x3f)
- value1 = 0x3f; /* max */
- read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_CONTRAST0,
- value1 | (oldvalue & ~0x3f));
- read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
- write_reg_fp(client, VPX322_ADDR_CONTRAST1,
- value1 | (oldvalue & ~0x3f));
- write_reg_fp(client, 0x140, 0x60);
- break;
- case V4L2_CID_SATURATION:
- if (ctrl->value > 127)
- dec->saturation = 127;
- else if (ctrl->value < 0)
- dec->saturation = 0;
- else
- dec->saturation = ctrl->value;
-
- value1 = dec->saturation * 4140 / 100;
- if (value1 > 4094)
- value1 = 4094;
- write_reg_fp(client, VPX322_ADDR_SAT, value1);
- break;
- case V4L2_CID_HUE:
- if (ctrl->value > 50)
- dec->hue = 50;
- else if (ctrl->value < -50)
- dec->hue = -50;
- else
- dec->hue = ctrl->value;
- /* clamp the hue range */
- value1 = dec->hue * 280 / 50;
- write_reg_fp(client, VPX322_ADDR_HUE, value1);
- break;
- }
+ return 0;
+}
+
+static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int value1;
+ u16 oldvalue;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value > 100)
+ state->brightness = 100;
+ else if (ctrl->value < 0)
+ state->brightness = 0;
+ else
+ state->brightness = ctrl->value;
+ value1 = (state->brightness - 50) * 255 / 100;
+ read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
+ value1 | (oldvalue & ~0xff));
+ read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
+ value1 | (oldvalue & ~0xff));
+ write_reg_fp(client, 0x140, 0x60);
break;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dec->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = dec->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = dec->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = dec->hue;
- break;
- }
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value > 100)
+ state->contrast = 100;
+ else if (ctrl->value < 0)
+ state->contrast = 0;
+ else
+ state->contrast = ctrl->value;
+ value1 = state->contrast * 0x40 / 100;
+ if (value1 > 0x3f)
+ value1 = 0x3f; /* max */
+ read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_CONTRAST0,
+ value1 | (oldvalue & ~0x3f));
+ read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
+ write_reg_fp(client, VPX322_ADDR_CONTRAST1,
+ value1 | (oldvalue & ~0x3f));
+ write_reg_fp(client, 0x140, 0x60);
break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value > 100)
+ state->saturation = 100;
+ else if (ctrl->value < 0)
+ state->saturation = 0;
+ else
+ state->saturation = ctrl->value;
+ value1 = state->saturation * 4140 / 100;
+ if (value1 > 4094)
+ value1 = 4094;
+ write_reg_fp(client, VPX322_ADDR_SAT, value1);
+ break;
+ case V4L2_CID_HUE:
+ if (ctrl->value > 50)
+ state->hue = 50;
+ else if (ctrl->value < -50)
+ state->hue = -50;
+ else
+ state->hue = ctrl->value;
+ /* clamp the hue range */
+ value1 = state->hue * 280 / 50;
+ write_reg_fp(client, VPX322_ADDR_HUE, value1);
+ break;
+ default:
+ return -EINVAL;
}
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *fmt = arg;
- if (fmt->fmt.pix.height < 640) {
- write_reg_fp(client, 0x12b, dec->reg12b_val | 0x400);
- write_reg_fp(client, 0x140, 0x060);
- } else {
- write_reg_fp(client, 0x12b, dec->reg12b_val & ~0x400);
- write_reg_fp(client, 0x140, 0x060);
- }
- return 0;
- }
- case VIDIOC_G_AUDIO:
- {
- struct v4l2_audio *audio = arg;
+ return 0;
+}
- memset(audio, 0, sizeof(*audio));
- audio->index = dec->audio_input;
- /* fall through */
- }
- case VIDIOC_ENUMAUDIO:
- {
- struct v4l2_audio *audio = arg;
-
- switch (audio->index) {
- case 0:
- strcpy(audio->name, "Line In");
- break;
- case 1:
- strcpy(audio->name, "Mic");
- break;
- case 2:
- strcpy(audio->name, "Mic Boost");
- break;
- default:
- audio->name[0] = '\0';
- return 0;
- }
- audio->capability = V4L2_AUDCAP_STEREO;
- audio->mode = 0;
- return 0;
+static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct s2250 *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = state->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = state->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = state->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = state->hue;
+ break;
+ default:
+ return -EINVAL;
}
- case VIDIOC_S_AUDIO:
- {
- struct v4l2_audio *audio = arg;
-
- switch (audio->index) {
- case 0:
- write_reg(dec->audio, 0x08, 0x02); /* Line In */
- break;
- case 1:
- write_reg(dec->audio, 0x08, 0x04); /* Mic */
- break;
- case 2:
- write_reg(dec->audio, 0x08, 0x05); /* Mic Boost */
- break;
- default:
- return -EINVAL;
- }
- dec->audio_input = audio->index;
- return 0;
+ return 0;
+}
+
+static int s2250_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct s2250 *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (fmt->fmt.pix.height < 640) {
+ write_reg_fp(client, 0x12b, state->reg12b_val | 0x400);
+ write_reg_fp(client, 0x140, 0x060);
+ } else {
+ write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400);
+ write_reg_fp(client, 0x140, 0x060);
}
+ return 0;
+}
- default:
- printk(KERN_INFO "s2250: unknown command 0x%x\n", cmd);
+static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+ u32 config)
+{
+ struct s2250 *state = to_state(sd);
+
+ switch (input) {
+ case 0:
+ write_reg(state->audio, 0x08, 0x02); /* Line In */
+ break;
+ case 1:
+ write_reg(state->audio, 0x08, 0x04); /* Mic */
break;
+ case 2:
+ write_reg(state->audio, 0x08, 0x05); /* Mic Boost */
+ break;
+ default:
+ return -EINVAL;
}
+ state->audio_input = input;
+ return 0;
+}
+
+
+static int s2250_log_status(struct v4l2_subdev *sd)
+{
+ struct s2250 *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" :
+ state->std == V4L2_STD_PAL ? "PAL" :
+ state->std == V4L2_STD_SECAM ? "SECAM" :
+ "unknown");
+ v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" :
+ state->input == 1 ? "S-video" :
+ "error");
+ v4l2_info(sd, "Brightness: %d\n", state->brightness);
+ v4l2_info(sd, "Contrast: %d\n", state->contrast);
+ v4l2_info(sd, "Saturation: %d\n", state->saturation);
+ v4l2_info(sd, "Hue: %d\n", state->hue); return 0;
+ v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" :
+ state->audio_input == 1 ? "Mic" :
+ state->audio_input == 2 ? "Mic Boost" :
+ "error");
return 0;
}
+/* --------------------------------------------------------------------------*/
+
+static const struct v4l2_subdev_core_ops s2250_core_ops = {
+ .log_status = s2250_log_status,
+ .g_ctrl = s2250_g_ctrl,
+ .s_ctrl = s2250_s_ctrl,
+ .queryctrl = s2250_queryctrl,
+ .s_std = s2250_s_std,
+};
+
+static const struct v4l2_subdev_audio_ops s2250_audio_ops = {
+ .s_routing = s2250_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops s2250_video_ops = {
+ .s_routing = s2250_s_video_routing,
+ .s_fmt = s2250_s_fmt,
+};
+
+static const struct v4l2_subdev_ops s2250_ops = {
+ .core = &s2250_core_ops,
+ .audio = &s2250_audio_ops,
+ .video = &s2250_video_ops,
+};
+
+/* --------------------------------------------------------------------------*/
+
static int s2250_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_client *audio;
struct i2c_adapter *adapter = client->adapter;
- struct s2250 *dec;
+ struct s2250 *state;
+ struct v4l2_subdev *sd;
u8 *data;
struct go7007 *go = i2c_get_adapdata(adapter);
struct go7007_usb *usb = go->hpi_context;
@@ -561,30 +580,31 @@ static int s2250_probe(struct i2c_client *client,
if (audio == NULL)
return -ENOMEM;
- dec = kmalloc(sizeof(struct s2250), GFP_KERNEL);
- if (dec == NULL) {
+ state = kmalloc(sizeof(struct s2250), GFP_KERNEL);
+ if (state == NULL) {
i2c_unregister_device(audio);
return -ENOMEM;
}
- dec->std = V4L2_STD_NTSC;
- dec->brightness = 50;
- dec->contrast = 50;
- dec->saturation = 50;
- dec->hue = 0;
- dec->audio = audio;
- i2c_set_clientdata(client, dec);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &s2250_ops);
+
+ v4l2_info(sd, "initializing %s at address 0x%x on %s\n",
+ "Sensoray 2250/2251", client->addr, client->adapter->name);
- printk(KERN_DEBUG
- "s2250: initializing video decoder on %s\n",
- adapter->name);
+ state->std = V4L2_STD_NTSC;
+ state->brightness = 50;
+ state->contrast = 50;
+ state->saturation = 50;
+ state->hue = 0;
+ state->audio = audio;
/* initialize the audio */
if (write_regs(audio, aud_regs) < 0) {
printk(KERN_ERR
"s2250: error initializing audio\n");
i2c_unregister_device(audio);
- kfree(dec);
+ kfree(state);
return 0;
}
@@ -592,14 +612,14 @@ static int s2250_probe(struct i2c_client *client,
printk(KERN_ERR
"s2250: error initializing decoder\n");
i2c_unregister_device(audio);
- kfree(dec);
+ kfree(state);
return 0;
}
if (write_regs_fp(client, vid_regs_fp) < 0) {
printk(KERN_ERR
"s2250: error initializing decoder\n");
i2c_unregister_device(audio);
- kfree(dec);
+ kfree(state);
return 0;
}
/* set default channel */
@@ -609,7 +629,7 @@ static int s2250_probe(struct i2c_client *client,
write_reg_fp(client, 0x140, 0x060);
/* set default audio input */
- dec->audio_input = 0;
+ state->audio_input = 0;
write_reg(client, 0x08, 0x02); /* Line In */
if (mutex_lock_interruptible(&usb->i2c_lock) == 0) {
@@ -634,60 +654,28 @@ static int s2250_probe(struct i2c_client *client,
mutex_unlock(&usb->i2c_lock);
}
- printk("s2250: initialized successfully\n");
+ v4l2_info(sd, "initialized successfully\n");
return 0;
}
static int s2250_remove(struct i2c_client *client)
{
- struct s2250 *dec = i2c_get_clientdata(client);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
- i2c_set_clientdata(client, NULL);
- i2c_unregister_device(dec->audio);
- kfree(dec);
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_state(sd));
return 0;
}
static struct i2c_device_id s2250_id[] = {
- { "s2250_board", 0 },
+ { "s2250", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, s2250_id);
-static struct i2c_driver s2250_driver = {
- .driver = {
- .name = "Sensoray 2250 board driver",
- },
- .probe = s2250_probe,
- .remove = s2250_remove,
- .command = s2250_command,
- .id_table = s2250_id,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "s2250",
+ .probe = s2250_probe,
+ .remove = s2250_remove,
+ .id_table = s2250_id,
};
-
-static int __init s2250_init(void)
-{
- int r;
-
- r = s2250loader_init();
- if (r < 0)
- return r;
-
- r = i2c_add_driver(&s2250_driver);
- if (r < 0)
- s2250loader_cleanup();
-
- return r;
-}
-
-static void __exit s2250_cleanup(void)
-{
- i2c_del_driver(&s2250_driver);
-
- s2250loader_cleanup();
-}
-
-module_init(s2250_init);
-module_exit(s2250_cleanup);
-
-MODULE_AUTHOR("");
-MODULE_DESCRIPTION("Board driver for Sensoryray 2250");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/go7007/s2250-loader.c
index d7bf8298327..c152ab9be2f 100644
--- a/drivers/staging/go7007/s2250-loader.c
+++ b/drivers/staging/go7007/s2250-loader.c
@@ -162,7 +162,7 @@ static struct usb_driver s2250loader_driver = {
.id_table = s2250loader_ids,
};
-int s2250loader_init(void)
+static int __init s2250loader_init(void)
{
int r;
unsigned i = 0;
@@ -179,11 +179,15 @@ int s2250loader_init(void)
printk(KERN_INFO "s2250loader_init: driver registered\n");
return 0;
}
-EXPORT_SYMBOL(s2250loader_init);
+module_init(s2250loader_init);
-void s2250loader_cleanup(void)
+static void __exit s2250loader_cleanup(void)
{
printk(KERN_INFO "s2250loader_cleanup\n");
usb_deregister(&s2250loader_driver);
}
-EXPORT_SYMBOL(s2250loader_cleanup);
+module_exit(s2250loader_cleanup);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/netwave/Kconfig b/drivers/staging/netwave/Kconfig
new file mode 100644
index 00000000000..8033e8171f9
--- /dev/null
+++ b/drivers/staging/netwave/Kconfig
@@ -0,0 +1,11 @@
+config PCMCIA_NETWAVE
+ tristate "Xircom Netwave AirSurfer Pcmcia wireless support"
+ depends on PCMCIA && WLAN
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ help
+ Say Y here if you intend to attach this type of PCMCIA (PC-card)
+ wireless Ethernet networking card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called netwave_cs. If unsure, say N.
diff --git a/drivers/staging/netwave/Makefile b/drivers/staging/netwave/Makefile
new file mode 100644
index 00000000000..2ab89de59b9
--- /dev/null
+++ b/drivers/staging/netwave/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o
diff --git a/drivers/staging/netwave/TODO b/drivers/staging/netwave/TODO
new file mode 100644
index 00000000000..9bd15a2f6d9
--- /dev/null
+++ b/drivers/staging/netwave/TODO
@@ -0,0 +1,7 @@
+TODO:
+ - step up and maintain this driver to ensure that it continues
+ to work. Having the hardware for this is pretty much a
+ requirement. If this does not happen, the will be removed in
+ the 2.6.35 kernel release.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com>.
diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/staging/netwave/netwave_cs.c
index 9498b46c99a..e61e6b9440a 100644
--- a/drivers/net/wireless/netwave_cs.c
+++ b/drivers/staging/netwave/netwave_cs.c
@@ -145,23 +145,6 @@ static const unsigned int txConfEUD = 0x10; /* Enable Uni-Data packets */
static const unsigned int txConfKey = 0x02; /* Scramble data packets */
static const unsigned int txConfLoop = 0x01; /* Loopback mode */
-/*
- All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
- you do not define PCMCIA_DEBUG at all, all the debug code will be
- left out. If you compile with PCMCIA_DEBUG=0, the debug code will
- be present but disabled -- but it can then be enabled for specific
- modules at load time with a 'pc_debug=#' option to insmod.
-*/
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
-#else
-#define DEBUG(n, args...)
-#endif
/*====================================================================*/
@@ -383,7 +366,7 @@ static int netwave_probe(struct pcmcia_device *link)
struct net_device *dev;
netwave_private *priv;
- DEBUG(0, "netwave_attach()\n");
+ dev_dbg(&link->dev, "netwave_attach()\n");
/* Initialize the struct pcmcia_device structure */
dev = alloc_etherdev(sizeof(netwave_private));
@@ -401,8 +384,7 @@ static int netwave_probe(struct pcmcia_device *link)
link->io.IOAddrLines = 5;
/* Interrupt setup */
- link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.Handler = &netwave_interrupt;
/* General socket configuration */
@@ -421,8 +403,6 @@ static int netwave_probe(struct pcmcia_device *link)
dev->watchdog_timeo = TX_TIMEOUT;
- link->irq.Instance = dev;
-
return netwave_pcmcia_config( link);
} /* netwave_attach */
@@ -438,7 +418,7 @@ static void netwave_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- DEBUG(0, "netwave_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "netwave_detach\n");
netwave_release(link);
@@ -725,18 +705,15 @@ static const struct iw_handler_def netwave_handler_def =
*
*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static int netwave_pcmcia_config(struct pcmcia_device *link) {
struct net_device *dev = link->priv;
netwave_private *priv = netdev_priv(dev);
- int i, j, last_ret, last_fn;
+ int i, j, ret;
win_req_t req;
memreq_t mem;
u_char __iomem *ramBase = NULL;
- DEBUG(0, "netwave_pcmcia_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "netwave_pcmcia_config\n");
/*
* Try allocating IO ports. This tries a few fixed addresses.
@@ -749,22 +726,24 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) {
if (i == 0)
break;
}
- if (i != 0) {
- cs_error(link, RequestIO, i);
+ if (i != 0)
goto failed;
- }
/*
* Now allocate an interrupt line. Note that this does not
* actually assign a handler to the interrupt.
*/
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
/*
* This actually configures the PCMCIA socket -- setting up
* the I/O windows and the interrupt mapping.
*/
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
/*
* Allocate a 32K memory window. Note that the struct pcmcia_device
@@ -772,14 +751,18 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) {
* device needs several windows, you'll need to keep track of
* the handles in your private data structure, dev->priv.
*/
- DEBUG(1, "Setting mem speed of %d\n", mem_speed);
+ dev_dbg(&link->dev, "Setting mem speed of %d\n", mem_speed);
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
req.Base = 0; req.Size = 0x8000;
req.AccessSpeed = mem_speed;
- CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win));
+ ret = pcmcia_request_window(link, &req, &link->win);
+ if (ret)
+ goto failed;
mem.CardOffset = 0x20000; mem.Page = 0;
- CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
+ ret = pcmcia_map_mem_page(link, link->win, &mem);
+ if (ret)
+ goto failed;
/* Store base address of the common window frame */
ramBase = ioremap(req.Base, 0x8000);
@@ -787,7 +770,7 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) {
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
if (register_netdev(dev) != 0) {
printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n");
@@ -818,8 +801,6 @@ static int netwave_pcmcia_config(struct pcmcia_device *link) {
get_uint16(ramBase + NETWAVE_EREG_ARW+2));
return 0;
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
netwave_release(link);
return -ENODEV;
@@ -837,7 +818,7 @@ static void netwave_release(struct pcmcia_device *link)
struct net_device *dev = link->priv;
netwave_private *priv = netdev_priv(dev);
- DEBUG(0, "netwave_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "netwave_release\n");
pcmcia_disable_device(link);
if (link->win)
@@ -892,7 +873,7 @@ static void netwave_reset(struct net_device *dev) {
u_char __iomem *ramBase = priv->ramBase;
unsigned int iobase = dev->base_addr;
- DEBUG(0, "netwave_reset: Done with hardware reset\n");
+ pr_debug("netwave_reset: Done with hardware reset\n");
priv->timeoutCounter = 0;
@@ -988,7 +969,7 @@ static int netwave_hw_xmit(unsigned char* data, int len,
dev->stats.tx_bytes += len;
- DEBUG(3, "Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n",
+ pr_debug("Transmitting with SPCQ %x SPU %x LIF %x ISPLQ %x\n",
readb(ramBase + NETWAVE_EREG_SPCQ),
readb(ramBase + NETWAVE_EREG_SPU),
readb(ramBase + NETWAVE_EREG_LIF),
@@ -1000,7 +981,7 @@ static int netwave_hw_xmit(unsigned char* data, int len,
MaxData = get_uint16(ramBase + NETWAVE_EREG_TDP+2);
DataOffset = get_uint16(ramBase + NETWAVE_EREG_TDP+4);
- DEBUG(3, "TxFreeList %x, MaxData %x, DataOffset %x\n",
+ pr_debug("TxFreeList %x, MaxData %x, DataOffset %x\n",
TxFreeList, MaxData, DataOffset);
/* Copy packet to the adapter fragment buffers */
@@ -1088,7 +1069,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id)
status = inb(iobase + NETWAVE_REG_ASR);
if (!pcmcia_dev_present(link)) {
- DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x "
+ pr_debug("netwave_interrupt: Interrupt with status 0x%x "
"from removed or suspended card!\n", status);
break;
}
@@ -1132,7 +1113,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id)
int txStatus;
txStatus = readb(ramBase + NETWAVE_EREG_TSER);
- DEBUG(3, "Transmit done. TSER = %x id %x\n",
+ pr_debug("Transmit done. TSER = %x id %x\n",
txStatus, readb(ramBase + NETWAVE_EREG_TSER + 1));
if (txStatus & 0x20) {
@@ -1156,7 +1137,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id)
* TxGU and TxNOAP is set. (Those are the only ones
* to set TxErr).
*/
- DEBUG(3, "netwave_interrupt: TxDN with error status %x\n",
+ pr_debug("netwave_interrupt: TxDN with error status %x\n",
txStatus);
/* Clear out TxGU, TxNOAP, TxErr and TxTrys */
@@ -1164,7 +1145,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id)
writeb(0xdf & txStatus, ramBase+NETWAVE_EREG_TSER+4);
++dev->stats.tx_errors;
}
- DEBUG(3, "New status is TSER %x ASR %x\n",
+ pr_debug("New status is TSER %x ASR %x\n",
readb(ramBase + NETWAVE_EREG_TSER),
inb(iobase + NETWAVE_REG_ASR));
@@ -1172,7 +1153,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id)
}
/* TxBA, this would trigger on all error packets received */
/* if (status & 0x01) {
- DEBUG(4, "Transmit buffers available, %x\n", status);
+ pr_debug("Transmit buffers available, %x\n", status);
}
*/
}
@@ -1190,7 +1171,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id)
*/
static void netwave_watchdog(struct net_device *dev) {
- DEBUG(1, "%s: netwave_watchdog: watchdog timer expired\n", dev->name);
+ pr_debug("%s: netwave_watchdog: watchdog timer expired\n", dev->name);
netwave_reset(dev);
dev->trans_start = jiffies;
netif_wake_queue(dev);
@@ -1211,7 +1192,7 @@ static int netwave_rx(struct net_device *dev)
int i;
u_char *ptr;
- DEBUG(3, "xinw_rx: Receiving ... \n");
+ pr_debug("xinw_rx: Receiving ... \n");
/* Receive max 10 packets for now. */
for (i = 0; i < 10; i++) {
@@ -1237,7 +1218,7 @@ static int netwave_rx(struct net_device *dev)
skb = dev_alloc_skb(rcvLen+5);
if (skb == NULL) {
- DEBUG(1, "netwave_rx: Could not allocate an sk_buff of "
+ pr_debug("netwave_rx: Could not allocate an sk_buff of "
"length %d\n", rcvLen);
++dev->stats.rx_dropped;
/* Tell the adapter to skip the packet */
@@ -1279,7 +1260,7 @@ static int netwave_rx(struct net_device *dev)
wait_WOC(iobase);
writeb(NETWAVE_CMD_SRP, ramBase + NETWAVE_EREG_CB + 0);
writeb(NETWAVE_CMD_EOC, ramBase + NETWAVE_EREG_CB + 1);
- DEBUG(3, "Packet reception ok\n");
+ pr_debug("Packet reception ok\n");
}
return 0;
}
@@ -1288,7 +1269,7 @@ static int netwave_open(struct net_device *dev) {
netwave_private *priv = netdev_priv(dev);
struct pcmcia_device *link = priv->p_dev;
- DEBUG(1, "netwave_open: starting.\n");
+ dev_dbg(&link->dev, "netwave_open: starting.\n");
if (!pcmcia_dev_present(link))
return -ENODEV;
@@ -1305,7 +1286,7 @@ static int netwave_close(struct net_device *dev) {
netwave_private *priv = netdev_priv(dev);
struct pcmcia_device *link = priv->p_dev;
- DEBUG(1, "netwave_close: finishing.\n");
+ dev_dbg(&link->dev, "netwave_close: finishing.\n");
link->open--;
netif_stop_queue(dev);
@@ -1358,11 +1339,11 @@ static void set_multicast_list(struct net_device *dev)
u_char rcvMode = 0;
#ifdef PCMCIA_DEBUG
- if (pc_debug > 2) {
- static int old;
+ {
+ xstatic int old;
if (old != dev->mc_count) {
old = dev->mc_count;
- DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+ pr_debug("%s: setting Rx mode to %d addresses.\n",
dev->name, dev->mc_count);
}
}
diff --git a/drivers/staging/otus/80211core/pub_zfi.h b/drivers/staging/otus/80211core/pub_zfi.h
index a35bd5d41d2..60b7d1c56de 100644
--- a/drivers/staging/otus/80211core/pub_zfi.h
+++ b/drivers/staging/otus/80211core/pub_zfi.h
@@ -20,7 +20,7 @@
#include "../oal_dt.h"
/***** Section 1 : Tunable Parameters *****/
-/* The defintions in this section are tunabel parameters */
+/* The definitions in this section are tunabel parameters */
/* Maximum number of BSS that could be scaned */
#define ZM_MAX_BSS 128
diff --git a/drivers/staging/otus/zdcompat.h b/drivers/staging/otus/zdcompat.h
index 84ac43356b7..cdcaef54afc 100644
--- a/drivers/staging/otus/zdcompat.h
+++ b/drivers/staging/otus/zdcompat.h
@@ -17,7 +17,7 @@
/* Module Name : zdcompat.h */
/* */
/* Abstract */
-/* This module contains function defintion for compatibility. */
+/* This module contains function definition for compatibility. */
/* */
/* NOTES */
/* Platform dependent. */
diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c
index c94de313922..f69b7783027 100644
--- a/drivers/staging/pohmelfs/inode.c
+++ b/drivers/staging/pohmelfs/inode.c
@@ -143,7 +143,6 @@ static int pohmelfs_writepages(struct address_space *mapping, struct writeback_c
struct inode *inode = mapping->host;
struct pohmelfs_inode *pi = POHMELFS_I(inode);
struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb);
- struct backing_dev_info *bdi = mapping->backing_dev_info;
int err = 0;
int done = 0;
int nr_pages;
@@ -152,11 +151,6 @@ static int pohmelfs_writepages(struct address_space *mapping, struct writeback_c
int scanned = 0;
int range_whole = 0;
- if (wbc->nonblocking && bdi_write_congested(bdi)) {
- wbc->encountered_congestion = 1;
- return 0;
- }
-
if (wbc->range_cyclic) {
index = mapping->writeback_index; /* Start from prev offset */
end = -1;
@@ -248,10 +242,6 @@ retry:
if (wbc->nr_to_write <= 0)
done = 1;
- if (wbc->nonblocking && bdi_write_congested(bdi)) {
- wbc->encountered_congestion = 1;
- done = 1;
- }
continue;
out_continue:
diff --git a/drivers/staging/rtl8187se/Kconfig b/drivers/staging/rtl8187se/Kconfig
index 203c79b8180..3211dd3765a 100644
--- a/drivers/staging/rtl8187se/Kconfig
+++ b/drivers/staging/rtl8187se/Kconfig
@@ -1,6 +1,7 @@
config RTL8187SE
tristate "RealTek RTL8187SE Wireless LAN NIC driver"
depends on PCI && WLAN
- depends on WIRELESS_EXT
+ select WIRELESS_EXT
+ select WEXT_PRIV
default N
---help---
diff --git a/drivers/staging/rtl8187se/r8180.h b/drivers/staging/rtl8187se/r8180.h
index 8216d7e96d6..35ed60be891 100644
--- a/drivers/staging/rtl8187se/r8180.h
+++ b/drivers/staging/rtl8187se/r8180.h
@@ -521,7 +521,7 @@ typedef struct r8180_priv
//u32 NumTxOkInPeriod; //YJ,del,080828
u8 TxPollingTimes;
- bool bApBufOurFrame;// TRUE if AP buffer our unicast data , we will keep eAwake untill receive data or timeout.
+ bool bApBufOurFrame;// TRUE if AP buffer our unicast data , we will keep eAwake until receive data or timeout.
u8 WaitBufDataBcnCount;
u8 WaitBufDataTimeOut;
diff --git a/drivers/staging/rtl8192e/Kconfig b/drivers/staging/rtl8192e/Kconfig
index 37e4fde4507..2ae3745f775 100644
--- a/drivers/staging/rtl8192e/Kconfig
+++ b/drivers/staging/rtl8192e/Kconfig
@@ -1,6 +1,7 @@
config RTL8192E
tristate "RealTek RTL8192E Wireless LAN NIC driver"
depends on PCI && WLAN
- depends on WIRELESS_EXT
+ select WIRELESS_EXT
+ select WEXT_PRIV
default N
---help---
diff --git a/drivers/staging/rtl8192su/r8192S_phyreg.h b/drivers/staging/rtl8192su/r8192S_phyreg.h
index 96c7cfa9254..acf644f430a 100644
--- a/drivers/staging/rtl8192su/r8192S_phyreg.h
+++ b/drivers/staging/rtl8192su/r8192S_phyreg.h
@@ -38,7 +38,7 @@
// 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00
// 3. RF register 0x00-2E
// 4. Bit Mask for BB/RF register
-// 5. Other defintion for BB/RF R/W
+// 5. Other definition for BB/RF R/W
//
diff --git a/drivers/staging/strip/Kconfig b/drivers/staging/strip/Kconfig
new file mode 100644
index 00000000000..36257b5cd6e
--- /dev/null
+++ b/drivers/staging/strip/Kconfig
@@ -0,0 +1,22 @@
+config STRIP
+ tristate "STRIP (Metricom starmode radio IP)"
+ depends on INET
+ select WIRELESS_EXT
+ ---help---
+ Say Y if you have a Metricom radio and intend to use Starmode Radio
+ IP. STRIP is a radio protocol developed for the MosquitoNet project
+ to send Internet traffic using Metricom radios. Metricom radios are
+ small, battery powered, 100kbit/sec packet radio transceivers, about
+ the size and weight of a cellular telephone. (You may also have heard
+ them called "Metricom modems" but we avoid the term "modem" because
+ it misleads many people into thinking that you can plug a Metricom
+ modem into a phone line and use it as a modem.)
+
+ You can use STRIP on any Linux machine with a serial port, although
+ it is obviously most useful for people with laptop computers. If you
+ think you might get a Metricom radio in the future, there is no harm
+ in saying Y to STRIP now, except that it makes the kernel a bit
+ bigger.
+
+ To compile this as a module, choose M here: the module will be
+ called strip.
diff --git a/drivers/staging/strip/Makefile b/drivers/staging/strip/Makefile
new file mode 100644
index 00000000000..6417bdcac2f
--- /dev/null
+++ b/drivers/staging/strip/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_STRIP) += strip.o
diff --git a/drivers/staging/strip/TODO b/drivers/staging/strip/TODO
new file mode 100644
index 00000000000..9bd15a2f6d9
--- /dev/null
+++ b/drivers/staging/strip/TODO
@@ -0,0 +1,7 @@
+TODO:
+ - step up and maintain this driver to ensure that it continues
+ to work. Having the hardware for this is pretty much a
+ requirement. If this does not happen, the will be removed in
+ the 2.6.35 kernel release.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com>.
diff --git a/drivers/net/wireless/strip.c b/drivers/staging/strip/strip.c
index ea6a87c1931..698aade79d4 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/staging/strip/strip.c
@@ -106,6 +106,7 @@ static const char StripVersion[] = "1.3A-STUART.CHESHIRE";
#include <linux/serial.h>
#include <linux/serialP.h>
#include <linux/rcupdate.h>
+#include <linux/compat.h>
#include <net/arp.h>
#include <net/net_namespace.h>
@@ -2725,6 +2726,19 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file,
return 0;
}
+#ifdef CONFIG_COMPAT
+static long strip_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case SIOCGIFNAME:
+ case SIOCSIFHWADDR:
+ return strip_ioctl(tty, file, cmd,
+ (unsigned long)compat_ptr(arg));
+ }
+ return -ENOIOCTLCMD;
+}
+#endif
/************************************************************************/
/* Initialization */
@@ -2736,6 +2750,9 @@ static struct tty_ldisc_ops strip_ldisc = {
.open = strip_open,
.close = strip_close,
.ioctl = strip_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = strip_compat_ioctl,
+#endif
.receive_buf = strip_receive_buf,
.write_wakeup = strip_write_some_more,
};
diff --git a/drivers/staging/vt6655/Kconfig b/drivers/staging/vt6655/Kconfig
index 9bec95adcce..825bbc4fc3f 100644
--- a/drivers/staging/vt6655/Kconfig
+++ b/drivers/staging/vt6655/Kconfig
@@ -1,6 +1,8 @@
config VT6655
tristate "VIA Technologies VT6655 support"
- depends on WIRELESS_EXT && PCI
+ depends on PCI
+ select WIRELESS_EXT
+ select WEXT_PRIV
---help---
This is a vendor-written driver for VIA VT6655.
diff --git a/drivers/staging/vt6656/Kconfig b/drivers/staging/vt6656/Kconfig
index 3165f2c4207..87bcd269310 100644
--- a/drivers/staging/vt6656/Kconfig
+++ b/drivers/staging/vt6656/Kconfig
@@ -1,6 +1,8 @@
config VT6656
tristate "VIA Technologies VT6656 support"
- depends on WIRELESS_EXT && USB
+ depends on USB
+ select WIRELESS_EXT
+ select WEXT_PRIV
---help---
This is a vendor-written driver for VIA VT6656.
diff --git a/drivers/staging/wavelan/Kconfig b/drivers/staging/wavelan/Kconfig
new file mode 100644
index 00000000000..af655668c2a
--- /dev/null
+++ b/drivers/staging/wavelan/Kconfig
@@ -0,0 +1,38 @@
+config WAVELAN
+ tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support"
+ depends on ISA && WLAN
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
+ ---help---
+ The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is
+ a Radio LAN (wireless Ethernet-like Local Area Network) using the
+ radio frequencies 900 MHz and 2.4 GHz.
+
+ If you want to use an ISA WaveLAN card under Linux, say Y and read
+ the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>. Some more specific
+ information is contained in
+ <file:Documentation/networking/wavelan.txt> and in the source code
+ <file:drivers/net/wireless/wavelan.p.h>.
+
+ You will also need the wireless tools package available from
+ <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
+ Please read the man pages contained therein.
+
+ To compile this driver as a module, choose M here: the module will be
+ called wavelan.
+
+config PCMCIA_WAVELAN
+ tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support"
+ depends on PCMCIA && WLAN
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
+ help
+ Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA
+ (PC-card) wireless Ethernet networking card to your computer. This
+ driver is for the non-IEEE-802.11 Wavelan cards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called wavelan_cs. If unsure, say N.
diff --git a/drivers/staging/wavelan/Makefile b/drivers/staging/wavelan/Makefile
new file mode 100644
index 00000000000..1cde17c69a4
--- /dev/null
+++ b/drivers/staging/wavelan/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_WAVELAN) += wavelan.o
+obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o
diff --git a/drivers/staging/wavelan/TODO b/drivers/staging/wavelan/TODO
new file mode 100644
index 00000000000..9bd15a2f6d9
--- /dev/null
+++ b/drivers/staging/wavelan/TODO
@@ -0,0 +1,7 @@
+TODO:
+ - step up and maintain this driver to ensure that it continues
+ to work. Having the hardware for this is pretty much a
+ requirement. If this does not happen, the will be removed in
+ the 2.6.35 kernel release.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com>.
diff --git a/drivers/net/wireless/i82586.h b/drivers/staging/wavelan/i82586.h
index 5f65b250646..5f65b250646 100644
--- a/drivers/net/wireless/i82586.h
+++ b/drivers/staging/wavelan/i82586.h
diff --git a/drivers/net/wireless/wavelan.c b/drivers/staging/wavelan/wavelan.c
index d634b2da3b8..d634b2da3b8 100644
--- a/drivers/net/wireless/wavelan.c
+++ b/drivers/staging/wavelan/wavelan.c
diff --git a/drivers/net/wireless/wavelan.h b/drivers/staging/wavelan/wavelan.h
index 9ab360558ff..9ab360558ff 100644
--- a/drivers/net/wireless/wavelan.h
+++ b/drivers/staging/wavelan/wavelan.h
diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/staging/wavelan/wavelan.p.h
index dbe8de6e5f5..dbe8de6e5f5 100644
--- a/drivers/net/wireless/wavelan.p.h
+++ b/drivers/staging/wavelan/wavelan.p.h
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/staging/wavelan/wavelan_cs.c
index 431a20ec6db..10c702b5be4 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/staging/wavelan/wavelan_cs.c
@@ -3656,10 +3656,7 @@ wv_pcmcia_reset(struct net_device * dev)
i = pcmcia_access_configuration_register(link, &reg);
if (i != 0)
- {
- cs_error(link, AccessConfigurationRegister, i);
return FALSE;
- }
#ifdef DEBUG_CONFIG_INFO
printk(KERN_DEBUG "%s: wavelan_pcmcia_reset(): Config reg is 0x%x\n",
@@ -3670,19 +3667,13 @@ wv_pcmcia_reset(struct net_device * dev)
reg.Value = reg.Value | COR_SW_RESET;
i = pcmcia_access_configuration_register(link, &reg);
if (i != 0)
- {
- cs_error(link, AccessConfigurationRegister, i);
return FALSE;
- }
reg.Action = CS_WRITE;
reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
i = pcmcia_access_configuration_register(link, &reg);
if (i != 0)
- {
- cs_error(link, AccessConfigurationRegister, i);
return FALSE;
- }
#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: <-wv_pcmcia_reset()\n", dev->name);
@@ -3857,10 +3848,7 @@ wv_pcmcia_config(struct pcmcia_device * link)
{
i = pcmcia_request_io(link, &link->io);
if (i != 0)
- {
- cs_error(link, RequestIO, i);
break;
- }
/*
* Now allocate an interrupt line. Note that this does not
@@ -3868,10 +3856,7 @@ wv_pcmcia_config(struct pcmcia_device * link)
*/
i = pcmcia_request_irq(link, &link->irq);
if (i != 0)
- {
- cs_error(link, RequestIRQ, i);
break;
- }
/*
* This actually configures the PCMCIA socket -- setting up
@@ -3880,10 +3865,7 @@ wv_pcmcia_config(struct pcmcia_device * link)
link->conf.ConfigIndex = 1;
i = pcmcia_request_configuration(link, &link->conf);
if (i != 0)
- {
- cs_error(link, RequestConfiguration, i);
break;
- }
/*
* Allocate a small memory window. Note that the struct pcmcia_device
@@ -3894,24 +3876,18 @@ wv_pcmcia_config(struct pcmcia_device * link)
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
req.Base = req.Size = 0;
req.AccessSpeed = mem_speed;
- i = pcmcia_request_window(&link, &req, &link->win);
+ i = pcmcia_request_window(link, &req, &link->win);
if (i != 0)
- {
- cs_error(link, RequestWindow, i);
break;
- }
lp->mem = ioremap(req.Base, req.Size);
dev->mem_start = (u_long)lp->mem;
dev->mem_end = dev->mem_start + req.Size;
mem.CardOffset = 0; mem.Page = 0;
- i = pcmcia_map_mem_page(link->win, &mem);
+ i = pcmcia_map_mem_page(link, link->win, &mem);
if (i != 0)
- {
- cs_error(link, MapMemPage, i);
break;
- }
/* Feed device with this info... */
dev->irq = link->irq.AssignedIRQ;
@@ -3923,7 +3899,7 @@ wv_pcmcia_config(struct pcmcia_device * link)
lp->mem, dev->irq, (u_int) dev->base_addr);
#endif
- SET_NETDEV_DEV(dev, &handle_to_dev(link));
+ SET_NETDEV_DEV(dev, &link->dev);
i = register_netdev(dev);
if(i != 0)
{
@@ -4011,7 +3987,7 @@ wavelan_interrupt(int irq,
#endif
/* Prevent reentrancy. We need to do that because we may have
- * multiple interrupt handler running concurently.
+ * multiple interrupt handler running concurrently.
* It is safe because interrupts are disabled before aquiring
* the spinlock. */
spin_lock(&lp->spinlock);
@@ -4462,8 +4438,7 @@ wavelan_probe(struct pcmcia_device *p_dev)
p_dev->io.IOAddrLines = 3;
/* Interrupt setup */
- p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
- p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
p_dev->irq.Handler = wavelan_interrupt;
/* General socket configuration */
@@ -4475,7 +4450,7 @@ wavelan_probe(struct pcmcia_device *p_dev)
if (!dev)
return -ENOMEM;
- p_dev->priv = p_dev->irq.Instance = dev;
+ p_dev->priv = dev;
lp = netdev_priv(dev);
diff --git a/drivers/net/wireless/wavelan_cs.h b/drivers/staging/wavelan/wavelan_cs.h
index 2e4bfe4147c..2e4bfe4147c 100644
--- a/drivers/net/wireless/wavelan_cs.h
+++ b/drivers/staging/wavelan/wavelan_cs.h
diff --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/staging/wavelan/wavelan_cs.p.h
index 81d91531c4f..8fbfaa8a5a6 100644
--- a/drivers/net/wireless/wavelan_cs.p.h
+++ b/drivers/staging/wavelan/wavelan_cs.p.h
@@ -446,7 +446,7 @@
#include <pcmcia/ds.h>
/* Wavelan declarations */
-#include "i82593.h" /* Definitions for the Intel chip */
+#include <linux/i82593.h> /* Definitions for the Intel chip */
#include "wavelan_cs.h" /* Others bits of the hardware */
diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c
index 40de151f278..e89304c7256 100644
--- a/drivers/telephony/ixj.c
+++ b/drivers/telephony/ixj.c
@@ -4190,7 +4190,7 @@ static void ixj_aec_start(IXJ *j, int level)
ixj_WriteDSPCommand(0x1224, j);
ixj_WriteDSPCommand(0xE014, j);
- ixj_WriteDSPCommand(0x0003, j); /* Lock threashold at 3dB */
+ ixj_WriteDSPCommand(0x0003, j); /* Lock threshold at 3dB */
ixj_WriteDSPCommand(0xE338, j); /* Set Echo Suppresser Attenuation to 0dB */
@@ -4235,7 +4235,7 @@ static void ixj_aec_start(IXJ *j, int level)
ixj_WriteDSPCommand(0x1224, j);
ixj_WriteDSPCommand(0xE014, j);
- ixj_WriteDSPCommand(0x0003, j); /* Lock threashold at 3dB */
+ ixj_WriteDSPCommand(0x0003, j); /* Lock threshold at 3dB */
ixj_WriteDSPCommand(0xE338, j); /* Set Echo Suppresser Attenuation to 0dB */
diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c
index 347c3ed1d9f..d442fd35620 100644
--- a/drivers/telephony/ixj_pcmcia.c
+++ b/drivers/telephony/ixj_pcmcia.c
@@ -19,13 +19,6 @@
* PCMCIA service support for Quicknet cards
*/
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-module_param(pc_debug, int, 0644);
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-#else
-#define DEBUG(n, args...)
-#endif
typedef struct ixj_info_t {
int ndev;
@@ -39,7 +32,7 @@ static void ixj_cs_release(struct pcmcia_device * link);
static int ixj_probe(struct pcmcia_device *p_dev)
{
- DEBUG(0, "ixj_attach()\n");
+ dev_dbg(&p_dev->dev, "ixj_attach()\n");
/* Create new ixj device */
p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
p_dev->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
@@ -55,33 +48,30 @@ static int ixj_probe(struct pcmcia_device *p_dev)
static void ixj_detach(struct pcmcia_device *link)
{
- DEBUG(0, "ixj_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "ixj_detach\n");
ixj_cs_release(link);
kfree(link->priv);
}
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-
static void ixj_get_serial(struct pcmcia_device * link, IXJ * j)
{
char *str;
int i, place;
- DEBUG(0, "ixj_get_serial(0x%p)\n", link);
+ dev_dbg(&link->dev, "ixj_get_serial\n");
str = link->prod_id[0];
if (!str)
- goto cs_failed;
+ goto failed;
printk("%s", str);
str = link->prod_id[1];
if (!str)
- goto cs_failed;
+ goto failed;
printk(" %s", str);
str = link->prod_id[2];
if (!str)
- goto cs_failed;
+ goto failed;
place = 1;
for (i = strlen(str) - 1; i >= 0; i--) {
switch (str[i]) {
@@ -118,9 +108,9 @@ static void ixj_get_serial(struct pcmcia_device * link, IXJ * j)
}
str = link->prod_id[3];
if (!str)
- goto cs_failed;
+ goto failed;
printk(" version %s\n", str);
- cs_failed:
+failed:
return;
}
@@ -151,13 +141,13 @@ static int ixj_config(struct pcmcia_device * link)
cistpl_cftable_entry_t dflt = { 0 };
info = link->priv;
- DEBUG(0, "ixj_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "ixj_config\n");
if (pcmcia_loop_config(link, ixj_config_check, &dflt))
- goto cs_failed;
+ goto failed;
if (pcmcia_request_configuration(link, &link->conf))
- goto cs_failed;
+ goto failed;
/*
* Register the card with the core.
@@ -170,7 +160,7 @@ static int ixj_config(struct pcmcia_device * link)
ixj_get_serial(link, j);
return 0;
- cs_failed:
+failed:
ixj_cs_release(link);
return -ENODEV;
}
@@ -178,7 +168,7 @@ static int ixj_config(struct pcmcia_device * link)
static void ixj_cs_release(struct pcmcia_device *link)
{
ixj_info_t *info = link->priv;
- DEBUG(0, "ixj_cs_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "ixj_cs_release\n");
info->ndev = 0;
pcmcia_disable_device(link);
}
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index d171b563e94..bba4d3eabe0 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -1958,7 +1958,7 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
goto bad1;
/* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
- * the first MEMACESS cmv. Ignore it...
+ * the first MEMACCESS cmv. Ignore it...
*/
if (cmv->bFunction != dsc->function) {
if (UEA_CHIP_VERSION(sc) == ADI930
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 2473cf0c6b1..b4bd2411c66 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -1,5 +1,5 @@
/**
- * drivers/usb/class/usbtmc.c - USB Test & Measurment class driver
+ * drivers/usb/class/usbtmc.c - USB Test & Measurement class driver
*
* Copyright (C) 2007 Stefan Kopp, Gechingen, Germany
* Copyright (C) 2008 Novell, Inc.
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index da718e84d58..e80f1af438c 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1890,7 +1890,7 @@ static void cancel_async_set_config(struct usb_device *udev)
* routine gets around the normal restrictions by using a work thread to
* submit the change-config request.
*
- * Returns 0 if the request was succesfully queued, error code otherwise.
+ * Returns 0 if the request was successfully queued, error code otherwise.
* The caller has no way to know whether the queued request will eventually
* succeed.
*/
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 7953948bfe4..4e3657808b0 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -432,7 +432,7 @@ static void acm_disable(struct usb_function *f)
* @length: size of data
* Context: irqs blocked, acm->lock held, acm_notify_req non-null
*
- * Returns zero on sucess or a negative errno.
+ * Returns zero on success or a negative errno.
*
* See section 6.3.5 of the CDC 1.1 specification for information
* about the only notification we issue: SerialState change.
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index a2db0e174f2..f81e4f025f2 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -52,9 +52,9 @@
#include <asm/unaligned.h>
#include <asm/mach-types.h>
-#include <mach/dma.h>
-#include <mach/usb.h>
-#include <mach/control.h>
+#include <plat/dma.h>
+#include <plat/usb.h>
+#include <plat/control.h>
#include "omap_udc.h"
@@ -2098,6 +2098,7 @@ static inline int machine_without_vbus_sense(void)
|| machine_is_omap_h4()
#endif
|| machine_is_sx1()
+ || cpu_is_omap7xx() /* No known omap7xx boards with vbus sense */
);
}
@@ -2838,6 +2839,16 @@ static int __init omap_udc_probe(struct platform_device *pdev)
udelay(100);
}
+ if (cpu_is_omap7xx()) {
+ dc_clk = clk_get(&pdev->dev, "usb_dc_ck");
+ hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck");
+ BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
+ /* can't use omap_udc_enable_clock yet */
+ clk_enable(dc_clk);
+ clk_enable(hhc_clk);
+ udelay(100);
+ }
+
INFO("OMAP UDC rev %d.%d%s\n",
omap_readw(UDC_REV) >> 4, omap_readw(UDC_REV) & 0xf,
config->otg ? ", Mini-AB" : "");
@@ -2970,7 +2981,7 @@ known:
goto cleanup3;
}
#endif
- if (cpu_is_omap16xx()) {
+ if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
udc->dc_clk = dc_clk;
udc->hhc_clk = hhc_clk;
clk_disable(hhc_clk);
@@ -3008,7 +3019,7 @@ cleanup0:
if (xceiv)
otg_put_transceiver(xceiv);
- if (cpu_is_omap16xx() || cpu_is_omap24xx()) {
+ if (cpu_is_omap16xx() || cpu_is_omap24xx() || cpu_is_omap7xx()) {
clk_disable(hhc_clk);
clk_disable(dc_clk);
clk_put(hhc_clk);
@@ -3115,6 +3126,10 @@ static struct platform_driver udc_driver = {
static int __init udc_init(void)
{
+ /* Disable DMA for omap7xx -- it doesn't work right. */
+ if (cpu_is_omap7xx())
+ use_dma = 0;
+
INFO("%s, version: " DRIVER_VERSION
#ifdef USE_ISO
" (iso)"
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 1937d8c7b43..adda1208a1e 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -1524,7 +1524,7 @@ static int pxa_udc_get_frame(struct usb_gadget *_gadget)
* pxa_udc_wakeup - Force udc device out of suspend
* @_gadget: usb gadget
*
- * Returns 0 if succesfull, error code otherwise
+ * Returns 0 if successfull, error code otherwise
*/
static int pxa_udc_wakeup(struct usb_gadget *_gadget)
{
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index f5f5601701c..d8f4aaa616f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -210,7 +210,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
if (error) {
ehci_halt(ehci);
ehci_to_hcd(ehci)->state = HC_STATE_HALT;
- ehci_err(ehci, "force halt; handhake %p %08x %08x -> %d\n",
+ ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n",
ptr, mask, done, error);
}
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
index 62a226b6167..00a29855d0c 100644
--- a/drivers/usb/host/fhci-sched.c
+++ b/drivers/usb/host/fhci-sched.c
@@ -627,7 +627,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd)
/*
- * Process normal completions(error or sucess) and clean the schedule.
+ * Process normal completions(error or success) and clean the schedule.
*
* This is the main path for handing urbs back to drivers. The only other patth
* is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 83cbecd2a1e..5645f70b921 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -24,10 +24,10 @@
#include <asm/io.h>
#include <asm/mach-types.h>
-#include <mach/mux.h>
+#include <plat/mux.h>
#include <mach/irqs.h>
-#include <mach/fpga.h>
-#include <mach/usb.h>
+#include <plat/fpga.h>
+#include <plat/usb.h>
/* OMAP-1510 OHCI has its own MMU for DMA */
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
index 516848dd9b4..39d253e841f 100644
--- a/drivers/usb/host/sl811_cs.c
+++ b/drivers/usb/host/sl811_cs.c
@@ -37,28 +37,8 @@ MODULE_LICENSE("GPL");
/* MACROS */
/*====================================================================*/
-#if defined(DEBUG) || defined(PCMCIA_DEBUG)
-
-static int pc_debug = 0;
-module_param(pc_debug, int, 0644);
-
-#define DBG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG "sl811_cs: " args)
-
-#else
-#define DBG(n, args...) do{}while(0)
-#endif /* no debugging */
-
#define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
-
-#define CS_CHECK(fn, ret) \
- do { \
- last_fn = (fn); \
- if ((last_ret = (ret)) != 0) \
- goto cs_failed; \
- } while (0)
-
/*====================================================================*/
/* VARIABLES */
/*====================================================================*/
@@ -76,7 +56,7 @@ static void sl811_cs_release(struct pcmcia_device * link);
static void release_platform_dev(struct device * dev)
{
- DBG(0, "sl811_cs platform_dev release\n");
+ dev_dbg(dev, "sl811_cs platform_dev release\n");
dev->parent = NULL;
}
@@ -140,7 +120,7 @@ static int sl811_hc_init(struct device *parent, resource_size_t base_addr,
static void sl811_cs_detach(struct pcmcia_device *link)
{
- DBG(0, "sl811_cs_detach(0x%p)\n", link);
+ dev_dbg(&link->dev, "sl811_cs_detach\n");
sl811_cs_release(link);
@@ -150,7 +130,7 @@ static void sl811_cs_detach(struct pcmcia_device *link)
static void sl811_cs_release(struct pcmcia_device * link)
{
- DBG(0, "sl811_cs_release(0x%p)\n", link);
+ dev_dbg(&link->dev, "sl811_cs_release\n");
pcmcia_disable_device(link);
platform_device_unregister(&platform_dev);
@@ -205,11 +185,11 @@ static int sl811_cs_config_check(struct pcmcia_device *p_dev,
static int sl811_cs_config(struct pcmcia_device *link)
{
- struct device *parent = &handle_to_dev(link);
+ struct device *parent = &link->dev;
local_info_t *dev = link->priv;
- int last_fn, last_ret;
+ int ret;
- DBG(0, "sl811_cs_config(0x%p)\n", link);
+ dev_dbg(&link->dev, "sl811_cs_config\n");
if (pcmcia_loop_config(link, sl811_cs_config_check, NULL))
goto failed;
@@ -217,14 +197,16 @@ static int sl811_cs_config(struct pcmcia_device *link)
/* require an IRQ and two registers */
if (!link->io.NumPorts1 || link->io.NumPorts1 < 2)
goto failed;
- if (link->conf.Attributes & CONF_ENABLE_IRQ)
- CS_CHECK(RequestIRQ,
- pcmcia_request_irq(link, &link->irq));
- else
+ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+ ret = pcmcia_request_irq(link, &link->irq);
+ if (ret)
+ goto failed;
+ } else
goto failed;
- CS_CHECK(RequestConfiguration,
- pcmcia_request_configuration(link, &link->conf));
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto failed;
sprintf(dev->node.dev_name, driver_name);
dev->node.major = dev->node.minor = 0;
@@ -241,8 +223,6 @@ static int sl811_cs_config(struct pcmcia_device *link)
if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ)
< 0) {
-cs_failed:
- cs_error(link, last_fn, last_ret);
failed:
printk(KERN_WARNING "sl811_cs_config failed\n");
sl811_cs_release(link);
@@ -263,7 +243,6 @@ static int sl811_cs_probe(struct pcmcia_device *link)
/* Initialize */
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
- link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
link->irq.Handler = NULL;
link->conf.Attributes = 0;
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 34875201ee0..6761d2088db 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -35,7 +35,7 @@
#include <asm/mach-types.h>
#include <mach/hardware.h>
-#include <mach/mux.h>
+#include <plat/mux.h>
#include "musb_core.h"
#include "omap2430.h"
diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h
index dc7670718cd..fbede7798ae 100644
--- a/drivers/usb/musb/omap2430.h
+++ b/drivers/usb/musb/omap2430.h
@@ -12,7 +12,7 @@
#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430)
#include <mach/hardware.h>
-#include <mach/usb.h>
+#include <plat/usb.h>
/*
* OMAP2430-specific definitions
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index 7e073a0d7ac..e13c77052e5 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -15,8 +15,8 @@
#include <linux/usb.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
-#include <mach/dma.h>
-#include <mach/mux.h>
+#include <plat/dma.h>
+#include <plat/mux.h>
#include "musb_core.h"
diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c
index 77a5f418899..d54460a8817 100644
--- a/drivers/usb/otg/isp1301_omap.c
+++ b/drivers/usb/otg/isp1301_omap.c
@@ -36,8 +36,8 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
-#include <mach/usb.h>
-#include <mach/mux.h>
+#include <plat/usb.h>
+#include <plat/mux.h>
#ifndef DEBUG
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
index 9ec7fd5da48..9579cf4c38b 100644
--- a/drivers/usb/wusbcore/crypto.c
+++ b/drivers/usb/wusbcore/crypto.c
@@ -111,7 +111,7 @@ struct aes_ccm_b1 {
*
* CCM uses Ax blocks to generate a keystream with which the MIC and
* the message's payload are encoded. A0 always encrypts/decrypts the
- * MIC. Ax (x>0) are used for the sucesive payload blocks.
+ * MIC. Ax (x>0) are used for the successive payload blocks.
*
* The x is the counter, and is increased for each block.
*/
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 613a5fc490d..489b47833e2 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -558,7 +558,7 @@ static void wa_seg_dto_cb(struct urb *urb)
/*
* Callback for the segment request
*
- * If succesful transition state (unless already transitioned or
+ * If successful transition state (unless already transitioned or
* outbound transfer); otherwise, take a note of the error, mark this
* segment done and try completion.
*
@@ -1364,7 +1364,7 @@ segment_aborted:
/*
* Callback for the IN data phase
*
- * If succesful transition state; otherwise, take a note of the
+ * If successful transition state; otherwise, take a note of the
* error, mark this segment done and try completion.
*
* Note we don't access until we are sure that the transfer hasn't
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
index c7080d49731..0bb665a0c02 100644
--- a/drivers/uwb/i1480/dfu/usb.c
+++ b/drivers/uwb/i1480/dfu/usb.c
@@ -229,7 +229,7 @@ void i1480_usb_neep_cb(struct urb *urb)
* will verify it.
*
* Set i1480->evt_result with the result of getting the event or its
- * size (if succesful).
+ * size (if successful).
*
* Delivers the data directly to i1480->evt_buf
*/
diff --git a/drivers/uwb/neh.c b/drivers/uwb/neh.c
index 0af8916d9be..78510a1f410 100644
--- a/drivers/uwb/neh.c
+++ b/drivers/uwb/neh.c
@@ -150,7 +150,7 @@ void uwb_rc_neh_put(struct uwb_rc_neh *neh)
* 0xff is invalid, so they must not be used. Initialization
* fills up those two in the bitmap so they are not allocated.
*
- * We spread the allocation around to reduce the posiblity of two
+ * We spread the allocation around to reduce the possibility of two
* consecutive opened @neh's getting the same context ID assigned (to
* avoid surprises with late events that timed out long time ago). So
* first we search from where @rc->ctx_roll is, if not found, we
diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c
index 86a853b8411..7350ed6909f 100644
--- a/drivers/uwb/wlp/txrx.c
+++ b/drivers/uwb/wlp/txrx.c
@@ -282,7 +282,7 @@ EXPORT_SYMBOL_GPL(wlp_receive_frame);
* and transmission will be done by the calling function.
* @dst: On return this will contain the device address to which the
* frame is destined.
- * @returns: 0 on success no tx : WLP header sucessfully applied to skb buffer,
+ * @returns: 0 on success no tx : WLP header successfully applied to skb buffer,
* calling function can proceed with tx
* 1 on success with tx : WLP will take over transmission of this
* frame
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 188e1ba3b69..bb5fbed89e7 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -5,6 +5,9 @@
menu "Graphics support"
depends on HAS_IOMEM
+config HAVE_FB_ATMEL
+ bool
+
source "drivers/char/agp/Kconfig"
source "drivers/gpu/vga/Kconfig"
@@ -937,7 +940,7 @@ config FB_S1D13XXX
config FB_ATMEL
tristate "AT91/AT32 LCD Controller support"
- depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9G10 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 || ARCH_AT91CAP9 || AVR32)
+ depends on FB && HAVE_FB_ATMEL
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -2063,6 +2066,7 @@ config XEN_FBDEV_FRONTEND
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
+ select XEN_XENBUS_FRONTEND
default y
help
This driver implements the front-end of the Xen virtual
@@ -2161,6 +2165,7 @@ config FB_BROADSHEET
a bridge adapter.
source "drivers/video/omap/Kconfig"
+source "drivers/video/omap2/Kconfig"
source "drivers/video/backlight/Kconfig"
source "drivers/video/display/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 80232e12488..0f8da331ba0 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -124,6 +124,7 @@ obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
obj-$(CONFIG_FB_OMAP) += omap/
+obj-y += omap2/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
obj-$(CONFIG_FB_CARMINE) += carminefb.o
obj-$(CONFIG_FB_MB862XX) += mb862xx/
diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c
index 37624f74e88..b7687c55fe1 100644
--- a/drivers/video/atafb.c
+++ b/drivers/video/atafb.c
@@ -2242,6 +2242,9 @@ static int ext_setcolreg(unsigned int regno, unsigned int red,
if (!external_vgaiobase)
return 1;
+ if (regno > 255)
+ return 1;
+
switch (external_card_type) {
case IS_VGA:
OUTB(0x3c8, regno);
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index d5e801076d3..3d886c6902f 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -964,7 +964,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
if (sinfo->atmel_lcdfb_power_control)
sinfo->atmel_lcdfb_power_control(1);
- dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n",
+ dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %d\n",
info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base);
return 0;
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 913b4a47ae5..1ddeb4c3476 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -3276,7 +3276,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base)
txtformat = "24 bit interface";
break;
default:
- txtformat = "unkown format";
+ txtformat = "unknown format";
}
} else {
switch (format & 7) {
@@ -3299,7 +3299,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base)
txtformat = "262144 colours (FDPI-2 mode)";
break;
default:
- txtformat = "unkown format";
+ txtformat = "unknown format";
}
}
PRINTKI("%s%s %s monitor detected: %s\n",
diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c
index 505c0823a10..2cf7ba52f67 100644
--- a/drivers/video/backlight/atmel-pwm-bl.c
+++ b/drivers/video/backlight/atmel-pwm-bl.c
@@ -158,7 +158,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
goto err_free_pwm;
}
- /* Turn display off by defatult. */
+ /* Turn display off by default. */
retval = gpio_direction_output(pwmbl->gpio_on,
0 ^ pdata->on_active_low);
if (retval)
diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c
index 701a1081e19..7fcb0eb54c6 100644
--- a/drivers/video/backlight/da903x_bl.c
+++ b/drivers/video/backlight/da903x_bl.c
@@ -25,6 +25,7 @@
#define DA9034_WLED_CONTROL1 0x3C
#define DA9034_WLED_CONTROL2 0x3D
+#define DA9034_WLED_ISET(x) ((x) & 0x1f)
#define DA9034_WLED_BOOST_EN (1 << 5)
@@ -101,6 +102,7 @@ static struct backlight_ops da903x_backlight_ops = {
static int da903x_backlight_probe(struct platform_device *pdev)
{
+ struct da9034_backlight_pdata *pdata = pdev->dev.platform_data;
struct da903x_backlight_data *data;
struct backlight_device *bl;
int max_brightness;
@@ -127,6 +129,11 @@ static int da903x_backlight_probe(struct platform_device *pdev)
data->da903x_dev = pdev->dev.parent;
data->current_brightness = 0;
+ /* adjust the WLED output current */
+ if (pdata)
+ da903x_write(data->da903x_dev, DA9034_WLED_CONTROL2,
+ DA9034_WLED_ISET(pdata->output_current));
+
bl = backlight_device_register(pdev->name, data->da903x_dev,
data, &da903x_backlight_ops);
if (IS_ERR(bl)) {
diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c
index cbad67e8982..8693e5fcd2e 100644
--- a/drivers/video/backlight/omap1_bl.c
+++ b/drivers/video/backlight/omap1_bl.c
@@ -26,8 +26,8 @@
#include <linux/backlight.h>
#include <mach/hardware.h>
-#include <mach/board.h>
-#include <mach/mux.h>
+#include <plat/board.h>
+#include <plat/mux.h>
#define OMAPBL_MAX_INTENSITY 0xff
diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c
index bbfb502add6..4a3d46e0801 100644
--- a/drivers/video/backlight/tdo24m.c
+++ b/drivers/video/backlight/tdo24m.c
@@ -367,6 +367,7 @@ static int __devinit tdo24m_probe(struct spi_device *spi)
spi_message_init(m);
+ x->cs_change = 1;
x->tx_buf = &lcd->buf[0];
spi_message_add_tail(x, m);
diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c
index 50ec17dfc51..fa32b94a454 100644
--- a/drivers/video/backlight/tosa_lcd.c
+++ b/drivers/video/backlight/tosa_lcd.c
@@ -177,7 +177,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
if (!data)
return -ENOMEM;
- data->is_vga = true; /* defaut to VGA mode */
+ data->is_vga = true; /* default to VGA mode */
/*
* bits_per_word cannot be configured in platform data
diff --git a/drivers/video/broadsheetfb.c b/drivers/video/broadsheetfb.c
index 509cb92e873..df9ccb901d8 100644
--- a/drivers/video/broadsheetfb.c
+++ b/drivers/video/broadsheetfb.c
@@ -470,7 +470,7 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev)
par->read_reg = broadsheet_read_reg;
init_waitqueue_head(&par->waitq);
- info->flags = FBINFO_FLAG_DEFAULT;
+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
info->fbdefio = &broadsheetfb_defio;
fb_deferred_io_init(info);
diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c
index 857b3668b3b..6468a297e34 100644
--- a/drivers/video/console/sticore.c
+++ b/drivers/video/console/sticore.c
@@ -436,7 +436,7 @@ sti_init_glob_cfg(struct sti_struct *sti,
(offs < PCI_BASE_ADDRESS_0 ||
offs > PCI_BASE_ADDRESS_5)) {
printk (KERN_WARNING
- "STI pci region maping for region %d (%02x) can't be mapped\n",
+ "STI pci region mapping for region %d (%02x) can't be mapped\n",
i,sti->rm_entry[i]);
continue;
}
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index da55ccaf4d5..cc4bbbe44ac 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -585,6 +585,11 @@ static void vgacon_init(struct vc_data *c, int init)
vgacon_uni_pagedir[1]++;
if (!vgacon_uni_pagedir[0] && p)
con_set_default_unimap(c);
+
+ /* Only set the default if the user didn't deliberately override it */
+ if (global_cursor_default == -1)
+ global_cursor_default =
+ !(screen_info.flags & VIDEO_FLAGS_NOCURSOR);
}
static void vgacon_deinit(struct vc_data *c)
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index c27ab1ed960..44ce908a478 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -71,7 +71,7 @@ int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync)
{
struct fb_info *info = file->private_data;
- /* Skip if deferred io is complied-in but disabled on this fbdev */
+ /* Skip if deferred io is compiled-in but disabled on this fbdev */
if (!info->fbdefio)
return 0;
@@ -144,7 +144,9 @@ static const struct address_space_operations fb_deferred_io_aops = {
static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
vma->vm_ops = &fb_deferred_io_vm_ops;
- vma->vm_flags |= ( VM_IO | VM_RESERVED | VM_DONTEXPAND );
+ vma->vm_flags |= ( VM_RESERVED | VM_DONTEXPAND );
+ if (!(info->flags & FBINFO_VIRTFB))
+ vma->vm_flags |= VM_IO;
vma->vm_private_data = info;
return 0;
}
diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c
index f67db426837..695fa013fe7 100644
--- a/drivers/video/gbefb.c
+++ b/drivers/video/gbefb.c
@@ -701,7 +701,7 @@ static int gbefb_set_par(struct fb_info *info)
blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit,
16bit and 32 bit modes (64 kB). They cover the screen with partial
tiles on the right and/or bottom of the screen if needed.
- For exemple in 640x480 8 bit mode the mapping is:
+ For example in 640x480 8 bit mode the mapping is:
<-------- 640 ----->
<---- 512 ----><128|384 offscreen>
diff --git a/drivers/video/hecubafb.c b/drivers/video/hecubafb.c
index 0b4bffbe67c..f9d77adf035 100644
--- a/drivers/video/hecubafb.c
+++ b/drivers/video/hecubafb.c
@@ -253,7 +253,7 @@ static int __devinit hecubafb_probe(struct platform_device *dev)
par->send_command = apollo_send_command;
par->send_data = apollo_send_data;
- info->flags = FBINFO_FLAG_DEFAULT;
+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
info->fbdefio = &hecubafb_defio;
fb_deferred_io_init(info);
diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c
index df1f757a616..661bfd20d19 100644
--- a/drivers/video/metronomefb.c
+++ b/drivers/video/metronomefb.c
@@ -700,7 +700,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
if (retval < 0)
goto err_free_irq;
- info->flags = FBINFO_FLAG_DEFAULT;
+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
info->fbdefio = &metronomefb_defio;
fb_deferred_io_init(info);
diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig
index 551e3e9c4cb..455c6055325 100644
--- a/drivers/video/omap/Kconfig
+++ b/drivers/video/omap/Kconfig
@@ -1,6 +1,7 @@
config FB_OMAP
tristate "OMAP frame buffer support (EXPERIMENTAL)"
- depends on FB && ARCH_OMAP
+ depends on FB && ARCH_OMAP && (OMAP2_DSS = "n")
+
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -72,7 +73,7 @@ config FB_OMAP_LCD_MIPID
config FB_OMAP_BOOTLOADER_INIT
bool "Check bootloader initialization"
- depends on FB_OMAP
+ depends on FB_OMAP || FB_OMAP2
help
Say Y here if you want to enable checking if the bootloader has
already initialized the display controller. In this case the
diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile
index b63b198d1f0..49226a1b909 100644
--- a/drivers/video/omap/Makefile
+++ b/drivers/video/omap/Makefile
@@ -35,6 +35,7 @@ objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o
objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o
objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
objs-y$(CONFIG_MACH_OVERO) += lcd_overo.o
+objs-y$(CONFIG_MACH_HERALD) += lcd_htcherald.o
omapfb-objs := $(objs-yy)
diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c
index 70dadf9d233..2ffb34af4c5 100644
--- a/drivers/video/omap/blizzard.c
+++ b/drivers/video/omap/blizzard.c
@@ -26,10 +26,10 @@
#include <linux/delay.h>
#include <linux/clk.h>
-#include <mach/dma.h>
-#include <mach/omapfb.h>
-#include <mach/blizzard.h>
+#include <plat/dma.h>
+#include <plat/blizzard.h>
+#include "omapfb.h"
#include "dispc.h"
#define MODULE_NAME "blizzard"
diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c
index f16e4215422..c7c6455f1fa 100644
--- a/drivers/video/omap/dispc.c
+++ b/drivers/video/omap/dispc.c
@@ -24,11 +24,12 @@
#include <linux/vmalloc.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/platform_device.h>
-#include <mach/sram.h>
-#include <mach/omapfb.h>
-#include <mach/board.h>
+#include <plat/sram.h>
+#include <plat/board.h>
+#include "omapfb.h"
#include "dispc.h"
#define MODULE_NAME "dispc"
@@ -188,6 +189,11 @@ static struct {
struct omapfb_color_key color_key;
} dispc;
+static struct platform_device omapdss_device = {
+ .name = "omapdss",
+ .id = -1,
+};
+
static void enable_lcd_clocks(int enable);
static void inline dispc_write_reg(int idx, u32 val)
@@ -204,6 +210,7 @@ static u32 inline dispc_read_reg(int idx)
/* Select RFBI or bypass mode */
static void enable_rfbi_mode(int enable)
{
+ void __iomem *rfbi_control;
u32 l;
l = dispc_read_reg(DISPC_CONTROL);
@@ -216,9 +223,15 @@ static void enable_rfbi_mode(int enable)
dispc_write_reg(DISPC_CONTROL, l);
/* Set bypass mode in RFBI module */
- l = __raw_readl(OMAP2_IO_ADDRESS(RFBI_CONTROL));
+ rfbi_control = ioremap(RFBI_CONTROL, SZ_1K);
+ if (!rfbi_control) {
+ pr_err("Unable to ioremap rfbi_control\n");
+ return;
+ }
+ l = __raw_readl(rfbi_control);
l |= enable ? 0 : (1 << 1);
- __raw_writel(l, OMAP2_IO_ADDRESS(RFBI_CONTROL));
+ __raw_writel(l, rfbi_control);
+ iounmap(rfbi_control);
}
static void set_lcd_data_lines(int data_lines)
@@ -907,20 +920,20 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
static int get_dss_clocks(void)
{
- dispc.dss_ick = clk_get(dispc.fbdev->dev, "ick");
+ dispc.dss_ick = clk_get(&omapdss_device.dev, "ick");
if (IS_ERR(dispc.dss_ick)) {
dev_err(dispc.fbdev->dev, "can't get ick\n");
return PTR_ERR(dispc.dss_ick);
}
- dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck");
+ dispc.dss1_fck = clk_get(&omapdss_device.dev, "dss1_fck");
if (IS_ERR(dispc.dss1_fck)) {
dev_err(dispc.fbdev->dev, "can't get dss1_fck\n");
clk_put(dispc.dss_ick);
return PTR_ERR(dispc.dss1_fck);
}
- dispc.dss_54m_fck = clk_get(dispc.fbdev->dev, "tv_fck");
+ dispc.dss_54m_fck = clk_get(&omapdss_device.dev, "tv_fck");
if (IS_ERR(dispc.dss_54m_fck)) {
dev_err(dispc.fbdev->dev, "can't get tv_fck\n");
clk_put(dispc.dss_ick);
@@ -1367,10 +1380,17 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
int r;
u32 l;
struct lcd_panel *panel = fbdev->panel;
+ void __iomem *ram_fw_base;
int tmo = 10000;
int skip_init = 0;
int i;
+ r = platform_device_register(&omapdss_device);
+ if (r) {
+ dev_err(fbdev->dev, "can't register omapdss device\n");
+ return r;
+ }
+
memset(&dispc, 0, sizeof(dispc));
dispc.base = ioremap(DISPC_BASE, SZ_1K);
@@ -1441,7 +1461,13 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
}
/* L3 firewall setting: enable access to OCM RAM */
- __raw_writel(0x402000b0, OMAP2_IO_ADDRESS(0x680050a0));
+ ram_fw_base = ioremap(0x68005000, SZ_1K);
+ if (!ram_fw_base) {
+ dev_err(dispc.fbdev->dev, "Cannot ioremap to enable OCM RAM\n");
+ goto fail1;
+ }
+ __raw_writel(0x402000b0, ram_fw_base + 0xa0);
+ iounmap(ram_fw_base);
if ((r = alloc_palette_ram()) < 0)
goto fail2;
@@ -1508,6 +1534,7 @@ static void omap_dispc_cleanup(void)
free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
put_dss_clocks();
iounmap(dispc.base);
+ platform_device_unregister(&omapdss_device);
}
const struct lcd_ctrl omap2_int_ctrl = {
diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c
index ca51583ec98..0016f77cd13 100644
--- a/drivers/video/omap/hwa742.c
+++ b/drivers/video/omap/hwa742.c
@@ -25,10 +25,11 @@
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/interrupt.h>
-#include <mach/dma.h>
-#include <mach/omapfb.h>
-#include <mach/hwa742.h>
+#include <plat/dma.h>
+#include <plat/hwa742.h>
+#include "omapfb.h"
#define HWA742_REV_CODE_REG 0x0
#define HWA742_CONFIG_REG 0x2
diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c
index 393712b6f36..760645d9dbb 100644
--- a/drivers/video/omap/lcd_2430sdp.c
+++ b/drivers/video/omap/lcd_2430sdp.c
@@ -27,10 +27,11 @@
#include <linux/gpio.h>
#include <linux/i2c/twl4030.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
#include <asm/mach-types.h>
+#include "omapfb.h"
+
#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91
#define SDP2430_LCD_PANEL_ENABLE_GPIO 154
#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24
diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c
index 1f7439955e0..567db6ac32c 100644
--- a/drivers/video/omap/lcd_ams_delta.c
+++ b/drivers/video/omap/lcd_ams_delta.c
@@ -25,9 +25,10 @@
#include <linux/io.h>
#include <linux/delay.h>
-#include <mach/board-ams-delta.h>
+#include <plat/board-ams-delta.h>
#include <mach/hardware.h>
-#include <mach/omapfb.h>
+
+#include "omapfb.h"
#define AMS_DELTA_DEFAULT_CONTRAST 112
@@ -123,12 +124,12 @@ struct platform_driver ams_delta_panel_driver = {
},
};
-static int ams_delta_panel_drv_init(void)
+static int __init ams_delta_panel_drv_init(void)
{
return platform_driver_register(&ams_delta_panel_driver);
}
-static void ams_delta_panel_drv_cleanup(void)
+static void __exit ams_delta_panel_drv_cleanup(void)
{
platform_driver_unregister(&ams_delta_panel_driver);
}
diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c
index 626ae3a532f..2be94eb3bbf 100644
--- a/drivers/video/omap/lcd_apollon.c
+++ b/drivers/video/omap/lcd_apollon.c
@@ -25,8 +25,9 @@
#include <linux/platform_device.h>
#include <mach/gpio.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
+
+#include "omapfb.h"
/* #define USE_35INCH_LCD 1 */
diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c
index 417ae5efa8b..8df688748b5 100644
--- a/drivers/video/omap/lcd_h3.c
+++ b/drivers/video/omap/lcd_h3.c
@@ -24,7 +24,7 @@
#include <linux/i2c/tps65010.h>
#include <mach/gpio.h>
-#include <mach/omapfb.h>
+#include "omapfb.h"
#define MODULE_NAME "omapfb-lcd_h3"
diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c
index 0c398bda760..03a06a98275 100644
--- a/drivers/video/omap/lcd_h4.c
+++ b/drivers/video/omap/lcd_h4.c
@@ -22,7 +22,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <mach/omapfb.h>
+#include "omapfb.h"
static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
{
diff --git a/drivers/video/omap/lcd_htcherald.c b/drivers/video/omap/lcd_htcherald.c
new file mode 100644
index 00000000000..a9007c5d1fa
--- /dev/null
+++ b/drivers/video/omap/lcd_htcherald.c
@@ -0,0 +1,130 @@
+/*
+ * File: drivers/video/omap/lcd-htcherald.c
+ *
+ * LCD panel support for the HTC Herald
+ *
+ * Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com>
+ * Copyright (C) 2009 Wing Linux
+ *
+ * Based on the lcd_htcwizard.c file from the linwizard project:
+ * Copyright (C) linwizard.sourceforge.net
+ * Author: Angelo Arrifano <miknix@gmail.com>
+ * Based on lcd_h4 by Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "omapfb.h"
+
+static int htcherald_panel_init(struct lcd_panel *panel,
+ struct omapfb_device *fbdev)
+{
+ return 0;
+}
+
+static void htcherald_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int htcherald_panel_enable(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+static void htcherald_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long htcherald_panel_get_caps(struct lcd_panel *panel)
+{
+ return 0;
+}
+
+/* Found on WIZ200 (miknix) and some HERA110 models (darkstar62) */
+struct lcd_panel htcherald_panel_1 = {
+ .name = "lcd_herald",
+ .config = OMAP_LCDC_PANEL_TFT |
+ OMAP_LCDC_INV_HSYNC |
+ OMAP_LCDC_INV_VSYNC |
+ OMAP_LCDC_INV_PIX_CLOCK,
+ .bpp = 16,
+ .data_lines = 16,
+ .x_res = 240,
+ .y_res = 320,
+ .pixel_clock = 6093,
+ .pcd = 0, /* 15 */
+ .hsw = 10,
+ .hfp = 10,
+ .hbp = 20,
+ .vsw = 3,
+ .vfp = 2,
+ .vbp = 2,
+
+ .init = htcherald_panel_init,
+ .cleanup = htcherald_panel_cleanup,
+ .enable = htcherald_panel_enable,
+ .disable = htcherald_panel_disable,
+ .get_caps = htcherald_panel_get_caps,
+};
+
+static int htcherald_panel_probe(struct platform_device *pdev)
+{
+ omapfb_register_panel(&htcherald_panel_1);
+ return 0;
+}
+
+static int htcherald_panel_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int htcherald_panel_suspend(struct platform_device *pdev,
+ pm_message_t mesg)
+{
+ return 0;
+}
+
+static int htcherald_panel_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver htcherald_panel_driver = {
+ .probe = htcherald_panel_probe,
+ .remove = htcherald_panel_remove,
+ .suspend = htcherald_panel_suspend,
+ .resume = htcherald_panel_resume,
+ .driver = {
+ .name = "lcd_htcherald",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int htcherald_panel_drv_init(void)
+{
+ return platform_driver_register(&htcherald_panel_driver);
+}
+
+static void htcherald_panel_drv_cleanup(void)
+{
+ platform_driver_unregister(&htcherald_panel_driver);
+}
+
+module_init(htcherald_panel_drv_init);
+module_exit(htcherald_panel_drv_cleanup);
+
diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c
index cdbd8bb607b..3271f1643b2 100644
--- a/drivers/video/omap/lcd_inn1510.c
+++ b/drivers/video/omap/lcd_inn1510.c
@@ -23,8 +23,8 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <mach/fpga.h>
-#include <mach/omapfb.h>
+#include <plat/fpga.h>
+#include "omapfb.h"
static int innovator1510_panel_init(struct lcd_panel *panel,
struct omapfb_device *fbdev)
diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c
index 268f7f808a4..9fff86f67bd 100644
--- a/drivers/video/omap/lcd_inn1610.c
+++ b/drivers/video/omap/lcd_inn1610.c
@@ -23,7 +23,7 @@
#include <linux/platform_device.h>
#include <mach/gpio.h>
-#include <mach/omapfb.h>
+#include "omapfb.h"
#define MODULE_NAME "omapfb-lcd_h3"
diff --git a/drivers/video/omap/lcd_ldp.c b/drivers/video/omap/lcd_ldp.c
index dbfe8974fb9..5bb7f6f1460 100644
--- a/drivers/video/omap/lcd_ldp.c
+++ b/drivers/video/omap/lcd_ldp.c
@@ -27,10 +27,11 @@
#include <linux/i2c/twl4030.h>
#include <mach/gpio.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
#include <asm/mach-types.h>
+#include "omapfb.h"
+
#define LCD_PANEL_BACKLIGHT_GPIO (15 + OMAP_MAX_GPIO_LINES)
#define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES)
diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c
index 918ee893419..abe1c76a325 100644
--- a/drivers/video/omap/lcd_mipid.c
+++ b/drivers/video/omap/lcd_mipid.c
@@ -23,8 +23,9 @@
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
-#include <mach/omapfb.h>
-#include <mach/lcd_mipid.h>
+#include <plat/lcd_mipid.h>
+
+#include "omapfb.h"
#define MIPID_MODULE_NAME "lcd_mipid"
@@ -607,7 +608,7 @@ static struct spi_driver mipid_spi_driver = {
.remove = __devexit_p(mipid_spi_remove),
};
-static int mipid_drv_init(void)
+static int __init mipid_drv_init(void)
{
spi_register_driver(&mipid_spi_driver);
@@ -615,7 +616,7 @@ static int mipid_drv_init(void)
}
module_init(mipid_drv_init);
-static void mipid_drv_cleanup(void)
+static void __exit mipid_drv_cleanup(void)
{
spi_unregister_driver(&mipid_spi_driver);
}
diff --git a/drivers/video/omap/lcd_omap2evm.c b/drivers/video/omap/lcd_omap2evm.c
index 7a2bbe2ecec..006c2fe7360 100644
--- a/drivers/video/omap/lcd_omap2evm.c
+++ b/drivers/video/omap/lcd_omap2evm.c
@@ -26,10 +26,11 @@
#include <linux/gpio.h>
#include <linux/i2c/twl4030.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
#include <asm/mach-types.h>
+#include "omapfb.h"
+
#define LCD_PANEL_ENABLE_GPIO 154
#define LCD_PANEL_LR 128
#define LCD_PANEL_UD 129
diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c
index 4011910123b..fc503d8f3c2 100644
--- a/drivers/video/omap/lcd_omap3beagle.c
+++ b/drivers/video/omap/lcd_omap3beagle.c
@@ -25,10 +25,12 @@
#include <linux/gpio.h>
#include <linux/i2c/twl4030.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
+#include <plat/mux.h>
#include <asm/mach-types.h>
+#include "omapfb.h"
+
#define LCD_PANEL_ENABLE_GPIO 170
static int omap3beagle_panel_init(struct lcd_panel *panel,
diff --git a/drivers/video/omap/lcd_omap3evm.c b/drivers/video/omap/lcd_omap3evm.c
index b6a4c2c57a2..ae2edc4081a 100644
--- a/drivers/video/omap/lcd_omap3evm.c
+++ b/drivers/video/omap/lcd_omap3evm.c
@@ -25,10 +25,11 @@
#include <linux/gpio.h>
#include <linux/i2c/twl4030.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
#include <asm/mach-types.h>
+#include "omapfb.h"
+
#define LCD_PANEL_ENABLE_GPIO 153
#define LCD_PANEL_LR 2
#define LCD_PANEL_UD 3
diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c
index b3fa88bc626..b87e8b83f29 100644
--- a/drivers/video/omap/lcd_osk.c
+++ b/drivers/video/omap/lcd_osk.c
@@ -24,8 +24,8 @@
#include <linux/platform_device.h>
#include <mach/gpio.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
+#include "omapfb.h"
static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
{
diff --git a/drivers/video/omap/lcd_overo.c b/drivers/video/omap/lcd_overo.c
index 2bc5c9268e5..56ee192e9ee 100644
--- a/drivers/video/omap/lcd_overo.c
+++ b/drivers/video/omap/lcd_overo.c
@@ -24,10 +24,11 @@
#include <linux/i2c/twl4030.h>
#include <mach/gpio.h>
-#include <mach/mux.h>
-#include <mach/omapfb.h>
+#include <plat/mux.h>
#include <asm/mach-types.h>
+#include "omapfb.h"
+
#define LCD_ENABLE 144
static int overo_panel_init(struct lcd_panel *panel,
diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c
index 4bf3c79f3cc..4cb301750d0 100644
--- a/drivers/video/omap/lcd_palmte.c
+++ b/drivers/video/omap/lcd_palmte.c
@@ -23,8 +23,8 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <mach/fpga.h>
-#include <mach/omapfb.h>
+#include <plat/fpga.h>
+#include "omapfb.h"
static int palmte_panel_init(struct lcd_panel *panel,
struct omapfb_device *fbdev)
diff --git a/drivers/video/omap/lcd_palmtt.c b/drivers/video/omap/lcd_palmtt.c
index 48ea1f9f2cb..ff0e6d7ab3a 100644
--- a/drivers/video/omap/lcd_palmtt.c
+++ b/drivers/video/omap/lcd_palmtt.c
@@ -30,7 +30,7 @@ GPIO13 - screen blanking
#include <linux/io.h>
#include <mach/gpio.h>
-#include <mach/omapfb.h>
+#include "omapfb.h"
static int palmtt_panel_init(struct lcd_panel *panel,
struct omapfb_device *fbdev)
diff --git a/drivers/video/omap/lcd_palmz71.c b/drivers/video/omap/lcd_palmz71.c
index 0697d29b4d3..2334e56536b 100644
--- a/drivers/video/omap/lcd_palmz71.c
+++ b/drivers/video/omap/lcd_palmz71.c
@@ -24,7 +24,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <mach/omapfb.h>
+#include "omapfb.h"
static int palmz71_panel_init(struct lcd_panel *panel,
struct omapfb_device *fbdev)
diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c
index ab394925667..b831e1df629 100644
--- a/drivers/video/omap/lcdc.c
+++ b/drivers/video/omap/lcdc.c
@@ -29,11 +29,12 @@
#include <linux/vmalloc.h>
#include <linux/clk.h>
-#include <mach/dma.h>
-#include <mach/omapfb.h>
+#include <plat/dma.h>
#include <asm/mach-types.h>
+#include "omapfb.h"
+
#include "lcdc.h"
#define MODULE_NAME "lcdc"
diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h
new file mode 100644
index 00000000000..46e4714014e
--- /dev/null
+++ b/drivers/video/omap/omapfb.h
@@ -0,0 +1,227 @@
+/*
+ * File: drivers/video/omap/omapfb.h
+ *
+ * Framebuffer driver for TI OMAP boards
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OMAPFB_H
+#define __OMAPFB_H
+
+#include <linux/fb.h>
+#include <linux/mutex.h>
+#include <linux/omapfb.h>
+
+#define OMAPFB_EVENT_READY 1
+#define OMAPFB_EVENT_DISABLED 2
+
+#define OMAP_LCDC_INV_VSYNC 0x0001
+#define OMAP_LCDC_INV_HSYNC 0x0002
+#define OMAP_LCDC_INV_PIX_CLOCK 0x0004
+#define OMAP_LCDC_INV_OUTPUT_EN 0x0008
+#define OMAP_LCDC_HSVS_RISING_EDGE 0x0010
+#define OMAP_LCDC_HSVS_OPPOSITE 0x0020
+
+#define OMAP_LCDC_SIGNAL_MASK 0x003f
+
+#define OMAP_LCDC_PANEL_TFT 0x0100
+
+#define OMAPFB_PLANE_XRES_MIN 8
+#define OMAPFB_PLANE_YRES_MIN 8
+
+struct omapfb_device;
+
+struct lcd_panel {
+ const char *name;
+ int config; /* TFT/STN, signal inversion */
+ int bpp; /* Pixel format in fb mem */
+ int data_lines; /* Lines on LCD HW interface */
+
+ int x_res, y_res;
+ int pixel_clock; /* In kHz */
+ int hsw; /* Horizontal synchronization
+ pulse width */
+ int hfp; /* Horizontal front porch */
+ int hbp; /* Horizontal back porch */
+ int vsw; /* Vertical synchronization
+ pulse width */
+ int vfp; /* Vertical front porch */
+ int vbp; /* Vertical back porch */
+ int acb; /* ac-bias pin frequency */
+ int pcd; /* pixel clock divider.
+ Obsolete use pixel_clock instead */
+
+ int (*init) (struct lcd_panel *panel,
+ struct omapfb_device *fbdev);
+ void (*cleanup) (struct lcd_panel *panel);
+ int (*enable) (struct lcd_panel *panel);
+ void (*disable) (struct lcd_panel *panel);
+ unsigned long (*get_caps) (struct lcd_panel *panel);
+ int (*set_bklight_level)(struct lcd_panel *panel,
+ unsigned int level);
+ unsigned int (*get_bklight_level)(struct lcd_panel *panel);
+ unsigned int (*get_bklight_max) (struct lcd_panel *panel);
+ int (*run_test) (struct lcd_panel *panel, int test_num);
+};
+
+struct extif_timings {
+ int cs_on_time;
+ int cs_off_time;
+ int we_on_time;
+ int we_off_time;
+ int re_on_time;
+ int re_off_time;
+ int we_cycle_time;
+ int re_cycle_time;
+ int cs_pulse_width;
+ int access_time;
+
+ int clk_div;
+
+ u32 tim[5]; /* set by extif->convert_timings */
+
+ int converted;
+};
+
+struct lcd_ctrl_extif {
+ int (*init) (struct omapfb_device *fbdev);
+ void (*cleanup) (void);
+ void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div);
+ unsigned long (*get_max_tx_rate)(void);
+ int (*convert_timings) (struct extif_timings *timings);
+ void (*set_timings) (const struct extif_timings *timings);
+ void (*set_bits_per_cycle)(int bpc);
+ void (*write_command) (const void *buf, unsigned int len);
+ void (*read_data) (void *buf, unsigned int len);
+ void (*write_data) (const void *buf, unsigned int len);
+ void (*transfer_area) (int width, int height,
+ void (callback)(void *data), void *data);
+ int (*setup_tearsync) (unsigned pin_cnt,
+ unsigned hs_pulse_time, unsigned vs_pulse_time,
+ int hs_pol_inv, int vs_pol_inv, int div);
+ int (*enable_tearsync) (int enable, unsigned line);
+
+ unsigned long max_transmit_size;
+};
+
+struct omapfb_notifier_block {
+ struct notifier_block nb;
+ void *data;
+ int plane_idx;
+};
+
+typedef int (*omapfb_notifier_callback_t)(struct notifier_block *,
+ unsigned long event,
+ void *fbi);
+
+struct lcd_ctrl {
+ const char *name;
+ void *data;
+
+ int (*init) (struct omapfb_device *fbdev,
+ int ext_mode,
+ struct omapfb_mem_desc *req_md);
+ void (*cleanup) (void);
+ void (*bind_client) (struct omapfb_notifier_block *nb);
+ void (*get_caps) (int plane, struct omapfb_caps *caps);
+ int (*set_update_mode)(enum omapfb_update_mode mode);
+ enum omapfb_update_mode (*get_update_mode)(void);
+ int (*setup_plane) (int plane, int channel_out,
+ unsigned long offset,
+ int screen_width,
+ int pos_x, int pos_y, int width,
+ int height, int color_mode);
+ int (*set_rotate) (int angle);
+ int (*setup_mem) (int plane, size_t size,
+ int mem_type, unsigned long *paddr);
+ int (*mmap) (struct fb_info *info,
+ struct vm_area_struct *vma);
+ int (*set_scale) (int plane,
+ int orig_width, int orig_height,
+ int out_width, int out_height);
+ int (*enable_plane) (int plane, int enable);
+ int (*update_window) (struct fb_info *fbi,
+ struct omapfb_update_window *win,
+ void (*callback)(void *),
+ void *callback_data);
+ void (*sync) (void);
+ void (*suspend) (void);
+ void (*resume) (void);
+ int (*run_test) (int test_num);
+ int (*setcolreg) (u_int regno, u16 red, u16 green,
+ u16 blue, u16 transp,
+ int update_hw_mem);
+ int (*set_color_key) (struct omapfb_color_key *ck);
+ int (*get_color_key) (struct omapfb_color_key *ck);
+};
+
+enum omapfb_state {
+ OMAPFB_DISABLED = 0,
+ OMAPFB_SUSPENDED = 99,
+ OMAPFB_ACTIVE = 100
+};
+
+struct omapfb_plane_struct {
+ int idx;
+ struct omapfb_plane_info info;
+ enum omapfb_color_format color_mode;
+ struct omapfb_device *fbdev;
+};
+
+struct omapfb_device {
+ int state;
+ int ext_lcdc; /* Using external
+ LCD controller */
+ struct mutex rqueue_mutex;
+
+ int palette_size;
+ u32 pseudo_palette[17];
+
+ struct lcd_panel *panel; /* LCD panel */
+ const struct lcd_ctrl *ctrl; /* LCD controller */
+ const struct lcd_ctrl *int_ctrl; /* internal LCD ctrl */
+ struct lcd_ctrl_extif *ext_if; /* LCD ctrl external
+ interface */
+ struct device *dev;
+ struct fb_var_screeninfo new_var; /* for mode changes */
+
+ struct omapfb_mem_desc mem_desc;
+ struct fb_info *fb_info[OMAPFB_PLANE_NUM];
+};
+
+#ifdef CONFIG_ARCH_OMAP1
+extern struct lcd_ctrl omap1_lcd_ctrl;
+#else
+extern struct lcd_ctrl omap2_disp_ctrl;
+#endif
+
+extern void omapfb_register_panel(struct lcd_panel *panel);
+extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval);
+extern void omapfb_notify_clients(struct omapfb_device *fbdev,
+ unsigned long event);
+extern int omapfb_register_client(struct omapfb_notifier_block *nb,
+ omapfb_notifier_callback_t callback,
+ void *callback_data);
+extern int omapfb_unregister_client(struct omapfb_notifier_block *nb);
+extern int omapfb_update_window_async(struct fb_info *fbi,
+ struct omapfb_update_window *win,
+ void (*callback)(void *),
+ void *callback_data);
+
+#endif /* __OMAPFB_H */
diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c
index 0d0c8c8b9b5..c7f59a5ccdb 100644
--- a/drivers/video/omap/omapfb_main.c
+++ b/drivers/video/omap/omapfb_main.c
@@ -28,9 +28,9 @@
#include <linux/mm.h>
#include <linux/uaccess.h>
-#include <mach/dma.h>
-#include <mach/omapfb.h>
+#include <plat/dma.h>
+#include "omapfb.h"
#include "lcdc.h"
#include "dispc.h"
diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c
index ee01e84e19c..fed7b1bda19 100644
--- a/drivers/video/omap/rfbi.c
+++ b/drivers/video/omap/rfbi.c
@@ -27,8 +27,7 @@
#include <linux/clk.h>
#include <linux/io.h>
-#include <mach/omapfb.h>
-
+#include "omapfb.h"
#include "dispc.h"
/* To work around an RFBI transfer rate limitation */
diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c
index a7694622024..8fb7c708f56 100644
--- a/drivers/video/omap/sossi.c
+++ b/drivers/video/omap/sossi.c
@@ -23,10 +23,11 @@
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/interrupt.h>
-#include <mach/dma.h>
-#include <mach/omapfb.h>
+#include <plat/dma.h>
+#include "omapfb.h"
#include "lcdc.h"
#define MODULE_NAME "omapfb-sossi"
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
new file mode 100644
index 00000000000..d877c361abd
--- /dev/null
+++ b/drivers/video/omap2/Kconfig
@@ -0,0 +1,9 @@
+config OMAP2_VRAM
+ bool
+
+config OMAP2_VRFB
+ bool
+
+source "drivers/video/omap2/dss/Kconfig"
+source "drivers/video/omap2/omapfb/Kconfig"
+source "drivers/video/omap2/displays/Kconfig"
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile
new file mode 100644
index 00000000000..d853d05dad3
--- /dev/null
+++ b/drivers/video/omap2/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_OMAP2_VRAM) += vram.o
+obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
+
+obj-y += dss/
+obj-y += omapfb/
+obj-y += displays/
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
new file mode 100644
index 00000000000..b12a59c9c50
--- /dev/null
+++ b/drivers/video/omap2/displays/Kconfig
@@ -0,0 +1,22 @@
+menu "OMAP2/3 Display Device Drivers"
+ depends on OMAP2_DSS
+
+config PANEL_GENERIC
+ tristate "Generic Panel"
+ help
+ Generic panel driver.
+ Used for DVI output for Beagle and OMAP3 SDP.
+
+config PANEL_SHARP_LS037V7DW01
+ tristate "Sharp LS037V7DW01 LCD Panel"
+ depends on OMAP2_DSS
+ help
+ LCD Panel used in TI's SDP3430 and EVM boards
+
+config PANEL_TAAL
+ tristate "Taal DSI Panel"
+ depends on OMAP2_DSS_DSI
+ help
+ Taal DSI command mode panel from TPO.
+
+endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
new file mode 100644
index 00000000000..955646440b3
--- /dev/null
+++ b/drivers/video/omap2/displays/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o
+obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
+
+obj-$(CONFIG_PANEL_TAAL) += panel-taal.o
diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c
new file mode 100644
index 00000000000..eb48d1afd80
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-generic.c
@@ -0,0 +1,104 @@
+/*
+ * Generic panel support
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <plat/display.h>
+
+static struct omap_video_timings generic_panel_timings = {
+ /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */
+ .x_res = 640,
+ .y_res = 480,
+ .pixel_clock = 23500,
+ .hfp = 48,
+ .hsw = 32,
+ .hbp = 80,
+ .vfp = 3,
+ .vsw = 4,
+ .vbp = 7,
+};
+
+static int generic_panel_probe(struct omap_dss_device *dssdev)
+{
+ dssdev->panel.config = OMAP_DSS_LCD_TFT;
+ dssdev->panel.timings = generic_panel_timings;
+
+ return 0;
+}
+
+static void generic_panel_remove(struct omap_dss_device *dssdev)
+{
+}
+
+static int generic_panel_enable(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ if (dssdev->platform_enable)
+ r = dssdev->platform_enable(dssdev);
+
+ return r;
+}
+
+static void generic_panel_disable(struct omap_dss_device *dssdev)
+{
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+}
+
+static int generic_panel_suspend(struct omap_dss_device *dssdev)
+{
+ generic_panel_disable(dssdev);
+ return 0;
+}
+
+static int generic_panel_resume(struct omap_dss_device *dssdev)
+{
+ return generic_panel_enable(dssdev);
+}
+
+static struct omap_dss_driver generic_driver = {
+ .probe = generic_panel_probe,
+ .remove = generic_panel_remove,
+
+ .enable = generic_panel_enable,
+ .disable = generic_panel_disable,
+ .suspend = generic_panel_suspend,
+ .resume = generic_panel_resume,
+
+ .driver = {
+ .name = "generic_panel",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init generic_panel_drv_init(void)
+{
+ return omap_dss_register_driver(&generic_driver);
+}
+
+static void __exit generic_panel_drv_exit(void)
+{
+ omap_dss_unregister_driver(&generic_driver);
+}
+
+module_init(generic_panel_drv_init);
+module_exit(generic_panel_drv_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
new file mode 100644
index 00000000000..bbe880bbe79
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
@@ -0,0 +1,153 @@
+/*
+ * LCD panel driver for Sharp LS037V7DW01
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+
+#include <plat/display.h>
+
+struct sharp_data {
+ /* XXX This regulator should actually be in SDP board file, not here,
+ * as it doesn't actually power the LCD, but something else that
+ * affects the output to LCD (I think. Somebody clarify). It doesn't do
+ * harm here, as SDP is the only board using this currently */
+ struct regulator *vdvi_reg;
+};
+
+static struct omap_video_timings sharp_ls_timings = {
+ .x_res = 480,
+ .y_res = 640,
+
+ .pixel_clock = 19200,
+
+ .hsw = 2,
+ .hfp = 1,
+ .hbp = 28,
+
+ .vsw = 1,
+ .vfp = 1,
+ .vbp = 1,
+};
+
+static int sharp_ls_panel_probe(struct omap_dss_device *dssdev)
+{
+ struct sharp_data *sd;
+
+ dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
+ OMAP_DSS_LCD_IHS;
+ dssdev->panel.acb = 0x28;
+ dssdev->panel.timings = sharp_ls_timings;
+
+ sd = kzalloc(sizeof(*sd), GFP_KERNEL);
+ if (!sd)
+ return -ENOMEM;
+
+ dev_set_drvdata(&dssdev->dev, sd);
+
+ sd->vdvi_reg = regulator_get(&dssdev->dev, "vdvi");
+ if (IS_ERR(sd->vdvi_reg)) {
+ kfree(sd);
+ pr_err("failed to get VDVI regulator\n");
+ return PTR_ERR(sd->vdvi_reg);
+ }
+
+ return 0;
+}
+
+static void sharp_ls_panel_remove(struct omap_dss_device *dssdev)
+{
+ struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
+
+ regulator_put(sd->vdvi_reg);
+
+ kfree(sd);
+}
+
+static int sharp_ls_panel_enable(struct omap_dss_device *dssdev)
+{
+ struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
+ int r = 0;
+
+ /* wait couple of vsyncs until enabling the LCD */
+ msleep(50);
+
+ regulator_enable(sd->vdvi_reg);
+
+ if (dssdev->platform_enable)
+ r = dssdev->platform_enable(dssdev);
+
+ return r;
+}
+
+static void sharp_ls_panel_disable(struct omap_dss_device *dssdev)
+{
+ struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
+
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ regulator_disable(sd->vdvi_reg);
+
+ /* wait at least 5 vsyncs after disabling the LCD */
+
+ msleep(100);
+}
+
+static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev)
+{
+ sharp_ls_panel_disable(dssdev);
+ return 0;
+}
+
+static int sharp_ls_panel_resume(struct omap_dss_device *dssdev)
+{
+ return sharp_ls_panel_enable(dssdev);
+}
+
+static struct omap_dss_driver sharp_ls_driver = {
+ .probe = sharp_ls_panel_probe,
+ .remove = 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",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sharp_ls_panel_drv_init(void)
+{
+ return omap_dss_register_driver(&sharp_ls_driver);
+}
+
+static void __exit sharp_ls_panel_drv_exit(void)
+{
+ omap_dss_unregister_driver(&sharp_ls_driver);
+}
+
+module_init(sharp_ls_panel_drv_init);
+module_exit(sharp_ls_panel_drv_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
new file mode 100644
index 00000000000..1f01dfc5e52
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -0,0 +1,1003 @@
+/*
+ * Taal DSI command mode panel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*#define DEBUG*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+
+#include <plat/display.h>
+
+/* DSI Virtual channel. Hardcoded for now. */
+#define TCH 0
+
+#define DCS_READ_NUM_ERRORS 0x05
+#define DCS_READ_POWER_MODE 0x0a
+#define DCS_READ_MADCTL 0x0b
+#define DCS_READ_PIXEL_FORMAT 0x0c
+#define DCS_RDDSDR 0x0f
+#define DCS_SLEEP_IN 0x10
+#define DCS_SLEEP_OUT 0x11
+#define DCS_DISPLAY_OFF 0x28
+#define DCS_DISPLAY_ON 0x29
+#define DCS_COLUMN_ADDR 0x2a
+#define DCS_PAGE_ADDR 0x2b
+#define DCS_MEMORY_WRITE 0x2c
+#define DCS_TEAR_OFF 0x34
+#define DCS_TEAR_ON 0x35
+#define DCS_MEM_ACC_CTRL 0x36
+#define DCS_PIXEL_FORMAT 0x3a
+#define DCS_BRIGHTNESS 0x51
+#define DCS_CTRL_DISPLAY 0x53
+#define DCS_WRITE_CABC 0x55
+#define DCS_READ_CABC 0x56
+#define DCS_GET_ID1 0xda
+#define DCS_GET_ID2 0xdb
+#define DCS_GET_ID3 0xdc
+
+/* #define TAAL_USE_ESD_CHECK */
+#define TAAL_ESD_CHECK_PERIOD msecs_to_jiffies(5000)
+
+struct taal_data {
+ struct backlight_device *bldev;
+
+ unsigned long hw_guard_end; /* next value of jiffies when we can
+ * issue the next sleep in/out command
+ */
+ unsigned long hw_guard_wait; /* max guard time in jiffies */
+
+ struct omap_dss_device *dssdev;
+
+ bool enabled;
+ u8 rotate;
+ bool mirror;
+
+ bool te_enabled;
+ bool use_ext_te;
+ struct completion te_completion;
+
+ bool use_dsi_bl;
+
+ bool cabc_broken;
+ unsigned cabc_mode;
+
+ bool intro_printed;
+
+ struct workqueue_struct *esd_wq;
+ struct delayed_work esd_work;
+};
+
+static void taal_esd_work(struct work_struct *work);
+
+static void hw_guard_start(struct taal_data *td, int guard_msec)
+{
+ td->hw_guard_wait = msecs_to_jiffies(guard_msec);
+ td->hw_guard_end = jiffies + td->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct taal_data *td)
+{
+ unsigned long wait = td->hw_guard_end - jiffies;
+
+ if ((long)wait > 0 && wait <= td->hw_guard_wait) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(wait);
+ }
+}
+
+static int taal_dcs_read_1(u8 dcs_cmd, u8 *data)
+{
+ int r;
+ u8 buf[1];
+
+ r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1);
+
+ if (r < 0)
+ return r;
+
+ *data = buf[0];
+
+ return 0;
+}
+
+static int taal_dcs_write_0(u8 dcs_cmd)
+{
+ return dsi_vc_dcs_write(TCH, &dcs_cmd, 1);
+}
+
+static int taal_dcs_write_1(u8 dcs_cmd, u8 param)
+{
+ u8 buf[2];
+ buf[0] = dcs_cmd;
+ buf[1] = param;
+ return dsi_vc_dcs_write(TCH, buf, 2);
+}
+
+static int taal_sleep_in(struct taal_data *td)
+
+{
+ u8 cmd;
+ int r;
+
+ hw_guard_wait(td);
+
+ cmd = DCS_SLEEP_IN;
+ r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1);
+ if (r)
+ return r;
+
+ hw_guard_start(td, 120);
+
+ msleep(5);
+
+ return 0;
+}
+
+static int taal_sleep_out(struct taal_data *td)
+{
+ int r;
+
+ hw_guard_wait(td);
+
+ r = taal_dcs_write_0(DCS_SLEEP_OUT);
+ if (r)
+ return r;
+
+ hw_guard_start(td, 120);
+
+ msleep(5);
+
+ return 0;
+}
+
+static int taal_get_id(u8 *id1, u8 *id2, u8 *id3)
+{
+ int r;
+
+ r = taal_dcs_read_1(DCS_GET_ID1, id1);
+ if (r)
+ return r;
+ r = taal_dcs_read_1(DCS_GET_ID2, id2);
+ if (r)
+ return r;
+ r = taal_dcs_read_1(DCS_GET_ID3, id3);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int taal_set_addr_mode(u8 rotate, bool mirror)
+{
+ int r;
+ u8 mode;
+ int b5, b6, b7;
+
+ r = taal_dcs_read_1(DCS_READ_MADCTL, &mode);
+ if (r)
+ return r;
+
+ switch (rotate) {
+ default:
+ case 0:
+ b7 = 0;
+ b6 = 0;
+ b5 = 0;
+ break;
+ case 1:
+ b7 = 0;
+ b6 = 1;
+ b5 = 1;
+ break;
+ case 2:
+ b7 = 1;
+ b6 = 1;
+ b5 = 0;
+ break;
+ case 3:
+ b7 = 1;
+ b6 = 0;
+ b5 = 1;
+ break;
+ }
+
+ if (mirror)
+ b6 = !b6;
+
+ mode &= ~((1<<7) | (1<<6) | (1<<5));
+ mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
+
+ return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode);
+}
+
+static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h)
+{
+ int r;
+ u16 x1 = x;
+ u16 x2 = x + w - 1;
+ u16 y1 = y;
+ u16 y2 = y + h - 1;
+
+ u8 buf[5];
+ buf[0] = DCS_COLUMN_ADDR;
+ buf[1] = (x1 >> 8) & 0xff;
+ buf[2] = (x1 >> 0) & 0xff;
+ buf[3] = (x2 >> 8) & 0xff;
+ buf[4] = (x2 >> 0) & 0xff;
+
+ r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
+ if (r)
+ return r;
+
+ buf[0] = DCS_PAGE_ADDR;
+ buf[1] = (y1 >> 8) & 0xff;
+ buf[2] = (y1 >> 0) & 0xff;
+ buf[3] = (y2 >> 8) & 0xff;
+ buf[4] = (y2 >> 0) & 0xff;
+
+ r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
+ if (r)
+ return r;
+
+ dsi_vc_send_bta_sync(TCH);
+
+ return r;
+}
+
+static int taal_bl_update_status(struct backlight_device *dev)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ int r;
+ int level;
+
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ level = dev->props.brightness;
+ else
+ level = 0;
+
+ dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
+
+ if (td->use_dsi_bl) {
+ if (td->enabled) {
+ dsi_bus_lock();
+ r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
+ dsi_bus_unlock();
+ if (r)
+ return r;
+ }
+ } else {
+ if (!dssdev->set_backlight)
+ return -EINVAL;
+
+ r = dssdev->set_backlight(dssdev, level);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+static int taal_bl_get_intensity(struct backlight_device *dev)
+{
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ return dev->props.brightness;
+
+ return 0;
+}
+
+static struct backlight_ops taal_bl_ops = {
+ .get_brightness = taal_bl_get_intensity,
+ .update_status = taal_bl_update_status,
+};
+
+static void taal_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ *timings = dssdev->panel.timings;
+}
+
+static void taal_get_resolution(struct omap_dss_device *dssdev,
+ u16 *xres, u16 *yres)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+
+ if (td->rotate == 0 || td->rotate == 2) {
+ *xres = dssdev->panel.timings.x_res;
+ *yres = dssdev->panel.timings.y_res;
+ } else {
+ *yres = dssdev->panel.timings.x_res;
+ *xres = dssdev->panel.timings.y_res;
+ }
+}
+
+static irqreturn_t taal_te_isr(int irq, void *data)
+{
+ struct omap_dss_device *dssdev = data;
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+
+ complete_all(&td->te_completion);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t taal_num_errors_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ u8 errors;
+ int r;
+
+ if (td->enabled) {
+ dsi_bus_lock();
+ r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
+ dsi_bus_unlock();
+ } else {
+ r = -ENODEV;
+ }
+
+ if (r)
+ return r;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", errors);
+}
+
+static ssize_t taal_hw_revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ u8 id1, id2, id3;
+ int r;
+
+ if (td->enabled) {
+ dsi_bus_lock();
+ r = taal_get_id(&id1, &id2, &id3);
+ dsi_bus_unlock();
+ } else {
+ r = -ENODEV;
+ }
+
+ if (r)
+ return r;
+
+ return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
+}
+
+static const char *cabc_modes[] = {
+ "off", /* used also always when CABC is not supported */
+ "ui",
+ "still-image",
+ "moving-image",
+};
+
+static ssize_t show_cabc_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ const char *mode_str;
+ int mode;
+ int len;
+
+ mode = td->cabc_mode;
+
+ mode_str = "unknown";
+ if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
+ mode_str = cabc_modes[mode];
+ len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
+
+ return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
+}
+
+static ssize_t store_cabc_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
+ if (sysfs_streq(cabc_modes[i], buf))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(cabc_modes))
+ return -EINVAL;
+
+ if (td->enabled) {
+ dsi_bus_lock();
+ if (!td->cabc_broken)
+ taal_dcs_write_1(DCS_WRITE_CABC, i);
+ dsi_bus_unlock();
+ }
+
+ td->cabc_mode = i;
+
+ return count;
+}
+
+static ssize_t show_cabc_available_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len;
+ int i;
+
+ for (i = 0, len = 0;
+ len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
+ len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
+ i ? " " : "", cabc_modes[i],
+ i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
+
+ return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
+}
+
+static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
+static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
+static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
+ show_cabc_mode, store_cabc_mode);
+static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
+ show_cabc_available_modes, NULL);
+
+static struct attribute *taal_attrs[] = {
+ &dev_attr_num_dsi_errors.attr,
+ &dev_attr_hw_revision.attr,
+ &dev_attr_cabc_mode.attr,
+ &dev_attr_cabc_available_modes.attr,
+ NULL,
+};
+
+static struct attribute_group taal_attr_group = {
+ .attrs = taal_attrs,
+};
+
+static int taal_probe(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td;
+ struct backlight_device *bldev;
+ int r;
+
+ const struct omap_video_timings taal_panel_timings = {
+ .x_res = 864,
+ .y_res = 480,
+ };
+
+ dev_dbg(&dssdev->dev, "probe\n");
+
+ dssdev->panel.config = OMAP_DSS_LCD_TFT;
+ dssdev->panel.timings = taal_panel_timings;
+ dssdev->ctrl.pixel_size = 24;
+
+ td = kzalloc(sizeof(*td), GFP_KERNEL);
+ if (!td) {
+ r = -ENOMEM;
+ goto err0;
+ }
+ td->dssdev = dssdev;
+
+ td->esd_wq = create_singlethread_workqueue("taal_esd");
+ if (td->esd_wq == NULL) {
+ dev_err(&dssdev->dev, "can't create ESD workqueue\n");
+ r = -ENOMEM;
+ goto err2;
+ }
+ INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
+
+ dev_set_drvdata(&dssdev->dev, td);
+
+ dssdev->get_timings = taal_get_timings;
+ dssdev->get_resolution = taal_get_resolution;
+
+ /* if no platform set_backlight() defined, presume DSI backlight
+ * control */
+ if (!dssdev->set_backlight)
+ td->use_dsi_bl = true;
+
+ bldev = backlight_device_register("taal", &dssdev->dev, dssdev,
+ &taal_bl_ops);
+ if (IS_ERR(bldev)) {
+ r = PTR_ERR(bldev);
+ goto err1;
+ }
+
+ td->bldev = bldev;
+
+ bldev->props.fb_blank = FB_BLANK_UNBLANK;
+ bldev->props.power = FB_BLANK_UNBLANK;
+ if (td->use_dsi_bl) {
+ bldev->props.max_brightness = 255;
+ bldev->props.brightness = 255;
+ } else {
+ bldev->props.max_brightness = 127;
+ bldev->props.brightness = 127;
+ }
+
+ taal_bl_update_status(bldev);
+
+ if (dssdev->phy.dsi.ext_te) {
+ int gpio = dssdev->phy.dsi.ext_te_gpio;
+
+ r = gpio_request(gpio, "taal irq");
+ if (r) {
+ dev_err(&dssdev->dev, "GPIO request failed\n");
+ goto err3;
+ }
+
+ gpio_direction_input(gpio);
+
+ r = request_irq(gpio_to_irq(gpio), taal_te_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING,
+ "taal vsync", dssdev);
+
+ if (r) {
+ dev_err(&dssdev->dev, "IRQ request failed\n");
+ gpio_free(gpio);
+ goto err3;
+ }
+
+ init_completion(&td->te_completion);
+
+ td->use_ext_te = true;
+ }
+
+ r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to create sysfs files\n");
+ goto err4;
+ }
+
+ return 0;
+err4:
+ if (td->use_ext_te) {
+ int gpio = dssdev->phy.dsi.ext_te_gpio;
+ free_irq(gpio_to_irq(gpio), dssdev);
+ gpio_free(gpio);
+ }
+err3:
+ backlight_device_unregister(bldev);
+err2:
+ cancel_delayed_work_sync(&td->esd_work);
+ destroy_workqueue(td->esd_wq);
+err1:
+ kfree(td);
+err0:
+ return r;
+}
+
+static void taal_remove(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ struct backlight_device *bldev;
+
+ dev_dbg(&dssdev->dev, "remove\n");
+
+ sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
+
+ if (td->use_ext_te) {
+ int gpio = dssdev->phy.dsi.ext_te_gpio;
+ free_irq(gpio_to_irq(gpio), dssdev);
+ gpio_free(gpio);
+ }
+
+ bldev = td->bldev;
+ bldev->props.power = FB_BLANK_POWERDOWN;
+ taal_bl_update_status(bldev);
+ backlight_device_unregister(bldev);
+
+ cancel_delayed_work_sync(&td->esd_work);
+ destroy_workqueue(td->esd_wq);
+
+ kfree(td);
+}
+
+static int taal_enable(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ u8 id1, id2, id3;
+ int r;
+
+ dev_dbg(&dssdev->dev, "enable\n");
+
+ if (dssdev->platform_enable) {
+ r = dssdev->platform_enable(dssdev);
+ if (r)
+ return r;
+ }
+
+ /* it seems we have to wait a bit until taal is ready */
+ msleep(5);
+
+ r = taal_sleep_out(td);
+ if (r)
+ goto err;
+
+ r = taal_get_id(&id1, &id2, &id3);
+ if (r)
+ goto err;
+
+ /* on early revisions CABC is broken */
+ if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
+ td->cabc_broken = true;
+
+ taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
+ taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */
+
+ taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
+
+ taal_set_addr_mode(td->rotate, td->mirror);
+ if (!td->cabc_broken)
+ taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
+
+ taal_dcs_write_0(DCS_DISPLAY_ON);
+
+#ifdef TAAL_USE_ESD_CHECK
+ queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
+#endif
+
+ td->enabled = 1;
+
+ if (!td->intro_printed) {
+ dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n",
+ id1, id2, id3);
+ if (td->cabc_broken)
+ dev_info(&dssdev->dev,
+ "old Taal version, CABC disabled\n");
+ td->intro_printed = true;
+ }
+
+ return 0;
+err:
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ return r;
+}
+
+static void taal_disable(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+
+ dev_dbg(&dssdev->dev, "disable\n");
+
+ cancel_delayed_work(&td->esd_work);
+
+ taal_dcs_write_0(DCS_DISPLAY_OFF);
+ taal_sleep_in(td);
+
+ /* wait a bit so that the message goes through */
+ msleep(10);
+
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ td->enabled = 0;
+}
+
+static int taal_suspend(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ struct backlight_device *bldev = td->bldev;
+
+ bldev->props.power = FB_BLANK_POWERDOWN;
+ taal_bl_update_status(bldev);
+
+ return 0;
+}
+
+static int taal_resume(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ struct backlight_device *bldev = td->bldev;
+
+ bldev->props.power = FB_BLANK_UNBLANK;
+ taal_bl_update_status(bldev);
+
+ return 0;
+}
+
+static void taal_setup_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ taal_set_update_window(x, y, w, h);
+}
+
+static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ td->te_enabled = enable;
+
+ if (enable)
+ r = taal_dcs_write_1(DCS_TEAR_ON, 0);
+ else
+ r = taal_dcs_write_0(DCS_TEAR_OFF);
+
+ return r;
+}
+
+static int taal_wait_te(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ long wait = msecs_to_jiffies(500);
+
+ if (!td->use_ext_te || !td->te_enabled)
+ return 0;
+
+ INIT_COMPLETION(td->te_completion);
+ wait = wait_for_completion_timeout(&td->te_completion, wait);
+ if (wait == 0) {
+ dev_err(&dssdev->dev, "timeout waiting TE\n");
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
+
+ if (td->enabled) {
+ r = taal_set_addr_mode(rotate, td->mirror);
+
+ if (r)
+ return r;
+ }
+
+ td->rotate = rotate;
+
+ return 0;
+}
+
+static u8 taal_get_rotate(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ return td->rotate;
+}
+
+static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ dev_dbg(&dssdev->dev, "mirror %d\n", enable);
+
+ if (td->enabled) {
+ r = taal_set_addr_mode(td->rotate, enable);
+
+ if (r)
+ return r;
+ }
+
+ td->mirror = enable;
+
+ return 0;
+}
+
+static bool taal_get_mirror(struct omap_dss_device *dssdev)
+{
+ struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+ return td->mirror;
+}
+
+static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
+{
+ u8 id1, id2, id3;
+ int r;
+
+ r = taal_dcs_read_1(DCS_GET_ID1, &id1);
+ if (r)
+ return r;
+ r = taal_dcs_read_1(DCS_GET_ID2, &id2);
+ if (r)
+ return r;
+ r = taal_dcs_read_1(DCS_GET_ID3, &id3);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int taal_memory_read(struct omap_dss_device *dssdev,
+ void *buf, size_t size,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ int r;
+ int first = 1;
+ int plen;
+ unsigned buf_used = 0;
+
+ if (size < w * h * 3)
+ return -ENOMEM;
+
+ size = min(w * h * 3,
+ dssdev->panel.timings.x_res *
+ dssdev->panel.timings.y_res * 3);
+
+ /* plen 1 or 2 goes into short packet. until checksum error is fixed,
+ * use short packets. plen 32 works, but bigger packets seem to cause
+ * an error. */
+ if (size % 2)
+ plen = 1;
+ else
+ plen = 2;
+
+ taal_setup_update(dssdev, x, y, w, h);
+
+ r = dsi_vc_set_max_rx_packet_size(TCH, plen);
+ if (r)
+ return r;
+
+ while (buf_used < size) {
+ u8 dcs_cmd = first ? 0x2e : 0x3e;
+ first = 0;
+
+ r = dsi_vc_dcs_read(TCH, dcs_cmd,
+ buf + buf_used, size - buf_used);
+
+ if (r < 0) {
+ dev_err(&dssdev->dev, "read error\n");
+ goto err;
+ }
+
+ buf_used += r;
+
+ if (r < plen) {
+ dev_err(&dssdev->dev, "short read\n");
+ break;
+ }
+
+ if (signal_pending(current)) {
+ dev_err(&dssdev->dev, "signal pending, "
+ "aborting memory read\n");
+ r = -ERESTARTSYS;
+ goto err;
+ }
+ }
+
+ r = buf_used;
+
+err:
+ dsi_vc_set_max_rx_packet_size(TCH, 1);
+
+ return r;
+}
+
+static void taal_esd_work(struct work_struct *work)
+{
+ struct taal_data *td = container_of(work, struct taal_data,
+ esd_work.work);
+ struct omap_dss_device *dssdev = td->dssdev;
+ u8 state1, state2;
+ int r;
+
+ if (!td->enabled)
+ return;
+
+ dsi_bus_lock();
+
+ r = taal_dcs_read_1(DCS_RDDSDR, &state1);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to read Taal status\n");
+ goto err;
+ }
+
+ /* Run self diagnostics */
+ r = taal_sleep_out(td);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
+ goto err;
+ }
+
+ r = taal_dcs_read_1(DCS_RDDSDR, &state2);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to read Taal status\n");
+ goto err;
+ }
+
+ /* Each sleep out command will trigger a self diagnostic and flip
+ * Bit6 if the test passes.
+ */
+ if (!((state1 ^ state2) & (1 << 6))) {
+ dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
+ goto err;
+ }
+ /* Self-diagnostics result is also shown on TE GPIO line. We need
+ * to re-enable TE after self diagnostics */
+ if (td->use_ext_te && td->te_enabled)
+ taal_enable_te(dssdev, true);
+
+ dsi_bus_unlock();
+
+ queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
+
+ return;
+err:
+ dev_err(&dssdev->dev, "performing LCD reset\n");
+
+ taal_disable(dssdev);
+ taal_enable(dssdev);
+
+ dsi_bus_unlock();
+
+ queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
+}
+
+static struct omap_dss_driver taal_driver = {
+ .probe = taal_probe,
+ .remove = taal_remove,
+
+ .enable = taal_enable,
+ .disable = taal_disable,
+ .suspend = taal_suspend,
+ .resume = taal_resume,
+
+ .setup_update = taal_setup_update,
+ .enable_te = taal_enable_te,
+ .wait_for_te = taal_wait_te,
+ .set_rotate = taal_rotate,
+ .get_rotate = taal_get_rotate,
+ .set_mirror = taal_mirror,
+ .get_mirror = taal_get_mirror,
+ .run_test = taal_run_test,
+ .memory_read = taal_memory_read,
+
+ .driver = {
+ .name = "taal",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init taal_init(void)
+{
+ omap_dss_register_driver(&taal_driver);
+
+ return 0;
+}
+
+static void __exit taal_exit(void)
+{
+ omap_dss_unregister_driver(&taal_driver);
+}
+
+module_init(taal_init);
+module_exit(taal_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("Taal Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
new file mode 100644
index 00000000000..71d8dec3063
--- /dev/null
+++ b/drivers/video/omap2/dss/Kconfig
@@ -0,0 +1,89 @@
+menuconfig OMAP2_DSS
+ tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)"
+ depends on ARCH_OMAP2 || ARCH_OMAP3
+ help
+ OMAP2/3 Display Subsystem support.
+
+if OMAP2_DSS
+
+config OMAP2_VRAM_SIZE
+ int "VRAM size (MB)"
+ range 0 32
+ default 0
+ 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.
+
+ 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
+ help
+ This enables debug messages. You need to enable printing
+ with 'debug' module parameter.
+
+config OMAP2_DSS_RFBI
+ bool "RFBI support"
+ default n
+ help
+ MIPI DBI, or RFBI (Remote Framebuffer Interface), support.
+
+config OMAP2_DSS_VENC
+ bool "VENC support"
+ default y
+ help
+ OMAP Video Encoder support.
+
+config OMAP2_DSS_SDI
+ bool "SDI support"
+ depends on ARCH_OMAP3
+ default n
+ help
+ SDI (Serial Display Interface) support.
+
+config OMAP2_DSS_DSI
+ bool "DSI support"
+ depends on ARCH_OMAP3
+ default n
+ help
+ MIPI DSI support.
+
+config OMAP2_DSS_USE_DSI_PLL
+ bool "Use DSI PLL for PCLK (EXPERIMENTAL)"
+ default n
+ depends on OMAP2_DSS_DSI
+ help
+ Use DSI PLL to generate pixel clock. Currently only for DPI output.
+ DSI PLL can be used to generate higher and more precise pixel clocks.
+
+config OMAP2_DSS_FAKE_VSYNC
+ bool "Fake VSYNC irq from manual update displays"
+ default n
+ help
+ If this is selected, DSI will generate a fake DISPC VSYNC interrupt
+ when DSI has sent a frame. This is only needed with DSI or RFBI
+ displays using manual mode, and you want VSYNC to, for example,
+ time animation.
+
+config OMAP2_DSS_MIN_FCK_PER_PCK
+ int "Minimum FCK/PCK ratio (for scaling)"
+ range 0 32
+ default 0
+ help
+ This can be used to adjust the minimum FCK/PCK ratio.
+
+ With this you can make sure that DISPC FCK is at least
+ n x PCK. Video plane scaling requires higher FCK than
+ normally.
+
+ If this is set to 0, there's no extra constraint on the
+ DISPC FCK. However, the FCK will at minimum be
+ 2xPCK (if active matrix) or 3xPCK (if passive matrix).
+
+ Max FCK is 173MHz, so this doesn't work if your PCK
+ is very high.
+
+endif
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
new file mode 100644
index 00000000000..980c72c2db9
--- /dev/null
+++ b/drivers/video/omap2/dss/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_OMAP2_DSS) += omapdss.o
+omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o
+omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
+omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
+omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
+omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
new file mode 100644
index 00000000000..29497a0c9a9
--- /dev/null
+++ b/drivers/video/omap2/dss/core.c
@@ -0,0 +1,919 @@
+/*
+ * linux/drivers/video/omap2/dss/core.c
+ *
+ * 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 "CORE"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/device.h>
+
+#include <plat/display.h>
+#include <plat/clock.h>
+
+#include "dss.h"
+
+static struct {
+ struct platform_device *pdev;
+ int ctx_id;
+
+ struct clk *dss_ick;
+ struct clk *dss1_fck;
+ struct clk *dss2_fck;
+ struct clk *dss_54m_fck;
+ struct clk *dss_96m_fck;
+ unsigned num_clks_enabled;
+} core;
+
+static void dss_clk_enable_all_no_ctx(void);
+static void dss_clk_disable_all_no_ctx(void);
+static void dss_clk_enable_no_ctx(enum dss_clock clks);
+static void dss_clk_disable_no_ctx(enum dss_clock clks);
+
+static char *def_disp_name;
+module_param_named(def_disp, def_disp_name, charp, 0);
+MODULE_PARM_DESC(def_disp_name, "default display name");
+
+#ifdef DEBUG
+unsigned int dss_debug;
+module_param_named(debug, dss_debug, bool, 0644);
+#endif
+
+/* CONTEXT */
+static int dss_get_ctx_id(void)
+{
+ struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
+ int r;
+
+ if (!pdata->get_last_off_on_transaction_id)
+ return 0;
+ r = pdata->get_last_off_on_transaction_id(&core.pdev->dev);
+ if (r < 0) {
+ dev_err(&core.pdev->dev, "getting transaction ID failed, "
+ "will force context restore\n");
+ r = -1;
+ }
+ return r;
+}
+
+int dss_need_ctx_restore(void)
+{
+ int id = dss_get_ctx_id();
+
+ if (id < 0 || id != core.ctx_id) {
+ DSSDBG("ctx id %d -> id %d\n",
+ core.ctx_id, id);
+ core.ctx_id = id;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void save_all_ctx(void)
+{
+ DSSDBG("save context\n");
+
+ dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dss_save_context();
+ dispc_save_context();
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_save_context();
+#endif
+
+ dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+static void restore_all_ctx(void)
+{
+ DSSDBG("restore context\n");
+
+ dss_clk_enable_all_no_ctx();
+
+ dss_restore_context();
+ dispc_restore_context();
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_restore_context();
+#endif
+
+ dss_clk_disable_all_no_ctx();
+}
+
+/* CLOCKS */
+static void core_dump_clocks(struct seq_file *s)
+{
+ int i;
+ struct clk *clocks[5] = {
+ core.dss_ick,
+ core.dss1_fck,
+ core.dss2_fck,
+ core.dss_54m_fck,
+ core.dss_96m_fck
+ };
+
+ seq_printf(s, "- CORE -\n");
+
+ seq_printf(s, "internal clk count\t\t%u\n", core.num_clks_enabled);
+
+ for (i = 0; i < 5; i++) {
+ if (!clocks[i])
+ continue;
+ seq_printf(s, "%-15s\t%lu\t%d\n",
+ clocks[i]->name,
+ clk_get_rate(clocks[i]),
+ clocks[i]->usecount);
+ }
+}
+
+static int dss_get_clock(struct clk **clock, const char *clk_name)
+{
+ struct clk *clk;
+
+ clk = clk_get(&core.pdev->dev, clk_name);
+
+ if (IS_ERR(clk)) {
+ DSSERR("can't get clock %s", clk_name);
+ return PTR_ERR(clk);
+ }
+
+ *clock = clk;
+
+ DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
+
+ return 0;
+}
+
+static int dss_get_clocks(void)
+{
+ int r;
+
+ core.dss_ick = NULL;
+ core.dss1_fck = NULL;
+ core.dss2_fck = NULL;
+ core.dss_54m_fck = NULL;
+ core.dss_96m_fck = NULL;
+
+ r = dss_get_clock(&core.dss_ick, "ick");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&core.dss1_fck, "dss1_fck");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&core.dss2_fck, "dss2_fck");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&core.dss_54m_fck, "tv_fck");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&core.dss_96m_fck, "video_fck");
+ if (r)
+ goto err;
+
+ return 0;
+
+err:
+ if (core.dss_ick)
+ clk_put(core.dss_ick);
+ if (core.dss1_fck)
+ clk_put(core.dss1_fck);
+ if (core.dss2_fck)
+ clk_put(core.dss2_fck);
+ if (core.dss_54m_fck)
+ clk_put(core.dss_54m_fck);
+ if (core.dss_96m_fck)
+ clk_put(core.dss_96m_fck);
+
+ return r;
+}
+
+static void dss_put_clocks(void)
+{
+ if (core.dss_96m_fck)
+ clk_put(core.dss_96m_fck);
+ clk_put(core.dss_54m_fck);
+ clk_put(core.dss1_fck);
+ clk_put(core.dss2_fck);
+ clk_put(core.dss_ick);
+}
+
+unsigned long dss_clk_get_rate(enum dss_clock clk)
+{
+ switch (clk) {
+ case DSS_CLK_ICK:
+ return clk_get_rate(core.dss_ick);
+ case DSS_CLK_FCK1:
+ return clk_get_rate(core.dss1_fck);
+ case DSS_CLK_FCK2:
+ return clk_get_rate(core.dss2_fck);
+ case DSS_CLK_54M:
+ return clk_get_rate(core.dss_54m_fck);
+ case DSS_CLK_96M:
+ return clk_get_rate(core.dss_96m_fck);
+ }
+
+ BUG();
+ return 0;
+}
+
+static unsigned count_clk_bits(enum dss_clock clks)
+{
+ unsigned num_clks = 0;
+
+ if (clks & DSS_CLK_ICK)
+ ++num_clks;
+ if (clks & DSS_CLK_FCK1)
+ ++num_clks;
+ if (clks & DSS_CLK_FCK2)
+ ++num_clks;
+ if (clks & DSS_CLK_54M)
+ ++num_clks;
+ if (clks & DSS_CLK_96M)
+ ++num_clks;
+
+ return num_clks;
+}
+
+static void dss_clk_enable_no_ctx(enum dss_clock clks)
+{
+ unsigned num_clks = count_clk_bits(clks);
+
+ if (clks & DSS_CLK_ICK)
+ clk_enable(core.dss_ick);
+ if (clks & DSS_CLK_FCK1)
+ clk_enable(core.dss1_fck);
+ if (clks & DSS_CLK_FCK2)
+ clk_enable(core.dss2_fck);
+ if (clks & DSS_CLK_54M)
+ clk_enable(core.dss_54m_fck);
+ if (clks & DSS_CLK_96M)
+ clk_enable(core.dss_96m_fck);
+
+ core.num_clks_enabled += num_clks;
+}
+
+void dss_clk_enable(enum dss_clock clks)
+{
+ dss_clk_enable_no_ctx(clks);
+
+ if (cpu_is_omap34xx() && dss_need_ctx_restore())
+ restore_all_ctx();
+}
+
+static void dss_clk_disable_no_ctx(enum dss_clock clks)
+{
+ unsigned num_clks = count_clk_bits(clks);
+
+ if (clks & DSS_CLK_ICK)
+ clk_disable(core.dss_ick);
+ if (clks & DSS_CLK_FCK1)
+ clk_disable(core.dss1_fck);
+ if (clks & DSS_CLK_FCK2)
+ clk_disable(core.dss2_fck);
+ if (clks & DSS_CLK_54M)
+ clk_disable(core.dss_54m_fck);
+ if (clks & DSS_CLK_96M)
+ clk_disable(core.dss_96m_fck);
+
+ core.num_clks_enabled -= num_clks;
+}
+
+void dss_clk_disable(enum dss_clock clks)
+{
+ if (cpu_is_omap34xx()) {
+ unsigned num_clks = count_clk_bits(clks);
+
+ BUG_ON(core.num_clks_enabled < num_clks);
+
+ if (core.num_clks_enabled == num_clks)
+ save_all_ctx();
+ }
+
+ dss_clk_disable_no_ctx(clks);
+}
+
+static void dss_clk_enable_all_no_ctx(void)
+{
+ enum dss_clock clks;
+
+ clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+ if (cpu_is_omap34xx())
+ clks |= DSS_CLK_96M;
+ dss_clk_enable_no_ctx(clks);
+}
+
+static void dss_clk_disable_all_no_ctx(void)
+{
+ enum dss_clock clks;
+
+ clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+ if (cpu_is_omap34xx())
+ clks |= DSS_CLK_96M;
+ dss_clk_disable_no_ctx(clks);
+}
+
+static void dss_clk_disable_all(void)
+{
+ enum dss_clock clks;
+
+ clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+ if (cpu_is_omap34xx())
+ clks |= DSS_CLK_96M;
+ dss_clk_disable(clks);
+}
+
+/* DEBUGFS */
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+static void dss_debug_dump_clocks(struct seq_file *s)
+{
+ core_dump_clocks(s);
+ dss_dump_clocks(s);
+ dispc_dump_clocks(s);
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_dump_clocks(s);
+#endif
+}
+
+static int dss_debug_show(struct seq_file *s, void *unused)
+{
+ void (*func)(struct seq_file *) = s->private;
+ func(s);
+ return 0;
+}
+
+static int dss_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dss_debug_show, inode->i_private);
+}
+
+static const struct file_operations dss_debug_fops = {
+ .open = dss_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *dss_debugfs_dir;
+
+static int dss_initialize_debugfs(void)
+{
+ dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
+ if (IS_ERR(dss_debugfs_dir)) {
+ int err = PTR_ERR(dss_debugfs_dir);
+ dss_debugfs_dir = NULL;
+ return err;
+ }
+
+ debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
+ &dss_debug_dump_clocks, &dss_debug_fops);
+
+ debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir,
+ &dss_dump_regs, &dss_debug_fops);
+ debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir,
+ &dispc_dump_regs, &dss_debug_fops);
+#ifdef CONFIG_OMAP2_DSS_RFBI
+ debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir,
+ &rfbi_dump_regs, &dss_debug_fops);
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+ debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir,
+ &dsi_dump_regs, &dss_debug_fops);
+#endif
+#ifdef CONFIG_OMAP2_DSS_VENC
+ debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir,
+ &venc_dump_regs, &dss_debug_fops);
+#endif
+ return 0;
+}
+
+static void dss_uninitialize_debugfs(void)
+{
+ if (dss_debugfs_dir)
+ debugfs_remove_recursive(dss_debugfs_dir);
+}
+#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
+
+/* PLATFORM DEVICE */
+static int omap_dss_probe(struct platform_device *pdev)
+{
+ struct omap_dss_board_info *pdata = pdev->dev.platform_data;
+ int skip_init = 0;
+ int r;
+ int i;
+
+ core.pdev = pdev;
+
+ dss_init_overlay_managers(pdev);
+ dss_init_overlays(pdev);
+
+ r = dss_get_clocks();
+ if (r)
+ goto fail0;
+
+ dss_clk_enable_all_no_ctx();
+
+ core.ctx_id = dss_get_ctx_id();
+ DSSDBG("initial ctx id %u\n", core.ctx_id);
+
+#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
+ /* DISPC_CONTROL */
+ if (omap_readl(0x48050440) & 1) /* LCD enabled? */
+ skip_init = 1;
+#endif
+
+ r = dss_init(skip_init);
+ if (r) {
+ DSSERR("Failed to initialize DSS\n");
+ goto fail0;
+ }
+
+#ifdef CONFIG_OMAP2_DSS_RFBI
+ r = rfbi_init();
+ if (r) {
+ DSSERR("Failed to initialize rfbi\n");
+ goto fail0;
+ }
+#endif
+
+ r = dpi_init();
+ if (r) {
+ DSSERR("Failed to initialize dpi\n");
+ goto fail0;
+ }
+
+ r = dispc_init();
+ if (r) {
+ DSSERR("Failed to initialize dispc\n");
+ goto fail0;
+ }
+#ifdef CONFIG_OMAP2_DSS_VENC
+ r = venc_init(pdev);
+ if (r) {
+ DSSERR("Failed to initialize venc\n");
+ goto fail0;
+ }
+#endif
+ if (cpu_is_omap34xx()) {
+#ifdef CONFIG_OMAP2_DSS_SDI
+ r = sdi_init(skip_init);
+ if (r) {
+ DSSERR("Failed to initialize SDI\n");
+ goto fail0;
+ }
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+ r = dsi_init(pdev);
+ if (r) {
+ DSSERR("Failed to initialize DSI\n");
+ goto fail0;
+ }
+#endif
+ }
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+ r = dss_initialize_debugfs();
+ if (r)
+ goto fail0;
+#endif
+
+ for (i = 0; i < pdata->num_devices; ++i) {
+ struct omap_dss_device *dssdev = pdata->devices[i];
+
+ r = omap_dss_register_device(dssdev);
+ if (r)
+ DSSERR("device reg failed %d\n", i);
+
+ if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0)
+ pdata->default_device = dssdev;
+ }
+
+ dss_clk_disable_all();
+
+ return 0;
+
+ /* XXX fail correctly */
+fail0:
+ return r;
+}
+
+static int omap_dss_remove(struct platform_device *pdev)
+{
+ struct omap_dss_board_info *pdata = pdev->dev.platform_data;
+ int i;
+ int c;
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+ dss_uninitialize_debugfs();
+#endif
+
+#ifdef CONFIG_OMAP2_DSS_VENC
+ venc_exit();
+#endif
+ dispc_exit();
+ dpi_exit();
+#ifdef CONFIG_OMAP2_DSS_RFBI
+ rfbi_exit();
+#endif
+ if (cpu_is_omap34xx()) {
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_exit();
+#endif
+#ifdef CONFIG_OMAP2_DSS_SDI
+ sdi_exit();
+#endif
+ }
+
+ dss_exit();
+
+ /* these should be removed at some point */
+ c = core.dss_ick->usecount;
+ if (c > 0) {
+ DSSERR("warning: dss_ick usecount %d, disabling\n", c);
+ while (c-- > 0)
+ clk_disable(core.dss_ick);
+ }
+
+ c = core.dss1_fck->usecount;
+ if (c > 0) {
+ DSSERR("warning: dss1_fck usecount %d, disabling\n", c);
+ while (c-- > 0)
+ clk_disable(core.dss1_fck);
+ }
+
+ c = core.dss2_fck->usecount;
+ if (c > 0) {
+ DSSERR("warning: dss2_fck usecount %d, disabling\n", c);
+ while (c-- > 0)
+ clk_disable(core.dss2_fck);
+ }
+
+ c = core.dss_54m_fck->usecount;
+ if (c > 0) {
+ DSSERR("warning: dss_54m_fck usecount %d, disabling\n", c);
+ while (c-- > 0)
+ clk_disable(core.dss_54m_fck);
+ }
+
+ if (core.dss_96m_fck) {
+ c = core.dss_96m_fck->usecount;
+ if (c > 0) {
+ DSSERR("warning: dss_96m_fck usecount %d, disabling\n",
+ c);
+ while (c-- > 0)
+ clk_disable(core.dss_96m_fck);
+ }
+ }
+
+ dss_put_clocks();
+
+ dss_uninit_overlays(pdev);
+ dss_uninit_overlay_managers(pdev);
+
+ for (i = 0; i < pdata->num_devices; ++i)
+ omap_dss_unregister_device(pdata->devices[i]);
+
+ return 0;
+}
+
+static void omap_dss_shutdown(struct platform_device *pdev)
+{
+ DSSDBG("shutdown\n");
+ dss_disable_all_devices();
+}
+
+static int omap_dss_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ DSSDBG("suspend %d\n", state.event);
+
+ return dss_suspend_all_devices();
+}
+
+static int omap_dss_resume(struct platform_device *pdev)
+{
+ DSSDBG("resume\n");
+
+ return dss_resume_all_devices();
+}
+
+static struct platform_driver omap_dss_driver = {
+ .probe = omap_dss_probe,
+ .remove = omap_dss_remove,
+ .shutdown = omap_dss_shutdown,
+ .suspend = omap_dss_suspend,
+ .resume = omap_dss_resume,
+ .driver = {
+ .name = "omapdss",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* BUS */
+static int dss_bus_match(struct device *dev, struct device_driver *driver)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+
+ DSSDBG("bus_match. dev %s/%s, drv %s\n",
+ dev_name(dev), dssdev->driver_name, driver->name);
+
+ return strcmp(dssdev->driver_name, driver->name) == 0;
+}
+
+static ssize_t device_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ dssdev->name ?
+ dssdev->name : "");
+}
+
+static struct device_attribute default_dev_attrs[] = {
+ __ATTR(name, S_IRUGO, device_name_show, NULL),
+ __ATTR_NULL,
+};
+
+static ssize_t driver_name_show(struct device_driver *drv, char *buf)
+{
+ struct omap_dss_driver *dssdrv = to_dss_driver(drv);
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ dssdrv->driver.name ?
+ dssdrv->driver.name : "");
+}
+static struct driver_attribute default_drv_attrs[] = {
+ __ATTR(name, S_IRUGO, driver_name_show, NULL),
+ __ATTR_NULL,
+};
+
+static struct bus_type dss_bus_type = {
+ .name = "omapdss",
+ .match = dss_bus_match,
+ .dev_attrs = default_dev_attrs,
+ .drv_attrs = default_drv_attrs,
+};
+
+static void dss_bus_release(struct device *dev)
+{
+ DSSDBG("bus_release\n");
+}
+
+static struct device dss_bus = {
+ .release = dss_bus_release,
+};
+
+struct bus_type *dss_get_bus(void)
+{
+ return &dss_bus_type;
+}
+
+/* DRIVER */
+static int dss_driver_probe(struct device *dev)
+{
+ int r;
+ struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
+ bool force;
+
+ DSSDBG("driver_probe: dev %s/%s, drv %s\n",
+ dev_name(dev), dssdev->driver_name,
+ dssdrv->driver.name);
+
+ dss_init_device(core.pdev, dssdev);
+
+ /* skip this if the device is behind a ctrl */
+ if (!dssdev->panel.ctrl) {
+ force = pdata->default_device == dssdev;
+ dss_recheck_connections(dssdev, force);
+ }
+
+ r = dssdrv->probe(dssdev);
+
+ if (r) {
+ DSSERR("driver probe failed: %d\n", r);
+ return r;
+ }
+
+ DSSDBG("probe done for device %s\n", dev_name(dev));
+
+ dssdev->driver = dssdrv;
+
+ return 0;
+}
+
+static int dss_driver_remove(struct device *dev)
+{
+ struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+
+ DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev),
+ dssdev->driver_name);
+
+ dssdrv->remove(dssdev);
+
+ dss_uninit_device(core.pdev, dssdev);
+
+ dssdev->driver = NULL;
+
+ return 0;
+}
+
+int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
+{
+ dssdriver->driver.bus = &dss_bus_type;
+ dssdriver->driver.probe = dss_driver_probe;
+ dssdriver->driver.remove = dss_driver_remove;
+ return driver_register(&dssdriver->driver);
+}
+EXPORT_SYMBOL(omap_dss_register_driver);
+
+void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver)
+{
+ driver_unregister(&dssdriver->driver);
+}
+EXPORT_SYMBOL(omap_dss_unregister_driver);
+
+/* DEVICE */
+static void reset_device(struct device *dev, int check)
+{
+ u8 *dev_p = (u8 *)dev;
+ u8 *dev_end = dev_p + sizeof(*dev);
+ void *saved_pdata;
+
+ saved_pdata = dev->platform_data;
+ if (check) {
+ /*
+ * Check if there is any other setting than platform_data
+ * in struct device; warn that these will be reset by our
+ * init.
+ */
+ dev->platform_data = NULL;
+ while (dev_p < dev_end) {
+ if (*dev_p) {
+ WARN("%s: struct device fields will be "
+ "discarded\n",
+ __func__);
+ break;
+ }
+ dev_p++;
+ }
+ }
+ memset(dev, 0, sizeof(*dev));
+ dev->platform_data = saved_pdata;
+}
+
+
+static void omap_dss_dev_release(struct device *dev)
+{
+ reset_device(dev, 0);
+}
+
+int omap_dss_register_device(struct omap_dss_device *dssdev)
+{
+ static int dev_num;
+ static int panel_num;
+ int r;
+
+ WARN_ON(!dssdev->driver_name);
+
+ reset_device(&dssdev->dev, 1);
+ dssdev->dev.bus = &dss_bus_type;
+ dssdev->dev.parent = &dss_bus;
+ dssdev->dev.release = omap_dss_dev_release;
+ dev_set_name(&dssdev->dev, "display%d", dev_num++);
+ r = device_register(&dssdev->dev);
+ if (r)
+ return r;
+
+ if (dssdev->ctrl.panel) {
+ struct omap_dss_device *panel = dssdev->ctrl.panel;
+
+ panel->panel.ctrl = dssdev;
+
+ reset_device(&panel->dev, 1);
+ panel->dev.bus = &dss_bus_type;
+ panel->dev.parent = &dssdev->dev;
+ panel->dev.release = omap_dss_dev_release;
+ dev_set_name(&panel->dev, "panel%d", panel_num++);
+ r = device_register(&panel->dev);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+void omap_dss_unregister_device(struct omap_dss_device *dssdev)
+{
+ device_unregister(&dssdev->dev);
+
+ if (dssdev->ctrl.panel) {
+ struct omap_dss_device *panel = dssdev->ctrl.panel;
+ device_unregister(&panel->dev);
+ }
+}
+
+/* BUS */
+static int omap_dss_bus_register(void)
+{
+ int r;
+
+ r = bus_register(&dss_bus_type);
+ if (r) {
+ DSSERR("bus register failed\n");
+ return r;
+ }
+
+ dev_set_name(&dss_bus, "omapdss");
+ r = device_register(&dss_bus);
+ if (r) {
+ DSSERR("bus driver register failed\n");
+ bus_unregister(&dss_bus_type);
+ return r;
+ }
+
+ return 0;
+}
+
+/* INIT */
+
+#ifdef CONFIG_OMAP2_DSS_MODULE
+static void omap_dss_bus_unregister(void)
+{
+ device_unregister(&dss_bus);
+
+ bus_unregister(&dss_bus_type);
+}
+
+static int __init omap_dss_init(void)
+{
+ int r;
+
+ r = omap_dss_bus_register();
+ if (r)
+ return r;
+
+ r = platform_driver_register(&omap_dss_driver);
+ if (r) {
+ omap_dss_bus_unregister();
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit omap_dss_exit(void)
+{
+ platform_driver_unregister(&omap_dss_driver);
+
+ omap_dss_bus_unregister();
+}
+
+module_init(omap_dss_init);
+module_exit(omap_dss_exit);
+#else
+static int __init omap_dss_init(void)
+{
+ return omap_dss_bus_register();
+}
+
+static int __init omap_dss_init2(void)
+{
+ return platform_driver_register(&omap_dss_driver);
+}
+
+core_initcall(omap_dss_init);
+device_initcall(omap_dss_init2);
+#endif
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("OMAP2/3 Display Subsystem");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
new file mode 100644
index 00000000000..6dabf4b2f00
--- /dev/null
+++ b/drivers/video/omap2/dss/dispc.c
@@ -0,0 +1,3091 @@
+/*
+ * linux/drivers/video/omap2/dss/dispc.c
+ *
+ * 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 "DISPC"
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+
+#include <plat/sram.h>
+#include <plat/clock.h>
+
+#include <plat/display.h>
+
+#include "dss.h"
+
+/* DISPC */
+#define DISPC_BASE 0x48050400
+
+#define DISPC_SZ_REGS SZ_1K
+
+struct dispc_reg { u16 idx; };
+
+#define DISPC_REG(idx) ((const struct dispc_reg) { idx })
+
+/* DISPC common */
+#define DISPC_REVISION DISPC_REG(0x0000)
+#define DISPC_SYSCONFIG DISPC_REG(0x0010)
+#define DISPC_SYSSTATUS DISPC_REG(0x0014)
+#define DISPC_IRQSTATUS DISPC_REG(0x0018)
+#define DISPC_IRQENABLE DISPC_REG(0x001C)
+#define DISPC_CONTROL DISPC_REG(0x0040)
+#define DISPC_CONFIG DISPC_REG(0x0044)
+#define DISPC_CAPABLE DISPC_REG(0x0048)
+#define DISPC_DEFAULT_COLOR0 DISPC_REG(0x004C)
+#define DISPC_DEFAULT_COLOR1 DISPC_REG(0x0050)
+#define DISPC_TRANS_COLOR0 DISPC_REG(0x0054)
+#define DISPC_TRANS_COLOR1 DISPC_REG(0x0058)
+#define DISPC_LINE_STATUS DISPC_REG(0x005C)
+#define DISPC_LINE_NUMBER DISPC_REG(0x0060)
+#define DISPC_TIMING_H DISPC_REG(0x0064)
+#define DISPC_TIMING_V DISPC_REG(0x0068)
+#define DISPC_POL_FREQ DISPC_REG(0x006C)
+#define DISPC_DIVISOR DISPC_REG(0x0070)
+#define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074)
+#define DISPC_SIZE_DIG DISPC_REG(0x0078)
+#define DISPC_SIZE_LCD DISPC_REG(0x007C)
+
+/* DISPC GFX plane */
+#define DISPC_GFX_BA0 DISPC_REG(0x0080)
+#define DISPC_GFX_BA1 DISPC_REG(0x0084)
+#define DISPC_GFX_POSITION DISPC_REG(0x0088)
+#define DISPC_GFX_SIZE DISPC_REG(0x008C)
+#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0)
+#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4)
+#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8)
+#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC)
+#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0)
+#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4)
+#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8)
+
+#define DISPC_DATA_CYCLE1 DISPC_REG(0x01D4)
+#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8)
+#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC)
+
+#define DISPC_CPR_COEF_R DISPC_REG(0x0220)
+#define DISPC_CPR_COEF_G DISPC_REG(0x0224)
+#define DISPC_CPR_COEF_B DISPC_REG(0x0228)
+
+#define DISPC_GFX_PRELOAD DISPC_REG(0x022C)
+
+/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */
+#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx)
+
+#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000)
+#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004)
+#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008)
+#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C)
+#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010)
+#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014)
+#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018)
+#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C)
+#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020)
+#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024)
+#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028)
+#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C)
+#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030)
+
+/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
+#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8)
+/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
+#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8)
+/* coef index i = {0, 1, 2, 3, 4} */
+#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4)
+/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
+#define DISPC_VID_FIR_COEF_V(n, i) DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4)
+
+#define DISPC_VID_PRELOAD(n) DISPC_REG(0x230 + (n)*0x04)
+
+
+#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;
+};
+
+#define REG_GET(idx, start, end) \
+ FLD_GET(dispc_read_reg(idx), start, end)
+
+#define REG_FLD_MOD(idx, val, start, end) \
+ dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end))
+
+static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES,
+ DISPC_VID_ATTRIBUTES(0),
+ DISPC_VID_ATTRIBUTES(1) };
+
+static struct {
+ void __iomem *base;
+
+ u32 fifo_size[3];
+
+ 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;
+
+ u32 ctx[DISPC_SZ_REGS / sizeof(u32)];
+} dispc;
+
+static void _omap_dispc_set_irqs(void);
+
+static inline void dispc_write_reg(const struct dispc_reg idx, u32 val)
+{
+ __raw_writel(val, dispc.base + idx.idx);
+}
+
+static inline u32 dispc_read_reg(const struct dispc_reg idx)
+{
+ return __raw_readl(dispc.base + idx.idx);
+}
+
+#define SR(reg) \
+ dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
+#define RR(reg) \
+ dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)])
+
+void dispc_save_context(void)
+{
+ if (cpu_is_omap24xx())
+ return;
+
+ SR(SYSCONFIG);
+ SR(IRQENABLE);
+ SR(CONTROL);
+ SR(CONFIG);
+ SR(DEFAULT_COLOR0);
+ SR(DEFAULT_COLOR1);
+ SR(TRANS_COLOR0);
+ SR(TRANS_COLOR1);
+ SR(LINE_NUMBER);
+ SR(TIMING_H);
+ SR(TIMING_V);
+ SR(POL_FREQ);
+ SR(DIVISOR);
+ SR(GLOBAL_ALPHA);
+ SR(SIZE_DIG);
+ SR(SIZE_LCD);
+
+ SR(GFX_BA0);
+ SR(GFX_BA1);
+ SR(GFX_POSITION);
+ SR(GFX_SIZE);
+ SR(GFX_ATTRIBUTES);
+ SR(GFX_FIFO_THRESHOLD);
+ SR(GFX_ROW_INC);
+ SR(GFX_PIXEL_INC);
+ SR(GFX_WINDOW_SKIP);
+ SR(GFX_TABLE_BA);
+
+ SR(DATA_CYCLE1);
+ SR(DATA_CYCLE2);
+ SR(DATA_CYCLE3);
+
+ SR(CPR_COEF_R);
+ SR(CPR_COEF_G);
+ SR(CPR_COEF_B);
+
+ SR(GFX_PRELOAD);
+
+ /* VID1 */
+ SR(VID_BA0(0));
+ SR(VID_BA1(0));
+ SR(VID_POSITION(0));
+ SR(VID_SIZE(0));
+ SR(VID_ATTRIBUTES(0));
+ SR(VID_FIFO_THRESHOLD(0));
+ SR(VID_ROW_INC(0));
+ SR(VID_PIXEL_INC(0));
+ SR(VID_FIR(0));
+ SR(VID_PICTURE_SIZE(0));
+ SR(VID_ACCU0(0));
+ SR(VID_ACCU1(0));
+
+ SR(VID_FIR_COEF_H(0, 0));
+ SR(VID_FIR_COEF_H(0, 1));
+ SR(VID_FIR_COEF_H(0, 2));
+ SR(VID_FIR_COEF_H(0, 3));
+ SR(VID_FIR_COEF_H(0, 4));
+ SR(VID_FIR_COEF_H(0, 5));
+ SR(VID_FIR_COEF_H(0, 6));
+ SR(VID_FIR_COEF_H(0, 7));
+
+ SR(VID_FIR_COEF_HV(0, 0));
+ SR(VID_FIR_COEF_HV(0, 1));
+ SR(VID_FIR_COEF_HV(0, 2));
+ SR(VID_FIR_COEF_HV(0, 3));
+ SR(VID_FIR_COEF_HV(0, 4));
+ SR(VID_FIR_COEF_HV(0, 5));
+ SR(VID_FIR_COEF_HV(0, 6));
+ SR(VID_FIR_COEF_HV(0, 7));
+
+ SR(VID_CONV_COEF(0, 0));
+ SR(VID_CONV_COEF(0, 1));
+ SR(VID_CONV_COEF(0, 2));
+ SR(VID_CONV_COEF(0, 3));
+ SR(VID_CONV_COEF(0, 4));
+
+ SR(VID_FIR_COEF_V(0, 0));
+ SR(VID_FIR_COEF_V(0, 1));
+ SR(VID_FIR_COEF_V(0, 2));
+ SR(VID_FIR_COEF_V(0, 3));
+ SR(VID_FIR_COEF_V(0, 4));
+ SR(VID_FIR_COEF_V(0, 5));
+ SR(VID_FIR_COEF_V(0, 6));
+ SR(VID_FIR_COEF_V(0, 7));
+
+ SR(VID_PRELOAD(0));
+
+ /* VID2 */
+ SR(VID_BA0(1));
+ SR(VID_BA1(1));
+ SR(VID_POSITION(1));
+ SR(VID_SIZE(1));
+ SR(VID_ATTRIBUTES(1));
+ SR(VID_FIFO_THRESHOLD(1));
+ SR(VID_ROW_INC(1));
+ SR(VID_PIXEL_INC(1));
+ SR(VID_FIR(1));
+ SR(VID_PICTURE_SIZE(1));
+ SR(VID_ACCU0(1));
+ SR(VID_ACCU1(1));
+
+ SR(VID_FIR_COEF_H(1, 0));
+ SR(VID_FIR_COEF_H(1, 1));
+ SR(VID_FIR_COEF_H(1, 2));
+ SR(VID_FIR_COEF_H(1, 3));
+ SR(VID_FIR_COEF_H(1, 4));
+ SR(VID_FIR_COEF_H(1, 5));
+ SR(VID_FIR_COEF_H(1, 6));
+ SR(VID_FIR_COEF_H(1, 7));
+
+ SR(VID_FIR_COEF_HV(1, 0));
+ SR(VID_FIR_COEF_HV(1, 1));
+ SR(VID_FIR_COEF_HV(1, 2));
+ SR(VID_FIR_COEF_HV(1, 3));
+ SR(VID_FIR_COEF_HV(1, 4));
+ SR(VID_FIR_COEF_HV(1, 5));
+ SR(VID_FIR_COEF_HV(1, 6));
+ SR(VID_FIR_COEF_HV(1, 7));
+
+ SR(VID_CONV_COEF(1, 0));
+ SR(VID_CONV_COEF(1, 1));
+ SR(VID_CONV_COEF(1, 2));
+ SR(VID_CONV_COEF(1, 3));
+ SR(VID_CONV_COEF(1, 4));
+
+ SR(VID_FIR_COEF_V(1, 0));
+ SR(VID_FIR_COEF_V(1, 1));
+ SR(VID_FIR_COEF_V(1, 2));
+ SR(VID_FIR_COEF_V(1, 3));
+ SR(VID_FIR_COEF_V(1, 4));
+ SR(VID_FIR_COEF_V(1, 5));
+ SR(VID_FIR_COEF_V(1, 6));
+ SR(VID_FIR_COEF_V(1, 7));
+
+ SR(VID_PRELOAD(1));
+}
+
+void dispc_restore_context(void)
+{
+ RR(SYSCONFIG);
+ RR(IRQENABLE);
+ /*RR(CONTROL);*/
+ RR(CONFIG);
+ RR(DEFAULT_COLOR0);
+ RR(DEFAULT_COLOR1);
+ RR(TRANS_COLOR0);
+ RR(TRANS_COLOR1);
+ RR(LINE_NUMBER);
+ RR(TIMING_H);
+ RR(TIMING_V);
+ RR(POL_FREQ);
+ RR(DIVISOR);
+ RR(GLOBAL_ALPHA);
+ RR(SIZE_DIG);
+ RR(SIZE_LCD);
+
+ RR(GFX_BA0);
+ RR(GFX_BA1);
+ RR(GFX_POSITION);
+ RR(GFX_SIZE);
+ RR(GFX_ATTRIBUTES);
+ RR(GFX_FIFO_THRESHOLD);
+ RR(GFX_ROW_INC);
+ RR(GFX_PIXEL_INC);
+ RR(GFX_WINDOW_SKIP);
+ RR(GFX_TABLE_BA);
+
+ RR(DATA_CYCLE1);
+ RR(DATA_CYCLE2);
+ RR(DATA_CYCLE3);
+
+ RR(CPR_COEF_R);
+ RR(CPR_COEF_G);
+ RR(CPR_COEF_B);
+
+ RR(GFX_PRELOAD);
+
+ /* VID1 */
+ RR(VID_BA0(0));
+ RR(VID_BA1(0));
+ RR(VID_POSITION(0));
+ RR(VID_SIZE(0));
+ RR(VID_ATTRIBUTES(0));
+ RR(VID_FIFO_THRESHOLD(0));
+ RR(VID_ROW_INC(0));
+ RR(VID_PIXEL_INC(0));
+ RR(VID_FIR(0));
+ RR(VID_PICTURE_SIZE(0));
+ RR(VID_ACCU0(0));
+ RR(VID_ACCU1(0));
+
+ RR(VID_FIR_COEF_H(0, 0));
+ RR(VID_FIR_COEF_H(0, 1));
+ RR(VID_FIR_COEF_H(0, 2));
+ RR(VID_FIR_COEF_H(0, 3));
+ RR(VID_FIR_COEF_H(0, 4));
+ RR(VID_FIR_COEF_H(0, 5));
+ RR(VID_FIR_COEF_H(0, 6));
+ RR(VID_FIR_COEF_H(0, 7));
+
+ RR(VID_FIR_COEF_HV(0, 0));
+ RR(VID_FIR_COEF_HV(0, 1));
+ RR(VID_FIR_COEF_HV(0, 2));
+ RR(VID_FIR_COEF_HV(0, 3));
+ RR(VID_FIR_COEF_HV(0, 4));
+ RR(VID_FIR_COEF_HV(0, 5));
+ RR(VID_FIR_COEF_HV(0, 6));
+ RR(VID_FIR_COEF_HV(0, 7));
+
+ RR(VID_CONV_COEF(0, 0));
+ RR(VID_CONV_COEF(0, 1));
+ RR(VID_CONV_COEF(0, 2));
+ RR(VID_CONV_COEF(0, 3));
+ RR(VID_CONV_COEF(0, 4));
+
+ RR(VID_FIR_COEF_V(0, 0));
+ RR(VID_FIR_COEF_V(0, 1));
+ RR(VID_FIR_COEF_V(0, 2));
+ RR(VID_FIR_COEF_V(0, 3));
+ RR(VID_FIR_COEF_V(0, 4));
+ RR(VID_FIR_COEF_V(0, 5));
+ RR(VID_FIR_COEF_V(0, 6));
+ RR(VID_FIR_COEF_V(0, 7));
+
+ RR(VID_PRELOAD(0));
+
+ /* VID2 */
+ RR(VID_BA0(1));
+ RR(VID_BA1(1));
+ RR(VID_POSITION(1));
+ RR(VID_SIZE(1));
+ RR(VID_ATTRIBUTES(1));
+ RR(VID_FIFO_THRESHOLD(1));
+ RR(VID_ROW_INC(1));
+ RR(VID_PIXEL_INC(1));
+ RR(VID_FIR(1));
+ RR(VID_PICTURE_SIZE(1));
+ RR(VID_ACCU0(1));
+ RR(VID_ACCU1(1));
+
+ RR(VID_FIR_COEF_H(1, 0));
+ RR(VID_FIR_COEF_H(1, 1));
+ RR(VID_FIR_COEF_H(1, 2));
+ RR(VID_FIR_COEF_H(1, 3));
+ RR(VID_FIR_COEF_H(1, 4));
+ RR(VID_FIR_COEF_H(1, 5));
+ RR(VID_FIR_COEF_H(1, 6));
+ RR(VID_FIR_COEF_H(1, 7));
+
+ RR(VID_FIR_COEF_HV(1, 0));
+ RR(VID_FIR_COEF_HV(1, 1));
+ RR(VID_FIR_COEF_HV(1, 2));
+ RR(VID_FIR_COEF_HV(1, 3));
+ RR(VID_FIR_COEF_HV(1, 4));
+ RR(VID_FIR_COEF_HV(1, 5));
+ RR(VID_FIR_COEF_HV(1, 6));
+ RR(VID_FIR_COEF_HV(1, 7));
+
+ RR(VID_CONV_COEF(1, 0));
+ RR(VID_CONV_COEF(1, 1));
+ RR(VID_CONV_COEF(1, 2));
+ RR(VID_CONV_COEF(1, 3));
+ RR(VID_CONV_COEF(1, 4));
+
+ RR(VID_FIR_COEF_V(1, 0));
+ RR(VID_FIR_COEF_V(1, 1));
+ RR(VID_FIR_COEF_V(1, 2));
+ RR(VID_FIR_COEF_V(1, 3));
+ RR(VID_FIR_COEF_V(1, 4));
+ RR(VID_FIR_COEF_V(1, 5));
+ RR(VID_FIR_COEF_V(1, 6));
+ RR(VID_FIR_COEF_V(1, 7));
+
+ RR(VID_PRELOAD(1));
+
+ /* enable last, because LCD & DIGIT enable are here */
+ RR(CONTROL);
+}
+
+#undef SR
+#undef RR
+
+static inline void enable_clocks(bool enable)
+{
+ if (enable)
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ else
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+bool dispc_go_busy(enum omap_channel channel)
+{
+ int bit;
+
+ if (channel == OMAP_DSS_CHANNEL_LCD)
+ bit = 5; /* GOLCD */
+ else
+ bit = 6; /* GODIGIT */
+
+ return REG_GET(DISPC_CONTROL, bit, bit) == 1;
+}
+
+void dispc_go(enum omap_channel channel)
+{
+ int bit;
+
+ enable_clocks(1);
+
+ if (channel == OMAP_DSS_CHANNEL_LCD)
+ bit = 0; /* LCDENABLE */
+ else
+ bit = 1; /* DIGITALENABLE */
+
+ /* if the channel is not enabled, we don't need GO */
+ if (REG_GET(DISPC_CONTROL, bit, bit) == 0)
+ goto end;
+
+ if (channel == OMAP_DSS_CHANNEL_LCD)
+ bit = 5; /* GOLCD */
+ else
+ bit = 6; /* GODIGIT */
+
+ if (REG_GET(DISPC_CONTROL, bit, bit) == 1) {
+ DSSERR("GO bit not down for channel %d\n", channel);
+ goto end;
+ }
+
+ DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT");
+
+ REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
+end:
+ enable_clocks(0);
+}
+
+static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value)
+{
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value);
+}
+
+static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value)
+{
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value);
+}
+
+static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value)
+{
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value);
+}
+
+static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
+ int vscaleup, int five_taps)
+{
+ /* Coefficients for horizontal up-sampling */
+ static const u32 coef_hup[8] = {
+ 0x00800000,
+ 0x0D7CF800,
+ 0x1E70F5FF,
+ 0x335FF5FE,
+ 0xF74949F7,
+ 0xF55F33FB,
+ 0xF5701EFE,
+ 0xF87C0DFF,
+ };
+
+ /* Coefficients for horizontal down-sampling */
+ static const u32 coef_hdown[8] = {
+ 0x24382400,
+ 0x28371FFE,
+ 0x2C361BFB,
+ 0x303516F9,
+ 0x11343311,
+ 0x1635300C,
+ 0x1B362C08,
+ 0x1F372804,
+ };
+
+ /* Coefficients for horizontal and vertical up-sampling */
+ static const u32 coef_hvup[2][8] = {
+ {
+ 0x00800000,
+ 0x037B02FF,
+ 0x0C6F05FE,
+ 0x205907FB,
+ 0x00404000,
+ 0x075920FE,
+ 0x056F0CFF,
+ 0x027B0300,
+ },
+ {
+ 0x00800000,
+ 0x0D7CF8FF,
+ 0x1E70F5FE,
+ 0x335FF5FB,
+ 0xF7404000,
+ 0xF55F33FE,
+ 0xF5701EFF,
+ 0xF87C0D00,
+ },
+ };
+
+ /* Coefficients for horizontal and vertical down-sampling */
+ static const u32 coef_hvdown[2][8] = {
+ {
+ 0x24382400,
+ 0x28391F04,
+ 0x2D381B08,
+ 0x3237170C,
+ 0x123737F7,
+ 0x173732F9,
+ 0x1B382DFB,
+ 0x1F3928FE,
+ },
+ {
+ 0x24382400,
+ 0x28371F04,
+ 0x2C361B08,
+ 0x3035160C,
+ 0x113433F7,
+ 0x163530F9,
+ 0x1B362CFB,
+ 0x1F3728FE,
+ },
+ };
+
+ /* Coefficients for vertical up-sampling */
+ static const u32 coef_vup[8] = {
+ 0x00000000,
+ 0x0000FF00,
+ 0x0000FEFF,
+ 0x0000FBFE,
+ 0x000000F7,
+ 0x0000FEFB,
+ 0x0000FFFE,
+ 0x000000FF,
+ };
+
+
+ /* Coefficients for vertical down-sampling */
+ static const u32 coef_vdown[8] = {
+ 0x00000000,
+ 0x000004FE,
+ 0x000008FB,
+ 0x00000CF9,
+ 0x0000F711,
+ 0x0000F90C,
+ 0x0000FB08,
+ 0x0000FE04,
+ };
+
+ const u32 *h_coef;
+ const u32 *hv_coef;
+ const u32 *hv_coef_mod;
+ const u32 *v_coef;
+ int i;
+
+ if (hscaleup)
+ h_coef = coef_hup;
+ else
+ h_coef = coef_hdown;
+
+ if (vscaleup) {
+ hv_coef = coef_hvup[five_taps];
+ v_coef = coef_vup;
+
+ if (hscaleup)
+ hv_coef_mod = NULL;
+ else
+ hv_coef_mod = coef_hvdown[five_taps];
+ } else {
+ hv_coef = coef_hvdown[five_taps];
+ v_coef = coef_vdown;
+
+ if (hscaleup)
+ hv_coef_mod = coef_hvup[five_taps];
+ else
+ hv_coef_mod = NULL;
+ }
+
+ for (i = 0; i < 8; i++) {
+ u32 h, hv;
+
+ h = h_coef[i];
+
+ hv = hv_coef[i];
+
+ if (hv_coef_mod) {
+ hv &= 0xffffff00;
+ hv |= (hv_coef_mod[i] & 0xff);
+ }
+
+ _dispc_write_firh_reg(plane, i, h);
+ _dispc_write_firhv_reg(plane, i, hv);
+ }
+
+ if (!five_taps)
+ return;
+
+ for (i = 0; i < 8; i++) {
+ u32 v;
+ v = v_coef[i];
+ _dispc_write_firv_reg(plane, i, v);
+ }
+}
+
+static void _dispc_setup_color_conv_coef(void)
+{
+ const struct color_conv_coef {
+ int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
+ int full_range;
+ } ctbl_bt601_5 = {
+ 298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
+ };
+
+ const struct color_conv_coef *ct;
+
+#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))
+
+ ct = &ctbl_bt601_5;
+
+ dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry));
+ dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb));
+ dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr));
+ dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by));
+ dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb));
+
+ dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry));
+ dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb));
+ dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr));
+ dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by));
+ dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb));
+
+#undef CVAL
+
+ REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11);
+ REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11);
+}
+
+
+static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr)
+{
+ const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0,
+ DISPC_VID_BA0(0),
+ DISPC_VID_BA0(1) };
+
+ dispc_write_reg(ba0_reg[plane], paddr);
+}
+
+static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr)
+{
+ const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1,
+ DISPC_VID_BA1(0),
+ DISPC_VID_BA1(1) };
+
+ dispc_write_reg(ba1_reg[plane], paddr);
+}
+
+static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y)
+{
+ const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION,
+ DISPC_VID_POSITION(0),
+ DISPC_VID_POSITION(1) };
+
+ u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);
+ dispc_write_reg(pos_reg[plane], val);
+}
+
+static void _dispc_set_pic_size(enum omap_plane plane, int width, int height)
+{
+ const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE,
+ DISPC_VID_PICTURE_SIZE(0),
+ DISPC_VID_PICTURE_SIZE(1) };
+ u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+ dispc_write_reg(siz_reg[plane], val);
+}
+
+static void _dispc_set_vid_size(enum omap_plane plane, int width, int height)
+{
+ u32 val;
+ const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0),
+ DISPC_VID_SIZE(1) };
+
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+ dispc_write_reg(vsi_reg[plane-1], val);
+}
+
+static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha)
+{
+
+ BUG_ON(plane == OMAP_DSS_VIDEO1);
+
+ if (cpu_is_omap24xx())
+ return;
+
+ if (plane == OMAP_DSS_GFX)
+ REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0);
+ else if (plane == OMAP_DSS_VIDEO2)
+ REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16);
+}
+
+static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc)
+{
+ const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC,
+ DISPC_VID_PIXEL_INC(0),
+ DISPC_VID_PIXEL_INC(1) };
+
+ dispc_write_reg(ri_reg[plane], inc);
+}
+
+static void _dispc_set_row_inc(enum omap_plane plane, s32 inc)
+{
+ const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC,
+ DISPC_VID_ROW_INC(0),
+ DISPC_VID_ROW_INC(1) };
+
+ dispc_write_reg(ri_reg[plane], inc);
+}
+
+static void _dispc_set_color_mode(enum omap_plane plane,
+ enum omap_color_mode color_mode)
+{
+ u32 m = 0;
+
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_CLUT1:
+ m = 0x0; break;
+ case OMAP_DSS_COLOR_CLUT2:
+ m = 0x1; break;
+ case OMAP_DSS_COLOR_CLUT4:
+ m = 0x2; break;
+ case OMAP_DSS_COLOR_CLUT8:
+ m = 0x3; break;
+ case OMAP_DSS_COLOR_RGB12U:
+ m = 0x4; break;
+ case OMAP_DSS_COLOR_ARGB16:
+ m = 0x5; break;
+ case OMAP_DSS_COLOR_RGB16:
+ m = 0x6; break;
+ case OMAP_DSS_COLOR_RGB24U:
+ m = 0x8; break;
+ case OMAP_DSS_COLOR_RGB24P:
+ m = 0x9; break;
+ case OMAP_DSS_COLOR_YUV2:
+ m = 0xa; break;
+ case OMAP_DSS_COLOR_UYVY:
+ m = 0xb; break;
+ case OMAP_DSS_COLOR_ARGB32:
+ m = 0xc; break;
+ case OMAP_DSS_COLOR_RGBA32:
+ m = 0xd; break;
+ case OMAP_DSS_COLOR_RGBX32:
+ m = 0xe; break;
+ default:
+ BUG(); break;
+ }
+
+ REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1);
+}
+
+static void _dispc_set_channel_out(enum omap_plane plane,
+ enum omap_channel channel)
+{
+ int shift;
+ u32 val;
+
+ switch (plane) {
+ case OMAP_DSS_GFX:
+ shift = 8;
+ break;
+ case OMAP_DSS_VIDEO1:
+ case OMAP_DSS_VIDEO2:
+ shift = 16;
+ break;
+ default:
+ BUG();
+ return;
+ }
+
+ val = dispc_read_reg(dispc_reg_att[plane]);
+ val = FLD_MOD(val, channel, shift, shift);
+ dispc_write_reg(dispc_reg_att[plane], val);
+}
+
+void dispc_set_burst_size(enum omap_plane plane,
+ enum omap_burst_size burst_size)
+{
+ int shift;
+ u32 val;
+
+ enable_clocks(1);
+
+ switch (plane) {
+ case OMAP_DSS_GFX:
+ shift = 6;
+ break;
+ case OMAP_DSS_VIDEO1:
+ case OMAP_DSS_VIDEO2:
+ shift = 14;
+ break;
+ default:
+ BUG();
+ return;
+ }
+
+ val = dispc_read_reg(dispc_reg_att[plane]);
+ val = FLD_MOD(val, burst_size, shift+1, shift);
+ dispc_write_reg(dispc_reg_att[plane], val);
+
+ enable_clocks(0);
+}
+
+static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)
+{
+ u32 val;
+
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ val = dispc_read_reg(dispc_reg_att[plane]);
+ val = FLD_MOD(val, enable, 9, 9);
+ dispc_write_reg(dispc_reg_att[plane], val);
+}
+
+void dispc_enable_replication(enum omap_plane plane, bool enable)
+{
+ int bit;
+
+ if (plane == OMAP_DSS_GFX)
+ bit = 5;
+ else
+ bit = 10;
+
+ enable_clocks(1);
+ REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit);
+ enable_clocks(0);
+}
+
+void dispc_set_lcd_size(u16 width, u16 height)
+{
+ u32 val;
+ BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
+ val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+ enable_clocks(1);
+ dispc_write_reg(DISPC_SIZE_LCD, val);
+ enable_clocks(0);
+}
+
+void dispc_set_digit_size(u16 width, u16 height)
+{
+ u32 val;
+ BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
+ val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+ enable_clocks(1);
+ dispc_write_reg(DISPC_SIZE_DIG, val);
+ enable_clocks(0);
+}
+
+static void dispc_read_plane_fifo_sizes(void)
+{
+ const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS,
+ DISPC_VID_FIFO_SIZE_STATUS(0),
+ DISPC_VID_FIFO_SIZE_STATUS(1) };
+ u32 size;
+ int plane;
+
+ enable_clocks(1);
+
+ for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
+ if (cpu_is_omap24xx())
+ size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0);
+ else if (cpu_is_omap34xx())
+ size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0);
+ else
+ BUG();
+
+ dispc.fifo_size[plane] = size;
+ }
+
+ enable_clocks(0);
+}
+
+u32 dispc_get_plane_fifo_size(enum omap_plane plane)
+{
+ return dispc.fifo_size[plane];
+}
+
+void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
+{
+ const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD,
+ DISPC_VID_FIFO_THRESHOLD(0),
+ DISPC_VID_FIFO_THRESHOLD(1) };
+ enable_clocks(1);
+
+ DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",
+ plane,
+ REG_GET(ftrs_reg[plane], 11, 0),
+ REG_GET(ftrs_reg[plane], 27, 16),
+ low, high);
+
+ if (cpu_is_omap24xx())
+ dispc_write_reg(ftrs_reg[plane],
+ FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0));
+ else
+ dispc_write_reg(ftrs_reg[plane],
+ FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0));
+
+ enable_clocks(0);
+}
+
+void dispc_enable_fifomerge(bool enable)
+{
+ enable_clocks(1);
+
+ DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
+ REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
+
+ enable_clocks(0);
+}
+
+static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc)
+{
+ u32 val;
+ const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0),
+ DISPC_VID_FIR(1) };
+
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ if (cpu_is_omap24xx())
+ val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0);
+ else
+ val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0);
+ dispc_write_reg(fir_reg[plane-1], val);
+}
+
+static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu)
+{
+ u32 val;
+ const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0),
+ DISPC_VID_ACCU0(1) };
+
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0);
+ dispc_write_reg(ac0_reg[plane-1], val);
+}
+
+static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu)
+{
+ u32 val;
+ const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0),
+ DISPC_VID_ACCU1(1) };
+
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0);
+ dispc_write_reg(ac1_reg[plane-1], val);
+}
+
+
+static void _dispc_set_scaling(enum omap_plane plane,
+ u16 orig_width, u16 orig_height,
+ u16 out_width, u16 out_height,
+ bool ilace, bool five_taps,
+ bool fieldmode)
+{
+ int fir_hinc;
+ int fir_vinc;
+ int hscaleup, vscaleup;
+ int accu0 = 0;
+ int accu1 = 0;
+ u32 l;
+
+ BUG_ON(plane == OMAP_DSS_GFX);
+
+ hscaleup = orig_width <= out_width;
+ vscaleup = orig_height <= out_height;
+
+ _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps);
+
+ if (!orig_width || orig_width == out_width)
+ fir_hinc = 0;
+ else
+ fir_hinc = 1024 * orig_width / out_width;
+
+ if (!orig_height || orig_height == out_height)
+ fir_vinc = 0;
+ else
+ fir_vinc = 1024 * orig_height / out_height;
+
+ _dispc_set_fir(plane, fir_hinc, fir_vinc);
+
+ l = dispc_read_reg(dispc_reg_att[plane]);
+ l &= ~((0x0f << 5) | (0x3 << 21));
+
+ l |= fir_hinc ? (1 << 5) : 0;
+ l |= fir_vinc ? (1 << 6) : 0;
+
+ l |= hscaleup ? 0 : (1 << 7);
+ l |= vscaleup ? 0 : (1 << 8);
+
+ l |= five_taps ? (1 << 21) : 0;
+ l |= five_taps ? (1 << 22) : 0;
+
+ dispc_write_reg(dispc_reg_att[plane], l);
+
+ /*
+ * field 0 = even field = bottom field
+ * field 1 = odd field = top field
+ */
+ if (ilace && !fieldmode) {
+ accu1 = 0;
+ accu0 = (fir_vinc / 2) & 0x3ff;
+ if (accu0 >= 1024/2) {
+ accu1 = 1024/2;
+ accu0 -= accu1;
+ }
+ }
+
+ _dispc_set_vid_accu0(plane, 0, accu0);
+ _dispc_set_vid_accu1(plane, 0, accu1);
+}
+
+static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation,
+ bool mirroring, enum omap_color_mode color_mode)
+{
+ if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+ color_mode == OMAP_DSS_COLOR_UYVY) {
+ int vidrot = 0;
+
+ if (mirroring) {
+ switch (rotation) {
+ case OMAP_DSS_ROT_0:
+ vidrot = 2;
+ break;
+ case OMAP_DSS_ROT_90:
+ vidrot = 1;
+ break;
+ case OMAP_DSS_ROT_180:
+ vidrot = 0;
+ break;
+ case OMAP_DSS_ROT_270:
+ vidrot = 3;
+ break;
+ }
+ } else {
+ switch (rotation) {
+ case OMAP_DSS_ROT_0:
+ vidrot = 0;
+ break;
+ case OMAP_DSS_ROT_90:
+ vidrot = 1;
+ break;
+ case OMAP_DSS_ROT_180:
+ vidrot = 2;
+ break;
+ case OMAP_DSS_ROT_270:
+ vidrot = 3;
+ break;
+ }
+ }
+
+ REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12);
+
+ if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270)
+ REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18);
+ else
+ REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18);
+ } else {
+ REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12);
+ REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18);
+ }
+}
+
+static int color_mode_to_bpp(enum omap_color_mode color_mode)
+{
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_CLUT1:
+ return 1;
+ case OMAP_DSS_COLOR_CLUT2:
+ return 2;
+ case OMAP_DSS_COLOR_CLUT4:
+ return 4;
+ case OMAP_DSS_COLOR_CLUT8:
+ return 8;
+ case OMAP_DSS_COLOR_RGB12U:
+ case OMAP_DSS_COLOR_RGB16:
+ case OMAP_DSS_COLOR_ARGB16:
+ case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_UYVY:
+ return 16;
+ case OMAP_DSS_COLOR_RGB24P:
+ return 24;
+ case OMAP_DSS_COLOR_RGB24U:
+ case OMAP_DSS_COLOR_ARGB32:
+ case OMAP_DSS_COLOR_RGBA32:
+ case OMAP_DSS_COLOR_RGBX32:
+ return 32;
+ default:
+ BUG();
+ }
+}
+
+static s32 pixinc(int pixels, u8 ps)
+{
+ if (pixels == 1)
+ return 1;
+ else if (pixels > 1)
+ return 1 + (pixels - 1) * ps;
+ else if (pixels < 0)
+ return 1 - (-pixels + 1) * ps;
+ else
+ BUG();
+}
+
+static void calc_vrfb_rotation_offset(u8 rotation, bool mirror,
+ u16 screen_width,
+ u16 width, u16 height,
+ enum omap_color_mode color_mode, bool fieldmode,
+ unsigned int field_offset,
+ unsigned *offset0, unsigned *offset1,
+ s32 *row_inc, s32 *pix_inc)
+{
+ u8 ps;
+
+ /* FIXME CLUT formats */
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_CLUT1:
+ case OMAP_DSS_COLOR_CLUT2:
+ case OMAP_DSS_COLOR_CLUT4:
+ case OMAP_DSS_COLOR_CLUT8:
+ BUG();
+ return;
+ case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_UYVY:
+ ps = 4;
+ break;
+ default:
+ ps = color_mode_to_bpp(color_mode) / 8;
+ break;
+ }
+
+ DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
+ width, height);
+
+ /*
+ * field 0 = even field = bottom field
+ * field 1 = odd field = top field
+ */
+ switch (rotation + mirror * 4) {
+ case OMAP_DSS_ROT_0:
+ case OMAP_DSS_ROT_180:
+ /*
+ * If the pixel format is YUV or UYVY divide the width
+ * of the image by 2 for 0 and 180 degree rotation.
+ */
+ if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+ color_mode == OMAP_DSS_COLOR_UYVY)
+ width = width >> 1;
+ case OMAP_DSS_ROT_90:
+ case OMAP_DSS_ROT_270:
+ *offset1 = 0;
+ if (field_offset)
+ *offset0 = field_offset * screen_width * ps;
+ else
+ *offset0 = 0;
+
+ *row_inc = pixinc(1 + (screen_width - width) +
+ (fieldmode ? screen_width : 0),
+ ps);
+ *pix_inc = pixinc(1, ps);
+ break;
+
+ case OMAP_DSS_ROT_0 + 4:
+ case OMAP_DSS_ROT_180 + 4:
+ /* If the pixel format is YUV or UYVY divide the width
+ * of the image by 2 for 0 degree and 180 degree
+ */
+ if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+ color_mode == OMAP_DSS_COLOR_UYVY)
+ width = width >> 1;
+ case OMAP_DSS_ROT_90 + 4:
+ case OMAP_DSS_ROT_270 + 4:
+ *offset1 = 0;
+ if (field_offset)
+ *offset0 = field_offset * screen_width * ps;
+ else
+ *offset0 = 0;
+ *row_inc = pixinc(1 - (screen_width + width) -
+ (fieldmode ? screen_width : 0),
+ ps);
+ *pix_inc = pixinc(1, ps);
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+static void calc_dma_rotation_offset(u8 rotation, bool mirror,
+ u16 screen_width,
+ u16 width, u16 height,
+ enum omap_color_mode color_mode, bool fieldmode,
+ unsigned int field_offset,
+ unsigned *offset0, unsigned *offset1,
+ s32 *row_inc, s32 *pix_inc)
+{
+ u8 ps;
+ u16 fbw, fbh;
+
+ /* FIXME CLUT formats */
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_CLUT1:
+ case OMAP_DSS_COLOR_CLUT2:
+ case OMAP_DSS_COLOR_CLUT4:
+ case OMAP_DSS_COLOR_CLUT8:
+ BUG();
+ return;
+ default:
+ ps = color_mode_to_bpp(color_mode) / 8;
+ break;
+ }
+
+ DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
+ width, height);
+
+ /* width & height are overlay sizes, convert to fb sizes */
+
+ if (rotation == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) {
+ fbw = width;
+ fbh = height;
+ } else {
+ fbw = height;
+ fbh = width;
+ }
+
+ /*
+ * field 0 = even field = bottom field
+ * field 1 = odd field = top field
+ */
+ switch (rotation + mirror * 4) {
+ case OMAP_DSS_ROT_0:
+ *offset1 = 0;
+ if (field_offset)
+ *offset0 = *offset1 + field_offset * screen_width * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(1 + (screen_width - fbw) +
+ (fieldmode ? screen_width : 0),
+ ps);
+ *pix_inc = pixinc(1, ps);
+ break;
+ case OMAP_DSS_ROT_90:
+ *offset1 = screen_width * (fbh - 1) * ps;
+ if (field_offset)
+ *offset0 = *offset1 + field_offset * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(screen_width * (fbh - 1) + 1 +
+ (fieldmode ? 1 : 0), ps);
+ *pix_inc = pixinc(-screen_width, ps);
+ break;
+ case OMAP_DSS_ROT_180:
+ *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
+ if (field_offset)
+ *offset0 = *offset1 - field_offset * screen_width * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(-1 -
+ (screen_width - fbw) -
+ (fieldmode ? screen_width : 0),
+ ps);
+ *pix_inc = pixinc(-1, ps);
+ break;
+ case OMAP_DSS_ROT_270:
+ *offset1 = (fbw - 1) * ps;
+ if (field_offset)
+ *offset0 = *offset1 - field_offset * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(-screen_width * (fbh - 1) - 1 -
+ (fieldmode ? 1 : 0), ps);
+ *pix_inc = pixinc(screen_width, ps);
+ break;
+
+ /* mirroring */
+ case OMAP_DSS_ROT_0 + 4:
+ *offset1 = (fbw - 1) * ps;
+ if (field_offset)
+ *offset0 = *offset1 + field_offset * screen_width * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(screen_width * 2 - 1 +
+ (fieldmode ? screen_width : 0),
+ ps);
+ *pix_inc = pixinc(-1, ps);
+ break;
+
+ case OMAP_DSS_ROT_90 + 4:
+ *offset1 = 0;
+ if (field_offset)
+ *offset0 = *offset1 + field_offset * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(-screen_width * (fbh - 1) + 1 +
+ (fieldmode ? 1 : 0),
+ ps);
+ *pix_inc = pixinc(screen_width, ps);
+ break;
+
+ case OMAP_DSS_ROT_180 + 4:
+ *offset1 = screen_width * (fbh - 1) * ps;
+ if (field_offset)
+ *offset0 = *offset1 - field_offset * screen_width * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(1 - screen_width * 2 -
+ (fieldmode ? screen_width : 0),
+ ps);
+ *pix_inc = pixinc(1, ps);
+ break;
+
+ case OMAP_DSS_ROT_270 + 4:
+ *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
+ if (field_offset)
+ *offset0 = *offset1 - field_offset * ps;
+ else
+ *offset0 = *offset1;
+ *row_inc = pixinc(screen_width * (fbh - 1) - 1 -
+ (fieldmode ? 1 : 0),
+ ps);
+ *pix_inc = pixinc(-screen_width, ps);
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+static unsigned long calc_fclk_five_taps(u16 width, u16 height,
+ u16 out_width, u16 out_height, enum omap_color_mode color_mode)
+{
+ u32 fclk = 0;
+ /* FIXME venc pclk? */
+ u64 tmp, pclk = dispc_pclk_rate();
+
+ if (height > out_height) {
+ /* FIXME get real display PPL */
+ unsigned int ppl = 800;
+
+ tmp = pclk * height * out_width;
+ do_div(tmp, 2 * out_height * ppl);
+ fclk = tmp;
+
+ if (height > 2 * out_height && ppl != out_width) {
+ tmp = pclk * (height - 2 * out_height) * out_width;
+ do_div(tmp, 2 * out_height * (ppl - out_width));
+ fclk = max(fclk, (u32) tmp);
+ }
+ }
+
+ if (width > out_width) {
+ tmp = pclk * width;
+ do_div(tmp, out_width);
+ fclk = max(fclk, (u32) tmp);
+
+ if (color_mode == OMAP_DSS_COLOR_RGB24U)
+ fclk <<= 1;
+ }
+
+ return fclk;
+}
+
+static unsigned long calc_fclk(u16 width, u16 height,
+ u16 out_width, u16 out_height)
+{
+ unsigned int hf, vf;
+
+ /*
+ * FIXME how to determine the 'A' factor
+ * for the no downscaling case ?
+ */
+
+ if (width > 3 * out_width)
+ hf = 4;
+ else if (width > 2 * out_width)
+ hf = 3;
+ else if (width > out_width)
+ hf = 2;
+ else
+ hf = 1;
+
+ if (height > out_height)
+ vf = 2;
+ else
+ vf = 1;
+
+ /* FIXME venc pclk? */
+ return dispc_pclk_rate() * vf * hf;
+}
+
+void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out)
+{
+ enable_clocks(1);
+ _dispc_set_channel_out(plane, channel_out);
+ enable_clocks(0);
+}
+
+static int _dispc_setup_plane(enum omap_plane plane,
+ u32 paddr, u16 screen_width,
+ u16 pos_x, u16 pos_y,
+ u16 width, u16 height,
+ u16 out_width, u16 out_height,
+ enum omap_color_mode color_mode,
+ bool ilace,
+ enum omap_dss_rotation_type rotation_type,
+ u8 rotation, int mirror,
+ u8 global_alpha)
+{
+ const int maxdownscale = cpu_is_omap34xx() ? 4 : 2;
+ bool five_taps = 0;
+ bool fieldmode = 0;
+ int cconv = 0;
+ unsigned offset0, offset1;
+ s32 row_inc;
+ s32 pix_inc;
+ u16 frame_height = height;
+ unsigned int field_offset = 0;
+
+ if (paddr == 0)
+ return -EINVAL;
+
+ if (ilace && height == out_height)
+ fieldmode = 1;
+
+ if (ilace) {
+ if (fieldmode)
+ height /= 2;
+ pos_y /= 2;
+ out_height /= 2;
+
+ DSSDBG("adjusting for ilace: height %d, pos_y %d, "
+ "out_height %d\n",
+ height, pos_y, out_height);
+ }
+
+ if (plane == OMAP_DSS_GFX) {
+ if (width != out_width || height != out_height)
+ return -EINVAL;
+
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_ARGB16:
+ case OMAP_DSS_COLOR_ARGB32:
+ case OMAP_DSS_COLOR_RGBA32:
+ case OMAP_DSS_COLOR_RGBX32:
+ if (cpu_is_omap24xx())
+ return -EINVAL;
+ /* fall through */
+ case OMAP_DSS_COLOR_RGB12U:
+ case OMAP_DSS_COLOR_RGB16:
+ case OMAP_DSS_COLOR_RGB24P:
+ case OMAP_DSS_COLOR_RGB24U:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else {
+ /* video plane */
+
+ unsigned long fclk = 0;
+
+ if (out_width < width / maxdownscale ||
+ out_width > width * 8)
+ return -EINVAL;
+
+ if (out_height < height / maxdownscale ||
+ out_height > height * 8)
+ return -EINVAL;
+
+ switch (color_mode) {
+ case OMAP_DSS_COLOR_RGBX32:
+ case OMAP_DSS_COLOR_RGB12U:
+ if (cpu_is_omap24xx())
+ return -EINVAL;
+ /* fall through */
+ case OMAP_DSS_COLOR_RGB16:
+ case OMAP_DSS_COLOR_RGB24P:
+ case OMAP_DSS_COLOR_RGB24U:
+ break;
+
+ case OMAP_DSS_COLOR_ARGB16:
+ case OMAP_DSS_COLOR_ARGB32:
+ case OMAP_DSS_COLOR_RGBA32:
+ if (cpu_is_omap24xx())
+ return -EINVAL;
+ if (plane == OMAP_DSS_VIDEO1)
+ return -EINVAL;
+ break;
+
+ case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_UYVY:
+ cconv = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Must use 5-tap filter? */
+ five_taps = height > out_height * 2;
+
+ if (!five_taps) {
+ fclk = calc_fclk(width, height,
+ out_width, out_height);
+
+ /* Try 5-tap filter if 3-tap fclk is too high */
+ if (cpu_is_omap34xx() && height > out_height &&
+ fclk > dispc_fclk_rate())
+ five_taps = true;
+ }
+
+ if (width > (2048 >> five_taps)) {
+ DSSERR("failed to set up scaling, fclk too low\n");
+ return -EINVAL;
+ }
+
+ if (five_taps)
+ fclk = calc_fclk_five_taps(width, height,
+ out_width, out_height, color_mode);
+
+ DSSDBG("required fclk rate = %lu Hz\n", fclk);
+ DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate());
+
+ if (fclk > dispc_fclk_rate()) {
+ DSSERR("failed to set up scaling, "
+ "required fclk rate = %lu Hz, "
+ "current fclk rate = %lu Hz\n",
+ fclk, dispc_fclk_rate());
+ return -EINVAL;
+ }
+ }
+
+ if (ilace && !fieldmode) {
+ /*
+ * when downscaling the bottom field may have to start several
+ * source lines below the top field. Unfortunately ACCUI
+ * registers will only hold the fractional part of the offset
+ * so the integer part must be added to the base address of the
+ * bottom field.
+ */
+ if (!height || height == out_height)
+ field_offset = 0;
+ else
+ field_offset = height / out_height / 2;
+ }
+
+ /* Fields are independent but interleaved in memory. */
+ if (fieldmode)
+ field_offset = 1;
+
+ if (rotation_type == OMAP_DSS_ROT_DMA)
+ calc_dma_rotation_offset(rotation, mirror,
+ screen_width, width, frame_height, color_mode,
+ fieldmode, field_offset,
+ &offset0, &offset1, &row_inc, &pix_inc);
+ else
+ calc_vrfb_rotation_offset(rotation, mirror,
+ screen_width, width, frame_height, color_mode,
+ fieldmode, field_offset,
+ &offset0, &offset1, &row_inc, &pix_inc);
+
+ DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
+ offset0, offset1, row_inc, pix_inc);
+
+ _dispc_set_color_mode(plane, color_mode);
+
+ _dispc_set_plane_ba0(plane, paddr + offset0);
+ _dispc_set_plane_ba1(plane, paddr + offset1);
+
+ _dispc_set_row_inc(plane, row_inc);
+ _dispc_set_pix_inc(plane, pix_inc);
+
+ DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height,
+ out_width, out_height);
+
+ _dispc_set_plane_pos(plane, pos_x, pos_y);
+
+ _dispc_set_pic_size(plane, width, height);
+
+ if (plane != OMAP_DSS_GFX) {
+ _dispc_set_scaling(plane, width, height,
+ out_width, out_height,
+ ilace, five_taps, fieldmode);
+ _dispc_set_vid_size(plane, out_width, out_height);
+ _dispc_set_vid_color_conv(plane, cconv);
+ }
+
+ _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode);
+
+ if (plane != OMAP_DSS_VIDEO1)
+ _dispc_setup_global_alpha(plane, global_alpha);
+
+ return 0;
+}
+
+static void _dispc_enable_plane(enum omap_plane plane, bool enable)
+{
+ REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0);
+}
+
+static void dispc_disable_isr(void *data, u32 mask)
+{
+ struct completion *compl = data;
+ complete(compl);
+}
+
+static void _enable_lcd_out(bool enable)
+{
+ REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
+}
+
+void dispc_enable_lcd_out(bool enable)
+{
+ struct completion frame_done_completion;
+ bool is_on;
+ int r;
+
+ enable_clocks(1);
+
+ /* 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 = REG_GET(DISPC_CONTROL, 0, 0);
+
+ if (!enable && is_on) {
+ init_completion(&frame_done_completion);
+
+ r = omap_dispc_register_isr(dispc_disable_isr,
+ &frame_done_completion,
+ DISPC_IRQ_FRAMEDONE);
+
+ if (r)
+ DSSERR("failed to register FRAMEDONE isr\n");
+ }
+
+ _enable_lcd_out(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,
+ DISPC_IRQ_FRAMEDONE);
+
+ if (r)
+ DSSERR("failed to unregister FRAMEDONE isr\n");
+ }
+
+ enable_clocks(0);
+}
+
+static void _enable_digit_out(bool enable)
+{
+ REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
+}
+
+void dispc_enable_digit_out(bool enable)
+{
+ struct completion frame_done_completion;
+ int r;
+
+ enable_clocks(1);
+
+ if (REG_GET(DISPC_CONTROL, 1, 1) == enable) {
+ enable_clocks(0);
+ return;
+ }
+
+ 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);
+
+ r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion,
+ DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+ if (r)
+ DSSERR("failed to register EVSYNC isr\n");
+
+ _enable_digit_out(enable);
+
+ /* 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 */
+ if (!wait_for_completion_timeout(&frame_done_completion,
+ msecs_to_jiffies(100)))
+ DSSERR("timeout waiting for EVSYNC\n");
+
+ if (!wait_for_completion_timeout(&frame_done_completion,
+ msecs_to_jiffies(100)))
+ DSSERR("timeout waiting for EVSYNC\n");
+
+ r = omap_dispc_unregister_isr(dispc_disable_isr,
+ &frame_done_completion,
+ DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+ if (r)
+ DSSERR("failed to unregister EVSYNC isr\n");
+
+ if (enable) {
+ unsigned long flags;
+ spin_lock_irqsave(&dispc.irq_lock, flags);
+ dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
+ dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
+ _omap_dispc_set_irqs();
+ spin_unlock_irqrestore(&dispc.irq_lock, flags);
+ }
+
+ enable_clocks(0);
+}
+
+void dispc_lcd_enable_signal_polarity(bool act_high)
+{
+ enable_clocks(1);
+ REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29);
+ enable_clocks(0);
+}
+
+void dispc_lcd_enable_signal(bool enable)
+{
+ enable_clocks(1);
+ REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28);
+ enable_clocks(0);
+}
+
+void dispc_pck_free_enable(bool enable)
+{
+ enable_clocks(1);
+ REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
+ enable_clocks(0);
+}
+
+void dispc_enable_fifohandcheck(bool enable)
+{
+ enable_clocks(1);
+ REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16);
+ enable_clocks(0);
+}
+
+
+void dispc_set_lcd_display_type(enum omap_lcd_display_type type)
+{
+ int mode;
+
+ switch (type) {
+ case OMAP_DSS_LCD_DISPLAY_STN:
+ mode = 0;
+ break;
+
+ case OMAP_DSS_LCD_DISPLAY_TFT:
+ mode = 1;
+ break;
+
+ default:
+ BUG();
+ return;
+ }
+
+ enable_clocks(1);
+ REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3);
+ enable_clocks(0);
+}
+
+void dispc_set_loadmode(enum omap_dss_load_mode mode)
+{
+ enable_clocks(1);
+ REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1);
+ enable_clocks(0);
+}
+
+
+void dispc_set_default_color(enum omap_channel channel, u32 color)
+{
+ const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0,
+ DISPC_DEFAULT_COLOR1 };
+
+ enable_clocks(1);
+ dispc_write_reg(def_reg[channel], color);
+ enable_clocks(0);
+}
+
+u32 dispc_get_default_color(enum omap_channel channel)
+{
+ const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0,
+ DISPC_DEFAULT_COLOR1 };
+ u32 l;
+
+ BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT &&
+ channel != OMAP_DSS_CHANNEL_LCD);
+
+ enable_clocks(1);
+ l = dispc_read_reg(def_reg[channel]);
+ enable_clocks(0);
+
+ return l;
+}
+
+void dispc_set_trans_key(enum omap_channel ch,
+ enum omap_dss_trans_key_type type,
+ u32 trans_key)
+{
+ const struct dispc_reg tr_reg[] = {
+ DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 };
+
+ enable_clocks(1);
+ if (ch == OMAP_DSS_CHANNEL_LCD)
+ REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);
+ else /* OMAP_DSS_CHANNEL_DIGIT */
+ REG_FLD_MOD(DISPC_CONFIG, type, 13, 13);
+
+ dispc_write_reg(tr_reg[ch], trans_key);
+ enable_clocks(0);
+}
+
+void dispc_get_trans_key(enum omap_channel ch,
+ enum omap_dss_trans_key_type *type,
+ u32 *trans_key)
+{
+ const struct dispc_reg tr_reg[] = {
+ DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 };
+
+ enable_clocks(1);
+ if (type) {
+ if (ch == OMAP_DSS_CHANNEL_LCD)
+ *type = REG_GET(DISPC_CONFIG, 11, 11);
+ else if (ch == OMAP_DSS_CHANNEL_DIGIT)
+ *type = REG_GET(DISPC_CONFIG, 13, 13);
+ else
+ BUG();
+ }
+
+ if (trans_key)
+ *trans_key = dispc_read_reg(tr_reg[ch]);
+ enable_clocks(0);
+}
+
+void dispc_enable_trans_key(enum omap_channel ch, bool enable)
+{
+ enable_clocks(1);
+ if (ch == OMAP_DSS_CHANNEL_LCD)
+ REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
+ else /* OMAP_DSS_CHANNEL_DIGIT */
+ REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);
+ enable_clocks(0);
+}
+void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)
+{
+ if (cpu_is_omap24xx())
+ return;
+
+ enable_clocks(1);
+ if (ch == OMAP_DSS_CHANNEL_LCD)
+ REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);
+ else /* OMAP_DSS_CHANNEL_DIGIT */
+ REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
+ enable_clocks(0);
+}
+bool dispc_alpha_blending_enabled(enum omap_channel ch)
+{
+ bool enabled;
+
+ if (cpu_is_omap24xx())
+ return false;
+
+ enable_clocks(1);
+ if (ch == OMAP_DSS_CHANNEL_LCD)
+ enabled = REG_GET(DISPC_CONFIG, 18, 18);
+ else if (ch == OMAP_DSS_CHANNEL_DIGIT)
+ enabled = REG_GET(DISPC_CONFIG, 18, 18);
+ else
+ BUG();
+ enable_clocks(0);
+
+ return enabled;
+
+}
+
+
+bool dispc_trans_key_enabled(enum omap_channel ch)
+{
+ bool enabled;
+
+ enable_clocks(1);
+ if (ch == OMAP_DSS_CHANNEL_LCD)
+ enabled = REG_GET(DISPC_CONFIG, 10, 10);
+ else if (ch == OMAP_DSS_CHANNEL_DIGIT)
+ enabled = REG_GET(DISPC_CONFIG, 12, 12);
+ else
+ BUG();
+ enable_clocks(0);
+
+ return enabled;
+}
+
+
+void dispc_set_tft_data_lines(u8 data_lines)
+{
+ int code;
+
+ switch (data_lines) {
+ case 12:
+ code = 0;
+ break;
+ case 16:
+ code = 1;
+ break;
+ case 18:
+ code = 2;
+ break;
+ case 24:
+ code = 3;
+ break;
+ default:
+ BUG();
+ return;
+ }
+
+ enable_clocks(1);
+ REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
+ enable_clocks(0);
+}
+
+void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode)
+{
+ u32 l;
+ int stallmode;
+ int gpout0 = 1;
+ int gpout1;
+
+ switch (mode) {
+ case OMAP_DSS_PARALLELMODE_BYPASS:
+ stallmode = 0;
+ gpout1 = 1;
+ break;
+
+ case OMAP_DSS_PARALLELMODE_RFBI:
+ stallmode = 1;
+ gpout1 = 0;
+ break;
+
+ case OMAP_DSS_PARALLELMODE_DSI:
+ stallmode = 1;
+ gpout1 = 1;
+ break;
+
+ default:
+ BUG();
+ return;
+ }
+
+ enable_clocks(1);
+
+ l = dispc_read_reg(DISPC_CONTROL);
+
+ l = FLD_MOD(l, stallmode, 11, 11);
+ l = FLD_MOD(l, gpout0, 15, 15);
+ l = FLD_MOD(l, gpout1, 16, 16);
+
+ dispc_write_reg(DISPC_CONTROL, l);
+
+ enable_clocks(0);
+}
+
+static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
+ int vsw, int vfp, int vbp)
+{
+ if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
+ if (hsw < 1 || hsw > 64 ||
+ hfp < 1 || hfp > 256 ||
+ hbp < 1 || hbp > 256 ||
+ vsw < 1 || vsw > 64 ||
+ vfp < 0 || vfp > 255 ||
+ vbp < 0 || vbp > 255)
+ return false;
+ } else {
+ if (hsw < 1 || hsw > 256 ||
+ hfp < 1 || hfp > 4096 ||
+ hbp < 1 || hbp > 4096 ||
+ vsw < 1 || vsw > 256 ||
+ vfp < 0 || vfp > 4095 ||
+ vbp < 0 || vbp > 4095)
+ return false;
+ }
+
+ return true;
+}
+
+bool dispc_lcd_timings_ok(struct omap_video_timings *timings)
+{
+ return _dispc_lcd_timings_ok(timings->hsw, timings->hfp,
+ timings->hbp, timings->vsw,
+ timings->vfp, timings->vbp);
+}
+
+static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp,
+ int vsw, int vfp, int vbp)
+{
+ u32 timing_h, timing_v;
+
+ if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
+ timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) |
+ FLD_VAL(hbp-1, 27, 20);
+
+ timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) |
+ FLD_VAL(vbp, 27, 20);
+ } else {
+ timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) |
+ FLD_VAL(hbp-1, 31, 20);
+
+ timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) |
+ FLD_VAL(vbp, 31, 20);
+ }
+
+ enable_clocks(1);
+ dispc_write_reg(DISPC_TIMING_H, timing_h);
+ dispc_write_reg(DISPC_TIMING_V, timing_v);
+ enable_clocks(0);
+}
+
+/* change name to mode? */
+void dispc_set_lcd_timings(struct omap_video_timings *timings)
+{
+ unsigned xtot, ytot;
+ unsigned long ht, vt;
+
+ if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp,
+ timings->hbp, timings->vsw,
+ timings->vfp, timings->vbp))
+ BUG();
+
+ _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp,
+ timings->vsw, timings->vfp, timings->vbp);
+
+ dispc_set_lcd_size(timings->x_res, timings->y_res);
+
+ xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp;
+ ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp;
+
+ ht = (timings->pixel_clock * 1000) / xtot;
+ vt = (timings->pixel_clock * 1000) / xtot / ytot;
+
+ DSSDBG("xres %u yres %u\n", timings->x_res, timings->y_res);
+ DSSDBG("pck %u\n", timings->pixel_clock);
+ DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
+ timings->hsw, timings->hfp, timings->hbp,
+ timings->vsw, timings->vfp, timings->vbp);
+
+ DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
+}
+
+static void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div)
+{
+ BUG_ON(lck_div < 1);
+ BUG_ON(pck_div < 2);
+
+ enable_clocks(1);
+ dispc_write_reg(DISPC_DIVISOR,
+ FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
+ enable_clocks(0);
+}
+
+static void dispc_get_lcd_divisor(int *lck_div, int *pck_div)
+{
+ u32 l;
+ l = dispc_read_reg(DISPC_DIVISOR);
+ *lck_div = FLD_GET(l, 23, 16);
+ *pck_div = FLD_GET(l, 7, 0);
+}
+
+unsigned long dispc_fclk_rate(void)
+{
+ unsigned long r = 0;
+
+ if (dss_get_dispc_clk_source() == 0)
+ r = dss_clk_get_rate(DSS_CLK_FCK1);
+ else
+#ifdef CONFIG_OMAP2_DSS_DSI
+ r = dsi_get_dsi1_pll_rate();
+#else
+ BUG();
+#endif
+ return r;
+}
+
+unsigned long dispc_lclk_rate(void)
+{
+ int lcd;
+ unsigned long r;
+ u32 l;
+
+ l = dispc_read_reg(DISPC_DIVISOR);
+
+ lcd = FLD_GET(l, 23, 16);
+
+ r = dispc_fclk_rate();
+
+ return r / lcd;
+}
+
+unsigned long dispc_pclk_rate(void)
+{
+ int lcd, pcd;
+ unsigned long r;
+ u32 l;
+
+ l = dispc_read_reg(DISPC_DIVISOR);
+
+ lcd = FLD_GET(l, 23, 16);
+ pcd = FLD_GET(l, 7, 0);
+
+ r = dispc_fclk_rate();
+
+ return r / lcd / pcd;
+}
+
+void dispc_dump_clocks(struct seq_file *s)
+{
+ int lcd, pcd;
+
+ enable_clocks(1);
+
+ dispc_get_lcd_divisor(&lcd, &pcd);
+
+ seq_printf(s, "- DISPC -\n");
+
+ seq_printf(s, "dispc fclk source = %s\n",
+ dss_get_dispc_clk_source() == 0 ?
+ "dss1_alwon_fclk" : "dsi1_pll_fclk");
+
+ seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate());
+ seq_printf(s, "lck\t\t%-16lulck div\t%u\n", dispc_lclk_rate(), lcd);
+ seq_printf(s, "pck\t\t%-16lupck div\t%u\n", dispc_pclk_rate(), pcd);
+
+ enable_clocks(0);
+}
+
+void dispc_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r))
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ DUMPREG(DISPC_REVISION);
+ DUMPREG(DISPC_SYSCONFIG);
+ DUMPREG(DISPC_SYSSTATUS);
+ DUMPREG(DISPC_IRQSTATUS);
+ DUMPREG(DISPC_IRQENABLE);
+ DUMPREG(DISPC_CONTROL);
+ DUMPREG(DISPC_CONFIG);
+ DUMPREG(DISPC_CAPABLE);
+ DUMPREG(DISPC_DEFAULT_COLOR0);
+ DUMPREG(DISPC_DEFAULT_COLOR1);
+ DUMPREG(DISPC_TRANS_COLOR0);
+ DUMPREG(DISPC_TRANS_COLOR1);
+ DUMPREG(DISPC_LINE_STATUS);
+ DUMPREG(DISPC_LINE_NUMBER);
+ DUMPREG(DISPC_TIMING_H);
+ DUMPREG(DISPC_TIMING_V);
+ DUMPREG(DISPC_POL_FREQ);
+ DUMPREG(DISPC_DIVISOR);
+ DUMPREG(DISPC_GLOBAL_ALPHA);
+ DUMPREG(DISPC_SIZE_DIG);
+ DUMPREG(DISPC_SIZE_LCD);
+
+ DUMPREG(DISPC_GFX_BA0);
+ DUMPREG(DISPC_GFX_BA1);
+ DUMPREG(DISPC_GFX_POSITION);
+ DUMPREG(DISPC_GFX_SIZE);
+ DUMPREG(DISPC_GFX_ATTRIBUTES);
+ DUMPREG(DISPC_GFX_FIFO_THRESHOLD);
+ DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS);
+ DUMPREG(DISPC_GFX_ROW_INC);
+ DUMPREG(DISPC_GFX_PIXEL_INC);
+ DUMPREG(DISPC_GFX_WINDOW_SKIP);
+ DUMPREG(DISPC_GFX_TABLE_BA);
+
+ DUMPREG(DISPC_DATA_CYCLE1);
+ DUMPREG(DISPC_DATA_CYCLE2);
+ DUMPREG(DISPC_DATA_CYCLE3);
+
+ DUMPREG(DISPC_CPR_COEF_R);
+ DUMPREG(DISPC_CPR_COEF_G);
+ DUMPREG(DISPC_CPR_COEF_B);
+
+ DUMPREG(DISPC_GFX_PRELOAD);
+
+ DUMPREG(DISPC_VID_BA0(0));
+ DUMPREG(DISPC_VID_BA1(0));
+ DUMPREG(DISPC_VID_POSITION(0));
+ DUMPREG(DISPC_VID_SIZE(0));
+ DUMPREG(DISPC_VID_ATTRIBUTES(0));
+ DUMPREG(DISPC_VID_FIFO_THRESHOLD(0));
+ DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0));
+ DUMPREG(DISPC_VID_ROW_INC(0));
+ DUMPREG(DISPC_VID_PIXEL_INC(0));
+ DUMPREG(DISPC_VID_FIR(0));
+ DUMPREG(DISPC_VID_PICTURE_SIZE(0));
+ DUMPREG(DISPC_VID_ACCU0(0));
+ DUMPREG(DISPC_VID_ACCU1(0));
+
+ DUMPREG(DISPC_VID_BA0(1));
+ DUMPREG(DISPC_VID_BA1(1));
+ DUMPREG(DISPC_VID_POSITION(1));
+ DUMPREG(DISPC_VID_SIZE(1));
+ DUMPREG(DISPC_VID_ATTRIBUTES(1));
+ DUMPREG(DISPC_VID_FIFO_THRESHOLD(1));
+ DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1));
+ DUMPREG(DISPC_VID_ROW_INC(1));
+ DUMPREG(DISPC_VID_PIXEL_INC(1));
+ DUMPREG(DISPC_VID_FIR(1));
+ DUMPREG(DISPC_VID_PICTURE_SIZE(1));
+ DUMPREG(DISPC_VID_ACCU0(1));
+ DUMPREG(DISPC_VID_ACCU1(1));
+
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 0));
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 1));
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 2));
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 3));
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 5));
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 6));
+ DUMPREG(DISPC_VID_FIR_COEF_H(0, 7));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7));
+ DUMPREG(DISPC_VID_CONV_COEF(0, 0));
+ DUMPREG(DISPC_VID_CONV_COEF(0, 1));
+ DUMPREG(DISPC_VID_CONV_COEF(0, 2));
+ DUMPREG(DISPC_VID_CONV_COEF(0, 3));
+ DUMPREG(DISPC_VID_CONV_COEF(0, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 0));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 1));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 2));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 3));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 5));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 6));
+ DUMPREG(DISPC_VID_FIR_COEF_V(0, 7));
+
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 0));
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 1));
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 2));
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 3));
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 5));
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 6));
+ DUMPREG(DISPC_VID_FIR_COEF_H(1, 7));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6));
+ DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7));
+ DUMPREG(DISPC_VID_CONV_COEF(1, 0));
+ DUMPREG(DISPC_VID_CONV_COEF(1, 1));
+ DUMPREG(DISPC_VID_CONV_COEF(1, 2));
+ DUMPREG(DISPC_VID_CONV_COEF(1, 3));
+ DUMPREG(DISPC_VID_CONV_COEF(1, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 0));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 1));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 2));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 3));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 4));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 5));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 6));
+ DUMPREG(DISPC_VID_FIR_COEF_V(1, 7));
+
+ DUMPREG(DISPC_VID_PRELOAD(0));
+ DUMPREG(DISPC_VID_PRELOAD(1));
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc,
+ bool ihs, bool ivs, u8 acbi, u8 acb)
+{
+ u32 l = 0;
+
+ DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n",
+ onoff, rf, ieo, ipc, ihs, ivs, acbi, acb);
+
+ l |= FLD_VAL(onoff, 17, 17);
+ l |= FLD_VAL(rf, 16, 16);
+ l |= FLD_VAL(ieo, 15, 15);
+ l |= FLD_VAL(ipc, 14, 14);
+ l |= FLD_VAL(ihs, 13, 13);
+ l |= FLD_VAL(ivs, 12, 12);
+ l |= FLD_VAL(acbi, 11, 8);
+ l |= FLD_VAL(acb, 7, 0);
+
+ enable_clocks(1);
+ dispc_write_reg(DISPC_POL_FREQ, l);
+ enable_clocks(0);
+}
+
+void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb)
+{
+ _dispc_set_pol_freq((config & OMAP_DSS_LCD_ONOFF) != 0,
+ (config & OMAP_DSS_LCD_RF) != 0,
+ (config & OMAP_DSS_LCD_IEO) != 0,
+ (config & OMAP_DSS_LCD_IPC) != 0,
+ (config & OMAP_DSS_LCD_IHS) != 0,
+ (config & OMAP_DSS_LCD_IVS) != 0,
+ acbi, acb);
+}
+
+/* with fck as input clock rate, find dispc dividers that produce req_pck */
+void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+ struct dispc_clock_info *cinfo)
+{
+ u16 pcd_min = is_tft ? 2 : 3;
+ unsigned long best_pck;
+ u16 best_ld, cur_ld;
+ u16 best_pd, cur_pd;
+
+ best_pck = 0;
+ best_ld = 0;
+ best_pd = 0;
+
+ for (cur_ld = 1; cur_ld <= 255; ++cur_ld) {
+ unsigned long lck = fck / cur_ld;
+
+ for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) {
+ unsigned long pck = lck / cur_pd;
+ long old_delta = abs(best_pck - req_pck);
+ long new_delta = abs(pck - req_pck);
+
+ if (best_pck == 0 || new_delta < old_delta) {
+ best_pck = pck;
+ best_ld = cur_ld;
+ best_pd = cur_pd;
+
+ if (pck == req_pck)
+ goto found;
+ }
+
+ if (pck < req_pck)
+ break;
+ }
+
+ if (lck / pcd_min < req_pck)
+ break;
+ }
+
+found:
+ cinfo->lck_div = best_ld;
+ cinfo->pck_div = best_pd;
+ cinfo->lck = fck / cinfo->lck_div;
+ cinfo->pck = cinfo->lck / cinfo->pck_div;
+}
+
+/* calculate clock rates using dividers in cinfo */
+int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
+ struct dispc_clock_info *cinfo)
+{
+ if (cinfo->lck_div > 255 || cinfo->lck_div == 0)
+ return -EINVAL;
+ if (cinfo->pck_div < 2 || cinfo->pck_div > 255)
+ return -EINVAL;
+
+ cinfo->lck = dispc_fclk_rate / cinfo->lck_div;
+ cinfo->pck = cinfo->lck / cinfo->pck_div;
+
+ return 0;
+}
+
+int dispc_set_clock_div(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);
+
+ dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div);
+
+ return 0;
+}
+
+int dispc_get_clock_div(struct dispc_clock_info *cinfo)
+{
+ unsigned long fck;
+
+ fck = dispc_fclk_rate();
+
+ cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16);
+ cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0);
+
+ cinfo->lck = fck / cinfo->lck_div;
+ cinfo->pck = cinfo->lck / cinfo->pck_div;
+
+ return 0;
+}
+
+/* dispc.irq_lock has to be locked by the caller */
+static void _omap_dispc_set_irqs(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;
+ }
+
+ enable_clocks(1);
+
+ 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);
+
+ enable_clocks(0);
+}
+
+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;
+ }
+
+ _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);
+ PIS(SYNC_LOST);
+ PIS(SYNC_LOST_DIGIT);
+#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. */
+void dispc_irq_handler(void)
+{
+ int i;
+ u32 irqstatus;
+ 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);
+
+#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);
+}
+
+static void dispc_error_worker(struct work_struct *work)
+{
+ int i;
+ u32 errors;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dispc.irq_lock, flags);
+ errors = dispc.error_irqs;
+ dispc.error_irqs = 0;
+ spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+ if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) {
+ DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n");
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ if (ovl->id == 0) {
+ dispc_enable_plane(ovl->id, 0);
+ dispc_go(ovl->manager->id);
+ mdelay(50);
+ break;
+ }
+ }
+ }
+
+ if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) {
+ DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n");
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ if (ovl->id == 1) {
+ dispc_enable_plane(ovl->id, 0);
+ dispc_go(ovl->manager->id);
+ mdelay(50);
+ break;
+ }
+ }
+ }
+
+ if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) {
+ DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n");
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ if (ovl->id == 2) {
+ dispc_enable_plane(ovl->id, 0);
+ dispc_go(ovl->manager->id);
+ mdelay(50);
+ break;
+ }
+ }
+ }
+
+ if (errors & DISPC_IRQ_SYNC_LOST) {
+ struct omap_overlay_manager *manager = NULL;
+ bool enable = false;
+
+ DSSERR("SYNC_LOST, disabling LCD\n");
+
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+ struct omap_overlay_manager *mgr;
+ mgr = omap_dss_get_overlay_manager(i);
+
+ if (mgr->id == OMAP_DSS_CHANNEL_LCD) {
+ manager = mgr;
+ enable = mgr->device->state ==
+ OMAP_DSS_DISPLAY_ACTIVE;
+ mgr->device->disable(mgr->device);
+ break;
+ }
+ }
+
+ if (manager) {
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ if (ovl->id != 0 && ovl->manager == manager)
+ dispc_enable_plane(ovl->id, 0);
+ }
+
+ dispc_go(manager->id);
+ mdelay(50);
+ if (enable)
+ manager->device->enable(manager->device);
+ }
+ }
+
+ if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) {
+ struct omap_overlay_manager *manager = NULL;
+ bool enable = false;
+
+ DSSERR("SYNC_LOST_DIGIT, disabling TV\n");
+
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+ struct omap_overlay_manager *mgr;
+ mgr = omap_dss_get_overlay_manager(i);
+
+ if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) {
+ manager = mgr;
+ enable = mgr->device->state ==
+ OMAP_DSS_DISPLAY_ACTIVE;
+ mgr->device->disable(mgr->device);
+ break;
+ }
+ }
+
+ if (manager) {
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ if (ovl->id != 0 && ovl->manager == manager)
+ dispc_enable_plane(ovl->id, 0);
+ }
+
+ dispc_go(manager->id);
+ mdelay(50);
+ if (enable)
+ manager->device->enable(manager->device);
+ }
+ }
+
+ 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);
+
+ if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC)
+ mgr->device->disable(mgr->device);
+ }
+ }
+
+ spin_lock_irqsave(&dispc.irq_lock, flags);
+ dispc.irq_error_mask |= errors;
+ _omap_dispc_set_irqs();
+ spin_unlock_irqrestore(&dispc.irq_lock, flags);
+}
+
+int omap_dispc_wait_for_irq_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_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;
+}
+
+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;
+}
+
+#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
+void dispc_fake_vsync_irq(void)
+{
+ u32 irqstatus = DISPC_IRQ_VSYNC;
+ int i;
+
+ local_irq_disable();
+
+ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+ struct omap_dispc_isr_data *isr_data;
+ isr_data = &dispc.registered_isr[i];
+
+ if (!isr_data->isr)
+ continue;
+
+ if (isr_data->mask & irqstatus)
+ isr_data->isr(isr_data->arg, irqstatus);
+ }
+
+ local_irq_enable();
+}
+#endif
+
+static void _omap_dispc_initialize_irq(void)
+{
+ 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;
+
+ /* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
+ * so clear it */
+ dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS));
+
+ _omap_dispc_set_irqs();
+
+ spin_unlock_irqrestore(&dispc.irq_lock, flags);
+}
+
+void dispc_enable_sidle(void)
+{
+ REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */
+}
+
+void dispc_disable_sidle(void)
+{
+ REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */
+}
+
+static void _omap_dispc_initial_config(void)
+{
+ u32 l;
+
+ l = dispc_read_reg(DISPC_SYSCONFIG);
+ l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */
+ l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */
+ l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */
+ l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */
+ dispc_write_reg(DISPC_SYSCONFIG, l);
+
+ /* FUNCGATED */
+ REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9);
+
+ /* L3 firewall setting: enable access to OCM RAM */
+ /* XXX this should be somewhere in plat-omap */
+ if (cpu_is_omap24xx())
+ __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0));
+
+ _dispc_setup_color_conv_coef();
+
+ dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
+
+ dispc_read_plane_fifo_sizes();
+}
+
+int dispc_init(void)
+{
+ u32 rev;
+
+ spin_lock_init(&dispc.irq_lock);
+
+ INIT_WORK(&dispc.error_work, dispc_error_worker);
+
+ dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS);
+ if (!dispc.base) {
+ DSSERR("can't ioremap DISPC\n");
+ return -ENOMEM;
+ }
+
+ enable_clocks(1);
+
+ _omap_dispc_initial_config();
+
+ _omap_dispc_initialize_irq();
+
+ dispc_save_context();
+
+ rev = dispc_read_reg(DISPC_REVISION);
+ printk(KERN_INFO "OMAP DISPC rev %d.%d\n",
+ FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+ enable_clocks(0);
+
+ return 0;
+}
+
+void dispc_exit(void)
+{
+ iounmap(dispc.base);
+}
+
+int dispc_enable_plane(enum omap_plane plane, bool enable)
+{
+ DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
+
+ enable_clocks(1);
+ _dispc_enable_plane(plane, enable);
+ enable_clocks(0);
+
+ return 0;
+}
+
+int dispc_setup_plane(enum omap_plane plane,
+ u32 paddr, u16 screen_width,
+ u16 pos_x, u16 pos_y,
+ u16 width, u16 height,
+ u16 out_width, u16 out_height,
+ enum omap_color_mode color_mode,
+ bool ilace,
+ enum omap_dss_rotation_type rotation_type,
+ u8 rotation, bool mirror, u8 global_alpha)
+{
+ int r = 0;
+
+ DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> "
+ "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n",
+ plane, paddr, screen_width, pos_x, pos_y,
+ width, height,
+ out_width, out_height,
+ ilace, color_mode,
+ rotation, mirror);
+
+ enable_clocks(1);
+
+ r = _dispc_setup_plane(plane,
+ paddr, screen_width,
+ pos_x, pos_y,
+ width, height,
+ out_width, out_height,
+ color_mode, ilace,
+ rotation_type,
+ rotation, mirror,
+ global_alpha);
+
+ enable_clocks(0);
+
+ return r;
+}
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
new file mode 100644
index 00000000000..3b92b84b956
--- /dev/null
+++ b/drivers/video/omap2/dss/display.c
@@ -0,0 +1,671 @@
+/*
+ * linux/drivers/video/omap2/dss/display.c
+ *
+ * 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/list.h>
+#include <linux/platform_device.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+static LIST_HEAD(display_list);
+
+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);
+ bool enabled, r;
+
+ enabled = simple_strtoul(buf, NULL, 10);
+
+ if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
+ if (enabled) {
+ r = dssdev->enable(dssdev);
+ if (r)
+ return r;
+ } else {
+ dssdev->disable(dssdev);
+ }
+ }
+
+ return size;
+}
+
+static ssize_t display_upd_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO;
+ if (dssdev->get_update_mode)
+ mode = dssdev->get_update_mode(dssdev);
+ return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+}
+
+static ssize_t display_upd_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ int val, r;
+ enum omap_dss_update_mode mode;
+
+ val = simple_strtoul(buf, NULL, 10);
+
+ switch (val) {
+ case OMAP_DSS_UPDATE_DISABLED:
+ case OMAP_DSS_UPDATE_AUTO:
+ case OMAP_DSS_UPDATE_MANUAL:
+ mode = (enum omap_dss_update_mode)val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ r = dssdev->set_update_mode(dssdev, mode);
+ if (r)
+ return r;
+
+ 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->get_te ? dssdev->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);
+ unsigned long te;
+ int r;
+
+ if (!dssdev->enable_te || !dssdev->get_te)
+ return -ENOENT;
+
+ te = simple_strtoul(buf, NULL, 0);
+
+ r = dssdev->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->get_timings)
+ return -ENOENT;
+
+ dssdev->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;
+ int r, found;
+
+ if (!dssdev->set_timings || !dssdev->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->check_timings(dssdev, &t);
+ if (r)
+ return r;
+
+ dssdev->set_timings(dssdev, &t);
+
+ 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->get_rotate)
+ return -ENOENT;
+ rotate = dssdev->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);
+ unsigned long rot;
+ int r;
+
+ if (!dssdev->set_rotate || !dssdev->get_rotate)
+ return -ENOENT;
+
+ rot = simple_strtoul(buf, NULL, 0);
+
+ r = dssdev->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->get_mirror)
+ return -ENOENT;
+ mirror = dssdev->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);
+ unsigned long mirror;
+ int r;
+
+ if (!dssdev->set_mirror || !dssdev->get_mirror)
+ return -ENOENT;
+
+ mirror = simple_strtoul(buf, NULL, 0);
+
+ r = dssdev->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->get_wss)
+ return -ENOENT;
+
+ wss = dssdev->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);
+ unsigned long wss;
+ int r;
+
+ if (!dssdev->get_wss || !dssdev->set_wss)
+ return -ENOENT;
+
+ if (strict_strtoul(buf, 0, &wss))
+ return -EINVAL;
+
+ if (wss > 0xfffff)
+ return -EINVAL;
+
+ r = dssdev->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(update_mode, S_IRUGO|S_IWUSR,
+ display_upd_mode_show, display_upd_mode_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_update_mode,
+ &dev_attr_tear_elim,
+ &dev_attr_timings,
+ &dev_attr_rotate,
+ &dev_attr_mirror,
+ &dev_attr_wss,
+ NULL
+};
+
+static void default_get_resolution(struct omap_dss_device *dssdev,
+ u16 *xres, u16 *yres)
+{
+ *xres = dssdev->panel.timings.x_res;
+ *yres = dssdev->panel.timings.y_res;
+}
+
+void default_get_overlay_fifo_thresholds(enum omap_plane plane,
+ u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 *fifo_low, u32 *fifo_high)
+{
+ unsigned burst_size_bytes;
+
+ *burst_size = OMAP_DSS_BURST_16x32;
+ burst_size_bytes = 16 * 32 / 8;
+
+ *fifo_high = fifo_size - 1;
+ *fifo_low = fifo_size - burst_size_bytes;
+}
+
+static int default_wait_vsync(struct omap_dss_device *dssdev)
+{
+ unsigned long timeout = msecs_to_jiffies(500);
+ u32 irq;
+
+ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC)
+ irq = DISPC_IRQ_EVSYNC_ODD;
+ else
+ irq = DISPC_IRQ_VSYNC;
+
+ return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+}
+
+static int default_get_recommended_bpp(struct omap_dss_device *dssdev)
+{
+ if (dssdev->panel.recommended_bpp)
+ return dssdev->panel.recommended_bpp;
+
+ switch (dssdev->type) {
+ case OMAP_DISPLAY_TYPE_DPI:
+ if (dssdev->phy.dpi.data_lines == 24)
+ return 24;
+ else
+ return 16;
+
+ case OMAP_DISPLAY_TYPE_DBI:
+ case OMAP_DISPLAY_TYPE_DSI:
+ if (dssdev->ctrl.pixel_size == 24)
+ return 24;
+ else
+ return 16;
+ case OMAP_DISPLAY_TYPE_VENC:
+ case OMAP_DISPLAY_TYPE_SDI:
+ return 24;
+ return 24;
+ default:
+ BUG();
+ }
+}
+
+/* Checks if replication logic should be used. Only use for active matrix,
+ * when overlay is in RGB12U or RGB16 mode, and LCD interface is
+ * 18bpp or 24bpp */
+bool dss_use_replication(struct omap_dss_device *dssdev,
+ enum omap_color_mode mode)
+{
+ int bpp;
+
+ if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
+ return false;
+
+ if (dssdev->type == OMAP_DISPLAY_TYPE_DPI &&
+ (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0)
+ return false;
+
+ switch (dssdev->type) {
+ case OMAP_DISPLAY_TYPE_DPI:
+ bpp = dssdev->phy.dpi.data_lines;
+ break;
+ case OMAP_DISPLAY_TYPE_VENC:
+ case OMAP_DISPLAY_TYPE_SDI:
+ bpp = 24;
+ break;
+ case OMAP_DISPLAY_TYPE_DBI:
+ case OMAP_DISPLAY_TYPE_DSI:
+ bpp = dssdev->ctrl.pixel_size;
+ break;
+ default:
+ BUG();
+ }
+
+ return bpp > 16;
+}
+
+void dss_init_device(struct platform_device *pdev,
+ struct omap_dss_device *dssdev)
+{
+ struct device_attribute *attr;
+ int i;
+ int r;
+
+ switch (dssdev->type) {
+ case OMAP_DISPLAY_TYPE_DPI:
+#ifdef CONFIG_OMAP2_DSS_RFBI
+ case OMAP_DISPLAY_TYPE_DBI:
+#endif
+#ifdef CONFIG_OMAP2_DSS_SDI
+ case OMAP_DISPLAY_TYPE_SDI:
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+ case OMAP_DISPLAY_TYPE_DSI:
+#endif
+#ifdef CONFIG_OMAP2_DSS_VENC
+ case OMAP_DISPLAY_TYPE_VENC:
+#endif
+ break;
+ default:
+ DSSERR("Support for display '%s' not compiled in.\n",
+ dssdev->name);
+ return;
+ }
+
+ dssdev->get_resolution = default_get_resolution;
+ dssdev->get_recommended_bpp = default_get_recommended_bpp;
+ dssdev->wait_vsync = default_wait_vsync;
+
+ switch (dssdev->type) {
+ case OMAP_DISPLAY_TYPE_DPI:
+ r = dpi_init_display(dssdev);
+ break;
+#ifdef CONFIG_OMAP2_DSS_RFBI
+ case OMAP_DISPLAY_TYPE_DBI:
+ r = rfbi_init_display(dssdev);
+ break;
+#endif
+#ifdef CONFIG_OMAP2_DSS_VENC
+ case OMAP_DISPLAY_TYPE_VENC:
+ r = venc_init_display(dssdev);
+ break;
+#endif
+#ifdef CONFIG_OMAP2_DSS_SDI
+ case OMAP_DISPLAY_TYPE_SDI:
+ r = sdi_init_display(dssdev);
+ break;
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+ case OMAP_DISPLAY_TYPE_DSI:
+ r = dsi_init_display(dssdev);
+ break;
+#endif
+ default:
+ BUG();
+ }
+
+ if (r) {
+ DSSERR("failed to init display %s\n", dssdev->name);
+ return;
+ }
+
+ /* create device sysfs files */
+ i = 0;
+ while ((attr = display_sysfs_attrs[i++]) != NULL) {
+ r = device_create_file(&dssdev->dev, attr);
+ if (r)
+ DSSERR("failed to create sysfs file\n");
+ }
+
+ /* create display? sysfs links */
+ r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj,
+ dev_name(&dssdev->dev));
+ if (r)
+ DSSERR("failed to create sysfs display link\n");
+}
+
+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);
+
+ if (dssdev->manager)
+ dssdev->manager->unset_device(dssdev->manager);
+}
+
+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) {
+ dssdev->activate_after_resume = false;
+ return 0;
+ }
+
+ if (!dssdev->suspend) {
+ DSSERR("display '%s' doesn't implement suspend\n",
+ dssdev->name);
+ return -ENOSYS;
+ }
+
+ r = dssdev->suspend(dssdev);
+ if (r)
+ return r;
+
+ dssdev->activate_after_resume = true;
+
+ return 0;
+}
+
+int dss_suspend_all_devices(void)
+{
+ int r;
+ struct bus_type *bus = dss_get_bus();
+
+ r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device);
+ if (r) {
+ /* resume all displays that were suspended */
+ dss_resume_all_devices();
+ return r;
+ }
+
+ return 0;
+}
+
+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->resume) {
+ r = dssdev->resume(dssdev);
+ if (r)
+ return r;
+ }
+
+ dssdev->activate_after_resume = false;
+
+ return 0;
+}
+
+int dss_resume_all_devices(void)
+{
+ struct bus_type *bus = dss_get_bus();
+
+ return bus_for_each_dev(bus, NULL, NULL, dss_resume_device);
+}
+
+static int dss_disable_device(struct device *dev, void *data)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ dssdev->disable(dssdev);
+ return 0;
+}
+
+void dss_disable_all_devices(void)
+{
+ struct bus_type *bus = dss_get_bus();
+ bus_for_each_dev(bus, NULL, NULL, dss_disable_device);
+}
+
+
+void omap_dss_get_device(struct omap_dss_device *dssdev)
+{
+ get_device(&dssdev->dev);
+}
+EXPORT_SYMBOL(omap_dss_get_device);
+
+void omap_dss_put_device(struct omap_dss_device *dssdev)
+{
+ put_device(&dssdev->dev);
+}
+EXPORT_SYMBOL(omap_dss_put_device);
+
+/* ref count of the found device is incremented. ref count
+ * of from-device is decremented. */
+struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
+{
+ struct device *dev;
+ struct device *dev_start = NULL;
+ struct omap_dss_device *dssdev = NULL;
+
+ int match(struct device *dev, void *data)
+ {
+ /* skip panels connected to controllers */
+ if (to_dss_device(dev)->panel.ctrl)
+ return 0;
+
+ return 1;
+ }
+
+ if (from)
+ dev_start = &from->dev;
+ dev = bus_find_device(dss_get_bus(), dev_start, NULL, match);
+ if (dev)
+ dssdev = to_dss_device(dev);
+ if (from)
+ put_device(&from->dev);
+
+ return dssdev;
+}
+EXPORT_SYMBOL(omap_dss_get_next_device);
+
+struct omap_dss_device *omap_dss_find_device(void *data,
+ int (*match)(struct omap_dss_device *dssdev, void *data))
+{
+ struct omap_dss_device *dssdev = NULL;
+
+ while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) {
+ if (match(dssdev, data))
+ return dssdev;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(omap_dss_find_device);
+
+int omap_dss_start_device(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ if (!dssdev->driver) {
+ DSSDBG("no driver\n");
+ r = -ENODEV;
+ goto err0;
+ }
+
+ if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) {
+ DSSDBG("no panel driver\n");
+ r = -ENODEV;
+ goto err0;
+ }
+
+ if (!try_module_get(dssdev->dev.driver->owner)) {
+ r = -ENODEV;
+ goto err0;
+ }
+
+ if (dssdev->ctrl.panel) {
+ if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) {
+ r = -ENODEV;
+ goto err1;
+ }
+ }
+
+ return 0;
+err1:
+ module_put(dssdev->dev.driver->owner);
+err0:
+ return r;
+}
+EXPORT_SYMBOL(omap_dss_start_device);
+
+void omap_dss_stop_device(struct omap_dss_device *dssdev)
+{
+ if (dssdev->ctrl.panel)
+ module_put(dssdev->ctrl.panel->dev.driver->owner);
+
+ module_put(dssdev->dev.driver->owner);
+}
+EXPORT_SYMBOL(omap_dss_stop_device);
+
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
new file mode 100644
index 00000000000..2d71031baa2
--- /dev/null
+++ b/drivers/video/omap2/dss/dpi.c
@@ -0,0 +1,399 @@
+/*
+ * linux/drivers/video/omap2/dss/dpi.c
+ *
+ * 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 "DPI"
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+static struct {
+ int update_enabled;
+} dpi;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req,
+ unsigned long *fck, int *lck_div, int *pck_div)
+{
+ struct dsi_clock_info dsi_cinfo;
+ struct dispc_clock_info dispc_cinfo;
+ int r;
+
+ r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo,
+ &dispc_cinfo);
+ if (r)
+ return r;
+
+ r = dsi_pll_set_clock_div(&dsi_cinfo);
+ if (r)
+ return r;
+
+ dss_select_clk_source(0, 1);
+
+ r = dispc_set_clock_div(&dispc_cinfo);
+ if (r)
+ return r;
+
+ *fck = dsi_cinfo.dsi1_pll_fclk;
+ *lck_div = dispc_cinfo.lck_div;
+ *pck_div = dispc_cinfo.pck_div;
+
+ return 0;
+}
+#else
+static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req,
+ unsigned long *fck, int *lck_div, int *pck_div)
+{
+ struct dss_clock_info dss_cinfo;
+ struct dispc_clock_info dispc_cinfo;
+ int r;
+
+ r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
+ if (r)
+ return r;
+
+ r = dss_set_clock_div(&dss_cinfo);
+ if (r)
+ return r;
+
+ r = dispc_set_clock_div(&dispc_cinfo);
+ if (r)
+ return r;
+
+ *fck = dss_cinfo.fck;
+ *lck_div = dispc_cinfo.lck_div;
+ *pck_div = dispc_cinfo.pck_div;
+
+ return 0;
+}
+#endif
+
+static int dpi_set_mode(struct omap_dss_device *dssdev)
+{
+ struct omap_video_timings *t = &dssdev->panel.timings;
+ int lck_div, pck_div;
+ unsigned long fck;
+ unsigned long pck;
+ bool is_tft;
+ int r = 0;
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,
+ dssdev->panel.acb);
+
+ is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+ r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000,
+ &fck, &lck_div, &pck_div);
+#else
+ r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000,
+ &fck, &lck_div, &pck_div);
+#endif
+ if (r)
+ goto err0;
+
+ pck = fck / lck_div / pck_div / 1000;
+
+ if (pck != t->pixel_clock) {
+ DSSWARN("Could not find exact pixel clock. "
+ "Requested %d kHz, got %lu kHz\n",
+ t->pixel_clock, pck);
+
+ t->pixel_clock = pck;
+ }
+
+ dispc_set_lcd_timings(t);
+
+err0:
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ return r;
+}
+
+static int dpi_basic_init(struct omap_dss_device *dssdev)
+{
+ bool is_tft;
+
+ is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+
+ dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);
+ dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT :
+ OMAP_DSS_LCD_DISPLAY_STN);
+ dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines);
+
+ return 0;
+}
+
+static int dpi_display_enable(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ r = omap_dss_start_device(dssdev);
+ if (r) {
+ DSSERR("failed to start device\n");
+ goto err0;
+ }
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+ DSSERR("display already enabled\n");
+ r = -EINVAL;
+ goto err1;
+ }
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ r = dpi_basic_init(dssdev);
+ if (r)
+ goto err2;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+ dss_clk_enable(DSS_CLK_FCK2);
+ r = dsi_pll_init(dssdev, 0, 1);
+ if (r)
+ goto err3;
+#endif
+ r = dpi_set_mode(dssdev);
+ if (r)
+ goto err4;
+
+ mdelay(2);
+
+ dispc_enable_lcd_out(1);
+
+ r = dssdev->driver->enable(dssdev);
+ if (r)
+ goto err5;
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ return 0;
+
+err5:
+ dispc_enable_lcd_out(0);
+err4:
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+ dsi_pll_uninit();
+err3:
+ dss_clk_disable(DSS_CLK_FCK2);
+#endif
+err2:
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+err1:
+ omap_dss_stop_device(dssdev);
+err0:
+ return r;
+}
+
+static int dpi_display_resume(struct omap_dss_device *dssdev);
+
+static void dpi_display_disable(struct omap_dss_device *dssdev)
+{
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
+ return;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ dpi_display_resume(dssdev);
+
+ dssdev->driver->disable(dssdev);
+
+ dispc_enable_lcd_out(0);
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+ dss_select_clk_source(0, 0);
+ dsi_pll_uninit();
+ dss_clk_disable(DSS_CLK_FCK2);
+#endif
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ omap_dss_stop_device(dssdev);
+}
+
+static int dpi_display_suspend(struct omap_dss_device *dssdev)
+{
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EINVAL;
+
+ DSSDBG("dpi_display_suspend\n");
+
+ if (dssdev->driver->suspend)
+ dssdev->driver->suspend(dssdev);
+
+ dispc_enable_lcd_out(0);
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ return 0;
+}
+
+static int dpi_display_resume(struct omap_dss_device *dssdev)
+{
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
+ return -EINVAL;
+
+ DSSDBG("dpi_display_resume\n");
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dispc_enable_lcd_out(1);
+
+ if (dssdev->driver->resume)
+ dssdev->driver->resume(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ return 0;
+}
+
+static void dpi_set_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ DSSDBG("dpi_set_timings\n");
+ dssdev->panel.timings = *timings;
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ dpi_set_mode(dssdev);
+ dispc_go(OMAP_DSS_CHANNEL_LCD);
+ }
+}
+
+static int dpi_check_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ bool is_tft;
+ int r;
+ int lck_div, pck_div;
+ unsigned long fck;
+ unsigned long pck;
+
+ if (!dispc_lcd_timings_ok(timings))
+ return -EINVAL;
+
+ if (timings->pixel_clock == 0)
+ return -EINVAL;
+
+ is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+ {
+ struct dsi_clock_info dsi_cinfo;
+ struct dispc_clock_info dispc_cinfo;
+ r = dsi_pll_calc_clock_div_pck(is_tft,
+ timings->pixel_clock * 1000,
+ &dsi_cinfo, &dispc_cinfo);
+
+ if (r)
+ return r;
+
+ fck = dsi_cinfo.dsi1_pll_fclk;
+ lck_div = dispc_cinfo.lck_div;
+ pck_div = dispc_cinfo.pck_div;
+ }
+#else
+ {
+ struct dss_clock_info dss_cinfo;
+ struct dispc_clock_info dispc_cinfo;
+ r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
+ &dss_cinfo, &dispc_cinfo);
+
+ if (r)
+ return r;
+
+ fck = dss_cinfo.fck;
+ lck_div = dispc_cinfo.lck_div;
+ pck_div = dispc_cinfo.pck_div;
+ }
+#endif
+
+ pck = fck / lck_div / pck_div / 1000;
+
+ timings->pixel_clock = pck;
+
+ return 0;
+}
+
+static void dpi_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ *timings = dssdev->panel.timings;
+}
+
+static int dpi_display_set_update_mode(struct omap_dss_device *dssdev,
+ enum omap_dss_update_mode mode)
+{
+ if (mode == OMAP_DSS_UPDATE_MANUAL)
+ return -EINVAL;
+
+ if (mode == OMAP_DSS_UPDATE_DISABLED) {
+ dispc_enable_lcd_out(0);
+ dpi.update_enabled = 0;
+ } else {
+ dispc_enable_lcd_out(1);
+ dpi.update_enabled = 1;
+ }
+
+ return 0;
+}
+
+static enum omap_dss_update_mode dpi_display_get_update_mode(
+ struct omap_dss_device *dssdev)
+{
+ return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
+ OMAP_DSS_UPDATE_DISABLED;
+}
+
+int dpi_init_display(struct omap_dss_device *dssdev)
+{
+ DSSDBG("init_display\n");
+
+ dssdev->enable = dpi_display_enable;
+ dssdev->disable = dpi_display_disable;
+ dssdev->suspend = dpi_display_suspend;
+ dssdev->resume = dpi_display_resume;
+ dssdev->set_timings = dpi_set_timings;
+ dssdev->check_timings = dpi_check_timings;
+ dssdev->get_timings = dpi_get_timings;
+ dssdev->set_update_mode = dpi_display_set_update_mode;
+ dssdev->get_update_mode = dpi_display_get_update_mode;
+
+ return 0;
+}
+
+int dpi_init(void)
+{
+ return 0;
+}
+
+void dpi_exit(void)
+{
+}
+
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
new file mode 100644
index 00000000000..5936487b5de
--- /dev/null
+++ b/drivers/video/omap2/dss/dsi.c
@@ -0,0 +1,3710 @@
+/*
+ * linux/drivers/video/omap2/dss/dsi.c
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "DSI"
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/seq_file.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+
+#include <plat/display.h>
+#include <plat/clock.h>
+
+#include "dss.h"
+
+/*#define VERBOSE_IRQ*/
+#define DSI_CATCH_MISSING_TE
+
+#define DSI_BASE 0x4804FC00
+
+struct dsi_reg { u16 idx; };
+
+#define DSI_REG(idx) ((const struct dsi_reg) { idx })
+
+#define DSI_SZ_REGS SZ_1K
+/* DSI Protocol Engine */
+
+#define DSI_REVISION DSI_REG(0x0000)
+#define DSI_SYSCONFIG DSI_REG(0x0010)
+#define DSI_SYSSTATUS DSI_REG(0x0014)
+#define DSI_IRQSTATUS DSI_REG(0x0018)
+#define DSI_IRQENABLE DSI_REG(0x001C)
+#define DSI_CTRL DSI_REG(0x0040)
+#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048)
+#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C)
+#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050)
+#define DSI_CLK_CTRL DSI_REG(0x0054)
+#define DSI_TIMING1 DSI_REG(0x0058)
+#define DSI_TIMING2 DSI_REG(0x005C)
+#define DSI_VM_TIMING1 DSI_REG(0x0060)
+#define DSI_VM_TIMING2 DSI_REG(0x0064)
+#define DSI_VM_TIMING3 DSI_REG(0x0068)
+#define DSI_CLK_TIMING DSI_REG(0x006C)
+#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070)
+#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074)
+#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078)
+#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C)
+#define DSI_VM_TIMING4 DSI_REG(0x0080)
+#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084)
+#define DSI_VM_TIMING5 DSI_REG(0x0088)
+#define DSI_VM_TIMING6 DSI_REG(0x008C)
+#define DSI_VM_TIMING7 DSI_REG(0x0090)
+#define DSI_STOPCLK_TIMING DSI_REG(0x0094)
+#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20))
+#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20))
+#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20))
+#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20))
+#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20))
+#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20))
+#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20))
+
+/* DSIPHY_SCP */
+
+#define DSI_DSIPHY_CFG0 DSI_REG(0x200 + 0x0000)
+#define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004)
+#define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008)
+#define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014)
+
+/* DSI_PLL_CTRL_SCP */
+
+#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000)
+#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004)
+#define DSI_PLL_GO DSI_REG(0x300 + 0x0008)
+#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C)
+#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010)
+
+#define REG_GET(idx, start, end) \
+ FLD_GET(dsi_read_reg(idx), start, end)
+
+#define REG_FLD_MOD(idx, val, start, end) \
+ dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end))
+
+/* Global interrupts */
+#define DSI_IRQ_VC0 (1 << 0)
+#define DSI_IRQ_VC1 (1 << 1)
+#define DSI_IRQ_VC2 (1 << 2)
+#define DSI_IRQ_VC3 (1 << 3)
+#define DSI_IRQ_WAKEUP (1 << 4)
+#define DSI_IRQ_RESYNC (1 << 5)
+#define DSI_IRQ_PLL_LOCK (1 << 7)
+#define DSI_IRQ_PLL_UNLOCK (1 << 8)
+#define DSI_IRQ_PLL_RECALL (1 << 9)
+#define DSI_IRQ_COMPLEXIO_ERR (1 << 10)
+#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14)
+#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15)
+#define DSI_IRQ_TE_TRIGGER (1 << 16)
+#define DSI_IRQ_ACK_TRIGGER (1 << 17)
+#define DSI_IRQ_SYNC_LOST (1 << 18)
+#define DSI_IRQ_LDO_POWER_GOOD (1 << 19)
+#define DSI_IRQ_TA_TIMEOUT (1 << 20)
+#define DSI_IRQ_ERROR_MASK \
+ (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
+ DSI_IRQ_TA_TIMEOUT)
+#define DSI_IRQ_CHANNEL_MASK 0xf
+
+/* Virtual channel interrupts */
+#define DSI_VC_IRQ_CS (1 << 0)
+#define DSI_VC_IRQ_ECC_CORR (1 << 1)
+#define DSI_VC_IRQ_PACKET_SENT (1 << 2)
+#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3)
+#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4)
+#define DSI_VC_IRQ_BTA (1 << 5)
+#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6)
+#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7)
+#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8)
+#define DSI_VC_IRQ_ERROR_MASK \
+ (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \
+ DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \
+ DSI_VC_IRQ_FIFO_TX_UDF)
+
+/* ComplexIO interrupts */
+#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0)
+#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1)
+#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2)
+#define DSI_CIO_IRQ_ERRESC1 (1 << 5)
+#define DSI_CIO_IRQ_ERRESC2 (1 << 6)
+#define DSI_CIO_IRQ_ERRESC3 (1 << 7)
+#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10)
+#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11)
+#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12)
+#define DSI_CIO_IRQ_STATEULPS1 (1 << 15)
+#define DSI_CIO_IRQ_STATEULPS2 (1 << 16)
+#define DSI_CIO_IRQ_STATEULPS3 (1 << 17)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
+#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
+#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
+
+#define DSI_DT_DCS_SHORT_WRITE_0 0x05
+#define DSI_DT_DCS_SHORT_WRITE_1 0x15
+#define DSI_DT_DCS_READ 0x06
+#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37
+#define DSI_DT_NULL_PACKET 0x09
+#define DSI_DT_DCS_LONG_WRITE 0x39
+
+#define DSI_DT_RX_ACK_WITH_ERR 0x02
+#define DSI_DT_RX_DCS_LONG_READ 0x1c
+#define DSI_DT_RX_SHORT_READ_1 0x21
+#define DSI_DT_RX_SHORT_READ_2 0x22
+
+#define FINT_MAX 2100000
+#define FINT_MIN 750000
+#define REGN_MAX (1 << 7)
+#define REGM_MAX ((1 << 11) - 1)
+#define REGM3_MAX (1 << 4)
+#define REGM4_MAX (1 << 4)
+#define LP_DIV_MAX ((1 << 13) - 1)
+
+enum fifo_size {
+ DSI_FIFO_SIZE_0 = 0,
+ DSI_FIFO_SIZE_32 = 1,
+ DSI_FIFO_SIZE_64 = 2,
+ DSI_FIFO_SIZE_96 = 3,
+ DSI_FIFO_SIZE_128 = 4,
+};
+
+enum dsi_vc_mode {
+ DSI_VC_MODE_L4 = 0,
+ DSI_VC_MODE_VP,
+};
+
+struct dsi_update_region {
+ bool dirty;
+ u16 x, y, w, h;
+ struct omap_dss_device *device;
+};
+
+static struct
+{
+ void __iomem *base;
+
+ struct dsi_clock_info current_cinfo;
+
+ struct regulator *vdds_dsi_reg;
+
+ struct {
+ enum dsi_vc_mode mode;
+ struct omap_dss_device *dssdev;
+ enum fifo_size fifo_size;
+ int dest_per; /* destination peripheral 0-3 */
+ } vc[4];
+
+ struct mutex lock;
+ struct mutex bus_lock;
+
+ unsigned pll_locked;
+
+ struct completion bta_completion;
+
+ struct task_struct *thread;
+ wait_queue_head_t waitqueue;
+
+ spinlock_t update_lock;
+ bool framedone_received;
+ struct dsi_update_region update_region;
+ struct dsi_update_region active_update_region;
+ struct completion update_completion;
+
+ enum omap_dss_update_mode user_update_mode;
+ enum omap_dss_update_mode update_mode;
+ bool te_enabled;
+ bool use_ext_te;
+
+#ifdef DSI_CATCH_MISSING_TE
+ struct timer_list te_timer;
+#endif
+
+ unsigned long cache_req_pck;
+ unsigned long cache_clk_freq;
+ struct dsi_clock_info cache_cinfo;
+
+ u32 errors;
+ spinlock_t errors_lock;
+#ifdef DEBUG
+ ktime_t perf_setup_time;
+ ktime_t perf_start_time;
+ ktime_t perf_start_time_auto;
+ int perf_measure_frames;
+#endif
+ int debug_read;
+ int debug_write;
+} dsi;
+
+#ifdef DEBUG
+static unsigned int dsi_perf;
+module_param_named(dsi_perf, dsi_perf, bool, 0644);
+#endif
+
+static inline void dsi_write_reg(const struct dsi_reg idx, u32 val)
+{
+ __raw_writel(val, dsi.base + idx.idx);
+}
+
+static inline u32 dsi_read_reg(const struct dsi_reg idx)
+{
+ return __raw_readl(dsi.base + idx.idx);
+}
+
+
+void dsi_save_context(void)
+{
+}
+
+void dsi_restore_context(void)
+{
+}
+
+void dsi_bus_lock(void)
+{
+ mutex_lock(&dsi.bus_lock);
+}
+EXPORT_SYMBOL(dsi_bus_lock);
+
+void dsi_bus_unlock(void)
+{
+ mutex_unlock(&dsi.bus_lock);
+}
+EXPORT_SYMBOL(dsi_bus_unlock);
+
+static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum,
+ int value)
+{
+ int t = 100000;
+
+ while (REG_GET(idx, bitnum, bitnum) != value) {
+ if (--t == 0)
+ return !value;
+ }
+
+ return value;
+}
+
+#ifdef DEBUG
+static void dsi_perf_mark_setup(void)
+{
+ dsi.perf_setup_time = ktime_get();
+}
+
+static void dsi_perf_mark_start(void)
+{
+ dsi.perf_start_time = ktime_get();
+}
+
+static void dsi_perf_mark_start_auto(void)
+{
+ dsi.perf_measure_frames = 0;
+ dsi.perf_start_time_auto = ktime_get();
+}
+
+static void dsi_perf_show(const char *name)
+{
+ ktime_t t, setup_time, trans_time;
+ u32 total_bytes;
+ u32 setup_us, trans_us, total_us;
+
+ if (!dsi_perf)
+ return;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED)
+ return;
+
+ t = ktime_get();
+
+ setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time);
+ setup_us = (u32)ktime_to_us(setup_time);
+ if (setup_us == 0)
+ setup_us = 1;
+
+ trans_time = ktime_sub(t, dsi.perf_start_time);
+ trans_us = (u32)ktime_to_us(trans_time);
+ if (trans_us == 0)
+ trans_us = 1;
+
+ total_us = setup_us + trans_us;
+
+ total_bytes = dsi.active_update_region.w *
+ dsi.active_update_region.h *
+ dsi.active_update_region.device->ctrl.pixel_size / 8;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+ static u32 s_total_trans_us, s_total_setup_us;
+ static u32 s_min_trans_us = 0xffffffff, s_min_setup_us;
+ static u32 s_max_trans_us, s_max_setup_us;
+ const int numframes = 100;
+ ktime_t total_time_auto;
+ u32 total_time_auto_us;
+
+ dsi.perf_measure_frames++;
+
+ if (setup_us < s_min_setup_us)
+ s_min_setup_us = setup_us;
+
+ if (setup_us > s_max_setup_us)
+ s_max_setup_us = setup_us;
+
+ s_total_setup_us += setup_us;
+
+ if (trans_us < s_min_trans_us)
+ s_min_trans_us = trans_us;
+
+ if (trans_us > s_max_trans_us)
+ s_max_trans_us = trans_us;
+
+ s_total_trans_us += trans_us;
+
+ if (dsi.perf_measure_frames < numframes)
+ return;
+
+ total_time_auto = ktime_sub(t, dsi.perf_start_time_auto);
+ total_time_auto_us = (u32)ktime_to_us(total_time_auto);
+
+ printk(KERN_INFO "DSI(%s): %u fps, setup %u/%u/%u, "
+ "trans %u/%u/%u\n",
+ name,
+ 1000 * 1000 * numframes / total_time_auto_us,
+ s_min_setup_us,
+ s_max_setup_us,
+ s_total_setup_us / numframes,
+ s_min_trans_us,
+ s_max_trans_us,
+ s_total_trans_us / numframes);
+
+ s_total_setup_us = 0;
+ s_min_setup_us = 0xffffffff;
+ s_max_setup_us = 0;
+ s_total_trans_us = 0;
+ s_min_trans_us = 0xffffffff;
+ s_max_trans_us = 0;
+ dsi_perf_mark_start_auto();
+ } else {
+ printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
+ "%u bytes, %u kbytes/sec\n",
+ name,
+ setup_us,
+ trans_us,
+ total_us,
+ 1000*1000 / total_us,
+ total_bytes,
+ total_bytes * 1000 / total_us);
+ }
+}
+#else
+#define dsi_perf_mark_setup()
+#define dsi_perf_mark_start()
+#define dsi_perf_mark_start_auto()
+#define dsi_perf_show(x)
+#endif
+
+static void print_irq_status(u32 status)
+{
+#ifndef VERBOSE_IRQ
+ if ((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);
+#undef PIS
+
+ printk("\n");
+}
+
+static void print_irq_status_vc(int channel, u32 status)
+{
+#ifndef VERBOSE_IRQ
+ if ((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);
+#undef PIS
+ printk("\n");
+}
+
+static void print_irq_status_cio(u32 status)
+{
+ 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);
+#undef PIS
+
+ printk("\n");
+}
+
+static int debug_irq;
+
+/* called from dss */
+void dsi_irq_handler(void)
+{
+ u32 irqstatus, vcstatus, ciostatus;
+ int i;
+
+ irqstatus = dsi_read_reg(DSI_IRQSTATUS);
+
+ if (irqstatus & DSI_IRQ_ERROR_MASK) {
+ DSSERR("DSI error, irqstatus %x\n", irqstatus);
+ print_irq_status(irqstatus);
+ spin_lock(&dsi.errors_lock);
+ dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK;
+ spin_unlock(&dsi.errors_lock);
+ } else if (debug_irq) {
+ print_irq_status(irqstatus);
+ }
+
+#ifdef DSI_CATCH_MISSING_TE
+ if (irqstatus & DSI_IRQ_TE_TRIGGER)
+ del_timer(&dsi.te_timer);
+#endif
+
+ for (i = 0; i < 4; ++i) {
+ if ((irqstatus & (1<<i)) == 0)
+ continue;
+
+ vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i));
+
+ if (vcstatus & DSI_VC_IRQ_BTA)
+ complete(&dsi.bta_completion);
+
+ if (vcstatus & DSI_VC_IRQ_ERROR_MASK) {
+ DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
+ i, vcstatus);
+ print_irq_status_vc(i, vcstatus);
+ } else if (debug_irq) {
+ print_irq_status_vc(i, vcstatus);
+ }
+
+ dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus);
+ /* flush posted write */
+ dsi_read_reg(DSI_VC_IRQSTATUS(i));
+ }
+
+ if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
+ ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+
+ dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
+ /* flush posted write */
+ dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+
+ DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
+ print_irq_status_cio(ciostatus);
+ }
+
+ dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
+ /* flush posted write */
+ dsi_read_reg(DSI_IRQSTATUS);
+}
+
+
+static void _dsi_initialize_irq(void)
+{
+ u32 l;
+ int i;
+
+ /* disable all interrupts */
+ dsi_write_reg(DSI_IRQENABLE, 0);
+ for (i = 0; i < 4; ++i)
+ dsi_write_reg(DSI_VC_IRQENABLE(i), 0);
+ dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0);
+
+ /* clear interrupt status */
+ l = dsi_read_reg(DSI_IRQSTATUS);
+ dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK);
+
+ for (i = 0; i < 4; ++i) {
+ l = dsi_read_reg(DSI_VC_IRQSTATUS(i));
+ dsi_write_reg(DSI_VC_IRQSTATUS(i), l);
+ }
+
+ l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+ dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l);
+
+ /* enable error irqs */
+ l = DSI_IRQ_ERROR_MASK;
+#ifdef DSI_CATCH_MISSING_TE
+ l |= DSI_IRQ_TE_TRIGGER;
+#endif
+ dsi_write_reg(DSI_IRQENABLE, l);
+
+ l = DSI_VC_IRQ_ERROR_MASK;
+ for (i = 0; i < 4; ++i)
+ dsi_write_reg(DSI_VC_IRQENABLE(i), l);
+
+ /* XXX zonda responds incorrectly, causing control error:
+ Exit from LP-ESC mode to LP11 uses wrong transition states on the
+ data lines LP0 and LN0. */
+ dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE,
+ -1 & (~DSI_CIO_IRQ_ERRCONTROL2));
+}
+
+static u32 dsi_get_errors(void)
+{
+ unsigned long flags;
+ u32 e;
+ spin_lock_irqsave(&dsi.errors_lock, flags);
+ e = dsi.errors;
+ dsi.errors = 0;
+ spin_unlock_irqrestore(&dsi.errors_lock, flags);
+ return e;
+}
+
+static void dsi_vc_enable_bta_irq(int channel)
+{
+ u32 l;
+
+ dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA);
+
+ l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
+ l |= DSI_VC_IRQ_BTA;
+ dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+}
+
+static void dsi_vc_disable_bta_irq(int channel)
+{
+ u32 l;
+
+ l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
+ l &= ~DSI_VC_IRQ_BTA;
+ dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+}
+
+/* DSI func clock. this could also be DSI2_PLL_FCLK */
+static inline void enable_clocks(bool enable)
+{
+ if (enable)
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ else
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+/* source clock for DSI PLL. this could also be PCLKFREE */
+static inline void dsi_enable_pll_clock(bool enable)
+{
+ if (enable)
+ dss_clk_enable(DSS_CLK_FCK2);
+ else
+ dss_clk_disable(DSS_CLK_FCK2);
+
+ if (enable && dsi.pll_locked) {
+ if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1)
+ DSSERR("cannot lock PLL when enabling clocks\n");
+ }
+}
+
+#ifdef DEBUG
+static void _dsi_print_reset_status(void)
+{
+ u32 l;
+
+ 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(DSI_DSIPHY_CFG5);
+
+ printk(KERN_DEBUG "DSI resets: ");
+
+ l = dsi_read_reg(DSI_PLL_STATUS);
+ printk("PLL (%d) ", FLD_GET(l, 0, 0));
+
+ l = dsi_read_reg(DSI_COMPLEXIO_CFG1);
+ printk("CIO (%d) ", FLD_GET(l, 29, 29));
+
+ l = dsi_read_reg(DSI_DSIPHY_CFG5);
+ printk("PHY (%x, %d, %d, %d)\n",
+ FLD_GET(l, 28, 26),
+ FLD_GET(l, 29, 29),
+ FLD_GET(l, 30, 30),
+ FLD_GET(l, 31, 31));
+}
+#else
+#define _dsi_print_reset_status()
+#endif
+
+static inline int dsi_if_enable(bool enable)
+{
+ DSSDBG("dsi_if_enable(%d)\n", enable);
+
+ enable = enable ? 1 : 0;
+ REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */
+
+ if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) {
+ DSSERR("Failed to set dsi_if_enable to %d\n", enable);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+unsigned long dsi_get_dsi1_pll_rate(void)
+{
+ return dsi.current_cinfo.dsi1_pll_fclk;
+}
+
+static unsigned long dsi_get_dsi2_pll_rate(void)
+{
+ return dsi.current_cinfo.dsi2_pll_fclk;
+}
+
+static unsigned long dsi_get_txbyteclkhs(void)
+{
+ return dsi.current_cinfo.clkin4ddr / 16;
+}
+
+static unsigned long dsi_fclk_rate(void)
+{
+ unsigned long r;
+
+ if (dss_get_dsi_clk_source() == 0) {
+ /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */
+ r = dss_clk_get_rate(DSS_CLK_FCK1);
+ } else {
+ /* DSI FCLK source is DSI2_PLL_FCLK */
+ r = dsi_get_dsi2_pll_rate();
+ }
+
+ return r;
+}
+
+static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev)
+{
+ unsigned long dsi_fclk;
+ unsigned lp_clk_div;
+ unsigned long lp_clk;
+
+ lp_clk_div = dssdev->phy.dsi.div.lp_clk_div;
+
+ if (lp_clk_div == 0 || lp_clk_div > LP_DIV_MAX)
+ return -EINVAL;
+
+ dsi_fclk = dsi_fclk_rate();
+
+ lp_clk = dsi_fclk / 2 / lp_clk_div;
+
+ DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
+ dsi.current_cinfo.lp_clk = lp_clk;
+ dsi.current_cinfo.lp_clk_div = lp_clk_div;
+
+ REG_FLD_MOD(DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_CLK_DIVISOR */
+
+ REG_FLD_MOD(DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0,
+ 21, 21); /* LP_RX_SYNCHRO_ENABLE */
+
+ return 0;
+}
+
+
+enum dsi_pll_power_state {
+ DSI_PLL_POWER_OFF = 0x0,
+ DSI_PLL_POWER_ON_HSCLK = 0x1,
+ DSI_PLL_POWER_ON_ALL = 0x2,
+ DSI_PLL_POWER_ON_DIV = 0x3,
+};
+
+static int dsi_pll_power(enum dsi_pll_power_state state)
+{
+ int t = 0;
+
+ REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */
+
+ /* PLL_PWR_STATUS */
+ while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) {
+ udelay(1);
+ if (t++ > 1000) {
+ DSSERR("Failed to set DSI PLL power mode to %d\n",
+ state);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+/* calculate clock rates using dividers in cinfo */
+static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo)
+{
+ if (cinfo->regn == 0 || cinfo->regn > REGN_MAX)
+ return -EINVAL;
+
+ if (cinfo->regm == 0 || cinfo->regm > REGM_MAX)
+ return -EINVAL;
+
+ if (cinfo->regm3 > REGM3_MAX)
+ return -EINVAL;
+
+ if (cinfo->regm4 > REGM4_MAX)
+ return -EINVAL;
+
+ if (cinfo->use_dss2_fck) {
+ cinfo->clkin = dss_clk_get_rate(DSS_CLK_FCK2);
+ /* XXX it is unclear if highfreq should be used
+ * with DSS2_FCK source also */
+ cinfo->highfreq = 0;
+ } else {
+ cinfo->clkin = dispc_pclk_rate();
+
+ if (cinfo->clkin < 32000000)
+ cinfo->highfreq = 0;
+ else
+ cinfo->highfreq = 1;
+ }
+
+ cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1));
+
+ if (cinfo->fint > FINT_MAX || cinfo->fint < FINT_MIN)
+ return -EINVAL;
+
+ cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint;
+
+ if (cinfo->clkin4ddr > 1800 * 1000 * 1000)
+ return -EINVAL;
+
+ if (cinfo->regm3 > 0)
+ cinfo->dsi1_pll_fclk = cinfo->clkin4ddr / cinfo->regm3;
+ else
+ cinfo->dsi1_pll_fclk = 0;
+
+ if (cinfo->regm4 > 0)
+ cinfo->dsi2_pll_fclk = cinfo->clkin4ddr / cinfo->regm4;
+ else
+ cinfo->dsi2_pll_fclk = 0;
+
+ return 0;
+}
+
+int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck,
+ struct dsi_clock_info *dsi_cinfo,
+ struct dispc_clock_info *dispc_cinfo)
+{
+ struct dsi_clock_info cur, best;
+ struct dispc_clock_info best_dispc;
+ int min_fck_per_pck;
+ int match = 0;
+ unsigned long dss_clk_fck2;
+
+ dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2);
+
+ if (req_pck == dsi.cache_req_pck &&
+ dsi.cache_cinfo.clkin == dss_clk_fck2) {
+ DSSDBG("DSI clock info found from cache\n");
+ *dsi_cinfo = dsi.cache_cinfo;
+ dispc_find_clk_divs(is_tft, req_pck, dsi_cinfo->dsi1_pll_fclk,
+ dispc_cinfo);
+ return 0;
+ }
+
+ min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
+
+ if (min_fck_per_pck &&
+ req_pck * min_fck_per_pck > DISPC_MAX_FCK) {
+ DSSERR("Requested pixel clock not possible with the current "
+ "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
+ "the constraint off.\n");
+ min_fck_per_pck = 0;
+ }
+
+ DSSDBG("dsi_pll_calc\n");
+
+retry:
+ memset(&best, 0, sizeof(best));
+ memset(&best_dispc, 0, sizeof(best_dispc));
+
+ memset(&cur, 0, sizeof(cur));
+ cur.clkin = dss_clk_fck2;
+ cur.use_dss2_fck = 1;
+ cur.highfreq = 0;
+
+ /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */
+ /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */
+ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */
+ for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) {
+ if (cur.highfreq == 0)
+ cur.fint = cur.clkin / cur.regn;
+ else
+ cur.fint = cur.clkin / (2 * cur.regn);
+
+ if (cur.fint > FINT_MAX || cur.fint < FINT_MIN)
+ continue;
+
+ /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */
+ for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) {
+ unsigned long a, b;
+
+ a = 2 * cur.regm * (cur.clkin/1000);
+ b = cur.regn * (cur.highfreq + 1);
+ cur.clkin4ddr = a / b * 1000;
+
+ if (cur.clkin4ddr > 1800 * 1000 * 1000)
+ break;
+
+ /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */
+ for (cur.regm3 = 1; cur.regm3 < REGM3_MAX;
+ ++cur.regm3) {
+ struct dispc_clock_info cur_dispc;
+ cur.dsi1_pll_fclk = cur.clkin4ddr / cur.regm3;
+
+ /* this will narrow down the search a bit,
+ * but still give pixclocks below what was
+ * requested */
+ if (cur.dsi1_pll_fclk < req_pck)
+ break;
+
+ if (cur.dsi1_pll_fclk > DISPC_MAX_FCK)
+ continue;
+
+ if (min_fck_per_pck &&
+ cur.dsi1_pll_fclk <
+ req_pck * min_fck_per_pck)
+ continue;
+
+ match = 1;
+
+ dispc_find_clk_divs(is_tft, req_pck,
+ cur.dsi1_pll_fclk,
+ &cur_dispc);
+
+ if (abs(cur_dispc.pck - req_pck) <
+ abs(best_dispc.pck - req_pck)) {
+ best = cur;
+ best_dispc = cur_dispc;
+
+ if (cur_dispc.pck == req_pck)
+ goto found;
+ }
+ }
+ }
+ }
+found:
+ if (!match) {
+ if (min_fck_per_pck) {
+ DSSERR("Could not find suitable clock settings.\n"
+ "Turning FCK/PCK constraint off and"
+ "trying again.\n");
+ min_fck_per_pck = 0;
+ goto retry;
+ }
+
+ DSSERR("Could not find suitable clock settings.\n");
+
+ return -EINVAL;
+ }
+
+ /* DSI2_PLL_FCLK (regm4) is not used */
+ best.regm4 = 0;
+ best.dsi2_pll_fclk = 0;
+
+ if (dsi_cinfo)
+ *dsi_cinfo = best;
+ if (dispc_cinfo)
+ *dispc_cinfo = best_dispc;
+
+ dsi.cache_req_pck = req_pck;
+ dsi.cache_clk_freq = 0;
+ dsi.cache_cinfo = best;
+
+ return 0;
+}
+
+int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo)
+{
+ int r = 0;
+ u32 l;
+ int f;
+
+ DSSDBGF();
+
+ dsi.current_cinfo.fint = cinfo->fint;
+ dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr;
+ dsi.current_cinfo.dsi1_pll_fclk = cinfo->dsi1_pll_fclk;
+ dsi.current_cinfo.dsi2_pll_fclk = cinfo->dsi2_pll_fclk;
+
+ dsi.current_cinfo.regn = cinfo->regn;
+ dsi.current_cinfo.regm = cinfo->regm;
+ dsi.current_cinfo.regm3 = cinfo->regm3;
+ dsi.current_cinfo.regm4 = cinfo->regm4;
+
+ DSSDBG("DSI Fint %ld\n", cinfo->fint);
+
+ DSSDBG("clkin (%s) rate %ld, highfreq %d\n",
+ cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree",
+ cinfo->clkin,
+ cinfo->highfreq);
+
+ /* DSIPHY == CLKIN4DDR */
+ DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu / %d = %lu\n",
+ cinfo->regm,
+ cinfo->regn,
+ cinfo->clkin,
+ cinfo->highfreq + 1,
+ cinfo->clkin4ddr);
+
+ DSSDBG("Data rate on 1 DSI lane %ld Mbps\n",
+ cinfo->clkin4ddr / 1000 / 1000 / 2);
+
+ DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4);
+
+ DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n",
+ cinfo->regm3, cinfo->dsi1_pll_fclk);
+ DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n",
+ cinfo->regm4, cinfo->dsi2_pll_fclk);
+
+ REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */
+
+ l = dsi_read_reg(DSI_PLL_CONFIGURATION1);
+ l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */
+ l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */
+ l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */
+ l = FLD_MOD(l, cinfo->regm3 > 0 ? cinfo->regm3 - 1 : 0,
+ 22, 19); /* DSI_CLOCK_DIV */
+ l = FLD_MOD(l, cinfo->regm4 > 0 ? cinfo->regm4 - 1 : 0,
+ 26, 23); /* DSIPROTO_CLOCK_DIV */
+ dsi_write_reg(DSI_PLL_CONFIGURATION1, l);
+
+ BUG_ON(cinfo->fint < 750000 || cinfo->fint > 2100000);
+ if (cinfo->fint < 1000000)
+ f = 0x3;
+ else if (cinfo->fint < 1250000)
+ f = 0x4;
+ else if (cinfo->fint < 1500000)
+ f = 0x5;
+ else if (cinfo->fint < 1750000)
+ f = 0x6;
+ else
+ f = 0x7;
+
+ l = dsi_read_reg(DSI_PLL_CONFIGURATION2);
+ l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */
+ l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1,
+ 11, 11); /* DSI_PLL_CLKSEL */
+ l = FLD_MOD(l, cinfo->highfreq,
+ 12, 12); /* DSI_PLL_HIGHFREQ */
+ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
+ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */
+ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */
+ dsi_write_reg(DSI_PLL_CONFIGURATION2, l);
+
+ REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */
+
+ if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) {
+ DSSERR("dsi pll go bit not going down.\n");
+ r = -EIO;
+ goto err;
+ }
+
+ if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) {
+ DSSERR("cannot lock PLL\n");
+ r = -EIO;
+ goto err;
+ }
+
+ dsi.pll_locked = 1;
+
+ l = dsi_read_reg(DSI_PLL_CONFIGURATION2);
+ l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */
+ l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */
+ l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */
+ l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */
+ l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */
+ l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */
+ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
+ l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */
+ l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */
+ l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */
+ l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */
+ l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */
+ l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */
+ l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */
+ dsi_write_reg(DSI_PLL_CONFIGURATION2, l);
+
+ DSSDBG("PLL config done\n");
+err:
+ return r;
+}
+
+int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk,
+ bool enable_hsdiv)
+{
+ int r = 0;
+ enum dsi_pll_power_state pwstate;
+
+ DSSDBG("PLL init\n");
+
+ enable_clocks(1);
+ dsi_enable_pll_clock(1);
+
+ r = regulator_enable(dsi.vdds_dsi_reg);
+ if (r)
+ goto err0;
+
+ /* XXX PLL does not come out of reset without this... */
+ dispc_pck_free_enable(1);
+
+ if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) {
+ DSSERR("PLL not coming out of reset.\n");
+ r = -ENODEV;
+ goto err1;
+ }
+
+ /* XXX ... but if left on, we get problems when planes do not
+ * fill the whole display. No idea about this */
+ dispc_pck_free_enable(0);
+
+ if (enable_hsclk && enable_hsdiv)
+ pwstate = DSI_PLL_POWER_ON_ALL;
+ else if (enable_hsclk)
+ pwstate = DSI_PLL_POWER_ON_HSCLK;
+ else if (enable_hsdiv)
+ pwstate = DSI_PLL_POWER_ON_DIV;
+ else
+ pwstate = DSI_PLL_POWER_OFF;
+
+ r = dsi_pll_power(pwstate);
+
+ if (r)
+ goto err1;
+
+ DSSDBG("PLL init done\n");
+
+ return 0;
+err1:
+ regulator_disable(dsi.vdds_dsi_reg);
+err0:
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+ return r;
+}
+
+void dsi_pll_uninit(void)
+{
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+
+ dsi.pll_locked = 0;
+ dsi_pll_power(DSI_PLL_POWER_OFF);
+ regulator_disable(dsi.vdds_dsi_reg);
+ DSSDBG("PLL uninit done\n");
+}
+
+void dsi_dump_clocks(struct seq_file *s)
+{
+ int clksel;
+ struct dsi_clock_info *cinfo = &dsi.current_cinfo;
+
+ enable_clocks(1);
+
+ clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11);
+
+ seq_printf(s, "- DSI PLL -\n");
+
+ seq_printf(s, "dsi pll source = %s\n",
+ clksel == 0 ?
+ "dss2_alwon_fclk" : "pclkfree");
+
+ seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn);
+
+ seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n",
+ cinfo->clkin4ddr, cinfo->regm);
+
+ seq_printf(s, "dsi1_pll_fck\t%-16luregm3 %u\t(%s)\n",
+ cinfo->dsi1_pll_fclk,
+ cinfo->regm3,
+ dss_get_dispc_clk_source() == 0 ? "off" : "on");
+
+ seq_printf(s, "dsi2_pll_fck\t%-16luregm4 %u\t(%s)\n",
+ cinfo->dsi2_pll_fclk,
+ cinfo->regm4,
+ dss_get_dsi_clk_source() == 0 ? "off" : "on");
+
+ seq_printf(s, "- DSI -\n");
+
+ seq_printf(s, "dsi fclk source = %s\n",
+ dss_get_dsi_clk_source() == 0 ?
+ "dss1_alwon_fclk" : "dsi2_pll_fclk");
+
+ seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate());
+
+ seq_printf(s, "DDR_CLK\t\t%lu\n",
+ cinfo->clkin4ddr / 4);
+
+ seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs());
+
+ seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk);
+
+ seq_printf(s, "VP_CLK\t\t%lu\n"
+ "VP_PCLK\t\t%lu\n",
+ dispc_lclk_rate(),
+ dispc_pclk_rate());
+
+ enable_clocks(0);
+}
+
+void dsi_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r))
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ DUMPREG(DSI_REVISION);
+ DUMPREG(DSI_SYSCONFIG);
+ DUMPREG(DSI_SYSSTATUS);
+ DUMPREG(DSI_IRQSTATUS);
+ DUMPREG(DSI_IRQENABLE);
+ DUMPREG(DSI_CTRL);
+ DUMPREG(DSI_COMPLEXIO_CFG1);
+ DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
+ DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
+ DUMPREG(DSI_CLK_CTRL);
+ DUMPREG(DSI_TIMING1);
+ DUMPREG(DSI_TIMING2);
+ DUMPREG(DSI_VM_TIMING1);
+ DUMPREG(DSI_VM_TIMING2);
+ DUMPREG(DSI_VM_TIMING3);
+ DUMPREG(DSI_CLK_TIMING);
+ DUMPREG(DSI_TX_FIFO_VC_SIZE);
+ DUMPREG(DSI_RX_FIFO_VC_SIZE);
+ DUMPREG(DSI_COMPLEXIO_CFG2);
+ DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
+ DUMPREG(DSI_VM_TIMING4);
+ DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
+ DUMPREG(DSI_VM_TIMING5);
+ DUMPREG(DSI_VM_TIMING6);
+ DUMPREG(DSI_VM_TIMING7);
+ DUMPREG(DSI_STOPCLK_TIMING);
+
+ DUMPREG(DSI_VC_CTRL(0));
+ DUMPREG(DSI_VC_TE(0));
+ DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
+ DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
+ DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
+ DUMPREG(DSI_VC_IRQSTATUS(0));
+ DUMPREG(DSI_VC_IRQENABLE(0));
+
+ DUMPREG(DSI_VC_CTRL(1));
+ DUMPREG(DSI_VC_TE(1));
+ DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
+ DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
+ DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
+ DUMPREG(DSI_VC_IRQSTATUS(1));
+ DUMPREG(DSI_VC_IRQENABLE(1));
+
+ DUMPREG(DSI_VC_CTRL(2));
+ DUMPREG(DSI_VC_TE(2));
+ DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
+ DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
+ DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
+ DUMPREG(DSI_VC_IRQSTATUS(2));
+ DUMPREG(DSI_VC_IRQENABLE(2));
+
+ DUMPREG(DSI_VC_CTRL(3));
+ DUMPREG(DSI_VC_TE(3));
+ DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
+ DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
+ DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
+ DUMPREG(DSI_VC_IRQSTATUS(3));
+ DUMPREG(DSI_VC_IRQENABLE(3));
+
+ DUMPREG(DSI_DSIPHY_CFG0);
+ DUMPREG(DSI_DSIPHY_CFG1);
+ DUMPREG(DSI_DSIPHY_CFG2);
+ DUMPREG(DSI_DSIPHY_CFG5);
+
+ DUMPREG(DSI_PLL_CONTROL);
+ DUMPREG(DSI_PLL_STATUS);
+ DUMPREG(DSI_PLL_GO);
+ DUMPREG(DSI_PLL_CONFIGURATION1);
+ DUMPREG(DSI_PLL_CONFIGURATION2);
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+enum dsi_complexio_power_state {
+ DSI_COMPLEXIO_POWER_OFF = 0x0,
+ DSI_COMPLEXIO_POWER_ON = 0x1,
+ DSI_COMPLEXIO_POWER_ULPS = 0x2,
+};
+
+static int dsi_complexio_power(enum dsi_complexio_power_state state)
+{
+ int t = 0;
+
+ /* PWR_CMD */
+ REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27);
+
+ /* PWR_STATUS */
+ while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) {
+ udelay(1);
+ if (t++ > 1000) {
+ DSSERR("failed to set complexio power state to "
+ "%d\n", state);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static void dsi_complexio_config(struct omap_dss_device *dssdev)
+{
+ u32 r;
+
+ int clk_lane = dssdev->phy.dsi.clk_lane;
+ int data1_lane = dssdev->phy.dsi.data1_lane;
+ int data2_lane = dssdev->phy.dsi.data2_lane;
+ int clk_pol = dssdev->phy.dsi.clk_pol;
+ int data1_pol = dssdev->phy.dsi.data1_pol;
+ int data2_pol = dssdev->phy.dsi.data2_pol;
+
+ r = dsi_read_reg(DSI_COMPLEXIO_CFG1);
+ r = FLD_MOD(r, clk_lane, 2, 0);
+ r = FLD_MOD(r, clk_pol, 3, 3);
+ r = FLD_MOD(r, data1_lane, 6, 4);
+ r = FLD_MOD(r, data1_pol, 7, 7);
+ r = FLD_MOD(r, data2_lane, 10, 8);
+ r = FLD_MOD(r, data2_pol, 11, 11);
+ dsi_write_reg(DSI_COMPLEXIO_CFG1, r);
+
+ /* The configuration of the DSI complex I/O (number of data lanes,
+ position, differential order) should not be changed while
+ DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for
+ the hardware to take into account a new configuration of the complex
+ I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to
+ follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1,
+ then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set
+ DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the
+ DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the
+ DSI complex I/O configuration is unknown. */
+
+ /*
+ REG_FLD_MOD(DSI_CTRL, 1, 0, 0);
+ REG_FLD_MOD(DSI_CTRL, 0, 0, 0);
+ REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20);
+ REG_FLD_MOD(DSI_CTRL, 1, 0, 0);
+ */
+}
+
+static inline unsigned ns2ddr(unsigned ns)
+{
+ /* convert time in ns to ddr ticks, rounding up */
+ unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4;
+ return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
+}
+
+static inline unsigned ddr2ns(unsigned ddr)
+{
+ unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4;
+ return ddr * 1000 * 1000 / (ddr_clk / 1000);
+}
+
+static void dsi_complexio_timings(void)
+{
+ u32 r;
+ u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
+ u32 tlpx_half, tclk_trail, tclk_zero;
+ u32 tclk_prepare;
+
+ /* calculate timings */
+
+ /* 1 * DDR_CLK = 2 * UI */
+
+ /* min 40ns + 4*UI max 85ns + 6*UI */
+ ths_prepare = ns2ddr(70) + 2;
+
+ /* min 145ns + 10*UI */
+ ths_prepare_ths_zero = ns2ddr(175) + 2;
+
+ /* min max(8*UI, 60ns+4*UI) */
+ ths_trail = ns2ddr(60) + 5;
+
+ /* min 100ns */
+ ths_exit = ns2ddr(145);
+
+ /* tlpx min 50n */
+ tlpx_half = ns2ddr(25);
+
+ /* min 60ns */
+ tclk_trail = ns2ddr(60) + 2;
+
+ /* min 38ns, max 95ns */
+ tclk_prepare = ns2ddr(65);
+
+ /* min tclk-prepare + tclk-zero = 300ns */
+ tclk_zero = ns2ddr(260);
+
+ DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
+ ths_prepare, ddr2ns(ths_prepare),
+ ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero));
+ DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
+ ths_trail, ddr2ns(ths_trail),
+ ths_exit, ddr2ns(ths_exit));
+
+ DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
+ "tclk_zero %u (%uns)\n",
+ tlpx_half, ddr2ns(tlpx_half),
+ tclk_trail, ddr2ns(tclk_trail),
+ tclk_zero, ddr2ns(tclk_zero));
+ DSSDBG("tclk_prepare %u (%uns)\n",
+ tclk_prepare, ddr2ns(tclk_prepare));
+
+ /* program timings */
+
+ r = dsi_read_reg(DSI_DSIPHY_CFG0);
+ r = FLD_MOD(r, ths_prepare, 31, 24);
+ r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
+ r = FLD_MOD(r, ths_trail, 15, 8);
+ r = FLD_MOD(r, ths_exit, 7, 0);
+ dsi_write_reg(DSI_DSIPHY_CFG0, r);
+
+ r = dsi_read_reg(DSI_DSIPHY_CFG1);
+ r = FLD_MOD(r, tlpx_half, 22, 16);
+ r = FLD_MOD(r, tclk_trail, 15, 8);
+ r = FLD_MOD(r, tclk_zero, 7, 0);
+ dsi_write_reg(DSI_DSIPHY_CFG1, r);
+
+ r = dsi_read_reg(DSI_DSIPHY_CFG2);
+ r = FLD_MOD(r, tclk_prepare, 7, 0);
+ dsi_write_reg(DSI_DSIPHY_CFG2, r);
+}
+
+
+static int dsi_complexio_init(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ DSSDBG("dsi_complexio_init\n");
+
+ /* CIO_CLK_ICG, enable L3 clk to CIO */
+ REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14);
+
+ /* 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. */
+ dsi_read_reg(DSI_DSIPHY_CFG5);
+
+ if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) {
+ DSSERR("ComplexIO PHY not coming out of reset.\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ dsi_complexio_config(dssdev);
+
+ r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON);
+
+ if (r)
+ goto err;
+
+ if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) {
+ DSSERR("ComplexIO not coming out of reset.\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) {
+ DSSERR("ComplexIO LDO power down.\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ dsi_complexio_timings();
+
+ /*
+ The configuration of the DSI complex I/O (number of data lanes,
+ position, differential order) should not be changed while
+ DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the
+ hardware to recognize a new configuration of the complex I/O (done
+ in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow
+ this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next
+ reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20]
+ LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN
+ bit to 1. If the sequence is not followed, the DSi complex I/O
+ configuration is undetermined.
+ */
+ dsi_if_enable(1);
+ dsi_if_enable(0);
+ REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
+ dsi_if_enable(1);
+ dsi_if_enable(0);
+
+ DSSDBG("CIO init done\n");
+err:
+ return r;
+}
+
+static void dsi_complexio_uninit(void)
+{
+ dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF);
+}
+
+static int _dsi_wait_reset(void)
+{
+ int i = 0;
+
+ while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) {
+ if (i++ > 5) {
+ DSSERR("soft reset failed\n");
+ return -ENODEV;
+ }
+ udelay(1);
+ }
+
+ return 0;
+}
+
+static int _dsi_reset(void)
+{
+ /* Soft reset */
+ REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1);
+ return _dsi_wait_reset();
+}
+
+static void dsi_reset_tx_fifo(int channel)
+{
+ u32 mask;
+ u32 l;
+
+ /* set fifosize of the channel to 0, then return the old size */
+ l = dsi_read_reg(DSI_TX_FIFO_VC_SIZE);
+
+ mask = FLD_MASK((8 * channel) + 7, (8 * channel) + 4);
+ dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l & ~mask);
+
+ dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l);
+}
+
+static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2,
+ enum fifo_size size3, enum fifo_size size4)
+{
+ u32 r = 0;
+ int add = 0;
+ int i;
+
+ dsi.vc[0].fifo_size = size1;
+ dsi.vc[1].fifo_size = size2;
+ dsi.vc[2].fifo_size = size3;
+ dsi.vc[3].fifo_size = size4;
+
+ for (i = 0; i < 4; i++) {
+ u8 v;
+ int size = dsi.vc[i].fifo_size;
+
+ if (add + size > 4) {
+ DSSERR("Illegal FIFO configuration\n");
+ BUG();
+ }
+
+ v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
+ r |= v << (8 * i);
+ /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
+ add += size;
+ }
+
+ dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r);
+}
+
+static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2,
+ enum fifo_size size3, enum fifo_size size4)
+{
+ u32 r = 0;
+ int add = 0;
+ int i;
+
+ dsi.vc[0].fifo_size = size1;
+ dsi.vc[1].fifo_size = size2;
+ dsi.vc[2].fifo_size = size3;
+ dsi.vc[3].fifo_size = size4;
+
+ for (i = 0; i < 4; i++) {
+ u8 v;
+ int size = dsi.vc[i].fifo_size;
+
+ if (add + size > 4) {
+ DSSERR("Illegal FIFO configuration\n");
+ BUG();
+ }
+
+ v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
+ r |= v << (8 * i);
+ /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
+ add += size;
+ }
+
+ dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r);
+}
+
+static int dsi_force_tx_stop_mode_io(void)
+{
+ u32 r;
+
+ r = dsi_read_reg(DSI_TIMING1);
+ r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
+ dsi_write_reg(DSI_TIMING1, r);
+
+ if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) {
+ DSSERR("TX_STOP bit not going down\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void dsi_vc_print_status(int channel)
+{
+ u32 r;
+
+ r = dsi_read_reg(DSI_VC_CTRL(channel));
+ DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, "
+ "TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ",
+ channel,
+ FLD_GET(r, 5, 5),
+ FLD_GET(r, 6, 6),
+ FLD_GET(r, 15, 15),
+ FLD_GET(r, 16, 16),
+ FLD_GET(r, 20, 20));
+
+ r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS);
+ DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff);
+}
+
+static int dsi_vc_enable(int channel, bool enable)
+{
+ if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
+ DSSDBG("dsi_vc_enable channel %d, enable %d\n",
+ channel, enable);
+
+ enable = enable ? 1 : 0;
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0);
+
+ if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) {
+ DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void dsi_vc_initial_config(int channel)
+{
+ u32 r;
+
+ DSSDBGF("%d", channel);
+
+ r = dsi_read_reg(DSI_VC_CTRL(channel));
+
+ if (FLD_GET(r, 15, 15)) /* VC_BUSY */
+ DSSERR("VC(%d) busy when trying to configure it!\n",
+ channel);
+
+ r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
+ r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */
+ r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
+ r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
+ r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
+ r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
+ r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
+
+ r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
+ r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
+
+ dsi_write_reg(DSI_VC_CTRL(channel), r);
+
+ dsi.vc[channel].mode = DSI_VC_MODE_L4;
+}
+
+static void dsi_vc_config_l4(int channel)
+{
+ if (dsi.vc[channel].mode == DSI_VC_MODE_L4)
+ return;
+
+ DSSDBGF("%d", channel);
+
+ dsi_vc_enable(channel, 0);
+
+ if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */
+ DSSERR("vc(%d) busy when trying to config for L4\n", channel);
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */
+
+ dsi_vc_enable(channel, 1);
+
+ dsi.vc[channel].mode = DSI_VC_MODE_L4;
+}
+
+static void dsi_vc_config_vp(int channel)
+{
+ if (dsi.vc[channel].mode == DSI_VC_MODE_VP)
+ return;
+
+ DSSDBGF("%d", channel);
+
+ dsi_vc_enable(channel, 0);
+
+ if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */
+ DSSERR("vc(%d) busy when trying to config for VP\n", channel);
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */
+
+ dsi_vc_enable(channel, 1);
+
+ dsi.vc[channel].mode = DSI_VC_MODE_VP;
+}
+
+
+static void dsi_vc_enable_hs(int channel, bool enable)
+{
+ DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
+
+ dsi_vc_enable(channel, 0);
+ dsi_if_enable(0);
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9);
+
+ dsi_vc_enable(channel, 1);
+ dsi_if_enable(1);
+
+ dsi_force_tx_stop_mode_io();
+}
+
+static void dsi_vc_flush_long_data(int channel)
+{
+ while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+ u32 val;
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
+ (val >> 0) & 0xff,
+ (val >> 8) & 0xff,
+ (val >> 16) & 0xff,
+ (val >> 24) & 0xff);
+ }
+}
+
+static void dsi_show_rx_ack_with_err(u16 err)
+{
+ DSSERR("\tACK with ERROR (%#x):\n", err);
+ if (err & (1 << 0))
+ DSSERR("\t\tSoT Error\n");
+ if (err & (1 << 1))
+ DSSERR("\t\tSoT Sync Error\n");
+ if (err & (1 << 2))
+ DSSERR("\t\tEoT Sync Error\n");
+ if (err & (1 << 3))
+ DSSERR("\t\tEscape Mode Entry Command Error\n");
+ if (err & (1 << 4))
+ DSSERR("\t\tLP Transmit Sync Error\n");
+ if (err & (1 << 5))
+ DSSERR("\t\tHS Receive Timeout Error\n");
+ if (err & (1 << 6))
+ DSSERR("\t\tFalse Control Error\n");
+ if (err & (1 << 7))
+ DSSERR("\t\t(reserved7)\n");
+ if (err & (1 << 8))
+ DSSERR("\t\tECC Error, single-bit (corrected)\n");
+ if (err & (1 << 9))
+ DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
+ if (err & (1 << 10))
+ DSSERR("\t\tChecksum Error\n");
+ if (err & (1 << 11))
+ DSSERR("\t\tData type not recognized\n");
+ if (err & (1 << 12))
+ DSSERR("\t\tInvalid VC ID\n");
+ if (err & (1 << 13))
+ DSSERR("\t\tInvalid Transmission Length\n");
+ if (err & (1 << 14))
+ DSSERR("\t\t(reserved14)\n");
+ if (err & (1 << 15))
+ DSSERR("\t\tDSI Protocol Violation\n");
+}
+
+static u16 dsi_vc_flush_receive_data(int channel)
+{
+ /* RX_FIFO_NOT_EMPTY */
+ while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+ u32 val;
+ u8 dt;
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ DSSDBG("\trawval %#08x\n", val);
+ dt = FLD_GET(val, 5, 0);
+ if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+ u16 err = FLD_GET(val, 23, 8);
+ dsi_show_rx_ack_with_err(err);
+ } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+ DSSDBG("\tDCS short response, 1 byte: %#x\n",
+ FLD_GET(val, 23, 8));
+ } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+ DSSDBG("\tDCS short response, 2 byte: %#x\n",
+ FLD_GET(val, 23, 8));
+ } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+ DSSDBG("\tDCS long response, len %d\n",
+ FLD_GET(val, 23, 8));
+ dsi_vc_flush_long_data(channel);
+ } else {
+ DSSERR("\tunknown datatype 0x%02x\n", dt);
+ }
+ }
+ return 0;
+}
+
+static int dsi_vc_send_bta(int channel)
+{
+ if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO &&
+ (dsi.debug_write || dsi.debug_read))
+ DSSDBG("dsi_vc_send_bta %d\n", channel);
+
+ WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+ if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */
+ DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
+ dsi_vc_flush_receive_data(channel);
+ }
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
+
+ return 0;
+}
+
+int dsi_vc_send_bta_sync(int channel)
+{
+ int r = 0;
+ u32 err;
+
+ INIT_COMPLETION(dsi.bta_completion);
+
+ dsi_vc_enable_bta_irq(channel);
+
+ r = dsi_vc_send_bta(channel);
+ if (r)
+ goto err;
+
+ if (wait_for_completion_timeout(&dsi.bta_completion,
+ msecs_to_jiffies(500)) == 0) {
+ DSSERR("Failed to receive BTA\n");
+ r = -EIO;
+ goto err;
+ }
+
+ err = dsi_get_errors();
+ if (err) {
+ DSSERR("Error while sending BTA: %x\n", err);
+ r = -EIO;
+ goto err;
+ }
+err:
+ dsi_vc_disable_bta_irq(channel);
+
+ return r;
+}
+EXPORT_SYMBOL(dsi_vc_send_bta_sync);
+
+static inline void dsi_vc_write_long_header(int channel, u8 data_type,
+ u16 len, u8 ecc)
+{
+ u32 val;
+ u8 data_id;
+
+ WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+ /*data_id = data_type | channel << 6; */
+ data_id = data_type | dsi.vc[channel].dest_per << 6;
+
+ val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
+ FLD_VAL(ecc, 31, 24);
+
+ dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val);
+}
+
+static inline void dsi_vc_write_long_payload(int channel,
+ u8 b1, u8 b2, u8 b3, u8 b4)
+{
+ u32 val;
+
+ val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0;
+
+/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
+ b1, b2, b3, b4, val); */
+
+ dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val);
+}
+
+static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len,
+ u8 ecc)
+{
+ /*u32 val; */
+ int i;
+ u8 *p;
+ int r = 0;
+ u8 b1, b2, b3, b4;
+
+ if (dsi.debug_write)
+ DSSDBG("dsi_vc_send_long, %d bytes\n", len);
+
+ /* len + header */
+ if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) {
+ DSSERR("unable to send long packet: packet too long.\n");
+ return -EINVAL;
+ }
+
+ dsi_vc_config_l4(channel);
+
+ dsi_vc_write_long_header(channel, data_type, len, ecc);
+
+ /*dsi_vc_print_status(0); */
+
+ p = data;
+ for (i = 0; i < len >> 2; i++) {
+ if (dsi.debug_write)
+ DSSDBG("\tsending full packet %d\n", i);
+ /*dsi_vc_print_status(0); */
+
+ b1 = *p++;
+ b2 = *p++;
+ b3 = *p++;
+ b4 = *p++;
+
+ dsi_vc_write_long_payload(channel, b1, b2, b3, b4);
+ }
+
+ i = len % 4;
+ if (i) {
+ b1 = 0; b2 = 0; b3 = 0;
+
+ if (dsi.debug_write)
+ DSSDBG("\tsending remainder bytes %d\n", i);
+
+ switch (i) {
+ case 3:
+ b1 = *p++;
+ b2 = *p++;
+ b3 = *p++;
+ break;
+ case 2:
+ b1 = *p++;
+ b2 = *p++;
+ break;
+ case 1:
+ b1 = *p++;
+ break;
+ }
+
+ dsi_vc_write_long_payload(channel, b1, b2, b3, 0);
+ }
+
+ return r;
+}
+
+static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc)
+{
+ u32 r;
+ u8 data_id;
+
+ WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+ if (dsi.debug_write)
+ DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
+ channel,
+ data_type, data & 0xff, (data >> 8) & 0xff);
+
+ dsi_vc_config_l4(channel);
+
+ if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) {
+ DSSERR("ERROR FIFO FULL, aborting transfer\n");
+ return -EINVAL;
+ }
+
+ data_id = data_type | channel << 6;
+
+ r = (data_id << 0) | (data << 8) | (ecc << 24);
+
+ dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r);
+
+ return 0;
+}
+
+int dsi_vc_send_null(int channel)
+{
+ u8 nullpkg[] = {0, 0, 0, 0};
+ return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0);
+}
+EXPORT_SYMBOL(dsi_vc_send_null);
+
+int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len)
+{
+ int r;
+
+ BUG_ON(len == 0);
+
+ if (len == 1) {
+ r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0,
+ data[0], 0);
+ } else if (len == 2) {
+ r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1,
+ data[0] | (data[1] << 8), 0);
+ } else {
+ /* 0x39 = DCS Long Write */
+ r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE,
+ data, len, 0);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(dsi_vc_dcs_write_nosync);
+
+int dsi_vc_dcs_write(int channel, u8 *data, int len)
+{
+ int r;
+
+ r = dsi_vc_dcs_write_nosync(channel, data, len);
+ if (r)
+ return r;
+
+ r = dsi_vc_send_bta_sync(channel);
+
+ return r;
+}
+EXPORT_SYMBOL(dsi_vc_dcs_write);
+
+int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
+{
+ u32 val;
+ u8 dt;
+ int r;
+
+ if (dsi.debug_read)
+ DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %u)\n", channel, dcs_cmd);
+
+ r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0);
+ if (r)
+ return r;
+
+ r = dsi_vc_send_bta_sync(channel);
+ if (r)
+ return r;
+
+ /* RX_FIFO_NOT_EMPTY */
+ if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) {
+ DSSERR("RX fifo empty when trying to read.\n");
+ return -EIO;
+ }
+
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ if (dsi.debug_read)
+ DSSDBG("\theader: %08x\n", val);
+ dt = FLD_GET(val, 5, 0);
+ if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+ u16 err = FLD_GET(val, 23, 8);
+ dsi_show_rx_ack_with_err(err);
+ return -EIO;
+
+ } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+ u8 data = FLD_GET(val, 15, 8);
+ if (dsi.debug_read)
+ DSSDBG("\tDCS short response, 1 byte: %02x\n", data);
+
+ if (buflen < 1)
+ return -EIO;
+
+ buf[0] = data;
+
+ return 1;
+ } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+ u16 data = FLD_GET(val, 23, 8);
+ if (dsi.debug_read)
+ DSSDBG("\tDCS short response, 2 byte: %04x\n", data);
+
+ if (buflen < 2)
+ return -EIO;
+
+ buf[0] = data & 0xff;
+ buf[1] = (data >> 8) & 0xff;
+
+ return 2;
+ } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+ int w;
+ int len = FLD_GET(val, 23, 8);
+ if (dsi.debug_read)
+ DSSDBG("\tDCS long response, len %d\n", len);
+
+ if (len > buflen)
+ return -EIO;
+
+ /* two byte checksum ends the packet, not included in len */
+ for (w = 0; w < len + 2;) {
+ int b;
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ if (dsi.debug_read)
+ DSSDBG("\t\t%02x %02x %02x %02x\n",
+ (val >> 0) & 0xff,
+ (val >> 8) & 0xff,
+ (val >> 16) & 0xff,
+ (val >> 24) & 0xff);
+
+ for (b = 0; b < 4; ++b) {
+ if (w < len)
+ buf[w] = (val >> (b * 8)) & 0xff;
+ /* we discard the 2 byte checksum */
+ ++w;
+ }
+ }
+
+ return len;
+
+ } else {
+ DSSERR("\tunknown datatype 0x%02x\n", dt);
+ return -EIO;
+ }
+}
+EXPORT_SYMBOL(dsi_vc_dcs_read);
+
+
+int dsi_vc_set_max_rx_packet_size(int channel, u16 len)
+{
+ int r;
+ r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
+ len, 0);
+
+ if (r)
+ return r;
+
+ r = dsi_vc_send_bta_sync(channel);
+
+ return r;
+}
+EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
+
+static void dsi_set_lp_rx_timeout(unsigned long ns)
+{
+ u32 r;
+ unsigned x4, x16;
+ unsigned long fck;
+ unsigned long ticks;
+
+ /* ticks in DSI_FCK */
+
+ fck = dsi_fclk_rate();
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+ x4 = 0;
+ x16 = 0;
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
+ x4 = 1;
+ x16 = 0;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+ x4 = 0;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
+ x4 = 1;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ DSSWARN("LP_TX_TO over limit, setting it to max\n");
+ ticks = 0x1fff;
+ x4 = 1;
+ x16 = 1;
+ }
+
+ r = dsi_read_reg(DSI_TIMING2);
+ r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
+ r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */
+ r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */
+ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
+ dsi_write_reg(DSI_TIMING2, r);
+
+ DSSDBG("LP_RX_TO %lu ns (%#lx ticks%s%s)\n",
+ (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+}
+
+static void dsi_set_ta_timeout(unsigned long ns)
+{
+ u32 r;
+ unsigned x8, x16;
+ unsigned long fck;
+ unsigned long ticks;
+
+ /* ticks in DSI_FCK */
+ fck = dsi_fclk_rate();
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+ x8 = 0;
+ x16 = 0;
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 8;
+ x8 = 1;
+ x16 = 0;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+ x8 = 0;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / (8 * 16);
+ x8 = 1;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ DSSWARN("TA_TO over limit, setting it to max\n");
+ ticks = 0x1fff;
+ x8 = 1;
+ x16 = 1;
+ }
+
+ r = dsi_read_reg(DSI_TIMING1);
+ r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
+ r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */
+ r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */
+ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
+ dsi_write_reg(DSI_TIMING1, r);
+
+ DSSDBG("TA_TO %lu ns (%#lx ticks%s%s)\n",
+ (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks, x8 ? " x8" : "", x16 ? " x16" : "");
+}
+
+static void dsi_set_stop_state_counter(unsigned long ns)
+{
+ u32 r;
+ unsigned x4, x16;
+ unsigned long fck;
+ unsigned long ticks;
+
+ /* ticks in DSI_FCK */
+
+ fck = dsi_fclk_rate();
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+ x4 = 0;
+ x16 = 0;
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
+ x4 = 1;
+ x16 = 0;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+ x4 = 0;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
+ x4 = 1;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ DSSWARN("STOP_STATE_COUNTER_IO over limit, "
+ "setting it to max\n");
+ ticks = 0x1fff;
+ x4 = 1;
+ x16 = 1;
+ }
+
+ r = dsi_read_reg(DSI_TIMING1);
+ r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
+ r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */
+ r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */
+ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
+ dsi_write_reg(DSI_TIMING1, r);
+
+ DSSDBG("STOP_STATE_COUNTER %lu ns (%#lx ticks%s%s)\n",
+ (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+}
+
+static void dsi_set_hs_tx_timeout(unsigned long ns)
+{
+ u32 r;
+ unsigned x4, x16;
+ unsigned long fck;
+ unsigned long ticks;
+
+ /* ticks in TxByteClkHS */
+
+ fck = dsi_get_txbyteclkhs();
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+ x4 = 0;
+ x16 = 0;
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
+ x4 = 1;
+ x16 = 0;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+ x4 = 0;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
+ x4 = 1;
+ x16 = 1;
+ }
+
+ if (ticks > 0x1fff) {
+ DSSWARN("HS_TX_TO over limit, setting it to max\n");
+ ticks = 0x1fff;
+ x4 = 1;
+ x16 = 1;
+ }
+
+ r = dsi_read_reg(DSI_TIMING2);
+ r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
+ r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */
+ r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */
+ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
+ dsi_write_reg(DSI_TIMING2, r);
+
+ DSSDBG("HS_TX_TO %lu ns (%#lx ticks%s%s)\n",
+ (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+}
+static int dsi_proto_config(struct omap_dss_device *dssdev)
+{
+ u32 r;
+ int buswidth = 0;
+
+ dsi_config_tx_fifo(DSI_FIFO_SIZE_128,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0);
+
+ dsi_config_rx_fifo(DSI_FIFO_SIZE_128,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0);
+
+ /* XXX what values for the timeouts? */
+ dsi_set_stop_state_counter(1000);
+ dsi_set_ta_timeout(6400000);
+ dsi_set_lp_rx_timeout(48000);
+ dsi_set_hs_tx_timeout(1000000);
+
+ switch (dssdev->ctrl.pixel_size) {
+ case 16:
+ buswidth = 0;
+ break;
+ case 18:
+ buswidth = 1;
+ break;
+ case 24:
+ buswidth = 2;
+ break;
+ default:
+ BUG();
+ }
+
+ r = dsi_read_reg(DSI_CTRL);
+ r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */
+ r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */
+ r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */
+ r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/
+ r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
+ r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
+ r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */
+ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
+ r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
+ r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */
+ r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */
+
+ dsi_write_reg(DSI_CTRL, r);
+
+ dsi_vc_initial_config(0);
+
+ /* set all vc targets to peripheral 0 */
+ dsi.vc[0].dest_per = 0;
+ dsi.vc[1].dest_per = 0;
+ dsi.vc[2].dest_per = 0;
+ dsi.vc[3].dest_per = 0;
+
+ return 0;
+}
+
+static void dsi_proto_timings(struct omap_dss_device *dssdev)
+{
+ unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail;
+ unsigned tclk_pre, tclk_post;
+ unsigned ths_prepare, ths_prepare_ths_zero, ths_zero;
+ unsigned ths_trail, ths_exit;
+ unsigned ddr_clk_pre, ddr_clk_post;
+ unsigned enter_hs_mode_lat, exit_hs_mode_lat;
+ unsigned ths_eot;
+ u32 r;
+
+ r = dsi_read_reg(DSI_DSIPHY_CFG0);
+ ths_prepare = FLD_GET(r, 31, 24);
+ ths_prepare_ths_zero = FLD_GET(r, 23, 16);
+ ths_zero = ths_prepare_ths_zero - ths_prepare;
+ ths_trail = FLD_GET(r, 15, 8);
+ ths_exit = FLD_GET(r, 7, 0);
+
+ r = dsi_read_reg(DSI_DSIPHY_CFG1);
+ tlpx = FLD_GET(r, 22, 16) * 2;
+ tclk_trail = FLD_GET(r, 15, 8);
+ tclk_zero = FLD_GET(r, 7, 0);
+
+ r = dsi_read_reg(DSI_DSIPHY_CFG2);
+ tclk_prepare = FLD_GET(r, 7, 0);
+
+ /* min 8*UI */
+ tclk_pre = 20;
+ /* min 60ns + 52*UI */
+ tclk_post = ns2ddr(60) + 26;
+
+ /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */
+ if (dssdev->phy.dsi.data1_lane != 0 &&
+ dssdev->phy.dsi.data2_lane != 0)
+ ths_eot = 2;
+ else
+ ths_eot = 4;
+
+ ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
+ 4);
+ ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
+
+ BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
+ BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
+
+ r = dsi_read_reg(DSI_CLK_TIMING);
+ r = FLD_MOD(r, ddr_clk_pre, 15, 8);
+ r = FLD_MOD(r, ddr_clk_post, 7, 0);
+ dsi_write_reg(DSI_CLK_TIMING, r);
+
+ DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
+ ddr_clk_pre,
+ ddr_clk_post);
+
+ enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
+ DIV_ROUND_UP(ths_prepare, 4) +
+ DIV_ROUND_UP(ths_zero + 3, 4);
+
+ exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
+
+ r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
+ FLD_VAL(exit_hs_mode_lat, 15, 0);
+ dsi_write_reg(DSI_VM_TIMING7, r);
+
+ DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
+ enter_hs_mode_lat, exit_hs_mode_lat);
+}
+
+
+#define DSI_DECL_VARS \
+ int __dsi_cb = 0; u32 __dsi_cv = 0;
+
+#define DSI_FLUSH(ch) \
+ if (__dsi_cb > 0) { \
+ /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \
+ dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \
+ __dsi_cb = __dsi_cv = 0; \
+ }
+
+#define DSI_PUSH(ch, data) \
+ do { \
+ __dsi_cv |= (data) << (__dsi_cb * 8); \
+ /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \
+ if (++__dsi_cb > 3) \
+ DSI_FLUSH(ch); \
+ } while (0)
+
+static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+ int x, int y, int w, int h)
+{
+ /* Note: supports only 24bit colors in 32bit container */
+ int first = 1;
+ int fifo_stalls = 0;
+ int max_dsi_packet_size;
+ int max_data_per_packet;
+ int max_pixels_per_packet;
+ int pixels_left;
+ int bytespp = dssdev->ctrl.pixel_size / 8;
+ int scr_width;
+ u32 __iomem *data;
+ int start_offset;
+ int horiz_inc;
+ int current_x;
+ struct omap_overlay *ovl;
+
+ debug_irq = 0;
+
+ DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n",
+ x, y, w, h);
+
+ ovl = dssdev->manager->overlays[0];
+
+ if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U)
+ return -EINVAL;
+
+ if (dssdev->ctrl.pixel_size != 24)
+ return -EINVAL;
+
+ scr_width = ovl->info.screen_width;
+ data = ovl->info.vaddr;
+
+ start_offset = scr_width * y + x;
+ horiz_inc = scr_width - w;
+ current_x = x;
+
+ /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes
+ * in fifo */
+
+ /* When using CPU, max long packet size is TX buffer size */
+ max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4;
+
+ /* we seem to get better perf if we divide the tx fifo to half,
+ and while the other half is being sent, we fill the other half
+ max_dsi_packet_size /= 2; */
+
+ max_data_per_packet = max_dsi_packet_size - 4 - 1;
+
+ max_pixels_per_packet = max_data_per_packet / bytespp;
+
+ DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet);
+
+ pixels_left = w * h;
+
+ DSSDBG("total pixels %d\n", pixels_left);
+
+ data += start_offset;
+
+ while (pixels_left > 0) {
+ /* 0x2c = write_memory_start */
+ /* 0x3c = write_memory_continue */
+ u8 dcs_cmd = first ? 0x2c : 0x3c;
+ int pixels;
+ DSI_DECL_VARS;
+ first = 0;
+
+#if 1
+ /* using fifo not empty */
+ /* TX_FIFO_NOT_EMPTY */
+ while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) {
+ udelay(1);
+ fifo_stalls++;
+ if (fifo_stalls > 0xfffff) {
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+ return -EIO;
+ }
+ }
+#elif 1
+ /* using fifo emptiness */
+ while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 <
+ max_dsi_packet_size) {
+ fifo_stalls++;
+ if (fifo_stalls > 0xfffff) {
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+ return -EIO;
+ }
+ }
+#else
+ while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) {
+ fifo_stalls++;
+ if (fifo_stalls > 0xfffff) {
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+ return -EIO;
+ }
+ }
+#endif
+ pixels = min(max_pixels_per_packet, pixels_left);
+
+ pixels_left -= pixels;
+
+ dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE,
+ 1 + pixels * bytespp, 0);
+
+ DSI_PUSH(0, dcs_cmd);
+
+ while (pixels-- > 0) {
+ u32 pix = __raw_readl(data++);
+
+ DSI_PUSH(0, (pix >> 16) & 0xff);
+ DSI_PUSH(0, (pix >> 8) & 0xff);
+ DSI_PUSH(0, (pix >> 0) & 0xff);
+
+ current_x++;
+ if (current_x == x+w) {
+ current_x = x;
+ data += horiz_inc;
+ }
+ }
+
+ DSI_FLUSH(0);
+ }
+
+ return 0;
+}
+
+static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ unsigned bytespp;
+ unsigned bytespl;
+ unsigned bytespf;
+ unsigned total_len;
+ unsigned packet_payload;
+ unsigned packet_len;
+ u32 l;
+ bool use_te_trigger;
+ const unsigned channel = 0;
+ /* line buffer is 1024 x 24bits */
+ /* XXX: for some reason using full buffer size causes considerable TX
+ * slowdown with update sizes that fill the whole buffer */
+ const unsigned line_buf_size = 1023 * 3;
+
+ use_te_trigger = dsi.te_enabled && !dsi.use_ext_te;
+
+ if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
+ DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
+ x, y, w, h);
+
+ bytespp = dssdev->ctrl.pixel_size / 8;
+ bytespl = w * bytespp;
+ bytespf = bytespl * h;
+
+ /* NOTE: packet_payload has to be equal to N * bytespl, where N is
+ * number of lines in a packet. See errata about VP_CLK_RATIO */
+
+ if (bytespf < line_buf_size)
+ packet_payload = bytespf;
+ else
+ packet_payload = (line_buf_size) / bytespl * bytespl;
+
+ packet_len = packet_payload + 1; /* 1 byte for DCS cmd */
+ total_len = (bytespf / packet_payload) * packet_len;
+
+ if (bytespf % packet_payload)
+ total_len += (bytespf % packet_payload) + 1;
+
+ if (0)
+ dsi_vc_print_status(1);
+
+ l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
+ dsi_write_reg(DSI_VC_TE(channel), l);
+
+ dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0);
+
+ if (use_te_trigger)
+ l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
+ else
+ l = FLD_MOD(l, 1, 31, 31); /* TE_START */
+ dsi_write_reg(DSI_VC_TE(channel), l);
+
+ /* We put SIDLEMODE to no-idle for the duration of the transfer,
+ * because DSS interrupts are not capable of waking up the CPU and the
+ * framedone interrupt could be delayed for quite a long time. I think
+ * the same goes for any DSS interrupts, but for some reason I have not
+ * seen the problem anywhere else than here.
+ */
+ dispc_disable_sidle();
+
+ dss_start_update(dssdev);
+
+ if (use_te_trigger) {
+ /* disable LP_RX_TO, so that we can receive TE. Time to wait
+ * for TE is longer than the timer allows */
+ REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
+
+ dsi_vc_send_bta(channel);
+
+#ifdef DSI_CATCH_MISSING_TE
+ mod_timer(&dsi.te_timer, jiffies + msecs_to_jiffies(250));
+#endif
+ }
+}
+
+#ifdef DSI_CATCH_MISSING_TE
+static void dsi_te_timeout(unsigned long arg)
+{
+ DSSERR("TE not received for 250ms!\n");
+}
+#endif
+
+static void dsi_framedone_irq_callback(void *data, u32 mask)
+{
+ /* Note: We get FRAMEDONE when DISPC has finished sending pixels and
+ * turns itself off. However, DSI still has the pixels in its buffers,
+ * and is sending the data.
+ */
+
+ /* SIDLEMODE back to smart-idle */
+ dispc_enable_sidle();
+
+ dsi.framedone_received = true;
+ wake_up(&dsi.waitqueue);
+}
+
+static void dsi_set_update_region(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ spin_lock(&dsi.update_lock);
+ if (dsi.update_region.dirty) {
+ dsi.update_region.x = min(x, dsi.update_region.x);
+ dsi.update_region.y = min(y, dsi.update_region.y);
+ dsi.update_region.w = max(w, dsi.update_region.w);
+ dsi.update_region.h = max(h, dsi.update_region.h);
+ } else {
+ dsi.update_region.x = x;
+ dsi.update_region.y = y;
+ dsi.update_region.w = w;
+ dsi.update_region.h = h;
+ }
+
+ dsi.update_region.device = dssdev;
+ dsi.update_region.dirty = true;
+
+ spin_unlock(&dsi.update_lock);
+
+}
+
+static int dsi_set_update_mode(struct omap_dss_device *dssdev,
+ enum omap_dss_update_mode mode)
+{
+ int r = 0;
+ int i;
+
+ WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+ if (dsi.update_mode != mode) {
+ dsi.update_mode = mode;
+
+ /* Mark the overlays dirty, and do apply(), so that we get the
+ * overlays configured properly after update mode change. */
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+ if (ovl->manager == dssdev->manager)
+ ovl->info_dirty = true;
+ }
+
+ r = dssdev->manager->apply(dssdev->manager);
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE &&
+ mode == OMAP_DSS_UPDATE_AUTO) {
+ u16 w, h;
+
+ DSSDBG("starting auto update\n");
+
+ dssdev->get_resolution(dssdev, &w, &h);
+
+ dsi_set_update_region(dssdev, 0, 0, w, h);
+
+ dsi_perf_mark_start_auto();
+
+ wake_up(&dsi.waitqueue);
+ }
+ }
+
+ return r;
+}
+
+static int dsi_set_te(struct omap_dss_device *dssdev, bool enable)
+{
+ int r;
+ r = dssdev->driver->enable_te(dssdev, enable);
+ /* XXX for some reason, DSI TE breaks if we don't wait here.
+ * Panel bug? Needs more studying */
+ msleep(100);
+ return r;
+}
+
+static void dsi_handle_framedone(void)
+{
+ int r;
+ const int channel = 0;
+ bool use_te_trigger;
+
+ use_te_trigger = dsi.te_enabled && !dsi.use_ext_te;
+
+ if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
+ DSSDBG("FRAMEDONE\n");
+
+ if (use_te_trigger) {
+ /* enable LP_RX_TO again after the TE */
+ REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
+ }
+
+ /* Send BTA after the frame. We need this for the TE to work, as TE
+ * trigger is only sent for BTAs without preceding packet. Thus we need
+ * to BTA after the pixel packets so that next BTA will cause TE
+ * trigger.
+ *
+ * This is not needed when TE is not in use, but we do it anyway to
+ * make sure that the transfer has been completed. It would be more
+ * optimal, but more complex, to wait only just before starting next
+ * transfer. */
+ r = dsi_vc_send_bta_sync(channel);
+ if (r)
+ DSSERR("BTA after framedone failed\n");
+
+ /* RX_FIFO_NOT_EMPTY */
+ if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+ DSSERR("Received error during frame transfer:\n");
+ dsi_vc_flush_receive_data(0);
+ }
+
+#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
+ dispc_fake_vsync_irq();
+#endif
+}
+
+static int dsi_update_thread(void *data)
+{
+ unsigned long timeout;
+ struct omap_dss_device *device;
+ u16 x, y, w, h;
+
+ while (1) {
+ bool sched;
+
+ wait_event_interruptible(dsi.waitqueue,
+ dsi.update_mode == OMAP_DSS_UPDATE_AUTO ||
+ (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
+ dsi.update_region.dirty == true) ||
+ kthread_should_stop());
+
+ if (kthread_should_stop())
+ break;
+
+ dsi_bus_lock();
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED ||
+ kthread_should_stop()) {
+ dsi_bus_unlock();
+ break;
+ }
+
+ dsi_perf_mark_setup();
+
+ if (dsi.update_region.dirty) {
+ spin_lock(&dsi.update_lock);
+ dsi.active_update_region = dsi.update_region;
+ dsi.update_region.dirty = false;
+ spin_unlock(&dsi.update_lock);
+ }
+
+ device = dsi.active_update_region.device;
+ x = dsi.active_update_region.x;
+ y = dsi.active_update_region.y;
+ w = dsi.active_update_region.w;
+ h = dsi.active_update_region.h;
+
+ if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+ dss_setup_partial_planes(device,
+ &x, &y, &w, &h);
+
+ dispc_set_lcd_size(w, h);
+ }
+
+ if (dsi.active_update_region.dirty) {
+ dsi.active_update_region.dirty = false;
+ /* XXX TODO we don't need to send the coords, if they
+ * are the same that are already programmed to the
+ * panel. That should speed up manual update a bit */
+ device->driver->setup_update(device, x, y, w, h);
+ }
+
+ dsi_perf_mark_start();
+
+ if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+ dsi_vc_config_vp(0);
+
+ if (dsi.te_enabled && dsi.use_ext_te)
+ device->driver->wait_for_te(device);
+
+ dsi.framedone_received = false;
+
+ dsi_update_screen_dispc(device, x, y, w, h);
+
+ /* wait for framedone */
+ timeout = msecs_to_jiffies(1000);
+ wait_event_timeout(dsi.waitqueue,
+ dsi.framedone_received == true,
+ timeout);
+
+ if (!dsi.framedone_received) {
+ DSSERR("framedone timeout\n");
+ DSSERR("failed update %d,%d %dx%d\n",
+ x, y, w, h);
+
+ dispc_enable_sidle();
+ dispc_enable_lcd_out(0);
+
+ dsi_reset_tx_fifo(0);
+ } else {
+ dsi_handle_framedone();
+ dsi_perf_show("DISPC");
+ }
+ } else {
+ dsi_update_screen_l4(device, x, y, w, h);
+ dsi_perf_show("L4");
+ }
+
+ sched = atomic_read(&dsi.bus_lock.count) < 0;
+
+ complete_all(&dsi.update_completion);
+
+ dsi_bus_unlock();
+
+ /* XXX We need to give others chance to get the bus lock. Is
+ * there a better way for this? */
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO && sched)
+ schedule_timeout_interruptible(1);
+ }
+
+ DSSDBG("update thread exiting\n");
+
+ return 0;
+}
+
+
+
+/* Display funcs */
+
+static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+ if (r) {
+ DSSERR("can't get FRAMEDONE irq\n");
+ return r;
+ }
+
+ dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
+
+ dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI);
+ dispc_enable_fifohandcheck(1);
+
+ dispc_set_tft_data_lines(dssdev->ctrl.pixel_size);
+
+ {
+ struct omap_video_timings timings = {
+ .hsw = 1,
+ .hfp = 1,
+ .hbp = 1,
+ .vsw = 1,
+ .vfp = 0,
+ .vbp = 0,
+ };
+
+ dispc_set_lcd_timings(&timings);
+ }
+
+ return 0;
+}
+
+static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
+{
+ omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+}
+
+static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
+{
+ struct dsi_clock_info cinfo;
+ int r;
+
+ /* we always use DSS2_FCK as input clock */
+ cinfo.use_dss2_fck = true;
+ cinfo.regn = dssdev->phy.dsi.div.regn;
+ cinfo.regm = dssdev->phy.dsi.div.regm;
+ cinfo.regm3 = dssdev->phy.dsi.div.regm3;
+ cinfo.regm4 = dssdev->phy.dsi.div.regm4;
+ r = dsi_calc_clock_rates(&cinfo);
+ if (r)
+ return r;
+
+ r = dsi_pll_set_clock_div(&cinfo);
+ if (r) {
+ DSSERR("Failed to set dsi clocks\n");
+ return r;
+ }
+
+ return 0;
+}
+
+static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
+{
+ struct dispc_clock_info dispc_cinfo;
+ int r;
+ unsigned long long fck;
+
+ fck = dsi_get_dsi1_pll_rate();
+
+ dispc_cinfo.lck_div = dssdev->phy.dsi.div.lck_div;
+ dispc_cinfo.pck_div = dssdev->phy.dsi.div.pck_div;
+
+ r = dispc_calc_clock_rates(fck, &dispc_cinfo);
+ if (r) {
+ DSSERR("Failed to calc dispc clocks\n");
+ return r;
+ }
+
+ r = dispc_set_clock_div(&dispc_cinfo);
+ if (r) {
+ DSSERR("Failed to set dispc clocks\n");
+ return r;
+ }
+
+ return 0;
+}
+
+static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ _dsi_print_reset_status();
+
+ r = dsi_pll_init(dssdev, true, true);
+ if (r)
+ goto err0;
+
+ r = dsi_configure_dsi_clocks(dssdev);
+ if (r)
+ goto err1;
+
+ dss_select_clk_source(true, true);
+
+ DSSDBG("PLL OK\n");
+
+ r = dsi_configure_dispc_clocks(dssdev);
+ if (r)
+ goto err2;
+
+ r = dsi_complexio_init(dssdev);
+ if (r)
+ goto err2;
+
+ _dsi_print_reset_status();
+
+ dsi_proto_timings(dssdev);
+ dsi_set_lp_clk_divisor(dssdev);
+
+ if (1)
+ _dsi_print_reset_status();
+
+ r = dsi_proto_config(dssdev);
+ if (r)
+ goto err3;
+
+ /* enable interface */
+ dsi_vc_enable(0, 1);
+ dsi_if_enable(1);
+ dsi_force_tx_stop_mode_io();
+
+ if (dssdev->driver->enable) {
+ r = dssdev->driver->enable(dssdev);
+ if (r)
+ goto err4;
+ }
+
+ /* enable high-speed after initial config */
+ dsi_vc_enable_hs(0, 1);
+
+ return 0;
+err4:
+ dsi_if_enable(0);
+err3:
+ dsi_complexio_uninit();
+err2:
+ dss_select_clk_source(false, false);
+err1:
+ dsi_pll_uninit();
+err0:
+ return r;
+}
+
+static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev)
+{
+ if (dssdev->driver->disable)
+ dssdev->driver->disable(dssdev);
+
+ dss_select_clk_source(false, false);
+ dsi_complexio_uninit();
+ dsi_pll_uninit();
+}
+
+static int dsi_core_init(void)
+{
+ /* Autoidle */
+ REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0);
+
+ /* ENWAKEUP */
+ REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2);
+
+ /* SIDLEMODE smart-idle */
+ REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3);
+
+ _dsi_initialize_irq();
+
+ return 0;
+}
+
+static int dsi_display_enable(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ DSSDBG("dsi_display_enable\n");
+
+ mutex_lock(&dsi.lock);
+ dsi_bus_lock();
+
+ r = omap_dss_start_device(dssdev);
+ if (r) {
+ DSSERR("failed to start device\n");
+ goto err0;
+ }
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+ DSSERR("dssdev already enabled\n");
+ r = -EINVAL;
+ goto err1;
+ }
+
+ enable_clocks(1);
+ dsi_enable_pll_clock(1);
+
+ r = _dsi_reset();
+ if (r)
+ goto err2;
+
+ dsi_core_init();
+
+ r = dsi_display_init_dispc(dssdev);
+ if (r)
+ goto err2;
+
+ r = dsi_display_init_dsi(dssdev);
+ if (r)
+ goto err3;
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ dsi.use_ext_te = dssdev->phy.dsi.ext_te;
+ r = dsi_set_te(dssdev, dsi.te_enabled);
+ if (r)
+ goto err4;
+
+ dsi_set_update_mode(dssdev, dsi.user_update_mode);
+
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+ return 0;
+
+err4:
+
+ dsi_display_uninit_dsi(dssdev);
+err3:
+ dsi_display_uninit_dispc(dssdev);
+err2:
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+err1:
+ omap_dss_stop_device(dssdev);
+err0:
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+ DSSDBG("dsi_display_enable FAILED\n");
+ return r;
+}
+
+static void dsi_display_disable(struct omap_dss_device *dssdev)
+{
+ DSSDBG("dsi_display_disable\n");
+
+ mutex_lock(&dsi.lock);
+ dsi_bus_lock();
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+ dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ goto end;
+
+ dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ dsi_display_uninit_dispc(dssdev);
+
+ dsi_display_uninit_dsi(dssdev);
+
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+
+ omap_dss_stop_device(dssdev);
+end:
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+}
+
+static int dsi_display_suspend(struct omap_dss_device *dssdev)
+{
+ DSSDBG("dsi_display_suspend\n");
+
+ mutex_lock(&dsi.lock);
+ dsi_bus_lock();
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+ dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ goto end;
+
+ dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ dsi_display_uninit_dispc(dssdev);
+
+ dsi_display_uninit_dsi(dssdev);
+
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+end:
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+ return 0;
+}
+
+static int dsi_display_resume(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ DSSDBG("dsi_display_resume\n");
+
+ mutex_lock(&dsi.lock);
+ dsi_bus_lock();
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+ DSSERR("dssdev not suspended\n");
+ r = -EINVAL;
+ goto err0;
+ }
+
+ enable_clocks(1);
+ dsi_enable_pll_clock(1);
+
+ r = _dsi_reset();
+ if (r)
+ goto err1;
+
+ dsi_core_init();
+
+ r = dsi_display_init_dispc(dssdev);
+ if (r)
+ goto err1;
+
+ r = dsi_display_init_dsi(dssdev);
+ if (r)
+ goto err2;
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ r = dsi_set_te(dssdev, dsi.te_enabled);
+ if (r)
+ goto err2;
+
+ dsi_set_update_mode(dssdev, dsi.user_update_mode);
+
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+ return 0;
+
+err2:
+ dsi_display_uninit_dispc(dssdev);
+err1:
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+err0:
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+ DSSDBG("dsi_display_resume FAILED\n");
+ return r;
+}
+
+static int dsi_display_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ int r = 0;
+ u16 dw, dh;
+
+ DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h);
+
+ mutex_lock(&dsi.lock);
+
+ if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL)
+ goto end;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ goto end;
+
+ dssdev->get_resolution(dssdev, &dw, &dh);
+
+ if (x > dw || y > dh)
+ goto end;
+
+ if (x + w > dw)
+ w = dw - x;
+
+ if (y + h > dh)
+ h = dh - y;
+
+ if (w == 0 || h == 0)
+ goto end;
+
+ if (w == 1) {
+ r = -EINVAL;
+ goto end;
+ }
+
+ dsi_set_update_region(dssdev, x, y, w, h);
+
+ wake_up(&dsi.waitqueue);
+
+end:
+ mutex_unlock(&dsi.lock);
+
+ return r;
+}
+
+static int dsi_display_sync(struct omap_dss_device *dssdev)
+{
+ bool wait;
+
+ DSSDBG("dsi_display_sync()\n");
+
+ mutex_lock(&dsi.lock);
+ dsi_bus_lock();
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
+ dsi.update_region.dirty) {
+ INIT_COMPLETION(dsi.update_completion);
+ wait = true;
+ } else {
+ wait = false;
+ }
+
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+ if (wait)
+ wait_for_completion_interruptible(&dsi.update_completion);
+
+ DSSDBG("dsi_display_sync() done\n");
+ return 0;
+}
+
+static int dsi_display_set_update_mode(struct omap_dss_device *dssdev,
+ enum omap_dss_update_mode mode)
+{
+ int r = 0;
+
+ DSSDBGF("%d", mode);
+
+ mutex_lock(&dsi.lock);
+ dsi_bus_lock();
+
+ dsi.user_update_mode = mode;
+ r = dsi_set_update_mode(dssdev, mode);
+
+ dsi_bus_unlock();
+ mutex_unlock(&dsi.lock);
+
+ return r;
+}
+
+static enum omap_dss_update_mode dsi_display_get_update_mode(
+ struct omap_dss_device *dssdev)
+{
+ return dsi.update_mode;
+}
+
+
+static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+ int r = 0;
+
+ DSSDBGF("%d", enable);
+
+ if (!dssdev->driver->enable_te)
+ return -ENOENT;
+
+ dsi_bus_lock();
+
+ dsi.te_enabled = enable;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ goto end;
+
+ r = dsi_set_te(dssdev, enable);
+end:
+ dsi_bus_unlock();
+
+ return r;
+}
+
+static int dsi_display_get_te(struct omap_dss_device *dssdev)
+{
+ return dsi.te_enabled;
+}
+
+static int dsi_display_set_rotate(struct omap_dss_device *dssdev, u8 rotate)
+{
+
+ DSSDBGF("%d", rotate);
+
+ if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
+ return -EINVAL;
+
+ dsi_bus_lock();
+ dssdev->driver->set_rotate(dssdev, rotate);
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+ u16 w, h;
+ /* the display dimensions may have changed, so set a new
+ * update region */
+ dssdev->get_resolution(dssdev, &w, &h);
+ dsi_set_update_region(dssdev, 0, 0, w, h);
+ }
+ dsi_bus_unlock();
+
+ return 0;
+}
+
+static u8 dsi_display_get_rotate(struct omap_dss_device *dssdev)
+{
+ if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
+ return 0;
+
+ return dssdev->driver->get_rotate(dssdev);
+}
+
+static int dsi_display_set_mirror(struct omap_dss_device *dssdev, bool mirror)
+{
+ DSSDBGF("%d", mirror);
+
+ if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
+ return -EINVAL;
+
+ dsi_bus_lock();
+ dssdev->driver->set_mirror(dssdev, mirror);
+ dsi_bus_unlock();
+
+ return 0;
+}
+
+static bool dsi_display_get_mirror(struct omap_dss_device *dssdev)
+{
+ if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
+ return 0;
+
+ return dssdev->driver->get_mirror(dssdev);
+}
+
+static int dsi_display_run_test(struct omap_dss_device *dssdev, int test_num)
+{
+ int r;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EIO;
+
+ DSSDBGF("%d", test_num);
+
+ dsi_bus_lock();
+
+ /* run test first in low speed mode */
+ dsi_vc_enable_hs(0, 0);
+
+ if (dssdev->driver->run_test) {
+ r = dssdev->driver->run_test(dssdev, test_num);
+ if (r)
+ goto end;
+ }
+
+ /* then in high speed */
+ dsi_vc_enable_hs(0, 1);
+
+ if (dssdev->driver->run_test) {
+ r = dssdev->driver->run_test(dssdev, test_num);
+ if (r)
+ goto end;
+ }
+
+end:
+ dsi_vc_enable_hs(0, 1);
+
+ dsi_bus_unlock();
+
+ return r;
+}
+
+static int dsi_display_memory_read(struct omap_dss_device *dssdev,
+ void *buf, size_t size,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ int r;
+
+ DSSDBGF("");
+
+ if (!dssdev->driver->memory_read)
+ return -EINVAL;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EIO;
+
+ dsi_bus_lock();
+
+ r = dssdev->driver->memory_read(dssdev, buf, size,
+ x, y, w, h);
+
+ /* Memory read usually changes the update area. This will
+ * force the next update to re-set the update area */
+ dsi.active_update_region.dirty = true;
+
+ dsi_bus_unlock();
+
+ return r;
+}
+
+void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
+ u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 *fifo_low, u32 *fifo_high)
+{
+ unsigned burst_size_bytes;
+
+ *burst_size = OMAP_DSS_BURST_16x32;
+ burst_size_bytes = 16 * 32 / 8;
+
+ *fifo_high = fifo_size - burst_size_bytes;
+ *fifo_low = fifo_size - burst_size_bytes * 8;
+}
+
+int dsi_init_display(struct omap_dss_device *dssdev)
+{
+ DSSDBG("DSI init\n");
+
+ dssdev->enable = dsi_display_enable;
+ dssdev->disable = dsi_display_disable;
+ dssdev->suspend = dsi_display_suspend;
+ dssdev->resume = dsi_display_resume;
+ dssdev->update = dsi_display_update;
+ dssdev->sync = dsi_display_sync;
+ dssdev->set_update_mode = dsi_display_set_update_mode;
+ dssdev->get_update_mode = dsi_display_get_update_mode;
+ dssdev->enable_te = dsi_display_enable_te;
+ dssdev->get_te = dsi_display_get_te;
+
+ dssdev->get_rotate = dsi_display_get_rotate;
+ dssdev->set_rotate = dsi_display_set_rotate;
+
+ dssdev->get_mirror = dsi_display_get_mirror;
+ dssdev->set_mirror = dsi_display_set_mirror;
+
+ dssdev->run_test = dsi_display_run_test;
+ dssdev->memory_read = dsi_display_memory_read;
+
+ /* XXX these should be figured out dynamically */
+ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
+ OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+
+ dsi.vc[0].dssdev = dssdev;
+ dsi.vc[1].dssdev = dssdev;
+
+ return 0;
+}
+
+int dsi_init(struct platform_device *pdev)
+{
+ u32 rev;
+ int r;
+ struct sched_param param = {
+ .sched_priority = MAX_USER_RT_PRIO-1
+ };
+
+ spin_lock_init(&dsi.errors_lock);
+ dsi.errors = 0;
+
+ init_completion(&dsi.bta_completion);
+ init_completion(&dsi.update_completion);
+
+ dsi.thread = kthread_create(dsi_update_thread, NULL, "dsi");
+ if (IS_ERR(dsi.thread)) {
+ DSSERR("cannot create kthread\n");
+ r = PTR_ERR(dsi.thread);
+ goto err0;
+ }
+ sched_setscheduler(dsi.thread, SCHED_FIFO, &param);
+
+ init_waitqueue_head(&dsi.waitqueue);
+ spin_lock_init(&dsi.update_lock);
+
+ mutex_init(&dsi.lock);
+ mutex_init(&dsi.bus_lock);
+
+#ifdef DSI_CATCH_MISSING_TE
+ init_timer(&dsi.te_timer);
+ dsi.te_timer.function = dsi_te_timeout;
+ dsi.te_timer.data = 0;
+#endif
+
+ dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+ dsi.user_update_mode = OMAP_DSS_UPDATE_DISABLED;
+
+ dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS);
+ if (!dsi.base) {
+ DSSERR("can't ioremap DSI\n");
+ r = -ENOMEM;
+ goto err1;
+ }
+
+ dsi.vdds_dsi_reg = regulator_get(&pdev->dev, "vdds_dsi");
+ if (IS_ERR(dsi.vdds_dsi_reg)) {
+ iounmap(dsi.base);
+ DSSERR("can't get VDDS_DSI regulator\n");
+ r = PTR_ERR(dsi.vdds_dsi_reg);
+ goto err2;
+ }
+
+ enable_clocks(1);
+
+ rev = dsi_read_reg(DSI_REVISION);
+ printk(KERN_INFO "OMAP DSI rev %d.%d\n",
+ FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+ enable_clocks(0);
+
+ wake_up_process(dsi.thread);
+
+ return 0;
+err2:
+ iounmap(dsi.base);
+err1:
+ kthread_stop(dsi.thread);
+err0:
+ return r;
+}
+
+void dsi_exit(void)
+{
+ kthread_stop(dsi.thread);
+
+ regulator_put(dsi.vdds_dsi_reg);
+
+ iounmap(dsi.base);
+
+ DSSDBG("omap_dsi_exit\n");
+}
+
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
new file mode 100644
index 00000000000..9b05ee65a15
--- /dev/null
+++ b/drivers/video/omap2/dss/dss.c
@@ -0,0 +1,596 @@
+/*
+ * linux/drivers/video/omap2/dss/dss.c
+ *
+ * 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 "DSS"
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+#include <linux/clk.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+#define DSS_BASE 0x48050000
+
+#define DSS_SZ_REGS SZ_512
+
+struct dss_reg {
+ u16 idx;
+};
+
+#define DSS_REG(idx) ((const struct dss_reg) { idx })
+
+#define DSS_REVISION DSS_REG(0x0000)
+#define DSS_SYSCONFIG DSS_REG(0x0010)
+#define DSS_SYSSTATUS DSS_REG(0x0014)
+#define DSS_IRQSTATUS DSS_REG(0x0018)
+#define DSS_CONTROL DSS_REG(0x0040)
+#define DSS_SDI_CONTROL DSS_REG(0x0044)
+#define DSS_PLL_CONTROL DSS_REG(0x0048)
+#define DSS_SDI_STATUS DSS_REG(0x005C)
+
+#define REG_GET(idx, start, end) \
+ FLD_GET(dss_read_reg(idx), start, end)
+
+#define REG_FLD_MOD(idx, val, start, end) \
+ dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end))
+
+static struct {
+ void __iomem *base;
+
+ struct clk *dpll4_m4_ck;
+
+ unsigned long cache_req_pck;
+ unsigned long cache_prate;
+ struct dss_clock_info cache_dss_cinfo;
+ struct dispc_clock_info cache_dispc_cinfo;
+
+ u32 ctx[DSS_SZ_REGS / sizeof(u32)];
+} dss;
+
+static int _omap_dss_wait_reset(void);
+
+static inline void dss_write_reg(const struct dss_reg idx, u32 val)
+{
+ __raw_writel(val, dss.base + idx.idx);
+}
+
+static inline u32 dss_read_reg(const struct dss_reg idx)
+{
+ return __raw_readl(dss.base + idx.idx);
+}
+
+#define SR(reg) \
+ dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
+#define RR(reg) \
+ dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
+
+void dss_save_context(void)
+{
+ if (cpu_is_omap24xx())
+ return;
+
+ SR(SYSCONFIG);
+ SR(CONTROL);
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+ SR(SDI_CONTROL);
+ SR(PLL_CONTROL);
+#endif
+}
+
+void dss_restore_context(void)
+{
+ if (_omap_dss_wait_reset())
+ DSSERR("DSS not coming out of reset after sleep\n");
+
+ RR(SYSCONFIG);
+ RR(CONTROL);
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+ RR(SDI_CONTROL);
+ RR(PLL_CONTROL);
+#endif
+}
+
+#undef SR
+#undef RR
+
+void dss_sdi_init(u8 datapairs)
+{
+ u32 l;
+
+ BUG_ON(datapairs > 3 || datapairs < 1);
+
+ l = dss_read_reg(DSS_SDI_CONTROL);
+ l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */
+ l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */
+ l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */
+ dss_write_reg(DSS_SDI_CONTROL, l);
+
+ l = dss_read_reg(DSS_PLL_CONTROL);
+ l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */
+ l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */
+ l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */
+ dss_write_reg(DSS_PLL_CONTROL, l);
+}
+
+int dss_sdi_enable(void)
+{
+ unsigned long timeout;
+
+ dispc_pck_free_enable(1);
+
+ /* Reset SDI PLL */
+ REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
+ udelay(1); /* wait 2x PCLK */
+
+ /* Lock SDI PLL */
+ REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */
+
+ /* Waiting for PLL lock request to complete */
+ timeout = jiffies + msecs_to_jiffies(500);
+ while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
+ if (time_after_eq(jiffies, timeout)) {
+ DSSERR("PLL lock request timed out\n");
+ goto err1;
+ }
+ }
+
+ /* Clearing PLL_GO bit */
+ REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
+
+ /* Waiting for PLL to lock */
+ timeout = jiffies + msecs_to_jiffies(500);
+ while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
+ if (time_after_eq(jiffies, timeout)) {
+ DSSERR("PLL lock timed out\n");
+ goto err1;
+ }
+ }
+
+ dispc_lcd_enable_signal(1);
+
+ /* Waiting for SDI reset to complete */
+ timeout = jiffies + msecs_to_jiffies(500);
+ while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
+ if (time_after_eq(jiffies, timeout)) {
+ DSSERR("SDI reset timed out\n");
+ goto err2;
+ }
+ }
+
+ return 0;
+
+ err2:
+ dispc_lcd_enable_signal(0);
+ err1:
+ /* Reset SDI PLL */
+ REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
+
+ dispc_pck_free_enable(0);
+
+ return -ETIMEDOUT;
+}
+
+void dss_sdi_disable(void)
+{
+ dispc_lcd_enable_signal(0);
+
+ dispc_pck_free_enable(0);
+
+ /* Reset SDI PLL */
+ REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
+}
+
+void dss_dump_clocks(struct seq_file *s)
+{
+ unsigned long dpll4_ck_rate;
+ unsigned long dpll4_m4_ck_rate;
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+ dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck);
+
+ seq_printf(s, "- DSS -\n");
+
+ seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate);
+
+ seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n",
+ dpll4_ck_rate,
+ dpll4_ck_rate / dpll4_m4_ck_rate,
+ dss_clk_get_rate(DSS_CLK_FCK1));
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+void dss_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ DUMPREG(DSS_REVISION);
+ DUMPREG(DSS_SYSCONFIG);
+ DUMPREG(DSS_SYSSTATUS);
+ DUMPREG(DSS_IRQSTATUS);
+ DUMPREG(DSS_CONTROL);
+ DUMPREG(DSS_SDI_CONTROL);
+ DUMPREG(DSS_PLL_CONTROL);
+ DUMPREG(DSS_SDI_STATUS);
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+void dss_select_clk_source(bool dsi, bool dispc)
+{
+ u32 r;
+ r = dss_read_reg(DSS_CONTROL);
+ r = FLD_MOD(r, dsi, 1, 1); /* DSI_CLK_SWITCH */
+ r = FLD_MOD(r, dispc, 0, 0); /* DISPC_CLK_SWITCH */
+ dss_write_reg(DSS_CONTROL, r);
+}
+
+int dss_get_dsi_clk_source(void)
+{
+ return FLD_GET(dss_read_reg(DSS_CONTROL), 1, 1);
+}
+
+int dss_get_dispc_clk_source(void)
+{
+ return FLD_GET(dss_read_reg(DSS_CONTROL), 0, 0);
+}
+
+/* calculate clock rates using dividers in cinfo */
+int dss_calc_clock_rates(struct dss_clock_info *cinfo)
+{
+ unsigned long prate;
+
+ if (cinfo->fck_div > 16 || cinfo->fck_div == 0)
+ return -EINVAL;
+
+ prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+
+ cinfo->fck = prate / cinfo->fck_div;
+
+ return 0;
+}
+
+int dss_set_clock_div(struct dss_clock_info *cinfo)
+{
+ unsigned long prate;
+ int r;
+
+ if (cpu_is_omap34xx()) {
+ prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+ DSSDBG("dpll4_m4 = %ld\n", prate);
+
+ r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div);
+ if (r)
+ return r;
+ }
+
+ DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div);
+
+ return 0;
+}
+
+int dss_get_clock_div(struct dss_clock_info *cinfo)
+{
+ cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1);
+
+ if (cpu_is_omap34xx()) {
+ unsigned long prate;
+ prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+ cinfo->fck_div = prate / (cinfo->fck / 2);
+ } else {
+ cinfo->fck_div = 0;
+ }
+
+ return 0;
+}
+
+unsigned long dss_get_dpll4_rate(void)
+{
+ if (cpu_is_omap34xx())
+ return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+ else
+ return 0;
+}
+
+int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
+ struct dss_clock_info *dss_cinfo,
+ struct dispc_clock_info *dispc_cinfo)
+{
+ unsigned long prate;
+ struct dss_clock_info best_dss;
+ struct dispc_clock_info best_dispc;
+
+ unsigned long fck;
+
+ u16 fck_div;
+
+ int match = 0;
+ int min_fck_per_pck;
+
+ prate = dss_get_dpll4_rate();
+
+ fck = dss_clk_get_rate(DSS_CLK_FCK1);
+ if (req_pck == dss.cache_req_pck &&
+ ((cpu_is_omap34xx() && prate == dss.cache_prate) ||
+ dss.cache_dss_cinfo.fck == fck)) {
+ DSSDBG("dispc clock info found from cache.\n");
+ *dss_cinfo = dss.cache_dss_cinfo;
+ *dispc_cinfo = dss.cache_dispc_cinfo;
+ return 0;
+ }
+
+ min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
+
+ if (min_fck_per_pck &&
+ req_pck * min_fck_per_pck > DISPC_MAX_FCK) {
+ DSSERR("Requested pixel clock not possible with the current "
+ "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
+ "the constraint off.\n");
+ min_fck_per_pck = 0;
+ }
+
+retry:
+ memset(&best_dss, 0, sizeof(best_dss));
+ memset(&best_dispc, 0, sizeof(best_dispc));
+
+ if (cpu_is_omap24xx()) {
+ struct dispc_clock_info cur_dispc;
+ /* XXX can we change the clock on omap2? */
+ fck = dss_clk_get_rate(DSS_CLK_FCK1);
+ fck_div = 1;
+
+ dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+ match = 1;
+
+ best_dss.fck = fck;
+ best_dss.fck_div = fck_div;
+
+ best_dispc = cur_dispc;
+
+ goto found;
+ } else if (cpu_is_omap34xx()) {
+ for (fck_div = 16; fck_div > 0; --fck_div) {
+ struct dispc_clock_info cur_dispc;
+
+ fck = prate / fck_div * 2;
+
+ if (fck > DISPC_MAX_FCK)
+ continue;
+
+ if (min_fck_per_pck &&
+ fck < req_pck * min_fck_per_pck)
+ continue;
+
+ match = 1;
+
+ dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+
+ if (abs(cur_dispc.pck - req_pck) <
+ abs(best_dispc.pck - req_pck)) {
+
+ best_dss.fck = fck;
+ best_dss.fck_div = fck_div;
+
+ best_dispc = cur_dispc;
+
+ if (cur_dispc.pck == req_pck)
+ goto found;
+ }
+ }
+ } else {
+ BUG();
+ }
+
+found:
+ if (!match) {
+ if (min_fck_per_pck) {
+ DSSERR("Could not find suitable clock settings.\n"
+ "Turning FCK/PCK constraint off and"
+ "trying again.\n");
+ min_fck_per_pck = 0;
+ goto retry;
+ }
+
+ DSSERR("Could not find suitable clock settings.\n");
+
+ return -EINVAL;
+ }
+
+ if (dss_cinfo)
+ *dss_cinfo = best_dss;
+ if (dispc_cinfo)
+ *dispc_cinfo = best_dispc;
+
+ dss.cache_req_pck = req_pck;
+ dss.cache_prate = prate;
+ dss.cache_dss_cinfo = best_dss;
+ dss.cache_dispc_cinfo = best_dispc;
+
+ return 0;
+}
+
+
+
+static irqreturn_t dss_irq_handler_omap2(int irq, void *arg)
+{
+ dispc_irq_handler();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dss_irq_handler_omap3(int irq, void *arg)
+{
+ u32 irqstatus;
+
+ irqstatus = dss_read_reg(DSS_IRQSTATUS);
+
+ if (irqstatus & (1<<0)) /* DISPC_IRQ */
+ dispc_irq_handler();
+#ifdef CONFIG_OMAP2_DSS_DSI
+ if (irqstatus & (1<<1)) /* DSI_IRQ */
+ dsi_irq_handler();
+#endif
+
+ return IRQ_HANDLED;
+}
+
+static int _omap_dss_wait_reset(void)
+{
+ unsigned timeout = 1000;
+
+ while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
+ udelay(1);
+ if (!--timeout) {
+ DSSERR("soft reset failed\n");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static int _omap_dss_reset(void)
+{
+ /* Soft reset */
+ REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1);
+ return _omap_dss_wait_reset();
+}
+
+void dss_set_venc_output(enum omap_dss_venc_type type)
+{
+ int l = 0;
+
+ if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
+ l = 0;
+ else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
+ l = 1;
+ else
+ BUG();
+
+ /* venc out selection. 0 = comp, 1 = svideo */
+ REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
+}
+
+void dss_set_dac_pwrdn_bgz(bool enable)
+{
+ REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */
+}
+
+int dss_init(bool skip_init)
+{
+ int r;
+ u32 rev;
+
+ dss.base = ioremap(DSS_BASE, DSS_SZ_REGS);
+ if (!dss.base) {
+ DSSERR("can't ioremap DSS\n");
+ r = -ENOMEM;
+ goto fail0;
+ }
+
+ if (!skip_init) {
+ /* disable LCD and DIGIT output. This seems to fix the synclost
+ * problem that we get, if the bootloader starts the DSS and
+ * the kernel resets it */
+ omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440);
+
+ /* We need to wait here a bit, otherwise we sometimes start to
+ * get synclost errors, and after that only power cycle will
+ * restore DSS functionality. I have no idea why this happens.
+ * And we have to wait _before_ resetting the DSS, but after
+ * enabling clocks.
+ */
+ msleep(50);
+
+ _omap_dss_reset();
+ }
+
+ /* autoidle */
+ REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0);
+
+ /* Select DPLL */
+ REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
+
+#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 */
+ REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
+#endif
+
+ r = request_irq(INT_24XX_DSS_IRQ,
+ cpu_is_omap24xx()
+ ? dss_irq_handler_omap2
+ : dss_irq_handler_omap3,
+ 0, "OMAP DSS", NULL);
+
+ if (r < 0) {
+ DSSERR("omap2 dss: request_irq failed\n");
+ goto fail1;
+ }
+
+ if (cpu_is_omap34xx()) {
+ dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
+ if (IS_ERR(dss.dpll4_m4_ck)) {
+ DSSERR("Failed to get dpll4_m4_ck\n");
+ r = PTR_ERR(dss.dpll4_m4_ck);
+ goto fail2;
+ }
+ }
+
+ dss_save_context();
+
+ rev = dss_read_reg(DSS_REVISION);
+ printk(KERN_INFO "OMAP DSS rev %d.%d\n",
+ FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+ return 0;
+
+fail2:
+ free_irq(INT_24XX_DSS_IRQ, NULL);
+fail1:
+ iounmap(dss.base);
+fail0:
+ return r;
+}
+
+void dss_exit(void)
+{
+ if (cpu_is_omap34xx())
+ clk_put(dss.dpll4_m4_ck);
+
+ free_irq(INT_24XX_DSS_IRQ, NULL);
+
+ iounmap(dss.base);
+}
+
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
new file mode 100644
index 00000000000..8da5ac42151
--- /dev/null
+++ b/drivers/video/omap2/dss/dss.h
@@ -0,0 +1,370 @@
+/*
+ * linux/drivers/video/omap2/dss/dss.h
+ *
+ * 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/>.
+ */
+
+#ifndef __OMAP2_DSS_H
+#define __OMAP2_DSS_H
+
+#ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT
+#define DEBUG
+#endif
+
+#ifdef DEBUG
+extern unsigned int 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__)
+#endif
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSDBGF(format, ...) \
+ if (dss_debug) \
+ printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME \
+ ": %s(" format ")\n", \
+ __func__, \
+ ## __VA_ARGS__)
+#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, ...)
+#endif
+
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSERR(format, ...) \
+ printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \
+ ## __VA_ARGS__)
+#else
+#define DSSERR(format, ...) \
+ printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__)
+#endif
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSINFO(format, ...) \
+ printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \
+ ## __VA_ARGS__)
+#else
+#define DSSINFO(format, ...) \
+ printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__)
+#endif
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSWARN(format, ...) \
+ printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \
+ ## __VA_ARGS__)
+#else
+#define DSSWARN(format, ...) \
+ printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__)
+#endif
+
+/* OMAP TRM gives bitfields as start:end, where start is the higher bit
+ number. For example 7:0 */
+#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
+#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
+#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
+#define FLD_MOD(orig, val, start, end) \
+ (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
+
+#define DISPC_MAX_FCK 173000000
+
+enum omap_burst_size {
+ OMAP_DSS_BURST_4x32 = 0,
+ OMAP_DSS_BURST_8x32 = 1,
+ OMAP_DSS_BURST_16x32 = 2,
+};
+
+enum omap_parallel_interface_mode {
+ OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */
+ OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */
+ OMAP_DSS_PARALLELMODE_DSI,
+};
+
+enum dss_clock {
+ DSS_CLK_ICK = 1 << 0,
+ DSS_CLK_FCK1 = 1 << 1,
+ DSS_CLK_FCK2 = 1 << 2,
+ DSS_CLK_54M = 1 << 3,
+ DSS_CLK_96M = 1 << 4,
+};
+
+struct dss_clock_info {
+ /* rates that we get with dividers below */
+ unsigned long fck;
+
+ /* dividers */
+ u16 fck_div;
+};
+
+struct dispc_clock_info {
+ /* rates that we get with dividers below */
+ unsigned long lck;
+ unsigned long pck;
+
+ /* dividers */
+ u16 lck_div;
+ u16 pck_div;
+};
+
+struct dsi_clock_info {
+ /* rates that we get with dividers below */
+ unsigned long fint;
+ unsigned long clkin4ddr;
+ unsigned long clkin;
+ unsigned long dsi1_pll_fclk;
+ unsigned long dsi2_pll_fclk;
+
+ unsigned long lp_clk;
+
+ /* dividers */
+ u16 regn;
+ u16 regm;
+ u16 regm3;
+ u16 regm4;
+
+ u16 lp_clk_div;
+
+ u8 highfreq;
+ bool use_dss2_fck;
+};
+
+struct seq_file;
+struct platform_device;
+
+/* core */
+void dss_clk_enable(enum dss_clock clks);
+void dss_clk_disable(enum dss_clock clks);
+unsigned long dss_clk_get_rate(enum dss_clock clk);
+int dss_need_ctx_restore(void);
+void dss_dump_clocks(struct seq_file *s);
+struct bus_type *dss_get_bus(void);
+
+/* display */
+int dss_suspend_all_devices(void);
+int dss_resume_all_devices(void);
+void dss_disable_all_devices(void);
+
+void dss_init_device(struct platform_device *pdev,
+ struct omap_dss_device *dssdev);
+void dss_uninit_device(struct platform_device *pdev,
+ struct omap_dss_device *dssdev);
+bool dss_use_replication(struct omap_dss_device *dssdev,
+ enum omap_color_mode mode);
+void default_get_overlay_fifo_thresholds(enum omap_plane plane,
+ u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 *fifo_low, u32 *fifo_high);
+
+/* manager */
+int dss_init_overlay_managers(struct platform_device *pdev);
+void dss_uninit_overlay_managers(struct platform_device *pdev);
+int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl);
+void dss_setup_partial_planes(struct omap_dss_device *dssdev,
+ u16 *x, u16 *y, u16 *w, u16 *h);
+void dss_start_update(struct omap_dss_device *dssdev);
+
+/* overlay */
+void dss_init_overlays(struct platform_device *pdev);
+void dss_uninit_overlays(struct platform_device *pdev);
+int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev);
+void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr);
+#ifdef L4_EXAMPLE
+void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr);
+#endif
+void dss_recheck_connections(struct omap_dss_device *dssdev, bool force);
+
+/* DSS */
+int dss_init(bool skip_init);
+void dss_exit(void);
+
+void dss_save_context(void);
+void dss_restore_context(void);
+
+void dss_dump_regs(struct seq_file *s);
+
+void dss_sdi_init(u8 datapairs);
+int dss_sdi_enable(void);
+void dss_sdi_disable(void);
+
+void dss_select_clk_source(bool dsi, bool dispc);
+int dss_get_dsi_clk_source(void);
+int dss_get_dispc_clk_source(void);
+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_get_clock_div(struct dss_clock_info *cinfo);
+int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
+ struct dss_clock_info *dss_cinfo,
+ struct dispc_clock_info *dispc_cinfo);
+
+/* SDI */
+int sdi_init(bool skip_init);
+void sdi_exit(void);
+int sdi_init_display(struct omap_dss_device *display);
+
+/* DSI */
+int dsi_init(struct platform_device *pdev);
+void dsi_exit(void);
+
+void dsi_dump_clocks(struct seq_file *s);
+void dsi_dump_regs(struct seq_file *s);
+
+void dsi_save_context(void);
+void dsi_restore_context(void);
+
+int dsi_init_display(struct omap_dss_device *display);
+void dsi_irq_handler(void);
+unsigned long dsi_get_dsi1_pll_rate(void);
+int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo);
+int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck,
+ struct dsi_clock_info *cinfo,
+ struct dispc_clock_info *dispc_cinfo);
+int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk,
+ bool enable_hsdiv);
+void dsi_pll_uninit(void);
+void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
+ u32 fifo_size, enum omap_burst_size *burst_size,
+ u32 *fifo_low, u32 *fifo_high);
+
+/* DPI */
+int dpi_init(void);
+void dpi_exit(void);
+int dpi_init_display(struct omap_dss_device *dssdev);
+
+/* DISPC */
+int dispc_init(void);
+void dispc_exit(void);
+void dispc_dump_clocks(struct seq_file *s);
+void dispc_dump_regs(struct seq_file *s);
+void dispc_irq_handler(void);
+void dispc_fake_vsync_irq(void);
+
+void dispc_save_context(void);
+void dispc_restore_context(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_fifohandcheck(bool enable);
+
+void dispc_set_lcd_size(u16 width, u16 height);
+void dispc_set_digit_size(u16 width, u16 height);
+u32 dispc_get_plane_fifo_size(enum omap_plane plane);
+void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high);
+void dispc_enable_fifomerge(bool enable);
+void dispc_set_burst_size(enum omap_plane plane,
+ enum omap_burst_size burst_size);
+
+void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);
+void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr);
+void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y);
+void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height);
+void dispc_set_channel_out(enum omap_plane plane,
+ enum omap_channel channel_out);
+
+int dispc_setup_plane(enum omap_plane plane,
+ u32 paddr, u16 screen_width,
+ u16 pos_x, u16 pos_y,
+ u16 width, u16 height,
+ u16 out_width, u16 out_height,
+ enum omap_color_mode color_mode,
+ bool ilace,
+ enum omap_dss_rotation_type rotation_type,
+ u8 rotation, bool mirror,
+ u8 global_alpha);
+
+bool dispc_go_busy(enum omap_channel channel);
+void dispc_go(enum omap_channel channel);
+void dispc_enable_lcd_out(bool enable);
+void dispc_enable_digit_out(bool enable);
+int dispc_enable_plane(enum omap_plane plane, bool enable);
+void dispc_enable_replication(enum omap_plane plane, bool enable);
+
+void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode);
+void dispc_set_tft_data_lines(u8 data_lines);
+void dispc_set_lcd_display_type(enum omap_lcd_display_type type);
+void dispc_set_loadmode(enum omap_dss_load_mode mode);
+
+void dispc_set_default_color(enum omap_channel channel, u32 color);
+u32 dispc_get_default_color(enum omap_channel channel);
+void dispc_set_trans_key(enum omap_channel ch,
+ enum omap_dss_trans_key_type type,
+ u32 trans_key);
+void dispc_get_trans_key(enum omap_channel ch,
+ enum omap_dss_trans_key_type *type,
+ u32 *trans_key);
+void dispc_enable_trans_key(enum omap_channel ch, bool enable);
+void dispc_enable_alpha_blending(enum omap_channel ch, bool enable);
+bool dispc_trans_key_enabled(enum omap_channel ch);
+bool dispc_alpha_blending_enabled(enum omap_channel ch);
+
+bool dispc_lcd_timings_ok(struct omap_video_timings *timings);
+void dispc_set_lcd_timings(struct omap_video_timings *timings);
+unsigned long dispc_fclk_rate(void);
+unsigned long dispc_lclk_rate(void);
+unsigned long dispc_pclk_rate(void);
+void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb);
+void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+ struct dispc_clock_info *cinfo);
+int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
+ struct dispc_clock_info *cinfo);
+int dispc_set_clock_div(struct dispc_clock_info *cinfo);
+int dispc_get_clock_div(struct dispc_clock_info *cinfo);
+
+
+/* VENC */
+int venc_init(struct platform_device *pdev);
+void venc_exit(void);
+void venc_dump_regs(struct seq_file *s);
+int venc_init_display(struct omap_dss_device *display);
+
+/* RFBI */
+int rfbi_init(void);
+void rfbi_exit(void);
+void rfbi_dump_regs(struct seq_file *s);
+
+int rfbi_configure(int rfbi_module, int bpp, int lines);
+void rfbi_enable_rfbi(bool enable);
+void rfbi_transfer_area(u16 width, u16 height,
+ void (callback)(void *data), void *data);
+void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t);
+unsigned long rfbi_get_max_tx_rate(void);
+int rfbi_init_display(struct omap_dss_device *display);
+
+#endif
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
new file mode 100644
index 00000000000..27d9c465c85
--- /dev/null
+++ b/drivers/video/omap2/dss/manager.c
@@ -0,0 +1,1487 @@
+/*
+ * linux/drivers/video/omap2/dss/manager.c
+ *
+ * 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 "MANAGER"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+static int num_managers;
+static struct list_head manager_list;
+
+static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name);
+}
+
+static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ mgr->device ? mgr->device->name : "<none>");
+}
+
+static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ int r = 0;
+ size_t len = size;
+ struct omap_dss_device *dssdev = NULL;
+
+ int match(struct omap_dss_device *dssdev, void *data)
+ {
+ const char *str = data;
+ return sysfs_streq(dssdev->name, str);
+ }
+
+ if (buf[size-1] == '\n')
+ --len;
+
+ if (len > 0)
+ dssdev = omap_dss_find_device((void *)buf, match);
+
+ if (len > 0 && dssdev == NULL)
+ return -EINVAL;
+
+ if (dssdev)
+ DSSDBG("display %s found\n", dssdev->name);
+
+ if (mgr->device) {
+ r = mgr->unset_device(mgr);
+ if (r) {
+ DSSERR("failed to unset display\n");
+ goto put_device;
+ }
+ }
+
+ if (dssdev) {
+ r = mgr->set_device(mgr, dssdev);
+ if (r) {
+ DSSERR("failed to set manager\n");
+ goto put_device;
+ }
+
+ r = mgr->apply(mgr);
+ if (r) {
+ DSSERR("failed to apply dispc config\n");
+ goto put_device;
+ }
+ }
+
+put_device:
+ if (dssdev)
+ omap_dss_put_device(dssdev);
+
+ return r ? r : size;
+}
+
+static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color);
+}
+
+static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ u32 color;
+ int r;
+
+ if (sscanf(buf, "%d", &color) != 1)
+ return -EINVAL;
+
+ mgr->get_manager_info(mgr, &info);
+
+ info.default_color = color;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static const char *trans_key_type_str[] = {
+ "gfx-destination",
+ "video-source",
+};
+
+static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ enum omap_dss_trans_key_type key_type;
+
+ key_type = mgr->info.trans_key_type;
+ BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str));
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]);
+}
+
+static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ enum omap_dss_trans_key_type key_type;
+ struct omap_overlay_manager_info info;
+ int r;
+
+ for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+ key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
+ if (sysfs_streq(buf, trans_key_type_str[key_type]))
+ break;
+ }
+
+ if (key_type == ARRAY_SIZE(trans_key_type_str))
+ return -EINVAL;
+
+ mgr->get_manager_info(mgr, &info);
+
+ info.trans_key_type = key_type;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key);
+}
+
+static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ u32 key_value;
+ int r;
+
+ if (sscanf(buf, "%d", &key_value) != 1)
+ return -EINVAL;
+
+ mgr->get_manager_info(mgr, &info);
+
+ info.trans_key = key_value;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled);
+}
+
+static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ int enable;
+ int r;
+
+ if (sscanf(buf, "%d", &enable) != 1)
+ return -EINVAL;
+
+ mgr->get_manager_info(mgr, &info);
+
+ info.trans_enabled = enable ? true : false;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t manager_alpha_blending_enabled_show(
+ struct omap_overlay_manager *mgr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled);
+}
+
+static ssize_t manager_alpha_blending_enabled_store(
+ struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ int enable;
+ int r;
+
+ if (sscanf(buf, "%d", &enable) != 1)
+ return -EINVAL;
+
+ mgr->get_manager_info(mgr, &info);
+
+ info.alpha_enabled = enable ? true : false;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
+struct manager_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct omap_overlay_manager *, char *);
+ ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
+};
+
+#define MANAGER_ATTR(_name, _mode, _show, _store) \
+ struct manager_attribute manager_attr_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
+static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
+ manager_display_show, manager_display_store);
+static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
+ manager_default_color_show, manager_default_color_store);
+static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
+ manager_trans_key_type_show, manager_trans_key_type_store);
+static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
+ manager_trans_key_value_show, manager_trans_key_value_store);
+static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
+ manager_trans_key_enabled_show,
+ manager_trans_key_enabled_store);
+static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
+ manager_alpha_blending_enabled_show,
+ manager_alpha_blending_enabled_store);
+
+
+static struct attribute *manager_sysfs_attrs[] = {
+ &manager_attr_name.attr,
+ &manager_attr_display.attr,
+ &manager_attr_default_color.attr,
+ &manager_attr_trans_key_type.attr,
+ &manager_attr_trans_key_value.attr,
+ &manager_attr_trans_key_enabled.attr,
+ &manager_attr_alpha_blending_enabled.attr,
+ NULL
+};
+
+static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct omap_overlay_manager *manager;
+ struct manager_attribute *manager_attr;
+
+ manager = container_of(kobj, struct omap_overlay_manager, kobj);
+ manager_attr = container_of(attr, struct manager_attribute, attr);
+
+ if (!manager_attr->show)
+ return -ENOENT;
+
+ return manager_attr->show(manager, buf);
+}
+
+static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager *manager;
+ struct manager_attribute *manager_attr;
+
+ manager = container_of(kobj, struct omap_overlay_manager, kobj);
+ manager_attr = container_of(attr, struct manager_attribute, attr);
+
+ if (!manager_attr->store)
+ return -ENOENT;
+
+ return manager_attr->store(manager, buf, size);
+}
+
+static struct sysfs_ops manager_sysfs_ops = {
+ .show = manager_attr_show,
+ .store = manager_attr_store,
+};
+
+static struct kobj_type manager_ktype = {
+ .sysfs_ops = &manager_sysfs_ops,
+ .default_attrs = manager_sysfs_attrs,
+};
+
+/*
+ * We have 4 levels of cache for the dispc settings. First two are in SW and
+ * the latter two in HW.
+ *
+ * +--------------------+
+ * |overlay/manager_info|
+ * +--------------------+
+ * v
+ * apply()
+ * v
+ * +--------------------+
+ * | dss_cache |
+ * +--------------------+
+ * v
+ * configure()
+ * v
+ * +--------------------+
+ * | shadow registers |
+ * +--------------------+
+ * v
+ * VFP or lcd/digit_enable
+ * v
+ * +--------------------+
+ * | registers |
+ * +--------------------+
+ */
+
+struct overlay_cache_data {
+ /* If true, cache changed, but not written to shadow registers. Set
+ * in apply(), cleared when registers written. */
+ bool dirty;
+ /* If true, shadow registers contain changed values not yet in real
+ * registers. Set when writing to shadow registers, cleared at
+ * VSYNC/EVSYNC */
+ bool shadow_dirty;
+
+ bool enabled;
+
+ u32 paddr;
+ void __iomem *vaddr;
+ u16 screen_width;
+ u16 width;
+ u16 height;
+ enum omap_color_mode color_mode;
+ u8 rotation;
+ enum omap_dss_rotation_type rotation_type;
+ bool mirror;
+
+ u16 pos_x;
+ u16 pos_y;
+ u16 out_width; /* if 0, out_width == width */
+ u16 out_height; /* if 0, out_height == height */
+ u8 global_alpha;
+
+ enum omap_channel channel;
+ bool replication;
+ bool ilace;
+
+ enum omap_burst_size burst_size;
+ u32 fifo_low;
+ u32 fifo_high;
+
+ bool manual_update;
+};
+
+struct manager_cache_data {
+ /* If true, cache changed, but not written to shadow registers. Set
+ * in apply(), cleared when registers written. */
+ bool dirty;
+ /* If true, shadow registers contain changed values not yet in real
+ * registers. Set when writing to shadow registers, cleared at
+ * VSYNC/EVSYNC */
+ bool shadow_dirty;
+
+ u32 default_color;
+
+ enum omap_dss_trans_key_type trans_key_type;
+ u32 trans_key;
+ bool trans_enabled;
+
+ bool alpha_enabled;
+
+ bool manual_upd_display;
+ bool manual_update;
+ bool do_manual_update;
+
+ /* manual update region */
+ u16 x, y, w, h;
+};
+
+static struct {
+ spinlock_t lock;
+ struct overlay_cache_data overlay_cache[3];
+ struct manager_cache_data manager_cache[2];
+
+ bool irq_enabled;
+} dss_cache;
+
+
+
+static int omap_dss_set_device(struct omap_overlay_manager *mgr,
+ struct omap_dss_device *dssdev)
+{
+ int i;
+ int r;
+
+ if (dssdev->manager) {
+ DSSERR("display '%s' already has a manager '%s'\n",
+ dssdev->name, dssdev->manager->name);
+ return -EINVAL;
+ }
+
+ if ((mgr->supported_displays & dssdev->type) == 0) {
+ DSSERR("display '%s' does not support manager '%s'\n",
+ dssdev->name, mgr->name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < mgr->num_overlays; i++) {
+ struct omap_overlay *ovl = mgr->overlays[i];
+
+ if (ovl->manager != mgr || !ovl->info.enabled)
+ continue;
+
+ r = dss_check_overlay(ovl, dssdev);
+ if (r)
+ return r;
+ }
+
+ dssdev->manager = mgr;
+ mgr->device = dssdev;
+ mgr->device_changed = true;
+
+ return 0;
+}
+
+static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
+{
+ if (!mgr->device) {
+ DSSERR("failed to unset display, display not set.\n");
+ return -EINVAL;
+ }
+
+ mgr->device->manager = NULL;
+ mgr->device = NULL;
+ mgr->device_changed = true;
+
+ return 0;
+}
+
+static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
+{
+ unsigned long timeout = msecs_to_jiffies(500);
+ struct manager_cache_data *mc;
+ enum omap_channel channel;
+ u32 irq;
+ int r;
+ int i;
+
+ if (!mgr->device)
+ return 0;
+
+ if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
+ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
+ channel = OMAP_DSS_CHANNEL_DIGIT;
+ } else {
+ if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+ enum omap_dss_update_mode mode;
+ mode = mgr->device->get_update_mode(mgr->device);
+ if (mode != OMAP_DSS_UPDATE_AUTO)
+ return 0;
+
+ irq = DISPC_IRQ_FRAMEDONE;
+ } else {
+ irq = DISPC_IRQ_VSYNC;
+ }
+ channel = OMAP_DSS_CHANNEL_LCD;
+ }
+
+ mc = &dss_cache.manager_cache[mgr->id];
+ i = 0;
+ while (1) {
+ unsigned long flags;
+ bool shadow_dirty, dirty;
+
+ spin_lock_irqsave(&dss_cache.lock, flags);
+ dirty = mc->dirty;
+ shadow_dirty = mc->shadow_dirty;
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+ if (!dirty && !shadow_dirty) {
+ r = 0;
+ break;
+ }
+
+ /* 4 iterations is the worst case:
+ * 1 - initial iteration, dirty = true (between VFP and VSYNC)
+ * 2 - first VSYNC, dirty = true
+ * 3 - dirty = false, shadow_dirty = true
+ * 4 - shadow_dirty = false */
+ if (i++ == 3) {
+ DSSERR("mgr(%d)->wait_for_go() not finishing\n",
+ mgr->id);
+ r = 0;
+ break;
+ }
+
+ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+ if (r == -ERESTARTSYS)
+ break;
+
+ if (r) {
+ DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
+ break;
+ }
+ }
+
+ return r;
+}
+
+int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
+{
+ unsigned long timeout = msecs_to_jiffies(500);
+ enum omap_channel channel;
+ struct overlay_cache_data *oc;
+ struct omap_dss_device *dssdev;
+ u32 irq;
+ int r;
+ int i;
+
+ if (!ovl->manager || !ovl->manager->device)
+ return 0;
+
+ dssdev = ovl->manager->device;
+
+ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
+ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
+ channel = OMAP_DSS_CHANNEL_DIGIT;
+ } else {
+ if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+ enum omap_dss_update_mode mode;
+ mode = dssdev->get_update_mode(dssdev);
+ if (mode != OMAP_DSS_UPDATE_AUTO)
+ return 0;
+
+ irq = DISPC_IRQ_FRAMEDONE;
+ } else {
+ irq = DISPC_IRQ_VSYNC;
+ }
+ channel = OMAP_DSS_CHANNEL_LCD;
+ }
+
+ oc = &dss_cache.overlay_cache[ovl->id];
+ i = 0;
+ while (1) {
+ unsigned long flags;
+ bool shadow_dirty, dirty;
+
+ spin_lock_irqsave(&dss_cache.lock, flags);
+ dirty = oc->dirty;
+ shadow_dirty = oc->shadow_dirty;
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+ if (!dirty && !shadow_dirty) {
+ r = 0;
+ break;
+ }
+
+ /* 4 iterations is the worst case:
+ * 1 - initial iteration, dirty = true (between VFP and VSYNC)
+ * 2 - first VSYNC, dirty = true
+ * 3 - dirty = false, shadow_dirty = true
+ * 4 - shadow_dirty = false */
+ if (i++ == 3) {
+ DSSERR("ovl(%d)->wait_for_go() not finishing\n",
+ ovl->id);
+ r = 0;
+ break;
+ }
+
+ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+ if (r == -ERESTARTSYS)
+ break;
+
+ if (r) {
+ DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
+ break;
+ }
+ }
+
+ return r;
+}
+
+static int overlay_enabled(struct omap_overlay *ovl)
+{
+ return ovl->info.enabled && ovl->manager && ovl->manager->device;
+}
+
+/* Is rect1 a subset of rect2? */
+static bool rectangle_subset(int x1, int y1, int w1, int h1,
+ int x2, int y2, int w2, int h2)
+{
+ if (x1 < x2 || y1 < y2)
+ return false;
+
+ if (x1 + w1 > x2 + w2)
+ return false;
+
+ if (y1 + h1 > y2 + h2)
+ return false;
+
+ return true;
+}
+
+/* Do rect1 and rect2 overlap? */
+static bool rectangle_intersects(int x1, int y1, int w1, int h1,
+ int x2, int y2, int w2, int h2)
+{
+ if (x1 >= x2 + w2)
+ return false;
+
+ if (x2 >= x1 + w1)
+ return false;
+
+ if (y1 >= y2 + h2)
+ return false;
+
+ if (y2 >= y1 + h1)
+ return false;
+
+ return true;
+}
+
+static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
+{
+ if (oc->out_width != 0 && oc->width != oc->out_width)
+ return true;
+
+ if (oc->out_height != 0 && oc->height != oc->out_height)
+ return true;
+
+ return false;
+}
+
+static int configure_overlay(enum omap_plane plane)
+{
+ struct overlay_cache_data *c;
+ struct manager_cache_data *mc;
+ u16 outw, outh;
+ u16 x, y, w, h;
+ u32 paddr;
+ int r;
+
+ DSSDBGF("%d", plane);
+
+ c = &dss_cache.overlay_cache[plane];
+
+ if (!c->enabled) {
+ dispc_enable_plane(plane, 0);
+ return 0;
+ }
+
+ mc = &dss_cache.manager_cache[c->channel];
+
+ x = c->pos_x;
+ y = c->pos_y;
+ w = c->width;
+ h = c->height;
+ outw = c->out_width == 0 ? c->width : c->out_width;
+ outh = c->out_height == 0 ? c->height : c->out_height;
+ paddr = c->paddr;
+
+ if (c->manual_update && mc->do_manual_update) {
+ unsigned bpp;
+ /* If the overlay is outside the update region, disable it */
+ if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
+ x, y, outw, outh)) {
+ dispc_enable_plane(plane, 0);
+ return 0;
+ }
+
+ switch (c->color_mode) {
+ case OMAP_DSS_COLOR_RGB16:
+ case OMAP_DSS_COLOR_ARGB16:
+ case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_UYVY:
+ bpp = 16;
+ break;
+
+ case OMAP_DSS_COLOR_RGB24P:
+ bpp = 24;
+ break;
+
+ case OMAP_DSS_COLOR_RGB24U:
+ case OMAP_DSS_COLOR_ARGB32:
+ case OMAP_DSS_COLOR_RGBA32:
+ case OMAP_DSS_COLOR_RGBX32:
+ bpp = 32;
+ break;
+
+ default:
+ BUG();
+ }
+
+ if (dispc_is_overlay_scaled(c)) {
+ /* If the overlay is scaled, the update area has
+ * already been enlarged to cover the whole overlay. We
+ * only need to adjust x/y here */
+ x = c->pos_x - mc->x;
+ y = c->pos_y - mc->y;
+ } else {
+ if (mc->x > c->pos_x) {
+ x = 0;
+ w -= (mc->x - c->pos_x);
+ paddr += (mc->x - c->pos_x) * bpp / 8;
+ } else {
+ x = c->pos_x - mc->x;
+ }
+
+ if (mc->y > c->pos_y) {
+ y = 0;
+ h -= (mc->y - c->pos_y);
+ paddr += (mc->y - c->pos_y) * c->screen_width *
+ bpp / 8;
+ } else {
+ y = c->pos_y - mc->y;
+ }
+
+ if (mc->w < (x+w))
+ w -= (x+w) - (mc->w);
+
+ if (mc->h < (y+h))
+ h -= (y+h) - (mc->h);
+
+ outw = w;
+ outh = h;
+ }
+ }
+
+ r = dispc_setup_plane(plane,
+ paddr,
+ c->screen_width,
+ x, y,
+ w, h,
+ outw, outh,
+ c->color_mode,
+ c->ilace,
+ c->rotation_type,
+ c->rotation,
+ c->mirror,
+ c->global_alpha);
+
+ if (r) {
+ /* this shouldn't happen */
+ DSSERR("dispc_setup_plane failed for ovl %d\n", plane);
+ dispc_enable_plane(plane, 0);
+ return r;
+ }
+
+ dispc_enable_replication(plane, c->replication);
+
+ dispc_set_burst_size(plane, c->burst_size);
+ dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high);
+
+ dispc_enable_plane(plane, 1);
+
+ return 0;
+}
+
+static void configure_manager(enum omap_channel channel)
+{
+ struct manager_cache_data *c;
+
+ DSSDBGF("%d", channel);
+
+ c = &dss_cache.manager_cache[channel];
+
+ dispc_set_trans_key(channel, c->trans_key_type, c->trans_key);
+ dispc_enable_trans_key(channel, c->trans_enabled);
+ dispc_enable_alpha_blending(channel, c->alpha_enabled);
+}
+
+/* configure_dispc() tries to write values from cache to shadow registers.
+ * It writes only to those managers/overlays that are not busy.
+ * returns 0 if everything could be written to shadow registers.
+ * returns 1 if not everything could be written to shadow registers. */
+static int configure_dispc(void)
+{
+ struct overlay_cache_data *oc;
+ struct manager_cache_data *mc;
+ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+ int i;
+ int r;
+ bool mgr_busy[2];
+ bool mgr_go[2];
+ bool busy;
+
+ r = 0;
+ busy = false;
+
+ mgr_busy[0] = dispc_go_busy(0);
+ mgr_busy[1] = dispc_go_busy(1);
+ mgr_go[0] = false;
+ mgr_go[1] = false;
+
+ /* Commit overlay settings */
+ for (i = 0; i < num_ovls; ++i) {
+ oc = &dss_cache.overlay_cache[i];
+ mc = &dss_cache.manager_cache[oc->channel];
+
+ if (!oc->dirty)
+ continue;
+
+ if (oc->manual_update && !mc->do_manual_update)
+ continue;
+
+ if (mgr_busy[oc->channel]) {
+ busy = true;
+ continue;
+ }
+
+ r = configure_overlay(i);
+ if (r)
+ DSSERR("configure_overlay %d failed\n", i);
+
+ oc->dirty = false;
+ oc->shadow_dirty = true;
+ mgr_go[oc->channel] = true;
+ }
+
+ /* Commit manager settings */
+ for (i = 0; i < num_mgrs; ++i) {
+ mc = &dss_cache.manager_cache[i];
+
+ if (!mc->dirty)
+ continue;
+
+ if (mc->manual_update && !mc->do_manual_update)
+ continue;
+
+ if (mgr_busy[i]) {
+ busy = true;
+ continue;
+ }
+
+ configure_manager(i);
+ mc->dirty = false;
+ mc->shadow_dirty = true;
+ mgr_go[i] = true;
+ }
+
+ /* set GO */
+ for (i = 0; i < num_mgrs; ++i) {
+ mc = &dss_cache.manager_cache[i];
+
+ if (!mgr_go[i])
+ continue;
+
+ /* We don't need GO with manual update display. LCD iface will
+ * always be turned off after frame, and new settings will be
+ * taken in to use at next update */
+ if (!mc->manual_upd_display)
+ dispc_go(i);
+ }
+
+ if (busy)
+ r = 1;
+ else
+ r = 0;
+
+ return r;
+}
+
+/* Configure dispc for partial update. Return possibly modified update
+ * area */
+void dss_setup_partial_planes(struct omap_dss_device *dssdev,
+ u16 *xi, u16 *yi, u16 *wi, u16 *hi)
+{
+ struct overlay_cache_data *oc;
+ struct manager_cache_data *mc;
+ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+ struct omap_overlay_manager *mgr;
+ int i;
+ u16 x, y, w, h;
+ unsigned long flags;
+
+ x = *xi;
+ y = *yi;
+ w = *wi;
+ h = *hi;
+
+ DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
+ *xi, *yi, *wi, *hi);
+
+ mgr = dssdev->manager;
+
+ if (!mgr) {
+ DSSDBG("no manager\n");
+ return;
+ }
+
+ spin_lock_irqsave(&dss_cache.lock, flags);
+
+ /* We need to show the whole overlay if it is scaled. So look for
+ * those, and make the update area larger if found.
+ * Also mark the overlay cache dirty */
+ for (i = 0; i < num_ovls; ++i) {
+ unsigned x1, y1, x2, y2;
+ unsigned outw, outh;
+
+ oc = &dss_cache.overlay_cache[i];
+
+ if (oc->channel != mgr->id)
+ continue;
+
+ oc->dirty = true;
+
+ if (!oc->enabled)
+ continue;
+
+ if (!dispc_is_overlay_scaled(oc))
+ continue;
+
+ outw = oc->out_width == 0 ? oc->width : oc->out_width;
+ outh = oc->out_height == 0 ? oc->height : oc->out_height;
+
+ /* is the overlay outside the update region? */
+ if (!rectangle_intersects(x, y, w, h,
+ oc->pos_x, oc->pos_y,
+ outw, outh))
+ continue;
+
+ /* if the overlay totally inside the update region? */
+ if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh,
+ x, y, w, h))
+ continue;
+
+ if (x > oc->pos_x)
+ x1 = oc->pos_x;
+ else
+ x1 = x;
+
+ if (y > oc->pos_y)
+ y1 = oc->pos_y;
+ else
+ y1 = y;
+
+ if ((x + w) < (oc->pos_x + outw))
+ x2 = oc->pos_x + outw;
+ else
+ x2 = x + w;
+
+ if ((y + h) < (oc->pos_y + outh))
+ y2 = oc->pos_y + outh;
+ else
+ y2 = y + h;
+
+ x = x1;
+ y = y1;
+ w = x2 - x1;
+ h = y2 - y1;
+
+ DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n",
+ i, x, y, w, h);
+ }
+
+ mc = &dss_cache.manager_cache[mgr->id];
+ mc->do_manual_update = true;
+ mc->x = x;
+ mc->y = y;
+ mc->w = w;
+ mc->h = h;
+
+ configure_dispc();
+
+ mc->do_manual_update = false;
+
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+ *xi = x;
+ *yi = y;
+ *wi = w;
+ *hi = h;
+}
+
+void dss_start_update(struct omap_dss_device *dssdev)
+{
+ struct manager_cache_data *mc;
+ struct overlay_cache_data *oc;
+ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+ struct omap_overlay_manager *mgr;
+ int i;
+
+ mgr = dssdev->manager;
+
+ for (i = 0; i < num_ovls; ++i) {
+ oc = &dss_cache.overlay_cache[i];
+ if (oc->channel != mgr->id)
+ continue;
+
+ oc->shadow_dirty = false;
+ }
+
+ for (i = 0; i < num_mgrs; ++i) {
+ mc = &dss_cache.manager_cache[i];
+ if (mgr->id != i)
+ continue;
+
+ mc->shadow_dirty = false;
+ }
+
+ dispc_enable_lcd_out(1);
+}
+
+static void dss_apply_irq_handler(void *data, u32 mask)
+{
+ struct manager_cache_data *mc;
+ struct overlay_cache_data *oc;
+ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+ int i, r;
+ bool mgr_busy[2];
+
+ mgr_busy[0] = dispc_go_busy(0);
+ mgr_busy[1] = dispc_go_busy(1);
+
+ spin_lock(&dss_cache.lock);
+
+ for (i = 0; i < num_ovls; ++i) {
+ oc = &dss_cache.overlay_cache[i];
+ if (!mgr_busy[oc->channel])
+ oc->shadow_dirty = false;
+ }
+
+ for (i = 0; i < num_mgrs; ++i) {
+ mc = &dss_cache.manager_cache[i];
+ if (!mgr_busy[i])
+ mc->shadow_dirty = false;
+ }
+
+ r = configure_dispc();
+ if (r == 1)
+ goto end;
+
+ /* re-read busy flags */
+ mgr_busy[0] = dispc_go_busy(0);
+ mgr_busy[1] = dispc_go_busy(1);
+
+ /* keep running as long as there are busy managers, so that
+ * we can collect overlay-applied information */
+ for (i = 0; i < num_mgrs; ++i) {
+ if (mgr_busy[i])
+ goto end;
+ }
+
+ omap_dispc_unregister_isr(dss_apply_irq_handler, NULL,
+ DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
+ DISPC_IRQ_EVSYNC_EVEN);
+ dss_cache.irq_enabled = false;
+
+end:
+ spin_unlock(&dss_cache.lock);
+}
+
+static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
+{
+ struct overlay_cache_data *oc;
+ struct manager_cache_data *mc;
+ int i;
+ struct omap_overlay *ovl;
+ int num_planes_enabled = 0;
+ bool use_fifomerge;
+ unsigned long flags;
+ int r;
+
+ DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
+
+ spin_lock_irqsave(&dss_cache.lock, flags);
+
+ /* Configure overlays */
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_dss_device *dssdev;
+
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ oc = &dss_cache.overlay_cache[ovl->id];
+
+ if (!overlay_enabled(ovl)) {
+ if (oc->enabled) {
+ oc->enabled = false;
+ oc->dirty = true;
+ }
+ continue;
+ }
+
+ if (!ovl->info_dirty) {
+ if (oc->enabled)
+ ++num_planes_enabled;
+ continue;
+ }
+
+ dssdev = ovl->manager->device;
+
+ if (dss_check_overlay(ovl, dssdev)) {
+ if (oc->enabled) {
+ oc->enabled = false;
+ oc->dirty = true;
+ }
+ continue;
+ }
+
+ ovl->info_dirty = false;
+ oc->dirty = true;
+
+ oc->paddr = ovl->info.paddr;
+ oc->vaddr = ovl->info.vaddr;
+ oc->screen_width = ovl->info.screen_width;
+ oc->width = ovl->info.width;
+ oc->height = ovl->info.height;
+ oc->color_mode = ovl->info.color_mode;
+ oc->rotation = ovl->info.rotation;
+ oc->rotation_type = ovl->info.rotation_type;
+ oc->mirror = ovl->info.mirror;
+ oc->pos_x = ovl->info.pos_x;
+ oc->pos_y = ovl->info.pos_y;
+ oc->out_width = ovl->info.out_width;
+ oc->out_height = ovl->info.out_height;
+ oc->global_alpha = ovl->info.global_alpha;
+
+ oc->replication =
+ dss_use_replication(dssdev, ovl->info.color_mode);
+
+ oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
+
+ oc->channel = ovl->manager->id;
+
+ oc->enabled = true;
+
+ oc->manual_update =
+ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+ dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
+
+ ++num_planes_enabled;
+ }
+
+ /* Configure managers */
+ list_for_each_entry(mgr, &manager_list, list) {
+ struct omap_dss_device *dssdev;
+
+ if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC))
+ continue;
+
+ mc = &dss_cache.manager_cache[mgr->id];
+
+ if (mgr->device_changed) {
+ mgr->device_changed = false;
+ mgr->info_dirty = true;
+ }
+
+ if (!mgr->info_dirty)
+ continue;
+
+ if (!mgr->device)
+ continue;
+
+ dssdev = mgr->device;
+
+ mgr->info_dirty = false;
+ mc->dirty = true;
+
+ mc->default_color = mgr->info.default_color;
+ mc->trans_key_type = mgr->info.trans_key_type;
+ mc->trans_key = mgr->info.trans_key;
+ mc->trans_enabled = mgr->info.trans_enabled;
+ mc->alpha_enabled = mgr->info.alpha_enabled;
+
+ mc->manual_upd_display =
+ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+
+ mc->manual_update =
+ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+ dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
+ }
+
+ /* XXX TODO: Try to get fifomerge working. The problem is that it
+ * affects both managers, not individually but at the same time. This
+ * means the change has to be well synchronized. I guess the proper way
+ * is to have a two step process for fifo merge:
+ * fifomerge enable:
+ * 1. disable other planes, leaving one plane enabled
+ * 2. wait until the planes are disabled on HW
+ * 3. config merged fifo thresholds, enable fifomerge
+ * fifomerge disable:
+ * 1. config unmerged fifo thresholds, disable fifomerge
+ * 2. wait until fifo changes are in HW
+ * 3. enable planes
+ */
+ use_fifomerge = false;
+
+ /* Configure overlay fifos */
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_dss_device *dssdev;
+ u32 size;
+
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ oc = &dss_cache.overlay_cache[ovl->id];
+
+ if (!oc->enabled)
+ continue;
+
+ dssdev = ovl->manager->device;
+
+ size = dispc_get_plane_fifo_size(ovl->id);
+ if (use_fifomerge)
+ size *= 3;
+
+ switch (dssdev->type) {
+ case OMAP_DISPLAY_TYPE_DPI:
+ case OMAP_DISPLAY_TYPE_DBI:
+ case OMAP_DISPLAY_TYPE_SDI:
+ case OMAP_DISPLAY_TYPE_VENC:
+ default_get_overlay_fifo_thresholds(ovl->id, size,
+ &oc->burst_size, &oc->fifo_low,
+ &oc->fifo_high);
+ break;
+#ifdef CONFIG_OMAP2_DSS_DSI
+ case OMAP_DISPLAY_TYPE_DSI:
+ dsi_get_overlay_fifo_thresholds(ovl->id, size,
+ &oc->burst_size, &oc->fifo_low,
+ &oc->fifo_high);
+ break;
+#endif
+ default:
+ BUG();
+ }
+ }
+
+ r = 0;
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ if (!dss_cache.irq_enabled) {
+ r = omap_dispc_register_isr(dss_apply_irq_handler, NULL,
+ DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
+ DISPC_IRQ_EVSYNC_EVEN);
+ dss_cache.irq_enabled = true;
+ }
+ configure_dispc();
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+ return r;
+}
+
+static int dss_check_manager(struct omap_overlay_manager *mgr)
+{
+ /* OMAP supports only graphics source transparency color key and alpha
+ * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
+
+ if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
+ mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
+ struct omap_overlay_manager_info *info)
+{
+ int r;
+ struct omap_overlay_manager_info old_info;
+
+ old_info = mgr->info;
+ mgr->info = *info;
+
+ r = dss_check_manager(mgr);
+ if (r) {
+ mgr->info = old_info;
+ return r;
+ }
+
+ mgr->info_dirty = true;
+
+ return 0;
+}
+
+static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
+ struct omap_overlay_manager_info *info)
+{
+ *info = mgr->info;
+}
+
+static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
+{
+ ++num_managers;
+ list_add_tail(&manager->list, &manager_list);
+}
+
+int dss_init_overlay_managers(struct platform_device *pdev)
+{
+ int i, r;
+
+ spin_lock_init(&dss_cache.lock);
+
+ INIT_LIST_HEAD(&manager_list);
+
+ num_managers = 0;
+
+ for (i = 0; i < 2; ++i) {
+ struct omap_overlay_manager *mgr;
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+ BUG_ON(mgr == NULL);
+
+ switch (i) {
+ case 0:
+ mgr->name = "lcd";
+ mgr->id = OMAP_DSS_CHANNEL_LCD;
+ mgr->supported_displays =
+ OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
+ OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI;
+ break;
+ case 1:
+ mgr->name = "tv";
+ mgr->id = OMAP_DSS_CHANNEL_DIGIT;
+ mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC;
+ break;
+ }
+
+ mgr->set_device = &omap_dss_set_device;
+ mgr->unset_device = &omap_dss_unset_device;
+ mgr->apply = &omap_dss_mgr_apply;
+ mgr->set_manager_info = &omap_dss_mgr_set_info;
+ mgr->get_manager_info = &omap_dss_mgr_get_info;
+ mgr->wait_for_go = &dss_mgr_wait_for_go;
+
+ mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC;
+
+ dss_overlay_setup_dispc_manager(mgr);
+
+ omap_dss_add_overlay_manager(mgr);
+
+ r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
+ &pdev->dev.kobj, "manager%d", i);
+
+ if (r) {
+ DSSERR("failed to create sysfs file\n");
+ continue;
+ }
+ }
+
+#ifdef L4_EXAMPLE
+ {
+ int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr)
+ {
+ DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name);
+
+ return 0;
+ }
+
+ struct omap_overlay_manager *mgr;
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+ BUG_ON(mgr == NULL);
+
+ mgr->name = "l4";
+ mgr->supported_displays =
+ OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI;
+
+ mgr->set_device = &omap_dss_set_device;
+ mgr->unset_device = &omap_dss_unset_device;
+ mgr->apply = &omap_dss_mgr_apply_l4;
+ mgr->set_manager_info = &omap_dss_mgr_set_info;
+ mgr->get_manager_info = &omap_dss_mgr_get_info;
+
+ dss_overlay_setup_l4_manager(mgr);
+
+ omap_dss_add_overlay_manager(mgr);
+
+ r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
+ &pdev->dev.kobj, "managerl4");
+
+ if (r)
+ DSSERR("failed to create sysfs file\n");
+ }
+#endif
+
+ return 0;
+}
+
+void dss_uninit_overlay_managers(struct platform_device *pdev)
+{
+ struct omap_overlay_manager *mgr;
+
+ while (!list_empty(&manager_list)) {
+ mgr = list_first_entry(&manager_list,
+ struct omap_overlay_manager, list);
+ list_del(&mgr->list);
+ kobject_del(&mgr->kobj);
+ kobject_put(&mgr->kobj);
+ kfree(mgr);
+ }
+
+ num_managers = 0;
+}
+
+int omap_dss_get_num_overlay_managers(void)
+{
+ return num_managers;
+}
+EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
+
+struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
+{
+ int i = 0;
+ struct omap_overlay_manager *mgr;
+
+ list_for_each_entry(mgr, &manager_list, list) {
+ if (i++ == num)
+ return mgr;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(omap_dss_get_overlay_manager);
+
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
new file mode 100644
index 00000000000..b7f9a733984
--- /dev/null
+++ b/drivers/video/omap2/dss/overlay.c
@@ -0,0 +1,680 @@
+/*
+ * linux/drivers/video/omap2/dss/overlay.c
+ *
+ * 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 "OVERLAY"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+static int num_overlays;
+static struct list_head overlay_list;
+
+static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name);
+}
+
+static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ ovl->manager ? ovl->manager->name : "<none>");
+}
+
+static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
+ size_t size)
+{
+ int i, r;
+ struct omap_overlay_manager *mgr = NULL;
+ struct omap_overlay_manager *old_mgr;
+ int len = size;
+
+ if (buf[size-1] == '\n')
+ --len;
+
+ if (len > 0) {
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+ mgr = omap_dss_get_overlay_manager(i);
+
+ if (strncmp(buf, mgr->name, len) == 0)
+ break;
+
+ mgr = NULL;
+ }
+ }
+
+ if (len > 0 && mgr == NULL)
+ return -EINVAL;
+
+ if (mgr)
+ DSSDBG("manager %s found\n", mgr->name);
+
+ if (mgr == ovl->manager)
+ return size;
+
+ old_mgr = ovl->manager;
+
+ /* detach old manager */
+ if (old_mgr) {
+ r = ovl->unset_manager(ovl);
+ if (r) {
+ DSSERR("detach failed\n");
+ return r;
+ }
+
+ r = old_mgr->apply(old_mgr);
+ if (r)
+ return r;
+ }
+
+ if (mgr) {
+ r = ovl->set_manager(ovl, mgr);
+ if (r) {
+ DSSERR("Failed to attach overlay\n");
+ return r;
+ }
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+ }
+
+ return size;
+}
+
+static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d,%d\n",
+ ovl->info.width, ovl->info.height);
+}
+
+static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width);
+}
+
+static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d,%d\n",
+ ovl->info.pos_x, ovl->info.pos_y);
+}
+
+static ssize_t overlay_position_store(struct omap_overlay *ovl,
+ const char *buf, size_t size)
+{
+ int r;
+ char *last;
+ struct omap_overlay_info info;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ info.pos_x = simple_strtoul(buf, &last, 10);
+ ++last;
+ if (last - buf >= size)
+ return -EINVAL;
+
+ info.pos_y = simple_strtoul(last, &last, 10);
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r)
+ return r;
+
+ if (ovl->manager) {
+ r = ovl->manager->apply(ovl->manager);
+ if (r)
+ return r;
+ }
+
+ return size;
+}
+
+static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d,%d\n",
+ ovl->info.out_width, ovl->info.out_height);
+}
+
+static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
+ const char *buf, size_t size)
+{
+ int r;
+ char *last;
+ struct omap_overlay_info info;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ info.out_width = simple_strtoul(buf, &last, 10);
+ ++last;
+ if (last - buf >= size)
+ return -EINVAL;
+
+ info.out_height = simple_strtoul(last, &last, 10);
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r)
+ return r;
+
+ if (ovl->manager) {
+ r = ovl->manager->apply(ovl->manager);
+ if (r)
+ return r;
+ }
+
+ return size;
+}
+
+static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled);
+}
+
+static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
+ size_t size)
+{
+ int r;
+ struct omap_overlay_info info;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ info.enabled = simple_strtoul(buf, NULL, 10);
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r)
+ return r;
+
+ if (ovl->manager) {
+ r = ovl->manager->apply(ovl->manager);
+ if (r)
+ return r;
+ }
+
+ return size;
+}
+
+static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ovl->info.global_alpha);
+}
+
+static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
+ const char *buf, size_t size)
+{
+ int r;
+ struct omap_overlay_info info;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ /* Video1 plane does not support global alpha
+ * to always make it 255 completely opaque
+ */
+ if (ovl->id == OMAP_DSS_VIDEO1)
+ info.global_alpha = 255;
+ else
+ info.global_alpha = simple_strtoul(buf, NULL, 10);
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r)
+ return r;
+
+ if (ovl->manager) {
+ r = ovl->manager->apply(ovl->manager);
+ if (r)
+ return r;
+ }
+
+ return size;
+}
+
+struct overlay_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct omap_overlay *, char *);
+ ssize_t (*store)(struct omap_overlay *, const char *, size_t);
+};
+
+#define OVERLAY_ATTR(_name, _mode, _show, _store) \
+ struct overlay_attribute overlay_attr_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
+static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
+ overlay_manager_show, overlay_manager_store);
+static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
+static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
+static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
+ overlay_position_show, overlay_position_store);
+static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
+ overlay_output_size_show, overlay_output_size_store);
+static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
+ overlay_enabled_show, overlay_enabled_store);
+static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
+ overlay_global_alpha_show, overlay_global_alpha_store);
+
+static struct attribute *overlay_sysfs_attrs[] = {
+ &overlay_attr_name.attr,
+ &overlay_attr_manager.attr,
+ &overlay_attr_input_size.attr,
+ &overlay_attr_screen_width.attr,
+ &overlay_attr_position.attr,
+ &overlay_attr_output_size.attr,
+ &overlay_attr_enabled.attr,
+ &overlay_attr_global_alpha.attr,
+ NULL
+};
+
+static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct omap_overlay *overlay;
+ struct overlay_attribute *overlay_attr;
+
+ overlay = container_of(kobj, struct omap_overlay, kobj);
+ overlay_attr = container_of(attr, struct overlay_attribute, attr);
+
+ if (!overlay_attr->show)
+ return -ENOENT;
+
+ return overlay_attr->show(overlay, buf);
+}
+
+static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay *overlay;
+ struct overlay_attribute *overlay_attr;
+
+ overlay = container_of(kobj, struct omap_overlay, kobj);
+ overlay_attr = container_of(attr, struct overlay_attribute, attr);
+
+ if (!overlay_attr->store)
+ return -ENOENT;
+
+ return overlay_attr->store(overlay, buf, size);
+}
+
+static struct sysfs_ops overlay_sysfs_ops = {
+ .show = overlay_attr_show,
+ .store = overlay_attr_store,
+};
+
+static struct kobj_type overlay_ktype = {
+ .sysfs_ops = &overlay_sysfs_ops,
+ .default_attrs = overlay_sysfs_attrs,
+};
+
+/* Check if overlay parameters are compatible with display */
+int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
+{
+ struct omap_overlay_info *info;
+ u16 outw, outh;
+ u16 dw, dh;
+
+ if (!dssdev)
+ return 0;
+
+ if (!ovl->info.enabled)
+ return 0;
+
+ info = &ovl->info;
+
+ if (info->paddr == 0) {
+ DSSDBG("check_overlay failed: paddr 0\n");
+ return -EINVAL;
+ }
+
+ dssdev->get_resolution(dssdev, &dw, &dh);
+
+ DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n",
+ ovl->id,
+ info->pos_x, info->pos_y,
+ info->width, info->height,
+ info->out_width, info->out_height,
+ dw, dh);
+
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
+ outw = info->width;
+ outh = info->height;
+ } else {
+ if (info->out_width == 0)
+ outw = info->width;
+ else
+ outw = info->out_width;
+
+ if (info->out_height == 0)
+ outh = info->height;
+ else
+ outh = info->out_height;
+ }
+
+ if (dw < info->pos_x + outw) {
+ DSSDBG("check_overlay failed 1: %d < %d + %d\n",
+ dw, info->pos_x, outw);
+ return -EINVAL;
+ }
+
+ if (dh < info->pos_y + outh) {
+ DSSDBG("check_overlay failed 2: %d < %d + %d\n",
+ dh, info->pos_y, outh);
+ return -EINVAL;
+ }
+
+ if ((ovl->supported_modes & info->color_mode) == 0) {
+ DSSERR("overlay doesn't support mode %d\n", info->color_mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
+ struct omap_overlay_info *info)
+{
+ int r;
+ struct omap_overlay_info old_info;
+
+ old_info = ovl->info;
+ ovl->info = *info;
+
+ if (ovl->manager) {
+ r = dss_check_overlay(ovl, ovl->manager->device);
+ if (r) {
+ ovl->info = old_info;
+ return r;
+ }
+ }
+
+ ovl->info_dirty = true;
+
+ return 0;
+}
+
+static void dss_ovl_get_overlay_info(struct omap_overlay *ovl,
+ struct omap_overlay_info *info)
+{
+ *info = ovl->info;
+}
+
+static int dss_ovl_wait_for_go(struct omap_overlay *ovl)
+{
+ return dss_mgr_wait_for_go_ovl(ovl);
+}
+
+static int omap_dss_set_manager(struct omap_overlay *ovl,
+ struct omap_overlay_manager *mgr)
+{
+ if (!mgr)
+ return -EINVAL;
+
+ if (ovl->manager) {
+ DSSERR("overlay '%s' already has a manager '%s'\n",
+ ovl->name, ovl->manager->name);
+ return -EINVAL;
+ }
+
+ if (ovl->info.enabled) {
+ DSSERR("overlay has to be disabled to change the manager\n");
+ return -EINVAL;
+ }
+
+ ovl->manager = mgr;
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ /* XXX: on manual update display, in auto update mode, a bug happens
+ * here. When an overlay is first enabled on LCD, then it's disabled,
+ * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT
+ * errors. Waiting before changing the channel_out fixes it. I'm
+ * guessing that the overlay is still somehow being used for the LCD,
+ * but I don't understand how or why. */
+ msleep(40);
+ dispc_set_channel_out(ovl->id, mgr->id);
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ return 0;
+}
+
+static int omap_dss_unset_manager(struct omap_overlay *ovl)
+{
+ int r;
+
+ if (!ovl->manager) {
+ DSSERR("failed to detach overlay: manager not set\n");
+ return -EINVAL;
+ }
+
+ if (ovl->info.enabled) {
+ DSSERR("overlay has to be disabled to unset the manager\n");
+ return -EINVAL;
+ }
+
+ r = ovl->wait_for_go(ovl);
+ if (r)
+ return r;
+
+ ovl->manager = NULL;
+
+ return 0;
+}
+
+int omap_dss_get_num_overlays(void)
+{
+ return num_overlays;
+}
+EXPORT_SYMBOL(omap_dss_get_num_overlays);
+
+struct omap_overlay *omap_dss_get_overlay(int num)
+{
+ int i = 0;
+ struct omap_overlay *ovl;
+
+ list_for_each_entry(ovl, &overlay_list, list) {
+ if (i++ == num)
+ return ovl;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(omap_dss_get_overlay);
+
+static void omap_dss_add_overlay(struct omap_overlay *overlay)
+{
+ ++num_overlays;
+ list_add_tail(&overlay->list, &overlay_list);
+}
+
+static struct omap_overlay *dispc_overlays[3];
+
+void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
+{
+ mgr->num_overlays = 3;
+ mgr->overlays = dispc_overlays;
+}
+
+#ifdef L4_EXAMPLE
+static struct omap_overlay *l4_overlays[1];
+void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr)
+{
+ mgr->num_overlays = 1;
+ mgr->overlays = l4_overlays;
+}
+#endif
+
+void dss_init_overlays(struct platform_device *pdev)
+{
+ int i, r;
+
+ INIT_LIST_HEAD(&overlay_list);
+
+ num_overlays = 0;
+
+ for (i = 0; i < 3; ++i) {
+ struct omap_overlay *ovl;
+ ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
+
+ BUG_ON(ovl == NULL);
+
+ switch (i) {
+ case 0:
+ ovl->name = "gfx";
+ ovl->id = OMAP_DSS_GFX;
+ ovl->supported_modes = cpu_is_omap34xx() ?
+ OMAP_DSS_COLOR_GFX_OMAP3 :
+ OMAP_DSS_COLOR_GFX_OMAP2;
+ ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
+ ovl->info.global_alpha = 255;
+ break;
+ case 1:
+ ovl->name = "vid1";
+ ovl->id = OMAP_DSS_VIDEO1;
+ ovl->supported_modes = cpu_is_omap34xx() ?
+ OMAP_DSS_COLOR_VID1_OMAP3 :
+ OMAP_DSS_COLOR_VID_OMAP2;
+ ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
+ OMAP_DSS_OVL_CAP_DISPC;
+ ovl->info.global_alpha = 255;
+ break;
+ case 2:
+ ovl->name = "vid2";
+ ovl->id = OMAP_DSS_VIDEO2;
+ ovl->supported_modes = cpu_is_omap34xx() ?
+ OMAP_DSS_COLOR_VID2_OMAP3 :
+ OMAP_DSS_COLOR_VID_OMAP2;
+ ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
+ OMAP_DSS_OVL_CAP_DISPC;
+ ovl->info.global_alpha = 255;
+ break;
+ }
+
+ ovl->set_manager = &omap_dss_set_manager;
+ ovl->unset_manager = &omap_dss_unset_manager;
+ ovl->set_overlay_info = &dss_ovl_set_overlay_info;
+ ovl->get_overlay_info = &dss_ovl_get_overlay_info;
+ ovl->wait_for_go = &dss_ovl_wait_for_go;
+
+ omap_dss_add_overlay(ovl);
+
+ r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
+ &pdev->dev.kobj, "overlay%d", i);
+
+ if (r) {
+ DSSERR("failed to create sysfs file\n");
+ continue;
+ }
+
+ dispc_overlays[i] = ovl;
+ }
+
+#ifdef L4_EXAMPLE
+ {
+ struct omap_overlay *ovl;
+ ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
+
+ BUG_ON(ovl == NULL);
+
+ ovl->name = "l4";
+ ovl->supported_modes = OMAP_DSS_COLOR_RGB24U;
+
+ ovl->set_manager = &omap_dss_set_manager;
+ ovl->unset_manager = &omap_dss_unset_manager;
+ ovl->set_overlay_info = &dss_ovl_set_overlay_info;
+ ovl->get_overlay_info = &dss_ovl_get_overlay_info;
+
+ omap_dss_add_overlay(ovl);
+
+ r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
+ &pdev->dev.kobj, "overlayl4");
+
+ if (r)
+ DSSERR("failed to create sysfs file\n");
+
+ l4_overlays[0] = ovl;
+ }
+#endif
+}
+
+/* connect overlays to the new device, if not already connected. if force
+ * selected, connect always. */
+void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
+{
+ int i;
+ struct omap_overlay_manager *lcd_mgr;
+ struct omap_overlay_manager *tv_mgr;
+ struct omap_overlay_manager *mgr = NULL;
+
+ lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD);
+ tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV);
+
+ if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) {
+ if (!lcd_mgr->device || force) {
+ if (lcd_mgr->device)
+ lcd_mgr->unset_device(lcd_mgr);
+ lcd_mgr->set_device(lcd_mgr, dssdev);
+ mgr = lcd_mgr;
+ }
+ }
+
+ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
+ if (!tv_mgr->device || force) {
+ if (tv_mgr->device)
+ tv_mgr->unset_device(tv_mgr);
+ tv_mgr->set_device(tv_mgr, dssdev);
+ mgr = tv_mgr;
+ }
+ }
+
+ if (mgr) {
+ for (i = 0; i < 3; i++) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+ if (!ovl->manager || force) {
+ if (ovl->manager)
+ omap_dss_unset_manager(ovl);
+ omap_dss_set_manager(ovl, mgr);
+ }
+ }
+ }
+}
+
+void dss_uninit_overlays(struct platform_device *pdev)
+{
+ struct omap_overlay *ovl;
+
+ while (!list_empty(&overlay_list)) {
+ ovl = list_first_entry(&overlay_list,
+ struct omap_overlay, list);
+ list_del(&ovl->list);
+ kobject_del(&ovl->kobj);
+ kobject_put(&ovl->kobj);
+ kfree(ovl);
+ }
+
+ num_overlays = 0;
+}
+
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
new file mode 100644
index 00000000000..d0b3006ad8a
--- /dev/null
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -0,0 +1,1309 @@
+/*
+ * linux/drivers/video/omap2/dss/rfbi.c
+ *
+ * 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 "RFBI"
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/kfifo.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/seq_file.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+/*#define MEASURE_PERF*/
+
+#define RFBI_BASE 0x48050800
+
+struct rfbi_reg { u16 idx; };
+
+#define RFBI_REG(idx) ((const struct rfbi_reg) { idx })
+
+#define RFBI_REVISION RFBI_REG(0x0000)
+#define RFBI_SYSCONFIG RFBI_REG(0x0010)
+#define RFBI_SYSSTATUS RFBI_REG(0x0014)
+#define RFBI_CONTROL RFBI_REG(0x0040)
+#define RFBI_PIXEL_CNT RFBI_REG(0x0044)
+#define RFBI_LINE_NUMBER RFBI_REG(0x0048)
+#define RFBI_CMD RFBI_REG(0x004c)
+#define RFBI_PARAM RFBI_REG(0x0050)
+#define RFBI_DATA RFBI_REG(0x0054)
+#define RFBI_READ RFBI_REG(0x0058)
+#define RFBI_STATUS RFBI_REG(0x005c)
+
+#define RFBI_CONFIG(n) RFBI_REG(0x0060 + (n)*0x18)
+#define RFBI_ONOFF_TIME(n) RFBI_REG(0x0064 + (n)*0x18)
+#define RFBI_CYCLE_TIME(n) RFBI_REG(0x0068 + (n)*0x18)
+#define RFBI_DATA_CYCLE1(n) RFBI_REG(0x006c + (n)*0x18)
+#define RFBI_DATA_CYCLE2(n) RFBI_REG(0x0070 + (n)*0x18)
+#define RFBI_DATA_CYCLE3(n) RFBI_REG(0x0074 + (n)*0x18)
+
+#define RFBI_VSYNC_WIDTH RFBI_REG(0x0090)
+#define RFBI_HSYNC_WIDTH RFBI_REG(0x0094)
+
+#define RFBI_CMD_FIFO_LEN_BYTES (16 * sizeof(struct update_param))
+
+#define REG_FLD_MOD(idx, val, start, end) \
+ rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end))
+
+/* To work around an RFBI transfer rate limitation */
+#define OMAP_RFBI_RATE_LIMIT 1
+
+enum omap_rfbi_cycleformat {
+ OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0,
+ OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1,
+ OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2,
+ OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3,
+};
+
+enum omap_rfbi_datatype {
+ OMAP_DSS_RFBI_DATATYPE_12 = 0,
+ OMAP_DSS_RFBI_DATATYPE_16 = 1,
+ OMAP_DSS_RFBI_DATATYPE_18 = 2,
+ OMAP_DSS_RFBI_DATATYPE_24 = 3,
+};
+
+enum omap_rfbi_parallelmode {
+ OMAP_DSS_RFBI_PARALLELMODE_8 = 0,
+ OMAP_DSS_RFBI_PARALLELMODE_9 = 1,
+ OMAP_DSS_RFBI_PARALLELMODE_12 = 2,
+ OMAP_DSS_RFBI_PARALLELMODE_16 = 3,
+};
+
+enum update_cmd {
+ RFBI_CMD_UPDATE = 0,
+ RFBI_CMD_SYNC = 1,
+};
+
+static int rfbi_convert_timings(struct rfbi_timings *t);
+static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div);
+static void process_cmd_fifo(void);
+
+static struct {
+ void __iomem *base;
+
+ unsigned long l4_khz;
+
+ enum omap_rfbi_datatype datatype;
+ enum omap_rfbi_parallelmode parallelmode;
+
+ enum omap_rfbi_te_mode te_mode;
+ int te_enabled;
+
+ void (*framedone_callback)(void *data);
+ void *framedone_callback_data;
+
+ struct omap_dss_device *dssdev[2];
+
+ struct kfifo *cmd_fifo;
+ spinlock_t cmd_lock;
+ struct completion cmd_done;
+ atomic_t cmd_fifo_full;
+ atomic_t cmd_pending;
+#ifdef MEASURE_PERF
+ unsigned perf_bytes;
+ ktime_t perf_setup_time;
+ ktime_t perf_start_time;
+#endif
+} rfbi;
+
+struct update_region {
+ u16 x;
+ u16 y;
+ u16 w;
+ u16 h;
+};
+
+struct update_param {
+ u8 rfbi_module;
+ u8 cmd;
+
+ union {
+ struct update_region r;
+ struct completion *sync;
+ } par;
+};
+
+static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val)
+{
+ __raw_writel(val, rfbi.base + idx.idx);
+}
+
+static inline u32 rfbi_read_reg(const struct rfbi_reg idx)
+{
+ return __raw_readl(rfbi.base + idx.idx);
+}
+
+static void rfbi_enable_clocks(bool enable)
+{
+ if (enable)
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ else
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+void omap_rfbi_write_command(const void *buf, u32 len)
+{
+ rfbi_enable_clocks(1);
+ switch (rfbi.parallelmode) {
+ case OMAP_DSS_RFBI_PARALLELMODE_8:
+ {
+ const u8 *b = buf;
+ for (; len; len--)
+ rfbi_write_reg(RFBI_CMD, *b++);
+ break;
+ }
+
+ case OMAP_DSS_RFBI_PARALLELMODE_16:
+ {
+ const u16 *w = buf;
+ BUG_ON(len & 1);
+ for (; len; len -= 2)
+ rfbi_write_reg(RFBI_CMD, *w++);
+ break;
+ }
+
+ case OMAP_DSS_RFBI_PARALLELMODE_9:
+ case OMAP_DSS_RFBI_PARALLELMODE_12:
+ default:
+ BUG();
+ }
+ rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_write_command);
+
+void omap_rfbi_read_data(void *buf, u32 len)
+{
+ rfbi_enable_clocks(1);
+ switch (rfbi.parallelmode) {
+ case OMAP_DSS_RFBI_PARALLELMODE_8:
+ {
+ u8 *b = buf;
+ for (; len; len--) {
+ rfbi_write_reg(RFBI_READ, 0);
+ *b++ = rfbi_read_reg(RFBI_READ);
+ }
+ break;
+ }
+
+ case OMAP_DSS_RFBI_PARALLELMODE_16:
+ {
+ u16 *w = buf;
+ BUG_ON(len & ~1);
+ for (; len; len -= 2) {
+ rfbi_write_reg(RFBI_READ, 0);
+ *w++ = rfbi_read_reg(RFBI_READ);
+ }
+ break;
+ }
+
+ case OMAP_DSS_RFBI_PARALLELMODE_9:
+ case OMAP_DSS_RFBI_PARALLELMODE_12:
+ default:
+ BUG();
+ }
+ rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_read_data);
+
+void omap_rfbi_write_data(const void *buf, u32 len)
+{
+ rfbi_enable_clocks(1);
+ switch (rfbi.parallelmode) {
+ case OMAP_DSS_RFBI_PARALLELMODE_8:
+ {
+ const u8 *b = buf;
+ for (; len; len--)
+ rfbi_write_reg(RFBI_PARAM, *b++);
+ break;
+ }
+
+ case OMAP_DSS_RFBI_PARALLELMODE_16:
+ {
+ const u16 *w = buf;
+ BUG_ON(len & 1);
+ for (; len; len -= 2)
+ rfbi_write_reg(RFBI_PARAM, *w++);
+ break;
+ }
+
+ case OMAP_DSS_RFBI_PARALLELMODE_9:
+ case OMAP_DSS_RFBI_PARALLELMODE_12:
+ default:
+ BUG();
+
+ }
+ rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_write_data);
+
+void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width,
+ u16 x, u16 y,
+ u16 w, u16 h)
+{
+ int start_offset = scr_width * y + x;
+ int horiz_offset = scr_width - w;
+ int i;
+
+ rfbi_enable_clocks(1);
+
+ if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 &&
+ rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) {
+ const u16 __iomem *pd = buf;
+ pd += start_offset;
+
+ for (; h; --h) {
+ for (i = 0; i < w; ++i) {
+ const u8 __iomem *b = (const u8 __iomem *)pd;
+ rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1));
+ rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0));
+ ++pd;
+ }
+ pd += horiz_offset;
+ }
+ } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 &&
+ rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) {
+ const u32 __iomem *pd = buf;
+ pd += start_offset;
+
+ for (; h; --h) {
+ for (i = 0; i < w; ++i) {
+ const u8 __iomem *b = (const u8 __iomem *)pd;
+ rfbi_write_reg(RFBI_PARAM, __raw_readb(b+2));
+ rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1));
+ rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0));
+ ++pd;
+ }
+ pd += horiz_offset;
+ }
+ } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 &&
+ rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) {
+ const u16 __iomem *pd = buf;
+ pd += start_offset;
+
+ for (; h; --h) {
+ for (i = 0; i < w; ++i) {
+ rfbi_write_reg(RFBI_PARAM, __raw_readw(pd));
+ ++pd;
+ }
+ pd += horiz_offset;
+ }
+ } else {
+ BUG();
+ }
+
+ rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_write_pixels);
+
+#ifdef MEASURE_PERF
+static void perf_mark_setup(void)
+{
+ rfbi.perf_setup_time = ktime_get();
+}
+
+static void perf_mark_start(void)
+{
+ rfbi.perf_start_time = ktime_get();
+}
+
+static void perf_show(const char *name)
+{
+ ktime_t t, setup_time, trans_time;
+ u32 total_bytes;
+ u32 setup_us, trans_us, total_us;
+
+ t = ktime_get();
+
+ setup_time = ktime_sub(rfbi.perf_start_time, rfbi.perf_setup_time);
+ setup_us = (u32)ktime_to_us(setup_time);
+ if (setup_us == 0)
+ setup_us = 1;
+
+ trans_time = ktime_sub(t, rfbi.perf_start_time);
+ trans_us = (u32)ktime_to_us(trans_time);
+ if (trans_us == 0)
+ trans_us = 1;
+
+ total_us = setup_us + trans_us;
+
+ total_bytes = rfbi.perf_bytes;
+
+ DSSINFO("%s update %u us + %u us = %u us (%uHz), %u bytes, "
+ "%u kbytes/sec\n",
+ name,
+ setup_us,
+ trans_us,
+ total_us,
+ 1000*1000 / total_us,
+ total_bytes,
+ total_bytes * 1000 / total_us);
+}
+#else
+#define perf_mark_setup()
+#define perf_mark_start()
+#define perf_show(x)
+#endif
+
+void rfbi_transfer_area(u16 width, u16 height,
+ void (callback)(void *data), void *data)
+{
+ u32 l;
+
+ /*BUG_ON(callback == 0);*/
+ BUG_ON(rfbi.framedone_callback != NULL);
+
+ DSSDBG("rfbi_transfer_area %dx%d\n", width, height);
+
+ dispc_set_lcd_size(width, height);
+
+ dispc_enable_lcd_out(1);
+
+ rfbi.framedone_callback = callback;
+ rfbi.framedone_callback_data = data;
+
+ rfbi_enable_clocks(1);
+
+ rfbi_write_reg(RFBI_PIXEL_CNT, width * height);
+
+ l = rfbi_read_reg(RFBI_CONTROL);
+ l = FLD_MOD(l, 1, 0, 0); /* enable */
+ if (!rfbi.te_enabled)
+ l = FLD_MOD(l, 1, 4, 4); /* ITE */
+
+ perf_mark_start();
+
+ rfbi_write_reg(RFBI_CONTROL, l);
+}
+
+static void framedone_callback(void *data, u32 mask)
+{
+ void (*callback)(void *data);
+
+ DSSDBG("FRAMEDONE\n");
+
+ perf_show("DISPC");
+
+ REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0);
+
+ rfbi_enable_clocks(0);
+
+ callback = rfbi.framedone_callback;
+ rfbi.framedone_callback = NULL;
+
+ /*callback(rfbi.framedone_callback_data);*/
+
+ atomic_set(&rfbi.cmd_pending, 0);
+
+ process_cmd_fifo();
+}
+
+#if 1 /* VERBOSE */
+static void rfbi_print_timings(void)
+{
+ u32 l;
+ u32 time;
+
+ l = rfbi_read_reg(RFBI_CONFIG(0));
+ time = 1000000000 / rfbi.l4_khz;
+ if (l & (1 << 4))
+ time *= 2;
+
+ DSSDBG("Tick time %u ps\n", time);
+ l = rfbi_read_reg(RFBI_ONOFF_TIME(0));
+ DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
+ "REONTIME %d, REOFFTIME %d\n",
+ l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f,
+ (l >> 20) & 0x0f, (l >> 24) & 0x3f);
+
+ l = rfbi_read_reg(RFBI_CYCLE_TIME(0));
+ DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
+ "ACCESSTIME %d\n",
+ (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f,
+ (l >> 22) & 0x3f);
+}
+#else
+static void rfbi_print_timings(void) {}
+#endif
+
+
+
+
+static u32 extif_clk_period;
+
+static inline unsigned long round_to_extif_ticks(unsigned long ps, int div)
+{
+ int bus_tick = extif_clk_period * div;
+ return (ps + bus_tick - 1) / bus_tick * bus_tick;
+}
+
+static int calc_reg_timing(struct rfbi_timings *t, int div)
+{
+ t->clk_div = div;
+
+ t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div);
+
+ t->we_on_time = round_to_extif_ticks(t->we_on_time, div);
+ t->we_off_time = round_to_extif_ticks(t->we_off_time, div);
+ t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div);
+
+ t->re_on_time = round_to_extif_ticks(t->re_on_time, div);
+ t->re_off_time = round_to_extif_ticks(t->re_off_time, div);
+ t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div);
+
+ t->access_time = round_to_extif_ticks(t->access_time, div);
+ t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div);
+ t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div);
+
+ DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n",
+ t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
+ DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n",
+ t->we_on_time, t->we_off_time, t->re_cycle_time,
+ t->we_cycle_time);
+ DSSDBG("[reg]rdaccess %d cspulse %d\n",
+ t->access_time, t->cs_pulse_width);
+
+ return rfbi_convert_timings(t);
+}
+
+static int calc_extif_timings(struct rfbi_timings *t)
+{
+ u32 max_clk_div;
+ int div;
+
+ rfbi_get_clk_info(&extif_clk_period, &max_clk_div);
+ for (div = 1; div <= max_clk_div; div++) {
+ if (calc_reg_timing(t, div) == 0)
+ break;
+ }
+
+ if (div <= max_clk_div)
+ return 0;
+
+ DSSERR("can't setup timings\n");
+ return -1;
+}
+
+
+void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t)
+{
+ int r;
+
+ if (!t->converted) {
+ r = calc_extif_timings(t);
+ if (r < 0)
+ DSSERR("Failed to calc timings\n");
+ }
+
+ BUG_ON(!t->converted);
+
+ rfbi_enable_clocks(1);
+ rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]);
+ rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]);
+
+ /* TIMEGRANULARITY */
+ REG_FLD_MOD(RFBI_CONFIG(rfbi_module),
+ (t->tim[2] ? 1 : 0), 4, 4);
+
+ rfbi_print_timings();
+ rfbi_enable_clocks(0);
+}
+
+static int ps_to_rfbi_ticks(int time, int div)
+{
+ unsigned long tick_ps;
+ int ret;
+
+ /* Calculate in picosecs to yield more exact results */
+ tick_ps = 1000000000 / (rfbi.l4_khz) * div;
+
+ ret = (time + tick_ps - 1) / tick_ps;
+
+ return ret;
+}
+
+#ifdef OMAP_RFBI_RATE_LIMIT
+unsigned long rfbi_get_max_tx_rate(void)
+{
+ unsigned long l4_rate, dss1_rate;
+ int min_l4_ticks = 0;
+ int i;
+
+ /* According to TI this can't be calculated so make the
+ * adjustments for a couple of known frequencies and warn for
+ * others.
+ */
+ static const struct {
+ unsigned long l4_clk; /* HZ */
+ unsigned long dss1_clk; /* HZ */
+ unsigned long min_l4_ticks;
+ } ftab[] = {
+ { 55, 132, 7, }, /* 7.86 MPix/s */
+ { 110, 110, 12, }, /* 9.16 MPix/s */
+ { 110, 132, 10, }, /* 11 Mpix/s */
+ { 120, 120, 10, }, /* 12 Mpix/s */
+ { 133, 133, 10, }, /* 13.3 Mpix/s */
+ };
+
+ l4_rate = rfbi.l4_khz / 1000;
+ dss1_rate = dss_clk_get_rate(DSS_CLK_FCK1) / 1000000;
+
+ for (i = 0; i < ARRAY_SIZE(ftab); i++) {
+ /* Use a window instead of an exact match, to account
+ * for different DPLL multiplier / divider pairs.
+ */
+ if (abs(ftab[i].l4_clk - l4_rate) < 3 &&
+ abs(ftab[i].dss1_clk - dss1_rate) < 3) {
+ min_l4_ticks = ftab[i].min_l4_ticks;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(ftab)) {
+ /* Can't be sure, return anyway the maximum not
+ * rate-limited. This might cause a problem only for the
+ * tearing synchronisation.
+ */
+ DSSERR("can't determine maximum RFBI transfer rate\n");
+ return rfbi.l4_khz * 1000;
+ }
+ return rfbi.l4_khz * 1000 / min_l4_ticks;
+}
+#else
+int rfbi_get_max_tx_rate(void)
+{
+ return rfbi.l4_khz * 1000;
+}
+#endif
+
+static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
+{
+ *clk_period = 1000000000 / rfbi.l4_khz;
+ *max_clk_div = 2;
+}
+
+static int rfbi_convert_timings(struct rfbi_timings *t)
+{
+ u32 l;
+ int reon, reoff, weon, weoff, cson, csoff, cs_pulse;
+ int actim, recyc, wecyc;
+ int div = t->clk_div;
+
+ if (div <= 0 || div > 2)
+ return -1;
+
+ /* Make sure that after conversion it still holds that:
+ * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff,
+ * csoff > cson, csoff >= max(weoff, reoff), actim > reon
+ */
+ weon = ps_to_rfbi_ticks(t->we_on_time, div);
+ weoff = ps_to_rfbi_ticks(t->we_off_time, div);
+ if (weoff <= weon)
+ weoff = weon + 1;
+ if (weon > 0x0f)
+ return -1;
+ if (weoff > 0x3f)
+ return -1;
+
+ reon = ps_to_rfbi_ticks(t->re_on_time, div);
+ reoff = ps_to_rfbi_ticks(t->re_off_time, div);
+ if (reoff <= reon)
+ reoff = reon + 1;
+ if (reon > 0x0f)
+ return -1;
+ if (reoff > 0x3f)
+ return -1;
+
+ cson = ps_to_rfbi_ticks(t->cs_on_time, div);
+ csoff = ps_to_rfbi_ticks(t->cs_off_time, div);
+ if (csoff <= cson)
+ csoff = cson + 1;
+ if (csoff < max(weoff, reoff))
+ csoff = max(weoff, reoff);
+ if (cson > 0x0f)
+ return -1;
+ if (csoff > 0x3f)
+ return -1;
+
+ l = cson;
+ l |= csoff << 4;
+ l |= weon << 10;
+ l |= weoff << 14;
+ l |= reon << 20;
+ l |= reoff << 24;
+
+ t->tim[0] = l;
+
+ actim = ps_to_rfbi_ticks(t->access_time, div);
+ if (actim <= reon)
+ actim = reon + 1;
+ if (actim > 0x3f)
+ return -1;
+
+ wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div);
+ if (wecyc < weoff)
+ wecyc = weoff;
+ if (wecyc > 0x3f)
+ return -1;
+
+ recyc = ps_to_rfbi_ticks(t->re_cycle_time, div);
+ if (recyc < reoff)
+ recyc = reoff;
+ if (recyc > 0x3f)
+ return -1;
+
+ cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div);
+ if (cs_pulse > 0x3f)
+ return -1;
+
+ l = wecyc;
+ l |= recyc << 6;
+ l |= cs_pulse << 12;
+ l |= actim << 22;
+
+ t->tim[1] = l;
+
+ t->tim[2] = div - 1;
+
+ t->converted = 1;
+
+ return 0;
+}
+
+/* xxx FIX module selection missing */
+int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode,
+ unsigned hs_pulse_time, unsigned vs_pulse_time,
+ int hs_pol_inv, int vs_pol_inv, int extif_div)
+{
+ int hs, vs;
+ int min;
+ u32 l;
+
+ hs = ps_to_rfbi_ticks(hs_pulse_time, 1);
+ vs = ps_to_rfbi_ticks(vs_pulse_time, 1);
+ if (hs < 2)
+ return -EDOM;
+ if (mode == OMAP_DSS_RFBI_TE_MODE_2)
+ min = 2;
+ else /* OMAP_DSS_RFBI_TE_MODE_1 */
+ min = 4;
+ if (vs < min)
+ return -EDOM;
+ if (vs == hs)
+ return -EINVAL;
+ rfbi.te_mode = mode;
+ DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n",
+ mode, hs, vs, hs_pol_inv, vs_pol_inv);
+
+ rfbi_enable_clocks(1);
+ rfbi_write_reg(RFBI_HSYNC_WIDTH, hs);
+ rfbi_write_reg(RFBI_VSYNC_WIDTH, vs);
+
+ l = rfbi_read_reg(RFBI_CONFIG(0));
+ if (hs_pol_inv)
+ l &= ~(1 << 21);
+ else
+ l |= 1 << 21;
+ if (vs_pol_inv)
+ l &= ~(1 << 20);
+ else
+ l |= 1 << 20;
+ rfbi_enable_clocks(0);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_rfbi_setup_te);
+
+/* xxx FIX module selection missing */
+int omap_rfbi_enable_te(bool enable, unsigned line)
+{
+ u32 l;
+
+ DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode);
+ if (line > (1 << 11) - 1)
+ return -EINVAL;
+
+ rfbi_enable_clocks(1);
+ l = rfbi_read_reg(RFBI_CONFIG(0));
+ l &= ~(0x3 << 2);
+ if (enable) {
+ rfbi.te_enabled = 1;
+ l |= rfbi.te_mode << 2;
+ } else
+ rfbi.te_enabled = 0;
+ rfbi_write_reg(RFBI_CONFIG(0), l);
+ rfbi_write_reg(RFBI_LINE_NUMBER, line);
+ rfbi_enable_clocks(0);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_rfbi_enable_te);
+
+#if 0
+static void rfbi_enable_config(int enable1, int enable2)
+{
+ u32 l;
+ int cs = 0;
+
+ if (enable1)
+ cs |= 1<<0;
+ if (enable2)
+ cs |= 1<<1;
+
+ rfbi_enable_clocks(1);
+
+ l = rfbi_read_reg(RFBI_CONTROL);
+
+ l = FLD_MOD(l, cs, 3, 2);
+ l = FLD_MOD(l, 0, 1, 1);
+
+ rfbi_write_reg(RFBI_CONTROL, l);
+
+
+ l = rfbi_read_reg(RFBI_CONFIG(0));
+ l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */
+ /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */
+ /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */
+
+ l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */
+ l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */
+ l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */
+
+ l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0);
+ rfbi_write_reg(RFBI_CONFIG(0), l);
+
+ rfbi_enable_clocks(0);
+}
+#endif
+
+int rfbi_configure(int rfbi_module, int bpp, int lines)
+{
+ u32 l;
+ int cycle1 = 0, cycle2 = 0, cycle3 = 0;
+ enum omap_rfbi_cycleformat cycleformat;
+ enum omap_rfbi_datatype datatype;
+ enum omap_rfbi_parallelmode parallelmode;
+
+ switch (bpp) {
+ case 12:
+ datatype = OMAP_DSS_RFBI_DATATYPE_12;
+ break;
+ case 16:
+ datatype = OMAP_DSS_RFBI_DATATYPE_16;
+ break;
+ case 18:
+ datatype = OMAP_DSS_RFBI_DATATYPE_18;
+ break;
+ case 24:
+ datatype = OMAP_DSS_RFBI_DATATYPE_24;
+ break;
+ default:
+ BUG();
+ return 1;
+ }
+ rfbi.datatype = datatype;
+
+ switch (lines) {
+ case 8:
+ parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8;
+ break;
+ case 9:
+ parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9;
+ break;
+ case 12:
+ parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12;
+ break;
+ case 16:
+ parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16;
+ break;
+ default:
+ BUG();
+ return 1;
+ }
+ rfbi.parallelmode = parallelmode;
+
+ if ((bpp % lines) == 0) {
+ switch (bpp / lines) {
+ case 1:
+ cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1;
+ break;
+ case 2:
+ cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1;
+ break;
+ case 3:
+ cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1;
+ break;
+ default:
+ BUG();
+ return 1;
+ }
+ } else if ((2 * bpp % lines) == 0) {
+ if ((2 * bpp / lines) == 3)
+ cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2;
+ else {
+ BUG();
+ return 1;
+ }
+ } else {
+ BUG();
+ return 1;
+ }
+
+ switch (cycleformat) {
+ case OMAP_DSS_RFBI_CYCLEFORMAT_1_1:
+ cycle1 = lines;
+ break;
+
+ case OMAP_DSS_RFBI_CYCLEFORMAT_2_1:
+ cycle1 = lines;
+ cycle2 = lines;
+ break;
+
+ case OMAP_DSS_RFBI_CYCLEFORMAT_3_1:
+ cycle1 = lines;
+ cycle2 = lines;
+ cycle3 = lines;
+ break;
+
+ case OMAP_DSS_RFBI_CYCLEFORMAT_3_2:
+ cycle1 = lines;
+ cycle2 = (lines / 2) | ((lines / 2) << 16);
+ cycle3 = (lines << 16);
+ break;
+ }
+
+ rfbi_enable_clocks(1);
+
+ REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */
+
+ l = 0;
+ l |= FLD_VAL(parallelmode, 1, 0);
+ l |= FLD_VAL(0, 3, 2); /* TRIGGERMODE: ITE */
+ l |= FLD_VAL(0, 4, 4); /* TIMEGRANULARITY */
+ l |= FLD_VAL(datatype, 6, 5);
+ /* l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */
+ l |= FLD_VAL(0, 8, 7); /* L4FORMAT, 1pix/L4 */
+ l |= FLD_VAL(cycleformat, 10, 9);
+ l |= FLD_VAL(0, 12, 11); /* UNUSEDBITS */
+ l |= FLD_VAL(0, 16, 16); /* A0POLARITY */
+ l |= FLD_VAL(0, 17, 17); /* REPOLARITY */
+ l |= FLD_VAL(0, 18, 18); /* WEPOLARITY */
+ l |= FLD_VAL(0, 19, 19); /* CSPOLARITY */
+ l |= FLD_VAL(1, 20, 20); /* TE_VSYNC_POLARITY */
+ l |= FLD_VAL(1, 21, 21); /* HSYNCPOLARITY */
+ rfbi_write_reg(RFBI_CONFIG(rfbi_module), l);
+
+ rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1);
+ rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2);
+ rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3);
+
+
+ l = rfbi_read_reg(RFBI_CONTROL);
+ l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */
+ l = FLD_MOD(l, 0, 1, 1); /* clear bypass */
+ rfbi_write_reg(RFBI_CONTROL, l);
+
+
+ DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n",
+ bpp, lines, cycle1, cycle2, cycle3);
+
+ rfbi_enable_clocks(0);
+
+ return 0;
+}
+EXPORT_SYMBOL(rfbi_configure);
+
+static int rfbi_find_display(struct omap_dss_device *dssdev)
+{
+ if (dssdev == rfbi.dssdev[0])
+ return 0;
+
+ if (dssdev == rfbi.dssdev[1])
+ return 1;
+
+ BUG();
+ return -1;
+}
+
+
+static void signal_fifo_waiters(void)
+{
+ if (atomic_read(&rfbi.cmd_fifo_full) > 0) {
+ /* DSSDBG("SIGNALING: Fifo not full for waiter!\n"); */
+ complete(&rfbi.cmd_done);
+ atomic_dec(&rfbi.cmd_fifo_full);
+ }
+}
+
+/* returns 1 for async op, and 0 for sync op */
+static int do_update(struct omap_dss_device *dssdev, struct update_region *upd)
+{
+ u16 x = upd->x;
+ u16 y = upd->y;
+ u16 w = upd->w;
+ u16 h = upd->h;
+
+ perf_mark_setup();
+
+ if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+ /*dssdev->driver->enable_te(dssdev, 1); */
+ dss_setup_partial_planes(dssdev, &x, &y, &w, &h);
+ }
+
+#ifdef MEASURE_PERF
+ rfbi.perf_bytes = w * h * 2; /* XXX always 16bit */
+#endif
+
+ dssdev->driver->setup_update(dssdev, x, y, w, h);
+
+ if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+ rfbi_transfer_area(w, h, NULL, NULL);
+ return 1;
+ } else {
+ struct omap_overlay *ovl;
+ void __iomem *addr;
+ int scr_width;
+
+ ovl = dssdev->manager->overlays[0];
+ scr_width = ovl->info.screen_width;
+ addr = ovl->info.vaddr;
+
+ omap_rfbi_write_pixels(addr, scr_width, x, y, w, h);
+
+ perf_show("L4");
+
+ return 0;
+ }
+}
+
+static void process_cmd_fifo(void)
+{
+ int len;
+ struct update_param p;
+ struct omap_dss_device *dssdev;
+ unsigned long flags;
+
+ if (atomic_inc_return(&rfbi.cmd_pending) != 1)
+ return;
+
+ while (true) {
+ spin_lock_irqsave(rfbi.cmd_fifo->lock, flags);
+
+ len = __kfifo_get(rfbi.cmd_fifo, (unsigned char *)&p,
+ sizeof(struct update_param));
+ if (len == 0) {
+ DSSDBG("nothing more in fifo\n");
+ atomic_set(&rfbi.cmd_pending, 0);
+ spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+ break;
+ }
+
+ /* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/
+
+ spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+
+ BUG_ON(len != sizeof(struct update_param));
+ BUG_ON(p.rfbi_module > 1);
+
+ dssdev = rfbi.dssdev[p.rfbi_module];
+
+ if (p.cmd == RFBI_CMD_UPDATE) {
+ if (do_update(dssdev, &p.par.r))
+ break; /* async op */
+ } else if (p.cmd == RFBI_CMD_SYNC) {
+ DSSDBG("Signaling SYNC done!\n");
+ complete(p.par.sync);
+ } else
+ BUG();
+ }
+
+ signal_fifo_waiters();
+}
+
+static void rfbi_push_cmd(struct update_param *p)
+{
+ int ret;
+
+ while (1) {
+ unsigned long flags;
+ int available;
+
+ spin_lock_irqsave(rfbi.cmd_fifo->lock, flags);
+ available = RFBI_CMD_FIFO_LEN_BYTES -
+ __kfifo_len(rfbi.cmd_fifo);
+
+/* DSSDBG("%d bytes left in fifo\n", available); */
+ if (available < sizeof(struct update_param)) {
+ DSSDBG("Going to wait because FIFO FULL..\n");
+ spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+ atomic_inc(&rfbi.cmd_fifo_full);
+ wait_for_completion(&rfbi.cmd_done);
+ /*DSSDBG("Woke up because fifo not full anymore\n");*/
+ continue;
+ }
+
+ ret = __kfifo_put(rfbi.cmd_fifo, (unsigned char *)p,
+ sizeof(struct update_param));
+/* DSSDBG("pushed %d bytes\n", ret);*/
+
+ spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+
+ BUG_ON(ret != sizeof(struct update_param));
+
+ break;
+ }
+}
+
+static void rfbi_push_update(int rfbi_module, int x, int y, int w, int h)
+{
+ struct update_param p;
+
+ p.rfbi_module = rfbi_module;
+ p.cmd = RFBI_CMD_UPDATE;
+
+ p.par.r.x = x;
+ p.par.r.y = y;
+ p.par.r.w = w;
+ p.par.r.h = h;
+
+ DSSDBG("RFBI pushed %d,%d %dx%d\n", x, y, w, h);
+
+ rfbi_push_cmd(&p);
+
+ process_cmd_fifo();
+}
+
+static void rfbi_push_sync(int rfbi_module, struct completion *sync_comp)
+{
+ struct update_param p;
+
+ p.rfbi_module = rfbi_module;
+ p.cmd = RFBI_CMD_SYNC;
+ p.par.sync = sync_comp;
+
+ rfbi_push_cmd(&p);
+
+ DSSDBG("RFBI sync pushed to cmd fifo\n");
+
+ process_cmd_fifo();
+}
+
+void rfbi_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r))
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ DUMPREG(RFBI_REVISION);
+ DUMPREG(RFBI_SYSCONFIG);
+ DUMPREG(RFBI_SYSSTATUS);
+ DUMPREG(RFBI_CONTROL);
+ DUMPREG(RFBI_PIXEL_CNT);
+ DUMPREG(RFBI_LINE_NUMBER);
+ DUMPREG(RFBI_CMD);
+ DUMPREG(RFBI_PARAM);
+ DUMPREG(RFBI_DATA);
+ DUMPREG(RFBI_READ);
+ DUMPREG(RFBI_STATUS);
+
+ DUMPREG(RFBI_CONFIG(0));
+ DUMPREG(RFBI_ONOFF_TIME(0));
+ DUMPREG(RFBI_CYCLE_TIME(0));
+ DUMPREG(RFBI_DATA_CYCLE1(0));
+ DUMPREG(RFBI_DATA_CYCLE2(0));
+ DUMPREG(RFBI_DATA_CYCLE3(0));
+
+ DUMPREG(RFBI_CONFIG(1));
+ DUMPREG(RFBI_ONOFF_TIME(1));
+ DUMPREG(RFBI_CYCLE_TIME(1));
+ DUMPREG(RFBI_DATA_CYCLE1(1));
+ DUMPREG(RFBI_DATA_CYCLE2(1));
+ DUMPREG(RFBI_DATA_CYCLE3(1));
+
+ DUMPREG(RFBI_VSYNC_WIDTH);
+ DUMPREG(RFBI_HSYNC_WIDTH);
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+int rfbi_init(void)
+{
+ u32 rev;
+ u32 l;
+
+ spin_lock_init(&rfbi.cmd_lock);
+ rfbi.cmd_fifo = kfifo_alloc(RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL,
+ &rfbi.cmd_lock);
+ if (IS_ERR(rfbi.cmd_fifo))
+ return -ENOMEM;
+
+ init_completion(&rfbi.cmd_done);
+ atomic_set(&rfbi.cmd_fifo_full, 0);
+ atomic_set(&rfbi.cmd_pending, 0);
+
+ rfbi.base = ioremap(RFBI_BASE, SZ_256);
+ if (!rfbi.base) {
+ DSSERR("can't ioremap RFBI\n");
+ return -ENOMEM;
+ }
+
+ rfbi_enable_clocks(1);
+
+ msleep(10);
+
+ rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000;
+
+ /* Enable autoidle and smart-idle */
+ l = rfbi_read_reg(RFBI_SYSCONFIG);
+ l |= (1 << 0) | (2 << 3);
+ rfbi_write_reg(RFBI_SYSCONFIG, l);
+
+ rev = rfbi_read_reg(RFBI_REVISION);
+ printk(KERN_INFO "OMAP RFBI rev %d.%d\n",
+ FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+ rfbi_enable_clocks(0);
+
+ return 0;
+}
+
+void rfbi_exit(void)
+{
+ DSSDBG("rfbi_exit\n");
+
+ kfifo_free(rfbi.cmd_fifo);
+
+ iounmap(rfbi.base);
+}
+
+/* struct omap_display support */
+static int rfbi_display_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ int rfbi_module;
+
+ if (w == 0 || h == 0)
+ return 0;
+
+ rfbi_module = rfbi_find_display(dssdev);
+
+ rfbi_push_update(rfbi_module, x, y, w, h);
+
+ return 0;
+}
+
+static int rfbi_display_sync(struct omap_dss_device *dssdev)
+{
+ struct completion sync_comp;
+ int rfbi_module;
+
+ rfbi_module = rfbi_find_display(dssdev);
+
+ init_completion(&sync_comp);
+ rfbi_push_sync(rfbi_module, &sync_comp);
+ DSSDBG("Waiting for SYNC to happen...\n");
+ wait_for_completion(&sync_comp);
+ DSSDBG("Released from SYNC\n");
+ return 0;
+}
+
+static int rfbi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+ dssdev->driver->enable_te(dssdev, enable);
+ return 0;
+}
+
+static int rfbi_display_enable(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ r = omap_dss_start_device(dssdev);
+ if (r) {
+ DSSERR("failed to start device\n");
+ goto err0;
+ }
+
+ r = omap_dispc_register_isr(framedone_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+ if (r) {
+ DSSERR("can't get FRAMEDONE irq\n");
+ goto err1;
+ }
+
+ dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
+
+ dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI);
+
+ dispc_set_tft_data_lines(dssdev->ctrl.pixel_size);
+
+ rfbi_configure(dssdev->phy.rfbi.channel,
+ dssdev->ctrl.pixel_size,
+ dssdev->phy.rfbi.data_lines);
+
+ rfbi_set_timings(dssdev->phy.rfbi.channel,
+ &dssdev->ctrl.rfbi_timings);
+
+
+ if (dssdev->driver->enable) {
+ r = dssdev->driver->enable(dssdev);
+ if (r)
+ goto err2;
+ }
+
+ return 0;
+err2:
+ omap_dispc_unregister_isr(framedone_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+err1:
+ omap_dss_stop_device(dssdev);
+err0:
+ return r;
+}
+
+static void rfbi_display_disable(struct omap_dss_device *dssdev)
+{
+ dssdev->driver->disable(dssdev);
+ omap_dispc_unregister_isr(framedone_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+ omap_dss_stop_device(dssdev);
+}
+
+int rfbi_init_display(struct omap_dss_device *dssdev)
+{
+ dssdev->enable = rfbi_display_enable;
+ dssdev->disable = rfbi_display_disable;
+ dssdev->update = rfbi_display_update;
+ dssdev->sync = rfbi_display_sync;
+ dssdev->enable_te = rfbi_display_enable_te;
+
+ rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev;
+
+ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+
+ return 0;
+}
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
new file mode 100644
index 00000000000..c24f307d3da
--- /dev/null
+++ b/drivers/video/omap2/dss/sdi.c
@@ -0,0 +1,277 @@
+/*
+ * linux/drivers/video/omap2/dss/sdi.c
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "SDI"
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+static struct {
+ bool skip_init;
+ bool update_enabled;
+} sdi;
+
+static void sdi_basic_init(void)
+{
+ dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);
+
+ dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
+ dispc_set_tft_data_lines(24);
+ dispc_lcd_enable_signal_polarity(1);
+}
+
+static int sdi_display_enable(struct omap_dss_device *dssdev)
+{
+ struct omap_video_timings *t = &dssdev->panel.timings;
+ struct dss_clock_info dss_cinfo;
+ struct dispc_clock_info dispc_cinfo;
+ u16 lck_div, pck_div;
+ unsigned long fck;
+ unsigned long pck;
+ int r;
+
+ r = omap_dss_start_device(dssdev);
+ if (r) {
+ DSSERR("failed to start device\n");
+ goto err0;
+ }
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+ DSSERR("dssdev already enabled\n");
+ r = -EINVAL;
+ goto err1;
+ }
+
+ /* In case of skip_init sdi_init has already enabled the clocks */
+ if (!sdi.skip_init)
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ sdi_basic_init();
+
+ /* 15.5.9.1.2 */
+ dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;
+
+ dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,
+ dssdev->panel.acb);
+
+ if (!sdi.skip_init) {
+ r = dss_calc_clock_div(1, t->pixel_clock * 1000,
+ &dss_cinfo, &dispc_cinfo);
+ } else {
+ r = dss_get_clock_div(&dss_cinfo);
+ r = dispc_get_clock_div(&dispc_cinfo);
+ }
+
+ if (r)
+ goto err2;
+
+ fck = dss_cinfo.fck;
+ lck_div = dispc_cinfo.lck_div;
+ pck_div = dispc_cinfo.pck_div;
+
+ pck = fck / lck_div / pck_div / 1000;
+
+ if (pck != t->pixel_clock) {
+ DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
+ "got %lu kHz\n",
+ t->pixel_clock, pck);
+
+ t->pixel_clock = pck;
+ }
+
+
+ dispc_set_lcd_timings(t);
+
+ r = dss_set_clock_div(&dss_cinfo);
+ if (r)
+ goto err2;
+
+ r = dispc_set_clock_div(&dispc_cinfo);
+ if (r)
+ goto err2;
+
+ if (!sdi.skip_init) {
+ dss_sdi_init(dssdev->phy.sdi.datapairs);
+ r = dss_sdi_enable();
+ if (r)
+ goto err1;
+ mdelay(2);
+ }
+
+ dispc_enable_lcd_out(1);
+
+ if (dssdev->driver->enable) {
+ r = dssdev->driver->enable(dssdev);
+ if (r)
+ goto err3;
+ }
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ sdi.skip_init = 0;
+
+ return 0;
+err3:
+ dispc_enable_lcd_out(0);
+err2:
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+err1:
+ omap_dss_stop_device(dssdev);
+err0:
+ return r;
+}
+
+static int sdi_display_resume(struct omap_dss_device *dssdev);
+
+static void sdi_display_disable(struct omap_dss_device *dssdev)
+{
+ if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
+ return;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+ if (sdi_display_resume(dssdev))
+ return;
+
+ if (dssdev->driver->disable)
+ dssdev->driver->disable(dssdev);
+
+ dispc_enable_lcd_out(0);
+
+ dss_sdi_disable();
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ omap_dss_stop_device(dssdev);
+}
+
+static int sdi_display_suspend(struct omap_dss_device *dssdev)
+{
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EINVAL;
+
+ if (dssdev->driver->suspend)
+ dssdev->driver->suspend(dssdev);
+
+ dispc_enable_lcd_out(0);
+
+ dss_sdi_disable();
+
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ return 0;
+}
+
+static int sdi_display_resume(struct omap_dss_device *dssdev)
+{
+ int r;
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
+ return -EINVAL;
+
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ r = dss_sdi_enable();
+ if (r)
+ goto err;
+ mdelay(2);
+
+ dispc_enable_lcd_out(1);
+
+ if (dssdev->driver->resume)
+ dssdev->driver->resume(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ return 0;
+err:
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ return r;
+}
+
+static int sdi_display_set_update_mode(struct omap_dss_device *dssdev,
+ enum omap_dss_update_mode mode)
+{
+ if (mode == OMAP_DSS_UPDATE_MANUAL)
+ return -EINVAL;
+
+ if (mode == OMAP_DSS_UPDATE_DISABLED) {
+ dispc_enable_lcd_out(0);
+ sdi.update_enabled = 0;
+ } else {
+ dispc_enable_lcd_out(1);
+ sdi.update_enabled = 1;
+ }
+
+ return 0;
+}
+
+static enum omap_dss_update_mode sdi_display_get_update_mode(
+ struct omap_dss_device *dssdev)
+{
+ return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
+ OMAP_DSS_UPDATE_DISABLED;
+}
+
+static void sdi_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ *timings = dssdev->panel.timings;
+}
+
+int sdi_init_display(struct omap_dss_device *dssdev)
+{
+ DSSDBG("SDI init\n");
+
+ dssdev->enable = sdi_display_enable;
+ dssdev->disable = sdi_display_disable;
+ dssdev->suspend = sdi_display_suspend;
+ dssdev->resume = sdi_display_resume;
+ dssdev->set_update_mode = sdi_display_set_update_mode;
+ dssdev->get_update_mode = sdi_display_get_update_mode;
+ dssdev->get_timings = sdi_get_timings;
+
+ return 0;
+}
+
+int sdi_init(bool skip_init)
+{
+ /* we store this for first display enable, then clear it */
+ sdi.skip_init = skip_init;
+
+ /*
+ * Enable clocks already here, otherwise there would be a toggle
+ * of them until sdi_display_enable is called.
+ */
+ if (skip_init)
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+ return 0;
+}
+
+void sdi_exit(void)
+{
+}
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
new file mode 100644
index 00000000000..749a5a0f5be
--- /dev/null
+++ b/drivers/video/omap2/dss/venc.c
@@ -0,0 +1,797 @@
+/*
+ * linux/drivers/video/omap2/dss/venc.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * VENC settings from TI's DSS driver
+ *
+ * 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 "VENC"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+#define VENC_BASE 0x48050C00
+
+/* Venc registers */
+#define VENC_REV_ID 0x00
+#define VENC_STATUS 0x04
+#define VENC_F_CONTROL 0x08
+#define VENC_VIDOUT_CTRL 0x10
+#define VENC_SYNC_CTRL 0x14
+#define VENC_LLEN 0x1C
+#define VENC_FLENS 0x20
+#define VENC_HFLTR_CTRL 0x24
+#define VENC_CC_CARR_WSS_CARR 0x28
+#define VENC_C_PHASE 0x2C
+#define VENC_GAIN_U 0x30
+#define VENC_GAIN_V 0x34
+#define VENC_GAIN_Y 0x38
+#define VENC_BLACK_LEVEL 0x3C
+#define VENC_BLANK_LEVEL 0x40
+#define VENC_X_COLOR 0x44
+#define VENC_M_CONTROL 0x48
+#define VENC_BSTAMP_WSS_DATA 0x4C
+#define VENC_S_CARR 0x50
+#define VENC_LINE21 0x54
+#define VENC_LN_SEL 0x58
+#define VENC_L21__WC_CTL 0x5C
+#define VENC_HTRIGGER_VTRIGGER 0x60
+#define VENC_SAVID__EAVID 0x64
+#define VENC_FLEN__FAL 0x68
+#define VENC_LAL__PHASE_RESET 0x6C
+#define VENC_HS_INT_START_STOP_X 0x70
+#define VENC_HS_EXT_START_STOP_X 0x74
+#define VENC_VS_INT_START_X 0x78
+#define VENC_VS_INT_STOP_X__VS_INT_START_Y 0x7C
+#define VENC_VS_INT_STOP_Y__VS_EXT_START_X 0x80
+#define VENC_VS_EXT_STOP_X__VS_EXT_START_Y 0x84
+#define VENC_VS_EXT_STOP_Y 0x88
+#define VENC_AVID_START_STOP_X 0x90
+#define VENC_AVID_START_STOP_Y 0x94
+#define VENC_FID_INT_START_X__FID_INT_START_Y 0xA0
+#define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4
+#define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8
+#define VENC_TVDETGP_INT_START_STOP_X 0xB0
+#define VENC_TVDETGP_INT_START_STOP_Y 0xB4
+#define VENC_GEN_CTRL 0xB8
+#define VENC_OUTPUT_CONTROL 0xC4
+#define VENC_OUTPUT_TEST 0xC8
+#define VENC_DAC_B__DAC_C 0xC8
+
+struct venc_config {
+ u32 f_control;
+ u32 vidout_ctrl;
+ u32 sync_ctrl;
+ u32 llen;
+ u32 flens;
+ u32 hfltr_ctrl;
+ u32 cc_carr_wss_carr;
+ u32 c_phase;
+ u32 gain_u;
+ u32 gain_v;
+ u32 gain_y;
+ u32 black_level;
+ u32 blank_level;
+ u32 x_color;
+ u32 m_control;
+ u32 bstamp_wss_data;
+ u32 s_carr;
+ u32 line21;
+ u32 ln_sel;
+ u32 l21__wc_ctl;
+ u32 htrigger_vtrigger;
+ u32 savid__eavid;
+ u32 flen__fal;
+ u32 lal__phase_reset;
+ u32 hs_int_start_stop_x;
+ u32 hs_ext_start_stop_x;
+ u32 vs_int_start_x;
+ u32 vs_int_stop_x__vs_int_start_y;
+ u32 vs_int_stop_y__vs_ext_start_x;
+ u32 vs_ext_stop_x__vs_ext_start_y;
+ u32 vs_ext_stop_y;
+ u32 avid_start_stop_x;
+ u32 avid_start_stop_y;
+ u32 fid_int_start_x__fid_int_start_y;
+ u32 fid_int_offset_y__fid_ext_start_x;
+ u32 fid_ext_start_y__fid_ext_offset_y;
+ u32 tvdetgp_int_start_stop_x;
+ u32 tvdetgp_int_start_stop_y;
+ u32 gen_ctrl;
+};
+
+/* from TRM */
+static const struct venc_config venc_config_pal_trm = {
+ .f_control = 0,
+ .vidout_ctrl = 1,
+ .sync_ctrl = 0x40,
+ .llen = 0x35F, /* 863 */
+ .flens = 0x270, /* 624 */
+ .hfltr_ctrl = 0,
+ .cc_carr_wss_carr = 0x2F7225ED,
+ .c_phase = 0,
+ .gain_u = 0x111,
+ .gain_v = 0x181,
+ .gain_y = 0x140,
+ .black_level = 0x3B,
+ .blank_level = 0x3B,
+ .x_color = 0x7,
+ .m_control = 0x2,
+ .bstamp_wss_data = 0x3F,
+ .s_carr = 0x2A098ACB,
+ .line21 = 0,
+ .ln_sel = 0x01290015,
+ .l21__wc_ctl = 0x0000F603,
+ .htrigger_vtrigger = 0,
+
+ .savid__eavid = 0x06A70108,
+ .flen__fal = 0x00180270,
+ .lal__phase_reset = 0x00040135,
+ .hs_int_start_stop_x = 0x00880358,
+ .hs_ext_start_stop_x = 0x000F035F,
+ .vs_int_start_x = 0x01A70000,
+ .vs_int_stop_x__vs_int_start_y = 0x000001A7,
+ .vs_int_stop_y__vs_ext_start_x = 0x01AF0000,
+ .vs_ext_stop_x__vs_ext_start_y = 0x000101AF,
+ .vs_ext_stop_y = 0x00000025,
+ .avid_start_stop_x = 0x03530083,
+ .avid_start_stop_y = 0x026C002E,
+ .fid_int_start_x__fid_int_start_y = 0x0001008A,
+ .fid_int_offset_y__fid_ext_start_x = 0x002E0138,
+ .fid_ext_start_y__fid_ext_offset_y = 0x01380001,
+
+ .tvdetgp_int_start_stop_x = 0x00140001,
+ .tvdetgp_int_start_stop_y = 0x00010001,
+ .gen_ctrl = 0x00FF0000,
+};
+
+/* from TRM */
+static const struct venc_config venc_config_ntsc_trm = {
+ .f_control = 0,
+ .vidout_ctrl = 1,
+ .sync_ctrl = 0x8040,
+ .llen = 0x359,
+ .flens = 0x20C,
+ .hfltr_ctrl = 0,
+ .cc_carr_wss_carr = 0x043F2631,
+ .c_phase = 0,
+ .gain_u = 0x102,
+ .gain_v = 0x16C,
+ .gain_y = 0x12F,
+ .black_level = 0x43,
+ .blank_level = 0x38,
+ .x_color = 0x7,
+ .m_control = 0x1,
+ .bstamp_wss_data = 0x38,
+ .s_carr = 0x21F07C1F,
+ .line21 = 0,
+ .ln_sel = 0x01310011,
+ .l21__wc_ctl = 0x0000F003,
+ .htrigger_vtrigger = 0,
+
+ .savid__eavid = 0x069300F4,
+ .flen__fal = 0x0016020C,
+ .lal__phase_reset = 0x00060107,
+ .hs_int_start_stop_x = 0x008E0350,
+ .hs_ext_start_stop_x = 0x000F0359,
+ .vs_int_start_x = 0x01A00000,
+ .vs_int_stop_x__vs_int_start_y = 0x020701A0,
+ .vs_int_stop_y__vs_ext_start_x = 0x01AC0024,
+ .vs_ext_stop_x__vs_ext_start_y = 0x020D01AC,
+ .vs_ext_stop_y = 0x00000006,
+ .avid_start_stop_x = 0x03480078,
+ .avid_start_stop_y = 0x02060024,
+ .fid_int_start_x__fid_int_start_y = 0x0001008A,
+ .fid_int_offset_y__fid_ext_start_x = 0x01AC0106,
+ .fid_ext_start_y__fid_ext_offset_y = 0x01060006,
+
+ .tvdetgp_int_start_stop_x = 0x00140001,
+ .tvdetgp_int_start_stop_y = 0x00010001,
+ .gen_ctrl = 0x00F90000,
+};
+
+static const struct venc_config venc_config_pal_bdghi = {
+ .f_control = 0,
+ .vidout_ctrl = 0,
+ .sync_ctrl = 0,
+ .hfltr_ctrl = 0,
+ .x_color = 0,
+ .line21 = 0,
+ .ln_sel = 21,
+ .htrigger_vtrigger = 0,
+ .tvdetgp_int_start_stop_x = 0x00140001,
+ .tvdetgp_int_start_stop_y = 0x00010001,
+ .gen_ctrl = 0x00FB0000,
+
+ .llen = 864-1,
+ .flens = 625-1,
+ .cc_carr_wss_carr = 0x2F7625ED,
+ .c_phase = 0xDF,
+ .gain_u = 0x111,
+ .gain_v = 0x181,
+ .gain_y = 0x140,
+ .black_level = 0x3e,
+ .blank_level = 0x3e,
+ .m_control = 0<<2 | 1<<1,
+ .bstamp_wss_data = 0x42,
+ .s_carr = 0x2a098acb,
+ .l21__wc_ctl = 0<<13 | 0x16<<8 | 0<<0,
+ .savid__eavid = 0x06A70108,
+ .flen__fal = 23<<16 | 624<<0,
+ .lal__phase_reset = 2<<17 | 310<<0,
+ .hs_int_start_stop_x = 0x00920358,
+ .hs_ext_start_stop_x = 0x000F035F,
+ .vs_int_start_x = 0x1a7<<16,
+ .vs_int_stop_x__vs_int_start_y = 0x000601A7,
+ .vs_int_stop_y__vs_ext_start_x = 0x01AF0036,
+ .vs_ext_stop_x__vs_ext_start_y = 0x27101af,
+ .vs_ext_stop_y = 0x05,
+ .avid_start_stop_x = 0x03530082,
+ .avid_start_stop_y = 0x0270002E,
+ .fid_int_start_x__fid_int_start_y = 0x0005008A,
+ .fid_int_offset_y__fid_ext_start_x = 0x002E0138,
+ .fid_ext_start_y__fid_ext_offset_y = 0x01380005,
+};
+
+const struct omap_video_timings omap_dss_pal_timings = {
+ .x_res = 720,
+ .y_res = 574,
+ .pixel_clock = 13500,
+ .hsw = 64,
+ .hfp = 12,
+ .hbp = 68,
+ .vsw = 5,
+ .vfp = 5,
+ .vbp = 41,
+};
+EXPORT_SYMBOL(omap_dss_pal_timings);
+
+const struct omap_video_timings omap_dss_ntsc_timings = {
+ .x_res = 720,
+ .y_res = 482,
+ .pixel_clock = 13500,
+ .hsw = 64,
+ .hfp = 16,
+ .hbp = 58,
+ .vsw = 6,
+ .vfp = 6,
+ .vbp = 31,
+};
+EXPORT_SYMBOL(omap_dss_ntsc_timings);
+
+static struct {
+ void __iomem *base;
+ struct mutex venc_lock;
+ u32 wss_data;
+ struct regulator *vdda_dac_reg;
+} venc;
+
+static inline void venc_write_reg(int idx, u32 val)
+{
+ __raw_writel(val, venc.base + idx);
+}
+
+static inline u32 venc_read_reg(int idx)
+{
+ u32 l = __raw_readl(venc.base + idx);
+ return l;
+}
+
+static void venc_write_config(const struct venc_config *config)
+{
+ DSSDBG("write venc conf\n");
+
+ venc_write_reg(VENC_LLEN, config->llen);
+ venc_write_reg(VENC_FLENS, config->flens);
+ venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr);
+ venc_write_reg(VENC_C_PHASE, config->c_phase);
+ venc_write_reg(VENC_GAIN_U, config->gain_u);
+ venc_write_reg(VENC_GAIN_V, config->gain_v);
+ venc_write_reg(VENC_GAIN_Y, config->gain_y);
+ venc_write_reg(VENC_BLACK_LEVEL, config->black_level);
+ venc_write_reg(VENC_BLANK_LEVEL, config->blank_level);
+ venc_write_reg(VENC_M_CONTROL, config->m_control);
+ venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
+ venc.wss_data);
+ venc_write_reg(VENC_S_CARR, config->s_carr);
+ venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl);
+ venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid);
+ venc_write_reg(VENC_FLEN__FAL, config->flen__fal);
+ venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset);
+ venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x);
+ venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x);
+ venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x);
+ venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y,
+ config->vs_int_stop_x__vs_int_start_y);
+ venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X,
+ config->vs_int_stop_y__vs_ext_start_x);
+ venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y,
+ config->vs_ext_stop_x__vs_ext_start_y);
+ venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y);
+ venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x);
+ venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y);
+ venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y,
+ config->fid_int_start_x__fid_int_start_y);
+ venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X,
+ config->fid_int_offset_y__fid_ext_start_x);
+ venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y,
+ config->fid_ext_start_y__fid_ext_offset_y);
+
+ venc_write_reg(VENC_DAC_B__DAC_C, venc_read_reg(VENC_DAC_B__DAC_C));
+ venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl);
+ venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl);
+ venc_write_reg(VENC_X_COLOR, config->x_color);
+ venc_write_reg(VENC_LINE21, config->line21);
+ venc_write_reg(VENC_LN_SEL, config->ln_sel);
+ venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger);
+ venc_write_reg(VENC_TVDETGP_INT_START_STOP_X,
+ config->tvdetgp_int_start_stop_x);
+ venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y,
+ config->tvdetgp_int_start_stop_y);
+ venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl);
+ venc_write_reg(VENC_F_CONTROL, config->f_control);
+ venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl);
+}
+
+static void venc_reset(void)
+{
+ int t = 1000;
+
+ venc_write_reg(VENC_F_CONTROL, 1<<8);
+ while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) {
+ if (--t == 0) {
+ DSSERR("Failed to reset venc\n");
+ return;
+ }
+ }
+
+ /* the magical sleep that makes things work */
+ msleep(20);
+}
+
+static void venc_enable_clocks(int enable)
+{
+ if (enable)
+ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M |
+ DSS_CLK_96M);
+ else
+ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M |
+ DSS_CLK_96M);
+}
+
+static const struct venc_config *venc_timings_to_config(
+ struct omap_video_timings *timings)
+{
+ if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0)
+ return &venc_config_pal_trm;
+
+ if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0)
+ return &venc_config_ntsc_trm;
+
+ BUG();
+}
+
+
+
+
+
+/* driver */
+static int venc_panel_probe(struct omap_dss_device *dssdev)
+{
+ dssdev->panel.timings = omap_dss_pal_timings;
+
+ return 0;
+}
+
+static void venc_panel_remove(struct omap_dss_device *dssdev)
+{
+}
+
+static int venc_panel_enable(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ /* wait couple of vsyncs until enabling the LCD */
+ msleep(50);
+
+ if (dssdev->platform_enable)
+ r = dssdev->platform_enable(dssdev);
+
+ return r;
+}
+
+static void venc_panel_disable(struct omap_dss_device *dssdev)
+{
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ /* wait at least 5 vsyncs after disabling the LCD */
+
+ msleep(100);
+}
+
+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 struct omap_dss_driver venc_driver = {
+ .probe = venc_panel_probe,
+ .remove = venc_panel_remove,
+
+ .enable = venc_panel_enable,
+ .disable = venc_panel_disable,
+ .suspend = venc_panel_suspend,
+ .resume = venc_panel_resume,
+
+ .driver = {
+ .name = "venc",
+ .owner = THIS_MODULE,
+ },
+};
+/* driver end */
+
+
+
+int venc_init(struct platform_device *pdev)
+{
+ u8 rev_id;
+
+ mutex_init(&venc.venc_lock);
+
+ venc.wss_data = 0;
+
+ venc.base = ioremap(VENC_BASE, SZ_1K);
+ if (!venc.base) {
+ DSSERR("can't ioremap VENC\n");
+ return -ENOMEM;
+ }
+
+ venc.vdda_dac_reg = regulator_get(&pdev->dev, "vdda_dac");
+ if (IS_ERR(venc.vdda_dac_reg)) {
+ iounmap(venc.base);
+ DSSERR("can't get VDDA_DAC regulator\n");
+ return PTR_ERR(venc.vdda_dac_reg);
+ }
+
+ venc_enable_clocks(1);
+
+ rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
+ printk(KERN_INFO "OMAP VENC rev %d\n", rev_id);
+
+ venc_enable_clocks(0);
+
+ return omap_dss_register_driver(&venc_driver);
+}
+
+void venc_exit(void)
+{
+ omap_dss_unregister_driver(&venc_driver);
+
+ regulator_put(venc.vdda_dac_reg);
+
+ iounmap(venc.base);
+}
+
+static void venc_power_on(struct omap_dss_device *dssdev)
+{
+ u32 l;
+
+ venc_enable_clocks(1);
+
+ venc_reset();
+ venc_write_config(venc_timings_to_config(&dssdev->panel.timings));
+
+ dss_set_venc_output(dssdev->phy.venc.type);
+ dss_set_dac_pwrdn_bgz(1);
+
+ l = 0;
+
+ if (dssdev->phy.venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE)
+ l |= 1 << 1;
+ else /* S-Video */
+ l |= (1 << 0) | (1 << 2);
+
+ if (dssdev->phy.venc.invert_polarity == false)
+ l |= 1 << 3;
+
+ venc_write_reg(VENC_OUTPUT_CONTROL, l);
+
+ dispc_set_digit_size(dssdev->panel.timings.x_res,
+ dssdev->panel.timings.y_res/2);
+
+ regulator_enable(venc.vdda_dac_reg);
+
+ if (dssdev->platform_enable)
+ dssdev->platform_enable(dssdev);
+
+ dispc_enable_digit_out(1);
+}
+
+static void venc_power_off(struct omap_dss_device *dssdev)
+{
+ venc_write_reg(VENC_OUTPUT_CONTROL, 0);
+ dss_set_dac_pwrdn_bgz(0);
+
+ dispc_enable_digit_out(0);
+
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ regulator_disable(venc.vdda_dac_reg);
+
+ venc_enable_clocks(0);
+}
+
+static int venc_enable_display(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ DSSDBG("venc_enable_display\n");
+
+ mutex_lock(&venc.venc_lock);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+ r = -EINVAL;
+ goto err;
+ }
+
+ venc_power_on(dssdev);
+
+ venc.wss_data = 0;
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+err:
+ mutex_unlock(&venc.venc_lock);
+
+ return r;
+}
+
+static void venc_disable_display(struct omap_dss_device *dssdev)
+{
+ DSSDBG("venc_disable_display\n");
+
+ mutex_lock(&venc.venc_lock);
+
+ 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;
+ }
+
+ venc_power_off(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+end:
+ mutex_unlock(&venc.venc_lock);
+}
+
+static int venc_display_suspend(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ DSSDBG("venc_display_suspend\n");
+
+ mutex_lock(&venc.venc_lock);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
+ r = -EINVAL;
+ goto err;
+ }
+
+ venc_power_off(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+err:
+ mutex_unlock(&venc.venc_lock);
+
+ return r;
+}
+
+static int venc_display_resume(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+
+ DSSDBG("venc_display_resume\n");
+
+ mutex_lock(&venc.venc_lock);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+ r = -EINVAL;
+ goto err;
+ }
+
+ venc_power_on(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+err:
+ mutex_unlock(&venc.venc_lock);
+
+ return r;
+}
+
+static void venc_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ *timings = dssdev->panel.timings;
+}
+
+static void venc_set_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ DSSDBG("venc_set_timings\n");
+
+ /* Reset WSS data when the TV standard changes. */
+ if (memcmp(&dssdev->panel.timings, timings, sizeof(*timings)))
+ venc.wss_data = 0;
+
+ dssdev->panel.timings = *timings;
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ /* turn the venc off and on to get new timings to use */
+ venc_disable_display(dssdev);
+ venc_enable_display(dssdev);
+ }
+}
+
+static int venc_check_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ DSSDBG("venc_check_timings\n");
+
+ if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0)
+ return 0;
+
+ if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0)
+ return 0;
+
+ return -EINVAL;
+}
+
+static u32 venc_get_wss(struct omap_dss_device *dssdev)
+{
+ /* Invert due to VENC_L21_WC_CTL:INV=1 */
+ return (venc.wss_data >> 8) ^ 0xfffff;
+}
+
+static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss)
+{
+ const struct venc_config *config;
+
+ DSSDBG("venc_set_wss\n");
+
+ mutex_lock(&venc.venc_lock);
+
+ config = venc_timings_to_config(&dssdev->panel.timings);
+
+ /* Invert due to VENC_L21_WC_CTL:INV=1 */
+ venc.wss_data = (wss ^ 0xfffff) << 8;
+
+ venc_enable_clocks(1);
+
+ venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
+ venc.wss_data);
+
+ venc_enable_clocks(0);
+
+ mutex_unlock(&venc.venc_lock);
+
+ return 0;
+}
+
+static enum omap_dss_update_mode venc_display_get_update_mode(
+ struct omap_dss_device *dssdev)
+{
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return OMAP_DSS_UPDATE_AUTO;
+ else
+ return OMAP_DSS_UPDATE_DISABLED;
+}
+
+int venc_init_display(struct omap_dss_device *dssdev)
+{
+ DSSDBG("init_display\n");
+
+ dssdev->enable = venc_enable_display;
+ dssdev->disable = venc_disable_display;
+ dssdev->suspend = venc_display_suspend;
+ dssdev->resume = venc_display_resume;
+ dssdev->get_timings = venc_get_timings;
+ dssdev->set_timings = venc_set_timings;
+ dssdev->check_timings = venc_check_timings;
+ dssdev->get_wss = venc_get_wss;
+ dssdev->set_wss = venc_set_wss;
+ dssdev->get_update_mode = venc_display_get_update_mode;
+
+ return 0;
+}
+
+void venc_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r))
+
+ venc_enable_clocks(1);
+
+ DUMPREG(VENC_F_CONTROL);
+ DUMPREG(VENC_VIDOUT_CTRL);
+ DUMPREG(VENC_SYNC_CTRL);
+ DUMPREG(VENC_LLEN);
+ DUMPREG(VENC_FLENS);
+ DUMPREG(VENC_HFLTR_CTRL);
+ DUMPREG(VENC_CC_CARR_WSS_CARR);
+ DUMPREG(VENC_C_PHASE);
+ DUMPREG(VENC_GAIN_U);
+ DUMPREG(VENC_GAIN_V);
+ DUMPREG(VENC_GAIN_Y);
+ DUMPREG(VENC_BLACK_LEVEL);
+ DUMPREG(VENC_BLANK_LEVEL);
+ DUMPREG(VENC_X_COLOR);
+ DUMPREG(VENC_M_CONTROL);
+ DUMPREG(VENC_BSTAMP_WSS_DATA);
+ DUMPREG(VENC_S_CARR);
+ DUMPREG(VENC_LINE21);
+ DUMPREG(VENC_LN_SEL);
+ DUMPREG(VENC_L21__WC_CTL);
+ DUMPREG(VENC_HTRIGGER_VTRIGGER);
+ DUMPREG(VENC_SAVID__EAVID);
+ DUMPREG(VENC_FLEN__FAL);
+ DUMPREG(VENC_LAL__PHASE_RESET);
+ DUMPREG(VENC_HS_INT_START_STOP_X);
+ DUMPREG(VENC_HS_EXT_START_STOP_X);
+ DUMPREG(VENC_VS_INT_START_X);
+ DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y);
+ DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X);
+ DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y);
+ DUMPREG(VENC_VS_EXT_STOP_Y);
+ DUMPREG(VENC_AVID_START_STOP_X);
+ DUMPREG(VENC_AVID_START_STOP_Y);
+ DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y);
+ DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X);
+ DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y);
+ DUMPREG(VENC_TVDETGP_INT_START_STOP_X);
+ DUMPREG(VENC_TVDETGP_INT_START_STOP_Y);
+ DUMPREG(VENC_GEN_CTRL);
+ DUMPREG(VENC_OUTPUT_CONTROL);
+ DUMPREG(VENC_OUTPUT_TEST);
+
+ venc_enable_clocks(0);
+
+#undef DUMPREG
+}
diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig
new file mode 100644
index 00000000000..bb694cc52a5
--- /dev/null
+++ b/drivers/video/omap2/omapfb/Kconfig
@@ -0,0 +1,37 @@
+menuconfig FB_OMAP2
+ tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)"
+ depends on FB && OMAP2_DSS
+
+ select OMAP2_VRAM
+ select OMAP2_VRFB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ Frame buffer driver for OMAP2/3 based boards.
+
+config FB_OMAP2_DEBUG_SUPPORT
+ bool "Debug support for OMAP2/3 FB"
+ default y
+ depends on FB_OMAP2
+ help
+ Support for debug output. You have to enable the actual printing
+ with debug module parameter.
+
+config FB_OMAP2_FORCE_AUTO_UPDATE
+ bool "Force main display to automatic update mode"
+ depends on FB_OMAP2
+ help
+ Forces main display to automatic update mode (if possible),
+ and also enables tearsync (if possible). By default
+ displays that support manual update are started in manual
+ update mode.
+
+config FB_OMAP2_NUM_FBS
+ int "Number of framebuffers"
+ range 1 10
+ default 3
+ depends on FB_OMAP2
+ help
+ Select the number of framebuffers created. OMAP2/3 has 3 overlays
+ so normally this would be 3.
diff --git a/drivers/video/omap2/omapfb/Makefile b/drivers/video/omap2/omapfb/Makefile
new file mode 100644
index 00000000000..51c2e00d9bf
--- /dev/null
+++ b/drivers/video/omap2/omapfb/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FB_OMAP2) += omapfb.o
+omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o
diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
new file mode 100644
index 00000000000..4c4bafdfaa4
--- /dev/null
+++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c
@@ -0,0 +1,755 @@
+/*
+ * linux/drivers/video/omap2/omapfb-ioctl.c
+ *
+ * Copyright (C) 2008 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/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/omapfb.h>
+#include <linux/vmalloc.h>
+
+#include <plat/display.h>
+#include <plat/vrfb.h>
+#include <plat/vram.h>
+
+#include "omapfb.h"
+
+static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_overlay *ovl;
+ struct omap_overlay_info info;
+ int r = 0;
+
+ DBG("omapfb_setup_plane\n");
+
+ if (ofbi->num_overlays != 1) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ /* XXX uses only the first overlay */
+ ovl = ofbi->overlays[0];
+
+ if (pi->enabled && !ofbi->region.size) {
+ /*
+ * This plane's memory was freed, can't enable it
+ * until it's reallocated.
+ */
+ r = -EINVAL;
+ goto out;
+ }
+
+ ovl->get_overlay_info(ovl, &info);
+
+ info.pos_x = pi->pos_x;
+ info.pos_y = pi->pos_y;
+ info.out_width = pi->out_width;
+ info.out_height = pi->out_height;
+ info.enabled = pi->enabled;
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r)
+ goto out;
+
+ if (ovl->manager) {
+ r = ovl->manager->apply(ovl->manager);
+ if (r)
+ goto out;
+ }
+
+out:
+ if (r)
+ dev_err(fbdev->dev, "setup_plane failed\n");
+ return r;
+}
+
+static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+
+ if (ofbi->num_overlays != 1) {
+ memset(pi, 0, sizeof(*pi));
+ } else {
+ struct omap_overlay_info *ovli;
+ struct omap_overlay *ovl;
+
+ ovl = ofbi->overlays[0];
+ ovli = &ovl->info;
+
+ pi->pos_x = ovli->pos_x;
+ pi->pos_y = ovli->pos_y;
+ pi->enabled = ovli->enabled;
+ pi->channel_out = 0; /* xxx */
+ pi->mirror = 0;
+ pi->out_width = ovli->out_width;
+ pi->out_height = ovli->out_height;
+ }
+
+ return 0;
+}
+
+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 omapfb2_mem_region *rg;
+ int r, i;
+ size_t size;
+
+ if (mi->type > OMAPFB_MEMTYPE_MAX)
+ return -EINVAL;
+
+ size = PAGE_ALIGN(mi->size);
+
+ rg = &ofbi->region;
+
+ for (i = 0; i < ofbi->num_overlays; i++) {
+ if (ofbi->overlays[i]->info.enabled)
+ return -EBUSY;
+ }
+
+ if (rg->size != size || rg->type != mi->type) {
+ r = omapfb_realloc_fbmem(fbi, size, mi->type);
+ if (r) {
+ dev_err(fbdev->dev, "realloc fbmem failed\n");
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_mem_region *rg;
+
+ rg = &ofbi->region;
+ memset(mi, 0, sizeof(*mi));
+
+ mi->size = rg->size;
+ mi->type = rg->type;
+
+ return 0;
+}
+
+static int omapfb_update_window_nolock(struct fb_info *fbi,
+ u32 x, u32 y, u32 w, u32 h)
+{
+ struct omap_dss_device *display = fb2display(fbi);
+ u16 dw, dh;
+
+ if (!display)
+ return 0;
+
+ if (w == 0 || h == 0)
+ return 0;
+
+ display->get_resolution(display, &dw, &dh);
+
+ if (x + w > dw || y + h > dh)
+ return -EINVAL;
+
+ return display->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;
+
+ omapfb_lock(fbdev);
+ lock_fb_info(fbi);
+
+ r = omapfb_update_window_nolock(fbi, x, y, w, h);
+
+ unlock_fb_info(fbi);
+ omapfb_unlock(fbdev);
+
+ return r;
+}
+EXPORT_SYMBOL(omapfb_update_window);
+
+static int omapfb_set_update_mode(struct fb_info *fbi,
+ enum omapfb_update_mode mode)
+{
+ struct omap_dss_device *display = fb2display(fbi);
+ enum omap_dss_update_mode um;
+ int r;
+
+ if (!display || !display->set_update_mode)
+ return -EINVAL;
+
+ switch (mode) {
+ case OMAPFB_UPDATE_DISABLED:
+ um = OMAP_DSS_UPDATE_DISABLED;
+ break;
+
+ case OMAPFB_AUTO_UPDATE:
+ um = OMAP_DSS_UPDATE_AUTO;
+ break;
+
+ case OMAPFB_MANUAL_UPDATE:
+ um = OMAP_DSS_UPDATE_MANUAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ r = display->set_update_mode(display, um);
+
+ return r;
+}
+
+static int omapfb_get_update_mode(struct fb_info *fbi,
+ enum omapfb_update_mode *mode)
+{
+ struct omap_dss_device *display = fb2display(fbi);
+ enum omap_dss_update_mode m;
+
+ if (!display || !display->get_update_mode)
+ return -EINVAL;
+
+ m = display->get_update_mode(display);
+
+ switch (m) {
+ case OMAP_DSS_UPDATE_DISABLED:
+ *mode = OMAPFB_UPDATE_DISABLED;
+ break;
+ case OMAP_DSS_UPDATE_AUTO:
+ *mode = OMAPFB_AUTO_UPDATE;
+ break;
+ case OMAP_DSS_UPDATE_MANUAL:
+ *mode = OMAPFB_MANUAL_UPDATE;
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+/* XXX this color key handling is a hack... */
+static struct omapfb_color_key omapfb_color_keys[2];
+
+static int _omapfb_set_color_key(struct omap_overlay_manager *mgr,
+ struct omapfb_color_key *ck)
+{
+ struct omap_overlay_manager_info info;
+ enum omap_dss_trans_key_type kt;
+ int r;
+
+ mgr->get_manager_info(mgr, &info);
+
+ if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) {
+ info.trans_enabled = false;
+ omapfb_color_keys[mgr->id] = *ck;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+
+ return r;
+ }
+
+ switch (ck->key_type) {
+ case OMAPFB_COLOR_KEY_GFX_DST:
+ kt = OMAP_DSS_COLOR_KEY_GFX_DST;
+ break;
+ case OMAPFB_COLOR_KEY_VID_SRC:
+ kt = OMAP_DSS_COLOR_KEY_VID_SRC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ info.default_color = ck->background;
+ info.trans_key = ck->trans_key;
+ info.trans_key_type = kt;
+ info.trans_enabled = true;
+
+ omapfb_color_keys[mgr->id] = *ck;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+
+ return r;
+}
+
+static int omapfb_set_color_key(struct fb_info *fbi,
+ struct omapfb_color_key *ck)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ int r;
+ int i;
+ struct omap_overlay_manager *mgr = NULL;
+
+ omapfb_lock(fbdev);
+
+ for (i = 0; i < ofbi->num_overlays; i++) {
+ if (ofbi->overlays[i]->manager) {
+ mgr = ofbi->overlays[i]->manager;
+ break;
+ }
+ }
+
+ if (!mgr) {
+ r = -EINVAL;
+ goto err;
+ }
+
+ r = _omapfb_set_color_key(mgr, ck);
+err:
+ omapfb_unlock(fbdev);
+
+ return r;
+}
+
+static int omapfb_get_color_key(struct fb_info *fbi,
+ struct omapfb_color_key *ck)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_overlay_manager *mgr = NULL;
+ int r = 0;
+ int i;
+
+ omapfb_lock(fbdev);
+
+ for (i = 0; i < ofbi->num_overlays; i++) {
+ if (ofbi->overlays[i]->manager) {
+ mgr = ofbi->overlays[i]->manager;
+ break;
+ }
+ }
+
+ if (!mgr) {
+ r = -EINVAL;
+ goto err;
+ }
+
+ *ck = omapfb_color_keys[mgr->id];
+err:
+ omapfb_unlock(fbdev);
+
+ return r;
+}
+
+static int omapfb_memory_read(struct fb_info *fbi,
+ struct omapfb_memory_read *mr)
+{
+ struct omap_dss_device *display = fb2display(fbi);
+ void *buf;
+ int r;
+
+ if (!display || !display->memory_read)
+ return -ENOENT;
+
+ if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size))
+ return -EFAULT;
+
+ if (mr->w * mr->h * 3 > mr->buffer_size)
+ return -EINVAL;
+
+ buf = vmalloc(mr->buffer_size);
+ if (!buf) {
+ DBG("vmalloc failed\n");
+ return -ENOMEM;
+ }
+
+ r = display->memory_read(display, buf, mr->buffer_size,
+ mr->x, mr->y, mr->w, mr->h);
+
+ if (r > 0) {
+ if (copy_to_user(mr->buffer, buf, mr->buffer_size))
+ r = -EFAULT;
+ }
+
+ vfree(buf);
+
+ return r;
+}
+
+static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev,
+ struct omapfb_ovl_colormode *mode)
+{
+ int ovl_idx = mode->overlay_idx;
+ int mode_idx = mode->mode_idx;
+ struct omap_overlay *ovl;
+ enum omap_color_mode supported_modes;
+ struct fb_var_screeninfo var;
+ int i;
+
+ if (ovl_idx >= fbdev->num_overlays)
+ return -ENODEV;
+ ovl = fbdev->overlays[ovl_idx];
+ supported_modes = ovl->supported_modes;
+
+ mode_idx = mode->mode_idx;
+
+ for (i = 0; i < sizeof(supported_modes) * 8; i++) {
+ if (!(supported_modes & (1 << i)))
+ continue;
+ /*
+ * It's possible that the FB doesn't support a mode
+ * that is supported by the overlay, so call the
+ * following here.
+ */
+ if (dss_mode_to_fb_mode(1 << i, &var) < 0)
+ continue;
+
+ mode_idx--;
+ if (mode_idx < 0)
+ break;
+ }
+
+ if (i == sizeof(supported_modes) * 8)
+ return -ENOENT;
+
+ mode->bits_per_pixel = var.bits_per_pixel;
+ mode->nonstd = var.nonstd;
+ mode->red = var.red;
+ mode->green = var.green;
+ mode->blue = var.blue;
+ mode->transp = var.transp;
+
+ return 0;
+}
+
+static int omapfb_wait_for_go(struct fb_info *fbi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ int r = 0;
+ int i;
+
+ for (i = 0; i < ofbi->num_overlays; ++i) {
+ struct omap_overlay *ovl = ofbi->overlays[i];
+ r = ovl->wait_for_go(ovl);
+ if (r)
+ break;
+ }
+
+ return r;
+}
+
+int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_dss_device *display = fb2display(fbi);
+
+ union {
+ struct omapfb_update_window_old uwnd_o;
+ struct omapfb_update_window uwnd;
+ struct omapfb_plane_info plane_info;
+ struct omapfb_caps caps;
+ struct omapfb_mem_info mem_info;
+ struct omapfb_color_key color_key;
+ struct omapfb_ovl_colormode ovl_colormode;
+ enum omapfb_update_mode update_mode;
+ int test_num;
+ struct omapfb_memory_read memory_read;
+ struct omapfb_vram_info vram_info;
+ struct omapfb_tearsync_info tearsync_info;
+ } p;
+
+ int r = 0;
+
+ switch (cmd) {
+ case OMAPFB_SYNC_GFX:
+ DBG("ioctl SYNC_GFX\n");
+ if (!display || !display->sync) {
+ /* DSS1 never returns an error here, so we neither */
+ /*r = -EINVAL;*/
+ break;
+ }
+
+ r = display->sync(display);
+ break;
+
+ case OMAPFB_UPDATE_WINDOW_OLD:
+ DBG("ioctl UPDATE_WINDOW_OLD\n");
+ if (!display || !display->update) {
+ r = -EINVAL;
+ break;
+ }
+
+ if (copy_from_user(&p.uwnd_o,
+ (void __user *)arg,
+ sizeof(p.uwnd_o))) {
+ r = -EFAULT;
+ break;
+ }
+
+ r = omapfb_update_window_nolock(fbi, p.uwnd_o.x, p.uwnd_o.y,
+ p.uwnd_o.width, p.uwnd_o.height);
+ break;
+
+ case OMAPFB_UPDATE_WINDOW:
+ DBG("ioctl UPDATE_WINDOW\n");
+ if (!display || !display->update) {
+ r = -EINVAL;
+ break;
+ }
+
+ if (copy_from_user(&p.uwnd, (void __user *)arg,
+ sizeof(p.uwnd))) {
+ r = -EFAULT;
+ break;
+ }
+
+ r = omapfb_update_window_nolock(fbi, p.uwnd.x, p.uwnd.y,
+ p.uwnd.width, p.uwnd.height);
+ break;
+
+ case OMAPFB_SETUP_PLANE:
+ DBG("ioctl SETUP_PLANE\n");
+ if (copy_from_user(&p.plane_info, (void __user *)arg,
+ sizeof(p.plane_info)))
+ r = -EFAULT;
+ else
+ r = omapfb_setup_plane(fbi, &p.plane_info);
+ break;
+
+ case OMAPFB_QUERY_PLANE:
+ DBG("ioctl QUERY_PLANE\n");
+ r = omapfb_query_plane(fbi, &p.plane_info);
+ if (r < 0)
+ break;
+ if (copy_to_user((void __user *)arg, &p.plane_info,
+ sizeof(p.plane_info)))
+ r = -EFAULT;
+ break;
+
+ case OMAPFB_SETUP_MEM:
+ DBG("ioctl SETUP_MEM\n");
+ if (copy_from_user(&p.mem_info, (void __user *)arg,
+ sizeof(p.mem_info)))
+ r = -EFAULT;
+ else
+ r = omapfb_setup_mem(fbi, &p.mem_info);
+ break;
+
+ case OMAPFB_QUERY_MEM:
+ DBG("ioctl QUERY_MEM\n");
+ r = omapfb_query_mem(fbi, &p.mem_info);
+ if (r < 0)
+ break;
+ if (copy_to_user((void __user *)arg, &p.mem_info,
+ sizeof(p.mem_info)))
+ r = -EFAULT;
+ break;
+
+ case OMAPFB_GET_CAPS:
+ DBG("ioctl GET_CAPS\n");
+ if (!display) {
+ r = -EINVAL;
+ break;
+ }
+
+ memset(&p.caps, 0, sizeof(p.caps));
+ if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
+ p.caps.ctrl |= OMAPFB_CAPS_MANUAL_UPDATE;
+ if (display->caps & OMAP_DSS_DISPLAY_CAP_TEAR_ELIM)
+ p.caps.ctrl |= OMAPFB_CAPS_TEARSYNC;
+
+ if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
+ r = -EFAULT;
+ break;
+
+ case OMAPFB_GET_OVERLAY_COLORMODE:
+ DBG("ioctl GET_OVERLAY_COLORMODE\n");
+ if (copy_from_user(&p.ovl_colormode, (void __user *)arg,
+ sizeof(p.ovl_colormode))) {
+ r = -EFAULT;
+ break;
+ }
+ r = omapfb_get_ovl_colormode(fbdev, &p.ovl_colormode);
+ if (r < 0)
+ break;
+ if (copy_to_user((void __user *)arg, &p.ovl_colormode,
+ sizeof(p.ovl_colormode)))
+ r = -EFAULT;
+ break;
+
+ case OMAPFB_SET_UPDATE_MODE:
+ DBG("ioctl SET_UPDATE_MODE\n");
+ if (get_user(p.update_mode, (int __user *)arg))
+ r = -EFAULT;
+ else
+ r = omapfb_set_update_mode(fbi, p.update_mode);
+ break;
+
+ case OMAPFB_GET_UPDATE_MODE:
+ DBG("ioctl GET_UPDATE_MODE\n");
+ r = omapfb_get_update_mode(fbi, &p.update_mode);
+ if (r)
+ break;
+ if (put_user(p.update_mode,
+ (enum omapfb_update_mode __user *)arg))
+ r = -EFAULT;
+ break;
+
+ case OMAPFB_SET_COLOR_KEY:
+ DBG("ioctl SET_COLOR_KEY\n");
+ if (copy_from_user(&p.color_key, (void __user *)arg,
+ sizeof(p.color_key)))
+ r = -EFAULT;
+ else
+ r = omapfb_set_color_key(fbi, &p.color_key);
+ break;
+
+ case OMAPFB_GET_COLOR_KEY:
+ DBG("ioctl GET_COLOR_KEY\n");
+ r = omapfb_get_color_key(fbi, &p.color_key);
+ if (r)
+ break;
+ if (copy_to_user((void __user *)arg, &p.color_key,
+ sizeof(p.color_key)))
+ r = -EFAULT;
+ break;
+
+ case OMAPFB_WAITFORVSYNC:
+ DBG("ioctl WAITFORVSYNC\n");
+ if (!display) {
+ r = -EINVAL;
+ break;
+ }
+
+ r = display->wait_vsync(display);
+ break;
+
+ case OMAPFB_WAITFORGO:
+ DBG("ioctl WAITFORGO\n");
+ if (!display) {
+ r = -EINVAL;
+ break;
+ }
+
+ r = omapfb_wait_for_go(fbi);
+ break;
+
+ /* LCD and CTRL tests do the same thing for backward
+ * compatibility */
+ case OMAPFB_LCD_TEST:
+ DBG("ioctl LCD_TEST\n");
+ if (get_user(p.test_num, (int __user *)arg)) {
+ r = -EFAULT;
+ break;
+ }
+ if (!display || !display->run_test) {
+ r = -EINVAL;
+ break;
+ }
+
+ r = display->run_test(display, p.test_num);
+
+ break;
+
+ case OMAPFB_CTRL_TEST:
+ DBG("ioctl CTRL_TEST\n");
+ if (get_user(p.test_num, (int __user *)arg)) {
+ r = -EFAULT;
+ break;
+ }
+ if (!display || !display->run_test) {
+ r = -EINVAL;
+ break;
+ }
+
+ r = display->run_test(display, p.test_num);
+
+ break;
+
+ case OMAPFB_MEMORY_READ:
+ DBG("ioctl MEMORY_READ\n");
+
+ if (copy_from_user(&p.memory_read, (void __user *)arg,
+ sizeof(p.memory_read))) {
+ r = -EFAULT;
+ break;
+ }
+
+ r = omapfb_memory_read(fbi, &p.memory_read);
+
+ 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;
+
+ if (copy_to_user((void __user *)arg, &p.vram_info,
+ sizeof(p.vram_info)))
+ r = -EFAULT;
+ break;
+ }
+
+ case OMAPFB_SET_TEARSYNC: {
+ DBG("ioctl SET_TEARSYNC\n");
+
+ if (copy_from_user(&p.tearsync_info, (void __user *)arg,
+ sizeof(p.tearsync_info))) {
+ r = -EFAULT;
+ break;
+ }
+
+ if (!display->enable_te) {
+ r = -ENODEV;
+ break;
+ }
+
+ r = display->enable_te(display, !!p.tearsync_info.enabled);
+
+ break;
+ }
+
+ default:
+ dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd);
+ r = -EINVAL;
+ }
+
+ if (r < 0)
+ DBG("ioctl failed: %d\n", r);
+
+ return r;
+}
+
+
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
new file mode 100644
index 00000000000..ef299839858
--- /dev/null
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -0,0 +1,2261 @@
+/*
+ * linux/drivers/video/omap2/omapfb-main.c
+ *
+ * Copyright (C) 2008 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/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/omapfb.h>
+
+#include <plat/display.h>
+#include <plat/vram.h>
+#include <plat/vrfb.h>
+
+#include "omapfb.h"
+
+#define MODULE_NAME "omapfb"
+
+#define OMAPFB_PLANE_XRES_MIN 8
+#define OMAPFB_PLANE_YRES_MIN 8
+
+static char *def_mode;
+static char *def_vram;
+static int def_vrfb;
+static int def_rotate;
+static int def_mirror;
+
+#ifdef DEBUG
+unsigned int omapfb_debug;
+module_param_named(debug, omapfb_debug, bool, 0644);
+static unsigned int omapfb_test_pattern;
+module_param_named(test, omapfb_test_pattern, bool, 0644);
+#endif
+
+static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi);
+
+#ifdef DEBUG
+static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color)
+{
+ struct fb_var_screeninfo *var = &fbi->var;
+ struct fb_fix_screeninfo *fix = &fbi->fix;
+ void __iomem *addr = fbi->screen_base;
+ const unsigned bytespp = var->bits_per_pixel >> 3;
+ const unsigned line_len = fix->line_length / bytespp;
+
+ int r = (color >> 16) & 0xff;
+ int g = (color >> 8) & 0xff;
+ int b = (color >> 0) & 0xff;
+
+ if (var->bits_per_pixel == 16) {
+ u16 __iomem *p = (u16 __iomem *)addr;
+ p += y * line_len + x;
+
+ r = r * 32 / 256;
+ g = g * 64 / 256;
+ b = b * 32 / 256;
+
+ __raw_writew((r << 11) | (g << 5) | (b << 0), p);
+ } else if (var->bits_per_pixel == 24) {
+ u8 __iomem *p = (u8 __iomem *)addr;
+ p += (y * line_len + x) * 3;
+
+ __raw_writeb(b, p + 0);
+ __raw_writeb(g, p + 1);
+ __raw_writeb(r, p + 2);
+ } else if (var->bits_per_pixel == 32) {
+ u32 __iomem *p = (u32 __iomem *)addr;
+ p += y * line_len + x;
+ __raw_writel(color, p);
+ }
+}
+
+static void fill_fb(struct fb_info *fbi)
+{
+ struct fb_var_screeninfo *var = &fbi->var;
+ const short w = var->xres_virtual;
+ const short h = var->yres_virtual;
+ void __iomem *addr = fbi->screen_base;
+ int y, x;
+
+ if (!addr)
+ return;
+
+ DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length);
+
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ if (x < 20 && y < 20)
+ draw_pixel(fbi, x, y, 0xffffff);
+ else if (x < 20 && (y > 20 && y < h - 20))
+ draw_pixel(fbi, x, y, 0xff);
+ else if (y < 20 && (x > 20 && x < w - 20))
+ draw_pixel(fbi, x, y, 0xff00);
+ else if (x > w - 20 && (y > 20 && y < h - 20))
+ draw_pixel(fbi, x, y, 0xff0000);
+ else if (y > h - 20 && (x > 20 && x < w - 20))
+ draw_pixel(fbi, x, y, 0xffff00);
+ else if (x == 20 || x == w - 20 ||
+ y == 20 || y == h - 20)
+ draw_pixel(fbi, x, y, 0xffffff);
+ else if (x == y || w - x == h - y)
+ draw_pixel(fbi, x, y, 0xff00ff);
+ else if (w - x == y || x == h - y)
+ draw_pixel(fbi, x, y, 0x00ffff);
+ else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) {
+ int t = x * 3 / w;
+ unsigned r = 0, g = 0, b = 0;
+ unsigned c;
+ if (var->bits_per_pixel == 16) {
+ if (t == 0)
+ b = (y % 32) * 256 / 32;
+ else if (t == 1)
+ g = (y % 64) * 256 / 64;
+ else if (t == 2)
+ r = (y % 32) * 256 / 32;
+ } else {
+ if (t == 0)
+ b = (y % 256);
+ else if (t == 1)
+ g = (y % 256);
+ else if (t == 2)
+ r = (y % 256);
+ }
+ c = (r << 16) | (g << 8) | (b << 0);
+ draw_pixel(fbi, x, y, c);
+ } else {
+ draw_pixel(fbi, x, y, 0);
+ }
+ }
+ }
+}
+#endif
+
+static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot)
+{
+ struct vrfb *vrfb = &ofbi->region.vrfb;
+ unsigned offset;
+
+ switch (rot) {
+ case FB_ROTATE_UR:
+ offset = 0;
+ break;
+ case FB_ROTATE_CW:
+ offset = vrfb->yoffset;
+ break;
+ case FB_ROTATE_UD:
+ offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset;
+ break;
+ case FB_ROTATE_CCW:
+ offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN;
+ break;
+ default:
+ BUG();
+ }
+
+ offset *= vrfb->bytespp;
+
+ return offset;
+}
+
+static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi, int rot)
+{
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+ return ofbi->region.vrfb.paddr[rot]
+ + omapfb_get_vrfb_offset(ofbi, rot);
+ } else {
+ return ofbi->region.paddr;
+ }
+}
+
+static u32 omapfb_get_region_paddr(struct omapfb_info *ofbi)
+{
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+ return ofbi->region.vrfb.paddr[0];
+ else
+ return ofbi->region.paddr;
+}
+
+static void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi)
+{
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+ return ofbi->region.vrfb.vaddr[0];
+ else
+ return ofbi->region.vaddr;
+}
+
+static struct omapfb_colormode omapfb_colormodes[] = {
+ {
+ .dssmode = OMAP_DSS_COLOR_UYVY,
+ .bits_per_pixel = 16,
+ .nonstd = OMAPFB_COLOR_YUV422,
+ }, {
+ .dssmode = OMAP_DSS_COLOR_YUV2,
+ .bits_per_pixel = 16,
+ .nonstd = OMAPFB_COLOR_YUY422,
+ }, {
+ .dssmode = OMAP_DSS_COLOR_ARGB16,
+ .bits_per_pixel = 16,
+ .red = { .length = 4, .offset = 8, .msb_right = 0 },
+ .green = { .length = 4, .offset = 4, .msb_right = 0 },
+ .blue = { .length = 4, .offset = 0, .msb_right = 0 },
+ .transp = { .length = 4, .offset = 12, .msb_right = 0 },
+ }, {
+ .dssmode = OMAP_DSS_COLOR_RGB16,
+ .bits_per_pixel = 16,
+ .red = { .length = 5, .offset = 11, .msb_right = 0 },
+ .green = { .length = 6, .offset = 5, .msb_right = 0 },
+ .blue = { .length = 5, .offset = 0, .msb_right = 0 },
+ .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+ }, {
+ .dssmode = OMAP_DSS_COLOR_RGB24P,
+ .bits_per_pixel = 24,
+ .red = { .length = 8, .offset = 16, .msb_right = 0 },
+ .green = { .length = 8, .offset = 8, .msb_right = 0 },
+ .blue = { .length = 8, .offset = 0, .msb_right = 0 },
+ .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+ }, {
+ .dssmode = OMAP_DSS_COLOR_RGB24U,
+ .bits_per_pixel = 32,
+ .red = { .length = 8, .offset = 16, .msb_right = 0 },
+ .green = { .length = 8, .offset = 8, .msb_right = 0 },
+ .blue = { .length = 8, .offset = 0, .msb_right = 0 },
+ .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+ }, {
+ .dssmode = OMAP_DSS_COLOR_ARGB32,
+ .bits_per_pixel = 32,
+ .red = { .length = 8, .offset = 16, .msb_right = 0 },
+ .green = { .length = 8, .offset = 8, .msb_right = 0 },
+ .blue = { .length = 8, .offset = 0, .msb_right = 0 },
+ .transp = { .length = 8, .offset = 24, .msb_right = 0 },
+ }, {
+ .dssmode = OMAP_DSS_COLOR_RGBA32,
+ .bits_per_pixel = 32,
+ .red = { .length = 8, .offset = 24, .msb_right = 0 },
+ .green = { .length = 8, .offset = 16, .msb_right = 0 },
+ .blue = { .length = 8, .offset = 8, .msb_right = 0 },
+ .transp = { .length = 8, .offset = 0, .msb_right = 0 },
+ }, {
+ .dssmode = OMAP_DSS_COLOR_RGBX32,
+ .bits_per_pixel = 32,
+ .red = { .length = 8, .offset = 24, .msb_right = 0 },
+ .green = { .length = 8, .offset = 16, .msb_right = 0 },
+ .blue = { .length = 8, .offset = 8, .msb_right = 0 },
+ .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+ },
+};
+
+static bool cmp_var_to_colormode(struct fb_var_screeninfo *var,
+ struct omapfb_colormode *color)
+{
+ bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2)
+ {
+ return f1->length == f2->length &&
+ f1->offset == f2->offset &&
+ f1->msb_right == f2->msb_right;
+ }
+
+ if (var->bits_per_pixel == 0 ||
+ var->red.length == 0 ||
+ var->blue.length == 0 ||
+ var->green.length == 0)
+ return 0;
+
+ return var->bits_per_pixel == color->bits_per_pixel &&
+ cmp_component(&var->red, &color->red) &&
+ cmp_component(&var->green, &color->green) &&
+ cmp_component(&var->blue, &color->blue) &&
+ cmp_component(&var->transp, &color->transp);
+}
+
+static void assign_colormode_to_var(struct fb_var_screeninfo *var,
+ struct omapfb_colormode *color)
+{
+ var->bits_per_pixel = color->bits_per_pixel;
+ var->nonstd = color->nonstd;
+ var->red = color->red;
+ var->green = color->green;
+ var->blue = color->blue;
+ var->transp = color->transp;
+}
+
+static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var,
+ enum omap_color_mode *mode)
+{
+ enum omap_color_mode dssmode;
+ int i;
+
+ /* first match with nonstd field */
+ if (var->nonstd) {
+ for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+ struct omapfb_colormode *m = &omapfb_colormodes[i];
+ if (var->nonstd == m->nonstd) {
+ assign_colormode_to_var(var, m);
+ *mode = m->dssmode;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ }
+
+ /* then try exact match of bpp and colors */
+ for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+ struct omapfb_colormode *m = &omapfb_colormodes[i];
+ if (cmp_var_to_colormode(var, m)) {
+ assign_colormode_to_var(var, m);
+ *mode = m->dssmode;
+ return 0;
+ }
+ }
+
+ /* match with bpp if user has not filled color fields
+ * properly */
+ switch (var->bits_per_pixel) {
+ case 1:
+ dssmode = OMAP_DSS_COLOR_CLUT1;
+ break;
+ case 2:
+ dssmode = OMAP_DSS_COLOR_CLUT2;
+ break;
+ case 4:
+ dssmode = OMAP_DSS_COLOR_CLUT4;
+ break;
+ case 8:
+ dssmode = OMAP_DSS_COLOR_CLUT8;
+ break;
+ case 12:
+ dssmode = OMAP_DSS_COLOR_RGB12U;
+ break;
+ case 16:
+ dssmode = OMAP_DSS_COLOR_RGB16;
+ break;
+ case 24:
+ dssmode = OMAP_DSS_COLOR_RGB24P;
+ break;
+ case 32:
+ dssmode = OMAP_DSS_COLOR_RGB24U;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+ struct omapfb_colormode *m = &omapfb_colormodes[i];
+ if (dssmode == m->dssmode) {
+ assign_colormode_to_var(var, m);
+ *mode = m->dssmode;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int check_fb_res_bounds(struct fb_var_screeninfo *var)
+{
+ int xres_min = OMAPFB_PLANE_XRES_MIN;
+ int xres_max = 2048;
+ int yres_min = OMAPFB_PLANE_YRES_MIN;
+ int yres_max = 2048;
+
+ /* XXX: some applications seem to set virtual res to 0. */
+ if (var->xres_virtual == 0)
+ var->xres_virtual = var->xres;
+
+ if (var->yres_virtual == 0)
+ var->yres_virtual = var->yres;
+
+ if (var->xres_virtual < xres_min || var->yres_virtual < yres_min)
+ return -EINVAL;
+
+ if (var->xres < xres_min)
+ var->xres = xres_min;
+ if (var->yres < yres_min)
+ var->yres = yres_min;
+ if (var->xres > xres_max)
+ var->xres = xres_max;
+ if (var->yres > yres_max)
+ var->yres = yres_max;
+
+ if (var->xres > var->xres_virtual)
+ var->xres = var->xres_virtual;
+ if (var->yres > var->yres_virtual)
+ var->yres = var->yres_virtual;
+
+ return 0;
+}
+
+static void shrink_height(unsigned long max_frame_size,
+ struct fb_var_screeninfo *var)
+{
+ DBG("can't fit FB into memory, reducing y\n");
+ var->yres_virtual = max_frame_size /
+ (var->xres_virtual * var->bits_per_pixel >> 3);
+
+ if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN)
+ var->yres_virtual = OMAPFB_PLANE_YRES_MIN;
+
+ if (var->yres > var->yres_virtual)
+ var->yres = var->yres_virtual;
+}
+
+static void shrink_width(unsigned long max_frame_size,
+ struct fb_var_screeninfo *var)
+{
+ DBG("can't fit FB into memory, reducing x\n");
+ var->xres_virtual = max_frame_size / var->yres_virtual /
+ (var->bits_per_pixel >> 3);
+
+ if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN)
+ var->xres_virtual = OMAPFB_PLANE_XRES_MIN;
+
+ if (var->xres > var->xres_virtual)
+ var->xres = var->xres_virtual;
+}
+
+static int check_vrfb_fb_size(unsigned long region_size,
+ const struct fb_var_screeninfo *var)
+{
+ unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual,
+ var->yres_virtual, var->bits_per_pixel >> 3);
+
+ return min_phys_size > region_size ? -EINVAL : 0;
+}
+
+static int check_fb_size(const struct omapfb_info *ofbi,
+ struct fb_var_screeninfo *var)
+{
+ unsigned long max_frame_size = ofbi->region.size;
+ int bytespp = var->bits_per_pixel >> 3;
+ unsigned long line_size = var->xres_virtual * bytespp;
+
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+ /* One needs to check for both VRFB and OMAPFB limitations. */
+ if (check_vrfb_fb_size(max_frame_size, var))
+ shrink_height(omap_vrfb_max_height(
+ max_frame_size, var->xres_virtual, bytespp) *
+ line_size, var);
+
+ if (check_vrfb_fb_size(max_frame_size, var)) {
+ DBG("cannot fit FB to memory\n");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size);
+
+ if (line_size * var->yres_virtual > max_frame_size)
+ shrink_height(max_frame_size, var);
+
+ if (line_size * var->yres_virtual > max_frame_size) {
+ shrink_width(max_frame_size, var);
+ line_size = var->xres_virtual * bytespp;
+ }
+
+ if (line_size * var->yres_virtual > max_frame_size) {
+ DBG("cannot fit FB to memory\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Consider if VRFB assisted rotation is in use and if the virtual space for
+ * the zero degree view needs to be mapped. The need for mapping also acts as
+ * the trigger for setting up the hardware on the context in question. This
+ * ensures that one does not attempt to access the virtual view before the
+ * hardware is serving the address translations.
+ */
+static int setup_vrfb_rotation(struct fb_info *fbi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_mem_region *rg = &ofbi->region;
+ struct vrfb *vrfb = &rg->vrfb;
+ struct fb_var_screeninfo *var = &fbi->var;
+ struct fb_fix_screeninfo *fix = &fbi->fix;
+ unsigned bytespp;
+ bool yuv_mode;
+ enum omap_color_mode mode;
+ int r;
+ bool reconf;
+
+ if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB)
+ return 0;
+
+ DBG("setup_vrfb_rotation\n");
+
+ r = fb_mode_to_dss_mode(var, &mode);
+ if (r)
+ return r;
+
+ bytespp = var->bits_per_pixel >> 3;
+
+ yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY;
+
+ /* We need to reconfigure VRFB if the resolution changes, if yuv mode
+ * is enabled/disabled, or if bytes per pixel changes */
+
+ /* XXX we shouldn't allow this when framebuffer is mmapped */
+
+ reconf = false;
+
+ if (yuv_mode != vrfb->yuv_mode)
+ reconf = true;
+ else if (bytespp != vrfb->bytespp)
+ reconf = true;
+ else if (vrfb->xres != var->xres_virtual ||
+ vrfb->yres != var->yres_virtual)
+ reconf = true;
+
+ if (vrfb->vaddr[0] && reconf) {
+ fbi->screen_base = NULL;
+ fix->smem_start = 0;
+ fix->smem_len = 0;
+ iounmap(vrfb->vaddr[0]);
+ vrfb->vaddr[0] = NULL;
+ DBG("setup_vrfb_rotation: reset fb\n");
+ }
+
+ if (vrfb->vaddr[0])
+ return 0;
+
+ omap_vrfb_setup(&rg->vrfb, rg->paddr,
+ var->xres_virtual,
+ var->yres_virtual,
+ bytespp, yuv_mode);
+
+ /* Now one can ioremap the 0 angle view */
+ r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0);
+ if (r)
+ return r;
+
+ /* used by open/write in fbmem.c */
+ fbi->screen_base = ofbi->region.vrfb.vaddr[0];
+
+ fix->smem_start = ofbi->region.vrfb.paddr[0];
+
+ switch (var->nonstd) {
+ case OMAPFB_COLOR_YUV422:
+ case OMAPFB_COLOR_YUY422:
+ fix->line_length =
+ (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2;
+ break;
+ default:
+ fix->line_length =
+ (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3;
+ break;
+ }
+
+ fix->smem_len = var->yres_virtual * fix->line_length;
+
+ return 0;
+}
+
+int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
+ struct fb_var_screeninfo *var)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+ struct omapfb_colormode *mode = &omapfb_colormodes[i];
+ if (dssmode == mode->dssmode) {
+ assign_colormode_to_var(var, mode);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+void set_fb_fix(struct fb_info *fbi)
+{
+ struct fb_fix_screeninfo *fix = &fbi->fix;
+ struct fb_var_screeninfo *var = &fbi->var;
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_mem_region *rg = &ofbi->region;
+
+ DBG("set_fb_fix\n");
+
+ /* used by open/write in fbmem.c */
+ fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi);
+
+ /* used by mmap in fbmem.c */
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+ switch (var->nonstd) {
+ case OMAPFB_COLOR_YUV422:
+ case OMAPFB_COLOR_YUY422:
+ fix->line_length =
+ (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2;
+ break;
+ default:
+ fix->line_length =
+ (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3;
+ break;
+ }
+
+ fix->smem_len = var->yres_virtual * fix->line_length;
+ } else {
+ fix->line_length =
+ (var->xres_virtual * var->bits_per_pixel) >> 3;
+ fix->smem_len = rg->size;
+ }
+
+ fix->smem_start = omapfb_get_region_paddr(ofbi);
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+
+ if (var->nonstd)
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ else {
+ switch (var->bits_per_pixel) {
+ case 32:
+ case 24:
+ case 16:
+ case 12:
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ /* 12bpp is stored in 16 bits */
+ break;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ }
+ }
+
+ fix->accel = FB_ACCEL_NONE;
+
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+}
+
+/* check new var and possibly modify it to be ok */
+int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omap_dss_device *display = fb2display(fbi);
+ enum omap_color_mode mode = 0;
+ int i;
+ int r;
+
+ DBG("check_fb_var %d\n", ofbi->id);
+
+ if (ofbi->region.size == 0)
+ return 0;
+
+ r = fb_mode_to_dss_mode(var, &mode);
+ if (r) {
+ DBG("cannot convert var to omap dss mode\n");
+ return r;
+ }
+
+ for (i = 0; i < ofbi->num_overlays; ++i) {
+ if ((ofbi->overlays[i]->supported_modes & mode) == 0) {
+ DBG("invalid mode\n");
+ return -EINVAL;
+ }
+ }
+
+ if (var->rotate < 0 || var->rotate > 3)
+ return -EINVAL;
+
+ if (check_fb_res_bounds(var))
+ return -EINVAL;
+
+ if (check_fb_size(ofbi, var))
+ return -EINVAL;
+
+ if (var->xres + var->xoffset > var->xres_virtual)
+ var->xoffset = var->xres_virtual - var->xres;
+ if (var->yres + var->yoffset > var->yres_virtual)
+ var->yoffset = var->yres_virtual - var->yres;
+
+ DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n",
+ var->xres, var->yres,
+ var->xres_virtual, var->yres_virtual);
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+
+ if (display && display->get_timings) {
+ struct omap_video_timings timings;
+ display->get_timings(display, &timings);
+
+ /* pixclock in ps, the rest in pixclock */
+ var->pixclock = timings.pixel_clock != 0 ?
+ KHZ2PICOS(timings.pixel_clock) :
+ 0;
+ var->left_margin = timings.hfp;
+ var->right_margin = timings.hbp;
+ var->upper_margin = timings.vfp;
+ var->lower_margin = timings.vbp;
+ var->hsync_len = timings.hsw;
+ var->vsync_len = timings.vsw;
+ } else {
+ var->pixclock = 0;
+ var->left_margin = 0;
+ var->right_margin = 0;
+ var->upper_margin = 0;
+ var->lower_margin = 0;
+ var->hsync_len = 0;
+ var->vsync_len = 0;
+ }
+
+ /* TODO: get these from panel->config */
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->sync = 0;
+
+ return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * fbdev framework callbacks
+ * ---------------------------------------------------------------------------
+ */
+static int omapfb_open(struct fb_info *fbi, int user)
+{
+ return 0;
+}
+
+static int omapfb_release(struct fb_info *fbi, int user)
+{
+#if 0
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_dss_device *display = fb2display(fbi);
+
+ DBG("Closing fb with plane index %d\n", ofbi->id);
+
+ omapfb_lock(fbdev);
+
+ if (display && display->get_update_mode && display->update) {
+ /* XXX this update should be removed, I think. But it's
+ * good for debugging */
+ if (display->get_update_mode(display) ==
+ OMAP_DSS_UPDATE_MANUAL) {
+ u16 w, h;
+
+ if (display->sync)
+ display->sync(display);
+
+ display->get_resolution(display, &w, &h);
+ display->update(display, 0, 0, w, h);
+ }
+ }
+
+ if (display && display->sync)
+ display->sync(display);
+
+ omapfb_unlock(fbdev);
+#endif
+ return 0;
+}
+
+static unsigned calc_rotation_offset_dma(struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix, int rotation)
+{
+ unsigned offset;
+
+ offset = var->yoffset * fix->line_length +
+ var->xoffset * (var->bits_per_pixel >> 3);
+
+ return offset;
+}
+
+static unsigned calc_rotation_offset_vrfb(struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix, int rotation)
+{
+ unsigned offset;
+
+ if (rotation == FB_ROTATE_UD)
+ offset = (var->yres_virtual - var->yres) *
+ fix->line_length;
+ else if (rotation == FB_ROTATE_CW)
+ offset = (var->yres_virtual - var->yres) *
+ (var->bits_per_pixel >> 3);
+ else
+ offset = 0;
+
+ if (rotation == FB_ROTATE_UR)
+ offset += var->yoffset * fix->line_length +
+ var->xoffset * (var->bits_per_pixel >> 3);
+ else if (rotation == FB_ROTATE_UD)
+ offset -= var->yoffset * fix->line_length +
+ var->xoffset * (var->bits_per_pixel >> 3);
+ else if (rotation == FB_ROTATE_CW)
+ offset -= var->xoffset * fix->line_length +
+ var->yoffset * (var->bits_per_pixel >> 3);
+ else if (rotation == FB_ROTATE_CCW)
+ offset += var->xoffset * fix->line_length +
+ var->yoffset * (var->bits_per_pixel >> 3);
+
+ return offset;
+}
+
+
+/* setup overlay according to the fb */
+static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
+ u16 posx, u16 posy, u16 outw, u16 outh)
+{
+ int r = 0;
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct fb_var_screeninfo *var = &fbi->var;
+ struct fb_fix_screeninfo *fix = &fbi->fix;
+ enum omap_color_mode mode = 0;
+ int offset;
+ u32 data_start_p;
+ void __iomem *data_start_v;
+ struct omap_overlay_info info;
+ int xres, yres;
+ int screen_width;
+ int mirror;
+ int rotation = var->rotate;
+ int i;
+
+ for (i = 0; i < ofbi->num_overlays; i++) {
+ if (ovl != ofbi->overlays[i])
+ continue;
+
+ rotation = (rotation + ofbi->rotation[i]) % 4;
+ break;
+ }
+
+ DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id,
+ posx, posy, outw, outh);
+
+ if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) {
+ xres = var->yres;
+ yres = var->xres;
+ } else {
+ xres = var->xres;
+ yres = var->yres;
+ }
+
+
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+ data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation);
+ data_start_v = NULL;
+ } else {
+ data_start_p = omapfb_get_region_paddr(ofbi);
+ data_start_v = omapfb_get_region_vaddr(ofbi);
+ }
+
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+ offset = calc_rotation_offset_vrfb(var, fix, rotation);
+ else
+ offset = calc_rotation_offset_dma(var, fix, rotation);
+
+ data_start_p += offset;
+ data_start_v += offset;
+
+ if (offset)
+ DBG("offset %d, %d = %d\n",
+ var->xoffset, var->yoffset, offset);
+
+ DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v);
+
+ r = fb_mode_to_dss_mode(var, &mode);
+ if (r) {
+ DBG("fb_mode_to_dss_mode failed");
+ goto err;
+ }
+
+ switch (var->nonstd) {
+ case OMAPFB_COLOR_YUV422:
+ case OMAPFB_COLOR_YUY422:
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+ screen_width = fix->line_length
+ / (var->bits_per_pixel >> 2);
+ break;
+ }
+ default:
+ screen_width = fix->line_length / (var->bits_per_pixel >> 3);
+ break;
+ }
+
+ ovl->get_overlay_info(ovl, &info);
+
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+ mirror = 0;
+ else
+ mirror = ofbi->mirror;
+
+ info.paddr = data_start_p;
+ info.vaddr = data_start_v;
+ info.screen_width = screen_width;
+ info.width = xres;
+ info.height = yres;
+ info.color_mode = mode;
+ info.rotation_type = ofbi->rotation_type;
+ info.rotation = rotation;
+ info.mirror = mirror;
+
+ info.pos_x = posx;
+ info.pos_y = posy;
+ info.out_width = outw;
+ info.out_height = outh;
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r) {
+ DBG("ovl->setup_overlay_info failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ DBG("setup_overlay failed\n");
+ return r;
+}
+
+/* apply var to the overlay */
+int omapfb_apply_changes(struct fb_info *fbi, int init)
+{
+ int r = 0;
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct fb_var_screeninfo *var = &fbi->var;
+ struct omap_overlay *ovl;
+ u16 posx, posy;
+ u16 outw, outh;
+ int i;
+
+#ifdef DEBUG
+ if (omapfb_test_pattern)
+ fill_fb(fbi);
+#endif
+
+ for (i = 0; i < ofbi->num_overlays; i++) {
+ ovl = ofbi->overlays[i];
+
+ DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id);
+
+ if (ofbi->region.size == 0) {
+ /* the fb is not available. disable the overlay */
+ omapfb_overlay_enable(ovl, 0);
+ if (!init && ovl->manager)
+ ovl->manager->apply(ovl->manager);
+ continue;
+ }
+
+ if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
+ int rotation = (var->rotate + ofbi->rotation[i]) % 4;
+ if (rotation == FB_ROTATE_CW ||
+ rotation == FB_ROTATE_CCW) {
+ outw = var->yres;
+ outh = var->xres;
+ } else {
+ outw = var->xres;
+ outh = var->yres;
+ }
+ } else {
+ outw = ovl->info.out_width;
+ outh = ovl->info.out_height;
+ }
+
+ if (init) {
+ posx = 0;
+ posy = 0;
+ } else {
+ posx = ovl->info.pos_x;
+ posy = ovl->info.pos_y;
+ }
+
+ r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh);
+ if (r)
+ goto err;
+
+ if (!init && ovl->manager)
+ ovl->manager->apply(ovl->manager);
+ }
+ return 0;
+err:
+ DBG("apply_changes failed\n");
+ return r;
+}
+
+/* checks var and eventually tweaks it to something supported,
+ * DO NOT MODIFY PAR */
+static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+ int r;
+
+ DBG("check_var(%d)\n", FB2OFB(fbi)->id);
+
+ r = check_fb_var(fbi, var);
+
+ return r;
+}
+
+/* set the video mode according to info->var */
+static int omapfb_set_par(struct fb_info *fbi)
+{
+ int r;
+
+ DBG("set_par(%d)\n", FB2OFB(fbi)->id);
+
+ set_fb_fix(fbi);
+
+ r = setup_vrfb_rotation(fbi);
+ if (r)
+ return r;
+
+ r = omapfb_apply_changes(fbi, 0);
+
+ return r;
+}
+
+static int omapfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fbi)
+{
+ struct fb_var_screeninfo new_var;
+ int r;
+
+ DBG("pan_display(%d)\n", FB2OFB(fbi)->id);
+
+ if (var->xoffset == fbi->var.xoffset &&
+ var->yoffset == fbi->var.yoffset)
+ return 0;
+
+ new_var = fbi->var;
+ new_var.xoffset = var->xoffset;
+ new_var.yoffset = var->yoffset;
+
+ fbi->var = new_var;
+
+ r = omapfb_apply_changes(fbi, 0);
+
+ return r;
+}
+
+static void mmap_user_open(struct vm_area_struct *vma)
+{
+ struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+
+ atomic_inc(&ofbi->map_count);
+}
+
+static void mmap_user_close(struct vm_area_struct *vma)
+{
+ struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+
+ atomic_dec(&ofbi->map_count);
+}
+
+static struct vm_operations_struct mmap_user_ops = {
+ .open = mmap_user_open,
+ .close = mmap_user_close,
+};
+
+static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct fb_fix_screeninfo *fix = &fbi->fix;
+ unsigned long off;
+ unsigned long start;
+ u32 len;
+
+ if (vma->vm_end - vma->vm_start == 0)
+ return 0;
+ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+ return -EINVAL;
+ off = vma->vm_pgoff << PAGE_SHIFT;
+
+ start = omapfb_get_region_paddr(ofbi);
+ len = fix->smem_len;
+ if (off >= len)
+ return -EINVAL;
+ if ((vma->vm_end - vma->vm_start + off) > len)
+ return -EINVAL;
+
+ off += start;
+
+ DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off);
+
+ vma->vm_pgoff = off >> PAGE_SHIFT;
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_ops = &mmap_user_ops;
+ vma->vm_private_data = ofbi;
+ if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot))
+ return -EAGAIN;
+ /* vm_ops.open won't be called for mmap itself. */
+ atomic_inc(&ofbi->map_count);
+ return 0;
+}
+
+/* Store a single color palette entry into a pseudo palette or the hardware
+ * palette if one is available. For now we support only 16bpp and thus store
+ * the entry only to the pseudo palette.
+ */
+static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp, int update_hw_pal)
+{
+ /*struct omapfb_info *ofbi = FB2OFB(fbi);*/
+ /*struct omapfb2_device *fbdev = ofbi->fbdev;*/
+ struct fb_var_screeninfo *var = &fbi->var;
+ int r = 0;
+
+ enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */
+
+ /*switch (plane->color_mode) {*/
+ switch (mode) {
+ case OMAPFB_COLOR_YUV422:
+ case OMAPFB_COLOR_YUV420:
+ case OMAPFB_COLOR_YUY422:
+ r = -EINVAL;
+ break;
+ case OMAPFB_COLOR_CLUT_8BPP:
+ case OMAPFB_COLOR_CLUT_4BPP:
+ case OMAPFB_COLOR_CLUT_2BPP:
+ case OMAPFB_COLOR_CLUT_1BPP:
+ /*
+ if (fbdev->ctrl->setcolreg)
+ r = fbdev->ctrl->setcolreg(regno, red, green, blue,
+ transp, update_hw_pal);
+ */
+ /* Fallthrough */
+ r = -EINVAL;
+ break;
+ case OMAPFB_COLOR_RGB565:
+ case OMAPFB_COLOR_RGB444:
+ case OMAPFB_COLOR_RGB24P:
+ case OMAPFB_COLOR_RGB24U:
+ if (r != 0)
+ break;
+
+ if (regno < 0) {
+ r = -EINVAL;
+ break;
+ }
+
+ if (regno < 16) {
+ u16 pal;
+ pal = ((red >> (16 - var->red.length)) <<
+ var->red.offset) |
+ ((green >> (16 - var->green.length)) <<
+ var->green.offset) |
+ (blue >> (16 - var->blue.length));
+ ((u32 *)(fbi->pseudo_palette))[regno] = pal;
+ }
+ break;
+ default:
+ BUG();
+ }
+ return r;
+}
+
+static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ DBG("setcolreg\n");
+
+ return _setcolreg(info, regno, red, green, blue, transp, 1);
+}
+
+static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+ int count, index, r;
+ u16 *red, *green, *blue, *transp;
+ u16 trans = 0xffff;
+
+ DBG("setcmap\n");
+
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ index = cmap->start;
+
+ for (count = 0; count < cmap->len; count++) {
+ if (transp)
+ trans = *transp++;
+ r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,
+ count == cmap->len - 1);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int omapfb_blank(int blank, struct fb_info *fbi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_dss_device *display = fb2display(fbi);
+ int do_update = 0;
+ int r = 0;
+
+ omapfb_lock(fbdev);
+
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
+ goto exit;
+
+ if (display->resume)
+ r = display->resume(display);
+
+ if (r == 0 && display->get_update_mode &&
+ display->get_update_mode(display) ==
+ OMAP_DSS_UPDATE_MANUAL)
+ do_update = 1;
+
+ break;
+
+ case FB_BLANK_NORMAL:
+ /* FB_BLANK_NORMAL could be implemented.
+ * Needs DSS additions. */
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
+ goto exit;
+
+ if (display->suspend)
+ r = display->suspend(display);
+
+ break;
+
+ default:
+ r = -EINVAL;
+ }
+
+exit:
+ omapfb_unlock(fbdev);
+
+ if (r == 0 && do_update && display->update) {
+ u16 w, h;
+ display->get_resolution(display, &w, &h);
+
+ r = display->update(display, 0, 0, w, h);
+ }
+
+ return r;
+}
+
+#if 0
+/* XXX fb_read and fb_write are needed for VRFB */
+ssize_t omapfb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos);
+ /* XXX needed for VRFB */
+ return count;
+}
+#endif
+
+static struct fb_ops omapfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = omapfb_open,
+ .fb_release = omapfb_release,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = omapfb_blank,
+ .fb_ioctl = omapfb_ioctl,
+ .fb_check_var = omapfb_check_var,
+ .fb_set_par = omapfb_set_par,
+ .fb_pan_display = omapfb_pan_display,
+ .fb_mmap = omapfb_mmap,
+ .fb_setcolreg = omapfb_setcolreg,
+ .fb_setcmap = omapfb_setcmap,
+ /*.fb_write = omapfb_write,*/
+};
+
+static void omapfb_free_fbmem(struct fb_info *fbi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omapfb2_mem_region *rg;
+
+ rg = &ofbi->region;
+
+ if (rg->paddr)
+ if (omap_vram_free(rg->paddr, rg->size))
+ dev_err(fbdev->dev, "VRAM FREE failed\n");
+
+ if (rg->vaddr)
+ iounmap(rg->vaddr);
+
+ 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->vaddr = NULL;
+ rg->paddr = 0;
+ rg->alloc = 0;
+ rg->size = 0;
+}
+
+static void clear_fb_info(struct fb_info *fbi)
+{
+ memset(&fbi->var, 0, sizeof(fbi->var));
+ memset(&fbi->fix, 0, sizeof(fbi->fix));
+ strlcpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id));
+}
+
+static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev)
+{
+ int i;
+
+ DBG("free all fbmem\n");
+
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ struct fb_info *fbi = fbdev->fbs[i];
+ omapfb_free_fbmem(fbi);
+ clear_fb_info(fbi);
+ }
+
+ return 0;
+}
+
+static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
+ unsigned long paddr)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omapfb2_mem_region *rg;
+ void __iomem *vaddr;
+ int r;
+
+ rg = &ofbi->region;
+ memset(rg, 0, sizeof(*rg));
+
+ size = PAGE_ALIGN(size);
+
+ if (!paddr) {
+ DBG("allocating %lu bytes for fb %d\n", size, ofbi->id);
+ r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr);
+ } else {
+ DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr,
+ ofbi->id);
+ r = omap_vram_reserve(paddr, size);
+ }
+
+ if (r) {
+ 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", paddr, vaddr);
+ } else {
+ r = omap_vrfb_request_ctx(&rg->vrfb);
+ if (r) {
+ dev_err(fbdev->dev, "vrfb create ctx failed\n");
+ return r;
+ }
+
+ vaddr = NULL;
+ }
+
+ rg->paddr = paddr;
+ rg->vaddr = vaddr;
+ rg->size = size;
+ rg->alloc = 1;
+
+ return 0;
+}
+
+/* allocate fbmem using display resolution as reference */
+static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
+ unsigned long paddr)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omap_dss_device *display;
+ int bytespp;
+
+ display = fb2display(fbi);
+
+ if (!display)
+ return 0;
+
+ switch (display->get_recommended_bpp(display)) {
+ case 16:
+ bytespp = 2;
+ break;
+ case 24:
+ bytespp = 4;
+ break;
+ default:
+ bytespp = 4;
+ break;
+ }
+
+ if (!size) {
+ u16 w, h;
+
+ display->get_resolution(display, &w, &h);
+
+ if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+ size = max(omap_vrfb_min_phys_size(w, h, bytespp),
+ omap_vrfb_min_phys_size(h, w, bytespp));
+
+ DBG("adjusting fb mem size for VRFB, %u -> %lu\n",
+ w * h * bytespp, size);
+ } else {
+ size = w * h * bytespp;
+ }
+ }
+
+ if (!size)
+ return 0;
+
+ return omapfb_alloc_fbmem(fbi, size, paddr);
+}
+
+static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt)
+{
+ enum omap_color_mode mode;
+
+ switch (fmt) {
+ case OMAPFB_COLOR_RGB565:
+ mode = OMAP_DSS_COLOR_RGB16;
+ break;
+ case OMAPFB_COLOR_YUV422:
+ mode = OMAP_DSS_COLOR_YUV2;
+ break;
+ case OMAPFB_COLOR_CLUT_8BPP:
+ mode = OMAP_DSS_COLOR_CLUT8;
+ break;
+ case OMAPFB_COLOR_CLUT_4BPP:
+ mode = OMAP_DSS_COLOR_CLUT4;
+ break;
+ case OMAPFB_COLOR_CLUT_2BPP:
+ mode = OMAP_DSS_COLOR_CLUT2;
+ break;
+ case OMAPFB_COLOR_CLUT_1BPP:
+ mode = OMAP_DSS_COLOR_CLUT1;
+ break;
+ case OMAPFB_COLOR_RGB444:
+ mode = OMAP_DSS_COLOR_RGB12U;
+ break;
+ case OMAPFB_COLOR_YUY422:
+ mode = OMAP_DSS_COLOR_UYVY;
+ break;
+ case OMAPFB_COLOR_ARGB16:
+ mode = OMAP_DSS_COLOR_ARGB16;
+ break;
+ case OMAPFB_COLOR_RGB24U:
+ mode = OMAP_DSS_COLOR_RGB24U;
+ break;
+ case OMAPFB_COLOR_RGB24P:
+ mode = OMAP_DSS_COLOR_RGB24P;
+ break;
+ case OMAPFB_COLOR_ARGB32:
+ mode = OMAP_DSS_COLOR_ARGB32;
+ break;
+ case OMAPFB_COLOR_RGBA32:
+ mode = OMAP_DSS_COLOR_RGBA32;
+ break;
+ case OMAPFB_COLOR_RGBX32:
+ mode = OMAP_DSS_COLOR_RGBX32;
+ break;
+ default:
+ mode = -EINVAL;
+ }
+
+ return mode;
+}
+
+static int omapfb_parse_vram_param(const char *param, int max_entries,
+ unsigned long *sizes, unsigned long *paddrs)
+{
+ int fbnum;
+ unsigned long size;
+ unsigned long paddr = 0;
+ char *p, *start;
+
+ start = (char *)param;
+
+ while (1) {
+ p = start;
+
+ fbnum = simple_strtoul(p, &p, 10);
+
+ if (p == param)
+ return -EINVAL;
+
+ if (*p != ':')
+ return -EINVAL;
+
+ if (fbnum >= max_entries)
+ return -EINVAL;
+
+ size = memparse(p + 1, &p);
+
+ if (!size)
+ return -EINVAL;
+
+ paddr = 0;
+
+ if (*p == '@') {
+ paddr = simple_strtoul(p + 1, &p, 16);
+
+ if (!paddr)
+ return -EINVAL;
+
+ }
+
+ paddrs[fbnum] = paddr;
+ sizes[fbnum] = size;
+
+ if (*p == 0)
+ break;
+
+ if (*p != ',')
+ return -EINVAL;
+
+ ++p;
+
+ start = p;
+ }
+
+ return 0;
+}
+
+static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev)
+{
+ int i, r;
+ unsigned long vram_sizes[10];
+ unsigned long vram_paddrs[10];
+
+ memset(&vram_sizes, 0, sizeof(vram_sizes));
+ memset(&vram_paddrs, 0, sizeof(vram_paddrs));
+
+ if (def_vram && omapfb_parse_vram_param(def_vram, 10,
+ vram_sizes, vram_paddrs)) {
+ dev_err(fbdev->dev, "failed to parse vram parameter\n");
+
+ memset(&vram_sizes, 0, sizeof(vram_sizes));
+ memset(&vram_paddrs, 0, sizeof(vram_paddrs));
+ }
+
+ if (fbdev->dev->platform_data) {
+ struct omapfb_platform_data *opd;
+ opd = fbdev->dev->platform_data;
+ for (i = 0; i < opd->mem_desc.region_cnt; ++i) {
+ if (!vram_sizes[i]) {
+ unsigned long size;
+ unsigned long paddr;
+
+ size = opd->mem_desc.region[i].size;
+ paddr = opd->mem_desc.region[i].paddr;
+
+ vram_sizes[i] = size;
+ vram_paddrs[i] = paddr;
+ }
+ }
+ }
+
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ /* allocate memory automatically only for fb0, or if
+ * excplicitly defined with vram or plat data option */
+ if (i == 0 || vram_sizes[i] != 0) {
+ r = omapfb_alloc_fbmem_display(fbdev->fbs[i],
+ vram_sizes[i], vram_paddrs[i]);
+
+ if (r)
+ return r;
+ }
+ }
+
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+ struct omapfb2_mem_region *rg;
+ rg = &ofbi->region;
+
+ DBG("region%d phys %08x virt %p size=%lu\n",
+ i,
+ rg->paddr,
+ rg->vaddr,
+ rg->size);
+ }
+
+ return 0;
+}
+
+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;
+ int old_type = rg->type;
+ int r;
+
+ if (type > OMAPFB_MEMTYPE_MAX)
+ return -EINVAL;
+
+ size = PAGE_ALIGN(size);
+
+ if (old_size == size && old_type == type)
+ return 0;
+
+ if (display && display->sync)
+ display->sync(display);
+
+ omapfb_free_fbmem(fbi);
+
+ if (size == 0) {
+ clear_fb_info(fbi);
+ return 0;
+ }
+
+ r = omapfb_alloc_fbmem(fbi, size, 0);
+
+ if (r) {
+ if (old_size)
+ omapfb_alloc_fbmem(fbi, old_size, old_paddr);
+
+ if (rg->size == 0)
+ clear_fb_info(fbi);
+
+ return r;
+ }
+
+ if (old_size == size)
+ return 0;
+
+ if (old_size == 0) {
+ DBG("initializing fb %d\n", ofbi->id);
+ r = omapfb_fb_init(fbdev, fbi);
+ if (r) {
+ DBG("omapfb_fb_init failed\n");
+ goto err;
+ }
+ r = omapfb_apply_changes(fbi, 1);
+ if (r) {
+ DBG("omapfb_apply_changes failed\n");
+ goto err;
+ }
+ } else {
+ struct fb_var_screeninfo new_var;
+ memcpy(&new_var, &fbi->var, sizeof(new_var));
+ r = check_fb_var(fbi, &new_var);
+ if (r)
+ goto err;
+ memcpy(&fbi->var, &new_var, sizeof(fbi->var));
+ set_fb_fix(fbi);
+ r = setup_vrfb_rotation(fbi);
+ if (r)
+ goto err;
+ }
+
+ return 0;
+err:
+ omapfb_free_fbmem(fbi);
+ clear_fb_info(fbi);
+ return r;
+}
+
+/* initialize fb_info, var, fix to something sane based on the display */
+static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
+{
+ struct fb_var_screeninfo *var = &fbi->var;
+ struct omap_dss_device *display = fb2display(fbi);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ int r = 0;
+
+ fbi->fbops = &omapfb_ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = fbdev->pseudo_palette;
+
+ if (ofbi->region.size == 0) {
+ clear_fb_info(fbi);
+ return 0;
+ }
+
+ var->nonstd = 0;
+ var->bits_per_pixel = 0;
+
+ var->rotate = def_rotate;
+
+ /*
+ * Check if there is a default color format set in the board file,
+ * and use this format instead the default deducted from the
+ * display bpp.
+ */
+ if (fbdev->dev->platform_data) {
+ struct omapfb_platform_data *opd;
+ int id = ofbi->id;
+
+ opd = fbdev->dev->platform_data;
+ if (opd->mem_desc.region[id].format_used) {
+ enum omap_color_mode mode;
+ enum omapfb_color_format format;
+
+ format = opd->mem_desc.region[id].format;
+ mode = fb_format_to_dss_mode(format);
+ if (mode < 0) {
+ r = mode;
+ goto err;
+ }
+ r = dss_mode_to_fb_mode(mode, var);
+ if (r < 0)
+ goto err;
+ }
+ }
+
+ if (display) {
+ u16 w, h;
+ int rotation = (var->rotate + ofbi->rotation[0]) % 4;
+
+ display->get_resolution(display, &w, &h);
+
+ if (rotation == FB_ROTATE_CW ||
+ rotation == FB_ROTATE_CCW) {
+ var->xres = h;
+ var->yres = w;
+ } else {
+ var->xres = w;
+ var->yres = h;
+ }
+
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres;
+
+ if (!var->bits_per_pixel) {
+ switch (display->get_recommended_bpp(display)) {
+ case 16:
+ var->bits_per_pixel = 16;
+ break;
+ case 24:
+ var->bits_per_pixel = 32;
+ break;
+ default:
+ dev_err(fbdev->dev, "illegal display "
+ "bpp\n");
+ return -EINVAL;
+ }
+ }
+ } else {
+ /* if there's no display, let's just guess some basic values */
+ var->xres = 320;
+ var->yres = 240;
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres;
+ if (!var->bits_per_pixel)
+ var->bits_per_pixel = 16;
+ }
+
+ r = check_fb_var(fbi, var);
+ if (r)
+ goto err;
+
+ set_fb_fix(fbi);
+ r = setup_vrfb_rotation(fbi);
+ if (r)
+ goto err;
+
+ r = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (r)
+ dev_err(fbdev->dev, "unable to allocate color map memory\n");
+
+err:
+ return r;
+}
+
+static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi)
+{
+ fb_dealloc_cmap(&fbi->cmap);
+}
+
+
+static void omapfb_free_resources(struct omapfb2_device *fbdev)
+{
+ int i;
+
+ DBG("free_resources\n");
+
+ if (fbdev == NULL)
+ return;
+
+ for (i = 0; i < fbdev->num_fbs; i++)
+ unregister_framebuffer(fbdev->fbs[i]);
+
+ /* free the reserved fbmem */
+ omapfb_free_all_fbmem(fbdev);
+
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ fbinfo_cleanup(fbdev, fbdev->fbs[i]);
+ framebuffer_release(fbdev->fbs[i]);
+ }
+
+ for (i = 0; i < fbdev->num_displays; i++) {
+ if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED)
+ fbdev->displays[i]->disable(fbdev->displays[i]);
+
+ omap_dss_put_device(fbdev->displays[i]);
+ }
+
+ dev_set_drvdata(fbdev->dev, NULL);
+ kfree(fbdev);
+}
+
+static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
+{
+ int r, i;
+
+ fbdev->num_fbs = 0;
+
+ DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS);
+
+ /* allocate fb_infos */
+ for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) {
+ struct fb_info *fbi;
+ struct omapfb_info *ofbi;
+
+ fbi = framebuffer_alloc(sizeof(struct omapfb_info),
+ fbdev->dev);
+
+ if (fbi == NULL) {
+ dev_err(fbdev->dev,
+ "unable to allocate memory for plane info\n");
+ return -ENOMEM;
+ }
+
+ clear_fb_info(fbi);
+
+ fbdev->fbs[i] = fbi;
+
+ ofbi = FB2OFB(fbi);
+ ofbi->fbdev = fbdev;
+ ofbi->id = i;
+
+ /* assign these early, so that fb alloc can use them */
+ ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB :
+ OMAP_DSS_ROT_DMA;
+ ofbi->mirror = def_mirror;
+
+ fbdev->num_fbs++;
+ }
+
+ DBG("fb_infos allocated\n");
+
+ /* assign overlays for the fbs */
+ for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) {
+ struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+
+ ofbi->overlays[0] = fbdev->overlays[i];
+ ofbi->num_overlays = 1;
+ }
+
+ /* allocate fb memories */
+ r = omapfb_allocate_all_fbs(fbdev);
+ if (r) {
+ dev_err(fbdev->dev, "failed to allocate fbmem\n");
+ return r;
+ }
+
+ DBG("fbmems allocated\n");
+
+ /* setup fb_infos */
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ r = omapfb_fb_init(fbdev, fbdev->fbs[i]);
+ if (r) {
+ dev_err(fbdev->dev, "failed to setup fb_info\n");
+ return r;
+ }
+ }
+
+ DBG("fb_infos initialized\n");
+
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ r = register_framebuffer(fbdev->fbs[i]);
+ if (r != 0) {
+ dev_err(fbdev->dev,
+ "registering framebuffer %d failed\n", i);
+ return r;
+ }
+ }
+
+ DBG("framebuffers registered\n");
+
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ r = omapfb_apply_changes(fbdev->fbs[i], 1);
+ if (r) {
+ dev_err(fbdev->dev, "failed to change mode\n");
+ return r;
+ }
+ }
+
+ DBG("create sysfs for fbs\n");
+ r = omapfb_create_sysfs(fbdev);
+ if (r) {
+ dev_err(fbdev->dev, "failed to create sysfs entries\n");
+ return r;
+ }
+
+ /* Enable fb0 */
+ if (fbdev->num_fbs > 0) {
+ struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]);
+
+ if (ofbi->num_overlays > 0) {
+ struct omap_overlay *ovl = ofbi->overlays[0];
+
+ r = omapfb_overlay_enable(ovl, 1);
+
+ if (r) {
+ dev_err(fbdev->dev,
+ "failed to enable overlay\n");
+ return r;
+ }
+ }
+ }
+
+ DBG("create_framebuffers done\n");
+
+ return 0;
+}
+
+static int omapfb_mode_to_timings(const char *mode_str,
+ struct omap_video_timings *timings, u8 *bpp)
+{
+ struct fb_info fbi;
+ struct fb_var_screeninfo var;
+ struct fb_ops fbops;
+ int r;
+
+#ifdef CONFIG_OMAP2_DSS_VENC
+ if (strcmp(mode_str, "pal") == 0) {
+ *timings = omap_dss_pal_timings;
+ *bpp = 0;
+ return 0;
+ } else if (strcmp(mode_str, "ntsc") == 0) {
+ *timings = omap_dss_ntsc_timings;
+ *bpp = 0;
+ return 0;
+ }
+#endif
+
+ /* this is quite a hack, but I wanted to use the modedb and for
+ * that we need fb_info and var, so we create dummy ones */
+
+ memset(&fbi, 0, sizeof(fbi));
+ memset(&var, 0, sizeof(var));
+ memset(&fbops, 0, sizeof(fbops));
+ fbi.fbops = &fbops;
+
+ r = fb_find_mode(&var, &fbi, mode_str, NULL, 0, NULL, 24);
+
+ if (r != 0) {
+ timings->pixel_clock = PICOS2KHZ(var.pixclock);
+ timings->hfp = var.left_margin;
+ timings->hbp = var.right_margin;
+ timings->vfp = var.upper_margin;
+ timings->vbp = var.lower_margin;
+ timings->hsw = var.hsync_len;
+ timings->vsw = var.vsync_len;
+ timings->x_res = var.xres;
+ timings->y_res = var.yres;
+
+ switch (var.bits_per_pixel) {
+ case 16:
+ *bpp = 16;
+ break;
+ case 24:
+ case 32:
+ default:
+ *bpp = 24;
+ break;
+ }
+
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static int omapfb_set_def_mode(struct omap_dss_device *display, char *mode_str)
+{
+ int r;
+ u8 bpp;
+ struct omap_video_timings timings;
+
+ r = omapfb_mode_to_timings(mode_str, &timings, &bpp);
+ if (r)
+ return r;
+
+ display->panel.recommended_bpp = bpp;
+
+ if (!display->check_timings || !display->set_timings)
+ return -EINVAL;
+
+ r = display->check_timings(display, &timings);
+ if (r)
+ return r;
+
+ display->set_timings(display, &timings);
+
+ return 0;
+}
+
+static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
+{
+ char *str, *options, *this_opt;
+ int r = 0;
+
+ str = kmalloc(strlen(def_mode) + 1, GFP_KERNEL);
+ strcpy(str, def_mode);
+ options = str;
+
+ while (!r && (this_opt = strsep(&options, ",")) != NULL) {
+ char *p, *display_str, *mode_str;
+ struct omap_dss_device *display;
+ int i;
+
+ p = strchr(this_opt, ':');
+ if (!p) {
+ r = -EINVAL;
+ break;
+ }
+
+ *p = 0;
+ display_str = this_opt;
+ mode_str = p + 1;
+
+ display = NULL;
+ for (i = 0; i < fbdev->num_displays; ++i) {
+ if (strcmp(fbdev->displays[i]->name,
+ display_str) == 0) {
+ display = fbdev->displays[i];
+ break;
+ }
+ }
+
+ if (!display) {
+ r = -EINVAL;
+ break;
+ }
+
+ r = omapfb_set_def_mode(display, mode_str);
+ if (r)
+ break;
+ }
+
+ kfree(str);
+
+ return r;
+}
+
+static int 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;
+
+ DBG("omapfb_probe\n");
+
+ if (pdev->num_resources != 0) {
+ dev_err(&pdev->dev, "probed for an unknown device\n");
+ r = -ENODEV;
+ goto err0;
+ }
+
+ fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL);
+ if (fbdev == NULL) {
+ r = -ENOMEM;
+ goto err0;
+ }
+
+ mutex_init(&fbdev->mtx);
+
+ fbdev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, fbdev);
+
+ fbdev->num_displays = 0;
+ dssdev = NULL;
+ for_each_dss_dev(dssdev) {
+ omap_dss_get_device(dssdev);
+ fbdev->displays[fbdev->num_displays++] = dssdev;
+ }
+
+ if (fbdev->num_displays == 0) {
+ dev_err(&pdev->dev, "no displays\n");
+ r = -EINVAL;
+ goto cleanup;
+ }
+
+ fbdev->num_overlays = omap_dss_get_num_overlays();
+ for (i = 0; i < fbdev->num_overlays; i++)
+ fbdev->overlays[i] = omap_dss_get_overlay(i);
+
+ fbdev->num_managers = omap_dss_get_num_overlay_managers();
+ for (i = 0; i < fbdev->num_managers; i++)
+ fbdev->managers[i] = omap_dss_get_overlay_manager(i);
+
+ if (def_mode && strlen(def_mode) > 0) {
+ if (omapfb_parse_def_modes(fbdev))
+ dev_warn(&pdev->dev, "cannot parse default modes\n");
+ }
+
+ r = omapfb_create_framebuffers(fbdev);
+ if (r)
+ goto cleanup;
+
+ for (i = 0; i < fbdev->num_managers; i++) {
+ struct omap_overlay_manager *mgr;
+ mgr = fbdev->managers[i];
+ r = mgr->apply(mgr);
+ if (r)
+ dev_warn(fbdev->dev, "failed to apply dispc config\n");
+ }
+
+ DBG("mgr->apply'ed\n");
+
+ /* 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);
+ if (ovl->manager && ovl->manager->device) {
+ def_display = ovl->manager->device;
+ } else {
+ dev_warn(&pdev->dev, "cannot find default display\n");
+ def_display = NULL;
+ }
+
+ if (def_display) {
+#ifndef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE
+ u16 w, h;
+#endif
+ r = def_display->enable(def_display);
+ if (r)
+ dev_warn(fbdev->dev, "Failed to enable display '%s'\n",
+ def_display->name);
+
+ /* set the update mode */
+ if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE
+ if (def_display->enable_te)
+ def_display->enable_te(def_display, 1);
+ if (def_display->set_update_mode)
+ def_display->set_update_mode(def_display,
+ OMAP_DSS_UPDATE_AUTO);
+#else /* MANUAL_UPDATE */
+ if (def_display->enable_te)
+ def_display->enable_te(def_display, 0);
+ if (def_display->set_update_mode)
+ def_display->set_update_mode(def_display,
+ OMAP_DSS_UPDATE_MANUAL);
+
+ def_display->get_resolution(def_display, &w, &h);
+ def_display->update(def_display, 0, 0, w, h);
+#endif
+ } else {
+ if (def_display->set_update_mode)
+ def_display->set_update_mode(def_display,
+ OMAP_DSS_UPDATE_AUTO);
+ }
+ }
+
+ return 0;
+
+cleanup:
+ omapfb_free_resources(fbdev);
+err0:
+ dev_err(&pdev->dev, "failed to setup omapfb\n");
+ return r;
+}
+
+static int omapfb_remove(struct platform_device *pdev)
+{
+ struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+
+ /* FIXME: wait till completion of pending events */
+
+ omapfb_remove_sysfs(fbdev);
+
+ omapfb_free_resources(fbdev);
+
+ return 0;
+}
+
+static struct platform_driver omapfb_driver = {
+ .probe = omapfb_probe,
+ .remove = omapfb_remove,
+ .driver = {
+ .name = "omapfb",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omapfb_init(void)
+{
+ DBG("omapfb_init\n");
+
+ if (platform_driver_register(&omapfb_driver)) {
+ printk(KERN_ERR "failed to register omapfb driver\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit omapfb_exit(void)
+{
+ DBG("omapfb_exit\n");
+ platform_driver_unregister(&omapfb_driver);
+}
+
+module_param_named(mode, def_mode, charp, 0);
+module_param_named(vram, def_vram, charp, 0);
+module_param_named(rotate, def_rotate, int, 0);
+module_param_named(vrfb, def_vrfb, bool, 0);
+module_param_named(mirror, def_mirror, bool, 0);
+
+/* late_initcall to let panel/ctrl drivers loaded first.
+ * I guess better option would be a more dynamic approach,
+ * so that omapfb reacts to new panels when they are loaded */
+late_initcall(omapfb_init);
+/*module_init(omapfb_init);*/
+module_exit(omapfb_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("OMAP2/3 Framebuffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c
new file mode 100644
index 00000000000..62bb88f5c19
--- /dev/null
+++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c
@@ -0,0 +1,507 @@
+/*
+ * linux/drivers/video/omap2/omapfb-sysfs.c
+ *
+ * Copyright (C) 2008 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/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/omapfb.h>
+
+#include <plat/display.h>
+#include <plat/vrfb.h>
+
+#include "omapfb.h"
+
+static ssize_t show_rotate_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type);
+}
+
+static ssize_t store_rotate_type(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ enum omap_dss_rotation_type rot_type;
+ int r;
+
+ rot_type = simple_strtoul(buf, NULL, 0);
+
+ if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB)
+ return -EINVAL;
+
+ lock_fb_info(fbi);
+
+ r = 0;
+ if (rot_type == ofbi->rotation_type)
+ goto out;
+
+ if (ofbi->region.size) {
+ r = -EBUSY;
+ goto out;
+ }
+
+ ofbi->rotation_type = rot_type;
+
+ /*
+ * Since the VRAM for this FB is not allocated at the moment we don't
+ * need to do any further parameter checking at this point.
+ */
+out:
+ unlock_fb_info(fbi);
+
+ return r ? r : count;
+}
+
+
+static ssize_t show_mirror(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror);
+}
+
+static ssize_t store_mirror(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ bool mirror;
+ int r;
+ struct fb_var_screeninfo new_var;
+
+ mirror = simple_strtoul(buf, NULL, 0);
+
+ if (mirror != 0 && mirror != 1)
+ return -EINVAL;
+
+ lock_fb_info(fbi);
+
+ ofbi->mirror = mirror;
+
+ memcpy(&new_var, &fbi->var, sizeof(new_var));
+ r = check_fb_var(fbi, &new_var);
+ if (r)
+ goto out;
+ memcpy(&fbi->var, &new_var, sizeof(fbi->var));
+
+ set_fb_fix(fbi);
+
+ r = omapfb_apply_changes(fbi, 0);
+ if (r)
+ goto out;
+
+ r = count;
+out:
+ unlock_fb_info(fbi);
+
+ return r;
+}
+
+static ssize_t show_overlays(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ ssize_t l = 0;
+ int t;
+
+ omapfb_lock(fbdev);
+ lock_fb_info(fbi);
+
+ for (t = 0; t < ofbi->num_overlays; t++) {
+ struct omap_overlay *ovl = ofbi->overlays[t];
+ int ovlnum;
+
+ for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum)
+ if (ovl == fbdev->overlays[ovlnum])
+ break;
+
+ l += snprintf(buf + l, PAGE_SIZE - l, "%s%d",
+ t == 0 ? "" : ",", ovlnum);
+ }
+
+ l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+ unlock_fb_info(fbi);
+ omapfb_unlock(fbdev);
+
+ return l;
+}
+
+static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev,
+ struct omap_overlay *ovl)
+{
+ int i, t;
+
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+
+ for (t = 0; t < ofbi->num_overlays; t++) {
+ if (ofbi->overlays[t] == ovl)
+ return ofbi;
+ }
+ }
+
+ return NULL;
+}
+
+static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+ struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB];
+ struct omap_overlay *ovl;
+ int num_ovls, r, i;
+ int len;
+ bool added = false;
+
+ num_ovls = 0;
+
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ len = len - 1;
+
+ omapfb_lock(fbdev);
+ lock_fb_info(fbi);
+
+ if (len > 0) {
+ char *p = (char *)buf;
+ int ovlnum;
+
+ while (p < buf + len) {
+ int found;
+ if (num_ovls == OMAPFB_MAX_OVL_PER_FB) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ ovlnum = simple_strtoul(p, &p, 0);
+ if (ovlnum > fbdev->num_overlays) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ found = 0;
+ for (i = 0; i < num_ovls; ++i) {
+ if (ovls[i] == fbdev->overlays[ovlnum]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ ovls[num_ovls++] = fbdev->overlays[ovlnum];
+
+ p++;
+ }
+ }
+
+ for (i = 0; i < num_ovls; ++i) {
+ struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]);
+ if (ofbi2 && ofbi2 != ofbi) {
+ dev_err(fbdev->dev, "overlay already in use\n");
+ r = -EINVAL;
+ goto out;
+ }
+ }
+
+ /* detach unused overlays */
+ for (i = 0; i < ofbi->num_overlays; ++i) {
+ int t, found;
+
+ ovl = ofbi->overlays[i];
+
+ found = 0;
+
+ for (t = 0; t < num_ovls; ++t) {
+ if (ovl == ovls[t]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ DBG("detaching %d\n", ofbi->overlays[i]->id);
+
+ omapfb_overlay_enable(ovl, 0);
+
+ if (ovl->manager)
+ ovl->manager->apply(ovl->manager);
+
+ for (t = i + 1; t < ofbi->num_overlays; t++) {
+ ofbi->rotation[t-1] = ofbi->rotation[t];
+ ofbi->overlays[t-1] = ofbi->overlays[t];
+ }
+
+ ofbi->num_overlays--;
+ i--;
+ }
+
+ for (i = 0; i < num_ovls; ++i) {
+ int t, found;
+
+ ovl = ovls[i];
+
+ found = 0;
+
+ for (t = 0; t < ofbi->num_overlays; ++t) {
+ if (ovl == ofbi->overlays[t]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+ ofbi->rotation[ofbi->num_overlays] = 0;
+ ofbi->overlays[ofbi->num_overlays++] = ovl;
+
+ added = true;
+ }
+
+ if (added) {
+ r = omapfb_apply_changes(fbi, 0);
+ if (r)
+ goto out;
+ }
+
+ r = count;
+out:
+ unlock_fb_info(fbi);
+ omapfb_unlock(fbdev);
+
+ return r;
+}
+
+static ssize_t show_overlays_rotate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ ssize_t l = 0;
+ int t;
+
+ lock_fb_info(fbi);
+
+ for (t = 0; t < ofbi->num_overlays; t++) {
+ l += snprintf(buf + l, PAGE_SIZE - l, "%s%d",
+ t == 0 ? "" : ",", ofbi->rotation[t]);
+ }
+
+ l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+ unlock_fb_info(fbi);
+
+ return l;
+}
+
+static ssize_t store_overlays_rotate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ int num_ovls = 0, r, i;
+ int len;
+ bool changed = false;
+ u8 rotation[OMAPFB_MAX_OVL_PER_FB];
+
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ len = len - 1;
+
+ lock_fb_info(fbi);
+
+ if (len > 0) {
+ char *p = (char *)buf;
+
+ while (p < buf + len) {
+ int rot;
+
+ if (num_ovls == ofbi->num_overlays) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ rot = simple_strtoul(p, &p, 0);
+ if (rot < 0 || rot > 3) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (ofbi->rotation[num_ovls] != rot)
+ changed = true;
+
+ rotation[num_ovls++] = rot;
+
+ p++;
+ }
+ }
+
+ if (num_ovls != ofbi->num_overlays) {
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (changed) {
+ for (i = 0; i < num_ovls; ++i)
+ ofbi->rotation[i] = rotation[i];
+
+ r = omapfb_apply_changes(fbi, 0);
+ if (r)
+ goto out;
+
+ /* FIXME error handling? */
+ }
+
+ r = count;
+out:
+ unlock_fb_info(fbi);
+
+ return r;
+}
+
+static ssize_t show_size(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size);
+}
+
+static ssize_t store_size(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ unsigned long size;
+ int r;
+ int i;
+
+ size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0));
+
+ lock_fb_info(fbi);
+
+ for (i = 0; i < ofbi->num_overlays; i++) {
+ if (ofbi->overlays[i]->info.enabled) {
+ r = -EBUSY;
+ goto out;
+ }
+ }
+
+ if (size != ofbi->region.size) {
+ r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type);
+ if (r) {
+ dev_err(dev, "realloc fbmem failed\n");
+ goto out;
+ }
+ }
+
+ r = count;
+out:
+ unlock_fb_info(fbi);
+
+ return r;
+}
+
+static ssize_t show_phys(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+
+ return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr);
+}
+
+static ssize_t show_virt(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fbi = dev_get_drvdata(dev);
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+
+ return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr);
+}
+
+static struct device_attribute omapfb_attrs[] = {
+ __ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type,
+ store_rotate_type),
+ __ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror),
+ __ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size),
+ __ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays),
+ __ATTR(overlays_rotate, S_IRUGO | S_IWUSR, show_overlays_rotate,
+ store_overlays_rotate),
+ __ATTR(phys_addr, S_IRUGO, show_phys, NULL),
+ __ATTR(virt_addr, S_IRUGO, show_virt, NULL),
+};
+
+int omapfb_create_sysfs(struct omapfb2_device *fbdev)
+{
+ int i;
+ int r;
+
+ DBG("create sysfs for fbs\n");
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ int t;
+ for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) {
+ r = device_create_file(fbdev->fbs[i]->dev,
+ &omapfb_attrs[t]);
+
+ if (r) {
+ dev_err(fbdev->dev, "failed to create sysfs "
+ "file\n");
+ return r;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void omapfb_remove_sysfs(struct omapfb2_device *fbdev)
+{
+ int i, t;
+
+ DBG("remove sysfs for fbs\n");
+ for (i = 0; i < fbdev->num_fbs; i++) {
+ for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++)
+ device_remove_file(fbdev->fbs[i]->dev,
+ &omapfb_attrs[t]);
+ }
+}
+
diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h
new file mode 100644
index 00000000000..f7c9c739e5e
--- /dev/null
+++ b/drivers/video/omap2/omapfb/omapfb.h
@@ -0,0 +1,146 @@
+/*
+ * linux/drivers/video/omap2/omapfb.h
+ *
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__
+#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__
+
+#ifdef CONFIG_FB_OMAP2_DEBUG_SUPPORT
+#define DEBUG
+#endif
+
+#include <plat/display.h>
+
+#ifdef DEBUG
+extern unsigned int omapfb_debug;
+#define DBG(format, ...) \
+ if (omapfb_debug) \
+ printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define FB2OFB(fb_info) ((struct omapfb_info *)(fb_info->par))
+
+/* max number of overlays to which a framebuffer data can be direct */
+#define OMAPFB_MAX_OVL_PER_FB 3
+
+struct omapfb2_mem_region {
+ u32 paddr;
+ void __iomem *vaddr;
+ struct vrfb vrfb;
+ unsigned long size;
+ u8 type; /* OMAPFB_PLANE_MEM_* */
+ bool alloc; /* allocated by the driver */
+ bool map; /* kernel mapped by the driver */
+};
+
+/* appended to fb_info */
+struct omapfb_info {
+ int id;
+ struct omapfb2_mem_region region;
+ atomic_t map_count;
+ int num_overlays;
+ struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB];
+ struct omapfb2_device *fbdev;
+ enum omap_dss_rotation_type rotation_type;
+ u8 rotation[OMAPFB_MAX_OVL_PER_FB];
+ bool mirror;
+};
+
+struct omapfb2_device {
+ struct device *dev;
+ struct mutex mtx;
+
+ u32 pseudo_palette[17];
+
+ int state;
+
+ unsigned num_fbs;
+ struct fb_info *fbs[10];
+
+ unsigned num_displays;
+ struct omap_dss_device *displays[10];
+ unsigned num_overlays;
+ struct omap_overlay *overlays[10];
+ unsigned num_managers;
+ struct omap_overlay_manager *managers[10];
+};
+
+struct omapfb_colormode {
+ enum omap_color_mode dssmode;
+ u32 bits_per_pixel;
+ u32 nonstd;
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+void set_fb_fix(struct fb_info *fbi);
+int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var);
+int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type);
+int omapfb_apply_changes(struct fb_info *fbi, int init);
+
+int omapfb_create_sysfs(struct omapfb2_device *fbdev);
+void omapfb_remove_sysfs(struct omapfb2_device *fbdev);
+
+int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg);
+
+int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
+ struct fb_var_screeninfo *var);
+
+/* find the display connected to this fb, if any */
+static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
+{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ int i;
+
+ /* XXX: returns the display connected to first attached overlay */
+ for (i = 0; i < ofbi->num_overlays; i++) {
+ if (ofbi->overlays[i]->manager)
+ return ofbi->overlays[i]->manager->device;
+ }
+
+ return NULL;
+}
+
+static inline void omapfb_lock(struct omapfb2_device *fbdev)
+{
+ mutex_lock(&fbdev->mtx);
+}
+
+static inline void omapfb_unlock(struct omapfb2_device *fbdev)
+{
+ mutex_unlock(&fbdev->mtx);
+}
+
+static inline int omapfb_overlay_enable(struct omap_overlay *ovl,
+ int enable)
+{
+ struct omap_overlay_info info;
+
+ ovl->get_overlay_info(ovl, &info);
+ info.enabled = enable;
+ return ovl->set_overlay_info(ovl, &info);
+}
+
+#endif
diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c
new file mode 100644
index 00000000000..55a4de5e5d1
--- /dev/null
+++ b/drivers/video/omap2/vram.c
@@ -0,0 +1,655 @@
+/*
+ * 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/seq_file.h>
+#include <linux/bootmem.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#include <asm/setup.h>
+
+#include <plat/sram.h>
+#include <plat/vram.h>
+#include <plat/dma.h>
+
+#ifdef DEBUG
+#define DBG(format, ...) pr_debug("VRAM: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define OMAP2_SRAM_START 0x40200000
+/* Maximum size, in reality this is smaller if SRAM is partially locked. */
+#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */
+
+/* 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 inline int region_mem_type(unsigned long paddr)
+{
+ if (paddr >= OMAP2_SRAM_START &&
+ paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE)
+ return OMAP_VRAM_MEMTYPE_SRAM;
+ else
+ return OMAP_VRAM_MEMTYPE_SDRAM;
+}
+
+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);
+
+ if (region_mem_type(rm->paddr) != region_mem_type(paddr))
+ continue;
+
+ 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 void _omap_vram_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct completion *compl = data;
+ complete(compl);
+}
+
+static int _omap_vram_clear(u32 paddr, unsigned pages)
+{
+ struct completion compl;
+ unsigned elem_count;
+ unsigned frame_count;
+ int r;
+ int lch;
+
+ init_completion(&compl);
+
+ r = omap_request_dma(OMAP_DMA_NO_DEVICE, "VRAM DMA",
+ _omap_vram_dma_cb,
+ &compl, &lch);
+ if (r) {
+ pr_err("VRAM: request_dma failed for memory clear\n");
+ return -EBUSY;
+ }
+
+ elem_count = pages * PAGE_SIZE / 4;
+ frame_count = 1;
+
+ omap_set_dma_transfer_params(lch, OMAP_DMA_DATA_TYPE_S32,
+ elem_count, frame_count,
+ OMAP_DMA_SYNC_ELEMENT,
+ 0, 0);
+
+ omap_set_dma_dest_params(lch, 0, OMAP_DMA_AMODE_POST_INC,
+ paddr, 0, 0);
+
+ omap_set_dma_color_mode(lch, OMAP_DMA_CONSTANT_FILL, 0x000000);
+
+ omap_start_dma(lch);
+
+ if (wait_for_completion_timeout(&compl, msecs_to_jiffies(1000)) == 0) {
+ omap_stop_dma(lch);
+ pr_err("VRAM: dma timeout while clearing memory\n");
+ r = -EIO;
+ goto err;
+ }
+
+ r = 0;
+err:
+ omap_free_dma(lch);
+
+ return r;
+}
+
+static int _omap_vram_alloc(int mtype, 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);
+
+ if (region_mem_type(rm->paddr) != mtype)
+ continue;
+
+ 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;
+
+ _omap_vram_clear(start, pages);
+
+ return 0;
+ }
+
+ return -ENOMEM;
+}
+
+int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr)
+{
+ unsigned pages;
+ int r;
+
+ BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size);
+
+ DBG("alloc mem type %d size %d\n", mtype, size);
+
+ size = PAGE_ALIGN(size);
+ pages = size >> PAGE_SHIFT;
+
+ mutex_lock(&region_mutex);
+
+ r = _omap_vram_alloc(mtype, 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_sram_start __initdata;
+static u32 omap_vram_sram_size __initdata;
+
+/* 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 void __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);
+}
+__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(void)
+{
+ struct bootmem_data *bdata;
+ unsigned long sdram_start, sdram_size;
+ 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 = PAGE_ALIGN(size);
+
+ bdata = NODE_DATA(0)->bdata;
+ sdram_start = bdata->node_min_pfn << PAGE_SHIFT;
+ sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start;
+
+ if (paddr) {
+ if ((paddr & ~PAGE_MASK) || paddr < sdram_start ||
+ paddr + size > sdram_start + sdram_size) {
+ pr_err("Illegal SDRAM region for VRAM\n");
+ return;
+ }
+
+ if (reserve_bootmem(paddr, size, BOOTMEM_EXCLUSIVE) < 0) {
+ pr_err("FB: failed to reserve VRAM\n");
+ return;
+ }
+ } else {
+ if (size > sdram_size) {
+ pr_err("Illegal SDRAM size for VRAM\n");
+ return;
+ }
+
+ paddr = virt_to_phys(alloc_bootmem_pages(size));
+ BUG_ON(paddr & ~PAGE_MASK);
+ }
+
+ omap_vram_add_region(paddr, size);
+
+ pr_info("Reserving %u bytes SDRAM for VRAM\n", size);
+}
+
+/*
+ * Called at sram init time, before anything is pushed to the SRAM stack.
+ * Because of the stack scheme, we will allocate everything from the
+ * start of the lowest address region to the end of SRAM. This will also
+ * include padding for page alignment and possible holes between regions.
+ *
+ * As opposed to the SDRAM case, we'll also do any dynamic allocations at
+ * this point, since the driver built as a module would have problem with
+ * freeing / reallocating the regions.
+ */
+unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart,
+ unsigned long sram_vstart,
+ unsigned long sram_size,
+ unsigned long pstart_avail,
+ unsigned long size_avail)
+{
+ unsigned long pend_avail;
+ unsigned long reserved;
+ u32 paddr;
+ u32 size;
+
+ paddr = omap_vram_sram_start;
+ size = omap_vram_sram_size;
+
+ if (!size)
+ return 0;
+
+ reserved = 0;
+ pend_avail = pstart_avail + size_avail;
+
+ if (!paddr) {
+ /* Dynamic allocation */
+ if ((size_avail & PAGE_MASK) < size) {
+ pr_err("Not enough SRAM for VRAM\n");
+ return 0;
+ }
+ size_avail = (size_avail - size) & PAGE_MASK;
+ paddr = pstart_avail + size_avail;
+ }
+
+ if (paddr < sram_pstart ||
+ paddr + size > sram_pstart + sram_size) {
+ pr_err("Illegal SRAM region for VRAM\n");
+ return 0;
+ }
+
+ /* Reserve everything above the start of the region. */
+ if (pend_avail - paddr > reserved)
+ reserved = pend_avail - paddr;
+ size_avail = pend_avail - reserved - pstart_avail;
+
+ omap_vram_add_region(paddr, size);
+
+ if (reserved)
+ pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved);
+
+ return reserved;
+}
+
+void __init omap_vram_set_sdram_vram(u32 size, u32 start)
+{
+ omap_vram_sdram_start = start;
+ omap_vram_sdram_size = size;
+}
+
+void __init omap_vram_set_sram_vram(u32 size, u32 start)
+{
+ omap_vram_sram_start = start;
+ omap_vram_sram_size = size;
+}
diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c
new file mode 100644
index 00000000000..fd227160037
--- /dev/null
+++ b/drivers/video/omap2/vrfb.c
@@ -0,0 +1,315 @@
+/*
+ * VRFB Rotation Engine
+ *
+ * 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/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+
+#include <mach/io.h>
+#include <plat/vrfb.h>
+#include <plat/sdrc.h>
+
+#ifdef DEBUG
+#define DBG(format, ...) pr_debug("VRFB: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define SMS_ROT_VIRT_BASE(context, rot) \
+ (((context >= 4) ? 0xD0000000 : 0x70000000) \
+ + (0x4000000 * (context)) \
+ + (0x1000000 * (rot)))
+
+#define OMAP_VRFB_SIZE (2048 * 2048 * 4)
+
+#define VRFB_PAGE_WIDTH_EXP 5 /* Assuming SDRAM pagesize= 1024 */
+#define VRFB_PAGE_HEIGHT_EXP 5 /* 1024 = 2^5 * 2^5 */
+#define VRFB_PAGE_WIDTH (1 << VRFB_PAGE_WIDTH_EXP)
+#define VRFB_PAGE_HEIGHT (1 << VRFB_PAGE_HEIGHT_EXP)
+#define SMS_IMAGEHEIGHT_OFFSET 16
+#define SMS_IMAGEWIDTH_OFFSET 0
+#define SMS_PH_OFFSET 8
+#define SMS_PW_OFFSET 4
+#define SMS_PS_OFFSET 0
+
+#define VRFB_NUM_CTXS 12
+/* bitmap of reserved contexts */
+static unsigned long ctx_map;
+
+static DEFINE_MUTEX(ctx_lock);
+
+/*
+ * Access to this happens from client drivers or the PM core after wake-up.
+ * For the first case we require locking at the driver level, for the second
+ * we don't need locking, since no drivers will run until after the wake-up
+ * has finished.
+ */
+static struct {
+ u32 physical_ba;
+ u32 control;
+ u32 size;
+} vrfb_hw_context[VRFB_NUM_CTXS];
+
+static inline void restore_hw_context(int ctx)
+{
+ omap2_sms_write_rot_control(vrfb_hw_context[ctx].control, ctx);
+ omap2_sms_write_rot_size(vrfb_hw_context[ctx].size, ctx);
+ omap2_sms_write_rot_physical_ba(vrfb_hw_context[ctx].physical_ba, ctx);
+}
+
+static u32 get_image_width_roundup(u16 width, u8 bytespp)
+{
+ unsigned long stride = width * bytespp;
+ unsigned long ceil_pages_per_stride = (stride / VRFB_PAGE_WIDTH) +
+ (stride % VRFB_PAGE_WIDTH != 0);
+
+ return ceil_pages_per_stride * VRFB_PAGE_WIDTH / bytespp;
+}
+
+/*
+ * This the extra space needed in the VRFB physical area for VRFB to safely wrap
+ * any memory accesses to the invisible part of the virtual view to the physical
+ * area.
+ */
+static inline u32 get_extra_physical_size(u16 image_width_roundup, u8 bytespp)
+{
+ return (OMAP_VRFB_LINE_LEN - image_width_roundup) * VRFB_PAGE_HEIGHT *
+ bytespp;
+}
+
+void omap_vrfb_restore_context(void)
+{
+ int i;
+ unsigned long map = ctx_map;
+
+ for (i = ffs(map); i; i = ffs(map)) {
+ /* i=1..32 */
+ i--;
+ map &= ~(1 << i);
+ restore_hw_context(i);
+ }
+}
+
+void omap_vrfb_adjust_size(u16 *width, u16 *height,
+ u8 bytespp)
+{
+ *width = ALIGN(*width * bytespp, VRFB_PAGE_WIDTH) / bytespp;
+ *height = ALIGN(*height, VRFB_PAGE_HEIGHT);
+}
+EXPORT_SYMBOL(omap_vrfb_adjust_size);
+
+u32 omap_vrfb_min_phys_size(u16 width, u16 height, u8 bytespp)
+{
+ unsigned long image_width_roundup = get_image_width_roundup(width,
+ bytespp);
+
+ if (image_width_roundup > OMAP_VRFB_LINE_LEN)
+ return 0;
+
+ return (width * height * bytespp) + get_extra_physical_size(
+ image_width_roundup, bytespp);
+}
+EXPORT_SYMBOL(omap_vrfb_min_phys_size);
+
+u16 omap_vrfb_max_height(u32 phys_size, u16 width, u8 bytespp)
+{
+ unsigned long image_width_roundup = get_image_width_roundup(width,
+ bytespp);
+ unsigned long height;
+ unsigned long extra;
+
+ if (image_width_roundup > OMAP_VRFB_LINE_LEN)
+ return 0;
+
+ extra = get_extra_physical_size(image_width_roundup, bytespp);
+
+ if (phys_size < extra)
+ return 0;
+
+ height = (phys_size - extra) / (width * bytespp);
+
+ /* Virtual views provided by VRFB are limited to 2048x2048. */
+ return min_t(unsigned long, height, 2048);
+}
+EXPORT_SYMBOL(omap_vrfb_max_height);
+
+void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr,
+ u16 width, u16 height,
+ unsigned bytespp, bool yuv_mode)
+{
+ unsigned pixel_size_exp;
+ u16 vrfb_width;
+ u16 vrfb_height;
+ u8 ctx = vrfb->context;
+ u32 size;
+ u32 control;
+
+ DBG("omapfb_set_vrfb(%d, %lx, %dx%d, %d, %d)\n", ctx, paddr,
+ width, height, bytespp, yuv_mode);
+
+ /* For YUV2 and UYVY modes VRFB needs to handle pixels a bit
+ * differently. See TRM. */
+ if (yuv_mode) {
+ bytespp *= 2;
+ width /= 2;
+ }
+
+ if (bytespp == 4)
+ pixel_size_exp = 2;
+ else if (bytespp == 2)
+ pixel_size_exp = 1;
+ else
+ BUG();
+
+ vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp;
+ vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT);
+
+ DBG("vrfb w %u, h %u bytespp %d\n", vrfb_width, vrfb_height, bytespp);
+
+ size = vrfb_width << SMS_IMAGEWIDTH_OFFSET;
+ size |= vrfb_height << SMS_IMAGEHEIGHT_OFFSET;
+
+ control = pixel_size_exp << SMS_PS_OFFSET;
+ control |= VRFB_PAGE_WIDTH_EXP << SMS_PW_OFFSET;
+ control |= VRFB_PAGE_HEIGHT_EXP << SMS_PH_OFFSET;
+
+ vrfb_hw_context[ctx].physical_ba = paddr;
+ vrfb_hw_context[ctx].size = size;
+ vrfb_hw_context[ctx].control = control;
+
+ omap2_sms_write_rot_physical_ba(paddr, ctx);
+ omap2_sms_write_rot_size(size, ctx);
+ omap2_sms_write_rot_control(control, ctx);
+
+ DBG("vrfb offset pixels %d, %d\n",
+ vrfb_width - width, vrfb_height - height);
+
+ vrfb->xres = width;
+ vrfb->yres = height;
+ vrfb->xoffset = vrfb_width - width;
+ vrfb->yoffset = vrfb_height - height;
+ vrfb->bytespp = bytespp;
+ vrfb->yuv_mode = yuv_mode;
+}
+EXPORT_SYMBOL(omap_vrfb_setup);
+
+int omap_vrfb_map_angle(struct vrfb *vrfb, u16 height, u8 rot)
+{
+ unsigned long size = height * OMAP_VRFB_LINE_LEN * vrfb->bytespp;
+
+ vrfb->vaddr[rot] = ioremap_wc(vrfb->paddr[rot], size);
+
+ if (!vrfb->vaddr[rot]) {
+ printk(KERN_ERR "vrfb: ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ DBG("ioremapped vrfb area %d of size %lu into %p\n", rot, size,
+ vrfb->vaddr[rot]);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_vrfb_map_angle);
+
+void omap_vrfb_release_ctx(struct vrfb *vrfb)
+{
+ int rot;
+ int ctx = vrfb->context;
+
+ if (ctx == 0xff)
+ return;
+
+ DBG("release ctx %d\n", ctx);
+
+ mutex_lock(&ctx_lock);
+
+ BUG_ON(!(ctx_map & (1 << ctx)));
+
+ clear_bit(ctx, &ctx_map);
+
+ for (rot = 0; rot < 4; ++rot) {
+ if (vrfb->paddr[rot]) {
+ release_mem_region(vrfb->paddr[rot], OMAP_VRFB_SIZE);
+ vrfb->paddr[rot] = 0;
+ }
+ }
+
+ vrfb->context = 0xff;
+
+ mutex_unlock(&ctx_lock);
+}
+EXPORT_SYMBOL(omap_vrfb_release_ctx);
+
+int omap_vrfb_request_ctx(struct vrfb *vrfb)
+{
+ int rot;
+ u32 paddr;
+ u8 ctx;
+ int r;
+
+ DBG("request ctx\n");
+
+ mutex_lock(&ctx_lock);
+
+ for (ctx = 0; ctx < VRFB_NUM_CTXS; ++ctx)
+ if ((ctx_map & (1 << ctx)) == 0)
+ break;
+
+ if (ctx == VRFB_NUM_CTXS) {
+ pr_err("vrfb: no free contexts\n");
+ r = -EBUSY;
+ goto out;
+ }
+
+ DBG("found free ctx %d\n", ctx);
+
+ set_bit(ctx, &ctx_map);
+
+ memset(vrfb, 0, sizeof(*vrfb));
+
+ vrfb->context = ctx;
+
+ for (rot = 0; rot < 4; ++rot) {
+ paddr = SMS_ROT_VIRT_BASE(ctx, rot);
+ if (!request_mem_region(paddr, OMAP_VRFB_SIZE, "vrfb")) {
+ pr_err("vrfb: failed to reserve VRFB "
+ "area for ctx %d, rotation %d\n",
+ ctx, rot * 90);
+ omap_vrfb_release_ctx(vrfb);
+ r = -ENOMEM;
+ goto out;
+ }
+
+ vrfb->paddr[rot] = paddr;
+
+ DBG("VRFB %d/%d: %lx\n", ctx, rot*90, vrfb->paddr[rot]);
+ }
+
+ r = 0;
+out:
+ mutex_unlock(&ctx_lock);
+ return r;
+}
+EXPORT_SYMBOL(omap_vrfb_request_ctx);
diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c
index 84d8327e47d..75285d3f393 100644
--- a/drivers/video/pxa168fb.c
+++ b/drivers/video/pxa168fb.c
@@ -687,6 +687,7 @@ static int __init pxa168fb_probe(struct platform_device *pdev)
}
info->fix.smem_start = (unsigned long)fbi->fb_start_dma;
+ set_graphics_start(info, 0, 0);
/*
* Set video mode according to platform data.
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 1820c4a2443..f58a3aae6ea 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -80,7 +80,8 @@
static int pxafb_activate_var(struct fb_var_screeninfo *var,
struct pxafb_info *);
static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);
-static void setup_base_frame(struct pxafb_info *fbi, int branch);
+static void setup_base_frame(struct pxafb_info *fbi,
+ struct fb_var_screeninfo *var, int branch);
static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
unsigned long offset, size_t size);
@@ -397,6 +398,7 @@ static void pxafb_setmode(struct fb_var_screeninfo *var,
var->lower_margin = mode->lower_margin;
var->sync = mode->sync;
var->grayscale = mode->cmap_greyscale;
+ var->transp.length = mode->transparency;
/* set the initial RGBA bitfields */
pxafb_set_pixfmt(var, mode->depth);
@@ -531,12 +533,22 @@ static int pxafb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct pxafb_info *fbi = (struct pxafb_info *)info;
+ struct fb_var_screeninfo newvar;
int dma = DMA_MAX + DMA_BASE;
if (fbi->state != C_ENABLE)
return 0;
- setup_base_frame(fbi, 1);
+ /* Only take .xoffset, .yoffset and .vmode & FB_VMODE_YWRAP from what
+ * was passed in and copy the rest from the old screeninfo.
+ */
+ memcpy(&newvar, &fbi->fb.var, sizeof(newvar));
+ newvar.xoffset = var->xoffset;
+ newvar.yoffset = var->yoffset;
+ newvar.vmode &= ~FB_VMODE_YWRAP;
+ newvar.vmode |= var->vmode & FB_VMODE_YWRAP;
+
+ setup_base_frame(fbi, &newvar, 1);
if (fbi->lccr0 & LCCR0_SDS)
lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1);
@@ -1052,9 +1064,10 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
return 0;
}
-static void setup_base_frame(struct pxafb_info *fbi, int branch)
+static void setup_base_frame(struct pxafb_info *fbi,
+ struct fb_var_screeninfo *var,
+ int branch)
{
- struct fb_var_screeninfo *var = &fbi->fb.var;
struct fb_fix_screeninfo *fix = &fbi->fb.fix;
int nbytes, dma, pal, bpp = var->bits_per_pixel;
unsigned long offset;
@@ -1332,7 +1345,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var,
#endif
setup_parallel_timing(fbi, var);
- setup_base_frame(fbi, 0);
+ setup_base_frame(fbi, var, 0);
fbi->reg_lccr0 = fbi->lccr0 |
(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |
diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c
index bba53714a7b..f86012239bf 100644
--- a/drivers/video/sgivwfb.c
+++ b/drivers/video/sgivwfb.c
@@ -260,13 +260,13 @@ static int sgivwfb_check_var(struct fb_var_screeninfo *var,
var->grayscale = 0; /* No grayscale for now */
/* determine valid resolution and timing */
- for (min_mode = 0; min_mode < DBE_VT_SIZE; min_mode++) {
+ for (min_mode = 0; min_mode < ARRAY_SIZE(dbeVTimings); min_mode++) {
if (dbeVTimings[min_mode].width >= var->xres &&
dbeVTimings[min_mode].height >= var->yres)
break;
}
- if (min_mode == DBE_VT_SIZE)
+ if (min_mode == ARRAY_SIZE(dbeVTimings))
return -EINVAL; /* Resolution to high */
/* XXX FIXME - should try to pick best refresh rate */
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 3ad5157f989..b4b5de930cf 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -281,18 +281,34 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
struct sh_mobile_lcdc_chan *ch = info->par;
- unsigned int nr_pages;
/* enable clocks before accessing hardware */
sh_mobile_lcdc_clk_on(ch->lcdc);
- nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
- dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
-
- /* trigger panel update */
- lcdc_write_chan(ch, LDSM2R, 1);
-
- dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ /*
+ * It's possible to get here without anything on the pagelist via
+ * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
+ * invocation. In the former case, the acceleration routines are
+ * stepped in to when using the framebuffer console causing the
+ * workqueue to be scheduled without any dirty pages on the list.
+ *
+ * Despite this, a panel update is still needed given that the
+ * acceleration routines have their own methods for writing in
+ * that still need to be updated.
+ *
+ * The fsync() and empty pagelist case could be optimized for,
+ * but we don't bother, as any application exhibiting such
+ * behaviour is fundamentally broken anyways.
+ */
+ if (!list_empty(pagelist)) {
+ unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
+
+ /* trigger panel update */
+ dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ lcdc_write_chan(ch, LDSM2R, 1);
+ dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ } else
+ lcdc_write_chan(ch, LDSM2R, 1);
}
static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
diff --git a/drivers/video/stifb.c b/drivers/video/stifb.c
index 6120f0c526f..876648e15e9 100644
--- a/drivers/video/stifb.c
+++ b/drivers/video/stifb.c
@@ -756,9 +756,9 @@ hyperResetPlanes(struct stifb_info *fb, int enable)
if (fb->info.var.bits_per_pixel == 32)
controlPlaneReg = 0x04000F00;
else
- controlPlaneReg = 0x00000F00; /* 0x00000800 should be enought, but lets clear all 4 bits */
+ controlPlaneReg = 0x00000F00; /* 0x00000800 should be enough, but lets clear all 4 bits */
else
- controlPlaneReg = 0x00000F00; /* 0x00000100 should be enought, but lets clear all 4 bits */
+ controlPlaneReg = 0x00000F00; /* 0x00000100 should be enough, but lets clear all 4 bits */
switch (enable) {
case ENABLE:
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c
index ff43c888502..98054839004 100644
--- a/drivers/video/tdfxfb.c
+++ b/drivers/video/tdfxfb.c
@@ -52,7 +52,7 @@
*
* 0.1.3 (released 1999-11-02) added Attila's panning support, code
* reorg, hwcursor address page size alignment
- * (for mmaping both frame buffer and regs),
+ * (for mmapping both frame buffer and regs),
* and my changes to get rid of hardcoded
* VGA i/o register locations (uses PCI
* configuration info now)
diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c
index c5c32b6b6e6..67b36932212 100644
--- a/drivers/video/via/dvi.c
+++ b/drivers/video/via/dvi.c
@@ -467,7 +467,7 @@ static int dvi_get_panel_size_from_DDCv1(void)
default:
viaparinfo->tmds_setting_info->dvi_panel_size =
VIA_RES_1024X768;
- DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d !\
+ DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d !\
set default panel size.\n", max_h);
break;
}
@@ -534,7 +534,7 @@ static int dvi_get_panel_size_from_DDCv2(void)
default:
viaparinfo->tmds_setting_info->dvi_panel_size =
VIA_RES_1024X768;
- DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d!\
+ DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d!\
set default panel size.\n", HSize);
break;
}
diff --git a/drivers/video/vt8623fb.c b/drivers/video/vt8623fb.c
index 3df17dc8c3d..65ccd215d49 100644
--- a/drivers/video/vt8623fb.c
+++ b/drivers/video/vt8623fb.c
@@ -446,7 +446,7 @@ static int vt8623fb_set_par(struct fb_info *info)
svga_wseq_mask(0x1E, 0xF0, 0xF0); // DI/DVP bus
svga_wseq_mask(0x2A, 0x0F, 0x0F); // DI/DVP bus
- svga_wseq_mask(0x16, 0x08, 0xBF); // FIFO read treshold
+ svga_wseq_mask(0x16, 0x08, 0xBF); // FIFO read threshold
vga_wseq(NULL, 0x17, 0x1F); // FIFO depth
vga_wseq(NULL, 0x18, 0x4E);
svga_wseq_mask(0x1A, 0x08, 0x08); // enable MMIO ?
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 54cd9161017..91a68e9eb66 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -440,7 +440,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.accel = FB_ACCEL_NONE;
- fb_info->flags = FBINFO_FLAG_DEFAULT;
+ fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
ret = fb_alloc_cmap(&fb_info->cmap, 256, 0);
if (ret < 0) {
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index 381026c0bd7..923cc68dba2 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -508,7 +508,7 @@ void coh901327_watchdog_reset(void)
* deactivating the watchdog before it is shut down by it.
*
* NOTE: on future versions of the watchdog, this restriction is
- * gone: the watchdog will be reloaded with a defaul value (1 min)
+ * gone: the watchdog will be reloaded with a default value (1 min)
* instead of last value, and you can conveniently set the watchdog
* timeout to 10ms (value = 1) without any problems.
*/
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 6a51edde6ea..e44fbb31bc6 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -1,5 +1,5 @@
/*
- * intel TCO Watchdog Driver (Used in i82801 and i63xxESB chipsets)
+ * intel TCO Watchdog Driver
*
* (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>.
*
@@ -14,47 +14,22 @@
*
* The TCO watchdog is implemented in the following I/O controller hubs:
* (See the intel documentation on http://developer.intel.com.)
- * 82801AA (ICH) : document number 290655-003, 290677-014,
- * 82801AB (ICHO) : document number 290655-003, 290677-014,
- * 82801BA (ICH2) : document number 290687-002, 298242-027,
- * 82801BAM (ICH2-M) : document number 290687-002, 298242-027,
- * 82801CA (ICH3-S) : document number 290733-003, 290739-013,
- * 82801CAM (ICH3-M) : document number 290716-001, 290718-007,
- * 82801DB (ICH4) : document number 290744-001, 290745-025,
- * 82801DBM (ICH4-M) : document number 252337-001, 252663-008,
- * 82801E (C-ICH) : document number 273599-001, 273645-002,
- * 82801EB (ICH5) : document number 252516-001, 252517-028,
- * 82801ER (ICH5R) : document number 252516-001, 252517-028,
- * 6300ESB (6300ESB) : document number 300641-004, 300884-013,
- * 82801FB (ICH6) : document number 301473-002, 301474-026,
- * 82801FR (ICH6R) : document number 301473-002, 301474-026,
- * 82801FBM (ICH6-M) : document number 301473-002, 301474-026,
- * 82801FW (ICH6W) : document number 301473-001, 301474-026,
- * 82801FRW (ICH6RW) : document number 301473-001, 301474-026,
- * 631xESB (631xESB) : document number 313082-001, 313075-006,
- * 632xESB (632xESB) : document number 313082-001, 313075-006,
- * 82801GB (ICH7) : document number 307013-003, 307014-024,
- * 82801GR (ICH7R) : document number 307013-003, 307014-024,
- * 82801GDH (ICH7DH) : document number 307013-003, 307014-024,
- * 82801GBM (ICH7-M) : document number 307013-003, 307014-024,
- * 82801GHM (ICH7-M DH) : document number 307013-003, 307014-024,
- * 82801GU (ICH7-U) : document number 307013-003, 307014-024,
- * 82801HB (ICH8) : document number 313056-003, 313057-017,
- * 82801HR (ICH8R) : document number 313056-003, 313057-017,
- * 82801HBM (ICH8M) : document number 313056-003, 313057-017,
- * 82801HH (ICH8DH) : document number 313056-003, 313057-017,
- * 82801HO (ICH8DO) : document number 313056-003, 313057-017,
- * 82801HEM (ICH8M-E) : document number 313056-003, 313057-017,
- * 82801IB (ICH9) : document number 316972-004, 316973-012,
- * 82801IR (ICH9R) : document number 316972-004, 316973-012,
- * 82801IH (ICH9DH) : document number 316972-004, 316973-012,
- * 82801IO (ICH9DO) : document number 316972-004, 316973-012,
- * 82801IBM (ICH9M) : document number 316972-004, 316973-012,
- * 82801IEM (ICH9M-E) : document number 316972-004, 316973-012,
- * 82801JIB (ICH10) : document number 319973-002, 319974-002,
- * 82801JIR (ICH10R) : document number 319973-002, 319974-002,
- * 82801JD (ICH10D) : document number 319973-002, 319974-002,
- * 82801JDO (ICH10DO) : document number 319973-002, 319974-002
+ * document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
+ * document number 290687-002, 298242-027: 82801BA (ICH2)
+ * document number 290733-003, 290739-013: 82801CA (ICH3-S)
+ * document number 290716-001, 290718-007: 82801CAM (ICH3-M)
+ * document number 290744-001, 290745-025: 82801DB (ICH4)
+ * document number 252337-001, 252663-008: 82801DBM (ICH4-M)
+ * document number 273599-001, 273645-002: 82801E (C-ICH)
+ * document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R)
+ * document number 300641-004, 300884-013: 6300ESB
+ * document number 301473-002, 301474-026: 82801F (ICH6)
+ * document number 313082-001, 313075-006: 631xESB, 632xESB
+ * document number 307013-003, 307014-024: 82801G (ICH7)
+ * document number 313056-003, 313057-017: 82801H (ICH8)
+ * document number 316972-004, 316973-012: 82801I (ICH9)
+ * document number 319973-002, 319974-002: 82801J (ICH10)
+ * document number 322169-001, 322170-001: 5 Series, 3400 Series (PCH)
*/
/*
@@ -122,6 +97,9 @@ enum iTCO_chipsets {
TCO_ICH10R, /* ICH10R */
TCO_ICH10D, /* ICH10D */
TCO_ICH10DO, /* ICH10DO */
+ TCO_PCH, /* PCH Desktop Full Featured */
+ TCO_PCHM, /* PCH Mobile Full Featured */
+ TCO_PCHMSFF, /* PCH Mobile SFF Full Featured */
};
static struct {
@@ -162,6 +140,9 @@ static struct {
{"ICH10R", 2},
{"ICH10D", 2},
{"ICH10DO", 2},
+ {"PCH Desktop Full Featured", 2},
+ {"PCH Mobile Full Featured", 2},
+ {"PCH Mobile SFF Full Featured", 2},
{NULL, 0}
};
@@ -230,6 +211,9 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = {
{ ITCO_PCI_DEVICE(0x3a16, TCO_ICH10R)},
{ ITCO_PCI_DEVICE(0x3a1a, TCO_ICH10D)},
{ ITCO_PCI_DEVICE(0x3a14, TCO_ICH10DO)},
+ { ITCO_PCI_DEVICE(0x3b00, TCO_PCH)},
+ { ITCO_PCI_DEVICE(0x3b01, TCO_PCHM)},
+ { ITCO_PCI_DEVICE(0x3b0d, TCO_PCHMSFF)},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c
index b6b3f59ab44..47d719717a3 100644
--- a/drivers/watchdog/machzwd.c
+++ b/drivers/watchdog/machzwd.c
@@ -21,7 +21,7 @@
* wd#1 - 2 seconds;
* wd#2 - 7.2 ms;
* After the expiration of wd#1, it can generate a NMI, SCI, SMI, or
- * a system RESET and it starts wd#2 that unconditionaly will RESET
+ * a system RESET and it starts wd#2 that unconditionally will RESET
* the system when the counter reaches zero.
*
* 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 3ed571a2ab1..429ea99eaee 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -43,7 +43,7 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <mach/hardware.h>
-#include <mach/prcm.h>
+#include <plat/prcm.h>
#include "omap_wdt.h"
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
index d3c824dc235..c14ae867690 100644
--- a/drivers/watchdog/riowd.c
+++ b/drivers/watchdog/riowd.c
@@ -10,7 +10,6 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
-#include <linux/smp_lock.h>
#include <linux/watchdog.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -75,7 +74,6 @@ static void riowd_writereg(struct riowd *p, u8 val, int index)
static int riowd_open(struct inode *inode, struct file *filp)
{
- cycle_kernel_lock();
nonseekable_open(inode, filp);
return 0;
}
@@ -194,6 +192,8 @@ static int __devinit riowd_probe(struct of_device *op,
printk(KERN_ERR PFX "Cannot map registers.\n");
goto out_free;
}
+ /* Make miscdev useable right away */
+ riowd_device = p;
err = misc_register(&riowd_miscdev);
if (err) {
@@ -205,10 +205,10 @@ static int __devinit riowd_probe(struct of_device *op,
"regs at %p\n", riowd_timeout, p->regs);
dev_set_drvdata(&op->dev, p);
- riowd_device = p;
return 0;
out_iounmap:
+ riowd_device = NULL;
of_iounmap(&op->resource[0], p->regs, 2);
out_free:
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index b57ac6b4914..85b93e15d01 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -36,6 +36,7 @@
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/cpufreq.h>
#include <mach/map.h>
@@ -142,9 +143,14 @@ static void s3c2410wdt_start(void)
spin_unlock(&wdt_lock);
}
+static inline int s3c2410wdt_is_running(void)
+{
+ return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
+}
+
static int s3c2410wdt_set_heartbeat(int timeout)
{
- unsigned int freq = clk_get_rate(wdt_clock);
+ unsigned long freq = clk_get_rate(wdt_clock);
unsigned int count;
unsigned int divisor = 1;
unsigned long wtcon;
@@ -155,7 +161,7 @@ static int s3c2410wdt_set_heartbeat(int timeout)
freq /= 128;
count = timeout * freq;
- DBG("%s: count=%d, timeout=%d, freq=%d\n",
+ DBG("%s: count=%d, timeout=%d, freq=%lu\n",
__func__, count, timeout, freq);
/* if the count is bigger than the watchdog register,
@@ -324,6 +330,73 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
s3c2410wdt_keepalive();
return IRQ_HANDLED;
}
+
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ int ret;
+
+ if (!s3c2410wdt_is_running())
+ goto done;
+
+ if (val == CPUFREQ_PRECHANGE) {
+ /* To ensure that over the change we don't cause the
+ * watchdog to trigger, we perform an keep-alive if
+ * the watchdog is running.
+ */
+
+ s3c2410wdt_keepalive();
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ s3c2410wdt_stop();
+
+ ret = s3c2410wdt_set_heartbeat(tmr_margin);
+
+ if (ret >= 0)
+ s3c2410wdt_start();
+ else
+ goto err;
+ }
+
+done:
+ return 0;
+
+ err:
+ dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin);
+ return ret;
+}
+
+static struct notifier_block s3c2410wdt_cpufreq_transition_nb = {
+ .notifier_call = s3c2410wdt_cpufreq_transition,
+};
+
+static inline int s3c2410wdt_cpufreq_register(void)
+{
+ return cpufreq_register_notifier(&s3c2410wdt_cpufreq_transition_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c2410wdt_cpufreq_deregister(void)
+{
+ cpufreq_unregister_notifier(&s3c2410wdt_cpufreq_transition_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c2410wdt_cpufreq_register(void)
+{
+ return 0;
+}
+
+static inline void s3c2410wdt_cpufreq_deregister(void)
+{
+}
+#endif
+
+
+
/* device interface */
static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
@@ -387,6 +460,11 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
clk_enable(wdt_clock);
+ if (s3c2410wdt_cpufreq_register() < 0) {
+ printk(KERN_ERR PFX "failed to register cpufreq\n");
+ goto err_clk;
+ }
+
/* see if we can actually set the requested timer margin, and if
* not, try the default value */
@@ -407,7 +485,7 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
- goto err_clk;
+ goto err_cpufreq;
}
if (tmr_atboot && started == 0) {
@@ -432,6 +510,9 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
return 0;
+ err_cpufreq:
+ s3c2410wdt_cpufreq_deregister();
+
err_clk:
clk_disable(wdt_clock);
clk_put(wdt_clock);
@@ -451,6 +532,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
static int __devexit s3c2410wdt_remove(struct platform_device *dev)
{
+ s3c2410wdt_cpufreq_deregister();
+
release_resource(wdt_mem);
kfree(wdt_mem);
wdt_mem = NULL;
diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c
index 9748eed7319..c8eadd47817 100644
--- a/drivers/watchdog/sb_wdog.c
+++ b/drivers/watchdog/sb_wdog.c
@@ -1,7 +1,7 @@
/*
* Watchdog driver for SiByte SB1 SoCs
*
- * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@onstor.com>
+ * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@lsi.com>
*
* This driver is intended to make the second of two hardware watchdogs
* on the Sibyte 12XX and 11XX SoCs available to the user. There are two
@@ -326,7 +326,7 @@ static void __exit sbwdog_exit(void)
module_init(sbwdog_init);
module_exit(sbwdog_exit);
-MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>");
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>");
MODULE_DESCRIPTION("SiByte Watchdog");
module_param(timeout, ulong, 0);
diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c
index 3bde56bce63..5bfb1f2c531 100644
--- a/drivers/watchdog/wdrtas.c
+++ b/drivers/watchdog/wdrtas.c
@@ -542,7 +542,7 @@ static struct notifier_block wdrtas_notifier = {
/**
* wdrtas_get_tokens - reads in RTAS tokens
*
- * returns 0 on succes, <0 on failure
+ * returns 0 on success, <0 on failure
*
* wdrtas_get_tokens reads in the tokens for the RTAS calls used in
* this watchdog driver. It tolerates, if "get-sensor-state" and
@@ -598,7 +598,7 @@ static void wdrtas_unregister_devs(void)
/**
* wdrtas_register_devs - registers the misc dev handlers
*
- * returns 0 on succes, <0 on failure
+ * returns 0 on success, <0 on failure
*
* wdrtas_register_devs registers the watchdog and temperature watchdog
* misc devs
@@ -630,7 +630,7 @@ static int wdrtas_register_devs(void)
/**
* wdrtas_init - init function of the watchdog driver
*
- * returns 0 on succes, <0 on failure
+ * returns 0 on success, <0 on failure
*
* registers the file handlers and the reboot notifier
*/
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index d31505b6f7a..42043361358 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -66,8 +66,6 @@ struct balloon_stats {
/* We aim for 'current allocation' == 'target allocation'. */
unsigned long current_pages;
unsigned long target_pages;
- /* We may hit the hard limit in Xen. If we do then we remember it. */
- unsigned long hard_limit;
/*
* Drivers may alter the memory reservation independently, but they
* must inform the balloon driver so we avoid hitting the hard limit.
@@ -136,6 +134,8 @@ static void balloon_append(struct page *page)
list_add(&page->lru, &ballooned_pages);
balloon_stats.balloon_low++;
}
+
+ totalram_pages--;
}
/* balloon_retrieve: rescue a page from the balloon, if it is not empty. */
@@ -156,6 +156,8 @@ static struct page *balloon_retrieve(void)
else
balloon_stats.balloon_low--;
+ totalram_pages++;
+
return page;
}
@@ -181,7 +183,7 @@ static void balloon_alarm(unsigned long unused)
static unsigned long current_target(void)
{
- unsigned long target = min(balloon_stats.target_pages, balloon_stats.hard_limit);
+ unsigned long target = balloon_stats.target_pages;
target = min(target,
balloon_stats.current_pages +
@@ -217,23 +219,10 @@ static int increase_reservation(unsigned long nr_pages)
set_xen_guest_handle(reservation.extent_start, frame_list);
reservation.nr_extents = nr_pages;
rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
- if (rc < nr_pages) {
- if (rc > 0) {
- int ret;
-
- /* We hit the Xen hard limit: reprobe. */
- reservation.nr_extents = rc;
- ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
- &reservation);
- BUG_ON(ret != rc);
- }
- if (rc >= 0)
- balloon_stats.hard_limit = (balloon_stats.current_pages + rc -
- balloon_stats.driver_pages);
+ if (rc < 0)
goto out;
- }
- for (i = 0; i < nr_pages; i++) {
+ for (i = 0; i < rc; i++) {
page = balloon_retrieve();
BUG_ON(page == NULL);
@@ -259,13 +248,12 @@ static int increase_reservation(unsigned long nr_pages)
__free_page(page);
}
- balloon_stats.current_pages += nr_pages;
- totalram_pages = balloon_stats.current_pages;
+ balloon_stats.current_pages += rc;
out:
spin_unlock_irqrestore(&balloon_lock, flags);
- return 0;
+ return rc < 0 ? rc : rc != nr_pages;
}
static int decrease_reservation(unsigned long nr_pages)
@@ -323,7 +311,6 @@ static int decrease_reservation(unsigned long nr_pages)
BUG_ON(ret != nr_pages);
balloon_stats.current_pages -= nr_pages;
- totalram_pages = balloon_stats.current_pages;
spin_unlock_irqrestore(&balloon_lock, flags);
@@ -367,7 +354,6 @@ static void balloon_process(struct work_struct *work)
static void balloon_set_new_target(unsigned long target)
{
/* No need for lock. Not read-modify-write updates. */
- balloon_stats.hard_limit = ~0UL;
balloon_stats.target_pages = target;
schedule_work(&balloon_worker);
}
@@ -422,12 +408,10 @@ static int __init balloon_init(void)
pr_info("xen_balloon: Initialising balloon driver.\n");
balloon_stats.current_pages = min(xen_start_info->nr_pages, max_pfn);
- totalram_pages = balloon_stats.current_pages;
balloon_stats.target_pages = balloon_stats.current_pages;
balloon_stats.balloon_low = 0;
balloon_stats.balloon_high = 0;
balloon_stats.driver_pages = 0UL;
- balloon_stats.hard_limit = ~0UL;
init_timer(&balloon_timer);
balloon_timer.data = 0;
@@ -472,9 +456,6 @@ module_exit(balloon_exit);
BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages));
BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low));
BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high));
-BALLOON_SHOW(hard_limit_kb,
- (balloon_stats.hard_limit!=~0UL) ? "%lu\n" : "???\n",
- (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0);
BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages));
static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr,
@@ -544,7 +525,6 @@ static struct attribute *balloon_info_attrs[] = {
&attr_current_kb.attr,
&attr_low_kb.attr,
&attr_high_kb.attr,
- &attr_hard_limit_kb.attr,
&attr_driver_kb.attr,
NULL
};
diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c
index bdfd584ad85..0f765a92018 100644
--- a/drivers/xen/cpu_hotplug.c
+++ b/drivers/xen/cpu_hotplug.c
@@ -86,7 +86,7 @@ static int setup_cpu_watcher(struct notifier_block *notifier,
for_each_possible_cpu(cpu) {
if (vcpu_online(cpu) == 0) {
(void)cpu_down(cpu);
- cpu_clear(cpu, cpu_present_map);
+ set_cpu_present(cpu, false);
}
}
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 2f57276e87a..ce602dd09bc 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -474,6 +474,9 @@ static void unbind_from_irq(unsigned int irq)
bind_evtchn_to_cpu(evtchn, 0);
evtchn_to_irq[evtchn] = -1;
+ }
+
+ if (irq_info[irq].type != IRQT_UNBOUND) {
irq_info[irq] = mk_unbound_info();
dynamic_irq_cleanup(irq);
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index 10d03d7931c..c4997930afc 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -43,7 +43,6 @@ static int xen_suspend(void *data)
if (err) {
printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
err);
- dpm_resume_noirq(PMSG_RESUME);
return err;
}
@@ -69,7 +68,6 @@ static int xen_suspend(void *data)
}
sysdev_resume();
- dpm_resume_noirq(PMSG_RESUME);
return 0;
}
@@ -81,6 +79,12 @@ static void do_suspend(void)
shutting_down = SHUTDOWN_SUSPEND;
+ err = stop_machine_create();
+ if (err) {
+ printk(KERN_ERR "xen suspend: failed to setup stop_machine %d\n", err);
+ goto out;
+ }
+
#ifdef CONFIG_PREEMPT
/* If the kernel is preemptible, we need to freeze all the processes
to prevent them from being in the middle of a pagetable update
@@ -88,29 +92,32 @@ static void do_suspend(void)
err = freeze_processes();
if (err) {
printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
- return;
+ goto out_destroy_sm;
}
#endif
err = dpm_suspend_start(PMSG_SUSPEND);
if (err) {
printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
- goto out;
+ goto out_thaw;
}
- printk(KERN_DEBUG "suspending xenstore...\n");
- xs_suspend();
-
err = dpm_suspend_noirq(PMSG_SUSPEND);
if (err) {
printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
- goto resume_devices;
+ goto out_resume;
}
+ printk(KERN_DEBUG "suspending xenstore...\n");
+ xs_suspend();
+
err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
+
+ dpm_resume_noirq(PMSG_RESUME);
+
if (err) {
printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
- goto out;
+ cancelled = 1;
}
if (!cancelled) {
@@ -119,17 +126,21 @@ static void do_suspend(void)
} else
xs_suspend_cancel();
- dpm_resume_noirq(PMSG_RESUME);
-
-resume_devices:
+out_resume:
dpm_resume_end(PMSG_RESUME);
/* Make sure timer events get retriggered on all CPUs */
clock_was_set();
-out:
+
+out_thaw:
#ifdef CONFIG_PREEMPT
thaw_processes();
+
+out_destroy_sm:
#endif
+ stop_machine_destroy();
+
+out:
shutting_down = SHUTDOWN_INVALID;
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index d42e25d5968..649fcdf114b 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -454,21 +454,21 @@ static ssize_t xendev_show_nodename(struct device *dev,
{
return sprintf(buf, "%s\n", to_xenbus_device(dev)->nodename);
}
-DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL);
+static DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL);
static ssize_t xendev_show_devtype(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", to_xenbus_device(dev)->devicetype);
}
-DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL);
+static DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL);
static ssize_t xendev_show_modalias(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "xen:%s\n", to_xenbus_device(dev)->devicetype);
}
-DEVICE_ATTR(modalias, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_modalias, NULL);
+static DEVICE_ATTR(modalias, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_modalias, NULL);
int xenbus_probe_node(struct xen_bus_type *bus,
const char *type,
@@ -843,7 +843,7 @@ postcore_initcall(xenbus_probe_init);
MODULE_LICENSE("GPL");
-static int is_disconnected_device(struct device *dev, void *data)
+static int is_device_connecting(struct device *dev, void *data)
{
struct xenbus_device *xendev = to_xenbus_device(dev);
struct device_driver *drv = data;
@@ -861,14 +861,15 @@ static int is_disconnected_device(struct device *dev, void *data)
return 0;
xendrv = to_xenbus_driver(dev->driver);
- return (xendev->state != XenbusStateConnected ||
- (xendrv->is_ready && !xendrv->is_ready(xendev)));
+ return (xendev->state < XenbusStateConnected ||
+ (xendev->state == XenbusStateConnected &&
+ xendrv->is_ready && !xendrv->is_ready(xendev)));
}
-static int exists_disconnected_device(struct device_driver *drv)
+static int exists_connecting_device(struct device_driver *drv)
{
return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
- is_disconnected_device);
+ is_device_connecting);
}
static int print_device_status(struct device *dev, void *data)
@@ -884,10 +885,13 @@ static int print_device_status(struct device *dev, void *data)
/* Information only: is this too noisy? */
printk(KERN_INFO "XENBUS: Device with no driver: %s\n",
xendev->nodename);
- } else if (xendev->state != XenbusStateConnected) {
+ } else if (xendev->state < XenbusStateConnected) {
+ enum xenbus_state rstate = XenbusStateUnknown;
+ if (xendev->otherend)
+ rstate = xenbus_read_driver_state(xendev->otherend);
printk(KERN_WARNING "XENBUS: Timeout connecting "
- "to device: %s (state %d)\n",
- xendev->nodename, xendev->state);
+ "to device: %s (local state %d, remote state %d)\n",
+ xendev->nodename, xendev->state, rstate);
}
return 0;
@@ -897,7 +901,7 @@ static int print_device_status(struct device *dev, void *data)
static int ready_to_wait_for_devices;
/*
- * On a 10 second timeout, wait for all devices currently configured. We need
+ * On a 5-minute timeout, wait for all devices currently configured. We need
* to do this to guarantee that the filesystems and / or network devices
* needed for boot are available, before we can allow the boot to proceed.
*
@@ -912,18 +916,30 @@ static int ready_to_wait_for_devices;
*/
static void wait_for_devices(struct xenbus_driver *xendrv)
{
- unsigned long timeout = jiffies + 10*HZ;
+ unsigned long start = jiffies;
struct device_driver *drv = xendrv ? &xendrv->driver : NULL;
+ unsigned int seconds_waited = 0;
if (!ready_to_wait_for_devices || !xen_domain())
return;
- while (exists_disconnected_device(drv)) {
- if (time_after(jiffies, timeout))
- break;
+ while (exists_connecting_device(drv)) {
+ if (time_after(jiffies, start + (seconds_waited+5)*HZ)) {
+ if (!seconds_waited)
+ printk(KERN_WARNING "XENBUS: Waiting for "
+ "devices to initialise: ");
+ seconds_waited += 5;
+ printk("%us...", 300 - seconds_waited);
+ if (seconds_waited == 300)
+ break;
+ }
+
schedule_timeout_interruptible(HZ/10);
}
+ if (seconds_waited)
+ printk("\n");
+
bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
print_device_status);
}
diff --git a/drivers/zorro/zorro-driver.c b/drivers/zorro/zorro-driver.c
index e6c4390d8bd..53180a37cc9 100644
--- a/drivers/zorro/zorro-driver.c
+++ b/drivers/zorro/zorro-driver.c
@@ -156,5 +156,4 @@ postcore_initcall(zorro_driver_init);
EXPORT_SYMBOL(zorro_match_device);
EXPORT_SYMBOL(zorro_register_driver);
EXPORT_SYMBOL(zorro_unregister_driver);
-EXPORT_SYMBOL(zorro_dev_driver);
EXPORT_SYMBOL(zorro_bus_type);